summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.mailmap2
-rwxr-xr-xAndroid.bp135
-rwxr-xr-xAndroid.mk18
-rw-r--r--CleanSpec.mk1
-rw-r--r--apct-tests/perftests/autofill/AndroidManifest.xml2
-rw-r--r--apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java12
-rw-r--r--apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java4
-rw-r--r--apct-tests/perftests/core/Android.mk6
-rw-r--r--apct-tests/perftests/core/AndroidManifest.xml9
-rw-r--r--apct-tests/perftests/core/apps/overlay/Android.bp188
-rw-r--r--apct-tests/perftests/core/apps/overlay/AndroidManifest.xml21
-rw-r--r--apct-tests/perftests/core/apps/overlay/res/values/values.xml19
-rw-r--r--apct-tests/perftests/core/apps/overlay/res_large/values/values.xml274
-rw-r--r--apct-tests/perftests/core/apps/reources_manager/Android.bp34
-rw-r--r--apct-tests/perftests/core/apps/reources_manager/AndroidManifest.xml20
-rw-r--r--apct-tests/perftests/core/res/color/color_state_list.xml25
-rw-r--r--apct-tests/perftests/core/res/values/overlayable.xml280
-rw-r--r--apct-tests/perftests/core/res/values/strings.xml32
-rw-r--r--apct-tests/perftests/core/res/values/values.xml368
-rw-r--r--apct-tests/perftests/core/src/android/app/OverlayManagerPerfTest.java234
-rw-r--r--apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java4
-rw-r--r--apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java139
-rw-r--r--apct-tests/perftests/core/src/android/app/ResourcesPerfTest.java165
-rw-r--r--apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java71
-rw-r--r--apct-tests/perftests/core/src/android/graphics/perftests/PaintHasGlyphPerfTest.java5
-rw-r--r--apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java6
-rw-r--r--apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java3
-rw-r--r--apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java5
-rw-r--r--apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java5
-rw-r--r--apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java5
-rw-r--r--apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java5
-rw-r--r--apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java5
-rw-r--r--apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java6
-rw-r--r--apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java6
-rw-r--r--apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java5
-rw-r--r--apct-tests/perftests/core/src/android/wm/InternalWindowOperationPerfTest.java121
-rw-r--r--apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java88
-rw-r--r--apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java6
-rw-r--r--apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java110
-rw-r--r--apct-tests/perftests/multiuser/AndroidManifest.xml1
-rw-r--r--apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java77
-rwxr-xr-xapct-tests/perftests/textclassifier/run.sh2
-rw-r--r--apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java100
-rw-r--r--apct-tests/perftests/utils/src/android/perftests/utils/PerfTestActivity.java46
-rw-r--r--apct-tests/perftests/utils/src/android/perftests/utils/Stats.java24
-rw-r--r--apct-tests/perftests/utils/src/android/perftests/utils/StubActivity.java30
-rw-r--r--apct-tests/perftests/utils/src/android/perftests/utils/TestPackageInstaller.java145
-rw-r--r--apct-tests/perftests/utils/src/android/perftests/utils/TraceMarkParser.java232
-rw-r--r--apex/jobscheduler/README_js-mainline.md22
-rw-r--r--apex/jobscheduler/framework/Android.bp9
-rw-r--r--apex/jobscheduler/framework/java/android/app/DeviceIdleFrameworkInitializer.java2
-rw-r--r--apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java18
-rw-r--r--apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl1
-rw-r--r--apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java20
-rw-r--r--apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java112
-rw-r--r--apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java250
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java213
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java2
-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/ConnectivityController.java10
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java12
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java23
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java72
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java74
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java (renamed from services/usage/java/com/android/server/usage/AppIdleHistory.java)0
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java1754
-rw-r--r--apex/permission/Android.bp33
-rw-r--r--apex/permission/OWNERS6
-rw-r--r--apex/permission/apex_manifest.json4
-rw-r--r--apex/permission/com.android.permission.avbpubkeybin0 -> 1032 bytes
-rw-r--r--apex/permission/com.android.permission.pem51
-rw-r--r--apex/permission/com.android.permission.pk8bin0 -> 2376 bytes
-rw-r--r--apex/permission/com.android.permission.x509.pem35
-rw-r--r--apex/statsd/.clang-format17
-rw-r--r--apex/statsd/OWNERS9
-rw-r--r--apex/statsd/service/Android.bp16
-rw-r--r--apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java2762
-rw-r--r--api/current.txt237
-rw-r--r--api/lint-baseline.txt1179
-rw-r--r--api/system-current.txt186
-rw-r--r--api/system-lint-baseline.txt391
-rw-r--r--api/test-current.txt106
-rw-r--r--cmds/idmap2/Android.bp9
-rw-r--r--cmds/idmap2/idmap2/Create.cpp6
-rw-r--r--cmds/idmap2/idmap2/Dump.cpp2
-rw-r--r--cmds/idmap2/idmap2/Lookup.cpp39
-rw-r--r--cmds/idmap2/idmap2/Scan.cpp13
-rw-r--r--cmds/idmap2/idmap2d/Idmap2Service.cpp4
-rw-r--r--cmds/idmap2/include/idmap2/BinaryStreamVisitor.h3
-rw-r--r--cmds/idmap2/include/idmap2/Idmap.h114
-rw-r--r--cmds/idmap2/include/idmap2/PrettyPrintVisitor.h2
-rw-r--r--cmds/idmap2/include/idmap2/RawPrintVisitor.h6
-rw-r--r--cmds/idmap2/include/idmap2/ResourceMapping.h132
-rw-r--r--cmds/idmap2/include/idmap2/ResourceUtils.h31
-rw-r--r--cmds/idmap2/include/idmap2/Xml.h49
-rw-r--r--cmds/idmap2/include/idmap2/XmlParser.h147
-rw-r--r--cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp44
-rw-r--r--cmds/idmap2/libidmap2/Idmap.cpp311
-rw-r--r--cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp34
-rw-r--r--cmds/idmap2/libidmap2/RawPrintVisitor.cpp123
-rw-r--r--cmds/idmap2/libidmap2/ResourceMapping.cpp417
-rw-r--r--cmds/idmap2/libidmap2/ResourceUtils.cpp112
-rw-r--r--cmds/idmap2/libidmap2/Xml.cpp80
-rw-r--r--cmds/idmap2/libidmap2/XmlParser.cpp163
-rw-r--r--cmds/idmap2/libidmap2/ZipFile.cpp1
-rwxr-xr-xcmds/idmap2/static-checks.sh5
-rw-r--r--cmds/idmap2/tests/BinaryStreamVisitorTests.cpp127
-rw-r--r--cmds/idmap2/tests/Idmap2BinaryTests.cpp2
-rw-r--r--cmds/idmap2/tests/IdmapTests.cpp486
-rw-r--r--cmds/idmap2/tests/PrettyPrintVisitorTests.cpp5
-rw-r--r--cmds/idmap2/tests/RawPrintVisitorTests.cpp36
-rw-r--r--cmds/idmap2/tests/ResourceMappingTests.cpp323
-rw-r--r--cmds/idmap2/tests/TestHelpers.h82
-rw-r--r--cmds/idmap2/tests/XmlParserTests.cpp174
-rw-r--r--cmds/idmap2/tests/XmlTests.cpp68
-rw-r--r--cmds/idmap2/tests/data/overlay/AndroidManifest.xml5
-rwxr-xr-xcmds/idmap2/tests/data/overlay/build2
-rw-r--r--cmds/idmap2/tests/data/overlay/overlay-no-name-static.apkbin1643 -> 3477 bytes
-rw-r--r--cmds/idmap2/tests/data/overlay/overlay-no-name.apkbin1599 -> 3389 bytes
-rw-r--r--cmds/idmap2/tests/data/overlay/overlay-static-1.apkbin1643 -> 3469 bytes
-rw-r--r--cmds/idmap2/tests/data/overlay/overlay-static-2.apkbin1643 -> 3469 bytes
-rw-r--r--cmds/idmap2/tests/data/overlay/overlay.apkbin1595 -> 3489 bytes
-rw-r--r--cmds/idmap2/tests/data/overlay/res/xml/overlays.xml23
-rw-r--r--cmds/idmap2/tests/data/overlay/res/xml/overlays_different_package.xml20
-rw-r--r--cmds/idmap2/tests/data/overlay/res/xml/overlays_inline.xml20
-rw-r--r--cmds/idmap2/tests/data/overlay/res/xml/overlays_swap.xml21
-rw-r--r--cmds/idmap2/tests/data/target/res/xml/test.xml19
-rw-r--r--cmds/idmap2/tests/data/target/target-no-overlayable.apkbin2263 -> 2749 bytes
-rw-r--r--cmds/idmap2/tests/data/target/target.apkbin5017 -> 5017 bytes
-rwxr-xr-xcmds/idmap2/valgrind.sh59
-rw-r--r--cmds/locksettings/TEST_MAPPING2
-rw-r--r--cmds/statsd/Android.bp8
-rw-r--r--cmds/statsd/src/HashableDimensionKey.cpp10
-rw-r--r--cmds/statsd/src/HashableDimensionKey.h9
-rw-r--r--cmds/statsd/src/StatsLogProcessor.cpp16
-rw-r--r--cmds/statsd/src/StatsLogProcessor.h17
-rw-r--r--cmds/statsd/src/StatsService.cpp15
-rw-r--r--cmds/statsd/src/StatsService.h4
-rw-r--r--cmds/statsd/src/anomaly/subscriber_util.cpp2
-rw-r--r--cmds/statsd/src/atoms.proto107
-rw-r--r--cmds/statsd/src/external/GpuStatsPuller.cpp12
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.cpp32
-rw-r--r--cmds/statsd/src/external/SurfaceflingerStatsPuller.cpp99
-rw-r--r--cmds/statsd/src/external/SurfaceflingerStatsPuller.h48
-rw-r--r--cmds/statsd/src/logd/LogEvent.cpp7
-rw-r--r--cmds/statsd/src/logd/LogEvent.h3
-rw-r--r--cmds/statsd/src/main.cpp2
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.cpp33
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.h26
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.cpp22
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.h15
-rw-r--r--cmds/statsd/src/metrics/EventMetricProducer.cpp15
-rw-r--r--cmds/statsd/src/metrics/EventMetricProducer.h11
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.cpp20
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.h20
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.cpp45
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.h88
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.cpp20
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.h4
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.cpp30
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.h19
-rw-r--r--cmds/statsd/src/metrics/metrics_manager_util.cpp338
-rw-r--r--cmds/statsd/src/metrics/metrics_manager_util.h21
-rwxr-xr-xcmds/statsd/src/socket/StatsSocketListener.cpp3
-rw-r--r--cmds/statsd/src/state/StateListener.h52
-rw-r--r--cmds/statsd/src/state/StateManager.cpp91
-rw-r--r--cmds/statsd/src/state/StateManager.h78
-rw-r--r--cmds/statsd/src/state/StateTracker.cpp159
-rw-r--r--cmds/statsd/src/state/StateTracker.h95
-rw-r--r--cmds/statsd/src/stats_log.proto13
-rw-r--r--cmds/statsd/src/stats_log_util.cpp2
-rw-r--r--cmds/statsd/src/statsd_config.proto37
-rw-r--r--cmds/statsd/tests/LogEvent_test.cpp4
-rw-r--r--cmds/statsd/tests/StatsLogProcessor_test.cpp312
-rw-r--r--cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp5
-rw-r--r--cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp197
-rw-r--r--cmds/statsd/tests/external/SurfaceflingerStatsPuller_test.cpp95
-rw-r--r--cmds/statsd/tests/metrics/CountMetricProducer_test.cpp27
-rw-r--r--cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp12
-rw-r--r--cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp21
-rw-r--r--cmds/statsd/tests/state/StateTracker_test.cpp382
-rw-r--r--cmds/statsd/tests/statsd_test_util.cpp95
-rw-r--r--cmds/statsd/tests/statsd_test_util.h33
-rw-r--r--cmds/telecom/src/com/android/commands/telecom/Telecom.java76
-rw-r--r--config/hiddenapi-greylist.txt283
-rw-r--r--config/preloaded-classes1
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java31
-rw-r--r--core/java/android/accessibilityservice/AccessibilityServiceInfo.java14
-rw-r--r--core/java/android/accessibilityservice/AccessibilityShortcutInfo.java225
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl2
-rw-r--r--core/java/android/accounts/AccountManager.java31
-rw-r--r--core/java/android/app/Activity.java88
-rw-r--r--core/java/android/app/ActivityManager.java51
-rw-r--r--core/java/android/app/ActivityThread.java52
-rw-r--r--core/java/android/app/ActivityTransitionState.java13
-rw-r--r--core/java/android/app/ActivityView.java48
-rw-r--r--core/java/android/app/AppCompatCallbacks.java11
-rw-r--r--core/java/android/app/AppOpsManager.java1125
-rw-r--r--core/java/android/app/AppOpsManagerInternal.java11
-rw-r--r--core/java/android/app/ApplicationPackageManager.java23
-rw-r--r--core/java/android/app/AsyncNotedAppOp.java101
-rw-r--r--core/java/android/app/ContextImpl.java87
-rw-r--r--core/java/android/app/EnterTransitionCoordinator.java13
-rw-r--r--core/java/android/app/IActivityManager.aidl11
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl1
-rw-r--r--core/java/android/app/IApplicationThread.aidl1
-rw-r--r--core/java/android/app/ITaskStackListener.aidl9
-rw-r--r--core/java/android/app/KeyguardManager.java6
-rw-r--r--core/java/android/app/Notification.java7
-rw-r--r--core/java/android/app/NotificationManager.java17
-rw-r--r--core/java/android/app/ResourcesManager.java716
-rw-r--r--core/java/android/app/SyncNotedAppOp.java13
-rw-r--r--core/java/android/app/SystemServiceRegistry.java12
-rw-r--r--core/java/android/app/TEST_MAPPING9
-rw-r--r--core/java/android/app/TaskStackListener.java4
-rw-r--r--core/java/android/app/UiAutomation.java32
-rw-r--r--core/java/android/app/admin/DelegatedAdminReceiver.java4
-rw-r--r--core/java/android/app/admin/DeviceAdminReceiver.java4
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java190
-rw-r--r--core/java/android/app/admin/DevicePolicyManagerInternal.java21
-rw-r--r--core/java/android/app/admin/DeviceStateCache.java56
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl4
-rw-r--r--core/java/android/app/admin/PasswordMetrics.java751
-rw-r--r--core/java/android/app/admin/PasswordPolicy.java83
-rw-r--r--core/java/android/app/assist/AssistStructure.java25
-rw-r--r--core/java/android/app/role/IRoleManager.aidl6
-rw-r--r--core/java/android/app/slice/SliceManager.java2
-rw-r--r--core/java/android/app/timedetector/ITimeDetectorService.aidl6
-rw-r--r--core/java/android/app/timedetector/PhoneTimeSuggestion.aidl19
-rw-r--r--core/java/android/app/timedetector/PhoneTimeSuggestion.java137
-rw-r--r--core/java/android/app/timedetector/TimeDetector.java6
-rw-r--r--core/java/android/app/timedetector/TimeSignal.aidl19
-rw-r--r--core/java/android/app/timedetector/TimeSignal.java110
-rw-r--r--core/java/android/app/usage/AppStandbyInfo.java4
-rw-r--r--core/java/android/app/usage/EventList.java15
-rw-r--r--core/java/android/app/usage/UsageEvents.java35
-rw-r--r--core/java/android/app/usage/UsageStats.java11
-rw-r--r--core/java/android/app/usage/UsageStatsManager.java8
-rw-r--r--core/java/android/app/usage/UsageStatsManagerInternal.java12
-rw-r--r--core/java/android/content/ContentProvider.java21
-rw-r--r--core/java/android/content/ContentResolver.java56
-rw-r--r--core/java/android/content/Context.java73
-rw-r--r--core/java/android/content/ContextWrapper.java13
-rw-r--r--core/java/android/content/Intent.java93
-rw-r--r--core/java/android/content/PermissionChecker.java318
-rw-r--r--core/java/android/content/om/IOverlayManager.aidl6
-rw-r--r--core/java/android/content/om/OverlayManager.java24
-rw-r--r--core/java/android/content/pm/AndroidTelephonyCommonUpdater.java82
-rw-r--r--core/java/android/content/pm/AndroidTestBaseUpdater.java36
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl7
-rw-r--r--core/java/android/content/pm/LauncherApps.java4
-rw-r--r--core/java/android/content/pm/PackageBackwardCompatibility.java2
-rw-r--r--core/java/android/content/pm/PackageInstaller.java22
-rw-r--r--core/java/android/content/pm/PackageList.java81
-rw-r--r--core/java/android/content/pm/PackageManager.java78
-rw-r--r--core/java/android/content/pm/PackageManagerInternal.java792
-rw-r--r--core/java/android/content/pm/PackageParser.java39
-rw-r--r--core/java/android/content/pm/PackageUserState.java197
-rw-r--r--core/java/android/content/pm/PermissionInfo.java30
-rw-r--r--core/java/android/content/pm/SharedLibraryNames.java2
-rw-r--r--core/java/android/content/pm/UserInfo.java50
-rw-r--r--core/java/android/content/res/ApkAssets.java124
-rw-r--r--core/java/android/content/res/AssetManager.java179
-rw-r--r--core/java/android/content/res/Resources.java177
-rw-r--r--core/java/android/content/res/ResourcesImpl.java47
-rw-r--r--core/java/android/content/res/StringBlock.java8
-rw-r--r--core/java/android/content/res/ThemedResourceCache.java16
-rw-r--r--core/java/android/content/res/loader/DirectoryResourceLoader.java74
-rw-r--r--core/java/android/content/res/loader/ResourceLoader.java116
-rw-r--r--core/java/android/content/res/loader/ResourceLoaderManager.java189
-rw-r--r--core/java/android/content/res/loader/ResourcesProvider.java139
-rw-r--r--core/java/android/hardware/biometrics/Authenticator.java35
-rw-r--r--core/java/android/hardware/biometrics/BiometricAuthenticator.java13
-rw-r--r--core/java/android/hardware/biometrics/BiometricConstants.java7
-rw-r--r--core/java/android/hardware/biometrics/BiometricManager.java52
-rw-r--r--core/java/android/hardware/biometrics/BiometricPrompt.java102
-rw-r--r--core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl58
-rw-r--r--core/java/android/hardware/biometrics/IBiometricConfirmDeviceCredentialCallback.aidl26
-rw-r--r--core/java/android/hardware/biometrics/IBiometricService.aidl25
-rw-r--r--core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl2
-rw-r--r--core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl4
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java172
-rw-r--r--core/java/android/hardware/face/FaceManager.java3
-rw-r--r--core/java/android/hardware/face/IFaceService.aidl2
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java3
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl2
-rw-r--r--core/java/android/hardware/input/IInputManager.aidl3
-rw-r--r--core/java/android/hardware/input/InputManager.java16
-rw-r--r--core/java/android/hardware/location/ContextHubClientCallback.java13
-rw-r--r--core/java/android/hardware/usb/UsbDevice.java93
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java15
-rw-r--r--core/java/android/net/ConnectivityManager.java249
-rw-r--r--core/java/android/net/INetworkPolicyListener.aidl4
-rw-r--r--core/java/android/net/INetworkPolicyManager.aidl2
-rw-r--r--core/java/android/net/LinkProperties.java47
-rw-r--r--core/java/android/net/MacAddress.java27
-rw-r--r--core/java/android/net/NetworkCapabilities.java44
-rw-r--r--core/java/android/net/NetworkMisc.java6
-rw-r--r--core/java/android/net/NetworkPolicyManager.java6
-rw-r--r--core/java/android/os/Binder.java9
-rwxr-xr-xcore/java/android/os/Build.java3
-rw-r--r--core/java/android/os/ConfigUpdate.java15
-rw-r--r--core/java/android/os/Debug.java31
-rw-r--r--core/java/android/os/GraphicsEnvironment.java56
-rw-r--r--core/java/android/os/Handler.java4
-rw-r--r--core/java/android/os/HwParcel.java52
-rw-r--r--core/java/android/os/IUserManager.aidl7
-rw-r--r--core/java/android/os/IUserRestrictionsListener.aidl26
-rw-r--r--core/java/android/os/OWNERS2
-rw-r--r--core/java/android/os/Parcel.java6
-rw-r--r--core/java/android/os/ParcelFileDescriptor.java11
-rw-r--r--core/java/android/os/Process.java5
-rw-r--r--core/java/android/os/SystemProperties.java7
-rw-r--r--core/java/android/os/TraceNameSupplier.java31
-rw-r--r--core/java/android/os/UpdateEngine.java8
-rw-r--r--core/java/android/os/UserHandle.java59
-rw-r--r--core/java/android/os/UserManager.java155
-rw-r--r--core/java/android/os/UserManagerInternal.java240
-rw-r--r--core/java/android/os/ZygoteProcess.java3
-rw-r--r--core/java/android/os/storage/StorageManager.java14
-rw-r--r--core/java/android/permission/IPermissionController.aidl4
-rw-r--r--core/java/android/permission/PermissionControllerManager.java117
-rw-r--r--core/java/android/permission/PermissionControllerService.java100
-rw-r--r--core/java/android/preference/SeekBarVolumizer.java4
-rw-r--r--core/java/android/print/TEST_MAPPING12
-rw-r--r--core/java/android/print/pdf/TEST_MAPPING7
-rw-r--r--core/java/android/provider/CallLog.java5
-rw-r--r--core/java/android/provider/DeviceConfig.java42
-rw-r--r--core/java/android/provider/DocumentsContract.java32
-rw-r--r--core/java/android/provider/MediaStore.java237
-rw-r--r--core/java/android/provider/SearchIndexablesContract.java24
-rw-r--r--core/java/android/provider/SearchIndexablesProvider.java23
-rw-r--r--core/java/android/provider/Settings.java262
-rw-r--r--core/java/android/service/autofill/AutofillFieldClassificationService.java7
-rw-r--r--core/java/android/service/autofill/SaveInfo.java101
-rw-r--r--core/java/android/service/carrier/CarrierService.java21
-rw-r--r--core/java/android/service/euicc/EuiccService.java42
-rw-r--r--core/java/android/service/euicc/IEuiccService.aidl2
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java30
-rw-r--r--core/java/android/service/quicksettings/TileService.java22
-rw-r--r--core/java/android/service/textclassifier/TextClassifierService.java4
-rw-r--r--core/java/android/speech/RecognitionService.java24
-rw-r--r--core/java/android/telephony/PhoneStateListener.java1280
-rw-r--r--core/java/android/telephony/TelephonyRegistryManager.java697
-rw-r--r--core/java/android/text/TextUtils.java11
-rw-r--r--core/java/android/text/style/AccessibilityReplacementSpan.java100
-rw-r--r--core/java/android/text/style/ReplacementSpan.java23
-rw-r--r--core/java/android/util/ArraySet.java13
-rw-r--r--core/java/android/util/DebugUtils.java2
-rw-r--r--core/java/android/util/FeatureFlagUtils.java3
-rw-r--r--core/java/android/util/LongSparseArray.java93
-rw-r--r--core/java/android/util/LongSparseLongArray.java47
-rw-r--r--core/java/android/util/OWNERS2
-rw-r--r--core/java/android/util/StatsEvent.java331
-rw-r--r--core/java/android/util/TimestampedValue.java89
-rw-r--r--core/java/android/view/CompositionSamplingListener.java22
-rw-r--r--core/java/android/view/DragAndDropPermissions.java6
-rw-r--r--core/java/android/view/IPinnedStackListener.aidl8
-rw-r--r--core/java/android/view/IRecentsAnimationController.aidl6
-rw-r--r--core/java/android/view/IWindow.aidl16
-rw-r--r--core/java/android/view/IWindowManager.aidl23
-rw-r--r--core/java/android/view/InputMonitor.java151
-rw-r--r--core/java/android/view/InputWindowHandle.java10
-rw-r--r--core/java/android/view/InsetsAnimationControlImpl.java38
-rw-r--r--core/java/android/view/InsetsController.java71
-rw-r--r--core/java/android/view/InsetsFlags.java60
-rw-r--r--core/java/android/view/InsetsState.java30
-rw-r--r--core/java/android/view/SurfaceControl.java224
-rw-r--r--core/java/android/view/SurfaceHolder.java18
-rw-r--r--core/java/android/view/SurfaceView.java191
-rw-r--r--core/java/android/view/View.java6
-rw-r--r--core/java/android/view/ViewDebug.java644
-rw-r--r--core/java/android/view/ViewGroup.java10
-rw-r--r--core/java/android/view/ViewRootImpl.java130
-rw-r--r--core/java/android/view/ViewStructure.java2
-rw-r--r--core/java/android/view/WindowInsetsController.java20
-rw-r--r--core/java/android/view/WindowManager.java24
-rw-r--r--core/java/android/view/WindowManagerGlobal.java22
-rw-r--r--core/java/android/view/WindowlessViewRoot.java8
-rw-r--r--core/java/android/view/WindowlessWindowManager.java64
-rw-r--r--core/java/android/view/accessibility/AccessibilityInteractionClient.java35
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java70
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java101
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeProvider.java130
-rw-r--r--core/java/android/view/accessibility/AccessibilityWindowInfo.aidl1
-rw-r--r--core/java/android/view/accessibility/AccessibilityWindowInfo.java48
-rw-r--r--core/java/android/view/contentcapture/ChildContentCaptureSession.java9
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureManager.java4
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureSession.java21
-rw-r--r--core/java/android/view/contentcapture/MainContentCaptureSession.java18
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java6
-rw-r--r--core/java/android/view/inspector/OWNERS2
-rw-r--r--core/java/android/webkit/FindAddress.java6
-rw-r--r--core/java/android/webkit/MimeTypeMap.java2
-rw-r--r--core/java/android/webkit/WebSettings.java45
-rwxr-xr-xcore/java/android/widget/AbsListView.java3
-rw-r--r--core/java/android/widget/SimpleMonthView.java1
-rw-r--r--core/java/android/widget/TextView.java6
-rw-r--r--core/java/com/android/internal/accessibility/AccessibilityShortcutController.java2
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java28
-rw-r--r--core/java/com/android/internal/app/IAppOpsService.aidl14
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java93
-rw-r--r--core/java/com/android/internal/app/ResolverListController.java87
-rw-r--r--core/java/com/android/internal/app/procstats/AssociationState.java481
-rw-r--r--core/java/com/android/internal/app/procstats/ProcessStats.java157
-rw-r--r--core/java/com/android/internal/compat/ChangeReporter.java178
-rw-r--r--core/java/com/android/internal/compat/CompatibilityChangeConfig.aidl19
-rw-r--r--core/java/com/android/internal/compat/CompatibilityChangeConfig.java95
-rw-r--r--core/java/com/android/internal/compat/IPlatformCompat.aidl104
-rw-r--r--core/java/com/android/internal/compat/OWNERS7
-rw-r--r--core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java7
-rw-r--r--core/java/com/android/internal/content/FileSystemProvider.java10
-rw-r--r--core/java/com/android/internal/content/PackageMonitor.java9
-rw-r--r--core/java/com/android/internal/infra/ServiceConnector.java5
-rw-r--r--core/java/com/android/internal/os/KernelWakelockReader.java16
-rw-r--r--core/java/com/android/internal/os/RuntimeInit.java26
-rw-r--r--core/java/com/android/internal/os/Zygote.java3
-rw-r--r--core/java/com/android/internal/os/ZygoteArguments.java2
-rw-r--r--core/java/com/android/internal/os/ZygoteConnection.java6
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java8
-rw-r--r--core/java/com/android/internal/policy/KeyInterceptionInfo.java35
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl55
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl20
-rw-r--r--core/java/com/android/internal/statusbar/RegisterStatusBarResult.java25
-rw-r--r--core/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl (renamed from telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl)0
-rw-r--r--core/java/com/android/internal/telephony/IPhoneStateListener.aidl69
-rw-r--r--core/java/com/android/internal/telephony/ITelephonyRegistry.aidl103
-rw-r--r--core/java/com/android/internal/util/MimeIconUtils.java2
-rw-r--r--core/java/com/android/internal/util/function/DecConsumer.java28
-rw-r--r--core/java/com/android/internal/util/function/DecFunction.java28
-rw-r--r--core/java/com/android/internal/util/function/DecPredicate.java28
-rw-r--r--core/java/com/android/internal/util/function/UndecConsumer.java28
-rw-r--r--core/java/com/android/internal/util/function/UndecFunction.java28
-rw-r--r--core/java/com/android/internal/util/function/UndecPredicate.java28
-rwxr-xr-xcore/java/com/android/internal/util/function/pooled/OmniFunction.java87
-rwxr-xr-xcore/java/com/android/internal/util/function/pooled/PooledLambda.java304
-rwxr-xr-xcore/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java110
-rw-r--r--core/java/com/android/internal/util/function/pooled/PooledRunnable.java5
-rw-r--r--core/java/com/android/internal/view/AppearanceRegion.aidl19
-rw-r--r--core/java/com/android/internal/view/AppearanceRegion.java142
-rw-r--r--core/java/com/android/internal/view/BaseIWindow.java9
-rw-r--r--core/java/com/android/internal/widget/ILockSettings.aidl18
-rw-r--r--core/java/com/android/internal/widget/LockPatternChecker.java187
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java763
-rw-r--r--core/java/com/android/internal/widget/LockPatternView.java2
-rw-r--r--core/java/com/android/internal/widget/LockSettingsInternal.java4
-rw-r--r--core/java/com/android/internal/widget/LockscreenCredential.aidl23
-rw-r--r--core/java/com/android/internal/widget/LockscreenCredential.java339
-rw-r--r--core/java/com/android/internal/widget/PasswordValidationError.java78
-rw-r--r--core/java/com/android/server/SystemConfig.java174
-rw-r--r--core/jni/Android.bp14
-rw-r--r--core/jni/AndroidRuntime.cpp134
-rwxr-xr-xcore/jni/android/graphics/Bitmap.cpp58
-rw-r--r--core/jni/android/graphics/Bitmap.h2
-rw-r--r--core/jni/android/graphics/ByteBufferStreamAdaptor.cpp29
-rw-r--r--core/jni/android/graphics/ImageDecoder.cpp11
-rw-r--r--core/jni/android/graphics/apex/TypeCast.h70
-rw-r--r--core/jni/android/graphics/apex/android_bitmap.cpp106
-rw-r--r--core/jni/android/graphics/apex/android_canvas.cpp55
-rw-r--r--core/jni/android/graphics/apex/android_paint.cpp47
-rw-r--r--core/jni/android/graphics/apex/include/android/graphics/bitmap.h102
-rw-r--r--core/jni/android/graphics/apex/include/android/graphics/canvas.h88
-rw-r--r--core/jni/android/graphics/apex/include/android/graphics/jni_runtime.h32
-rw-r--r--core/jni/android/graphics/apex/include/android/graphics/paint.h66
-rw-r--r--core/jni/android/graphics/apex/jni_runtime.cpp169
-rw-r--r--core/jni/android/opengl/util.cpp159
-rw-r--r--core/jni/android_content_res_ApkAssets.cpp74
-rw-r--r--core/jni/android_graphics_BLASTBufferQueue.cpp82
-rw-r--r--core/jni/android_graphics_GraphicBuffer.cpp10
-rw-r--r--core/jni/android_media_JetPlayer.cpp538
-rw-r--r--core/jni/android_os_Debug.cpp30
-rw-r--r--core/jni/android_os_GraphicsEnvironment.cpp5
-rw-r--r--core/jni/android_os_Parcel.cpp6
-rw-r--r--core/jni/android_util_AssetManager.cpp65
-rw-r--r--core/jni/android_util_Binder.cpp31
-rw-r--r--core/jni/android_util_Process.cpp66
-rw-r--r--core/jni/android_view_InputChannel.cpp10
-rw-r--r--core/jni/android_view_InputEventReceiver.cpp12
-rw-r--r--core/jni/android_view_PointerIcon.cpp6
-rw-r--r--core/jni/android_view_PointerIcon.h6
-rw-r--r--core/jni/android_view_RenderNode.cpp12
-rw-r--r--core/jni/android_view_Surface.cpp11
-rw-r--r--core/jni/android_view_SurfaceControl.cpp22
-rw-r--r--core/jni/android_view_TextureLayer.cpp3
-rw-r--r--core/jni/android_view_TextureView.cpp10
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp16
-rw-r--r--core/jni/com_android_internal_os_ZygoteInit.cpp10
-rw-r--r--core/proto/Android.bp4
-rw-r--r--core/proto/android/app/settings_enums.proto14
-rw-r--r--core/proto/android/providers/settings/global.proto1
-rw-r--r--core/proto/android/providers/settings/secure.proto5
-rw-r--r--core/proto/android/server/activitymanagerservice.proto4
-rw-r--r--core/proto/android/server/jobscheduler.proto19
-rw-r--r--core/proto/android/server/notificationhistory.proto89
-rw-r--r--core/proto/android/server/protolog.proto61
-rw-r--r--core/proto/android/server/usagestatsservice.proto2
-rw-r--r--core/proto/android/server/usagestatsservice_v2.proto135
-rw-r--r--core/proto/android/server/windowmanagerlog.proto61
-rw-r--r--core/proto/android/service/package.proto11
-rw-r--r--core/proto/android/service/procstats.proto15
-rw-r--r--core/proto/android/stats/devicepolicy/device_policy_enums.proto6
-rw-r--r--core/proto/android/telecomm/enums.proto18
-rw-r--r--core/res/AndroidManifest.xml61
-rw-r--r--core/res/TEST_MAPPING3
-rw-r--r--core/res/res/anim/screen_rotate_0_exit.xml3
-rw-r--r--core/res/res/anim/screen_rotate_180_exit.xml3
-rw-r--r--core/res/res/anim/screen_rotate_alpha.xml27
-rw-r--r--core/res/res/anim/screen_rotate_minus_90_exit.xml5
-rw-r--r--core/res/res/anim/screen_rotate_minus_90_frame.xml28
-rw-r--r--core/res/res/anim/screen_rotate_plus_90_exit.xml5
-rw-r--r--core/res/res/anim/screen_rotate_plus_90_frame.xml28
-rw-r--r--core/res/res/drawable-hdpi/ic_launcher_android.pngbin3283 -> 3997 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_launcher_android.pngbin2225 -> 2479 bytes
-rw-r--r--core/res/res/drawable-xhdpi/ic_launcher_android.pngbin4264 -> 5707 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/ic_launcher_android.pngbin6171 -> 9538 bytes
-rw-r--r--core/res/res/drawable-xxxhdpi/ic_launcher_android.pngbin0 -> 13489 bytes
-rw-r--r--core/res/res/layout/resolve_list_item.xml20
-rw-r--r--core/res/res/layout/resolver_different_item_header.xml16
-rw-r--r--core/res/res/layout/resolver_list.xml63
-rw-r--r--core/res/res/layout/resolver_list_with_default.xml76
-rw-r--r--core/res/res/values-af/strings.xml16
-rw-r--r--core/res/res/values-am/strings.xml10
-rw-r--r--core/res/res/values-ar/strings.xml24
-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.xml16
-rw-r--r--core/res/res/values-be/strings.xml22
-rw-r--r--core/res/res/values-bg/strings.xml14
-rw-r--r--core/res/res/values-bn/strings.xml18
-rw-r--r--core/res/res/values-bs/strings.xml18
-rw-r--r--core/res/res/values-ca/strings.xml50
-rw-r--r--core/res/res/values-cs/strings.xml14
-rw-r--r--core/res/res/values-da/strings.xml24
-rw-r--r--core/res/res/values-de/strings.xml16
-rw-r--r--core/res/res/values-el/strings.xml36
-rw-r--r--core/res/res/values-en-rAU/strings.xml10
-rw-r--r--core/res/res/values-en-rCA/strings.xml10
-rw-r--r--core/res/res/values-en-rGB/strings.xml10
-rw-r--r--core/res/res/values-en-rIN/strings.xml10
-rw-r--r--core/res/res/values-en-rXC/strings.xml10
-rw-r--r--core/res/res/values-es-rUS/strings.xml12
-rw-r--r--core/res/res/values-es/strings.xml16
-rw-r--r--core/res/res/values-et/strings.xml12
-rw-r--r--core/res/res/values-eu/strings.xml42
-rw-r--r--core/res/res/values-fa/strings.xml12
-rw-r--r--core/res/res/values-fi/strings.xml22
-rw-r--r--core/res/res/values-fr-rCA/strings.xml22
-rw-r--r--core/res/res/values-fr/strings.xml20
-rw-r--r--core/res/res/values-gl/strings.xml10
-rw-r--r--core/res/res/values-gu/strings.xml26
-rw-r--r--core/res/res/values-hi/strings.xml28
-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.xml26
-rw-r--r--core/res/res/values-in/strings.xml14
-rw-r--r--core/res/res/values-is/strings.xml10
-rw-r--r--core/res/res/values-it/strings.xml52
-rw-r--r--core/res/res/values-iw/strings.xml22
-rw-r--r--core/res/res/values-ja/strings.xml12
-rw-r--r--core/res/res/values-ka/strings.xml16
-rw-r--r--core/res/res/values-kk/strings.xml12
-rw-r--r--core/res/res/values-km/strings.xml28
-rw-r--r--core/res/res/values-kn/strings.xml44
-rw-r--r--core/res/res/values-ko/strings.xml10
-rw-r--r--core/res/res/values-ky/strings.xml28
-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-mcc310-mnc030-da/strings.xml2
-rw-r--r--core/res/res/values-mcc310-mnc170-da/strings.xml2
-rw-r--r--core/res/res/values-mcc310-mnc280-da/strings.xml2
-rw-r--r--core/res/res/values-mcc310-mnc380-da/strings.xml2
-rw-r--r--core/res/res/values-mcc310-mnc410-da/strings.xml2
-rw-r--r--core/res/res/values-mcc310-mnc560-da/strings.xml2
-rw-r--r--core/res/res/values-mcc310-mnc950-da/strings.xml2
-rw-r--r--core/res/res/values-mcc311-mnc180-da/strings.xml2
-rw-r--r--core/res/res/values-mk/strings.xml18
-rw-r--r--core/res/res/values-ml/strings.xml34
-rw-r--r--core/res/res/values-mn/strings.xml28
-rw-r--r--core/res/res/values-mr/strings.xml12
-rw-r--r--core/res/res/values-ms/strings.xml14
-rw-r--r--core/res/res/values-my/strings.xml46
-rw-r--r--core/res/res/values-nb/strings.xml14
-rw-r--r--core/res/res/values-ne/strings.xml34
-rw-r--r--core/res/res/values-nl/strings.xml18
-rw-r--r--core/res/res/values-or/strings.xml42
-rw-r--r--core/res/res/values-pa/strings.xml14
-rw-r--r--core/res/res/values-pl/strings.xml14
-rw-r--r--core/res/res/values-pt-rBR/strings.xml24
-rw-r--r--core/res/res/values-pt-rPT/strings.xml14
-rw-r--r--core/res/res/values-pt/strings.xml24
-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.xml12
-rw-r--r--core/res/res/values-sk/strings.xml18
-rw-r--r--core/res/res/values-sl/strings.xml10
-rw-r--r--core/res/res/values-sq/strings.xml22
-rw-r--r--core/res/res/values-sr/strings.xml16
-rw-r--r--core/res/res/values-sv/strings.xml10
-rw-r--r--core/res/res/values-sw/strings.xml34
-rw-r--r--core/res/res/values-ta/strings.xml62
-rw-r--r--core/res/res/values-te/strings.xml16
-rw-r--r--core/res/res/values-th/strings.xml16
-rw-r--r--core/res/res/values-tl/strings.xml12
-rw-r--r--core/res/res/values-tr/strings.xml12
-rw-r--r--core/res/res/values-uk/strings.xml34
-rw-r--r--core/res/res/values-ur/strings.xml12
-rw-r--r--core/res/res/values-uz/strings.xml12
-rw-r--r--core/res/res/values-vi/strings.xml16
-rw-r--r--core/res/res/values-zh-rCN/strings.xml10
-rw-r--r--core/res/res/values-zh-rHK/strings.xml14
-rw-r--r--core/res/res/values-zh-rTW/strings.xml14
-rw-r--r--core/res/res/values-zu/strings.xml18
-rw-r--r--core/res/res/values/attrs.xml12
-rw-r--r--core/res/res/values/attrs_manifest.xml17
-rw-r--r--core/res/res/values/config.xml57
-rw-r--r--core/res/res/values/dimens.xml14
-rw-r--r--core/res/res/values/public.xml2
-rw-r--r--core/res/res/values/strings.xml32
-rw-r--r--core/res/res/values/symbols.xml27
-rw-r--r--core/tests/PlatformCompatFramework/Android.bp14
-rw-r--r--core/tests/PlatformCompatFramework/AndroidManifest.xml11
-rw-r--r--core/tests/PlatformCompatFramework/OWNERS7
-rw-r--r--core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java115
-rw-r--r--core/tests/ResourceLoaderTests/Android.bp63
-rw-r--r--core/tests/ResourceLoaderTests/AndroidManifest.xml42
-rw-r--r--core/tests/ResourceLoaderTests/AndroidTest.xml37
-rw-r--r--core/tests/ResourceLoaderTests/NonAsset.txt1
-rw-r--r--core/tests/ResourceLoaderTests/SplitOne/Android.bp19
-rw-r--r--core/tests/ResourceLoaderTests/SplitOne/AndroidManifest.xml27
-rw-r--r--core/tests/ResourceLoaderTests/SplitOne/res/values/string_split_one.xml21
-rw-r--r--core/tests/ResourceLoaderTests/assets/Asset.txt1
-rw-r--r--core/tests/ResourceLoaderTests/lib/kotlin-reflect-sources.jarbin0 -> 689094 bytes
-rw-r--r--core/tests/ResourceLoaderTests/lib/kotlin-reflect.jarbin0 -> 2796397 bytes
-rw-r--r--core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk7-sources.jarbin0 -> 2453 bytes
-rw-r--r--core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk7.jarbin0 -> 3125 bytes
-rw-r--r--core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk8-sources.jarbin0 -> 5568 bytes
-rw-r--r--core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk8.jarbin0 -> 13827 bytes
-rw-r--r--core/tests/ResourceLoaderTests/lib/kotlin-stdlib-sources.jarbin0 -> 415274 bytes
-rw-r--r--core/tests/ResourceLoaderTests/lib/kotlin-stdlib.jarbin0 -> 1290543 bytes
-rw-r--r--core/tests/ResourceLoaderTests/lib/kotlin-test-sources.jarbin0 -> 3136 bytes
-rw-r--r--core/tests/ResourceLoaderTests/lib/kotlin-test.jarbin0 -> 30966 bytes
-rw-r--r--core/tests/ResourceLoaderTests/overlay/Android.bp20
-rw-r--r--core/tests/ResourceLoaderTests/overlay/AndroidManifest.xml27
-rw-r--r--core/tests/ResourceLoaderTests/overlay/res/values/strings.xml22
-rw-r--r--core/tests/ResourceLoaderTests/res/drawable-nodpi/non_asset_bitmap.pngbin0 -> 146 bytes
-rw-r--r--core/tests/ResourceLoaderTests/res/drawable-nodpi/non_asset_drawable.xml21
-rw-r--r--core/tests/ResourceLoaderTests/res/layout/layout.xml23
-rw-r--r--core/tests/ResourceLoaderTests/res/values/strings.xml23
-rw-r--r--core/tests/ResourceLoaderTests/resources/AndroidManifestApp.xml26
-rw-r--r--core/tests/ResourceLoaderTests/resources/AndroidManifestFramework.xml27
-rwxr-xr-xcore/tests/ResourceLoaderTests/resources/compileAndLink.sh108
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/drawable-mdpi/ic_delete.pngbin0 -> 1445 bytes
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapBlue.pngbin0 -> 146 bytes
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapGreen.pngbin0 -> 146 bytes
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableOne.xml21
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableTwo.xml21
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/layout/activity_list_item.xml23
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/layout/layout_one.xml23
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/layout/layout_two.xml23
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/values/activity_list_item_id.xml20
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/values/dimen_one.xml21
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/values/dimen_two.xml21
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/values/drawable_one.xml20
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/values/layout_id.xml20
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/values/non_asset_bitmap_id.xml20
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/values/non_asset_drawable_id.xml20
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/values/string_one.xml21
-rw-r--r--core/tests/ResourceLoaderTests/resources/res/values/string_two.xml21
-rw-r--r--core/tests/ResourceLoaderTests/splits/Android.bp19
-rw-r--r--core/tests/ResourceLoaderTests/splits/AndroidManifest.xml27
-rw-r--r--core/tests/ResourceLoaderTests/splits/res/values/string_split_two.xml21
-rw-r--r--core/tests/ResourceLoaderTests/src/android/content/res/loader/test/DirectoryResourceLoaderTest.kt101
-rw-r--r--core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderAssetTest.kt169
-rw-r--r--core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderChangesTest.kt236
-rw-r--r--core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderDrawableTest.kt183
-rw-r--r--core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderLayoutTest.kt153
-rw-r--r--core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderTestBase.kt226
-rw-r--r--core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderValuesTest.kt354
-rw-r--r--core/tests/ResourceLoaderTests/src/android/content/res/loader/test/Utils.kt72
-rw-r--r--core/tests/bugreports/Android.bp1
-rw-r--r--core/tests/coretests/AndroidManifest.xml3
-rw-r--r--core/tests/coretests/res/values/strings.xml6
-rw-r--r--core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml22
-rw-r--r--core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java98
-rw-r--r--core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java321
-rw-r--r--core/tests/coretests/src/android/app/admin/PasswordPolicyTest.java196
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java4
-rw-r--r--core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java75
-rw-r--r--core/tests/coretests/src/android/app/usage/UsageStatsTest.java10
-rw-r--r--core/tests/coretests/src/android/content/pm/AndroidTelephonyCommonUpdaterTest.java140
-rw-r--r--core/tests/coretests/src/android/content/pm/PackageBuilder.java8
-rw-r--r--core/tests/coretests/src/android/provider/DeviceConfigTest.java239
-rw-r--r--core/tests/coretests/src/android/util/TimestampedValueTest.java29
-rw-r--r--core/tests/coretests/src/android/view/InsetsFlagsTest.java71
-rw-r--r--core/tests/coretests/src/android/view/ViewGroupTest.java71
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java4
-rw-r--r--core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java9
-rw-r--r--core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java4
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java2
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java31
-rw-r--r--core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java8
-rw-r--r--core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java182
-rw-r--r--core/tests/featureflagtests/OWNERS2
-rw-r--r--core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java4
-rw-r--r--data/etc/Android.bp11
-rw-r--r--data/etc/TEST_MAPPING13
-rw-r--r--data/etc/car/com.google.android.car.kitchensink.xml13
-rwxr-xr-xdata/etc/framework-sysconfig.xml2
-rw-r--r--data/etc/hiddenapi-package-whitelist.xml4
-rw-r--r--data/etc/platform.xml6
-rw-r--r--data/etc/preinstalled-packages-platform.xml90
-rw-r--r--data/etc/privapp-permissions-platform.xml11
-rw-r--r--data/etc/services.core.protolog.json2158
-rw-r--r--data/fonts/Android.mk4
-rw-r--r--data/fonts/fonts.xml10
-rw-r--r--graphics/java/android/graphics/BLASTBufferQueue.java67
-rw-r--r--graphics/java/android/graphics/Bitmap.java47
-rw-r--r--graphics/java/android/graphics/ColorSpace.java4
-rw-r--r--graphics/java/android/graphics/ImageDecoder.java6
-rw-r--r--graphics/java/android/graphics/RenderNode.java20
-rw-r--r--graphics/java/android/graphics/drawable/GradientDrawable.java147
-rw-r--r--graphics/java/android/graphics/pdf/TEST_MAPPING7
-rw-r--r--keystore/java/android/security/KeyChain.java10
-rw-r--r--keystore/java/android/security/keystore/AttestationUtils.java14
-rw-r--r--libs/androidfw/Android.bp6
-rw-r--r--libs/androidfw/ApkAssets.cpp97
-rw-r--r--libs/androidfw/Asset.cpp20
-rw-r--r--libs/androidfw/AssetManager2.cpp427
-rw-r--r--libs/androidfw/Idmap.cpp257
-rw-r--r--libs/androidfw/LoadedArsc.cpp64
-rw-r--r--libs/androidfw/ResourceTypes.cpp50
-rw-r--r--libs/androidfw/TEST_MAPPING3
-rw-r--r--libs/androidfw/include/androidfw/ApkAssets.h48
-rw-r--r--libs/androidfw/include/androidfw/Asset.h5
-rw-r--r--libs/androidfw/include/androidfw/AssetManager2.h114
-rw-r--r--libs/androidfw/include/androidfw/Idmap.h158
-rw-r--r--libs/androidfw/include/androidfw/LoadedArsc.h38
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h98
-rw-r--r--libs/androidfw/tests/ApkAssets_test.cpp33
-rw-r--r--libs/androidfw/tests/AssetManager2_test.cpp12
-rw-r--r--libs/androidfw/tests/Idmap_test.cpp269
-rw-r--r--libs/androidfw/tests/LoadedArsc_test.cpp94
-rw-r--r--libs/androidfw/tests/data/loader/AndroidManifest.xml20
-rwxr-xr-xlibs/androidfw/tests/data/loader/build27
-rw-r--r--libs/androidfw/tests/data/loader/res/values/public.xml19
-rw-r--r--libs/androidfw/tests/data/loader/res/values/values.xml19
-rw-r--r--libs/androidfw/tests/data/loader/resources.arscbin0 -> 652 bytes
-rw-r--r--libs/androidfw/tests/data/overlay/AndroidManifest.xml8
-rw-r--r--libs/androidfw/tests/data/overlay/R.h38
-rwxr-xr-xlibs/androidfw/tests/data/overlay/build11
-rw-r--r--libs/androidfw/tests/data/overlay/overlay.apkbin5211 -> 2988 bytes
-rw-r--r--libs/androidfw/tests/data/overlay/overlay.idmapbin0 -> 1077 bytes
-rw-r--r--libs/androidfw/tests/data/overlay/res/layout/hello_view.xml20
-rw-r--r--libs/androidfw/tests/data/overlay/res/values/values.xml14
-rw-r--r--libs/androidfw/tests/data/overlay/res/xml/overlays.xml47
-rw-r--r--libs/androidfw/tests/data/overlayable/R.h37
-rwxr-xr-xlibs/androidfw/tests/data/overlayable/build5
-rw-r--r--libs/androidfw/tests/data/overlayable/overlayable.apkbin3443 -> 6306 bytes
-rw-r--r--libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml20
-rw-r--r--libs/androidfw/tests/data/overlayable/res/values/overlayable.xml54
-rw-r--r--libs/androidfw/tests/data/overlayable/res/values/public.xml17
-rw-r--r--libs/androidfw/tests/data/overlayable/res/values/values.xml11
-rw-r--r--libs/androidfw/tests/data/system/R.h7
-rw-r--r--libs/androidfw/tests/data/system/res/values/public.xml20
-rw-r--r--libs/androidfw/tests/data/system/res/values/values.xml20
-rw-r--r--libs/androidfw/tests/data/system/system.apkbin1624 -> 1976 bytes
-rw-r--r--libs/hwui/Android.bp6
-rw-r--r--libs/hwui/DamageAccumulator.h2
-rw-r--r--libs/hwui/RecordingCanvas.h1
-rw-r--r--libs/hwui/RenderNode.cpp14
-rw-r--r--libs/hwui/RenderNode.h3
-rw-r--r--libs/hwui/pipeline/skia/LayerDrawable.cpp25
-rw-r--r--libs/hwui/pipeline/skia/SkiaDisplayList.h1
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp1
-rw-r--r--libs/hwui/renderthread/CacheManager.cpp2
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp4
-rw-r--r--libs/hwui/renderthread/CanvasContext.h3
-rw-r--r--libs/hwui/utils/LinearAllocator.h1
-rw-r--r--libs/input/Android.bp2
-rw-r--r--libs/input/PointerController.cpp6
-rw-r--r--libs/input/SpriteController.cpp51
-rw-r--r--libs/input/SpriteController.h18
-rw-r--r--libs/input/tests/Android.bp2
-rw-r--r--location/java/android/location/AbstractListenerManager.java141
-rw-r--r--location/java/android/location/BatchedLocationCallbackTransport.java66
-rw-r--r--location/java/android/location/GnssMeasurementCallbackTransport.java97
-rw-r--r--location/java/android/location/GnssNavigationMessageCallbackTransport.java79
-rw-r--r--location/java/android/location/ILocationManager.aidl29
-rw-r--r--location/java/android/location/LocalListenerHelper.java134
-rw-r--r--location/java/android/location/Location.java11
-rw-r--r--location/java/android/location/LocationManager.java3233
-rw-r--r--location/java/android/location/LocationProvider.java20
-rw-r--r--location/lib/Android.bp7
-rw-r--r--media/Android.bp20
-rw-r--r--media/apex/java/android/media/DataSourceDesc.java384
-rw-r--r--media/apex/java/android/media/FileDataSourceDesc.java134
-rw-r--r--media/apex/java/android/media/Media2HTTPConnection.java385
-rw-r--r--media/apex/java/android/media/Media2HTTPService.java58
-rw-r--r--media/apex/java/android/media/Media2Utils.java78
-rw-r--r--media/apex/java/android/media/MediaPlayer2.java5507
-rw-r--r--media/apex/java/android/media/MediaPlayer2Utils.java43
-rw-r--r--media/apex/java/android/media/UriDataSourceDesc.java80
-rw-r--r--media/java/android/media/AudioAttributes.java4
-rw-r--r--media/java/android/media/AudioManager.java27
-rw-r--r--media/java/android/media/AudioSystem.java47
-rw-r--r--media/java/android/media/ExifInterface.java345
-rw-r--r--media/java/android/media/IAudioService.aidl4
-rw-r--r--media/java/android/media/IMediaRoute2Provider.aidl2
-rw-r--r--media/java/android/media/IMediaRouterService.aidl10
-rw-r--r--media/java/android/media/JetPlayer.java12
-rw-r--r--media/java/android/media/MediaCodec.java15
-rw-r--r--media/java/android/media/MediaCodecInfo.java17
-rw-r--r--media/java/android/media/MediaFile.java2
-rw-r--r--media/java/android/media/MediaFormat.java9
-rw-r--r--media/java/android/media/MediaMetadataRetriever.java136
-rw-r--r--media/java/android/media/MediaPlayer.java20
-rw-r--r--media/java/android/media/MediaRoute2Info.java124
-rw-r--r--media/java/android/media/MediaRoute2ProviderService.java28
-rw-r--r--media/java/android/media/MediaRouter.java29
-rw-r--r--media/java/android/media/MediaRouter2.java79
-rw-r--r--media/java/android/media/MediaRouter2Manager.java50
-rw-r--r--media/java/android/media/MediaScanner.java1960
-rw-r--r--media/java/android/media/RingtoneManager.java63
-rw-r--r--media/java/android/media/ThumbnailUtils.java18
-rw-r--r--media/java/android/media/audiopolicy/AudioPolicy.java4
-rw-r--r--media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl4
-rw-r--r--media/java/android/media/midi/MidiDeviceInfo.aidl2
-rw-r--r--media/jni/Android.bp98
-rw-r--r--media/jni/JetPlayer.cpp471
-rw-r--r--media/jni/JetPlayer.h126
-rw-r--r--media/jni/android_media_DataSourceCallback.cpp159
-rw-r--r--media/jni/android_media_DataSourceCallback.h70
-rw-r--r--media/jni/android_media_JetPlayer.cpp538
-rw-r--r--media/jni/android_media_MediaDrm.h5
-rw-r--r--media/jni/android_media_MediaMetadataRetriever.cpp19
-rw-r--r--media/jni/android_media_MediaMetricsJNI.cpp3
-rw-r--r--media/jni/android_media_MediaPlayer.cpp12
-rw-r--r--media/jni/android_media_MediaPlayer2.cpp1477
-rw-r--r--media/jni/android_media_MediaScanner.cpp468
-rw-r--r--media/jni/soundpool/Android.bp7
-rw-r--r--media/jni/soundpool/Sound.cpp241
-rw-r--r--media/jni/soundpool/Sound.h93
-rw-r--r--media/jni/soundpool/SoundDecoder.cpp115
-rw-r--r--media/jni/soundpool/SoundDecoder.h51
-rw-r--r--media/jni/soundpool/SoundManager.cpp104
-rw-r--r--media/jni/soundpool/SoundManager.h110
-rw-r--r--media/jni/soundpool/SoundPool.cpp1160
-rw-r--r--media/jni/soundpool/SoundPool.h248
-rw-r--r--media/jni/soundpool/SoundPoolThread.cpp114
-rw-r--r--media/jni/soundpool/SoundPoolThread.h66
-rw-r--r--media/jni/soundpool/Stream.cpp448
-rw-r--r--media/jni/soundpool/Stream.h150
-rw-r--r--media/jni/soundpool/StreamManager.cpp407
-rw-r--r--media/jni/soundpool/StreamManager.h467
-rw-r--r--media/lib/signer/Android.bp7
-rw-r--r--media/native/midi/Android.bp7
-rw-r--r--media/native/midi/MidiDeviceInfo.cpp138
-rw-r--r--media/native/midi/MidiDeviceInfo.h81
-rw-r--r--media/native/midi/amidi.cpp2
-rw-r--r--media/proto/Android.bp20
-rw-r--r--media/proto/jarjar-rules.txt2
-rw-r--r--media/proto/mediaplayer2.proto53
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaFileTest.java2
-rw-r--r--media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java49
-rw-r--r--media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java211
-rw-r--r--mime/Android.bp121
-rw-r--r--mime/TEST_MAPPING7
-rw-r--r--mime/jarjar-rules.txt1
-rw-r--r--mime/java-res/android.mime.types146
-rw-r--r--mime/java-res/vendor.mime.types49
-rw-r--r--mime/java/android/content/type/DefaultMimeMapFactory.java106
-rw-r--r--mms/OWNERS14
-rw-r--r--mms/java/android/telephony/MmsManager.java118
-rw-r--r--mms/java/com/android/internal/telephony/IMms.aidl (renamed from telephony/java/com/android/internal/telephony/IMms.aidl)0
-rw-r--r--native/android/Android.bp33
-rw-r--r--native/android/aidl/com/android/internal/compat/IPlatformCompatNative.aidl93
-rw-r--r--native/android/surface_control.cpp2
-rw-r--r--opengl/java/android/opengl/GLUtils.java30
-rw-r--r--packages/BackupEncryption/Android.bp2
-rw-r--r--packages/BackupEncryption/AndroidManifest.xml11
-rw-r--r--packages/BackupEncryption/proto/key_value_listing.proto40
-rw-r--r--packages/BackupEncryption/proto/key_value_pair.proto31
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/BackupEncryptionService.java63
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/EncryptionKeyHelper.java81
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/FullBackupDataProcessor.java109
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/FullRestoreDataProcessor.java51
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/FullRestoreDownloader.java47
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/KeyValueEncrypter.java143
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/StreamUtils.java64
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/BackupFileBuilder.java232
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ProtoStore.java174
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/client/CryptoBackupServer.java67
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/client/UnexpectedActiveSecondaryOnServerException.java30
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutput.java111
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java77
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/ActiveSecondaryNotInKeychainException.java27
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/BackupFileDecryptorTask.java378
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/ClearCryptoStateTask.java71
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java8
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedBackupTask.java243
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessor.java210
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTask.java197
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullRestoreTask.java137
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedKvBackupTask.java244
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedKvRestoreTask.java139
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/FullRestoreToFileTask.java87
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/InitializeRecoverableSecondaryKeyTask.java157
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/KvBackupEncrypter.java179
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/MalformedEncryptedFileException.java24
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/MessageDigestMismatchException.java27
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/NoActiveSecondaryKeyException.java28
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/NonIncrementalBackupRequiredException.java25
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTask.java270
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/SizeQuotaExceededException.java24
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/UnsupportedEncryptedFileException.java28
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransport.java211
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransportManager.java110
-rw-r--r--packages/BackupEncryption/test/robolectric-integration/Android.bp34
-rw-r--r--packages/BackupEncryption/test/robolectric-integration/AndroidManifest.xml24
-rw-r--r--packages/BackupEncryption/test/robolectric-integration/config/robolectric.properties17
-rw-r--r--packages/BackupEncryption/test/robolectric-integration/src/com/android/server/backup/encryption/RoundTripTest.java321
-rw-r--r--packages/BackupEncryption/test/robolectric/Android.bp2
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/StreamUtilsTest.java75
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/BackupFileBuilderTest.java614
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ProtoStoreTest.java264
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutputTest.java164
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/KeyValueListingBuilderTest.java110
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/BackupFileDecryptorTaskTest.java583
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/ClearCryptoStateTaskTest.java109
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedBackupTaskTest.java397
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessorTest.java387
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTaskTest.java234
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullRestoreTaskTest.java129
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedKvBackupTaskTest.java356
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedKvRestoreTaskTest.java185
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/FullRestoreToFileTaskTest.java128
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/InitializeRecoverableSecondaryKeyTaskTest.java165
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/KvBackupEncrypterTest.java287
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTaskTest.java363
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/DiffScriptProcessor.java256
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/QueuingNonAutomaticExecutorService.java83
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/testing/CryptoTestUtils.java132
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/testing/TestFileUtils.java31
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/testing/fakes/FakeCryptoBackupServer.java90
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/testing/fakes/FakeCryptoBackupServerTest.java143
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/DataEntity.java100
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowBackupDataInput.java106
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java66
-rw-r--r--packages/BackupEncryption/test/unittest/Android.bp22
-rw-r--r--packages/BackupEncryption/test/unittest/AndroidManifest.xml27
-rw-r--r--packages/BackupEncryption/test/unittest/AndroidTest.xml29
-rw-r--r--packages/BackupEncryption/test/unittest/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransportManagerTest.java120
-rw-r--r--packages/BackupEncryption/test/unittest/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransportTest.java180
-rw-r--r--packages/BackupRestoreConfirmation/res/values-fi/strings.xml2
-rw-r--r--packages/CarSystemUI/res/layout/car_left_navigation_bar.xml22
-rw-r--r--packages/CarSystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml8
-rw-r--r--packages/CarSystemUI/res/layout/car_navigation_bar.xml34
-rw-r--r--packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml6
-rw-r--r--packages/CarSystemUI/res/layout/car_right_navigation_bar.xml25
-rw-r--r--packages/CarSystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml8
-rw-r--r--packages/CarSystemUI/res/layout/car_top_navigation_bar.xml10
-rw-r--r--packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml147
-rw-r--r--packages/CarSystemUI/res/layout/super_status_bar.xml8
-rw-r--r--packages/CarSystemUI/res/values/config.xml27
-rw-r--r--packages/CarSystemUI/res/values/dimens.xml69
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java34
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java10
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java18
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java10
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java7
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java6
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/navigationbar/car/AssitantButton.java68
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButton.java232
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButtonController.java206
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java381
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java143
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java160
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/navigationbar/car/FacetButtonTaskStackListener.java65
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/navigationbar/car/NavigationBarViewFactory.java147
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/AssitantButton.java69
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java5
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java232
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java205
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java142
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java160
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java554
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java69
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java3
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java21
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java22
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java16
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java10
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java11
-rw-r--r--packages/CarrierDefaultApp/OWNERS3
-rw-r--r--packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java5
-rw-r--r--packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java16
-rw-r--r--packages/EncryptedLocalTransport/Android.bp27
-rw-r--r--packages/EncryptedLocalTransport/AndroidManifest.xml36
-rw-r--r--packages/EncryptedLocalTransport/proguard.flags2
-rw-r--r--packages/EncryptedLocalTransport/src/com/android/encryptedlocaltransport/EncryptedLocalTransport.java109
-rw-r--r--packages/EncryptedLocalTransport/src/com/android/encryptedlocaltransport/EncryptedLocalTransportService.java47
-rw-r--r--packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java48
-rw-r--r--packages/LocalTransport/src/com/android/localtransport/LocalTransport.java61
-rw-r--r--packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java4
-rw-r--r--packages/PackageInstaller/res/values-television/themes.xml31
-rw-r--r--packages/PrintSpooler/TEST_MAPPING12
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java8
-rw-r--r--packages/SettingsLib/SearchWidget/res/values-gl/strings.xml2
-rw-r--r--packages/SettingsLib/Tile/src/com/android/settingslib/drawer/ActivityTile.java81
-rw-r--r--packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java110
-rw-r--r--packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java133
-rw-r--r--packages/SettingsLib/res/values-af/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-am/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ar/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-as/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-az/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-b+sr+Latn/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-be/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-bg/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-bn/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-bs/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ca/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-cs/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-da/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-de/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-el/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-en-rAU/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-en-rCA/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-en-rGB/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-en-rIN/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-en-rXC/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-es-rUS/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-es/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-et/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-eu/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-fa/arrays.xml4
-rw-r--r--packages/SettingsLib/res/values-fa/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-fi/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-fr-rCA/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-fr/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-gl/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-gu/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-hi/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-hr/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-hu/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-hy/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-in/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-is/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-it/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-iw/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ja/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ka/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-kk/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-km/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-kn/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ko/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ky/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-lo/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-lt/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-lv/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-mk/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ml/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-mn/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-mr/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ms/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-my/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-nb/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ne/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-nl/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-or/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-pa/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-pl/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-pt-rBR/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-pt-rPT/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-pt/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ro/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ru/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-si/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-sk/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-sl/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-sq/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-sr/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-sv/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-sw/arrays.xml2
-rw-r--r--packages/SettingsLib/res/values-sw/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ta/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-te/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-th/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-tl/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-tr/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-uk/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ur/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-uz/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-vi/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-zh-rCN/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-zh-rHK/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-zh-rTW/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-zu/strings.xml4
-rw-r--r--packages/SettingsLib/res/values/strings.xml7
-rw-r--r--packages/SettingsLib/search/Android.bp12
-rw-r--r--packages/SettingsLib/search/AndroidManifest.xml21
-rw-r--r--packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java4
-rw-r--r--packages/SettingsLib/search/src/com/android/settingslib/search/Indexable.java66
-rw-r--r--packages/SettingsLib/search/src/com/android/settingslib/search/SearchIndexableRaw.java64
-rw-r--r--packages/SettingsLib/search/src/com/android/settingslib/search/SearchIndexableResources.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java18
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java8
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java9
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java9
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java9
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java1
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java15
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java23
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java9
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java10
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java2
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java7
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/OWNERS5
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/DeviceSpecificSettings.java36
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java13
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java7
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java26
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java17
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java5
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java14
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java283
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java7
-rw-r--r--packages/SettingsProvider/test/src/android/provider/OWNERS4
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java29
-rw-r--r--packages/Shell/AndroidManifest.xml22
-rw-r--r--packages/Shell/res/values-pt-rBR/strings.xml14
-rw-r--r--packages/Shell/res/values-pt/strings.xml14
-rw-r--r--packages/Shell/src/com/android/shell/BugreportProgressService.java702
-rw-r--r--packages/Shell/src/com/android/shell/BugreportReceiver.java88
-rw-r--r--packages/Shell/src/com/android/shell/RemoteBugreportReceiver.java67
-rw-r--r--packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java6
-rw-r--r--packages/SystemUI/Android.bp38
-rw-r--r--packages/SystemUI/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/OWNERS3
-rw-r--r--packages/SystemUI/README.md6
-rw-r--r--packages/SystemUI/legacy/recents/AndroidManifest.xml51
-rw-r--r--packages/SystemUI/legacy/recents/proguard.flags14
-rw-r--r--packages/SystemUI/legacy/recents/res/anim/recents_fast_toggle_app_home_exit.xml27
-rw-r--r--packages/SystemUI/legacy/recents/res/anim/recents_from_launcher_enter.xml28
-rw-r--r--packages/SystemUI/legacy/recents/res/anim/recents_from_launcher_exit.xml28
-rw-r--r--packages/SystemUI/legacy/recents/res/anim/recents_from_unknown_enter.xml28
-rw-r--r--packages/SystemUI/legacy/recents/res/anim/recents_from_unknown_exit.xml28
-rw-r--r--packages/SystemUI/legacy/recents/res/anim/recents_launch_next_affiliated_task_bounce.xml49
-rw-r--r--packages/SystemUI/legacy/recents/res/anim/recents_launch_next_affiliated_task_source.xml34
-rw-r--r--packages/SystemUI/legacy/recents/res/anim/recents_launch_next_affiliated_task_target.xml28
-rw-r--r--packages/SystemUI/legacy/recents/res/anim/recents_launch_prev_affiliated_task_bounce.xml33
-rw-r--r--packages/SystemUI/legacy/recents/res/anim/recents_launch_prev_affiliated_task_source.xml27
-rw-r--r--packages/SystemUI/legacy/recents/res/anim/recents_launch_prev_affiliated_task_target.xml36
-rw-r--r--packages/SystemUI/legacy/recents/res/anim/recents_to_launcher_enter.xml28
-rw-r--r--packages/SystemUI/legacy/recents/res/anim/recents_to_launcher_exit.xml28
-rw-r--r--packages/SystemUI/legacy/recents/res/drawable-hdpi/recents_lower_gradient.9.pngbin203 -> 0 bytes
-rw-r--r--packages/SystemUI/legacy/recents/res/drawable-hdpi/recents_status_gradient.9.pngbin169 -> 0 bytes
-rw-r--r--packages/SystemUI/legacy/recents/res/drawable-mdpi/recents_lower_gradient.9.pngbin181 -> 0 bytes
-rw-r--r--packages/SystemUI/legacy/recents/res/drawable-mdpi/recents_status_gradient.9.pngbin163 -> 0 bytes
-rw-r--r--packages/SystemUI/legacy/recents/res/drawable-xhdpi/recents_lower_gradient.9.pngbin220 -> 0 bytes
-rw-r--r--packages/SystemUI/legacy/recents/res/drawable-xhdpi/recents_status_gradient.9.pngbin186 -> 0 bytes
-rw-r--r--packages/SystemUI/legacy/recents/res/drawable-xxhdpi/recents_lower_gradient.9.pngbin265 -> 0 bytes
-rw-r--r--packages/SystemUI/legacy/recents/res/drawable-xxhdpi/recents_status_gradient.9.pngbin223 -> 0 bytes
-rw-r--r--packages/SystemUI/legacy/recents/res/drawable-xxhdpi/recents_task_shadow.9.pngbin2540 -> 0 bytes
-rw-r--r--packages/SystemUI/legacy/recents/res/drawable/recents_dismiss_dark.xml29
-rw-r--r--packages/SystemUI/legacy/recents/res/drawable/recents_dismiss_light.xml29
-rw-r--r--packages/SystemUI/legacy/recents/res/drawable/recents_empty.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/drawable/recents_grid_task_view_focus_frame_background.xml19
-rw-r--r--packages/SystemUI/legacy/recents/res/drawable/recents_info_dark.xml24
-rw-r--r--packages/SystemUI/legacy/recents/res/drawable/recents_info_light.xml24
-rw-r--r--packages/SystemUI/legacy/recents/res/drawable/recents_lock_to_app_pin.xml25
-rw-r--r--packages/SystemUI/legacy/recents/res/drawable/recents_lock_to_task_button_bg.xml26
-rw-r--r--packages/SystemUI/legacy/recents/res/drawable/recents_low_ram_stack_button_background.xml22
-rw-r--r--packages/SystemUI/legacy/recents/res/drawable/recents_move_task_fullscreen_dark.xml37
-rw-r--r--packages/SystemUI/legacy/recents/res/drawable/recents_move_task_fullscreen_light.xml37
-rw-r--r--packages/SystemUI/legacy/recents/res/drawable/recents_stack_action_background.xml25
-rw-r--r--packages/SystemUI/legacy/recents/res/interpolator/recents_from_launcher_exit_interpolator.xml23
-rw-r--r--packages/SystemUI/legacy/recents/res/interpolator/recents_launch_next_affiliated_task_bounce_scale.xml20
-rw-r--r--packages/SystemUI/legacy/recents/res/interpolator/recents_launch_prev_affiliated_task_bounce_ydelta.xml20
-rw-r--r--packages/SystemUI/legacy/recents/res/interpolator/recents_to_launcher_enter_interpolator.xml23
-rw-r--r--packages/SystemUI/legacy/recents/res/layout/recents.xml44
-rw-r--r--packages/SystemUI/legacy/recents/res/layout/recents_empty.xml30
-rw-r--r--packages/SystemUI/legacy/recents/res/layout/recents_grid_task_view.xml35
-rw-r--r--packages/SystemUI/legacy/recents/res/layout/recents_incompatible_app_overlay.xml31
-rw-r--r--packages/SystemUI/legacy/recents/res/layout/recents_low_ram_stack_action_button.xml34
-rw-r--r--packages/SystemUI/legacy/recents/res/layout/recents_search_bar.xml30
-rw-r--r--packages/SystemUI/legacy/recents/res/layout/recents_stack_action_button.xml37
-rw-r--r--packages/SystemUI/legacy/recents/res/layout/recents_task_view.xml35
-rw-r--r--packages/SystemUI/legacy/recents/res/layout/recents_task_view_header.xml75
-rw-r--r--packages/SystemUI/legacy/recents/res/layout/recents_task_view_header_overlay.xml52
-rw-r--r--packages/SystemUI/legacy/recents/res/layout/recents_task_view_header_progress_bar.xml22
-rw-r--r--packages/SystemUI/legacy/recents/res/layout/recents_task_view_incompatible_app_toast.xml26
-rw-r--r--packages/SystemUI/legacy/recents/res/layout/recents_task_view_lock_to_app.xml34
-rw-r--r--packages/SystemUI/legacy/recents/res/values-af/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-am/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-ar/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-as/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-az/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-b+sr+Latn/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-be/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-bg/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-bn/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-bs/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-ca/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-cs/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-da/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-de/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-el/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-en-rAU/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-en-rCA/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-en-rGB/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-en-rIN/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-en-rXC/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-es-rUS/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-es/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-et/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-eu/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-fa/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-fi/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-fr-rCA/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-fr/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-gl/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-gu/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-hi/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-hr/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-hu/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-hy/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-in/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-is/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-it/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-iw/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-ja/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-ka/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-kk/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-km/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-kn/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-ko/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-ky/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-lo/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-lt/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-lv/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-mk/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-ml/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-mn/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-mr/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-ms/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-my/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-nb/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-ne/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-nl/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-or/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-pa/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-pl/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-pt-rBR/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-pt-rPT/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-pt/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-ro/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-ru/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-si/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-sk/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-sl/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-sq/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-sr/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-sv/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-sw/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-sw600dp/dimens.xml21
-rw-r--r--packages/SystemUI/legacy/recents/res/values-ta/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-te/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-th/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-tl/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-tr/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-uk/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-ur/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-uz/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-vi/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-zh-rCN/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-zh-rHK/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-zh-rTW/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values-zu/strings.xml43
-rw-r--r--packages/SystemUI/legacy/recents/res/values/attrs.xml26
-rw-r--r--packages/SystemUI/legacy/recents/res/values/colors.xml51
-rw-r--r--packages/SystemUI/legacy/recents/res/values/config.xml75
-rw-r--r--packages/SystemUI/legacy/recents/res/values/dimens.xml110
-rw-r--r--packages/SystemUI/legacy/recents/res/values/dimens_grid.xml28
-rw-r--r--packages/SystemUI/legacy/recents/res/values/strings.xml67
-rw-r--r--packages/SystemUI/legacy/recents/res/values/styles.xml55
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/Constants.java34
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl38
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl36
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/LegacyRecentsImpl.java750
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsActivity.java858
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsActivityLaunchState.java53
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsConfiguration.java126
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsDebugFlags.java29
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsImpl.java1118
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsImplProxy.java161
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsSystemUser.java121
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsSystemUserService.java53
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/EventBus.java763
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/AppTransitionFinishedEvent.java25
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java34
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java38
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/DismissRecentsToHomeAnimationStarted.java31
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/DockedFirstAnimationFrameEvent.java25
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/DockedTopTaskEvent.java34
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationCompletedEvent.java28
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/EnterRecentsWindowLastAnimationFrameEvent.java22
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ExitRecentsWindowFirstAnimationFrameEvent.java28
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/HideRecentsEvent.java34
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java36
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchMostRecentTaskRequestEvent.java26
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchNextTaskRequestEvent.java27
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskEvent.java56
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskFailedEvent.java26
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskStartedEvent.java36
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskSucceededEvent.java31
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java38
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/PackagesChangedEvent.java37
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/RecentsActivityStartingEvent.java26
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ShowEmptyViewEvent.java25
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ShowStackActionButtonEvent.java32
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java37
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ToggleRecentsEvent.java26
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/UndockingTaskEvent.java28
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ActivityPinnedEvent.java31
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ActivityUnpinnedEvent.java25
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ExpandPipEvent.java25
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/HidePipMenuEvent.java26
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java36
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java35
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/SetWaitingForTransitionStartEvent.java31
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ShowUserToastEvent.java33
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/AllTaskViewsDismissedEvent.java31
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DeleteTaskDataEvent.java33
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DismissAllTaskViewsEvent.java27
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DismissTaskViewEvent.java32
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DraggingInRecentsEndedEvent.java15
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DraggingInRecentsEvent.java15
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/HideIncompatibleAppOverlayEvent.java26
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/RecentsDrawnEvent.java26
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/RecentsGrowingEvent.java26
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java32
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/ShowIncompatibleAppOverlayEvent.java26
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/StackViewScrolledEvent.java37
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/TaskSnapshotChangedEvent.java34
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/TaskViewDismissedEvent.java38
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/UserInteractionEvent.java26
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java36
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragEndCancelledEvent.java38
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java38
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java46
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java40
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/DismissFocusedTaskViewEvent.java27
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java26
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/FocusPreviousTaskViewEvent.java26
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/NavigateTaskViewEvent.java49
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/DozeTrigger.java106
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/FreePathInterpolator.java177
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java130
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SysUiTaskStackChangeListener.java35
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SystemServicesProxy.java535
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/BackgroundTaskLoader.java169
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/FilteredTaskList.java125
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/HighResThumbnailLoader.java247
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java216
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/RecentsTaskLoader.java421
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskFilter.java28
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskKeyStrongCache.java73
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskResourceLoadQueue.java61
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskStack.java402
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/utilities/AnimationProps.java229
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/utilities/Utilities.java339
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/AnimateableViewBounds.java135
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/DockState.java351
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/DropTarget.java31
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/FakeShadowDrawable.java290
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/FixedSizeFrameLayout.java97
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/FixedSizeImageView.java77
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsEntrancePathInterpolator.java47
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsTransitionComposer.java175
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java1077
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java286
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/SystemBarScrimViews.java194
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java705
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java1283
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackView.java2291
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackViewScroller.java347
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java706
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskView.java737
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java94
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewHeader.java685
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewThumbnail.java392
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewTransform.java203
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/ViewPool.java86
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/AnimateableGridViewBounds.java32
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/GridTaskView.java72
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java192
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java325
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java141
-rw-r--r--packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/lowram/TaskStackLowRamLayoutAlgorithm.java261
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/HomeControlsPlugin.java5
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/NotificationPersonExtractorPlugin.java72
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java2
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java7
-rw-r--r--packages/SystemUI/proguard.flags4
-rw-r--r--packages/SystemUI/res-keyguard/values-af/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-am/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-ar/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-as/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-az/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-be/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-bg/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-bn/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-bs/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-ca/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-cs/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-da/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-de/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-el/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-en-rAU/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-en-rCA/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-en-rGB/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-en-rIN/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-en-rXC/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-es-rUS/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-es/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-et/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-eu/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-fa/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-fi/strings.xml4
-rw-r--r--packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-fr/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-gl/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-gu/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-hi/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-hr/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-hu/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-hy/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-in/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-is/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-it/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-iw/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-ja/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-ka/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-kk/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-km/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-kn/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-ko/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-ky/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-lo/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-lt/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-lv/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-mk/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-ml/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-mn/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-mr/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-ms/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-my/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-nb/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-ne/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-nl/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-or/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-pa/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-pl/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-pt/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-ro/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-ru/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-si/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-sk/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-sl/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-sq/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-sr/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-sv/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-sw/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-ta/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-te/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-th/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-tl/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-tr/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-uk/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-ur/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-uz/strings.xml4
-rw-r--r--packages/SystemUI/res-keyguard/values-vi/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-zu/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values/strings.xml2
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/tv_card_gradient_protection.pngbin0 -> 139 bytes
-rw-r--r--packages/SystemUI/res/drawable/auth_dialog_lock.xml27
-rw-r--r--packages/SystemUI/res/drawable/circle_red.xml21
-rw-r--r--packages/SystemUI/res/drawable/ic_lightbulb_outline_gm2_24px.xml9
-rw-r--r--packages/SystemUI/res/drawable/tv_bg_item_app_info.xml22
-rw-r--r--packages/SystemUI/res/drawable/tv_gradient_protection.xml22
-rw-r--r--packages/SystemUI/res/drawable/tv_ic_mic_white.xml25
-rw-r--r--packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml113
-rw-r--r--packages/SystemUI/res/layout/auth_container_view.xml2
-rw-r--r--packages/SystemUI/res/layout/auth_credential_password_view.xml101
-rw-r--r--packages/SystemUI/res/layout/auth_credential_pattern_view.xml97
-rw-r--r--packages/SystemUI/res/layout/home_controls.xml13
-rw-r--r--packages/SystemUI/res/layout/navigation_bar.xml15
-rw-r--r--packages/SystemUI/res/layout/people_strip.xml239
-rw-r--r--packages/SystemUI/res/layout/qqs_media_panel.xml100
-rw-r--r--packages/SystemUI/res/layout/qs_carrier_group.xml3
-rw-r--r--packages/SystemUI/res/layout/qs_media_panel.xml124
-rw-r--r--packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml2
-rw-r--r--packages/SystemUI/res/layout/rounded_corners.xml12
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded.xml24
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml6
-rw-r--r--packages/SystemUI/res/layout/super_status_bar.xml2
-rw-r--r--packages/SystemUI/res/layout/tv_item_app_info.xml41
-rw-r--r--packages/SystemUI/res/layout/tv_status_bar_audio_recording.xml63
-rw-r--r--packages/SystemUI/res/values-af/strings.xml10
-rw-r--r--packages/SystemUI/res/values-am/strings.xml12
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml10
-rw-r--r--packages/SystemUI/res/values-as/strings.xml11
-rw-r--r--packages/SystemUI/res/values-az/strings.xml10
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml10
-rw-r--r--packages/SystemUI/res/values-be/strings.xml10
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml10
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml10
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml12
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml21
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml10
-rw-r--r--packages/SystemUI/res/values-da/strings.xml10
-rw-r--r--packages/SystemUI/res/values-de/strings.xml10
-rw-r--r--packages/SystemUI/res/values-el/strings.xml14
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings.xml10
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml10
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings.xml10
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings.xml10
-rw-r--r--packages/SystemUI/res/values-en-rXC/strings.xml10
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml10
-rw-r--r--packages/SystemUI/res/values-es/strings.xml11
-rw-r--r--packages/SystemUI/res/values-et/strings.xml10
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml12
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml12
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml10
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml10
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml10
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml10
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml12
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml11
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml10
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml10
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml14
-rw-r--r--packages/SystemUI/res/values-in/strings.xml10
-rw-r--r--packages/SystemUI/res/values-is/strings.xml10
-rw-r--r--packages/SystemUI/res/values-it/strings.xml18
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml11
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml10
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml10
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml12
-rw-r--r--packages/SystemUI/res/values-km/strings.xml12
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml16
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml12
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml14
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml10
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml12
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml10
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml12
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml19
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml10
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml11
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml10
-rw-r--r--packages/SystemUI/res/values-my/strings.xml16
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml10
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml15
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml10
-rw-r--r--packages/SystemUI/res/values-or/strings.xml19
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml11
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml10
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml10
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml12
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml10
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml12
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml10
-rw-r--r--packages/SystemUI/res/values-si/strings.xml12
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml12
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml10
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml12
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml10
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml10
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml14
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml40
-rw-r--r--packages/SystemUI/res/values-te/strings.xml11
-rw-r--r--packages/SystemUI/res/values-th/strings.xml10
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml10
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml10
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml12
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml11
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml10
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml12
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml10
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml10
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml10
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml14
-rw-r--r--packages/SystemUI/res/values/arrays_tv.xml4
-rw-r--r--packages/SystemUI/res/values/colors_tv.xml10
-rw-r--r--packages/SystemUI/res/values/config.xml4
-rw-r--r--packages/SystemUI/res/values/dimens.xml12
-rw-r--r--packages/SystemUI/res/values/ids.xml3
-rw-r--r--packages/SystemUI/res/values/strings.xml33
-rw-r--r--packages/SystemUI/res/values/styles.xml6
-rw-r--r--packages/SystemUI/shared/Android.bp2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java9
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java11
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java13
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java9
-rw-r--r--packages/SystemUI/src/com/android/keyguard/CarrierTextController.java27
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java21
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java26
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java19
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java12
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java7
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/ActivityBinder.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/ComponentBinder.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/ContextComponentHelper.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/ContextComponentResolver.java80
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/DependencyBinder.java236
-rw-r--r--packages/SystemUI/src/com/android/systemui/DependencyProvider.java241
-rw-r--r--packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/ImageWallpaper.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/LatencyTester.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java270
-rw-r--r--packages/SystemUI/src/com/android/systemui/ServiceBinder.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/SysUIToast.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemBars.java88
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUI.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIBinder.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIDefaultModule.java85
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIFactory.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIModule.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIRootComponent.java95
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIService.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/VendorServices.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistHandleViewController.java191
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistModule.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/ui/EdgeLight.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java155
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java225
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java115
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java121
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java105
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java265
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java118
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/Utils.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java65
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/ActivityBinder.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/ComponentBinder.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java82
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java252
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java234
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/ServiceBinder.java52
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java130
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java83
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java88
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java65
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java92
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/qualifiers/BgHandler.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/qualifiers/BgLooper.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainHandler.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainLooper.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainResources.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeEvent.java154
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java108
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeHost.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeLog.java421
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java76
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeService.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeUi.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java107
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/Event.java67
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/RichEvent.java107
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/SysuiLog.java163
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipUI.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerUI.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt133
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java91
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java301
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java121
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java203
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/TileLayout.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ControlsTile.java197
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/Recents.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java230
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NewNotifPipeline.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java102
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt73
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/DismissedByUserStats.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java426
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionListener.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLifetimeExtender.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilder.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java118
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java114
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java160
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java125
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt272
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt133
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/ViewPipeline.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java2
-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/NotificationGuts.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java115
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java130
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java64
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java465
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java52
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java165
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java10
-rwxr-xr-xpackages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java201
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java63
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java120
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java23
-rwxr-xr-xpackages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java178
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java1092
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java395
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java564
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java229
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java183
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java177
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java326
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java108
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java203
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/OWNERS8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/leak/DumpTruck.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java134
-rwxr-xr-xpackages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java66
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java56
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java83
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleViewControllerTest.java104
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java95
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java126
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java256
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java187
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java120
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java62
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java53
-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.java95
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/log/RichEventTest.java69
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java82
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupTest.java33
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt128
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java85
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NavigationBarControllerTest.java31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryHelper.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java81
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt69
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java625
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java99
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java8
-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/row/wrapper/NotificationMediaTemplateViewWrapperTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java119
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DoubleTapHelperTest.java369
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java71
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java224
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java143
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java33
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarInflaterViewTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java40
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java52
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java381
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java33
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java44
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java526
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java88
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyButtonViewTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java146
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java53
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java83
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java95
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java32
-rw-r--r--proto/src/metrics_constants/metrics_constants.proto6
-rw-r--r--proto/src/system_messages.proto6
-rw-r--r--services/Android.bp5
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java119
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java857
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java66
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java616
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java18
-rw-r--r--services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java194
-rw-r--r--services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java197
-rw-r--r--services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java8
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java3
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java23
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java145
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java26
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java5
-rw-r--r--services/art-profile529
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/SaveUi.java27
-rw-r--r--services/backup/Android.bp1
-rw-r--r--services/backup/backuplib/Android.bp5
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/TransportManager.java (renamed from services/backup/java/com/android/server/backup/TransportManager.java)0
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/DelegatingTransport.java414
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/OnTransportRegisteredListener.java (renamed from services/backup/java/com/android/server/backup/transport/OnTransportRegisteredListener.java)0
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java (renamed from services/backup/java/com/android/server/backup/transport/TransportClient.java)0
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/TransportClientManager.java206
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/TransportConnectionListener.java (renamed from services/backup/java/com/android/server/backup/transport/TransportConnectionListener.java)0
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/TransportNotAvailableException.java (renamed from services/backup/java/com/android/server/backup/transport/TransportNotAvailableException.java)0
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/TransportNotRegisteredException.java (renamed from services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java)0
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/TransportStats.java (renamed from services/backup/java/com/android/server/backup/transport/TransportStats.java)0
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/TransportUtils.java (renamed from services/backup/java/com/android/server/backup/transport/TransportUtils.java)0
-rw-r--r--services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java7
-rw-r--r--services/backup/java/com/android/server/backup/transport/TransportClientManager.java148
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java17
-rw-r--r--services/core/Android.bp78
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java812
-rw-r--r--services/core/java/android/os/UserManagerInternal.java250
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java52
-rw-r--r--services/core/java/com/android/server/AnimationThread.java2
-rw-r--r--services/core/java/com/android/server/AppStateTracker.java7
-rw-r--r--services/core/java/com/android/server/BatteryService.java2
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java164
-rw-r--r--services/core/java/com/android/server/ContextHubSystemService.java8
-rw-r--r--services/core/java/com/android/server/GnssManagerService.java812
-rw-r--r--services/core/java/com/android/server/LocationManagerService.java764
-rw-r--r--services/core/java/com/android/server/LocationManagerServiceUtils.java163
-rw-r--r--services/core/java/com/android/server/MasterClearReceiver.java2
-rw-r--r--services/core/java/com/android/server/PersistentDataBlockService.java2
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java50
-rw-r--r--services/core/java/com/android/server/SystemServerInitThreadPool.java88
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java143
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java2
-rw-r--r--services/core/java/com/android/server/UiThread.java18
-rw-r--r--services/core/java/com/android/server/VibratorService.java27
-rw-r--r--services/core/java/com/android/server/Watchdog.java1
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java95
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java26
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java393
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java35
-rw-r--r--services/core/java/com/android/server/am/AppErrorDialog.java2
-rw-r--r--services/core/java/com/android/server/am/AppNotRespondingDialog.java2
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java24
-rw-r--r--services/core/java/com/android/server/am/ContentProviderRecord.java4
-rw-r--r--services/core/java/com/android/server/am/MemoryStatUtil.java67
-rw-r--r--services/core/java/com/android/server/am/OomAdjProfiler.java46
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java724
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java133
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java114
-rw-r--r--services/core/java/com/android/server/am/ProcessStatsService.java2
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java3
-rw-r--r--services/core/java/com/android/server/am/TEST_MAPPING1
-rw-r--r--services/core/java/com/android/server/am/UserController.java68
-rw-r--r--services/core/java/com/android/server/am/UserSwitchingDialog.java2
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java1037
-rw-r--r--services/core/java/com/android/server/appop/TEST_MAPPING17
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java1
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java113
-rw-r--r--services/core/java/com/android/server/audio/BtHelper.java2
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java898
-rw-r--r--services/core/java/com/android/server/biometrics/Utils.java44
-rw-r--r--services/core/java/com/android/server/biometrics/face/FaceAuthenticator.java75
-rw-r--r--services/core/java/com/android/server/biometrics/face/FaceService.java11
-rw-r--r--services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java75
-rw-r--r--services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java8
-rw-r--r--services/core/java/com/android/server/biometrics/iris/IrisAuthenticator.java68
-rw-r--r--services/core/java/com/android/server/compat/CompatConfig.java38
-rw-r--r--services/core/java/com/android/server/compat/OWNERS7
-rw-r--r--services/core/java/com/android/server/compat/PlatformCompat.java88
-rw-r--r--services/core/java/com/android/server/compat/PlatformCompatNative.java50
-rw-r--r--services/core/java/com/android/server/connectivity/Nat464Xlat.java7
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkNotificationManager.java21
-rw-r--r--services/core/java/com/android/server/connectivity/Tethering.java73
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java72
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java14
-rw-r--r--services/core/java/com/android/server/display/ColorFade.java14
-rw-r--r--services/core/java/com/android/server/display/DisplayModeDirector.java5
-rw-r--r--services/core/java/com/android/server/display/utils/AmbientFilter.java254
-rw-r--r--services/core/java/com/android/server/display/utils/AmbientFilterFactory.java105
-rw-r--r--services/core/java/com/android/server/display/whitebalance/AmbientFilter.java257
-rw-r--r--services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java21
-rw-r--r--services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java40
-rw-r--r--services/core/java/com/android/server/hdmi/Constants.java8
-rwxr-xr-xservices/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java11
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java52
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java3
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java43
-rw-r--r--services/core/java/com/android/server/hdmi/OneTouchPlayAction.java2
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java117
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java18
-rw-r--r--services/core/java/com/android/server/integrity/OWNERS6
-rw-r--r--services/core/java/com/android/server/integrity/TEST_MAPPING18
-rw-r--r--services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java79
-rw-r--r--services/core/java/com/android/server/integrity/engine/RuleEvaluator.java126
-rw-r--r--services/core/java/com/android/server/integrity/engine/RuleLoader.java61
-rw-r--r--services/core/java/com/android/server/integrity/model/AppInstallMetadata.java169
-rw-r--r--services/core/java/com/android/server/integrity/model/AtomicFormula.java236
-rw-r--r--services/core/java/com/android/server/integrity/model/Formula.java24
-rw-r--r--services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java67
-rw-r--r--services/core/java/com/android/server/integrity/model/OpenFormula.java99
-rw-r--r--services/core/java/com/android/server/integrity/model/Rule.java89
-rw-r--r--services/core/java/com/android/server/location/CallerIdentity.java10
-rw-r--r--services/core/java/com/android/server/location/GeofenceManager.java11
-rw-r--r--services/core/java/com/android/server/location/GeofenceState.java11
-rw-r--r--services/core/java/com/android/server/location/GnssLocationProvider.java43
-rw-r--r--services/core/java/com/android/server/location/RemoteListenerHelper.java4
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java796
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java116
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsStorage.java63
-rw-r--r--services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java7
-rw-r--r--services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java155
-rw-r--r--services/core/java/com/android/server/locksettings/TEST_MAPPING2
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java29
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java3
-rw-r--r--services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java49
-rw-r--r--services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java3
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java298
-rw-r--r--services/core/java/com/android/server/media/MediaRouterService.java39
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java1
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java148
-rw-r--r--services/core/java/com/android/server/net/watchlist/HarmfulCrcs.java63
-rw-r--r--services/core/java/com/android/server/net/watchlist/WatchlistConfig.java33
-rw-r--r--services/core/java/com/android/server/notification/GroupHelper.java53
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java2
-rw-r--r--services/core/java/com/android/server/notification/NotificationDelegate.java7
-rwxr-xr-x[-rw-r--r--]services/core/java/com/android/server/notification/NotificationManagerService.java220
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java20
-rw-r--r--services/core/java/com/android/server/om/IdmapManager.java50
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerService.java35
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerServiceImpl.java5
-rw-r--r--services/core/java/com/android/server/os/BugreportManagerServiceImpl.java8
-rw-r--r--services/core/java/com/android/server/os/SchedulingPolicyService.java3
-rw-r--r--services/core/java/com/android/server/os/TEST_MAPPING7
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java84
-rw-r--r--services/core/java/com/android/server/pm/ComponentResolver.java28
-rw-r--r--services/core/java/com/android/server/pm/InstallSource.java122
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java27
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java29
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java57
-rw-r--r--services/core/java/com/android/server/pm/PackageList.java82
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java1419
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java103
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java7
-rw-r--r--services/core/java/com/android/server/pm/PackageSettingBase.java100
-rw-r--r--services/core/java/com/android/server/pm/SELinuxMMAC.java7
-rw-r--r--services/core/java/com/android/server/pm/Settings.java189
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java10
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java151
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java378
-rw-r--r--services/core/java/com/android/server/pm/UserSystemPackageInstaller.java466
-rw-r--r--services/core/java/com/android/server/pm/permission/BasePermission.java6
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java17
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java286
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java13
-rw-r--r--services/core/java/com/android/server/policy/LegacyGlobalActions.java6
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java410
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java89
-rw-r--r--services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java165
-rw-r--r--services/core/java/com/android/server/policy/WindowManagerPolicy.java10
-rw-r--r--services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java33
-rw-r--r--services/core/java/com/android/server/power/Notifier.java66
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java15
-rw-r--r--services/core/java/com/android/server/protolog/ProtoLogImpl.java466
-rw-r--r--services/core/java/com/android/server/protolog/ProtoLogViewerConfigReader.java107
-rw-r--r--services/core/java/com/android/server/protolog/common/BitmaskConversionException.java26
-rw-r--r--services/core/java/com/android/server/protolog/common/IProtoLogGroup.java64
-rw-r--r--services/core/java/com/android/server/protolog/common/InvalidFormatStringException.java30
-rw-r--r--services/core/java/com/android/server/protolog/common/LogDataType.java102
-rw-r--r--services/core/java/com/android/server/protolog/common/ProtoLog.java115
-rw-r--r--services/core/java/com/android/server/role/RoleManagerService.java36
-rw-r--r--services/core/java/com/android/server/rollback/AppDataRollbackHelper.java179
-rw-r--r--services/core/java/com/android/server/rollback/Rollback.java548
-rw-r--r--services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java570
-rw-r--r--services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java2
-rw-r--r--services/core/java/com/android/server/rollback/RollbackStore.java49
-rw-r--r--services/core/java/com/android/server/slice/SliceManagerService.java9
-rw-r--r--services/core/java/com/android/server/stats/ProcfsMemoryUtil.java80
-rw-r--r--services/core/java/com/android/server/stats/StatsCompanionService.java2737
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java15
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java100
-rw-r--r--services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java12
-rw-r--r--services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java43
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorService.java6
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java4
-rw-r--r--services/core/java/com/android/server/tv/TvRemoteProviderProxy.java431
-rw-r--r--services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java10
-rw-r--r--services/core/java/com/android/server/tv/TvRemoteService.java234
-rw-r--r--services/core/java/com/android/server/tv/TvRemoteServiceInput.java244
-rw-r--r--services/core/java/com/android/server/twilight/TwilightService.java25
-rw-r--r--services/core/java/com/android/server/updates/EmergencyNumberDbInstallReceiver.java39
-rw-r--r--services/core/java/com/android/server/utils/TraceBuffer.java159
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java43
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java44
-rw-r--r--services/core/java/com/android/server/wm/ActivityDisplay.java37
-rw-r--r--services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java51
-rw-r--r--services/core/java/com/android/server/wm/ActivityMetricsLogger.java124
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java3955
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java250
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java71
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartController.java10
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartInterceptor.java5
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java169
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java6
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java96
-rw-r--r--services/core/java/com/android/server/wm/AnimatingActivityRegistry.java120
-rw-r--r--services/core/java/com/android/server/wm/AnimatingAppWindowTokenRegistry.java120
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java161
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java230
-rw-r--r--services/core/java/com/android/server/wm/AppWindowThumbnail.java60
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java3309
-rw-r--r--services/core/java/com/android/server/wm/BlackFrame.java118
-rw-r--r--services/core/java/com/android/server/wm/CircularDisplayMask.java53
-rw-r--r--services/core/java/com/android/server/wm/ConfigurationContainer.java61
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java543
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java198
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java80
-rw-r--r--services/core/java/com/android/server/wm/DockedStackDividerController.java27
-rw-r--r--services/core/java/com/android/server/wm/DragDropController.java5
-rw-r--r--services/core/java/com/android/server/wm/DragState.java38
-rw-r--r--services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java24
-rw-r--r--services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java96
-rw-r--r--services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java2
-rw-r--r--services/core/java/com/android/server/wm/InputConsumerImpl.java4
-rw-r--r--services/core/java/com/android/server/wm/InputManagerCallback.java99
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java25
-rw-r--r--services/core/java/com/android/server/wm/InsetsControlTarget.java28
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java170
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java34
-rw-r--r--services/core/java/com/android/server/wm/InsetsStateController.java38
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java9
-rw-r--r--services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java41
-rw-r--r--services/core/java/com/android/server/wm/LaunchParamsPersister.java6
-rw-r--r--services/core/java/com/android/server/wm/Letterbox.java21
-rw-r--r--services/core/java/com/android/server/wm/LockTaskController.java16
-rw-r--r--services/core/java/com/android/server/wm/PinnedStackController.java34
-rw-r--r--services/core/java/com/android/server/wm/ProtoLogGroup.java135
-rw-r--r--services/core/java/com/android/server/wm/RecentTasks.java66
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimation.java88
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java138
-rw-r--r--services/core/java/com/android/server/wm/RemoteAnimationController.java94
-rw-r--r--services/core/java/com/android/server/wm/RootActivityContainer.java30
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java95
-rw-r--r--services/core/java/com/android/server/wm/ScreenRotationAnimation.java411
-rw-r--r--services/core/java/com/android/server/wm/SeamlessRotator.java5
-rw-r--r--services/core/java/com/android/server/wm/Session.java11
-rw-r--r--services/core/java/com/android/server/wm/SimpleSurfaceAnimatable.java308
-rw-r--r--services/core/java/com/android/server/wm/SnapshotStartingData.java4
-rw-r--r--services/core/java/com/android/server/wm/SplashScreenStartingData.java6
-rw-r--r--services/core/java/com/android/server/wm/StartingData.java6
-rw-r--r--services/core/java/com/android/server/wm/StrictModeFlash.java21
-rw-r--r--services/core/java/com/android/server/wm/Task.java83
-rw-r--r--services/core/java/com/android/server/wm/TaskChangeNotificationController.java18
-rw-r--r--services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java54
-rw-r--r--services/core/java/com/android/server/wm/TaskPersister.java16
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioner.java20
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioningController.java15
-rw-r--r--services/core/java/com/android/server/wm/TaskRecord.java378
-rw-r--r--services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java18
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotCache.java16
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java66
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotSurface.java78
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java39
-rw-r--r--services/core/java/com/android/server/wm/UnknownAppVisibilityController.java44
-rw-r--r--services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java18
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java68
-rw-r--r--services/core/java/com/android/server/wm/Watermark.java14
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java72
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java110
-rw-r--r--services/core/java/com/android/server/wm/WindowContainerController.java83
-rw-r--r--services/core/java/com/android/server/wm/WindowContainerListener.java29
-rw-r--r--services/core/java/com/android/server/wm/WindowHashMap.java28
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerDebugConfig.java25
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java35
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java1056
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerShellCommand.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java39
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java445
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java136
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfaceController.java67
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfacePlacer.java20
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java25
-rw-r--r--services/core/java/com/android/server/wm/WindowTraceBuffer.java160
-rw-r--r--services/core/java/com/android/server/wm/WindowTracing.java20
-rw-r--r--services/core/jni/Android.bp1
-rw-r--r--services/core/jni/com_android_server_VibratorService.cpp139
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp55
-rw-r--r--services/core/jni/com_android_server_security_VerityUtils.cpp34
-rw-r--r--services/devicepolicy/Android.bp9
-rw-r--r--services/devicepolicy/TEST_MAPPING4
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java514
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DeviceStateCacheImpl.java57
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java2
-rw-r--r--services/java/com/android/server/SystemServer.java29
-rw-r--r--services/net/Android.bp79
-rw-r--r--services/net/aidl/ipmemorystore/1/android/net/IIpMemoryStore.aidl9
-rw-r--r--services/net/aidl/ipmemorystore/1/android/net/IIpMemoryStoreCallbacks.aidl4
-rw-r--r--services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/Blob.aidl4
-rw-r--r--services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl4
-rw-r--r--services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl4
-rw-r--r--services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl4
-rw-r--r--services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl4
-rw-r--r--services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnStatusListener.aidl4
-rw-r--r--services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/NetworkAttributesParcelable.aidl8
-rw-r--r--services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl6
-rw-r--r--services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/StatusParcelable.aidl4
-rw-r--r--services/net/aidl/ipmemorystore/2/android/net/IIpMemoryStore.aidl9
-rw-r--r--services/net/aidl/ipmemorystore/2/android/net/IIpMemoryStoreCallbacks.aidl4
-rw-r--r--services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/Blob.aidl4
-rw-r--r--services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl4
-rw-r--r--services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl4
-rw-r--r--services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl4
-rw-r--r--services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl4
-rw-r--r--services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/IOnStatusListener.aidl4
-rw-r--r--services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/NetworkAttributesParcelable.aidl8
-rw-r--r--services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl6
-rw-r--r--services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/StatusParcelable.aidl4
-rw-r--r--services/net/aidl/ipmemorystore/3/android/net/IIpMemoryStore.aidl27
-rw-r--r--services/net/aidl/ipmemorystore/3/android/net/IIpMemoryStoreCallbacks.aidl21
-rw-r--r--services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/Blob.aidl21
-rw-r--r--services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl21
-rw-r--r--services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl21
-rw-r--r--services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl21
-rw-r--r--services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl21
-rw-r--r--services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnStatusListener.aidl21
-rw-r--r--services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/NetworkAttributesParcelable.aidl25
-rw-r--r--services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl23
-rw-r--r--services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/StatusParcelable.aidl21
-rw-r--r--services/net/aidl/networkstack/1/android/net/DhcpResultsParcelable.aidl8
-rw-r--r--services/net/aidl/networkstack/1/android/net/INetworkMonitor.aidl17
-rw-r--r--services/net/aidl/networkstack/1/android/net/INetworkMonitorCallbacks.aidl8
-rw-r--r--services/net/aidl/networkstack/1/android/net/INetworkStackConnector.aidl7
-rw-r--r--services/net/aidl/networkstack/1/android/net/INetworkStackStatusCallback.aidl4
-rw-r--r--services/net/aidl/networkstack/1/android/net/InitialConfigurationParcelable.aidl7
-rw-r--r--services/net/aidl/networkstack/1/android/net/PrivateDnsConfigParcel.aidl5
-rw-r--r--services/net/aidl/networkstack/1/android/net/ProvisioningConfigurationParcelable.aidl15
-rw-r--r--services/net/aidl/networkstack/1/android/net/TcpKeepalivePacketDataParcelable.aidl13
-rw-r--r--services/net/aidl/networkstack/1/android/net/dhcp/DhcpServingParamsParcel.aidl11
-rw-r--r--services/net/aidl/networkstack/1/android/net/dhcp/IDhcpServer.aidl10
-rw-r--r--services/net/aidl/networkstack/1/android/net/dhcp/IDhcpServerCallbacks.aidl4
-rw-r--r--services/net/aidl/networkstack/1/android/net/ip/IIpClient.aidl14
-rw-r--r--services/net/aidl/networkstack/1/android/net/ip/IIpClientCallbacks.aidl16
-rw-r--r--services/net/aidl/networkstack/2/android/net/DhcpResultsParcelable.aidl9
-rw-r--r--services/net/aidl/networkstack/2/android/net/INetworkMonitor.aidl24
-rw-r--r--services/net/aidl/networkstack/2/android/net/INetworkMonitorCallbacks.aidl8
-rw-r--r--services/net/aidl/networkstack/2/android/net/INetworkStackConnector.aidl7
-rw-r--r--services/net/aidl/networkstack/2/android/net/INetworkStackStatusCallback.aidl4
-rw-r--r--services/net/aidl/networkstack/2/android/net/InitialConfigurationParcelable.aidl7
-rw-r--r--services/net/aidl/networkstack/2/android/net/NattKeepalivePacketDataParcelable.aidl7
-rw-r--r--services/net/aidl/networkstack/2/android/net/PrivateDnsConfigParcel.aidl5
-rw-r--r--services/net/aidl/networkstack/2/android/net/ProvisioningConfigurationParcelable.aidl15
-rw-r--r--services/net/aidl/networkstack/2/android/net/TcpKeepalivePacketDataParcelable.aidl13
-rw-r--r--services/net/aidl/networkstack/2/android/net/dhcp/DhcpServingParamsParcel.aidl11
-rw-r--r--services/net/aidl/networkstack/2/android/net/dhcp/IDhcpServer.aidl10
-rw-r--r--services/net/aidl/networkstack/2/android/net/dhcp/IDhcpServerCallbacks.aidl4
-rw-r--r--services/net/aidl/networkstack/2/android/net/ip/IIpClient.aidl15
-rw-r--r--services/net/aidl/networkstack/2/android/net/ip/IIpClientCallbacks.aidl16
-rw-r--r--services/net/aidl/networkstack/3/android/net/DhcpResultsParcelable.aidl26
-rw-r--r--services/net/aidl/networkstack/3/android/net/INetworkMonitor.aidl41
-rw-r--r--services/net/aidl/networkstack/3/android/net/INetworkMonitorCallbacks.aidl25
-rw-r--r--services/net/aidl/networkstack/3/android/net/INetworkStackConnector.aidl24
-rw-r--r--services/net/aidl/networkstack/3/android/net/INetworkStackStatusCallback.aidl21
-rw-r--r--services/net/aidl/networkstack/3/android/net/InitialConfigurationParcelable.aidl24
-rw-r--r--services/net/aidl/networkstack/3/android/net/NattKeepalivePacketDataParcelable.aidl24
-rw-r--r--services/net/aidl/networkstack/3/android/net/PrivateDnsConfigParcel.aidl22
-rw-r--r--services/net/aidl/networkstack/3/android/net/ProvisioningConfigurationParcelable.aidl32
-rw-r--r--services/net/aidl/networkstack/3/android/net/TcpKeepalivePacketDataParcelable.aidl30
-rw-r--r--services/net/aidl/networkstack/3/android/net/dhcp/DhcpServingParamsParcel.aidl28
-rw-r--r--services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServer.aidl27
-rw-r--r--services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServerCallbacks.aidl21
-rw-r--r--services/net/aidl/networkstack/3/android/net/ip/IIpClient.aidl33
-rw-r--r--services/net/aidl/networkstack/3/android/net/ip/IIpClientCallbacks.aidl33
-rw-r--r--services/net/java/android/net/DhcpResultsParcelable.aidl28
-rw-r--r--services/net/java/android/net/IIpMemoryStore.aidl118
-rw-r--r--services/net/java/android/net/IIpMemoryStoreCallbacks.aidl24
-rw-r--r--services/net/java/android/net/INetworkMonitor.aidl68
-rw-r--r--services/net/java/android/net/INetworkMonitorCallbacks.aidl29
-rw-r--r--services/net/java/android/net/INetworkStackConnector.aidl32
-rw-r--r--services/net/java/android/net/INetworkStackStatusCallback.aidl22
-rw-r--r--services/net/java/android/net/InitialConfigurationParcelable.aidl27
-rw-r--r--services/net/java/android/net/IpMemoryStoreClient.java227
-rw-r--r--services/net/java/android/net/NattKeepalivePacketDataParcelable.aidl25
-rw-r--r--services/net/java/android/net/PrivateDnsConfigParcel.aidl22
-rw-r--r--services/net/java/android/net/ProvisioningConfigurationParcelable.aidl38
-rw-r--r--services/net/java/android/net/TcpKeepalivePacketDataParcelable.aidl30
-rw-r--r--services/net/java/android/net/dhcp/DhcpServingParamsParcel.aidl30
-rw-r--r--services/net/java/android/net/dhcp/IDhcpServer.aidl32
-rw-r--r--services/net/java/android/net/dhcp/IDhcpServerCallbacks.aidl24
-rw-r--r--services/net/java/android/net/ip/IIpClient.aidl38
-rw-r--r--services/net/java/android/net/ip/IIpClientCallbacks.aidl66
-rw-r--r--services/net/java/android/net/ip/IpServer.java5
-rw-r--r--services/net/java/android/net/ipmemorystore/Blob.aidl26
-rw-r--r--services/net/java/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl30
-rw-r--r--services/net/java/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl27
-rw-r--r--services/net/java/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl30
-rw-r--r--services/net/java/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl29
-rw-r--r--services/net/java/android/net/ipmemorystore/IOnStatusListener.aidl27
-rw-r--r--services/net/java/android/net/ipmemorystore/NetworkAttributes.java371
-rw-r--r--services/net/java/android/net/ipmemorystore/NetworkAttributesParcelable.aidl37
-rw-r--r--services/net/java/android/net/ipmemorystore/OnBlobRetrievedListener.java50
-rw-r--r--services/net/java/android/net/ipmemorystore/OnL2KeyResponseListener.java50
-rw-r--r--services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java54
-rw-r--r--services/net/java/android/net/ipmemorystore/OnSameL3NetworkResponseListener.java53
-rw-r--r--services/net/java/android/net/ipmemorystore/OnStatusListener.java49
-rw-r--r--services/net/java/android/net/ipmemorystore/SameL3NetworkResponse.java147
-rw-r--r--services/net/java/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl24
-rw-r--r--services/net/java/android/net/ipmemorystore/Status.java74
-rw-r--r--services/net/java/android/net/ipmemorystore/StatusParcelable.aidl22
-rw-r--r--services/net/java/android/net/netlink/InetDiagMessage.java27
-rw-r--r--services/print/java/com/android/server/print/TEST_MAPPING12
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java20
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java27
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java143
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java16
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java50
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java50
-rw-r--r--services/tests/servicestests/src/com/android/server/SystemConfigTest.java180
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java23
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java297
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java91
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java30
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java53
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java89
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/utils/MotionEventMatcher.java88
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java24
-rw-r--r--services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java111
-rw-r--r--services/tests/servicestests/src/com/android/server/am/UserControllerTest.java52
-rw-r--r--services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/transport/DelegatingTransportTest.java386
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java515
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java84
-rw-r--r--services/tests/servicestests/src/com/android/server/display/utils/AmbientFilterTest.java126
-rw-r--r--services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientFilterStubber.java27
-rw-r--r--services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientFilterTest.java125
-rw-r--r--services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java48
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java212
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java183
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/model/AtomicFormulaTest.java100
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/model/OpenFormulaTest.java67
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/model/RuleTest.java103
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java83
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java67
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java36
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java68
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java322
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java147
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java37
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java54
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockscreenFrpTest.java118
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java313
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java27
-rw-r--r--services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java33
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java36
-rw-r--r--services/tests/servicestests/src/com/android/server/net/watchlist/HarmfulCrcsTests.java60
-rw-r--r--services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java141
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java121
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ScanTests.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java396
-rw-r--r--services/tests/servicestests/src/com/android/server/protolog/ProtoLogImplTest.java462
-rw-r--r--services/tests/servicestests/src/com/android/server/protolog/ProtoLogViewerConfigReaderTest.java114
-rw-r--r--services/tests/servicestests/src/com/android/server/protolog/common/LogDataTypeTest.java85
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java41
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java324
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java257
-rw-r--r--services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java62
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java88
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java31
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java211
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java191
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java114
-rw-r--r--services/tests/servicestests/src/com/android/server/utils/TraceBufferTest.java184
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java321
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java192
-rwxr-xr-x[-rw-r--r--]services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java199
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java41
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java32
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java145
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java30
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java42
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java35
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java104
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java104
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java42
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java98
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java41
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppWindowThumbnailTest.java13
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java64
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java294
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java9
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DimmerTests.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java283
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java671
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java11
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java59
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java38
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java88
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java12
-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.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java82
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java75
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java36
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java138
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java36
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/StubTransaction.java16
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java59
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java84
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java78
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java61
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java31
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java26
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java62
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskTests.java50
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestIWindow.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java17
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java42
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java44
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java109
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java38
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java119
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerGlobalLockRule.java47
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java9
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java57
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java130
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java63
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java184
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java2
-rw-r--r--services/usage/java/com/android/server/usage/AppStandbyController.java2032
-rw-r--r--services/usage/java/com/android/server/usage/IntervalStats.java238
-rw-r--r--services/usage/java/com/android/server/usage/PackagesTokenData.java176
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsDatabase.java219
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsProto.java189
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsProtoV2.java788
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java210
-rw-r--r--services/usage/java/com/android/server/usage/UserUsageStatsService.java72
-rw-r--r--services/usb/java/com/android/server/usb/UsbHostManager.java2
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java1
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java118
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java9
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbVCEndpoint.java61
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbVCHeader.java50
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbVCHeaderInterface.java56
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbVCInputTerminal.java49
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbVCInterface.java52
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbVCOutputTerminal.java49
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbVCProcessingUnit.java50
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbVCSelectorUnit.java50
-rw-r--r--services/wifi/Android.bp3
-rw-r--r--services/wifi/java/android/net/wifi/IWifiStackConnector.aidl5
-rw-r--r--services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl23
-rw-r--r--services/wifi/java/android/net/wifi/WifiStackClient.java57
-rw-r--r--startop/apps/test/Android.bp9
-rw-r--r--startop/apps/test/AndroidManifest.xml20
-rw-r--r--startop/apps/test/README.md11
-rw-r--r--startop/apps/test/src/CPUIntensive.java67
-rw-r--r--startop/apps/test/src/CPUIntensiveBenchmarkActivity.java30
-rw-r--r--startop/apps/test/src/CPUIntensiveBenchmarks.java89
-rw-r--r--startop/apps/test/src/NonInteractiveSystemServerBenchmarkActivity.java71
-rw-r--r--startop/apps/test/src/SystemServerBenchmarkActivity.java193
-rw-r--r--startop/apps/test/src/SystemServerBenchmarks.java245
-rw-r--r--startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java93
-rw-r--r--startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java34
-rw-r--r--startop/iorap/tests/src/com/google/android/startop/iorap/AppLaunchEventTest.kt181
-rwxr-xr-xstartop/scripts/app_startup/app_startup_runner.py26
-rwxr-xr-xstartop/scripts/app_startup/app_startup_runner_test.py2
-rwxr-xr-xstartop/scripts/iorap/common2
-rw-r--r--startop/scripts/iorap/compiler_test.py2
-rw-r--r--startop/scripts/iorap/test_fixtures/compiler/test_result_systrace1416
-rw-r--r--startop/view_compiler/Android.bp1
-rw-r--r--startop/view_compiler/dex_builder.cc21
-rw-r--r--startop/view_compiler/dex_builder.h54
-rw-r--r--startop/view_compiler/dex_layout_compiler.cc211
-rw-r--r--startop/view_compiler/dex_layout_compiler.h31
-rw-r--r--startop/view_compiler/dex_testcase_generator.cc36
-rw-r--r--telecomm/java/android/telecom/Call.java66
-rw-r--r--telecomm/java/android/telecom/CallScreeningService.java44
-rw-r--r--telecomm/java/android/telecom/Conference.java5
-rw-r--r--telecomm/java/android/telecom/Connection.java341
-rw-r--r--telecomm/java/android/telecom/InCallAdapter.java22
-rw-r--r--telecomm/java/android/telecom/Phone.java41
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java32
-rw-r--r--telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl2
-rw-r--r--telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl4
-rw-r--r--telecomm/java/com/android/internal/telecom/ITelecomService.aidl17
-rw-r--r--telephony/OWNERS3
-rw-r--r--telephony/common/com/android/internal/telephony/HbpcdLookup.java124
-rw-r--r--telephony/common/com/android/internal/telephony/HbpcdUtils.java163
-rw-r--r--telephony/common/com/android/internal/telephony/SmsApplication.java1077
-rw-r--r--telephony/common/com/android/internal/telephony/SmsNumberUtils.java627
-rw-r--r--telephony/common/com/google/android/mms/ContentType.java (renamed from telephony/java/com/google/android/mms/ContentType.java)0
-rw-r--r--telephony/common/com/google/android/mms/InvalidHeaderValueException.java (renamed from telephony/java/com/google/android/mms/InvalidHeaderValueException.java)0
-rw-r--r--telephony/common/com/google/android/mms/MmsException.java (renamed from telephony/java/com/google/android/mms/MmsException.java)0
-rwxr-xr-xtelephony/common/com/google/android/mms/package.html (renamed from telephony/java/com/google/android/mms/package.html)0
-rw-r--r--telephony/common/com/google/android/mms/pdu/AcknowledgeInd.java (renamed from telephony/java/com/google/android/mms/pdu/AcknowledgeInd.java)0
-rw-r--r--telephony/common/com/google/android/mms/pdu/Base64.java (renamed from telephony/java/com/google/android/mms/pdu/Base64.java)0
-rw-r--r--telephony/common/com/google/android/mms/pdu/CharacterSets.java (renamed from telephony/java/com/google/android/mms/pdu/CharacterSets.java)0
-rw-r--r--telephony/common/com/google/android/mms/pdu/DeliveryInd.java (renamed from telephony/java/com/google/android/mms/pdu/DeliveryInd.java)0
-rw-r--r--telephony/common/com/google/android/mms/pdu/EncodedStringValue.java (renamed from telephony/java/com/google/android/mms/pdu/EncodedStringValue.java)0
-rw-r--r--telephony/common/com/google/android/mms/pdu/GenericPdu.java (renamed from telephony/java/com/google/android/mms/pdu/GenericPdu.java)0
-rw-r--r--telephony/common/com/google/android/mms/pdu/MultimediaMessagePdu.java (renamed from telephony/java/com/google/android/mms/pdu/MultimediaMessagePdu.java)0
-rw-r--r--telephony/common/com/google/android/mms/pdu/NotificationInd.java (renamed from telephony/java/com/google/android/mms/pdu/NotificationInd.java)0
-rw-r--r--telephony/common/com/google/android/mms/pdu/NotifyRespInd.java (renamed from telephony/java/com/google/android/mms/pdu/NotifyRespInd.java)0
-rw-r--r--telephony/common/com/google/android/mms/pdu/PduBody.java (renamed from telephony/java/com/google/android/mms/pdu/PduBody.java)0
-rw-r--r--telephony/common/com/google/android/mms/pdu/PduComposer.java (renamed from telephony/java/com/google/android/mms/pdu/PduComposer.java)0
-rw-r--r--telephony/common/com/google/android/mms/pdu/PduContentTypes.java (renamed from telephony/java/com/google/android/mms/pdu/PduContentTypes.java)0
-rw-r--r--telephony/common/com/google/android/mms/pdu/PduHeaders.java (renamed from telephony/java/com/google/android/mms/pdu/PduHeaders.java)0
-rwxr-xr-xtelephony/common/com/google/android/mms/pdu/PduParser.java (renamed from telephony/java/com/google/android/mms/pdu/PduParser.java)0
-rw-r--r--telephony/common/com/google/android/mms/pdu/PduPart.java (renamed from telephony/java/com/google/android/mms/pdu/PduPart.java)0
-rwxr-xr-xtelephony/common/com/google/android/mms/pdu/PduPersister.java (renamed from telephony/java/com/google/android/mms/pdu/PduPersister.java)0
-rw-r--r--telephony/common/com/google/android/mms/pdu/QuotedPrintable.java (renamed from telephony/java/com/google/android/mms/pdu/QuotedPrintable.java)0
-rw-r--r--telephony/common/com/google/android/mms/pdu/ReadOrigInd.java (renamed from telephony/java/com/google/android/mms/pdu/ReadOrigInd.java)0
-rw-r--r--telephony/common/com/google/android/mms/pdu/ReadRecInd.java (renamed from telephony/java/com/google/android/mms/pdu/ReadRecInd.java)0
-rw-r--r--telephony/common/com/google/android/mms/pdu/RetrieveConf.java (renamed from telephony/java/com/google/android/mms/pdu/RetrieveConf.java)0
-rw-r--r--telephony/common/com/google/android/mms/pdu/SendConf.java (renamed from telephony/java/com/google/android/mms/pdu/SendConf.java)0
-rw-r--r--telephony/common/com/google/android/mms/pdu/SendReq.java (renamed from telephony/java/com/google/android/mms/pdu/SendReq.java)0
-rwxr-xr-xtelephony/common/com/google/android/mms/pdu/package.html (renamed from telephony/java/com/google/android/mms/pdu/package.html)0
-rw-r--r--telephony/common/com/google/android/mms/util/AbstractCache.java (renamed from telephony/java/com/google/android/mms/util/AbstractCache.java)0
-rw-r--r--telephony/common/com/google/android/mms/util/DownloadDrmHelper.java (renamed from telephony/java/com/google/android/mms/util/DownloadDrmHelper.java)0
-rw-r--r--telephony/common/com/google/android/mms/util/DrmConvertSession.java (renamed from telephony/java/com/google/android/mms/util/DrmConvertSession.java)0
-rw-r--r--telephony/common/com/google/android/mms/util/PduCache.java (renamed from telephony/java/com/google/android/mms/util/PduCache.java)0
-rw-r--r--telephony/common/com/google/android/mms/util/PduCacheEntry.java (renamed from telephony/java/com/google/android/mms/util/PduCacheEntry.java)0
-rw-r--r--telephony/common/com/google/android/mms/util/SqliteWrapper.java (renamed from telephony/java/com/google/android/mms/util/SqliteWrapper.java)0
-rwxr-xr-xtelephony/common/com/google/android/mms/util/package.html (renamed from telephony/java/com/google/android/mms/util/package.html)0
-rw-r--r--telephony/java/android/provider/Telephony.java13
-rw-r--r--telephony/java/android/telephony/Annotation.java512
-rw-r--r--telephony/java/android/telephony/CallAttributes.java2
-rwxr-xr-xtelephony/java/android/telephony/CarrierConfigManager.java33
-rw-r--r--telephony/java/android/telephony/CellBroadcastService.java118
-rw-r--r--telephony/java/android/telephony/CellIdentity.java27
-rw-r--r--telephony/java/android/telephony/CellInfo.java8
-rw-r--r--telephony/java/android/telephony/CellInfoNr.java4
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthWcdma.java7
-rw-r--r--telephony/java/android/telephony/DataFailCause.java362
-rw-r--r--telephony/java/android/telephony/ICellBroadcastService.aidl32
-rw-r--r--telephony/java/android/telephony/ICellInfoCallback.aidl3
-rw-r--r--telephony/java/android/telephony/IFinancialSmsCallback.aidl34
-rw-r--r--telephony/java/android/telephony/MbmsDownloadSession.java113
-rw-r--r--telephony/java/android/telephony/MbmsGroupCallSession.java121
-rw-r--r--telephony/java/android/telephony/MbmsStreamingSession.java119
-rw-r--r--telephony/java/android/telephony/ModemActivityInfo.java14
-rw-r--r--telephony/java/android/telephony/NetworkRegistrationInfo.java4
-rw-r--r--telephony/java/android/telephony/PhoneStateListener.java1268
-rw-r--r--telephony/java/android/telephony/PhysicalChannelConfig.java2
-rw-r--r--telephony/java/android/telephony/PreciseCallState.java36
-rw-r--r--telephony/java/android/telephony/PreciseDataConnectionState.java28
-rw-r--r--telephony/java/android/telephony/ServiceState.java36
-rw-r--r--telephony/java/android/telephony/SmsCbMessage.java26
-rw-r--r--telephony/java/android/telephony/SmsManager.java916
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java233
-rw-r--r--telephony/java/android/telephony/SubscriptionPlan.java57
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java361
-rw-r--r--telephony/java/android/telephony/TelephonyScanManager.java22
-rw-r--r--telephony/java/android/telephony/cdma/CdmaSmsCbProgramData.java112
-rw-r--r--telephony/java/android/telephony/data/ApnSetting.java21
-rw-r--r--telephony/java/android/telephony/data/DataCallResponse.java12
-rw-r--r--telephony/java/android/telephony/data/DataProfile.java2
-rw-r--r--telephony/java/android/telephony/data/QualifiedNetworksService.java4
-rw-r--r--telephony/java/android/telephony/emergency/EmergencyNumber.java2
-rw-r--r--telephony/java/android/telephony/euicc/EuiccManager.java36
-rw-r--r--telephony/java/android/telephony/ims/ImsMmTelManager.java21
-rw-r--r--telephony/java/android/telephony/ims/ImsReasonInfo.java7
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java144
-rw-r--r--telephony/java/com/android/internal/telephony/DctConstants.java2
-rw-r--r--telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl66
-rw-r--r--telephony/java/com/android/internal/telephony/ISms.aidl12
-rw-r--r--telephony/java/com/android/internal/telephony/ISmsImplBase.java7
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl34
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl99
-rw-r--r--telephony/java/com/android/internal/telephony/PhoneConstants.java6
-rw-r--r--telephony/java/com/android/internal/telephony/SmsApplication.java1081
-rwxr-xr-xtelephony/java/com/android/internal/telephony/cdma/SmsMessage.java19
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java19
-rw-r--r--telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl2
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java20
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java34
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SmsMessage.java2
-rw-r--r--telephony/java/com/android/internal/telephony/uicc/IccUtils.java31
-rw-r--r--test-mock/Android.bp7
-rw-r--r--test-mock/src/android/test/mock/MockContext.java6
-rw-r--r--tests/ApkVerityTest/Android.bp34
-rw-r--r--tests/ApkVerityTest/AndroidTest.xml41
-rw-r--r--tests/ApkVerityTest/ApkVerityTestApp/Android.bp29
-rw-r--r--tests/ApkVerityTest/ApkVerityTestApp/AndroidManifest.xml23
-rw-r--r--tests/ApkVerityTest/ApkVerityTestApp/feature_split/AndroidManifest.xml25
-rw-r--r--tests/ApkVerityTest/ApkVerityTestApp/feature_split/src/com/android/apkverity/feature_x/DummyActivity.java22
-rw-r--r--tests/ApkVerityTest/ApkVerityTestApp/src/com/android/apkverity/DummyActivity.java22
-rw-r--r--tests/ApkVerityTest/block_device_writer/Android.bp30
-rw-r--r--tests/ApkVerityTest/block_device_writer/block_device_writer.cpp248
-rw-r--r--tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java504
-rw-r--r--tests/ApkVerityTest/src/com/android/apkverity/BaseInstallMultiple.java140
-rw-r--r--tests/ApkVerityTest/testdata/Android.bp77
-rw-r--r--tests/ApkVerityTest/testdata/ApkVerityTestApp.dmbin0 -> 237348 bytes
-rw-r--r--tests/ApkVerityTest/testdata/ApkVerityTestAppSplit.dmbin0 -> 237025 bytes
-rw-r--r--tests/ApkVerityTest/testdata/ApkVerityTestCert.derbin0 -> 1330 bytes
-rw-r--r--tests/ApkVerityTest/testdata/ApkVerityTestCert.pem30
-rw-r--r--tests/ApkVerityTest/testdata/ApkVerityTestKey.pem52
-rw-r--r--tests/ApkVerityTest/testdata/README.md13
-rw-r--r--tests/BootImageProfileTest/AndroidTest.xml4
-rw-r--r--tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java33
-rwxr-xr-xtests/Codegen/runTest.sh16
-rw-r--r--tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.aidl19
-rw-r--r--tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java108
-rw-r--r--tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.aidl19
-rw-r--r--tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java130
-rw-r--r--tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java420
-rw-r--r--tests/Codegen/src/com/android/codegentest/SampleDataClass.java190
-rw-r--r--tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java34
-rw-r--r--tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java91
-rw-r--r--tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java75
-rw-r--r--tests/FlickerTests/AndroidManifest.xml6
-rw-r--r--tests/FlickerTests/AndroidTest.xml1
-rw-r--r--tests/FlickerTests/TEST_MAPPING3
-rw-r--r--tests/FlickerTests/lib/Android.bp57
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java134
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java183
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java27
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java420
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java140
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java433
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java240
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java144
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java192
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/helpers/AutomationUtils.java263
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ITransitionMonitor.java63
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java64
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java80
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java74
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitor.java100
-rw-r--r--tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java59
-rw-r--r--tests/FlickerTests/lib/test/Android.bp33
-rw-r--r--tests/FlickerTests/lib/test/AndroidManifest.xml26
-rw-r--r--tests/FlickerTests/lib/test/AndroidTest.xml20
-rw-r--r--tests/FlickerTests/lib/test/assets/testdata/layers_trace_emptyregion.pbbin565800 -> 0 bytes
-rw-r--r--tests/FlickerTests/lib/test/assets/testdata/layers_trace_invalid_layer_visibility.pbbin3580523 -> 0 bytes
-rw-r--r--tests/FlickerTests/lib/test/assets/testdata/layers_trace_orphanlayers.pbbin1936878 -> 0 bytes
-rw-r--r--tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome.pbbin355010 -> 0 bytes
-rw-r--r--tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome2.pbbin194353 -> 0 bytes
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsCheckerTest.java181
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsTest.java60
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceSubjectTest.java88
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceTest.java230
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TestFileUtils.java36
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TransitionRunnerTest.java258
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WindowManagerTraceTest.java108
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WmTraceSubjectTest.java49
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/LayersTraceMonitorTest.java78
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java70
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java50
-rw-r--r--tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitorTest.java79
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java43
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToAppTest.java77
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToHomeTest.java69
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java37
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java41
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java104
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java72
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java48
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.java80
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java67
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java7
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java65
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java27
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java60
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java5
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java15
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/StandardAppHelper.java59
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.java31
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.java31
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.java41
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.java43
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml9
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml1
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java30
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java5
-rw-r--r--tests/MirrorSurfaceTest/Android.bp6
-rw-r--r--tests/MirrorSurfaceTest/AndroidManifest.xml34
-rw-r--r--tests/MirrorSurfaceTest/res/layout/activity_mirror_surface.xml111
-rw-r--r--tests/MirrorSurfaceTest/res/layout/move_view.xml101
-rw-r--r--tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java441
-rw-r--r--tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java37
-rw-r--r--tests/RollbackTest/Android.bp1
-rw-r--r--tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java15
-rw-r--r--tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java125
-rw-r--r--tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java39
-rw-r--r--tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java73
-rw-r--r--tests/net/Android.bp2
-rw-r--r--tests/net/common/java/android/net/LinkPropertiesTest.java14
-rw-r--r--tests/net/common/java/android/net/NetworkCapabilitiesTest.java2
-rw-r--r--tests/net/java/android/net/ip/IpServerTest.java27
-rw-r--r--tests/net/java/android/net/netlink/InetDiagSocketTest.java35
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java514
-rw-r--r--tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java27
-rw-r--r--tests/net/java/com/android/server/connectivity/TetheringTest.java202
-rw-r--r--tests/utils/testutils/java/android/os/test/TestLooper.java38
-rw-r--r--tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java1
-rw-r--r--tools/aapt/Command.cpp4
-rw-r--r--tools/aapt2/Main.cpp11
-rw-r--r--tools/aapt2/format/Container.cpp23
-rw-r--r--tools/aapt2/format/binary/XmlFlattener_test.cpp6
-rw-r--r--tools/aapt2/formats.md17
-rw-r--r--tools/apilint/apilint.py12
-rw-r--r--tools/codegen/src/com/android/codegen/ClassInfo.kt5
-rw-r--r--tools/codegen/src/com/android/codegen/ClassPrinter.kt10
-rw-r--r--tools/codegen/src/com/android/codegen/FieldInfo.kt2
-rw-r--r--tools/codegen/src/com/android/codegen/Generators.kt241
-rw-r--r--tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt8
-rwxr-xr-xtools/codegen/src/com/android/codegen/Main.kt18
-rw-r--r--tools/codegen/src/com/android/codegen/SharedConstants.kt2
-rw-r--r--tools/codegen/src/com/android/codegen/Utils.kt12
-rw-r--r--tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt5
-rw-r--r--tools/protologtool/Android.bp23
-rw-r--r--tools/protologtool/README.md41
-rw-r--r--tools/protologtool/manifest.txt2
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/CodeUtils.kt81
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/CommandOptions.kt212
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/Constants.kt24
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/LogGroup.kt24
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/LogLevel.kt38
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/LogParser.kt115
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ParsingContext.kt26
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt113
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt23
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ProtoLogGroupReader.kt50
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt218
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt231
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ViewerConfigBuilder.kt120
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ViewerConfigParser.kt122
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/exceptions.kt41
-rw-r--r--tools/protologtool/src/com/android/protologtool/CodeUtils.kt135
-rw-r--r--tools/protologtool/src/com/android/protologtool/CommandOptions.kt205
-rw-r--r--tools/protologtool/src/com/android/protologtool/Constants.kt27
-rw-r--r--tools/protologtool/src/com/android/protologtool/LogGroup.kt24
-rw-r--r--tools/protologtool/src/com/android/protologtool/LogLevel.kt37
-rw-r--r--tools/protologtool/src/com/android/protologtool/LogParser.kt112
-rw-r--r--tools/protologtool/src/com/android/protologtool/ProtoLogCallProcessor.kt108
-rw-r--r--tools/protologtool/src/com/android/protologtool/ProtoLogCallVisitor.kt23
-rw-r--r--tools/protologtool/src/com/android/protologtool/ProtoLogGroupReader.kt60
-rw-r--r--tools/protologtool/src/com/android/protologtool/ProtoLogTool.kt95
-rw-r--r--tools/protologtool/src/com/android/protologtool/SourceTransformer.kt194
-rw-r--r--tools/protologtool/src/com/android/protologtool/ViewerConfigBuilder.kt91
-rw-r--r--tools/protologtool/src/com/android/protologtool/ViewerConfigParser.kt125
-rw-r--r--tools/protologtool/src/com/android/protologtool/exceptions.kt44
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/CodeUtilsTest.kt182
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/CommandOptionsTest.kt287
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/LogParserTest.kt187
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorTest.kt226
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/ProtoLogToolTest.kt52
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt451
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/ViewerConfigBuilderTest.kt94
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/ViewerConfigParserTest.kt327
-rw-r--r--tools/protologtool/tests/com/android/protologtool/CodeUtilsTest.kt206
-rw-r--r--tools/protologtool/tests/com/android/protologtool/CommandOptionsTest.kt250
-rw-r--r--tools/protologtool/tests/com/android/protologtool/LogParserTest.kt187
-rw-r--r--tools/protologtool/tests/com/android/protologtool/ProtoLogCallProcessorTest.kt226
-rw-r--r--tools/protologtool/tests/com/android/protologtool/SourceTransformerTest.kt445
-rw-r--r--tools/protologtool/tests/com/android/protologtool/ViewerConfigBuilderTest.kt120
-rw-r--r--tools/protologtool/tests/com/android/protologtool/ViewerConfigParserTest.kt327
-rw-r--r--tools/streaming_proto/Android.bp2
-rw-r--r--tools/validatekeymaps/Main.cpp1
-rw-r--r--wifi/java/android/net/wifi/IActionListener.aidl27
-rw-r--r--wifi/java/android/net/wifi/ILocalOnlyHotspotCallback.aidl30
-rw-r--r--wifi/java/android/net/wifi/IScanResultsListener.aidl27
-rw-r--r--wifi/java/android/net/wifi/ITxPacketCountListener.aidl27
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl24
-rw-r--r--wifi/java/android/net/wifi/RssiPacketCountInfo.java75
-rw-r--r--wifi/java/android/net/wifi/WifiConfiguration.java20
-rw-r--r--wifi/java/android/net/wifi/WifiInfo.java66
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java765
-rw-r--r--wifi/java/android/net/wifi/WifiNetworkSpecifier.java3
-rw-r--r--wifi/java/android/net/wifi/WifiNetworkSuggestion.java132
-rw-r--r--wifi/java/android/net/wifi/WifiScanner.java69
-rw-r--r--wifi/java/android/net/wifi/WifiUsabilityStatsEntry.java2
-rw-r--r--wifi/java/android/net/wifi/hotspot2/ConfigParser.java6
-rw-r--r--wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java18
-rw-r--r--wifi/java/com/android/server/wifi/BaseWifiService.java65
-rw-r--r--wifi/tests/assets/hsr1/HSR1ProfileWithUpdateIdentifier.base6488
-rw-r--r--wifi/tests/src/android/net/wifi/WifiConfigurationTest.java49
-rw-r--r--wifi/tests/src/android/net/wifi/WifiInfoTest.java3
-rw-r--r--wifi/tests/src/android/net/wifi/WifiManagerTest.java309
-rw-r--r--wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java222
-rw-r--r--wifi/tests/src/android/net/wifi/WifiScannerTest.java34
-rw-r--r--wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java20
-rw-r--r--wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java191
-rw-r--r--wifi/tests/src/android/net/wifi/hotspot2/PasspointTestUtils.java172
2950 files changed, 130940 insertions, 103022 deletions
diff --git a/.mailmap b/.mailmap
index b061ccf9e467..40c295ee59a5 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1 +1 @@
-Ember Rose <emberr@google.com> <ashleyrose@google.com>
+Ember Rose <emberrose@google.com> <ashleyrose@google.com>
diff --git a/Android.bp b/Android.bp
index e17a9b45d3ef..3b6eaa7a3afe 100755
--- a/Android.bp
+++ b/Android.bp
@@ -112,6 +112,14 @@ filegroup {
}
filegroup {
+ name: "framework-mime-sources",
+ srcs: [
+ "mime/java/**/*.java",
+ ],
+ path: "mime/java",
+}
+
+filegroup {
name: "framework-opengl-sources",
srcs: [
"opengl/java/**/*.java",
@@ -154,6 +162,23 @@ filegroup {
}
filegroup {
+ name: "framework-telephony-common-sources",
+ srcs: [
+ "telephony/common/**/*.java",
+ ],
+ path: "telephony/common",
+}
+
+filegroup {
+ name: "framework-mms-sources",
+ srcs: [
+ "mms/java/**/*.java",
+ "mms/java/**/*.aidl",
+ ],
+ path: "mms/java",
+}
+
+filegroup {
name: "framework-wifi-sources",
srcs: [
"wifi/java/**/*.java",
@@ -169,22 +194,29 @@ filegroup {
":framework-core-sources",
":framework-drm-sources",
":framework-graphics-sources",
+ ":framework-jobscheduler-sources", // jobscheduler is not a module for R
":framework-keystore-sources",
":framework-location-sources",
":framework-lowpan-sources",
- ":framework-media-sources",
":framework-mca-effect-sources",
":framework-mca-filterfw-sources",
":framework-mca-filterpacks-sources",
+ ":framework-media-sources",
+ ":framework-mime-sources",
+ ":framework-mms-sources",
":framework-opengl-sources",
":framework-rs-sources",
":framework-sax-sources",
":framework-telecomm-sources",
+ ":framework-telephony-common-sources",
":framework-telephony-sources",
":framework-wifi-sources",
":PacProcessor-aidl-sources",
":ProxyHandler-aidl-sources",
+ // AIDL from frameworks/base/native/
+ ":platform-compat-native-aidl",
+
// AIDL sources from external directories
":dumpstate_aidl",
":framework_native_aidl",
@@ -234,6 +266,7 @@ java_defaults {
"media/mca/effect/java",
"media/mca/filterfw/java",
"media/mca/filterpacks/java",
+ "mms/java",
"opengl/java",
"rs/java",
"sax/java",
@@ -244,8 +277,9 @@ java_defaults {
},
required: [
- // TODO: remove gps_debug when the build system propagates "required" properly.
+ // TODO: remove gps_debug and protolog.conf.json when the build system propagates "required" properly.
"gps_debug.conf",
+ "protolog.conf.json.gz",
],
}
@@ -315,7 +349,10 @@ java_defaults {
jarjar_rules: ":framework-jarjar-rules",
- static_libs: ["framework-internal-utils"],
+ static_libs: [
+ "framework-internal-utils",
+ "mimemap",
+ ],
dxflags: [
"--core-library",
@@ -374,12 +411,13 @@ java_library {
installable: true,
static_libs: [
"framework-minus-apex",
- "jobscheduler-framework",
],
required: [
"framework-platform-compat-config",
"libcore-platform-compat-config",
"services-platform-compat-config",
+ "media-provider-platform-compat-config",
+ "services-devicepolicy-platform-compat-config",
],
sdk_version: "core_platform",
}
@@ -393,7 +431,7 @@ java_library {
java_library {
name: "framework-annotation-proc",
- defaults: ["framework-defaults"],
+ defaults: ["framework-aidl-export-defaults"],
srcs: [":framework-all-sources"],
installable: false,
plugins: [
@@ -419,7 +457,9 @@ java_library {
srcs: [
"core/java/android/annotation/IntDef.java",
"core/java/android/annotation/UnsupportedAppUsage.java",
- ":unsupportedappusage_annotation_files",
+ ],
+ static_libs: [
+ "art.module.api.annotations",
],
sdk_version: "core_current",
@@ -798,9 +838,11 @@ frameworks_base_subdirs = [
"media/mca/filterfw/java",
"media/mca/filterpacks/java",
"drm/java",
+ "mms/java",
"opengl/java",
"sax/java",
"telecomm/java",
+ "telephony/common",
"telephony/java",
"wifi/java",
"lowpan/java",
@@ -899,8 +941,10 @@ metalava_framework_docs_args += " --replace-documentation " +
packages_to_document = [
"android",
+ "dalvik",
"java",
"javax",
+ "junit",
"org.apache.http",
"org.json",
"org.w3c.dom",
@@ -916,11 +960,11 @@ stubs_defaults {
"test-base/src/**/*.java",
":opt-telephony-srcs",
":opt-net-voip-srcs",
+ ":core-current-stubs-source",
":core_public_api_files",
":updatable-media-srcs",
"test-mock/src/**/*.java",
"test-runner/src/**/*.java",
- ":jobscheduler-framework-source",
],
libs: framework_docs_only_libs,
local_sourcepaths: frameworks_base_subdirs,
@@ -931,7 +975,7 @@ stubs_defaults {
"sdk-dir",
"api-versions-jars-dir",
],
- previous_api: ":last-released-public-api",
+ previous_api: ":last-released-public-api-for-metalava-annotations",
merge_annotations_dirs: [
"metalava-manual",
"ojluni-annotated-sdk-stubs",
@@ -980,15 +1024,15 @@ stubs_defaults {
"core/java/**/*.logtags",
":opt-telephony-srcs",
":opt-net-voip-srcs",
+ ":core-current-stubs-source",
":core_public_api_files",
":updatable-media-srcs",
- ":jobscheduler-framework-source",
],
libs: ["framework-internal-utils"],
local_sourcepaths: frameworks_base_subdirs,
installable: false,
annotations_enabled: true,
- previous_api: ":last-released-public-api",
+ previous_api: ":last-released-public-api-for-metalava-annotations",
merge_annotations_dirs: [
"metalava-manual",
"ojluni-annotated-sdk-stubs",
@@ -1009,6 +1053,7 @@ droidstubs {
"core/res/AndroidManifest.xml",
],
args: metalava_framework_docs_args,
+ write_sdk_values: true,
}
droidstubs {
@@ -1018,6 +1063,7 @@ droidstubs {
"core/res/AndroidManifest.xml",
],
args: metalava_framework_docs_args + " --show-annotation android.annotation.SystemApi ",
+ write_sdk_values: true,
}
droiddoc {
@@ -1041,7 +1087,6 @@ droiddoc {
],
proofread_file: "offline-sdk-docs-proofrerad.txt",
args: framework_docs_only_args + " -offlinemode -title \"Android SDK\"",
- write_sdk_values: true,
static_doc_index_redirect: "docs/docs-preview-index.html",
}
@@ -1059,7 +1104,6 @@ droiddoc {
],
proofread_file: "offline-sdk-referenceonly-docs-proofrerad.txt",
args: framework_docs_only_args + " -offlinemode -title \"Android SDK\" -referenceonly",
- write_sdk_values: true,
static_doc_index_redirect: "docs/docs-documentation-redirect.html",
static_doc_properties: "docs/source.properties",
}
@@ -1079,7 +1123,6 @@ droiddoc {
proofread_file: "offline-system-sdk-referenceonly-docs-proofrerad.txt",
args: framework_docs_only_args + " -hide 101 -hide 104 -hide 108" +
" -offlinemode -title \"Android System SDK\" -referenceonly",
- write_sdk_values: true,
static_doc_index_redirect: "docs/docs-documentation-redirect.html",
static_doc_properties: "docs/source.properties",
}
@@ -1324,7 +1367,7 @@ droidstubs {
installable: false,
sdk_version: "core_platform",
annotations_enabled: true,
- previous_api: ":last-released-public-api",
+ previous_api: ":last-released-public-api-for-metalava-annotations",
merge_annotations_dirs: [
"metalava-manual",
"ojluni-annotated-sdk-stubs",
@@ -1419,6 +1462,11 @@ droidstubs {
removed_api_file: "api/removed.txt",
baseline_file: ":public-api-incompatibilities-with-last-released",
},
+ api_lint: {
+ enabled: true,
+ new_since: ":last-released-public-api",
+ baseline_file: "api/lint-baseline.txt",
+ },
},
jdiff_enabled: true,
}
@@ -1445,6 +1493,11 @@ droidstubs {
removed_api_file: "api/system-removed.txt",
baseline_file: ":system-api-incompatibilities-with-last-released"
},
+ api_lint: {
+ enabled: true,
+ new_since: ":last-released-system-api",
+ baseline_file: "api/system-lint-baseline.txt",
+ },
},
jdiff_enabled: true,
}
@@ -1511,3 +1564,57 @@ genrule {
targets: ["droidcore"],
},
}
+
+// Avoid including Parcelable classes as we don't want to have two copies of
+// Parcelable cross the process.
+filegroup {
+ name: "framework-telephony-stack-shared-srcs",
+ srcs: [
+ "core/java/android/os/RegistrantList.java",
+ "core/java/android/os/Registrant.java",
+ "core/java/android/util/LocalLog.java",
+ "core/java/android/util/Slog.java",
+ "core/java/android/util/TimeUtils.java",
+ "core/java/com/android/internal/os/SomeArgs.java",
+ "core/java/com/android/internal/util/Preconditions.java",
+ "core/java/com/android/internal/util/State.java",
+ "core/java/com/android/internal/util/StateMachine.java",
+ "core/java/com/android/internal/util/XmlUtils.java",
+ "core/java/com/android/internal/util/HexDump.java",
+ "core/java/com/android/internal/util/IndentingPrintWriter.java",
+ "core/java/com/android/internal/util/DumpUtils.java"
+ ],
+}
+
+// Avoid including Parcelable classes as we don't want to have two copies of
+// Parcelable cross the process.
+filegroup {
+ name: "framework-cellbroadcast-shared-srcs",
+ srcs: [
+ "core/java/android/util/LocalLog.java",
+ "core/java/android/util/Slog.java",
+ "core/java/com/android/internal/util/State.java",
+ "core/java/com/android/internal/util/StateMachine.java",
+ ],
+}
+
+filegroup {
+ name: "framework-wifistack-shared-srcs",
+ srcs: [
+ ":framework-annotations",
+ "core/java/android/util/KeyValueListParser.java",
+ "core/java/android/util/LocalLog.java",
+ "core/java/android/util/Rational.java",
+ "core/java/android/util/proto/ProtoStream.java",
+ "core/java/android/util/proto/ProtoOutputStream.java",
+ "core/java/com/android/internal/util/FastXmlSerializer.java",
+ "core/java/com/android/internal/util/HexDump.java",
+ "core/java/com/android/internal/util/IState.java",
+ "core/java/com/android/internal/util/MessageUtils.java",
+ "core/java/com/android/internal/util/Preconditions.java",
+ "core/java/com/android/internal/util/State.java",
+ "core/java/com/android/internal/util/StateMachine.java",
+ "core/java/com/android/internal/util/WakeupMessage.java",
+ "core/java/com/android/internal/util/XmlUtils.java",
+ ],
+}
diff --git a/Android.mk b/Android.mk
index 9bda2dc6d69a..815a169f3880 100755
--- a/Android.mk
+++ b/Android.mk
@@ -54,6 +54,24 @@ $(OUT_DOCS)/offline-sdk-timestamp: $(OUT_DOCS)/offline-sdk-docs-docs.zip
.PHONY: docs offline-sdk-docs
docs offline-sdk-docs: $(OUT_DOCS)/offline-sdk-timestamp
+SDK_METADATA_DIR :=$= $(call intermediates-dir-for,PACKAGING,framework-doc-stubs-metadata,,COMMON)
+SDK_METADATA_FILES :=$= $(addprefix $(SDK_METADATA_DIR)/,\
+ activity_actions.txt \
+ broadcast_actions.txt \
+ categories.txt \
+ features.txt \
+ service_actions.txt \
+ widgets.txt)
+SDK_METADATA :=$= $(firstword $(SDK_METADATA_FILES))
+$(SDK_METADATA): .KATI_IMPLICIT_OUTPUTS := $(filter-out $(SDK_METADATA),$(SDK_METADATA_FILES))
+$(SDK_METADATA): $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/framework-doc-stubs-metadata.zip
+ rm -rf $(SDK_METADATA_DIR)
+ mkdir -p $(SDK_METADATA_DIR)
+ unzip -qo $< -d $(SDK_METADATA_DIR)
+
+.PHONY: framework-doc-stubs
+framework-doc-stubs: $(SDK_METADATA)
+
# Run this for checkbuild
checkbuild: doc-comment-check-docs
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 6a909c0364e9..f7a285835126 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -254,6 +254,7 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/DynamicAndroidInsta
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/DefaultContainerService)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/CaptivePortalLogin)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/ext.jar)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/telephony/java/com/google/android/mms)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
# ******************************************************************
diff --git a/apct-tests/perftests/autofill/AndroidManifest.xml b/apct-tests/perftests/autofill/AndroidManifest.xml
index 9c8abc32eb72..1e3532b3c1ef 100644
--- a/apct-tests/perftests/autofill/AndroidManifest.xml
+++ b/apct-tests/perftests/autofill/AndroidManifest.xml
@@ -18,7 +18,7 @@
<application>
<uses-library android:name="android.test.runner" />
- <activity android:name="android.perftests.utils.StubActivity">
+ <activity android:name="android.perftests.utils.PerfTestActivity">
<intent-filter>
<action android:name="com.android.perftests.core.PERFTEST" />
</intent-filter>
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java b/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
index 6979f0f0875d..48ce8ab2fce5 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
@@ -20,9 +20,9 @@ import static org.junit.Assert.assertTrue;
import android.os.Looper;
import android.perftests.utils.PerfStatusReporter;
+import android.perftests.utils.PerfTestActivity;
import android.perftests.utils.SettingsHelper;
import android.perftests.utils.SettingsStateKeeperRule;
-import android.perftests.utils.StubActivity;
import android.provider.Settings;
import androidx.test.InstrumentationRegistry;
@@ -46,8 +46,8 @@ public abstract class AbstractAutofillPerfTestCase {
Settings.Secure.AUTOFILL_SERVICE);
@Rule
- public ActivityTestRule<StubActivity> mActivityRule =
- new ActivityTestRule<StubActivity>(StubActivity.class);
+ public ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule<>(PerfTestActivity.class);
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@@ -68,7 +68,7 @@ public abstract class AbstractAutofillPerfTestCase {
Looper.getMainLooper().getThread() == Thread.currentThread());
assertTrue("We should be running on the main thread",
Looper.myLooper() == Looper.getMainLooper());
- StubActivity activity = mActivityRule.getActivity();
+ PerfTestActivity activity = mActivityRule.getActivity();
activity.setContentView(mLayoutId);
onCreate(activity);
});
@@ -89,9 +89,9 @@ public abstract class AbstractAutofillPerfTestCase {
}
/**
- * Initializes the {@link StubActivity} after it was launched.
+ * Initializes the {@link PerfTestActivity} after it was launched.
*/
- protected abstract void onCreate(StubActivity activity);
+ protected abstract void onCreate(PerfTestActivity activity);
/**
* Uses the {@code settings} binary to set the autofill service.
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
index 80908266c5c0..fb5ea80e6ed1 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
@@ -20,7 +20,7 @@ import static android.view.autofill.AutofillManager.AutofillCallback.EVENT_INPUT
import static android.view.autofill.AutofillManager.AutofillCallback.EVENT_INPUT_SHOWN;
import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import android.view.View;
import android.widget.EditText;
@@ -39,7 +39,7 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
}
@Override
- protected void onCreate(StubActivity activity) {
+ protected void onCreate(PerfTestActivity activity) {
View root = activity.getWindow().getDecorView();
mUsername = root.findViewById(R.id.username);
mPassword = root.findViewById(R.id.password);
diff --git a/apct-tests/perftests/core/Android.mk b/apct-tests/perftests/core/Android.mk
index 3f87a1c8b598..968478c3f338 100644
--- a/apct-tests/perftests/core/Android.mk
+++ b/apct-tests/perftests/core/Android.mk
@@ -9,8 +9,11 @@ LOCAL_SRC_FILES := \
src/android/os/ISomeService.aidl
LOCAL_STATIC_JAVA_LIBRARIES := \
+ androidx.appcompat_appcompat \
androidx.test.rules \
androidx.annotation_annotation \
+ apct-perftests-overlay-apps \
+ apct-perftests-resources-manager-apps \
apct-perftests-utils \
guava
@@ -25,5 +28,6 @@ LOCAL_JNI_SHARED_LIBRARIES := libperftestscore_jni
LOCAL_ASSET_DIR := $(TOP)/external/google-fonts/dancing-script
LOCAL_COMPATIBILITY_SUITE += device-tests
+LOCAL_CERTIFICATE := platform
-include $(BUILD_PACKAGE)
+include $(BUILD_PACKAGE) \ No newline at end of file
diff --git a/apct-tests/perftests/core/AndroidManifest.xml b/apct-tests/perftests/core/AndroidManifest.xml
index a564a4d27fb3..290f178fb669 100644
--- a/apct-tests/perftests/core/AndroidManifest.xml
+++ b/apct-tests/perftests/core/AndroidManifest.xml
@@ -5,12 +5,15 @@
<permission android:name="com.android.perftests.core.TestPermission" />
<uses-permission android:name="com.android.perftests.core.TestPermission" />
- <uses-permission
- android:name="android.permission.GET_ACCOUNTS" />
+ <uses-permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES" />
+ <uses-permission android:name="android.permission.DELETE_PACKAGES" />
+ <uses-permission android:name="android.permission.GET_ACCOUNTS" />
+ <uses-permission android:name="android.permission.INSTALL_PACKAGES"/>
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<application>
<uses-library android:name="android.test.runner" />
- <activity android:name="android.perftests.utils.StubActivity">
+ <activity android:name="android.perftests.utils.PerfTestActivity">
<intent-filter>
<action android:name="com.android.perftests.core.PERFTEST" />
</intent-filter>
diff --git a/apct-tests/perftests/core/apps/overlay/Android.bp b/apct-tests/perftests/core/apps/overlay/Android.bp
new file mode 100644
index 000000000000..7bee30ee9cb4
--- /dev/null
+++ b/apct-tests/perftests/core/apps/overlay/Android.bp
@@ -0,0 +1,188 @@
+// 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.
+
+android_test_helper_app {
+ name: "Overlay0",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay0",
+ ]
+}
+
+android_test_helper_app {
+ name: "Overlay1",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay1",
+ ]
+}
+
+android_test_helper_app {
+ name: "Overlay2",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay2",
+ ]
+}
+
+android_test_helper_app {
+ name: "Overlay3",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay3",
+ ]
+}
+
+android_test_helper_app {
+ name: "Overlay4",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay4",
+ ]
+}
+
+android_test_helper_app {
+ name: "Overlay5",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay5",
+ ]
+}
+
+android_test_helper_app {
+ name: "Overlay6",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay6",
+ ]
+}
+
+android_test_helper_app {
+ name: "Overlay7",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay7",
+ ]
+}
+android_test_helper_app {
+ name: "Overlay8",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay8",
+ ]
+}
+
+android_test_helper_app {
+ name: "Overlay9",
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay9",
+ ]
+}
+
+android_test_helper_app {
+ name: "LargeOverlay0",
+ resource_dirs : [ "res_large" ],
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay.large0",
+ ]
+}
+
+android_test_helper_app {
+ name: "LargeOverlay1",
+ resource_dirs : [ "res_large" ],
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay.large1",
+ ]
+}
+
+android_test_helper_app {
+ name: "LargeOverlay2",
+ resource_dirs : [ "res_large" ],
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay.large2",
+ ]
+}
+
+android_test_helper_app {
+ name: "LargeOverlay3",
+ resource_dirs : [ "res_large" ],
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay.large3",
+ ]
+}
+
+android_test_helper_app {
+ name: "LargeOverlay4",
+ resource_dirs : [ "res_large" ],
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay.large4",
+ ]
+}
+
+android_test_helper_app {
+ name: "LargeOverlay5",
+ resource_dirs : [ "res_large" ],
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay.large5",
+ ]
+}
+
+android_test_helper_app {
+ name: "LargeOverlay6",
+ resource_dirs : [ "res_large" ],
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay.large6",
+ ]
+}
+
+android_test_helper_app {
+ name: "LargeOverlay7",
+ resource_dirs : [ "res_large" ],
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay.large7",
+ ]
+}
+
+android_test_helper_app {
+ name: "LargeOverlay8",
+ resource_dirs : [ "res_large" ],
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay.large8",
+ ]
+}
+
+android_test_helper_app {
+ name: "LargeOverlay9",
+ resource_dirs : [ "res_large" ],
+ aaptflags: [
+ "--rename-manifest-package com.android.perftests.overlay.large9",
+ ]
+}
+
+java_library {
+ name: "apct-perftests-overlay-apps",
+ java_resources: [
+ ":Overlay0",
+ ":Overlay1",
+ ":Overlay2",
+ ":Overlay3",
+ ":Overlay4",
+ ":Overlay5",
+ ":Overlay6",
+ ":Overlay7",
+ ":Overlay8",
+ ":Overlay9",
+ ":LargeOverlay0",
+ ":LargeOverlay1",
+ ":LargeOverlay2",
+ ":LargeOverlay3",
+ ":LargeOverlay4",
+ ":LargeOverlay5",
+ ":LargeOverlay6",
+ ":LargeOverlay7",
+ ":LargeOverlay8",
+ ":LargeOverlay9",
+ ],
+} \ No newline at end of file
diff --git a/apct-tests/perftests/core/apps/overlay/AndroidManifest.xml b/apct-tests/perftests/core/apps/overlay/AndroidManifest.xml
new file mode 100644
index 000000000000..52f5a89bc9e4
--- /dev/null
+++ b/apct-tests/perftests/core/apps/overlay/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.perftests.overlay">
+ <application android:hasCode="false" />
+ <uses-sdk android:targetSdkVersion="29" />
+ <overlay android:targetPackage="com.android.perftests.core" android:targetName="TestResources"/>
+</manifest> \ No newline at end of file
diff --git a/apct-tests/perftests/core/apps/overlay/res/values/values.xml b/apct-tests/perftests/core/apps/overlay/res/values/values.xml
new file mode 100644
index 000000000000..a1a8d83eb193
--- /dev/null
+++ b/apct-tests/perftests/core/apps/overlay/res/values/values.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<resources>
+ <string name="short_text">B</string>
+</resources>
diff --git a/apct-tests/perftests/core/apps/overlay/res_large/values/values.xml b/apct-tests/perftests/core/apps/overlay/res_large/values/values.xml
new file mode 100644
index 000000000000..e74144648e32
--- /dev/null
+++ b/apct-tests/perftests/core/apps/overlay/res_large/values/values.xml
@@ -0,0 +1,274 @@
+<?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.
+-->
+
+<resources>
+ <string name="short_text000">B</string>
+ <string name="short_text001">B</string>
+ <string name="short_text002">B</string>
+ <string name="short_text003">B</string>
+ <string name="short_text004">B</string>
+ <string name="short_text005">B</string>
+ <string name="short_text006">B</string>
+ <string name="short_text007">B</string>
+ <string name="short_text008">B</string>
+ <string name="short_text009">B</string>
+ <string name="short_text010">B</string>
+ <string name="short_text011">B</string>
+ <string name="short_text012">B</string>
+ <string name="short_text013">B</string>
+ <string name="short_text014">B</string>
+ <string name="short_text015">B</string>
+ <string name="short_text016">B</string>
+ <string name="short_text017">B</string>
+ <string name="short_text018">B</string>
+ <string name="short_text019">B</string>
+ <string name="short_text020">B</string>
+ <string name="short_text021">B</string>
+ <string name="short_text022">B</string>
+ <string name="short_text023">B</string>
+ <string name="short_text024">B</string>
+ <string name="short_text025">B</string>
+ <string name="short_text026">B</string>
+ <string name="short_text027">B</string>
+ <string name="short_text028">B</string>
+ <string name="short_text029">B</string>
+ <string name="short_text030">B</string>
+ <string name="short_text031">B</string>
+ <string name="short_text032">B</string>
+ <string name="short_text033">B</string>
+ <string name="short_text034">B</string>
+ <string name="short_text035">B</string>
+ <string name="short_text036">B</string>
+ <string name="short_text037">B</string>
+ <string name="short_text038">B</string>
+ <string name="short_text039">B</string>
+ <string name="short_text040">B</string>
+ <string name="short_text041">B</string>
+ <string name="short_text042">B</string>
+ <string name="short_text043">B</string>
+ <string name="short_text044">B</string>
+ <string name="short_text045">B</string>
+ <string name="short_text046">B</string>
+ <string name="short_text047">B</string>
+ <string name="short_text048">B</string>
+ <string name="short_text049">B</string>
+ <string name="short_text050">B</string>
+ <string name="short_text051">B</string>
+ <string name="short_text052">B</string>
+ <string name="short_text053">B</string>
+ <string name="short_text054">B</string>
+ <string name="short_text055">B</string>
+ <string name="short_text056">B</string>
+ <string name="short_text057">B</string>
+ <string name="short_text058">B</string>
+ <string name="short_text059">B</string>
+ <string name="short_text060">B</string>
+ <string name="short_text061">B</string>
+ <string name="short_text062">B</string>
+ <string name="short_text063">B</string>
+ <string name="short_text064">B</string>
+ <string name="short_text065">B</string>
+ <string name="short_text066">B</string>
+ <string name="short_text067">B</string>
+ <string name="short_text068">B</string>
+ <string name="short_text069">B</string>
+ <string name="short_text070">B</string>
+ <string name="short_text071">B</string>
+ <string name="short_text072">B</string>
+ <string name="short_text073">B</string>
+ <string name="short_text074">B</string>
+ <string name="short_text075">B</string>
+ <string name="short_text076">B</string>
+ <string name="short_text077">B</string>
+ <string name="short_text078">B</string>
+ <string name="short_text079">B</string>
+ <string name="short_text080">B</string>
+ <string name="short_text081">B</string>
+ <string name="short_text082">B</string>
+ <string name="short_text083">B</string>
+ <string name="short_text084">B</string>
+ <string name="short_text085">B</string>
+ <string name="short_text086">B</string>
+ <string name="short_text087">B</string>
+ <string name="short_text088">B</string>
+ <string name="short_text089">B</string>
+ <string name="short_text090">B</string>
+ <string name="short_text091">B</string>
+ <string name="short_text092">B</string>
+ <string name="short_text093">B</string>
+ <string name="short_text094">B</string>
+ <string name="short_text095">B</string>
+ <string name="short_text096">B</string>
+ <string name="short_text097">B</string>
+ <string name="short_text098">B</string>
+ <string name="short_text099">B</string>
+ <string name="short_text100">B</string>
+ <string name="short_text101">B</string>
+ <string name="short_text102">B</string>
+ <string name="short_text103">B</string>
+ <string name="short_text104">B</string>
+ <string name="short_text105">B</string>
+ <string name="short_text106">B</string>
+ <string name="short_text107">B</string>
+ <string name="short_text108">B</string>
+ <string name="short_text109">B</string>
+ <string name="short_text110">B</string>
+ <string name="short_text111">B</string>
+ <string name="short_text112">B</string>
+ <string name="short_text113">B</string>
+ <string name="short_text114">B</string>
+ <string name="short_text115">B</string>
+ <string name="short_text116">B</string>
+ <string name="short_text117">B</string>
+ <string name="short_text118">B</string>
+ <string name="short_text119">B</string>
+ <string name="short_text120">B</string>
+ <string name="short_text121">B</string>
+ <string name="short_text122">B</string>
+ <string name="short_text123">B</string>
+ <string name="short_text124">B</string>
+ <string name="short_text125">B</string>
+ <string name="short_text126">B</string>
+ <string name="short_text127">B</string>
+ <string name="short_text128">B</string>
+ <string name="short_text129">B</string>
+ <string name="short_text130">B</string>
+ <string name="short_text131">B</string>
+ <string name="short_text132">B</string>
+ <string name="short_text133">B</string>
+ <string name="short_text134">B</string>
+ <string name="short_text135">B</string>
+ <string name="short_text136">B</string>
+ <string name="short_text137">B</string>
+ <string name="short_text138">B</string>
+ <string name="short_text139">B</string>
+ <string name="short_text140">B</string>
+ <string name="short_text141">B</string>
+ <string name="short_text142">B</string>
+ <string name="short_text143">B</string>
+ <string name="short_text144">B</string>
+ <string name="short_text145">B</string>
+ <string name="short_text146">B</string>
+ <string name="short_text147">B</string>
+ <string name="short_text148">B</string>
+ <string name="short_text149">B</string>
+ <string name="short_text150">B</string>
+ <string name="short_text151">B</string>
+ <string name="short_text152">B</string>
+ <string name="short_text153">B</string>
+ <string name="short_text154">B</string>
+ <string name="short_text155">B</string>
+ <string name="short_text156">B</string>
+ <string name="short_text157">B</string>
+ <string name="short_text158">B</string>
+ <string name="short_text159">B</string>
+ <string name="short_text160">B</string>
+ <string name="short_text161">B</string>
+ <string name="short_text162">B</string>
+ <string name="short_text163">B</string>
+ <string name="short_text164">B</string>
+ <string name="short_text165">B</string>
+ <string name="short_text166">B</string>
+ <string name="short_text167">B</string>
+ <string name="short_text168">B</string>
+ <string name="short_text169">B</string>
+ <string name="short_text170">B</string>
+ <string name="short_text171">B</string>
+ <string name="short_text172">B</string>
+ <string name="short_text173">B</string>
+ <string name="short_text174">B</string>
+ <string name="short_text175">B</string>
+ <string name="short_text176">B</string>
+ <string name="short_text177">B</string>
+ <string name="short_text178">B</string>
+ <string name="short_text179">B</string>
+ <string name="short_text180">B</string>
+ <string name="short_text181">B</string>
+ <string name="short_text182">B</string>
+ <string name="short_text183">B</string>
+ <string name="short_text184">B</string>
+ <string name="short_text185">B</string>
+ <string name="short_text186">B</string>
+ <string name="short_text187">B</string>
+ <string name="short_text188">B</string>
+ <string name="short_text189">B</string>
+ <string name="short_text190">B</string>
+ <string name="short_text191">B</string>
+ <string name="short_text192">B</string>
+ <string name="short_text193">B</string>
+ <string name="short_text194">B</string>
+ <string name="short_text195">B</string>
+ <string name="short_text196">B</string>
+ <string name="short_text197">B</string>
+ <string name="short_text198">B</string>
+ <string name="short_text199">B</string>
+ <string name="short_text200">B</string>
+ <string name="short_text201">B</string>
+ <string name="short_text202">B</string>
+ <string name="short_text203">B</string>
+ <string name="short_text204">B</string>
+ <string name="short_text205">B</string>
+ <string name="short_text206">B</string>
+ <string name="short_text207">B</string>
+ <string name="short_text208">B</string>
+ <string name="short_text209">B</string>
+ <string name="short_text210">B</string>
+ <string name="short_text211">B</string>
+ <string name="short_text212">B</string>
+ <string name="short_text213">B</string>
+ <string name="short_text214">B</string>
+ <string name="short_text215">B</string>
+ <string name="short_text216">B</string>
+ <string name="short_text217">B</string>
+ <string name="short_text218">B</string>
+ <string name="short_text219">B</string>
+ <string name="short_text220">B</string>
+ <string name="short_text221">B</string>
+ <string name="short_text222">B</string>
+ <string name="short_text223">B</string>
+ <string name="short_text224">B</string>
+ <string name="short_text225">B</string>
+ <string name="short_text226">B</string>
+ <string name="short_text227">B</string>
+ <string name="short_text228">B</string>
+ <string name="short_text229">B</string>
+ <string name="short_text230">B</string>
+ <string name="short_text231">B</string>
+ <string name="short_text232">B</string>
+ <string name="short_text233">B</string>
+ <string name="short_text234">B</string>
+ <string name="short_text235">B</string>
+ <string name="short_text236">B</string>
+ <string name="short_text237">B</string>
+ <string name="short_text238">B</string>
+ <string name="short_text239">B</string>
+ <string name="short_text240">B</string>
+ <string name="short_text241">B</string>
+ <string name="short_text242">B</string>
+ <string name="short_text243">B</string>
+ <string name="short_text244">B</string>
+ <string name="short_text245">B</string>
+ <string name="short_text246">B</string>
+ <string name="short_text247">B</string>
+ <string name="short_text248">B</string>
+ <string name="short_text249">B</string>
+ <string name="short_text250">B</string>
+ <string name="short_text251">B</string>
+ <string name="short_text252">B</string>
+ <string name="short_text253">B</string>
+ <string name="short_text254">B</string>
+ <string name="short_text255">B</string>
+</resources>
diff --git a/apct-tests/perftests/core/apps/reources_manager/Android.bp b/apct-tests/perftests/core/apps/reources_manager/Android.bp
new file mode 100644
index 000000000000..451613236140
--- /dev/null
+++ b/apct-tests/perftests/core/apps/reources_manager/Android.bp
@@ -0,0 +1,34 @@
+// 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.
+
+android_test_helper_app {
+ name: "LargeResourcesCompressed",
+ static_libs: [ "androidx.appcompat_appcompat" ],
+}
+
+genrule {
+ name: "LargeResourcesUncompressed",
+ srcs: [ ":LargeResourcesCompressed" ],
+ out: ["LargeResourcesUncompressed.apk"],
+ cmd: "cp $(in) $(out) && unzip -o $(out) resources.arsc"
+ + " && zip $(out) resources.arsc"
+}
+
+java_library {
+ name: "apct-perftests-resources-manager-apps",
+ java_resources: [
+ ":LargeResourcesCompressed",
+ ":LargeResourcesUncompressed",
+ ],
+} \ No newline at end of file
diff --git a/apct-tests/perftests/core/apps/reources_manager/AndroidManifest.xml b/apct-tests/perftests/core/apps/reources_manager/AndroidManifest.xml
new file mode 100644
index 000000000000..adb4e406c608
--- /dev/null
+++ b/apct-tests/perftests/core/apps/reources_manager/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="fake.android">
+ <application android:hasCode="false" />
+ <uses-sdk android:targetSdkVersion="29" />
+</manifest> \ No newline at end of file
diff --git a/apct-tests/perftests/core/res/color/color_state_list.xml b/apct-tests/perftests/core/res/color/color_state_list.xml
new file mode 100644
index 000000000000..142e47ae2738
--- /dev/null
+++ b/apct-tests/perftests/core/res/color/color_state_list.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true"
+ android:color="#000000" />
+ <item android:state_pressed="true"
+ android:state_enabled="false"
+ android:color="#212121" />
+ <item android:state_enabled="false"
+ android:color="#414141" />
+ <item android:color="#616161" />
+</selector> \ No newline at end of file
diff --git a/apct-tests/perftests/core/res/values/overlayable.xml b/apct-tests/perftests/core/res/values/overlayable.xml
new file mode 100644
index 000000000000..70cedd7b0b75
--- /dev/null
+++ b/apct-tests/perftests/core/res/values/overlayable.xml
@@ -0,0 +1,280 @@
+<?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
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <overlayable name="TestResources">
+ <policy type="public">
+ <item type="string" name="short_text" />
+ <item type="string" name="short_text000" />
+ <item type="string" name="short_text001" />
+ <item type="string" name="short_text002" />
+ <item type="string" name="short_text003" />
+ <item type="string" name="short_text004" />
+ <item type="string" name="short_text005" />
+ <item type="string" name="short_text006" />
+ <item type="string" name="short_text007" />
+ <item type="string" name="short_text008" />
+ <item type="string" name="short_text009" />
+ <item type="string" name="short_text010" />
+ <item type="string" name="short_text011" />
+ <item type="string" name="short_text012" />
+ <item type="string" name="short_text013" />
+ <item type="string" name="short_text014" />
+ <item type="string" name="short_text015" />
+ <item type="string" name="short_text016" />
+ <item type="string" name="short_text017" />
+ <item type="string" name="short_text018" />
+ <item type="string" name="short_text019" />
+ <item type="string" name="short_text020" />
+ <item type="string" name="short_text021" />
+ <item type="string" name="short_text022" />
+ <item type="string" name="short_text023" />
+ <item type="string" name="short_text024" />
+ <item type="string" name="short_text025" />
+ <item type="string" name="short_text026" />
+ <item type="string" name="short_text027" />
+ <item type="string" name="short_text028" />
+ <item type="string" name="short_text029" />
+ <item type="string" name="short_text030" />
+ <item type="string" name="short_text031" />
+ <item type="string" name="short_text032" />
+ <item type="string" name="short_text033" />
+ <item type="string" name="short_text034" />
+ <item type="string" name="short_text035" />
+ <item type="string" name="short_text036" />
+ <item type="string" name="short_text037" />
+ <item type="string" name="short_text038" />
+ <item type="string" name="short_text039" />
+ <item type="string" name="short_text040" />
+ <item type="string" name="short_text041" />
+ <item type="string" name="short_text042" />
+ <item type="string" name="short_text043" />
+ <item type="string" name="short_text044" />
+ <item type="string" name="short_text045" />
+ <item type="string" name="short_text046" />
+ <item type="string" name="short_text047" />
+ <item type="string" name="short_text048" />
+ <item type="string" name="short_text049" />
+ <item type="string" name="short_text050" />
+ <item type="string" name="short_text051" />
+ <item type="string" name="short_text052" />
+ <item type="string" name="short_text053" />
+ <item type="string" name="short_text054" />
+ <item type="string" name="short_text055" />
+ <item type="string" name="short_text056" />
+ <item type="string" name="short_text057" />
+ <item type="string" name="short_text058" />
+ <item type="string" name="short_text059" />
+ <item type="string" name="short_text060" />
+ <item type="string" name="short_text061" />
+ <item type="string" name="short_text062" />
+ <item type="string" name="short_text063" />
+ <item type="string" name="short_text064" />
+ <item type="string" name="short_text065" />
+ <item type="string" name="short_text066" />
+ <item type="string" name="short_text067" />
+ <item type="string" name="short_text068" />
+ <item type="string" name="short_text069" />
+ <item type="string" name="short_text070" />
+ <item type="string" name="short_text071" />
+ <item type="string" name="short_text072" />
+ <item type="string" name="short_text073" />
+ <item type="string" name="short_text074" />
+ <item type="string" name="short_text075" />
+ <item type="string" name="short_text076" />
+ <item type="string" name="short_text077" />
+ <item type="string" name="short_text078" />
+ <item type="string" name="short_text079" />
+ <item type="string" name="short_text080" />
+ <item type="string" name="short_text081" />
+ <item type="string" name="short_text082" />
+ <item type="string" name="short_text083" />
+ <item type="string" name="short_text084" />
+ <item type="string" name="short_text085" />
+ <item type="string" name="short_text086" />
+ <item type="string" name="short_text087" />
+ <item type="string" name="short_text088" />
+ <item type="string" name="short_text089" />
+ <item type="string" name="short_text090" />
+ <item type="string" name="short_text091" />
+ <item type="string" name="short_text092" />
+ <item type="string" name="short_text093" />
+ <item type="string" name="short_text094" />
+ <item type="string" name="short_text095" />
+ <item type="string" name="short_text096" />
+ <item type="string" name="short_text097" />
+ <item type="string" name="short_text098" />
+ <item type="string" name="short_text099" />
+ <item type="string" name="short_text100" />
+ <item type="string" name="short_text101" />
+ <item type="string" name="short_text102" />
+ <item type="string" name="short_text103" />
+ <item type="string" name="short_text104" />
+ <item type="string" name="short_text105" />
+ <item type="string" name="short_text106" />
+ <item type="string" name="short_text107" />
+ <item type="string" name="short_text108" />
+ <item type="string" name="short_text109" />
+ <item type="string" name="short_text110" />
+ <item type="string" name="short_text111" />
+ <item type="string" name="short_text112" />
+ <item type="string" name="short_text113" />
+ <item type="string" name="short_text114" />
+ <item type="string" name="short_text115" />
+ <item type="string" name="short_text116" />
+ <item type="string" name="short_text117" />
+ <item type="string" name="short_text118" />
+ <item type="string" name="short_text119" />
+ <item type="string" name="short_text120" />
+ <item type="string" name="short_text121" />
+ <item type="string" name="short_text122" />
+ <item type="string" name="short_text123" />
+ <item type="string" name="short_text124" />
+ <item type="string" name="short_text125" />
+ <item type="string" name="short_text126" />
+ <item type="string" name="short_text127" />
+ <item type="string" name="short_text128" />
+ <item type="string" name="short_text129" />
+ <item type="string" name="short_text130" />
+ <item type="string" name="short_text131" />
+ <item type="string" name="short_text132" />
+ <item type="string" name="short_text133" />
+ <item type="string" name="short_text134" />
+ <item type="string" name="short_text135" />
+ <item type="string" name="short_text136" />
+ <item type="string" name="short_text137" />
+ <item type="string" name="short_text138" />
+ <item type="string" name="short_text139" />
+ <item type="string" name="short_text140" />
+ <item type="string" name="short_text141" />
+ <item type="string" name="short_text142" />
+ <item type="string" name="short_text143" />
+ <item type="string" name="short_text144" />
+ <item type="string" name="short_text145" />
+ <item type="string" name="short_text146" />
+ <item type="string" name="short_text147" />
+ <item type="string" name="short_text148" />
+ <item type="string" name="short_text149" />
+ <item type="string" name="short_text150" />
+ <item type="string" name="short_text151" />
+ <item type="string" name="short_text152" />
+ <item type="string" name="short_text153" />
+ <item type="string" name="short_text154" />
+ <item type="string" name="short_text155" />
+ <item type="string" name="short_text156" />
+ <item type="string" name="short_text157" />
+ <item type="string" name="short_text158" />
+ <item type="string" name="short_text159" />
+ <item type="string" name="short_text160" />
+ <item type="string" name="short_text161" />
+ <item type="string" name="short_text162" />
+ <item type="string" name="short_text163" />
+ <item type="string" name="short_text164" />
+ <item type="string" name="short_text165" />
+ <item type="string" name="short_text166" />
+ <item type="string" name="short_text167" />
+ <item type="string" name="short_text168" />
+ <item type="string" name="short_text169" />
+ <item type="string" name="short_text170" />
+ <item type="string" name="short_text171" />
+ <item type="string" name="short_text172" />
+ <item type="string" name="short_text173" />
+ <item type="string" name="short_text174" />
+ <item type="string" name="short_text175" />
+ <item type="string" name="short_text176" />
+ <item type="string" name="short_text177" />
+ <item type="string" name="short_text178" />
+ <item type="string" name="short_text179" />
+ <item type="string" name="short_text180" />
+ <item type="string" name="short_text181" />
+ <item type="string" name="short_text182" />
+ <item type="string" name="short_text183" />
+ <item type="string" name="short_text184" />
+ <item type="string" name="short_text185" />
+ <item type="string" name="short_text186" />
+ <item type="string" name="short_text187" />
+ <item type="string" name="short_text188" />
+ <item type="string" name="short_text189" />
+ <item type="string" name="short_text190" />
+ <item type="string" name="short_text191" />
+ <item type="string" name="short_text192" />
+ <item type="string" name="short_text193" />
+ <item type="string" name="short_text194" />
+ <item type="string" name="short_text195" />
+ <item type="string" name="short_text196" />
+ <item type="string" name="short_text197" />
+ <item type="string" name="short_text198" />
+ <item type="string" name="short_text199" />
+ <item type="string" name="short_text200" />
+ <item type="string" name="short_text201" />
+ <item type="string" name="short_text202" />
+ <item type="string" name="short_text203" />
+ <item type="string" name="short_text204" />
+ <item type="string" name="short_text205" />
+ <item type="string" name="short_text206" />
+ <item type="string" name="short_text207" />
+ <item type="string" name="short_text208" />
+ <item type="string" name="short_text209" />
+ <item type="string" name="short_text210" />
+ <item type="string" name="short_text211" />
+ <item type="string" name="short_text212" />
+ <item type="string" name="short_text213" />
+ <item type="string" name="short_text214" />
+ <item type="string" name="short_text215" />
+ <item type="string" name="short_text216" />
+ <item type="string" name="short_text217" />
+ <item type="string" name="short_text218" />
+ <item type="string" name="short_text219" />
+ <item type="string" name="short_text220" />
+ <item type="string" name="short_text221" />
+ <item type="string" name="short_text222" />
+ <item type="string" name="short_text223" />
+ <item type="string" name="short_text224" />
+ <item type="string" name="short_text225" />
+ <item type="string" name="short_text226" />
+ <item type="string" name="short_text227" />
+ <item type="string" name="short_text228" />
+ <item type="string" name="short_text229" />
+ <item type="string" name="short_text230" />
+ <item type="string" name="short_text231" />
+ <item type="string" name="short_text232" />
+ <item type="string" name="short_text233" />
+ <item type="string" name="short_text234" />
+ <item type="string" name="short_text235" />
+ <item type="string" name="short_text236" />
+ <item type="string" name="short_text237" />
+ <item type="string" name="short_text238" />
+ <item type="string" name="short_text239" />
+ <item type="string" name="short_text240" />
+ <item type="string" name="short_text241" />
+ <item type="string" name="short_text242" />
+ <item type="string" name="short_text243" />
+ <item type="string" name="short_text244" />
+ <item type="string" name="short_text245" />
+ <item type="string" name="short_text246" />
+ <item type="string" name="short_text247" />
+ <item type="string" name="short_text248" />
+ <item type="string" name="short_text249" />
+ <item type="string" name="short_text250" />
+ <item type="string" name="short_text251" />
+ <item type="string" name="short_text252" />
+ <item type="string" name="short_text253" />
+ <item type="string" name="short_text254" />
+ <item type="string" name="short_text255" />
+ </policy>
+ </overlayable>
+</resources>
diff --git a/apct-tests/perftests/core/res/values/strings.xml b/apct-tests/perftests/core/res/values/strings.xml
deleted file mode 100644
index 7ab325f79dc7..000000000000
--- a/apct-tests/perftests/core/res/values/strings.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?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:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_text">text text text text text text text text text text text text
- text text text text text text text text text text text text text text text text
- text text text text text text text text text text text text text text text text
- text text text text text text text text text text text text text text text text
- text text text text text text text text text text text text text text text text
- text text text text text text text text text text text text text text text text
- text text text text text text text typo text text text text text text text text
- text text text text text text text text text text text text text text text text
- text text text text text text text text text text text text text text text text
- text text text text text text text text text text text text text text text text
- text text text text text text text text text text text text text text text text
- text text text text text text text text text text text text </string>
- <string name="short_text">text text</string>
-</resources>
diff --git a/apct-tests/perftests/core/res/values/values.xml b/apct-tests/perftests/core/res/values/values.xml
new file mode 100644
index 000000000000..aad42ba04e11
--- /dev/null
+++ b/apct-tests/perftests/core/res/values/values.xml
@@ -0,0 +1,368 @@
+<?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:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="long_text">text text text text text text text text text text text text
+ text text text text text text text text text text text text text text text text
+ text text text text text text text text text text text text text text text text
+ text text text text text text text text text text text text text text text text
+ text text text text text text text text text text text text text text text text
+ text text text text text text text text text text text text text text text text
+ text text text text text text text typo text text text text text text text text
+ text text text text text text text text text text text text text text text text
+ text text text text text text text text text text text text text text text text
+ text text text text text text text text text text text text text text text text
+ text text text text text text text text text text text text text text text text
+ text text text text text text text text text text text text </string>
+
+ <plurals name="plurals_text">
+ <item quantity="one">1 text</item>
+ <item quantity="other"><xliff:g id="count" example="3">%d</xliff:g> texts</item>
+ </plurals>
+
+ <string-array name="strings">
+ <item>Run</item>
+ <item>the</item>
+ <item>performance</item>
+ <item>tests!</item>
+ <item>Run</item>
+ <item>the</item>
+ <item>performance</item>
+ <item>tests!</item>
+ <item>Run</item>
+ <item>the</item>
+ <item>performance</item>
+ <item>tests!</item>
+ <item>Run</item>
+ <item>the</item>
+ <item>performance</item>
+ <item>tests!</item>
+ <item>Run</item>
+ <item>the</item>
+ <item>performance</item>
+ <item>tests!</item>
+ <item>Run</item>
+ <item>the</item>
+ <item>performance</item>
+ <item>tests!</item>
+ <item>Run</item>
+ <item>the</item>
+ <item>performance</item>
+ <item>tests!</item>
+ <item>Run</item>
+ <item>the</item>
+ <item>performance</item>
+ <item>tests!</item>
+ </string-array>
+
+ <integer-array name="ints">
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ </integer-array>
+
+ <color name="white">#ffffff</color>
+
+ <integer name="forty_two">42</integer>
+
+ <string name="short_text">text text</string>
+ <string name="short_text000">B</string>
+ <string name="short_text001">B</string>
+ <string name="short_text002">B</string>
+ <string name="short_text003">B</string>
+ <string name="short_text004">B</string>
+ <string name="short_text005">B</string>
+ <string name="short_text006">B</string>
+ <string name="short_text007">B</string>
+ <string name="short_text008">B</string>
+ <string name="short_text009">B</string>
+ <string name="short_text010">B</string>
+ <string name="short_text011">B</string>
+ <string name="short_text012">B</string>
+ <string name="short_text013">B</string>
+ <string name="short_text014">B</string>
+ <string name="short_text015">B</string>
+ <string name="short_text016">B</string>
+ <string name="short_text017">B</string>
+ <string name="short_text018">B</string>
+ <string name="short_text019">B</string>
+ <string name="short_text020">B</string>
+ <string name="short_text021">B</string>
+ <string name="short_text022">B</string>
+ <string name="short_text023">B</string>
+ <string name="short_text024">B</string>
+ <string name="short_text025">B</string>
+ <string name="short_text026">B</string>
+ <string name="short_text027">B</string>
+ <string name="short_text028">B</string>
+ <string name="short_text029">B</string>
+ <string name="short_text030">B</string>
+ <string name="short_text031">B</string>
+ <string name="short_text032">B</string>
+ <string name="short_text033">B</string>
+ <string name="short_text034">B</string>
+ <string name="short_text035">B</string>
+ <string name="short_text036">B</string>
+ <string name="short_text037">B</string>
+ <string name="short_text038">B</string>
+ <string name="short_text039">B</string>
+ <string name="short_text040">B</string>
+ <string name="short_text041">B</string>
+ <string name="short_text042">B</string>
+ <string name="short_text043">B</string>
+ <string name="short_text044">B</string>
+ <string name="short_text045">B</string>
+ <string name="short_text046">B</string>
+ <string name="short_text047">B</string>
+ <string name="short_text048">B</string>
+ <string name="short_text049">B</string>
+ <string name="short_text050">B</string>
+ <string name="short_text051">B</string>
+ <string name="short_text052">B</string>
+ <string name="short_text053">B</string>
+ <string name="short_text054">B</string>
+ <string name="short_text055">B</string>
+ <string name="short_text056">B</string>
+ <string name="short_text057">B</string>
+ <string name="short_text058">B</string>
+ <string name="short_text059">B</string>
+ <string name="short_text060">B</string>
+ <string name="short_text061">B</string>
+ <string name="short_text062">B</string>
+ <string name="short_text063">B</string>
+ <string name="short_text064">B</string>
+ <string name="short_text065">B</string>
+ <string name="short_text066">B</string>
+ <string name="short_text067">B</string>
+ <string name="short_text068">B</string>
+ <string name="short_text069">B</string>
+ <string name="short_text070">B</string>
+ <string name="short_text071">B</string>
+ <string name="short_text072">B</string>
+ <string name="short_text073">B</string>
+ <string name="short_text074">B</string>
+ <string name="short_text075">B</string>
+ <string name="short_text076">B</string>
+ <string name="short_text077">B</string>
+ <string name="short_text078">B</string>
+ <string name="short_text079">B</string>
+ <string name="short_text080">B</string>
+ <string name="short_text081">B</string>
+ <string name="short_text082">B</string>
+ <string name="short_text083">B</string>
+ <string name="short_text084">B</string>
+ <string name="short_text085">B</string>
+ <string name="short_text086">B</string>
+ <string name="short_text087">B</string>
+ <string name="short_text088">B</string>
+ <string name="short_text089">B</string>
+ <string name="short_text090">B</string>
+ <string name="short_text091">B</string>
+ <string name="short_text092">B</string>
+ <string name="short_text093">B</string>
+ <string name="short_text094">B</string>
+ <string name="short_text095">B</string>
+ <string name="short_text096">B</string>
+ <string name="short_text097">B</string>
+ <string name="short_text098">B</string>
+ <string name="short_text099">B</string>
+ <string name="short_text100">B</string>
+ <string name="short_text101">B</string>
+ <string name="short_text102">B</string>
+ <string name="short_text103">B</string>
+ <string name="short_text104">B</string>
+ <string name="short_text105">B</string>
+ <string name="short_text106">B</string>
+ <string name="short_text107">B</string>
+ <string name="short_text108">B</string>
+ <string name="short_text109">B</string>
+ <string name="short_text110">B</string>
+ <string name="short_text111">B</string>
+ <string name="short_text112">B</string>
+ <string name="short_text113">B</string>
+ <string name="short_text114">B</string>
+ <string name="short_text115">B</string>
+ <string name="short_text116">B</string>
+ <string name="short_text117">B</string>
+ <string name="short_text118">B</string>
+ <string name="short_text119">B</string>
+ <string name="short_text120">B</string>
+ <string name="short_text121">B</string>
+ <string name="short_text122">B</string>
+ <string name="short_text123">B</string>
+ <string name="short_text124">B</string>
+ <string name="short_text125">B</string>
+ <string name="short_text126">B</string>
+ <string name="short_text127">B</string>
+ <string name="short_text128">B</string>
+ <string name="short_text129">B</string>
+ <string name="short_text130">B</string>
+ <string name="short_text131">B</string>
+ <string name="short_text132">B</string>
+ <string name="short_text133">B</string>
+ <string name="short_text134">B</string>
+ <string name="short_text135">B</string>
+ <string name="short_text136">B</string>
+ <string name="short_text137">B</string>
+ <string name="short_text138">B</string>
+ <string name="short_text139">B</string>
+ <string name="short_text140">B</string>
+ <string name="short_text141">B</string>
+ <string name="short_text142">B</string>
+ <string name="short_text143">B</string>
+ <string name="short_text144">B</string>
+ <string name="short_text145">B</string>
+ <string name="short_text146">B</string>
+ <string name="short_text147">B</string>
+ <string name="short_text148">B</string>
+ <string name="short_text149">B</string>
+ <string name="short_text150">B</string>
+ <string name="short_text151">B</string>
+ <string name="short_text152">B</string>
+ <string name="short_text153">B</string>
+ <string name="short_text154">B</string>
+ <string name="short_text155">B</string>
+ <string name="short_text156">B</string>
+ <string name="short_text157">B</string>
+ <string name="short_text158">B</string>
+ <string name="short_text159">B</string>
+ <string name="short_text160">B</string>
+ <string name="short_text161">B</string>
+ <string name="short_text162">B</string>
+ <string name="short_text163">B</string>
+ <string name="short_text164">B</string>
+ <string name="short_text165">B</string>
+ <string name="short_text166">B</string>
+ <string name="short_text167">B</string>
+ <string name="short_text168">B</string>
+ <string name="short_text169">B</string>
+ <string name="short_text170">B</string>
+ <string name="short_text171">B</string>
+ <string name="short_text172">B</string>
+ <string name="short_text173">B</string>
+ <string name="short_text174">B</string>
+ <string name="short_text175">B</string>
+ <string name="short_text176">B</string>
+ <string name="short_text177">B</string>
+ <string name="short_text178">B</string>
+ <string name="short_text179">B</string>
+ <string name="short_text180">B</string>
+ <string name="short_text181">B</string>
+ <string name="short_text182">B</string>
+ <string name="short_text183">B</string>
+ <string name="short_text184">B</string>
+ <string name="short_text185">B</string>
+ <string name="short_text186">B</string>
+ <string name="short_text187">B</string>
+ <string name="short_text188">B</string>
+ <string name="short_text189">B</string>
+ <string name="short_text190">B</string>
+ <string name="short_text191">B</string>
+ <string name="short_text192">B</string>
+ <string name="short_text193">B</string>
+ <string name="short_text194">B</string>
+ <string name="short_text195">B</string>
+ <string name="short_text196">B</string>
+ <string name="short_text197">B</string>
+ <string name="short_text198">B</string>
+ <string name="short_text199">B</string>
+ <string name="short_text200">B</string>
+ <string name="short_text201">B</string>
+ <string name="short_text202">B</string>
+ <string name="short_text203">B</string>
+ <string name="short_text204">B</string>
+ <string name="short_text205">B</string>
+ <string name="short_text206">B</string>
+ <string name="short_text207">B</string>
+ <string name="short_text208">B</string>
+ <string name="short_text209">B</string>
+ <string name="short_text210">B</string>
+ <string name="short_text211">B</string>
+ <string name="short_text212">B</string>
+ <string name="short_text213">B</string>
+ <string name="short_text214">B</string>
+ <string name="short_text215">B</string>
+ <string name="short_text216">B</string>
+ <string name="short_text217">B</string>
+ <string name="short_text218">B</string>
+ <string name="short_text219">B</string>
+ <string name="short_text220">B</string>
+ <string name="short_text221">B</string>
+ <string name="short_text222">B</string>
+ <string name="short_text223">B</string>
+ <string name="short_text224">B</string>
+ <string name="short_text225">B</string>
+ <string name="short_text226">B</string>
+ <string name="short_text227">B</string>
+ <string name="short_text228">B</string>
+ <string name="short_text229">B</string>
+ <string name="short_text230">B</string>
+ <string name="short_text231">B</string>
+ <string name="short_text232">B</string>
+ <string name="short_text233">B</string>
+ <string name="short_text234">B</string>
+ <string name="short_text235">B</string>
+ <string name="short_text236">B</string>
+ <string name="short_text237">B</string>
+ <string name="short_text238">B</string>
+ <string name="short_text239">B</string>
+ <string name="short_text240">B</string>
+ <string name="short_text241">B</string>
+ <string name="short_text242">B</string>
+ <string name="short_text243">B</string>
+ <string name="short_text244">B</string>
+ <string name="short_text245">B</string>
+ <string name="short_text246">B</string>
+ <string name="short_text247">B</string>
+ <string name="short_text248">B</string>
+ <string name="short_text249">B</string>
+ <string name="short_text250">B</string>
+ <string name="short_text251">B</string>
+ <string name="short_text252">B</string>
+ <string name="short_text253">B</string>
+ <string name="short_text254">B</string>
+ <string name="short_text255">B</string>
+</resources>
diff --git a/apct-tests/perftests/core/src/android/app/OverlayManagerPerfTest.java b/apct-tests/perftests/core/src/android/app/OverlayManagerPerfTest.java
new file mode 100644
index 000000000000..fcb13a8d51f1
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/app/OverlayManagerPerfTest.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.content.om.OverlayManager;
+import android.os.UserHandle;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.perftests.utils.TestPackageInstaller;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
+import com.android.perftests.core.R;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.concurrent.Executor;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Benchmarks for {@link android.content.om.OverlayManager}.
+ */
+@LargeTest
+public class OverlayManagerPerfTest {
+ private static final int OVERLAY_PKG_COUNT = 10;
+ private static Context sContext;
+ private static OverlayManager sOverlayManager;
+ private static Executor sExecutor;
+ private static ArrayList<TestPackageInstaller.InstalledPackage> sSmallOverlays =
+ new ArrayList<>();
+ private static ArrayList<TestPackageInstaller.InstalledPackage> sLargeOverlays =
+ new ArrayList<>();
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @BeforeClass
+ public static void classSetUp() throws Exception {
+ sContext = InstrumentationRegistry.getTargetContext();
+ sOverlayManager = new OverlayManager(sContext);
+ sExecutor = (command) -> new Thread(command).start();
+
+ // Install all of the test overlays.
+ TestPackageInstaller installer = new TestPackageInstaller(sContext);
+ for (int i = 0; i < OVERLAY_PKG_COUNT; i++) {
+ sSmallOverlays.add(installer.installPackage("Overlay" + i +".apk"));
+ sLargeOverlays.add(installer.installPackage("LargeOverlay" + i +".apk"));
+ }
+ }
+
+ @AfterClass
+ public static void classTearDown() throws Exception {
+ for (TestPackageInstaller.InstalledPackage overlay : sSmallOverlays) {
+ overlay.uninstall();
+ }
+
+ for (TestPackageInstaller.InstalledPackage overlay : sLargeOverlays) {
+ overlay.uninstall();
+ }
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ // Disable all test overlays after each test.
+ for (TestPackageInstaller.InstalledPackage overlay : sSmallOverlays) {
+ assertSetEnabled(sContext, overlay.getPackageName(), false);
+ }
+
+ for (TestPackageInstaller.InstalledPackage overlay : sLargeOverlays) {
+ assertSetEnabled(sContext, overlay.getPackageName(), false);
+ }
+ }
+
+ /**
+ * Enables the overlay and waits for the APK path change sto be propagated to the context
+ * AssetManager.
+ */
+ private void assertSetEnabled(Context context, String overlayPackage, boolean eanabled)
+ throws Exception {
+ sOverlayManager.setEnabled(overlayPackage, true, UserHandle.SYSTEM);
+
+ // Wait for the overlay changes to propagate
+ FutureTask<Boolean> task = new FutureTask<>(() -> {
+ while (true) {
+ for (String path : context.getAssets().getApkPaths()) {
+ if (eanabled == path.contains(overlayPackage)) {
+ return true;
+ }
+ }
+ }
+ });
+
+ sExecutor.execute(task);
+ assertTrue("Failed to load overlay " + overlayPackage,
+ task.get(20, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void setEnabledWarmCache() throws Exception {
+ String packageName = sSmallOverlays.get(0).getPackageName();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ assertSetEnabled(sContext, packageName, true);
+
+ // Disable the overlay for the next iteration of the test
+ state.pauseTiming();
+ assertSetEnabled(sContext, packageName, false);
+ state.resumeTiming();
+ }
+ }
+
+ @Test
+ public void setEnabledColdCacheSmallOverlay() throws Exception {
+ String packageName = sSmallOverlays.get(0).getPackageName();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ assertSetEnabled(sContext, packageName, true);
+
+ // Disable the overlay and remove the idmap for the next iteration of the test
+ state.pauseTiming();
+ assertSetEnabled(sContext, packageName, false);
+ sOverlayManager.invalidateCachesForOverlay(packageName, UserHandle.SYSTEM);
+ state.resumeTiming();
+ }
+ }
+
+ @Test
+ public void setEnabledColdCacheLargeOverlay() throws Exception {
+ String packageName = sLargeOverlays.get(0).getPackageName();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ assertSetEnabled(sContext, packageName, true);
+
+ // Disable the overlay and remove the idmap for the next iteration of the test
+ state.pauseTiming();
+ assertSetEnabled(sContext, packageName, false);
+ sOverlayManager.invalidateCachesForOverlay(packageName, UserHandle.SYSTEM);
+ state.resumeTiming();
+ }
+ }
+
+ @Test
+ public void setEnabledDisable() throws Exception {
+ String packageName = sSmallOverlays.get(0).getPackageName();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ assertSetEnabled(sContext, packageName, true);
+ state.resumeTiming();
+
+ assertSetEnabled(sContext, packageName, false);
+ }
+ }
+
+ @Test
+ public void getStringOneSmallOverlay() throws Exception {
+ String packageName = sSmallOverlays.get(0).getPackageName();
+ assertSetEnabled(sContext, packageName, true);
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ sContext.getString(R.string.short_text);
+ }
+
+ assertSetEnabled(sContext, packageName, false);
+ }
+
+ @Test
+ public void getStringOneLargeOverlay() throws Exception {
+ String packageName = sLargeOverlays.get(0).getPackageName();
+ assertSetEnabled(sContext, packageName, true);
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int resId = R.string.short_text000; resId < R.string.short_text255; resId++) {
+ sContext.getString(resId);
+ }
+ }
+
+ assertSetEnabled(sContext, packageName, false);
+ }
+
+ @Test
+ public void getStringTenOverlays() throws Exception {
+ // Enable all test overlays
+ for (TestPackageInstaller.InstalledPackage overlay : sSmallOverlays) {
+ assertSetEnabled(sContext, overlay.getPackageName(), true);
+ }
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ sContext.getString(R.string.short_text);
+ }
+ }
+
+ @Test
+ public void getStringLargeTenOverlays() throws Exception {
+ // Enable all test overlays
+ for (TestPackageInstaller.InstalledPackage overlay : sLargeOverlays) {
+ assertSetEnabled(sContext, overlay.getPackageName(), true);
+ }
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int resId = R.string.short_text000; resId < R.string.short_text255; resId++) {
+ sContext.getString(resId);
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java b/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java
index b3f83596e8a1..a320514dd97a 100644
--- a/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java
@@ -20,7 +20,7 @@ import android.content.Context;
import android.content.Intent;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
@@ -48,7 +48,7 @@ public class PendingIntentPerfTest {
@Before
public void setUp() {
mContext = InstrumentationRegistry.getTargetContext();
- mIntent = StubActivity.createLaunchIntent(mContext);
+ mIntent = PerfTestActivity.createLaunchIntent(mContext);
}
/**
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
new file mode 100644
index 000000000000..2955d2ca7d0e
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
@@ -0,0 +1,139 @@
+/*
+ * 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 android.app;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.view.Display;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Benchmarks for {@link android.app.ResourcesManager}.
+ */
+@LargeTest
+public class ResourcesManagerPerfTest {
+ private static Context sContext;
+ private static File sResourcesCompressed;
+ private static File sResourcesUncompressed;
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ sContext = InstrumentationRegistry.getTargetContext();
+ sResourcesCompressed = copyApkToTemp("LargeResourcesCompressed.apk",
+ "LargeResourcesCompressed.apk");
+ sResourcesUncompressed = copyApkToTemp("LargeResourcesUncompressed.apk",
+ "LargeResourcesUncompressed.apk");
+ }
+
+ @AfterClass
+ public static void tearDown() {
+ Assert.assertTrue(sResourcesCompressed.delete());
+ Assert.assertTrue(sResourcesUncompressed.delete());
+ }
+
+ private static File copyApkToTemp(String inputFileName, String fileName) throws Exception {
+ File file = File.createTempFile(fileName, null, sContext.getCacheDir());
+ try (OutputStream tempOutputStream = new FileOutputStream(file);
+ InputStream is = sContext.getResources().getAssets().openNonAsset(inputFileName)) {
+ byte[] buffer = new byte[4096];
+ int n;
+ while ((n = is.read(buffer)) >= 0) {
+ tempOutputStream.write(buffer, 0, n);
+ }
+ tempOutputStream.flush();
+ }
+ return file;
+ }
+
+ private void getResourcesForPath(String path) {
+ ResourcesManager.getInstance().getResources(null, path, null, null, null,
+ Display.DEFAULT_DISPLAY, null, sContext.getResources().getCompatibilityInfo(),
+ null);
+ }
+
+ @Test
+ public void getResourcesCached() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ getResourcesForPath(sResourcesCompressed.getPath());
+ while (state.keepRunning()) {
+ getResourcesForPath(sResourcesCompressed.getPath());
+ }
+ }
+
+ @Test
+ public void getResourcesCompressedUncached() {
+ ResourcesManager resourcesManager = ResourcesManager.getInstance();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ resourcesManager.invalidatePath(sResourcesCompressed.getPath());
+ state.resumeTiming();
+
+ getResourcesForPath(sResourcesCompressed.getPath());
+ }
+ }
+
+ @Test
+ public void getResourcesUncompressedUncached() {
+ ResourcesManager resourcesManager = ResourcesManager.getInstance();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ resourcesManager.invalidatePath(sResourcesUncompressed.getPath());
+ state.resumeTiming();
+
+ getResourcesForPath(sResourcesUncompressed.getPath());
+ }
+ }
+
+ @Test
+ public void applyConfigurationToResourcesLocked() {
+ ResourcesManager resourcesManager = ResourcesManager.getInstance();
+ Configuration c = new Configuration(resourcesManager.getConfiguration());
+ c.uiMode = Configuration.UI_MODE_TYPE_WATCH;
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ resourcesManager.applyConfigurationToResources(c, null);
+
+ // Alternate configurations to ensure the set configuration is different each iteration
+ if (c.uiMode == Configuration.UI_MODE_TYPE_WATCH) {
+ c.uiMode = Configuration.UI_MODE_TYPE_TELEVISION;
+ } else {
+ c.uiMode = Configuration.UI_MODE_TYPE_WATCH;
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesPerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesPerfTest.java
index c3e43ee07453..72162448a2e0 100644
--- a/apct-tests/perftests/core/src/android/app/ResourcesPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/ResourcesPerfTest.java
@@ -18,15 +18,18 @@ package android.app;
import static org.junit.Assert.fail;
-import android.content.res.AssetManager;
+import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
+import android.util.TypedValue;
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
-import org.junit.After;
+import com.android.perftests.core.R;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -43,36 +46,123 @@ public class ResourcesPerfTest {
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
- private AssetManager mAsset;
private Resources mRes;
- private int mTextId;
- private int mColorId;
- private int mIntegerId;
- private int mLayoutId;
-
@Before
public void setUp() {
- mAsset = new AssetManager();
- mAsset.addAssetPath("/system/framework/framework-res.apk");
- mRes = new Resources(mAsset, null, null);
+ Context context = InstrumentationRegistry.getTargetContext();
+ mRes = context.getResources();
+ }
+
+ @Test
+ public void getValue() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ TypedValue value = new TypedValue();
+ while (state.keepRunning()) {
+ mRes.getValue(R.integer.forty_two, value, false /* resolve_refs */);
+ }
+ }
- mTextId = mRes.getIdentifier("cancel", "string", "android");
- mColorId = mRes.getIdentifier("transparent", "color", "android");
- mIntegerId = mRes.getIdentifier("config_shortAnimTime", "integer", "android");
- mLayoutId = mRes.getIdentifier("two_line_list_item", "layout", "android");
+ @Test
+ public void getFrameworkValue() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ TypedValue value = new TypedValue();
+ while (state.keepRunning()) {
+ mRes.getValue(com.android.internal.R.integer.autofill_max_visible_datasets, value,
+ false /* resolve_refs */);
+ }
+ }
+
+ @Test
+ public void getValueString() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ TypedValue value = new TypedValue();
+ while (state.keepRunning()) {
+ mRes.getValue(R.string.long_text, value, false /* resolve_refs */);
+ }
+ }
+
+ @Test
+ public void getFrameworkStringValue() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ TypedValue value = new TypedValue();
+ while (state.keepRunning()) {
+ mRes.getValue(com.android.internal.R.string.cancel, value, false /* resolve_refs */);
+ }
}
- @After
- public void tearDown() {
- mAsset.close();
+ @Test
+ public void getValueManyConfigurations() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ TypedValue value = new TypedValue();
+ while (state.keepRunning()) {
+ mRes.getValue(com.android.internal.R.string.mmcc_illegal_me, value,
+ false /* resolve_refs */);
+ }
}
@Test
public void getText() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
- mRes.getText(mTextId);
+ mRes.getText(R.string.long_text);
+ }
+ }
+
+
+ @Test
+ public void getFont() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mRes.getFont(R.font.samplefont);
+ }
+ }
+
+ @Test
+ public void getString() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mRes.getString(R.string.long_text);
+ }
+ }
+
+ @Test
+ public void getQuantityString() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mRes.getQuantityString(R.plurals.plurals_text, 5);
+ }
+ }
+
+ @Test
+ public void getQuantityText() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mRes.getQuantityText(R.plurals.plurals_text, 5);
+ }
+ }
+
+ @Test
+ public void getTextArray() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mRes.getTextArray(R.array.strings);
+ }
+ }
+
+ @Test
+ public void getStringArray() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mRes.getStringArray(R.array.strings);
+ }
+ }
+
+ @Test
+ public void getIntegerArray() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mRes.getIntArray(R.array.ints);
}
}
@@ -80,15 +170,23 @@ public class ResourcesPerfTest {
public void getColor() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
- mRes.getColor(mColorId, null);
+ mRes.getColor(R.color.white, null);
}
}
@Test
- public void getInteger() {
+ public void getColorStateList() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
- mRes.getInteger(mIntegerId);
+ mRes.getColorStateList(R.color.color_state_list, null);
+ }
+ }
+
+ @Test
+ public void getVectorDrawable() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mRes.getDrawable(R.drawable.vector_drawable01, null);
}
}
@@ -96,13 +194,32 @@ public class ResourcesPerfTest {
public void getLayoutAndTravese() {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
- try (XmlResourceParser parser = mRes.getLayout(mLayoutId)) {
+ try (XmlResourceParser parser = mRes.getLayout(R.layout.test_relative_layout)) {
+ while (parser.next() != XmlPullParser.END_DOCUMENT) {
+ // Walk the entire tree
+ }
+ } catch (IOException | XmlPullParserException exception) {
+ fail("Parsing of the layout failed. Something is really broken");
+ }
+ }
+ }
+
+ @Test
+ public void getLayoutAndTraverseInvalidateCaches() {
+ mRes.flushLayoutCache();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ try (XmlResourceParser parser = mRes.getLayout(R.layout.test_relative_layout)) {
while (parser.next() != XmlPullParser.END_DOCUMENT) {
// Walk the entire tree
}
} catch (IOException | XmlPullParserException exception) {
fail("Parsing of the layout failed. Something is really broken");
}
+
+ state.pauseTiming();
+ mRes.flushLayoutCache();
+ state.resumeTiming();
}
}
-}
+} \ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
index 1b07572fd3f8..6123e69b584e 100644
--- a/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
@@ -16,13 +16,19 @@
package android.app;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
import android.content.res.Resources;
+import android.os.UserHandle;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
+import android.view.Display;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -35,13 +41,69 @@ public class ResourcesThemePerfTest {
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ private Context mContext;
+ private int mThemeResId;
private Resources.Theme mTheme;
@Before
- public void setUp() {
- Context context = InstrumentationRegistry.getTargetContext();
- mTheme = context.getTheme();
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mThemeResId = com.android.perftests.core.R.style.Base_V7_Theme_AppCompat;
+ mTheme = mContext.getResources().newTheme();
+ mTheme.applyStyle(mThemeResId, true /* force */);
+ }
+
+ @Test
+ public void applyStyle() {
+ Resources.Theme destTheme = mContext.getResources().newTheme();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ destTheme.applyStyle(mThemeResId, true /* force */);
+ }
+ }
+ @Test
+ public void rebase() {
+ Resources.Theme destTheme = mContext.getResources().newTheme();
+ destTheme.applyStyle(mThemeResId, true /* force */);
+ destTheme.applyStyle(android.R.style.Theme_Material, true /* force */);
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ destTheme.rebase();
+ }
+ }
+
+ @Test
+ public void setToSameAssetManager() {
+ Resources.Theme destTheme = mContext.getResources().newTheme();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ destTheme.setTo(mTheme);
+ }
+ }
+
+ @Test
+ public void setToDifferentAssetManager() throws Exception {
+ // Create a new Resources object with the same asset paths but a different AssetManager
+ PackageManager packageManager = mContext.getApplicationContext().getPackageManager();
+ ApplicationInfo ai = packageManager.getApplicationInfo(mContext.getPackageName(),
+ UserHandle.myUserId());
+
+ ResourcesManager resourcesManager = ResourcesManager.getInstance();
+ Configuration c = resourcesManager.getConfiguration();
+ c.orientation = (c.orientation == Configuration.ORIENTATION_PORTRAIT)
+ ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+
+ Resources destResources = resourcesManager.getResources(null, ai.sourceDir,
+ ai.splitSourceDirs, ai.resourceDirs, ai.sharedLibraryFiles, Display.DEFAULT_DISPLAY,
+ c, mContext.getResources().getCompatibilityInfo(), null);
+ Assert.assertNotEquals(destResources.getAssets(), mContext.getAssets());
+
+ Resources.Theme destTheme = destResources.newTheme();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ destTheme.setTo(mTheme);
+ }
}
@Test
@@ -51,5 +113,4 @@ public class ResourcesThemePerfTest {
mTheme.obtainStyledAttributes(android.R.style.Theme_Material, android.R.styleable.View);
}
}
-
-}
+} \ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/PaintHasGlyphPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/PaintHasGlyphPerfTest.java
index 3a8002003299..b9c7af46c67c 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/PaintHasGlyphPerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/PaintHasGlyphPerfTest.java
@@ -19,7 +19,7 @@ package android.graphics.perftests;
import android.graphics.Paint;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
@@ -58,7 +58,8 @@ public class PaintHasGlyphPerfTest {
}
@Rule
- public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
+ public ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule<>(PerfTestActivity.class);
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java
index 3b2b8a94f610..d14e93e553f6 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java
@@ -26,7 +26,7 @@ import android.graphics.drawable.VectorDrawable;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.BitmapUtils;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import android.test.suitebuilder.annotation.LargeTest;
import androidx.test.rule.ActivityTestRule;
@@ -48,8 +48,8 @@ public class VectorDrawablePerfTest {
private int[] mTestHeights = {512, 1024};
@Rule
- public ActivityTestRule<StubActivity> mActivityRule =
- new ActivityTestRule(StubActivity.class);
+ public ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule<>(PerfTestActivity.class);
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java b/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java
index 3aa6749502bc..236f548cf6dd 100644
--- a/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java
@@ -38,7 +38,8 @@ public class PackageManagerPerfTest {
private static final String PERMISSION_NAME_DOESNT_EXIST =
"com.android.perftests.core.TestBadPermission";
private static final ComponentName TEST_ACTIVITY =
- new ComponentName("com.android.perftests.core", "android.perftests.utils.StubActivity");
+ new ComponentName("com.android.perftests.core",
+ "android.perftests.utils.PerfTestActivity");
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java
index 5be99d9d779e..6b295e55c9d4 100644
--- a/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java
@@ -23,7 +23,7 @@ import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import android.text.style.ReplacementSpan;
import android.util.ArraySet;
@@ -75,7 +75,8 @@ public class DynamicLayoutPerfTest {
}
@Rule
- public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
+ public ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule<>(PerfTestActivity.class);
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java b/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
index b34001dcf6b5..b0edb117a00b 100644
--- a/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
+++ b/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
@@ -23,7 +23,7 @@ import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import android.view.View.MeasureSpec;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -46,7 +46,8 @@ import java.util.List;
public class ViewShowHidePerfTest {
@Rule
- public ActivityTestRule mActivityRule = new ActivityTestRule(StubActivity.class);
+ public ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule<>(PerfTestActivity.class);
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java b/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java
index b3ea62aa7da0..270b4e5b49cc 100644
--- a/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java
@@ -18,7 +18,7 @@ package android.widget;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import android.text.Selection;
import android.view.KeyEvent;
import android.view.View.MeasureSpec;
@@ -80,7 +80,8 @@ public class EditTextBackspacePerfTest {
}
@Rule
- public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
+ public ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule<>(PerfTestActivity.class);
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java b/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java
index aa47d5bdd998..8028f11be63b 100644
--- a/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java
@@ -18,7 +18,7 @@ package android.widget;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import android.text.Selection;
import android.view.KeyEvent;
import android.view.View.MeasureSpec;
@@ -74,7 +74,8 @@ public class EditTextCursorMovementPerfTest {
}
@Rule
- public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
+ public ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule<>(PerfTestActivity.class);
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java b/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java
index e50016c45008..f4ad5ddd3ed2 100644
--- a/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java
@@ -19,7 +19,7 @@ package android.widget;
import android.app.Activity;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import android.view.KeyEvent;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
@@ -59,7 +59,8 @@ public class EditTextLongTextPerfTest {
}
@Rule
- public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
+ public ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule<>(PerfTestActivity.class);
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java b/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java
index 644095b36206..223a3165b1d5 100644
--- a/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java
@@ -28,7 +28,7 @@ import android.app.Activity;
import android.os.Looper;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import android.view.View;
import android.view.ViewGroup;
@@ -72,8 +72,8 @@ public class LayoutPerfTest {
}
@Rule
- public ActivityTestRule<StubActivity> mActivityRule =
- new ActivityTestRule(StubActivity.class);
+ public ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule(PerfTestActivity.class);
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java
index bed173bd269a..694e1f477f89 100644
--- a/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java
@@ -22,7 +22,7 @@ import android.app.Activity;
import android.os.Looper;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
@@ -64,8 +64,8 @@ public class TextViewAutoSizeLayoutPerfTest {
}
@Rule
- public ActivityTestRule<StubActivity> mActivityRule =
- new ActivityTestRule(StubActivity.class);
+ public ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule(PerfTestActivity.class);
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java
index 00bd8db7f5a3..a5466678167e 100644
--- a/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java
@@ -18,7 +18,7 @@ package android.widget;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
@@ -56,7 +56,8 @@ public class TextViewSetTextLocalePerfTest {
}
@Rule
- public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
+ public ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule<>(PerfTestActivity.class);
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/wm/InternalWindowOperationPerfTest.java b/apct-tests/perftests/core/src/android/wm/InternalWindowOperationPerfTest.java
new file mode 100644
index 000000000000..c096cd22bdba
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/wm/InternalWindowOperationPerfTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.wm;
+
+import static android.perftests.utils.ManualBenchmarkState.StatsReport;
+
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.perftests.utils.ManualBenchmarkState;
+import android.perftests.utils.ManualBenchmarkState.ManualBenchmarkTest;
+import android.perftests.utils.PerfManualStatusReporter;
+import android.perftests.utils.TraceMarkParser;
+import android.perftests.utils.TraceMarkParser.TraceMarkSlice;
+import android.util.Log;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.lifecycle.Stage;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.concurrent.TimeUnit;
+
+/** Measure the performance of internal methods in window manager service by trace tag. */
+@LargeTest
+public class InternalWindowOperationPerfTest extends WindowManagerPerfTestBase {
+ private static final String TAG = InternalWindowOperationPerfTest.class.getSimpleName();
+
+ @Rule
+ public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();
+
+ @Rule
+ public final PerfTestActivityRule mActivityRule = new PerfTestActivityRule();
+
+ private final TraceMarkParser mTraceMarkParser = new TraceMarkParser(
+ "applyPostLayoutPolicy",
+ "applySurfaceChanges",
+ "AppTransitionReady",
+ "closeSurfaceTransactiom",
+ "openSurfaceTransaction",
+ "performLayout",
+ "performSurfacePlacement",
+ "prepareSurfaces",
+ "updateInputWindows",
+ "WSA#startAnimation",
+ "activityIdle",
+ "activityPaused",
+ "activityStopped",
+ "activityDestroyed",
+ "finishActivity",
+ "startActivityInner");
+
+ @Test
+ @ManualBenchmarkTest(
+ targetTestDurationNs = 20 * TIME_1_S_IN_NS,
+ statsReport = @StatsReport(
+ flags = StatsReport.FLAG_ITERATION | StatsReport.FLAG_MEAN
+ | StatsReport.FLAG_MAX | StatsReport.FLAG_COEFFICIENT_VAR))
+ public void testLaunchAndFinishActivity() throws Throwable {
+ final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ long measuredTimeNs = 0;
+ boolean isTraceStarted = false;
+
+ while (state.keepRunning(measuredTimeNs)) {
+ if (!isTraceStarted && !state.isWarmingUp()) {
+ startAsyncAtrace();
+ isTraceStarted = true;
+ }
+ final long startTime = SystemClock.elapsedRealtimeNanos();
+ mActivityRule.launchActivity();
+ mActivityRule.finishActivity();
+ mActivityRule.waitForIdleSync(Stage.DESTROYED);
+ measuredTimeNs = SystemClock.elapsedRealtimeNanos() - startTime;
+ }
+
+ stopAsyncAtrace();
+
+ mTraceMarkParser.forAllSlices((key, slices) -> {
+ for (TraceMarkSlice slice : slices) {
+ state.addExtraResult(key, (long) (slice.getDurarionInSeconds() * NANOS_PER_S));
+ }
+ });
+
+ Log.i(TAG, String.valueOf(mTraceMarkParser));
+ }
+
+ private void startAsyncAtrace() throws IOException {
+ sUiAutomation.executeShellCommand("atrace -b 32768 --async_start wm");
+ // Avoid atrace isn't ready immediately.
+ SystemClock.sleep(TimeUnit.NANOSECONDS.toMillis(TIME_1_S_IN_NS));
+ }
+
+ private void stopAsyncAtrace() throws IOException {
+ final ParcelFileDescriptor pfd = sUiAutomation.executeShellCommand("atrace --async_stop");
+ final InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ mTraceMarkParser.visit(line);
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java b/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java
index 9cfc3d272145..73b4a1914ad1 100644
--- a/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java
@@ -16,16 +16,13 @@
package android.wm;
-import static android.perftests.utils.ManualBenchmarkState.STATS_REPORT_COEFFICIENT_VAR;
-import static android.perftests.utils.ManualBenchmarkState.STATS_REPORT_ITERATION;
-import static android.perftests.utils.ManualBenchmarkState.STATS_REPORT_MEAN;
+import static android.perftests.utils.ManualBenchmarkState.StatsReport;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.hamcrest.core.AnyOf.anyOf;
import static org.hamcrest.core.Is.is;
-import android.app.Activity;
import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
@@ -39,23 +36,16 @@ import android.os.SystemClock;
import android.perftests.utils.ManualBenchmarkState;
import android.perftests.utils.ManualBenchmarkState.ManualBenchmarkTest;
import android.perftests.utils.PerfManualStatusReporter;
-import android.perftests.utils.StubActivity;
import android.util.Pair;
import android.view.IRecentsAnimationController;
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationTarget;
-import android.view.WindowManager;
import androidx.test.filters.LargeTest;
-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.After;
import org.junit.AfterClass;
import org.junit.Assume;
-import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
@@ -77,11 +67,10 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase {
public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();
@Rule
- public final ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule<>(
- StubActivity.class, false /* initialTouchMode */, false /* launchActivity */);
+ public final PerfTestActivityRule mActivityRule =
+ new PerfTestActivityRule(true /* launchActivity */);
private long mMeasuredTimeNs;
- private LifecycleListener mLifecycleListener;
@Parameterized.Parameter(0)
public int intervalBetweenOperations;
@@ -127,24 +116,6 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase {
sUiAutomation.dropShellPermissionIdentity();
}
- @Before
- @Override
- public void setUp() {
- super.setUp();
- final Activity testActivity = mActivityRule.launchActivity(null /* intent */);
- try {
- mActivityRule.runOnUiThread(() -> testActivity.getWindow()
- .addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON));
- } catch (Throwable ignored) { }
- mLifecycleListener = new LifecycleListener(testActivity);
- ActivityLifecycleMonitorRegistry.getInstance().addLifecycleCallback(mLifecycleListener);
- }
-
- @After
- public void tearDown() {
- ActivityLifecycleMonitorRegistry.getInstance().removeLifecycleCallback(mLifecycleListener);
- }
-
/** Simulate the timing of touch. */
private void makeInterval() {
SystemClock.sleep(intervalBetweenOperations);
@@ -167,8 +138,8 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase {
@ManualBenchmarkTest(
warmupDurationNs = TIME_1_S_IN_NS,
targetTestDurationNs = TIME_5_S_IN_NS,
- statsReportFlags =
- STATS_REPORT_ITERATION | STATS_REPORT_MEAN | STATS_REPORT_COEFFICIENT_VAR)
+ statsReport = @StatsReport(flags = StatsReport.FLAG_ITERATION | StatsReport.FLAG_MEAN
+ | StatsReport.FLAG_COEFFICIENT_VAR))
public void testRecentsAnimation() throws Throwable {
final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
final IActivityTaskManager atm = ActivityTaskManager.getService();
@@ -201,7 +172,7 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase {
state.addExtraResult(finishCase.first, elapsedTimeNsOfFinish);
if (moveRecentsToTop) {
- mLifecycleListener.waitForIdleSync(Stage.STOPPED);
+ mActivityRule.waitForIdleSync(Stage.STOPPED);
startTime = SystemClock.elapsedRealtimeNanos();
atm.startActivityFromRecents(testActivityTaskId, null /* options */);
@@ -209,7 +180,7 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase {
mMeasuredTimeNs += elapsedTimeNs;
state.addExtraResult("startFromRecents", elapsedTimeNs);
- mLifecycleListener.waitForIdleSync(Stage.RESUMED);
+ mActivityRule.waitForIdleSync(Stage.RESUMED);
}
makeInterval();
@@ -223,55 +194,18 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase {
}
};
+ recentsSemaphore.tryAcquire();
while (state.keepRunning(mMeasuredTimeNs)) {
- Assume.assumeTrue(recentsSemaphore.tryAcquire(TIME_5_S_IN_NS, TimeUnit.NANOSECONDS));
+ mMeasuredTimeNs = 0;
final long startTime = SystemClock.elapsedRealtimeNanos();
atm.startRecentsActivity(sRecentsIntent, null /* unused */, anim);
final long elapsedTimeNsOfStart = SystemClock.elapsedRealtimeNanos() - startTime;
mMeasuredTimeNs += elapsedTimeNsOfStart;
state.addExtraResult("start", elapsedTimeNsOfStart);
- }
-
- // Ensure the last round of animation callback is done.
- recentsSemaphore.tryAcquire(TIME_5_S_IN_NS, TimeUnit.NANOSECONDS);
- recentsSemaphore.release();
- }
- private static class LifecycleListener implements ActivityLifecycleCallback {
- private final Activity mTargetActivity;
- private Stage mWaitingStage;
- private Stage mReceivedStage;
-
- LifecycleListener(Activity activity) {
- mTargetActivity = activity;
- }
-
- void waitForIdleSync(Stage state) {
- synchronized (this) {
- if (state != mReceivedStage) {
- mWaitingStage = state;
- try {
- wait(TimeUnit.NANOSECONDS.toMillis(TIME_5_S_IN_NS));
- } catch (InterruptedException impossible) { }
- }
- mWaitingStage = mReceivedStage = null;
- }
- getInstrumentation().waitForIdleSync();
- }
-
- @Override
- public void onActivityLifecycleChanged(Activity activity, Stage stage) {
- if (mTargetActivity != activity) {
- return;
- }
-
- synchronized (this) {
- mReceivedStage = stage;
- if (mWaitingStage == mReceivedStage) {
- notifyAll();
- }
- }
+ // Ensure the animation callback is done.
+ Assume.assumeTrue(recentsSemaphore.tryAcquire(TIME_5_S_IN_NS, TimeUnit.NANOSECONDS));
}
}
}
diff --git a/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java
index f0c474bfc5bb..f43bdf8348ea 100644
--- a/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java
@@ -24,7 +24,7 @@ import android.graphics.Rect;
import android.os.RemoteException;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import android.util.MergedConfiguration;
import android.view.DisplayCutout;
import android.view.IWindow;
@@ -57,8 +57,8 @@ public class RelayoutPerfTest extends WindowManagerPerfTestBase {
public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@Rule
- public final ActivityTestRule<StubActivity> mActivityRule =
- new ActivityTestRule<>(StubActivity.class);
+ public final ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule<>(PerfTestActivity.class);
/** This is only a placement to match the input parameters from {@link #getParameters}. */
@Parameterized.Parameter(0)
diff --git a/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java b/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java
index 4864da4b0195..4d278c3c2d9a 100644
--- a/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java
+++ b/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java
@@ -18,9 +18,21 @@ package android.wm;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import android.app.Activity;
import android.app.UiAutomation;
+import android.content.Intent;
+import android.perftests.utils.PerfTestActivity;
-import org.junit.Before;
+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.BeforeClass;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.concurrent.TimeUnit;
public class WindowManagerPerfTestBase {
static final UiAutomation sUiAutomation = getInstrumentation().getUiAutomation();
@@ -28,10 +40,102 @@ public class WindowManagerPerfTestBase {
static final long TIME_1_S_IN_NS = 1 * NANOS_PER_S;
static final long TIME_5_S_IN_NS = 5 * NANOS_PER_S;
- @Before
- public void setUp() {
+ @BeforeClass
+ public static void setUpOnce() {
// In order to be closer to the real use case.
sUiAutomation.executeShellCommand("input keyevent KEYCODE_WAKEUP");
sUiAutomation.executeShellCommand("wm dismiss-keyguard");
+ getInstrumentation().getContext().startActivity(new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ }
+
+ /**
+ * Provides an activity that keeps screen on and is able to wait for a stable lifecycle stage.
+ */
+ static class PerfTestActivityRule extends ActivityTestRule<PerfTestActivity> {
+ private final Intent mStartIntent =
+ new Intent().putExtra(PerfTestActivity.INTENT_EXTRA_KEEP_SCREEN_ON, true);
+ private final LifecycleListener mLifecycleListener = new LifecycleListener();
+
+ PerfTestActivityRule() {
+ this(false /* launchActivity */);
+ }
+
+ PerfTestActivityRule(boolean launchActivity) {
+ super(PerfTestActivity.class, false /* initialTouchMode */, launchActivity);
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ final Statement wrappedStatement = new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ ActivityLifecycleMonitorRegistry.getInstance()
+ .addLifecycleCallback(mLifecycleListener);
+ base.evaluate();
+ ActivityLifecycleMonitorRegistry.getInstance()
+ .removeLifecycleCallback(mLifecycleListener);
+ }
+ };
+ return super.apply(wrappedStatement, description);
+ }
+
+ @Override
+ protected Intent getActivityIntent() {
+ return mStartIntent;
+ }
+
+ @Override
+ public PerfTestActivity launchActivity(Intent intent) {
+ final PerfTestActivity activity = super.launchActivity(intent);
+ mLifecycleListener.setTargetActivity(activity);
+ return activity;
+ }
+
+ PerfTestActivity launchActivity() {
+ return launchActivity(mStartIntent);
+ }
+
+ void waitForIdleSync(Stage state) {
+ mLifecycleListener.waitForIdleSync(state);
+ }
+ }
+
+ static class LifecycleListener implements ActivityLifecycleCallback {
+ private Activity mTargetActivity;
+ private Stage mWaitingStage;
+ private Stage mReceivedStage;
+
+ void setTargetActivity(Activity activity) {
+ mTargetActivity = activity;
+ mReceivedStage = mWaitingStage = null;
+ }
+
+ void waitForIdleSync(Stage stage) {
+ synchronized (this) {
+ if (stage != mReceivedStage) {
+ mWaitingStage = stage;
+ try {
+ wait(TimeUnit.NANOSECONDS.toMillis(TIME_5_S_IN_NS));
+ } catch (InterruptedException impossible) { }
+ }
+ mWaitingStage = mReceivedStage = null;
+ }
+ getInstrumentation().waitForIdleSync();
+ }
+
+ @Override
+ public void onActivityLifecycleChanged(Activity activity, Stage stage) {
+ if (mTargetActivity != activity) {
+ return;
+ }
+
+ synchronized (this) {
+ mReceivedStage = stage;
+ if (mWaitingStage == mReceivedStage) {
+ notifyAll();
+ }
+ }
+ }
}
}
diff --git a/apct-tests/perftests/multiuser/AndroidManifest.xml b/apct-tests/perftests/multiuser/AndroidManifest.xml
index b2a9524d29c4..893c8ca9328b 100644
--- a/apct-tests/perftests/multiuser/AndroidManifest.xml
+++ b/apct-tests/perftests/multiuser/AndroidManifest.xml
@@ -22,6 +22,7 @@
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 32107b4e789e..e74e4a958eb9 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -38,8 +38,10 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.IProgressListener;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.perftests.utils.ShellHelper;
import android.util.Log;
import android.view.WindowManagerGlobal;
@@ -85,6 +87,14 @@ public class UserLifecycleTests {
private static final String DUMMY_PACKAGE_NAME = "perftests.multiuser.apps.dummyapp";
+ // Copy of UserSystemPackageInstaller whitelist mode constants.
+ private static final String PACKAGE_WHITELIST_MODE_PROP =
+ "persist.debug.user.package_whitelist_mode";
+ private static final int USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE = 0;
+ private static final int USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE = 0b001;
+ private static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST = 0b100;
+ private static final int USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT = -1;
+
private UserManager mUm;
private ActivityManager mAm;
private IActivityManager mIam;
@@ -442,6 +452,55 @@ public class UserLifecycleTests {
}
}
+ // TODO: This is just a POC. Do this properly and add more.
+ /** Tests starting (unlocking) a newly-created profile using the user-type-pkg-whitelist. */
+ @Test
+ public void managedProfileUnlock_usingWhitelist() throws Exception {
+ assumeTrue(mHasManagedUserFeature);
+ final int origMode = getUserTypePackageWhitelistMode();
+ setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE
+ | USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST);
+
+ try {
+ while (mRunner.keepRunning()) {
+ mRunner.pauseTiming();
+ final int userId = createManagedProfile();
+ mRunner.resumeTiming();
+
+ startUserInBackground(userId);
+
+ mRunner.pauseTiming();
+ removeUser(userId);
+ mRunner.resumeTiming();
+ }
+ } finally {
+ setUserTypePackageWhitelistMode(origMode);
+ }
+ }
+ /** Tests starting (unlocking) a newly-created profile NOT using the user-type-pkg-whitelist. */
+ @Test
+ public void managedProfileUnlock_notUsingWhitelist() throws Exception {
+ assumeTrue(mHasManagedUserFeature);
+ final int origMode = getUserTypePackageWhitelistMode();
+ setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE);
+
+ try {
+ while (mRunner.keepRunning()) {
+ mRunner.pauseTiming();
+ final int userId = createManagedProfile();
+ mRunner.resumeTiming();
+
+ startUserInBackground(userId);
+
+ mRunner.pauseTiming();
+ removeUser(userId);
+ mRunner.resumeTiming();
+ }
+ } finally {
+ setUserTypePackageWhitelistMode(origMode);
+ }
+ }
+
/** Creates a new user, returning its userId. */
private int createUserNoFlags() {
return createUserWithFlags(/* flags= */ 0);
@@ -458,6 +517,10 @@ public class UserLifecycleTests {
private int createManagedProfile() {
final UserInfo userInfo = mUm.createProfileForUser("TestProfile",
UserInfo.FLAG_MANAGED_PROFILE, mAm.getCurrentUser());
+ if (userInfo == null) {
+ throw new IllegalStateException("Creating managed profile failed. Most likely there is "
+ + "already a pre-existing profile on the device.");
+ }
mUsersToRemove.add(userInfo.id);
return userInfo.id;
}
@@ -627,6 +690,20 @@ public class UserLifecycleTests {
}
}
+ /** Gets the PACKAGE_WHITELIST_MODE_PROP System Property. */
+ private int getUserTypePackageWhitelistMode() {
+ return SystemProperties.getInt(PACKAGE_WHITELIST_MODE_PROP,
+ USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT);
+ }
+
+ /** Sets the PACKAGE_WHITELIST_MODE_PROP System Property to the given value. */
+ private void setUserTypePackageWhitelistMode(int mode) {
+ String result = ShellHelper.runShellCommand(
+ String.format("setprop %s %d", PACKAGE_WHITELIST_MODE_PROP, mode));
+ attestFalse("Failed to set sysprop " + PACKAGE_WHITELIST_MODE_PROP + ": " + result,
+ result != null && result.contains("Failed"));
+ }
+
private void removeUser(int userId) {
try {
mUm.removeUser(userId);
diff --git a/apct-tests/perftests/textclassifier/run.sh b/apct-tests/perftests/textclassifier/run.sh
index 8660d26388ac..d36d190a573a 100755
--- a/apct-tests/perftests/textclassifier/run.sh
+++ b/apct-tests/perftests/textclassifier/run.sh
@@ -1,5 +1,5 @@
set -e
-make TextClassifierPerfTests perf-setup.sh
+build/soong/soong_ui.bash --make-mode TextClassifierPerfTests perf-setup.sh
adb install ${OUT}/testcases/TextClassifierPerfTests/arm64/TextClassifierPerfTests.apk
adb shell cmd package compile -m speed -f com.android.perftests.textclassifier
adb push ${OUT}/obj/EXECUTABLES/perf-setup.sh_intermediates/perf-setup.sh /data/local/tmp/
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java b/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java
index ffe39e8679e1..a83254b463f4 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java
@@ -59,27 +59,37 @@ import java.util.concurrent.TimeUnit;
public final class ManualBenchmarkState {
private static final String TAG = ManualBenchmarkState.class.getSimpleName();
- @IntDef(prefix = {"STATS_REPORT"}, value = {
- STATS_REPORT_MEDIAN,
- STATS_REPORT_MEAN,
- STATS_REPORT_MIN,
- STATS_REPORT_MAX,
- STATS_REPORT_PERCENTILE90,
- STATS_REPORT_PERCENTILE95,
- STATS_REPORT_STDDEV,
- STATS_REPORT_ITERATION,
- })
- public @interface StatsReport {}
+ @Target(ElementType.ANNOTATION_TYPE)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface StatsReport {
+ int FLAG_MEDIAN = 0x00000001;
+ int FLAG_MEAN = 0x00000002;
+ int FLAG_MIN = 0x00000004;
+ int FLAG_MAX = 0x00000008;
+ int FLAG_STDDEV = 0x00000010;
+ int FLAG_COEFFICIENT_VAR = 0x00000020;
+ int FLAG_ITERATION = 0x00000040;
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @IntDef(value = {
+ FLAG_MEDIAN,
+ FLAG_MEAN,
+ FLAG_MIN,
+ FLAG_MAX,
+ FLAG_STDDEV,
+ FLAG_COEFFICIENT_VAR,
+ FLAG_ITERATION,
+ })
+ @interface Flag {}
- public static final int STATS_REPORT_MEDIAN = 0x00000001;
- public static final int STATS_REPORT_MEAN = 0x00000002;
- public static final int STATS_REPORT_MIN = 0x00000004;
- public static final int STATS_REPORT_MAX = 0x00000008;
- public static final int STATS_REPORT_PERCENTILE90 = 0x00000010;
- public static final int STATS_REPORT_PERCENTILE95 = 0x00000020;
- public static final int STATS_REPORT_STDDEV = 0x00000040;
- public static final int STATS_REPORT_COEFFICIENT_VAR = 0x00000080;
- public static final int STATS_REPORT_ITERATION = 0x00000100;
+ /** Defines which type of statistics should output. */
+ @Flag int flags() default -1;
+ /** An array with value 0~100 to provide the percentiles. */
+ int[] percentiles() default {};
+ }
+
+ /** It means the entire {@link StatsReport} is not given. */
+ private static final int DEFAULT_STATS_REPORT = -2;
// TODO: Tune these values.
// warm-up for duration
@@ -116,8 +126,9 @@ public final class ManualBenchmarkState {
// The computation needs double precision, but long int is fine for final reporting.
private Stats mStats;
- private int mStatsReportFlags = STATS_REPORT_MEDIAN | STATS_REPORT_MEAN
- | STATS_REPORT_PERCENTILE90 | STATS_REPORT_PERCENTILE95 | STATS_REPORT_STDDEV;
+ private int mStatsReportFlags =
+ StatsReport.FLAG_MEDIAN | StatsReport.FLAG_MEAN | StatsReport.FLAG_STDDEV;
+ private int[] mStatsReportPercentiles = {90 , 95};
private boolean shouldReport(int statsReportFlag) {
return (mStatsReportFlags & statsReportFlag) != 0;
@@ -136,9 +147,10 @@ public final class ManualBenchmarkState {
if (targetTestDurationNs >= 0) {
mTargetTestDurationNs = targetTestDurationNs;
}
- final int statsReportFlags = testAnnotation.statsReportFlags();
- if (statsReportFlags >= 0) {
- mStatsReportFlags = statsReportFlags;
+ final StatsReport statsReport = testAnnotation.statsReport();
+ if (statsReport != null && statsReport.flags() != DEFAULT_STATS_REPORT) {
+ mStatsReportFlags = statsReport.flags();
+ mStatsReportPercentiles = statsReport.percentiles();
}
}
@@ -189,11 +201,20 @@ public final class ManualBenchmarkState {
}
/**
- * Adds additional result while this benchmark is running. It is used when a sequence of
+ * @return {@code true} if the benchmark is in warmup state. It can be used to skip the
+ * operations or measurements that are unnecessary while the test isn't running the
+ * actual benchmark.
+ */
+ public boolean isWarmingUp() {
+ return mState == WARMUP;
+ }
+
+ /**
+ * Adds additional result while this benchmark isn't warming up. It is used when a sequence of
* operations is executed consecutively, the duration of each operation can also be recorded.
*/
public void addExtraResult(String key, long duration) {
- if (mState != RUNNING) {
+ if (isWarmingUp()) {
return;
}
if (mExtraResults == null) {
@@ -221,31 +242,30 @@ public final class ManualBenchmarkState {
}
private void fillStatus(Bundle status, String key, Stats stats) {
- if (shouldReport(STATS_REPORT_ITERATION)) {
+ if (shouldReport(StatsReport.FLAG_ITERATION)) {
status.putLong(key + "_iteration", stats.getSize());
}
- if (shouldReport(STATS_REPORT_MEDIAN)) {
+ if (shouldReport(StatsReport.FLAG_MEDIAN)) {
status.putLong(key + "_median", stats.getMedian());
}
- if (shouldReport(STATS_REPORT_MEAN)) {
+ if (shouldReport(StatsReport.FLAG_MEAN)) {
status.putLong(key + "_mean", Math.round(stats.getMean()));
}
- if (shouldReport(STATS_REPORT_MIN)) {
+ if (shouldReport(StatsReport.FLAG_MIN)) {
status.putLong(key + "_min", stats.getMin());
}
- if (shouldReport(STATS_REPORT_MAX)) {
+ if (shouldReport(StatsReport.FLAG_MAX)) {
status.putLong(key + "_max", stats.getMax());
}
- if (shouldReport(STATS_REPORT_PERCENTILE90)) {
- status.putLong(key + "_percentile90", stats.getPercentile90());
- }
- if (shouldReport(STATS_REPORT_PERCENTILE95)) {
- status.putLong(key + "_percentile95", stats.getPercentile95());
+ if (mStatsReportPercentiles != null) {
+ for (int percentile : mStatsReportPercentiles) {
+ status.putLong(key + "_percentile" + percentile, stats.getPercentile(percentile));
+ }
}
- if (shouldReport(STATS_REPORT_STDDEV)) {
+ if (shouldReport(StatsReport.FLAG_STDDEV)) {
status.putLong(key + "_stddev", Math.round(stats.getStandardDeviation()));
}
- if (shouldReport(STATS_REPORT_COEFFICIENT_VAR)) {
+ if (shouldReport(StatsReport.FLAG_COEFFICIENT_VAR)) {
status.putLong(key + "_cv",
Math.round((100 * stats.getStandardDeviation() / stats.getMean())));
}
@@ -276,6 +296,6 @@ public final class ManualBenchmarkState {
public @interface ManualBenchmarkTest {
long warmupDurationNs() default -1;
long targetTestDurationNs() default -1;
- @StatsReport int statsReportFlags() default -1;
+ StatsReport statsReport() default @StatsReport(flags = DEFAULT_STATS_REPORT);
}
}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/PerfTestActivity.java b/apct-tests/perftests/utils/src/android/perftests/utils/PerfTestActivity.java
new file mode 100644
index 000000000000..e934feb01a84
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/PerfTestActivity.java
@@ -0,0 +1,46 @@
+/*
+ * 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.perftests.utils;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+/**
+ * A simple activity used for testing, e.g. performance of activity switching, or as a base
+ * container of testing view.
+ */
+public class PerfTestActivity extends Activity {
+ public static final String INTENT_EXTRA_KEEP_SCREEN_ON = "keep_screen_on";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (getIntent().getBooleanExtra(INTENT_EXTRA_KEEP_SCREEN_ON, false)) {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+ }
+
+ public static Intent createLaunchIntent(Context context) {
+ final Intent intent = new Intent();
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setClass(context, PerfTestActivity.class);
+ return intent;
+ }
+}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java b/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java
index f650e810c6de..fb516a818f75 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java
@@ -16,34 +16,34 @@
package android.perftests.utils;
+import android.annotation.IntRange;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Stats {
- private long mMedian, mMin, mMax, mPercentile90, mPercentile95;
+ private long mMedian, mMin, mMax;
private double mMean, mStandardDeviation;
- private final int mSize;
+ private final List<Long> mValues;
/* Calculate stats in constructor. */
public Stats(List<Long> values) {
- // make a copy since we're modifying it
- values = new ArrayList<>(values);
final int size = values.size();
if (size < 2) {
throw new IllegalArgumentException("At least two results are necessary.");
}
+ // Make a copy since we're modifying it.
+ mValues = values = new ArrayList<>(values);
+
Collections.sort(values);
- mSize = size;
mMin = values.get(0);
mMax = values.get(values.size() - 1);
mMedian = size % 2 == 0 ? (values.get(size / 2) + values.get(size / 2 - 1)) / 2 :
values.get(size / 2);
- mPercentile90 = getPercentile(values, 90);
- mPercentile95 = getPercentile(values, 95);
for (int i = 0; i < size; ++i) {
long result = values.get(i);
@@ -59,7 +59,7 @@ public class Stats {
}
public int getSize() {
- return mSize;
+ return mValues.size();
}
public double getMean() {
@@ -82,12 +82,8 @@ public class Stats {
return mStandardDeviation;
}
- public long getPercentile90() {
- return mPercentile90;
- }
-
- public long getPercentile95() {
- return mPercentile95;
+ public long getPercentile(@IntRange(from = 0, to = 100) int percentile) {
+ return getPercentile(mValues, percentile);
}
private static long getPercentile(List<Long> values, int percentile) {
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/StubActivity.java b/apct-tests/perftests/utils/src/android/perftests/utils/StubActivity.java
deleted file mode 100644
index 8f03f7eea584..000000000000
--- a/apct-tests/perftests/utils/src/android/perftests/utils/StubActivity.java
+++ /dev/null
@@ -1,30 +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 android.perftests.utils;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-
-public class StubActivity extends Activity {
- public static Intent createLaunchIntent(Context context) {
- final Intent intent = new Intent();
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.setClass(context, StubActivity.class);
- return intent;
- }
-}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/TestPackageInstaller.java b/apct-tests/perftests/utils/src/android/perftests/utils/TestPackageInstaller.java
new file mode 100644
index 000000000000..a433d801acaf
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/TestPackageInstaller.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.perftests.utils;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.PackageInstaller;
+import android.content.res.Resources;
+import android.util.Log;
+
+import org.junit.Assert;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * Installs packages included within the assets directory.
+ */
+public class TestPackageInstaller {
+ private static final String LOG_TAG = "TestPackageInstaller";
+ private static final String BROADCAST_ACTION =
+ "com.android.perftests.core.ACTION_INSTALL_COMMIT";
+
+ private final Context mContext;
+ public TestPackageInstaller(Context context) {
+ mContext = context;
+ }
+
+
+
+ /**
+ * Installs an APK located at the specified path in the assets directory.
+ **/
+ public InstalledPackage installPackage(String resourceName) throws IOException,
+ InterruptedException {
+ Log.d(LOG_TAG, "Installing resource APK '" + resourceName + "'");
+ LocalBroadcastReceiver intentSender = new LocalBroadcastReceiver(mContext);
+
+ // Initialize the package install session.
+ PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
+ PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ params.setInstallAsInstantApp(false);
+ int sessionId = packageInstaller.createSession(params);
+ PackageInstaller.Session session = packageInstaller.openSession(sessionId);
+
+ // Copy the apk to the install session.
+ try (OutputStream os = session.openWrite("TestPackageInstaller", 0, -1);
+ InputStream is = mContext.getResources().getAssets().openNonAsset(resourceName)) {
+ if (is == null) {
+ throw new IOException("Resource " + resourceName + " not found");
+ }
+ byte[] buffer = new byte[4096];
+ int n;
+ while ((n = is.read(buffer)) >= 0) {
+ os.write(buffer, 0, n);
+ }
+ }
+
+ session.commit(intentSender.getIntentSender(sessionId));
+ session.close();
+
+ // Retrieve the results of the installation.
+ Intent intent = intentSender.getIntentSenderResult();
+ int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ Assert.assertEquals(PackageInstaller.STATUS_SUCCESS, status);
+ String packageName = intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME);
+ return new InstalledPackage(sessionId, packageName);
+ }
+
+ public class InstalledPackage {
+ private int mSessionId;
+ private String mPackageName;
+
+ private InstalledPackage(int sessionId, String packageName) {
+ mSessionId = sessionId;
+ mPackageName = packageName;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public void uninstall() throws Exception {
+ Log.d(LOG_TAG, "Uninstalling package '" + mPackageName + "'");
+ LocalBroadcastReceiver intentSender = new LocalBroadcastReceiver(mContext);
+ PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
+ packageInstaller.uninstall(mPackageName, intentSender.getIntentSender(mSessionId));
+ int status = intentSender.getIntentSenderResult()
+ .getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
+ Assert.assertEquals(PackageInstaller.STATUS_SUCCESS, status);
+ }
+ }
+
+ private class LocalBroadcastReceiver extends BroadcastReceiver {
+ private final BlockingQueue<Intent> mIntentSenderResults = new LinkedBlockingQueue<>();
+ private final Context mContext;
+
+ private LocalBroadcastReceiver(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mIntentSenderResults.add(intent);
+ }
+
+ IntentSender getIntentSender(int sessionId) {
+ String action = BROADCAST_ACTION + "." + sessionId;
+ IntentFilter filter = new IntentFilter(action);
+ mContext.registerReceiver(this, filter);
+
+ Intent intent = new Intent(action);
+ PendingIntent pending = PendingIntent.getBroadcast(mContext, sessionId, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ return pending.getIntentSender();
+ }
+
+ Intent getIntentSenderResult() throws InterruptedException {
+ return mIntentSenderResults.take();
+ }
+ }
+}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/TraceMarkParser.java b/apct-tests/perftests/utils/src/android/perftests/utils/TraceMarkParser.java
new file mode 100644
index 000000000000..1afed3a0be5b
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/TraceMarkParser.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.perftests.utils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.Predicate;
+
+/**
+ * Utility to get the slice of tracing_mark_write S,F,B,E (Async: start, finish, Sync: begin, end).
+ * Use {@link #visit(String)} to process the trace in text form. The filtered results can be
+ * obtained by {@link #forAllSlices(BiConsumer)}.
+ *
+ * @see android.os.Trace
+ */
+public class TraceMarkParser {
+ /** All slices by the name of {@link TraceMarkLine}. */
+ private final Map<String, List<TraceMarkSlice>> mSlicesMap = new HashMap<>();
+ /** The nested depth of each task-pid. */
+ private final Map<String, Integer> mDepthMap = new HashMap<>();
+ /** The start trace lines that haven't matched the corresponding end. */
+ private final Map<String, TraceMarkLine> mPendingStarts = new HashMap<>();
+
+ private final Predicate<TraceMarkLine> mTraceLineFilter;
+
+ public TraceMarkParser(Predicate<TraceMarkLine> traceLineFilter) {
+ mTraceLineFilter = traceLineFilter;
+ }
+
+ /** Only accept the trace event with the given names. */
+ public TraceMarkParser(String... traceNames) {
+ this(line -> {
+ for (String name : traceNames) {
+ if (name.equals(line.name)) {
+ return true;
+ }
+ }
+ return false;
+ });
+ }
+
+ /** Computes {@link TraceMarkSlice} by the given trace line. */
+ public void visit(String textTraceLine) {
+ final TraceMarkLine line = TraceMarkLine.parse(textTraceLine);
+ if (line == null) {
+ return;
+ }
+
+ if (line.isAsync) {
+ // Async-trace contains name in the start and finish event.
+ if (mTraceLineFilter.test(line)) {
+ if (line.isBegin) {
+ mPendingStarts.put(line.name, line);
+ } else {
+ final TraceMarkLine start = mPendingStarts.remove(line.name);
+ if (start != null) {
+ addSlice(start, line);
+ }
+ }
+ }
+ return;
+ }
+
+ int depth = 1;
+ if (line.isBegin) {
+ final Integer existingDepth = mDepthMap.putIfAbsent(line.taskPid, 1);
+ if (existingDepth != null) {
+ mDepthMap.put(line.taskPid, depth = existingDepth + 1);
+ }
+ // Sync-trace only contains name in the begin event.
+ if (mTraceLineFilter.test(line)) {
+ mPendingStarts.put(getSyncPendingStartKey(line, depth), line);
+ }
+ } else {
+ final Integer existingDepth = mDepthMap.get(line.taskPid);
+ if (existingDepth != null) {
+ depth = existingDepth;
+ mDepthMap.put(line.taskPid, existingDepth - 1);
+ }
+ final TraceMarkLine begin = mPendingStarts.remove(getSyncPendingStartKey(line, depth));
+ if (begin != null) {
+ addSlice(begin, line);
+ }
+ }
+ }
+
+ private static String getSyncPendingStartKey(TraceMarkLine line, int depth) {
+ return line.taskPid + "@" + depth;
+ }
+
+ private void addSlice(TraceMarkLine begin, TraceMarkLine end) {
+ mSlicesMap.computeIfAbsent(
+ begin.name, k -> new ArrayList<>()).add(new TraceMarkSlice(begin, end));
+ }
+
+ public void forAllSlices(BiConsumer<String, List<TraceMarkSlice>> consumer) {
+ for (Map.Entry<String, List<TraceMarkSlice>> entry : mSlicesMap.entrySet()) {
+ consumer.accept(entry.getKey(), entry.getValue());
+ }
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ forAllSlices((key, slices) -> {
+ double totalMs = 0;
+ for (TraceMarkSlice s : slices) {
+ totalMs += s.getDurarionInSeconds() * 1000;
+ }
+ sb.append(key).append(" count=").append(slices.size()).append(" avg=")
+ .append(totalMs / slices.size()).append("ms\n");
+ });
+
+ if (!mPendingStarts.isEmpty()) {
+ sb.append("[Warning] Unresolved events:").append(mPendingStarts).append("\n");
+ }
+ return sb.toString();
+ }
+
+ public static class TraceMarkSlice {
+ public final TraceMarkLine begin;
+ public final TraceMarkLine end;
+
+ TraceMarkSlice(TraceMarkLine begin, TraceMarkLine end) {
+ this.begin = begin;
+ this.end = end;
+ }
+
+ public double getDurarionInSeconds() {
+ return end.timestamp - begin.timestamp;
+ }
+ }
+
+ // taskPid timestamp name
+ // # Async:
+ // Binder:129_F-349 ( 1296) [003] ...1 12.2776: tracing_mark_write: S|1296|launching: a.test|0
+ // android.anim-135 ( 1296) [005] ...1 12.3361: tracing_mark_write: F|1296|launching: a.test|0
+ // # Normal:
+ // Binder:129_6-315 ( 1296) [007] ...1 97.4576: tracing_mark_write: B|1296|relayoutWindow: xxx
+ // ... there may have other nested begin/end
+ // Binder:129_6-315 ( 1296) [007] ...1 97.4580: tracing_mark_write: E|1296
+ public static class TraceMarkLine {
+ static final String EVENT_KEYWORD = ": tracing_mark_write: ";
+ static final char ASYNC_START = 'S';
+ static final char ASYNC_FINISH = 'F';
+ static final char SYNC_BEGIN = 'B';
+ static final char SYNC_END = 'E';
+
+ public final String taskPid;
+ public final double timestamp;
+ public final String name;
+ public final boolean isAsync;
+ public final boolean isBegin;
+
+ TraceMarkLine(String rawLine, int typePos, int type) throws IllegalArgumentException {
+ taskPid = rawLine.substring(0, rawLine.indexOf('(')).trim();
+ final int timeEnd = rawLine.indexOf(':', taskPid.length());
+ if (timeEnd < 0) {
+ throw new IllegalArgumentException("Timestamp end not found");
+ }
+ final int timeBegin = rawLine.lastIndexOf(' ', timeEnd);
+ if (timeBegin < 0) {
+ throw new IllegalArgumentException("Timestamp start not found");
+ }
+ timestamp = Double.parseDouble(rawLine.substring(timeBegin, timeEnd));
+ isAsync = type == ASYNC_START || type == ASYNC_FINISH;
+ isBegin = type == ASYNC_START || type == SYNC_BEGIN;
+
+ if (!isAsync && !isBegin) {
+ name = "";
+ } else {
+ // Get the position of the second '|' from "S|1234|name".
+ final int nameBegin = rawLine.indexOf('|', typePos + 2) + 1;
+ if (nameBegin == 0) {
+ throw new IllegalArgumentException("Name begin not found");
+ }
+ if (isAsync) {
+ // Get the name from "S|1234|name|0".
+ name = rawLine.substring(nameBegin, rawLine.lastIndexOf('|'));
+ } else {
+ name = rawLine.substring(nameBegin);
+ }
+ }
+ }
+
+ static TraceMarkLine parse(String rawLine) {
+ final int eventPos = rawLine.indexOf(EVENT_KEYWORD);
+ if (eventPos < 0) {
+ return null;
+ }
+ final int typePos = eventPos + EVENT_KEYWORD.length();
+ if (typePos >= rawLine.length()) {
+ return null;
+ }
+ final int type = rawLine.charAt(typePos);
+ if (type != ASYNC_START && type != ASYNC_FINISH
+ && type != SYNC_BEGIN && type != SYNC_END) {
+ return null;
+ }
+
+ try {
+ return new TraceMarkLine(rawLine, typePos, type);
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "TraceMarkLine{pid=" + taskPid + " time=" + timestamp + " name=" + name
+ + " async=" + isAsync + " begin=" + isBegin + "}";
+ }
+ }
+}
diff --git a/apex/jobscheduler/README_js-mainline.md b/apex/jobscheduler/README_js-mainline.md
index c1ad666e3e05..ea20e3e29d99 100644
--- a/apex/jobscheduler/README_js-mainline.md
+++ b/apex/jobscheduler/README_js-mainline.md
@@ -1,23 +1,11 @@
# Making Job Scheduler into a Mainline Module
-## TODOs
-
-See also:
-- http://go/moving-js-code-for-mainline
-- http://go/jobscheduler-code-dependencies-2019-07
-
-- [ ] Move this into `frameworks/apex/jobscheduler/...`. Currently it's in `frameworks/base/apex/...`
-because `frameworks/apex/` is not a part of any git projects. (and also working on multiple
-projects is a pain.)
-
## Current structure
- JS service side classes are put in `jobscheduler-service.jar`.
It's *not* included in services.jar, and instead it's put in the system server classpath,
which currently looks like the following:
-`SYSTEMSERVERCLASSPATH=/system/framework/services.jar:/system/framework/jobscheduler-service.jar:/system/framework/ethernet-service.jar:/system/framework/wifi-service.jar:/system/framework/com.android.location.provider.jar`
-
- (Note `jobscheduler-service.jar` will be put at the end in http://ag/9128109)
+`SYSTEMSERVERCLASSPATH=/system/framework/services.jar:/system/framework/ethernet-service.jar:/system/framework/com.android.location.provider.jar:/system/framework/jobscheduler-service.jar`
`SYSTEMSERVERCLASSPATH` is generated from `PRODUCT_SYSTEM_SERVER_JARS`.
@@ -29,10 +17,4 @@ as of http://ag/9145619.
`framework.jar` merging the two jar files, and this jar file is what's
put on the device and loaded by Zygote.
-
-This is *not* the final design. From a gerrit comment on http://ag/9145619:
-
-> This CL is just the first step, and the current state isn't not really the final form. For now we just want to have two separate jars, which makes it easier for us to analyze dependencies between them, and I wanted to minimize the change to the rest of the system. So, for example, zygote will still only have "framework.jar" in its classpath, instead of the two jars for now.
-> But yes, eventually, we won't even be able to have the monolithic "framework.jar" file because of mainline, so we need to figure out how to build the system without creating it. At that point zygote will have the two separate jar files in its classpath.
-> When we reach that point, we should revisit the naming of it, and yes, maybe the simple "framework.jar" is a good option.
-> But again, for now, I want to make this change as transparent as possible to the rest of the world.
+The current structure is *not* the final design.
diff --git a/apex/jobscheduler/framework/Android.bp b/apex/jobscheduler/framework/Android.bp
index 621ff9a92bc5..3902aa212e32 100644
--- a/apex/jobscheduler/framework/Android.bp
+++ b/apex/jobscheduler/framework/Android.bp
@@ -1,5 +1,5 @@
filegroup {
- name: "jobscheduler-framework-source",
+ name: "framework-jobscheduler-sources",
srcs: [
"java/**/*.java",
"java/android/app/job/IJobCallback.aidl",
@@ -12,13 +12,12 @@ filegroup {
java_library {
name: "jobscheduler-framework",
- installable: true,
+ installable: false,
+ compile_dex: true,
sdk_version: "core_platform",
-
srcs: [
- ":jobscheduler-framework-source",
+ ":framework-jobscheduler-sources",
],
-
aidl: {
export_include_dirs: [
"java",
diff --git a/apex/jobscheduler/framework/java/android/app/DeviceIdleFrameworkInitializer.java b/apex/jobscheduler/framework/java/android/app/DeviceIdleFrameworkInitializer.java
index a807eb1d3311..5b1405628b49 100644
--- a/apex/jobscheduler/framework/java/android/app/DeviceIdleFrameworkInitializer.java
+++ b/apex/jobscheduler/framework/java/android/app/DeviceIdleFrameworkInitializer.java
@@ -36,7 +36,7 @@ public class DeviceIdleFrameworkInitializer {
SystemServiceRegistry.registerCachedService(
Context.DEVICE_IDLE_CONTROLLER, DeviceIdleManager.class,
(context, b) -> new DeviceIdleManager(
- context.getOuterContext(), IDeviceIdleController.Stub.asInterface(b)));
+ context, IDeviceIdleController.Stub.asInterface(b)));
PowerManager.setIsIgnoringBatteryOptimizationsCallback((packageName) -> {
// No need for synchronization on sIDeviceIdleController; worst case
// we just initialize it twice.
diff --git a/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java b/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
index 9039f921b3ba..e27670c34fb2 100644
--- a/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
+++ b/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
@@ -17,10 +17,13 @@
package android.os;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
+import java.util.List;
+
/**
* Access to the service that keeps track of device idleness and drives low power mode based on
* that.
@@ -66,4 +69,19 @@ public class DeviceIdleManager {
return new String[0];
}
}
+
+ /**
+ * Add the specified packages to the power save whitelist.
+ *
+ * @return the number of packages that were successfully added to the whitelist
+ */
+ @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+ public int addPowerSaveWhitelistApps(@NonNull List<String> packageNames) {
+ try {
+ return mService.addPowerSaveWhitelistApps(packageNames);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return 0;
+ }
+ }
}
diff --git a/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
index 9d5becbf77cd..20fb000b36d3 100644
--- a/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
+++ b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
@@ -21,6 +21,7 @@ import android.os.UserHandle;
/** @hide */
interface IDeviceIdleController {
void addPowerSaveWhitelistApp(String name);
+ int addPowerSaveWhitelistApps(in List<String> packageNames);
void removePowerSaveWhitelistApp(String name);
/* Removes an app from the system whitelist. Calling restoreSystemPowerWhitelistApp will add
the app back into the system whitelist */
diff --git a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
index 127324936e09..6475f5706a6d 100644
--- a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
@@ -49,4 +49,24 @@ public interface DeviceIdleInternal {
int[] getPowerSaveWhitelistUserAppIds();
int[] getPowerSaveTempWhitelistAppIds();
+
+ /**
+ * Listener to be notified when DeviceIdleController determines that the device has moved or is
+ * stationary.
+ */
+ interface StationaryListener {
+ void onDeviceStationaryChanged(boolean isStationary);
+ }
+
+ /**
+ * Registers a listener that will be notified when the system has detected that the device is
+ * stationary or in motion.
+ */
+ void registerStationaryListener(StationaryListener listener);
+
+ /**
+ * Unregisters a registered stationary listener from being notified when the system has detected
+ * that the device is stationary or in motion.
+ */
+ void unregisterStationaryListener(StationaryListener listener);
}
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
new file mode 100644
index 000000000000..c036c772d7d0
--- /dev/null
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -0,0 +1,112 @@
+package com.android.server.usage;
+
+import android.app.usage.AppStandbyInfo;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManager.StandbyBuckets;
+import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
+import android.content.Context;
+import android.os.Looper;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+import java.util.Set;
+
+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) {
+ 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);
+ } catch (NoSuchMethodException | InstantiationException
+ | IllegalAccessException | InvocationTargetException | ClassNotFoundException e) {
+ throw new RuntimeException("Unable to instantiate AppStandbyController!", e);
+ }
+ }
+
+ void onBootPhase(int phase);
+
+ void postCheckIdleStates(int userId);
+
+ /**
+ * We send a different message to check idle states once, otherwise we would end up
+ * scheduling a series of repeating checkIdleStates each time we fired off one.
+ */
+ void postOneTimeCheckIdleStates();
+
+ void reportEvent(UsageEvents.Event event, long elapsedRealtime, int userId);
+
+ void setLastJobRunTime(String packageName, int userId, long elapsedRealtime);
+
+ long getTimeSinceLastJobRun(String packageName, int userId);
+
+ void onUserRemoved(int userId);
+
+ void addListener(AppIdleStateChangeListener listener);
+
+ void removeListener(AppIdleStateChangeListener listener);
+
+ int getAppId(String packageName);
+
+ /**
+ * @see #isAppIdleFiltered(String, int, int, long)
+ */
+ boolean isAppIdleFiltered(String packageName, int userId, long elapsedRealtime,
+ boolean shouldObfuscateInstantApps);
+
+ /**
+ * Checks if an app has been idle for a while and filters out apps that are excluded.
+ * It returns false if the current system state allows all apps to be considered active.
+ * Called by interface impls.
+ */
+ boolean isAppIdleFiltered(String packageName, int appId, int userId,
+ long elapsedRealtime);
+
+ int[] getIdleUidsForUser(int userId);
+
+ void setAppIdleAsync(String packageName, boolean idle, int userId);
+
+ @StandbyBuckets
+ int getAppStandbyBucket(String packageName, int userId,
+ long elapsedRealtime, boolean shouldObfuscateInstantApps);
+
+ List<AppStandbyInfo> getAppStandbyBuckets(int userId);
+
+ void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
+ int reason, long elapsedRealtime, boolean resetTimeout);
+
+ void addActiveDeviceAdmin(String adminPkg, int userId);
+
+ void setActiveAdminApps(Set<String> adminPkgs, int userId);
+
+ void onAdminDataAvailable();
+
+ void clearCarrierPrivilegedApps();
+
+ void flushToDisk(int userId);
+
+ void flushDurationsToDisk();
+
+ void initializeDefaultsForSystemApps(int userId);
+
+ void postReportContentProviderUsage(String name, String packageName, int userId);
+
+ void postReportSyncScheduled(String packageName, int userId, boolean exempted);
+
+ void postReportExemptedSyncStart(String packageName, int userId);
+
+ void dumpUser(IndentingPrintWriter idpw, int userId, String pkg);
+
+ void dumpState(String[] args, PrintWriter pw);
+
+ boolean isAppIdleEnabled();
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 65aaf20ecacb..4ee46f453bca 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -48,7 +48,6 @@ import android.os.BatteryStats;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
-import android.os.FileUtils;
import android.os.Handler;
import android.os.IDeviceIdleController;
import android.os.Looper;
@@ -106,6 +105,8 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.stream.Collectors;
/**
@@ -275,6 +276,7 @@ public class DeviceIdleController extends SystemService
private IBatteryStats mBatteryStats;
private ActivityManagerInternal mLocalActivityManager;
private ActivityTaskManagerInternal mLocalActivityTaskManager;
+ private DeviceIdleInternal mLocalService;
private PowerManagerInternal mLocalPowerManager;
private PowerManager mPowerManager;
private INetworkPolicyManager mNetworkPolicyManager;
@@ -289,6 +291,7 @@ public class DeviceIdleController extends SystemService
private boolean mLightEnabled;
private boolean mDeepEnabled;
private boolean mQuickDozeActivated;
+ private boolean mQuickDozeActivatedWhileIdling;
private boolean mForceIdle;
private boolean mNetworkConnected;
private boolean mScreenOn;
@@ -300,6 +303,10 @@ public class DeviceIdleController extends SystemService
private boolean mHasNetworkLocation;
private Location mLastGenericLocation;
private Location mLastGpsLocation;
+
+ /** Time in the elapsed realtime timebase when this listener last received a motion event. */
+ private long mLastMotionEventElapsed;
+
// Current locked state of the screen
private boolean mScreenLocked;
private int mNumBlockingConstraints = 0;
@@ -549,6 +556,9 @@ public class DeviceIdleController extends SystemService
*/
private ArrayMap<String, Integer> mRemovedFromSystemWhitelistApps = new ArrayMap<>();
+ private final ArraySet<DeviceIdleInternal.StationaryListener> mStationaryListeners =
+ new ArraySet<>();
+
private static final int EVENT_NULL = 0;
private static final int EVENT_NORMAL = 1;
private static final int EVENT_LIGHT_IDLE = 2;
@@ -607,6 +617,22 @@ public class DeviceIdleController extends SystemService
}
};
+
+ private final AlarmManager.OnAlarmListener mMotionTimeoutAlarmListener = () -> {
+ synchronized (DeviceIdleController.this) {
+ if (!isStationaryLocked()) {
+ // If the device keeps registering motion, then the alarm should be
+ // rescheduled, so this shouldn't go off until the device is stationary.
+ // This case may happen in a race condition (alarm goes off right before
+ // motion is detected, but handleMotionDetectedLocked is called before
+ // we enter this block).
+ Slog.w(TAG, "motion timeout went off and device isn't stationary");
+ return;
+ }
+ }
+ postStationaryStatusUpdated();
+ };
+
private final AlarmManager.OnAlarmListener mSensingTimeoutAlarmListener
= new AlarmManager.OnAlarmListener() {
@Override
@@ -656,12 +682,70 @@ public class DeviceIdleController extends SystemService
}
};
+ /** Post stationary status only to this listener. */
+ private void postStationaryStatus(DeviceIdleInternal.StationaryListener listener) {
+ mHandler.obtainMessage(MSG_REPORT_STATIONARY_STATUS, listener).sendToTarget();
+ }
+
+ /** Post stationary status to all registered listeners. */
+ private void postStationaryStatusUpdated() {
+ mHandler.sendEmptyMessage(MSG_REPORT_STATIONARY_STATUS);
+ }
+
+ private boolean isStationaryLocked() {
+ final long now = mInjector.getElapsedRealtime();
+ return mMotionListener.active
+ // Listening for motion for long enough and last motion was long enough ago.
+ && now - Math.max(mMotionListener.activatedTimeElapsed, mLastMotionEventElapsed)
+ >= mConstants.MOTION_INACTIVE_TIMEOUT;
+ }
+
+ @VisibleForTesting
+ void registerStationaryListener(DeviceIdleInternal.StationaryListener listener) {
+ synchronized (this) {
+ if (!mStationaryListeners.add(listener)) {
+ // Listener already registered.
+ return;
+ }
+ postStationaryStatus(listener);
+ if (mMotionListener.active) {
+ if (!isStationaryLocked() && mStationaryListeners.size() == 1) {
+ // First listener to be registered and the device isn't stationary, so we
+ // need to register the alarm to report the device is stationary.
+ scheduleMotionTimeoutAlarmLocked();
+ }
+ } else {
+ startMonitoringMotionLocked();
+ scheduleMotionTimeoutAlarmLocked();
+ }
+ }
+ }
+
+ private void unregisterStationaryListener(DeviceIdleInternal.StationaryListener listener) {
+ synchronized (this) {
+ if (mStationaryListeners.remove(listener) && mStationaryListeners.size() == 0
+ // Motion detection is started when transitioning from INACTIVE to IDLE_PENDING
+ // and so doesn't need to be on for ACTIVE or INACTIVE states.
+ // Motion detection isn't needed when idling due to Quick Doze.
+ && (mState == STATE_ACTIVE || mState == STATE_INACTIVE
+ || mQuickDozeActivated)) {
+ maybeStopMonitoringMotionLocked();
+ }
+ }
+ }
+
@VisibleForTesting
final class MotionListener extends TriggerEventListener
implements SensorEventListener {
boolean active = false;
+ /**
+ * Time in the elapsed realtime timebase when this listener was activated. Only valid if
+ * {@link #active} is true.
+ */
+ long activatedTimeElapsed;
+
public boolean isActive() {
return active;
}
@@ -669,7 +753,6 @@ public class DeviceIdleController extends SystemService
@Override
public void onTrigger(TriggerEvent event) {
synchronized (DeviceIdleController.this) {
- active = false;
motionLocked();
}
}
@@ -677,8 +760,6 @@ public class DeviceIdleController extends SystemService
@Override
public void onSensorChanged(SensorEvent event) {
synchronized (DeviceIdleController.this) {
- mSensorManager.unregisterListener(this, mMotionSensor);
- active = false;
motionLocked();
}
}
@@ -696,6 +777,7 @@ public class DeviceIdleController extends SystemService
}
if (success) {
active = true;
+ activatedTimeElapsed = mInjector.getElapsedRealtime();
} else {
Slog.e(TAG, "Unable to register for " + mMotionSensor);
}
@@ -1303,6 +1385,8 @@ public class DeviceIdleController extends SystemService
private static final int MSG_REPORT_IDLE_OFF = 4;
private static final int MSG_REPORT_ACTIVE = 5;
private static final int MSG_TEMP_APP_WHITELIST_TIMEOUT = 6;
+ @VisibleForTesting
+ static final int MSG_REPORT_STATIONARY_STATUS = 7;
private static final int MSG_FINISH_IDLE_OP = 8;
private static final int MSG_REPORT_TEMP_APP_WHITELIST_CHANGED = 9;
private static final int MSG_SEND_CONSTRAINT_MONITORING = 10;
@@ -1428,6 +1512,32 @@ public class DeviceIdleController extends SystemService
updatePreIdleFactor();
maybeDoImmediateMaintenance();
} break;
+ case MSG_REPORT_STATIONARY_STATUS: {
+ final DeviceIdleInternal.StationaryListener newListener =
+ (DeviceIdleInternal.StationaryListener) msg.obj;
+ final DeviceIdleInternal.StationaryListener[] listeners;
+ final boolean isStationary;
+ synchronized (DeviceIdleController.this) {
+ isStationary = isStationaryLocked();
+ if (newListener == null) {
+ // Only notify all listeners if we aren't directing to one listener.
+ listeners = mStationaryListeners.toArray(
+ new DeviceIdleInternal.StationaryListener[
+ mStationaryListeners.size()]);
+ } else {
+ listeners = null;
+ }
+ }
+ if (listeners != null) {
+ for (DeviceIdleInternal.StationaryListener listener : listeners) {
+ listener.onDeviceStationaryChanged(isStationary);
+ }
+ }
+ if (newListener != null) {
+ newListener.onDeviceStationaryChanged(isStationary);
+ }
+ }
+ break;
}
}
}
@@ -1441,11 +1551,20 @@ public class DeviceIdleController extends SystemService
if (DEBUG) {
Slog.i(TAG, "addPowerSaveWhitelistApp(name = " + name + ")");
}
+ addPowerSaveWhitelistApps(Collections.singletonList(name));
+ }
+
+ @Override
+ public int addPowerSaveWhitelistApps(List<String> packageNames) {
+ if (DEBUG) {
+ Slog.i(TAG,
+ "addPowerSaveWhitelistApps(name = " + packageNames + ")");
+ }
getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
null);
long ident = Binder.clearCallingIdentity();
try {
- addPowerSaveWhitelistAppInternal(name);
+ return addPowerSaveWhitelistAppsInternal(packageNames);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1680,11 +1799,21 @@ public class DeviceIdleController extends SystemService
public int[] getPowerSaveTempWhitelistAppIds() {
return DeviceIdleController.this.getAppIdTempWhitelistInternal();
}
+
+ @Override
+ public void registerStationaryListener(StationaryListener listener) {
+ DeviceIdleController.this.registerStationaryListener(listener);
+ }
+
+ @Override
+ public void unregisterStationaryListener(StationaryListener listener) {
+ DeviceIdleController.this.unregisterStationaryListener(listener);
+ }
}
static class Injector {
private final Context mContext;
- private ConnectivityService mConnectivityService;
+ private ConnectivityManager mConnectivityManager;
private Constants mConstants;
private LocationManager mLocationManager;
@@ -1705,12 +1834,11 @@ public class DeviceIdleController extends SystemService
return new AppStateTracker(ctx, looper);
}
- ConnectivityService getConnectivityService() {
- if (mConnectivityService == null) {
- mConnectivityService = (ConnectivityService) ServiceManager.getService(
- Context.CONNECTIVITY_SERVICE);
+ ConnectivityManager getConnectivityManager() {
+ if (mConnectivityManager == null) {
+ mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
}
- return mConnectivityService;
+ return mConnectivityManager;
}
Constants getConstants(DeviceIdleController controller, Handler handler,
@@ -1864,7 +1992,8 @@ public class DeviceIdleController extends SystemService
mBinderService = new BinderService();
publishBinderService(Context.DEVICE_IDLE_CONTROLLER, mBinderService);
- publishLocalService(DeviceIdleInternal.class, new LocalService());
+ mLocalService = new LocalService();
+ publishLocalService(DeviceIdleInternal.class, mLocalService);
}
@Override
@@ -1909,7 +2038,7 @@ public class DeviceIdleController extends SystemService
if (getContext().getResources().getBoolean(
com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) {
- mLocationRequest = new LocationRequest()
+ mLocationRequest = LocationRequest.create()
.setQuality(LocationRequest.ACCURACY_FINE)
.setInterval(0)
.setFastestInterval(0)
@@ -2070,21 +2199,35 @@ public class DeviceIdleController extends SystemService
}
}
- public boolean addPowerSaveWhitelistAppInternal(String name) {
+ private int addPowerSaveWhitelistAppsInternal(List<String> pkgNames) {
+ int numAdded = 0;
+ int numErrors = 0;
synchronized (this) {
- try {
- ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(name,
- PackageManager.MATCH_ANY_USER);
- if (mPowerSaveWhitelistUserApps.put(name, UserHandle.getAppId(ai.uid)) == null) {
- reportPowerSaveWhitelistChangedLocked();
- updateWhitelistAppIdsLocked();
- writeConfigFileLocked();
+ for (int i = pkgNames.size() - 1; i >= 0; --i) {
+ final String name = pkgNames.get(i);
+ if (name == null) {
+ numErrors++;
+ continue;
}
- return true;
- } catch (PackageManager.NameNotFoundException e) {
- return false;
+ try {
+ ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(name,
+ PackageManager.MATCH_ANY_USER);
+ if (mPowerSaveWhitelistUserApps.put(name, UserHandle.getAppId(ai.uid))
+ == null) {
+ numAdded++;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Tried to add unknown package to power save whitelist: " + name);
+ numErrors++;
+ }
+ }
+ if (numAdded > 0) {
+ reportPowerSaveWhitelistChangedLocked();
+ updateWhitelistAppIdsLocked();
+ writeConfigFileLocked();
}
}
+ return pkgNames.size() - numErrors;
}
public boolean removePowerSaveWhitelistAppInternal(String name) {
@@ -2497,9 +2640,9 @@ public class DeviceIdleController extends SystemService
}
void updateConnectivityState(Intent connIntent) {
- ConnectivityService cm;
+ ConnectivityManager cm;
synchronized (this) {
- cm = mInjector.getConnectivityService();
+ cm = mInjector.getConnectivityManager();
}
if (cm == null) {
return;
@@ -2595,6 +2738,8 @@ public class DeviceIdleController extends SystemService
void updateQuickDozeFlagLocked(boolean enabled) {
if (DEBUG) Slog.i(TAG, "updateQuickDozeFlagLocked: enabled=" + enabled);
mQuickDozeActivated = enabled;
+ mQuickDozeActivatedWhileIdling =
+ mQuickDozeActivated && (mState == STATE_IDLE || mState == STATE_IDLE_MAINTENANCE);
if (enabled) {
// If Quick Doze is enabled, see if we should go straight into it.
becomeInactiveIfAppropriateLocked();
@@ -2779,10 +2924,11 @@ public class DeviceIdleController extends SystemService
mNextIdlePendingDelay = 0;
mNextIdleDelay = 0;
mIdleStartTime = 0;
+ mQuickDozeActivatedWhileIdling = false;
cancelAlarmLocked();
cancelSensingTimeoutAlarmLocked();
cancelLocatingLocked();
- stopMonitoringMotionLocked();
+ maybeStopMonitoringMotionLocked();
mAnyMotionDetector.stop();
updateActiveConstraintsLocked();
}
@@ -3269,11 +3415,23 @@ public class DeviceIdleController extends SystemService
void motionLocked() {
if (DEBUG) Slog.d(TAG, "motionLocked()");
- // The motion sensor will have been disabled at this point
+ mLastMotionEventElapsed = mInjector.getElapsedRealtime();
handleMotionDetectedLocked(mConstants.MOTION_INACTIVE_TIMEOUT, "motion");
}
void handleMotionDetectedLocked(long timeout, String type) {
+ if (mStationaryListeners.size() > 0) {
+ postStationaryStatusUpdated();
+ scheduleMotionTimeoutAlarmLocked();
+ }
+ if (mQuickDozeActivated && !mQuickDozeActivatedWhileIdling) {
+ // Don't exit idle due to motion if quick doze is enabled.
+ // However, if the device started idling due to the normal progression (going through
+ // all the states) and then had quick doze activated, come out briefly on motion so the
+ // user can get slightly fresher content.
+ return;
+ }
+ maybeStopMonitoringMotionLocked();
// The device is not yet active, so we want to go back to the pending idle
// state to wait again for no motion. Note that we only monitor for motion
// after moving out of the inactive state, so no need to worry about that.
@@ -3325,10 +3483,15 @@ public class DeviceIdleController extends SystemService
}
}
- void stopMonitoringMotionLocked() {
- if (DEBUG) Slog.d(TAG, "stopMonitoringMotionLocked()");
- if (mMotionSensor != null && mMotionListener.active) {
+ /**
+ * Stops motion monitoring. Will not stop monitoring if there are registered stationary
+ * listeners.
+ */
+ private void maybeStopMonitoringMotionLocked() {
+ if (DEBUG) Slog.d(TAG, "maybeStopMonitoringMotionLocked()");
+ if (mMotionSensor != null && mMotionListener.active && mStationaryListeners.size() == 0) {
mMotionListener.unregisterLocked();
+ cancelMotionTimeoutAlarmLocked();
}
}
@@ -3355,6 +3518,10 @@ public class DeviceIdleController extends SystemService
}
}
+ private void cancelMotionTimeoutAlarmLocked() {
+ mAlarmManager.cancel(mMotionTimeoutAlarmListener);
+ }
+
void cancelSensingTimeoutAlarmLocked() {
if (mNextSensingTimeoutAlarmTime != 0) {
mNextSensingTimeoutAlarmTime = 0;
@@ -3395,6 +3562,14 @@ public class DeviceIdleController extends SystemService
mNextLightAlarmTime, "DeviceIdleController.light", mLightAlarmListener, mHandler);
}
+ private void scheduleMotionTimeoutAlarmLocked() {
+ if (DEBUG) Slog.d(TAG, "scheduleMotionAlarmLocked");
+ long nextMotionTimeoutAlarmTime =
+ mInjector.getElapsedRealtime() + mConstants.MOTION_INACTIVE_TIMEOUT;
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextMotionTimeoutAlarmTime,
+ "DeviceIdleController.motion", mMotionTimeoutAlarmListener, mHandler);
+ }
+
void scheduleSensingTimeoutAlarmLocked(long delay) {
if (DEBUG) Slog.d(TAG, "scheduleSensingAlarmLocked(" + delay + ")");
mNextSensingTimeoutAlarmTime = SystemClock.elapsedRealtime() + delay;
@@ -3598,9 +3773,6 @@ public class DeviceIdleController extends SystemService
try {
stream = mConfigFile.startWrite();
memStream.writeTo(stream);
- stream.flush();
- FileUtils.sync(stream);
- stream.close();
mConfigFile.finishWrite(stream);
} catch (IOException e) {
Slog.w(TAG, "Error writing config file", e);
@@ -3923,7 +4095,8 @@ public class DeviceIdleController extends SystemService
char op = arg.charAt(0);
String pkg = arg.substring(1);
if (op == '+') {
- if (addPowerSaveWhitelistAppInternal(pkg)) {
+ if (addPowerSaveWhitelistAppsInternal(Collections.singletonList(pkg))
+ == 1) {
pw.println("Added: " + pkg);
} else {
pw.println("Unknown package: " + pkg);
@@ -4315,9 +4488,14 @@ public class DeviceIdleController extends SystemService
}
pw.println(" }");
}
- if (mUseMotionSensor) {
+ if (mUseMotionSensor || mStationaryListeners.size() > 0) {
pw.print(" mMotionActive="); pw.println(mMotionListener.active);
pw.print(" mNotMoving="); pw.println(mNotMoving);
+ pw.print(" mMotionListener.activatedTimeElapsed=");
+ pw.println(mMotionListener.activatedTimeElapsed);
+ pw.print(" mLastMotionEventElapsed="); pw.println(mLastMotionEventElapsed);
+ pw.print(" "); pw.print(mStationaryListeners.size());
+ pw.println(" stationary listeners registered");
}
pw.print(" mLocating="); pw.print(mLocating); pw.print(" mHasGps=");
pw.print(mHasGps); pw.print(" mHasNetwork=");
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 a633350996cd..c3ffad66d829 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -56,8 +56,6 @@ import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
import android.os.Binder;
import android.os.Handler;
-import android.os.IThermalService;
-import android.os.IThermalStatusListener;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
@@ -66,7 +64,6 @@ import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.SystemClock;
-import android.os.Temperature;
import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.os.WorkSource;
@@ -81,7 +78,6 @@ import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.ArrayUtils;
@@ -105,12 +101,17 @@ import com.android.server.job.controllers.QuotaController;
import com.android.server.job.controllers.StateController;
import com.android.server.job.controllers.StorageController;
import com.android.server.job.controllers.TimeController;
+import com.android.server.job.restrictions.JobRestriction;
+import com.android.server.job.restrictions.ThermalStatusRestriction;
import libcore.util.EmptyArray;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -146,10 +147,53 @@ public class JobSchedulerService extends com.android.server.SystemService
@VisibleForTesting
public static Clock sSystemClock = Clock.systemUTC();
+
+ private abstract static class MySimpleClock extends Clock {
+ private final ZoneId mZoneId;
+
+ MySimpleClock(ZoneId zoneId) {
+ this.mZoneId = zoneId;
+ }
+
+ @Override
+ public ZoneId getZone() {
+ return mZoneId;
+ }
+
+ @Override
+ public Clock withZone(ZoneId zone) {
+ return new MySimpleClock(zone) {
+ @Override
+ public long millis() {
+ return MySimpleClock.this.millis();
+ }
+ };
+ }
+
+ @Override
+ public abstract long millis();
+
+ @Override
+ public Instant instant() {
+ return Instant.ofEpochMilli(millis());
+ }
+ }
+
@VisibleForTesting
- public static Clock sUptimeMillisClock = SystemClock.uptimeClock();
+ public static Clock sUptimeMillisClock = new MySimpleClock(ZoneOffset.UTC) {
+ @Override
+ public long millis() {
+ return SystemClock.uptimeMillis();
+ }
+ };
+
@VisibleForTesting
- public static Clock sElapsedRealtimeClock = SystemClock.elapsedRealtimeClock();
+ public static Clock sElapsedRealtimeClock = new MySimpleClock(ZoneOffset.UTC) {
+ @Override
+ public long millis() {
+ return SystemClock.elapsedRealtime();
+ }
+ };
/** Global local for all job scheduler state. */
final Object mLock = new Object();
@@ -186,12 +230,12 @@ public class JobSchedulerService extends com.android.server.SystemService
private final DeviceIdleJobsController mDeviceIdleJobsController;
/** Needed to get remaining quota time. */
private final QuotaController mQuotaController;
-
- /** Need directly for receiving thermal events */
- private IThermalService mThermalService;
- /** Thermal constraint. */
- @GuardedBy("mLock")
- private boolean mThermalConstraint = false;
+ /**
+ * List of restrictions.
+ * Note: do not add to or remove from this list at runtime except in the constructor, because we
+ * do not synchronize access to this list.
+ */
+ private final List<JobRestriction> mJobRestrictions;
/**
* Queue of pending jobs. The JobServiceContext class will receive jobs from this list
@@ -222,11 +266,6 @@ public class JobSchedulerService extends com.android.server.SystemService
boolean mReportedActive;
/**
- * Are we currently in device-wide standby parole?
- */
- volatile boolean mInParole;
-
- /**
* A mapping of which uids are currently in the foreground to their effective priority.
*/
final SparseIntArray mUidPriorityOverride = new SparseIntArray();
@@ -285,19 +324,6 @@ public class JobSchedulerService extends com.android.server.SystemService
}
}
- /**
- * Thermal event received from Thermal Service
- */
- private final class ThermalStatusListener extends IThermalStatusListener.Stub {
- @Override public void onStatusChange(int status) {
- // Throttle for Temperature.THROTTLING_SEVERE and above
- synchronized (mLock) {
- mThermalConstraint = status >= Temperature.THROTTLING_SEVERE;
- }
- onControllerStateChanged();
- }
- }
-
static class MaxJobCounts {
private final KeyValueListParser.IntValue mTotal;
private final KeyValueListParser.IntValue mMaxBg;
@@ -994,17 +1020,18 @@ public class JobSchedulerService extends com.android.server.SystemService
// This may throw a SecurityException.
jobStatus.prepareLocked();
- if (work != null) {
- // If work has been supplied, enqueue it into the new job.
- jobStatus.enqueueWorkLocked(work);
- }
-
if (toCancel != null) {
// Implicitly replaces the existing job record with the new instance
cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
} else {
startTrackingJobLocked(jobStatus, null);
}
+
+ if (work != null) {
+ // If work has been supplied, enqueue it into the new job.
+ jobStatus.enqueueWorkLocked(work);
+ }
+
StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
uId, null, jobStatus.getBatteryName(),
StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
@@ -1292,6 +1319,10 @@ public class JobSchedulerService extends com.android.server.SystemService
mQuotaController = new QuotaController(this);
mControllers.add(mQuotaController);
+ // Create restrictions
+ mJobRestrictions = new ArrayList<>();
+ mJobRestrictions.add(new ThermalStatusRestriction(this));
+
// If the job store determined that it can't yet reschedule persisted jobs,
// we need to start watching the clock.
if (!mJobs.jobTimesInflatedValid()) {
@@ -1383,15 +1414,9 @@ public class JobSchedulerService extends com.android.server.SystemService
// Remove any jobs that are not associated with any of the current users.
cancelJobsForNonExistentUsers();
- // Register thermal callback
- mThermalService = IThermalService.Stub.asInterface(
- ServiceManager.getService(Context.THERMAL_SERVICE));
- if (mThermalService != null) {
- try {
- mThermalService.registerThermalStatusListener(new ThermalStatusListener());
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to register thermal callback.", e);
- }
+
+ for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
+ mJobRestrictions.get(i).onSystemServicesReady();
}
} else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
synchronized (mLock) {
@@ -1833,9 +1858,28 @@ public class JobSchedulerService extends com.android.server.SystemService
}
}
- private boolean isJobThermalConstrainedLocked(JobStatus job) {
- return mThermalConstraint && job.hasConnectivityConstraint()
- && (evaluateJobPriorityLocked(job) < JobInfo.PRIORITY_FOREGROUND_APP);
+ /**
+ * Check if a job is restricted by any of the declared {@link JobRestriction}s.
+ * Note, that the jobs with {@link JobInfo#PRIORITY_FOREGROUND_APP} priority or higher may not
+ * be restricted, thus we won't even perform the check, but simply return null early.
+ *
+ * @param job to be checked
+ * @return the first {@link JobRestriction} restricting the given job that has been found; null
+ * - if passes all the restrictions or has priority {@link JobInfo#PRIORITY_FOREGROUND_APP}
+ * or higher.
+ */
+ private JobRestriction checkIfRestricted(JobStatus job) {
+ if (evaluateJobPriorityLocked(job) >= JobInfo.PRIORITY_FOREGROUND_APP) {
+ // Jobs with PRIORITY_FOREGROUND_APP or higher should not be restricted
+ return null;
+ }
+ for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
+ final JobRestriction restriction = mJobRestrictions.get(i);
+ if (restriction.isJobRestricted(job)) {
+ return restriction;
+ }
+ }
+ return null;
}
private void stopNonReadyActiveJobsLocked() {
@@ -1849,10 +1893,13 @@ public class JobSchedulerService extends com.android.server.SystemService
serviceContext.cancelExecutingJobLocked(
JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
"cancelled due to unsatisfied constraints");
- } else if (isJobThermalConstrainedLocked(running)) {
- serviceContext.cancelExecutingJobLocked(
- JobParameters.REASON_DEVICE_THERMAL,
- "cancelled due to thermal condition");
+ } else {
+ final JobRestriction restriction = checkIfRestricted(running);
+ if (restriction != null) {
+ final int reason = restriction.getReason();
+ serviceContext.cancelExecutingJobLocked(reason,
+ "restricted due to " + JobParameters.getReasonName(reason));
+ }
}
}
}
@@ -2089,7 +2136,7 @@ public class JobSchedulerService extends com.android.server.SystemService
return false;
}
- if (isJobThermalConstrainedLocked(job)) {
+ if (checkIfRestricted(job) != null) {
return false;
}
@@ -2121,7 +2168,7 @@ public class JobSchedulerService extends com.android.server.SystemService
job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
job.getUserId());
} catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
+ throw new RuntimeException(e);
}
if (service == null) {
@@ -2170,7 +2217,7 @@ public class JobSchedulerService extends com.android.server.SystemService
return false;
}
- if (isJobThermalConstrainedLocked(job)) {
+ if (checkIfRestricted(job) != null) {
return false;
}
@@ -2309,14 +2356,6 @@ public class JobSchedulerService extends com.android.server.SystemService
}
@Override
- public void onParoleStateChanged(boolean isParoleOn) {
- if (DEBUG_STANDBY) {
- Slog.i(TAG, "Global parole state now " + (isParoleOn ? "ON" : "OFF"));
- }
- mInParole = isParoleOn;
- }
-
- @Override
public void onUserInteractionStarted(String packageName, int userId) {
final int uid = mLocalPM.getPackageUid(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
@@ -2979,12 +3018,11 @@ public class JobSchedulerService extends com.android.server.SystemService
}
pw.println();
- pw.print(" In parole?: ");
- pw.print(mInParole);
- pw.println();
- pw.print(" In thermal throttling?: ");
- pw.print(mThermalConstraint);
- pw.println();
+ for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
+ pw.print(" ");
+ mJobRestrictions.get(i).dumpConstants(pw);
+ pw.println();
+ }
pw.println();
pw.println("Started users: " + Arrays.toString(mStartedUsers));
@@ -3005,14 +3043,30 @@ public class JobSchedulerService extends com.android.server.SystemService
job.dump(pw, " ", true, nowElapsed);
+
+ pw.print(" Restricted due to:");
+ final boolean isRestricted = checkIfRestricted(job) != null;
+ if (isRestricted) {
+ for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
+ final JobRestriction restriction = mJobRestrictions.get(i);
+ if (restriction.isJobRestricted(job)) {
+ final int reason = restriction.getReason();
+ pw.write(" " + JobParameters.getReasonName(reason) + "[" + reason + "]");
+ }
+ }
+ } else {
+ pw.print(" none");
+ }
+ pw.println(".");
+
pw.print(" Ready: ");
pw.print(isReadyToBeExecutedLocked(job));
pw.print(" (job=");
pw.print(job.isReady());
pw.print(" user=");
pw.print(areUsersStartedLocked(job));
- pw.print(" !thermal=");
- pw.print(!isJobThermalConstrainedLocked(job));
+ pw.print(" !restricted=");
+ pw.print(!isRestricted);
pw.print(" !pending=");
pw.print(!mPendingJobs.contains(job));
pw.print(" !active=");
@@ -3151,8 +3205,9 @@ public class JobSchedulerService extends com.android.server.SystemService
}
proto.end(settingsToken);
- proto.write(JobSchedulerServiceDumpProto.IN_PAROLE, mInParole);
- proto.write(JobSchedulerServiceDumpProto.IN_THERMAL, mThermalConstraint);
+ for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
+ mJobRestrictions.get(i).dumpConstants(proto);
+ }
for (int u : mStartedUsers) {
proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u);
@@ -3179,8 +3234,8 @@ public class JobSchedulerService extends com.android.server.SystemService
proto.write(JobSchedulerServiceDumpProto.RegisteredJob.ARE_USERS_STARTED,
areUsersStartedLocked(job));
proto.write(
- JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_THERMAL_CONSTRAINED,
- isJobThermalConstrainedLocked(job));
+ JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_RESTRICTED,
+ checkIfRestricted(job) != null);
proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_PENDING,
mPendingJobs.contains(job));
proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE,
@@ -3190,6 +3245,16 @@ public class JobSchedulerService extends com.android.server.SystemService
proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_USABLE,
isComponentUsable(job));
+ for (JobRestriction restriction : mJobRestrictions) {
+ final long restrictionsToken = proto.start(
+ JobSchedulerServiceDumpProto.RegisteredJob.RESTRICTIONS);
+ proto.write(JobSchedulerServiceDumpProto.JobRestriction.REASON,
+ restriction.getReason());
+ proto.write(JobSchedulerServiceDumpProto.JobRestriction.IS_RESTRICTING,
+ restriction.isJobRestricted(job));
+ proto.end(restrictionsToken);
+ }
+
proto.end(rjToken);
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 782e6463d845..26db4a30ebda 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -251,7 +251,7 @@ public final class JobServiceContext implements ServiceConnection {
binding = mContext.bindServiceAsUser(intent, this,
Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
| Context.BIND_NOT_PERCEPTIBLE,
- new UserHandle(job.getUserId()));
+ UserHandle.of(job.getUserId()));
} catch (SecurityException e) {
// Some permission policy, for example INTERACT_ACROSS_USERS and
// android:singleUser, can result in a SecurityException being thrown from
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 c2bdb6caffd3..2f5f555817ec 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -1157,22 +1157,14 @@ public final class JobStore {
private void removeAll(Predicate<JobStatus> predicate) {
for (int jobSetIndex = mJobs.size() - 1; jobSetIndex >= 0; jobSetIndex--) {
final ArraySet<JobStatus> jobs = mJobs.valueAt(jobSetIndex);
- for (int jobIndex = jobs.size() - 1; jobIndex >= 0; jobIndex--) {
- if (predicate.test(jobs.valueAt(jobIndex))) {
- jobs.removeAt(jobIndex);
- }
- }
+ jobs.removeIf(predicate);
if (jobs.size() == 0) {
mJobs.removeAt(jobSetIndex);
}
}
for (int jobSetIndex = mJobsPerSourceUid.size() - 1; jobSetIndex >= 0; jobSetIndex--) {
final ArraySet<JobStatus> jobs = mJobsPerSourceUid.valueAt(jobSetIndex);
- for (int jobIndex = jobs.size() - 1; jobIndex >= 0; jobIndex--) {
- if (predicate.test(jobs.valueAt(jobIndex))) {
- jobs.removeAt(jobIndex);
- }
- }
+ jobs.removeIf(predicate);
if (jobs.size() == 0) {
mJobsPerSourceUid.removeAt(jobSetIndex);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index 43e8dfb6bab6..7d3630338fc1 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -34,6 +34,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.UserHandle;
import android.text.format.DateUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DataUnit;
import android.util.Log;
@@ -441,7 +442,7 @@ public final class ConnectivityController extends StateController implements
synchronized (mLock) {
// Since this is a really hot codepath, temporarily cache any
// answers that we get from ConnectivityManager.
- final SparseArray<NetworkCapabilities> networkToCapabilities = new SparseArray<>();
+ final ArrayMap<Network, NetworkCapabilities> networkToCapabilities = new ArrayMap<>();
boolean changed = false;
if (filterUid == -1) {
@@ -460,17 +461,16 @@ public final class ConnectivityController extends StateController implements
}
private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs, Network filterNetwork,
- SparseArray<NetworkCapabilities> networkToCapabilities) {
+ ArrayMap<Network, NetworkCapabilities> networkToCapabilities) {
if (jobs == null || jobs.size() == 0) {
return false;
}
final Network network = mConnManager.getActiveNetworkForUid(jobs.valueAt(0).getSourceUid());
- final int netId = network != null ? network.netId : -1;
- NetworkCapabilities capabilities = networkToCapabilities.get(netId);
+ NetworkCapabilities capabilities = networkToCapabilities.get(network);
if (capabilities == null) {
capabilities = mConnManager.getNetworkCapabilities(network);
- networkToCapabilities.put(netId, capabilities);
+ networkToCapabilities.put(network, capabilities);
}
final boolean networkMatch = (filterNetwork == null
|| Objects.equals(filterNetwork, network));
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index adb43141b9c1..c76346ffd996 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -28,7 +28,7 @@ import android.net.Network;
import android.net.Uri;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.text.format.TimeMigrationUtils;
+import android.text.format.DateFormat;
import android.util.ArraySet;
import android.util.Pair;
import android.util.Slog;
@@ -1518,7 +1518,7 @@ public final class JobStatus {
if (job.getClipData() != null) {
pw.print(prefix); pw.print(" Clip data: ");
StringBuilder b = new StringBuilder(128);
- job.getClipData().toShortString(b);
+ b.append(job.getClipData());
pw.println(b);
}
if (uriPerms != null) {
@@ -1659,14 +1659,18 @@ public final class JobStatus {
}
if (mLastSuccessfulRunTime != 0) {
pw.print(prefix); pw.print("Last successful run: ");
- pw.println(TimeMigrationUtils.formatMillisWithFixedFormat(mLastSuccessfulRunTime));
+ pw.println(formatTime(mLastSuccessfulRunTime));
}
if (mLastFailedRunTime != 0) {
pw.print(prefix); pw.print("Last failed run: ");
- pw.println(TimeMigrationUtils.formatMillisWithFixedFormat(mLastFailedRunTime));
+ pw.println(formatTime(mLastFailedRunTime));
}
}
+ private static CharSequence formatTime(long time) {
+ return DateFormat.format("yyyy-MM-dd HH:mm:ss", time);
+ }
+
public void dump(ProtoOutputStream proto, long fieldId, boolean full, long elapsedRealtimeMillis) {
final long token = proto.start(fieldId);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 400d90203453..14dce84e686a 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -414,8 +414,6 @@ public final class QuotaController extends StateController {
private final Handler mHandler;
private final QcConstants mQcConstants;
- private volatile boolean mInParole;
-
/** How much time each app will have to run jobs within their standby bucket window. */
private long mAllowedTimePerPeriodMs = QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_MS;
@@ -711,7 +709,6 @@ public final class QuotaController extends StateController {
public long getMaxJobExecutionTimeMsLocked(@NonNull final JobStatus jobStatus) {
// If quota is currently "free", then the job can run for the full amount of time.
if (mChargeTracker.isCharging()
- || mInParole
|| isTopStartedJobLocked(jobStatus)
|| isUidInForeground(jobStatus.getSourceUid())) {
return JobServiceContext.EXECUTING_TIMESLICE_MILLIS;
@@ -737,8 +734,8 @@ public final class QuotaController extends StateController {
final int standbyBucket) {
if (standbyBucket == NEVER_INDEX) return false;
- // Quota constraint is not enforced while charging or when parole is on.
- if (mChargeTracker.isCharging() || mInParole) {
+ // Quota constraint is not enforced while charging.
+ if (mChargeTracker.isCharging()) {
return true;
}
@@ -1780,20 +1777,6 @@ public final class QuotaController extends StateController {
}
});
}
-
- @Override
- public void onParoleStateChanged(final boolean isParoleOn) {
- mInParole = isParoleOn;
- if (DEBUG) {
- Slog.i(TAG, "Global parole state now " + (isParoleOn ? "ON" : "OFF"));
- }
- // Update job bookkeeping out of band.
- BackgroundThread.getHandler().post(() -> {
- synchronized (mLock) {
- maybeUpdateAllConstraintsLocked();
- }
- });
- }
}
private final class DeleteTimingSessionsFunctor implements Consumer<List<TimingSession>> {
@@ -2515,7 +2498,6 @@ public final class QuotaController extends StateController {
public void dumpControllerStateLocked(final IndentingPrintWriter pw,
final Predicate<JobStatus> predicate) {
pw.println("Is charging: " + mChargeTracker.isCharging());
- pw.println("In parole: " + mInParole);
pw.println("Current elapsed time: " + sElapsedRealtimeClock.millis());
pw.println();
@@ -2639,7 +2621,6 @@ public final class QuotaController extends StateController {
final long mToken = proto.start(StateControllerProto.QUOTA);
proto.write(StateControllerProto.QuotaController.IS_CHARGING, mChargeTracker.isCharging());
- proto.write(StateControllerProto.QuotaController.IS_IN_PAROLE, mInParole);
proto.write(StateControllerProto.QuotaController.ELAPSED_REALTIME,
sElapsedRealtimeClock.millis());
diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java
new file mode 100644
index 000000000000..e180c55e1bf2
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java
@@ -0,0 +1,72 @@
+/*
+ * 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.job.restrictions;
+
+import android.app.job.JobInfo;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.job.JobSchedulerService;
+import com.android.server.job.controllers.JobStatus;
+
+/**
+ * Used by {@link JobSchedulerService} to impose additional restrictions regarding whether jobs
+ * should be scheduled or not based on the state of the system/device.
+ * Every restriction is associated with exactly one reason (from {@link
+ * android.app.job.JobParameters#JOB_STOP_REASON_CODES}), which could be retrieved using {@link
+ * #getReason()}.
+ * Note, that this is not taken into account for the jobs that have priority
+ * {@link JobInfo#PRIORITY_FOREGROUND_APP} or higher.
+ */
+public abstract class JobRestriction {
+
+ final JobSchedulerService mService;
+ private final int mReason;
+
+ JobRestriction(JobSchedulerService service, int reason) {
+ mService = service;
+ mReason = reason;
+ }
+
+ /**
+ * Called when the system boot phase has reached
+ * {@link com.android.server.SystemService#PHASE_SYSTEM_SERVICES_READY}.
+ */
+ public void onSystemServicesReady() {
+ }
+
+ /**
+ * Called by {@link JobSchedulerService} to check if it may proceed with scheduling the job (in
+ * case all constraints are satisfied and all other {@link JobRestriction}s are fine with it)
+ *
+ * @param job to be checked
+ * @return false if the {@link JobSchedulerService} should not schedule this job at the moment,
+ * true - otherwise
+ */
+ public abstract boolean isJobRestricted(JobStatus job);
+
+ /** Dump any internal constants the Restriction may have. */
+ public abstract void dumpConstants(IndentingPrintWriter pw);
+
+ /** Dump any internal constants the Restriction may have. */
+ public abstract void dumpConstants(ProtoOutputStream proto);
+
+ /** @return reason code for the Restriction. */
+ public final int getReason() {
+ return mReason;
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
new file mode 100644
index 000000000000..aa7696df6dbd
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
@@ -0,0 +1,74 @@
+/*
+ * 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.job.restrictions;
+
+import android.app.job.JobParameters;
+import android.os.PowerManager;
+import android.os.PowerManager.OnThermalStatusChangedListener;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.job.JobSchedulerService;
+import com.android.server.job.JobSchedulerServiceDumpProto;
+import com.android.server.job.controllers.JobStatus;
+
+public class ThermalStatusRestriction extends JobRestriction {
+ private static final String TAG = "ThermalStatusRestriction";
+
+ private volatile boolean mIsThermalRestricted = false;
+
+ private PowerManager mPowerManager;
+
+ public ThermalStatusRestriction(JobSchedulerService service) {
+ super(service, JobParameters.REASON_DEVICE_THERMAL);
+ }
+
+ @Override
+ public void onSystemServicesReady() {
+ mPowerManager = mService.getContext().getSystemService(PowerManager.class);
+ // Use MainExecutor
+ mPowerManager.addThermalStatusListener(new OnThermalStatusChangedListener() {
+ @Override
+ public void onThermalStatusChanged(int status) {
+ // This is called on the main thread. Do not do any slow operations in it.
+ // mService.onControllerStateChanged() will just post a message, which is okay.
+ final boolean shouldBeActive = status >= PowerManager.THERMAL_STATUS_SEVERE;
+ if (mIsThermalRestricted == shouldBeActive) {
+ return;
+ }
+ mIsThermalRestricted = shouldBeActive;
+ mService.onControllerStateChanged();
+ }
+ });
+ }
+
+ @Override
+ public boolean isJobRestricted(JobStatus job) {
+ return mIsThermalRestricted && job.hasConnectivityConstraint();
+ }
+
+ @Override
+ public void dumpConstants(IndentingPrintWriter pw) {
+ pw.print("In thermal throttling?: ");
+ pw.print(mIsThermalRestricted);
+ }
+
+ @Override
+ public void dumpConstants(ProtoOutputStream proto) {
+ proto.write(JobSchedulerServiceDumpProto.IN_THERMAL, mIsThermalRestricted);
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index 1e4861a89694..1e4861a89694 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
new file mode 100644
index 000000000000..ecc045995521
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -0,0 +1,1754 @@
+/**
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.android.server.usage;
+
+import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_PREDICTED_RESTORED;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOUT;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_START;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED_PRIV;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYNC_ADAPTER;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_INTERACTION;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_UPDATE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+
+import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
+
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.app.usage.AppStandbyInfo;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManager.StandbyBuckets;
+import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
+import android.appwidget.AppWidgetManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ParceledListSlice;
+import android.database.ContentObserver;
+import android.hardware.display.DisplayManager;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkRequest;
+import android.net.NetworkScoreManager;
+import android.os.BatteryStats;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.IDeviceIdleController;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings.Global;
+import android.telephony.TelephonyManager;
+import android.util.ArraySet;
+import android.util.KeyValueListParser;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.TimeUtils;
+import android.view.Display;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.os.SomeArgs;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.ConcurrentUtils;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocalServices;
+import com.android.server.usage.AppIdleHistory.AppUsageHistory;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.time.Duration;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Manages the standby state of an app, listening to various events.
+ *
+ * Unit test:
+ atest com.android.server.usage.AppStandbyControllerTests
+ */
+public class AppStandbyController implements AppStandbyInternal {
+
+ private static final String TAG = "AppStandbyController";
+ static final boolean DEBUG = false;
+
+ static final boolean COMPRESS_TIME = false;
+ private static final long ONE_MINUTE = 60 * 1000;
+ private static final long ONE_HOUR = ONE_MINUTE * 60;
+ private static final long ONE_DAY = ONE_HOUR * 24;
+
+ static final long[] SCREEN_TIME_THRESHOLDS = {
+ 0,
+ 0,
+ COMPRESS_TIME ? 120 * 1000 : 1 * ONE_HOUR,
+ COMPRESS_TIME ? 240 * 1000 : 2 * ONE_HOUR
+ };
+
+ static final long[] ELAPSED_TIME_THRESHOLDS = {
+ 0,
+ COMPRESS_TIME ? 1 * ONE_MINUTE : 12 * ONE_HOUR,
+ COMPRESS_TIME ? 4 * ONE_MINUTE : 24 * ONE_HOUR,
+ COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR
+ };
+
+ static final int[] THRESHOLD_BUCKETS = {
+ STANDBY_BUCKET_ACTIVE,
+ STANDBY_BUCKET_WORKING_SET,
+ STANDBY_BUCKET_FREQUENT,
+ STANDBY_BUCKET_RARE
+ };
+
+ /** Default expiration time for bucket prediction. After this, use thresholds to downgrade. */
+ private static final long DEFAULT_PREDICTION_TIMEOUT = 12 * ONE_HOUR;
+
+ /**
+ * Indicates the maximum wait time for admin data to be available;
+ */
+ private static final long WAIT_FOR_ADMIN_DATA_TIMEOUT_MS = 10_000;
+
+ // To name the lock for stack traces
+ static class Lock {}
+
+ /** Lock to protect the app's standby state. Required for calls into AppIdleHistory */
+ private final Object mAppIdleLock = new Lock();
+
+ /** Keeps the history and state for each app. */
+ @GuardedBy("mAppIdleLock")
+ private AppIdleHistory mAppIdleHistory;
+
+ @GuardedBy("mPackageAccessListeners")
+ private ArrayList<AppIdleStateChangeListener>
+ mPackageAccessListeners = new ArrayList<>();
+
+ /** Whether we've queried the list of carrier privileged apps. */
+ @GuardedBy("mAppIdleLock")
+ private boolean mHaveCarrierPrivilegedApps;
+
+ /** List of carrier-privileged apps that should be excluded from standby */
+ @GuardedBy("mAppIdleLock")
+ private List<String> mCarrierPrivilegedApps;
+
+ @GuardedBy("mActiveAdminApps")
+ private final SparseArray<Set<String>> mActiveAdminApps = new SparseArray<>();
+
+ private final CountDownLatch mAdminDataAvailableLatch = new CountDownLatch(1);
+
+ // Messages for the handler
+ static final int MSG_INFORM_LISTENERS = 3;
+ static final int MSG_FORCE_IDLE_STATE = 4;
+ static final int MSG_CHECK_IDLE_STATES = 5;
+ static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8;
+ static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
+ /** Check the state of one app: arg1 = userId, arg2 = uid, obj = (String) packageName */
+ static final int MSG_CHECK_PACKAGE_IDLE_STATE = 11;
+ static final int MSG_REPORT_SYNC_SCHEDULED = 12;
+ static final int MSG_REPORT_EXEMPTED_SYNC_START = 13;
+
+ long mCheckIdleIntervalMillis;
+ long[] mAppStandbyScreenThresholds = SCREEN_TIME_THRESHOLDS;
+ long[] mAppStandbyElapsedThresholds = ELAPSED_TIME_THRESHOLDS;
+ /** Minimum time a strong usage event should keep the bucket elevated. */
+ long mStrongUsageTimeoutMillis;
+ /** Minimum time a notification seen event should keep the bucket elevated. */
+ long mNotificationSeenTimeoutMillis;
+ /** Minimum time a system update event should keep the buckets elevated. */
+ long mSystemUpdateUsageTimeoutMillis;
+ /** Maximum time to wait for a prediction before using simple timeouts to downgrade buckets. */
+ long mPredictionTimeoutMillis;
+ /** Maximum time a sync adapter associated with a CP should keep the buckets elevated. */
+ long mSyncAdapterTimeoutMillis;
+ /**
+ * Maximum time an exempted sync should keep the buckets elevated, when sync is scheduled in
+ * non-doze
+ */
+ long mExemptedSyncScheduledNonDozeTimeoutMillis;
+ /**
+ * Maximum time an exempted sync should keep the buckets elevated, when sync is scheduled in
+ * doze
+ */
+ long mExemptedSyncScheduledDozeTimeoutMillis;
+ /**
+ * Maximum time an exempted sync should keep the buckets elevated, when sync is started.
+ */
+ long mExemptedSyncStartTimeoutMillis;
+ /**
+ * Maximum time an unexempted sync should keep the buckets elevated, when sync is scheduled
+ */
+ long mUnexemptedSyncScheduledTimeoutMillis;
+ /** Maximum time a system interaction should keep the buckets elevated. */
+ long mSystemInteractionTimeoutMillis;
+ /**
+ * Maximum time a foreground service start should keep the buckets elevated if the service
+ * start is the first usage of the app
+ */
+ long mInitialForegroundServiceStartTimeoutMillis;
+
+ private volatile boolean mAppIdleEnabled;
+ private boolean mSystemServicesReady = false;
+ // There was a system update, defaults need to be initialized after services are ready
+ private boolean mPendingInitializeDefaults;
+
+ private volatile boolean mPendingOneTimeCheckIdleStates;
+
+ private final AppStandbyHandler mHandler;
+ private final Context mContext;
+
+ // TODO: Provide a mechanism to set an external bucketing service
+
+ private AppWidgetManager mAppWidgetManager;
+ private ConnectivityManager mConnectivityManager;
+ private PackageManager mPackageManager;
+ Injector mInjector;
+
+ static final ArrayList<StandbyUpdateRecord> sStandbyUpdatePool = new ArrayList<>(4);
+
+ public static class StandbyUpdateRecord {
+ // Identity of the app whose standby state has changed
+ String packageName;
+ int userId;
+
+ // What the standby bucket the app is now in
+ int bucket;
+
+ // Whether the bucket change is because the user has started interacting with the app
+ boolean isUserInteraction;
+
+ // Reason for bucket change
+ int reason;
+
+ StandbyUpdateRecord(String pkgName, int userId, int bucket, int reason,
+ boolean isInteraction) {
+ this.packageName = pkgName;
+ this.userId = userId;
+ this.bucket = bucket;
+ this.reason = reason;
+ this.isUserInteraction = isInteraction;
+ }
+
+ public static StandbyUpdateRecord obtain(String pkgName, int userId,
+ int bucket, int reason, boolean isInteraction) {
+ synchronized (sStandbyUpdatePool) {
+ final int size = sStandbyUpdatePool.size();
+ if (size < 1) {
+ return new StandbyUpdateRecord(pkgName, userId, bucket, reason, isInteraction);
+ }
+ StandbyUpdateRecord r = sStandbyUpdatePool.remove(size - 1);
+ r.packageName = pkgName;
+ r.userId = userId;
+ r.bucket = bucket;
+ r.reason = reason;
+ r.isUserInteraction = isInteraction;
+ return r;
+ }
+ }
+
+ public void recycle() {
+ synchronized (sStandbyUpdatePool) {
+ sStandbyUpdatePool.add(this);
+ }
+ }
+ }
+
+ public AppStandbyController(Context context, Looper looper) {
+ this(new Injector(context, looper));
+ }
+
+ AppStandbyController(Injector injector) {
+ mInjector = injector;
+ mContext = mInjector.getContext();
+ mHandler = new AppStandbyHandler(mInjector.getLooper());
+ mPackageManager = mContext.getPackageManager();
+
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory = new AppIdleHistory(mInjector.getDataSystemDirectory(),
+ mInjector.elapsedRealtime());
+ }
+
+ IntentFilter packageFilter = new IntentFilter();
+ packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ packageFilter.addDataScheme("package");
+
+ mContext.registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, packageFilter,
+ null, mHandler);
+ }
+
+ @VisibleForTesting
+ void setAppIdleEnabled(boolean enabled) {
+ mAppIdleEnabled = enabled;
+ }
+
+ @Override
+ public boolean isAppIdleEnabled() {
+ return mAppIdleEnabled;
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ mInjector.onBootPhase(phase);
+ if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ Slog.d(TAG, "Setting app idle enabled state");
+ // Observe changes to the threshold
+ SettingsObserver settingsObserver = new SettingsObserver(mHandler);
+ settingsObserver.registerObserver();
+ settingsObserver.updateSettings();
+
+ mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class);
+ mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
+
+ mInjector.registerDisplayListener(mDisplayListener, mHandler);
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.updateDisplay(isDisplayOn(), mInjector.elapsedRealtime());
+ }
+
+ mSystemServicesReady = true;
+
+ boolean userFileExists;
+ synchronized (mAppIdleLock) {
+ userFileExists = mAppIdleHistory.userFileExists(UserHandle.USER_SYSTEM);
+ }
+
+ if (mPendingInitializeDefaults || !userFileExists) {
+ initializeDefaultsForSystemApps(UserHandle.USER_SYSTEM);
+ }
+
+ if (mPendingOneTimeCheckIdleStates) {
+ postOneTimeCheckIdleStates();
+ }
+ }
+ }
+
+ private void reportContentProviderUsage(String authority, String providerPkgName, int userId) {
+ if (!mAppIdleEnabled) return;
+
+ // Get sync adapters for the authority
+ String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser(
+ authority, userId);
+ final long elapsedRealtime = mInjector.elapsedRealtime();
+ for (String packageName: packages) {
+ // Only force the sync adapters to active if the provider is not in the same package and
+ // the sync adapter is a system package.
+ try {
+ PackageInfo pi = mPackageManager.getPackageInfoAsUser(
+ packageName, PackageManager.MATCH_SYSTEM_ONLY, userId);
+ if (pi == null || pi.applicationInfo == null) {
+ continue;
+ }
+ if (!packageName.equals(providerPkgName)) {
+ synchronized (mAppIdleLock) {
+ AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId,
+ STANDBY_BUCKET_ACTIVE, REASON_SUB_USAGE_SYNC_ADAPTER,
+ 0,
+ elapsedRealtime + mSyncAdapterTimeoutMillis);
+ maybeInformListeners(packageName, userId, elapsedRealtime,
+ appUsage.currentBucket, appUsage.bucketingReason, false);
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Shouldn't happen
+ }
+ }
+ }
+
+ private void reportExemptedSyncScheduled(String packageName, int userId) {
+ if (!mAppIdleEnabled) return;
+
+ final int bucketToPromote;
+ final int usageReason;
+ final long durationMillis;
+
+ if (!mInjector.isDeviceIdleMode()) {
+ // Not dozing.
+ bucketToPromote = STANDBY_BUCKET_ACTIVE;
+ usageReason = REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
+ durationMillis = mExemptedSyncScheduledNonDozeTimeoutMillis;
+ } else {
+ // Dozing.
+ bucketToPromote = STANDBY_BUCKET_WORKING_SET;
+ usageReason = REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE;
+ durationMillis = mExemptedSyncScheduledDozeTimeoutMillis;
+ }
+
+ final long elapsedRealtime = mInjector.elapsedRealtime();
+
+ synchronized (mAppIdleLock) {
+ AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId,
+ bucketToPromote, usageReason,
+ 0,
+ elapsedRealtime + durationMillis);
+ maybeInformListeners(packageName, userId, elapsedRealtime,
+ appUsage.currentBucket, appUsage.bucketingReason, false);
+ }
+ }
+
+ private void reportUnexemptedSyncScheduled(String packageName, int userId) {
+ if (!mAppIdleEnabled) return;
+
+ final long elapsedRealtime = mInjector.elapsedRealtime();
+ synchronized (mAppIdleLock) {
+ final int currentBucket =
+ mAppIdleHistory.getAppStandbyBucket(packageName, userId, elapsedRealtime);
+ if (currentBucket == STANDBY_BUCKET_NEVER) {
+ // Bring the app out of the never bucket
+ AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId,
+ STANDBY_BUCKET_WORKING_SET, REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED,
+ 0,
+ elapsedRealtime + mUnexemptedSyncScheduledTimeoutMillis);
+ maybeInformListeners(packageName, userId, elapsedRealtime,
+ appUsage.currentBucket, appUsage.bucketingReason, false);
+ }
+ }
+ }
+
+ private void reportExemptedSyncStart(String packageName, int userId) {
+ if (!mAppIdleEnabled) return;
+
+ final long elapsedRealtime = mInjector.elapsedRealtime();
+
+ synchronized (mAppIdleLock) {
+ AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId,
+ STANDBY_BUCKET_ACTIVE, REASON_SUB_USAGE_EXEMPTED_SYNC_START,
+ 0,
+ elapsedRealtime + mExemptedSyncStartTimeoutMillis);
+ maybeInformListeners(packageName, userId, elapsedRealtime,
+ appUsage.currentBucket, appUsage.bucketingReason, false);
+ }
+ }
+
+ @Override
+ public void postCheckIdleStates(int userId) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
+ }
+
+ @Override
+ public void postOneTimeCheckIdleStates() {
+ if (mInjector.getBootPhase() < PHASE_SYSTEM_SERVICES_READY) {
+ // Not booted yet; wait for it!
+ mPendingOneTimeCheckIdleStates = true;
+ } else {
+ mHandler.sendEmptyMessage(MSG_ONE_TIME_CHECK_IDLE_STATES);
+ mPendingOneTimeCheckIdleStates = false;
+ }
+ }
+
+ @VisibleForTesting
+ boolean checkIdleStates(int checkUserId) {
+ if (!mAppIdleEnabled) {
+ return false;
+ }
+
+ final int[] runningUserIds;
+ try {
+ runningUserIds = mInjector.getRunningUserIds();
+ if (checkUserId != UserHandle.USER_ALL
+ && !ArrayUtils.contains(runningUserIds, checkUserId)) {
+ return false;
+ }
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+
+ final long elapsedRealtime = mInjector.elapsedRealtime();
+ for (int i = 0; i < runningUserIds.length; i++) {
+ final int userId = runningUserIds[i];
+ if (checkUserId != UserHandle.USER_ALL && checkUserId != userId) {
+ continue;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Checking idle state for user " + userId);
+ }
+ List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
+ PackageManager.MATCH_DISABLED_COMPONENTS,
+ userId);
+ final int packageCount = packages.size();
+ for (int p = 0; p < packageCount; p++) {
+ final PackageInfo pi = packages.get(p);
+ final String packageName = pi.packageName;
+ checkAndUpdateStandbyState(packageName, userId, pi.applicationInfo.uid,
+ elapsedRealtime);
+ }
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "checkIdleStates took "
+ + (mInjector.elapsedRealtime() - elapsedRealtime));
+ }
+ return true;
+ }
+
+ /** Check if we need to update the standby state of a specific app. */
+ private void checkAndUpdateStandbyState(String packageName, @UserIdInt int userId,
+ int uid, long elapsedRealtime) {
+ if (uid <= 0) {
+ try {
+ uid = mPackageManager.getPackageUidAsUser(packageName, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ // Not a valid package for this user, nothing to do
+ // TODO: Remove any history of removed packages
+ return;
+ }
+ }
+ final boolean isSpecial = isAppSpecial(packageName,
+ UserHandle.getAppId(uid),
+ userId);
+ if (DEBUG) {
+ Slog.d(TAG, " Checking idle state for " + packageName + " special=" +
+ isSpecial);
+ }
+ if (isSpecial) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
+ STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
+ }
+ maybeInformListeners(packageName, userId, elapsedRealtime,
+ STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT, false);
+ } else {
+ synchronized (mAppIdleLock) {
+ final AppIdleHistory.AppUsageHistory app =
+ mAppIdleHistory.getAppUsageHistory(packageName,
+ userId, elapsedRealtime);
+ int reason = app.bucketingReason;
+ final int oldMainReason = reason & REASON_MAIN_MASK;
+
+ // If the bucket was forced by the user/developer, leave it alone.
+ // A usage event will be the only way to bring it out of this forced state
+ if (oldMainReason == REASON_MAIN_FORCED) {
+ return;
+ }
+ final int oldBucket = app.currentBucket;
+ int newBucket = Math.max(oldBucket, STANDBY_BUCKET_ACTIVE); // Undo EXEMPTED
+ boolean predictionLate = predictionTimedOut(app, elapsedRealtime);
+ // Compute age-based bucket
+ if (oldMainReason == REASON_MAIN_DEFAULT
+ || oldMainReason == REASON_MAIN_USAGE
+ || oldMainReason == REASON_MAIN_TIMEOUT
+ || predictionLate) {
+
+ if (!predictionLate && app.lastPredictedBucket >= STANDBY_BUCKET_ACTIVE
+ && app.lastPredictedBucket <= STANDBY_BUCKET_RARE) {
+ newBucket = app.lastPredictedBucket;
+ reason = REASON_MAIN_PREDICTED | REASON_SUB_PREDICTED_RESTORED;
+ if (DEBUG) {
+ Slog.d(TAG, "Restored predicted newBucket = " + newBucket);
+ }
+ } else {
+ newBucket = getBucketForLocked(packageName, userId,
+ elapsedRealtime);
+ if (DEBUG) {
+ Slog.d(TAG, "Evaluated AOSP newBucket = " + newBucket);
+ }
+ reason = REASON_MAIN_TIMEOUT;
+ }
+ }
+
+ // Check if the app is within one of the timeouts for forced bucket elevation
+ final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
+ if (newBucket >= STANDBY_BUCKET_ACTIVE
+ && app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
+ newBucket = STANDBY_BUCKET_ACTIVE;
+ reason = app.bucketingReason;
+ if (DEBUG) {
+ Slog.d(TAG, " Keeping at ACTIVE due to min timeout");
+ }
+ } else if (newBucket >= STANDBY_BUCKET_WORKING_SET
+ && app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
+ newBucket = STANDBY_BUCKET_WORKING_SET;
+ // If it was already there, keep the reason, else assume timeout to WS
+ reason = (newBucket == oldBucket)
+ ? app.bucketingReason
+ : REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
+ if (DEBUG) {
+ Slog.d(TAG, " Keeping at WORKING_SET due to min timeout");
+ }
+ }
+ if (DEBUG) {
+ Slog.d(TAG, " Old bucket=" + oldBucket
+ + ", newBucket=" + newBucket);
+ }
+ if (oldBucket < newBucket || predictionLate) {
+ mAppIdleHistory.setAppStandbyBucket(packageName, userId,
+ elapsedRealtime, newBucket, reason);
+ maybeInformListeners(packageName, userId, elapsedRealtime,
+ newBucket, reason, false);
+ }
+ }
+ }
+ }
+
+ /** Returns true if there hasn't been a prediction for the app in a while. */
+ private boolean predictionTimedOut(AppIdleHistory.AppUsageHistory app, long elapsedRealtime) {
+ return app.lastPredictedTime > 0
+ && mAppIdleHistory.getElapsedTime(elapsedRealtime)
+ - app.lastPredictedTime > mPredictionTimeoutMillis;
+ }
+
+ /** Inform listeners if the bucket has changed since it was last reported to listeners */
+ private void maybeInformListeners(String packageName, int userId,
+ long elapsedRealtime, int bucket, int reason, boolean userStartedInteracting) {
+ synchronized (mAppIdleLock) {
+ if (mAppIdleHistory.shouldInformListeners(packageName, userId,
+ elapsedRealtime, bucket)) {
+ final StandbyUpdateRecord r = StandbyUpdateRecord.obtain(packageName, userId,
+ bucket, reason, userStartedInteracting);
+ if (DEBUG) Slog.d(TAG, "Standby bucket for " + packageName + "=" + bucket);
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, r));
+ }
+ }
+ }
+
+ /**
+ * Evaluates next bucket based on time since last used and the bucketing thresholds.
+ * @param packageName the app
+ * @param userId the user
+ * @param elapsedRealtime as the name suggests, current elapsed time
+ * @return the bucket for the app, based on time since last used
+ */
+ @GuardedBy("mAppIdleLock")
+ @StandbyBuckets
+ private int getBucketForLocked(String packageName, int userId,
+ long elapsedRealtime) {
+ int bucketIndex = mAppIdleHistory.getThresholdIndex(packageName, userId,
+ elapsedRealtime, mAppStandbyScreenThresholds, mAppStandbyElapsedThresholds);
+ return THRESHOLD_BUCKETS[bucketIndex];
+ }
+
+ private void notifyBatteryStats(String packageName, int userId, boolean idle) {
+ try {
+ final int uid = mPackageManager.getPackageUidAsUser(packageName,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+ if (idle) {
+ mInjector.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE,
+ packageName, uid);
+ } else {
+ mInjector.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE,
+ packageName, uid);
+ }
+ } catch (PackageManager.NameNotFoundException | RemoteException e) {
+ }
+ }
+
+ @Override
+ public void reportEvent(UsageEvents.Event event, long elapsedRealtime, int userId) {
+ if (!mAppIdleEnabled) return;
+ synchronized (mAppIdleLock) {
+ final String pkg = event.getPackageName();
+ final int eventType = event.getEventType();
+ // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
+ // about apps that are on some kind of whitelist anyway.
+ final boolean previouslyIdle = mAppIdleHistory.isIdle(
+ pkg, userId, elapsedRealtime);
+ // Inform listeners if necessary
+ if ((eventType == UsageEvents.Event.ACTIVITY_RESUMED
+ || eventType == UsageEvents.Event.ACTIVITY_PAUSED
+ || eventType == UsageEvents.Event.SYSTEM_INTERACTION
+ || eventType == UsageEvents.Event.USER_INTERACTION
+ || eventType == UsageEvents.Event.NOTIFICATION_SEEN
+ || eventType == UsageEvents.Event.SLICE_PINNED
+ || eventType == UsageEvents.Event.SLICE_PINNED_PRIV
+ || eventType == UsageEvents.Event.FOREGROUND_SERVICE_START)) {
+
+ final AppUsageHistory appHistory = mAppIdleHistory.getAppUsageHistory(
+ pkg, userId, elapsedRealtime);
+ final int prevBucket = appHistory.currentBucket;
+ final int prevBucketReason = appHistory.bucketingReason;
+ final long nextCheckTime;
+ final int subReason = usageEventToSubReason(eventType);
+ final int reason = REASON_MAIN_USAGE | subReason;
+ if (eventType == UsageEvents.Event.NOTIFICATION_SEEN
+ || eventType == UsageEvents.Event.SLICE_PINNED) {
+ // Mild usage elevates to WORKING_SET but doesn't change usage time.
+ mAppIdleHistory.reportUsage(appHistory, pkg,
+ STANDBY_BUCKET_WORKING_SET, subReason,
+ 0, elapsedRealtime + mNotificationSeenTimeoutMillis);
+ nextCheckTime = mNotificationSeenTimeoutMillis;
+ } else if (eventType == UsageEvents.Event.SYSTEM_INTERACTION) {
+ mAppIdleHistory.reportUsage(appHistory, pkg,
+ STANDBY_BUCKET_ACTIVE, subReason,
+ 0, elapsedRealtime + mSystemInteractionTimeoutMillis);
+ nextCheckTime = mSystemInteractionTimeoutMillis;
+ } else if (eventType == UsageEvents.Event.FOREGROUND_SERVICE_START) {
+ // Only elevate bucket if this is the first usage of the app
+ if (prevBucket != STANDBY_BUCKET_NEVER) return;
+ mAppIdleHistory.reportUsage(appHistory, pkg,
+ STANDBY_BUCKET_ACTIVE, subReason,
+ 0, elapsedRealtime + mInitialForegroundServiceStartTimeoutMillis);
+ nextCheckTime = mInitialForegroundServiceStartTimeoutMillis;
+ } else {
+ mAppIdleHistory.reportUsage(appHistory, pkg,
+ STANDBY_BUCKET_ACTIVE, subReason,
+ elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis);
+ nextCheckTime = mStrongUsageTimeoutMillis;
+ }
+ mHandler.sendMessageDelayed(mHandler.obtainMessage
+ (MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, pkg),
+ nextCheckTime);
+ final boolean userStartedInteracting =
+ appHistory.currentBucket == STANDBY_BUCKET_ACTIVE &&
+ prevBucket != appHistory.currentBucket &&
+ (prevBucketReason & REASON_MAIN_MASK) != REASON_MAIN_USAGE;
+ maybeInformListeners(pkg, userId, elapsedRealtime,
+ appHistory.currentBucket, reason, userStartedInteracting);
+
+ if (previouslyIdle) {
+ notifyBatteryStats(pkg, userId, false);
+ }
+ }
+ }
+ }
+
+ private int usageEventToSubReason(int eventType) {
+ switch (eventType) {
+ case UsageEvents.Event.ACTIVITY_RESUMED: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
+ case UsageEvents.Event.ACTIVITY_PAUSED: return REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
+ case UsageEvents.Event.SYSTEM_INTERACTION: return REASON_SUB_USAGE_SYSTEM_INTERACTION;
+ case UsageEvents.Event.USER_INTERACTION: return REASON_SUB_USAGE_USER_INTERACTION;
+ case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN;
+ case UsageEvents.Event.SLICE_PINNED: return REASON_SUB_USAGE_SLICE_PINNED;
+ case UsageEvents.Event.SLICE_PINNED_PRIV: return REASON_SUB_USAGE_SLICE_PINNED_PRIV;
+ case UsageEvents.Event.FOREGROUND_SERVICE_START:
+ return REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
+ default: return 0;
+ }
+ }
+
+ @VisibleForTesting
+ void forceIdleState(String packageName, int userId, boolean idle) {
+ if (!mAppIdleEnabled) return;
+
+ final int appId = getAppId(packageName);
+ if (appId < 0) return;
+ final long elapsedRealtime = mInjector.elapsedRealtime();
+
+ final boolean previouslyIdle = isAppIdleFiltered(packageName, appId,
+ userId, elapsedRealtime);
+ final int standbyBucket;
+ synchronized (mAppIdleLock) {
+ standbyBucket = mAppIdleHistory.setIdle(packageName, userId, idle, elapsedRealtime);
+ }
+ final boolean stillIdle = isAppIdleFiltered(packageName, appId,
+ userId, elapsedRealtime);
+ // Inform listeners if necessary
+ if (previouslyIdle != stillIdle) {
+ maybeInformListeners(packageName, userId, elapsedRealtime, standbyBucket,
+ REASON_MAIN_FORCED, false);
+ if (!stillIdle) {
+ notifyBatteryStats(packageName, userId, idle);
+ }
+ }
+ }
+
+ @Override
+ public void setLastJobRunTime(String packageName, int userId, long elapsedRealtime) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.setLastJobRunTime(packageName, userId, elapsedRealtime);
+ }
+ }
+
+ @Override
+ public long getTimeSinceLastJobRun(String packageName, int userId) {
+ final long elapsedRealtime = mInjector.elapsedRealtime();
+ synchronized (mAppIdleLock) {
+ return mAppIdleHistory.getTimeSinceLastJobRun(packageName, userId, elapsedRealtime);
+ }
+ }
+
+ @Override
+ public void onUserRemoved(int userId) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.onUserRemoved(userId);
+ synchronized (mActiveAdminApps) {
+ mActiveAdminApps.remove(userId);
+ }
+ }
+ }
+
+ private boolean isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime) {
+ synchronized (mAppIdleLock) {
+ return mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
+ }
+ }
+
+ @Override
+ public void addListener(AppIdleStateChangeListener listener) {
+ synchronized (mPackageAccessListeners) {
+ if (!mPackageAccessListeners.contains(listener)) {
+ mPackageAccessListeners.add(listener);
+ }
+ }
+ }
+
+ @Override
+ public void removeListener(AppIdleStateChangeListener listener) {
+ synchronized (mPackageAccessListeners) {
+ mPackageAccessListeners.remove(listener);
+ }
+ }
+
+ @Override
+ public int getAppId(String packageName) {
+ try {
+ ApplicationInfo ai = mPackageManager.getApplicationInfo(packageName,
+ PackageManager.MATCH_ANY_USER
+ | PackageManager.MATCH_DISABLED_COMPONENTS);
+ return ai.uid;
+ } catch (PackageManager.NameNotFoundException re) {
+ return -1;
+ }
+ }
+
+ @Override
+ public boolean isAppIdleFiltered(String packageName, int userId, long elapsedRealtime,
+ boolean shouldObfuscateInstantApps) {
+ if (shouldObfuscateInstantApps &&
+ mInjector.isPackageEphemeral(userId, packageName)) {
+ return false;
+ }
+ return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
+ }
+
+ private boolean isAppSpecial(String packageName, int appId, int userId) {
+ if (packageName == null) return false;
+ // If not enabled at all, of course nobody is ever idle.
+ if (!mAppIdleEnabled) {
+ return true;
+ }
+ if (appId < Process.FIRST_APPLICATION_UID) {
+ // System uids never go idle.
+ return true;
+ }
+ if (packageName.equals("android")) {
+ // Nor does the framework (which should be redundant with the above, but for MR1 we will
+ // retain this for safety).
+ return true;
+ }
+ if (mSystemServicesReady) {
+ try {
+ // We allow all whitelisted apps, including those that don't want to be whitelisted
+ // for idle mode, because app idle (aka app standby) is really not as big an issue
+ // for controlling who participates vs. doze mode.
+ if (mInjector.isPowerSaveWhitelistExceptIdleApp(packageName)) {
+ return true;
+ }
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+
+ if (isActiveDeviceAdmin(packageName, userId)) {
+ return true;
+ }
+
+ if (isActiveNetworkScorer(packageName)) {
+ return true;
+ }
+
+ if (mAppWidgetManager != null
+ && mInjector.isBoundWidgetPackage(mAppWidgetManager, packageName, userId)) {
+ return true;
+ }
+
+ if (isDeviceProvisioningPackage(packageName)) {
+ return true;
+ }
+ }
+
+ // Check this last, as it can be the most expensive check
+ if (isCarrierApp(packageName)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean isAppIdleFiltered(String packageName, int appId, int userId,
+ long elapsedRealtime) {
+ if (isAppSpecial(packageName, appId, userId)) {
+ return false;
+ } else {
+ return isAppIdleUnfiltered(packageName, userId, elapsedRealtime);
+ }
+ }
+
+ @Override
+ public int[] getIdleUidsForUser(int userId) {
+ if (!mAppIdleEnabled) {
+ return new int[0];
+ }
+
+ final long elapsedRealtime = mInjector.elapsedRealtime();
+
+ List<ApplicationInfo> apps;
+ try {
+ ParceledListSlice<ApplicationInfo> slice = AppGlobals.getPackageManager()
+ .getInstalledApplications(/* flags= */ 0, userId);
+ if (slice == null) {
+ return new int[0];
+ }
+ apps = slice.getList();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ // State of each uid. Key is the uid. Value lower 16 bits is the number of apps
+ // associated with that uid, upper 16 bits is the number of those apps that is idle.
+ SparseIntArray uidStates = new SparseIntArray();
+
+ // Now resolve all app state. Iterating over all apps, keeping track of how many
+ // we find for each uid and how many of those are idle.
+ for (int i = apps.size() - 1; i >= 0; i--) {
+ ApplicationInfo ai = apps.get(i);
+
+ // Check whether this app is idle.
+ boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid),
+ userId, elapsedRealtime);
+
+ int index = uidStates.indexOfKey(ai.uid);
+ if (index < 0) {
+ uidStates.put(ai.uid, 1 + (idle ? 1<<16 : 0));
+ } else {
+ int value = uidStates.valueAt(index);
+ uidStates.setValueAt(index, value + 1 + (idle ? 1<<16 : 0));
+ }
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "getIdleUids took " + (mInjector.elapsedRealtime() - elapsedRealtime));
+ }
+ int numIdle = 0;
+ for (int i = uidStates.size() - 1; i >= 0; i--) {
+ int value = uidStates.valueAt(i);
+ if ((value&0x7fff) == (value>>16)) {
+ numIdle++;
+ }
+ }
+
+ int[] res = new int[numIdle];
+ numIdle = 0;
+ for (int i = uidStates.size() - 1; i >= 0; i--) {
+ int value = uidStates.valueAt(i);
+ if ((value&0x7fff) == (value>>16)) {
+ res[numIdle] = uidStates.keyAt(i);
+ numIdle++;
+ }
+ }
+
+ return res;
+ }
+
+ @Override
+ public void setAppIdleAsync(String packageName, boolean idle, int userId) {
+ if (packageName == null || !mAppIdleEnabled) return;
+
+ mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName)
+ .sendToTarget();
+ }
+
+ @Override
+ @StandbyBuckets public int getAppStandbyBucket(String packageName, int userId,
+ long elapsedRealtime, boolean shouldObfuscateInstantApps) {
+ if (!mAppIdleEnabled || (shouldObfuscateInstantApps
+ && mInjector.isPackageEphemeral(userId, packageName))) {
+ return STANDBY_BUCKET_ACTIVE;
+ }
+
+ synchronized (mAppIdleLock) {
+ return mAppIdleHistory.getAppStandbyBucket(packageName, userId, elapsedRealtime);
+ }
+ }
+
+ @Override
+ public List<AppStandbyInfo> getAppStandbyBuckets(int userId) {
+ synchronized (mAppIdleLock) {
+ return mAppIdleHistory.getAppStandbyBuckets(userId, mAppIdleEnabled);
+ }
+ }
+
+ @VisibleForTesting
+ void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
+ int reason, long elapsedRealtime) {
+ setAppStandbyBucket(packageName, userId, newBucket, reason, elapsedRealtime, false);
+ }
+
+ @Override
+ public void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
+ int reason, long elapsedRealtime, boolean resetTimeout) {
+ synchronized (mAppIdleLock) {
+ // If the package is not installed, don't allow the bucket to be set.
+ if (!mInjector.isPackageInstalled(packageName, 0, userId)) {
+ return;
+ }
+ AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName,
+ userId, elapsedRealtime);
+ boolean predicted = (reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED;
+
+ // Don't allow changing bucket if higher than ACTIVE
+ if (app.currentBucket < STANDBY_BUCKET_ACTIVE) return;
+
+ // Don't allow prediction to change from/to NEVER
+ if ((app.currentBucket == STANDBY_BUCKET_NEVER
+ || newBucket == STANDBY_BUCKET_NEVER)
+ && predicted) {
+ return;
+ }
+
+ // If the bucket was forced, don't allow prediction to override
+ if ((app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED && predicted) return;
+
+ // If the bucket is required to stay in a higher state for a specified duration, don't
+ // override unless the duration has passed
+ if (predicted) {
+ // Check if the app is within one of the timeouts for forced bucket elevation
+ final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
+ // In case of not using the prediction, just keep track of it for applying after
+ // ACTIVE or WORKING_SET timeout.
+ mAppIdleHistory.updateLastPrediction(app, elapsedTimeAdjusted, newBucket);
+
+ if (newBucket > STANDBY_BUCKET_ACTIVE
+ && app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
+ newBucket = STANDBY_BUCKET_ACTIVE;
+ reason = app.bucketingReason;
+ if (DEBUG) {
+ Slog.d(TAG, " Keeping at ACTIVE due to min timeout");
+ }
+ } else if (newBucket > STANDBY_BUCKET_WORKING_SET
+ && app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
+ newBucket = STANDBY_BUCKET_WORKING_SET;
+ if (app.currentBucket != newBucket) {
+ reason = REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
+ } else {
+ reason = app.bucketingReason;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, " Keeping at WORKING_SET due to min timeout");
+ }
+ }
+ }
+
+ mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket,
+ reason, resetTimeout);
+ }
+ maybeInformListeners(packageName, userId, elapsedRealtime, newBucket, reason, false);
+ }
+
+ @VisibleForTesting
+ boolean isActiveDeviceAdmin(String packageName, int userId) {
+ synchronized (mActiveAdminApps) {
+ final Set<String> adminPkgs = mActiveAdminApps.get(userId);
+ return adminPkgs != null && adminPkgs.contains(packageName);
+ }
+ }
+
+ @Override
+ public void addActiveDeviceAdmin(String adminPkg, int userId) {
+ synchronized (mActiveAdminApps) {
+ Set<String> adminPkgs = mActiveAdminApps.get(userId);
+ if (adminPkgs == null) {
+ adminPkgs = new ArraySet<>();
+ mActiveAdminApps.put(userId, adminPkgs);
+ }
+ adminPkgs.add(adminPkg);
+ }
+ }
+
+ @Override
+ public void setActiveAdminApps(Set<String> adminPkgs, int userId) {
+ synchronized (mActiveAdminApps) {
+ if (adminPkgs == null) {
+ mActiveAdminApps.remove(userId);
+ } else {
+ mActiveAdminApps.put(userId, adminPkgs);
+ }
+ }
+ }
+
+ @Override
+ public void onAdminDataAvailable() {
+ mAdminDataAvailableLatch.countDown();
+ }
+
+ /**
+ * This will only ever be called once - during device boot.
+ */
+ private void waitForAdminData() {
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) {
+ ConcurrentUtils.waitForCountDownNoInterrupt(mAdminDataAvailableLatch,
+ WAIT_FOR_ADMIN_DATA_TIMEOUT_MS, "Wait for admin data");
+ }
+ }
+
+ @VisibleForTesting
+ Set<String> getActiveAdminAppsForTest(int userId) {
+ synchronized (mActiveAdminApps) {
+ return mActiveAdminApps.get(userId);
+ }
+ }
+
+ /**
+ * Returns {@code true} if the supplied package is the device provisioning app. Otherwise,
+ * returns {@code false}.
+ */
+ private boolean isDeviceProvisioningPackage(String packageName) {
+ String deviceProvisioningPackage = mContext.getResources().getString(
+ com.android.internal.R.string.config_deviceProvisioningPackage);
+ return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName);
+ }
+
+ private boolean isCarrierApp(String packageName) {
+ synchronized (mAppIdleLock) {
+ if (!mHaveCarrierPrivilegedApps) {
+ fetchCarrierPrivilegedAppsLocked();
+ }
+ if (mCarrierPrivilegedApps != null) {
+ return mCarrierPrivilegedApps.contains(packageName);
+ }
+ return false;
+ }
+ }
+
+ @Override
+ public void clearCarrierPrivilegedApps() {
+ if (DEBUG) {
+ Slog.i(TAG, "Clearing carrier privileged apps list");
+ }
+ synchronized (mAppIdleLock) {
+ mHaveCarrierPrivilegedApps = false;
+ mCarrierPrivilegedApps = null; // Need to be refetched.
+ }
+ }
+
+ @GuardedBy("mAppIdleLock")
+ private void fetchCarrierPrivilegedAppsLocked() {
+ TelephonyManager telephonyManager =
+ mContext.getSystemService(TelephonyManager.class);
+ mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivilegesForAllPhones();
+ mHaveCarrierPrivilegedApps = true;
+ if (DEBUG) {
+ Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps);
+ }
+ }
+
+ private boolean isActiveNetworkScorer(String packageName) {
+ String activeScorer = mInjector.getActiveNetworkScorer();
+ return packageName != null && packageName.equals(activeScorer);
+ }
+
+ private void informListeners(String packageName, int userId, int bucket, int reason,
+ boolean userInteraction) {
+ final boolean idle = bucket >= STANDBY_BUCKET_RARE;
+ synchronized (mPackageAccessListeners) {
+ for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
+ listener.onAppIdleStateChanged(packageName, userId, idle, bucket, reason);
+ if (userInteraction) {
+ listener.onUserInteractionStarted(packageName, userId);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void flushToDisk(int userId) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.writeAppIdleTimes(userId);
+ }
+ }
+
+ @Override
+ public void flushDurationsToDisk() {
+ // Persist elapsed and screen on time. If this fails for whatever reason, the apps will be
+ // considered not-idle, which is the safest outcome in such an event.
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.writeAppIdleDurations();
+ }
+ }
+
+ private boolean isDisplayOn() {
+ return mInjector.isDefaultDisplayOn();
+ }
+
+ @VisibleForTesting
+ void clearAppIdleForPackage(String packageName, int userId) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.clearUsage(packageName, userId);
+ }
+ }
+
+ private class PackageReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (Intent.ACTION_PACKAGE_ADDED.equals(action)
+ || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
+ clearCarrierPrivilegedApps();
+ }
+ if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
+ Intent.ACTION_PACKAGE_ADDED.equals(action))
+ && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ clearAppIdleForPackage(intent.getData().getSchemeSpecificPart(),
+ getSendingUserId());
+ }
+ }
+ }
+
+ @Override
+ public void initializeDefaultsForSystemApps(int userId) {
+ if (!mSystemServicesReady) {
+ // Do it later, since SettingsProvider wasn't queried yet for app_standby_enabled
+ mPendingInitializeDefaults = true;
+ return;
+ }
+ Slog.d(TAG, "Initializing defaults for system apps on user " + userId + ", "
+ + "appIdleEnabled=" + mAppIdleEnabled);
+ final long elapsedRealtime = mInjector.elapsedRealtime();
+ List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
+ PackageManager.MATCH_DISABLED_COMPONENTS,
+ userId);
+ final int packageCount = packages.size();
+ synchronized (mAppIdleLock) {
+ for (int i = 0; i < packageCount; i++) {
+ final PackageInfo pi = packages.get(i);
+ String packageName = pi.packageName;
+ if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
+ // Mark app as used for 2 hours. After that it can timeout to whatever the
+ // past usage pattern was.
+ mAppIdleHistory.reportUsage(packageName, userId, STANDBY_BUCKET_ACTIVE,
+ REASON_SUB_USAGE_SYSTEM_UPDATE, 0,
+ elapsedRealtime + mSystemUpdateUsageTimeoutMillis);
+ }
+ }
+ // Immediately persist defaults to disk
+ mAppIdleHistory.writeAppIdleTimes(userId);
+ }
+ }
+
+ @Override
+ public void postReportContentProviderUsage(String name, String packageName, int userId) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = name;
+ args.arg2 = packageName;
+ args.arg3 = userId;
+ mHandler.obtainMessage(MSG_REPORT_CONTENT_PROVIDER_USAGE, args)
+ .sendToTarget();
+ }
+
+ @Override
+ public void postReportSyncScheduled(String packageName, int userId, boolean exempted) {
+ mHandler.obtainMessage(MSG_REPORT_SYNC_SCHEDULED, userId, exempted ? 1 : 0, packageName)
+ .sendToTarget();
+ }
+
+ @Override
+ public void postReportExemptedSyncStart(String packageName, int userId) {
+ mHandler.obtainMessage(MSG_REPORT_EXEMPTED_SYNC_START, userId, 0, packageName)
+ .sendToTarget();
+ }
+
+ @Override
+ public void dumpUser(IndentingPrintWriter idpw, int userId, String pkg) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.dump(idpw, userId, pkg);
+ }
+ }
+
+ @Override
+ public void dumpState(String[] args, PrintWriter pw) {
+ synchronized (mAppIdleLock) {
+ pw.println("Carrier privileged apps (have=" + mHaveCarrierPrivilegedApps
+ + "): " + mCarrierPrivilegedApps);
+ }
+
+ final long now = System.currentTimeMillis();
+
+ pw.println();
+ pw.println("Settings:");
+
+ pw.print(" mCheckIdleIntervalMillis=");
+ TimeUtils.formatDuration(mCheckIdleIntervalMillis, pw);
+ pw.println();
+
+ pw.print(" mStrongUsageTimeoutMillis=");
+ TimeUtils.formatDuration(mStrongUsageTimeoutMillis, pw);
+ pw.println();
+ pw.print(" mNotificationSeenTimeoutMillis=");
+ TimeUtils.formatDuration(mNotificationSeenTimeoutMillis, pw);
+ pw.println();
+ pw.print(" mSyncAdapterTimeoutMillis=");
+ TimeUtils.formatDuration(mSyncAdapterTimeoutMillis, pw);
+ pw.println();
+ pw.print(" mSystemInteractionTimeoutMillis=");
+ TimeUtils.formatDuration(mSystemInteractionTimeoutMillis, pw);
+ pw.println();
+ pw.print(" mInitialForegroundServiceStartTimeoutMillis=");
+ TimeUtils.formatDuration(mInitialForegroundServiceStartTimeoutMillis, pw);
+ pw.println();
+
+ pw.print(" mPredictionTimeoutMillis=");
+ TimeUtils.formatDuration(mPredictionTimeoutMillis, pw);
+ pw.println();
+
+ pw.print(" mExemptedSyncScheduledNonDozeTimeoutMillis=");
+ TimeUtils.formatDuration(mExemptedSyncScheduledNonDozeTimeoutMillis, pw);
+ pw.println();
+ pw.print(" mExemptedSyncScheduledDozeTimeoutMillis=");
+ TimeUtils.formatDuration(mExemptedSyncScheduledDozeTimeoutMillis, pw);
+ pw.println();
+ pw.print(" mExemptedSyncStartTimeoutMillis=");
+ TimeUtils.formatDuration(mExemptedSyncStartTimeoutMillis, pw);
+ pw.println();
+ pw.print(" mUnexemptedSyncScheduledTimeoutMillis=");
+ TimeUtils.formatDuration(mUnexemptedSyncScheduledTimeoutMillis, pw);
+ pw.println();
+
+ pw.print(" mSystemUpdateUsageTimeoutMillis=");
+ TimeUtils.formatDuration(mSystemUpdateUsageTimeoutMillis, pw);
+ pw.println();
+
+ pw.println();
+ pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
+ pw.println();
+ pw.print("mScreenThresholds="); pw.println(Arrays.toString(mAppStandbyScreenThresholds));
+ pw.print("mElapsedThresholds="); pw.println(Arrays.toString(mAppStandbyElapsedThresholds));
+ pw.println();
+ }
+
+ /**
+ * Injector for interaction with external code. Override methods to provide a mock
+ * implementation for tests.
+ * onBootPhase() must be called with at least the PHASE_SYSTEM_SERVICES_READY
+ */
+ static class Injector {
+
+ private final Context mContext;
+ private final Looper mLooper;
+ private IDeviceIdleController mDeviceIdleController;
+ private IBatteryStats mBatteryStats;
+ private PackageManagerInternal mPackageManagerInternal;
+ private DisplayManager mDisplayManager;
+ private PowerManager mPowerManager;
+ int mBootPhase;
+
+ Injector(Context context, Looper looper) {
+ mContext = context;
+ mLooper = looper;
+ }
+
+ Context getContext() {
+ return mContext;
+ }
+
+ Looper getLooper() {
+ return mLooper;
+ }
+
+ void onBootPhase(int phase) {
+ if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
+ mBatteryStats = IBatteryStats.Stub.asInterface(
+ ServiceManager.getService(BatteryStats.SERVICE_NAME));
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ mDisplayManager = (DisplayManager) mContext.getSystemService(
+ Context.DISPLAY_SERVICE);
+ mPowerManager = mContext.getSystemService(PowerManager.class);
+ }
+ mBootPhase = phase;
+ }
+
+ int getBootPhase() {
+ return mBootPhase;
+ }
+
+ /**
+ * Returns the elapsed realtime since the device started. Override this
+ * to control the clock.
+ * @return elapsed realtime
+ */
+ long elapsedRealtime() {
+ return SystemClock.elapsedRealtime();
+ }
+
+ long currentTimeMillis() {
+ return System.currentTimeMillis();
+ }
+
+ boolean isAppIdleEnabled() {
+ final boolean buildFlag = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_enableAutoPowerModes);
+ final boolean runtimeFlag = Global.getInt(mContext.getContentResolver(),
+ Global.APP_STANDBY_ENABLED, 1) == 1
+ && Global.getInt(mContext.getContentResolver(),
+ Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, 1) == 1;
+ return buildFlag && runtimeFlag;
+ }
+
+ boolean isPowerSaveWhitelistExceptIdleApp(String packageName) throws RemoteException {
+ return mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName);
+ }
+
+ File getDataSystemDirectory() {
+ return Environment.getDataSystemDirectory();
+ }
+
+ void noteEvent(int event, String packageName, int uid) throws RemoteException {
+ mBatteryStats.noteEvent(event, packageName, uid);
+ }
+
+ boolean isPackageEphemeral(int userId, String packageName) {
+ return mPackageManagerInternal.isPackageEphemeral(userId, packageName);
+ }
+
+ boolean isPackageInstalled(String packageName, int flags, int userId) {
+ return mPackageManagerInternal.getPackageUid(packageName, flags, userId) >= 0;
+ }
+
+ int[] getRunningUserIds() throws RemoteException {
+ return ActivityManager.getService().getRunningUserIds();
+ }
+
+ boolean isDefaultDisplayOn() {
+ return mDisplayManager
+ .getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON;
+ }
+
+ void registerDisplayListener(DisplayManager.DisplayListener listener, Handler handler) {
+ mDisplayManager.registerDisplayListener(listener, handler);
+ }
+
+ String getActiveNetworkScorer() {
+ NetworkScoreManager nsm = (NetworkScoreManager) mContext.getSystemService(
+ Context.NETWORK_SCORE_SERVICE);
+ return nsm.getActiveScorerPackage();
+ }
+
+ public boolean isBoundWidgetPackage(AppWidgetManager appWidgetManager, String packageName,
+ int userId) {
+ return appWidgetManager.isBoundWidgetPackage(packageName, userId);
+ }
+
+ String getAppIdleSettings() {
+ return Global.getString(mContext.getContentResolver(),
+ Global.APP_IDLE_CONSTANTS);
+ }
+
+ /** Whether the device is in doze or not. */
+ public boolean isDeviceIdleMode() {
+ return mPowerManager.isDeviceIdleMode();
+ }
+ }
+
+ class AppStandbyHandler extends Handler {
+
+ AppStandbyHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_INFORM_LISTENERS:
+ StandbyUpdateRecord r = (StandbyUpdateRecord) msg.obj;
+ informListeners(r.packageName, r.userId, r.bucket, r.reason,
+ r.isUserInteraction);
+ r.recycle();
+ break;
+
+ case MSG_FORCE_IDLE_STATE:
+ forceIdleState((String) msg.obj, msg.arg1, msg.arg2 == 1);
+ break;
+
+ case MSG_CHECK_IDLE_STATES:
+ if (checkIdleStates(msg.arg1) && mAppIdleEnabled) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ MSG_CHECK_IDLE_STATES, msg.arg1, 0),
+ mCheckIdleIntervalMillis);
+ }
+ break;
+
+ case MSG_ONE_TIME_CHECK_IDLE_STATES:
+ mHandler.removeMessages(MSG_ONE_TIME_CHECK_IDLE_STATES);
+ waitForAdminData();
+ checkIdleStates(UserHandle.USER_ALL);
+ break;
+
+ case MSG_REPORT_CONTENT_PROVIDER_USAGE:
+ SomeArgs args = (SomeArgs) msg.obj;
+ reportContentProviderUsage((String) args.arg1, // authority name
+ (String) args.arg2, // package name
+ (int) args.arg3); // userId
+ args.recycle();
+ break;
+
+ case MSG_CHECK_PACKAGE_IDLE_STATE:
+ checkAndUpdateStandbyState((String) msg.obj, msg.arg1, msg.arg2,
+ mInjector.elapsedRealtime());
+ break;
+
+ case MSG_REPORT_SYNC_SCHEDULED:
+ final boolean exempted = msg.arg1 > 0 ? true : false;
+ if (exempted) {
+ reportExemptedSyncScheduled((String) msg.obj, msg.arg1);
+ } else {
+ reportUnexemptedSyncScheduled((String) msg.obj, msg.arg1);
+ }
+ break;
+
+ case MSG_REPORT_EXEMPTED_SYNC_START:
+ reportExemptedSyncStart((String) msg.obj, msg.arg1);
+ break;
+
+ default:
+ super.handleMessage(msg);
+ break;
+
+ }
+ }
+ };
+
+ private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder().build();
+
+ private final ConnectivityManager.NetworkCallback mNetworkCallback
+ = new ConnectivityManager.NetworkCallback() {
+ @Override
+ public void onAvailable(Network network) {
+ mConnectivityManager.unregisterNetworkCallback(this);
+ }
+ };
+
+ private final DisplayManager.DisplayListener mDisplayListener
+ = new DisplayManager.DisplayListener() {
+
+ @Override public void onDisplayAdded(int displayId) {
+ }
+
+ @Override public void onDisplayRemoved(int displayId) {
+ }
+
+ @Override public void onDisplayChanged(int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ final boolean displayOn = isDisplayOn();
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.updateDisplay(displayOn, mInjector.elapsedRealtime());
+ }
+ }
+ }
+ };
+
+ /**
+ * Observe settings changes for {@link Global#APP_IDLE_CONSTANTS}.
+ */
+ private class SettingsObserver extends ContentObserver {
+ private static final String KEY_SCREEN_TIME_THRESHOLDS = "screen_thresholds";
+ private static final String KEY_ELAPSED_TIME_THRESHOLDS = "elapsed_thresholds";
+ private static final String KEY_STRONG_USAGE_HOLD_DURATION = "strong_usage_duration";
+ private static final String KEY_NOTIFICATION_SEEN_HOLD_DURATION =
+ "notification_seen_duration";
+ private static final String KEY_SYSTEM_UPDATE_HOLD_DURATION =
+ "system_update_usage_duration";
+ private static final String KEY_PREDICTION_TIMEOUT = "prediction_timeout";
+ private static final String KEY_SYNC_ADAPTER_HOLD_DURATION = "sync_adapter_duration";
+ private static final String KEY_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_HOLD_DURATION =
+ "exempted_sync_scheduled_nd_duration";
+ private static final String KEY_EXEMPTED_SYNC_SCHEDULED_DOZE_HOLD_DURATION =
+ "exempted_sync_scheduled_d_duration";
+ private static final String KEY_EXEMPTED_SYNC_START_HOLD_DURATION =
+ "exempted_sync_start_duration";
+ private static final String KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION =
+ "unexempted_sync_scheduled_duration";
+ private static final String KEY_SYSTEM_INTERACTION_HOLD_DURATION =
+ "system_interaction_duration";
+ private static final String KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION =
+ "initial_foreground_service_start_duration";
+ public static final long DEFAULT_STRONG_USAGE_TIMEOUT = 1 * ONE_HOUR;
+ public static final long DEFAULT_NOTIFICATION_TIMEOUT = 12 * ONE_HOUR;
+ public static final long DEFAULT_SYSTEM_UPDATE_TIMEOUT = 2 * ONE_HOUR;
+ public static final long DEFAULT_SYSTEM_INTERACTION_TIMEOUT = 10 * ONE_MINUTE;
+ public static final long DEFAULT_SYNC_ADAPTER_TIMEOUT = 10 * ONE_MINUTE;
+ public static final long DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT = 10 * ONE_MINUTE;
+ public static final long DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT = 4 * ONE_HOUR;
+ public static final long DEFAULT_EXEMPTED_SYNC_START_TIMEOUT = 10 * ONE_MINUTE;
+ public static final long DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT = 10 * ONE_MINUTE;
+ public static final long DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT = 30 * ONE_MINUTE;
+
+ private final KeyValueListParser mParser = new KeyValueListParser(',');
+
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ void registerObserver() {
+ final ContentResolver cr = mContext.getContentResolver();
+ cr.registerContentObserver(Global.getUriFor(Global.APP_IDLE_CONSTANTS), false, this);
+ cr.registerContentObserver(Global.getUriFor(Global.APP_STANDBY_ENABLED), false, this);
+ cr.registerContentObserver(Global.getUriFor(Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED),
+ false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ updateSettings();
+ postOneTimeCheckIdleStates();
+ }
+
+ void updateSettings() {
+ if (DEBUG) {
+ Slog.d(TAG,
+ "appidle=" + Global.getString(mContext.getContentResolver(),
+ Global.APP_STANDBY_ENABLED));
+ Slog.d(TAG,
+ "adaptivebat=" + Global.getString(mContext.getContentResolver(),
+ Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED));
+ Slog.d(TAG, "appidleconstants=" + Global.getString(
+ mContext.getContentResolver(),
+ Global.APP_IDLE_CONSTANTS));
+ }
+
+ // Look at global settings for this.
+ // TODO: Maybe apply different thresholds for different users.
+ try {
+ mParser.setString(mInjector.getAppIdleSettings());
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage());
+ // fallthrough, mParser is empty and all defaults will be returned.
+ }
+
+ synchronized (mAppIdleLock) {
+
+ String screenThresholdsValue = mParser.getString(KEY_SCREEN_TIME_THRESHOLDS, null);
+ mAppStandbyScreenThresholds = parseLongArray(screenThresholdsValue,
+ SCREEN_TIME_THRESHOLDS);
+
+ String elapsedThresholdsValue = mParser.getString(KEY_ELAPSED_TIME_THRESHOLDS,
+ null);
+ mAppStandbyElapsedThresholds = parseLongArray(elapsedThresholdsValue,
+ ELAPSED_TIME_THRESHOLDS);
+ mCheckIdleIntervalMillis = Math.min(mAppStandbyElapsedThresholds[1] / 4,
+ COMPRESS_TIME ? ONE_MINUTE : 4 * 60 * ONE_MINUTE); // 4 hours
+ mStrongUsageTimeoutMillis = mParser.getDurationMillis(
+ KEY_STRONG_USAGE_HOLD_DURATION,
+ COMPRESS_TIME ? ONE_MINUTE : DEFAULT_STRONG_USAGE_TIMEOUT);
+ mNotificationSeenTimeoutMillis = mParser.getDurationMillis(
+ KEY_NOTIFICATION_SEEN_HOLD_DURATION,
+ COMPRESS_TIME ? 12 * ONE_MINUTE : DEFAULT_NOTIFICATION_TIMEOUT);
+ mSystemUpdateUsageTimeoutMillis = mParser.getDurationMillis(
+ KEY_SYSTEM_UPDATE_HOLD_DURATION,
+ COMPRESS_TIME ? 2 * ONE_MINUTE : DEFAULT_SYSTEM_UPDATE_TIMEOUT);
+ mPredictionTimeoutMillis = mParser.getDurationMillis(
+ KEY_PREDICTION_TIMEOUT,
+ COMPRESS_TIME ? 10 * ONE_MINUTE : DEFAULT_PREDICTION_TIMEOUT);
+ mSyncAdapterTimeoutMillis = mParser.getDurationMillis(
+ KEY_SYNC_ADAPTER_HOLD_DURATION,
+ COMPRESS_TIME ? ONE_MINUTE : DEFAULT_SYNC_ADAPTER_TIMEOUT);
+
+ mExemptedSyncScheduledNonDozeTimeoutMillis = mParser.getDurationMillis(
+ KEY_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_HOLD_DURATION,
+ COMPRESS_TIME ? (ONE_MINUTE / 2)
+ : DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT);
+
+ mExemptedSyncScheduledDozeTimeoutMillis = mParser.getDurationMillis(
+ KEY_EXEMPTED_SYNC_SCHEDULED_DOZE_HOLD_DURATION,
+ COMPRESS_TIME ? ONE_MINUTE
+ : DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT);
+
+ mExemptedSyncStartTimeoutMillis = mParser.getDurationMillis(
+ KEY_EXEMPTED_SYNC_START_HOLD_DURATION,
+ COMPRESS_TIME ? ONE_MINUTE
+ : DEFAULT_EXEMPTED_SYNC_START_TIMEOUT);
+
+ mUnexemptedSyncScheduledTimeoutMillis = mParser.getDurationMillis(
+ KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION,
+ COMPRESS_TIME ? ONE_MINUTE
+ : DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT); // TODO
+
+ mSystemInteractionTimeoutMillis = mParser.getDurationMillis(
+ KEY_SYSTEM_INTERACTION_HOLD_DURATION,
+ COMPRESS_TIME ? ONE_MINUTE : DEFAULT_SYSTEM_INTERACTION_TIMEOUT);
+
+ mInitialForegroundServiceStartTimeoutMillis = mParser.getDurationMillis(
+ KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION,
+ COMPRESS_TIME ? ONE_MINUTE :
+ DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT);
+ }
+
+ // Check if app_idle_enabled has changed. Do this after getting the rest of the settings
+ // in case we need to change something based on the new values.
+ setAppIdleEnabled(mInjector.isAppIdleEnabled());
+ }
+
+ long[] parseLongArray(String values, long[] defaults) {
+ if (values == null) return defaults;
+ if (values.isEmpty()) {
+ // Reset to defaults
+ return defaults;
+ } else {
+ String[] thresholds = values.split("/");
+ if (thresholds.length == THRESHOLD_BUCKETS.length) {
+ long[] array = new long[THRESHOLD_BUCKETS.length];
+ for (int i = 0; i < THRESHOLD_BUCKETS.length; i++) {
+ try {
+ if (thresholds[i].startsWith("P") || thresholds[i].startsWith("p")) {
+ array[i] = Duration.parse(thresholds[i]).toMillis();
+ } else {
+ array[i] = Long.parseLong(thresholds[i]);
+ }
+ } catch (NumberFormatException|DateTimeParseException e) {
+ return defaults;
+ }
+ }
+ return array;
+ } else {
+ return defaults;
+ }
+ }
+ }
+ }
+}
diff --git a/apex/permission/Android.bp b/apex/permission/Android.bp
new file mode 100644
index 000000000000..027481401557
--- /dev/null
+++ b/apex/permission/Android.bp
@@ -0,0 +1,33 @@
+// 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.
+
+apex {
+ name: "com.android.permission",
+
+ manifest: "apex_manifest.json",
+
+ key: "com.android.permission.key",
+ certificate: ":com.android.permission.certificate",
+}
+
+apex_key {
+ name: "com.android.permission.key",
+ public_key: "com.android.permission.avbpubkey",
+ private_key: "com.android.permission.pem",
+}
+
+android_app_certificate {
+ name: "com.android.permission.certificate",
+ certificate: "com.android.permission",
+}
diff --git a/apex/permission/OWNERS b/apex/permission/OWNERS
new file mode 100644
index 000000000000..957e10a582a0
--- /dev/null
+++ b/apex/permission/OWNERS
@@ -0,0 +1,6 @@
+svetoslavganov@google.com
+moltmann@google.com
+eugenesusla@google.com
+zhanghai@google.com
+evanseverson@google.com
+ntmyren@google.com
diff --git a/apex/permission/apex_manifest.json b/apex/permission/apex_manifest.json
new file mode 100644
index 000000000000..2a8c4f737dff
--- /dev/null
+++ b/apex/permission/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+ "name": "com.android.permission",
+ "version": 1
+}
diff --git a/apex/permission/com.android.permission.avbpubkey b/apex/permission/com.android.permission.avbpubkey
new file mode 100644
index 000000000000..9eaf85259637
--- /dev/null
+++ b/apex/permission/com.android.permission.avbpubkey
Binary files differ
diff --git a/apex/permission/com.android.permission.pem b/apex/permission/com.android.permission.pem
new file mode 100644
index 000000000000..3d584be5440d
--- /dev/null
+++ b/apex/permission/com.android.permission.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKgIBAAKCAgEA6snt4eqoz85xiL9Sf6w1S1b9FgSHK05zYTh2JYPvQKQ3yeZp
+E6avJ6FN6XcbmkDzSd658BvUGDBSPhOlzuUO4BsoKBuLMxP6TxIQXFKidzDqY0vQ
+4qkS++bdIhUjwBP3OSZ3Czu0BiihK8GC75Abr//EyCyObGIGGfHEGANiOgrpP4X5
++OmLzQLCjk4iE1kg+U6cRSRI/XLaoWC0TvIIuzxznrQ6r5GmzgTOwyBWyIB+bj73
+bmsweHTU+w9Y7kGOx4hO3XCLIhoBWEw0EbuW9nZmQ4sZls5Jo/CbyJlCclF11yVo
+SCf2LG/T+9pah5NOmDQ1kPbU+0iKZIV4YFHGTIhyGDE/aPOuUT05ziCGDifgHr0u
+SG1x/RLqsVh/POvNxnvP9cQFMQ08BvbEJaTTgB785iwKsvdqCfmng/SAyxSetmzP
+StXVB3fh1OoZ8vunRbQYxnmUxycVqaA96zmBx2wLvbvzKo7pZFDE6nbhnT5+MRAM
+z/VIK89W26uB4gj8sBFslqZjT0jPqsAZuvDm7swOtMwIcEolyGJuFLqlhN7UwMz2
+9y8+IpYixR+HvD1TZI9NtmuCmv3kPrWgoMZg6yvaBayTIr8RdYzi6FO/C1lLiraz
+48dH3sXWRa8cgw6VcSUwYrEBIc3sotdsupO1iOjcFybIwaee0YTZJfjvbqkCAwEA
+AQKCAgEArRnfdpaJi1xLPGTCMDsIt9kUku0XswgN7PmxsYsKFAB+2S40/jYAIRm9
+1YjpItsMA8RgFfSOdJ77o6TctCMQyo17F8bm4+uwuic5RLfv7Cx2QmsdQF8jDfFx
+y7UGPJD7znjbf76uxXOjEB2FqZX3s9TAgkzHXIUQtoQW7RVhkCWHPjxKxgd5+NY2
+FrDoUpd9xhD9CcTsw1+wbRZdGW88nL6/B50dP2AFORM2VYo8MWr6y9FEn3YLsGOC
+uu7fxBk1aUrHyl81VRkTMMROB1zkuiUk1FtzrEm+5U15rXXBFYOVe9+qeLhtuOlh
+wueDoz0pzvF/JLe24uTik6YL0Ae6SD0pFXQ2KDrdH3cUHLok3r76/yGzaDNTFjS2
+2WbQ8dEJV8veNHk8gjGpFTJIsBUlcZpmUCDHlfvVMb3+2ahQ+28piQUt5t3zqJdZ
+NDqsOHzY6BRPc+Wm85Xii/lWiQceZSee/b1Enu+nchsyXhSenBfC6bIGZReyMI0K
+KKKuVhyR6OSOiR5ZdZ/NyXGqsWy05fn/h0X9hnpETsNaNYNKWvpHLfKll+STJpf7
+AZquJPIclQyiq5NONx6kfPztoCLkKV/zOgIj3Sx5oSZq+5gpO91nXWVwkTbqK1d1
+004q2Mah6UQyAk1XGQc2pHx7ouVcWawjU30vZ4C015Hv2lm/gVkCggEBAPltATYS
+OqOSL1YAtIHPiHxMjNAgUdglq8JiJFXVfkocGU9eNub3Ed3sSWu6GB9Myu/sSKje
+bJ5DZqxJnvB2Fqmu9I9OunLGFSD0aXs4prwsQ1Rm5FcbImtrxcciASdkoo8Pj0z4
+vk2r2NZD3VtER5Uh+YjSDkxcS9gBStXUpCL6gj69UpOxMmWqZVjyHatVB4lEvYJl
+N82uT7N7QVNL1DzcZ9z4C4r7ks1Pm7ka12s5m/oaAlAMdVeofiPJe1xA9zRToSr4
+tIbMkOeXFLVRLuji/7XsOgal5Rl59p+OwLshX5cswPVOMrH6zt+hbsJ5q8M5dqnX
+VAOBK7KNQ/EKZwcCggEBAPD6KVvyCim46n5EbcEqCkO7gevwZkw/9vLwmM5YsxTh
+z9FQkPO0iB7mwbX8w04I91Pre4NdfcgMG0pP1b13Sb4KHBchqW1a+TCs3kSGC6gn
+1SxmXHnA9jRxAkrWlGkoAQEz+aP61cXiiy2tXpQwJ8xQCKprfoqWZwhkCtEVU6CE
+S7v9cscOHIqgNxx4WoceMmq4EoihHAZzHxTcNVbByckMjb2XQJ0iNw3lDP4ddvc+
+a4HzHfHkhzeQ5ZNc8SvWU8z80aSCOKRsSD3aUTZzxhZ4O2tZSW7v7p+FpvVee7bC
+g8YCfszTdpVUMlLRLjScimAcovcFLSvtyupinxWg4M8CggEAN9YGEmOsSte7zwXj
+YrfhtumwEBtcFwX/2Ej+F1Tuq4p0xAa0RaoDjumJWhtTsRYQy/raHSuFpzwxbNoi
+QXQ+CIhI6RfXtz/OlQ0B2/rHoJJMFEXgUfuaDfAXW0eqeHYXyezSyIlamKqipPyW
+Pgsf9yue39keKEv1EorfhNTQVaA8rezV4oglXwrxGyNALw2e3UTNI7ai8mFWKDis
+XAg6n9E7UwUYGGnO6DUtCBgRJ0jDOQ6/e8n+LrxiWIKPIgzNCiK6jpMUXqTGv4Fb
+umdNGAdQ9RnHt5tFmRlrczaSwJFtA7uaCpAR2zPpQbiywchZAiAIB2dTwGEXNiZX
+kksg2wKCAQEA6pNad3qhkgPDoK6T+Jkn7M82paoaqtcJWWwEE7oceZNnbWZz9Agl
+CY+vuawXonrv5+0vCq2Tp4zBdBFLC2h3jFrjBVFrUFxifpOIukOSTVqZFON/2bWQ
+9XOcu6UuSz7522Xw+UNPnZXtzcUacD6AP08ZYGvLfrTyDyTzspyED5k48ALEHCkM
+d5WGkFxII4etpF0TDZVnZo/iDbhe49k4yFFEGO6Ho26PESOLBkNAb2V/2bwDxlij
+l9+g21Z6HiZA5SamHPH2mXgeyrcen1cL2QupK9J6vVcqfnboE6qp2zp2c+Yx8MlY
+gfy4EA44YFaSDQVTTgrn8f9Eq+zc130H2QKCAQEAqOKgv68nIPdDSngNyCVyWego
+boFiDaEJoBBg8FrBjTJ6wFLrNAnXmbvfTtgNmNAzF1cUPJZlIIsHgGrMCfpehbXq
+WQQIw+E+yFbTGLxseGRfsLrV0CsgnAoOVeod+yIHmqc3livaUbrWhL1V2f6Ue+sE
+7YLp/iP43NaMfA4kYk2ep7+ZJoEVkCjHJJaHWgAG3RynPJHkTJlSgu7wLYvGc9uE
+ZsEFUM46lX02t7rrtMfasVGrUy1c2xOxFb4v1vG6iEZ7+YWeq5o3AkxUwEGn+mG4
+/3p+k4AaTXJDXgyZ0Sv6CkGuPHenAYG4cswcUUEf/G4Ag77x6LBNMgycJBxUJA==
+-----END RSA PRIVATE KEY-----
diff --git a/apex/permission/com.android.permission.pk8 b/apex/permission/com.android.permission.pk8
new file mode 100644
index 000000000000..d51673dbc2fc
--- /dev/null
+++ b/apex/permission/com.android.permission.pk8
Binary files differ
diff --git a/apex/permission/com.android.permission.x509.pem b/apex/permission/com.android.permission.x509.pem
new file mode 100644
index 000000000000..4b146c9edd4f
--- /dev/null
+++ b/apex/permission/com.android.permission.x509.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGKzCCBBOgAwIBAgIUezo3fQeVZsmLpm/dkpGWJ/G/MN8wDQYJKoZIhvcNAQEL
+BQAwgaMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
+DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
+b2lkMR8wHQYDVQQDDBZjb20uYW5kcm9pZC5wZXJtaXNzaW9uMSIwIAYJKoZIhvcN
+AQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMCAXDTE5MTAwOTIxMzExOVoYDzQ3NTcw
+OTA0MjEzMTE5WjCBozELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx
+FjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB0FuZHJvaWQxEDAOBgNV
+BAsMB0FuZHJvaWQxHzAdBgNVBAMMFmNvbS5hbmRyb2lkLnBlcm1pc3Npb24xIjAg
+BgkqhkiG9w0BCQEWE2FuZHJvaWRAYW5kcm9pZC5jb20wggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQCxefguRJ7E6tBCTEOeU2HJEGs6AQQapLz9hMed0aaJ
+Qr7aTQiYJEk+sG4+jPYbjpxa8JDDzJHp+4g7DjfSb+dvT9n84A8lWaI/yRXTZTQN
+Hu5m/bgHhi0LbySpiaFyodXBKUAnOhZyGPtYjtBFywFylueub8ryc1Z6UxxU7udH
+1mkIr7sE48Qkq5SyjFROE96iFmYA+vS/JXOfS0NBHiMB4GBxx4V7kXpvrTI7hhZG
+HiyhKvNh7wyHIhO9nDEw1rwtAH6CsL3YkQEVBeAU98m+0Au+qStLYkKHh2l8zT4W
+7sVK1VSqfB+VqOUmeIGdzlBfqMsoXD+FJz6KnIdUHIwjFDjL7Xr+hd+7xve+Q3S+
+U3Blk/U6atY8PM09wNfilG+SvwcKk5IgriDcu3rWKgIFxbUUaxLrDW7pLlu6wt/d
+GGtKK+Bc0jF+9Z901Tl33i5xhc5yOktT0btkKs7lSeE6VzP/Nk5g0SuzixmuRoh9
+f5Ge41N2ZCEHNXx3wZeVZwHIIPfYrL7Yql1Xoxbfs4ETFk6ChzVQcvjfDQQuK58J
+uNc+TOCoI/qflXwGCwpuHl0ier8V5Z4tpMUl5rWyVR/QGRtLPvs2lLuxczDw1OXq
+wEVtCMn9aNnd4y7R9PZ52hi53HAvDjpWefrLYi+Q04J6iGFQ1qAFBClK9DquBvmR
+swIDAQABo1MwUTAdBgNVHQ4EFgQULpfus5s5SrqLkoUKyPXA0D1iHPMwHwYDVR0j
+BBgwFoAULpfus5s5SrqLkoUKyPXA0D1iHPMwDwYDVR0TAQH/BAUwAwEB/zANBgkq
+hkiG9w0BAQsFAAOCAgEAjxQG5EFv8V/9yV2glI53VOmlWMjfEgvUjd39s/XLyPlr
+OzPOKSB0NFo8To3l4l+MsManxPK8y0OyfEVKbWVz9onv0ovo5MVokBmV/2G0jmsV
+B4e9yjOq+DmqIvY/Qh63Ywb97sTgcFI8620MhQDbh2IpEGv4ZNV0H6rgXmgdSCBw
+1EjBoYfFpN5aMgZjeyzZcq+d1IapdWqdhuEJQkMvoYS4WIumNIJlEXPQRoq/F5Ih
+nszdbKI/jVyiGFa2oeZ3rja1Y6GCRU8TYEoKx1pjS8uQDOEDTwsG/QnUe9peEj0V
+SsCkIidJWTomAmq9Tub9vpBe1zuTpuRAwxwR0qwgSxozV1Mvow1dJ19oFtHX0yD6
+ZjCpRn5PW9kMvSWSlrcrFs1NJf0j1Cvf7bHpkEDqLqpMnnh9jaFQq3nzDY+MWcIR
+jDcgQpI+AiE2/qtauZnFEVhbce49nCnk9+5bpTTIZJdzqeaExe5KXHwEtZLaEDh4
+atLY9LuEvPsjmDIMOR6hycD9FvwGXhJOQBjESIWFwigtSb1Yud9n6201jw3MLJ4k
++WhkbmZgWy+xc+Mdm5H3XyB1lvHaHGkxu+QB9KyQuVQKwbUVcbwZIfTFPN6Zr/dS
+ZXJqAbBhG/dBgF0LazuLaPVpibi+a3Y+tb9b8eXGkz4F97PWZIEDkELQ+9KOvhc=
+-----END CERTIFICATE-----
diff --git a/apex/statsd/.clang-format b/apex/statsd/.clang-format
new file mode 100644
index 000000000000..cead3a079435
--- /dev/null
+++ b/apex/statsd/.clang-format
@@ -0,0 +1,17 @@
+BasedOnStyle: Google
+AllowShortIfStatementsOnASingleLine: true
+AllowShortFunctionsOnASingleLine: false
+AllowShortLoopsOnASingleLine: true
+BinPackArguments: true
+BinPackParameters: true
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+ContinuationIndentWidth: 8
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+AccessModifierOffset: -4
+IncludeCategories:
+ - Regex: '^"Log\.h"'
+ Priority: -1
diff --git a/apex/statsd/OWNERS b/apex/statsd/OWNERS
new file mode 100644
index 000000000000..bed9600bc955
--- /dev/null
+++ b/apex/statsd/OWNERS
@@ -0,0 +1,9 @@
+jeffreyhuang@google.com
+joeo@google.com
+jtnguyen@google.com
+muhammadq@google.com
+ruchirr@google.com
+singhtejinder@google.com
+tsaichristine@google.com
+yaochen@google.com
+yro@google.com \ No newline at end of file
diff --git a/apex/statsd/service/Android.bp b/apex/statsd/service/Android.bp
new file mode 100644
index 000000000000..786e8b0260f8
--- /dev/null
+++ b/apex/statsd/service/Android.bp
@@ -0,0 +1,16 @@
+// Statsd Service jar, which will eventually be put in the statsd mainline apex.
+// statsd-service needs to be added to PRODUCT_SYSTEM_SERVER_JARS.
+// This jar will contain StatsCompanionService
+java_library {
+ name: "statsd-service",
+ installable: true,
+
+ srcs: [
+ "java/**/*.java",
+ ],
+
+ libs: [
+ "framework",
+ "services.core",
+ ],
+} \ No newline at end of file
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
new file mode 100644
index 000000000000..1a10753d7bcb
--- /dev/null
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -0,0 +1,2762 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.stats;
+
+import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED;
+import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
+import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
+import static android.os.Process.getPidsForCommands;
+import static android.os.Process.getUidForPid;
+import static android.os.storage.VolumeInfo.TYPE_PRIVATE;
+import static android.os.storage.VolumeInfo.TYPE_PUBLIC;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
+import static com.android.server.stats.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
+import static com.android.server.stats.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
+import static com.android.server.stats.ProcfsMemoryUtil.readCmdlineFromProcfs;
+import static com.android.server.stats.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManagerInternal;
+import android.app.AlarmManager;
+import android.app.AlarmManager.OnAlarmListener;
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.HistoricalOps;
+import android.app.AppOpsManager.HistoricalOpsRequest;
+import android.app.AppOpsManager.HistoricalPackageOps;
+import android.app.AppOpsManager.HistoricalUidOps;
+import android.app.ProcessMemoryState;
+import android.app.StatsManager;
+import android.bluetooth.BluetoothActivityEnergyInfo;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.UidTraffic;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
+import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
+import android.net.ConnectivityManager;
+import android.net.INetworkStatsService;
+import android.net.Network;
+import android.net.NetworkRequest;
+import android.net.NetworkStats;
+import android.net.wifi.IWifiManager;
+import android.net.wifi.WifiActivityEnergyInfo;
+import android.os.BatteryStats;
+import android.os.BatteryStatsInternal;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.CoolingDevice;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.IStatsCompanionService;
+import android.os.IStatsManager;
+import android.os.IStoraged;
+import android.os.IThermalEventListener;
+import android.os.IThermalService;
+import android.os.Looper;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.StatFs;
+import android.os.StatsDimensionsValue;
+import android.os.StatsLogEventWrapper;
+import android.os.SynchronousResultReceiver;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.Temperature;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.storage.DiskInfo;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.provider.Settings;
+import android.stats.storage.StorageEnums;
+import android.telephony.ModemActivityInfo;
+import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+import android.util.StatsLog;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.procstats.IProcessStats;
+import com.android.internal.app.procstats.ProcessStats;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatteryStatsHelper;
+import com.android.internal.os.BinderCallsStats.ExportedCallStat;
+import com.android.internal.os.KernelCpuSpeedReader;
+import com.android.internal.os.KernelCpuThreadReader;
+import com.android.internal.os.KernelCpuThreadReaderDiff;
+import com.android.internal.os.KernelCpuThreadReaderSettingsObserver;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
+import com.android.internal.os.KernelWakelockReader;
+import com.android.internal.os.KernelWakelockStats;
+import com.android.internal.os.LooperStats;
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.ProcessCpuTracker;
+import com.android.internal.os.StoragedUidIoStatsReader;
+import com.android.internal.util.DumpUtils;
+import com.android.server.BinderCallsStatsService;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.SystemServiceManager;
+import com.android.server.am.MemoryStatUtil.MemoryStat;
+import com.android.server.role.RoleManagerInternal;
+import com.android.server.stats.IonMemoryUtil.IonAllocations;
+import com.android.server.stats.ProcfsMemoryUtil.MemorySnapshot;
+import com.android.server.storage.DiskStatsFileLogger;
+import com.android.server.storage.DiskStatsLoggingService;
+
+import libcore.io.IoUtils;
+
+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.InputStream;
+import java.io.PrintWriter;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Helper service for statsd (the native stats management service in cmds/statsd/).
+ * Used for registering and receiving alarms on behalf of statsd.
+ *
+ * @hide
+ */
+public class StatsCompanionService extends IStatsCompanionService.Stub {
+ /**
+ * How long to wait on an individual subsystem to return its stats.
+ */
+ private static final long EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS = 2000;
+ private static final long MILLIS_IN_A_DAY = TimeUnit.DAYS.toMillis(1);
+
+ public static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity";
+ public static final String CONFIG_DIR = "/data/misc/stats-service";
+
+ static final String TAG = "StatsCompanionService";
+ static final boolean DEBUG = false;
+ /**
+ * Hard coded field ids of frameworks/base/cmds/statsd/src/uid_data.proto
+ * to be used in ProtoOutputStream.
+ */
+ private static final int APPLICATION_INFO_FIELD_ID = 1;
+ private static final int UID_FIELD_ID = 1;
+ private static final int VERSION_FIELD_ID = 2;
+ private static final int VERSION_STRING_FIELD_ID = 3;
+ private static final int PACKAGE_NAME_FIELD_ID = 4;
+ private static final int INSTALLER_FIELD_ID = 5;
+
+ public static final int CODE_DATA_BROADCAST = 1;
+ public static final int CODE_SUBSCRIBER_BROADCAST = 1;
+ public static final int CODE_ACTIVE_CONFIGS_BROADCAST = 1;
+ /**
+ * The last report time is provided with each intent registered to
+ * StatsManager#setFetchReportsOperation. This allows easy de-duping in the receiver if
+ * statsd is requesting the client to retrieve the same statsd data. The last report time
+ * corresponds to the last_report_elapsed_nanos that will provided in the current
+ * ConfigMetricsReport, and this timestamp also corresponds to the
+ * current_report_elapsed_nanos of the most recently obtained ConfigMetricsReport.
+ */
+ public static final String EXTRA_LAST_REPORT_TIME = "android.app.extra.LAST_REPORT_TIME";
+ public static final int DEATH_THRESHOLD = 10;
+ /**
+ * Which native processes to snapshot memory for.
+ *
+ * <p>Processes are matched by their cmdline in procfs. Example: cat /proc/pid/cmdline returns
+ * /system/bin/statsd for the stats daemon.
+ */
+ private static final String[] MEMORY_INTERESTING_NATIVE_PROCESSES = new String[]{
+ "/system/bin/statsd", // Stats daemon.
+ "/system/bin/surfaceflinger",
+ "/system/bin/apexd", // APEX daemon.
+ "/system/bin/audioserver",
+ "/system/bin/cameraserver",
+ "/system/bin/drmserver",
+ "/system/bin/healthd",
+ "/system/bin/incidentd",
+ "/system/bin/installd",
+ "/system/bin/lmkd", // Low memory killer daemon.
+ "/system/bin/logd",
+ "media.codec",
+ "media.extractor",
+ "media.metrics",
+ "/system/bin/mediadrmserver",
+ "/system/bin/mediaserver",
+ "/system/bin/performanced",
+ "/system/bin/tombstoned",
+ "/system/bin/traced", // Perfetto.
+ "/system/bin/traced_probes", // Perfetto.
+ "webview_zygote",
+ "zygote",
+ "zygote64",
+ };
+ /**
+ * Lowest available uid for apps.
+ *
+ * <p>Used to quickly discard memory snapshots of the zygote forks from native process
+ * measurements.
+ */
+ private static final int MIN_APP_UID = 10_000;
+
+ private static final int CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES = 8;
+
+ static final class CompanionHandler extends Handler {
+ CompanionHandler(Looper looper) {
+ super(looper);
+ }
+ }
+
+ private final Context mContext;
+ private final AlarmManager mAlarmManager;
+ private final INetworkStatsService mNetworkStatsService;
+ @GuardedBy("sStatsdLock")
+ private static IStatsManager sStatsd;
+ private static final Object sStatsdLock = new Object();
+
+ private final OnAlarmListener mAnomalyAlarmListener = new AnomalyAlarmListener();
+ private final OnAlarmListener mPullingAlarmListener = new PullingAlarmListener();
+ private final OnAlarmListener mPeriodicAlarmListener = new PeriodicAlarmListener();
+ private final BroadcastReceiver mAppUpdateReceiver;
+ private final BroadcastReceiver mUserUpdateReceiver;
+ private final ShutdownEventReceiver mShutdownEventReceiver;
+ private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
+ private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
+ private IWifiManager mWifiManager = null;
+ private TelephonyManager mTelephony = null;
+ @GuardedBy("sStatsdLock")
+ private final HashSet<Long> mDeathTimeMillis = new HashSet<>();
+ @GuardedBy("sStatsdLock")
+ private final HashMap<Long, String> mDeletedFiles = new HashMap<>();
+ private final CompanionHandler mHandler;
+
+ // Disables throttler on CPU time readers.
+ private KernelCpuUidUserSysTimeReader mCpuUidUserSysTimeReader =
+ new KernelCpuUidUserSysTimeReader(false);
+ private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
+ private KernelCpuUidFreqTimeReader mCpuUidFreqTimeReader =
+ new KernelCpuUidFreqTimeReader(false);
+ private KernelCpuUidActiveTimeReader mCpuUidActiveTimeReader =
+ new KernelCpuUidActiveTimeReader(false);
+ private KernelCpuUidClusterTimeReader mCpuUidClusterTimeReader =
+ new KernelCpuUidClusterTimeReader(false);
+ private StoragedUidIoStatsReader mStoragedUidIoStatsReader =
+ new StoragedUidIoStatsReader();
+ @Nullable
+ private final KernelCpuThreadReaderDiff mKernelCpuThreadReader;
+
+ private long mDebugElapsedClockPreviousValue = 0;
+ private long mDebugElapsedClockPullCount = 0;
+ private long mDebugFailingElapsedClockPreviousValue = 0;
+ private long mDebugFailingElapsedClockPullCount = 0;
+ private BatteryStatsHelper mBatteryStatsHelper = null;
+ private static final int MAX_BATTERY_STATS_HELPER_FREQUENCY_MS = 1000;
+ private long mBatteryStatsHelperTimestampMs = -MAX_BATTERY_STATS_HELPER_FREQUENCY_MS;
+
+ private static IThermalService sThermalService;
+ private File mBaseDir =
+ new File(SystemServiceManager.ensureSystemDir(), "stats_companion");
+ @GuardedBy("this")
+ ProcessCpuTracker mProcessCpuTracker = null;
+
+ public StatsCompanionService(Context context) {
+ super();
+ mContext = context;
+ mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ mNetworkStatsService = INetworkStatsService.Stub.asInterface(
+ ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+ mBaseDir.mkdirs();
+ mAppUpdateReceiver = new AppUpdateReceiver();
+ mUserUpdateReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (sStatsdLock) {
+ sStatsd = fetchStatsdService();
+ if (sStatsd == null) {
+ Slog.w(TAG, "Could not access statsd for UserUpdateReceiver");
+ return;
+ }
+ try {
+ // Pull the latest state of UID->app name, version mapping.
+ // Needed since the new user basically has a version of every app.
+ informAllUidsLocked(context);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to inform statsd latest update of all apps", e);
+ forgetEverythingLocked();
+ }
+ }
+ }
+ };
+ mShutdownEventReceiver = new ShutdownEventReceiver();
+ if (DEBUG) Slog.d(TAG, "Registered receiver for ACTION_PACKAGE_REPLACED and ADDED.");
+ PowerProfile powerProfile = new PowerProfile(context);
+ final int numClusters = powerProfile.getNumCpuClusters();
+ mKernelCpuSpeedReaders = new KernelCpuSpeedReader[numClusters];
+ int firstCpuOfCluster = 0;
+ for (int i = 0; i < numClusters; i++) {
+ final int numSpeedSteps = powerProfile.getNumSpeedStepsInCpuCluster(i);
+ mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster,
+ numSpeedSteps);
+ firstCpuOfCluster += powerProfile.getNumCoresInCpuCluster(i);
+ }
+
+ // Enable push notifications of throttling from vendor thermal
+ // management subsystem via thermalservice.
+ IBinder b = ServiceManager.getService("thermalservice");
+
+ if (b != null) {
+ sThermalService = IThermalService.Stub.asInterface(b);
+ try {
+ sThermalService.registerThermalEventListener(
+ new ThermalEventListener());
+ Slog.i(TAG, "register thermal listener successfully");
+ } catch (RemoteException e) {
+ // Should never happen.
+ Slog.e(TAG, "register thermal listener error");
+ }
+ } else {
+ Slog.e(TAG, "cannot find thermalservice, no throttling push notifications");
+ }
+
+ // Default NetworkRequest should cover all transport types.
+ final NetworkRequest request = new NetworkRequest.Builder().build();
+ final ConnectivityManager connectivityManager =
+ (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ connectivityManager.registerNetworkCallback(request, new ConnectivityStatsCallback());
+
+ HandlerThread handlerThread = new HandlerThread(TAG);
+ handlerThread.start();
+ mHandler = new CompanionHandler(handlerThread.getLooper());
+
+ mKernelCpuThreadReader =
+ KernelCpuThreadReaderSettingsObserver.getSettingsModifiedReader(mContext);
+ }
+
+ @Override
+ public void sendDataBroadcast(IBinder intentSenderBinder, long lastReportTimeNs) {
+ enforceCallingPermission();
+ IntentSender intentSender = new IntentSender(intentSenderBinder);
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_LAST_REPORT_TIME, lastReportTimeNs);
+ try {
+ intentSender.sendIntent(mContext, CODE_DATA_BROADCAST, intent, null, null);
+ } catch (IntentSender.SendIntentException e) {
+ Slog.w(TAG, "Unable to send using IntentSender");
+ }
+ }
+
+ @Override
+ public void sendActiveConfigsChangedBroadcast(IBinder intentSenderBinder, long[] configIds) {
+ enforceCallingPermission();
+ IntentSender intentSender = new IntentSender(intentSenderBinder);
+ Intent intent = new Intent();
+ intent.putExtra(StatsManager.EXTRA_STATS_ACTIVE_CONFIG_KEYS, configIds);
+ try {
+ intentSender.sendIntent(mContext, CODE_ACTIVE_CONFIGS_BROADCAST, intent, null, null);
+ if (DEBUG) {
+ Slog.d(TAG, "Sent broadcast with config ids " + Arrays.toString(configIds));
+ }
+ } catch (IntentSender.SendIntentException e) {
+ Slog.w(TAG, "Unable to send active configs changed broadcast using IntentSender");
+ }
+ }
+
+ @Override
+ public void sendSubscriberBroadcast(IBinder intentSenderBinder, long configUid, long configKey,
+ long subscriptionId, long subscriptionRuleId, String[] cookies,
+ StatsDimensionsValue dimensionsValue) {
+ enforceCallingPermission();
+ IntentSender intentSender = new IntentSender(intentSenderBinder);
+ Intent intent =
+ new Intent()
+ .putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid)
+ .putExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, configKey)
+ .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId)
+ .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID, subscriptionRuleId)
+ .putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue);
+
+ ArrayList<String> cookieList = new ArrayList<>(cookies.length);
+ for (String cookie : cookies) {
+ cookieList.add(cookie);
+ }
+ intent.putStringArrayListExtra(
+ StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES, cookieList);
+
+ if (DEBUG) {
+ Slog.d(TAG,
+ String.format("Statsd sendSubscriberBroadcast with params {%d %d %d %d %s %s}",
+ configUid, configKey, subscriptionId, subscriptionRuleId,
+ Arrays.toString(cookies),
+ dimensionsValue));
+ }
+ try {
+ intentSender.sendIntent(mContext, CODE_SUBSCRIBER_BROADCAST, intent, null, null);
+ } catch (IntentSender.SendIntentException e) {
+ Slog.w(TAG,
+ "Unable to send using IntentSender from uid " + configUid
+ + "; presumably it had been cancelled.");
+ }
+ }
+
+ private final static int[] toIntArray(List<Integer> list) {
+ int[] ret = new int[list.size()];
+ for (int i = 0; i < ret.length; i++) {
+ ret[i] = list.get(i);
+ }
+ return ret;
+ }
+
+ private final static long[] toLongArray(List<Long> list) {
+ long[] ret = new long[list.size()];
+ for (int i = 0; i < ret.length; i++) {
+ ret[i] = list.get(i);
+ }
+ return ret;
+ }
+
+ // Assumes that sStatsdLock is held.
+ @GuardedBy("sStatsdLock")
+ private final void informAllUidsLocked(Context context) throws RemoteException {
+ UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ PackageManager pm = context.getPackageManager();
+ final List<UserInfo> users = um.getUsers(true);
+ if (DEBUG) {
+ Slog.d(TAG, "Iterating over " + users.size() + " profiles.");
+ }
+
+ ParcelFileDescriptor[] fds;
+ try {
+ fds = ParcelFileDescriptor.createPipe();
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to create a pipe to send uid map data.", e);
+ return;
+ }
+ sStatsd.informAllUidData(fds[0]);
+ try {
+ fds[0].close();
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to close the read side of the pipe.", e);
+ }
+ final ParcelFileDescriptor writeFd = fds[1];
+ BackgroundThread.getHandler().post(() -> {
+ FileOutputStream fout = new ParcelFileDescriptor.AutoCloseOutputStream(writeFd);
+ try {
+ ProtoOutputStream output = new ProtoOutputStream(fout);
+ int numRecords = 0;
+ // Add in all the apps for every user/profile.
+ for (UserInfo profile : users) {
+ List<PackageInfo> pi =
+ pm.getInstalledPackagesAsUser(PackageManager.MATCH_KNOWN_PACKAGES,
+ profile.id);
+ for (int j = 0; j < pi.size(); j++) {
+ if (pi.get(j).applicationInfo != null) {
+ String installer;
+ try {
+ installer = pm.getInstallerPackageName(pi.get(j).packageName);
+ } catch (IllegalArgumentException e) {
+ installer = "";
+ }
+ long applicationInfoToken =
+ output.start(ProtoStream.FIELD_TYPE_MESSAGE
+ | ProtoStream.FIELD_COUNT_REPEATED
+ | APPLICATION_INFO_FIELD_ID);
+ output.write(ProtoStream.FIELD_TYPE_INT32
+ | ProtoStream.FIELD_COUNT_SINGLE | UID_FIELD_ID,
+ pi.get(j).applicationInfo.uid);
+ output.write(ProtoStream.FIELD_TYPE_INT64
+ | ProtoStream.FIELD_COUNT_SINGLE
+ | VERSION_FIELD_ID, pi.get(j).getLongVersionCode());
+ output.write(ProtoStream.FIELD_TYPE_STRING
+ | ProtoStream.FIELD_COUNT_SINGLE | VERSION_STRING_FIELD_ID,
+ pi.get(j).versionName);
+ output.write(ProtoStream.FIELD_TYPE_STRING
+ | ProtoStream.FIELD_COUNT_SINGLE
+ | PACKAGE_NAME_FIELD_ID, pi.get(j).packageName);
+ output.write(ProtoStream.FIELD_TYPE_STRING
+ | ProtoStream.FIELD_COUNT_SINGLE
+ | INSTALLER_FIELD_ID,
+ installer == null ? "" : installer);
+ numRecords++;
+ output.end(applicationInfoToken);
+ }
+ }
+ }
+ output.flush();
+ if (DEBUG) {
+ Slog.d(TAG, "Sent data for " + numRecords + " apps");
+ }
+ } finally {
+ IoUtils.closeQuietly(fout);
+ }
+ });
+ }
+
+ private final static class AppUpdateReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ /**
+ * App updates actually consist of REMOVE, ADD, and then REPLACE broadcasts. To avoid
+ * waste, we ignore the REMOVE and ADD broadcasts that contain the replacing flag.
+ * If we can't find the value for EXTRA_REPLACING, we default to false.
+ */
+ if (!intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)
+ && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ return; // Keep only replacing or normal add and remove.
+ }
+ if (DEBUG) Slog.d(TAG, "StatsCompanionService noticed an app was updated.");
+ synchronized (sStatsdLock) {
+ if (sStatsd == null) {
+ Slog.w(TAG, "Could not access statsd to inform it of an app update");
+ return;
+ }
+ try {
+ if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) {
+ Bundle b = intent.getExtras();
+ int uid = b.getInt(Intent.EXTRA_UID);
+ boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+ if (!replacing) {
+ // Don't bother sending an update if we're right about to get another
+ // intent for the new version that's added.
+ PackageManager pm = context.getPackageManager();
+ String app = intent.getData().getSchemeSpecificPart();
+ sStatsd.informOnePackageRemoved(app, uid);
+ }
+ } else {
+ PackageManager pm = context.getPackageManager();
+ Bundle b = intent.getExtras();
+ int uid = b.getInt(Intent.EXTRA_UID);
+ String app = intent.getData().getSchemeSpecificPart();
+ PackageInfo pi = pm.getPackageInfo(app, PackageManager.MATCH_ANY_USER);
+ String installer;
+ try {
+ installer = pm.getInstallerPackageName(app);
+ } catch (IllegalArgumentException e) {
+ installer = "";
+ }
+ sStatsd.informOnePackage(app, uid, pi.getLongVersionCode(), pi.versionName,
+ installer == null ? "" : installer);
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Failed to inform statsd of an app update", e);
+ }
+ }
+ }
+ }
+
+ public final static class AnomalyAlarmListener implements OnAlarmListener {
+ @Override
+ public void onAlarm() {
+ Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred at time "
+ + System.currentTimeMillis() + "ms.");
+ synchronized (sStatsdLock) {
+ if (sStatsd == null) {
+ Slog.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
+ return;
+ }
+ try {
+ // Two-way call to statsd to retain AlarmManager wakelock
+ sStatsd.informAnomalyAlarmFired();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to inform statsd of anomaly alarm firing", e);
+ }
+ }
+ // AlarmManager releases its own wakelock here.
+ }
+ }
+
+ public final static class PullingAlarmListener implements OnAlarmListener {
+ @Override
+ public void onAlarm() {
+ if (DEBUG) {
+ Slog.d(TAG, "Time to poll something.");
+ }
+ synchronized (sStatsdLock) {
+ if (sStatsd == null) {
+ Slog.w(TAG, "Could not access statsd to inform it of pulling alarm firing.");
+ return;
+ }
+ try {
+ // Two-way call to statsd to retain AlarmManager wakelock
+ sStatsd.informPollAlarmFired();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to inform statsd of pulling alarm firing.", e);
+ }
+ }
+ }
+ }
+
+ public final static class PeriodicAlarmListener implements OnAlarmListener {
+ @Override
+ public void onAlarm() {
+ if (DEBUG) {
+ Slog.d(TAG, "Time to trigger periodic alarm.");
+ }
+ synchronized (sStatsdLock) {
+ if (sStatsd == null) {
+ Slog.w(TAG, "Could not access statsd to inform it of periodic alarm firing.");
+ return;
+ }
+ try {
+ // Two-way call to statsd to retain AlarmManager wakelock
+ sStatsd.informAlarmForSubscriberTriggeringFired();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to inform statsd of periodic alarm firing.", e);
+ }
+ }
+ // AlarmManager releases its own wakelock here.
+ }
+ }
+
+ public final static class ShutdownEventReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ /**
+ * Skip immediately if intent is not relevant to device shutdown.
+ */
+ if (!intent.getAction().equals(Intent.ACTION_REBOOT)
+ && !(intent.getAction().equals(Intent.ACTION_SHUTDOWN)
+ && (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0)) {
+ return;
+ }
+
+ Slog.i(TAG, "StatsCompanionService noticed a shutdown.");
+ synchronized (sStatsdLock) {
+ if (sStatsd == null) {
+ Slog.w(TAG, "Could not access statsd to inform it of a shutdown event.");
+ return;
+ }
+ try {
+ sStatsd.informDeviceShutdown();
+ } catch (Exception e) {
+ Slog.w(TAG, "Failed to inform statsd of a shutdown event.", e);
+ }
+ }
+ }
+ }
+
+ @Override // Binder call
+ public void setAnomalyAlarm(long timestampMs) {
+ enforceCallingPermission();
+ if (DEBUG) Slog.d(TAG, "Setting anomaly alarm for " + timestampMs);
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
+ // only fire when it awakens.
+ // AlarmManager will automatically cancel any previous mAnomalyAlarmListener alarm.
+ mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, timestampMs, TAG + ".anomaly",
+ mAnomalyAlarmListener, mHandler);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ }
+
+ @Override // Binder call
+ public void cancelAnomalyAlarm() {
+ enforceCallingPermission();
+ if (DEBUG) Slog.d(TAG, "Cancelling anomaly alarm");
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ mAlarmManager.cancel(mAnomalyAlarmListener);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ }
+
+ @Override // Binder call
+ public void setAlarmForSubscriberTriggering(long timestampMs) {
+ enforceCallingPermission();
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Setting periodic alarm in about " + (timestampMs
+ - SystemClock.elapsedRealtime()));
+ }
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
+ // only fire when it awakens.
+ mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, timestampMs, TAG + ".periodic",
+ mPeriodicAlarmListener, mHandler);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ }
+
+ @Override // Binder call
+ public void cancelAlarmForSubscriberTriggering() {
+ enforceCallingPermission();
+ if (DEBUG) {
+ Slog.d(TAG, "Cancelling periodic alarm");
+ }
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ mAlarmManager.cancel(mPeriodicAlarmListener);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ }
+
+ @Override // Binder call
+ public void setPullingAlarm(long nextPullTimeMs) {
+ enforceCallingPermission();
+ if (DEBUG) {
+ Slog.d(TAG, "Setting pulling alarm in about "
+ + (nextPullTimeMs - SystemClock.elapsedRealtime()));
+ }
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
+ // only fire when it awakens.
+ mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, nextPullTimeMs, TAG + ".pull",
+ mPullingAlarmListener, mHandler);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ }
+
+ @Override // Binder call
+ public void cancelPullingAlarm() {
+ enforceCallingPermission();
+ if (DEBUG) {
+ Slog.d(TAG, "Cancelling pulling alarm");
+ }
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ mAlarmManager.cancel(mPullingAlarmListener);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ }
+
+ private void addNetworkStats(
+ int tag, List<StatsLogEventWrapper> ret, NetworkStats stats, boolean withFGBG) {
+ int size = stats.size();
+ long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ long wallClockNanos = SystemClock.currentTimeMicro() * 1000L;
+ NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
+ for (int j = 0; j < size; j++) {
+ stats.getValues(j, entry);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tag, elapsedNanos, wallClockNanos);
+ e.writeInt(entry.uid);
+ if (withFGBG) {
+ e.writeInt(entry.set);
+ }
+ e.writeLong(entry.rxBytes);
+ e.writeLong(entry.rxPackets);
+ e.writeLong(entry.txBytes);
+ e.writeLong(entry.txPackets);
+ ret.add(e);
+ }
+ }
+
+ /**
+ * Allows rollups per UID but keeping the set (foreground/background) slicing.
+ * Adapted from groupedByUid in frameworks/base/core/java/android/net/NetworkStats.java
+ */
+ private NetworkStats rollupNetworkStatsByFGBG(NetworkStats stats) {
+ final NetworkStats ret = new NetworkStats(stats.getElapsedRealtime(), 1);
+
+ final NetworkStats.Entry entry = new NetworkStats.Entry();
+ entry.iface = NetworkStats.IFACE_ALL;
+ entry.tag = NetworkStats.TAG_NONE;
+ entry.metered = NetworkStats.METERED_ALL;
+ entry.roaming = NetworkStats.ROAMING_ALL;
+
+ int size = stats.size();
+ NetworkStats.Entry recycle = new NetworkStats.Entry(); // Used for retrieving values
+ for (int i = 0; i < size; i++) {
+ stats.getValues(i, recycle);
+
+ // Skip specific tags, since already counted in TAG_NONE
+ if (recycle.tag != NetworkStats.TAG_NONE) continue;
+
+ entry.set = recycle.set; // Allows slicing by background/foreground
+ entry.uid = recycle.uid;
+ entry.rxBytes = recycle.rxBytes;
+ entry.rxPackets = recycle.rxPackets;
+ entry.txBytes = recycle.txBytes;
+ entry.txPackets = recycle.txPackets;
+ // Operations purposefully omitted since we don't use them for statsd.
+ ret.combineValues(entry);
+ }
+ return ret;
+ }
+
+ /**
+ * Helper method to extract the Parcelable controller info from a
+ * SynchronousResultReceiver.
+ */
+ private static <T extends Parcelable> T awaitControllerInfo(
+ @Nullable SynchronousResultReceiver receiver) {
+ if (receiver == null) {
+ return null;
+ }
+
+ try {
+ final SynchronousResultReceiver.Result result =
+ receiver.awaitResult(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS);
+ if (result.bundle != null) {
+ // This is the final destination for the Bundle.
+ result.bundle.setDefusable(true);
+
+ final T data = result.bundle.getParcelable(
+ RESULT_RECEIVER_CONTROLLER_KEY);
+ if (data != null) {
+ return data;
+ }
+ }
+ Slog.e(TAG, "no controller energy info supplied for " + receiver.getName());
+ } catch (TimeoutException e) {
+ Slog.w(TAG, "timeout reading " + receiver.getName() + " stats");
+ }
+ return null;
+ }
+
+ private void pullKernelWakelock(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ final KernelWakelockStats wakelockStats =
+ mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
+ for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
+ String name = ent.getKey();
+ KernelWakelockStats.Entry kws = ent.getValue();
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeString(name);
+ e.writeInt(kws.mCount);
+ e.writeInt(kws.mVersion);
+ e.writeLong(kws.mTotalTime);
+ pulledData.add(e);
+ }
+ }
+
+ private void pullWifiBytesTransfer(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ // TODO: Consider caching the following call to get BatteryStatsInternal.
+ BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
+ String[] ifaces = bs.getWifiIfaces();
+ if (ifaces.length == 0) {
+ return;
+ }
+ if (mNetworkStatsService == null) {
+ Slog.e(TAG, "NetworkStats Service is not available!");
+ return;
+ }
+ // Combine all the metrics per Uid into one record.
+ NetworkStats stats = mNetworkStatsService.getDetailedUidStats(ifaces).groupedByUid();
+ addNetworkStats(tagId, pulledData, stats, false);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Pulling netstats for wifi bytes has error", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void pullWifiBytesTransferByFgBg(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
+ String[] ifaces = bs.getWifiIfaces();
+ if (ifaces.length == 0) {
+ return;
+ }
+ if (mNetworkStatsService == null) {
+ Slog.e(TAG, "NetworkStats Service is not available!");
+ return;
+ }
+ NetworkStats stats = rollupNetworkStatsByFGBG(
+ mNetworkStatsService.getDetailedUidStats(ifaces));
+ addNetworkStats(tagId, pulledData, stats, true);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void pullMobileBytesTransfer(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
+ String[] ifaces = bs.getMobileIfaces();
+ if (ifaces.length == 0) {
+ return;
+ }
+ if (mNetworkStatsService == null) {
+ Slog.e(TAG, "NetworkStats Service is not available!");
+ return;
+ }
+ // Combine all the metrics per Uid into one record.
+ NetworkStats stats = mNetworkStatsService.getDetailedUidStats(ifaces).groupedByUid();
+ addNetworkStats(tagId, pulledData, stats, false);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Pulling netstats for mobile bytes has error", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void pullBluetoothBytesTransfer(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ BluetoothActivityEnergyInfo info = fetchBluetoothData();
+ if (info.getUidTraffic() != null) {
+ for (UidTraffic traffic : info.getUidTraffic()) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+ wallClockNanos);
+ e.writeInt(traffic.getUid());
+ e.writeLong(traffic.getRxBytes());
+ e.writeLong(traffic.getTxBytes());
+ pulledData.add(e);
+ }
+ }
+ }
+
+ private void pullMobileBytesTransferByFgBg(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
+ String[] ifaces = bs.getMobileIfaces();
+ if (ifaces.length == 0) {
+ return;
+ }
+ if (mNetworkStatsService == null) {
+ Slog.e(TAG, "NetworkStats Service is not available!");
+ return;
+ }
+ NetworkStats stats = rollupNetworkStatsByFGBG(
+ mNetworkStatsService.getDetailedUidStats(ifaces));
+ addNetworkStats(tagId, pulledData, stats, true);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void pullCpuTimePerFreq(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
+ long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readAbsolute();
+ if (clusterTimeMs != null) {
+ for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+ wallClockNanos);
+ e.writeInt(cluster);
+ e.writeInt(speed);
+ e.writeLong(clusterTimeMs[speed]);
+ pulledData.add(e);
+ }
+ }
+ }
+ }
+
+ private void pullKernelUidCpuTime(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ mCpuUidUserSysTimeReader.readAbsolute((uid, timesUs) -> {
+ long userTimeUs = timesUs[0], systemTimeUs = timesUs[1];
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(uid);
+ e.writeLong(userTimeUs);
+ e.writeLong(systemTimeUs);
+ pulledData.add(e);
+ });
+ }
+
+ private void pullKernelUidCpuFreqTime(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ mCpuUidFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> {
+ for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) {
+ if (cpuFreqTimeMs[freqIndex] != 0) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+ wallClockNanos);
+ e.writeInt(uid);
+ e.writeInt(freqIndex);
+ e.writeLong(cpuFreqTimeMs[freqIndex]);
+ pulledData.add(e);
+ }
+ }
+ });
+ }
+
+ private void pullKernelUidCpuClusterTime(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ mCpuUidClusterTimeReader.readAbsolute((uid, cpuClusterTimesMs) -> {
+ for (int i = 0; i < cpuClusterTimesMs.length; i++) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+ wallClockNanos);
+ e.writeInt(uid);
+ e.writeInt(i);
+ e.writeLong(cpuClusterTimesMs[i]);
+ pulledData.add(e);
+ }
+ });
+ }
+
+ private void pullKernelUidCpuActiveTime(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ mCpuUidActiveTimeReader.readAbsolute((uid, cpuActiveTimesMs) -> {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(uid);
+ e.writeLong((long) cpuActiveTimesMs);
+ pulledData.add(e);
+ });
+ }
+
+ private void pullWifiActivityInfo(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ long token = Binder.clearCallingIdentity();
+ synchronized (this) {
+ if (mWifiManager == null) {
+ mWifiManager =
+ IWifiManager.Stub.asInterface(
+ ServiceManager.getService(Context.WIFI_SERVICE));
+ }
+ }
+ if (mWifiManager != null) {
+ try {
+ SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
+ mWifiManager.requestActivityInfo(wifiReceiver);
+ final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+ wallClockNanos);
+ e.writeLong(wifiInfo.getTimeStamp());
+ e.writeInt(wifiInfo.getStackState());
+ e.writeLong(wifiInfo.getControllerTxTimeMillis());
+ e.writeLong(wifiInfo.getControllerRxTimeMillis());
+ e.writeLong(wifiInfo.getControllerIdleTimeMillis());
+ e.writeLong(wifiInfo.getControllerEnergyUsed());
+ pulledData.add(e);
+ } catch (RemoteException e) {
+ Slog.e(TAG,
+ "Pulling wifiManager for wifi controller activity energy info has error",
+ e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ private void pullModemActivityInfo(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ long token = Binder.clearCallingIdentity();
+ synchronized (this) {
+ if (mTelephony == null) {
+ mTelephony = TelephonyManager.from(mContext);
+ }
+ }
+ if (mTelephony != null) {
+ SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
+ mTelephony.requestModemActivityInfo(modemReceiver);
+ final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeLong(modemInfo.getTimestamp());
+ e.writeLong(modemInfo.getSleepTimeMillis());
+ e.writeLong(modemInfo.getIdleTimeMillis());
+ e.writeLong(modemInfo.getTransmitPowerInfo().get(0).getTimeInMillis());
+ e.writeLong(modemInfo.getTransmitPowerInfo().get(1).getTimeInMillis());
+ e.writeLong(modemInfo.getTransmitPowerInfo().get(2).getTimeInMillis());
+ e.writeLong(modemInfo.getTransmitPowerInfo().get(3).getTimeInMillis());
+ e.writeLong(modemInfo.getTransmitPowerInfo().get(4).getTimeInMillis());
+ e.writeLong(modemInfo.getReceiveTimeMillis());
+ pulledData.add(e);
+ }
+ }
+
+ private void pullBluetoothActivityInfo(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ BluetoothActivityEnergyInfo info = fetchBluetoothData();
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeLong(info.getTimeStamp());
+ e.writeInt(info.getBluetoothStackState());
+ e.writeLong(info.getControllerTxTimeMillis());
+ e.writeLong(info.getControllerRxTimeMillis());
+ e.writeLong(info.getControllerIdleTimeMillis());
+ e.writeLong(info.getControllerEnergyUsed());
+ pulledData.add(e);
+ }
+
+ private synchronized BluetoothActivityEnergyInfo fetchBluetoothData() {
+ final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter != null) {
+ SynchronousResultReceiver bluetoothReceiver = new SynchronousResultReceiver(
+ "bluetooth");
+ adapter.requestControllerActivityEnergyInfo(bluetoothReceiver);
+ return awaitControllerInfo(bluetoothReceiver);
+ } else {
+ Slog.e(TAG, "Failed to get bluetooth adapter!");
+ return null;
+ }
+ }
+
+ private void pullSystemElapsedRealtime(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeLong(SystemClock.elapsedRealtime());
+ pulledData.add(e);
+ }
+
+ private void pullSystemUpTime(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeLong(SystemClock.uptimeMillis());
+ pulledData.add(e);
+ }
+
+ private void pullProcessMemoryState(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ List<ProcessMemoryState> processMemoryStates =
+ LocalServices.getService(
+ ActivityManagerInternal.class).getMemoryStateForProcesses();
+ for (ProcessMemoryState processMemoryState : processMemoryStates) {
+ final MemoryStat memoryStat = readMemoryStatFromFilesystem(processMemoryState.uid,
+ processMemoryState.pid);
+ if (memoryStat == null) {
+ continue;
+ }
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(processMemoryState.uid);
+ e.writeString(processMemoryState.processName);
+ e.writeInt(processMemoryState.oomScore);
+ e.writeLong(memoryStat.pgfault);
+ e.writeLong(memoryStat.pgmajfault);
+ e.writeLong(memoryStat.rssInBytes);
+ e.writeLong(memoryStat.cacheInBytes);
+ e.writeLong(memoryStat.swapInBytes);
+ e.writeLong(-1); // unused
+ e.writeLong(-1); // unused
+ e.writeInt(-1); // unsed
+ pulledData.add(e);
+ }
+ }
+
+ private void pullProcessMemoryHighWaterMark(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ List<ProcessMemoryState> managedProcessList =
+ LocalServices.getService(
+ ActivityManagerInternal.class).getMemoryStateForProcesses();
+ for (ProcessMemoryState managedProcess : managedProcessList) {
+ final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(managedProcess.pid);
+ if (snapshot == null) {
+ continue;
+ }
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(managedProcess.uid);
+ e.writeString(managedProcess.processName);
+ // RSS high-water mark in bytes.
+ e.writeLong((long) snapshot.rssHighWaterMarkInKilobytes * 1024L);
+ e.writeInt(snapshot.rssHighWaterMarkInKilobytes);
+ pulledData.add(e);
+ }
+ int[] pids = getPidsForCommands(MEMORY_INTERESTING_NATIVE_PROCESSES);
+ for (int pid : pids) {
+ final String processName = readCmdlineFromProcfs(pid);
+ final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid);
+ if (snapshot == null) {
+ continue;
+ }
+ // Sometimes we get here a process that is not included in the whitelist. It comes
+ // from forking the zygote for an app. We can ignore that sample because this process
+ // is collected by ProcessMemoryState.
+ if (isAppUid(snapshot.uid)) {
+ continue;
+ }
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(snapshot.uid);
+ e.writeString(processName);
+ // RSS high-water mark in bytes.
+ e.writeLong((long) snapshot.rssHighWaterMarkInKilobytes * 1024L);
+ e.writeInt(snapshot.rssHighWaterMarkInKilobytes);
+ pulledData.add(e);
+ }
+ // Invoke rss_hwm_reset binary to reset RSS HWM counters for all processes.
+ SystemProperties.set("sys.rss_hwm_reset.on", "1");
+ }
+
+ private void pullProcessMemorySnapshot(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ List<ProcessMemoryState> managedProcessList =
+ LocalServices.getService(
+ ActivityManagerInternal.class).getMemoryStateForProcesses();
+ for (ProcessMemoryState managedProcess : managedProcessList) {
+ final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(managedProcess.pid);
+ if (snapshot == null) {
+ continue;
+ }
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(managedProcess.uid);
+ e.writeString(managedProcess.processName);
+ e.writeInt(managedProcess.pid);
+ e.writeInt(managedProcess.oomScore);
+ e.writeInt(snapshot.rssInKilobytes);
+ e.writeInt(snapshot.anonRssInKilobytes);
+ e.writeInt(snapshot.swapInKilobytes);
+ e.writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes);
+ pulledData.add(e);
+ }
+ int[] pids = getPidsForCommands(MEMORY_INTERESTING_NATIVE_PROCESSES);
+ for (int pid : pids) {
+ final String processName = readCmdlineFromProcfs(pid);
+ final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid);
+ if (snapshot == null) {
+ continue;
+ }
+ // Sometimes we get here a process that is not included in the whitelist. It comes
+ // from forking the zygote for an app. We can ignore that sample because this process
+ // is collected by ProcessMemoryState.
+ if (isAppUid(snapshot.uid)) {
+ continue;
+ }
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(snapshot.uid);
+ e.writeString(processName);
+ e.writeInt(pid);
+ e.writeInt(-1001); // Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1.
+ e.writeInt(snapshot.rssInKilobytes);
+ e.writeInt(snapshot.anonRssInKilobytes);
+ e.writeInt(snapshot.swapInKilobytes);
+ e.writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes);
+ pulledData.add(e);
+ }
+ }
+
+ private static boolean isAppUid(int uid) {
+ return uid >= MIN_APP_UID;
+ }
+
+ private void pullSystemIonHeapSize(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ final long systemIonHeapSizeInBytes = readSystemIonHeapSizeFromDebugfs();
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeLong(systemIonHeapSizeInBytes);
+ pulledData.add(e);
+ }
+
+ private void pullProcessSystemIonHeapSize(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ List<IonAllocations> result = readProcessSystemIonHeapSizesFromDebugfs();
+ for (IonAllocations allocations : result) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(getUidForPid(allocations.pid));
+ e.writeString(readCmdlineFromProcfs(allocations.pid));
+ e.writeInt((int) (allocations.totalSizeInBytes / 1024));
+ e.writeInt(allocations.count);
+ e.writeInt((int) (allocations.maxSizeInBytes / 1024));
+ pulledData.add(e);
+ }
+ }
+
+ private void pullBinderCallsStats(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ BinderCallsStatsService.Internal binderStats =
+ LocalServices.getService(BinderCallsStatsService.Internal.class);
+ if (binderStats == null) {
+ throw new IllegalStateException("binderStats is null");
+ }
+
+ List<ExportedCallStat> callStats = binderStats.getExportedCallStats();
+ binderStats.reset();
+ for (ExportedCallStat callStat : callStats) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(callStat.workSourceUid);
+ e.writeString(callStat.className);
+ e.writeString(callStat.methodName);
+ e.writeLong(callStat.callCount);
+ e.writeLong(callStat.exceptionCount);
+ e.writeLong(callStat.latencyMicros);
+ e.writeLong(callStat.maxLatencyMicros);
+ e.writeLong(callStat.cpuTimeMicros);
+ e.writeLong(callStat.maxCpuTimeMicros);
+ e.writeLong(callStat.maxReplySizeBytes);
+ e.writeLong(callStat.maxRequestSizeBytes);
+ e.writeLong(callStat.recordedCallCount);
+ e.writeInt(callStat.screenInteractive ? 1 : 0);
+ e.writeInt(callStat.callingUid);
+ pulledData.add(e);
+ }
+ }
+
+ private void pullBinderCallsStatsExceptions(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ BinderCallsStatsService.Internal binderStats =
+ LocalServices.getService(BinderCallsStatsService.Internal.class);
+ if (binderStats == null) {
+ throw new IllegalStateException("binderStats is null");
+ }
+
+ ArrayMap<String, Integer> exceptionStats = binderStats.getExportedExceptionStats();
+ // TODO: decouple binder calls exceptions with the rest of the binder calls data so that we
+ // can reset the exception stats.
+ for (Entry<String, Integer> entry : exceptionStats.entrySet()) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeString(entry.getKey());
+ e.writeInt(entry.getValue());
+ pulledData.add(e);
+ }
+ }
+
+ private void pullLooperStats(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ LooperStats looperStats = LocalServices.getService(LooperStats.class);
+ if (looperStats == null) {
+ throw new IllegalStateException("looperStats null");
+ }
+
+ List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+ looperStats.reset();
+ for (LooperStats.ExportedEntry entry : entries) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(entry.workSourceUid);
+ e.writeString(entry.handlerClassName);
+ e.writeString(entry.threadName);
+ e.writeString(entry.messageName);
+ e.writeLong(entry.messageCount);
+ e.writeLong(entry.exceptionCount);
+ e.writeLong(entry.recordedMessageCount);
+ e.writeLong(entry.totalLatencyMicros);
+ e.writeLong(entry.cpuUsageMicros);
+ e.writeBoolean(entry.isInteractive);
+ e.writeLong(entry.maxCpuUsageMicros);
+ e.writeLong(entry.maxLatencyMicros);
+ e.writeLong(entry.recordedDelayMessageCount);
+ e.writeLong(entry.delayMillis);
+ e.writeLong(entry.maxDelayMillis);
+ pulledData.add(e);
+ }
+ }
+
+ private void pullDiskStats(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ // Run a quick-and-dirty performance test: write 512 bytes
+ byte[] junk = new byte[512];
+ for (int i = 0; i < junk.length; i++) junk[i] = (byte) i; // Write nonzero bytes
+
+ File tmp = new File(Environment.getDataDirectory(), "system/statsdperftest.tmp");
+ FileOutputStream fos = null;
+ IOException error = null;
+
+ long before = SystemClock.elapsedRealtime();
+ try {
+ fos = new FileOutputStream(tmp);
+ fos.write(junk);
+ } catch (IOException e) {
+ error = e;
+ } finally {
+ try {
+ if (fos != null) fos.close();
+ } catch (IOException e) {
+ // Do nothing.
+ }
+ }
+
+ long latency = SystemClock.elapsedRealtime() - before;
+ if (tmp.exists()) tmp.delete();
+
+ if (error != null) {
+ Slog.e(TAG, "Error performing diskstats latency test");
+ latency = -1;
+ }
+ // File based encryption.
+ boolean fileBased = StorageManager.isFileEncryptedNativeOnly();
+
+ //Recent disk write speed. Binder call to storaged.
+ int writeSpeed = -1;
+ try {
+ IBinder binder = ServiceManager.getService("storaged");
+ if (binder == null) {
+ Slog.e(TAG, "storaged not found");
+ }
+ IStoraged storaged = IStoraged.Stub.asInterface(binder);
+ writeSpeed = storaged.getRecentPerf();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "storaged not found");
+ }
+
+ // Add info pulledData.
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeLong(latency);
+ e.writeBoolean(fileBased);
+ e.writeInt(writeSpeed);
+ pulledData.add(e);
+ }
+
+ private void pullDirectoryUsage(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ StatFs statFsData = new StatFs(Environment.getDataDirectory().getAbsolutePath());
+ StatFs statFsSystem = new StatFs(Environment.getRootDirectory().getAbsolutePath());
+ StatFs statFsCache = new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
+
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__DATA);
+ e.writeLong(statFsData.getAvailableBytes());
+ e.writeLong(statFsData.getTotalBytes());
+ pulledData.add(e);
+
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__CACHE);
+ e.writeLong(statFsCache.getAvailableBytes());
+ e.writeLong(statFsCache.getTotalBytes());
+ pulledData.add(e);
+
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__SYSTEM);
+ e.writeLong(statFsSystem.getAvailableBytes());
+ e.writeLong(statFsSystem.getTotalBytes());
+ pulledData.add(e);
+ }
+
+ private void pullAppSize(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ try {
+ String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH);
+ JSONObject json = new JSONObject(jsonStr);
+ long cache_time = json.optLong(DiskStatsFileLogger.LAST_QUERY_TIMESTAMP_KEY, -1L);
+ JSONArray pkg_names = json.getJSONArray(DiskStatsFileLogger.PACKAGE_NAMES_KEY);
+ JSONArray app_sizes = json.getJSONArray(DiskStatsFileLogger.APP_SIZES_KEY);
+ JSONArray app_data_sizes = json.getJSONArray(DiskStatsFileLogger.APP_DATA_KEY);
+ JSONArray app_cache_sizes = json.getJSONArray(DiskStatsFileLogger.APP_CACHES_KEY);
+ // Sanity check: Ensure all 4 lists have the same length.
+ int length = pkg_names.length();
+ if (app_sizes.length() != length || app_data_sizes.length() != length
+ || app_cache_sizes.length() != length) {
+ Slog.e(TAG, "formatting error in diskstats cache file!");
+ return;
+ }
+ for (int i = 0; i < length; i++) {
+ StatsLogEventWrapper e =
+ new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeString(pkg_names.getString(i));
+ e.writeLong(app_sizes.optLong(i, -1L));
+ e.writeLong(app_data_sizes.optLong(i, -1L));
+ e.writeLong(app_cache_sizes.optLong(i, -1L));
+ e.writeLong(cache_time);
+ pulledData.add(e);
+ }
+ } catch (IOException | JSONException e) {
+ Slog.e(TAG, "exception reading diskstats cache file", e);
+ }
+ }
+
+ private void pullCategorySize(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ try {
+ String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH);
+ JSONObject json = new JSONObject(jsonStr);
+ long cacheTime = json.optLong(DiskStatsFileLogger.LAST_QUERY_TIMESTAMP_KEY, -1L);
+
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_SIZE);
+ e.writeLong(json.optLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY, -1L));
+ e.writeLong(cacheTime);
+ pulledData.add(e);
+
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_DATA_SIZE);
+ e.writeLong(json.optLong(DiskStatsFileLogger.APP_DATA_SIZE_AGG_KEY, -1L));
+ e.writeLong(cacheTime);
+ pulledData.add(e);
+
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_CACHE_SIZE);
+ e.writeLong(json.optLong(DiskStatsFileLogger.APP_CACHE_AGG_KEY, -1L));
+ e.writeLong(cacheTime);
+ pulledData.add(e);
+
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__PHOTOS);
+ e.writeLong(json.optLong(DiskStatsFileLogger.PHOTOS_KEY, -1L));
+ e.writeLong(cacheTime);
+ pulledData.add(e);
+
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__VIDEOS);
+ e.writeLong(json.optLong(DiskStatsFileLogger.VIDEOS_KEY, -1L));
+ e.writeLong(cacheTime);
+ pulledData.add(e);
+
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__AUDIO);
+ e.writeLong(json.optLong(DiskStatsFileLogger.AUDIO_KEY, -1L));
+ e.writeLong(cacheTime);
+ pulledData.add(e);
+
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__DOWNLOADS);
+ e.writeLong(json.optLong(DiskStatsFileLogger.DOWNLOADS_KEY, -1L));
+ e.writeLong(cacheTime);
+ pulledData.add(e);
+
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__SYSTEM);
+ e.writeLong(json.optLong(DiskStatsFileLogger.SYSTEM_KEY, -1L));
+ e.writeLong(cacheTime);
+ pulledData.add(e);
+
+ e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__OTHER);
+ e.writeLong(json.optLong(DiskStatsFileLogger.MISC_KEY, -1L));
+ e.writeLong(cacheTime);
+ pulledData.add(e);
+ } catch (IOException | JSONException e) {
+ Slog.e(TAG, "exception reading diskstats cache file", e);
+ }
+ }
+
+ private void pullNumBiometricsEnrolled(int modality, int tagId, long elapsedNanos,
+ long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+ final PackageManager pm = mContext.getPackageManager();
+ FingerprintManager fingerprintManager = null;
+ FaceManager faceManager = null;
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+ fingerprintManager = mContext.getSystemService(
+ FingerprintManager.class);
+ }
+ if (pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
+ faceManager = mContext.getSystemService(FaceManager.class);
+ }
+
+ if (modality == BiometricsProtoEnums.MODALITY_FINGERPRINT && fingerprintManager == null) {
+ return;
+ }
+ if (modality == BiometricsProtoEnums.MODALITY_FACE && faceManager == null) {
+ return;
+ }
+ UserManager userManager = mContext.getSystemService(UserManager.class);
+ if (userManager == null) {
+ return;
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ for (UserInfo user : userManager.getUsers()) {
+ final int userId = user.getUserHandle().getIdentifier();
+ int numEnrolled = 0;
+ if (modality == BiometricsProtoEnums.MODALITY_FINGERPRINT) {
+ numEnrolled = fingerprintManager.getEnrolledFingerprints(userId).size();
+ } else if (modality == BiometricsProtoEnums.MODALITY_FACE) {
+ numEnrolled = faceManager.getEnrolledFaces(userId).size();
+ } else {
+ return;
+ }
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(userId);
+ e.writeInt(numEnrolled);
+ pulledData.add(e);
+ }
+ Binder.restoreCallingIdentity(token);
+ }
+
+ // read high watermark for section
+ private long readProcStatsHighWaterMark(int section) {
+ try {
+ File[] files = mBaseDir.listFiles((d, name) -> {
+ return name.toLowerCase().startsWith(String.valueOf(section) + '_');
+ });
+ if (files == null || files.length == 0) {
+ return 0;
+ }
+ if (files.length > 1) {
+ Log.e(TAG, "Only 1 file expected for high water mark. Found " + files.length);
+ }
+ return Long.valueOf(files[0].getName().split("_")[1]);
+ } catch (SecurityException e) {
+ Log.e(TAG, "Failed to get procstats high watermark file.", e);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Failed to parse file name.", e);
+ }
+ return 0;
+ }
+
+ private IProcessStats mProcessStats =
+ IProcessStats.Stub.asInterface(ServiceManager.getService(ProcessStats.SERVICE_NAME));
+
+ private void pullProcessStats(int section, int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ synchronized (this) {
+ try {
+ long lastHighWaterMark = readProcStatsHighWaterMark(section);
+ List<ParcelFileDescriptor> statsFiles = new ArrayList<>();
+ long highWaterMark = mProcessStats.getCommittedStats(
+ lastHighWaterMark, section, true, statsFiles);
+ if (statsFiles.size() != 1) {
+ return;
+ }
+ InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(
+ statsFiles.get(0));
+ int[] len = new int[1];
+ byte[] stats = readFully(stream, len);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+ wallClockNanos);
+ e.writeStorage(Arrays.copyOf(stats, len[0]));
+ pulledData.add(e);
+ new File(mBaseDir.getAbsolutePath() + "/" + section + "_"
+ + lastHighWaterMark).delete();
+ new File(
+ mBaseDir.getAbsolutePath() + "/" + section + "_"
+ + highWaterMark).createNewFile();
+ } catch (IOException e) {
+ Log.e(TAG, "Getting procstats failed: ", e);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Getting procstats failed: ", e);
+ } catch (SecurityException e) {
+ Log.e(TAG, "Getting procstats failed: ", e);
+ }
+ }
+ }
+
+ static byte[] readFully(InputStream stream, int[] outLen) throws IOException {
+ int pos = 0;
+ final int initialAvail = stream.available();
+ byte[] data = new byte[initialAvail > 0 ? (initialAvail + 1) : 16384];
+ while (true) {
+ int amt = stream.read(data, pos, data.length - pos);
+ if (DEBUG) {
+ Slog.i(TAG, "Read " + amt + " bytes at " + pos + " of avail " + data.length);
+ }
+ if (amt < 0) {
+ if (DEBUG) {
+ Slog.i(TAG, "**** FINISHED READING: pos=" + pos + " len=" + data.length);
+ }
+ outLen[0] = pos;
+ return data;
+ }
+ pos += amt;
+ if (pos >= data.length) {
+ byte[] newData = new byte[pos + 16384];
+ if (DEBUG) {
+ Slog.i(TAG, "Copying " + pos + " bytes to new array len " + newData.length);
+ }
+ System.arraycopy(data, 0, newData, 0, pos);
+ data = newData;
+ }
+ }
+ }
+
+ private void pullPowerProfile(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ PowerProfile powerProfile = new PowerProfile(mContext);
+ checkNotNull(powerProfile);
+
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+ wallClockNanos);
+ ProtoOutputStream proto = new ProtoOutputStream();
+ powerProfile.writeToProto(proto);
+ proto.flush();
+ e.writeStorage(proto.getBytes());
+ pulledData.add(e);
+ }
+
+ private void pullBuildInformation(int tagId,
+ long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeString(Build.FINGERPRINT);
+ e.writeString(Build.BRAND);
+ e.writeString(Build.PRODUCT);
+ e.writeString(Build.DEVICE);
+ e.writeString(Build.VERSION.RELEASE);
+ e.writeString(Build.ID);
+ e.writeString(Build.VERSION.INCREMENTAL);
+ e.writeString(Build.TYPE);
+ e.writeString(Build.TAGS);
+ pulledData.add(e);
+ }
+
+ private BatteryStatsHelper getBatteryStatsHelper() {
+ if (mBatteryStatsHelper == null) {
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ // clearCallingIdentity required for BatteryStatsHelper.checkWifiOnly().
+ mBatteryStatsHelper = new BatteryStatsHelper(mContext, false);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ mBatteryStatsHelper.create((Bundle) null);
+ }
+ long currentTime = SystemClock.elapsedRealtime();
+ if (currentTime - mBatteryStatsHelperTimestampMs >= MAX_BATTERY_STATS_HELPER_FREQUENCY_MS) {
+ // Load BatteryStats and do all the calculations.
+ mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.USER_ALL);
+ // Calculations are done so we don't need to save the raw BatteryStats data in RAM.
+ mBatteryStatsHelper.clearStats();
+ mBatteryStatsHelperTimestampMs = currentTime;
+ }
+ return mBatteryStatsHelper;
+ }
+
+ private long milliAmpHrsToNanoAmpSecs(double mAh) {
+ final long MILLI_AMP_HR_TO_NANO_AMP_SECS = 1_000_000L * 3600L;
+ return (long) (mAh * MILLI_AMP_HR_TO_NANO_AMP_SECS + 0.5);
+ }
+
+ private void pullDeviceCalculatedPowerUse(int tagId,
+ long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+ BatteryStatsHelper bsHelper = getBatteryStatsHelper();
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeLong(milliAmpHrsToNanoAmpSecs(bsHelper.getComputedPower()));
+ pulledData.add(e);
+ }
+
+ private void pullDeviceCalculatedPowerBlameUid(int tagId,
+ long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+ final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
+ if (sippers == null) {
+ return;
+ }
+ for (BatterySipper bs : sippers) {
+ if (bs.drainType != bs.drainType.APP) {
+ continue;
+ }
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(bs.uidObj.getUid());
+ e.writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah));
+ pulledData.add(e);
+ }
+ }
+
+ private void pullDeviceCalculatedPowerBlameOther(int tagId,
+ long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+ final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
+ if (sippers == null) {
+ return;
+ }
+ for (BatterySipper bs : sippers) {
+ if (bs.drainType == bs.drainType.APP) {
+ continue; // This is a separate atom; see pullDeviceCalculatedPowerBlameUid().
+ }
+ if (bs.drainType == bs.drainType.USER) {
+ continue; // This is not supported. We purposefully calculate over USER_ALL.
+ }
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(bs.drainType.ordinal());
+ e.writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah));
+ pulledData.add(e);
+ }
+ }
+
+ private void pullDiskIo(int tagId, long elapsedNanos, final long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ mStoragedUidIoStatsReader.readAbsolute((uid, fgCharsRead, fgCharsWrite, fgBytesRead,
+ fgBytesWrite, bgCharsRead, bgCharsWrite, bgBytesRead, bgBytesWrite,
+ fgFsync, bgFsync) -> {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+ wallClockNanos);
+ e.writeInt(uid);
+ e.writeLong(fgCharsRead);
+ e.writeLong(fgCharsWrite);
+ e.writeLong(fgBytesRead);
+ e.writeLong(fgBytesWrite);
+ e.writeLong(bgCharsRead);
+ e.writeLong(bgCharsWrite);
+ e.writeLong(bgBytesRead);
+ e.writeLong(bgBytesWrite);
+ e.writeLong(fgFsync);
+ e.writeLong(bgFsync);
+ pulledData.add(e);
+ });
+ }
+
+ private void pullProcessCpuTime(int tagId, long elapsedNanos, final long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ synchronized (this) {
+ if (mProcessCpuTracker == null) {
+ mProcessCpuTracker = new ProcessCpuTracker(false);
+ mProcessCpuTracker.init();
+ }
+ mProcessCpuTracker.update();
+ for (int i = 0; i < mProcessCpuTracker.countStats(); i++) {
+ ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+ wallClockNanos);
+ e.writeInt(st.uid);
+ e.writeString(st.name);
+ e.writeLong(st.base_utime);
+ e.writeLong(st.base_stime);
+ pulledData.add(e);
+ }
+ }
+ }
+
+ private void pullCpuTimePerThreadFreq(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ if (this.mKernelCpuThreadReader == null) {
+ throw new IllegalStateException("mKernelCpuThreadReader is null");
+ }
+ ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsages =
+ this.mKernelCpuThreadReader.getProcessCpuUsageDiffed();
+ if (processCpuUsages == null) {
+ throw new IllegalStateException("processCpuUsages is null");
+ }
+ int[] cpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz();
+ if (cpuFrequencies.length > CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES) {
+ String message = "Expected maximum " + CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES
+ + " frequencies, but got " + cpuFrequencies.length;
+ Slog.w(TAG, message);
+ throw new IllegalStateException(message);
+ }
+ for (int i = 0; i < processCpuUsages.size(); i++) {
+ KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = processCpuUsages.get(i);
+ ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages =
+ processCpuUsage.threadCpuUsages;
+ for (int j = 0; j < threadCpuUsages.size(); j++) {
+ KernelCpuThreadReader.ThreadCpuUsage threadCpuUsage = threadCpuUsages.get(j);
+ if (threadCpuUsage.usageTimesMillis.length != cpuFrequencies.length) {
+ String message = "Unexpected number of usage times,"
+ + " expected " + cpuFrequencies.length
+ + " but got " + threadCpuUsage.usageTimesMillis.length;
+ Slog.w(TAG, message);
+ throw new IllegalStateException(message);
+ }
+
+ StatsLogEventWrapper e =
+ new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(processCpuUsage.uid);
+ e.writeInt(processCpuUsage.processId);
+ e.writeInt(threadCpuUsage.threadId);
+ e.writeString(processCpuUsage.processName);
+ e.writeString(threadCpuUsage.threadName);
+ 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]);
+ } 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);
+ }
+ }
+ pulledData.add(e);
+ }
+ }
+ }
+
+ private void pullTemperature(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ long callingToken = Binder.clearCallingIdentity();
+ try {
+ List<Temperature> temperatures = sThermalService.getCurrentTemperatures();
+ for (Temperature temp : temperatures) {
+ StatsLogEventWrapper e =
+ new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(temp.getType());
+ e.writeString(temp.getName());
+ e.writeInt((int) (temp.getValue() * 10));
+ e.writeInt(temp.getStatus());
+ pulledData.add(e);
+ }
+ } catch (RemoteException e) {
+ // Should not happen.
+ Slog.e(TAG, "Disconnected from thermal service. Cannot pull temperatures.");
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ }
+
+ private void pullCoolingDevices(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ long callingToken = Binder.clearCallingIdentity();
+ try {
+ List<CoolingDevice> devices = sThermalService.getCurrentCoolingDevices();
+ for (CoolingDevice device : devices) {
+ StatsLogEventWrapper e =
+ new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(device.getType());
+ e.writeString(device.getName());
+ e.writeInt((int) (device.getValue()));
+ pulledData.add(e);
+ }
+ } catch (RemoteException e) {
+ // Should not happen.
+ Slog.e(TAG, "Disconnected from thermal service. Cannot pull temperatures.");
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ }
+
+ private void pullDebugElapsedClock(int tagId,
+ long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+ final long elapsedMillis = SystemClock.elapsedRealtime();
+ final long clockDiffMillis = mDebugElapsedClockPreviousValue == 0
+ ? 0 : elapsedMillis - mDebugElapsedClockPreviousValue;
+
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeLong(mDebugElapsedClockPullCount);
+ e.writeLong(elapsedMillis);
+ // Log it twice to be able to test multi-value aggregation from ValueMetric.
+ e.writeLong(elapsedMillis);
+ e.writeLong(clockDiffMillis);
+ e.writeInt(1 /* always set */);
+ pulledData.add(e);
+
+ if (mDebugElapsedClockPullCount % 2 == 1) {
+ StatsLogEventWrapper e2 = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e2.writeLong(mDebugElapsedClockPullCount);
+ e2.writeLong(elapsedMillis);
+ // Log it twice to be able to test multi-value aggregation from ValueMetric.
+ e2.writeLong(elapsedMillis);
+ e2.writeLong(clockDiffMillis);
+ e2.writeInt(2 /* set on odd pulls */);
+ pulledData.add(e2);
+ }
+
+ mDebugElapsedClockPullCount++;
+ mDebugElapsedClockPreviousValue = elapsedMillis;
+ }
+
+ private void pullDebugFailingElapsedClock(int tagId,
+ long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ final long elapsedMillis = SystemClock.elapsedRealtime();
+ // Fails every 5 buckets.
+ if (mDebugFailingElapsedClockPullCount++ % 5 == 0) {
+ mDebugFailingElapsedClockPreviousValue = elapsedMillis;
+ throw new RuntimeException("Failing debug elapsed clock");
+ }
+
+ e.writeLong(mDebugFailingElapsedClockPullCount);
+ e.writeLong(elapsedMillis);
+ // Log it twice to be able to test multi-value aggregation from ValueMetric.
+ e.writeLong(elapsedMillis);
+ e.writeLong(mDebugFailingElapsedClockPreviousValue == 0
+ ? 0 : elapsedMillis - mDebugFailingElapsedClockPreviousValue);
+ mDebugFailingElapsedClockPreviousValue = elapsedMillis;
+ pulledData.add(e);
+ }
+
+ private void pullDangerousPermissionState(long elapsedNanos, final long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ PackageManager pm = mContext.getPackageManager();
+
+ List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
+
+ int numUsers = users.size();
+ for (int userNum = 0; userNum < numUsers; userNum++) {
+ UserHandle user = users.get(userNum).getUserHandle();
+
+ List<PackageInfo> pkgs = pm.getInstalledPackagesAsUser(
+ PackageManager.GET_PERMISSIONS, user.getIdentifier());
+
+ int numPkgs = pkgs.size();
+ for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) {
+ PackageInfo pkg = pkgs.get(pkgNum);
+
+ if (pkg.requestedPermissions == null) {
+ continue;
+ }
+
+ int numPerms = pkg.requestedPermissions.length;
+ for (int permNum = 0; permNum < numPerms; permNum++) {
+ String permName = pkg.requestedPermissions[permNum];
+
+ PermissionInfo permissionInfo;
+ int permissionFlags = 0;
+ try {
+ permissionInfo = pm.getPermissionInfo(permName, 0);
+ permissionFlags =
+ pm.getPermissionFlags(permName, pkg.packageName, user);
+
+ } catch (PackageManager.NameNotFoundException ignored) {
+ continue;
+ }
+
+ if (permissionInfo.getProtection() != PROTECTION_DANGEROUS) {
+ continue;
+ }
+
+ StatsLogEventWrapper e = new StatsLogEventWrapper(
+ StatsLog.DANGEROUS_PERMISSION_STATE, elapsedNanos, wallClockNanos);
+
+ e.writeString(permName);
+ e.writeInt(pkg.applicationInfo.uid);
+ e.writeString(pkg.packageName);
+ e.writeBoolean((pkg.requestedPermissionsFlags[permNum]
+ & REQUESTED_PERMISSION_GRANTED) != 0);
+ e.writeInt(permissionFlags);
+
+ pulledData.add(e);
+ }
+ }
+ }
+ } catch (Throwable t) {
+ Log.e(TAG, "Could not read permissions", t);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void pullAppOps(long elapsedNanos, final long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+
+ CompletableFuture<HistoricalOps> ops = new CompletableFuture<>();
+ HistoricalOpsRequest histOpsRequest =
+ new HistoricalOpsRequest.Builder(
+ Instant.now().minus(1, ChronoUnit.HOURS).toEpochMilli(),
+ Long.MAX_VALUE).build();
+ appOps.getHistoricalOps(histOpsRequest, mContext.getMainExecutor(), ops::complete);
+
+ HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
+ TimeUnit.MILLISECONDS);
+
+ for (int uidIdx = 0; uidIdx < histOps.getUidCount(); uidIdx++) {
+ final HistoricalUidOps uidOps = histOps.getUidOpsAt(uidIdx);
+ final int uid = uidOps.getUid();
+ for (int pkgIdx = 0; pkgIdx < uidOps.getPackageCount(); pkgIdx++) {
+ final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(pkgIdx);
+ for (int opIdx = 0; opIdx < packageOps.getOpCount(); opIdx++) {
+ final AppOpsManager.HistoricalOp op = packageOps.getOpAt(opIdx);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(StatsLog.APP_OPS,
+ elapsedNanos, wallClockNanos);
+
+ e.writeInt(uid);
+ e.writeString(packageOps.getPackageName());
+ e.writeInt(op.getOpCode());
+ e.writeLong(op.getForegroundAccessCount(OP_FLAGS_ALL_TRUSTED));
+ e.writeLong(op.getBackgroundAccessCount(OP_FLAGS_ALL_TRUSTED));
+ e.writeLong(op.getForegroundRejectCount(OP_FLAGS_ALL_TRUSTED));
+ e.writeLong(op.getBackgroundRejectCount(OP_FLAGS_ALL_TRUSTED));
+ e.writeLong(op.getForegroundAccessDuration(OP_FLAGS_ALL_TRUSTED));
+ e.writeLong(op.getBackgroundAccessDuration(OP_FLAGS_ALL_TRUSTED));
+ pulledData.add(e);
+ }
+ }
+ }
+ } catch (Throwable t) {
+ Log.e(TAG, "Could not read appops", t);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+
+ /**
+ * Add a RoleHolder atom for each package that holds a role.
+ *
+ * @param elapsedNanos the time since boot
+ * @param wallClockNanos the time on the clock
+ * @param pulledData the data sink to write to
+ */
+ private void pullRoleHolders(long elapsedNanos, final long wallClockNanos,
+ @NonNull List<StatsLogEventWrapper> pulledData) {
+ long callingToken = Binder.clearCallingIdentity();
+ try {
+ PackageManager pm = mContext.getPackageManager();
+ RoleManagerInternal rmi = LocalServices.getService(RoleManagerInternal.class);
+
+ List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
+
+ int numUsers = users.size();
+ for (int userNum = 0; userNum < numUsers; userNum++) {
+ int userId = users.get(userNum).getUserHandle().getIdentifier();
+
+ ArrayMap<String, ArraySet<String>> roles = rmi.getRolesAndHolders(
+ userId);
+
+ int numRoles = roles.size();
+ for (int roleNum = 0; roleNum < numRoles; roleNum++) {
+ String roleName = roles.keyAt(roleNum);
+ ArraySet<String> holders = roles.valueAt(roleNum);
+
+ int numHolders = holders.size();
+ for (int holderNum = 0; holderNum < numHolders; holderNum++) {
+ String holderName = holders.valueAt(holderNum);
+
+ PackageInfo pkg;
+ try {
+ pkg = pm.getPackageInfoAsUser(holderName, 0, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Role holder " + holderName + " not found");
+ return;
+ }
+
+ StatsLogEventWrapper e = new StatsLogEventWrapper(StatsLog.ROLE_HOLDER,
+ elapsedNanos, wallClockNanos);
+ e.writeInt(pkg.applicationInfo.uid);
+ e.writeString(holderName);
+ e.writeString(roleName);
+ pulledData.add(e);
+ }
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ }
+
+ private void pullTimeZoneDataInfo(int tagId,
+ long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+ String tzDbVersion = "Unknown";
+ try {
+ tzDbVersion = android.icu.util.TimeZone.getTZDataVersion();
+ } catch (Exception e) {
+ Log.e(TAG, "Getting tzdb version failed: ", e);
+ }
+
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeString(tzDbVersion);
+ pulledData.add(e);
+ }
+
+ private void pullExternalStorageInfo(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ StorageManager storageManager = mContext.getSystemService(StorageManager.class);
+ if (storageManager != null) {
+ List<VolumeInfo> volumes = storageManager.getVolumes();
+ for (VolumeInfo vol : volumes) {
+ final String envState = VolumeInfo.getEnvironmentForState(vol.getState());
+ final DiskInfo diskInfo = vol.getDisk();
+ if (diskInfo != null) {
+ if (envState.equals(Environment.MEDIA_MOUNTED)) {
+ // Get the type of the volume, if it is adoptable or portable.
+ int volumeType = StatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__OTHER;
+ if (vol.getType() == TYPE_PUBLIC) {
+ volumeType = StatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__PUBLIC;
+ } else if (vol.getType() == TYPE_PRIVATE) {
+ volumeType = StatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__PRIVATE;
+ }
+ // Get the type of external storage inserted in the device (sd cards,
+ // usb, etc)
+ int externalStorageType;
+ if (diskInfo.isSd()) {
+ externalStorageType = StorageEnums.SD_CARD;
+ } else if (diskInfo.isUsb()) {
+ externalStorageType = StorageEnums.USB;
+ } else {
+ externalStorageType = StorageEnums.OTHER;
+ }
+ StatsLogEventWrapper e =
+ new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(externalStorageType);
+ e.writeInt(volumeType);
+ e.writeLong(diskInfo.size);
+ pulledData.add(e);
+ }
+ }
+ }
+ }
+ }
+
+ private void pullAppsOnExternalStorageInfo(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ PackageManager pm = mContext.getPackageManager();
+ StorageManager storage = mContext.getSystemService(StorageManager.class);
+ List<ApplicationInfo> apps = pm.getInstalledApplications(/* flags = */ 0);
+ for (ApplicationInfo appInfo : apps) {
+ UUID storageUuid = appInfo.storageUuid;
+ if (storageUuid != null) {
+ VolumeInfo volumeInfo = storage.findVolumeByUuid(appInfo.storageUuid.toString());
+ if (volumeInfo != null) {
+ DiskInfo diskInfo = volumeInfo.getDisk();
+ if (diskInfo != null) {
+ int externalStorageType = -1;
+ if (diskInfo.isSd()) {
+ externalStorageType = StorageEnums.SD_CARD;
+ } else if (diskInfo.isUsb()) {
+ externalStorageType = StorageEnums.USB;
+ } else if (appInfo.isExternal()) {
+ externalStorageType = StorageEnums.OTHER;
+ }
+ // App is installed on external storage.
+ if (externalStorageType != -1) {
+ StatsLogEventWrapper e =
+ new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(externalStorageType);
+ e.writeString(appInfo.packageName);
+ pulledData.add(e);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void pullFaceSettings(int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ long callingToken = Binder.clearCallingIdentity();
+ try {
+ List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
+ int numUsers = users.size();
+ for (int userNum = 0; userNum < numUsers; userNum++) {
+ int userId = users.get(userNum).getUserHandle().getIdentifier();
+
+ StatsLogEventWrapper e =
+ new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeBoolean(Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED, 1,
+ userId) != 0);
+ e.writeBoolean(Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD,
+ 0, userId) != 0);
+ e.writeBoolean(Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED, 1,
+ userId) != 0);
+ e.writeBoolean(Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.FACE_UNLOCK_APP_ENABLED, 1,
+ userId) != 0);
+ e.writeBoolean(Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, 0,
+ userId) != 0);
+ e.writeBoolean(Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.FACE_UNLOCK_DIVERSITY_REQUIRED, 1,
+ userId) != 0);
+
+ pulledData.add(e);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
+ }
+
+ /**
+ * Pulls various data.
+ */
+ @Override // Binder call
+ public StatsLogEventWrapper[] pullData(int tagId) {
+ enforceCallingPermission();
+ if (DEBUG) {
+ Slog.d(TAG, "Pulling " + tagId);
+ }
+ List<StatsLogEventWrapper> ret = new ArrayList<>();
+ long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ long wallClockNanos = SystemClock.currentTimeMicro() * 1000L;
+ switch (tagId) {
+ case StatsLog.WIFI_BYTES_TRANSFER: {
+ pullWifiBytesTransfer(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.MOBILE_BYTES_TRANSFER: {
+ pullMobileBytesTransfer(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: {
+ pullWifiBytesTransferByFgBg(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: {
+ pullMobileBytesTransferByFgBg(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.BLUETOOTH_BYTES_TRANSFER: {
+ pullBluetoothBytesTransfer(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.KERNEL_WAKELOCK: {
+ pullKernelWakelock(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.CPU_TIME_PER_FREQ: {
+ pullCpuTimePerFreq(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.CPU_TIME_PER_UID: {
+ pullKernelUidCpuTime(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.CPU_TIME_PER_UID_FREQ: {
+ pullKernelUidCpuFreqTime(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.CPU_CLUSTER_TIME: {
+ pullKernelUidCpuClusterTime(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.CPU_ACTIVE_TIME: {
+ pullKernelUidCpuActiveTime(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.WIFI_ACTIVITY_INFO: {
+ pullWifiActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.MODEM_ACTIVITY_INFO: {
+ pullModemActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.BLUETOOTH_ACTIVITY_INFO: {
+ pullBluetoothActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.SYSTEM_UPTIME: {
+ pullSystemUpTime(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.SYSTEM_ELAPSED_REALTIME: {
+ pullSystemElapsedRealtime(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.PROCESS_MEMORY_STATE: {
+ pullProcessMemoryState(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.PROCESS_MEMORY_HIGH_WATER_MARK: {
+ pullProcessMemoryHighWaterMark(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.PROCESS_MEMORY_SNAPSHOT: {
+ pullProcessMemorySnapshot(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.SYSTEM_ION_HEAP_SIZE: {
+ pullSystemIonHeapSize(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.PROCESS_SYSTEM_ION_HEAP_SIZE: {
+ pullProcessSystemIonHeapSize(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.BINDER_CALLS: {
+ pullBinderCallsStats(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.BINDER_CALLS_EXCEPTIONS: {
+ pullBinderCallsStatsExceptions(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.LOOPER_STATS: {
+ pullLooperStats(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.DISK_STATS: {
+ pullDiskStats(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.DIRECTORY_USAGE: {
+ pullDirectoryUsage(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.APP_SIZE: {
+ pullAppSize(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.CATEGORY_SIZE: {
+ pullCategorySize(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.NUM_FINGERPRINTS_ENROLLED: {
+ pullNumBiometricsEnrolled(BiometricsProtoEnums.MODALITY_FINGERPRINT, tagId,
+ elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.NUM_FACES_ENROLLED: {
+ pullNumBiometricsEnrolled(BiometricsProtoEnums.MODALITY_FACE, tagId, elapsedNanos,
+ wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.PROC_STATS: {
+ pullProcessStats(ProcessStats.REPORT_ALL, tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.PROC_STATS_PKG_PROC: {
+ pullProcessStats(ProcessStats.REPORT_PKG_PROC_STATS, tagId, elapsedNanos,
+ wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.DISK_IO: {
+ pullDiskIo(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.POWER_PROFILE: {
+ pullPowerProfile(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.BUILD_INFORMATION: {
+ pullBuildInformation(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.PROCESS_CPU_TIME: {
+ pullProcessCpuTime(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.CPU_TIME_PER_THREAD_FREQ: {
+ pullCpuTimePerThreadFreq(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.DEVICE_CALCULATED_POWER_USE: {
+ pullDeviceCalculatedPowerUse(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.DEVICE_CALCULATED_POWER_BLAME_UID: {
+ pullDeviceCalculatedPowerBlameUid(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER: {
+ pullDeviceCalculatedPowerBlameOther(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.TEMPERATURE: {
+ pullTemperature(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.COOLING_DEVICE: {
+ pullCoolingDevices(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.DEBUG_ELAPSED_CLOCK: {
+ pullDebugElapsedClock(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.DEBUG_FAILING_ELAPSED_CLOCK: {
+ pullDebugFailingElapsedClock(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.ROLE_HOLDER: {
+ pullRoleHolders(elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.DANGEROUS_PERMISSION_STATE: {
+ pullDangerousPermissionState(elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.TIME_ZONE_DATA_INFO: {
+ pullTimeZoneDataInfo(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.EXTERNAL_STORAGE_INFO: {
+ pullExternalStorageInfo(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.APPS_ON_EXTERNAL_STORAGE_INFO: {
+ pullAppsOnExternalStorageInfo(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.FACE_SETTINGS: {
+ pullFaceSettings(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ case StatsLog.APP_OPS: {
+ pullAppOps(elapsedNanos, wallClockNanos, ret);
+ break;
+ }
+ default:
+ Slog.w(TAG, "No such tagId data as " + tagId);
+ return null;
+ }
+ return ret.toArray(new StatsLogEventWrapper[ret.size()]);
+ }
+
+ @Override // Binder call
+ public void statsdReady() {
+ enforceCallingPermission();
+ if (DEBUG) {
+ Slog.d(TAG, "learned that statsdReady");
+ }
+ sayHiToStatsd(); // tell statsd that we're ready too and link to it
+ mContext.sendBroadcastAsUser(new Intent(StatsManager.ACTION_STATSD_STARTED)
+ .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND),
+ UserHandle.SYSTEM, android.Manifest.permission.DUMP);
+ }
+
+ @Override
+ public void triggerUidSnapshot() {
+ enforceCallingPermission();
+ synchronized (sStatsdLock) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ informAllUidsLocked(mContext);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to trigger uid snapshot.", e);
+ } finally {
+ restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ private void enforceCallingPermission() {
+ if (Binder.getCallingPid() == Process.myPid()) {
+ return;
+ }
+ mContext.enforceCallingPermission(android.Manifest.permission.STATSCOMPANION, null);
+ }
+
+ // Lifecycle and related code
+
+ /**
+ * Fetches the statsd IBinder service
+ */
+ private static IStatsManager fetchStatsdService() {
+ return IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
+ }
+
+ public static final class Lifecycle extends SystemService {
+ private StatsCompanionService mStatsCompanionService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mStatsCompanionService = new StatsCompanionService(getContext());
+ try {
+ publishBinderService(Context.STATS_COMPANION_SERVICE,
+ mStatsCompanionService);
+ if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_COMPANION_SERVICE);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to publishBinderService", e);
+ }
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ super.onBootPhase(phase);
+ if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+ mStatsCompanionService.systemReady();
+ }
+ }
+ }
+
+ /**
+ * Now that the android system is ready, StatsCompanion is ready too, so inform statsd.
+ */
+ private void systemReady() {
+ if (DEBUG) Slog.d(TAG, "Learned that systemReady");
+ sayHiToStatsd();
+ }
+
+ /**
+ * Tells statsd that statscompanion is ready. If the binder call returns, link to
+ * statsd.
+ */
+ private void sayHiToStatsd() {
+ synchronized (sStatsdLock) {
+ if (sStatsd != null) {
+ Slog.e(TAG, "Trying to fetch statsd, but it was already fetched",
+ new IllegalStateException(
+ "sStatsd is not null when being fetched"));
+ return;
+ }
+ sStatsd = fetchStatsdService();
+ if (sStatsd == null) {
+ Slog.i(TAG,
+ "Could not yet find statsd to tell it that StatsCompanion is "
+ + "alive.");
+ return;
+ }
+ if (DEBUG) Slog.d(TAG, "Saying hi to statsd");
+ try {
+ sStatsd.statsCompanionReady();
+ // If the statsCompanionReady two-way binder call returns, link to statsd.
+ try {
+ sStatsd.asBinder().linkToDeath(new StatsdDeathRecipient(), 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e);
+ forgetEverythingLocked();
+ }
+ // Setup broadcast receiver for updates.
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REPLACED);
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ mContext.registerReceiverAsUser(mAppUpdateReceiver, UserHandle.ALL, filter,
+ null,
+ null);
+
+ // Setup receiver for user initialize (which happens once for a new user)
+ // and
+ // if a user is removed.
+ filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE);
+ filter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiverAsUser(mUserUpdateReceiver, UserHandle.ALL,
+ filter, null, null);
+
+ // Setup receiver for device reboots or shutdowns.
+ filter = new IntentFilter(Intent.ACTION_REBOOT);
+ filter.addAction(Intent.ACTION_SHUTDOWN);
+ mContext.registerReceiverAsUser(
+ mShutdownEventReceiver, UserHandle.ALL, filter, null, null);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // Pull the latest state of UID->app name, version mapping when
+ // statsd starts.
+ informAllUidsLocked(mContext);
+ } finally {
+ restoreCallingIdentity(token);
+ }
+ Slog.i(TAG, "Told statsd that StatsCompanionService is alive.");
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
+ forgetEverythingLocked();
+ }
+ }
+ }
+
+ private class StatsdDeathRecipient implements IBinder.DeathRecipient {
+ @Override
+ public void binderDied() {
+ Slog.i(TAG, "Statsd is dead - erase all my knowledge.");
+ synchronized (sStatsdLock) {
+ long now = SystemClock.elapsedRealtime();
+ for (Long timeMillis : mDeathTimeMillis) {
+ long ageMillis = now - timeMillis;
+ if (ageMillis > MILLIS_IN_A_DAY) {
+ mDeathTimeMillis.remove(timeMillis);
+ }
+ }
+ for (Long timeMillis : mDeletedFiles.keySet()) {
+ long ageMillis = now - timeMillis;
+ if (ageMillis > MILLIS_IN_A_DAY * 7) {
+ mDeletedFiles.remove(timeMillis);
+ }
+ }
+ mDeathTimeMillis.add(now);
+ if (mDeathTimeMillis.size() >= DEATH_THRESHOLD) {
+ mDeathTimeMillis.clear();
+ File[] configs = FileUtils.listFilesOrEmpty(new File(CONFIG_DIR));
+ if (configs.length > 0) {
+ String fileName = configs[0].getName();
+ if (configs[0].delete()) {
+ mDeletedFiles.put(now, fileName);
+ }
+ }
+ }
+ forgetEverythingLocked();
+ }
+ }
+ }
+
+ @GuardedBy("StatsCompanionService.sStatsdLock")
+ private void forgetEverythingLocked() {
+ sStatsd = null;
+ mContext.unregisterReceiver(mAppUpdateReceiver);
+ mContext.unregisterReceiver(mUserUpdateReceiver);
+ mContext.unregisterReceiver(mShutdownEventReceiver);
+ cancelAnomalyAlarm();
+ cancelPullingAlarm();
+
+ BinderCallsStatsService.Internal binderStats =
+ LocalServices.getService(BinderCallsStatsService.Internal.class);
+ if (binderStats != null) {
+ binderStats.reset();
+ }
+
+ LooperStats looperStats = LocalServices.getService(LooperStats.class);
+ if (looperStats != null) {
+ looperStats.reset();
+ }
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
+
+ synchronized (sStatsdLock) {
+ writer.println(
+ "Number of configuration files deleted: " + mDeletedFiles.size());
+ if (mDeletedFiles.size() > 0) {
+ writer.println(" timestamp, deleted file name");
+ }
+ long lastBootMillis =
+ SystemClock.currentThreadTimeMillis() - SystemClock.elapsedRealtime();
+ for (Long elapsedMillis : mDeletedFiles.keySet()) {
+ long deletionMillis = lastBootMillis + elapsedMillis;
+ writer.println(
+ " " + deletionMillis + ", " + mDeletedFiles.get(elapsedMillis));
+ }
+ }
+ }
+
+ // Thermal event received from vendor thermal management subsystem
+ private static final class ThermalEventListener extends IThermalEventListener.Stub {
+ @Override
+ public void notifyThrottling(Temperature temp) {
+ StatsLog.write(StatsLog.THERMAL_THROTTLING_SEVERITY_STATE_CHANGED, temp.getType(),
+ temp.getName(), (int) (temp.getValue() * 10), temp.getStatus());
+ }
+ }
+
+ private static final class ConnectivityStatsCallback extends
+ ConnectivityManager.NetworkCallback {
+ @Override
+ public void onAvailable(Network network) {
+ StatsLog.write(StatsLog.CONNECTIVITY_STATE_CHANGED, network.netId,
+ StatsLog.CONNECTIVITY_STATE_CHANGED__STATE__CONNECTED);
+ }
+
+ @Override
+ public void onLost(Network network) {
+ StatsLog.write(StatsLog.CONNECTIVITY_STATE_CHANGED, network.netId,
+ StatsLog.CONNECTIVITY_STATE_CHANGED__STATE__DISCONNECTED);
+ }
+ }
+}
diff --git a/api/current.txt b/api/current.txt
index 21753ad200b5..6c8d82e1270c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2833,6 +2833,7 @@ package android.accessibilityservice {
method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
method @NonNull public final android.accessibilityservice.AccessibilityService.SoftKeyboardController getSoftKeyboardController();
method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
+ method @NonNull public final android.util.SparseArray<java.util.List<android.view.accessibility.AccessibilityWindowInfo>> getWindowsOnAllDisplays();
method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public final android.os.IBinder onBind(android.content.Intent);
method @Deprecated protected boolean onGesture(int);
@@ -2945,6 +2946,7 @@ package android.accessibilityservice {
field public static final int FEEDBACK_SPOKEN = 1; // 0x1
field public static final int FEEDBACK_VISUAL = 8; // 0x8
field public static final int FLAG_ENABLE_ACCESSIBILITY_VOLUME = 128; // 0x80
+ field public static final int FLAG_HANDLE_SHORTCUT = 2048; // 0x800
field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
field public static final int FLAG_REPORT_VIEW_IDS = 16; // 0x10
field public static final int FLAG_REQUEST_ACCESSIBILITY_BUTTON = 256; // 0x100
@@ -3880,6 +3882,7 @@ package android.app {
method public void setTitle(CharSequence);
method public void setTitle(int);
method @Deprecated public void setTitleColor(int);
+ method public boolean setTranslucent(boolean);
method public void setTurnScreenOn(boolean);
method public void setVisible(boolean);
method public final void setVolumeControlStream(int);
@@ -4277,23 +4280,24 @@ package android.app {
method @Deprecated public int checkOp(@NonNull String, int, @NonNull String);
method @Deprecated public int checkOpNoThrow(@NonNull String, int, @NonNull String);
method public void checkPackage(int, @NonNull String);
- method public void finishOp(@NonNull String, int, @NonNull String);
+ method @Deprecated public void finishOp(@NonNull String, int, @NonNull String);
+ method public void finishOp(@NonNull String, int, @NonNull String, @Nullable String);
method public boolean isOpActive(@NonNull String, int, @NonNull String);
method @Deprecated public int noteOp(@NonNull String, int, @NonNull String);
- method public int noteOp(@NonNull String, int, @Nullable String, @Nullable String);
+ method public int noteOp(@NonNull String, int, @Nullable String, @Nullable String, @Nullable String);
method @Deprecated public int noteOpNoThrow(@NonNull String, int, @NonNull String);
- method public int noteOpNoThrow(@NonNull String, int, @NonNull String, @Nullable String);
+ method public int noteOpNoThrow(@NonNull String, int, @NonNull String, @Nullable String, @Nullable String);
method @Deprecated public int noteProxyOp(@NonNull String, @NonNull String);
- method public int noteProxyOp(@NonNull String, @Nullable String, int, @Nullable String);
+ method public int noteProxyOp(@NonNull String, @Nullable String, int, @Nullable String, @Nullable String);
method @Deprecated public int noteProxyOpNoThrow(@NonNull String, @NonNull String);
method @Deprecated public int noteProxyOpNoThrow(@NonNull String, @Nullable String, int);
- method public int noteProxyOpNoThrow(@NonNull String, @Nullable String, int, @Nullable String);
+ method public int noteProxyOpNoThrow(@NonNull String, @Nullable String, int, @Nullable String, @Nullable String);
method public static String permissionToOp(String);
method public void setNotedAppOpsCollector(@Nullable android.app.AppOpsManager.AppOpsCollector);
method @Deprecated public int startOp(@NonNull String, int, @NonNull String);
- method public int startOp(@NonNull String, int, @Nullable String, @Nullable String);
+ method public int startOp(@NonNull String, int, @Nullable String, @NonNull String, @Nullable String);
method @Deprecated public int startOpNoThrow(@NonNull String, int, @NonNull String);
- method public int startOpNoThrow(@NonNull String, int, @NonNull String, @Nullable String);
+ method public int startOpNoThrow(@NonNull String, int, @NonNull String, @NonNull String, @Nullable String);
method public void startWatchingActive(@NonNull String[], @NonNull java.util.concurrent.Executor, @NonNull android.app.AppOpsManager.OnOpActiveChangedListener);
method public void startWatchingMode(@NonNull String, @Nullable String, @NonNull android.app.AppOpsManager.OnOpChangedListener);
method public void startWatchingMode(@NonNull String, @Nullable String, int, @NonNull android.app.AppOpsManager.OnOpChangedListener);
@@ -4475,12 +4479,13 @@ package android.app {
public final class AsyncNotedAppOp implements android.os.Parcelable {
method public int describeContents();
+ method @Nullable public String getFeatureId();
method @NonNull public String getMessage();
method @Nullable public String getNotingPackageName();
method @IntRange(from=0) public int getNotingUid();
method @NonNull public String getOp();
method @IntRange(from=0) public long getTime();
- method public void writeToParcel(android.os.Parcel, int);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.AsyncNotedAppOp> CREATOR;
}
@@ -5871,6 +5876,7 @@ package android.app {
method public android.service.notification.StatusBarNotification[] getActiveNotifications();
method public android.app.AutomaticZenRule getAutomaticZenRule(String);
method public java.util.Map<java.lang.String,android.app.AutomaticZenRule> getAutomaticZenRules();
+ method @Nullable public android.app.NotificationManager.Policy getConsolidatedNotificationPolicy();
method public final int getCurrentInterruptionFilter();
method public int getImportance();
method public android.app.NotificationChannel getNotificationChannel(String);
@@ -6298,6 +6304,7 @@ package android.app {
}
public final class SyncNotedAppOp {
+ method @Nullable public String getFeatureId();
method @NonNull public String getOp();
}
@@ -6362,6 +6369,7 @@ package android.app {
method public android.view.WindowAnimationFrameStats getWindowAnimationFrameStats();
method public android.view.WindowContentFrameStats getWindowContentFrameStats(int);
method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
+ method @NonNull public android.util.SparseArray<java.util.List<android.view.accessibility.AccessibilityWindowInfo>> getWindowsOnAllDisplays();
method public void grantRuntimePermission(String, String);
method public void grantRuntimePermissionAsUser(String, String, android.os.UserHandle);
method public boolean injectInputEvent(android.view.InputEvent, boolean);
@@ -6799,6 +6807,7 @@ package android.app.admin {
method public boolean isResetPasswordTokenActive(android.content.ComponentName);
method public boolean isSecurityLoggingEnabled(@Nullable android.content.ComponentName);
method public boolean isUninstallBlocked(@Nullable android.content.ComponentName, String);
+ method public boolean isUniqueDeviceAttestationSupported();
method public boolean isUsingUnifiedPassword(@NonNull android.content.ComponentName);
method public void lockNow();
method public void lockNow(int);
@@ -6842,6 +6851,7 @@ package android.app.admin {
method public boolean setKeyPairCertificate(@Nullable android.content.ComponentName, @NonNull String, @NonNull java.util.List<java.security.cert.Certificate>, boolean);
method public boolean setKeyguardDisabled(@NonNull android.content.ComponentName, boolean);
method public void setKeyguardDisabledFeatures(@NonNull android.content.ComponentName, int);
+ method public void setLocationEnabled(@NonNull android.content.ComponentName, boolean);
method public void setLockTaskFeatures(@NonNull android.content.ComponentName, int);
method public void setLockTaskPackages(@NonNull android.content.ComponentName, @NonNull String[]) throws java.lang.SecurityException;
method public void setLogoutEnabled(@NonNull android.content.ComponentName, boolean);
@@ -6980,6 +6990,7 @@ package android.app.admin {
field public static final int FLAG_PARENT_CAN_ACCESS_MANAGED = 1; // 0x1
field public static final int ID_TYPE_BASE_INFO = 1; // 0x1
field public static final int ID_TYPE_IMEI = 4; // 0x4
+ field public static final int ID_TYPE_INDIVIDUAL_ATTESTATION = 16; // 0x10
field public static final int ID_TYPE_MEID = 8; // 0x8
field public static final int ID_TYPE_SERIAL = 2; // 0x2
field public static final int INSTALLKEY_REQUEST_CREDENTIALS_ACCESS = 1; // 0x1
@@ -9479,6 +9490,7 @@ package android.content {
method @Nullable public android.database.Cursor query(@NonNull android.net.Uri, @Nullable String[], @Nullable String, @Nullable String[], @Nullable String, @Nullable android.os.CancellationSignal);
method @Nullable public android.database.Cursor query(@NonNull android.net.Uri, @Nullable String[], @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal);
method public boolean refresh(android.net.Uri, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal);
+ method @NonNull public final android.content.Context requireContext();
method public final void restoreCallingIdentity(@NonNull android.content.ContentProvider.CallingIdentity);
method protected final void setPathPermissions(@Nullable android.content.pm.PathPermission[]);
method protected final void setReadPermission(@Nullable String);
@@ -9675,6 +9687,7 @@ package android.content {
field public static final String QUERY_ARG_SORT_COLLATION = "android:query-arg-sort-collation";
field public static final String QUERY_ARG_SORT_COLUMNS = "android:query-arg-sort-columns";
field public static final String QUERY_ARG_SORT_DIRECTION = "android:query-arg-sort-direction";
+ field public static final String QUERY_ARG_SORT_LOCALE = "android:query-arg-sort-locale";
field public static final String QUERY_ARG_SQL_SELECTION = "android:query-arg-sql-selection";
field public static final String QUERY_ARG_SQL_SELECTION_ARGS = "android:query-arg-sql-selection-args";
field public static final String QUERY_ARG_SQL_SORT_ORDER = "android:query-arg-sql-sort-order";
@@ -9770,6 +9783,7 @@ package android.content {
method public abstract android.content.Context createContextForSplit(String) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.content.Context createDeviceProtectedStorageContext();
method public abstract android.content.Context createDisplayContext(@NonNull android.view.Display);
+ method @NonNull public android.content.Context createFeatureContext(@Nullable String);
method public abstract android.content.Context createPackageContext(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract String[] databaseList();
method public abstract boolean deleteDatabase(String);
@@ -9800,7 +9814,8 @@ package android.content {
method public abstract java.io.File[] getExternalCacheDirs();
method @Nullable public abstract java.io.File getExternalFilesDir(@Nullable String);
method public abstract java.io.File[] getExternalFilesDirs(String);
- method public abstract java.io.File[] getExternalMediaDirs();
+ method @Deprecated public abstract java.io.File[] getExternalMediaDirs();
+ method @Nullable public String getFeatureId();
method public abstract java.io.File getFileStreamPath(String);
method public abstract java.io.File getFilesDir();
method public java.util.concurrent.Executor getMainExecutor();
@@ -10411,6 +10426,7 @@ package android.content {
field public static final String ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND";
field @Deprecated public static final String ACTION_WALLPAPER_CHANGED = "android.intent.action.WALLPAPER_CHANGED";
field public static final String ACTION_WEB_SEARCH = "android.intent.action.WEB_SEARCH";
+ field public static final String CATEGORY_ACCESSIBILITY_SHORTCUT_TARGET = "android.intent.category.ACCESSIBILITY_SHORTCUT_TARGET";
field public static final String CATEGORY_ALTERNATIVE = "android.intent.category.ALTERNATIVE";
field public static final String CATEGORY_APP_BROWSER = "android.intent.category.APP_BROWSER";
field public static final String CATEGORY_APP_CALCULATOR = "android.intent.category.APP_CALCULATOR";
@@ -11392,7 +11408,7 @@ package android.content.pm {
method public abstract void onPackageRemoved(String, android.os.UserHandle);
method public abstract void onPackagesAvailable(String[], android.os.UserHandle, boolean);
method public void onPackagesSuspended(String[], android.os.UserHandle);
- method public void onPackagesSuspended(String[], android.os.UserHandle, @Nullable android.os.Bundle);
+ method @Deprecated public void onPackagesSuspended(String[], android.os.UserHandle, @Nullable android.os.Bundle);
method public abstract void onPackagesUnavailable(String[], android.os.UserHandle, boolean);
method public void onPackagesUnsuspended(String[], android.os.UserHandle);
method public void onShortcutsChanged(@NonNull String, @NonNull java.util.List<android.content.pm.ShortcutInfo>, @NonNull android.os.UserHandle);
@@ -11905,6 +11921,7 @@ package android.content.pm {
field public static final int SIGNATURE_NO_MATCH = -3; // 0xfffffffd
field public static final int SIGNATURE_SECOND_NOT_SIGNED = -2; // 0xfffffffe
field public static final int SIGNATURE_UNKNOWN_PACKAGE = -4; // 0xfffffffc
+ field public static final int SYNCHRONOUS = 2; // 0x2
field public static final int VERIFICATION_ALLOW = 1; // 0x1
field public static final int VERIFICATION_REJECT = -1; // 0xffffffff
field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff
@@ -12395,6 +12412,8 @@ package android.content.res {
public class Resources {
ctor @Deprecated public Resources(android.content.res.AssetManager, android.util.DisplayMetrics, android.content.res.Configuration);
+ method public void addLoader(@NonNull android.content.res.loader.ResourceLoader, @NonNull android.content.res.loader.ResourcesProvider, @IntRange(from=0) int);
+ method public int addLoader(@NonNull android.content.res.loader.ResourceLoader, @NonNull android.content.res.loader.ResourcesProvider);
method public final void finishPreloading();
method public final void flushLayoutCache();
method @NonNull public android.content.res.XmlResourceParser getAnimation(@AnimatorRes @AnimRes int) throws android.content.res.Resources.NotFoundException;
@@ -12421,6 +12440,7 @@ package android.content.res {
method @NonNull public int[] getIntArray(@ArrayRes int) throws android.content.res.Resources.NotFoundException;
method public int getInteger(@IntegerRes int) throws android.content.res.Resources.NotFoundException;
method @NonNull public android.content.res.XmlResourceParser getLayout(@LayoutRes int) throws android.content.res.Resources.NotFoundException;
+ method @NonNull public java.util.List<android.util.Pair<android.content.res.loader.ResourceLoader,android.content.res.loader.ResourcesProvider>> getLoaders();
method @Deprecated public android.graphics.Movie getMovie(@RawRes int) throws android.content.res.Resources.NotFoundException;
method @NonNull public String getQuantityString(@PluralsRes int, int, java.lang.Object...) throws android.content.res.Resources.NotFoundException;
method @NonNull public String getQuantityString(@PluralsRes int, int) throws android.content.res.Resources.NotFoundException;
@@ -12448,6 +12468,8 @@ package android.content.res {
method public android.content.res.AssetFileDescriptor openRawResourceFd(@RawRes int) throws android.content.res.Resources.NotFoundException;
method public void parseBundleExtra(String, android.util.AttributeSet, android.os.Bundle) throws org.xmlpull.v1.XmlPullParserException;
method public void parseBundleExtras(android.content.res.XmlResourceParser, android.os.Bundle) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public int removeLoader(@NonNull android.content.res.loader.ResourceLoader);
+ method public void setLoaders(@Nullable java.util.List<android.util.Pair<android.content.res.loader.ResourceLoader,android.content.res.loader.ResourcesProvider>>);
method @Deprecated public void updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics);
field @AnyRes public static final int ID_NULL = 0; // 0x0
}
@@ -12515,6 +12537,33 @@ package android.content.res {
}
+package android.content.res.loader {
+
+ public class DirectoryResourceLoader implements android.content.res.loader.ResourceLoader {
+ ctor public DirectoryResourceLoader(@NonNull java.io.File);
+ method @Nullable public java.io.File findFile(@NonNull String);
+ method @NonNull public java.io.File getDirectory();
+ }
+
+ public interface ResourceLoader {
+ method @Nullable public default java.io.InputStream loadAsset(@NonNull String, int) throws java.io.IOException;
+ method @Nullable public default android.os.ParcelFileDescriptor loadAssetFd(@NonNull String) throws java.io.IOException;
+ method @Nullable public default android.graphics.drawable.Drawable loadDrawable(@NonNull android.util.TypedValue, int, int, @Nullable android.content.res.Resources.Theme);
+ method @Nullable public default android.content.res.XmlResourceParser loadXmlResourceParser(@NonNull String, @AnyRes int);
+ }
+
+ public final class ResourcesProvider implements java.lang.AutoCloseable java.io.Closeable {
+ method public void close();
+ method @NonNull public static android.content.res.loader.ResourcesProvider empty();
+ method @NonNull public static android.content.res.loader.ResourcesProvider loadFromApk(@NonNull android.os.ParcelFileDescriptor) throws java.io.IOException;
+ method @NonNull public static android.content.res.loader.ResourcesProvider loadFromApk(@NonNull android.os.SharedMemory) throws java.io.IOException;
+ method @NonNull public static android.content.res.loader.ResourcesProvider loadFromArsc(@NonNull android.os.ParcelFileDescriptor) throws java.io.IOException;
+ method @NonNull public static android.content.res.loader.ResourcesProvider loadFromArsc(@NonNull android.os.SharedMemory) throws java.io.IOException;
+ method @NonNull public static android.content.res.loader.ResourcesProvider loadFromSplit(@NonNull android.content.Context, @NonNull String) throws java.io.IOException;
+ }
+
+}
+
package android.database {
public abstract class AbstractCursor implements android.database.CrossProcessCursor {
@@ -13732,7 +13781,9 @@ package android.graphics {
public enum Bitmap.CompressFormat {
enum_constant public static final android.graphics.Bitmap.CompressFormat JPEG;
enum_constant public static final android.graphics.Bitmap.CompressFormat PNG;
- enum_constant public static final android.graphics.Bitmap.CompressFormat WEBP;
+ enum_constant @Deprecated public static final android.graphics.Bitmap.CompressFormat WEBP;
+ enum_constant public static final android.graphics.Bitmap.CompressFormat WEBP_LOSSLESS;
+ enum_constant public static final android.graphics.Bitmap.CompressFormat WEBP_LOSSY;
}
public enum Bitmap.Config {
@@ -23089,8 +23140,9 @@ package android.location {
public class LocationManager {
method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean addGpsStatusListener(android.location.GpsStatus.Listener);
- method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean addNmeaListener(@NonNull android.location.OnNmeaMessageListener);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean addNmeaListener(@NonNull android.location.OnNmeaMessageListener);
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean addNmeaListener(@NonNull android.location.OnNmeaMessageListener, @Nullable android.os.Handler);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean addNmeaListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.OnNmeaMessageListener);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void addProximityAlert(double, double, float, long, @NonNull android.app.PendingIntent);
method public void addTestProvider(@NonNull String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
method @Deprecated public void clearTestProviderEnabled(@NonNull String);
@@ -23098,6 +23150,7 @@ package android.location {
method @Deprecated public void clearTestProviderStatus(@NonNull String);
method @NonNull public java.util.List<java.lang.String> getAllProviders();
method @Nullable public String getBestProvider(@NonNull android.location.Criteria, boolean);
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull String, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>);
method @Nullable public String getGnssHardwareModelName();
method public int getGnssYearOfHardware();
method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public android.location.GpsStatus getGpsStatus(@Nullable android.location.GpsStatus);
@@ -23107,12 +23160,15 @@ package android.location {
method @NonNull public java.util.List<java.lang.String> getProviders(@NonNull android.location.Criteria, boolean);
method public boolean isLocationEnabled();
method public boolean isProviderEnabled(@NonNull String);
- method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback);
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback, @Nullable android.os.Handler);
- method public boolean registerGnssNavigationMessageCallback(@NonNull android.location.GnssNavigationMessage.Callback);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback);
+ method @Deprecated public boolean registerGnssNavigationMessageCallback(@NonNull android.location.GnssNavigationMessage.Callback);
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssNavigationMessageCallback(@NonNull android.location.GnssNavigationMessage.Callback, @Nullable android.os.Handler);
- method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssStatusCallback(@NonNull android.location.GnssStatus.Callback);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssNavigationMessageCallback(@NonNull java.util.concurrent.Executor, @NonNull android.location.GnssNavigationMessage.Callback);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssStatusCallback(@NonNull android.location.GnssStatus.Callback);
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssStatusCallback(@NonNull android.location.GnssStatus.Callback, @Nullable android.os.Handler);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssStatusCallback(@NonNull java.util.concurrent.Executor, @NonNull android.location.GnssStatus.Callback);
method @Deprecated public void removeGpsStatusListener(android.location.GpsStatus.Listener);
method public void removeNmeaListener(@NonNull android.location.OnNmeaMessageListener);
method @RequiresPermission(anyOf={"android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION"}, apis="..22") public void removeProximityAlert(@NonNull android.app.PendingIntent);
@@ -23121,13 +23177,15 @@ package android.location {
method public void removeUpdates(@NonNull android.app.PendingIntent);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull String, long, float, @NonNull android.location.LocationListener);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull String, long, float, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull String, long, float, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(long, float, @NonNull android.location.Criteria, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(long, float, @NonNull android.location.Criteria, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull String, long, float, @NonNull android.app.PendingIntent);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(long, float, @NonNull android.location.Criteria, @NonNull android.app.PendingIntent);
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestSingleUpdate(@NonNull String, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestSingleUpdate(@NonNull android.location.Criteria, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestSingleUpdate(@NonNull String, @NonNull android.app.PendingIntent);
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestSingleUpdate(@NonNull android.location.Criteria, @NonNull android.app.PendingIntent);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestSingleUpdate(@NonNull String, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestSingleUpdate(@NonNull android.location.Criteria, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestSingleUpdate(@NonNull String, @NonNull android.app.PendingIntent);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestSingleUpdate(@NonNull android.location.Criteria, @NonNull android.app.PendingIntent);
method public boolean sendExtraCommand(@NonNull String, @NonNull String, @Nullable android.os.Bundle);
method public void setTestProviderEnabled(@NonNull String, boolean);
method public void setTestProviderLocation(@NonNull String, @NonNull android.location.Location);
@@ -23955,6 +24013,7 @@ package android.media {
ctor public ExifInterface(@NonNull String) throws java.io.IOException;
ctor public ExifInterface(@NonNull java.io.FileDescriptor) throws java.io.IOException;
ctor public ExifInterface(@NonNull java.io.InputStream) throws java.io.IOException;
+ method @NonNull public static android.media.ExifInterface fromStandalone(@NonNull java.io.InputStream) throws java.io.IOException;
method public double getAltitude(double);
method @Nullable public String getAttribute(@NonNull String);
method @Nullable public byte[] getAttributeBytes(@NonNull String);
@@ -24342,6 +24401,7 @@ package android.media {
field public static final int INFO_OUTPUT_FORMAT_CHANGED = -2; // 0xfffffffe
field public static final int INFO_TRY_AGAIN_LATER = -1; // 0xffffffff
field public static final String PARAMETER_KEY_HDR10_PLUS_INFO = "hdr10-plus-info";
+ field public static final String PARAMETER_KEY_LOW_LATENCY = "low-latency";
field public static final String PARAMETER_KEY_OFFSET_TIME = "time-offset-us";
field public static final String PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync";
field public static final String PARAMETER_KEY_SUSPEND = "drop-input-frames";
@@ -24515,6 +24575,7 @@ package android.media {
field public static final String FEATURE_DynamicTimestamp = "dynamic-timestamp";
field public static final String FEATURE_FrameParsing = "frame-parsing";
field public static final String FEATURE_IntraRefresh = "intra-refresh";
+ field public static final String FEATURE_LowLatency = "low-latency";
field public static final String FEATURE_MultipleFrames = "multiple-frames";
field public static final String FEATURE_PartialFrame = "partial-frame";
field public static final String FEATURE_SecurePlayback = "secure-playback";
@@ -24603,6 +24664,7 @@ package android.media {
field public static final int DolbyVisionLevelUhd30 = 64; // 0x40
field public static final int DolbyVisionLevelUhd48 = 128; // 0x80
field public static final int DolbyVisionLevelUhd60 = 256; // 0x100
+ field public static final int DolbyVisionProfileDvav110 = 1024; // 0x400
field public static final int DolbyVisionProfileDvavPen = 2; // 0x2
field public static final int DolbyVisionProfileDvavPer = 1; // 0x1
field public static final int DolbyVisionProfileDvavSe = 512; // 0x200
@@ -25225,6 +25287,7 @@ package android.media {
field public static final String KEY_LANGUAGE = "language";
field public static final String KEY_LATENCY = "latency";
field public static final String KEY_LEVEL = "level";
+ field public static final String KEY_LOW_LATENCY = "low-latency";
field public static final String KEY_MAX_B_FRAMES = "max-bframes";
field public static final String KEY_MAX_FPS_TO_ENCODER = "max-fps-to-encoder";
field public static final String KEY_MAX_HEIGHT = "max-height";
@@ -25368,20 +25431,22 @@ package android.media {
public class MediaMetadataRetriever implements java.lang.AutoCloseable {
ctor public MediaMetadataRetriever();
method public void close();
- method public String extractMetadata(int);
- method public byte[] getEmbeddedPicture();
- method public android.graphics.Bitmap getFrameAtIndex(int, @NonNull android.media.MediaMetadataRetriever.BitmapParams);
- method public android.graphics.Bitmap getFrameAtIndex(int);
- method public android.graphics.Bitmap getFrameAtTime(long, int);
- method public android.graphics.Bitmap getFrameAtTime(long);
- method public android.graphics.Bitmap getFrameAtTime();
+ method @Nullable public String extractMetadata(int);
+ method @Nullable public byte[] getEmbeddedPicture();
+ method @Nullable public android.graphics.Bitmap getFrameAtIndex(int, @NonNull android.media.MediaMetadataRetriever.BitmapParams);
+ method @Nullable public android.graphics.Bitmap getFrameAtIndex(int);
+ method @Nullable public android.graphics.Bitmap getFrameAtTime(long, int);
+ method @Nullable public android.graphics.Bitmap getFrameAtTime(long, int, @NonNull android.media.MediaMetadataRetriever.BitmapParams);
+ method @Nullable public android.graphics.Bitmap getFrameAtTime(long);
+ method @Nullable public android.graphics.Bitmap getFrameAtTime();
method @NonNull public java.util.List<android.graphics.Bitmap> getFramesAtIndex(int, int, @NonNull android.media.MediaMetadataRetriever.BitmapParams);
method @NonNull public java.util.List<android.graphics.Bitmap> getFramesAtIndex(int, int);
- method public android.graphics.Bitmap getImageAtIndex(int, @NonNull android.media.MediaMetadataRetriever.BitmapParams);
- method public android.graphics.Bitmap getImageAtIndex(int);
- method public android.graphics.Bitmap getPrimaryImage(@NonNull android.media.MediaMetadataRetriever.BitmapParams);
- method public android.graphics.Bitmap getPrimaryImage();
- method public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int);
+ method @Nullable public android.graphics.Bitmap getImageAtIndex(int, @NonNull android.media.MediaMetadataRetriever.BitmapParams);
+ method @Nullable public android.graphics.Bitmap getImageAtIndex(int);
+ method @Nullable public android.graphics.Bitmap getPrimaryImage(@NonNull android.media.MediaMetadataRetriever.BitmapParams);
+ method @Nullable public android.graphics.Bitmap getPrimaryImage();
+ method @Nullable public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int);
+ method @Nullable public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int, @NonNull android.media.MediaMetadataRetriever.BitmapParams);
method public void release();
method public void setDataSource(String) throws java.lang.IllegalArgumentException;
method public void setDataSource(String, java.util.Map<java.lang.String,java.lang.String>) throws java.lang.IllegalArgumentException;
@@ -25396,6 +25461,9 @@ package android.media {
field public static final int METADATA_KEY_BITRATE = 20; // 0x14
field public static final int METADATA_KEY_CAPTURE_FRAMERATE = 25; // 0x19
field public static final int METADATA_KEY_CD_TRACK_NUMBER = 0; // 0x0
+ field public static final int METADATA_KEY_COLOR_RANGE = 37; // 0x25
+ field public static final int METADATA_KEY_COLOR_STANDARD = 35; // 0x23
+ field public static final int METADATA_KEY_COLOR_TRANSFER = 36; // 0x24
field public static final int METADATA_KEY_COMPILATION = 15; // 0xf
field public static final int METADATA_KEY_COMPOSER = 4; // 0x4
field public static final int METADATA_KEY_DATE = 5; // 0x5
@@ -28795,6 +28863,7 @@ package android.net {
method @Nullable public String getPrivateDnsServerName();
method @NonNull public java.util.List<android.net.RouteInfo> getRoutes();
method public boolean isPrivateDnsActive();
+ method public boolean isWakeOnLanSupported();
method public void setDnsServers(@NonNull java.util.Collection<java.net.InetAddress>);
method public void setDomains(@Nullable String);
method public void setHttpProxy(@Nullable android.net.ProxyInfo);
@@ -28865,7 +28934,9 @@ package android.net {
method @NonNull public static android.net.MacAddress fromBytes(@NonNull byte[]);
method @NonNull public static android.net.MacAddress fromString(@NonNull String);
method public int getAddressType();
+ method @Nullable public java.net.Inet6Address getLinkLocalIpv6FromEui48Mac();
method public boolean isLocallyAssigned();
+ method public boolean matches(@NonNull android.net.MacAddress, @NonNull android.net.MacAddress);
method @NonNull public byte[] toByteArray();
method @NonNull public String toOuiString();
method public void writeToParcel(android.os.Parcel, int);
@@ -29463,7 +29534,8 @@ package android.net.rtp {
}
public class AudioGroup {
- ctor public AudioGroup();
+ ctor @Deprecated public AudioGroup();
+ ctor public AudioGroup(@Nullable android.content.Context);
method public void clear();
method public int getMode();
method public android.net.rtp.AudioStream[] getStreams();
@@ -29905,16 +29977,23 @@ package android.net.wifi {
method public String getSSID();
method public android.net.wifi.SupplicantState getSupplicantState();
method @IntRange(from=0xffffffff) public int getTxLinkSpeedMbps();
+ method public int getWifiTechnology();
method public void writeToParcel(android.os.Parcel, int);
field public static final String FREQUENCY_UNITS = "MHz";
field public static final String LINK_SPEED_UNITS = "Mbps";
field public static final int LINK_SPEED_UNKNOWN = -1; // 0xffffffff
+ field public static final int WIFI_TECHNOLOGY_11AC = 5; // 0x5
+ field public static final int WIFI_TECHNOLOGY_11AX = 6; // 0x6
+ field public static final int WIFI_TECHNOLOGY_11N = 4; // 0x4
+ field public static final int WIFI_TECHNOLOGY_LEGACY = 1; // 0x1
+ field public static final int WIFI_TECHNOLOGY_UNKNOWN = 0; // 0x0
}
public class WifiManager {
method @Deprecated public int addNetwork(android.net.wifi.WifiConfiguration);
method @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public int addNetworkSuggestions(@NonNull java.util.List<android.net.wifi.WifiNetworkSuggestion>);
method public void addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void addScanResultsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.ScanResultsListener);
method public static int calculateSignalLevel(int, int);
method @Deprecated public void cancelWps(android.net.wifi.WifiManager.WpsCallback);
method public static int compareSignalLevel(int, int);
@@ -29950,6 +30029,7 @@ package android.net.wifi {
method @Deprecated public boolean removeNetwork(int);
method @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public int removeNetworkSuggestions(@NonNull java.util.List<android.net.wifi.WifiNetworkSuggestion>);
method @Deprecated @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", "android.permission.NETWORK_CARRIER_PROVISIONING"}) public void removePasspointConfiguration(String);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void removeScanResultsListener(@NonNull android.net.wifi.WifiManager.ScanResultsListener);
method @Deprecated public boolean saveConfiguration();
method public void setTdlsEnabled(java.net.InetAddress, boolean);
method public void setTdlsEnabledWithMacAddress(String, boolean);
@@ -30025,6 +30105,10 @@ package android.net.wifi {
method public void setReferenceCounted(boolean);
}
+ public static interface WifiManager.ScanResultsListener {
+ method public void onScanResultsAvailable();
+ }
+
public class WifiManager.WifiLock {
method public void acquire();
method public boolean isHeld();
@@ -30076,6 +30160,7 @@ package android.net.wifi {
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsHiddenSsid(boolean);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsMetered(boolean);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsUserInteractionRequired(boolean);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPasspointConfig(@NonNull android.net.wifi.hotspot2.PasspointConfiguration);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPriority(@IntRange(from=0) int);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setSsid(@NonNull String);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWpa2EnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
@@ -30259,6 +30344,8 @@ package android.net.wifi.hotspot2 {
method public int describeContents();
method public android.net.wifi.hotspot2.pps.Credential getCredential();
method public android.net.wifi.hotspot2.pps.HomeSp getHomeSp();
+ method public long getSubscriptionExpirationTimeInMillis();
+ method public boolean isOsuProvisioned();
method public void setCredential(android.net.wifi.hotspot2.pps.Credential);
method public void setHomeSp(android.net.wifi.hotspot2.pps.HomeSp);
method public void writeToParcel(android.os.Parcel, int);
@@ -37013,6 +37100,7 @@ package android.provider {
field public static final int FEATURES_PULLED_EXTERNALLY = 2; // 0x2
field public static final int FEATURES_RTT = 32; // 0x20
field public static final int FEATURES_VIDEO = 1; // 0x1
+ field public static final int FEATURES_VOLTE = 64; // 0x40
field public static final int FEATURES_WIFI = 8; // 0x8
field public static final String GEOCODED_LOCATION = "geocoded_location";
field public static final int INCOMING_TYPE = 1; // 0x1
@@ -38224,6 +38312,7 @@ package android.provider {
field public static final String COLUMN_MIME_TYPE = "mime_type";
field public static final String COLUMN_SIZE = "_size";
field public static final String COLUMN_SUMMARY = "summary";
+ field public static final int FLAG_DIR_BLOCKS_TREE = 32768; // 0x8000
field public static final int FLAG_DIR_PREFERS_GRID = 16; // 0x10
field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 32; // 0x20
field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8
@@ -38442,16 +38531,17 @@ package android.provider {
public static final class MediaStore.Audio {
ctor public MediaStore.Audio();
- method public static String keyFor(String);
+ method @Deprecated @Nullable public static String keyFor(@Nullable String);
}
public static interface MediaStore.Audio.AlbumColumns {
field public static final String ALBUM = "album";
field @Deprecated public static final String ALBUM_ART = "album_art";
field public static final String ALBUM_ID = "album_id";
- field public static final String ALBUM_KEY = "album_key";
+ field @Deprecated public static final String ALBUM_KEY = "album_key";
field public static final String ARTIST = "artist";
field public static final String ARTIST_ID = "artist_id";
+ field @Deprecated public static final String ARTIST_KEY = "artist_key";
field public static final String FIRST_YEAR = "minyear";
field public static final String LAST_YEAR = "maxyear";
field public static final String NUMBER_OF_SONGS = "numsongs";
@@ -38470,7 +38560,7 @@ package android.provider {
public static interface MediaStore.Audio.ArtistColumns {
field public static final String ARTIST = "artist";
- field public static final String ARTIST_KEY = "artist_key";
+ field @Deprecated public static final String ARTIST_KEY = "artist_key";
field public static final String NUMBER_OF_ALBUMS = "number_of_albums";
field public static final String NUMBER_OF_TRACKS = "number_of_tracks";
}
@@ -38493,19 +38583,23 @@ package android.provider {
public static interface MediaStore.Audio.AudioColumns extends android.provider.MediaStore.MediaColumns {
field public static final String ALBUM = "album";
field public static final String ALBUM_ID = "album_id";
- field public static final String ALBUM_KEY = "album_key";
+ field @Deprecated public static final String ALBUM_KEY = "album_key";
field public static final String ARTIST = "artist";
field public static final String ARTIST_ID = "artist_id";
- field public static final String ARTIST_KEY = "artist_key";
+ field @Deprecated public static final String ARTIST_KEY = "artist_key";
field public static final String BOOKMARK = "bookmark";
field public static final String COMPOSER = "composer";
+ field public static final String GENRE = "genre";
+ field public static final String GENRE_ID = "genre_id";
+ field @Deprecated public static final String GENRE_KEY = "genre_key";
field public static final String IS_ALARM = "is_alarm";
field public static final String IS_AUDIOBOOK = "is_audiobook";
field public static final String IS_MUSIC = "is_music";
field public static final String IS_NOTIFICATION = "is_notification";
field public static final String IS_PODCAST = "is_podcast";
field public static final String IS_RINGTONE = "is_ringtone";
- field public static final String TITLE_KEY = "title_key";
+ field @Deprecated public static final String TITLE_KEY = "title_key";
+ field public static final String TITLE_RESOURCE_URI = "title_resource_uri";
field public static final String TRACK = "track";
field public static final String YEAR = "year";
}
@@ -38731,6 +38825,9 @@ package android.provider {
field public static final String ARTIST = "artist";
field public static final String BOOKMARK = "bookmark";
field public static final String CATEGORY = "category";
+ field public static final String COLOR_RANGE = "color_range";
+ field public static final String COLOR_STANDARD = "color_standard";
+ field public static final String COLOR_TRANSFER = "color_transfer";
field public static final String DESCRIPTION = "description";
field public static final String IS_PRIVATE = "isprivate";
field public static final String LANGUAGE = "language";
@@ -40789,6 +40886,7 @@ package android.security {
field public static final String EXTRA_KEY_ALIAS = "android.security.extra.KEY_ALIAS";
field public static final String EXTRA_NAME = "name";
field public static final String EXTRA_PKCS12 = "PKCS12";
+ field public static final String KEY_ALIAS_SELECTION_DENIED = "android:alias-selection-denied";
}
public interface KeyChainAliasCallback {
@@ -41280,11 +41378,16 @@ package android.service.autofill {
field public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 1; // 0x1
field public static final int NEGATIVE_BUTTON_STYLE_CANCEL = 0; // 0x0
field public static final int NEGATIVE_BUTTON_STYLE_REJECT = 1; // 0x1
+ field public static final int POSITIVE_BUTTON_STYLE_CONTINUE = 1; // 0x1
+ field public static final int POSITIVE_BUTTON_STYLE_SAVE = 0; // 0x0
field public static final int SAVE_DATA_TYPE_ADDRESS = 2; // 0x2
field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 4; // 0x4
+ field public static final int SAVE_DATA_TYPE_DEBIT_CARD = 32; // 0x20
field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 16; // 0x10
field public static final int SAVE_DATA_TYPE_GENERIC = 0; // 0x0
+ field public static final int SAVE_DATA_TYPE_GENERIC_CARD = 128; // 0x80
field public static final int SAVE_DATA_TYPE_PASSWORD = 1; // 0x1
+ field public static final int SAVE_DATA_TYPE_PAYMENT_CARD = 64; // 0x40
field public static final int SAVE_DATA_TYPE_USERNAME = 8; // 0x8
}
@@ -41298,6 +41401,7 @@ package android.service.autofill {
method @NonNull public android.service.autofill.SaveInfo.Builder setFlags(int);
method @NonNull public android.service.autofill.SaveInfo.Builder setNegativeAction(int, @Nullable android.content.IntentSender);
method @NonNull public android.service.autofill.SaveInfo.Builder setOptionalIds(@NonNull android.view.autofill.AutofillId[]);
+ method @NonNull public android.service.autofill.SaveInfo.Builder setPositiveAction(int);
method @NonNull public android.service.autofill.SaveInfo.Builder setTriggerId(@NonNull android.view.autofill.AutofillId);
method @NonNull public android.service.autofill.SaveInfo.Builder setValidator(@NonNull android.service.autofill.Validator);
}
@@ -41849,6 +41953,7 @@ package android.service.quicksettings {
field public static final String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
field public static final String ACTION_QS_TILE_PREFERENCES = "android.service.quicksettings.action.QS_TILE_PREFERENCES";
field public static final String META_DATA_ACTIVE_TILE = "android.service.quicksettings.ACTIVE_TILE";
+ field public static final String META_DATA_BOOLEAN_TILE = "android.service.quicksettings.BOOLEAN_TILE";
}
}
@@ -43161,6 +43266,7 @@ package android.telecom {
field public static final String EXTRA_SILENT_RINGING_REQUESTED = "android.telecom.extra.SILENT_RINGING_REQUESTED";
field public static final String EXTRA_SUGGESTED_PHONE_ACCOUNTS = "android.telecom.extra.SUGGESTED_PHONE_ACCOUNTS";
field public static final int STATE_ACTIVE = 4; // 0x4
+ field public static final int STATE_AUDIO_PROCESSING = 12; // 0xc
field public static final int STATE_CONNECTING = 9; // 0x9
field public static final int STATE_DIALING = 1; // 0x1
field public static final int STATE_DISCONNECTED = 7; // 0x7
@@ -43170,6 +43276,7 @@ package android.telecom {
field public static final int STATE_PULLING_CALL = 11; // 0xb
field public static final int STATE_RINGING = 2; // 0x2
field public static final int STATE_SELECT_PHONE_ACCOUNT = 8; // 0x8
+ field public static final int STATE_SIMULATED_RINGING = 13; // 0xd
}
public abstract static class Call.Callback {
@@ -43395,8 +43502,10 @@ package android.telecom {
method public final int getState();
method public final android.telecom.StatusHints getStatusHints();
method public final android.telecom.Connection.VideoProvider getVideoProvider();
+ method public final int getVideoState();
method public void handleRttUpgradeResponse(@Nullable android.telecom.Connection.RttTextStream);
method public final boolean isRingbackRequested();
+ method public final void notifyConferenceMergeFailed();
method public void onAbort();
method public void onAnswer(int);
method public void onAnswer();
@@ -43475,8 +43584,15 @@ package android.telecom {
field public static final int CAPABILITY_SUPPORT_DEFLECT = 33554432; // 0x2000000
field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
+ field public static final String EVENT_CALL_HOLD_FAILED = "android.telecom.event.CALL_HOLD_FAILED";
field public static final String EVENT_CALL_MERGE_FAILED = "android.telecom.event.CALL_MERGE_FAILED";
field public static final String EVENT_CALL_PULL_FAILED = "android.telecom.event.CALL_PULL_FAILED";
+ field public static final String EVENT_CALL_REMOTELY_HELD = "android.telecom.event.CALL_REMOTELY_HELD";
+ field public static final String EVENT_CALL_REMOTELY_UNHELD = "android.telecom.event.CALL_REMOTELY_UNHELD";
+ field public static final String EVENT_MERGE_COMPLETE = "android.telecom.event.MERGE_COMPLETE";
+ field public static final String EVENT_MERGE_START = "android.telecom.event.MERGE_START";
+ field public static final String EVENT_ON_HOLD_TONE_END = "android.telecom.event.ON_HOLD_TONE_END";
+ field public static final String EVENT_ON_HOLD_TONE_START = "android.telecom.event.ON_HOLD_TONE_START";
field public static final String EVENT_RTT_AUDIO_INDICATION_CHANGED = "android.telecom.event.RTT_AUDIO_INDICATION_CHANGED";
field public static final String EXTRA_ANSWERING_DROPS_FG_CALL = "android.telecom.extra.ANSWERING_DROPS_FG_CALL";
field public static final String EXTRA_ANSWERING_DROPS_FG_CALL_APP_NAME = "android.telecom.extra.ANSWERING_DROPS_FG_CALL_APP_NAME";
@@ -43486,9 +43602,12 @@ package android.telecom {
field public static final String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
field public static final String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE";
field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
+ field public static final int PROPERTY_HIGH_DEF_AUDIO = 4; // 0x4
field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
field public static final int PROPERTY_IS_RTT = 256; // 0x100
+ field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 1024; // 0x400
field public static final int PROPERTY_SELF_MANAGED = 128; // 0x80
+ field public static final int PROPERTY_WIFI = 8; // 0x8
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_DIALING = 3; // 0x3
field public static final int STATE_DISCONNECTED = 6; // 0x6
@@ -44185,9 +44304,11 @@ package android.telephony {
field public static final String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
field public static final String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG = "data_warning_threshold_bytes_long";
field public static final String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
+ field public static final String KEY_DEFAULT_VM_NUMBER_ROAMING_AND_IMS_UNREGISTERED_STRING = "default_vm_number_roaming_and_ims_unregistered_string";
field public static final String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
field public static final String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array";
field public static final String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
+ field public static final String KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY = "disconnect_cause_play_busytone_int_array";
field public static final String KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL = "display_hd_audio_property_bool";
field public static final String KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL = "drop_video_call_when_answering_audio_call_bool";
field public static final String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool";
@@ -44281,6 +44402,7 @@ package android.telephony {
field public static final String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool";
field public static final String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool";
field public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
+ field public static final String KEY_USE_RCS_SIP_OPTIONS_BOOL = "use_rcs_sip_options_bool";
field public static final String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool";
field public static final String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool";
field public static final String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int";
@@ -44388,6 +44510,8 @@ package android.telephony {
public abstract class CellInfo implements android.os.Parcelable {
method public int describeContents();
method public int getCellConnectionStatus();
+ method @NonNull public abstract android.telephony.CellIdentity getCellIdentity();
+ method @NonNull public abstract android.telephony.CellSignalStrength getCellSignalStrength();
method public long getTimeStamp();
method public boolean isRegistered();
field public static final int CONNECTION_NONE = 0; // 0x0
@@ -44532,6 +44656,7 @@ package android.telephony {
method public int describeContents();
method public int getAsuLevel();
method public int getDbm();
+ method public int getEcNo();
method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public int getLevel();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthWcdma> CREATOR;
@@ -45012,6 +45137,7 @@ package android.telephony {
field public static final int DATA_ROAMING_DISABLE = 0; // 0x0
field public static final int DATA_ROAMING_ENABLE = 1; // 0x1
field public static final int DEFAULT_SUBSCRIPTION_ID = 2147483647; // 0x7fffffff
+ field public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX";
field public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
field public static final int INVALID_SIM_SLOT_INDEX = -1; // 0xffffffff
field public static final int INVALID_SUBSCRIPTION_ID = -1; // 0xffffffff
@@ -45036,6 +45162,7 @@ package android.telephony {
method public long getDataLimitBytes();
method public long getDataUsageBytes();
method public long getDataUsageTime();
+ method @Nullable public int[] getNetworkTypes();
method @Nullable public CharSequence getSummary();
method @Nullable public CharSequence getTitle();
method public void writeToParcel(android.os.Parcel, int);
@@ -45055,6 +45182,7 @@ package android.telephony {
method public static android.telephony.SubscriptionPlan.Builder createRecurring(java.time.ZonedDateTime, java.time.Period);
method public android.telephony.SubscriptionPlan.Builder setDataLimit(long, int);
method public android.telephony.SubscriptionPlan.Builder setDataUsage(long, long);
+ method @NonNull public android.telephony.SubscriptionPlan.Builder setNetworkTypes(@Nullable int[]);
method public android.telephony.SubscriptionPlan.Builder setSummary(@Nullable CharSequence);
method public android.telephony.SubscriptionPlan.Builder setTitle(@Nullable CharSequence);
}
@@ -45064,6 +45192,7 @@ package android.telephony {
method @Nullable public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
method public android.telephony.TelephonyManager createForSubscriptionId(int);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean doesSwitchMultiSimConfigTriggerReboot();
+ method public int getActiveModemCount();
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo();
method public int getCallState();
method public int getCardIdForDefaultEuicc();
@@ -45075,7 +45204,7 @@ package android.telephony {
method public int getDataState();
method @Deprecated @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getDeviceId();
method @Deprecated @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getDeviceId(int);
- method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getDeviceSoftwareVersion();
+ method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @Nullable public String getDeviceSoftwareVersion();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @NonNull public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getEmergencyNumberList();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @NonNull public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getEmergencyNumberList(int);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String[] getForbiddenPlmns();
@@ -45090,13 +45219,13 @@ package android.telephony {
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getMeid(int);
method public String getMmsUAProfUrl();
method public String getMmsUserAgent();
- method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getNai();
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getNai();
method public String getNetworkCountryIso();
method public String getNetworkOperator();
method public String getNetworkOperatorName();
method public String getNetworkSpecifier();
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getNetworkType();
- method public int getPhoneCount();
+ method @Deprecated public int getPhoneCount();
method public int getPhoneType();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription();
method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState();
@@ -45112,6 +45241,7 @@ package android.telephony {
method public int getSimState();
method public int getSimState(int);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getSubscriberId();
+ method public int getSupportedModemCount();
method @Nullable public String getTypeAllocationCode();
method @Nullable public String getTypeAllocationCode(int);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @NonNull public java.util.List<android.telephony.UiccCardInfo> getUiccCardsInfo();
@@ -45149,6 +45279,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
method public void sendVisualVoicemailSms(String, int, String, android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setForbiddenPlmns(@NonNull java.util.List<java.lang.String>);
method public boolean setLine1NumberForDisplay(String, String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setNetworkSelectionModeAutomatic();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setNetworkSelectionModeManual(String, boolean);
@@ -45163,6 +45294,7 @@ package android.telephony {
method public void updateAvailableNetworks(@NonNull java.util.List<android.telephony.AvailableNetworkInfo>, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
field public static final String ACTION_CARRIER_MESSAGING_CLIENT_SERVICE = "android.telephony.action.CARRIER_MESSAGING_CLIENT_SERVICE";
field public static final String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL";
+ field public static final String ACTION_MULTI_SIM_CONFIG_CHANGED = "android.telephony.action.MULTI_SIM_CONFIG_CHANGED";
field public static final String ACTION_NETWORK_COUNTRY_CHANGED = "android.telephony.action.NETWORK_COUNTRY_CHANGED";
field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
field public static final String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
@@ -45203,6 +45335,7 @@ package android.telephony {
field public static final String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT = "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
field public static final String EXTRA_NETWORK_COUNTRY = "android.telephony.extra.NETWORK_COUNTRY";
field public static final String EXTRA_NOTIFICATION_COUNT = "android.telephony.extra.NOTIFICATION_COUNT";
+ field public static final String EXTRA_NUM_OF_ACTIVE_SIM_SUPPORTED = "android.telephony.extra.NUM_OF_ACTIVE_SIM_SUPPORTED";
field public static final String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telephony.extra.PHONE_ACCOUNT_HANDLE";
field public static final String EXTRA_SPECIFIC_CARRIER_ID = "android.telephony.extra.SPECIFIC_CARRIER_ID";
field public static final String EXTRA_SPECIFIC_CARRIER_NAME = "android.telephony.extra.SPECIFIC_CARRIER_NAME";
@@ -45213,6 +45346,10 @@ package android.telephony {
field public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.extra.SUBSCRIPTION_ID";
field public static final String EXTRA_VOICEMAIL_NUMBER = "android.telephony.extra.VOICEMAIL_NUMBER";
field public static final String METADATA_HIDE_VOICEMAIL_SETTINGS_MENU = "android.telephony.HIDE_VOICEMAIL_SETTINGS_MENU";
+ field public static final int MODEM_COUNT_DUAL_MODEM = 2; // 0x2
+ field public static final int MODEM_COUNT_NO_MODEM = 0; // 0x0
+ field public static final int MODEM_COUNT_SINGLE_MODEM = 1; // 0x1
+ field public static final int MODEM_COUNT_TRI_MODEM = 3; // 0x3
field public static final int MULTISIM_ALLOWED = 0; // 0x0
field public static final int MULTISIM_NOT_SUPPORTED_BY_CARRIER = 2; // 0x2
field public static final int MULTISIM_NOT_SUPPORTED_BY_HARDWARE = 1; // 0x1
@@ -47114,7 +47251,9 @@ package android.text.style {
public abstract class ReplacementSpan extends android.text.style.MetricAffectingSpan {
ctor public ReplacementSpan();
method public abstract void draw(@NonNull android.graphics.Canvas, CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, float, int, int, int, @NonNull android.graphics.Paint);
+ method @Nullable public CharSequence getContentDescription();
method public abstract int getSize(@NonNull android.graphics.Paint, CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @Nullable android.graphics.Paint.FontMetricsInt);
+ method public void setContentDescription(@Nullable CharSequence);
method public void updateDrawState(android.text.TextPaint);
method public void updateMeasureState(android.text.TextPaint);
}
@@ -47888,6 +48027,7 @@ package android.util {
ctor public ArraySet(int);
ctor public ArraySet(android.util.ArraySet<E>);
ctor public ArraySet(java.util.Collection<? extends E>);
+ ctor public ArraySet(@Nullable E[]);
method public boolean add(E);
method public void addAll(android.util.ArraySet<? extends E>);
method public boolean addAll(java.util.Collection<? extends E>);
@@ -50140,14 +50280,14 @@ package android.view {
}
public static interface SurfaceHolder.Callback {
- method public void surfaceChanged(android.view.SurfaceHolder, int, int, int);
- method public void surfaceCreated(android.view.SurfaceHolder);
- method public void surfaceDestroyed(android.view.SurfaceHolder);
+ method public void surfaceChanged(@NonNull android.view.SurfaceHolder, int, @IntRange(from=0) int, @IntRange(from=0) int);
+ method public void surfaceCreated(@NonNull android.view.SurfaceHolder);
+ method public void surfaceDestroyed(@NonNull android.view.SurfaceHolder);
}
public static interface SurfaceHolder.Callback2 extends android.view.SurfaceHolder.Callback {
- method public void surfaceRedrawNeeded(android.view.SurfaceHolder);
- method public default void surfaceRedrawNeededAsync(android.view.SurfaceHolder, Runnable);
+ method public void surfaceRedrawNeeded(@NonNull android.view.SurfaceHolder);
+ method public default void surfaceRedrawNeededAsync(@NonNull android.view.SurfaceHolder, @NonNull Runnable);
}
public class SurfaceView extends android.view.View {
@@ -53042,7 +53182,8 @@ package android.view.contentcapture {
method @NonNull public android.view.autofill.AutofillId newAutofillId(@NonNull android.view.autofill.AutofillId, long);
method @NonNull public final android.view.ViewStructure newViewStructure(@NonNull android.view.View);
method @NonNull public final android.view.ViewStructure newVirtualViewStructure(@NonNull android.view.autofill.AutofillId, long);
- method public final void notifySessionLifecycle(boolean);
+ method public final void notifySessionPaused();
+ method public final void notifySessionResumed();
method public final void notifyViewAppeared(@NonNull android.view.ViewStructure);
method public final void notifyViewDisappeared(@NonNull android.view.autofill.AutofillId);
method public final void notifyViewTextChanged(@NonNull android.view.autofill.AutofillId, @Nullable CharSequence);
@@ -57395,7 +57536,7 @@ package android.widget {
method public boolean onPrivateIMECommand(String, android.os.Bundle);
method public void onRestoreInstanceState(android.os.Parcelable);
method public android.os.Parcelable onSaveInstanceState();
- method protected void onSelectionChanged(int, int);
+ method @CallSuper protected void onSelectionChanged(int, int);
method protected void onTextChanged(CharSequence, int, int, int);
method public boolean onTextContextMenuItem(int);
method public void removeTextChangedListener(android.text.TextWatcher);
diff --git a/api/lint-baseline.txt b/api/lint-baseline.txt
new file mode 100644
index 000000000000..2ca8cf4ae0a4
--- /dev/null
+++ b/api/lint-baseline.txt
@@ -0,0 +1,1179 @@
+// Baseline format: 1.0
+AcronymName: android.system.ErrnoException#rethrowAsIOException():
+
+
+
+ActionValue: android.provider.Settings#ACTION_CONDITION_PROVIDER_SETTINGS:
+
+
+
+AllUpper: android.media.MediaCodecInfo.CodecCapabilities#FEATURE_LowLatency:
+
+
+
+ArrayReturn: android.app.Notification.MessagingStyle.Message#getMessagesFromBundleArray(android.os.Parcelable[]) parameter #0:
+
+ArrayReturn: android.content.ContentProviderOperation#resolveExtrasBackReferences(android.content.ContentProviderResult[], int) parameter #0:
+
+
+
+BroadcastBehavior: android.app.AlarmManager#ACTION_NEXT_ALARM_CLOCK_CHANGED:
+
+BroadcastBehavior: android.app.admin.DevicePolicyManager#ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED:
+
+BroadcastBehavior: android.app.admin.DevicePolicyManager#ACTION_MANAGED_PROFILE_PROVISIONED:
+
+BroadcastBehavior: android.bluetooth.BluetoothAdapter#ACTION_DISCOVERY_FINISHED:
+
+BroadcastBehavior: android.bluetooth.BluetoothAdapter#ACTION_DISCOVERY_STARTED:
+
+BroadcastBehavior: android.bluetooth.BluetoothAdapter#ACTION_LOCAL_NAME_CHANGED:
+
+BroadcastBehavior: android.bluetooth.BluetoothAdapter#ACTION_SCAN_MODE_CHANGED:
+
+BroadcastBehavior: android.bluetooth.BluetoothAdapter#ACTION_STATE_CHANGED:
+
+BroadcastBehavior: android.bluetooth.BluetoothDevice#ACTION_ACL_CONNECTED:
+
+BroadcastBehavior: android.bluetooth.BluetoothDevice#ACTION_ACL_DISCONNECTED:
+
+BroadcastBehavior: android.bluetooth.BluetoothDevice#ACTION_ACL_DISCONNECT_REQUESTED:
+
+BroadcastBehavior: android.bluetooth.BluetoothDevice#ACTION_BOND_STATE_CHANGED:
+
+BroadcastBehavior: android.bluetooth.BluetoothDevice#ACTION_CLASS_CHANGED:
+
+BroadcastBehavior: android.bluetooth.BluetoothDevice#ACTION_FOUND:
+
+BroadcastBehavior: android.bluetooth.BluetoothDevice#ACTION_NAME_CHANGED:
+
+BroadcastBehavior: android.bluetooth.BluetoothDevice#ACTION_PAIRING_REQUEST:
+
+BroadcastBehavior: android.bluetooth.BluetoothDevice#ACTION_UUID:
+
+BroadcastBehavior: android.content.Intent#ACTION_AIRPLANE_MODE_CHANGED:
+
+BroadcastBehavior: android.content.Intent#ACTION_APPLICATION_RESTRICTIONS_CHANGED:
+
+BroadcastBehavior: android.content.Intent#ACTION_BATTERY_CHANGED:
+
+BroadcastBehavior: android.content.Intent#ACTION_BATTERY_LOW:
+
+BroadcastBehavior: android.content.Intent#ACTION_BATTERY_OKAY:
+
+BroadcastBehavior: android.content.Intent#ACTION_CAMERA_BUTTON:
+
+BroadcastBehavior: android.content.Intent#ACTION_CLOSE_SYSTEM_DIALOGS:
+
+BroadcastBehavior: android.content.Intent#ACTION_CONFIGURATION_CHANGED:
+
+BroadcastBehavior: android.content.Intent#ACTION_DATE_CHANGED:
+
+BroadcastBehavior: android.content.Intent#ACTION_DEVICE_STORAGE_LOW:
+
+BroadcastBehavior: android.content.Intent#ACTION_DEVICE_STORAGE_OK:
+
+BroadcastBehavior: android.content.Intent#ACTION_DOCK_EVENT:
+
+BroadcastBehavior: android.content.Intent#ACTION_DREAMING_STARTED:
+
+BroadcastBehavior: android.content.Intent#ACTION_DREAMING_STOPPED:
+
+BroadcastBehavior: android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
+
+BroadcastBehavior: android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
+
+BroadcastBehavior: android.content.Intent#ACTION_GTALK_SERVICE_CONNECTED:
+
+BroadcastBehavior: android.content.Intent#ACTION_GTALK_SERVICE_DISCONNECTED:
+
+BroadcastBehavior: android.content.Intent#ACTION_HEADSET_PLUG:
+
+BroadcastBehavior: android.content.Intent#ACTION_INPUT_METHOD_CHANGED:
+
+BroadcastBehavior: android.content.Intent#ACTION_LOCALE_CHANGED:
+
+BroadcastBehavior: android.content.Intent#ACTION_LOCKED_BOOT_COMPLETED:
+
+BroadcastBehavior: android.content.Intent#ACTION_MANAGE_PACKAGE_STORAGE:
+
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_BAD_REMOVAL:
+
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_BUTTON:
+
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_CHECKING:
+
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_EJECT:
+
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_MOUNTED:
+
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_NOFS:
+
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_REMOVED:
+
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_SCANNER_FINISHED:
+
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_SCANNER_SCAN_FILE:
+
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_SCANNER_STARTED:
+
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_SHARED:
+
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_UNMOUNTABLE:
+
+BroadcastBehavior: android.content.Intent#ACTION_MEDIA_UNMOUNTED:
+
+BroadcastBehavior: android.content.Intent#ACTION_MY_PACKAGE_REPLACED:
+
+BroadcastBehavior: android.content.Intent#ACTION_MY_PACKAGE_SUSPENDED:
+
+BroadcastBehavior: android.content.Intent#ACTION_MY_PACKAGE_UNSUSPENDED:
+
+BroadcastBehavior: android.content.Intent#ACTION_NEW_OUTGOING_CALL:
+
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGES_SUSPENDED:
+
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGES_UNSUSPENDED:
+
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_ADDED:
+
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_CHANGED:
+
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_DATA_CLEARED:
+
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_FIRST_LAUNCH:
+
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_FULLY_REMOVED:
+
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_INSTALL:
+
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_NEEDS_VERIFICATION:
+
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_REMOVED:
+
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_REPLACED:
+
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_RESTARTED:
+
+BroadcastBehavior: android.content.Intent#ACTION_PACKAGE_VERIFIED:
+
+BroadcastBehavior: android.content.Intent#ACTION_POWER_CONNECTED:
+
+BroadcastBehavior: android.content.Intent#ACTION_POWER_DISCONNECTED:
+
+BroadcastBehavior: android.content.Intent#ACTION_PROVIDER_CHANGED:
+
+BroadcastBehavior: android.content.Intent#ACTION_REBOOT:
+
+BroadcastBehavior: android.content.Intent#ACTION_SCREEN_OFF:
+
+BroadcastBehavior: android.content.Intent#ACTION_SCREEN_ON:
+
+BroadcastBehavior: android.content.Intent#ACTION_SHUTDOWN:
+
+BroadcastBehavior: android.content.Intent#ACTION_TIMEZONE_CHANGED:
+
+BroadcastBehavior: android.content.Intent#ACTION_TIME_CHANGED:
+
+BroadcastBehavior: android.content.Intent#ACTION_TIME_TICK:
+
+BroadcastBehavior: android.content.Intent#ACTION_UID_REMOVED:
+
+BroadcastBehavior: android.content.Intent#ACTION_UMS_CONNECTED:
+
+BroadcastBehavior: android.content.Intent#ACTION_UMS_DISCONNECTED:
+
+BroadcastBehavior: android.content.Intent#ACTION_USER_PRESENT:
+
+BroadcastBehavior: android.content.Intent#ACTION_USER_UNLOCKED:
+
+BroadcastBehavior: android.content.Intent#ACTION_WALLPAPER_CHANGED:
+
+BroadcastBehavior: android.content.pm.PackageInstaller#ACTION_SESSION_COMMITTED:
+
+BroadcastBehavior: android.content.pm.PackageInstaller#ACTION_SESSION_UPDATED:
+
+BroadcastBehavior: android.hardware.Camera#ACTION_NEW_PICTURE:
+
+BroadcastBehavior: android.hardware.Camera#ACTION_NEW_VIDEO:
+
+BroadcastBehavior: android.hardware.input.InputManager#ACTION_QUERY_KEYBOARD_LAYOUTS:
+
+BroadcastBehavior: android.hardware.usb.UsbManager#ACTION_USB_ACCESSORY_DETACHED:
+
+BroadcastBehavior: android.hardware.usb.UsbManager#ACTION_USB_DEVICE_DETACHED:
+
+BroadcastBehavior: android.media.AudioManager#ACTION_HDMI_AUDIO_PLUG:
+
+BroadcastBehavior: android.media.AudioManager#ACTION_HEADSET_PLUG:
+
+BroadcastBehavior: android.media.AudioManager#ACTION_MICROPHONE_MUTE_CHANGED:
+
+BroadcastBehavior: android.media.AudioManager#ACTION_SPEAKERPHONE_STATE_CHANGED:
+
+BroadcastBehavior: android.media.tv.TvContract#ACTION_INITIALIZE_PROGRAMS:
+
+BroadcastBehavior: android.media.tv.TvContract#ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT:
+
+BroadcastBehavior: android.media.tv.TvContract#ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED:
+
+BroadcastBehavior: android.media.tv.TvContract#ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED:
+
+BroadcastBehavior: android.net.ConnectivityManager#ACTION_BACKGROUND_DATA_SETTING_CHANGED:
+
+BroadcastBehavior: android.net.Proxy#PROXY_CHANGE_ACTION:
+
+BroadcastBehavior: android.nfc.NfcAdapter#ACTION_ADAPTER_STATE_CHANGED:
+
+BroadcastBehavior: android.nfc.NfcAdapter#ACTION_TRANSACTION_DETECTED:
+
+BroadcastBehavior: android.os.DropBoxManager#ACTION_DROPBOX_ENTRY_ADDED:
+
+BroadcastBehavior: android.provider.CalendarContract#ACTION_EVENT_REMINDER:
+
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#DATA_SMS_RECEIVED_ACTION:
+
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SECRET_CODE_ACTION:
+
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SIM_FULL_ACTION:
+
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_CB_RECEIVED_ACTION:
+
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_DELIVER_ACTION:
+
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION:
+
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_REJECTED_ACTION:
+
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION:
+
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#WAP_PUSH_DELIVER_ACTION:
+
+BroadcastBehavior: android.provider.Telephony.Sms.Intents#WAP_PUSH_RECEIVED_ACTION:
+
+BroadcastBehavior: android.security.KeyChain#ACTION_KEYCHAIN_CHANGED:
+
+BroadcastBehavior: android.security.KeyChain#ACTION_KEY_ACCESS_CHANGED:
+
+BroadcastBehavior: android.security.KeyChain#ACTION_STORAGE_CHANGED:
+
+BroadcastBehavior: android.security.KeyChain#ACTION_TRUST_STORE_CHANGED:
+
+BroadcastBehavior: android.speech.tts.TextToSpeech#ACTION_TTS_QUEUE_PROCESSING_COMPLETED:
+
+BroadcastBehavior: android.speech.tts.TextToSpeech.Engine#ACTION_TTS_DATA_INSTALLED:
+
+BroadcastBehavior: android.telephony.SubscriptionManager#ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED:
+
+BroadcastBehavior: android.telephony.SubscriptionManager#ACTION_DEFAULT_SUBSCRIPTION_CHANGED:
+
+BroadcastBehavior: android.telephony.SubscriptionManager#ACTION_REFRESH_SUBSCRIPTION_PLANS:
+
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SECRET_CODE:
+
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED:
+
+BroadcastBehavior: android.telephony.TelephonyManager#ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED:
+
+BroadcastBehavior: android.telephony.euicc.EuiccManager#ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE:
+
+
+
+CompileTimeConstant: android.icu.util.JapaneseCalendar#REIWA:
+
+
+
+DeprecationMismatch: android.accounts.AccountManager#newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle):
+
+DeprecationMismatch: android.app.Activity#enterPictureInPictureMode():
+
+DeprecationMismatch: android.app.Instrumentation#startAllocCounting():
+
+DeprecationMismatch: android.app.Instrumentation#stopAllocCounting():
+
+DeprecationMismatch: android.app.Notification#bigContentView:
+
+DeprecationMismatch: android.app.Notification#contentView:
+
+DeprecationMismatch: android.app.Notification#headsUpContentView:
+
+DeprecationMismatch: android.app.Notification#tickerView:
+
+DeprecationMismatch: android.app.Notification.Action.Builder#Builder(int, CharSequence, android.app.PendingIntent):
+
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#getCancelLabel():
+
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#getConfirmLabel():
+
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#getInProgressLabel():
+
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#setCancelLabel(CharSequence):
+
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#setConfirmLabel(CharSequence):
+
+DeprecationMismatch: android.app.Notification.Action.WearableExtender#setInProgressLabel(CharSequence):
+
+DeprecationMismatch: android.app.Notification.Builder#setContent(android.widget.RemoteViews):
+
+DeprecationMismatch: android.app.Notification.Builder#setTicker(CharSequence, android.widget.RemoteViews):
+
+DeprecationMismatch: android.app.Notification.WearableExtender#getContentIcon():
+
+DeprecationMismatch: android.app.Notification.WearableExtender#getContentIconGravity():
+
+DeprecationMismatch: android.app.Notification.WearableExtender#getCustomContentHeight():
+
+DeprecationMismatch: android.app.Notification.WearableExtender#getCustomSizePreset():
+
+DeprecationMismatch: android.app.Notification.WearableExtender#getGravity():
+
+DeprecationMismatch: android.app.Notification.WearableExtender#getHintAvoidBackgroundClipping():
+
+DeprecationMismatch: android.app.Notification.WearableExtender#getHintHideIcon():
+
+DeprecationMismatch: android.app.Notification.WearableExtender#getHintScreenTimeout():
+
+DeprecationMismatch: android.app.Notification.WearableExtender#getHintShowBackgroundOnly():
+
+DeprecationMismatch: android.app.Notification.WearableExtender#setContentIcon(int):
+
+DeprecationMismatch: android.app.Notification.WearableExtender#setContentIconGravity(int):
+
+DeprecationMismatch: android.app.Notification.WearableExtender#setCustomContentHeight(int):
+
+DeprecationMismatch: android.app.Notification.WearableExtender#setCustomSizePreset(int):
+
+DeprecationMismatch: android.app.Notification.WearableExtender#setGravity(int):
+
+DeprecationMismatch: android.app.Notification.WearableExtender#setHintAvoidBackgroundClipping(boolean):
+
+DeprecationMismatch: android.app.Notification.WearableExtender#setHintHideIcon(boolean):
+
+DeprecationMismatch: android.app.Notification.WearableExtender#setHintScreenTimeout(int):
+
+DeprecationMismatch: android.app.Notification.WearableExtender#setHintShowBackgroundOnly(boolean):
+
+DeprecationMismatch: android.content.ContextWrapper#clearWallpaper():
+
+DeprecationMismatch: android.content.ContextWrapper#getWallpaper():
+
+DeprecationMismatch: android.content.ContextWrapper#getWallpaperDesiredMinimumHeight():
+
+DeprecationMismatch: android.content.ContextWrapper#getWallpaperDesiredMinimumWidth():
+
+DeprecationMismatch: android.content.ContextWrapper#peekWallpaper():
+
+DeprecationMismatch: android.content.ContextWrapper#removeStickyBroadcast(android.content.Intent):
+
+DeprecationMismatch: android.content.ContextWrapper#removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle):
+
+DeprecationMismatch: android.content.ContextWrapper#sendStickyBroadcast(android.content.Intent):
+
+DeprecationMismatch: android.content.ContextWrapper#sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle):
+
+DeprecationMismatch: android.content.ContextWrapper#sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle):
+
+DeprecationMismatch: android.content.ContextWrapper#sendStickyOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle):
+
+DeprecationMismatch: android.content.ContextWrapper#setWallpaper(android.graphics.Bitmap):
+
+DeprecationMismatch: android.content.ContextWrapper#setWallpaper(java.io.InputStream):
+
+DeprecationMismatch: android.database.CursorWrapper#deactivate():
+
+DeprecationMismatch: android.database.CursorWrapper#requery():
+
+DeprecationMismatch: android.graphics.ComposeShader#ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode):
+
+DeprecationMismatch: android.graphics.PixelFormat#A_8:
+
+DeprecationMismatch: android.graphics.PixelFormat#LA_88:
+
+DeprecationMismatch: android.graphics.PixelFormat#L_8:
+
+DeprecationMismatch: android.graphics.PixelFormat#RGBA_4444:
+
+DeprecationMismatch: android.graphics.PixelFormat#RGBA_5551:
+
+DeprecationMismatch: android.graphics.PixelFormat#RGB_332:
+
+DeprecationMismatch: android.net.wifi.WifiManager#EXTRA_BSSID:
+
+DeprecationMismatch: android.net.wifi.WifiManager#EXTRA_WIFI_INFO:
+
+DeprecationMismatch: android.opengl.EGL14#eglCreatePixmapSurface(android.opengl.EGLDisplay, android.opengl.EGLConfig, int, int[], int):
+
+DeprecationMismatch: android.opengl.GLES20#GL_STENCIL_INDEX:
+
+DeprecationMismatch: android.opengl.GLSurfaceView#surfaceRedrawNeeded(android.view.SurfaceHolder):
+
+DeprecationMismatch: android.os.UserManager#setUserRestrictions(android.os.Bundle):
+
+DeprecationMismatch: android.os.UserManager#setUserRestrictions(android.os.Bundle, android.os.UserHandle):
+
+DeprecationMismatch: android.provider.Contacts.People#markAsContacted(android.content.ContentResolver, long):
+
+DeprecationMismatch: android.renderscript.Type.CubemapFace#POSITVE_X:
+
+DeprecationMismatch: android.renderscript.Type.CubemapFace#POSITVE_Y:
+
+DeprecationMismatch: android.renderscript.Type.CubemapFace#POSITVE_Z:
+
+DeprecationMismatch: android.speech.tts.TextToSpeech#areDefaultsEnforced():
+
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_12HOUR:
+
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_24HOUR:
+
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_CAP_AMPM:
+
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_CAP_MIDNIGHT:
+
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_CAP_NOON:
+
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_CAP_NOON_MIDNIGHT:
+
+DeprecationMismatch: android.text.format.DateUtils#FORMAT_NO_NOON_MIDNIGHT:
+
+DeprecationMismatch: android.view.ViewGroup.LayoutParams#FILL_PARENT:
+
+DeprecationMismatch: android.view.Window#setTitleColor(int):
+
+DeprecationMismatch: android.view.accessibility.AccessibilityEvent#MAX_TEXT_LENGTH:
+
+DeprecationMismatch: android.webkit.WebSettings#getSaveFormData():
+
+DeprecationMismatch: android.webkit.WebView#shouldDelayChildPressedState():
+
+DeprecationMismatch: android.webkit.WebViewDatabase#clearFormData():
+
+DeprecationMismatch: android.webkit.WebViewDatabase#hasFormData():
+
+DeprecationMismatch: javax.microedition.khronos.egl.EGL10#eglCreatePixmapSurface(javax.microedition.khronos.egl.EGLDisplay, javax.microedition.khronos.egl.EGLConfig, Object, int[]):
+
+
+
+GenericException: android.content.res.loader.ResourcesProvider#finalize():
+ Methods must not throw generic exceptions (`java.lang.Throwable`)
+
+
+HiddenSuperclass: android.content.res.ColorStateList:
+
+HiddenSuperclass: android.graphics.Canvas:
+
+HiddenSuperclass: android.graphics.RecordingCanvas:
+
+HiddenSuperclass: android.hardware.biometrics.BiometricPrompt.AuthenticationCallback:
+
+HiddenSuperclass: android.hardware.biometrics.BiometricPrompt.AuthenticationResult:
+
+HiddenSuperclass: android.hardware.biometrics.BiometricPrompt.CryptoObject:
+
+HiddenSuperclass: android.hardware.fingerprint.FingerprintManager.AuthenticationCallback:
+
+HiddenSuperclass: android.hardware.fingerprint.FingerprintManager.CryptoObject:
+
+HiddenSuperclass: android.media.AudioTrack:
+
+HiddenSuperclass: android.media.MediaPlayer:
+
+HiddenSuperclass: android.media.SoundPool:
+
+HiddenSuperclass: android.service.autofill.CharSequenceTransformation:
+
+HiddenSuperclass: android.service.autofill.DateTransformation:
+
+HiddenSuperclass: android.service.autofill.DateValueSanitizer:
+
+HiddenSuperclass: android.service.autofill.ImageTransformation:
+
+HiddenSuperclass: android.service.autofill.LuhnChecksumValidator:
+
+HiddenSuperclass: android.service.autofill.RegexValidator:
+
+HiddenSuperclass: android.service.autofill.TextValueSanitizer:
+
+HiddenSuperclass: android.service.autofill.VisibilitySetterAction:
+
+HiddenSuperclass: android.util.StatsLog:
+
+
+
+MissingNullability: android.app.AsyncNotedAppOp#equals(Object) parameter #0:
+
+MissingNullability: android.app.AsyncNotedAppOp#writeToParcel(android.os.Parcel, int) parameter #0:
+
+MissingNullability: android.app.SyncNotedAppOp#equals(Object) parameter #0:
+
+MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#EGYPTIAN_HIEROGLYPH_FORMAT_CONTROLS:
+
+MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#ELYMAIC:
+
+MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#NANDINAGARI:
+
+MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#NYIAKENG_PUACHUE_HMONG:
+
+MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#OTTOMAN_SIYAQ_NUMBERS:
+
+MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#SMALL_KANA_EXTENSION:
+
+MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#SYMBOLS_AND_PICTOGRAPHS_EXTENDED_A:
+
+MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#TAMIL_SUPPLEMENT:
+
+MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#WANCHO:
+
+MissingNullability: android.icu.text.DateTimePatternGenerator#getFieldDisplayName(int, android.icu.text.DateTimePatternGenerator.DisplayWidth):
+
+MissingNullability: android.icu.text.DateTimePatternGenerator#getFieldDisplayName(int, android.icu.text.DateTimePatternGenerator.DisplayWidth) parameter #1:
+
+MissingNullability: android.icu.util.VersionInfo#UNICODE_12_0:
+
+MissingNullability: android.icu.util.VersionInfo#UNICODE_12_1:
+
+MissingNullability: android.media.MediaMetadataRetriever#getFrameAtTime(long, int, android.media.MediaMetadataRetriever.BitmapParams):
+
+MissingNullability: android.media.MediaMetadataRetriever#getScaledFrameAtTime(long, int, int, int, android.media.MediaMetadataRetriever.BitmapParams):
+
+
+
+RequiresPermission: android.accounts.AccountManager#getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler):
+
+RequiresPermission: android.accounts.AccountManager#hasFeatures(android.accounts.Account, String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler):
+
+RequiresPermission: android.app.AlarmManager#setTime(long):
+
+RequiresPermission: android.app.AppOpsManager#isOpActive(String, int, String):
+
+RequiresPermission: android.app.AppOpsManager#startWatchingActive(String[], java.util.concurrent.Executor, android.app.AppOpsManager.OnOpActiveChangedListener):
+
+RequiresPermission: android.app.DownloadManager.Request#setDestinationInExternalPublicDir(String, String):
+
+RequiresPermission: android.app.DownloadManager.Request#setDestinationUri(android.net.Uri):
+
+RequiresPermission: android.app.DownloadManager.Request#setNotificationVisibility(int):
+
+RequiresPermission: android.app.DownloadManager.Request#setShowRunningNotification(boolean):
+
+RequiresPermission: android.app.Notification.Builder#setFullScreenIntent(android.app.PendingIntent, boolean):
+
+RequiresPermission: android.app.Service#startForeground(int, android.app.Notification):
+
+RequiresPermission: android.app.WallpaperInfo#getSettingsSliceUri():
+
+RequiresPermission: android.app.WallpaperManager#clear():
+
+RequiresPermission: android.app.WallpaperManager#clearWallpaper():
+
+RequiresPermission: android.app.WallpaperManager#setBitmap(android.graphics.Bitmap):
+
+RequiresPermission: android.app.WallpaperManager#setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean):
+
+RequiresPermission: android.app.WallpaperManager#setDisplayPadding(android.graphics.Rect):
+
+RequiresPermission: android.app.WallpaperManager#setResource(int):
+
+RequiresPermission: android.app.WallpaperManager#setStream(java.io.InputStream):
+
+RequiresPermission: android.app.WallpaperManager#setStream(java.io.InputStream, android.graphics.Rect, boolean):
+
+RequiresPermission: android.app.WallpaperManager#suggestDesiredDimensions(int, int):
+
+RequiresPermission: android.app.admin.DevicePolicyManager#bindDeviceAdminServiceAsUser(android.content.ComponentName, android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle):
+
+RequiresPermission: android.app.admin.DevicePolicyManager#getPasswordComplexity():
+
+RequiresPermission: android.app.admin.DevicePolicyManager#setAlwaysOnVpnPackage(android.content.ComponentName, String, boolean):
+
+RequiresPermission: android.app.backup.BackupManager#dataChanged(String):
+
+RequiresPermission: android.app.usage.StorageStatsManager#queryExternalStatsForUser(java.util.UUID, android.os.UserHandle):
+
+RequiresPermission: android.app.usage.StorageStatsManager#queryStatsForPackage(java.util.UUID, String, android.os.UserHandle):
+
+RequiresPermission: android.app.usage.StorageStatsManager#queryStatsForUid(java.util.UUID, int):
+
+RequiresPermission: android.app.usage.StorageStatsManager#queryStatsForUser(java.util.UUID, android.os.UserHandle):
+
+RequiresPermission: android.app.usage.UsageStatsManager#queryAndAggregateUsageStats(long, long):
+
+RequiresPermission: android.app.usage.UsageStatsManager#queryConfigurations(int, long, long):
+
+RequiresPermission: android.app.usage.UsageStatsManager#queryEventStats(int, long, long):
+
+RequiresPermission: android.app.usage.UsageStatsManager#queryEvents(long, long):
+
+RequiresPermission: android.app.usage.UsageStatsManager#queryUsageStats(int, long, long):
+
+RequiresPermission: android.appwidget.AppWidgetManager#bindAppWidgetIdIfAllowed(int, android.os.UserHandle, android.content.ComponentName, android.os.Bundle):
+
+RequiresPermission: android.bluetooth.BluetoothA2dp#isA2dpPlaying(android.bluetooth.BluetoothDevice):
+
+RequiresPermission: android.bluetooth.BluetoothAdapter#getName():
+
+RequiresPermission: android.bluetooth.BluetoothDevice#setPin(byte[]):
+
+RequiresPermission: android.bluetooth.BluetoothGatt#abortReliableWrite():
+
+RequiresPermission: android.bluetooth.BluetoothGatt#beginReliableWrite():
+
+RequiresPermission: android.bluetooth.BluetoothGatt#disconnect():
+
+RequiresPermission: android.bluetooth.BluetoothGatt#discoverServices():
+
+RequiresPermission: android.bluetooth.BluetoothGatt#executeReliableWrite():
+
+RequiresPermission: android.bluetooth.BluetoothGatt#getService(java.util.UUID):
+
+RequiresPermission: android.bluetooth.BluetoothGatt#getServices():
+
+RequiresPermission: android.bluetooth.BluetoothGatt#readCharacteristic(android.bluetooth.BluetoothGattCharacteristic):
+
+RequiresPermission: android.bluetooth.BluetoothGatt#readDescriptor(android.bluetooth.BluetoothGattDescriptor):
+
+RequiresPermission: android.bluetooth.BluetoothGatt#readRemoteRssi():
+
+RequiresPermission: android.bluetooth.BluetoothGatt#requestMtu(int):
+
+RequiresPermission: android.bluetooth.BluetoothGatt#setCharacteristicNotification(android.bluetooth.BluetoothGattCharacteristic, boolean):
+
+RequiresPermission: android.bluetooth.BluetoothGatt#writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic):
+
+RequiresPermission: android.bluetooth.BluetoothGatt#writeDescriptor(android.bluetooth.BluetoothGattDescriptor):
+
+RequiresPermission: android.bluetooth.BluetoothGattCharacteristic#BluetoothGattCharacteristic(java.util.UUID, int, int):
+
+RequiresPermission: android.bluetooth.BluetoothGattCharacteristic#addDescriptor(android.bluetooth.BluetoothGattDescriptor):
+
+RequiresPermission: android.bluetooth.BluetoothGattDescriptor#BluetoothGattDescriptor(java.util.UUID, int):
+
+RequiresPermission: android.bluetooth.BluetoothGattServer#addService(android.bluetooth.BluetoothGattService):
+
+RequiresPermission: android.bluetooth.BluetoothGattServer#cancelConnection(android.bluetooth.BluetoothDevice):
+
+RequiresPermission: android.bluetooth.BluetoothGattServer#clearServices():
+
+RequiresPermission: android.bluetooth.BluetoothGattServer#connect(android.bluetooth.BluetoothDevice, boolean):
+
+RequiresPermission: android.bluetooth.BluetoothGattServer#getService(java.util.UUID):
+
+RequiresPermission: android.bluetooth.BluetoothGattServer#getServices():
+
+RequiresPermission: android.bluetooth.BluetoothGattServer#notifyCharacteristicChanged(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothGattCharacteristic, boolean):
+
+RequiresPermission: android.bluetooth.BluetoothGattServer#removeService(android.bluetooth.BluetoothGattService):
+
+RequiresPermission: android.bluetooth.BluetoothGattServer#sendResponse(android.bluetooth.BluetoothDevice, int, int, int, byte[]):
+
+RequiresPermission: android.bluetooth.BluetoothGattService#BluetoothGattService(java.util.UUID, int):
+
+RequiresPermission: android.bluetooth.BluetoothGattService#addCharacteristic(android.bluetooth.BluetoothGattCharacteristic):
+
+RequiresPermission: android.bluetooth.BluetoothGattService#addService(android.bluetooth.BluetoothGattService):
+
+RequiresPermission: android.bluetooth.BluetoothHeadset#isAudioConnected(android.bluetooth.BluetoothDevice):
+
+RequiresPermission: android.bluetooth.BluetoothHeadset#sendVendorSpecificResultCode(android.bluetooth.BluetoothDevice, String, String):
+
+RequiresPermission: android.bluetooth.BluetoothHeadset#startVoiceRecognition(android.bluetooth.BluetoothDevice):
+
+RequiresPermission: android.bluetooth.BluetoothHeadset#stopVoiceRecognition(android.bluetooth.BluetoothDevice):
+
+RequiresPermission: android.bluetooth.BluetoothHealth#connectChannelToSource(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration):
+
+RequiresPermission: android.bluetooth.BluetoothHealth#disconnectChannel(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration, int):
+
+RequiresPermission: android.bluetooth.BluetoothHealth#getConnectedDevices():
+
+RequiresPermission: android.bluetooth.BluetoothHealth#getConnectionState(android.bluetooth.BluetoothDevice):
+
+RequiresPermission: android.bluetooth.BluetoothHealth#getDevicesMatchingConnectionStates(int[]):
+
+RequiresPermission: android.bluetooth.BluetoothHealth#getMainChannelFd(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration):
+
+RequiresPermission: android.bluetooth.BluetoothHealth#registerSinkAppConfiguration(String, int, android.bluetooth.BluetoothHealthCallback):
+
+RequiresPermission: android.bluetooth.BluetoothHealth#unregisterAppConfiguration(android.bluetooth.BluetoothHealthAppConfiguration):
+
+RequiresPermission: android.bluetooth.le.AdvertisingSet#enableAdvertising(boolean, int, int):
+
+RequiresPermission: android.bluetooth.le.BluetoothLeAdvertiser#startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback):
+
+RequiresPermission: android.bluetooth.le.BluetoothLeAdvertiser#startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback):
+
+RequiresPermission: android.bluetooth.le.BluetoothLeAdvertiser#stopAdvertising(android.bluetooth.le.AdvertiseCallback):
+
+RequiresPermission: android.companion.CompanionDeviceManager#associate(android.companion.AssociationRequest, android.companion.CompanionDeviceManager.Callback, android.os.Handler):
+
+RequiresPermission: android.content.ContentResolver#addPeriodicSync(android.accounts.Account, String, android.os.Bundle, long):
+
+RequiresPermission: android.content.ContentResolver#cancelSync(android.content.SyncRequest):
+
+RequiresPermission: android.content.ContentResolver#getCurrentSync():
+
+RequiresPermission: android.content.ContentResolver#getCurrentSyncs():
+
+RequiresPermission: android.content.ContentResolver#getIsSyncable(android.accounts.Account, String):
+
+RequiresPermission: android.content.ContentResolver#getMasterSyncAutomatically():
+
+RequiresPermission: android.content.ContentResolver#getPeriodicSyncs(android.accounts.Account, String):
+
+RequiresPermission: android.content.ContentResolver#getSyncAutomatically(android.accounts.Account, String):
+
+RequiresPermission: android.content.ContentResolver#isSyncActive(android.accounts.Account, String):
+
+RequiresPermission: android.content.ContentResolver#isSyncPending(android.accounts.Account, String):
+
+RequiresPermission: android.content.ContentResolver#removePeriodicSync(android.accounts.Account, String, android.os.Bundle):
+
+RequiresPermission: android.content.ContentResolver#setIsSyncable(android.accounts.Account, String, int):
+
+RequiresPermission: android.content.ContentResolver#setMasterSyncAutomatically(boolean):
+
+RequiresPermission: android.content.ContentResolver#setSyncAutomatically(android.accounts.Account, String, boolean):
+
+RequiresPermission: android.content.Context#clearWallpaper():
+
+RequiresPermission: android.content.Context#getExternalCacheDir():
+
+RequiresPermission: android.content.Context#getExternalCacheDirs():
+
+RequiresPermission: android.content.Context#getExternalFilesDir(String):
+
+RequiresPermission: android.content.Context#getExternalFilesDirs(String):
+
+RequiresPermission: android.content.Context#getExternalMediaDirs():
+
+RequiresPermission: android.content.Context#getObbDir():
+
+RequiresPermission: android.content.Context#getObbDirs():
+
+RequiresPermission: android.content.Context#removeStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle):
+
+RequiresPermission: android.content.Context#setWallpaper(android.graphics.Bitmap):
+
+RequiresPermission: android.content.Context#setWallpaper(java.io.InputStream):
+
+RequiresPermission: android.content.pm.LauncherApps.Callback#onPackagesSuspended(String[], android.os.UserHandle, android.os.Bundle):
+
+RequiresPermission: android.content.pm.PackageManager#canRequestPackageInstalls():
+
+RequiresPermission: android.content.pm.PackageManager#getSuspendedPackageAppExtras():
+
+RequiresPermission: android.content.pm.PackageManager#isPackageSuspended():
+
+RequiresPermission: android.hardware.camera2.CameraCharacteristics#getKeysNeedingPermission():
+
+RequiresPermission: android.hardware.usb.UsbManager#hasPermission(android.hardware.usb.UsbDevice):
+
+RequiresPermission: android.hardware.usb.UsbManager#requestPermission(android.hardware.usb.UsbDevice, android.app.PendingIntent):
+
+RequiresPermission: android.location.LocationManager#addGpsStatusListener(android.location.GpsStatus.Listener):
+
+RequiresPermission: android.location.LocationManager#addNmeaListener(android.location.OnNmeaMessageListener):
+
+RequiresPermission: android.location.LocationManager#addNmeaListener(android.location.OnNmeaMessageListener, android.os.Handler):
+
+RequiresPermission: android.location.LocationManager#addNmeaListener(java.util.concurrent.Executor, android.location.OnNmeaMessageListener):
+
+RequiresPermission: android.location.LocationManager#addProximityAlert(double, double, float, long, android.app.PendingIntent):
+
+RequiresPermission: android.location.LocationManager#registerGnssStatusCallback(android.location.GnssStatus.Callback):
+
+RequiresPermission: android.location.LocationManager#registerGnssStatusCallback(android.location.GnssStatus.Callback, android.os.Handler):
+
+RequiresPermission: android.location.LocationManager#registerGnssStatusCallback(java.util.concurrent.Executor, android.location.GnssStatus.Callback):
+
+RequiresPermission: android.media.AudioManager#startBluetoothSco():
+
+RequiresPermission: android.media.AudioManager#stopBluetoothSco():
+
+RequiresPermission: android.media.MediaExtractor#setDataSource(String):
+
+RequiresPermission: android.media.MediaExtractor#setDataSource(String, java.util.Map<java.lang.String,java.lang.String>):
+
+RequiresPermission: android.media.MediaExtractor#setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String,java.lang.String>):
+
+RequiresPermission: android.media.MediaPlayer#setWakeMode(android.content.Context, int):
+
+RequiresPermission: android.media.MediaSession2Service#onUpdateNotification(android.media.MediaSession2):
+
+RequiresPermission: android.media.RingtoneManager#getCursor():
+
+RequiresPermission: android.media.RingtoneManager#getValidRingtoneUri(android.content.Context):
+
+RequiresPermission: android.media.session.MediaSessionManager#addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName):
+
+RequiresPermission: android.media.session.MediaSessionManager#addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName, android.os.Handler):
+
+RequiresPermission: android.media.session.MediaSessionManager#getActiveSessions(android.content.ComponentName):
+
+RequiresPermission: android.media.session.MediaSessionManager#isTrustedForMediaControl(android.media.session.MediaSessionManager.RemoteUserInfo):
+
+RequiresPermission: android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, android.app.PendingIntent):
+
+RequiresPermission: android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback):
+
+RequiresPermission: android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback, android.os.Handler):
+
+RequiresPermission: android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback, android.os.Handler, int):
+
+RequiresPermission: android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback, int):
+
+RequiresPermission: android.net.sip.SipAudioCall#setSpeakerMode(boolean):
+
+RequiresPermission: android.net.sip.SipAudioCall#startAudio():
+
+RequiresPermission: android.net.wifi.WifiManager#getScanResults():
+
+RequiresPermission: android.net.wifi.WifiManager#setWifiEnabled(boolean):
+
+RequiresPermission: android.net.wifi.WifiManager#startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, android.os.Handler):
+
+RequiresPermission: android.net.wifi.WifiManager#startScan():
+
+RequiresPermission: android.net.wifi.aware.IdentityChangedListener#onIdentityChanged(byte[]):
+
+RequiresPermission: android.net.wifi.aware.WifiAwareManager#attach(android.net.wifi.aware.AttachCallback, android.net.wifi.aware.IdentityChangedListener, android.os.Handler):
+
+RequiresPermission: android.net.wifi.aware.WifiAwareSession#publish(android.net.wifi.aware.PublishConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler):
+
+RequiresPermission: android.net.wifi.aware.WifiAwareSession#subscribe(android.net.wifi.aware.SubscribeConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler):
+
+RequiresPermission: android.nfc.NfcAdapter#disableForegroundDispatch(android.app.Activity):
+
+RequiresPermission: android.nfc.NfcAdapter#disableForegroundNdefPush(android.app.Activity):
+
+RequiresPermission: android.nfc.NfcAdapter#enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]):
+
+RequiresPermission: android.nfc.NfcAdapter#enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage):
+
+RequiresPermission: android.nfc.NfcAdapter#setBeamPushUris(android.net.Uri[], android.app.Activity):
+
+RequiresPermission: android.nfc.NfcAdapter#setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity):
+
+RequiresPermission: android.nfc.NfcAdapter#setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...):
+
+RequiresPermission: android.nfc.NfcAdapter#setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...):
+
+RequiresPermission: android.nfc.NfcAdapter#setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...):
+
+RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForAid(android.content.ComponentName, String):
+
+RequiresPermission: android.nfc.cardemulation.CardEmulation#isDefaultServiceForCategory(android.content.ComponentName, String):
+
+RequiresPermission: android.nfc.cardemulation.CardEmulation#setOffHostForService(android.content.ComponentName, String):
+
+RequiresPermission: android.nfc.tech.IsoDep#getTimeout():
+
+RequiresPermission: android.nfc.tech.IsoDep#setTimeout(int):
+
+RequiresPermission: android.nfc.tech.IsoDep#transceive(byte[]):
+
+RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyA(int, byte[]):
+
+RequiresPermission: android.nfc.tech.MifareClassic#authenticateSectorWithKeyB(int, byte[]):
+
+RequiresPermission: android.nfc.tech.MifareClassic#decrement(int, int):
+
+RequiresPermission: android.nfc.tech.MifareClassic#getTimeout():
+
+RequiresPermission: android.nfc.tech.MifareClassic#increment(int, int):
+
+RequiresPermission: android.nfc.tech.MifareClassic#readBlock(int):
+
+RequiresPermission: android.nfc.tech.MifareClassic#restore(int):
+
+RequiresPermission: android.nfc.tech.MifareClassic#setTimeout(int):
+
+RequiresPermission: android.nfc.tech.MifareClassic#transceive(byte[]):
+
+RequiresPermission: android.nfc.tech.MifareClassic#transfer(int):
+
+RequiresPermission: android.nfc.tech.MifareClassic#writeBlock(int, byte[]):
+
+RequiresPermission: android.nfc.tech.MifareUltralight#getTimeout():
+
+RequiresPermission: android.nfc.tech.MifareUltralight#readPages(int):
+
+RequiresPermission: android.nfc.tech.MifareUltralight#setTimeout(int):
+
+RequiresPermission: android.nfc.tech.MifareUltralight#transceive(byte[]):
+
+RequiresPermission: android.nfc.tech.MifareUltralight#writePage(int, byte[]):
+
+RequiresPermission: android.nfc.tech.Ndef#getNdefMessage():
+
+RequiresPermission: android.nfc.tech.Ndef#isWritable():
+
+RequiresPermission: android.nfc.tech.Ndef#makeReadOnly():
+
+RequiresPermission: android.nfc.tech.Ndef#writeNdefMessage(android.nfc.NdefMessage):
+
+RequiresPermission: android.nfc.tech.NdefFormatable#format(android.nfc.NdefMessage):
+
+RequiresPermission: android.nfc.tech.NdefFormatable#formatReadOnly(android.nfc.NdefMessage):
+
+RequiresPermission: android.nfc.tech.NfcA#getTimeout():
+
+RequiresPermission: android.nfc.tech.NfcA#setTimeout(int):
+
+RequiresPermission: android.nfc.tech.NfcA#transceive(byte[]):
+
+RequiresPermission: android.nfc.tech.NfcB#transceive(byte[]):
+
+RequiresPermission: android.nfc.tech.NfcF#getTimeout():
+
+RequiresPermission: android.nfc.tech.NfcF#setTimeout(int):
+
+RequiresPermission: android.nfc.tech.NfcF#transceive(byte[]):
+
+RequiresPermission: android.nfc.tech.NfcV#transceive(byte[]):
+
+RequiresPermission: android.nfc.tech.TagTechnology#close():
+
+RequiresPermission: android.nfc.tech.TagTechnology#connect():
+
+RequiresPermission: android.os.Build#getSerial():
+
+RequiresPermission: android.os.Debug#dumpService(String, java.io.FileDescriptor, String[]):
+
+RequiresPermission: android.os.Environment#getExternalStorageDirectory():
+
+RequiresPermission: android.os.PowerManager#newWakeLock(int, String):
+
+RequiresPermission: android.os.PowerManager#reboot(String):
+
+RequiresPermission: android.os.RecoverySystem#rebootWipeUserData(android.content.Context):
+
+RequiresPermission: android.os.StrictMode.VmPolicy.Builder#detectFileUriExposure():
+
+RequiresPermission: android.os.UserManager#getUserName():
+
+RequiresPermission: android.os.UserManager#isUserUnlocked(android.os.UserHandle):
+
+RequiresPermission: android.os.health.SystemHealthManager#takeUidSnapshot(int):
+
+RequiresPermission: android.os.health.SystemHealthManager#takeUidSnapshots(int[]):
+
+RequiresPermission: android.os.storage.StorageVolume#createAccessIntent(String):
+
+RequiresPermission: android.provider.MediaStore#setRequireOriginal(android.net.Uri):
+
+RequiresPermission: android.provider.Settings#canDrawOverlays(android.content.Context):
+
+RequiresPermission: android.provider.Settings.System#canWrite(android.content.Context):
+
+RequiresPermission: android.telecom.TelecomManager#acceptHandover(android.net.Uri, int, android.telecom.PhoneAccountHandle):
+
+RequiresPermission: android.telecom.TelecomManager#acceptRingingCall():
+
+RequiresPermission: android.telecom.TelecomManager#acceptRingingCall(int):
+
+RequiresPermission: android.telecom.TelecomManager#addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle):
+
+RequiresPermission: android.telecom.TelecomManager#cancelMissedCallsNotification():
+
+RequiresPermission: android.telecom.TelecomManager#endCall():
+
+RequiresPermission: android.telecom.TelecomManager#getAdnUriForPhoneAccount(android.telecom.PhoneAccountHandle):
+
+RequiresPermission: android.telecom.TelecomManager#getCallCapablePhoneAccounts():
+
+RequiresPermission: android.telecom.TelecomManager#getDefaultOutgoingPhoneAccount(String):
+
+RequiresPermission: android.telecom.TelecomManager#getLine1Number(android.telecom.PhoneAccountHandle):
+
+RequiresPermission: android.telecom.TelecomManager#getSelfManagedPhoneAccounts():
+
+RequiresPermission: android.telecom.TelecomManager#getVoiceMailNumber(android.telecom.PhoneAccountHandle):
+
+RequiresPermission: android.telecom.TelecomManager#handleMmi(String):
+
+RequiresPermission: android.telecom.TelecomManager#handleMmi(String, android.telecom.PhoneAccountHandle):
+
+RequiresPermission: android.telecom.TelecomManager#isInCall():
+
+RequiresPermission: android.telecom.TelecomManager#isInManagedCall():
+
+RequiresPermission: android.telecom.TelecomManager#isVoiceMailNumber(android.telecom.PhoneAccountHandle, String):
+
+RequiresPermission: android.telecom.TelecomManager#placeCall(android.net.Uri, android.os.Bundle):
+
+RequiresPermission: android.telecom.TelecomManager#showInCallScreen(boolean):
+
+RequiresPermission: android.telecom.TelecomManager#silenceRinger():
+
+RequiresPermission: android.telephony.CarrierConfigManager#getConfig():
+
+RequiresPermission: android.telephony.CarrierConfigManager#getConfigByComponentForSubId(String, int):
+
+RequiresPermission: android.telephony.CarrierConfigManager#getConfigForSubId(int):
+
+RequiresPermission: android.telephony.PhoneStateListener#onCallStateChanged(int, String):
+
+RequiresPermission: android.telephony.SmsManager#injectSmsPdu(byte[], String, android.app.PendingIntent):
+
+RequiresPermission: android.telephony.SmsManager#sendDataMessage(String, String, short, byte[], android.app.PendingIntent, android.app.PendingIntent):
+
+RequiresPermission: android.telephony.SmsManager#sendMultipartTextMessage(String, String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>):
+
+RequiresPermission: android.telephony.SmsManager#sendTextMessage(String, String, String, android.app.PendingIntent, android.app.PendingIntent):
+
+RequiresPermission: android.telephony.SmsManager#sendTextMessageWithoutPersisting(String, String, String, android.app.PendingIntent, android.app.PendingIntent):
+
+RequiresPermission: android.telephony.SubscriptionManager#addSubscriptionsIntoGroup(java.util.List<java.lang.Integer>, android.os.ParcelUuid):
+
+RequiresPermission: android.telephony.SubscriptionManager#createSubscriptionGroup(java.util.List<java.lang.Integer>):
+
+RequiresPermission: android.telephony.SubscriptionManager#getActiveSubscriptionInfo(int):
+
+RequiresPermission: android.telephony.SubscriptionManager#getActiveSubscriptionInfoCount():
+
+RequiresPermission: android.telephony.SubscriptionManager#getActiveSubscriptionInfoForSimSlotIndex(int):
+
+RequiresPermission: android.telephony.SubscriptionManager#getActiveSubscriptionInfoList():
+
+RequiresPermission: android.telephony.SubscriptionManager#getOpportunisticSubscriptions():
+
+RequiresPermission: android.telephony.SubscriptionManager#getSubscriptionsInGroup(android.os.ParcelUuid):
+
+RequiresPermission: android.telephony.SubscriptionManager#removeSubscriptionsFromGroup(java.util.List<java.lang.Integer>, android.os.ParcelUuid):
+
+RequiresPermission: android.telephony.SubscriptionManager#setOpportunistic(boolean, int):
+
+RequiresPermission: android.telephony.TelephonyManager#doesSwitchMultiSimConfigTriggerReboot():
+
+RequiresPermission: android.telephony.TelephonyManager#getCarrierConfig():
+
+RequiresPermission: android.telephony.TelephonyManager#getDataNetworkType():
+
+RequiresPermission: android.telephony.TelephonyManager#getDeviceId():
+
+RequiresPermission: android.telephony.TelephonyManager#getDeviceId(int):
+
+RequiresPermission: android.telephony.TelephonyManager#getDeviceSoftwareVersion():
+
+RequiresPermission: android.telephony.TelephonyManager#getEmergencyNumberList():
+
+RequiresPermission: android.telephony.TelephonyManager#getEmergencyNumberList(int):
+
+RequiresPermission: android.telephony.TelephonyManager#getForbiddenPlmns():
+
+RequiresPermission: android.telephony.TelephonyManager#getGroupIdLevel1():
+
+RequiresPermission: android.telephony.TelephonyManager#getImei(int):
+
+RequiresPermission: android.telephony.TelephonyManager#getLine1Number():
+
+RequiresPermission: android.telephony.TelephonyManager#getMeid():
+
+RequiresPermission: android.telephony.TelephonyManager#getMeid(int):
+
+RequiresPermission: android.telephony.TelephonyManager#getNai():
+
+RequiresPermission: android.telephony.TelephonyManager#getPreferredOpportunisticDataSubscription():
+
+RequiresPermission: android.telephony.TelephonyManager#getServiceState():
+
+RequiresPermission: android.telephony.TelephonyManager#getSimSerialNumber():
+
+RequiresPermission: android.telephony.TelephonyManager#getSubscriberId():
+
+RequiresPermission: android.telephony.TelephonyManager#getVisualVoicemailPackageName():
+
+RequiresPermission: android.telephony.TelephonyManager#getVoiceMailAlphaTag():
+
+RequiresPermission: android.telephony.TelephonyManager#getVoiceMailNumber():
+
+RequiresPermission: android.telephony.TelephonyManager#getVoiceNetworkType():
+
+RequiresPermission: android.telephony.TelephonyManager#iccCloseLogicalChannel(int):
+
+RequiresPermission: android.telephony.TelephonyManager#iccExchangeSimIO(int, int, int, int, int, String):
+
+RequiresPermission: android.telephony.TelephonyManager#iccOpenLogicalChannel(String):
+
+RequiresPermission: android.telephony.TelephonyManager#iccOpenLogicalChannel(String, int):
+
+RequiresPermission: android.telephony.TelephonyManager#iccTransmitApduBasicChannel(int, int, int, int, int, String):
+
+RequiresPermission: android.telephony.TelephonyManager#iccTransmitApduLogicalChannel(int, int, int, int, int, int, String):
+
+RequiresPermission: android.telephony.TelephonyManager#isDataEnabled():
+
+RequiresPermission: android.telephony.TelephonyManager#isDataRoamingEnabled():
+
+RequiresPermission: android.telephony.TelephonyManager#isMultiSimSupported():
+
+RequiresPermission: android.telephony.TelephonyManager#requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback):
+
+RequiresPermission: android.telephony.TelephonyManager#sendEnvelopeWithStatus(String):
+
+RequiresPermission: android.telephony.TelephonyManager#sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler):
+
+RequiresPermission: android.telephony.TelephonyManager#sendVisualVoicemailSms(String, int, String, android.app.PendingIntent):
+
+RequiresPermission: android.telephony.TelephonyManager#setDataEnabled(boolean):
+
+RequiresPermission: android.telephony.TelephonyManager#setNetworkSelectionModeAutomatic():
+
+RequiresPermission: android.telephony.TelephonyManager#setNetworkSelectionModeManual(String, boolean):
+
+RequiresPermission: android.telephony.TelephonyManager#setPreferredOpportunisticDataSubscription(int, boolean, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+
+RequiresPermission: android.telephony.TelephonyManager#setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri):
+
+RequiresPermission: android.telephony.TelephonyManager#setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean):
+
+RequiresPermission: android.telephony.TelephonyManager#switchMultiSimConfig(int):
+
+RequiresPermission: android.telephony.TelephonyManager#updateAvailableNetworks(java.util.List<android.telephony.AvailableNetworkInfo>, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+
+RequiresPermission: android.telephony.euicc.EuiccManager#deleteSubscription(int, android.app.PendingIntent):
+
+RequiresPermission: android.telephony.euicc.EuiccManager#downloadSubscription(android.telephony.euicc.DownloadableSubscription, boolean, android.app.PendingIntent):
+
+RequiresPermission: android.telephony.euicc.EuiccManager#switchToSubscription(int, android.app.PendingIntent):
+
+RequiresPermission: android.telephony.euicc.EuiccManager#updateSubscriptionNickname(int, String, android.app.PendingIntent):
+
+RequiresPermission: android.view.inputmethod.InputMethodManager#setCurrentInputMethodSubtype(android.view.inputmethod.InputMethodSubtype):
+
+RequiresPermission: android.view.inputmethod.InputMethodManager#setInputMethod(android.os.IBinder, String):
+
+RequiresPermission: android.view.inputmethod.InputMethodManager#setInputMethodAndSubtype(android.os.IBinder, String, android.view.inputmethod.InputMethodSubtype):
+
+RequiresPermission: android.webkit.WebSettings#setBlockNetworkLoads(boolean):
+
+RequiresPermission: android.webkit.WebSettings#setGeolocationEnabled(boolean):
+
+
+
+SamShouldBeLast: android.location.LocationManager#registerGnssMeasurementsCallback(java.util.concurrent.Executor, android.location.GnssMeasurementsEvent.Callback):
+
+SamShouldBeLast: android.location.LocationManager#registerGnssNavigationMessageCallback(java.util.concurrent.Executor, android.location.GnssNavigationMessage.Callback):
+
+SamShouldBeLast: android.location.LocationManager#registerGnssStatusCallback(java.util.concurrent.Executor, android.location.GnssStatus.Callback):
+
+SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(String, long, float, java.util.concurrent.Executor, android.location.LocationListener):
+
+SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(long, float, android.location.Criteria, java.util.concurrent.Executor, android.location.LocationListener):
+
+
+
+StreamFiles: android.content.res.loader.DirectoryResourceLoader#DirectoryResourceLoader(java.io.File):
+ Methods accepting `File` should also accept `FileDescriptor` or streams: constructor android.content.res.loader.DirectoryResourceLoader(java.io.File)
+
+
+Todo: android.hardware.camera2.params.StreamConfigurationMap:
+
+Todo: android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration(Class<T>, android.util.Size):
+
+Todo: android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration(int, android.util.Size):
+
+Todo: android.provider.ContactsContract.RawContacts#newEntityIterator(android.database.Cursor):
+
+Todo: android.telephony.CarrierConfigManager#KEY_USE_OTASP_FOR_PROVISIONING_BOOL:
+
diff --git a/api/system-current.txt b/api/system-current.txt
index d818678a6e66..e790b3f5b060 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -24,6 +24,7 @@ package android {
field public static final String BACKUP = "android.permission.BACKUP";
field public static final String BIND_ATTENTION_SERVICE = "android.permission.BIND_ATTENTION_SERVICE";
field public static final String BIND_AUGMENTED_AUTOFILL_SERVICE = "android.permission.BIND_AUGMENTED_AUTOFILL_SERVICE";
+ field public static final String BIND_CELL_BROADCAST_SERVICE = "android.permission.BIND_CELL_BROADCAST_SERVICE";
field @Deprecated public static final String BIND_CONNECTION_SERVICE = "android.permission.BIND_CONNECTION_SERVICE";
field public static final String BIND_CONTENT_CAPTURE_SERVICE = "android.permission.BIND_CONTENT_CAPTURE_SERVICE";
field public static final String BIND_CONTENT_SUGGESTIONS_SERVICE = "android.permission.BIND_CONTENT_SUGGESTIONS_SERVICE";
@@ -171,6 +172,7 @@ package android {
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE";
field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
+ field public static final String RESTORE_RUNTIME_PERMISSIONS = "android.permission.RESTORE_RUNTIME_PERMISSIONS";
field public static final String RESTRICTED_VR_ACCESS = "android.permission.RESTRICTED_VR_ACCESS";
field public static final String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
field public static final String REVIEW_ACCESSIBILITY_SERVICES = "android.permission.REVIEW_ACCESSIBILITY_SERVICES";
@@ -225,6 +227,7 @@ package android {
field public static final int isVrOnly = 16844152; // 0x1010578
field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
+ field public static final int resourcesMap = 16844297; // 0x1010609
field public static final int supportsAmbientMode = 16844173; // 0x101058d
field public static final int userRestriction = 16844164; // 0x1010584
}
@@ -455,6 +458,7 @@ package android.app {
public static final class AppOpsManager.OpEntry implements android.os.Parcelable {
method public int describeContents();
method public long getDuration();
+ method @NonNull public java.util.Map<java.lang.String,android.app.AppOpsManager.OpFeatureEntry> getFeatures();
method public long getLastAccessBackgroundTime(int);
method public long getLastAccessForegroundTime(int);
method public long getLastAccessTime(int);
@@ -468,15 +472,37 @@ package android.app {
method public long getLastRejectTime(int, int, int);
method public int getMode();
method @NonNull public String getOpStr();
- method @Nullable public String getProxyPackageName();
+ method @Deprecated @Nullable public String getProxyPackageName();
method @Nullable public String getProxyPackageName(int, int);
- method public int getProxyUid();
+ method @Deprecated public int getProxyUid();
method public int getProxyUid(int, int);
method public boolean isRunning();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpEntry> CREATOR;
}
+ public static final class AppOpsManager.OpFeatureEntry {
+ method public long getDuration();
+ method public long getLastAccessBackgroundTime(int);
+ method public long getLastAccessForegroundTime(int);
+ method public long getLastAccessTime(int);
+ method public long getLastAccessTime(int, int, int);
+ method public long getLastBackgroundDuration(int);
+ method public long getLastDuration(int, int, int);
+ method public long getLastForegroundDuration(int);
+ method public long getLastRejectBackgroundTime(int);
+ method public long getLastRejectForegroundTime(int);
+ method public long getLastRejectTime(int);
+ method public long getLastRejectTime(int, int, int);
+ method @Nullable public String getProxyFeatureId();
+ method @Nullable public String getProxyFeatureId(int, int);
+ method @Nullable public String getProxyPackageName();
+ method @Nullable public String getProxyPackageName(int, int);
+ method public int getProxyUid();
+ method public int getProxyUid(int, int);
+ method public boolean isRunning();
+ }
+
public static final class AppOpsManager.PackageOps implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<android.app.AppOpsManager.OpEntry> getOps();
@@ -1357,8 +1383,9 @@ package android.content {
public abstract class Context {
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean bindServiceAsUser(@RequiresPermission android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle);
+ method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle, int);
method public abstract android.content.Context createCredentialProtectedStorageContext();
- method public android.content.Context createPackageContextAsUser(String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull public android.content.Context createPackageContextAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract java.io.File getPreloadsFileCache();
method public abstract boolean isCredentialProtectedStorage();
method public abstract void sendBroadcast(android.content.Intent, @Nullable String, @Nullable android.os.Bundle);
@@ -1382,6 +1409,7 @@ package android.content {
field public static final String STATS_MANAGER = "stats";
field public static final String STATUS_BAR_SERVICE = "statusbar";
field public static final String SYSTEM_UPDATE_SERVICE = "system_update";
+ field public static final String TELEPHONY_REGISTRY_SERVICE = "telephony_registry";
field public static final String VR_SERVICE = "vrmanager";
field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager";
field public static final String WIFI_SCANNING_SERVICE = "wifiscanner";
@@ -1401,6 +1429,7 @@ package android.content {
field public static final String ACTION_CALL_EMERGENCY = "android.intent.action.CALL_EMERGENCY";
field public static final String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED";
field public static final String ACTION_DEVICE_CUSTOMIZATION_READY = "android.intent.action.DEVICE_CUSTOMIZATION_READY";
+ field public static final String ACTION_DIAL_EMERGENCY = "android.intent.action.DIAL_EMERGENCY";
field public static final String ACTION_FACTORY_RESET = "android.intent.action.FACTORY_RESET";
field public static final String ACTION_GLOBAL_BUTTON = "android.intent.action.GLOBAL_BUTTON";
field public static final String ACTION_INCIDENT_REPORT_READY = "android.intent.action.INCIDENT_REPORT_READY";
@@ -1663,7 +1692,8 @@ package android.content.pm {
field public static final int FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT = 4096; // 0x1000
field public static final int FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT = 8192; // 0x2000
field public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 64; // 0x40
- field public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8
+ field public static final int FLAG_PERMISSION_REVOKED_COMPAT = 8; // 0x8
+ field @Deprecated public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8
field public static final int FLAG_PERMISSION_SYSTEM_FIXED = 16; // 0x10
field public static final int FLAG_PERMISSION_USER_FIXED = 2; // 0x2
field public static final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED = 512; // 0x200
@@ -1731,7 +1761,7 @@ package android.content.pm {
method public void onPermissionsChanged(int);
}
- @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags {
+ @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags {
}
public class PermissionGroupInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
@@ -1749,7 +1779,9 @@ package android.content.pm {
field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000
field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
+ field public static final int PROTECTION_FLAG_TELEPHONY = 4194304; // 0x400000
field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
+ field public static final int PROTECTION_FLAG_WIFI = 8388608; // 0x800000
field @Nullable public final String backgroundPermission;
field @StringRes public int requestRes;
}
@@ -3415,11 +3447,14 @@ package android.location {
public class Location implements android.os.Parcelable {
method public boolean isComplete();
method public void makeComplete();
+ method public void setExtraLocation(@Nullable String, @Nullable android.location.Location);
method public void setIsFromMockProvider(boolean);
+ field public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
}
public class LocationManager {
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void flushGnssBatch();
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>);
method @Nullable public String getExtraLocationControllerPackage();
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public int getGnssBatchSize();
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public android.location.GnssCapabilities getGnssCapabilities();
@@ -3429,8 +3464,9 @@ package android.location {
method public boolean isProviderEnabledForUser(@NonNull String, @NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@NonNull String);
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean registerGnssBatchedLocationCallback(long, boolean, @NonNull android.location.BatchedLocationCallback, @Nullable android.os.Handler);
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull android.location.LocationRequest, @NonNull android.app.PendingIntent);
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setExtraLocationControllerPackage(@Nullable String);
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setExtraLocationControllerPackageEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle);
@@ -3614,6 +3650,10 @@ package android.media {
method public void stop();
}
+ public class RingtoneManager {
+ method @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void ensureDefaultRingtones(@NonNull android.content.Context);
+ }
+
}
package android.media.audiopolicy {
@@ -4721,7 +4761,7 @@ package android.net.wifi {
method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void addOnWifiUsabilityStatsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void connect(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void connect(int, @Nullable android.net.wifi.WifiManager.ActionListener);
- method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void disable(int, @Nullable android.net.wifi.WifiManager.ActionListener);
+ method @Deprecated @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void disable(int, @Nullable android.net.wifi.WifiManager.ActionListener);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void forget(int, @Nullable android.net.wifi.WifiManager.ActionListener);
method @NonNull @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.util.Pair<android.net.wifi.WifiConfiguration,java.util.Map<java.lang.Integer,java.util.List<android.net.wifi.ScanResult>>>> getAllMatchingWifiConfigs(@NonNull java.util.List<android.net.wifi.ScanResult>);
method @NonNull @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,java.util.List<android.net.wifi.ScanResult>> getMatchingOsuProviders(@Nullable java.util.List<android.net.wifi.ScanResult>);
@@ -4740,8 +4780,11 @@ package android.net.wifi {
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsConfiguratorInitiator(@NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsEnrolleeInitiator(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public boolean startScan(android.os.WorkSource);
+ method @RequiresPermission(anyOf={"android.permission.NETWORK_STACK", android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean startSoftAp(@Nullable android.net.wifi.WifiConfiguration);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startSubscriptionProvisioning(@NonNull android.net.wifi.hotspot2.OsuProvider, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.hotspot2.ProvisioningCallback);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void stopEasyConnectSession();
+ method @RequiresPermission(anyOf={"android.permission.NETWORK_STACK", android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean stopSoftAp();
+ method @RequiresPermission(anyOf={"android.permission.NETWORK_STACK", android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void updateInterfaceIpState(@Nullable String, int);
method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void updateWifiUsabilityScore(int, int, int);
field public static final int CHANGE_REASON_ADDED = 0; // 0x0
field public static final int CHANGE_REASON_CONFIG_CHANGE = 2; // 0x2
@@ -4756,10 +4799,16 @@ package android.net.wifi {
field public static final String EXTRA_CHANGE_REASON = "changeReason";
field public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
field public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
+ field public static final String EXTRA_WIFI_AP_INTERFACE_NAME = "android.net.wifi.extra.WIFI_AP_INTERFACE_NAME";
+ field public static final String EXTRA_WIFI_AP_MODE = "android.net.wifi.extra.WIFI_AP_MODE";
field public static final String EXTRA_WIFI_AP_STATE = "wifi_state";
field public static final String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration";
field public static final String EXTRA_WIFI_CREDENTIAL_EVENT_TYPE = "et";
field public static final String EXTRA_WIFI_CREDENTIAL_SSID = "ssid";
+ field public static final int IFACE_IP_MODE_CONFIGURATION_ERROR = 0; // 0x0
+ field public static final int IFACE_IP_MODE_LOCAL_ONLY = 2; // 0x2
+ field public static final int IFACE_IP_MODE_TETHERED = 1; // 0x1
+ field public static final int IFACE_IP_MODE_UNSPECIFIED = -1; // 0xffffffff
field public static final int PASSPOINT_HOME_NETWORK = 0; // 0x0
field public static final int PASSPOINT_ROAMING_NETWORK = 1; // 0x1
field public static final String WIFI_AP_STATE_CHANGED_ACTION = "android.net.wifi.WIFI_AP_STATE_CHANGED";
@@ -5199,6 +5248,7 @@ package android.os {
field public static final String ACTION_UPDATE_CARRIER_PROVISIONING_URLS = "android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS";
field public static final String ACTION_UPDATE_CONVERSATION_ACTIONS = "android.intent.action.UPDATE_CONVERSATION_ACTIONS";
field public static final String ACTION_UPDATE_CT_LOGS = "android.intent.action.UPDATE_CT_LOGS";
+ field public static final String ACTION_UPDATE_EMERGENCY_NUMBER_DB = "android.os.action.UPDATE_EMERGENCY_NUMBER_DB";
field public static final String ACTION_UPDATE_INTENT_FIREWALL = "android.intent.action.UPDATE_INTENT_FIREWALL";
field public static final String ACTION_UPDATE_LANG_ID = "android.intent.action.UPDATE_LANG_ID";
field public static final String ACTION_UPDATE_NETWORK_WATCHLIST = "android.intent.action.UPDATE_NETWORK_WATCHLIST";
@@ -5516,7 +5566,7 @@ package android.os {
public class UpdateEngine {
ctor public UpdateEngine();
method public void applyPayload(String, long, long, String[]);
- method public void applyPayload(@NonNull java.io.FileDescriptor, long, long, @NonNull String[]);
+ method public void applyPayload(@NonNull android.os.ParcelFileDescriptor, long, long, @NonNull String[]);
method public boolean bind(android.os.UpdateEngineCallback, android.os.Handler);
method public boolean bind(android.os.UpdateEngineCallback);
method public void cancel();
@@ -5595,6 +5645,8 @@ package android.os {
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isPrimaryUser();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isRestrictedProfile();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isRestrictedProfile(@NonNull android.os.UserHandle);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isSameProfileGroup(@NonNull android.os.UserHandle, @NonNull android.os.UserHandle);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public boolean isUserUnlockingOrUnlocked(@NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean removeUser(@NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserIcon(@NonNull android.graphics.Bitmap);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserName(@Nullable String);
@@ -5683,7 +5735,10 @@ package android.os.storage {
package android.permission {
public final class PermissionControllerManager {
+ method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.RESTORE_RUNTIME_PERMISSIONS}) public void applyStagedRuntimePermissionBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public void getRuntimePermissionBackup(@NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<byte[]>);
method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public void revokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull java.util.concurrent.Executor, @NonNull android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.RESTORE_RUNTIME_PERMISSIONS}) public void stageAndApplyRuntimePermissionsBackup(@NonNull byte[], @NonNull android.os.UserHandle);
field public static final int COUNT_ONLY_WHEN_GRANTED = 1; // 0x1
field public static final int COUNT_WHEN_SYSTEM = 2; // 0x2
field public static final int REASON_INSTALLER_POLICY_VIOLATION = 2; // 0x2
@@ -5697,17 +5752,19 @@ package android.permission {
public abstract class PermissionControllerService extends android.app.Service {
ctor public PermissionControllerService();
+ method @BinderThread public void onApplyStagedRuntimePermissionBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @NonNull public final android.os.IBinder onBind(android.content.Intent);
method @BinderThread public abstract void onCountPermissionApps(@NonNull java.util.List<java.lang.String>, int, @NonNull java.util.function.IntConsumer);
method @BinderThread public abstract void onGetAppPermissions(@NonNull String, @NonNull java.util.function.Consumer<java.util.List<android.permission.RuntimePermissionPresentationInfo>>);
method @BinderThread public abstract void onGetPermissionUsages(boolean, long, @NonNull java.util.function.Consumer<java.util.List<android.permission.RuntimePermissionUsageInfo>>);
method @BinderThread public abstract void onGetRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.OutputStream, @NonNull Runnable);
method @BinderThread public abstract void onGrantOrUpgradeDefaultRuntimePermissions(@NonNull Runnable);
- method @BinderThread public abstract void onRestoreDelayedRuntimePermissionsBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.function.Consumer<java.lang.Boolean>);
- method @BinderThread public abstract void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
+ method @Deprecated @BinderThread public void onRestoreDelayedRuntimePermissionsBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @Deprecated @BinderThread public void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
method @BinderThread public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String, @NonNull Runnable);
method @BinderThread public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.util.List<java.lang.String>>>);
method @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @BinderThread public void onStageAndApplyRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
method @BinderThread public void onUpdateUserSensitive();
field public static final String SERVICE_INTERFACE = "android.permission.PermissionControllerService";
}
@@ -5863,6 +5920,7 @@ package android.provider {
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static float getFloat(@NonNull String, @NonNull String, float);
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static int getInt(@NonNull String, @NonNull String, int);
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static long getLong(@NonNull String, @NonNull String, long);
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static android.provider.DeviceConfig.Properties getProperties(@NonNull String, @NonNull java.lang.String...);
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static String getProperty(@NonNull String, @NonNull String);
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static String getString(@NonNull String, @NonNull String, @Nullable String);
method public static void removeOnPropertiesChangedListener(@NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
@@ -5890,7 +5948,8 @@ package android.provider {
field public static final String NAMESPACE_RUNTIME_NATIVE = "runtime_native";
field public static final String NAMESPACE_RUNTIME_NATIVE_BOOT = "runtime_native_boot";
field public static final String NAMESPACE_SCHEDULER = "scheduler";
- field public static final String NAMESPACE_STORAGE = "storage";
+ field @Deprecated public static final String NAMESPACE_STORAGE = "storage";
+ field public static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot";
field public static final String NAMESPACE_SYSTEMUI = "systemui";
field public static final String NAMESPACE_TELEPHONY = "telephony";
field public static final String NAMESPACE_TEXTCLASSIFIER = "textclassifier";
@@ -6170,6 +6229,7 @@ package android.security.keystore {
field public static final int ID_TYPE_IMEI = 2; // 0x2
field public static final int ID_TYPE_MEID = 3; // 0x3
field public static final int ID_TYPE_SERIAL = 1; // 0x1
+ field public static final int USE_INDIVIDUAL_ATTESTATION = 4; // 0x4
}
public class DeviceIdAttestationException extends java.lang.Exception {
@@ -6348,6 +6408,7 @@ package android.service.autofill {
method public android.os.IBinder onBind(android.content.Intent);
method @Nullable public float[][] onCalculateScores(@NonNull java.util.List<android.view.autofill.AutofillValue>, @NonNull java.util.List<java.lang.String>, @NonNull java.util.List<java.lang.String>, @Nullable String, @Nullable android.os.Bundle, @Nullable java.util.Map, @Nullable java.util.Map);
method @Deprecated @Nullable public float[][] onGetScores(@Nullable String, @Nullable android.os.Bundle, @NonNull java.util.List<android.view.autofill.AutofillValue>, @NonNull java.util.List<java.lang.String>);
+ field public static final String REQUIRED_ALGORITHM_CREDIT_CARD = "CREDIT_CARD";
field public static final String REQUIRED_ALGORITHM_EDIT_DISTANCE = "EDIT_DISTANCE";
field public static final String REQUIRED_ALGORITHM_EXACT_MATCH = "EXACT_MATCH";
field public static final String SERVICE_INTERFACE = "android.service.autofill.AutofillFieldClassificationService";
@@ -6542,7 +6603,8 @@ package android.service.euicc {
method public abstract int onDeleteSubscription(int, String);
method public android.service.euicc.DownloadSubscriptionResult onDownloadSubscription(int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean, @Nullable android.os.Bundle);
method @Deprecated public int onDownloadSubscription(int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean);
- method public abstract int onEraseSubscriptions(int);
+ method @Deprecated public abstract int onEraseSubscriptions(int);
+ method public int onEraseSubscriptionsWithOptions(int, @android.telephony.euicc.EuiccCardManager.ResetOption int);
method public abstract android.service.euicc.GetDefaultDownloadableSubscriptionListResult onGetDefaultDownloadableSubscriptionList(int, boolean);
method public abstract android.service.euicc.GetDownloadableSubscriptionMetadataResult onGetDownloadableSubscriptionMetadata(int, android.telephony.euicc.DownloadableSubscription, boolean);
method public abstract String onGetEid(int);
@@ -6954,6 +7016,8 @@ package android.telecom {
public final class Call {
method @Deprecated public void addListener(android.telecom.Call.Listener);
+ method public void enterBackgroundAudioProcessing();
+ method public void exitBackgroundAudioProcessing(boolean);
method @Deprecated public void removeListener(android.telecom.Call.Listener);
field @Deprecated public static final int STATE_PRE_DIAL_WAIT = 8; // 0x8
}
@@ -6962,6 +7026,10 @@ package android.telecom {
ctor @Deprecated public Call.Listener();
}
+ public static class CallScreeningService.CallResponse.Builder {
+ method public android.telecom.CallScreeningService.CallResponse.Builder setShouldScreenCallFurther(boolean);
+ }
+
public abstract class Conference extends android.telecom.Conferenceable {
method @Deprecated public final android.telecom.AudioState getAudioState();
method @Deprecated public final long getConnectTimeMillis();
@@ -6972,7 +7040,26 @@ package android.telecom {
public abstract class Connection extends android.telecom.Conferenceable {
method @Deprecated public final android.telecom.AudioState getAudioState();
+ method public final int getCallRadioTech();
+ method public final long getConnectElapsedTimeMillis();
+ method public final long getConnectTimeMillis();
+ method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle();
+ method @Nullable public final String getTelecomCallId();
method @Deprecated public void onAudioStateChanged(android.telecom.AudioState);
+ method public final void resetConnectionTime();
+ method public void setCallDirection(int);
+ method public final void setCallRadioTech(int);
+ method public final void setConnectTimeMillis(long);
+ method public final void setConnectionStartElapsedRealTime(long);
+ method public void setPhoneAccountHandle(@NonNull android.telecom.PhoneAccountHandle);
+ method public void setTelecomCallId(@NonNull String);
+ field public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 2097152; // 0x200000
+ field public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 262144; // 0x40000
+ field public static final String EXTRA_DISABLE_ADD_CALL = "android.telecom.extra.DISABLE_ADD_CALL";
+ field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 1; // 0x1
+ field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2
+ field public static final int PROPERTY_IS_DOWNGRADED_CONFERENCE = 64; // 0x40
+ field public static final int PROPERTY_REMOTELY_HOSTED = 2048; // 0x800
}
public abstract class InCallService extends android.app.Service {
@@ -7168,6 +7255,7 @@ package android.telecom {
method public void addNewUnknownCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
method @Deprecated public void clearAccounts();
method public void clearPhoneAccounts();
+ method @NonNull public android.content.Intent createLaunchEmergencyDialerIntent(@Nullable String);
method @RequiresPermission(android.Manifest.permission.DUMP) public android.telecom.TelecomAnalytics dumpAnalytics();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void enablePhoneAccount(android.telecom.PhoneAccountHandle, boolean);
method public java.util.List<android.telecom.PhoneAccountHandle> getAllPhoneAccountHandles();
@@ -7284,6 +7372,14 @@ package android.telephony {
method @NonNull public android.telephony.CarrierRestrictionRules.Builder setMultiSimPolicy(int);
}
+ public abstract class CellBroadcastService extends android.app.Service {
+ ctor public CellBroadcastService();
+ method @CallSuper public android.os.IBinder onBind(android.content.Intent);
+ method public abstract void onCdmaCellBroadcastSms(int, byte[], int);
+ method public abstract void onGsmCellBroadcastSms(int, byte[]);
+ field public static final String CELL_BROADCAST_SERVICE_INTERFACE = "android.telephony.CellBroadcastService";
+ }
+
public final class DataFailCause {
field public static final int ACCESS_ATTEMPT_ALREADY_IN_PROGRESS = 2219; // 0x8ab
field public static final int ACCESS_BLOCK = 2087; // 0x827
@@ -7846,6 +7942,8 @@ package android.telephony {
method public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onCallDisconnectCauseChanged(int, int);
method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
+ method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
+ method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
method public void onRadioPowerStateChanged(int);
@@ -7854,8 +7952,8 @@ package android.telephony {
field public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000
- field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_CALL_EMERGENCY_NUMBER = 268435456; // 0x10000000
- field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_SMS_EMERGENCY_NUMBER = 536870912; // 0x20000000
+ field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
+ field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
field public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000
@@ -8083,7 +8181,7 @@ package android.telephony {
}
public final class SmsCbMessage implements android.os.Parcelable {
- ctor public SmsCbMessage(int, int, int, @NonNull android.telephony.SmsCbLocation, int, @Nullable String, @Nullable String, int, @Nullable android.telephony.SmsCbEtwsInfo, @Nullable android.telephony.SmsCbCmasInfo);
+ ctor public SmsCbMessage(int, int, int, @NonNull android.telephony.SmsCbLocation, int, @Nullable String, @Nullable String, int, @Nullable android.telephony.SmsCbEtwsInfo, @Nullable android.telephony.SmsCbCmasInfo, int);
method @NonNull public static android.telephony.SmsCbMessage createFromCursor(@NonNull android.database.Cursor);
method public int describeContents();
method @Nullable public android.telephony.SmsCbCmasInfo getCmasWarningInfo();
@@ -8098,6 +8196,7 @@ package android.telephony {
method public long getReceivedTime();
method public int getSerialNumber();
method public int getServiceCategory();
+ method public int getSlotIndex();
method public boolean isCmasMessage();
method public boolean isEmergencyMessage();
method public boolean isEtwsMessage();
@@ -8197,6 +8296,9 @@ package android.telephony {
public class TelephonyManager {
method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void call(String, String);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void carrierActionReportDefaultNetworkStatus(int, boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void carrierActionResetAll(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void carrierActionSetRadioEnabled(int, boolean);
method public int checkCarrierPrivilegesForPackage(String);
method public int checkCarrierPrivilegesForPackageAnyPhone(String);
method public void dial(String);
@@ -8219,11 +8321,13 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getDataActivationState();
method @Deprecated public boolean getDataEnabled();
method @Deprecated public boolean getDataEnabled(int);
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDeviceSoftwareVersion(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Map<java.lang.Integer,java.lang.Integer> getLogicalToPhysicalSlotMapping();
method public static long getMaxNumberVerificationTimeoutMillis();
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getNetworkCountryIso(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getPreferredNetworkTypeBitmask();
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public int getRadioPowerState();
method public int getSimApplicationState();
@@ -8244,6 +8348,7 @@ package android.telephony {
method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduLogicalChannelBySlot(int, int, int, int, int, int, int, @Nullable String);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
method public boolean isDataConnectivityPossible();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
@@ -8331,6 +8436,14 @@ package android.telephony {
field public static final int SRVCC_STATE_HANDOVER_STARTED = 0; // 0x0
}
+ public class TelephonyRegistryManager {
+ method public void addOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
+ method public void addOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
+ method public void notifyCarrierNetworkChange(boolean);
+ method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
+ method public void removeOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
+ }
+
public final class UiccAccessRule implements android.os.Parcelable {
ctor public UiccAccessRule(byte[], @Nullable String, long);
method public int describeContents();
@@ -8367,6 +8480,27 @@ package android.telephony {
}
+package android.telephony.cdma {
+
+ public final class CdmaSmsCbProgramData implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getCategory();
+ method public int getOperation();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY = 4099; // 0x1003
+ field public static final int CATEGORY_CMAS_EXTREME_THREAT = 4097; // 0x1001
+ field public static final int CATEGORY_CMAS_LAST_RESERVED_VALUE = 4351; // 0x10ff
+ field public static final int CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT = 4096; // 0x1000
+ field public static final int CATEGORY_CMAS_SEVERE_THREAT = 4098; // 0x1002
+ field public static final int CATEGORY_CMAS_TEST_MESSAGE = 4100; // 0x1004
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.cdma.CdmaSmsCbProgramData> CREATOR;
+ field public static final int OPERATION_ADD_CATEGORY = 1; // 0x1
+ field public static final int OPERATION_CLEAR_CATEGORIES = 2; // 0x2
+ field public static final int OPERATION_DELETE_CATEGORY = 0; // 0x0
+ }
+
+}
+
package android.telephony.data {
public final class DataCallResponse implements android.os.Parcelable {
@@ -8565,7 +8699,8 @@ package android.telephony.euicc {
public class EuiccManager {
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void continueOperation(android.content.Intent, android.os.Bundle);
- method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void eraseSubscriptions(android.app.PendingIntent);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void eraseSubscriptions(@NonNull android.app.PendingIntent);
+ method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void eraseSubscriptionsWithOptions(@android.telephony.euicc.EuiccCardManager.ResetOption int, @NonNull android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDefaultDownloadableSubscriptionList(android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public int getOtaStatus();
@@ -9516,17 +9651,17 @@ package android.telephony.ims.stub {
public class ImsSmsImplBase {
ctor public ImsSmsImplBase();
- method public void acknowledgeSms(int, int, int);
- method public void acknowledgeSmsReport(int, int, int);
+ method public void acknowledgeSms(int, @IntRange(from=0, to=65535) int, int);
+ method public void acknowledgeSmsReport(int, @IntRange(from=0, to=65535) int, int);
method public String getSmsFormat();
method public void onReady();
- method @Deprecated public final void onSendSmsResult(int, int, int, int) throws java.lang.RuntimeException;
- method public final void onSendSmsResultError(int, int, int, int, int) throws java.lang.RuntimeException;
- method public final void onSendSmsResultSuccess(int, int) throws java.lang.RuntimeException;
+ method @Deprecated public final void onSendSmsResult(int, @IntRange(from=0, to=65535) int, int, int) throws java.lang.RuntimeException;
+ method public final void onSendSmsResultError(int, @IntRange(from=0, to=65535) int, int, int, int) throws java.lang.RuntimeException;
+ method public final void onSendSmsResultSuccess(int, @IntRange(from=0, to=65535) int) throws java.lang.RuntimeException;
method public final void onSmsReceived(int, String, byte[]) throws java.lang.RuntimeException;
- method @Deprecated public final void onSmsStatusReportReceived(int, int, String, byte[]) throws java.lang.RuntimeException;
+ method @Deprecated public final void onSmsStatusReportReceived(int, @IntRange(from=0, to=65535) int, String, byte[]) throws java.lang.RuntimeException;
method public final void onSmsStatusReportReceived(int, String, byte[]) throws java.lang.RuntimeException;
- method public void sendSms(int, int, String, String, boolean, byte[]);
+ method public void sendSms(int, @IntRange(from=0, to=65535) int, String, String, boolean, byte[]);
field public static final int DELIVER_STATUS_ERROR_GENERIC = 2; // 0x2
field public static final int DELIVER_STATUS_ERROR_NO_MEMORY = 3; // 0x3
field public static final int DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED = 4; // 0x4
@@ -9699,9 +9834,10 @@ package android.view {
method public final long getUserActivityTimeout();
method public final void setUserActivityTimeout(long);
field @RequiresPermission(android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS) public static final int SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 524288; // 0x80000
+ field @RequiresPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW) public static final int SYSTEM_FLAG_SHOW_FOR_ALL_USERS = 16; // 0x10
}
- @IntDef(flag=true, prefix={"SYSTEM_FLAG_"}, value={android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowManager.LayoutParams.SystemFlags {
+ @IntDef(flag=true, prefix={"SYSTEM_FLAG_"}, value={android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowManager.LayoutParams.SystemFlags {
}
}
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
new file mode 100644
index 000000000000..57a853a42676
--- /dev/null
+++ b/api/system-lint-baseline.txt
@@ -0,0 +1,391 @@
+// Baseline format: 1.0
+ActionValue: android.location.Location#EXTRA_NO_GPS_LOCATION:
+
+
+
+ArrayReturn: android.view.contentcapture.ViewNode#getAutofillOptions():
+
+
+
+GenericException: android.app.prediction.AppPredictor#finalize():
+
+GenericException: android.hardware.location.ContextHubClient#finalize():
+
+GenericException: android.net.IpSecManager.IpSecTunnelInterface#finalize():
+
+GenericException: android.service.autofill.augmented.FillWindow#finalize():
+
+
+
+InterfaceConstant: android.service.storage.ExternalStorageService#SERVICE_INTERFACE:
+
+
+
+KotlinKeyword: android.app.Notification#when:
+
+
+
+MissingNullability: android.hardware.soundtrigger.SoundTrigger.ModuleProperties#toString():
+
+MissingNullability: android.hardware.soundtrigger.SoundTrigger.ModuleProperties#writeToParcel(android.os.Parcel, int) parameter #0:
+
+MissingNullability: android.media.session.MediaSessionManager.Callback#onAddressedPlayerChanged(android.content.ComponentName) parameter #0:
+
+MissingNullability: android.media.session.MediaSessionManager.Callback#onAddressedPlayerChanged(android.media.session.MediaSession.Token) parameter #0:
+
+MissingNullability: android.media.session.MediaSessionManager.Callback#onMediaKeyEventDispatched(android.view.KeyEvent, android.content.ComponentName) parameter #0:
+
+MissingNullability: android.media.session.MediaSessionManager.Callback#onMediaKeyEventDispatched(android.view.KeyEvent, android.content.ComponentName) parameter #1:
+
+MissingNullability: android.media.session.MediaSessionManager.Callback#onMediaKeyEventDispatched(android.view.KeyEvent, android.media.session.MediaSession.Token) parameter #0:
+
+MissingNullability: android.media.session.MediaSessionManager.Callback#onMediaKeyEventDispatched(android.view.KeyEvent, android.media.session.MediaSession.Token) parameter #1:
+
+MissingNullability: android.media.soundtrigger.SoundTriggerDetectionService#onUnbind(android.content.Intent) parameter #0:
+
+MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #0:
+
+MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #1:
+
+MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #2:
+
+MissingNullability: android.net.wifi.rtt.RangingRequest.Builder#addResponder(android.net.wifi.rtt.ResponderConfig):
+
+MissingNullability: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context) parameter #0:
+
+MissingNullability: android.provider.ContactsContract.MetadataSync#CONTENT_URI:
+
+MissingNullability: android.provider.ContactsContract.MetadataSync#METADATA_AUTHORITY_URI:
+
+MissingNullability: android.provider.ContactsContract.MetadataSyncState#CONTENT_URI:
+
+MissingNullability: android.provider.SearchIndexablesProvider#attachInfo(android.content.Context, android.content.pm.ProviderInfo) parameter #0:
+
+MissingNullability: android.provider.SearchIndexablesProvider#attachInfo(android.content.Context, android.content.pm.ProviderInfo) parameter #1:
+
+MissingNullability: android.service.autofill.augmented.AugmentedAutofillService#onUnbind(android.content.Intent) parameter #0:
+
+MissingNullability: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #0:
+
+MissingNullability: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #1:
+
+MissingNullability: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #2:
+
+MissingNullability: android.service.notification.NotificationAssistantService#attachBaseContext(android.content.Context) parameter #0:
+
+MissingNullability: android.telecom.CallScreeningService.CallResponse.Builder#setShouldScreenCallFurther(boolean):
+
+MissingNullability: android.telephony.CallerInfo#toString():
+
+MissingNullability: android.telephony.CellBroadcastService#onBind(android.content.Intent):
+
+MissingNullability: android.telephony.CellBroadcastService#onBind(android.content.Intent) parameter #0:
+
+MissingNullability: android.telephony.CellBroadcastService#onCdmaCellBroadcastSms(int, byte[]) parameter #1:
+
+MissingNullability: android.telephony.CellBroadcastService#onCdmaCellBroadcastSms(int, byte[], int) parameter #1:
+
+MissingNullability: android.telephony.CellBroadcastService#onGsmCellBroadcastSms(int, byte[]) parameter #1:
+
+MissingNullability: android.telephony.ModemActivityInfo#toString():
+
+MissingNullability: android.telephony.ModemActivityInfo#writeToParcel(android.os.Parcel, int) parameter #0:
+
+MissingNullability: android.telephony.ModemActivityInfo.TransmitPower#toString():
+
+MissingNullability: android.telephony.NetworkService#onUnbind(android.content.Intent) parameter #0:
+
+MissingNullability: android.telephony.SmsCbCmasInfo#toString():
+
+MissingNullability: android.telephony.SmsCbCmasInfo#writeToParcel(android.os.Parcel, int) parameter #0:
+
+MissingNullability: android.telephony.SmsCbEtwsInfo#toString():
+
+MissingNullability: android.telephony.SmsCbEtwsInfo#writeToParcel(android.os.Parcel, int) parameter #0:
+
+MissingNullability: android.telephony.SmsCbLocation#equals(Object) parameter #0:
+
+MissingNullability: android.telephony.SmsCbLocation#toString():
+
+MissingNullability: android.telephony.SmsCbLocation#writeToParcel(android.os.Parcel, int) parameter #0:
+
+MissingNullability: android.telephony.SmsCbMessage#toString():
+
+MissingNullability: android.telephony.SmsCbMessage#writeToParcel(android.os.Parcel, int) parameter #0:
+
+MissingNullability: android.telephony.SubscriptionPlan.Builder#createRecurringDaily(java.time.ZonedDateTime) parameter #0:
+
+MissingNullability: android.telephony.SubscriptionPlan.Builder#createRecurringMonthly(java.time.ZonedDateTime) parameter #0:
+
+MissingNullability: android.telephony.SubscriptionPlan.Builder#createRecurringWeekly(java.time.ZonedDateTime) parameter #0:
+
+MissingNullability: android.telephony.cdma.CdmaSmsCbProgramData#toString():
+
+MissingNullability: android.telephony.cdma.CdmaSmsCbProgramData#writeToParcel(android.os.Parcel, int) parameter #0:
+
+MissingNullability: android.telephony.data.DataService#onUnbind(android.content.Intent) parameter #0:
+
+MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsStatusReportReceived(int, String, byte[]) parameter #1:
+
+MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsStatusReportReceived(int, String, byte[]) parameter #2:
+
+MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String):
+
+MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String) parameter #0:
+
+
+
+NoClone: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #0:
+
+
+
+ProtectedMember: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context):
+
+ProtectedMember: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]):
+
+ProtectedMember: android.service.notification.NotificationAssistantService#attachBaseContext(android.content.Context):
+
+
+
+SamShouldBeLast: android.accounts.AccountManager#addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
+
+SamShouldBeLast: android.accounts.AccountManager#addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean):
+
+SamShouldBeLast: android.accounts.AccountManager#addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean, String[]):
+
+SamShouldBeLast: android.accounts.AccountManager#confirmCredentials(android.accounts.Account, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
+
+SamShouldBeLast: android.accounts.AccountManager#editProperties(String, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
+
+SamShouldBeLast: android.accounts.AccountManager#finishSession(android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
+
+SamShouldBeLast: android.accounts.AccountManager#getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler):
+
+SamShouldBeLast: android.accounts.AccountManager#getAuthToken(android.accounts.Account, String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
+
+SamShouldBeLast: android.accounts.AccountManager#getAuthToken(android.accounts.Account, String, android.os.Bundle, boolean, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
+
+SamShouldBeLast: android.accounts.AccountManager#getAuthToken(android.accounts.Account, String, boolean, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
+
+SamShouldBeLast: android.accounts.AccountManager#getAuthTokenByFeatures(String, String, String[], android.app.Activity, android.os.Bundle, android.os.Bundle, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
+
+SamShouldBeLast: android.accounts.AccountManager#hasFeatures(android.accounts.Account, String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler):
+
+SamShouldBeLast: android.accounts.AccountManager#isCredentialsUpdateSuggested(android.accounts.Account, String, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler):
+
+SamShouldBeLast: android.accounts.AccountManager#removeAccount(android.accounts.Account, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler):
+
+SamShouldBeLast: android.accounts.AccountManager#removeAccount(android.accounts.Account, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
+
+SamShouldBeLast: android.accounts.AccountManager#renameAccount(android.accounts.Account, String, android.accounts.AccountManagerCallback<android.accounts.Account>, android.os.Handler):
+
+SamShouldBeLast: android.accounts.AccountManager#startAddAccountSession(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
+
+SamShouldBeLast: android.accounts.AccountManager#startUpdateCredentialsSession(android.accounts.Account, String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
+
+SamShouldBeLast: android.accounts.AccountManager#updateCredentials(android.accounts.Account, String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
+
+SamShouldBeLast: android.app.AlarmManager#set(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler):
+
+SamShouldBeLast: android.app.AlarmManager#setExact(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler):
+
+SamShouldBeLast: android.app.AlarmManager#setWindow(int, long, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler):
+
+SamShouldBeLast: android.app.WallpaperInfo#dump(android.util.Printer, String):
+
+SamShouldBeLast: android.app.admin.DevicePolicyManager#installSystemUpdate(android.content.ComponentName, android.net.Uri, java.util.concurrent.Executor, android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback):
+
+SamShouldBeLast: android.content.Context#bindIsolatedService(android.content.Intent, int, String, java.util.concurrent.Executor, android.content.ServiceConnection):
+
+SamShouldBeLast: android.content.Context#bindService(android.content.Intent, int, java.util.concurrent.Executor, android.content.ServiceConnection):
+
+SamShouldBeLast: android.content.ContextWrapper#bindIsolatedService(android.content.Intent, int, String, java.util.concurrent.Executor, android.content.ServiceConnection):
+
+SamShouldBeLast: android.content.ContextWrapper#bindService(android.content.Intent, int, java.util.concurrent.Executor, android.content.ServiceConnection):
+
+SamShouldBeLast: android.content.IntentFilter#dump(android.util.Printer, String):
+
+SamShouldBeLast: android.content.pm.ApplicationInfo#dump(android.util.Printer, String):
+
+SamShouldBeLast: android.content.pm.LauncherApps#registerPackageInstallerSessionCallback(java.util.concurrent.Executor, android.content.pm.PackageInstaller.SessionCallback):
+
+SamShouldBeLast: android.content.pm.PackageItemInfo#dumpBack(android.util.Printer, String):
+
+SamShouldBeLast: android.content.pm.PackageItemInfo#dumpFront(android.util.Printer, String):
+
+SamShouldBeLast: android.content.pm.ResolveInfo#dump(android.util.Printer, String):
+
+SamShouldBeLast: android.location.Location#dump(android.util.Printer, String):
+
+SamShouldBeLast: android.location.LocationManager#addNmeaListener(android.location.OnNmeaMessageListener, android.os.Handler):
+
+SamShouldBeLast: android.location.LocationManager#registerGnssMeasurementsCallback(java.util.concurrent.Executor, android.location.GnssMeasurementsEvent.Callback):
+
+SamShouldBeLast: android.location.LocationManager#registerGnssNavigationMessageCallback(java.util.concurrent.Executor, android.location.GnssNavigationMessage.Callback):
+
+SamShouldBeLast: android.location.LocationManager#registerGnssStatusCallback(java.util.concurrent.Executor, android.location.GnssStatus.Callback):
+
+SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(String, long, float, java.util.concurrent.Executor, android.location.LocationListener):
+
+SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(android.location.LocationRequest, java.util.concurrent.Executor, android.location.LocationListener):
+
+SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(long, float, android.location.Criteria, java.util.concurrent.Executor, android.location.LocationListener):
+
+SamShouldBeLast: android.media.AudioFocusRequest.Builder#setOnAudioFocusChangeListener(android.media.AudioManager.OnAudioFocusChangeListener, android.os.Handler):
+
+SamShouldBeLast: android.media.AudioManager#requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int):
+
+SamShouldBeLast: android.media.AudioRecord#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
+
+SamShouldBeLast: android.media.AudioRecord#registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback):
+
+SamShouldBeLast: android.media.AudioRecordingMonitor#registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback):
+
+SamShouldBeLast: android.media.AudioRouting#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
+
+SamShouldBeLast: android.media.MediaRecorder#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
+
+SamShouldBeLast: android.media.MediaRecorder#registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback):
+
+SamShouldBeLast: android.media.session.MediaSessionManager#addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName):
+
+SamShouldBeLast: android.media.session.MediaSessionManager#addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName, android.os.Handler):
+
+SamShouldBeLast: android.media.session.MediaSessionManager#addOnSession2TokensChangedListener(android.media.session.MediaSessionManager.OnSession2TokensChangedListener, android.os.Handler):
+
+SamShouldBeLast: android.media.session.MediaSessionManager#registerCallback(java.util.concurrent.Executor, android.media.session.MediaSessionManager.Callback):
+
+SamShouldBeLast: android.net.ConnectivityManager#createSocketKeepalive(android.net.Network, android.net.IpSecManager.UdpEncapsulationSocket, java.net.InetAddress, java.net.InetAddress, java.util.concurrent.Executor, android.net.SocketKeepalive.Callback):
+
+SamShouldBeLast: android.net.wifi.rtt.WifiRttManager#startRanging(android.net.wifi.rtt.RangingRequest, java.util.concurrent.Executor, android.net.wifi.rtt.RangingResultCallback):
+
+SamShouldBeLast: android.nfc.NfcAdapter#enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle):
+
+SamShouldBeLast: android.nfc.NfcAdapter#ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler):
+
+SamShouldBeLast: android.nfc.NfcAdapter#setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity):
+
+SamShouldBeLast: android.nfc.NfcAdapter#setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...):
+
+SamShouldBeLast: android.nfc.NfcAdapter#setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...):
+
+SamShouldBeLast: android.os.Binder#attachInterface(android.os.IInterface, String):
+
+SamShouldBeLast: android.os.Binder#linkToDeath(android.os.IBinder.DeathRecipient, int):
+
+SamShouldBeLast: android.os.Binder#unlinkToDeath(android.os.IBinder.DeathRecipient, int):
+
+SamShouldBeLast: android.os.Handler#dump(android.util.Printer, String):
+
+SamShouldBeLast: android.os.Handler#postAtTime(Runnable, Object, long):
+
+SamShouldBeLast: android.os.Handler#postAtTime(Runnable, long):
+
+SamShouldBeLast: android.os.Handler#postDelayed(Runnable, Object, long):
+
+SamShouldBeLast: android.os.Handler#postDelayed(Runnable, long):
+
+SamShouldBeLast: android.os.Handler#removeCallbacks(Runnable, Object):
+
+SamShouldBeLast: android.os.IBinder#linkToDeath(android.os.IBinder.DeathRecipient, int):
+
+SamShouldBeLast: android.os.IBinder#unlinkToDeath(android.os.IBinder.DeathRecipient, int):
+
+SamShouldBeLast: android.os.RecoverySystem#verifyPackage(java.io.File, android.os.RecoverySystem.ProgressListener, java.io.File):
+
+SamShouldBeLast: android.telephony.MbmsDownloadSession#addProgressListener(android.telephony.mbms.DownloadRequest, java.util.concurrent.Executor, android.telephony.mbms.DownloadProgressListener):
+
+SamShouldBeLast: android.telephony.MbmsDownloadSession#addStatusListener(android.telephony.mbms.DownloadRequest, java.util.concurrent.Executor, android.telephony.mbms.DownloadStatusListener):
+
+SamShouldBeLast: android.telephony.MbmsDownloadSession#create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsDownloadSessionCallback):
+
+SamShouldBeLast: android.telephony.MbmsDownloadSession#create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsDownloadSessionCallback):
+
+SamShouldBeLast: android.telephony.MbmsGroupCallSession#create(android.content.Context, int, java.util.concurrent.Executor, android.telephony.mbms.MbmsGroupCallSessionCallback):
+
+SamShouldBeLast: android.telephony.MbmsGroupCallSession#create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsGroupCallSessionCallback):
+
+SamShouldBeLast: android.telephony.MbmsGroupCallSession#startGroupCall(long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, java.util.concurrent.Executor, android.telephony.mbms.GroupCallCallback):
+
+SamShouldBeLast: android.telephony.MbmsStreamingSession#create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsStreamingSessionCallback):
+
+SamShouldBeLast: android.telephony.MbmsStreamingSession#create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsStreamingSessionCallback):
+
+SamShouldBeLast: android.telephony.MbmsStreamingSession#startStreaming(android.telephony.mbms.StreamingServiceInfo, java.util.concurrent.Executor, android.telephony.mbms.StreamingServiceCallback):
+
+SamShouldBeLast: android.telephony.SmsManager#getSmsMessagesForFinancialApp(android.os.Bundle, java.util.concurrent.Executor, android.telephony.SmsManager.FinancialSmsCallback):
+
+SamShouldBeLast: android.telephony.SubscriptionManager#addOnOpportunisticSubscriptionsChangedListener(java.util.concurrent.Executor, android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener):
+
+SamShouldBeLast: android.telephony.TelephonyManager#requestCellInfoUpdate(java.util.concurrent.Executor, android.telephony.TelephonyManager.CellInfoCallback):
+
+SamShouldBeLast: android.telephony.TelephonyManager#requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback):
+
+SamShouldBeLast: android.telephony.TelephonyManager#setPreferredOpportunisticDataSubscription(int, boolean, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+
+SamShouldBeLast: android.telephony.TelephonyManager#updateAvailableNetworks(java.util.List<android.telephony.AvailableNetworkInfo>, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Integer>):
+
+SamShouldBeLast: android.view.View#postDelayed(Runnable, long):
+
+SamShouldBeLast: android.view.View#postOnAnimationDelayed(Runnable, long):
+
+SamShouldBeLast: android.view.View#scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long):
+
+SamShouldBeLast: android.view.Window#addOnFrameMetricsAvailableListener(android.view.Window.OnFrameMetricsAvailableListener, android.os.Handler):
+
+SamShouldBeLast: android.view.accessibility.AccessibilityManager#addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener, android.os.Handler):
+
+SamShouldBeLast: android.view.accessibility.AccessibilityManager#addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener, android.os.Handler):
+
+SamShouldBeLast: android.webkit.WebChromeClient#onShowFileChooser(android.webkit.WebView, android.webkit.ValueCallback<android.net.Uri[]>, android.webkit.WebChromeClient.FileChooserParams):
+
+SamShouldBeLast: android.webkit.WebView#setWebViewRenderProcessClient(java.util.concurrent.Executor, android.webkit.WebViewRenderProcessClient):
+
+
+
+ServiceName: android.Manifest.permission#BIND_ATTENTION_SERVICE:
+
+ServiceName: android.Manifest.permission#BIND_AUGMENTED_AUTOFILL_SERVICE:
+
+ServiceName: android.Manifest.permission#BIND_CELL_BROADCAST_SERVICE:
+
+ServiceName: android.Manifest.permission#BIND_CONTENT_CAPTURE_SERVICE:
+
+ServiceName: android.Manifest.permission#BIND_CONTENT_SUGGESTIONS_SERVICE:
+
+ServiceName: android.Manifest.permission#BIND_EUICC_SERVICE:
+
+ServiceName: android.Manifest.permission#BIND_EXTERNAL_STORAGE_SERVICE:
+
+ServiceName: android.Manifest.permission#BIND_IMS_SERVICE:
+
+ServiceName: android.Manifest.permission#BIND_NETWORK_RECOMMENDATION_SERVICE:
+
+ServiceName: android.Manifest.permission#BIND_NOTIFICATION_ASSISTANT_SERVICE:
+
+ServiceName: android.Manifest.permission#BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE:
+
+ServiceName: android.Manifest.permission#BIND_PRINT_RECOMMENDATION_SERVICE:
+
+ServiceName: android.Manifest.permission#BIND_RESOLVER_RANKER_SERVICE:
+
+ServiceName: android.Manifest.permission#BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE:
+
+ServiceName: android.Manifest.permission#BIND_SETTINGS_SUGGESTIONS_SERVICE:
+
+ServiceName: android.Manifest.permission#BIND_SOUND_TRIGGER_DETECTION_SERVICE:
+
+ServiceName: android.Manifest.permission#BIND_TELEPHONY_DATA_SERVICE:
+
+ServiceName: android.Manifest.permission#BIND_TELEPHONY_NETWORK_SERVICE:
+
+ServiceName: android.Manifest.permission#BIND_TEXTCLASSIFIER_SERVICE:
+
+ServiceName: android.Manifest.permission#BIND_TV_REMOTE_SERVICE:
+
+ServiceName: android.Manifest.permission#PROVIDE_RESOLVER_RANKER_SERVICE:
+
+ServiceName: android.Manifest.permission#REQUEST_NOTIFICATION_ASSISTANT_SERVICE:
+
+ServiceName: android.provider.DeviceConfig#NAMESPACE_PACKAGE_MANAGER_SERVICE:
+
diff --git a/api/test-current.txt b/api/test-current.txt
index 34312f69116a..b3834bf9681c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5,6 +5,7 @@ package android {
field public static final String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS";
field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS";
+ field public static final String BIND_CELL_BROADCAST_SERVICE = "android.permission.BIND_CELL_BROADCAST_SERVICE";
field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
field public static final String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE";
field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
@@ -21,6 +22,10 @@ package android {
field public static final String WRITE_OBB = "android.permission.WRITE_OBB";
}
+ public static final class Manifest.permission_group {
+ field public static final String UNDEFINED = "android.permission-group.UNDEFINED";
+ }
+
public static final class R.bool {
field public static final int config_perDisplayFocusEnabled = 17891332; // 0x1110004
}
@@ -126,7 +131,7 @@ package android.app {
method public void startActivity(@NonNull android.content.Intent);
method public void startActivity(@NonNull android.content.Intent, android.os.UserHandle);
method public void startActivity(@NonNull android.app.PendingIntent);
- method public void startActivity(@NonNull android.app.PendingIntent, @NonNull android.app.ActivityOptions);
+ method public void startActivity(@NonNull android.app.PendingIntent, @Nullable android.content.Intent, @NonNull android.app.ActivityOptions);
}
public abstract static class ActivityView.StateCallback {
@@ -300,6 +305,7 @@ package android.app {
public static final class AppOpsManager.OpEntry implements android.os.Parcelable {
method public int describeContents();
method public long getDuration();
+ method @NonNull public java.util.Map<java.lang.String,android.app.AppOpsManager.OpFeatureEntry> getFeatures();
method public long getLastAccessBackgroundTime(int);
method public long getLastAccessForegroundTime(int);
method public long getLastAccessTime(int);
@@ -313,15 +319,37 @@ package android.app {
method public long getLastRejectTime(int, int, int);
method public int getMode();
method @NonNull public String getOpStr();
- method @Nullable public String getProxyPackageName();
+ method @Deprecated @Nullable public String getProxyPackageName();
method @Nullable public String getProxyPackageName(int, int);
- method public int getProxyUid();
+ method @Deprecated public int getProxyUid();
method public int getProxyUid(int, int);
method public boolean isRunning();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpEntry> CREATOR;
}
+ public static final class AppOpsManager.OpFeatureEntry {
+ method public long getDuration();
+ method public long getLastAccessBackgroundTime(int);
+ method public long getLastAccessForegroundTime(int);
+ method public long getLastAccessTime(int);
+ method public long getLastAccessTime(int, int, int);
+ method public long getLastBackgroundDuration(int);
+ method public long getLastDuration(int, int, int);
+ method public long getLastForegroundDuration(int);
+ method public long getLastRejectBackgroundTime(int);
+ method public long getLastRejectForegroundTime(int);
+ method public long getLastRejectTime(int);
+ method public long getLastRejectTime(int, int, int);
+ method @Nullable public String getProxyFeatureId();
+ method @Nullable public String getProxyFeatureId(int, int);
+ method @Nullable public String getProxyPackageName();
+ method @Nullable public String getProxyPackageName(int, int);
+ method public int getProxyUid();
+ method public int getProxyUid(int, int);
+ method public boolean isRunning();
+ }
+
public class DownloadManager {
field public static final String COLUMN_MEDIASTORE_URI = "mediastore_uri";
}
@@ -654,7 +682,8 @@ package android.content {
}
public abstract class Context {
- method public android.content.Context createPackageContextAsUser(String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle, int);
+ method @NonNull public android.content.Context createPackageContextAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.view.Display getDisplay();
method public abstract int getDisplayId();
method public android.os.UserHandle getUser();
@@ -725,6 +754,7 @@ package android.content.pm {
method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS", "android.permission.GET_RUNTIME_PERMISSIONS"}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @NonNull public abstract String getServicesSystemSharedLibraryPackageName();
method @NonNull public abstract String getSharedSystemSharedLibraryPackageName();
+ method @Nullable public String[] getTelephonyPackageNames();
method @Nullable public String getWellbeingPackageName();
method @RequiresPermission("android.permission.GRANT_RUNTIME_PERMISSIONS") public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void removeOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener);
@@ -739,7 +769,8 @@ package android.content.pm {
field public static final int FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT = 4096; // 0x1000
field public static final int FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT = 8192; // 0x2000
field public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 64; // 0x40
- field public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8
+ field public static final int FLAG_PERMISSION_REVOKED_COMPAT = 8; // 0x8
+ field @Deprecated public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8
field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80
field public static final int FLAG_PERMISSION_SYSTEM_FIXED = 16; // 0x10
field public static final int FLAG_PERMISSION_USER_FIXED = 2; // 0x2
@@ -762,8 +793,10 @@ package android.content.pm {
field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000
field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
+ field public static final int PROTECTION_FLAG_TELEPHONY = 4194304; // 0x400000
field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
+ field public static final int PROTECTION_FLAG_WIFI = 8388608; // 0x800000
field @Nullable public final String backgroundPermission;
}
@@ -781,7 +814,9 @@ package android.content.res {
public final class AssetManager implements java.lang.AutoCloseable {
method @NonNull public String[] getApkPaths();
- method @Nullable public java.util.Map<java.lang.String,java.lang.String> getOverlayableMap(String);
+ method @Nullable public String getLastResourceResolution();
+ method @Nullable public String getOverlayablesToString(String);
+ method public void setResourceResolutionLoggingEnabled(boolean);
}
public final class Configuration implements java.lang.Comparable<android.content.res.Configuration> android.os.Parcelable {
@@ -920,6 +955,10 @@ package android.hardware.camera2 {
field public static final int SESSION_OPERATION_MODE_VENDOR_START = 32768; // 0x8000
}
+ public final class CameraManager {
+ method public String[] getCameraIdListNoLazy() throws android.hardware.camera2.CameraAccessException;
+ }
+
}
package android.hardware.display {
@@ -1078,14 +1117,19 @@ package android.location {
public class Location implements android.os.Parcelable {
method public void makeComplete();
+ method public void setExtraLocation(@Nullable String, @Nullable android.location.Location);
+ field public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
}
public class LocationManager {
method @NonNull public String[] getBackgroundThrottlingWhitelist();
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>);
method @NonNull public String[] getIgnoreSettingsWhitelist();
+ method @Nullable @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public java.util.List<java.lang.String> getProviderPackages(@NonNull String);
method @NonNull public java.util.List<android.location.LocationRequest> getTestProviderCurrentRequests(String);
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@NonNull android.location.LocationRequest, @NonNull android.app.PendingIntent);
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle);
}
@@ -1729,6 +1773,7 @@ package android.os {
}
public class DeviceIdleManager {
+ method @RequiresPermission("android.permission.DEVICE_POWER") public int addPowerSaveWhitelistApps(@NonNull java.util.List<java.lang.String>);
method @NonNull public String[] getSystemPowerWhitelist();
method @NonNull public String[] getSystemPowerWhitelistExceptIdle();
}
@@ -2023,10 +2068,18 @@ package android.os {
public final class UserHandle implements android.os.Parcelable {
method public static int getAppId(int);
method public int getIdentifier();
+ method public static int getUid(int, int);
+ method public static int getUserId(int);
method public static boolean isApp(int);
+ method public static int myUserId();
+ method public static android.os.UserHandle of(int);
field @NonNull public static final android.os.UserHandle ALL;
field @NonNull public static final android.os.UserHandle CURRENT;
+ field public static final int MIN_SECONDARY_USER_ID = 10; // 0xa
field @NonNull public static final android.os.UserHandle SYSTEM;
+ field public static final int USER_ALL = -1; // 0xffffffff
+ field public static final int USER_NULL = -10000; // 0xffffd8f0
+ field public static final int USER_SYSTEM = 0; // 0x0
}
public class UserManager {
@@ -2224,8 +2277,11 @@ package android.os.strictmode {
package android.permission {
public final class PermissionControllerManager {
+ method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.RESTORE_RUNTIME_PERMISSIONS"}) public void applyStagedRuntimePermissionBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission("android.permission.GET_RUNTIME_PERMISSIONS") public void getAppPermissions(@NonNull String, @NonNull android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, @Nullable android.os.Handler);
+ method @RequiresPermission("android.permission.GET_RUNTIME_PERMISSIONS") public void getRuntimePermissionBackup(@NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<byte[]>);
method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public void revokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull java.util.concurrent.Executor, @NonNull android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback);
+ method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.RESTORE_RUNTIME_PERMISSIONS"}) public void stageAndApplyRuntimePermissionsBackup(@NonNull byte[], @NonNull android.os.UserHandle);
field public static final int COUNT_ONLY_WHEN_GRANTED = 1; // 0x1
field public static final int COUNT_WHEN_SYSTEM = 2; // 0x2
field public static final int REASON_INSTALLER_POLICY_VIOLATION = 2; // 0x2
@@ -2394,7 +2450,6 @@ package android.provider {
field public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis";
field public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis";
field public static final String NOTIFICATION_BADGING = "notification_badging";
- field @Deprecated public static final String NOTIFICATION_BUBBLES = "notification_bubbles";
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";
field public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service";
@@ -2426,6 +2481,7 @@ package android.security.keystore {
field public static final int ID_TYPE_IMEI = 2; // 0x2
field public static final int ID_TYPE_MEID = 3; // 0x3
field public static final int ID_TYPE_SERIAL = 1; // 0x1
+ field public static final int USE_INDIVIDUAL_ATTESTATION = 4; // 0x4
}
public class DeviceIdAttestationException extends java.lang.Exception {
@@ -2470,6 +2526,7 @@ package android.service.autofill {
public abstract class AutofillFieldClassificationService extends android.app.Service {
ctor public AutofillFieldClassificationService();
method public android.os.IBinder onBind(android.content.Intent);
+ field public static final String REQUIRED_ALGORITHM_CREDIT_CARD = "CREDIT_CARD";
field public static final String REQUIRED_ALGORITHM_EDIT_DISTANCE = "EDIT_DISTANCE";
field public static final String REQUIRED_ALGORITHM_EXACT_MATCH = "EXACT_MATCH";
field public static final String SERVICE_INTERFACE = "android.service.autofill.AutofillFieldClassificationService";
@@ -2760,10 +2817,23 @@ package android.service.quicksettings {
package android.telecom {
+ public final class Call {
+ method public void enterBackgroundAudioProcessing();
+ method public void exitBackgroundAudioProcessing(boolean);
+ }
+
+ public static class Call.Details {
+ method public String getTelecomCallId();
+ }
+
public final class CallAudioState implements android.os.Parcelable {
ctor public CallAudioState(boolean, int, int, @Nullable android.bluetooth.BluetoothDevice, @NonNull java.util.Collection<android.bluetooth.BluetoothDevice>);
}
+ public static class CallScreeningService.CallResponse.Builder {
+ method public android.telecom.CallScreeningService.CallResponse.Builder setShouldScreenCallFurther(boolean);
+ }
+
public abstract class Conference extends android.telecom.Conferenceable {
method public android.telecom.Connection getPrimaryConnection();
}
@@ -2879,6 +2949,13 @@ package android.telephony {
method public static void setMinMatchForTest(int);
}
+ public class PhoneStateListener {
+ method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
+ method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
+ field @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
+ field @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
+ }
+
public class ServiceState implements android.os.Parcelable {
method public void addNetworkRegistrationInfo(android.telephony.NetworkRegistrationInfo);
method public void setCdmaSystemAndNetworkId(int, int);
@@ -2903,6 +2980,7 @@ package android.telephony {
method public int checkCarrierPrivilegesForPackage(String);
method public int getCarrierIdListVersion();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag();
+ method @NonNull @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getNetworkCountryIso(int);
method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile();
method @Deprecated public void setCarrierTestOverride(String, String, String, String, String, String, String);
@@ -2916,6 +2994,14 @@ package android.telephony {
}
+package android.telephony.emergency {
+
+ public final class EmergencyNumber implements java.lang.Comparable<android.telephony.emergency.EmergencyNumber> android.os.Parcelable {
+ field public static final int EMERGENCY_NUMBER_SOURCE_TEST = 32; // 0x20
+ }
+
+}
+
package android.telephony.mbms {
public static class DownloadRequest.Builder {
@@ -3047,7 +3133,6 @@ package android.util {
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_WIFITRACKER2 = "settings_wifitracker2";
- field public static final String USE_BUGREPORT_API = "settings_use_bugreport_api";
}
public class TimeUtils {
@@ -3338,6 +3423,7 @@ package android.view {
public class WindowlessViewRoot {
ctor public WindowlessViewRoot(android.content.Context, android.view.Display, android.view.SurfaceControl);
method public void addView(android.view.View, android.view.WindowManager.LayoutParams);
+ method public void relayout(android.view.WindowManager.LayoutParams);
}
}
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index d4d587108a54..41a17064c3ba 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -43,9 +43,10 @@ cc_library {
"libidmap2/Policies.cpp",
"libidmap2/PrettyPrintVisitor.cpp",
"libidmap2/RawPrintVisitor.cpp",
+ "libidmap2/ResourceMapping.cpp",
"libidmap2/ResourceUtils.cpp",
"libidmap2/Result.cpp",
- "libidmap2/Xml.cpp",
+ "libidmap2/XmlParser.cpp",
"libidmap2/ZipFile.cpp",
],
export_include_dirs: ["include"],
@@ -69,6 +70,7 @@ cc_library {
static_libs: [
"libandroidfw",
"libbase",
+ "libcutils",
"libutils",
"libziparchive",
],
@@ -96,9 +98,10 @@ cc_test {
"tests/PoliciesTests.cpp",
"tests/PrettyPrintVisitorTests.cpp",
"tests/RawPrintVisitorTests.cpp",
+ "tests/ResourceMappingTests.cpp",
"tests/ResourceUtilsTests.cpp",
"tests/ResultTests.cpp",
- "tests/XmlTests.cpp",
+ "tests/XmlParserTests.cpp",
"tests/ZipFileTests.cpp",
],
required: [
@@ -121,6 +124,7 @@ cc_test {
static_libs: [
"libandroidfw",
"libbase",
+ "libcutils",
"libidmap2",
"liblog",
"libutils",
@@ -163,6 +167,7 @@ cc_binary {
static_libs: [
"libandroidfw",
"libbase",
+ "libcutils",
"libidmap2",
"liblog",
"libutils",
diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp
index bb8d92737563..3ff6d3514331 100644
--- a/cmds/idmap2/idmap2/Create.cpp
+++ b/cmds/idmap2/idmap2/Create.cpp
@@ -50,7 +50,7 @@ Result<Unit> Create(const std::vector<std::string>& args) {
std::string overlay_apk_path;
std::string idmap_path;
std::vector<std::string> policies;
- bool ignore_overlayable;
+ bool ignore_overlayable = false;
const CommandLineOptions opts =
CommandLineOptions("idmap2 create")
@@ -99,8 +99,8 @@ Result<Unit> Create(const std::vector<std::string>& args) {
return Error("failed to load apk %s", overlay_apk_path.c_str());
}
- const auto idmap = Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path,
- *overlay_apk, fulfilled_policies, !ignore_overlayable);
+ const auto idmap =
+ Idmap::FromApkAssets(*target_apk, *overlay_apk, fulfilled_policies, !ignore_overlayable);
if (!idmap) {
return Error(idmap.GetError(), "failed to create idmap");
}
diff --git a/cmds/idmap2/idmap2/Dump.cpp b/cmds/idmap2/idmap2/Dump.cpp
index 8716bf313ed0..47f442aab235 100644
--- a/cmds/idmap2/idmap2/Dump.cpp
+++ b/cmds/idmap2/idmap2/Dump.cpp
@@ -39,7 +39,7 @@ using android::idmap2::Unit;
Result<Unit> Dump(const std::vector<std::string>& args) {
SYSTRACE << "Dump " << args;
std::string idmap_path;
- bool verbose;
+ bool verbose = false;
const CommandLineOptions opts =
CommandLineOptions("idmap2 dump")
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index b7ae9d090cee..c5cf9807b689 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -33,9 +33,10 @@
#include "androidfw/Util.h"
#include "idmap2/CommandLineOptions.h"
#include "idmap2/Idmap.h"
+#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
-#include "idmap2/Xml.h"
+#include "idmap2/XmlParser.h"
#include "idmap2/ZipFile.h"
#include "utils/String16.h"
#include "utils/String8.h"
@@ -57,8 +58,7 @@ using android::idmap2::IdmapHeader;
using android::idmap2::ResourceId;
using android::idmap2::Result;
using android::idmap2::Unit;
-using android::idmap2::Xml;
-using android::idmap2::ZipFile;
+using android::idmap2::utils::ExtractOverlayManifestInfo;
using android::util::Utf16ToUtf8;
namespace {
@@ -132,29 +132,6 @@ Result<std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId res
return out;
}
-Result<std::string> GetTargetPackageNameFromManifest(const std::string& apk_path) {
- const auto zip = ZipFile::Open(apk_path);
- if (!zip) {
- return Error("failed to open %s as zip", apk_path.c_str());
- }
- const auto entry = zip->Uncompress("AndroidManifest.xml");
- if (!entry) {
- return Error("failed to uncompress AndroidManifest.xml in %s", apk_path.c_str());
- }
- const auto xml = Xml::Create(entry->buf, entry->size);
- if (!xml) {
- return Error("failed to create XML buffer");
- }
- const auto tag = xml->FindTag("overlay");
- if (!tag) {
- return Error("failed to find <overlay> tag");
- }
- const auto iter = tag->find("targetPackage");
- if (iter == tag->end()) {
- return Error("failed to find targetPackage attribute");
- }
- return iter->second;
-}
} // namespace
Result<Unit> Lookup(const std::vector<std::string>& args) {
@@ -202,12 +179,12 @@ Result<Unit> Lookup(const std::vector<std::string>& args) {
}
apk_assets.push_back(std::move(target_apk));
- const Result<std::string> package_name =
- GetTargetPackageNameFromManifest(idmap_header->GetOverlayPath().to_string());
- if (!package_name) {
- return Error("failed to parse android:targetPackage from overlay manifest");
+ auto manifest_info = ExtractOverlayManifestInfo(idmap_header->GetOverlayPath().to_string(),
+ true /* assert_overlay */);
+ if (!manifest_info) {
+ return manifest_info.GetError();
}
- target_package_name = *package_name;
+ target_package_name = (*manifest_info).target_package;
} else if (target_path != idmap_header->GetTargetPath()) {
return Error("different target APKs (expected target APK %s but %s has target APK %s)",
target_path.c_str(), idmap_path.c_str(),
diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp
index 053b7bc9afa3..b4fdd0b8a94d 100644
--- a/cmds/idmap2/idmap2/Scan.cpp
+++ b/cmds/idmap2/idmap2/Scan.cpp
@@ -33,7 +33,7 @@
#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
-#include "idmap2/Xml.h"
+#include "idmap2/XmlParser.h"
#include "idmap2/ZipFile.h"
using android::idmap2::CommandLineOptions;
@@ -178,6 +178,17 @@ Result<Unit> Scan(const std::vector<std::string>& args) {
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;
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index 8ee79f61520a..4aabf8399a25 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -137,8 +137,8 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path,
return error("failed to load apk " + overlay_apk_path);
}
- const auto idmap = Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path,
- *overlay_apk, policy_bitmask, enforce_overlayable);
+ const auto idmap =
+ Idmap::FromApkAssets(*target_apk, *overlay_apk, policy_bitmask, enforce_overlayable);
if (!idmap) {
return error(idmap.GetErrorMessage());
}
diff --git a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
index 1a0d4438f1b3..924efe5cfb7b 100644
--- a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
+++ b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
@@ -34,9 +34,10 @@ class BinaryStreamVisitor : public Visitor {
void visit(const IdmapHeader& header) override;
void visit(const IdmapData& data) override;
void visit(const IdmapData::Header& header) override;
- void visit(const IdmapData::TypeEntry& type_entry) override;
private:
+ void Write(const void* value, size_t length);
+ void Write8(uint8_t value);
void Write16(uint16_t value);
void Write32(uint32_t value);
void WriteString(const StringPiece& value);
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
index ebbb5ffc989d..2639c6f470ae 100644
--- a/cmds/idmap2/include/idmap2/Idmap.h
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -41,6 +41,18 @@
* # idmap file format changelog
* ## v1
* - Identical to idmap v1.
+ * ## v2
+ * - Entries are no longer separated by type into type specific data blocks.
+ * - Added overlay-indexed target resource id lookup capabilities.
+ * - Target and overlay entries are stored as a sparse array in the data block. The target entries
+ * array maps from target resource id to overlay data type and value and the array is sorted by
+ * target resource id. The overlay entries array maps from overlay resource id to target resource
+ * id and the array is sorted by overlay resource id. It is important for both arrays to be sorted
+ * to allow for O(log(number_of_overlaid_resources)) performance when looking up resource
+ * mappings at runtime.
+ * - Idmap can now encode a type and value to override a resource without needing a table entry.
+ * - A string pool block is included to retrieve the value of strings that do not have a resource
+ * table entry.
*/
#ifndef IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
@@ -56,20 +68,14 @@
#include "androidfw/ResourceTypes.h"
#include "androidfw/StringPiece.h"
#include "idmap2/Policies.h"
+#include "idmap2/ResourceMapping.h"
namespace android::idmap2 {
class Idmap;
class Visitor;
-// use typedefs to let the compiler warn us about implicit casts
-typedef uint32_t ResourceId; // 0xpptteeee
-typedef uint8_t PackageId; // pp in 0xpptteeee
-typedef uint8_t TypeId; // tt in 0xpptteeee
-typedef uint16_t EntryId; // eeee in 0xpptteeee
-
static constexpr const ResourceId kPadding = 0xffffffffu;
-
static constexpr const EntryId kNoEntry = 0xffffu;
// magic number: all idmap files start with this
@@ -131,7 +137,6 @@ class IdmapHeader {
friend Idmap;
DISALLOW_COPY_AND_ASSIGN(IdmapHeader);
};
-
class IdmapData {
public:
class Header {
@@ -142,70 +147,72 @@ class IdmapData {
return target_package_id_;
}
- inline uint16_t GetTypeCount() const {
- return type_count_;
- }
-
- void accept(Visitor* v) const;
-
- private:
- Header() {
+ inline PackageId GetOverlayPackageId() const {
+ return overlay_package_id_;
}
- PackageId target_package_id_;
- uint16_t type_count_;
-
- friend Idmap;
- DISALLOW_COPY_AND_ASSIGN(Header);
- };
-
- class TypeEntry {
- public:
- static std::unique_ptr<const TypeEntry> FromBinaryStream(std::istream& stream);
-
- inline TypeId GetTargetTypeId() const {
- return target_type_id_;
+ inline uint32_t GetTargetEntryCount() const {
+ return target_entry_count;
}
- inline TypeId GetOverlayTypeId() const {
- return overlay_type_id_;
+ inline uint32_t GetOverlayEntryCount() const {
+ return overlay_entry_count;
}
- inline uint16_t GetEntryCount() const {
- return entries_.size();
+ inline uint32_t GetStringPoolIndexOffset() const {
+ return string_pool_index_offset;
}
- inline uint16_t GetEntryOffset() const {
- return entry_offset_;
- }
-
- inline EntryId GetEntry(size_t i) const {
- return i < entries_.size() ? entries_[i] : 0xffffu;
+ inline uint32_t GetStringPoolLength() const {
+ return string_pool_len;
}
void accept(Visitor* v) const;
private:
- TypeEntry() {
- }
-
- TypeId target_type_id_;
- TypeId overlay_type_id_;
- uint16_t entry_offset_;
- std::vector<EntryId> entries_;
+ PackageId target_package_id_;
+ PackageId overlay_package_id_;
+ uint32_t target_entry_count;
+ uint32_t overlay_entry_count;
+ uint32_t string_pool_index_offset;
+ uint32_t string_pool_len;
+ Header() = default;
friend Idmap;
- DISALLOW_COPY_AND_ASSIGN(TypeEntry);
+ friend IdmapData;
+ DISALLOW_COPY_AND_ASSIGN(Header);
+ };
+
+ struct TargetEntry {
+ ResourceId target_id;
+ TargetValue::DataType data_type;
+ TargetValue::DataValue data_value;
+ };
+
+ struct OverlayEntry {
+ ResourceId overlay_id;
+ ResourceId target_id;
};
static std::unique_ptr<const IdmapData> FromBinaryStream(std::istream& stream);
+ static Result<std::unique_ptr<const IdmapData>> FromResourceMapping(
+ const ResourceMapping& resource_mapping);
+
inline const std::unique_ptr<const Header>& GetHeader() const {
return header_;
}
- inline const std::vector<std::unique_ptr<const TypeEntry>>& GetTypeEntries() const {
- return type_entries_;
+ inline const std::vector<TargetEntry>& GetTargetEntries() const {
+ return target_entries_;
+ }
+
+ inline const std::vector<OverlayEntry>& GetOverlayEntries() const {
+ return overlay_entries_;
+ }
+
+ inline const void* GetStringPoolData() const {
+ return string_pool_.get();
}
void accept(Visitor* v) const;
@@ -215,7 +222,9 @@ class IdmapData {
}
std::unique_ptr<const Header> header_;
- std::vector<std::unique_ptr<const TypeEntry>> type_entries_;
+ std::vector<TargetEntry> target_entries_;
+ std::vector<OverlayEntry> overlay_entries_;
+ std::unique_ptr<uint8_t[]> string_pool_;
friend Idmap;
DISALLOW_COPY_AND_ASSIGN(IdmapData);
@@ -232,9 +241,7 @@ class Idmap {
// file is used; change this in the next version of idmap to use a named
// package instead; also update FromApkAssets to take additional parameters:
// the target and overlay package names
- static Result<std::unique_ptr<const Idmap>> FromApkAssets(const std::string& target_apk_path,
- const ApkAssets& target_apk_assets,
- const std::string& overlay_apk_path,
+ static Result<std::unique_ptr<const Idmap>> FromApkAssets(const ApkAssets& target_apk_assets,
const ApkAssets& overlay_apk_assets,
const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable);
@@ -267,7 +274,6 @@ class Visitor {
virtual void visit(const IdmapHeader& header) = 0;
virtual void visit(const IdmapData& data) = 0;
virtual void visit(const IdmapData::Header& header) = 0;
- virtual void visit(const IdmapData::TypeEntry& type_entry) = 0;
};
} // namespace android::idmap2
diff --git a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
index f0f141a3757c..5dcf217e2aa3 100644
--- a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
+++ b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
@@ -38,13 +38,11 @@ class PrettyPrintVisitor : public Visitor {
void visit(const IdmapHeader& header) override;
void visit(const IdmapData& data) override;
void visit(const IdmapData::Header& header) override;
- void visit(const IdmapData::TypeEntry& type_entry) override;
private:
std::ostream& stream_;
std::unique_ptr<const ApkAssets> target_apk_;
AssetManager2 target_am_;
- PackageId last_seen_package_id_;
};
} // namespace idmap2
diff --git a/cmds/idmap2/include/idmap2/RawPrintVisitor.h b/cmds/idmap2/include/idmap2/RawPrintVisitor.h
index cd3897109a32..76475ab58731 100644
--- a/cmds/idmap2/include/idmap2/RawPrintVisitor.h
+++ b/cmds/idmap2/include/idmap2/RawPrintVisitor.h
@@ -39,18 +39,20 @@ class RawPrintVisitor : public Visitor {
void visit(const IdmapHeader& header) override;
void visit(const IdmapData& data) override;
void visit(const IdmapData::Header& header) override;
- void visit(const IdmapData::TypeEntry& type_entry) override;
private:
+ void print(uint8_t value, const char* fmt, ...);
void print(uint16_t value, const char* fmt, ...);
void print(uint32_t value, const char* fmt, ...);
void print(const std::string& value, const char* fmt, ...);
+ void print_raw(uint32_t length, const char* fmt, ...);
std::ostream& stream_;
std::unique_ptr<const ApkAssets> target_apk_;
+ std::unique_ptr<const ApkAssets> overlay_apk_;
AssetManager2 target_am_;
+ AssetManager2 overlay_am_;
size_t offset_;
- PackageId last_seen_package_id_;
};
} // namespace idmap2
diff --git a/cmds/idmap2/include/idmap2/ResourceMapping.h b/cmds/idmap2/include/idmap2/ResourceMapping.h
new file mode 100644
index 000000000000..c3e1ef06c20f
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/ResourceMapping.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_
+#define IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_
+
+#include <map>
+#include <memory>
+#include <utility>
+
+#include "androidfw/ApkAssets.h"
+#include "idmap2/Policies.h"
+#include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
+#include "idmap2/XmlParser.h"
+
+using android::idmap2::utils::OverlayManifestInfo;
+
+namespace android::idmap2 {
+
+struct TargetValue {
+ typedef uint8_t DataType;
+ typedef uint32_t DataValue;
+ DataType data_type;
+ DataValue data_value;
+};
+
+using TargetResourceMap = std::map<ResourceId, TargetValue>;
+using OverlayResourceMap = std::map<ResourceId, ResourceId>;
+
+class ResourceMapping {
+ public:
+ // Creates a ResourceMapping using the target and overlay APKs. Setting enforce_overlayable to
+ // `false` disables all overlayable and policy enforcement: this is intended for backwards
+ // compatibility pre-Q and unit tests.
+ static Result<ResourceMapping> FromApkAssets(const ApkAssets& target_apk_assets,
+ const ApkAssets& overlay_apk_assets,
+ const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies,
+ bool enforce_overlayable);
+
+ // Retrieves the mapping of target resource id to overlay value.
+ inline TargetResourceMap GetTargetToOverlayMap() const {
+ return target_map_;
+ }
+
+ // Retrieves the mapping of overlay resource id to target resource id. This allows a reference to
+ // an overlay resource to appear as a reference to its corresponding target resource at runtime.
+ OverlayResourceMap GetOverlayToTargetMap() const;
+
+ // Retrieves the build-time package id of the target package.
+ inline uint32_t GetTargetPackageId() const {
+ return target_package_id_;
+ }
+
+ // Retrieves the build-time package id of the overlay package.
+ inline uint32_t GetOverlayPackageId() const {
+ return overlay_package_id_;
+ }
+
+ // Retrieves the offset that was added to the index of inline string overlay values so the indices
+ // do not collide with the indices of the overlay resource table string pool.
+ inline uint32_t GetStringPoolOffset() const {
+ return string_pool_offset_;
+ }
+
+ // Retrieves the raw string pool data from the xml referenced in android:resourcesMap.
+ inline const std::pair<const uint8_t*, uint32_t> GetStringPoolData() const {
+ return std::make_pair(string_pool_data_.get(), string_pool_data_length_);
+ }
+
+ private:
+ ResourceMapping() = default;
+
+ // Apps a mapping of target resource id to the type and value of the data that overlays the
+ // target resource. The data_type is the runtime format of the data value (see
+ // Res_value::dataType). If rewrite_overlay_reference is `true` then references to an overlay
+ // resource should appear as a reference to its corresponding target resource at runtime.
+ Result<Unit> AddMapping(ResourceId target_resource, TargetValue::DataType data_type,
+ TargetValue::DataValue data_value, bool rewrite_overlay_reference);
+
+ // Removes the overlay value mapping for the target resource.
+ void RemoveMapping(ResourceId target_resource);
+
+ // Parses the mapping of target resources to overlay resources to generate a ResourceMapping.
+ static Result<ResourceMapping> CreateResourceMapping(const AssetManager2* target_am,
+ const LoadedPackage* target_package,
+ const LoadedPackage* overlay_package,
+ size_t string_pool_offset,
+ const XmlParser& overlay_parser);
+
+ // Generates a ResourceMapping that maps target resources to overlay resources by name. To overlay
+ // a target resource, a resource must exist in the overlay with the same type and entry name as
+ // the target resource.
+ static Result<ResourceMapping> CreateResourceMappingLegacy(const AssetManager2* target_am,
+ const AssetManager2* overlay_am,
+ const LoadedPackage* target_package,
+ const LoadedPackage* overlay_package);
+
+ // Removes resources that do not pass policy or overlayable checks of the target package.
+ void FilterOverlayableResources(const AssetManager2* target_am,
+ const LoadedPackage* target_package,
+ const LoadedPackage* overlay_package,
+ const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies);
+
+ TargetResourceMap target_map_;
+ std::multimap<ResourceId, ResourceId> overlay_map_;
+
+ uint32_t target_package_id_ = 0;
+ uint32_t overlay_package_id_ = 0;
+ uint32_t string_pool_offset_ = 0;
+ uint32_t string_pool_data_length_ = 0;
+ std::unique_ptr<uint8_t[]> string_pool_data_ = nullptr;
+};
+
+} // namespace android::idmap2
+
+#endif // IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_
diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h
index 8797a788dd1d..de1dbc90eb2d 100644
--- a/cmds/idmap2/include/idmap2/ResourceUtils.h
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -21,17 +21,32 @@
#include <string>
#include "androidfw/AssetManager2.h"
-#include "idmap2/Idmap.h"
#include "idmap2/Result.h"
#include "idmap2/ZipFile.h"
-namespace android::idmap2::utils {
+namespace android::idmap2 {
+
+// use typedefs to let the compiler warn us about implicit casts
+typedef uint32_t ResourceId; // 0xpptteeee
+typedef uint8_t PackageId; // pp in 0xpptteeee
+typedef uint8_t TypeId; // tt in 0xpptteeee
+typedef uint16_t EntryId; // eeee in 0xpptteeee
+
+#define EXTRACT_TYPE(resid) ((0x00ff0000 & (resid)) >> 16)
+#define EXTRACT_ENTRY(resid) (0x0000ffff & (resid))
+
+namespace utils {
+
+StringPiece DataTypeToString(uint8_t data_type);
struct OverlayManifestInfo {
- std::string target_package; // NOLINT(misc-non-private-member-variables-in-classes)
- std::string target_name; // NOLINT(misc-non-private-member-variables-in-classes)
- bool is_static; // NOLINT(misc-non-private-member-variables-in-classes)
- int priority = -1; // NOLINT(misc-non-private-member-variables-in-classes)
+ std::string target_package; // NOLINT(misc-non-private-member-variables-in-classes)
+ std::string target_name; // NOLINT(misc-non-private-member-variables-in-classes)
+ std::string requiredSystemPropertyName; // NOLINT(misc-non-private-member-variables-in-classes)
+ std::string requiredSystemPropertyValue; // NOLINT(misc-non-private-member-variables-in-classes)
+ uint32_t resource_mapping; // NOLINT(misc-non-private-member-variables-in-classes)
+ bool is_static; // NOLINT(misc-non-private-member-variables-in-classes)
+ int priority = -1; // NOLINT(misc-non-private-member-variables-in-classes)
};
Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path,
@@ -39,6 +54,8 @@ Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path,
Result<std::string> ResToTypeEntryName(const AssetManager2& am, ResourceId resid);
-} // namespace android::idmap2::utils
+} // namespace utils
+
+} // namespace android::idmap2
#endif // IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_
diff --git a/cmds/idmap2/include/idmap2/Xml.h b/cmds/idmap2/include/idmap2/Xml.h
deleted file mode 100644
index dd89dee0f64f..000000000000
--- a/cmds/idmap2/include/idmap2/Xml.h
+++ /dev/null
@@ -1,49 +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.
- */
-
-#ifndef IDMAP2_INCLUDE_IDMAP2_XML_H_
-#define IDMAP2_INCLUDE_IDMAP2_XML_H_
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include "android-base/macros.h"
-#include "androidfw/ResourceTypes.h"
-#include "utils/String16.h"
-
-namespace android::idmap2 {
-
-class Xml {
- public:
- static std::unique_ptr<const Xml> Create(const uint8_t* data, size_t size, bool copyData = false);
-
- std::unique_ptr<std::map<std::string, std::string>> FindTag(const std::string& name) const;
-
- ~Xml();
-
- private:
- Xml() {
- }
-
- mutable ResXMLTree xml_;
-
- DISALLOW_COPY_AND_ASSIGN(Xml);
-};
-
-} // namespace android::idmap2
-
-#endif // IDMAP2_INCLUDE_IDMAP2_XML_H_
diff --git a/cmds/idmap2/include/idmap2/XmlParser.h b/cmds/idmap2/include/idmap2/XmlParser.h
new file mode 100644
index 000000000000..972a6d7e3427
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/XmlParser.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_
+#define IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_
+
+#include <iostream>
+#include <map>
+#include <memory>
+#include <string>
+
+#include "Result.h"
+#include "android-base/macros.h"
+#include "androidfw/ResourceTypes.h"
+#include "utils/String16.h"
+
+namespace android::idmap2 {
+
+class XmlParser {
+ public:
+ using Event = ResXMLParser::event_code_t;
+ class iterator;
+
+ class Node {
+ public:
+ Event event() const;
+ std::string name() const;
+
+ Result<std::string> GetAttributeStringValue(const std::string& name) const;
+ Result<Res_value> GetAttributeValue(const std::string& name) const;
+
+ bool operator==(const Node& rhs) const;
+ bool operator!=(const Node& rhs) const;
+
+ private:
+ explicit Node(const ResXMLTree& tree);
+ Node(const ResXMLTree& tree, const ResXMLParser::ResXMLPosition& pos);
+
+ // Retrieves/Sets the position of the position of the xml parser in the xml tree.
+ ResXMLParser::ResXMLPosition get_position() const;
+ void set_position(const ResXMLParser::ResXMLPosition& pos);
+
+ // If `inner_child` is true, seek advances the parser to the first inner child of the current
+ // node. Otherwise, seek advances the parser to the following node. Returns false if there is
+ // no node to seek to.
+ bool Seek(bool inner_child);
+
+ ResXMLParser parser_;
+ friend iterator;
+ };
+
+ class iterator {
+ public:
+ iterator(const iterator& other) : iterator(other.tree_, other.iter_) {
+ }
+
+ inline iterator& operator=(const iterator& rhs) {
+ iter_.set_position(rhs.iter_.get_position());
+ return *this;
+ }
+
+ inline bool operator==(const iterator& rhs) const {
+ return iter_ == rhs.iter_;
+ }
+
+ inline bool operator!=(const iterator& rhs) const {
+ return !(*this == rhs);
+ }
+
+ inline iterator operator++() {
+ // Seek to the following xml node.
+ iter_.Seek(false /* inner_child */);
+ return *this;
+ }
+
+ iterator begin() const {
+ iterator child_it(*this);
+ // Seek to the first inner child of the current node.
+ child_it.iter_.Seek(true /* inner_child */);
+ return child_it;
+ }
+
+ iterator end() const {
+ iterator child_it = begin();
+ while (child_it.iter_.Seek(false /* inner_child */)) {
+ // Continue iterating until the end tag is found.
+ }
+
+ return child_it;
+ }
+
+ inline const Node operator*() {
+ return Node(tree_, iter_.get_position());
+ }
+
+ inline const Node* operator->() {
+ return &iter_;
+ }
+
+ private:
+ explicit iterator(const ResXMLTree& tree) : tree_(tree), iter_(Node(tree)) {
+ }
+ iterator(const ResXMLTree& tree, const Node& node)
+ : tree_(tree), iter_(Node(tree, node.get_position())) {
+ }
+
+ const ResXMLTree& tree_;
+ Node iter_;
+ friend XmlParser;
+ };
+
+ // Creates a new xml parser beginning at the first tag.
+ static Result<std::unique_ptr<const XmlParser>> Create(const void* data, size_t size,
+ bool copy_data = false);
+ ~XmlParser();
+
+ inline iterator tree_iterator() const {
+ return iterator(tree_);
+ }
+
+ inline const ResStringPool& get_strings() const {
+ return tree_.getStrings();
+ }
+
+ private:
+ XmlParser() = default;
+ mutable ResXMLTree tree_;
+
+ DISALLOW_COPY_AND_ASSIGN(XmlParser);
+};
+
+} // namespace android::idmap2
+
+#endif // IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_
diff --git a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
index dee2d219cbe1..3b0940ae06ef 100644
--- a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
+++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
@@ -24,6 +24,14 @@
namespace android::idmap2 {
+void BinaryStreamVisitor::Write(const void* value, size_t length) {
+ stream_.write(reinterpret_cast<const char*>(value), length);
+}
+
+void BinaryStreamVisitor::Write8(uint8_t value) {
+ stream_.write(reinterpret_cast<char*>(&value), sizeof(uint8_t));
+}
+
void BinaryStreamVisitor::Write16(uint16_t value) {
uint16_t x = htodl(value);
stream_.write(reinterpret_cast<char*>(&x), sizeof(uint16_t));
@@ -54,26 +62,28 @@ void BinaryStreamVisitor::visit(const IdmapHeader& header) {
WriteString(header.GetOverlayPath());
}
-void BinaryStreamVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
- // nothing to do
-}
+void BinaryStreamVisitor::visit(const IdmapData& data) {
+ for (const auto& target_entry : data.GetTargetEntries()) {
+ Write32(target_entry.target_id);
+ Write8(target_entry.data_type);
+ Write32(target_entry.data_value);
+ }
-void BinaryStreamVisitor::visit(const IdmapData::Header& header) {
- Write16(header.GetTargetPackageId());
- Write16(header.GetTypeCount());
-}
+ for (const auto& overlay_entry : data.GetOverlayEntries()) {
+ Write32(overlay_entry.overlay_id);
+ Write32(overlay_entry.target_id);
+ }
-void BinaryStreamVisitor::visit(const IdmapData::TypeEntry& type_entry) {
- const uint16_t entryCount = type_entry.GetEntryCount();
+ Write(data.GetStringPoolData(), data.GetHeader()->GetStringPoolLength());
+}
- Write16(type_entry.GetTargetTypeId());
- Write16(type_entry.GetOverlayTypeId());
- Write16(entryCount);
- Write16(type_entry.GetEntryOffset());
- for (uint16_t i = 0; i < entryCount; i++) {
- EntryId entry_id = type_entry.GetEntry(i);
- Write32(entry_id != kNoEntry ? static_cast<uint32_t>(entry_id) : kPadding);
- }
+void BinaryStreamVisitor::visit(const IdmapData::Header& header) {
+ Write8(header.GetTargetPackageId());
+ Write8(header.GetOverlayPackageId());
+ Write32(header.GetTargetEntryCount());
+ Write32(header.GetOverlayEntryCount());
+ Write32(header.GetStringPoolIndexOffset());
+ Write32(header.GetStringPoolLength());
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 4649675965db..5cb91d713db7 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -30,6 +30,7 @@
#include "android-base/macros.h"
#include "android-base/stringprintf.h"
#include "androidfw/AssetManager2.h"
+#include "idmap2/ResourceMapping.h"
#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
@@ -41,34 +42,10 @@ namespace android::idmap2 {
namespace {
-#define EXTRACT_TYPE(resid) ((0x00ff0000 & (resid)) >> 16)
-
-#define EXTRACT_ENTRY(resid) (0x0000ffff & (resid))
-
-class MatchingResources {
- public:
- void Add(ResourceId target_resid, ResourceId overlay_resid) {
- TypeId target_typeid = EXTRACT_TYPE(target_resid);
- if (map_.find(target_typeid) == map_.end()) {
- map_.emplace(target_typeid, std::set<std::pair<ResourceId, ResourceId>>());
- }
- map_[target_typeid].insert(std::make_pair(target_resid, overlay_resid));
- }
-
- inline const std::map<TypeId, std::set<std::pair<ResourceId, ResourceId>>>& WARN_UNUSED
- Map() const {
- return map_;
- }
-
- private:
- // target type id -> set { pair { overlay entry id, overlay entry id } }
- std::map<TypeId, std::set<std::pair<ResourceId, ResourceId>>> map_;
-};
-
-bool WARN_UNUSED Read16(std::istream& stream, uint16_t* out) {
- uint16_t value;
- if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint16_t))) {
- *out = dtohl(value);
+bool WARN_UNUSED Read8(std::istream& stream, uint8_t* out) {
+ uint8_t value;
+ if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint8_t))) {
+ *out = value;
return true;
}
return false;
@@ -83,6 +60,15 @@ bool WARN_UNUSED Read32(std::istream& stream, uint32_t* out) {
return false;
}
+bool WARN_UNUSED ReadBuffer(std::istream& stream, std::unique_ptr<uint8_t[]>* out, size_t length) {
+ auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[length]);
+ if (stream.read(reinterpret_cast<char*>(buffer.get()), length)) {
+ *out = std::move(buffer);
+ return true;
+ }
+ return false;
+}
+
// a string is encoded as a kIdmapStringLength char array; the array is always null-terminated
bool WARN_UNUSED ReadString(std::istream& stream, char out[kIdmapStringLength]) {
char buf[kIdmapStringLength];
@@ -97,28 +83,6 @@ bool WARN_UNUSED ReadString(std::istream& stream, char out[kIdmapStringLength])
return true;
}
-ResourceId NameToResid(const AssetManager2& am, const std::string& name) {
- return am.GetResourceId(name);
-}
-
-// TODO(martenkongstad): scan for package name instead of assuming package at index 0
-//
-// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package
-// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so
-// this assumption tends to work out. That said, the correct thing to do is to scan
-// resources.arsc for a package with a given name as read from the package manifest instead of
-// relying on a hard-coded index. This however requires storing the package name in the idmap
-// header, which in turn requires incrementing the idmap version. Because the initial version of
-// idmap2 is compatible with idmap, this will have to wait for now.
-const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) {
- const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc.GetPackages();
- if (packages.empty()) {
- return nullptr;
- }
- int id = packages[0]->GetPackageId();
- return loaded_arsc.GetPackageById(id);
-}
-
Result<uint32_t> GetCrc(const ZipFile& zip) {
const Result<uint32_t> a = zip.Crc("resources.arsc");
const Result<uint32_t> b = zip.Crc("AndroidManifest.xml");
@@ -187,51 +151,48 @@ Result<Unit> IdmapHeader::IsUpToDate() const {
std::unique_ptr<const IdmapData::Header> IdmapData::Header::FromBinaryStream(std::istream& stream) {
std::unique_ptr<IdmapData::Header> idmap_data_header(new IdmapData::Header());
- uint16_t target_package_id16;
- if (!Read16(stream, &target_package_id16) || !Read16(stream, &idmap_data_header->type_count_)) {
+ if (!Read8(stream, &idmap_data_header->target_package_id_) ||
+ !Read8(stream, &idmap_data_header->overlay_package_id_) ||
+ !Read32(stream, &idmap_data_header->target_entry_count) ||
+ !Read32(stream, &idmap_data_header->overlay_entry_count) ||
+ !Read32(stream, &idmap_data_header->string_pool_index_offset) ||
+ !Read32(stream, &idmap_data_header->string_pool_len)) {
return nullptr;
}
- idmap_data_header->target_package_id_ = target_package_id16;
return std::move(idmap_data_header);
}
-std::unique_ptr<const IdmapData::TypeEntry> IdmapData::TypeEntry::FromBinaryStream(
- std::istream& stream) {
- std::unique_ptr<IdmapData::TypeEntry> data(new IdmapData::TypeEntry());
- uint16_t target_type16;
- uint16_t overlay_type16;
- uint16_t entry_count;
- if (!Read16(stream, &target_type16) || !Read16(stream, &overlay_type16) ||
- !Read16(stream, &entry_count) || !Read16(stream, &data->entry_offset_)) {
- return nullptr;
- }
- data->target_type_id_ = target_type16;
- data->overlay_type_id_ = overlay_type16;
- for (uint16_t i = 0; i < entry_count; i++) {
- ResourceId resid;
- if (!Read32(stream, &resid)) {
- return nullptr;
- }
- data->entries_.push_back(resid);
- }
-
- return std::move(data);
-}
-
std::unique_ptr<const IdmapData> IdmapData::FromBinaryStream(std::istream& stream) {
std::unique_ptr<IdmapData> data(new IdmapData());
data->header_ = IdmapData::Header::FromBinaryStream(stream);
if (!data->header_) {
return nullptr;
}
- for (size_t type_count = 0; type_count < data->header_->GetTypeCount(); type_count++) {
- std::unique_ptr<const TypeEntry> type = IdmapData::TypeEntry::FromBinaryStream(stream);
- if (!type) {
+ // Read the mapping of target resource id to overlay resource value.
+ for (size_t i = 0; i < data->header_->GetTargetEntryCount(); i++) {
+ TargetEntry target_entry{};
+ if (!Read32(stream, &target_entry.target_id) || !Read8(stream, &target_entry.data_type) ||
+ !Read32(stream, &target_entry.data_value)) {
+ return nullptr;
+ }
+ data->target_entries_.emplace_back(target_entry);
+ }
+
+ // Read the mapping of overlay resource id to target resource id.
+ for (size_t i = 0; i < data->header_->GetOverlayEntryCount(); i++) {
+ OverlayEntry overlay_entry{};
+ if (!Read32(stream, &overlay_entry.overlay_id) || !Read32(stream, &overlay_entry.target_id)) {
return nullptr;
}
- data->type_entries_.push_back(std::move(type));
+ data->overlay_entries_.emplace_back(overlay_entry);
}
+
+ // Read raw string pool bytes.
+ if (!ReadBuffer(stream, &data->string_pool_, data->header_->string_pool_len)) {
+ return nullptr;
+ }
+
return std::move(data);
}
@@ -266,95 +227,45 @@ Result<std::unique_ptr<const Idmap>> Idmap::FromBinaryStream(std::istream& strea
return {std::move(idmap)};
}
-std::string ConcatPolicies(const std::vector<std::string>& policies) {
- std::string message;
- for (const std::string& policy : policies) {
- if (!message.empty()) {
- message.append("|");
- }
- message.append(policy);
+Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping(
+ const ResourceMapping& resource_mapping) {
+ if (resource_mapping.GetTargetToOverlayMap().empty()) {
+ return Error("no resources were overlaid");
}
- return message;
-}
-
-Result<Unit> CheckOverlayable(const LoadedPackage& target_package,
- const utils::OverlayManifestInfo& overlay_info,
- const PolicyBitmask& fulfilled_policies, const ResourceId& resid) {
- static constexpr const PolicyBitmask sDefaultPolicies =
- PolicyFlags::POLICY_ODM_PARTITION | PolicyFlags::POLICY_OEM_PARTITION |
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION |
- PolicyFlags::POLICY_PRODUCT_PARTITION | PolicyFlags::POLICY_SIGNATURE;
-
- // If the resource does not have an overlayable definition, allow the resource to be overlaid if
- // the overlay is preinstalled or signed with the same signature as the target.
- if (!target_package.DefinesOverlayable()) {
- return (sDefaultPolicies & fulfilled_policies) != 0
- ? Result<Unit>({})
- : Error(
- "overlay must be preinstalled or signed with the same signature as the "
- "target");
+ std::unique_ptr<IdmapData> data(new IdmapData());
+ for (const auto& mappings : resource_mapping.GetTargetToOverlayMap()) {
+ data->target_entries_.emplace_back(IdmapData::TargetEntry{
+ mappings.first, mappings.second.data_type, mappings.second.data_value});
}
- const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(resid);
- if (overlayable_info == nullptr) {
- // Do not allow non-overlayable resources to be overlaid.
- return Error("resource has no overlayable declaration");
+ for (const auto& mappings : resource_mapping.GetOverlayToTargetMap()) {
+ data->overlay_entries_.emplace_back(IdmapData::OverlayEntry{mappings.first, mappings.second});
}
- if (overlay_info.target_name != overlayable_info->name) {
- // If the overlay supplies a target overlayable name, the resource must belong to the
- // overlayable defined with the specified name to be overlaid.
- return Error("<overlay> android:targetName '%s' does not match overlayable name '%s'",
- overlay_info.target_name.c_str(), overlayable_info->name.c_str());
- }
+ std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header());
+ data_header->target_package_id_ = resource_mapping.GetTargetPackageId();
+ data_header->overlay_package_id_ = resource_mapping.GetOverlayPackageId();
+ data_header->target_entry_count = static_cast<uint32_t>(data->target_entries_.size());
+ data_header->overlay_entry_count = static_cast<uint32_t>(data->overlay_entries_.size());
+ data_header->string_pool_index_offset = resource_mapping.GetStringPoolOffset();
- // Enforce policy restrictions if the resource is declared as overlayable.
- if ((overlayable_info->policy_flags & fulfilled_policies) == 0) {
- return Error("overlay with policies '%s' does not fulfill any overlayable policies '%s'",
- ConcatPolicies(BitmaskToPolicies(fulfilled_policies)).c_str(),
- ConcatPolicies(BitmaskToPolicies(overlayable_info->policy_flags)).c_str());
- }
+ const auto string_pool_data = resource_mapping.GetStringPoolData();
+ data_header->string_pool_len = string_pool_data.second;
+ data->string_pool_ = std::unique_ptr<uint8_t[]>(new uint8_t[data_header->string_pool_len]);
+ memcpy(data->string_pool_.get(), string_pool_data.first, data_header->string_pool_len);
- return Result<Unit>({});
+ data->header_ = std::move(data_header);
+ return {std::move(data)};
}
-Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const std::string& target_apk_path,
- const ApkAssets& target_apk_assets,
- const std::string& overlay_apk_path,
+Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const ApkAssets& target_apk_assets,
const ApkAssets& overlay_apk_assets,
const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable) {
SYSTRACE << "Idmap::FromApkAssets";
- AssetManager2 target_asset_manager;
- if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true, false)) {
- return Error("failed to create target asset manager");
- }
-
- AssetManager2 overlay_asset_manager;
- if (!overlay_asset_manager.SetApkAssets({&overlay_apk_assets}, true, false)) {
- return Error("failed to create overlay asset manager");
- }
-
- const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc();
- if (target_arsc == nullptr) {
- return Error("failed to load target resources.arsc");
- }
-
- const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc();
- if (overlay_arsc == nullptr) {
- return Error("failed to load overlay resources.arsc");
- }
-
- const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc);
- if (target_pkg == nullptr) {
- return Error("failed to load target package from resources.arsc");
- }
-
- const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc);
- if (overlay_pkg == nullptr) {
- return Error("failed to load overlay package from resources.arsc");
- }
+ const std::string& target_apk_path = target_apk_assets.GetPath();
+ const std::string& overlay_apk_path = overlay_apk_assets.GetPath();
const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_apk_path);
if (!target_zip) {
@@ -366,11 +277,6 @@ Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const std::string& tar
return Error("failed to open overlay as zip");
}
- auto overlay_info = utils::ExtractOverlayManifestInfo(overlay_apk_path);
- if (!overlay_info) {
- return overlay_info.GetError();
- }
-
std::unique_ptr<IdmapHeader> header(new IdmapHeader());
header->magic_ = kIdmapMagic;
header->version_ = kIdmapCurrentVersion;
@@ -395,7 +301,7 @@ Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const std::string& tar
memcpy(header->target_path_, target_apk_path.data(), target_apk_path.size());
if (overlay_apk_path.size() > sizeof(header->overlay_path_)) {
- return Error("overlay apk path \"%s\" longer than maximum size %zu", target_apk_path.c_str(),
+ return Error("overlay apk path \"%s\" longer than maximum size %zu", overlay_apk_path.c_str(),
sizeof(header->target_path_));
}
memset(header->overlay_path_, 0, sizeof(header->overlay_path_));
@@ -404,70 +310,24 @@ Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const std::string& tar
std::unique_ptr<Idmap> idmap(new Idmap());
idmap->header_ = std::move(header);
- // find the resources that exist in both packages
- MatchingResources matching_resources;
- const auto end = overlay_pkg->end();
- for (auto iter = overlay_pkg->begin(); iter != end; ++iter) {
- const ResourceId overlay_resid = *iter;
- Result<std::string> name = utils::ResToTypeEntryName(overlay_asset_manager, overlay_resid);
- if (!name) {
- continue;
- }
- // prepend "<package>:" to turn name into "<package>:<type>/<name>"
- const std::string full_name =
- base::StringPrintf("%s:%s", target_pkg->GetPackageName().c_str(), name->c_str());
- const ResourceId target_resid = NameToResid(target_asset_manager, full_name);
- if (target_resid == 0) {
- continue;
- }
-
- if (enforce_overlayable) {
- Result<Unit> success =
- CheckOverlayable(*target_pkg, *overlay_info, fulfilled_policies, target_resid);
- if (!success) {
- LOG(WARNING) << "overlay \"" << overlay_apk_path
- << "\" is not allowed to overlay resource \"" << full_name
- << "\": " << success.GetErrorMessage();
- continue;
- }
- }
-
- matching_resources.Add(target_resid, overlay_resid);
+ auto overlay_info = utils::ExtractOverlayManifestInfo(overlay_apk_path);
+ if (!overlay_info) {
+ return overlay_info.GetError();
}
- if (matching_resources.Map().empty()) {
- return Error("overlay \"%s\" does not successfully overlay any resource",
- overlay_apk_path.c_str());
+ auto resource_mapping =
+ ResourceMapping::FromApkAssets(target_apk_assets, overlay_apk_assets, *overlay_info,
+ fulfilled_policies, enforce_overlayable);
+ if (!resource_mapping) {
+ return resource_mapping.GetError();
}
- // encode idmap data
- std::unique_ptr<IdmapData> data(new IdmapData());
- const auto types_end = matching_resources.Map().cend();
- for (auto ti = matching_resources.Map().cbegin(); ti != types_end; ++ti) {
- auto ei = ti->second.cbegin();
- std::unique_ptr<IdmapData::TypeEntry> type(new IdmapData::TypeEntry());
- type->target_type_id_ = EXTRACT_TYPE(ei->first);
- type->overlay_type_id_ = EXTRACT_TYPE(ei->second);
- type->entry_offset_ = EXTRACT_ENTRY(ei->first);
- EntryId last_target_entry = kNoEntry;
- for (; ei != ti->second.cend(); ++ei) {
- if (last_target_entry != kNoEntry) {
- int count = EXTRACT_ENTRY(ei->first) - last_target_entry - 1;
- type->entries_.insert(type->entries_.end(), count, kNoEntry);
- }
- type->entries_.push_back(EXTRACT_ENTRY(ei->second));
- last_target_entry = EXTRACT_ENTRY(ei->first);
- }
- data->type_entries_.push_back(std::move(type));
+ auto idmap_data = IdmapData::FromResourceMapping(*resource_mapping);
+ if (!idmap_data) {
+ return idmap_data.GetError();
}
- std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header());
- data_header->target_package_id_ = target_pkg->GetPackageId();
- data_header->type_count_ = data->type_entries_.size();
- data->header_ = std::move(data_header);
-
- idmap->data_.push_back(std::move(data));
-
+ idmap->data_.push_back(std::move(*idmap_data));
return {std::move(idmap)};
}
@@ -481,25 +341,16 @@ void IdmapData::Header::accept(Visitor* v) const {
v->visit(*this);
}
-void IdmapData::TypeEntry::accept(Visitor* v) const {
- assert(v != nullptr);
- v->visit(*this);
-}
-
void IdmapData::accept(Visitor* v) const {
assert(v != nullptr);
- v->visit(*this);
header_->accept(v);
- auto end = type_entries_.cend();
- for (auto iter = type_entries_.cbegin(); iter != end; ++iter) {
- (*iter)->accept(v);
- }
+ v->visit(*this);
}
void Idmap::accept(Visitor* v) const {
assert(v != nullptr);
- v->visit(*this);
header_->accept(v);
+ v->visit(*this);
auto end = data_.cend();
for (auto iter = data_.cbegin(); iter != end; ++iter) {
(*iter)->accept(v);
diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
index fbf2c777be9a..a662aa59b615 100644
--- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
@@ -41,29 +41,33 @@ void PrettyPrintVisitor::visit(const IdmapHeader& header) {
}
}
-void PrettyPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
-}
-
void PrettyPrintVisitor::visit(const IdmapData::Header& header ATTRIBUTE_UNUSED) {
- last_seen_package_id_ = header.GetTargetPackageId();
}
-void PrettyPrintVisitor::visit(const IdmapData::TypeEntry& type_entry) {
+void PrettyPrintVisitor::visit(const IdmapData& data) {
const bool target_package_loaded = !target_am_.GetApkAssets().empty();
- for (uint16_t i = 0; i < type_entry.GetEntryCount(); i++) {
- const EntryId entry = type_entry.GetEntry(i);
- if (entry == kNoEntry) {
- continue;
+ const ResStringPool string_pool(data.GetStringPoolData(),
+ data.GetHeader()->GetStringPoolLength());
+ const size_t string_pool_offset = data.GetHeader()->GetStringPoolIndexOffset();
+
+ for (auto& target_entry : data.GetTargetEntries()) {
+ stream_ << base::StringPrintf("0x%08x ->", target_entry.target_id);
+
+ if (target_entry.data_type != Res_value::TYPE_REFERENCE &&
+ target_entry.data_type != Res_value::TYPE_DYNAMIC_REFERENCE) {
+ stream_ << " " << utils::DataTypeToString(target_entry.data_type);
}
- const ResourceId target_resid =
- RESID(last_seen_package_id_, type_entry.GetTargetTypeId(), type_entry.GetEntryOffset() + i);
- const ResourceId overlay_resid =
- RESID(last_seen_package_id_, type_entry.GetOverlayTypeId(), entry);
+ if (target_entry.data_type == Res_value::TYPE_STRING) {
+ stream_ << " \""
+ << string_pool.string8ObjectAt(target_entry.data_value - string_pool_offset).c_str()
+ << "\"";
+ } else {
+ stream_ << " " << base::StringPrintf("0x%08x", target_entry.data_value);
+ }
- stream_ << base::StringPrintf("0x%08x -> 0x%08x", target_resid, overlay_resid);
if (target_package_loaded) {
- Result<std::string> name = utils::ResToTypeEntryName(target_am_, target_resid);
+ Result<std::string> name = utils::ResToTypeEntryName(target_am_, target_entry.target_id);
if (name) {
stream_ << " " << *name;
}
diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
index dd14fd47aea8..13973d64fe68 100644
--- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -47,46 +47,94 @@ void RawPrintVisitor::visit(const IdmapHeader& header) {
if (target_apk_) {
target_am_.SetApkAssets({target_apk_.get()});
}
+
+ overlay_apk_ = ApkAssets::Load(header.GetOverlayPath().to_string());
+ if (overlay_apk_) {
+ overlay_am_.SetApkAssets({overlay_apk_.get()});
+ }
}
void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
-}
+ const bool target_package_loaded = !target_am_.GetApkAssets().empty();
+ const bool overlay_package_loaded = !overlay_am_.GetApkAssets().empty();
-void RawPrintVisitor::visit(const IdmapData::Header& header) {
- print(static_cast<uint16_t>(header.GetTargetPackageId()), "target package id");
- print(header.GetTypeCount(), "type count");
- last_seen_package_id_ = header.GetTargetPackageId();
-}
+ for (auto& target_entry : data.GetTargetEntries()) {
+ Result<std::string> target_name(Error(""));
+ if (target_package_loaded) {
+ target_name = utils::ResToTypeEntryName(target_am_, target_entry.target_id);
+ }
+ if (target_name) {
+ print(target_entry.target_id, "target id: %s", target_name->c_str());
+ } else {
+ print(target_entry.target_id, "target id");
+ }
-void RawPrintVisitor::visit(const IdmapData::TypeEntry& type_entry) {
- const bool target_package_loaded = !target_am_.GetApkAssets().empty();
+ print(target_entry.data_type, "type: %s",
+ utils::DataTypeToString(target_entry.data_type).data());
+
+ Result<std::string> overlay_name(Error(""));
+ if (overlay_package_loaded && (target_entry.data_type == Res_value::TYPE_REFERENCE ||
+ target_entry.data_type == Res_value::TYPE_DYNAMIC_REFERENCE)) {
+ overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.data_value);
+ }
+ if (overlay_name) {
+ print(target_entry.data_value, "value: %s", overlay_name->c_str());
+ } else {
+ print(target_entry.data_value, "value");
+ }
+ }
+
+ for (auto& overlay_entry : data.GetOverlayEntries()) {
+ Result<std::string> overlay_name(Error(""));
+ if (overlay_package_loaded) {
+ overlay_name = utils::ResToTypeEntryName(overlay_am_, overlay_entry.overlay_id);
+ }
+
+ if (overlay_name) {
+ print(overlay_entry.overlay_id, "overlay id: %s", overlay_name->c_str());
+ } else {
+ print(overlay_entry.overlay_id, "overlay id");
+ }
- print(static_cast<uint16_t>(type_entry.GetTargetTypeId()), "target type");
- print(static_cast<uint16_t>(type_entry.GetOverlayTypeId()), "overlay type");
- print(static_cast<uint16_t>(type_entry.GetEntryCount()), "entry count");
- print(static_cast<uint16_t>(type_entry.GetEntryOffset()), "entry offset");
+ Result<std::string> target_name(Error(""));
+ if (target_package_loaded) {
+ target_name = utils::ResToTypeEntryName(target_am_, overlay_entry.target_id);
+ }
- for (uint16_t i = 0; i < type_entry.GetEntryCount(); i++) {
- const EntryId entry = type_entry.GetEntry(i);
- if (entry == kNoEntry) {
- print(kPadding, "no entry");
+ if (target_name) {
+ print(overlay_entry.target_id, "target id: %s", target_name->c_str());
} else {
- const ResourceId target_resid = RESID(last_seen_package_id_, type_entry.GetTargetTypeId(),
- type_entry.GetEntryOffset() + i);
- const ResourceId overlay_resid =
- RESID(last_seen_package_id_, type_entry.GetOverlayTypeId(), entry);
- Result<std::string> name(Error(""));
- if (target_package_loaded) {
- name = utils::ResToTypeEntryName(target_am_, target_resid);
- }
- if (name) {
- print(static_cast<uint32_t>(entry), "0x%08x -> 0x%08x %s", target_resid, overlay_resid,
- name->c_str());
- } else {
- print(static_cast<uint32_t>(entry), "0x%08x -> 0x%08x", target_resid, overlay_resid);
- }
+ print(overlay_entry.target_id, "target id");
}
}
+
+ const size_t string_pool_length = data.GetHeader()->GetStringPoolLength();
+ if (string_pool_length > 0) {
+ print_raw(string_pool_length, "%zu raw string pool bytes", string_pool_length);
+ }
+}
+
+void RawPrintVisitor::visit(const IdmapData::Header& header) {
+ print(header.GetTargetPackageId(), "target package id");
+ print(header.GetOverlayPackageId(), "overlay package id");
+ print(header.GetTargetEntryCount(), "target entry count");
+ print(header.GetOverlayEntryCount(), "overlay entry count");
+ print(header.GetStringPoolIndexOffset(), "string pool index offset");
+ print(header.GetStringPoolLength(), "string pool byte length");
+}
+
+// NOLINTNEXTLINE(cert-dcl50-cpp)
+void RawPrintVisitor::print(uint8_t value, const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ std::string comment;
+ base::StringAppendV(&comment, fmt, ap);
+ va_end(ap);
+
+ stream_ << base::StringPrintf("%08zx: %02x", offset_, value) << " " << comment
+ << std::endl;
+
+ offset_ += sizeof(uint8_t);
}
// NOLINTNEXTLINE(cert-dcl50-cpp)
@@ -123,10 +171,23 @@ void RawPrintVisitor::print(const std::string& value, const char* fmt, ...) {
base::StringAppendV(&comment, fmt, ap);
va_end(ap);
- stream_ << base::StringPrintf("%08zx: ", offset_) << "........ " << comment << ": " << value
+ stream_ << base::StringPrintf("%08zx: ", offset_) << "........ " << comment << ": " << value
<< std::endl;
offset_ += kIdmapStringLength;
}
+// NOLINTNEXTLINE(cert-dcl50-cpp)
+void RawPrintVisitor::print_raw(uint32_t length, const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ std::string comment;
+ base::StringAppendV(&comment, fmt, ap);
+ va_end(ap);
+
+ stream_ << base::StringPrintf("%08zx: ", offset_) << "........ " << comment << std::endl;
+
+ offset_ += length;
+}
+
} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp
new file mode 100644
index 000000000000..651d20fb7c68
--- /dev/null
+++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "idmap2/ResourceMapping.h"
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "android-base/stringprintf.h"
+#include "idmap2/ResourceUtils.h"
+
+using android::base::StringPrintf;
+using android::idmap2::utils::ResToTypeEntryName;
+
+namespace android::idmap2 {
+
+namespace {
+
+#define EXTRACT_PACKAGE(resid) ((0xff000000 & (resid)) >> 24)
+
+std::string ConcatPolicies(const std::vector<std::string>& policies) {
+ std::string message;
+ for (const std::string& policy : policies) {
+ if (!message.empty()) {
+ message.append("|");
+ }
+ message.append(policy);
+ }
+
+ return message;
+}
+
+Result<Unit> CheckOverlayable(const LoadedPackage& target_package,
+ const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies,
+ const ResourceId& target_resource) {
+ static constexpr const PolicyBitmask sDefaultPolicies =
+ PolicyFlags::POLICY_ODM_PARTITION | PolicyFlags::POLICY_OEM_PARTITION |
+ PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION |
+ PolicyFlags::POLICY_PRODUCT_PARTITION | PolicyFlags::POLICY_SIGNATURE;
+
+ // If the resource does not have an overlayable definition, allow the resource to be overlaid if
+ // the overlay is preinstalled or signed with the same signature as the target.
+ if (!target_package.DefinesOverlayable()) {
+ return (sDefaultPolicies & fulfilled_policies) != 0
+ ? Result<Unit>({})
+ : Error(
+ "overlay must be preinstalled or signed with the same signature as the "
+ "target");
+ }
+
+ const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(target_resource);
+ if (overlayable_info == nullptr) {
+ // Do not allow non-overlayable resources to be overlaid.
+ return Error("target resource has no overlayable declaration");
+ }
+
+ if (overlay_info.target_name != overlayable_info->name) {
+ // If the overlay supplies a target overlayable name, the resource must belong to the
+ // overlayable defined with the specified name to be overlaid.
+ return Error(R"(<overlay> android:targetName "%s" does not match overlayable name "%s")",
+ overlay_info.target_name.c_str(), overlayable_info->name.c_str());
+ }
+
+ // Enforce policy restrictions if the resource is declared as overlayable.
+ if ((overlayable_info->policy_flags & fulfilled_policies) == 0) {
+ return Error(R"(overlay with policies "%s" does not fulfill any overlayable policies "%s")",
+ ConcatPolicies(BitmaskToPolicies(fulfilled_policies)).c_str(),
+ ConcatPolicies(BitmaskToPolicies(overlayable_info->policy_flags)).c_str());
+ }
+
+ return Result<Unit>({});
+}
+
+// TODO(martenkongstad): scan for package name instead of assuming package at index 0
+//
+// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package
+// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so
+// this assumption tends to work out. That said, the correct thing to do is to scan
+// resources.arsc for a package with a given name as read from the package manifest instead of
+// relying on a hard-coded index. This however requires storing the package name in the idmap
+// header, which in turn requires incrementing the idmap version. Because the initial version of
+// idmap2 is compatible with idmap, this will have to wait for now.
+const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) {
+ const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc.GetPackages();
+ if (packages.empty()) {
+ return nullptr;
+ }
+ int id = packages[0]->GetPackageId();
+ return loaded_arsc.GetPackageById(id);
+}
+
+Result<std::unique_ptr<Asset>> OpenNonAssetFromResource(const ResourceId& resource_id,
+ const AssetManager2& asset_manager) {
+ Res_value value{};
+ ResTable_config selected_config{};
+ uint32_t flags;
+ auto cookie =
+ asset_manager.GetResource(resource_id, /* may_be_bag */ false,
+ /* density_override */ 0U, &value, &selected_config, &flags);
+ if (cookie == kInvalidCookie) {
+ return Error("failed to find resource for id 0x%08x", resource_id);
+ }
+
+ if (value.dataType != Res_value::TYPE_STRING) {
+ return Error("resource for is 0x%08x is not a file", resource_id);
+ }
+
+ auto string_pool = asset_manager.GetStringPoolForCookie(cookie);
+ size_t len;
+ auto file_path16 = string_pool->stringAt(value.data, &len);
+ if (file_path16 == nullptr) {
+ return Error("failed to find string for index %d", value.data);
+ }
+
+ // Load the overlay resource mappings from the file specified using android:resourcesMap.
+ auto file_path = String8(String16(file_path16));
+ auto asset = asset_manager.OpenNonAsset(file_path.c_str(), Asset::AccessMode::ACCESS_BUFFER);
+ if (asset == nullptr) {
+ return Error("file \"%s\" not found", file_path.c_str());
+ }
+
+ return asset;
+}
+
+} // namespace
+
+Result<ResourceMapping> ResourceMapping::CreateResourceMapping(const AssetManager2* target_am,
+ const LoadedPackage* target_package,
+ const LoadedPackage* overlay_package,
+ size_t string_pool_offset,
+ const XmlParser& overlay_parser) {
+ ResourceMapping resource_mapping;
+ auto root_it = overlay_parser.tree_iterator();
+ if (root_it->event() != XmlParser::Event::START_TAG || root_it->name() != "overlay") {
+ return Error("root element is not <overlay> tag");
+ }
+
+ const uint8_t overlay_package_id = overlay_package->GetPackageId();
+ auto overlay_it_end = root_it.end();
+ for (auto overlay_it = root_it.begin(); overlay_it != overlay_it_end; ++overlay_it) {
+ if (overlay_it->event() == XmlParser::Event::BAD_DOCUMENT) {
+ return Error("failed to parse overlay xml document");
+ }
+
+ if (overlay_it->event() != XmlParser::Event::START_TAG) {
+ continue;
+ }
+
+ if (overlay_it->name() != "item") {
+ return Error("unexpected tag <%s> in <overlay>", overlay_it->name().c_str());
+ }
+
+ Result<std::string> target_resource = overlay_it->GetAttributeStringValue("target");
+ if (!target_resource) {
+ return Error(R"(<item> tag missing expected attribute "target")");
+ }
+
+ Result<android::Res_value> overlay_resource = overlay_it->GetAttributeValue("value");
+ if (!overlay_resource) {
+ return Error(R"(<item> tag missing expected attribute "value")");
+ }
+
+ ResourceId target_id =
+ target_am->GetResourceId(*target_resource, "", target_package->GetPackageName());
+ if (target_id == 0U) {
+ LOG(WARNING) << "failed to find resource \"" << *target_resource << "\" in target resources";
+ continue;
+ }
+
+ if (overlay_resource->dataType == Res_value::TYPE_STRING) {
+ overlay_resource->data += string_pool_offset;
+ }
+
+ // Only rewrite resources defined within the overlay package to their corresponding target
+ // resource ids at runtime.
+ bool rewrite_overlay_reference =
+ (overlay_resource->dataType == Res_value::TYPE_REFERENCE ||
+ overlay_resource->dataType == Res_value::TYPE_DYNAMIC_REFERENCE)
+ ? overlay_package_id == EXTRACT_PACKAGE(overlay_resource->data)
+ : false;
+
+ if (rewrite_overlay_reference) {
+ overlay_resource->dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
+ }
+
+ resource_mapping.AddMapping(target_id, overlay_resource->dataType, overlay_resource->data,
+ rewrite_overlay_reference);
+ }
+
+ return resource_mapping;
+}
+
+Result<ResourceMapping> ResourceMapping::CreateResourceMappingLegacy(
+ const AssetManager2* target_am, const AssetManager2* overlay_am,
+ const LoadedPackage* target_package, const LoadedPackage* overlay_package) {
+ ResourceMapping resource_mapping;
+ const auto end = overlay_package->end();
+ for (auto iter = overlay_package->begin(); iter != end; ++iter) {
+ const ResourceId overlay_resid = *iter;
+ Result<std::string> name = utils::ResToTypeEntryName(*overlay_am, overlay_resid);
+ if (!name) {
+ continue;
+ }
+
+ // Find the resource with the same type and entry name within the target package.
+ const std::string full_name =
+ base::StringPrintf("%s:%s", target_package->GetPackageName().c_str(), name->c_str());
+ const ResourceId target_resource = target_am->GetResourceId(full_name);
+ if (target_resource == 0U) {
+ continue;
+ }
+
+ resource_mapping.AddMapping(target_resource, Res_value::TYPE_REFERENCE, overlay_resid,
+ /* rewrite_overlay_reference */ false);
+ }
+
+ return resource_mapping;
+}
+
+void ResourceMapping::FilterOverlayableResources(const AssetManager2* target_am,
+ const LoadedPackage* target_package,
+ const LoadedPackage* overlay_package,
+ const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies) {
+ std::set<ResourceId> remove_ids;
+ for (const auto& target_map : target_map_) {
+ const ResourceId target_resid = target_map.first;
+ Result<Unit> success =
+ CheckOverlayable(*target_package, overlay_info, fulfilled_policies, target_resid);
+ if (success) {
+ continue;
+ }
+
+ // Attempting to overlay a resource that is not allowed to be overlaid is treated as a
+ // warning.
+ Result<std::string> name = utils::ResToTypeEntryName(*target_am, target_resid);
+ if (!name) {
+ name = StringPrintf("0x%08x", target_resid);
+ }
+
+ LOG(WARNING) << "overlay \"" << overlay_package->GetPackageName()
+ << "\" is not allowed to overlay resource \"" << *name
+ << "\" in target: " << success.GetErrorMessage();
+
+ remove_ids.insert(target_resid);
+ }
+
+ for (const ResourceId target_resid : remove_ids) {
+ RemoveMapping(target_resid);
+ }
+}
+
+Result<ResourceMapping> ResourceMapping::FromApkAssets(const ApkAssets& target_apk_assets,
+ const ApkAssets& overlay_apk_assets,
+ const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies,
+ bool enforce_overlayable) {
+ AssetManager2 target_asset_manager;
+ if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true /* invalidate_caches */,
+ false /* filter_incompatible_configs*/)) {
+ return Error("failed to create target asset manager");
+ }
+
+ AssetManager2 overlay_asset_manager;
+ if (!overlay_asset_manager.SetApkAssets({&overlay_apk_assets}, true /* invalidate_caches */,
+ false /* filter_incompatible_configs */)) {
+ return Error("failed to create overlay asset manager");
+ }
+
+ const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc();
+ if (target_arsc == nullptr) {
+ return Error("failed to load target resources.arsc");
+ }
+
+ const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc();
+ if (overlay_arsc == nullptr) {
+ return Error("failed to load overlay resources.arsc");
+ }
+
+ const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc);
+ if (target_pkg == nullptr) {
+ return Error("failed to load target package from resources.arsc");
+ }
+
+ const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc);
+ if (overlay_pkg == nullptr) {
+ return Error("failed to load overlay package from resources.arsc");
+ }
+
+ size_t string_pool_data_length = 0U;
+ size_t string_pool_offset = 0U;
+ std::unique_ptr<uint8_t[]> string_pool_data;
+ Result<ResourceMapping> resource_mapping = {{}};
+ if (overlay_info.resource_mapping != 0U) {
+ // Load the overlay resource mappings from the file specified using android:resourcesMap.
+ auto asset = OpenNonAssetFromResource(overlay_info.resource_mapping, overlay_asset_manager);
+ if (!asset) {
+ return Error("failed opening xml for android:resourcesMap: %s",
+ asset.GetErrorMessage().c_str());
+ }
+
+ auto parser =
+ XmlParser::Create((*asset)->getBuffer(true /* wordAligned*/), (*asset)->getLength());
+ if (!parser) {
+ return Error("failed opening ResXMLTree");
+ }
+
+ // Copy the xml string pool data before the parse goes out of scope.
+ auto& string_pool = (*parser)->get_strings();
+ string_pool_data_length = string_pool.bytes();
+ string_pool_data.reset(new uint8_t[string_pool_data_length]);
+ memcpy(string_pool_data.get(), string_pool.data(), string_pool_data_length);
+
+ // Offset string indices by the size of the overlay resource table string pool.
+ string_pool_offset = overlay_arsc->GetStringPool()->size();
+
+ resource_mapping = CreateResourceMapping(&target_asset_manager, target_pkg, overlay_pkg,
+ string_pool_offset, *(*parser));
+ } else {
+ // If no file is specified using android:resourcesMap, it is assumed that the overlay only
+ // defines resources intended to override target resources of the same type and name.
+ resource_mapping = CreateResourceMappingLegacy(&target_asset_manager, &overlay_asset_manager,
+ target_pkg, overlay_pkg);
+ }
+
+ if (!resource_mapping) {
+ return resource_mapping.GetError();
+ }
+
+ if (enforce_overlayable) {
+ // Filter out resources the overlay is not allowed to override.
+ (*resource_mapping)
+ .FilterOverlayableResources(&target_asset_manager, target_pkg, overlay_pkg, overlay_info,
+ fulfilled_policies);
+ }
+
+ resource_mapping->target_package_id_ = target_pkg->GetPackageId();
+ resource_mapping->overlay_package_id_ = overlay_pkg->GetPackageId();
+ resource_mapping->string_pool_offset_ = string_pool_offset;
+ resource_mapping->string_pool_data_ = std::move(string_pool_data);
+ resource_mapping->string_pool_data_length_ = string_pool_data_length;
+ return std::move(*resource_mapping);
+}
+
+OverlayResourceMap ResourceMapping::GetOverlayToTargetMap() const {
+ // An overlay resource can override multiple target resources at once. Rewrite the overlay
+ // resource as the first target resource it overrides.
+ OverlayResourceMap map;
+ for (const auto& mappings : overlay_map_) {
+ map.insert(std::make_pair(mappings.first, mappings.second));
+ }
+ return map;
+}
+
+Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource,
+ TargetValue::DataType data_type,
+ TargetValue::DataValue data_value,
+ bool rewrite_overlay_reference) {
+ if (target_map_.find(target_resource) != target_map_.end()) {
+ return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
+ }
+
+ // TODO(141485591): Ensure that the overlay type is compatible with the target type. If the
+ // runtime types are not compatible, it could cause runtime crashes when the resource is resolved.
+
+ target_map_.insert(std::make_pair(target_resource, TargetValue{data_type, data_value}));
+
+ if (rewrite_overlay_reference &&
+ (data_type == Res_value::TYPE_REFERENCE || data_type == Res_value::TYPE_DYNAMIC_REFERENCE)) {
+ overlay_map_.insert(std::make_pair(data_value, target_resource));
+ }
+
+ return Result<Unit>({});
+}
+
+void ResourceMapping::RemoveMapping(ResourceId target_resource) {
+ auto target_iter = target_map_.find(target_resource);
+ if (target_iter == target_map_.end()) {
+ return;
+ }
+
+ const TargetValue value = target_iter->second;
+ target_map_.erase(target_iter);
+
+ if (value.data_type != Res_value::TYPE_REFERENCE &&
+ value.data_type != Res_value::TYPE_DYNAMIC_REFERENCE) {
+ return;
+ }
+
+ auto overlay_iter = overlay_map_.equal_range(value.data_value);
+ for (auto i = overlay_iter.first; i != overlay_iter.second; ++i) {
+ if (i->second == target_resource) {
+ overlay_map_.erase(i);
+ return;
+ }
+ }
+}
+
+} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp
index 71ba3f0f1ac2..a5df746ca733 100644
--- a/cmds/idmap2/libidmap2/ResourceUtils.cpp
+++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp
@@ -22,18 +22,52 @@
#include "androidfw/StringPiece.h"
#include "androidfw/Util.h"
#include "idmap2/Result.h"
-#include "idmap2/Xml.h"
+#include "idmap2/XmlParser.h"
#include "idmap2/ZipFile.h"
using android::StringPiece16;
using android::idmap2::Result;
-using android::idmap2::Xml;
+using android::idmap2::XmlParser;
using android::idmap2::ZipFile;
using android::util::Utf16ToUtf8;
namespace android::idmap2::utils {
-Result<std::string> ResToTypeEntryName(const AssetManager2& am, ResourceId resid) {
+StringPiece DataTypeToString(uint8_t data_type) {
+ switch (data_type) {
+ case Res_value::TYPE_NULL:
+ return "null";
+ case Res_value::TYPE_REFERENCE:
+ return "reference";
+ case Res_value::TYPE_ATTRIBUTE:
+ return "attribute";
+ case Res_value::TYPE_STRING:
+ return "string";
+ case Res_value::TYPE_FLOAT:
+ return "float";
+ case Res_value::TYPE_DIMENSION:
+ return "dimension";
+ case Res_value::TYPE_FRACTION:
+ return "fraction";
+ case Res_value::TYPE_DYNAMIC_REFERENCE:
+ return "reference (dynamic)";
+ case Res_value::TYPE_DYNAMIC_ATTRIBUTE:
+ return "attribute (dynamic)";
+ case Res_value::TYPE_INT_DEC:
+ case Res_value::TYPE_INT_HEX:
+ return "integer";
+ case Res_value::TYPE_INT_BOOLEAN:
+ return "boolean";
+ case Res_value::TYPE_INT_COLOR_ARGB8:
+ case Res_value::TYPE_INT_COLOR_RGB8:
+ case Res_value::TYPE_INT_COLOR_RGB4:
+ return "color";
+ default:
+ return "unknown";
+ }
+}
+
+Result<std::string> ResToTypeEntryName(const AssetManager2& am, uint32_t resid) {
AssetManager2::ResourceName name;
if (!am.GetResourceName(resid, &name)) {
return Error("no resource 0x%08x in asset manager", resid);
@@ -65,42 +99,72 @@ Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path,
return Error("failed to uncompress AndroidManifest.xml from %s", path.c_str());
}
- std::unique_ptr<const Xml> xml = Xml::Create(entry->buf, entry->size);
+ Result<std::unique_ptr<const XmlParser>> xml = XmlParser::Create(entry->buf, entry->size);
if (!xml) {
return Error("failed to parse AndroidManifest.xml from %s", path.c_str());
}
+ auto manifest_it = (*xml)->tree_iterator();
+ if (manifest_it->event() != XmlParser::Event::START_TAG || manifest_it->name() != "manifest") {
+ return Error("root element tag is not <manifest> in AndroidManifest.xml of %s", path.c_str());
+ }
+
+ auto overlay_it = std::find_if(manifest_it.begin(), manifest_it.end(), [](const auto& it) {
+ return it.event() == XmlParser::Event::START_TAG && it.name() == "overlay";
+ });
+
OverlayManifestInfo info{};
- const auto tag = xml->FindTag("overlay");
- if (!tag) {
- if (assert_overlay) {
- return Error("<overlay> missing from AndroidManifest.xml of %s", path.c_str());
+ if (overlay_it == manifest_it.end()) {
+ if (!assert_overlay) {
+ return info;
}
- return info;
+ return Error("<overlay> missing from AndroidManifest.xml of %s", path.c_str());
}
- auto iter = tag->find("targetPackage");
- if (iter == tag->end()) {
- if (assert_overlay) {
- return Error("android:targetPackage missing from <overlay> of %s", path.c_str());
- }
+ if (auto result_str = overlay_it->GetAttributeStringValue("targetPackage")) {
+ info.target_package = *result_str;
} else {
- info.target_package = iter->second;
+ return Error("android:targetPackage missing from <overlay> of %s: %s", path.c_str(),
+ result_str.GetErrorMessage().c_str());
+ }
+
+ if (auto result_str = overlay_it->GetAttributeStringValue("targetName")) {
+ info.target_name = *result_str;
+ }
+
+ if (auto result_value = overlay_it->GetAttributeValue("resourcesMap")) {
+ if ((*result_value).dataType == Res_value::TYPE_REFERENCE) {
+ info.resource_mapping = (*result_value).data;
+ } else {
+ return Error("android:resourcesMap is not a reference in AndroidManifest.xml of %s",
+ path.c_str());
+ }
}
- iter = tag->find("targetName");
- if (iter != tag->end()) {
- info.target_name = iter->second;
+ if (auto result_value = overlay_it->GetAttributeValue("isStatic")) {
+ if ((*result_value).dataType >= Res_value::TYPE_FIRST_INT &&
+ (*result_value).dataType <= Res_value::TYPE_LAST_INT) {
+ info.is_static = (*result_value).data != 0U;
+ } else {
+ return Error("android:isStatic is not a boolean in AndroidManifest.xml of %s", path.c_str());
+ }
+ }
+
+ if (auto result_value = overlay_it->GetAttributeValue("priority")) {
+ if ((*result_value).dataType >= Res_value::TYPE_FIRST_INT &&
+ (*result_value).dataType <= Res_value::TYPE_LAST_INT) {
+ info.priority = (*result_value).data;
+ } else {
+ return Error("android:priority is not an integer in AndroidManifest.xml of %s", path.c_str());
+ }
}
- iter = tag->find("isStatic");
- if (iter != tag->end()) {
- info.is_static = std::stoul(iter->second) != 0U;
+ if (auto result_str = overlay_it->GetAttributeStringValue("requiredSystemPropertyName")) {
+ info.requiredSystemPropertyName = *result_str;
}
- iter = tag->find("priority");
- if (iter != tag->end()) {
- info.priority = std::stoi(iter->second);
+ if (auto result_str = overlay_it->GetAttributeStringValue("requiredSystemPropertyValue")) {
+ info.requiredSystemPropertyValue = *result_str;
}
return info;
diff --git a/cmds/idmap2/libidmap2/Xml.cpp b/cmds/idmap2/libidmap2/Xml.cpp
deleted file mode 100644
index 264586829c47..000000000000
--- a/cmds/idmap2/libidmap2/Xml.cpp
+++ /dev/null
@@ -1,80 +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 "idmap2/Xml.h"
-
-#include <map>
-#include <memory>
-#include <string>
-#include <utility>
-
-namespace android::idmap2 {
-
-std::unique_ptr<const Xml> Xml::Create(const uint8_t* data, size_t size, bool copyData) {
- std::unique_ptr<Xml> xml(new Xml());
- if (xml->xml_.setTo(data, size, copyData) != NO_ERROR) {
- return nullptr;
- }
- return xml;
-}
-
-std::unique_ptr<std::map<std::string, std::string>> Xml::FindTag(const std::string& name) const {
- const String16 tag_to_find(name.c_str(), name.size());
- xml_.restart();
- ResXMLParser::event_code_t type;
- do {
- type = xml_.next();
- if (type == ResXMLParser::START_TAG) {
- size_t len;
- const String16 tag(xml_.getElementName(&len));
- if (tag == tag_to_find) {
- std::unique_ptr<std::map<std::string, std::string>> map(
- new std::map<std::string, std::string>());
- for (size_t i = 0; i < xml_.getAttributeCount(); i++) {
- const String16 key16(xml_.getAttributeName(i, &len));
- std::string key = String8(key16).c_str();
-
- std::string value;
- switch (xml_.getAttributeDataType(i)) {
- case Res_value::TYPE_STRING: {
- const String16 value16(xml_.getAttributeStringValue(i, &len));
- value = String8(value16).c_str();
- } break;
- case Res_value::TYPE_INT_DEC:
- case Res_value::TYPE_INT_HEX:
- case Res_value::TYPE_INT_BOOLEAN: {
- Res_value resValue;
- xml_.getAttributeValue(i, &resValue);
- value = std::to_string(resValue.data);
- } break;
- default:
- return nullptr;
- }
-
- map->emplace(std::make_pair(key, value));
- }
- return map;
- }
- }
- } while (type != ResXMLParser::BAD_DOCUMENT && type != ResXMLParser::END_DOCUMENT);
- return nullptr;
-}
-
-Xml::~Xml() {
- xml_.uninit();
-}
-
-} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/XmlParser.cpp b/cmds/idmap2/libidmap2/XmlParser.cpp
new file mode 100644
index 000000000000..526a560907aa
--- /dev/null
+++ b/cmds/idmap2/libidmap2/XmlParser.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "idmap2/XmlParser.h"
+
+#include <iostream>
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+
+namespace android::idmap2 {
+
+template <typename T>
+ResXMLParser::ResXMLPosition get_tree_position(const T& tree) {
+ ResXMLParser::ResXMLPosition pos{};
+ tree.getPosition(&pos);
+ return pos;
+}
+
+XmlParser::Node::Node(const ResXMLTree& tree) : Node(tree, get_tree_position(tree)) {
+}
+XmlParser::Node::Node(const ResXMLTree& tree, const ResXMLParser::ResXMLPosition& pos)
+ : parser_(tree) {
+ set_position(pos);
+}
+
+bool XmlParser::Node::operator==(const XmlParser::Node& rhs) const {
+ ResXMLParser::ResXMLPosition pos = get_position();
+ ResXMLParser::ResXMLPosition rhs_pos = rhs.get_position();
+ return pos.curExt == rhs_pos.curExt && pos.curNode == rhs_pos.curNode &&
+ pos.eventCode == rhs_pos.eventCode;
+}
+
+bool XmlParser::Node::operator!=(const XmlParser::Node& rhs) const {
+ return !(*this == rhs);
+}
+
+ResXMLParser::ResXMLPosition XmlParser::Node::get_position() const {
+ return get_tree_position(parser_);
+}
+
+void XmlParser::Node::set_position(const ResXMLParser::ResXMLPosition& pos) {
+ parser_.setPosition(pos);
+}
+
+bool XmlParser::Node::Seek(bool inner_child) {
+ if (parser_.getEventType() == XmlParser::Event::END_TAG) {
+ return false;
+ }
+
+ ssize_t depth = 0;
+ XmlParser::Event code;
+ while ((code = parser_.next()) != XmlParser::Event::BAD_DOCUMENT &&
+ code != XmlParser::Event::END_DOCUMENT) {
+ if (code == XmlParser::Event::START_TAG) {
+ if (++depth == (inner_child ? 1 : 0)) {
+ return true;
+ }
+ } else if (code == XmlParser::Event::END_TAG) {
+ if (--depth == (inner_child ? -1 : -2)) {
+ return false;
+ }
+ }
+ }
+
+ return false;
+}
+
+XmlParser::Event XmlParser::Node::event() const {
+ return parser_.getEventType();
+}
+
+std::string XmlParser::Node::name() const {
+ size_t len;
+ const String16 key16(parser_.getElementName(&len));
+ return String8(key16).c_str();
+}
+
+Result<std::string> XmlParser::Node::GetAttributeStringValue(const std::string& name) const {
+ auto value = GetAttributeValue(name);
+ if (!value) {
+ return value.GetError();
+ }
+
+ switch ((*value).dataType) {
+ case Res_value::TYPE_STRING: {
+ size_t len;
+ const String16 value16(parser_.getStrings().stringAt((*value).data, &len));
+ return std::string(String8(value16).c_str());
+ }
+ case Res_value::TYPE_INT_DEC:
+ case Res_value::TYPE_INT_HEX:
+ case Res_value::TYPE_INT_BOOLEAN: {
+ return std::to_string((*value).data);
+ }
+ default:
+ return Error(R"(Failed to convert attribute "%s" value to a string)", name.c_str());
+ }
+}
+
+Result<Res_value> XmlParser::Node::GetAttributeValue(const std::string& name) const {
+ size_t len;
+ for (size_t i = 0; i < parser_.getAttributeCount(); i++) {
+ const String16 key16(parser_.getAttributeName(i, &len));
+ std::string key = String8(key16).c_str();
+ if (key != name) {
+ continue;
+ }
+
+ Res_value res_value{};
+ if (parser_.getAttributeValue(i, &res_value) == BAD_TYPE) {
+ return Error(R"(Bad value for attribute "%s")", name.c_str());
+ }
+
+ return res_value;
+ }
+
+ return Error(R"(Failed to find attribute "%s")", name.c_str());
+}
+
+Result<std::unique_ptr<const XmlParser>> XmlParser::Create(const void* data, size_t size,
+ bool copy_data) {
+ auto parser = std::unique_ptr<const XmlParser>(new XmlParser());
+ if (parser->tree_.setTo(data, size, copy_data) != NO_ERROR) {
+ return Error("Malformed xml block");
+ }
+
+ // Find the beginning of the first tag.
+ XmlParser::Event event;
+ while ((event = parser->tree_.next()) != XmlParser::Event::BAD_DOCUMENT &&
+ event != XmlParser::Event::END_DOCUMENT && event != XmlParser::Event::START_TAG) {
+ }
+
+ if (event == XmlParser::Event::END_DOCUMENT) {
+ return Error("Root tag was not be found");
+ }
+
+ if (event == XmlParser::Event::BAD_DOCUMENT) {
+ return Error("Bad xml document");
+ }
+
+ return parser;
+}
+
+XmlParser::~XmlParser() {
+ tree_.uninit();
+}
+
+} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/ZipFile.cpp b/cmds/idmap2/libidmap2/ZipFile.cpp
index 4f5e3a45f183..1e1a218163f0 100644
--- a/cmds/idmap2/libidmap2/ZipFile.cpp
+++ b/cmds/idmap2/libidmap2/ZipFile.cpp
@@ -34,6 +34,7 @@ std::unique_ptr<const ZipFile> ZipFile::Open(const std::string& path) {
::ZipArchiveHandle handle;
int32_t status = ::OpenArchive(path.c_str(), &handle);
if (status != 0) {
+ ::CloseArchive(handle);
return nullptr;
}
return std::unique_ptr<ZipFile>(new ZipFile(handle));
diff --git a/cmds/idmap2/static-checks.sh b/cmds/idmap2/static-checks.sh
index 41d3c69540b2..a372abdaa146 100755
--- a/cmds/idmap2/static-checks.sh
+++ b/cmds/idmap2/static-checks.sh
@@ -27,10 +27,11 @@ function _eval()
local red="\e[31m"
local green="\e[32m"
local reset="\e[0m"
+ local output
_log "${green}[ RUN ]${reset} ${label}"
- local output="$(eval "$cmd")"
- if [[ -z "${output}" ]]; then
+ output="$(eval "$cmd" 2>&1)"
+ if [[ $? -eq 0 ]]; then
_log "${green}[ OK ]${reset} ${label}"
return 0
else
diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
index 9348ab721493..db4778c8ee09 100644
--- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
+++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
@@ -18,6 +18,7 @@
#include <sstream>
#include <string>
#include <utility>
+#include <vector>
#include "TestHelpers.h"
#include "androidfw/ApkAssets.h"
@@ -52,113 +53,39 @@ TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) {
ASSERT_EQ(idmap1->GetData().size(), 1U);
ASSERT_EQ(idmap1->GetData().size(), idmap2->GetData().size());
- const auto& data1 = idmap1->GetData()[0];
- const auto& data2 = idmap2->GetData()[0];
-
- ASSERT_EQ(data1->GetHeader()->GetTargetPackageId(), data2->GetHeader()->GetTargetPackageId());
- ASSERT_EQ(data1->GetTypeEntries().size(), 2U);
- ASSERT_EQ(data1->GetTypeEntries().size(), data2->GetTypeEntries().size());
- ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(0), data2->GetTypeEntries()[0]->GetEntry(0));
- ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(1), data2->GetTypeEntries()[0]->GetEntry(1));
- ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(2), data2->GetTypeEntries()[0]->GetEntry(2));
- ASSERT_EQ(data1->GetTypeEntries()[1]->GetEntry(0), data2->GetTypeEntries()[1]->GetEntry(0));
- ASSERT_EQ(data1->GetTypeEntries()[1]->GetEntry(1), data2->GetTypeEntries()[1]->GetEntry(1));
- ASSERT_EQ(data1->GetTypeEntries()[1]->GetEntry(2), data2->GetTypeEntries()[1]->GetEntry(2));
-}
-
-TEST(BinaryStreamVisitorTests, CreateIdmapFromApkAssetsInteropWithLoadedIdmap) {
- const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
- std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
- ASSERT_THAT(target_apk, NotNull());
-
- const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
- std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
- ASSERT_THAT(overlay_apk, NotNull());
-
- const auto idmap =
- Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
- PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true);
- ASSERT_TRUE(idmap);
-
- std::stringstream stream;
- BinaryStreamVisitor visitor(stream);
- (*idmap)->accept(&visitor);
- const std::string str = stream.str();
- const StringPiece data(str);
- std::unique_ptr<const LoadedIdmap> loaded_idmap = LoadedIdmap::Load(data);
- ASSERT_THAT(loaded_idmap, NotNull());
- ASSERT_EQ(loaded_idmap->TargetPackageId(), 0x7f);
-
- const IdmapEntry_header* header = loaded_idmap->GetEntryMapForType(0x01);
- ASSERT_THAT(header, NotNull());
-
- EntryId entry;
- bool success = LoadedIdmap::Lookup(header, 0x0000, &entry);
- ASSERT_TRUE(success);
- ASSERT_EQ(entry, 0x0000);
-
- header = loaded_idmap->GetEntryMapForType(0x02);
- ASSERT_THAT(header, NotNull());
-
- success = LoadedIdmap::Lookup(header, 0x0000, &entry); // string/a
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0001, &entry); // string/b
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0002, &entry); // string/c
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0003, &entry); // string/policy_odm
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0004, &entry); // string/policy_oem
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0005, &entry); // string/other
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0006, &entry); // string/not_overlayable
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0007, &entry); // string/policy_product
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0008, &entry); // string/policy_public
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0009, &entry); // string/policy_system
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x000a, &entry); // string/policy_system_vendor
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x000b, &entry); // string/policy_signature
- ASSERT_FALSE(success);
+ const std::vector<std::unique_ptr<const IdmapData>>& data_blocks1 = idmap1->GetData();
+ ASSERT_EQ(data_blocks1.size(), 1U);
+ const std::unique_ptr<const IdmapData>& data1 = data_blocks1[0];
+ ASSERT_THAT(data1, NotNull());
- success = LoadedIdmap::Lookup(header, 0x000c, &entry); // string/str1
- ASSERT_TRUE(success);
- ASSERT_EQ(entry, 0x0000);
+ const std::vector<std::unique_ptr<const IdmapData>>& data_blocks2 = idmap2->GetData();
+ ASSERT_EQ(data_blocks2.size(), 1U);
+ const std::unique_ptr<const IdmapData>& data2 = data_blocks2[0];
+ ASSERT_THAT(data2, NotNull());
- success = LoadedIdmap::Lookup(header, 0x000d, &entry); // string/str2
- ASSERT_FALSE(success);
+ const auto& target_entries1 = data1->GetTargetEntries();
+ const auto& target_entries2 = data2->GetTargetEntries();
+ ASSERT_EQ(target_entries1.size(), target_entries2.size());
+ ASSERT_EQ(target_entries1[0].target_id, target_entries2[0].target_id);
+ ASSERT_EQ(target_entries1[0].data_value, target_entries2[0].data_value);
- success = LoadedIdmap::Lookup(header, 0x000e, &entry); // string/str3
- ASSERT_TRUE(success);
- ASSERT_EQ(entry, 0x0001);
+ ASSERT_EQ(target_entries1[1].target_id, target_entries2[1].target_id);
+ ASSERT_EQ(target_entries1[1].data_value, target_entries2[1].data_value);
- success = LoadedIdmap::Lookup(header, 0x000f, &entry); // string/str4
- ASSERT_TRUE(success);
- ASSERT_EQ(entry, 0x0002);
+ ASSERT_EQ(target_entries1[2].target_id, target_entries2[2].target_id);
+ ASSERT_EQ(target_entries1[2].data_value, target_entries2[2].data_value);
- success = LoadedIdmap::Lookup(header, 0x0010, &entry); // string/x
- ASSERT_FALSE(success);
+ const auto& overlay_entries1 = data1->GetOverlayEntries();
+ const auto& overlay_entries2 = data2->GetOverlayEntries();
+ ASSERT_EQ(overlay_entries1.size(), overlay_entries2.size());
+ ASSERT_EQ(overlay_entries1[0].overlay_id, overlay_entries2[0].overlay_id);
+ ASSERT_EQ(overlay_entries1[0].target_id, overlay_entries2[0].target_id);
- success = LoadedIdmap::Lookup(header, 0x0011, &entry); // string/y
- ASSERT_FALSE(success);
+ ASSERT_EQ(overlay_entries1[1].overlay_id, overlay_entries2[1].overlay_id);
+ ASSERT_EQ(overlay_entries1[1].target_id, overlay_entries2[1].target_id);
- success = LoadedIdmap::Lookup(header, 0x0012, &entry); // string/z
- ASSERT_FALSE(success);
+ ASSERT_EQ(overlay_entries1[2].overlay_id, overlay_entries2[2].overlay_id);
+ ASSERT_EQ(overlay_entries1[2].target_id, overlay_entries2[2].target_id);
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
index 8a48f4b8e6d5..b535f30de1f5 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -131,7 +131,6 @@ TEST_F(Idmap2BinaryTests, Dump) {
ASSERT_NE(result->stdout.find("0x7f02000c -> 0x7f020000 string/str1"), std::string::npos);
ASSERT_NE(result->stdout.find("0x7f02000e -> 0x7f020001 string/str3"), std::string::npos);
ASSERT_NE(result->stdout.find("0x7f02000f -> 0x7f020002 string/str4"), std::string::npos);
- ASSERT_EQ(result->stdout.find("00000210: 007f target package id"), std::string::npos);
// clang-format off
result = ExecuteBinary({"idmap2",
@@ -142,7 +141,6 @@ TEST_F(Idmap2BinaryTests, Dump) {
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
ASSERT_NE(result->stdout.find("00000000: 504d4449 magic"), std::string::npos);
- ASSERT_NE(result->stdout.find("00000210: 007f target package id"), std::string::npos);
// clang-format off
result = ExecuteBinary({"idmap2",
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 0f47f1e77d7b..cd816ddea814 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -31,11 +31,21 @@
#include "idmap2/CommandLineOptions.h"
#include "idmap2/Idmap.h"
+using android::Res_value;
using ::testing::IsNull;
using ::testing::NotNull;
namespace android::idmap2 {
+#define ASSERT_TARGET_ENTRY(entry, target_resid, type, value) \
+ ASSERT_EQ(entry.target_id, target_resid); \
+ ASSERT_EQ(entry.data_type, type); \
+ ASSERT_EQ(entry.data_value, value)
+
+#define ASSERT_OVERLAY_ENTRY(entry, overlay_resid, target_resid) \
+ ASSERT_EQ(entry.overlay_id, overlay_resid); \
+ ASSERT_EQ(entry.target_id, target_resid)
+
TEST(IdmapTests, TestCanonicalIdmapPathFor) {
ASSERT_EQ(Idmap::CanonicalIdmapPathFor("/foo", "/vendor/overlay/bar.apk"),
"/foo/vendor@overlay@bar.apk@idmap");
@@ -47,11 +57,11 @@ TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) {
std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
ASSERT_THAT(header, NotNull());
ASSERT_EQ(header->GetMagic(), 0x504d4449U);
- ASSERT_EQ(header->GetVersion(), 0x01U);
+ ASSERT_EQ(header->GetVersion(), 0x02U);
ASSERT_EQ(header->GetTargetCrc(), 0x1234U);
ASSERT_EQ(header->GetOverlayCrc(), 0x5678U);
- ASSERT_EQ(header->GetTargetPath().to_string(), "target.apk");
- ASSERT_EQ(header->GetOverlayPath().to_string(), "overlay.apk");
+ ASSERT_EQ(header->GetTargetPath().to_string(), "targetX.apk");
+ ASSERT_EQ(header->GetOverlayPath().to_string(), "overlayX.apk");
}
TEST(IdmapTests, FailToCreateIdmapHeaderFromBinaryStreamIfPathTooLong) {
@@ -73,23 +83,8 @@ TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) {
std::unique_ptr<const IdmapData::Header> header = IdmapData::Header::FromBinaryStream(stream);
ASSERT_THAT(header, NotNull());
- ASSERT_EQ(header->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(header->GetTypeCount(), 2U);
-}
-
-TEST(IdmapTests, CreateIdmapDataResourceTypeFromBinaryStream) {
- const size_t offset = 0x214;
- std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
- idmap_raw_data_len - offset);
- std::istringstream stream(raw);
-
- std::unique_ptr<const IdmapData::TypeEntry> data = IdmapData::TypeEntry::FromBinaryStream(stream);
- ASSERT_THAT(data, NotNull());
- ASSERT_EQ(data->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(data->GetOverlayTypeId(), 0x02U);
- ASSERT_EQ(data->GetEntryCount(), 1U);
- ASSERT_EQ(data->GetEntryOffset(), 0U);
- ASSERT_EQ(data->GetEntry(0), 0U);
+ ASSERT_EQ(header->GetTargetEntryCount(), 0x03);
+ ASSERT_EQ(header->GetOverlayEntryCount(), 0x03);
}
TEST(IdmapTests, CreateIdmapDataFromBinaryStream) {
@@ -100,24 +95,21 @@ TEST(IdmapTests, CreateIdmapDataFromBinaryStream) {
std::unique_ptr<const IdmapData> data = IdmapData::FromBinaryStream(stream);
ASSERT_THAT(data, NotNull());
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2U);
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 2U);
-
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetEntryCount(), 1U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 0U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);
-
- ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03U);
- ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03U);
- ASSERT_EQ(types[1]->GetEntryCount(), 3U);
- ASSERT_EQ(types[1]->GetEntryOffset(), 3U);
- ASSERT_EQ(types[1]->GetEntry(0), 0x0000U);
- ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
- ASSERT_EQ(types[1]->GetEntry(2), 0x0001U);
+
+ const auto& target_entries = data->GetTargetEntries();
+ ASSERT_EQ(target_entries.size(), 3U);
+ ASSERT_TARGET_ENTRY(target_entries[0], 0x7f020000, 0x01 /* Res_value::TYPE_REFERENCE */,
+ 0x7f020000);
+ ASSERT_TARGET_ENTRY(target_entries[1], 0x7f030000, 0x01 /* Res_value::TYPE_REFERENCE */,
+ 0x7f030000);
+ ASSERT_TARGET_ENTRY(target_entries[2], 0x7f030002, 0x01 /* Res_value::TYPE_REFERENCE */,
+ 0x7f030001);
+
+ const auto& overlay_entries = data->GetOverlayEntries();
+ ASSERT_EQ(target_entries.size(), 3U);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[0], 0x7f020000, 0x7f020000);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[1], 0x7f030000, 0x7f030000);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[2], 0x7f030001, 0x7f030002);
}
TEST(IdmapTests, CreateIdmapFromBinaryStream) {
@@ -130,34 +122,29 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) {
ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
- ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U);
+ ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x02U);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U);
ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U);
- ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), "target.apk");
- ASSERT_EQ(idmap->GetHeader()->GetOverlayPath().to_string(), "overlay.apk");
+ ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), "targetX.apk");
+ ASSERT_EQ(idmap->GetHeader()->GetOverlayPath().to_string(), "overlayX.apk");
const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
ASSERT_EQ(dataBlocks.size(), 1U);
const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2U);
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 2U);
-
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetEntryCount(), 1U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 0U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);
-
- ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03U);
- ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03U);
- ASSERT_EQ(types[1]->GetEntryCount(), 3U);
- ASSERT_EQ(types[1]->GetEntryOffset(), 3U);
- ASSERT_EQ(types[1]->GetEntry(0), 0x0000U);
- ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
- ASSERT_EQ(types[1]->GetEntry(2), 0x0001U);
+ ASSERT_THAT(data, NotNull());
+
+ const auto& target_entries = data->GetTargetEntries();
+ ASSERT_EQ(target_entries.size(), 3U);
+ ASSERT_TARGET_ENTRY(target_entries[0], 0x7f020000, Res_value::TYPE_REFERENCE, 0x7f020000);
+ ASSERT_TARGET_ENTRY(target_entries[1], 0x7f030000, Res_value::TYPE_REFERENCE, 0x7f030000);
+ ASSERT_TARGET_ENTRY(target_entries[2], 0x7f030002, Res_value::TYPE_REFERENCE, 0x7f030001);
+
+ const auto& overlay_entries = data->GetOverlayEntries();
+ ASSERT_EQ(target_entries.size(), 3U);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[0], 0x7f020000, 0x7f020000);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[1], 0x7f030000, 0x7f030000);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[2], 0x7f030001, 0x7f030002);
}
TEST(IdmapTests, GracefullyFailToCreateIdmapFromCorruptBinaryStream) {
@@ -169,301 +156,140 @@ TEST(IdmapTests, GracefullyFailToCreateIdmapFromCorruptBinaryStream) {
ASSERT_FALSE(result);
}
-void CreateIdmap(const StringPiece& target_apk_path, const StringPiece& overlay_apk_path,
- const PolicyBitmask& fulfilled_policies, bool enforce_overlayable,
- std::unique_ptr<const Idmap>* out_idmap) {
- std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path.to_string());
+TEST(IdmapTests, CreateIdmapHeaderFromApkAssets) {
+ std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
+ std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk";
+
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
ASSERT_THAT(target_apk, NotNull());
- std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path.to_string());
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- auto result =
- Idmap::FromApkAssets(target_apk_path.to_string(), *target_apk, overlay_apk_path.to_string(),
- *overlay_apk, fulfilled_policies, enforce_overlayable);
- *out_idmap = result ? std::move(*result) : nullptr;
-}
-
-TEST(IdmapTests, CreateIdmapFromApkAssets) {
- std::unique_ptr<const Idmap> idmap;
- std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
- std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk";
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ true, &idmap);
+ auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ true);
+ ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
+ auto& idmap = *idmap_result;
+ ASSERT_THAT(idmap, NotNull());
ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
- ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U);
+ ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x02U);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x76a20829);
- ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x8635c2ed);
+ ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xc054fb26);
ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path);
ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
- ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
-
- const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
- ASSERT_EQ(dataBlocks.size(), 1U);
-
- const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
-
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2U);
-
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 2U);
-
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetEntryCount(), 1U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 0U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);
-
- ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02U);
- ASSERT_EQ(types[1]->GetEntryCount(), 4U);
- ASSERT_EQ(types[1]->GetEntryOffset(), 12U);
- ASSERT_EQ(types[1]->GetEntry(0), 0x0000U);
- ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
- ASSERT_EQ(types[1]->GetEntry(2), 0x0001U);
- ASSERT_EQ(types[1]->GetEntry(3), 0x0002U);
}
-// Overlays should abide by all overlayable restrictions if enforcement of overlayable is enabled.
-TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
- std::unique_ptr<const Idmap> idmap;
- std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
- std::string overlay_apk_path = GetTestDataPath() + "/system-overlay/system-overlay.apk";
- CreateIdmap(target_apk_path, overlay_apk_path,
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, NotNull());
-
- const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
- ASSERT_EQ(dataBlocks.size(), 1U);
-
- const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
-
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U);
-
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 1U);
-
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetEntryCount(), 4U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 8U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/policy_public
- ASSERT_EQ(types[0]->GetEntry(1), kNoEntry); // string/policy_signature
- ASSERT_EQ(types[0]->GetEntry(2), 0x0001U); // string/policy_system
- ASSERT_EQ(types[0]->GetEntry(3), 0x0002U); // string/policy_system_vendor
-}
-
-TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySignature) {
- std::unique_ptr<const Idmap> idmap;
- std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
- std::string overlay_apk_path = GetTestDataPath() + "/signature-overlay/signature-overlay.apk";
- CreateIdmap(target_apk_path, overlay_apk_path,
- PolicyFlags::POLICY_PUBLIC | PolicyFlags::POLICY_SIGNATURE,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, NotNull());
-
- const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
- ASSERT_EQ(dataBlocks.size(), 1U);
-
- const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
-
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U);
-
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 1U);
-
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetEntryCount(), 1U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 9U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/policy_signature
-}
-
-// Overlays should abide by all overlayable restrictions if enforcement of overlayable is enabled.
-TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
- std::unique_ptr<const Idmap> idmap;
- std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
- std::string overlay_apk_path =
- GetTestDataPath() + "/system-overlay-invalid/system-overlay-invalid.apk";
- CreateIdmap(target_apk_path, overlay_apk_path,
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, NotNull());
-
- const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
- ASSERT_EQ(dataBlocks.size(), 1U);
+Result<std::unique_ptr<const IdmapData>> TestIdmapDataFromApkAssets(
+ const android::StringPiece& local_target_apk_path,
+ const android::StringPiece& local_overlay_apk_path, const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies, bool enforce_overlayable) {
+ const std::string target_apk_path(GetTestDataPath() + local_target_apk_path.data());
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ if (!target_apk) {
+ return Error(R"(Failed to load target apk "%s")", target_apk_path.data());
+ }
- const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+ const std::string overlay_apk_path(GetTestDataPath() + local_overlay_apk_path.data());
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ if (!overlay_apk) {
+ return Error(R"(Failed to load overlay apk "%s")", overlay_apk_path.data());
+ }
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U);
+ auto mapping = ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, overlay_info,
+ fulfilled_policies, enforce_overlayable);
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 1U);
+ if (!mapping) {
+ return mapping.GetError();
+ }
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetEntryCount(), 4U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 8U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0005U); // string/policy_public
- ASSERT_EQ(types[0]->GetEntry(1), kNoEntry); // string/policy_signature
- ASSERT_EQ(types[0]->GetEntry(2), 0x0007U); // string/policy_system
- ASSERT_EQ(types[0]->GetEntry(3), 0x0008U); // string/policy_system_vendor
+ return IdmapData::FromResourceMapping(*mapping);
}
-// Overlays should ignore all overlayable restrictions if enforcement of overlayable is disabled.
-TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) {
- std::unique_ptr<const Idmap> idmap;
+TEST(IdmapTests, CreateIdmapDataFromApkAssets) {
std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
- std::string overlay_apk_path =
- GetTestDataPath() + "/system-overlay-invalid/system-overlay-invalid.apk";
- CreateIdmap(target_apk_path, overlay_apk_path,
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ false, &idmap);
- ASSERT_THAT(idmap, NotNull());
-
- const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
- ASSERT_EQ(dataBlocks.size(), 1U);
+ std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk";
- const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ ASSERT_THAT(target_apk, NotNull());
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U);
-
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 1U);
-
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetEntryCount(), 9U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 3U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/not_overlayable
- ASSERT_EQ(types[0]->GetEntry(1), 0x0001U); // string/policy_odm
- ASSERT_EQ(types[0]->GetEntry(2), 0x0002U); // string/policy_oem
- ASSERT_EQ(types[0]->GetEntry(3), 0x0003U); // string/other
- ASSERT_EQ(types[0]->GetEntry(4), 0x0004U); // string/policy_product
- ASSERT_EQ(types[0]->GetEntry(5), 0x0005U); // string/policy_public
- ASSERT_EQ(types[0]->GetEntry(6), 0x0006U); // string/policy_signature
- ASSERT_EQ(types[0]->GetEntry(7), 0x0007U); // string/policy_system
- ASSERT_EQ(types[0]->GetEntry(8), 0x0008U); // string/policy_system_vendor
-}
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ ASSERT_THAT(overlay_apk, NotNull());
-// Overlays that do not specify a target <overlayable> can overlay resources defined as overlayable.
-TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsNoDefinedOverlayableAndNoTargetName) {
- std::unique_ptr<const Idmap> idmap;
- std::string target_apk_path = GetTestDataPath() + "/target/target-no-overlayable.apk";
- std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay-no-name.apk";
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ false, &idmap);
+ auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ true);
+ ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
+ auto& idmap = *idmap_result;
ASSERT_THAT(idmap, NotNull());
const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
ASSERT_EQ(dataBlocks.size(), 1U);
const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+ ASSERT_THAT(data, NotNull());
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2U);
-
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 2U);
-
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetEntryCount(), 1U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 0U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/int1
-
- ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02U);
- ASSERT_EQ(types[1]->GetEntryCount(), 4U);
- ASSERT_EQ(types[1]->GetEntryOffset(), 12U);
- ASSERT_EQ(types[1]->GetEntry(0), 0x0000U); // string/str1
- ASSERT_EQ(types[1]->GetEntry(1), kNoEntry); // string/str2
- ASSERT_EQ(types[1]->GetEntry(2), 0x0001U); // string/str3
- ASSERT_EQ(types[1]->GetEntry(3), 0x0002U); // string/str4
+ const auto& target_entries = data->GetTargetEntries();
+ ASSERT_EQ(target_entries.size(), 4U);
+ ASSERT_TARGET_ENTRY(target_entries[0], 0x7f010000, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f010000);
+ ASSERT_TARGET_ENTRY(target_entries[1], 0x7f02000c, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020000);
+ ASSERT_TARGET_ENTRY(target_entries[2], 0x7f02000e, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020001);
+ ASSERT_TARGET_ENTRY(target_entries[3], 0x7f02000f, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020002);
+
+ const auto& overlay_entries = data->GetOverlayEntries();
+ ASSERT_EQ(target_entries.size(), 4U);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[0], 0x7f010000, 0x7f010000);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[1], 0x7f020000, 0x7f02000c);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[2], 0x7f020001, 0x7f02000e);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[3], 0x7f020002, 0x7f02000f);
}
-// Overlays that are not pre-installed and are not signed with the same signature as the target
-// cannot overlay packages that have not defined overlayable resources.
-TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsDefaultPoliciesPublicFail) {
- std::unique_ptr<const Idmap> idmap;
- std::string target_apk_path = GetTestDataPath() + "/target/target-no-overlayable.apk";
- std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay-no-name.apk";
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, IsNull());
+TEST(IdmapTests, CreateIdmapDataDoNotRewriteNonOverlayResourceId) {
+ OverlayManifestInfo info{};
+ info.target_package = "test.target";
+ info.target_name = "TestResources";
+ info.resource_mapping = 0x7f030001; // xml/overlays_different_packages
+ auto idmap_data = TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", info,
+ PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage();
+ auto& data = *idmap_data;
+
+ const auto& target_entries = data->GetTargetEntries();
+ ASSERT_EQ(target_entries.size(), 2U);
+ ASSERT_TARGET_ENTRY(target_entries[0], 0x7f02000c, Res_value::TYPE_REFERENCE,
+ 0x0104000a); // string/str1 -> android:string/ok
+ ASSERT_TARGET_ENTRY(target_entries[1], 0x7f02000e, Res_value::TYPE_DYNAMIC_REFERENCE,
+ 0x7f020001); // string/str3 -> string/str4
+
+ const auto& overlay_entries = data->GetOverlayEntries();
+ ASSERT_EQ(overlay_entries.size(), 1U);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[0], 0x7f020001, 0x7f02000e); // string/str3 <- string/str4
}
-// Overlays that are pre-installed or are signed with the same signature as the target can overlay
-// packages that have not defined overlayable resources.
-TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsDefaultPolicies) {
- std::unique_ptr<const Idmap> idmap;
- std::string target_apk_path = GetTestDataPath() + "/target/target-no-overlayable.apk";
- std::string overlay_apk_path =
- GetTestDataPath() + "/system-overlay-invalid/system-overlay-invalid.apk";
-
- auto CheckEntries = [&]() -> void {
- const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
- ASSERT_EQ(dataBlocks.size(), 1U);
-
- const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U);
-
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 1U);
-
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetEntryCount(), 9U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 3U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/not_overlayable
- ASSERT_EQ(types[0]->GetEntry(1), 0x0001U); // string/policy_odm
- ASSERT_EQ(types[0]->GetEntry(2), 0x0002U); // string/policy_oem
- ASSERT_EQ(types[0]->GetEntry(3), 0x0003U); // string/other
- ASSERT_EQ(types[0]->GetEntry(4), 0x0004U); // string/policy_product
- ASSERT_EQ(types[0]->GetEntry(5), 0x0005U); // string/policy_public
- ASSERT_EQ(types[0]->GetEntry(6), 0x0006U); // string/policy_signature
- ASSERT_EQ(types[0]->GetEntry(7), 0x0007U); // string/policy_system
- ASSERT_EQ(types[0]->GetEntry(8), 0x0008U); // string/policy_system_vendor
- };
-
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_SIGNATURE,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, NotNull());
- CheckEntries();
-
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_PRODUCT_PARTITION,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, NotNull());
- CheckEntries();
-
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_SYSTEM_PARTITION,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, NotNull());
- CheckEntries();
-
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_VENDOR_PARTITION,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, NotNull());
- CheckEntries();
-
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_ODM_PARTITION,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, NotNull());
- CheckEntries();
-
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_OEM_PARTITION,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, NotNull());
- CheckEntries();
+TEST(IdmapTests, CreateIdmapDataInlineResources) {
+ OverlayManifestInfo info{};
+ info.target_package = "test.target";
+ info.target_name = "TestResources";
+ info.resource_mapping = 0x7f030002; // xml/overlays_inline
+ auto idmap_data = TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", info,
+ PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage();
+ auto& data = *idmap_data;
+
+ constexpr size_t overlay_string_pool_size = 8U;
+ const auto& target_entries = data->GetTargetEntries();
+ ASSERT_EQ(target_entries.size(), 2U);
+ ASSERT_TARGET_ENTRY(target_entries[0], 0x7f010000, Res_value::TYPE_INT_DEC,
+ 73U); // integer/int1 -> 73
+ ASSERT_TARGET_ENTRY(target_entries[1], 0x7f02000c, Res_value::TYPE_STRING,
+ overlay_string_pool_size + 0U); // string/str1 -> "Hello World"
+
+ const auto& overlay_entries = data->GetOverlayEntries();
+ ASSERT_EQ(overlay_entries.size(), 0U);
}
TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) {
@@ -480,9 +306,8 @@ TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) {
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- const auto result =
- Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
- PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true);
+ const auto result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_FALSE(result);
}
@@ -497,8 +322,7 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) {
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- auto result = Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
- PolicyFlags::POLICY_PUBLIC,
+ auto result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(result);
const auto idmap = std::move(*result);
@@ -605,10 +429,6 @@ class TestVisitor : public Visitor {
stream_ << "TestVisitor::visit(IdmapData::Header)" << std::endl;
}
- void visit(const IdmapData::TypeEntry& idmap ATTRIBUTE_UNUSED) override {
- stream_ << "TestVisitor::visit(IdmapData::TypeEntry)" << std::endl;
- }
-
private:
std::ostream& stream_;
};
@@ -625,12 +445,10 @@ TEST(IdmapTests, TestVisitor) {
(*idmap)->accept(&visitor);
ASSERT_EQ(test_stream.str(),
- "TestVisitor::visit(Idmap)\n"
"TestVisitor::visit(IdmapHeader)\n"
- "TestVisitor::visit(IdmapData)\n"
+ "TestVisitor::visit(Idmap)\n"
"TestVisitor::visit(IdmapData::Header)\n"
- "TestVisitor::visit(IdmapData::TypeEntry)\n"
- "TestVisitor::visit(IdmapData::TypeEntry)\n");
+ "TestVisitor::visit(IdmapData)\n");
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
index c41250457678..1d34e42e188d 100644
--- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
@@ -43,9 +43,8 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) {
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- const auto idmap =
- Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
- PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true);
+ const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_TRUE(idmap);
std::stringstream stream;
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index 26951763cd66..d387880cb771 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -38,9 +38,8 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- const auto idmap =
- Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
- PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true);
+ const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_TRUE(idmap);
std::stringstream stream;
@@ -48,11 +47,21 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
(*idmap)->accept(&visitor);
ASSERT_NE(stream.str().find("00000000: 504d4449 magic\n"), std::string::npos);
- ASSERT_NE(stream.str().find("00000004: 00000001 version\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000004: 00000002 version\n"), std::string::npos);
ASSERT_NE(stream.str().find("00000008: 76a20829 target crc\n"), std::string::npos);
- ASSERT_NE(stream.str().find("0000000c: 8635c2ed overlay crc\n"), std::string::npos);
- ASSERT_NE(stream.str().find("0000021c: 00000000 0x7f010000 -> 0x7f010000 integer/int1\n"),
+ ASSERT_NE(stream.str().find("0000000c: c054fb26 overlay crc\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000210: 7f target package id\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000211: 7f overlay package id\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000212: 00000004 target entry count\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000216: 00000004 overlay entry count\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("0000021a: 00000008 string pool index offset\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("0000021e: 000000b4 string pool byte length\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000222: 7f010000 target id: integer/int1\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000226: 07 type: reference (dynamic)\n"),
std::string::npos);
+ ASSERT_NE(stream.str().find("00000227: 7f010000 value: integer/int1\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000246: 7f010000 overlay id: integer/int1\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("0000024a: 7f010000 target id: integer/int1\n"), std::string::npos);
}
TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
@@ -69,10 +78,21 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
(*idmap)->accept(&visitor);
ASSERT_NE(stream.str().find("00000000: 504d4449 magic\n"), std::string::npos);
- ASSERT_NE(stream.str().find("00000004: 00000001 version\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000004: 00000002 version\n"), std::string::npos);
ASSERT_NE(stream.str().find("00000008: 00001234 target crc\n"), std::string::npos);
ASSERT_NE(stream.str().find("0000000c: 00005678 overlay crc\n"), std::string::npos);
- ASSERT_NE(stream.str().find("0000021c: 00000000 0x7f020000 -> 0x7f020000\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000210: 7f target package id\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000211: 7f overlay package id\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000212: 00000003 target entry count\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000216: 00000003 overlay entry count\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("0000021a: 00000000 string pool index offset\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("0000021e: 00000000 string pool byte length\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000222: 7f020000 target id\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000226: 01 type: reference\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000227: 7f020000 value\n"), std::string::npos);
+
+ ASSERT_NE(stream.str().find("0000023d: 7f020000 overlay id\n"), std::string::npos);
+ ASSERT_NE(stream.str().find("00000241: 7f020000 target id\n"), std::string::npos);
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp
new file mode 100644
index 000000000000..64304f64d22c
--- /dev/null
+++ b/cmds/idmap2/tests/ResourceMappingTests.cpp
@@ -0,0 +1,323 @@
+/*
+ * 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 <cstdio> // fclose
+#include <fstream>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "TestHelpers.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "idmap2/ResourceMapping.h"
+
+using android::Res_value;
+using android::idmap2::utils::ExtractOverlayManifestInfo;
+
+namespace android::idmap2 {
+
+#define ASSERT_RESULT(r) \
+ do { \
+ auto result = r; \
+ ASSERT_TRUE(result) << result.GetErrorMessage(); \
+ } while (0)
+
+Result<ResourceMapping> TestGetResourceMapping(const android::StringPiece& local_target_apk_path,
+ const android::StringPiece& local_overlay_apk_path,
+ const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies,
+ bool enforce_overlayable) {
+ const std::string target_apk_path(GetTestDataPath() + local_target_apk_path.data());
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ if (!target_apk) {
+ return Error(R"(Failed to load target apk "%s")", target_apk_path.data());
+ }
+
+ const std::string overlay_apk_path(GetTestDataPath() + local_overlay_apk_path.data());
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ if (!overlay_apk) {
+ return Error(R"(Failed to load overlay apk "%s")", overlay_apk_path.data());
+ }
+
+ return ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, overlay_info, fulfilled_policies,
+ enforce_overlayable);
+}
+
+Result<ResourceMapping> TestGetResourceMapping(const android::StringPiece& local_target_apk_path,
+ const android::StringPiece& local_overlay_apk_path,
+ const PolicyBitmask& fulfilled_policies,
+ bool enforce_overlayable) {
+ auto overlay_info = ExtractOverlayManifestInfo(GetTestDataPath() + local_overlay_apk_path.data());
+ if (!overlay_info) {
+ return overlay_info.GetError();
+ }
+ return TestGetResourceMapping(local_target_apk_path, local_overlay_apk_path, *overlay_info,
+ fulfilled_policies, enforce_overlayable);
+}
+
+Result<Unit> MappingExists(const ResourceMapping& mapping, const ResourceId& target_resource,
+ const uint8_t type, const uint32_t value, bool rewrite) {
+ auto target_map = mapping.GetTargetToOverlayMap();
+ auto entry_map = target_map.find(target_resource);
+ if (entry_map == target_map.end()) {
+ return Error("Failed to find mapping for target resource");
+ }
+
+ if (entry_map->second.data_type != type) {
+ return Error(R"(Expected type: "0x%02x" Actual type: "0x%02x")", type,
+ entry_map->second.data_type);
+ }
+
+ if (entry_map->second.data_value != value) {
+ return Error(R"(Expected value: "0x%08x" Actual value: "0x%08x")", type,
+ entry_map->second.data_value);
+ }
+
+ auto overlay_map = mapping.GetOverlayToTargetMap();
+ auto overlay_iter = overlay_map.find(entry_map->second.data_value);
+ if ((overlay_iter != overlay_map.end()) != rewrite) {
+ return Error(R"(Expected rewriting: "%s")", rewrite ? "true" : "false");
+ }
+
+ return Result<Unit>({});
+}
+
+TEST(ResourceMappingTests, ResourcesFromApkAssetsLegacy) {
+ OverlayManifestInfo info{};
+ info.target_package = "test.target";
+ info.target_name = "TestResources";
+ info.resource_mapping = 0U; // no xml
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
+ PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
+ ASSERT_RESULT(MappingExists(res, 0x7f010000, Res_value::TYPE_REFERENCE, 0x7f010000,
+ false /* rewrite */)); // integer/int1
+ ASSERT_RESULT(MappingExists(res, 0x7f02000c, Res_value::TYPE_REFERENCE, 0x7f020000,
+ false /* rewrite */)); // string/str1
+ ASSERT_RESULT(MappingExists(res, 0x7f02000e, Res_value::TYPE_REFERENCE, 0x7f020001,
+ false /* rewrite */)); // string/str3
+ ASSERT_RESULT(MappingExists(res, 0x7f02000f, Res_value::TYPE_REFERENCE, 0x7f020002,
+ false /* rewrite */)); // string/str4
+}
+
+TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) {
+ OverlayManifestInfo info{};
+ info.target_package = "test.target";
+ info.target_name = "TestResources";
+ info.resource_mapping = 0x7f030003; // xml/overlays_swap
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
+ PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
+ ASSERT_RESULT(MappingExists(res, 0x7f02000c, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020002,
+ true /* rewrite */)); // string/str1 -> string/str4
+ ASSERT_RESULT(MappingExists(res, 0x7f02000e, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020000,
+ true /* rewrite */)); // string/str3 -> string/str1
+ ASSERT_RESULT(MappingExists(res, 0x7f02000f, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020001,
+ true /* rewrite */)); // string/str4 -> string/str3
+}
+
+TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) {
+ OverlayManifestInfo info{};
+ info.target_package = "test.target";
+ info.target_name = "TestResources";
+ info.resource_mapping = 0x7f030001; // xml/overlays_different_packages
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
+ PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
+ ASSERT_EQ(res.GetOverlayToTargetMap().size(), 1U);
+ ASSERT_RESULT(MappingExists(res, 0x7f02000c, Res_value::TYPE_REFERENCE, 0x0104000a,
+ false /* rewrite */)); // string/str1 -> android:string/ok
+ ASSERT_RESULT(MappingExists(res, 0x7f02000e, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020001,
+ true /* rewrite */)); // string/str3 -> string/str4
+}
+
+TEST(ResourceMappingTests, InlineResources) {
+ OverlayManifestInfo info{};
+ info.target_package = "test.target";
+ info.target_name = "TestResources";
+ info.resource_mapping = 0x7f030002; // xml/overlays_inline
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
+ PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ false);
+
+ constexpr size_t overlay_string_pool_size = 8U;
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
+ ASSERT_EQ(res.GetOverlayToTargetMap().size(), 0U);
+ ASSERT_RESULT(MappingExists(res, 0x7f02000c, Res_value::TYPE_STRING,
+ overlay_string_pool_size + 0U,
+ false /* rewrite */)); // string/str1 -> "Hello World"
+ ASSERT_RESULT(MappingExists(res, 0x7f010000, Res_value::TYPE_INT_DEC, 73U,
+ false /* rewrite */)); // string/str1 -> "Hello World"
+}
+
+TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
+ auto resources =
+ TestGetResourceMapping("/target/target.apk", "/system-overlay/system-overlay.apk",
+ PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ true);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
+ ASSERT_RESULT(MappingExists(res, 0x7f020008, Res_value::TYPE_REFERENCE, 0x7f010000,
+ false /* rewrite */)); // string/policy_public
+ ASSERT_RESULT(MappingExists(res, 0x7f02000a, Res_value::TYPE_REFERENCE, 0x7f010001,
+ false /* rewrite */)); // string/policy_system
+ ASSERT_RESULT(MappingExists(res, 0x7f02000b, Res_value::TYPE_REFERENCE, 0x7f010002,
+ false /* rewrite */)); // string/policy_system_vendor
+}
+
+// Resources that are not declared as overlayable and resources that a protected by policies the
+// overlay does not fulfill must not map to overlay resources.
+TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
+ auto resources = TestGetResourceMapping(
+ "/target/target.apk", "/system-overlay-invalid/system-overlay-invalid.apk",
+ PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ true);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
+ ASSERT_RESULT(MappingExists(res, 0x7f020008, Res_value::TYPE_REFERENCE, 0x7f010005,
+ false /* rewrite */)); // string/policy_public
+ ASSERT_RESULT(MappingExists(res, 0x7f02000a, Res_value::TYPE_REFERENCE, 0x7f010007,
+ false /* rewrite */)); // string/policy_system
+ ASSERT_RESULT(MappingExists(res, 0x7f02000b, Res_value::TYPE_REFERENCE, 0x7f010008,
+ false /* rewrite */)); // string/policy_system_vendor
+}
+
+// Resources that are not declared as overlayable and resources that a protected by policies the
+// overlay does not fulfilled can map to overlay resources when overlayable enforcement is turned
+// off.
+TEST(ResourceMappingTests, ResourcesFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) {
+ auto resources = TestGetResourceMapping(
+ "/target/target.apk", "/system-overlay-invalid/system-overlay-invalid.apk",
+ PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 9U);
+ ASSERT_RESULT(MappingExists(res, 0x7f020003, Res_value::TYPE_REFERENCE, 0x7f010000,
+ false /* rewrite */)); // string/not_overlayable
+ ASSERT_RESULT(MappingExists(res, 0x7f020004, Res_value::TYPE_REFERENCE, 0x7f010001,
+ false /* rewrite */)); // string/other
+ ASSERT_RESULT(MappingExists(res, 0x7f020005, Res_value::TYPE_REFERENCE, 0x7f010002,
+ false /* rewrite */)); // string/policy_odm
+ ASSERT_RESULT(MappingExists(res, 0x7f020006, Res_value::TYPE_REFERENCE, 0x7f010003,
+ false /* rewrite */)); // string/policy_oem
+ ASSERT_RESULT(MappingExists(res, 0x7f020007, Res_value::TYPE_REFERENCE, 0x7f010004,
+ false /* rewrite */)); // string/policy_product
+ ASSERT_RESULT(MappingExists(res, 0x7f020008, Res_value::TYPE_REFERENCE, 0x7f010005,
+ false /* rewrite */)); // string/policy_public
+ ASSERT_RESULT(MappingExists(res, 0x7f020009, Res_value::TYPE_REFERENCE, 0x7f010006,
+ false /* rewrite */)); // string/policy_signature
+ ASSERT_RESULT(MappingExists(res, 0x7f02000a, Res_value::TYPE_REFERENCE, 0x7f010007,
+ false /* rewrite */)); // string/policy_system
+ ASSERT_RESULT(MappingExists(res, 0x7f02000b, Res_value::TYPE_REFERENCE, 0x7f010008,
+ false /* rewrite */)); // string/policy_system_vendor
+}
+
+// Overlays that do not target an <overlayable> tag can overlay resources defined within any
+// <overlayable> tag.
+TEST(ResourceMappingTests, ResourcesFromApkAssetsNoDefinedOverlayableAndNoTargetName) {
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay-no-name.apk",
+ PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
+ ASSERT_RESULT(MappingExists(res, 0x7f010000, Res_value::TYPE_REFERENCE, 0x7f010000,
+ false /* rewrite */)); // integer/int1
+ ASSERT_RESULT(MappingExists(res, 0x7f02000c, Res_value::TYPE_REFERENCE, 0x7f020000,
+ false /* rewrite */)); // string/str1
+ ASSERT_RESULT(MappingExists(res, 0x7f02000e, Res_value::TYPE_REFERENCE, 0x7f020001,
+ false /* rewrite */)); // string/str3
+ ASSERT_RESULT(MappingExists(res, 0x7f02000f, Res_value::TYPE_REFERENCE, 0x7f020002,
+ false /* rewrite */)); // string/str4
+}
+
+// Overlays that are neither pre-installed nor signed with the same signature as the target cannot
+// overlay packages that have not defined overlayable resources.
+TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPoliciesPublicFail) {
+ auto resources =
+ TestGetResourceMapping("/target/target-no-overlayable.apk", "/overlay/overlay-no-name.apk",
+ PolicyFlags::POLICY_PUBLIC,
+ /* enforce_overlayable */ true);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 0U);
+}
+
+// Overlays that are pre-installed or are signed with the same signature as the target can overlay
+// packages that have not defined overlayable resources.
+TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPolicies) {
+ auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) -> void {
+ auto resources = TestGetResourceMapping("/target/target-no-overlayable.apk",
+ "/system-overlay-invalid/system-overlay-invalid.apk",
+ fulfilled_policies,
+ /* enforce_overlayable */ true);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 9U);
+ ASSERT_RESULT(MappingExists(res, 0x7f020003, Res_value::TYPE_REFERENCE, 0x7f010000,
+ false /* rewrite */)); // string/not_overlayable
+ ASSERT_RESULT(MappingExists(res, 0x7f020004, Res_value::TYPE_REFERENCE, 0x7f010001,
+ false /* rewrite */)); // string/other
+ ASSERT_RESULT(MappingExists(res, 0x7f020005, Res_value::TYPE_REFERENCE, 0x7f010002,
+ false /* rewrite */)); // string/policy_odm
+ ASSERT_RESULT(MappingExists(res, 0x7f020006, Res_value::TYPE_REFERENCE, 0x7f010003,
+ false /* rewrite */)); // string/policy_oem
+ ASSERT_RESULT(MappingExists(res, 0x7f020007, Res_value::TYPE_REFERENCE, 0x7f010004,
+ false /* rewrite */)); // string/policy_product
+ ASSERT_RESULT(MappingExists(res, 0x7f020008, Res_value::TYPE_REFERENCE, 0x7f010005,
+ false /* rewrite */)); // string/policy_public
+ ASSERT_RESULT(MappingExists(res, 0x7f020009, Res_value::TYPE_REFERENCE, 0x7f010006,
+ false /* rewrite */)); // string/policy_signature
+ ASSERT_RESULT(MappingExists(res, 0x7f02000a, Res_value::TYPE_REFERENCE, 0x7f010007,
+ false /* rewrite */)); // string/policy_system
+ ASSERT_RESULT(MappingExists(res, 0x7f02000b, Res_value::TYPE_REFERENCE, 0x7f010008,
+ false /* rewrite */)); // string/policy_system_vendor
+ };
+
+ CheckEntries(PolicyFlags::POLICY_SIGNATURE);
+ CheckEntries(PolicyFlags::POLICY_PRODUCT_PARTITION);
+ CheckEntries(PolicyFlags::POLICY_SYSTEM_PARTITION);
+ CheckEntries(PolicyFlags::POLICY_VENDOR_PARTITION);
+ CheckEntries(PolicyFlags::POLICY_ODM_PARTITION);
+ CheckEntries(PolicyFlags::POLICY_OEM_PARTITION);
+}
+
+} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h
index a7c2f284e7c5..8868b5376796 100644
--- a/cmds/idmap2/tests/TestHelpers.h
+++ b/cmds/idmap2/tests/TestHelpers.h
@@ -30,7 +30,7 @@ const unsigned char idmap_raw_data[] = {
0x49, 0x44, 0x4d, 0x50,
// 0x4: version
- 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00,
// 0x8: target crc
0x34, 0x12, 0x00, 0x00,
@@ -38,8 +38,8 @@ const unsigned char idmap_raw_data[] = {
// 0xc: overlay crc
0x78, 0x56, 0x00, 0x00,
- // 0x10: target path "target.apk"
- 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ // 0x10: target path "targetX.apk"
+ 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x58, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -56,8 +56,8 @@ const unsigned char idmap_raw_data[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- // 0x110: overlay path "overlay.apk"
- 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00,
+ // 0x110: overlay path "overlayX.apk"
+ 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x58, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -75,49 +75,63 @@ const unsigned char idmap_raw_data[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// DATA HEADER
- // 0x210: target package id
- 0x7f, 0x00,
+ // 0x210: target_package_id
+ 0x7f,
- // 0x212: types count
- 0x02, 0x00,
+ // 0x211: overlay_package_id
+ 0x7f,
- // DATA BLOCK
- // 0x214: target type
- 0x02, 0x00,
+ // 0x212: target_entry_count
+ 0x03, 0x00, 0x00, 0x00,
- // 0x216: overlay type
- 0x02, 0x00,
+ // 0x216: overlay_entry_count
+ 0x03, 0x00, 0x00, 0x00,
- // 0x218: entry count
- 0x01, 0x00,
-
- // 0x21a: entry offset
- 0x00, 0x00,
+ // 0x21a: string_pool_offset
+ 0x00, 0x00, 0x00, 0x00,
- // 0x21c: entries
+ // 0x21e: string_pool_byte_length
0x00, 0x00, 0x00, 0x00,
- // DATA BLOCK
- // 0x220: target type
- 0x03, 0x00,
+ // TARGET ENTRIES
+ // 0x222: 0x7f020000
+ 0x00, 0x00, 0x02, 0x7f,
- // 0x222: overlay type
- 0x03, 0x00,
+ // 0x226: TYPE_REFERENCE
+ 0x01,
- // 0x224: entry count
- 0x03, 0x00,
+ // 0x227: 0x7f020000
+ 0x00, 0x00, 0x02, 0x7f,
- // 0x226: entry offset
- 0x03, 0x00,
+ // 0x22b: 0x7f030000
+ 0x00, 0x00, 0x03, 0x7f,
- // 0x228, 0x22c, 0x230: entries
- 0x00, 0x00, 0x00, 0x00,
+ // 0x22f: TYPE_REFERENCE
+ 0x01,
+
+ // 0x230: 0x7f030000
+ 0x00, 0x00, 0x03, 0x7f,
+
+ // 0x234: 0x7f030002
+ 0x02, 0x00, 0x03, 0x7f,
+
+ // 0x238: TYPE_REFERENCE
+ 0x01,
+
+ // 0x239: 0x7f030001
+ 0x01, 0x00, 0x03, 0x7f,
+
+ // OVERLAY ENTRIES
+ // 0x23d: 0x7f020000 -> 0x7f020000
+ 0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f,
- 0xff, 0xff, 0xff, 0xff,
+ // 0x245: 0x7f030000 -> 0x7f030000
+ 0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f,
- 0x01, 0x00, 0x00, 0x00};
+ // 0x24d: 0x7f030001 -> 0x7f030002
+ 0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f};
-const unsigned int idmap_raw_data_len = 565;
+const unsigned int idmap_raw_data_len = 0x255;
std::string GetTestDataPath();
diff --git a/cmds/idmap2/tests/XmlParserTests.cpp b/cmds/idmap2/tests/XmlParserTests.cpp
new file mode 100644
index 000000000000..1a7eaca4d67b
--- /dev/null
+++ b/cmds/idmap2/tests/XmlParserTests.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdio> // fclose
+#include <memory>
+#include <string>
+
+#include "TestHelpers.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "idmap2/XmlParser.h"
+#include "idmap2/ZipFile.h"
+
+namespace android::idmap2 {
+
+Result<std::unique_ptr<const XmlParser>> CreateTestParser(const std::string& test_file) {
+ auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
+ if (zip == nullptr) {
+ return Error("Failed to open zip file");
+ }
+
+ auto data = zip->Uncompress(test_file);
+ if (data == nullptr) {
+ return Error("Failed to open xml file");
+ }
+
+ return XmlParser::Create(data->buf, data->size, /* copy_data */ true);
+}
+
+TEST(XmlParserTests, Create) {
+ auto xml = CreateTestParser("AndroidManifest.xml");
+ ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+ fclose(stderr); // silence expected warnings from libandroidfw
+ const char* not_xml = "foo";
+ auto fail = XmlParser::Create(reinterpret_cast<const uint8_t*>(not_xml), strlen(not_xml));
+ ASSERT_FALSE(fail);
+}
+
+TEST(XmlParserTests, NextChild) {
+ auto xml = CreateTestParser("res/xml/test.xml");
+ ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+ auto root_iter = (*xml)->tree_iterator();
+ ASSERT_EQ(root_iter->event(), XmlParser::Event::START_TAG);
+ ASSERT_EQ(root_iter->name(), "a");
+
+ auto a_iter = root_iter.begin();
+ ASSERT_EQ(a_iter->event(), XmlParser::Event::START_TAG);
+ ASSERT_EQ(a_iter->name(), "b");
+
+ auto c_iter = a_iter.begin();
+ ASSERT_EQ(c_iter->event(), XmlParser::Event::START_TAG);
+ ASSERT_EQ(c_iter->name(), "c");
+
+ ++c_iter;
+ ASSERT_EQ(c_iter->event(), XmlParser::Event::END_TAG);
+ ASSERT_EQ(c_iter, a_iter.end());
+
+ ++a_iter;
+ ASSERT_EQ(a_iter->event(), XmlParser::Event::START_TAG);
+ ASSERT_EQ(a_iter->name(), "d");
+
+ // Skip the <e> tag.
+ ++a_iter;
+ ASSERT_EQ(a_iter->event(), XmlParser::Event::END_TAG);
+ ASSERT_EQ(a_iter, root_iter.end());
+}
+
+TEST(XmlParserTests, AttributeValues) {
+ auto xml = CreateTestParser("res/xml/test.xml");
+ ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+ // Start at the <a> tag.
+ auto root_iter = (*xml)->tree_iterator();
+
+ // Start at the <b> tag.
+ auto a_iter = root_iter.begin();
+ auto attribute_str = a_iter->GetAttributeStringValue("type_string");
+ ASSERT_TRUE(attribute_str);
+ ASSERT_EQ(*attribute_str, "fortytwo");
+
+ auto attribute_value = a_iter->GetAttributeValue("type_int_dec");
+ ASSERT_TRUE(attribute_value);
+ ASSERT_EQ(attribute_value->data, 42);
+
+ attribute_value = a_iter->GetAttributeValue("type_int_hex");
+ ASSERT_TRUE(attribute_value);
+ ASSERT_EQ(attribute_value->data, 42);
+
+ attribute_value = a_iter->GetAttributeValue("type_int_boolean");
+ ASSERT_TRUE(attribute_value);
+ ASSERT_EQ(attribute_value->data, 0xffffffff);
+}
+
+TEST(XmlParserTests, IteratorEquality) {
+ auto xml = CreateTestParser("res/xml/test.xml");
+ ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+ // Start at the <a> tag.
+ auto root_iter_1 = (*xml)->tree_iterator();
+ auto root_iter_2 = (*xml)->tree_iterator();
+ ASSERT_EQ(root_iter_1, root_iter_2);
+ ASSERT_EQ(*root_iter_1, *root_iter_2);
+
+ // Start at the <b> tag.
+ auto a_iter_1 = root_iter_1.begin();
+ auto a_iter_2 = root_iter_2.begin();
+ ASSERT_NE(a_iter_1, root_iter_1.end());
+ ASSERT_NE(a_iter_2, root_iter_2.end());
+ ASSERT_EQ(a_iter_1, a_iter_2);
+ ASSERT_EQ(*a_iter_1, *a_iter_2);
+
+ // Move to the <d> tag.
+ ++a_iter_1;
+ ++a_iter_2;
+ ASSERT_NE(a_iter_1, root_iter_1.end());
+ ASSERT_NE(a_iter_2, root_iter_2.end());
+ ASSERT_EQ(a_iter_1, a_iter_2);
+ ASSERT_EQ(*a_iter_1, *a_iter_2);
+
+ // Move to the end of the <a> tag.
+ ++a_iter_1;
+ ++a_iter_2;
+ ASSERT_EQ(a_iter_1, root_iter_1.end());
+ ASSERT_EQ(a_iter_2, root_iter_2.end());
+ ASSERT_EQ(a_iter_1, a_iter_2);
+ ASSERT_EQ(*a_iter_1, *a_iter_2);
+}
+
+TEST(XmlParserTests, Backtracking) {
+ auto xml = CreateTestParser("res/xml/test.xml");
+ ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+ // Start at the <a> tag.
+ auto root_iter_1 = (*xml)->tree_iterator();
+
+ // Start at the <b> tag.
+ auto a_iter_1 = root_iter_1.begin();
+
+ // Start a second iterator at the <a> tag.
+ auto root_iter_2 = root_iter_1;
+ ASSERT_EQ(root_iter_1, root_iter_2);
+ ASSERT_EQ(*root_iter_1, *root_iter_2);
+
+ // Move the first iterator to the end of the <a> tag.
+ auto root_iter_end_1 = root_iter_1.end();
+ ++root_iter_1;
+ ASSERT_NE(root_iter_1, root_iter_2);
+ ASSERT_NE(*root_iter_1, *root_iter_2);
+
+ // Move to the <d> tag.
+ ++a_iter_1;
+ ASSERT_NE(a_iter_1, root_iter_end_1);
+
+ // Move to the end of the <a> tag.
+ ++a_iter_1;
+ ASSERT_EQ(a_iter_1, root_iter_end_1);
+}
+
+} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/XmlTests.cpp b/cmds/idmap2/tests/XmlTests.cpp
deleted file mode 100644
index df63211a9209..000000000000
--- a/cmds/idmap2/tests/XmlTests.cpp
+++ /dev/null
@@ -1,68 +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 <cstdio> // fclose
-
-#include "TestHelpers.h"
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "idmap2/Xml.h"
-#include "idmap2/ZipFile.h"
-
-using ::testing::IsNull;
-using ::testing::NotNull;
-
-namespace android::idmap2 {
-
-TEST(XmlTests, Create) {
- auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
- ASSERT_THAT(zip, NotNull());
-
- auto data = zip->Uncompress("AndroidManifest.xml");
- ASSERT_THAT(data, NotNull());
-
- auto xml = Xml::Create(data->buf, data->size);
- ASSERT_THAT(xml, NotNull());
-
- fclose(stderr); // silence expected warnings from libandroidfw
- const char* not_xml = "foo";
- auto fail = Xml::Create(reinterpret_cast<const uint8_t*>(not_xml), strlen(not_xml));
- ASSERT_THAT(fail, IsNull());
-}
-
-TEST(XmlTests, FindTag) {
- auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
- ASSERT_THAT(zip, NotNull());
-
- auto data = zip->Uncompress("res/xml/test.xml");
- ASSERT_THAT(data, NotNull());
-
- auto xml = Xml::Create(data->buf, data->size);
- ASSERT_THAT(xml, NotNull());
-
- auto attrs = xml->FindTag("c");
- ASSERT_THAT(attrs, NotNull());
- ASSERT_EQ(attrs->size(), 4U);
- ASSERT_EQ(attrs->at("type_string"), "fortytwo");
- ASSERT_EQ(std::stoi(attrs->at("type_int_dec")), 42);
- ASSERT_EQ(std::stoi(attrs->at("type_int_hex")), 42);
- ASSERT_NE(std::stoul(attrs->at("type_int_boolean")), 0U);
-
- auto fail = xml->FindTag("does-not-exist");
- ASSERT_THAT(fail, IsNull());
-}
-
-} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/data/overlay/AndroidManifest.xml b/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
index 619bb6ce0f25..cf3691c3b3cf 100644
--- a/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
+++ b/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
@@ -16,8 +16,11 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="test.overlay">
+
<application android:hasCode="false"/>
+
<overlay
android:targetPackage="test.target"
- android:targetName="TestResources"/>
+ android:targetName="TestResources"
+ android:resourcesMap="@xml/overlays"/>
</manifest>
diff --git a/cmds/idmap2/tests/data/overlay/build b/cmds/idmap2/tests/data/overlay/build
index 68b9f507a11d..b921b0d3d3ad 100755
--- a/cmds/idmap2/tests/data/overlay/build
+++ b/cmds/idmap2/tests/data/overlay/build
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-FRAMEWORK_RES_APK=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/public/android.jar
+FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk
aapt2 compile --dir res -o compiled.flata
diff --git a/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk b/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk
index 18ee43dc57a4..7c25985e5a61 100644
--- a/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-no-name.apk b/cmds/idmap2/tests/data/overlay/overlay-no-name.apk
index 642519008b15..c75f3e1dbddf 100644
--- a/cmds/idmap2/tests/data/overlay/overlay-no-name.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay-no-name.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-static-1.apk b/cmds/idmap2/tests/data/overlay/overlay-static-1.apk
index 642ab90d00ae..5b8a6e4a90ed 100644
--- a/cmds/idmap2/tests/data/overlay/overlay-static-1.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay-static-1.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-static-2.apk b/cmds/idmap2/tests/data/overlay/overlay-static-2.apk
index 2ec56020c4aa..698a1fd6e702 100644
--- a/cmds/idmap2/tests/data/overlay/overlay-static-2.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay-static-2.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay.apk b/cmds/idmap2/tests/data/overlay/overlay.apk
index 5842da4f432e..1db303ff05b5 100644
--- a/cmds/idmap2/tests/data/overlay/overlay.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays.xml
new file mode 100644
index 000000000000..edd33f7dc90d
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+<overlay>
+ <item target="string/str1" value="@string/str1"/>
+ <item target="string/str3" value="@string/str3" />
+ <item target="string/str4" value="@string/str4" />
+ <item target="integer/int1" value="@integer/int1" />
+ <item target="integer/not_in_target" value="@integer/not_in_target" />
+</overlay>
+
diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays_different_package.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays_different_package.xml
new file mode 100644
index 000000000000..aa7fefaa305e
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays_different_package.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<overlay>
+ <item target="string/str1" value="@android:string/ok"/>
+ <item target="string/str3" value="@string/str3" />
+</overlay>
+
diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays_inline.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays_inline.xml
new file mode 100644
index 000000000000..e12b823ff50d
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays_inline.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<overlay>
+ <item target="string/str1" value="Hello World"/>
+ <item target="integer/int1" value="73" />
+</overlay>
+
diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays_swap.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays_swap.xml
new file mode 100644
index 000000000000..5728e672d94a
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays_swap.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<overlay>
+ <item target="string/str1" value="@string/str4"/>
+ <item target="string/str3" value="@string/str1" />
+ <item target="string/str4" value="@string/str3" />
+ <item target="integer/int_not_in_target" value="@integer/int1" />
+</overlay>
diff --git a/cmds/idmap2/tests/data/target/res/xml/test.xml b/cmds/idmap2/tests/data/target/res/xml/test.xml
index 0fe21c6b6d0a..56a3f7f0b13a 100644
--- a/cmds/idmap2/tests/data/target/res/xml/test.xml
+++ b/cmds/idmap2/tests/data/target/res/xml/test.xml
@@ -14,12 +14,15 @@
limitations under the License.
-->
<a>
- <b>
- <c
- type_string="fortytwo"
- type_int_dec="42"
- type_int_hex="0x2a"
- type_int_boolean="true"
- />
+ <b type_string="fortytwo"
+ type_int_dec="42"
+ type_int_hex="0x2a"
+ type_int_boolean="true">
+
+ <c />
</b>
-</a>
+
+ <d>
+ <e />
+ </d>
+</a> \ No newline at end of file
diff --git a/cmds/idmap2/tests/data/target/target-no-overlayable.apk b/cmds/idmap2/tests/data/target/target-no-overlayable.apk
index 033305aaed4f..2eb7c477c3b4 100644
--- a/cmds/idmap2/tests/data/target/target-no-overlayable.apk
+++ b/cmds/idmap2/tests/data/target/target-no-overlayable.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/target/target.apk b/cmds/idmap2/tests/data/target/target.apk
index 9bcd6dcabcde..251cf46f969d 100644
--- a/cmds/idmap2/tests/data/target/target.apk
+++ b/cmds/idmap2/tests/data/target/target.apk
Binary files differ
diff --git a/cmds/idmap2/valgrind.sh b/cmds/idmap2/valgrind.sh
new file mode 100755
index 000000000000..b4ebab0c7ffe
--- /dev/null
+++ b/cmds/idmap2/valgrind.sh
@@ -0,0 +1,59 @@
+#!/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.
+#
+
+function _log()
+{
+ echo -e "$*" >&2
+}
+
+function _eval()
+{
+ local label="$1"
+ local cmd="$2"
+ local red="\e[31m"
+ local green="\e[32m"
+ local reset="\e[0m"
+ local output
+
+ _log "${green}[ RUN ]${reset} ${label}"
+ output="$(eval "$cmd" 2>&1)"
+ if [[ $? -eq 0 ]]; then
+ _log "${green}[ OK ]${reset} ${label}"
+ return 0
+ else
+ echo "${output}"
+ _log "${red}[ FAILED ]${reset} ${label}"
+ errors=$((errors + 1))
+ return 1
+ fi
+}
+
+errors=0
+script="$(readlink -f "$BASH_SOURCE")"
+prefix="$(dirname "$script")"
+target_path="${prefix}/tests/data/target/target.apk"
+overlay_path="${prefix}/tests/data/overlay/overlay.apk"
+idmap_path="/tmp/a.idmap"
+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/locksettings/TEST_MAPPING b/cmds/locksettings/TEST_MAPPING
index c1cba5f7f22d..56f5cc034f05 100644
--- a/cmds/locksettings/TEST_MAPPING
+++ b/cmds/locksettings/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "presubmit": [
+ "presubmit-devicepolicy": [
{
"name": "CtsDevicePolicyManagerTestCases",
"options": [
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 43058d538552..8af925aa7a63 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -74,6 +74,7 @@ cc_defaults {
"src/external/StatsPuller.cpp",
"src/external/StatsPullerManager.cpp",
"src/external/SubsystemSleepStatePuller.cpp",
+ "src/external/SurfaceflingerStatsPuller.cpp",
"src/external/TrainInfoPuller.cpp",
"src/FieldValue.cpp",
"src/guardrail/StatsdStats.cpp",
@@ -99,6 +100,8 @@ cc_defaults {
"src/shell/shell_config.proto",
"src/shell/ShellSubscriber.cpp",
"src/socket/StatsSocketListener.cpp",
+ "src/state/StateManager.cpp",
+ "src/state/StateTracker.cpp",
"src/stats_log_util.cpp",
"src/statscompanion_util.cpp",
"src/statsd_config.proto",
@@ -135,7 +138,9 @@ cc_defaults {
"libprotoutil",
"libservices",
"libstatslog",
+ "libstatssocket",
"libsysutils",
+ "libtimestats_proto",
"libutils",
],
}
@@ -225,6 +230,7 @@ cc_test {
"tests/e2e/Anomaly_duration_sum_e2e_test.cpp",
"tests/e2e/Attribution_e2e_test.cpp",
"tests/e2e/ConfigTtl_e2e_test.cpp",
+ "tests/e2e/CountMetric_e2e_test.cpp",
"tests/e2e/DurationMetric_e2e_test.cpp",
"tests/e2e/GaugeMetric_e2e_pull_test.cpp",
"tests/e2e/GaugeMetric_e2e_push_test.cpp",
@@ -237,6 +243,7 @@ cc_test {
"tests/external/IncidentReportArgs_test.cpp",
"tests/external/puller_util_test.cpp",
"tests/external/StatsPuller_test.cpp",
+ "tests/external/SurfaceflingerStatsPuller_test.cpp",
"tests/FieldValue_test.cpp",
"tests/guardrail/StatsdStats_test.cpp",
"tests/indexed_priority_queue_test.cpp",
@@ -253,6 +260,7 @@ cc_test {
"tests/metrics/ValueMetricProducer_test.cpp",
"tests/MetricsManager_test.cpp",
"tests/shell/ShellSubscriber_test.cpp",
+ "tests/state/StateTracker_test.cpp",
"tests/statsd_test_util.cpp",
"tests/StatsLogProcessor_test.cpp",
"tests/StatsService_test.cpp",
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index af8b3af6ea61..5e156bb26caa 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -59,6 +59,16 @@ android::hash_t hashDimension(const HashableDimensionKey& value) {
return JenkinsHashWhiten(hash);
}
+bool filterValues(const Matcher& matcherField, const vector<FieldValue>& values, Value* output) {
+ for (const auto& value : values) {
+ if (value.mField.matches(matcherField)) {
+ (*output) = value.mValue;
+ return true;
+ }
+ }
+ return false;
+}
+
bool filterValues(const vector<Matcher>& matcherFields, const vector<FieldValue>& values,
HashableDimensionKey* output) {
size_t num_matches = 0;
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 6f4941f717ee..a12385057585 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -120,6 +120,13 @@ class MetricDimensionKey {
android::hash_t hashDimension(const HashableDimensionKey& key);
/**
+ * Returns true if a FieldValue field matches the matcher field.
+ * The value of the FieldValue is output.
+ */
+bool filterValues(const Matcher& matcherField, const std::vector<FieldValue>& values,
+ Value* output);
+
+/**
* Creating HashableDimensionKeys from FieldValues using matcher.
*
* This function may make modifications to the Field if the matcher has Position=FIRST,LAST or ALL
@@ -169,4 +176,4 @@ struct hash<MetricDimensionKey> {
return android::JenkinsHashWhiten(hash);
}
};
-} // namespace std \ No newline at end of file
+} // namespace std
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index ff7416c4b9e0..6c3dff24e5fe 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -16,24 +16,26 @@
#define DEBUG false // STOPSHIP if true
#include "Log.h"
-#include "statslog.h"
+
+#include "StatsLogProcessor.h"
#include <android-base/file.h>
#include <dirent.h>
#include <frameworks/base/cmds/statsd/src/active_config_list.pb.h>
-#include "StatsLogProcessor.h"
+#include <log/log_event_list.h>
+#include <utils/Errors.h>
+#include <utils/SystemClock.h>
+
#include "android-base/stringprintf.h"
#include "external/StatsPullerManager.h"
#include "guardrail/StatsdStats.h"
#include "metrics/CountMetricProducer.h"
+#include "state/StateManager.h"
#include "stats_log_util.h"
#include "stats_util.h"
+#include "statslog.h"
#include "storage/StorageManager.h"
-#include <log/log_event_list.h>
-#include <utils/Errors.h>
-#include <utils/SystemClock.h>
-
using namespace android;
using android::base::StringPrintf;
using android::util::FIELD_COUNT_REPEATED;
@@ -218,6 +220,8 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) {
onIsolatedUidChangedEventLocked(*event);
}
+ StateManager::getInstance().onLogEvent(*event);
+
if (mMetricsManagers.empty()) {
return;
}
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index f0db1b0128a1..8292a3a9194a 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -248,19 +248,6 @@ private:
FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation);
- FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_Link_OR_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_OR_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_OR_CombinationCondition);
-
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_SimpleCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_SimpleCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_SimpleCondition);
-
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_AND_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_AND_CombinationCondition);
-
FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket);
FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
@@ -275,6 +262,10 @@ private:
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
+ FRIEND_TEST(CountMetricE2eTest, TestWithSimpleState);
+ FRIEND_TEST(CountMetricE2eTest, TestWithMappedState);
+ FRIEND_TEST(CountMetricE2eTest, TestWithMultipleStates);
+
FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 1c7180ffbde1..b665a8b99fbd 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -266,7 +266,9 @@ status_t StatsService::onTransact(uint32_t code, const Parcel& data, Parcel* rep
IResultReceiver::asInterface(data.readStrongBinder());
err = command(in, out, err, args, resultReceiver);
- resultReceiver->send(err);
+ if (resultReceiver != nullptr) {
+ resultReceiver->send(err);
+ }
return NO_ERROR;
}
default: { return BnStatsManager::onTransact(code, data, reply, flags); }
@@ -411,13 +413,20 @@ status_t StatsService::command(int in, int out, int err, Vector<String8>& args,
return cmd_trigger_active_config_broadcast(out, args);
}
if (!args[0].compare(String8("data-subscribe"))) {
- if (mShellSubscriber == nullptr) {
- mShellSubscriber = new ShellSubscriber(mUidMap, mPullerManager);
+ {
+ std::lock_guard<std::mutex> lock(mShellSubscriberMutex);
+ if (mShellSubscriber == nullptr) {
+ mShellSubscriber = new ShellSubscriber(mUidMap, mPullerManager);
+ }
}
int timeoutSec = -1;
if (argCount >= 2) {
timeoutSec = atoi(args[1].c_str());
}
+ if (resultReceiver == nullptr) {
+ ALOGI("Null resultReceiver given, no subscription will be started");
+ return UNEXPECTED_NULL;
+ }
mShellSubscriber->startNewSubscription(in, out, resultReceiver, timeoutSec);
return NO_ERROR;
}
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 53b6ce989195..949094871936 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -432,6 +432,10 @@ private:
sp<ShellSubscriber> mShellSubscriber;
+ /**
+ * Mutex for setting the shell subscriber
+ */
+ mutable mutex mShellSubscriberMutex;
std::shared_ptr<LogEventQueue> mEventQueue;
FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp
index e09d5751d323..4c30c4cb223c 100644
--- a/cmds/statsd/src/anomaly/subscriber_util.cpp
+++ b/cmds/statsd/src/anomaly/subscriber_util.cpp
@@ -40,7 +40,7 @@ void triggerSubscribers(int64_t ruleId, int64_t metricId, const MetricDimensionK
for (const Subscription& subscription : subscriptions) {
if (subscription.probability_of_informing() < 1
- && ((float)rand() / RAND_MAX) >= subscription.probability_of_informing()) {
+ && ((float)rand() / (float)RAND_MAX) >= subscription.probability_of_informing()) {
// Note that due to float imprecision, 0.0 and 1.0 might not truly mean never/always.
// The config writer was advised to use -0.1 and 1.1 for never/always.
ALOGI("Fate decided that a subscriber would not be informed.");
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 78a6609e5e0d..8618d4ded817 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -340,7 +340,7 @@ message Atom {
}
// Pulled events will start at field 10000.
- // Next: 10064
+ // Next: 10065
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000;
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -378,7 +378,6 @@ message Atom {
PowerProfile power_profile = 10033;
ProcStatsPkgProc proc_stats_pkg_proc = 10034;
ProcessCpuTime process_cpu_time = 10035;
- NativeProcessMemoryState native_process_memory_state = 10036;
CpuTimePerThreadFreq cpu_time_per_thread_freq = 10037;
OnDevicePowerMeasurement on_device_power_measurement = 10038;
DeviceCalculatedPowerUse device_calculated_power_use = 10039;
@@ -406,11 +405,14 @@ message Atom {
ProcessSystemIonHeapSize process_system_ion_heap_size = 10061;
SurfaceflingerStatsGlobalInfo surfaceflinger_stats_global_info = 10062;
SurfaceflingerStatsLayerInfo surfaceflinger_stats_layer_info = 10063;
+ ProcessMemorySnapshot process_memory_snapshot = 10064;
}
// DO NOT USE field numbers above 100,000 in AOSP.
// Field numbers 100,000 - 199,999 are reserved for non-AOSP (e.g. OEMs) to use.
// Field numbers 200,000 and above are reserved for future use; do not use them at all.
+
+ reserved 10036;
}
/**
@@ -3069,9 +3071,9 @@ message PictureInPictureStateChanged {
* services/core/java/com/android/server/wm/Session.java
*/
message OverlayStateChanged {
- optional int32 uid = 1 [(is_uid) = true];
+ optional int32 uid = 1 [(state_field_option).option = PRIMARY, (is_uid) = true];
- optional string package_name = 2;
+ optional string package_name = 2 [(state_field_option).option = PRIMARY];
optional bool using_alert_window = 3;
@@ -3079,7 +3081,7 @@ message OverlayStateChanged {
ENTERED = 1;
EXITED = 2;
}
- optional State state = 4;
+ optional State state = 4 [(state_field_option).option = EXCLUSIVE];
}
/*
@@ -4018,8 +4020,8 @@ message ProcessMemoryState {
optional int64 page_major_fault = 5;
// RSS
- // Value is read from /proc/PID/status. Or from memory.stat, field
- // total_rss if per-app memory cgroups are enabled.
+ // Value is read from memory.stat, field total_rss if per-app memory
+ // cgroups are enabled. Otherwise, value from /proc/pid/stat.
optional int64 rss_in_bytes = 6;
// CACHE
@@ -4029,67 +4031,52 @@ message ProcessMemoryState {
// SWAP
// Value is read from memory.stat, field total_swap if per-app memory
- // cgroups are enabled. Otherwise, VmSwap from /proc/PID/status.
+ // cgroups are enabled. Otherwise, 0.
optional int64 swap_in_bytes = 8;
- // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0.
+ // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always -1.
optional int64 rss_high_watermark_in_bytes = 9 [deprecated = true];
- // Elapsed real time when the process started.
- // Value is read from /proc/PID/stat, field 22. 0 if read from per-app memory cgroups.
- optional int64 start_time_nanos = 10;
+ // Deprecated: use ProcessMemorySnapshot atom instead. Always -1.
+ optional int64 start_time_nanos = 10 [deprecated = true];
- // Anonymous page size plus swap size. Values are read from /proc/PID/status.
- optional int32 anon_rss_and_swap_in_kilobytes = 11;
+ // Deprecated: use ProcessMemorySnapshot atom instead. Always -1.
+ optional int32 anon_rss_and_swap_in_kilobytes = 11 [deprecated = true];
}
/*
- * Logs the memory stats for a native process (from procfs).
+ * Logs the memory high-water mark for a process.
+ *
+ * Pulled from StatsCompanionService for all managed processes (from ActivityManagerServie)
+ * and for selected native processes.
*
- * Pulled from StatsCompanionService for selected native processes.
+ * Pulling this atom resets high-water mark counters for all processes.
*/
-message NativeProcessMemoryState {
+message ProcessMemoryHighWaterMark {
// The uid if available. -1 means not available.
optional int32 uid = 1 [(is_uid) = true];
// The process name.
- // Value read from /proc/PID/cmdline.
+ // Usually package name or process cmdline.
+ // Provided by ActivityManagerService or read from /proc/PID/cmdline.
optional string process_name = 2;
- // # of page-faults
- optional int64 page_fault = 3;
-
- // # of major page-faults
- optional int64 page_major_fault = 4;
-
- // RSS
- // Value read from /proc/PID/status.
- optional int64 rss_in_bytes = 5;
-
- // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0.
- optional int64 rss_high_watermark_in_bytes = 6 [deprecated = true];
-
- // Elapsed real time when the process started.
- // Value is read from /proc/PID/stat, field 22.
- optional int64 start_time_nanos = 7;
-
- // SWAP
- // Value read from /proc/PID/status, field VmSwap.
- optional int64 swap_in_bytes = 8;
+ // Deprecated: use rss_high_water_mark_in_kilobytes instead. This field is
+ // computed by converting kilobytes to bytes.
+ optional int64 rss_high_water_mark_in_bytes = 3 [deprecated = true];
- // Anonymous page size plus swap size. Values are read from /proc/PID/status.
- optional int32 anon_rss_and_swap_in_kilobytes = 9;
+ // RSS high-water mark. Peak RSS usage of the process. Read from the VmHWM field in
+ // /proc/PID/status.
+ optional int32 rss_high_water_mark_in_kilobytes = 4;
}
/*
- * Logs the memory high-water mark for a process.
+ * Logs the memory stats for a process.
*
- * Pulled from StatsCompanionService for all managed processes (from ActivityManagerServie)
+ * Pulled from StatsCompanionService for all managed processes (from ActivityManagerService)
* and for selected native processes.
- *
- * Pulling this atom resets high-water mark counters for all processes.
*/
-message ProcessMemoryHighWaterMark {
+message ProcessMemorySnapshot {
// The uid if available. -1 means not available.
optional int32 uid = 1 [(is_uid) = true];
@@ -4098,13 +4085,29 @@ message ProcessMemoryHighWaterMark {
// Provided by ActivityManagerService or read from /proc/PID/cmdline.
optional string process_name = 2;
- // Deprecated: use rss_high_water_mark_in_kilobytes instead. This field is
- // computed by converting kilobytes to bytes.
- optional int64 rss_high_water_mark_in_bytes = 3 [deprecated = true];
+ // The pid of the process.
+ // Allows to disambiguate instances of the process.
+ optional int32 pid = 3;
- // RSS high-water mark. Peak RSS usage of the process. Read from the VmHWM field in
- // /proc/PID/status.
- optional int32 rss_high_water_mark_in_kilobytes = 4;
+ // The current OOM score adjustment value.
+ // Read from ProcessRecord for managed processes.
+ // Placeholder -1001 (OOM_SCORE_ADJ_MIN - 1, outside of allowed range) for native ones.
+ optional int32 oom_score_adj = 4;
+
+ // The current RSS of the process.
+ // VmRSS from /proc/pid/status.
+ optional int32 rss_in_kilobytes = 5;
+
+ // The current anon RSS of the process.
+ // RssAnon from /proc/pid/status.
+ optional int32 anon_rss_in_kilobytes = 6;
+
+ // The current swap size of the process.
+ // VmSwap from /proc/pid/status.
+ optional int32 swap_in_kilobytes = 7;
+
+ // The sum of rss_in_kilobytes and swap_in_kilobytes.
+ optional int32 anon_rss_and_swap_in_kilobytes = 8;
}
/*
@@ -5757,6 +5760,8 @@ message PermissionGrantRequestResultReported {
AUTO_DENIED = 8;
// permission request was ignored because permission is restricted
IGNORED_RESTRICTED_PERMISSION = 9;
+ // one time permission was granted by user action
+ USER_GRANTED_ONE_TIME = 10;
}
// The result of the permission grant
optional Result result = 6;
diff --git a/cmds/statsd/src/external/GpuStatsPuller.cpp b/cmds/statsd/src/external/GpuStatsPuller.cpp
index bbdb5405ca05..d38b87f046e0 100644
--- a/cmds/statsd/src/external/GpuStatsPuller.cpp
+++ b/cmds/statsd/src/external/GpuStatsPuller.cpp
@@ -92,9 +92,15 @@ static bool pullGpuStatsAppInfo(const sp<IGpuService>& gpuService,
android::util::GPU_STATS_APP_INFO, getWallClockNs(), getElapsedRealtimeNs());
if (!event->write(info.appPackageName)) return false;
if (!event->write((int64_t)info.driverVersionCode)) return false;
- if (!event->write(int64VectorToProtoByteString(info.glDriverLoadingTime))) return false;
- if (!event->write(int64VectorToProtoByteString(info.vkDriverLoadingTime))) return false;
- if (!event->write(int64VectorToProtoByteString(info.angleDriverLoadingTime))) return false;
+ if (!event->writeBytes(int64VectorToProtoByteString(info.glDriverLoadingTime))) {
+ return false;
+ }
+ if (!event->writeBytes(int64VectorToProtoByteString(info.vkDriverLoadingTime))) {
+ return false;
+ }
+ if (!event->writeBytes(int64VectorToProtoByteString(info.angleDriverLoadingTime))) {
+ return false;
+ }
if (!event->write(info.cpuVulkanInUse)) return false;
if (!event->write(info.falsePrerotation)) return false;
event->init();
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 475f18a9b0b8..5a76d1f9c80d 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -17,12 +17,17 @@
#define DEBUG false
#include "Log.h"
+#include "StatsPullerManager.h"
+
#include <android/os/IStatsCompanionService.h>
#include <android/os/IStatsPullerCallback.h>
#include <cutils/log.h>
#include <math.h>
#include <stdint.h>
+
#include <algorithm>
+#include <iostream>
+
#include "../StatsService.h"
#include "../logd/LogEvent.h"
#include "../stats_log_util.h"
@@ -32,13 +37,11 @@
#include "ResourceHealthManagerPuller.h"
#include "StatsCallbackPuller.h"
#include "StatsCompanionServicePuller.h"
-#include "StatsPullerManager.h"
#include "SubsystemSleepStatePuller.h"
+#include "SurfaceflingerStatsPuller.h"
#include "TrainInfoPuller.h"
#include "statslog.h"
-#include <iostream>
-
using std::make_shared;
using std::map;
using std::shared_ptr;
@@ -119,9 +122,10 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
{.puller = new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
// system_elapsed_realtime
{android::util::SYSTEM_ELAPSED_REALTIME,
- {.pullTimeoutNs = NS_PER_SEC / 2,
- .coolDownNs = NS_PER_SEC,
- .puller = new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
+ {.coolDownNs = NS_PER_SEC,
+ .puller = new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME),
+ .pullTimeoutNs = NS_PER_SEC / 2,
+ }},
// system_uptime
{android::util::SYSTEM_UPTIME,
{.puller = new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
@@ -142,17 +146,15 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
{.puller = new ResourceHealthManagerPuller(android::util::BATTERY_CYCLE_COUNT)}},
// process_memory_state
{android::util::PROCESS_MEMORY_STATE,
- {.additiveFields = {4, 5, 6, 7, 8, 9},
+ {.additiveFields = {4, 5, 6, 7, 8},
.puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
- // native_process_memory_state
- {android::util::NATIVE_PROCESS_MEMORY_STATE,
- {.additiveFields = {3, 4, 5, 6, 8},
- .puller = new StatsCompanionServicePuller(android::util::NATIVE_PROCESS_MEMORY_STATE)}},
// process_memory_high_water_mark
{android::util::PROCESS_MEMORY_HIGH_WATER_MARK,
- {.additiveFields = {3},
- .puller =
+ {.puller =
new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
+ // process_memory_snapshot
+ {android::util::PROCESS_MEMORY_SNAPSHOT,
+ {.puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_SNAPSHOT)}},
// system_ion_heap_size
{android::util::SYSTEM_ION_HEAP_SIZE,
{.puller = new StatsCompanionServicePuller(android::util::SYSTEM_ION_HEAP_SIZE)}},
@@ -266,6 +268,10 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
// App ops
{android::util::APP_OPS,
{.puller = new StatsCompanionServicePuller(android::util::APP_OPS)}},
+ // SurfaceflingerStatsGlobalInfo
+ {android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ {.puller =
+ new SurfaceflingerStatsPuller(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO)}},
};
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/statsd/src/external/SurfaceflingerStatsPuller.cpp b/cmds/statsd/src/external/SurfaceflingerStatsPuller.cpp
new file mode 100644
index 000000000000..23b2236f35f2
--- /dev/null
+++ b/cmds/statsd/src/external/SurfaceflingerStatsPuller.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+#include "SurfaceflingerStatsPuller.h"
+
+#include <cutils/compiler.h>
+
+#include <numeric>
+
+#include "logd/LogEvent.h"
+#include "stats_log_util.h"
+#include "statslog.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+SurfaceflingerStatsPuller::SurfaceflingerStatsPuller(const int tagId) : StatsPuller(tagId) {
+}
+
+bool SurfaceflingerStatsPuller::PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) {
+ switch (mTagId) {
+ case android::util::SURFACEFLINGER_STATS_GLOBAL_INFO:
+ return pullGlobalInfo(data);
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static int64_t getTotalTime(
+ const google::protobuf::RepeatedPtrField<surfaceflinger::SFTimeStatsHistogramBucketProto>&
+ buckets) {
+ int64_t total = 0;
+ for (const auto& bucket : buckets) {
+ if (bucket.time_millis() == 1000) {
+ continue;
+ }
+
+ total += bucket.time_millis() * bucket.frame_count();
+ }
+
+ return total;
+}
+
+bool SurfaceflingerStatsPuller::pullGlobalInfo(std::vector<std::shared_ptr<LogEvent>>* data) {
+ std::string protoBytes;
+ if (CC_UNLIKELY(mStatsProvider)) {
+ protoBytes = mStatsProvider();
+ } else {
+ std::unique_ptr<FILE, decltype(&pclose)> pipe(popen("dumpsys SurfaceFlinger --timestats -dump --proto", "r"), pclose);
+ if (!pipe.get()) {
+ return false;
+ }
+ char buf[1024];
+ size_t bytesRead = 0;
+ do {
+ bytesRead = fread(buf, 1, sizeof(buf), pipe.get());
+ protoBytes.append(buf, bytesRead);
+ } while (bytesRead > 0);
+ }
+ surfaceflinger::SFTimeStatsGlobalProto proto;
+ proto.ParseFromString(protoBytes);
+
+ int64_t totalTime = getTotalTime(proto.present_to_present());
+
+ data->clear();
+ data->reserve(1);
+ std::shared_ptr<LogEvent> event =
+ make_shared<LogEvent>(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, getWallClockNs(),
+ getElapsedRealtimeNs());
+ if (!event->write(proto.total_frames())) return false;
+ if (!event->write(proto.missed_frames())) return false;
+ if (!event->write(proto.client_composition_frames())) return false;
+ if (!event->write(proto.display_on_time())) return false;
+ if (!event->write(totalTime)) return false;
+ event->init();
+ data->emplace_back(event);
+
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/SurfaceflingerStatsPuller.h b/cmds/statsd/src/external/SurfaceflingerStatsPuller.h
new file mode 100644
index 000000000000..ed7153edf797
--- /dev/null
+++ b/cmds/statsd/src/external/SurfaceflingerStatsPuller.h
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <timestatsproto/TimeStatsProtoHeader.h>
+
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Pull metrics from Surfaceflinger
+ */
+class SurfaceflingerStatsPuller : public StatsPuller {
+public:
+ explicit SurfaceflingerStatsPuller(const int tagId);
+
+ // StatsPuller interface
+ bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) override;
+
+protected:
+ // Test-only, for injecting fake data
+ using StatsProvider = std::function<std::string()>;
+ StatsProvider mStatsProvider;
+
+private:
+ bool pullGlobalInfo(std::vector<std::shared_ptr<LogEvent>>* data);
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 0ade53118d77..fd19c9d61f87 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -332,6 +332,13 @@ bool LogEvent::write(float value) {
return false;
}
+bool LogEvent::writeBytes(const string& value) {
+ if (mContext) {
+ return android_log_write_char_array(mContext, value.c_str(), value.length()) >= 0;
+ }
+ return false;
+}
+
bool LogEvent::writeKeyValuePairs(int32_t uid,
const std::map<int32_t, int32_t>& int_map,
const std::map<int32_t, int64_t>& long_map,
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 531ce299beef..f1f45a2d3bc7 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -21,9 +21,9 @@
#include <android/frameworks/stats/1.0/types.h>
#include <android/os/StatsLogEventWrapper.h>
#include <android/util/ProtoOutputStream.h>
-#include <log/log_event_list.h>
#include <log/log_read.h>
#include <private/android_logger.h>
+#include <stats_event_list.h>
#include <utils/Errors.h>
#include <string>
@@ -157,6 +157,7 @@ public:
bool write(float value);
bool write(const std::vector<AttributionNodeInternal>& nodes);
bool write(const AttributionNodeInternal& node);
+ bool writeBytes(const std::string& value);
bool writeKeyValuePairs(int32_t uid,
const std::map<int32_t, int32_t>& int_map,
const std::map<int32_t, int64_t>& long_map,
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index 42132ee0daae..7d446a9a1ed6 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -53,6 +53,8 @@ void sigHandler(int sig) {
if (gStatsService != nullptr) {
gStatsService->Terminate();
}
+ ALOGW("statsd terminated on receiving signal %d.", sig);
+ exit(1);
}
void registerSigHandler()
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 23d025f82c26..4a06387e357f 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -18,13 +18,15 @@
#include "Log.h"
#include "CountMetricProducer.h"
-#include "guardrail/StatsdStats.h"
-#include "stats_util.h"
-#include "stats_log_util.h"
+#include <inttypes.h>
#include <limits.h>
#include <stdlib.h>
+#include "guardrail/StatsdStats.h"
+#include "stats_log_util.h"
+#include "stats_util.h"
+
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FLOAT;
@@ -37,6 +39,7 @@ using std::map;
using std::string;
using std::unordered_map;
using std::vector;
+using std::shared_ptr;
namespace android {
namespace os {
@@ -64,11 +67,16 @@ const int FIELD_ID_BUCKET_NUM = 4;
const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
-CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric& metric,
- const int conditionIndex,
- const sp<ConditionWizard>& wizard,
- const int64_t timeBaseNs, const int64_t startTimeNs)
- : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard) {
+CountMetricProducer::CountMetricProducer(
+ const ConfigKey& key, const CountMetric& metric, const int conditionIndex,
+ const sp<ConditionWizard>& wizard, const int64_t timeBaseNs, const int64_t startTimeNs,
+
+ const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+ const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
+ : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap,
+ eventDeactivationMap, slicedStateAtoms, stateGroupMap) {
if (metric.has_bucket()) {
mBucketSizeNs =
TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
@@ -94,6 +102,8 @@ CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric
mConditionSliced = true;
}
+ // TODO(tsaichristine): b/142124705 handle metric state links
+
flushIfNeededLocked(startTimeNs);
// Adjust start for partial bucket
mCurrentBucketStartTimeNs = startTimeNs;
@@ -106,6 +116,12 @@ CountMetricProducer::~CountMetricProducer() {
VLOG("~CountMetricProducer() called");
}
+void CountMetricProducer::onStateChanged(int atomId, const HashableDimensionKey& primaryKey,
+ int oldState, int newState) {
+ VLOG("CountMetric %lld onStateChanged State%d, key %s, %d -> %d", (long long)mMetricId, atomId,
+ primaryKey.toString().c_str(), oldState, newState);
+}
+
void CountMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
if (mCurrentSlicedCounter == nullptr ||
mCurrentSlicedCounter->size() == 0) {
@@ -251,6 +267,7 @@ bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
ALOGE("CountMetric %lld dropping data for dimension key %s",
(long long)mMetricId, newKey.toString().c_str());
+ StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId);
return true;
}
}
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index b4a910c6f410..61e0892d50a9 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -17,15 +17,16 @@
#ifndef COUNT_METRIC_PRODUCER_H
#define COUNT_METRIC_PRODUCER_H
-#include <unordered_map>
-
#include <android/util/ProtoOutputStream.h>
#include <gtest/gtest_prod.h>
-#include "../anomaly/AnomalyTracker.h"
-#include "../condition/ConditionTracker.h"
-#include "../matchers/matcher_util.h"
+
+#include <unordered_map>
+
#include "MetricProducer.h"
+#include "anomaly/AnomalyTracker.h"
+#include "condition/ConditionTracker.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "matchers/matcher_util.h"
#include "stats_util.h"
namespace android {
@@ -40,12 +41,20 @@ struct CountBucket {
class CountMetricProducer : public MetricProducer {
public:
- CountMetricProducer(const ConfigKey& key, const CountMetric& countMetric,
- const int conditionIndex, const sp<ConditionWizard>& wizard,
- const int64_t timeBaseNs, const int64_t startTimeNs);
+ CountMetricProducer(
+ const ConfigKey& key, const CountMetric& countMetric, const int conditionIndex,
+ const sp<ConditionWizard>& wizard, const int64_t timeBaseNs, const int64_t startTimeNs,
+ const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
+ const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+ eventDeactivationMap = {},
+ const vector<int>& slicedStateAtoms = {},
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
virtual ~CountMetricProducer();
+ void onStateChanged(int atomId, const HashableDimensionKey& primaryKey, int oldState,
+ int newState) override;
+
protected:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
@@ -102,6 +111,7 @@ private:
FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgrade);
FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket);
FRIEND_TEST(CountMetricProducerTest, TestFirstBucket);
+ FRIEND_TEST(CountMetricProducerTest, TestOneWeekTimeUnit);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index cca793b2146a..ab2a1c3bd65c 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -36,6 +36,7 @@ using android::util::ProtoOutputStream;
using std::string;
using std::unordered_map;
using std::vector;
+using std::shared_ptr;
namespace android {
namespace os {
@@ -62,14 +63,17 @@ const int FIELD_ID_BUCKET_NUM = 4;
const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
-DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const DurationMetric& metric,
- const int conditionIndex, const size_t startIndex,
- const size_t stopIndex, const size_t stopAllIndex,
- const bool nesting,
- const sp<ConditionWizard>& wizard,
- const FieldMatcher& internalDimensions,
- const int64_t timeBaseNs, const int64_t startTimeNs)
- : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
+DurationMetricProducer::DurationMetricProducer(
+ const ConfigKey& key, const DurationMetric& metric, const int conditionIndex,
+ const size_t startIndex, const size_t stopIndex, const size_t stopAllIndex,
+ const bool nesting, const sp<ConditionWizard>& wizard,
+ const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs,
+ const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+ const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
+ : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap,
+ eventDeactivationMap, slicedStateAtoms, stateGroupMap),
mAggregationType(metric.aggregation_type()),
mStartIndex(startIndex),
mStopIndex(stopIndex),
@@ -491,6 +495,7 @@ bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
ALOGE("DurationMetric %lld dropping data for condition dimension key %s",
(long long)mMetricId, newKey.getDimensionKeyInCondition().toString().c_str());
+ StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId);
return true;
}
}
@@ -504,6 +509,7 @@ bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
ALOGE("DurationMetric %lld dropping data for what dimension key %s",
(long long)mMetricId, newKey.getDimensionKeyInWhat().toString().c_str());
+ StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId);
return true;
}
}
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 56c9fd68eac5..7457d7fb2dd9 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -38,11 +38,16 @@ namespace statsd {
class DurationMetricProducer : public MetricProducer {
public:
- DurationMetricProducer(const ConfigKey& key, const DurationMetric& durationMetric,
- const int conditionIndex, const size_t startIndex,
- const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
- const sp<ConditionWizard>& wizard,
- const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs);
+ DurationMetricProducer(
+ const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex,
+ const size_t startIndex, const size_t stopIndex, const size_t stopAllIndex,
+ const bool nesting, const sp<ConditionWizard>& wizard,
+ const FieldMatcher& internalDimensions, const int64_t timeBaseNs,
+ const int64_t startTimeNs,
+ const unordered_map<int, shared_ptr<Activation>>& eventActivationMap = {},
+ const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap = {},
+ const vector<int>& slicedStateAtoms = {},
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
virtual ~DurationMetricProducer();
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 96133bd0a38d..32eb077e1cf4 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -36,6 +36,7 @@ using std::map;
using std::string;
using std::unordered_map;
using std::vector;
+using std::shared_ptr;
namespace android {
namespace os {
@@ -51,11 +52,15 @@ const int FIELD_ID_DATA = 1;
const int FIELD_ID_ELAPSED_TIMESTAMP_NANOS = 1;
const int FIELD_ID_ATOMS = 2;
-EventMetricProducer::EventMetricProducer(const ConfigKey& key, const EventMetric& metric,
- const int conditionIndex,
- const sp<ConditionWizard>& wizard,
- const int64_t startTimeNs)
- : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard) {
+EventMetricProducer::EventMetricProducer(
+ const ConfigKey& key, const EventMetric& metric, const int conditionIndex,
+ const sp<ConditionWizard>& wizard, const int64_t startTimeNs,
+ const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+ const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
+ : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard, eventActivationMap,
+ eventDeactivationMap, slicedStateAtoms, stateGroupMap) {
if (metric.links().size() > 0) {
for (const auto& link : metric.links()) {
Metric2Condition mc;
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 74e6bc845c04..dca37e8790a4 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -33,9 +33,14 @@ namespace statsd {
class EventMetricProducer : public MetricProducer {
public:
- EventMetricProducer(const ConfigKey& key, const EventMetric& eventMetric,
- const int conditionIndex, const sp<ConditionWizard>& wizard,
- const int64_t startTimeNs);
+ EventMetricProducer(
+ const ConfigKey& key, const EventMetric& eventMetric, const int conditionIndex,
+ const sp<ConditionWizard>& wizard, const int64_t startTimeNs,
+ const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
+ const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+ eventDeactivationMap = {},
+ const vector<int>& slicedStateAtoms = {},
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
virtual ~EventMetricProducer();
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 1f423cd384ef..d0f88a867da7 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -72,8 +72,13 @@ GaugeMetricProducer::GaugeMetricProducer(
const sp<ConditionWizard>& wizard, const int whatMatcherIndex,
const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int triggerAtomId,
const int atomId, const int64_t timeBaseNs, const int64_t startTimeNs,
- const sp<StatsPullerManager>& pullerManager)
- : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
+ const sp<StatsPullerManager>& pullerManager,
+ const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+ const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
+ : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap,
+ eventDeactivationMap, slicedStateAtoms, stateGroupMap),
mWhatMatcherIndex(whatMatcherIndex),
mEventMatcherWizard(matcherWizard),
mPullerManager(pullerManager),
@@ -133,8 +138,11 @@ GaugeMetricProducer::GaugeMetricProducer(
mBucketSizeNs);
}
- // Adjust start for partial bucket
+ // Adjust start for partial first bucket and then pull if needed
mCurrentBucketStartTimeNs = startTimeNs;
+ if (mIsActive && mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
+ pullAndMatchEventsLocked(mCurrentBucketStartTimeNs);
+ }
VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d",
(long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs,
@@ -295,11 +303,6 @@ void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
}
}
-void GaugeMetricProducer::prepareFirstBucketLocked() {
- if (mIsActive && mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
- pullAndMatchEventsLocked(mCurrentBucketStartTimeNs);
- }
-}
void GaugeMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
bool triggerPuller = false;
@@ -439,6 +442,7 @@ bool GaugeMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
if (newTupleCount > mDimensionHardLimit) {
ALOGE("GaugeMetric %lld dropping data for dimension key %s",
(long long)mMetricId, newKey.toString().c_str());
+ StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId);
return true;
}
}
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index a612adf8a38b..640a02a9a8ab 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -56,13 +56,17 @@ typedef std::unordered_map<MetricDimensionKey, std::vector<GaugeAtom>>
// producer always reports the guage at the earliest time of the bucket when the condition is met.
class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
public:
- GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
- const int conditionIndex, const sp<ConditionWizard>& conditionWizard,
- const int whatMatcherIndex,
- const sp<EventMatcherWizard>& matcherWizard,
- const int pullTagId, const int triggerAtomId, const int atomId,
- const int64_t timeBaseNs, const int64_t startTimeNs,
- const sp<StatsPullerManager>& pullerManager);
+ GaugeMetricProducer(
+ const ConfigKey& key, const GaugeMetric& gaugeMetric, const int conditionIndex,
+ const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
+ const sp<EventMatcherWizard>& matcherWizard, const int pullTagId,
+ const int triggerAtomId, const int atomId, const int64_t timeBaseNs,
+ const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
+ const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+ eventDeactivationMap = {},
+ const vector<int>& slicedStateAtoms = {},
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
virtual ~GaugeMetricProducer();
@@ -125,8 +129,6 @@ private:
void flushCurrentBucketLocked(const int64_t& eventTimeNs,
const int64_t& nextBucketStartTimeNs) override;
- void prepareFirstBucketLocked() override;
-
void pullAndMatchEventsLocked(const int64_t timestampNs);
const int mWhatMatcherIndex;
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 1ab4fdf6e90c..2a700efe3d37 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -40,6 +40,33 @@ const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX = 1;
const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS = 2;
const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE = 3;
+MetricProducer::MetricProducer(
+ const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
+ const int conditionIndex, const sp<ConditionWizard>& wizard,
+ const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
+ const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+ eventDeactivationMap,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
+ : mMetricId(metricId),
+ mConfigKey(key),
+ mTimeBaseNs(timeBaseNs),
+ mCurrentBucketStartTimeNs(timeBaseNs),
+ mCurrentBucketNum(0),
+ mCondition(initialCondition(conditionIndex)),
+ mConditionTrackerIndex(conditionIndex),
+ mConditionSliced(false),
+ mWizard(wizard),
+ mContainANYPositionInDimensionsInWhat(false),
+ mSliceByPositionALL(false),
+ mHasLinksToAllConditionDimensionsInTracker(false),
+ mEventActivationMap(eventActivationMap),
+ mEventDeactivationMap(eventDeactivationMap),
+ mIsActive(mEventActivationMap.empty()),
+ mSlicedStateAtoms(slicedStateAtoms),
+ mStateGroupMap(stateGroupMap) {
+}
+
void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
if (!mIsActive) {
return;
@@ -97,24 +124,6 @@ void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) {
}
}
-void MetricProducer::addActivation(int activationTrackerIndex, const ActivationType& activationType,
- int64_t ttl_seconds, int deactivationTrackerIndex) {
- std::lock_guard<std::mutex> lock(mMutex);
- // When a metric producer does not depend on any activation, its mIsActive is true.
- // Therefore, if this is the 1st activation, mIsActive will turn to false. Otherwise it does not
- // change.
- if (mEventActivationMap.empty()) {
- mIsActive = false;
- }
- std::shared_ptr<Activation> activation =
- std::make_shared<Activation>(activationType, ttl_seconds * NS_PER_SEC);
- mEventActivationMap.emplace(activationTrackerIndex, activation);
- if (-1 != deactivationTrackerIndex) {
- auto& deactivationList = mEventDeactivationMap[deactivationTrackerIndex];
- deactivationList.push_back(activation);
- }
-}
-
void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs) {
auto it = mEventActivationMap.find(activationTrackerIndex);
if (it == mEventActivationMap.end()) {
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index fdbdc83fb66e..a72de22bd433 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -17,19 +17,19 @@
#ifndef METRIC_PRODUCER_H
#define METRIC_PRODUCER_H
-#include <shared_mutex>
-
#include <frameworks/base/cmds/statsd/src/active_config_list.pb.h>
+#include <log/logprint.h>
+#include <utils/RefBase.h>
+
+#include <unordered_map>
+
#include "HashableDimensionKey.h"
#include "anomaly/AnomalyTracker.h"
#include "condition/ConditionWizard.h"
#include "config/ConfigKey.h"
#include "matchers/matcher_util.h"
#include "packages/PackageInfoListener.h"
-
-#include <log/logprint.h>
-#include <utils/RefBase.h>
-#include <unordered_map>
+#include "state/StateListener.h"
namespace android {
namespace os {
@@ -69,28 +69,32 @@ enum DumpLatency {
NO_TIME_CONSTRAINTS = 2
};
+struct Activation {
+ Activation(const ActivationType& activationType, const int64_t ttlNs)
+ : ttl_ns(ttlNs),
+ start_ns(0),
+ state(ActivationState::kNotActive),
+ activationType(activationType) {}
+
+ const int64_t ttl_ns;
+ int64_t start_ns;
+ ActivationState state;
+ const ActivationType activationType;
+};
+
// A MetricProducer is responsible for compute one single metrics, creating stats log report, and
// writing the report to dropbox. MetricProducers should respond to package changes as required in
// PackageInfoListener, but if none of the metrics are slicing by package name, then the update can
// be a no-op.
-class MetricProducer : public virtual PackageInfoListener {
+class MetricProducer : public virtual PackageInfoListener, public virtual StateListener {
public:
MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
- const int conditionIndex, const sp<ConditionWizard>& wizard)
- : mMetricId(metricId),
- mConfigKey(key),
- mTimeBaseNs(timeBaseNs),
- mCurrentBucketStartTimeNs(timeBaseNs),
- mCurrentBucketNum(0),
- mCondition(initialCondition(conditionIndex)),
- mConditionTrackerIndex(conditionIndex),
- mConditionSliced(false),
- mWizard(wizard),
- mContainANYPositionInDimensionsInWhat(false),
- mSliceByPositionALL(false),
- mHasLinksToAllConditionDimensionsInTracker(false),
- mIsActive(true) {
- }
+ const int conditionIndex, const sp<ConditionWizard>& wizard,
+ const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
+ const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+ eventDeactivationMap,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap);
virtual ~MetricProducer(){};
@@ -149,6 +153,9 @@ public:
return mConditionSliced;
};
+ void onStateChanged(int atomId, const HashableDimensionKey& primaryKey, int oldState,
+ int newState){};
+
// Output the metrics data to [protoOutput]. All metrics reports end with the same timestamp.
// This method clears all the past buckets.
void onDumpReport(const int64_t dumpTimeNs,
@@ -188,11 +195,6 @@ public:
dropDataLocked(dropTimeNs);
}
- void prepareFirstBucket() {
- std::lock_guard<std::mutex> lock(mMutex);
- prepareFirstBucketLocked();
- }
-
void loadActiveMetric(const ActiveMetric& activeMetric, int64_t currentTimeNs) {
std::lock_guard<std::mutex> lock(mMutex);
loadActiveMetricLocked(activeMetric, currentTimeNs);
@@ -215,9 +217,6 @@ public:
void flushIfExpire(int64_t elapsedTimestampNs);
- void addActivation(int activationTrackerIndex, const ActivationType& activationType,
- int64_t ttl_seconds, int deactivationTrackerIndex = -1);
-
void writeActiveMetricToProtoOutputStream(
int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
@@ -236,6 +235,11 @@ public:
return mBucketSizeNs;
}
+ inline const std::vector<int> getSlicedStateAtoms() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mSlicedStateAtoms;
+ }
+
/* If alert is valid, adds an AnomalyTracker and returns it. If invalid, returns nullptr. */
virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
const sp<AlarmMonitor>& anomalyAlarmMonitor) {
@@ -310,7 +314,6 @@ protected:
virtual size_t byteSizeLocked() const = 0;
virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0;
virtual void dropDataLocked(const int64_t dropTimeNs) = 0;
- virtual void prepareFirstBucketLocked() {};
void loadActiveMetricLocked(const ActiveMetric& activeMetric, int64_t currentTimeNs);
void activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs);
void cancelEventActivationLocked(int deactivationTrackerIndex);
@@ -379,19 +382,6 @@ protected:
mutable std::mutex mMutex;
- struct Activation {
- Activation(const ActivationType& activationType, const int64_t ttlNs)
- : ttl_ns(ttlNs),
- start_ns(0),
- state(ActivationState::kNotActive),
- activationType(activationType) {}
-
- const int64_t ttl_ns;
- int64_t start_ns;
- ActivationState state;
- const ActivationType activationType;
- };
-
// When the metric producer has multiple activations, these activations are ORed to determine
// whether the metric producer is ready to generate metrics.
std::unordered_map<int, std::shared_ptr<Activation>> mEventActivationMap;
@@ -401,6 +391,16 @@ protected:
bool mIsActive;
+ // The slice_by_state atom ids defined in statsd_config.
+ std::vector<int> mSlicedStateAtoms;
+
+ // Maps atom ids and state values to group_ids (<atom_id, <value, group_id>>).
+ std::unordered_map<int, std::unordered_map<int, int64_t>> mStateGroupMap;
+
+ FRIEND_TEST(CountMetricE2eTest, TestWithSimpleState);
+ FRIEND_TEST(CountMetricE2eTest, TestWithMappedState);
+ FRIEND_TEST(CountMetricE2eTest, TestWithMultipleStates);
+
FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 963205ee56f4..7bae4b929b48 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -15,8 +15,12 @@
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
+
#include "MetricsManager.h"
-#include "statslog.h"
+
+#include <log/logprint.h>
+#include <private/android_filesystem_config.h>
+#include <utils/SystemClock.h>
#include "CountMetricProducer.h"
#include "condition/CombinationConditionTracker.h"
@@ -25,12 +29,10 @@
#include "matchers/CombinationLogMatchingTracker.h"
#include "matchers/SimpleLogMatchingTracker.h"
#include "metrics_manager_util.h"
-#include "stats_util.h"
+#include "state/StateManager.h"
#include "stats_log_util.h"
-
-#include <log/logprint.h>
-#include <private/android_filesystem_config.h>
-#include <utils/SystemClock.h>
+#include "stats_util.h"
+#include "statslog.h"
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_INT32;
@@ -149,6 +151,12 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
}
MetricsManager::~MetricsManager() {
+ for (auto it : mAllMetricProducers) {
+ for (int atomId : it->getSlicedStateAtoms()) {
+ StateManager::getInstance().unregisterListener(atomId, it);
+ }
+ }
+
VLOG("~MetricsManager()");
}
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 3dad61465416..d184121f5ba0 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -282,6 +282,10 @@ private:
TestActivationOnBootMultipleActivationsDifferentActivationTypes);
FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
+ FRIEND_TEST(CountMetricE2eTest, TestWithSimpleState);
+ FRIEND_TEST(CountMetricE2eTest, TestWithMappedState);
+ FRIEND_TEST(CountMetricE2eTest, TestWithMultipleStates);
+
FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index bc1602497c39..6fd03273a434 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -81,8 +81,13 @@ ValueMetricProducer::ValueMetricProducer(
const ConfigKey& key, const ValueMetric& metric, const int conditionIndex,
const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int64_t timeBaseNs,
- const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager)
- : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, conditionWizard),
+ const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+ const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
+ : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, conditionWizard,
+ eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap),
mWhatMatcherIndex(whatMatcherIndex),
mEventMatcherWizard(matcherWizard),
mPullerManager(pullerManager),
@@ -108,7 +113,7 @@ ValueMetricProducer::ValueMetricProducer(
mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC
: StatsdStats::kPullMaxDelayNs),
mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()),
- // Condition timer will be set in prepareFirstBucketLocked.
+ // Condition timer will be set later within the constructor after pulling events
mConditionTimer(false, timeBaseNs) {
int64_t bucketSizeMills = 0;
if (metric.has_bucket()) {
@@ -154,6 +159,15 @@ ValueMetricProducer::ValueMetricProducer(
// Adjust start for partial bucket
mCurrentBucketStartTimeNs = startTimeNs;
mConditionTimer.newBucketStart(mCurrentBucketStartTimeNs);
+
+ // Kicks off the puller immediately if condition is true and diff based.
+ if (mIsActive && mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) {
+ pullAndMatchEventsLocked(mCurrentBucketStartTimeNs, mCondition);
+ }
+ // Now that activations are processed, start the condition timer if needed.
+ mConditionTimer.onConditionChanged(mIsActive && mCondition == ConditionState::kTrue,
+ mCurrentBucketStartTimeNs);
+
VLOG("value metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
(long long)mBucketSizeNs, (long long)mTimeBaseNs);
}
@@ -165,16 +179,6 @@ ValueMetricProducer::~ValueMetricProducer() {
}
}
-void ValueMetricProducer::prepareFirstBucketLocked() {
- // Kicks off the puller immediately if condition is true and diff based.
- if (mIsActive && mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) {
- pullAndMatchEventsLocked(mCurrentBucketStartTimeNs, mCondition);
- }
- // Now that activations are processed, start the condition timer if needed.
- mConditionTimer.onConditionChanged(mIsActive && mCondition == ConditionState::kTrue,
- mCurrentBucketStartTimeNs);
-}
-
void ValueMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
const int64_t eventTime) {
VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 739f6ef07cc4..206e602dd1c7 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -52,12 +52,17 @@ struct ValueBucket {
// - an alarm set to the end of the bucket
class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
public:
- ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
- const int conditionIndex, const sp<ConditionWizard>& conditionWizard,
- const int whatMatcherIndex,
- const sp<EventMatcherWizard>& matcherWizard,
- const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs,
- const sp<StatsPullerManager>& pullerManager);
+ ValueMetricProducer(
+ const ConfigKey& key, const ValueMetric& valueMetric, const int conditionIndex,
+ const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
+ const sp<EventMatcherWizard>& matcherWizard, const int pullTagId,
+ const int64_t timeBaseNs, const int64_t startTimeNs,
+ const sp<StatsPullerManager>& pullerManager,
+ const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
+ const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+ eventDeactivationMap = {},
+ const vector<int>& slicedStateAtoms = {},
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
virtual ~ValueMetricProducer();
@@ -116,8 +121,6 @@ private:
void flushCurrentBucketLocked(const int64_t& eventTimeNs,
const int64_t& nextBucketStartTimeNs) override;
- void prepareFirstBucketLocked() override;
-
void dropDataLocked(const int64_t dropTimeNs) override;
// Calculate previous bucket end time based on current time.
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 40484f4fb86b..33e162ec2d24 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -18,25 +18,26 @@
#include "Log.h"
#include "metrics_manager_util.h"
+#include "MetricProducer.h"
-#include "../condition/CombinationConditionTracker.h"
-#include "../condition/SimpleConditionTracker.h"
-#include "../condition/StateConditionTracker.h"
-#include "../external/StatsPullerManager.h"
-#include "../matchers/CombinationLogMatchingTracker.h"
-#include "../matchers/SimpleLogMatchingTracker.h"
-#include "../matchers/EventMatcherWizard.h"
-#include "../metrics/CountMetricProducer.h"
-#include "../metrics/DurationMetricProducer.h"
-#include "../metrics/EventMetricProducer.h"
-#include "../metrics/GaugeMetricProducer.h"
-#include "../metrics/ValueMetricProducer.h"
+#include <inttypes.h>
+#include "condition/CombinationConditionTracker.h"
+#include "condition/SimpleConditionTracker.h"
+#include "condition/StateConditionTracker.h"
+#include "external/StatsPullerManager.h"
+#include "matchers/CombinationLogMatchingTracker.h"
+#include "matchers/EventMatcherWizard.h"
+#include "matchers/SimpleLogMatchingTracker.h"
+#include "metrics/CountMetricProducer.h"
+#include "metrics/DurationMetricProducer.h"
+#include "metrics/EventMetricProducer.h"
+#include "metrics/GaugeMetricProducer.h"
+#include "metrics/ValueMetricProducer.h"
+#include "state/StateManager.h"
#include "stats_util.h"
#include "statslog.h"
-#include <inttypes.h>
-
using std::set;
using std::string;
using std::unordered_map;
@@ -137,6 +138,97 @@ bool handleMetricWithConditions(
return true;
}
+// Initializes state data structures for a metric.
+// input:
+// [config]: the input config
+// [stateIds]: the slice_by_state ids for this metric
+// [stateAtomIdMap]: this map contains the mapping from all state ids to atom ids
+// [allStateGroupMaps]: this map contains the mapping from state ids and state
+// values to state group ids for all states
+// output:
+// [slicedStateAtoms]: a vector of atom ids of all the slice_by_states
+// [stateGroupMap]: this map should contain the mapping from states ids and state
+// values to state group ids for all states that this metric
+// is interested in
+bool handleMetricWithStates(
+ const StatsdConfig& config, const ::google::protobuf::RepeatedField<int64_t>& stateIds,
+ const unordered_map<int64_t, int>& stateAtomIdMap,
+ const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+ vector<int>& slicedStateAtoms,
+ unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) {
+ for (const auto& stateId : stateIds) {
+ auto it = stateAtomIdMap.find(stateId);
+ if (it == stateAtomIdMap.end()) {
+ ALOGW("cannot find State %" PRId64 " in the config", stateId);
+ return false;
+ }
+ int atomId = it->second;
+ slicedStateAtoms.push_back(atomId);
+
+ auto stateIt = allStateGroupMaps.find(stateId);
+ if (stateIt != allStateGroupMaps.end()) {
+ stateGroupMap[atomId] = stateIt->second;
+ }
+ }
+ return true;
+}
+
+// Validates a metricActivation and populates state.
+// EventActivationMap and EventDeactivationMap are supplied to a MetricProducer
+// to provide the producer with state about its activators and deactivators.
+// Returns false if there are errors.
+bool handleMetricActivation(
+ const StatsdConfig& config,
+ const int64_t metricId,
+ const int metricIndex,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ const unordered_map<int64_t, int>& logTrackerMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation,
+ unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+ unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) {
+ // Check if metric has an associated activation
+ auto itr = metricToActivationMap.find(metricId);
+ if (itr == metricToActivationMap.end()) return true;
+
+ int activationIndex = itr->second;
+ const MetricActivation& metricActivation = config.metric_activation(activationIndex);
+
+ for (int i = 0; i < metricActivation.event_activation_size(); i++) {
+ const EventActivation& activation = metricActivation.event_activation(i);
+
+ auto itr = logTrackerMap.find(activation.atom_matcher_id());
+ if (itr == logTrackerMap.end()) {
+ ALOGE("Atom matcher not found for event activation.");
+ return false;
+ }
+
+ ActivationType activationType = (activation.has_activation_type()) ?
+ activation.activation_type() : metricActivation.activation_type();
+ std::shared_ptr<Activation> activationWrapper = std::make_shared<Activation>(
+ activationType, activation.ttl_seconds() * NS_PER_SEC);
+
+ int atomMatcherIndex = itr->second;
+ activationAtomTrackerToMetricMap[atomMatcherIndex].push_back(metricIndex);
+ eventActivationMap.emplace(atomMatcherIndex, activationWrapper);
+
+ if (activation.has_deactivation_atom_matcher_id()) {
+ itr = logTrackerMap.find(activation.deactivation_atom_matcher_id());
+ if (itr == logTrackerMap.end()) {
+ ALOGE("Atom matcher not found for event deactivation.");
+ return false;
+ }
+ int deactivationAtomMatcherIndex = itr->second;
+ deactivationAtomTrackerToMetricMap[deactivationAtomMatcherIndex].push_back(metricIndex);
+ eventDeactivationMap[deactivationAtomMatcherIndex].push_back(activationWrapper);
+ }
+ }
+
+ metricsWithActivation.push_back(metricIndex);
+ return true;
+}
+
bool initLogTrackers(const StatsdConfig& config, const UidMap& uidMap,
unordered_map<int64_t, int>& logTrackerMap,
vector<sp<LogMatchingTracker>>& allAtomMatchers, set<int>& allTagIds) {
@@ -285,24 +377,61 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config,
return true;
}
+bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
+ unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps) {
+ for (int i = 0; i < config.state_size(); i++) {
+ const State& state = config.state(i);
+ const int64_t stateId = state.id();
+ stateAtomIdMap[stateId] = state.atom_id();
+
+ const StateMap& stateMap = state.map();
+ for (auto group : stateMap.group()) {
+ for (auto value : group.value()) {
+ allStateGroupMaps[stateId][value] = group.group_id();
+ }
+ }
+ }
+
+ return true;
+}
+
bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
const int64_t currentTimeNs, UidMap& uidMap,
const sp<StatsPullerManager>& pullerManager,
const unordered_map<int64_t, int>& logTrackerMap,
const unordered_map<int64_t, int>& conditionTrackerMap,
const vector<sp<LogMatchingTracker>>& allAtomMatchers,
+ const unordered_map<int64_t, int>& stateAtomIdMap,
+ const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
vector<sp<ConditionTracker>>& allConditionTrackers,
vector<sp<MetricProducer>>& allMetricProducers,
- unordered_map<int, std::vector<int>>& conditionToMetricMap,
- unordered_map<int, std::vector<int>>& trackerToMetricMap,
- unordered_map<int64_t, int>& metricMap, std::set<int64_t>& noReportMetricIds) {
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int64_t, int>& metricMap, std::set<int64_t>& noReportMetricIds,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchers);
const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
- config.event_metric_size() + config.value_metric_size();
+ config.event_metric_size() + config.gauge_metric_size() +
+ config.value_metric_size();
allMetricProducers.reserve(allMetricsCount);
StatsPullerManager statsPullerManager;
+ // Construct map from metric id to metric activation index. The map will be used to determine
+ // the metric activation corresponding to a metric.
+ unordered_map<int64_t, int> metricToActivationMap;
+ for (int i = 0; i < config.metric_activation_size(); i++) {
+ const MetricActivation& metricActivation = config.metric_activation(i);
+ int64_t metricId = metricActivation.metric_id();
+ if (metricToActivationMap.find(metricId) != metricToActivationMap.end()) {
+ ALOGE("Metric %lld has multiple MetricActivations", (long long) metricId);
+ return false;
+ }
+ metricToActivationMap.insert({metricId, i});
+ }
+
// Build MetricProducers for each metric defined in config.
// build CountMetricProducer
for (int i = 0; i < config.count_metric_size(); i++) {
@@ -324,10 +453,9 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
int conditionIndex = -1;
if (metric.has_condition()) {
- bool good = handleMetricWithConditions(
- metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
- allConditionTrackers, conditionIndex, conditionToMetricMap);
- if (!good) {
+ if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+ metric.links(), allConditionTrackers, conditionIndex,
+ conditionToMetricMap)) {
return false;
}
} else {
@@ -337,8 +465,29 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
}
}
- sp<MetricProducer> countProducer =
- new CountMetricProducer(key, metric, conditionIndex, wizard, timeBaseTimeNs, currentTimeNs);
+ std::vector<int> slicedStateAtoms;
+ unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+ if (metric.slice_by_state_size() > 0) {
+ if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
+ allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
+ return false;
+ }
+ }
+
+ // TODO(tsaichristine): add check for unequal number of MetricStateLinks
+ // and slice_by_states
+
+ unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+ bool success = handleMetricActivation(config, metric.id(), metricIndex,
+ metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
+ eventDeactivationMap);
+ if (!success) return false;
+
+ sp<MetricProducer> countProducer = new CountMetricProducer(
+ key, metric, conditionIndex, wizard, timeBaseTimeNs, currentTimeNs,
+ eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap);
allMetricProducers.push_back(countProducer);
}
@@ -406,9 +555,18 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
}
}
+ unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+ bool success = handleMetricActivation(config, metric.id(), metricIndex,
+ metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
+ eventDeactivationMap);
+ if (!success) return false;
+
sp<MetricProducer> durationMetric = new DurationMetricProducer(
key, metric, conditionIndex, trackerIndices[0], trackerIndices[1],
- trackerIndices[2], nesting, wizard, internalDimensions, timeBaseTimeNs, currentTimeNs);
+ trackerIndices[2], nesting, wizard, internalDimensions, timeBaseTimeNs,
+ currentTimeNs, eventActivationMap, eventDeactivationMap);
allMetricProducers.push_back(durationMetric);
}
@@ -443,8 +601,17 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
}
}
- sp<MetricProducer> eventMetric =
- new EventMetricProducer(key, metric, conditionIndex, wizard, timeBaseTimeNs);
+ unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+ bool success = handleMetricActivation(config, metric.id(), metricIndex,
+ metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
+ eventDeactivationMap);
+ if (!success) return false;
+
+ sp<MetricProducer> eventMetric = new EventMetricProducer(
+ key, metric, conditionIndex, wizard, timeBaseTimeNs, eventActivationMap,
+ eventDeactivationMap);
allMetricProducers.push_back(eventMetric);
}
@@ -500,9 +667,18 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
}
}
+ unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+ bool success = handleMetricActivation(config, metric.id(), metricIndex,
+ metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
+ eventDeactivationMap);
+ if (!success) return false;
+
sp<MetricProducer> valueProducer = new ValueMetricProducer(
key, metric, conditionIndex, wizard, trackerIndex, matcherWizard, pullTagId,
- timeBaseTimeNs, currentTimeNs, pullerManager);
+ timeBaseTimeNs, currentTimeNs, pullerManager, eventActivationMap,
+ eventDeactivationMap);
allMetricProducers.push_back(valueProducer);
}
@@ -586,10 +762,19 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
}
}
+ unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+ bool success = handleMetricActivation(config, metric.id(), metricIndex,
+ metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
+ eventDeactivationMap);
+ if (!success) return false;
+
sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(
key, metric, conditionIndex, wizard,
trackerIndex, matcherWizard, pullTagId, triggerAtomId, atomTagId,
- timeBaseTimeNs, currentTimeNs, pullerManager);
+ timeBaseTimeNs, currentTimeNs, pullerManager,
+ eventActivationMap, eventDeactivationMap);
allMetricProducers.push_back(gaugeProducer);
}
for (int i = 0; i < config.no_report_metric_size(); ++i) {
@@ -602,6 +787,13 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
}
for (const auto& it : allMetricProducers) {
uidMap.addListener(it);
+
+ // Register metrics to StateTrackers
+ for (int atomId : it->getSlicedStateAtoms()) {
+ if (!StateManager::getInstance().registerListener(atomId, it)) {
+ return false;
+ }
+ }
}
return true;
}
@@ -707,73 +899,6 @@ bool initAlarms(const StatsdConfig& config, const ConfigKey& key,
return true;
}
-bool initMetricActivations(const ConfigKey& key, const StatsdConfig& config,
- const int64_t currentTimeNs,
- const unordered_map<int64_t, int> &logEventTrackerMap,
- const unordered_map<int64_t, int> &metricProducerMap,
- vector<sp<MetricProducer>>& allMetricProducers,
- unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation) {
- for (int i = 0; i < config.metric_activation_size(); ++i) {
- const MetricActivation& metric_activation = config.metric_activation(i);
- auto itr = metricProducerMap.find(metric_activation.metric_id());
- if (itr == metricProducerMap.end()) {
- ALOGE("Metric id not found in metric activation: %lld",
- (long long)metric_activation.metric_id());
- return false;
- }
- const int metricTrackerIndex = itr->second;
- if (metricTrackerIndex < 0 || metricTrackerIndex >= (int)allMetricProducers.size()) {
- ALOGE("Invalid metric tracker index.");
- return false;
- }
- const sp<MetricProducer>& metric = allMetricProducers[metricTrackerIndex];
- metricsWithActivation.push_back(metricTrackerIndex);
- for (int j = 0; j < metric_activation.event_activation_size(); ++j) {
- const EventActivation& activation = metric_activation.event_activation(j);
- auto logTrackerIt = logEventTrackerMap.find(activation.atom_matcher_id());
- if (logTrackerIt == logEventTrackerMap.end()) {
- ALOGE("Atom matcher not found for event activation.");
- return false;
- }
- const int atomMatcherIndex = logTrackerIt->second;
- activationAtomTrackerToMetricMap[atomMatcherIndex].push_back(
- metricTrackerIndex);
-
- ActivationType activationType;
- if (activation.has_activation_type()) {
- activationType = activation.activation_type();
- } else {
- activationType = metric_activation.activation_type();
- }
-
- if (activation.has_deactivation_atom_matcher_id()) {
- auto deactivationAtomMatcherIt =
- logEventTrackerMap.find(activation.deactivation_atom_matcher_id());
- if (deactivationAtomMatcherIt == logEventTrackerMap.end()) {
- ALOGE("Atom matcher not found for event deactivation.");
- return false;
- }
- const int deactivationMatcherIndex = deactivationAtomMatcherIt->second;
- deactivationAtomTrackerToMetricMap[deactivationMatcherIndex]
- .push_back(metricTrackerIndex);
- metric->addActivation(atomMatcherIndex, activationType, activation.ttl_seconds(),
- deactivationMatcherIndex);
- } else {
- metric->addActivation(atomMatcherIndex, activationType, activation.ttl_seconds());
- }
- }
- }
- return true;
-}
-
-void prepareFirstBucket(const vector<sp<MetricProducer>>& allMetricProducers) {
- for (const auto& metric: allMetricProducers) {
- metric->prepareFirstBucket();
- }
-}
-
bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap,
const sp<StatsPullerManager>& pullerManager,
const sp<AlarmMonitor>& anomalyAlarmMonitor,
@@ -794,6 +919,8 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap&
unordered_map<int64_t, int> logTrackerMap;
unordered_map<int64_t, int> conditionTrackerMap;
unordered_map<int64_t, int> metricProducerMap;
+ unordered_map<int64_t, int> stateAtomIdMap;
+ unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
if (!initLogTrackers(config, uidMap, logTrackerMap, allAtomMatchers, allTagIds)) {
ALOGE("initLogMatchingTrackers failed");
@@ -806,11 +933,16 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap&
ALOGE("initConditionTrackers failed");
return false;
}
-
+ if (!initStates(config, stateAtomIdMap, allStateGroupMaps)) {
+ ALOGE("initStates failed");
+ return false;
+ }
if (!initMetrics(key, config, timeBaseNs, currentTimeNs, uidMap, pullerManager, logTrackerMap,
- conditionTrackerMap, allAtomMatchers, allConditionTrackers, allMetricProducers,
+ conditionTrackerMap, allAtomMatchers, stateAtomIdMap, allStateGroupMaps,
+ allConditionTrackers, allMetricProducers,
conditionToMetricMap, trackerToMetricMap, metricProducerMap,
- noReportMetricIds)) {
+ noReportMetricIds, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
ALOGE("initMetricProducers failed");
return false;
}
@@ -824,14 +956,6 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap&
ALOGE("initAlarms failed");
return false;
}
- if (!initMetricActivations(key, config, currentTimeNs, logTrackerMap, metricProducerMap,
- allMetricProducers, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
- ALOGE("initMetricActivations failed");
- return false;
- }
-
- prepareFirstBucket(allMetricProducers);
return true;
}
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 3704969039c4..95b2ab81fa53 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -68,6 +68,17 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config,
std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks);
+// Initialize State maps using State protos in the config. These maps will
+// eventually be passed to MetricProducers to initialize their state info.
+// input:
+// [config]: the input config
+// output:
+// [stateAtomIdMap]: this map should contain the mapping from state ids to atom ids
+// [allStateGroupMaps]: this map should contain the mapping from states ids and state
+// values to state group ids for all states
+bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
+ unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps);
+
// Initialize MetricProducers.
// input:
// [key]: the config key that this config belongs to
@@ -75,6 +86,9 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config,
// [timeBaseSec]: start time base for all metrics
// [logTrackerMap]: LogMatchingTracker name to index mapping from previous step.
// [conditionTrackerMap]: condition name to index mapping
+// [stateAtomIdMap]: contains the mapping from state ids to atom ids
+// [allStateGroupMaps]: contains the mapping from atom ids and state values to
+// state group ids for all states
// output:
// [allMetricProducers]: contains the list of sp to the MetricProducers created.
// [conditionToMetricMap]: contains the mapping from condition tracker index to
@@ -87,11 +101,16 @@ bool initMetrics(
const std::unordered_map<int64_t, int>& conditionTrackerMap,
const std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
const vector<sp<LogMatchingTracker>>& allAtomMatchers,
+ const unordered_map<int64_t, int>& stateAtomIdMap,
+ const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
vector<sp<ConditionTracker>>& allConditionTrackers,
std::vector<sp<MetricProducer>>& allMetricProducers,
std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
- std::set<int64_t>& noReportMetricIds);
+ std::set<int64_t>& noReportMetricIds,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation);
// Initialize MetricsManager from StatsdConfig.
// Parameters are the members of MetricsManager. See MetricsManager for declaration.
diff --git a/cmds/statsd/src/socket/StatsSocketListener.cpp b/cmds/statsd/src/socket/StatsSocketListener.cpp
index 92200f99c3cc..b59d88dc1cea 100755
--- a/cmds/statsd/src/socket/StatsSocketListener.cpp
+++ b/cmds/statsd/src/socket/StatsSocketListener.cpp
@@ -56,8 +56,7 @@ bool StatsSocketListener::onDataAvailable(SocketClient* cli) {
}
// + 1 to ensure null terminator if MAX_PAYLOAD buffer is received
- char buffer[sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time) + LOGGER_ENTRY_MAX_PAYLOAD +
- 1];
+ char buffer[sizeof(android_log_header_t) + LOGGER_ENTRY_MAX_PAYLOAD + 1];
struct iovec iov = {buffer, sizeof(buffer) - 1};
alignas(4) char control[CMSG_SPACE(sizeof(struct ucred))];
diff --git a/cmds/statsd/src/state/StateListener.h b/cmds/statsd/src/state/StateListener.h
new file mode 100644
index 000000000000..a31690a102ed
--- /dev/null
+++ b/cmds/statsd/src/state/StateListener.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <utils/RefBase.h>
+
+#include "HashableDimensionKey.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class StateListener : public virtual RefBase {
+public:
+ StateListener(){};
+
+ virtual ~StateListener(){};
+
+ /**
+ * Interface for handling a state change.
+ *
+ * The old and new state values map to the original state values.
+ * StateTrackers only track the original state values and are unaware
+ * of higher-level state groups. MetricProducers hold information on
+ * state groups and are responsible for mapping original state values to
+ * the correct state group.
+ *
+ * [atomId]: The id of the state atom
+ * [primaryKey]: The primary field values of the state atom
+ * [oldState]: Previous state value before state change
+ * [newState]: Current state value after state change
+ */
+ virtual void onStateChanged(int atomId, const HashableDimensionKey& primaryKey, int oldState,
+ int newState) = 0;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/state/StateManager.cpp b/cmds/statsd/src/state/StateManager.cpp
new file mode 100644
index 000000000000..95b2c7691803
--- /dev/null
+++ b/cmds/statsd/src/state/StateManager.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG false // STOPSHIP if true
+#include "Log.h"
+
+#include "StateManager.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+StateManager& StateManager::getInstance() {
+ static StateManager sStateManager;
+ return sStateManager;
+}
+
+void StateManager::onLogEvent(const LogEvent& event) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mStateTrackers.find(event.GetTagId()) != mStateTrackers.end()) {
+ mStateTrackers[event.GetTagId()]->onLogEvent(event);
+ }
+}
+
+bool StateManager::registerListener(int atomId, wp<StateListener> listener) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ // Check if state tracker already exists
+ if (mStateTrackers.find(atomId) == mStateTrackers.end()) {
+ // Create a new state tracker iff atom is a state atom
+ auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(atomId);
+ if (it != android::util::AtomsInfo::kStateAtomsFieldOptions.end()) {
+ mStateTrackers[atomId] = new StateTracker(atomId, it->second);
+ } else {
+ ALOGE("StateManager cannot register listener, Atom %d is not a state atom", atomId);
+ return false;
+ }
+ }
+ mStateTrackers[atomId]->registerListener(listener);
+ return true;
+}
+
+void StateManager::unregisterListener(int atomId, wp<StateListener> listener) {
+ std::unique_lock<std::mutex> lock(mMutex);
+
+ // Hold the sp<> until the lock is released so that ~StateTracker() is
+ // not called while the lock is held.
+ sp<StateTracker> toRemove;
+
+ // Unregister listener from correct StateTracker
+ auto it = mStateTrackers.find(atomId);
+ if (it != mStateTrackers.end()) {
+ it->second->unregisterListener(listener);
+
+ // Remove the StateTracker if it has no listeners
+ if (it->second->getListenersCount() == 0) {
+ toRemove = it->second;
+ mStateTrackers.erase(it);
+ }
+ } else {
+ ALOGE("StateManager cannot unregister listener, StateTracker for atom %d does not exist",
+ atomId);
+ }
+ lock.unlock();
+}
+
+int StateManager::getStateValue(int atomId, const HashableDimensionKey& key) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mStateTrackers.find(atomId) != mStateTrackers.end()) {
+ return mStateTrackers[atomId]->getStateValue(key);
+ }
+
+ return StateTracker::kStateUnknown;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/state/StateManager.h b/cmds/statsd/src/state/StateManager.h
new file mode 100644
index 000000000000..89ee6c04b922
--- /dev/null
+++ b/cmds/statsd/src/state/StateManager.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <gtest/gtest_prod.h>
+#include <inttypes.h>
+#include <utils/RefBase.h>
+
+#include "HashableDimensionKey.h"
+#include "state/StateListener.h"
+#include "state/StateTracker.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class StateManager : public virtual RefBase {
+public:
+ StateManager(){};
+
+ ~StateManager(){};
+
+ // Returns a pointer to the single, shared StateManager object.
+ static StateManager& getInstance();
+
+ // Notifies the correct StateTracker of an event.
+ void onLogEvent(const LogEvent& event);
+
+ // Returns true if atomId is being tracked and is associated with a state
+ // atom. StateManager notifies the correct StateTracker to register listener.
+ // If the correct StateTracker does not exist, a new StateTracker is created.
+ bool registerListener(int atomId, wp<StateListener> listener);
+
+ // Notifies the correct StateTracker to unregister a listener
+ // and removes the tracker if it no longer has any listeners.
+ void unregisterListener(int atomId, wp<StateListener> listener);
+
+ // Queries the correct StateTracker for the original/un-mapped state value
+ // that is mapped to the given query key.
+ // If the StateTracker doesn't exist, returns StateTracker::kStateUnknown.
+ int getStateValue(int atomId, const HashableDimensionKey& queryKey);
+
+ inline int getStateTrackersCount() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mStateTrackers.size();
+ }
+
+ inline int getListenersCount(int atomId) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mStateTrackers.find(atomId) != mStateTrackers.end()) {
+ return mStateTrackers[atomId]->getListenersCount();
+ }
+ return -1;
+ }
+
+private:
+ mutable std::mutex mMutex;
+
+ // Maps state atom ids to StateTrackers
+ std::unordered_map<int, sp<StateTracker>> mStateTrackers;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp
new file mode 100644
index 000000000000..323fc0e94ab2
--- /dev/null
+++ b/cmds/statsd/src/state/StateTracker.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include "stats_util.h"
+
+#include "StateTracker.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+StateTracker::StateTracker(const int atomId,
+ const util::StateAtomFieldOptions& stateAtomInfo)
+ : mAtomId(atomId),
+ mStateField(getSimpleMatcher(atomId, stateAtomInfo.exclusiveField)) {
+ // create matcher for each primary field
+ // TODO(tsaichristine): b/142108433 handle when primary field is first uid in chain
+ for (const auto& primary : stateAtomInfo.primaryFields) {
+ Matcher matcher = getSimpleMatcher(atomId, primary);
+ mPrimaryFields.push_back(matcher);
+ }
+
+ // TODO(tsaichristine): b/142108433 set default state, reset state, and nesting
+}
+
+void StateTracker::onLogEvent(const LogEvent& event) {
+ // parse event for primary field values i.e. primary key
+ HashableDimensionKey primaryKey;
+ if (mPrimaryFields.size() > 0) {
+ if (!filterValues(mPrimaryFields, event.getValues(), &primaryKey) ||
+ primaryKey.getValues().size() != mPrimaryFields.size()) {
+ ALOGE("StateTracker error extracting primary key from log event.");
+ handleReset();
+ return;
+ }
+ } else {
+ // atom has no primary fields
+ primaryKey = DEFAULT_DIMENSION_KEY;
+ }
+
+ // parse event for state value
+ Value state;
+ int32_t stateValue;
+ if (!filterValues(mStateField, event.getValues(), &state) || state.getType() != INT) {
+ ALOGE("StateTracker error extracting state from log event. Type: %d", state.getType());
+ handlePartialReset(primaryKey);
+ return;
+ }
+ stateValue = state.int_value;
+
+ if (stateValue == mResetState) {
+ VLOG("StateTracker Reset state: %s", state.toString().c_str());
+ handleReset();
+ }
+
+ // track and update state
+ int32_t oldState = 0;
+ int32_t newState = 0;
+ updateState(primaryKey, stateValue, &oldState, &newState);
+
+ // notify all listeners if state has changed
+ if (oldState != newState) {
+ VLOG("StateTracker updated state");
+ for (auto listener : mListeners) {
+ auto sListener = listener.promote(); // safe access to wp<>
+ if (sListener != nullptr) {
+ sListener->onStateChanged(mAtomId, primaryKey, oldState, newState);
+ }
+ }
+ } else {
+ VLOG("StateTracker NO updated state");
+ }
+}
+
+void StateTracker::registerListener(wp<StateListener> listener) {
+ mListeners.insert(listener);
+}
+
+void StateTracker::unregisterListener(wp<StateListener> listener) {
+ mListeners.erase(listener);
+}
+
+int StateTracker::getStateValue(const HashableDimensionKey& queryKey) const {
+ if (queryKey.getValues().size() == mPrimaryFields.size()) {
+ auto it = mStateMap.find(queryKey);
+ if (it != mStateMap.end()) {
+ return it->second.state;
+ }
+ } else if (queryKey.getValues().size() > mPrimaryFields.size()) {
+ ALOGE("StateTracker query key size > primary key size is illegal");
+ } else {
+ ALOGE("StateTracker query key size < primary key size is not supported");
+ }
+ return mDefaultState;
+}
+
+void StateTracker::handleReset() {
+ VLOG("StateTracker handle reset");
+ for (const auto pair : mStateMap) {
+ for (auto l : mListeners) {
+ auto sl = l.promote();
+ if (sl != nullptr) {
+ sl->onStateChanged(mAtomId, pair.first, pair.second.state, mDefaultState);
+ }
+ }
+ }
+ mStateMap.clear();
+}
+
+void StateTracker::handlePartialReset(const HashableDimensionKey& primaryKey) {
+ VLOG("StateTracker handle partial reset");
+ if (mStateMap.find(primaryKey) != mStateMap.end()) {
+ mStateMap.erase(primaryKey);
+ }
+}
+
+void StateTracker::updateState(const HashableDimensionKey& primaryKey, const int32_t eventState,
+ int32_t* oldState, int32_t* newState) {
+ // get old state (either current state in map or default state)
+ auto it = mStateMap.find(primaryKey);
+ if (it != mStateMap.end()) {
+ *oldState = it->second.state;
+ } else {
+ *oldState = mDefaultState;
+ }
+
+ // update state map
+ if (eventState == mDefaultState) {
+ // remove (key, state) pair if state returns to default state
+ VLOG("\t StateTracker changed to default state")
+ mStateMap.erase(primaryKey);
+ } else {
+ mStateMap[primaryKey].state = eventState;
+ mStateMap[primaryKey].count = 1;
+ }
+ *newState = eventState;
+
+ // TODO(tsaichristine): support atoms with nested counting
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/state/StateTracker.h b/cmds/statsd/src/state/StateTracker.h
new file mode 100644
index 000000000000..cfa9fd8f6272
--- /dev/null
+++ b/cmds/statsd/src/state/StateTracker.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <statslog.h>
+#include <utils/RefBase.h>
+#include "HashableDimensionKey.h"
+#include "logd/LogEvent.h"
+
+#include "state/StateListener.h"
+
+#include <unordered_map>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class StateTracker : public virtual RefBase {
+public:
+ StateTracker(const int atomId, const util::StateAtomFieldOptions& stateAtomInfo);
+
+ virtual ~StateTracker(){};
+
+ // Updates state map and notifies all listeners if a state change occurs.
+ // Checks if a state change has occurred by getting the state value from
+ // the log event and comparing the old and new states.
+ void onLogEvent(const LogEvent& event);
+
+ // Adds new listeners to set of StateListeners. If a listener is already
+ // registered, it is ignored.
+ void registerListener(wp<StateListener> listener);
+
+ void unregisterListener(wp<StateListener> listener);
+
+ // Returns the state value mapped to the given query key.
+ // If the key isn't mapped to a state or the key size doesn't match the
+ // primary key size, the default state is returned.
+ int getStateValue(const HashableDimensionKey& queryKey) const;
+
+ inline int getListenersCount() const {
+ return mListeners.size();
+ }
+
+ const static int kStateUnknown = -1;
+
+private:
+ struct StateValueInfo {
+ int32_t state; // state value
+ int count; // nested count (only used for binary states)
+ };
+
+ const int32_t mAtomId; // id of the state atom being tracked
+
+ Matcher mStateField; // matches the atom's exclusive state field
+
+ std::vector<Matcher> mPrimaryFields; // matches the atom's primary fields
+
+ int32_t mDefaultState = kStateUnknown;
+
+ int32_t mResetState;
+
+ // Maps primary key to state value info
+ std::unordered_map<HashableDimensionKey, StateValueInfo> mStateMap;
+
+ // Set of all StateListeners (objects listening for state changes)
+ std::set<wp<StateListener>> mListeners;
+
+ // Reset all state values in map to default state
+ void handleReset();
+
+ // Reset only the state value mapped to primary key to default state
+ void handlePartialReset(const HashableDimensionKey& primaryKey);
+
+ // Update the StateMap based on the received state value.
+ // Store the old and new states.
+ void updateState(const HashableDimensionKey& primaryKey, const int32_t eventState,
+ int32_t* oldState, int32_t* newState);
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index d9c04f248af0..e45e24fe49d4 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -41,6 +41,15 @@ message DimensionsValueTuple {
repeated DimensionsValue dimensions_value = 1;
}
+message StateValue {
+ optional int32 atom_id = 1;
+
+ oneof contents {
+ int64 group_id = 2;
+ int32 value = 3;
+ }
+}
+
message EventMetricData {
optional int64 elapsed_timestamp_nanos = 1;
@@ -66,12 +75,14 @@ message CountBucketInfo {
message CountMetricData {
optional DimensionsValue dimensions_in_what = 1;
- optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
+ repeated StateValue slice_by_state = 6;
repeated CountBucketInfo bucket_info = 3;
repeated DimensionsValue dimension_leaf_values_in_what = 4;
+ optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
+
repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
}
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 67625eb82454..c22e3cc07a3b 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -445,6 +445,8 @@ int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit) {
return 12 * 60 * 60 * 1000LL;
case ONE_DAY:
return 24 * 60 * 60 * 1000LL;
+ case ONE_WEEK:
+ return 7 * 24 * 60 * 60 * 1000LL;
case CTS:
return 1000;
case TIME_UNIT_UNSPECIFIED:
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 1b7f39806608..0664867aa866 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -35,7 +35,7 @@ enum Position {
enum TimeUnit {
TIME_UNIT_UNSPECIFIED = 0;
- ONE_MINUTE = 1;
+ ONE_MINUTE = 1; // WILL BE GUARDRAILED TO 5 MINS UNLESS UID = SHELL OR ROOT
FIVE_MINUTES = 2;
TEN_MINUTES = 3;
THIRTY_MINUTES = 4;
@@ -44,6 +44,7 @@ enum TimeUnit {
SIX_HOURS = 7;
TWELVE_HOURS = 8;
ONE_DAY = 9;
+ ONE_WEEK = 10;
CTS = 1000;
}
@@ -150,6 +151,24 @@ message Predicate {
}
}
+message StateMap {
+ message StateGroup {
+ optional int64 group_id = 1;
+
+ repeated int32 value = 2;
+ }
+
+ repeated StateGroup group = 1;
+}
+
+message State {
+ optional int64 id = 1;
+
+ optional int32 atom_id = 2;
+
+ optional StateMap map = 3;
+}
+
message MetricConditionLink {
optional int64 condition = 1;
@@ -158,6 +177,14 @@ message MetricConditionLink {
optional FieldMatcher fields_in_condition = 3;
}
+message MetricStateLink {
+ optional int64 state = 1;
+
+ optional FieldMatcher fields_in_what = 2;
+
+ optional FieldMatcher fields_in_state = 3;
+}
+
message FieldFilter {
optional bool include_all = 1 [default = false];
optional FieldMatcher fields = 2;
@@ -182,11 +209,15 @@ message CountMetric {
optional FieldMatcher dimensions_in_what = 4;
- optional FieldMatcher dimensions_in_condition = 7 [deprecated = true];
+ repeated int64 slice_by_state = 8;
optional TimeUnit bucket = 5;
repeated MetricConditionLink links = 6;
+
+ repeated MetricStateLink state_link = 9;
+
+ optional FieldMatcher dimensions_in_condition = 7 [deprecated = true];
}
message DurationMetric {
@@ -438,6 +469,8 @@ message StatsdConfig {
optional bool persist_locally = 20 [default = false];
+ repeated State state = 21;
+
// Field number 1000 is reserved for later use.
reserved 1000;
}
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 504ee22f72e4..0743480bf4ee 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -582,7 +582,7 @@ TEST(LogEventTest, TestBinaryFieldAtom) {
event1.write((int32_t)stats::launcher::LauncherAction::LONGPRESS);
event1.write((int32_t)stats::launcher::LauncherState::OVERVIEW);
event1.write((int64_t)stats::launcher::LauncherState::ALLAPPS);
- event1.write(extension_str);
+ event1.writeBytes(extension_str);
event1.init();
ProtoOutputStream proto;
@@ -621,7 +621,7 @@ TEST(LogEventTest, TestBinaryFieldAtom_empty) {
event1.write((int32_t)stats::launcher::LauncherAction::LONGPRESS);
event1.write((int32_t)stats::launcher::LauncherState::OVERVIEW);
event1.write((int64_t)stats::launcher::LauncherState::ALLAPPS);
- event1.write(extension_str);
+ event1.writeBytes(extension_str);
event1.init();
ProtoOutputStream proto;
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index fe25a257aa67..460b9e0995c8 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -1199,87 +1199,66 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi
ConfigKey cfgKey1(uid, 12341);
long timeBase1 = 1;
- sp<StatsLogProcessor> processor =
+ sp<StatsLogProcessor> processor1 =
CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
// Metric 1 is not active.
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_EQ(1, processor->mMetricsManagers.size());
- auto it = processor->mMetricsManagers.find(cfgKey1);
- EXPECT_TRUE(it != processor->mMetricsManagers.end());
+ EXPECT_EQ(1, processor1->mMetricsManagers.size());
+ auto it = processor1->mMetricsManagers.find(cfgKey1);
+ EXPECT_TRUE(it != processor1->mMetricsManagers.end());
auto& metricsManager1 = it->second;
EXPECT_TRUE(metricsManager1->isActive());
- auto metricIt = metricsManager1->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId1) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
- auto& metricProducer1 = *metricIt;
- EXPECT_FALSE(metricProducer1->isActive());
-
- metricIt = metricsManager1->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId2) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
- auto& metricProducer2 = *metricIt;
- EXPECT_TRUE(metricProducer2->isActive());
-
- int i = 0;
- for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
- if (metricsManager1->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger1->atom_matcher_id()) {
- break;
- }
- }
- const auto& activation1 = metricProducer1->mEventActivationMap.at(i);
- EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
- EXPECT_EQ(0, activation1->start_ns);
- EXPECT_EQ(kNotActive, activation1->state);
- EXPECT_EQ(ACTIVATE_ON_BOOT, activation1->activationType);
-
- i = 0;
- for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
- if (metricsManager1->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger2->atom_matcher_id()) {
- break;
- }
- }
- const auto& activation2 = metricProducer1->mEventActivationMap.at(i);
- EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns);
- EXPECT_EQ(0, activation2->start_ns);
- EXPECT_EQ(kNotActive, activation2->state);
- EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2->activationType);
+ EXPECT_EQ(metricsManager1->mAllMetricProducers.size(), 2);
+ // We assume that the index of a MetricProducer within the mAllMetricProducers
+ // array follows the order in which metrics are added to the config.
+ auto& metricProducer1_1 = metricsManager1->mAllMetricProducers[0];
+ EXPECT_EQ(metricProducer1_1->getMetricId(), metricId1);
+ EXPECT_FALSE(metricProducer1_1->isActive()); // inactive due to associated MetricActivation
+
+ auto& metricProducer1_2 = metricsManager1->mAllMetricProducers[1];
+ EXPECT_EQ(metricProducer1_2->getMetricId(), metricId2);
+ EXPECT_TRUE(metricProducer1_2->isActive());
+
+ EXPECT_EQ(metricProducer1_1->mEventActivationMap.size(), 2);
+ // The key in mEventActivationMap is the index of the associated atom matcher. We assume
+ // that matchers are indexed in the order that they are added to the config.
+ const auto& activation1_1_1 = metricProducer1_1->mEventActivationMap.at(0);
+ EXPECT_EQ(100 * NS_PER_SEC, activation1_1_1->ttl_ns);
+ EXPECT_EQ(0, activation1_1_1->start_ns);
+ EXPECT_EQ(kNotActive, activation1_1_1->state);
+ EXPECT_EQ(ACTIVATE_ON_BOOT, activation1_1_1->activationType);
+
+ const auto& activation1_1_2 = metricProducer1_1->mEventActivationMap.at(1);
+ EXPECT_EQ(200 * NS_PER_SEC, activation1_1_2->ttl_ns);
+ EXPECT_EQ(0, activation1_1_2->start_ns);
+ EXPECT_EQ(kNotActive, activation1_1_2->state);
+ EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1_1_2->activationType);
// }}}------------------------------------------------------------------------------
// Trigger Activation 1 for Metric 1
std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1);
- processor->OnLogEvent(event.get());
+ processor1->OnLogEvent(event.get());
// Metric 1 is not active; Activation 1 set to kActiveOnBoot
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_FALSE(metricProducer1->isActive());
- EXPECT_EQ(0, activation1->start_ns);
- EXPECT_EQ(kActiveOnBoot, activation1->state);
- EXPECT_EQ(0, activation2->start_ns);
- EXPECT_EQ(kNotActive, activation2->state);
+ EXPECT_FALSE(metricProducer1_1->isActive());
+ EXPECT_EQ(0, activation1_1_1->start_ns);
+ EXPECT_EQ(kActiveOnBoot, activation1_1_1->state);
+ EXPECT_EQ(0, activation1_1_2->start_ns);
+ EXPECT_EQ(kNotActive, activation1_1_2->state);
- EXPECT_TRUE(metricProducer2->isActive());
+ EXPECT_TRUE(metricProducer1_2->isActive());
// }}}-----------------------------------------------------------------------------
// Simulate shutdown by saving state to disk
int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
- processor->SaveActiveConfigsToDisk(shutDownTime);
- EXPECT_FALSE(metricProducer1->isActive());
- int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC;
+ processor1->SaveActiveConfigsToDisk(shutDownTime);
+ EXPECT_FALSE(metricProducer1_1->isActive());
// Simulate device restarted state by creating new instance of StatsLogProcessor with the
// same config.
@@ -1293,55 +1272,34 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi
EXPECT_EQ(1, processor2->mMetricsManagers.size());
it = processor2->mMetricsManagers.find(cfgKey1);
EXPECT_TRUE(it != processor2->mMetricsManagers.end());
- auto& metricsManager1001 = it->second;
- EXPECT_TRUE(metricsManager1001->isActive());
-
- metricIt = metricsManager1001->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId1) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
- auto& metricProducer1001 = *metricIt;
- EXPECT_FALSE(metricProducer1001->isActive());
-
- metricIt = metricsManager1001->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId2) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
- auto& metricProducer1002 = *metricIt;
- EXPECT_TRUE(metricProducer1002->isActive());
-
- i = 0;
- for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
- if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger1->atom_matcher_id()) {
- break;
- }
- }
- const auto& activation1001_1 = metricProducer1001->mEventActivationMap.at(i);
- EXPECT_EQ(100 * NS_PER_SEC, activation1001_1->ttl_ns);
- EXPECT_EQ(0, activation1001_1->start_ns);
- EXPECT_EQ(kNotActive, activation1001_1->state);
- EXPECT_EQ(ACTIVATE_ON_BOOT, activation1001_1->activationType);
-
- i = 0;
- for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
- if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger2->atom_matcher_id()) {
- break;
- }
- }
+ auto& metricsManager2 = it->second;
+ EXPECT_TRUE(metricsManager2->isActive());
- const auto& activation1001_2 = metricProducer1001->mEventActivationMap.at(i);
- EXPECT_EQ(200 * NS_PER_SEC, activation1001_2->ttl_ns);
- EXPECT_EQ(0, activation1001_2->start_ns);
- EXPECT_EQ(kNotActive, activation1001_2->state);
- EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1001_2->activationType);
+ EXPECT_EQ(metricsManager2->mAllMetricProducers.size(), 2);
+ // We assume that the index of a MetricProducer within the mAllMetricProducers
+ // array follows the order in which metrics are added to the config.
+ auto& metricProducer2_1 = metricsManager2->mAllMetricProducers[0];
+ EXPECT_EQ(metricProducer2_1->getMetricId(), metricId1);
+ EXPECT_FALSE(metricProducer2_1->isActive());
+
+ auto& metricProducer2_2 = metricsManager2->mAllMetricProducers[1];
+ EXPECT_EQ(metricProducer2_2->getMetricId(), metricId2);
+ EXPECT_TRUE(metricProducer2_2->isActive());
+
+ EXPECT_EQ(metricProducer2_1->mEventActivationMap.size(), 2);
+ // The key in mEventActivationMap is the index of the associated atom matcher. We assume
+ // that matchers are indexed in the order that they are added to the config.
+ const auto& activation2_1_1 = metricProducer2_1->mEventActivationMap.at(0);
+ EXPECT_EQ(100 * NS_PER_SEC, activation2_1_1->ttl_ns);
+ EXPECT_EQ(0, activation2_1_1->start_ns);
+ EXPECT_EQ(kNotActive, activation2_1_1->state);
+ EXPECT_EQ(ACTIVATE_ON_BOOT, activation2_1_1->activationType);
+
+ const auto& activation2_1_2 = metricProducer2_1->mEventActivationMap.at(1);
+ EXPECT_EQ(200 * NS_PER_SEC, activation2_1_2->ttl_ns);
+ EXPECT_EQ(0, activation2_1_2->start_ns);
+ EXPECT_EQ(kNotActive, activation2_1_2->state);
+ EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2_1_2->activationType);
// }}}-----------------------------------------------------------------------------------
// Load saved state from disk.
@@ -1350,13 +1308,14 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi
// Metric 1 active; Activation 1 is active, Activation 2 is not active
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_TRUE(metricProducer1001->isActive());
- EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns);
- EXPECT_EQ(kActive, activation1001_1->state);
- EXPECT_EQ(0, activation1001_2->start_ns);
- EXPECT_EQ(kNotActive, activation1001_2->state);
+ EXPECT_TRUE(metricProducer2_1->isActive());
+ int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC;
+ EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns);
+ EXPECT_EQ(kActive, activation2_1_1->state);
+ EXPECT_EQ(0, activation2_1_2->start_ns);
+ EXPECT_EQ(kNotActive, activation2_1_2->state);
- EXPECT_TRUE(metricProducer1002->isActive());
+ EXPECT_TRUE(metricProducer2_2->isActive());
// }}}--------------------------------------------------------------------------------
// Trigger Activation 2 for Metric 1.
@@ -1369,23 +1328,23 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi
// Metric 1 active; Activation 1 is active, Activation 2 is active
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_TRUE(metricProducer1001->isActive());
- EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns);
- EXPECT_EQ(kActive, activation1001_1->state);
- EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation1001_2->start_ns);
- EXPECT_EQ(kActive, activation1001_2->state);
+ EXPECT_TRUE(metricProducer2_1->isActive());
+ EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns);
+ EXPECT_EQ(kActive, activation2_1_1->state);
+ EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation2_1_2->start_ns);
+ EXPECT_EQ(kActive, activation2_1_2->state);
- EXPECT_TRUE(metricProducer1002->isActive());
+ EXPECT_TRUE(metricProducer2_2->isActive());
// }}}---------------------------------------------------------------------------
// Simulate shutdown by saving state to disk
shutDownTime = timeBase2 + 50 * NS_PER_SEC;
processor2->SaveActiveConfigsToDisk(shutDownTime);
- EXPECT_TRUE(metricProducer1001->isActive());
- EXPECT_TRUE(metricProducer1002->isActive());
- ttl1 = timeBase2 + metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC - shutDownTime;
- int64_t ttl2 = screenOnEvent->GetElapsedTimestampNs() +
- metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC - shutDownTime;
+ EXPECT_TRUE(metricProducer2_1->isActive());
+ EXPECT_TRUE(metricProducer2_2->isActive());
+ ttl1 -= shutDownTime - timeBase2;
+ int64_t ttl2 = metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC
+ - (shutDownTime - screenOnEvent->GetElapsedTimestampNs());
// Simulate device restarted state by creating new instance of StatsLogProcessor with the
// same config.
@@ -1399,55 +1358,34 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi
EXPECT_EQ(1, processor3->mMetricsManagers.size());
it = processor3->mMetricsManagers.find(cfgKey1);
EXPECT_TRUE(it != processor3->mMetricsManagers.end());
- auto& metricsManagerTimeBase3 = it->second;
- EXPECT_TRUE(metricsManagerTimeBase3->isActive());
-
- metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin();
- for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId1) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end());
- auto& metricProducerTimeBase3_1 = *metricIt;
- EXPECT_FALSE(metricProducerTimeBase3_1->isActive());
-
- metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin();
- for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId2) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end());
- auto& metricProducerTimeBase3_2 = *metricIt;
- EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
-
- i = 0;
- for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
- if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger1->atom_matcher_id()) {
- break;
- }
- }
- const auto& activationTimeBase3_1 = metricProducerTimeBase3_1->mEventActivationMap.at(i);
- EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase3_1->ttl_ns);
- EXPECT_EQ(0, activationTimeBase3_1->start_ns);
- EXPECT_EQ(kNotActive, activationTimeBase3_1->state);
- EXPECT_EQ(ACTIVATE_ON_BOOT, activationTimeBase3_1->activationType);
-
- i = 0;
- for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
- if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger2->atom_matcher_id()) {
- break;
- }
- }
+ auto& metricsManager3 = it->second;
+ EXPECT_TRUE(metricsManager3->isActive());
- const auto& activationTimeBase3_2 = metricProducerTimeBase3_1->mEventActivationMap.at(i);
- EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase3_2->ttl_ns);
- EXPECT_EQ(0, activationTimeBase3_2->start_ns);
- EXPECT_EQ(kNotActive, activationTimeBase3_2->state);
- EXPECT_EQ(ACTIVATE_IMMEDIATELY, activationTimeBase3_2->activationType);
+ EXPECT_EQ(metricsManager3->mAllMetricProducers.size(), 2);
+ // We assume that the index of a MetricProducer within the mAllMetricProducers
+ // array follows the order in which metrics are added to the config.
+ auto& metricProducer3_1 = metricsManager3->mAllMetricProducers[0];
+ EXPECT_EQ(metricProducer3_1->getMetricId(), metricId1);
+ EXPECT_FALSE(metricProducer3_1->isActive());
+
+ auto& metricProducer3_2 = metricsManager3->mAllMetricProducers[1];
+ EXPECT_EQ(metricProducer3_2->getMetricId(), metricId2);
+ EXPECT_TRUE(metricProducer3_2->isActive());
+
+ EXPECT_EQ(metricProducer3_1->mEventActivationMap.size(), 2);
+ // The key in mEventActivationMap is the index of the associated atom matcher. We assume
+ // that matchers are indexed in the order that they are added to the config.
+ const auto& activation3_1_1 = metricProducer3_1->mEventActivationMap.at(0);
+ EXPECT_EQ(100 * NS_PER_SEC, activation3_1_1->ttl_ns);
+ EXPECT_EQ(0, activation3_1_1->start_ns);
+ EXPECT_EQ(kNotActive, activation3_1_1->state);
+ EXPECT_EQ(ACTIVATE_ON_BOOT, activation3_1_1->activationType);
+
+ const auto& activation3_1_2 = metricProducer3_1->mEventActivationMap.at(1);
+ EXPECT_EQ(200 * NS_PER_SEC, activation3_1_2->ttl_ns);
+ EXPECT_EQ(0, activation3_1_2->start_ns);
+ EXPECT_EQ(kNotActive, activation3_1_2->state);
+ EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation3_1_2->activationType);
// }}}----------------------------------------------------------------------------------
// Load saved state from disk.
@@ -1456,13 +1394,13 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi
// Metric 1 active: Activation 1 is active, Activation 2 is active
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_TRUE(metricProducerTimeBase3_1->isActive());
- EXPECT_EQ(timeBase3 + ttl1 - activationTimeBase3_1->ttl_ns, activationTimeBase3_1->start_ns);
- EXPECT_EQ(kActive, activationTimeBase3_1->state);
- EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns);
- EXPECT_EQ(kActive, activationTimeBase3_2->state);
+ EXPECT_TRUE(metricProducer3_1->isActive());
+ EXPECT_EQ(timeBase3 + ttl1 - activation3_1_1->ttl_ns, activation3_1_1->start_ns);
+ EXPECT_EQ(kActive, activation3_1_1->state);
+ EXPECT_EQ(timeBase3 + ttl2 - activation3_1_2->ttl_ns, activation3_1_2->start_ns);
+ EXPECT_EQ(kActive, activation3_1_2->state);
- EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
+ EXPECT_TRUE(metricProducer3_2->isActive());
// }}}-------------------------------------------------------------------------------
@@ -1473,15 +1411,16 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi
);
processor3->OnLogEvent(screenOnEvent.get());
- // Metric 1 active; Activation 1 is not active, Activation 2 is set to active
+ // Metric 1 active; Activation 1 is inactive (above screenOnEvent causes ttl1 to expire),
+ // Activation 2 is set to active
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_TRUE(metricProducerTimeBase3_1->isActive());
- EXPECT_EQ(kNotActive, activationTimeBase3_1->state);
- EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activationTimeBase3_2->start_ns);
- EXPECT_EQ(kActive, activationTimeBase3_2->state);
+ EXPECT_TRUE(metricProducer3_1->isActive());
+ EXPECT_EQ(kNotActive, activation3_1_1->state);
+ EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation3_1_2->start_ns);
+ EXPECT_EQ(kActive, activation3_1_2->state);
- EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
+ EXPECT_TRUE(metricProducer3_2->isActive());
// }}}---------------------------------------------------------------------------
}
@@ -1713,6 +1652,11 @@ TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) {
EXPECT_EQ(kActive, activation1004->state);
EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1004->activationType);
// }}}------------------------------------------------------------------------------
+
+ // Clear the data stored on disk as a result of the system server death.
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey1, configAddedTimeNs + NS_PER_SEC, false, true,
+ ADB_DUMP, FAST, &buffer);
}
#else
diff --git a/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp b/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp
index b98dc60086ac..325e869e5a9b 100644
--- a/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp
@@ -96,6 +96,11 @@ TEST(ConfigTtlE2eTest, TestCountMetric) {
EXPECT_EQ((int64_t)(bucketStartTimeNs + 25 * bucketSizeNs + 2 + 2 * 3600 * NS_PER_SEC),
processor->mMetricsManagers.begin()->second->getTtlEndNs());
+
+ // Clear the data stored on disk as a result of the ttl.
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 25 * bucketSizeNs + 3, false, true,
+ ADB_DUMP, FAST, &buffer);
}
diff --git a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
new file mode 100644
index 000000000000..6591d69cdc1a
--- /dev/null
+++ b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "src/StatsLogProcessor.h"
+#include "src/state/StateManager.h"
+#include "tests/statsd_test_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+TEST(CountMetricE2eTest, TestWithSimpleState) {
+ // Initialize config
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
+ auto syncStartMatcher = CreateSyncStartAtomMatcher();
+ *config.add_atom_matcher() = syncStartMatcher;
+
+ auto state = CreateScreenState();
+ *config.add_state() = state;
+
+ // Create count metric that slices by screen state
+ int64_t metricId = 123456;
+ auto countMetric = config.add_count_metric();
+ countMetric->set_id(metricId);
+ countMetric->set_what(syncStartMatcher.id());
+ countMetric->set_bucket(TimeUnit::ONE_MINUTE);
+ countMetric->add_slice_by_state(state.id());
+
+ // Initialize StatsLogProcessor
+ const int64_t baseTimeNs = 0; // 0:00
+ const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01
+ const int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
+
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+
+ auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey);
+
+ // Check that StateTrackers were properly initialized.
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1,
+ StateManager::getInstance().getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+ // Check that CountMetricProducer was initialized correctly.
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
+ EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), android::util::SCREEN_STATE_CHANGED);
+ EXPECT_EQ(metricProducer->mStateGroupMap.size(), 0);
+}
+
+TEST(CountMetricE2eTest, TestWithMappedState) {
+ // Initialize config
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
+ auto syncStartMatcher = CreateSyncStartAtomMatcher();
+ *config.add_atom_matcher() = syncStartMatcher;
+
+ auto state = CreateScreenStateWithOnOffMap();
+ *config.add_state() = state;
+
+ // Create count metric that slices by screen state with on/off map
+ int64_t metricId = 123456;
+ auto countMetric = config.add_count_metric();
+ countMetric->set_id(metricId);
+ countMetric->set_what(syncStartMatcher.id());
+ countMetric->set_bucket(TimeUnit::ONE_MINUTE);
+ countMetric->add_slice_by_state(state.id());
+
+ // Initialize StatsLogProcessor
+ const int64_t baseTimeNs = 0; // 0:00
+ const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01
+ const int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
+
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+
+ auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey);
+
+ // Check that StateTrackers were properly initialized.
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1,
+ StateManager::getInstance().getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+ // Check that CountMetricProducer was initialized correctly.
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
+ EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), android::util::SCREEN_STATE_CHANGED);
+ EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1);
+
+ StateMap map = state.map();
+ for (auto group : map.group()) {
+ for (auto value : group.value()) {
+ EXPECT_EQ(metricProducer->mStateGroupMap[android::util::SCREEN_STATE_CHANGED][value],
+ group.group_id());
+ }
+ }
+}
+
+TEST(CountMetricE2eTest, TestWithMultipleStates) {
+ // Initialize config
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
+ auto syncStartMatcher = CreateSyncStartAtomMatcher();
+ *config.add_atom_matcher() = syncStartMatcher;
+
+ auto state1 = CreateScreenStateWithOnOffMap();
+ *config.add_state() = state1;
+ auto state2 = CreateUidProcessState();
+ *config.add_state() = state2;
+
+ // Create count metric that slices by screen state with on/off map
+ int64_t metricId = 123456;
+ auto countMetric = config.add_count_metric();
+ countMetric->set_id(metricId);
+ countMetric->set_what(syncStartMatcher.id());
+ countMetric->set_bucket(TimeUnit::ONE_MINUTE);
+ countMetric->add_slice_by_state(state1.id());
+ countMetric->add_slice_by_state(state2.id());
+
+ // Initialize StatsLogProcessor
+ const int64_t baseTimeNs = 0; // 0:00
+ const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01
+ const int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
+
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+
+ auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey);
+
+ // Check that StateTrackers were properly initialized.
+ EXPECT_EQ(2, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1,
+ StateManager::getInstance().getListenersCount(android::util::SCREEN_STATE_CHANGED));
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
+ android::util::UID_PROCESS_STATE_CHANGED));
+
+ // Check that CountMetricProducer was initialized correctly.
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 2);
+ EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), android::util::SCREEN_STATE_CHANGED);
+ EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(1), android::util::UID_PROCESS_STATE_CHANGED);
+ EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1);
+
+ StateMap map = state1.map();
+ for (auto group : map.group()) {
+ for (auto value : group.value()) {
+ EXPECT_EQ(metricProducer->mStateGroupMap[android::util::SCREEN_STATE_CHANGED][value],
+ group.group_id());
+ }
+ }
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/external/SurfaceflingerStatsPuller_test.cpp b/cmds/statsd/tests/external/SurfaceflingerStatsPuller_test.cpp
new file mode 100644
index 000000000000..5c9636fa99cc
--- /dev/null
+++ b/cmds/statsd/tests/external/SurfaceflingerStatsPuller_test.cpp
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SurfaceflingerStatsPuller_test"
+
+#include "src/external/SurfaceflingerStatsPuller.h"
+
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class TestableSurfaceflingerStatsPuller : public SurfaceflingerStatsPuller {
+public:
+ TestableSurfaceflingerStatsPuller(const int tagId) : SurfaceflingerStatsPuller(tagId){};
+
+ void injectStats(const StatsProvider& statsProvider) {
+ mStatsProvider = statsProvider;
+ }
+};
+
+class SurfaceflingerStatsPullerTest : public ::testing::Test {
+public:
+ SurfaceflingerStatsPullerTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ ~SurfaceflingerStatsPullerTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+};
+
+TEST_F(SurfaceflingerStatsPullerTest, pullGlobalStats) {
+ surfaceflinger::SFTimeStatsGlobalProto proto;
+ proto.set_total_frames(1);
+ proto.set_missed_frames(2);
+ proto.set_client_composition_frames(2);
+ proto.set_display_on_time(4);
+
+ auto bucketOne = proto.add_present_to_present();
+ bucketOne->set_time_millis(2);
+ bucketOne->set_frame_count(4);
+ auto bucketTwo = proto.add_present_to_present();
+ bucketTwo->set_time_millis(4);
+ bucketTwo->set_frame_count(1);
+ auto bucketThree = proto.add_present_to_present();
+ bucketThree->set_time_millis(1000);
+ bucketThree->set_frame_count(1);
+ static constexpr int64_t expectedAnimationMillis = 12;
+ TestableSurfaceflingerStatsPuller puller(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
+
+ puller.injectStats([&] {
+ return proto.SerializeAsString();
+ });
+ puller.ForceClearCache();
+ vector<std::shared_ptr<LogEvent>> outData;
+ puller.Pull(&outData);
+
+ ASSERT_EQ(1, outData.size());
+ EXPECT_EQ(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, outData[0]->GetTagId());
+ EXPECT_EQ(proto.total_frames(), outData[0]->getValues()[0].mValue.long_value);
+ EXPECT_EQ(proto.missed_frames(), outData[0]->getValues()[1].mValue.long_value);
+ EXPECT_EQ(proto.client_composition_frames(), outData[0]->getValues()[2].mValue.long_value);
+ EXPECT_EQ(proto.display_on_time(), outData[0]->getValues()[3].mValue.long_value);
+ EXPECT_EQ(expectedAnimationMillis, outData[0]->getValues()[4].mValue.long_value);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 839daa4b7f8a..8915c7364bc7 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -43,8 +43,8 @@ TEST(CountMetricProducerTest, TestFirstBucket) {
metric.set_bucket(ONE_MINUTE);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
- 5, 600 * NS_PER_SEC + NS_PER_SEC/2);
+ CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 5,
+ 600 * NS_PER_SEC + NS_PER_SEC / 2);
EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(10, countProducer.mCurrentBucketNum);
EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs());
@@ -131,7 +131,8 @@ TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) {
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs, bucketStartTimeNs);
+ CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs,
+ bucketStartTimeNs);
countProducer.onConditionChanged(true, bucketStartTimeNs);
countProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
@@ -396,6 +397,26 @@ TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) {
std::ceil(1.0 * event7.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
}
+TEST(CountMetricProducerTest, TestOneWeekTimeUnit) {
+ CountMetric metric;
+ metric.set_id(1);
+ metric.set_bucket(ONE_WEEK);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ int64_t oneDayNs = 24 * 60 * 60 * 1e9;
+ int64_t fiveWeeksNs = 5 * 7 * oneDayNs;
+
+ CountMetricProducer countProducer(
+ kConfigKey, metric, -1 /* meaning no condition */, wizard, oneDayNs, fiveWeeksNs);
+
+ int64_t fiveWeeksOneDayNs = fiveWeeksNs + oneDayNs;
+
+ EXPECT_EQ(fiveWeeksNs, countProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(4, countProducer.mCurrentBucketNum);
+ EXPECT_EQ(fiveWeeksOneDayNs, countProducer.getCurrentBucketEndTimeNs());
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 47c21aa8f1df..b027e8e687db 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -79,8 +79,6 @@ TEST(GaugeMetricProducerTest, TestFirstBucket) {
logEventMatcherIndex, eventMatcherWizard,
-1, -1, tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2,
pullerManager);
- gaugeProducer.prepareFirstBucket();
-
EXPECT_EQ(600500000000, gaugeProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(10, gaugeProducer.mCurrentBucketNum);
@@ -126,8 +124,6 @@ TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) {
tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
- gaugeProducer.prepareFirstBucket();
-
vector<shared_ptr<LogEvent>> allData;
allData.clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
@@ -211,7 +207,6 @@ TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) {
logEventMatcherIndex, eventMatcherWizard,
-1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- gaugeProducer.prepareFirstBucket();
sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor);
EXPECT_TRUE(anomalyTracker != nullptr);
@@ -303,7 +298,6 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) {
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- gaugeProducer.prepareFirstBucket();
vector<shared_ptr<LogEvent>> allData;
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
@@ -370,7 +364,6 @@ TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
logEventMatcherIndex, eventMatcherWizard,
tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
- gaugeProducer.prepareFirstBucket();
vector<shared_ptr<LogEvent>> allData;
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
@@ -431,7 +424,6 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) {
GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard,
logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- gaugeProducer.prepareFirstBucket();
gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8);
EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
@@ -521,7 +513,6 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) {
GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard,
logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- gaugeProducer.prepareFirstBucket();
gaugeProducer.onSlicedConditionMayChange(true, bucketStartTimeNs + 8);
@@ -572,7 +563,6 @@ TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) {
logEventMatcherIndex, eventMatcherWizard,
tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
- gaugeProducer.prepareFirstBucket();
Alert alert;
alert.set_id(101);
@@ -681,7 +671,6 @@ TEST(GaugeMetricProducerTest, TestPullOnTrigger) {
logEventMatcherIndex, eventMatcherWizard,
tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
- gaugeProducer.prepareFirstBucket();
vector<shared_ptr<LogEvent>> allData;
@@ -766,7 +755,6 @@ TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) {
logEventMatcherIndex, eventMatcherWizard,
tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
- gaugeProducer.prepareFirstBucket();
vector<shared_ptr<LogEvent>> allData;
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 2262c76e64f9..4b9d0c0ca718 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -105,7 +105,6 @@ class ValueMetricProducerTestHelper {
kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
logEventMatcherIndex, eventMatcherWizard, tagId,
bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer->prepareFirstBucket();
return valueProducer;
}
@@ -125,7 +124,6 @@ class ValueMetricProducerTestHelper {
new ValueMetricProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer->prepareFirstBucket();
valueProducer->mCondition = ConditionState::kFalse;
return valueProducer;
}
@@ -169,7 +167,6 @@ TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
logEventMatcherIndex, eventMatcherWizard, -1, startTimeBase,
22, pullerManager);
- valueProducer.prepareFirstBucket();
EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
@@ -199,7 +196,6 @@ TEST(ValueMetricProducerTest, TestFirstBucket) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
logEventMatcherIndex, eventMatcherWizard, -1, 5,
600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager);
- valueProducer.prepareFirstBucket();
EXPECT_EQ(600500000000, valueProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(10, valueProducer.mCurrentBucketNum);
@@ -381,7 +377,6 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) {
kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
logEventMatcherIndex, eventMatcherWizard, tagId,
bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer->prepareFirstBucket();
vector<shared_ptr<LogEvent>> allData;
allData.clear();
@@ -670,7 +665,6 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
- valueProducer.prepareFirstBucket();
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -728,7 +722,6 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.prepareFirstBucket();
vector<shared_ptr<LogEvent>> allData;
allData.clear();
@@ -779,7 +772,6 @@ TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.prepareFirstBucket();
vector<shared_ptr<LogEvent>> allData;
allData.clear();
@@ -854,7 +846,6 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
- valueProducer.prepareFirstBucket();
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -897,7 +888,6 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
- valueProducer.prepareFirstBucket();
valueProducer.mCondition = ConditionState::kFalse;
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
@@ -972,7 +962,6 @@ TEST(ValueMetricProducerTest, TestAnomalyDetection) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
logEventMatcherIndex, eventMatcherWizard, -1 /*not pulled*/,
bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer.prepareFirstBucket();
sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert, alarmMonitor);
@@ -1269,7 +1258,6 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMin) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
- valueProducer.prepareFirstBucket();
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -1314,7 +1302,6 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
- valueProducer.prepareFirstBucket();
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -1361,7 +1348,6 @@ TEST(ValueMetricProducerTest, TestPushedAggregateAvg) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
- valueProducer.prepareFirstBucket();
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -1412,7 +1398,6 @@ TEST(ValueMetricProducerTest, TestPushedAggregateSum) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
- valueProducer.prepareFirstBucket();
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -1458,7 +1443,6 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
- valueProducer.prepareFirstBucket();
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -1532,7 +1516,6 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
- valueProducer.prepareFirstBucket();
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -2081,7 +2064,6 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) {
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
eventMatcherWizard, tagId, bucket2StartTimeNs,
bucket2StartTimeNs, pullerManager);
- valueProducer.prepareFirstBucket();
valueProducer.mCondition = ConditionState::kFalse;
// Event should be skipped since it is from previous bucket.
@@ -2862,7 +2844,6 @@ TEST(ValueMetricProducerTest, TestPullNeededFastDump) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.prepareFirstBucket();
ProtoOutputStream output;
std::set<string> strSet;
@@ -2905,7 +2886,6 @@ TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.prepareFirstBucket();
vector<shared_ptr<LogEvent>> allData;
allData.clear();
@@ -2969,7 +2949,6 @@ TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.prepareFirstBucket();
ProtoOutputStream output;
std::set<string> strSet;
diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp
new file mode 100644
index 000000000000..8d380006a8ab
--- /dev/null
+++ b/cmds/statsd/tests/state/StateTracker_test.cpp
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <gtest/gtest.h>
+#include "state/StateManager.h"
+#include "state/StateTracker.h"
+#include "state/StateListener.h"
+
+#include "tests/statsd_test_util.h"
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Mock StateListener class for testing.
+ * Stores primary key and state pairs.
+ */
+class TestStateListener : public virtual StateListener {
+public:
+ TestStateListener(){};
+
+ virtual ~TestStateListener(){};
+
+ struct Update {
+ Update(const HashableDimensionKey& key, int state) : mKey(key), mState(state){};
+ HashableDimensionKey mKey;
+ int mState;
+ };
+
+ std::vector<Update> updates;
+
+ void onStateChanged(int atomId, const HashableDimensionKey& primaryKey, int oldState,
+ int newState) {
+ updates.emplace_back(primaryKey, newState);
+ }
+};
+
+// START: build event functions.
+// State with no primary fields - ScreenStateChanged
+std::shared_ptr<LogEvent> buildScreenEvent(int state) {
+ std::shared_ptr<LogEvent> event =
+ std::make_shared<LogEvent>(android::util::SCREEN_STATE_CHANGED, 1000 /*timestamp*/);
+ event->write((int32_t)state);
+ event->init();
+ return event;
+}
+
+// State with one primary field - UidProcessStateChanged
+std::shared_ptr<LogEvent> buildUidProcessEvent(int uid, int state) {
+ std::shared_ptr<LogEvent> event =
+ std::make_shared<LogEvent>(android::util::UID_PROCESS_STATE_CHANGED, 1000 /*timestamp*/);
+ event->write((int32_t)uid);
+ event->write((int32_t)state);
+ event->init();
+ return event;
+}
+
+// State with multiple primary fields - OverlayStateChanged
+std::shared_ptr<LogEvent> buildOverlayEvent(int uid, const std::string& packageName, int state) {
+ std::shared_ptr<LogEvent> event =
+ std::make_shared<LogEvent>(android::util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/);
+ event->write((int32_t)uid);
+ event->write(packageName);
+ event->write(true); // using_alert_window
+ event->write((int32_t)state);
+ event->init();
+ return event;
+}
+
+// Incorrect event - missing fields
+std::shared_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName, int state) {
+ std::shared_ptr<LogEvent> event =
+ std::make_shared<LogEvent>(android::util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/);
+ event->write((int32_t)uid);
+ event->write(packageName);
+ event->write((int32_t)state);
+ event->init();
+ return event;
+}
+
+// Incorrect event - exclusive state has wrong type
+std::shared_ptr<LogEvent> buildOverlayEventBadStateType(int uid, const std::string& packageName) {
+ std::shared_ptr<LogEvent> event =
+ std::make_shared<LogEvent>(android::util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/);
+ event->write((int32_t)uid);
+ event->write(packageName);
+ event->write(true);
+ event->write("string"); // exclusive state: string instead of int
+ event->init();
+ return event;
+}
+// END: build event functions.
+
+// START: get primary key functions
+void getUidProcessKey(int uid, HashableDimensionKey* key) {
+ int pos1[] = {1, 0, 0};
+ Field field1(27 /* atom id */, pos1, 0 /* depth */);
+ Value value1((int32_t)uid);
+
+ key->addValue(FieldValue(field1, value1));
+}
+
+void getOverlayKey(int uid, string packageName, HashableDimensionKey* key) {
+ int pos1[] = {1, 0, 0};
+ int pos2[] = {2, 0, 0};
+
+ Field field1(59 /* atom id */, pos1, 0 /* depth */);
+ Field field2(59 /* atom id */, pos2, 0 /* depth */);
+
+ Value value1((int32_t)uid);
+ Value value2(packageName);
+
+ key->addValue(FieldValue(field1, value1));
+ key->addValue(FieldValue(field2, value2));
+}
+// END: get primary key functions
+
+TEST(StateListenerTest, TestStateListenerWeakPointer) {
+ sp<TestStateListener> listener = new TestStateListener();
+ wp<TestStateListener> wListener = listener;
+ listener = nullptr; // let go of listener
+ EXPECT_TRUE(wListener.promote() == nullptr);
+}
+
+TEST(StateManagerTest, TestStateManagerGetInstance) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ StateManager& mgr = StateManager::getInstance();
+
+ mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+}
+
+/**
+ * Test registering listeners to StateTrackers
+ *
+ * - StateManager will create a new StateTracker if it doesn't already exist
+ * and then register the listener to the StateTracker.
+ * - If a listener is already registered to a StateTracker, it is not added again.
+ * - StateTrackers are only created for atoms that are state atoms.
+ */
+TEST(StateTrackerTest, TestRegisterListener) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ sp<TestStateListener> listener2 = new TestStateListener();
+ StateManager mgr;
+
+ // Register listener to non-existing StateTracker
+ EXPECT_EQ(0, mgr.getStateTrackersCount());
+ EXPECT_TRUE(mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1));
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+ // Register listener to existing StateTracker
+ EXPECT_TRUE(mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2));
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(2, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+ // Register already registered listener to existing StateTracker
+ EXPECT_TRUE(mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2));
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(2, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+ // Register listener to non-state atom
+ EXPECT_FALSE(mgr.registerListener(android::util::BATTERY_LEVEL_CHANGED, listener2));
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+}
+
+/**
+ * Test unregistering listeners from StateTrackers
+ *
+ * - StateManager will unregister listeners from a StateTracker only if the
+ * StateTracker exists and the listener is registered to the StateTracker.
+ * - Once all listeners are removed from a StateTracker, the StateTracker
+ * is also removed.
+ */
+TEST(StateTrackerTest, TestUnregisterListener) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ sp<TestStateListener> listener2 = new TestStateListener();
+ StateManager mgr;
+
+ // Unregister listener from non-existing StateTracker
+ EXPECT_EQ(0, mgr.getStateTrackersCount());
+ mgr.unregisterListener(android::util::SCREEN_STATE_CHANGED, listener1);
+ EXPECT_EQ(0, mgr.getStateTrackersCount());
+ EXPECT_EQ(-1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+ // Unregister non-registered listener from existing StateTracker
+ mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+ mgr.unregisterListener(android::util::SCREEN_STATE_CHANGED, listener2);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+ // Unregister second-to-last listener from StateTracker
+ mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2);
+ mgr.unregisterListener(android::util::SCREEN_STATE_CHANGED, listener1);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+ // Unregister last listener from StateTracker
+ mgr.unregisterListener(android::util::SCREEN_STATE_CHANGED, listener2);
+ EXPECT_EQ(0, mgr.getStateTrackersCount());
+ EXPECT_EQ(-1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+}
+
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
+ * updates listener for states without primary keys.
+ */
+TEST(StateTrackerTest, TestStateChangeNoPrimaryFields) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
+
+ // log event
+ std::shared_ptr<LogEvent> event =
+ buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ mgr.onLogEvent(*event);
+
+ // check listener was updated
+ EXPECT_EQ(1, listener1->updates.size());
+ EXPECT_EQ(DEFAULT_DIMENSION_KEY, listener1->updates[0].mKey);
+ EXPECT_EQ(2, listener1->updates[0].mState);
+
+ // check StateTracker was updated by querying for state
+ HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY;
+ EXPECT_EQ(2, mgr.getStateValue(android::util::SCREEN_STATE_CHANGED, queryKey));
+}
+
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
+ * updates listener for states with one primary key.
+ */
+TEST(StateTrackerTest, TestStateChangeOnePrimaryField) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(android::util::UID_PROCESS_STATE_CHANGED, listener1);
+
+ // log event
+ std::shared_ptr<LogEvent> event =
+ buildUidProcessEvent(1000 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP);
+ mgr.onLogEvent(*event);
+
+ // check listener was updated
+ EXPECT_EQ(1, listener1->updates.size());
+ EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
+ EXPECT_EQ(1002, listener1->updates[0].mState);
+
+ // check StateTracker was updated by querying for state
+ HashableDimensionKey queryKey;
+ getUidProcessKey(1000 /* uid */, &queryKey);
+ EXPECT_EQ(1002, mgr.getStateValue(android::util::UID_PROCESS_STATE_CHANGED, queryKey));
+}
+
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
+ * updates listener for states with multiple primary keys.
+ */
+TEST(StateTrackerTest, TestStateChangeMultiplePrimaryFields) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener1);
+
+ // log event
+ std::shared_ptr<LogEvent> event =
+ buildOverlayEvent(1000 /* uid */, "package1", 1); // state: ENTERED
+ mgr.onLogEvent(*event);
+
+ // check listener was updated
+ EXPECT_EQ(1, listener1->updates.size());
+ EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
+ EXPECT_EQ(1, listener1->updates[0].mState);
+
+ // check StateTracker was updated by querying for state
+ HashableDimensionKey queryKey;
+ getOverlayKey(1000 /* uid */, "package1", &queryKey);
+ EXPECT_EQ(1, mgr.getStateValue(android::util::OVERLAY_STATE_CHANGED, queryKey));
+}
+
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged
+ * when there is an error extracting state from log event. Listener is not
+ * updated of state change.
+ */
+TEST(StateTrackerTest, TestStateChangeEventError) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener1);
+
+ // log event
+ std::shared_ptr<LogEvent> event1 =
+ buildIncorrectOverlayEvent(1000 /* uid */, "package1", 1 /* state */);
+ std::shared_ptr<LogEvent> event2 = buildOverlayEventBadStateType(1001 /* uid */, "package2");
+
+ // check listener was updated
+ mgr.onLogEvent(*event1);
+ EXPECT_EQ(0, listener1->updates.size());
+ mgr.onLogEvent(*event2);
+ EXPECT_EQ(0, listener1->updates.size());
+}
+
+TEST(StateTrackerTest, TestStateQuery) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ sp<TestStateListener> listener2 = new TestStateListener();
+ sp<TestStateListener> listener3 = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
+ mgr.registerListener(android::util::UID_PROCESS_STATE_CHANGED, listener2);
+ mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener3);
+
+ std::shared_ptr<LogEvent> event1 = buildUidProcessEvent(
+ 1000,
+ android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002
+ std::shared_ptr<LogEvent> event2 = buildUidProcessEvent(
+ 1001,
+ android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE); // state value:
+ // 1003
+ std::shared_ptr<LogEvent> event3 = buildUidProcessEvent(
+ 1002,
+ android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT); // state value: 1000
+ std::shared_ptr<LogEvent> event4 = buildUidProcessEvent(
+ 1001,
+ android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002
+ std::shared_ptr<LogEvent> event5 =
+ buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ std::shared_ptr<LogEvent> event6 = buildOverlayEvent(1000, "package1", 1);
+ std::shared_ptr<LogEvent> event7 = buildOverlayEvent(1000, "package2", 2);
+
+ mgr.onLogEvent(*event1);
+ mgr.onLogEvent(*event2);
+ mgr.onLogEvent(*event3);
+ mgr.onLogEvent(*event5);
+ mgr.onLogEvent(*event5);
+ mgr.onLogEvent(*event6);
+ mgr.onLogEvent(*event7);
+
+ // Query for UidProcessState of uid 1001
+ HashableDimensionKey queryKey1;
+ getUidProcessKey(1001, &queryKey1);
+ EXPECT_EQ(1003, mgr.getStateValue(android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
+
+ // Query for UidProcessState of uid 1004 - not in state map
+ HashableDimensionKey queryKey2;
+ getUidProcessKey(1004, &queryKey2);
+ EXPECT_EQ(-1, mgr.getStateValue(android::util::UID_PROCESS_STATE_CHANGED,
+ queryKey2)); // default state
+
+ // Query for UidProcessState of uid 1001 - after change in state
+ mgr.onLogEvent(*event4);
+ EXPECT_EQ(1002, mgr.getStateValue(android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
+
+ // Query for ScreenState
+ EXPECT_EQ(2, mgr.getStateValue(android::util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY));
+
+ // Query for OverlayState of uid 1000, package name "package2"
+ HashableDimensionKey queryKey3;
+ getOverlayKey(1000, "package2", &queryKey3);
+ EXPECT_EQ(2, mgr.getStateValue(android::util::OVERLAY_STATE_CHANGED, queryKey3));
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 2c4f3c7692c9..38c22ab5d253 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -251,6 +251,101 @@ Predicate CreateIsInBackgroundPredicate() {
return predicate;
}
+State CreateScreenState() {
+ State state;
+ state.set_id(StringToId("ScreenState"));
+ state.set_atom_id(29);
+ return state;
+}
+
+State CreateUidProcessState() {
+ State state;
+ state.set_id(StringToId("UidProcessState"));
+ state.set_atom_id(27);
+ return state;
+}
+
+State CreateOverlayState() {
+ State state;
+ state.set_id(StringToId("OverlayState"));
+ state.set_atom_id(59);
+ return state;
+}
+
+State CreateScreenStateWithOnOffMap() {
+ State state;
+ state.set_id(StringToId("ScreenStateOnOff"));
+ state.set_atom_id(29);
+
+ auto map = CreateScreenStateOnOffMap();
+ *state.mutable_map() = map;
+
+ return state;
+}
+
+State CreateScreenStateWithInDozeMap() {
+ State state;
+ state.set_id(StringToId("ScreenStateInDoze"));
+ state.set_atom_id(29);
+
+ auto map = CreateScreenStateInDozeMap();
+ *state.mutable_map() = map;
+
+ return state;
+}
+
+StateMap_StateGroup CreateScreenStateOnGroup() {
+ StateMap_StateGroup group;
+ group.set_group_id(StringToId("SCREEN_ON"));
+ group.add_value(2);
+ group.add_value(5);
+ group.add_value(6);
+ return group;
+}
+
+StateMap_StateGroup CreateScreenStateOffGroup() {
+ StateMap_StateGroup group;
+ group.set_group_id(StringToId("SCREEN_OFF"));
+ group.add_value(0);
+ group.add_value(1);
+ group.add_value(3);
+ group.add_value(4);
+ return group;
+}
+
+StateMap CreateScreenStateOnOffMap() {
+ StateMap map;
+ *map.add_group() = CreateScreenStateOnGroup();
+ *map.add_group() = CreateScreenStateOffGroup();
+ return map;
+}
+
+StateMap_StateGroup CreateScreenStateInDozeGroup() {
+ StateMap_StateGroup group;
+ group.set_group_id(StringToId("SCREEN_DOZE"));
+ group.add_value(3);
+ group.add_value(4);
+ return group;
+}
+
+StateMap_StateGroup CreateScreenStateNotDozeGroup() {
+ StateMap_StateGroup group;
+ group.set_group_id(StringToId("SCREEN_NOT_DOZE"));
+ group.add_value(0);
+ group.add_value(1);
+ group.add_value(2);
+ group.add_value(5);
+ group.add_value(6);
+ return group;
+}
+
+StateMap CreateScreenStateInDozeMap() {
+ StateMap map;
+ *map.add_group() = CreateScreenStateInDozeGroup();
+ *map.add_group() = CreateScreenStateNotDozeGroup();
+ return map;
+}
+
void addPredicateToPredicateCombination(const Predicate& predicate,
Predicate* combinationPredicate) {
combinationPredicate->mutable_combination()->add_predicate(predicate.id());
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index 635c5835333a..c02610540cf0 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -104,6 +104,37 @@ Predicate CreateIsSyncingPredicate();
// Create a Predicate proto for app is in background.
Predicate CreateIsInBackgroundPredicate();
+// Create State proto for screen state atom.
+State CreateScreenState();
+
+// Create State proto for uid process state atom.
+State CreateUidProcessState();
+
+// Create State proto for overlay state atom.
+State CreateOverlayState();
+
+State CreateScreenStateWithOnOffMap();
+
+State CreateScreenStateWithInDozeMap();
+
+// Create StateGroup proto for ScreenState ON group
+StateMap_StateGroup CreateScreenStateOnGroup();
+
+// Create StateGroup proto for ScreenState OFF group
+StateMap_StateGroup CreateScreenStateOffGroup();
+
+// Create StateMap proto for ScreenState ON/OFF map
+StateMap CreateScreenStateOnOffMap();
+
+// Create StateGroup proto for ScreenState IN DOZE group
+StateMap_StateGroup CreateScreenStateInDozeGroup();
+
+// Create StateGroup proto for ScreenState NOT IN DOZE group
+StateMap_StateGroup CreateScreenStateNotDozeGroup();
+
+// Create StateMap proto for ScreenState IN DOZE map
+StateMap CreateScreenStateInDozeMap();
+
// Add a predicate to the predicate combination.
void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combination);
@@ -319,4 +350,4 @@ void backfillStartEndTimestampForSkippedBuckets(const int64_t timeBaseNs, T* met
}
} // namespace statsd
} // namespace os
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index e0f7d862e70a..c9f069d62003 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -65,11 +65,21 @@ 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";
+ /**
+ * Change the system dialer package name if a package name was specified,
+ * Example: adb shell telecom set-system-dialer <PACKAGE>
+ *
+ * Restore it to the default if if argument is "default" or no argument is passed.
+ * Example: adb shell telecom set-system-dialer default
+ */
+ private static final String COMMAND_SET_SYSTEM_DIALER = "set-system-dialer";
private static final String COMMAND_GET_SYSTEM_DIALER = "get-system-dialer";
private static final String COMMAND_WAIT_ON_HANDLERS = "wait-on-handlers";
private static final String COMMAND_SET_SIM_COUNT = "set-sim-count";
private static final String COMMAND_GET_SIM_CONFIG = "get-sim-config";
private static final String COMMAND_GET_MAX_PHONES = "get-max-phones";
+ private static final String COMMAND_SET_TEST_EMERGENCY_PHONE_ACCOUNT_PACKAGE_FILTER =
+ "set-test-emergency-phone-account-package-filter";
private ComponentName mComponent;
private String mAccountId;
@@ -83,7 +93,10 @@ public final class Telecom extends BaseCommand {
+ "usage: telecom set-phone-account-enabled <COMPONENT> <ID> <USER_SN>\n"
+ "usage: telecom set-phone-account-disabled <COMPONENT> <ID> <USER_SN>\n"
+ "usage: telecom register-phone-account <COMPONENT> <ID> <USER_SN> <LABEL>\n"
- + "usage: telecom set-user-selected-outgoing-phone-account <COMPONENT> <ID> "
+ + "usage: telecom register-sim-phone-account [-e] <COMPONENT> <ID> <USER_SN>"
+ + " <LABEL>: registers a PhoneAccount with CAPABILITY_SIM_SUBSCRIPTION"
+ + " and optionally CAPABILITY_PLACE_EMERGENCY_CALLS if \"-e\" is provided\n"
+ + "usage: telecom set-user-selected-outgoing-phone-account [-e] <COMPONENT> <ID> "
+ "<USER_SN>\n"
+ "usage: telecom set-test-call-redirection-app <PACKAGE>\n"
+ "usage: telecom set-test-call-screening-app <PACKAGE>\n"
@@ -100,6 +113,7 @@ 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 set-emer-phone-account-filter <PACKAGE>\n"
+ "\n"
+ "telecom set-phone-account-enabled: Enables the given phone account, if it has"
+ " already been registered with Telecom.\n"
@@ -113,6 +127,8 @@ public final class Telecom extends BaseCommand {
+ "telecom get-default-dialer: Displays the current default dialer.\n"
+ "\n"
+ "telecom get-system-dialer: Displays the current system dialer.\n"
+ + "telecom set-system-dialer: Set the override system dialer to the given"
+ + " component. To remove the override, send \"default\"\n"
+ "\n"
+ "telecom wait-on-handlers: Wait until all handlers finish their work.\n"
+ "\n"
@@ -123,6 +139,10 @@ public final class Telecom extends BaseCommand {
+ " or \"\" for single SIM\n"
+ "\n"
+ "telecom get-max-phones: Get the max supported phones from the modem.\n"
+ + "telecom set-test-emergency-phone-account-package-filter <PACKAGE>: sets a"
+ + " package name that will be used for test emergency calls. To clear,"
+ + " send an empty package name. Real emergency calls will still be placed"
+ + " over Telephony.\n"
);
}
@@ -193,6 +213,9 @@ public final class Telecom extends BaseCommand {
case COMMAND_GET_DEFAULT_DIALER:
runGetDefaultDialer();
break;
+ case COMMAND_SET_SYSTEM_DIALER:
+ runSetSystemDialer();
+ break;
case COMMAND_GET_SYSTEM_DIALER:
runGetSystemDialer();
break;
@@ -208,6 +231,9 @@ public final class Telecom extends BaseCommand {
case COMMAND_GET_MAX_PHONES:
runGetMaxPhones();
break;
+ case COMMAND_SET_TEST_EMERGENCY_PHONE_ACCOUNT_PACKAGE_FILTER:
+ runSetEmergencyPhoneAccountPackageFilter();
+ break;
default:
Log.w(this, "onRun: unknown command: %s", command);
throw new IllegalArgumentException ("unknown command '" + command + "'");
@@ -234,19 +260,31 @@ public final class Telecom extends BaseCommand {
}
private void runRegisterSimPhoneAccount() throws RemoteException {
+ boolean isEmergencyAccount = false;
+ String opt;
+ while ((opt = nextOption()) != null) {
+ switch (opt) {
+ case "-e": {
+ isEmergencyAccount = true;
+ break;
+ }
+ }
+ }
final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
final String label = nextArgRequired();
final String address = nextArgRequired();
+ int capabilities = PhoneAccount.CAPABILITY_CALL_PROVIDER
+ | PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION
+ | (isEmergencyAccount ? PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS : 0);
PhoneAccount account = PhoneAccount.builder(
handle, label)
- .setAddress(Uri.parse(address))
- .setSubscriptionAddress(Uri.parse(address))
- .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER |
- PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
- .setShortDescription(label)
- .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
- .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
- .build();
+ .setAddress(Uri.parse(address))
+ .setSubscriptionAddress(Uri.parse(address))
+ .setCapabilities(capabilities)
+ .setShortDescription(label)
+ .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
+ .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
+ .build();
mTelecomService.registerPhoneAccount(account);
System.out.println("Success - " + handle + " registered.");
}
@@ -297,6 +335,14 @@ public final class Telecom extends BaseCommand {
System.out.println("Success - " + packageName + " set as override default dialer.");
}
+ private void runSetSystemDialer() throws RemoteException {
+ final String flatComponentName = nextArg();
+ final ComponentName componentName = (flatComponentName.equals("default")
+ ? null : parseComponentName(flatComponentName));
+ mTelecomService.setSystemDialer(componentName);
+ System.out.println("Success - " + componentName + " set as override system dialer.");
+ }
+
private void runGetDefaultDialer() throws RemoteException {
System.out.println(mTelecomService.getDefaultDialerPackage());
}
@@ -338,6 +384,18 @@ public final class Telecom extends BaseCommand {
}
}
+ private void runSetEmergencyPhoneAccountPackageFilter() throws RemoteException {
+ String packageName = mArgs.getNextArg();
+ if (TextUtils.isEmpty(packageName)) {
+ mTelecomService.setTestEmergencyPhoneAccountPackageNameFilter(null);
+ System.out.println("Success - filter cleared");
+ } else {
+ mTelecomService.setTestEmergencyPhoneAccountPackageNameFilter(packageName);
+ System.out.println("Success = filter set to " + packageName);
+ }
+
+ }
+
private PhoneAccountHandle getPhoneAccountHandleFromArgs() throws RemoteException {
if (TextUtils.isEmpty(mArgs.peekNextArg())) {
return null;
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index a6e1f0ab0cde..3cb2273776f2 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -218,7 +218,6 @@ Landroid/location/ILocationManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/location/ILocationManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/ILocationManager;
Landroid/location/ILocationManager$Stub;->TRANSACTION_getAllProviders:I
Landroid/location/INetInitiatedListener$Stub;-><init>()V
-Landroid/location/LocationManager$ListenerTransport;-><init>(Landroid/location/LocationManager;Landroid/location/LocationListener;Landroid/os/Looper;)V
Landroid/Manifest$permission;->CAPTURE_SECURE_VIDEO_OUTPUT:Ljava/lang/String;
Landroid/Manifest$permission;->CAPTURE_VIDEO_OUTPUT:Ljava/lang/String;
Landroid/Manifest$permission;->READ_FRAME_BUFFER:Ljava/lang/String;
@@ -1150,6 +1149,8 @@ Lcom/android/internal/statusbar/IStatusBar$Stub;->asInterface(Landroid/os/IBinde
Lcom/android/internal/statusbar/IStatusBarService$Stub;-><init>()V
Lcom/android/internal/statusbar/IStatusBarService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/statusbar/IStatusBarService;
Lcom/android/internal/telecom/ITelecomService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telecom/ITelecomService;
+Lcom/android/internal/telephony/IIccPhoneBook$Stub$Proxy;->mRemote:Landroid/os/IBinder;
+Lcom/android/internal/telephony/IIccPhoneBook$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IIccPhoneBook;
Lcom/android/internal/telephony/IMms$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IMms;
Lcom/android/internal/telephony/IPhoneStateListener$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IPhoneStateListener;
Lcom/android/internal/telephony/IPhoneSubInfo$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -1157,6 +1158,7 @@ Lcom/android/internal/telephony/IPhoneSubInfo$Stub;->asInterface(Landroid/os/IBi
Lcom/android/internal/telephony/IPhoneSubInfo$Stub;->TRANSACTION_getDeviceId:I
Lcom/android/internal/telephony/ISms$Stub;-><init>()V
Lcom/android/internal/telephony/ISms$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ISms;
+Lcom/android/internal/telephony/ISub$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Lcom/android/internal/telephony/ISub$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ISub;
Lcom/android/internal/telephony/ITelephony$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->mRemote:Landroid/os/IBinder;
@@ -1180,283 +1182,4 @@ Lcom/android/server/net/BaseNetworkObserver;-><init>()V
Lcom/android/server/ResettableTimeout$T;-><init>(Lcom/android/server/ResettableTimeout;)V
Lcom/google/android/gles_jni/EGLImpl;-><init>()V
Lcom/google/android/gles_jni/GLImpl;-><init>()V
-Lcom/google/android/mms/ContentType;->getAudioTypes()Ljava/util/ArrayList;
-Lcom/google/android/mms/ContentType;->getImageTypes()Ljava/util/ArrayList;
-Lcom/google/android/mms/ContentType;->getVideoTypes()Ljava/util/ArrayList;
-Lcom/google/android/mms/ContentType;->isAudioType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isDrmType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isImageType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isSupportedAudioType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isSupportedImageType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isSupportedType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isSupportedVideoType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isTextType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isVideoType(Ljava/lang/String;)Z
-Lcom/google/android/mms/InvalidHeaderValueException;-><init>(Ljava/lang/String;)V
-Lcom/google/android/mms/MmsException;-><init>()V
-Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/String;)V
-Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/String;Ljava/lang/Throwable;)V
-Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/Throwable;)V
-Lcom/google/android/mms/pdu/AcknowledgeInd;-><init>(I[B)V
-Lcom/google/android/mms/pdu/AcknowledgeInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/AcknowledgeInd;->setReportAllowed(I)V
-Lcom/google/android/mms/pdu/AcknowledgeInd;->setTransactionId([B)V
-Lcom/google/android/mms/pdu/Base64;->decodeBase64([B)[B
-Lcom/google/android/mms/pdu/CharacterSets;->getMibEnumValue(Ljava/lang/String;)I
-Lcom/google/android/mms/pdu/CharacterSets;->getMimeName(I)Ljava/lang/String;
-Lcom/google/android/mms/pdu/DeliveryInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/DeliveryInd;->getDate()J
-Lcom/google/android/mms/pdu/DeliveryInd;->getMessageId()[B
-Lcom/google/android/mms/pdu/DeliveryInd;->getStatus()I
-Lcom/google/android/mms/pdu/DeliveryInd;->getTo()[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(I[B)V
-Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(Ljava/lang/String;)V
-Lcom/google/android/mms/pdu/EncodedStringValue;-><init>([B)V
-Lcom/google/android/mms/pdu/EncodedStringValue;->appendTextString([B)V
-Lcom/google/android/mms/pdu/EncodedStringValue;->concat([Lcom/google/android/mms/pdu/EncodedStringValue;)Ljava/lang/String;
-Lcom/google/android/mms/pdu/EncodedStringValue;->copy(Lcom/google/android/mms/pdu/EncodedStringValue;)Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/EncodedStringValue;->encodeStrings([Ljava/lang/String;)[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/EncodedStringValue;->extract(Ljava/lang/String;)[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/EncodedStringValue;->getCharacterSet()I
-Lcom/google/android/mms/pdu/EncodedStringValue;->getString()Ljava/lang/String;
-Lcom/google/android/mms/pdu/EncodedStringValue;->getTextString()[B
-Lcom/google/android/mms/pdu/EncodedStringValue;->setCharacterSet(I)V
-Lcom/google/android/mms/pdu/EncodedStringValue;->setTextString([B)V
-Lcom/google/android/mms/pdu/GenericPdu;-><init>()V
-Lcom/google/android/mms/pdu/GenericPdu;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/GenericPdu;->getMessageType()I
-Lcom/google/android/mms/pdu/GenericPdu;->getPduHeaders()Lcom/google/android/mms/pdu/PduHeaders;
-Lcom/google/android/mms/pdu/GenericPdu;->mPduHeaders:Lcom/google/android/mms/pdu/PduHeaders;
-Lcom/google/android/mms/pdu/GenericPdu;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/GenericPdu;->setMessageType(I)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;-><init>()V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;-><init>(Lcom/google/android/mms/pdu/PduHeaders;Lcom/google/android/mms/pdu/PduBody;)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->addTo(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getBody()Lcom/google/android/mms/pdu/PduBody;
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getDate()J
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getPriority()I
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getSubject()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getTo()[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setBody(Lcom/google/android/mms/pdu/PduBody;)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setDate(J)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setPriority(I)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setSubject(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/NotificationInd;-><init>()V
-Lcom/google/android/mms/pdu/NotificationInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/NotificationInd;->getContentClass()I
-Lcom/google/android/mms/pdu/NotificationInd;->getContentLocation()[B
-Lcom/google/android/mms/pdu/NotificationInd;->getDeliveryReport()I
-Lcom/google/android/mms/pdu/NotificationInd;->getExpiry()J
-Lcom/google/android/mms/pdu/NotificationInd;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/NotificationInd;->getMessageClass()[B
-Lcom/google/android/mms/pdu/NotificationInd;->getMessageSize()J
-Lcom/google/android/mms/pdu/NotificationInd;->getSubject()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/NotificationInd;->getTransactionId()[B
-Lcom/google/android/mms/pdu/NotificationInd;->setContentClass(I)V
-Lcom/google/android/mms/pdu/NotificationInd;->setContentLocation([B)V
-Lcom/google/android/mms/pdu/NotificationInd;->setDeliveryReport(I)V
-Lcom/google/android/mms/pdu/NotificationInd;->setExpiry(J)V
-Lcom/google/android/mms/pdu/NotificationInd;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/NotificationInd;->setMessageClass([B)V
-Lcom/google/android/mms/pdu/NotificationInd;->setMessageSize(J)V
-Lcom/google/android/mms/pdu/NotificationInd;->setSubject(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/NotificationInd;->setTransactionId([B)V
-Lcom/google/android/mms/pdu/NotifyRespInd;-><init>(I[BI)V
-Lcom/google/android/mms/pdu/NotifyRespInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/NotifyRespInd;->setReportAllowed(I)V
-Lcom/google/android/mms/pdu/NotifyRespInd;->setStatus(I)V
-Lcom/google/android/mms/pdu/NotifyRespInd;->setTransactionId([B)V
-Lcom/google/android/mms/pdu/PduBody;-><init>()V
-Lcom/google/android/mms/pdu/PduBody;->addPart(ILcom/google/android/mms/pdu/PduPart;)V
-Lcom/google/android/mms/pdu/PduBody;->addPart(Lcom/google/android/mms/pdu/PduPart;)Z
-Lcom/google/android/mms/pdu/PduBody;->getPart(I)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduBody;->getPartByContentId(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduBody;->getPartByContentLocation(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduBody;->getPartByFileName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduBody;->getPartByName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduBody;->getPartIndex(Lcom/google/android/mms/pdu/PduPart;)I
-Lcom/google/android/mms/pdu/PduBody;->getPartsNum()I
-Lcom/google/android/mms/pdu/PduBody;->removePart(I)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduComposer$BufferStack;->copy()V
-Lcom/google/android/mms/pdu/PduComposer$BufferStack;->mark()Lcom/google/android/mms/pdu/PduComposer$PositionMarker;
-Lcom/google/android/mms/pdu/PduComposer$BufferStack;->newbuf()V
-Lcom/google/android/mms/pdu/PduComposer$BufferStack;->pop()V
-Lcom/google/android/mms/pdu/PduComposer$PositionMarker;->getLength()I
-Lcom/google/android/mms/pdu/PduComposer;-><init>(Landroid/content/Context;Lcom/google/android/mms/pdu/GenericPdu;)V
-Lcom/google/android/mms/pdu/PduComposer;->appendEncodedString(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/PduComposer;->appendHeader(I)I
-Lcom/google/android/mms/pdu/PduComposer;->appendLongInteger(J)V
-Lcom/google/android/mms/pdu/PduComposer;->appendOctet(I)V
-Lcom/google/android/mms/pdu/PduComposer;->appendQuotedString(Ljava/lang/String;)V
-Lcom/google/android/mms/pdu/PduComposer;->appendQuotedString([B)V
-Lcom/google/android/mms/pdu/PduComposer;->appendShortInteger(I)V
-Lcom/google/android/mms/pdu/PduComposer;->appendTextString(Ljava/lang/String;)V
-Lcom/google/android/mms/pdu/PduComposer;->appendTextString([B)V
-Lcom/google/android/mms/pdu/PduComposer;->appendUintvarInteger(J)V
-Lcom/google/android/mms/pdu/PduComposer;->appendValueLength(J)V
-Lcom/google/android/mms/pdu/PduComposer;->arraycopy([BII)V
-Lcom/google/android/mms/pdu/PduComposer;->make()[B
-Lcom/google/android/mms/pdu/PduComposer;->mContentTypeMap:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduComposer;->mMessage:Ljava/io/ByteArrayOutputStream;
-Lcom/google/android/mms/pdu/PduComposer;->mPdu:Lcom/google/android/mms/pdu/GenericPdu;
-Lcom/google/android/mms/pdu/PduComposer;->mPduHeader:Lcom/google/android/mms/pdu/PduHeaders;
-Lcom/google/android/mms/pdu/PduComposer;->mPosition:I
-Lcom/google/android/mms/pdu/PduComposer;->mResolver:Landroid/content/ContentResolver;
-Lcom/google/android/mms/pdu/PduComposer;->mStack:Lcom/google/android/mms/pdu/PduComposer$BufferStack;
-Lcom/google/android/mms/pdu/PduContentTypes;->contentTypes:[Ljava/lang/String;
-Lcom/google/android/mms/pdu/PduHeaders;-><init>()V
-Lcom/google/android/mms/pdu/PduHeaders;->appendEncodedStringValue(Lcom/google/android/mms/pdu/EncodedStringValue;I)V
-Lcom/google/android/mms/pdu/PduHeaders;->getEncodedStringValue(I)Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/PduHeaders;->getEncodedStringValues(I)[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/PduHeaders;->getLongInteger(I)J
-Lcom/google/android/mms/pdu/PduHeaders;->getOctet(I)I
-Lcom/google/android/mms/pdu/PduHeaders;->getTextString(I)[B
-Lcom/google/android/mms/pdu/PduHeaders;->setEncodedStringValue(Lcom/google/android/mms/pdu/EncodedStringValue;I)V
-Lcom/google/android/mms/pdu/PduHeaders;->setLongInteger(JI)V
-Lcom/google/android/mms/pdu/PduHeaders;->setOctet(II)V
-Lcom/google/android/mms/pdu/PduParser;-><init>([BZ)V
-Lcom/google/android/mms/pdu/PduParser;->checkPartPosition(Lcom/google/android/mms/pdu/PduPart;)I
-Lcom/google/android/mms/pdu/PduParser;->log(Ljava/lang/String;)V
-Lcom/google/android/mms/pdu/PduParser;->parse()Lcom/google/android/mms/pdu/GenericPdu;
-Lcom/google/android/mms/pdu/PduParser;->parseContentType(Ljava/io/ByteArrayInputStream;Ljava/util/HashMap;)[B
-Lcom/google/android/mms/pdu/PduParser;->parsePartHeaders(Ljava/io/ByteArrayInputStream;Lcom/google/android/mms/pdu/PduPart;I)Z
-Lcom/google/android/mms/pdu/PduParser;->parseShortInteger(Ljava/io/ByteArrayInputStream;)I
-Lcom/google/android/mms/pdu/PduParser;->parseUnsignedInt(Ljava/io/ByteArrayInputStream;)I
-Lcom/google/android/mms/pdu/PduParser;->parseValueLength(Ljava/io/ByteArrayInputStream;)I
-Lcom/google/android/mms/pdu/PduParser;->parseWapString(Ljava/io/ByteArrayInputStream;I)[B
-Lcom/google/android/mms/pdu/PduPart;-><init>()V
-Lcom/google/android/mms/pdu/PduPart;->generateLocation()Ljava/lang/String;
-Lcom/google/android/mms/pdu/PduPart;->getCharset()I
-Lcom/google/android/mms/pdu/PduPart;->getContentDisposition()[B
-Lcom/google/android/mms/pdu/PduPart;->getContentId()[B
-Lcom/google/android/mms/pdu/PduPart;->getContentLocation()[B
-Lcom/google/android/mms/pdu/PduPart;->getContentTransferEncoding()[B
-Lcom/google/android/mms/pdu/PduPart;->getContentType()[B
-Lcom/google/android/mms/pdu/PduPart;->getData()[B
-Lcom/google/android/mms/pdu/PduPart;->getDataLength()I
-Lcom/google/android/mms/pdu/PduPart;->getDataUri()Landroid/net/Uri;
-Lcom/google/android/mms/pdu/PduPart;->getFilename()[B
-Lcom/google/android/mms/pdu/PduPart;->getName()[B
-Lcom/google/android/mms/pdu/PduPart;->setCharset(I)V
-Lcom/google/android/mms/pdu/PduPart;->setContentDisposition([B)V
-Lcom/google/android/mms/pdu/PduPart;->setContentId([B)V
-Lcom/google/android/mms/pdu/PduPart;->setContentLocation([B)V
-Lcom/google/android/mms/pdu/PduPart;->setContentTransferEncoding([B)V
-Lcom/google/android/mms/pdu/PduPart;->setContentType([B)V
-Lcom/google/android/mms/pdu/PduPart;->setData([B)V
-Lcom/google/android/mms/pdu/PduPart;->setDataUri(Landroid/net/Uri;)V
-Lcom/google/android/mms/pdu/PduPart;->setFilename([B)V
-Lcom/google/android/mms/pdu/PduPart;->setName([B)V
-Lcom/google/android/mms/pdu/PduPersister;->ADDRESS_FIELDS:[I
-Lcom/google/android/mms/pdu/PduPersister;->CHARSET_COLUMN_NAME_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->ENCODED_STRING_COLUMN_NAME_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->getByteArrayFromPartColumn(Landroid/database/Cursor;I)[B
-Lcom/google/android/mms/pdu/PduPersister;->getBytes(Ljava/lang/String;)[B
-Lcom/google/android/mms/pdu/PduPersister;->getIntegerFromPartColumn(Landroid/database/Cursor;I)Ljava/lang/Integer;
-Lcom/google/android/mms/pdu/PduPersister;->getPartContentType(Lcom/google/android/mms/pdu/PduPart;)Ljava/lang/String;
-Lcom/google/android/mms/pdu/PduPersister;->getPduPersister(Landroid/content/Context;)Lcom/google/android/mms/pdu/PduPersister;
-Lcom/google/android/mms/pdu/PduPersister;->getPendingMessages(J)Landroid/database/Cursor;
-Lcom/google/android/mms/pdu/PduPersister;->load(Landroid/net/Uri;)Lcom/google/android/mms/pdu/GenericPdu;
-Lcom/google/android/mms/pdu/PduPersister;->loadRecipients(ILjava/util/HashSet;Ljava/util/HashMap;Z)V
-Lcom/google/android/mms/pdu/PduPersister;->LONG_COLUMN_NAME_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->mContentResolver:Landroid/content/ContentResolver;
-Lcom/google/android/mms/pdu/PduPersister;->mContext:Landroid/content/Context;
-Lcom/google/android/mms/pdu/PduPersister;->MESSAGE_BOX_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->move(Landroid/net/Uri;Landroid/net/Uri;)Landroid/net/Uri;
-Lcom/google/android/mms/pdu/PduPersister;->mTelephonyManager:Landroid/telephony/TelephonyManager;
-Lcom/google/android/mms/pdu/PduPersister;->OCTET_COLUMN_NAME_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->PART_PROJECTION:[Ljava/lang/String;
-Lcom/google/android/mms/pdu/PduPersister;->PDU_CACHE_INSTANCE:Lcom/google/android/mms/util/PduCache;
-Lcom/google/android/mms/pdu/PduPersister;->persist(Lcom/google/android/mms/pdu/GenericPdu;Landroid/net/Uri;ZZLjava/util/HashMap;)Landroid/net/Uri;
-Lcom/google/android/mms/pdu/PduPersister;->persistAddress(JI[Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/PduPersister;->persistPart(Lcom/google/android/mms/pdu/PduPart;JLjava/util/HashMap;)Landroid/net/Uri;
-Lcom/google/android/mms/pdu/PduPersister;->TEXT_STRING_COLUMN_NAME_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->toIsoString([B)Ljava/lang/String;
-Lcom/google/android/mms/pdu/PduPersister;->updateAddress(JI[Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/PduPersister;->updateHeaders(Landroid/net/Uri;Lcom/google/android/mms/pdu/SendReq;)V
-Lcom/google/android/mms/pdu/PduPersister;->updateParts(Landroid/net/Uri;Lcom/google/android/mms/pdu/PduBody;Ljava/util/HashMap;)V
-Lcom/google/android/mms/pdu/QuotedPrintable;->decodeQuotedPrintable([B)[B
-Lcom/google/android/mms/pdu/ReadOrigInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/ReadOrigInd;->getMessageId()[B
-Lcom/google/android/mms/pdu/ReadOrigInd;->getReadStatus()I
-Lcom/google/android/mms/pdu/ReadRecInd;-><init>(Lcom/google/android/mms/pdu/EncodedStringValue;[BII[Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/ReadRecInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/ReadRecInd;->getMessageId()[B
-Lcom/google/android/mms/pdu/ReadRecInd;->setDate(J)V
-Lcom/google/android/mms/pdu/RetrieveConf;-><init>()V
-Lcom/google/android/mms/pdu/RetrieveConf;-><init>(Lcom/google/android/mms/pdu/PduHeaders;Lcom/google/android/mms/pdu/PduBody;)V
-Lcom/google/android/mms/pdu/RetrieveConf;->addCc(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/RetrieveConf;->getCc()[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/RetrieveConf;->getContentType()[B
-Lcom/google/android/mms/pdu/RetrieveConf;->getDeliveryReport()I
-Lcom/google/android/mms/pdu/RetrieveConf;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/RetrieveConf;->getMessageClass()[B
-Lcom/google/android/mms/pdu/RetrieveConf;->getMessageId()[B
-Lcom/google/android/mms/pdu/RetrieveConf;->getReadReport()I
-Lcom/google/android/mms/pdu/RetrieveConf;->getRetrieveStatus()I
-Lcom/google/android/mms/pdu/RetrieveConf;->getRetrieveText()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/RetrieveConf;->getTransactionId()[B
-Lcom/google/android/mms/pdu/RetrieveConf;->setContentType([B)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setDeliveryReport(I)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setMessageClass([B)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setMessageId([B)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setReadReport(I)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setRetrieveStatus(I)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setRetrieveText(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setTransactionId([B)V
-Lcom/google/android/mms/pdu/SendConf;-><init>()V
-Lcom/google/android/mms/pdu/SendConf;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/SendConf;->getMessageId()[B
-Lcom/google/android/mms/pdu/SendConf;->getResponseStatus()I
-Lcom/google/android/mms/pdu/SendConf;->getTransactionId()[B
-Lcom/google/android/mms/pdu/SendReq;-><init>()V
-Lcom/google/android/mms/pdu/SendReq;-><init>(Lcom/google/android/mms/pdu/PduHeaders;Lcom/google/android/mms/pdu/PduBody;)V
-Lcom/google/android/mms/pdu/SendReq;->addBcc(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/SendReq;->addCc(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/SendReq;->getBcc()[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/SendReq;->getCc()[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/SendReq;->getContentType()[B
-Lcom/google/android/mms/pdu/SendReq;->getDeliveryReport()I
-Lcom/google/android/mms/pdu/SendReq;->getExpiry()J
-Lcom/google/android/mms/pdu/SendReq;->getMessageClass()[B
-Lcom/google/android/mms/pdu/SendReq;->getMessageSize()J
-Lcom/google/android/mms/pdu/SendReq;->getReadReport()I
-Lcom/google/android/mms/pdu/SendReq;->getTransactionId()[B
-Lcom/google/android/mms/pdu/SendReq;->setBcc([Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/SendReq;->setCc([Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/SendReq;->setContentType([B)V
-Lcom/google/android/mms/pdu/SendReq;->setDeliveryReport(I)V
-Lcom/google/android/mms/pdu/SendReq;->setExpiry(J)V
-Lcom/google/android/mms/pdu/SendReq;->setMessageClass([B)V
-Lcom/google/android/mms/pdu/SendReq;->setMessageSize(J)V
-Lcom/google/android/mms/pdu/SendReq;->setReadReport(I)V
-Lcom/google/android/mms/pdu/SendReq;->setTo([Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/SendReq;->setTransactionId([B)V
-Lcom/google/android/mms/util/AbstractCache;-><init>()V
-Lcom/google/android/mms/util/AbstractCache;->get(Ljava/lang/Object;)Ljava/lang/Object;
-Lcom/google/android/mms/util/AbstractCache;->purge(Ljava/lang/Object;)Ljava/lang/Object;
-Lcom/google/android/mms/util/AbstractCache;->purgeAll()V
-Lcom/google/android/mms/util/AbstractCache;->put(Ljava/lang/Object;Ljava/lang/Object;)Z
-Lcom/google/android/mms/util/DownloadDrmHelper;->isDrmConvertNeeded(Ljava/lang/String;)Z
-Lcom/google/android/mms/util/DownloadDrmHelper;->modifyDrmFwLockFileExtension(Ljava/lang/String;)Ljava/lang/String;
-Lcom/google/android/mms/util/DrmConvertSession;->close(Ljava/lang/String;)I
-Lcom/google/android/mms/util/DrmConvertSession;->convert([BI)[B
-Lcom/google/android/mms/util/DrmConvertSession;->open(Landroid/content/Context;Ljava/lang/String;)Lcom/google/android/mms/util/DrmConvertSession;
-Lcom/google/android/mms/util/PduCache;-><init>()V
-Lcom/google/android/mms/util/PduCache;->getInstance()Lcom/google/android/mms/util/PduCache;
-Lcom/google/android/mms/util/PduCache;->isUpdating(Landroid/net/Uri;)Z
-Lcom/google/android/mms/util/PduCache;->purge(Landroid/net/Uri;)Lcom/google/android/mms/util/PduCacheEntry;
-Lcom/google/android/mms/util/PduCache;->purge(Ljava/lang/Object;)Ljava/lang/Object;
-Lcom/google/android/mms/util/PduCache;->purgeAll()V
-Lcom/google/android/mms/util/PduCacheEntry;-><init>(Lcom/google/android/mms/pdu/GenericPdu;IJ)V
-Lcom/google/android/mms/util/PduCacheEntry;->getMessageBox()I
-Lcom/google/android/mms/util/PduCacheEntry;->getPdu()Lcom/google/android/mms/pdu/GenericPdu;
-Lcom/google/android/mms/util/PduCacheEntry;->getThreadId()J
-Lcom/google/android/mms/util/SqliteWrapper;->checkSQLiteException(Landroid/content/Context;Landroid/database/sqlite/SQLiteException;)V
-Lcom/google/android/mms/util/SqliteWrapper;->delete(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;[Ljava/lang/String;)I
-Lcom/google/android/mms/util/SqliteWrapper;->insert(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri;
-Lcom/google/android/mms/util/SqliteWrapper;->query(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;
-Lcom/google/android/mms/util/SqliteWrapper;->requery(Landroid/content/Context;Landroid/database/Cursor;)Z
-Lcom/google/android/mms/util/SqliteWrapper;->update(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/String;)I
Lcom/google/android/util/AbstractMessageParser$Token$Type;->values()[Lcom/google/android/util/AbstractMessageParser$Token$Type;
diff --git a/config/preloaded-classes b/config/preloaded-classes
index e117e689b598..8d911443ce06 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -4838,7 +4838,6 @@ com.android.internal.telephony.PhoneConstantConversions
com.android.internal.telephony.PhoneConstants$DataState
com.android.internal.telephony.PhoneConstants$State
com.android.internal.telephony.PhoneFactory
-com.android.internal.telephony.PhoneInternalInterface$DataActivityState
com.android.internal.telephony.PhoneInternalInterface
com.android.internal.telephony.PhoneNotifier
com.android.internal.telephony.PhoneStateIntentReceiver
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index d3c274f4bdfe..47fdcdeafce9 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -605,7 +605,7 @@ public abstract class AccessibilityService extends Service {
}
/**
- * Gets the windows on the screen. This method returns only the windows
+ * Gets the windows on the screen of the default display. This method returns only the windows
* that a sighted user can interact with, as opposed to all windows.
* For example, if there is a modal dialog shown and the user cannot touch
* anything behind it, then only the modal window will be reported
@@ -632,6 +632,34 @@ public abstract class AccessibilityService extends Service {
}
/**
+ * Gets the windows on the screen of all displays. This method returns only the windows
+ * that a sighted user can interact with, as opposed to all windows.
+ * For example, if there is a modal dialog shown and the user cannot touch
+ * anything behind it, then only the modal window will be reported
+ * (assuming it is the top one). For convenience the returned windows
+ * are ordered in a descending layer order, which is the windows that
+ * are on top are reported first. Since the user can always
+ * interact with the window that has input focus by typing, the focused
+ * window is always returned (even if covered by a modal window).
+ * <p>
+ * <strong>Note:</strong> In order to access the windows your service has
+ * to declare the capability to retrieve window content by setting the
+ * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
+ * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
+ * Also the service has to opt-in to retrieve the interactive windows by
+ * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}
+ * flag.
+ * </p>
+ *
+ * @return The windows of all displays if there are windows and the service is can retrieve
+ * them, otherwise an empty list. The key of SparseArray is display ID.
+ */
+ @NonNull
+ public final SparseArray<List<AccessibilityWindowInfo>> getWindowsOnAllDisplays() {
+ return AccessibilityInteractionClient.getInstance().getWindowsOnAllDisplays(mConnectionId);
+ }
+
+ /**
* Gets the root node in the currently active window if this service
* can retrieve window content. The active window is the one that the user
* is currently touching or the window with input focus, if the user is not
@@ -822,6 +850,7 @@ public abstract class AccessibilityService extends Service {
GestureResultCallbackInfo callbackInfo;
synchronized (mLock) {
callbackInfo = mGestureStatusCallbackInfos.get(sequence);
+ mGestureStatusCallbackInfos.remove(sequence);
}
final GestureResultCallbackInfo finalCallbackInfo = callbackInfo;
if ((callbackInfo != null) && (callbackInfo.gestureDescription != null)
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index cf24b8e1ffa6..e738b19f420a 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -18,6 +18,7 @@ package android.accessibilityservice;
import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
+import android.accessibilityservice.AccessibilityButtonController.AccessibilityButtonCallback;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.UnsupportedAppUsage;
@@ -322,6 +323,14 @@ public class AccessibilityServiceInfo implements Parcelable {
*/
public static final int FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK = 0x00000400;
+ /**
+ * This flag indicates that the accessibility service will handle the shortcut action itself.
+ * A callback {@link AccessibilityButtonCallback#onClicked(AccessibilityButtonController)} is
+ * called when the user presses the accessibility shortcut. Otherwise, the service is enabled
+ * or disabled by the system instead.
+ */
+ public static final int FLAG_HANDLE_SHORTCUT = 0x00000800;
+
/** {@hide} */
public static final int FLAG_FORCE_DIRECT_BOOT_AWARE = 0x00010000;
@@ -423,12 +432,13 @@ public class AccessibilityServiceInfo implements Parcelable {
* @see #FLAG_ENABLE_ACCESSIBILITY_VOLUME
* @see #FLAG_REQUEST_ACCESSIBILITY_BUTTON
* @see #FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK
+ * @see #FLAG_HANDLE_SHORTCUT
*/
public int flags;
/**
* Whether or not the service has crashed and is awaiting restart. Only valid from {@link
- * android.view.accessibility.AccessibilityManager#getEnabledAccessibilityServiceList(int)},
+ * android.view.accessibility.AccessibilityManager#getInstalledAccessibilityServiceList()},
* because that is populated from the internal list of running services.
*
* @hide
@@ -1103,6 +1113,8 @@ public class AccessibilityServiceInfo implements Parcelable {
return "FLAG_REQUEST_FINGERPRINT_GESTURES";
case FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK:
return "FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK";
+ case FLAG_HANDLE_SHORTCUT:
+ return "FLAG_HANDLE_SHORTCUT";
default:
return null;
}
diff --git a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
new file mode 100644
index 000000000000..f4cadfd8bbc1
--- /dev/null
+++ b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accessibilityservice;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.util.AttributeSet;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * Activities of interest to users with accessibility needs may request to be targets of the
+ * accessibility shortcut. These activities must handle the
+ * {@link Intent#ACTION_MAIN} intent with category
+ * {@link Intent#CATEGORY_ACCESSIBILITY_SHORTCUT_TARGET}, which will be dispatched by the system
+ * when the user activates the shortcut when it is configured to point at this target.
+ *
+ * @see Intent#CATEGORY_ACCESSIBILITY_SHORTCUT_TARGET
+ *
+ * @hide
+ */
+public final class AccessibilityShortcutInfo {
+ private static final String TAG_ACCESSIBILITY_SHORTCUT = "accessibility-shortcut-target";
+
+ /**
+ * Name under which an activity component of the accessibility shortcut publishes information
+ * about itself. This meta-data must reference an XML resource containing an
+ * <code>&lt;accessibility-shortcut-target&gt;</code> tag.
+ */
+ public static final String META_DATA = "android.accessibilityshortcut.target";
+
+ /**
+ * The component name of the accessibility shortcut target.
+ */
+ private final ComponentName mComponentName;
+
+ /**
+ * The activity info of the accessibility shortcut target.
+ */
+ private final ActivityInfo mActivityInfo;
+
+ /**
+ * Resource id of the summary of the accessibility shortcut target.
+ */
+ private final int mSummaryResId;
+
+ /**
+ * Resource id of the description of the accessibility shortcut target.
+ */
+ private final int mDescriptionResId;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param context Context for accessing resources.
+ * @param activityInfo The activity info.
+ * @throws XmlPullParserException If a XML parsing error occurs.
+ * @throws IOException If a XML parsing error occurs.
+ */
+ public AccessibilityShortcutInfo(@NonNull Context context, @NonNull ActivityInfo activityInfo)
+ throws XmlPullParserException, IOException {
+ final PackageManager packageManager = context.getPackageManager();
+ mComponentName = activityInfo.getComponentName();
+ mActivityInfo = activityInfo;
+
+ try (XmlResourceParser parser = mActivityInfo.loadXmlMetaData(
+ packageManager, META_DATA)) {
+ if (parser == null) {
+ throw new XmlPullParserException("Meta-data "
+ + TAG_ACCESSIBILITY_SHORTCUT + " does not exist");
+ }
+
+ int type = 0;
+ while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
+ type = parser.next();
+ }
+
+ final String nodeName = parser.getName();
+ if (!TAG_ACCESSIBILITY_SHORTCUT.equals(nodeName)) {
+ throw new XmlPullParserException("Meta-data does not start with"
+ + TAG_ACCESSIBILITY_SHORTCUT + " tag");
+ }
+
+ final AttributeSet allAttributes = Xml.asAttributeSet(parser);
+ final Resources resources = packageManager.getResourcesForApplication(
+ mActivityInfo.applicationInfo);
+ final TypedArray asAttributes = resources.obtainAttributes(allAttributes,
+ com.android.internal.R.styleable.AccessibilityShortcutTarget);
+
+ // Gets description
+ mDescriptionResId = asAttributes.getResourceId(
+ com.android.internal.R.styleable.AccessibilityShortcutTarget_description, 0);
+ // Gets summary
+ mSummaryResId = asAttributes.getResourceId(
+ com.android.internal.R.styleable.AccessibilityShortcutTarget_summary, 0);
+ asAttributes.recycle();
+
+ if (mDescriptionResId == 0 || mSummaryResId == 0) {
+ throw new XmlPullParserException("No description or summary in meta-data");
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new XmlPullParserException("Unable to create context for: "
+ + mActivityInfo.packageName);
+ }
+ }
+
+ /**
+ * The {@link ActivityInfo} of accessibility shortcut target.
+ *
+ * @return The activity info.
+ */
+ @NonNull
+ public ActivityInfo getActivityInfo() {
+ return mActivityInfo;
+ }
+
+ /**
+ * The localized summary of the accessibility shortcut target.
+ *
+ * @return The localized summary if available, and {@code null} if a summary
+ * has not been provided.
+ */
+ @Nullable
+ public String loadSummary(@NonNull PackageManager packageManager) {
+ return loadResourceString(packageManager, mActivityInfo, mSummaryResId);
+ }
+
+ /**
+ * The localized description of the accessibility shortcut target.
+ *
+ * @return The localized description.
+ */
+ @Nullable
+ public String loadDescription(@NonNull PackageManager packageManager) {
+ return loadResourceString(packageManager, mActivityInfo, mDescriptionResId);
+ }
+
+ /**
+ * Gets string resource by the given activity and resource id.
+ */
+ @Nullable
+ private String loadResourceString(@NonNull PackageManager packageManager,
+ @NonNull ActivityInfo activityInfo, int resId) {
+ if (resId == 0) {
+ return null;
+ }
+ final CharSequence text = packageManager.getText(activityInfo.packageName,
+ resId, activityInfo.applicationInfo);
+ if (text != null) {
+ return text.toString().trim();
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return 31 * 1 + ((mComponentName == null) ? 0 : mComponentName.hashCode());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final AccessibilityShortcutInfo other = (AccessibilityShortcutInfo) obj;
+ if (mComponentName == null) {
+ if (other.mComponentName != null) {
+ return false;
+ }
+ } else if (!mComponentName.equals(other.mComponentName)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("AccessibilityShortcutInfo[");
+ stringBuilder.append("activityInfo: ").append(mActivityInfo);
+ stringBuilder.append("]");
+ return stringBuilder.toString();
+ }
+}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 1ca07dd6ed23..4841781170e1 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -60,7 +60,7 @@ interface IAccessibilityServiceConnection {
AccessibilityWindowInfo getWindow(int windowId);
- List<AccessibilityWindowInfo> getWindows();
+ AccessibilityWindowInfo.WindowListSparseArray getWindows();
AccessibilityServiceInfo getServiceInfo();
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index a387576c6059..b08269dbaed3 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -565,6 +565,21 @@ public class AccountManager {
* account, or the AbstractAcccountAuthenticator managing the account did so or because the
* client shares a signature with the managing AbstractAccountAuthenticator.
*
+ * <div class="caution"><p><b>Caution: </b>This method returns personal and sensitive user data.
+ * If your app accesses, collects, uses, or shares personal and sensitive data, you must clearly
+ * disclose that fact to users. For apps published on Google Play, policies protecting user data
+ * require that you do the following:</p>
+ * <ul>
+ * <li>Disclose to the user how your app accesses, collects, uses, or shares personal and
+ * sensitive data. Learn more about
+ * <a href="https://play.google.com/about/privacy-security-deception/user-data/#!#personal-sensitive">acceptable
+ * disclosure and consent</a>.</li>
+ * <li>Provide a privacy policy that describes your use of this data on- and off-device.</li>
+ * </ul>
+ * <p>To learn more, visit the
+ * <a href="https://play.google.com/about/privacy-security-deception/user-data">Google Play
+ * Policy regarding user data</a>.</p></div>
+ *
* <p>
* It is safe to call this method from the main thread.
*
@@ -651,6 +666,22 @@ public class AccountManager {
* the account. For example, there are types corresponding to Google and Facebook. The exact
* string token to use will be published somewhere associated with the authenticator in
* question.
+ * </p>
+ *
+ * <div class="caution"><p><b>Caution: </b>This method returns personal and sensitive user data.
+ * If your app accesses, collects, uses, or shares personal and sensitive data, you must clearly
+ * disclose that fact to users. For apps published on Google Play, policies protecting user data
+ * require that you do the following:</p>
+ * <ul>
+ * <li>Disclose to the user how your app accesses, collects, uses, or shares personal and
+ * sensitive data. Learn more about
+ * <a href="https://play.google.com/about/privacy-security-deception/user-data/#!#personal-sensitive">acceptable
+ * disclosure and consent</a>.</li>
+ * <li>Provide a privacy policy that describes your use of this data on- and off-device.</li>
+ * </ul>
+ * <p>To learn more, visit the
+ * <a href="https://play.google.com/about/privacy-security-deception/user-data">Google Play
+ * Policy regarding user data</a>.</p></div>
*
* <p>
* It is safe to call this method from the main thread.
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index bf7d6324764f..2c57622a4df8 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -76,7 +76,6 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.StrictMode;
-import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.text.Selection;
@@ -1925,11 +1924,15 @@ public class Activity extends ContextThemeWrapper
}
/**
- * Like {@link #isVoiceInteraction}, but only returns true if this is also the root
- * of a voice interaction. That is, returns true if this activity was directly
+ * Like {@link #isVoiceInteraction}, but only returns {@code true} if this is also the root
+ * of a voice interaction. That is, returns {@code true} if this activity was directly
* started by the voice interaction service as the initiation of a voice interaction.
* Otherwise, for example if it was started by another activity while under voice
- * interaction, returns false.
+ * interaction, returns {@code false}.
+ * If the activity {@link android.R.styleable#AndroidManifestActivity_launchMode launchMode} is
+ * {@code singleTask}, it forces the activity to launch in a new task, separate from the one
+ * that started it. Therefore, there is no longer a relationship between them, and
+ * {@link #isVoiceInteractionRoot()} return {@code false} in this case.
*/
public boolean isVoiceInteractionRoot() {
try {
@@ -2473,17 +2476,13 @@ public class Activity extends ContextThemeWrapper
getAutofillManager().onInvisibleForAutofill();
}
- if (isFinishing()) {
- if (mAutoFillResetNeeded) {
- getAutofillManager().onActivityFinishing();
- } else if (mIntent != null
- && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) {
- // Activity was launched when user tapped a link in the Autofill Save UI - since
- // user launched another activity, the Save UI should not be restored when this
- // activity is finished.
- getAutofillManager().onPendingSaveUi(AutofillManager.PENDING_UI_OPERATION_CANCEL,
- mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN));
- }
+ if (isFinishing() && !mAutoFillResetNeeded && mIntent != null
+ && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) {
+ // Activity was launched when user tapped a link in the Autofill Save UI - since
+ // user launched another activity, the Save UI should not be restored when this
+ // activity is finished.
+ getAutofillManager().onPendingSaveUi(AutofillManager.PENDING_UI_OPERATION_CANCEL,
+ mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN));
}
mEnterAnimationComplete = false;
}
@@ -2521,6 +2520,10 @@ public class Activity extends ContextThemeWrapper
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onDestroy " + this);
mCalled = true;
+ if (isFinishing() && mAutoFillResetNeeded) {
+ getAutofillManager().onActivityFinishing();
+ }
+
// dismiss any dialogs we are managing.
if (mManagedDialogs != null) {
final int numDialogs = mManagedDialogs.size();
@@ -7094,14 +7097,28 @@ public class Activity extends ContextThemeWrapper
}
/**
- * Convert a translucent themed Activity {@link android.R.attr#windowIsTranslucent} to a
- * fullscreen opaque Activity.
- * <p>
- * Call this whenever the background of a translucent Activity has changed to become opaque.
- * Doing so will allow the {@link android.view.Surface} of the Activity behind to be released.
+ * Convert an activity, which particularly with {@link android.R.attr#windowIsTranslucent} or
+ * {@link android.R.attr#windowIsFloating} attribute, to a fullscreen opaque activity, or
+ * convert it from opaque back to translucent.
+ *
+ * @param translucent {@code true} convert from opaque to translucent.
+ * {@code false} convert from translucent to opaque.
+ * @return The result of setting translucency. Return {@code true} if set successfully,
+ * {@code false} otherwise.
+ */
+ public boolean setTranslucent(boolean translucent) {
+ if (translucent) {
+ return convertToTranslucent(null /* callback */, null /* options */);
+ } else {
+ return convertFromTranslucentInternal();
+ }
+ }
+
+ /**
+ * Convert an activity to a fullscreen opaque activity.
* <p>
- * This call has no effect on non-translucent activities or on activities with the
- * {@link android.R.attr#windowIsFloating} attribute.
+ * Call this whenever the background of a translucent activity has changed to become opaque.
+ * Doing so will allow the {@link android.view.Surface} of the activity behind to be released.
*
* @see #convertToTranslucent(android.app.Activity.TranslucentConversionListener,
* ActivityOptions)
@@ -7111,31 +7128,33 @@ public class Activity extends ContextThemeWrapper
*/
@SystemApi
public void convertFromTranslucent() {
+ convertFromTranslucentInternal();
+ }
+
+ private boolean convertFromTranslucentInternal() {
try {
mTranslucentCallback = null;
if (ActivityTaskManager.getService().convertFromTranslucent(mToken)) {
WindowManagerGlobal.getInstance().changeCanvasOpacity(mToken, true);
+ return true;
}
} catch (RemoteException e) {
// pass
}
+ return false;
}
/**
- * Convert a translucent themed Activity {@link android.R.attr#windowIsTranslucent} back from
- * opaque to translucent following a call to {@link #convertFromTranslucent()}.
+ * Convert an activity to a translucent activity.
* <p>
- * Calling this allows the Activity behind this one to be seen again. Once all such Activities
+ * Calling this allows the activity behind this one to be seen again. Once all such activities
* have been redrawn {@link TranslucentConversionListener#onTranslucentConversionComplete} will
* be called indicating that it is safe to make this activity translucent again. Until
* {@link TranslucentConversionListener#onTranslucentConversionComplete} is called the image
- * behind the frontmost Activity will be indeterminate.
- * <p>
- * This call has no effect on non-translucent activities or on activities with the
- * {@link android.R.attr#windowIsFloating} attribute.
+ * behind the frontmost activity will be indeterminate.
*
- * @param callback the method to call when all visible Activities behind this one have been
- * drawn and it is safe to make this Activity translucent again.
+ * @param callback the method to call when all visible activities behind this one have been
+ * drawn and it is safe to make this activity translucent again.
* @param options activity options delivered to the activity below this one. The options
* are retrieved using {@link #getActivityOptions}.
* @return <code>true</code> if Window was opaque and will become translucent or
@@ -7862,13 +7881,10 @@ public class Activity extends ContextThemeWrapper
mFragments.dispatchStart();
mFragments.reportLoaderStart();
+ // Warn app developers if the dynamic linker logged anything during startup.
boolean isAppDebuggable =
(mApplication.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
-
- // This property is set for all non-user builds except final release
- boolean isDlwarningEnabled = SystemProperties.getInt("ro.bionic.ld.warning", 0) == 1;
-
- if (isAppDebuggable || isDlwarningEnabled) {
+ if (isAppDebuggable) {
String dlwarning = getDlWarning();
if (dlwarning != null) {
String appName = getApplicationInfo().loadLabel(getPackageManager())
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index cb99a3aa11c6..7f597fef807a 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -188,57 +188,6 @@ public class ActivityManager {
final ArrayMap<OnUidImportanceListener, UidObserver> mImportanceListeners = new ArrayMap<>();
/**
- * Defines acceptable types of bugreports.
- * @hide
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "BUGREPORT_OPTION_" }, value = {
- BUGREPORT_OPTION_FULL,
- BUGREPORT_OPTION_INTERACTIVE,
- BUGREPORT_OPTION_REMOTE,
- BUGREPORT_OPTION_WEAR,
- BUGREPORT_OPTION_TELEPHONY,
- BUGREPORT_OPTION_WIFI
- })
- public @interface BugreportMode {}
- /**
- * Takes a bugreport without user interference (and hence causing less
- * interference to the system), but includes all sections.
- * @hide
- */
- public static final int BUGREPORT_OPTION_FULL = 0;
- /**
- * Allows user to monitor progress and enter additional data; might not include all
- * sections.
- * @hide
- */
- public static final int BUGREPORT_OPTION_INTERACTIVE = 1;
- /**
- * Takes a bugreport requested remotely by administrator of the Device Owner app,
- * not the device's user.
- * @hide
- */
- public static final int BUGREPORT_OPTION_REMOTE = 2;
- /**
- * Takes a bugreport on a wearable device.
- * @hide
- */
- public static final int BUGREPORT_OPTION_WEAR = 3;
-
- /**
- * Takes a lightweight version of bugreport that only includes a few, urgent sections
- * used to report telephony bugs.
- * @hide
- */
- public static final int BUGREPORT_OPTION_TELEPHONY = 4;
-
- /**
- * Takes a lightweight bugreport that only includes a few sections related to Wifi.
- * @hide
- */
- public static final int BUGREPORT_OPTION_WIFI = 5;
-
- /**
* <a href="{@docRoot}guide/topics/manifest/meta-data-element.html">{@code
* <meta-data>}</a> name for a 'home' Activity that declares a package that is to be
* uninstalled in lieu of the declaring one. The package named here must be
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index cbfd53638d00..d6458ec312d5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1164,6 +1164,10 @@ public final class ActivityThread extends ClientTransactionHandler {
sendMessage(H.ATTACH_AGENT, agent);
}
+ public void attachStartupAgents(String dataDir) {
+ sendMessage(H.ATTACH_STARTUP_AGENTS, dataDir);
+ }
+
public void setSchedulingGroup(int group) {
// Note: do this immediately, since going into the foreground
// should happen regardless of what pending work we have to do
@@ -1813,6 +1817,7 @@ public final class ActivityThread extends ClientTransactionHandler {
public static final int EXECUTE_TRANSACTION = 159;
public static final int RELAUNCH_ACTIVITY = 160;
public static final int PURGE_RESOURCES = 161;
+ public static final int ATTACH_STARTUP_AGENTS = 162;
String codeToString(int code) {
if (DEBUG_MESSAGES) {
@@ -1856,6 +1861,7 @@ public final class ActivityThread extends ClientTransactionHandler {
case EXECUTE_TRANSACTION: return "EXECUTE_TRANSACTION";
case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY";
case PURGE_RESOURCES: return "PURGE_RESOURCES";
+ case ATTACH_STARTUP_AGENTS: return "ATTACH_STARTUP_AGENTS";
}
}
return Integer.toString(code);
@@ -2044,6 +2050,9 @@ public final class ActivityThread extends ClientTransactionHandler {
case PURGE_RESOURCES:
schedulePurgeIdler();
break;
+ case ATTACH_STARTUP_AGENTS:
+ handleAttachStartupAgents((String) msg.obj);
+ break;
}
Object obj = msg.obj;
if (obj instanceof SomeArgs) {
@@ -3780,6 +3789,27 @@ public final class ActivityThread extends ClientTransactionHandler {
}
}
+ static void handleAttachStartupAgents(String dataDir) {
+ try {
+ Path code_cache = ContextImpl.getCodeCacheDirBeforeBind(new File(dataDir)).toPath();
+ if (!Files.exists(code_cache)) {
+ return;
+ }
+ Path startup_path = code_cache.resolve("startup_agents");
+ if (Files.exists(startup_path)) {
+ for (Path p : Files.newDirectoryStream(startup_path)) {
+ handleAttachAgent(
+ p.toAbsolutePath().toString()
+ + "="
+ + dataDir,
+ null);
+ }
+ }
+ } catch (Exception e) {
+ // Ignored.
+ }
+ }
+
private static final ThreadLocal<Intent> sCurrentBroadcastIntent = new ThreadLocal<Intent>();
/**
@@ -4836,7 +4866,7 @@ public final class ActivityThread extends ClientTransactionHandler {
View.sDebugViewAttributesApplicationPackage = mCoreSettings.getString(
Settings.Global.DEBUG_VIEW_ATTRIBUTES_APPLICATION_PACKAGE, "");
String currentPackage = (mBoundApplication != null && mBoundApplication.appInfo != null)
- ? mBoundApplication.appInfo.packageName : "";
+ ? mBoundApplication.appInfo.packageName : "<unknown-app>";
View.sDebugViewAttributes =
mCoreSettings.getInt(Settings.Global.DEBUG_VIEW_ATTRIBUTES, 0) != 0
|| View.sDebugViewAttributesApplicationPackage.equals(currentPackage);
@@ -6446,26 +6476,6 @@ public final class ActivityThread extends ClientTransactionHandler {
NetworkSecurityConfigProvider.install(appContext);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
- if (isAppDebuggable) {
- try {
- // Load all the agents in the code_cache/startup_agents directory.
- // We pass the absolute path to the data_dir as an argument.
- Path startup_path = appContext.getCodeCacheDir().toPath().resolve("startup_agents");
- if (Files.exists(startup_path)) {
- for (Path p : Files.newDirectoryStream(startup_path)) {
- handleAttachAgent(
- p.toAbsolutePath().toString()
- + "="
- + appContext.getDataDir().toPath().toAbsolutePath().toString(),
- data.info);
- }
- }
- } catch (Exception e) {
- // Ignored.
- }
- }
-
// Continue loading instrumentation.
if (ii != null) {
ApplicationInfo instrApp;
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 0f9a6e63a1d2..5c4125e01dfd 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -204,14 +204,7 @@ class ActivityTransitionState {
}
mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning(),
- mEnterActivityOptions.isCrossTask(),
- () -> {
- if (isReturning) {
- // once it is done transitioning, we don't need the coordinator --
- // if we kept it around, it could leak Views
- mEnterTransitionCoordinator = null;
- }
- });
+ mEnterActivityOptions.isCrossTask());
if (mEnterActivityOptions.isCrossTask()) {
mExitingFrom = new ArrayList<>(mEnterActivityOptions.getSharedElementNames());
mExitingTo = new ArrayList<>(mEnterActivityOptions.getSharedElementNames());
@@ -280,6 +273,10 @@ class ActivityTransitionState {
mEnterTransitionCoordinator.isWaitingForRemoteExit()) {
restoreExitedViews();
restoreReenteringViews();
+ } else if (mEnterTransitionCoordinator.isReturning()) {
+ mEnterTransitionCoordinator.runAfterTransitionsComplete(() -> {
+ mEnterTransitionCoordinator = null;
+ });
}
}
}, 1000);
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index e8918285777b..fbf1f59141a8 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -22,6 +22,7 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLI
import static android.view.Display.INVALID_DISPLAY;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.app.ActivityManager.StackInfo;
import android.content.ComponentName;
@@ -29,6 +30,7 @@ import android.content.Context;
import android.content.Intent;
import android.graphics.Insets;
import android.graphics.Matrix;
+import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
@@ -101,6 +103,8 @@ public class ActivityView extends ViewGroup {
private Insets mForwardedInsets;
+ private float mCornerRadius;
+
public ActivityView(Context context) {
this(context, null /* attrs */);
}
@@ -204,6 +208,45 @@ public class ActivityView extends ViewGroup {
}
/**
+ * @hide
+ */
+ public float getCornerRadius() {
+ return mSurfaceView.getCornerRadius();
+ }
+
+ /**
+ * Control whether the surface is clipped to the same bounds as the View. If true, then
+ * the bounds set by {@link #setSurfaceClipBounds(Rect)} are applied to the surface as
+ * window-crop.
+ *
+ * @param clippingEnabled whether to enable surface clipping
+ * @hide
+ */
+ public void setSurfaceClippingEnabled(boolean clippingEnabled) {
+ mSurfaceView.setEnableSurfaceClipping(clippingEnabled);
+ }
+
+ /**
+ * Sets an area on the contained surface to which it will be clipped
+ * when it is drawn. Setting the value to null will remove the clip bounds
+ * and the surface will draw normally, using its full bounds.
+ *
+ * @param clipBounds The rectangular area, in the local coordinates of
+ * this view, to which future drawing operations will be clipped.
+ * @hide
+ */
+ public void setSurfaceClipBounds(Rect clipBounds) {
+ mSurfaceView.setClipBounds(clipBounds);
+ }
+
+ /**
+ * @hide
+ */
+ public boolean getSurfaceClipBounds(Rect outRect) {
+ return mSurfaceView.getClipBounds(outRect);
+ }
+
+ /**
* Launch a new activity into this container.
* <p>Activity resolved by the provided {@link Intent} must have
* {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
@@ -282,16 +325,17 @@ public class ActivityView extends ViewGroup {
* this method can be called.
*
* @param pendingIntent Intent used to launch an activity.
+ * @param fillInIntent Additional Intent data, see {@link Intent#fillIn Intent.fillIn()}.
* @param options options for the activity
*
* @see StateCallback
* @see #startActivity(Intent)
*/
- public void startActivity(@NonNull PendingIntent pendingIntent,
+ public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
@NonNull ActivityOptions options) {
options.setLaunchDisplayId(mVirtualDisplay.getDisplay().getDisplayId());
try {
- pendingIntent.send(null /* context */, 0 /* code */, null /* intent */,
+ pendingIntent.send(getContext(), 0 /* code */, fillInIntent,
null /* onFinished */, null /* handler */, null /* requiredPermission */,
options.toBundle());
} catch (PendingIntent.CanceledException e) {
diff --git a/core/java/android/app/AppCompatCallbacks.java b/core/java/android/app/AppCompatCallbacks.java
index 08c97eb284e3..19d158dedd06 100644
--- a/core/java/android/app/AppCompatCallbacks.java
+++ b/core/java/android/app/AppCompatCallbacks.java
@@ -18,7 +18,6 @@ package android.app;
import android.compat.Compatibility;
import android.os.Process;
-import android.util.Log;
import android.util.StatsLog;
import com.android.internal.compat.ChangeReporter;
@@ -31,8 +30,6 @@ import java.util.Arrays;
* @hide
*/
public final class AppCompatCallbacks extends Compatibility.Callbacks {
- private static final String TAG = "Compatibility";
-
private final long[] mDisabledChanges;
private final ChangeReporter mChangeReporter;
@@ -48,7 +45,8 @@ public final class AppCompatCallbacks extends Compatibility.Callbacks {
private AppCompatCallbacks(long[] disabledChanges) {
mDisabledChanges = Arrays.copyOf(disabledChanges, disabledChanges.length);
Arrays.sort(mDisabledChanges);
- mChangeReporter = new ChangeReporter();
+ mChangeReporter = new ChangeReporter(
+ StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__APP_PROCESS);
}
protected void reportChange(long changeId) {
@@ -67,10 +65,7 @@ public final class AppCompatCallbacks extends Compatibility.Callbacks {
private void reportChange(long changeId, int state) {
int uid = Process.myUid();
- //TODO(b/138374585): Implement rate limiting for the logs.
- Log.d(TAG, ChangeReporter.createLogString(uid, changeId, state));
- mChangeReporter.reportChange(uid, changeId,
- state, /* source */StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__APP_PROCESS);
+ mChangeReporter.reportChange(uid, changeId, state);
}
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 563174bb9534..d5e41f0eacbe 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -50,6 +50,7 @@ import android.os.UserManager;
import android.util.ArrayMap;
import android.util.LongSparseArray;
import android.util.LongSparseLongArray;
+import android.util.Pair;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
@@ -77,10 +78,12 @@ import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Supplier;
+import java.util.function.ToLongFunction;
/**
* API for interacting with "application operation" tracking.
@@ -853,12 +856,14 @@ public class AppOpsManager {
public static final int OP_ACCESS_ACCESSIBILITY = 88;
/** @hide Read the device identifiers (IMEI / MEID, IMSI, SIM / Build serial) */
public static final int OP_READ_DEVICE_IDENTIFIERS = 89;
+ /** @hide Read location metadata from media */
+ public static final int OP_ACCESS_MEDIA_LOCATION = 90;
/** @hide Query all apps on device, regardless of declarations in the calling app manifest */
- public static final int OP_QUERY_ALL_PACKAGES = 90;
+ public static final int OP_QUERY_ALL_PACKAGES = 91;
/** @hide */
@UnsupportedAppUsage
- public static final int _NUM_OP = 91;
+ public static final int _NUM_OP = 92;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1134,6 +1139,8 @@ public class AppOpsManager {
/** @hide Has a legacy (non-isolated) view of storage. */
@SystemApi @TestApi
public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage";
+ /** @hide Read location metadata from media */
+ public static final String OPSTR_ACCESS_MEDIA_LOCATION = "android:access_media_location";
/** @hide Interact with accessibility. */
@SystemApi
@@ -1180,6 +1187,7 @@ public class AppOpsManager {
// Storage
OP_READ_EXTERNAL_STORAGE,
OP_WRITE_EXTERNAL_STORAGE,
+ OP_ACCESS_MEDIA_LOCATION,
// Location
OP_COARSE_LOCATION,
OP_FINE_LOCATION,
@@ -1322,6 +1330,7 @@ public class AppOpsManager {
OP_LEGACY_STORAGE, // LEGACY_STORAGE
OP_ACCESS_ACCESSIBILITY, // ACCESS_ACCESSIBILITY
OP_READ_DEVICE_IDENTIFIERS, // READ_DEVICE_IDENTIFIERS
+ OP_ACCESS_MEDIA_LOCATION, // ACCESS_MEDIA_LOCATION
OP_QUERY_ALL_PACKAGES, // QUERY_ALL_PACKAGES
};
@@ -1419,6 +1428,7 @@ public class AppOpsManager {
OPSTR_LEGACY_STORAGE,
OPSTR_ACCESS_ACCESSIBILITY,
OPSTR_READ_DEVICE_IDENTIFIERS,
+ OPSTR_ACCESS_MEDIA_LOCATION,
OPSTR_QUERY_ALL_PACKAGES,
};
@@ -1517,6 +1527,7 @@ public class AppOpsManager {
"LEGACY_STORAGE",
"ACCESS_ACCESSIBILITY",
"READ_DEVICE_IDENTIFIERS",
+ "ACCESS_MEDIA_LOCATION",
"QUERY_ALL_PACKAGES",
};
@@ -1616,6 +1627,7 @@ public class AppOpsManager {
null, // no permission for OP_LEGACY_STORAGE
null, // no permission for OP_ACCESS_ACCESSIBILITY
null, // no direct permission for OP_READ_DEVICE_IDENTIFIERS
+ Manifest.permission.ACCESS_MEDIA_LOCATION,
null, // no permission for OP_QUERY_ALL_PACKAGES
};
@@ -1715,6 +1727,7 @@ public class AppOpsManager {
null, // LEGACY_STORAGE
null, // ACCESS_ACCESSIBILITY
null, // READ_DEVICE_IDENTIFIERS
+ null, // ACCESS_MEDIA_LOCATION
null, // QUERY_ALL_PACKAGES
};
@@ -1813,6 +1826,7 @@ public class AppOpsManager {
false, // LEGACY_STORAGE
false, // ACCESS_ACCESSIBILITY
false, // READ_DEVICE_IDENTIFIERS
+ false, // ACCESS_MEDIA_LOCATION
false, // QUERY_ALL_PACKAGES
};
@@ -1910,6 +1924,7 @@ public class AppOpsManager {
AppOpsManager.MODE_DEFAULT, // LEGACY_STORAGE
AppOpsManager.MODE_ALLOWED, // ACCESS_ACCESSIBILITY
AppOpsManager.MODE_ERRORED, // READ_DEVICE_IDENTIFIERS
+ AppOpsManager.MODE_ALLOWED, // ALLOW_MEDIA_LOCATION
AppOpsManager.MODE_DEFAULT, // QUERY_ALL_PACKAGES
};
@@ -2011,6 +2026,7 @@ public class AppOpsManager {
false, // LEGACY_STORAGE
false, // ACCESS_ACCESSIBILITY
false, // READ_DEVICE_IDENTIFIERS
+ false, // ACCESS_MEDIA_LOCATION
false, // QUERY_ALL_PACKAGES
};
@@ -2039,7 +2055,7 @@ public class AppOpsManager {
*
* @see #markAppOpNoted
*/
- private static final ThreadLocal<long[]> sAppOpsNotedInThisBinderTransaction =
+ private static final ThreadLocal<ArrayMap<String, long[]>> sAppOpsNotedInThisBinderTransaction =
new ThreadLocal<>();
/** Whether noting for an appop should be collected */
@@ -2316,51 +2332,42 @@ public class AppOpsManager {
}
/**
- * Class holding the information about one unique operation of an application.
+ * Class holding the information about one unique operation of a
+ * {@link Context#createFeatureContext(String) feature}.
+ *
* @hide
*/
@TestApi
@Immutable
@SystemApi
- public static final class OpEntry implements Parcelable {
- private final int mOp;
+ public static final class OpFeatureEntry {
+ private final @NonNull OpEntry mParent;
private final boolean mRunning;
- private final @Mode int mMode;
+
private final @Nullable LongSparseLongArray mAccessTimes;
private final @Nullable LongSparseLongArray mRejectTimes;
private final @Nullable LongSparseLongArray mDurations;
private final @Nullable LongSparseLongArray mProxyUids;
private final @Nullable LongSparseArray<String> mProxyPackageNames;
+ private final @Nullable LongSparseArray<String> mProxyFeatureIds;
/**
* @hide
*/
- public OpEntry(int op, boolean running, @Mode int mode,
- @Nullable LongSparseLongArray accessTimes, @Nullable LongSparseLongArray rejectTimes,
+ public OpFeatureEntry(@NonNull OpEntry parent, boolean running,
+ @Nullable LongSparseLongArray accessTimes,
+ @Nullable LongSparseLongArray rejectTimes,
@Nullable LongSparseLongArray durations, @Nullable LongSparseLongArray proxyUids,
- @Nullable LongSparseArray<String> proxyPackageNames) {
- mOp = op;
+ @Nullable LongSparseArray<String> proxyPackageNames,
+ @Nullable LongSparseArray<String> proxyFeatureIds) {
+ mParent = Preconditions.checkNotNull(parent);
mRunning = running;
- mMode = mode;
mAccessTimes = accessTimes;
mRejectTimes = rejectTimes;
mDurations = durations;
mProxyUids = proxyUids;
mProxyPackageNames = proxyPackageNames;
- }
-
- /**
- * @hide
- */
- public OpEntry(int op, @Mode int mode) {
- mOp = op;
- mMode = mode;
- mRunning = false;
- mAccessTimes = null;
- mRejectTimes = null;
- mDurations = null;
- mProxyUids = null;
- mProxyPackageNames = null;
+ mProxyFeatureIds = proxyFeatureIds;
}
/**
@@ -2377,29 +2384,6 @@ public class AppOpsManager {
/**
* @hide
*/
- @UnsupportedAppUsage
- public int getOp() {
- return mOp;
- }
-
- /**
- * @return This entry's op string name, such as {@link #OPSTR_COARSE_LOCATION}.
- */
- public @NonNull String getOpStr() {
- return sOpToString[mOp];
- }
-
- /**
- * @return this entry's current mode, such as {@link #MODE_ALLOWED}.
- */
- public @Mode int getMode() {
- return mMode;
- }
-
- /**
- * @hide
- */
- @UnsupportedAppUsage
public long getTime() {
return getLastAccessTime(OP_FLAGS_ALL);
}
@@ -2442,7 +2426,7 @@ public class AppOpsManager {
*/
public long getLastAccessForegroundTime(@OpFlags int flags) {
return maxForFlagsInStates(mAccessTimes, MAX_PRIORITY_UID_STATE,
- resolveFirstUnrestrictedUidState(mOp), flags);
+ resolveFirstUnrestrictedUidState(mParent.mOp), flags);
}
/**
@@ -2462,7 +2446,7 @@ public class AppOpsManager {
* @see #getLastAccessTime(int, int, int)
*/
public long getLastAccessBackgroundTime(@OpFlags int flags) {
- return maxForFlagsInStates(mAccessTimes, resolveLastRestrictedUidState(mOp),
+ return maxForFlagsInStates(mAccessTimes, resolveLastRestrictedUidState(mParent.mOp),
MIN_PRIORITY_UID_STATE, flags);
}
@@ -2496,7 +2480,6 @@ public class AppOpsManager {
/**
* @hide
*/
- @UnsupportedAppUsage
public long getRejectTime() {
return getLastRejectTime(OP_FLAGS_ALL);
}
@@ -2540,7 +2523,7 @@ public class AppOpsManager {
*/
public long getLastRejectForegroundTime(@OpFlags int flags) {
return maxForFlagsInStates(mRejectTimes, MAX_PRIORITY_UID_STATE,
- resolveFirstUnrestrictedUidState(mOp), flags);
+ resolveFirstUnrestrictedUidState(mParent.mOp), flags);
}
/**
@@ -2560,7 +2543,7 @@ public class AppOpsManager {
* @see #getLastRejectTime(int)
*/
public long getLastRejectBackgroundTime(@OpFlags int flags) {
- return maxForFlagsInStates(mRejectTimes, resolveLastRestrictedUidState(mOp),
+ return maxForFlagsInStates(mRejectTimes, resolveLastRestrictedUidState(mParent.mOp),
MIN_PRIORITY_UID_STATE, flags);
}
@@ -2620,7 +2603,7 @@ public class AppOpsManager {
*/
public long getLastForegroundDuration(@OpFlags int flags) {
return sumForFlagsInStates(mDurations, MAX_PRIORITY_UID_STATE,
- resolveFirstUnrestrictedUidState(mOp), flags);
+ resolveFirstUnrestrictedUidState(mParent.mOp), flags);
}
/**
@@ -2638,7 +2621,7 @@ public class AppOpsManager {
* @see #getLastDuration(int, int, int)
*/
public long getLastBackgroundDuration(@OpFlags int flags) {
- return sumForFlagsInStates(mDurations, resolveLastRestrictedUidState(mOp),
+ return sumForFlagsInStates(mDurations, resolveLastRestrictedUidState(mParent.mOp),
MIN_PRIORITY_UID_STATE, flags);
}
@@ -2723,12 +2706,663 @@ public class AppOpsManager {
* {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
* {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
* for any flag.
- * @return The proxy package name.
+ * @return The feature id.
*/
public @Nullable String getProxyPackageName(@UidState int uidState, @OpFlags int flags) {
return findFirstNonNullForFlagsInStates(mProxyPackageNames, uidState, uidState, flags);
}
+ /**
+ * Gets the feature of the app that performed the op on behalf of this
+ * app and as a result blamed the op on this app or {@code null}
+ * if there is no proxy.
+ *
+ * @return The proxy package name.
+ */
+ public @Nullable String getProxyFeatureId() {
+ return findFirstNonNullForFlagsInStates(mProxyFeatureIds, MAX_PRIORITY_UID_STATE,
+ MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL);
+ }
+
+ /**
+ * Gets the feature of the app that performed the op on behalf of this
+ * app and as a result blamed the op on this app for a UID state or
+ * {@code null} if there is no proxy.
+ *
+ * @param uidState The UID state for which to query. Could be one of
+ * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
+ * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
+ * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
+ * @param flags The flags which are any combination of
+ * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
+ * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
+ * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
+ * for any flag.
+ * @return The feature id.
+ */
+ public @Nullable String getProxyFeatureId(@UidState int uidState, @OpFlags int flags) {
+ return findFirstNonNullForFlagsInStates(mProxyFeatureIds, uidState, uidState, flags);
+ }
+
+ /**
+ * @hide
+ */
+ public static class Builder {
+ private final boolean mRunning;
+
+ private final @Nullable LongSparseLongArray mAccessTimes;
+ private final @Nullable LongSparseLongArray mRejectTimes;
+ private final @Nullable LongSparseLongArray mDurations;
+ private final @Nullable LongSparseLongArray mProxyUids;
+ private final @Nullable LongSparseArray<String> mProxyPackageNames;
+ private final @Nullable LongSparseArray<String> mProxyFeatureIds;
+ private @NonNull OpEntry mParent;
+
+ public Builder(boolean running, @Nullable LongSparseLongArray accessTimes,
+ @Nullable LongSparseLongArray rejectTimes,
+ @Nullable LongSparseLongArray durations,
+ @Nullable LongSparseLongArray proxyUids,
+ @Nullable LongSparseArray<String> proxyPackageNames,
+ @Nullable LongSparseArray<String> proxyFeatureIds) {
+ mRunning = running;
+ mAccessTimes = accessTimes;
+ mRejectTimes = rejectTimes;
+ mDurations = durations;
+ mProxyUids = proxyUids;
+ mProxyPackageNames = proxyPackageNames;
+ mProxyFeatureIds = proxyFeatureIds;
+ }
+
+ public Builder setParent(@NonNull OpEntry parent) {
+ mParent = parent;
+
+ return this;
+ }
+
+ /**
+ * Create OpFeatureEntry from builder
+ */
+ public OpFeatureEntry build() {
+ Preconditions.checkNotNull(mParent);
+
+ return new OpFeatureEntry(mParent, mRunning, mAccessTimes, mRejectTimes,
+ mDurations, mProxyUids, mProxyPackageNames, mProxyFeatureIds);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ LongSparseLongArray.Parcelling longSparseLongArrayParcelling =
+ LongSparseLongArray.Parcelling.Cache.getOrCreate(
+ LongSparseLongArray.Parcelling.class);
+ LongSparseArray.StringParcelling longSparseStringArrayParcelling =
+ LongSparseArray.StringParcelling.Cache.getOrCreate(
+ LongSparseArray.StringParcelling.class);
+
+ dest.writeBoolean(mRunning);
+ longSparseLongArrayParcelling.parcel(mAccessTimes, dest, flags);
+ longSparseLongArrayParcelling.parcel(mRejectTimes, dest, flags);
+ longSparseLongArrayParcelling.parcel(mDurations, dest, flags);
+ longSparseLongArrayParcelling.parcel(mProxyUids, dest, flags);
+ longSparseStringArrayParcelling.parcel(mProxyPackageNames, dest, flags);
+ longSparseStringArrayParcelling.parcel(mProxyFeatureIds, dest, flags);
+ }
+
+ /**
+ * @hide
+ */
+ public static OpFeatureEntry.Builder createFromParcel(@NonNull Parcel source) {
+ LongSparseLongArray.Parcelling longSparseLongArrayParcelling =
+ LongSparseLongArray.Parcelling.Cache.getOrCreate(
+ LongSparseLongArray.Parcelling.class);
+ LongSparseArray.StringParcelling longSparseStringArrayParcelling =
+ LongSparseArray.StringParcelling.Cache.getOrCreate(
+ LongSparseArray.StringParcelling.class);
+
+ return new OpFeatureEntry.Builder(source.readBoolean(),
+ (LongSparseLongArray) longSparseLongArrayParcelling.unparcel(source),
+ (LongSparseLongArray) longSparseLongArrayParcelling.unparcel(source),
+ (LongSparseLongArray) longSparseLongArrayParcelling.unparcel(source),
+ (LongSparseLongArray) longSparseLongArrayParcelling.unparcel(source),
+ (LongSparseArray<String>) longSparseStringArrayParcelling.unparcel(source),
+ (LongSparseArray<String>) longSparseStringArrayParcelling.unparcel(source));
+ }
+ }
+
+ /**
+ * Class holding the information about one unique operation of an application.
+ * @hide
+ */
+ @TestApi
+ @Immutable
+ @SystemApi
+ public static final class OpEntry implements Parcelable {
+ private final @IntRange(from = 0, to = _NUM_OP - 1) int mOp;
+ private final @Mode int mMode;
+ private final @NonNull ArrayMap<String, OpFeatureEntry> mFeatures;
+
+ /**
+ * @hide
+ */
+ public OpEntry(@IntRange(from = 0, to = _NUM_OP - 1) int op, @Mode int mode,
+ @NonNull Pair<String, OpFeatureEntry.Builder>[] featureBuilders) {
+ mOp = Preconditions.checkArgumentInRange(op, 0, _NUM_OP - 1, "op");
+ mMode = Preconditions.checkArgumentInRange(mode, 0, MODE_FOREGROUND, "mode");
+
+ mFeatures = new ArrayMap<>(featureBuilders.length);
+ for (Pair<String, OpFeatureEntry.Builder> feature : featureBuilders) {
+ mFeatures.put(feature.first, feature.second.setParent(this).build());
+ }
+ }
+
+ /**
+ * @return The mapping from the feature ids to the feature state
+ */
+ public @NonNull Map<String, OpFeatureEntry> getFeatures() {
+ return mFeatures;
+ }
+
+ /**
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@code "
+ + "#getOpStr()}")
+ public int getOp() {
+ return mOp;
+ }
+
+ /**
+ * @return This entry's op string name, such as {@link #OPSTR_COARSE_LOCATION}.
+ */
+ public @NonNull String getOpStr() {
+ return sOpToString[mOp];
+ }
+
+ /**
+ * @return this entry's current mode, such as {@link #MODE_ALLOWED}.
+ */
+ public @Mode int getMode() {
+ return mMode;
+ }
+
+ /**
+ * @deprecated Use {@link OpEntry#getLastAccessTime(int)} instead
+ *
+ * @hide
+ */
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@code "
+ + "#getLastAccessTime(int)}")
+ public long getTime() {
+ return getLastAccessTime(OP_FLAGS_ALL);
+ }
+
+ private long getMaxOfFeatures(@NonNull ToLongFunction<OpFeatureEntry> timeGetter) {
+ long max = 0;
+
+ int numFeatures = mFeatures.size();
+ for (int i = 0; i < numFeatures; i++) {
+ max = Math.max(max, timeGetter.applyAsLong(mFeatures.valueAt(i)));
+ }
+
+ return max;
+ }
+
+ private long getSumOfFeatures(@NonNull ToLongFunction<OpFeatureEntry> getter) {
+ long sum = 0;
+
+ int numFeatures = mFeatures.size();
+ for (int i = 0; i < numFeatures; i++) {
+ sum += getter.applyAsLong(mFeatures.valueAt(i));
+ }
+
+ return sum;
+ }
+
+ /**
+ * Return the last wall clock time in milliseconds this op was accessed
+ * by the app for a given range of UID states.
+ *
+ * @param fromUidState The UID state for which to query. Could be one of
+ * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
+ * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
+ * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
+ * @param toUidState The UID state for which to query.
+ * @param flags The flags which are any combination of
+ * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
+ * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
+ * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
+ * for any flag.
+ *
+ * @return the last foreground access time in milliseconds since
+ * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
+ *
+ * @see #getLastAccessForegroundTime(int)
+ * @see #getLastAccessBackgroundTime(int)
+ * @see #getLastAccessTime(int)
+ */
+ public long getLastAccessTime(@OpFlags int flags) {
+ return getMaxOfFeatures(
+ (featureEntry -> featureEntry.getLastAccessTime(flags)));
+ }
+
+ /**
+ * Return the last wall clock time in milliseconds this op was accessed
+ * by the app while in the foreground.
+ *
+ * @param flags The flags which are any combination of
+ * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
+ * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
+ * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
+ * for any flag.
+ * @return the last foreground access time in milliseconds since
+ * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
+ *
+ * @see #getLastAccessBackgroundTime(int)
+ * @see #getLastAccessTime(int)
+ * @see #getLastAccessTime(int, int, int)
+ */
+ public long getLastAccessForegroundTime(@OpFlags int flags) {
+ return getMaxOfFeatures(
+ (featureEntry -> featureEntry.getLastAccessForegroundTime(flags)));
+ }
+
+ /**
+ * Return the last wall clock time in milliseconds this op was accessed
+ * by the app while in the background.
+ *
+ * @param flags The flags which are any combination of
+ * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
+ * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
+ * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
+ * for any flag.
+ * @return the last foreground access time in milliseconds since
+ * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
+ *
+ * @see #getLastAccessForegroundTime(int)
+ * @see #getLastAccessTime(int)
+ * @see #getLastAccessTime(int, int, int)
+ */
+ public long getLastAccessBackgroundTime(@OpFlags int flags) {
+ return getMaxOfFeatures(
+ (featureEntry -> featureEntry.getLastAccessBackgroundTime(flags)));
+ }
+
+ /**
+ * Return the last wall clock time in milliseconds this op was accessed
+ * by the app for a given range of UID states.
+ *
+ * @param fromUidState The UID state for which to query. Could be one of
+ * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
+ * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
+ * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
+ * @param toUidState The UID state for which to query.
+ * @param flags The flags which are any combination of
+ * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
+ * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
+ * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
+ * for any flag.
+ *
+ * @return the last foreground access time in milliseconds since
+ * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
+ *
+ * @see #getLastAccessForegroundTime(int)
+ * @see #getLastAccessBackgroundTime(int)
+ * @see #getLastAccessTime(int)
+ */
+ public long getLastAccessTime(@UidState int fromUidState, @UidState int toUidState,
+ @OpFlags int flags) {
+ return getMaxOfFeatures(
+ (featureEntry -> featureEntry.getLastAccessTime(fromUidState,
+ toUidState, flags)));
+ }
+
+ /**
+ * @deprecated Use {@link OpEntry#getLastRejectTime(int)} instead
+ *
+ * @hide
+ */
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@code "
+ + "#getLastRejectTime(int)}")
+ public long getRejectTime() {
+ return getLastRejectTime(OP_FLAGS_ALL);
+ }
+
+ /**
+ * Return the last wall clock time in milliseconds the app made an attempt
+ * to access this op but was rejected.
+ *
+ * @param flags The flags which are any combination of
+ * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
+ * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
+ * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
+ * for any flag.
+ * @return the last reject time in milliseconds since
+ * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
+ *
+ * @see #getLastRejectBackgroundTime(int)
+ * @see #getLastRejectForegroundTime(int)
+ * @see #getLastRejectTime(int, int, int)
+ */
+ public long getLastRejectTime(@OpFlags int flags) {
+ return getMaxOfFeatures(
+ (featureEntry -> featureEntry.getLastRejectTime(flags)));
+ }
+
+ /**
+ * Return the last wall clock time in milliseconds the app made an attempt
+ * to access this op while in the foreground but was rejected.
+ *
+ * @param flags The flags which are any combination of
+ * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
+ * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
+ * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
+ * for any flag.
+ * @return the last foreground reject time in milliseconds since
+ * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
+ *
+ * @see #getLastRejectBackgroundTime(int)
+ * @see #getLastRejectTime(int, int, int)
+ * @see #getLastRejectTime(int)
+ */
+ public long getLastRejectForegroundTime(@OpFlags int flags) {
+ return getMaxOfFeatures(
+ (featureEntry -> featureEntry.getLastRejectForegroundTime(flags)));
+ }
+
+ /**
+ * Return the last wall clock time in milliseconds the app made an attempt
+ * to access this op while in the background but was rejected.
+ *
+ * @param flags The flags which are any combination of
+ * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
+ * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
+ * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
+ * for any flag.
+ * @return the last background reject time in milliseconds since
+ * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
+ *
+ * @see #getLastRejectForegroundTime(int)
+ * @see #getLastRejectTime(int, int, int)
+ * @see #getLastRejectTime(int)
+ */
+ public long getLastRejectBackgroundTime(@OpFlags int flags) {
+ return getMaxOfFeatures(
+ (featureEntry -> featureEntry.getLastRejectBackgroundTime(flags)));
+ }
+
+ /**
+ * Return the last wall clock time state in milliseconds the app made an
+ * attempt to access this op for a given range of UID states.
+ *
+ * @param fromUidState The UID state from which to query. Could be one of
+ * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
+ * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
+ * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
+ * @param toUidState The UID state to which to query.
+ * @param flags The flags which are any combination of
+ * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
+ * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
+ * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
+ * for any flag.
+ * @return the last foreground access time in milliseconds since
+ * epoch start (January 1, 1970 00:00:00.000 GMT - Gregorian).
+ *
+ * @see #getLastRejectForegroundTime(int)
+ * @see #getLastRejectBackgroundTime(int)
+ * @see #getLastRejectTime(int)
+ */
+ public long getLastRejectTime(@UidState int fromUidState, @UidState int toUidState,
+ @OpFlags int flags) {
+ return getMaxOfFeatures(
+ (featureEntry -> featureEntry.getLastRejectTime(fromUidState,
+ toUidState, flags)));
+ }
+
+ /**
+ * @return Whether the operation is running.
+ */
+ public boolean isRunning() {
+ int numFeatures = mFeatures.size();
+ if (mFeatures.isEmpty()) {
+ return false;
+ }
+
+ for (int i = 0; i < numFeatures; i++) {
+ if (mFeatures.valueAt(i).mRunning) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @return The duration of the operation in milliseconds. The duration is in wall time.
+ */
+ public long getDuration() {
+ return getLastDuration(MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL);
+ }
+
+ /**
+ * Return the duration in milliseconds the app accessed this op while
+ * in the foreground. The duration is in wall time.
+ *
+ * @param flags The flags which are any combination of
+ * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
+ * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
+ * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
+ * for any flag.
+ * @return the foreground access duration in milliseconds.
+ *
+ * @see #getLastBackgroundDuration(int)
+ * @see #getLastDuration(int, int, int)
+ */
+ public long getLastForegroundDuration(@OpFlags int flags) {
+ return getSumOfFeatures((featureEntry) ->
+ featureEntry.getLastForegroundDuration(flags));
+ }
+
+ /**
+ * Return the duration in milliseconds the app accessed this op while
+ * in the background. The duration is in wall time.
+ *
+ * @param flags The flags which are any combination of
+ * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
+ * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
+ * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
+ * for any flag.
+ * @return the background access duration in milliseconds.
+ *
+ * @see #getLastForegroundDuration(int)
+ * @see #getLastDuration(int, int, int)
+ */
+ public long getLastBackgroundDuration(@OpFlags int flags) {
+ return getSumOfFeatures((featureEntry) ->
+ featureEntry.getLastBackgroundDuration(flags));
+ }
+
+ /**
+ * Return the duration in milliseconds the app accessed this op for
+ * a given range of UID states. The duration is in wall time.
+ *
+ * @param fromUidState The UID state for which to query. Could be one of
+ * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
+ * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
+ * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
+ * @param toUidState The UID state for which to query.
+ * @param flags The flags which are any combination of
+ * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
+ * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
+ * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
+ * for any flag.
+ * @return the access duration in milliseconds.
+ */
+ public long getLastDuration(@UidState int fromUidState, @UidState int toUidState,
+ @OpFlags int flags) {
+ return getSumOfFeatures((featureEntry) ->
+ featureEntry.getLastDuration(fromUidState, toUidState, flags));
+ }
+
+ /**
+ * Like {@link #findFirstNonNegativeForFlagsInStates(LongSparseLongArray, int, int, int)}
+ * but for all proxy uid in all features.
+ */
+ private long findFirstNonNegativeProxyUidInFeatureStates(@UidState int beginUidState,
+ @UidState int endUidState, @OpFlags int flags) {
+ int numFeatures = mFeatures.size();
+
+ if (numFeatures == 0) {
+ return -1;
+ }
+
+ while (flags != 0) {
+ final int flag = 1 << Integer.numberOfTrailingZeros(flags);
+ flags &= ~flag;
+ for (int uidState : UID_STATES) {
+ if (uidState < beginUidState || uidState > endUidState) {
+ continue;
+ }
+
+ final long key = makeKey(uidState, flag);
+
+ for (int i = 0; i < numFeatures; i++) {
+ OpFeatureEntry featureEntry = mFeatures.valueAt(i);
+
+ if (featureEntry.mProxyUids == null) {
+ continue;
+ }
+
+ final long proxyUid = featureEntry.mProxyUids.get(key);
+ if (proxyUid >= 0) {
+ return proxyUid;
+ }
+ }
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Like {@link #findFirstNonNullForFlagsInStates(LongSparseArray, int, int, int)} but
+ * for all proxyPackageNames in all features.
+ */
+ private @Nullable String findFirstNonNullProxyPackageNameInFeatureStates(
+ @OpFlags int flags, @UidState int beginUidState, @UidState int endUidState) {
+ int numFeatures = mFeatures.size();
+
+ if (numFeatures == 0) {
+ return null;
+ }
+
+ while (flags != 0) {
+ final int flag = 1 << Integer.numberOfTrailingZeros(flags);
+ flags &= ~flag;
+ for (int uidState : UID_STATES) {
+ if (uidState < beginUidState || uidState > endUidState) {
+ continue;
+ }
+ final long key = makeKey(uidState, flag);
+
+ for (int i = 0; i < numFeatures; i++) {
+ OpFeatureEntry featureEntry = mFeatures.valueAt(i);
+
+ if (featureEntry.mProxyPackageNames == null) {
+ continue;
+ }
+
+ final String proxyName = featureEntry.mProxyPackageNames.get(key);
+ if (proxyName != null) {
+ return proxyName;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @deprecated Use {@link #getProxyUid(int, int)} instead
+ */
+ @Deprecated
+ public int getProxyUid() {
+ return (int) findFirstNonNegativeProxyUidInFeatureStates(MAX_PRIORITY_UID_STATE,
+ MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL);
+ }
+
+ /**
+ * Gets the UID of the app that performed the op on behalf of this app and
+ * as a result blamed the op on this app or {@link Process#INVALID_UID} if
+ * there is no proxy.
+ *
+ * @param uidState The UID state for which to query. Could be one of
+ * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
+ * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
+ * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
+ * @param flags The flags which are any combination of
+ * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
+ * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
+ * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
+ * for any flag.
+ *
+ * @return The proxy UID.
+ */
+ public int getProxyUid(@UidState int uidState, @OpFlags int flags) {
+ return (int) findFirstNonNegativeProxyUidInFeatureStates(uidState, uidState, flags);
+ }
+
+ /**
+ * @deprecated Use {@link #getProxyPackageName(int, int)} instead
+ */
+ @Deprecated
+ public @Nullable String getProxyPackageName() {
+ return findFirstNonNullProxyPackageNameInFeatureStates(MAX_PRIORITY_UID_STATE,
+ MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL);
+ }
+
+ /**
+ * Gets the package name of the app that performed the op on behalf of this
+ * app and as a result blamed the op on this app for a UID state or
+ * {@code null} if there is no proxy.
+ *
+ * @param uidState The UID state for which to query. Could be one of
+ * {@link #UID_STATE_PERSISTENT}, {@link #UID_STATE_TOP},
+ * {@link #UID_STATE_FOREGROUND_SERVICE}, {@link #UID_STATE_FOREGROUND},
+ * {@link #UID_STATE_BACKGROUND}, {@link #UID_STATE_CACHED}.
+ * @param flags The flags which are any combination of
+ * {@link #OP_FLAG_SELF}, {@link #OP_FLAG_TRUSTED_PROXY},
+ * {@link #OP_FLAG_UNTRUSTED_PROXY}, {@link #OP_FLAG_TRUSTED_PROXIED},
+ * {@link #OP_FLAG_UNTRUSTED_PROXIED}. You can use {@link #OP_FLAGS_ALL}
+ * for any flag.
+ * @return The proxy package name.
+ */
+ public @Nullable String getProxyPackageName(@UidState int uidState, @OpFlags int flags) {
+ return findFirstNonNullProxyPackageNameInFeatureStates(uidState, uidState, flags);
+ }
+
+ /**
+ * Create OpEntry from parcel.
+ *
+ * @hide
+ */
+ public static OpEntry createFromParcel(@NonNull Parcel source) {
+ int op = source.readInt();
+ int mode = source.readInt();
+
+ int numFeatures = source.readInt();
+ Pair<String, OpFeatureEntry.Builder>[] features = new Pair[numFeatures];
+ for (int i = 0; i < numFeatures; i++) {
+ features[i] = new Pair<>(source.readString(),
+ OpFeatureEntry.createFromParcel(source));
+ }
+
+ return new OpEntry(op, mode, features);
+ }
+
@Override
public int describeContents() {
return 0;
@@ -2738,31 +3372,23 @@ public class AppOpsManager {
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mOp);
dest.writeInt(mMode);
- dest.writeBoolean(mRunning);
- writeLongSparseLongArrayToParcel(mAccessTimes, dest);
- writeLongSparseLongArrayToParcel(mRejectTimes, dest);
- writeLongSparseLongArrayToParcel(mDurations, dest);
- writeLongSparseLongArrayToParcel(mProxyUids, dest);
- writeLongSparseStringArrayToParcel(mProxyPackageNames, dest);
- }
- OpEntry(Parcel source) {
- mOp = source.readInt();
- mMode = source.readInt();
- mRunning = source.readBoolean();
- mAccessTimes = readLongSparseLongArrayFromParcel(source);
- mRejectTimes = readLongSparseLongArrayFromParcel(source);
- mDurations = readLongSparseLongArrayFromParcel(source);
- mProxyUids = readLongSparseLongArrayFromParcel(source);
- mProxyPackageNames = readLongSparseStringArrayFromParcel(source);
+ int numFeatures = mFeatures.size();
+ dest.writeInt(numFeatures);
+ for (int i = 0; i < numFeatures; i++) {
+ dest.writeString(mFeatures.keyAt(i));
+ mFeatures.valueAt(i).writeToParcel(dest, flags);
+ }
}
- public static final @android.annotation.NonNull Creator<OpEntry> CREATOR = new Creator<OpEntry>() {
- @Override public OpEntry createFromParcel(Parcel source) {
- return new OpEntry(source);
+ public static final @NonNull Creator<OpEntry> CREATOR = new Creator<OpEntry>() {
+ @Override
+ public @NonNull OpEntry createFromParcel(@NonNull Parcel parcel) {
+ return OpEntry.createFromParcel(parcel);
}
- @Override public OpEntry[] newArray(int size) {
+ @Override
+ public @NonNull OpEntry[] newArray(int size) {
return new OpEntry[size];
}
};
@@ -4415,7 +5041,7 @@ public class AppOpsManager {
* state due to UID policy or because it's controlled by a different master op.
*
* Use {@link #unsafeCheckOp(String, int, String)}} or
- * {@link #noteOp(String, int, String, String)} if the effective mode is needed.
+ * {@link #noteOp(String, int, String, String, String)} if the effective mode is needed.
*
* @param ops The set of operations you are interested in, or null if you want all of them.
* @hide
@@ -4439,7 +5065,7 @@ public class AppOpsManager {
* state due to UID policy or because it's controlled by a different master op.
*
* Use {@link #unsafeCheckOp(String, int, String)}} or
- * {@link #noteOp(String, int, String, String)} if the effective mode is needed.
+ * {@link #noteOp(String, int, String, String, String)} if the effective mode is needed.
*
* @param ops The set of operations you are interested in, or null if you want all of them.
* @hide
@@ -4461,7 +5087,7 @@ public class AppOpsManager {
* state due to UID policy or because it's controlled by a different master op.
*
* Use {@link #unsafeCheckOp(String, int, String)}} or
- * {@link #noteOp(String, int, String, String)} if the effective mode is needed.
+ * {@link #noteOp(String, int, String, String, String)} if the effective mode is needed.
*
* @param uid The uid of the application of interest.
* @param packageName The name of the application of interest.
@@ -4494,7 +5120,7 @@ public class AppOpsManager {
* state due to UID policy or because it's controlled by a different master op.
*
* Use {@link #unsafeCheckOp(String, int, String)}} or
- * {@link #noteOp(String, int, String, String)} if the effective mode is needed.
+ * {@link #noteOp(String, int, String, String, String)} if the effective mode is needed.
*
* @param uid The uid of the application of interest.
* @param packageName The name of the application of interest.
@@ -4884,8 +5510,8 @@ public class AppOpsManager {
*
* @see #isOperationActive
* @see #stopWatchingActive
- * @see #startOp(int, int, String, boolean, String)
- * @see #finishOp(int, int, String)
+ * @see #startOp(int, int, String, boolean, String, String)
+ * @see #finishOp(int, int, String, String)
*/
// TODO: Uncomment below annotation once b/73559440 is fixed
// @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
@@ -4935,8 +5561,8 @@ public class AppOpsManager {
*
* @see #isOperationActive
* @see #startWatchingActive
- * @see #startOp(int, int, String, boolean, String)
- * @see #finishOp(int, int, String)
+ * @see #startOp(int, int, String, boolean, String, String)
+ * @see #finishOp(int, int, String, String)
*/
public void stopWatchingActive(@NonNull OnOpActiveChangedListener callback) {
synchronized (mActiveWatchers) {
@@ -4966,7 +5592,7 @@ public class AppOpsManager {
*
* @see #startWatchingActive(int[], OnOpActiveChangedListener)
* @see #stopWatchingNoted(OnOpNotedListener)
- * @see #noteOp(String, int, String, String)
+ * @see #noteOp(String, int, String, String, String)
*
* @hide
*/
@@ -4998,7 +5624,7 @@ public class AppOpsManager {
* Unregistering a non-registered callback has no effect.
*
* @see #startWatchingNoted(int[], OnOpNotedListener)
- * @see #noteOp(String, int, String, String)
+ * @see #noteOp(String, int, String, String, String)
*
* @hide
*/
@@ -5034,15 +5660,15 @@ public class AppOpsManager {
/**
* Do a quick check for whether an application might be able to perform an operation.
* This is <em>not</em> a security check; you must use {@link #noteOp(String, int, String,
- * String)} or {@link #startOp(String, int, String, String)} for your actual security checks,
- * which also ensure that the given uid and package name are consistent. This function can just
- * be used for a quick check to see if an operation has been disabled for the application,
- * as an early reject of some work. This does not modify the time stamp or other data
- * about the operation.
+ * String, String)} or {@link #startOp(String, int, String, String, String)} for your actual
+ * security checks, which also ensure that the given uid and package name are consistent. This
+ * function can just be used for a quick check to see if an operation has been disabled for the
+ * application, as an early reject of some work. This does not modify the time stamp or other
+ * data about the operation.
*
* <p>Important things this will not do (which you need to ultimate use
- * {@link #noteOp(String, int, String, String)} or
- * {@link #startOp(String, int, String, String)} to cover):</p>
+ * {@link #noteOp(String, int, String, String, String)} or
+ * {@link #startOp(String, int, String, String, String)} to cover):</p>
* <ul>
* <li>Verifying the uid and package are consistent, so callers can't spoof
* their identity.</li>
@@ -5113,35 +5739,37 @@ public class AppOpsManager {
}
/**
- * @deprecated Use {@link #noteOp(String, int, String, String)} instead
+ * @deprecated Use {@link #noteOp(String, int, String, String, String)} instead
*/
@Deprecated
public int noteOp(@NonNull String op, int uid, @NonNull String packageName) {
- return noteOp(op, uid, packageName, null);
+ return noteOp(op, uid, packageName, null, null);
}
/**
- * @deprecated Use {@link #noteOp(String, int, String, String)} instead
+ * @deprecated Use {@link #noteOp(String, int, String, String, String)} instead
*
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
- + "#noteOp(java.lang.String, int, java.lang.String, java.lang.String)} instead")
+ + "#noteOp(java.lang.String, int, java.lang.String, java.lang.String, "
+ + "java.lang.String)} instead")
@Deprecated
public int noteOp(int op) {
- return noteOp(op, Process.myUid(), mContext.getOpPackageName(), null);
+ return noteOp(op, Process.myUid(), mContext.getOpPackageName(), null, null);
}
/**
- * @deprecated Use {@link #noteOp(String, int, String, String)} instead
+ * @deprecated Use {@link #noteOp(String, int, String, String, String)} instead
*
* @hide
*/
@Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
- + "#noteOp(java.lang.String, int, java.lang.String, java.lang.String)} instead")
+ + "#noteOp(java.lang.String, int, java.lang.String, java.lang.String, "
+ + "java.lang.String)} instead")
public int noteOp(int op, int uid, @Nullable String packageName) {
- return noteOp(op, uid, packageName, null);
+ return noteOp(op, uid, packageName, null, null);
}
/**
@@ -5154,6 +5782,7 @@ public class AppOpsManager {
* @param op The operation to note. One of the OPSTR_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
+ * @param featureId The feature in the app or {@code null} for default feature
* @param message A message describing the reason the op was noted
*
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
@@ -5163,8 +5792,8 @@ public class AppOpsManager {
* @throws SecurityException If the app has been configured to crash on this op.
*/
public int noteOp(@NonNull String op, int uid, @Nullable String packageName,
- @Nullable String message) {
- return noteOp(strOpToOp(op), uid, packageName, message);
+ @Nullable String featureId, @Nullable String message) {
+ return noteOp(strOpToOp(op), uid, packageName, featureId, message);
}
/**
@@ -5177,6 +5806,7 @@ public class AppOpsManager {
* @param op The operation to note. One of the OP_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
+ * @param featureId The feature in the app or {@code null} for default feature
* @param message A message describing the reason the op was noted
*
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
@@ -5187,8 +5817,9 @@ public class AppOpsManager {
*
* @hide
*/
- public int noteOp(int op, int uid, @Nullable String packageName, @Nullable String message) {
- final int mode = noteOpNoThrow(op, uid, packageName, message);
+ public int noteOp(int op, int uid, @Nullable String packageName, @Nullable String featureId,
+ @Nullable String message) {
+ final int mode = noteOpNoThrow(op, uid, packageName, featureId, message);
if (mode == MODE_ERRORED) {
throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
}
@@ -5196,27 +5827,28 @@ public class AppOpsManager {
}
/**
- * @deprecated Use {@link #noteOpNoThrow(String, int, String, String)} instead
+ * @deprecated Use {@link #noteOpNoThrow(String, int, String, String, String)} instead
*/
@Deprecated
public int noteOpNoThrow(@NonNull String op, int uid, @NonNull String packageName) {
- return noteOpNoThrow(op, uid, packageName, null);
+ return noteOpNoThrow(op, uid, packageName, null, null);
}
/**
- * @deprecated Use {@link #noteOpNoThrow(int, int, String, String)} instead
+ * @deprecated Use {@link #noteOpNoThrow(int, int, String, String, String)} instead
*
* @hide
*/
@Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
- + "#noteOpNoThrow(java.lang.String, int, java.lang.String, java.lang.String)} instead")
+ + "#noteOpNoThrow(java.lang.String, int, java.lang.String, java.lang.String, "
+ + "java.lang.String)} instead")
public int noteOpNoThrow(int op, int uid, String packageName) {
- return noteOpNoThrow(op, uid, packageName, null);
+ return noteOpNoThrow(op, uid, packageName, null, null);
}
/**
- * Like {@link #noteOp(String, int, String, String)} but instead of throwing a
+ * Like {@link #noteOp(String, int, String, String, String)} but instead of throwing a
* {@link SecurityException} it returns {@link #MODE_ERRORED}.
*
* @param op The operation to note. One of the OPSTR_* constants.
@@ -5229,17 +5861,18 @@ public class AppOpsManager {
* causing the app to crash).
*/
public int noteOpNoThrow(@NonNull String op, int uid, @NonNull String packageName,
- @Nullable String message) {
- return noteOpNoThrow(strOpToOp(op), uid, packageName, message);
+ @Nullable String feature, @Nullable String message) {
+ return noteOpNoThrow(strOpToOp(op), uid, packageName, feature, message);
}
/**
- * Like {@link #noteOp(String, int, String, String)} but instead of throwing a
+ * Like {@link #noteOp(String, int, String, String, String)} but instead of throwing a
* {@link SecurityException} it returns {@link #MODE_ERRORED}.
*
* @param op The operation to note. One of the OP_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
+ * @param featureId The feature in the app or {@code null} for default feature
* @param message A message describing the reason the op was noted
*
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
@@ -5249,11 +5882,11 @@ public class AppOpsManager {
* @hide
*/
public int noteOpNoThrow(int op, int uid, @Nullable String packageName,
- @Nullable String message) {
+ @Nullable String featureId, @Nullable String message) {
try {
- int mode = mService.noteOperation(op, uid, packageName);
+ int mode = mService.noteOperation(op, uid, packageName, featureId);
if (mode == MODE_ALLOWED) {
- markAppOpNoted(uid, packageName, op, message);
+ markAppOpNoted(uid, packageName, op, featureId, message);
}
return mode;
@@ -5263,23 +5896,24 @@ public class AppOpsManager {
}
/**
- * @deprecated Use {@link #noteProxyOp(String, String, int, String)} instead
+ * @deprecated Use {@link #noteProxyOp(String, String, int, String, String)} instead
*/
@Deprecated
public int noteProxyOp(@NonNull String op, @NonNull String proxiedPackageName) {
- return noteProxyOp(op, proxiedPackageName, Binder.getCallingUid(), null);
+ return noteProxyOp(op, proxiedPackageName, Binder.getCallingUid(), null, null);
}
/**
- * @deprecated Use {@link #noteProxyOp(String, String, int, String)} instead
+ * @deprecated Use {@link #noteProxyOp(String, String, int, String, String)} instead
*
* @hide
*/
@Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
- + "#noteProxyOp(java.lang.String, java.lang.String, int, java.lang.String)} instead")
+ + "#noteProxyOp(java.lang.String, java.lang.String, int, java.lang.String, "
+ + "java.lang.String)} instead")
public int noteProxyOp(int op, @Nullable String proxiedPackageName) {
- return noteProxyOp(op, proxiedPackageName, Binder.getCallingUid(), null);
+ return noteProxyOp(op, proxiedPackageName, Binder.getCallingUid(), null, null);
}
/**
@@ -5291,6 +5925,8 @@ public class AppOpsManager {
* @param op The operation to note. One of the OP_* constants.
* @param proxiedPackageName The name of the application calling into the proxy application.
* @param proxiedUid The uid of the proxied application
+ * @param proxiedFeatureId The feature in the proxied app or {@code null} for default
+ * feature
* @param message A message describing the reason the op was noted
*
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
@@ -5302,8 +5938,9 @@ public class AppOpsManager {
* @hide
*/
public int noteProxyOp(int op, @Nullable String proxiedPackageName, int proxiedUid,
- @Nullable String message) {
- int mode = noteProxyOpNoThrow(op, proxiedPackageName, proxiedUid, message);
+ @Nullable String proxiedFeatureId, @Nullable String message) {
+ int mode = noteProxyOpNoThrow(op, proxiedPackageName, proxiedUid, proxiedFeatureId,
+ message);
if (mode == MODE_ERRORED) {
throw new SecurityException("Proxy package " + mContext.getOpPackageName()
+ " from uid " + Process.myUid() + " or calling package " + proxiedPackageName
@@ -5321,6 +5958,8 @@ public class AppOpsManager {
* @param op The operation to note. One of the OPSTR_* constants.
* @param proxiedPackageName The name of the application calling into the proxy application.
* @param proxiedUid The uid of the proxied application
+ * @param proxiedFeatureId The feature in the proxied app or {@code null} for default
+ * feature
* @param message A message describing the reason the op was noted
*
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
@@ -5330,29 +5969,30 @@ public class AppOpsManager {
* op.
*/
public int noteProxyOp(@NonNull String op, @Nullable String proxiedPackageName, int proxiedUid,
- @Nullable String message) {
- return noteProxyOp(strOpToOp(op), proxiedPackageName, proxiedUid, message);
+ @Nullable String proxiedFeatureId, @Nullable String message) {
+ return noteProxyOp(strOpToOp(op), proxiedPackageName, proxiedUid, proxiedFeatureId,
+ message);
}
/**
- * @deprecated Use {@link #noteProxyOpNoThrow(String, String, int, String)} instead
+ * @deprecated Use {@link #noteProxyOpNoThrow(String, String, int, String, String)} instead
*/
@Deprecated
public int noteProxyOpNoThrow(@NonNull String op, @NonNull String proxiedPackageName) {
- return noteProxyOpNoThrow(op, proxiedPackageName, Binder.getCallingUid(), null);
+ return noteProxyOpNoThrow(op, proxiedPackageName, Binder.getCallingUid(), null, null);
}
/**
- * @deprecated Use {@link #noteProxyOpNoThrow(String, String, int, String)} instead
+ * @deprecated Use {@link #noteProxyOpNoThrow(String, String, int, String, String)} instead
*/
@Deprecated
public int noteProxyOpNoThrow(@NonNull String op, @Nullable String proxiedPackageName,
int proxiedUid) {
- return noteProxyOpNoThrow(op, proxiedPackageName, proxiedUid, null);
+ return noteProxyOpNoThrow(op, proxiedPackageName, proxiedUid, null, null);
}
/**
- * Like {@link #noteProxyOp(String, String, int, String)} but instead
+ * Like {@link #noteProxyOp(String, String, int, String, String)} but instead
* of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}.
*
* <p>This API requires package with the {@code proxiedPackageName} to belong to
@@ -5361,37 +6001,43 @@ public class AppOpsManager {
* @param op The op to note
* @param proxiedPackageName The package to note the op for
* @param proxiedUid The uid the package belongs to
+ * @param proxiedFeatureId The feature in the proxied app or {@code null} for default
+ * feature
* @param message A message describing the reason the op was noted
*/
public int noteProxyOpNoThrow(@NonNull String op, @Nullable String proxiedPackageName,
- int proxiedUid, @Nullable String message) {
- return noteProxyOpNoThrow(strOpToOp(op), proxiedPackageName, proxiedUid, message);
+ int proxiedUid, @Nullable String proxiedFeatureId, @Nullable String message) {
+ return noteProxyOpNoThrow(strOpToOp(op), proxiedPackageName, proxiedUid,
+ proxiedFeatureId, message);
}
/**
- * Like {@link #noteProxyOp(int, String, int, String)} but instead
+ * Like {@link #noteProxyOp(int, String, int, String, String)} but instead
* of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}.
*
* @param op The op to note
* @param proxiedPackageName The package to note the op for or {@code null} if the op should be
* noted for the "android" package
* @param proxiedUid The uid the package belongs to
+ * @param proxiedFeatureId The feature in the proxied app or {@code null} for default
+ * feature
* @param message A message describing the reason the op was noted
*
* @hide
*/
public int noteProxyOpNoThrow(int op, @Nullable String proxiedPackageName, int proxiedUid,
- @Nullable String message) {
+ @Nullable String proxiedFeatureId, @Nullable String message) {
int myUid = Process.myUid();
try {
- int mode = mService.noteProxyOperation(op, myUid, mContext.getOpPackageName(),
- proxiedUid, proxiedPackageName);
+ int mode = mService.noteProxyOperation(op, proxiedUid, proxiedPackageName,
+ proxiedFeatureId, myUid, mContext.getOpPackageName(),
+ mContext.getFeatureId());
if (mode == MODE_ALLOWED
// Only collect app-ops when the proxy is trusted
&& mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1, myUid)
== PackageManager.PERMISSION_GRANTED) {
- markAppOpNoted(proxiedUid, proxiedPackageName, op, message);
+ markAppOpNoted(proxiedUid, proxiedPackageName, op, proxiedFeatureId, message);
}
return mode;
@@ -5403,15 +6049,15 @@ public class AppOpsManager {
/**
* Do a quick check for whether an application might be able to perform an operation.
* This is <em>not</em> a security check; you must use {@link #noteOp(String, int, String,
- * String)} or {@link #startOp(int, int, String, boolean, String)} for your actual security
- * checks, which also ensure that the given uid and package name are consistent. This function
- * can just be used for a quick check to see if an operation has been disabled for the
- * application, as an early reject of some work. This does not modify the time stamp or other
- * data about the operation.
+ * String, String)} or {@link #startOp(int, int, String, boolean, String, String)} for your
+ * actual security checks, which also ensure that the given uid and package name are consistent.
+ * This function can just be used for a quick check to see if an operation has been disabled for
+ * the application, as an early reject of some work. This does not modify the time stamp or
+ * other data about the operation.
*
* <p>Important things this will not do (which you need to ultimate use
- * {@link #noteOp(String, int, String, String)} or
- * {@link #startOp(int, int, String, boolean, String)} to cover):</p>
+ * {@link #noteOp(String, int, String, String, String)} or
+ * {@link #startOp(int, int, String, boolean, String, String)} to cover):</p>
* <ul>
* <li>Verifying the uid and package are consistent, so callers can't spoof
* their identity.</li>
@@ -5521,41 +6167,41 @@ public class AppOpsManager {
/**
- * @deprecated use {@link #startOp(String, int, String, String)} instead
+ * @deprecated use {@link #startOp(String, int, String, String, String)} instead
*/
@Deprecated
public int startOp(@NonNull String op, int uid, @NonNull String packageName) {
- return startOp(op, uid, packageName, null);
+ return startOp(op, uid, packageName, null, null);
}
/**
- * @deprecated Use {@link #startOp(int, int, String, boolean, String)} instead
+ * @deprecated Use {@link #startOp(int, int, String, boolean, String, String)} instead
*
* @hide
*/
@Deprecated
public int startOp(int op) {
- return startOp(op, Process.myUid(), mContext.getOpPackageName(), false, null);
+ return startOp(op, Process.myUid(), mContext.getOpPackageName(), false, null, null);
}
/**
- * @deprecated Use {@link #startOp(int, int, String, boolean, String)} instead
+ * @deprecated Use {@link #startOp(int, int, String, boolean, String, String)} instead
*
* @hide
*/
@Deprecated
public int startOp(int op, int uid, String packageName) {
- return startOp(op, uid, packageName, false, null);
+ return startOp(op, uid, packageName, false, null, null);
}
/**
- * @deprecated Use {@link #startOp(int, int, String, boolean, String)} instead
+ * @deprecated Use {@link #startOp(int, int, String, boolean, String, String)} instead
*
* @hide
*/
@Deprecated
public int startOp(int op, int uid, String packageName, boolean startIfModeDefault) {
- return startOp(op, uid, packageName, startIfModeDefault, null);
+ return startOp(op, uid, packageName, startIfModeDefault, null, null);
}
/**
@@ -5564,6 +6210,7 @@ public class AppOpsManager {
* @param op The operation to start. One of the OPSTR_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
+ * @param featureId The feature in the app or {@code null} for default feature
* @param message Description why op was started
*
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
@@ -5574,8 +6221,8 @@ public class AppOpsManager {
* the package is not in the passed in UID.
*/
public int startOp(@NonNull String op, int uid, @Nullable String packageName,
- @Nullable String message) {
- return startOp(strOpToOp(op), uid, packageName, false, message);
+ @NonNull String featureId, @Nullable String message) {
+ return startOp(strOpToOp(op), uid, packageName, false, featureId, message);
}
/**
@@ -5584,6 +6231,7 @@ public class AppOpsManager {
* @param op The operation to start. One of the OP_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
+ * @param featureId The feature in the app or {@code null} for default feature
* @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}.
* @param message Description why op was started
*
@@ -5597,8 +6245,9 @@ public class AppOpsManager {
* @hide
*/
public int startOp(int op, int uid, @Nullable String packageName, boolean startIfModeDefault,
- @Nullable String message) {
- final int mode = startOpNoThrow(op, uid, packageName, startIfModeDefault, message);
+ @NonNull String featureId, @Nullable String message) {
+ final int mode = startOpNoThrow(op, uid, packageName, startIfModeDefault, featureId,
+ message);
if (mode == MODE_ERRORED) {
throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
}
@@ -5606,40 +6255,41 @@ public class AppOpsManager {
}
/**
- * @deprecated use {@link #startOpNoThrow(String, int, String, String)} instead
+ * @deprecated use {@link #startOpNoThrow(String, int, String, String, String)} instead
*/
@Deprecated
public int startOpNoThrow(@NonNull String op, int uid, @NonNull String packageName) {
- return startOpNoThrow(op, uid, packageName, null);
+ return startOpNoThrow(op, uid, packageName, null, null);
}
/**
- * @deprecated Use {@link #startOpNoThrow(int, int, String, boolean, String} instead
+ * @deprecated Use {@link #startOpNoThrow(int, int, String, boolean, String, String} instead
*
* @hide
*/
@Deprecated
public int startOpNoThrow(int op, int uid, String packageName) {
- return startOpNoThrow(op, uid, packageName, false, null);
+ return startOpNoThrow(op, uid, packageName, false, null, null);
}
/**
- * @deprecated Use {@link #startOpNoThrow(int, int, String, boolean, String} instead
+ * @deprecated Use {@link #startOpNoThrow(int, int, String, boolean, String, String} instead
*
* @hide
*/
@Deprecated
public int startOpNoThrow(int op, int uid, String packageName, boolean startIfModeDefault) {
- return startOpNoThrow(op, uid, packageName, startIfModeDefault, null);
+ return startOpNoThrow(op, uid, packageName, startIfModeDefault, null, null);
}
/**
- * Like {@link #startOp(String, int, String, String)} but instead of throwing a
+ * Like {@link #startOp(String, int, String, String, String)} but instead of throwing a
* {@link SecurityException} it returns {@link #MODE_ERRORED}.
*
* @param op The operation to start. One of the OP_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
+ * @param featureId The feature in the app or {@code null} for default feature
* @param message Description why op was started
*
* @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
@@ -5647,17 +6297,18 @@ public class AppOpsManager {
* causing the app to crash).
*/
public int startOpNoThrow(@NonNull String op, int uid, @NonNull String packageName,
- @Nullable String message) {
- return startOpNoThrow(strOpToOp(op), uid, packageName, false, message);
+ @NonNull String featureId, @Nullable String message) {
+ return startOpNoThrow(strOpToOp(op), uid, packageName, false, featureId, message);
}
/**
- * Like {@link #startOp(int, int, String, boolean, String)} but instead of throwing a
+ * Like {@link #startOp(int, int, String, boolean, String, String)} but instead of throwing a
* {@link SecurityException} it returns {@link #MODE_ERRORED}.
*
* @param op The operation to start. One of the OP_* constants.
* @param uid The user id of the application attempting to perform the operation.
* @param packageName The name of the application attempting to perform the operation.
+ * @param featureId The feature in the app or {@code null} for default feature
* @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}.
* @param message Description why op was started
*
@@ -5668,12 +6319,12 @@ public class AppOpsManager {
* @hide
*/
public int startOpNoThrow(int op, int uid, @NonNull String packageName,
- boolean startIfModeDefault, @Nullable String message) {
+ boolean startIfModeDefault, @Nullable String featureId, @Nullable String message) {
try {
int mode = mService.startOperation(getToken(mService), op, uid, packageName,
- startIfModeDefault);
+ featureId, startIfModeDefault);
if (mode == MODE_ALLOWED) {
- markAppOpNoted(uid, packageName, op, message);
+ markAppOpNoted(uid, packageName, op, featureId, message);
}
return mode;
@@ -5683,36 +6334,54 @@ public class AppOpsManager {
}
/**
- * @deprecated Use {@link #finishOp(String, int, String)} instead
+ * @deprecated Use {@link #finishOp(String, int, String, String)} instead
*
* @hide
*/
@Deprecated
public void finishOp(int op) {
- finishOp(op, Process.myUid(), mContext.getOpPackageName());
+ finishOp(op, Process.myUid(), mContext.getOpPackageName(), null);
}
/**
- * Report that an application is no longer performing an operation that had previously
- * been started with {@link #startOp(String, int, String, String)}. There is no validation of
- * input or result; the parameters supplied here must be the exact same ones previously passed
- * in when starting the operation.
+ * @deprecated Use {@link #finishOp(String, int, String, String)} instead
*/
public void finishOp(@NonNull String op, int uid, @NonNull String packageName) {
- finishOp(strOpToOp(op), uid, packageName);
+ finishOp(strOpToOp(op), uid, packageName, null);
}
/**
* Report that an application is no longer performing an operation that had previously
- * been started with {@link #startOp(int, int, String, boolean, String)}. There is no
+ * been started with {@link #startOp(String, int, String, String, String)}. There is no
* validation of input or result; the parameters supplied here must be the exact same ones
* previously passed in when starting the operation.
+ */
+ public void finishOp(@NonNull String op, int uid, @NonNull String packageName,
+ @Nullable String featureId) {
+ finishOp(strOpToOp(op), uid, packageName, featureId);
+ }
+
+ /**
+ * @deprecated Use {@link #finishOp(int, int, String, String)} instead
*
* @hide
*/
public void finishOp(int op, int uid, @NonNull String packageName) {
+ finishOp(op, uid, packageName, null);
+ }
+
+ /**
+ * Report that an application is no longer performing an operation that had previously
+ * been started with {@link #startOp(int, int, String, boolean, String, String)}. There is no
+ * validation of input or result; the parameters supplied here must be the exact same ones
+ * previously passed in when starting the operation.
+ *
+ * @hide
+ */
+ public void finishOp(int op, int uid, @NonNull String packageName,
+ @Nullable String featureId) {
try {
- mService.finishOperation(getToken(mService), op, uid, packageName);
+ mService.finishOperation(getToken(mService), op, uid, packageName, featureId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -5724,8 +6393,8 @@ public class AppOpsManager {
* If you don't hold the {@code android.Manifest.permission#WATCH_APPOPS}
* permission you can query only for your UID.
*
- * @see #finishOp(String, int, String)
- * @see #startOp(String, int, String, String)
+ * @see #finishOp(String, int, String, String)
+ * @see #startOp(String, int, String, String, String)
*/
public boolean isOpActive(@NonNull String op, int uid, @NonNull String packageName) {
return isOperationActive(strOpToOp(op), uid, packageName);
@@ -5753,9 +6422,10 @@ public class AppOpsManager {
*/
public static class PausedNotedAppOpsCollection {
final int mUid;
- final @Nullable long[] mCollectedNotedAppOps;
+ final @Nullable ArrayMap<String, long[]> mCollectedNotedAppOps;
- PausedNotedAppOpsCollection(int uid, @Nullable long[] collectedNotedAppOps) {
+ PausedNotedAppOpsCollection(int uid, @Nullable ArrayMap<String,
+ long[]> collectedNotedAppOps) {
mUid = uid;
mCollectedNotedAppOps = collectedNotedAppOps;
}
@@ -5773,7 +6443,8 @@ public class AppOpsManager {
public static @Nullable PausedNotedAppOpsCollection pauseNotedAppOpsCollection() {
Integer previousUid = sBinderThreadCallingUid.get();
if (previousUid != null) {
- long[] previousCollectedNotedAppOps = sAppOpsNotedInThisBinderTransaction.get();
+ ArrayMap<String, long[]> previousCollectedNotedAppOps =
+ sAppOpsNotedInThisBinderTransaction.get();
sBinderThreadCallingUid.remove();
sAppOpsNotedInThisBinderTransaction.remove();
@@ -5819,8 +6490,12 @@ public class AppOpsManager {
/**
* Mark an app-op as noted
*/
- private void markAppOpNoted(int uid, @NonNull String packageName, int code,
- @Nullable String message) {
+ private void markAppOpNoted(int uid, @Nullable String packageName, int code,
+ @Nullable String featureId, @Nullable String message) {
+ if (packageName == null) {
+ packageName = "android";
+ }
+
// check it the appops needs to be collected and cache result
if (sAppOpsToNote[code] == SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED) {
boolean shouldCollectNotes;
@@ -5847,7 +6522,7 @@ public class AppOpsManager {
if (sNotedAppOpsCollector != null && uid == Process.myUid() && packageName.equals(
ActivityThread.currentOpPackageName())) {
// This app is noting an app-op for itself. Deliver immediately.
- sNotedAppOpsCollector.onSelfNoted(new SyncNotedAppOp(code));
+ sNotedAppOpsCollector.onSelfNoted(new SyncNotedAppOp(code, featureId));
return;
}
@@ -5856,18 +6531,24 @@ public class AppOpsManager {
if (binderUid != null && binderUid == uid) {
// If this is inside of a two-way binder call: Delivered to caller via
// {@link #prefixParcelWithAppOpsIfNeeded}
- long[] appOpsNotedInThisBinderTransaction;
+ // We are inside of a two-way binder call. Delivered to caller via
+ // {@link #prefixParcelWithAppOpsIfNeeded}
+ ArrayMap<String, long[]> appOpsNoted = sAppOpsNotedInThisBinderTransaction.get();
+ if (appOpsNoted == null) {
+ appOpsNoted = new ArrayMap<>(1);
+ sAppOpsNotedInThisBinderTransaction.set(appOpsNoted);
+ }
- appOpsNotedInThisBinderTransaction = sAppOpsNotedInThisBinderTransaction.get();
- if (appOpsNotedInThisBinderTransaction == null) {
- appOpsNotedInThisBinderTransaction = new long[2];
- sAppOpsNotedInThisBinderTransaction.set(appOpsNotedInThisBinderTransaction);
+ long[] appOpsNotedForFeature = appOpsNoted.get(featureId);
+ if (appOpsNotedForFeature == null) {
+ appOpsNotedForFeature = new long[2];
+ appOpsNoted.put(featureId, appOpsNotedForFeature);
}
if (code < 64) {
- appOpsNotedInThisBinderTransaction[0] |= 1L << code;
+ appOpsNotedForFeature[0] |= 1L << code;
} else {
- appOpsNotedInThisBinderTransaction[1] |= 1L << (code - 64);
+ appOpsNotedForFeature[1] |= 1L << (code - 64);
}
} else {
// Cannot deliver the note synchronous: Hence send it to the system server to
@@ -5879,7 +6560,8 @@ public class AppOpsManager {
long token = Binder.clearCallingIdentity();
try {
- mService.noteAsyncOp(mContext.getOpPackageName(), uid, packageName, code, message);
+ mService.noteAsyncOp(mContext.getOpPackageName(), uid, packageName, code,
+ featureId, message);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
} finally {
@@ -5899,14 +6581,21 @@ public class AppOpsManager {
* @hide
*/
public static void prefixParcelWithAppOpsIfNeeded(@NonNull Parcel p) {
- long[] notedAppOps = sAppOpsNotedInThisBinderTransaction.get();
- if (notedAppOps == null || (notedAppOps[0] == 0 && notedAppOps[1] == 0)) {
+ ArrayMap<String, long[]> notedAppOps = sAppOpsNotedInThisBinderTransaction.get();
+ if (notedAppOps == null) {
return;
}
p.writeInt(Parcel.EX_HAS_NOTED_APPOPS_REPLY_HEADER);
- p.writeLong(notedAppOps[0]);
- p.writeLong(notedAppOps[1]);
+
+ int numFeatureWithNotesAppOps = notedAppOps.size();
+ p.writeInt(numFeatureWithNotesAppOps);
+
+ for (int i = 0; i < numFeatureWithNotesAppOps; i++) {
+ p.writeString(notedAppOps.keyAt(i));
+ p.writeLong(notedAppOps.valueAt(i)[0]);
+ p.writeLong(notedAppOps.valueAt(i)[1]);
+ }
}
/**
@@ -5915,26 +6604,28 @@ public class AppOpsManager {
* <p>This is called on the calling side of a two way binder transaction just after the
* transaction returns.
*
- * <p>Note: Make sure to keep frameworks/native/libs/binder/Status.cpp::readAndLogNotedAppops
- * in sync.
- *
* @param p The parcel to read from
*
* @hide
*/
public static void readAndLogNotedAppops(@NonNull Parcel p) {
- long[] rawNotedAppOps = new long[2];
- rawNotedAppOps[0] = p.readLong();
- rawNotedAppOps[1] = p.readLong();
-
- if (rawNotedAppOps[0] != 0 || rawNotedAppOps[1] != 0) {
- BitSet notedAppOps = BitSet.valueOf(rawNotedAppOps);
-
- synchronized (sLock) {
- for (int code = notedAppOps.nextSetBit(0); code != -1;
- code = notedAppOps.nextSetBit(code + 1)) {
- if (sNotedAppOpsCollector != null) {
- sNotedAppOpsCollector.onNoted(new SyncNotedAppOp(code));
+ int numFeaturesWithNotedAppOps = p.readInt();
+
+ for (int i = 0; i < numFeaturesWithNotedAppOps; i++) {
+ String featureId = p.readString();
+ long[] rawNotedAppOps = new long[2];
+ rawNotedAppOps[0] = p.readLong();
+ rawNotedAppOps[1] = p.readLong();
+
+ if (rawNotedAppOps[0] != 0 || rawNotedAppOps[1] != 0) {
+ BitSet notedAppOps = BitSet.valueOf(rawNotedAppOps);
+
+ synchronized (sLock) {
+ for (int code = notedAppOps.nextSetBit(0); code != -1;
+ code = notedAppOps.nextSetBit(code + 1)) {
+ if (sNotedAppOpsCollector != null) {
+ sNotedAppOpsCollector.onNoted(new SyncNotedAppOp(code, featureId));
+ }
}
}
}
@@ -6121,8 +6812,8 @@ public class AppOpsManager {
*
* @see #startWatchingActive(int[], OnOpActiveChangedListener)
* @see #stopWatchingMode(OnOpChangedListener)
- * @see #finishOp(int)
- * @see #startOp(int, int, String, boolean, String)
+ * @see #finishOp(int, int, String, String)
+ * @see #startOp(int, int, String, boolean, String, String)
*
* @hide */
@TestApi
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index 19be6c92e91c..bd7ef2ac3f68 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -16,10 +16,11 @@
package android.app;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.util.SparseIntArray;
import com.android.internal.util.function.QuadFunction;
-import com.android.internal.util.function.TriFunction;
/**
* App ops service local interface.
@@ -60,12 +61,14 @@ public abstract class AppOpsManagerInternal {
*
* @param code The op code to note.
* @param uid The UID for which to note.
- * @param packageName The package for which to note.
+ * @param packageName The package for which to note. {@code null} for system package.
+ * @param featureId Id of the feature in the package
* @param superImpl The super implementation.
* @return The app op note result.
*/
- int noteOperation(int code, int uid, String packageName,
- TriFunction<Integer, Integer, String, Integer> superImpl);
+ int noteOperation(int code, int uid, @Nullable String packageName,
+ @Nullable String featureId,
+ @NonNull QuadFunction<Integer, Integer, String, String, Integer> superImpl);
}
/**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index d74399c54bda..03ef286c48c1 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2414,14 +2414,11 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public Bundle getSuspendedPackageAppExtras() {
- final PersistableBundle extras;
try {
- extras = mPM.getSuspendedPackageAppExtras(mContext.getOpPackageName(),
- getUserId());
+ return mPM.getSuspendedPackageAppExtras(mContext.getOpPackageName(), getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- return extras != null ? new Bundle(extras.deepCopy()) : null;
}
@Override
@@ -3163,6 +3160,15 @@ public class ApplicationPackageManager extends PackageManager {
}
@Override
+ public String[] getTelephonyPackageNames() {
+ try {
+ return mPM.getTelephonyPackageNames();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
public String getSystemCaptionsServicePackageName() {
try {
return mPM.getSystemCaptionsServicePackageName();
@@ -3172,6 +3178,15 @@ public class ApplicationPackageManager extends PackageManager {
}
@Override
+ public String getSetupWizardPackageName() {
+ try {
+ return mPM.getSetupWizardPackageName();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
public String getIncidentReportApproverPackageName() {
try {
return mPM.getIncidentReportApproverPackageName();
diff --git a/core/java/android/app/AsyncNotedAppOp.java b/core/java/android/app/AsyncNotedAppOp.java
index 64f886aa2f1d..958ebae003ea 100644
--- a/core/java/android/app/AsyncNotedAppOp.java
+++ b/core/java/android/app/AsyncNotedAppOp.java
@@ -25,7 +25,7 @@ import com.android.internal.annotations.Immutable;
import com.android.internal.util.DataClass;
/**
- * When an {@link AppOpsManager#noteOp(String, int, String, String) app-op is noted} and the
+ * When an {@link AppOpsManager#noteOp(String, int, String, String, String) app-op is noted} and the
* app the app-op is noted for has a {@link AppOpsManager.AppOpsCollector} registered the note-event
* needs to be delivered to the collector. Usually this is done via an {@link SyncNotedAppOp}, but
* in some cases this is not possible. In this case an {@link AsyncNotedAppOp} is send to the system
@@ -51,6 +51,9 @@ public final class AsyncNotedAppOp implements Parcelable {
*/
private final @Nullable String mNotingPackageName;
+ /** {@link android.content.Context#createFeatureContext Feature} in the app */
+ private final @Nullable String mFeatureId;
+
/** Message associated with the noteOp. This message is set by the app noting the op */
private final @NonNull String mMessage;
@@ -66,14 +69,18 @@ public final class AsyncNotedAppOp implements Parcelable {
- // Code below generated by codegen v1.0.0.
+ // Code below generated by codegen v1.0.9.
//
// DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/AsyncNotedAppOp.java
//
- // CHECKSTYLE:OFF Generated code
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
/**
* Creates a new AsyncNotedAppOp.
@@ -83,7 +90,10 @@ public final class AsyncNotedAppOp implements Parcelable {
* @param notingUid
* Uid that noted the op
* @param notingPackageName
- * Package that noted the op
+ * Package that noted the op. {@code null} if the package name that noted the op could be not
+ * be determined (e.g. when the op is noted from native code).
+ * @param featureId
+ * {@link android.content.Context#createFeatureContext Feature} in the app
* @param message
* Message associated with the noteOp. This message is set by the app noting the op
* @param time
@@ -95,6 +105,7 @@ public final class AsyncNotedAppOp implements Parcelable {
@IntRange(from = 0, to = AppOpsManager._NUM_OP - 1) int opCode,
@IntRange(from = 0) int notingUid,
@Nullable String notingPackageName,
+ @Nullable String featureId,
@NonNull String message,
@IntRange(from = 0) long time) {
this.mOpCode = opCode;
@@ -107,6 +118,7 @@ public final class AsyncNotedAppOp implements Parcelable {
IntRange.class, null, mNotingUid,
"from", 0);
this.mNotingPackageName = notingPackageName;
+ this.mFeatureId = featureId;
this.mMessage = message;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mMessage);
@@ -127,7 +139,8 @@ public final class AsyncNotedAppOp implements Parcelable {
}
/**
- * Package that noted the op
+ * Package that noted the op. {@code null} if the package name that noted the op could be not
+ * be determined (e.g. when the op is noted from native code).
*/
@DataClass.Generated.Member
public @Nullable String getNotingPackageName() {
@@ -135,6 +148,14 @@ public final class AsyncNotedAppOp implements Parcelable {
}
/**
+ * {@link android.content.Context#createFeatureContext Feature} in the app
+ */
+ @DataClass.Generated.Member
+ public @Nullable String getFeatureId() {
+ return mFeatureId;
+ }
+
+ /**
* Message associated with the noteOp. This message is set by the app noting the op
*/
@DataClass.Generated.Member
@@ -152,7 +173,7 @@ public final class AsyncNotedAppOp implements Parcelable {
@Override
@DataClass.Generated.Member
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
// You can override field equality logic by defining either of the methods like:
// boolean fieldNameEquals(AsyncNotedAppOp other) { ... }
// boolean fieldNameEquals(FieldType otherValue) { ... }
@@ -166,6 +187,7 @@ public final class AsyncNotedAppOp implements Parcelable {
&& mOpCode == that.mOpCode
&& mNotingUid == that.mNotingUid
&& java.util.Objects.equals(mNotingPackageName, that.mNotingPackageName)
+ && java.util.Objects.equals(mFeatureId, that.mFeatureId)
&& java.util.Objects.equals(mMessage, that.mMessage)
&& mTime == that.mTime;
}
@@ -180,6 +202,7 @@ public final class AsyncNotedAppOp implements Parcelable {
_hash = 31 * _hash + mOpCode;
_hash = 31 * _hash + mNotingUid;
_hash = 31 * _hash + java.util.Objects.hashCode(mNotingPackageName);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mFeatureId);
_hash = 31 * _hash + java.util.Objects.hashCode(mMessage);
_hash = 31 * _hash + Long.hashCode(mTime);
return _hash;
@@ -187,16 +210,18 @@ public final class AsyncNotedAppOp implements Parcelable {
@Override
@DataClass.Generated.Member
- public void writeToParcel(android.os.Parcel dest, int flags) {
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
byte flg = 0;
if (mNotingPackageName != null) flg |= 0x4;
+ if (mFeatureId != null) flg |= 0x8;
dest.writeByte(flg);
dest.writeInt(mOpCode);
dest.writeInt(mNotingUid);
if (mNotingPackageName != null) dest.writeString(mNotingPackageName);
+ if (mFeatureId != null) dest.writeString(mFeatureId);
dest.writeString(mMessage);
dest.writeLong(mTime);
}
@@ -205,6 +230,43 @@ public final class AsyncNotedAppOp implements Parcelable {
@DataClass.Generated.Member
public int describeContents() { return 0; }
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ AsyncNotedAppOp(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ int opCode = in.readInt();
+ int notingUid = in.readInt();
+ String notingPackageName = (flg & 0x4) == 0 ? null : in.readString();
+ String featureId = (flg & 0x8) == 0 ? null : in.readString();
+ String message = in.readString();
+ long time = in.readLong();
+
+ this.mOpCode = opCode;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mOpCode,
+ "from", 0,
+ "to", AppOpsManager._NUM_OP - 1);
+ this.mNotingUid = notingUid;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mNotingUid,
+ "from", 0);
+ this.mNotingPackageName = notingPackageName;
+ this.mFeatureId = featureId;
+ this.mMessage = message;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mMessage);
+ this.mTime = time;
+ com.android.internal.util.AnnotationValidations.validate(
+ IntRange.class, null, mTime,
+ "from", 0);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
@DataClass.Generated.Member
public static final @NonNull Parcelable.Creator<AsyncNotedAppOp> CREATOR
= new Parcelable.Creator<AsyncNotedAppOp>() {
@@ -214,31 +276,16 @@ public final class AsyncNotedAppOp implements Parcelable {
}
@Override
- @SuppressWarnings({"unchecked", "RedundantCast"})
- public AsyncNotedAppOp createFromParcel(android.os.Parcel in) {
- // You can override field unparcelling by defining methods like:
- // static FieldType unparcelFieldName(Parcel in) { ... }
-
- byte flg = in.readByte();
- int opCode = in.readInt();
- int notingUid = in.readInt();
- String notingPackageName = (flg & 0x4) == 0 ? null : in.readString();
- String message = in.readString();
- long time = in.readLong();
- return new AsyncNotedAppOp(
- opCode,
- notingUid,
- notingPackageName,
- message,
- time);
+ public AsyncNotedAppOp createFromParcel(@NonNull android.os.Parcel in) {
+ return new AsyncNotedAppOp(in);
}
};
@DataClass.Generated(
- time = 1566503083973L,
- codegenVersion = "1.0.0",
+ time = 1571327470155L,
+ codegenVersion = "1.0.9",
sourceFile = "frameworks/base/core/java/android/app/AsyncNotedAppOp.java",
- inputSignatures = "private final @android.annotation.IntRange(from=0L, to=90L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mNotingPackageName\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
+ inputSignatures = "private final @android.annotation.IntRange(from=0L, to=91L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mNotingPackageName\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index ef23d5e7a424..eb2b2bca8ca9 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -204,6 +204,9 @@ class ContextImpl extends Context {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final String mOpPackageName;
+ /** If of feature this context is for */
+ private final @Nullable String mFeatureId;
+
private final @NonNull ResourcesManager mResourcesManager;
@UnsupportedAppUsage
private @NonNull Resources mResources;
@@ -395,6 +398,12 @@ class ContextImpl extends Context {
return mOpPackageName != null ? mOpPackageName : getBasePackageName();
}
+ /** @hide */
+ @Override
+ public @Nullable String getFeatureId() {
+ return mFeatureId;
+ }
+
@Override
public ApplicationInfo getApplicationInfo() {
if (mPackageInfo != null) {
@@ -741,12 +750,21 @@ class ContextImpl extends Context {
public File getCodeCacheDir() {
synchronized (mSync) {
if (mCodeCacheDir == null) {
- mCodeCacheDir = new File(getDataDir(), "code_cache");
+ mCodeCacheDir = getCodeCacheDirBeforeBind(getDataDir());
}
return ensurePrivateCacheDirExists(mCodeCacheDir, XATTR_INODE_CODE_CACHE);
}
}
+ /**
+ * Helper for getting code-cache dir potentially before application bind.
+ *
+ * @hide
+ */
+ static File getCodeCacheDirBeforeBind(File dataDir) {
+ return new File(dataDir, "code_cache");
+ }
+
@Override
public File getExternalCacheDir() {
// Operates on primary external storage
@@ -2150,7 +2168,7 @@ class ContextImpl extends Context {
LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
flags | CONTEXT_REGISTER_PACKAGE);
if (pi != null) {
- ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken,
+ ContextImpl c = new ContextImpl(this, mMainThread, pi, null, null, mActivityToken,
new UserHandle(UserHandle.getUserId(application.uid)), flags, null, null);
final int displayId = getDisplayId();
@@ -2178,15 +2196,15 @@ class ContextImpl extends Context {
if (packageName.equals("system") || packageName.equals("android")) {
// The system resources are loaded in every application, so we can safely copy
// the context without reloading Resources.
- return new ContextImpl(this, mMainThread, mPackageInfo, null, mActivityToken, user,
- flags, null, null);
+ return new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, null,
+ mActivityToken, user, flags, null, null);
}
LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
if (pi != null) {
- ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, user,
- flags, null, null);
+ ContextImpl c = new ContextImpl(this, mMainThread, pi, mFeatureId, null,
+ mActivityToken, user, flags, null, null);
final int displayId = getDisplayId();
@@ -2203,6 +2221,15 @@ class ContextImpl extends Context {
}
@Override
+ public Context createContextAsUser(UserHandle user, @CreatePackageOptions int flags) {
+ try {
+ return createPackageContextAsUser(getPackageName(), flags, user);
+ } catch (NameNotFoundException e) {
+ throw new IllegalStateException("Own package not found: package=" + getPackageName());
+ }
+ }
+
+ @Override
public Context createContextForSplit(String splitName) throws NameNotFoundException {
if (!mPackageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {
// All Splits are always loaded.
@@ -2212,8 +2239,8 @@ class ContextImpl extends Context {
final ClassLoader classLoader = mPackageInfo.getSplitClassLoader(splitName);
final String[] paths = mPackageInfo.getSplitPaths(splitName);
- final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, splitName,
- mActivityToken, mUser, mFlags, classLoader, null);
+ final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo,
+ mFeatureId, splitName, mActivityToken, mUser, mFlags, classLoader, null);
final int displayId = getDisplayId();
@@ -2236,8 +2263,8 @@ class ContextImpl extends Context {
throw new IllegalArgumentException("overrideConfiguration must not be null");
}
- ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName,
- mActivityToken, mUser, mFlags, mClassLoader, null);
+ ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId,
+ mSplitName, mActivityToken, mUser, mFlags, mClassLoader, null);
final int displayId = getDisplayId();
context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
@@ -2251,8 +2278,8 @@ class ContextImpl extends Context {
throw new IllegalArgumentException("display must not be null");
}
- ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName,
- mActivityToken, mUser, mFlags, mClassLoader, null);
+ ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId,
+ mSplitName, mActivityToken, mUser, mFlags, mClassLoader, null);
final int displayId = display.getDisplayId();
context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
@@ -2262,19 +2289,25 @@ class ContextImpl extends Context {
}
@Override
+ public @NonNull Context createFeatureContext(@Nullable String featureId) {
+ return new ContextImpl(this, mMainThread, mPackageInfo, featureId, mSplitName,
+ mActivityToken, mUser, mFlags, mClassLoader, null);
+ }
+
+ @Override
public Context createDeviceProtectedStorageContext() {
final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE)
| Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
- return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser,
- flags, mClassLoader, null);
+ return new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, mSplitName,
+ mActivityToken, mUser, flags, mClassLoader, null);
}
@Override
public Context createCredentialProtectedStorageContext() {
final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE)
| Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
- return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser,
- flags, mClassLoader, null);
+ return new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId, mSplitName,
+ mActivityToken, mUser, flags, mClassLoader, null);
}
@Override
@@ -2418,8 +2451,8 @@ class ContextImpl extends Context {
@UnsupportedAppUsage
static ContextImpl createSystemContext(ActivityThread mainThread) {
LoadedApk packageInfo = new LoadedApk(mainThread);
- ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
- null, null);
+ ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, null,
+ 0, null, null);
context.setResources(packageInfo.getResources());
context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
context.mResourcesManager.getDisplayMetrics());
@@ -2436,7 +2469,7 @@ class ContextImpl extends Context {
static ContextImpl createSystemUiContext(ContextImpl systemContext, int displayId) {
final LoadedApk packageInfo = systemContext.mPackageInfo;
ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo, null,
- null, null, 0, null, null);
+ null, null, null, 0, null, null);
context.setResources(createResources(null, packageInfo, null, displayId, null,
packageInfo.getCompatibilityInfo()));
context.updateDisplay(displayId);
@@ -2459,8 +2492,8 @@ class ContextImpl extends Context {
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo,
String opPackageName) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
- ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
- null, opPackageName);
+ ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, null,
+ 0, null, opPackageName);
context.setResources(packageInfo.getResources());
return context;
}
@@ -2487,8 +2520,8 @@ class ContextImpl extends Context {
}
}
- ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
- activityToken, null, 0, classLoader, null);
+ ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null,
+ activityInfo.splitName, activityToken, null, 0, classLoader, null);
// Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;
@@ -2516,9 +2549,9 @@ class ContextImpl extends Context {
}
private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread,
- @NonNull LoadedApk packageInfo, @Nullable String splitName,
- @Nullable IBinder activityToken, @Nullable UserHandle user, int flags,
- @Nullable ClassLoader classLoader, @Nullable String overrideOpPackageName) {
+ @NonNull LoadedApk packageInfo, @Nullable String featureId,
+ @Nullable String splitName, @Nullable IBinder activityToken, @Nullable UserHandle user,
+ int flags, @Nullable ClassLoader classLoader, @Nullable String overrideOpPackageName) {
mOuterContext = this;
// If creator didn't specify which storage to use, use the default
@@ -2569,7 +2602,7 @@ class ContextImpl extends Context {
}
mOpPackageName = overrideOpPackageName != null ? overrideOpPackageName : opPackageName;
-
+ mFeatureId = featureId;
mContentResolver = new ApplicationContentResolver(this, mainThread);
}
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 905f47540fd9..96751dba04f4 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -18,7 +18,6 @@ package android.app;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
-import android.annotation.NonNull;
import android.app.SharedElementCallback.OnSharedElementsReadyListener;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
@@ -70,13 +69,11 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
private Runnable mOnTransitionComplete;
EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
- ArrayList<String> sharedElementNames, boolean isReturning, boolean isCrossTask,
- @NonNull Runnable onTransitionComplete) {
+ ArrayList<String> sharedElementNames, boolean isReturning, boolean isCrossTask) {
super(activity.getWindow(), sharedElementNames,
getListener(activity, isReturning && !isCrossTask), isReturning);
mActivity = activity;
mIsCrossTask = isCrossTask;
- mOnTransitionComplete = onTransitionComplete;
setResultReceiver(resultReceiver);
prepareEnter();
Bundle resultReceiverBundle = new Bundle();
@@ -570,6 +567,14 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
return transition;
}
+ public void runAfterTransitionsComplete(Runnable onTransitionComplete) {
+ if (!isTransitionRunning()) {
+ onTransitionsComplete();
+ } else {
+ mOnTransitionComplete = onTransitionComplete;
+ }
+ }
+
@Override
protected void onTransitionsComplete() {
moveSharedElementsFromOverlay();
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index dca00a2bbd83..31a29d4a5c86 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -351,10 +351,6 @@ interface IActivityManager {
// Request a heap dump for the system server.
void requestSystemServerHeapDump();
- // Deprecated - This method is only used by a few internal components and it will soon start
- // using bug report API (which will be restricted to a few, pre-defined apps).
- // No new code should be calling it.
- @UnsupportedAppUsage
void requestBugReport(int bugreportType);
void requestBugReportWithDescription(in @nullable String shareTitle,
in @nullable String shareDescription, int bugreportType);
@@ -364,7 +360,7 @@ interface IActivityManager {
* that are passed to this API as parameters
*
* @param shareTitle should be a valid legible string less than 50 chars long
- * @param shareDescription should be less than 91 bytes when encoded into UTF-8 format
+ * @param shareDescription should be less than 150 chars long
*
* @throws IllegalArgumentException if shareTitle or shareDescription is too big or if the
* paremeters cannot be encoding to an UTF-8 charset.
@@ -372,13 +368,12 @@ interface IActivityManager {
void requestTelephonyBugReport(in String shareTitle, in String shareDescription);
/**
- * Deprecated - This method is only used by Wifi, and it will soon start using
- * bug report API.
+ * This method is only used by Wifi.
*
* Takes a minimal bugreport of Wifi-related state.
*
* @param shareTitle should be a valid legible string less than 50 chars long
- * @param shareDescription should be less than 91 bytes when encoded into UTF-8 format
+ * @param shareDescription should be less than 150 chars long
*
* @throws IllegalArgumentException if shareTitle or shareDescription is too big or if the
* parameters cannot be encoding to an UTF-8 charset.
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index dda3bb53ebe3..f5914412663b 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -215,7 +215,6 @@ interface IActivityTaskManager {
void releaseSomeActivities(in IApplicationThread app);
Bitmap getTaskDescriptionIcon(in String filename, int userId);
- void startInPlaceAnimationOnFrontMostApplication(in Bundle opts);
void registerTaskStackListener(in ITaskStackListener listener);
void unregisterTaskStackListener(in ITaskStackListener listener);
void setTaskResizeable(int taskId, int resizeableMode);
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index cfa065ba5bc9..51a64fff7c45 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -137,6 +137,7 @@ oneway interface IApplicationThread {
IVoiceInteractor voiceInteractor);
void handleTrustStorageUpdate();
void attachAgent(String path);
+ void attachStartupAgents(String dataDir);
void scheduleApplicationInfoChanged(in ApplicationInfo ai);
void setNetworkBlockSeq(long procStateSeq);
void scheduleTransaction(in ClientTransaction transaction);
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 750020eb5bb8..06288c0794b0 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -158,7 +158,7 @@ oneway interface ITaskStackListener {
* @param activityToken Token of the size compatibility mode activity. It will be null when
* switching to a activity that is not in size compatibility mode or the
* configuration of the activity.
- * @see com.android.server.wm.AppWindowToken#inSizeCompatMode
+ * @see com.android.server.wm.ActivityRecord#inSizeCompatMode
*/
void onSizeCompatModeActivityChanged(int displayId, in IBinder activityToken);
@@ -197,4 +197,11 @@ oneway interface ITaskStackListener {
* Called when any additions or deletions to the recent tasks list have been made.
*/
void onRecentTaskListUpdated();
+
+ /**
+ * Called when Recent Tasks list is frozen or unfrozen.
+ *
+ * @param frozen if true, Recents Tasks list is currently frozen, false otherwise
+ */
+ void onRecentTaskListFrozenChanged(boolean frozen);
}
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 9b667a118ebc..b1565ab8a501 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -87,12 +87,6 @@ public class KeyguardManager {
"android.app.action.CONFIRM_FRP_CREDENTIAL";
/**
- * @hide
- */
- public static final String EXTRA_BIOMETRIC_PROMPT_BUNDLE =
- "android.app.extra.BIOMETRIC_PROMPT_BUNDLE";
-
- /**
* A CharSequence dialog title to show to the user when used with a
* {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
* @hide
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2f03ed484e96..efb9f6bb88f1 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -10511,12 +10511,7 @@ public class Notification implements Parcelable
final StandardTemplateParams fillTextsFrom(Builder b) {
Bundle extras = b.mN.extras;
this.title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE));
-
- CharSequence text = extras.getCharSequence(EXTRA_BIG_TEXT);
- if (TextUtils.isEmpty(text)) {
- text = extras.getCharSequence(EXTRA_TEXT);
- }
- this.text = b.processLegacyText(text);
+ this.text = b.processLegacyText(extras.getCharSequence(EXTRA_TEXT));
this.summaryText = extras.getCharSequence(EXTRA_SUB_TEXT);
return this;
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 316cab8d600b..6dca5d921210 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -997,8 +997,13 @@ public class NotificationManager {
}
/**
- * @hide
+ * <p>
+ * Gets the currently applied notification policy. If {@link #getCurrentInterruptionFilter}
+ * is equal to {@link #INTERRUPTION_FILTER_ALL}, then the consolidated notification policy
+ * will match the default notification policy returned by {@link #getNotificationPolicy}.
+ * </p>
*/
+ @Nullable
public NotificationManager.Policy getConsolidatedNotificationPolicy() {
INotificationManager service = getService();
try {
@@ -1024,7 +1029,7 @@ public class NotificationManager {
* Returns AutomaticZenRules owned by the caller.
*
* <p>
- * Throws a SecurityException if policy access is granted to this package.
+ * Throws a SecurityException if policy access is not granted to this package.
* See {@link #isNotificationPolicyAccessGranted}.
*/
public Map<String, AutomaticZenRule> getAutomaticZenRules() {
@@ -1048,7 +1053,7 @@ public class NotificationManager {
* Returns the AutomaticZenRule with the given id, if it exists and the caller has access.
*
* <p>
- * Throws a SecurityException if policy access is granted to this package.
+ * Throws a SecurityException if policy access is not granted to this package.
* See {@link #isNotificationPolicyAccessGranted}.
*
* <p>
@@ -1068,7 +1073,7 @@ public class NotificationManager {
* Creates the given zen rule.
*
* <p>
- * Throws a SecurityException if policy access is granted to this package.
+ * Throws a SecurityException if policy access is not granted to this package.
* See {@link #isNotificationPolicyAccessGranted}.
*
* @param automaticZenRule the rule to create.
@@ -1087,7 +1092,7 @@ public class NotificationManager {
* Updates the given zen rule.
*
* <p>
- * Throws a SecurityException if policy access is granted to this package.
+ * Throws a SecurityException if policy access is not granted to this package.
* See {@link #isNotificationPolicyAccessGranted}.
*
* <p>
@@ -1129,7 +1134,7 @@ public class NotificationManager {
* Deletes the automatic zen rule with the given id.
*
* <p>
- * Throws a SecurityException if policy access is granted to this package.
+ * Throws a SecurityException if policy access is not granted to this package.
* See {@link #isNotificationPolicyAccessGranted}.
*
* <p>
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index a5f76ac52095..dc01a924267e 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -20,6 +20,7 @@ import static android.app.ActivityThread.DEBUG_CONFIGURATION;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -31,6 +32,7 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.ResourcesImpl;
import android.content.res.ResourcesKey;
+import android.content.res.loader.ResourceLoader;
import android.hardware.display.DisplayManagerGlobal;
import android.os.IBinder;
import android.os.Process;
@@ -44,6 +46,7 @@ import android.util.Slog;
import android.view.Display;
import android.view.DisplayAdjustments;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
@@ -52,7 +55,11 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.WeakHashMap;
import java.util.function.Predicate;
@@ -91,6 +98,52 @@ public class ResourcesManager {
new ArrayMap<>();
/**
+ * A list of {@link Resources} that contain unique {@link ResourcesImpl}s.
+ *
+ * These are isolated so that {@link ResourceLoader}s can be added and removed without
+ * affecting other instances.
+ *
+ * When a reference is added here, it is guaranteed that the {@link ResourcesImpl}
+ * it contains is unique to itself and will never be set to a shared reference.
+ */
+ @GuardedBy("this")
+ private List<ResourcesWithLoaders> mResourcesWithLoaders = Collections.emptyList();
+
+ private static class ResourcesWithLoaders {
+
+ private WeakReference<Resources> mResources;
+ private ResourcesKey mResourcesKey;
+
+ @Nullable
+ private WeakReference<IBinder> mActivityToken;
+
+ ResourcesWithLoaders(Resources resources, ResourcesKey resourcesKey,
+ IBinder activityToken) {
+ this.mResources = new WeakReference<>(resources);
+ this.mResourcesKey = resourcesKey;
+ this.mActivityToken = new WeakReference<>(activityToken);
+ }
+
+ @Nullable
+ Resources resources() {
+ return mResources.get();
+ }
+
+ @Nullable
+ IBinder activityToken() {
+ return mActivityToken == null ? null : mActivityToken.get();
+ }
+
+ ResourcesKey resourcesKey() {
+ return mResourcesKey;
+ }
+
+ void updateKey(ResourcesKey newKey) {
+ mResourcesKey = newKey;
+ }
+ }
+
+ /**
* A list of Resource references that can be reused.
*/
@UnsupportedAppUsage
@@ -181,16 +234,48 @@ public class ResourcesManager {
public void invalidatePath(String path) {
synchronized (this) {
int count = 0;
- for (int i = 0; i < mResourceImpls.size();) {
+
+ for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
final ResourcesKey key = mResourceImpls.keyAt(i);
if (key.isPathReferenced(path)) {
- cleanupResourceImpl(key);
+ ResourcesImpl impl = mResourceImpls.removeAt(i).get();
+ if (impl != null) {
+ impl.flushLayoutCache();
+ }
+ count++;
+ }
+ }
+
+ for (int i = mResourcesWithLoaders.size() - 1; i >= 0; i--) {
+ ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(i);
+ Resources resources = resourcesWithLoaders.resources();
+ if (resources == null) {
+ continue;
+ }
+
+ final ResourcesKey key = resourcesWithLoaders.resourcesKey();
+ if (key.isPathReferenced(path)) {
+ mResourcesWithLoaders.remove(i);
+ ResourcesImpl impl = resources.getImpl();
+ if (impl != null) {
+ impl.flushLayoutCache();
+ }
count++;
- } else {
- i++;
}
}
+
Log.i(TAG, "Invalidated " + count + " asset managers that referenced " + path);
+
+ for (int i = mCachedApkAssets.size() - 1; i >= 0; i--) {
+ final ApkKey key = mCachedApkAssets.keyAt(i);
+ if (key.path.equals(path)) {
+ WeakReference<ApkAssets> apkAssetsRef = mCachedApkAssets.remove(key);
+ if (apkAssetsRef != null && apkAssetsRef.get() != null) {
+ apkAssetsRef.get().close();
+ }
+ mCachedApkAssets.remove(key);
+ }
+ }
}
}
@@ -305,15 +390,6 @@ public class ResourcesManager {
}
}
- private void cleanupResourceImpl(ResourcesKey removedKey) {
- // Remove resource key to resource impl mapping and flush cache
- final ResourcesImpl res = mResourceImpls.remove(removedKey).get();
-
- if (res != null) {
- res.flushLayoutCache();
- }
- }
-
private static String overlayPathToIdmapPath(String path) {
return "/data/resource-cache/" + path.substring(1).replace('/', '@') + "@idmap";
}
@@ -487,6 +563,16 @@ public class ResourcesManager {
pw.print("resource impls: ");
pw.println(countLiveReferences(mResourceImpls.values()));
+
+ int resourcesWithLoadersCount = 0;
+ for (int index = 0; index < mResourcesWithLoaders.size(); index++) {
+ if (mResourcesWithLoaders.get(index).resources() != null) {
+ resourcesWithLoadersCount++;
+ }
+ }
+
+ pw.print("resources with loaders: ");
+ pw.println(resourcesWithLoadersCount);
}
}
@@ -567,11 +653,24 @@ public class ResourcesManager {
*/
private @Nullable ResourcesKey findKeyForResourceImplLocked(
@NonNull ResourcesImpl resourceImpl) {
- final int refCount = mResourceImpls.size();
+ int size = mResourcesWithLoaders.size();
+ for (int index = 0; index < size; index++) {
+ ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(index);
+ Resources resources = resourcesWithLoaders.resources();
+ if (resources == null) {
+ continue;
+ }
+
+ if (resourceImpl == resources.getImpl()) {
+ return resourcesWithLoaders.resourcesKey();
+ }
+ }
+
+ int refCount = mResourceImpls.size();
for (int i = 0; i < refCount; i++) {
WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
- if (impl != null && resourceImpl == impl) {
+ if (resourceImpl == impl) {
return mResourceImpls.keyAt(i);
}
}
@@ -613,31 +712,55 @@ public class ResourcesManager {
return activityResources;
}
- /**
- * Gets an existing Resources object tied to this Activity, or creates one if it doesn't exist
- * or the class loader is different.
- */
- private @NonNull Resources getOrCreateResourcesForActivityLocked(@NonNull IBinder activityToken,
- @NonNull ClassLoader classLoader, @NonNull ResourcesImpl impl,
- @NonNull CompatibilityInfo compatInfo) {
- final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
- activityToken);
+ @Nullable
+ private Resources findResourcesForActivityLocked(@NonNull IBinder targetActivityToken,
+ @NonNull ResourcesKey targetKey, @NonNull ClassLoader targetClassLoader) {
+ int size = mResourcesWithLoaders.size();
+ for (int index = 0; index < size; index++) {
+ ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(index);
+ Resources resources = resourcesWithLoaders.resources();
+ if (resources == null) {
+ continue;
+ }
- final int refCount = activityResources.activityResources.size();
- for (int i = 0; i < refCount; i++) {
- WeakReference<Resources> weakResourceRef = activityResources.activityResources.get(i);
- Resources resources = weakResourceRef.get();
+ IBinder activityToken = resourcesWithLoaders.activityToken();
+ ResourcesKey key = resourcesWithLoaders.resourcesKey();
- if (resources != null
- && Objects.equals(resources.getClassLoader(), classLoader)
- && resources.getImpl() == impl) {
- if (DEBUG) {
- Slog.d(TAG, "- using existing ref=" + resources);
- }
+ ClassLoader classLoader = resources.getClassLoader();
+
+ if (Objects.equals(activityToken, targetActivityToken)
+ && Objects.equals(key, targetKey)
+ && Objects.equals(classLoader, targetClassLoader)) {
+ return resources;
+ }
+ }
+
+ ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
+ targetActivityToken);
+
+ size = activityResources.activityResources.size();
+ for (int index = 0; index < size; index++) {
+ WeakReference<Resources> ref = activityResources.activityResources.get(index);
+ Resources resources = ref.get();
+ ResourcesKey key = resources == null ? null : findKeyForResourceImplLocked(
+ resources.getImpl());
+
+ if (key != null
+ && Objects.equals(resources.getClassLoader(), targetClassLoader)
+ && Objects.equals(key, targetKey)) {
return resources;
}
}
+ return null;
+ }
+
+ private @NonNull Resources createResourcesForActivityLocked(@NonNull IBinder activityToken,
+ @NonNull ClassLoader classLoader, @NonNull ResourcesImpl impl,
+ @NonNull CompatibilityInfo compatInfo) {
+ final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
+ activityToken);
+
Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
: new Resources(classLoader);
resources.setImpl(impl);
@@ -649,28 +772,8 @@ public class ResourcesManager {
return resources;
}
- /**
- * Gets an existing Resources object if the class loader and ResourcesImpl are the same,
- * otherwise creates a new Resources object.
- */
- private @NonNull Resources getOrCreateResourcesLocked(@NonNull ClassLoader classLoader,
+ private @NonNull Resources createResourcesLocked(@NonNull ClassLoader classLoader,
@NonNull ResourcesImpl impl, @NonNull CompatibilityInfo compatInfo) {
- // Find an existing Resources that has this ResourcesImpl set.
- final int refCount = mResourceReferences.size();
- for (int i = 0; i < refCount; i++) {
- WeakReference<Resources> weakResourceRef = mResourceReferences.get(i);
- Resources resources = weakResourceRef.get();
- if (resources != null &&
- Objects.equals(resources.getClassLoader(), classLoader) &&
- resources.getImpl() == impl) {
- if (DEBUG) {
- Slog.d(TAG, "- using existing ref=" + resources);
- }
- return resources;
- }
- }
-
- // Create a new Resources reference and use the existing ResourcesImpl object.
Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
: new Resources(classLoader);
resources.setImpl(impl);
@@ -738,16 +841,73 @@ public class ResourcesManager {
updateResourcesForActivity(activityToken, overrideConfig, displayId,
false /* movedToDifferentDisplay */);
+ cleanupReferences(activityToken);
+ rebaseKeyForActivity(activityToken, key);
+
+ synchronized (this) {
+ Resources resources = findResourcesForActivityLocked(activityToken, key,
+ classLoader);
+ if (resources != null) {
+ return resources;
+ }
+ }
+
// Now request an actual Resources object.
- return getOrCreateResources(activityToken, key, classLoader);
+ return createResources(activityToken, key, classLoader);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
/**
- * Gets an existing Resources object set with a ResourcesImpl object matching the given key,
- * or creates one if it doesn't exist.
+ * Rebases a key's override config on top of the Activity's base override.
+ */
+ private void rebaseKeyForActivity(IBinder activityToken, ResourcesKey key) {
+ final ActivityResources activityResources =
+ getOrCreateActivityResourcesStructLocked(activityToken);
+
+ // Clean up any dead references so they don't pile up.
+ ArrayUtils.unstableRemoveIf(activityResources.activityResources,
+ sEmptyReferencePredicate);
+
+ // Rebase the key's override config on top of the Activity's base override.
+ if (key.hasOverrideConfiguration()
+ && !activityResources.overrideConfig.equals(Configuration.EMPTY)) {
+ final Configuration temp = new Configuration(activityResources.overrideConfig);
+ temp.updateFrom(key.mOverrideConfiguration);
+ key.mOverrideConfiguration.setTo(temp);
+ }
+ }
+
+ /**
+ * Check WeakReferences and remove any dead references so they don't pile up.
+ * @param activityToken optional token to clean up Activity resources
+ */
+ private void cleanupReferences(IBinder activityToken) {
+ synchronized (this) {
+ if (activityToken != null) {
+ ActivityResources activityResources = mActivityResourceReferences.get(
+ activityToken);
+ if (activityResources != null) {
+ ArrayUtils.unstableRemoveIf(activityResources.activityResources,
+ sEmptyReferencePredicate);
+ }
+ } else {
+ ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate);
+ }
+
+ for (int index = mResourcesWithLoaders.size() - 1; index >= 0; index--) {
+ ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(index);
+ Resources resources = resourcesWithLoaders.resources();
+ if (resources == null) {
+ mResourcesWithLoaders.remove(index);
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates a Resources object set with a ResourcesImpl object matching the given key.
*
* @param activityToken The Activity this Resources object should be associated with.
* @param key The key describing the parameters of the ResourcesImpl object.
@@ -757,7 +917,7 @@ public class ResourcesManager {
* {@link #applyConfigurationToResourcesLocked(Configuration, CompatibilityInfo)}
* is called.
*/
- private @Nullable Resources getOrCreateResources(@Nullable IBinder activityToken,
+ private @Nullable Resources createResources(@Nullable IBinder activityToken,
@NonNull ResourcesKey key, @NonNull ClassLoader classLoader) {
synchronized (this) {
if (DEBUG) {
@@ -766,66 +926,17 @@ public class ResourcesManager {
Slog.w(TAG, "!! Get resources for activity=" + activityToken + " key=" + key, here);
}
- if (activityToken != null) {
- final ActivityResources activityResources =
- getOrCreateActivityResourcesStructLocked(activityToken);
-
- // Clean up any dead references so they don't pile up.
- ArrayUtils.unstableRemoveIf(activityResources.activityResources,
- sEmptyReferencePredicate);
-
- // Rebase the key's override config on top of the Activity's base override.
- if (key.hasOverrideConfiguration()
- && !activityResources.overrideConfig.equals(Configuration.EMPTY)) {
- final Configuration temp = new Configuration(activityResources.overrideConfig);
- temp.updateFrom(key.mOverrideConfiguration);
- key.mOverrideConfiguration.setTo(temp);
- }
-
- ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(key);
- if (resourcesImpl != null) {
- if (DEBUG) {
- Slog.d(TAG, "- using existing impl=" + resourcesImpl);
- }
- return getOrCreateResourcesForActivityLocked(activityToken, classLoader,
- resourcesImpl, key.mCompatInfo);
- }
-
- // We will create the ResourcesImpl object outside of holding this lock.
-
- } else {
- // Clean up any dead references so they don't pile up.
- ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate);
-
- // Not tied to an Activity, find a shared Resources that has the right ResourcesImpl
- ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(key);
- if (resourcesImpl != null) {
- if (DEBUG) {
- Slog.d(TAG, "- using existing impl=" + resourcesImpl);
- }
- return getOrCreateResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
- }
-
- // We will create the ResourcesImpl object outside of holding this lock.
- }
-
- // If we're here, we didn't find a suitable ResourcesImpl to use, so create one now.
- ResourcesImpl resourcesImpl = createResourcesImpl(key);
+ ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(key);
if (resourcesImpl == null) {
return null;
}
- // Add this ResourcesImpl to the cache.
- mResourceImpls.put(key, new WeakReference<>(resourcesImpl));
-
- final Resources resources;
if (activityToken != null) {
- resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader,
+ return createResourcesForActivityLocked(activityToken, classLoader,
resourcesImpl, key.mCompatInfo);
} else {
- resources = getOrCreateResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
+ return createResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
}
- return resources;
}
}
@@ -856,7 +967,8 @@ public class ResourcesManager {
* {@link ClassLoader#getSystemClassLoader()} is used.
* @return a Resources object from which to access resources.
*/
- public @Nullable Resources getResources(@Nullable IBinder activityToken,
+ public @Nullable Resources getResources(
+ @Nullable IBinder activityToken,
@Nullable String resDir,
@Nullable String[] splitResDirs,
@Nullable String[] overlayDirs,
@@ -876,7 +988,14 @@ public class ResourcesManager {
overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
compatInfo);
classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
- return getOrCreateResources(activityToken, key, classLoader);
+
+ cleanupReferences(activityToken);
+
+ if (activityToken != null) {
+ rebaseKeyForActivity(activityToken, key);
+ }
+
+ return createResources(activityToken, key, classLoader);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
@@ -932,67 +1051,40 @@ public class ResourcesManager {
here);
}
- final boolean activityHasOverrideConfig =
- !activityResources.overrideConfig.equals(Configuration.EMPTY);
// Rebase each Resources associated with this Activity.
final int refCount = activityResources.activityResources.size();
for (int i = 0; i < refCount; i++) {
WeakReference<Resources> weakResRef = activityResources.activityResources.get(
i);
+
Resources resources = weakResRef.get();
if (resources == null) {
continue;
}
- // Extract the ResourcesKey that was last used to create the Resources for this
- // activity.
- final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
- if (oldKey == null) {
- Slog.e(TAG, "can't find ResourcesKey for resources impl="
- + resources.getImpl());
- continue;
- }
-
- // Build the new override configuration for this ResourcesKey.
- final Configuration rebasedOverrideConfig = new Configuration();
- if (overrideConfig != null) {
- rebasedOverrideConfig.setTo(overrideConfig);
- }
+ ResourcesKey newKey = rebaseActivityOverrideConfig(resources, oldConfig,
+ overrideConfig, displayId);
+ updateActivityResources(resources, newKey, false);
+ }
- if (activityHasOverrideConfig && oldKey.hasOverrideConfiguration()) {
- // Generate a delta between the old base Activity override configuration and
- // the actual final override configuration that was used to figure out the
- // real delta this Resources object wanted.
- Configuration overrideOverrideConfig = Configuration.generateDelta(
- oldConfig, oldKey.mOverrideConfiguration);
- rebasedOverrideConfig.updateFrom(overrideOverrideConfig);
+ // Also find loaders that are associated with an Activity
+ final int loaderCount = mResourcesWithLoaders.size();
+ for (int index = loaderCount - 1; index >= 0; index--) {
+ ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(
+ index);
+ Resources resources = resourcesWithLoaders.resources();
+ if (resources == null
+ || resourcesWithLoaders.activityToken() != activityToken) {
+ continue;
}
- // Create the new ResourcesKey with the rebased override config.
- final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir,
- oldKey.mSplitResDirs,
- oldKey.mOverlayDirs, oldKey.mLibDirs, displayId,
- rebasedOverrideConfig, oldKey.mCompatInfo);
-
- if (DEBUG) {
- Slog.d(TAG, "rebasing ref=" + resources + " from oldKey=" + oldKey
- + " to newKey=" + newKey + ", displayId=" + displayId);
- }
+ ResourcesKey newKey = rebaseActivityOverrideConfig(resources, oldConfig,
+ overrideConfig, displayId);
- ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey);
- if (resourcesImpl == null) {
- resourcesImpl = createResourcesImpl(newKey);
- if (resourcesImpl != null) {
- mResourceImpls.put(newKey, new WeakReference<>(resourcesImpl));
- }
- }
+ updateActivityResources(resources, newKey, true);
- if (resourcesImpl != null && resourcesImpl != resources.getImpl()) {
- // Set the ResourcesImpl, updating it for all users of this Resources
- // object.
- resources.setImpl(resourcesImpl);
- }
+ resourcesWithLoaders.updateKey(newKey);
}
}
} finally {
@@ -1000,6 +1092,78 @@ public class ResourcesManager {
}
}
+ /**
+ * Rebases an updated override config over any old override config and returns the new one
+ * that an Activity's Resources should be set to.
+ */
+ private ResourcesKey rebaseActivityOverrideConfig(Resources resources,
+ Configuration oldOverrideConfig, @Nullable Configuration newOverrideConfig,
+ int displayId) {
+ // Extract the ResourcesKey that was last used to create the Resources for this
+ // activity.
+ final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
+ if (oldKey == null) {
+ Slog.e(TAG, "can't find ResourcesKey for resources impl="
+ + resources.getImpl());
+ return null;
+ }
+
+ // Build the new override configuration for this ResourcesKey.
+ final Configuration rebasedOverrideConfig = new Configuration();
+ if (newOverrideConfig != null) {
+ rebasedOverrideConfig.setTo(newOverrideConfig);
+ }
+
+ final boolean hadOverrideConfig = !oldOverrideConfig.equals(Configuration.EMPTY);
+ if (hadOverrideConfig && oldKey.hasOverrideConfiguration()) {
+ // Generate a delta between the old base Activity override configuration and
+ // the actual final override configuration that was used to figure out the
+ // real delta this Resources object wanted.
+ Configuration overrideOverrideConfig = Configuration.generateDelta(
+ oldOverrideConfig, oldKey.mOverrideConfiguration);
+ rebasedOverrideConfig.updateFrom(overrideOverrideConfig);
+ }
+
+ // Create the new ResourcesKey with the rebased override config.
+ final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir,
+ oldKey.mSplitResDirs,
+ oldKey.mOverlayDirs, oldKey.mLibDirs, displayId,
+ rebasedOverrideConfig, oldKey.mCompatInfo);
+
+ if (DEBUG) {
+ Slog.d(TAG, "rebasing ref=" + resources + " from oldKey=" + oldKey
+ + " to newKey=" + newKey + ", displayId=" + displayId);
+ }
+
+ return newKey;
+ }
+
+ private void updateActivityResources(Resources resources, ResourcesKey newKey,
+ boolean hasLoader) {
+ final ResourcesImpl resourcesImpl;
+
+ if (hasLoader) {
+ // Loaders always get new Impls because they cannot be shared
+ resourcesImpl = createResourcesImpl(newKey);
+ } else {
+ resourcesImpl = findOrCreateResourcesImplForKeyLocked(newKey);
+ }
+
+ if (resourcesImpl != null && resourcesImpl != resources.getImpl()) {
+ // Set the ResourcesImpl, updating it for all users of this Resources
+ // object.
+ resources.setImpl(resourcesImpl);
+ }
+ }
+
+ @TestApi
+ public final boolean applyConfigurationToResources(@NonNull Configuration config,
+ @Nullable CompatibilityInfo compat) {
+ synchronized(this) {
+ return applyConfigurationToResourcesLocked(config, compat);
+ }
+ }
+
public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
@Nullable CompatibilityInfo compat) {
try {
@@ -1030,61 +1194,77 @@ public class ResourcesManager {
ApplicationPackageManager.configurationChanged();
//Slog.i(TAG, "Configuration changed in " + currentPackageName());
- Configuration tmpConfig = null;
+ Configuration tmpConfig = new Configuration();
for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
ResourcesKey key = mResourceImpls.keyAt(i);
WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
ResourcesImpl r = weakImplRef != null ? weakImplRef.get() : null;
if (r != null) {
- if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
- + r + " config to: " + config);
- int displayId = key.mDisplayId;
- boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
- DisplayMetrics dm = defaultDisplayMetrics;
- final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
- if (!isDefaultDisplay || hasOverrideConfiguration) {
- if (tmpConfig == null) {
- tmpConfig = new Configuration();
- }
- tmpConfig.setTo(config);
-
- // Get new DisplayMetrics based on the DisplayAdjustments given
- // to the ResourcesImpl. Update a copy if the CompatibilityInfo
- // changed, because the ResourcesImpl object will handle the
- // update internally.
- DisplayAdjustments daj = r.getDisplayAdjustments();
- if (compat != null) {
- daj = new DisplayAdjustments(daj);
- daj.setCompatibilityInfo(compat);
- }
- dm = getDisplayMetrics(displayId, daj);
-
- if (!isDefaultDisplay) {
- applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
- }
-
- if (hasOverrideConfiguration) {
- tmpConfig.updateFrom(key.mOverrideConfiguration);
- }
- r.updateConfiguration(tmpConfig, dm, compat);
- } else {
- r.updateConfiguration(config, dm, compat);
- }
- //Slog.i(TAG, "Updated app resources " + v.getKey()
- // + " " + r + ": " + r.getConfiguration());
+ applyConfigurationToResourcesLocked(config, compat, tmpConfig,
+ defaultDisplayMetrics, key, r);
} else {
- //Slog.i(TAG, "Removing old resources " + v.getKey());
mResourceImpls.removeAt(i);
}
}
+ for (int index = mResourcesWithLoaders.size() - 1; index >= 0; index--) {
+ ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(index);
+ Resources resources = resourcesWithLoaders.resources();
+ if (resources == null) {
+ mResourcesWithLoaders.remove(index);
+ continue;
+ }
+
+ applyConfigurationToResourcesLocked(config, compat, tmpConfig,
+ defaultDisplayMetrics, resourcesWithLoaders.resourcesKey(),
+ resources.getImpl());
+ }
+
return changes != 0;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
+ private void applyConfigurationToResourcesLocked(@NonNull Configuration config,
+ @Nullable CompatibilityInfo compat, Configuration tmpConfig,
+ DisplayMetrics defaultDisplayMetrics, ResourcesKey key, ResourcesImpl resourcesImpl) {
+ if (DEBUG || DEBUG_CONFIGURATION) {
+ Slog.v(TAG, "Changing resources "
+ + resourcesImpl + " config to: " + config);
+ }
+ int displayId = key.mDisplayId;
+ boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+ DisplayMetrics dm = defaultDisplayMetrics;
+ final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
+ if (!isDefaultDisplay || hasOverrideConfiguration) {
+ tmpConfig.setTo(config);
+
+ // Get new DisplayMetrics based on the DisplayAdjustments given
+ // to the ResourcesImpl. Update a copy if the CompatibilityInfo
+ // changed, because the ResourcesImpl object will handle the
+ // update internally.
+ DisplayAdjustments daj = resourcesImpl.getDisplayAdjustments();
+ if (compat != null) {
+ daj = new DisplayAdjustments(daj);
+ daj.setCompatibilityInfo(compat);
+ }
+ dm = getDisplayMetrics(displayId, daj);
+
+ if (!isDefaultDisplay) {
+ applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
+ }
+
+ if (hasOverrideConfiguration) {
+ tmpConfig.updateFrom(key.mOverrideConfiguration);
+ }
+ resourcesImpl.updateConfiguration(tmpConfig, dm, compat);
+ } else {
+ resourcesImpl.updateConfiguration(config, dm, compat);
+ }
+ }
+
/**
* Appends the library asset path to any ResourcesImpl object that contains the main
* assetPath.
@@ -1120,7 +1300,7 @@ public class ResourcesManager {
ArrayUtils.appendElement(String.class, newLibAssets, libAsset);
}
- if (newLibAssets != key.mLibDirs) {
+ if (!Arrays.equals(newLibAssets, key.mLibDirs)) {
updatedResourceKeys.put(impl, new ResourcesKey(
key.mResDir,
key.mSplitResDirs,
@@ -1133,10 +1313,106 @@ public class ResourcesManager {
}
}
+ final int count = mResourcesWithLoaders.size();
+ for (int index = 0; index < count; index++) {
+ ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(index);
+ Resources resources = resourcesWithLoaders.resources();
+ if (resources == null) {
+ continue;
+ }
+
+ ResourcesKey key = resourcesWithLoaders.resourcesKey();
+ if (Objects.equals(key.mResDir, assetPath)) {
+ String[] newLibAssets = key.mLibDirs;
+ for (String libAsset : libAssets) {
+ newLibAssets =
+ ArrayUtils.appendElement(String.class, newLibAssets, libAsset);
+ }
+
+ if (!Arrays.equals(newLibAssets, key.mLibDirs)) {
+ updatedResourceKeys.put(resources.getImpl(),
+ new ResourcesKey(
+ key.mResDir,
+ key.mSplitResDirs,
+ key.mOverlayDirs,
+ newLibAssets,
+ key.mDisplayId,
+ key.mOverrideConfiguration,
+ key.mCompatInfo));
+ }
+ }
+ }
+
redirectResourcesToNewImplLocked(updatedResourceKeys);
}
}
+ /**
+ * Mark a {@link Resources} as containing a {@link ResourceLoader}.
+ *
+ * This removes its {@link ResourcesImpl} from the shared pool and creates it a new one. It is
+ * then tracked separately, kept unique, and restored properly across {@link Activity}
+ * recreations.
+ */
+ public void registerForLoaders(Resources resources) {
+ synchronized (this) {
+ boolean found = false;
+ IBinder activityToken = null;
+
+ // Remove the Resources from the reference list as it's now tracked separately
+ int size = mResourceReferences.size();
+ for (int index = 0; index < size; index++) {
+ WeakReference<Resources> reference = mResourceReferences.get(index);
+ if (reference.get() == resources) {
+ mResourceReferences.remove(index);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ // Do the same removal for any Activity Resources
+ for (Map.Entry<IBinder, ActivityResources> entry :
+ mActivityResourceReferences.entrySet()) {
+ ArrayList<WeakReference<Resources>> activityResourcesList =
+ entry.getValue().activityResources;
+ final int resCount = activityResourcesList.size();
+ for (int index = 0; index < resCount; index++) {
+ WeakReference<Resources> reference = activityResourcesList.get(index);
+ if (reference.get() == resources) {
+ activityToken = entry.getKey();
+ activityResourcesList.remove(index);
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ break;
+ }
+ }
+ }
+
+ if (!found) {
+ throw new IllegalArgumentException("Resources " + resources
+ + " registered for loaders but was not previously tracked by"
+ + " ResourcesManager");
+ }
+
+ ResourcesKey key = findKeyForResourceImplLocked(resources.getImpl());
+ ResourcesImpl impl = createResourcesImpl(key);
+
+ if (mResourcesWithLoaders == Collections.EMPTY_LIST) {
+ mResourcesWithLoaders = Collections.synchronizedList(new ArrayList<>());
+ }
+
+ mResourcesWithLoaders.add(new ResourcesWithLoaders(resources, key, activityToken));
+
+ // Set the new Impl, which is now guaranteed to be unique per Resources object
+ resources.setImpl(impl);
+ }
+ }
+
// TODO(adamlesinski): Make this accept more than just overlay directories.
final void applyNewResourceDirsLocked(@NonNull final ApplicationInfo appInfo,
@Nullable final String[] oldPaths) {
@@ -1181,6 +1457,32 @@ public class ResourcesManager {
}
}
+ final int count = mResourcesWithLoaders.size();
+ for (int index = 0; index < count; index++) {
+ ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(index);
+ Resources resources = resourcesWithLoaders.resources();
+ if (resources == null) {
+ continue;
+ }
+
+ ResourcesKey key = resourcesWithLoaders.resourcesKey();
+
+ if (key.mResDir == null
+ || key.mResDir.equals(baseCodePath)
+ || ArrayUtils.contains(oldPaths, key.mResDir)) {
+ updatedResourceKeys.put(resources.getImpl(),
+ new ResourcesKey(
+ baseCodePath,
+ copiedSplitDirs,
+ copiedResourceDirs,
+ key.mLibDirs,
+ key.mDisplayId,
+ key.mOverrideConfiguration,
+ key.mCompatInfo
+ ));
+ }
+ }
+
redirectResourcesToNewImplLocked(updatedResourceKeys);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
@@ -1230,5 +1532,25 @@ public class ResourcesManager {
}
}
}
+
+ // Update any references that need to be re-built with loaders. These are intentionally not
+ // part of either of the above lists.
+ final int loaderCount = mResourcesWithLoaders.size();
+ for (int index = loaderCount - 1; index >= 0; index--) {
+ ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(index);
+ Resources resources = resourcesWithLoaders.resources();
+ if (resources == null) {
+ continue;
+ }
+
+ ResourcesKey newKey = updatedResourceKeys.get(resources.getImpl());
+ if (newKey == null) {
+ continue;
+ }
+
+ resourcesWithLoaders.updateKey(newKey);
+ resourcesWithLoaders.resources()
+ .setImpl(createResourcesImpl(newKey));
+ }
}
}
diff --git a/core/java/android/app/SyncNotedAppOp.java b/core/java/android/app/SyncNotedAppOp.java
index f7b83d409a02..065d5de368ae 100644
--- a/core/java/android/app/SyncNotedAppOp.java
+++ b/core/java/android/app/SyncNotedAppOp.java
@@ -18,6 +18,7 @@ package android.app;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import com.android.internal.annotations.Immutable;
@@ -33,6 +34,7 @@ import com.android.internal.annotations.Immutable;
@Immutable
public final class SyncNotedAppOp {
private final int mOpCode;
+ private final @Nullable String mFeatureId;
/**
* @return The op that was noted.
@@ -42,14 +44,23 @@ public final class SyncNotedAppOp {
}
/**
+ * @return The {@link android.content.Context#createFeatureContext Feature} in the app
+ */
+ public @Nullable String getFeatureId() {
+ return mFeatureId;
+ }
+
+ /**
* Create a new sync op description
*
* @param opCode The op that was noted
*
* @hide
*/
- public SyncNotedAppOp(@IntRange(from = 0, to = AppOpsManager._NUM_OP - 1) int opCode) {
+ public SyncNotedAppOp(@IntRange(from = 0, to = AppOpsManager._NUM_OP - 1) int opCode,
+ @Nullable String featureId) {
mOpCode = opCode;
+ mFeatureId = featureId;
}
@Override
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index e4fd5665d318..8350fa13b097 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -152,6 +152,7 @@ import android.os.health.SystemHealthManager;
import android.os.image.DynamicSystemManager;
import android.os.image.IDynamicSystemService;
import android.os.storage.StorageManager;
+import android.telephony.TelephonyRegistryManager;
import android.permission.PermissionControllerManager;
import android.permission.PermissionManager;
import android.print.IPrintManager;
@@ -606,6 +607,13 @@ public final class SystemServiceRegistry {
return new TelephonyManager(ctx.getOuterContext());
}});
+ registerService(Context.TELEPHONY_REGISTRY_SERVICE, TelephonyRegistryManager.class,
+ new CachedServiceFetcher<TelephonyRegistryManager>() {
+ @Override
+ public TelephonyRegistryManager createService(ContextImpl ctx) {
+ return new TelephonyRegistryManager(ctx);
+ }});
+
registerService(Context.TELEPHONY_SUBSCRIPTION_SERVICE, SubscriptionManager.class,
new CachedServiceFetcher<SubscriptionManager>() {
@Override
@@ -1335,13 +1343,13 @@ public final class SystemServiceRegistry {
* @hide
*/
public static <T> void registerCachedService(String serviceName, Class<T> serviceWrapperClass,
- BiFunction<ContextImpl, IBinder, T> serviceFetcher) {
+ BiFunction<Context, IBinder, T> serviceFetcher) {
registerService(serviceName, serviceWrapperClass,
new CachedServiceFetcher<T>() {
@Override
public T createService(ContextImpl ctx) throws ServiceNotFoundException {
IBinder b = ServiceManager.getServiceOrThrow(serviceName);
- return serviceFetcher.apply(ctx, b);
+ return serviceFetcher.apply(ctx.getOuterContext(), b);
}});
}
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index def1f457fb4a..35c710458486 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -26,6 +26,15 @@
"include-filter": "com.android.server.appop"
}
]
+ },
+ {
+ "file_patterns": ["(/|^)AppOpsManager.java"],
+ "name": "CtsPermission2TestCases",
+ "options": [
+ {
+ "include-filter": "android.permission2.cts.RuntimePermissionProperties"
+ }
+ ]
}
],
"postsubmit": [
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 46045faecbd4..f21aaf3867fd 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -190,4 +190,8 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub {
@Override
public void onRecentTaskListUpdated() throws RemoteException {
}
+
+ @Override
+ public void onRecentTaskListFrozenChanged(boolean frozen) {
+ }
}
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index fd93450c29f0..13d566c0e04b 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -42,6 +42,7 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Log;
+import android.util.SparseArray;
import android.view.Display;
import android.view.InputEvent;
import android.view.KeyEvent;
@@ -535,7 +536,7 @@ public final class UiAutomation {
}
/**
- * Gets the windows on the screen. This method returns only the windows
+ * Gets the windows on the screen of the default display. This method returns only the windows
* that a sighted user can interact with, as opposed to all windows.
* For example, if there is a modal dialog shown and the user cannot touch
* anything behind it, then only the modal window will be reported
@@ -562,6 +563,35 @@ public final class UiAutomation {
}
/**
+ * Gets the windows on the screen of all displays. This method returns only the windows
+ * that a sighted user can interact with, as opposed to all windows.
+ * For example, if there is a modal dialog shown and the user cannot touch
+ * anything behind it, then only the modal window will be reported
+ * (assuming it is the top one). For convenience the returned windows
+ * are ordered in a descending layer order, which is the windows that
+ * are higher in the Z-order are reported first.
+ * <p>
+ * <strong>Note:</strong> In order to access the windows you have to opt-in
+ * to retrieve the interactive windows by setting the
+ * {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} flag.
+ * </p>
+ *
+ * @return The windows of all displays if there are windows and the service is can retrieve
+ * them, otherwise an empty list. The key of SparseArray is display ID.
+ */
+ @NonNull
+ public SparseArray<List<AccessibilityWindowInfo>> getWindowsOnAllDisplays() {
+ final int connectionId;
+ synchronized (mLock) {
+ throwIfNotConnectedLocked();
+ connectionId = mConnectionId;
+ }
+ // Calling out without a lock held.
+ return AccessibilityInteractionClient.getInstance()
+ .getWindowsOnAllDisplays(connectionId);
+ }
+
+ /**
* Gets the root {@link AccessibilityNodeInfo} in the active window.
*
* @return The root info.
diff --git a/core/java/android/app/admin/DelegatedAdminReceiver.java b/core/java/android/app/admin/DelegatedAdminReceiver.java
index f66de8d238ed..25b8eab452bf 100644
--- a/core/java/android/app/admin/DelegatedAdminReceiver.java
+++ b/core/java/android/app/admin/DelegatedAdminReceiver.java
@@ -63,6 +63,10 @@ public class DelegatedAdminReceiver extends BroadcastReceiver {
* Allows this receiver to select the alias for a private key and certificate pair for
* authentication. If this method returns null, the default {@link android.app.Activity} will
* be shown that lets the user pick a private key and certificate pair.
+ * If this method returns {@link KeyChain#KEY_ALIAS_SELECTION_DENIED},
+ * the default {@link android.app.Activity} will not be shown and the user will not be allowed
+ * to pick anything. And the app, that called {@link KeyChain#choosePrivateKeyAlias}, will
+ * receive {@code null} back.
*
* <p> This callback is only applicable if the delegated app has
* {@link DevicePolicyManager#DELEGATION_CERT_SELECTION} capability. Additionally, it must
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index 0c500da0294a..d175a66e90ea 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -791,6 +791,10 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* Allows this receiver to select the alias for a private key and certificate pair for
* authentication. If this method returns null, the default {@link android.app.Activity} will be
* shown that lets the user pick a private key and certificate pair.
+ * If this method returns {@link KeyChain#KEY_ALIAS_SELECTION_DENIED},
+ * the default {@link android.app.Activity} will not be shown and the user will not be allowed
+ * to pick anything. And the app, that called {@link KeyChain#choosePrivateKeyAlias}, will
+ * receive {@code null} back.
*
* @param context The running context as per {@link #onReceive}.
* @param intent The received intent as per {@link #onReceive}.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 02cac231f372..ad671dfcf80a 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1551,7 +1551,8 @@ public class DevicePolicyManager {
* scopes will be sent in an {@code ArrayList<String>} extra identified by the
* {@link #EXTRA_DELEGATION_SCOPES} key.
*
- * <p class=”note”> Note: This is a protected intent that can only be sent by the system.</p>
+ * <p class="note"><b>Note:</b> This is a protected intent that can only be sent by the
+ * system.</p>
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED =
@@ -2078,7 +2079,8 @@ public class DevicePolicyManager {
ID_TYPE_BASE_INFO,
ID_TYPE_SERIAL,
ID_TYPE_IMEI,
- ID_TYPE_MEID
+ ID_TYPE_MEID,
+ ID_TYPE_INDIVIDUAL_ATTESTATION
})
public @interface AttestationIdType {}
@@ -2113,6 +2115,14 @@ public class DevicePolicyManager {
public static final int ID_TYPE_MEID = 8;
/**
+ * Specifies that the device should attest using an individual attestation certificate.
+ * For use with {@link #generateKeyPair}.
+ *
+ * @see #generateKeyPair
+ */
+ public static final int ID_TYPE_INDIVIDUAL_ATTESTATION = 16;
+
+ /**
* Service-specific error code for {@link #generateKeyPair}:
* Indicates the call has failed due to StrongBox unavailability.
* @hide
@@ -2320,6 +2330,12 @@ public class DevicePolicyManager {
"android.app.action.ADMIN_POLICY_COMPLIANCE";
/**
+ * Maximum supported password length. Kind-of arbitrary.
+ * @hide
+ */
+ public static final int MAX_PASSWORD_LENGTH = 16;
+
+ /**
* Return true if the given administrator component is currently active (enabled) in the system.
*
* @param admin The administrator component to check for.
@@ -2609,6 +2625,7 @@ public class DevicePolicyManager {
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param quality The new desired quality. One of {@link #PASSWORD_QUALITY_UNSPECIFIED},
+ * {@link #PASSWORD_QUALITY_BIOMETRIC_WEAK},
* {@link #PASSWORD_QUALITY_SOMETHING}, {@link #PASSWORD_QUALITY_NUMERIC},
* {@link #PASSWORD_QUALITY_NUMERIC_COMPLEX}, {@link #PASSWORD_QUALITY_ALPHABETIC},
* {@link #PASSWORD_QUALITY_ALPHANUMERIC} or {@link #PASSWORD_QUALITY_COMPLEX}.
@@ -2667,7 +2684,10 @@ public class DevicePolicyManager {
* only imposed if the administrator has also requested either {@link #PASSWORD_QUALITY_NUMERIC}
* , {@link #PASSWORD_QUALITY_NUMERIC_COMPLEX}, {@link #PASSWORD_QUALITY_ALPHABETIC},
* {@link #PASSWORD_QUALITY_ALPHANUMERIC}, or {@link #PASSWORD_QUALITY_COMPLEX} with
- * {@link #setPasswordQuality}.
+ * {@link #setPasswordQuality}. If an app targeting SDK level
+ * {@link android.os.Build.VERSION_CODES#R} and above enforces this constraint without settings
+ * password quality to one of these values first, this method will throw
+ * {@link IllegalStateException}.
* <p>
* On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
* password is always treated as empty.
@@ -2682,9 +2702,12 @@ public class DevicePolicyManager {
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param length The new desired minimum password length. A value of 0 means there is no
- * restriction.
+ * restriction.
* @throws SecurityException if {@code admin} is not an active administrator or {@code admin}
- * does not use {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD}
+ * does not use {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD}
+ * @throws IllegalStateException if the calling app is targeting SDK level
+ * {@link android.os.Build.VERSION_CODES#R} and above and didn't set a sufficient password
+ * quality requirement prior to calling this method.
*/
public void setPasswordMinimumLength(@NonNull ComponentName admin, int length) {
if (mService != null) {
@@ -2736,7 +2759,10 @@ public class DevicePolicyManager {
* place immediately. To prompt the user for a new password, use
* {@link #ACTION_SET_NEW_PASSWORD} or {@link #ACTION_SET_NEW_PARENT_PROFILE_PASSWORD} after
* setting this value. This constraint is only imposed if the administrator has also requested
- * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The default value is 0.
+ * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. If an app targeting
+ * SDK level {@link android.os.Build.VERSION_CODES#R} and above enforces this constraint without
+ * settings password quality to {@link #PASSWORD_QUALITY_COMPLEX} first, this method will throw
+ * {@link IllegalStateException}. The default value is 0.
* <p>
* On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
* password is always treated as empty.
@@ -2754,6 +2780,9 @@ public class DevicePolicyManager {
* A value of 0 means there is no restriction.
* @throws SecurityException if {@code admin} is not an active administrator or {@code admin}
* does not use {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD}
+ * @throws IllegalStateException if the calling app is targeting SDK level
+ * {@link android.os.Build.VERSION_CODES#R} and above and didn't set a sufficient password
+ * quality requirement prior to calling this method.
*/
public void setPasswordMinimumUpperCase(@NonNull ComponentName admin, int length) {
if (mService != null) {
@@ -2812,7 +2841,10 @@ public class DevicePolicyManager {
* place immediately. To prompt the user for a new password, use
* {@link #ACTION_SET_NEW_PASSWORD} or {@link #ACTION_SET_NEW_PARENT_PROFILE_PASSWORD} after
* setting this value. This constraint is only imposed if the administrator has also requested
- * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The default value is 0.
+ * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. If an app targeting
+ * SDK level {@link android.os.Build.VERSION_CODES#R} and above enforces this constraint without
+ * settings password quality to {@link #PASSWORD_QUALITY_COMPLEX} first, this method will throw
+ * {@link IllegalStateException}. The default value is 0.
* <p>
* On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
* password is always treated as empty.
@@ -2830,6 +2862,9 @@ public class DevicePolicyManager {
* A value of 0 means there is no restriction.
* @throws SecurityException if {@code admin} is not an active administrator or {@code admin}
* does not use {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD}
+ * @throws IllegalStateException if the calling app is targeting SDK level
+ * {@link android.os.Build.VERSION_CODES#R} and above and didn't set a sufficient password
+ * quality requirement prior to calling this method.
*/
public void setPasswordMinimumLowerCase(@NonNull ComponentName admin, int length) {
if (mService != null) {
@@ -2888,7 +2923,10 @@ public class DevicePolicyManager {
* immediately. To prompt the user for a new password, use {@link #ACTION_SET_NEW_PASSWORD} or
* {@link #ACTION_SET_NEW_PARENT_PROFILE_PASSWORD} after setting this value. This constraint is
* only imposed if the administrator has also requested {@link #PASSWORD_QUALITY_COMPLEX} with
- * {@link #setPasswordQuality}. The default value is 1.
+ * {@link #setPasswordQuality}. If an app targeting SDK level
+ * {@link android.os.Build.VERSION_CODES#R} and above enforces this constraint without settings
+ * password quality to {@link #PASSWORD_QUALITY_COMPLEX} first, this method will throw
+ * {@link IllegalStateException}. The default value is 1.
* <p>
* On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
* password is always treated as empty.
@@ -2906,6 +2944,9 @@ public class DevicePolicyManager {
* 0 means there is no restriction.
* @throws SecurityException if {@code admin} is not an active administrator or {@code admin}
* does not use {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD}
+ * @throws IllegalStateException if the calling app is targeting SDK level
+ * {@link android.os.Build.VERSION_CODES#R} and above and didn't set a sufficient password
+ * quality requirement prior to calling this method.
*/
public void setPasswordMinimumLetters(@NonNull ComponentName admin, int length) {
if (mService != null) {
@@ -2963,7 +3004,10 @@ public class DevicePolicyManager {
* place immediately. To prompt the user for a new password, use
* {@link #ACTION_SET_NEW_PASSWORD} or {@link #ACTION_SET_NEW_PARENT_PROFILE_PASSWORD} after
* setting this value. This constraint is only imposed if the administrator has also requested
- * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The default value is 1.
+ * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. If an app targeting
+ * SDK level {@link android.os.Build.VERSION_CODES#R} and above enforces this constraint without
+ * settings password quality to {@link #PASSWORD_QUALITY_COMPLEX} first, this method will throw
+ * {@link IllegalStateException}. The default value is 1.
* <p>
* On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
* password is always treated as empty.
@@ -2981,6 +3025,9 @@ public class DevicePolicyManager {
* value of 0 means there is no restriction.
* @throws SecurityException if {@code admin} is not an active administrator or {@code admin}
* does not use {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD}
+ * @throws IllegalStateException if the calling app is targeting SDK level
+ * {@link android.os.Build.VERSION_CODES#R} and above and didn't set a sufficient password
+ * quality requirement prior to calling this method.
*/
public void setPasswordMinimumNumeric(@NonNull ComponentName admin, int length) {
if (mService != null) {
@@ -3038,7 +3085,10 @@ public class DevicePolicyManager {
* immediately. To prompt the user for a new password, use {@link #ACTION_SET_NEW_PASSWORD} or
* {@link #ACTION_SET_NEW_PARENT_PROFILE_PASSWORD} after setting this value. This constraint is
* only imposed if the administrator has also requested {@link #PASSWORD_QUALITY_COMPLEX} with
- * {@link #setPasswordQuality}. The default value is 1.
+ * {@link #setPasswordQuality}. If an app targeting SDK level
+ * {@link android.os.Build.VERSION_CODES#R} and above enforces this constraint without settings
+ * password quality to {@link #PASSWORD_QUALITY_COMPLEX} first, this method will throw
+ * {@link IllegalStateException}. The default value is 1.
* <p>
* On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
* password is always treated as empty.
@@ -3056,6 +3106,9 @@ public class DevicePolicyManager {
* 0 means there is no restriction.
* @throws SecurityException if {@code admin} is not an active administrator or {@code admin}
* does not use {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD}
+ * @throws IllegalStateException if the calling app is targeting SDK level
+ * {@link android.os.Build.VERSION_CODES#R} and above and didn't set a sufficient password
+ * quality requirement prior to calling this method.
*/
public void setPasswordMinimumSymbols(@NonNull ComponentName admin, int length) {
if (mService != null) {
@@ -3112,7 +3165,10 @@ public class DevicePolicyManager {
* one, so the change does not take place immediately. To prompt the user for a new password,
* use {@link #ACTION_SET_NEW_PASSWORD} or {@link #ACTION_SET_NEW_PARENT_PROFILE_PASSWORD} after
* setting this value. This constraint is only imposed if the administrator has also requested
- * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The default value is 0.
+ * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. If an app targeting
+ * SDK level {@link android.os.Build.VERSION_CODES#R} and above enforces this constraint without
+ * settings password quality to {@link #PASSWORD_QUALITY_COMPLEX} first, this method will throw
+ * {@link IllegalStateException}. The default value is 0.
* <p>
* On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
* password is always treated as empty.
@@ -3130,6 +3186,9 @@ public class DevicePolicyManager {
* 0 means there is no restriction.
* @throws SecurityException if {@code admin} is not an active administrator or {@code admin}
* does not use {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD}
+ * @throws IllegalStateException if the calling app is targeting SDK level
+ * {@link android.os.Build.VERSION_CODES#R} and above and didn't set a sufficient password
+ * quality requirement prior to calling this method.
*/
public void setPasswordMinimumNonLetter(@NonNull ComponentName admin, int length) {
if (mService != null) {
@@ -3180,6 +3239,22 @@ public class DevicePolicyManager {
}
/**
+ * Returns minimum PasswordMetrics that satisfies all admin policies.
+ *
+ * @hide
+ */
+ public PasswordMetrics getPasswordMinimumMetrics(@UserIdInt int userHandle) {
+ if (mService != null) {
+ try {
+ return mService.getPasswordMinimumMetrics(userHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return null;
+ }
+
+ /**
* Called by an application that is administering the device to set the length of the password
* history. After setting this, the user will not be able to enter a new password that is the
* same as any password in the history. Note that the current password will remain until the
@@ -3362,8 +3437,7 @@ public class DevicePolicyManager {
if (!pm.hasSystemFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)) {
return 0;
}
- // Kind-of arbitrary.
- return 16;
+ return MAX_PASSWORD_LENGTH;
}
/**
@@ -4890,24 +4964,47 @@ public class DevicePolicyManager {
* have been given to access the key and certificates associated with this alias will be
* revoked.
*
+ * <p>Attestation: to enable attestation, set an attestation challenge in {@code keySpec} via
+ * {@link KeyGenParameterSpec.Builder#setAttestationChallenge}. By specifying flags to the
+ * {@code idAttestationFlags} parameter, it is possible to request the device's unique
+ * identity to be included in the attestation record.
+ *
+ * <p>Specific identifiers can be included in the attestation record, and an individual
+ * attestation certificate can be used to sign the attestation record. To find out if the device
+ * supports these features, refer to {@link #isDeviceIdAttestationSupported()} and
+ * {@link #isUniqueDeviceAttestationSupported()}.
+ *
+ * <p>Device owner, profile owner and their delegated certificate installer can use
+ * {@link #ID_TYPE_BASE_INFO} to request inclusion of the general device information
+ * including manufacturer, model, brand, device and product in the attestation record.
+ * Only device owner and their delegated certificate installer can use
+ * {@link #ID_TYPE_SERIAL}, {@link #ID_TYPE_IMEI} and {@link #ID_TYPE_MEID} to request
+ * unique device identifiers to be attested (the serial number, IMEI and MEID correspondingly),
+ * if supported by the device (see {@link #isDeviceIdAttestationSupported()}).
+ * Additionally, device owner and their delegated certificate installer can also request the
+ * attestation record to be signed using an individual attestation certificate by specifying
+ * the {@link #ID_TYPE_INDIVIDUAL_ATTESTATION} flag (if supported by the device, see
+ * {@link #isUniqueDeviceAttestationSupported()}).
+ * <p>
+ * If any of {@link #ID_TYPE_SERIAL}, {@link #ID_TYPE_IMEI} and {@link #ID_TYPE_MEID}
+ * is set, it is implicitly assumed that {@link #ID_TYPE_BASE_INFO} is also set.
+ * <p>
+ * Attestation using {@link #ID_TYPE_INDIVIDUAL_ATTESTATION} can only be requested if
+ * key generation is done in StrongBox.
+ *
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
* {@code null} if calling from a delegated certificate installer.
* @param algorithm The key generation algorithm, see {@link java.security.KeyPairGenerator}.
* @param keySpec Specification of the key to generate, see
* {@link java.security.KeyPairGenerator}.
- * @param idAttestationFlags A bitmask of all the identifiers that should be included in the
+ * @param idAttestationFlags A bitmask of the identifiers that should be included in the
* attestation record ({@code ID_TYPE_BASE_INFO}, {@code ID_TYPE_SERIAL},
- * {@code ID_TYPE_IMEI} and {@code ID_TYPE_MEID}), or {@code 0} if no device
- * identification is required in the attestation record.
- * Device owner, profile owner and their delegated certificate installer can use
- * {@link #ID_TYPE_BASE_INFO} to request inclusion of the general device information
- * including manufacturer, model, brand, device and product in the attestation record.
- * Only device owner and their delegated certificate installer can use
- * {@link #ID_TYPE_SERIAL}, {@link #ID_TYPE_IMEI} and {@link #ID_TYPE_MEID} to request
- * unique device identifiers to be attested.
+ * {@code ID_TYPE_IMEI} and {@code ID_TYPE_MEID}), and
+ * {@code ID_TYPE_INDIVIDUAL_ATTESTATION} if the attestation record should be signed
+ * using an individual attestation certificate.
* <p>
- * If any of {@link #ID_TYPE_SERIAL}, {@link #ID_TYPE_IMEI} and {@link #ID_TYPE_MEID}
- * is set, it is implicitly assumed that {@link #ID_TYPE_BASE_INFO} is also set.
+ * {@code 0} should be passed in if no device identification is required in the
+ * attestation record and the batch attestation certificate should be used.
* <p>
* If any flag is specified, then an attestation challenge must be included in the
* {@code keySpec}.
@@ -5049,7 +5146,8 @@ public class DevicePolicyManager {
/**
* Returns {@code true} if the device supports attestation of device identifiers in addition
- * to key attestation.
+ * to key attestation. See
+ * {@link #generateKeyPair(ComponentName, String, KeyGenParameterSpec, int)}
* @return {@code true} if Device ID attestation is supported.
*/
public boolean isDeviceIdAttestationSupported() {
@@ -5058,6 +5156,20 @@ public class DevicePolicyManager {
}
/**
+ * Returns {@code true} if the StrongBox Keymaster implementation on the device was provisioned
+ * with an individual attestation certificate and can sign attestation records using it (as
+ * attestation using an individual attestation certificate is a feature only Keymaster
+ * implementations with StrongBox security level can implement).
+ * For use prior to calling
+ * {@link #generateKeyPair(ComponentName, String, KeyGenParameterSpec, int)}.
+ * @return {@code true} if individual attestation is supported.
+ */
+ public boolean isUniqueDeviceAttestationSupported() {
+ PackageManager pm = mContext.getPackageManager();
+ return pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_UNIQUE_ATTESTATION);
+ }
+
+ /**
* Called by a device or profile owner, or delegated certificate installer, to associate
* certificates with a key pair that was generated using {@link #generateKeyPair}, and
* set whether the key is available for the user to choose in the certificate selection
@@ -6521,6 +6633,8 @@ public class DevicePolicyManager {
* The calling device admin must be a profile owner or device owner. If it is not, a security
* exception will be thrown.
*
+ * <p>NOTE: Performs disk I/O and shouldn't be called on the main thread.
+ *
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param filter The IntentFilter for which a default handler is added.
* @param activity The Activity that is added as default intent handler.
@@ -7091,7 +7205,8 @@ public class DevicePolicyManager {
* used, calling with an empty list only allows the built-in system services. Any non-system
* accessibility service that's currently enabled must be included in the list.
* <p>
- * System accessibility services are always available to the user the list can't modify this.
+ * System accessibility services are always available to the user and this method can't
+ * disable them.
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param packageNames List of accessibility service package names.
* @return {@code true} if the operation succeeded, or {@code false} if the list didn't
@@ -8236,6 +8351,24 @@ public class DevicePolicyManager {
}
/**
+ * Called by device owners to set the user's master location setting.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with
+ * @param locationEnabled whether location should be enabled or disabled
+ * @throws SecurityException if {@code admin} is not a device owner.
+ */
+ public void setLocationEnabled(@NonNull ComponentName admin, boolean locationEnabled) {
+ throwIfParentInstance("setLocationEnabled");
+ if (mService != null) {
+ try {
+ mService.setLocationEnabled(admin, locationEnabled);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
* Called by profile or device owners to update {@link android.provider.Settings.Secure}
* settings. Validation that the value of the setting is in the correct form for the setting
* type should be performed by the caller.
@@ -8264,6 +8397,11 @@ public class DevicePolicyManager {
* all users.
* </strong>
*
+ * <strong>Note: Starting from Android R, apps should no longer call this method with the
+ * setting {@link android.provider.Settings.Secure#LOCATION_MODE}, which is deprecated. Instead,
+ * device owners should call {@link #setLocationEnabled(ComponentName, boolean)}.
+ * </strong>
+ *
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param setting The name of the setting to update.
* @param value The value to update the setting to.
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 8765760b216b..713126ee9341 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -24,6 +24,10 @@ import java.util.List;
/**
* Device policy manager local system service interface.
*
+ * Maintenance note: if you need to expose information from DPMS to lower level services such as
+ * PM/UM/AM/etc, then exposing it from DevicePolicyManagerInternal is not safe because it may cause
+ * lock order inversion. Consider using {@link DevicePolicyCache} instead.
+ *
* @hide Only for use within the system server.
*/
public abstract class DevicePolicyManagerInternal {
@@ -81,6 +85,16 @@ public abstract class DevicePolicyManagerInternal {
public abstract boolean isActiveAdminWithPolicy(int uid, int reqPolicy);
/**
+ * Checks if an app with given uid is the active supervision admin.
+ *
+ * <p>This takes the DPMS lock. DO NOT call from PM/UM/AM with their lock held.
+ *
+ * @param uid App uid.
+ * @return true if the uid is the active supervision app.
+ */
+ public abstract boolean isActiveSupervisionApp(int uid);
+
+ /**
* Creates an intent to show the admin support dialog to say that an action is disallowed by
* the device/profile owner.
*
@@ -153,4 +167,11 @@ public abstract class DevicePolicyManagerInternal {
* Do not call it directly. Use {@link DevicePolicyCache#getInstance()} instead.
*/
protected abstract DevicePolicyCache getDevicePolicyCache();
+
+ /**
+ * @return cached version of device state related to DPM that can be accessed without risking
+ * deadlocks.
+ * Do not call it directly. Use {@link DevicePolicyCache#getInstance()} instead.
+ */
+ protected abstract DeviceStateCache getDeviceStateCache();
}
diff --git a/core/java/android/app/admin/DeviceStateCache.java b/core/java/android/app/admin/DeviceStateCache.java
new file mode 100644
index 000000000000..7619aa2b7473
--- /dev/null
+++ b/core/java/android/app/admin/DeviceStateCache.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.admin;
+
+import com.android.server.LocalServices;
+
+/**
+ * Stores a copy of the set of device state maintained by {@link DevicePolicyManager} which
+ * is not directly related to admin policies. This lives in its own class so that the state
+ * can be accessed from any place without risking dead locks.
+ *
+ * @hide
+ */
+public abstract class DeviceStateCache {
+ protected DeviceStateCache() {
+ }
+
+ /**
+ * @return the instance.
+ */
+ public static DeviceStateCache getInstance() {
+ final DevicePolicyManagerInternal dpmi =
+ LocalServices.getService(DevicePolicyManagerInternal.class);
+ return (dpmi != null) ? dpmi.getDeviceStateCache() : EmptyDeviceStateCache.INSTANCE;
+ }
+
+ /**
+ * See {@link DevicePolicyManager#isDeviceProvisioned}
+ */
+ public abstract boolean isDeviceProvisioned();
+
+ /**
+ * Empty implementation.
+ */
+ private static class EmptyDeviceStateCache extends DeviceStateCache {
+ private static final EmptyDeviceStateCache INSTANCE = new EmptyDeviceStateCache();
+
+ @Override
+ public boolean isDeviceProvisioned() {
+ return false;
+ }
+ }
+}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 0da5b7a1cf62..6b505223163c 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -72,6 +72,8 @@ interface IDevicePolicyManager {
void setPasswordMinimumNonLetter(in ComponentName who, int length, boolean parent);
int getPasswordMinimumNonLetter(in ComponentName who, int userHandle, boolean parent);
+ PasswordMetrics getPasswordMinimumMetrics(int userHandle);
+
void setPasswordHistoryLength(in ComponentName who, int length, boolean parent);
int getPasswordHistoryLength(in ComponentName who, int userHandle, boolean parent);
@@ -256,6 +258,8 @@ interface IDevicePolicyManager {
void setSystemSetting(in ComponentName who, in String setting, in String value);
void setSecureSetting(in ComponentName who, in String setting, in String value);
+ void setLocationEnabled(in ComponentName who, boolean locationEnabled);
+
boolean setTime(in ComponentName who, long millis);
boolean setTimeZone(in ComponentName who, String timeZone);
diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java
index 992985528fca..0ecfca74b26e 100644
--- a/core/java/android/app/admin/PasswordMetrics.java
+++ b/core/java/android/app/admin/PasswordMetrics.java
@@ -16,45 +16,70 @@
package android.app.admin;
+import static android.app.admin.DevicePolicyManager.MAX_PASSWORD_LENGTH;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+import static com.android.internal.widget.LockPatternUtils.MIN_LOCK_PASSWORD_SIZE;
+import static com.android.internal.widget.PasswordValidationError.CONTAINS_INVALID_CHARACTERS;
+import static com.android.internal.widget.PasswordValidationError.CONTAINS_SEQUENCE;
+import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_DIGITS;
+import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_LETTERS;
+import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_LOWER_CASE;
+import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_NON_DIGITS;
+import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_NON_LETTER;
+import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_SYMBOLS;
+import static com.android.internal.widget.PasswordValidationError.NOT_ENOUGH_UPPER_CASE;
+import static com.android.internal.widget.PasswordValidationError.TOO_LONG;
+import static com.android.internal.widget.PasswordValidationError.TOO_SHORT;
+import static com.android.internal.widget.PasswordValidationError.WEAK_CREDENTIAL_TYPE;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Log;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
import com.android.internal.widget.LockPatternUtils.CredentialType;
+import com.android.internal.widget.LockscreenCredential;
+import com.android.internal.widget.PasswordValidationError;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
/**
* A class that represents the metrics of a credential that are used to decide whether or not a
- * credential meets the requirements. If the credential is a pattern, only quality matters.
+ * credential meets the requirements.
*
* {@hide}
*/
-public class PasswordMetrics implements Parcelable {
+public final class PasswordMetrics implements Parcelable {
+ private static final String TAG = "PasswordMetrics";
+
// Maximum allowed number of repeated or ordered characters in a sequence before we'll
// consider it a complex PIN/password.
public static final int MAX_ALLOWED_SEQUENCE = 3;
- public int quality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+ // One of CREDENTIAL_TYPE_NONE, CREDENTIAL_TYPE_PATTERN or CREDENTIAL_TYPE_PASSWORD.
+ // Note that this class still uses CREDENTIAL_TYPE_PASSWORD to represent both numeric PIN
+ // and alphabetic password. This is OK as long as this definition is only used internally,
+ // and the value never gets mixed up with credential types from other parts of the framework.
+ // TODO: fix this (ideally after we move logic to PasswordPolicy)
+ public @CredentialType int credType;
+ // Fields below only make sense when credType is PASSWORD.
public int length = 0;
public int letters = 0;
public int upperCase = 0;
@@ -62,139 +87,62 @@ public class PasswordMetrics implements Parcelable {
public int numeric = 0;
public int symbols = 0;
public int nonLetter = 0;
+ public int nonNumeric = 0;
+ // MAX_VALUE is the most relaxed value, any sequence is ok, e.g. 123456789. 4 would forbid it.
+ public int seqLength = Integer.MAX_VALUE;
- public PasswordMetrics() {}
-
- public PasswordMetrics(int quality) {
- this.quality = quality;
+ public PasswordMetrics(int credType) {
+ this.credType = credType;
}
- public PasswordMetrics(int quality, int length) {
- this.quality = quality;
+ public PasswordMetrics(int credType , int length, int letters, int upperCase, int lowerCase,
+ int numeric, int symbols, int nonLetter, int nonNumeric, int seqLength) {
+ this.credType = credType;
this.length = length;
- }
-
- public PasswordMetrics(int quality, int length, int letters, int upperCase, int lowerCase,
- int numeric, int symbols, int nonLetter) {
- this(quality, length);
this.letters = letters;
this.upperCase = upperCase;
this.lowerCase = lowerCase;
this.numeric = numeric;
this.symbols = symbols;
this.nonLetter = nonLetter;
+ this.nonNumeric = nonNumeric;
+ this.seqLength = seqLength;
}
- private PasswordMetrics(Parcel in) {
- quality = in.readInt();
- length = in.readInt();
- letters = in.readInt();
- upperCase = in.readInt();
- lowerCase = in.readInt();
- numeric = in.readInt();
- symbols = in.readInt();
- nonLetter = in.readInt();
- }
-
- /** Returns the min quality allowed by {@code complexityLevel}. */
- public static int complexityLevelToMinQuality(@PasswordComplexity int complexityLevel) {
- // this would be the quality of the first metrics since mMetrics is sorted in ascending
- // order of quality
- return PasswordComplexityBucket
- .complexityLevelToBucket(complexityLevel).mMetrics[0].quality;
- }
-
- /**
- * Returns a merged minimum {@link PasswordMetrics} requirements that a new password must meet
- * to fulfil {@code requestedQuality}, {@code requiresNumeric} and {@code
- * requiresLettersOrSymbols}, which are derived from {@link DevicePolicyManager} requirements,
- * and {@code complexityLevel}.
- *
- * <p>Note that we are taking {@code userEnteredPasswordQuality} into account because there are
- * more than one set of metrics to meet the minimum complexity requirement and inspecting what
- * the user has entered can help determine whether the alphabetic or alphanumeric set of metrics
- * should be used. For example, suppose minimum complexity requires either ALPHABETIC(8+), or
- * ALPHANUMERIC(6+). If the user has entered "a", the length requirement displayed on the UI
- * would be 8. Then the user appends "1" to make it "a1". We now know the user is entering
- * an alphanumeric password so we would update the min complexity required min length to 6.
- */
- public static PasswordMetrics getMinimumMetrics(@PasswordComplexity int complexityLevel,
- int userEnteredPasswordQuality, int requestedQuality, boolean requiresNumeric,
- boolean requiresLettersOrSymbols) {
- int targetQuality = Math.max(
- userEnteredPasswordQuality,
- getActualRequiredQuality(
- requestedQuality, requiresNumeric, requiresLettersOrSymbols));
- return getTargetQualityMetrics(complexityLevel, targetQuality);
- }
-
- /**
- * Returns the {@link PasswordMetrics} at {@code complexityLevel} which the metrics quality
- * is the same as {@code targetQuality}.
- *
- * <p>If {@code complexityLevel} does not allow {@code targetQuality}, returns the metrics
- * with the min quality at {@code complexityLevel}.
- */
- // TODO(bernardchau): update tests to test getMinimumMetrics and change this to be private
- @VisibleForTesting
- public static PasswordMetrics getTargetQualityMetrics(
- @PasswordComplexity int complexityLevel, int targetQuality) {
- PasswordComplexityBucket targetBucket =
- PasswordComplexityBucket.complexityLevelToBucket(complexityLevel);
- for (PasswordMetrics metrics : targetBucket.mMetrics) {
- if (targetQuality == metrics.quality) {
- return metrics;
- }
- }
- // none of the metrics at complexityLevel has targetQuality, return metrics with min quality
- // see test case testGetMinimumMetrics_actualRequiredQualityStricter for an example, where
- // min complexity allows at least NUMERIC_COMPLEX, user has not entered anything yet, and
- // requested quality is NUMERIC
- return targetBucket.mMetrics[0];
- }
-
- /**
- * Finds out the actual quality requirement based on whether quality is {@link
- * DevicePolicyManager#PASSWORD_QUALITY_COMPLEX} and whether digits, letters or symbols are
- * required.
- */
- @VisibleForTesting
- // TODO(bernardchau): update tests to test getMinimumMetrics and change this to be private
- public static int getActualRequiredQuality(
- int requestedQuality, boolean requiresNumeric, boolean requiresLettersOrSymbols) {
- if (requestedQuality != PASSWORD_QUALITY_COMPLEX) {
- return requestedQuality;
- }
-
- // find out actual password quality from complex requirements
- if (requiresNumeric && requiresLettersOrSymbols) {
- return PASSWORD_QUALITY_ALPHANUMERIC;
- }
- if (requiresLettersOrSymbols) {
- return PASSWORD_QUALITY_ALPHABETIC;
- }
- if (requiresNumeric) {
- // cannot specify numeric complex using complex quality so this must be numeric
- return PASSWORD_QUALITY_NUMERIC;
- }
-
- // reaching here means dpm sets quality to complex without specifying any requirements
- return PASSWORD_QUALITY_UNSPECIFIED;
+ private PasswordMetrics(PasswordMetrics other) {
+ this(other.credType, other.length, other.letters, other.upperCase, other.lowerCase,
+ other.numeric, other.symbols, other.nonLetter, other.nonNumeric, other.seqLength);
}
/**
* Returns {@code complexityLevel} or {@link DevicePolicyManager#PASSWORD_COMPLEXITY_NONE}
* if {@code complexityLevel} is not valid.
+ *
+ * TODO: move to PasswordPolicy
*/
@PasswordComplexity
public static int sanitizeComplexityLevel(@PasswordComplexity int complexityLevel) {
- return PasswordComplexityBucket.complexityLevelToBucket(complexityLevel).mComplexityLevel;
+ switch (complexityLevel) {
+ case PASSWORD_COMPLEXITY_HIGH:
+ case PASSWORD_COMPLEXITY_MEDIUM:
+ case PASSWORD_COMPLEXITY_LOW:
+ case PASSWORD_COMPLEXITY_NONE:
+ return complexityLevel;
+ default:
+ Log.w(TAG, "Invalid password complexity used: " + complexityLevel);
+ return PASSWORD_COMPLEXITY_NONE;
+ }
}
- public boolean isDefault() {
- return quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED
- && length == 0 && letters == 0 && upperCase == 0 && lowerCase == 0
- && numeric == 0 && symbols == 0 && nonLetter == 0;
+ private static boolean hasInvalidCharacters(byte[] password) {
+ // Allow non-control Latin-1 characters only.
+ for (byte b : password) {
+ char c = (char) b;
+ if (c < 32 || c > 127) {
+ return true;
+ }
+ }
+ return false;
}
@Override
@@ -204,7 +152,7 @@ public class PasswordMetrics implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(quality);
+ dest.writeInt(credType);
dest.writeInt(length);
dest.writeInt(letters);
dest.writeInt(upperCase);
@@ -212,35 +160,50 @@ public class PasswordMetrics implements Parcelable {
dest.writeInt(numeric);
dest.writeInt(symbols);
dest.writeInt(nonLetter);
+ dest.writeInt(nonNumeric);
+ dest.writeInt(seqLength);
}
- public static final @android.annotation.NonNull Parcelable.Creator<PasswordMetrics> CREATOR
+ public static final @NonNull Parcelable.Creator<PasswordMetrics> CREATOR
= new Parcelable.Creator<PasswordMetrics>() {
- public PasswordMetrics createFromParcel(Parcel in) {
- return new PasswordMetrics(in);
- }
+ @Override
+ public PasswordMetrics createFromParcel(Parcel in) {
+ int credType = in.readInt();
+ int length = in.readInt();
+ int letters = in.readInt();
+ int upperCase = in.readInt();
+ int lowerCase = in.readInt();
+ int numeric = in.readInt();
+ int symbols = in.readInt();
+ int nonLetter = in.readInt();
+ int nonNumeric = in.readInt();
+ int seqLength = in.readInt();
+ return new PasswordMetrics(credType, length, letters, upperCase, lowerCase,
+ numeric, symbols, nonLetter, nonNumeric, seqLength);
+ }
- public PasswordMetrics[] newArray(int size) {
- return new PasswordMetrics[size];
- }
+ @Override
+ public PasswordMetrics[] newArray(int size) {
+ return new PasswordMetrics[size];
+ }
};
/**
- * Returnsthe {@code PasswordMetrics} for a given credential.
+ * Returns the {@code PasswordMetrics} for a given credential.
*
* If the credential is a pin or a password, equivalent to {@link #computeForPassword(byte[])}.
* {@code credential} cannot be null when {@code type} is
* {@link com.android.internal.widget.LockPatternUtils#CREDENTIAL_TYPE_PASSWORD}.
*/
- public static PasswordMetrics computeForCredential(
- @CredentialType int type, byte[] credential) {
- if (type == CREDENTIAL_TYPE_PASSWORD) {
- Preconditions.checkNotNull(credential, "credential cannot be null");
- return PasswordMetrics.computeForPassword(credential);
- } else if (type == CREDENTIAL_TYPE_PATTERN) {
- return new PasswordMetrics(PASSWORD_QUALITY_SOMETHING);
- } else /* if (type == CREDENTIAL_TYPE_NONE) */ {
- return new PasswordMetrics(PASSWORD_QUALITY_UNSPECIFIED);
+ public static PasswordMetrics computeForCredential(LockscreenCredential credential) {
+ if (credential.isPassword() || credential.isPin()) {
+ return PasswordMetrics.computeForPassword(credential.getCredential());
+ } else if (credential.isPattern()) {
+ return new PasswordMetrics(CREDENTIAL_TYPE_PATTERN);
+ } else if (credential.isNone()) {
+ return new PasswordMetrics(CREDENTIAL_TYPE_NONE);
+ } else {
+ throw new IllegalArgumentException("Unknown credential type " + credential.getType());
}
}
@@ -255,16 +218,19 @@ public class PasswordMetrics implements Parcelable {
int numeric = 0;
int symbols = 0;
int nonLetter = 0;
+ int nonNumeric = 0;
final int length = password.length;
for (byte b : password) {
switch (categoryChar((char) b)) {
case CHAR_LOWER_CASE:
letters++;
lowerCase++;
+ nonNumeric++;
break;
case CHAR_UPPER_CASE:
letters++;
upperCase++;
+ nonNumeric++;
break;
case CHAR_DIGIT:
numeric++;
@@ -273,53 +239,14 @@ public class PasswordMetrics implements Parcelable {
case CHAR_SYMBOL:
symbols++;
nonLetter++;
+ nonNumeric++;
break;
}
}
- // Determine the quality of the password
- final boolean hasNumeric = numeric > 0;
- final boolean hasNonNumeric = (letters + symbols) > 0;
- final int quality;
- if (hasNonNumeric && hasNumeric) {
- quality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
- } else if (hasNonNumeric) {
- quality = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
- } else if (hasNumeric) {
- quality = maxLengthSequence(password) > MAX_ALLOWED_SEQUENCE
- ? DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
- : DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
- } else {
- quality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
- }
-
- return new PasswordMetrics(
- quality, length, letters, upperCase, lowerCase, numeric, symbols, nonLetter);
- }
-
- @Override
- public boolean equals(Object other) {
- if (!(other instanceof PasswordMetrics)) {
- return false;
- }
- PasswordMetrics o = (PasswordMetrics) other;
- return this.quality == o.quality
- && this.length == o.length
- && this.letters == o.letters
- && this.upperCase == o.upperCase
- && this.lowerCase == o.lowerCase
- && this.numeric == o.numeric
- && this.symbols == o.symbols
- && this.nonLetter == o.nonLetter;
- }
-
- private boolean satisfiesBucket(PasswordMetrics... bucket) {
- for (PasswordMetrics metrics : bucket) {
- if (this.quality == metrics.quality) {
- return this.length >= metrics.length;
- }
- }
- return false;
+ final int seqLength = maxLengthSequence(password);
+ return new PasswordMetrics(CREDENTIAL_TYPE_PASSWORD, length, letters, upperCase, lowerCase,
+ numeric, symbols, nonLetter, nonNumeric, seqLength);
}
/**
@@ -404,108 +331,394 @@ public class PasswordMetrics implements Parcelable {
}
}
- /** Determines the {@link PasswordComplexity} of this {@link PasswordMetrics}. */
- @PasswordComplexity
- public int determineComplexity() {
- for (PasswordComplexityBucket bucket : PasswordComplexityBucket.BUCKETS) {
- if (satisfiesBucket(bucket.mMetrics)) {
- return bucket.mComplexityLevel;
- }
+ /**
+ * Returns the weakest metrics that is stricter or equal to all given metrics.
+ *
+ * TODO: move to PasswordPolicy
+ */
+ public static PasswordMetrics merge(List<PasswordMetrics> metrics) {
+ PasswordMetrics result = new PasswordMetrics(CREDENTIAL_TYPE_NONE);
+ for (PasswordMetrics m : metrics) {
+ result.maxWith(m);
}
- return PASSWORD_COMPLEXITY_NONE;
+
+ return result;
}
/**
- * Requirements in terms of {@link PasswordMetrics} for each {@link PasswordComplexity}.
+ * Makes current metric at least as strong as {@code other} in every criterion.
+ *
+ * TODO: move to PasswordPolicy
*/
- private static class PasswordComplexityBucket {
- /**
- * Definition of {@link DevicePolicyManager#PASSWORD_COMPLEXITY_HIGH} in terms of
- * {@link PasswordMetrics}.
- */
- private static final PasswordComplexityBucket HIGH =
- new PasswordComplexityBucket(
- PASSWORD_COMPLEXITY_HIGH,
- new PasswordMetrics(
- DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, /* length= */
- 8),
- new PasswordMetrics(
- DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, /* length= */ 6),
- new PasswordMetrics(
- DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, /* length= */
- 6));
-
- /**
- * Definition of {@link DevicePolicyManager#PASSWORD_COMPLEXITY_MEDIUM} in terms of
- * {@link PasswordMetrics}.
- */
- private static final PasswordComplexityBucket MEDIUM =
- new PasswordComplexityBucket(
- PASSWORD_COMPLEXITY_MEDIUM,
- new PasswordMetrics(
- DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, /* length= */
- 4),
- new PasswordMetrics(
- DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, /* length= */ 4),
- new PasswordMetrics(
- DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, /* length= */
- 4));
-
- /**
- * Definition of {@link DevicePolicyManager#PASSWORD_COMPLEXITY_LOW} in terms of
- * {@link PasswordMetrics}.
- */
- private static final PasswordComplexityBucket LOW =
- new PasswordComplexityBucket(
- PASSWORD_COMPLEXITY_LOW,
- new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING),
- new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC),
- new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX),
- new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC),
- new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC));
-
- /**
- * A special bucket to represent {@link DevicePolicyManager#PASSWORD_COMPLEXITY_NONE}.
- */
- private static final PasswordComplexityBucket NONE =
- new PasswordComplexityBucket(PASSWORD_COMPLEXITY_NONE, new PasswordMetrics());
-
- /** Array containing all buckets from high to low. */
- private static final PasswordComplexityBucket[] BUCKETS =
- new PasswordComplexityBucket[] {HIGH, MEDIUM, LOW};
-
- @PasswordComplexity
- private final int mComplexityLevel;
- private final PasswordMetrics[] mMetrics;
-
- /**
- * @param metricsArray must be sorted in ascending order of {@link #quality}.
- */
- private PasswordComplexityBucket(@PasswordComplexity int complexityLevel,
- PasswordMetrics... metricsArray) {
- int previousQuality = PASSWORD_QUALITY_UNSPECIFIED;
- for (PasswordMetrics metrics : metricsArray) {
- if (metrics.quality < previousQuality) {
- throw new IllegalArgumentException("metricsArray must be sorted in ascending"
- + " order of quality");
- }
- previousQuality = metrics.quality;
+ private void maxWith(PasswordMetrics other) {
+ credType = Math.max(credType, other.credType);
+ if (credType != CREDENTIAL_TYPE_PASSWORD) {
+ return;
+ }
+ length = Math.max(length, other.length);
+ letters = Math.max(letters, other.letters);
+ upperCase = Math.max(upperCase, other.upperCase);
+ lowerCase = Math.max(lowerCase, other.lowerCase);
+ numeric = Math.max(numeric, other.numeric);
+ symbols = Math.max(symbols, other.symbols);
+ nonLetter = Math.max(nonLetter, other.nonLetter);
+ nonNumeric = Math.max(nonNumeric, other.nonNumeric);
+ seqLength = Math.min(seqLength, other.seqLength);
+ }
+
+ /**
+ * Returns minimum password quality for a given complexity level.
+ *
+ * TODO: this function is used for determining allowed credential types, so it should return
+ * credential type rather than 'quality'.
+ *
+ * TODO: move to PasswordPolicy
+ */
+ public static int complexityLevelToMinQuality(int complexity) {
+ switch (complexity) {
+ case PASSWORD_COMPLEXITY_HIGH:
+ case PASSWORD_COMPLEXITY_MEDIUM:
+ return PASSWORD_QUALITY_NUMERIC_COMPLEX;
+ case PASSWORD_COMPLEXITY_LOW:
+ return PASSWORD_QUALITY_SOMETHING;
+ case PASSWORD_COMPLEXITY_NONE:
+ default:
+ return PASSWORD_QUALITY_UNSPECIFIED;
+ }
+ }
+
+ /**
+ * Enum representing requirements for each complexity level.
+ *
+ * TODO: move to PasswordPolicy
+ */
+ private enum ComplexityBucket {
+ // Keep ordered high -> low.
+ BUCKET_HIGH(PASSWORD_COMPLEXITY_HIGH) {
+ @Override
+ boolean canHaveSequence() {
+ return false;
}
- this.mMetrics = metricsArray;
- this.mComplexityLevel = complexityLevel;
+ @Override
+ int getMinimumLength(boolean containsNonNumeric) {
+ return containsNonNumeric ? 6 : 8;
+ }
+
+ @Override
+ boolean allowsNumericPassword() {
+ return false;
+ }
+
+ @Override
+ boolean allowsCredType(int credType) {
+ return credType == CREDENTIAL_TYPE_PASSWORD;
+ }
+ },
+ BUCKET_MEDIUM(PASSWORD_COMPLEXITY_MEDIUM) {
+ @Override
+ boolean canHaveSequence() {
+ return false;
+ }
+
+ @Override
+ int getMinimumLength(boolean containsNonNumeric) {
+ return 4;
+ }
+
+ @Override
+ boolean allowsNumericPassword() {
+ return false;
+ }
+
+ @Override
+ boolean allowsCredType(int credType) {
+ return credType == CREDENTIAL_TYPE_PASSWORD;
+ }
+ },
+ BUCKET_LOW(PASSWORD_COMPLEXITY_LOW) {
+ @Override
+ boolean canHaveSequence() {
+ return true;
+ }
+
+ @Override
+ int getMinimumLength(boolean containsNonNumeric) {
+ return 0;
+ }
+
+ @Override
+ boolean allowsNumericPassword() {
+ return true;
+ }
+
+ @Override
+ boolean allowsCredType(int credType) {
+ return credType != CREDENTIAL_TYPE_NONE;
+ }
+ },
+ BUCKET_NONE(PASSWORD_COMPLEXITY_NONE) {
+ @Override
+ boolean canHaveSequence() {
+ return true;
+ }
+
+ @Override
+ int getMinimumLength(boolean containsNonNumeric) {
+ return 0;
+ }
+
+ @Override
+ boolean allowsNumericPassword() {
+ return true;
+ }
+
+ @Override
+ boolean allowsCredType(int credType) {
+ return true;
+ }
+ };
+
+ int mComplexityLevel;
+ abstract boolean canHaveSequence();
+ abstract int getMinimumLength(boolean containsNonNumeric);
+ abstract boolean allowsNumericPassword();
+ abstract boolean allowsCredType(int credType);
+
+ ComplexityBucket(int complexityLevel) {
+ this.mComplexityLevel = complexityLevel;
}
- /** Returns the bucket that {@code complexityLevel} represents. */
- private static PasswordComplexityBucket complexityLevelToBucket(
- @PasswordComplexity int complexityLevel) {
- for (PasswordComplexityBucket bucket : BUCKETS) {
+ static ComplexityBucket forComplexity(int complexityLevel) {
+ for (ComplexityBucket bucket : values()) {
if (bucket.mComplexityLevel == complexityLevel) {
return bucket;
}
}
- return NONE;
+ throw new IllegalArgumentException("Invalid complexity level: " + complexityLevel);
+ }
+ }
+
+ /**
+ * Returns whether current metrics satisfies a given complexity bucket.
+ *
+ * TODO: move inside ComplexityBucket.
+ */
+ private boolean satisfiesBucket(ComplexityBucket bucket) {
+ if (!bucket.allowsCredType(credType)) {
+ return false;
+ }
+ if (credType != CREDENTIAL_TYPE_PASSWORD) {
+ return true;
+ }
+ return (bucket.canHaveSequence() || seqLength <= MAX_ALLOWED_SEQUENCE)
+ && length >= bucket.getMinimumLength(nonNumeric > 0 /* hasNonNumeric */);
+ }
+
+ /**
+ * Returns the maximum complexity level satisfied by password with this metrics.
+ *
+ * TODO: move inside ComplexityBucket.
+ */
+ public int determineComplexity() {
+ for (ComplexityBucket bucket : ComplexityBucket.values()) {
+ if (satisfiesBucket(bucket)) {
+ return bucket.mComplexityLevel;
+ }
+ }
+ throw new IllegalStateException("Failed to figure out complexity for a given metrics");
+ }
+
+ /**
+ * Validates password against minimum metrics and complexity.
+ *
+ * @param adminMetrics - minimum metrics to satisfy admin requirements.
+ * @param minComplexity - minimum complexity imposed by the requester.
+ * @param isPin - whether it is PIN that should be only digits
+ * @param password - password to validate.
+ * @return a list of password validation errors. An empty list means the password is OK.
+ *
+ * TODO: move to PasswordPolicy
+ */
+ public static List<PasswordValidationError> validatePassword(
+ PasswordMetrics adminMetrics, int minComplexity, boolean isPin, byte[] password) {
+
+ if (hasInvalidCharacters(password)) {
+ return Collections.singletonList(
+ new PasswordValidationError(CONTAINS_INVALID_CHARACTERS, 0));
+ }
+
+ final PasswordMetrics enteredMetrics = computeForPassword(password);
+ return validatePasswordMetrics(adminMetrics, minComplexity, isPin, enteredMetrics);
+ }
+
+ /**
+ * Validates password metrics against minimum metrics and complexity
+ *
+ * @param adminMetrics - minimum metrics to satisfy admin requirements.
+ * @param minComplexity - minimum complexity imposed by the requester.
+ * @param isPin - whether it is PIN that should be only digits
+ * @param actualMetrics - metrics for password to validate.
+ * @return a list of password validation errors. An empty list means the password is OK.
+ *
+ * TODO: move to PasswordPolicy
+ */
+ public static List<PasswordValidationError> validatePasswordMetrics(
+ PasswordMetrics adminMetrics, int minComplexity, boolean isPin,
+ PasswordMetrics actualMetrics) {
+ final ComplexityBucket bucket = ComplexityBucket.forComplexity(minComplexity);
+
+ // Make sure credential type is satisfactory.
+ // TODO: stop relying on credential type ordering.
+ if (actualMetrics.credType < adminMetrics.credType
+ || !bucket.allowsCredType(actualMetrics.credType)) {
+ return Collections.singletonList(new PasswordValidationError(WEAK_CREDENTIAL_TYPE, 0));
+ }
+ // TODO: this needs to be modified if CREDENTIAL_TYPE_PIN is added.
+ if (actualMetrics.credType != CREDENTIAL_TYPE_PASSWORD) {
+ return Collections.emptyList(); // Nothing to check for pattern or none.
}
+
+ if (isPin && actualMetrics.nonNumeric > 0) {
+ return Collections.singletonList(
+ new PasswordValidationError(CONTAINS_INVALID_CHARACTERS, 0));
+ }
+
+ final ArrayList<PasswordValidationError> result = new ArrayList<>();
+ if (actualMetrics.length > MAX_PASSWORD_LENGTH) {
+ result.add(new PasswordValidationError(TOO_LONG, MAX_PASSWORD_LENGTH));
+ }
+
+ final PasswordMetrics minMetrics = applyComplexity(adminMetrics, isPin, bucket);
+
+ // Clamp required length between maximum and minimum valid values.
+ minMetrics.length = Math.min(MAX_PASSWORD_LENGTH,
+ Math.max(minMetrics.length, MIN_LOCK_PASSWORD_SIZE));
+ minMetrics.removeOverlapping();
+
+ comparePasswordMetrics(minMetrics, actualMetrics, result);
+
+ return result;
+ }
+
+ /**
+ * TODO: move to PasswordPolicy
+ */
+ private static void comparePasswordMetrics(PasswordMetrics minMetrics,
+ PasswordMetrics actualMetrics, ArrayList<PasswordValidationError> result) {
+ if (actualMetrics.length < minMetrics.length) {
+ result.add(new PasswordValidationError(TOO_SHORT, minMetrics.length));
+ }
+ if (actualMetrics.letters < minMetrics.letters) {
+ result.add(new PasswordValidationError(NOT_ENOUGH_LETTERS, minMetrics.letters));
+ }
+ if (actualMetrics.upperCase < minMetrics.upperCase) {
+ result.add(new PasswordValidationError(NOT_ENOUGH_UPPER_CASE, minMetrics.upperCase));
+ }
+ if (actualMetrics.lowerCase < minMetrics.lowerCase) {
+ result.add(new PasswordValidationError(NOT_ENOUGH_LOWER_CASE, minMetrics.lowerCase));
+ }
+ if (actualMetrics.numeric < minMetrics.numeric) {
+ result.add(new PasswordValidationError(NOT_ENOUGH_DIGITS, minMetrics.numeric));
+ }
+ if (actualMetrics.symbols < minMetrics.symbols) {
+ result.add(new PasswordValidationError(NOT_ENOUGH_SYMBOLS, minMetrics.symbols));
+ }
+ if (actualMetrics.nonLetter < minMetrics.nonLetter) {
+ result.add(new PasswordValidationError(NOT_ENOUGH_NON_LETTER, minMetrics.nonLetter));
+ }
+ if (actualMetrics.nonNumeric < minMetrics.nonNumeric) {
+ result.add(new PasswordValidationError(NOT_ENOUGH_NON_DIGITS, minMetrics.nonNumeric));
+ }
+ if (actualMetrics.seqLength > minMetrics.seqLength) {
+ result.add(new PasswordValidationError(CONTAINS_SEQUENCE, 0));
+ }
+ }
+
+ /**
+ * Drop requirements that are superseded by others, e.g. if it is required to have 5 upper case
+ * letters and 5 lower case letters, there is no need to require minimum number of letters to
+ * be 10 since it will be fulfilled once upper and lower case requirements are fulfilled.
+ *
+ * TODO: move to PasswordPolicy
+ */
+ private void removeOverlapping() {
+ // upperCase + lowerCase can override letters
+ final int indirectLetters = upperCase + lowerCase;
+
+ // numeric + symbols can override nonLetter
+ final int indirectNonLetter = numeric + symbols;
+
+ // letters + symbols can override nonNumeric
+ final int effectiveLetters = Math.max(letters, indirectLetters);
+ final int indirectNonNumeric = effectiveLetters + symbols;
+
+ // letters + nonLetters can override length
+ // numeric + nonNumeric can also override length, so max it with previous.
+ final int effectiveNonLetter = Math.max(nonLetter, indirectNonLetter);
+ final int effectiveNonNumeric = Math.max(nonNumeric, indirectNonNumeric);
+ final int indirectLength = Math.max(effectiveLetters + effectiveNonLetter,
+ numeric + effectiveNonNumeric);
+
+ if (indirectLetters >= letters) {
+ letters = 0;
+ }
+ if (indirectNonLetter >= nonLetter) {
+ nonLetter = 0;
+ }
+ if (indirectNonNumeric >= nonNumeric) {
+ nonNumeric = 0;
+ }
+ if (indirectLength >= length) {
+ length = 0;
+ }
+ }
+
+ /**
+ * Combine minimum metrics, set by admin, complexity set by the requester and actual entered
+ * password metrics to get resulting minimum metrics that the password has to satisfy. Always
+ * returns a new PasswordMetrics object.
+ *
+ * TODO: move to PasswordPolicy
+ */
+ private static PasswordMetrics applyComplexity(
+ PasswordMetrics adminMetrics, boolean isPin, ComplexityBucket bucket) {
+ final PasswordMetrics minMetrics = new PasswordMetrics(adminMetrics);
+
+ if (!bucket.canHaveSequence()) {
+ minMetrics.seqLength = Math.min(minMetrics.seqLength, MAX_ALLOWED_SEQUENCE);
+ }
+
+ minMetrics.length = Math.max(minMetrics.length, bucket.getMinimumLength(!isPin));
+
+ if (!isPin && !bucket.allowsNumericPassword()) {
+ minMetrics.nonNumeric = Math.max(minMetrics.nonNumeric, 1);
+ }
+
+ return minMetrics;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ final PasswordMetrics that = (PasswordMetrics) o;
+ return credType == that.credType
+ && length == that.length
+ && letters == that.letters
+ && upperCase == that.upperCase
+ && lowerCase == that.lowerCase
+ && numeric == that.numeric
+ && symbols == that.symbols
+ && nonLetter == that.nonLetter
+ && nonNumeric == that.nonNumeric
+ && seqLength == that.seqLength;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(credType, length, letters, upperCase, lowerCase, numeric, symbols,
+ nonLetter, nonNumeric, seqLength);
}
}
diff --git a/core/java/android/app/admin/PasswordPolicy.java b/core/java/android/app/admin/PasswordPolicy.java
new file mode 100644
index 000000000000..13f11ad74d12
--- /dev/null
+++ b/core/java/android/app/admin/PasswordPolicy.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+
+/**
+ * {@hide}
+ */
+public class PasswordPolicy {
+ public static final int DEF_MINIMUM_LENGTH = 0;
+ public static final int DEF_MINIMUM_LETTERS = 1;
+ public static final int DEF_MINIMUM_UPPER_CASE = 0;
+ public static final int DEF_MINIMUM_LOWER_CASE = 0;
+ public static final int DEF_MINIMUM_NUMERIC = 1;
+ public static final int DEF_MINIMUM_SYMBOLS = 1;
+ public static final int DEF_MINIMUM_NON_LETTER = 0;
+
+ public int quality = PASSWORD_QUALITY_UNSPECIFIED;
+ public int length = DEF_MINIMUM_LENGTH;
+ public int letters = DEF_MINIMUM_LETTERS;
+ public int upperCase = DEF_MINIMUM_UPPER_CASE;
+ public int lowerCase = DEF_MINIMUM_LOWER_CASE;
+ public int numeric = DEF_MINIMUM_NUMERIC;
+ public int symbols = DEF_MINIMUM_SYMBOLS;
+ public int nonLetter = DEF_MINIMUM_NON_LETTER;
+
+ /**
+ * Returns a minimum password metrics that the password should have to satisfy current policy.
+ */
+ public PasswordMetrics getMinMetrics() {
+ if (quality == PASSWORD_QUALITY_UNSPECIFIED) {
+ return new PasswordMetrics(CREDENTIAL_TYPE_NONE);
+ } else if (quality == PASSWORD_QUALITY_BIOMETRIC_WEAK
+ || quality == PASSWORD_QUALITY_SOMETHING) {
+ return new PasswordMetrics(CREDENTIAL_TYPE_PATTERN);
+ } // quality is NUMERIC or stronger.
+
+ PasswordMetrics result = new PasswordMetrics(CREDENTIAL_TYPE_PASSWORD);
+ result.length = length;
+
+ if (quality == PASSWORD_QUALITY_NUMERIC_COMPLEX) {
+ result.seqLength = PasswordMetrics.MAX_ALLOWED_SEQUENCE;
+ } else if (quality == PASSWORD_QUALITY_ALPHABETIC) {
+ result.nonNumeric = 1;
+ } else if (quality == PASSWORD_QUALITY_ALPHANUMERIC) {
+ result.numeric = 1;
+ result.nonNumeric = 1;
+ } else if (quality == PASSWORD_QUALITY_COMPLEX) {
+ result.numeric = numeric;
+ result.letters = letters;
+ result.upperCase = upperCase;
+ result.lowerCase = lowerCase;
+ result.nonLetter = nonLetter;
+ result.symbols = symbols;
+ }
+ return result;
+ }
+}
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index f9e710e118ea..80c5b17cad23 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -684,8 +684,9 @@ public class AssistStructure implements Parcelable {
static final int FLAGS_HAS_EXTRAS = 0x00400000;
static final int FLAGS_HAS_ID = 0x00200000;
static final int FLAGS_HAS_CHILDREN = 0x00100000;
- static final int FLAGS_HAS_URL = 0x00080000;
+ static final int FLAGS_HAS_URL_DOMAIN = 0x00080000;
static final int FLAGS_HAS_INPUT_TYPE = 0x00040000;
+ static final int FLAGS_HAS_URL_SCHEME = 0x00020000;
static final int FLAGS_HAS_LOCALE_LIST = 0x00010000;
static final int FLAGS_ALL_CONTROL = 0xfff00000;
@@ -829,8 +830,10 @@ public class AssistStructure implements Parcelable {
if ((flags&FLAGS_HAS_INPUT_TYPE) != 0) {
mInputType = in.readInt();
}
- if ((flags&FLAGS_HAS_URL) != 0) {
+ if ((flags&FLAGS_HAS_URL_SCHEME) != 0) {
mWebScheme = in.readString();
+ }
+ if ((flags&FLAGS_HAS_URL_DOMAIN) != 0) {
mWebDomain = in.readString();
}
if ((flags&FLAGS_HAS_LOCALE_LIST) != 0) {
@@ -891,8 +894,11 @@ public class AssistStructure implements Parcelable {
if (mInputType != 0) {
flags |= FLAGS_HAS_INPUT_TYPE;
}
- if (mWebScheme != null || mWebDomain != null) {
- flags |= FLAGS_HAS_URL;
+ if (mWebScheme != null) {
+ flags |= FLAGS_HAS_URL_SCHEME;
+ }
+ if (mWebDomain != null) {
+ flags |= FLAGS_HAS_URL_DOMAIN;
}
if (mLocaleList != null) {
flags |= FLAGS_HAS_LOCALE_LIST;
@@ -1055,8 +1061,10 @@ public class AssistStructure implements Parcelable {
if ((flags&FLAGS_HAS_INPUT_TYPE) != 0) {
out.writeInt(mInputType);
}
- if ((flags&FLAGS_HAS_URL) != 0) {
+ if ((flags & FLAGS_HAS_URL_SCHEME) != 0) {
out.writeString(mWebScheme);
+ }
+ if ((flags&FLAGS_HAS_URL_DOMAIN) != 0) {
out.writeString(mWebDomain);
}
if ((flags&FLAGS_HAS_LOCALE_LIST) != 0) {
@@ -1431,13 +1439,18 @@ public class AssistStructure implements Parcelable {
public void setWebDomain(@Nullable String domain) {
if (domain == null) return;
- final Uri uri = Uri.parse(domain);
+ Uri uri = Uri.parse(domain);
if (uri == null) {
// Cannot log domain because it could contain PII;
Log.w(TAG, "Failed to parse web domain");
return;
}
+
mWebScheme = uri.getScheme();
+ if (mWebScheme == null) {
+ uri = Uri.parse("http://" + domain);
+ }
+
mWebDomain = uri.getHost();
}
diff --git a/core/java/android/app/role/IRoleManager.aidl b/core/java/android/app/role/IRoleManager.aidl
index d8cea2860aa8..6d790b381ace 100644
--- a/core/java/android/app/role/IRoleManager.aidl
+++ b/core/java/android/app/role/IRoleManager.aidl
@@ -19,7 +19,6 @@ package android.app.role;
import android.app.role.IOnRoleHoldersChangedListener;
import android.os.Bundle;
import android.os.RemoteCallback;
-import android.telephony.IFinancialSmsCallback;
/**
* @hide
@@ -55,9 +54,4 @@ interface IRoleManager {
List<String> getHeldRolesFromController(in String packageName);
String getDefaultSmsPackage(int userId);
-
- /**
- * Get filtered SMS messages for financial app.
- */
- void getSmsMessagesForFinancialApp(in String callingPkg, in Bundle params, in IFinancialSmsCallback callback);
}
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 955093d3380e..90ecce2a2170 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -390,6 +390,8 @@ public class SliceManager {
}
Bundle extras = new Bundle();
extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
+ extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+ new ArrayList<>(supportedSpecs));
final Bundle res = provider.call(SliceProvider.METHOD_MAP_INTENT, null, extras);
if (res == null) {
return null;
diff --git a/core/java/android/app/timedetector/ITimeDetectorService.aidl b/core/java/android/app/timedetector/ITimeDetectorService.aidl
index f624446a2027..ddc4932d6fec 100644
--- a/core/java/android/app/timedetector/ITimeDetectorService.aidl
+++ b/core/java/android/app/timedetector/ITimeDetectorService.aidl
@@ -16,10 +16,10 @@
package android.app.timedetector;
-import android.app.timedetector.TimeSignal;
+import android.app.timedetector.PhoneTimeSuggestion;
/**
- * System private API to comunicate with time detector service.
+ * System private API to communicate with time detector service.
*
* <p>Used by parts of the Android system with signals associated with the device's time to provide
* information to the Time Detector Service.
@@ -32,5 +32,5 @@ import android.app.timedetector.TimeSignal;
* {@hide}
*/
interface ITimeDetectorService {
- void suggestTime(in TimeSignal timeSignal);
+ void suggestPhoneTime(in PhoneTimeSuggestion timeSuggestion);
}
diff --git a/core/java/android/app/timedetector/PhoneTimeSuggestion.aidl b/core/java/android/app/timedetector/PhoneTimeSuggestion.aidl
new file mode 100644
index 000000000000..f5e240549a9a
--- /dev/null
+++ b/core/java/android/app/timedetector/PhoneTimeSuggestion.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timedetector;
+
+parcelable PhoneTimeSuggestion;
diff --git a/core/java/android/app/timedetector/PhoneTimeSuggestion.java b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
new file mode 100644
index 000000000000..475a4aafd929
--- /dev/null
+++ b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timedetector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.TimestampedValue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A time signal from a telephony source. The value consists of the number of milliseconds elapsed
+ * since 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime clock when that number
+ * was established. The elapsed realtime clock is considered accurate but volatile, so time signals
+ * must not be persisted across device resets.
+ *
+ * @hide
+ */
+public final class PhoneTimeSuggestion implements Parcelable {
+
+ public static final @NonNull Parcelable.Creator<PhoneTimeSuggestion> CREATOR =
+ new Parcelable.Creator<PhoneTimeSuggestion>() {
+ public PhoneTimeSuggestion createFromParcel(Parcel in) {
+ return PhoneTimeSuggestion.createFromParcel(in);
+ }
+
+ public PhoneTimeSuggestion[] newArray(int size) {
+ return new PhoneTimeSuggestion[size];
+ }
+ };
+
+ private final int mPhoneId;
+ @NonNull
+ private final TimestampedValue<Long> mUtcTime;
+ @Nullable
+ private ArrayList<String> mDebugInfo;
+
+ public PhoneTimeSuggestion(int phoneId, @NonNull TimestampedValue<Long> utcTime) {
+ mPhoneId = phoneId;
+ mUtcTime = Objects.requireNonNull(utcTime);
+ }
+
+ private static PhoneTimeSuggestion createFromParcel(Parcel in) {
+ int phoneId = in.readInt();
+ TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */);
+ PhoneTimeSuggestion suggestion = new PhoneTimeSuggestion(phoneId, utcTime);
+ @SuppressWarnings("unchecked")
+ ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
+ suggestion.mDebugInfo = debugInfo;
+ return suggestion;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mPhoneId);
+ dest.writeParcelable(mUtcTime, 0);
+ dest.writeList(mDebugInfo);
+ }
+
+ public int getPhoneId() {
+ return mPhoneId;
+ }
+
+ @NonNull
+ public TimestampedValue<Long> getUtcTime() {
+ return mUtcTime;
+ }
+
+ @NonNull
+ public List<String> getDebugInfo() {
+ return 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;
+ }
+ PhoneTimeSuggestion that = (PhoneTimeSuggestion) o;
+ return mPhoneId == that.mPhoneId
+ && Objects.equals(mUtcTime, that.mUtcTime);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPhoneId, mUtcTime);
+ }
+
+ @Override
+ public String toString() {
+ return "PhoneTimeSuggestion{"
+ + "mPhoneId='" + mPhoneId + '\''
+ + ", mUtcTime=" + mUtcTime
+ + ", mDebugInfo=" + mDebugInfo
+ + '}';
+ }
+}
diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java
index 052050df8c9a..334e9582a145 100644
--- a/core/java/android/app/timedetector/TimeDetector.java
+++ b/core/java/android/app/timedetector/TimeDetector.java
@@ -45,12 +45,12 @@ public final class TimeDetector {
* signals are available such as those that come from more reliable sources or were
* determined more recently.
*/
- public void suggestTime(@NonNull TimeSignal timeSignal) {
+ public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
if (DEBUG) {
- Log.d(TAG, "suggestTime called: " + timeSignal);
+ Log.d(TAG, "suggestPhoneTime called: " + timeSuggestion);
}
try {
- mITimeDetectorService.suggestTime(timeSignal);
+ mITimeDetectorService.suggestPhoneTime(timeSuggestion);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/timedetector/TimeSignal.aidl b/core/java/android/app/timedetector/TimeSignal.aidl
deleted file mode 100644
index d2ec357555bc..000000000000
--- a/core/java/android/app/timedetector/TimeSignal.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.timedetector;
-
-parcelable TimeSignal; \ No newline at end of file
diff --git a/core/java/android/app/timedetector/TimeSignal.java b/core/java/android/app/timedetector/TimeSignal.java
deleted file mode 100644
index da21794cd649..000000000000
--- a/core/java/android/app/timedetector/TimeSignal.java
+++ /dev/null
@@ -1,110 +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 android.app.timedetector;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.TimestampedValue;
-
-import java.util.Objects;
-
-/**
- * A time signal from a named source. The value consists of the number of milliseconds elapsed since
- * 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime clock when that number was
- * established. The elapsed realtime clock is considered accurate but volatile, so time signals
- * must not be persisted across device resets.
- *
- * @hide
- */
-public final class TimeSignal implements Parcelable {
-
- public static final @android.annotation.NonNull Parcelable.Creator<TimeSignal> CREATOR =
- new Parcelable.Creator<TimeSignal>() {
- public TimeSignal createFromParcel(Parcel in) {
- return TimeSignal.createFromParcel(in);
- }
-
- public TimeSignal[] newArray(int size) {
- return new TimeSignal[size];
- }
- };
-
- public static final String SOURCE_ID_NITZ = "nitz";
-
- private final String mSourceId;
- private final TimestampedValue<Long> mUtcTime;
-
- public TimeSignal(String sourceId, TimestampedValue<Long> utcTime) {
- mSourceId = Objects.requireNonNull(sourceId);
- mUtcTime = Objects.requireNonNull(utcTime);
- }
-
- private static TimeSignal createFromParcel(Parcel in) {
- String sourceId = in.readString();
- TimestampedValue<Long> utcTime =
- TimestampedValue.readFromParcel(in, null /* classLoader */, Long.class);
- return new TimeSignal(sourceId, utcTime);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeString(mSourceId);
- TimestampedValue.writeToParcel(dest, mUtcTime);
- }
-
- @NonNull
- public String getSourceId() {
- return mSourceId;
- }
-
- @NonNull
- public TimestampedValue<Long> getUtcTime() {
- return mUtcTime;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- TimeSignal that = (TimeSignal) o;
- return Objects.equals(mSourceId, that.mSourceId)
- && Objects.equals(mUtcTime, that.mUtcTime);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mSourceId, mUtcTime);
- }
-
- @Override
- public String toString() {
- return "TimeSignal{"
- + "mSourceId='" + mSourceId + '\''
- + ", mUtcTime=" + mUtcTime
- + '}';
- }
-}
diff --git a/core/java/android/app/usage/AppStandbyInfo.java b/core/java/android/app/usage/AppStandbyInfo.java
index ebdbf833b81a..c283702a6bbf 100644
--- a/core/java/android/app/usage/AppStandbyInfo.java
+++ b/core/java/android/app/usage/AppStandbyInfo.java
@@ -22,6 +22,10 @@ import android.os.Parcelable;
/**
* A pair of {package, bucket} to denote the app standby bucket for a given package.
* Used as a vehicle of data across the binder IPC.
+ *
+ * Note we're not moving this class to the jobscheduler apex, because it's consumed by
+ * UsageStatsManager, which is not updatable anyway, so making this updatable isn't really
+ * beneficial.
* @hide
*/
public final class AppStandbyInfo implements Parcelable {
diff --git a/core/java/android/app/usage/EventList.java b/core/java/android/app/usage/EventList.java
index 8c0340585573..afdcbe6d164f 100644
--- a/core/java/android/app/usage/EventList.java
+++ b/core/java/android/app/usage/EventList.java
@@ -79,6 +79,21 @@ public class EventList {
}
/**
+ * Removes the event at the given index.
+ *
+ * @param index the index of the event to remove
+ * @return the event removed, or {@code null} if the index was out of bounds
+ */
+ public UsageEvents.Event remove(int index) {
+ try {
+ return mEvents.remove(index);
+ } catch (IndexOutOfBoundsException e) {
+ // catch and handle the exception here instead of throwing it to the client
+ return null;
+ }
+ }
+
+ /**
* Finds the index of the first event whose timestamp is greater than or equal to the given
* timestamp.
*
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 5dbca12fa3dd..4bf9c04dfade 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -312,6 +312,11 @@ public final class UsageEvents implements Parcelable {
public static final int VALID_FLAG_BITS = FLAG_IS_PACKAGE_INSTANT_APP;
/**
+ * @hide
+ */
+ private static final int UNASSIGNED_TOKEN = -1;
+
+ /**
* {@hide}
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -320,12 +325,22 @@ public final class UsageEvents implements Parcelable {
/**
* {@hide}
*/
+ public int mPackageToken = UNASSIGNED_TOKEN;
+
+ /**
+ * {@hide}
+ */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public String mClass;
/**
* {@hide}
*/
+ public int mClassToken = UNASSIGNED_TOKEN;
+
+ /**
+ * {@hide}
+ */
public int mInstanceId;
/**
@@ -336,11 +351,21 @@ public final class UsageEvents implements Parcelable {
/**
* {@hide}
*/
+ public int mTaskRootPackageToken = UNASSIGNED_TOKEN;
+
+ /**
+ * {@hide}
+ */
public String mTaskRootClass;
/**
* {@hide}
*/
+ public int mTaskRootClassToken = UNASSIGNED_TOKEN;
+
+ /**
+ * {@hide}
+ */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public long mTimeStamp;
@@ -365,6 +390,11 @@ public final class UsageEvents implements Parcelable {
public String mShortcutId;
/**
+ * {@hide}
+ */
+ public int mShortcutIdToken = UNASSIGNED_TOKEN;
+
+ /**
* Action type passed to ChooserActivity
* Only present for {@link #CHOOSER_ACTION} event types.
* {@hide}
@@ -401,6 +431,11 @@ public final class UsageEvents implements Parcelable {
*/
public String mNotificationChannelId;
+ /**
+ * {@hide}
+ */
+ public int mNotificationChannelIdToken = UNASSIGNED_TOKEN;
+
/** @hide */
@EventFlags
public int mFlags;
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index 2c021cc42cb5..9d43dd34d558 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -35,6 +35,7 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
+import android.util.SparseArray;
import android.util.SparseIntArray;
/**
@@ -52,6 +53,11 @@ public final class UsageStats implements Parcelable {
/**
* {@hide}
*/
+ public int mPackageToken = -1;
+
+ /**
+ * {@hide}
+ */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public long mBeginTimeStamp;
@@ -143,6 +149,11 @@ public final class UsageStats implements Parcelable {
/**
* {@hide}
*/
+ public SparseArray<SparseIntArray> mChooserCountsObfuscated = new SparseArray<>();
+
+ /**
+ * {@hide}
+ */
public UsageStats() {
}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 656f474f72ce..1f13a1e13d13 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -801,8 +801,8 @@ public final class UsageStatsManager {
* {@link #EXTRA_TIME_USED}. Cannot be {@code null} unless the observer is
* being registered with a {@code timeUsed} equal to or greater than
* {@code timeLimit}.
- * @throws SecurityException if the caller doesn't have both SUSPEND_APPS and OBSERVE_APP_USAGE
- * permissions.
+ * @throws SecurityException if the caller is neither the active supervision app nor does it
+ * have both SUSPEND_APPS and OBSERVE_APP_USAGE permissions.
* @hide
*/
@SystemApi
@@ -827,8 +827,8 @@ public final class UsageStatsManager {
* an observer that was already unregistered or never registered will have no effect.
*
* @param observerId The id of the observer that was previously registered.
- * @throws SecurityException if the caller doesn't have both SUSPEND_APPS and OBSERVE_APP_USAGE
- * permissions.
+ * @throws SecurityException if the caller is neither the active supervision app nor does it
+ * have both SUSPEND_APPS and OBSERVE_APP_USAGE permissions.
* @hide
*/
@SystemApi
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index b3260c4c5cce..024afe25f98e 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -154,12 +154,6 @@ public abstract class UsageStatsManagerInternal {
public abstract int[] getIdleUidsForUser(@UserIdInt int userId);
/**
- * @return True if currently app idle parole mode is on. This means all idle apps are allow to
- * run for a short period of time.
- */
- public abstract boolean isAppIdleParoleOn();
-
- /**
* Sets up a listener for changes to packages being accessed.
* @param listener A listener within the system process.
*/
@@ -180,12 +174,6 @@ public abstract class UsageStatsManagerInternal {
boolean idle, int bucket, int reason);
/**
- * Callback to inform listeners that the parole state has changed. This means apps are
- * allowed to do work even if they're idle or in a low bucket.
- */
- public abstract void onParoleStateChanged(boolean isParoleOn);
-
- /**
* Optional callback to inform the listener that the app has transitioned into
* an active state due to user interaction.
*/
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 9dbfbc7795a3..7de87938c1f6 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -834,6 +834,23 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
}
/**
+ * Retrieves a Non-Nullable Context this provider is running in, this is intended to be called
+ * after {@link #onCreate}. When called before context was created, an IllegalStateException
+ * will be thrown.
+ * <p>
+ * Note A provider must be declared in the manifest and created automatically by the system,
+ * and context is only available after {@link #onCreate} is called.
+ */
+ @NonNull
+ public final Context requireContext() {
+ final Context ctx = getContext();
+ if (ctx == null) {
+ throw new IllegalStateException("Cannot find context from the provider.");
+ }
+ return ctx;
+ }
+
+ /**
* Set the calling package, returning the current value (or {@code null})
* which can be used later to restore the previous state.
*/
@@ -1469,9 +1486,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
* This method can be called from multiple threads, as described in
* <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
* and Threads</a>.
- * @param uri The content:// URI of the insertion request. This must not be {@code null}.
+ * @param uri The content:// URI of the insertion request.
* @param values A set of column_name/value pairs to add to the database.
- * This must not be {@code null}.
* @return The URI for the newly inserted item.
*/
@Override
@@ -1538,7 +1554,6 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
* @param uri The URI to query. This can potentially have a record ID if this
* is an update request for a specific record.
* @param values A set of column_name/value pairs to update in the database.
- * This must not be {@code null}.
* @param selection An optional filter to match rows to update.
* @return the number of rows affected.
*/
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 29f35ef73b45..607a4edd52a9 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -265,6 +265,7 @@ public abstract class ContentResolver implements ContentInterface {
* @see #QUERY_ARG_SORT_COLUMNS
* @see #QUERY_ARG_SORT_DIRECTION
* @see #QUERY_ARG_SORT_COLLATION
+ * @see #QUERY_ARG_SORT_LOCALE
*/
public static final String QUERY_ARG_SQL_SELECTION = "android:query-arg-sql-selection";
@@ -282,6 +283,7 @@ public abstract class ContentResolver implements ContentInterface {
* @see #QUERY_ARG_SORT_COLUMNS
* @see #QUERY_ARG_SORT_DIRECTION
* @see #QUERY_ARG_SORT_COLLATION
+ * @see #QUERY_ARG_SORT_LOCALE
*/
public static final String QUERY_ARG_SQL_SELECTION_ARGS =
"android:query-arg-sql-selection-args";
@@ -297,6 +299,7 @@ public abstract class ContentResolver implements ContentInterface {
* @see #QUERY_ARG_SORT_COLUMNS
* @see #QUERY_ARG_SORT_DIRECTION
* @see #QUERY_ARG_SORT_COLLATION
+ * @see #QUERY_ARG_SORT_LOCALE
*/
public static final String QUERY_ARG_SQL_SORT_ORDER = "android:query-arg-sql-sort-order";
@@ -351,20 +354,22 @@ public abstract class ContentResolver implements ContentInterface {
/**
* Allows client to specify a hint to the provider declaring which collation
- * to use when sorting text values.
- *
- * <p>Providers may support custom collators. When specifying a custom collator
+ * to use when sorting values.
+ * <p>
+ * Providers may support custom collators. When specifying a custom collator
* the value is determined by the Provider.
- *
- * <li>{@link ContentProvider} implementations: When preparing data in
- * {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)}, if sort collation
- * is reflected in the returned Cursor, it is strongly recommended that
- * {@link #QUERY_ARG_SORT_COLLATION} then be included in the array of honored arguments
- * reflected in {@link Cursor} extras {@link Bundle} under {@link #EXTRA_HONORED_ARGS}.
- *
- * <li>When querying a provider, where no QUERY_ARG_SQL* otherwise exists in the
- * arguments {@link Bundle}, the Content framework will attempt to synthesize
- * a QUERY_ARG_SQL* argument using the corresponding QUERY_ARG_SORT* values.
+ * <p>
+ * {@link ContentProvider} implementations: When preparing data in
+ * {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)},
+ * if sort collation is reflected in the returned Cursor, it is strongly
+ * recommended that {@link #QUERY_ARG_SORT_COLLATION} then be included in
+ * the array of honored arguments reflected in {@link Cursor} extras
+ * {@link Bundle} under {@link #EXTRA_HONORED_ARGS}.
+ * <p>
+ * When querying a provider, where no QUERY_ARG_SQL* otherwise exists in the
+ * arguments {@link Bundle}, the Content framework will attempt to
+ * synthesize a QUERY_ARG_SQL* argument using the corresponding
+ * QUERY_ARG_SORT* values.
*
* @see java.text.Collator#PRIMARY
* @see java.text.Collator#SECONDARY
@@ -374,6 +379,28 @@ public abstract class ContentResolver implements ContentInterface {
public static final String QUERY_ARG_SORT_COLLATION = "android:query-arg-sort-collation";
/**
+ * Allows client to specify a hint to the provider declaring which locale to
+ * use when sorting values.
+ * <p>
+ * The value is defined as a RFC 3066 locale ID followed by an optional
+ * keyword list, which is the locale format used to configure ICU through
+ * classes like {@link android.icu.util.ULocale}. This supports requesting
+ * advanced sorting options, such as {@code de@collation=phonebook},
+ * {@code zh@collation=pinyin}, etc.
+ * <p>
+ * {@link ContentProvider} implementations: When preparing data in
+ * {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)},
+ * if sort locale is reflected in the returned Cursor, it is strongly
+ * recommended that {@link #QUERY_ARG_SORT_LOCALE} then be included in the
+ * array of honored arguments reflected in {@link Cursor} extras
+ * {@link Bundle} under {@link #EXTRA_HONORED_ARGS}.
+ *
+ * @see java.util.Locale#Locale(String)
+ * @see android.icu.util.ULocale#ULocale(String)
+ */
+ public static final String QUERY_ARG_SORT_LOCALE = "android:query-arg-sort-locale";
+
+ /**
* Allows provider to report back to client which query keys are honored in a Cursor.
*
* <p>Key identifying a {@code String[]} containing all QUERY_ARG_SORT* arguments
@@ -386,6 +413,7 @@ public abstract class ContentResolver implements ContentInterface {
* @see #QUERY_ARG_SORT_COLUMNS
* @see #QUERY_ARG_SORT_DIRECTION
* @see #QUERY_ARG_SORT_COLLATION
+ * @see #QUERY_ARG_SORT_LOCALE
*/
public static final String EXTRA_HONORED_ARGS = "android.content.extra.HONORED_ARGS";
@@ -1739,7 +1767,7 @@ public abstract class ContentResolver implements ContentInterface {
stableProvider = null;
return new AssetFileDescriptor(pfd, fd.getStartOffset(),
- fd.getDeclaredLength());
+ fd.getDeclaredLength(), fd.getExtras());
} catch (RemoteException e) {
// Whatever, whatever, we'll go away.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b612f1c4672a..9a9fc89588f5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -63,6 +63,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.provider.MediaStore;
+import android.telephony.TelephonyRegistryManager;
import android.util.AttributeSet;
import android.view.Display;
import android.view.DisplayAdjustments;
@@ -74,6 +75,7 @@ import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient;
import android.view.textclassifier.TextClassificationManager;
import com.android.internal.compat.IPlatformCompat;
+import com.android.internal.compat.IPlatformCompatNative;
import java.io.File;
import java.io.FileInputStream;
@@ -810,6 +812,17 @@ public abstract class Context {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
+ /**
+ * <p>Features are used in complex apps to logically separate parts of the app. E.g. a
+ * blogging app might also have a instant messaging app built in.
+ *
+ * @return the feature id this context is for or {@code null} if this is the default
+ * feature.
+ */
+ public @Nullable String getFeatureId() {
+ return null;
+ }
+
/** Return the full application info for this context's package. */
public abstract ApplicationInfo getApplicationInfo();
@@ -1559,7 +1572,14 @@ public abstract class Context {
* @see Environment#getExternalStorageState(File)
* @see Environment#isExternalStorageEmulated(File)
* @see Environment#isExternalStorageRemovable(File)
+ * @deprecated These directories still exist and are scanned, but developers
+ * are encouraged to migrate to inserting content into a
+ * {@link MediaStore} collection directly, as any app can
+ * contribute new media to {@link MediaStore} with no
+ * permissions required, starting in
+ * {@link android.os.Build.VERSION_CODES#Q}.
*/
+ @Deprecated
public abstract File[] getExternalMediaDirs();
/**
@@ -3236,6 +3256,7 @@ public abstract class Context {
//@hide ROLE_CONTROLLER_SERVICE,
CAMERA_SERVICE,
//@hide: PLATFORM_COMPAT_SERVICE,
+ //@hide: PLATFORM_COMPAT_NATIVE_SERVICE,
PRINT_SERVICE,
CONSUMER_IR_SERVICE,
//@hide: TRUST_SERVICE,
@@ -3301,7 +3322,7 @@ public abstract class Context {
* <dt> {@link #VIBRATOR_SERVICE} ("vibrator")
* <dd> A {@link android.os.Vibrator} for interacting with the vibrator
* hardware.
- * <dt> {@link #CONNECTIVITY_SERVICE} ("connection")
+ * <dt> {@link #CONNECTIVITY_SERVICE} ("connectivity")
* <dd> A {@link android.net.ConnectivityManager ConnectivityManager} for
* handling management of network connections.
* <dt> {@link #IPSEC_SERVICE} ("ipsec")
@@ -4613,6 +4634,14 @@ public abstract class Context {
public static final String PLATFORM_COMPAT_SERVICE = "platform_compat";
/**
+ * Use with {@link android.os.ServiceManager.getService()} to retrieve a
+ * {@link IPlatformCompatNative} IBinder for native code communicating with the platform compat
+ * service.
+ * @hide
+ */
+ public static final String PLATFORM_COMPAT_NATIVE_SERVICE = "platform_compat_native";
+
+ /**
* Service to capture a bugreport.
* @see #getSystemService(String)
* @see android.os.BugreportManager
@@ -4708,6 +4737,14 @@ public abstract class Context {
public static final String DYNAMIC_SYSTEM_SERVICE = "dynamic_system";
/**
+ * Use with {@link #getSystemService(String)} to retrieve an
+ * {@link TelephonyRegistryManager}.
+ * @hide
+ */
+ @SystemApi
+ public static final String TELEPHONY_REGISTRY_SERVICE = "telephony_registry";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
@@ -5209,8 +5246,9 @@ public abstract class Context {
*/
@SystemApi
@TestApi
+ @NonNull
public Context createPackageContextAsUser(
- String packageName, @CreatePackageOptions int flags, UserHandle user)
+ @NonNull String packageName, @CreatePackageOptions int flags, @NonNull UserHandle user)
throws PackageManager.NameNotFoundException {
if (Build.IS_ENG) {
throw new IllegalStateException("createPackageContextAsUser not overridden!");
@@ -5219,6 +5257,23 @@ public abstract class Context {
}
/**
+ * Similar to {@link #createPackageContext(String, int)}, but for the own package with a
+ * different {@link UserHandle}. For example, {@link #getContentResolver()}
+ * will open any {@link Uri} as the given user.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @NonNull
+ public Context createContextAsUser(@NonNull UserHandle user, @CreatePackageOptions int flags) {
+ if (Build.IS_ENG) {
+ throw new IllegalStateException("createContextAsUser not overridden!");
+ }
+ return this;
+ }
+
+ /**
* Creates a context given an {@link android.content.pm.ApplicationInfo}.
*
* @hide
@@ -5298,6 +5353,20 @@ public abstract class Context {
public abstract Context createDisplayContext(@NonNull Display display);
/**
+ * Return a new Context object for the current Context but for a different feature in the app.
+ * Features can be used by complex apps to separate logical parts.
+ *
+ * @param featureId The feature id or {@code null} to create a context for the default feature.
+ *
+ * @return A {@link Context} for the feature
+ *
+ * @see #getFeatureId()
+ */
+ public @NonNull Context createFeatureContext(@Nullable String featureId) {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+
+ /**
* Return a new Context object for the current Context but whose storage
* APIs are backed by device-protected storage.
* <p>
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 0859f97e81a1..d5d0dcea9438 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -16,6 +16,7 @@
package android.content;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
@@ -158,6 +159,12 @@ public class ContextWrapper extends Context {
return mBase.getOpPackageName();
}
+ /** @hide */
+ @Override
+ public @Nullable String getFeatureId() {
+ return mBase.getFeatureId();
+ }
+
@Override
public ApplicationInfo getApplicationInfo() {
return mBase.getApplicationInfo();
@@ -885,6 +892,12 @@ public class ContextWrapper extends Context {
/** @hide */
@Override
+ public Context createContextAsUser(UserHandle user, @CreatePackageOptions int flags) {
+ return mBase.createContextAsUser(user, flags);
+ }
+
+ /** @hide */
+ @Override
@UnsupportedAppUsage
public Context createApplicationContext(ApplicationInfo application,
int flags) throws PackageManager.NameNotFoundException {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 78cec49778c6..44adbb9652b6 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1134,6 +1134,18 @@ public class Intent implements Parcelable, Cloneable {
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_CALL_EMERGENCY = "android.intent.action.CALL_EMERGENCY";
/**
+ * Activity Action: Dial a emergency number specified by the data. This shows a
+ * UI with the number being dialed, allowing the user to explicitly
+ * initiate the call.
+ * <p>Input: If nothing, an empty emergency dialer is started; else {@link #getData}
+ * is a tel: URI of an explicit emergency phone number.
+ * <p>Output: nothing.
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_DIAL_EMERGENCY = "android.intent.action.DIAL_EMERGENCY";
+ /**
* Activity action: Perform a call to any number (emergency or not)
* specified by the data.
* <p>Input: {@link #getData} is URI of a phone number to be dialed or a
@@ -2025,17 +2037,6 @@ public class Intent implements Parcelable, Cloneable {
public static final String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
/**
- * Intent extra: A {@link Bundle} of extras supplied for the launcher when any packages on
- * device are suspended. Will be sent with {@link #ACTION_PACKAGES_SUSPENDED}.
- *
- * @see PackageManager#isPackageSuspended()
- * @see #ACTION_PACKAGES_SUSPENDED
- *
- * @hide
- */
- public static final String EXTRA_LAUNCHER_EXTRAS = "android.intent.extra.LAUNCHER_EXTRAS";
-
- /**
* Intent extra: ID of the shortcut used to send the share intent. Will be sent with
* {@link #ACTION_SEND}.
*
@@ -2677,6 +2678,9 @@ public class Intent implements Parcelable, Cloneable {
* that application is first launched (that is the first time it is moved
* out of the stopped state). The data contains the name of the package.
*
+ * <p>When the application is first launched, the application itself doesn't receive this
+ * broadcast.</p>
+ *
* <p class="note">This is a protected intent that can only be sent
* by the system.
*/
@@ -4677,6 +4681,59 @@ public class Intent implements Parcelable, Cloneable {
*/
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_VR_HOME = "android.intent.category.VR_HOME";
+
+ /**
+ * The accessibility shortcut is a global gesture for users with disabilities to trigger an
+ * important for them accessibility feature to help developers determine whether they want to
+ * make their activity a shortcut target.
+ * <p>
+ * An activity of interest to users with accessibility needs may request to be the target of
+ * the accessibility shortcut. It handles intent {@link #ACTION_MAIN} with this category,
+ * which will be dispatched by the system when the user activates the shortcut when it is
+ * configured to point at this target.
+ * </p>
+ * <p>
+ * An activity declared itself to be a target of the shortcut in AndroidManifest.xml. It must
+ * also do two things:
+ * <ul>
+ * <ol>
+ * Specify that it handles the <code>android.intent.action.MAIN</code>
+ * {@link android.content.Intent}
+ * with category <code>android.intent.category.ACCESSIBILITY_SHORTCUT_TARGET</code>.
+ * </ol>
+ * <ol>
+ * Provide a meta-data entry <code>android.accessibilityshortcut.target</code> in the
+ * manifest when declaring the activity.
+ * </ol>
+ * </ul>
+ * If either of these items is missing, the system will ignore the accessibility shortcut
+ * target. Following is an example declaration:
+ * </p>
+ * <pre>
+ * &lt;activity android:name=".MainActivity"
+ * . . .
+ * &lt;intent-filter&gt;
+ * &lt;action android:name="android.intent.action.MAIN" /&gt;
+ * &lt;category android:name="android.intent.category.ACCESSIBILITY_SHORTCUT_TARGET" /&gt;
+ * &lt;/intent-filter&gt;
+ * &lt;meta-data android:name="android.accessibilityshortcut.target"
+ * android:resource="@xml/accessibilityshortcut" /&gt;
+ * &lt;/activity&gt;
+ * </pre>
+ * <p> This is a sample XML file configuring a accessibility shortcut target: </p>
+ * <pre>
+ * &lt;accessibility-shortcut-target
+ * android:description="@string/shortcut_target_description"
+ * android:summary="@string/shortcut_target_summary" /&gt;
+ * </pre>
+ * <p>
+ * Both description and summary are necessary. The system will ignore the accessibility
+ * shortcut target if they are missing.
+ * </p>
+ */
+ @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+ public static final String CATEGORY_ACCESSIBILITY_SHORTCUT_TARGET =
+ "android.intent.category.ACCESSIBILITY_SHORTCUT_TARGET";
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Application launch intent categories (see addCategory()).
@@ -10016,7 +10073,10 @@ public class Intent implements Parcelable, Cloneable {
if (!Objects.equals(this.mData, other.mData)) return false;
if (!Objects.equals(this.mType, other.mType)) return false;
if (!Objects.equals(this.mIdentifier, other.mIdentifier)) return false;
- if (!Objects.equals(this.mPackage, other.mPackage)) return false;
+ if (!(this.hasPackageEquivalentComponent() && other.hasPackageEquivalentComponent())
+ && !Objects.equals(this.mPackage, other.mPackage)) {
+ return false;
+ }
if (!Objects.equals(this.mComponent, other.mComponent)) return false;
if (!Objects.equals(this.mCategories, other.mCategories)) return false;
@@ -10024,6 +10084,15 @@ public class Intent implements Parcelable, Cloneable {
}
/**
+ * Return {@code true} if the component name is not null and is in the same package that this
+ * intent limited to. otherwise return {@code false}.
+ */
+ private boolean hasPackageEquivalentComponent() {
+ return mComponent != null
+ && (mPackage == null || mPackage.equals(mComponent.getPackageName()));
+ }
+
+ /**
* Generate hash code that matches semantics of filterEquals().
*
* @return Returns the hash value of the action, data, type, class, and
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
index e24512ac525d..85f96fc61681 100644
--- a/core/java/android/content/PermissionChecker.java
+++ b/core/java/android/content/PermissionChecker.java
@@ -16,8 +16,6 @@
package android.content;
-import static android.app.AppOpsManager.strOpToOp;
-
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -52,6 +50,19 @@ import java.lang.annotation.RetentionPolicy;
* permission model for which the user had disabled the "permission"
* which is achieved by disallowing the corresponding app op.
* </p>
+ * <p>
+ * This class has two types of methods and you should be careful which
+ * type to call based on whether permission protected data is being
+ * passed to the app or you are just checking whether the app holds a
+ * permission. The reason is that a permission check requires checking
+ * the runtime permission and if it is granted checking the corresponding
+ * app op as for apps not supporting the runtime mode we never revoke
+ * permissions but disable app ops. Since there are two types of app op
+ * checks, one that does not leave a record an action was performed and
+ * another the does, one needs to call the preflight flavor of the checks
+ * named xxxForPreflight only if no private data is being delivered but
+ * a permission check is what is needed and the xxxForDataDelivery where
+ * the permission check is right before private data delivery.
*
* @hide
*/
@@ -65,6 +76,9 @@ public final class PermissionChecker {
/** Permission result: The permission is denied because the app op is not allowed. */
public static final int PERMISSION_DENIED_APP_OP = PackageManager.PERMISSION_DENIED - 1;
+ /** Constant when the PID for which we check permissions is unknown. */
+ public static final int PID_UNKNOWN = -1;
+
/** @hide */
@IntDef({PERMISSION_GRANTED,
PERMISSION_DENIED,
@@ -77,63 +91,97 @@ public final class PermissionChecker {
}
/**
- * @deprecated Use {@link #checkPermission(Context, String, int, int, String, String)} instead
+ * Checks whether a given package in a UID and PID has a given permission
+ * and whether the app op that corresponds to this permission is allowed.
+ *
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * point where you will deliver the permission protected data to clients.
+ *
+ * <p>For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use {@link #checkPermissionForPreflight(Context, String, int, int, String)}
+ * to determine if the app has or may have location permission (if app has only foreground
+ * location the grant state depends on the app's fg/gb state) and this check will not
+ * leave a trace that permission protected data was delivered. When you are about to
+ * deliver the location data to a registered listener you should use this method which
+ * will evaluate the permission access based on the current fg/bg state of the app and
+ * leave a record that the data was accessed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @param pid The process id for which to check. Use {@link #PID_UNKNOWN} if the PID
+ * is not known.
+ * @param uid The uid for which to check.
+ * @param packageName The package name for which to check. If null the
+ * the first package for the calling UID will be used.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ * @param message A message describing the reason the permission was checked
+ *
+ * @see #checkPermissionForPreflight(Context, String, int, int, String)
*/
- @Deprecated
@PermissionResult
- public static int checkPermission(@NonNull Context context, @NonNull String permission,
- int pid, int uid, @Nullable String packageName) {
- return checkPermission(context, permission, pid, uid, packageName, null);
+ public static int checkPermissionForDataDelivery(@NonNull Context context,
+ @NonNull String permission, int pid, int uid, @Nullable String packageName,
+ @Nullable String message) {
+ return checkPermissionCommon(context, permission, pid, uid, packageName, message,
+ true /*forDataDelivery*/);
}
/**
* Checks whether a given package in a UID and PID has a given permission
* and whether the app op that corresponds to this permission is allowed.
*
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * preflight point where you will not deliver the permission protected data
+ * to clients but schedule permission data delivery, apps register listeners,
+ * etc.
+ *
+ * <p>For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use this method to determine if the app has or may have location
+ * permission (if app has only foreground location the grant state depends on the app's
+ * fg/gb state) and this check will not leave a trace that permission protected data
+ * was delivered. When you are about to deliver the location data to a registered
+ * listener you should use {@link #checkPermissionForDataDelivery(Context, String,
+ * int, int, String, String)} which will evaluate the permission access based on the current
+ * fg/bg state of the app and leave a record that the data was accessed.
+ *
* @param context Context for accessing resources.
* @param permission The permission to check.
* @param pid The process id for which to check.
* @param uid The uid for which to check.
* @param packageName The package name for which to check. If null the
* the first package for the calling UID will be used.
- * @param message A message describing the reason the permission was checked
- *
* @return The permission check result which is either {@link #PERMISSION_GRANTED}
* or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ *
+ * @see #checkPermissionForDataDelivery(Context, String, int, int, String, String)
*/
@PermissionResult
- public static int checkPermission(@NonNull Context context, @NonNull String permission,
- int pid, int uid, @Nullable String packageName, @Nullable String message) {
- if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
- return PERMISSION_DENIED;
- }
-
- AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
- String op = appOpsManager.permissionToOp(permission);
- if (op == null) {
- return PERMISSION_GRANTED;
- }
-
- if (packageName == null) {
- String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
- if (packageNames == null || packageNames.length <= 0) {
- return PERMISSION_DENIED;
- }
- packageName = packageNames[0];
- }
-
- if (appOpsManager.noteProxyOpNoThrow(strOpToOp(op), packageName, uid, message)
- != AppOpsManager.MODE_ALLOWED) {
- return PERMISSION_DENIED_APP_OP;
- }
-
- return PERMISSION_GRANTED;
+ public static int checkPermissionForPreflight(@NonNull Context context,
+ @NonNull String permission, int pid, int uid, @Nullable String packageName) {
+ return checkPermissionCommon(context, permission, pid, uid, packageName, null /*message*/,
+ false /*forDataDelivery*/);
}
/**
* Checks whether your app has a given permission and whether the app op
* that corresponds to this permission is allowed.
*
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * point where you will deliver the permission protected data to clients.
+ *
+ * <p>For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use {@link #checkSelfPermissionForPreflight(Context, String)}
+ * to determine if the app has or may have location permission (if app has only foreground
+ * location the grant state depends on the app's fg/gb state) and this check will not
+ * leave a trace that permission protected data was delivered. When you are about to
+ * deliver the location data to a registered listener you should use this method
+ * which will evaluate the permission access based on the current fg/bg state of the
+ * app and leave a record that the data was accessed.
+ *
* <p>This API assumes the the {@link Binder#getCallingUid()} is the same as
* {@link Process#myUid()}.
*
@@ -141,74 +189,230 @@ public final class PermissionChecker {
* @param permission The permission to check.
* @return The permission check result which is either {@link #PERMISSION_GRANTED}
* or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ * @param message A message describing the reason the permission was checked
+ *
+ * @see #checkSelfPermissionForPreflight(Context, String)
*/
@PermissionResult
- public static int checkSelfPermission(@NonNull Context context,
- @NonNull String permission) {
- return checkPermission(context, permission, Process.myPid(),
- Process.myUid(), context.getPackageName(), null /* self access */);
+ public static int checkSelfPermissionForDataDelivery(@NonNull Context context,
+ @NonNull String permission, @Nullable String message) {
+ return checkPermissionForDataDelivery(context, permission, Process.myPid(),
+ Process.myUid(), context.getPackageName(), message);
}
/**
- * @deprecated Use {@link #checkCallingPermission(Context, String, String, String)} instead
+ * Checks whether your app has a given permission and whether the app op
+ * that corresponds to this permission is allowed.
+ *
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * preflight point where you will not deliver the permission protected data
+ * to clients but schedule permission data delivery, apps register listeners,
+ * etc.
+ *
+ * <p>For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use this method to determine if the app has or may have location
+ * permission (if app has only foreground location the grant state depends on the
+ * app's fg/gb state) and this check will not leave a trace that permission protected
+ * data was delivered. When you are about to deliver the location data to a registered
+ * listener you should use this method which will evaluate the permission access based
+ * on the current fg/bg state of the app and leave a record that the data was accessed.
+ *
+ * <p>This API assumes the the {@link Binder#getCallingUid()} is the same as
+ * {@link Process#myUid()}.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ *
+ * @see #checkSelfPermissionForDataDelivery(Context, String, String)
*/
- @Deprecated
@PermissionResult
- public static int checkCallingPermission(@NonNull Context context,
- @NonNull String permission, @Nullable String packageName) {
- return checkCallingPermission(context, permission, packageName, null);
+ public static int checkSelfPermissionForPreflight(@NonNull Context context,
+ @NonNull String permission) {
+ return checkPermissionForPreflight(context, permission, Process.myPid(),
+ Process.myUid(), context.getPackageName());
}
/**
* Checks whether the IPC you are handling has a given permission and whether
* the app op that corresponds to this permission is allowed.
*
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * point where you will deliver the permission protected data to clients.
+ *
+ * <p>For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use {@link #checkCallingPermissionForPreflight(Context, String, String)}
+ * to determine if the app has or may have location permission (if app has only foreground
+ * location the grant state depends on the app's fg/gb state) and this check will not
+ * leave a trace that permission protected data was delivered. When you are about to
+ * deliver the location data to a registered listener you should use this method which
+ * will evaluate the permission access based on the current fg/bg state of the app and
+ * leave a record that the data was accessed.
+ *
* @param context Context for accessing resources.
* @param permission The permission to check.
* @param packageName The package name making the IPC. If null the
* the first package for the calling UID will be used.
- * @param message A message describing the reason the permission was checked
- *
* @return The permission check result which is either {@link #PERMISSION_GRANTED}
* or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ * @param message A message describing the reason the permission was checked
+ *
+ * @see #checkCallingPermissionForPreflight(Context, String, String)
*/
@PermissionResult
- public static int checkCallingPermission(@NonNull Context context,
+ public static int checkCallingPermissionForDataDelivery(@NonNull Context context,
@NonNull String permission, @Nullable String packageName, @Nullable String message) {
if (Binder.getCallingPid() == Process.myPid()) {
return PERMISSION_DENIED;
}
- return checkPermission(context, permission, Binder.getCallingPid(),
+ return checkPermissionForDataDelivery(context, permission, Binder.getCallingPid(),
Binder.getCallingUid(), packageName, message);
}
/**
- * @deprecated Use {@link #checkCallingOrSelfPermission(Context, String, String)} instead
+ * Checks whether the IPC you are handling has a given permission and whether
+ * the app op that corresponds to this permission is allowed.
+ *
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * preflight point where you will not deliver the permission protected data
+ * to clients but schedule permission data delivery, apps register listeners,
+ * etc.
+ *
+ * <p>For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use this method to determine if the app has or may have location
+ * permission (if app has only foreground location the grant state depends on the app's
+ * fg/gb state) and this check will not leave a trace that permission protected data
+ * was delivered. When you are about to deliver the location data to a registered
+ * listener you should use {@link #checkCallingOrSelfPermissionForDataDelivery(Context,
+ * String, String)} which will evaluate the permission access based on the current fg/bg state
+ * of the app and leave a record that the data was accessed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @param packageName The package name making the IPC. If null the
+ * the first package for the calling UID will be used.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ *
+ * @see #checkCallingPermissionForDataDelivery(Context, String, String, String)
*/
- @Deprecated
@PermissionResult
- public static int checkCallingOrSelfPermission(@NonNull Context context,
- @NonNull String permission) {
- return checkCallingOrSelfPermission(context, permission, null);
+ public static int checkCallingPermissionForPreflight(@NonNull Context context,
+ @NonNull String permission, @Nullable String packageName) {
+ if (Binder.getCallingPid() == Process.myPid()) {
+ return PERMISSION_DENIED;
+ }
+ return checkPermissionForPreflight(context, permission, Binder.getCallingPid(),
+ Binder.getCallingUid(), packageName);
}
/**
* Checks whether the IPC you are handling or your app has a given permission
* and whether the app op that corresponds to this permission is allowed.
*
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * point where you will deliver the permission protected data to clients.
+ *
+ * <p>For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use {@link #checkCallingOrSelfPermissionForPreflight(Context, String)}
+ * to determine if the app has or may have location permission (if app has only foreground
+ * location the grant state depends on the app's fg/gb state) and this check will not
+ * leave a trace that permission protected data was delivered. When you are about to
+ * deliver the location data to a registered listener you should use this method which
+ * will evaluate the permission access based on the current fg/bg state of the app and
+ * leave a record that the data was accessed.
+ *
* @param context Context for accessing resources.
* @param permission The permission to check.
- * @param message A message describing the reason the permission was checked
- *
* @return The permission check result which is either {@link #PERMISSION_GRANTED}
* or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ * @param message A message describing the reason the permission was checked
+ *
+ * @see #checkCallingOrSelfPermissionForPreflight(Context, String)
*/
@PermissionResult
- public static int checkCallingOrSelfPermission(@NonNull Context context,
+ public static int checkCallingOrSelfPermissionForDataDelivery(@NonNull Context context,
@NonNull String permission, @Nullable String message) {
String packageName = (Binder.getCallingPid() == Process.myPid())
? context.getPackageName() : null;
- return checkPermission(context, permission, Binder.getCallingPid(),
+ return checkPermissionForDataDelivery(context, permission, Binder.getCallingPid(),
Binder.getCallingUid(), packageName, message);
}
+
+ /**
+ * Checks whether the IPC you are handling or your app has a given permission
+ * and whether the app op that corresponds to this permission is allowed.
+ *
+ * <strong>NOTE:</strong> Use this method only for permission checks at the
+ * preflight point where you will not deliver the permission protected data
+ * to clients but schedule permission data delivery, apps register listeners,
+ * etc.
+ *
+ * <p>For example, if an app registers a location listener it should have the location
+ * permission but no data is actually sent to the app at the moment of registration
+ * and you should use this method to determine if the app has or may have location
+ * permission (if app has only foreground location the grant state depends on the
+ * app's fg/gb state) and this check will not leave a trace that permission protected
+ * data was delivered. When you are about to deliver the location data to a registered
+ * listener you should use {@link #checkCallingOrSelfPermissionForDataDelivery(Context,
+ * String, String)} which will evaluate the permission access based on the current fg/bg state
+ * of the app and leave a record that the data was accessed.
+ *
+ * @param context Context for accessing resources.
+ * @param permission The permission to check.
+ * @return The permission check result which is either {@link #PERMISSION_GRANTED}
+ * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
+ *
+ * @see #checkCallingOrSelfPermissionForDataDelivery(Context, String, String)
+ */
+ @PermissionResult
+ public static int checkCallingOrSelfPermissionForPreflight(@NonNull Context context,
+ @NonNull String permission) {
+ String packageName = (Binder.getCallingPid() == Process.myPid())
+ ? context.getPackageName() : null;
+ return checkPermissionForPreflight(context, permission, Binder.getCallingPid(),
+ Binder.getCallingUid(), packageName);
+ }
+
+ private static int checkPermissionCommon(@NonNull Context context, @NonNull String permission,
+ int pid, int uid, @Nullable String packageName, @Nullable String message,
+ boolean forDataDelivery) {
+ if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
+ return PERMISSION_DENIED;
+ }
+
+ AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+ String op = appOpsManager.permissionToOp(permission);
+ if (op == null) {
+ return PERMISSION_GRANTED;
+ }
+
+ if (packageName == null) {
+ String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
+ if (packageNames == null || packageNames.length <= 0) {
+ return PERMISSION_DENIED;
+ }
+ packageName = packageNames[0];
+ }
+
+ if (forDataDelivery) {
+ // TODO moltmann: Set correct feature id
+ if (appOpsManager.noteProxyOpNoThrow(op, packageName, uid, null, message)
+ != AppOpsManager.MODE_ALLOWED) {
+ return PERMISSION_DENIED_APP_OP;
+ }
+ } else {
+ final int mode = appOpsManager.unsafeCheckOpRawNoThrow(op, uid, packageName);
+ if (mode != AppOpsManager.MODE_ALLOWED && mode != AppOpsManager.MODE_FOREGROUND) {
+ return PERMISSION_DENIED_APP_OP;
+ }
+ }
+
+ return PERMISSION_GRANTED;
+ }
}
diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl
index 43a4fe5bc414..3d7e3befd9f1 100644
--- a/core/java/android/content/om/IOverlayManager.aidl
+++ b/core/java/android/content/om/IOverlayManager.aidl
@@ -157,4 +157,10 @@ interface IOverlayManager {
* Returns the list of default overlay packages, or an empty array if there are none.
*/
String[] getDefaultOverlayPackages();
+
+ /**
+ * Invalidates and removes the idmap for an overlay,
+ * @param packageName The name of the overlay package whose idmap should be deleted.
+ */
+ void invalidateCachesForOverlay(in String packageName, in int userIs);
}
diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java
index f2716fedc186..a2f8886eb7d2 100644
--- a/core/java/android/content/om/OverlayManager.java
+++ b/core/java/android/content/om/OverlayManager.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -163,4 +164,27 @@ public class OverlayManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Clear part of the overlay manager's internal cache of PackageInfo
+ * objects. Only intended for testing.
+ *
+ * @param targetPackageName The name of the target package.
+ * @param user The user to get the OverlayInfos for.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(anyOf = {
+ "android.permission.INTERACT_ACROSS_USERS",
+ })
+ @NonNull
+ public void invalidateCachesForOverlay(@NonNull final String targetPackageName,
+ @NonNull UserHandle user) {
+ try {
+ mService.invalidateCachesForOverlay(targetPackageName, user.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/content/pm/AndroidTelephonyCommonUpdater.java b/core/java/android/content/pm/AndroidTelephonyCommonUpdater.java
deleted file mode 100644
index 1a720d50f2ce..000000000000
--- a/core/java/android/content/pm/AndroidTelephonyCommonUpdater.java
+++ /dev/null
@@ -1,82 +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 android.content.pm;
-
-import static android.content.pm.SharedLibraryNames.ANDROID_TELEPHONY_COMMON;
-
-
-import com.android.internal.compat.IPlatformCompat;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
-import android.content.pm.PackageParser.Package;
-
-import android.os.Build.VERSION_CODES;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.util.Log;
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * Updates a package to ensure that
- * <ul>
- * <li> if apps have target SDK < R, then telephony-common library is included by default to
- * their class path. Even without <uses-library>.</li>
- * <li> if apps with target SDK level >= R && have special permission (or Phone UID):
- * apply <uses-library> on telephony-common should work.</li>
- * <li> Otherwise not allow to use the lib.
- * See {@link PackageSharedLibraryUpdater#removeLibrary(Package, String)}.</li>
- * </ul>
- *
- * @hide
- */
-@VisibleForTesting
-public class AndroidTelephonyCommonUpdater extends PackageSharedLibraryUpdater {
-
- private static final String TAG = AndroidTelephonyCommonUpdater.class.getSimpleName();
- /**
- * Restrict telephony-common lib for apps having target SDK >= R
- */
- @ChangeId
- @EnabledAfter(targetSdkVersion = VERSION_CODES.Q)
- static final long RESTRICT_TELEPHONY_COMMON_CHANGE_ID = 139318877L;
-
- private static boolean apkTargetsApiLevelLessThanROrCurrent(Package pkg) {
- boolean shouldRestrict = false;
- try {
- IBinder b = ServiceManager.getService("platform_compat");
- IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(b);
- shouldRestrict = platformCompat.isChangeEnabled(RESTRICT_TELEPHONY_COMMON_CHANGE_ID,
- pkg.applicationInfo);
- } catch (RemoteException ex) {
- Log.e(TAG, ex.getMessage());
- }
- // TODO(b/139318877): remove version check for CUR_DEVELOPEMENT after clean up work.
- return !shouldRestrict
- || pkg.applicationInfo.targetSdkVersion == VERSION_CODES.CUR_DEVELOPMENT;
- }
-
- @Override
- public void updatePackage(Package pkg) {
- // for apps with targetSDKVersion < R include the library for backward compatibility.
- if (apkTargetsApiLevelLessThanROrCurrent(pkg)) {
- prefixRequiredLibrary(pkg, ANDROID_TELEPHONY_COMMON);
- } else if (pkg.mSharedUserId == null || !pkg.mSharedUserId.equals("android.uid.phone")) {
- // if apps target >= R
- removeLibrary(pkg, ANDROID_TELEPHONY_COMMON);
- }
- }
-}
diff --git a/core/java/android/content/pm/AndroidTestBaseUpdater.java b/core/java/android/content/pm/AndroidTestBaseUpdater.java
index da1a693b13c3..18d3ba33552e 100644
--- a/core/java/android/content/pm/AndroidTestBaseUpdater.java
+++ b/core/java/android/content/pm/AndroidTestBaseUpdater.java
@@ -18,10 +18,17 @@ package android.content.pm;
import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
+import android.content.Context;
import android.content.pm.PackageParser.Package;
import android.os.Build;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.compat.IPlatformCompat;
/**
* Updates a package to ensure that if it targets <= Q that the android.test.base library is
@@ -37,10 +44,31 @@ import com.android.internal.annotations.VisibleForTesting;
*/
@VisibleForTesting
public class AndroidTestBaseUpdater extends PackageSharedLibraryUpdater {
+ private static final String TAG = "AndroidTestBaseUpdater";
- private static boolean apkTargetsApiLevelLessThanOrEqualToQ(Package pkg) {
- int targetSdkVersion = pkg.applicationInfo.targetSdkVersion;
- return targetSdkVersion <= Build.VERSION_CODES.Q;
+ /**
+ * Remove android.test.base library for apps that target SDK R or more and do not depend on
+ * android.test.runner (as it depends on classes from the android.test.base library).
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+ private static final long REMOVE_ANDROID_TEST_BASE = 133396946L;
+
+ private static boolean isChangeEnabled(Package pkg) {
+ // Do not ask platform compat for system apps to prevent a boot time regression in tests.
+ // b/142558883.
+ if (!pkg.applicationInfo.isSystemApp()) {
+ IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+ try {
+ return platformCompat.isChangeEnabled(REMOVE_ANDROID_TEST_BASE,
+ pkg.applicationInfo);
+ } catch (RemoteException | NullPointerException e) {
+ Log.e(TAG, "Failed to get a response from PLATFORM_COMPAT_SERVICE", e);
+ }
+ }
+ // Fall back to previous behaviour.
+ return pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.Q;
}
@Override
@@ -48,7 +76,7 @@ public class AndroidTestBaseUpdater extends PackageSharedLibraryUpdater {
// Packages targeted at <= Q expect the classes in the android.test.base library
// to be accessible so this maintains backward compatibility by adding the
// android.test.base library to those packages.
- if (apkTargetsApiLevelLessThanOrEqualToQ(pkg)) {
+ if (!isChangeEnabled(pkg)) {
prefixRequiredLibrary(pkg, ANDROID_TEST_BASE);
} else {
// If a package already depends on android.test.runner then add a dependency on
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index c6beee2f898a..1d78e2c36cd3 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -50,6 +50,7 @@ import android.content.pm.VersionedPackage;
import android.content.pm.dex.IArtManager;
import android.graphics.Bitmap;
import android.net.Uri;
+import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.content.IntentSender;
@@ -280,7 +281,7 @@ interface IPackageManager {
boolean isPackageSuspendedForUser(String packageName, int userId);
- PersistableBundle getSuspendedPackageAppExtras(String packageName, int userId);
+ Bundle getSuspendedPackageAppExtras(String packageName, int userId);
/**
* Backup/restore support - only the system uid may use these.
@@ -681,10 +682,14 @@ interface IPackageManager {
String getWellbeingPackageName();
+ String[] getTelephonyPackageNames();
+
String getAppPredictionServicePackageName();
String getSystemCaptionsServicePackageName();
+ String getSetupWizardPackageName();
+
String getIncidentReportApproverPackageName();
boolean isPackageStateProtected(String packageName, int userId);
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 3933e81c732a..d2a40304e078 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -248,7 +248,11 @@ public class LauncherApps {
* system, {@code null} otherwise.
* @see PackageManager#isPackageSuspended()
* @see #getSuspendedPackageLauncherExtras(String, UserHandle)
+ * @deprecated {@code launcherExtras} should be obtained by using
+ * {@link #getSuspendedPackageLauncherExtras(String, UserHandle)}. For all other cases,
+ * {@link #onPackagesSuspended(String[], UserHandle)} should be used.
*/
+ @Deprecated
public void onPackagesSuspended(String[] packageNames, UserHandle user,
@Nullable Bundle launcherExtras) {
onPackagesSuspended(packageNames, user);
diff --git a/core/java/android/content/pm/PackageBackwardCompatibility.java b/core/java/android/content/pm/PackageBackwardCompatibility.java
index 797ba64b5d1f..4331bd4ac4d4 100644
--- a/core/java/android/content/pm/PackageBackwardCompatibility.java
+++ b/core/java/android/content/pm/PackageBackwardCompatibility.java
@@ -51,8 +51,6 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater {
packageUpdaters.add(new AndroidHidlUpdater());
- packageUpdaters.add(new AndroidTelephonyCommonUpdater());
-
// Add this before adding AndroidTestBaseUpdater so that android.test.base comes before
// android.test.mock.
packageUpdaters.add(new AndroidTestRunnerSplitUpdater());
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 1099d8bdc7dd..edc66c58b197 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -101,19 +101,6 @@ import java.util.concurrent.Executor;
* <p>
* The ApiDemos project contains examples of using this API:
* <code>ApiDemos/src/com/example/android/apis/content/InstallApk*.java</code>.
- * <p>
- * On Android Q or above, an app installed notification will be posted
- * by system after a new app is installed.
- * To customize installer's notification icon, you should declare the following in the manifest
- * &lt;application> as follows: </p>
- * <pre>
- * &lt;meta-data android:name="com.android.packageinstaller.notification.smallIcon"
- * android:resource="@drawable/installer_notification_icon"/>
- * </pre>
- * <pre>
- * &lt;meta-data android:name="com.android.packageinstaller.notification.color"
- * android:resource="@color/installer_notification_color"/>
- * </pre>
*/
public class PackageInstaller {
private static final String TAG = "PackageInstaller";
@@ -362,14 +349,7 @@ public class PackageInstaller {
*/
public int createSession(@NonNull SessionParams params) throws IOException {
try {
- final String installerPackage;
- if (params.installerPackageName == null) {
- installerPackage = mInstallerPackageName;
- } else {
- installerPackage = params.installerPackageName;
- }
-
- return mInstaller.createSession(params, installerPackage, mUserId);
+ return mInstaller.createSession(params, mInstallerPackageName, mUserId);
} catch (RuntimeException e) {
ExceptionUtils.maybeUnwrapIOException(e);
throw e;
diff --git a/core/java/android/content/pm/PackageList.java b/core/java/android/content/pm/PackageList.java
deleted file mode 100644
index e3eb2c55a2bb..000000000000
--- a/core/java/android/content/pm/PackageList.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.pm.PackageManagerInternal.PackageListObserver;
-
-import com.android.server.LocalServices;
-
-import java.util.List;
-
-/**
- * All of the package name installed on the system.
- * <p>A self observable list that automatically removes the listener when it goes out of scope.
- *
- * @hide Only for use within the system server.
- */
-public class PackageList implements PackageListObserver, AutoCloseable {
- private final PackageListObserver mWrappedObserver;
- private final List<String> mPackageNames;
-
- /**
- * Create a new object.
- * <p>Ownership of the given {@link List} transfers to this object and should not
- * be modified by the caller.
- */
- public PackageList(@NonNull List<String> packageNames, @Nullable PackageListObserver observer) {
- mPackageNames = packageNames;
- mWrappedObserver = observer;
- }
-
- @Override
- public void onPackageAdded(String packageName, int uid) {
- if (mWrappedObserver != null) {
- mWrappedObserver.onPackageAdded(packageName, uid);
- }
- }
-
- @Override
- public void onPackageChanged(String packageName, int uid) {
- if (mWrappedObserver != null) {
- mWrappedObserver.onPackageChanged(packageName, uid);
- }
- }
-
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- if (mWrappedObserver != null) {
- mWrappedObserver.onPackageRemoved(packageName, uid);
- }
- }
-
- @Override
- public void close() throws Exception {
- LocalServices.getService(PackageManagerInternal.class).removePackageListObserver(this);
- }
-
- /**
- * Returns the names of packages installed on the system.
- * <p>The list is a copy-in-time and the actual set of installed packages may differ. Real
- * time updates to the package list are sent via the {@link PackageListObserver} callback.
- */
- public @NonNull List<String> getPackageNames() {
- return mPackageNames;
- }
-}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8dfe00a8adf6..7509065a34b1 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -917,8 +917,9 @@ public abstract class PackageManager {
public static final int INSTALL_DRY_RUN = 0x00800000;
/** @hide */
- @IntDef(flag = true, prefix = { "DONT_KILL_APP" }, value = {
- DONT_KILL_APP
+ @IntDef(flag = true, value = {
+ DONT_KILL_APP,
+ SYNCHRONOUS
})
@Retention(RetentionPolicy.SOURCE)
public @interface EnabledFlags {}
@@ -931,6 +932,14 @@ public abstract class PackageManager {
*/
public static final int DONT_KILL_APP = 0x00000001;
+ /**
+ * Flag parameter for
+ * {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate
+ * that the given user's package restrictions state will be serialised to disk after the
+ * component state has been updated.
+ */
+ public static final int SYNCHRONOUS = 0x00000002;
+
/** @hide */
@IntDef(prefix = { "INSTALL_REASON_" }, value = {
INSTALL_REASON_UNKNOWN,
@@ -2849,6 +2858,25 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device does not have slices implementation.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_SLICES_DISABLED = "android.software.slices_disabled";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device supports device-unique Keystore attestations. Only available on devices that
+ * also support {@link #FEATURE_STRONGBOX_KEYSTORE}, and can only be used by device owner
+ * apps (see {@link android.app.admin.DevicePolicyManager#generateKeyPair}).
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_DEVICE_UNIQUE_ATTESTATION =
+ "android.hardware.device_unique_attestation";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
* The device has a Keymaster implementation that supports Device ID attestation.
*
* @see DevicePolicyManager#isDeviceIdAttestationSupported
@@ -3078,8 +3106,11 @@ public abstract class PackageManager {
* because the app was updated to support runtime permissions, the
* the permission will be revoked in the upgrade process.
*
+ * @deprecated Renamed to {@link #FLAG_PERMISSION_REVOKED_COMPAT}.
+ *
* @hide
*/
+ @Deprecated
@SystemApi
@TestApi
public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 1 << 3;
@@ -3202,6 +3233,18 @@ public abstract class PackageManager {
public static final int FLAG_PERMISSION_GRANTED_BY_ROLE = 1 << 15;
/**
+ * Permission flag: The permission should have been revoked but is kept granted for
+ * compatibility. The data protected by the permission should be protected by a no-op (empty
+ * list, default error, etc) instead of crashing the client. The permission will be revoked if
+ * the app is upgraded to supports it.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int FLAG_PERMISSION_REVOKED_COMPAT = FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+
+ /**
* Permission flags: Bitwise or of all permission flags allowing an
* exemption for a restricted permission.
* @hide
@@ -3241,7 +3284,8 @@ public abstract class PackageManager {
| FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT
| FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT
| FLAG_PERMISSION_APPLY_RESTRICTION
- | FLAG_PERMISSION_GRANTED_BY_ROLE;
+ | FLAG_PERMISSION_GRANTED_BY_ROLE
+ | FLAG_PERMISSION_REVOKED_COMPAT;
/**
* Injected activity in app that forwards user to setting activity of that app.
@@ -4017,7 +4061,8 @@ public abstract class PackageManager {
FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT,
FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT,
FLAG_PERMISSION_APPLY_RESTRICTION,
- FLAG_PERMISSION_GRANTED_BY_ROLE
+ FLAG_PERMISSION_GRANTED_BY_ROLE,
+ FLAG_PERMISSION_REVOKED_COMPAT
})
@Retention(RetentionPolicy.SOURCE)
public @interface PermissionFlags {}
@@ -7086,7 +7131,6 @@ public abstract class PackageManager {
case FLAG_PERMISSION_POLICY_FIXED: return "POLICY_FIXED";
case FLAG_PERMISSION_SYSTEM_FIXED: return "SYSTEM_FIXED";
case FLAG_PERMISSION_USER_SET: return "USER_SET";
- case FLAG_PERMISSION_REVOKE_ON_UPGRADE: return "REVOKE_ON_UPGRADE";
case FLAG_PERMISSION_USER_FIXED: return "USER_FIXED";
case FLAG_PERMISSION_REVIEW_REQUIRED: return "REVIEW_REQUIRED";
case FLAG_PERMISSION_REVOKE_WHEN_REQUESTED: return "REVOKE_WHEN_REQUESTED";
@@ -7097,6 +7141,7 @@ public abstract class PackageManager {
case FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT: return "RESTRICTION_UPGRADE_EXEMPT";
case FLAG_PERMISSION_APPLY_RESTRICTION: return "APPLY_RESTRICTION";
case FLAG_PERMISSION_GRANTED_BY_ROLE: return "GRANTED_BY_ROLE";
+ case FLAG_PERMISSION_REVOKED_COMPAT: return "REVOKED_COMPAT";
default: return Integer.toString(flag);
}
}
@@ -7379,6 +7424,18 @@ public abstract class PackageManager {
}
/**
+ * @return the system defined telephony package names, or null if there's none.
+ *
+ * @hide
+ */
+ @Nullable
+ @TestApi
+ public String[] getTelephonyPackageNames() {
+ throw new UnsupportedOperationException(
+ "getTelephonyPackageNames not implemented in subclass");
+ }
+
+ /**
* @return the system defined content capture service package name, or null if there's none.
*
* @hide
@@ -7390,6 +7447,17 @@ public abstract class PackageManager {
}
/**
+ * @return the system defined setup wizard package name, or null if there's none.
+ *
+ * @hide
+ */
+ @Nullable
+ public String getSetupWizardPackageName() {
+ throw new UnsupportedOperationException(
+ "getSetupWizardPackageName not implemented in subclass");
+ }
+
+ /**
* @return the incident report approver app package name, or null if it's not defined
* by the OEM.
*
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
deleted file mode 100644
index 24ee21360ed8..000000000000
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ /dev/null
@@ -1,792 +0,0 @@
-/*
- * 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.content.pm;
-
-import android.annotation.AppIdInt;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.content.pm.PackageManager.ApplicationInfoFlags;
-import android.content.pm.PackageManager.ComponentInfoFlags;
-import android.content.pm.PackageManager.PackageInfoFlags;
-import android.content.pm.PackageManager.ResolveInfoFlags;
-import android.os.Bundle;
-import android.os.PersistableBundle;
-import android.util.ArraySet;
-import android.util.SparseArray;
-
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-import java.util.function.Consumer;
-
-/**
- * Package manager local system service interface.
- *
- * @hide Only for use within the system server.
- */
-public abstract class PackageManagerInternal {
- public static final int PACKAGE_SYSTEM = 0;
- public static final int PACKAGE_SETUP_WIZARD = 1;
- public static final int PACKAGE_INSTALLER = 2;
- public static final int PACKAGE_VERIFIER = 3;
- public static final int PACKAGE_BROWSER = 4;
- public static final int PACKAGE_SYSTEM_TEXT_CLASSIFIER = 5;
- public static final int PACKAGE_PERMISSION_CONTROLLER = 6;
- public static final int PACKAGE_WELLBEING = 7;
- public static final int PACKAGE_DOCUMENTER = 8;
- public static final int PACKAGE_CONFIGURATOR = 9;
- public static final int PACKAGE_INCIDENT_REPORT_APPROVER = 10;
- public static final int PACKAGE_APP_PREDICTOR = 11;
- @IntDef(value = {
- PACKAGE_SYSTEM,
- PACKAGE_SETUP_WIZARD,
- PACKAGE_INSTALLER,
- PACKAGE_VERIFIER,
- PACKAGE_BROWSER,
- PACKAGE_SYSTEM_TEXT_CLASSIFIER,
- PACKAGE_PERMISSION_CONTROLLER,
- PACKAGE_WELLBEING,
- PACKAGE_DOCUMENTER,
- PACKAGE_CONFIGURATOR,
- PACKAGE_INCIDENT_REPORT_APPROVER,
- PACKAGE_APP_PREDICTOR,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface KnownPackage {}
-
- /** Observer called whenever the list of packages changes */
- public interface PackageListObserver {
- /** A package was added to the system. */
- void onPackageAdded(@NonNull String packageName, int uid);
- /** A package was changed - either installed for a specific user or updated. */
- default void onPackageChanged(@NonNull String packageName, int uid) {}
- /** A package was removed from the system. */
- void onPackageRemoved(@NonNull String packageName, int uid);
- }
-
- /**
- * Called when the package for the default SMS handler changed
- *
- * @param packageName the new sms package
- * @param userId user for which the change was made
- */
- public void onDefaultSmsAppChanged(String packageName, int userId) {}
-
- /**
- * Called when the package for the default sim call manager changed
- *
- * @param packageName the new sms package
- * @param userId user for which the change was made
- */
- public void onDefaultSimCallManagerAppChanged(String packageName, int userId) {}
-
- /**
- * Sets a list of apps to keep in PM's internal data structures and as APKs even if no user has
- * currently installed it. The apps are not preloaded.
- * @param packageList List of package names to keep cached.
- */
- public abstract void setKeepUninstalledPackages(List<String> packageList);
-
- /**
- * Gets whether some of the permissions used by this package require a user
- * review before any of the app components can run.
- * @param packageName The package name for which to check.
- * @param userId The user under which to check.
- * @return True a permissions review is required.
- */
- public abstract boolean isPermissionsReviewRequired(String packageName, int userId);
-
- /**
- * Retrieve all of the information we know about a particular package/application.
- * @param filterCallingUid The results will be filtered in the context of this UID instead
- * of the calling UID.
- * @see PackageManager#getPackageInfo(String, int)
- */
- public abstract PackageInfo getPackageInfo(String packageName,
- @PackageInfoFlags int flags, int filterCallingUid, int userId);
-
- /**
- * Return a List of all application packages that are installed on the
- * device, for a specific user. If flag GET_UNINSTALLED_PACKAGES has been
- * set, a list of all applications including those deleted with
- * {@code DONT_DELETE_DATA} (partially installed apps with data directory)
- * will be returned.
- *
- * @param flags Additional option flags to modify the data returned.
- * @param userId The user for whom the installed applications are to be
- * listed
- * @param callingUid The uid of the original caller app
- * @return A List of ApplicationInfo objects, one for each installed
- * application. In the unlikely case there are no installed
- * packages, an empty list is returned. If flag
- * {@code MATCH_UNINSTALLED_PACKAGES} is set, the application
- * information is retrieved from the list of uninstalled
- * applications (which includes installed applications as well as
- * applications with data directory i.e. applications which had been
- * deleted with {@code DONT_DELETE_DATA} flag set).
- */
- public abstract List<ApplicationInfo> getInstalledApplications(
- @ApplicationInfoFlags int flags, @UserIdInt int userId, int callingUid);
-
- /**
- * Retrieve launcher extras for a suspended package provided to the system in
- * {@link PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
- * PersistableBundle, String)}.
- *
- * @param packageName The package for which to return launcher extras.
- * @param userId The user for which to check.
- * @return The launcher extras.
- *
- * @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
- * PersistableBundle, String)
- * @see PackageManager#isPackageSuspended()
- */
- public abstract Bundle getSuspendedPackageLauncherExtras(String packageName,
- int userId);
-
- /**
- * Internal api to query the suspended state of a package.
- * @param packageName The package to check.
- * @param userId The user id to check for.
- * @return {@code true} if the package is suspended, {@code false} otherwise.
- * @see PackageManager#isPackageSuspended(String)
- */
- public abstract boolean isPackageSuspended(String packageName, int userId);
-
- /**
- * Get the name of the package that suspended the given package. Packages can be suspended by
- * device administrators or apps holding {@link android.Manifest.permission#MANAGE_USERS} or
- * {@link android.Manifest.permission#SUSPEND_APPS}.
- *
- * @param suspendedPackage The package that has been suspended.
- * @param userId The user for which to check.
- * @return Name of the package that suspended the given package. Returns {@code null} if the
- * given package is not currently suspended and the platform package name - i.e.
- * {@code "android"} - if the package was suspended by a device admin.
- */
- public abstract String getSuspendingPackage(String suspendedPackage, int userId);
-
- /**
- * Get the information describing the dialog to be shown to the user when they try to launch a
- * suspended application.
- *
- * @param suspendedPackage The package that has been suspended.
- * @param userId The user for which to check.
- * @return A {@link SuspendDialogInfo} object describing the dialog to be shown.
- */
- @Nullable
- public abstract SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, int userId);
-
- /**
- * Gets any distraction flags set via
- * {@link PackageManager#setDistractingPackageRestrictions(String[], int)}
- *
- * @param packageName
- * @param userId
- * @return A bitwise OR of any of the {@link PackageManager.DistractionRestriction}
- */
- public abstract @PackageManager.DistractionRestriction int getDistractingPackageRestrictions(
- String packageName, int userId);
-
- /**
- * Do a straight uid lookup for the given package/application in the given user.
- * @see PackageManager#getPackageUidAsUser(String, int, int)
- * @return The app's uid, or < 0 if the package was not found in that user
- */
- public abstract int getPackageUid(String packageName,
- @PackageInfoFlags int flags, int userId);
-
- /**
- * Retrieve all of the information we know about a particular package/application.
- * @param filterCallingUid The results will be filtered in the context of this UID instead
- * of the calling UID.
- * @see PackageManager#getApplicationInfo(String, int)
- */
- public abstract ApplicationInfo getApplicationInfo(String packageName,
- @ApplicationInfoFlags int flags, int filterCallingUid, int userId);
-
- /**
- * Retrieve all of the information we know about a particular activity class.
- * @param filterCallingUid The results will be filtered in the context of this UID instead
- * of the calling UID.
- * @see PackageManager#getActivityInfo(ComponentName, int)
- */
- public abstract ActivityInfo getActivityInfo(ComponentName component,
- @ComponentInfoFlags int flags, int filterCallingUid, int userId);
-
- /**
- * Retrieve all activities that can be performed for the given intent.
- * @param filterCallingUid The results will be filtered in the context of this UID instead
- * of the calling UID.
- * @see PackageManager#queryIntentActivities(Intent, int)
- */
- public abstract List<ResolveInfo> queryIntentActivities(Intent intent,
- @ResolveInfoFlags int flags, int filterCallingUid, int userId);
-
- /**
- * Retrieve all services that can be performed for the given intent.
- * @see PackageManager#queryIntentServices(Intent, int)
- */
- public abstract List<ResolveInfo> queryIntentServices(
- Intent intent, int flags, int callingUid, int userId);
-
- /**
- * Interface to {@link com.android.server.pm.PackageManagerService#getHomeActivitiesAsUser}.
- */
- public abstract ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
- int userId);
-
- /**
- * @return The default home activity component name.
- */
- public abstract ComponentName getDefaultHomeActivity(int userId);
-
- /**
- * Called by DeviceOwnerManagerService to set the package names of device owner and profile
- * owners.
- */
- public abstract void setDeviceAndProfileOwnerPackages(
- int deviceOwnerUserId, String deviceOwner, SparseArray<String> profileOwners);
-
- /**
- * Returns {@code true} if a given package can't be wiped. Otherwise, returns {@code false}.
- */
- public abstract boolean isPackageDataProtected(int userId, String packageName);
-
- /**
- * Returns {@code true} if a given package's state is protected, e.g. it cannot be force
- * stopped, suspended, disabled or hidden. Otherwise, returns {@code false}.
- */
- public abstract boolean isPackageStateProtected(String packageName, int userId);
-
- /**
- * Returns {@code true} if a given package is installed as ephemeral. Otherwise, returns
- * {@code false}.
- */
- public abstract boolean isPackageEphemeral(int userId, String packageName);
-
- /**
- * Gets whether the package was ever launched.
- * @param packageName The package name.
- * @param userId The user for which to check.
- * @return Whether was launched.
- * @throws IllegalArgumentException if the package is not found
- */
- public abstract boolean wasPackageEverLaunched(String packageName, int userId);
-
- /**
- * Retrieve the official name associated with a uid. This name is
- * guaranteed to never change, though it is possible for the underlying
- * uid to be changed. That is, if you are storing information about
- * uids in persistent storage, you should use the string returned
- * by this function instead of the raw uid.
- *
- * @param uid The uid for which you would like to retrieve a name.
- * @return Returns a unique name for the given uid, or null if the
- * uid is not currently assigned.
- */
- public abstract String getNameForUid(int uid);
-
- /**
- * Request to perform the second phase of ephemeral resolution.
- * @param responseObj The response of the first phase of ephemeral resolution
- * @param origIntent The original intent that triggered ephemeral resolution
- * @param resolvedType The resolved type of the intent
- * @param callingPackage The name of the package requesting the ephemeral application
- * @param verificationBundle Optional bundle to pass to the installer for additional
- * verification
- * @param userId The ID of the user that triggered ephemeral resolution
- */
- public abstract void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
- Intent origIntent, String resolvedType, String callingPackage,
- Bundle verificationBundle, int userId);
-
- /**
- * Grants implicit access based on an interaction between two apps. This grants the target app
- * access to the calling application's package metadata.
- * <p>
- * When an application explicitly tries to interact with another application [via an
- * activity, service or provider that is either declared in the caller's
- * manifest via the {@code <queries>} tag or has been exposed via the target apps manifest using
- * the {@code visibleToInstantApp} attribute], the target application must be able to see
- * metadata about the calling app. If the calling application uses an implicit intent [ie
- * action VIEW, category BROWSABLE], it remains hidden from the launched app.
- * <p>
- * @param userId the user
- * @param intent the intent that triggered the grant
- * @param callingAppId The app ID of the calling application
- * @param targetAppId The app ID of the target application
- */
- public abstract void grantImplicitAccess(
- @UserIdInt int userId, Intent intent, @AppIdInt int callingAppId,
- @AppIdInt int targetAppId);
-
- public abstract boolean isInstantAppInstallerComponent(ComponentName component);
- /**
- * Prunes instant apps and state associated with uninstalled
- * instant apps according to the current platform policy.
- */
- public abstract void pruneInstantApps();
-
- /**
- * @return The SetupWizard package name.
- */
- public abstract String getSetupWizardPackageName();
-
- public interface ExternalSourcesPolicy {
-
- int USER_TRUSTED = 0; // User has trusted the package to install apps
- int USER_BLOCKED = 1; // User has blocked the package to install apps
- int USER_DEFAULT = 2; // Default code to use when user response is unavailable
-
- /**
- * Checks the user preference for whether a package is trusted to request installs through
- * package installer
- *
- * @param packageName The package to check for
- * @param uid the uid in which the package is running
- * @return {@link #USER_TRUSTED} if the user has trusted the package, {@link #USER_BLOCKED}
- * if user has blocked requests from the package, {@link #USER_DEFAULT} if the user response
- * is not yet available
- */
- int getPackageTrustedToInstallApps(String packageName, int uid);
- }
-
- public abstract void setExternalSourcesPolicy(ExternalSourcesPolicy policy);
-
- /**
- * Return true if the given package is a persistent app process.
- */
- public abstract boolean isPackagePersistent(String packageName);
-
- /**
- * Returns whether or not the given package represents a legacy system application released
- * prior to runtime permissions.
- */
- public abstract boolean isLegacySystemApp(PackageParser.Package pkg);
-
- /**
- * Get all overlay packages for a user.
- * @param userId The user for which to get the overlays.
- * @return A list of overlay packages. An empty list is returned if the
- * user has no installed overlay packages.
- */
- public abstract List<PackageInfo> getOverlayPackages(int userId);
-
- /**
- * Get the names of all target packages for a user.
- * @param userId The user for which to get the package names.
- * @return A list of target package names. This list includes the "android" package.
- */
- public abstract List<String> getTargetPackageNames(int userId);
-
- /**
- * Set which overlay to use for a package.
- * @param userId The user for which to update the overlays.
- * @param targetPackageName The package name of the package for which to update the overlays.
- * @param overlayPackageNames The complete list of overlay packages that should be enabled for
- * the target. Previously enabled overlays not specified in the list
- * will be disabled. Pass in null or an empty list to disable
- * all overlays. The order of the items is significant if several
- * overlays modify the same resource.
- * @return true if all packages names were known by the package manager, false otherwise
- */
- public abstract boolean setEnabledOverlayPackages(int userId, String targetPackageName,
- List<String> overlayPackageNames);
-
- /**
- * Resolves an activity intent, allowing instant apps to be resolved.
- */
- public abstract ResolveInfo resolveIntent(Intent intent, String resolvedType,
- int flags, int userId, boolean resolveForStart, int filterCallingUid);
-
- /**
- * Resolves a service intent, allowing instant apps to be resolved.
- */
- public abstract ResolveInfo resolveService(Intent intent, String resolvedType,
- int flags, int userId, int callingUid);
-
- /**
- * Resolves a content provider intent.
- */
- public abstract ProviderInfo resolveContentProvider(String name, int flags, int userId);
-
- /**
- * Track the creator of a new isolated uid.
- * @param isolatedUid The newly created isolated uid.
- * @param ownerUid The uid of the app that created the isolated process.
- */
- public abstract void addIsolatedUid(int isolatedUid, int ownerUid);
-
- /**
- * Track removal of an isolated uid.
- * @param isolatedUid isolated uid that is no longer being used.
- */
- public abstract void removeIsolatedUid(int isolatedUid);
-
- /**
- * Return the taget SDK version for the app with the given UID.
- */
- public abstract int getUidTargetSdkVersion(int uid);
-
- /**
- * Return the taget SDK version for the app with the given package name.
- */
- public abstract int getPackageTargetSdkVersion(String packageName);
-
- /** Whether the binder caller can access instant apps. */
- public abstract boolean canAccessInstantApps(int callingUid, int userId);
-
- /** Whether the binder caller can access the given component. */
- public abstract boolean canAccessComponent(int callingUid, ComponentName component, int userId);
-
- /**
- * Returns {@code true} if a given package has instant application meta-data.
- * Otherwise, returns {@code false}. Meta-data is state (eg. cookie, app icon, etc)
- * associated with an instant app. It may be kept after the instant app has been uninstalled.
- */
- public abstract boolean hasInstantApplicationMetadata(String packageName, int userId);
-
- /**
- * Updates a package last used time.
- */
- public abstract void notifyPackageUse(String packageName, int reason);
-
- /**
- * Returns a package object for the given package name.
- */
- public abstract @Nullable PackageParser.Package getPackage(@NonNull String packageName);
-
- /**
- * Returns a package for the given UID. If the UID is part of a shared user ID, one
- * of the packages will be chosen to be returned.
- */
- public abstract @Nullable PackageParser.Package getPackage(int uid);
-
- /**
- * Returns a list without a change observer.
- *
- * @see #getPackageList(PackageListObserver)
- */
- public @NonNull PackageList getPackageList() {
- return getPackageList(null);
- }
-
- /**
- * Returns the list of packages installed at the time of the method call.
- * <p>The given observer is notified when the list of installed packages
- * changes [eg. a package was installed or uninstalled]. It will not be
- * notified if a package is updated.
- * <p>The package list will not be updated automatically as packages are
- * installed / uninstalled. Any changes must be handled within the observer.
- */
- public abstract @NonNull PackageList getPackageList(@Nullable PackageListObserver observer);
-
- /**
- * Removes the observer.
- * <p>Generally not needed. {@link #getPackageList(PackageListObserver)} will automatically
- * remove the observer.
- * <p>Does nothing if the observer isn't currently registered.
- * <p>Observers are notified asynchronously and it's possible for an observer to be
- * invoked after its been removed.
- */
- public abstract void removePackageListObserver(@NonNull PackageListObserver observer);
-
- /**
- * Returns a package object for the disabled system package name.
- */
- public abstract @Nullable PackageParser.Package getDisabledSystemPackage(
- @NonNull String packageName);
-
- /**
- * Returns the package name for the disabled system package.
- *
- * This is equivalent to
- * {@link #getDisabledSystemPackage(String)}.{@link PackageParser.Package#packageName}
- */
- public abstract @Nullable String getDisabledSystemPackageName(@NonNull String packageName);
-
- /**
- * Returns whether or not the component is the resolver activity.
- */
- public abstract boolean isResolveActivityComponent(@NonNull ComponentInfo component);
-
- /**
- * Returns the package name for a known package.
- */
- public abstract @Nullable String getKnownPackageName(
- @KnownPackage int knownPackage, int userId);
-
- /**
- * Returns whether the package is an instant app.
- */
- public abstract boolean isInstantApp(String packageName, int userId);
-
- /**
- * Returns whether the package is an instant app.
- */
- public abstract @Nullable String getInstantAppPackageName(int uid);
-
- /**
- * Returns whether or not access to the application should be filtered.
- * <p>
- * Access may be limited based upon whether the calling or target applications
- * are instant applications.
- *
- * @see #canAccessInstantApps
- */
- public abstract boolean filterAppAccess(
- @NonNull PackageParser.Package pkg, int callingUid, int userId);
-
- /**
- * Returns whether or not access to the application should be filtered.
- *
- * @see #filterAppAccess(android.content.pm.PackageParser.Package, int, int)
- */
- public abstract boolean filterAppAccess(
- @NonNull String packageName, int callingUid, int userId);
-
- /** Returns whether the given package was signed by the platform */
- public abstract boolean isPlatformSigned(String pkg);
-
- /**
- * Returns true if it's still safe to restore data backed up from this app's version
- * that was signed with restoringFromSigHash.
- */
- public abstract boolean isDataRestoreSafe(@NonNull byte[] restoringFromSigHash,
- @NonNull String packageName);
-
- /**
- * Returns true if it's still safe to restore data backed up from this app's version
- * that was signed with restoringFromSig.
- */
- public abstract boolean isDataRestoreSafe(@NonNull Signature restoringFromSig,
- @NonNull String packageName);
-
- /**
- * Returns {@code true} if the the signing information for {@code clientUid} is sufficient
- * to gain access gated by {@code capability}. This can happen if the two UIDs have the
- * same signing information, if the signing information {@code clientUid} indicates that
- * it has the signing certificate for {@code serverUid} in its signing history (if it was
- * previously signed by it), or if the signing certificate for {@code clientUid} is in the
- * signing history for {@code serverUid} and with the {@code capability} specified.
- */
- public abstract boolean hasSignatureCapability(int serverUid, int clientUid,
- @PackageParser.SigningDetails.CertCapabilities int capability);
-
- /**
- * Get appIds of all available apps which specified android:sharedUserId in the manifest.
- *
- * @return a SparseArray mapping from appId to it's sharedUserId.
- */
- public abstract SparseArray<String> getAppsWithSharedUserIds();
-
- /**
- * Get the value of attribute android:sharedUserId for the given packageName if specified,
- * otherwise {@code null}.
- */
- public abstract String getSharedUserIdForPackage(@NonNull String packageName);
-
- /**
- * Get all packages which specified the given sharedUserId as android:sharedUserId attribute
- * or an empty array if no package specified it.
- */
- public abstract String[] getPackagesForSharedUserId(@NonNull String sharedUserId, int userId);
-
- /**
- * Return if device is currently in a "core" boot environment, typically
- * used to support full-disk encryption. Only apps marked with
- * {@code coreApp} attribute are available.
- */
- public abstract boolean isOnlyCoreApps();
-
- /**
- * Make a best-effort attempt to provide the requested free disk space by
- * deleting cached files.
- *
- * @throws IOException if the request was unable to be fulfilled.
- */
- public abstract void freeStorage(String volumeUuid, long bytes, int storageFlags)
- throws IOException;
-
- /** Returns {@code true} if the specified component is enabled and matches the given flags. */
- public abstract boolean isEnabledAndMatches(@NonNull ComponentInfo info, int flags, int userId);
-
- /** Returns {@code true} if the given user requires extra badging for icons. */
- public abstract boolean userNeedsBadging(int userId);
-
- /**
- * Perform the given action for each package.
- * Note that packages lock will be held while performin the actions.
- *
- * @param actionLocked action to be performed
- */
- public abstract void forEachPackage(Consumer<PackageParser.Package> actionLocked);
-
- /**
- * Perform the given action for each installed package for a user.
- * Note that packages lock will be held while performin the actions.
- */
- public abstract void forEachInstalledPackage(
- @NonNull Consumer<PackageParser.Package> actionLocked, @UserIdInt int userId);
-
- /** Returns the list of enabled components */
- public abstract ArraySet<String> getEnabledComponents(String packageName, int userId);
-
- /** Returns the list of disabled components */
- public abstract ArraySet<String> getDisabledComponents(String packageName, int userId);
-
- /** Returns whether the given package is enabled for the given user */
- public abstract @PackageManager.EnabledState int getApplicationEnabledState(
- String packageName, int userId);
-
- /**
- * Extra field name for the token of a request to enable rollback for a
- * package.
- */
- public static final String EXTRA_ENABLE_ROLLBACK_TOKEN =
- "android.content.pm.extra.ENABLE_ROLLBACK_TOKEN";
-
- /**
- * Extra field name for the installFlags of a request to enable rollback
- * for a package.
- */
- public static final String EXTRA_ENABLE_ROLLBACK_INSTALL_FLAGS =
- "android.content.pm.extra.ENABLE_ROLLBACK_INSTALL_FLAGS";
-
- /**
- * Extra field name for the user id an install is associated with when
- * enabling rollback.
- */
- public static final String EXTRA_ENABLE_ROLLBACK_USER =
- "android.content.pm.extra.ENABLE_ROLLBACK_USER";
-
- /**
- * Used as the {@code enableRollbackCode} argument for
- * {@link PackageManagerInternal#setEnableRollbackCode} to indicate that
- * enabling rollback succeeded.
- */
- public static final int ENABLE_ROLLBACK_SUCCEEDED = 1;
-
- /**
- * Used as the {@code enableRollbackCode} argument for
- * {@link PackageManagerInternal#setEnableRollbackCode} to indicate that
- * enabling rollback failed.
- */
- public static final int ENABLE_ROLLBACK_FAILED = -1;
-
- /**
- * Allows the rollback manager listening to the
- * {@link Intent#ACTION_PACKAGE_ENABLE_ROLLBACK enable rollback broadcast}
- * to respond to the package manager. The response must include the
- * {@code enableRollbackCode} which is one of
- * {@link PackageManager#ENABLE_ROLLBACK_SUCCEEDED} or
- * {@link PackageManager#ENABLE_ROLLBACK_FAILED}.
- *
- * @param token pending package identifier as passed via the
- * {@link PackageManager#EXTRA_ENABLE_ROLLBACK_TOKEN} Intent extra.
- * @param enableRollbackCode the status code result of enabling rollback
- * @throws SecurityException if the caller does not have the
- * PACKAGE_ROLLBACK_AGENT permission.
- */
- public abstract void setEnableRollbackCode(int token, int enableRollbackCode);
-
- /**
- * Ask the package manager to compile layouts in the given package.
- */
- public abstract boolean compileLayouts(String packageName);
-
- /*
- * Inform the package manager that the pending package install identified by
- * {@code token} can be completed.
- */
- public abstract void finishPackageInstall(int token, boolean didLaunch);
-
- /**
- * Remove the default browser stored in the legacy package settings.
- *
- * @param userId the user id
- *
- * @return the package name of the default browser, or {@code null} if none
- */
- @Nullable
- public abstract String removeLegacyDefaultBrowserPackageName(int userId);
-
- /**
- * Returns {@code true} if given {@code packageName} is an apex package.
- */
- public abstract boolean isApexPackage(String packageName);
-
- /**
- * Uninstalls given {@code packageName}.
- *
- * @param packageName apex package to uninstall.
- * @param versionCode version of a package to uninstall.
- * @param userId user to uninstall apex package for. Must be
- * {@link android.os.UserHandle#USER_ALL}, otherwise failure will be reported.
- * @param intentSender a {@link IntentSender} to send result of an uninstall to.
- */
- public abstract void uninstallApex(String packageName, long versionCode, int userId,
- IntentSender intentSender);
-
- /**
- * Get fingerprint of build that updated the runtime permissions for a user.
- *
- * @param userId The user to update
- * @param fingerPrint The fingerprint to set
- */
- public abstract void setRuntimePermissionsFingerPrint(@NonNull String fingerPrint,
- @UserIdInt int userId);
-
- /**
- * Migrates legacy obb data to its new location.
- */
- public abstract void migrateLegacyObbData();
-
- /**
- * Writes all package manager settings to disk. If {@code async} is {@code true}, the
- * settings are written at some point in the future. Otherwise, the call blocks until
- * the settings have been written.
- */
- public abstract void writeSettings(boolean async);
-
- /**
- * Writes all permission settings for the given set of users to disk. If {@code async}
- * is {@code true}, the settings are written at some point in the future. Otherwise,
- * the call blocks until the settings have been written.
- */
- public abstract void writePermissionSettings(@NonNull @UserIdInt int[] userIds, boolean async);
-
- /**
- * Returns {@code true} if the caller is the installer of record for the given package.
- * Otherwise, {@code false}.
- */
- public abstract boolean isCallerInstallerOfRecord(
- @NonNull PackageParser.Package pkg, int callingUid);
-
- /** Returns whether or not default runtime permissions are granted for the given user */
- public abstract boolean areDefaultRuntimePermissionsGranted(@UserIdInt int userId);
-
- /** Sets the enforcement of reading external storage */
- public abstract void setReadExternalStorageEnforced(boolean enforced);
-}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 0b157fa3bb1e..cf21e9665c71 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -577,8 +577,6 @@ public class PackageParser {
*/
public interface Callback {
boolean hasFeature(String feature);
- String[] getOverlayPaths(String targetPackageName, String targetPath);
- String[] getOverlayApks(String targetPackageName);
}
/**
@@ -595,14 +593,6 @@ public class PackageParser {
@Override public boolean hasFeature(String feature) {
return mPm.hasSystemFeature(feature);
}
-
- @Override public String[] getOverlayPaths(String targetPackageName, String targetPath) {
- return null;
- }
-
- @Override public String[] getOverlayApks(String targetPackageName) {
- return null;
- }
}
/**
@@ -1195,19 +1185,7 @@ public class PackageParser {
}
final byte[] bytes = IoUtils.readFileAsByteArray(cacheFile.getAbsolutePath());
- Package p = fromCacheEntry(bytes);
- if (mCallback != null) {
- String[] overlayApks = mCallback.getOverlayApks(p.packageName);
- if (overlayApks != null && overlayApks.length > 0) {
- for (String overlayApk : overlayApks) {
- // If a static RRO is updated, return null.
- if (!isCacheUpToDate(new File(overlayApk), cacheFile)) {
- return null;
- }
- }
- }
- }
- return p;
+ return fromCacheEntry(bytes);
} catch (Throwable e) {
Slog.w(TAG, "Error reading package cache: ", e);
@@ -1381,7 +1359,7 @@ public class PackageParser {
final Resources res = new Resources(assets, mMetrics, null);
final String[] outError = new String[1];
- final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
+ final Package pkg = parseBaseApk(res, parser, flags, outError);
if (pkg == null) {
throw new PackageParserException(mParseError,
apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
@@ -1944,7 +1922,6 @@ public class PackageParser {
* need to consider whether they should be supported by split APKs and child
* packages.
*
- * @param apkPath The package apk file path
* @param res The resources from which to resolve values
* @param parser The manifest parser
* @param flags Flags how to parse
@@ -1954,8 +1931,7 @@ public class PackageParser {
* @throws XmlPullParserException
* @throws IOException
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
+ private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
String[] outError) throws XmlPullParserException, IOException {
final String splitName;
final String pkgName;
@@ -1975,15 +1951,6 @@ public class PackageParser {
return null;
}
- if (mCallback != null) {
- String[] overlayPaths = mCallback.getOverlayPaths(pkgName, apkPath);
- if (overlayPaths != null && overlayPaths.length > 0) {
- for (String overlayPath : overlayPaths) {
- res.getAssets().addOverlayPath(overlayPath);
- }
- }
- }
-
final Package pkg = new Package(pkgName);
TypedArray sa = res.obtainAttributes(parser,
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 249b6919fc28..5c74efb8ff1b 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -31,6 +31,7 @@ import android.annotation.UnsupportedAppUsage;
import android.os.BaseBundle;
import android.os.Debug;
import android.os.PersistableBundle;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.Slog;
@@ -38,6 +39,11 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
import java.util.Arrays;
import java.util.Objects;
@@ -56,10 +62,7 @@ public class PackageUserState {
public boolean hidden; // Is the app restricted by owner / admin
public int distractionFlags;
public boolean suspended;
- public String suspendingPackage;
- public SuspendDialogInfo dialogInfo;
- public PersistableBundle suspendedAppExtras;
- public PersistableBundle suspendedLauncherExtras;
+ public ArrayMap<String, SuspendParams> suspendParams; // Suspending package to suspend params
public boolean instantApp;
public boolean virtualPreload;
public int enabled;
@@ -95,10 +98,7 @@ public class PackageUserState {
hidden = o.hidden;
distractionFlags = o.distractionFlags;
suspended = o.suspended;
- suspendingPackage = o.suspendingPackage;
- dialogInfo = o.dialogInfo;
- suspendedAppExtras = o.suspendedAppExtras;
- suspendedLauncherExtras = o.suspendedLauncherExtras;
+ suspendParams = new ArrayMap<>(o.suspendParams);
instantApp = o.instantApp;
virtualPreload = o.virtualPreload;
enabled = o.enabled;
@@ -231,19 +231,7 @@ public class PackageUserState {
return false;
}
if (suspended) {
- if (suspendingPackage == null
- || !suspendingPackage.equals(oldState.suspendingPackage)) {
- return false;
- }
- if (!Objects.equals(dialogInfo, oldState.dialogInfo)) {
- return false;
- }
- if (!BaseBundle.kindofEquals(suspendedAppExtras,
- oldState.suspendedAppExtras)) {
- return false;
- }
- if (!BaseBundle.kindofEquals(suspendedLauncherExtras,
- oldState.suspendedLauncherExtras)) {
+ if (!Objects.equals(suspendParams, oldState.suspendParams)) {
return false;
}
}
@@ -308,4 +296,171 @@ public class PackageUserState {
}
return true;
}
+
+ @Override
+ public int hashCode() {
+ int hashCode = Long.hashCode(ceDataInode);
+ hashCode = 31 * hashCode + Boolean.hashCode(installed);
+ hashCode = 31 * hashCode + Boolean.hashCode(stopped);
+ hashCode = 31 * hashCode + Boolean.hashCode(notLaunched);
+ hashCode = 31 * hashCode + Boolean.hashCode(hidden);
+ hashCode = 31 * hashCode + distractionFlags;
+ hashCode = 31 * hashCode + Boolean.hashCode(suspended);
+ hashCode = 31 * hashCode + Objects.hashCode(suspendParams);
+ hashCode = 31 * hashCode + Boolean.hashCode(instantApp);
+ hashCode = 31 * hashCode + Boolean.hashCode(virtualPreload);
+ hashCode = 31 * hashCode + enabled;
+ hashCode = 31 * hashCode + Objects.hashCode(lastDisableAppCaller);
+ hashCode = 31 * hashCode + domainVerificationStatus;
+ hashCode = 31 * hashCode + appLinkGeneration;
+ hashCode = 31 * hashCode + categoryHint;
+ hashCode = 31 * hashCode + installReason;
+ hashCode = 31 * hashCode + Objects.hashCode(disabledComponents);
+ hashCode = 31 * hashCode + Objects.hashCode(enabledComponents);
+ hashCode = 31 * hashCode + Objects.hashCode(harmfulAppWarning);
+ return hashCode;
+ }
+
+ /**
+ * Container to describe suspension parameters.
+ */
+ public static final class SuspendParams {
+ private static final String TAG_DIALOG_INFO = "dialog-info";
+ private static final String TAG_APP_EXTRAS = "app-extras";
+ private static final String TAG_LAUNCHER_EXTRAS = "launcher-extras";
+
+ public SuspendDialogInfo dialogInfo;
+ public PersistableBundle appExtras;
+ public PersistableBundle launcherExtras;
+
+ private SuspendParams() {
+ }
+
+ /**
+ * Returns a {@link SuspendParams} object with the given fields. Returns {@code null} if all
+ * the fields are {@code null}.
+ *
+ * @param dialogInfo
+ * @param appExtras
+ * @param launcherExtras
+ * @return A {@link SuspendParams} object or {@code null}.
+ */
+ public static SuspendParams getInstanceOrNull(SuspendDialogInfo dialogInfo,
+ PersistableBundle appExtras, PersistableBundle launcherExtras) {
+ if (dialogInfo == null && appExtras == null && launcherExtras == null) {
+ return null;
+ }
+ final SuspendParams instance = new SuspendParams();
+ instance.dialogInfo = dialogInfo;
+ instance.appExtras = appExtras;
+ instance.launcherExtras = launcherExtras;
+ return instance;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof SuspendParams)) {
+ return false;
+ }
+ final SuspendParams other = (SuspendParams) obj;
+ if (!Objects.equals(dialogInfo, other.dialogInfo)) {
+ return false;
+ }
+ if (!BaseBundle.kindofEquals(appExtras, other.appExtras)) {
+ return false;
+ }
+ if (!BaseBundle.kindofEquals(launcherExtras, other.launcherExtras)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int hashCode = Objects.hashCode(dialogInfo);
+ hashCode = 31 * hashCode + ((appExtras != null) ? appExtras.size() : 0);
+ hashCode = 31 * hashCode + ((launcherExtras != null) ? launcherExtras.size() : 0);
+ return hashCode;
+ }
+
+ /**
+ * Serializes this object into an xml format
+ * @param out the {@link XmlSerializer} object
+ * @throws IOException
+ */
+ public void saveToXml(XmlSerializer out) throws IOException {
+ if (dialogInfo != null) {
+ out.startTag(null, TAG_DIALOG_INFO);
+ dialogInfo.saveToXml(out);
+ out.endTag(null, TAG_DIALOG_INFO);
+ }
+ if (appExtras != null) {
+ out.startTag(null, TAG_APP_EXTRAS);
+ try {
+ appExtras.saveToXml(out);
+ } catch (XmlPullParserException e) {
+ Slog.e(LOG_TAG, "Exception while trying to write appExtras."
+ + " Will be lost on reboot", e);
+ }
+ out.endTag(null, TAG_APP_EXTRAS);
+ }
+ if (launcherExtras != null) {
+ out.startTag(null, TAG_LAUNCHER_EXTRAS);
+ try {
+ launcherExtras.saveToXml(out);
+ } catch (XmlPullParserException e) {
+ Slog.e(LOG_TAG, "Exception while trying to write launcherExtras."
+ + " Will be lost on reboot", e);
+ }
+ out.endTag(null, TAG_LAUNCHER_EXTRAS);
+ }
+ }
+
+ /**
+ * Parses this object from the xml format. Returns {@code null} if no object related
+ * information could be read.
+ * @param in the reader
+ * @return
+ */
+ public static SuspendParams restoreFromXml(XmlPullParser in) throws IOException {
+ SuspendDialogInfo readDialogInfo = null;
+ PersistableBundle readAppExtras = null;
+ PersistableBundle readLauncherExtras = null;
+
+ final int currentDepth = in.getDepth();
+ int type;
+ try {
+ while ((type = in.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG
+ || in.getDepth() > currentDepth)) {
+ if (type == XmlPullParser.END_TAG
+ || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ switch (in.getName()) {
+ case TAG_DIALOG_INFO:
+ readDialogInfo = SuspendDialogInfo.restoreFromXml(in);
+ break;
+ case TAG_APP_EXTRAS:
+ readAppExtras = PersistableBundle.restoreFromXml(in);
+ break;
+ case TAG_LAUNCHER_EXTRAS:
+ readLauncherExtras = PersistableBundle.restoreFromXml(in);
+ break;
+ default:
+ Slog.w(LOG_TAG, "Unknown tag " + in.getName()
+ + " in SuspendParams. Ignoring");
+ break;
+ }
+ }
+ } catch (XmlPullParserException e) {
+ Slog.e(LOG_TAG, "Exception while trying to parse SuspendParams,"
+ + " some fields may default", e);
+ }
+ return getInstanceOrNull(readDialogInfo, readAppExtras, readLauncherExtras);
+ }
+ }
}
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index dd5c6a53cc20..aa6f58e82bcf 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -237,6 +237,28 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
@TestApi
public static final int PROTECTION_FLAG_APP_PREDICTOR = 0x200000;
+ /**
+ * Additional flag for {@link #protectionLevel}, corresponding
+ * to the <code>telephony</code> value of
+ * {@link android.R.attr#protectionLevel}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int PROTECTION_FLAG_TELEPHONY = 0x400000;
+
+ /**
+ * Additional flag for {@link #protectionLevel}, corresponding
+ * to the <code>wifi</code> value of
+ * {@link android.R.attr#protectionLevel}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int PROTECTION_FLAG_WIFI = 0x800000;
+
/** @hide */
@IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = {
PROTECTION_FLAG_PRIVILEGED,
@@ -258,6 +280,8 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
PROTECTION_FLAG_CONFIGURATOR,
PROTECTION_FLAG_INCIDENT_REPORT_APPROVER,
PROTECTION_FLAG_APP_PREDICTOR,
+ PROTECTION_FLAG_TELEPHONY,
+ PROTECTION_FLAG_WIFI,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ProtectionFlags {}
@@ -501,6 +525,12 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
if ((level & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0) {
protLevel += "|appPredictor";
}
+ if ((level & PermissionInfo.PROTECTION_FLAG_TELEPHONY) != 0) {
+ protLevel += "|telephony";
+ }
+ if ((level & PermissionInfo.PROTECTION_FLAG_WIFI) != 0) {
+ protLevel += "|wifi";
+ }
return protLevel;
}
diff --git a/core/java/android/content/pm/SharedLibraryNames.java b/core/java/android/content/pm/SharedLibraryNames.java
index 4c66fc007856..a607a9ff682b 100644
--- a/core/java/android/content/pm/SharedLibraryNames.java
+++ b/core/java/android/content/pm/SharedLibraryNames.java
@@ -33,6 +33,4 @@ public class SharedLibraryNames {
static final String ANDROID_TEST_RUNNER = "android.test.runner";
public static final String ORG_APACHE_HTTP_LEGACY = "org.apache.http.legacy";
-
- public static final String ANDROID_TELEPHONY_COMMON = "telephony-common";
}
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index e65d761cb77e..1e88ce7e3f5c 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -126,6 +126,13 @@ public class UserInfo implements Parcelable {
public static final int FLAG_SYSTEM = 0x00000800;
/**
+ * Indicates that this user is some sort of profile. Right now, the only profile type is
+ * {@link #FLAG_MANAGED_PROFILE}, but this can include other types of profiles too if any
+ * are created in the future. This is therefore not a flag, but an OR of several flags.
+ */
+ public static final int PROFILE_FLAGS_MASK = FLAG_MANAGED_PROFILE;
+
+ /**
* @hide
*/
@IntDef(flag = true, prefix = "FLAG_", value = {
@@ -180,6 +187,18 @@ public class UserInfo implements Parcelable {
@UnsupportedAppUsage
public boolean guestToRemove;
+ /**
+ * This is used to optimize the creation of an user, i.e. OEMs might choose to pre-create a
+ * number of users at the first boot, so the actual creation later is faster.
+ *
+ * <p>A {@code preCreated} user is not a real user yet, so it should not show up on regular
+ * user operations (other than user creation per se).
+ *
+ * <p>Once the pre-created is used to create a "real" user later on, {@code preCreate} is set to
+ * {@code false}.
+ */
+ public boolean preCreated;
+
@UnsupportedAppUsage
public UserInfo(int id, String name, int flags) {
this(id, name, null, flags);
@@ -207,6 +226,13 @@ public class UserInfo implements Parcelable {
@UnsupportedAppUsage
public boolean isGuest() {
+ return isGuest(flags);
+ }
+
+ /**
+ * Checks if the flag denotes a guest user.
+ */
+ public static boolean isGuest(@UserInfoFlag int flags) {
return (flags & FLAG_GUEST) == FLAG_GUEST;
}
@@ -217,6 +243,13 @@ public class UserInfo implements Parcelable {
@UnsupportedAppUsage
public boolean isManagedProfile() {
+ return isManagedProfile(flags);
+ }
+
+ /**
+ * Checks if the flag denotes a managed profile.
+ */
+ public static boolean isManagedProfile(@UserInfoFlag int flags) {
return (flags & FLAG_MANAGED_PROFILE) == FLAG_MANAGED_PROFILE;
}
@@ -279,7 +312,7 @@ public class UserInfo implements Parcelable {
*/
public boolean supportsSwitchToByUser() {
// Hide the system user when it does not represent a human user.
- boolean hideSystemUser = UserManager.isSplitSystemUser();
+ boolean hideSystemUser = UserManager.isHeadlessSystemUserMode();
return (!hideSystemUser || id != UserHandle.USER_SYSTEM) && supportsSwitchTo();
}
@@ -308,6 +341,7 @@ public class UserInfo implements Parcelable {
lastLoggedInTime = orig.lastLoggedInTime;
lastLoggedInFingerprint = orig.lastLoggedInFingerprint;
partial = orig.partial;
+ preCreated = orig.preCreated;
profileGroupId = orig.profileGroupId;
restrictedProfileParentId = orig.restrictedProfileParentId;
guestToRemove = orig.guestToRemove;
@@ -316,7 +350,7 @@ public class UserInfo implements Parcelable {
@UnsupportedAppUsage
public UserHandle getUserHandle() {
- return new UserHandle(id);
+ return UserHandle.of(id);
}
@Override
@@ -332,6 +366,8 @@ public class UserInfo implements Parcelable {
return "UserInfo[id=" + id
+ ", name=" + name
+ ", flags=" + flagsToString(flags)
+ + (preCreated ? " (pre-created)" : "")
+ + (partial ? " (partial)" : "")
+ "]";
}
@@ -355,9 +391,10 @@ public class UserInfo implements Parcelable {
dest.writeLong(creationTime);
dest.writeLong(lastLoggedInTime);
dest.writeString(lastLoggedInFingerprint);
- dest.writeInt(partial ? 1 : 0);
+ dest.writeBoolean(partial);
+ dest.writeBoolean(preCreated);
dest.writeInt(profileGroupId);
- dest.writeInt(guestToRemove ? 1 : 0);
+ dest.writeBoolean(guestToRemove);
dest.writeInt(restrictedProfileParentId);
dest.writeInt(profileBadge);
}
@@ -382,9 +419,10 @@ public class UserInfo implements Parcelable {
creationTime = source.readLong();
lastLoggedInTime = source.readLong();
lastLoggedInFingerprint = source.readString();
- partial = source.readInt() != 0;
+ partial = source.readBoolean();
+ preCreated = source.readBoolean();
profileGroupId = source.readInt();
- guestToRemove = source.readInt() != 0;
+ guestToRemove = source.readBoolean();
restrictedProfileParentId = source.readInt();
profileBadge = source.readInt();
}
diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java
index 69462ab99483..de1d514d0a5b 100644
--- a/core/java/android/content/res/ApkAssets.java
+++ b/core/java/android/content/res/ApkAssets.java
@@ -16,7 +16,10 @@
package android.content.res;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
+import android.content.res.loader.ResourcesProvider;
+import android.text.TextUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
@@ -36,10 +39,14 @@ import java.io.IOException;
*/
public final class ApkAssets {
@GuardedBy("this") private final long mNativePtr;
+
+ @Nullable
@GuardedBy("this") private final StringBlock mStringBlock;
@GuardedBy("this") private boolean mOpen = true;
+ private final boolean mForLoader;
+
/**
* Creates a new ApkAssets instance from the given path on disk.
*
@@ -48,7 +55,8 @@ public final class ApkAssets {
* @throws IOException if a disk I/O error or parsing error occurred.
*/
public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException {
- return new ApkAssets(path, false /*system*/, false /*forceSharedLib*/, false /*overlay*/);
+ return new ApkAssets(path, false /*system*/, false /*forceSharedLib*/, false /*overlay*/,
+ false /*arscOnly*/, false /*forLoader*/);
}
/**
@@ -61,7 +69,8 @@ public final class ApkAssets {
*/
public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system)
throws IOException {
- return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/);
+ return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/,
+ false /*arscOnly*/, false /*forLoader*/);
}
/**
@@ -76,7 +85,8 @@ public final class ApkAssets {
*/
public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system,
boolean forceSharedLibrary) throws IOException {
- return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/);
+ return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/,
+ false /*arscOnly*/, false /*forLoader*/);
}
/**
@@ -96,7 +106,8 @@ public final class ApkAssets {
public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd,
@NonNull String friendlyName, boolean system, boolean forceSharedLibrary)
throws IOException {
- return new ApkAssets(fd, friendlyName, system, forceSharedLibrary);
+ return new ApkAssets(fd, friendlyName, system, forceSharedLibrary, false /*arscOnly*/,
+ false /*forLoader*/);
}
/**
@@ -110,21 +121,90 @@ public final class ApkAssets {
*/
public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath, boolean system)
throws IOException {
- return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/);
+ return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/,
+ false /*arscOnly*/, false /*forLoader*/);
+ }
+
+ /**
+ * Creates a new ApkAssets instance from the given path on disk for use with a
+ * {@link ResourcesProvider}.
+ *
+ * @param path The path to an APK on disk.
+ * @return a new instance of ApkAssets.
+ * @throws IOException if a disk I/O error or parsing error occurred.
+ */
+ public static @NonNull ApkAssets loadApkForLoader(@NonNull String path)
+ throws IOException {
+ return new ApkAssets(path, false /*system*/, false /*forceSharedLibrary*/,
+ false /*overlay*/, false /*arscOnly*/, true /*forLoader*/);
+ }
+
+ /**
+ * Creates a new ApkAssets instance from the given file descriptor for use with a
+ * {@link ResourcesProvider}.
+ *
+ * Performs a dup of the underlying fd, so you must take care of still closing
+ * the FileDescriptor yourself (and can do that whenever you want).
+ *
+ * @param fd The FileDescriptor of an open, readable APK.
+ * @return a new instance of ApkAssets.
+ * @throws IOException if a disk I/O error or parsing error occurred.
+ */
+ @NonNull
+ public static ApkAssets loadApkForLoader(@NonNull FileDescriptor fd) throws IOException {
+ return new ApkAssets(fd, TextUtils.emptyIfNull(fd.toString()),
+ false /*system*/, false /*forceSharedLib*/, false /*arscOnly*/, true /*forLoader*/);
}
- private ApkAssets(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay)
+ /**
+ * Creates a new ApkAssets instance from the given file descriptor representing an ARSC
+ * for use with a {@link ResourcesProvider}.
+ *
+ * Performs a dup of the underlying fd, so you must take care of still closing
+ * the FileDescriptor yourself (and can do that whenever you want).
+ *
+ * @param fd The FileDescriptor of an open, readable .arsc.
+ * @return a new instance of ApkAssets.
+ * @throws IOException if a disk I/O error or parsing error occurred.
+ */
+ public static @NonNull ApkAssets loadArscForLoader(@NonNull FileDescriptor fd)
throws IOException {
+ return new ApkAssets(fd, TextUtils.emptyIfNull(fd.toString()),
+ false /*system*/, false /*forceSharedLib*/, true /*arscOnly*/, true /*forLoader*/);
+ }
+
+ /**
+ * Generates an entirely empty ApkAssets. Needed because the ApkAssets instance and presence
+ * is required for a lot of APIs, and it's easier to have a non-null reference rather than
+ * tracking a separate identifier.
+ */
+ @NonNull
+ public static ApkAssets loadEmptyForLoader() {
+ return new ApkAssets(true);
+ }
+
+ private ApkAssets(boolean forLoader) {
+ mForLoader = forLoader;
+ mNativePtr = nativeLoadEmpty(forLoader);
+ mStringBlock = null;
+ }
+
+ private ApkAssets(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay,
+ boolean arscOnly, boolean forLoader) throws IOException {
+ mForLoader = forLoader;
Preconditions.checkNotNull(path, "path");
- mNativePtr = nativeLoad(path, system, forceSharedLib, overlay);
+ mNativePtr = arscOnly ? nativeLoadArsc(path, forLoader)
+ : nativeLoad(path, system, forceSharedLib, overlay, forLoader);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
}
private ApkAssets(@NonNull FileDescriptor fd, @NonNull String friendlyName, boolean system,
- boolean forceSharedLib) throws IOException {
+ boolean forceSharedLib, boolean arscOnly, boolean forLoader) throws IOException {
+ mForLoader = forLoader;
Preconditions.checkNotNull(fd, "fd");
Preconditions.checkNotNull(friendlyName, "friendlyName");
- mNativePtr = nativeLoadFromFd(fd, friendlyName, system, forceSharedLib);
+ mNativePtr = arscOnly ? nativeLoadArscFromFd(fd, friendlyName, forLoader)
+ : nativeLoadFromFd(fd, friendlyName, system, forceSharedLib, forLoader);
mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
}
@@ -136,11 +216,19 @@ public final class ApkAssets {
}
CharSequence getStringFromPool(int idx) {
+ if (mStringBlock == null) {
+ return null;
+ }
+
synchronized (this) {
return mStringBlock.get(idx);
}
}
+ public boolean isForLoader() {
+ return mForLoader;
+ }
+
/**
* Retrieve a parser for a compiled XML file. This is associated with a single APK and
* <em>NOT</em> a full AssetManager. This means that shared-library references will not be
@@ -188,22 +276,30 @@ public final class ApkAssets {
/**
* Closes this class and the contained {@link #mStringBlock}.
*/
- public void close() throws Throwable {
+ public void close() {
synchronized (this) {
if (mOpen) {
mOpen = false;
- mStringBlock.close();
+ if (mStringBlock != null) {
+ mStringBlock.close();
+ }
nativeDestroy(mNativePtr);
}
}
}
- private static native long nativeLoad(
- @NonNull String path, boolean system, boolean forceSharedLib, boolean overlay)
+ private static native long nativeLoad(@NonNull String path, boolean system,
+ boolean forceSharedLib, boolean overlay, boolean forLoader)
throws IOException;
private static native long nativeLoadFromFd(@NonNull FileDescriptor fd,
- @NonNull String friendlyName, boolean system, boolean forceSharedLib)
+ @NonNull String friendlyName, boolean system, boolean forceSharedLib,
+ boolean forLoader)
+ throws IOException;
+ private static native long nativeLoadArsc(@NonNull String path, boolean forLoader)
throws IOException;
+ private static native long nativeLoadArscFromFd(@NonNull FileDescriptor fd,
+ @NonNull String friendlyName, boolean forLoader) throws IOException;
+ private static native long nativeLoadEmpty(boolean forLoader);
private static native void nativeDestroy(long ptr);
private static native @NonNull String nativeGetAssetPath(long ptr);
private static native long nativeGetStringBlock(long ptr);
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 2420a6109155..23e772075ad6 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -27,9 +27,13 @@ import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration.NativeConfig;
+import android.content.res.loader.ResourceLoader;
+import android.content.res.loader.ResourceLoaderManager;
+import android.content.res.loader.ResourcesProvider;
import android.os.ParcelFileDescriptor;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Pair;
import android.util.SparseArray;
import android.util.TypedValue;
@@ -39,15 +43,19 @@ import com.android.internal.util.Preconditions;
import libcore.io.IoUtils;
import java.io.BufferedReader;
+import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.channels.FileLock;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
import java.util.Map;
/**
@@ -60,7 +68,6 @@ import java.util.Map;
public final class AssetManager implements AutoCloseable {
private static final String TAG = "AssetManager";
private static final boolean DEBUG_REFS = false;
- private static final boolean FEATURE_FLAG_IDMAP2 = true;
private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk";
@@ -111,6 +118,13 @@ public final class AssetManager implements AutoCloseable {
@GuardedBy("this") private int mNumRefs = 1;
@GuardedBy("this") private HashMap<Long, RuntimeException> mRefStacks;
+ private ResourceLoaderManager mResourceLoaderManager;
+
+ /** @hide */
+ public void setResourceLoaderManager(ResourceLoaderManager resourceLoaderManager) {
+ mResourceLoaderManager = resourceLoaderManager;
+ }
+
/**
* A Builder class that helps create an AssetManager with only a single invocation of
* {@link AssetManager#setApkAssets(ApkAssets[], boolean)}. Without using this builder,
@@ -202,20 +216,14 @@ public final class AssetManager implements AutoCloseable {
try {
final ArrayList<ApkAssets> apkAssets = new ArrayList<>();
apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/));
- if (FEATURE_FLAG_IDMAP2) {
- final String[] systemIdmapPaths =
- nativeCreateIdmapsForStaticOverlaysTargetingAndroid();
- if (systemIdmapPaths != null) {
- for (String idmapPath : systemIdmapPaths) {
- apkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/));
- }
- } else {
- Log.w(TAG, "'idmap2 --scan' failed: no static=\"true\" overlays targeting "
- + "\"android\" will be loaded");
+ final String[] systemIdmapPaths = nativeCreateIdmapsForStaticOverlaysTargetingAndroid();
+ if (systemIdmapPaths != null) {
+ for (String idmapPath : systemIdmapPaths) {
+ apkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/));
}
} else {
- nativeVerifySystemIdmaps();
- loadStaticRuntimeOverlays(apkAssets);
+ Log.w(TAG, "'idmap2 --scan' failed: no static=\"true\" overlays targeting "
+ + "\"android\" will be loaded");
}
sSystemApkAssetsSet = new ArraySet<>(apkAssets);
@@ -514,7 +522,7 @@ public final class AssetManager implements AutoCloseable {
outValue.changingConfigurations);
if (outValue.type == TypedValue.TYPE_STRING) {
- outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data);
+ outValue.string = getPooledStringForCookie(cookie, outValue.data);
}
return true;
}
@@ -561,7 +569,7 @@ public final class AssetManager implements AutoCloseable {
outValue.changingConfigurations);
if (outValue.type == TypedValue.TYPE_STRING) {
- return mApkAssets[cookie - 1].getStringFromPool(outValue.data);
+ return getPooledStringForCookie(cookie, outValue.data);
}
return outValue.coerceToString();
}
@@ -639,7 +647,7 @@ public final class AssetManager implements AutoCloseable {
int cookie = rawInfoArray[i];
int index = rawInfoArray[i + 1];
retArray[j] = (index >= 0 && cookie > 0)
- ? mApkAssets[cookie - 1].getStringFromPool(index) : null;
+ ? getPooledStringForCookie(cookie, index) : null;
}
return retArray;
}
@@ -695,7 +703,7 @@ public final class AssetManager implements AutoCloseable {
outValue.changingConfigurations);
if (outValue.type == TypedValue.TYPE_STRING) {
- outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data);
+ outValue.string = getPooledStringForCookie(cookie, outValue.data);
}
return true;
}
@@ -760,6 +768,7 @@ public final class AssetManager implements AutoCloseable {
*
* @hide
*/
+ @TestApi
public void setResourceResolutionLoggingEnabled(boolean enabled) {
synchronized (this) {
ensureValidLocked();
@@ -775,6 +784,7 @@ public final class AssetManager implements AutoCloseable {
*
* @hide
*/
+ @TestApi
public @Nullable String getLastResourceResolution() {
synchronized (this) {
ensureValidLocked();
@@ -821,6 +831,13 @@ public final class AssetManager implements AutoCloseable {
Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
ensureOpenLocked();
+
+ String path = Paths.get("assets", fileName).toString();
+ InputStream inputStream = searchLoaders(0, path, accessMode);
+ if (inputStream != null) {
+ return inputStream;
+ }
+
final long asset = nativeOpenAsset(mObject, fileName, accessMode);
if (asset == 0) {
throw new FileNotFoundException("Asset file: " + fileName);
@@ -845,6 +862,13 @@ public final class AssetManager implements AutoCloseable {
Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
ensureOpenLocked();
+
+ String path = Paths.get("assets", fileName).toString();
+ AssetFileDescriptor fileDescriptor = searchLoadersFd(0, path);
+ if (fileDescriptor != null) {
+ return fileDescriptor;
+ }
+
final ParcelFileDescriptor pfd = nativeOpenAssetFd(mObject, fileName, mOffsets);
if (pfd == null) {
throw new FileNotFoundException("Asset file: " + fileName);
@@ -938,6 +962,12 @@ public final class AssetManager implements AutoCloseable {
Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
ensureOpenLocked();
+
+ InputStream inputStream = searchLoaders(cookie, fileName, accessMode);
+ if (inputStream != null) {
+ return inputStream;
+ }
+
final long asset = nativeOpenNonAsset(mObject, cookie, fileName, accessMode);
if (asset == 0) {
throw new FileNotFoundException("Asset absolute file: " + fileName);
@@ -977,6 +1007,12 @@ public final class AssetManager implements AutoCloseable {
Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
ensureOpenLocked();
+
+ AssetFileDescriptor fileDescriptor = searchLoadersFd(cookie, fileName);
+ if (fileDescriptor != null) {
+ return fileDescriptor;
+ }
+
final ParcelFileDescriptor pfd =
nativeOpenNonAssetFd(mObject, cookie, fileName, mOffsets);
if (pfd == null) {
@@ -1038,7 +1074,16 @@ public final class AssetManager implements AutoCloseable {
Preconditions.checkNotNull(fileName, "fileName");
synchronized (this) {
ensureOpenLocked();
- final long xmlBlock = nativeOpenXmlAsset(mObject, cookie, fileName);
+
+ final long xmlBlock;
+ AssetFileDescriptor fileDescriptor = searchLoadersFd(cookie, fileName);
+ if (fileDescriptor != null) {
+ xmlBlock = nativeOpenXmlAssetFd(mObject, cookie,
+ fileDescriptor.getFileDescriptor());
+ } else {
+ xmlBlock = nativeOpenXmlAsset(mObject, cookie, fileName);
+ }
+
if (xmlBlock == 0) {
throw new FileNotFoundException("Asset XML file: " + fileName);
}
@@ -1048,6 +1093,85 @@ public final class AssetManager implements AutoCloseable {
}
}
+ private InputStream searchLoaders(int cookie, @NonNull String fileName, int accessMode)
+ throws IOException {
+ if (mResourceLoaderManager == null) {
+ return null;
+ }
+
+ List<Pair<ResourceLoader, ResourcesProvider>> loaders =
+ mResourceLoaderManager.getInternalList();
+
+ // A cookie of 0 means no specific ApkAssets, so search everything
+ if (cookie == 0) {
+ for (int index = loaders.size() - 1; index >= 0; index--) {
+ Pair<ResourceLoader, ResourcesProvider> pair = loaders.get(index);
+ try {
+ InputStream inputStream = pair.first.loadAsset(fileName, accessMode);
+ if (inputStream != null) {
+ return inputStream;
+ }
+ } catch (IOException ignored) {
+ // When searching, ignore read failures
+ }
+ }
+
+ return null;
+ }
+
+ ApkAssets apkAssets = mApkAssets[cookie - 1];
+ for (int index = loaders.size() - 1; index >= 0; index--) {
+ Pair<ResourceLoader, ResourcesProvider> pair = loaders.get(index);
+ if (pair.second.getApkAssets() == apkAssets) {
+ return pair.first.loadAsset(fileName, accessMode);
+ }
+ }
+
+ return null;
+ }
+
+ private AssetFileDescriptor searchLoadersFd(int cookie, @NonNull String fileName)
+ throws IOException {
+ if (mResourceLoaderManager == null) {
+ return null;
+ }
+
+ List<Pair<ResourceLoader, ResourcesProvider>> loaders =
+ mResourceLoaderManager.getInternalList();
+
+ // A cookie of 0 means no specific ApkAssets, so search everything
+ if (cookie == 0) {
+ for (int index = loaders.size() - 1; index >= 0; index--) {
+ Pair<ResourceLoader, ResourcesProvider> pair = loaders.get(index);
+ try {
+ ParcelFileDescriptor fileDescriptor = pair.first.loadAssetFd(fileName);
+ if (fileDescriptor != null) {
+ return new AssetFileDescriptor(fileDescriptor, 0,
+ AssetFileDescriptor.UNKNOWN_LENGTH);
+ }
+ } catch (IOException ignored) {
+ // When searching, ignore read failures
+ }
+ }
+
+ return null;
+ }
+
+ ApkAssets apkAssets = mApkAssets[cookie - 1];
+ for (int index = loaders.size() - 1; index >= 0; index--) {
+ Pair<ResourceLoader, ResourcesProvider> pair = loaders.get(index);
+ if (pair.second.getApkAssets() == apkAssets) {
+ ParcelFileDescriptor fileDescriptor = pair.first.loadAssetFd(fileName);
+ if (fileDescriptor != null) {
+ return new AssetFileDescriptor(fileDescriptor, 0,
+ AssetFileDescriptor.UNKNOWN_LENGTH);
+ }
+ return null;
+ }
+ }
+ return null;
+ }
+
void xmlBlockGone(int id) {
synchronized (this) {
decRefsLocked(id);
@@ -1303,7 +1427,7 @@ public final class AssetManager implements AutoCloseable {
*
* <p>On SDK 21 (Android 5.0: Lollipop) and above, Locale strings are valid
* <a href="https://tools.ietf.org/html/bcp47">BCP-47</a> language tags and can be
- * parsed using {@link java.util.Locale#forLanguageTag(String)}.
+ * parsed using {@link Locale#forLanguageTag(String)}.
*
* <p>On SDK 20 (Android 4.4W: Kitkat for watches) and below, locale strings
* are of the form {@code ll_CC} where {@code ll} is a two letter language code,
@@ -1376,7 +1500,6 @@ public final class AssetManager implements AutoCloseable {
/**
* @hide
*/
- @TestApi
@GuardedBy("this")
public @Nullable Map<String, String> getOverlayableMap(String packageName) {
synchronized (this) {
@@ -1385,6 +1508,18 @@ public final class AssetManager implements AutoCloseable {
}
}
+ /**
+ * @hide
+ */
+ @TestApi
+ @GuardedBy("this")
+ public @Nullable String getOverlayablesToString(String packageName) {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetOverlayablesToString(mObject, packageName);
+ }
+ }
+
@GuardedBy("this")
private void incRefsLocked(long id) {
if (DEBUG_REFS) {
@@ -1435,6 +1570,8 @@ public final class AssetManager implements AutoCloseable {
private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie,
@NonNull String fileName, @NonNull long[] outOffsets) throws IOException;
private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName);
+ private static native long nativeOpenXmlAssetFd(long ptr, int cookie,
+ @NonNull FileDescriptor fileDescriptor);
// Primitive resource native methods.
private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density,
@@ -1504,6 +1641,8 @@ public final class AssetManager implements AutoCloseable {
private static native String[] nativeCreateIdmapsForStaticOverlaysTargetingAndroid();
private static native @Nullable Map nativeGetOverlayableMap(long ptr,
@NonNull String packageName);
+ private static native @Nullable String nativeGetOverlayablesToString(long ptr,
+ @NonNull String packageName);
// Global debug native methods.
/**
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index d7e4e1452cfe..2698c2de4c61 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -30,6 +30,7 @@ import android.annotation.DimenRes;
import android.annotation.DrawableRes;
import android.annotation.FontRes;
import android.annotation.FractionRes;
+import android.annotation.IntRange;
import android.annotation.IntegerRes;
import android.annotation.LayoutRes;
import android.annotation.NonNull;
@@ -41,8 +42,12 @@ import android.annotation.StyleRes;
import android.annotation.StyleableRes;
import android.annotation.UnsupportedAppUsage;
import android.annotation.XmlRes;
+import android.app.ResourcesManager;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.Config;
+import android.content.res.loader.ResourceLoader;
+import android.content.res.loader.ResourceLoaderManager;
+import android.content.res.loader.ResourcesProvider;
import android.graphics.Movie;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
@@ -54,13 +59,16 @@ import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.LongSparseArray;
+import android.util.Pair;
import android.util.Pools.SynchronizedPool;
import android.util.TypedValue;
import android.view.DisplayAdjustments;
import android.view.ViewDebug;
import android.view.ViewHierarchyEncoder;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
import com.android.internal.util.XmlUtils;
@@ -71,6 +79,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
/**
* Class for accessing an application's resources. This sits on top of the
@@ -133,6 +143,11 @@ public class Resources {
@UnsupportedAppUsage
final ClassLoader mClassLoader;
+ private final Object mResourceLoaderLock = new Object();
+
+ @GuardedBy("mResourceLoaderLock")
+ private ResourceLoaderManager mResourceLoaderManager;
+
/**
* WeakReferences to Themes that were constructed from this Resources object.
* We keep track of these in case our underlying implementation is changed, in which case
@@ -148,6 +163,8 @@ public class Resources {
private static final int MIN_THEME_REFS_FLUSH_SIZE = 32;
private int mThemeRefsNextFlushSize = MIN_THEME_REFS_FLUSH_SIZE;
+ private int mBaseApkAssetsSize;
+
/**
* Returns the most appropriate default theme for the specified target SDK version.
* <ul>
@@ -283,8 +300,15 @@ public class Resources {
return;
}
+ mBaseApkAssetsSize = ArrayUtils.size(impl.getAssets().getApkAssets());
mResourcesImpl = impl;
+ synchronized (mResourceLoaderLock) {
+ if (mResourceLoaderManager != null) {
+ mResourceLoaderManager.onImplUpdate(mResourcesImpl);
+ }
+ }
+
// Create new ThemeImpls that are identical to the ones we have.
synchronized (mThemeRefs) {
final int count = mThemeRefs.size();
@@ -903,7 +927,7 @@ public class Resources {
try {
final ResourcesImpl impl = mResourcesImpl;
impl.getValueForDensity(id, density, value, true);
- return impl.loadDrawable(this, value, id, density, theme);
+ return loadDrawable(value, id, density, theme);
} finally {
releaseTempTypedValue(value);
}
@@ -913,6 +937,14 @@ public class Resources {
@UnsupportedAppUsage
Drawable loadDrawable(@NonNull TypedValue value, int id, int density, @Nullable Theme theme)
throws NotFoundException {
+ ResourceLoader loader = findLoader(value.assetCookie);
+ if (loader != null) {
+ Drawable drawable = loader.loadDrawable(value, id, density, theme);
+ if (drawable != null) {
+ return drawable;
+ }
+ }
+
return mResourcesImpl.loadDrawable(this, value, id, density, theme);
}
@@ -2280,7 +2312,7 @@ public class Resources {
final ResourcesImpl impl = mResourcesImpl;
impl.getValue(id, value, true);
if (value.type == TypedValue.TYPE_STRING) {
- return impl.loadXmlResourceParser(value.string.toString(), id,
+ return loadXmlResourceParser(value.string.toString(), id,
value.assetCookie, type);
}
throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
@@ -2304,6 +2336,14 @@ public class Resources {
@UnsupportedAppUsage
XmlResourceParser loadXmlResourceParser(String file, int id, int assetCookie,
String type) throws NotFoundException {
+ ResourceLoader loader = findLoader(assetCookie);
+ if (loader != null) {
+ XmlResourceParser xml = loader.loadXmlResourceParser(file, id);
+ if (xml != null) {
+ return xml;
+ }
+ }
+
return mResourcesImpl.loadXmlResourceParser(file, id, assetCookie, type);
}
@@ -2329,4 +2369,137 @@ public class Resources {
}
return theme.obtainStyledAttributes(set, attrs, 0, 0);
}
+
+ private ResourceLoader findLoader(int assetCookie) {
+ ApkAssets[] apkAssetsArray = mResourcesImpl.getAssets().getApkAssets();
+ int apkAssetsIndex = assetCookie - 1;
+ if (apkAssetsIndex < apkAssetsArray.length && apkAssetsIndex >= 0) {
+ ApkAssets apkAssets = apkAssetsArray[apkAssetsIndex];
+ if (apkAssets.isForLoader()) {
+ List<Pair<ResourceLoader, ResourcesProvider>> loaders;
+ // Since we don't lock the entire resolution path anyways,
+ // only lock here instead of entire method. The list is copied
+ // and effectively a snapshot is used.
+ synchronized (mResourceLoaderLock) {
+ loaders = mResourceLoaderManager.getInternalList();
+ }
+
+ if (!ArrayUtils.isEmpty(loaders)) {
+ int size = loaders.size();
+ for (int index = 0; index < size; index++) {
+ Pair<ResourceLoader, ResourcesProvider> pair = loaders.get(index);
+ if (pair.second.getApkAssets() == apkAssets) {
+ return pair.first;
+ }
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @return copied list of loaders and providers previously added
+ */
+ @NonNull
+ public List<Pair<ResourceLoader, ResourcesProvider>> getLoaders() {
+ synchronized (mResourceLoaderLock) {
+ return mResourceLoaderManager == null
+ ? Collections.emptyList()
+ : mResourceLoaderManager.getLoaders();
+ }
+ }
+
+ /**
+ * Add a custom {@link ResourceLoader} which is added to the paths searched by
+ * {@link AssetManager} when resolving a resource.
+ *
+ * Resources are resolved as if the loader was a resource overlay, meaning the latest
+ * in the list, of equal or better config, is returned.
+ *
+ * {@link ResourcesProvider}s passed in here are not managed and a reference should be held
+ * to remove, re-use, or close them when necessary.
+ *
+ * @param resourceLoader an interface used to resolve file paths for drawables/XML files;
+ * a reference should be kept to remove the loader if necessary
+ * @param resourcesProvider an .apk or .arsc file representation
+ * @param index where to add the loader in the list
+ * @throws IllegalArgumentException if the resourceLoader is already added
+ * @throws IndexOutOfBoundsException if the index is invalid
+ */
+ public void addLoader(@NonNull ResourceLoader resourceLoader,
+ @NonNull ResourcesProvider resourcesProvider, @IntRange(from = 0) int index) {
+ synchronized (mResourceLoaderLock) {
+ if (mResourceLoaderManager == null) {
+ ResourcesManager.getInstance().registerForLoaders(this);
+ mResourceLoaderManager = new ResourceLoaderManager(mResourcesImpl);
+ }
+
+ mResourceLoaderManager.addLoader(resourceLoader, resourcesProvider, index);
+ }
+ }
+
+ /**
+ * @see #addLoader(ResourceLoader, ResourcesProvider, int).
+ *
+ * Adds to the end of the list.
+ *
+ * @return index the loader was added at
+ */
+ public int addLoader(@NonNull ResourceLoader resourceLoader,
+ @NonNull ResourcesProvider resourcesProvider) {
+ synchronized (mResourceLoaderLock) {
+ int index = getLoaders().size();
+ addLoader(resourceLoader, resourcesProvider, index);
+ return index;
+ }
+ }
+
+ /**
+ * Remove a loader previously added by
+ * {@link #addLoader(ResourceLoader, ResourcesProvider, int)}
+ *
+ * The caller maintains responsibility for holding a reference to the matching
+ * {@link ResourcesProvider} and closing it after this method has been called.
+ *
+ * @param resourceLoader the same reference passed into [addLoader
+ * @return the index the loader was at in the list, or -1 if the loader was not found
+ */
+ public int removeLoader(@NonNull ResourceLoader resourceLoader) {
+ synchronized (mResourceLoaderLock) {
+ if (mResourceLoaderManager == null) {
+ return -1;
+ }
+
+ return mResourceLoaderManager.removeLoader(resourceLoader);
+ }
+ }
+
+ /**
+ * Swap the current set of loaders. Preferred to multiple remove/add calls as this doesn't
+ * update the resource data structures after each modification.
+ *
+ * Set to null or an empty list to clear the set of loaders.
+ *
+ * The caller maintains responsibility for holding references to the added
+ * {@link ResourcesProvider}s and closing them after this method has been called.
+ *
+ * @param resourceLoadersAndProviders a list of pairs to add
+ */
+ public void setLoaders(
+ @Nullable List<Pair<ResourceLoader, ResourcesProvider>> resourceLoadersAndProviders) {
+ synchronized (mResourceLoaderLock) {
+ if (mResourceLoaderManager == null) {
+ if (ArrayUtils.isEmpty(resourceLoadersAndProviders)) {
+ return;
+ }
+
+ ResourcesManager.getInstance().registerForLoaders(this);
+ mResourceLoaderManager = new ResourceLoaderManager(mResourcesImpl);
+ }
+
+ mResourceLoaderManager.setLoaders(resourceLoadersAndProviders);
+ }
+ }
}
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index b72544c02d6a..84489cfb768c 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -57,6 +57,8 @@ import android.view.DisplayAdjustments;
import com.android.internal.util.GrowingArrayUtils;
+import libcore.io.IoUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -376,7 +378,7 @@ public class ResourcesImpl {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesImpl#updateConfiguration");
try {
synchronized (mAccessLock) {
- if (false) {
+ if (DEBUG_CONFIG) {
Slog.i(TAG, "**** Updating config of " + this + ": old config is "
+ mConfiguration + " old compat is "
+ mDisplayAdjustments.getCompatibilityInfo());
@@ -572,6 +574,20 @@ public class ResourcesImpl {
}
}
+ /**
+ * Wipe all caches that might be read and return an outdated object when resolving a resource.
+ */
+ public void clearAllCaches() {
+ synchronized (mAccessLock) {
+ mDrawableCache.clear();
+ mColorDrawableCache.clear();
+ mComplexColorCache.clear();
+ mAnimatorCache.clear();
+ mStateListAnimatorCache.clear();
+ flushLayoutCache();
+ }
+ }
+
@Nullable
Drawable loadDrawable(@NonNull Resources wrapper, @NonNull TypedValue value, int id,
int density, @Nullable Resources.Theme theme)
@@ -802,6 +818,27 @@ public class ResourcesImpl {
}
/**
+ * Loads a Drawable from an encoded image stream, or null.
+ *
+ * This call will handle closing the {@link InputStream}.
+ */
+ @Nullable
+ private Drawable decodeImageDrawable(@NonNull InputStream inputStream,
+ @NonNull Resources wrapper, @NonNull TypedValue value) {
+ ImageDecoder.Source src = ImageDecoder.createSource(wrapper, inputStream, value.density);
+ try {
+ return ImageDecoder.decodeDrawable(src, (decoder, info, s) ->
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE));
+ } catch (IOException ignored) {
+ // This is okay. This may be something that ImageDecoder does not
+ // support, like SVG.
+ return null;
+ } finally {
+ IoUtils.closeQuietly(inputStream);
+ }
+ }
+
+ /**
* Loads a drawable from XML or resources stream.
*
* @return Drawable, or null if Drawable cannot be decoded.
@@ -865,8 +902,12 @@ public class ResourcesImpl {
} else {
final InputStream is = mAssets.openNonAsset(
value.assetCookie, file, AssetManager.ACCESS_STREAMING);
- AssetInputStream ais = (AssetInputStream) is;
- dr = decodeImageDrawable(ais, wrapper, value);
+ if (is instanceof AssetInputStream) {
+ AssetInputStream ais = (AssetInputStream) is;
+ dr = decodeImageDrawable(ais, wrapper, value);
+ } else {
+ dr = decodeImageDrawable(is, wrapper, value);
+ }
}
} finally {
stack.pop();
diff --git a/core/java/android/content/res/StringBlock.java b/core/java/android/content/res/StringBlock.java
index b7bc8229fa45..d43bd36b4c74 100644
--- a/core/java/android/content/res/StringBlock.java
+++ b/core/java/android/content/res/StringBlock.java
@@ -47,6 +47,7 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import java.io.Closeable;
import java.util.Arrays;
/**
@@ -54,7 +55,7 @@ import java.util.Arrays;
*
* {@hide}
*/
-final class StringBlock {
+public final class StringBlock implements Closeable {
private static final String TAG = "AssetManager";
private static final boolean localLOGV = false;
@@ -175,7 +176,8 @@ final class StringBlock {
}
}
- public void close() throws Throwable {
+ @Override
+ public void close() {
synchronized (this) {
if (mOpen) {
mOpen = false;
@@ -517,7 +519,7 @@ final class StringBlock {
* of this newly creating StringBlock.
*/
@UnsupportedAppUsage
- StringBlock(long obj, boolean useSparse) {
+ public StringBlock(long obj, boolean useSparse) {
mNative = obj;
mUseSparse = useSparse;
mOwnsNative = false;
diff --git a/core/java/android/content/res/ThemedResourceCache.java b/core/java/android/content/res/ThemedResourceCache.java
index 06cafdb2bb91..968ab401ccba 100644
--- a/core/java/android/content/res/ThemedResourceCache.java
+++ b/core/java/android/content/res/ThemedResourceCache.java
@@ -22,8 +22,8 @@ import android.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo.Config;
import android.content.res.Resources.Theme;
import android.content.res.Resources.ThemeKey;
-import android.util.LongSparseArray;
import android.util.ArrayMap;
+import android.util.LongSparseArray;
import java.lang.ref.WeakReference;
@@ -234,4 +234,18 @@ abstract class ThemedResourceCache<T> {
return entry == null || (configChanges != 0
&& shouldInvalidateEntry(entry, configChanges));
}
+
+ public synchronized void clear() {
+ if (mThemedEntries != null) {
+ mThemedEntries.clear();
+ }
+
+ if (mUnthemedEntries != null) {
+ mUnthemedEntries.clear();
+ }
+
+ if (mNullThemedEntries != null) {
+ mNullThemedEntries.clear();
+ }
+ }
}
diff --git a/core/java/android/content/res/loader/DirectoryResourceLoader.java b/core/java/android/content/res/loader/DirectoryResourceLoader.java
new file mode 100644
index 000000000000..7d90e72ab07e
--- /dev/null
+++ b/core/java/android/content/res/loader/DirectoryResourceLoader.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res.loader;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.ParcelFileDescriptor;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A {@link ResourceLoader} that searches a directory for assets.
+ *
+ * Assumes that resource paths are resolvable child paths of the directory passed in.
+ */
+public class DirectoryResourceLoader implements ResourceLoader {
+
+ @NonNull
+ private final File mDirectory;
+
+ public DirectoryResourceLoader(@NonNull File directory) {
+ this.mDirectory = directory;
+ }
+
+ @Nullable
+ @Override
+ public InputStream loadAsset(@NonNull String path, int accessMode) throws IOException {
+ File file = findFile(path);
+ if (file == null || !file.exists()) {
+ return null;
+ }
+ return new FileInputStream(file);
+ }
+
+ @Nullable
+ @Override
+ public ParcelFileDescriptor loadAssetFd(@NonNull String path) throws IOException {
+ File file = findFile(path);
+ if (file == null || !file.exists()) {
+ return null;
+ }
+ return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
+ }
+
+ /**
+ * Find the file for the given path encoded into the resource table.
+ */
+ @Nullable
+ public File findFile(@NonNull String path) {
+ return mDirectory.toPath().resolve(path).toFile();
+ }
+
+ @NonNull
+ public File getDirectory() {
+ return mDirectory;
+ }
+}
diff --git a/core/java/android/content/res/loader/ResourceLoader.java b/core/java/android/content/res/loader/ResourceLoader.java
new file mode 100644
index 000000000000..af32aa2c6875
--- /dev/null
+++ b/core/java/android/content/res/loader/ResourceLoader.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res.loader;
+
+import android.annotation.AnyRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
+import android.os.ParcelFileDescriptor;
+import android.util.TypedValue;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Exposes methods for overriding file-based resource loading from a {@link Resources}.
+ *
+ * To be used with {@link Resources#addLoader(ResourceLoader, ResourcesProvider, int)} and related
+ * methods to override resource loading.
+ *
+ * Note that this class doesn't actually contain any resource data. Non-file-based resources are
+ * loaded directly from the {@link ResourcesProvider}'s .arsc representation.
+ *
+ * An instance's methods will only be called if its corresponding {@link ResourcesProvider}'s
+ * resources table contains an entry for the resource ID being resolved,
+ * with the exception of the non-cookie variants of {@link AssetManager}'s openAsset and
+ * openNonAsset.
+ *
+ * Those methods search backwards through all {@link ResourceLoader}s and then any paths provided
+ * by the application or system.
+ *
+ * Otherwise, an ARSC that defines R.drawable.some_id must be provided if a {@link ResourceLoader}
+ * wants to point R.drawable.some_id to a different file on disk.
+ */
+public interface ResourceLoader {
+
+ /**
+ * Given the value resolved from the string pool of the {@link ResourcesProvider} passed to
+ * {@link Resources#addLoader(ResourceLoader, ResourcesProvider, int)}, return a
+ * {@link Drawable} which should be returned by the parent
+ * {@link Resources#getDrawable(int, Resources.Theme)}.
+ *
+ * @param value the resolved {@link TypedValue} before it has been converted to a Drawable
+ * object
+ * @param id the R.drawable ID this resolution is for
+ * @param density the requested density
+ * @param theme the {@link Resources.Theme} resolved under
+ * @return null if resolution should try to find an entry inside the {@link ResourcesProvider},
+ * including calling through to {@link #loadAsset(String, int)} or {@link #loadAssetFd(String)}
+ */
+ @Nullable
+ default Drawable loadDrawable(@NonNull TypedValue value, int id, int density,
+ @Nullable Resources.Theme theme) {
+ return null;
+ }
+
+ /**
+ * Given the value resolved from the string pool of the {@link ResourcesProvider} passed to
+ * {@link Resources#addLoader(ResourceLoader, ResourcesProvider, int)}, return an
+ * {@link XmlResourceParser} which should be returned by the parent
+ * {@link Resources#getDrawable(int, Resources.Theme)}.
+ *
+ * @param path the string that was found in the string pool
+ * @param id the XML ID this resolution is for, can be R.anim, R.layout, or R.xml
+ * @return null if resolution should try to find an entry inside the {@link ResourcesProvider},
+ * including calling through to {@link #loadAssetFd(String)} (String, int)}
+ */
+ @Nullable
+ default XmlResourceParser loadXmlResourceParser(@NonNull String path, @AnyRes int id) {
+ return null;
+ }
+
+ /**
+ * Given the value resolved from the string pool of the {@link ResourcesProvider} passed to
+ * {@link Resources#addLoader(ResourceLoader, ResourcesProvider, int)}, return an
+ * {@link InputStream} which should be returned when an asset is loaded by {@link AssetManager}.
+ * Assets will be loaded from a provider's root, with anything in its assets subpath prefixed
+ * with "assets/".
+ *
+ * @param path the asset path to load
+ * @param accessMode {@link AssetManager} access mode; does not have to be respected
+ * @return null if resolution should try to find an entry inside the {@link ResourcesProvider}
+ */
+ @Nullable
+ default InputStream loadAsset(@NonNull String path, int accessMode) throws IOException {
+ return null;
+ }
+
+ /**
+ * {@link ParcelFileDescriptor} variant of {@link #loadAsset(String, int)}.
+ *
+ * @param path the asset path to load
+ * @return null if resolution should try to find an entry inside the {@link ResourcesProvider}
+ */
+ @Nullable
+ default ParcelFileDescriptor loadAssetFd(@NonNull String path) throws IOException {
+ return null;
+ }
+}
diff --git a/core/java/android/content/res/loader/ResourceLoaderManager.java b/core/java/android/content/res/loader/ResourceLoaderManager.java
new file mode 100644
index 000000000000..ddbfa81390e4
--- /dev/null
+++ b/core/java/android/content/res/loader/ResourceLoaderManager.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res.loader;
+
+import android.annotation.Nullable;
+import android.content.res.ApkAssets;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.content.res.ResourcesImpl;
+import android.util.Pair;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * @hide
+ */
+public class ResourceLoaderManager {
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final List<Pair<ResourceLoader, ResourcesProvider>> mResourceLoaders =
+ new ArrayList<>();
+
+ @GuardedBy("mLock")
+ private ResourcesImpl mResourcesImpl;
+
+ public ResourceLoaderManager(ResourcesImpl resourcesImpl) {
+ this.mResourcesImpl = resourcesImpl;
+ this.mResourcesImpl.getAssets().setResourceLoaderManager(this);
+ }
+
+ /**
+ * Copies the list to ensure that ongoing mutations don't affect the list if it's being used
+ * as a search set.
+ *
+ * @see Resources#getLoaders()
+ */
+ public List<Pair<ResourceLoader, ResourcesProvider>> getLoaders() {
+ synchronized (mLock) {
+ return new ArrayList<>(mResourceLoaders);
+ }
+ }
+
+ /**
+ * Returns a list for searching for a loader. Locks and copies the list to ensure that
+ * ongoing mutations don't affect the search set.
+ */
+ public List<Pair<ResourceLoader, ResourcesProvider>> getInternalList() {
+ synchronized (mLock) {
+ return new ArrayList<>(mResourceLoaders);
+ }
+ }
+
+ /**
+ * TODO(b/136251855): Consider optional boolean ignoreConfigurations to allow ResourceLoader
+ * to override every configuration in the target package
+ *
+ * @see Resources#addLoader(ResourceLoader, ResourcesProvider)
+ */
+ public void addLoader(ResourceLoader resourceLoader, ResourcesProvider resourcesProvider,
+ int index) {
+ synchronized (mLock) {
+ for (int listIndex = 0; listIndex < mResourceLoaders.size(); listIndex++) {
+ if (Objects.equals(mResourceLoaders.get(listIndex).first, resourceLoader)) {
+ throw new IllegalArgumentException("Cannot add the same ResourceLoader twice");
+ }
+ }
+
+ mResourceLoaders.add(index, Pair.create(resourceLoader, resourcesProvider));
+ updateLoaders();
+ }
+ }
+
+ /**
+ * @see Resources#removeLoader(ResourceLoader)
+ */
+ public int removeLoader(ResourceLoader resourceLoader) {
+ synchronized (mLock) {
+ int indexOfLoader = -1;
+
+ for (int index = 0; index < mResourceLoaders.size(); index++) {
+ if (mResourceLoaders.get(index).first == resourceLoader) {
+ indexOfLoader = index;
+ break;
+ }
+ }
+
+ if (indexOfLoader < 0) {
+ return indexOfLoader;
+ }
+
+ mResourceLoaders.remove(indexOfLoader);
+ updateLoaders();
+ return indexOfLoader;
+ }
+ }
+
+ /**
+ * @see Resources#setLoaders(List)
+ */
+ public void setLoaders(
+ @Nullable List<Pair<ResourceLoader, ResourcesProvider>> newLoadersAndProviders) {
+ synchronized (mLock) {
+ if (ArrayUtils.isEmpty(newLoadersAndProviders)) {
+ mResourceLoaders.clear();
+ updateLoaders();
+ return;
+ }
+
+ int size = newLoadersAndProviders.size();
+ for (int newIndex = 0; newIndex < size; newIndex++) {
+ ResourceLoader resourceLoader = newLoadersAndProviders.get(newIndex).first;
+ for (int oldIndex = 0; oldIndex < mResourceLoaders.size(); oldIndex++) {
+ if (Objects.equals(mResourceLoaders.get(oldIndex).first, resourceLoader)) {
+ throw new IllegalArgumentException(
+ "Cannot add the same ResourceLoader twice");
+ }
+ }
+ }
+
+ mResourceLoaders.clear();
+ mResourceLoaders.addAll(newLoadersAndProviders);
+
+ updateLoaders();
+ }
+ }
+
+ /**
+ * Swap the tracked {@link ResourcesImpl} and reattach any loaders to it.
+ */
+ public void onImplUpdate(ResourcesImpl resourcesImpl) {
+ synchronized (mLock) {
+ this.mResourcesImpl = resourcesImpl;
+ updateLoaders();
+ }
+ }
+
+ private void updateLoaders() {
+ synchronized (mLock) {
+ AssetManager assetManager = mResourcesImpl.getAssets();
+ ApkAssets[] existingApkAssets = assetManager.getApkAssets();
+ int baseApkAssetsSize = 0;
+ for (int index = existingApkAssets.length - 1; index >= 0; index--) {
+ // Loaders are always last, so the first non-loader is the end of the base assets
+ if (!existingApkAssets[index].isForLoader()) {
+ baseApkAssetsSize = index + 1;
+ break;
+ }
+ }
+
+ List<ApkAssets> newAssets = new ArrayList<>();
+ for (int index = 0; index < baseApkAssetsSize; index++) {
+ newAssets.add(existingApkAssets[index]);
+ }
+
+ int size = mResourceLoaders.size();
+ for (int index = 0; index < size; index++) {
+ ApkAssets apkAssets = mResourceLoaders.get(index).second.getApkAssets();
+ newAssets.add(apkAssets);
+ }
+
+ assetManager.setApkAssets(newAssets.toArray(new ApkAssets[0]), true);
+
+ // Short of resolving every resource, it's too difficult to determine what has changed
+ // when a resource loader is changed, so just clear everything.
+ mResourcesImpl.clearAllCaches();
+ }
+ }
+}
diff --git a/core/java/android/content/res/loader/ResourcesProvider.java b/core/java/android/content/res/loader/ResourcesProvider.java
new file mode 100644
index 000000000000..050aeb7c5fda
--- /dev/null
+++ b/core/java/android/content/res/loader/ResourcesProvider.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res.loader;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.res.ApkAssets;
+import android.content.res.Resources;
+import android.os.ParcelFileDescriptor;
+import android.os.SharedMemory;
+
+import com.android.internal.util.ArrayUtils;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * Provides methods to load resources from an .apk or .arsc file to pass to
+ * {@link Resources#addLoader(ResourceLoader, ResourcesProvider, int)}.
+ *
+ * It is the responsibility of the app to close any instances.
+ */
+public final class ResourcesProvider implements AutoCloseable, Closeable {
+
+ /**
+ * Contains no data, assuming that any resource loading behavior will be handled in the
+ * corresponding {@link ResourceLoader}.
+ */
+ @NonNull
+ public static ResourcesProvider empty() {
+ return new ResourcesProvider(ApkAssets.loadEmptyForLoader());
+ }
+
+ /**
+ * Read from an .apk file descriptor.
+ *
+ * The file descriptor is duplicated and the one passed in may be closed by the application
+ * at any time.
+ */
+ @NonNull
+ public static ResourcesProvider loadFromApk(@NonNull ParcelFileDescriptor fileDescriptor)
+ throws IOException {
+ return new ResourcesProvider(
+ ApkAssets.loadApkForLoader(fileDescriptor.getFileDescriptor()));
+ }
+
+ /**
+ * Read from an .apk file representation in memory.
+ */
+ @NonNull
+ public static ResourcesProvider loadFromApk(@NonNull SharedMemory sharedMemory)
+ throws IOException {
+ return new ResourcesProvider(
+ ApkAssets.loadApkForLoader(sharedMemory.getFileDescriptor()));
+ }
+
+ /**
+ * Read from an .arsc file descriptor.
+ *
+ * The file descriptor is duplicated and the one passed in may be closed by the application
+ * at any time.
+ */
+ @NonNull
+ public static ResourcesProvider loadFromArsc(@NonNull ParcelFileDescriptor fileDescriptor)
+ throws IOException {
+ return new ResourcesProvider(
+ ApkAssets.loadArscForLoader(fileDescriptor.getFileDescriptor()));
+ }
+
+ /**
+ * Read from an .arsc file representation in memory.
+ */
+ @NonNull
+ public static ResourcesProvider loadFromArsc(@NonNull SharedMemory sharedMemory)
+ throws IOException {
+ return new ResourcesProvider(
+ ApkAssets.loadArscForLoader(sharedMemory.getFileDescriptor()));
+ }
+
+ /**
+ * Read from a split installed alongside the application, which may not have been
+ * loaded initially because the application requested isolated split loading.
+ */
+ @NonNull
+ public static ResourcesProvider loadFromSplit(@NonNull Context context,
+ @NonNull String splitName) throws IOException {
+ ApplicationInfo appInfo = context.getApplicationInfo();
+ int splitIndex = ArrayUtils.indexOf(appInfo.splitNames, splitName);
+ if (splitIndex < 0) {
+ throw new IllegalArgumentException("Split " + splitName + " not found");
+ }
+
+ String splitPath = appInfo.getSplitCodePaths()[splitIndex];
+ return new ResourcesProvider(ApkAssets.loadApkForLoader(splitPath));
+ }
+
+
+ @NonNull
+ private final ApkAssets mApkAssets;
+
+ private ResourcesProvider(@NonNull ApkAssets apkAssets) {
+ this.mApkAssets = apkAssets;
+ }
+
+ /** @hide */
+ @NonNull
+ public ApkAssets getApkAssets() {
+ return mApkAssets;
+ }
+
+ @Override
+ public void close() {
+ try {
+ mApkAssets.close();
+ } catch (Throwable ignored) {
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ close();
+ super.finalize();
+ }
+}
diff --git a/core/java/android/hardware/biometrics/Authenticator.java b/core/java/android/hardware/biometrics/Authenticator.java
new file mode 100644
index 000000000000..6d7e7488f2d0
--- /dev/null
+++ b/core/java/android/hardware/biometrics/Authenticator.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+/**
+ * Type of authenticators defined on a granularity that the BiometricManager / BiometricPrompt
+ * supports.
+ * @hide
+ */
+public class Authenticator {
+
+ /**
+ * Device credential, e.g. Pin/Pattern/Password.
+ */
+ public static final int TYPE_CREDENTIAL = 1 << 0;
+ /**
+ * Encompasses all biometrics on the device, e.g. Fingerprint/Iris/Face.
+ */
+ public static final int TYPE_BIOMETRIC = 1 << 1;
+
+}
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
index 0ec812fe0350..698876b9c59e 100644
--- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -36,23 +36,30 @@ public interface BiometricAuthenticator {
* @hide
*/
int TYPE_NONE = 0;
+
+ /**
+ * Constant representing credential (PIN, pattern, or password).
+ * @hide
+ */
+ int TYPE_CREDENTIAL = 1 << 0;
+
/**
* Constant representing fingerprint.
* @hide
*/
- int TYPE_FINGERPRINT = 1 << 0;
+ int TYPE_FINGERPRINT = 1 << 1;
/**
* Constant representing iris.
* @hide
*/
- int TYPE_IRIS = 1 << 1;
+ int TYPE_IRIS = 1 << 2;
/**
* Constant representing face.
* @hide
*/
- int TYPE_FACE = 1 << 2;
+ int TYPE_FACE = 1 << 3;
/**
* Container for biometric data
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index 27c04b407315..c8bf570e1bc8 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -134,6 +134,13 @@ public interface BiometricConstants {
int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14;
/**
+ * This constant is only used by SystemUI. It notifies SystemUI that authentication was paused
+ * because the authentication attempt was unsuccessful.
+ * @hide
+ */
+ int BIOMETRIC_PAUSED_REJECTED = 100;
+
+ /**
* @hide
*/
@UnsupportedAppUsage
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index d8110f33d723..9d427c8d8bab 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -137,7 +137,7 @@ public class BiometricManager {
public boolean hasEnrolledBiometrics(int userId) {
if (mService != null) {
try {
- return mService.hasEnrolledBiometrics(userId);
+ return mService.hasEnrolledBiometrics(userId, mContext.getOpPackageName());
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception in hasEnrolledBiometrics(): " + e);
return false;
@@ -201,55 +201,5 @@ public class BiometricManager {
}
}
- /**
- * TODO(b/123378871): Remove when moved.
- * @hide
- */
- @RequiresPermission(USE_BIOMETRIC_INTERNAL)
- public void onConfirmDeviceCredentialSuccess() {
- if (mService != null) {
- try {
- mService.onConfirmDeviceCredentialSuccess();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- } else {
- Slog.w(TAG, "onConfirmDeviceCredentialSuccess(): Service not connected");
- }
- }
-
- /**
- * TODO(b/123378871): Remove when moved.
- * @hide
- */
- @RequiresPermission(USE_BIOMETRIC_INTERNAL)
- public void onConfirmDeviceCredentialError(int error, String message) {
- if (mService != null) {
- try {
- mService.onConfirmDeviceCredentialError(error, message);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- } else {
- Slog.w(TAG, "onConfirmDeviceCredentialError(): Service not connected");
- }
- }
-
- /**
- * TODO(b/123378871): Remove when moved.
- * @hide
- */
- @RequiresPermission(USE_BIOMETRIC_INTERNAL)
- public void registerCancellationCallback(IBiometricConfirmDeviceCredentialCallback callback) {
- if (mService != null) {
- try {
- mService.registerCancellationCallback(callback);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- } else {
- Slog.w(TAG, "registerCancellationCallback(): Service not connected");
- }
- }
}
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index fb6b231632f1..9c51b5246749 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -26,6 +26,8 @@ import android.annotation.RequiresPermission;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.DialogInterface;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -69,24 +71,21 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
/**
* @hide
*/
- public static final String KEY_POSITIVE_TEXT = "positive_text";
- /**
- * @hide
- */
public static final String KEY_NEGATIVE_TEXT = "negative_text";
/**
* @hide
*/
public static final String KEY_REQUIRE_CONFIRMATION = "require_confirmation";
/**
+ * This is deprecated. Internally we should use {@link #KEY_AUTHENTICATORS_ALLOWED}
* @hide
*/
public static final String KEY_ALLOW_DEVICE_CREDENTIAL = "allow_device_credential";
/**
+ * If this key is set, we will ignore {@link #KEY_ALLOW_DEVICE_CREDENTIAL}
* @hide
*/
- public static final String KEY_FROM_CONFIRM_DEVICE_CREDENTIAL
- = "from_confirm_device_credential";
+ public static final String KEY_AUTHENTICATORS_ALLOWED = "authenticators_allowed";
/**
* Error/help message will show for this amount of time.
@@ -100,7 +99,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
/**
* @hide
*/
- public static final int DISMISSED_REASON_CONFIRMED = 1;
+ public static final int DISMISSED_REASON_BIOMETRIC_CONFIRMED = 1;
/**
* Dialog is done animating away after user clicked on the button set via
@@ -119,7 +118,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
* Authenticated, confirmation not required. Dialog animated away.
* @hide
*/
- public static final int DISMISSED_REASON_CONFIRM_NOT_REQUIRED = 4;
+ public static final int DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED = 4;
/**
* Error message shown on SystemUI. When BiometricService receives this, the UI is already
@@ -134,6 +133,11 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
*/
public static final int DISMISSED_REASON_SERVER_REQUESTED = 6;
+ /**
+ * @hide
+ */
+ public static final int DISMISSED_REASON_CREDENTIAL_CONFIRMED = 7;
+
private static class ButtonInfo {
Executor executor;
DialogInterface.OnClickListener listener;
@@ -203,30 +207,6 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
}
/**
- * Optional: Set the text for the positive button. If not set, the positive button
- * will not show.
- * @param text
- * @return
- * @hide
- */
- @NonNull public Builder setPositiveButton(@NonNull CharSequence text,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull DialogInterface.OnClickListener listener) {
- if (TextUtils.isEmpty(text)) {
- throw new IllegalArgumentException("Text must be set and non-empty");
- }
- if (executor == null) {
- throw new IllegalArgumentException("Executor must not be null");
- }
- if (listener == null) {
- throw new IllegalArgumentException("Listener must not be null");
- }
- mBundle.putCharSequence(KEY_POSITIVE_TEXT, text);
- mPositiveButtonInfo = new ButtonInfo(executor, listener);
- return this;
- }
-
- /**
* Required: Set the text for the negative button. This would typically be used as a
* "Cancel" button, but may be also used to show an alternative method for authentication,
* such as screen that asks for a backup password.
@@ -298,17 +278,6 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
}
/**
- * TODO(123378871): Remove when moved.
- * @return
- * @hide
- */
- @RequiresPermission(USE_BIOMETRIC_INTERNAL)
- @NonNull public Builder setFromConfirmDeviceCredential() {
- mBundle.putBoolean(KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, true);
- return this;
- }
-
- /**
* Creates a {@link BiometricPrompt}.
* @return a {@link BiometricPrompt}
* @throws IllegalArgumentException if any of the required fields are not set.
@@ -317,15 +286,19 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
final CharSequence title = mBundle.getCharSequence(KEY_TITLE);
final CharSequence negative = mBundle.getCharSequence(KEY_NEGATIVE_TEXT);
final boolean useDefaultTitle = mBundle.getBoolean(KEY_USE_DEFAULT_TITLE);
- final boolean enableFallback = mBundle.getBoolean(KEY_ALLOW_DEVICE_CREDENTIAL);
+ final boolean allowCredential = mBundle.getBoolean(KEY_ALLOW_DEVICE_CREDENTIAL);
+ final Object authenticatorsAllowed = mBundle.get(KEY_AUTHENTICATORS_ALLOWED);
if (TextUtils.isEmpty(title) && !useDefaultTitle) {
throw new IllegalArgumentException("Title must be set and non-empty");
- } else if (TextUtils.isEmpty(negative) && !enableFallback) {
+ } else if (TextUtils.isEmpty(negative) && !allowCredential) {
throw new IllegalArgumentException("Negative text must be set and non-empty");
- } else if (!TextUtils.isEmpty(negative) && enableFallback) {
+ } else if (!TextUtils.isEmpty(negative) && allowCredential) {
throw new IllegalArgumentException("Can't have both negative button behavior"
+ " and device credential enabled");
+ } else if (authenticatorsAllowed != null && allowCredential) {
+ throw new IllegalArgumentException("setAuthenticatorsAllowed and"
+ + " setDeviceCredentialAllowed should not be used simultaneously");
}
return new BiometricPrompt(mContext, mBundle, mPositiveButtonInfo, mNegativeButtonInfo);
}
@@ -368,9 +341,23 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
}
@Override
- public void onError(int error, String message) throws RemoteException {
+ public void onError(int modality, int error, int vendorCode) throws RemoteException {
mExecutor.execute(() -> {
- mAuthenticationCallback.onAuthenticationError(error, message);
+ String errorMessage;
+ switch (modality) {
+ case TYPE_FACE:
+ errorMessage = FaceManager.getErrorString(mContext, error, vendorCode);
+ break;
+
+ case TYPE_FINGERPRINT:
+ errorMessage = FingerprintManager.getErrorString(mContext, error,
+ vendorCode);
+ break;
+
+ default:
+ errorMessage = "";
+ }
+ mAuthenticationCallback.onAuthenticationError(error, errorMessage);
});
}
@@ -384,7 +371,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
@Override
public void onDialogDismissed(int reason) throws RemoteException {
// Check the reason and invoke OnClickListener(s) if necessary
- if (reason == DISMISSED_REASON_CONFIRMED) {
+ if (reason == DISMISSED_REASON_BIOMETRIC_CONFIRMED) {
mPositiveButtonInfo.executor.execute(() -> {
mPositiveButtonInfo.listener.onClick(null, DialogInterface.BUTTON_POSITIVE);
});
@@ -532,8 +519,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
public void authenticateUser(@NonNull CancellationSignal cancel,
@NonNull @CallbackExecutor Executor executor,
@NonNull AuthenticationCallback callback,
- int userId,
- IBiometricConfirmDeviceCredentialCallback confirmDeviceCredentialCallback) {
+ int userId) {
if (cancel == null) {
throw new IllegalArgumentException("Must supply a cancellation signal");
}
@@ -543,8 +529,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
if (callback == null) {
throw new IllegalArgumentException("Must supply a callback");
}
- authenticateInternal(null /* crypto */, cancel, executor, callback, userId,
- confirmDeviceCredentialCallback);
+ authenticateInternal(null /* crypto */, cancel, executor, callback, userId);
}
/**
@@ -595,8 +580,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
if (mBundle.getBoolean(KEY_ALLOW_DEVICE_CREDENTIAL)) {
throw new IllegalArgumentException("Device credential not supported with crypto");
}
- authenticateInternal(crypto, cancel, executor, callback, mContext.getUserId(),
- null /* confirmDeviceCredentialCallback */);
+ authenticateInternal(crypto, cancel, executor, callback, mContext.getUserId());
}
/**
@@ -638,8 +622,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
if (callback == null) {
throw new IllegalArgumentException("Must supply a callback");
}
- authenticateInternal(null /* crypto */, cancel, executor, callback, mContext.getUserId(),
- null /* confirmDeviceCredentialCallback */);
+ authenticateInternal(null /* crypto */, cancel, executor, callback, mContext.getUserId());
}
private void cancelAuthentication() {
@@ -656,8 +639,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
@NonNull CancellationSignal cancel,
@NonNull @CallbackExecutor Executor executor,
@NonNull AuthenticationCallback callback,
- int userId,
- IBiometricConfirmDeviceCredentialCallback confirmDeviceCredentialCallback) {
+ int userId) {
try {
if (cancel.isCanceled()) {
Log.w(TAG, "Authentication already canceled");
@@ -672,7 +654,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
final long sessionId = crypto != null ? crypto.getOpId() : 0;
if (BiometricManager.hasBiometrics(mContext)) {
mService.authenticate(mToken, sessionId, userId, mBiometricServiceReceiver,
- mContext.getOpPackageName(), mBundle, confirmDeviceCredentialCallback);
+ mContext.getOpPackageName(), mBundle);
} else {
mExecutor.execute(() -> {
callback.onAuthenticationError(BiometricPrompt.BIOMETRIC_ERROR_HW_NOT_PRESENT,
diff --git a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
new file mode 100644
index 000000000000..987d19799644
--- /dev/null
+++ b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
+import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.face.IFaceServiceReceiver;
+import android.hardware.face.Face;
+
+/**
+ * This interface encapsulates fingerprint, face, iris, etc. authenticators.
+ * Implementations of this interface are meant to be registered with BiometricService.
+ * @hide
+ */
+interface IBiometricAuthenticator {
+
+ // This method prepares the service to start authenticating, but doesn't start authentication.
+ // This is protected by the MANAGE_BIOMETRIC signature permission. This method should only be
+ // called from BiometricService. The additional uid, pid, userId arguments should be determined
+ // by BiometricService. To start authentication after the clients are ready, use
+ // startPreparedClient().
+ void prepareForAuthentication(boolean requireConfirmation, IBinder token, long sessionId,
+ int userId, IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName,
+ int cookie, int callingUid, int callingPid, int callingUserId);
+
+ // Starts authentication with the previously prepared client.
+ void startPreparedClient(int cookie);
+
+ // Same as above, with extra arguments.
+ void cancelAuthenticationFromService(IBinder token, String opPackageName,
+ int callingUid, int callingPid, int callingUserId, boolean fromClient);
+
+ // Determine if HAL is loaded and ready
+ boolean isHardwareDetected(String opPackageName);
+
+ // Determine if a user has at least one enrolled face
+ boolean hasEnrolledTemplates(int userId, String opPackageName);
+
+ // Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
+ void resetLockout(in byte [] token);
+
+ // Explicitly set the active user (for enrolling work profile)
+ void setActiveUser(int uid);
+}
diff --git a/core/java/android/hardware/biometrics/IBiometricConfirmDeviceCredentialCallback.aidl b/core/java/android/hardware/biometrics/IBiometricConfirmDeviceCredentialCallback.aidl
deleted file mode 100644
index 8b35852efd31..000000000000
--- a/core/java/android/hardware/biometrics/IBiometricConfirmDeviceCredentialCallback.aidl
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.biometrics;
-
-/**
- * Communication channel between ConfirmDeviceCredential / ConfirmLock* and BiometricService.
- * @hide
- */
-interface IBiometricConfirmDeviceCredentialCallback {
- // Invoked when authentication should be canceled.
- oneway void cancel();
-} \ No newline at end of file
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index f0a0b2f0235f..06336a55ac5e 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -17,9 +17,9 @@
package android.hardware.biometrics;
import android.os.Bundle;
-import android.hardware.biometrics.IBiometricConfirmDeviceCredentialCallback;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.IBiometricAuthenticator;
/**
* Communication channel from BiometricPrompt and BiometricManager to BiometricService. The
@@ -31,10 +31,8 @@ import android.hardware.biometrics.IBiometricServiceReceiver;
interface IBiometricService {
// Requests authentication. The service choose the appropriate biometric to use, and show
// the corresponding BiometricDialog.
- // TODO(b/123378871): Remove callback when moved.
void authenticate(IBinder token, long sessionId, int userId,
- IBiometricServiceReceiver receiver, String opPackageName, in Bundle bundle,
- IBiometricConfirmDeviceCredentialCallback callback);
+ IBiometricServiceReceiver receiver, String opPackageName, in Bundle bundle);
// Cancel authentication for the given sessionId
void cancelAuthentication(IBinder token, String opPackageName);
@@ -43,7 +41,12 @@ interface IBiometricService {
int canAuthenticate(String opPackageName, int userId);
// Checks if any biometrics are enrolled.
- boolean hasEnrolledBiometrics(int userId);
+ boolean hasEnrolledBiometrics(int userId, String opPackageName);
+
+ // Registers an authenticator (e.g. face, fingerprint, iris).
+ // Id must be unique, whereas strength and modality don't need to be.
+ // TODO(b/123321528): Turn strength and modality into enums.
+ void registerAuthenticator(int id, int strength, int modality, IBiometricAuthenticator authenticator);
// Register callback for when keyguard biometric eligibility changes.
void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback);
@@ -57,16 +60,4 @@ interface IBiometricService {
// Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
void resetLockout(in byte [] token);
-
- // TODO(b/123378871): Remove when moved.
- // CDCA needs to send results to BiometricService if it was invoked using BiometricPrompt's
- // setAllowDeviceCredential method, since there's no way for us to intercept onActivityResult.
- // CDCA is launched from BiometricService (startActivityAsUser) instead of *ForResult.
- void onConfirmDeviceCredentialSuccess();
- // TODO(b/123378871): Remove when moved.
- void onConfirmDeviceCredentialError(int error, String message);
- // TODO(b/123378871): Remove when moved.
- // When ConfirmLock* is invoked from BiometricPrompt, it needs to register a callback so that
- // it can receive the cancellation signal.
- void registerCancellationCallback(IBiometricConfirmDeviceCredentialCallback callback);
}
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
index 22ef33e86e17..c960049438f1 100644
--- a/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
@@ -25,7 +25,7 @@ oneway interface IBiometricServiceReceiver {
// Noties that authentication failed.
void onAuthenticationFailed();
// Notify BiometricPrompt that an error has occurred.
- void onError(int error, String message);
+ void onError(int modality, int error, int vendorCode);
// Notifies that a biometric has been acquired.
void onAcquired(int acquiredInfo, String message);
// Notifies that the SystemUI dialog has been dismissed.
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
index ca6114e4d842..61310f302fe4 100644
--- a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
@@ -31,11 +31,13 @@ oneway interface IBiometricServiceReceiverInternal {
void onAuthenticationFailed();
// Notify BiometricService than an error has occured. Forward to the correct receiver depending
// on the cookie.
- void onError(int cookie, int error, String message);
+ void onError(int cookie, int modality, int error, int vendorCode);
// Notifies that a biometric has been acquired.
void onAcquired(int acquiredInfo, String message);
// Notifies that the SystemUI dialog has been dismissed.
void onDialogDismissed(int reason);
// Notifies that the user has pressed the "try again" button on SystemUI
void onTryAgainPressed();
+ // Notifies that the user has pressed the "use password" button on SystemUI
+ void onDeviceCredentialPressed();
}
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 4184108871fb..4fe533b66a91 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.app.ActivityThread;
import android.content.Context;
import android.hardware.CameraInfo;
@@ -50,6 +51,7 @@ import android.view.WindowManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
@@ -112,6 +114,21 @@ public final class CameraManager {
}
/**
+ * Similar to getCameraIdList(). However, getCamerIdListNoLazy() necessarily communicates with
+ * cameraserver in order to get the list of camera ids. This is to faciliate testing since some
+ * camera ids may go 'offline' without callbacks from cameraserver because of changes in
+ * SYSTEM_CAMERA permissions (though this is not a changeable permission, tests may call
+ * adopt(drop)ShellPermissionIdentity() and effectively change their permissions). This call
+ * affects the camera ids returned by getCameraIdList() as well. Tests which do adopt shell
+ * permission identity should not mix getCameraIdList() and getCameraListNoLazyCalls().
+ */
+ /** @hide */
+ @TestApi
+ public String[] getCameraIdListNoLazy() throws CameraAccessException {
+ return CameraManagerGlobal.get().getCameraIdListNoLazy();
+ }
+
+ /**
* Register a callback to be notified about camera device availability.
*
* <p>Registering the same callback again will replace the handler with the
@@ -998,50 +1015,42 @@ public final class CameraManager {
// Camera service is now down, leave mCameraService as null
}
}
-
- /**
- * Get a list of all camera IDs that are at least PRESENT; ignore devices that are
- * NOT_PRESENT or ENUMERATING, since they cannot be used by anyone.
- */
- public String[] getCameraIdList() {
+ private String[] extractCameraIdListLocked() {
String[] cameraIds = null;
- synchronized(mLock) {
- // Try to make sure we have an up-to-date list of camera devices.
- connectCameraServiceLocked();
-
- boolean exposeAuxCamera = false;
- String packageName = ActivityThread.currentOpPackageName();
- String packageList = SystemProperties.get("vendor.camera.aux.packagelist");
- if (packageList.length() > 0) {
- TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
- splitter.setString(packageList);
- for (String str : splitter) {
- if (packageName.equals(str)) {
- exposeAuxCamera = true;
- break;
- }
+ boolean exposeAuxCamera = false;
+ String packageName = ActivityThread.currentOpPackageName();
+ String packageList = SystemProperties.get("vendor.camera.aux.packagelist");
+ if (packageList.length() > 0) {
+ TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
+ splitter.setString(packageList);
+ for (String str : splitter) {
+ if (packageName.equals(str)) {
+ exposeAuxCamera = true;
+ break;
}
}
- int idCount = 0;
- for (int i = 0; i < mDeviceStatus.size(); i++) {
- if(!exposeAuxCamera && (i == 2)) break;
- int status = mDeviceStatus.valueAt(i);
- if (status == ICameraServiceListener.STATUS_NOT_PRESENT ||
- status == ICameraServiceListener.STATUS_ENUMERATING) continue;
- idCount++;
- }
- cameraIds = new String[idCount];
- idCount = 0;
- for (int i = 0; i < mDeviceStatus.size(); i++) {
- if(!exposeAuxCamera && (i == 2)) break;
- int status = mDeviceStatus.valueAt(i);
- if (status == ICameraServiceListener.STATUS_NOT_PRESENT ||
- status == ICameraServiceListener.STATUS_ENUMERATING) continue;
- cameraIds[idCount] = mDeviceStatus.keyAt(i);
- idCount++;
- }
}
-
+ int idCount = 0;
+ for (int i = 0; i < mDeviceStatus.size(); i++) {
+ if(!exposeAuxCamera && (i == 2)) break;
+ int status = mDeviceStatus.valueAt(i);
+ if (status == ICameraServiceListener.STATUS_NOT_PRESENT
+ || status == ICameraServiceListener.STATUS_ENUMERATING) continue;
+ idCount++;
+ }
+ cameraIds = new String[idCount];
+ idCount = 0;
+ for (int i = 0; i < mDeviceStatus.size(); i++) {
+ if(!exposeAuxCamera && (i == 2)) break;
+ int status = mDeviceStatus.valueAt(i);
+ if (status == ICameraServiceListener.STATUS_NOT_PRESENT
+ || status == ICameraServiceListener.STATUS_ENUMERATING) continue;
+ cameraIds[idCount] = mDeviceStatus.keyAt(i);
+ idCount++;
+ }
+ return cameraIds;
+ }
+ private static void sortCameraIds(String[] cameraIds) {
// The sort logic must match the logic in
// libcameraservice/common/CameraProviderManager.cpp::getAPI1CompatibleCameraDeviceIds
Arrays.sort(cameraIds, new Comparator<String>() {
@@ -1072,6 +1081,89 @@ public final class CameraManager {
return s1.compareTo(s2);
}
}});
+
+ }
+
+ public static boolean cameraStatusesContains(CameraStatus[] cameraStatuses, String id) {
+ for (CameraStatus c : cameraStatuses) {
+ if (c.cameraId.equals(id)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public String[] getCameraIdListNoLazy() {
+ CameraStatus[] cameraStatuses;
+ ICameraServiceListener.Stub testListener = new ICameraServiceListener.Stub() {
+ @Override
+ public void onStatusChanged(int status, String id) throws RemoteException {
+ }
+ @Override
+ public void onTorchStatusChanged(int status, String id) throws RemoteException {
+ }
+ @Override
+ public void onCameraAccessPrioritiesChanged() {
+ }};
+
+ String[] cameraIds = null;
+ synchronized (mLock) {
+ connectCameraServiceLocked();
+ try {
+ // The purpose of the addListener, removeListener pair here is to get a fresh
+ // list of camera ids from cameraserver. We do this since for in test processes,
+ // changes can happen w.r.t non-changeable permissions (eg: SYSTEM_CAMERA
+ // permissions can be effectively changed by calling
+ // adopt(drop)ShellPermissionIdentity()).
+ // Camera devices, which have their discovery affected by these permission
+ // changes, will not have clients get callbacks informing them about these
+ // devices going offline (in real world scenarios, these permissions aren't
+ // changeable). Future calls to getCameraIdList() will reflect the changes in
+ // the camera id list after getCameraIdListNoLazy() is called.
+ cameraStatuses = mCameraService.addListener(testListener);
+ mCameraService.removeListener(testListener);
+ for (CameraStatus c : cameraStatuses) {
+ onStatusChangedLocked(c.status, c.cameraId);
+ }
+ Set<String> deviceCameraIds = mDeviceStatus.keySet();
+ ArrayList<String> deviceIdsToRemove = new ArrayList<String>();
+ for (String deviceCameraId : deviceCameraIds) {
+ // Its possible that a device id was removed without a callback notifying
+ // us. This may happen in case a process 'drops' system camera permissions
+ // (even though the permission isn't a changeable one, tests may call
+ // adoptShellPermissionIdentity() and then dropShellPermissionIdentity().
+ if (!cameraStatusesContains(cameraStatuses, deviceCameraId)) {
+ deviceIdsToRemove.add(deviceCameraId);
+ }
+ }
+ for (String id : deviceIdsToRemove) {
+ onStatusChangedLocked(ICameraServiceListener.STATUS_NOT_PRESENT, id);
+ }
+ } catch (ServiceSpecificException e) {
+ // Unexpected failure
+ throw new IllegalStateException("Failed to register a camera service listener",
+ e);
+ } catch (RemoteException e) {
+ // Camera service is now down, leave mCameraService as null
+ }
+ cameraIds = extractCameraIdListLocked();
+ }
+ sortCameraIds(cameraIds);
+ return cameraIds;
+ }
+
+ /**
+ * Get a list of all camera IDs that are at least PRESENT; ignore devices that are
+ * NOT_PRESENT or ENUMERATING, since they cannot be used by anyone.
+ */
+ public String[] getCameraIdList() {
+ String[] cameraIds = null;
+ synchronized (mLock) {
+ // Try to make sure we have an up-to-date list of camera devices.
+ connectCameraServiceLocked();
+ cameraIds = extractCameraIdListLocked();
+ }
+ sortCameraIds(cameraIds);
return cameraIds;
}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 12b285a0f0ab..55ebe285af1e 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -504,8 +504,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
public boolean isHardwareDetected() {
if (mService != null) {
try {
- long deviceId = 0; /* TODO: plumb hardware id to FPMS */
- return mService.isHardwareDetected(deviceId, mContext.getOpPackageName());
+ return mService.isHardwareDetected(mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index b6a0afbf716c..68a4aef7af08 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -67,7 +67,7 @@ interface IFaceService {
List<Face> getEnrolledFaces(int userId, String opPackageName);
// Determine if HAL is loaded and ready
- boolean isHardwareDetected(long deviceId, String opPackageName);
+ boolean isHardwareDetected(String opPackageName);
// Get a pre-enrollment authentication token
long generateChallenge(IBinder token);
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index d0622c88b4ca..22f8590a4e97 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -691,8 +691,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
public boolean isHardwareDetected() {
if (mService != null) {
try {
- long deviceId = 0; /* TODO: plumb hardware id to FPMS */
- return mService.isHardwareDetected(deviceId, mContext.getOpPackageName());
+ return mService.isHardwareDetected(mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index dd6b29d87d67..1a7e12856753 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -71,7 +71,7 @@ interface IFingerprintService {
List<Fingerprint> getEnrolledFingerprints(int groupId, String opPackageName);
// Determine if HAL is loaded and ready
- boolean isHardwareDetected(long deviceId, String opPackageName);
+ boolean isHardwareDetected(String opPackageName);
// Get a pre-enrollment authentication token
long preEnroll(IBinder token);
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 0daf30f25d59..638d81b2f635 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -76,6 +76,9 @@ interface IInputManager {
// Registers a tablet mode change listener
void registerTabletModeChangedListener(ITabletModeChangedListener listener);
+ // Queries whether the device's microphone is muted by switch
+ int isMicMuted();
+
// Input device vibrator control.
void vibrate(int deviceId, in long[] pattern, int repeat, IBinder token);
void cancelVibrate(int deviceId, IBinder token);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 2a59be28a937..0c0f248e3222 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -520,6 +520,22 @@ public final class InputManager {
}
/**
+ * Queries whether the device's microphone is muted
+ *
+ * @return The mic mute switch state which is one of {@link #SWITCH_STATE_UNKNOWN},
+ * {@link #SWITCH_STATE_OFF} or {@link #SWITCH_STATE_ON}.
+ * @hide
+ */
+ @SwitchState
+ public int isMicMuted() {
+ try {
+ return mIm.isMicMuted();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Gets information about all supported keyboard layouts.
* <p>
* The input manager consults the built-in keyboard layouts as well
diff --git a/core/java/android/hardware/location/ContextHubClientCallback.java b/core/java/android/hardware/location/ContextHubClientCallback.java
index cc2fe65dcb7e..20fa7538e2d5 100644
--- a/core/java/android/hardware/location/ContextHubClientCallback.java
+++ b/core/java/android/hardware/location/ContextHubClientCallback.java
@@ -66,7 +66,9 @@ public class ContextHubClientCallback {
public void onNanoAppAborted(ContextHubClient client, long nanoAppId, int abortCode) {}
/**
- * Callback invoked when a nanoapp is loaded at the attached Context Hub.
+ * Callback invoked when a nanoapp is dynamically loaded at the attached Context Hub through
+ * the {@link android.hardware.location.ContextHubManager}. This callback is not invoked for a
+ * nanoapp that is loaded internally by CHRE (e.g. nanoapps that are preloaded by the system).
*
* @param client the client that is associated with this callback
* @param nanoAppId the ID of the nanoapp that had been loaded
@@ -74,7 +76,8 @@ public class ContextHubClientCallback {
public void onNanoAppLoaded(ContextHubClient client, long nanoAppId) {}
/**
- * Callback invoked when a nanoapp is unloaded from the attached Context Hub.
+ * Callback invoked when a nanoapp is dynamically unloaded from the attached Context Hub through
+ * the {@link android.hardware.location.ContextHubManager}.
*
* @param client the client that is associated with this callback
* @param nanoAppId the ID of the nanoapp that had been unloaded
@@ -82,7 +85,8 @@ public class ContextHubClientCallback {
public void onNanoAppUnloaded(ContextHubClient client, long nanoAppId) {}
/**
- * Callback invoked when a nanoapp is enabled at the attached Context Hub.
+ * Callback invoked when a nanoapp is dynamically enabled at the attached Context Hub through
+ * the {@link android.hardware.location.ContextHubManager}.
*
* @param client the client that is associated with this callback
* @param nanoAppId the ID of the nanoapp that had been enabled
@@ -90,7 +94,8 @@ public class ContextHubClientCallback {
public void onNanoAppEnabled(ContextHubClient client, long nanoAppId) {}
/**
- * Callback invoked when a nanoapp is disabled at the attached Context Hub.
+ * Callback invoked when a nanoapp is dynamically disabled at the attached Context Hub through
+ * the {@link android.hardware.location.ContextHubManager}.
*
* @param client the client that is associated with this callback
* @param nanoAppId the ID of the nanoapp that had been disabled
diff --git a/core/java/android/hardware/usb/UsbDevice.java b/core/java/android/hardware/usb/UsbDevice.java
index 08c9eea6e012..ee2e2622b314 100644
--- a/core/java/android/hardware/usb/UsbDevice.java
+++ b/core/java/android/hardware/usb/UsbDevice.java
@@ -60,6 +60,11 @@ public class UsbDevice implements Parcelable {
private final int mClass;
private final int mSubclass;
private final int mProtocol;
+ private final boolean mHasAudioPlayback;
+ private final boolean mHasAudioCapture;
+ private final boolean mHasMidi;
+ private final boolean mHasVideoPlayback;
+ private final boolean mHasVideoCapture;
/** All interfaces on the device. Initialized on first call to getInterfaceList */
@UnsupportedAppUsage
@@ -73,7 +78,9 @@ public class UsbDevice implements Parcelable {
private UsbDevice(@NonNull String name, int vendorId, int productId, int Class, int subClass,
int protocol, @Nullable String manufacturerName, @Nullable String productName,
@NonNull String version, @NonNull UsbConfiguration[] configurations,
- @NonNull IUsbSerialReader serialNumberReader) {
+ @NonNull IUsbSerialReader serialNumberReader,
+ boolean hasAudioPlayback, boolean hasAudioCapture, boolean hasMidi,
+ boolean hasVideoPlayback, boolean hasVideoCapture) {
mName = Preconditions.checkNotNull(name);
mVendorId = vendorId;
mProductId = productId;
@@ -85,6 +92,11 @@ public class UsbDevice implements Parcelable {
mVersion = Preconditions.checkStringNotEmpty(version);
mConfigurations = Preconditions.checkArrayElementsNotNull(configurations, "configurations");
mSerialNumberReader = Preconditions.checkNotNull(serialNumberReader);
+ mHasAudioPlayback = hasAudioPlayback;
+ mHasAudioCapture = hasAudioCapture;
+ mHasMidi = hasMidi;
+ mHasVideoPlayback = hasVideoPlayback;
+ mHasVideoCapture = hasVideoCapture;
// Make sure the binder belongs to the system
if (ActivityThread.isSystem()) {
@@ -214,6 +226,31 @@ public class UsbDevice implements Parcelable {
return mConfigurations.length;
}
+ /** @hide */
+ public boolean getHasAudioPlayback() {
+ return mHasAudioPlayback;
+ }
+
+ /** @hide */
+ public boolean getHasAudioCapture() {
+ return mHasAudioCapture;
+ }
+
+ /** @hide */
+ public boolean getHasMidi() {
+ return mHasMidi;
+ }
+
+ /** @hide */
+ public boolean getHasVideoPlayback() {
+ return mHasVideoPlayback;
+ }
+
+ /** @hide */
+ public boolean getHasVideoCapture() {
+ return mHasVideoCapture;
+ }
+
/**
* Returns the {@link UsbConfiguration} at the given index.
*
@@ -286,12 +323,17 @@ public class UsbDevice implements Parcelable {
@Override
public String toString() {
- StringBuilder builder = new StringBuilder("UsbDevice[mName=" + mName +
- ",mVendorId=" + mVendorId + ",mProductId=" + mProductId +
- ",mClass=" + mClass + ",mSubclass=" + mSubclass + ",mProtocol=" + mProtocol +
- ",mManufacturerName=" + mManufacturerName + ",mProductName=" + mProductName +
- ",mVersion=" + mVersion + ",mSerialNumberReader=" + mSerialNumberReader
- + ",mConfigurations=[");
+ StringBuilder builder = new StringBuilder("UsbDevice[mName=" + mName
+ + ",mVendorId=" + mVendorId + ",mProductId=" + mProductId
+ + ",mClass=" + mClass + ",mSubclass=" + mSubclass + ",mProtocol=" + mProtocol
+ + ",mManufacturerName=" + mManufacturerName + ",mProductName=" + mProductName
+ + ",mVersion=" + mVersion + ",mSerialNumberReader=" + mSerialNumberReader
+ + ", mHasAudioPlayback=" + mHasAudioPlayback
+ + ", mHasAudioCapture=" + mHasAudioCapture
+ + ", mHasMidi=" + mHasMidi
+ + ", mHasVideoCapture=" + mHasVideoCapture
+ + ", mHasVideoPlayback=" + mHasVideoPlayback
+ + ", mConfigurations=[");
for (int i = 0; i < mConfigurations.length; i++) {
builder.append("\n");
builder.append(mConfigurations[i].toString());
@@ -316,9 +358,17 @@ public class UsbDevice implements Parcelable {
IUsbSerialReader.Stub.asInterface(in.readStrongBinder());
UsbConfiguration[] configurations = in.readParcelableArray(
UsbConfiguration.class.getClassLoader(), UsbConfiguration.class);
+ // Capabilities
+ boolean hasAudioPlayback = in.readInt() == 1;
+ boolean hasAudioCapture = in.readInt() == 1;
+ boolean hasMidi = in.readInt() == 1;
+ boolean hasVideoPlayback = in.readInt() == 1;
+ boolean hasVideoCapture = in.readInt() == 1;
UsbDevice device = new UsbDevice(name, vendorId, productId, clasz, subClass, protocol,
- manufacturerName, productName, version, configurations,
- serialNumberReader);
+ manufacturerName, productName, version, configurations, serialNumberReader,
+ hasAudioPlayback, hasAudioCapture, hasMidi,
+ hasVideoPlayback, hasVideoCapture);
+
return device;
}
@@ -343,7 +393,12 @@ public class UsbDevice implements Parcelable {
parcel.writeString(mVersion);
parcel.writeStrongBinder(mSerialNumberReader.asBinder());
parcel.writeParcelableArray(mConfigurations, 0);
- }
+ parcel.writeInt(mHasAudioPlayback ? 1 : 0);
+ parcel.writeInt(mHasAudioCapture ? 1 : 0);
+ parcel.writeInt(mHasMidi ? 1 : 0);
+ parcel.writeInt(mHasVideoPlayback ? 1 : 0);
+ parcel.writeInt(mHasVideoCapture ? 1 : 0);
+ }
public static int getDeviceId(String name) {
return native_get_device_id(name);
@@ -370,6 +425,11 @@ public class UsbDevice implements Parcelable {
private final @Nullable String mProductName;
private final @NonNull String mVersion;
private final @NonNull UsbConfiguration[] mConfigurations;
+ private final boolean mHasAudioPlayback;
+ private final boolean mHasAudioCapture;
+ private final boolean mHasMidi;
+ private final boolean mHasVideoPlayback;
+ private final boolean mHasVideoCapture;
// Temporary storage for serial number. Serial number reader need to be wrapped in a
// IUsbSerialReader as they might be used as PII.
@@ -378,7 +438,9 @@ public class UsbDevice implements Parcelable {
public Builder(@NonNull String name, int vendorId, int productId, int Class, int subClass,
int protocol, @Nullable String manufacturerName, @Nullable String productName,
@NonNull String version, @NonNull UsbConfiguration[] configurations,
- @Nullable String serialNumber) {
+ @Nullable String serialNumber,
+ boolean hasAudioPlayback, boolean hasAudioCapture, boolean hasMidi,
+ boolean hasVideoPlayback, boolean hasVideoCapture) {
mName = Preconditions.checkNotNull(name);
mVendorId = vendorId;
mProductId = productId;
@@ -390,6 +452,11 @@ public class UsbDevice implements Parcelable {
mVersion = Preconditions.checkStringNotEmpty(version);
mConfigurations = configurations;
this.serialNumber = serialNumber;
+ mHasAudioPlayback = hasAudioPlayback;
+ mHasAudioCapture = hasAudioCapture;
+ mHasMidi = hasMidi;
+ mHasVideoPlayback = hasVideoPlayback;
+ mHasVideoCapture = hasVideoCapture;
}
/**
@@ -401,7 +468,9 @@ public class UsbDevice implements Parcelable {
*/
public UsbDevice build(@NonNull IUsbSerialReader serialReader) {
return new UsbDevice(mName, mVendorId, mProductId, mClass, mSubclass, mProtocol,
- mManufacturerName, mProductName, mVersion, mConfigurations, serialReader);
+ mManufacturerName, mProductName, mVersion, mConfigurations, serialReader,
+ mHasAudioPlayback, mHasAudioCapture, mHasMidi,
+ mHasVideoPlayback, mHasVideoCapture);
}
}
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 83391f368ac1..43842c5c3403 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -94,6 +94,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
/**
* InputMethodService provides a standard implementation of an InputMethod,
@@ -434,6 +435,7 @@ public class InputMethodService extends AbstractInputMethodService {
final int[] mTmpLocation = new int[2];
final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> {
+ onComputeInsets(mTmpInsets);
if (isExtractViewShown()) {
// In true fullscreen mode, we just say the window isn't covering
// any content so we don't impact whatever is behind.
@@ -442,12 +444,15 @@ public class InputMethodService extends AbstractInputMethodService {
info.touchableRegion.setEmpty();
info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
} else {
- onComputeInsets(mTmpInsets);
info.contentInsets.top = mTmpInsets.contentTopInsets;
info.visibleInsets.top = mTmpInsets.visibleTopInsets;
info.touchableRegion.set(mTmpInsets.touchableRegion);
info.setTouchableInsets(mTmpInsets.touchableInsets);
}
+
+ if (mInputFrame != null) {
+ setImeExclusionRect(mTmpInsets.visibleTopInsets);
+ }
};
final View.OnClickListener mActionClickListener = v -> {
@@ -672,6 +677,14 @@ public class InputMethodService extends AbstractInputMethodService {
mPrivOps.setImeWindowStatus(visibilityFlags, backDisposition);
}
+ /** Set region of the keyboard to be avoided from back gesture */
+ private void setImeExclusionRect(int visibleTopInsets) {
+ View inputFrameRootView = mInputFrame.getRootView();
+ Rect r = new Rect(0, visibleTopInsets, inputFrameRootView.getWidth(),
+ inputFrameRootView.getHeight());
+ inputFrameRootView.setSystemGestureExclusionRects(Collections.singletonList(r));
+ }
+
/**
* Concrete implementation of
* {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 9ecf8d2c4095..274c35f2ab6d 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -473,11 +473,19 @@ public class ConnectivityManager {
public static final int TETHERING_BLUETOOTH = 2;
/**
+ * Wifi P2p tethering type.
+ * Wifi P2p tethering is set through events automatically, and don't
+ * need to start from #startTethering(int, boolean, OnStartTetheringCallback).
+ * @hide
+ */
+ public static final int TETHERING_WIFI_P2P = 3;
+
+ /**
* WIGIG tethering type. Use a separate type to prevent
* conflicts with TETHERING_WIFI
* @hide
*/
- public static final int TETHERING_WIGIG = 3;
+ public static final int TETHERING_WIGIG = 4;
/**
* Extra used for communicating with the TetherService. Includes the type of tethering to
@@ -3272,42 +3280,77 @@ public class ConnectivityManager {
/**
* Called when the framework connects and has declared a new network ready for use.
- * This callback may be called more than once if the {@link Network} that is
- * satisfying the request changes. This will always immediately be followed by a
- * call to {@link #onCapabilitiesChanged(Network, NetworkCapabilities)} then by a
- * call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}, and a call to
- * {@link #onBlockedStatusChanged(Network, boolean)}.
+ *
+ * <p>For callbacks registered with {@link #registerNetworkCallback}, multiple networks may
+ * be available at the same time, and onAvailable will be called for each of these as they
+ * appear.
+ *
+ * <p>For callbacks registered with {@link #requestNetwork} and
+ * {@link #registerDefaultNetworkCallback}, this means the network passed as an argument
+ * is the new best network for this request and is now tracked by this callback ; this
+ * callback will no longer receive method calls about other networks that may have been
+ * passed to this method previously. The previously-best network may have disconnected, or
+ * it may still be around and the newly-best network may simply be better.
+ *
+ * <p>Starting with {@link android.os.Build.VERSION_CODES#O}, this will always immediately
+ * be followed by a call to {@link #onCapabilitiesChanged(Network, NetworkCapabilities)}
+ * then by a call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}, and a call
+ * to {@link #onBlockedStatusChanged(Network, boolean)}.
+ *
+ * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or
+ * {@link #getLinkProperties(Network)} or other synchronous ConnectivityManager methods in
+ * this callback as this is prone to race conditions (there is no guarantee the objects
+ * returned by these methods will be current). Instead, wait for a call to
+ * {@link #onCapabilitiesChanged(Network, NetworkCapabilities)} and
+ * {@link #onLinkPropertiesChanged(Network, LinkProperties)} whose arguments are guaranteed
+ * to be well-ordered with respect to other callbacks.
*
* @param network The {@link Network} of the satisfying network.
*/
public void onAvailable(@NonNull Network network) {}
/**
- * Called when the network is about to be disconnected. Often paired with an
- * {@link NetworkCallback#onAvailable} call with the new replacement network
- * for graceful handover. This may not be called if we have a hard loss
- * (loss without warning). This may be followed by either a
- * {@link NetworkCallback#onLost} call or a
- * {@link NetworkCallback#onAvailable} call for this network depending
- * on whether we lose or regain it.
+ * Called when the network is about to be lost, typically because there are no outstanding
+ * requests left for it. This may be paired with a {@link NetworkCallback#onAvailable} call
+ * with the new replacement network for graceful handover. This method is not guaranteed
+ * to be called before {@link NetworkCallback#onLost} is called, for example in case a
+ * network is suddenly disconnected.
+ *
+ * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or
+ * {@link #getLinkProperties(Network)} or other synchronous ConnectivityManager methods in
+ * this callback as this is prone to race conditions ; calling these methods while in a
+ * callback may return an outdated or even a null object.
*
- * @param network The {@link Network} that is about to be disconnected.
- * @param maxMsToLive The time in ms the framework will attempt to keep the
- * network connected. Note that the network may suffer a
- * hard loss at any time.
+ * @param network The {@link Network} that is about to be lost.
+ * @param maxMsToLive The time in milliseconds the system intends to keep the network
+ * connected for graceful handover; note that the network may still
+ * suffer a hard loss at any time.
*/
public void onLosing(@NonNull Network network, int maxMsToLive) {}
/**
- * Called when the framework has a hard loss of the network or when the
- * graceful failure ends.
+ * Called when a network disconnects or otherwise no longer satisfies this request or
+ * callback.
+ *
+ * <p>If the callback was registered with requestNetwork() or
+ * registerDefaultNetworkCallback(), it will only be invoked against the last network
+ * returned by onAvailable() when that network is lost and no other network satisfies
+ * the criteria of the request.
+ *
+ * <p>If the callback was registered with registerNetworkCallback() it will be called for
+ * each network which no longer satisfies the criteria of the callback.
+ *
+ * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or
+ * {@link #getLinkProperties(Network)} or other synchronous ConnectivityManager methods in
+ * this callback as this is prone to race conditions ; calling these methods while in a
+ * callback may return an outdated or even a null object.
*
* @param network The {@link Network} lost.
*/
public void onLost(@NonNull Network network) {}
/**
- * Called if no network is found in the timeout time specified in
+ * Called if no network is found within the timeout time specified in
* {@link #requestNetwork(NetworkRequest, NetworkCallback, int)} call or if the
* requested network request cannot be fulfilled (whether or not a timeout was
* specified). When this callback is invoked the associated
@@ -3317,8 +3360,15 @@ public class ConnectivityManager {
public void onUnavailable() {}
/**
- * Called when the network the framework connected to for this request
- * changes capabilities but still satisfies the stated need.
+ * Called when the network corresponding to this request changes capabilities but still
+ * satisfies the requested criteria.
+ *
+ * <p>Starting with {@link android.os.Build.VERSION_CODES#O} this method is guaranteed
+ * to be called immediately after {@link #onAvailable}.
+ *
+ * <p>Do NOT call {@link #getLinkProperties(Network)} or other synchronous
+ * ConnectivityManager methods in this callback as this is prone to race conditions :
+ * calling these methods while in a callback may return an outdated or even a null object.
*
* @param network The {@link Network} whose capabilities have changed.
* @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this
@@ -3328,8 +3378,14 @@ public class ConnectivityManager {
@NonNull NetworkCapabilities networkCapabilities) {}
/**
- * Called when the network the framework connected to for this request
- * changes {@link LinkProperties}.
+ * Called when the network corresponding to this request changes {@link LinkProperties}.
+ *
+ * <p>Starting with {@link android.os.Build.VERSION_CODES#O} this method is guaranteed
+ * to be called immediately after {@link #onAvailable}.
+ *
+ * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or other synchronous
+ * ConnectivityManager methods in this callback as this is prone to race conditions :
+ * calling these methods while in a callback may return an outdated or even a null object.
*
* @param network The {@link Network} whose link properties have changed.
* @param linkProperties The new {@link LinkProperties} for this network.
@@ -3338,12 +3394,20 @@ public class ConnectivityManager {
@NonNull LinkProperties linkProperties) {}
/**
- * Called when the network the framework connected to for this request
- * goes into {@link NetworkInfo.State#SUSPENDED}.
- * This generally means that while the TCP connections are still live,
- * temporarily network data fails to transfer. Specifically this is used
- * on cellular networks to mask temporary outages when driving through
- * a tunnel, etc.
+ * Called when the network the framework connected to for this request suspends data
+ * transmission temporarily.
+ *
+ * <p>This generally means that while the TCP connections are still live temporarily
+ * network data fails to transfer. To give a specific example, this is used on cellular
+ * networks to mask temporary outages when driving through a tunnel, etc. In general this
+ * means read operations on sockets on this network will block once the buffers are
+ * drained, and write operations will block once the buffers are full.
+ *
+ * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or
+ * {@link #getLinkProperties(Network)} or other synchronous ConnectivityManager methods in
+ * this callback as this is prone to race conditions (there is no guarantee the objects
+ * returned by these methods will be current).
+ *
* @hide
*/
public void onNetworkSuspended(@NonNull Network network) {}
@@ -3352,6 +3416,12 @@ public class ConnectivityManager {
* Called when the network the framework connected to for this request
* returns from a {@link NetworkInfo.State#SUSPENDED} state. This should always be
* preceded by a matching {@link NetworkCallback#onNetworkSuspended} call.
+
+ * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or
+ * {@link #getLinkProperties(Network)} or other synchronous ConnectivityManager methods in
+ * this callback as this is prone to race conditions : calling these methods while in a
+ * callback may return an outdated or even a null object.
+ *
* @hide
*/
public void onNetworkResumed(@NonNull Network network) {}
@@ -3359,6 +3429,11 @@ public class ConnectivityManager {
/**
* Called when access to the specified network is blocked or unblocked.
*
+ * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or
+ * {@link #getLinkProperties(Network)} or other synchronous ConnectivityManager methods in
+ * this callback as this is prone to race conditions : calling these methods while in a
+ * callback may return an outdated or even a null object.
+ *
* @param network The {@link Network} whose blocked status has changed.
* @param blocked The blocked status of this {@link Network}.
*/
@@ -3595,13 +3670,51 @@ public class ConnectivityManager {
/**
* Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
*
- * This {@link NetworkRequest} will live until released via
- * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits. A
- * version of the method which takes a timeout is
- * {@link #requestNetwork(NetworkRequest, NetworkCallback, int)}.
- * Status of the request can be followed by listening to the various
- * callbacks described in {@link NetworkCallback}. The {@link Network}
- * can be used to direct traffic to the network.
+ * <p>This method will attempt to find the best network that matches the passed
+ * {@link NetworkRequest}, and to bring up one that does if none currently satisfies the
+ * criteria. The platform will evaluate which network is the best at its own discretion.
+ * Throughput, latency, cost per byte, policy, user preference and other considerations
+ * may be factored in the decision of what is considered the best network.
+ *
+ * <p>As long as this request is outstanding, the platform will try to maintain the best network
+ * matching this request, while always attempting to match the request to a better network if
+ * possible. If a better match is found, the platform will switch this request to the now-best
+ * network and inform the app of the newly best network by invoking
+ * {@link NetworkCallback#onAvailable(Network)} on the provided callback. Note that the platform
+ * will not try to maintain any other network than the best one currently matching the request:
+ * a network not matching any network request may be disconnected at any time.
+ *
+ * <p>For example, an application could use this method to obtain a connected cellular network
+ * even if the device currently has a data connection over Ethernet. This may cause the cellular
+ * radio to consume additional power. Or, an application could inform the system that it wants
+ * a network supporting sending MMSes and have the system let it know about the currently best
+ * MMS-supporting network through the provided {@link NetworkCallback}.
+ *
+ * <p>The status of the request can be followed by listening to the various callbacks described
+ * in {@link NetworkCallback}. The {@link Network} object passed to the callback methods can be
+ * used to direct traffic to the network (although accessing some networks may be subject to
+ * holding specific permissions). Callers will learn about the specific characteristics of the
+ * network through
+ * {@link NetworkCallback#onCapabilitiesChanged(Network, NetworkCapabilities)} and
+ * {@link NetworkCallback#onLinkPropertiesChanged(Network, LinkProperties)}. The methods of the
+ * provided {@link NetworkCallback} will only be invoked due to changes in the best network
+ * matching the request at any given time; therefore when a better network matching the request
+ * becomes available, the {@link NetworkCallback#onAvailable(Network)} method is called
+ * with the new network after which no further updates are given about the previously-best
+ * network, unless it becomes the best again at some later time. All callbacks are invoked
+ * in order on the same thread, which by default is a thread created by the framework running
+ * in the app.
+ * {@see #requestNetwork(NetworkRequest, NetworkCallback, Handler)} to change where the
+ * callbacks are invoked.
+ *
+ * <p>This{@link NetworkRequest} will live until released via
+ * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits, at
+ * which point the system may let go of the network at any time.
+ *
+ * <p>A version of this method which takes a timeout is
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback, int)}, that an app can use to only
+ * wait for a limited amount of time for the network to become unavailable.
+ *
* <p>It is presently unsupported to request a network with mutable
* {@link NetworkCapabilities} such as
* {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or
@@ -3609,7 +3722,7 @@ public class ConnectivityManager {
* as these {@code NetworkCapabilities} represent states that a particular
* network may never attain, and whether a network will attain these states
* is unknown prior to bringing up the network so the framework does not
- * know how to go about satisfing a request with these capabilities.
+ * know how to go about satisfying a request with these capabilities.
*
* <p>This method requires the caller to hold either the
* {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
@@ -3632,34 +3745,17 @@ public class ConnectivityManager {
/**
* Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
*
- * This {@link NetworkRequest} will live until released via
- * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits. A
- * version of the method which takes a timeout is
- * {@link #requestNetwork(NetworkRequest, NetworkCallback, int)}.
- * Status of the request can be followed by listening to the various
- * callbacks described in {@link NetworkCallback}. The {@link Network}
- * can be used to direct traffic to the network.
- * <p>It is presently unsupported to request a network with mutable
- * {@link NetworkCapabilities} such as
- * {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or
- * {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL}
- * as these {@code NetworkCapabilities} represent states that a particular
- * network may never attain, and whether a network will attain these states
- * is unknown prior to bringing up the network so the framework does not
- * know how to go about satisfying a request with these capabilities.
+ * This method behaves identically to {@link #requestNetwork(NetworkRequest, NetworkCallback)}
+ * but runs all the callbacks on the passed Handler.
*
- * <p>This method requires the caller to hold either the
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
- * or the ability to modify system settings as determined by
- * {@link android.provider.Settings.System#canWrite}.</p>
+ * <p>This method has the same permission requirements as
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)} and throws the same exceptions in
+ * the same conditions.
*
* @param request {@link NetworkRequest} describing this request.
* @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
* the callback must not be shared - it uniquely specifies this request.
* @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
- * @throws IllegalArgumentException if {@code request} contains invalid network capabilities.
- * @throws SecurityException if missing the appropriate permissions.
- * @throws RuntimeException if request limit per UID is exceeded.
*/
public void requestNetwork(@NonNull NetworkRequest request,
@NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
@@ -3684,10 +3780,9 @@ public class ConnectivityManager {
* timeout) - {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)} is provided
* for that purpose. Calling this method will attempt to bring up the requested network.
*
- * <p>This method requires the caller to hold either the
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
- * or the ability to modify system settings as determined by
- * {@link android.provider.Settings.System#canWrite}.</p>
+ * <p>This method has the same permission requirements as
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)} and throws the same exceptions in
+ * the same conditions.
*
* @param request {@link NetworkRequest} describing this request.
* @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
@@ -3695,9 +3790,6 @@ public class ConnectivityManager {
* @param timeoutMs The time in milliseconds to attempt looking for a suitable network
* before {@link NetworkCallback#onUnavailable()} is called. The timeout must
* be a positive value (i.e. >0).
- * @throws IllegalArgumentException if {@code request} contains invalid network capabilities.
- * @throws SecurityException if missing the appropriate permissions.
- * @throws RuntimeException if request limit per UID is exceeded.
*/
public void requestNetwork(@NonNull NetworkRequest request,
@NonNull NetworkCallback networkCallback, int timeoutMs) {
@@ -3710,21 +3802,13 @@ public class ConnectivityManager {
* Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited
* by a timeout.
*
- * This function behaves identically to the version without timeout, but if a suitable
- * network is not found within the given time (in milliseconds) the
- * {@link NetworkCallback#onUnavailable} callback is called. The request can still be
- * released normally by calling {@link #unregisterNetworkCallback(NetworkCallback)} but does
- * not have to be released if timed-out (it is automatically released). Unregistering a
- * request that timed out is not an error.
- *
- * <p>Do not use this method to poll for the existence of specific networks (e.g. with a small
- * timeout) - {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)} is provided
- * for that purpose. Calling this method will attempt to bring up the requested network.
+ * This method behaves identically to
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback, int)} but runs all the callbacks
+ * on the passed Handler.
*
- * <p>This method requires the caller to hold either the
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
- * or the ability to modify system settings as determined by
- * {@link android.provider.Settings.System#canWrite}.</p>
+ * <p>This method has the same permission requirements as
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback, int)} and throws the same exceptions
+ * in the same conditions.
*
* @param request {@link NetworkRequest} describing this request.
* @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
@@ -3732,9 +3816,6 @@ public class ConnectivityManager {
* @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
* @param timeoutMs The time in milliseconds to attempt looking for a suitable network
* before {@link NetworkCallback#onUnavailable} is called.
- * @throws IllegalArgumentException if {@code request} contains invalid network capabilities.
- * @throws SecurityException if missing the appropriate permissions.
- * @throws RuntimeException if request limit per UID is exceeded.
*/
public void requestNetwork(@NonNull NetworkRequest request,
@NonNull NetworkCallback networkCallback, @NonNull Handler handler, int timeoutMs) {
diff --git a/core/java/android/net/INetworkPolicyListener.aidl b/core/java/android/net/INetworkPolicyListener.aidl
index 106b7be5c8d3..fe9141cb6a20 100644
--- a/core/java/android/net/INetworkPolicyListener.aidl
+++ b/core/java/android/net/INetworkPolicyListener.aidl
@@ -15,6 +15,7 @@
*/
package android.net;
+import android.telephony.SubscriptionPlan;
/** {@hide} */
oneway interface INetworkPolicyListener {
@@ -22,5 +23,6 @@ oneway interface INetworkPolicyListener {
void onMeteredIfacesChanged(in String[] meteredIfaces);
void onRestrictBackgroundChanged(boolean restrictBackground);
void onUidPoliciesChanged(int uid, int uidPolicies);
- void onSubscriptionOverride(int subId, int overrideMask, int overrideValue, long networkTypeMask);
+ void onSubscriptionOverride(int subId, int overrideMask, int overrideValue);
+ void onSubscriptionPlansChanged(int subId, in SubscriptionPlan[] plans);
}
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 90327663e34b..385cb1d68b57 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -76,7 +76,7 @@ interface INetworkPolicyManager {
SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage);
void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage);
String getSubscriptionPlansOwner(int subId);
- void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, long networkTypeMask, long timeoutMillis, String callingPackage);
+ void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, long timeoutMillis, String callingPackage);
void factoryReset(String subscriber);
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index d3f48acdd40e..3ec0aeac472b 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -68,6 +68,7 @@ public final class LinkProperties implements Parcelable {
// in the format "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max"
private String mTcpBufferSizes;
private IpPrefix mNat64Prefix;
+ private boolean mWakeOnLanSupported;
private static final int MIN_MTU = 68;
private static final int MIN_MTU_V6 = 1280;
@@ -193,6 +194,7 @@ public final class LinkProperties implements Parcelable {
setMtu(source.mMtu);
mTcpBufferSizes = source.mTcpBufferSizes;
mNat64Prefix = source.mNat64Prefix;
+ mWakeOnLanSupported = source.mWakeOnLanSupported;
}
}
@@ -852,6 +854,7 @@ public final class LinkProperties implements Parcelable {
mMtu = 0;
mTcpBufferSizes = null;
mNat64Prefix = null;
+ mWakeOnLanSupported = false;
}
/**
@@ -913,6 +916,10 @@ public final class LinkProperties implements Parcelable {
resultJoiner.add("MTU:");
resultJoiner.add(Integer.toString(mMtu));
+ if (mWakeOnLanSupported) {
+ resultJoiner.add("WakeOnLanSupported: true");
+ }
+
if (mTcpBufferSizes != null) {
resultJoiner.add("TcpBufferSizes:");
resultJoiner.add(mTcpBufferSizes);
@@ -1425,6 +1432,37 @@ public final class LinkProperties implements Parcelable {
}
/**
+ * Compares this {@code LinkProperties} WakeOnLan supported against the target.
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isIdenticalWakeOnLan(LinkProperties target) {
+ return isWakeOnLanSupported() == target.isWakeOnLanSupported();
+ }
+
+ /**
+ * Set whether the network interface supports WakeOnLAN
+ *
+ * @param supported WakeOnLAN supported value
+ *
+ * @hide
+ */
+ public void setWakeOnLanSupported(boolean supported) {
+ mWakeOnLanSupported = supported;
+ }
+
+ /**
+ * Returns whether the network interface supports WakeOnLAN
+ *
+ * @return {@code true} if interface supports WakeOnLAN, {@code false} otherwise.
+ */
+ public boolean isWakeOnLanSupported() {
+ return mWakeOnLanSupported;
+ }
+
+ /**
* Compares this {@code LinkProperties} instance against the target
* LinkProperties in {@code obj}. Two LinkPropertieses are equal if
* all their fields are equal in values.
@@ -1461,7 +1499,8 @@ public final class LinkProperties implements Parcelable {
&& isIdenticalStackedLinks(target)
&& isIdenticalMtu(target)
&& isIdenticalTcpBufferSizes(target)
- && isIdenticalNat64Prefix(target);
+ && isIdenticalNat64Prefix(target)
+ && isIdenticalWakeOnLan(target);
}
/**
@@ -1577,7 +1616,8 @@ public final class LinkProperties implements Parcelable {
+ (mUsePrivateDns ? 57 : 0)
+ mPcscfs.size() * 67
+ ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode())
- + Objects.hash(mNat64Prefix);
+ + Objects.hash(mNat64Prefix)
+ + (mWakeOnLanSupported ? 71 : 0);
}
/**
@@ -1622,6 +1662,8 @@ public final class LinkProperties implements Parcelable {
ArrayList<LinkProperties> stackedLinks = new ArrayList<>(mStackedLinks.values());
dest.writeList(stackedLinks);
+
+ dest.writeBoolean(mWakeOnLanSupported);
}
/**
@@ -1677,6 +1719,7 @@ public final class LinkProperties implements Parcelable {
for (LinkProperties stackedLink: stackedLinks) {
netProp.addStackedLink(stackedLink);
}
+ netProp.setWakeOnLanSupported(in.readBoolean());
return netProp;
}
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index a809b28c9204..87295142bec4 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -85,6 +85,9 @@ public final class MacAddress implements Parcelable {
private static final long OUI_MASK = MacAddress.fromString("ff:ff:ff:0:0:0").mAddr;
private static final long NIC_MASK = MacAddress.fromString("0:0:0:ff:ff:ff").mAddr;
private static final MacAddress BASE_GOOGLE_MAC = MacAddress.fromString("da:a1:19:0:0:0");
+ /** Default wifi MAC address used for a special purpose **/
+ private static final MacAddress DEFAULT_MAC_ADDRESS =
+ MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
// Internal representation of the MAC address as a single 8 byte long.
// The encoding scheme sets the two most significant bytes to 0. The 6 bytes of the
@@ -361,16 +364,7 @@ public final class MacAddress implements Parcelable {
* @hide
*/
public static @NonNull MacAddress createRandomUnicastAddress() {
- SecureRandom r = new SecureRandom();
- long addr = r.nextLong() & VALID_LONG_MASK;
- addr |= LOCALLY_ASSIGNED_MASK;
- addr &= ~MULTICAST_MASK;
- MacAddress mac = new MacAddress(addr);
- // WifiInfo.DEFAULT_MAC_ADDRESS is being used for another purpose, so do not use it here.
- if (mac.toString().equals(WifiInfo.DEFAULT_MAC_ADDRESS)) {
- return createRandomUnicastAddress();
- }
- return mac;
+ return createRandomUnicastAddress(null, new SecureRandom());
}
/**
@@ -380,18 +374,23 @@ public final class MacAddress implements Parcelable {
* The locally assigned bit is always set to 1. The multicast bit is always set to 0.
*
* @param base a base MacAddress whose OUI is used for generating the random address.
+ * If base == null then the OUI will also be randomized.
* @param r a standard Java Random object used for generating the random address.
* @return a random locally assigned MacAddress.
*
* @hide
*/
public static @NonNull MacAddress createRandomUnicastAddress(MacAddress base, Random r) {
- long addr = (base.mAddr & OUI_MASK) | (NIC_MASK & r.nextLong());
+ long addr;
+ if (base == null) {
+ addr = r.nextLong() & VALID_LONG_MASK;
+ } else {
+ addr = (base.mAddr & OUI_MASK) | (NIC_MASK & r.nextLong());
+ }
addr |= LOCALLY_ASSIGNED_MASK;
addr &= ~MULTICAST_MASK;
MacAddress mac = new MacAddress(addr);
- // WifiInfo.DEFAULT_MAC_ADDRESS is being used for another purpose, so do not use it here.
- if (mac.toString().equals(WifiInfo.DEFAULT_MAC_ADDRESS)) {
+ if (mac.equals(DEFAULT_MAC_ADDRESS)) {
return createRandomUnicastAddress(base, r);
}
return mac;
@@ -417,7 +416,6 @@ public final class MacAddress implements Parcelable {
* @param mask MacAddress representing the mask to use during comparison.
* @return true if this MAC Address matches the given range.
*
- * @hide
*/
public boolean matches(@NonNull MacAddress baseAddress, @NonNull MacAddress mask) {
Preconditions.checkNotNull(baseAddress);
@@ -431,7 +429,6 @@ public final class MacAddress implements Parcelable {
* IPv6 address per RFC 4862.
*
* @return A link-local Inet6Address constructed from the MAC address.
- * @hide
*/
public @Nullable Inet6Address getLinkLocalIpv6FromEui48Mac() {
byte[] macEui48Bytes = toByteArray();
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 3e325b748f51..e3259ffa9d47 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -57,6 +57,9 @@ public final class NetworkCapabilities implements Parcelable {
private static final String TAG = "NetworkCapabilities";
private static final int INVALID_UID = -1;
+ // Set to true when private DNS is broken.
+ private boolean mPrivateDnsBroken;
+
/**
* @hide
*/
@@ -86,6 +89,7 @@ public final class NetworkCapabilities implements Parcelable {
mUids = null;
mEstablishingVpnAppUid = INVALID_UID;
mSSID = null;
+ mPrivateDnsBroken = false;
}
/**
@@ -104,6 +108,7 @@ public final class NetworkCapabilities implements Parcelable {
mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid;
mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities;
mSSID = nc.mSSID;
+ mPrivateDnsBroken = nc.mPrivateDnsBroken;
}
/**
@@ -557,6 +562,9 @@ public final class NetworkCapabilities implements Parcelable {
}
if (mLinkUpBandwidthKbps != 0 || mLinkDownBandwidthKbps != 0) return "link bandwidth";
if (hasSignalStrength()) return "signalStrength";
+ if (isPrivateDnsBroken()) {
+ return "privateDnsBroken";
+ }
return null;
}
@@ -1443,7 +1451,8 @@ public final class NetworkCapabilities implements Parcelable {
&& equalsSpecifier(that)
&& equalsTransportInfo(that)
&& equalsUids(that)
- && equalsSSID(that));
+ && equalsSSID(that)
+ && equalsPrivateDnsBroken(that));
}
@Override
@@ -1460,7 +1469,8 @@ public final class NetworkCapabilities implements Parcelable {
+ (mSignalStrength * 29)
+ Objects.hashCode(mUids) * 31
+ Objects.hashCode(mSSID) * 37
- + Objects.hashCode(mTransportInfo) * 41;
+ + Objects.hashCode(mTransportInfo) * 41
+ + Objects.hashCode(mPrivateDnsBroken) * 43;
}
@Override
@@ -1479,6 +1489,7 @@ public final class NetworkCapabilities implements Parcelable {
dest.writeInt(mSignalStrength);
dest.writeArraySet(mUids);
dest.writeString(mSSID);
+ dest.writeBoolean(mPrivateDnsBroken);
}
public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -1498,6 +1509,7 @@ public final class NetworkCapabilities implements Parcelable {
netCap.mUids = (ArraySet<UidRange>) in.readArraySet(
null /* ClassLoader, null for default */);
netCap.mSSID = in.readString();
+ netCap.mPrivateDnsBroken = in.readBoolean();
return netCap;
}
@Override
@@ -1555,6 +1567,10 @@ public final class NetworkCapabilities implements Parcelable {
sb.append(" SSID: ").append(mSSID);
}
+ if (mPrivateDnsBroken) {
+ sb.append(" Private DNS is broken");
+ }
+
sb.append("]");
return sb.toString();
}
@@ -1706,4 +1722,28 @@ public final class NetworkCapabilities implements Parcelable {
public boolean isMetered() {
return !hasCapability(NET_CAPABILITY_NOT_METERED);
}
+
+ /**
+ * Check if private dns is broken.
+ *
+ * @return {@code true} if {@code mPrivateDnsBroken} is set when private DNS is broken.
+ * @hide
+ */
+ public boolean isPrivateDnsBroken() {
+ return mPrivateDnsBroken;
+ }
+
+ /**
+ * Set mPrivateDnsBroken to true when private dns is broken.
+ *
+ * @param broken the status of private DNS to be set.
+ * @hide
+ */
+ public void setPrivateDnsBroken(boolean broken) {
+ mPrivateDnsBroken = broken;
+ }
+
+ private boolean equalsPrivateDnsBroken(NetworkCapabilities nc) {
+ return mPrivateDnsBroken == nc.mPrivateDnsBroken;
+ }
}
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index 9ba3bd940a96..4ad52d5aa1bc 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -77,6 +77,12 @@ public class NetworkMisc implements Parcelable {
*/
public boolean skip464xlat;
+ /**
+ * Set to true if the PRIVATE_DNS_BROKEN notification has shown for this network.
+ * Reset this bit when private DNS mode is changed from strict mode to opportunistic/off mode.
+ */
+ public boolean hasShownBroken;
+
public NetworkMisc() {
}
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 628dcd2691cf..9150aae14049 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -31,6 +31,7 @@ import android.net.wifi.WifiInfo;
import android.os.Build;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.telephony.SubscriptionPlan;
import android.util.DebugUtils;
import android.util.Pair;
import android.util.Range;
@@ -380,7 +381,8 @@ public class NetworkPolicyManager {
@Override public void onMeteredIfacesChanged(String[] meteredIfaces) { }
@Override public void onRestrictBackgroundChanged(boolean restrictBackground) { }
@Override public void onUidPoliciesChanged(int uid, int uidPolicies) { }
- @Override public void onSubscriptionOverride(int subId, int overrideMask, int overrideValue,
- long networkTypeMask) { }
+ @Override public void onSubscriptionOverride(int subId, int overrideMask,
+ int overrideValue) { }
+ @Override public void onSubscriptionPlansChanged(int subId, SubscriptionPlan[] plans) { }
}
}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 2c9333bc1a7c..ef3afabfe878 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -1085,4 +1085,13 @@ public class Binder implements IBinder {
StrictMode.clearGatheredViolations();
return res;
}
+
+ /**
+ * Returns the specified service from servicemanager. If the service is not running,
+ * servicemanager will attempt to start it, and this function will wait for it to be ready.
+ * Returns nullptr only if there are permission problems or fatal errors.
+ * @hide
+ */
+ public static final native @Nullable IBinder waitForService(@NonNull String serviceName)
+ throws RemoteException;
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index b3125d89a1a7..6a709b56f35a 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -243,7 +243,8 @@ public class Build {
public static final String BASE_OS = SystemProperties.get("ro.build.version.base_os", "");
/**
- * The user-visible security patch level.
+ * The user-visible security patch level. This value represents the date when the device
+ * most recently applied a security patch.
*/
public static final String SECURITY_PATCH = SystemProperties.get(
"ro.build.version.security_patch", "");
diff --git a/core/java/android/os/ConfigUpdate.java b/core/java/android/os/ConfigUpdate.java
index 767c15caefd0..9c999b202797 100644
--- a/core/java/android/os/ConfigUpdate.java
+++ b/core/java/android/os/ConfigUpdate.java
@@ -113,6 +113,21 @@ public final class ConfigUpdate {
public static final String ACTION_UPDATE_CARRIER_ID_DB
= "android.os.action.UPDATE_CARRIER_ID_DB";
+ /**
+ * Broadcast intent action indicating that the updated emergency number database is available.
+ * <p>Extra: "VERSION" the numeric version of the new data. Devices should only install if the
+ * update version is newer than the current one.
+ * <p>Extra: "REQUIRED_HASH" the hash of the current update data.
+ * <p>Input: {@link android.content.Intent#getData} is URI of downloaded emergency number file.
+ * Devices should pick up the downloaded file and persist to the database
+ * {@code com.android.internal.telephony.emergency.EmergencyNumberTracker}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_UPDATE_EMERGENCY_NUMBER_DB =
+ "android.os.action.UPDATE_EMERGENCY_NUMBER_DB";
+
private ConfigUpdate() {
}
}
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 3cc28197503a..bc32df42ea4f 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -278,11 +278,13 @@ public final class Debug
/** @hide */
public static final int OTHER_DALVIK_OTHER_ACCOUNTING = 22;
/** @hide */
- public static final int OTHER_DALVIK_OTHER_CODE_CACHE = 23;
+ public static final int OTHER_DALVIK_OTHER_ZYGOTE_CODE_CACHE = 23;
/** @hide */
- public static final int OTHER_DALVIK_OTHER_COMPILER_METADATA = 24;
+ public static final int OTHER_DALVIK_OTHER_APP_CODE_CACHE = 24;
/** @hide */
- public static final int OTHER_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE = 25;
+ public static final int OTHER_DALVIK_OTHER_COMPILER_METADATA = 25;
+ /** @hide */
+ public static final int OTHER_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE = 26;
/** @hide */
public static final int OTHER_DVK_STAT_DALVIK_OTHER_START =
OTHER_DALVIK_OTHER_LINEARALLOC - NUM_OTHER_STATS;
@@ -292,11 +294,11 @@ public final class Debug
// Dex subsections (Boot vdex, App dex, and App vdex).
/** @hide */
- public static final int OTHER_DEX_BOOT_VDEX = 26;
+ public static final int OTHER_DEX_BOOT_VDEX = 27;
/** @hide */
- public static final int OTHER_DEX_APP_DEX = 27;
+ public static final int OTHER_DEX_APP_DEX = 28;
/** @hide */
- public static final int OTHER_DEX_APP_VDEX = 28;
+ public static final int OTHER_DEX_APP_VDEX = 29;
/** @hide */
public static final int OTHER_DVK_STAT_DEX_START = OTHER_DEX_BOOT_VDEX - NUM_OTHER_STATS;
/** @hide */
@@ -304,9 +306,9 @@ public final class Debug
// Art subsections (App image, boot image).
/** @hide */
- public static final int OTHER_ART_APP = 29;
+ public static final int OTHER_ART_APP = 30;
/** @hide */
- public static final int OTHER_ART_BOOT = 30;
+ public static final int OTHER_ART_BOOT = 31;
/** @hide */
public static final int OTHER_DVK_STAT_ART_START = OTHER_ART_APP - NUM_OTHER_STATS;
/** @hide */
@@ -314,7 +316,7 @@ public final class Debug
/** @hide */
@UnsupportedAppUsage
- public static final int NUM_DVK_STATS = 14;
+ public static final int NUM_DVK_STATS = OTHER_ART_BOOT + 1 - OTHER_DALVIK_NORMAL;
/** @hide */
public static final int NUM_CATEGORIES = 9;
@@ -540,7 +542,8 @@ public final class Debug
case OTHER_DALVIK_NON_MOVING: return ".NonMoving";
case OTHER_DALVIK_OTHER_LINEARALLOC: return ".LinearAlloc";
case OTHER_DALVIK_OTHER_ACCOUNTING: return ".GC";
- case OTHER_DALVIK_OTHER_CODE_CACHE: return ".JITCache";
+ case OTHER_DALVIK_OTHER_ZYGOTE_CODE_CACHE: return ".ZygoteJIT";
+ case OTHER_DALVIK_OTHER_APP_CODE_CACHE: return ".AppJIT";
case OTHER_DALVIK_OTHER_COMPILER_METADATA: return ".CompilerMetadata";
case OTHER_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE: return ".IndirectRef";
case OTHER_DEX_BOOT_VDEX: return ".Boot vdex";
@@ -722,7 +725,9 @@ public final class Debug
+ getOtherPrivate(OTHER_APK)
+ getOtherPrivate(OTHER_TTF)
+ getOtherPrivate(OTHER_DEX)
- + getOtherPrivate(OTHER_OAT);
+ + getOtherPrivate(OTHER_OAT)
+ + getOtherPrivate(OTHER_DALVIK_OTHER_ZYGOTE_CODE_CACHE)
+ + getOtherPrivate(OTHER_DALVIK_OTHER_APP_CODE_CACHE);
}
/**
@@ -813,7 +818,9 @@ public final class Debug
+ getOtherRss(OTHER_APK)
+ getOtherRss(OTHER_TTF)
+ getOtherRss(OTHER_DEX)
- + getOtherRss(OTHER_OAT);
+ + getOtherRss(OTHER_OAT)
+ + getOtherRss(OTHER_DALVIK_OTHER_ZYGOTE_CODE_CACHE)
+ + getOtherRss(OTHER_DALVIK_OTHER_APP_CODE_CACHE);
}
/**
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 7a70e93b69d5..947b0a1efebd 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -69,6 +69,8 @@ public class GraphicsEnvironment {
private static final String METADATA_DRIVER_BUILD_TIME = "com.android.gamedriver.build_time";
private static final String METADATA_DEVELOPER_DRIVER_ENABLE =
"com.android.graphics.developerdriver.enable";
+ private static final String METADATA_INJECT_LAYERS_ENABLE =
+ "com.android.graphics.injectLayers.enable";
private static final String ANGLE_RULES_FILE = "a4a_rules.json";
private static final String ANGLE_TEMP_RULES = "debug.angle.rules";
private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID";
@@ -100,14 +102,16 @@ public class GraphicsEnvironment {
public void setup(Context context, Bundle coreSettings) {
final PackageManager pm = context.getPackageManager();
final String packageName = context.getPackageName();
+ final ApplicationInfo appInfoWithMetaData =
+ getAppInfoWithMetadata(context, pm, packageName);
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupGpuLayers");
- setupGpuLayers(context, coreSettings, pm, packageName);
+ setupGpuLayers(context, coreSettings, pm, packageName, appInfoWithMetaData);
Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupAngle");
setupAngle(context, coreSettings, pm, packageName);
Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "chooseDriver");
- if (!chooseDriver(context, coreSettings, pm, packageName)) {
+ if (!chooseDriver(context, coreSettings, pm, packageName, appInfoWithMetaData)) {
setGpuStats(SYSTEM_DRIVER_NAME, SYSTEM_DRIVER_VERSION_NAME, SYSTEM_DRIVER_VERSION_CODE,
SystemProperties.getLong(PROPERTY_GFX_DRIVER_BUILD_TIME, 0), packageName,
getVulkanVersion(pm));
@@ -180,6 +184,14 @@ public class GraphicsEnvironment {
}
/**
+ * Check whether application is has set the manifest metadata for layer injection.
+ */
+ private static boolean canInjectLayers(ApplicationInfo ai) {
+ return (ai.metaData != null && ai.metaData.getBoolean(METADATA_INJECT_LAYERS_ENABLE)
+ && setInjectLayersPrSetDumpable());
+ }
+
+ /**
* Store the layer paths available to the loader.
*/
public void setLayerPaths(ClassLoader classLoader,
@@ -225,15 +237,16 @@ public class GraphicsEnvironment {
* If debuggable, check for additional debug settings
*/
private void setupGpuLayers(
- Context context, Bundle coreSettings, PackageManager pm, String packageName) {
+ Context context, Bundle coreSettings, PackageManager pm, String packageName,
+ ApplicationInfo ai) {
String layerPaths = "";
// Only enable additional debug functionality if the following conditions are met:
- // 1. App is debuggable or device is rooted
+ // 1. App is debuggable or device is rooted or layer injection metadata flag is true
// 2. ENABLE_GPU_DEBUG_LAYERS is true
// 3. Package name is equal to GPU_DEBUG_APP
- if (isDebuggable(context) || (getCanLoadSystemLibraries() == 1)) {
+ if (isDebuggable(context) || (getCanLoadSystemLibraries() == 1) || canInjectLayers(ai)) {
final int enable = coreSettings.getInt(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0);
@@ -343,6 +356,20 @@ public class GraphicsEnvironment {
return -1;
}
+ private static ApplicationInfo getAppInfoWithMetadata(Context context,
+ PackageManager pm, String packageName) {
+ ApplicationInfo ai;
+ try {
+ // Get the ApplicationInfo from PackageManager so that metadata fields present.
+ ai = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
+ } catch (PackageManager.NameNotFoundException e) {
+ // Unlikely to fail for applications, but in case of failure, fall back to use the
+ // ApplicationInfo from context directly.
+ ai = context.getApplicationInfo();
+ }
+ return ai;
+ }
+
private static String getDriverForPkg(Context context, Bundle bundle, String packageName) {
final String allUseAngle;
if (bundle != null) {
@@ -693,8 +720,7 @@ public class GraphicsEnvironment {
/**
* Return the driver package name to use. Return null for system driver.
*/
- private static String chooseDriverInternal(
- Context context, Bundle coreSettings, PackageManager pm, String packageName) {
+ private static String chooseDriverInternal(Bundle coreSettings, ApplicationInfo ai) {
final String gameDriver = SystemProperties.get(PROPERTY_GFX_DRIVER);
final boolean hasGameDriver = gameDriver != null && !gameDriver.isEmpty();
@@ -709,15 +735,6 @@ public class GraphicsEnvironment {
// To minimize risk of driver updates crippling the device beyond user repair, never use an
// updated driver for privileged or non-updated system apps. Presumably pre-installed apps
// were tested thoroughly with the pre-installed driver.
- ApplicationInfo ai;
- try {
- // Get the ApplicationInfo from PackageManager so that metadata fields present.
- ai = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
- } catch (PackageManager.NameNotFoundException e) {
- // Unlikely to fail for applications, but in case of failure, fall back to use the
- // ApplicationInfo from context directly.
- ai = context.getApplicationInfo();
- }
if (ai.isPrivilegedApp() || (ai.isSystemApp() && !ai.isUpdatedSystemApp())) {
if (DEBUG) Log.v(TAG, "Ignoring driver package for privileged/non-updated system app.");
return null;
@@ -797,9 +814,9 @@ public class GraphicsEnvironment {
* Choose whether the current process should use the builtin or an updated driver.
*/
private static boolean chooseDriver(
- Context context, Bundle coreSettings, PackageManager pm, String packageName) {
- final String driverPackageName = chooseDriverInternal(context, coreSettings, pm,
- packageName);
+ Context context, Bundle coreSettings, PackageManager pm, String packageName,
+ ApplicationInfo ai) {
+ final String driverPackageName = chooseDriverInternal(coreSettings, ai);
if (driverPackageName == null) {
return false;
}
@@ -911,4 +928,5 @@ public class GraphicsEnvironment {
private static native void setAngleInfo(String path, String appPackage, String devOptIn,
FileDescriptor rulesFd, long rulesOffset, long rulesLength);
private static native boolean getShouldUseAngle(String packageName);
+ private static native boolean setInjectLayersPrSetDumpable();
}
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 9af9edae9a3f..45a9cf5ab869 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -295,6 +295,10 @@ public class Handler {
/** {@hide} */
@NonNull
public String getTraceName(@NonNull Message message) {
+ if (message.callback instanceof TraceNameSupplier) {
+ return ((TraceNameSupplier) message.callback).getTraceName();
+ }
+
final StringBuilder sb = new StringBuilder();
sb.append(getClass().getName()).append(": ");
if (message.callback != null) {
diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java
index cfb582ef442e..5e8929c6c999 100644
--- a/core/java/android/os/HwParcel.java
+++ b/core/java/android/os/HwParcel.java
@@ -23,6 +23,8 @@ import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
+import dalvik.annotation.optimization.FastNative;
+
import libcore.util.NativeAllocationRegistry;
import java.lang.annotation.Retention;
@@ -72,46 +74,54 @@ public class HwParcel {
/**
* Writes an interface token into the parcel used to verify that
- * a transaction has made it to the write type of interface.
+ * a transaction has made it to the right type of interface.
*
* @param interfaceName fully qualified name of interface message
* is being sent to.
*/
+ @FastNative
public native final void writeInterfaceToken(String interfaceName);
/**
* Writes a boolean value to the end of the parcel.
* @param val to write
*/
+ @FastNative
public native final void writeBool(boolean val);
/**
* Writes a byte value to the end of the parcel.
* @param val to write
*/
+ @FastNative
public native final void writeInt8(byte val);
/**
* Writes a short value to the end of the parcel.
* @param val to write
*/
+ @FastNative
public native final void writeInt16(short val);
/**
* Writes a int value to the end of the parcel.
* @param val to write
*/
+ @FastNative
public native final void writeInt32(int val);
/**
* Writes a long value to the end of the parcel.
* @param val to write
*/
+ @FastNative
public native final void writeInt64(long val);
/**
* Writes a float value to the end of the parcel.
* @param val to write
*/
+ @FastNative
public native final void writeFloat(float val);
/**
* Writes a double value to the end of the parcel.
* @param val to write
*/
+ @FastNative
public native final void writeDouble(double val);
/**
* Writes a String value to the end of the parcel.
@@ -120,6 +130,7 @@ public class HwParcel {
*
* @param val to write
*/
+ @FastNative
public native final void writeString(String val);
/**
* Writes a native handle (without duplicating the underlying
@@ -127,42 +138,50 @@ public class HwParcel {
*
* @param val to write
*/
+ @FastNative
public native final void writeNativeHandle(@Nullable NativeHandle val);
/**
* Writes an array of boolean values to the end of the parcel.
* @param val to write
*/
+ @FastNative
private native final void writeBoolVector(boolean[] val);
/**
* Writes an array of byte values to the end of the parcel.
* @param val to write
*/
+ @FastNative
private native final void writeInt8Vector(byte[] val);
/**
* Writes an array of short values to the end of the parcel.
* @param val to write
*/
+ @FastNative
private native final void writeInt16Vector(short[] val);
/**
* Writes an array of int values to the end of the parcel.
* @param val to write
*/
+ @FastNative
private native final void writeInt32Vector(int[] val);
/**
* Writes an array of long values to the end of the parcel.
* @param val to write
*/
+ @FastNative
private native final void writeInt64Vector(long[] val);
/**
* Writes an array of float values to the end of the parcel.
* @param val to write
*/
+ @FastNative
private native final void writeFloatVector(float[] val);
/**
* Writes an array of double values to the end of the parcel.
* @param val to write
*/
+ @FastNative
private native final void writeDoubleVector(double[] val);
/**
* Writes an array of String values to the end of the parcel.
@@ -171,6 +190,7 @@ public class HwParcel {
*
* @param val to write
*/
+ @FastNative
private native final void writeStringVector(String[] val);
/**
* Writes an array of native handles to the end of the parcel.
@@ -179,6 +199,7 @@ public class HwParcel {
*
* @param val array of {@link NativeHandle} objects to write
*/
+ @FastNative
private native final void writeNativeHandleVector(NativeHandle[] val);
/**
@@ -299,6 +320,7 @@ public class HwParcel {
* Write a hwbinder object to the end of the parcel.
* @param binder value to write
*/
+ @FastNative
public native final void writeStrongBinder(IHwBinder binder);
/**
@@ -314,48 +336,56 @@ public class HwParcel {
* @return value parsed from the parcel
* @throws IllegalArgumentException if the parcel has no more data
*/
+ @FastNative
public native final boolean readBool();
/**
* Reads a byte value from the current location in the parcel.
* @return value parsed from the parcel
* @throws IllegalArgumentException if the parcel has no more data
*/
+ @FastNative
public native final byte readInt8();
/**
* Reads a short value from the current location in the parcel.
* @return value parsed from the parcel
* @throws IllegalArgumentException if the parcel has no more data
*/
+ @FastNative
public native final short readInt16();
/**
* Reads a int value from the current location in the parcel.
* @return value parsed from the parcel
* @throws IllegalArgumentException if the parcel has no more data
*/
+ @FastNative
public native final int readInt32();
/**
* Reads a long value from the current location in the parcel.
* @return value parsed from the parcel
* @throws IllegalArgumentException if the parcel has no more data
*/
+ @FastNative
public native final long readInt64();
/**
* Reads a float value from the current location in the parcel.
* @return value parsed from the parcel
* @throws IllegalArgumentException if the parcel has no more data
*/
+ @FastNative
public native final float readFloat();
/**
* Reads a double value from the current location in the parcel.
* @return value parsed from the parcel
* @throws IllegalArgumentException if the parcel has no more data
*/
+ @FastNative
public native final double readDouble();
/**
* Reads a String value from the current location in the parcel.
* @return value parsed from the parcel
* @throws IllegalArgumentException if the parcel has no more data
*/
+ @FastNative
public native final String readString();
/**
* Reads a native handle (without duplicating the underlying file
@@ -366,6 +396,7 @@ public class HwParcel {
* @return a {@link NativeHandle} instance parsed from the parcel
* @throws IllegalArgumentException if the parcel has no more data
*/
+ @FastNative
public native final @Nullable NativeHandle readNativeHandle();
/**
* Reads an embedded native handle (without duplicating the underlying
@@ -379,6 +410,7 @@ public class HwParcel {
* @return a {@link NativeHandle} instance parsed from the parcel
* @throws IllegalArgumentException if the parcel has no more data
*/
+ @FastNative
public native final @Nullable NativeHandle readEmbeddedNativeHandle(
long parentHandle, long offset);
@@ -387,54 +419,63 @@ public class HwParcel {
* @return array of parsed values
* @throws IllegalArgumentException if the parcel has no more data
*/
+ @FastNative
private native final boolean[] readBoolVectorAsArray();
/**
* Reads an array of byte values from the parcel.
* @return array of parsed values
* @throws IllegalArgumentException if the parcel has no more data
*/
+ @FastNative
private native final byte[] readInt8VectorAsArray();
/**
* Reads an array of short values from the parcel.
* @return array of parsed values
* @throws IllegalArgumentException if the parcel has no more data
*/
+ @FastNative
private native final short[] readInt16VectorAsArray();
/**
* Reads an array of int values from the parcel.
* @return array of parsed values
* @throws IllegalArgumentException if the parcel has no more data
*/
+ @FastNative
private native final int[] readInt32VectorAsArray();
/**
* Reads an array of long values from the parcel.
* @return array of parsed values
* @throws IllegalArgumentException if the parcel has no more data
*/
+ @FastNative
private native final long[] readInt64VectorAsArray();
/**
* Reads an array of float values from the parcel.
* @return array of parsed values
* @throws IllegalArgumentException if the parcel has no more data
*/
+ @FastNative
private native final float[] readFloatVectorAsArray();
/**
* Reads an array of double values from the parcel.
* @return array of parsed values
* @throws IllegalArgumentException if the parcel has no more data
*/
+ @FastNative
private native final double[] readDoubleVectorAsArray();
/**
* Reads an array of String values from the parcel.
* @return array of parsed values
* @throws IllegalArgumentException if the parcel has no more data
*/
+ @FastNative
private native final String[] readStringVectorAsArray();
/**
* Reads an array of native handles from the parcel.
* @return array of {@link NativeHandle} objects
* @throws IllegalArgumentException if the parcel has no more data
*/
+ @FastNative
private native final NativeHandle[] readNativeHandleAsArray();
/**
@@ -537,6 +578,7 @@ public class HwParcel {
* @return binder object read from parcel or null if no binder can be read
* @throws IllegalArgumentException if the parcel has no more data
*/
+ @FastNative
public native final IHwBinder readStrongBinder();
/**
@@ -544,6 +586,7 @@ public class HwParcel {
* @return blob of size expectedSize
* @throws IllegalArgumentException if the parcel has no more data
*/
+ @FastNative
public native final HwBlob readBuffer(long expectedSize);
/**
@@ -559,6 +602,7 @@ public class HwParcel {
* @throws NullPointerException if the transaction specified the blob to be null
* but nullable is false
*/
+ @FastNative
public native final HwBlob readEmbeddedBuffer(
long expectedSize, long parentHandle, long offset,
boolean nullable);
@@ -567,26 +611,31 @@ public class HwParcel {
* Write a buffer into the transaction.
* @param blob blob to write into the parcel.
*/
+ @FastNative
public native final void writeBuffer(HwBlob blob);
/**
* Write a status value into the blob.
* @param status value to write
*/
+ @FastNative
public native final void writeStatus(int status);
/**
* @throws IllegalArgumentException if a success vaue cannot be read
* @throws RemoteException if success value indicates a transaction error
*/
+ @FastNative
public native final void verifySuccess();
/**
* Should be called to reduce memory pressure when this object no longer needs
* to be written to.
*/
+ @FastNative
public native final void releaseTemporaryStorage();
/**
* Should be called when object is no longer needed to reduce possible memory
* pressure if the Java GC does not get to this object in time.
*/
+ @FastNative
public native final void release();
/**
@@ -597,6 +646,7 @@ public class HwParcel {
// Returns address of the "freeFunction".
private static native final long native_init();
+ @FastNative
private native final void native_setup(boolean allocate);
static {
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 63641e538b8e..e8cc73f43a3d 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -18,6 +18,7 @@
package android.os;
import android.os.Bundle;
+import android.os.IUserRestrictionsListener;
import android.os.PersistableBundle;
import android.os.UserManager;
import android.content.pm.UserInfo;
@@ -41,6 +42,7 @@ interface IUserManager {
*/
UserInfo createUser(in String name, int flags);
+ UserInfo preCreateUser(int flags);
UserInfo createProfileForUser(in String name, int flags, int userHandle,
in String[] disallowedPackages);
UserInfo createRestrictedProfile(String name, int parentUserHandle);
@@ -53,7 +55,7 @@ interface IUserManager {
void setUserIcon(int userHandle, in Bitmap icon);
ParcelFileDescriptor getUserIcon(int userHandle);
UserInfo getPrimaryUser();
- List<UserInfo> getUsers(boolean excludeDying);
+ List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying, boolean excludePreCreated);
List<UserInfo> getProfiles(int userHandle, boolean enabledOnly);
int[] getProfileIds(int userId, boolean enabledOnly);
boolean canAddMoreManagedProfiles(int userHandle, boolean allowedToRemoveOne);
@@ -74,6 +76,8 @@ interface IUserManager {
boolean hasBaseUserRestriction(String restrictionKey, int userHandle);
boolean hasUserRestriction(in String restrictionKey, int userHandle);
boolean hasUserRestrictionOnAnyUser(in String restrictionKey);
+ boolean isSettingRestrictedForUser(in String setting, int userId, in String value, int callingUid);
+ void addUserRestrictionsListener(IUserRestrictionsListener listener);
void setUserRestriction(String key, boolean value, int userHandle);
void setApplicationRestrictions(in String packageName, in Bundle restrictions,
int userHandle);
@@ -92,6 +96,7 @@ interface IUserManager {
boolean someUserHasSeedAccount(in String accountName, in String accountType);
boolean isManagedProfile(int userId);
boolean isDemoUser(int userId);
+ boolean isPreCreated(int userId);
UserInfo createProfileForUserEvenWhenDisallowed(in String name, int flags, int userHandle,
in String[] disallowedPackages);
boolean isUserUnlockingOrUnlocked(int userId);
diff --git a/core/java/android/os/IUserRestrictionsListener.aidl b/core/java/android/os/IUserRestrictionsListener.aidl
new file mode 100644
index 000000000000..e7d027ff6ac7
--- /dev/null
+++ b/core/java/android/os/IUserRestrictionsListener.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+oneway interface IUserRestrictionsListener {
+ void onUserRestrictionsChanged(int userId, in Bundle newRestrictions, in Bundle prevRestrictions);
+}
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index b568f157c01d..e371df001151 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -1,2 +1,4 @@
# Zygote
per-file ZygoteProcess.java = chriswailes@google.com, ngeoffray@google.com, sehr@google.com, narayan@google.com, maco@google.com
+
+per-file GraphicsEnvironment.java = chrisforbes@google.com, cnorthrop@google.com, lpy@google.com, timvp@google.com, zzyiwei@google.com
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 50487e9e7a99..783ab44bc4e2 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -305,8 +305,11 @@ public final class Parcel {
private static native void nativeWriteFloat(long nativePtr, float val);
@FastNative
private static native void nativeWriteDouble(long nativePtr, double val);
+ @FastNative
static native void nativeWriteString(long nativePtr, String val);
+ @FastNative
private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);
+ @FastNative
private static native long nativeWriteFileDescriptor(long nativePtr, FileDescriptor val);
private static native byte[] nativeCreateByteArray(long nativePtr);
@@ -320,8 +323,11 @@ public final class Parcel {
private static native float nativeReadFloat(long nativePtr);
@CriticalNative
private static native double nativeReadDouble(long nativePtr);
+ @FastNative
static native String nativeReadString(long nativePtr);
+ @FastNative
private static native IBinder nativeReadStrongBinder(long nativePtr);
+ @FastNative
private static native FileDescriptor nativeReadFileDescriptor(long nativePtr);
private static native long nativeCreate();
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index bcb94ce2d2d5..fdb44e7050e1 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -35,12 +35,15 @@ import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.net.Uri;
import android.os.MessageQueue.OnFileDescriptorEventListener;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructStat;
import android.util.Log;
+import android.util.Size;
import dalvik.system.CloseGuard;
import dalvik.system.VMRuntime;
@@ -204,6 +207,10 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
/**
* Create a new ParcelFileDescriptor accessing a given file.
+ * <p>
+ * This method should only be used for files that you have direct access to;
+ * if you'd like to work with files hosted outside your app, use an API like
+ * {@link ContentResolver#openFile(Uri, String, CancellationSignal)}.
*
* @param file The file to be opened.
* @param mode The desired access mode, must be one of
@@ -226,6 +233,10 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
/**
* Create a new ParcelFileDescriptor accessing a given file.
+ * <p>
+ * This method should only be used for files that you have direct access to;
+ * if you'd like to work with files hosted outside your app, use an API like
+ * {@link ContentResolver#openFile(Uri, String, CancellationSignal)}.
*
* @param file The file to be opened.
* @param mode The desired access mode, must be one of
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index c9e84f3b186f..6b2000ee194f 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -420,10 +420,9 @@ public class Process {
* Background thread group - All threads in
* this group are scheduled with a reduced share of the CPU.
* Value is same as constant SP_BACKGROUND of enum SchedPolicy.
- * FIXME rename to THREAD_GROUP_BACKGROUND.
* @hide
*/
- public static final int THREAD_GROUP_BG_NONINTERACTIVE = 0;
+ public static final int THREAD_GROUP_BACKGROUND = 0;
/**
* Foreground thread group - All threads in
@@ -809,7 +808,7 @@ public class Process {
*
* group == THREAD_GROUP_DEFAULT means to move all non-background priority
* threads to the foreground scheduling group, but to leave background
- * priority threads alone. group == THREAD_GROUP_BG_NONINTERACTIVE moves all
+ * priority threads alone. group == THREAD_GROUP_BACKGROUND moves all
* threads, regardless of priority, to the background scheduling group.
* group == THREAD_GROUP_FOREGROUND is not allowed.
*
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index b6af82948da3..d4abf28d2e63 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -39,6 +39,13 @@ import java.util.HashMap;
* Gives access to the system properties store. The system properties
* store contains a list of string key-value pairs.
*
+ * <p>Use this class only for the system properties that are local. e.g., within
+ * an app, a partition, or a module. For system properties used across the
+ * boundaries, formally define them in <code>*.sysprop</code> files and use the
+ * auto-generated methods. For more information, see <a href=
+ * "https://source.android.com/devices/architecture/sysprops-apis">Implementing
+ * System Properties as APIs</a>.</p>
+ *
* {@hide}
*/
@SystemApi
diff --git a/core/java/android/os/TraceNameSupplier.java b/core/java/android/os/TraceNameSupplier.java
new file mode 100644
index 000000000000..e4b3a4edce38
--- /dev/null
+++ b/core/java/android/os/TraceNameSupplier.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os;
+
+import android.annotation.NonNull;
+
+/**
+ * Supplier for custom trace messages.
+ *
+ * @hide
+ */
+public interface TraceNameSupplier {
+
+ /**
+ * Gets the name used for trace messages.
+ */
+ @NonNull String getTraceName();
+}
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index dd5e20e39904..a9ddffe7d55c 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -22,8 +22,6 @@ import android.os.IUpdateEngine;
import android.os.IUpdateEngineCallback;
import android.os.RemoteException;
-import java.io.FileDescriptor;
-
/**
* UpdateEngine handles calls to the update engine which takes care of A/B OTA
* updates. It wraps up the update engine Binder APIs and exposes them as
@@ -315,16 +313,16 @@ public class UpdateEngine {
}
/**
- * Applies the payload passed as file descriptor {@code fd} instead of
+ * Applies the payload passed as ParcelFileDescriptor {@code pfd} instead of
* using the {@code file://} scheme.
*
* <p>See {@link #applyPayload(String)} for {@code offset}, {@code size} and
* {@code headerKeyValuePairs} parameters.
*/
- public void applyPayload(@NonNull FileDescriptor fd, long offset, long size,
+ public void applyPayload(@NonNull ParcelFileDescriptor pfd, long offset, long size,
@NonNull String[] headerKeyValuePairs) {
try {
- mUpdateEngine.applyPayloadFd(fd, offset, size, headerKeyValuePairs);
+ mUpdateEngine.applyPayloadFd(pfd, offset, size, headerKeyValuePairs);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 0754dc78a629..3558fcd24993 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -39,6 +39,7 @@ public final class UserHandle implements Parcelable {
/** @hide A user id to indicate all users on the device */
@UnsupportedAppUsage
+ @TestApi
public static final @UserIdInt int USER_ALL = -1;
/** @hide A user handle to indicate all users on the device */
@@ -69,8 +70,11 @@ public final class UserHandle implements Parcelable {
/** @hide An undefined user id */
@UnsupportedAppUsage
+ @TestApi
public static final @UserIdInt int USER_NULL = -10000;
+ private static final @NonNull UserHandle NULL = new UserHandle(USER_NULL);
+
/**
* @hide A user id constant to indicate the "owner" user of the device
* @deprecated Consider using either {@link UserHandle#USER_SYSTEM} constant or
@@ -91,6 +95,7 @@ public final class UserHandle implements Parcelable {
/** @hide A user id constant to indicate the "system" user of the device */
@UnsupportedAppUsage
+ @TestApi
public static final @UserIdInt int USER_SYSTEM = 0;
/** @hide A user serial constant to indicate the "system" user of the device */
@@ -110,6 +115,27 @@ public final class UserHandle implements Parcelable {
public static final boolean MU_ENABLED = true;
/** @hide */
+ @TestApi
+ public static final int MIN_SECONDARY_USER_ID = 10;
+
+ /**
+ * Arbitrary user handle cache size. We use the cache even when {@link #MU_ENABLED} is false
+ * anyway, so we can always assume in CTS that UserHandle.of(10) returns a cached instance
+ * even on non-multiuser devices.
+ */
+ private static final int NUM_CACHED_USERS = 4;
+
+ private static final UserHandle[] CACHED_USER_INFOS = new UserHandle[NUM_CACHED_USERS];
+
+ static {
+ // Not lazily initializing the cache, so that we can share them across processes.
+ // (We'll create them in zygote.)
+ for (int i = 0; i < CACHED_USER_INFOS.length; i++) {
+ CACHED_USER_INFOS[i] = new UserHandle(MIN_SECONDARY_USER_ID + i);
+ }
+ }
+
+ /** @hide */
@UnsupportedAppUsage
public static final int ERR_GID = -1;
/** @hide */
@@ -209,6 +235,7 @@ public final class UserHandle implements Parcelable {
* @hide
*/
@UnsupportedAppUsage
+ @TestApi
public static @UserIdInt int getUserId(int uid) {
if (MU_ENABLED) {
return uid / PER_USER_RANGE;
@@ -229,9 +256,31 @@ public final class UserHandle implements Parcelable {
}
/** @hide */
+ @TestApi
@SystemApi
public static UserHandle of(@UserIdInt int userId) {
- return userId == USER_SYSTEM ? SYSTEM : new UserHandle(userId);
+ if (userId == USER_SYSTEM) {
+ return SYSTEM; // Most common.
+ }
+ // These are sequential; so use a switch. Maybe they'll be optimized to a table lookup.
+ switch (userId) {
+ case USER_ALL:
+ return ALL;
+
+ case USER_CURRENT:
+ return CURRENT;
+
+ case USER_CURRENT_OR_SELF:
+ return CURRENT_OR_SELF;
+ }
+ if (userId >= MIN_SECONDARY_USER_ID
+ && userId < (MIN_SECONDARY_USER_ID + CACHED_USER_INFOS.length)) {
+ return CACHED_USER_INFOS[userId - MIN_SECONDARY_USER_ID];
+ }
+ if (userId == USER_NULL) { // Not common.
+ return NULL;
+ }
+ return new UserHandle(userId);
}
/**
@@ -239,6 +288,7 @@ public final class UserHandle implements Parcelable {
* @hide
*/
@UnsupportedAppUsage
+ @TestApi
public static int getUid(@UserIdInt int userId, @AppIdInt int appId) {
if (MU_ENABLED) {
return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);
@@ -404,6 +454,7 @@ public final class UserHandle implements Parcelable {
* @hide
*/
@SystemApi
+ @TestApi
public static @UserIdInt int myUserId() {
return getUserId(Process.myUid());
}
@@ -513,7 +564,9 @@ public final class UserHandle implements Parcelable {
public static final @android.annotation.NonNull Parcelable.Creator<UserHandle> CREATOR
= new Parcelable.Creator<UserHandle>() {
public UserHandle createFromParcel(Parcel in) {
- return new UserHandle(in);
+ // Try to avoid allocation; use of() here. Keep this and the constructor below
+ // in sync.
+ return UserHandle.of(in.readInt());
}
public UserHandle[] newArray(int size) {
@@ -532,6 +585,6 @@ public final class UserHandle implements Parcelable {
* positioned at the location in the buffer where it was written.
*/
public UserHandle(Parcel in) {
- mHandle = in.readInt();
+ mHandle = in.readInt(); // Keep this and createFromParcel() in sync.
}
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index af574da084f0..71b94ed41351 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -37,6 +37,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.UserInfo;
+import android.content.pm.UserInfo.UserInfoFlag;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -1747,8 +1748,30 @@ public class UserManager {
}
}
- /** {@hide} */
- public boolean isUserUnlockingOrUnlocked(UserHandle user) {
+ /**
+ * Return whether the provided user is already running in an
+ * "unlocked" state or in the process of unlocking.
+ * <p>
+ * On devices with direct boot, a user is unlocked only after they've
+ * entered their credentials (such as a lock pattern or PIN). On devices
+ * without direct boot, a user is unlocked as soon as it starts.
+ * <p>
+ * When a user is locked, only device-protected data storage is available.
+ * When a user is unlocked, both device-protected and credential-protected
+ * private app data storage is available.
+ *
+ * <p>Requires {@code android.permission.MANAGE_USERS} or
+ * {@code android.permission.INTERACT_ACROSS_USERS}, otherwise specified {@link UserHandle user}
+ * must be the calling user or a profile associated with it.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.INTERACT_ACROSS_USERS
+ })
+ public boolean isUserUnlockingOrUnlocked(@NonNull UserHandle user) {
return isUserUnlockingOrUnlocked(user.getIdentifier());
}
@@ -1985,6 +2008,38 @@ public class UserManager {
}
/**
+ * @hide
+ *
+ * Checks whether changing the given setting to the given value is prohibited
+ * by the corresponding user restriction in the given user.
+ *
+ * May only be called by the OS itself.
+ *
+ * @return {@code true} if the change is prohibited, {@code false} if the change is allowed.
+ */
+ public boolean isSettingRestrictedForUser(String setting, @UserIdInt int userId,
+ String value, int callingUid) {
+ try {
+ return mService.isSettingRestrictedForUser(setting, userId, value, callingUid);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Register a binder callback for user restrictions changes.
+ * May only be called by the OS itself.
+ */
+ public void addUserRestrictionsListener(final IUserRestrictionsListener listener) {
+ try {
+ mService.addUserRestrictionsListener(listener);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Return the serial number for a user. This is a device-unique
* number assigned to that user; if the user is deleted and then a new
* user created, the new users will not be given the same serial number.
@@ -2013,18 +2068,20 @@ public class UserManager {
/**
* Creates a user with the specified name and options. For non-admin users, default user
- * restrictions are going to be applied.
- * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ * restrictions will be applied.
+ *
+ * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
*
* @param name the user's name
- * @param flags flags that identify the type of user and other properties.
+ * @param flags UserInfo flags that identify the type of user and other properties.
* @see UserInfo
*
- * @return the UserInfo object for the created user, or null if the user could not be created.
+ * @return the UserInfo object for the created user, or {@code null} if the user could not be
+ * created.
* @hide
*/
@UnsupportedAppUsage
- public UserInfo createUser(String name, int flags) {
+ public @Nullable UserInfo createUser(@Nullable String name, @UserInfoFlag int flags) {
UserInfo user = null;
try {
user = mService.createUser(name, flags);
@@ -2041,6 +2098,36 @@ public class UserManager {
}
/**
+ * Pre-creates a user with the specified name and options. For non-admin users, default user
+ * restrictions will be applied.
+ *
+ * <p>This method can be used by OEMs to "warm" up the user creation by pre-creating some users
+ * at the first boot, so they when the "real" user is created (for example,
+ * by {@link #createUser(String, int)} or {@link #createGuest(Context, String)}), it takes
+ * less time.
+ *
+ * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ *
+ * @param flags UserInfo flags that identify the type of user and other properties.
+ * @see UserInfo
+ *
+ * @return the UserInfo object for the created user, or {@code null} if the user could not be
+ * created.
+ *
+ * @throw {@link IllegalArgumentException} if {@code flags} contains
+ * {@link UserInfo#FLAG_MANAGED_PROFILE}.
+ *
+ * @hide
+ */
+ public @Nullable UserInfo preCreateUser(@UserInfoFlag int flags) {
+ try {
+ return mService.preCreateUser(flags);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Creates a guest user and configures it.
* @param context an application context
* @param name the name to set for the user
@@ -2356,15 +2443,26 @@ public class UserManager {
/**
* Returns information for all users on this device, including ones marked for deletion.
* To retrieve only users that are alive, use {@link #getUsers(boolean)}.
- * <p>
- * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ *
* @return the list of users that exist on the device.
* @hide
*/
@UnsupportedAppUsage
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public List<UserInfo> getUsers() {
+ return getUsers(/* excludeDying= */ false);
+ }
+
+ /**
+ * Returns information for all users on this device, based on the filtering parameters.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ public List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying,
+ boolean excludePreCreated) {
try {
- return mService.getUsers(false);
+ return mService.getUsers(excludePartial, excludeDying, excludePreCreated);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2380,16 +2478,12 @@ public class UserManager {
@SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public long[] getSerialNumbersOfUsers(boolean excludeDying) {
- try {
- List<UserInfo> users = mService.getUsers(excludeDying);
- long[] result = new long[users.size()];
- for (int i = 0; i < result.length; i++) {
- result[i] = users.get(i).serialNumber;
- }
- return result;
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
+ List<UserInfo> users = getUsers(excludeDying);
+ long[] result = new long[users.size()];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = users.get(i).serialNumber;
}
+ return result;
}
/**
@@ -2496,6 +2590,22 @@ public class UserManager {
}
/**
+ * Checks if the 2 provided user handles belong to the same profile group.
+ *
+ * @param user one of the two user handles to check.
+ * @param otherUser one of the two user handles to check.
+ * @return true if the two users are in the same profile group.
+ *
+ * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ public boolean isSameProfileGroup(@NonNull UserHandle user, @NonNull UserHandle otherUser) {
+ return isSameProfileGroup(user.getIdentifier(), otherUser.getIdentifier());
+ }
+
+ /**
* Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
* @param userId one of the two user ids to check.
* @param otherUserId one of the two user ids to check.
@@ -2773,11 +2883,8 @@ public class UserManager {
*/
@UnsupportedAppUsage
public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
- try {
- return mService.getUsers(excludeDying);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
+ return getUsers(/*excludePartial= */ true, excludeDying,
+ /* excludePreCreated= */ true);
}
/**
diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java
deleted file mode 100644
index a5b71f64668d..000000000000
--- a/core/java/android/os/UserManagerInternal.java
+++ /dev/null
@@ -1,240 +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 android.os;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.content.Context;
-import android.content.pm.UserInfo;
-import android.graphics.Bitmap;
-
-/**
- * @hide Only for use within the system server.
- */
-public abstract class UserManagerInternal {
- public static final int CAMERA_NOT_DISABLED = 0;
- public static final int CAMERA_DISABLED_LOCALLY = 1;
- public static final int CAMERA_DISABLED_GLOBALLY = 2;
-
- public interface UserRestrictionsListener {
- /**
- * Called when a user restriction changes.
- *
- * @param userId target user id
- * @param newRestrictions new user restrictions
- * @param prevRestrictions user restrictions that were previously set
- */
- void onUserRestrictionsChanged(int userId, Bundle newRestrictions, Bundle prevRestrictions);
- }
-
- /**
- * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to set
- * restrictions enforced by the user.
- *
- * @param userId target user id for the local restrictions.
- * @param restrictions a bundle of user restrictions.
- * @param isDeviceOwner whether {@code userId} corresponds to device owner user id.
- * @param cameraRestrictionScope is camera disabled and if so what is the scope of restriction.
- * Should be one of {@link #CAMERA_NOT_DISABLED}, {@link #CAMERA_DISABLED_LOCALLY} or
- * {@link #CAMERA_DISABLED_GLOBALLY}
- */
- public abstract void setDevicePolicyUserRestrictions(int userId, @Nullable Bundle restrictions,
- boolean isDeviceOwner, int cameraRestrictionScope);
-
- /**
- * Returns the "base" user restrictions.
- *
- * Used by {@link com.android.server.devicepolicy.DevicePolicyManagerService} for upgrading
- * from MNC.
- */
- public abstract Bundle getBaseUserRestrictions(int userId);
-
- /**
- * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} for upgrading
- * from MNC.
- */
- public abstract void setBaseUserRestrictionsByDpmsForMigration(int userId,
- Bundle baseRestrictions);
-
- /** Return a user restriction. */
- public abstract boolean getUserRestriction(int userId, String key);
-
- /** Adds a listener to user restriction changes. */
- public abstract void addUserRestrictionsListener(UserRestrictionsListener listener);
-
- /** Remove a {@link UserRestrictionsListener}. */
- public abstract void removeUserRestrictionsListener(UserRestrictionsListener listener);
-
- /**
- * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to update
- * whether the device is managed by device owner.
- */
- public abstract void setDeviceManaged(boolean isManaged);
-
- /**
- * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to update
- * whether the user is managed by profile owner.
- */
- public abstract void setUserManaged(int userId, boolean isManaged);
-
- /**
- * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to omit
- * restriction check, because DevicePolicyManager must always be able to set user icon
- * regardless of any restriction.
- * Also called by {@link com.android.server.pm.UserManagerService} because the logic of setting
- * the icon is in this method.
- */
- public abstract void setUserIcon(int userId, Bitmap bitmap);
-
- /**
- * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to inform the
- * user manager whether all users should be created ephemeral.
- */
- public abstract void setForceEphemeralUsers(boolean forceEphemeralUsers);
-
- /**
- * Switches to the system user and deletes all other users.
- *
- * <p>Called by the {@link com.android.server.devicepolicy.DevicePolicyManagerService} when
- * the force-ephemeral-users policy is toggled on to make sure there are no pre-existing
- * non-ephemeral users left.
- */
- public abstract void removeAllUsers();
-
- /**
- * Called by the activity manager when the ephemeral user goes to background and its removal
- * starts as a result.
- *
- * <p>It marks the ephemeral user as disabled in order to prevent it from being re-entered
- * before its removal finishes.
- *
- * @param userId the ID of the ephemeral user.
- */
- public abstract void onEphemeralUserStop(int userId);
-
- /**
- * Same as UserManager.createUser(), but bypasses the check for
- * {@link UserManager#DISALLOW_ADD_USER} and {@link UserManager#DISALLOW_ADD_MANAGED_PROFILE}
- *
- * <p>Called by the {@link com.android.server.devicepolicy.DevicePolicyManagerService} when
- * createAndManageUser is called by the device owner.
- */
- public abstract UserInfo createUserEvenWhenDisallowed(String name, int flags,
- String[] disallowedPackages);
-
- /**
- * Same as {@link UserManager#removeUser(int userId)}, but bypasses the check for
- * {@link UserManager#DISALLOW_REMOVE_USER} and
- * {@link UserManager#DISALLOW_REMOVE_MANAGED_PROFILE} and does not require the
- * {@link android.Manifest.permission#MANAGE_USERS} permission.
- */
- public abstract boolean removeUserEvenWhenDisallowed(int userId);
-
- /**
- * Return whether the given user is running in an
- * {@code UserState.STATE_RUNNING_UNLOCKING} or
- * {@code UserState.STATE_RUNNING_UNLOCKED} state.
- */
- public abstract boolean isUserUnlockingOrUnlocked(int userId);
-
- /**
- * Return whether the given user is running in an
- * {@code UserState.STATE_RUNNING_UNLOCKED} state.
- */
- public abstract boolean isUserUnlocked(int userId);
-
- /**
- * Returns whether the given user is running
- */
- public abstract boolean isUserRunning(int userId);
-
- /**
- * Returns whether the given user is initialized
- */
- public abstract boolean isUserInitialized(int userId);
-
- /**
- * Returns whether the given user exists
- */
- public abstract boolean exists(int userId);
-
- /**
- * Set user's running state
- */
- public abstract void setUserState(int userId, int userState);
-
- /**
- * Remove user's running state
- */
- public abstract void removeUserState(int userId);
-
- /**
- * Returns an array of user ids. This array is cached in UserManagerService and passed as a
- * reference, so do not modify the returned array.
- *
- * @return the array of user ids.
- */
- public abstract int[] getUserIds();
-
- /**
- * Checks if the {@code callingUserId} and {@code targetUserId} are same or in same group
- * and that the {@code callingUserId} is not a managed profile and
- * {@code targetUserId} is enabled.
- *
- * @return TRUE if the {@code callingUserId} can access {@code targetUserId}. FALSE
- * otherwise
- *
- * @throws SecurityException if the calling user and {@code targetUser} are not in the same
- * group and {@code throwSecurityException} is true, otherwise if will simply return false.
- */
- public abstract boolean isProfileAccessible(int callingUserId, int targetUserId,
- String debugMsg, boolean throwSecurityException);
-
- /**
- * If {@code userId} is of a managed profile, return the parent user ID. Otherwise return
- * itself.
- */
- public abstract int getProfileParentId(int userId);
-
- /**
- * Checks whether changing a setting to a value is prohibited by the corresponding user
- * restriction.
- *
- * <p>See also {@link com.android.server.pm.UserRestrictionsUtils#applyUserRestriction(
- * Context, int, String, boolean)}, which should be in sync with this method.
- *
- * @return {@code true} if the change is prohibited, {@code false} if the change is allowed.
- *
- * @hide
- */
- public abstract boolean isSettingRestrictedForUser(String setting, int userId, String value,
- int callingUid);
-
- /** @return a specific user restriction that's in effect currently. */
- public abstract boolean hasUserRestriction(String restriction, int userId);
-
- /**
- * Gets an {@link UserInfo} for the given {@code userId}, or {@code null} if not
- * found.
- */
- public abstract @Nullable UserInfo getUserInfo(@UserIdInt int userId);
-
- /**
- * Gets all {@link UserInfo UserInfos}.
- */
- public abstract @NonNull UserInfo[] getUserInfos();
-}
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index eaf9929c56d0..907eae87bcb2 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -576,6 +576,8 @@ public class ZygoteProcess {
argsForZygote.add("--mount-external-installer");
} else if (mountExternal == Zygote.MOUNT_EXTERNAL_LEGACY) {
argsForZygote.add("--mount-external-legacy");
+ } else if (mountExternal == Zygote.MOUNT_EXTERNAL_PASS_THROUGH) {
+ argsForZygote.add("--mount-external-pass-through");
}
argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
@@ -756,6 +758,7 @@ public class ZygoteProcess {
ZygoteState state = openZygoteSocketIfNeeded(abi);
state.mZygoteOutputWriter.write("1\n--boot-completed\n");
state.mZygoteOutputWriter.flush();
+ state.mZygoteInputStream.readInt();
}
} catch (Exception ex) {
throw new RuntimeException("Failed to inform zygote of boot_completed", ex);
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 5b9205d16898..2d8af83c7ca7 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -153,6 +153,11 @@ public class StorageManager {
public static final String PROP_ISOLATED_STORAGE = "persist.sys.isolated_storage";
/** {@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_FUSE_SNAPSHOT = "sys.fuse_snapshot";
+
/** {@hide} */
public static final String UUID_PRIVATE_INTERNAL = null;
@@ -1575,7 +1580,14 @@ public class StorageManager {
/** {@hide} */
public static boolean hasAdoptable() {
- return SystemProperties.getBoolean(PROP_HAS_ADOPTABLE, false);
+ switch (SystemProperties.get(PROP_ADOPTABLE)) {
+ case "force_on":
+ return true;
+ case "force_off":
+ return false;
+ default:
+ return SystemProperties.getBoolean(PROP_HAS_ADOPTABLE, false);
+ }
}
/**
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index 654b0f7d30aa..26c1ec170834 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -31,8 +31,8 @@ oneway interface IPermissionController {
void revokeRuntimePermissions(in Bundle request, boolean doDryRun, int reason,
String callerPackageName, in AndroidFuture callback);
void getRuntimePermissionBackup(in UserHandle user, in ParcelFileDescriptor pipe);
- void restoreRuntimePermissionBackup(in UserHandle user, in ParcelFileDescriptor pipe);
- void restoreDelayedRuntimePermissionBackup(String packageName, in UserHandle user,
+ void stageAndApplyRuntimePermissionsBackup(in UserHandle user, in ParcelFileDescriptor pipe);
+ void applyStagedRuntimePermissionBackup(String packageName, in UserHandle user,
in AndroidFuture callback);
void getAppPermissions(String packageName, in AndroidFuture callback);
void revokeRuntimePermission(String packageName, String permissionName);
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 923d9f85cc79..421e29ed0542 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -62,6 +62,7 @@ import libcore.util.EmptyArray;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -139,20 +140,6 @@ public final class PermissionControllerManager {
}
/**
- * Callback for delivering the result of {@link #getRuntimePermissionBackup}.
- *
- * @hide
- */
- public interface OnGetRuntimePermissionBackupCallback {
- /**
- * The result for {@link #getRuntimePermissionBackup}.
- *
- * @param backup The backup file
- */
- void onGetRuntimePermissionsBackup(@NonNull byte[] backup);
- }
-
- /**
* Callback for delivering the result of {@link #getAppPermissions}.
*
* @hide
@@ -246,6 +233,24 @@ public final class PermissionControllerManager {
}
/**
+ * Throw a {@link SecurityException} if not at least one of the permissions is granted.
+ *
+ * @param requiredPermissions A list of permissions. Any of of them if sufficient to pass the
+ * check
+ */
+ private void enforceSomePermissionsGrantedToSelf(@NonNull String... requiredPermissions) {
+ for (String requiredPermission : requiredPermissions) {
+ if (mContext.checkSelfPermission(requiredPermission)
+ == PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ }
+
+ throw new SecurityException("At lest one of the following permissions is required: "
+ + Arrays.toString(requiredPermissions));
+ }
+
+ /**
* Revoke a set of runtime permissions for various apps.
*
* @param request The permissions to revoke as {@code Map<packageName, List<permission>>}
@@ -268,11 +273,7 @@ public final class PermissionControllerManager {
}
// Check required permission to fail immediately instead of inside the oneway binder call
- if (mContext.checkSelfPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
- + " required");
- }
+ enforceSomePermissionsGrantedToSelf(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
mRemoteService.postAsync(service -> {
Bundle bundledizedRequest = new Bundle();
@@ -358,46 +359,61 @@ public final class PermissionControllerManager {
*
* @param user The user to be backed up
* @param executor Executor on which to invoke the callback
- * @param callback Callback to receive the result
- *
- * @hide
+ * @param callback Callback to receive the result. The resulting backup-file is opaque and no
+ * guarantees are made other than that the file can be send to
+ * {@link #restoreRuntimePermissionBackup} in this and future versions of
+ * Android.
*/
@RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
public void getRuntimePermissionBackup(@NonNull UserHandle user,
@NonNull @CallbackExecutor Executor executor,
- @NonNull OnGetRuntimePermissionBackupCallback callback) {
+ @NonNull Consumer<byte[]> callback) {
checkNotNull(user);
checkNotNull(executor);
checkNotNull(callback);
+ // Check required permission to fail immediately instead of inside the oneway binder call
+ enforceSomePermissionsGrantedToSelf(Manifest.permission.GET_RUNTIME_PERMISSIONS);
+
mRemoteService.postAsync(service -> RemoteStream.receiveBytes(remotePipe -> {
service.getRuntimePermissionBackup(user, remotePipe);
})).whenCompleteAsync((bytes, err) -> {
if (err != null) {
Log.e(TAG, "Error getting permission backup", err);
- callback.onGetRuntimePermissionsBackup(EmptyArray.BYTE);
+ callback.accept(EmptyArray.BYTE);
} else {
- callback.onGetRuntimePermissionsBackup(bytes);
+ callback.accept(bytes);
}
}, executor);
}
/**
- * Restore a backup of the runtime permissions.
+ * Restore a {@link #getRuntimePermissionBackup backup-file} of the runtime permissions.
*
- * @param backup the backup to restore. The backup is sent asynchronously, hence it should not
- * be modified after calling this method.
- * @param user The user to be restore
+ * <p>This might leave some part of the backup-file unapplied if an package mentioned in the
+ * backup-file is not yet installed. It is required that
+ * {@link #applyStagedRuntimePermissionBackup} is called after any package is installed to
+ * apply the rest of the backup-file.
*
- * @hide
+ * @param backup the backup-file to restore. The backup is sent asynchronously, hence it should
+ * not be modified after calling this method.
+ * @param user The user to be restore
*/
- @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
- public void restoreRuntimePermissionBackup(@NonNull byte[] backup, @NonNull UserHandle user) {
+ @RequiresPermission(anyOf = {
+ Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+ Manifest.permission.RESTORE_RUNTIME_PERMISSIONS
+ })
+ public void stageAndApplyRuntimePermissionsBackup(@NonNull byte[] backup,
+ @NonNull UserHandle user) {
checkNotNull(backup);
checkNotNull(user);
+ // Check required permission to fail immediately instead of inside the oneway binder call
+ enforceSomePermissionsGrantedToSelf(Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+ Manifest.permission.RESTORE_RUNTIME_PERMISSIONS);
+
mRemoteService.postAsync(service -> RemoteStream.sendBytes(remotePipe -> {
- service.restoreRuntimePermissionBackup(user, remotePipe);
+ service.stageAndApplyRuntimePermissionsBackup(user, remotePipe);
}, backup))
.whenComplete((nullResult, err) -> {
if (err != null) {
@@ -407,17 +423,22 @@ public final class PermissionControllerManager {
}
/**
- * Restore a backup of the runtime permissions that has been delayed.
+ * Restore unapplied parts of a {@link #stageAndApplyRuntimePermissionsBackup previously staged}
+ * backup-file of the runtime permissions.
+ *
+ * <p>This should be called every time after a package is installed until the callback
+ * reports that there is no more unapplied backup left.
*
* @param packageName The package that is ready to have it's permissions restored.
- * @param user The user to restore
+ * @param user The user the package belongs to
* @param executor Executor to execute the callback on
- * @param callback Is called with {@code true} iff there is still more delayed backup left
- *
- * @hide
+ * @param callback Is called with {@code true} iff there is still more unapplied backup left
*/
- @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
- public void restoreDelayedRuntimePermissionBackup(@NonNull String packageName,
+ @RequiresPermission(anyOf = {
+ Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+ Manifest.permission.RESTORE_RUNTIME_PERMISSIONS
+ })
+ public void applyStagedRuntimePermissionBackup(@NonNull String packageName,
@NonNull UserHandle user,
@NonNull @CallbackExecutor Executor executor,
@NonNull Consumer<Boolean> callback) {
@@ -426,13 +447,17 @@ public final class PermissionControllerManager {
checkNotNull(executor);
checkNotNull(callback);
+ // Check required permission to fail immediately instead of inside the oneway binder call
+ enforceSomePermissionsGrantedToSelf(Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+ Manifest.permission.RESTORE_RUNTIME_PERMISSIONS);
+
mRemoteService.postAsync(service -> {
- AndroidFuture<Boolean> restoreDelayedRuntimePermissionBackupResult =
+ AndroidFuture<Boolean> applyStagedRuntimePermissionBackupResult =
new AndroidFuture<>();
- service.restoreDelayedRuntimePermissionBackup(packageName, user,
- restoreDelayedRuntimePermissionBackupResult);
- return restoreDelayedRuntimePermissionBackupResult;
- }).whenCompleteAsync((restoreDelayedRuntimePermissionBackupResult, err) -> {
+ service.applyStagedRuntimePermissionBackup(packageName, user,
+ applyStagedRuntimePermissionBackupResult);
+ return applyStagedRuntimePermissionBackupResult;
+ }).whenCompleteAsync((applyStagedRuntimePermissionBackupResult, err) -> {
long token = Binder.clearCallingIdentity();
try {
if (err != null) {
@@ -440,7 +465,7 @@ public final class PermissionControllerManager {
callback.accept(true);
} else {
callback.accept(
- Boolean.TRUE.equals(restoreDelayedRuntimePermissionBackupResult));
+ Boolean.TRUE.equals(applyStagedRuntimePermissionBackupResult));
}
} finally {
Binder.restoreCallingIdentity(token);
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index 7363d7783903..8f765faa1a0b 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -54,6 +54,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
@@ -105,31 +106,54 @@ public abstract class PermissionControllerService extends Service {
public abstract void onGetRuntimePermissionsBackup(@NonNull UserHandle user,
@NonNull OutputStream backup, @NonNull Runnable callback);
+
+ /**
+ * @deprecated Implement {@link #onStageAndApplyRuntimePermissionsBackup} instead
+ */
+ @Deprecated
+ @BinderThread
+ public void onRestoreRuntimePermissionsBackup(@NonNull UserHandle user,
+ @NonNull InputStream backup, @NonNull Runnable callback) {
+ }
+
/**
* Restore a backup of the runtime permissions.
*
* <p>If an app mentioned in the backup is not installed the state should be saved to later
- * be restored via {@link #onRestoreDelayedRuntimePermissionsBackup}.
+ * be restored via {@link #onApplyStagedRuntimePermissionBackup}.
*
* @param user The user to restore
* @param backup The stream to read the backup from
* @param callback Callback waiting for operation to be complete
*/
@BinderThread
- public abstract void onRestoreRuntimePermissionsBackup(@NonNull UserHandle user,
- @NonNull InputStream backup, @NonNull Runnable callback);
+ public void onStageAndApplyRuntimePermissionsBackup(@NonNull UserHandle user,
+ @NonNull InputStream backup, @NonNull Runnable callback) {
+ onRestoreRuntimePermissionsBackup(user, backup, callback);
+ }
+
+ /**
+ * @deprecated Implement {@link #onApplyStagedRuntimePermissionBackup} instead
+ */
+ @Deprecated
+ @BinderThread
+ public void onRestoreDelayedRuntimePermissionsBackup(@NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Consumer<Boolean> callback) {
+ }
/**
* Restore the permission state of an app that was provided in
- * {@link #onRestoreRuntimePermissionsBackup} but could not be restored back then.
+ * {@link #onStageAndApplyRuntimePermissionsBackup} but could not be restored back then.
*
* @param packageName The app to restore
* @param user The user to restore
* @param callback Callback waiting for whether there is still delayed backup left
*/
@BinderThread
- public abstract void onRestoreDelayedRuntimePermissionsBackup(@NonNull String packageName,
- @NonNull UserHandle user, @NonNull Consumer<Boolean> callback);
+ public void onApplyStagedRuntimePermissionBackup(@NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Consumer<Boolean> callback) {
+ onRestoreDelayedRuntimePermissionsBackup(packageName, user, callback);
+ }
/**
* Gets the runtime permissions for an app.
@@ -238,7 +262,8 @@ public abstract class PermissionControllerService extends Service {
request.put(packageName, permissions);
}
- enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, null);
+ enforceSomePermissionsGrantedToCaller(
+ Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
// Verify callerPackageName
try {
@@ -258,12 +283,33 @@ public abstract class PermissionControllerService extends Service {
});
}
+ /**
+ * Throw a {@link SecurityException} if not at least one of the permissions is granted.
+ *
+ * @param requiredPermissions A list of permissions. Any of of them if sufficient to
+ * pass the check
+ */
+ private void enforceSomePermissionsGrantedToCaller(
+ @NonNull String... requiredPermissions) {
+ for (String requiredPermission : requiredPermissions) {
+ if (checkCallingPermission(requiredPermission)
+ == PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ }
+
+ throw new SecurityException(
+ "At lest one of the following permissions is required: " + Arrays.toString(
+ requiredPermissions));
+ }
+
+
@Override
public void getRuntimePermissionBackup(UserHandle user, ParcelFileDescriptor pipe) {
checkNotNull(user);
checkNotNull(pipe);
- enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);
+ enforceSomePermissionsGrantedToCaller(Manifest.permission.GET_RUNTIME_PERMISSIONS);
try (OutputStream backup = new ParcelFileDescriptor.AutoCloseOutputStream(pipe)) {
CountDownLatch latch = new CountDownLatch(1);
@@ -277,15 +323,17 @@ public abstract class PermissionControllerService extends Service {
}
@Override
- public void restoreRuntimePermissionBackup(UserHandle user, ParcelFileDescriptor pipe) {
+ public void stageAndApplyRuntimePermissionsBackup(UserHandle user,
+ ParcelFileDescriptor pipe) {
checkNotNull(user);
checkNotNull(pipe);
- enforceCallingPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS, null);
+ enforceSomePermissionsGrantedToCaller(Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+ Manifest.permission.RESTORE_RUNTIME_PERMISSIONS);
try (InputStream backup = new ParcelFileDescriptor.AutoCloseInputStream(pipe)) {
CountDownLatch latch = new CountDownLatch(1);
- onRestoreRuntimePermissionsBackup(user, backup, latch::countDown);
+ onStageAndApplyRuntimePermissionsBackup(user, backup, latch::countDown);
latch.await();
} catch (IOException e) {
Log.e(LOG_TAG, "Could not open pipe to read backup from", e);
@@ -295,15 +343,16 @@ public abstract class PermissionControllerService extends Service {
}
@Override
- public void restoreDelayedRuntimePermissionBackup(String packageName, UserHandle user,
+ public void applyStagedRuntimePermissionBackup(String packageName, UserHandle user,
AndroidFuture callback) {
checkNotNull(packageName);
checkNotNull(user);
checkNotNull(callback);
- enforceCallingPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS, null);
+ enforceSomePermissionsGrantedToCaller(Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+ Manifest.permission.RESTORE_RUNTIME_PERMISSIONS);
- onRestoreDelayedRuntimePermissionsBackup(packageName, user, callback::complete);
+ onApplyStagedRuntimePermissionBackup(packageName, user, callback::complete);
}
@Override
@@ -311,7 +360,7 @@ public abstract class PermissionControllerService extends Service {
checkNotNull(packageName, "packageName");
checkNotNull(callback, "callback");
- enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);
+ enforceSomePermissionsGrantedToCaller(Manifest.permission.GET_RUNTIME_PERMISSIONS);
onGetAppPermissions(packageName, callback::complete);
}
@@ -321,7 +370,8 @@ public abstract class PermissionControllerService extends Service {
checkNotNull(packageName, "packageName");
checkNotNull(permissionName, "permissionName");
- enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, null);
+ enforceSomePermissionsGrantedToCaller(
+ Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
CountDownLatch latch = new CountDownLatch(1);
PermissionControllerService.this.onRevokeRuntimePermission(packageName,
@@ -340,7 +390,7 @@ public abstract class PermissionControllerService extends Service {
checkFlagsArgument(flags, COUNT_WHEN_SYSTEM | COUNT_ONLY_WHEN_GRANTED);
checkNotNull(callback, "callback");
- enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);
+ enforceSomePermissionsGrantedToCaller(Manifest.permission.GET_RUNTIME_PERMISSIONS);
onCountPermissionApps(permissionNames, flags, callback::complete);
}
@@ -351,7 +401,7 @@ public abstract class PermissionControllerService extends Service {
checkArgumentNonnegative(numMillis);
checkNotNull(callback, "callback");
- enforceCallingPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS, null);
+ enforceSomePermissionsGrantedToCaller(Manifest.permission.GET_RUNTIME_PERMISSIONS);
onGetPermissionUsages(countSystem, numMillis, callback::complete);
}
@@ -369,15 +419,17 @@ public abstract class PermissionControllerService extends Service {
checkNotNull(callback);
if (grantState == PERMISSION_GRANT_STATE_DENIED) {
- enforceCallingPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS, null);
+ enforceSomePermissionsGrantedToCaller(
+ Manifest.permission.GRANT_RUNTIME_PERMISSIONS);
}
if (grantState == PERMISSION_GRANT_STATE_DENIED) {
- enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, null);
+ enforceSomePermissionsGrantedToCaller(
+ Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
}
- enforceCallingPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
- null);
+ enforceSomePermissionsGrantedToCaller(
+ Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY);
onSetRuntimePermissionGrantStateByDeviceAdmin(callerPackageName,
packageName, permission, grantState, callback::complete);
@@ -387,8 +439,8 @@ public abstract class PermissionControllerService extends Service {
public void grantOrUpgradeDefaultRuntimePermissions(@NonNull AndroidFuture callback) {
checkNotNull(callback, "callback");
- enforceCallingPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
- null);
+ enforceSomePermissionsGrantedToCaller(
+ Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY);
onGrantOrUpgradeDefaultRuntimePermissions(() -> callback.complete(true));
}
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 2b3a2ab05e82..4dd9baba3e5f 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -151,7 +151,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
.PRIORITY_CATEGORY_ALARMS) != 0;
mAllowMedia = (mNotificationPolicy.priorityCategories & NotificationManager.Policy
.PRIORITY_CATEGORY_MEDIA) != 0;
- mAllowRinger = !ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(
+ mAllowRinger = !ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(
mNotificationPolicy);
mStreamType = streamType;
mAffectedByRingerMode = mAudioManager.isStreamAffectedByRingerMode(mStreamType);
@@ -571,7 +571,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
.PRIORITY_CATEGORY_ALARMS) != 0;
mAllowMedia = (mNotificationPolicy.priorityCategories
& NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA) != 0;
- mAllowRinger = !ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(
+ mAllowRinger = !ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(
mNotificationPolicy);
updateSlider();
}
diff --git a/core/java/android/print/TEST_MAPPING b/core/java/android/print/TEST_MAPPING
new file mode 100644
index 000000000000..4fa882265e53
--- /dev/null
+++ b/core/java/android/print/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsPrintTestCases",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/java/android/print/pdf/TEST_MAPPING b/core/java/android/print/pdf/TEST_MAPPING
new file mode 100644
index 000000000000..d763598f5ba0
--- /dev/null
+++ b/core/java/android/print/pdf/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsPdfTestCases"
+ }
+ ]
+}
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 0973a6412384..1643a2137de0 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -38,11 +38,11 @@ import android.provider.ContactsContract.DataUsageFeedback;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
+import android.telephony.CallerInfo;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import android.util.Log;
-import android.telephony.CallerInfo;
import com.android.internal.telephony.PhoneConstants;
import java.util.List;
@@ -234,6 +234,9 @@ public class CallLog {
/** Call was on RTT at some point */
public static final int FEATURES_RTT = 1 << 5;
+ /** Call was VoLTE */
+ public static final int FEATURES_VOLTE = 1 << 6;
+
/**
* The phone number as the user entered it.
* <P>Type: TEXT</P>
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 5e201e4f0e42..e456c8a42e19 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -257,12 +257,22 @@ public final class DeviceConfig {
/**
* Namespace for storage-related features.
*
+ * @deprecated Replace storage namespace with storage_native_boot.
* @hide
*/
+ @Deprecated
@SystemApi
public static final String NAMESPACE_STORAGE = "storage";
/**
+ * Namespace for storage-related features, including native and boot.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot";
+
+ /**
* Namespace for System UI related features.
*
* @hide
@@ -393,9 +403,37 @@ public final class DeviceConfig {
@TestApi
@RequiresPermission(READ_DEVICE_CONFIG)
public static String getProperty(@NonNull String namespace, @NonNull String name) {
+ // Fetch all properties for the namespace at once and cache them in the local process, so we
+ // incur the cost of the IPC less often. Lookups happen much more frequently than updates,
+ // and we want to optimize the former.
+ return getProperties(namespace, name).getString(name, null);
+ }
+
+ /**
+ * Look up the values of multiple properties for a particular namespace. The lookup is atomic,
+ * such that the values of these properties cannot change between the time when the first is
+ * fetched and the time when the last is fetched.
+ *
+ * TODO: reference setProperties when it is added.
+ *
+ * @param namespace The namespace containing the properties to look up.
+ * @param names The names of properties to look up, or empty to fetch all properties for the
+ * given namespace.
+ * @return {@link Properties} object containing the requested properties. This reflects the
+ * state of these properties at the time of the lookup, and is not updated to reflect any
+ * future changes. The keyset of this Properties object will contain only the intersection
+ * of properties already set and properties requested via the names parameter. Properties
+ * that are already set but were not requested will not be contained here. Properties that
+ * are not set, but were requested will not be contained here either.
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ @RequiresPermission(READ_DEVICE_CONFIG)
+ public static Properties getProperties(@NonNull String namespace, @NonNull String ... names) {
ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
- String compositeName = createCompositeName(namespace, name);
- return Settings.Config.getString(contentResolver, compositeName);
+ return new Properties(namespace,
+ Settings.Config.getStrings(contentResolver, namespace, Arrays.asList(names)));
}
/**
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index fd81178d2cfb..eb09930005b5 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -363,15 +363,22 @@ public final class DocumentsContract {
* <p>
* Type: INTEGER (int)
*
- * @see #FLAG_SUPPORTS_WRITE
- * @see #FLAG_SUPPORTS_DELETE
- * @see #FLAG_SUPPORTS_THUMBNAIL
+ * @see #FLAG_DIR_BLOCKS_TREE
* @see #FLAG_DIR_PREFERS_GRID
* @see #FLAG_DIR_PREFERS_LAST_MODIFIED
- * @see #FLAG_VIRTUAL_DOCUMENT
+ * @see #FLAG_DIR_SUPPORTS_CREATE
+ * @see #FLAG_PARTIAL
* @see #FLAG_SUPPORTS_COPY
+ * @see #FLAG_SUPPORTS_DELETE
+ * @see #FLAG_SUPPORTS_METADATA
* @see #FLAG_SUPPORTS_MOVE
* @see #FLAG_SUPPORTS_REMOVE
+ * @see #FLAG_SUPPORTS_RENAME
+ * @see #FLAG_SUPPORTS_SETTINGS
+ * @see #FLAG_SUPPORTS_THUMBNAIL
+ * @see #FLAG_SUPPORTS_WRITE
+ * @see #FLAG_VIRTUAL_DOCUMENT
+ * @see #FLAG_WEB_LINKABLE
*/
public static final String COLUMN_FLAGS = "flags";
@@ -542,6 +549,23 @@ public final class DocumentsContract {
* @see DocumentsContract#getDocumentMetadata(ContentInterface, Uri)
*/
public static final int FLAG_SUPPORTS_METADATA = 1 << 14;
+
+ /**
+ * Flag indicating that a document is a directory that wants to block itself
+ * from being selected when the user launches an {@link Intent#ACTION_OPEN_DOCUMENT_TREE}
+ * intent. Only valid when {@link #COLUMN_MIME_TYPE} is {@link #MIME_TYPE_DIR}.
+ * <p>
+ * Note that this flag <em>only</em> applies to the single directory to which it is
+ * applied. It does <em>not</em> block the user from selecting either a parent or
+ * child directory during an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} request.
+ * In particular, the only way to guarantee that a specific directory can never
+ * be granted via an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} request is to ensure
+ * that both it and <em>all of its parent directories</em> have set this flag.
+ *
+ * @see Intent#ACTION_OPEN_DOCUMENT_TREE
+ * @see #COLUMN_FLAGS
+ */
+ public static final int FLAG_DIR_BLOCKS_TREE = 1 << 15;
}
/**
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 079a42ddaa6c..a1333df13820 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -39,7 +39,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.UriPermission;
import android.database.Cursor;
-import android.database.DatabaseUtils;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageDecoder;
@@ -47,6 +46,7 @@ import android.graphics.Point;
import android.graphics.PostProcessor;
import android.media.ExifInterface;
import android.media.MediaFile;
+import android.media.MediaFormat;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -69,15 +69,19 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import libcore.util.HexEncoding;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.text.Collator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Pattern;
@@ -2058,7 +2062,17 @@ public final class MediaStore {
/**
* A non human readable key calculated from the TITLE, used for
* searching, sorting and grouping
+ *
+ * @see Audio#keyFor(String)
+ * @deprecated These keys are generated using
+ * {@link java.util.Locale#ROOT}, which means they don't
+ * reflect locale-specific sorting preferences. To apply
+ * locale-specific sorting preferences, use
+ * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+ * {@code COLLATE LOCALIZED}, or
+ * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
*/
+ @Deprecated
@Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
public static final String TITLE_KEY = "title_key";
@@ -2103,7 +2117,17 @@ public final class MediaStore {
/**
* A non human readable key calculated from the ARTIST, used for
* searching, sorting and grouping
+ *
+ * @see Audio#keyFor(String)
+ * @deprecated These keys are generated using
+ * {@link java.util.Locale#ROOT}, which means they don't
+ * reflect locale-specific sorting preferences. To apply
+ * locale-specific sorting preferences, use
+ * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+ * {@code COLLATE LOCALIZED}, or
+ * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
*/
+ @Deprecated
@Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
public static final String ARTIST_KEY = "artist_key";
@@ -2128,7 +2152,17 @@ public final class MediaStore {
/**
* A non human readable key calculated from the ALBUM, used for
* searching, sorting and grouping
+ *
+ * @see Audio#keyFor(String)
+ * @deprecated These keys are generated using
+ * {@link java.util.Locale#ROOT}, which means they don't
+ * reflect locale-specific sorting preferences. To apply
+ * locale-specific sorting preferences, use
+ * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+ * {@code COLLATE LOCALIZED}, or
+ * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
*/
+ @Deprecated
@Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
public static final String ALBUM_KEY = "album_key";
@@ -2185,91 +2219,89 @@ public final class MediaStore {
public static final String IS_AUDIOBOOK = "is_audiobook";
/**
- * The genre of the audio file, if any
- * Does not exist in the database - only used by the media scanner for inserts.
- * @hide
+ * The id of the genre the audio file is from, if any
*/
- @Deprecated
- // @Column(Cursor.FIELD_TYPE_STRING)
+ @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
+ public static final String GENRE_ID = "genre_id";
+
+ /**
+ * The genre of the audio file, if any.
+ */
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
public static final String GENRE = "genre";
/**
- * The resource URI of a localized title, if any
+ * A non human readable key calculated from the GENRE, used for
+ * searching, sorting and grouping
+ *
+ * @see Audio#keyFor(String)
+ * @deprecated These keys are generated using
+ * {@link java.util.Locale#ROOT}, which means they don't
+ * reflect locale-specific sorting preferences. To apply
+ * locale-specific sorting preferences, use
+ * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+ * {@code COLLATE LOCALIZED}, or
+ * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
+ */
+ @Deprecated
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ public static final String GENRE_KEY = "genre_key";
+
+ /**
+ * The resource URI of a localized title, if any.
+ * <p>
* Conforms to this pattern:
- * Scheme: {@link ContentResolver.SCHEME_ANDROID_RESOURCE}
- * Authority: Package Name of ringtone title provider
- * First Path Segment: Type of resource (must be "string")
- * Second Path Segment: Resource ID of title
- * @hide
+ * <ul>
+ * <li>Scheme: {@link ContentResolver#SCHEME_ANDROID_RESOURCE}
+ * <li>Authority: Package Name of ringtone title provider
+ * <li>First Path Segment: Type of resource (must be "string")
+ * <li>Second Path Segment: Resource ID of title
+ * </ul>
*/
@Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
public static final String TITLE_RESOURCE_URI = "title_resource_uri";
}
+ private static final Pattern PATTERN_TRIM_BEFORE = Pattern.compile(
+ "(?i)(^(the|an|a) |,\\s*(the|an|a)$|[^\\w\\s]|^\\s+|\\s+$)");
+ private static final Pattern PATTERN_TRIM_AFTER = Pattern.compile(
+ "(^(00)+|(00)+$)");
+
/**
- * Converts a name to a "key" that can be used for grouping, sorting
- * and searching.
- * The rules that govern this conversion are:
- * - remove 'special' characters like ()[]'!?.,
- * - remove leading/trailing spaces
- * - convert everything to lowercase
- * - remove leading "the ", "an " and "a "
- * - remove trailing ", the|an|a"
- * - remove accents. This step leaves us with CollationKey data,
- * which is not human readable
+ * Converts a user-visible string into a "key" that can be used for
+ * grouping, sorting, and searching.
*
- * @param name The artist or album name to convert
- * @return The "key" for the given name.
- */
- public static String keyFor(String name) {
- if (name != null) {
- boolean sortfirst = false;
- if (name.equals(UNKNOWN_STRING)) {
- return "\001";
- }
- // Check if the first character is \001. We use this to
- // force sorting of certain special files, like the silent ringtone.
- if (name.startsWith("\001")) {
- sortfirst = true;
- }
- name = name.trim().toLowerCase();
- if (name.startsWith("the ")) {
- name = name.substring(4);
- }
- if (name.startsWith("an ")) {
- name = name.substring(3);
- }
- if (name.startsWith("a ")) {
- name = name.substring(2);
- }
- if (name.endsWith(", the") || name.endsWith(",the") ||
- name.endsWith(", an") || name.endsWith(",an") ||
- name.endsWith(", a") || name.endsWith(",a")) {
- name = name.substring(0, name.lastIndexOf(','));
- }
- name = name.replaceAll("[\\[\\]\\(\\)\"'.,?!]", "").trim();
- if (name.length() > 0) {
- // Insert a separator between the characters to avoid
- // matches on a partial character. If we ever change
- // to start-of-word-only matches, this can be removed.
- StringBuilder b = new StringBuilder();
- b.append('.');
- int nl = name.length();
- for (int i = 0; i < nl; i++) {
- b.append(name.charAt(i));
- b.append('.');
- }
- name = b.toString();
- String key = DatabaseUtils.getCollationKey(name);
- if (sortfirst) {
- key = "\001" + key;
- }
- return key;
- } else {
- return "";
- }
+ * @return Opaque token that should not be parsed or displayed to users.
+ * @deprecated These keys are generated using
+ * {@link java.util.Locale#ROOT}, which means they don't
+ * reflect locale-specific sorting preferences. To apply
+ * locale-specific sorting preferences, use
+ * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+ * {@code COLLATE LOCALIZED}, or
+ * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
+ */
+ @Deprecated
+ public static @Nullable String keyFor(@Nullable String name) {
+ if (TextUtils.isEmpty(name)) return null;
+
+ if (UNKNOWN_STRING.equals(name)) {
+ return "01";
}
- return null;
+
+ final boolean sortFirst = name.startsWith("\001");
+
+ name = PATTERN_TRIM_BEFORE.matcher(name).replaceAll("");
+ if (TextUtils.isEmpty(name)) return null;
+
+ final Collator c = Collator.getInstance(Locale.ROOT);
+ c.setStrength(Collator.PRIMARY);
+ name = HexEncoding.encodeToString(c.getCollationKey(name).toByteArray(), false);
+
+ name = PATTERN_TRIM_AFTER.matcher(name).replaceAll("");
+ if (sortFirst) {
+ name = "01" + name;
+ }
+ return name;
}
public static final class Media implements AudioColumns {
@@ -2631,7 +2663,17 @@ public final class MediaStore {
/**
* A non human readable key calculated from the ARTIST, used for
* searching, sorting and grouping
+ *
+ * @see Audio#keyFor(String)
+ * @deprecated These keys are generated using
+ * {@link java.util.Locale#ROOT}, which means they don't
+ * reflect locale-specific sorting preferences. To apply
+ * locale-specific sorting preferences, use
+ * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+ * {@code COLLATE LOCALIZED}, or
+ * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
*/
+ @Deprecated
@Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
public static final String ARTIST_KEY = "artist_key";
@@ -2735,6 +2777,23 @@ public final class MediaStore {
public static final String ARTIST = "artist";
/**
+ * A non human readable key calculated from the ARTIST, used for
+ * searching, sorting and grouping
+ *
+ * @see Audio#keyFor(String)
+ * @deprecated These keys are generated using
+ * {@link java.util.Locale#ROOT}, which means they don't
+ * reflect locale-specific sorting preferences. To apply
+ * locale-specific sorting preferences, use
+ * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+ * {@code COLLATE LOCALIZED}, or
+ * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
+ */
+ @Deprecated
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ public static final String ARTIST_KEY = "artist_key";
+
+ /**
* The number of songs on this album
*/
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
@@ -2769,7 +2828,17 @@ public final class MediaStore {
/**
* A non human readable key calculated from the ALBUM, used for
* searching, sorting and grouping
+ *
+ * @see Audio#keyFor(String)
+ * @deprecated These keys are generated using
+ * {@link java.util.Locale#ROOT}, which means they don't
+ * reflect locale-specific sorting preferences. To apply
+ * locale-specific sorting preferences, use
+ * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+ * {@code COLLATE LOCALIZED}, or
+ * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
*/
+ @Deprecated
@Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
public static final String ALBUM_KEY = "album_key";
@@ -3007,22 +3076,32 @@ public final class MediaStore {
public static final String BOOKMARK = "bookmark";
/**
- * The standard of color aspects
- * @hide
+ * The color standard of this media file, if available.
+ *
+ * @see MediaFormat#COLOR_STANDARD_BT709
+ * @see MediaFormat#COLOR_STANDARD_BT601_PAL
+ * @see MediaFormat#COLOR_STANDARD_BT601_NTSC
+ * @see MediaFormat#COLOR_STANDARD_BT2020
*/
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
public static final String COLOR_STANDARD = "color_standard";
/**
- * The transfer of color aspects
- * @hide
+ * The color transfer of this media file, if available.
+ *
+ * @see MediaFormat#COLOR_TRANSFER_LINEAR
+ * @see MediaFormat#COLOR_TRANSFER_SDR_VIDEO
+ * @see MediaFormat#COLOR_TRANSFER_ST2084
+ * @see MediaFormat#COLOR_TRANSFER_HLG
*/
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
public static final String COLOR_TRANSFER = "color_transfer";
/**
- * The range of color aspects
- * @hide
+ * The color range of this media file, if available.
+ *
+ * @see MediaFormat#COLOR_RANGE_LIMITED
+ * @see MediaFormat#COLOR_RANGE_FULL
*/
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
public static final String COLOR_RANGE = "color_range";
diff --git a/core/java/android/provider/SearchIndexablesContract.java b/core/java/android/provider/SearchIndexablesContract.java
index 5f8266d2b579..298628e7578b 100644
--- a/core/java/android/provider/SearchIndexablesContract.java
+++ b/core/java/android/provider/SearchIndexablesContract.java
@@ -84,9 +84,9 @@ public class SearchIndexablesContract {
/**
* Last path segment for Preference Key, Slice Uri pair.
* <p>
- * The (Key, Slice Uri) pairs are a mapping between the primary key of the search result and
- * a Uri for a Slice that represents the same data. Thus, an app can specify a list of Uris
- * for Slices that replace regular intent-based search results with inline content.
+ * The (Key, Slice Uri) pairs are a mapping between the primary key of the search result and
+ * a Uri for a Slice that represents the same data. Thus, an app can specify a list of Uris
+ * for Slices that replace regular intent-based search results with inline content.
* </p>
*/
public static final String SLICE_URI_PAIRS = "slice_uri_pairs";
@@ -96,6 +96,22 @@ public class SearchIndexablesContract {
*/
public static final String SLICE_URI_PAIRS_PATH = SETTINGS + "/" + SLICE_URI_PAIRS;
+
+ /**
+ * Dynamic indexable raw data names.
+ *
+ * @hide
+ */
+ public static final String DYNAMIC_INDEXABLES_RAW = "dynamic_indexables_raw";
+
+ /**
+ * ContentProvider path for dynamic indexable raw data.
+ *
+ * @hide
+ */
+ public static final String DYNAMIC_INDEXABLES_RAW_PATH =
+ SETTINGS + "/" + DYNAMIC_INDEXABLES_RAW;
+
/**
* Indexable xml resources columns.
*/
@@ -212,7 +228,7 @@ public class SearchIndexablesContract {
* Cursor schema for SliceUriPairs.
*/
@NonNull
- public static final String[] SLICE_URI_PAIRS_COLUMNS = new String[]{
+ public static final String[] SLICE_URI_PAIRS_COLUMNS = new String[] {
SliceUriPairColumns.KEY,
SliceUriPairColumns.SLICE_URI
};
diff --git a/core/java/android/provider/SearchIndexablesProvider.java b/core/java/android/provider/SearchIndexablesProvider.java
index da29e2e5e39f..68284b4895c3 100644
--- a/core/java/android/provider/SearchIndexablesProvider.java
+++ b/core/java/android/provider/SearchIndexablesProvider.java
@@ -77,6 +77,7 @@ public abstract class SearchIndexablesProvider extends ContentProvider {
private static final int MATCH_NON_INDEXABLE_KEYS_CODE = 3;
private static final int MATCH_SITE_MAP_PAIRS_CODE = 4;
private static final int MATCH_SLICE_URI_PAIRS_CODE = 5;
+ private static final int MATCH_DYNAMIC_RAW_CODE = 6;
/**
* Implementation is provided by the parent class.
@@ -96,6 +97,8 @@ public abstract class SearchIndexablesProvider extends ContentProvider {
MATCH_SITE_MAP_PAIRS_CODE);
mMatcher.addURI(mAuthority, SearchIndexablesContract.SLICE_URI_PAIRS_PATH,
MATCH_SLICE_URI_PAIRS_CODE);
+ mMatcher.addURI(mAuthority, SearchIndexablesContract.DYNAMIC_INDEXABLES_RAW_PATH,
+ MATCH_DYNAMIC_RAW_CODE);
// Sanity check our setup
if (!info.exported) {
@@ -126,6 +129,8 @@ public abstract class SearchIndexablesProvider extends ContentProvider {
return querySiteMapPairs();
case MATCH_SLICE_URI_PAIRS_CODE:
return querySliceUriPairs();
+ case MATCH_DYNAMIC_RAW_CODE:
+ return queryDynamicRawData(null);
default:
throw new UnsupportedOperationException("Unknown Uri " + uri);
}
@@ -191,12 +196,30 @@ public abstract class SearchIndexablesProvider extends ContentProvider {
return null;
}
+ /**
+ * Returns all {@link android.provider.SearchIndexablesContract.RawData}.
+ *
+ * Those are the dynamic raw indexable data.
+ *
+ * @param projection list of {@link android.provider.SearchIndexablesContract.RawData} columns
+ * to put into the cursor. If {@code null} all supported columns should be
+ * included.
+ *
+ * @hide
+ */
+ @Nullable
+ public Cursor queryDynamicRawData(String[] projection) {
+ // By default no-op;
+ return null;
+ }
+
@Override
public String getType(Uri uri) {
switch (mMatcher.match(uri)) {
case MATCH_RES_CODE:
return SearchIndexablesContract.XmlResource.MIME_TYPE;
case MATCH_RAW_CODE:
+ case MATCH_DYNAMIC_RAW_CODE:
return SearchIndexablesContract.RawData.MIME_TYPE;
case MATCH_NON_INDEXABLE_KEYS_CODE:
return SearchIndexablesContract.NonIndexableKey.MIME_TYPE;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f985a9070976..00ac37877aaa 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -84,8 +84,10 @@ import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.URISyntaxException;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -2248,7 +2250,7 @@ public final class Settings {
private static final String NAME_EQ_PLACEHOLDER = "name=?";
// Must synchronize on 'this' to access mValues and mValuesVersion.
- private final HashMap<String, String> mValues = new HashMap<>();
+ private final ArrayMap<String, String> mValues = new ArrayMap<>();
private final Uri mUri;
@UnsupportedAppUsage
@@ -2258,15 +2260,22 @@ public final class Settings {
// for the fast path of retrieving settings.
private final String mCallGetCommand;
private final String mCallSetCommand;
+ private final String mCallListCommand;
@GuardedBy("this")
private GenerationTracker mGenerationTracker;
public NameValueCache(Uri uri, String getCommand, String setCommand,
ContentProviderHolder providerHolder) {
+ this(uri, getCommand, setCommand, null, providerHolder);
+ }
+
+ NameValueCache(Uri uri, String getCommand, String setCommand, String listCommand,
+ ContentProviderHolder providerHolder) {
mUri = uri;
mCallGetCommand = getCommand;
mCallSetCommand = setCommand;
+ mCallListCommand = listCommand;
mProviderHolder = providerHolder;
}
@@ -2448,8 +2457,8 @@ public final class Settings {
String value = c.moveToNext() ? c.getString(0) : null;
synchronized (NameValueCache.this) {
- if(mGenerationTracker != null &&
- currentGeneration == mGenerationTracker.getCurrentGeneration()) {
+ if (mGenerationTracker != null
+ && currentGeneration == mGenerationTracker.getCurrentGeneration()) {
mValues.put(name, value);
}
}
@@ -2466,6 +2475,141 @@ public final class Settings {
}
}
+ public ArrayMap<String, String> getStringsForPrefix(ContentResolver cr, String prefix,
+ List<String> names) {
+ ArrayMap<String, String> keyValues = new ArrayMap<>();
+ int currentGeneration = -1;
+
+ synchronized (NameValueCache.this) {
+ if (mGenerationTracker != null) {
+ if (mGenerationTracker.isGenerationChanged()) {
+ if (DEBUG) {
+ Log.i(TAG, "Generation changed for type:" + mUri.getPath()
+ + " in package:" + cr.getPackageName());
+ }
+ mValues.clear();
+ } else {
+ boolean prefixCached = false;
+ int size = mValues.size();
+ for (int i = 0; i < size; ++i) {
+ if (mValues.keyAt(i).startsWith(prefix + "/")) {
+ prefixCached = true;
+ break;
+ }
+ }
+ if (prefixCached) {
+ if (!names.isEmpty()) {
+ for (String name : names) {
+ if (mValues.containsKey(name)) {
+ keyValues.put(name, mValues.get(name));
+ }
+ }
+ } else {
+ for (int i = 0; i < size; ++i) {
+ String key = mValues.keyAt(i);
+ if (key.startsWith(prefix + "/")) {
+ keyValues.put(key, mValues.get(key));
+ }
+ }
+ }
+ return keyValues;
+ }
+ }
+ if (mGenerationTracker != null) {
+ currentGeneration = mGenerationTracker.getCurrentGeneration();
+ }
+ }
+ }
+
+ if (mCallListCommand == null) {
+ // No list command specified, return empty map
+ return keyValues;
+ }
+ IContentProvider cp = mProviderHolder.getProvider(cr);
+
+ try {
+ Bundle args = new Bundle();
+ args.putString(Settings.CALL_METHOD_PREFIX_KEY, prefix);
+ boolean needsGenerationTracker = false;
+ synchronized (NameValueCache.this) {
+ if (mGenerationTracker == null) {
+ needsGenerationTracker = true;
+ args.putString(CALL_METHOD_TRACK_GENERATION_KEY, null);
+ if (DEBUG) {
+ Log.i(TAG, "Requested generation tracker for type: "
+ + mUri.getPath() + " in package:" + cr.getPackageName());
+ }
+ }
+ }
+
+ // Fetch all flags for the namespace at once for caching purposes
+ Bundle b = cp.call(cr.getPackageName(), mProviderHolder.mUri.getAuthority(),
+ mCallListCommand, null, args);
+ if (b == null) {
+ // Invalid response, return an empty map
+ return keyValues;
+ }
+
+ // All flags for the namespace
+ Map<String, String> flagsToValues =
+ (HashMap) b.getSerializable(Settings.NameValueTable.VALUE);
+ // Only the flags requested by the caller
+ if (!names.isEmpty()) {
+ for (Map.Entry<String, String> flag : flagsToValues.entrySet()) {
+ if (names.contains(flag.getKey())) {
+ keyValues.put(flag.getKey(), flag.getValue());
+ }
+ }
+ } else {
+ keyValues.putAll(flagsToValues);
+ }
+
+ synchronized (NameValueCache.this) {
+ if (needsGenerationTracker) {
+ MemoryIntArray array = b.getParcelable(
+ CALL_METHOD_TRACK_GENERATION_KEY);
+ final int index = b.getInt(
+ CALL_METHOD_GENERATION_INDEX_KEY, -1);
+ if (array != null && index >= 0) {
+ final int generation = b.getInt(
+ CALL_METHOD_GENERATION_KEY, 0);
+ if (DEBUG) {
+ Log.i(TAG, "Received generation tracker for type:"
+ + mUri.getPath() + " in package:"
+ + cr.getPackageName() + " with index:" + index);
+ }
+ if (mGenerationTracker != null) {
+ mGenerationTracker.destroy();
+ }
+ mGenerationTracker = new GenerationTracker(array, index,
+ generation, () -> {
+ synchronized (NameValueCache.this) {
+ Log.e(TAG, "Error accessing generation tracker"
+ + " - removing");
+ if (mGenerationTracker != null) {
+ GenerationTracker generationTracker =
+ mGenerationTracker;
+ mGenerationTracker = null;
+ generationTracker.destroy();
+ mValues.clear();
+ }
+ }
+ });
+ }
+ }
+ if (mGenerationTracker != null && currentGeneration
+ == mGenerationTracker.getCurrentGeneration()) {
+ // cache the complete list of flags for the namespace
+ mValues.putAll(flagsToValues);
+ }
+ }
+ return keyValues;
+ } catch (RemoteException e) {
+ // Not supported by the remote side, return an empty map
+ return keyValues;
+ }
+ }
+
public void clearGenerationTrackerForTest() {
synchronized (NameValueCache.this) {
if (mGenerationTracker != null) {
@@ -7186,6 +7330,19 @@ public final class Settings {
public static final String SILENCE_CALL_TOUCH_COUNT = "silence_call_touch_count";
/**
+ * Number of successful "Motion Sense" tap gestures to pause media.
+ * @hide
+ */
+ public static final String AWARE_TAP_PAUSE_GESTURE_COUNT = "aware_tap_pause_gesture_count";
+
+ /**
+ * Number of touch interactions to pause media when a "Motion Sense" gesture could
+ * have been used.
+ * @hide
+ */
+ public static final String AWARE_TAP_PAUSE_TOUCH_COUNT = "aware_tap_pause_touch_count";
+
+ /**
* The current night mode that has been selected by the user. Owned
* and controlled by UiModeManagerService. Constants are as per
* UiModeManager.
@@ -7642,6 +7799,19 @@ public final class Settings {
"face_unlock_always_require_confirmation";
/**
+ * Whether or not a user should re enroll their face.
+ *
+ * Face unlock re enroll.
+ * 0 = No re enrollment.
+ * 1 = Re enrollment is suggested.
+ * 2 = Re enrollment is required after a set time period.
+ * 3 = Re enrollment is required immediately.
+ *
+ * @hide
+ */
+ public static final String FACE_UNLOCK_RE_ENROLL = "face_unlock_re_enroll";
+
+ /**
* Whether or not debugging is enabled.
* @hide
*/
@@ -7892,14 +8062,6 @@ public final class Settings {
public static final String DEVICE_PAIRED = "device_paired";
/**
- * Integer state indicating whether package verifier is enabled.
- * TODO(b/34259924): Remove this setting.
- *
- * @hide
- */
- public static final String PACKAGE_VERIFIER_STATE = "package_verifier_state";
-
- /**
* Specifies additional package name for broadcasting the CMAS messages.
* @hide
*/
@@ -7914,16 +8076,6 @@ public final class Settings {
public static final String NOTIFICATION_BADGING = "notification_badging";
/**
- * Whether the notification bubbles are globally enabled
- * The value is boolean (1 or 0).
- * @hide
- * @deprecated use {@link Global#NOTIFICATION_BUBBLES} instead.
- */
- @TestApi
- @Deprecated
- public static final String NOTIFICATION_BUBBLES = "notification_bubbles";
-
- /**
* Whether notifications are dismissed by a right-to-left swipe (instead of a left-to-right
* swipe).
*
@@ -8113,20 +8265,6 @@ public final class Settings {
public static final String AWARE_LOCK_ENABLED = "aware_lock_enabled";
/**
- * The settings values which should only be restored if the target device is the
- * same as the source device
- *
- * NOTE: Settings are backed up and restored in the order they appear
- * in this array. If you have one setting depending on another,
- * make sure that they are ordered appropriately.
- *
- * @hide
- */
- public static final String[] DEVICE_SPECIFIC_SETTINGS_TO_BACKUP = {
- DISPLAY_DENSITY_FORCED,
- };
-
- /**
* Keys we no longer back up under the current schema, but want to continue to
* process when restoring historical backup datasets.
*
@@ -8467,13 +8605,19 @@ public final class Settings {
public static final String POWER_SOUNDS_ENABLED = "power_sounds_enabled";
/**
- * URI for the "wireless charging started" and "wired charging started" sound.
+ * URI for the "wireless charging started" sound.
* @hide
*/
- public static final String CHARGING_STARTED_SOUND =
+ public static final String WIRELESS_CHARGING_STARTED_SOUND =
"wireless_charging_started_sound";
/**
+ * URI for "wired charging started" sound.
+ * @hide
+ */
+ public static final String CHARGING_STARTED_SOUND = "charging_started_sound";
+
+ /**
* Whether to play a sound for charging events.
* @deprecated Use {@link android.provider.Settings.Secure#CHARGING_SOUNDS_ENABLED} instead
* @hide
@@ -10759,16 +10903,13 @@ public final class Settings {
* App standby (app idle) specific settings.
* This is encoded as a key=value list, separated by commas. Ex:
* <p>
- * "idle_duration=5000,parole_interval=4500,screen_thresholds=0/0/60000/120000"
+ * "idle_duration=5000,prediction_timeout=4500,screen_thresholds=0/0/60000/120000"
* <p>
* All durations are in millis.
* Array values are separated by forward slashes
* The following keys are supported:
*
* <pre>
- * parole_interval (long)
- * parole_window (long)
- * parole_duration (long)
* screen_thresholds (long[4])
* elapsed_thresholds (long[4])
* strong_usage_duration (long)
@@ -10779,17 +10920,12 @@ public final class Settings {
* exempted_sync_duration (long)
* system_interaction_duration (long)
* initial_foreground_service_start_duration (long)
- * stable_charging_threshold (long)
- *
- * idle_duration (long) // This is deprecated and used to circumvent b/26355386.
- * idle_duration2 (long) // deprecated
- * wallclock_threshold (long) // deprecated
* </pre>
*
* <p>
* Type: string
* @hide
- * @see com.android.server.usage.UsageStatsService.SettingsObserver
+ * @see com.android.server.usage.AppStandbyController
*/
public static final String APP_IDLE_CONSTANTS = "app_idle_constants";
@@ -13525,6 +13661,7 @@ public final class Settings {
DeviceConfig.CONTENT_URI,
CALL_METHOD_GET_CONFIG,
CALL_METHOD_PUT_CONFIG,
+ CALL_METHOD_LIST_CONFIG,
sProviderHolder);
/**
@@ -13541,6 +13678,37 @@ public final class Settings {
}
/**
+ * Look up a list of names in the database, based on a common prefix.
+ *
+ * @param resolver to access the database with
+ * @param prefix to apply to all of the names which will be fetched
+ * @param names to look up in the table
+ * @return a non null, but possibly empty, map from name to value for any of the names that
+ * were found during lookup.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
+ static Map<String, String> getStrings(@NonNull ContentResolver resolver,
+ @NonNull String prefix, @NonNull List<String> names) {
+ List<String> concatenatedNames = new ArrayList<>(names.size());
+ for (String name : names) {
+ concatenatedNames.add(prefix + "/" + name);
+ }
+
+ ArrayMap<String, String> rawKeyValues = sNameValueCache.getStringsForPrefix(
+ resolver, prefix, concatenatedNames);
+ int size = rawKeyValues.size();
+ int substringLength = prefix.length() + 1;
+ ArrayMap<String, String> keyValues = new ArrayMap<>(size);
+ for (int i = 0; i < size; ++i) {
+ keyValues.put(rawKeyValues.keyAt(i).substring(substringLength),
+ rawKeyValues.valueAt(i));
+ }
+ return keyValues;
+ }
+
+ /**
* Store a name/value pair into the database.
* <p>
* Also the method takes an argument whether to make the value the default for this setting.
diff --git a/core/java/android/service/autofill/AutofillFieldClassificationService.java b/core/java/android/service/autofill/AutofillFieldClassificationService.java
index 320dcec675cc..28842a7fa1d7 100644
--- a/core/java/android/service/autofill/AutofillFieldClassificationService.java
+++ b/core/java/android/service/autofill/AutofillFieldClassificationService.java
@@ -93,6 +93,13 @@ public abstract class AutofillFieldClassificationService extends Service {
*/
public static final String REQUIRED_ALGORITHM_EXACT_MATCH = "EXACT_MATCH";
+ /**
+ * Field classification algorithm that compares a credit card string to known last four digits.
+ *
+ * <p>Service implementation must provide this algorithm.</p>
+ */
+ public static final String REQUIRED_ALGORITHM_CREDIT_CARD = "CREDIT_CARD";
+
/** {@hide} **/
public static final String EXTRA_SCORES = "scores";
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 94b9d050a44d..48ba4295f3c8 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -181,6 +181,23 @@ public final class SaveInfo implements Parcelable {
public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 0x10;
/**
+ * Type used when the {@link FillResponse} represents a debit card.
+ */
+ public static final int SAVE_DATA_TYPE_DEBIT_CARD = 0x20;
+
+ /**
+ * Type used when the {@link FillResponse} represents a payment card except for credit and
+ * debit cards.
+ */
+ public static final int SAVE_DATA_TYPE_PAYMENT_CARD = 0x40;
+
+ /**
+ * Type used when the {@link FillResponse} represents a card that does not a specified card or
+ * cannot identify what the card is for.
+ */
+ public static final int SAVE_DATA_TYPE_GENERIC_CARD = 0x80;
+
+ /**
* Style for the negative button of the save UI to cancel the
* save operation. In this case, the user tapping the negative
* button signals that they would prefer to not save the filled
@@ -207,6 +224,30 @@ public final class SaveInfo implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
@interface NegativeButtonStyle{}
+ /**
+ * Style for the positive button of save UI to request the save operation.
+ * In this case, the user tapping the positive button signals that they
+ * agrees to save the filled content.
+ */
+ public static final int POSITIVE_BUTTON_STYLE_SAVE = 0;
+
+ /**
+ * Style for the positive button of save UI to have next action before the save operation.
+ * This could be useful if the filled content contains sensitive personally identifiable
+ * information and then requires user confirmation or verification. In this case, the user
+ * tapping the positive button signals that they would complete the next required action
+ * to save the filled content.
+ */
+ public static final int POSITIVE_BUTTON_STYLE_CONTINUE = 1;
+
+ /** @hide */
+ @IntDef(prefix = { "POSITIVE_BUTTON_STYLE_" }, value = {
+ POSITIVE_BUTTON_STYLE_SAVE,
+ POSITIVE_BUTTON_STYLE_CONTINUE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface PositiveButtonStyle{}
+
/** @hide */
@IntDef(flag = true, prefix = { "SAVE_DATA_TYPE_" }, value = {
SAVE_DATA_TYPE_GENERIC,
@@ -214,7 +255,10 @@ public final class SaveInfo implements Parcelable {
SAVE_DATA_TYPE_ADDRESS,
SAVE_DATA_TYPE_CREDIT_CARD,
SAVE_DATA_TYPE_USERNAME,
- SAVE_DATA_TYPE_EMAIL_ADDRESS
+ SAVE_DATA_TYPE_EMAIL_ADDRESS,
+ SAVE_DATA_TYPE_DEBIT_CARD,
+ SAVE_DATA_TYPE_PAYMENT_CARD,
+ SAVE_DATA_TYPE_GENERIC_CARD
})
@Retention(RetentionPolicy.SOURCE)
@interface SaveDataType{}
@@ -266,6 +310,7 @@ public final class SaveInfo implements Parcelable {
private final @SaveDataType int mType;
private final @NegativeButtonStyle int mNegativeButtonStyle;
+ private final @PositiveButtonStyle int mPositiveButtonStyle;
private final IntentSender mNegativeActionListener;
private final AutofillId[] mRequiredIds;
private final AutofillId[] mOptionalIds;
@@ -281,6 +326,7 @@ public final class SaveInfo implements Parcelable {
mType = builder.mType;
mNegativeButtonStyle = builder.mNegativeButtonStyle;
mNegativeActionListener = builder.mNegativeActionListener;
+ mPositiveButtonStyle = builder.mPositiveButtonStyle;
mRequiredIds = builder.mRequiredIds;
mOptionalIds = builder.mOptionalIds;
mDescription = builder.mDescription;
@@ -313,6 +359,11 @@ public final class SaveInfo implements Parcelable {
}
/** @hide */
+ public @PositiveButtonStyle int getPositiveActionStyle() {
+ return mPositiveButtonStyle;
+ }
+
+ /** @hide */
public @Nullable AutofillId[] getRequiredIds() {
return mRequiredIds;
}
@@ -374,6 +425,7 @@ public final class SaveInfo implements Parcelable {
private final @SaveDataType int mType;
private @NegativeButtonStyle int mNegativeButtonStyle = NEGATIVE_BUTTON_STYLE_CANCEL;
+ private @PositiveButtonStyle int mPositiveButtonStyle = POSITIVE_BUTTON_STYLE_SAVE;
private IntentSender mNegativeActionListener;
private final AutofillId[] mRequiredIds;
private AutofillId[] mOptionalIds;
@@ -394,8 +446,9 @@ public final class SaveInfo implements Parcelable {
* can be any combination of {@link SaveInfo#SAVE_DATA_TYPE_GENERIC},
* {@link SaveInfo#SAVE_DATA_TYPE_PASSWORD},
* {@link SaveInfo#SAVE_DATA_TYPE_ADDRESS}, {@link SaveInfo#SAVE_DATA_TYPE_CREDIT_CARD},
- * {@link SaveInfo#SAVE_DATA_TYPE_USERNAME}, or
- * {@link SaveInfo#SAVE_DATA_TYPE_EMAIL_ADDRESS}.
+ * {@link SaveInfo#SAVE_DATA_TYPE_DEBIT_CARD}, {@link SaveInfo#SAVE_DATA_TYPE_PAYMENT_CARD},
+ * {@link SaveInfo#SAVE_DATA_TYPE_GENERIC_CARD}, {@link SaveInfo#SAVE_DATA_TYPE_USERNAME},
+ * or {@link SaveInfo#SAVE_DATA_TYPE_EMAIL_ADDRESS}.
* @param requiredIds ids of all required views that will trigger a save request.
*
* <p>See {@link SaveInfo} for more info.
@@ -418,8 +471,9 @@ public final class SaveInfo implements Parcelable {
* can be any combination of {@link SaveInfo#SAVE_DATA_TYPE_GENERIC},
* {@link SaveInfo#SAVE_DATA_TYPE_PASSWORD},
* {@link SaveInfo#SAVE_DATA_TYPE_ADDRESS}, {@link SaveInfo#SAVE_DATA_TYPE_CREDIT_CARD},
- * {@link SaveInfo#SAVE_DATA_TYPE_USERNAME}, or
- * {@link SaveInfo#SAVE_DATA_TYPE_EMAIL_ADDRESS}.
+ * {@link SaveInfo#SAVE_DATA_TYPE_DEBIT_CARD}, {@link SaveInfo#SAVE_DATA_TYPE_PAYMENT_CARD},
+ * {@link SaveInfo#SAVE_DATA_TYPE_GENERIC_CARD}, {@link SaveInfo#SAVE_DATA_TYPE_USERNAME},
+ * or {@link SaveInfo#SAVE_DATA_TYPE_EMAIL_ADDRESS}.
*
* <p>See {@link SaveInfo} for more info.
*/
@@ -523,6 +577,8 @@ public final class SaveInfo implements Parcelable {
public @NonNull Builder setNegativeAction(@NegativeButtonStyle int style,
@Nullable IntentSender listener) {
throwIfDestroyed();
+ Preconditions.checkArgumentInRange(style, NEGATIVE_BUTTON_STYLE_CANCEL,
+ NEGATIVE_BUTTON_STYLE_REJECT, "style");
if (style != NEGATIVE_BUTTON_STYLE_CANCEL
&& style != NEGATIVE_BUTTON_STYLE_REJECT) {
throw new IllegalArgumentException("Invalid style: " + style);
@@ -533,6 +589,33 @@ public final class SaveInfo implements Parcelable {
}
/**
+ * Sets the style for the positive save action.
+ *
+ * <p>This allows an autofill service to customize the style of the
+ * positive action in the save UI. Note that selecting the positive
+ * action regardless of its style would dismiss the save UI and calling
+ * into the {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback) save request}.
+ * The service should take the next action if selecting style
+ * {@link #POSITIVE_BUTTON_STYLE_CONTINUE}. The default style is
+ * {@link #POSITIVE_BUTTON_STYLE_SAVE}
+ *
+ * @param style The action style.
+ * @return This builder.
+ *
+ * @see #POSITIVE_BUTTON_STYLE_SAVE
+ * @see #POSITIVE_BUTTON_STYLE_CONTINUE
+ *
+ * @throws IllegalArgumentException If the style is invalid
+ */
+ public @NonNull Builder setPositiveAction(@PositiveButtonStyle int style) {
+ throwIfDestroyed();
+ Preconditions.checkArgumentInRange(style, POSITIVE_BUTTON_STYLE_SAVE,
+ POSITIVE_BUTTON_STYLE_CONTINUE, "style");
+ mPositiveButtonStyle = style;
+ return this;
+ }
+
+ /**
* Sets an object used to validate the user input - if the input is not valid, the
* autofill save UI is not shown.
*
@@ -717,8 +800,10 @@ public final class SaveInfo implements Parcelable {
final StringBuilder builder = new StringBuilder("SaveInfo: [type=")
.append(DebugUtils.flagsToString(SaveInfo.class, "SAVE_DATA_TYPE_", mType))
.append(", requiredIds=").append(Arrays.toString(mRequiredIds))
- .append(", style=").append(DebugUtils.flagsToString(SaveInfo.class,
- "NEGATIVE_BUTTON_STYLE_", mNegativeButtonStyle));
+ .append(", negative style=").append(DebugUtils.flagsToString(SaveInfo.class,
+ "NEGATIVE_BUTTON_STYLE_", mNegativeButtonStyle))
+ .append(", positive style=").append(DebugUtils.flagsToString(SaveInfo.class,
+ "POSITIVE_BUTTON_STYLE_", mPositiveButtonStyle));
if (mOptionalIds != null) {
builder.append(", optionalIds=").append(Arrays.toString(mOptionalIds));
}
@@ -763,6 +848,7 @@ public final class SaveInfo implements Parcelable {
parcel.writeParcelableArray(mOptionalIds, flags);
parcel.writeInt(mNegativeButtonStyle);
parcel.writeParcelable(mNegativeActionListener, flags);
+ parcel.writeInt(mPositiveButtonStyle);
parcel.writeCharSequence(mDescription);
parcel.writeParcelable(mCustomDescription, flags);
parcel.writeParcelable(mValidator, flags);
@@ -794,6 +880,7 @@ public final class SaveInfo implements Parcelable {
}
builder.setNegativeAction(parcel.readInt(), parcel.readParcelable(null));
+ builder.setPositiveAction(parcel.readInt());
builder.setDescription(parcel.readCharSequence());
final CustomDescription customDescripton = parcel.readParcelable(null);
if (customDescripton != null) {
diff --git a/core/java/android/service/carrier/CarrierService.java b/core/java/android/service/carrier/CarrierService.java
index aeb186b00686..eefc1b70bac9 100644
--- a/core/java/android/service/carrier/CarrierService.java
+++ b/core/java/android/service/carrier/CarrierService.java
@@ -16,17 +16,15 @@ package android.service.carrier;
import android.annotation.CallSuper;
import android.app.Service;
+import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.PersistableBundle;
-import android.os.RemoteException;
import android.os.ResultReceiver;
-import android.os.ServiceManager;
+import android.telephony.TelephonyRegistryManager;
import android.util.Log;
-import com.android.internal.telephony.ITelephonyRegistry;
-
/**
* A service that exposes carrier-specific functionality to the system.
* <p>
@@ -55,16 +53,10 @@ public abstract class CarrierService extends Service {
public static final String CARRIER_SERVICE_INTERFACE = "android.service.carrier.CarrierService";
- private static ITelephonyRegistry sRegistry;
-
private final ICarrierService.Stub mStubWrapper;
public CarrierService() {
mStubWrapper = new ICarrierServiceWrapper();
- if (sRegistry == null) {
- sRegistry = ITelephonyRegistry.Stub.asInterface(
- ServiceManager.getService("telephony.registry"));
- }
}
/**
@@ -122,9 +114,12 @@ public abstract class CarrierService extends Service {
* @see android.telephony.TelephonyManager#hasCarrierPrivileges
*/
public final void notifyCarrierNetworkChange(boolean active) {
- try {
- if (sRegistry != null) sRegistry.notifyCarrierNetworkChange(active);
- } catch (RemoteException | NullPointerException ex) {}
+ TelephonyRegistryManager telephonyRegistryMgr =
+ (TelephonyRegistryManager) this.getSystemService(
+ Context.TELEPHONY_REGISTRY_SERVICE);
+ if (telephonyRegistryMgr != null) {
+ telephonyRegistryMgr.notifyCarrierNetworkChange(active);
+ }
}
/**
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index ff8b13506201..12c25806d666 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -15,6 +15,8 @@
*/
package android.service.euicc;
+import static android.telephony.euicc.EuiccCardManager.ResetOption;
+
import android.annotation.CallSuper;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -503,7 +505,7 @@ public abstract class EuiccService extends Service {
String nickname);
/**
- * Erase all of the subscriptions on the device.
+ * Erase all operational subscriptions on the device.
*
* <p>This is intended to be used for device resets. As such, the reset should be performed even
* if an active SIM must be deactivated in order to access the eUICC.
@@ -512,10 +514,31 @@ public abstract class EuiccService extends Service {
* @return the result of the erase operation. May be one of the predefined {@code RESULT_}
* constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
* @see android.telephony.euicc.EuiccManager#eraseSubscriptions
+ *
+ * @deprecated From R, callers should specify a flag for specific set of subscriptions to erase
+ * and use {@link #onEraseSubscriptionsWithOptions(int, int)} instead
*/
+ @Deprecated
public abstract int onEraseSubscriptions(int slotId);
/**
+ * Erase specific subscriptions on the device.
+ *
+ * <p>This is intended to be used for device resets. As such, the reset should be performed even
+ * if an active SIM must be deactivated in order to access the eUICC.
+ *
+ * @param slotIndex index of the SIM slot to use for the operation.
+ * @param options flag for specific group of subscriptions to erase
+ * @return the result of the erase operation. May be one of the predefined {@code RESULT_}
+ * constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
+ * @see android.telephony.euicc.EuiccManager#eraseSubscriptionsWithOptions
+ */
+ public int onEraseSubscriptionsWithOptions(int slotIndex, @ResetOption int options) {
+ throw new UnsupportedOperationException(
+ "This method must be overridden to enable the ResetOption parameter");
+ }
+
+ /**
* Ensure that subscriptions will be retained on the next factory reset.
*
* <p>Called directly before a factory reset. Assumes that a normal factory reset will lead to
@@ -751,6 +774,23 @@ public abstract class EuiccService extends Service {
}
@Override
+ public void eraseSubscriptionsWithOptions(
+ int slotIndex, @ResetOption int options, IEraseSubscriptionsCallback callback) {
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ int result = EuiccService.this.onEraseSubscriptionsWithOptions(
+ slotIndex, options);
+ try {
+ callback.onComplete(result);
+ } catch (RemoteException e) {
+ // Can't communicate with the phone process; ignore.
+ }
+ }
+ });
+ }
+
+ @Override
public void retainSubscriptionsForFactoryReset(int slotId,
IRetainSubscriptionsForFactoryResetCallback callback) {
mExecutor.execute(new Runnable() {
diff --git a/core/java/android/service/euicc/IEuiccService.aidl b/core/java/android/service/euicc/IEuiccService.aidl
index c2cdf093706f..2acc47aae919 100644
--- a/core/java/android/service/euicc/IEuiccService.aidl
+++ b/core/java/android/service/euicc/IEuiccService.aidl
@@ -52,6 +52,8 @@ oneway interface IEuiccService {
void updateSubscriptionNickname(int slotId, String iccid, String nickname,
in IUpdateSubscriptionNicknameCallback callback);
void eraseSubscriptions(int slotId, in IEraseSubscriptionsCallback callback);
+ void eraseSubscriptionsWithOptions(
+ int slotIndex, int options, in IEraseSubscriptionsCallback callback);
void retainSubscriptionsForFactoryReset(
int slotId, in IRetainSubscriptionsForFactoryResetCallback callback);
} \ No newline at end of file
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 937990f7e88e..1f2c872a2233 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -1904,10 +1904,10 @@ public class ZenModeConfig implements Parcelable {
}
/**
- * Determines whether dnd behavior should mute all notification/ringer sounds
- * (sounds associated with ringer volume discluding system)
+ * Determines whether dnd behavior should mute all ringer-controlled sounds
+ * This includes notification, ringer and system sounds
*/
- public static boolean areAllPriorityOnlyNotificationZenSoundsMuted(NotificationManager.Policy
+ public static boolean areAllPriorityOnlyRingerSoundsMuted(NotificationManager.Policy
policy) {
boolean allowReminders = (policy.priorityCategories
& NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
@@ -1920,20 +1920,19 @@ public class ZenModeConfig implements Parcelable {
boolean allowRepeatCallers = (policy.priorityCategories
& NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) != 0;
boolean areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0;
+ boolean allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0;
return !allowReminders && !allowCalls && !allowMessages && !allowEvents
- && !allowRepeatCallers && !areChannelsBypassingDnd;
+ && !allowRepeatCallers && !areChannelsBypassingDnd && !allowSystem;
}
/**
- * Determines whether dnd behavior should mute all sounds controlled by ringer
+ * Determines whether dnd behavior should mute all sounds
*/
public static boolean areAllZenBehaviorSoundsMuted(NotificationManager.Policy
policy) {
boolean allowAlarms = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0;
boolean allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0;
- boolean allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0;
- return !allowAlarms && !allowMedia && !allowSystem
- && areAllPriorityOnlyNotificationZenSoundsMuted(policy);
+ return !allowAlarms && !allowMedia && areAllPriorityOnlyRingerSoundsMuted(policy);
}
/**
@@ -1943,24 +1942,25 @@ public class ZenModeConfig implements Parcelable {
return zen == Global.ZEN_MODE_NO_INTERRUPTIONS
|| zen == Global.ZEN_MODE_ALARMS
|| (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
- && ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(consolidatedPolicy));
+ && ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(consolidatedPolicy));
}
/**
- * Determines whether dnd behavior should mute all sounds controlled by ringer
+ * Determines whether dnd behavior should mute all ringer-controlled sounds
+ * This includes notification, ringer and system sounds
*/
- public static boolean areAllPriorityOnlyNotificationZenSoundsMuted(ZenModeConfig config) {
+ public static boolean areAllPriorityOnlyRingerSoundsMuted(ZenModeConfig config) {
return !config.allowReminders && !config.allowCalls && !config.allowMessages
&& !config.allowEvents && !config.allowRepeatCallers
- && !config.areChannelsBypassingDnd;
+ && !config.areChannelsBypassingDnd && !config.allowSystem;
}
/**
- * Determines whether all dnd mutes all sounds
+ * Determines whether dnd mutes all sounds
*/
public static boolean areAllZenBehaviorSoundsMuted(ZenModeConfig config) {
- return !config.allowAlarms && !config.allowMedia && !config.allowSystem
- && areAllPriorityOnlyNotificationZenSoundsMuted(config);
+ return !config.allowAlarms && !config.allowMedia
+ && areAllPriorityOnlyRingerSoundsMuted(config);
}
/**
diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java
index f1c870d96065..dd2586cd58ad 100644
--- a/core/java/android/service/quicksettings/TileService.java
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -126,11 +126,29 @@ public class TileService extends Service {
= "android.service.quicksettings.ACTIVE_TILE";
/**
+ * Meta-data for a tile to support {@code BooleanState}.
+ * <p>
+ * BooleanState is for tiles that should support switch tile behavior in accessibility. This is
+ * the behavior of most of the framework tiles.
+ *
+ * To make a TileService support BooleanState, set this meta-data to true on the TileService's
+ * manifest declaration.
+ * <pre class="prettyprint">
+ * {@literal
+ * <meta-data android:name="android.service.quicksettings.BOOLEAN_TILE"
+ * android:value="true" />
+ * }
+ * </pre>
+ */
+ public static final String META_DATA_BOOLEAN_TILE =
+ "android.service.quicksettings.BOOLEAN_TILE";
+
+ /**
* Used to notify SysUI that Listening has be requested.
* @hide
*/
- public static final String ACTION_REQUEST_LISTENING
- = "android.service.quicksettings.action.REQUEST_LISTENING";
+ public static final String ACTION_REQUEST_LISTENING =
+ "android.service.quicksettings.action.REQUEST_LISTENING";
/**
* @hide
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 5143f1820c2d..2470d197c3fc 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -58,8 +58,8 @@ import java.util.concurrent.Executors;
* Abstract base class for the TextClassifier service.
*
* <p>A TextClassifier service provides text classification related features for the system.
- * The system's default TextClassifierService is configured in
- * {@code config_defaultTextClassifierService}. If this config has no value, a
+ * The system's default TextClassifierService provider is configured in
+ * {@code config_defaultTextClassifierPackage}. If this config has no value, a
* {@link android.view.textclassifier.TextClassifierImpl} is loaded in the calling app's process.
*
* <p>See: {@link TextClassifier}.
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index 70dfef574ca5..dfc5c8217fda 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -170,13 +170,23 @@ public abstract class RecognitionService extends Service {
* Checks whether the caller has sufficient permissions
*
* @param listener to send the error message to in case of error
+ * @param forDataDelivery If the permission check is for delivering the sensitive data.
* @return {@code true} if the caller has enough permissions, {@code false} otherwise
*/
- private boolean checkPermissions(IRecognitionListener listener) {
+ private boolean checkPermissions(IRecognitionListener listener, boolean forDataDelivery) {
if (DBG) Log.d(TAG, "checkPermissions");
- if (PermissionChecker.checkCallingOrSelfPermission(this,
- android.Manifest.permission.RECORD_AUDIO) == PermissionChecker.PERMISSION_GRANTED) {
- return true;
+ if (forDataDelivery) {
+ if (PermissionChecker.checkCallingOrSelfPermissionForDataDelivery(this,
+ android.Manifest.permission.RECORD_AUDIO, null /*message*/)
+ == PermissionChecker.PERMISSION_GRANTED) {
+ return true;
+ }
+ } else {
+ if (PermissionChecker.checkCallingOrSelfPermissionForPreflight(this,
+ android.Manifest.permission.RECORD_AUDIO)
+ == PermissionChecker.PERMISSION_GRANTED) {
+ return true;
+ }
}
try {
Log.e(TAG, "call for recognition service without RECORD_AUDIO permissions");
@@ -342,7 +352,7 @@ public abstract class RecognitionService extends Service {
public void startListening(Intent recognizerIntent, IRecognitionListener listener) {
if (DBG) Log.d(TAG, "startListening called by:" + listener.asBinder());
final RecognitionService service = mServiceRef.get();
- if (service != null && service.checkPermissions(listener)) {
+ if (service != null && service.checkPermissions(listener, true /*forDataDelivery*/)) {
service.mHandler.sendMessage(Message.obtain(service.mHandler,
MSG_START_LISTENING, service.new StartListeningArgs(
recognizerIntent, listener, Binder.getCallingUid())));
@@ -353,7 +363,7 @@ public abstract class RecognitionService extends Service {
public void stopListening(IRecognitionListener listener) {
if (DBG) Log.d(TAG, "stopListening called by:" + listener.asBinder());
final RecognitionService service = mServiceRef.get();
- if (service != null && service.checkPermissions(listener)) {
+ if (service != null && service.checkPermissions(listener, false /*forDataDelivery*/)) {
service.mHandler.sendMessage(Message.obtain(service.mHandler,
MSG_STOP_LISTENING, listener));
}
@@ -363,7 +373,7 @@ public abstract class RecognitionService extends Service {
public void cancel(IRecognitionListener listener) {
if (DBG) Log.d(TAG, "cancel called by:" + listener.asBinder());
final RecognitionService service = mServiceRef.get();
- if (service != null && service.checkPermissions(listener)) {
+ if (service != null && service.checkPermissions(listener, false /*forDataDelivery*/)) {
service.mHandler.sendMessage(Message.obtain(service.mHandler,
MSG_CANCEL, listener));
}
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
new file mode 100644
index 000000000000..a65c8fdf50c9
--- /dev/null
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -0,0 +1,1280 @@
+/*
+ * 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.telephony;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.annotation.UnsupportedAppUsage;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
+import android.telephony.Annotation.CallState;
+import android.telephony.Annotation.RadioPowerState;
+import android.telephony.Annotation.SimActivationState;
+import android.telephony.Annotation.SrvccState;
+import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.ImsReasonInfo;
+
+import com.android.internal.telephony.IPhoneStateListener;
+import com.android.internal.annotations.VisibleForTesting;
+
+import dalvik.system.VMRuntime;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * A listener class for monitoring changes in specific telephony states
+ * on the device, including service state, signal strength, message
+ * waiting indicator (voicemail), and others.
+ * <p>
+ * Override the methods for the state that you wish to receive updates for, and
+ * pass your PhoneStateListener object, along with bitwise-or of the LISTEN_
+ * flags to {@link TelephonyManager#listen TelephonyManager.listen()}. Methods are
+ * called when the state changes, as well as once on initial registration.
+ * <p>
+ * Note that access to some telephony information is
+ * permission-protected. Your application won't receive updates for protected
+ * information unless it has the appropriate permissions declared in
+ * its manifest file. Where permissions apply, they are noted in the
+ * appropriate LISTEN_ flags.
+ */
+public class PhoneStateListener {
+ private static final String LOG_TAG = "PhoneStateListener";
+ private static final boolean DBG = false; // STOPSHIP if true
+
+ /**
+ * Stop listening for updates.
+ *
+ * The PhoneStateListener is not tied to any subscription and unregistered for any update.
+ */
+ public static final int LISTEN_NONE = 0;
+
+ /**
+ * Listen for changes to the network service state (cellular).
+ *
+ * @see #onServiceStateChanged
+ * @see ServiceState
+ */
+ public static final int LISTEN_SERVICE_STATE = 0x00000001;
+
+ /**
+ * Listen for changes to the network signal strength (cellular).
+ * {@more}
+ *
+ * @see #onSignalStrengthChanged
+ *
+ * @deprecated by {@link #LISTEN_SIGNAL_STRENGTHS}
+ */
+ @Deprecated
+ public static final int LISTEN_SIGNAL_STRENGTH = 0x00000002;
+
+ /**
+ * Listen for changes to the message-waiting indicator.
+ * {@more}
+ * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
+ * READ_PHONE_STATE} or that the calling app has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges}).
+ * <p>
+ * Example: The status bar uses this to determine when to display the
+ * voicemail icon.
+ *
+ * @see #onMessageWaitingIndicatorChanged
+ */
+ public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 0x00000004;
+
+ /**
+ * Listen for changes to the call-forwarding indicator.
+ * {@more}
+ * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
+ * READ_PHONE_STATE} or that the calling app has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @see #onCallForwardingIndicatorChanged
+ */
+ public static final int LISTEN_CALL_FORWARDING_INDICATOR = 0x00000008;
+
+ /**
+ * Listen for changes to the device's cell location. Note that
+ * this will result in frequent callbacks to the listener.
+ * {@more}
+ * Requires Permission: {@link android.Manifest.permission#ACCESS_COARSE_LOCATION
+ * ACCESS_COARSE_LOCATION}
+ * <p>
+ * If you need regular location updates but want more control over
+ * the update interval or location precision, you can set up a listener
+ * through the {@link android.location.LocationManager location manager}
+ * instead.
+ *
+ * @see #onCellLocationChanged
+ */
+ public static final int LISTEN_CELL_LOCATION = 0x00000010;
+
+ /**
+ * Listen for changes to the device call state.
+ * {@more}
+ *
+ * @see #onCallStateChanged
+ */
+ public static final int LISTEN_CALL_STATE = 0x00000020;
+
+ /**
+ * Listen for changes to the data connection state (cellular).
+ *
+ * @see #onDataConnectionStateChanged
+ */
+ public static final int LISTEN_DATA_CONNECTION_STATE = 0x00000040;
+
+ /**
+ * Listen for changes to the direction of data traffic on the data
+ * connection (cellular).
+ * {@more}
+ * Example: The status bar uses this to display the appropriate
+ * data-traffic icon.
+ *
+ * @see #onDataActivity
+ */
+ public static final int LISTEN_DATA_ACTIVITY = 0x00000080;
+
+ /**
+ * Listen for changes to the network signal strengths (cellular).
+ * <p>
+ * Example: The status bar uses this to control the signal-strength
+ * icon.
+ *
+ * @see #onSignalStrengthsChanged
+ */
+ public static final int LISTEN_SIGNAL_STRENGTHS = 0x00000100;
+
+ /**
+ * Listen for changes to OTASP mode.
+ *
+ * @see #onOtaspChanged
+ * @hide
+ */
+ public static final int LISTEN_OTASP_CHANGED = 0x00000200;
+
+ /**
+ * Listen for changes to observed cell info.
+ *
+ * @see #onCellInfoChanged
+ */
+ public static final int LISTEN_CELL_INFO = 0x00000400;
+
+ /**
+ * Listen for {@link android.telephony.Annotation.PreciseCallStates} of ringing,
+ * background and foreground calls.
+ *
+ * @hide
+ */
+ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
+ @SystemApi
+ public static final int LISTEN_PRECISE_CALL_STATE = 0x00000800;
+
+ /**
+ * Listen for {@link PreciseDataConnectionState} on the data connection (cellular).
+ *
+ * @see #onPreciseDataConnectionStateChanged
+ *
+ * @hide
+ */
+ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
+ @SystemApi
+ public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 0x00001000;
+
+ /**
+ * Listen for real time info for all data connections (cellular)).
+ * {@more}
+ * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE}
+ * @see #onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo)
+ *
+ * @deprecated Use {@link TelephonyManager#getModemActivityInfo()}
+ * @hide
+ */
+ @Deprecated
+ public static final int LISTEN_DATA_CONNECTION_REAL_TIME_INFO = 0x00002000;
+
+ /**
+ * Listen for changes to the SRVCC state of the active call.
+ * @see #onServiceStateChanged(ServiceState)
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public static final int LISTEN_SRVCC_STATE_CHANGED = 0x00004000;
+
+ /**
+ * Listen for OEM hook raw event
+ *
+ * @see #onOemHookRawEvent
+ * @hide
+ * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
+ */
+ @Deprecated
+ public static final int LISTEN_OEM_HOOK_RAW_EVENT = 0x00008000;
+
+ /**
+ * Listen for carrier network changes indicated by a carrier app.
+ *
+ * @see #onCarrierNetworkRequest
+ * @see TelephonyManager#notifyCarrierNetworkChange(boolean)
+ * @hide
+ */
+ public static final int LISTEN_CARRIER_NETWORK_CHANGE = 0x00010000;
+
+ /**
+ * Listen for changes to the sim voice activation state
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+ * {@more}
+ * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been
+ * fully activated
+ *
+ * @see #onVoiceActivationStateChanged
+ * @hide
+ */
+ @SystemApi
+ public static final int LISTEN_VOICE_ACTIVATION_STATE = 0x00020000;
+
+ /**
+ * Listen for changes to the sim data activation state
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
+ * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+ * {@more}
+ * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been
+ * fully activated
+ *
+ * @see #onDataActivationStateChanged
+ * @hide
+ */
+ public static final int LISTEN_DATA_ACTIVATION_STATE = 0x00040000;
+
+ /**
+ * Listen for changes to the user mobile data state
+ *
+ * @see #onUserMobileDataStateChanged
+ */
+ public static final int LISTEN_USER_MOBILE_DATA_STATE = 0x00080000;
+
+ /**
+ * Listen for changes to the physical channel configuration.
+ *
+ * @see #onPhysicalChannelConfigurationChanged
+ * @hide
+ */
+ public static final int LISTEN_PHYSICAL_CHANNEL_CONFIGURATION = 0x00100000;
+
+ /**
+ * Listen for changes to the phone capability.
+ *
+ * @see #onPhoneCapabilityChanged
+ * @hide
+ */
+ public static final int LISTEN_PHONE_CAPABILITY_CHANGE = 0x00200000;
+
+ /**
+ * Listen for changes to active data subId. Active data subscription is
+ * the current subscription used to setup Cellular Internet data. For example,
+ * it could be the current active opportunistic subscription in use, or the
+ * subscription user selected as default data subscription in DSDS mode.
+ *
+ * @see #onActiveDataSubscriptionIdChanged
+ */
+ public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 0x00400000;
+
+ /**
+ * Listen for changes to the radio power state.
+ *
+ * @see #onRadioPowerStateChanged
+ * @hide
+ */
+ @SystemApi
+ public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 0x00800000;
+
+ /**
+ * Listen for changes to emergency number list based on all active subscriptions.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
+ * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @see #onEmergencyNumberListChanged
+ */
+ public static final int LISTEN_EMERGENCY_NUMBER_LIST = 0x01000000;
+
+ /**
+ * Listen for call disconnect causes which contains {@link DisconnectCause} and
+ * {@link PreciseDisconnectCause}.
+ *
+ * @hide
+ */
+ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
+ @SystemApi
+ public static final int LISTEN_CALL_DISCONNECT_CAUSES = 0x02000000;
+
+ /**
+ * Listen for changes to the call attributes of a currently active call.
+ * {@more}
+ * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE}
+ *
+ * @see #onCallAttributesChanged
+ * @hide
+ */
+ @SystemApi
+ public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 0x04000000;
+
+ /**
+ * Listen for IMS call disconnect causes which contains
+ * {@link android.telephony.ims.ImsReasonInfo}
+ *
+ * @see #onImsCallDisconnectCauseChanged(ImsReasonInfo)
+ * @hide
+ */
+ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
+ @SystemApi
+ public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 0x08000000;
+
+ /**
+ * Listen for the emergency number placed from an outgoing call.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
+ *
+ * @see #onOutgoingEmergencyCall
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+ public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 0x10000000;
+
+ /**
+ * Listen for the emergency number placed from an outgoing SMS.
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
+ *
+ * @see #onOutgoingEmergencySms
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
+ public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 0x20000000;
+
+ /*
+ * Subscription used to listen to the phone state changes
+ * @hide
+ */
+ /** @hide */
+ @UnsupportedAppUsage
+ protected Integer mSubId;
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ @UnsupportedAppUsage
+ public final IPhoneStateListener callback;
+
+ /**
+ * Create a PhoneStateListener for the Phone with the default subscription.
+ * This class requires Looper.myLooper() not return null.
+ */
+ public PhoneStateListener() {
+ this(null, Looper.myLooper());
+ }
+
+ /**
+ * Create a PhoneStateListener for the Phone with the default subscription
+ * using a particular non-null Looper.
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ public PhoneStateListener(Looper looper) {
+ this(null, looper);
+ }
+
+ /**
+ * Create a PhoneStateListener for the Phone using the specified subscription.
+ * This class requires Looper.myLooper() not return null. To supply your
+ * own non-null Looper use PhoneStateListener(int subId, Looper looper) below.
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ public PhoneStateListener(Integer subId) {
+ this(subId, Looper.myLooper());
+ if (subId != null && VMRuntime.getRuntime().getTargetSdkVersion()
+ >= Build.VERSION_CODES.Q) {
+ throw new IllegalArgumentException("PhoneStateListener with subId: "
+ + subId + " is not supported, use default constructor");
+ }
+ }
+ /**
+ * Create a PhoneStateListener for the Phone using the specified subscription
+ * and non-null Looper.
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+ public PhoneStateListener(Integer subId, Looper looper) {
+ this(subId, new HandlerExecutor(new Handler(looper)));
+ if (subId != null && VMRuntime.getRuntime().getTargetSdkVersion()
+ >= Build.VERSION_CODES.Q) {
+ throw new IllegalArgumentException("PhoneStateListener with subId: "
+ + subId + " is not supported, use default constructor");
+ }
+ }
+
+ /**
+ * Create a PhoneStateListener for the Phone using the specified Executor
+ *
+ * <p>Create a PhoneStateListener with a specified Executor for handling necessary callbacks.
+ * The Executor must not be null.
+ *
+ * @param executor a non-null Executor that will execute callbacks for the PhoneStateListener.
+ */
+ public PhoneStateListener(@NonNull Executor executor) {
+ this(null, executor);
+ }
+
+ private PhoneStateListener(Integer subId, Executor e) {
+ if (e == null) {
+ throw new IllegalArgumentException("PhoneStateListener Executor must be non-null");
+ }
+ mSubId = subId;
+ callback = new IPhoneStateListenerStub(this, e);
+ }
+
+ /**
+ * Callback invoked when device service state changes on the registered subscription.
+ * Note, the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @see ServiceState#STATE_EMERGENCY_ONLY
+ * @see ServiceState#STATE_IN_SERVICE
+ * @see ServiceState#STATE_OUT_OF_SERVICE
+ * @see ServiceState#STATE_POWER_OFF
+ */
+ public void onServiceStateChanged(ServiceState serviceState) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when network signal strength changes on the registered subscription.
+ * Note, the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @see ServiceState#STATE_EMERGENCY_ONLY
+ * @see ServiceState#STATE_IN_SERVICE
+ * @see ServiceState#STATE_OUT_OF_SERVICE
+ * @see ServiceState#STATE_POWER_OFF
+ * @deprecated Use {@link #onSignalStrengthsChanged(SignalStrength)}
+ */
+ @Deprecated
+ public void onSignalStrengthChanged(int asu) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when the message-waiting indicator changes on the registered subscription.
+ * Note, the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ */
+ public void onMessageWaitingIndicatorChanged(boolean mwi) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when the call-forwarding indicator changes on the registered subscription.
+ * Note, the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ */
+ public void onCallForwardingIndicatorChanged(boolean cfi) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when device cell location changes on the registered subscription.
+ * Note, the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ */
+ public void onCellLocationChanged(CellLocation location) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when device call state changes.
+ * <p>
+ * Reports the state of Telephony (mobile) calls on the device for the registered subscription.
+ * <p>
+ * Note: the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ * <p>
+ * Note: The state returned here may differ from that returned by
+ * {@link TelephonyManager#getCallState()}. Receivers of this callback should be aware that
+ * calling {@link TelephonyManager#getCallState()} from within this callback may return a
+ * different state than the callback reports.
+ *
+ * @param state call state
+ * @param phoneNumber call phone number. If application does not have
+ * {@link android.Manifest.permission#READ_CALL_LOG READ_CALL_LOG} permission or carrier
+ * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), an empty string will be
+ * passed as an argument.
+ */
+ public void onCallStateChanged(@CallState int state, String phoneNumber) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when connection state changes on the registered subscription.
+ * Note, the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @see TelephonyManager#DATA_DISCONNECTED
+ * @see TelephonyManager#DATA_CONNECTING
+ * @see TelephonyManager#DATA_CONNECTED
+ * @see TelephonyManager#DATA_SUSPENDED
+ */
+ public void onDataConnectionStateChanged(int state) {
+ // default implementation empty
+ }
+
+ /**
+ * same as above, but with the network type. Both called.
+ */
+ public void onDataConnectionStateChanged(int state, int networkType) {
+ }
+
+ /**
+ * Callback invoked when data activity state changes on the registered subscription.
+ * Note, the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @see TelephonyManager#DATA_ACTIVITY_NONE
+ * @see TelephonyManager#DATA_ACTIVITY_IN
+ * @see TelephonyManager#DATA_ACTIVITY_OUT
+ * @see TelephonyManager#DATA_ACTIVITY_INOUT
+ * @see TelephonyManager#DATA_ACTIVITY_DORMANT
+ */
+ public void onDataActivity(int direction) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when network signal strengths changes on the registered subscription.
+ * Note, the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ */
+ public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+ // default implementation empty
+ }
+
+
+ /**
+ * The Over The Air Service Provisioning (OTASP) has changed on the registered subscription.
+ * Note, the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * Requires the READ_PHONE_STATE permission.
+ * @param otaspMode is integer <code>OTASP_UNKNOWN=1<code>
+ * means the value is currently unknown and the system should wait until
+ * <code>OTASP_NEEDED=2<code> or <code>OTASP_NOT_NEEDED=3<code> is received before
+ * making the decision to perform OTASP or not.
+ *
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public void onOtaspChanged(int otaspMode) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when a observed cell info has changed or new cells have been added
+ * or removed on the registered subscription.
+ * Note, the registration subId s from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param cellInfo is the list of currently visible cells.
+ */
+ public void onCellInfoChanged(List<CellInfo> cellInfo) {
+ }
+
+ /**
+ * Callback invoked when precise device call state changes on the registered subscription.
+ * Note, the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ * @param callState {@link PreciseCallState}
+ * @hide
+ */
+ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
+ @SystemApi
+ public void onPreciseCallStateChanged(@NonNull PreciseCallState callState) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when call disconnect cause changes on the registered subscription.
+ * Note, the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param disconnectCause {@link DisconnectCause}.
+ * @param preciseDisconnectCause {@link PreciseDisconnectCause}.
+ *
+ * @hide
+ */
+ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
+ @SystemApi
+ public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when Ims call disconnect cause changes on the registered subscription.
+ * Note, the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed.
+ *
+ * @hide
+ */
+ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
+ @SystemApi
+ public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when data connection state changes with precise information
+ * on the registered subscription.
+ * Note, the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param dataConnectionState {@link PreciseDataConnectionState}
+ *
+ * @hide
+ */
+ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
+ @SystemApi
+ public void onPreciseDataConnectionStateChanged(
+ @NonNull PreciseDataConnectionState dataConnectionState) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when data connection real time info changes on the registered subscription.
+ * Note, the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public void onDataConnectionRealTimeInfoChanged(
+ DataConnectionRealTimeInfo dcRtInfo) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when there has been a change in the Single Radio Voice Call Continuity
+ * (SRVCC) state for the currently active call on the registered subscription.
+ *
+ * Note, the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void onSrvccStateChanged(@SrvccState int srvccState) {
+
+ }
+
+ /**
+ * Callback invoked when the SIM voice activation state has changed on the registered
+ * subscription.
+ * Note, the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param state is the current SIM voice activation state
+ * @hide
+ */
+ @SystemApi
+ public void onVoiceActivationStateChanged(@SimActivationState int state) {
+ }
+
+ /**
+ * Callback invoked when the SIM data activation state has changed on the registered
+ * subscription.
+ * Note, the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param state is the current SIM data activation state
+ * @hide
+ */
+ public void onDataActivationStateChanged(@SimActivationState int state) {
+ }
+
+ /**
+ * Callback invoked when the user mobile data state has changed on the registered subscription.
+ * Note, the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param enabled indicates whether the current user mobile data state is enabled or disabled.
+ */
+ public void onUserMobileDataStateChanged(boolean enabled) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when the current physical channel configuration has changed on the
+ * registered subscription.
+ * Note, the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param configs List of the current {@link PhysicalChannelConfig}s
+ * @hide
+ */
+ public void onPhysicalChannelConfigurationChanged(
+ @NonNull List<PhysicalChannelConfig> configs) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when the current emergency number list has changed on the registered
+ * subscription.
+ * Note, the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * @param emergencyNumberList Map including the key as the active subscription ID
+ * (Note: if there is no active subscription, the key is
+ * {@link SubscriptionManager#getDefaultSubscriptionId})
+ * and the value as the list of {@link EmergencyNumber};
+ * null if this information is not available.
+ * @hide
+ */
+ public void onEmergencyNumberListChanged(
+ @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when an outgoing call is placed to an emergency number.
+ *
+ * @param placedEmergencyNumber the emergency number {@link EmergencyNumber} the call is placed
+ * to.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when an outgoing SMS is placed to an emergency number.
+ *
+ * @param sentEmergencyNumber the emergency number {@link EmergencyNumber} the SMS is sent to.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when OEM hook raw event is received on the registered subscription.
+ * Note, the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * Requires the READ_PRIVILEGED_PHONE_STATE permission.
+ * @param rawData is the byte array of the OEM hook raw data.
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public void onOemHookRawEvent(byte[] rawData) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when phone capability changes.
+ * Note, this callback triggers regardless of registered subscription.
+ *
+ * Requires the READ_PRIVILEGED_PHONE_STATE permission.
+ * @param capability the new phone capability
+ * @hide
+ */
+ public void onPhoneCapabilityChanged(PhoneCapability capability) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when active data subId changes.
+ * Note, this callback triggers regardless of registered subscription.
+ *
+ * Requires the READ_PHONE_STATE permission.
+ * @param subId current subscription used to setup Cellular Internet data.
+ * For example, it could be the current active opportunistic subscription in use,
+ * or the subscription user selected as default data subscription in DSDS mode.
+ */
+ public void onActiveDataSubscriptionIdChanged(int subId) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when the call attributes changes on the registered subscription.
+ * Note, the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * Requires the READ_PRIVILEGED_PHONE_STATE permission.
+ * @param callAttributes the call attributes
+ * @hide
+ */
+ @SystemApi
+ public void onCallAttributesChanged(@NonNull CallAttributes callAttributes) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when modem radio power state changes on the registered subscription.
+ * Note, the registration subId comes from {@link TelephonyManager} object which registers
+ * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
+ * If this TelephonyManager object was created with
+ * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
+ * subId. Otherwise, this callback applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ *
+ * Requires
+ * the READ_PRIVILEGED_PHONE_STATE permission.
+ * @param state the modem radio power state
+ * @hide
+ */
+ @SystemApi
+ public void onRadioPowerStateChanged(@RadioPowerState int state) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when telephony has received notice from a carrier
+ * app that a network action that could result in connectivity loss
+ * has been requested by an app using
+ * {@link android.telephony.TelephonyManager#notifyCarrierNetworkChange(boolean)}
+ *
+ * Note, this callback is pinned to the registered subscription and will be invoked when
+ * the notifying carrier app has carrier privilege rule on the registered
+ * subscription. {@link android.telephony.TelephonyManager#hasCarrierPrivileges}
+ *
+ * @param active Whether the carrier network change is or shortly
+ * will be active. This value is true to indicate
+ * showing alternative UI and false to stop.
+ *
+ * @hide
+ */
+ public void onCarrierNetworkChange(boolean active) {
+ // default implementation empty
+ }
+
+ /**
+ * The callback methods need to be called on the handler thread where
+ * this object was created. If the binder did that for us it'd be nice.
+ *
+ * Using a static class and weak reference here to avoid memory leak caused by the
+ * IPhoneStateListener.Stub callback retaining references to the outside PhoneStateListeners:
+ * even caller has been destroyed and "un-registered" the PhoneStateListener, it is still not
+ * eligible for GC given the references coming from:
+ * Native Stack --> PhoneStateListener --> Context (Activity).
+ * memory of caller's context will be collected after GC from service side get triggered
+ */
+ private static class IPhoneStateListenerStub extends IPhoneStateListener.Stub {
+ private WeakReference<PhoneStateListener> mPhoneStateListenerWeakRef;
+ private Executor mExecutor;
+
+ IPhoneStateListenerStub(PhoneStateListener phoneStateListener, Executor executor) {
+ mPhoneStateListenerWeakRef = new WeakReference<PhoneStateListener>(phoneStateListener);
+ mExecutor = executor;
+ }
+
+ public void onServiceStateChanged(ServiceState serviceState) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onServiceStateChanged(serviceState)));
+ }
+
+ public void onSignalStrengthChanged(int asu) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onSignalStrengthChanged(asu)));
+ }
+
+ public void onMessageWaitingIndicatorChanged(boolean mwi) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onMessageWaitingIndicatorChanged(mwi)));
+ }
+
+ public void onCallForwardingIndicatorChanged(boolean cfi) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onCallForwardingIndicatorChanged(cfi)));
+ }
+
+ public void onCellLocationChanged(Bundle bundle) {
+ CellLocation location = CellLocation.newFromBundle(bundle);
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onCellLocationChanged(location)));
+ }
+
+ public void onCallStateChanged(int state, String incomingNumber) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onCallStateChanged(state, incomingNumber)));
+ }
+
+ public void onDataConnectionStateChanged(int state, int networkType) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(() -> mExecutor.execute(
+ () -> {
+ psl.onDataConnectionStateChanged(state, networkType);
+ psl.onDataConnectionStateChanged(state);
+ }));
+ }
+
+ public void onDataActivity(int direction) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onDataActivity(direction)));
+ }
+
+ public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onSignalStrengthsChanged(signalStrength)));
+ }
+
+ public void onOtaspChanged(int otaspMode) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onOtaspChanged(otaspMode)));
+ }
+
+ public void onCellInfoChanged(List<CellInfo> cellInfo) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onCellInfoChanged(cellInfo)));
+ }
+
+ public void onPreciseCallStateChanged(PreciseCallState callState) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onPreciseCallStateChanged(callState)));
+ }
+
+ public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onCallDisconnectCauseChanged(
+ disconnectCause, preciseDisconnectCause)));
+ }
+
+ public void onPreciseDataConnectionStateChanged(
+ PreciseDataConnectionState dataConnectionState) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> psl.onPreciseDataConnectionStateChanged(dataConnectionState)));
+ }
+
+ public void onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo dcRtInfo) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> psl.onDataConnectionRealTimeInfoChanged(dcRtInfo)));
+ }
+
+ public void onSrvccStateChanged(int state) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onSrvccStateChanged(state)));
+ }
+
+ public void onVoiceActivationStateChanged(int activationState) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> psl.onVoiceActivationStateChanged(activationState)));
+ }
+
+ public void onDataActivationStateChanged(int activationState) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> psl.onDataActivationStateChanged(activationState)));
+ }
+
+ public void onUserMobileDataStateChanged(boolean enabled) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> psl.onUserMobileDataStateChanged(enabled)));
+ }
+
+ public void onOemHookRawEvent(byte[] rawData) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onOemHookRawEvent(rawData)));
+ }
+
+ public void onCarrierNetworkChange(boolean active) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onCarrierNetworkChange(active)));
+ }
+
+ public void onPhysicalChannelConfigurationChanged(List<PhysicalChannelConfig> configs) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> psl.onPhysicalChannelConfigurationChanged(configs)));
+ }
+
+ public void onEmergencyNumberListChanged(Map emergencyNumberList) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> psl.onEmergencyNumberListChanged(emergencyNumberList)));
+ }
+
+ public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> psl.onOutgoingEmergencyCall(placedEmergencyNumber)));
+ }
+
+ public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> psl.onOutgoingEmergencySms(sentEmergencyNumber)));
+ }
+
+ public void onPhoneCapabilityChanged(PhoneCapability capability) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onPhoneCapabilityChanged(capability)));
+ }
+
+ public void onRadioPowerStateChanged(@RadioPowerState int state) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onRadioPowerStateChanged(state)));
+ }
+
+ public void onCallAttributesChanged(CallAttributes callAttributes) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onCallAttributesChanged(callAttributes)));
+ }
+
+ public void onActiveDataSubIdChanged(int subId) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> psl.onActiveDataSubscriptionIdChanged(subId)));
+ }
+
+ public void onImsCallDisconnectCauseChanged(ImsReasonInfo disconnectCause) {
+ PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+ if (psl == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(
+ () -> psl.onImsCallDisconnectCauseChanged(disconnectCause)));
+
+ }
+ }
+
+
+ private void log(String s) {
+ Rlog.d(LOG_TAG, s);
+ }
+}
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
new file mode 100644
index 000000000000..64d612405c34
--- /dev/null
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -0,0 +1,697 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.Annotation.ApnType;
+import android.telephony.Annotation.CallState;
+import android.telephony.Annotation.DataActivityType;
+import android.telephony.Annotation.DataFailureCause;
+import android.telephony.Annotation.DataState;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.Annotation.PreciseCallStates;
+import android.telephony.Annotation.RadioPowerState;
+import android.telephony.Annotation.SimActivationState;
+import android.telephony.Annotation.SrvccState;
+import android.telephony.CallQuality;
+import android.telephony.CellInfo;
+import android.telephony.DisconnectCause;
+import android.telephony.PhoneCapability;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
+import android.telephony.ims.ImsReasonInfo;
+import android.util.Log;
+
+import com.android.internal.telephony.ITelephonyRegistry;
+import com.android.internal.telephony.IOnSubscriptionsChangedListener;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * A centralized place to notify telephony related status changes, e.g, {@link ServiceState} update
+ * or {@link PhoneCapability} changed. This might trigger callback from applications side through
+ * {@link android.telephony.PhoneStateListener}
+ *
+ * TODO: limit API access to only carrier apps with certain permissions or apps running on
+ * privileged UID.
+ *
+ * @hide
+ */
+@SystemApi
+public class TelephonyRegistryManager {
+
+ private static final String TAG = "TelephonyRegistryManager";
+ private static ITelephonyRegistry sRegistry;
+ private final Context mContext;
+
+ /**
+ * A mapping between {@link SubscriptionManager.OnSubscriptionsChangedListener} and
+ * its callback IOnSubscriptionsChangedListener.
+ */
+ private final Map<SubscriptionManager.OnSubscriptionsChangedListener,
+ IOnSubscriptionsChangedListener> mSubscriptionChangedListenerMap = new HashMap<>();
+ /**
+ * A mapping between {@link SubscriptionManager.OnOpportunisticSubscriptionsChangedListener} and
+ * its callback IOnSubscriptionsChangedListener.
+ */
+ private final Map<SubscriptionManager.OnOpportunisticSubscriptionsChangedListener,
+ IOnSubscriptionsChangedListener> mOpportunisticSubscriptionChangedListenerMap
+ = new HashMap<>();
+
+
+ /** @hide **/
+ public TelephonyRegistryManager(@NonNull Context context) {
+ mContext = context;
+ if (sRegistry == null) {
+ sRegistry = ITelephonyRegistry.Stub.asInterface(
+ ServiceManager.getService("telephony.registry"));
+ }
+ }
+
+ /**
+ * Register for changes to the list of active {@link SubscriptionInfo} records or to the
+ * individual records themselves. When a change occurs the onSubscriptionsChanged method of
+ * the listener will be invoked immediately if there has been a notification. The
+ * onSubscriptionChanged method will also be triggered once initially when calling this
+ * function.
+ *
+ * @param listener an instance of {@link SubscriptionManager.OnSubscriptionsChangedListener}
+ * with onSubscriptionsChanged overridden.
+ * @param executor the executor that will execute callbacks.
+ */
+ public void addOnSubscriptionsChangedListener(
+ @NonNull SubscriptionManager.OnSubscriptionsChangedListener listener,
+ @NonNull Executor executor) {
+ IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() {
+ @Override
+ public void onSubscriptionsChanged () {
+ Log.d(TAG, "onSubscriptionsChangedListener callback received.");
+ executor.execute(() -> listener.onSubscriptionsChanged());
+ }
+ };
+ mSubscriptionChangedListenerMap.put(listener, callback);
+ try {
+ sRegistry.addOnSubscriptionsChangedListener(mContext.getOpPackageName(), callback);
+ } catch (RemoteException ex) {
+ // system server crash
+ }
+ }
+
+ /**
+ * Unregister the {@link SubscriptionManager.OnSubscriptionsChangedListener}. This is not
+ * strictly necessary as the listener will automatically be unregistered if an attempt to
+ * invoke the listener fails.
+ *
+ * @param listener that is to be unregistered.
+ */
+ public void removeOnSubscriptionsChangedListener(
+ @NonNull SubscriptionManager.OnSubscriptionsChangedListener listener) {
+ if (mSubscriptionChangedListenerMap.get(listener) == null) {
+ return;
+ }
+ try {
+ sRegistry.removeOnSubscriptionsChangedListener(mContext.getOpPackageName(),
+ mSubscriptionChangedListenerMap.get(listener));
+ mSubscriptionChangedListenerMap.remove(listener);
+ } catch (RemoteException ex) {
+ // system server crash
+ }
+ }
+
+ /**
+ * Register for changes to the list of opportunistic subscription records or to the
+ * individual records themselves. When a change occurs the onOpportunisticSubscriptionsChanged
+ * method of the listener will be invoked immediately if there has been a notification.
+ *
+ * @param listener an instance of
+ * {@link SubscriptionManager.OnOpportunisticSubscriptionsChangedListener} with
+ * onOpportunisticSubscriptionsChanged overridden.
+ * @param executor an Executor that will execute callbacks.
+ */
+ public void addOnOpportunisticSubscriptionsChangedListener(
+ @NonNull SubscriptionManager.OnOpportunisticSubscriptionsChangedListener listener,
+ @NonNull Executor executor) {
+ /**
+ * The callback methods need to be called on the executor thread where
+ * this object was created. If the binder did that for us it'd be nice.
+ */
+ IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() {
+ @Override
+ public void onSubscriptionsChanged() {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Log.d(TAG, "onOpportunisticSubscriptionsChanged callback received.");
+ executor.execute(() -> listener.onOpportunisticSubscriptionsChanged());
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ };
+ mOpportunisticSubscriptionChangedListenerMap.put(listener, callback);
+ try {
+ sRegistry.addOnOpportunisticSubscriptionsChangedListener(mContext.getOpPackageName(),
+ callback);
+ } catch (RemoteException ex) {
+ // system server crash
+ }
+ }
+
+ /**
+ * Unregister the {@link SubscriptionManager.OnOpportunisticSubscriptionsChangedListener}
+ * that is currently listening opportunistic subscriptions change. This is not strictly
+ * necessary as the listener will automatically be unregistered if an attempt to invoke the
+ * listener fails.
+ *
+ * @param listener that is to be unregistered.
+ */
+ public void removeOnOpportunisticSubscriptionsChangedListener(
+ @NonNull SubscriptionManager.OnOpportunisticSubscriptionsChangedListener listener) {
+ try {
+ sRegistry.removeOnSubscriptionsChangedListener(mContext.getOpPackageName(),
+ mOpportunisticSubscriptionChangedListenerMap.get(listener));
+ mOpportunisticSubscriptionChangedListenerMap.remove(listener);
+ } catch (RemoteException ex) {
+ // system server crash
+ }
+ }
+
+ /**
+ * Informs the system of an intentional upcoming carrier network change by a carrier app.
+ * This call only used to allow the system to provide alternative UI while telephony is
+ * performing an action that may result in intentional, temporary network lack of connectivity.
+ * <p>
+ * Based on the active parameter passed in, this method will either show or hide the alternative
+ * UI. There is no timeout associated with showing this UX, so a carrier app must be sure to
+ * call with active set to false sometime after calling with it set to {@code true}.
+ * <p>
+ * Requires Permission: calling app has carrier privileges.
+ *
+ * @param active Whether the carrier network change is or shortly will be
+ * active. Set this value to true to begin showing alternative UI and false to stop.
+ * @see TelephonyManager#hasCarrierPrivileges
+ */
+ public void notifyCarrierNetworkChange(boolean active) {
+ try {
+ sRegistry.notifyCarrierNetworkChange(active);
+ } catch (RemoteException ex) {
+ // system server crash
+ }
+ }
+
+ /**
+ * Notify call state changed on certain subscription.
+ *
+ * @param subId for which call state changed.
+ * @param slotIndex for which call state changed. Can be derived from subId except when subId is
+ * invalid.
+ * @param state latest call state. e.g, offhook, ringing
+ * @param incomingNumer incoming phone number.
+ *
+ * @hide
+ */
+ public void notifyCallStateChanged(int subId, int slotIndex, @CallState int state,
+ String incomingNumer) {
+ try {
+ sRegistry.notifyCallState(slotIndex, subId, state, incomingNumer);
+ } catch (RemoteException ex) {
+ // system server crash
+ }
+ }
+
+ /**
+ * Notify {@link ServiceState} update on certain subscription.
+ *
+ * @param subId for which the service state changed.
+ * @param slotIndex for which the service state changed. Can be derived from subId except
+ * subId is invalid.
+ * @param state service state e.g, in service, out of service or roaming status.
+ *
+ * @hide
+ */
+ public void notifyServiceStateChanged(int subId, int slotIndex, ServiceState state) {
+ try {
+ sRegistry.notifyServiceStateForPhoneId(slotIndex, subId, state);
+ } catch (RemoteException ex) {
+ // system server crash
+ }
+ }
+
+ /**
+ * Notify {@link SignalStrength} update on certain subscription.
+ *
+ * @param subId for which the signalstrength changed.
+ * @param slotIndex for which the signalstrength changed. Can be derived from subId except when
+ * subId is invalid.
+ * @param signalStrength e.g, signalstrength level {@see SignalStrength#getLevel()}
+ *
+ * @hide
+ */
+ public void notifySignalStrengthChanged(int subId, int slotIndex,
+ SignalStrength signalStrength) {
+ try {
+ sRegistry.notifySignalStrengthForPhoneId(slotIndex, subId, signalStrength);
+ } catch (RemoteException ex) {
+ // system server crash
+ }
+ }
+
+ /**
+ * Notify changes to the message-waiting indicator on certain subscription. e.g, The status bar
+ * uses message waiting indicator to determine when to display the voicemail icon.
+ *
+ * @param subId for which message waiting indicator changed.
+ * @param slotIndex for which message waiting indicator changed. Can be derived from subId
+ * except when subId is invalid.
+ * @param msgWaitingInd {@code true} indicates there is message-waiting indicator, {@code false}
+ * otherwise.
+ *
+ * @hide
+ */
+ public void notifyMessageWaitingChanged(int subId, int slotIndex, boolean msgWaitingInd) {
+ try {
+ sRegistry.notifyMessageWaitingChangedForPhoneId(slotIndex, subId, msgWaitingInd);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
+ * Notify changes to the call-forwarding status on certain subscription.
+ *
+ * @param subId for which call forwarding status changed.
+ * @param callForwardInd {@code true} indicates there is call forwarding, {@code false}
+ * otherwise.
+ *
+ * @hide
+ */
+ public void notifyCallForwardingChanged(int subId, boolean callForwardInd) {
+ try {
+ sRegistry.notifyCallForwardingChangedForSubscriber(subId, callForwardInd);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
+ * Notify changes to activity state changes on certain subscription.
+ *
+ * @param subId for which data activity state changed.
+ * @param dataActivityType indicates the latest data activity type e.g, {@link
+ * TelephonyManager#DATA_ACTIVITY_IN}
+ *
+ * @hide
+ */
+ public void notifyDataActivityChanged(int subId, @DataActivityType int dataActivityType) {
+ try {
+ sRegistry.notifyDataActivityForSubscriber(subId, dataActivityType);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
+ * Notify changes to default (Internet) data connection state on certain subscription.
+ *
+ * @param subId for which data connection state changed.
+ * @param slotIndex for which data connections state changed. Can be derived from subId except
+ * when subId is invalid.
+ * @param state latest data connection state, e.g,
+ * @param isDataConnectivityPossible indicates if data is allowed
+ * @param apn the APN {@link ApnSetting#getApnName()} of this data connection.
+ * @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN.
+ * @param linkProperties {@link LinkProperties} associated with this data connection.
+ * @param networkCapabilities {@link NetworkCapabilities} associated with this data connection.
+ * @param networkType associated with this data connection.
+ * @param roaming {@code true} indicates in roaming, {@false} otherwise.
+ * @see TelephonyManager#DATA_DISCONNECTED
+ * @see TelephonyManager#isDataConnectivityPossible()
+ *
+ * @hide
+ */
+ public void notifyDataConnectionForSubscriber(int slotIndex, int subId, @DataState int state,
+ boolean isDataConnectivityPossible,
+ @ApnType String apn, String apnType, LinkProperties linkProperties,
+ NetworkCapabilities networkCapabilities, int networkType, boolean roaming) {
+ try {
+ sRegistry.notifyDataConnectionForSubscriber(slotIndex, subId, state,
+ isDataConnectivityPossible,
+ apn, apnType, linkProperties, networkCapabilities, networkType, roaming);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
+ * Notify {@link CallQuality} change on certain subscription.
+ *
+ * @param subId for which call quality state changed.
+ * @param slotIndex for which call quality state changed. Can be derived from subId except when
+ * subId is invalid.
+ * @param callQuality Information about call quality e.g, call quality level
+ * @param networkType associated with this data connection. e.g, LTE
+ *
+ * @hide
+ */
+ public void notifyCallQualityChanged(int subId, int slotIndex, CallQuality callQuality,
+ @NetworkType int networkType) {
+ try {
+ sRegistry.notifyCallQualityChanged(callQuality, slotIndex, subId, networkType);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
+ * Notify emergency number list changed on certain subscription.
+ *
+ * @param subId for which emergency number list changed.
+ * @param slotIndex for which emergency number list changed. Can be derived from subId except
+ * when subId is invalid.
+ *
+ * @hide
+ */
+ public void notifyEmergencyNumberList(int subId, int slotIndex) {
+ try {
+ sRegistry.notifyEmergencyNumberList(slotIndex, subId);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
+ * Notify radio power state changed on certain subscription.
+ *
+ * @param subId for which radio power state changed.
+ * @param slotIndex for which radio power state changed. Can be derived from subId except when
+ * subId is invalid.
+ * @param radioPowerState the current modem radio state.
+ *
+ * @hide
+ */
+ public void notifyRadioPowerStateChanged(int subId, int slotIndex,
+ @RadioPowerState int radioPowerState) {
+ try {
+ sRegistry.notifyRadioPowerStateChanged(slotIndex, subId, radioPowerState);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
+ * Notify {@link PhoneCapability} changed.
+ *
+ * @param phoneCapability the capability of the modem group.
+ *
+ * @hide
+ */
+ public void notifyPhoneCapabilityChanged(PhoneCapability phoneCapability) {
+ try {
+ sRegistry.notifyPhoneCapabilityChanged(phoneCapability);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
+ * Notify data activation state changed on certain subscription.
+ * @see TelephonyManager#getDataActivationState()
+ *
+ * @param subId for which data activation state changed.
+ * @param slotIndex for which data activation state changed. Can be derived from subId except
+ * when subId is invalid.
+ * @param activationState sim activation state e.g, activated.
+ *
+ * @hide
+ */
+ public void notifyDataActivationStateChanged(int subId, int slotIndex,
+ @SimActivationState int activationState) {
+ try {
+ sRegistry.notifySimActivationStateChangedForPhoneId(slotIndex, subId,
+ TelephonyManager.SIM_ACTIVATION_TYPE_DATA, activationState);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
+ * Notify voice activation state changed on certain subscription.
+ * @see TelephonyManager#getVoiceActivationState()
+ *
+ * @param subId for which voice activation state changed.
+ * @param slotIndex for which voice activation state changed. Can be derived from subId except
+ * subId is invalid.
+ * @param activationState sim activation state e.g, activated.
+ *
+ * @hide
+ */
+ public void notifyVoiceActivationStateChanged(int subId, int slotIndex,
+ @SimActivationState int activationState) {
+ try {
+ sRegistry.notifySimActivationStateChangedForPhoneId(slotIndex, subId,
+ TelephonyManager.SIM_ACTIVATION_TYPE_VOICE, activationState);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
+ * Notify User mobile data state changed on certain subscription. e.g, mobile data is enabled
+ * or disabled.
+ *
+ * @param subId for which mobile data state has changed.
+ * @param slotIndex for which mobile data state has changed. Can be derived from subId except
+ * when subId is invalid.
+ * @param state {@code true} indicates mobile data is enabled/on. {@code false} otherwise.
+ *
+ * @hide
+ */
+ public void notifyUserMobileDataStateChanged(int slotIndex, int subId, boolean state) {
+ try {
+ sRegistry.notifyUserMobileDataStateChangedForPhoneId(slotIndex, subId, state);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
+ * TODO: this is marked as deprecated, can we move this one safely?
+ *
+ * @param subId
+ * @param slotIndex
+ * @param rawData
+ *
+ * @hide
+ */
+ public void notifyOemHookRawEventForSubscriber(int subId, int slotIndex, byte[] rawData) {
+ try {
+ sRegistry.notifyOemHookRawEventForSubscriber(slotIndex, subId, rawData);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
+ * Notify IMS call disconnect causes which contains {@link android.telephony.ims.ImsReasonInfo}.
+ *
+ * @param subId for which ims call disconnect.
+ * @param imsReasonInfo the reason for ims call disconnect.
+ *
+ * @hide
+ */
+ public void notifyImsDisconnectCause(int subId, ImsReasonInfo imsReasonInfo) {
+ try {
+ sRegistry.notifyImsDisconnectCause(subId, imsReasonInfo);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
+ * Notify precise data connection failed cause on certain subscription.
+ *
+ * @param subId for which data connection failed.
+ * @param slotIndex for which data conenction failed. Can be derived from subId except when
+ * subId is invalid.
+ * @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN.
+ * @param apn the APN {@link ApnSetting#getApnName()} of this data connection.
+ * @param failCause data fail cause.
+ *
+ * @hide
+ */
+ public void notifyPreciseDataConnectionFailed(int subId, int slotIndex, String apnType,
+ String apn, @DataFailureCause int failCause) {
+ try {
+ sRegistry.notifyPreciseDataConnectionFailed(slotIndex, subId, apnType, apn, failCause);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
+ * Notify single Radio Voice Call Continuity (SRVCC) state change for the currently active call
+ * on certain subscription.
+ *
+ * @param subId for which srvcc state changed.
+ * @param state srvcc state
+ *
+ * @hide
+ */
+ public void notifySrvccStateChanged(int subId, @SrvccState int state) {
+ try {
+ sRegistry.notifySrvccStateChanged(subId, state);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
+ * Notify over the air sim provisioning(OTASP) mode changed on certain subscription.
+ *
+ * @param subId for which otasp mode changed.
+ * @param otaspMode latest mode for OTASP e.g, OTASP needed.
+ *
+ * @hide
+ */
+ public void notifyOtaspChanged(int subId, int otaspMode) {
+ try {
+ sRegistry.notifyOtaspChanged(subId, otaspMode);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
+ * Notify precise call state changed on certain subscription, including foreground, background
+ * and ringcall states.
+ *
+ * @param subId for which precise call state changed.
+ * @param slotIndex for which precise call state changed. Can be derived from subId except when
+ * subId is invalid.
+ * @param ringCallPreciseState ringCall state.
+ * @param foregroundCallPreciseState foreground call state.
+ * @param backgroundCallPreciseState background call state.
+ *
+ * @hide
+ */
+ public void notifyPreciseCallState(int subId, int slotIndex,
+ @PreciseCallStates int ringCallPreciseState,
+ @PreciseCallStates int foregroundCallPreciseState,
+ @PreciseCallStates int backgroundCallPreciseState) {
+ try {
+ sRegistry.notifyPreciseCallState(slotIndex, subId, ringCallPreciseState,
+ foregroundCallPreciseState, backgroundCallPreciseState);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
+ * Notify call disconnect causes which contains {@link DisconnectCause} and {@link
+ * android.telephony.PreciseDisconnectCause}.
+ *
+ * @param subId for which call disconnected.
+ * @param slotIndex for which call disconnected. Can be derived from subId except when subId is
+ * invalid.
+ * @param cause {@link DisconnectCause} for the disconnected call.
+ * @param preciseCause {@link android.telephony.PreciseDisconnectCause} for the disconnected
+ * call.
+ *
+ * @hide
+ */
+ public void notifyDisconnectCause(int slotIndex, int subId, int cause, int preciseCause) {
+ try {
+ sRegistry.notifyDisconnectCause(slotIndex, subId, cause, preciseCause);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
+ * Notify data connection failed on certain subscription.
+ *
+ * @param subId for which data connection failed.
+ * @param slotIndex for which data conenction faled. Can be derived from subId except when subId
+ * is invalid.
+ * @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN. Note each data
+ * connection can support multiple anyTypes.
+ *
+ * @hide
+ */
+ public void notifyDataConnectionFailed(int subId, int slotIndex, String apnType) {
+ try {
+ sRegistry.notifyDataConnectionFailedForSubscriber(slotIndex, subId, apnType);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
+ * TODO change from bundle to CellLocation?
+ * @hide
+ */
+ public void notifyCellLocation(int subId, Bundle cellLocation) {
+ try {
+ sRegistry.notifyCellLocationForSubscriber(subId, cellLocation);
+ } catch (RemoteException ex) {
+ // system process is dead
+ }
+ }
+
+ /**
+ * Notify {@link CellInfo} changed on certain subscription. e.g, when an observed cell info has
+ * changed or new cells have been added or removed on the given subscription.
+ *
+ * @param subId for which cellinfo changed.
+ * @param cellInfo A list of cellInfo associated with the given subscription.
+ *
+ * @hide
+ */
+ public void notifyCellInfoChanged(int subId, List<CellInfo> cellInfo) {
+ try {
+ sRegistry.notifyCellInfoForSubscriber(subId, cellInfo);
+ } catch (RemoteException ex) {
+
+ }
+ }
+
+ /**
+ * @param activeDataSubId
+ * @hide
+ */
+ public void notifyActiveDataSubIdChanged(int activeDataSubId) {
+ try {
+ sRegistry.notifyActiveDataSubIdChanged(activeDataSubId);
+ } catch (RemoteException ex) {
+
+ }
+ }
+}
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 81643e90428f..5bda86704d88 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -36,6 +36,7 @@ import android.os.Parcelable;
import android.sysprop.DisplayProperties;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.AccessibilityClickableSpan;
+import android.text.style.AccessibilityReplacementSpan;
import android.text.style.AccessibilityURLSpan;
import android.text.style.AlignmentSpan;
import android.text.style.BackgroundColorSpan;
@@ -735,6 +736,8 @@ public class TextUtils {
/** @hide */
public static final int LINE_HEIGHT_SPAN = 28;
/** @hide */
+ public static final int ACCESSIBILITY_REPLACEMENT_SPAN = 29;
+ /** @hide */
public static final int LAST_SPAN = LINE_HEIGHT_SPAN;
/**
@@ -860,7 +863,7 @@ public class TextUtils {
case LEADING_MARGIN_SPAN:
readSpan(p, sp, new LeadingMarginSpan.Standard(p));
- break;
+ break;
case URL_SPAN:
readSpan(p, sp, new URLSpan(p));
@@ -933,7 +936,11 @@ public class TextUtils {
case LINE_HEIGHT_SPAN:
readSpan(p, sp, new LineHeightSpan.Standard(p));
break;
-
+
+ case ACCESSIBILITY_REPLACEMENT_SPAN:
+ readSpan(p, sp, new AccessibilityReplacementSpan(p));
+ break;
+
default:
throw new RuntimeException("bogus span encoding " + kind);
}
diff --git a/core/java/android/text/style/AccessibilityReplacementSpan.java b/core/java/android/text/style/AccessibilityReplacementSpan.java
new file mode 100644
index 000000000000..07b0975846aa
--- /dev/null
+++ b/core/java/android/text/style/AccessibilityReplacementSpan.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.style;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.ParcelableSpan;
+import android.text.TextUtils;
+
+/**
+ * This class serves as a parcelable placeholder for the ReplacementSpans.
+ *
+ * This span contains content description of original span to let Accessibility service to do the
+ * substitution for it.
+ *
+ * @hide
+ */
+public class AccessibilityReplacementSpan extends ReplacementSpan
+ implements ParcelableSpan {
+ // The content description of the span this one replaces
+ private CharSequence mContentDescription;
+
+ /**
+ * @param contentDescription The content description of the span this one replaces
+ */
+ public AccessibilityReplacementSpan(CharSequence contentDescription) {
+ this.setContentDescription(contentDescription);
+ mContentDescription = contentDescription;
+ }
+
+ public AccessibilityReplacementSpan(Parcel p) {
+ mContentDescription = p.readCharSequence();
+ }
+
+ @Override
+ public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ @Override
+ public int getSpanTypeIdInternal() {
+ return TextUtils.ACCESSIBILITY_REPLACEMENT_SPAN;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ @Override
+ public void writeToParcelInternal(Parcel dest, int flags) {
+ dest.writeCharSequence(mContentDescription);
+ }
+
+ @Override
+ public int getSize(Paint paint, CharSequence text, int start, int end,
+ Paint.FontMetricsInt fm) {
+ return 0;
+ }
+
+ @Override
+ public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y,
+ int bottom, Paint paint) {
+ }
+
+ public static final @android.annotation.NonNull
+ Parcelable.Creator<AccessibilityReplacementSpan> CREATOR =
+ new Parcelable.Creator<AccessibilityReplacementSpan>() {
+ @Override
+ public AccessibilityReplacementSpan createFromParcel(Parcel parcel) {
+ return new AccessibilityReplacementSpan(parcel);
+ }
+
+ @Override
+ public AccessibilityReplacementSpan[] newArray(int size) {
+ return new AccessibilityReplacementSpan[size];
+ }
+ };
+}
diff --git a/core/java/android/text/style/ReplacementSpan.java b/core/java/android/text/style/ReplacementSpan.java
index 5f94ad054b63..05532326cd84 100644
--- a/core/java/android/text/style/ReplacementSpan.java
+++ b/core/java/android/text/style/ReplacementSpan.java
@@ -25,6 +25,8 @@ import android.text.TextPaint;
public abstract class ReplacementSpan extends MetricAffectingSpan {
+ private CharSequence mContentDescription = null;
+
/**
* Returns the width of the span. Extending classes can set the height of the span by updating
* attributes of {@link android.graphics.Paint.FontMetricsInt}. If the span covers the whole
@@ -61,6 +63,27 @@ public abstract class ReplacementSpan extends MetricAffectingSpan {
int top, int y, int bottom, @NonNull Paint paint);
/**
+ * Gets a brief description of this ImageSpan for use in accessibility support.
+ *
+ * @return The content description.
+ */
+ @Nullable
+ public CharSequence getContentDescription() {
+ return mContentDescription;
+ }
+
+ /**
+ * Sets the specific content description into ImageSpan.
+ * ReplacementSpans are shared with accessibility services,
+ * but only the content description is available from them.
+ *
+ * @param contentDescription content description. The default value is null.
+ */
+ public void setContentDescription(@Nullable CharSequence contentDescription) {
+ mContentDescription = contentDescription;
+ }
+
+ /**
* This method does nothing, since ReplacementSpans are measured
* explicitly instead of affecting Paint properties.
*/
diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java
index 44c5af20d891..4dda709f285d 100644
--- a/core/java/android/util/ArraySet.java
+++ b/core/java/android/util/ArraySet.java
@@ -16,6 +16,7 @@
package android.util;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
@@ -329,6 +330,18 @@ public final class ArraySet<E> implements Collection<E>, Set<E> {
}
/**
+ * Create a new ArraySet with items from the given array
+ */
+ public ArraySet(@Nullable E[] array) {
+ this();
+ if (array != null) {
+ for (E value : array) {
+ add(value);
+ }
+ }
+ }
+
+ /**
* Make the array map empty. All storage is released.
*/
@Override
diff --git a/core/java/android/util/DebugUtils.java b/core/java/android/util/DebugUtils.java
index 20e0d14ac14d..bc5edf89b4b0 100644
--- a/core/java/android/util/DebugUtils.java
+++ b/core/java/android/util/DebugUtils.java
@@ -255,7 +255,7 @@ public class DebugUtils {
if (value == 0 && flagsWasZero) {
return constNameWithoutPrefix(prefix, field);
}
- if ((flags & value) == value) {
+ if (value != 0 && (flags & value) == value) {
flags &= ~value;
res.append(constNameWithoutPrefix(prefix, field)).append('|');
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index b66764e81e8f..b1fd4e5e699e 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -41,14 +41,12 @@ 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 USE_BUGREPORT_API = "settings_use_bugreport_api";
private static final Map<String, String> DEFAULT_FLAGS;
static {
DEFAULT_FLAGS = new HashMap<>();
DEFAULT_FLAGS.put("settings_audio_switcher", "true");
- DEFAULT_FLAGS.put("settings_use_bugreport_api", "true");
DEFAULT_FLAGS.put("settings_mobile_network_v2", "true");
DEFAULT_FLAGS.put("settings_network_and_internet_v2", "true");
DEFAULT_FLAGS.put("settings_systemui_theme", "true");
@@ -59,6 +57,7 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put("settings_wifi_details_datausage_header", "false");
DEFAULT_FLAGS.put("settings_skip_direction_mutable", "true");
DEFAULT_FLAGS.put(SETTINGS_WIFITRACKER2, "false");
+ DEFAULT_FLAGS.put("settings_work_profile", "false");
}
/**
diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java
index d3b2c46ff1c3..e78b79695d3d 100644
--- a/core/java/android/util/LongSparseArray.java
+++ b/core/java/android/util/LongSparseArray.java
@@ -16,8 +16,12 @@
package android.util;
+import android.os.Parcel;
+import android.os.Parcelable;
+
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
+import com.android.internal.util.Preconditions;
import libcore.util.EmptyArray;
@@ -442,4 +446,93 @@ public class LongSparseArray<E> implements Cloneable {
buffer.append('}');
return buffer.toString();
}
+
+ /**
+ * @hide
+ */
+ public static class StringParcelling implements com.android.internal.util.Parcelling {
+ @Override
+ public void parcel(Object item, Parcel dest, int parcelFlags) {
+ if (item == null) {
+ dest.writeInt(-1);
+ return;
+ }
+
+ LongSparseArray<String> array = (LongSparseArray<String>) item;
+ dest.writeInt(array.mSize);
+ dest.writeLongArray(array.mKeys);
+ dest.writeStringArray((String[]) array.mValues);
+ }
+
+ @Override
+ public Object unparcel(Parcel source) {
+ int size = source.readInt();
+ if (size == -1) {
+ return null;
+ }
+
+ LongSparseArray<String> array = new LongSparseArray<>(0);
+ array.mSize = size;
+ array.mKeys = source.createLongArray();
+ array.mValues = source.createStringArray();
+
+ // Make sure array is sane
+ Preconditions.checkArgument(array.mKeys.length >= size);
+ Preconditions.checkArgument(array.mValues.length >= size);
+
+ if (size > 0) {
+ long last = array.mKeys[0];
+ for (int i = 1; i < size; i++) {
+ Preconditions.checkArgument(last < array.mKeys[i]);
+ }
+ }
+
+ return array;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static class Parcelling<T extends Parcelable> implements
+ com.android.internal.util.Parcelling {
+ @Override
+ public void parcel(Object item, Parcel dest, int parcelFlags) {
+ if (item == null) {
+ dest.writeInt(-1);
+ return;
+ }
+
+ LongSparseArray<T> array = (LongSparseArray<T>) item;
+ dest.writeInt(array.mSize);
+ dest.writeLongArray(array.mKeys);
+ dest.writeParcelableArray((T[]) array.mValues, parcelFlags);
+ }
+
+ @Override
+ public Object unparcel(Parcel source) {
+ int size = source.readInt();
+ if (size == -1) {
+ return null;
+ }
+
+ LongSparseArray<T> array = new LongSparseArray<>(0);
+ array.mSize = size;
+ array.mKeys = source.createLongArray();
+ array.mValues = source.readParcelableArray(null);
+
+ // Make sure array is sane
+ Preconditions.checkArgument(array.mKeys.length >= size);
+ Preconditions.checkArgument(array.mValues.length >= size);
+
+ if (size > 0) {
+ long last = array.mKeys[0];
+ for (int i = 1; i < size; i++) {
+ Preconditions.checkArgument(last < array.mKeys[i]);
+ }
+ }
+
+ return array;
+ }
+ }
}
diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java
index 7b7eea09e884..9ffd4f05bd47 100644
--- a/core/java/android/util/LongSparseLongArray.java
+++ b/core/java/android/util/LongSparseLongArray.java
@@ -17,9 +17,11 @@
package android.util;
import android.annotation.UnsupportedAppUsage;
+import android.os.Parcel;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
+import com.android.internal.util.Preconditions;
import libcore.util.EmptyArray;
@@ -283,4 +285,49 @@ public class LongSparseLongArray implements Cloneable {
buffer.append('}');
return buffer.toString();
}
+
+ /**
+ * @hide
+ */
+ public static class Parcelling implements com.android.internal.util.Parcelling {
+ @Override
+ public void parcel(Object item, Parcel dest, int parcelFlags) {
+ if (item == null) {
+ dest.writeInt(-1);
+ return;
+ }
+
+ LongSparseLongArray array = (LongSparseLongArray) item;
+ dest.writeInt(array.mSize);
+ dest.writeLongArray(array.mKeys);
+ dest.writeLongArray(array.mValues);
+ }
+
+ @Override
+ public Object unparcel(Parcel source) {
+ int size = source.readInt();
+ if (size == -1) {
+ return null;
+ }
+
+ LongSparseLongArray array = new LongSparseLongArray(0);
+
+ array.mSize = size;
+ array.mKeys = source.createLongArray();
+ array.mValues = source.createLongArray();
+
+ // Make sure array is sane
+ Preconditions.checkArgument(array.mKeys.length >= size);
+ Preconditions.checkArgument(array.mValues.length >= size);
+
+ if (size > 0) {
+ long last = array.mKeys[0];
+ for (int i = 1; i < size; i++) {
+ Preconditions.checkArgument(last < array.mKeys[i]);
+ }
+ }
+
+ return array;
+ }
+ }
}
diff --git a/core/java/android/util/OWNERS b/core/java/android/util/OWNERS
index 98297fba8ef3..8f3d9f6f5881 100644
--- a/core/java/android/util/OWNERS
+++ b/core/java/android/util/OWNERS
@@ -1,3 +1,3 @@
per-file FeatureFlagUtils.java = sbasi@google.com
-per-file FeatureFlagUtils.java = zhfan@google.com
+per-file FeatureFlagUtils.java = tmfang@google.com
per-file FeatureFlagUtils.java = asapperstein@google.com
diff --git a/core/java/android/util/StatsEvent.java b/core/java/android/util/StatsEvent.java
new file mode 100644
index 000000000000..91a5ec0303f7
--- /dev/null
+++ b/core/java/android/util/StatsEvent.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * StatsEvent builds and stores the buffer sent over the statsd socket.
+ * This class defines and encapsulates the socket protocol.
+ * @hide
+ **/
+public final class StatsEvent implements AutoCloseable {
+ private static final int POS_NUM_ELEMENTS = 1;
+ private static final int POS_TIMESTAMP = POS_NUM_ELEMENTS + 1;
+
+ private static final int LOGGER_ENTRY_MAX_PAYLOAD = 4068;
+
+ // Max payload size is 4 KB less 4 bytes which are reserved for statsEventTag.
+ // See android_util_StatsLog.cpp.
+ private static final int MAX_EVENT_PAYLOAD = LOGGER_ENTRY_MAX_PAYLOAD - 4;
+
+ private static final byte INT_TYPE = 0;
+ private static final byte LONG_TYPE = 1;
+ private static final byte STRING_TYPE = 2;
+ private static final byte LIST_TYPE = 3;
+ private static final byte FLOAT_TYPE = 4;
+
+ private static final int INT_TYPE_SIZE = 5;
+ private static final int FLOAT_TYPE_SIZE = 5;
+ private static final int LONG_TYPE_SIZE = 9;
+
+ private static final int STRING_TYPE_OVERHEAD = 5;
+ private static final int LIST_TYPE_OVERHEAD = 2;
+
+ public static final int SUCCESS = 0;
+ public static final int ERROR_BUFFER_LIMIT_EXCEEDED = -1;
+ public static final int ERROR_NO_TIMESTAMP = -2;
+ public static final int ERROR_TIMESTAMP_ALREADY_WRITTEN = -3;
+ public static final int ERROR_NO_ATOM_ID = -4;
+ public static final int ERROR_ATOM_ID_ALREADY_WRITTEN = -5;
+ public static final int ERROR_UID_TAG_COUNT_MISMATCH = -6;
+
+ private static Object sLock = new Object();
+
+ @GuardedBy("sLock")
+ private static StatsEvent sPool;
+
+ private final byte[] mBuffer = new byte[MAX_EVENT_PAYLOAD];
+ private int mPos;
+ private int mNumElements;
+ private int mAtomId;
+
+ private StatsEvent() {
+ // Write LIST_TYPE to buffer
+ mBuffer[0] = LIST_TYPE;
+ reset();
+ }
+
+ private void reset() {
+ // Reset state.
+ mPos = POS_TIMESTAMP;
+ mNumElements = 0;
+ mAtomId = 0;
+ }
+
+ /**
+ * Returns a StatsEvent object from the pool.
+ **/
+ @NonNull
+ public static StatsEvent obtain() {
+ final StatsEvent statsEvent;
+ synchronized (sLock) {
+ statsEvent = null == sPool ? new StatsEvent() : sPool;
+ sPool = null;
+ }
+ statsEvent.reset();
+ return statsEvent;
+ }
+
+ @Override
+ public void close() {
+ synchronized (sLock) {
+ if (null == sPool) {
+ sPool = this;
+ }
+ }
+ }
+
+ /**
+ * Writes the event timestamp to the buffer.
+ **/
+ public int writeTimestampNs(final long timestampNs) {
+ if (hasTimestamp()) {
+ return ERROR_TIMESTAMP_ALREADY_WRITTEN;
+ }
+ return writeLong(timestampNs);
+ }
+
+ private boolean hasTimestamp() {
+ return mPos > POS_TIMESTAMP;
+ }
+
+ private boolean hasAtomId() {
+ return mAtomId != 0;
+ }
+
+ /**
+ * Writes the atom id to the buffer.
+ **/
+ public int writeAtomId(final int atomId) {
+ if (!hasTimestamp()) {
+ return ERROR_NO_TIMESTAMP;
+ } else if (hasAtomId()) {
+ return ERROR_ATOM_ID_ALREADY_WRITTEN;
+ }
+
+ final int writeResult = writeInt(atomId);
+ if (SUCCESS == writeResult) {
+ mAtomId = atomId;
+ }
+ return writeResult;
+ }
+
+ /**
+ * Appends the given int to the StatsEvent buffer.
+ **/
+ public int writeInt(final int value) {
+ if (!hasTimestamp()) {
+ return ERROR_NO_TIMESTAMP;
+ } else if (!hasAtomId()) {
+ return ERROR_NO_ATOM_ID;
+ } else if (mPos + INT_TYPE_SIZE > MAX_EVENT_PAYLOAD) {
+ return ERROR_BUFFER_LIMIT_EXCEEDED;
+ }
+
+ mBuffer[mPos] = INT_TYPE;
+ copyInt(mBuffer, mPos + 1, value);
+ mPos += INT_TYPE_SIZE;
+ mNumElements++;
+ return SUCCESS;
+ }
+
+ /**
+ * Appends the given long to the StatsEvent buffer.
+ **/
+ public int writeLong(final long value) {
+ if (!hasTimestamp()) {
+ return ERROR_NO_TIMESTAMP;
+ } else if (!hasAtomId()) {
+ return ERROR_NO_ATOM_ID;
+ } else if (mPos + LONG_TYPE_SIZE > MAX_EVENT_PAYLOAD) {
+ return ERROR_BUFFER_LIMIT_EXCEEDED;
+ }
+
+ mBuffer[mPos] = LONG_TYPE;
+ copyLong(mBuffer, mPos + 1, value);
+ mPos += LONG_TYPE_SIZE;
+ mNumElements++;
+ return SUCCESS;
+ }
+
+ /**
+ * Appends the given float to the StatsEvent buffer.
+ **/
+ public int writeFloat(final float value) {
+ if (!hasTimestamp()) {
+ return ERROR_NO_TIMESTAMP;
+ } else if (!hasAtomId()) {
+ return ERROR_NO_ATOM_ID;
+ } else if (mPos + FLOAT_TYPE_SIZE > MAX_EVENT_PAYLOAD) {
+ return ERROR_BUFFER_LIMIT_EXCEEDED;
+ }
+
+ mBuffer[mPos] = FLOAT_TYPE;
+ copyInt(mBuffer, mPos + 1, Float.floatToIntBits(value));
+ mPos += FLOAT_TYPE_SIZE;
+ mNumElements++;
+ return SUCCESS;
+ }
+
+ /**
+ * Appends the given boolean to the StatsEvent buffer.
+ **/
+ public int writeBoolean(final boolean value) {
+ return writeInt(value ? 1 : 0);
+ }
+
+ /**
+ * Appends the given byte array to the StatsEvent buffer.
+ **/
+ public int writeByteArray(@NonNull final byte[] value) {
+ if (!hasTimestamp()) {
+ return ERROR_NO_TIMESTAMP;
+ } else if (!hasAtomId()) {
+ return ERROR_NO_ATOM_ID;
+ } else if (mPos + STRING_TYPE_OVERHEAD + value.length > MAX_EVENT_PAYLOAD) {
+ return ERROR_BUFFER_LIMIT_EXCEEDED;
+ }
+
+ mBuffer[mPos] = STRING_TYPE;
+ copyInt(mBuffer, mPos + 1, value.length);
+ System.arraycopy(value, 0, mBuffer, mPos + STRING_TYPE_OVERHEAD, value.length);
+ mPos += STRING_TYPE_OVERHEAD + value.length;
+ mNumElements++;
+ return SUCCESS;
+ }
+
+ /**
+ * Appends the given String to the StatsEvent buffer.
+ **/
+ public int writeString(@NonNull final String value) {
+ final byte[] valueBytes = stringToBytes(value);
+ return writeByteArray(valueBytes);
+ }
+
+ /**
+ * Appends the AttributionNode specified as array of uids and array of tags.
+ **/
+ public int writeAttributionNode(@NonNull final int[] uids, @NonNull final String[] tags) {
+ if (!hasTimestamp()) {
+ return ERROR_NO_TIMESTAMP;
+ } else if (!hasAtomId()) {
+ return ERROR_NO_ATOM_ID;
+ } else if (mPos + LIST_TYPE_OVERHEAD > MAX_EVENT_PAYLOAD) {
+ return ERROR_BUFFER_LIMIT_EXCEEDED;
+ }
+
+ final int numTags = tags.length;
+ final int numUids = uids.length;
+ if (numTags != numUids) {
+ return ERROR_UID_TAG_COUNT_MISMATCH;
+ }
+
+ int pos = mPos;
+ mBuffer[pos] = LIST_TYPE;
+ mBuffer[pos + 1] = (byte) numTags;
+ pos += LIST_TYPE_OVERHEAD;
+ for (int i = 0; i < numTags; i++) {
+ final byte[] tagBytes = stringToBytes(tags[i]);
+
+ if (pos + LIST_TYPE_OVERHEAD + INT_TYPE_SIZE
+ + STRING_TYPE_OVERHEAD + tagBytes.length > MAX_EVENT_PAYLOAD) {
+ return ERROR_BUFFER_LIMIT_EXCEEDED;
+ }
+
+ mBuffer[pos] = LIST_TYPE;
+ mBuffer[pos + 1] = 2;
+ pos += LIST_TYPE_OVERHEAD;
+ mBuffer[pos] = INT_TYPE;
+ copyInt(mBuffer, pos + 1, uids[i]);
+ pos += INT_TYPE_SIZE;
+ mBuffer[pos] = STRING_TYPE;
+ copyInt(mBuffer, pos + 1, tagBytes.length);
+ System.arraycopy(tagBytes, 0, mBuffer, pos + STRING_TYPE_OVERHEAD, tagBytes.length);
+ pos += STRING_TYPE_OVERHEAD + tagBytes.length;
+ }
+ mPos = pos;
+ mNumElements++;
+ return SUCCESS;
+ }
+
+ /**
+ * Returns the byte array containing data in the statsd socket format.
+ * @hide
+ **/
+ @NonNull
+ public byte[] getBuffer() {
+ // Encode number of elements in the buffer.
+ mBuffer[POS_NUM_ELEMENTS] = (byte) mNumElements;
+ return mBuffer;
+ }
+
+ /**
+ * Returns number of bytes used by the buffer.
+ * @hide
+ **/
+ public int size() {
+ return mPos;
+ }
+
+ /**
+ * Getter for atom id.
+ * @hide
+ **/
+ public int getAtomId() {
+ return mAtomId;
+ }
+
+ @NonNull
+ private static byte[] stringToBytes(@Nullable final String value) {
+ return (null == value ? "" : value).getBytes(UTF_8);
+ }
+
+ // Helper methods for copying primitives
+ private static void copyInt(@NonNull byte[] buff, int pos, int value) {
+ buff[pos] = (byte) (value);
+ buff[pos + 1] = (byte) (value >> 8);
+ buff[pos + 2] = (byte) (value >> 16);
+ buff[pos + 3] = (byte) (value >> 24);
+ }
+
+ private static void copyLong(@NonNull byte[] buff, int pos, long value) {
+ buff[pos] = (byte) (value);
+ buff[pos + 1] = (byte) (value >> 8);
+ buff[pos + 2] = (byte) (value >> 16);
+ buff[pos + 3] = (byte) (value >> 24);
+ buff[pos + 4] = (byte) (value >> 32);
+ buff[pos + 5] = (byte) (value >> 40);
+ buff[pos + 6] = (byte) (value >> 48);
+ buff[pos + 7] = (byte) (value >> 56);
+ }
+}
diff --git a/core/java/android/util/TimestampedValue.java b/core/java/android/util/TimestampedValue.java
index 1289e4db0743..45056730b08b 100644
--- a/core/java/android/util/TimestampedValue.java
+++ b/core/java/android/util/TimestampedValue.java
@@ -19,6 +19,7 @@ package android.util;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
+import android.os.Parcelable;
import android.os.SystemClock;
import java.util.Objects;
@@ -30,14 +31,14 @@ import java.util.Objects;
* If a suitable clock is used the reference time can be used to identify the age of a value or
* ordering between values.
*
- * <p>To read and write a timestamped value from / to a Parcel see
- * {@link #readFromParcel(Parcel, ClassLoader, Class)} and
- * {@link #writeToParcel(Parcel, TimestampedValue)}.
+ * <p>This class implements {@link Parcelable} for convenience but instances will only actually be
+ * parcelable if the value type held is {@code null}, {@link Parcelable}, or one of the other types
+ * supported by {@link Parcel#writeValue(Object)} / {@link Parcel#readValue(ClassLoader)}.
*
* @param <T> the type of the value with an associated timestamp
* @hide
*/
-public final class TimestampedValue<T> {
+public final class TimestampedValue<T> implements Parcelable {
private final long mReferenceTimeMillis;
private final T mValue;
@@ -81,57 +82,43 @@ public final class TimestampedValue<T> {
}
/**
- * Read a {@link TimestampedValue} from a parcel that was stored using
- * {@link #writeToParcel(Parcel, TimestampedValue)}.
- *
- * <p>The marshalling/unmarshalling of the value relies upon {@link Parcel#writeValue(Object)}
- * and {@link Parcel#readValue(ClassLoader)} and so this method can only be used with types
- * supported by those methods.
- *
- * @param in the Parcel to read from
- * @param classLoader the ClassLoader to pass to {@link Parcel#readValue(ClassLoader)}
- * @param valueClass the expected type of the value, typically the same as {@code <T>} but can
- * also be a subclass
- * @throws RuntimeException if the value read is not compatible with {@code valueClass} or the
- * object could not be read
- */
- @SuppressWarnings("unchecked")
- @NonNull
- public static <T> TimestampedValue<T> readFromParcel(
- @NonNull Parcel in, @Nullable ClassLoader classLoader, Class<? extends T> valueClass) {
- long referenceTimeMillis = in.readLong();
- T value = (T) in.readValue(classLoader);
- // Equivalent to static code: if (!(value.getClass() instanceof {valueClass})) {
- if (value != null && !valueClass.isAssignableFrom(value.getClass())) {
- throw new RuntimeException("Value was of type " + value.getClass()
- + " is not assignable to " + valueClass);
- }
- return new TimestampedValue<>(referenceTimeMillis, value);
- }
-
- /**
- * Write a {@link TimestampedValue} to a parcel so that it can be read using
- * {@link #readFromParcel(Parcel, ClassLoader, Class)}.
- *
- * <p>The marshalling/unmarshalling of the value relies upon {@link Parcel#writeValue(Object)}
- * and {@link Parcel#readValue(ClassLoader)} and so this method can only be used with types
- * supported by those methods.
- *
- * @param dest the Parcel
- * @param timestampedValue the value
- * @throws RuntimeException if the value could not be written to the Parcel
- */
- public static void writeToParcel(
- @NonNull Parcel dest, @NonNull TimestampedValue<?> timestampedValue) {
- dest.writeLong(timestampedValue.mReferenceTimeMillis);
- dest.writeValue(timestampedValue.mValue);
- }
-
- /**
* Returns the difference in milliseconds between two instance's reference times.
*/
public static long referenceTimeDifference(
@NonNull TimestampedValue<?> one, @NonNull TimestampedValue<?> two) {
return one.mReferenceTimeMillis - two.mReferenceTimeMillis;
}
+
+ public static final @NonNull Parcelable.Creator<TimestampedValue<?>> CREATOR =
+ new Parcelable.ClassLoaderCreator<TimestampedValue<?>>() {
+
+ @Override
+ public TimestampedValue<?> createFromParcel(@NonNull Parcel source) {
+ return createFromParcel(source, null);
+ }
+
+ @Override
+ public TimestampedValue<?> createFromParcel(
+ @NonNull Parcel source, @Nullable ClassLoader classLoader) {
+ long referenceTimeMillis = source.readLong();
+ Object value = source.readValue(classLoader);
+ return new TimestampedValue<>(referenceTimeMillis, value);
+ }
+
+ @Override
+ public TimestampedValue[] newArray(int size) {
+ return new TimestampedValue[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mReferenceTimeMillis);
+ dest.writeValue(mValue);
+ }
}
diff --git a/core/java/android/view/CompositionSamplingListener.java b/core/java/android/view/CompositionSamplingListener.java
index 368445cde72c..677a559cd3b0 100644
--- a/core/java/android/view/CompositionSamplingListener.java
+++ b/core/java/android/view/CompositionSamplingListener.java
@@ -28,7 +28,7 @@ import java.util.concurrent.Executor;
*/
public abstract class CompositionSamplingListener {
- private final long mNativeListener;
+ private long mNativeListener;
private final Executor mExecutor;
public CompositionSamplingListener(Executor executor) {
@@ -36,13 +36,19 @@ public abstract class CompositionSamplingListener {
mNativeListener = nativeCreate(this);
}
+ public void destroy() {
+ if (mNativeListener == 0) {
+ return;
+ }
+ unregister(this);
+ nativeDestroy(mNativeListener);
+ mNativeListener = 0;
+ }
+
@Override
protected void finalize() throws Throwable {
try {
- if (mNativeListener != 0) {
- unregister(this);
- nativeDestroy(mNativeListener);
- }
+ destroy();
} finally {
super.finalize();
}
@@ -58,6 +64,9 @@ public abstract class CompositionSamplingListener {
*/
public static void register(CompositionSamplingListener listener,
int displayId, SurfaceControl stopLayer, Rect samplingArea) {
+ if (listener.mNativeListener == 0) {
+ return;
+ }
Preconditions.checkArgument(displayId == Display.DEFAULT_DISPLAY,
"default display only for now");
long nativeStopLayerObject = stopLayer != null ? stopLayer.mNativeObject : 0;
@@ -69,6 +78,9 @@ public abstract class CompositionSamplingListener {
* Unregisters a sampling listener.
*/
public static void unregister(CompositionSamplingListener listener) {
+ if (listener.mNativeListener == 0) {
+ return;
+ }
nativeUnregister(listener.mNativeListener);
}
diff --git a/core/java/android/view/DragAndDropPermissions.java b/core/java/android/view/DragAndDropPermissions.java
index e72ff38ce3f0..d47604d6ec41 100644
--- a/core/java/android/view/DragAndDropPermissions.java
+++ b/core/java/android/view/DragAndDropPermissions.java
@@ -37,7 +37,7 @@ import com.android.internal.view.IDragAndDropPermissions;
* View.startDragAndDrop} by the app that started the drag operation.
* </p>
* <p>
- * The life cycle of the permissions is bound to the activity used to call {@link
+ * The lifecycle of the permissions is bound to the activity used to call {@link
* android.app.Activity#requestDragAndDropPermissions(DragEvent) requestDragAndDropPermissions}. The
* permissions are revoked when this activity is destroyed, or when {@link #release()} is called,
* whichever occurs first.
@@ -49,6 +49,10 @@ import com.android.internal.view.IDragAndDropPermissions;
* {@link Activity#onSaveInstanceState} bundle and later retrieved in order to manually release
* the permissions once they are no longer needed.
* </p>
+ * <p>
+ * Learn more about <a href="/guide/topics/ui/drag-drop#DragPermissionsMultiWindow">drag permissions
+ * in multi-window mode</a>.
+ * </p>
*/
public final class DragAndDropPermissions implements Parcelable {
diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedStackListener.aidl
index 806d81e39cca..f4bee575f811 100644
--- a/core/java/android/view/IPinnedStackListener.aidl
+++ b/core/java/android/view/IPinnedStackListener.aidl
@@ -55,14 +55,6 @@ oneway interface IPinnedStackListener {
void onImeVisibilityChanged(boolean imeVisible, int imeHeight);
/**
- * Called when window manager decides to adjust the pinned stack bounds because of the shelf, or
- * when the listener is first registered to allow the listener to synchronized its state with
- * the controller. This call will always be followed by a onMovementBoundsChanged() call
- * with fromShelfAdjustment set to {@code true}.
- */
- void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight);
-
- /**
* Called when window manager decides to adjust the minimized state, or when the listener
* is first registered to allow the listener to synchronized its state with the controller.
*/
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
index 955be8d40c47..762366eb6295 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -120,4 +120,10 @@ interface IRecentsAnimationController {
* @see IRecentsAnimationRunner#onCancelled
*/
void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot);
+
+ /**
+ * Sets a state for controller to decide which surface is the destination when the recents
+ * animation is cancelled through fail safe mechanism.
+ */
+ void setWillFinishToHome(boolean willFinishToHome);
}
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index f34f9e6d5ce8..8bf99ec4f251 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -73,6 +73,22 @@ oneway interface IWindow {
*/
void insetsControlChanged(in InsetsState insetsState, in InsetsSourceControl[] activeControls);
+ /**
+ * Called when a set of insets source window should be shown by policy.
+ *
+ * @param types internal inset types (WindowInsets.Type.InsetType) to show
+ * @param fromIme true if this request originated from IME (InputMethodService).
+ */
+ void showInsets(int types, boolean fromIme);
+
+ /**
+ * Called when a set of insets source window should be hidden by policy.
+ *
+ * @param types internal inset types (WindowInsets.Type.InsetType) to hide
+ * @param fromIme true if this request originated from IME (InputMethodService).
+ */
+ void hideInsets(int types, boolean fromIme);
+
void moved(int newX, int newY);
void dispatchAppVisibility(boolean visible);
void dispatchGetNewSurface();
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 1c3294858db8..7f717a72b0f9 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -306,6 +306,11 @@ interface IWindowManager
oneway void statusBarVisibilityChanged(int displayId, int visibility);
/**
+ * Called by System UI to notify Window Manager to hide transient bars.
+ */
+ 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
@@ -326,12 +331,6 @@ interface IWindowManager
oneway void setPipVisibility(boolean visible);
/**
- * Called by System UI to notify of changes to the visibility and height of the shelf.
- */
- @UnsupportedAppUsage
- void setShelfHeight(boolean visible, int shelfHeight);
-
- /**
* Called by System UI to enable or disable haptic feedback on the navigation bar buttons.
*/
@UnsupportedAppUsage
@@ -650,4 +649,16 @@ interface IWindowManager
* Enables/disables SurfaceFlinger layer tracing.
*/
void setLayerTracing(boolean enabled);
+
+ /**
+ * Mirrors a specified display. The root of the mirrored hierarchy will be stored in
+ * outSurfaceControl.
+ * Requires the ACCESS_SURFACE_FLINGER permission.
+ *
+ * @param displayId The id of the display to mirror
+ * @param outSurfaceControl The SurfaceControl for the root of the mirrored hierarchy.
+ *
+ * @return true if the display was successfully mirrored.
+ */
+ boolean mirrorDisplay(int displayId, out SurfaceControl outSurfaceControl);
}
diff --git a/core/java/android/view/InputMonitor.java b/core/java/android/view/InputMonitor.java
index bbd27dc45779..ad1f201ba3c1 100644
--- a/core/java/android/view/InputMonitor.java
+++ b/core/java/android/view/InputMonitor.java
@@ -21,6 +21,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
+import com.android.internal.util.DataClass;
+
/**
* An {@code InputMonitor} allows privileged applications and components to monitor streams of
* {@link InputEvent}s without having to be the designated recipient for the event.
@@ -31,57 +33,17 @@ import android.os.RemoteException;
*
* @hide
*/
+@DataClass(genToString = true)
public final class InputMonitor implements Parcelable {
private static final String TAG = "InputMonitor";
private static final boolean DEBUG = false;
- public static final Parcelable.Creator<InputMonitor> CREATOR =
- new Parcelable.Creator<InputMonitor>() {
-
- public InputMonitor createFromParcel(Parcel source) {
- return new InputMonitor(source);
- }
-
- public InputMonitor[] newArray(int size) {
- return new InputMonitor[size];
- }
- };
-
- @NonNull
- private final String mName;
@NonNull
- private final InputChannel mChannel;
+ private final InputChannel mInputChannel;
@NonNull
private final IInputMonitorHost mHost;
- public InputMonitor(@NonNull String name, @NonNull InputChannel channel,
- @NonNull IInputMonitorHost host) {
- mName = name;
- mChannel = channel;
- mHost = host;
- }
-
- public InputMonitor(Parcel in) {
- mName = in.readString();
- mChannel = in.readParcelable(null);
- mHost = IInputMonitorHost.Stub.asInterface(in.readStrongBinder());
- }
-
- /**
- * Get the {@link InputChannel} corresponding to this InputMonitor
- */
- public InputChannel getInputChannel() {
- return mChannel;
- }
-
- /**
- * Get the name of this channel.
- */
- public String getName() {
- return mName;
- }
-
/**
* Takes all of the current pointer events streams that are currently being sent to this
@@ -107,7 +69,7 @@ public final class InputMonitor implements Parcelable {
* no longer be used.
*/
public void dispose() {
- mChannel.dispose();
+ mInputChannel.dispose();
try {
mHost.dispose();
} catch (RemoteException e) {
@@ -115,20 +77,107 @@ public final class InputMonitor implements Parcelable {
}
}
+
+
+ // Code below generated by codegen v1.0.7.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/InputMonitor.java
+
+
+ @DataClass.Generated.Member
+ public InputMonitor(
+ @NonNull InputChannel inputChannel,
+ @NonNull IInputMonitorHost host) {
+ this.mInputChannel = inputChannel;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mInputChannel);
+ this.mHost = host;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mHost);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull InputChannel getInputChannel() {
+ return mInputChannel;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull IInputMonitorHost getHost() {
+ return mHost;
+ }
+
@Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(mName);
- out.writeParcelable(mChannel, flags);
- out.writeStrongBinder(mHost.asBinder());
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "InputMonitor { " +
+ "inputChannel = " + mInputChannel + ", " +
+ "host = " + mHost +
+ " }";
}
@Override
- public int describeContents() {
- return 0;
+ @DataClass.Generated.Member
+ public void writeToParcel(Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeTypedObject(mInputChannel, flags);
+ dest.writeStrongInterface(mHost);
}
@Override
- public String toString() {
- return "InputMonitor{mName=" + mName + ", mChannel=" + mChannel + ", mHost=" + mHost + "}";
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ InputMonitor(Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ InputChannel inputChannel = (InputChannel) in.readTypedObject(InputChannel.CREATOR);
+ IInputMonitorHost host = IInputMonitorHost.Stub.asInterface(in.readStrongBinder());
+
+ this.mInputChannel = inputChannel;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mInputChannel);
+ this.mHost = host;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mHost);
+
+ // onConstructed(); // You can define this method to get a callback
}
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<InputMonitor> CREATOR
+ = new Parcelable.Creator<InputMonitor>() {
+ @Override
+ public InputMonitor[] newArray(int size) {
+ return new InputMonitor[size];
+ }
+
+ @Override
+ public InputMonitor createFromParcel(Parcel in) {
+ return new InputMonitor(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1571177265149L,
+ codegenVersion = "1.0.7",
+ sourceFile = "frameworks/base/core/java/android/view/InputMonitor.java",
+ inputSignatures = "private static final java.lang.String TAG\nprivate static final boolean DEBUG\nprivate final @android.annotation.NonNull android.view.InputChannel mInputChannel\nprivate final @android.annotation.NonNull android.view.IInputMonitorHost mHost\npublic void pilferPointers()\npublic void dispose()\nclass InputMonitor extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true)")
+ @Deprecated
+ private void __metadata() {}
+
}
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 10a9aaaa5b0c..08e4eeb45400 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -38,10 +38,8 @@ public final class InputWindowHandle {
// The input application handle.
public final InputApplicationHandle inputApplicationHandle;
- // The client window.
- public final IWindow clientWindow;
-
- // The token associated with the window.
+ // The token associates input data with a window and its input channel. The client input
+ // channel and the server input channel will both contain this token.
public IBinder token;
// The window name.
@@ -120,10 +118,8 @@ public final class InputWindowHandle {
private native void nativeDispose();
- public InputWindowHandle(InputApplicationHandle inputApplicationHandle,
- IWindow clientWindow, int displayId) {
+ public InputWindowHandle(InputApplicationHandle inputApplicationHandle, int displayId) {
this.inputApplicationHandle = inputApplicationHandle;
- this.clientWindow = clientWindow;
this.displayId = displayId;
}
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 341c2147c64a..0fb1c33df2ab 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -146,20 +146,11 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
}
final Insets offset = Insets.subtract(mShownInsets, mPendingInsets);
ArrayList<SurfaceParams> params = new ArrayList<>();
- if (offset.left != 0) {
- updateLeashesForSide(INSET_SIDE_LEFT, offset.left, mPendingInsets.left, params, state);
- }
- if (offset.top != 0) {
- updateLeashesForSide(INSET_SIDE_TOP, offset.top, mPendingInsets.top, params, state);
- }
- if (offset.right != 0) {
- updateLeashesForSide(INSET_SIDE_RIGHT, offset.right, mPendingInsets.right, params,
- state);
- }
- if (offset.bottom != 0) {
- updateLeashesForSide(INSET_SIDE_BOTTOM, offset.bottom, mPendingInsets.bottom, params,
- state);
- }
+ updateLeashesForSide(INSET_SIDE_LEFT, offset.left, mPendingInsets.left, params, state);
+ updateLeashesForSide(INSET_SIDE_TOP, offset.top, mPendingInsets.top, params, state);
+ updateLeashesForSide(INSET_SIDE_RIGHT, offset.right, mPendingInsets.right, params, state);
+ updateLeashesForSide(INSET_SIDE_BOTTOM, offset.bottom, mPendingInsets.bottom, params,
+ state);
SyncRtSurfaceTransactionApplier applier = mTransactionApplierSupplier.get();
applier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
mCurrentInsets = mPendingInsets;
@@ -224,11 +215,18 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
private void updateLeashesForSide(@InsetSide int side, int offset, int inset,
ArrayList<SurfaceParams> surfaceParams, InsetsState state) {
ArraySet<InsetsSourceConsumer> items = mSideSourceMap.get(side);
+ if (items == null) {
+ return;
+ }
// TODO: Implement behavior when inset spans over multiple types
for (int i = items.size() - 1; i >= 0; i--) {
final InsetsSourceConsumer consumer = items.valueAt(i);
final InsetsSource source = mInitialInsetsState.getSource(consumer.getType());
final InsetsSourceControl control = consumer.getControl();
+ if (control == null) {
+ // Control may not be available for consumer yet or revoked.
+ continue;
+ }
final SurfaceControl leash = consumer.getControl().getLeash();
mTmpMatrix.setTranslate(control.getSurfacePosition().x, control.getSurfacePosition().y);
@@ -270,9 +268,15 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
SparseSetArray<InsetsSourceConsumer> sideSourcesMap,
SparseArray<InsetsSourceConsumer> consumers) {
for (int i = typeSideMap.size() - 1; i >= 0; i--) {
- int type = typeSideMap.keyAt(i);
- int side = typeSideMap.valueAt(i);
- sideSourcesMap.add(side, consumers.get(type));
+ final int type = typeSideMap.keyAt(i);
+ final int side = typeSideMap.valueAt(i);
+ final InsetsSourceConsumer consumer = consumers.get(type);
+ if (consumer == null) {
+ // If the types that we are controlling are less than the types that the system has,
+ // there can be some null consumers.
+ continue;
+ }
+ sideSourcesMap.add(side, consumer);
}
}
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index c798d85f0fea..eca6dcb1daac 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -67,9 +67,9 @@ public class InsetsController implements WindowInsetsController {
* Translation animation evaluator.
*/
private static TypeEvaluator<Insets> sEvaluator = (fraction, startValue, endValue) -> Insets.of(
- 0,
+ (int) (startValue.left + fraction * (endValue.left - startValue.left)),
(int) (startValue.top + fraction * (endValue.top - startValue.top)),
- 0,
+ (int) (startValue.right + fraction * (endValue.right - startValue.right)),
(int) (startValue.bottom + fraction * (endValue.bottom - startValue.bottom)));
/**
@@ -224,7 +224,7 @@ public class InsetsController implements WindowInsetsController {
show(types, false /* fromIme */);
}
- private void show(@InsetType int types, boolean fromIme) {
+ void show(@InsetType int types, boolean fromIme) {
// TODO: Support a ResultReceiver for IME.
// TODO(b/123718661): Make show() work for multi-session IME.
int typesReady = 0;
@@ -251,6 +251,10 @@ public class InsetsController implements WindowInsetsController {
@Override
public void hide(@InsetType int types) {
+ hide(types, false /* fromIme */);
+ }
+
+ void hide(@InsetType int types, boolean fromIme) {
int typesReady = 0;
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
for (int i = internalTypes.size() - 1; i >= 0; i--) {
@@ -265,7 +269,7 @@ public class InsetsController implements WindowInsetsController {
}
typesReady |= InsetsState.toPublicType(consumer.getType());
}
- applyAnimation(typesReady, false /* show */, false /* fromIme */);
+ applyAnimation(typesReady, false /* show */, fromIme /* fromIme */);
}
@Override
@@ -331,42 +335,35 @@ public class InsetsController implements WindowInsetsController {
boolean isReady = true;
for (int i = internalTypes.size() - 1; i >= 0; i--) {
InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
- // Double check for IME that IME target window has focus.
- if (consumer.getType() != TYPE_IME || consumer.hasWindowFocus()) {
- boolean setVisible = !consumer.isVisible();
- if (setVisible) {
- // Show request
- switch(consumer.requestShow(fromIme)) {
- case ShowResult.SHOW_IMMEDIATELY:
- typesReady |= InsetsState.toPublicType(consumer.getType());
- break;
- case ShowResult.SHOW_DELAYED:
- isReady = false;
- break;
- case ShowResult.SHOW_FAILED:
- // IME cannot be shown (since it didn't have focus), proceed
- // with animation of other types.
- if (mPendingTypesToShow != 0) {
- // remove IME from pending because view no longer has focus.
- mPendingTypesToShow &= ~InsetsState.toPublicType(TYPE_IME);
- }
- break;
- }
- } else {
- // Hide request
- // TODO: Move notifyHidden() to beginning of the hide animation
- // (when visibility actually changes using hideDirectly()).
- consumer.notifyHidden();
- typesReady |= InsetsState.toPublicType(consumer.getType());
+ boolean setVisible = !consumer.isVisible();
+ if (setVisible) {
+ // Show request
+ switch(consumer.requestShow(fromIme)) {
+ case ShowResult.SHOW_IMMEDIATELY:
+ typesReady |= InsetsState.toPublicType(consumer.getType());
+ break;
+ case ShowResult.SHOW_DELAYED:
+ isReady = false;
+ break;
+ case ShowResult.SHOW_FAILED:
+ // IME cannot be shown (since it didn't have focus), proceed
+ // with animation of other types.
+ if (mPendingTypesToShow != 0) {
+ // remove IME from pending because view no longer has focus.
+ mPendingTypesToShow &= ~InsetsState.toPublicType(TYPE_IME);
+ }
+ break;
}
- consumers.put(consumer.getType(), consumer);
} else {
- // window doesnt have focus, no-op.
- isReady = false;
- // TODO: Let the calling app know that window has lost focus and
- // show()/hide()/controlWindowInsetsAnimation requests will be ignored.
- typesReady &= ~InsetsState.toPublicType(consumer.getType());
+ // Hide request
+ // TODO: Move notifyHidden() to beginning of the hide animation
+ // (when visibility actually changes using hideDirectly()).
+ if (!fromIme) {
+ consumer.notifyHidden();
+ }
+ typesReady |= InsetsState.toPublicType(consumer.getType());
}
+ consumers.put(consumer.getType(), consumer);
}
return new Pair<>(typesReady, isReady);
}
diff --git a/core/java/android/view/InsetsFlags.java b/core/java/android/view/InsetsFlags.java
index 276e80a772f3..6e459b22e657 100644
--- a/core/java/android/view/InsetsFlags.java
+++ b/core/java/android/view/InsetsFlags.java
@@ -16,10 +16,18 @@
package android.view;
+import static android.view.View.NAVIGATION_BAR_TRANSLUCENT;
+import static android.view.View.NAVIGATION_BAR_TRANSPARENT;
+import static android.view.View.STATUS_BAR_TRANSLUCENT;
+import static android.view.View.STATUS_BAR_TRANSPARENT;
+import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_SIDE_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_TOP_BAR;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
-import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_SIDE_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_TOP_BAR;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
@@ -35,9 +43,13 @@ public class InsetsFlags {
@ViewDebug.ExportedProperty(flagMapping = {
@ViewDebug.FlagToString(
- mask = APPEARANCE_OPAQUE_BARS,
- equals = APPEARANCE_OPAQUE_BARS,
- name = "OPAQUE_BARS"),
+ mask = APPEARANCE_OPAQUE_TOP_BAR,
+ equals = APPEARANCE_OPAQUE_TOP_BAR,
+ name = "OPAQUE_TOP_BAR"),
+ @ViewDebug.FlagToString(
+ mask = APPEARANCE_OPAQUE_SIDE_BARS,
+ equals = APPEARANCE_OPAQUE_SIDE_BARS,
+ name = "OPAQUE_SIDE_BARS"),
@ViewDebug.FlagToString(
mask = APPEARANCE_LOW_PROFILE_BARS,
equals = APPEARANCE_LOW_PROFILE_BARS,
@@ -64,4 +76,44 @@ public class InsetsFlags {
name = "SHOW_TRANSIENT_BARS_BY_SWIPE")
})
public @Behavior int behavior;
+
+ /**
+ * Converts system UI visibility to appearance.
+ *
+ * @param systemUiVisibility the system UI visibility to be converted.
+ * @return the outcome {@link Appearance}
+ */
+ public static @Appearance int getAppearance(int systemUiVisibility) {
+ int appearance = 0;
+ appearance |= convertFlag(systemUiVisibility, SYSTEM_UI_FLAG_LOW_PROFILE,
+ APPEARANCE_LOW_PROFILE_BARS);
+ appearance |= convertFlag(systemUiVisibility, SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,
+ APPEARANCE_LIGHT_TOP_BAR);
+ appearance |= convertFlag(systemUiVisibility, SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+ APPEARANCE_LIGHT_SIDE_BARS);
+ appearance |= convertNoFlag(systemUiVisibility,
+ STATUS_BAR_TRANSLUCENT | STATUS_BAR_TRANSPARENT, APPEARANCE_OPAQUE_TOP_BAR);
+ appearance |= convertNoFlag(systemUiVisibility,
+ NAVIGATION_BAR_TRANSLUCENT | NAVIGATION_BAR_TRANSPARENT,
+ APPEARANCE_OPAQUE_SIDE_BARS);
+ return appearance;
+ }
+
+ /**
+ * Converts the system UI visibility into an appearance flag if the given visibility contains
+ * the given system UI flag.
+ */
+ private static @Appearance int convertFlag(int systemUiVisibility, int systemUiFlag,
+ @Appearance int appearance) {
+ return (systemUiVisibility & systemUiFlag) != 0 ? appearance : 0;
+ }
+
+ /**
+ * Converts the system UI visibility into an appearance flag if the given visibility doesn't
+ * contains the given system UI flag.
+ */
+ private static @Appearance int convertNoFlag(int systemUiVisibility, int systemUiFlag,
+ @Appearance int appearance) {
+ return (systemUiVisibility & systemUiFlag) == 0 ? appearance : 0;
+ }
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index a04c39b63c85..99502a6c1d72 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -270,10 +270,23 @@ public class InsetsState implements Parcelable {
*
* @param type The {@link InternalInsetType} of the source to remove
*/
- public void removeSource(int type) {
+ public void removeSource(@InternalInsetType int type) {
mSources.remove(type);
}
+ /**
+ * A shortcut for setting the visibility of the source.
+ *
+ * @param type The {@link InternalInsetType} of the source to set the visibility
+ * @param visible {@code true} for visible
+ */
+ public void setSourceVisible(@InternalInsetType int type, boolean visible) {
+ InsetsSource source = mSources.get(type);
+ if (source != null) {
+ source.setVisible(visible);
+ }
+ }
+
public void set(InsetsState other) {
set(other, false /* copySources */);
}
@@ -357,6 +370,19 @@ public class InsetsState implements Parcelable {
}
}
+ public static boolean containsType(@InternalInsetType int[] types,
+ @InternalInsetType int type) {
+ if (types == null) {
+ return false;
+ }
+ for (int t : types) {
+ if (t == type) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.println(prefix + "InsetsState");
for (int i = mSources.size() - 1; i >= 0; i--) {
@@ -364,7 +390,7 @@ public class InsetsState implements Parcelable {
}
}
- public static String typeToString(int type) {
+ public static String typeToString(@InternalInsetType int type) {
switch (type) {
case TYPE_TOP_BAR:
return "TYPE_TOP_BAR";
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 9705fb3c0297..b6c3476bfa4f 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -91,7 +91,7 @@ public final class SurfaceControl implements Parcelable {
boolean captureSecureLayers);
private static native ScreenshotGraphicBuffer nativeCaptureLayers(IBinder displayToken,
long layerObject, Rect sourceCrop, float frameScale, long[] excludeLayerObjects);
-
+ private static native long nativeMirrorSurface(long mirrorOfObject);
private static native long nativeCreateTransaction();
private static native long nativeGetNativeTransactionFinalizer();
private static native void nativeApplyTransaction(long transactionObj, boolean sync);
@@ -181,14 +181,12 @@ public final class SurfaceControl implements Parcelable {
private static native void nativeSeverChildren(long transactionObj, long nativeObject);
private static native void nativeSetOverrideScalingMode(long transactionObj, long nativeObject,
int scalingMode);
- private static native boolean nativeGetTransformToDisplayInverse(long nativeObject);
private static native Display.HdrCapabilities nativeGetHdrCapabilities(IBinder displayToken);
private static native void nativeSetInputWindowInfo(long transactionObj, long nativeObject,
InputWindowHandle handle);
- private static native void nativeTransferTouchFocus(long transactionObj, IBinder fromToken,
- IBinder toToken);
+
private static native boolean nativeGetProtectedContentSupport();
private static native void nativeSetMetadata(long transactionObj, long nativeObject, int key,
Parcel data);
@@ -201,7 +199,10 @@ public final class SurfaceControl implements Parcelable {
private final CloseGuard mCloseGuard = CloseGuard.get();
private String mName;
- long mNativeObject; // package visibility only for Surface.java access
+ /**
+ * @hide
+ */
+ public long mNativeObject;
// TODO: Move this to native.
private final Object mSizeLock = new Object();
@@ -304,8 +305,8 @@ public final class SurfaceControl implements Parcelable {
/**
* Surface creation flag: Creates a Dim surface.
* Everything behind this surface is dimmed by the amount specified
- * in {@link #setAlpha}. It is an error to lock a Dim surface, since it
- * doesn't have a backing store.
+ * in {@link Transaction#setAlpha(SurfaceControl, float)}. It is an error to lock a Dim
+ * surface, since it doesn't have a backing store.
*
* @hide
*/
@@ -320,6 +321,11 @@ public final class SurfaceControl implements Parcelable {
public static final int FX_SURFACE_CONTAINER = 0x00080000;
/**
+ * @hide
+ */
+ public static final int FX_SURFACE_BLAST = 0x00040000;
+
+ /**
* Mask used for FX values above.
*
* @hide
@@ -698,6 +704,14 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * @hide
+ */
+ public Builder setBLASTLayer() {
+ unsetBufferSize();
+ return setFlags(FX_SURFACE_BLAST, FX_SURFACE_MASK);
+ }
+
+ /**
* Indicates whether a 'ContainerLayer' is to be constructed.
*
* Container layers will not be rendered in any fashion and instead are used
@@ -744,20 +758,20 @@ public final class SurfaceControl implements Parcelable {
* <p>
* Good practice is to first create the surface with the {@link #HIDDEN} flag
* specified, open a transaction, set the surface layer, layer stack, alpha,
- * and position, call {@link #show} if appropriate, and close the transaction.
+ * and position, call {@link Transaction#show(SurfaceControl)} if appropriate, and close the
+ * transaction.
* <p>
* Bounds of the surface is determined by its crop and its buffer size. If the
* surface has no buffer or crop, the surface is boundless and only constrained
* by the size of its parent bounds.
*
- * @param session The surface session, must not be null.
- * @param name The surface name, must not be null.
- * @param w The surface initial width.
- * @param h The surface initial height.
- * @param flags The surface creation flags. Should always include {@link #HIDDEN}
- * in the creation flags.
+ * @param session The surface session, must not be null.
+ * @param name The surface name, must not be null.
+ * @param w The surface initial width.
+ * @param h The surface initial height.
+ * @param flags The surface creation flags. Should always include {@link #HIDDEN}
+ * in the creation flags.
* @param metadata Initial metadata.
- *
* @throws throws OutOfResourcesException If the SurfaceControl cannot be created.
*/
private SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
@@ -1018,15 +1032,6 @@ public final class SurfaceControl implements Parcelable {
/**
* @hide
*/
- public void deferTransactionUntil(Surface barrier, long frame) {
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.deferTransactionUntilSurface(this, barrier, frame);
- }
- }
-
- /**
- * @hide
- */
public void reparentChildren(SurfaceControl newParent) {
synchronized(SurfaceControl.class) {
sGlobalTransaction.reparentChildren(this, newParent);
@@ -1036,15 +1041,6 @@ public final class SurfaceControl implements Parcelable {
/**
* @hide
*/
- public void reparent(SurfaceControl newParent) {
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.reparent(this, newParent);
- }
- }
-
- /**
- * @hide
- */
public void detachChildren() {
synchronized(SurfaceControl.class) {
sGlobalTransaction.detachChildren(this);
@@ -1064,15 +1060,6 @@ public final class SurfaceControl implements Parcelable {
/**
* @hide
*/
- public static void setAnimationTransaction() {
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setAnimationTransaction();
- }
- }
-
- /**
- * @hide
- */
@UnsupportedAppUsage
public void setLayer(int zorder) {
checkNotReleased();
@@ -1084,16 +1071,6 @@ public final class SurfaceControl implements Parcelable {
/**
* @hide
*/
- public void setRelativeLayer(SurfaceControl relativeTo, int zorder) {
- checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.setRelativeLayer(this, relativeTo, zorder);
- }
- }
-
- /**
- * @hide
- */
@UnsupportedAppUsage
public void setPosition(float x, float y) {
checkNotReleased();
@@ -1187,16 +1164,6 @@ public final class SurfaceControl implements Parcelable {
/**
* @hide
*/
- public void setColor(@Size(3) float[] color) {
- checkNotReleased();
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setColor(this, color);
- }
- }
-
- /**
- * @hide
- */
public void setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) {
checkNotReleased();
synchronized(SurfaceControl.class) {
@@ -1205,36 +1172,6 @@ public final class SurfaceControl implements Parcelable {
}
/**
- * Sets the transform and position of a {@link SurfaceControl} from a 3x3 transformation matrix.
- *
- * @param matrix The matrix to apply.
- * @param float9 An array of 9 floats to be used to extract the values from the matrix.
- * @hide
- */
- public void setMatrix(Matrix matrix, float[] float9) {
- checkNotReleased();
- matrix.getValues(float9);
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setMatrix(this, float9[MSCALE_X], float9[MSKEW_Y],
- float9[MSKEW_X], float9[MSCALE_Y]);
- sGlobalTransaction.setPosition(this, float9[MTRANS_X], float9[MTRANS_Y]);
- }
- }
-
- /**
- * Sets the color transform for the Surface.
- * @param matrix A float array with 9 values represents a 3x3 transform matrix
- * @param translation A float array with 3 values represents a translation vector
- * @hide
- */
- public void setColorTransform(@Size(9) float[] matrix, @Size(3) float[] translation) {
- checkNotReleased();
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setColorTransform(this, matrix, translation);
- }
- }
-
- /**
* Sets the Surface to be color space agnostic. If a surface is color space agnostic,
* the color can be interpreted in any color space.
* @param agnostic A boolean to indicate whether the surface is color space agnostic
@@ -1264,43 +1201,6 @@ public final class SurfaceControl implements Parcelable {
}
/**
- * Same as {@link SurfaceControl#setWindowCrop(Rect)} but sets the crop rect top left at 0, 0.
- *
- * @param width width of crop rect
- * @param height height of crop rect
- * @hide
- */
- public void setWindowCrop(int width, int height) {
- checkNotReleased();
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setWindowCrop(this, width, height);
- }
- }
-
- /**
- * Sets the corner radius of a {@link SurfaceControl}.
- *
- * @param cornerRadius Corner radius in pixels.
- * @hide
- */
- public void setCornerRadius(float cornerRadius) {
- checkNotReleased();
- synchronized (SurfaceControl.class) {
- sGlobalTransaction.setCornerRadius(this, cornerRadius);
- }
- }
-
- /**
- * @hide
- */
- public void setLayerStack(int layerStack) {
- checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.setLayerStack(this, layerStack);
- }
- }
-
- /**
* @hide
*/
public void setOpaque(boolean isOpaque) {
@@ -2038,6 +1938,28 @@ public final class SurfaceControl implements Parcelable {
return nativeSetDisplayBrightness(displayToken, brightness);
}
+ /**
+ * Creates a mirrored hierarchy for the mirrorOf {@link SurfaceControl}
+ *
+ * Real Hierarchy Mirror
+ * SC (value that's returned)
+ * |
+ * A A'
+ * | |
+ * B B'
+ *
+ * @param mirrorOf The root of the hierarchy that should be mirrored.
+ * @return A SurfaceControl that's the parent of the root of the mirrored hierarchy.
+ *
+ * @hide
+ */
+ public static SurfaceControl mirrorSurface(SurfaceControl mirrorOf) {
+ long nativeObj = nativeMirrorSurface(mirrorOf.mNativeObject);
+ SurfaceControl sc = new SurfaceControl();
+ sc.assignNativeObject(nativeObj);
+ return sc;
+ }
+
/**
* An atomic set of changes to a set of SurfaceControl.
*/
@@ -2048,7 +1970,10 @@ public final class SurfaceControl implements Parcelable {
public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
Transaction.class.getClassLoader(),
nativeGetNativeTransactionFinalizer(), 512);
- private long mNativeObject;
+ /**
+ * @hide
+ */
+ public long mNativeObject;
private final ArrayMap<SurfaceControl, Point> mResizedSurfaces = new ArrayMap<>();
Runnable mFreeNativeResources;
@@ -2242,22 +2167,6 @@ public final class SurfaceControl implements Parcelable {
}
/**
- * Transfers touch focus from one window to another. It is possible for multiple windows to
- * have touch focus if they support split touch dispatch
- * {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH} but this
- * method only transfers touch focus of the specified window without affecting
- * other windows that may also have touch focus at the same time.
- * @param fromToken The token of a window that currently has touch focus.
- * @param toToken The token of the window that should receive touch focus in
- * place of the first.
- * @hide
- */
- public Transaction transferTouchFocus(IBinder fromToken, IBinder toToken) {
- nativeTransferTouchFocus(mNativeObject, fromToken, toToken);
- return this;
- }
-
- /**
* Waits until any changes to input windows have been sent from SurfaceFlinger to
* InputFlinger before returning.
*
@@ -2300,6 +2209,12 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * Sets the transform and position of a {@link SurfaceControl} from a 3x3 transformation
+ * matrix.
+ *
+ * @param sc SurfaceControl to set matrix of
+ * @param matrix The matrix to apply.
+ * @param float9 An array of 9 floats to be used to extract the values from the matrix.
* @hide
*/
@UnsupportedAppUsage
@@ -2313,7 +2228,9 @@ public final class SurfaceControl implements Parcelable {
/**
* Sets the color transform for the Surface.
- * @param matrix A float array with 9 values represents a 3x3 transform matrix
+ *
+ * @param sc SurfaceControl to set color transform of
+ * @param matrix A float array with 9 values represents a 3x3 transform matrix
* @param translation A float array with 3 values represents a translation vector
* @hide
*/
@@ -2337,6 +2254,13 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * Bounds the surface and its children to the bounds specified. Size of the surface will be
+ * ignored and only the crop and buffer size will be used to determine the bounds of the
+ * surface. If no crop is specified and the surface has no buffer, the surface bounds is
+ * only constrained by the size of its parent bounds.
+ *
+ * @param sc SurfaceControl to set crop of.
+ * @param crop Bounds of the crop to apply.
* @hide
*/
@UnsupportedAppUsage
@@ -2353,6 +2277,12 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * Same as {@link Transaction#setWindowCrop(SurfaceControl, Rect)} but sets the crop rect
+ * top left at 0, 0.
+ *
+ * @param sc SurfaceControl to set crop of.
+ * @param width width of crop rect
+ * @param height height of crop rect
* @hide
*/
public Transaction setWindowCrop(SurfaceControl sc, int width, int height) {
diff --git a/core/java/android/view/SurfaceHolder.java b/core/java/android/view/SurfaceHolder.java
index 2fd2e966dc38..c5d45c318f67 100644
--- a/core/java/android/view/SurfaceHolder.java
+++ b/core/java/android/view/SurfaceHolder.java
@@ -16,7 +16,10 @@
package android.view;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
import android.graphics.Canvas;
+import android.graphics.PixelFormat;
import android.graphics.Rect;
/**
@@ -76,7 +79,7 @@ public interface SurfaceHolder {
*
* @param holder The SurfaceHolder whose surface is being created.
*/
- public void surfaceCreated(SurfaceHolder holder);
+ void surfaceCreated(@NonNull SurfaceHolder holder);
/**
* This is called immediately after any structural changes (format or
@@ -85,12 +88,12 @@ public interface SurfaceHolder {
* once, after {@link #surfaceCreated}.
*
* @param holder The SurfaceHolder whose surface has changed.
- * @param format The new PixelFormat of the surface.
+ * @param format The new {@link PixelFormat} of the surface.
* @param width The new width of the surface.
* @param height The new height of the surface.
*/
- public void surfaceChanged(SurfaceHolder holder, int format, int width,
- int height);
+ void surfaceChanged(@NonNull SurfaceHolder holder, @PixelFormat.Format int format,
+ @IntRange(from = 0) int width, @IntRange(from = 0) int height);
/**
* This is called immediately before a surface is being destroyed. After
@@ -101,7 +104,7 @@ public interface SurfaceHolder {
*
* @param holder The SurfaceHolder whose surface is being destroyed.
*/
- public void surfaceDestroyed(SurfaceHolder holder);
+ void surfaceDestroyed(@NonNull SurfaceHolder holder);
}
/**
@@ -122,7 +125,7 @@ public interface SurfaceHolder {
*
* @param holder The SurfaceHolder whose surface has changed.
*/
- void surfaceRedrawNeeded(SurfaceHolder holder);
+ void surfaceRedrawNeeded(@NonNull SurfaceHolder holder);
/**
* An alternative to surfaceRedrawNeeded where it is not required to block
@@ -140,7 +143,8 @@ public interface SurfaceHolder {
* from any thread.
*
*/
- default void surfaceRedrawNeededAsync(SurfaceHolder holder, Runnable drawingFinished) {
+ default void surfaceRedrawNeededAsync(@NonNull SurfaceHolder holder,
+ @NonNull Runnable drawingFinished) {
surfaceRedrawNeeded(holder);
drawingFinished.run();
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 262b9e50ad00..2f0a4ebb84f8 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -167,6 +167,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
boolean mUseAlpha = false;
float mSurfaceAlpha = 1f;
+ boolean mClipSurfaceToBounds;
@UnsupportedAppUsage
boolean mHaveFrame = false;
@@ -417,12 +418,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
Log.d(TAG, System.identityHashCode(this)
+ " updateSurfaceAlpha: set alpha=" + alpha);
}
- SurfaceControl.openTransaction();
- try {
- mSurfaceControl.setAlpha(alpha);
- } finally {
- SurfaceControl.closeTransaction();
- }
+ mTmpTransaction.setAlpha(mSurfaceControl, alpha).apply();
}
mSurfaceAlpha = alpha;
}
@@ -555,9 +551,52 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
super.dispatchDraw(canvas);
}
+ /**
+ * Control whether the surface is clipped to the same bounds as the View. If true, then
+ * the bounds set by {@link #setClipBounds(Rect)} are applied to the surface as window-crop.
+ *
+ * @param enabled whether to enable surface clipping
+ * @hide
+ */
+ public void setEnableSurfaceClipping(boolean enabled) {
+ mClipSurfaceToBounds = enabled;
+ invalidate();
+ }
+
+ @Override
+ public void setClipBounds(Rect clipBounds) {
+ super.setClipBounds(clipBounds);
+
+ if (!mClipSurfaceToBounds) {
+ return;
+ }
+
+ // When cornerRadius is non-zero, a draw() is required to update
+ // the viewport (rounding the corners of the clipBounds).
+ if (mCornerRadius > 0f && !isAboveParent()) {
+ invalidate();
+ }
+
+ if (mSurfaceControl != null) {
+ if (mClipBounds != null) {
+ mTmpRect.set(mClipBounds);
+ } else {
+ mTmpRect.set(0, 0, mSurfaceWidth, mSurfaceHeight);
+ }
+ SyncRtSurfaceTransactionApplier applier = new SyncRtSurfaceTransactionApplier(this);
+ applier.scheduleApply(
+ new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(mSurfaceControl)
+ .withWindowCrop(mTmpRect)
+ .build());
+ }
+ }
+
private void clearSurfaceViewPort(Canvas canvas) {
if (mCornerRadius > 0f) {
canvas.getClipBounds(mTmpRect);
+ if (mClipSurfaceToBounds && mClipBounds != null) {
+ mTmpRect.intersect(mClipBounds);
+ }
canvas.drawRoundRect(mTmpRect.left, mTmpRect.top, mTmpRect.right, mTmpRect.bottom,
mCornerRadius, mCornerRadius, mRoundedViewportPaint);
} else {
@@ -583,6 +622,16 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
/**
+ * Returns the corner radius for the SurfaceView.
+
+ * @return the radius of the corners in pixels
+ * @hide
+ */
+ public float getCornerRadius() {
+ return mCornerRadius;
+ }
+
+ /**
* Control whether the surface view's surface is placed on top of another
* regular surface view in the window (but still behind the window itself).
* This is typically used to place overlays on top of an underlying media
@@ -647,17 +696,18 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
}
- private void updateBackgroundVisibilityInTransaction() {
+ private void updateBackgroundVisibility(Transaction t) {
if (mBackgroundControl == null) {
return;
}
if ((mSubLayer < 0) && ((mSurfaceFlags & SurfaceControl.OPAQUE) != 0)) {
- mBackgroundControl.show();
+ t.show(mBackgroundControl);
} else {
- mBackgroundControl.hide();
+ t.hide(mBackgroundControl);
}
}
+
private void releaseSurfaces() {
mSurfaceAlpha = 1f;
@@ -675,6 +725,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
mTmpTransaction.remove(mBackgroundControl);
mBackgroundControl = null;
}
+ mSurface.release();
mTmpTransaction.apply();
}
}
@@ -798,56 +849,60 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ "Cur surface: " + mSurface);
- SurfaceControl.openTransaction();
- try {
- // If we are creating the surface control or the parent surface has not
- // changed, then set relative z. Otherwise allow the parent
- // SurfaceChangedCallback to update the relative z. This is needed so that
- // we do not change the relative z before the server is ready to swap the
- // parent surface.
- if (creating || (mParentSurfaceGenerationId
- == viewRoot.mSurface.getGenerationId())) {
- SurfaceControl.mergeToGlobalTransaction(updateRelativeZ());
- }
- mParentSurfaceGenerationId = viewRoot.mSurface.getGenerationId();
+ // If we are creating the surface control or the parent surface has not
+ // changed, then set relative z. Otherwise allow the parent
+ // SurfaceChangedCallback to update the relative z. This is needed so that
+ // we do not change the relative z before the server is ready to swap the
+ // parent surface.
+ if (creating || (mParentSurfaceGenerationId
+ == viewRoot.mSurface.getGenerationId())) {
+ updateRelativeZ(mTmpTransaction);
+ }
+ mParentSurfaceGenerationId = viewRoot.mSurface.getGenerationId();
- if (mViewVisibility) {
- mSurfaceControl.show();
- } else {
- mSurfaceControl.hide();
- }
- updateBackgroundVisibilityInTransaction();
- if (mUseAlpha) {
- mSurfaceControl.setAlpha(alpha);
- mSurfaceAlpha = alpha;
- }
+ if (mViewVisibility) {
+ mTmpTransaction.show(mSurfaceControl);
+ } else {
+ mTmpTransaction.hide(mSurfaceControl);
+ }
+ updateBackgroundVisibility(mTmpTransaction);
+ if (mUseAlpha) {
+ mTmpTransaction.setAlpha(mSurfaceControl, alpha);
+ mSurfaceAlpha = alpha;
+ }
- // While creating the surface, we will set it's initial
- // geometry. Outside of that though, we should generally
- // leave it to the RenderThread.
- //
- // There is one more case when the buffer size changes we aren't yet
- // prepared to sync (as even following the transaction applying
- // we still need to latch a buffer).
- // b/28866173
- if (sizeChanged || creating || !mRtHandlingPositionUpdates) {
- mSurfaceControl.setPosition(mScreenRect.left, mScreenRect.top);
- mSurfaceControl.setMatrix(mScreenRect.width() / (float) mSurfaceWidth,
- 0.0f, 0.0f,
- mScreenRect.height() / (float) mSurfaceHeight);
- // Set a window crop when creating the surface or changing its size to
- // crop the buffer to the surface size since the buffer producer may
- // use SCALING_MODE_SCALE and submit a larger size than the surface
- // size.
- mSurfaceControl.setWindowCrop(mSurfaceWidth, mSurfaceHeight);
- }
- mSurfaceControl.setCornerRadius(mCornerRadius);
- if (sizeChanged && !creating) {
- mSurfaceControl.setBufferSize(mSurfaceWidth, mSurfaceHeight);
+ // While creating the surface, we will set it's initial
+ // geometry. Outside of that though, we should generally
+ // leave it to the RenderThread.
+ //
+ // There is one more case when the buffer size changes we aren't yet
+ // prepared to sync (as even following the transaction applying
+ // we still need to latch a buffer).
+ // b/28866173
+ if (sizeChanged || creating || !mRtHandlingPositionUpdates) {
+ mTmpTransaction.setPosition(mSurfaceControl, mScreenRect.left,
+ mScreenRect.top);
+ mTmpTransaction.setMatrix(mSurfaceControl,
+ mScreenRect.width() / (float) mSurfaceWidth, 0.0f, 0.0f,
+ mScreenRect.height() / (float) mSurfaceHeight);
+ // Set a window crop when creating the surface or changing its size to
+ // crop the buffer to the surface size since the buffer producer may
+ // use SCALING_MODE_SCALE and submit a larger size than the surface
+ // size.
+ if (mClipSurfaceToBounds && mClipBounds != null) {
+ mTmpTransaction.setWindowCrop(mSurfaceControl, mClipBounds);
+ } else {
+ mTmpTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth,
+ mSurfaceHeight);
}
- } finally {
- SurfaceControl.closeTransaction();
}
+ mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
+ if (sizeChanged && !creating) {
+ mTmpTransaction.setBufferSize(mSurfaceControl, mSurfaceWidth,
+ mSurfaceHeight);
+ }
+
+ mTmpTransaction.apply();
if (sizeChanged || creating) {
redrawNeeded = true;
@@ -962,7 +1017,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
} finally {
mIsCreating = false;
if (mSurfaceControl != null && !mSurfaceCreated) {
- mSurface.release();
releaseSurfaces();
}
}
@@ -1128,11 +1182,12 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
return;
}
- if (frameNumber > 0) {
- final ViewRootImpl viewRoot = getViewRootImpl();
-
- mRtTransaction.deferTransactionUntilSurface(mSurfaceControl, viewRoot.mSurface,
- frameNumber);
+ final ViewRootImpl viewRoot = getViewRootImpl();
+ if (frameNumber > 0 && viewRoot != null) {
+ if (viewRoot.mSurface.isValid()) {
+ mRtTransaction.deferTransactionUntilSurface(mSurfaceControl, viewRoot.mSurface,
+ frameNumber);
+ }
}
mRtTransaction.hide(mSurfaceControl);
@@ -1143,6 +1198,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
mRtTransaction.remove(mBackgroundControl);
mSurfaceControl = null;
mBackgroundControl = null;
+ mSurface.release();
}
mRtHandlingPositionUpdates = false;
}
@@ -1200,12 +1256,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
final float[] colorComponents = new float[] { Color.red(bgColor) / 255.f,
Color.green(bgColor) / 255.f, Color.blue(bgColor) / 255.f };
- SurfaceControl.openTransaction();
- try {
- mBackgroundControl.setColor(colorComponents);
- } finally {
- SurfaceControl.closeTransaction();
- }
+ mTmpTransaction.setColor(mBackgroundControl, colorComponents).apply();
}
@UnsupportedAppUsage
@@ -1420,15 +1471,13 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
@Override
public void surfaceReplaced(Transaction t) {
if (mSurfaceControl != null && mBackgroundControl != null) {
- t.merge(updateRelativeZ());
+ updateRelativeZ(t);
}
}
- private Transaction updateRelativeZ() {
- Transaction t = new Transaction();
+ private void updateRelativeZ(Transaction t) {
SurfaceControl viewRoot = getViewRootImpl().getSurfaceControl();
t.setRelativeLayer(mBackgroundControl, viewRoot, Integer.MIN_VALUE);
t.setRelativeLayer(mSurfaceControl, viewRoot, mSubLayer);
- return t;
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7f704e366f36..0cb55701caa4 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -75,6 +75,7 @@ import android.graphics.RenderNode;
import android.graphics.Shader;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
import android.hardware.display.DisplayManagerGlobal;
import android.net.Uri;
import android.os.Build;
@@ -4496,8 +4497,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* When non-null and valid, this is expected to contain an up-to-date copy
* of the background drawable. It is cleared on temporary detach, and reset
* on cleanup.
+ * @hide
*/
- private RenderNode mBackgroundRenderNode;
+ RenderNode mBackgroundRenderNode;
@UnsupportedAppUsage
private int mBackgroundResource;
@@ -5228,6 +5230,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
sBrokenWindowBackground = targetSdkVersion < Build.VERSION_CODES.Q;
+ GradientDrawable.sWrapNegativeAngleMeasurements =
+ targetSdkVersion >= Build.VERSION_CODES.Q;
sCompatibilityDone = true;
}
}
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 8a1fd62c0201..ad59ae5d2bee 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -32,6 +32,7 @@ import android.graphics.RenderNode;
import android.os.Debug;
import android.os.Handler;
import android.os.Looper;
+import android.os.Message;
import android.os.RemoteException;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -46,6 +47,7 @@ import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
+import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -53,12 +55,12 @@ import java.lang.annotation.Target;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayDeque;
-import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.concurrent.Callable;
-import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
@@ -68,6 +70,7 @@ import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
+import java.util.stream.Stream;
/**
* Various debugging/tracing tools related to {@link View} and the view hierarchy.
@@ -331,11 +334,83 @@ public class ViewDebug {
public View findHierarchyView(String className, int hashCode);
}
- private static HashMap<Class<?>, Method[]> mCapturedViewMethodsForClasses = null;
- private static HashMap<Class<?>, Field[]> mCapturedViewFieldsForClasses = null;
+ private abstract static class PropertyInfo<T extends Annotation,
+ R extends AccessibleObject & Member> {
+
+ public final R member;
+ public final T property;
+ public final String name;
+ public final Class<?> returnType;
+
+ public String entrySuffix = "";
+ public String valueSuffix = "";
+
+ PropertyInfo(Class<T> property, R member, Class<?> returnType) {
+ this.member = member;
+ this.name = member.getName();
+ this.property = member.getAnnotation(property);
+ this.returnType = returnType;
+ }
+
+ public abstract Object invoke(Object target) throws Exception;
+
+ static <T extends Annotation> PropertyInfo<T, ?> forMethod(Method method,
+ Class<T> property) {
+ // Ensure the method return and parameter types can be resolved.
+ try {
+ if ((method.getReturnType() == Void.class)
+ || (method.getParameterTypes().length != 0)) {
+ return null;
+ }
+ } catch (NoClassDefFoundError e) {
+ return null;
+ }
+ if (!method.isAnnotationPresent(property)) {
+ return null;
+ }
+ method.setAccessible(true);
+
+ PropertyInfo info = new MethodPI(method, property);
+ info.entrySuffix = "()";
+ info.valueSuffix = ";";
+ return info;
+ }
+
+ static <T extends Annotation> PropertyInfo<T, ?> forField(Field field, Class<T> property) {
+ if (!field.isAnnotationPresent(property)) {
+ return null;
+ }
+ field.setAccessible(true);
+ return new FieldPI<>(field, property);
+ }
+ }
+
+ private static class MethodPI<T extends Annotation> extends PropertyInfo<T, Method> {
+
+ MethodPI(Method method, Class<T> property) {
+ super(property, method, method.getReturnType());
+ }
+
+ @Override
+ public Object invoke(Object target) throws Exception {
+ return member.invoke(target);
+ }
+ }
+
+ private static class FieldPI<T extends Annotation> extends PropertyInfo<T, Field> {
+
+ FieldPI(Field field, Class<T> property) {
+ super(property, field, field.getType());
+ }
+
+ @Override
+ public Object invoke(Object target) throws Exception {
+ return member.get(target);
+ }
+ }
// Maximum delay in ms after which we stop trying to capture a View's drawing
- private static final int CAPTURE_TIMEOUT = 4000;
+ private static final int CAPTURE_TIMEOUT = 6000;
private static final String REMOTE_COMMAND_CAPTURE = "CAPTURE";
private static final String REMOTE_COMMAND_DUMP = "DUMP";
@@ -346,9 +421,9 @@ public class ViewDebug {
private static final String REMOTE_COMMAND_CAPTURE_LAYERS = "CAPTURE_LAYERS";
private static final String REMOTE_COMMAND_OUTPUT_DISPLAYLIST = "OUTPUT_DISPLAYLIST";
- private static HashMap<Class<?>, Field[]> sFieldsForClasses;
- private static HashMap<Class<?>, Method[]> sMethodsForClasses;
- private static HashMap<AccessibleObject, ExportedProperty> sAnnotations;
+ private static HashMap<Class<?>, PropertyInfo<ExportedProperty, ?>[]> sExportProperties;
+ private static HashMap<Class<?>, PropertyInfo<CapturedViewProperty, ?>[]>
+ sCapturedViewProperties;
/**
* @deprecated This enum is now unused
@@ -1157,6 +1232,69 @@ public class ViewDebug {
private static void dumpViewHierarchy(Context context, ViewGroup group,
BufferedWriter out, int level, boolean skipChildren, boolean includeProperties) {
+ cacheExportedProperties(group.getClass());
+ if (!skipChildren) {
+ cacheExportedPropertiesForChildren(group);
+ }
+ // Try to use the handler provided by the view
+ Handler handler = group.getHandler();
+ // Fall back on using the main thread
+ if (handler == null) {
+ handler = new Handler(Looper.getMainLooper());
+ }
+
+ if (handler.getLooper() == Looper.myLooper()) {
+ dumpViewHierarchyOnUIThread(context, group, out, level, skipChildren,
+ includeProperties);
+ } else {
+ FutureTask task = new FutureTask(() ->
+ dumpViewHierarchyOnUIThread(context, group, out, level, skipChildren,
+ includeProperties), null);
+ Message msg = Message.obtain(handler, task);
+ msg.setAsynchronous(true);
+ handler.sendMessage(msg);
+ while (true) {
+ try {
+ task.get(CAPTURE_TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS);
+ return;
+ } catch (InterruptedException e) {
+ // try again
+ } catch (ExecutionException | TimeoutException e) {
+ // Something unexpected happened.
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+
+ private static void cacheExportedPropertiesForChildren(ViewGroup group) {
+ final int count = group.getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View view = group.getChildAt(i);
+ cacheExportedProperties(view.getClass());
+ if (view instanceof ViewGroup) {
+ cacheExportedPropertiesForChildren((ViewGroup) view);
+ }
+ }
+ }
+
+ private static void cacheExportedProperties(Class<?> klass) {
+ if (sExportProperties != null && sExportProperties.containsKey(klass)) {
+ return;
+ }
+ do {
+ for (PropertyInfo<ExportedProperty, ?> info : getExportedProperties(klass)) {
+ if (!info.returnType.isPrimitive() && info.property.deepExport()) {
+ cacheExportedProperties(info.returnType);
+ }
+ }
+ klass = klass.getSuperclass();
+ } while (klass != Object.class);
+ }
+
+
+ private static void dumpViewHierarchyOnUIThread(Context context, ViewGroup group,
+ BufferedWriter out, int level, boolean skipChildren, boolean includeProperties) {
if (!dumpView(context, group, out, level, includeProperties)) {
return;
}
@@ -1169,16 +1307,16 @@ public class ViewDebug {
for (int i = 0; i < count; i++) {
final View view = group.getChildAt(i);
if (view instanceof ViewGroup) {
- dumpViewHierarchy(context, (ViewGroup) view, out, level + 1, skipChildren,
- includeProperties);
+ dumpViewHierarchyOnUIThread(context, (ViewGroup) view, out, level + 1,
+ skipChildren, includeProperties);
} else {
dumpView(context, view, out, level + 1, includeProperties);
}
if (view.mOverlay != null) {
ViewOverlay overlay = view.getOverlay();
ViewGroup overlayContainer = overlay.mOverlayViewGroup;
- dumpViewHierarchy(context, overlayContainer, out, level + 2, skipChildren,
- includeProperties);
+ dumpViewHierarchyOnUIThread(context, overlayContainer, out, level + 2,
+ skipChildren, includeProperties);
}
}
if (group instanceof HierarchyHandler) {
@@ -1212,81 +1350,28 @@ public class ViewDebug {
return true;
}
- private static Field[] getExportedPropertyFields(Class<?> klass) {
- if (sFieldsForClasses == null) {
- sFieldsForClasses = new HashMap<Class<?>, Field[]>();
- }
- if (sAnnotations == null) {
- sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512);
- }
-
- final HashMap<Class<?>, Field[]> map = sFieldsForClasses;
-
- Field[] fields = map.get(klass);
- if (fields != null) {
- return fields;
- }
-
- try {
- final Field[] declaredFields = klass.getDeclaredFieldsUnchecked(false);
- final ArrayList<Field> foundFields = new ArrayList<Field>();
- for (final Field field : declaredFields) {
- // Fields which can't be resolved have a null type.
- if (field.getType() != null && field.isAnnotationPresent(ExportedProperty.class)) {
- field.setAccessible(true);
- foundFields.add(field);
- sAnnotations.put(field, field.getAnnotation(ExportedProperty.class));
- }
- }
- fields = foundFields.toArray(new Field[foundFields.size()]);
- map.put(klass, fields);
- } catch (NoClassDefFoundError e) {
- throw new AssertionError(e);
- }
-
- return fields;
+ private static <T extends Annotation> PropertyInfo<T, ?>[] convertToPropertyInfos(
+ Method[] methods, Field[] fields, Class<T> property) {
+ return Stream.of(Arrays.stream(methods).map(m -> PropertyInfo.forMethod(m, property)),
+ Arrays.stream(fields).map(f -> PropertyInfo.forField(f, property)))
+ .flatMap(Function.identity())
+ .filter(i -> i != null)
+ .toArray(PropertyInfo[]::new);
}
- private static Method[] getExportedPropertyMethods(Class<?> klass) {
- if (sMethodsForClasses == null) {
- sMethodsForClasses = new HashMap<Class<?>, Method[]>(100);
- }
- if (sAnnotations == null) {
- sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512);
- }
-
- final HashMap<Class<?>, Method[]> map = sMethodsForClasses;
-
- Method[] methods = map.get(klass);
- if (methods != null) {
- return methods;
+ private static PropertyInfo<ExportedProperty, ?>[] getExportedProperties(Class<?> klass) {
+ if (sExportProperties == null) {
+ sExportProperties = new HashMap<>();
}
+ final HashMap<Class<?>, PropertyInfo<ExportedProperty, ?>[]> map = sExportProperties;
+ PropertyInfo<ExportedProperty, ?>[] properties = sExportProperties.get(klass);
- methods = klass.getDeclaredMethodsUnchecked(false);
-
- final ArrayList<Method> foundMethods = new ArrayList<Method>();
- for (final Method method : methods) {
- // Ensure the method return and parameter types can be resolved.
- try {
- method.getReturnType();
- method.getParameterTypes();
- } catch (NoClassDefFoundError e) {
- continue;
- }
-
- if (method.getParameterTypes().length == 0 &&
- method.isAnnotationPresent(ExportedProperty.class) &&
- method.getReturnType() != Void.class) {
- method.setAccessible(true);
- foundMethods.add(method);
- sAnnotations.put(method, method.getAnnotation(ExportedProperty.class));
- }
+ if (properties == null) {
+ properties = convertToPropertyInfos(klass.getDeclaredMethodsUnchecked(false),
+ klass.getDeclaredFieldsUnchecked(false), ExportedProperty.class);
+ map.put(klass, properties);
}
-
- methods = foundMethods.toArray(new Method[foundMethods.size()]);
- map.put(klass, methods);
-
- return methods;
+ return properties;
}
private static void dumpViewProperties(Context context, Object view,
@@ -1305,233 +1390,97 @@ public class ViewDebug {
Class<?> klass = view.getClass();
do {
- exportFields(context, view, out, klass, prefix);
- exportMethods(context, view, out, klass, prefix);
+ writeExportedProperties(context, view, out, klass, prefix);
klass = klass.getSuperclass();
} while (klass != Object.class);
}
- private static Object callMethodOnAppropriateTheadBlocking(final Method method,
- final Object object) throws IllegalAccessException, InvocationTargetException,
- TimeoutException {
- if (!(object instanceof View)) {
- return method.invoke(object, (Object[]) null);
- }
-
- final View view = (View) object;
- Callable<Object> callable = new Callable<Object>() {
- @Override
- public Object call() throws IllegalAccessException, InvocationTargetException {
- return method.invoke(view, (Object[]) null);
- }
- };
- FutureTask<Object> future = new FutureTask<Object>(callable);
- // Try to use the handler provided by the view
- Handler handler = view.getHandler();
- // Fall back on using the main thread
- if (handler == null) {
- handler = new Handler(android.os.Looper.getMainLooper());
- }
- handler.post(future);
- while (true) {
- try {
- return future.get(CAPTURE_TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS);
- } catch (ExecutionException e) {
- Throwable t = e.getCause();
- if (t instanceof IllegalAccessException) {
- throw (IllegalAccessException)t;
- }
- if (t instanceof InvocationTargetException) {
- throw (InvocationTargetException)t;
- }
- throw new RuntimeException("Unexpected exception", t);
- } catch (InterruptedException e) {
- // Call get again
- } catch (CancellationException e) {
- throw new RuntimeException("Unexpected cancellation exception", e);
- }
- }
- }
-
private static String formatIntToHexString(int value) {
return "0x" + Integer.toHexString(value).toUpperCase();
}
- private static void exportMethods(Context context, Object view, BufferedWriter out,
+ private static void writeExportedProperties(Context context, Object view, BufferedWriter out,
Class<?> klass, String prefix) throws IOException {
-
- final Method[] methods = getExportedPropertyMethods(klass);
- int count = methods.length;
- for (int i = 0; i < count; i++) {
- final Method method = methods[i];
+ for (PropertyInfo<ExportedProperty, ?> info : getExportedProperties(klass)) {
//noinspection EmptyCatchBlock
+ Object value;
try {
- Object methodValue = callMethodOnAppropriateTheadBlocking(method, view);
- final Class<?> returnType = method.getReturnType();
- final ExportedProperty property = sAnnotations.get(method);
- String categoryPrefix =
- property.category().length() != 0 ? property.category() + ":" : "";
-
- if (returnType == int.class) {
- if (property.resolveId() && context != null) {
- final int id = (Integer) methodValue;
- methodValue = resolveId(context, id);
- } else {
- final FlagToString[] flagsMapping = property.flagMapping();
- if (flagsMapping.length > 0) {
- final int intValue = (Integer) methodValue;
- final String valuePrefix =
- categoryPrefix + prefix + method.getName() + '_';
- exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
- }
-
- final IntToString[] mapping = property.mapping();
- if (mapping.length > 0) {
- final int intValue = (Integer) methodValue;
- boolean mapped = false;
- int mappingCount = mapping.length;
- for (int j = 0; j < mappingCount; j++) {
- final IntToString mapper = mapping[j];
- if (mapper.from() == intValue) {
- methodValue = mapper.to();
- mapped = true;
- break;
- }
- }
-
- if (!mapped) {
- methodValue = intValue;
- }
- }
- }
- } else if (returnType == int[].class) {
- final int[] array = (int[]) methodValue;
- final String valuePrefix = categoryPrefix + prefix + method.getName() + '_';
- final String suffix = "()";
+ value = info.invoke(view);
+ } catch (Exception e) {
+ // ignore
+ continue;
+ }
- exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
+ String categoryPrefix =
+ info.property.category().length() != 0 ? info.property.category() + ":" : "";
- continue;
- } else if (returnType == String[].class) {
- final String[] array = (String[]) methodValue;
- if (property.hasAdjacentMapping() && array != null) {
- for (int j = 0; j < array.length; j += 2) {
- if (array[j] != null) {
- writeEntry(out, categoryPrefix + prefix, array[j], "()",
- array[j + 1] == null ? "null" : array[j + 1]);
- }
+ if (info.returnType == int.class || info.returnType == byte.class) {
+ if (info.property.resolveId() && context != null) {
+ final int id = (Integer) value;
+ value = resolveId(context, id);
- }
+ } else if (info.property.formatToHexString()) {
+ if (info.returnType == int.class) {
+ value = formatIntToHexString((Integer) value);
+ } else if (info.returnType == byte.class) {
+ value = "0x"
+ + HexEncoding.encodeToString((Byte) value, true);
}
-
- continue;
- } else if (!returnType.isPrimitive()) {
- if (property.deepExport()) {
- dumpViewProperties(context, methodValue, out, prefix + property.prefix());
- continue;
+ } else {
+ final ViewDebug.FlagToString[] flagsMapping = info.property.flagMapping();
+ if (flagsMapping.length > 0) {
+ final int intValue = (Integer) value;
+ final String valuePrefix =
+ categoryPrefix + prefix + info.name + '_';
+ exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
}
- }
-
- writeEntry(out, categoryPrefix + prefix, method.getName(), "()", methodValue);
- } catch (IllegalAccessException e) {
- } catch (InvocationTargetException e) {
- } catch (TimeoutException e) {
- }
- }
- }
-
- private static void exportFields(Context context, Object view, BufferedWriter out,
- Class<?> klass, String prefix) throws IOException {
-
- final Field[] fields = getExportedPropertyFields(klass);
- int count = fields.length;
- for (int i = 0; i < count; i++) {
- final Field field = fields[i];
-
- //noinspection EmptyCatchBlock
- try {
- Object fieldValue = null;
- final Class<?> type = field.getType();
- final ExportedProperty property = sAnnotations.get(field);
- String categoryPrefix =
- property.category().length() != 0 ? property.category() + ":" : "";
-
- if (type == int.class || type == byte.class) {
- if (property.resolveId() && context != null) {
- final int id = field.getInt(view);
- fieldValue = resolveId(context, id);
- } else {
- final FlagToString[] flagsMapping = property.flagMapping();
- if (flagsMapping.length > 0) {
- final int intValue = field.getInt(view);
- final String valuePrefix =
- categoryPrefix + prefix + field.getName() + '_';
- exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
- }
-
- final IntToString[] mapping = property.mapping();
- if (mapping.length > 0) {
- final int intValue = field.getInt(view);
- int mappingCount = mapping.length;
- for (int j = 0; j < mappingCount; j++) {
- final IntToString mapped = mapping[j];
- if (mapped.from() == intValue) {
- fieldValue = mapped.to();
- break;
- }
- }
-
- if (fieldValue == null) {
- fieldValue = intValue;
+ final ViewDebug.IntToString[] mapping = info.property.mapping();
+ if (mapping.length > 0) {
+ final int intValue = (Integer) value;
+ boolean mapped = false;
+ int mappingCount = mapping.length;
+ for (int j = 0; j < mappingCount; j++) {
+ final ViewDebug.IntToString mapper = mapping[j];
+ if (mapper.from() == intValue) {
+ value = mapper.to();
+ mapped = true;
+ break;
}
}
- if (property.formatToHexString()) {
- fieldValue = field.get(view);
- if (type == int.class) {
- fieldValue = formatIntToHexString((Integer) fieldValue);
- } else if (type == byte.class) {
- fieldValue = "0x"
- + HexEncoding.encodeToString((Byte) fieldValue, true);
- }
+ if (!mapped) {
+ value = intValue;
}
}
- } else if (type == int[].class) {
- final int[] array = (int[]) field.get(view);
- final String valuePrefix = categoryPrefix + prefix + field.getName() + '_';
- final String suffix = "";
-
- exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
+ }
+ } else if (info.returnType == int[].class) {
+ final int[] array = (int[]) value;
+ final String valuePrefix = categoryPrefix + prefix + info.name + '_';
+ exportUnrolledArray(context, out, info.property, array, valuePrefix,
+ info.entrySuffix);
- continue;
- } else if (type == String[].class) {
- final String[] array = (String[]) field.get(view);
- if (property.hasAdjacentMapping() && array != null) {
- for (int j = 0; j < array.length; j += 2) {
- if (array[j] != null) {
- writeEntry(out, categoryPrefix + prefix, array[j], "",
- array[j + 1] == null ? "null" : array[j + 1]);
- }
+ continue;
+ } else if (info.returnType == String[].class) {
+ final String[] array = (String[]) value;
+ if (info.property.hasAdjacentMapping() && array != null) {
+ for (int j = 0; j < array.length; j += 2) {
+ if (array[j] != null) {
+ writeEntry(out, categoryPrefix + prefix, array[j],
+ info.entrySuffix, array[j + 1] == null ? "null" : array[j + 1]);
}
}
-
- continue;
- } else if (!type.isPrimitive()) {
- if (property.deepExport()) {
- dumpViewProperties(context, field.get(view), out, prefix +
- property.prefix());
- continue;
- }
}
- if (fieldValue == null) {
- fieldValue = field.get(view);
+ continue;
+ } else if (!info.returnType.isPrimitive()) {
+ if (info.property.deepExport()) {
+ dumpViewProperties(context, value, out, prefix + info.property.prefix());
+ continue;
}
-
- writeEntry(out, categoryPrefix + prefix, field.getName(), "", fieldValue);
- } catch (IllegalAccessException e) {
}
+
+ writeEntry(out, categoryPrefix + prefix, info.name, info.entrySuffix, value);
}
}
@@ -1721,91 +1670,40 @@ public class ViewDebug {
}
}
- private static Field[] capturedViewGetPropertyFields(Class<?> klass) {
- if (mCapturedViewFieldsForClasses == null) {
- mCapturedViewFieldsForClasses = new HashMap<Class<?>, Field[]>();
- }
- final HashMap<Class<?>, Field[]> map = mCapturedViewFieldsForClasses;
-
- Field[] fields = map.get(klass);
- if (fields != null) {
- return fields;
- }
-
- final ArrayList<Field> foundFields = new ArrayList<Field>();
- fields = klass.getFields();
-
- int count = fields.length;
- for (int i = 0; i < count; i++) {
- final Field field = fields[i];
- if (field.isAnnotationPresent(CapturedViewProperty.class)) {
- field.setAccessible(true);
- foundFields.add(field);
- }
- }
-
- fields = foundFields.toArray(new Field[foundFields.size()]);
- map.put(klass, fields);
-
- return fields;
- }
-
- private static Method[] capturedViewGetPropertyMethods(Class<?> klass) {
- if (mCapturedViewMethodsForClasses == null) {
- mCapturedViewMethodsForClasses = new HashMap<Class<?>, Method[]>();
- }
- final HashMap<Class<?>, Method[]> map = mCapturedViewMethodsForClasses;
-
- Method[] methods = map.get(klass);
- if (methods != null) {
- return methods;
+ private static PropertyInfo<CapturedViewProperty, ?>[] getCapturedViewProperties(
+ Class<?> klass) {
+ if (sCapturedViewProperties == null) {
+ sCapturedViewProperties = new HashMap<>();
}
+ final HashMap<Class<?>, PropertyInfo<CapturedViewProperty, ?>[]> map =
+ sCapturedViewProperties;
- final ArrayList<Method> foundMethods = new ArrayList<Method>();
- methods = klass.getMethods();
-
- int count = methods.length;
- for (int i = 0; i < count; i++) {
- final Method method = methods[i];
- if (method.getParameterTypes().length == 0 &&
- method.isAnnotationPresent(CapturedViewProperty.class) &&
- method.getReturnType() != Void.class) {
- method.setAccessible(true);
- foundMethods.add(method);
- }
+ PropertyInfo<CapturedViewProperty, ?>[] infos = map.get(klass);
+ if (infos == null) {
+ infos = convertToPropertyInfos(klass.getMethods(), klass.getFields(),
+ CapturedViewProperty.class);
+ map.put(klass, infos);
}
-
- methods = foundMethods.toArray(new Method[foundMethods.size()]);
- map.put(klass, methods);
-
- return methods;
+ return infos;
}
- private static String capturedViewExportMethods(Object obj, Class<?> klass,
- String prefix) {
-
+ private static String exportCapturedViewProperties(Object obj, Class<?> klass, String prefix) {
if (obj == null) {
return "null";
}
StringBuilder sb = new StringBuilder();
- final Method[] methods = capturedViewGetPropertyMethods(klass);
- int count = methods.length;
- for (int i = 0; i < count; i++) {
- final Method method = methods[i];
+ for (PropertyInfo<CapturedViewProperty, ?> pi : getCapturedViewProperties(klass)) {
try {
- Object methodValue = method.invoke(obj, (Object[]) null);
- final Class<?> returnType = method.getReturnType();
+ Object methodValue = pi.invoke(obj);
- CapturedViewProperty property = method.getAnnotation(CapturedViewProperty.class);
- if (property.retrieveReturn()) {
+ if (pi.property.retrieveReturn()) {
//we are interested in the second level data only
- sb.append(capturedViewExportMethods(methodValue, returnType, method.getName() + "#"));
+ sb.append(exportCapturedViewProperties(methodValue, pi.returnType,
+ pi.name + "#"));
} else {
- sb.append(prefix);
- sb.append(method.getName());
- sb.append("()=");
+ sb.append(prefix).append(pi.name).append(pi.entrySuffix).append("=");
if (methodValue != null) {
final String value = methodValue.toString().replace("\n", "\\n");
@@ -1813,47 +1711,10 @@ public class ViewDebug {
} else {
sb.append("null");
}
- sb.append("; ");
- }
- } catch (IllegalAccessException e) {
- //Exception IllegalAccess, it is OK here
- //we simply ignore this method
- } catch (InvocationTargetException e) {
- //Exception InvocationTarget, it is OK here
- //we simply ignore this method
- }
- }
- return sb.toString();
- }
-
- private static String capturedViewExportFields(Object obj, Class<?> klass, String prefix) {
- if (obj == null) {
- return "null";
- }
-
- StringBuilder sb = new StringBuilder();
- final Field[] fields = capturedViewGetPropertyFields(klass);
-
- int count = fields.length;
- for (int i = 0; i < count; i++) {
- final Field field = fields[i];
- try {
- Object fieldValue = field.get(obj);
-
- sb.append(prefix);
- sb.append(field.getName());
- sb.append("=");
-
- if (fieldValue != null) {
- final String value = fieldValue.toString().replace("\n", "\\n");
- sb.append(value);
- } else {
- sb.append("null");
+ sb.append(pi.valueSuffix).append(" ");
}
- sb.append(' ');
- } catch (IllegalAccessException e) {
- //Exception IllegalAccess, it is OK here
- //we simply ignore this field
+ } catch (Exception e) {
+ //It is OK here, we simply ignore this property
}
}
return sb.toString();
@@ -1869,8 +1730,7 @@ public class ViewDebug {
public static void dumpCapturedView(String tag, Object view) {
Class<?> klass = view.getClass();
StringBuilder sb = new StringBuilder(klass.getName() + ": ");
- sb.append(capturedViewExportFields(view, klass, ""));
- sb.append(capturedViewExportMethods(view, klass, ""));
+ sb.append(exportCapturedViewProperties(view, klass, ""));
Log.d(tag, sb.toString());
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 82a5fa979fd2..853a30226982 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2615,7 +2615,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|| actionMasked == MotionEvent.ACTION_CANCEL;
// Update list of touch targets for pointer down, if needed.
- final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
+ final boolean isMouseEvent = ev.getSource() == InputDevice.SOURCE_MOUSE;
+ final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0
+ && !isMouseEvent;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
@@ -2632,8 +2634,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
- final float x = ev.getX(actionIndex);
- final float y = ev.getY(actionIndex);
+ final float x =
+ isMouseEvent ? ev.getXCursorPosition() : ev.getX(actionIndex);
+ final float y =
+ isMouseEvent ? ev.getYCursorPosition() : ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9e946509f39d..faf394eca4c2 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -45,6 +45,7 @@ import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.graphics.BLASTBufferQueue;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.FrameInfo;
@@ -91,6 +92,7 @@ import android.view.SurfaceControl.Transaction;
import android.view.View.AttachInfo;
import android.view.View.FocusDirection;
import android.view.View.MeasureSpec;
+import android.view.WindowInsets.Type.InsetType;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -169,6 +171,8 @@ public final class ViewRootImpl implements ViewParent,
*/
private static final boolean MT_RENDERER_AVAILABLE = true;
+ private static final boolean USE_BLAST_BUFFERQUEUE = false;
+
/**
* If set to 2, the view system will switch from using rectangles retrieved from window to
* dispatch to the view hierarchy to using {@link InsetsController}, that derives the insets
@@ -474,6 +478,9 @@ public final class ViewRootImpl implements ViewParent,
@UnsupportedAppUsage
public final Surface mSurface = new Surface();
private final SurfaceControl mSurfaceControl = new SurfaceControl();
+ private SurfaceControl mBlastSurfaceControl;
+
+ private BLASTBufferQueue mBlastBufferQueue;
/**
* Transaction object that can be used to synchronize child SurfaceControl changes with
@@ -1283,6 +1290,11 @@ public final class ViewRootImpl implements ViewParent,
}
mWindowAttributes.privateFlags |= compatibleWindowFlag;
+ if (USE_BLAST_BUFFERQUEUE) {
+ mWindowAttributes.privateFlags =
+ WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
+ }
+
if (mWindowAttributes.preservePreviousSurfaceInsets) {
// Restore old surface insets.
mWindowAttributes.surfaceInsets.set(
@@ -1630,6 +1642,29 @@ public final class ViewRootImpl implements ViewParent,
return mBoundsLayer;
}
+ Surface getOrCreateBLASTSurface(int width, int height) {
+ if (mSurfaceControl == null || !mSurfaceControl.isValid()) {
+ return null;
+ }
+ if (mBlastSurfaceControl == null) {
+ mBlastSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
+ .setParent(mSurfaceControl)
+ .setName("BLAST")
+ .setBLASTLayer()
+ .build();
+ mBlastBufferQueue = new BLASTBufferQueue(
+ mBlastSurfaceControl, width, height);
+
+ }
+ mBlastBufferQueue.update(mSurfaceControl, width, height);
+
+ mTransaction.show(mBlastSurfaceControl)
+ .reparent(mBlastSurfaceControl, mSurfaceControl)
+ .apply();
+
+ return mBlastBufferQueue.getSurface();
+ }
+
private void setBoundsLayerCrop() {
// mWinFrame is already adjusted for surface insets. So offset it and use it as
// the cropping bounds.
@@ -1659,6 +1694,13 @@ public final class ViewRootImpl implements ViewParent,
}
mSurface.release();
mSurfaceControl.release();
+
+ if (mBlastBufferQueue != null) {
+ mTransaction.remove(mBlastSurfaceControl).apply();
+ mBlastSurfaceControl = null;
+ // We should probably add an explicit dispose.
+ mBlastBufferQueue = null;
+ }
}
/**
@@ -2414,10 +2456,9 @@ public final class ViewRootImpl implements ViewParent,
// will be transparent
if (mAttachInfo.mThreadedRenderer != null) {
try {
- hwInitialized = mAttachInfo.mThreadedRenderer.initialize(
- mSurface);
+ hwInitialized = mAttachInfo.mThreadedRenderer.initialize(mSurface);
if (hwInitialized && (host.mPrivateFlags
- & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0) {
+ & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0) {
// Don't pre-allocate if transparent regions
// are requested as they may not be needed
mAttachInfo.mThreadedRenderer.allocateBuffers();
@@ -4530,6 +4571,9 @@ public final class ViewRootImpl implements ViewParent,
private static final int MSG_INSETS_CONTROL_CHANGED = 31;
private static final int MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED = 32;
private static final int MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED = 33;
+ private static final int MSG_SHOW_INSETS = 34;
+ private static final int MSG_HIDE_INSETS = 35;
+
final class ViewRootHandler extends Handler {
@Override
@@ -4593,6 +4637,10 @@ public final class ViewRootImpl implements ViewParent,
return "MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED";
case MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED:
return "MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED";
+ case MSG_SHOW_INSETS:
+ return "MSG_SHOW_INSETS";
+ case MSG_HIDE_INSETS:
+ return "MSG_HIDE_INSETS";
}
return super.getMessageName(message);
}
@@ -4707,6 +4755,14 @@ public final class ViewRootImpl implements ViewParent,
mInsetsController.onStateChanged((InsetsState) args.arg1);
break;
}
+ case MSG_SHOW_INSETS: {
+ mInsetsController.show(msg.arg1, msg.arg2 == 1);
+ break;
+ }
+ case MSG_HIDE_INSETS: {
+ mInsetsController.hide(msg.arg1, msg.arg2 == 1);
+ break;
+ }
case MSG_WINDOW_MOVED:
if (mAdded) {
final int w = mWinFrame.width();
@@ -7138,7 +7194,13 @@ public final class ViewRootImpl implements ViewParent,
mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
mPendingMergedConfiguration, mSurfaceControl, mTempInsets);
if (mSurfaceControl.isValid()) {
- mSurface.copyFrom(mSurfaceControl);
+ if (USE_BLAST_BUFFERQUEUE == false) {
+ mSurface.copyFrom(mSurfaceControl);
+ } else {
+ mSurface.transferFrom(getOrCreateBLASTSurface(
+ (int) (mView.getMeasuredWidth() * appScale + 0.5f),
+ (int) (mView.getMeasuredHeight() * appScale + 0.5f)));
+ }
} else {
destroySurface();
}
@@ -7296,26 +7358,42 @@ public final class ViewRootImpl implements ViewParent,
}
}
- public void dumpGfxInfo(int[] info) {
- info[0] = info[1] = 0;
- if (mView != null) {
- getGfxInfo(mView, info);
+ static final class GfxInfo {
+ public int viewCount;
+ public long renderNodeMemoryUsage;
+ public long renderNodeMemoryAllocated;
+
+ void add(GfxInfo other) {
+ viewCount += other.viewCount;
+ renderNodeMemoryUsage += other.renderNodeMemoryUsage;
+ renderNodeMemoryAllocated += other.renderNodeMemoryAllocated;
}
}
- private static void getGfxInfo(View view, int[] info) {
- RenderNode renderNode = view.mRenderNode;
- info[0]++;
- if (renderNode != null) {
- info[1] += (int) renderNode.computeApproximateMemoryUsage();
+ GfxInfo getGfxInfo() {
+ GfxInfo info = new GfxInfo();
+ if (mView != null) {
+ appendGfxInfo(mView, info);
}
+ return info;
+ }
+ private static void computeRenderNodeUsage(RenderNode node, GfxInfo info) {
+ if (node == null) return;
+ info.renderNodeMemoryUsage += node.computeApproximateMemoryUsage();
+ info.renderNodeMemoryAllocated += node.computeApproximateMemoryAllocated();
+ }
+
+ private static void appendGfxInfo(View view, GfxInfo info) {
+ info.viewCount++;
+ computeRenderNodeUsage(view.mRenderNode, info);
+ computeRenderNodeUsage(view.mBackgroundRenderNode, info);
if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
int count = group.getChildCount();
for (int i = 0; i < count; i++) {
- getGfxInfo(group.getChildAt(i), info);
+ appendGfxInfo(group.getChildAt(i), info);
}
}
}
@@ -7492,6 +7570,14 @@ public final class ViewRootImpl implements ViewParent,
mHandler.obtainMessage(MSG_INSETS_CONTROL_CHANGED, args).sendToTarget();
}
+ private void showInsets(@InsetType int types, boolean fromIme) {
+ mHandler.obtainMessage(MSG_SHOW_INSETS, types, fromIme ? 1 : 0).sendToTarget();
+ }
+
+ private void hideInsets(@InsetType int types, boolean fromIme) {
+ mHandler.obtainMessage(MSG_HIDE_INSETS, types, fromIme ? 1 : 0).sendToTarget();
+ }
+
public void dispatchMoved(int newX, int newY) {
if (DEBUG_LAYOUT) Log.v(mTag, "Window moved " + this + ": newX=" + newX + " newY=" + newY);
if (mTranslator != null) {
@@ -8607,6 +8693,22 @@ public final class ViewRootImpl implements ViewParent,
}
@Override
+ public void showInsets(@InsetType int types, boolean fromIme) {
+ final ViewRootImpl viewAncestor = mViewAncestor.get();
+ if (viewAncestor != null) {
+ viewAncestor.showInsets(types, fromIme);
+ }
+ }
+
+ @Override
+ public void hideInsets(@InsetType int types, boolean fromIme) {
+ final ViewRootImpl viewAncestor = mViewAncestor.get();
+ if (viewAncestor != null) {
+ viewAncestor.hideInsets(types, fromIme);
+ }
+ }
+
+ @Override
public void moved(int newX, int newY) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 5f3ce33db19d..606e8f99999a 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -230,6 +230,8 @@ public abstract class ViewStructure {
/**
* Sets the identifier used to set the hint associated with this view.
*
+ * <p>Used as metadata for fingerprinting view nodes/structures.
+ *
* <p>Should only be set when the node is used for autofill purposes - it will be ignored
* when used for Assist.
*/
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index 396422ef5c57..b41531988518 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -35,33 +35,39 @@ import java.lang.annotation.RetentionPolicy;
public interface WindowInsetsController {
/**
- * Makes system bars become opaque with solid dark background and light foreground.
+ * Makes the top bars become opaque with solid dark background and light foreground.
* @hide
*/
- int APPEARANCE_OPAQUE_BARS = 1;
+ int APPEARANCE_OPAQUE_TOP_BAR = 1;
+
+ /**
+ * Makes the side bars become opaque with solid dark background and light foreground.
+ * @hide
+ */
+ int APPEARANCE_OPAQUE_SIDE_BARS = 1 << 1;
/**
* Makes items on system bars become less noticeable without changing the layout of the bars.
* @hide
*/
- int APPEARANCE_LOW_PROFILE_BARS = 1 << 1;
+ int APPEARANCE_LOW_PROFILE_BARS = 1 << 2;
/**
* Changes the foreground color for the light top bar so that the items on the bar can be read
* clearly.
*/
- int APPEARANCE_LIGHT_TOP_BAR = 1 << 2;
+ int APPEARANCE_LIGHT_TOP_BAR = 1 << 3;
/**
* Changes the foreground color for the light side bars so that the items on the bar can be read
* clearly.
*/
- int APPEARANCE_LIGHT_SIDE_BARS = 1 << 3;
+ int APPEARANCE_LIGHT_SIDE_BARS = 1 << 4;
/** Determines the appearance of system bars. */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, value = {APPEARANCE_OPAQUE_BARS, APPEARANCE_LOW_PROFILE_BARS,
- APPEARANCE_LIGHT_TOP_BAR, APPEARANCE_LIGHT_SIDE_BARS})
+ @IntDef(flag = true, value = {APPEARANCE_OPAQUE_TOP_BAR, APPEARANCE_OPAQUE_SIDE_BARS,
+ APPEARANCE_LOW_PROFILE_BARS, APPEARANCE_LIGHT_TOP_BAR, APPEARANCE_LIGHT_SIDE_BARS})
@interface Appearance {
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 001ab6650551..db76bb6d3cde 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -197,12 +197,6 @@ public interface WindowManager extends ViewManager {
int TRANSIT_TASK_OPEN_BEHIND = 16;
/**
- * A window in a task is being animated in-place.
- * @hide
- */
- int TRANSIT_TASK_IN_PLACE = 17;
-
- /**
* An activity is being relaunched (e.g. due to configuration change).
* @hide
*/
@@ -286,7 +280,6 @@ public interface WindowManager extends ViewManager {
TRANSIT_WALLPAPER_INTRA_OPEN,
TRANSIT_WALLPAPER_INTRA_CLOSE,
TRANSIT_TASK_OPEN_BEHIND,
- TRANSIT_TASK_IN_PLACE,
TRANSIT_ACTIVITY_RELAUNCH,
TRANSIT_DOCK_TASK_FROM_RECENTS,
TRANSIT_KEYGUARD_GOING_AWAY,
@@ -1687,8 +1680,9 @@ public interface WindowManager extends ViewManager {
* to determine its default behavior.
*
* {@hide} */
- @UnsupportedAppUsage
- public static final int PRIVATE_FLAG_SHOW_FOR_ALL_USERS = 0x00000010;
+ @SystemApi
+ @RequiresPermission(permission.INTERNAL_SYSTEM_WINDOW)
+ public static final int SYSTEM_FLAG_SHOW_FOR_ALL_USERS = 0x00000010;
/**
* Never animate position changes of the window.
@@ -1834,6 +1828,13 @@ public interface WindowManager extends ViewManager {
public static final int PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC = 0x01000000;
/**
+ * Flag to request creation of a BLAST (Buffer as LayerState) Layer.
+ * If not specified the client will receive a BufferQueue layer.
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_USE_BLAST = 0x02000000;
+
+ /**
* An internal annotation for flags that can be specified to {@link #softInputMode}.
*
* @hide
@@ -1842,6 +1843,7 @@ public interface WindowManager extends ViewManager {
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "SYSTEM_FLAG_" }, value = {
SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
+ SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
})
public @interface SystemFlags {}
@@ -1863,8 +1865,8 @@ public interface WindowManager extends ViewManager {
equals = PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS,
name = "WANTS_OFFSET_NOTIFICATIONS"),
@ViewDebug.FlagToString(
- mask = PRIVATE_FLAG_SHOW_FOR_ALL_USERS,
- equals = PRIVATE_FLAG_SHOW_FOR_ALL_USERS,
+ mask = SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
+ equals = SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
name = "SHOW_FOR_ALL_USERS"),
@ViewDebug.FlagToString(
mask = PRIVATE_FLAG_NO_MOVE_ANIMATION,
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 379acbecb613..55b2a2a4033a 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -604,26 +604,24 @@ public final class WindowManagerGlobal {
pw.println("\nView hierarchy:\n");
- int viewsCount = 0;
- int displayListsSize = 0;
- int[] info = new int[2];
+ ViewRootImpl.GfxInfo totals = new ViewRootImpl.GfxInfo();
for (int i = 0; i < count; i++) {
ViewRootImpl root = mRoots.get(i);
- root.dumpGfxInfo(info);
+ ViewRootImpl.GfxInfo info = root.getGfxInfo();
+ totals.add(info);
String name = getWindowName(root);
- pw.printf(" %s\n %d views, %.2f kB of display lists",
- name, info[0], info[1] / 1024.0f);
+ pw.printf(" %s\n %d views, %.2f kB of render nodes",
+ name, info.viewCount, info.renderNodeMemoryUsage / 1024.f);
pw.printf("\n\n");
-
- viewsCount += info[0];
- displayListsSize += info[1];
}
- pw.printf("\nTotal ViewRootImpl: %d\n", count);
- pw.printf("Total Views: %d\n", viewsCount);
- pw.printf("Total DisplayList: %.2f kB\n\n", displayListsSize / 1024.0f);
+ pw.printf("\nTotal %-15s: %d\n", "ViewRootImpl", count);
+ pw.printf("Total %-15s: %d\n", "attached Views", totals.viewCount);
+ pw.printf("Total %-15s: %.2f kB (used) / %.2f kB (capacity)\n\n", "RenderNode",
+ totals.renderNodeMemoryUsage / 1024.0f,
+ totals.renderNodeMemoryAllocated / 1024.0f);
}
} finally {
pw.flush();
diff --git a/core/java/android/view/WindowlessViewRoot.java b/core/java/android/view/WindowlessViewRoot.java
index b76e1fad563e..c2500b89073e 100644
--- a/core/java/android/view/WindowlessViewRoot.java
+++ b/core/java/android/view/WindowlessViewRoot.java
@@ -41,4 +41,12 @@ public class WindowlessViewRoot {
public void addView(View view, WindowManager.LayoutParams attrs) {
mViewRoot.setView(view, attrs, null);
}
+
+ public void relayout(WindowManager.LayoutParams attrs) {
+ mViewRoot.setLayoutParams(attrs, false);
+ mViewRoot.setReportNextDraw();
+ mWm.setCompletionCallback(mViewRoot.mWindow.asBinder(), (SurfaceControl.Transaction t) -> {
+ t.apply();
+ });
+ }
}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 430fb6dd9770..f4f7d0b33fd1 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -39,11 +39,27 @@ import java.util.HashMap;
class WindowlessWindowManager implements IWindowSession {
private final static String TAG = "WindowlessWindowManager";
+ private class State {
+ SurfaceControl mSurfaceControl;
+ WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
+ State(SurfaceControl sc, WindowManager.LayoutParams p) {
+ mSurfaceControl = sc;
+ mParams.copyFrom(p);
+ }
+ };
/**
* Used to store SurfaceControl we've built for clients to
* reconfigure them if relayout is called.
*/
- final HashMap<IBinder, SurfaceControl> mScForWindow = new HashMap<IBinder, SurfaceControl>();
+ final HashMap<IBinder, State> mStateForWindow = new HashMap<IBinder, State>();
+
+ public interface ResizeCompleteCallback {
+ public void finished(SurfaceControl.Transaction completion);
+ }
+
+ final HashMap<IBinder, ResizeCompleteCallback> mResizeCompletionForWindow =
+ new HashMap<IBinder, ResizeCompleteCallback>();
+
final SurfaceSession mSurfaceSession = new SurfaceSession();
final SurfaceControl mRootSurface;
final Configuration mConfiguration;
@@ -58,6 +74,19 @@ class WindowlessWindowManager implements IWindowSession {
mRealWm = WindowManagerGlobal.getWindowSession();
}
+ /**
+ * Utility API.
+ */
+ void setCompletionCallback(IBinder window, ResizeCompleteCallback callback) {
+ if (mResizeCompletionForWindow.get(window) != null) {
+ Log.w(TAG, "Unsupported overlapping resizes");
+ }
+ mResizeCompletionForWindow.put(window, callback);
+ }
+
+ /**
+ * IWindowSession implementation.
+ */
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
Rect outStableInsets, Rect outOutsets,
@@ -68,7 +97,7 @@ class WindowlessWindowManager implements IWindowSession {
.setName(attrs.getTitle().toString());
final SurfaceControl sc = b.build();
synchronized (this) {
- mScForWindow.put(window.asBinder(), sc);
+ mStateForWindow.put(window.asBinder(), new State(sc, attrs));
}
if ((attrs.inputFeatures &
@@ -104,34 +133,40 @@ class WindowlessWindowManager implements IWindowSession {
}
@Override
- public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
+ public int relayout(IWindow window, int seq, WindowManager.LayoutParams inAttrs,
int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets,
Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState) {
- SurfaceControl sc = null;
+ State state = null;
synchronized (this) {
- sc = mScForWindow.get(window.asBinder());
+ state = mStateForWindow.get(window.asBinder());
}
- if (sc == null) {
+ if (state == null) {
throw new IllegalArgumentException(
"Invalid window token (never added or removed already)");
}
+ SurfaceControl sc = state.mSurfaceControl;
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ if (inAttrs != null) {
+ state.mParams.copyFrom(inAttrs);
+ }
+ WindowManager.LayoutParams attrs = state.mParams;
+
final Rect surfaceInsets = attrs.surfaceInsets;
int width = surfaceInsets != null ?
- requestedWidth + surfaceInsets.left + surfaceInsets.right : requestedWidth;
+ attrs.width + surfaceInsets.left + surfaceInsets.right : attrs.width;
int height = surfaceInsets != null ?
- requestedHeight + surfaceInsets.top + surfaceInsets.bottom : requestedHeight;
+ attrs.height + surfaceInsets.top + surfaceInsets.bottom : attrs.height;
t.show(sc)
.setBufferSize(sc, width, height)
.setOpaque(sc, isOpaque(attrs))
.apply();
outSurfaceControl.copyFrom(sc);
- outFrame.set(0, 0, requestedWidth, requestedHeight);
+ outFrame.set(0, 0, attrs.width, attrs.height);
mergedConfiguration.setConfiguration(mConfiguration, mConfiguration);
@@ -165,6 +200,17 @@ class WindowlessWindowManager implements IWindowSession {
@Override
public void finishDrawing(android.view.IWindow window,
android.view.SurfaceControl.Transaction postDrawTransaction) {
+ synchronized (this) {
+ final ResizeCompleteCallback c =
+ mResizeCompletionForWindow.get(window.asBinder());
+ if (c == null) {
+ // No one wanted the callback, but it wasn't necessarily unexpected.
+ postDrawTransaction.apply();
+ return;
+ }
+ c.finished(postDrawTransaction);
+ mResizeCompletionForWindow.remove(window.asBinder());
+ }
}
@Override
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index d9fa9f24f1ae..bb10ef10d79e 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -259,23 +259,38 @@ public final class AccessibilityInteractionClient
}
/**
- * Gets the info for all windows.
+ * Gets the info for all windows of the default display.
*
* @param connectionId The id of a connection for interacting with the system.
* @return The {@link AccessibilityWindowInfo} list.
*/
public List<AccessibilityWindowInfo> getWindows(int connectionId) {
+ final SparseArray<List<AccessibilityWindowInfo>> windows =
+ getWindowsOnAllDisplays(connectionId);
+ if (windows.size() > 0) {
+ return windows.valueAt(Display.DEFAULT_DISPLAY);
+ }
+ return Collections.emptyList();
+ }
+
+ /**
+ * Gets the info for all windows of all displays.
+ *
+ * @param connectionId The id of a connection for interacting with the system.
+ * @return The SparseArray of {@link AccessibilityWindowInfo} list.
+ * The key of SparseArray is display ID.
+ */
+ public SparseArray<List<AccessibilityWindowInfo>> getWindowsOnAllDisplays(int connectionId) {
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
- SparseArray<List<AccessibilityWindowInfo>> allWindows =
+ SparseArray<List<AccessibilityWindowInfo>> windows =
sAccessibilityCache.getWindowsOnAllDisplays();
- List<AccessibilityWindowInfo> windows;
- if (allWindows != null) {
+ if (windows != null) {
if (DEBUG) {
Log.i(LOG_TAG, "Windows cache hit");
}
- return allWindows.valueAt(Display.DEFAULT_DISPLAY);
+ return windows;
}
if (DEBUG) {
Log.i(LOG_TAG, "Windows cache miss");
@@ -287,9 +302,7 @@ public final class AccessibilityInteractionClient
Binder.restoreCallingIdentity(identityToken);
}
if (windows != null) {
- allWindows = new SparseArray<>();
- allWindows.put(Display.DEFAULT_DISPLAY, windows);
- sAccessibilityCache.setWindowsOnAllDisplays(allWindows);
+ sAccessibilityCache.setWindowsOnAllDisplays(windows);
return windows;
}
} else {
@@ -298,9 +311,11 @@ public final class AccessibilityInteractionClient
}
}
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Error while calling remote getWindows", re);
+ Log.e(LOG_TAG, "Error while calling remote getWindowsOnAllDisplays", re);
}
- return Collections.emptyList();
+
+ final SparseArray<List<AccessibilityWindowInfo>> emptyWindows = new SparseArray<>();
+ return emptyWindows;
}
/**
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 2e9d881f72f9..cc2884075c39 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -21,6 +21,7 @@ import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_
import android.Manifest;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityServiceInfo.FeedbackType;
+import android.accessibilityservice.AccessibilityShortcutInfo;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -30,9 +31,13 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
+import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.os.Binder;
@@ -56,6 +61,9 @@ import android.view.accessibility.AccessibilityEvent.EventType;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IntPair;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -699,10 +707,10 @@ public final class AccessibilityManager {
try {
services = service.getEnabledAccessibilityServiceList(feedbackTypeFlags, userId);
if (DEBUG) {
- Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
+ Log.i(LOG_TAG, "Enabled AccessibilityServices " + services);
}
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
+ Log.e(LOG_TAG, "Error while obtaining the enabled AccessibilityServices. ", re);
}
if (mAccessibilityPolicy != null) {
services = mAccessibilityPolicy.getEnabledAccessibilityServiceList(
@@ -1257,6 +1265,64 @@ public final class AccessibilityManager {
return null;
}
+ /**
+ * Returns the {@link AccessibilityShortcutInfo}s of the installed accessibility shortcut
+ * targets, for specific user.
+ *
+ * @param context The context of the application.
+ * @param userId The user id.
+ * @return A list with {@link AccessibilityShortcutInfo}s.
+ * @hide
+ */
+ @NonNull
+ public List<AccessibilityShortcutInfo> getInstalledAccessibilityShortcutListAsUser(
+ @NonNull Context context, @UserIdInt int userId) {
+ final List<AccessibilityShortcutInfo> shortcutInfos = new ArrayList<>();
+ final int flags = PackageManager.GET_ACTIVITIES
+ | PackageManager.GET_META_DATA
+ | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+ final Intent actionMain = new Intent(Intent.ACTION_MAIN);
+ actionMain.addCategory(Intent.CATEGORY_ACCESSIBILITY_SHORTCUT_TARGET);
+
+ final PackageManager packageManager = context.getPackageManager();
+ final List<ResolveInfo> installedShortcutList =
+ packageManager.queryIntentActivitiesAsUser(actionMain, flags, userId);
+ for (int i = 0; i < installedShortcutList.size(); i++) {
+ final AccessibilityShortcutInfo shortcutInfo =
+ getShortcutInfo(context, installedShortcutList.get(i));
+ if (shortcutInfo != null) {
+ shortcutInfos.add(shortcutInfo);
+ }
+ }
+ return shortcutInfos;
+ }
+
+ /**
+ * Returns an {@link AccessibilityShortcutInfo} according to the given {@link ResolveInfo} of
+ * an activity.
+ *
+ * @param context The context of the application.
+ * @param resolveInfo The resolve info of an activity.
+ * @return The AccessibilityShortcutInfo.
+ */
+ @Nullable
+ private AccessibilityShortcutInfo getShortcutInfo(@NonNull Context context,
+ @NonNull ResolveInfo resolveInfo) {
+ final ActivityInfo activityInfo = resolveInfo.activityInfo;
+ if (activityInfo == null || activityInfo.metaData == null
+ || activityInfo.metaData.getInt(AccessibilityShortcutInfo.META_DATA) == 0) {
+ return null;
+ }
+ try {
+ return new AccessibilityShortcutInfo(context, activityInfo);
+ } catch (XmlPullParserException | IOException exp) {
+ Log.e(LOG_TAG, "Error while initializing AccessibilityShortcutInfo", exp);
+ }
+ return null;
+ }
+
private IAccessibilityManager getServiceLocked() {
if (mService == null) {
tryConnectToServiceLocked(null);
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index cf29ed712793..06e9d0dbf6d6 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -39,8 +39,10 @@ import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.AccessibilityClickableSpan;
+import android.text.style.AccessibilityReplacementSpan;
import android.text.style.AccessibilityURLSpan;
import android.text.style.ClickableSpan;
+import android.text.style.ReplacementSpan;
import android.text.style.URLSpan;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -2641,37 +2643,86 @@ public class AccessibilityNodeInfo implements Parcelable {
public void setText(CharSequence text) {
enforceNotSealed();
mOriginalText = text;
- // Replace any ClickableSpans in mText with placeholders
if (text instanceof Spanned) {
- ClickableSpan[] spans =
- ((Spanned) text).getSpans(0, text.length(), ClickableSpan.class);
- if (spans.length > 0) {
- Spannable spannable = new SpannableStringBuilder(text);
- for (int i = 0; i < spans.length; i++) {
- ClickableSpan span = spans[i];
- if ((span instanceof AccessibilityClickableSpan)
- || (span instanceof AccessibilityURLSpan)) {
- // We've already done enough
- break;
- }
- int spanToReplaceStart = spannable.getSpanStart(span);
- int spanToReplaceEnd = spannable.getSpanEnd(span);
- int spanToReplaceFlags = spannable.getSpanFlags(span);
- spannable.removeSpan(span);
- ClickableSpan replacementSpan = (span instanceof URLSpan)
- ? new AccessibilityURLSpan((URLSpan) span)
- : new AccessibilityClickableSpan(span.getId());
- spannable.setSpan(replacementSpan, spanToReplaceStart, spanToReplaceEnd,
- spanToReplaceFlags);
- }
- mText = spannable;
- return;
- }
+ CharSequence tmpText = text;
+ tmpText = replaceClickableSpan(tmpText);
+ tmpText = replaceReplacementSpan(tmpText);
+ mText = tmpText;
+ return;
}
mText = (text == null) ? null : text.subSequence(0, text.length());
}
/**
+ * Replaces any ClickableSpans in mText with placeholders.
+ *
+ * @param text The text.
+ *
+ * @return The spannable with ClickableSpan replacement.
+ */
+ private CharSequence replaceClickableSpan(CharSequence text) {
+ ClickableSpan[] clickableSpans =
+ ((Spanned) text).getSpans(0, text.length(), ClickableSpan.class);
+ Spannable spannable = new SpannableStringBuilder(text);
+ if (clickableSpans.length == 0) {
+ return text;
+ }
+ for (int i = 0; i < clickableSpans.length; i++) {
+ ClickableSpan span = clickableSpans[i];
+ if ((span instanceof AccessibilityClickableSpan)
+ || (span instanceof AccessibilityURLSpan)) {
+ // We've already done enough
+ break;
+ }
+ int spanToReplaceStart = spannable.getSpanStart(span);
+ int spanToReplaceEnd = spannable.getSpanEnd(span);
+ int spanToReplaceFlags = spannable.getSpanFlags(span);
+ spannable.removeSpan(span);
+ ClickableSpan replacementSpan = (span instanceof URLSpan)
+ ? new AccessibilityURLSpan((URLSpan) span)
+ : new AccessibilityClickableSpan(span.getId());
+ spannable.setSpan(replacementSpan, spanToReplaceStart, spanToReplaceEnd,
+ spanToReplaceFlags);
+ }
+ return spannable;
+ }
+
+ /**
+ * Replace any ImageSpans in mText with its content description.
+ *
+ * @param text The text.
+ *
+ * @return The spannable with ReplacementSpan replacement.
+ */
+ private CharSequence replaceReplacementSpan(CharSequence text) {
+ ReplacementSpan[] replacementSpans =
+ ((Spanned) text).getSpans(0, text.length(), ReplacementSpan.class);
+ SpannableStringBuilder spannable = new SpannableStringBuilder(text);
+ if (replacementSpans.length == 0) {
+ return text;
+ }
+ for (int i = 0; i < replacementSpans.length; i++) {
+ ReplacementSpan span = replacementSpans[i];
+ CharSequence replacementText = span.getContentDescription();
+ if (span instanceof AccessibilityReplacementSpan) {
+ // We've already done enough
+ break;
+ }
+ if (replacementText == null) {
+ continue;
+ }
+ int spanToReplaceStart = spannable.getSpanStart(span);
+ int spanToReplaceEnd = spannable.getSpanEnd(span);
+ int spanToReplaceFlags = spannable.getSpanFlags(span);
+ spannable.removeSpan(span);
+ ReplacementSpan replacementSpan = new AccessibilityReplacementSpan(replacementText);
+ spannable.setSpan(replacementSpan, spanToReplaceStart, spanToReplaceEnd,
+ spanToReplaceFlags);
+ }
+ return spannable;
+ }
+
+ /**
* Gets the hint text of this node. Only applies to nodes where text can be entered.
*
* @return The hint text.
diff --git a/core/java/android/view/accessibility/AccessibilityNodeProvider.java b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
index 4b25378755f1..f4c7b96b8edc 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeProvider.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
@@ -44,28 +44,126 @@ import java.util.List;
* View itself. Similarly the returned instance is responsible for performing accessibility
* actions on any virtual view or the root view itself. For example:
* </p>
- * <pre>
- * getAccessibilityNodeProvider(
- * if (mAccessibilityNodeProvider == null) {
- * mAccessibilityNodeProvider = new AccessibilityNodeProvider() {
- * public boolean performAction(int action, int virtualDescendantId) {
- * // Implementation.
- * return false;
+ * <div>
+ * <div class="ds-selector-tabs"><section><h3 id="kotlin">Kotlin</h3>
+ * <pre class="prettyprint lang-kotlin">
+ * // "view" is the View instance on which this class performs accessibility functions.
+ * class MyCalendarViewAccessibilityDelegate(
+ * private var view: MyCalendarView) : AccessibilityDelegate() {
+ * override fun getAccessibilityNodeProvider(host: View): AccessibilityNodeProvider {
+ * return object : AccessibilityNodeProvider() {
+ * override fun createAccessibilityNodeInfo(virtualViewId: Int):
+ * AccessibilityNodeInfo? {
+ * when (virtualViewId) {
+ * <var>host-view-id</var> -&gt; {
+ * val node = AccessibilityNodeInfo.obtain(view)
+ * node.addChild(view, <var>child-view-id</var>)
+ * // Set other attributes like screenReaderFocusable
+ * // and contentDescription.
+ * return node
+ * }
+ * <var>child-view-id</var> -&gt; {
+ * val node = AccessibilityNodeInfo
+ * .obtain(view, virtualViewId)
+ * node.setParent(view)
+ * node.addAction(ACTION_SCROLL_UP)
+ * node.addAction(ACTION_SCROLL_DOWN)
+ * // Set other attributes like focusable and visibleToUser.
+ * node.setBoundsInScreen(
+ * Rect(<var>coords-of-edges-relative-to-screen</var>))
+ * return node
+ * }
+ * else -&gt; return null
* }
+ * }
*
- * public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text,
- * int virtualDescendantId) {
- * // Implementation.
- * return null;
+ * override fun performAction(
+ * virtualViewId: Int,
+ * action: Int,
+ * arguments: Bundle
+ * ): Boolean {
+ * if (virtualViewId == <var>host-view-id</var>) {
+ * return view.performAccessibilityAction(action, arguments)
* }
+ * when (action) {
+ * ACTION_SCROLL_UP.id -&gt; {
+ * // Implement logic in a separate method.
+ * navigateToPreviousMonth()
*
- * public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualDescendantId) {
- * // Implementation.
- * return null;
+ * return true
+ * }
+ * ACTION_SCROLL_DOWN.id -&gt;
+ * // Implement logic in a separate method.
+ * navigateToNextMonth()
+ *
+ * return true
+ * else -&gt; return false
* }
- * });
- * return mAccessibilityNodeProvider;
+ * }
+ * }
+ * }
+ * }
* </pre>
+ * </section><section><h3 id="java">Java</h3>
+ * <pre class="prettyprint lang-java">
+ * final class MyCalendarViewAccessibilityDelegate extends AccessibilityDelegate {
+ * // The View instance on which this class performs accessibility functions.
+ * private final MyCalendarView view;
+ *
+ * MyCalendarViewAccessibilityDelegate(MyCalendarView view) {
+ * this.view = view;
+ * }
+ *
+ * &#64;Override
+ * public AccessibilityNodeProvider getAccessibilityNodeProvider(View host) {
+ * return new AccessibilityNodeProvider() {
+ * &#64;Override
+ * &#64;Nullable
+ * public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
+ * if (virtualViewId == <var>host-view-id</var>) {
+ * AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain(view);
+ * node.addChild(view, <var>child-view-id</var>);
+ * // Set other attributes like screenReaderFocusable and contentDescription.
+ * return node;
+ * } else if (virtualViewId == <var>child-view-id</var>) {
+ * AccessibilityNodeInfo node =
+ * AccessibilityNodeInfo.obtain(view, virtualViewId);
+ * node.setParent(view);
+ * node.addAction(ACTION_SCROLL_UP);
+ * node.addAction(ACTION_SCROLL_DOWN);
+ * // Set other attributes like focusable and visibleToUser.
+ * node.setBoundsInScreen(
+ * new Rect(<var>coordinates-of-edges-relative-to-screen</var>));
+ * return node;
+ * } else {
+ * return null;
+ * }
+ * }
+ *
+ * &#64;Override
+ * public boolean performAction(int virtualViewId, int action, Bundle arguments) {
+ * if (virtualViewId == <var>host-view-id</var>) {
+ * return view.performAccessibilityAction(action, arguments);
+ * }
+ *
+ * if (action == ACTION_SCROLL_UP.getId()) {
+ * // Implement logic in a separate method.
+ * navigateToPreviousMonth();
+ *
+ * return true;
+ * } else if (action == ACTION_SCROLL_DOWN.getId()) {
+ * // Implement logic in a separate method.
+ * navigateToNextMonth();
+ *
+ * return true;
+ * } else {
+ * return false;
+ * }
+ * }
+ * };
+ * }
+ * }
+ * </pre></section></div></div>
*/
public abstract class AccessibilityNodeProvider {
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl b/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl
index fdb25fb1ee61..c36c4aa5b00a 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl
@@ -17,3 +17,4 @@
package android.view.accessibility;
parcelable AccessibilityWindowInfo;
+parcelable AccessibilityWindowInfo.WindowListSparseArray;
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index 6a3af3478449..5fa8a6e0e06b 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -26,9 +26,12 @@ import android.os.Parcelable;
import android.text.TextUtils;
import android.util.LongArray;
import android.util.Pools.SynchronizedPool;
+import android.util.SparseArray;
import android.view.Display;
import android.view.accessibility.AccessibilityEvent.WindowsChangeTypes;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
@@ -797,4 +800,49 @@ public final class AccessibilityWindowInfo implements Parcelable {
return new AccessibilityWindowInfo[size];
}
};
+
+ /**
+ * Transfers a sparsearray with lists having {@link AccessibilityWindowInfo}s across an IPC.
+ * The key of this sparsearray is display Id.
+ *
+ * @hide
+ */
+ public static final class WindowListSparseArray
+ extends SparseArray<List<AccessibilityWindowInfo>> implements Parcelable {
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ final int count = size();
+ dest.writeInt(count);
+ for (int i = 0; i < count; i++) {
+ dest.writeParcelableList(valueAt(i), 0);
+ dest.writeInt(keyAt(i));
+ }
+ }
+
+ public static final Parcelable.Creator<WindowListSparseArray> CREATOR =
+ new Parcelable.Creator<WindowListSparseArray>() {
+ public WindowListSparseArray createFromParcel(
+ Parcel source) {
+ final WindowListSparseArray array = new WindowListSparseArray();
+ final ClassLoader loader = array.getClass().getClassLoader();
+ final int count = source.readInt();
+ for (int i = 0; i < count; i++) {
+ List<AccessibilityWindowInfo> windows = new ArrayList<>();
+ source.readParcelableList(windows, loader);
+ array.put(source.readInt(), windows);
+ }
+ return array;
+ }
+
+ public WindowListSparseArray[] newArray(int size) {
+ return new WindowListSparseArray[size];
+ }
+ };
+ }
}
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
index 5e02de451fa3..7487ec4d921c 100644
--- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -89,8 +89,13 @@ final class ChildContentCaptureSession extends ContentCaptureSession {
}
@Override
- public void internalNotifySessionLifecycle(boolean started) {
- getMainCaptureSession().notifySessionLifecycle(mId, started);
+ void internalNotifySessionResumed() {
+ getMainCaptureSession().notifySessionResumed();
+ }
+
+ @Override
+ void internalNotifySessionPaused() {
+ getMainCaptureSession().notifySessionPaused();
}
@Override
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index d22fac996dbb..6040abd4f5f6 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -404,14 +404,14 @@ public final class ContentCaptureManager {
@UiThread
public void onActivityResumed() {
if (mOptions.lite) return;
- getMainContentCaptureSession().notifySessionLifecycle(/* started= */ true);
+ getMainContentCaptureSession().notifySessionResumed();
}
/** @hide */
@UiThread
public void onActivityPaused() {
if (mOptions.lite) return;
- getMainContentCaptureSession().notifySessionLifecycle(/* started= */ false);
+ getMainContentCaptureSession().notifySessionPaused();
}
/** @hide */
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index cf08c18b019a..232d96ba7f43 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -439,17 +439,26 @@ public abstract class ContentCaptureSession implements AutoCloseable {
public abstract void internalNotifyViewTreeEvent(boolean started);
/**
- * Notifies the Content Capture Service that a session has paused/resumed.
- *
- * @param started whether session has resumed.
+ * Notifies the Content Capture Service that a session has resumed.
+ */
+ public final void notifySessionResumed() {
+ if (!isContentCaptureEnabled()) return;
+
+ internalNotifySessionResumed();
+ }
+
+ abstract void internalNotifySessionResumed();
+
+ /**
+ * Notifies the Content Capture Service that a session has paused.
*/
- public final void notifySessionLifecycle(boolean started) {
+ public final void notifySessionPaused() {
if (!isContentCaptureEnabled()) return;
- internalNotifySessionLifecycle(started);
+ internalNotifySessionPaused();
}
- abstract void internalNotifySessionLifecycle(boolean started);
+ abstract void internalNotifySessionPaused();
/**
* Creates a {@link ViewStructure} for a "standard" view.
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 349ef09cf059..96f224fef251 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -583,8 +583,13 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
}
@Override
- public void internalNotifySessionLifecycle(boolean started) {
- notifySessionLifecycle(mId, started);
+ public void internalNotifySessionResumed() {
+ notifySessionResumed(mId);
+ }
+
+ @Override
+ public void internalNotifySessionPaused() {
+ notifySessionPaused(mId);
}
@Override
@@ -642,9 +647,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
sendEvent(new ContentCaptureEvent(sessionId, type), FORCE_FLUSH);
}
- void notifySessionLifecycle(int sessionId, boolean started) {
- final int type = started ? TYPE_SESSION_RESUMED : TYPE_SESSION_PAUSED;
- sendEvent(new ContentCaptureEvent(sessionId, type), FORCE_FLUSH);
+ void notifySessionResumed(int sessionId) {
+ sendEvent(new ContentCaptureEvent(sessionId, TYPE_SESSION_RESUMED), FORCE_FLUSH);
+ }
+
+ void notifySessionPaused(int sessionId) {
+ sendEvent(new ContentCaptureEvent(sessionId, TYPE_SESSION_PAUSED), FORCE_FLUSH);
}
void notifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) {
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 032af1c5c7b5..6420d71216cd 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -525,16 +525,16 @@ public final class InputMethodManager {
final InputMethodManager fallbackImm =
viewRootImpl.mContext.getSystemService(InputMethodManager.class);
if (fallbackImm == null) {
- Log.e(TAG, "b/117267690: Failed to get non-null fallback IMM. view=" + view);
+ Log.v(TAG, "b/117267690: Failed to get non-null fallback IMM. view=" + view);
return null;
}
if (fallbackImm.mDisplayId != viewRootDisplayId) {
- Log.e(TAG, "b/117267690: Failed to get fallback IMM with expected displayId="
+ Log.v(TAG, "b/117267690: Failed to get fallback IMM with expected displayId="
+ viewRootDisplayId + " actual IMM#displayId=" + fallbackImm.mDisplayId
+ " view=" + view);
return null;
}
- Log.w(TAG, "b/117267690: Display ID mismatch found."
+ Log.v(TAG, "b/117267690: Display ID mismatch found."
+ " ViewRootImpl displayId=" + viewRootDisplayId
+ " InputMethodManager displayId=" + mDisplayId
+ ". Use the right InputMethodManager instance to avoid performance overhead.",
diff --git a/core/java/android/view/inspector/OWNERS b/core/java/android/view/inspector/OWNERS
index 4554fdc2bfa0..c2827cc31592 100644
--- a/core/java/android/view/inspector/OWNERS
+++ b/core/java/android/view/inspector/OWNERS
@@ -1,3 +1,3 @@
alanv@google.com
aurimas@google.com
-emberr@google.com
+emberrose@google.com
diff --git a/core/java/android/webkit/FindAddress.java b/core/java/android/webkit/FindAddress.java
index 9183227b3962..b146e3f614a2 100644
--- a/core/java/android/webkit/FindAddress.java
+++ b/core/java/android/webkit/FindAddress.java
@@ -154,7 +154,7 @@ class FindAddress {
// A house number component is "one" or a number, optionally
// followed by a single alphabetic character, or
- private static final String HOUSE_COMPONENT = "(?:one|\\d+([a-z](?=[^a-z]|$)|st|nd|rd|th)?)";
+ private static final String HOUSE_COMPONENT = "(?:one|[0-9]+([a-z](?=[^a-z]|$)|st|nd|rd|th)?)";
// House numbers are a repetition of |HOUSE_COMPONENT|, separated by -, and followed by
// a delimiter character.
@@ -253,10 +253,10 @@ class FindAddress {
Pattern.CASE_INSENSITIVE);
private static final Pattern sSuffixedNumberRe =
- Pattern.compile("(\\d+)(st|nd|rd|th)", Pattern.CASE_INSENSITIVE);
+ Pattern.compile("([0-9]+)(st|nd|rd|th)", Pattern.CASE_INSENSITIVE);
private static final Pattern sZipCodeRe =
- Pattern.compile("(?:\\d{5}(?:-\\d{4})?)" + WORD_END, Pattern.CASE_INSENSITIVE);
+ Pattern.compile("(?:[0-9]{5}(?:-[0-9]{4})?)" + WORD_END, Pattern.CASE_INSENSITIVE);
private static boolean checkHouseNumber(String houseNumber) {
// Make sure that there are at most 5 digits.
diff --git a/core/java/android/webkit/MimeTypeMap.java b/core/java/android/webkit/MimeTypeMap.java
index fc23c54e834e..358fdc78c0a0 100644
--- a/core/java/android/webkit/MimeTypeMap.java
+++ b/core/java/android/webkit/MimeTypeMap.java
@@ -19,7 +19,7 @@ package android.webkit;
import android.annotation.Nullable;
import android.text.TextUtils;
-import libcore.net.MimeMap;
+import libcore.content.type.MimeMap;
import java.util.regex.Pattern;
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 18d4d691f726..7282008f7e3a 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -193,26 +193,24 @@ public abstract class WebSettings {
}
/**
- * Used with {@link #setMixedContentMode}
- *
* In this mode, the WebView will allow a secure origin to load content from any other origin,
* even if that origin is insecure. This is the least secure mode of operation for the WebView,
* and where possible apps should not set this mode.
+ *
+ * @see #setMixedContentMode
*/
public static final int MIXED_CONTENT_ALWAYS_ALLOW = 0;
/**
- * Used with {@link #setMixedContentMode}
- *
* In this mode, the WebView will not allow a secure origin to load content from an insecure
* origin. This is the preferred and most secure mode of operation for the WebView and apps are
* strongly advised to use this mode.
+ *
+ * @see #setMixedContentMode
*/
public static final int MIXED_CONTENT_NEVER_ALLOW = 1;
/**
- * Used with {@link #setMixedContentMode}
- *
* In this mode, the WebView will attempt to be compatible with the approach of a modern web
* browser with regard to mixed content. Some insecure content may be allowed to be loaded by
* a secure origin and other types of content will be blocked. The types of content are allowed
@@ -221,6 +219,8 @@ public abstract class WebSettings {
* This mode is intended to be used by apps that are not in control of the content that they
* render but desire to operate in a reasonably secure environment. For highest security, apps
* are recommended to use {@link #MIXED_CONTENT_NEVER_ALLOW}.
+ *
+ * @see #setMixedContentMode
*/
public static final int MIXED_CONTENT_COMPATIBILITY_MODE = 2;
@@ -234,30 +234,30 @@ public abstract class WebSettings {
public @interface ForceDark {}
/**
- * Used with {@link #setForceDark}
- *
* Disable force dark, irrespective of the force dark mode of the WebView parent. In this mode,
* WebView content will always be rendered as-is, regardless of whether native views are being
* automatically darkened.
+ *
+ * @see #setForceDark
*/
public static final int FORCE_DARK_OFF = 0;
/**
- * Used with {@link #setForceDark}
- *
* Enable force dark dependent on the state of the WebView parent view. If the WebView parent
* view is being automatically force darkened
* (see: {@link android.view.View#setForceDarkAllowed}), then WebView content will be rendered
* so as to emulate a dark theme. WebViews that are not attached to the view hierarchy will not
* be inverted.
+ *
+ * @see #setForceDark
*/
public static final int FORCE_DARK_AUTO = 1;
/**
- * Used with {@link #setForceDark}
- *
* Unconditionally enable force dark. In this mode WebView content will always be rendered so
* as to emulate a dark theme.
+ *
+ * @see #setForceDark
*/
public static final int FORCE_DARK_ON = 2;
@@ -1471,6 +1471,7 @@ public abstract class WebSettings {
* Set the force dark mode for this WebView.
*
* @param forceDark the force dark mode to set.
+ * @see #getForceDark
*/
public void setForceDark(@ForceDark int forceDark) {
// Stub implementation to satisfy Roboelectrc shadows that don't override this yet.
@@ -1478,10 +1479,10 @@ public abstract class WebSettings {
/**
* Get the force dark mode for this WebView.
- *
- * The default force dark mode is {@link #FORCE_DARK_AUTO}
+ * The default force dark mode is {@link #FORCE_DARK_AUTO}.
*
* @return the currently set force dark mode.
+ * @see #setForceDark
*/
public @ForceDark int getForceDark() {
// Stub implementation to satisfy Roboelectrc shadows that don't override this yet.
@@ -1516,34 +1517,34 @@ public abstract class WebSettings {
public abstract @MenuItemFlags int getDisabledActionModeMenuItems();
/**
- * Used with {@link #setDisabledActionModeMenuItems}.
- *
* No menu items should be disabled.
+ *
+ * @see #setDisabledActionModeMenuItems
*/
public static final int MENU_ITEM_NONE = 0;
/**
- * Used with {@link #setDisabledActionModeMenuItems}.
- *
* Disable menu item "Share".
+ *
+ * @see #setDisabledActionModeMenuItems
*/
public static final int MENU_ITEM_SHARE = 1 << 0;
/**
- * Used with {@link #setDisabledActionModeMenuItems}.
- *
* Disable menu item "Web Search".
+ *
+ * @see #setDisabledActionModeMenuItems
*/
public static final int MENU_ITEM_WEB_SEARCH = 1 << 1;
/**
- * Used with {@link #setDisabledActionModeMenuItems}.
- *
* Disable all the action mode menu items for text processing.
* By default WebView searches for activities that are able to handle
* {@link android.content.Intent#ACTION_PROCESS_TEXT} and show them in the
* action mode menu. If this flag is set via {@link
* #setDisabledActionModeMenuItems}, these menu items will be disabled.
+ *
+ * @see #setDisabledActionModeMenuItems
*/
public static final int MENU_ITEM_PROCESS_TEXT = 1 << 2;
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 0830938b50b8..c59fac367adf 100755
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2592,7 +2592,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
if (isItemClickable(view)) {
addAccessibilityActionIfEnabled(info, isItemEnabled, AccessibilityAction.ACTION_CLICK);
- info.setClickable(true);
+ // A disabled item is a separator which should not be clickable.
+ info.setClickable(isItemEnabled);
}
if (isLongClickable()) {
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 80de6fc65f90..562cc4ffeeaa 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -1103,6 +1103,7 @@ class SimpleMonthView extends View {
}
node.setEnabled(isDayEnabled);
+ node.setClickable(true);
if (virtualViewId == mActivatedDay) {
// TODO: This should use activated once that's supported.
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f997d6878a9c..31f50555af1d 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -23,6 +23,7 @@ import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_C
import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
import android.R;
+import android.annotation.CallSuper;
import android.annotation.CheckResult;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
@@ -10446,10 +10447,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/**
* This method is called when the selection has changed, in case any
* subclasses would like to know.
+ * </p>
+ * <p class="note"><strong>Note:</strong> Always call the super implementation, which informs
+ * the accessibility subsystem about the selection change.
+ * </p>
*
* @param selStart The new selection start location.
* @param selEnd The new selection end location.
*/
+ @CallSuper
protected void onSelectionChanged(int selStart, int selEnd) {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED);
}
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 925a5894db06..0b15cd06a7ea 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -246,7 +246,7 @@ public class AccessibilityShortcutController {
Toast warningToast = mFrameworkObjectProvider.makeToastFromText(
mContext, toastMessage, Toast.LENGTH_LONG);
warningToast.getWindowParams().privateFlags |=
- WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
warningToast.show();
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index cae1f3831b4a..00c1e2977c03 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2576,6 +2576,34 @@ public class ChooserActivity extends ResolverActivity {
}
}
+ /**
+ * Rather than fully sorting the input list, this sorting task will put the top k elements
+ * in the head of input list and fill the tail with other elements in undetermined order.
+ */
+ @Override
+ AsyncTask<List<ResolvedComponentInfo>,
+ Void,
+ List<ResolvedComponentInfo>> createSortingTask() {
+ return new AsyncTask<List<ResolvedComponentInfo>,
+ Void,
+ List<ResolvedComponentInfo>>() {
+ @Override
+ protected List<ResolvedComponentInfo> doInBackground(
+ List<ResolvedComponentInfo>... params) {
+ mResolverListController.topK(params[0],
+ getMaxRankedTargets());
+ return params[0];
+ }
+
+ @Override
+ protected void onPostExecute(List<ResolvedComponentInfo> sortedComponents) {
+ processSortedList(sortedComponents);
+ bindProfileView();
+ notifyDataSetChanged();
+ }
+ };
+ }
+
@Override
public void onListRebuilt() {
updateAlphabeticalList();
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 8f6c9500f54f..3ab0b0d2294b 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -31,25 +31,27 @@ interface IAppOpsService {
// be kept in sync with frameworks/native/libs/binder/include/binder/IAppOpsService.h
// and not be reordered
int checkOperation(int code, int uid, String packageName);
- int noteOperation(int code, int uid, String packageName);
- int startOperation(IBinder token, int code, int uid, String packageName,
+ int noteOperation(int code, int uid, String packageName, String featureId);
+ int startOperation(IBinder token, int code, int uid, String packageName, String featureId,
boolean startIfModeDefault);
@UnsupportedAppUsage
- void finishOperation(IBinder token, int code, int uid, String packageName);
+ void finishOperation(IBinder token, int code, int uid, String packageName,
+ String featureId);
void startWatchingMode(int op, String packageName, IAppOpsCallback callback);
void stopWatchingMode(IAppOpsCallback callback);
IBinder getToken(IBinder clientToken);
int permissionToOpCode(String permission);
int checkAudioOperation(int code, int usage, int uid, String packageName);
void noteAsyncOp(String callingPackageName, int uid, String packageName, int opCode,
- String message);
+ String featureId, String message);
boolean shouldCollectNotes(int opCode);
void setCameraAudioRestriction(int mode);
// End of methods also called by native code.
// Any new method exposed to native must be added after the last one, do not reorder
- int noteProxyOperation(int code, int proxyUid, String proxyPackageName,
- int callingUid, String callingPackageName);
+ int noteProxyOperation(int code, int proxiedUid, String proxiedPackageName,
+ String proxiedFeatureId, int proxyUid, String proxyPackageName,
+ String proxyFeatureId);
// Remaining methods are only used in Java.
int checkPackage(int uid, String packageName);
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 407a85f1bb05..7f18ae35b00c 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -388,21 +388,24 @@ public class ResolverActivity extends Activity {
mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
mSystemWindowInsets.right, 0);
- View emptyView = findViewById(R.id.empty);
- if (emptyView != null) {
- emptyView.setPadding(0, 0, 0, mSystemWindowInsets.bottom
- + getResources().getDimensionPixelSize(
- R.dimen.chooser_edge_margin_normal) * 2);
- }
-
- if (mFooterSpacer == null) {
- mFooterSpacer = new Space(getApplicationContext());
+ // Need extra padding so the list can fully scroll up
+ if (useLayoutWithDefault()) {
+ if (mFooterSpacer == null) {
+ mFooterSpacer = new Space(getApplicationContext());
+ } else {
+ ((ListView) mAdapterView).removeFooterView(mFooterSpacer);
+ }
+ mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
+ mSystemWindowInsets.bottom));
+ ((ListView) mAdapterView).addFooterView(mFooterSpacer);
} else {
- ((ListView) mAdapterView).removeFooterView(mFooterSpacer);
+ View emptyView = findViewById(R.id.empty);
+ if (emptyView != null) {
+ emptyView.setPadding(0, 0, 0, mSystemWindowInsets.bottom
+ + getResources().getDimensionPixelSize(
+ R.dimen.chooser_edge_margin_normal) * 2);
+ }
}
- mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
- mSystemWindowInsets.bottom));
- ((ListView) mAdapterView).addFooterView(mFooterSpacer);
resetButtonBar();
@@ -561,7 +564,7 @@ public class ResolverActivity extends Activity {
intent.getData().getHost(),
mAdapter.getFilteredItem().getDisplayLabel());
} else if (mAdapter.areAllTargetsBrowsers()) {
- dialogTitle = getString(ActionTitle.BROWSABLE_TITLE_RES);
+ dialogTitle = getString(ActionTitle.BROWSABLE_TITLE_RES);
} else {
dialogTitle = getString(ActionTitle.BROWSABLE_HOST_TITLE_RES,
intent.getData().getHost());
@@ -1304,6 +1307,7 @@ public class ResolverActivity extends Activity {
// In case this method is called again (due to activity recreation), avoid adding a new
// header if one is already present.
if (useHeader && listView != null && listView.getHeaderViewsCount() == 0) {
+ listView.setHeaderDividersEnabled(true);
listView.addHeaderView(LayoutInflater.from(this).inflate(
R.layout.resolver_different_item_header, listView, false));
}
@@ -1346,11 +1350,13 @@ public class ResolverActivity extends Activity {
final ViewGroup buttonLayout = findViewById(R.id.button_bar);
if (buttonLayout != null) {
buttonLayout.setVisibility(View.VISIBLE);
- int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0;
- buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(),
- buttonLayout.getPaddingRight(), getResources().getDimensionPixelSize(
- R.dimen.resolver_button_bar_spacing) + inset);
+ if (!useLayoutWithDefault()) {
+ int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0;
+ buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(),
+ buttonLayout.getPaddingRight(), getResources().getDimensionPixelSize(
+ R.dimen.resolver_button_bar_spacing) + inset);
+ }
mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
@@ -1618,7 +1624,7 @@ public class ResolverActivity extends Activity {
private final List<ResolveInfo> mBaseResolveList;
protected ResolveInfo mLastChosen;
private DisplayResolveInfo mOtherProfile;
- private ResolverListController mResolverListController;
+ ResolverListController mResolverListController;
private int mPlaceholderCount;
private boolean mAllTargetsAreBrowsers = false;
@@ -1779,27 +1785,7 @@ public class ResolverActivity extends Activity {
--placeholderCount;
}
setPlaceholderCount(placeholderCount);
- AsyncTask<List<ResolvedComponentInfo>,
- Void,
- List<ResolvedComponentInfo>> sortingTask =
- new AsyncTask<List<ResolvedComponentInfo>,
- Void,
- List<ResolvedComponentInfo>>() {
- @Override
- protected List<ResolvedComponentInfo> doInBackground(
- List<ResolvedComponentInfo>... params) {
- mResolverListController.sort(params[0]);
- return params[0];
- }
-
- @Override
- protected void onPostExecute(List<ResolvedComponentInfo> sortedComponents) {
- processSortedList(sortedComponents);
- bindProfileView();
- notifyDataSetChanged();
- }
- };
- sortingTask.execute(currentResolveList);
+ createSortingTask().execute(currentResolveList);
postListReadyRunnable();
return false;
} else {
@@ -1812,8 +1798,29 @@ public class ResolverActivity extends Activity {
}
}
+ AsyncTask<List<ResolvedComponentInfo>,
+ Void,
+ List<ResolvedComponentInfo>> createSortingTask() {
+ return new AsyncTask<List<ResolvedComponentInfo>,
+ Void,
+ List<ResolvedComponentInfo>>() {
+ @Override
+ protected List<ResolvedComponentInfo> doInBackground(
+ List<ResolvedComponentInfo>... params) {
+ mResolverListController.sort(params[0]);
+ return params[0];
+ }
+
+ @Override
+ protected void onPostExecute(List<ResolvedComponentInfo> sortedComponents) {
+ processSortedList(sortedComponents);
+ bindProfileView();
+ notifyDataSetChanged();
+ }
+ };
+ }
- private void processSortedList(List<ResolvedComponentInfo> sortedComponents) {
+ void processSortedList(List<ResolvedComponentInfo> sortedComponents) {
int N;
if (sortedComponents != null && (N = sortedComponents.size()) != 0) {
mAllTargetsAreBrowsers = mUseLayoutForBrowsables;
@@ -2057,7 +2064,9 @@ public class ResolverActivity extends Activity {
CharSequence subLabel = info.getExtendedInfo();
if (TextUtils.equals(label, subLabel)) subLabel = null;
- if (!TextUtils.equals(holder.text2.getText(), subLabel)) {
+ if (!TextUtils.equals(holder.text2.getText(), subLabel)
+ && !TextUtils.isEmpty(subLabel)) {
+ holder.text2.setVisibility(View.VISIBLE);
holder.text2.setText(subLabel);
}
diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java
index 5f92cddbaa38..28a8a8631372 100644
--- a/core/java/com/android/internal/app/ResolverListController.java
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -35,6 +35,7 @@ import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.PriorityQueue;
import java.util.concurrent.CountDownLatch;
/**
@@ -115,14 +116,6 @@ public class ResolverListController {
flags |= PackageManager.MATCH_INSTANT;
}
final List<ResolveInfo> infos = mpm.queryIntentActivities(intent, flags);
- // Remove any activities that are not exported.
- int totalSize = infos.size();
- for (int j = totalSize - 1; j >= 0 ; j--) {
- ResolveInfo info = infos.get(j);
- if (info.activityInfo != null && !info.activityInfo.exported) {
- infos.remove(j);
- }
- }
if (infos != null) {
if (resolvedComponents == null) {
resolvedComponents = new ArrayList<>();
@@ -238,22 +231,27 @@ public class ResolverListController {
}
}
- @VisibleForTesting
- @WorkerThread
- public void sort(List<ResolverActivity.ResolvedComponentInfo> inputList) {
+ private void compute(List<ResolverActivity.ResolvedComponentInfo> inputList)
+ throws InterruptedException {
if (mResolverComparator == null) {
Log.d(TAG, "Comparator has already been destroyed; skipped.");
return;
}
+ final CountDownLatch finishComputeSignal = new CountDownLatch(1);
+ ComputeCallback callback = new ComputeCallback(finishComputeSignal);
+ mResolverComparator.setCallBack(callback);
+ mResolverComparator.compute(inputList);
+ finishComputeSignal.await();
+ isComputed = true;
+ }
+
+ @VisibleForTesting
+ @WorkerThread
+ public void sort(List<ResolverActivity.ResolvedComponentInfo> inputList) {
try {
long beforeRank = System.currentTimeMillis();
if (!isComputed) {
- final CountDownLatch finishComputeSignal = new CountDownLatch(1);
- ComputeCallback callback = new ComputeCallback(finishComputeSignal);
- mResolverComparator.setCallBack(callback);
- mResolverComparator.compute(inputList);
- finishComputeSignal.await();
- isComputed = true;
+ compute(inputList);
}
Collections.sort(inputList, mResolverComparator);
@@ -266,6 +264,61 @@ public class ResolverListController {
}
}
+ @VisibleForTesting
+ @WorkerThread
+ public void topK(List<ResolverActivity.ResolvedComponentInfo> inputList, int k) {
+ if (inputList == null || inputList.isEmpty() || k <= 0) {
+ return;
+ }
+ if (inputList.size() <= k) {
+ // Fall into normal sort when number of ranked elements
+ // needed is not smaller than size of input list.
+ sort(inputList);
+ return;
+ }
+ try {
+ long beforeRank = System.currentTimeMillis();
+ if (!isComputed) {
+ compute(inputList);
+ }
+
+ // Top of this heap has lowest rank.
+ PriorityQueue<ResolverActivity.ResolvedComponentInfo> minHeap = new PriorityQueue<>(k,
+ (o1, o2) -> -mResolverComparator.compare(o1, o2));
+ final int size = inputList.size();
+ // Use this pointer to keep track of the position of next element
+ // to update in input list, starting from the last position.
+ int pointer = size - 1;
+ minHeap.addAll(inputList.subList(size - k, size));
+ for (int i = size - k - 1; i >= 0; --i) {
+ ResolverActivity.ResolvedComponentInfo ci = inputList.get(i);
+ if (-mResolverComparator.compare(ci, minHeap.peek()) > 0) {
+ // When ranked higher than top of heap, remove top of heap,
+ // update input list with it, add this new element to heap.
+ inputList.set(pointer--, minHeap.poll());
+ minHeap.add(ci);
+ } else {
+ // When ranked no higher than top of heap, update input list
+ // with this new element.
+ inputList.set(pointer--, ci);
+ }
+ }
+
+ // Now we have top k elements in heap, update first
+ // k positions of input list with them.
+ while (!minHeap.isEmpty()) {
+ inputList.set(pointer--, minHeap.poll());
+ }
+
+ long afterRank = System.currentTimeMillis();
+ if (DEBUG) {
+ Log.d(TAG, "Time Cost for top " + k + " targets: "
+ + Long.toString(afterRank - beforeRank));
+ }
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Compute & greatestOf was interrupted: " + e);
+ }
+ }
private static boolean isSameResolvedComponent(ResolveInfo a,
ResolverActivity.ResolvedComponentInfo b) {
diff --git a/core/java/com/android/internal/app/procstats/AssociationState.java b/core/java/com/android/internal/app/procstats/AssociationState.java
index 2df515835026..1d4239f7c6ed 100644
--- a/core/java/com/android/internal/app/procstats/AssociationState.java
+++ b/core/java/com/android/internal/app/procstats/AssociationState.java
@@ -16,7 +16,6 @@
package com.android.internal.app.procstats;
-
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.SystemClock;
@@ -24,23 +23,37 @@ import android.os.UserHandle;
import android.service.procstats.PackageAssociationProcessStatsProto;
import android.service.procstats.PackageAssociationSourceProcessStatsProto;
import android.util.ArrayMap;
+import android.util.Pair;
import android.util.Slog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.Objects;
public final class AssociationState {
private static final String TAG = "ProcessStats";
private static final boolean DEBUG = false;
+ private static final boolean VALIDATE_TIMES = false;
+
private final ProcessStats mProcessStats;
private final ProcessStats.PackageState mPackageState;
private final String mProcessName;
private final String mName;
+ private int mTotalNesting;
+ private long mTotalStartUptime;
+ private int mTotalCount;
+ private long mTotalDuration;
+ private int mTotalActiveNesting;
+ private long mTotalActiveStartUptime;
+ private int mTotalActiveCount;
+ private long mTotalActiveDuration;
+
public final class SourceState {
final SourceKey mKey;
int mProcStateSeq = -1;
@@ -55,7 +68,7 @@ public final class AssociationState {
int mActiveProcState = ProcessStats.STATE_NOTHING;
long mActiveStartUptime;
long mActiveDuration;
- DurationsTable mDurations;
+ DurationsTable mActiveDurations;
SourceState(SourceKey key) {
mKey = key;
@@ -97,9 +110,9 @@ public final class AssociationState {
public void stop() {
mNesting--;
if (mNesting == 0) {
- mDuration += SystemClock.uptimeMillis() - mStartUptime;
- mNumActive--;
- stopTracking(SystemClock.uptimeMillis());
+ final long now = SystemClock.uptimeMillis();
+ mDuration += now - mStartUptime;
+ stopTracking(now);
}
}
@@ -108,20 +121,25 @@ public final class AssociationState {
if (mActiveStartUptime == 0) {
mActiveStartUptime = now;
mActiveCount++;
+ AssociationState.this.mTotalActiveNesting++;
+ if (AssociationState.this.mTotalActiveNesting == 1) {
+ AssociationState.this.mTotalActiveCount++;
+ AssociationState.this.mTotalActiveStartUptime = now;
+ }
}
if (mActiveProcState != mProcState) {
if (mActiveProcState != ProcessStats.STATE_NOTHING) {
// Currently active proc state changed, need to store the duration
// so far and switch tracking to the new proc state.
- final long duration = mActiveDuration + now - mActiveStartUptime;
- if (duration != 0) {
- if (mDurations == null) {
+ final long addedDuration = mActiveDuration + now - mActiveStartUptime;
+ mActiveStartUptime = now;
+ if (addedDuration != 0) {
+ if (mActiveDurations == null) {
makeDurations();
}
- mDurations.addDuration(mActiveProcState, duration);
+ mActiveDurations.addDuration(mActiveProcState, addedDuration);
mActiveDuration = 0;
}
- mActiveStartUptime = now;
}
mActiveProcState = mProcState;
}
@@ -135,21 +153,44 @@ public final class AssociationState {
if (!mInTrackingList) {
Slog.wtf(TAG, "stopActive while not tracking: " + this);
}
- final long duration = mActiveDuration + now - mActiveStartUptime;
- if (mDurations != null) {
- mDurations.addDuration(mActiveProcState, duration);
+ final long addedDuration = now - mActiveStartUptime;
+ mActiveStartUptime = 0;
+ if (mActiveDurations != null) {
+ mActiveDurations.addDuration(mActiveProcState, addedDuration);
} else {
- mActiveDuration = duration;
+ mActiveDuration += addedDuration;
+ }
+ AssociationState.this.mTotalActiveNesting--;
+ if (AssociationState.this.mTotalActiveNesting == 0) {
+ AssociationState.this.mTotalActiveDuration += now
+ - AssociationState.this.mTotalActiveStartUptime;
+ AssociationState.this.mTotalActiveStartUptime = 0;
+ if (VALIDATE_TIMES) {
+ if (mActiveDuration > AssociationState.this.mTotalActiveDuration) {
+ RuntimeException ex = new RuntimeException();
+ ex.fillInStackTrace();
+ Slog.w(TAG, "Source act duration " + mActiveDurations
+ + " exceeds total " + AssociationState.this.mTotalActiveDuration
+ + " in procstate " + mActiveProcState + " in source "
+ + mKey.mProcess + " to assoc "
+ + AssociationState.this.mName, ex);
+ }
+
+ }
}
- mActiveStartUptime = 0;
}
}
void makeDurations() {
- mDurations = new DurationsTable(mProcessStats.mTableData);
+ mActiveDurations = new DurationsTable(mProcessStats.mTableData);
}
void stopTracking(long now) {
+ AssociationState.this.mTotalNesting--;
+ if (AssociationState.this.mTotalNesting == 0) {
+ AssociationState.this.mTotalDuration += now
+ - AssociationState.this.mTotalStartUptime;
+ }
stopActive(now);
if (mInTrackingList) {
mInTrackingList = false;
@@ -181,7 +222,17 @@ public final class AssociationState {
}
}
- private final static class SourceKey {
+ public final class SourceDumpContainer {
+ public final SourceState mState;
+ public long mTotalTime;
+ public long mActiveTime;
+
+ public SourceDumpContainer(SourceState state) {
+ mState = state;
+ }
+ }
+
+ public static final class SourceKey {
/**
* UID, consider this final. Not final just to avoid a temporary object during lookup.
*/
@@ -239,12 +290,10 @@ public final class AssociationState {
*/
private final ArrayMap<SourceKey, SourceState> mSources = new ArrayMap<>();
- private final SourceKey mTmpSourceKey = new SourceKey(0, null, null);
+ private static final SourceKey sTmpSourceKey = new SourceKey(0, null, null);
private ProcessState mProc;
- private int mNumActive;
-
public AssociationState(ProcessStats processStats, ProcessStats.PackageState packageState,
String name, String processName, ProcessState proc) {
mProcessStats = processStats;
@@ -278,11 +327,24 @@ public final class AssociationState {
mProc = proc;
}
+ public long getTotalDuration(long now) {
+ return mTotalDuration
+ + (mTotalNesting > 0 ? (now - mTotalStartUptime) : 0);
+ }
+
+ public long getActiveDuration(long now) {
+ return mTotalActiveDuration
+ + (mTotalActiveNesting > 0 ? (now - mTotalActiveStartUptime) : 0);
+ }
+
public SourceState startSource(int uid, String processName, String packageName) {
- mTmpSourceKey.mUid = uid;
- mTmpSourceKey.mProcess = processName;
- mTmpSourceKey.mPackage = packageName;
- SourceState src = mSources.get(mTmpSourceKey);
+ SourceState src;
+ synchronized (sTmpSourceKey) {
+ sTmpSourceKey.mUid = uid;
+ sTmpSourceKey.mProcess = processName;
+ sTmpSourceKey.mPackage = packageName;
+ src = mSources.get(sTmpSourceKey);
+ }
if (src == null) {
SourceKey key = new SourceKey(uid, processName, packageName);
src = new SourceState(key);
@@ -290,43 +352,88 @@ public final class AssociationState {
}
src.mNesting++;
if (src.mNesting == 1) {
+ final long now = SystemClock.uptimeMillis();
src.mCount++;
- src.mStartUptime = SystemClock.uptimeMillis();
- mNumActive++;
+ src.mStartUptime = now;
+ mTotalNesting++;
+ if (mTotalNesting == 1) {
+ mTotalCount++;
+ mTotalStartUptime = now;
+ }
}
return src;
}
public void add(AssociationState other) {
+ mTotalCount += other.mTotalCount;
+ final long origDuration = mTotalDuration;
+ mTotalDuration += other.mTotalDuration;
+ mTotalActiveCount += other.mTotalActiveCount;
+ mTotalActiveDuration += other.mTotalActiveDuration;
for (int isrc = other.mSources.size() - 1; isrc >= 0; isrc--) {
final SourceKey key = other.mSources.keyAt(isrc);
final SourceState otherSrc = other.mSources.valueAt(isrc);
SourceState mySrc = mSources.get(key);
+ boolean newSrc = false;
if (mySrc == null) {
mySrc = new SourceState(key);
mSources.put(key, mySrc);
+ newSrc = true;
+ }
+ if (VALIDATE_TIMES) {
+ Slog.w(TAG, "Adding tot duration " + mySrc.mDuration + "+"
+ + otherSrc.mDuration
+ + (newSrc ? " (new)" : " (old)") + " (total "
+ + origDuration + "+" + other.mTotalDuration + ") in source "
+ + mySrc.mKey.mProcess + " to assoc " + mName);
+ if ((mySrc.mDuration + otherSrc.mDuration) > mTotalDuration) {
+ RuntimeException ex = new RuntimeException();
+ ex.fillInStackTrace();
+ Slog.w(TAG, "Source tot duration " + mySrc.mDuration + "+"
+ + otherSrc.mDuration
+ + (newSrc ? " (new)" : " (old)") + " exceeds total "
+ + origDuration + "+" + other.mTotalDuration + " in source "
+ + mySrc.mKey.mProcess + " to assoc " + mName, ex);
+ }
+ if (mySrc.mActiveDurations == null && otherSrc.mActiveDurations == null) {
+ Slog.w(TAG, "Adding act duration " + mySrc.mActiveDuration
+ + "+" + otherSrc.mActiveDuration
+ + (newSrc ? " (new)" : " (old)") + " (total "
+ + origDuration + "+" + other.mTotalDuration + ") in source "
+ + mySrc.mKey.mProcess + " to assoc " + mName);
+ if ((mySrc.mActiveDuration + otherSrc.mActiveDuration) > mTotalDuration) {
+ RuntimeException ex = new RuntimeException();
+ ex.fillInStackTrace();
+ Slog.w(TAG, "Source act duration " + mySrc.mActiveDuration + "+"
+ + otherSrc.mActiveDuration
+ + (newSrc ? " (new)" : " (old)") + " exceeds total "
+ + origDuration + "+" + other.mTotalDuration + " in source "
+ + mySrc.mKey.mProcess + " to assoc " + mName, ex);
+ }
+ }
}
mySrc.mCount += otherSrc.mCount;
mySrc.mDuration += otherSrc.mDuration;
mySrc.mActiveCount += otherSrc.mActiveCount;
- if (otherSrc.mActiveDuration != 0 || otherSrc.mDurations != null) {
+ if (otherSrc.mActiveDuration != 0 || otherSrc.mActiveDurations != null) {
// Only need to do anything if the other one has some duration data.
- if (mySrc.mDurations != null) {
+ if (mySrc.mActiveDurations != null) {
// If the target already has multiple durations, just add in whatever
// we have in the other.
- if (otherSrc.mDurations != null) {
- mySrc.mDurations.addDurations(otherSrc.mDurations);
+ if (otherSrc.mActiveDurations != null) {
+ mySrc.mActiveDurations.addDurations(otherSrc.mActiveDurations);
} else {
- mySrc.mDurations.addDuration(otherSrc.mActiveProcState,
+ mySrc.mActiveDurations.addDuration(otherSrc.mActiveProcState,
otherSrc.mActiveDuration);
}
- } else if (otherSrc.mDurations != null) {
+ } else if (otherSrc.mActiveDurations != null) {
// The other one has multiple durations, but we don't. Expand to
// multiple durations and copy over.
mySrc.makeDurations();
- mySrc.mDurations.addDurations(otherSrc.mDurations);
+ mySrc.mActiveDurations.addDurations(otherSrc.mActiveDurations);
if (mySrc.mActiveDuration != 0) {
- mySrc.mDurations.addDuration(mySrc.mActiveProcState, mySrc.mActiveDuration);
+ mySrc.mActiveDurations.addDuration(mySrc.mActiveProcState,
+ mySrc.mActiveDuration);
mySrc.mActiveDuration = 0;
mySrc.mActiveProcState = ProcessStats.STATE_NOTHING;
}
@@ -334,13 +441,14 @@ public final class AssociationState {
// Both have a single inline duration... we can either add them together,
// or need to expand to multiple durations.
if (mySrc.mActiveProcState == otherSrc.mActiveProcState) {
- mySrc.mDuration += otherSrc.mDuration;
+ mySrc.mActiveDuration += otherSrc.mActiveDuration;
} else {
// The two have durations with different proc states, need to turn
// in to multiple durations.
mySrc.makeDurations();
- mySrc.mDurations.addDuration(mySrc.mActiveProcState, mySrc.mActiveDuration);
- mySrc.mDurations.addDuration(otherSrc.mActiveProcState,
+ mySrc.mActiveDurations.addDuration(mySrc.mActiveProcState,
+ mySrc.mActiveDuration);
+ mySrc.mActiveDurations.addDuration(otherSrc.mActiveProcState,
otherSrc.mActiveDuration);
mySrc.mActiveDuration = 0;
mySrc.mActiveProcState = ProcessStats.STATE_NOTHING;
@@ -355,12 +463,13 @@ public final class AssociationState {
}
public boolean isInUse() {
- return mNumActive > 0;
+ return mTotalNesting > 0;
}
public void resetSafely(long now) {
if (!isInUse()) {
mSources.clear();
+ mTotalCount = mTotalActiveCount = 0;
} else {
// We have some active sources... clear out everything but those.
for (int isrc = mSources.size() - 1; isrc >= 0; isrc--) {
@@ -376,15 +485,28 @@ public final class AssociationState {
src.mActiveCount = 0;
}
src.mActiveDuration = 0;
- src.mDurations = null;
+ src.mActiveDurations = null;
} else {
mSources.removeAt(isrc);
}
}
+ mTotalCount = 1;
+ mTotalStartUptime = now;
+ if (mTotalActiveNesting > 0) {
+ mTotalActiveCount = 1;
+ mTotalActiveStartUptime = now;
+ } else {
+ mTotalActiveCount = 0;
+ }
}
+ mTotalDuration = mTotalActiveDuration = 0;
}
public void writeToParcel(ProcessStats stats, Parcel out, long nowUptime) {
+ out.writeInt(mTotalCount);
+ out.writeLong(mTotalDuration);
+ out.writeInt(mTotalActiveCount);
+ out.writeLong(mTotalActiveDuration);
final int NSRC = mSources.size();
out.writeInt(NSRC);
for (int isrc = 0; isrc < NSRC; isrc++) {
@@ -396,9 +518,9 @@ public final class AssociationState {
out.writeInt(src.mCount);
out.writeLong(src.mDuration);
out.writeInt(src.mActiveCount);
- if (src.mDurations != null) {
+ if (src.mActiveDurations != null) {
out.writeInt(1);
- src.mDurations.writeToParcel(out);
+ src.mActiveDurations.writeToParcel(out);
} else {
out.writeInt(0);
out.writeInt(src.mActiveProcState);
@@ -412,6 +534,10 @@ public final class AssociationState {
* caused it to fail.
*/
public String readFromParcel(ProcessStats stats, Parcel in, int parcelVersion) {
+ mTotalCount = in.readInt();
+ mTotalDuration = in.readLong();
+ mTotalActiveCount = in.readInt();
+ mTotalActiveDuration = in.readLong();
final int NSRC = in.readInt();
if (NSRC < 0 || NSRC > 100000) {
return "Association with bad src count: " + NSRC;
@@ -427,13 +553,29 @@ public final class AssociationState {
src.mActiveCount = in.readInt();
if (in.readInt() != 0) {
src.makeDurations();
- if (!src.mDurations.readFromParcel(in)) {
+ if (!src.mActiveDurations.readFromParcel(in)) {
return "Duration table corrupt: " + key + " <- " + src;
}
} else {
src.mActiveProcState = in.readInt();
src.mActiveDuration = in.readLong();
}
+ if (VALIDATE_TIMES) {
+ if (src.mDuration > mTotalDuration) {
+ RuntimeException ex = new RuntimeException();
+ ex.fillInStackTrace();
+ Slog.w(TAG, "Reading tot duration " + src.mDuration
+ + " exceeds total " + mTotalDuration + " in source "
+ + src.mKey.mProcess + " to assoc " + mName, ex);
+ }
+ if (src.mActiveDurations == null && src.mActiveDuration > mTotalDuration) {
+ RuntimeException ex = new RuntimeException();
+ ex.fillInStackTrace();
+ Slog.w(TAG, "Reading act duration " + src.mActiveDuration
+ + " exceeds total " + mTotalDuration + " in source "
+ + src.mKey.mProcess + " to assoc " + mName, ex);
+ }
+ }
mSources.put(key, src);
}
return null;
@@ -448,19 +590,30 @@ public final class AssociationState {
src.mStartUptime = nowUptime;
}
if (src.mActiveStartUptime > 0) {
- final long duration = src.mActiveDuration + nowUptime - src.mActiveStartUptime;
- if (src.mDurations != null) {
- src.mDurations.addDuration(src.mActiveProcState, duration);
+ final long addedDuration = nowUptime - src.mActiveStartUptime;
+ src.mActiveStartUptime = nowUptime;
+ if (src.mActiveDurations != null) {
+ src.mActiveDurations.addDuration(src.mActiveProcState, addedDuration);
} else {
- src.mActiveDuration = duration;
+ src.mActiveDuration += addedDuration;
}
- src.mActiveStartUptime = nowUptime;
}
}
+ if (mTotalNesting > 0) {
+ mTotalDuration += nowUptime - mTotalStartUptime;
+ mTotalStartUptime = nowUptime;
+ }
+ if (mTotalActiveNesting > 0) {
+ mTotalActiveDuration += nowUptime - mTotalActiveStartUptime;
+ mTotalActiveStartUptime = nowUptime;
+ }
}
}
public boolean hasProcessOrPackage(String procName) {
+ if (mProcessName.equals(procName)) {
+ return true;
+ }
final int NSRC = mSources.size();
for (int isrc = 0; isrc < NSRC; isrc++) {
final SourceKey key = mSources.keyAt(isrc);
@@ -471,22 +624,110 @@ public final class AssociationState {
return false;
}
- public void dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix,
- long now, long totalTime, String reqPackage, boolean dumpDetails, boolean dumpAll) {
- if (dumpAll) {
- pw.print(prefix);
- pw.print("mNumActive=");
- pw.println(mNumActive);
+ static final Comparator<Pair<SourceKey, SourceDumpContainer>> ASSOCIATION_COMPARATOR =
+ (o1, o2) -> {
+ if (o1.second.mActiveTime != o2.second.mActiveTime) {
+ return o1.second.mActiveTime > o2.second.mActiveTime ? -1 : 1;
+ }
+ if (o1.second.mTotalTime != o2.second.mTotalTime) {
+ return o1.second.mTotalTime > o2.second.mTotalTime ? -1 : 1;
+ }
+ if (o1.first.mUid != o2.first.mUid) {
+ return o1.first.mUid < o2.first.mUid ? -1 : 1;
}
+ if (o1.first.mProcess != o2.first.mProcess) {
+ int diff = o1.first.mProcess.compareTo(o2.first.mProcess);
+ if (diff != 0) {
+ return diff;
+ }
+ }
+ return 0;
+ };
+
+ public ArrayList<Pair<SourceKey, SourceDumpContainer>> createSortedAssociations(long now,
+ long totalTime) {
final int NSRC = mSources.size();
+ ArrayList<Pair<SourceKey, SourceDumpContainer>> sources = new ArrayList<>(NSRC);
for (int isrc = 0; isrc < NSRC; isrc++) {
- final SourceKey key = mSources.keyAt(isrc);
final SourceState src = mSources.valueAt(isrc);
- if (reqPackage != null && !reqPackage.equals(key.mProcess)
- && !reqPackage.equals(key.mPackage)) {
- continue;
+ final SourceDumpContainer cont = new SourceDumpContainer(src);
+ long duration = src.mDuration;
+ if (src.mNesting > 0) {
+ duration += now - src.mStartUptime;
}
- pw.print(prefixInner);
+ cont.mTotalTime = duration;
+ cont.mActiveTime = dumpTime(null, null, src, totalTime, now, false, false);
+ if (cont.mActiveTime < 0) {
+ cont.mActiveTime = -cont.mActiveTime;
+ }
+ sources.add(new Pair<>(mSources.keyAt(isrc), cont));
+ }
+ Collections.sort(sources, ASSOCIATION_COMPARATOR);
+ return sources;
+ }
+
+ public void dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix,
+ ArrayList<Pair<SourceKey, SourceDumpContainer>> sources, long now, long totalTime,
+ String reqPackage, boolean dumpDetails, boolean dumpAll) {
+ final String prefixInnerInner = prefixInner + " ";
+ long totalDuration = mTotalActiveDuration;
+ if (mTotalActiveNesting > 0) {
+ totalDuration += now - mTotalActiveStartUptime;
+ }
+ if (totalDuration > 0 || mTotalActiveCount != 0) {
+ pw.print(prefix);
+ pw.print("Active count ");
+ pw.print(mTotalActiveCount);
+ if (dumpAll) {
+ pw.print(": ");
+ TimeUtils.formatDuration(totalDuration, pw);
+ pw.print(" / ");
+ } else {
+ pw.print(": time ");
+ }
+ DumpUtils.printPercent(pw, (double) totalDuration / (double) totalTime);
+ pw.println();
+ }
+ if (dumpAll && mTotalActiveNesting != 0) {
+ pw.print(prefix);
+ pw.print("mTotalActiveNesting=");
+ pw.print(mTotalActiveNesting);
+ pw.print(" mTotalActiveStartUptime=");
+ TimeUtils.formatDuration(mTotalActiveStartUptime, now, pw);
+ pw.println();
+ }
+ totalDuration = mTotalDuration;
+ if (mTotalNesting > 0) {
+ totalDuration += now - mTotalStartUptime;
+ }
+ if (totalDuration > 0 || mTotalCount != 0) {
+ pw.print(prefix);
+ pw.print("Total count ");
+ pw.print(mTotalCount);
+ if (dumpAll) {
+ pw.print(": ");
+ TimeUtils.formatDuration(totalDuration, pw);
+ pw.print(" / ");
+ } else {
+ pw.print(": time ");
+ }
+ DumpUtils.printPercent(pw, (double) totalDuration / (double) totalTime);
+ pw.println();
+ }
+ if (dumpAll && mTotalNesting != 0) {
+ pw.print(prefix);
+ pw.print("mTotalNesting=");
+ pw.print(mTotalNesting);
+ pw.print(" mTotalStartUptime=");
+ TimeUtils.formatDuration(mTotalStartUptime, now, pw);
+ pw.println();
+ }
+ final int NSRC = sources.size();
+ for (int isrc = 0; isrc < NSRC; isrc++) {
+ final SourceKey key = sources.get(isrc).first;
+ final SourceDumpContainer cont = sources.get(isrc).second;
+ final SourceState src = cont.mState;
+ pw.print(prefix);
pw.print("<- ");
pw.print(key.mProcess);
pw.print("/");
@@ -496,24 +737,69 @@ public final class AssociationState {
pw.print(key.mPackage);
pw.print(")");
}
+ // If we are skipping this one, we still print the first line just to give
+ // context for the others (so it is clear the total times for the overall
+ // association come from other sources whose times are not shown).
+ if (reqPackage != null && !reqPackage.equals(key.mProcess)
+ && !reqPackage.equals(key.mPackage)) {
+ pw.println();
+ continue;
+ }
pw.println(":");
+ if (src.mActiveCount != 0 || src.mActiveDurations != null || src.mActiveDuration != 0
+ || src.mActiveStartUptime != 0) {
+ pw.print(prefixInner);
+ pw.print(" Active count ");
+ pw.print(src.mActiveCount);
+ if (dumpDetails) {
+ if (dumpAll) {
+ if (src.mActiveDurations != null) {
+ pw.print(" (multi-state)");
+ } else if (src.mActiveProcState >= ProcessStats.STATE_PERSISTENT) {
+ pw.print(" (");
+ pw.print(DumpUtils.STATE_NAMES[src.mActiveProcState]);
+ pw.print(")");
+ } else {
+ pw.print(" (*UNKNOWN STATE*)");
+ }
+ }
+ if (dumpAll) {
+ pw.print(": ");
+ TimeUtils.formatDuration(cont.mActiveTime, pw);
+ pw.print(" / ");
+ } else {
+ pw.print(": time ");
+ }
+ DumpUtils.printPercent(pw, (double) cont.mActiveTime / (double) totalTime);
+ if (src.mActiveStartUptime != 0) {
+ pw.print(" (running)");
+ }
+ pw.println();
+ if (src.mActiveDurations != null) {
+ dumpTime(pw, prefixInnerInner, src, totalTime, now, dumpDetails, dumpAll);
+ }
+ } else {
+ pw.print(": ");
+ dumpActiveDurationSummary(pw, src, totalTime, now, dumpAll);
+ }
+ }
pw.print(prefixInner);
pw.print(" Total count ");
pw.print(src.mCount);
- long duration = src.mDuration;
- if (src.mNesting > 0) {
- duration += now - src.mStartUptime;
- }
if (dumpAll) {
- pw.print(": Duration ");
- TimeUtils.formatDuration(duration, pw);
+ pw.print(": ");
+ TimeUtils.formatDuration(cont.mTotalTime, pw);
pw.print(" / ");
} else {
pw.print(": time ");
}
- DumpUtils.printPercent(pw, (double)duration/(double)totalTime);
+ DumpUtils.printPercent(pw, (double) cont.mTotalTime / (double) totalTime);
if (src.mNesting > 0) {
pw.print(" (running");
+ if (dumpAll) {
+ pw.print(" nest=");
+ pw.print(src.mNesting);
+ }
if (src.mProcState != ProcessStats.STATE_NOTHING) {
pw.print(" / ");
pw.print(DumpUtils.STATE_NAMES[src.mProcState]);
@@ -523,23 +809,6 @@ public final class AssociationState {
pw.print(")");
}
pw.println();
- if (src.mActiveCount > 0 || src.mDurations != null || src.mActiveDuration != 0
- || src.mActiveStartUptime != 0) {
- pw.print(prefixInner);
- pw.print(" Active count ");
- pw.print(src.mActiveCount);
- if (dumpDetails) {
- if (dumpAll) {
- pw.print(src.mDurations != null ? " (multi-field)" : " (inline)");
- }
- pw.println(":");
- dumpTime(pw, prefixInner, src, totalTime, now, dumpDetails, dumpAll);
- } else {
- pw.print(": ");
- dumpActiveDurationSummary(pw, src, totalTime, now, dumpAll);
- pw.println();
- }
- }
if (dumpAll) {
if (src.mInTrackingList) {
pw.print(prefixInner);
@@ -565,7 +834,6 @@ public final class AssociationState {
duration = -duration;
}
if (dumpAll) {
- pw.print("Duration ");
TimeUtils.formatDuration(duration, pw);
pw.print(" / ");
} else {
@@ -584,10 +852,10 @@ public final class AssociationState {
boolean isRunning = false;
for (int iprocstate = 0; iprocstate < ProcessStats.STATE_COUNT; iprocstate++) {
long time;
- if (src.mDurations != null) {
- time = src.mDurations.getValueForId((byte)iprocstate);
+ if (src.mActiveDurations != null) {
+ time = src.mActiveDurations.getValueForId((byte) iprocstate);
} else {
- time = src.mActiveProcState == iprocstate ? src.mDuration : 0;
+ time = src.mActiveProcState == iprocstate ? src.mActiveDuration : 0;
}
final String running;
if (src.mActiveStartUptime != 0 && src.mActiveProcState == iprocstate) {
@@ -600,11 +868,9 @@ public final class AssociationState {
if (time != 0) {
if (pw != null) {
pw.print(prefix);
- pw.print(" ");
pw.print(DumpUtils.STATE_LABELS[iprocstate]);
pw.print(": ");
if (dumpAll) {
- pw.print("Duration ");
TimeUtils.formatDuration(time, pw);
pw.print(" / ");
} else {
@@ -619,21 +885,6 @@ public final class AssociationState {
totalTime += time;
}
}
- if (totalTime != 0 && pw != null) {
- pw.print(prefix);
- pw.print(" ");
- pw.print(DumpUtils.STATE_LABEL_TOTAL);
- pw.print(": ");
- if (dumpAll) {
- pw.print("Duration ");
- TimeUtils.formatDuration(totalTime, pw);
- pw.print(" / ");
- } else {
- pw.print("time ");
- }
- DumpUtils.printPercent(pw, (double) totalTime / (double) overallTime);
- pw.println();
- }
return isRunning ? -totalTime : totalTime;
}
@@ -667,11 +918,11 @@ public final class AssociationState {
pw.print(",");
pw.print(src.mActiveCount);
final long timeNow = src.mActiveStartUptime != 0 ? (now-src.mActiveStartUptime) : 0;
- if (src.mDurations != null) {
- final int N = src.mDurations.getKeyCount();
+ if (src.mActiveDurations != null) {
+ final int N = src.mActiveDurations.getKeyCount();
for (int i=0; i<N; i++) {
- final int dkey = src.mDurations.getKeyAt(i);
- duration = src.mDurations.getValue(dkey);
+ final int dkey = src.mActiveDurations.getKeyAt(i);
+ duration = src.mActiveDurations.getValue(dkey);
if (dkey == src.mActiveProcState) {
duration += timeNow;
}
@@ -699,6 +950,14 @@ public final class AssociationState {
proto.write(PackageAssociationProcessStatsProto.COMPONENT_NAME, mName);
+ proto.write(PackageAssociationProcessStatsProto.TOTAL_COUNT, mTotalCount);
+ proto.write(PackageAssociationProcessStatsProto.TOTAL_DURATION_MS, getTotalDuration(now));
+ if (mTotalActiveCount != 0) {
+ proto.write(PackageAssociationProcessStatsProto.ACTIVE_COUNT, mTotalActiveCount);
+ proto.write(PackageAssociationProcessStatsProto.ACTIVE_DURATION_MS,
+ getActiveDuration(now));
+ }
+
final int NSRC = mSources.size();
for (int isrc = 0; isrc < NSRC; isrc++) {
final SourceKey key = mSources.keyAt(isrc);
@@ -718,11 +977,11 @@ public final class AssociationState {
src.mActiveCount);
}
final long timeNow = src.mActiveStartUptime != 0 ? (now-src.mActiveStartUptime) : 0;
- if (src.mDurations != null) {
- final int N = src.mDurations.getKeyCount();
+ if (src.mActiveDurations != null) {
+ final int N = src.mActiveDurations.getKeyCount();
for (int i=0; i<N; i++) {
- final int dkey = src.mDurations.getKeyAt(i);
- duration = src.mDurations.getValue(dkey);
+ final int dkey = src.mActiveDurations.getKeyAt(i);
+ duration = src.mActiveDurations.getValue(dkey);
if (dkey == src.mActiveProcState) {
duration += timeNow;
}
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 8e88c510ec31..875cff8d0988 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -31,6 +31,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.LongSparseArray;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -48,6 +49,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Comparator;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -179,7 +181,7 @@ public final class ProcessStats implements Parcelable {
{"proc", "pkg-proc", "pkg-svc", "pkg-asc", "pkg-all", "all"};
// Current version of the parcel format.
- private static final int PARCEL_VERSION = 36;
+ private static final int PARCEL_VERSION = 38;
// In-memory Parcel magic number, used to detect attempts to unmarshall bad data
private static final int MAGIC = 0x50535454;
@@ -196,6 +198,9 @@ public final class ProcessStats implements Parcelable {
public int mMemFactor = STATE_NOTHING;
public long mStartTime;
+ // Number of individual stats that have been aggregated to create this one.
+ public int mNumAggregated = 1;
+
public long mTimePeriodStartClock;
public long mTimePeriodStartRealtime;
public long mTimePeriodEndRealtime;
@@ -348,6 +353,8 @@ public final class ProcessStats implements Parcelable {
mSysMemUsage.mergeStats(other.mSysMemUsage);
+ mNumAggregated += other.mNumAggregated;
+
if (other.mTimePeriodStartClock < mTimePeriodStartClock) {
mTimePeriodStartClock = other.mTimePeriodStartClock;
mTimePeriodStartClockStr = other.mTimePeriodStartClockStr;
@@ -569,6 +576,7 @@ public final class ProcessStats implements Parcelable {
}
private void resetCommon() {
+ mNumAggregated = 1;
mTimePeriodStartClock = System.currentTimeMillis();
buildTimePeriodStartClockStr();
mTimePeriodStartRealtime = mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
@@ -845,6 +853,7 @@ public final class ProcessStats implements Parcelable {
}
}
+ out.writeInt(mNumAggregated);
out.writeLong(mTimePeriodStartClock);
out.writeLong(mTimePeriodStartRealtime);
out.writeLong(mTimePeriodEndRealtime);
@@ -1031,6 +1040,7 @@ public final class ProcessStats implements Parcelable {
mIndexToCommonString = new ArrayList<String>();
+ mNumAggregated = in.readInt();
mTimePeriodStartClock = in.readLong();
buildTimePeriodStartClockStr();
mTimePeriodStartRealtime = in.readLong();
@@ -1457,15 +1467,79 @@ public final class ProcessStats implements Parcelable {
}
}
+ final class AssociationDumpContainer {
+ final AssociationState mState;
+ ArrayList<Pair<AssociationState.SourceKey, AssociationState.SourceDumpContainer>> mSources;
+ long mTotalTime;
+ long mActiveTime;
+
+ AssociationDumpContainer(AssociationState state) {
+ mState = state;
+ }
+ }
+
+ static final Comparator<AssociationDumpContainer> ASSOCIATION_COMPARATOR = (o1, o2) -> {
+ int diff = o1.mState.getProcessName().compareTo(o2.mState.getProcessName());
+ if (diff != 0) {
+ return diff;
+ }
+ if (o1.mActiveTime != o2.mActiveTime) {
+ return o1.mActiveTime > o2.mActiveTime ? -1 : 1;
+ }
+ if (o1.mTotalTime != o2.mTotalTime) {
+ return o1.mTotalTime > o2.mTotalTime ? -1 : 1;
+ }
+ diff = o1.mState.getName().compareTo(o2.mState.getName());
+ if (diff != 0) {
+ return diff;
+ }
+ return 0;
+ };
+
public void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpSummary,
boolean dumpDetails, boolean dumpAll, boolean activeOnly, int section) {
long totalTime = DumpUtils.dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
mStartTime, now);
- boolean sepNeeded = false;
+ pw.print(" Start time: ");
+ pw.print(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimePeriodStartClock));
+ pw.println();
+ pw.print(" Total uptime: ");
+ TimeUtils.formatDuration(
+ (mRunning ? SystemClock.uptimeMillis() : mTimePeriodEndUptime)
+ - mTimePeriodStartUptime, pw);
+ pw.println();
+ pw.print(" Total elapsed time: ");
+ TimeUtils.formatDuration(
+ (mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime)
+ - mTimePeriodStartRealtime, pw);
+ boolean partial = true;
+ if ((mFlags & FLAG_SHUTDOWN) != 0) {
+ pw.print(" (shutdown)");
+ partial = false;
+ }
+ if ((mFlags & FLAG_SYSPROPS) != 0) {
+ pw.print(" (sysprops)");
+ partial = false;
+ }
+ if ((mFlags & FLAG_COMPLETE) != 0) {
+ pw.print(" (complete)");
+ partial = false;
+ }
+ if (partial) {
+ pw.print(" (partial)");
+ }
+ if (mHasSwappedOutPss) {
+ pw.print(" (swapped-out-pss)");
+ }
+ pw.print(' ');
+ pw.print(mRuntime);
+ pw.println();
+ pw.print(" Aggregated over: ");
+ pw.println(mNumAggregated);
if (mSysMemUsage.getKeyCount() > 0) {
+ pw.println();
pw.println("System memory usage:");
mSysMemUsage.dump(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ);
- sepNeeded = true;
}
boolean printedHeader = false;
if ((section & REPORT_PKG_STATS) != 0) {
@@ -1485,8 +1559,8 @@ public final class ProcessStats implements Parcelable {
final int NASCS = pkgState.mAssociations.size();
final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName);
boolean onlyAssociations = false;
+ boolean procMatch = false;
if (!pkgMatch) {
- boolean procMatch = false;
for (int iproc = 0; iproc < NPROCS; iproc++) {
ProcessState proc = pkgState.mProcesses.valueAt(iproc);
if (reqPackage.equals(proc.getName())) {
@@ -1511,10 +1585,9 @@ public final class ProcessStats implements Parcelable {
}
if (NPROCS > 0 || NSRVS > 0 || NASCS > 0) {
if (!printedHeader) {
- if (sepNeeded) pw.println();
+ pw.println();
pw.println("Per-Package Stats:");
printedHeader = true;
- sepNeeded = true;
}
pw.print(" * ");
pw.print(pkgName);
@@ -1597,6 +1670,8 @@ public final class ProcessStats implements Parcelable {
}
}
if ((section & REPORT_PKG_ASC_STATS) != 0) {
+ ArrayList<AssociationDumpContainer> associations =
+ new ArrayList<>(NASCS);
for (int iasc = 0; iasc < NASCS; iasc++) {
AssociationState asc = pkgState.mAssociations.valueAt(iasc);
if (!pkgMatch && !reqPackage.equals(asc.getProcessName())) {
@@ -1604,6 +1679,18 @@ public final class ProcessStats implements Parcelable {
continue;
}
}
+ final AssociationDumpContainer cont =
+ new AssociationDumpContainer(asc);
+ cont.mSources = asc.createSortedAssociations(now, totalTime);
+ cont.mTotalTime = asc.getTotalDuration(now);
+ cont.mActiveTime = asc.getActiveDuration(now);
+ associations.add(cont);
+ }
+ Collections.sort(associations, ASSOCIATION_COMPARATOR);
+ final int NCONT = associations.size();
+ for (int iasc = 0; iasc < NCONT; iasc++) {
+ final AssociationDumpContainer cont = associations.get(iasc);
+ final AssociationState asc = cont.mState;
if (activeOnly && !asc.isInUse()) {
pw.print(" (Not active association: ");
pw.print(pkgState.mAssociations.keyAt(iasc));
@@ -1615,13 +1702,15 @@ public final class ProcessStats implements Parcelable {
} else {
pw.print(" * Asc ");
}
- pw.print(pkgState.mAssociations.keyAt(iasc));
+ pw.print(cont.mState.getName());
pw.println(":");
pw.print(" Process: ");
pw.println(asc.getProcessName());
asc.dumpStats(pw, " ", " ", " ",
- now, totalTime, onlyAssociations ? reqPackage : null,
- dumpDetails, dumpAll);
+ cont.mSources, now, totalTime,
+ onlyAssociations && !pkgMatch && !procMatch
+ && !asc.getProcessName().equals(reqPackage)
+ ? reqPackage : null, dumpDetails, dumpAll);
}
}
}
@@ -1651,10 +1740,7 @@ public final class ProcessStats implements Parcelable {
continue;
}
numShownProcs++;
- if (sepNeeded) {
- pw.println();
- }
- sepNeeded = true;
+ pw.println();
if (!printedHeader) {
pw.println("Multi-Package Common Processes:");
printedHeader = true;
@@ -1684,11 +1770,7 @@ public final class ProcessStats implements Parcelable {
}
if (dumpAll) {
- if (sepNeeded) {
- pw.println();
- }
- sepNeeded = true;
-
+ pw.println();
if (mTrackingAssociations.size() > 0) {
pw.println();
pw.println("Tracking associations:");
@@ -1734,9 +1816,7 @@ public final class ProcessStats implements Parcelable {
}
}
- if (sepNeeded) {
- pw.println();
- }
+ pw.println();
if (dumpSummary) {
pw.println("Process summary:");
dumpSummaryLocked(pw, reqPackage, now, activeOnly);
@@ -1861,41 +1941,6 @@ public final class ProcessStats implements Parcelable {
pw.print("x over ");
TimeUtils.formatDuration(mExternalSlowPssTime, pw);
pw.println();
- pw.println();
- pw.print(" Start time: ");
- pw.print(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimePeriodStartClock));
- pw.println();
- pw.print(" Total uptime: ");
- TimeUtils.formatDuration(
- (mRunning ? SystemClock.uptimeMillis() : mTimePeriodEndUptime)
- - mTimePeriodStartUptime, pw);
- pw.println();
- pw.print(" Total elapsed time: ");
- TimeUtils.formatDuration(
- (mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime)
- - mTimePeriodStartRealtime, pw);
- boolean partial = true;
- if ((mFlags&FLAG_SHUTDOWN) != 0) {
- pw.print(" (shutdown)");
- partial = false;
- }
- if ((mFlags&FLAG_SYSPROPS) != 0) {
- pw.print(" (sysprops)");
- partial = false;
- }
- if ((mFlags&FLAG_COMPLETE) != 0) {
- pw.print(" (complete)");
- partial = false;
- }
- if (partial) {
- pw.print(" (partial)");
- }
- if (mHasSwappedOutPss) {
- pw.print(" (swapped-out-pss)");
- }
- pw.print(' ');
- pw.print(mRuntime);
- pw.println();
}
void dumpFilteredSummaryLocked(PrintWriter pw, String header, String prefix, String prcLabel,
diff --git a/core/java/com/android/internal/compat/ChangeReporter.java b/core/java/com/android/internal/compat/ChangeReporter.java
index 1ce071bd005a..e0eb9af8b228 100644
--- a/core/java/com/android/internal/compat/ChangeReporter.java
+++ b/core/java/com/android/internal/compat/ChangeReporter.java
@@ -16,58 +16,188 @@
package com.android.internal.compat;
+import android.util.Log;
+import android.util.Slog;
import android.util.StatsLog;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
/**
* A helper class to report changes to stats log.
*
* @hide
*/
public final class ChangeReporter {
+ private static final String TAG = "CompatibilityChangeReporter";
+ private int mSource;
+
+ private static final class ChangeReport {
+ long mChangeId;
+ int mState;
+
+ ChangeReport(long changeId, int state) {
+ mChangeId = changeId;
+ mState = state;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ChangeReport that = (ChangeReport) o;
+ return mChangeId == that.mChangeId
+ && mState == that.mState;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mChangeId, mState);
+ }
+ }
+
+ // Maps uid to a set of ChangeReports (that were reported for that uid).
+ @GuardedBy("mReportedChanges")
+ private final Map<Integer, Set<ChangeReport>> mReportedChanges;
+
+ // When true will of every time to debug (logcat).
+ private boolean mDebugLogAll;
+
+ public ChangeReporter(int source) {
+ mSource = source;
+ mReportedChanges = new HashMap<>();
+ mDebugLogAll = false;
+ }
/**
- * Transforms StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE enum to a string.
+ * Report the change to stats log and to the debug log if the change was not previously
+ * logged already.
*
- * @param state to transform
- * @return a string representing the state
+ * @param uid affected by the change
+ * @param changeId the reported change id
+ * @param state of the reported change - enabled/disabled/only logged
*/
- private static String stateToString(int state) {
- switch (state) {
- case StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED:
- return "LOGGED";
- case StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED:
- return "ENABLED";
- case StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__DISABLED:
- return "DISABLED";
- default:
- return "UNKNOWN";
+ public void reportChange(int uid, long changeId, int state) {
+ if (shouldWriteToStatsLog(uid, changeId, state)) {
+ StatsLog.write(StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED, uid, changeId,
+ state, mSource);
+ }
+ if (shouldWriteToDebug(uid, changeId, state)) {
+ debugLog(uid, changeId, state);
}
+ markAsReported(uid, new ChangeReport(changeId, state));
+ }
+
+ /**
+ * Start logging all the time to logcat.
+ */
+ public void startDebugLogAll() {
+ mDebugLogAll = true;
+ }
+
+ /**
+ * Stop logging all the time to logcat.
+ */
+ public void stopDebugLogAll() {
+ mDebugLogAll = false;
}
+
/**
- * Constructs and returns a string to be logged to logcat when a change is reported.
+ * Returns whether the next report should be logged to statsLog.
*
* @param uid affected by the change
* @param changeId the reported change id
* @param state of the reported change - enabled/disabled/only logged
- * @return string to log
+ * @return true if the report should be logged
*/
- public static String createLogString(int uid, long changeId, int state) {
- return String.format("Compat change id reported: %d; UID %d; state: %s", changeId, uid,
- stateToString(state));
+ @VisibleForTesting
+ public boolean shouldWriteToStatsLog(int uid, long changeId, int state) {
+ return !isAlreadyReported(uid, new ChangeReport(changeId, state));
}
/**
- * Report the change to stats log.
+ * Returns whether the next report should be logged to logcat.
*
* @param uid affected by the change
* @param changeId the reported change id
* @param state of the reported change - enabled/disabled/only logged
- * @param source of the logging - app process or system server
+ * @return true if the report should be logged
+ */
+ @VisibleForTesting
+ public boolean shouldWriteToDebug(int uid, long changeId, int state) {
+ return mDebugLogAll || !isAlreadyReported(uid, new ChangeReport(changeId, state));
+ }
+
+ private boolean isAlreadyReported(int uid, ChangeReport report) {
+ synchronized (mReportedChanges) {
+ Set<ChangeReport> reportedChangesForUid = mReportedChanges.get(uid);
+ if (reportedChangesForUid == null) {
+ return false;
+ } else {
+ return reportedChangesForUid.contains(report);
+ }
+ }
+ }
+
+ private void markAsReported(int uid, ChangeReport report) {
+ synchronized (mReportedChanges) {
+ Set<ChangeReport> reportedChangesForUid = mReportedChanges.get(uid);
+ if (reportedChangesForUid == null) {
+ mReportedChanges.put(uid, new HashSet<ChangeReport>());
+ reportedChangesForUid = mReportedChanges.get(uid);
+ }
+ reportedChangesForUid.add(report);
+ }
+ }
+
+ /**
+ * Clears the saved information about a given uid. Requests to report uid again will be reported
+ * regardless to the past reports.
+ *
+ * <p> Only intended to be called from PlatformCompat.
+ *
+ * @param uid to reset
+ */
+ public void resetReportedChanges(int uid) {
+ synchronized (mReportedChanges) {
+ mReportedChanges.remove(uid);
+ }
+ }
+
+ private void debugLog(int uid, long changeId, int state) {
+ String message = String.format("Compat change id reported: %d; UID %d; state: %s", changeId,
+ uid, stateToString(state));
+ if (mSource == StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER) {
+ Slog.d(TAG, message);
+ } else {
+ Log.d(TAG, message);
+ }
+
+ }
+
+ /**
+ * Transforms StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE enum to a string.
+ *
+ * @param state to transform
+ * @return a string representing the state
*/
- public void reportChange(int uid, long changeId, int state, int source) {
- //TODO(b/138374585): Implement rate limiting for stats log.
- StatsLog.write(StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED, uid, changeId,
- state, source);
+ private static String stateToString(int state) {
+ switch (state) {
+ case StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED:
+ return "LOGGED";
+ case StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED:
+ return "ENABLED";
+ case StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__DISABLED:
+ return "DISABLED";
+ default:
+ return "UNKNOWN";
+ }
}
}
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeConfig.aidl b/core/java/com/android/internal/compat/CompatibilityChangeConfig.aidl
new file mode 100644
index 000000000000..434c1b819582
--- /dev/null
+++ b/core/java/com/android/internal/compat/CompatibilityChangeConfig.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.compat;
+
+parcelable CompatibilityChangeConfig;
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeConfig.java b/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
new file mode 100644
index 000000000000..fd2ada08edc1
--- /dev/null
+++ b/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.compat;
+
+
+import android.compat.Compatibility.ChangeConfig;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Parcelable containing compat config overrides for a given application.
+ * @hide
+ */
+public final class CompatibilityChangeConfig implements Parcelable {
+ private final ChangeConfig mChangeConfig;
+
+ public CompatibilityChangeConfig(ChangeConfig changeConfig) {
+ mChangeConfig = changeConfig;
+ }
+
+ /**
+ * Changes forced to be enabled.
+ */
+ public Set<Long> enabledChanges() {
+ return mChangeConfig.forceEnabledSet();
+ }
+
+ /**
+ * Changes forced to be disabled.
+ */
+ public Set<Long> disabledChanges() {
+ return mChangeConfig.forceDisabledSet();
+ }
+
+ private CompatibilityChangeConfig(Parcel in) {
+ long[] enabledArray = in.createLongArray();
+ long[] disabledArray = in.createLongArray();
+ Set<Long> enabled = toLongSet(enabledArray);
+ Set<Long> disabled = toLongSet(disabledArray);
+ mChangeConfig = new ChangeConfig(enabled, disabled);
+ }
+
+ private static Set<Long> toLongSet(long[] values) {
+ Set<Long> ret = new HashSet<>();
+ for (long value: values) {
+ ret.add(value);
+ }
+ return ret;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ long[] enabled = mChangeConfig.forceEnabledChangesArray();
+ long[] disabled = mChangeConfig.forceDisabledChangesArray();
+
+ dest.writeLongArray(enabled);
+ dest.writeLongArray(disabled);
+ }
+
+ public static final Parcelable.Creator<CompatibilityChangeConfig> CREATOR =
+ new Parcelable.Creator<CompatibilityChangeConfig>() {
+
+ @Override
+ public CompatibilityChangeConfig createFromParcel(Parcel in) {
+ return new CompatibilityChangeConfig(in);
+ }
+
+ @Override
+ public CompatibilityChangeConfig[] newArray(int size) {
+ return new CompatibilityChangeConfig[size];
+ }
+ };
+}
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index 9049c3aea7ec..8391ad2f12c2 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -18,6 +18,8 @@ package com.android.internal.compat;
import android.content.pm.ApplicationInfo;
+parcelable CompatibilityChangeConfig;
+
/**
* Platform private API for talking with the PlatformCompat service.
*
@@ -33,15 +35,37 @@ interface IPlatformCompat
* Reports that a compatibility change is affecting an app process now.
*
* <p>Note: for changes that are gated using {@link #isChangeEnabled(long, ApplicationInfo)},
- * you do not need to call this API directly. The change will be reported for you in the case
- * that {@link #isChangeEnabled(long, ApplicationInfo)} returns {@code true}.
+ * you do not need to call this API directly. The change will be reported for you.
*
* @param changeId The ID of the compatibility change taking effect.
- * @param appInfo Representing the affected app.
+ * @param appInfo Representing the affected app.
*/
void reportChange(long changeId, in ApplicationInfo appInfo);
/**
+ * Reports that a compatibility change is affecting an app process now.
+ *
+ * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, String)},
+ * you do not need to call this API directly. The change will be reported for you.
+ *
+ * @param changeId The ID of the compatibility change taking effect.
+ * @param userId The ID of the user that the operation is done for.
+ * @param packageName The package name of the app in question.
+ */
+ void reportChangeByPackageName(long changeId, in String packageName, int userId);
+
+ /**
+ * Reports that a compatibility change is affecting an app process now.
+ *
+ * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, int)},
+ * you do not need to call this API directly. The change will be reported for you.
+ *
+ * @param changeId The ID of the compatibility change taking effect.
+ * @param uid The UID of the app in question.
+ */
+ void reportChangeByUid(long changeId, int uid);
+
+ /**
* Query if a given compatibility change is enabled for an app process. This method should
* be called when implementing functionality on behalf of the affected app.
*
@@ -49,13 +73,77 @@ interface IPlatformCompat
* change, resulting in differing behaviour compared to earlier releases. If this method returns
* {@code false}, the calling code should behave as it did in earlier releases.
*
- * <p>When this method returns {@code true}, it will also report the change as
- * {@link #reportChange(long, ApplicationInfo)} would, so there is no need to call that method
- * directly.
+ * <p>It will also report the change as {@link #reportChange(long, ApplicationInfo)} would, so
+ * there is no need to call that method directly.
*
* @param changeId The ID of the compatibility change in question.
- * @param appInfo Representing the app in question.
+ * @param appInfo Representing the app in question.
* @return {@code true} if the change is enabled for the current app.
*/
boolean isChangeEnabled(long changeId, in ApplicationInfo appInfo);
-} \ No newline at end of file
+
+ /**
+ * Query if a given compatibility change is enabled for an app process. This method should
+ * be called when implementing functionality on behalf of the affected app.
+ *
+ * <p>Same as {@link #isChangeEnabled(long, ApplicationInfo)}, except it receives a package name
+ * and userId instead of an {@link ApplicationInfo}
+ * object, and finds an app info object based on the package name. Returns {@code true} if
+ * there is no installed package by that name.
+ *
+ * <p>If this method returns {@code true}, the calling code should implement the compatibility
+ * change, resulting in differing behaviour compared to earlier releases. If this method
+ * returns
+ * {@code false}, the calling code should behave as it did in earlier releases.
+ *
+ * <p>It will also report the change as {@link #reportChange(long, String)} would, so there is
+ * no need to call that method directly.
+ *
+ * @param changeId The ID of the compatibility change in question.
+ * @param packageName The package name of the app in question.
+ * @param userId The ID of the user that the operation is done for.
+ * @return {@code true} if the change is enabled for the current app.
+ */
+ boolean isChangeEnabledByPackageName(long changeId, in String packageName, int userId);
+
+ /**
+ * Query if a given compatibility change is enabled for an app process. This method should
+ * be called when implementing functionality on behalf of the affected app.
+ *
+ * <p>Same as {@link #isChangeEnabled(long, ApplicationInfo)}, except it receives a uid
+ * instead of an {@link ApplicationInfo} object, and finds an app info object based on the
+ * uid (or objects if there's more than one package associated with the UID).
+ * Returns {@code true} if there are no installed packages for the required UID, or if the
+ * change is enabled for ALL of the installed packages associated with the provided UID. Please
+ * use a more specific API if you want a different behaviour for multi-package UIDs.
+ *
+ * <p>If this method returns {@code true}, the calling code should implement the compatibility
+ * change, resulting in differing behaviour compared to earlier releases. If this method
+ * returns {@code false}, the calling code should behave as it did in earlier releases.
+ *
+ * <p>It will also report the change as {@link #reportChange(long, int)} would, so there is
+ * no need to call that method directly.
+ *
+ * @param changeId The ID of the compatibility change in question.
+ * @param uid The UID of the app in question.
+ * @return {@code true} if the change is enabled for the current app.
+ */
+ boolean isChangeEnabledByUid(long changeId, int uid);
+
+ /**
+ * Add overrides to compatibility changes.
+ *
+ * @param overrides Parcelable containing the compat change overrides to be applied.
+ * @param packageName The package name of the app whose changes will be overridden.
+ *
+ */
+ void setOverrides(in CompatibilityChangeConfig overrides, in String packageName);
+
+ /**
+ * Revert overrides to compatibility changes.
+ *
+ * @param packageName The package name of the app whose overrides will be cleared.
+ *
+ */
+ void clearOverrides(in String packageName);
+}
diff --git a/core/java/com/android/internal/compat/OWNERS b/core/java/com/android/internal/compat/OWNERS
new file mode 100644
index 000000000000..2b7cdb0cbce9
--- /dev/null
+++ b/core/java/com/android/internal/compat/OWNERS
@@ -0,0 +1,7 @@
+# Use this reviewer by default.
+platform-compat-eng+reviews@google.com
+
+andreionea@google.com
+atrost@google.com
+mathewi@google.com
+satayev@google.com
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 5142d3cd2b99..033e9b2c6eba 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -239,6 +239,13 @@ public final class SystemUiDeviceConfigFlags {
public static final String ASSIST_TRANSCRIPTION_MIN_DURATION =
"assist_transcription_min_duration";
+ /**
+ * (boolean) Whether or not to enable an extra section in the notification shade which
+ * filters for "people" related messages.
+ */
+ public static final String NOTIFICATIONS_USE_PEOPLE_FILTERING =
+ "notifications_use_people_filtering";
+
// Flags related to brightline falsing
/**
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index cdb79abbb7ce..f5708a5c89af 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -17,6 +17,7 @@
package com.android.internal.content;
import android.annotation.CallSuper;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Intent;
@@ -552,6 +553,11 @@ public abstract class FileSystemProvider extends DocumentsProvider {
flags |= Document.FLAG_SUPPORTS_DELETE;
flags |= Document.FLAG_SUPPORTS_RENAME;
flags |= Document.FLAG_SUPPORTS_MOVE;
+
+ if (shouldBlockFromTree(docId)) {
+ flags |= Document.FLAG_DIR_BLOCKS_TREE;
+ }
+
} else {
flags |= Document.FLAG_SUPPORTS_WRITE;
flags |= Document.FLAG_SUPPORTS_DELETE;
@@ -592,6 +598,10 @@ public abstract class FileSystemProvider extends DocumentsProvider {
return row;
}
+ protected boolean shouldBlockFromTree(@NonNull String docId) {
+ return false;
+ }
+
protected boolean typeSupportsMetadata(String mimeType) {
return MetadataReader.isSupportedMimeType(mimeType)
|| Document.MIME_TYPE_DIR.equals(mimeType);
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index c928f3f39f03..d6dcb29e0682 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -22,11 +22,11 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
-import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.util.Slog;
+
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.Preconditions;
@@ -203,10 +203,6 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {
public void onPackagesSuspended(String[] packages) {
}
- public void onPackagesSuspended(String[] packages, Bundle launcherExtras) {
- onPackagesSuspended(packages);
- }
-
public void onPackagesUnsuspended(String[] packages) {
}
@@ -446,9 +442,8 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {
}
} else if (Intent.ACTION_PACKAGES_SUSPENDED.equals(action)) {
String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- final Bundle launcherExtras = intent.getBundleExtra(Intent.EXTRA_LAUNCHER_EXTRAS);
mSomePackagesChanged = true;
- onPackagesSuspended(pkgList, launcherExtras);
+ onPackagesSuspended(pkgList);
} else if (Intent.ACTION_PACKAGES_UNSUSPENDED.equals(action)) {
String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
mSomePackagesChanged = true;
diff --git a/core/java/com/android/internal/infra/ServiceConnector.java b/core/java/com/android/internal/infra/ServiceConnector.java
index d6862f0188ce..98d679eb776b 100644
--- a/core/java/com/android/internal/infra/ServiceConnector.java
+++ b/core/java/com/android/internal/infra/ServiceConnector.java
@@ -32,6 +32,7 @@ import android.text.TextUtils;
import android.util.DebugUtils;
import android.util.Log;
+import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
import java.io.PrintWriter;
@@ -351,7 +352,7 @@ public interface ServiceConnector<I extends IInterface> {
@Override
public <R> CompletionAwareJob<I, R> postForResult(@NonNull Job<I, R> job) {
CompletionAwareJob<I, R> task = new CompletionAwareJob<>();
- task.mDelegate = job;
+ task.mDelegate = Preconditions.checkNotNull(job);
enqueue(task);
return task;
}
@@ -359,7 +360,7 @@ public interface ServiceConnector<I extends IInterface> {
@Override
public <R> AndroidFuture<R> postAsync(@NonNull Job<I, CompletableFuture<R>> job) {
CompletionAwareJob<I, R> task = new CompletionAwareJob<>();
- task.mDelegate = (Job) job;
+ task.mDelegate = Preconditions.checkNotNull((Job) job);
task.mAsync = true;
enqueue(task);
return task;
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
index e09e0e609380..cffb0ad9fdb9 100644
--- a/core/java/com/android/internal/os/KernelWakelockReader.java
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -29,6 +29,7 @@ import com.android.internal.annotations.VisibleForTesting;
import java.io.File;
import java.io.FileInputStream;
+import java.util.Arrays;
import java.util.Iterator;
/**
@@ -66,6 +67,7 @@ public class KernelWakelockReader {
private final String[] mProcWakelocksName = new String[3];
private final long[] mProcWakelocksData = new long[3];
private ISuspendControlService mSuspendControlService = null;
+ private byte[] mKernelWakelockBuffer = new byte[32 * 1024];
/**
* Reads kernel wakelock stats and updates the staleStats with the new information.
@@ -84,7 +86,7 @@ public class KernelWakelockReader {
}
return removeOldStats(staleStats);
} else {
- byte[] buffer = new byte[32*1024];
+ Arrays.fill(mKernelWakelockBuffer, (byte) 0);
int len = 0;
boolean wakeup_sources;
final long startTime = SystemClock.uptimeMillis();
@@ -107,7 +109,8 @@ public class KernelWakelockReader {
}
int cnt;
- while ((cnt = is.read(buffer, len, buffer.length - len)) > 0) {
+ while ((cnt = is.read(mKernelWakelockBuffer, len,
+ mKernelWakelockBuffer.length - len)) > 0) {
len += cnt;
}
@@ -125,12 +128,13 @@ public class KernelWakelockReader {
}
if (len > 0) {
- if (len >= buffer.length) {
- Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length);
+ if (len >= mKernelWakelockBuffer.length) {
+ Slog.wtf(TAG, "Kernel wake locks exceeded mKernelWakelockBuffer size "
+ + mKernelWakelockBuffer.length);
}
int i;
for (i=0; i<len; i++) {
- if (buffer[i] == '\0') {
+ if (mKernelWakelockBuffer[i] == '\0') {
len = i;
break;
}
@@ -143,7 +147,7 @@ public class KernelWakelockReader {
Slog.w(TAG, "Failed to get Native wakelock stats from SystemSuspend");
}
// Get kernel wakelock stats
- parseProcWakelocks(buffer, len, wakeup_sources, staleStats);
+ parseProcWakelocks(mKernelWakelockBuffer, len, wakeup_sources, staleStats);
return removeOldStats(staleStats);
}
}
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 1de2e7272f4d..fd3cd42b07a1 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -20,6 +20,7 @@ import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ApplicationErrorReport;
+import android.content.type.DefaultMimeMapFactory;
import android.os.Build;
import android.os.DeadObjectException;
import android.os.Debug;
@@ -33,6 +34,9 @@ import com.android.internal.logging.AndroidConfig;
import com.android.server.NetworkManagementSocketTagger;
import dalvik.system.RuntimeHooks;
import dalvik.system.VMRuntime;
+
+import libcore.content.type.MimeMap;
+
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -192,6 +196,24 @@ public class RuntimeInit {
}
}
+ /**
+ * Common initialization that (unlike {@link #commonInit()} should happen prior to
+ * the Zygote fork.
+ */
+ public static void preForkInit() {
+ if (DEBUG) Slog.d(TAG, "Entered preForkInit.");
+ RuntimeInit.enableDdms();
+ // TODO(b/142019040#comment13): Decide whether to load the default instance eagerly, i.e.
+ // MimeMap.setDefault(DefaultMimeMapFactory.create());
+ /*
+ * Replace libcore's minimal default mapping between MIME types and file
+ * extensions with a mapping that's suitable for Android. Android's mapping
+ * contains many more entries that are derived from IANA registrations but
+ * with several customizations (extensions, overrides).
+ */
+ MimeMap.setDefaultSupplier(DefaultMimeMapFactory::create);
+ }
+
@UnsupportedAppUsage
protected static final void commonInit() {
if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");
@@ -324,7 +346,7 @@ public class RuntimeInit {
@UnsupportedAppUsage
public static final void main(String[] argv) {
- enableDdms();
+ preForkInit();
if (argv.length == 2 && argv[1].equals("application")) {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application");
redirectLogStreams();
@@ -418,7 +440,7 @@ public class RuntimeInit {
/**
* Enable DDMS.
*/
- static final void enableDdms() {
+ private static void enableDdms() {
// Register handlers for DDM messages.
android.ddm.DdmRegister.registerHandlers();
}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 9d4cdc73b452..3ce3838a212e 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -135,6 +135,9 @@ public final class Zygote {
/** Read-write external storage should be mounted instead of package sandbox */
public static final int MOUNT_EXTERNAL_FULL = IVold.REMOUNT_MODE_FULL;
+ /** The lower file system should be bind mounted directly on external storage */
+ public static final int MOUNT_EXTERNAL_PASS_THROUGH = IVold.REMOUNT_MODE_PASS_THROUGH;
+
/** Number of bytes sent to the Zygote over USAP pipes or the pool event FD */
static final int USAP_MANAGEMENT_MESSAGE_BYTES = 8;
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index abc416061cc8..a23e659db49a 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -362,6 +362,8 @@ class ZygoteArguments {
mMountExternal = Zygote.MOUNT_EXTERNAL_INSTALLER;
} else if (arg.equals("--mount-external-legacy")) {
mMountExternal = Zygote.MOUNT_EXTERNAL_LEGACY;
+ } else if (arg.equals("--mount-external-pass-through")) {
+ mMountExternal = Zygote.MOUNT_EXTERNAL_PASS_THROUGH;
} else if (arg.equals("--query-abi-list")) {
mAbiListQuery = true;
} else if (arg.equals("--get-pid")) {
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index b3ec5f56e75a..a14b09343a01 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -306,6 +306,12 @@ class ZygoteConnection {
}
private void handleBootCompleted() {
+ try {
+ mSocketOutStream.writeInt(0);
+ } catch (IOException ioe) {
+ throw new IllegalStateException("Error writing to command socket", ioe);
+ }
+
VMRuntime.bootCompleted();
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 8f088570d242..8797d1f07a05 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -185,6 +185,12 @@ public class ZygoteInit {
System.loadLibrary("compiler_rt");
System.loadLibrary("jnigraphics");
try {
+ System.loadLibrary("sfplugin_ccodec");
+ } catch (Error | RuntimeException e) {
+ // tolerate missing sfplugin_ccodec which is only present on Codec 2 devices
+ }
+
+ try {
System.loadLibrary("qti_performance");
} catch (UnsatisfiedLinkError e) {
Log.e(TAG, "Couldn't load qti_performance");
@@ -852,7 +858,7 @@ public class ZygoteInit {
TimingsTraceLog bootTimingsTraceLog = new TimingsTraceLog(bootTimeTag,
Trace.TRACE_TAG_DALVIK);
bootTimingsTraceLog.traceBegin("ZygoteInit");
- RuntimeInit.enableDdms();
+ RuntimeInit.preForkInit();
boolean startSystemServer = false;
String zygoteSocketName = "zygote";
diff --git a/core/java/com/android/internal/policy/KeyInterceptionInfo.java b/core/java/com/android/internal/policy/KeyInterceptionInfo.java
new file mode 100644
index 000000000000..964be01952ea
--- /dev/null
+++ b/core/java/com/android/internal/policy/KeyInterceptionInfo.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy;
+
+
+/**
+ * Stores a snapshot of window information used to decide whether to intercept a key event.
+ */
+public class KeyInterceptionInfo {
+ // Window layout params attributes.
+ public final int layoutParamsType;
+ public final int layoutParamsPrivateFlags;
+ // Debug friendly name to help identify the window
+ public final String windowTitle;
+
+ public KeyInterceptionInfo(int type, int flags, String title) {
+ layoutParamsType = type;
+ layoutParamsPrivateFlags = flags;
+ windowTitle = title;
+ }
+}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 9441825a1ed6..317469e58245 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -23,6 +23,7 @@ import android.os.Bundle;
import android.service.notification.StatusBarNotification;
import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.view.AppearanceRegion;
/** @hide */
oneway interface IStatusBar
@@ -56,7 +57,7 @@ oneway interface IStatusBar
int mask, in Rect fullscreenBounds, in Rect dockedBounds,
boolean navbarColorManagedByIme);
- void topAppWindowChanged(int displayId, boolean menuVisible);
+ void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive);
void setImeWindowStatus(int displayId, in IBinder token, int vis, int backDisposition,
boolean showImeSwitcher, boolean isMultiClientImeEnabled);
void setWindowState(int display, int window, int state);
@@ -151,17 +152,17 @@ oneway interface IStatusBar
void showShutdownUi(boolean isReboot, String reason);
- // Used to show the dialog when BiometricService starts authentication
- void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
- boolean requireConfirmation, int userId, String opPackageName);
- // Used to hide the dialog when a biometric is authenticated
- void onBiometricAuthenticated(boolean authenticated, String failureReason);
+ // Used to show the authentication dialog (Biometrics, Device Credential)
+ void showAuthenticationDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver,
+ int biometricModality, boolean requireConfirmation, int userId, String opPackageName);
+ // Used to notify the authentication dialog that a biometric has been authenticated
+ void onBiometricAuthenticated();
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
void onBiometricHelp(String message);
- // Used to set a message - the dialog will dismiss after a certain amount of time
- void onBiometricError(String error);
- // Used to hide the biometric dialog when the AuthenticationClient is stopped
- void hideBiometricDialog();
+ // Used to show an error - the dialog will dismiss after a certain amount of time
+ void onBiometricError(int modality, int error, int vendorCode);
+ // Used to hide the authentication dialog, e.g. when the application cancels authentication
+ void hideAuthenticationDialog();
/**
* Notifies System UI that the display is ready to show system decorations.
@@ -172,4 +173,38 @@ oneway interface IStatusBar
* Notifies System UI whether the recents animation is running or not.
*/
void onRecentsAnimationStateChanged(boolean running);
+
+ /**
+ * Notifies System UI side of system bar appearance change on the specified display.
+ *
+ * @param displayId the ID of the display to notify
+ * @param appearance the appearance of the focused window. The light top bar appearance is not
+ * controlled here, but primaryAppearance and secondaryAppearance.
+ * @param appearanceRegions a set of appearances which will be only applied in their own bounds.
+ * This is for system bars which across multiple stack, e.g., status
+ * bar, that the bar can have partial appearances in corresponding
+ * stacks.
+ * @param navbarColorManagedByIme {@code true} if navigation bar color is managed by IME.
+ */
+ void onSystemBarAppearanceChanged(int displayId, int appearance,
+ in AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme);
+
+ /**
+ * Notifies System UI to show transient bars. The transient bars are system bars, e.g., status
+ * bar and navigation bar which are temporarily visible to the user.
+ *
+ * @param displayId the ID of the display to notify.
+ * @param types the internal insets types of the bars are about to show transiently.
+ */
+ void showTransient(int displayId, in int[] types);
+
+ /**
+ * Notifies System UI to abort the transient state of system bars, which prevents the bars being
+ * hidden automatically. This is usually called when the app wants to show the permanent system
+ * bars again.
+ *
+ * @param displayId the ID of the display to notify.
+ * @param types the internal insets types of the bars are about to abort the transient state.
+ */
+ void abortTransient(int displayId, in int[] types);
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 4c3a177a013b..499a4d2fb949 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -17,6 +17,7 @@
package com.android.internal.statusbar;
import android.app.Notification;
+import android.net.Uri;
import android.content.ComponentName;
import android.graphics.Rect;
import android.os.Bundle;
@@ -77,6 +78,7 @@ interface IStatusBarService
void onNotificationSettingsViewed(String key);
void setSystemUiVisibility(int displayId, int vis, int mask, String cause);
void onNotificationBubbleChanged(String key, boolean isBubble);
+ void grantInlineReplyUriPermission(String key, in Uri uri);
void onGlobalActionsShown();
void onGlobalActionsHidden();
@@ -99,15 +101,15 @@ interface IStatusBarService
void showPinningEnterExitToast(boolean entering);
void showPinningEscapeToast();
- // Used to show the dialog when BiometricService starts authentication
- void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
- boolean requireConfirmation, int userId, String opPackageName);
- // Used to hide the dialog when a biometric is authenticated
- void onBiometricAuthenticated(boolean authenticated, String failureReason);
+ // Used to show the authentication dialog (Biometrics, Device Credential)
+ void showAuthenticationDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver,
+ int biometricModality, boolean requireConfirmation, int userId, String opPackageName);
+ // Used to notify the authentication dialog that a biometric has been authenticated
+ void onBiometricAuthenticated();
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
void onBiometricHelp(String message);
- // Used to set a message - the dialog will dismiss after a certain amount of time
- void onBiometricError(String error);
- // Used to hide the biometric dialog when the AuthenticationClient is stopped
- void hideBiometricDialog();
+ // Used to show an error - the dialog will dismiss after a certain amount of time
+ void onBiometricError(int modality, int error, int vendorCode);
+ // Used to hide the authentication dialog, e.g. when the application cancels authentication
+ void hideAuthenticationDialog();
}
diff --git a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
index 6b0f8b2f5dc9..4c3f04b10892 100644
--- a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
+++ b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
@@ -29,7 +29,6 @@ public final class RegisterStatusBarResult implements Parcelable {
public final ArrayMap<String, StatusBarIcon> mIcons;
public final int mDisabledFlags1; // switch[0]
public final int mSystemUiVisibility; // switch[1]
- public final boolean mMenuVisible; // switch[2]
public final int mImeWindowVis; // switch[3]
public final int mImeBackDisposition; // switch[4]
public final boolean mShowImeSwitcher; // switch[5]
@@ -40,16 +39,18 @@ public final class RegisterStatusBarResult implements Parcelable {
public final Rect mFullscreenStackBounds;
public final Rect mDockedStackBounds;
public final boolean mNavbarColorManagedByIme;
+ public final boolean mAppFullscreen;
+ public final boolean mAppImmersive;
public RegisterStatusBarResult(ArrayMap<String, StatusBarIcon> icons, int disabledFlags1,
- int systemUiVisibility, boolean menuVisible, int imeWindowVis, int imeBackDisposition,
+ int systemUiVisibility, int imeWindowVis, int imeBackDisposition,
boolean showImeSwitcher, int disabledFlags2, int fullscreenStackSysUiVisibility,
int dockedStackSysUiVisibility, IBinder imeToken, Rect fullscreenStackBounds,
- Rect dockedStackBounds, boolean navbarColorManagedByIme) {
+ Rect dockedStackBounds, boolean navbarColorManagedByIme, boolean appFullscreen,
+ boolean appImmersive) {
mIcons = new ArrayMap<>(icons);
mDisabledFlags1 = disabledFlags1;
mSystemUiVisibility = systemUiVisibility;
- mMenuVisible = menuVisible;
mImeWindowVis = imeWindowVis;
mImeBackDisposition = imeBackDisposition;
mShowImeSwitcher = showImeSwitcher;
@@ -60,6 +61,8 @@ public final class RegisterStatusBarResult implements Parcelable {
mFullscreenStackBounds = fullscreenStackBounds;
mDockedStackBounds = dockedStackBounds;
mNavbarColorManagedByIme = navbarColorManagedByIme;
+ mAppFullscreen = appFullscreen;
+ mAppImmersive = appImmersive;
}
@Override
@@ -72,7 +75,6 @@ public final class RegisterStatusBarResult implements Parcelable {
dest.writeTypedArrayMap(mIcons, flags);
dest.writeInt(mDisabledFlags1);
dest.writeInt(mSystemUiVisibility);
- dest.writeBoolean(mMenuVisible);
dest.writeInt(mImeWindowVis);
dest.writeInt(mImeBackDisposition);
dest.writeBoolean(mShowImeSwitcher);
@@ -83,6 +85,8 @@ public final class RegisterStatusBarResult implements Parcelable {
dest.writeTypedObject(mFullscreenStackBounds, flags);
dest.writeTypedObject(mDockedStackBounds, flags);
dest.writeBoolean(mNavbarColorManagedByIme);
+ dest.writeBoolean(mAppFullscreen);
+ dest.writeBoolean(mAppImmersive);
}
/**
@@ -96,7 +100,6 @@ public final class RegisterStatusBarResult implements Parcelable {
source.createTypedArrayMap(StatusBarIcon.CREATOR);
final int disabledFlags1 = source.readInt();
final int systemUiVisibility = source.readInt();
- final boolean menuVisible = source.readBoolean();
final int imeWindowVis = source.readInt();
final int imeBackDisposition = source.readInt();
final boolean showImeSwitcher = source.readBoolean();
@@ -107,11 +110,13 @@ public final class RegisterStatusBarResult implements Parcelable {
final Rect fullscreenStackBounds = source.readTypedObject(Rect.CREATOR);
final Rect dockedStackBounds = source.readTypedObject(Rect.CREATOR);
final boolean navbarColorManagedByIme = source.readBoolean();
+ final boolean appFullscreen = source.readBoolean();
+ final boolean appImmersive = source.readBoolean();
return new RegisterStatusBarResult(icons, disabledFlags1, systemUiVisibility,
- menuVisible, imeWindowVis, imeBackDisposition, showImeSwitcher,
- disabledFlags2, fullscreenStackSysUiVisibility,
- dockedStackSysUiVisibility, imeToken, fullscreenStackBounds,
- dockedStackBounds, navbarColorManagedByIme);
+ imeWindowVis, imeBackDisposition, showImeSwitcher, disabledFlags2,
+ fullscreenStackSysUiVisibility, dockedStackSysUiVisibility, imeToken,
+ fullscreenStackBounds, dockedStackBounds, navbarColorManagedByIme,
+ appFullscreen, appImmersive);
}
@Override
diff --git a/telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl b/core/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl
index 493b1ff6aba7..493b1ff6aba7 100644
--- a/telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl
+++ b/core/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
new file mode 100644
index 000000000000..084a3cc64a35
--- /dev/null
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -0,0 +1,69 @@
+/*
+ * 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.internal.telephony;
+
+import android.os.Bundle;
+import android.telephony.CallAttributes;
+import android.telephony.CellInfo;
+import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.PhoneCapability;
+import android.telephony.PhysicalChannelConfig;
+import android.telephony.PreciseCallState;
+import android.telephony.PreciseDataConnectionState;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.ImsReasonInfo;
+
+/**
+ * {@hide}
+ */
+oneway interface IPhoneStateListener {
+ void onServiceStateChanged(in ServiceState serviceState);
+ void onSignalStrengthChanged(int asu);
+ void onMessageWaitingIndicatorChanged(boolean mwi);
+ void onCallForwardingIndicatorChanged(boolean cfi);
+
+ // we use bundle here instead of CellLocation so it can get the right subclass
+ void onCellLocationChanged(in Bundle location);
+ void onCallStateChanged(int state, String incomingNumber);
+ void onDataConnectionStateChanged(int state, int networkType);
+ void onDataActivity(int direction);
+ void onSignalStrengthsChanged(in SignalStrength signalStrength);
+ void onPhysicalChannelConfigurationChanged(in List<PhysicalChannelConfig> configs);
+ void onOtaspChanged(in int otaspMode);
+ void onCellInfoChanged(in List<CellInfo> cellInfo);
+ void onPreciseCallStateChanged(in PreciseCallState callState);
+ void onPreciseDataConnectionStateChanged(in PreciseDataConnectionState dataConnectionState);
+ void onDataConnectionRealTimeInfoChanged(in DataConnectionRealTimeInfo dcRtInfo);
+ void onSrvccStateChanged(in int state);
+ void onVoiceActivationStateChanged(int activationState);
+ void onDataActivationStateChanged(int activationState);
+ void onOemHookRawEvent(in byte[] rawData);
+ void onCarrierNetworkChange(in boolean active);
+ void onUserMobileDataStateChanged(in boolean enabled);
+ void onPhoneCapabilityChanged(in PhoneCapability capability);
+ void onActiveDataSubIdChanged(in int subId);
+ void onRadioPowerStateChanged(in int state);
+ void onCallAttributesChanged(in CallAttributes callAttributes);
+ void onEmergencyNumberListChanged(in Map emergencyNumberList);
+ void onOutgoingEmergencyCall(in EmergencyNumber placedEmergencyNumber);
+ void onOutgoingEmergencySms(in EmergencyNumber sentEmergencyNumber);
+ void onCallDisconnectCauseChanged(in int disconnectCause, in int preciseDisconnectCause);
+ void onImsCallDisconnectCauseChanged(in ImsReasonInfo imsReasonInfo);
+}
+
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
new file mode 100644
index 000000000000..d7a7af1d530f
--- /dev/null
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -0,0 +1,103 @@
+/*
+ * 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.internal.telephony;
+
+import android.content.Intent;
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
+import android.os.Bundle;
+import android.telephony.CallQuality;
+import android.telephony.CellInfo;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.PhoneCapability;
+import android.telephony.PhysicalChannelConfig;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.emergency.EmergencyNumber;
+import com.android.internal.telephony.IPhoneStateListener;
+import com.android.internal.telephony.IOnSubscriptionsChangedListener;
+
+interface ITelephonyRegistry {
+ void addOnSubscriptionsChangedListener(String pkg,
+ IOnSubscriptionsChangedListener callback);
+ void addOnOpportunisticSubscriptionsChangedListener(String pkg,
+ IOnSubscriptionsChangedListener callback);
+ void removeOnSubscriptionsChangedListener(String pkg,
+ IOnSubscriptionsChangedListener callback);
+ @UnsupportedAppUsage
+ void listen(String pkg, IPhoneStateListener callback, int events, boolean notifyNow);
+ void listenForSubscriber(in int subId, String pkg, IPhoneStateListener callback, int events,
+ boolean notifyNow);
+ @UnsupportedAppUsage
+ void notifyCallStateForAllSubs(int state, String incomingNumber);
+ void notifyCallState(in int phoneId, in int subId, int state, String incomingNumber);
+ void notifyServiceStateForPhoneId(in int phoneId, in int subId, in ServiceState state);
+ void notifySignalStrengthForPhoneId(in int phoneId, in int subId,
+ in SignalStrength signalStrength);
+ void notifyMessageWaitingChangedForPhoneId(in int phoneId, in int subId, in boolean mwi);
+ @UnsupportedAppUsage(maxTargetSdk = 28)
+ void notifyCallForwardingChanged(boolean cfi);
+ void notifyCallForwardingChangedForSubscriber(in int subId, boolean cfi);
+ @UnsupportedAppUsage(maxTargetSdk = 28)
+ void notifyDataActivity(int state);
+ void notifyDataActivityForSubscriber(in int subId, int state);
+ void notifyDataConnection(int state, boolean isDataConnectivityPossible,
+ String apn, String apnType, in LinkProperties linkProperties,
+ in NetworkCapabilities networkCapabilities, int networkType, boolean roaming);
+ void notifyDataConnectionForSubscriber(int phoneId, int subId, int state,
+ boolean isDataConnectivityPossible,
+ String apn, String apnType, in LinkProperties linkProperties,
+ in NetworkCapabilities networkCapabilities, int networkType, boolean roaming);
+ @UnsupportedAppUsage
+ void notifyDataConnectionFailed(String apnType);
+ void notifyDataConnectionFailedForSubscriber(int phoneId, int subId, String apnType);
+ @UnsupportedAppUsage(maxTargetSdk = 28)
+ void notifyCellLocation(in Bundle cellLocation);
+ void notifyCellLocationForSubscriber(in int subId, in Bundle cellLocation);
+ @UnsupportedAppUsage(maxTargetSdk = 28)
+ void notifyOtaspChanged(in int subId, in int otaspMode);
+ @UnsupportedAppUsage
+ void notifyCellInfo(in List<CellInfo> cellInfo);
+ void notifyPhysicalChannelConfigurationForSubscriber(in int phoneId, in int subId,
+ in List<PhysicalChannelConfig> configs);
+ void notifyPreciseCallState(int phoneId, int subId, int ringingCallState,
+ int foregroundCallState, int backgroundCallState);
+ void notifyDisconnectCause(int phoneId, int subId, int disconnectCause,
+ int preciseDisconnectCause);
+ void notifyPreciseDataConnectionFailed(int phoneId, int subId, String apnType, String apn,
+ int failCause);
+ void notifyCellInfoForSubscriber(in int subId, in List<CellInfo> cellInfo);
+ void notifySrvccStateChanged(in int subId, in int lteState);
+ void notifySimActivationStateChangedForPhoneId(in int phoneId, in int subId,
+ int activationState, int activationType);
+ void notifyOemHookRawEventForSubscriber(in int phoneId, in int subId, in byte[] rawData);
+ void notifySubscriptionInfoChanged();
+ void notifyOpportunisticSubscriptionInfoChanged();
+ void notifyCarrierNetworkChange(in boolean active);
+ void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state);
+ void notifyPhoneCapabilityChanged(in PhoneCapability capability);
+ void notifyActiveDataSubIdChanged(int activeDataSubId);
+ void notifyRadioPowerStateChanged(in int phoneId, in int subId, in int state);
+ void notifyEmergencyNumberList(in int phoneId, in int subId);
+ void notifyOutgoingEmergencyCall(in int phoneId, in int subId,
+ in EmergencyNumber emergencyNumber);
+ void notifyOutgoingEmergencySms(in int phoneId, in int subId,
+ in EmergencyNumber emergencyNumber);
+ void notifyCallQualityChanged(in CallQuality callQuality, int phoneId, int subId,
+ int callNetworkType);
+ void notifyImsDisconnectCause(int subId, in ImsReasonInfo imsReasonInfo);
+}
diff --git a/core/java/com/android/internal/util/MimeIconUtils.java b/core/java/com/android/internal/util/MimeIconUtils.java
index 2230c3134301..31ea5b2bf07c 100644
--- a/core/java/com/android/internal/util/MimeIconUtils.java
+++ b/core/java/com/android/internal/util/MimeIconUtils.java
@@ -27,7 +27,7 @@ import android.util.ArrayMap;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
-import libcore.net.MimeMap;
+import libcore.content.type.MimeMap;
import java.util.Locale;
import java.util.Objects;
diff --git a/core/java/com/android/internal/util/function/DecConsumer.java b/core/java/com/android/internal/util/function/DecConsumer.java
new file mode 100644
index 000000000000..0abb785bea32
--- /dev/null
+++ b/core/java/com/android/internal/util/function/DecConsumer.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.function;
+
+import java.util.function.Consumer;
+
+/**
+ * A 10-argument {@link Consumer}
+ *
+ * @hide
+ */
+public interface DecConsumer<A, B, C, D, E, F, G, H, I, J> {
+ void accept(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j);
+}
diff --git a/core/java/com/android/internal/util/function/DecFunction.java b/core/java/com/android/internal/util/function/DecFunction.java
new file mode 100644
index 000000000000..59fc5e6e6562
--- /dev/null
+++ b/core/java/com/android/internal/util/function/DecFunction.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.function;
+
+import java.util.function.Function;
+
+/**
+ * A 10-argument {@link Function}
+ *
+ * @hide
+ */
+public interface DecFunction<A, B, C, D, E, F, G, H, I, J, R> {
+ R apply(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j);
+}
diff --git a/core/java/com/android/internal/util/function/DecPredicate.java b/core/java/com/android/internal/util/function/DecPredicate.java
new file mode 100644
index 000000000000..975993dc05dc
--- /dev/null
+++ b/core/java/com/android/internal/util/function/DecPredicate.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.function;
+
+import java.util.function.Predicate;
+
+/**
+ * A 10-argument {@link Predicate}
+ *
+ * @hide
+ */
+public interface DecPredicate<A, B, C, D, E, F, G, H, I, J> {
+ boolean test(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j);
+}
diff --git a/core/java/com/android/internal/util/function/UndecConsumer.java b/core/java/com/android/internal/util/function/UndecConsumer.java
new file mode 100644
index 000000000000..1a1d4ca600af
--- /dev/null
+++ b/core/java/com/android/internal/util/function/UndecConsumer.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.function;
+
+import java.util.function.Consumer;
+
+/**
+ * A 11-argument {@link Consumer}
+ *
+ * @hide
+ */
+public interface UndecConsumer<A, B, C, D, E, F, G, H, I, J, K> {
+ void accept(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k);
+}
diff --git a/core/java/com/android/internal/util/function/UndecFunction.java b/core/java/com/android/internal/util/function/UndecFunction.java
new file mode 100644
index 000000000000..5cd324c2a7e5
--- /dev/null
+++ b/core/java/com/android/internal/util/function/UndecFunction.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.function;
+
+import java.util.function.Function;
+
+/**
+ * A 11-argument {@link Function}
+ *
+ * @hide
+ */
+public interface UndecFunction<A, B, C, D, E, F, G, H, I, J, K, R> {
+ R apply(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k);
+}
diff --git a/core/java/com/android/internal/util/function/UndecPredicate.java b/core/java/com/android/internal/util/function/UndecPredicate.java
new file mode 100644
index 000000000000..c09193eb7a11
--- /dev/null
+++ b/core/java/com/android/internal/util/function/UndecPredicate.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.function;
+
+import java.util.function.Predicate;
+
+/**
+ * A 11-argument {@link Predicate}
+ *
+ * @hide
+ */
+public interface UndecPredicate<A, B, C, D, E, F, G, H, I, J, K> {
+ boolean test(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k);
+}
diff --git a/core/java/com/android/internal/util/function/pooled/OmniFunction.java b/core/java/com/android/internal/util/function/pooled/OmniFunction.java
index d74e715605bb..7a17253ffd35 100755
--- a/core/java/com/android/internal/util/function/pooled/OmniFunction.java
+++ b/core/java/com/android/internal/util/function/pooled/OmniFunction.java
@@ -18,6 +18,8 @@ package com.android.internal.util.function.pooled;
import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
+import com.android.internal.util.function.DecConsumer;
+import com.android.internal.util.function.DecFunction;
import com.android.internal.util.function.HeptConsumer;
import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.HexConsumer;
@@ -32,6 +34,8 @@ import com.android.internal.util.function.QuintConsumer;
import com.android.internal.util.function.QuintFunction;
import com.android.internal.util.function.TriConsumer;
import com.android.internal.util.function.TriFunction;
+import com.android.internal.util.function.UndecConsumer;
+import com.android.internal.util.function.UndecFunction;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
@@ -43,62 +47,65 @@ import java.util.function.Function;
*
* @hide
*/
-abstract class OmniFunction<A, B, C, D, E, F, G, H, I, R> implements
+abstract class OmniFunction<A, B, C, D, E, F, G, H, I, J, K, R> implements
PooledFunction<A, R>, BiFunction<A, B, R>, TriFunction<A, B, C, R>,
QuadFunction<A, B, C, D, R>, QuintFunction<A, B, C, D, E, R>,
HexFunction<A, B, C, D, E, F, R>, HeptFunction<A, B, C, D, E, F, G, R>,
OctFunction<A, B, C, D, E, F, G, H, R>, NonaFunction<A, B, C, D, E, F, G, H, I, R>,
+ DecFunction<A, B, C, D, E, F, G, H, I, J, R>,
+ UndecFunction<A, B, C, D, E, F, G, H, I, J, K, R>,
PooledConsumer<A>, BiConsumer<A, B>, TriConsumer<A, B, C>, QuadConsumer<A, B, C, D>,
QuintConsumer<A, B, C, D, E>, HexConsumer<A, B, C, D, E, F>,
HeptConsumer<A, B, C, D, E, F, G>, OctConsumer<A, B, C, D, E, F, G, H>,
- NonaConsumer<A, B, C, D, E, F, G, H, I>, PooledPredicate<A>, BiPredicate<A, B>,
- PooledSupplier<R>, PooledRunnable, ThrowingRunnable, ThrowingSupplier<R>,
- PooledSupplier.OfInt, PooledSupplier.OfLong, PooledSupplier.OfDouble {
+ NonaConsumer<A, B, C, D, E, F, G, H, I>, DecConsumer<A, B, C, D, E, F, G, H, I, J>,
+ UndecConsumer<A, B, C, D, E, F, G, H, I, J, K>,
+ PooledPredicate<A>, BiPredicate<A, B>, PooledSupplier<R>, PooledRunnable, ThrowingRunnable,
+ ThrowingSupplier<R>, PooledSupplier.OfInt, PooledSupplier.OfLong, PooledSupplier.OfDouble {
- abstract R invoke(A a, B b, C c, D d, E e, F f, G g, H h, I i);
+ abstract R invoke(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k);
@Override
public R apply(A o, B o2) {
- return invoke(o, o2, null, null, null, null, null, null, null);
+ return invoke(o, o2, null, null, null, null, null, null, null, null, null);
}
@Override
public R apply(A o) {
- return invoke(o, null, null, null, null, null, null, null, null);
+ return invoke(o, null, null, null, null, null, null, null, null, null, null);
}
- public abstract <V> OmniFunction<A, B, C, D, E, F, G, H, I, V> andThen(
+ public abstract <V> OmniFunction<A, B, C, D, E, F, G, H, I, J, K, V> andThen(
Function<? super R, ? extends V> after);
- public abstract OmniFunction<A, B, C, D, E, F, G, H, I, R> negate();
+ public abstract OmniFunction<A, B, C, D, E, F, G, H, I, J, K, R> negate();
@Override
public void accept(A o, B o2) {
- invoke(o, o2, null, null, null, null, null, null, null);
+ invoke(o, o2, null, null, null, null, null, null, null, null, null);
}
@Override
public void accept(A o) {
- invoke(o, null, null, null, null, null, null, null, null);
+ invoke(o, null, null, null, null, null, null, null, null, null, null);
}
@Override
public void run() {
- invoke(null, null, null, null, null, null, null, null, null);
+ invoke(null, null, null, null, null, null, null, null, null, null, null);
}
@Override
public R get() {
- return invoke(null, null, null, null, null, null, null, null, null);
+ return invoke(null, null, null, null, null, null, null, null, null, null, null);
}
@Override
public boolean test(A o, B o2) {
- return (Boolean) invoke(o, o2, null, null, null, null, null, null, null);
+ return (Boolean) invoke(o, o2, null, null, null, null, null, null, null, null, null);
}
@Override
public boolean test(A o) {
- return (Boolean) invoke(o, null, null, null, null, null, null, null, null);
+ return (Boolean) invoke(o, null, null, null, null, null, null, null, null, null, null);
}
@Override
@@ -113,72 +120,92 @@ abstract class OmniFunction<A, B, C, D, E, F, G, H, I, R> implements
@Override
public R apply(A a, B b, C c) {
- return invoke(a, b, c, null, null, null, null, null, null);
+ return invoke(a, b, c, null, null, null, null, null, null, null, null);
}
@Override
public void accept(A a, B b, C c) {
- invoke(a, b, c, null, null, null, null, null, null);
+ invoke(a, b, c, null, null, null, null, null, null, null, null);
}
@Override
public R apply(A a, B b, C c, D d) {
- return invoke(a, b, c, d, null, null, null, null, null);
+ return invoke(a, b, c, d, null, null, null, null, null, null, null);
}
@Override
public R apply(A a, B b, C c, D d, E e) {
- return invoke(a, b, c, d, e, null, null, null, null);
+ return invoke(a, b, c, d, e, null, null, null, null, null, null);
}
@Override
public R apply(A a, B b, C c, D d, E e, F f) {
- return invoke(a, b, c, d, e, f, null, null, null);
+ return invoke(a, b, c, d, e, f, null, null, null, null, null);
}
@Override
public R apply(A a, B b, C c, D d, E e, F f, G g) {
- return invoke(a, b, c, d, e, f, g, null, null);
+ return invoke(a, b, c, d, e, f, g, null, null, null, null);
}
@Override
public R apply(A a, B b, C c, D d, E e, F f, G g, H h) {
- return invoke(a, b, c, d, e, f, g, h, null);
+ return invoke(a, b, c, d, e, f, g, h, null, null, null);
}
@Override
public R apply(A a, B b, C c, D d, E e, F f, G g, H h, I i) {
- return invoke(a, b, c, d, e, f, g, h, i);
+ return invoke(a, b, c, d, e, f, g, h, i, null, null);
+ }
+
+ @Override
+ public R apply(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j) {
+ return invoke(a, b, c, d, e, f, g, h, i, j, null);
+ }
+
+ @Override
+ public R apply(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k) {
+ return invoke(a, b, c, d, e, f, g, h, i, j, k);
}
@Override
public void accept(A a, B b, C c, D d) {
- invoke(a, b, c, d, null, null, null, null, null);
+ invoke(a, b, c, d, null, null, null, null, null, null, null);
}
@Override
public void accept(A a, B b, C c, D d, E e) {
- invoke(a, b, c, d, e, null, null, null, null);
+ invoke(a, b, c, d, e, null, null, null, null, null, null);
}
@Override
public void accept(A a, B b, C c, D d, E e, F f) {
- invoke(a, b, c, d, e, f, null, null, null);
+ invoke(a, b, c, d, e, f, null, null, null, null, null);
}
@Override
public void accept(A a, B b, C c, D d, E e, F f, G g) {
- invoke(a, b, c, d, e, f, g, null, null);
+ invoke(a, b, c, d, e, f, g, null, null, null, null);
}
@Override
public void accept(A a, B b, C c, D d, E e, F f, G g, H h) {
- invoke(a, b, c, d, e, f, g, h, null);
+ invoke(a, b, c, d, e, f, g, h, null, null, null);
}
@Override
public void accept(A a, B b, C c, D d, E e, F f, G g, H h, I i) {
- invoke(a, b, c, d, e, f, g, h, i);
+ invoke(a, b, c, d, e, f, g, h, i, null, null);
+ }
+
+ @Override
+ public void accept(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j) {
+ invoke(a, b, c, d, e, f, g, h, i, j, null);
+ }
+
+ @Override
+ public void accept(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k) {
+ invoke(a, b, c, d, e, f, g, h, i, j, k);
}
@Override
@@ -192,5 +219,5 @@ abstract class OmniFunction<A, B, C, D, E, F, G, H, I, R> implements
}
@Override
- public abstract OmniFunction<A, B, C, D, E, F, G, H, I, R> recycleOnUse();
+ public abstract OmniFunction<A, B, C, D, E, F, G, H, I, J, K, R> recycleOnUse();
}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambda.java b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
index c00932e7a8aa..b9bf9337c3d6 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambda.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
@@ -21,6 +21,8 @@ import static com.android.internal.util.function.pooled.PooledLambdaImpl.acquire
import android.os.Message;
+import com.android.internal.util.function.DecConsumer;
+import com.android.internal.util.function.DecFunction;
import com.android.internal.util.function.HeptConsumer;
import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.HexConsumer;
@@ -35,6 +37,8 @@ import com.android.internal.util.function.QuintConsumer;
import com.android.internal.util.function.QuintFunction;
import com.android.internal.util.function.TriConsumer;
import com.android.internal.util.function.TriFunction;
+import com.android.internal.util.function.UndecConsumer;
+import com.android.internal.util.function.UndecFunction;
import com.android.internal.util.function.pooled.PooledLambdaImpl.LambdaType.ReturnType;
import java.util.function.BiConsumer;
@@ -181,7 +185,7 @@ public interface PooledLambda {
A arg1) {
return acquire(PooledLambdaImpl.sPool,
function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -198,7 +202,7 @@ public interface PooledLambda {
A arg1) {
return acquire(PooledLambdaImpl.sPool,
function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -215,7 +219,7 @@ public interface PooledLambda {
A arg1) {
return acquire(PooledLambdaImpl.sPool,
function, 1, 0, ReturnType.OBJECT, arg1, null, null, null, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -246,7 +250,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null,
- null);
+ null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -266,7 +270,7 @@ public interface PooledLambda {
A arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -284,7 +288,7 @@ public interface PooledLambda {
A arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -302,7 +306,7 @@ public interface PooledLambda {
A arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -320,7 +324,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -338,7 +342,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -356,7 +360,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -374,7 +378,7 @@ public interface PooledLambda {
A arg1, ArgumentPlaceholder<B> arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -392,7 +396,7 @@ public interface PooledLambda {
A arg1, ArgumentPlaceholder<B> arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -410,7 +414,7 @@ public interface PooledLambda {
A arg1, ArgumentPlaceholder<B> arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -442,7 +446,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
- null);
+ null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -463,7 +467,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -482,7 +486,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -501,7 +505,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -520,7 +524,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -539,7 +543,7 @@ public interface PooledLambda {
A arg1, ArgumentPlaceholder<B> arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -558,7 +562,7 @@ public interface PooledLambda {
A arg1, ArgumentPlaceholder<B> arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -577,7 +581,7 @@ public interface PooledLambda {
A arg1, B arg2, ArgumentPlaceholder<C> arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -596,7 +600,7 @@ public interface PooledLambda {
A arg1, B arg2, ArgumentPlaceholder<C> arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -629,7 +633,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
- null);
+ null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -651,7 +655,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -671,7 +675,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -691,7 +695,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -711,7 +715,7 @@ public interface PooledLambda {
ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -731,7 +735,7 @@ public interface PooledLambda {
A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -751,7 +755,7 @@ public interface PooledLambda {
A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -771,7 +775,7 @@ public interface PooledLambda {
A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -791,7 +795,7 @@ public interface PooledLambda {
A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -811,7 +815,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -831,7 +835,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -865,7 +869,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null);
+ null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -888,7 +892,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, D arg4, E arg5) {
return acquire(PooledLambdaImpl.sPool,
function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -909,7 +913,7 @@ public interface PooledLambda {
function, A arg1, B arg2, C arg3, D arg4, E arg5) {
return acquire(PooledLambdaImpl.sPool,
function, 5, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, null, null, null,
- null);
+ null, null, null);
}
/**
@@ -945,7 +949,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null,
- null);
+ null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -969,7 +973,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
return acquire(PooledLambdaImpl.sPool,
function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
- null);
+ null, null, null);
}
/**
@@ -991,7 +995,7 @@ public interface PooledLambda {
? extends R> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
return acquire(PooledLambdaImpl.sPool,
function, 6, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
- null);
+ null, null, null);
}
/**
@@ -1028,7 +1032,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
- null);
+ null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1053,7 +1057,7 @@ public interface PooledLambda {
? super G> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
return acquire(PooledLambdaImpl.sPool,
function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
- null);
+ null, null, null);
}
/**
@@ -1077,7 +1081,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
return acquire(PooledLambdaImpl.sPool,
function, 7, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
- null);
+ null, null, null);
}
/**
@@ -1115,7 +1119,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
- null);
+ null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1142,7 +1146,7 @@ public interface PooledLambda {
H arg8) {
return acquire(PooledLambdaImpl.sPool,
function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- null);
+ null, null, null);
}
/**
@@ -1167,7 +1171,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8) {
return acquire(PooledLambdaImpl.sPool,
function, 8, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- null);
+ null, null, null);
}
/**
@@ -1207,7 +1211,7 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- null);
+ null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1235,7 +1239,7 @@ public interface PooledLambda {
E arg5, F arg6, G arg7, H arg8, I arg9) {
return acquire(PooledLambdaImpl.sPool,
function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9);
+ arg9, null, null);
}
/**
@@ -1261,7 +1265,7 @@ public interface PooledLambda {
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9) {
return acquire(PooledLambdaImpl.sPool,
function, 9, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9);
+ arg9, null, null);
}
/**
@@ -1302,7 +1306,209 @@ public interface PooledLambda {
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9);
+ arg9, null, null);
+ return Message.obtain().setCallback(callback.recycleOnUse());
+ }
+ }
+
+ /**
+ * {@link PooledRunnable} factory
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @param arg10 parameter supplied to {@code function} on call
+ * @return a {@link PooledRunnable}, equivalent to lambda:
+ * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) }
+ */
+ static <A, B, C, D, E, F, G, H, I, J> PooledRunnable obtainRunnable(
+ DecConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I, ? super J> function, A arg1, B arg2, C arg3,
+ D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10) {
+ return acquire(PooledLambdaImpl.sPool,
+ function, 10, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ arg9, arg10, null);
+ }
+
+ /**
+ * {@link PooledSupplier} factory
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @param arg10 parameter supplied to {@code function} on call
+ * @return a {@link PooledSupplier}, equivalent to lambda:
+ * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) }
+ */
+ static <A, B, C, D, E, F, G, H, I, J, R> PooledSupplier<R> obtainSupplier(
+ DecFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I, ? super J, ? extends R> function,
+ A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10) {
+ return acquire(PooledLambdaImpl.sPool,
+ function, 10, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ arg9, arg10, null);
+ }
+
+ /**
+ * Factory of {@link Message}s that contain an
+ * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
+ * {@link Message#getCallback internal callback}.
+ *
+ * The callback is equivalent to one obtainable via
+ * {@link #obtainRunnable(QuintConsumer, Object, Object, Object, Object, Object)}
+ *
+ * Note that using this method with {@link android.os.Handler#handleMessage}
+ * is more efficient than the alternative of {@link android.os.Handler#post}
+ * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points
+ * when obtaining {@link Message} and {@link PooledRunnable} from pools separately
+ *
+ * You may optionally set a {@link Message#what} for the message if you want to be
+ * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise
+ * there's no need to do so
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @param arg10 parameter supplied to {@code function} on call
+ * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5, arg6,
+ * arg7, arg8, arg9, arg10) } when handled
+ */
+ static <A, B, C, D, E, F, G, H, I, J> Message obtainMessage(
+ DecConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I, ? super J> function, A arg1, B arg2, C arg3,
+ D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10) {
+ synchronized (Message.sPoolSync) {
+ PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
+ function, 10, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
+ arg8, arg9, arg10, null);
+ return Message.obtain().setCallback(callback.recycleOnUse());
+ }
+ }
+
+ /**
+ * {@link PooledRunnable} factory
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @param arg10 parameter supplied to {@code function} on call
+ * @param arg11 parameter supplied to {@code function} on call
+ * @return a {@link PooledRunnable}, equivalent to lambda:
+ * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10,
+ * arg11) }
+ */
+ static <A, B, C, D, E, F, G, H, I, J, K> PooledRunnable obtainRunnable(
+ UndecConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I, ? super J, ? super K> function, A arg1, B arg2,
+ C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10, K arg11) {
+ return acquire(PooledLambdaImpl.sPool,
+ function, 11, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ arg9, arg10, arg11);
+ }
+
+ /**
+ * {@link PooledSupplier} factory
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @param arg10 parameter supplied to {@code function} on call
+ * @param arg11 parameter supplied to {@code function} on call
+ * @return a {@link PooledSupplier}, equivalent to lambda:
+ * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10,
+ * arg11) }
+ */
+ static <A, B, C, D, E, F, G, H, I, J, K, R> PooledSupplier<R> obtainSupplier(
+ UndecFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I, ? super J, ? super K, ? extends R> function,
+ A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10,
+ K arg11) {
+ return acquire(PooledLambdaImpl.sPool,
+ function, 11, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ arg9, arg10, arg11);
+ }
+
+ /**
+ * Factory of {@link Message}s that contain an
+ * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
+ * {@link Message#getCallback internal callback}.
+ *
+ * The callback is equivalent to one obtainable via
+ * {@link #obtainRunnable(QuintConsumer, Object, Object, Object, Object, Object)}
+ *
+ * Note that using this method with {@link android.os.Handler#handleMessage}
+ * is more efficient than the alternative of {@link android.os.Handler#post}
+ * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points
+ * when obtaining {@link Message} and {@link PooledRunnable} from pools separately
+ *
+ * You may optionally set a {@link Message#what} for the message if you want to be
+ * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise
+ * there's no need to do so
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @param arg10 parameter supplied to {@code function} on call
+ * @param arg11 parameter supplied to {@code function} on call
+ * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5, arg6,
+ * arg7, arg8, arg9, arg10, arg11) } when handled
+ */
+ static <A, B, C, D, E, F, G, H, I, J, K> Message obtainMessage(
+ UndecConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I, ? super J, ? super K> function, A arg1, B arg2,
+ C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10, K arg11) {
+ synchronized (Message.sPoolSync) {
+ PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
+ function, 11, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
+ arg8, arg9, arg10, arg11);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
index 1fdb1f30125f..c7502ef04f1b 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
@@ -16,6 +16,7 @@
package com.android.internal.util.function.pooled;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Message;
import android.text.TextUtils;
@@ -25,6 +26,9 @@ import android.util.Pools;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.BitUtils;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.DecConsumer;
+import com.android.internal.util.function.DecFunction;
+import com.android.internal.util.function.DecPredicate;
import com.android.internal.util.function.HeptConsumer;
import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.HeptPredicate;
@@ -46,6 +50,9 @@ import com.android.internal.util.function.QuintPredicate;
import com.android.internal.util.function.TriConsumer;
import com.android.internal.util.function.TriFunction;
import com.android.internal.util.function.TriPredicate;
+import com.android.internal.util.function.UndecConsumer;
+import com.android.internal.util.function.UndecFunction;
+import com.android.internal.util.function.UndecPredicate;
import java.util.Arrays;
import java.util.Objects;
@@ -62,12 +69,12 @@ import java.util.function.Supplier;
* @hide
*/
final class PooledLambdaImpl<R> extends OmniFunction<Object,
- Object, Object, Object, Object, Object, Object, Object, Object, R> {
+ Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, R> {
private static final boolean DEBUG = false;
private static final String LOG_TAG = "PooledLambdaImpl";
- private static final int MAX_ARGS = 9;
+ private static final int MAX_ARGS = 11;
private static final int MAX_POOL_SIZE = 50;
@@ -133,7 +140,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object,
/**
* Bit schema:
- * AAAAAAAAABCDEEEEEEFFFFFF
+ * AAAAAAAAAAABCDEEEEEEFFFFFF
*
* Where:
* A - whether {@link #mArgs arg} at corresponding index was specified at
@@ -170,18 +177,18 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object,
@Override
R invoke(Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7,
- Object a8, Object a9) {
+ Object a8, Object a9, Object a10, Object a11) {
checkNotRecycled();
if (DEBUG) {
Log.i(LOG_TAG, this + ".invoke("
+ commaSeparateFirstN(
- new Object[] { a1, a2, a3, a4, a5, a6, a7, a8, a9 },
+ new Object[] { a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11 },
LambdaType.decodeArgCount(getFlags(MASK_EXPOSED_AS)))
+ ")");
}
final boolean notUsed = fillInArg(a1) && fillInArg(a2) && fillInArg(a3) && fillInArg(a4)
&& fillInArg(a5) && fillInArg(a6) && fillInArg(a7) && fillInArg(a8)
- && fillInArg(a9);
+ && fillInArg(a9) && fillInArg(a10) && fillInArg(a11);
int argCount = LambdaType.decodeArgCount(getFlags(MASK_FUNC_TYPE));
if (argCount != LambdaType.MASK_ARG_COUNT) {
for (int i = 0; i < argCount; i++) {
@@ -409,6 +416,48 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object,
}
}
} break;
+
+ case 10: {
+ switch (returnType) {
+ case LambdaType.ReturnType.VOID: {
+ ((DecConsumer) mFunc).accept(popArg(0), popArg(1),
+ popArg(2), popArg(3), popArg(4), popArg(5),
+ popArg(6), popArg(7), popArg(8), popArg(9));
+ return null;
+ }
+ case LambdaType.ReturnType.BOOLEAN: {
+ return (R) (Object) ((DecPredicate) mFunc).test(popArg(0),
+ popArg(1), popArg(2), popArg(3), popArg(4),
+ popArg(5), popArg(6), popArg(7), popArg(8), popArg(9));
+ }
+ case LambdaType.ReturnType.OBJECT: {
+ return (R) ((DecFunction) mFunc).apply(popArg(0), popArg(1),
+ popArg(2), popArg(3), popArg(4), popArg(5),
+ popArg(6), popArg(7), popArg(8), popArg(9));
+ }
+ }
+ } break;
+
+ case 11: {
+ switch (returnType) {
+ case LambdaType.ReturnType.VOID: {
+ ((UndecConsumer) mFunc).accept(popArg(0), popArg(1),
+ popArg(2), popArg(3), popArg(4), popArg(5),
+ popArg(6), popArg(7), popArg(8), popArg(9), popArg(10));
+ return null;
+ }
+ case LambdaType.ReturnType.BOOLEAN: {
+ return (R) (Object) ((UndecPredicate) mFunc).test(popArg(0),
+ popArg(1), popArg(2), popArg(3), popArg(4),
+ popArg(5), popArg(6), popArg(7), popArg(8), popArg(9), popArg(10));
+ }
+ case LambdaType.ReturnType.OBJECT: {
+ return (R) ((UndecFunction) mFunc).apply(popArg(0), popArg(1),
+ popArg(2), popArg(3), popArg(4), popArg(5),
+ popArg(6), popArg(7), popArg(8), popArg(9), popArg(10));
+ }
+ }
+ } break;
}
throw new IllegalStateException("Unknown function type: " + LambdaType.toString(funcType));
}
@@ -474,7 +523,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object,
*/
static <E extends PooledLambda> E acquire(Pool pool, Object func,
int fNumArgs, int numPlaceholders, int fReturnType, Object a, Object b, Object c,
- Object d, Object e, Object f, Object g, Object h, Object i) {
+ Object d, Object e, Object f, Object g, Object h, Object i, Object j, Object k) {
PooledLambdaImpl r = acquire(pool);
if (DEBUG) {
Log.i(LOG_TAG,
@@ -492,6 +541,8 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object,
+ ", g = " + g
+ ", h = " + h
+ ", i = " + i
+ + ", j = " + j
+ + ", k = " + k
+ ")");
}
r.mFunc = Preconditions.checkNotNull(func);
@@ -507,6 +558,8 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object,
setIfInBounds(r.mArgs, 6, g);
setIfInBounds(r.mArgs, 7, h);
setIfInBounds(r.mArgs, 8, i);
+ setIfInBounds(r.mArgs, 9, j);
+ setIfInBounds(r.mArgs, 10, k);
return (E) r;
}
@@ -527,19 +580,49 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object,
return r;
}
+ // TODO: add unit test
+ @NonNull
+ private static String getFriendlyName(@NonNull Object function) {
+ // Full function has one of the following formats:
+ // package-$$Lambda$class$randomId
+ // package-$$Lambda$randomId
+ //
+ // We just want just package.class$Lambda (or package$Lambda) respectively
+
+ final String fullFunction = function.toString();
+
+ final int endPkgIdx = fullFunction.indexOf("-$$");
+ if (endPkgIdx == -1) return fullFunction;
+
+ // firstDollarIdx could be either beginning of class or beginning of the random id
+ final int firstDollarIdx = fullFunction.indexOf('$', endPkgIdx + 3);
+ if (firstDollarIdx == -1) return fullFunction;
+
+ final int endClassIdx = fullFunction.indexOf('$', firstDollarIdx + 1);
+ if (endClassIdx == -1) {
+ // Just package
+ return fullFunction.substring(0, endPkgIdx - 1) + "$Lambda";
+ }
+
+ // Package + class
+ return fullFunction.substring(0, endPkgIdx)
+ + fullFunction.substring(firstDollarIdx + 1, endClassIdx)
+ + "$Lambda";
+ }
+
private static void setIfInBounds(Object[] array, int i, Object a) {
if (i < ArrayUtils.size(array)) array[i] = a;
}
@Override
public OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object,
- R> negate() {
+ Object, Object, R> negate() {
throw new UnsupportedOperationException();
}
@Override
public <V> OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object,
- V> andThen(Function<? super R, ? extends V> after) {
+ Object, Object, V> andThen(Function<? super R, ? extends V> after) {
throw new UnsupportedOperationException();
}
@@ -560,12 +643,17 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object,
@Override
public OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object,
- R> recycleOnUse() {
+ Object, Object, R> recycleOnUse() {
if (DEBUG) Log.i(LOG_TAG, this + ".recycleOnUse()");
mFlags |= FLAG_RECYCLE_ON_USE;
return this;
}
+ @Override
+ public String getTraceName() {
+ return getFriendlyName(mFunc);
+ }
+
private boolean isRecycled() {
return (mFlags & FLAG_RECYCLED) != 0;
}
@@ -647,6 +735,8 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object,
case 7: return "Hept";
case 8: return "Oct";
case 9: return "Nona";
+ case 10: return "Dec";
+ case 11: return "Undec";
default: return "" + argCount + "arg";
}
}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledRunnable.java b/core/java/com/android/internal/util/function/pooled/PooledRunnable.java
index 89ca82e2f3ce..f0bc2cadb1c5 100644
--- a/core/java/com/android/internal/util/function/pooled/PooledRunnable.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledRunnable.java
@@ -16,6 +16,8 @@
package com.android.internal.util.function.pooled;
+import android.os.TraceNameSupplier;
+
import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
/**
@@ -24,7 +26,8 @@ import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
* @see PooledLambda
* @hide
*/
-public interface PooledRunnable extends PooledLambda, Runnable, ThrowingRunnable {
+public interface PooledRunnable
+ extends PooledLambda, Runnable, ThrowingRunnable, TraceNameSupplier {
/** @inheritDoc */
PooledRunnable recycleOnUse();
}
diff --git a/core/java/com/android/internal/view/AppearanceRegion.aidl b/core/java/com/android/internal/view/AppearanceRegion.aidl
new file mode 100644
index 000000000000..1638bf5bbfd6
--- /dev/null
+++ b/core/java/com/android/internal/view/AppearanceRegion.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.view;
+
+parcelable AppearanceRegion;
diff --git a/core/java/com/android/internal/view/AppearanceRegion.java b/core/java/com/android/internal/view/AppearanceRegion.java
new file mode 100644
index 000000000000..1a0cb4b0dfbf
--- /dev/null
+++ b/core/java/com/android/internal/view/AppearanceRegion.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.view;
+
+import android.annotation.NonNull;
+import android.graphics.Rect;
+import android.os.Parcelable;
+import android.view.InsetsFlags;
+import android.view.ViewDebug;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Specifies which region applies which appearance.
+ */
+@DataClass
+public class AppearanceRegion implements Parcelable {
+
+ private int mAppearance;
+ private @NonNull Rect mBounds;
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final AppearanceRegion sa = (AppearanceRegion) o;
+ return mAppearance == sa.mAppearance && mBounds.equals(sa.mBounds);
+ }
+
+ @Override
+ public String toString() {
+ final String appearanceString =
+ ViewDebug.flagsToString(InsetsFlags.class, "appearance", mAppearance);
+ return "AppearanceRegion{" + appearanceString + " bounds=" + mBounds.toShortString() + "}";
+ }
+
+
+
+ // Code below generated by codegen v1.0.7.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/view/AppearanceRegion.java
+
+
+ @DataClass.Generated.Member
+ public AppearanceRegion(
+ int appearance,
+ @NonNull Rect bounds) {
+ this.mAppearance = appearance;
+ this.mBounds = bounds;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mBounds);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public int getAppearance() {
+ return mAppearance;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Rect getBounds() {
+ return mBounds;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeInt(mAppearance);
+ dest.writeTypedObject(mBounds, flags);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ protected AppearanceRegion(android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int appearance = in.readInt();
+ Rect bounds = (Rect) in.readTypedObject(Rect.CREATOR);
+
+ this.mAppearance = appearance;
+ this.mBounds = bounds;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mBounds);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<AppearanceRegion> CREATOR
+ = new Parcelable.Creator<AppearanceRegion>() {
+ @Override
+ public AppearanceRegion[] newArray(int size) {
+ return new AppearanceRegion[size];
+ }
+
+ @Override
+ public AppearanceRegion createFromParcel(android.os.Parcel in) {
+ return new AppearanceRegion(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1570909617357L,
+ codegenVersion = "1.0.7",
+ sourceFile = "frameworks/base/core/java/com/android/internal/view/AppearanceRegion.java",
+ inputSignatures = "private int mAppearance\nprivate @android.annotation.NonNull android.graphics.Rect mBounds\npublic @java.lang.Override boolean equals(java.lang.Object)\npublic @java.lang.Override java.lang.String toString()\nclass AppearanceRegion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass")
+ @Deprecated
+ private void __metadata() {}
+
+}
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index cc468f41a7f3..7e1f13afc2cb 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -30,6 +30,7 @@ import android.view.IWindowSession;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.PointerIcon;
+import android.view.WindowInsets.Type.InsetType;
import com.android.internal.os.IResultReceiver;
@@ -75,6 +76,14 @@ public class BaseIWindow extends IWindow.Stub {
}
@Override
+ public void showInsets(@InsetType int types, boolean fromIme) throws RemoteException {
+ }
+
+ @Override
+ public void hideInsets(@InsetType int types, boolean fromIme) throws RemoteException {
+ }
+
+ @Override
public void moved(int newX, int newY) {
}
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index e7a02a76bcdf..326094ecf9b4 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -24,6 +24,7 @@ import android.security.keystore.recovery.KeyChainSnapshot;
import android.security.keystore.recovery.KeyChainProtectionParams;
import android.security.keystore.recovery.RecoveryCertPath;
import com.android.internal.widget.ICheckCredentialProgressCallback;
+import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.VerifyCredentialResponse;
import java.util.Map;
@@ -42,19 +43,16 @@ interface ILockSettings {
long getLong(in String key, in long defaultValue, in int userId);
@UnsupportedAppUsage
String getString(in String key, in String defaultValue, in int userId);
- boolean setLockCredential(in byte[] credential, int type, in byte[] savedCredential, int requestedQuality, int userId, boolean allowUntrustedChange);
+ boolean setLockCredential(in LockscreenCredential credential, in LockscreenCredential savedCredential, int userId, boolean allowUntrustedChange);
void resetKeyStore(int userId);
- VerifyCredentialResponse checkCredential(in byte[] credential, int type, int userId,
+ VerifyCredentialResponse checkCredential(in LockscreenCredential credential, int userId,
in ICheckCredentialProgressCallback progressCallback);
- VerifyCredentialResponse verifyCredential(in byte[] credential, int type, long challenge, int userId);
- VerifyCredentialResponse verifyTiedProfileChallenge(in byte[] credential, int type, long challenge, int userId);
+ VerifyCredentialResponse verifyCredential(in LockscreenCredential credential, long challenge, int userId);
+ VerifyCredentialResponse verifyTiedProfileChallenge(in LockscreenCredential credential, long challenge, int userId);
boolean checkVoldPassword(int userId);
- @UnsupportedAppUsage
- boolean havePattern(int userId);
- @UnsupportedAppUsage
- boolean havePassword(int userId);
- byte[] getHashFactor(in byte[] currentCredential, int userId);
- void setSeparateProfileChallengeEnabled(int userId, boolean enabled, in byte[] managedUserPassword);
+ int getCredentialType(int userId);
+ byte[] getHashFactor(in LockscreenCredential currentCredential, int userId);
+ void setSeparateProfileChallengeEnabled(int userId, boolean enabled, in LockscreenCredential managedUserPassword);
boolean getSeparateProfileChallengeEnabled(int userId);
void registerStrongAuthTracker(in IStrongAuthTracker tracker);
void unregisterStrongAuthTracker(in IStrongAuthTracker tracker);
diff --git a/core/java/com/android/internal/widget/LockPatternChecker.java b/core/java/com/android/internal/widget/LockPatternChecker.java
index 09bc28c1f5ec..85a45fd8e0c0 100644
--- a/core/java/com/android/internal/widget/LockPatternChecker.java
+++ b/core/java/com/android/internal/widget/LockPatternChecker.java
@@ -1,13 +1,9 @@
package com.android.internal.widget;
-import android.annotation.UnsupportedAppUsage;
import android.os.AsyncTask;
import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* Helper class to check/verify PIN/Password/Pattern asynchronously.
*/
@@ -53,34 +49,28 @@ public final class LockPatternChecker {
}
/**
- * Verify a pattern asynchronously.
+ * Verify a lockscreen credential asynchronously.
*
* @param utils The LockPatternUtils instance to use.
- * @param pattern The pattern to check.
- * @param challenge The challenge to verify against the pattern.
- * @param userId The user to check against the pattern.
+ * @param credential The credential to check.
+ * @param challenge The challenge to verify against the credential.
+ * @param userId The user to check against the credential.
* @param callback The callback to be invoked with the verification result.
*/
- public static AsyncTask<?, ?, ?> verifyPattern(final LockPatternUtils utils,
- final List<LockPatternView.Cell> pattern,
+ public static AsyncTask<?, ?, ?> verifyCredential(final LockPatternUtils utils,
+ final LockscreenCredential credential,
final long challenge,
final int userId,
final OnVerifyCallback callback) {
+ // Create a copy of the credential since checking credential is asynchrounous.
+ final LockscreenCredential credentialCopy = credential.duplicate();
AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() {
private int mThrottleTimeout;
- private List<LockPatternView.Cell> patternCopy;
-
- @Override
- protected void onPreExecute() {
- // Make a copy of the pattern to prevent race conditions.
- // No need to clone the individual cells because they are immutable.
- patternCopy = new ArrayList(pattern);
- }
@Override
protected byte[] doInBackground(Void... args) {
try {
- return utils.verifyPattern(patternCopy, challenge, userId);
+ return utils.verifyCredential(credentialCopy, challenge, userId);
} catch (RequestThrottledException ex) {
mThrottleTimeout = ex.getTimeoutMs();
return null;
@@ -90,6 +80,12 @@ public final class LockPatternChecker {
@Override
protected void onPostExecute(byte[] result) {
callback.onVerified(result, mThrottleTimeout);
+ credentialCopy.zeroize();
+ }
+
+ @Override
+ protected void onCancelled() {
+ credentialCopy.zeroize();
}
};
task.execute();
@@ -97,32 +93,26 @@ public final class LockPatternChecker {
}
/**
- * Checks a pattern asynchronously.
+ * Checks a lockscreen credential asynchronously.
*
* @param utils The LockPatternUtils instance to use.
- * @param pattern The pattern to check.
- * @param userId The user to check against the pattern.
+ * @param credential The credential to check.
+ * @param userId The user to check against the credential.
* @param callback The callback to be invoked with the check result.
*/
- public static AsyncTask<?, ?, ?> checkPattern(final LockPatternUtils utils,
- final List<LockPatternView.Cell> pattern,
+ public static AsyncTask<?, ?, ?> checkCredential(final LockPatternUtils utils,
+ final LockscreenCredential credential,
final int userId,
final OnCheckCallback callback) {
+ // Create a copy of the credential since checking credential is asynchrounous.
+ final LockscreenCredential credentialCopy = credential.duplicate();
AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() {
private int mThrottleTimeout;
- private List<LockPatternView.Cell> patternCopy;
-
- @Override
- protected void onPreExecute() {
- // Make a copy of the pattern to prevent race conditions.
- // No need to clone the individual cells because they are immutable.
- patternCopy = new ArrayList(pattern);
- }
@Override
protected Boolean doInBackground(Void... args) {
try {
- return utils.checkPattern(patternCopy, userId, callback::onEarlyMatched);
+ return utils.checkCredential(credentialCopy, userId, callback::onEarlyMatched);
} catch (RequestThrottledException ex) {
mThrottleTimeout = ex.getTimeoutMs();
return false;
@@ -132,11 +122,13 @@ public final class LockPatternChecker {
@Override
protected void onPostExecute(Boolean result) {
callback.onChecked(result, mThrottleTimeout);
+ credentialCopy.zeroize();
}
@Override
protected void onCancelled() {
callback.onCancelled();
+ credentialCopy.zeroize();
}
};
task.execute();
@@ -144,84 +136,29 @@ public final class LockPatternChecker {
}
/**
- * Verify a password asynchronously.
+ * Perform a lockscreen credential verification explicitly on a managed profile with unified
+ * challenge, using the parent user's credential.
*
* @param utils The LockPatternUtils instance to use.
- * @param password The password to check.
- * @param challenge The challenge to verify against the pattern.
- * @param userId The user to check against the pattern.
- * @param callback The callback to be invoked with the verification result.
- *
- * @deprecated Pass the password as a byte array.
- */
- @Deprecated
- public static AsyncTask<?, ?, ?> verifyPassword(final LockPatternUtils utils,
- final String password,
- final long challenge,
- final int userId,
- final OnVerifyCallback callback) {
- byte[] passwordBytes = password != null ? password.getBytes() : null;
- return verifyPassword(utils, passwordBytes, challenge, userId, callback);
- }
-
- /**
- * Verify a password asynchronously.
- *
- * @param utils The LockPatternUtils instance to use.
- * @param password The password to check.
- * @param challenge The challenge to verify against the pattern.
- * @param userId The user to check against the pattern.
- * @param callback The callback to be invoked with the verification result.
- */
- public static AsyncTask<?, ?, ?> verifyPassword(final LockPatternUtils utils,
- final byte[] password,
- final long challenge,
- final int userId,
- final OnVerifyCallback callback) {
- AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() {
- private int mThrottleTimeout;
-
- @Override
- protected byte[] doInBackground(Void... args) {
- try {
- return utils.verifyPassword(password, challenge, userId);
- } catch (RequestThrottledException ex) {
- mThrottleTimeout = ex.getTimeoutMs();
- return null;
- }
- }
-
- @Override
- protected void onPostExecute(byte[] result) {
- callback.onVerified(result, mThrottleTimeout);
- }
- };
- task.execute();
- return task;
- }
-
- /**
- * Verify a password asynchronously.
- *
- * @param utils The LockPatternUtils instance to use.
- * @param password The password to check.
- * @param challenge The challenge to verify against the pattern.
- * @param userId The user to check against the pattern.
+ * @param credential The credential to check.
+ * @param challenge The challenge to verify against the credential.
+ * @param userId The user to check against the credential.
* @param callback The callback to be invoked with the verification result.
*/
public static AsyncTask<?, ?, ?> verifyTiedProfileChallenge(final LockPatternUtils utils,
- final byte[] password,
- final boolean isPattern,
+ final LockscreenCredential credential,
final long challenge,
final int userId,
final OnVerifyCallback callback) {
+ // Create a copy of the credential since checking credential is asynchrounous.
+ final LockscreenCredential credentialCopy = credential.duplicate();
AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() {
private int mThrottleTimeout;
@Override
protected byte[] doInBackground(Void... args) {
try {
- return utils.verifyTiedProfileChallenge(password, isPattern, challenge, userId);
+ return utils.verifyTiedProfileChallenge(credentialCopy, challenge, userId);
} catch (RequestThrottledException ex) {
mThrottleTimeout = ex.getTimeoutMs();
return null;
@@ -231,64 +168,12 @@ public final class LockPatternChecker {
@Override
protected void onPostExecute(byte[] result) {
callback.onVerified(result, mThrottleTimeout);
- }
- };
- task.execute();
- return task;
- }
-
- /**
- * Checks a password asynchronously.
- *
- * @param utils The LockPatternUtils instance to use.
- * @param password The password to check.
- * @param userId The user to check against the pattern.
- * @param callback The callback to be invoked with the check result.
- * @deprecated Pass passwords as byte[]
- */
- @UnsupportedAppUsage
- @Deprecated
- public static AsyncTask<?, ?, ?> checkPassword(final LockPatternUtils utils,
- final String password,
- final int userId,
- final OnCheckCallback callback) {
- byte[] passwordBytes = password != null ? password.getBytes() : null;
- return checkPassword(utils, passwordBytes, userId, callback);
- }
-
- /**
- * Checks a password asynchronously.
- *
- * @param utils The LockPatternUtils instance to use.
- * @param passwordBytes The password to check.
- * @param userId The user to check against the pattern.
- * @param callback The callback to be invoked with the check result.
- */
- public static AsyncTask<?, ?, ?> checkPassword(final LockPatternUtils utils,
- final byte[] passwordBytes,
- final int userId,
- final OnCheckCallback callback) {
- AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() {
- private int mThrottleTimeout;
-
- @Override
- protected Boolean doInBackground(Void... args) {
- try {
- return utils.checkPassword(passwordBytes, userId, callback::onEarlyMatched);
- } catch (RequestThrottledException ex) {
- mThrottleTimeout = ex.getTimeoutMs();
- return false;
- }
- }
-
- @Override
- protected void onPostExecute(Boolean result) {
- callback.onChecked(result, mThrottleTimeout);
+ credentialCopy.zeroize();
}
@Override
protected void onCancelled() {
- callback.onCancelled();
+ credentialCopy.zeroize();
}
};
task.execute();
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index a1af0c06bba7..8b889593c7d3 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -17,15 +17,13 @@
package com.android.internal.widget;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_MANAGED;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.app.admin.DevicePolicyManager;
@@ -77,7 +75,6 @@ import java.util.StringJoiner;
* Utilities for the lock pattern and its settings.
*/
public class LockPatternUtils {
-
private static final String TAG = "LockPatternUtils";
private static final boolean FRP_CREDENTIAL_ENABLED = true;
@@ -114,15 +111,22 @@ public class LockPatternUtils {
*/
public static final int MIN_PATTERN_REGISTER_FAIL = MIN_LOCK_PATTERN_SIZE;
+ // NOTE: When modifying this, make sure credential sufficiency validation logic is intact.
public static final int CREDENTIAL_TYPE_NONE = -1;
public static final int CREDENTIAL_TYPE_PATTERN = 1;
- public static final int CREDENTIAL_TYPE_PASSWORD = 2;
+ // This is the legacy value persisted on disk. Never return it to clients, but internally
+ // we still need it to handle upgrade cases.
+ public static final int CREDENTIAL_TYPE_PASSWORD_OR_PIN = 2;
+ public static final int CREDENTIAL_TYPE_PIN = 3;
+ public static final int CREDENTIAL_TYPE_PASSWORD = 4;
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"CREDENTIAL_TYPE_"}, value = {
CREDENTIAL_TYPE_NONE,
CREDENTIAL_TYPE_PATTERN,
- CREDENTIAL_TYPE_PASSWORD, // Either pin or password.
+ CREDENTIAL_TYPE_PASSWORD,
+ CREDENTIAL_TYPE_PIN,
+ // CREDENTIAL_TYPE_PASSWORD_OR_PIN is missing on purpose.
})
public @interface CredentialType {}
@@ -168,6 +172,7 @@ public class LockPatternUtils {
public static final String SYNTHETIC_PASSWORD_HANDLE_KEY = "sp-handle";
public static final String SYNTHETIC_PASSWORD_ENABLED_KEY = "enable-sp";
+ public static final int SYNTHETIC_PASSWORD_ENABLED_BY_DEFAULT = 1;
private static final String HISTORY_DELIMITER = ",";
@UnsupportedAppUsage
@@ -289,10 +294,10 @@ public class LockPatternUtils {
return getDevicePolicyManager().getPasswordMaximumLength(quality);
}
- /**
- * Gets the device policy password mode. If the mode is non-specific, returns
- * MODE_PATTERN which allows the user to choose anything.
- */
+ public PasswordMetrics getRequestedPasswordMetrics(int userId) {
+ return getDevicePolicyManager().getPasswordMinimumMetrics(userId);
+ }
+
public int getRequestedPasswordQuality(int userId) {
return getDevicePolicyManager().getPasswordQuality(null, userId);
}
@@ -365,11 +370,24 @@ public class LockPatternUtils {
null /* componentName */, userId);
}
- private byte[] verifyCredential(byte[] credential, int type, long challenge, int userId)
- throws RequestThrottledException {
+ /**
+ * Check to see if a credential matches the saved one.
+ * If credential matches, return an opaque attestation that the challenge was verified.
+ *
+ * @param credential The credential to check.
+ * @param challenge The challenge to verify against the credential
+ * @param userId The user whose credential is being verified
+ * @return the attestation that the challenge was verified, or null
+ * @throws RequestThrottledException if credential verification is being throttled due to
+ * to many incorrect attempts.
+ * @throws IllegalStateException if called on the main thread.
+ */
+ public byte[] verifyCredential(@NonNull LockscreenCredential credential, long challenge,
+ int userId) throws RequestThrottledException {
+ throwIfCalledOnMainThread();
try {
- VerifyCredentialResponse response = getLockSettings().verifyCredential(credential,
- type, challenge, userId);
+ VerifyCredentialResponse response = getLockSettings().verifyCredential(
+ credential, challenge, userId);
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
return response.getPayload();
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
@@ -378,16 +396,29 @@ public class LockPatternUtils {
return null;
}
} catch (RemoteException re) {
+ Log.e(TAG, "failed to verify credential", re);
return null;
}
}
- private boolean checkCredential(byte[] credential, int type, int userId,
+ /**
+ * Check to see if a credential matches the saved one.
+ *
+ * @param credential The credential to check.
+ * @param userId The user whose credential is being checked
+ * @param progressCallback callback to deliver early signal that the credential matches
+ * @return {@code true} if credential matches, {@code false} otherwise
+ * @throws RequestThrottledException if credential verification is being throttled due to
+ * to many incorrect attempts.
+ * @throws IllegalStateException if called on the main thread.
+ */
+ public boolean checkCredential(@NonNull LockscreenCredential credential, int userId,
@Nullable CheckCredentialProgressCallback progressCallback)
throws RequestThrottledException {
+ throwIfCalledOnMainThread();
try {
- VerifyCredentialResponse response = getLockSettings().checkCredential(credential, type,
- userId, wrapCallback(progressCallback));
+ VerifyCredentialResponse response = getLockSettings().checkCredential(
+ credential, userId, wrapCallback(progressCallback));
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
return true;
@@ -397,84 +428,31 @@ public class LockPatternUtils {
return false;
}
} catch (RemoteException re) {
+ Log.e(TAG, "failed to check credential", re);
return false;
}
}
/**
- * Check to see if a pattern matches the saved pattern.
- * If pattern matches, return an opaque attestation that the challenge
- * was verified.
- *
- * @param pattern The pattern to check.
- * @param challenge The challenge to verify against the pattern
- * @return the attestation that the challenge was verified, or null.
- */
- public byte[] verifyPattern(List<LockPatternView.Cell> pattern, long challenge, int userId)
- throws RequestThrottledException {
- throwIfCalledOnMainThread();
- return verifyCredential(patternToByteArray(pattern), CREDENTIAL_TYPE_PATTERN, challenge,
- userId);
- }
-
- /**
- * Check to see if a pattern matches the saved pattern. If no pattern exists,
- * always returns true.
- * @param pattern The pattern to check.
- * @return Whether the pattern matches the stored one.
- */
- public boolean checkPattern(List<LockPatternView.Cell> pattern, int userId)
- throws RequestThrottledException {
- return checkPattern(pattern, userId, null /* progressCallback */);
- }
-
- /**
- * Check to see if a pattern matches the saved pattern. If no pattern exists,
- * always returns true.
- * @param pattern The pattern to check.
- * @return Whether the pattern matches the stored one.
- */
- public boolean checkPattern(List<LockPatternView.Cell> pattern, int userId,
- @Nullable CheckCredentialProgressCallback progressCallback)
- throws RequestThrottledException {
- throwIfCalledOnMainThread();
- return checkCredential(patternToByteArray(pattern), CREDENTIAL_TYPE_PATTERN, userId,
- progressCallback);
- }
-
- /**
- * Check to see if a password matches the saved password.
- * If password matches, return an opaque attestation that the challenge
- * was verified.
- *
- * @param password The password to check.
- * @param challenge The challenge to verify against the password
- * @return the attestation that the challenge was verified, or null.
- */
- public byte[] verifyPassword(byte[] password, long challenge, int userId)
- throws RequestThrottledException {
- throwIfCalledOnMainThread();
- return verifyCredential(password, CREDENTIAL_TYPE_PASSWORD, challenge, userId);
- }
-
-
- /**
- * Check to see if a password matches the saved password.
- * If password matches, return an opaque attestation that the challenge
- * was verified.
+ * Check if the credential of a managed profile with unified challenge matches. In this context,
+ * The credential should be the parent user's lockscreen password. If credential matches,
+ * return an opaque attestation associated with the managed profile that the challenge was
+ * verified.
*
- * @param password The password to check.
- * @param challenge The challenge to verify against the password
- * @return the attestation that the challenge was verified, or null.
- */
- public byte[] verifyTiedProfileChallenge(byte[] password, boolean isPattern, long challenge,
- int userId) throws RequestThrottledException {
+ * @param credential The parent user's credential to check.
+ * @param challenge The challenge to verify against the credential
+ * @return the attestation that the challenge was verified, or null
+ * @param userId The managed profile user id
+ * @throws RequestThrottledException if credential verification is being throttled due to
+ * to many incorrect attempts.
+ * @throws IllegalStateException if called on the main thread.
+ */
+ public byte[] verifyTiedProfileChallenge(@NonNull LockscreenCredential credential,
+ long challenge, int userId) throws RequestThrottledException {
throwIfCalledOnMainThread();
try {
VerifyCredentialResponse response =
- getLockSettings().verifyTiedProfileChallenge(password,
- isPattern ? CREDENTIAL_TYPE_PATTERN : CREDENTIAL_TYPE_PASSWORD, challenge,
- userId);
+ getLockSettings().verifyTiedProfileChallenge(credential, challenge, userId);
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
return response.getPayload();
@@ -484,66 +462,12 @@ public class LockPatternUtils {
return null;
}
} catch (RemoteException re) {
+ Log.e(TAG, "failed to verify tied profile credential", re);
return null;
}
}
/**
- *
- * Check to see if a password matches the saved password. If no password exists,
- * always returns true.
- * @param password The password to check.
- * @return Whether the password matches the stored one.
- */
- @UnsupportedAppUsage
- public boolean checkPassword(String password, int userId) throws RequestThrottledException {
- byte[] passwordBytes = password != null ? password.getBytes() : null;
- return checkPassword(passwordBytes, userId, null /* progressCallback */);
- }
-
-
- /**
- *
- * Check to see if a password matches the saved password. If no password exists,
- * always returns true.
- * @param password The password to check.
- * @return Whether the password matches the stored one.
- */
- public boolean checkPassword(byte[] password, int userId) throws RequestThrottledException {
- return checkPassword(password, userId, null /* progressCallback */);
- }
-
- // TODO(b/120484642): This method is necessary for vendor/qcom code and is a hidden api
- /* *
- * Check to see if a password matches the saved password. If no password exists,
- * always returns true.
- * @param password The password to check.
- * @return Whether the password matches the stored one.
- */
- public boolean checkPassword(String password, int userId,
- @Nullable CheckCredentialProgressCallback progressCallback)
- throws RequestThrottledException {
- byte[] passwordBytes = password != null ? password.getBytes() : null;
- throwIfCalledOnMainThread();
- return checkCredential(passwordBytes, CREDENTIAL_TYPE_PASSWORD, userId, progressCallback);
-
- }
-
- /**
- * Check to see if a password matches the saved password. If no password exists,
- * always returns true.
- * @param password The password to check.
- * @return Whether the password matches the stored one.
- */
-
- public boolean checkPassword(byte[] password, int userId,
- @Nullable CheckCredentialProgressCallback progressCallback)
- throws RequestThrottledException {
- throwIfCalledOnMainThread();
- return checkCredential(password, CREDENTIAL_TYPE_PASSWORD, userId, progressCallback);
- }
-
- /**
* Check to see if vold already has the password.
* Note that this also clears vold's copy of the password.
* @return Whether the vold password matches or not.
@@ -552,6 +476,7 @@ public class LockPatternUtils {
try {
return getLockSettings().checkVoldPassword(userId);
} catch (RemoteException re) {
+ Log.e(TAG, "failed to check vold password", re);
return false;
}
}
@@ -560,7 +485,8 @@ public class LockPatternUtils {
* Returns the password history hash factor, needed to check new password against password
* history with {@link #checkPasswordHistory(byte[], byte[], int)}
*/
- public byte[] getPasswordHistoryHashFactor(byte[] currentPassword, int userId) {
+ public byte[] getPasswordHistoryHashFactor(@NonNull LockscreenCredential currentPassword,
+ int userId) {
try {
return getLockSettings().getHashFactor(currentPassword, userId);
} catch (RemoteException e) {
@@ -604,30 +530,6 @@ public class LockPatternUtils {
}
/**
- * Check to see if the user has stored a lock pattern.
- * @return Whether a saved pattern exists.
- */
- private boolean savedPatternExists(int userId) {
- try {
- return getLockSettings().havePattern(userId);
- } catch (RemoteException re) {
- return false;
- }
- }
-
- /**
- * Check to see if the user has stored a lock pattern.
- * @return Whether a saved pattern exists.
- */
- private boolean savedPasswordExists(int userId) {
- try {
- return getLockSettings().havePassword(userId);
- } catch (RemoteException re) {
- return false;
- }
- }
-
- /**
* Return true if the user has ever chosen a pattern. This is true even if the pattern is
* currently cleared.
*
@@ -648,22 +550,11 @@ public class LockPatternUtils {
/**
* Used by device policy manager to validate the current password
* information it has.
+ * @Deprecated use {@link #getKeyguardStoredPasswordQuality}
*/
@UnsupportedAppUsage
public int getActivePasswordQuality(int userId) {
- int quality = getKeyguardStoredPasswordQuality(userId);
-
- if (isLockPasswordEnabled(quality, userId)) {
- // Quality is a password and a password exists. Return the quality.
- return quality;
- }
-
- if (isLockPatternEnabled(quality, userId)) {
- // Quality is a pattern and a pattern exists. Return the quality.
- return quality;
- }
-
- return PASSWORD_QUALITY_UNSPECIFIED;
+ return getKeyguardStoredPasswordQuality(userId);
}
/**
@@ -679,57 +570,6 @@ public class LockPatternUtils {
}
/**
- * Clear any lock pattern or password.
-
- * <p> This method will fail (returning {@code false}) if the previously
- * saved password provided is incorrect, or if the lockscreen verification
- * is still being throttled.
- *
- * @param savedCredential The previously saved credential
- * @param userHandle the user whose pattern is to be saved.
- * @return whether this was successful or not.
- * @throws RuntimeException if password change encountered an unrecoverable error.
- */
- public boolean clearLock(byte[] savedCredential, int userHandle) {
- return clearLock(savedCredential, userHandle, false);
- }
-
- /**
- * Clear any lock pattern or password, with the option to ignore incorrect existing credential.
- * <p> This method will fail (returning {@code false}) if the previously
- * saved password provided is incorrect, or if the lockscreen verification
- * is still being throttled.
- *
- * @param savedCredential The previously saved credential
- * @param userHandle the user whose pattern is to be saved.
- * @return whether this was successful or not.
- * @throws RuntimeException if password change encountered an unrecoverable error.
- */
- public boolean clearLock(byte[] savedCredential, int userHandle, boolean allowUntrustedChange) {
- final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
- setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED, userHandle);
-
- try {
- if (!getLockSettings().setLockCredential(null, CREDENTIAL_TYPE_NONE, savedCredential,
- PASSWORD_QUALITY_UNSPECIFIED, userHandle, allowUntrustedChange)) {
- return false;
- }
- } catch (RemoteException | RuntimeException e) {
- setKeyguardStoredPasswordQuality(currentQuality, userHandle);
- throw new RuntimeException("Failed to clear lock", e);
- }
-
- if (userHandle == UserHandle.USER_SYSTEM) {
- // Set the encryption password to default.
- updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
- setCredentialRequiredToDecrypt(false);
- }
-
- onAfterChangingPassword(userHandle);
- return true;
- }
-
- /**
* Disable showing lock screen at all for a given user.
* This is only meaningful if pattern, pin or password are not set.
*
@@ -762,75 +602,97 @@ public class LockPatternUtils {
|| isDemoUser;
}
+ /** Returns if the given quality maps to an alphabetic password */
+ public static boolean isQualityAlphabeticPassword(int quality) {
+ return quality >= PASSWORD_QUALITY_ALPHABETIC;
+ }
+
+ /** Returns if the given quality maps to an numeric pin */
+ public static boolean isQualityNumericPin(int quality) {
+ return quality == PASSWORD_QUALITY_NUMERIC || quality == PASSWORD_QUALITY_NUMERIC_COMPLEX;
+ }
+
+ /** Returns the canonical password quality corresponding to the given credential type. */
+ public static int credentialTypeToPasswordQuality(int credentialType) {
+ switch (credentialType) {
+ case CREDENTIAL_TYPE_NONE:
+ return PASSWORD_QUALITY_UNSPECIFIED;
+ case CREDENTIAL_TYPE_PATTERN:
+ return PASSWORD_QUALITY_SOMETHING;
+ case CREDENTIAL_TYPE_PIN:
+ return PASSWORD_QUALITY_NUMERIC;
+ case CREDENTIAL_TYPE_PASSWORD:
+ return PASSWORD_QUALITY_ALPHABETIC;
+ default:
+ throw new IllegalStateException("Unknown type: " + credentialType);
+ }
+ }
+
/**
- * Save a lock pattern.
+ * Save a new lockscreen credential.
*
- * <p> This method will fail (returning {@code false}) if the previously saved pattern provided
- * is incorrect, or if the lockscreen verification is still being throttled.
+ * <p> This method will fail (returning {@code false}) if the previously saved credential
+ * provided is incorrect, or if the lockscreen verification is still being throttled.
+ *
+ * @param newCredential The new credential to save
+ * @param savedCredential The current credential
+ * @param userId the user whose lockscreen credential is to be changed
*
- * @param pattern The new pattern to save.
- * @param savedPattern The previously saved pattern, converted to byte[] format
- * @param userId the user whose pattern is to be saved.
- * @return whether this was successful or not.
+ * @return whether this method saved the new password successfully or not. This flow will fail
+ * and return false if the given credential is wrong.
* @throws RuntimeException if password change encountered an unrecoverable error.
*/
- public boolean saveLockPattern(List<LockPatternView.Cell> pattern, byte[] savedPattern,
- int userId) {
- return saveLockPattern(pattern, savedPattern, userId, false);
+ public boolean setLockCredential(@NonNull LockscreenCredential newCredential,
+ @NonNull LockscreenCredential savedCredential, int userId) {
+ return setLockCredential(newCredential, savedCredential, userId, false);
}
/**
- * Save a lock pattern.
- *
+ * Save a new lockscreen credential.
* <p> This method will fail (returning {@code false}) if the previously saved pattern provided
- * is incorrect, or if the lockscreen verification is still being throttled.
+ * is incorrect and allowUntrustedChange is false, or if the lockscreen verification is still
+ * being throttled.
+ * @param newCredential The new credential to save
+ * @param savedCredential The current credential
+ * @param userHandle the user whose lockscreen credential is to be changed
+ * @param allowUntrustedChange whether we want to allow saving a new pattern if the existing
+ * credentialt being provided is incorrect.
*
- * @param pattern The new pattern to save.
- * @param savedPattern The previously saved pattern, converted to byte[] format
- * @param userId the user whose pattern is to be saved.
- * @param allowUntrustedChange whether we want to allow saving a new password if the existing
- * password being provided is incorrect.
- * @return whether this was successful or not.
+ * @return whether this method saved the new password successfully or not. This flow will fail
+ * and return false if the given credential is wrong and allowUntrustedChange is false.
* @throws RuntimeException if password change encountered an unrecoverable error.
*/
- public boolean saveLockPattern(List<LockPatternView.Cell> pattern, byte[] savedPattern,
- int userId, boolean allowUntrustedChange) {
+ public boolean setLockCredential(@NonNull LockscreenCredential newCredential,
+ @NonNull LockscreenCredential savedCredential, int userHandle,
+ boolean allowUntrustedChange) {
if (!hasSecureLockScreen()) {
throw new UnsupportedOperationException(
"This operation requires the lock screen feature.");
}
- if (pattern == null || pattern.size() < MIN_LOCK_PATTERN_SIZE) {
- throw new IllegalArgumentException("pattern must not be null and at least "
- + MIN_LOCK_PATTERN_SIZE + " dots long.");
- }
+ newCredential.checkLength();
- final byte[] bytePattern = patternToByteArray(pattern);
- final int currentQuality = getKeyguardStoredPasswordQuality(userId);
- setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_SOMETHING, userId);
try {
- if (!getLockSettings().setLockCredential(bytePattern, CREDENTIAL_TYPE_PATTERN,
- savedPattern, PASSWORD_QUALITY_SOMETHING, userId, allowUntrustedChange)) {
+ if (!getLockSettings().setLockCredential(
+ newCredential, savedCredential, userHandle, allowUntrustedChange)) {
return false;
}
- } catch (RemoteException | RuntimeException e) {
- setKeyguardStoredPasswordQuality(currentQuality, userId);
- throw new RuntimeException("Couldn't save lock pattern", e);
- }
- // Update the device encryption password.
- if (userId == UserHandle.USER_SYSTEM
- && LockPatternUtils.isDeviceEncryptionEnabled()) {
- if (!shouldEncryptWithCredentials(true)) {
- clearEncryptionPassword();
- } else {
- updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, bytePattern);
- }
+ } catch (RemoteException e) {
+ throw new RuntimeException("Unable to save lock password", e);
}
- reportPatternWasChosen(userId);
- onAfterChangingPassword(userId);
+ onPostPasswordChanged(newCredential, userHandle);
return true;
}
+ private void onPostPasswordChanged(LockscreenCredential newCredential, int userHandle) {
+ updateEncryptionPasswordIfNeeded(newCredential, userHandle);
+ if (newCredential.isPattern()) {
+ reportPatternWasChosen(userHandle);
+ }
+ updatePasswordHistory(newCredential, userHandle);
+ reportEnabledTrustAgentsChanged(userHandle);
+ }
+
/**
* clears stored password.
*/
@@ -940,149 +802,35 @@ public class LockPatternUtils {
}
/**
- * Save a lock password. Does not ensure that the password is as good
- * as the requested mode, but will adjust the mode to be as good as the
- * password.
- *
- * <p> This method will fail (returning {@code false}) if the previously
- * saved password provided is incorrect, or if the lockscreen verification
- * is still being throttled.
- *
- * @param password The password to save
- * @param savedPassword The previously saved lock password, or null if none
- * @param requestedQuality {@see DevicePolicyManager#getPasswordQuality(
- * android.content.ComponentName)}
- * @param userHandle The userId of the user to change the password for
- * @return whether this was successful or not.
- * @throws RuntimeException if password change encountered an unrecoverable error.
- * @deprecated Pass password as a byte array
- */
- @Deprecated
- public boolean saveLockPassword(String password, String savedPassword, int requestedQuality,
- int userHandle) {
- byte[] passwordBytes = password != null ? password.getBytes() : null;
- byte[] savedPasswordBytes = savedPassword != null ? savedPassword.getBytes() : null;
- return saveLockPassword(passwordBytes, savedPasswordBytes, requestedQuality, userHandle);
- }
-
- /**
- * Save a lock password. Does not ensure that the password is as good
- * as the requested mode, but will adjust the mode to be as good as the
- * password.
- *
- * <p> This method will fail (returning {@code false}) if the previously
- * saved password provided is incorrect, or if the lockscreen verification
- * is still being throttled.
- *
- * @param password The password to save
- * @param savedPassword The previously saved lock password, or null if none
- * @param requestedQuality {@see DevicePolicyManager#getPasswordQuality(
- * android.content.ComponentName)}
- * @param userHandle The userId of the user to change the password for
- * @return whether this was successful or not.
- * @throws RuntimeException if password change encountered an unrecoverable error.
- */
- public boolean saveLockPassword(byte[] password, byte[] savedPassword, int requestedQuality,
- int userHandle) {
- return saveLockPassword(password, savedPassword, requestedQuality,
- userHandle, false);
- }
-
- /**
- * Save a lock password. Does not ensure that the password is as good
- * as the requested mode, but will adjust the mode to be as good as the
- * password.
- *
- * <p> This method will fail (returning {@code false}) if the previously
- * saved password provided is incorrect, or if the lockscreen verification
- * is still being throttled.
- *
- * @param password The password to save
- * @param savedPassword The previously saved lock password, or null if none
- * @param requestedQuality {@see DevicePolicyManager#getPasswordQuality(
- * android.content.ComponentName)}
- * @param userHandle The userId of the user to change the password for
- * @param allowUntrustedChange whether we want to allow saving a new password if the existing
- * password being provided is incorrect.
- * @return whether this method saved the new password successfully or not. This flow will fail
- * and return false if the given credential is wrong and allowUntrustedChange is false.
- * @throws RuntimeException if password change encountered an unrecoverable error.
- */
- public boolean saveLockPassword(byte[] password, byte[] savedPassword,
- int requestedQuality, int userHandle, boolean allowUntrustedChange) {
- if (!hasSecureLockScreen()) {
- throw new UnsupportedOperationException(
- "This operation requires the lock screen feature.");
- }
- if (password == null || password.length < MIN_LOCK_PASSWORD_SIZE) {
- throw new IllegalArgumentException("password must not be null and at least "
- + "of length " + MIN_LOCK_PASSWORD_SIZE);
- }
-
- if (requestedQuality < PASSWORD_QUALITY_NUMERIC) {
- throw new IllegalArgumentException("quality must be at least NUMERIC, but was "
- + requestedQuality);
- }
-
- final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
- final int passwordQuality = PasswordMetrics.computeForPassword(password).quality;
- final int newKeyguardQuality =
- computeKeyguardQuality(CREDENTIAL_TYPE_PASSWORD, requestedQuality, passwordQuality);
- setKeyguardStoredPasswordQuality(newKeyguardQuality, userHandle);
- try {
- getLockSettings().setLockCredential(password, CREDENTIAL_TYPE_PASSWORD, savedPassword,
- requestedQuality, userHandle, allowUntrustedChange);
- } catch (RemoteException | RuntimeException e) {
- setKeyguardStoredPasswordQuality(currentQuality, userHandle);
- throw new RuntimeException("Unable to save lock password", e);
- }
-
- updateEncryptionPasswordIfNeeded(password, passwordQuality, userHandle);
- updatePasswordHistory(password, userHandle);
- onAfterChangingPassword(userHandle);
- return true;
- }
-
- /**
- * Compute keyguard credential quality to store in PASSWORD_TYPE_KEY by computing max between
- * them so that digit-only password is distinguished from PIN.
- *
- * TODO: remove this method and make CREDENTIAL_TYPE distinguish between PIN and password, so
- * that this quality is no longer needs to be persisted.
- */
- private int computeKeyguardQuality(
- @CredentialType int credentialType, int requestedQuality, int passwordQuality) {
- return credentialType == CREDENTIAL_TYPE_PASSWORD
- ? Math.max(passwordQuality, requestedQuality) : passwordQuality;
- }
-
- /**
* Update device encryption password if calling user is USER_SYSTEM and device supports
* encryption.
*/
- private void updateEncryptionPasswordIfNeeded(byte[] password, int quality, int userHandle) {
+ private void updateEncryptionPasswordIfNeeded(LockscreenCredential credential, int userHandle) {
// Update the device encryption password.
- if (userHandle == UserHandle.USER_SYSTEM
- && LockPatternUtils.isDeviceEncryptionEnabled()) {
- if (!shouldEncryptWithCredentials(true)) {
- clearEncryptionPassword();
- } else {
- boolean numeric = quality == PASSWORD_QUALITY_NUMERIC;
- boolean numericComplex = quality == PASSWORD_QUALITY_NUMERIC_COMPLEX;
- int type = numeric || numericComplex ? StorageManager.CRYPT_TYPE_PIN
- : StorageManager.CRYPT_TYPE_PASSWORD;
- updateEncryptionPassword(type, password);
- }
+ if (userHandle != UserHandle.USER_SYSTEM || !isDeviceEncryptionEnabled()) {
+ return;
+ }
+ if (!shouldEncryptWithCredentials(true)) {
+ updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
+ return;
+ }
+ if (credential.isNone()) {
+ // Set the encryption password to default.
+ setCredentialRequiredToDecrypt(false);
}
+ updateEncryptionPassword(credential.getStorageCryptType(), credential.getCredential());
}
/**
* Store the hash of the *current* password in the password history list, if device policy
* enforces password history requirement.
*/
- private void updatePasswordHistory(byte[] password, int userHandle) {
- if (password == null || password.length == 0) {
- Log.e(TAG, "checkPasswordHistory: empty password");
+ private void updatePasswordHistory(LockscreenCredential password, int userHandle) {
+ if (password.isNone()) {
+ return;
+ }
+ if (password.isPattern()) {
+ // Do not keep track of historical patterns
return;
}
// Add the password to the password history. We assume all
@@ -1096,10 +844,10 @@ public class LockPatternUtils {
passwordHistory = "";
} else {
final byte[] hashFactor = getPasswordHistoryHashFactor(password, userHandle);
- String hash = passwordToHistoryHash(password, hashFactor, userHandle);
+ String hash = passwordToHistoryHash(password.getCredential(), hashFactor, userHandle);
if (hash == null) {
Log.e(TAG, "Compute new style password hash failed, fallback to legacy style");
- hash = legacyPasswordToHash(password, userHandle);
+ hash = legacyPasswordToHash(password.getCredential(), userHandle);
}
if (TextUtils.isEmpty(passwordHistory)) {
passwordHistory = hash;
@@ -1143,52 +891,50 @@ public class LockPatternUtils {
}
/**
- * Retrieves the quality mode for {@param userHandle}.
- * {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
+ * Retrieves the quality mode for {@code userHandle}.
+ * @see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)
*
* @return stored password quality
+ * @deprecated use {@link #getCredentialTypeForUser(int)} instead
*/
@UnsupportedAppUsage
+ @Deprecated
public int getKeyguardStoredPasswordQuality(int userHandle) {
- return (int) getLong(PASSWORD_TYPE_KEY, PASSWORD_QUALITY_UNSPECIFIED, userHandle);
- }
-
- private void setKeyguardStoredPasswordQuality(int quality, int userHandle) {
- setLong(PASSWORD_TYPE_KEY, quality, userHandle);
+ return credentialTypeToPasswordQuality(getCredentialTypeForUser(userHandle));
}
/**
- * Enables/disables the Separate Profile Challenge for this {@param userHandle}. This is a no-op
+ * Enables/disables the Separate Profile Challenge for this {@code userHandle}. This is a no-op
* for user handles that do not belong to a managed profile.
*
* @param userHandle Managed profile user id
* @param enabled True if separate challenge is enabled
- * @param managedUserPassword Managed profile previous password. Null when {@param enabled} is
+ * @param profilePassword Managed profile previous password. Null when {@code enabled} is
* true
*/
public void setSeparateProfileChallengeEnabled(int userHandle, boolean enabled,
- byte[] managedUserPassword) {
+ LockscreenCredential profilePassword) {
if (!isManagedProfile(userHandle)) {
return;
}
try {
getLockSettings().setSeparateProfileChallengeEnabled(userHandle, enabled,
- managedUserPassword);
- onAfterChangingPassword(userHandle);
+ profilePassword);
+ reportEnabledTrustAgentsChanged(userHandle);
} catch (RemoteException e) {
Log.e(TAG, "Couldn't update work profile challenge enabled");
}
}
/**
- * Returns true if {@param userHandle} is a managed profile with separate challenge.
+ * Returns true if {@code userHandle} is a managed profile with separate challenge.
*/
public boolean isSeparateProfileChallengeEnabled(int userHandle) {
return isManagedProfile(userHandle) && hasSeparateChallenge(userHandle);
}
/**
- * Returns true if {@param userHandle} is a managed profile with unified challenge.
+ * Returns true if {@code userHandle} is a managed profile with unified challenge.
*/
public boolean isManagedProfileWithUnifiedChallenge(int userHandle) {
return isManagedProfile(userHandle) && !hasSeparateChallenge(userHandle);
@@ -1229,20 +975,6 @@ public class LockPatternUtils {
/**
* Deserialize a pattern.
- * @param string The pattern serialized with {@link #patternToString}
- * @return The pattern.
- * @deprecated Pass patterns as byte[] and use byteArrayToPattern
- */
- @Deprecated
- public static List<LockPatternView.Cell> stringToPattern(String string) {
- if (string == null) {
- return null;
- }
- return byteArrayToPattern(string.getBytes());
- }
-
- /**
- * Deserialize a pattern.
* @param bytes The pattern serialized with {@link #patternToByteArray}
* @return The pattern.
*/
@@ -1263,19 +995,6 @@ public class LockPatternUtils {
/**
* Serialize a pattern.
* @param pattern The pattern.
- * @return The pattern in string form.
- * @deprecated Use patternToByteArray instead.
- */
- @UnsupportedAppUsage
- @Deprecated
- public static String patternToString(List<LockPatternView.Cell> pattern) {
- return new String(patternToByteArray(pattern));
- }
-
-
- /**
- * Serialize a pattern.
- * @param pattern The pattern.
* @return The pattern in byte array form.
*/
public static byte[] patternToByteArray(List<LockPatternView.Cell> pattern) {
@@ -1292,34 +1011,6 @@ public class LockPatternUtils {
return res;
}
- /*
- * Generate an SHA-1 hash for the pattern. Not the most secure, but it is
- * at least a second level of protection. First level is that the file
- * is in a location only readable by the system process.
- * @param pattern the gesture pattern.
- * @return the hash of the pattern in a byte array.
- */
- @UnsupportedAppUsage
- public static byte[] patternToHash(List<LockPatternView.Cell> pattern) {
- if (pattern == null) {
- return null;
- }
-
- final int patternSize = pattern.size();
- byte[] res = new byte[patternSize];
- for (int i = 0; i < patternSize; i++) {
- LockPatternView.Cell cell = pattern.get(i);
- res[i] = (byte) (cell.getRow() * 3 + cell.getColumn());
- }
- try {
- MessageDigest md = MessageDigest.getInstance("SHA-1");
- byte[] hash = md.digest(res);
- return hash;
- } catch (NoSuchAlgorithmException nsa) {
- return res;
- }
- }
-
private String getSalt(int userId) {
long salt = getLong(LOCK_PASSWORD_SALT_KEY, 0, userId);
if (salt == 0) {
@@ -1343,6 +1034,7 @@ public class LockPatternUtils {
* @param password the gesture pattern.
*
* @return the hash of the pattern in a byte array.
+ * TODO: move to LockscreenCredential class
*/
public String legacyPasswordToHash(byte[] password, int userId) {
if (password == null || password.length == 0) {
@@ -1373,6 +1065,7 @@ public class LockPatternUtils {
/**
* Hash the password for password history check purpose.
+ * TODO: move to LockscreenCredential class
*/
private String passwordToHistoryHash(byte[] passwordToHash, byte[] hashFactor, int userId) {
if (passwordToHash == null || passwordToHash.length == 0 || hashFactor == null) {
@@ -1394,28 +1087,33 @@ public class LockPatternUtils {
}
/**
+ * Returns the credential type of the user, can be one of {@link #CREDENTIAL_TYPE_NONE},
+ * {@link #CREDENTIAL_TYPE_PATTERN}, {@link #CREDENTIAL_TYPE_PIN} and
+ * {@link #CREDENTIAL_TYPE_PASSWORD}
+ */
+ public @CredentialType int getCredentialTypeForUser(int userHandle) {
+ try {
+ return getLockSettings().getCredentialType(userHandle);
+ } catch (RemoteException re) {
+ Log.e(TAG, "failed to get credential type", re);
+ return CREDENTIAL_TYPE_NONE;
+ }
+ }
+
+ /**
* @param userId the user for which to report the value
* @return Whether the lock screen is secured.
*/
@UnsupportedAppUsage
public boolean isSecure(int userId) {
- int mode = getKeyguardStoredPasswordQuality(userId);
- return isLockPatternEnabled(mode, userId) || isLockPasswordEnabled(mode, userId);
+ int type = getCredentialTypeForUser(userId);
+ return type != CREDENTIAL_TYPE_NONE;
}
@UnsupportedAppUsage
public boolean isLockPasswordEnabled(int userId) {
- return isLockPasswordEnabled(getKeyguardStoredPasswordQuality(userId), userId);
- }
-
- private boolean isLockPasswordEnabled(int mode, int userId) {
- final boolean passwordEnabled = mode == PASSWORD_QUALITY_ALPHABETIC
- || mode == PASSWORD_QUALITY_NUMERIC
- || mode == PASSWORD_QUALITY_NUMERIC_COMPLEX
- || mode == PASSWORD_QUALITY_ALPHANUMERIC
- || mode == PASSWORD_QUALITY_COMPLEX
- || mode == PASSWORD_QUALITY_MANAGED;
- return passwordEnabled && savedPasswordExists(userId);
+ int type = getCredentialTypeForUser(userId);
+ return type == CREDENTIAL_TYPE_PASSWORD || type == CREDENTIAL_TYPE_PIN;
}
/**
@@ -1423,7 +1121,8 @@ public class LockPatternUtils {
*/
@UnsupportedAppUsage
public boolean isLockPatternEnabled(int userId) {
- return isLockPatternEnabled(getKeyguardStoredPasswordQuality(userId), userId);
+ int type = getCredentialTypeForUser(userId);
+ return type == CREDENTIAL_TYPE_PATTERN;
}
@Deprecated
@@ -1439,10 +1138,6 @@ public class LockPatternUtils {
setBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, true, userId);
}
- private boolean isLockPatternEnabled(int mode, int userId) {
- return mode == PASSWORD_QUALITY_SOMETHING && savedPatternExists(userId);
- }
-
/**
* @return Whether the visible pattern is enabled.
*/
@@ -1640,7 +1335,7 @@ public class LockPatternUtils {
}
/**
- * Disable trust until credentials have been entered for user {@param userId}.
+ * Disable trust until credentials have been entered for user {@code userId}.
*
* Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
*
@@ -1651,7 +1346,7 @@ public class LockPatternUtils {
}
/**
- * Requests strong authentication for user {@param userId}.
+ * Requests strong authentication for user {@code userId}.
*
* Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
*
@@ -1668,7 +1363,7 @@ public class LockPatternUtils {
}
}
- private void onAfterChangingPassword(int userHandle) {
+ private void reportEnabledTrustAgentsChanged(int userHandle) {
getTrustManager().reportEnabledTrustAgentsChanged(userHandle);
}
@@ -1834,54 +1529,25 @@ public class LockPatternUtils {
* <p>This method is only available to code running in the system server process itself.
*
* @param credential The new credential to be set
- * @param type Credential type: password / pattern / none.
- * @param requestedQuality the requested password quality by DevicePolicyManager.
- * See {@link DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
* @param tokenHandle Handle of the escrow token
* @param token Escrow token
- * @param userId The user who's lock credential to be changed
+ * @param userHandle The user who's lock credential to be changed
* @return {@code true} if the operation is successful.
*/
- public boolean setLockCredentialWithToken(byte[] credential, int type, int requestedQuality,
- long tokenHandle, byte[] token, int userId) {
+ public boolean setLockCredentialWithToken(@NonNull LockscreenCredential credential,
+ long tokenHandle, byte[] token, int userHandle) {
if (!hasSecureLockScreen()) {
throw new UnsupportedOperationException(
"This operation requires the lock screen feature.");
}
+ credential.checkLength();
LockSettingsInternal localService = getLockSettingsInternal();
- if (type != CREDENTIAL_TYPE_NONE) {
- if (credential == null || credential.length < MIN_LOCK_PASSWORD_SIZE) {
- throw new IllegalArgumentException("password must not be null and at least "
- + "of length " + MIN_LOCK_PASSWORD_SIZE);
- }
- final int quality = PasswordMetrics.computeForCredential(type, credential).quality;
- final int keyguardQuality = computeKeyguardQuality(type, quality, requestedQuality);
- if (!localService.setLockCredentialWithToken(credential, type, tokenHandle, token,
- keyguardQuality, userId)) {
- return false;
- }
- setKeyguardStoredPasswordQuality(quality, userId);
- updateEncryptionPasswordIfNeeded(credential, quality, userId);
- updatePasswordHistory(credential, userId);
- onAfterChangingPassword(userId);
- } else {
- if (!(credential == null || credential.length == 0)) {
- throw new IllegalArgumentException("password must be emtpy for NONE type");
- }
- if (!localService.setLockCredentialWithToken(null, CREDENTIAL_TYPE_NONE, tokenHandle,
- token, PASSWORD_QUALITY_UNSPECIFIED, userId)) {
- return false;
- }
- setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED, userId);
-
- if (userId == UserHandle.USER_SYSTEM) {
- // Set the encryption password to default.
- updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
- setCredentialRequiredToDecrypt(false);
- }
+ if (!localService.setLockCredentialWithToken(credential, tokenHandle, token, userHandle)) {
+ return false;
}
- onAfterChangingPassword(userId);
+
+ onPostPasswordChanged(credential, userHandle);
return true;
}
@@ -2008,7 +1674,7 @@ public class LockPatternUtils {
}
/**
- * @return true if unlocking with trust alone is allowed for {@param userId} by the current
+ * @return true if unlocking with trust alone is allowed for {@code userId} by the current
* strong authentication requirements.
*/
public boolean isTrustAllowedForUser(int userId) {
@@ -2016,7 +1682,7 @@ public class LockPatternUtils {
}
/**
- * @return true if unlocking with a biometric method alone is allowed for {@param userId}
+ * @return true if unlocking with a biometric method alone is allowed for {@code userId}
* by the current strong authentication requirements.
*/
public boolean isBiometricAllowedForUser(int userId) {
@@ -2024,7 +1690,7 @@ public class LockPatternUtils {
}
/**
- * Called when the strong authentication requirements for {@param userId} changed.
+ * Called when the strong authentication requirements for {@code userId} changed.
*/
public void onStrongAuthRequiredChanged(int userId) {
}
@@ -2079,7 +1745,8 @@ public class LockPatternUtils {
}
public boolean isSyntheticPasswordEnabled() {
- return getLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 0, UserHandle.USER_SYSTEM) != 0;
+ return getLong(SYNTHETIC_PASSWORD_ENABLED_KEY, SYNTHETIC_PASSWORD_ENABLED_BY_DEFAULT,
+ UserHandle.USER_SYSTEM) != 0;
}
/**
@@ -2113,22 +1780,4 @@ public class LockPatternUtils {
return FRP_CREDENTIAL_ENABLED && context.getResources().getBoolean(
com.android.internal.R.bool.config_enableCredentialFactoryResetProtection);
}
-
- /**
- * Converts a CharSequence to a byte array without requiring a toString(), which creates an
- * additional copy.
- *
- * @param chars The CharSequence to convert
- * @return A byte array representing the input
- */
- public static byte[] charSequenceToByteArray(CharSequence chars) {
- if (chars == null) {
- return null;
- }
- byte[] bytes = new byte[chars.length()];
- for (int i = 0; i < chars.length(); i++) {
- bytes[i] = (byte) chars.charAt(i);
- }
- return bytes;
- }
}
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 3f6c4d4f5634..74a0aa37dafb 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -1318,7 +1318,7 @@ public class LockPatternView extends View {
super.onRestoreInstanceState(ss.getSuperState());
setPattern(
DisplayMode.Correct,
- LockPatternUtils.stringToPattern(ss.getSerializedPattern()));
+ LockPatternUtils.byteArrayToPattern(ss.getSerializedPattern().getBytes()));
mPatternDisplayMode = DisplayMode.values()[ss.getDisplayMode()];
mInputEnabled = ss.isInputEnabled();
mInStealthMode = ss.isInStealthMode();
diff --git a/core/java/com/android/internal/widget/LockSettingsInternal.java b/core/java/com/android/internal/widget/LockSettingsInternal.java
index aab2f4b288b2..dd05576338ef 100644
--- a/core/java/com/android/internal/widget/LockSettingsInternal.java
+++ b/core/java/com/android/internal/widget/LockSettingsInternal.java
@@ -59,8 +59,8 @@ public abstract class LockSettingsInternal {
*
* @return true if password is set.
*/
- public abstract boolean setLockCredentialWithToken(byte[] credential, int type,
- long tokenHandle, byte[] token, int requestedQuality, int userId);
+ public abstract boolean setLockCredentialWithToken(LockscreenCredential credential,
+ long tokenHandle, byte[] token, int userId);
public abstract boolean unlockUserWithToken(long tokenHandle, byte[] token, int userId);
diff --git a/core/java/com/android/internal/widget/LockscreenCredential.aidl b/core/java/com/android/internal/widget/LockscreenCredential.aidl
new file mode 100644
index 000000000000..22501ff5a562
--- /dev/null
+++ b/core/java/com/android/internal/widget/LockscreenCredential.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+/**
+ * A class representing a lockscreen credential.
+ */
+parcelable LockscreenCredential;
+
diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java
new file mode 100644
index 000000000000..f456349a8937
--- /dev/null
+++ b/core/java/com/android/internal/widget/LockscreenCredential.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.storage.StorageManager;
+import android.text.TextUtils;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A class representing a lockscreen credential. It can be either an empty password, a pattern
+ * or a password (or PIN).
+ *
+ * <p> As required by some security certification, the framework tries its best to
+ * remove copies of the lockscreen credential bytes from memory. In this regard, this class
+ * abuses the {@link AutoCloseable} interface for sanitizing memory. This
+ * presents a nice syntax to auto-zeroize memory with the try-with-resource statement:
+ * <pre>
+ * try {LockscreenCredential credential = LockscreenCredential.createPassword(...) {
+ * // Process the credential in some way
+ * }
+ * </pre>
+ * With this construct, we can garantee that there will be no copies of the password left in
+ * memory when the credential goes out of scope. This should help mitigate certain class of
+ * attacks where the attcker gains read-only access to full device memory (cold boot attack,
+ * unsecured software/hardware memory dumping interfaces such as JTAG).
+ */
+public class LockscreenCredential implements Parcelable, AutoCloseable {
+
+ private final int mType;
+ // Stores raw credential bytes, or null if credential has been zeroized. An empty password
+ // is represented as a byte array of length 0.
+ private byte[] mCredential;
+
+ /**
+ * Private constructor, use static builder methods instead.
+ *
+ * <p> Builder methods should create a private copy of the credential bytes and pass in here.
+ * LockscreenCredential will only store the reference internally without copying. This is to
+ * minimize the number of extra copies introduced.
+ */
+ private LockscreenCredential(int type, byte[] credential) {
+ Preconditions.checkNotNull(credential);
+ if (type == CREDENTIAL_TYPE_NONE) {
+ Preconditions.checkArgument(credential.length == 0);
+ } else {
+ // Do not allow constructing a CREDENTIAL_TYPE_PASSWORD_OR_PIN object.
+ Preconditions.checkArgument(type == CREDENTIAL_TYPE_PIN
+ || type == CREDENTIAL_TYPE_PASSWORD
+ || type == CREDENTIAL_TYPE_PATTERN);
+ Preconditions.checkArgument(credential.length > 0);
+ }
+ mType = type;
+ mCredential = credential;
+ }
+
+ /**
+ * Creates a LockscreenCredential object representing empty password.
+ */
+ public static LockscreenCredential createNone() {
+ return new LockscreenCredential(CREDENTIAL_TYPE_NONE, new byte[0]);
+ }
+
+ /**
+ * Creates a LockscreenCredential object representing the given pattern.
+ */
+ public static LockscreenCredential createPattern(@NonNull List<LockPatternView.Cell> pattern) {
+ return new LockscreenCredential(CREDENTIAL_TYPE_PATTERN,
+ LockPatternUtils.patternToByteArray(pattern));
+ }
+
+ /**
+ * Creates a LockscreenCredential object representing the given alphabetic password.
+ */
+ public static LockscreenCredential createPassword(@NonNull CharSequence password) {
+ return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD,
+ charSequenceToByteArray(password));
+ }
+
+ /**
+ * Creates a LockscreenCredential object representing a managed password for profile with
+ * unified challenge. This credentiall will have type {@code CREDENTIAL_TYPE_PASSWORD} for now.
+ * TODO: consider add a new credential type for this. This can then supersede the
+ * isLockTiedToParent argument in various places in LSS.
+ */
+ public static LockscreenCredential createManagedPassword(@NonNull byte[] password) {
+ return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD,
+ Arrays.copyOf(password, password.length));
+ }
+
+ /**
+ * Creates a LockscreenCredential object representing the given numeric PIN.
+ */
+ public static LockscreenCredential createPin(@NonNull CharSequence pin) {
+ return new LockscreenCredential(CREDENTIAL_TYPE_PIN,
+ charSequenceToByteArray(pin));
+ }
+
+ /**
+ * Creates a LockscreenCredential object representing the given alphabetic password.
+ * If the supplied password is empty, create an empty credential object.
+ */
+ public static LockscreenCredential createPasswordOrNone(@Nullable CharSequence password) {
+ if (TextUtils.isEmpty(password)) {
+ return createNone();
+ } else {
+ return createPassword(password);
+ }
+ }
+
+ /**
+ * Creates a LockscreenCredential object representing the given numeric PIN.
+ * If the supplied password is empty, create an empty credential object.
+ */
+ public static LockscreenCredential createPinOrNone(@Nullable CharSequence pin) {
+ if (TextUtils.isEmpty(pin)) {
+ return createNone();
+ } else {
+ return createPin(pin);
+ }
+ }
+
+ private void ensureNotZeroized() {
+ Preconditions.checkState(mCredential != null, "Credential is already zeroized");
+ }
+ /**
+ * Returns the type of this credential. Can be one of {@link #CREDENTIAL_TYPE_NONE},
+ * {@link #CREDENTIAL_TYPE_PATTERN}, {@link #CREDENTIAL_TYPE_PIN} or
+ * {@link #CREDENTIAL_TYPE_PASSWORD}.
+ */
+ public int getType() {
+ ensureNotZeroized();
+ return mType;
+ }
+
+ /**
+ * Returns the credential bytes. This is a direct reference of the internal field so
+ * callers should not modify it.
+ *
+ */
+ public byte[] getCredential() {
+ ensureNotZeroized();
+ return mCredential;
+ }
+
+ /**
+ * Returns the credential type recognized by {@link StorageManager}. Can be one of
+ * {@link StorageManager#CRYPT_TYPE_DEFAULT}, {@link StorageManager#CRYPT_TYPE_PATTERN},
+ * {@link StorageManager#CRYPT_TYPE_PIN} or {@link StorageManager#CRYPT_TYPE_PASSWORD}.
+ */
+ public int getStorageCryptType() {
+ if (isNone()) {
+ return StorageManager.CRYPT_TYPE_DEFAULT;
+ }
+ if (isPattern()) {
+ return StorageManager.CRYPT_TYPE_PATTERN;
+ }
+ if (isPin()) {
+ return StorageManager.CRYPT_TYPE_PIN;
+ }
+ if (isPassword()) {
+ return StorageManager.CRYPT_TYPE_PASSWORD;
+ }
+ throw new IllegalStateException("Unhandled credential type");
+ }
+
+ /** Returns whether this is an empty credential */
+ public boolean isNone() {
+ ensureNotZeroized();
+ return mType == CREDENTIAL_TYPE_NONE;
+ }
+
+ /** Returns whether this is a pattern credential */
+ public boolean isPattern() {
+ ensureNotZeroized();
+ return mType == CREDENTIAL_TYPE_PATTERN;
+ }
+
+ /** Returns whether this is a numeric pin credential */
+ public boolean isPin() {
+ ensureNotZeroized();
+ return mType == CREDENTIAL_TYPE_PIN;
+ }
+
+ /** Returns whether this is an alphabetic password credential */
+ public boolean isPassword() {
+ ensureNotZeroized();
+ return mType == CREDENTIAL_TYPE_PASSWORD;
+ }
+
+ /** Returns the length of the credential */
+ public int size() {
+ ensureNotZeroized();
+ return mCredential.length;
+ }
+
+ /** Create a copy of the credential */
+ public LockscreenCredential duplicate() {
+ return new LockscreenCredential(mType,
+ mCredential != null ? Arrays.copyOf(mCredential, mCredential.length) : null);
+ }
+
+ /**
+ * Zeroize the credential bytes.
+ */
+ public void zeroize() {
+ if (mCredential != null) {
+ Arrays.fill(mCredential, (byte) 0);
+ mCredential = null;
+ }
+ }
+
+ /**
+ * Check if the credential meets minimal length requirement.
+ *
+ * @throws IllegalArgumentException if the credential is too short.
+ */
+ public void checkLength() {
+ if (isNone()) {
+ return;
+ }
+ if (isPattern()) {
+ if (size() < LockPatternUtils.MIN_LOCK_PATTERN_SIZE) {
+ throw new IllegalArgumentException("pattern must not be null and at least "
+ + LockPatternUtils.MIN_LOCK_PATTERN_SIZE + " dots long.");
+ }
+ return;
+ }
+ if (isPassword() || isPin()) {
+ if (size() < LockPatternUtils.MIN_LOCK_PASSWORD_SIZE) {
+ throw new IllegalArgumentException("password must not be null and at least "
+ + "of length " + LockPatternUtils.MIN_LOCK_PASSWORD_SIZE);
+ }
+ return;
+ }
+ }
+
+ /**
+ * Check if this credential's type matches one that's retrieved from disk. The nuance here is
+ * that the framework used to not distinguish between PIN and password, so this method will
+ * allow a PIN/Password LockscreenCredential to match against the legacy
+ * {@link #CREDENTIAL_TYPE_PASSWORD_OR_PIN} stored on disk.
+ */
+ public boolean checkAgainstStoredType(int storedCredentialType) {
+ if (storedCredentialType == CREDENTIAL_TYPE_PASSWORD_OR_PIN) {
+ return getType() == CREDENTIAL_TYPE_PASSWORD || getType() == CREDENTIAL_TYPE_PIN;
+ }
+ return getType() == storedCredentialType;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mType);
+ dest.writeByteArray(mCredential);
+ }
+
+ public static final Parcelable.Creator<LockscreenCredential> CREATOR =
+ new Parcelable.Creator<LockscreenCredential>() {
+
+ @Override
+ public LockscreenCredential createFromParcel(Parcel source) {
+ return new LockscreenCredential(source.readInt(), source.createByteArray());
+ }
+
+ @Override
+ public LockscreenCredential[] newArray(int size) {
+ return new LockscreenCredential[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void close() {
+ zeroize();
+ }
+
+ @Override
+ public int hashCode() {
+ // Effective Java — Item 9
+ return (17 + mType) * 31 + mCredential.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (!(o instanceof LockscreenCredential)) return false;
+ final LockscreenCredential other = (LockscreenCredential) o;
+ return mType == other.mType && Arrays.equals(mCredential, other.mCredential);
+ }
+
+ /**
+ * Converts a CharSequence to a byte array without requiring a toString(), which creates an
+ * additional copy.
+ *
+ * @param chars The CharSequence to convert
+ * @return A byte array representing the input
+ */
+ private static byte[] charSequenceToByteArray(CharSequence chars) {
+ if (chars == null) {
+ return new byte[0];
+ }
+ byte[] bytes = new byte[chars.length()];
+ for (int i = 0; i < chars.length(); i++) {
+ bytes[i] = (byte) chars.charAt(i);
+ }
+ return bytes;
+ }
+}
diff --git a/core/java/com/android/internal/widget/PasswordValidationError.java b/core/java/com/android/internal/widget/PasswordValidationError.java
new file mode 100644
index 000000000000..41b234ef024e
--- /dev/null
+++ b/core/java/com/android/internal/widget/PasswordValidationError.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+/**
+ * Password validation error containing an error code and optional requirement.
+ */
+public class PasswordValidationError {
+ // Password validation error codes
+ public static final int WEAK_CREDENTIAL_TYPE = 1;
+ public static final int CONTAINS_INVALID_CHARACTERS = 2;
+ public static final int TOO_SHORT = 3;
+ public static final int TOO_LONG = 4;
+ public static final int CONTAINS_SEQUENCE = 5;
+ public static final int NOT_ENOUGH_LETTERS = 6;
+ public static final int NOT_ENOUGH_UPPER_CASE = 7;
+ public static final int NOT_ENOUGH_LOWER_CASE = 8;
+ public static final int NOT_ENOUGH_DIGITS = 9;
+ public static final int NOT_ENOUGH_SYMBOLS = 10;
+ public static final int NOT_ENOUGH_NON_LETTER = 11;
+ public static final int NOT_ENOUGH_NON_DIGITS = 12;
+ public static final int RECENTLY_USED = 13;
+ // WARNING: if you add a new error, make sure it is presented to the user correctly in Settings.
+
+ public final int errorCode;
+ public final int requirement;
+
+ public PasswordValidationError(int errorCode) {
+ this(errorCode, 0);
+ }
+
+ public PasswordValidationError(int errorCode, int requirement) {
+ this.errorCode = errorCode;
+ this.requirement = requirement;
+ }
+
+ @Override
+ public String toString() {
+ return errorCodeToString(errorCode) + (requirement > 0 ? "; required: " + requirement : "");
+ }
+
+ /**
+ * Returns textual representation of the error for logging purposes.
+ */
+ private static String errorCodeToString(int error) {
+ switch (error) {
+ case WEAK_CREDENTIAL_TYPE: return "Weak credential type";
+ case CONTAINS_INVALID_CHARACTERS: return "Contains an invalid character";
+ case TOO_SHORT: return "Password too short";
+ case TOO_LONG: return "Password too long";
+ case CONTAINS_SEQUENCE: return "Sequence too long";
+ case NOT_ENOUGH_LETTERS: return "Too few letters";
+ case NOT_ENOUGH_UPPER_CASE: return "Too few upper case letters";
+ case NOT_ENOUGH_LOWER_CASE: return "Too few lower case letters";
+ case NOT_ENOUGH_DIGITS: return "Too few numeric characters";
+ case NOT_ENOUGH_SYMBOLS: return "Too few symbols";
+ case NOT_ENOUGH_NON_LETTER: return "Too few non-letter characters";
+ case NOT_ENOUGH_NON_DIGITS: return "Too few non-numeric characters";
+ case RECENTLY_USED: return "Pin or password was recently used";
+ default: return "Unknown error " + error;
+ }
+ }
+
+}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 7cd3e95c6499..b7523963ec19 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -26,6 +26,7 @@ import android.os.Build;
import android.os.Environment;
import android.os.Process;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.storage.StorageManager;
import android.permission.PermissionManager.SplitPermissionInfo;
import android.text.TextUtils;
@@ -33,8 +34,10 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.TimingsTraceLog;
import android.util.Xml;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
import libcore.io.IoUtils;
@@ -50,6 +53,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Loads global system configuration info.
@@ -166,6 +170,10 @@ public class SystemConfig {
// These are the permitted backup transport service components
final ArraySet<ComponentName> mBackupTransportWhitelist = new ArraySet<>();
+ // These are packages mapped to maps of component class name to default enabled state.
+ final ArrayMap<String, ArrayMap<String, Boolean>> mPackageComponentEnabledState =
+ new ArrayMap<>();
+
// Package names that are exempted from private API blacklisting
final ArraySet<String> mHiddenApiPackageWhitelist = new ArraySet<>();
@@ -209,6 +217,10 @@ public class SystemConfig {
private final ArraySet<String> mBugreportWhitelistedPackages = new ArraySet<>();
+ // Map of packagesNames to userTypes. Stored temporarily until cleared by UserManagerService().
+ private ArrayMap<String, Set<String>> mPackageToUserTypeWhitelist = new ArrayMap<>();
+ private ArrayMap<String, Set<String>> mPackageToUserTypeBlacklist = new ArrayMap<>();
+
public static SystemConfig getInstance() {
if (!isSystemProcess()) {
Slog.wtf(TAG, "SystemConfig is being accessed by a process other than "
@@ -295,6 +307,10 @@ public class SystemConfig {
return mBackupTransportWhitelist;
}
+ public ArrayMap<String, Boolean> getComponentsEnabledStates(String packageName) {
+ return mPackageComponentEnabledState.get(packageName);
+ }
+
public ArraySet<String> getDisabledUntilUsedPreinstalledCarrierApps() {
return mDisabledUntilUsedPreinstalledCarrierApps;
}
@@ -359,7 +375,54 @@ public class SystemConfig {
return mBugreportWhitelistedPackages;
}
+ /**
+ * Gets map of packagesNames to userTypes, dictating on which user types each package should be
+ * initially installed, and then removes this map from SystemConfig.
+ * Called by UserManagerService when it is constructed.
+ */
+ public ArrayMap<String, Set<String>> getAndClearPackageToUserTypeWhitelist() {
+ ArrayMap<String, Set<String>> r = mPackageToUserTypeWhitelist;
+ mPackageToUserTypeWhitelist = new ArrayMap<>(0);
+ return r;
+ }
+
+ /**
+ * Gets map of packagesNames to userTypes, dictating on which user types each package should NOT
+ * be initially installed, even if they are whitelisted, and then removes this map from
+ * SystemConfig.
+ * Called by UserManagerService when it is constructed.
+ */
+ public ArrayMap<String, Set<String>> getAndClearPackageToUserTypeBlacklist() {
+ ArrayMap<String, Set<String>> r = mPackageToUserTypeBlacklist;
+ mPackageToUserTypeBlacklist = new ArrayMap<>(0);
+ return r;
+ }
+
+ /**
+ * Only use for testing. Do NOT use in production code.
+ * @param readPermissions false to create an empty SystemConfig; true to read the permissions.
+ */
+ @VisibleForTesting
+ protected SystemConfig(boolean readPermissions) {
+ if (readPermissions) {
+ Slog.w(TAG, "Constructing a test SystemConfig");
+ readAllPermissions();
+ } else {
+ Slog.w(TAG, "Constructing an empty test SystemConfig");
+ }
+ }
+
SystemConfig() {
+ TimingsTraceLog log = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
+ log.traceBegin("readAllPermissions");
+ try {
+ readAllPermissions();
+ } finally {
+ log.traceEnd();
+ }
+ }
+
+ private void readAllPermissions() {
// Read configuration from system
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);
@@ -419,7 +482,8 @@ public class SystemConfig {
Environment.getSystemExtDirectory(), "etc", "permissions"), ALLOW_ALL);
}
- void readPermissions(File libraryDir, int permissionFlag) {
+ @VisibleForTesting
+ public void readPermissions(File libraryDir, int permissionFlag) {
// Read permissions from given directory.
if (!libraryDir.exists() || !libraryDir.isDirectory()) {
if (permissionFlag == ALLOW_ALL) {
@@ -798,6 +862,14 @@ public class SystemConfig {
}
XmlUtils.skipCurrentTag(parser);
} break;
+ case "component-override": {
+ if (allowAppConfigs) {
+ readComponentOverrides(parser, permFile);
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
case "backup-transport-whitelisted-service": {
if (allowFeatures) {
String serviceName = parser.getAttributeValue(null, "service");
@@ -954,6 +1026,11 @@ public class SystemConfig {
}
XmlUtils.skipCurrentTag(parser);
} break;
+ case "install-in-user-type": {
+ // NB: We allow any directory permission to declare install-in-user-type.
+ readInstallInUserType(parser,
+ mPackageToUserTypeWhitelist, mPackageToUserTypeBlacklist);
+ } break;
default: {
Slog.w(TAG, "Tag " + name + " is unknown in "
+ permFile + " at " + parser.getPositionDescription());
@@ -1091,6 +1168,53 @@ public class SystemConfig {
}
}
+ private void readInstallInUserType(XmlPullParser parser,
+ Map<String, Set<String>> doInstallMap,
+ Map<String, Set<String>> nonInstallMap)
+ throws IOException, XmlPullParserException {
+ final String packageName = parser.getAttributeValue(null, "package");
+ if (TextUtils.isEmpty(packageName)) {
+ Slog.w(TAG, "package is required for <install-in-user-type> in "
+ + parser.getPositionDescription());
+ return;
+ }
+
+ Set<String> userTypesYes = doInstallMap.get(packageName);
+ Set<String> userTypesNo = nonInstallMap.get(packageName);
+ final int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ final String name = parser.getName();
+ if ("install-in".equals(name)) {
+ final String userType = parser.getAttributeValue(null, "user-type");
+ if (TextUtils.isEmpty(userType)) {
+ Slog.w(TAG, "user-type is required for <install-in-user-type> in "
+ + parser.getPositionDescription());
+ continue;
+ }
+ if (userTypesYes == null) {
+ userTypesYes = new ArraySet<>();
+ doInstallMap.put(packageName, userTypesYes);
+ }
+ userTypesYes.add(userType);
+ } else if ("do-not-install-in".equals(name)) {
+ final String userType = parser.getAttributeValue(null, "user-type");
+ if (TextUtils.isEmpty(userType)) {
+ Slog.w(TAG, "user-type is required for <install-in-user-type> in "
+ + parser.getPositionDescription());
+ continue;
+ }
+ if (userTypesNo == null) {
+ userTypesNo = new ArraySet<>();
+ nonInstallMap.put(packageName, userTypesNo);
+ }
+ userTypesNo.add(userType);
+ } else {
+ Slog.w(TAG, "unrecognized tag in <install-in-user-type> in "
+ + parser.getPositionDescription());
+ }
+ }
+ }
+
void readOemPermissions(XmlPullParser parser) throws IOException, XmlPullParserException {
final String packageName = parser.getAttributeValue(null, "package");
if (TextUtils.isEmpty(packageName)) {
@@ -1169,6 +1293,54 @@ public class SystemConfig {
}
}
+ private void readComponentOverrides(XmlPullParser parser, File permFile)
+ throws IOException, XmlPullParserException {
+ String pkgname = parser.getAttributeValue(null, "package");
+ if (pkgname == null) {
+ Slog.w(TAG, "<component-override> without package in "
+ + permFile + " at " + parser.getPositionDescription());
+ return;
+ }
+
+ pkgname = pkgname.intern();
+
+ final int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ String name = parser.getName();
+ if ("component".equals(name)) {
+ String clsname = parser.getAttributeValue(null, "class");
+ String enabled = parser.getAttributeValue(null, "enabled");
+ if (clsname == null) {
+ Slog.w(TAG, "<component> without class in "
+ + permFile + " at " + parser.getPositionDescription());
+ return;
+ } else if (enabled == null) {
+ Slog.w(TAG, "<component> without enabled in "
+ + permFile + " at " + parser.getPositionDescription());
+ return;
+ }
+
+ if (clsname.startsWith(".")) {
+ clsname = pkgname + clsname;
+ }
+
+ clsname = clsname.intern();
+
+ ArrayMap<String, Boolean> componentEnabledStates =
+ mPackageComponentEnabledState.get(pkgname);
+ if (componentEnabledStates == null) {
+ componentEnabledStates = new ArrayMap<>();
+ mPackageComponentEnabledState.put(pkgname,
+ componentEnabledStates);
+ }
+
+ componentEnabledStates.put(clsname, !"false".equals(enabled));
+ } else {
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ }
+
private static boolean isSystemProcess() {
return Process.myUid() == Process.SYSTEM_UID;
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index e60f838a93eb..0852f826e9f2 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -54,6 +54,8 @@ cc_library_shared {
whole_static_libs: ["libandroid_graphics"],
+ export_static_lib_headers: ["libandroid_graphics"],
+
shared_libs: [
"libbase",
"libcutils",
@@ -108,7 +110,6 @@ cc_library_shared {
"android_view_InputEventReceiver.cpp",
"android_view_InputEventSender.cpp",
"android_view_InputQueue.cpp",
- "android_view_FrameMetricsObserver.cpp",
"android_view_KeyCharacterMap.cpp",
"android_view_KeyEvent.cpp",
"android_view_MotionEvent.cpp",
@@ -116,6 +117,7 @@ cc_library_shared {
"android_view_RenderNodeAnimator.cpp",
"android_view_Surface.cpp",
"android_view_SurfaceControl.cpp",
+ "android_graphics_BLASTBufferQueue.cpp",
"android_view_SurfaceSession.cpp",
"android_view_TextureView.cpp",
"android_view_VelocityTracker.cpp",
@@ -157,7 +159,6 @@ cc_library_shared {
"android_media_AudioVolumeGroups.cpp",
"android_media_AudioVolumeGroupCallback.cpp",
"android_media_DeviceCallback.cpp",
- "android_media_JetPlayer.cpp",
"android_media_MediaMetricsJNI.cpp",
"android_media_MicrophoneInfo.cpp",
"android_media_midi.cpp",
@@ -256,8 +257,6 @@ cc_library_shared {
"libnativeloader_lazy",
"libmemunreachable",
"libhidlbase",
- "libhidltransport",
- "libhwbinder",
"libvintf",
"libnativewindow",
"libdl",
@@ -337,8 +336,9 @@ cc_library_static {
cppflags: ["-Wno-conversion-null"],
srcs: [
- "android_util_SeempLog.cpp",
+ "android/graphics/apex/android_bitmap.cpp",
"android/graphics/apex/android_region.cpp",
+ "android/graphics/apex/android_paint.cpp",
"android_graphics_Canvas.cpp",
"android_graphics_ColorSpace.cpp",
@@ -349,6 +349,7 @@ cc_library_static {
"android_view_DisplayListCanvas.cpp",
"android_view_RenderNode.cpp",
"android_util_PathParser.cpp",
+ "android_util_SeempLog.cpp",
"android/graphics/AnimatedImageDrawable.cpp",
"android/graphics/Bitmap.cpp",
@@ -392,7 +393,6 @@ cc_library_static {
],
export_include_dirs: [
- ".",
"android/graphics/apex/include",
],
@@ -422,7 +422,9 @@ cc_library_static {
android: {
srcs: [ // sources that depend on android only libraries
"android/graphics/apex/android_canvas.cpp",
+ "android/graphics/apex/jni_runtime.cpp",
+ "android_view_FrameMetricsObserver.cpp",
"android_view_TextureLayer.cpp",
"android_view_ThreadedRenderer.cpp",
"android/graphics/BitmapRegionDecoder.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 173f40f2e8df..a3d56a6bf834 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -22,6 +22,7 @@
#include <android-base/macros.h>
#include <android-base/properties.h>
+#include <android/graphics/jni_runtime.h>
#include <binder/IBinder.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
@@ -33,8 +34,6 @@
#include <cutils/properties.h>
#include <server_configurable_flags/get_flags.h>
-#include <SkGraphics.h>
-
#include "jni.h"
#include <nativehelper/JNIHelp.h>
#include <nativehelper/JniInvocation.h>
@@ -56,24 +55,7 @@ using android::base::GetProperty;
extern int register_android_os_Binder(JNIEnv* env);
extern int register_android_os_Process(JNIEnv* env);
-extern int register_android_graphics_Bitmap(JNIEnv*);
-extern int register_android_graphics_BitmapFactory(JNIEnv*);
-extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*);
-extern int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env);
-extern int register_android_graphics_Camera(JNIEnv* env);
-extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env);
extern int register_android_graphics_GraphicBuffer(JNIEnv* env);
-extern int register_android_graphics_Graphics(JNIEnv* env);
-extern int register_android_graphics_ImageDecoder(JNIEnv*);
-extern int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv*);
-extern int register_android_graphics_Interpolator(JNIEnv* env);
-extern int register_android_graphics_MaskFilter(JNIEnv* env);
-extern int register_android_graphics_Movie(JNIEnv* env);
-extern int register_android_graphics_NinePatch(JNIEnv*);
-extern int register_android_graphics_PathEffect(JNIEnv* env);
-extern int register_android_graphics_Shader(JNIEnv* env);
-extern int register_android_graphics_Typeface(JNIEnv* env);
-extern int register_android_graphics_YuvImage(JNIEnv* env);
extern int register_com_google_android_gles_jni_EGLImpl(JNIEnv* env);
extern int register_com_google_android_gles_jni_GLImpl(JNIEnv* env);
@@ -113,7 +95,6 @@ extern int register_android_media_AudioProductStrategies(JNIEnv *env);
extern int register_android_media_AudioVolumeGroups(JNIEnv *env);
extern int register_android_media_AudioVolumeGroupChangeHandler(JNIEnv *env);
extern int register_android_media_MicrophoneInfo(JNIEnv *env);
-extern int register_android_media_JetPlayer(JNIEnv *env);
extern int register_android_media_ToneGenerator(JNIEnv *env);
extern int register_android_media_midi(JNIEnv *env);
@@ -131,46 +112,19 @@ extern int register_android_util_StatsLog(JNIEnv* env);
extern int register_android_util_StatsLogInternal(JNIEnv* env);
extern int register_android_util_Log(JNIEnv* env);
extern int register_android_util_MemoryIntArray(JNIEnv* env);
-extern int register_android_util_PathParser(JNIEnv* env);
extern int register_android_content_StringBlock(JNIEnv* env);
extern int register_android_content_XmlBlock(JNIEnv* env);
extern int register_android_content_res_ApkAssets(JNIEnv* env);
-extern int register_android_graphics_Canvas(JNIEnv* env);
-extern int register_android_graphics_CanvasProperty(JNIEnv* env);
-extern int register_android_graphics_ColorFilter(JNIEnv* env);
-extern int register_android_graphics_ColorSpace(JNIEnv* env);
-extern int register_android_graphics_DrawFilter(JNIEnv* env);
-extern int register_android_graphics_FontFamily(JNIEnv* env);
-extern int register_android_graphics_Matrix(JNIEnv* env);
-extern int register_android_graphics_Paint(JNIEnv* env);
-extern int register_android_graphics_Path(JNIEnv* env);
-extern int register_android_graphics_PathMeasure(JNIEnv* env);
-extern int register_android_graphics_Picture(JNIEnv*);
-extern int register_android_graphics_Region(JNIEnv* env);
-extern int register_android_graphics_SurfaceTexture(JNIEnv* env);
-extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env);
-extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env);
-extern int register_android_graphics_fonts_Font(JNIEnv* env);
-extern int register_android_graphics_fonts_FontFamily(JNIEnv* env);
-extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env);
-extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env);
-extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env);
-extern int register_android_graphics_text_MeasuredText(JNIEnv* env);
-extern int register_android_graphics_text_LineBreaker(JNIEnv *env);
+extern int register_android_graphics_BLASTBufferQueue(JNIEnv* env);
extern int register_android_view_DisplayEventReceiver(JNIEnv* env);
-extern int register_android_view_DisplayListCanvas(JNIEnv* env);
-extern int register_android_view_FrameMetricsObserver(JNIEnv* env);
extern int register_android_view_InputApplicationHandle(JNIEnv* env);
extern int register_android_view_InputWindowHandle(JNIEnv* env);
-extern int register_android_view_TextureLayer(JNIEnv* env);
-extern int register_android_view_RenderNode(JNIEnv* env);
extern int register_android_view_RenderNodeAnimator(JNIEnv* env);
extern int register_android_view_Surface(JNIEnv* env);
extern int register_android_view_SurfaceControl(JNIEnv* env);
extern int register_android_view_SurfaceSession(JNIEnv* env);
extern int register_android_view_CompositionSamplingListener(JNIEnv* env);
extern int register_android_view_TextureView(JNIEnv* env);
-extern int register_android_view_ThreadedRenderer(JNIEnv* env);
extern int register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper(JNIEnv *env);
extern int register_android_database_CursorWindow(JNIEnv* env);
extern int register_android_database_SQLiteConnection(JNIEnv* env);
@@ -316,7 +270,7 @@ AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength)
mArgBlockStart(argBlockStart),
mArgBlockLength(argBlockLength)
{
- SkGraphics::Init();
+ init_android_graphics();
// Pre-allocate enough space to hold a fair number of options.
mOptions.setCapacity(20);
@@ -698,26 +652,32 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p
// Read if we are using the profile configuration, do this at the start since the last ART args
// take precedence.
property_get("dalvik.vm.profilebootclasspath", propBuf, "");
- std::string profile_boot_class_path = propBuf;
+ std::string profile_boot_class_path_flag = propBuf;
// Empty means the property is unset and we should default to the phenotype property.
// The possible values are {"true", "false", ""}
- if (profile_boot_class_path.empty()) {
- profile_boot_class_path = server_configurable_flags::GetServerConfigurableFlag(
+ if (profile_boot_class_path_flag.empty()) {
+ profile_boot_class_path_flag = server_configurable_flags::GetServerConfigurableFlag(
RUNTIME_NATIVE_BOOT_NAMESPACE,
PROFILE_BOOT_CLASS_PATH,
/*default_value=*/ "");
}
- if (profile_boot_class_path == "true") {
+ const bool profile_boot_class_path = (profile_boot_class_path_flag == "true");
+ if (profile_boot_class_path) {
+ addOption("-Xcompiler-option");
+ addOption("--count-hotness-in-compiled-code");
addOption("-Xps-profile-boot-class-path");
addOption("-Xps-profile-aot-code");
addOption("-Xjitsaveprofilinginfo");
}
- std::string use_apex_image =
+ std::string use_apex_image_flag =
server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
ENABLE_APEX_IMAGE,
/*default_value=*/ "");
- if (use_apex_image == "true") {
+ // Use the APEX boot image for boot class path profiling to get JIT samples on BCP methods.
+ // Also use the APEX boot image if it's explicitly enabled via configuration flag.
+ const bool use_apex_image = profile_boot_class_path || (use_apex_image_flag == "true");
+ if (use_apex_image) {
addOption(kApexImageOption);
ALOGI("Using Apex boot image: '%s'\n", kApexImageOption);
} else if (parseRuntimeOption("dalvik.vm.boot-image", bootImageBuf, "-Ximage:")) {
@@ -1165,9 +1125,15 @@ void AndroidRuntime::start(const char* className, const Vector<String8>& options
setenv("ANDROID_ROOT", rootDir, 1);
}
- const char* runtimeRootDir = getenv("ANDROID_RUNTIME_ROOT");
- if (runtimeRootDir == NULL) {
- LOG_FATAL("No runtime directory specified with ANDROID_RUNTIME_ROOT environment variable.");
+ const char* artRootDir = getenv("ANDROID_ART_ROOT");
+ if (artRootDir == NULL) {
+ LOG_FATAL("No ART directory specified with ANDROID_ART_ROOT environment variable.");
+ return;
+ }
+
+ const char* i18nRootDir = getenv("ANDROID_I18N_ROOT");
+ if (i18nRootDir == NULL) {
+ LOG_FATAL("No runtime directory specified with ANDROID_I18N_ROOT environment variable.");
return;
}
@@ -1448,7 +1414,6 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_util_EventLog),
REG_JNI(register_android_util_Log),
REG_JNI(register_android_util_MemoryIntArray),
- REG_JNI(register_android_util_PathParser),
REG_JNI(register_android_util_StatsLog),
REG_JNI(register_android_util_StatsLogInternal),
REG_JNI(register_android_app_admin_SecurityLog),
@@ -1472,20 +1437,10 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_os_NativeHandle),
REG_JNI(register_android_os_VintfObject),
REG_JNI(register_android_os_VintfRuntimeInfo),
- REG_JNI(register_android_graphics_Canvas),
- // This needs to be before register_android_graphics_Graphics, or the latter
- // will not be able to find the jmethodID for ColorSpace.get().
- REG_JNI(register_android_graphics_ColorSpace),
- REG_JNI(register_android_graphics_Graphics),
REG_JNI(register_android_view_DisplayEventReceiver),
- REG_JNI(register_android_view_RenderNode),
REG_JNI(register_android_view_RenderNodeAnimator),
- REG_JNI(register_android_view_DisplayListCanvas),
- REG_JNI(register_android_view_FrameMetricsObserver),
REG_JNI(register_android_view_InputApplicationHandle),
REG_JNI(register_android_view_InputWindowHandle),
- REG_JNI(register_android_view_TextureLayer),
- REG_JNI(register_android_view_ThreadedRenderer),
REG_JNI(register_android_view_Surface),
REG_JNI(register_android_view_SurfaceControl),
REG_JNI(register_android_view_SurfaceSession),
@@ -1506,45 +1461,9 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_opengl_jni_GLES31),
REG_JNI(register_android_opengl_jni_GLES31Ext),
REG_JNI(register_android_opengl_jni_GLES32),
-
- REG_JNI(register_android_graphics_Bitmap),
- REG_JNI(register_android_graphics_BitmapFactory),
- REG_JNI(register_android_graphics_BitmapRegionDecoder),
- REG_JNI(register_android_graphics_ByteBufferStreamAdaptor),
- REG_JNI(register_android_graphics_Camera),
- REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor),
- REG_JNI(register_android_graphics_CanvasProperty),
- REG_JNI(register_android_graphics_ColorFilter),
- REG_JNI(register_android_graphics_DrawFilter),
- REG_JNI(register_android_graphics_FontFamily),
+ REG_JNI(register_android_graphics_classes),
+ REG_JNI(register_android_graphics_BLASTBufferQueue),
REG_JNI(register_android_graphics_GraphicBuffer),
- REG_JNI(register_android_graphics_ImageDecoder),
- REG_JNI(register_android_graphics_drawable_AnimatedImageDrawable),
- REG_JNI(register_android_graphics_Interpolator),
- REG_JNI(register_android_graphics_MaskFilter),
- REG_JNI(register_android_graphics_Matrix),
- REG_JNI(register_android_graphics_Movie),
- REG_JNI(register_android_graphics_NinePatch),
- REG_JNI(register_android_graphics_Paint),
- REG_JNI(register_android_graphics_Path),
- REG_JNI(register_android_graphics_PathMeasure),
- REG_JNI(register_android_graphics_PathEffect),
- REG_JNI(register_android_graphics_Picture),
- REG_JNI(register_android_graphics_Region),
- REG_JNI(register_android_graphics_Shader),
- REG_JNI(register_android_graphics_SurfaceTexture),
- REG_JNI(register_android_graphics_Typeface),
- REG_JNI(register_android_graphics_YuvImage),
- REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable),
- REG_JNI(register_android_graphics_drawable_VectorDrawable),
- REG_JNI(register_android_graphics_fonts_Font),
- REG_JNI(register_android_graphics_fonts_FontFamily),
- REG_JNI(register_android_graphics_pdf_PdfDocument),
- REG_JNI(register_android_graphics_pdf_PdfEditor),
- REG_JNI(register_android_graphics_pdf_PdfRenderer),
- REG_JNI(register_android_graphics_text_MeasuredText),
- REG_JNI(register_android_graphics_text_LineBreaker),
-
REG_JNI(register_android_database_CursorWindow),
REG_JNI(register_android_database_SQLiteConnection),
REG_JNI(register_android_database_SQLiteGlobal),
@@ -1585,7 +1504,6 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_media_AudioProductStrategies),
REG_JNI(register_android_media_AudioVolumeGroups),
REG_JNI(register_android_media_AudioVolumeGroupChangeHandler),
- REG_JNI(register_android_media_JetPlayer),
REG_JNI(register_android_media_MicrophoneInfo),
REG_JNI(register_android_media_RemoteDisplay),
REG_JNI(register_android_media_ToneGenerator),
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 18a1b43d3f5f..0487e139d264 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -9,6 +9,7 @@
#include "SkColorSpace.h"
#include "GraphicsJNI.h"
#include "SkStream.h"
+#include "SkWebpEncoder.h"
#include "android_os_Parcel.h"
#include "android_util_Binder.h"
@@ -265,6 +266,20 @@ void imageInfo(JNIEnv* env, jobject bitmap, AndroidBitmapInfo* info) {
info->format = ANDROID_BITMAP_FORMAT_NONE;
break;
}
+ switch (imageInfo.alphaType()) {
+ case kUnknown_SkAlphaType:
+ LOG_ALWAYS_FATAL("Bitmap has no alpha type");
+ break;
+ case kOpaque_SkAlphaType:
+ info->flags |= ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE;
+ break;
+ case kPremul_SkAlphaType:
+ info->flags |= ANDROID_BITMAP_FLAGS_ALPHA_PREMUL;
+ break;
+ case kUnpremul_SkAlphaType:
+ info->flags |= ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL;
+ break;
+ }
}
void* lockPixels(JNIEnv* env, jobject bitmap) {
@@ -512,27 +527,14 @@ static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle,
enum JavaEncodeFormat {
kJPEG_JavaEncodeFormat = 0,
kPNG_JavaEncodeFormat = 1,
- kWEBP_JavaEncodeFormat = 2
+ kWEBP_JavaEncodeFormat = 2,
+ kWEBP_LOSSY_JavaEncodeFormat = 3,
+ kWEBP_LOSSLESS_JavaEncodeFormat = 4,
};
static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle,
jint format, jint quality,
jobject jstream, jbyteArray jstorage) {
- SkEncodedImageFormat fm;
- switch (format) {
- case kJPEG_JavaEncodeFormat:
- fm = SkEncodedImageFormat::kJPEG;
- break;
- case kPNG_JavaEncodeFormat:
- fm = SkEncodedImageFormat::kPNG;
- break;
- case kWEBP_JavaEncodeFormat:
- fm = SkEncodedImageFormat::kWEBP;
- break;
- default:
- return JNI_FALSE;
- }
-
LocalScopedBitmap bitmap(bitmapHandle);
if (!bitmap.valid()) {
return JNI_FALSE;
@@ -563,6 +565,30 @@ static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle,
}
skbitmap = p3;
}
+ SkEncodedImageFormat fm;
+ switch (format) {
+ case kJPEG_JavaEncodeFormat:
+ fm = SkEncodedImageFormat::kJPEG;
+ break;
+ case kPNG_JavaEncodeFormat:
+ fm = SkEncodedImageFormat::kPNG;
+ break;
+ case kWEBP_JavaEncodeFormat:
+ fm = SkEncodedImageFormat::kWEBP;
+ break;
+ case kWEBP_LOSSY_JavaEncodeFormat:
+ case kWEBP_LOSSLESS_JavaEncodeFormat: {
+ SkWebpEncoder::Options options;
+ options.fQuality = quality;
+ options.fCompression = format == kWEBP_LOSSY_JavaEncodeFormat ?
+ SkWebpEncoder::Compression::kLossy : SkWebpEncoder::Compression::kLossless;
+ return SkWebpEncoder::Encode(strm.get(), skbitmap.pixmap(), options) ?
+ JNI_TRUE : JNI_FALSE;
+ }
+ default:
+ return JNI_FALSE;
+ }
+
return SkEncodeImage(strm.get(), skbitmap, fm, quality) ? JNI_TRUE : JNI_FALSE;
}
diff --git a/core/jni/android/graphics/Bitmap.h b/core/jni/android/graphics/Bitmap.h
index 06e31a1518ca..59adbb207a0c 100644
--- a/core/jni/android/graphics/Bitmap.h
+++ b/core/jni/android/graphics/Bitmap.h
@@ -39,8 +39,6 @@ jobject createBitmap(JNIEnv* env, Bitmap* bitmap,
jobject ninePatchInsets = nullptr, int density = -1);
-void toSkBitmap(jlong bitmapHandle, SkBitmap* outBitmap);
-
Bitmap& toBitmap(JNIEnv* env, jobject bitmap);
Bitmap& toBitmap(jlong bitmapHandle);
diff --git a/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp b/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp
index 173818b13837..d443fd8cdf14 100644
--- a/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp
+++ b/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp
@@ -1,6 +1,7 @@
#include "ByteBufferStreamAdaptor.h"
#include "core_jni_helpers.h"
#include "Utils.h"
+#include <jni.h>
#include <SkStream.h>
@@ -9,6 +10,24 @@ using namespace android;
static jmethodID gByteBuffer_getMethodID;
static jmethodID gByteBuffer_setPositionMethodID;
+/**
+ * Helper method for accessing the JNI interface pointer.
+ *
+ * Image decoding (which this supports) is started on a thread that is already
+ * attached to the Java VM. But an AnimatedImageDrawable continues decoding on
+ * the AnimatedImageThread, which is not attached. This will attach if
+ * necessary.
+ */
+static JNIEnv* requireEnv(JavaVM* jvm) {
+ JNIEnv* env;
+ if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ if (jvm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
+ LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!");
+ }
+ }
+ return env;
+}
+
class ByteBufferStream : public SkStreamAsset {
private:
ByteBufferStream(JavaVM* jvm, jobject jbyteBuffer, size_t initialPosition, size_t length,
@@ -46,7 +65,7 @@ public:
}
~ByteBufferStream() override {
- auto* env = get_env_or_die(mJvm);
+ auto* env = requireEnv(mJvm);
env->DeleteGlobalRef(mByteBuffer);
env->DeleteGlobalRef(mStorage);
}
@@ -63,7 +82,7 @@ public:
return this->setPosition(mPosition + size) ? size : 0;
}
- auto* env = get_env_or_die(mJvm);
+ auto* env = requireEnv(mJvm);
size_t bytesRead = 0;
do {
const size_t requested = (size > kStorageSize) ? kStorageSize : size;
@@ -146,7 +165,7 @@ private:
// Range has already been checked by the caller.
bool setPosition(size_t newPosition) {
- auto* env = get_env_or_die(mJvm);
+ auto* env = requireEnv(mJvm);
env->CallObjectMethod(mByteBuffer, gByteBuffer_setPositionMethodID,
newPosition + mInitialPosition);
if (env->ExceptionCheck()) {
@@ -185,7 +204,7 @@ public:
}
~ByteArrayStream() override {
- auto* env = get_env_or_die(mJvm);
+ auto* env = requireEnv(mJvm);
env->DeleteGlobalRef(mByteArray);
}
@@ -197,7 +216,7 @@ public:
return 0;
}
- auto* env = get_env_or_die(mJvm);
+ auto* env = requireEnv(mJvm);
if (buffer) {
env->GetByteArrayRegion(mByteArray, mPosition + mOffset, size,
reinterpret_cast<jbyte*>(buffer));
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index ec91cbf854a6..4d907f6d4942 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -374,16 +374,17 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
if (scale || jsubset) {
int translateX = 0;
int translateY = 0;
+ SkImageInfo scaledInfo;
if (jsubset) {
SkIRect subset;
GraphicsJNI::jrect_to_irect(env, jsubset, &subset);
- translateX = -subset.fLeft;
- translateY = -subset.fTop;
- desiredWidth = subset.width();
- desiredHeight = subset.height();
+ translateX = -subset.fLeft;
+ translateY = -subset.fTop;
+ scaledInfo = bitmapInfo.makeWH(subset.width(), subset.height());
+ } else {
+ scaledInfo = bitmapInfo.makeWH(desiredWidth, desiredHeight);
}
- SkImageInfo scaledInfo = bitmapInfo.makeWH(desiredWidth, desiredHeight);
SkBitmap scaledBm;
if (!scaledBm.setInfo(scaledInfo)) {
doThrowIOE(env, "Failed scaled setInfo");
diff --git a/core/jni/android/graphics/apex/TypeCast.h b/core/jni/android/graphics/apex/TypeCast.h
new file mode 100644
index 000000000000..96721d007951
--- /dev/null
+++ b/core/jni/android/graphics/apex/TypeCast.h
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_GRAPHICS_TYPECAST_H
+#define ANDROID_GRAPHICS_TYPECAST_H
+
+struct ABitmap;
+struct ACanvas;
+struct APaint;
+
+namespace android {
+
+ class Bitmap;
+ class Canvas;
+ class Paint;
+
+ class TypeCast {
+ public:
+ static inline Bitmap& toBitmapRef(const ABitmap* bitmap) {
+ return const_cast<Bitmap&>(reinterpret_cast<const Bitmap&>(*bitmap));
+ }
+
+ static inline Bitmap* toBitmap(ABitmap* bitmap) {
+ return reinterpret_cast<Bitmap*>(bitmap);
+ }
+
+ static inline ABitmap* toABitmap(Bitmap* bitmap) {
+ return reinterpret_cast<ABitmap*>(bitmap);
+ }
+
+ static inline Canvas* toCanvas(ACanvas* canvas) {
+ return reinterpret_cast<Canvas*>(canvas);
+ }
+
+ static inline ACanvas* toACanvas(Canvas* canvas) {
+ return reinterpret_cast<ACanvas *>(canvas);
+ }
+
+ static inline const Paint& toPaintRef(const APaint* paint) {
+ return reinterpret_cast<const Paint&>(*paint);
+ }
+
+ static inline const Paint* toPaint(const APaint* paint) {
+ return reinterpret_cast<const Paint*>(paint);
+ }
+
+ static inline Paint* toPaint(APaint* paint) {
+ return reinterpret_cast<Paint*>(paint);
+ }
+
+ static inline APaint* toAPaint(Paint* paint) {
+ return reinterpret_cast<APaint*>(paint);
+ }
+ };
+}; // namespace android
+
+#endif // ANDROID_GRAPHICS_TYPECAST_H
diff --git a/core/jni/android/graphics/apex/android_bitmap.cpp b/core/jni/android/graphics/apex/android_bitmap.cpp
new file mode 100644
index 000000000000..96cc5db8a5a4
--- /dev/null
+++ b/core/jni/android/graphics/apex/android_bitmap.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android/graphics/bitmap.h"
+#include "Bitmap.h"
+#include "TypeCast.h"
+
+#include <hwui/Bitmap.h>
+
+using namespace android;
+
+ABitmap* ABitmap_acquireBitmapFromJava(JNIEnv* env, jobject bitmapObj) {
+ Bitmap& bitmap = android::bitmap::toBitmap(env, bitmapObj);
+ bitmap.ref();
+ return TypeCast::toABitmap(&bitmap);
+}
+
+void ABitmap_acquireRef(ABitmap* bitmap) {
+ SkSafeRef(TypeCast::toBitmap(bitmap));
+}
+
+void ABitmap_releaseRef(ABitmap* bitmap) {
+ SkSafeUnref(TypeCast::toBitmap(bitmap));
+}
+
+static AndroidBitmapFormat getFormat(Bitmap* bitmap) {
+ switch (bitmap->colorType()) {
+ case kN32_SkColorType:
+ return ANDROID_BITMAP_FORMAT_RGBA_8888;
+ case kRGB_565_SkColorType:
+ return ANDROID_BITMAP_FORMAT_RGB_565;
+ case kARGB_4444_SkColorType:
+ return ANDROID_BITMAP_FORMAT_RGBA_4444;
+ case kAlpha_8_SkColorType:
+ return ANDROID_BITMAP_FORMAT_A_8;
+ case kRGBA_F16_SkColorType:
+ return ANDROID_BITMAP_FORMAT_RGBA_F16;
+ default:
+ return ANDROID_BITMAP_FORMAT_NONE;
+ }
+}
+
+static SkColorType getColorType(AndroidBitmapFormat format) {
+ switch (format) {
+ case ANDROID_BITMAP_FORMAT_RGBA_8888:
+ return kN32_SkColorType;
+ case ANDROID_BITMAP_FORMAT_RGB_565:
+ return kRGB_565_SkColorType;
+ case ANDROID_BITMAP_FORMAT_RGBA_4444:
+ return kARGB_4444_SkColorType;
+ case ANDROID_BITMAP_FORMAT_A_8:
+ return kAlpha_8_SkColorType;
+ case ANDROID_BITMAP_FORMAT_RGBA_F16:
+ return kRGBA_F16_SkColorType;
+ default:
+ return kUnknown_SkColorType;
+ }
+}
+
+ABitmap* ABitmap_copy(ABitmap* srcBitmapHandle, AndroidBitmapFormat dstFormat) {
+ SkColorType dstColorType = getColorType(dstFormat);
+ if (srcBitmapHandle && dstColorType != kUnknown_SkColorType) {
+ SkBitmap srcBitmap;
+ TypeCast::toBitmap(srcBitmapHandle)->getSkBitmap(&srcBitmap);
+
+ sk_sp<Bitmap> dstBitmap =
+ Bitmap::allocateHeapBitmap(srcBitmap.info().makeColorType(dstColorType));
+ if (dstBitmap && srcBitmap.readPixels(dstBitmap->info(), dstBitmap->pixels(),
+ dstBitmap->rowBytes(), 0, 0)) {
+ return TypeCast::toABitmap(dstBitmap.release());
+ }
+ }
+ return nullptr;
+}
+
+AndroidBitmapInfo ABitmap_getInfo(ABitmap* bitmapHandle) {
+ Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle);
+
+ AndroidBitmapInfo info;
+ info.width = bitmap->width();
+ info.height = bitmap->height();
+ info.stride = bitmap->rowBytes();
+ info.format = getFormat(bitmap);
+ return info;
+}
+
+void* ABitmap_getPixels(ABitmap* bitmapHandle) {
+ Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle);
+ if (bitmap->isHardware()) {
+ return nullptr;
+ }
+ return bitmap->pixels();
+}
diff --git a/core/jni/android/graphics/apex/android_canvas.cpp b/core/jni/android/graphics/apex/android_canvas.cpp
index 7a4495f4f259..527a745426e3 100644
--- a/core/jni/android/graphics/apex/android_canvas.cpp
+++ b/core/jni/android/graphics/apex/android_canvas.cpp
@@ -16,6 +16,7 @@
#include "android/graphics/canvas.h"
+#include "TypeCast.h"
#include "GraphicsJNI.h"
#include <hwui/Canvas.h>
@@ -25,14 +26,6 @@
using namespace android;
-static inline Canvas* toCanvas(ACanvas* aCanvas) {
- return reinterpret_cast<Canvas*>(aCanvas);
-}
-
-static inline ACanvas* toACanvas(Canvas* canvas) {
- return reinterpret_cast<ACanvas*>(canvas);
-}
-
bool ACanvas_isSupportedPixelFormat(int32_t bufferFormat) {
ANativeWindow_Buffer buffer { 0, 0, 0, bufferFormat, nullptr, {0} };
const SkColorType colorType = uirenderer::ANativeWindowToImageInfo(buffer, nullptr).colorType();
@@ -40,11 +33,11 @@ bool ACanvas_isSupportedPixelFormat(int32_t bufferFormat) {
}
ACanvas* ACanvas_getNativeHandleFromJava(JNIEnv* env, jobject canvasObj) {
- return toACanvas(GraphicsJNI::getNativeCanvas(env, canvasObj));
+ return TypeCast::toACanvas(GraphicsJNI::getNativeCanvas(env, canvasObj));
}
-void ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer,
- int32_t /*android_dataspace_t*/ dataspace) {
+static SkBitmap convert(const ANativeWindow_Buffer* buffer,
+ int32_t /*android_dataspace_t*/ dataspace) {
SkBitmap bitmap;
if (buffer != nullptr && buffer->width > 0 && buffer->height > 0) {
sk_sp<SkColorSpace> cs(uirenderer::DataSpaceToColorSpace((android_dataspace)dataspace));
@@ -53,18 +46,44 @@ void ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer,
bitmap.setInfo(imageInfo, rowBytes);
bitmap.setPixels(buffer->bits);
}
+ return bitmap;
+}
- toCanvas(canvas)->setBitmap(bitmap);
+ACanvas* ACanvas_createCanvas(const ANativeWindow_Buffer* buffer,
+ int32_t /*android_dataspace_t*/ dataspace) {
+ return TypeCast::toACanvas(Canvas::create_canvas(convert(buffer, dataspace)));
}
-void ACanvas_clipRect(ACanvas* canvas, const ARect& clipRect, bool /*doAA*/) {
+void ACanvas_destroyCanvas(ACanvas* canvas) {
+ delete TypeCast::toCanvas(canvas);
+}
+
+void ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer,
+ int32_t /*android_dataspace_t*/ dataspace) {
+
+
+ TypeCast::toCanvas(canvas)->setBitmap(convert(buffer, dataspace));
+}
+
+void ACanvas_clipRect(ACanvas* canvas, const ARect* clipRect, bool /*doAA*/) {
//TODO update Canvas to take antialias param
- toCanvas(canvas)->clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
- SkClipOp::kIntersect);
+ TypeCast::toCanvas(canvas)->clipRect(clipRect->left, clipRect->top, clipRect->right,
+ clipRect->bottom, SkClipOp::kIntersect);
}
-void ACanvas_clipOutRect(ACanvas* canvas, const ARect& clipRect, bool /*doAA*/) {
+void ACanvas_clipOutRect(ACanvas* canvas, const ARect* clipRect, bool /*doAA*/) {
//TODO update Canvas to take antialias param
- toCanvas(canvas)->clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
- SkClipOp::kDifference);
+ TypeCast::toCanvas(canvas)->clipRect(clipRect->left, clipRect->top, clipRect->right,
+ clipRect->bottom, SkClipOp::kDifference);
+}
+
+void ACanvas_drawRect(ACanvas* canvas, const ARect* rect, const APaint* paint) {
+ TypeCast::toCanvas(canvas)->drawRect(rect->left, rect->top, rect->right, rect->bottom,
+ TypeCast::toPaintRef(paint));
+}
+
+void ACanvas_drawBitmap(ACanvas* canvas, const ABitmap* bitmap, float left, float top,
+ const APaint* paint) {
+ TypeCast::toCanvas(canvas)->drawBitmap(TypeCast::toBitmapRef(bitmap), left, top,
+ TypeCast::toPaint(paint));
}
diff --git a/core/jni/android/graphics/apex/android_paint.cpp b/core/jni/android/graphics/apex/android_paint.cpp
new file mode 100644
index 000000000000..70bd085343ce
--- /dev/null
+++ b/core/jni/android/graphics/apex/android_paint.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android/graphics/paint.h"
+
+#include "TypeCast.h"
+
+#include <hwui/Paint.h>
+
+using namespace android;
+
+
+APaint* APaint_createPaint() {
+ return TypeCast::toAPaint(new Paint());
+}
+
+void APaint_destroyPaint(APaint* paint) {
+ delete TypeCast::toPaint(paint);
+}
+
+static SkBlendMode convertBlendMode(ABlendMode blendMode) {
+ switch (blendMode) {
+ case ABLEND_MODE_CLEAR:
+ return SkBlendMode::kClear;
+ case ABLEND_MODE_SRC_OVER:
+ return SkBlendMode::kSrcOver;
+ case ABLEND_MODE_SRC:
+ return SkBlendMode::kSrc;
+ }
+}
+
+void APaint_setBlendMode(APaint* paint, ABlendMode blendMode) {
+ TypeCast::toPaint(paint)->setBlendMode(convertBlendMode(blendMode));
+}
diff --git a/core/jni/android/graphics/apex/include/android/graphics/bitmap.h b/core/jni/android/graphics/apex/include/android/graphics/bitmap.h
new file mode 100644
index 000000000000..bfa4c8df407f
--- /dev/null
+++ b/core/jni/android/graphics/apex/include/android/graphics/bitmap.h
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+#ifndef ANDROID_GRAPHICS_BITMAP_H
+#define ANDROID_GRAPHICS_BITMAP_H
+
+#include <android/bitmap.h>
+#include <jni.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/**
+ * Opaque handle for a native graphics bitmap.
+ */
+typedef struct ABitmap ABitmap;
+
+ABitmap* ABitmap_acquireBitmapFromJava(JNIEnv* env, jobject bitmapObj);
+
+ABitmap* ABitmap_copy(ABitmap* srcBitmap, AndroidBitmapFormat dstFormat);
+
+void ABitmap_acquireRef(ABitmap* bitmap);
+void ABitmap_releaseRef(ABitmap* bitmap);
+
+AndroidBitmapInfo ABitmap_getInfo(ABitmap* bitmap);
+
+void* ABitmap_getPixels(ABitmap* bitmap);
+
+__END_DECLS
+
+#ifdef __cplusplus
+namespace android {
+namespace graphics {
+ class Bitmap {
+ public:
+ Bitmap() : mBitmap(nullptr) {}
+ Bitmap(JNIEnv* env, jobject bitmapObj) :
+ mBitmap(ABitmap_acquireBitmapFromJava(env, bitmapObj)) {}
+ Bitmap(const Bitmap& src) : mBitmap(src.mBitmap) { ABitmap_acquireRef(src.mBitmap); }
+ ~Bitmap() { ABitmap_releaseRef(mBitmap); }
+
+ // copy operator
+ Bitmap& operator=(const Bitmap& other) {
+ if (&other != this) {
+ ABitmap_releaseRef(mBitmap);
+ mBitmap = other.mBitmap;
+ ABitmap_acquireRef(mBitmap);
+ }
+ return *this;
+ }
+
+ // move operator
+ Bitmap& operator=(Bitmap&& other) {
+ if (&other != this) {
+ ABitmap_releaseRef(mBitmap);
+ mBitmap = other.mBitmap;
+ other.mBitmap = nullptr;
+ }
+ return *this;
+ }
+
+ Bitmap copy(AndroidBitmapFormat dstFormat) const {
+ return Bitmap(ABitmap_copy(mBitmap, dstFormat));
+ }
+
+ bool isValid() const { return mBitmap != nullptr; }
+ bool isEmpty() const {
+ AndroidBitmapInfo info = getInfo();
+ return info.width <= 0 || info.height <= 0;
+ }
+ void reset() {
+ ABitmap_releaseRef(mBitmap);
+ mBitmap = nullptr;
+ }
+
+ const ABitmap* get() const { return mBitmap; }
+
+ AndroidBitmapInfo getInfo() const { return ABitmap_getInfo(mBitmap); }
+ void* getPixels() const { return ABitmap_getPixels(mBitmap); }
+ private:
+ // takes ownership of the provided ABitmap
+ Bitmap(ABitmap* bitmap) : mBitmap(bitmap) {}
+
+ ABitmap* mBitmap;
+ };
+}; // namespace graphics
+}; // namespace android
+#endif // __cplusplus
+
+#endif // ANDROID_GRAPHICS_BITMAP_H \ No newline at end of file
diff --git a/core/jni/android/graphics/apex/include/android/graphics/canvas.h b/core/jni/android/graphics/apex/include/android/graphics/canvas.h
index c35a7d69b836..190aba4565f8 100644
--- a/core/jni/android/graphics/apex/include/android/graphics/canvas.h
+++ b/core/jni/android/graphics/apex/include/android/graphics/canvas.h
@@ -16,6 +16,8 @@
#ifndef ANDROID_GRAPHICS_CANVAS_H
#define ANDROID_GRAPHICS_CANVAS_H
+#include <android/graphics/bitmap.h>
+#include <android/graphics/paint.h>
#include <android/native_window.h>
#include <android/rect.h>
#include <jni.h>
@@ -23,8 +25,8 @@
__BEGIN_DECLS
/**
-* Opaque handle for a native graphics canvas.
-*/
+ * Opaque handle for a native graphics canvas.
+ */
typedef struct ACanvas ACanvas;
// One of AHardwareBuffer_Format.
@@ -33,34 +35,104 @@ bool ACanvas_isSupportedPixelFormat(int32_t bufferFormat);
/**
* Returns a native handle to a Java android.graphics.Canvas
*
- * @param env
- * @param canvas
* @return ACanvas* that is only valid for the life of the jobject.
*/
ACanvas* ACanvas_getNativeHandleFromJava(JNIEnv* env, jobject canvas);
/**
+ * Creates a canvas that wraps the buffer
+ *
+ * @param buffer required
+ */
+ACanvas* ACanvas_createCanvas(const ANativeWindow_Buffer* buffer,
+ int32_t /*android_dataspace_t*/ dataspace);
+
+void ACanvas_destroyCanvas(ACanvas* canvas);
+
+/**
* Updates the canvas to render into the pixels in the provided buffer
*
- * @param canvas
* @param buffer The buffer that will provide the backing store for this canvas. The buffer must
* remain valid until the this method is called again with either another active
* buffer or nullptr. If nullptr is given the canvas will release the previous buffer
* and set an empty backing store.
- * @param dataspace
*/
void ACanvas_setBuffer(ACanvas* canvas, const ANativeWindow_Buffer* buffer,
int32_t /*android_dataspace_t*/ dataspace);
/**
* Clips operations on the canvas to the intersection of the current clip and the provided clipRect.
+ *
+ * @param clipRect required
*/
-void ACanvas_clipRect(ACanvas* canvas, const ARect& clipRect, bool doAntiAlias = false);
+void ACanvas_clipRect(ACanvas* canvas, const ARect* clipRect, bool doAntiAlias = false);
/**
* Clips operations on the canvas to the difference of the current clip and the provided clipRect.
+ *
+ * @param clipRect required
+ */
+void ACanvas_clipOutRect(ACanvas* canvas, const ARect* clipRect, bool doAntiAlias = false);
+
+/**
+ *
+ * @param rect required
+ * @param paint required
+ */
+void ACanvas_drawRect(ACanvas* canvas, const ARect* rect, const APaint* paint);
+
+/**
+ *
+ * @param bitmap required
+ * @param left
+ * @param top
+ * @param paint
*/
-void ACanvas_clipOutRect(ACanvas* canvas, const ARect& clipRect, bool doAntiAlias = false);
+void ACanvas_drawBitmap(ACanvas* canvas, const ABitmap* bitmap, float left, float top,
+ const APaint* paint);
__END_DECLS
+
+#ifdef __cplusplus
+namespace android {
+namespace graphics {
+ class Canvas {
+ public:
+ Canvas(JNIEnv* env, jobject canvasObj) :
+ mCanvas(ACanvas_getNativeHandleFromJava(env, canvasObj)),
+ mOwnedPtr(false) {}
+ Canvas(const ANativeWindow_Buffer& buffer, int32_t /*android_dataspace_t*/ dataspace) :
+ mCanvas(ACanvas_createCanvas(&buffer, dataspace)),
+ mOwnedPtr(true) {}
+ ~Canvas() {
+ if (mOwnedPtr) {
+ ACanvas_destroyCanvas(mCanvas);
+ }
+ }
+
+ void setBuffer(const ANativeWindow_Buffer* buffer,
+ int32_t /*android_dataspace_t*/ dataspace) {
+ ACanvas_setBuffer(mCanvas, buffer, dataspace);
+ }
+
+ void clipRect(const ARect& clipRect, bool doAntiAlias = false) {
+ ACanvas_clipRect(mCanvas, &clipRect, doAntiAlias);
+ }
+
+ void drawRect(const ARect& rect, const Paint& paint) {
+ ACanvas_drawRect(mCanvas, &rect, &paint.get());
+ }
+ void drawBitmap(const Bitmap& bitmap, float left, float top, const Paint* paint) {
+ const APaint* aPaint = (paint) ? &paint->get() : nullptr;
+ ACanvas_drawBitmap(mCanvas, bitmap.get(), left, top, aPaint);
+ }
+
+ private:
+ ACanvas* mCanvas;
+ const bool mOwnedPtr;
+ };
+}; // namespace graphics
+}; // namespace android
+#endif // __cplusplus
+
#endif // ANDROID_GRAPHICS_CANVAS_H \ No newline at end of file
diff --git a/core/jni/android/graphics/apex/include/android/graphics/jni_runtime.h b/core/jni/android/graphics/apex/include/android/graphics/jni_runtime.h
new file mode 100644
index 000000000000..872a9497ab90
--- /dev/null
+++ b/core/jni/android/graphics/apex/include/android/graphics/jni_runtime.h
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+#ifndef ANDROID_GRAPHICS_JNI_RUNTIME_H
+#define ANDROID_GRAPHICS_JNI_RUNTIME_H
+
+#include <jni.h>
+
+__BEGIN_DECLS
+
+void init_android_graphics();
+
+int register_android_graphics_classes(JNIEnv* env);
+
+void zygote_preload_graphics();
+
+__END_DECLS
+
+
+#endif // ANDROID_GRAPHICS_JNI_RUNTIME_H \ No newline at end of file
diff --git a/core/jni/android/graphics/apex/include/android/graphics/paint.h b/core/jni/android/graphics/apex/include/android/graphics/paint.h
new file mode 100644
index 000000000000..5895e006bf93
--- /dev/null
+++ b/core/jni/android/graphics/apex/include/android/graphics/paint.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_GRAPHICS_PAINT_H
+#define ANDROID_GRAPHICS_PAINT_H
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/**
+ * Opaque handle for a native graphics canvas.
+ */
+typedef struct APaint APaint;
+
+/** Bitmap pixel format. */
+enum ABlendMode {
+ /** replaces destination with zero: fully transparent */
+ ABLEND_MODE_CLEAR = 0,
+ /** source over destination */
+ ABLEND_MODE_SRC_OVER = 1,
+ /** replaces destination **/
+ ABLEND_MODE_SRC = 2,
+};
+
+APaint* APaint_createPaint();
+
+void APaint_destroyPaint(APaint* paint);
+
+void APaint_setBlendMode(APaint* paint, ABlendMode blendMode);
+
+__END_DECLS
+
+#ifdef __cplusplus
+namespace android {
+namespace graphics {
+ class Paint {
+ public:
+ Paint() : mPaint(APaint_createPaint()) {}
+ ~Paint() { APaint_destroyPaint(mPaint); }
+
+ void setBlendMode(ABlendMode blendMode) { APaint_setBlendMode(mPaint, blendMode); }
+
+ const APaint& get() const { return *mPaint; }
+
+ private:
+ APaint* mPaint;
+ };
+}; // namespace graphics
+}; // namespace android
+#endif // __cplusplus
+
+
+#endif // ANDROID_GRAPHICS_PAINT_H \ No newline at end of file
diff --git a/core/jni/android/graphics/apex/jni_runtime.cpp b/core/jni/android/graphics/apex/jni_runtime.cpp
new file mode 100644
index 000000000000..7f9bac0df44a
--- /dev/null
+++ b/core/jni/android/graphics/apex/jni_runtime.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android/graphics/jni_runtime.h"
+
+#include <android/log.h>
+#include <nativehelper/JNIHelp.h>
+#include <sys/cdefs.h>
+
+#include <EGL/egl.h>
+#include <Properties.h>
+#include <SkGraphics.h>
+
+#define LOG_TAG "AndroidGraphicsJNI"
+
+extern int register_android_graphics_Bitmap(JNIEnv*);
+extern int register_android_graphics_BitmapFactory(JNIEnv*);
+extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*);
+extern int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env);
+extern int register_android_graphics_Camera(JNIEnv* env);
+extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env);
+extern int register_android_graphics_Graphics(JNIEnv* env);
+extern int register_android_graphics_ImageDecoder(JNIEnv*);
+extern int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv*);
+extern int register_android_graphics_Interpolator(JNIEnv* env);
+extern int register_android_graphics_MaskFilter(JNIEnv* env);
+extern int register_android_graphics_Movie(JNIEnv* env);
+extern int register_android_graphics_NinePatch(JNIEnv*);
+extern int register_android_graphics_PathEffect(JNIEnv* env);
+extern int register_android_graphics_Shader(JNIEnv* env);
+extern int register_android_graphics_Typeface(JNIEnv* env);
+extern int register_android_graphics_YuvImage(JNIEnv* env);
+
+namespace android {
+
+extern int register_android_graphics_Canvas(JNIEnv* env);
+extern int register_android_graphics_CanvasProperty(JNIEnv* env);
+extern int register_android_graphics_ColorFilter(JNIEnv* env);
+extern int register_android_graphics_ColorSpace(JNIEnv* env);
+extern int register_android_graphics_DrawFilter(JNIEnv* env);
+extern int register_android_graphics_FontFamily(JNIEnv* env);
+extern int register_android_graphics_Matrix(JNIEnv* env);
+extern int register_android_graphics_Paint(JNIEnv* env);
+extern int register_android_graphics_Path(JNIEnv* env);
+extern int register_android_graphics_PathMeasure(JNIEnv* env);
+extern int register_android_graphics_Picture(JNIEnv*);
+extern int register_android_graphics_Region(JNIEnv* env);
+extern int register_android_graphics_SurfaceTexture(JNIEnv* env);
+extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env);
+extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env);
+extern int register_android_graphics_fonts_Font(JNIEnv* env);
+extern int register_android_graphics_fonts_FontFamily(JNIEnv* env);
+extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env);
+extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env);
+extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env);
+extern int register_android_graphics_text_MeasuredText(JNIEnv* env);
+extern int register_android_graphics_text_LineBreaker(JNIEnv *env);
+
+extern int register_android_util_PathParser(JNIEnv* env);
+extern int register_android_view_DisplayListCanvas(JNIEnv* env);
+extern int register_android_view_FrameMetricsObserver(JNIEnv* env);
+extern int register_android_view_RenderNode(JNIEnv* env);
+extern int register_android_view_TextureLayer(JNIEnv* env);
+extern int register_android_view_ThreadedRenderer(JNIEnv* env);
+
+#ifdef NDEBUG
+ #define REG_JNI(name) { name }
+ struct RegJNIRec {
+ int (*mProc)(JNIEnv*);
+ };
+#else
+ #define REG_JNI(name) { name, #name }
+ struct RegJNIRec {
+ int (*mProc)(JNIEnv*);
+ const char* mName;
+ };
+#endif
+
+static const RegJNIRec gRegJNI[] = {
+ REG_JNI(register_android_graphics_Canvas),
+ // This needs to be before register_android_graphics_Graphics, or the latter
+ // will not be able to find the jmethodID for ColorSpace.get().
+ REG_JNI(register_android_graphics_ColorSpace),
+ REG_JNI(register_android_graphics_Graphics),
+ REG_JNI(register_android_graphics_Bitmap),
+ REG_JNI(register_android_graphics_BitmapFactory),
+ REG_JNI(register_android_graphics_BitmapRegionDecoder),
+ REG_JNI(register_android_graphics_ByteBufferStreamAdaptor),
+ REG_JNI(register_android_graphics_Camera),
+ REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor),
+ REG_JNI(register_android_graphics_CanvasProperty),
+ REG_JNI(register_android_graphics_ColorFilter),
+ REG_JNI(register_android_graphics_DrawFilter),
+ REG_JNI(register_android_graphics_FontFamily),
+ REG_JNI(register_android_graphics_ImageDecoder),
+ REG_JNI(register_android_graphics_drawable_AnimatedImageDrawable),
+ REG_JNI(register_android_graphics_Interpolator),
+ REG_JNI(register_android_graphics_MaskFilter),
+ REG_JNI(register_android_graphics_Matrix),
+ REG_JNI(register_android_graphics_Movie),
+ REG_JNI(register_android_graphics_NinePatch),
+ REG_JNI(register_android_graphics_Paint),
+ REG_JNI(register_android_graphics_Path),
+ REG_JNI(register_android_graphics_PathMeasure),
+ REG_JNI(register_android_graphics_PathEffect),
+ REG_JNI(register_android_graphics_Picture),
+ REG_JNI(register_android_graphics_Region),
+ REG_JNI(register_android_graphics_Shader),
+ REG_JNI(register_android_graphics_SurfaceTexture),
+ REG_JNI(register_android_graphics_Typeface),
+ REG_JNI(register_android_graphics_YuvImage),
+ REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable),
+ REG_JNI(register_android_graphics_drawable_VectorDrawable),
+ REG_JNI(register_android_graphics_fonts_Font),
+ REG_JNI(register_android_graphics_fonts_FontFamily),
+ REG_JNI(register_android_graphics_pdf_PdfDocument),
+ REG_JNI(register_android_graphics_pdf_PdfEditor),
+ REG_JNI(register_android_graphics_pdf_PdfRenderer),
+ REG_JNI(register_android_graphics_text_MeasuredText),
+ REG_JNI(register_android_graphics_text_LineBreaker),
+
+ REG_JNI(register_android_util_PathParser),
+ REG_JNI(register_android_view_RenderNode),
+ REG_JNI(register_android_view_DisplayListCanvas),
+ REG_JNI(register_android_view_FrameMetricsObserver),
+ REG_JNI(register_android_view_TextureLayer),
+ REG_JNI(register_android_view_ThreadedRenderer),
+};
+
+} // namespace android
+
+void init_android_graphics() {
+ SkGraphics::Init();
+}
+
+int register_android_graphics_classes(JNIEnv *env) {
+ for (size_t i = 0; i < NELEM(android::gRegJNI); i++) {
+ if (android::gRegJNI[i].mProc(env) < 0) {
+#ifndef NDEBUG
+ __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, "JNI Error!!! %s failed to load\n",
+ android::gRegJNI[i].mName);
+#endif
+ return -1;
+ }
+ }
+ return 0;
+}
+
+using android::uirenderer::Properties;
+using android::uirenderer::RenderPipelineType;
+
+void zygote_preload_graphics() {
+ if (Properties::peekRenderPipelineType() == RenderPipelineType::SkiaGL) {
+ eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ }
+} \ No newline at end of file
diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp
index 58c5871aba28..82601baee914 100644
--- a/core/jni/android/opengl/util.cpp
+++ b/core/jni/android/opengl/util.cpp
@@ -24,15 +24,13 @@
#include <assert.h>
#include <dlfcn.h>
+#include <android/graphics/bitmap.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <GLES3/gl3.h>
#include <ETC1/etc1.h>
-#include <SkBitmap.h>
-
#include "core_jni_helpers.h"
-#include "android/graphics/Bitmap.h"
#undef LOG_TAG
#define LOG_TAG "OpenGLUtil"
@@ -628,31 +626,27 @@ void util_multiplyMV(JNIEnv *env, jclass clazz,
// The internal format is no longer the same as pixel format, per Table 2 in
// https://www.khronos.org/registry/OpenGL-Refpages/es3.1/html/glTexImage2D.xhtml
-static int checkInternalFormat(SkColorType colorType, int internalformat,
- int type)
+static bool checkInternalFormat(int32_t bitmapFormat, int internalformat, int type)
{
- switch(colorType) {
- case kN32_SkColorType:
- return (type == GL_UNSIGNED_BYTE &&
- internalformat == GL_RGBA) ||
- (type == GL_UNSIGNED_BYTE &&
- internalformat == GL_SRGB8_ALPHA8) ? 0 : -1;
- case kAlpha_8_SkColorType:
- return (type == GL_UNSIGNED_BYTE &&
- internalformat == GL_ALPHA) ? 0 : -1;
- case kARGB_4444_SkColorType:
- return (type == GL_UNSIGNED_SHORT_4_4_4_4 &&
- internalformat == GL_RGBA) ? 0 : -1;
- case kRGB_565_SkColorType:
- return (type == GL_UNSIGNED_SHORT_5_6_5 &&
- internalformat == GL_RGB) ? 0 : -1;
- case kRGBA_F16_SkColorType:
- return (type == GL_HALF_FLOAT &&
- internalformat == GL_RGBA16F) ? 0 : -1;
+ if (internalformat == GL_PALETTE8_RGBA8_OES) {
+ return false;
+ }
+ switch(bitmapFormat) {
+ case ANDROID_BITMAP_FORMAT_RGBA_8888:
+ return (type == GL_UNSIGNED_BYTE && internalformat == GL_RGBA) ||
+ (type == GL_UNSIGNED_BYTE && internalformat == GL_SRGB8_ALPHA8);
+ case ANDROID_BITMAP_FORMAT_A_8:
+ return (type == GL_UNSIGNED_BYTE && internalformat == GL_ALPHA);
+ case ANDROID_BITMAP_FORMAT_RGBA_4444:
+ return (type == GL_UNSIGNED_SHORT_4_4_4_4 && internalformat == GL_RGBA);
+ case ANDROID_BITMAP_FORMAT_RGB_565:
+ return (type == GL_UNSIGNED_SHORT_5_6_5 && internalformat == GL_RGB);
+ case ANDROID_BITMAP_FORMAT_RGBA_F16:
+ return (type == GL_HALF_FLOAT && internalformat == GL_RGBA16F);
default:
break;
}
- return -1;
+ return false;
}
// The internal format is no longer the same as pixel format, per Table 2 in
@@ -670,107 +664,92 @@ static int getPixelFormatFromInternalFormat(uint32_t internalFormat) {
}
}
-static int getInternalFormat(SkColorType colorType)
-{
- switch(colorType) {
- case kAlpha_8_SkColorType:
+static int getInternalFormat(int32_t bitmapFormat) {
+ switch(bitmapFormat) {
+ case ANDROID_BITMAP_FORMAT_A_8:
return GL_ALPHA;
- case kARGB_4444_SkColorType:
+ case ANDROID_BITMAP_FORMAT_RGBA_4444:
return GL_RGBA;
- case kN32_SkColorType:
+ case ANDROID_BITMAP_FORMAT_RGBA_8888:
return GL_RGBA;
- case kRGB_565_SkColorType:
+ case ANDROID_BITMAP_FORMAT_RGB_565:
return GL_RGB;
- case kRGBA_F16_SkColorType:
+ case ANDROID_BITMAP_FORMAT_RGBA_F16:
return GL_RGBA16F;
default:
return -1;
}
}
-static int getType(SkColorType colorType)
-{
- switch(colorType) {
- case kAlpha_8_SkColorType:
+static int getType(int32_t bitmapFormat) {
+ switch(bitmapFormat) {
+ case ANDROID_BITMAP_FORMAT_A_8:
return GL_UNSIGNED_BYTE;
- case kARGB_4444_SkColorType:
+ case ANDROID_BITMAP_FORMAT_RGBA_4444:
return GL_UNSIGNED_SHORT_4_4_4_4;
- case kN32_SkColorType:
+ case ANDROID_BITMAP_FORMAT_RGBA_8888:
return GL_UNSIGNED_BYTE;
- case kRGB_565_SkColorType:
+ case ANDROID_BITMAP_FORMAT_RGB_565:
return GL_UNSIGNED_SHORT_5_6_5;
- case kRGBA_F16_SkColorType:
+ case ANDROID_BITMAP_FORMAT_RGBA_F16:
return GL_HALF_FLOAT;
default:
return -1;
}
}
-static jint util_getInternalFormat(JNIEnv *env, jclass clazz,
- jlong bitmapPtr)
+static jint util_getInternalFormat(JNIEnv *env, jclass clazz, jobject bitmapObj)
{
- SkBitmap nativeBitmap;
- bitmap::toSkBitmap(bitmapPtr, &nativeBitmap);
- return getInternalFormat(nativeBitmap.colorType());
+ graphics::Bitmap bitmap(env, bitmapObj);
+ return getInternalFormat(bitmap.getInfo().format);
}
-static jint util_getType(JNIEnv *env, jclass clazz,
- jlong bitmapPtr)
+static jint util_getType(JNIEnv *env, jclass clazz, jobject bitmapObj)
{
- SkBitmap nativeBitmap;
- bitmap::toSkBitmap(bitmapPtr, &nativeBitmap);
- return getType(nativeBitmap.colorType());
+ graphics::Bitmap bitmap(env, bitmapObj);
+ return getType(bitmap.getInfo().format);
}
-static jint util_texImage2D(JNIEnv *env, jclass clazz,
- jint target, jint level, jint internalformat,
- jlong bitmapPtr, jint type, jint border)
+static jint util_texImage2D(JNIEnv *env, jclass clazz, jint target, jint level,
+ jint internalformat, jobject bitmapObj, jint type, jint border)
{
- SkBitmap bitmap;
- bitmap::toSkBitmap(bitmapPtr, &bitmap);
- SkColorType colorType = bitmap.colorType();
+ graphics::Bitmap bitmap(env, bitmapObj);
+ AndroidBitmapInfo bitmapInfo = bitmap.getInfo();
+
if (internalformat < 0) {
- internalformat = getInternalFormat(colorType);
+ internalformat = getInternalFormat(bitmapInfo.format);
}
if (type < 0) {
- type = getType(colorType);
- }
- int err = checkInternalFormat(colorType, internalformat, type);
- if (err)
- return err;
- const int w = bitmap.width();
- const int h = bitmap.height();
- const void* p = bitmap.getPixels();
- if (internalformat == GL_PALETTE8_RGBA8_OES) {
- err = -1;
- } else {
- glTexImage2D(target, level, internalformat, w, h, border,
- getPixelFormatFromInternalFormat(internalformat), type, p);
+ type = getType(bitmapInfo.format);
}
- return err;
+
+ if (checkInternalFormat(bitmapInfo.format, internalformat, type)) {
+ glTexImage2D(target, level, internalformat, bitmapInfo.width, bitmapInfo.height, border,
+ getPixelFormatFromInternalFormat(internalformat), type, bitmap.getPixels());
+ return 0;
+ }
+ return -1;
}
-static jint util_texSubImage2D(JNIEnv *env, jclass clazz,
- jint target, jint level, jint xoffset, jint yoffset,
- jlong bitmapPtr, jint format, jint type)
+static jint util_texSubImage2D(JNIEnv *env, jclass clazz, jint target, jint level,
+ jint xoffset, jint yoffset, jobject bitmapObj, jint format, jint type)
{
- SkBitmap bitmap;
- bitmap::toSkBitmap(bitmapPtr, &bitmap);
- SkColorType colorType = bitmap.colorType();
- int internalFormat = getInternalFormat(colorType);
+ graphics::Bitmap bitmap(env, bitmapObj);
+ AndroidBitmapInfo bitmapInfo = bitmap.getInfo();
+
+ int internalFormat = getInternalFormat(bitmapInfo.format);
if (format < 0) {
format = getPixelFormatFromInternalFormat(internalFormat);
if (format == GL_PALETTE8_RGBA8_OES)
return -1; // glCompressedTexSubImage2D() not supported
}
- int err = checkInternalFormat(colorType, internalFormat, type);
- if (err)
- return err;
- const int w = bitmap.width();
- const int h = bitmap.height();
- const void* p = bitmap.getPixels();
- glTexSubImage2D(target, level, xoffset, yoffset, w, h, format, type, p);
- return 0;
+
+ if (checkInternalFormat(bitmapInfo.format, internalFormat, type)) {
+ glTexSubImage2D(target, level, xoffset, yoffset, bitmapInfo.width, bitmapInfo.height,
+ format, type, bitmap.getPixels());
+ return 0;
+ }
+ return -1;
}
/*
@@ -1036,10 +1015,10 @@ static const JNINativeMethod gVisibilityMethods[] = {
};
static const JNINativeMethod gUtilsMethods[] = {
- { "native_getInternalFormat", "(J)I", (void*) util_getInternalFormat },
- { "native_getType", "(J)I", (void*) util_getType },
- { "native_texImage2D", "(IIIJII)I", (void*)util_texImage2D },
- { "native_texSubImage2D", "(IIIIJII)I", (void*)util_texSubImage2D },
+ { "native_getInternalFormat", "(Landroid/graphics/Bitmap;)I", (void*) util_getInternalFormat },
+ { "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType },
+ { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D },
+ { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D },
};
static const JNINativeMethod gEtc1Methods[] = {
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index bd4862dfb08d..637025329e37 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -16,6 +16,7 @@
#define ATRACE_TAG ATRACE_TAG_RESOURCES
+#include "android-base/logging.h"
#include "android-base/macros.h"
#include "android-base/stringprintf.h"
#include "android-base/unique_fd.h"
@@ -32,7 +33,7 @@ using ::android::base::unique_fd;
namespace android {
static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboolean system,
- jboolean force_shared_lib, jboolean overlay) {
+ jboolean force_shared_lib, jboolean overlay, jboolean for_loader) {
ScopedUtfChars path(env, java_path);
if (path.c_str() == nullptr) {
return 0;
@@ -46,7 +47,7 @@ static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboole
} else if (force_shared_lib) {
apk_assets = ApkAssets::LoadAsSharedLibrary(path.c_str(), system);
} else {
- apk_assets = ApkAssets::Load(path.c_str(), system);
+ apk_assets = ApkAssets::Load(path.c_str(), system, for_loader);
}
if (apk_assets == nullptr) {
@@ -58,7 +59,8 @@ static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboole
}
static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descriptor,
- jstring friendly_name, jboolean system, jboolean force_shared_lib) {
+ jstring friendly_name, jboolean system, jboolean force_shared_lib,
+ jboolean for_loader) {
ScopedUtfChars friendly_name_utf8(env, friendly_name);
if (friendly_name_utf8.c_str() == nullptr) {
return 0;
@@ -80,7 +82,9 @@ static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descri
std::unique_ptr<const ApkAssets> apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd),
friendly_name_utf8.c_str(),
- system, force_shared_lib);
+ system, force_shared_lib,
+ for_loader);
+
if (apk_assets == nullptr) {
std::string error_msg = base::StringPrintf("Failed to load asset path %s from fd %d",
friendly_name_utf8.c_str(), dup_fd.get());
@@ -90,6 +94,60 @@ static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descri
return reinterpret_cast<jlong>(apk_assets.release());
}
+static jlong NativeLoadArsc(JNIEnv* env, jclass /*clazz*/, jstring java_path,
+ jboolean for_loader) {
+ ScopedUtfChars path(env, java_path);
+ if (path.c_str() == nullptr) {
+ return 0;
+ }
+
+ ATRACE_NAME(base::StringPrintf("LoadApkAssetsArsc(%s)", path.c_str()).c_str());
+
+ std::unique_ptr<const ApkAssets> apk_assets = ApkAssets::LoadArsc(path.c_str(), for_loader);
+
+ if (apk_assets == nullptr) {
+ std::string error_msg = base::StringPrintf("Failed to load asset path %s", path.c_str());
+ jniThrowException(env, "java/io/IOException", error_msg.c_str());
+ return 0;
+ }
+ return reinterpret_cast<jlong>(apk_assets.release());
+}
+
+static jlong NativeLoadArscFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descriptor,
+ jstring friendly_name, jboolean for_loader) {
+ ScopedUtfChars friendly_name_utf8(env, friendly_name);
+ if (friendly_name_utf8.c_str() == nullptr) {
+ return 0;
+ }
+
+ int fd = jniGetFDFromFileDescriptor(env, file_descriptor);
+ ATRACE_NAME(base::StringPrintf("LoadApkAssetsArscFd(%d)", fd).c_str());
+ if (fd < 0) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor");
+ return 0;
+ }
+
+ unique_fd dup_fd(::fcntl(fd, F_DUPFD_CLOEXEC, 0));
+ if (dup_fd < 0) {
+ jniThrowIOException(env, errno);
+ return 0;
+ }
+
+ std::unique_ptr<const ApkAssets> apk_assets =
+ ApkAssets::LoadArsc(std::move(dup_fd), friendly_name_utf8.c_str(), for_loader);
+ if (apk_assets == nullptr) {
+ std::string error_msg = base::StringPrintf("Failed to load asset path from fd %d", fd);
+ jniThrowException(env, "java/io/IOException", error_msg.c_str());
+ return 0;
+ }
+ return reinterpret_cast<jlong>(apk_assets.release());
+}
+
+static jlong NativeLoadEmpty(JNIEnv* env, jclass /*clazz*/, jboolean for_loader) {
+ std::unique_ptr<const ApkAssets> apk_assets = ApkAssets::LoadEmpty(for_loader);
+ return reinterpret_cast<jlong>(apk_assets.release());
+}
+
static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
delete reinterpret_cast<ApkAssets*>(ptr);
}
@@ -138,9 +196,13 @@ static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring fil
// JNI registration.
static const JNINativeMethod gApkAssetsMethods[] = {
- {"nativeLoad", "(Ljava/lang/String;ZZZ)J", (void*)NativeLoad},
- {"nativeLoadFromFd", "(Ljava/io/FileDescriptor;Ljava/lang/String;ZZ)J",
+ {"nativeLoad", "(Ljava/lang/String;ZZZZ)J", (void*)NativeLoad},
+ {"nativeLoadFromFd", "(Ljava/io/FileDescriptor;Ljava/lang/String;ZZZ)J",
(void*)NativeLoadFromFd},
+ {"nativeLoadArsc", "(Ljava/lang/String;Z)J", (void*)NativeLoadArsc},
+ {"nativeLoadArscFromFd", "(Ljava/io/FileDescriptor;Ljava/lang/String;Z)J",
+ (void*)NativeLoadArscFromFd},
+ {"nativeLoadEmpty", "(Z)J", (void*)NativeLoadEmpty},
{"nativeDestroy", "(J)V", (void*)NativeDestroy},
{"nativeGetAssetPath", "(J)Ljava/lang/String;", (void*)NativeGetAssetPath},
{"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock},
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
new file mode 100644
index 000000000000..185e58160adf
--- /dev/null
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BLASTBufferQueue"
+
+#include <nativehelper/JNIHelp.h>
+
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/android_view_Surface.h>
+#include <utils/Log.h>
+#include <utils/RefBase.h>
+
+#include <gui/BLASTBufferQueue.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+
+namespace android {
+
+static jlong nativeCreate(JNIEnv* env, jclass clazz, jlong surfaceControl, jlong width, jlong height) {
+ sp<BLASTBufferQueue> queue = new BLASTBufferQueue(
+ reinterpret_cast<SurfaceControl*>(surfaceControl), width, height);
+ queue->incStrong((void*)nativeCreate);
+ return reinterpret_cast<jlong>(queue.get());
+}
+
+static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
+ sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+ queue->decStrong((void*)nativeCreate);
+}
+
+static jobject nativeGetSurface(JNIEnv* env, jclass clazz, jlong ptr) {
+ sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+ return android_view_Surface_createFromIGraphicBufferProducer(env, queue->getIGraphicBufferProducer());
+}
+
+static void nativeSetNextTransaction(JNIEnv* env, jclass clazz, jlong ptr, jlong transactionPtr) {
+ sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionPtr);
+ queue->setNextTransaction(transaction);
+}
+
+static void nativeUpdate(JNIEnv*env, jclass clazz, jlong ptr, jlong surfaceControl, jlong width, jlong height) {
+ sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+ queue->update(reinterpret_cast<SurfaceControl*>(surfaceControl), width, height);
+}
+
+static const JNINativeMethod gMethods[] = {
+ /* name, signature, funcPtr */
+ { "nativeCreate", "(JJJ)J",
+ (void*)nativeCreate },
+ { "nativeGetSurface", "(J)Landroid/view/Surface;",
+ (void*)nativeGetSurface },
+ { "nativeDestroy", "(J)V",
+ (void*)nativeDestroy },
+ { "nativeSetNextTransaction", "(JJ)V",
+ (void*)nativeSetNextTransaction },
+ { "nativeUpdate", "(JJJJ)V",
+ (void*)nativeUpdate }
+};
+
+int register_android_graphics_BLASTBufferQueue(JNIEnv* env) {
+ int res = jniRegisterNativeMethods(env, "android/graphics/BLASTBufferQueue",
+ gMethods, NELEM(gMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
+
+ return 0;
+}
+
+} // namespace android
diff --git a/core/jni/android_graphics_GraphicBuffer.cpp b/core/jni/android_graphics_GraphicBuffer.cpp
index 43d22eb7df0e..b6d50898a057 100644
--- a/core/jni/android_graphics_GraphicBuffer.cpp
+++ b/core/jni/android_graphics_GraphicBuffer.cpp
@@ -178,9 +178,9 @@ static jboolean android_graphics_GraphicBuffer_lockCanvas(JNIEnv* env, jobject,
nativeBuffer.format = AHardwareBuffer_convertFromPixelFormat(buffer->getPixelFormat());
nativeBuffer.bits = bits;
- ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
- ACanvas_setBuffer(canvas, &nativeBuffer, ADATASPACE_UNKNOWN);
- ACanvas_clipRect(canvas, {rect.left, rect.top, rect.right, rect.bottom});
+ graphics::Canvas canvas(env, canvasObj);
+ canvas.setBuffer(&nativeBuffer, ADATASPACE_UNKNOWN);
+ canvas.clipRect({rect.left, rect.top, rect.right, rect.bottom});
if (dirtyRect) {
INVOKEV(dirtyRect, gRectClassInfo.set,
@@ -193,8 +193,8 @@ static jboolean android_graphics_GraphicBuffer_lockCanvas(JNIEnv* env, jobject,
static jboolean android_graphics_GraphicBuffer_unlockCanvasAndPost(JNIEnv* env, jobject,
jlong wrapperHandle, jobject canvasObj) {
// release the buffer from the canvas
- ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
- ACanvas_setBuffer(canvas, nullptr, ADATASPACE_UNKNOWN);
+ graphics::Canvas canvas(env, canvasObj);
+ canvas.setBuffer(nullptr, ADATASPACE_UNKNOWN);
GraphicBufferWrapper* wrapper =
reinterpret_cast<GraphicBufferWrapper*>(wrapperHandle);
diff --git a/core/jni/android_media_JetPlayer.cpp b/core/jni/android_media_JetPlayer.cpp
deleted file mode 100644
index da116bf7c96a..000000000000
--- a/core/jni/android_media_JetPlayer.cpp
+++ /dev/null
@@ -1,538 +0,0 @@
-/*
- * 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.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "JET_JNI"
-
-
-#include <stdio.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#include <jni.h>
-#include <nativehelper/JNIHelp.h>
-#include "core_jni_helpers.h"
-
-#include <utils/Log.h>
-#include <media/JetPlayer.h>
-
-
-using namespace android;
-
-// ----------------------------------------------------------------------------
-static const char* const kClassPathName = "android/media/JetPlayer";
-
-// ----------------------------------------------------------------------------
-struct fields_t {
- // these fields provide access from C++ to the...
- jclass jetClass; // JetPlayer java class global ref
- jmethodID postNativeEventInJava; // java method to post events to the Java thread from native
- jfieldID nativePlayerInJavaObj; // stores in Java the native JetPlayer object
-};
-
-static fields_t javaJetPlayerFields;
-
-
-// ----------------------------------------------------------------------------
-// ----------------------------------------------------------------------------
-
-/*
- * This function is called from JetPlayer instance's render thread
- */
-static void
-jetPlayerEventCallback(int what, int arg1=0, int arg2=0, void* javaTarget = NULL)
-{
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (env) {
- env->CallStaticVoidMethod(
- javaJetPlayerFields.jetClass, javaJetPlayerFields.postNativeEventInJava,
- javaTarget,
- what, arg1, arg2);
- if (env->ExceptionCheck()) {
- env->ExceptionDescribe();
- env->ExceptionClear();
- }
- } else {
- ALOGE("JET jetPlayerEventCallback(): No JNI env for JET event callback, can't post event.");
- return;
- }
-}
-
-
-// ----------------------------------------------------------------------------
-// ----------------------------------------------------------------------------
-
-static jboolean
-android_media_JetPlayer_setup(JNIEnv *env, jobject thiz, jobject weak_this,
- jint maxTracks, jint trackBufferSize)
-{
- //ALOGV("android_media_JetPlayer_setup(): entering.");
- JetPlayer* lpJet = new JetPlayer(env->NewGlobalRef(weak_this), maxTracks, trackBufferSize);
-
- EAS_RESULT result = lpJet->init();
-
- if (result==EAS_SUCCESS) {
- // save our newly created C++ JetPlayer in the "nativePlayerInJavaObj" field
- // of the Java object (in mNativePlayerInJavaObj)
- env->SetLongField(thiz, javaJetPlayerFields.nativePlayerInJavaObj, (jlong)lpJet);
- return JNI_TRUE;
- } else {
- ALOGE("android_media_JetPlayer_setup(): initialization failed with EAS error code %d", (int)result);
- delete lpJet;
- env->SetLongField(weak_this, javaJetPlayerFields.nativePlayerInJavaObj, 0);
- return JNI_FALSE;
- }
-}
-
-
-// ----------------------------------------------------------------------------
-static void
-android_media_JetPlayer_finalize(JNIEnv *env, jobject thiz)
-{
- ALOGV("android_media_JetPlayer_finalize(): entering.");
- JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
- thiz, javaJetPlayerFields.nativePlayerInJavaObj);
- if (lpJet != NULL) {
- lpJet->release();
- delete lpJet;
- }
-
- ALOGV("android_media_JetPlayer_finalize(): exiting.");
-}
-
-
-// ----------------------------------------------------------------------------
-static void
-android_media_JetPlayer_release(JNIEnv *env, jobject thiz)
-{
- android_media_JetPlayer_finalize(env, thiz);
- env->SetLongField(thiz, javaJetPlayerFields.nativePlayerInJavaObj, 0);
- ALOGV("android_media_JetPlayer_release() done");
-}
-
-
-// ----------------------------------------------------------------------------
-static jboolean
-android_media_JetPlayer_loadFromFile(JNIEnv *env, jobject thiz, jstring path)
-{
- JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
- thiz, javaJetPlayerFields.nativePlayerInJavaObj);
- if (lpJet == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Unable to retrieve JetPlayer pointer for openFile()");
- return JNI_FALSE;
- }
-
- // set up event callback function
- lpJet->setEventCallback(jetPlayerEventCallback);
-
- const char *pathStr = env->GetStringUTFChars(path, NULL);
- if (pathStr == NULL) { // Out of memory
- ALOGE("android_media_JetPlayer_openFile(): aborting, out of memory");
- return JNI_FALSE;
- }
-
- ALOGV("android_media_JetPlayer_openFile(): trying to open %s", pathStr );
- EAS_RESULT result = lpJet->loadFromFile(pathStr);
- env->ReleaseStringUTFChars(path, pathStr);
-
- if (result==EAS_SUCCESS) {
- //ALOGV("android_media_JetPlayer_openFile(): file successfully opened");
- return JNI_TRUE;
- } else {
- ALOGE("android_media_JetPlayer_openFile(): failed to open file with EAS error %d",
- (int)result);
- return JNI_FALSE;
- }
-}
-
-
-// ----------------------------------------------------------------------------
-static jboolean
-android_media_JetPlayer_loadFromFileD(JNIEnv *env, jobject thiz,
- jobject fileDescriptor, jlong offset, jlong length)
-{
- JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
- thiz, javaJetPlayerFields.nativePlayerInJavaObj);
- if (lpJet == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Unable to retrieve JetPlayer pointer for openFile()");
- return JNI_FALSE;
- }
-
- // set up event callback function
- lpJet->setEventCallback(jetPlayerEventCallback);
-
- ALOGV("android_media_JetPlayer_openFileDescr(): trying to load JET file through its fd" );
- EAS_RESULT result = lpJet->loadFromFD(jniGetFDFromFileDescriptor(env, fileDescriptor),
- (long long)offset, (long long)length); // cast params to types used by EAS_FILE
-
- if (result==EAS_SUCCESS) {
- ALOGV("android_media_JetPlayer_openFileDescr(): file successfully opened");
- return JNI_TRUE;
- } else {
- ALOGE("android_media_JetPlayer_openFileDescr(): failed to open file with EAS error %d",
- (int)result);
- return JNI_FALSE;
- }
-}
-
-
-// ----------------------------------------------------------------------------
-static jboolean
-android_media_JetPlayer_closeFile(JNIEnv *env, jobject thiz)
-{
- JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
- thiz, javaJetPlayerFields.nativePlayerInJavaObj);
- if (lpJet == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Unable to retrieve JetPlayer pointer for closeFile()");
- return JNI_FALSE;
- }
-
- if (lpJet->closeFile()==EAS_SUCCESS) {
- //ALOGV("android_media_JetPlayer_closeFile(): file successfully closed");
- return JNI_TRUE;
- } else {
- ALOGE("android_media_JetPlayer_closeFile(): failed to close file");
- return JNI_FALSE;
- }
-}
-
-
-// ----------------------------------------------------------------------------
-static jboolean
-android_media_JetPlayer_play(JNIEnv *env, jobject thiz)
-{
- JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
- thiz, javaJetPlayerFields.nativePlayerInJavaObj);
- if (lpJet == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Unable to retrieve JetPlayer pointer for play()");
- return JNI_FALSE;
- }
-
- EAS_RESULT result = lpJet->play();
- if (result==EAS_SUCCESS) {
- //ALOGV("android_media_JetPlayer_play(): play successful");
- return JNI_TRUE;
- } else {
- ALOGE("android_media_JetPlayer_play(): failed to play with EAS error code %ld",
- result);
- return JNI_FALSE;
- }
-}
-
-
-// ----------------------------------------------------------------------------
-static jboolean
-android_media_JetPlayer_pause(JNIEnv *env, jobject thiz)
-{
- JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
- thiz, javaJetPlayerFields.nativePlayerInJavaObj);
- if (lpJet == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Unable to retrieve JetPlayer pointer for pause()");
- return JNI_FALSE;
- }
-
- EAS_RESULT result = lpJet->pause();
- if (result==EAS_SUCCESS) {
- //ALOGV("android_media_JetPlayer_pause(): pause successful");
- return JNI_TRUE;
- } else {
- if (result==EAS_ERROR_QUEUE_IS_EMPTY) {
- ALOGV("android_media_JetPlayer_pause(): paused with an empty queue");
- return JNI_TRUE;
- } else
- ALOGE("android_media_JetPlayer_pause(): failed to pause with EAS error code %ld",
- result);
- return JNI_FALSE;
- }
-}
-
-
-// ----------------------------------------------------------------------------
-static jboolean
-android_media_JetPlayer_queueSegment(JNIEnv *env, jobject thiz,
- jint segmentNum, jint libNum, jint repeatCount, jint transpose, jint muteFlags,
- jbyte userID)
-{
- JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
- thiz, javaJetPlayerFields.nativePlayerInJavaObj);
- if (lpJet == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Unable to retrieve JetPlayer pointer for queueSegment()");
- return JNI_FALSE;
- }
-
- EAS_RESULT result
- = lpJet->queueSegment(segmentNum, libNum, repeatCount, transpose, muteFlags, userID);
- if (result==EAS_SUCCESS) {
- //ALOGV("android_media_JetPlayer_queueSegment(): segment successfully queued");
- return JNI_TRUE;
- } else {
- ALOGE("android_media_JetPlayer_queueSegment(): failed with EAS error code %ld",
- result);
- return JNI_FALSE;
- }
-}
-
-
-// ----------------------------------------------------------------------------
-static jboolean
-android_media_JetPlayer_queueSegmentMuteArray(JNIEnv *env, jobject thiz,
- jint segmentNum, jint libNum, jint repeatCount, jint transpose, jbooleanArray muteArray,
- jbyte userID)
-{
- JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
- thiz, javaJetPlayerFields.nativePlayerInJavaObj);
- if (lpJet == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Unable to retrieve JetPlayer pointer for queueSegmentMuteArray()");
- return JNI_FALSE;
- }
-
- EAS_RESULT result=EAS_FAILURE;
-
- jboolean *muteTracks = NULL;
- muteTracks = env->GetBooleanArrayElements(muteArray, NULL);
- if (muteTracks == NULL) {
- ALOGE("android_media_JetPlayer_queueSegment(): failed to read track mute mask.");
- return JNI_FALSE;
- }
-
- EAS_U32 muteMask=0;
- int maxTracks = lpJet->getMaxTracks();
- for (jint trackIndex=0; trackIndex<maxTracks; trackIndex++) {
- if (muteTracks[maxTracks-1-trackIndex]==JNI_TRUE)
- muteMask = (muteMask << 1) | 0x00000001;
- else
- muteMask = muteMask << 1;
- }
- //ALOGV("android_media_JetPlayer_queueSegmentMuteArray(): FINAL mute mask =0x%08lX", mask);
-
- result = lpJet->queueSegment(segmentNum, libNum, repeatCount, transpose, muteMask, userID);
-
- env->ReleaseBooleanArrayElements(muteArray, muteTracks, 0);
- if (result==EAS_SUCCESS) {
- //ALOGV("android_media_JetPlayer_queueSegmentMuteArray(): segment successfully queued");
- return JNI_TRUE;
- } else {
- ALOGE("android_media_JetPlayer_queueSegmentMuteArray(): failed with EAS error code %ld",
- result);
- return JNI_FALSE;
- }
-}
-
-
-// ----------------------------------------------------------------------------
-static jboolean
-android_media_JetPlayer_setMuteFlags(JNIEnv *env, jobject thiz,
- jint muteFlags /*unsigned?*/, jboolean bSync)
-{
- JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
- thiz, javaJetPlayerFields.nativePlayerInJavaObj);
- if (lpJet == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Unable to retrieve JetPlayer pointer for setMuteFlags()");
- return JNI_FALSE;
- }
-
- EAS_RESULT result;
- result = lpJet->setMuteFlags(muteFlags, bSync==JNI_TRUE ? true : false);
- if (result==EAS_SUCCESS) {
- //ALOGV("android_media_JetPlayer_setMuteFlags(): mute flags successfully updated");
- return JNI_TRUE;
- } else {
- ALOGE("android_media_JetPlayer_setMuteFlags(): failed with EAS error code %ld", result);
- return JNI_FALSE;
- }
-}
-
-
-// ----------------------------------------------------------------------------
-static jboolean
-android_media_JetPlayer_setMuteArray(JNIEnv *env, jobject thiz,
- jbooleanArray muteArray, jboolean bSync)
-{
- JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
- thiz, javaJetPlayerFields.nativePlayerInJavaObj);
- if (lpJet == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Unable to retrieve JetPlayer pointer for setMuteArray()");
- return JNI_FALSE;
- }
-
- EAS_RESULT result=EAS_FAILURE;
-
- jboolean *muteTracks = NULL;
- muteTracks = env->GetBooleanArrayElements(muteArray, NULL);
- if (muteTracks == NULL) {
- ALOGE("android_media_JetPlayer_setMuteArray(): failed to read track mute mask.");
- return JNI_FALSE;
- }
-
- EAS_U32 muteMask=0;
- int maxTracks = lpJet->getMaxTracks();
- for (jint trackIndex=0; trackIndex<maxTracks; trackIndex++) {
- if (muteTracks[maxTracks-1-trackIndex]==JNI_TRUE)
- muteMask = (muteMask << 1) | 0x00000001;
- else
- muteMask = muteMask << 1;
- }
- //ALOGV("android_media_JetPlayer_setMuteArray(): FINAL mute mask =0x%08lX", muteMask);
-
- result = lpJet->setMuteFlags(muteMask, bSync==JNI_TRUE ? true : false);
-
- env->ReleaseBooleanArrayElements(muteArray, muteTracks, 0);
- if (result==EAS_SUCCESS) {
- //ALOGV("android_media_JetPlayer_setMuteArray(): mute flags successfully updated");
- return JNI_TRUE;
- } else {
- ALOGE("android_media_JetPlayer_setMuteArray(): \
- failed to update mute flags with EAS error code %ld", result);
- return JNI_FALSE;
- }
-}
-
-
-// ----------------------------------------------------------------------------
-static jboolean
-android_media_JetPlayer_setMuteFlag(JNIEnv *env, jobject thiz,
- jint trackId, jboolean muteFlag, jboolean bSync)
-{
- JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
- thiz, javaJetPlayerFields.nativePlayerInJavaObj);
- if (lpJet == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Unable to retrieve JetPlayer pointer for setMuteFlag()");
- return JNI_FALSE;
- }
-
- EAS_RESULT result;
- result = lpJet->setMuteFlag(trackId,
- muteFlag==JNI_TRUE ? true : false, bSync==JNI_TRUE ? true : false);
- if (result==EAS_SUCCESS) {
- //ALOGV("android_media_JetPlayer_setMuteFlag(): mute flag successfully updated for track %d", trackId);
- return JNI_TRUE;
- } else {
- ALOGE("android_media_JetPlayer_setMuteFlag(): failed to update mute flag for track %d with EAS error code %ld",
- trackId, result);
- return JNI_FALSE;
- }
-}
-
-
-// ----------------------------------------------------------------------------
-static jboolean
-android_media_JetPlayer_triggerClip(JNIEnv *env, jobject thiz, jint clipId)
-{
- JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
- thiz, javaJetPlayerFields.nativePlayerInJavaObj);
- if (lpJet == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Unable to retrieve JetPlayer pointer for triggerClip()");
- return JNI_FALSE;
- }
-
- EAS_RESULT result;
- result = lpJet->triggerClip(clipId);
- if (result==EAS_SUCCESS) {
- //ALOGV("android_media_JetPlayer_triggerClip(): triggerClip successful for clip %d", clipId);
- return JNI_TRUE;
- } else {
- ALOGE("android_media_JetPlayer_triggerClip(): triggerClip for clip %d failed with EAS error code %ld",
- clipId, result);
- return JNI_FALSE;
- }
-}
-
-
-// ----------------------------------------------------------------------------
-static jboolean
-android_media_JetPlayer_clearQueue(JNIEnv *env, jobject thiz)
-{
- JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
- thiz, javaJetPlayerFields.nativePlayerInJavaObj);
- if (lpJet == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Unable to retrieve JetPlayer pointer for clearQueue()");
- return JNI_FALSE;
- }
-
- EAS_RESULT result = lpJet->clearQueue();
- if (result==EAS_SUCCESS) {
- //ALOGV("android_media_JetPlayer_clearQueue(): clearQueue successful");
- return JNI_TRUE;
- } else {
- ALOGE("android_media_JetPlayer_clearQueue(): clearQueue failed with EAS error code %ld",
- result);
- return JNI_FALSE;
- }
-}
-
-
-// ----------------------------------------------------------------------------
-// ----------------------------------------------------------------------------
-static const JNINativeMethod gMethods[] = {
- // name, signature, funcPtr
- {"native_setup", "(Ljava/lang/Object;II)Z", (void *)android_media_JetPlayer_setup},
- {"native_finalize", "()V", (void *)android_media_JetPlayer_finalize},
- {"native_release", "()V", (void *)android_media_JetPlayer_release},
- {"native_loadJetFromFile",
- "(Ljava/lang/String;)Z", (void *)android_media_JetPlayer_loadFromFile},
- {"native_loadJetFromFileD", "(Ljava/io/FileDescriptor;JJ)Z",
- (void *)android_media_JetPlayer_loadFromFileD},
- {"native_closeJetFile","()Z", (void *)android_media_JetPlayer_closeFile},
- {"native_playJet", "()Z", (void *)android_media_JetPlayer_play},
- {"native_pauseJet", "()Z", (void *)android_media_JetPlayer_pause},
- {"native_queueJetSegment",
- "(IIIIIB)Z", (void *)android_media_JetPlayer_queueSegment},
- {"native_queueJetSegmentMuteArray",
- "(IIII[ZB)Z", (void *)android_media_JetPlayer_queueSegmentMuteArray},
- {"native_setMuteFlags","(IZ)Z", (void *)android_media_JetPlayer_setMuteFlags},
- {"native_setMuteArray","([ZZ)Z", (void *)android_media_JetPlayer_setMuteArray},
- {"native_setMuteFlag", "(IZZ)Z", (void *)android_media_JetPlayer_setMuteFlag},
- {"native_triggerClip", "(I)Z", (void *)android_media_JetPlayer_triggerClip},
- {"native_clearQueue", "()Z", (void *)android_media_JetPlayer_clearQueue},
-};
-
-#define JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME "mNativePlayerInJavaObj"
-#define JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME "postEventFromNative"
-
-
-int register_android_media_JetPlayer(JNIEnv *env)
-{
- javaJetPlayerFields.jetClass = NULL;
- javaJetPlayerFields.postNativeEventInJava = NULL;
- javaJetPlayerFields.nativePlayerInJavaObj = NULL;
-
- // Get the JetPlayer java class
- jclass jetPlayerClass = FindClassOrDie(env, kClassPathName);
- javaJetPlayerFields.jetClass = MakeGlobalRefOrDie(env, jetPlayerClass);
-
- // Get the mNativePlayerInJavaObj variable field
- javaJetPlayerFields.nativePlayerInJavaObj = GetFieldIDOrDie(env,
- jetPlayerClass, JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME, "J");
-
- // Get the callback to post events from this native code to Java
- javaJetPlayerFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
- javaJetPlayerFields.jetClass, JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME,
- "(Ljava/lang/Object;III)V");
-
- return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
-}
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 9c52a6433360..d62d2d967d85 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -85,7 +85,8 @@ enum {
// Dalvik other extra sections.
HEAP_DALVIK_OTHER_LINEARALLOC,
HEAP_DALVIK_OTHER_ACCOUNTING,
- HEAP_DALVIK_OTHER_CODE_CACHE,
+ HEAP_DALVIK_OTHER_ZYGOTE_CODE_CACHE,
+ HEAP_DALVIK_OTHER_APP_CODE_CACHE,
HEAP_DALVIK_OTHER_COMPILER_METADATA,
HEAP_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE,
@@ -138,8 +139,8 @@ static stat_field_names stat_field_names[_NUM_CORE_HEAP] = {
"nativePrivateClean", "nativeSharedClean", "nativeSwappedOut", "nativeSwappedOutPss" }
};
-jfieldID otherStats_field;
-jfieldID hasSwappedOutPss_field;
+static jfieldID otherStats_field;
+static jfieldID hasSwappedOutPss_field;
struct stats_t {
int pss;
@@ -257,6 +258,8 @@ static void load_maps(int pid, stats_t* stats, bool* foundSwapPss)
which_heap = HEAP_NATIVE;
} else if (base::StartsWith(name, "[anon:libc_malloc]")) {
which_heap = HEAP_NATIVE;
+ } else if (base::StartsWith(name, "[anon:scudo:")) {
+ which_heap = HEAP_NATIVE;
} else if (base::StartsWith(name, "[stack")) {
which_heap = HEAP_STACK;
} else if (base::StartsWith(name, "[anon:stack_and_tls:")) {
@@ -280,9 +283,10 @@ static void load_maps(int pid, stats_t* stats, bool* foundSwapPss)
is_swappable = true;
} else if (base::EndsWith(name, ".vdex")) {
which_heap = HEAP_DEX;
- // Handle system@framework@boot and system/framework/boot
+ // Handle system@framework@boot and system/framework/boot|apex
if ((strstr(name.c_str(), "@boot") != nullptr) ||
- (strstr(name.c_str(), "/boot"))) {
+ (strstr(name.c_str(), "/boot") != nullptr) ||
+ (strstr(name.c_str(), "/apex") != nullptr)) {
sub_heap = HEAP_DEX_BOOT_VDEX;
} else {
sub_heap = HEAP_DEX_APP_VDEX;
@@ -293,9 +297,10 @@ static void load_maps(int pid, stats_t* stats, bool* foundSwapPss)
is_swappable = true;
} else if (base::EndsWith(name, ".art") || base::EndsWith(name, ".art]")) {
which_heap = HEAP_ART;
- // Handle system@framework@boot* and system/framework/boot*
+ // Handle system@framework@boot* and system/framework/boot|apex*
if ((strstr(name.c_str(), "@boot") != nullptr) ||
- (strstr(name.c_str(), "/boot"))) {
+ (strstr(name.c_str(), "/boot") != nullptr) ||
+ (strstr(name.c_str(), "/apex") != nullptr)) {
sub_heap = HEAP_ART_BOOT;
} else {
sub_heap = HEAP_ART_APP;
@@ -307,9 +312,18 @@ static void load_maps(int pid, stats_t* stats, bool* foundSwapPss)
which_heap = HEAP_GL_DEV;
} else if (base::StartsWith(name, "/dev/ashmem/CursorWindow")) {
which_heap = HEAP_CURSOR;
+ } else if (base::StartsWith(name, "/dev/ashmem/jit-zygote-cache")) {
+ which_heap = HEAP_DALVIK_OTHER;
+ sub_heap = HEAP_DALVIK_OTHER_ZYGOTE_CODE_CACHE;
} else if (base::StartsWith(name, "/dev/ashmem")) {
which_heap = HEAP_ASHMEM;
}
+ } else if (base::StartsWith(name, "/memfd:jit-cache")) {
+ which_heap = HEAP_DALVIK_OTHER;
+ sub_heap = HEAP_DALVIK_OTHER_APP_CODE_CACHE;
+ } else if (base::StartsWith(name, "/memfd:jit-zygote-cache")) {
+ which_heap = HEAP_DALVIK_OTHER;
+ sub_heap = HEAP_DALVIK_OTHER_ZYGOTE_CODE_CACHE;
} else if (base::StartsWith(name, "[anon:")) {
which_heap = HEAP_UNKNOWN;
if (base::StartsWith(name, "[anon:dalvik-")) {
@@ -337,7 +351,7 @@ static void load_maps(int pid, stats_t* stats, bool* foundSwapPss)
sub_heap = HEAP_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE;
} else if (base::StartsWith(name, "[anon:dalvik-jit-code-cache") ||
base::StartsWith(name, "[anon:dalvik-data-code-cache")) {
- sub_heap = HEAP_DALVIK_OTHER_CODE_CACHE;
+ sub_heap = HEAP_DALVIK_OTHER_APP_CODE_CACHE;
} else if (base::StartsWith(name, "[anon:dalvik-CompilerMetadata")) {
sub_heap = HEAP_DALVIK_OTHER_COMPILER_METADATA;
} else {
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index be9aee410d40..7582cae1a761 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -85,6 +85,10 @@ void setDebugLayersGLES_native(JNIEnv* env, jobject clazz, jstring layers) {
}
}
+bool setInjectLayersPrSetDumpable_native() {
+ return android::GraphicsEnv::getInstance().setInjectLayersPrSetDumpable();
+}
+
void hintActivityLaunch_native(JNIEnv* env, jobject clazz) {
android::GraphicsEnv::getInstance().hintActivityLaunch();
}
@@ -93,6 +97,7 @@ const JNINativeMethod g_methods[] = {
{ "getCanLoadSystemLibraries", "()I", reinterpret_cast<void*>(getCanLoadSystemLibraries_native) },
{ "setDriverPathAndSphalLibraries", "(Ljava/lang/String;Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPathAndSphalLibraries_native) },
{ "setGpuStats", "(Ljava/lang/String;Ljava/lang/String;JJLjava/lang/String;I)V", reinterpret_cast<void*>(setGpuStats_native) },
+ { "setInjectLayersPrSetDumpable", "()Z", reinterpret_cast<void*>(setInjectLayersPrSetDumpable_native) },
{ "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/io/FileDescriptor;JJ)V", reinterpret_cast<void*>(setAngleInfo_native) },
{ "getShouldUseAngle", "(Ljava/lang/String;)Z", reinterpret_cast<void*>(shouldUseAngle_native) },
{ "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) },
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index d80c071c3e26..483b455965f7 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -721,8 +721,11 @@ static const JNINativeMethod gParcelMethods[] = {
{"nativeWriteFloat", "(JF)V", (void*)android_os_Parcel_writeFloat},
// @FastNative
{"nativeWriteDouble", "(JD)V", (void*)android_os_Parcel_writeDouble},
+ // @FastNative
{"nativeWriteString", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeString},
+ // @FastNative
{"nativeWriteStrongBinder", "(JLandroid/os/IBinder;)V", (void*)android_os_Parcel_writeStrongBinder},
+ // @FastNative
{"nativeWriteFileDescriptor", "(JLjava/io/FileDescriptor;)J", (void*)android_os_Parcel_writeFileDescriptor},
{"nativeCreateByteArray", "(J)[B", (void*)android_os_Parcel_createByteArray},
@@ -736,8 +739,11 @@ static const JNINativeMethod gParcelMethods[] = {
{"nativeReadFloat", "(J)F", (void*)android_os_Parcel_readFloat},
// @CriticalNative
{"nativeReadDouble", "(J)D", (void*)android_os_Parcel_readDouble},
+ // @FastNative
{"nativeReadString", "(J)Ljava/lang/String;", (void*)android_os_Parcel_readString},
+ // @FastNative
{"nativeReadStrongBinder", "(J)Landroid/os/IBinder;", (void*)android_os_Parcel_readStrongBinder},
+ // @FastNative
{"nativeReadFileDescriptor", "(J)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_readFileDescriptor},
{"nativeCreate", "()J", (void*)android_os_Parcel_create},
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index bf4ffc7e42e0..3c0971b9ec1a 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -108,7 +108,7 @@ static struct arraymap_offsets_t {
jmethodID put;
} gArrayMapOffsets;
-jclass g_stringClass = nullptr;
+static jclass g_stringClass = nullptr;
// ----------------------------------------------------------------------------
@@ -352,7 +352,7 @@ static Guarded<AssetManager2>& AssetManagerFromLong(jlong ptr) {
}
static jobject NativeGetOverlayableMap(JNIEnv* env, jclass /*clazz*/, jlong ptr,
- jstring package_name) {
+ jstring package_name) {
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
const ScopedUtfChars package_name_utf8(env, package_name);
CHECK(package_name_utf8.c_str() != nullptr);
@@ -397,6 +397,21 @@ static jobject NativeGetOverlayableMap(JNIEnv* env, jclass /*clazz*/, jlong ptr,
return array_map;
}
+static jstring NativeGetOverlayablesToString(JNIEnv* env, jclass /*clazz*/, jlong ptr,
+ jstring package_name) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const ScopedUtfChars package_name_utf8(env, package_name);
+ CHECK(package_name_utf8.c_str() != nullptr);
+ const std::string std_package_name(package_name_utf8.c_str());
+
+ std::string result;
+ if (!assetmanager->GetOverlayablesToString(std_package_name, &result)) {
+ return nullptr;
+ }
+
+ return env->NewStringUTF(result.c_str());
+}
+
#ifdef __ANDROID__ // Layoutlib does not support parcel
static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr<Asset> asset,
jlongArray out_offsets) {
@@ -735,9 +750,48 @@ static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint
}
// May be nullptr.
- const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie);
+ std::shared_ptr<const DynamicRefTable> dynamic_ref_table =
+ assetmanager->GetDynamicRefTableForCookie(cookie);
+
+ std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(
+ std::move(dynamic_ref_table));
+ status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true);
+ asset.reset();
+
+ if (err != NO_ERROR) {
+ jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
+ return 0;
+ }
+ return reinterpret_cast<jlong>(xml_tree.release());
+}
+
+static jlong NativeOpenXmlAssetFd(JNIEnv* env, jobject /*clazz*/, jlong ptr, int jcookie,
+ jobject file_descriptor) {
+ int fd = jniGetFDFromFileDescriptor(env, file_descriptor);
+ ATRACE_NAME(base::StringPrintf("AssetManager::OpenXmlAssetFd(%d)", fd).c_str());
+ if (fd < 0) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor");
+ return 0;
+ }
+
+ base::unique_fd dup_fd(::fcntl(fd, F_DUPFD_CLOEXEC, 0));
+ if (dup_fd < 0) {
+ jniThrowIOException(env, errno);
+ return 0;
+ }
+
+ std::unique_ptr<Asset>
+ asset(Asset::createFromFd(dup_fd.release(), nullptr, Asset::AccessMode::ACCESS_BUFFER));
+
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
+
+ // May be nullptr.
+ std::shared_ptr<const DynamicRefTable> dynamic_ref_table =
+ assetmanager->GetDynamicRefTableForCookie(cookie);
- std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(dynamic_ref_table);
+ std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(
+ std::move(dynamic_ref_table));
status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true);
asset.reset();
@@ -1549,6 +1603,7 @@ static const JNINativeMethod gAssetManagerMethods[] = {
{"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
(void*)NativeOpenNonAssetFd},
{"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset},
+ {"nativeOpenXmlAssetFd", "(JILjava/io/FileDescriptor;)J", (void*)NativeOpenXmlAssetFd},
// AssetManager resource methods.
{"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue},
@@ -1608,6 +1663,8 @@ static const JNINativeMethod gAssetManagerMethods[] = {
(void*)NativeCreateIdmapsForStaticOverlaysTargetingAndroid},
{"nativeGetOverlayableMap", "(JLjava/lang/String;)Ljava/util/Map;",
(void*)NativeGetOverlayableMap},
+ {"nativeGetOverlayablesToString", "(JLjava/lang/String;)Ljava/lang/String;",
+ (void*)NativeGetOverlayablesToString},
// Global management/debug methods.
{"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount},
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 0afbaa0e174c..22323931e94c 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -697,6 +697,9 @@ BinderProxyNativeData* getBPNativeData(JNIEnv* env, jobject obj) {
// same IBinder, and the original BinderProxy is still alive, return the same BinderProxy.
jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{
+ // N.B. This function is called from a @FastNative JNI method, so don't take locks around
+ // calls to Java code or block the calling thread for a long time for any reason.
+
if (val == NULL) return NULL;
if (val->checkSubclass(&gBinderOffsets)) {
@@ -989,6 +992,31 @@ static void android_os_Binder_blockUntilThreadAvailable(JNIEnv* env, jobject cla
return IPCThreadState::self()->blockUntilThreadAvailable();
}
+static jobject android_os_Binder_waitForService(
+ JNIEnv *env,
+ jclass /* clazzObj */,
+ jstring serviceNameObj) {
+
+ const jchar* serviceName = env->GetStringCritical(serviceNameObj, nullptr);
+ if (!serviceName) {
+ signalExceptionForError(env, nullptr, BAD_VALUE, true /*canThrowRemoteException*/);
+ return nullptr;
+ }
+ String16 nameCopy = String16(reinterpret_cast<const char16_t *>(serviceName),
+ env->GetStringLength(serviceNameObj));
+ env->ReleaseStringCritical(serviceNameObj, serviceName);
+
+ auto sm = android::defaultServiceManager();
+ sp<IBinder> service = sm->waitForService(nameCopy);
+
+ if (!service) {
+ signalExceptionForError(env, nullptr, NAME_NOT_FOUND, true /*canThrowRemoteException*/);
+ return nullptr;
+ }
+
+ return javaObjectForIBinder(env, service);
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gBinderMethods[] = {
@@ -1016,7 +1044,8 @@ static const JNINativeMethod gBinderMethods[] = {
{ "flushPendingCommands", "()V", (void*)android_os_Binder_flushPendingCommands },
{ "getNativeBBinderHolder", "()J", (void*)android_os_Binder_getNativeBBinderHolder },
{ "getNativeFinalizer", "()J", (void*)android_os_Binder_getNativeFinalizer },
- { "blockUntilThreadAvailable", "()V", (void*)android_os_Binder_blockUntilThreadAvailable }
+ { "blockUntilThreadAvailable", "()V", (void*)android_os_Binder_blockUntilThreadAvailable },
+ { "waitForService", "(Ljava/lang/String;)Landroid/os/IBinder;", (void*)android_os_Binder_waitForService }
};
const char* const kBinderPathName = "android/os/Binder";
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index a07b730041c5..9267c9ae4219 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -63,20 +63,20 @@
using namespace android;
-static const bool kDebugPolicy = false;
-static const bool kDebugProc = false;
+static constexpr bool kDebugPolicy = false;
+static constexpr bool kDebugProc = false;
// Stack reservation for reading small proc files. Most callers of
// readProcFile() are reading files under this threshold, e.g.,
// /proc/pid/stat. /proc/pid/time_in_state ends up being about 520
// bytes, so use 1024 for the stack to provide a bit of slack.
-static const ssize_t kProcReadStackBufferSize = 1024;
+static constexpr ssize_t kProcReadStackBufferSize = 1024;
// The other files we read from proc tend to be a bit larger (e.g.,
// /proc/stat is about 3kB), so once we exhaust the stack buffer,
// retry with a relatively large heap-allocated buffer. We double
// this size and retry until the whole file fits.
-static const ssize_t kProcReadMinHeapBufferSize = 4096;
+static constexpr ssize_t kProcReadMinHeapBufferSize = 4096;
static int kCgroupFollowForDex2oatOnly = -1;
@@ -195,11 +195,25 @@ jint android_os_Process_getGidForName(JNIEnv* env, jobject clazz, jstring name)
return -1;
}
+static bool verifyGroup(JNIEnv* env, int grp)
+{
+ if (grp < SP_DEFAULT || grp >= SP_CNT) {
+ signalExceptionForError(env, EINVAL, grp);
+ return false;
+ }
+ return true;
+}
+
void android_os_Process_setThreadGroup(JNIEnv* env, jobject clazz, int tid, jint grp)
{
ALOGV("%s tid=%d grp=%" PRId32, __func__, tid, grp);
+ if (!verifyGroup(env, grp)) {
+ return;
+ }
+
SchedPolicy sp = (SchedPolicy) grp;
- int res = set_sched_policy(tid, sp);
+ int res = SetTaskProfiles(tid, {get_sched_policy_name((SchedPolicy)grp)}, true) ? 0 : -1;
+
if (res != NO_ERROR) {
signalExceptionForGroupError(env, -res, tid);
}
@@ -215,14 +229,12 @@ void android_os_Process_setThreadGroup(JNIEnv* env, jobject clazz, int tid, jint
void android_os_Process_setThreadGroupAndCpuset(JNIEnv* env, jobject clazz, int tid, jint grp)
{
ALOGV("%s tid=%d grp=%" PRId32, __func__, tid, grp);
- SchedPolicy sp = (SchedPolicy) grp;
- int res = set_sched_policy(tid, sp);
-
- if (res != NO_ERROR) {
- signalExceptionForGroupError(env, -res, tid);
+ if (!verifyGroup(env, grp)) {
+ return;
}
- res = set_cpuset_policy(tid, sp);
+ int res = SetTaskProfiles(tid, {get_cpuset_policy_profile_name((SchedPolicy)grp)}, true) ? 0 : -1;
+
if (res != NO_ERROR) {
signalExceptionForGroupError(env, -res, tid);
}
@@ -235,7 +247,11 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin
char proc_path[255];
struct dirent *de;
- if ((grp == SP_FOREGROUND) || (grp > SP_MAX)) {
+ if (!verifyGroup(env, grp)) {
+ return;
+ }
+
+ if (grp == SP_FOREGROUND) {
signalExceptionForGroupError(env, EINVAL, pid);
return;
}
@@ -245,7 +261,6 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin
grp = SP_FOREGROUND;
isDefault = true;
}
- SchedPolicy sp = (SchedPolicy) grp;
if (kDebugPolicy) {
char cmdline[32];
@@ -261,7 +276,7 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin
close(fd);
}
- if (sp == SP_BACKGROUND) {
+ if (grp == SP_BACKGROUND) {
ALOGD("setProcessGroup: vvv pid %d (%s)", pid, cmdline);
} else {
ALOGD("setProcessGroup: ^^^ pid %d (%s)", pid, cmdline);
@@ -279,6 +294,7 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin
while ((de = readdir(d))) {
int t_pid;
int t_pri;
+ int err;
if (de->d_name[0] == '.')
continue;
@@ -304,28 +320,16 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin
if (t_pri >= ANDROID_PRIORITY_BACKGROUND) {
// This task wants to stay at background
// update its cpuset so it doesn't only run on bg core(s)
- if (cpusets_enabled()) {
- int err = set_cpuset_policy(t_pid, sp);
- if (err != NO_ERROR) {
- signalExceptionForGroupError(env, -err, t_pid);
- break;
- }
+ err = SetTaskProfiles(t_pid, {get_cpuset_policy_profile_name((SchedPolicy)grp)}, true) ? 0 : -1;
+ if (err != NO_ERROR) {
+ signalExceptionForGroupError(env, -err, t_pid);
+ break;
}
continue;
}
}
- int err;
-
- if (cpusets_enabled()) {
- // set both cpuset and cgroup for general threads
- err = set_cpuset_policy(t_pid, sp);
- if (err != NO_ERROR) {
- signalExceptionForGroupError(env, -err, t_pid);
- break;
- }
- }
- err = set_sched_policy(t_pid, sp);
+ err = SetTaskProfiles(t_pid, {get_cpuset_policy_profile_name((SchedPolicy)grp)}, true) ? 0 : -1;
if (err != NO_ERROR) {
signalExceptionForGroupError(env, -err, t_pid);
break;
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index af34e7b7a7ff..bf1cea8cff2e 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -112,7 +112,9 @@ void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChan
}
static jobject android_view_InputChannel_createInputChannel(JNIEnv* env,
- std::unique_ptr<NativeInputChannel> nativeInputChannel) {
+ sp<InputChannel> inputChannel) {
+ std::unique_ptr<NativeInputChannel> nativeInputChannel =
+ std::make_unique<NativeInputChannel>(inputChannel);
jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,
gInputChannelClassInfo.ctor);
if (inputChannelObj) {
@@ -143,14 +145,12 @@ static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv*
return nullptr;
}
- jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
- std::make_unique<NativeInputChannel>(serverChannel));
+ jobject serverChannelObj = android_view_InputChannel_createInputChannel(env, serverChannel);
if (env->ExceptionCheck()) {
return nullptr;
}
- jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
- std::make_unique<NativeInputChannel>(clientChannel));
+ jobject clientChannelObj = android_view_InputChannel_createInputChannel(env, clientChannel);
if (env->ExceptionCheck()) {
return nullptr;
}
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 0ba000559c1d..d8ca19e6642e 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -151,7 +151,7 @@ void NativeInputEventReceiver::setFdEvents(int events) {
mFdEvents = events;
int fd = mInputConsumer.getChannel()->getFd();
if (events) {
- mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
+ mMessageQueue->getLooper()->addFd(fd, 0, events, this, nullptr);
} else {
mMessageQueue->getLooper()->removeFd(fd);
}
@@ -172,7 +172,7 @@ int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data)
if (events & ALOOPER_EVENT_INPUT) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
- status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
+ status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);
mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
return status == OK || status == NO_MEMORY ? 1 : 0;
}
@@ -233,7 +233,7 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
*outConsumedBatch = false;
}
- ScopedLocalRef<jobject> receiverObj(env, NULL);
+ ScopedLocalRef<jobject> receiverObj(env, nullptr);
bool skipCallbacks = false;
for (;;) {
uint32_t seq;
@@ -332,7 +332,7 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
default:
assert(false); // InputConsumer should prevent this from ever happening
- inputEventObj = NULL;
+ inputEventObj = nullptr;
}
if (inputEventObj) {
@@ -364,13 +364,13 @@ static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject inputChannelObj, jobject messageQueueObj) {
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
- if (inputChannel == NULL) {
+ if (inputChannel == nullptr) {
jniThrowRuntimeException(env, "InputChannel is not initialized.");
return 0;
}
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
- if (messageQueue == NULL) {
+ if (messageQueue == nullptr) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
diff --git a/core/jni/android_view_PointerIcon.cpp b/core/jni/android_view_PointerIcon.cpp
index 4f79790dec51..ed2ce506ab23 100644
--- a/core/jni/android_view_PointerIcon.cpp
+++ b/core/jni/android_view_PointerIcon.cpp
@@ -23,7 +23,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
#include <utils/Log.h>
-#include <android/graphics/GraphicsJNI.h>
+#include <android/graphics/bitmap.h>
#include <nativehelper/ScopedLocalRef.h>
#include "core_jni_helpers.h"
@@ -88,7 +88,7 @@ status_t android_view_PointerIcon_getLoadedIcon(JNIEnv* env, jobject pointerIcon
ScopedLocalRef<jobject> bitmapObj(
env, env->GetObjectField(pointerIconObj, gPointerIconClassInfo.mBitmap));
if (bitmapObj.get()) {
- GraphicsJNI::getSkBitmap(env, bitmapObj.get(), &(outPointerIcon->bitmap));
+ outPointerIcon->bitmap = graphics::Bitmap(env, bitmapObj.get());
}
ScopedLocalRef<jobjectArray> bitmapFramesObj(env, reinterpret_cast<jobjectArray>(
@@ -100,7 +100,7 @@ status_t android_view_PointerIcon_getLoadedIcon(JNIEnv* env, jobject pointerIcon
outPointerIcon->bitmapFrames.resize(size);
for (jsize i = 0; i < size; ++i) {
ScopedLocalRef<jobject> bitmapObj(env, env->GetObjectArrayElement(bitmapFramesObj.get(), i));
- GraphicsJNI::getSkBitmap(env, bitmapObj.get(), &(outPointerIcon->bitmapFrames[i]));
+ outPointerIcon->bitmapFrames[i] = graphics::Bitmap(env, bitmapObj.get());
}
}
diff --git a/core/jni/android_view_PointerIcon.h b/core/jni/android_view_PointerIcon.h
index 00bdfb4bf04e..908948ea2aa4 100644
--- a/core/jni/android_view_PointerIcon.h
+++ b/core/jni/android_view_PointerIcon.h
@@ -21,8 +21,8 @@
#include <vector>
+#include <android/graphics/bitmap.h>
#include <utils/Errors.h>
-#include <SkBitmap.h>
namespace android {
@@ -68,10 +68,10 @@ struct PointerIcon {
}
int32_t style;
- SkBitmap bitmap;
+ graphics::Bitmap bitmap;
float hotSpotX;
float hotSpotY;
- std::vector<SkBitmap> bitmapFrames;
+ std::vector<graphics::Bitmap> bitmapFrames;
int32_t durationPerFrame;
inline bool isNullIcon() {
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 222a87321aa4..538861e21d96 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -52,9 +52,14 @@ static void android_view_RenderNode_output(JNIEnv* env, jobject clazz, jlong ren
renderNode->output();
}
-static jint android_view_RenderNode_getDebugSize(JNIEnv* env, jobject clazz, jlong renderNodePtr) {
+static jint android_view_RenderNode_getUsageSize(JNIEnv* env, jobject clazz, jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- return renderNode->getDebugSize();
+ return renderNode->getUsageSize();
+}
+
+static jint android_view_RenderNode_getAllocatedSize(JNIEnv* env, jobject clazz, jlong renderNodePtr) {
+ RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ return renderNode->getAllocatedSize();
}
static jlong android_view_RenderNode_create(JNIEnv* env, jobject, jstring name) {
@@ -647,7 +652,8 @@ static const JNINativeMethod gMethods[] = {
{ "nCreate", "(Ljava/lang/String;)J", (void*) android_view_RenderNode_create },
{ "nGetNativeFinalizer", "()J", (void*) android_view_RenderNode_getNativeFinalizer },
{ "nOutput", "(J)V", (void*) android_view_RenderNode_output },
- { "nGetDebugSize", "(J)I", (void*) android_view_RenderNode_getDebugSize },
+ { "nGetUsageSize", "(J)I", (void*) android_view_RenderNode_getUsageSize },
+ { "nGetAllocatedSize", "(J)I", (void*) android_view_RenderNode_getAllocatedSize },
{ "nAddAnimator", "(JJ)V", (void*) android_view_RenderNode_addAnimator },
{ "nEndAllAnimators", "(J)V", (void*) android_view_RenderNode_endAllAnimators },
{ "nRequestPositionUpdates", "(JLandroid/graphics/RenderNode$PositionUpdateListener;)V", (void*) android_view_RenderNode_requestPositionUpdates },
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 4c2e91f986d0..058a4c8ee2f9 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -237,12 +237,11 @@ static jlong nativeLockCanvas(JNIEnv* env, jclass clazz,
return 0;
}
- ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
- ACanvas_setBuffer(canvas, &buffer, static_cast<int32_t>(surface->getBuffersDataSpace()));
+ graphics::Canvas canvas(env, canvasObj);
+ canvas.setBuffer(&buffer, static_cast<int32_t>(surface->getBuffersDataSpace()));
if (dirtyRectPtr) {
- ACanvas_clipRect(canvas, {dirtyRect.left, dirtyRect.top,
- dirtyRect.right, dirtyRect.bottom});
+ canvas.clipRect({dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom});
}
if (dirtyRectObj) {
@@ -268,8 +267,8 @@ static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz,
}
// detach the canvas from the surface
- ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
- ACanvas_setBuffer(canvas, nullptr, ADATASPACE_UNKNOWN);
+ graphics::Canvas canvas(env, canvasObj);
+ canvas.setBuffer(nullptr, ADATASPACE_UNKNOWN);
// unlock surface
status_t err = surface->unlockAndPost();
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index bf0f10eb50c6..d5cd278063c0 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -458,15 +458,6 @@ static void nativeSetInputWindowInfo(JNIEnv* env, jclass clazz, jlong transactio
transaction->setInputWindowInfo(ctrl, *handle->getInfo());
}
-static void nativeTransferTouchFocus(JNIEnv* env, jclass clazz, jlong transactionObj,
- jobject fromTokenObj, jobject toTokenObj) {
- auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
-
- sp<IBinder> fromToken(ibinderForJavaObject(env, fromTokenObj));
- sp<IBinder> toToken(ibinderForJavaObject(env, toTokenObj));
- transaction->transferTouchFocus(fromToken, toToken);
-}
-
static void nativeSyncInputWindows(JNIEnv* env, jclass clazz, jlong transactionObj) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
transaction->syncInputWindows();
@@ -1253,6 +1244,15 @@ static jlong nativeReadTransactionFromParcel(JNIEnv* env, jclass clazz, jobject
return reinterpret_cast<jlong>(transaction.release());
}
+static jlong nativeMirrorSurface(JNIEnv* env, jclass clazz, jlong mirrorOfObj) {
+ sp<SurfaceComposerClient> client = SurfaceComposerClient::getDefault();
+ SurfaceControl *mirrorOf = reinterpret_cast<SurfaceControl*>(mirrorOfObj);
+ sp<SurfaceControl> surface = client->mirrorSurface(mirrorOf);
+
+ surface->incStrong((void *)nativeCreate);
+ return reinterpret_cast<jlong>(surface.get());
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod sSurfaceControlMethods[] = {
@@ -1381,8 +1381,6 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeCaptureLayers },
{"nativeSetInputWindowInfo", "(JJLandroid/view/InputWindowHandle;)V",
(void*)nativeSetInputWindowInfo },
- {"nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;)V",
- (void*)nativeTransferTouchFocus },
{"nativeSetMetadata", "(JJILandroid/os/Parcel;)V",
(void*)nativeSetMetadata },
{"nativeGetDisplayedContentSamplingAttributes",
@@ -1405,6 +1403,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeReadTransactionFromParcel },
{"nativeWriteTransactionToParcel", "(JLandroid/os/Parcel;)V",
(void*)nativeWriteTransactionToParcel },
+ {"nativeMirrorSurface", "(J)J",
+ (void*)nativeMirrorSurface },
};
int register_android_view_SurfaceControl(JNIEnv* env)
diff --git a/core/jni/android_view_TextureLayer.cpp b/core/jni/android_view_TextureLayer.cpp
index 1ccb6a8f610c..8a3f54039d05 100644
--- a/core/jni/android_view_TextureLayer.cpp
+++ b/core/jni/android_view_TextureLayer.cpp
@@ -26,10 +26,7 @@
#include <gui/GLConsumer.h>
#include <hwui/Paint.h>
-#include <SkBitmap.h>
-#include <SkCanvas.h>
#include <SkMatrix.h>
-#include <SkBlendMode.h>
#include <DeferredLayerUpdater.h>
#include <Rect.h>
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index 1f69c8bbbe5d..391f515af115 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -124,9 +124,9 @@ static jboolean android_view_TextureView_lockCanvas(JNIEnv* env, jobject,
int32_t status = native_window_lock(window.get(), &outBuffer, &rect);
if (status) return JNI_FALSE;
- ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
- ACanvas_setBuffer(canvas, &outBuffer, ANativeWindow_getBuffersDataSpace(window.get()));
- ACanvas_clipRect(canvas, {rect.left, rect.top, rect.right, rect.bottom});
+ graphics::Canvas canvas(env, canvasObj);
+ canvas.setBuffer(&outBuffer, ANativeWindow_getBuffersDataSpace(window.get()));
+ canvas.clipRect({rect.left, rect.top, rect.right, rect.bottom});
if (dirtyRect) {
INVOKEV(dirtyRect, gRectClassInfo.set,
@@ -140,8 +140,8 @@ static void android_view_TextureView_unlockCanvasAndPost(JNIEnv* env, jobject,
jlong nativeWindow, jobject canvasObj) {
// release the buffer from the canvas
- ACanvas* canvas = ACanvas_getNativeHandleFromJava(env, canvasObj);
- ACanvas_setBuffer(canvas, nullptr, ADATASPACE_UNKNOWN);
+ graphics::Canvas canvas(env, canvasObj);
+ canvas.setBuffer(nullptr, ADATASPACE_UNKNOWN);
if (nativeWindow) {
sp<ANativeWindow> window((ANativeWindow*) nativeWindow);
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 2ea835bb9fc8..4a76654edac2 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -74,7 +74,6 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <bionic/malloc.h>
-#include <cutils/ashmem.h>
#include <cutils/fs.h>
#include <cutils/multiuser.h>
#include <cutils/properties.h>
@@ -305,7 +304,8 @@ enum MountExternalKind {
MOUNT_EXTERNAL_LEGACY = 4,
MOUNT_EXTERNAL_INSTALLER = 5,
MOUNT_EXTERNAL_FULL = 6,
- MOUNT_EXTERNAL_COUNT = 7
+ MOUNT_EXTERNAL_PASS_THROUGH = 7,
+ MOUNT_EXTERNAL_COUNT = 8
};
// The order of entries here must be kept in sync with MountExternalKind enum values.
@@ -717,15 +717,14 @@ static void MountEmulatedStorage(uid_t uid, jint mount_mode,
const userid_t user_id = multiuser_get_user_id(uid);
const std::string user_source = StringPrintf("/mnt/user/%d", user_id);
+ const std::string pass_through_source = StringPrintf("/mnt/pass_through/%d", user_id);
bool isFuse = GetBoolProperty(kPropFuse, false);
CreateDir(user_source, 0751, AID_ROOT, AID_ROOT, fail_fn);
if (isFuse) {
- // TODO(b/135341433): Bind mount the appropriate storage view for the app given its permissions
- // media and media_location permission access. This should prevent the kernel from incorrectly
- // sharing a cache across permission buckets
- BindMount(user_source, "/storage", fail_fn);
+ BindMount(mount_mode == MOUNT_EXTERNAL_PASS_THROUGH ? pass_through_source : user_source,
+ "/storage", fail_fn);
} else {
const std::string& storage_source = ExternalStorageViews[mount_mode];
BindMount(storage_source, "/storage", fail_fn);
@@ -1667,11 +1666,6 @@ static void com_android_internal_os_Zygote_nativeInitNativeState(JNIEnv* env, jc
if (!SetTaskProfiles(0, {})) {
ZygoteFailure(env, "zygote", nullptr, "Zygote SetTaskProfiles failed");
}
-
- /*
- * ashmem initialization to avoid dlopen overhead
- */
- ashmem_init();
}
/**
diff --git a/core/jni/com_android_internal_os_ZygoteInit.cpp b/core/jni/com_android_internal_os_ZygoteInit.cpp
index c2a5ee43dbd5..a5152b6e689c 100644
--- a/core/jni/com_android_internal_os_ZygoteInit.cpp
+++ b/core/jni/com_android_internal_os_ZygoteInit.cpp
@@ -16,17 +16,13 @@
#define LOG_TAG "Zygote"
-#include <EGL/egl.h>
-#include <Properties.h>
+#include <android/graphics/jni_runtime.h>
#include <ui/GraphicBufferMapper.h>
#include "core_jni_helpers.h"
namespace {
-using android::uirenderer::Properties;
-using android::uirenderer::RenderPipelineType;
-
// Shadow call stack (SCS) is a security mitigation that uses a separate stack
// (the SCS) for return addresses. In versions of Android newer than P, the
// compiler cooperates with the system to ensure that the SCS address is always
@@ -64,9 +60,7 @@ void android_internal_os_ZygoteInit_nativePreloadAppProcessHALs(JNIEnv* env, jcl
void android_internal_os_ZygoteInit_nativePreloadGraphicsDriver(JNIEnv* env, jclass) {
ScopedSCSExit x;
- if (Properties::peekRenderPipelineType() == RenderPipelineType::SkiaGL) {
- eglGetDisplay(EGL_DEFAULT_DISPLAY);
- }
+ zygote_preload_graphics();
}
const JNINativeMethod gMethods[] = {
diff --git a/core/proto/Android.bp b/core/proto/Android.bp
index e199dab181e0..6119d71d4456 100644
--- a/core/proto/Android.bp
+++ b/core/proto/Android.bp
@@ -30,9 +30,9 @@ cc_library_static {
}
java_library_host {
- name: "windowmanager-log-proto",
+ name: "protolog-proto",
srcs: [
- "android/server/windowmanagerlog.proto"
+ "android/server/protolog.proto"
],
proto: {
type: "full",
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index b8c5270ef9d8..94be61f40eae 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2412,4 +2412,18 @@ enum PageId {
// OS: R
SETTINGS_WIFI_CONFIGURE_NETWORK = 1800;
+ // OPEN: Settings > Accessibility > Magnification
+ // CATEGORY: SETTINGS
+ // OS: R
+ // Note: Shows up only when Magnify with shortcut is enabled
+ // and under accessibility button mode.
+ DIALOG_TOGGLE_SCREEN_MAGNIFICATION_ACCESSIBILITY_BUTTON = 1801;
+
+ // OPEN: Settings > Accessibility > Magnification
+ // CATEGORY: SETTINGS
+ // OS: R
+ // Note: Shows up only when Magnify with shortcut is enabled.
+ // and under gesture navigation mode.
+ DIALOG_TOGGLE_SCREEN_MAGNIFICATION_GESTURE_NAVIGATION = 1802;
+
}
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 0a893c74051e..920d0f6e7794 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -855,6 +855,7 @@ message GlobalSettingsProto {
optional SettingProto low_battery_sounds_enabled = 12 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto trusted = 13;
optional SettingProto unlock = 14;
+ optional SettingProto wireless_charging_started = 15;
}
optional Sounds sounds = 110;
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index d5528deeaf15..4ea574dadc73 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -211,6 +211,11 @@ message SecureSettingsProto {
optional SettingProto silence_timer_touch_count = 11 [ (android.privacy).dest =
DEST_AUTOMATIC ];
optional SettingProto skip_touch_count = 12 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto aware_tap_pause_gesture_count = 13 [
+ (android.privacy).dest =
+ DEST_AUTOMATIC ];
+ optional SettingProto aware_tap_pause_touch_count = 14 [ (android.privacy).dest =
+ DEST_AUTOMATIC ];
}
optional Gesture gesture = 74;
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index d54b6b0b00e5..d01a45c2002a 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -118,13 +118,15 @@ message TaskRecordProto {
message ActivityRecordProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
- optional .com.android.server.wm.ConfigurationContainerProto configuration_container = 1;
+ // To be removed soon.
+ optional .com.android.server.wm.ConfigurationContainerProto configuration_container = 1 [deprecated=true];
optional .com.android.server.wm.IdentifierProto identifier = 2;
optional string state = 3;
optional bool visible = 4;
optional bool front_of_task = 5;
optional int32 proc_id = 6;
optional bool translucent = 7;
+ optional .com.android.server.wm.AppWindowTokenProto app_window_token = 8;
}
message KeyguardControllerProto {
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 79167ab476c1..06040a599df1 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -43,11 +43,18 @@ message JobSchedulerServiceDumpProto {
reserved 15; // next_heartbeat
reserved 16; // last_heartbeat_time_millis
reserved 17; // next_heartbeat_time_millis
- optional bool in_parole = 18;
+ reserved 18; // in_parole
optional bool in_thermal = 19;
repeated int32 started_users = 2;
+ message JobRestriction {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional .android.app.job.StopReasonEnum reason = 1;
+ optional bool is_restricting = 2;
+ }
+
message RegisteredJob {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -56,20 +63,22 @@ message JobSchedulerServiceDumpProto {
optional bool is_job_ready_to_be_executed = 10;
// A job is ready to be executed if:
- // is_job_ready && are_users_started && !is_job_thermal_constrained && !is_job_pending &&
+ // is_job_ready && are_users_started && !is_job_restricted && !is_job_pending &&
// !is_job_currently_active && !is_uid_backing_up &&
// is_component_usable.
optional bool is_job_ready = 3;
optional bool are_users_started = 4;
- optional bool is_job_thermal_constrained = 11;
+ optional bool is_job_restricted = 11;
optional bool is_job_pending = 5;
optional bool is_job_currently_active = 6;
optional bool is_uid_backing_up = 7;
optional bool is_component_usable = 8;
+ repeated JobRestriction restrictions = 12;
+
reserved 9; // last_run_heartbeat
- // Next tag: 12
+ // Next tag: 13
}
repeated RegisteredJob registered_jobs = 3;
@@ -525,7 +534,7 @@ message StateControllerProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional bool is_charging = 1;
- optional bool is_in_parole = 2;
+ reserved 2; // is_in_parole
optional int64 elapsed_realtime = 6;
// List of UIDs currently in the foreground.
diff --git a/core/proto/android/server/notificationhistory.proto b/core/proto/android/server/notificationhistory.proto
new file mode 100644
index 000000000000..148bd7e4b663
--- /dev/null
+++ b/core/proto/android/server/notificationhistory.proto
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package com.android.server.notification;
+
+import "frameworks/base/core/proto/android/server/enums.proto";
+
+option java_multiple_files = true;
+
+// On disk data store for historical notifications
+message NotificationHistoryProto {
+ message StringPool {
+ optional int32 size = 1;
+ repeated string strings = 2;
+ }
+
+ message Notification {
+ // The package that posted the notification
+ optional string package = 1;
+ // package_index contains the index + 1 of the package name in the string pool
+ optional int32 package_index = 2;
+
+ // The name of the NotificationChannel this notification was posted to
+ optional string channel_name = 3;
+ // channel_name_index contains the index + 1 of the channel name in the string pool
+ optional int32 channel_name_index = 4;
+
+ // The id of the NotificationChannel this notification was posted to
+ optional string channel_id = 5;
+ // channel_id_index contains the index + 1 of the channel id in the string pool
+ optional int32 channel_id_index = 6;
+
+ // The uid of the package that posted the notification
+ optional int32 uid = 7;
+ // The user id of the package that posted the notification
+ optional int32 user_id = 8;
+ // The time at which the notification was posted
+ optional int64 posted_time_ms = 9;
+ // The title of the notification
+ optional string title = 10;
+ // The text of the notification
+ optional string text = 11;
+ // The small icon of the notification
+ optional Icon icon = 12;
+
+ // Matches the constants of android.graphics.drawable.Icon
+ enum ImageTypeEnum {
+ TYPE_UNKNOWN = 0;
+ TYPE_BITMAP = 1;
+ TYPE_RESOURCE = 2;
+ TYPE_DATA = 3;
+ TYPE_URI = 4;
+ TYPE_ADAPTIVE_BITMAP = 5;
+ }
+
+ message Icon {
+ optional ImageTypeEnum image_type = 1;
+ optional string image_bitmap_filename = 2;
+ optional int32 image_resource_id = 3;
+ optional bytes image_data = 4;
+ optional string image_uri = 5;
+ }
+ }
+
+ // The time the last entry was written
+ optional int64 end_time_ms = 1;
+ // Pool of strings to save space
+ optional StringPool stringpool = 2;
+ // Versioning fields
+ optional int32 major_version = 3;
+ optional int32 minor_version = 4;
+
+ // List of historical notifications
+ repeated Notification notification = 5;
+}
diff --git a/core/proto/android/server/protolog.proto b/core/proto/android/server/protolog.proto
new file mode 100644
index 000000000000..3512c0aea4a5
--- /dev/null
+++ b/core/proto/android/server/protolog.proto
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+package com.android.server.protolog;
+
+option java_multiple_files = true;
+
+/* represents a single log entry */
+message ProtoLogMessage {
+ /* log statement identifier, created from message string and log level. */
+ optional sfixed32 message_hash = 1;
+ /* log time, relative to the elapsed system time clock. */
+ optional fixed64 elapsed_realtime_nanos = 2;
+ /* string parameters passed to the log call. */
+ repeated string str_params = 3;
+ /* integer parameters passed to the log call. */
+ repeated sint64 sint64_params = 4 [packed=true];
+ /* floating point parameters passed to the log call. */
+ repeated double double_params = 5 [packed=true];
+ /* boolean parameters passed to the log call. */
+ repeated bool boolean_params = 6 [packed=true];
+}
+
+/* represents a log file containing ProtoLog log entries.
+ Encoded, it should start with 0x9 0x50 0x52 0x4f 0x54 0x4f 0x4c 0x4f 0x47 (.PROTOLOG), such
+ that they can be easily identified. */
+message ProtoLogFileProto {
+ /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L
+ (this is needed because enums have to be 32 bits and there's no nice way to put 64bit
+ constants into .proto files. */
+ enum MagicNumber {
+ INVALID = 0;
+ MAGIC_NUMBER_L = 0x544f5250; /* PROT (little-endian ASCII) */
+ MAGIC_NUMBER_H = 0x474f4c4f; /* OLOG (little-endian ASCII) */
+ }
+
+ /* the magic number header */
+ optional fixed64 magic_number = 1;
+ /* log proto version. */
+ optional string version = 2;
+ /* offset between real-time clock and elapsed system time clock in miliseconds.
+ Calculated as: (System.currentTimeMillis() - (SystemClock.elapsedRealtimeNanos() / 1000000) */
+ optional fixed64 realTimeToElapsedTimeOffsetMillis = 3;
+ /* log entries */
+ repeated ProtoLogMessage log = 4;
+}
diff --git a/core/proto/android/server/usagestatsservice.proto b/core/proto/android/server/usagestatsservice.proto
index 75f265ea6a88..f26eefad24e1 100644
--- a/core/proto/android/server/usagestatsservice.proto
+++ b/core/proto/android/server/usagestatsservice.proto
@@ -114,6 +114,4 @@ message IntervalStatsProto {
repeated UsageStats packages = 20;
repeated Configuration configurations = 21;
repeated Event event_log = 22;
-
- repeated Event pending_events = 23; // TODO: move to usagestatsservice_v2.proto
}
diff --git a/core/proto/android/server/usagestatsservice_v2.proto b/core/proto/android/server/usagestatsservice_v2.proto
new file mode 100644
index 000000000000..a28fcf3589f1
--- /dev/null
+++ b/core/proto/android/server/usagestatsservice_v2.proto
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package com.android.server.usage;
+import "frameworks/base/core/proto/android/content/configuration.proto";
+import "frameworks/base/core/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+
+/**
+ * Obfuscated version of android.service.IntervalStatsProto (usagestatsservice.proto).
+ */
+message IntervalStatsObfuscatedProto {
+
+ message CountAndTime {
+ optional int32 count = 1;
+ optional int64 time_ms = 2;
+ }
+
+ // Stores the relevant information an IntervalStats will have about a Configuration
+ message Configuration {
+ optional .android.content.ConfigurationProto config = 1;
+ optional int64 last_time_active_ms = 2;
+ optional int64 total_time_active_ms = 3;
+ optional int32 count = 4;
+ optional bool active = 5;
+ }
+
+ // The following fields contain supplemental data used to build IntervalStats.
+ optional int64 end_time_ms = 1;
+ optional int32 major_version = 2;
+ optional int32 minor_version = 3;
+
+ // The following fields contain aggregated usage stats data
+ optional CountAndTime interactive = 10;
+ optional CountAndTime non_interactive = 11;
+ optional CountAndTime keyguard_shown = 12;
+ optional CountAndTime keyguard_hidden = 13;
+
+ // The following fields contain listed usage stats data
+ repeated UsageStatsObfuscatedProto packages = 20;
+ repeated Configuration configurations = 21;
+ repeated EventObfuscatedProto event_log = 22;
+ // The following field is only used to persist the reported events before a user unlock
+ repeated PendingEventProto pending_events = 23;
+}
+
+/**
+ * Stores the relevant information from an obfuscated UsageStats.
+ */
+message UsageStatsObfuscatedProto {
+ message ChooserAction {
+ message CategoryCount {
+ optional int32 category_token = 1;
+ optional int32 count = 2;
+ }
+ optional int32 action_token = 1;
+ repeated CategoryCount counts = 2;
+ }
+ optional int32 package_token = 1;
+ optional int64 last_time_active_ms = 3;
+ optional int64 total_time_active_ms = 4;
+ optional int32 last_event = 5;
+ optional int32 app_launch_count = 6;
+ repeated ChooserAction chooser_actions = 7;
+ optional int64 last_time_service_used_ms = 8;
+ optional int64 total_time_service_used_ms = 9;
+ optional int64 last_time_visible_ms = 10;
+ optional int64 total_time_visible_ms = 11;
+}
+
+/**
+ * Stores the relevant information from an obfuscated Event.
+ */
+message EventObfuscatedProto {
+ optional int32 package_token = 1;
+ optional int32 class_token = 2;
+ optional int64 time_ms = 3;
+ optional int32 flags = 4;
+ optional int32 type = 5;
+ optional .android.content.ConfigurationProto config = 6;
+ optional int32 shortcut_id_token = 7;
+ optional int32 standby_bucket = 8;
+ optional int32 notification_channel_id_token = 9;
+ optional int32 instance_id = 10;
+ optional int32 task_root_package_token = 11;
+ optional int32 task_root_class_token = 12;
+}
+
+/**
+ * This message stores all of the fields in an Event object as strings instead of tokens.
+ */
+message PendingEventProto {
+ optional string package_name = 1;
+ optional string class_name = 2;
+ optional int64 time_ms = 3;
+ optional int32 flags = 4;
+ optional int32 type = 5;
+ optional .android.content.ConfigurationProto config = 6;
+ optional string shortcut_id = 7;
+ optional int32 standby_bucket = 8;
+ optional string notification_channel_id = 9;
+ optional int32 instance_id = 10;
+ optional string task_root_package = 11;
+ optional string task_root_class = 12;
+}
+
+/**
+ * A proto message representing the obfuscated tokens mappings for Usage Stats.
+ */
+message ObfuscatedPackagesProto {
+ message PackagesMap {
+ optional int32 package_token = 1;
+ // The list of strings for each package where their indices are the token
+ repeated string strings = 2;
+ }
+
+ optional int32 counter = 1;
+ // Stores the mappings for every package
+ repeated PackagesMap packages_map = 2;
+}
diff --git a/core/proto/android/server/windowmanagerlog.proto b/core/proto/android/server/windowmanagerlog.proto
deleted file mode 100644
index 5bee1bd670fc..000000000000
--- a/core/proto/android/server/windowmanagerlog.proto
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.
- */
-
-syntax = "proto2";
-
-package com.android.server.wm;
-
-option java_multiple_files = true;
-
-/* represents a single log entry */
-message ProtoLogMessage {
- /* log statement identifier, created from message string and log level. */
- optional fixed32 message_hash = 1;
- /* log time, relative to the elapsed system time clock. */
- optional fixed64 elapsed_realtime_nanos = 2;
- /* string parameters passed to the log call. */
- repeated string str_params = 3;
- /* integer parameters passed to the log call. */
- repeated sint64 sint64_params = 4 [packed=true];
- /* floating point parameters passed to the log call. */
- repeated double double_params = 5 [packed=true];
- /* boolean parameters passed to the log call. */
- repeated bool boolean_params = 6 [packed=true];
-}
-
-/* represents a log file containing window manager log entries.
- Encoded, it should start with 0x9 0x57 0x49 0x4e 0x44 0x4f 0x4c 0x4f 0x47 (.WINDOLOG), such
- that they can be easily identified. */
-message WindowManagerLogFileProto {
- /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L
- (this is needed because enums have to be 32 bits and there's no nice way to put 64bit
- constants into .proto files. */
- enum MagicNumber {
- INVALID = 0;
- MAGIC_NUMBER_L = 0x444e4957; /* WIND (little-endian ASCII) */
- MAGIC_NUMBER_H = 0x474f4c4f; /* OLOG (little-endian ASCII) */
- }
-
- /* the magic number header */
- optional fixed64 magic_number = 1;
- /* log proto version. */
- optional string version = 2;
- /* offset between real-time clock and elapsed system time clock in miliseconds.
- Calculated as: (System.currentTimeMillis() - (SystemClock.elapsedRealtimeNanos() / 1000000) */
- optional fixed64 realTimeToElapsedTimeOffsetMillis = 3;
- /* log entries */
- repeated ProtoLogMessage log = 4;
-}
diff --git a/core/proto/android/service/package.proto b/core/proto/android/service/package.proto
index 6ffa0c943037..d010c8ff8ad9 100644
--- a/core/proto/android/service/package.proto
+++ b/core/proto/android/service/package.proto
@@ -110,10 +110,17 @@ message PackageProto {
optional bool is_launched = 6;
optional EnabledState enabled_state = 7;
optional string last_disabled_app_caller = 8;
- optional string suspending_package = 9;
+ repeated string suspending_package = 9;
optional int32 distraction_flags = 10;
}
+ message InstallSourceProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // The package that requested the installation of this one.
+ optional string initiating_package_name = 1;
+ }
+
// Name of package. e.g. "com.android.providers.telephony".
optional string name = 1;
// UID for this package as assigned by Android OS.
@@ -133,4 +140,6 @@ message PackageProto {
repeated SplitProto splits = 8;
// Per-user package info.
repeated UserInfoProto users = 9;
+ // Where the request to install this package came from,
+ optional InstallSourceProto install_source = 10;
}
diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto
index f49a04422c0e..ad7299d9a45c 100644
--- a/core/proto/android/service/procstats.proto
+++ b/core/proto/android/service/procstats.proto
@@ -241,12 +241,25 @@ message PackageAssociationSourceProcessStatsProto {
repeated StateStats active_state_stats = 6;
}
-// Next Tag: 3
+// Next Tag: 7
message PackageAssociationProcessStatsProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
// Name of the target component.
optional string component_name = 1;
+
+ // Total count of the times this association appeared.
+ optional int32 total_count = 3;
+
+ // Millisecond uptime total duration this association was around.
+ optional int64 total_duration_ms = 4;
+
+ // Total count of the times this association became actively impacting its target process.
+ optional int32 active_count = 5;
+
+ // Millisecond uptime total duration this association was around.
+ optional int64 active_duration_ms = 6;
+
// Information on one source in this association.
repeated PackageAssociationSourceProcessStatsProto sources = 2;
}
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index 0821d147044d..15813a1b2a72 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -113,9 +113,9 @@ enum EventId {
PROVISIONING_PREPROVISIONING_ACTIVITY_TIME_MS = 87;
PROVISIONING_ENCRYPT_DEVICE_ACTIVITY_TIME_MS = 88;
PROVISIONING_WEB_ACTIVITY_TIME_MS = 89;
- PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 90;
- PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 91;
- PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 92;
+ PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 90 [deprecated=true];
+ PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 91 [deprecated=true];
+ PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 92 [deprecated=true];
PROVISIONING_NETWORK_TYPE = 93;
PROVISIONING_ACTION = 94;
PROVISIONING_EXTRAS = 95;
diff --git a/core/proto/android/telecomm/enums.proto b/core/proto/android/telecomm/enums.proto
index 7a2ba624c021..5ca4a85f7c6a 100644
--- a/core/proto/android/telecomm/enums.proto
+++ b/core/proto/android/telecomm/enums.proto
@@ -110,6 +110,24 @@ enum CallStateEnum {
* {@link android.telecom.Connection#CAPABILITY_CAN_PULL_CALL}.
*/
PULLING = 10;
+
+ /**
+ * Indicates that an incoming call has been answered by the in-call UI, but Telephony hasn't yet
+ * set the call to active.
+ */
+ ANSWERED = 11;
+
+ /**
+ * Indicates that the call is undergoing audio processing by a different app in the background.
+ * @see android.telecom.Call#STATE_AUDIO_PROCESSING
+ */
+ AUDIO_PROCESSING = 12;
+
+ /**
+ * Indicates that the call is in a fake ringing state.
+ * @see android.telecom.Call#STATE_SIMULATED_RINGING
+ */
+ SIMULATED_RINGING = 13;
}
// Disconnect causes for a call. Primarily used by android/telecom/DisconnectCause.java
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ac91f125047d..0304f3f16a76 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -676,6 +676,7 @@
<!-- Grouping for platform runtime permissions is not accessible to apps
@hide
@SystemApi
+ @TestApi
-->
<permission-group android:name="android.permission-group.UNDEFINED"
android:priority="100" />
@@ -826,6 +827,18 @@
android:permissionFlags="hardRestricted"
android:protectionLevel="dangerous" />
+ <!-- @SystemApi @TestApi Allows an application to forward cell broadcast messages to the cell
+ broadcast module. This is required in order to bind to the cell broadcast service, and
+ ensures that only the system can forward messages to it.
+
+ <p>Protection level: signature
+
+ @hide -->
+ <permission android:name="android.permission.BIND_CELL_BROADCAST_SERVICE"
+ android:label="@string/permlab_bindCellBroadcastService"
+ android:description="@string/permdesc_bindCellBroadcastService"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi @TestApi Allows an application to read previously received cell broadcast
messages and to register a content observer to get notifications when
a cell broadcast has been received and added to the database. For
@@ -1649,7 +1662,7 @@
@hide This should only be used by Settings and SystemUI.
-->
<permission android:name="android.permission.NETWORK_SETTINGS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|telephony" />
<!-- Allows SetupWizard to call methods in Networking services
<p>Not for use by any other third-party or privileged applications.
@@ -2163,12 +2176,12 @@
<!-- Must be required by a telephony data service to ensure that only the
system can bind to it.
- <p>Protection level: signature
+ <p>Protection level: signature|telephony
@SystemApi
@hide
-->
<permission android:name="android.permission.BIND_TELEPHONY_DATA_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|telephony" />
<!-- Must be required by a NetworkService to ensure that only the
system can bind to it.
@@ -2189,11 +2202,11 @@
<!-- @SystemApi Must be required by an EuiccService to ensure that only the system can bind to
it.
- <p>Protection level: signature
+ <p>Protection level: signature|telephony
@hide
-->
<permission android:name="android.permission.BIND_EUICC_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|telephony" />
<!-- ================================== -->
<!-- Permissions for sdcard interaction -->
@@ -2299,7 +2312,7 @@
types of interactions
@hide -->
<permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
- android:protectionLevel="signature|installer" />
+ android:protectionLevel="signature|installer|telephony" />
<!-- @SystemApi Allows an application to start its own activities, but on a different profile
associated with the user. For example, an application running on the main profile of a user
@@ -2929,8 +2942,9 @@
<!-- Allows an application to be the status bar. Currently used only by SystemUI.apk
@hide -->
+ // TODO: remove telephony once decouple settings activity from phone process
<permission android:name="android.permission.STATUS_BAR_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|telephony" />
<!-- Allows an application to bind to third party quick settings tiles.
<p>Should only be requested by the System, should be required by
@@ -2980,7 +2994,7 @@
@hide
-->
<permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|telephony|wifi" />
<!-- @SystemApi Allows an application to use
{@link android.view.WindowManager.LayoutsParams#SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS}
@@ -3057,7 +3071,7 @@
@hide
-->
<permission android:name="android.permission.SET_ACTIVITY_WATCHER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|telephony" />
<!-- @SystemApi Allows an application to call the activity manager shutdown() API
to put the higher-level system there into a shutdown state.
@@ -3536,6 +3550,13 @@
<permission android:name="android.permission.GET_RUNTIME_PERMISSIONS"
android:protectionLevel="signature" />
+ <!-- @SystemApi Allows the system to restore runtime permission state. This might grant
+ permissions, hence this is a more scoped, less powerful variant of GRANT_RUNTIME_PERMISSIONS.
+ Among other restrictions this cannot override user choices.
+ @hide -->
+ <permission android:name="android.permission.RESTORE_RUNTIME_PERMISSIONS"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows an application to change policy_fixed permissions.
@hide -->
<permission android:name="android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY"
@@ -3554,7 +3575,7 @@
<!-- @SystemApi Allows an application to manage the holders of a role.
@hide -->
<permission android:name="android.permission.MANAGE_ROLE_HOLDERS"
- android:protectionLevel="signature|installer" />
+ android:protectionLevel="signature|installer|telephony" />
<!-- @SystemApi Allows an application to observe role holder changes.
@hide -->
@@ -3765,7 +3786,7 @@
@hide
-->
<permission android:name="android.permission.DEVICE_POWER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|telephony" />
<!-- Allows toggling battery saver on the system.
Superseded by DEVICE_POWER permission. @hide @SystemApi
@@ -3800,13 +3821,13 @@
<p>Not for use by third-party applications.
-->
<permission android:name="android.permission.BROADCAST_SMS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|telephony" />
<!-- Allows an application to broadcast a WAP PUSH receipt notification.
<p>Not for use by third-party applications.
-->
<permission android:name="android.permission.BROADCAST_WAP_PUSH"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|telephony" />
<!-- @SystemApi Allows an application to broadcast privileged networking requests.
<p>Not for use by third-party applications.
@@ -4421,13 +4442,13 @@
{@link android.provider.BlockedNumberContract}.
@hide -->
<permission android:name="android.permission.READ_BLOCKED_NUMBERS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|telephony" />
<!-- Allows the holder to write blocked numbers. See
{@link android.provider.BlockedNumberContract}.
@hide -->
<permission android:name="android.permission.WRITE_BLOCKED_NUMBERS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|telephony" />
<!-- Must be required by an {@link android.service.vr.VrListenerService}, to ensure that only
the system can bind to it.
@@ -4587,7 +4608,7 @@
<!-- @hide Permission that allows configuring appops.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.MANAGE_APPOPS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|telephony" />
<!-- @hide Permission that allows background clipboard access.
<p>Not for use by third-party applications. -->
@@ -4969,6 +4990,14 @@
</intent-filter>
</receiver>
+ <receiver android:name="com.android.server.updates.EmergencyNumberDbInstallReceiver"
+ android:permission="android.permission.UPDATE_CONFIG">
+ <intent-filter>
+ <action android:name="android.os.action.UPDATE_EMERGENCY_NUMBER_DB" />
+ <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+ </intent-filter>
+ </receiver>
+
<receiver android:name="com.android.server.MasterClearReceiver"
android:permission="android.permission.MASTER_CLEAR">
<intent-filter
diff --git a/core/res/TEST_MAPPING b/core/res/TEST_MAPPING
index ccd91a41ab68..9185bae4e992 100644
--- a/core/res/TEST_MAPPING
+++ b/core/res/TEST_MAPPING
@@ -5,6 +5,9 @@
"options": [
{
"include-filter": "android.permission2.cts.PermissionPolicyTest#platformPermissionPolicyIsUnaltered"
+ },
+ {
+ "include-filter": "android.permission2.cts.RuntimePermissionProperties"
}
]
}
diff --git a/core/res/res/anim/screen_rotate_0_exit.xml b/core/res/res/anim/screen_rotate_0_exit.xml
index f1df2de04619..37d5a4115621 100644
--- a/core/res/res/anim/screen_rotate_0_exit.xml
+++ b/core/res/res/anim/screen_rotate_0_exit.xml
@@ -19,7 +19,4 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
- <alpha android:fromAlpha="1.0" android:toAlpha="0"
- android:interpolator="@interpolator/decelerate_quint"
- android:duration="@android:integer/config_shortAnimTime" />
</set>
diff --git a/core/res/res/anim/screen_rotate_180_exit.xml b/core/res/res/anim/screen_rotate_180_exit.xml
index 1eb6361c736f..58a1868bd398 100644
--- a/core/res/res/anim/screen_rotate_180_exit.xml
+++ b/core/res/res/anim/screen_rotate_180_exit.xml
@@ -25,7 +25,4 @@
android:fillEnabled="true"
android:fillBefore="true" android:fillAfter="true"
android:duration="@android:integer/config_mediumAnimTime" />
- <alpha android:fromAlpha="1.0" android:toAlpha="0"
- android:interpolator="@interpolator/decelerate_cubic"
- android:duration="@android:integer/config_mediumAnimTime"/>
</set> \ No newline at end of file
diff --git a/core/res/res/anim/screen_rotate_alpha.xml b/core/res/res/anim/screen_rotate_alpha.xml
new file mode 100644
index 000000000000..c49ef9cafd39
--- /dev/null
+++ b/core/res/res/anim/screen_rotate_alpha.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime" />
+</set>
diff --git a/core/res/res/anim/screen_rotate_minus_90_exit.xml b/core/res/res/anim/screen_rotate_minus_90_exit.xml
index 9b389392c3e4..0927dd30ceb3 100644
--- a/core/res/res/anim/screen_rotate_minus_90_exit.xml
+++ b/core/res/res/anim/screen_rotate_minus_90_exit.xml
@@ -40,9 +40,4 @@
android:fillEnabled="true"
android:fillBefore="true" android:fillAfter="true"
android:duration="@android:integer/config_mediumAnimTime" />
- <alpha android:fromAlpha="1.0" android:toAlpha="0"
- android:interpolator="@interpolator/decelerate_quint"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:duration="@android:integer/config_mediumAnimTime" />
</set>
diff --git a/core/res/res/anim/screen_rotate_minus_90_frame.xml b/core/res/res/anim/screen_rotate_minus_90_frame.xml
deleted file mode 100644
index 2d198f3929e6..000000000000
--- a/core/res/res/anim/screen_rotate_minus_90_frame.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
- <rotate android:fromDegrees="0" android:toDegrees="90"
- android:pivotX="50%" android:pivotY="50%"
- android:interpolator="@interpolator/decelerate_quint"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:duration="@android:integer/config_longAnimTime" />
-</set>
diff --git a/core/res/res/anim/screen_rotate_plus_90_exit.xml b/core/res/res/anim/screen_rotate_plus_90_exit.xml
index fa345337cbf6..fd786f9afce0 100644
--- a/core/res/res/anim/screen_rotate_plus_90_exit.xml
+++ b/core/res/res/anim/screen_rotate_plus_90_exit.xml
@@ -40,9 +40,4 @@
android:fillEnabled="true"
android:fillBefore="true" android:fillAfter="true"
android:duration="@android:integer/config_mediumAnimTime" />
- <alpha android:fromAlpha="1.0" android:toAlpha="0"
- android:interpolator="@interpolator/decelerate_quint"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:duration="@android:integer/config_mediumAnimTime" />
</set>
diff --git a/core/res/res/anim/screen_rotate_plus_90_frame.xml b/core/res/res/anim/screen_rotate_plus_90_frame.xml
deleted file mode 100644
index cd2005087291..000000000000
--- a/core/res/res/anim/screen_rotate_plus_90_frame.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
- <rotate android:fromDegrees="0" android:toDegrees="-90"
- android:pivotX="50%" android:pivotY="50%"
- android:interpolator="@interpolator/decelerate_quint"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:duration="@android:integer/config_longAnimTime" />
-</set>
diff --git a/core/res/res/drawable-hdpi/ic_launcher_android.png b/core/res/res/drawable-hdpi/ic_launcher_android.png
index 2e9b196c9625..8fed9534e1c8 100644
--- a/core/res/res/drawable-hdpi/ic_launcher_android.png
+++ b/core/res/res/drawable-hdpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_launcher_android.png b/core/res/res/drawable-mdpi/ic_launcher_android.png
index baacd4f23e43..16b66a15d544 100644
--- a/core/res/res/drawable-mdpi/ic_launcher_android.png
+++ b/core/res/res/drawable-mdpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_launcher_android.png b/core/res/res/drawable-xhdpi/ic_launcher_android.png
index 00b69a53a62b..824794a6b76e 100644
--- a/core/res/res/drawable-xhdpi/ic_launcher_android.png
+++ b/core/res/res/drawable-xhdpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_launcher_android.png b/core/res/res/drawable-xxhdpi/ic_launcher_android.png
index ad05cd5b337b..81268b38ccc8 100644
--- a/core/res/res/drawable-xxhdpi/ic_launcher_android.png
+++ b/core/res/res/drawable-xxhdpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_launcher_android.png b/core/res/res/drawable-xxxhdpi/ic_launcher_android.png
new file mode 100644
index 000000000000..eedc9f94fc89
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/layout/resolve_list_item.xml b/core/res/res/layout/resolve_list_item.xml
index 0bdb25a8d307..485709523e66 100644
--- a/core/res/res/layout/resolve_list_item.xml
+++ b/core/res/res/layout/resolve_list_item.xml
@@ -22,8 +22,6 @@
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:minHeight="?attr/listPreferredItemHeightSmall"
- android:paddingTop="4dp"
- android:paddingBottom="4dp"
android:background="?attr/activatedBackgroundIndicator">
<!-- Activity icon when presenting dialog
@@ -32,7 +30,8 @@
android:layout_width="@dimen/resolver_icon_size"
android:layout_height="@dimen/resolver_icon_size"
android:layout_gravity="start|center_vertical"
- android:layout_marginStart="?attr/listPreferredItemPaddingStart"
+ android:layout_marginStart="@dimen/resolver_icon_margin"
+ android:layout_marginEnd="@dimen/resolver_icon_margin"
android:layout_marginTop="12dp"
android:layout_marginBottom="12dp"
android:scaleType="fitCenter" />
@@ -40,8 +39,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:gravity="start|center_vertical"
android:orientation="vertical"
- android:paddingStart="?attr/listPreferredItemPaddingStart"
- android:paddingEnd="?attr/listPreferredItemPaddingEnd"
+ android:paddingEnd="@dimen/resolver_edge_margin"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="start|center_vertical">
@@ -49,14 +47,20 @@
<TextView android:id="@android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?attr/textAppearanceMedium"
- android:textColor="?attr/textColorPrimary"
+ android:layout_gravity="start|center_vertical"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@android:string/config_bodyFontFamily"
+ android:textSize="16sp"
android:minLines="1"
android:maxLines="1"
android:ellipsize="marquee" />
<!-- Extended activity info to distinguish between duplicate activity names -->
<TextView android:id="@android:id/text2"
- android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:fontFamily="@android:string/config_bodyFontFamily"
+ android:layout_gravity="start|center_vertical"
+ android:textSize="14sp"
+ android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minLines="1"
diff --git a/core/res/res/layout/resolver_different_item_header.xml b/core/res/res/layout/resolver_different_item_header.xml
index 7d9ffd72870d..0a35edc42329 100644
--- a/core/res/res/layout/resolver_different_item_header.xml
+++ b/core/res/res/layout/resolver_different_item_header.xml
@@ -22,12 +22,12 @@
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
android:text="@string/use_a_different_app"
- android:minHeight="56dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+ android:textSize="16sp"
android:gravity="start|center_vertical"
- android:paddingStart="16dp"
- android:paddingEnd="16dp"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:elevation="8dp"
- />
+ android:paddingStart="@dimen/resolver_edge_margin"
+ android:paddingEnd="@dimen/resolver_edge_margin"
+ android:paddingTop="@dimen/resolver_small_margin"
+ android:paddingBottom="@dimen/resolver_edge_margin"
+ android:elevation="1dp" />
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index 1dd420746e8a..6e45e7a4c509 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -29,16 +29,18 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
- android:elevation="8dp"
- android:background="?attr/colorBackgroundFloating">
+ android:elevation="@dimen/resolver_elevation"
+ android:paddingTop="@dimen/resolver_small_margin"
+ android:paddingStart="@dimen/resolver_edge_margin"
+ android:paddingEnd="@dimen/resolver_edge_margin"
+ android:paddingBottom="@dimen/resolver_edge_margin"
+ android:background="@drawable/bottomsheet_background">
<TextView
android:id="@+id/profile_button"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginEnd="8dp"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
android:visibility="gone"
style="?attr/borderlessButtonStyle"
android:textAppearance="?attr/textAppearanceButton"
@@ -50,36 +52,49 @@
<TextView
android:id="@+id/title"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="56dp"
- android:textAppearance="?attr/textAppearanceMedium"
- android:gravity="start|center_vertical"
- android:paddingStart="?attr/dialogPreferredPadding"
- android:paddingEnd="?attr/dialogPreferredPadding"
- android:paddingTop="8dp"
android:layout_below="@id/profile_button"
android:layout_alignParentStart="true"
- android:paddingBottom="8dp" />
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+ android:textSize="16sp"
+ android:gravity="start|center_vertical" />
</RelativeLayout>
+ <View
+ android:layout_alwaysShow="true"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical" />
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/resolver_list"
android:clipToPadding="false"
- android:scrollbarStyle="outsideOverlay"
android:background="?attr/colorBackgroundFloating"
- android:elevation="8dp"
+ android:elevation="@dimen/resolver_elevation"
android:nestedScrollingEnabled="true"
+ android:scrollbarStyle="outsideOverlay"
android:scrollIndicators="top|bottom"
- android:divider="@null" />
+ android:divider="?attr/dividerVertical"
+ android:footerDividersEnabled="false"
+ android:headerDividersEnabled="false"
+ android:dividerHeight="1dp" />
+ <View
+ android:layout_alwaysShow="true"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical" />
+
<TextView android:id="@+id/empty"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorBackgroundFloating"
- android:elevation="8dp"
+ android:elevation="@dimen/resolver_elevation"
android:layout_alwaysShow="true"
android:text="@string/noApplications"
android:padding="32dp"
@@ -102,18 +117,19 @@
android:background="?attr/colorBackgroundFloating"
android:paddingTop="@dimen/resolver_button_bar_spacing"
android:paddingBottom="@dimen/resolver_button_bar_spacing"
- android:paddingStart="12dp"
- android:paddingEnd="12dp"
- android:elevation="8dp">
+ android:paddingStart="@dimen/resolver_edge_margin"
+ android:paddingEnd="@dimen/resolver_small_margin"
+ android:elevation="@dimen/resolver_elevation">
<Button
android:id="@+id/button_once"
android:layout_width="wrap_content"
android:layout_gravity="start"
android:maxLines="2"
- style="?attr/buttonBarNegativeButtonStyle"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
+ style="?attr/buttonBarButtonStyle"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
android:layout_height="wrap_content"
+ android:textAllCaps="false"
android:enabled="false"
android:text="@string/activity_resolver_use_once"
android:onClick="onButtonClick" />
@@ -123,8 +139,9 @@
android:layout_width="wrap_content"
android:layout_gravity="end"
android:maxLines="2"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- style="?attr/buttonBarPositiveButtonStyle"
+ style="?attr/buttonBarButtonStyle"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+ android:textAllCaps="false"
android:layout_height="wrap_content"
android:enabled="false"
android:text="@string/activity_resolver_use_always"
diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml
index 740a7eb9374e..dbba0b7bcc25 100644
--- a/core/res/res/layout/resolver_list_with_default.xml
+++ b/core/res/res/layout/resolver_list_with_default.xml
@@ -29,22 +29,22 @@
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
android:orientation="vertical"
- android:background="?attr/colorBackgroundFloating"
- android:elevation="8dp">
+ android:background="@drawable/bottomsheet_background"
+ android:paddingTop="@dimen/resolver_small_margin"
+ android:elevation="@dimen/resolver_elevation">
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="64dp"
- android:orientation="horizontal">
-
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingBottom="@dimen/resolver_edge_margin"
+ android:paddingEnd="@dimen/resolver_edge_margin">
<ImageView
android:id="@+id/icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
+ android:layout_width="@dimen/resolver_icon_size"
+ android:layout_height="@dimen/resolver_icon_size"
android:layout_gravity="start|top"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="16dp"
- android:layout_marginTop="20dp"
+ android:layout_marginStart="@dimen/resolver_icon_margin"
android:src="@drawable/resolver_icon_placeholder"
android:scaleType="fitCenter" />
@@ -52,9 +52,11 @@
android:id="@+id/title"
android:layout_width="0dp"
android:layout_weight="1"
- android:layout_height="?attr/listPreferredItemHeight"
- android:layout_marginStart="16dp"
- android:textAppearance="?attr/textAppearanceMedium"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/resolver_icon_margin"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+ android:textSize="16sp"
android:gravity="start|center_vertical"
android:paddingEnd="16dp" />
@@ -107,21 +109,22 @@
android:orientation="horizontal"
android:layoutDirection="locale"
android:measureWithLargestChild="true"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:paddingStart="12dp"
- android:paddingEnd="12dp"
- android:elevation="8dp">
+ android:paddingTop="@dimen/resolver_button_bar_spacing"
+ android:paddingBottom="@dimen/resolver_button_bar_spacing"
+ android:paddingStart="@dimen/resolver_edge_margin"
+ android:paddingEnd="@dimen/resolver_small_margin"
+ android:elevation="@dimen/resolver_elevation">
<Button
android:id="@+id/button_once"
android:layout_width="wrap_content"
android:layout_gravity="start"
android:maxLines="2"
- style="?attr/buttonBarNegativeButtonStyle"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
+ style="?attr/buttonBarButtonStyle"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
android:layout_height="wrap_content"
android:enabled="false"
+ android:textAllCaps="false"
android:text="@string/activity_resolver_use_once"
android:onClick="onButtonClick" />
@@ -130,29 +133,40 @@
android:layout_width="wrap_content"
android:layout_gravity="end"
android:maxLines="2"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- style="?attr/buttonBarPositiveButtonStyle"
+ style="?attr/buttonBarButtonStyle"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
android:layout_height="wrap_content"
android:enabled="false"
+ android:textAllCaps="false"
android:text="@string/activity_resolver_use_always"
android:onClick="onButtonClick" />
</LinearLayout>
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="?attr/dividerVertical" />
</LinearLayout>
+ <View
+ android:layout_alwaysShow="true"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical" />
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/resolver_list"
android:clipToPadding="false"
- android:scrollbarStyle="outsideOverlay"
android:background="?attr/colorBackgroundFloating"
- android:elevation="8dp"
+ android:elevation="@dimen/resolver_elevation"
android:nestedScrollingEnabled="true"
- android:divider="@null" />
-
+ android:scrollbarStyle="outsideOverlay"
+ android:scrollIndicators="top|bottom"
+ android:divider="?attr/dividerVertical"
+ android:footerDividersEnabled="false"
+ android:headerDividersEnabled="false"
+ android:dividerHeight="1dp" />
+ <View
+ android:layout_alwaysShow="true"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical" />
</com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index a44b137ab93d..4ca0df4a4a0e 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -295,7 +295,7 @@
<string name="permgrouprequest_sms" msgid="7168124215838204719">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om SMS\'e te stuur en te bekyk?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Berging"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"toegang te verkry tot foto\'s, media en lêers op jou toestel"</string>
- <string name="permgrouprequest_storage" msgid="7885942926944299560">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot jou foto\'s, media en lêers op jou toestel?"</string>
+ <string name="permgrouprequest_storage" msgid="7885942926944299560">"Gee vir &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot jou foto\'s, media en lêers op jou toestel?"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofoon"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"oudio op te neem"</string>
<string name="permgrouprequest_microphone" msgid="9167492350681916038">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om oudio op te neem?"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Laat die program toe om SMS-boodskappe te ontvang en te verwerk. Dit beteken dat die program boodskappe wat na jou toestel gestuur is, kan monitor of uitvee, sonder dat jy dit gesien het."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"ontvang teksboodskappe (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Laat die program toe om MMS-boodskappe te ontvang en te verwerk. Dit beteken dat die program boodskappe wat na jou toestel gestuur is kan monitor of uitvee, sonder dat jy dit gesien het."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Stuur seluitsendingboodskappe aan"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Laat die program toe om die seluitsendingmodule te bind om seluitsendingboodskappe aan te stuur wanneer hulle ontvang word. Seluitsendingwaarskuwings word in sommige liggings gelewer om jou oor noodsituasies te waarsku. Kwaadwillige programme kan met die werkverrigting of werking van jou toestel inmeng wanneer \'n noodseluitsending ontvang word."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"lees seluitsending-boodskappe"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Laat die program toe om seluitsending-boodskappe te lees wat deur jou toestel ontvang word. Seluitsending-waarskuwings word in sommige plekke afgelewer om jou van noodsituasies te waarsku. Kwaadwillige programme mag inmeng met die prestasie of die werking van jou toestel wanneer \'n noodgeval se seluitsending ontvang word."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"lees ingetekende nuus"</string>
@@ -1075,7 +1077,7 @@
<string name="Midnight" msgid="5630806906897892201">"Middernag"</string>
<string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<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="1846071997616654124">"<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="6876518925844129331">"Kies alle"</string>
+ <string name="selectAll" msgid="6876518925844129331">"Kies alles"</string>
<string name="cut" msgid="3092569408438626261">"Sny"</string>
<string name="copy" msgid="2681946229533511987">"Kopieer"</string>
<string name="failed_to_copy_to_clipboard" msgid="1833662432489814471">"Kon nie na knipbord toe kopieer nie"</string>
@@ -1356,11 +1358,13 @@
<string name="usb_power_notification_message" msgid="4647527153291917218">"Laai tans gekoppelde toestel. Tik vir meer opsies."</string>
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Analoë oudiobykomstigheid bespeur"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Die aangehegde toestel is nie met hierdie foon versoenbaar nie. Tik om meer te wete te kom."</string>
- <string name="adb_active_notification_title" msgid="6729044778949189918">"USB-ontfouter gekoppel"</string>
+ <string name="adb_active_notification_title" msgid="6729044778949189918">"USB-ontfouting gekoppel"</string>
<string name="adb_active_notification_message" msgid="7463062450474107752">"Tik om USB-ontfouting af te skakel"</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Kies om USB-ontfouting te deaktiveer."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Toetsraamwerkmodus is geaktiveer"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Voer \'n fabriekterugstelling uit om Toetsraamwerkmodus te deaktiveer."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Reekskonsole is geaktiveer"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Werkverrigting word beïnvloed. Gaan selflaaiprogram na om te deaktiveer."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Vloeistof of vuilgoed in USB-poort"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB-poort is outomaties gedeaktiveer. Tik om meer te wete te kom."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"OK om USB-poort te gebruik"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Ongekategoriseer"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Jy stel die belangrikheid van hierdie kennisgewings."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Dit is belangrik as gevolg van die mense wat betrokke is."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Laat <xliff:g id="APP">%1$s</xliff:g> toe om \'n nuwe gebruiker met <xliff:g id="ACCOUNT">%2$s</xliff:g> te skep (\'n gebruiker met hierdie rekening bestaan reeds)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Laat <xliff:g id="APP">%1$s</xliff:g> toe om \'n nuwe gebruiker met <xliff:g id="ACCOUNT">%2$s</xliff:g> te skep?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Voeg \'n taal by"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Streekvoorkeur"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Voer taalnaam in"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index db758e3e45f8..eb5265ed0e61 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"መተግበሪያው የኤስ.ኤም.ኤስ. መልዕክቶችን እንዲያነብ እና እንዲያካሂድ ይፈቅዳል። ይህ ማለት መተግበሪያው ወደ መሳሪያህ የተላኩ መልዕክቶችን ላንተ ሳያሳይህ ሊቆጣጠር ወይም ሊሰርዝ ይችላል።"</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"የፅሁፍ መልዕክቶችን ተቀበል (ኤም.ኤም.ኤስ.)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"መተግበሪያው የኤም.ኤም.ኤስ. መልዕክቶችን እንዲያነብ እና እንዲያካሂድ ይፈቅዳል። ይህ ማለት መተግበሪያው ወደ መሳሪያህ የተላኩ መልዕክቶችን ላንተ ሳያሳይህ ሊቆጣጠር ወይም ሊሰርዝ ይችላል።"</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"የሕዋስ ስርጭት መልዕክቶችን ማስተላለፍ"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"የሕዋስ ስርጭት መልዕክቶች እንደመጡ ለማስተላለፍ መተግበሪያው ከሕዋስ ስርጭት ሞዱሉ ጋር እንዲተሳሰር ያስችለዋል። የሕዋስ ስርጭት ማንቂያዎች አስቸኳይ ሁኔታዎች ሲያጋጥሙ አንዳንድ አካባቢዎች ላይ የሚላኩ ናቸው። የሕዋስ ስርጭት ሲደርስ ተንኮል-አዘል መተግበሪያዎች በመሣሪያዎ አፈጻጸም ወይም አሰራር ላይ ጣልቃ ሊገቡ ይችላሉ።"</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"የህዋስ ስርጭት መልዕክቶችን አንብብ"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"መሣሪያህ የህዋስ ስርጭት መልዕክቶች ሲቀበል መተግበሪያው እንዲያነበው ይፈቅድለታል። የህዋስ ስርጭት ማንቂያዎች አስቸኳይ ሁኔታዎች ሲያጋጥሙ አንዳንድ አካባቢዎች ላይ የሚላኩ ናቸው። የህዋስ ስርጭት ሲደርስ ተንኮል አዘል መተግበሪያዎች በመሣሪያህ አፈጻጸም ወይም አሰራር ላይ ጣልቃ ሊገቡ ይችላሉ።"</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"የምዝገባ መግቦች አንበብ"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB ማረሚያ ላለማንቃት ምረጥ።"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"የሙከራ ጥቅል ሁነታ ነቅቷል"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"የመሞከሪያ ጥቅል ሁነታን ለማሰናከል የፋብሪካ ዳግም ቅንብርን ይሞክሩ።"</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"ተከታታይ ኮንሶል ነቅቷል"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"አፈጻጸም ተጽዕኖ አርፎበታል። ለማሰናከል፣ bootloader ን ይፈትሹ።"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"በዩኤስቢ ወደብ ውስጥ ፈሳሽ ወይም ፍርስራሽ"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"የዩኤስቢ ወደብ በራስ-ሰር ተሰናክሏል። የበለጠ ለመረዳት መታ ያድርጉ።"</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"የዩኤስቢ ወደቡን መጠቀም ችግር የለውም"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"ያልተመደቡ"</string>
<string name="importance_from_user" msgid="7318955817386549931">"የእነዚህን ማሳወቂያዎች አስፈላጊነት አዘጋጅተዋል።"</string>
<string name="importance_from_person" msgid="9160133597262938296">"ይሄ በሚሳተፉ ሰዎች ምክንያት አስፈላጊ ነው።"</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="APP">%1$s</xliff:g> በ<xliff:g id="ACCOUNT">%2$s</xliff:g> አዲስ ተጠቃሚ እንዲፈጥር ይፈቀድለት (ይህ መለያ ያለው ተጠቃሚ አስቀድሞ አለ)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"<xliff:g id="APP">%1$s</xliff:g> አዲስ ተጠቃሚ ከ <xliff:g id="ACCOUNT">%2$s</xliff:g> ጋር መፍጠር እንዲችል ይፍቀዱ?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"ቋንቋ ያክሉ"</string>
<string name="country_selection_title" msgid="2954859441620215513">"የክልል ምርጫ"</string>
<string name="search_language_hint" msgid="7042102592055108574">"የቋንቋ ስም ይተይቡ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 113a3426cf5f..c9067e5d26cb 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -356,6 +356,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"‏للسماح للتطبيق بتلقي ومعالجة الرسائل القصيرة SMS. وهذا يعني أنه يمكن للتطبيق مراقبة الرسائل التي يتم إرسالها إلى جهازك أو حذفها بدون عرضها لك."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"تلقي رسائل نصية (رسائل وسائط متعددة)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"للسماح للتطبيق بتلقي ومعالجة رسائل الوسائط المتعددة. وهذا يعني أنه يمكن للتطبيق مراقبة الرسائل التي يتم إرسالها لجهازك أو حذفها بدون عرضها لك."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"إعادة توجيه رسائل البث الخلوي"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"يسمح للتطبيق بالارتباط بوحدة البث الخلوي لإعادة توجيه رسائل البث الخلوي بينما يتم استقبالها. ويتم تسليم تنبيهات البث الخلوي في بعض المواقع لتحذيرك في حالات الطوارئ. ويمكن أن تؤثر التطبيقات الضارة على أداء الجهاز أو تشغيله عندما يتم تلقي بث خلوي في حالات الطوارئ."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"قراءة رسائل بث الخلية"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"السماح للتطبيق بقراءة رسائل بث الخلية التي يتلقاها هذا الجهاز. يتم تسليم اشعارات بث الخلية في بعض المواقع لتحذيرك من حالات طارئة. يمكن أن تتداخل التطبيقات الضارة مع أداء أو تشغيل الجهاز عندما يتم تلقي بث خلية طارئ."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"قراءة الخلاصات المشتركة"</string>
@@ -526,7 +528,7 @@
<string name="permdesc_useBiometric" msgid="8389855232721612926">"للسماح للتطبيق باستخدام الأجهزة البيومترية للمصادقة"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"لإدارة أجهزة بصمة الإصبع"</string>
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"للسماح للتطبيق باستدعاء طرق لإضافة نماذج من بصمات الأصابع وحذفها."</string>
- <string name="permlab_useFingerprint" msgid="3150478619915124905">"لاستخدام أجهزة بصمة الإصبع"</string>
+ <string name="permlab_useFingerprint" msgid="3150478619915124905">"استخدام أجهزة بصمة الإصبع"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"للسماح للتطبيق باستخدام أجهزة بصمة الإصبع للمصادقة"</string>
<string name="permlab_audioWrite" msgid="2661772059799779292">"تعديل مجموعتك الموسيقية"</string>
<string name="permdesc_audioWrite" msgid="8888544708166230494">"للسماح للتطبيق بتعديل مجموعتك الموسيقية."</string>
@@ -536,7 +538,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"للسماح للتطبيق بتعديل مجموعة صورك."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"قراءة المواقع من مجموعة الوسائط التابعة لك"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"للسماح للتطبيق بقراءة المواقع من مجموعة الوسائط التابعة لك."</string>
- <string name="biometric_dialog_default_title" msgid="881952973720613213">"التحقق من هويتك"</string>
+ <string name="biometric_dialog_default_title" msgid="881952973720613213">"إثبات هويتك"</string>
<string name="biometric_error_hw_unavailable" msgid="645781226537551036">"معدّات المقاييس الحيوية غير متاحة."</string>
<string name="biometric_error_user_canceled" msgid="2260175018114348727">"تم إلغاء المصادقة."</string>
<string name="biometric_not_recognized" msgid="5770511773560736082">"لم يتم التعرف عليها."</string>
@@ -1155,7 +1157,7 @@
<string name="Midnight" msgid="5630806906897892201">"منتصف الليل"</string>
<string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<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="1846071997616654124">"<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="6876518925844129331">"تحديد الكل"</string>
+ <string name="selectAll" msgid="6876518925844129331">"اختيار الكل"</string>
<string name="cut" msgid="3092569408438626261">"قص"</string>
<string name="copy" msgid="2681946229533511987">"نسخ"</string>
<string name="failed_to_copy_to_clipboard" msgid="1833662432489814471">"تعذّر النسخ في الحافظة"</string>
@@ -1444,11 +1446,13 @@
<string name="usb_power_notification_message" msgid="4647527153291917218">"جارٍ شحن الجهاز المتصل. انقر لعرض خيارات أكثر."</string>
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"تم اكتشاف ملحق صوتي تناظري"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"الجهاز الذي تم توصيله بالهاتف غير متوافق معه. انقر للحصول على المزيد من المعلومات."</string>
- <string name="adb_active_notification_title" msgid="6729044778949189918">"‏تم توصيل تصحيح أخطاء USB"</string>
- <string name="adb_active_notification_message" msgid="7463062450474107752">"‏انقر لإيقاف تصحيح أخطاء USB."</string>
+ <string name="adb_active_notification_title" msgid="6729044778949189918">"‏تم توصيل تصحيح أخطاء الجهاز عبر USB"</string>
+ <string name="adb_active_notification_message" msgid="7463062450474107752">"‏انقر لإيقاف تصحيح أخطاء الجهاز عبر USB."</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"‏اختيار إيقاف تصحيح أخطاء USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"تم تفعيل وضع \"مفعّل الاختبار\""</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"يمكنك إجراء إعادة ضبط على إعدادات المصنع لإيقاف وضع \"مفعِّل اختبار\"."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"وحدة التحكّم التسلسلية مفعّلة"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"الأداء متأثر. لإيقاف وحدة التحكّم، تحقّق من برنامج الإقلاع."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"‏السوائل والشوائب في منفذ USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"‏تمّ إيقاف منفذ USB تلقائيًا. انقُر لمعرفة المزيد من المعلومات."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"‏مسموح باستخدام منفذ USB"</string>
@@ -1671,8 +1675,8 @@
<string name="expires_on" msgid="3676242949915959821">"تنتهي الصلاحية في:"</string>
<string name="serial_number" msgid="758814067660862493">"الرقم المسلسل:"</string>
<string name="fingerprints" msgid="4516019619850763049">"بصمات الأصابع:"</string>
- <string name="sha256_fingerprint" msgid="4391271286477279263">"‏بصمة أصبع SHA-256:"</string>
- <string name="sha1_fingerprint" msgid="7930330235269404581">"‏بصمة أصبع SHA-1:"</string>
+ <string name="sha256_fingerprint" msgid="4391271286477279263">"‏بصمة إصبع SHA-256:"</string>
+ <string name="sha1_fingerprint" msgid="7930330235269404581">"‏بصمة إصبع SHA-1:"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"عرض الكل"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"اختيار نشاط"</string>
<string name="share_action_provider_share_with" msgid="5247684435979149216">"مشاركة مع"</string>
@@ -2026,10 +2030,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"غير مصنفة"</string>
<string name="importance_from_user" msgid="7318955817386549931">"لقد عيَّنت أهمية هذه الإشعارات."</string>
<string name="importance_from_person" msgid="9160133597262938296">"هذه الرسالة مهمة نظرًا لأهمية الأشخاص المعنيين."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"هل تسمح لتطبيق <xliff:g id="APP">%1$s</xliff:g> بإنشاء مستخدم جديد باستخدام <xliff:g id="ACCOUNT">%2$s</xliff:g> (يوجد مستخدم بهذا الحساب مسبقًا)؟"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"هل تسمح لتطبيق <xliff:g id="APP">%1$s</xliff:g> بإنشاء مستخدم جديد باستخدام <xliff:g id="ACCOUNT">%2$s</xliff:g> ؟"</string>
<string name="language_selection_title" msgid="2680677278159281088">"إضافة لغة"</string>
<string name="country_selection_title" msgid="2954859441620215513">"تفضيل المنطقة"</string>
<string name="search_language_hint" msgid="7042102592055108574">"اكتب اسم اللغة"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 00617a548082..7a89b654da19 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"এপটোক এছএমএছ বাৰ্তাবোৰ পাবলৈ আৰু প্ৰক্ৰিয়া সম্পন্ন কৰিবলৈ অনুমতি দিয়ে৷ ইয়াৰ অৰ্থ এইটোৱেই যে এপটোৱে আপোনাক বাৰ্তাবোৰ নেদেখুৱাকৈয়ে আপোনাৰ ডিভাইচলৈ পঠিওৱা বাৰ্তাবোৰ নিৰীক্ষণ কৰিব বা মচিব পাৰে৷"</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"পাঠ বার্তা (এমএমএছ) বোৰ লাভ কৰক"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"এমএমএছ বার্তাবোৰ লাভ আৰু ইয়াৰ প্ৰক্ৰিয়া সম্পন্ন কৰিবলৈ এপক অনুমতি দিয়ে। ইয়াৰ অৰ্থ হৈছে এই এপে আপোনাৰ ডিভাইচলৈ প্ৰেৰণ কৰা বার্তাসমূহ আপোনাক নেদেখুৱাকৈয়ে পৰ্যবেক্ষণ আৰু মচিব পাৰে।"</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"চেল সম্প্ৰচাৰ বাৰ্তাসমূহ ফৰৱাৰ্ড কৰক"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"চেল সম্প্ৰচাৰ বাৰ্তাসমূহ লাভ কৰিলে সেইবোৰ ফৰৱাৰ্ড কৰিবলৈ এপ্‌টোক চেল সম্প্ৰচাৰ মডিউলটোৰ সৈতে সংযুক্ত হ\'বলৈ অনুমতি দিয়ে। আপোনাক জৰুৰীকালীন পৰিস্থিতিসমূহৰ বিষয়ে সতৰ্ক কৰিবলৈ কিছুমান অৱস্থানত চেল সম্প্ৰচাৰ সতৰ্কবাৰ্তাসমূহ ডেলিভাৰ কৰা হয়। কোনো জৰুৰীকালীন চেল সম্প্ৰচাৰ লাভ কৰিলে ক্ষতিকাৰক এপ্‌সমূহে আপোনাৰ ডিভাইচটোৰ কাৰ্যক্ষমতা অথবা কাৰ্যপ্ৰণালীত হস্তক্ষেপ কৰিব পাৰে।"</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"চেল সম্প্ৰচাৰৰ বার্তাবোৰ পঢ়ক"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"আপোনাৰ ডিভাইচে লাভ কৰা চেল সম্প্ৰচাৰৰ বার্তাবোৰ পঢ়িবলৈ এপক অনুমতি দিয়ে। আপোনাক জৰুৰীকালীন পৰিস্থিতিবোৰত সর্তক কৰিবলৈ চেল সম্প্ৰচাৰৰ বার্তাবোৰ প্ৰেৰণ কৰা হয়। জৰুৰীকালীন চেল সম্প্ৰচাৰ লাভ কৰাৰ সময়ত আপোনাৰ ডিভাইচৰ কাৰ্যদক্ষতা বা কাৰ্যপ্ৰণালীত ক্ষতিকাৰক এপবোৰে হস্তক্ষেপ কৰিব পাৰে।"</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"আপুনি সদস্যভুক্ত হোৱা ফীডসমূহ পঢ়ক"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"ইউএছবি ডিবাগিং অক্ষম কৰিবলৈ বাছনি কৰক।"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"টেষ্ট হাৰনেছ ম’ড সক্ষম কৰা আছে"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"টেষ্ট হাৰনেছ ম’ড অক্ষম কৰিবলৈ ফেক্টৰী ৰিছেট কৰক।"</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"ক্ৰমিক কনছ’ল সক্ষম কৰা আছে"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"কাৰ্যক্ষমতা প্ৰভাৱিত হৈছে। অক্ষম কৰিবলৈ বুটল’ডাৰ পৰীক্ষা কৰক।"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"ইউএছবি প’ৰ্টত তৰল বা ধূলি-মাকতি আছে"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"ইউএছবি প’ৰ্ট স্বয়ংক্ৰিয়ভাৱে অক্ষম কৰা হয়। অধিক জানিবৰ বাবে টিপক।"</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"ইউএছবি প’ৰ্ট ব্যৱহাৰ কৰাত সমস্যা নাই"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"শ্ৰেণীবদ্ধ নকৰা"</string>
<string name="importance_from_user" msgid="7318955817386549931">"এই জাননীবোৰৰ গুৰুত্ব আপুনি ছেট কৰব লাগিব।"</string>
<string name="importance_from_person" msgid="9160133597262938296">"এই কার্যৰ সৈতে জড়িত থকা লোকসকলক ভিত্তি কৰি এইয়া গুৰুত্বপূর্ণ বুলি বিবেচনা কৰা হৈছ।"</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="APP">%1$s</xliff:g>ক <xliff:g id="ACCOUNT">%2$s</xliff:g>ৰ (এই একাউণ্টটোৰ এজন ব্যৱহাৰকাৰী ইতিমধ্যে আছে) জৰিয়তে এজন নতুন ব্যৱহাৰকাৰী সৃষ্টি কৰিবলৈ অনুমতি দিবনে ?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"<xliff:g id="APP">%1$s</xliff:g>ক <xliff:g id="ACCOUNT">%2$s</xliff:g>ৰ জৰিয়তে এজন নতুন ব্যৱহাৰকাৰী সৃষ্টি কৰিবলৈ অনুমতি দিবনে?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"ভাষা যোগ কৰক"</string>
<string name="country_selection_title" msgid="2954859441620215513">"অঞ্চলৰ অগ্ৰাধিকাৰ"</string>
<string name="search_language_hint" msgid="7042102592055108574">"ভাষাৰ নাম লিখক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index f79decd068d3..21dbf03eaaff 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Tətbiqə MMS mesajlarını almaq və emal etmək icazəsi verir. Bu o deməkdir ki, tətbiq sizin mesajlarınızı sizə göstərmədən monitorinq edə və ya silə bilər."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"mətn mesajlarını qəbul edir (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Tətbiqə MMS mesajlarını qəbul və emal üçün imkan verir. Bu o deməkdir ki, bu tətbiq sizə göstərmədən cihazınıza göndərilən mesajları silə bilər."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Şəbəkə yayımı mesajlarını yönləndirin"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Tətbiqə şəbəkə yayım mesajlarını əldə edildiyi anda yönləndirmək üçün şəbəkə yayımı moduluna bağlanmaq icazəsi verir. Şəbəkə yayımı bəzi məkanlarda olan fövqəladə hadisələrlə bağlı Sizi xəbərdar etmək üçün qəbul edilir. Zərərli tətbiqlər fövqəladə şəbəkə yayımı əldə edildiyi zaman cihazın performansına və əməliyyatına müdaxilə edə bilər."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"mobil yayım mesajlarını oxuyur"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Tətbiqə telefonunuz tərəfindən alınmış yayım mesajlarını oxuma icazəsi verir. Telefon yayımı bəzi məkanlarda olan fövqəladə hadisələrlə bağlı sizi xəbərdar etmək üçün qəbul edilir. Zərərli tətbiqlər təcili mobil yayım qəbul edildiyi zaman telefonunun performansına və əməliyyatına müdaxilə edə bilər."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"abunə olunmuş xəbərləri oxuyur"</string>
@@ -530,7 +532,7 @@
<string name="biometric_not_recognized" msgid="5770511773560736082">"Tanınmır"</string>
<string name="biometric_error_canceled" msgid="349665227864885880">"Doğrulama ləğv edildi"</string>
<string name="biometric_error_device_not_secured" msgid="6583143098363528349">"Pin, nümunə və ya parol ayarlanmayıb"</string>
- <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Barmaq qismən müəyyən olundu. Lütfən, yenidən cəhd edin."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Barmaq izi yarımçıq müəyyən olundu. Lütfən, yenidən cəhd edin."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Barmaq izi tanınmadı. Lütfən, yenidən cəhd edin."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Barmaq izi sensoru çirklidir. Lütfən, təmizləyin və yenidən cəhd edin."</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Barmağı çox tez tərpətdiniz. Yenidən edin."</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USb debaqı deaktivasiya etməyi seçin."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Test Rejimi aktivdir"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Test Rejimini deaktiv etmək üçün fabrika ayarlarına sıfırlayın."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Ardıcıl konsol aktiv edildi"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Performansa təsir edir. Deaktiv etmək üçün yükləyicini yoxlayın."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB portuna maye sızıb və ya qırılıb"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB portu avtomatik deaktiv edildi. Ətraflı məlumat üçün klikləyin."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB portundan istifadə etmək üçün OK"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Kateqoriyasız"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Bildirişlərin əhəmiyyətini Siz ayarlaryırsınız."</string>
<string name="importance_from_person" msgid="9160133597262938296">"İnsanlar cəlb olunduğu üçün bu vacibdir."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="APP">%1$s</xliff:g> tətbiqinə <xliff:g id="ACCOUNT">%2$s</xliff:g> (artıq bu hesabı olan İstifadəçi mövcuddur) ilə yeni İstifadəçi yaratmağa icazə verilsin?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"<xliff:g id="APP">%1$s</xliff:g> tətbiqinə <xliff:g id="ACCOUNT">%2$s</xliff:g> ilə yeni İstifadəçi yartmağa icazə verilsin?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Dil əlavə edin"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Region seçimi"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Dil adını daxil edin"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 10dc339ae8ff..dbb788dd2513 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -27,7 +27,7 @@
<string name="terabyteShort" msgid="231613018159186962">"TB"</string>
<string name="petabyteShort" msgid="5637816680144990219">"PB"</string>
<string name="fileSizeSuffix" msgid="8897567456150907538">"<xliff:g id="NUMBER">%1$s</xliff:g> <xliff:g id="UNIT">%2$s</xliff:g>"</string>
- <string name="untitled" msgid="4638956954852782576">"&lt;Bez naslova&gt;"</string>
+ <string name="untitled" msgid="4638956954852782576">"&lt;Bez imena&gt;"</string>
<string name="emptyPhoneNumber" msgid="7694063042079676517">"(Nema broja telefona)"</string>
<string name="unknownName" msgid="6867811765370350269">"Nepoznato"</string>
<string name="defaultVoiceMailAlphaTag" msgid="2660020990097733077">"Glasovna pošta"</string>
@@ -347,6 +347,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Dozvoljava aplikaciji da prima i obrađuje SMS poruke. To znači da aplikacija može da nadgleda ili briše poruke koje se šalju uređaju, a da vam ih ne prikaže."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"prijem tekstualnih poruka (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Dozvoljava aplikaciji da prima i obrađuje MMS poruke. To znači da aplikacija može da nadgleda ili briše poruke koje se šalju uređaju, a da vam ih ne prikaže."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Prosleđivanje poruka za mobilne uređaje na lokalitetu"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Dozvoljava aplikaciji da se vezuje za modul poruka za mobilne uređaje na lokalitetu da bi prosleđivala poruke za mobilne uređaje na lokalitetu onako kako su primljene. Obaveštenja poruka za mobilne uređaje na lokalitetu se na nekim lokacijama primaju kao upozorenja na hitne slučajeve. Zlonamerne aplikacije mogu da utiču na učinak ili ometaju rad uređaja kada se primi poruka o hitnom slučaju za mobilne uređaje na lokalitetu."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"čitanje poruka info servisa"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Omogućava aplikaciji da čita poruke info servisa koje uređaj prima. Upozorenja info servisa se na nekim lokacijama primaju kao upozorenja na hitne slučajeve. Zlonamerne aplikacije mogu da utiču na učinak ili ometaju funkcionisanje uređaja kada se primi poruka info servisa o hitnom slučaju."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"čitanje prijavljenih fidova"</string>
@@ -1383,6 +1385,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Izaberite da biste onemogućili otklanjanja grešaka sa USB-a."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Omogućen je režim probnog korišćenja"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Obavite resetovanje na fabrička podešavanja da biste onemogućili režim probnog korišćenja."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Serijska konzola je omogućena"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Učinak je smanjen. Da biste onemogući konzolu, proverite pokretački program."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Tečnost ili nečistoća u USB portu"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB port je automatski isključen. Dodirnite da biste saznali više."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Korišćenje USB porta je dozvoljeno"</string>
@@ -1602,8 +1606,8 @@
<string name="expires_on" msgid="3676242949915959821">"Ističe:"</string>
<string name="serial_number" msgid="758814067660862493">"Serijski broj:"</string>
<string name="fingerprints" msgid="4516019619850763049">"Digitalni otisci:"</string>
- <string name="sha256_fingerprint" msgid="4391271286477279263">"SHA-256 digitalni otisak:"</string>
- <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 digitalni otisak:"</string>
+ <string name="sha256_fingerprint" msgid="4391271286477279263">"SHA-256 otisak prsta:"</string>
+ <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 otisak prsta:"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Prikaži sve"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Izbor aktivnosti"</string>
<string name="share_action_provider_share_with" msgid="5247684435979149216">"Deli sa"</string>
@@ -1924,10 +1928,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Nekategorizovano"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Vi podešavate važnost ovih obaveštenja."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Ovo je važno zbog ljudi koji učestvuju."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Želite li da dozvolite da <xliff:g id="APP">%1$s</xliff:g> napravi novog korisnika sa nalogom <xliff:g id="ACCOUNT">%2$s</xliff:g> (korisnik sa tim nalogom već postoji)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Želite li da dozvolite da <xliff:g id="APP">%1$s</xliff:g> napravi novog korisnika sa nalogom <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Dodajte jezik"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Podešavanje regiona"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Unesite naziv jezika"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index ce65bead0fd1..bae2640bb893 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -330,7 +330,7 @@
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Кіраваць маштабам дысплэя і пазіцыянаваннем."</string>
<string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Выконваць жэсты"</string>
<string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Можна кранаць, праводзіць пальцам, маштабаваць шчыпком, а таксама выконваць іншыя жэсты."</string>
- <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"Жэсты адбіткаў пальцаў"</string>
+ <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"Жэсты на сканеры адбіткаў пальцаў"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"Можа распазнаваць жэсты на сканеры адбіткаў пальцаў прылады."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"адключаць ці змяняць радок стану"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Дазваляе прыкладанням адключаць радок стану або дадаваць і выдаляць сістэмныя значкі."</string>
@@ -350,6 +350,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Дазваляе прыкладанням атрымліваць і апрацоўваць SMS-паведамленні. Гэта значыць, што прыкладанне можа кантраляваць або выдаляць паведамленні, адпраўленыя на прыладу, не паказваючы іх вам."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"атрыманне тэкставых паведамленняў (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Дазваляе прыкладанням атрымліваць і апрацоўваць MMS-паведамленнi. Гэта значыць, што прыкладанне можа кантраляваць або выдаляць паведамленні, адпраўленыя на прыладу, не паказваючы іх вам."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Пераадрасоўваць паведамленні сотавай трансляцыі"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Дазваляе праграме звязвацца з модулем сотавай трансляцыі, каб пераадрасоўваць атрыманыя там паведамленні. Абвесткі сотавай трансляцыі дасылаюцца ў некаторыя месцы, каб папярэджваць вас пра надзвычайныя сітуацыі. Шкодныя праграмы могуць негатыўна ўплываць на прадукцыйнасць або працу прылады падчас атрымання паведамленняў сотавай трансляцыі пра надзвычайныя сітуацыі."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"чытаць паведамленні базавай станцыі"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Дазваляе прыкладанню чытаць паведамленні базавай станцыі, атрыманыя прыладай. Папярэджанні базавай станцыі дасылаюцца ў некаторыя месцы, каб папярэдзіць вас аб надзвычайных сітуацыях. Шкоднасныя прыкладанні могуць уплываць на прадукцыйнасць ці працу прылады пры атрыманні паведамлення базавай станцыі аб надзвычайнай сітуацыі."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"чытаць падпісаныя каналы"</string>
@@ -536,7 +538,7 @@
<string name="biometric_not_recognized" msgid="5770511773560736082">"Не распазнана"</string>
<string name="biometric_error_canceled" msgid="349665227864885880">"Аўтэнтыфікацыя скасавана"</string>
<string name="biometric_error_device_not_secured" msgid="6583143098363528349">"Не заданы PIN-код, узор разблакіроўкі або пароль"</string>
- <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Выяўлена частка адбіткаў пальцаў. Паспрабуйце яшчэ раз."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Не ўвесь адбітак пальца адсканіраваны. Паспрабуйце яшчэ раз."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Не атрымалася апрацаваць адбітак пальца. Паспрабуйце яшчэ раз."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Датчык адбіткаў пальцаў брудны. Ачысціце яго і паспрабуйце яшчэ раз."</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Палец рухаўся занадта хутка. Паспрабуйце яшчэ раз."</string>
@@ -547,12 +549,12 @@
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Твар распазнаны"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Твар распазнаны. Націсніце, каб пацвердзіць"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Апаратныя сродкі адбіткаў пальцаў недаступныя."</string>
- <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Адбіткі пальцаў нельга захаваць. Выдаліце існы адбітак."</string>
- <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Час чакання адбіткаў пальцаў выйшаў. Паспрабуйце яшчэ раз."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Адбіткі пальцаў нельга захаваць. Выдаліце існуючы адбітак."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Час чакання выйшаў. Паспрабуйце яшчэ раз."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Аперацыя з адбіткамі пальцаў скасавана."</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Аўтэнтыфікацыя па адбітках пальцаў скасавана карыстальнікам."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Занадта шмат спроб. Паспрабуйце яшчэ раз пазней."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Занадта шмат спроб. Сканер адбіткаў пальцаў адключаны."</string>
+ <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Занадта шмат спроб. Сканер адбіткаў пальцаў выключаны."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Паспрабуйце яшчэ раз."</string>
<string name="fingerprint_error_no_fingerprints" msgid="7654382120628334248">"Адбіткі пальцаў не зарэгістраваны."</string>
<string name="fingerprint_error_hw_not_present" msgid="409523969613176352">"На гэтай прыладзе няма сканера адбіткаў пальцаў."</string>
@@ -1405,6 +1407,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Выберыце, каб адключыць адладку USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Тэставы рэжым уключаны"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Каб выключыць тэставы рэжым, скіньце налады да заводскіх значэнняў."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Паслядоўная кансоль уключана"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Паказчык эфектыўнасці змяніўся. Каб выключыць кансоль, праверце загрузчык."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Вадкасць або смецце ў порце USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Порт USB аўтаматычна адключаны. Каб даведацца больш, націсніце тут."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Порт USB можна выкарыстоўваць"</string>
@@ -1625,7 +1629,7 @@
<string name="expires_on" msgid="3676242949915959821">"Заканчваецца:"</string>
<string name="serial_number" msgid="758814067660862493">"Серыйны нумар:"</string>
<string name="fingerprints" msgid="4516019619850763049">"Адбіткі пальцаў:"</string>
- <string name="sha256_fingerprint" msgid="4391271286477279263">"Адбітак пальцаў SHA-256:"</string>
+ <string name="sha256_fingerprint" msgid="4391271286477279263">"Адбітак SHA-256:"</string>
<string name="sha1_fingerprint" msgid="7930330235269404581">"Адбіткі пальцаў SHA-1:"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Прагледзець усё"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Выберыце працэс"</string>
@@ -1958,10 +1962,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Некатэгарызаванае"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Вы задалі важнасць гэтых апавяшчэнняў."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Гэта важна, бо з гэтым звязаны пэўныя людзі."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Дазволіць праграме \"<xliff:g id="APP">%1$s</xliff:g>\" стварыць новага Карыстальніка з уліковым запісам <xliff:g id="ACCOUNT">%2$s</xliff:g> (Карыстальнік з гэтым уліковым запісам ужо існуе)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Дазволіць праграме \"<xliff:g id="APP">%1$s</xliff:g>\" стварыць новага Карыстальніка з уліковым запісам <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Дадаць мову"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Параметры рэгіёна"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Увядзіце назву мовы"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 87ea6de20162..e7b309028cce 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Разрешава на приложението да получава и обработва SMS съобщения. Това означава, че то може да наблюдава или изтрива изпратените до устройството ви, без да ви ги покаже."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"получаване на текстови съобщения (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Разрешава на приложението да получава и обработва MMS съобщения. Това означава, че то може да наблюдава или изтрива изпратените до устройството ви, без да ви ги покаже."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Препращане на съобщения с клетъчно излъчване"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Разрешава на приложението да се обвърже с модула за клетъчно излъчване, за да препраща получените съобщения с клетъчно излъчване. Сигналите с клетъчно излъчване се получават на някои местоположения, за да ви предупредят за спешни случаи. Злонамерените приложения могат да възпрепятстват изпълнението или работата на устройството ви при получаване на такова спешно излъчване."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"четене на съобщения с клетъчно излъчване"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Разрешава на приложението да чете съобщения с клетъчно излъчване, получени от устройството ви. Сигналите с клетъчно излъчване се получават на някои местоположения, за да ви предупредят за спешни ситуации. Злонамерените приложения могат да възпрепятстват изпълнението или работата на устройството ви при получаване на такова спешно излъчване."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"четене на емисиите с абонамент"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Изберете, за да деактивирате отстраняването на грешки през USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Режимът за тестова среда е активиран"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Възстановете фабричните настройки, за да деактивирате режима за тестова среда."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Серийната конзола е активирана"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Ефективността е засегната. За да деактивирате, проверете програмата за първоначално зареждане."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Течност или замърсяване в USB порта"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB портът е деактивиран автоматично. Докоснете, за да научите повече."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Можете да използвате USB порта"</string>
@@ -1579,8 +1583,8 @@
<string name="expires_on" msgid="3676242949915959821">"Изтича на:"</string>
<string name="serial_number" msgid="758814067660862493">"Сериен номер:"</string>
<string name="fingerprints" msgid="4516019619850763049">"Пръстови отпечатъци:"</string>
- <string name="sha256_fingerprint" msgid="4391271286477279263">"Пръстов отпечатък SHA-256:"</string>
- <string name="sha1_fingerprint" msgid="7930330235269404581">"Пръстов отпечатък SHA-1:"</string>
+ <string name="sha256_fingerprint" msgid="4391271286477279263">"Отпечатък SHA-256:"</string>
+ <string name="sha1_fingerprint" msgid="7930330235269404581">"Отпечатък SHA-1:"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Вижте всички"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Избор на активност"</string>
<string name="share_action_provider_share_with" msgid="5247684435979149216">"Споделяне със:"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Некатегоризирани"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Зададохте важността на тези известия."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Това е важно заради участващите хора."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Да се разреши ли на <xliff:g id="APP">%1$s</xliff:g> да създаде нов потребител с профила <xliff:g id="ACCOUNT">%2$s</xliff:g> (вече съществува потребител с този профил)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Да се разреши ли на <xliff:g id="APP">%1$s</xliff:g> да създаде нов потребител с профила <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Добавяне на език"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Предпочитание за региона"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Въведете име на език"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index c0843eaeba7d..beb08b22e953 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -283,7 +283,7 @@
<string name="permgrouprequest_contacts" msgid="6032805601881764300">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-কে আপনার পরিচিতিতে অ্যাক্সেস দেবেন?"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"লোকেশন"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"এই ডিভাইসের লোকেশন অ্যাক্সেস"</string>
- <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-কে এই ডিভাইসের লোকেশন অ্যাক্সেস করতে দেবেন?"</string>
+ <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে এই ডিভাইসের লোকেশন অ্যাক্সেস করতে দেবেন?"</string>
<string name="permgrouprequestdetail_location" msgid="1347189607421252902">"আপনি এই অ্যাপ ব্যবহার করার সময়েই শুধু সেটি আপনার লোকেশন অ্যাক্সেস করতে পারবে"</string>
<string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে এই ডিভাইসের লোকেশন &lt;b&gt;সব সময়&lt;/b&gt; অ্যাক্সেস করার অনুমতি দিতে চান?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"আপনি যখন অ্যাপটি ব্যবহার করবেন শুধুমাত্র তখনই অ্যাপটি বর্তমান লোকেশন অ্যাক্সেস করতে পারবে।"</string>
@@ -298,7 +298,7 @@
<string name="permgrouprequest_storage" msgid="7885942926944299560">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-কে আপনার ডিভাইসের ফটো, মিডিয়া এবং ফাইলে অ্যাক্সেস দেবেন?"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"মাইক্রোফোন"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"অডিও রেকর্ড"</string>
- <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-কে অডিও রেকর্ড করতে দেবেন?"</string>
+ <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে অডিও রেকর্ড করতে দেবেন?"</string>
<string name="permgrouplab_activityRecognition" msgid="1565108047054378642">"শারীরিক অ্যাক্টিভিটি"</string>
<string name="permgroupdesc_activityRecognition" msgid="6949472038320473478">"শারীরিক অ্যাক্টিভিটি অ্যাক্সেস করা"</string>
<string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-কে আপনার শারীরিক অ্যাক্টিভিটি অ্যাক্সেস করার অনুমতি দিতে চান?"</string>
@@ -324,7 +324,7 @@
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"প্রদর্শনের জুমের স্তর এবং লোকেশন নির্ধারন নিয়ন্ত্রণ করুন৷"</string>
<string name="capability_title_canPerformGestures" msgid="7418984730362576862">"অঙ্গভঙ্গির কাজগুলি সম্পাদন"</string>
<string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"আলতো চাপ দেওয়া, সোয়াইপ, পিঞ্চ করা এবং অন্যান্য ইঙ্গিতের কাজগুলি সম্পাদন করতে পারবেন৷"</string>
- <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"ফিঙ্গারপ্রিন্ট সেন্সরের উপর করা অঙ্গভঙ্গিগুলি"</string>
+ <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"আঙ্গুলের ছাপ সেন্সরের উপর করা জেসচার"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"ডিভাইসের আঙ্গুলের ছাপের সেন্সরের উপরে ইঙ্গিত করলে বুঝতে পারে।"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"স্ট্যাটাস বার নিষ্ক্রিয় অথবা সংশোধন করে"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"অ্যাপ্লিকেশনকে স্ট্যাটাস বার অক্ষম করতে এবং সিস্টেম আইকনগুলি সরাতে দেয়৷"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"অ্যাপ্লিকেশানটিকে এসএমএস প্রাপ্ত করার এবং প্রক্রিয়া করার অনুমতি দেয়৷ এর মানে হল অ্যাপ্লিকেশানটি আপনার ডিভাইস থেকে পাঠানো বার্তাগুলিকে পর্যবেক্ষণ করতে পারে এবং মুছতে পারে সেগুলিকে আপনাকে না দেখিয়ে৷"</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"টেক্সট মেসেজ পান (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"অ্যাপ্লিকেশানটিকে MMS মেসেজ প্রাপ্ত করার এবং প্রক্রিয়া করার অনুমতি দেয়৷ এর মানে হল অ্যাপ্লিকেশানটি আপনার ডিভাইস থেকে পাঠানো মেসেজগুলিকে পর্যবেক্ষণ করতে পারে এবং মুছতে পারে সেগুলিকে আপনাকে না দেখিয়ে৷"</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"সেল ব্রডকাস্টের মাধ্যমে মেসেজ ফরওয়ার্ড করুন"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"সেল ব্রডকাস্ট মেসেজ পেলে এটি সেল ব্রডকাস্ট মডিউলের সাথে তা যুক্ত করে যাতে সেই মেসেজ ফরওয়ার্ড করা যায়। আপনাকে জরুরি অবস্থা সম্পর্কে সাবধান করতে কিছু লোকেশনে সেল ব্রডকাস্ট অ্যালার্ট মেসেজ ডেলিভার করা হয়। জরুরি সেল ব্রডকাস্ট পাওয়া গেলে ক্ষতিকারক অ্যাপ আপনার ডিভাইসের পারফর্ম্যান্স ও অপারেশনে বাধা সৃষ্টি করতে পারে।"</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"সেল সম্প্রচার মেসেজ পড়ুন"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"আপনার ডিভাইস দ্বারা প্রাপ্ত সেল সম্প্রচার পড়তে অ্যাপ্লিকেশানটিকে অনুমতি দেয়৷ কয়েকটি স্থানে আপনাকে জরুরি অবস্থার জন্য সতর্ক করতে জরুরি সতর্কতাগুলি বিতরণ করা হয়৷ যখন একটি জরুরি সেল সম্প্রচার প্রাপ্ত হয় তখন ক্ষতিকারক অ্যাপ্লিকেশানগুলি আপনার ডিভাইসের কার্য সম্পাদনা বা কার্যকলাপে প্রতিবন্ধকতার সৃষ্টি করতে পারে৷"</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"গ্রাহক হিসেবে নেওয়া ফিডগুলি পড়ে"</string>
@@ -549,7 +551,7 @@
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"বহুবার চেষ্টা করেছেন। আঙ্গুলের ছাপ নেওয়ার সেন্সর অক্ষম করা হয়েছে।"</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"আবার চেষ্টা করুন৷"</string>
<string name="fingerprint_error_no_fingerprints" msgid="7654382120628334248">"কোনও আঙ্গুলের ছাপ নথিভুক্ত করা হয়নি।"</string>
- <string name="fingerprint_error_hw_not_present" msgid="409523969613176352">"এই ডিভাইসে ফিঙ্গারপ্রিন্ট সেন্সর নেই।"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="409523969613176352">"এই ডিভাইসে আঙ্গুলের ছাপ নেওয়ার সেন্সর নেই।"</string>
<string name="fingerprint_name_template" msgid="5870957565512716938">"আঙ্গুল <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1362,6 +1364,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB ডিবাগিং অক্ষম করতে বেছে নিন।"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"টেস্ট হারনেস মোড চালু আছে"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"টেস্ট হারনেস মোড বন্ধ করতে ফ্যাক্টরি রিসেট করুন।"</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"সিরিয়াল কনসোল চালু করা হয়েছে"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"পারফর্ম্যান্সে এর প্রভাব পড়বে। চালানো বন্ধ করতে \'বুটলোডার\' প্রোগ্রামে এটিকে চেক করে দেখুন।"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"ইউএসবি পোর্টে তরল পদার্থ অথবা ধুলো কণা"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"ইউএসবি পোর্ট নিজে থেকে বন্ধ করা হবে। আরও জানতে ট্যাপ করুন।"</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"ইউএসবি পোর্ট ব্যবহার করা যেতে পারে"</string>
@@ -1891,10 +1895,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"বিভাগ নির্ধারিত নয়"</string>
<string name="importance_from_user" msgid="7318955817386549931">"আপনি এই বিজ্ঞপ্তিগুলির গুরুত্ব সেট করেছেন।"</string>
<string name="importance_from_person" msgid="9160133597262938296">"লোকজন জড়িত থাকার কারণে এটি গুরুত্বপূর্ণ।"</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="ACCOUNT">%2$s</xliff:g>-এ (একজন ব্যবহারকারী এই অ্যাকাউন্টে আগে থেকেই রয়েছেন) একজন নতুন ব্যবহারকারী তৈরি করার অনুমতি <xliff:g id="APP">%1$s</xliff:g>-কে দেবেন?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"<xliff:g id="ACCOUNT">%2$s</xliff:g>-এ একজন নতুন ব্যবহারকারী তৈরি করার অনুমতি <xliff:g id="APP">%1$s</xliff:g>-কে দেবেন?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"একটি ভাষা যোগ করুন"</string>
<string name="country_selection_title" msgid="2954859441620215513">"পছন্দের অঞ্চল"</string>
<string name="search_language_hint" msgid="7042102592055108574">"ভাষার নাম লিখুন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 0d4b12c30332..862beee9d53a 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -347,6 +347,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Omogućava aplikaciji primanje i obradu SMS poruka. Ovo znači da aplikacija može pratiti ili brisati poruke poslane na vaš uređaj, a da vam ih pritom ne prikazuje."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"primanje tekstualnih poruka (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Omogućava aplikaciji prijem i obradu MMS poruka. Ovo znači da aplikacija može pratiti ili brisati poruke poslane na vaš uređaj, a da vam ih pritom ne prikazuje."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Prosljeđivanje poruka info servisa"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Dopušta aplikaciji da se veže za modul info servisa kako bi prosljeđivala poruke info servisa. Upozorenja koja emitira info servis se isporučuju na nekim lokacijama kako bi vas upozorila na vanredne situacije. Zlonamjerne aplikacije mogu ometati performanse ili rad vašeg uređaja kada primite informacije o vanrednoj situaciji od info servisa."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"čitanje poruka info servisa"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Omogućava aplikaciji čitanje poruka info servisa koje je primio vaš uređaj. Upozorenja koja emitira info servis se isporučuju na nekim lokacijama kako bi vas upozorila na vanredne situacije. Zlonamjerne aplikacije mogu ometati performanse ili rad vašeg uređaja kada primite informaciju o vanrednoj situaciji od info servisa."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"čitanje sadržaja na koje ste pretplaćeni"</string>
@@ -515,10 +517,10 @@
<string name="permdesc_requestPasswordComplexity" msgid="4730994229754212347">"Omogućava aplikaciji da sazna nivo kompleksnosti zaključavanja ekrana (visoki, srednji, niski ili bez zaključavanja), što naznačava mogući raspon trajanja i vrste zaključavanja ekrana. Aplikacija također može korisnicima predložiti da ažuriraju zaključavanje ekrana do određenog nivoa, ali korisnici slobodno mogu ignorirati prijedlog i napustiti stranicu. Važno je napomenuti da se zaključavanje ekrana ne pohranjuje kao obični tekst tako da aplikacija ne zna tačnu lozinku."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"koristi biometrijski hardver za otiske prstij"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Omogućava aplikaciji da za autentifikaciju koristi biometrijski hardver"</string>
- <string name="permlab_manageFingerprint" msgid="5640858826254575638">"upravljanje hardverom za otiske prstiju"</string>
+ <string name="permlab_manageFingerprint" msgid="5640858826254575638">"upravljanje hardverom za otisak prsta"</string>
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Omogućava aplikaciji da koristi metode za dodavanje i brisanje šablona otisaka prstiju za upotrebu."</string>
- <string name="permlab_useFingerprint" msgid="3150478619915124905">"korištenje hardvera za otiske prstiju"</string>
- <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Omogućava aplikaciji da za autentifikaciju koristi hardver za otiske prstiju"</string>
+ <string name="permlab_useFingerprint" msgid="3150478619915124905">"korištenje hardvera za otisak prsta"</string>
+ <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Omogućava aplikaciji da za autentifikaciju koristi hardver za otisak prsta"</string>
<string name="permlab_audioWrite" msgid="2661772059799779292">"izmjena muzičke kolekcije"</string>
<string name="permdesc_audioWrite" msgid="8888544708166230494">"Omogućava aplikaciji da mijenja vašu muzičku kolekciju."</string>
<string name="permlab_videoWrite" msgid="128769316366746446">"izmjena kolekcije videozapisa"</string>
@@ -546,7 +548,7 @@
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardver za otisak prsta nije dostupan."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Otisak prsta se ne može pohraniti. Uklonite postojeći otisak prsta."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Vrijeme za prepoznavanje otiska prsta je isteklo. Pokušajte ponovo."</string>
- <string name="fingerprint_error_canceled" msgid="4402024612660774395">"Radnja sa otiskom prsta je otkazana."</string>
+ <string name="fingerprint_error_canceled" msgid="4402024612660774395">"Radnja s otiskom prsta je otkazana."</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Korisnik je otkazao radnju s otiskom prsta."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Previše pokušaja. Pokušajte ponovo kasnije."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Previše pokušaja. Senzor za otisak prsta je onemogućen."</string>
@@ -1385,6 +1387,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Odaberite da onemogućite ispravljanje grešaka koristeći USB"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Omogućen način rada okvira za testiranje"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Izvršite vraćanje na fabričke postavke da onemogućite način rada okvira za testiranje."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Serijska konzola omogućena"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Izvedba je otežana. Da onemogućite, provjerite program za učitavanje operativnog sistema."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Tečnost ili nečistoće u USB priključku"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB priključak je automatski onemogućen. Dodirnite da saznate više."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB priključak je sada sigurno koristiti"</string>
@@ -1926,10 +1930,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Nije kategorizirano"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Vi određujete značaj ovih obavještenja."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Ovo je značajno zbog osoba koje su uključene."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Dozvoliti aplikaciji <xliff:g id="APP">%1$s</xliff:g> da kreira novog korisnika s računom <xliff:g id="ACCOUNT">%2$s</xliff:g> (korisnik s ovim računom već postoji)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Dozvoliti aplikaciji <xliff:g id="APP">%1$s</xliff:g> da kreira novog korisnika s računom <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Dodajte jezik"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Izbor regije"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Upišite ime jezika"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index d82884434305..28b9a160ba46 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -154,7 +154,7 @@
<string name="fcComplete" msgid="3118848230966886575">"Codi de funció completat."</string>
<string name="fcError" msgid="3327560126588500777">"Problema de connexió o codi de funció no vàlid."</string>
<string name="httpErrorOk" msgid="1191919378083472204">"D\'acord"</string>
- <string name="httpError" msgid="7956392511146698522">"S\'ha produït un error de xarxa."</string>
+ <string name="httpError" msgid="7956392511146698522">"S\'ha produït un error de la xarxa."</string>
<string name="httpErrorLookup" msgid="4711687456111963163">"No s\'ha pogut trobar l\'URL."</string>
<string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"L\'esquema d\'autenticació de llocs no és compatible."</string>
<string name="httpErrorAuth" msgid="1435065629438044534">"No s\'ha pogut autenticar."</string>
@@ -324,8 +324,8 @@
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controla el nivell i la posició del zoom de la pantalla."</string>
<string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Fer gestos"</string>
<string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Permet tocar, lliscar, pinçar i fer altres gestos."</string>
- <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"Gestos d\'empremtes digitals"</string>
- <string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"Captura gestos realitzats en el sensor d\'empremtes digitals del dispositiu."</string>
+ <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"Gestos d\'empremtes dactilars"</string>
+ <string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"Captura gestos realitzats en el sensor d\'empremtes dactilars del dispositiu."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"desactivar o modificar la barra d\'estat"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permet que l\'aplicació desactivi la barra d\'estat o afegeixi i elimini icones del sistema."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"aparèixer a la barra d\'estat"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Permet que l\'aplicació rebi i processi missatges SMS. Això vol dir que l\'aplicació pot controlar o suprimir missatges que s\'han enviat al teu dispositiu sense mostrar-te\'ls."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"recepció de missatges de text (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Permet que l\'aplicació rebi i processi missatges MMS. Això vol dir que l\'aplicació pot controlar o suprimir missatges que s\'han enviat al teu dispositiu sense mostrar-te\'ls."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Reenviar els missatges de difusió mòbil"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Permet que l\'aplicació es vinculi al mòdul de difusió mòbil per poder reenviar els missatges de difusió mòbil a mesura que es reben. Les alertes de difusió mòbil s\'entreguen en algunes ubicacions per alertar de situacions d\'emergència. És possible que les aplicacions malicioses interfereixin en el rendiment o en el funcionament del dispositiu quan es rebi una difusió mòbil d\'emergència."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"llegir missatges de difusió mòbil"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permet que l\'aplicació llegeixi missatges de difusió mòbil rebuts pel dispositiu. Les alertes de difusió mòbil s\'entreguen en algunes ubicacions per alertar de situacions d\'emergència. És possible que les aplicacions malicioses interfereixin en el rendiment o en el funcionament del dispositiu quan es rep una difusió mòbil d\'emergència."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"llegir els feeds als quals esteu subscrit"</string>
@@ -512,10 +514,10 @@
<string name="permdesc_requestPasswordComplexity" msgid="4730994229754212347">"Permet que l\'aplicació conegui el nivell de complexitat del bloqueig de pantalla (alt, mitjà, baix o cap), que indica la llargària i el tipus de bloqueig de pantalla possibles. L\'aplicació també pot suggerir que els usuaris actualitzin el bloqueig de pantalla a un nivell determinat, però els usuaris poden ignorar aquestes recomanacions. Tingues en compte que el bloqueig de pantalla no s\'emmagatzema com a text sense format, de manera que l\'aplicació no coneix la contrasenya exacta."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"utilitza maquinari biomètric"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Permet que l\'aplicació faci servir maquinari biomètric per a l\'autenticació"</string>
- <string name="permlab_manageFingerprint" msgid="5640858826254575638">"Gestionar el maquinari d\'empremtes digitals"</string>
- <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Permet que l\'aplicació invoqui mètodes per afegir i suprimir plantilles d\'empremtes digitals que es puguin fer servir."</string>
- <string name="permlab_useFingerprint" msgid="3150478619915124905">"Utilitzar el maquinari d\'empremtes digitals"</string>
- <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Permet que l\'aplicació faci servir maquinari d\'empremtes digitals per a l\'autenticació"</string>
+ <string name="permlab_manageFingerprint" msgid="5640858826254575638">"Gestionar el maquinari d\'empremtes dactilars"</string>
+ <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Permet que l\'aplicació invoqui mètodes per afegir i suprimir plantilles d\'empremtes dactilars que es puguin fer servir."</string>
+ <string name="permlab_useFingerprint" msgid="3150478619915124905">"Utilitzar el maquinari d\'empremtes dactilars"</string>
+ <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Permet que l\'aplicació faci servir maquinari d\'empremtes dactilars per a l\'autenticació"</string>
<string name="permlab_audioWrite" msgid="2661772059799779292">"modificar la teva col·lecció de música"</string>
<string name="permdesc_audioWrite" msgid="8888544708166230494">"Permet que l\'aplicació modifiqui la teva col·lecció de música."</string>
<string name="permlab_videoWrite" msgid="128769316366746446">"modificar la teva col·lecció de vídeos"</string>
@@ -530,30 +532,30 @@
<string name="biometric_not_recognized" msgid="5770511773560736082">"No s\'ha reconegut"</string>
<string name="biometric_error_canceled" msgid="349665227864885880">"S\'ha cancel·lat l\'autenticació"</string>
<string name="biometric_error_device_not_secured" msgid="6583143098363528349">"No s\'ha establert cap PIN, patró o contrasenya"</string>
- <string name="fingerprint_acquired_partial" msgid="735082772341716043">"S\'ha detectat una empremta digital parcial. Torna-ho a provar."</string>
- <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"No s\'ha pogut processar l\'empremta digital. Torna-ho a provar."</string>
- <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"El sensor d\'empremtes digitals està brut. Neteja\'l i torna-ho a provar."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"S\'ha detectat una empremta dactilar parcial. Torna-ho a provar."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"No s\'ha pogut processar l\'empremta dactilar. Torna-ho a provar."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"El sensor d\'empremtes dactilars està brut. Neteja\'l i torna-ho a provar."</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"El dit s\'ha mogut massa ràpid. Torna-ho a provar."</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"El dit s\'ha mogut massa lentament. Torna-ho a provar."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_authenticated" msgid="5309333983002526448">"L\'empremta digital s\'ha autenticat"</string>
+ <string name="fingerprint_authenticated" msgid="5309333983002526448">"L\'empremta dactilar s\'ha autenticat"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Cara autenticada"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Cara autenticada; prem el botó per confirmar"</string>
- <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"El maquinari per a empremtes digitals no està disponible."</string>
- <string name="fingerprint_error_no_space" msgid="1055819001126053318">"L\'empremta digital no es pot desar. Suprimeix-ne una."</string>
- <string name="fingerprint_error_timeout" msgid="3927186043737732875">"S\'ha esgotat el temps d\'espera per a l\'empremta digital. Torna-ho a provar."</string>
- <string name="fingerprint_error_canceled" msgid="4402024612660774395">"S\'ha cancel·lat l\'operació d\'empremta digital."</string>
- <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"L\'usuari ha cancel·lat l\'operació d\'empremta digital."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"El maquinari per a empremtes dactilars no està disponible."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"L\'empremta dactilar no es pot desar. Suprimeix-ne una."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"S\'ha esgotat el temps d\'espera per a l\'empremta dactilar. Torna-ho a provar."</string>
+ <string name="fingerprint_error_canceled" msgid="4402024612660774395">"S\'ha cancel·lat l\'operació d\'empremta dactilar."</string>
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"L\'usuari ha cancel·lat l\'operació d\'empremta dactilar."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"S\'han produït massa intents. Torna-ho a provar més tard."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"S\'han fet massa intents. S\'ha desactivat el sensor d\'empremtes digitals."</string>
+ <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"S\'han fet massa intents. S\'ha desactivat el sensor d\'empremtes dactilars."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Torna-ho a provar."</string>
<string name="fingerprint_error_no_fingerprints" msgid="7654382120628334248">"No s\'ha registrat cap empremta digital."</string>
- <string name="fingerprint_error_hw_not_present" msgid="409523969613176352">"Aquest dispositiu no té sensor d\'empremtes digitals."</string>
+ <string name="fingerprint_error_hw_not_present" msgid="409523969613176352">"Aquest dispositiu no té sensor d\'empremtes dactilars."</string>
<string name="fingerprint_name_template" msgid="5870957565512716938">"Dit <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
- <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icona d\'empremta digital"</string>
+ <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icona d\'empremta dactilar"</string>
<string name="permlab_manageFace" msgid="7262837876352591553">"gestiona el maquinari de desbloqueig facial"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Permet que l\'aplicació afegeixi i suprimeixi plantilles de cares que es puguin fer servir."</string>
<string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"utilitza el maquinari de desbloqueig facial"</string>
@@ -1159,7 +1161,7 @@
<string name="clearDefaultHintMsg" msgid="3252584689512077257">"Esborra els paràmetres predeterminats a Configuració del sistema &gt; Aplicacions &gt; Baixades."</string>
<string name="chooseActivity" msgid="7486876147751803333">"Selecciona una acció"</string>
<string name="chooseUsbActivity" msgid="6894748416073583509">"Tria una aplicació per al dispositiu USB"</string>
- <string name="noApplications" msgid="2991814273936504689">"No hi ha cap aplicació que pugui dur a terme aquesta acció."</string>
+ <string name="noApplications" msgid="2991814273936504689">"No hi ha cap aplicació que pugui fer aquesta acció."</string>
<string name="aerr_application" msgid="250320989337856518">"S\'ha aturat <xliff:g id="APPLICATION">%1$s</xliff:g>"</string>
<string name="aerr_process" msgid="6201597323218674729">"<xliff:g id="PROCESS">%1$s</xliff:g> s\'ha aturat"</string>
<string name="aerr_application_repeated" msgid="3146328699537439573">"L\'aplicació <xliff:g id="APPLICATION">%1$s</xliff:g> s\'atura contínuament"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Selecciona per desactivar la depuració per USB"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"S\'ha activat el mode Agent de prova"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Si vols desactivar el mode Agent de prova, restableix les dades de fàbrica."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"S\'ha activat la consola de sèrie"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"El rendiment s\'ha vist afectat. Per desactivar-la, comprova el bootloader."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Hi ha líquid o pols al port USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"El port USB es desactiva automàticament. Toca per obtenir més informació."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Ja pots utilitzar el port USB"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Sense classificar"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Has definit la importància d\'aquestes notificacions."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Aquest missatge és important per les persones implicades."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Concedeixes permís a <xliff:g id="APP">%1$s</xliff:g> per crear un usuari amb el compte <xliff:g id="ACCOUNT">%2$s</xliff:g>? (Ja hi ha un usuari amb aquest compte.)"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Concedeixes permís a <xliff:g id="APP">%1$s</xliff:g> per crear un usuari amb el compte <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Afegeix un idioma"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Preferència de regió"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Nom de l\'idioma"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 2b524ae64aba..8ba6f3425103 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -234,7 +234,7 @@
<string name="global_action_bug_report" msgid="7934010578922304799">"Hlášení chyb"</string>
<string name="global_action_logout" msgid="935179188218826050">"Ukončit relaci"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Snímek obrazovky"</string>
- <string name="bugreport_title" msgid="5981047024855257269">"Zpráva o chybě"</string>
+ <string name="bugreport_title" msgid="5981047024855257269">"Hlášení chyb"</string>
<string name="bugreport_message" msgid="398447048750350456">"Shromažďuje informace o aktuálním stavu zařízení. Tyto informace je následně možné poslat v e-mailové zprávě, chvíli však potrvá, než bude hlášení o chybě připraveno k odeslání. Buďte prosím trpěliví."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktivní přehled"</string>
<string name="bugreport_option_interactive_summary" msgid="229299488536107968">"Tato možnost se používá ve většině případů. Umožňuje sledovat průběh přehledu, zadat další podrobnosti o problému a pořizovat snímky obrazovky. Mohou být vynechány některé méně používané sekce, jejichž kontrola trvá dlouho."</string>
@@ -350,6 +350,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Umožňuje aplikaci přijmout a zpracovat zprávy SMS. Znamená to, že aplikace může sledovat zprávy odeslané do vašeho zařízení nebo je smazat, aniž by se vám zobrazily."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"příjem textových zpráv (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Umožňuje aplikaci přijmout a zpracovat zprávy MMS. Znamená to, že aplikace může sledovat zprávy odeslané do vašeho zařízení nebo je smazat, aniž by se vám zobrazily."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Přesměrování zpráv informačních služeb"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Umožňuje aplikaci vytvořit vazbu s modulem informačních služeb za účelem přesměrovávání přijatých zpráv informačních služeb. Výstražná upozornění informačních služeb jsou v některých oblastech odesílána za účelem varování před výjimečnými událostmi. Škodlivé aplikace mohou narušit výkon či provoz vašeho zařízení během přijímání zpráv informačních služeb."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"čtení zpráv informačních služeb"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Umožňuje aplikaci číst zprávy informačních služeb přijaté ve vašem zařízení. Výstražná upozornění informačních služeb jsou v některých oblastech odesílána za účelem varování před výjimečnými událostmi. Škodlivé aplikace mohou narušit výkon či provoz vašeho zařízení během přijímání zpráv informačních služeb."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"čtení zdrojů přihlášených k odběru"</string>
@@ -519,7 +521,7 @@
<string name="permlab_useBiometric" msgid="8837753668509919318">"použití biometrického hardwaru"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Umožňuje aplikaci použít k ověření biometrický hardware"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"správa hardwaru na čtení otisků prstů"</string>
- <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Umožňuje aplikaci volat metody k přidání a smazání šablon otisků prstů, které budou použity."</string>
+ <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Umožňuje aplikaci volit metody k přidávání a mazání šablon otisků prstů, které budou použity."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"použití hardwaru na čtení otisků prstů"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Umožňuje aplikaci použít k ověření hardware na čtení otisků prstů"</string>
<string name="permlab_audioWrite" msgid="2661772059799779292">"úprava hudební sbírky"</string>
@@ -1405,6 +1407,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Vyberte, chcete-li zakázat ladění přes USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Režim správce testů je aktivní"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Chcete-li deaktivovat režim správce testů, restartujte zařízení do továrního nastavení."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Je zapnutá sériová konzole"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Toto má dopad na výkon. Chcete-li ji vypnout, zkontrolujte bootloader."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Kapalina nebo nečistota v portu USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Port USB byl automaticky deaktivován. Klepnutím zobrazíte další informace."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Port USB lze bezpečně použít"</string>
@@ -1958,10 +1962,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Neklasifikováno"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Důležitost oznámení určujete vy."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Tato zpráva je důležitá kvůli lidem zapojeným do konverzace."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Povolit aplikaci <xliff:g id="APP">%1$s</xliff:g> vytvořit nového uživatele s účtem <xliff:g id="ACCOUNT">%2$s</xliff:g>? (Uživatel s tímto účtem již existuje.)"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Povolit aplikaci <xliff:g id="APP">%1$s</xliff:g> vytvořit nového uživatele s účtem <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Přidat jazyk"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Preferovaná oblast"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Zadejte název jazyka"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 4d57a90fa068..0d40688ec427 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -75,7 +75,7 @@
<string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"Standarder for opkalds-id til begrænset. Næste opkald: Ikke begrænset"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"Standarder for opkalds-id til ikke begrænset. Næste opkald: Begrænset"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"Standarder for opkalds-id til ikke begrænset. Næste opkald: Ikke begrænset"</string>
- <string name="serviceNotProvisioned" msgid="8614830180508686666">"Tjenesten leveres ikke!"</string>
+ <string name="serviceNotProvisioned" msgid="8614830180508686666">"Tjenesten provisioneres ikke."</string>
<string name="CLIRPermanent" msgid="3377371145926835671">"Du kan ikke ændre indstillingen for opkalds-id\'et."</string>
<string name="RestrictedOnDataTitle" msgid="5221736429761078014">"Ingen mobildatatjeneste"</string>
<string name="RestrictedOnEmergencyTitle" msgid="6855466023161191166">"Det er ikke muligt at foretage nødopkald"</string>
@@ -226,7 +226,7 @@
<string name="global_actions" product="default" msgid="2406416831541615258">"Indstillinger for telefon"</string>
<string name="global_action_lock" msgid="2844945191792119712">"Skærmlås"</string>
<string name="global_action_power_off" msgid="4471879440839879722">"Sluk"</string>
- <string name="global_action_emergency" msgid="7112311161137421166">"Nødopkald"</string>
+ <string name="global_action_emergency" msgid="7112311161137421166">"Nødsituation"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Fejlrapport"</string>
<string name="global_action_logout" msgid="935179188218826050">"Afslut sessionen"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Tillader, at appen kan modtage og behandle sms-beskeder. Det betyder, at appen kan overvåge eller slette de beskeder, der sendes til din enhed, uden at vise dem til dig."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"modtage tekstbeskeder (mms)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Tillader, at appen kan modtage og behandle mms-beskeder. Det betyder, at appen kan overvåge eller slette de beskeder, der sendes til din enhed, uden at vise dem til dig."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Videresend Cell Broadcast-meddelelser"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Tillader, at appen bindes til Cell Broadcast-modulet, så Cell Broadcast-meddelelser kan videresendes, når de modtages. I nogle områder sendes der Cell Broadcast-underretninger for at advare dig om nødsituationer. Ondsindede apps kan forstyrre effektiviteten eller driften af din enhed, når den modtager en Cell Broadcast-meddelelse om en nødsituation."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"læse Cell Broadcast-meddelelser"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Tillader, at appen læser Cell Broadcast-underretninger, der modtages af din enhed. I nogle områder sendes der Cell Broadcast-underretninger for at advare om nødsituationer. Ondsindede apps kan forstyrre ydelsen eller driften af ​din ​enhed, når der modtages en Cell Broadcast-meddelelse om en nødsituation."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"læse feeds, jeg abonnerer på"</string>
@@ -512,10 +514,10 @@
<string name="permdesc_requestPasswordComplexity" msgid="4730994229754212347">"Giver appen tilladelse til at kende skærmlåsens kompleksitet (høj, medium, lav eller ingen), hvilket kan afsløre oplysninger om skærmlåsens længde og type. Appen kan også foreslå brugerne at opdatere deres skærmlås til et bestemt niveau, men brugerne kan frit ignorere det og gå videre. Bemærk! Skærmlåsen gemmes ikke som almindelig tekst, så appen kender ikke den nøjagtige adgangskode."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"brug biometrisk hardware"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Tillader, at appen kan bruge biometrisk hardware til godkendelse"</string>
- <string name="permlab_manageFingerprint" msgid="5640858826254575638">"administrer fingeraftrykhardware"</string>
+ <string name="permlab_manageFingerprint" msgid="5640858826254575638">"administrer hardware til fingeraftryk"</string>
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Tillader, at appen kan køre metoder til at tilføje og slette fingeraftryksskabeloner"</string>
- <string name="permlab_useFingerprint" msgid="3150478619915124905">"bruge fingeraftrykhardware"</string>
- <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Tillader, at appen kan bruge fingeraftrykhardware til godkendelse"</string>
+ <string name="permlab_useFingerprint" msgid="3150478619915124905">"bruge hardware til fingeraftryk"</string>
+ <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Tillader, at appen kan bruge hardware til fingeraftryk til godkendelse"</string>
<string name="permlab_audioWrite" msgid="2661772059799779292">"ændre din musiksamling"</string>
<string name="permdesc_audioWrite" msgid="8888544708166230494">"Tillader, at appen kan ændre din musiksamling."</string>
<string name="permlab_videoWrite" msgid="128769316366746446">"ændre din videosamling"</string>
@@ -532,7 +534,7 @@
<string name="biometric_error_device_not_secured" msgid="6583143098363528349">"Der er ikke angivet pinkode, mønster eller adgangskode"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Der blev registreret et delvist fingeraftryk. Prøv igen."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Fingeraftrykket kunne ikke behandles. Prøv igen."</string>
- <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Sensoren til registrering af fingeraftryk er beskidt. Tør den af, og prøv igen."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingeraftrykslæseren er beskidt. Tør den af, og prøv igen."</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Du bevægede fingeren for hurtigt. Prøv igen."</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Du bevægede fingeren for langsomt. Prøv igen."</string>
<string-array name="fingerprint_acquired_vendor">
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Vælg for at deaktivere USB-fejlretning."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Tilstanden Testsele er aktiveret"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Gendan fabriksindstillingerne for at deaktivere tilstanden Testsele."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Seriekonsollen er aktiveret"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Effektiviteten er påvirket. Deaktiver via bootloaderen."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Væske eller snavs i USB-porten"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB-porten deaktiveres automatisk. Tryk for at få flere oplysninger."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB-porten kan bruges"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Uden kategori"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Du angiver, hvor vigtige disse notifikationer er."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Dette er vigtigt på grund af de personer, det handler om."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Vil du give <xliff:g id="APP">%1$s</xliff:g> tilladelse til at oprette en ny bruger med <xliff:g id="ACCOUNT">%2$s</xliff:g> (der findes allerede en bruger med denne konto)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Vil du give <xliff:g id="APP">%1$s</xliff:g> tilladelse til at oprette en nye bruger med <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Tilføj et sprog"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Områdeindstilling"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Angiv sprog"</string>
@@ -1971,7 +1973,7 @@
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Svar"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
<string name="mmcc_authentication_reject" msgid="5767701075994754356">"SIM-kort er ikke tilladt for tale"</string>
- <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"SIM-kort er ikke aktiveret for tale"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"SIM-kort er ikke provisioneret til tale"</string>
<string name="mmcc_illegal_ms" msgid="807334478177362062">"SIM-kort er ikke tilladt for tale"</string>
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Telefon er ikke tilladt for tale"</string>
<string name="mmcc_authentication_reject_msim_template" msgid="1217031195834766479">"SIM-kortet <xliff:g id="SIMNUMBER">%d</xliff:g> er ikke tilladt"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 2ec70d36a412..f0513456e6e6 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Ermöglicht der App, SMS zu empfangen und zu verarbeiten. Das bedeutet, dass die App an dein Gerät gesendete Nachrichten überwachen und löschen kann, ohne sie dir anzuzeigen."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"MMS empfangen"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Ermöglicht der App, MMS zu empfangen und zu verarbeiten. Das bedeutet, dass die App an dein Gerät gesendete Nachrichten überwachen und löschen kann, ohne sie dir anzuzeigen."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Cell-Broadcast-Nachrichten weiterleiten"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Ermöglicht der App, sich mit dem Cell-Broadcast-Modul zu verbinden, um empfangene Cell-Broadcast-Nachrichten weiterzuleiten. Cell-Broadcast-Benachrichtigungen können in einigen Ländern oder Regionen gesendet werden, um dich bei Notfallsituationen zu warnen. Schädliche Apps können die Leistung oder den Betrieb deines Geräts beeinträchtigen, wenn eine Cell-Broadcast-Notfallbenachrichtigung eingeht."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"Cell Broadcast-Nachrichten lesen"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Ermöglicht der App, von deinem Gerät empfangene Cell Broadcast-Nachrichten zu lesen. Cell Broadcast-Benachrichtigungen werden an einigen Standorten gesendet, um dich über Notfallsituationen zu informieren. Schädliche Apps können die Leistung oder den Betrieb deines Geräts beeinträchtigen, wenn eine Cell Broadcast-Notfallbenachrichtigung eingeht."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"Abonnierte Feeds lesen"</string>
@@ -542,7 +544,7 @@
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Gesicht authentifiziert, bitte bestätigen"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Fingerabdruckhardware nicht verfügbar"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingerabdruck kann nicht gespeichert werden. Entferne einen vorhandenen Fingerabdruck."</string>
- <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Zeitüberschreitung für Fingerabdruck. Bitte versuche es noch einmal."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Zeitüberschreitung bei Fingerabdruck. Bitte versuche es noch einmal."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Fingerabdruckvorgang abgebrochen"</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Vorgang der Fingerabdruckauthentifizierung vom Nutzer abgebrochen."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Zu viele Versuche, bitte später noch einmal versuchen"</string>
@@ -1361,7 +1363,9 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB-Debugging deaktivieren: auswählen"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Test-Harnischmodus aktiviert"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Setz das Gerät auf die Werkseinstellungen zurück, um den Test-Harnischmodus zu deaktivieren."</string>
- <string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Flüssigkeiten oder Fremdkörper im USB-Port"</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Serielle Konsole aktiviert"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Leistung wird beeinflusst. Überprüfe Bootloader zum Deaktivieren."</string>
+ <string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Flüssigkeit oder Fremdkörper im USB-Port"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Der USB-Port wird automatisch deaktiviert. Für weitere Informationen tippen."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB-Port kann wieder verwendet werden"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Das Smartphone erkennt keine Flüssigkeiten oder Fremdkörper mehr."</string>
@@ -1588,7 +1592,7 @@
<string name="launchBrowserDefault" msgid="2057951947297614725">"Browser starten?"</string>
<string name="SetupCallDefault" msgid="5834948469253758575">"Anruf annehmen?"</string>
<string name="activity_resolver_use_always" msgid="8017770747801494933">"Immer"</string>
- <string name="activity_resolver_set_always" msgid="1422574191056490585">"Auf \"Immer geöffnet\" festlegen"</string>
+ <string name="activity_resolver_set_always" msgid="1422574191056490585">"Auf \"Immer öffnen\" festlegen"</string>
<string name="activity_resolver_use_once" msgid="2404644797149173758">"Nur diesmal"</string>
<string name="activity_resolver_app_settings" msgid="8965806928986509855">"Einstellungen"</string>
<string name="activity_resolver_work_profiles_support" msgid="185598180676883455">"Das Arbeitsprofil wird von %1$s nicht unterstützt."</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Unkategorisiert"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Du hast die Wichtigkeit dieser Benachrichtigungen festgelegt."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Diese Benachrichtigung ist aufgrund der beteiligten Personen wichtig."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Es gibt bereits einen Nutzer mit <xliff:g id="ACCOUNT">%2$s</xliff:g>. Möchtest du zulassen, dass <xliff:g id="APP">%1$s</xliff:g> einen neuen Nutzer mit diesem Konto erstellt?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Möchtest du zulassen, dass <xliff:g id="APP">%1$s</xliff:g> einen neuen Nutzer mit <xliff:g id="ACCOUNT">%2$s</xliff:g> erstellt?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Sprache hinzufügen"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Region auswählen"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Sprache eingeben"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 6998c0c16811..a360de0f38b0 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -325,7 +325,7 @@
<string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Εκτέλεση κινήσεων"</string>
<string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Επιτρέπει το πάτημα, την ολίσθηση, το πλησίασμα και άλλες κινήσεις."</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"Κινήσεις δακτυλικών αποτυπωμάτων"</string>
- <string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"Μπορεί να αναγνωρίσει κινήσεις που εκτελούνται στον αισθητήρα δακτυλικών αποτυπωμάτων της συσκευής."</string>
+ <string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"Μπορεί να αναγνωρίσει κινήσεις που εκτελούνται στον αισθητήρα δακτυλικού αποτυπώματος της συσκευής."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"απενεργοποιεί ή να τροποποιεί την γραμμή κατάστασης"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Επιτρέπει στην εφαρμογή να απενεργοποιεί τη γραμμή κατάστασης ή να προσθέτει και να αφαιρεί εικονίδια συστήματος."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ορίζεται ως γραμμή κατάστασης"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Επιτρέπει στην εφαρμογή τη λήψη και την επεξεργασία μηνυμάτων SMS. Αυτό σημαίνει ότι η εφαρμογή θα μπορούσε να παρακολουθήσει ή να διαγράψει τα μηνύματα που αποστέλλονται στη συσκευή σας χωρίς αυτά να εμφανιστούν σε εσάς."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"λαμβάνει μηνύματα κειμένου (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Επιτρέπει στην εφαρμογή τη λήψη και την επεξεργασία μηνυμάτων MMS. Αυτό σημαίνει ότι η εφαρμογή θα μπορούσε να παρακολουθήσει ή να διαγράψει τα μηνύματα που αποστέλλονται στη συσκευή σας χωρίς αυτά να εμφανιστούν σε εσάς."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Προώθηση μηνυμάτων εκπομπής κινητής τηλεφωνίας"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Επιτρέπει στην εφαρμογή να συνδέεται στη λειτουργική μονάδα εκπομπής κινητής τηλεφωνίας, προκειμένου να προωθεί τα μηνύματα εκπομπής κινητής τηλεφωνίας κατά τη λήψη τους. Οι ειδοποιήσεις εκπομπής κινητής τηλεφωνίας προβάλλονται σε ορισμένες τοποθεσίες, για να σας προειδοποιήσουν σχετικά με καταστάσεις έκτακτης ανάγκης. Οι κακόβουλες εφαρμογές μπορεί να επηρεάσουν την απόδοση ή τη λειτουργία της συσκευής σας κατά τη λήψη μιας εκπομπής κινητής τηλεφωνίας έκτακτης ανάγκης."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"διαβάζει μηνύματα που έχουν μεταδοθεί μέσω κινητού τηλεφώνου"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Επιτρέπει στην εφαρμογή την ανάγνωση μηνυμάτων που έχουν μεταδοθεί μέσω κινητού τηλεφώνου και έχουν ληφθεί από τη συσκευή σας. Ειδοποιήσεις που μεταδίδονται μέσω κινητού παραδίδονται σε ορισμένες τοποθεσίες για να σας προειδοποιήσουν για καταστάσεις έκτακτης ανάγκης. Κακόβουλες εφαρμογές ενδέχεται να παρεμποδίσουν την απόδοση ή τη λειτουργία της συσκευής σας κατά τη λήψη μετάδοσης μέσω κινητού σχετικά με μια επείγουσα κατάσταση."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"διαβάζει ροές δεδομένων στις οποίες έχετε εγγραφεί"</string>
@@ -512,10 +514,10 @@
<string name="permdesc_requestPasswordComplexity" msgid="4730994229754212347">"Επιτρέπει στην εφαρμογή να μάθει το επίπεδο πολυπλοκότητας του κλειδώματος οθόνης (υψηλό, μέσο, χαμηλό ή κανένα), το οποίο υποδεικνύει το πιθανό εύρος του μήκους και του τύπου κλειδώματος οθόνης. Η εφαρμογή μπορεί επίσης να προτείνει στους χρήστες να ενημερώσουν το κλείδωμα οθόνης σε ένα συγκεκριμένο επίπεδο, όμως οι χρήστες μπορούν να την αγνοήσουν και να συνεχίσουν. Λάβετε υπόψη ότι το κλείδωμα οθόνης δεν αποθηκεύεται σε απλό κείμενο. Συνεπώς, η εφαρμογή δεν γνωρίζει τον κωδικό."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"χρήση βιομετρικού εξοπλισμού"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Επιτρέπει στην εφαρμογή να χρησιμοποιεί βιομετρικό εξοπλισμό για έλεγχο ταυτότητας"</string>
- <string name="permlab_manageFingerprint" msgid="5640858826254575638">"διαχειρίζεται τον εξοπλισμό δακτυλικού αποτυπώματος"</string>
- <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Επιτρέπει στην εφαρμογή να επικαλείται μεθόδους για την προσθήκη και τη διαγραφή προτύπων μοναδικού χαρακτηριστικού για χρήση."</string>
- <string name="permlab_useFingerprint" msgid="3150478619915124905">"χρησιμοποιεί τον εξοπλισμό δακτυλικού αποτυπώματος"</string>
- <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Επιτρέπει στην εφαρμογή να χρησιμοποιεί εξοπλισμό μοναδικού χαρακτηριστικού για έλεγχο ταυτότητας"</string>
+ <string name="permlab_manageFingerprint" msgid="5640858826254575638">"διαχείριση εξοπλισμού δακτυλικού αποτυπώματος"</string>
+ <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Επιτρέπει στην εφαρμογή να επικαλείται μεθόδους για την προσθήκη και τη διαγραφή προτύπων δακτυλικών αποτυπωμάτων για χρήση."</string>
+ <string name="permlab_useFingerprint" msgid="3150478619915124905">"χρήση εξοπλισμού δακτυλικού αποτυπώματος"</string>
+ <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Επιτρέπει στην εφαρμογή να χρησιμοποιεί εξοπλισμό δακτυλικού αποτυπώματος για έλεγχο ταυτότητας"</string>
<string name="permlab_audioWrite" msgid="2661772059799779292">"τροποποίηση της μουσικής συλλογής σας"</string>
<string name="permdesc_audioWrite" msgid="8888544708166230494">"Επιτρέπει στην εφαρμογή να τροποποιήσει τη μουσική συλλογή σας."</string>
<string name="permlab_videoWrite" msgid="128769316366746446">"τροποποίηση της συλλογής βίντεό σας"</string>
@@ -530,9 +532,9 @@
<string name="biometric_not_recognized" msgid="5770511773560736082">"Δεν αναγνωρίστηκε"</string>
<string name="biometric_error_canceled" msgid="349665227864885880">"Ο έλεγχος ταυτότητας ακυρώθηκε"</string>
<string name="biometric_error_device_not_secured" msgid="6583143098363528349">"Δεν έχει οριστεί PIN, μοτίβο ή κωδικός πρόσβασης"</string>
- <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Εντοπίστηκε μερικό μοναδικό χαρακτηριστικό. Δοκιμάστε ξανά."</string>
- <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Δεν ήταν δυνατή η επεξεργασία του μοναδικού χαρακτηριστικού. Δοκιμάστε ξανά."</string>
- <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Ο αισθητήρας μοναδικού χαρακτηριστικού δεν είναι καθαρός. Καθαρίστε τον και δοκιμάστε ξανά."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Εντοπίστηκε μόνο μέρος του δακτυλικού αποτυπώματος. Δοκιμάστε ξανά."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Δεν ήταν δυνατή η επεξεργασία του δακτυλικού αποτυπώματος. Δοκιμάστε ξανά."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Ο αισθητήρας δακτυλικού αποτυπώματος δεν είναι καθαρός. Καθαρίστε τον και δοκιμάστε ξανά."</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Πολύ γρήγορη κίνηση δαχτύλου. Δοκιμάστε ξανά."</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Πολύ αργή κίνηση δαχτύλου. Δοκιμάστε ξανά."</string>
<string-array name="fingerprint_acquired_vendor">
@@ -540,16 +542,16 @@
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Η ταυτότητα του δακτυλικού αποτυπώματος ελέγχθηκε"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Έγινε έλεγχος ταυτότητας προσώπου"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Έγινε έλεγχος ταυτότητας προσώπου, πατήστε \"Επιβεβαίωση\""</string>
- <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Ο εξοπλισμός μοναδικού χαρακτηριστικού δεν είναι διαθέσιμος."</string>
- <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Δεν είναι δυνατή η αποθήκευση μοναδικού χαρακτηριστικού. Καταργήστε το υπάρχον μοναδικό χαρακτηριστικό."</string>
- <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Λήξη χρονικού ορίου μοναδικού χαρακτηριστικού. Δοκιμάστε ξανά."</string>
- <string name="fingerprint_error_canceled" msgid="4402024612660774395">"Η λειτουργία μοναδικού χαρακτηριστικού ακυρώθηκε."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Ο εξοπλισμός δακτυλικού αποτυπώματος δεν είναι διαθέσιμος."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Δεν είναι δυνατή η αποθήκευση του δακτυλικού αποτυπώματος. Καταργήστε το υπάρχον δακτυλικό αποτύπωμα."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Λήξη χρονικού ορίου δακτυλικού αποτυπώματος. Δοκιμάστε ξανά."</string>
+ <string name="fingerprint_error_canceled" msgid="4402024612660774395">"Η λειτουργία δακτυλικού αποτυπώματος ακυρώθηκε."</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Η λειτουργία δακτυλικού αποτυπώματος ακυρώθηκε από τον χρήστη."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Πάρα πολλές προσπάθειες. Δοκιμάστε ξανά αργότερα."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Πάρα πολλές προσπάθειες. Ο αισθητήρας δακτυλικών αποτυπωμάτων απενεργοποιήθηκε."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Δοκιμάστε ξανά."</string>
<string name="fingerprint_error_no_fingerprints" msgid="7654382120628334248">"Δεν έχουν καταχωριστεί δακτυλικά αποτυπώματα."</string>
- <string name="fingerprint_error_hw_not_present" msgid="409523969613176352">"Αυτή η συσκευή δεν διαθέτει αισθητήρα δακτυλικών αποτυπωμάτων."</string>
+ <string name="fingerprint_error_hw_not_present" msgid="409523969613176352">"Αυτή η συσκευή δεν διαθέτει αισθητήρα δακτυλικού αποτυπώματος."</string>
<string name="fingerprint_name_template" msgid="5870957565512716938">"Δάχτυλο <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Επιλογή για απενεργοποίηση του εντοπισμού σφαλμάτων USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Η λειτουργία περιβάλλοντος δοκιμών ενεργοποιήθηκε"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Εκτελέστε επαναφορά εργοστασιακών ρυθμίσεων για να απενεργοποιήσετε τη λειτουργία περιβάλλοντος δοκιμών."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Η σειριακή κονσόλα ενεργοποιήθηκε"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Η απόδοση επηρεάζεται. Για απενεργοποίηση, επιλέξτε το πρόγραμμα φόρτωσης εκκίνησης."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Υγρασία ή ακαθαρσίες στη θύρα USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Η θύρα USB απενεργοποιείται αυτόματα. Πατήστε για να μάθετε περισσότερα."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Αποδεκτή η χρήση της θύρας USB"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Μη κατηγοριοποιημένο"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Μπορείτε να ρυθμίσετε τη βαρύτητα αυτών των ειδοποιήσεων."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Αυτό είναι σημαντικό λόγω των ατόμων που συμμετέχουν."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Επιτρέπετε στην εφαρμογή <xliff:g id="APP">%1$s</xliff:g> να δημιουργήσει έναν νέο χρήστη με τον λογαριασμό <xliff:g id="ACCOUNT">%2$s</xliff:g> (υπάρχει ήδη χρήστης με αυτόν τον λογαριασμό);"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Επιτρέπετε στην εφαρμογή <xliff:g id="APP">%1$s</xliff:g> να δημιουργήσει έναν νέο χρήστη με τον λογαριασμό <xliff:g id="ACCOUNT">%2$s</xliff:g>;"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Προσθήκη γλώσσας"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Προτίμηση περιοχής"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Εισαγ. όνομα γλώσσας"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 7c2b32989c5d..1e0e93a5f1d3 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Allows the app to receive and process SMS messages. This means that the app could monitor or delete messages sent to your device without showing them to you."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"receive text messages (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Allows the app to receive and process MMS messages. This means that the app could monitor or delete messages sent to your device without showing them to you."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Forward cell broadcast messages"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Allows the app to bind to the cell broadcast module in order to forward cell broadcast messages as they are received. Cell broadcast alerts are delivered in some locations to warn you of emergency situations. Malicious apps may interfere with the performance or operation of your device when an emergency cell broadcast is received."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"read mobile broadcast messages"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Allows the app to read mobile broadcast messages received by your device. Cell broadcast alerts are delivered in some locations to warn you of emergency situations. Malicious apps may interfere with the performance or operation of your device when an emergency mobile broadcast is received."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"read subscribed feeds"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Select to disable USB debugging."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Test Harness Mode enabled"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Perform a factory reset to disable Test Harness Mode."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Serial console enabled"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Performance is impacted. To disable, check bootloader."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Liquid or debris in USB port"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB port is automatically disabled. Tap to learn more."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"OK to use USB port"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Uncategorised"</string>
<string name="importance_from_user" msgid="7318955817386549931">"You set the importance of these notifications."</string>
<string name="importance_from_person" msgid="9160133597262938296">"This is important because of the people involved."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Allow <xliff:g id="APP">%1$s</xliff:g> to create a new User with <xliff:g id="ACCOUNT">%2$s</xliff:g> (a User with this account already exists)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Allow <xliff:g id="APP">%1$s</xliff:g> to create a new User with <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Add a language"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Region preference"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Type language name"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 6da3a5fbdcf9..4deaf2e0cf14 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Allows the app to receive and process SMS messages. This means that the app could monitor or delete messages sent to your device without showing them to you."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"receive text messages (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Allows the app to receive and process MMS messages. This means that the app could monitor or delete messages sent to your device without showing them to you."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Forward cell broadcast messages"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Allows the app to bind to the cell broadcast module in order to forward cell broadcast messages as they are received. Cell broadcast alerts are delivered in some locations to warn you of emergency situations. Malicious apps may interfere with the performance or operation of your device when an emergency cell broadcast is received."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"read mobile broadcast messages"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Allows the app to read mobile broadcast messages received by your device. Cell broadcast alerts are delivered in some locations to warn you of emergency situations. Malicious apps may interfere with the performance or operation of your device when an emergency mobile broadcast is received."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"read subscribed feeds"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Select to disable USB debugging."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Test Harness Mode enabled"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Perform a factory reset to disable Test Harness Mode."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Serial console enabled"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Performance is impacted. To disable, check bootloader."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Liquid or debris in USB port"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB port is automatically disabled. Tap to learn more."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"OK to use USB port"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Uncategorised"</string>
<string name="importance_from_user" msgid="7318955817386549931">"You set the importance of these notifications."</string>
<string name="importance_from_person" msgid="9160133597262938296">"This is important because of the people involved."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Allow <xliff:g id="APP">%1$s</xliff:g> to create a new User with <xliff:g id="ACCOUNT">%2$s</xliff:g> (a User with this account already exists)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Allow <xliff:g id="APP">%1$s</xliff:g> to create a new User with <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Add a language"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Region preference"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Type language name"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 7c2b32989c5d..1e0e93a5f1d3 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Allows the app to receive and process SMS messages. This means that the app could monitor or delete messages sent to your device without showing them to you."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"receive text messages (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Allows the app to receive and process MMS messages. This means that the app could monitor or delete messages sent to your device without showing them to you."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Forward cell broadcast messages"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Allows the app to bind to the cell broadcast module in order to forward cell broadcast messages as they are received. Cell broadcast alerts are delivered in some locations to warn you of emergency situations. Malicious apps may interfere with the performance or operation of your device when an emergency cell broadcast is received."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"read mobile broadcast messages"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Allows the app to read mobile broadcast messages received by your device. Cell broadcast alerts are delivered in some locations to warn you of emergency situations. Malicious apps may interfere with the performance or operation of your device when an emergency mobile broadcast is received."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"read subscribed feeds"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Select to disable USB debugging."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Test Harness Mode enabled"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Perform a factory reset to disable Test Harness Mode."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Serial console enabled"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Performance is impacted. To disable, check bootloader."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Liquid or debris in USB port"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB port is automatically disabled. Tap to learn more."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"OK to use USB port"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Uncategorised"</string>
<string name="importance_from_user" msgid="7318955817386549931">"You set the importance of these notifications."</string>
<string name="importance_from_person" msgid="9160133597262938296">"This is important because of the people involved."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Allow <xliff:g id="APP">%1$s</xliff:g> to create a new User with <xliff:g id="ACCOUNT">%2$s</xliff:g> (a User with this account already exists)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Allow <xliff:g id="APP">%1$s</xliff:g> to create a new User with <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Add a language"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Region preference"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Type language name"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 7c2b32989c5d..1e0e93a5f1d3 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Allows the app to receive and process SMS messages. This means that the app could monitor or delete messages sent to your device without showing them to you."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"receive text messages (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Allows the app to receive and process MMS messages. This means that the app could monitor or delete messages sent to your device without showing them to you."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Forward cell broadcast messages"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Allows the app to bind to the cell broadcast module in order to forward cell broadcast messages as they are received. Cell broadcast alerts are delivered in some locations to warn you of emergency situations. Malicious apps may interfere with the performance or operation of your device when an emergency cell broadcast is received."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"read mobile broadcast messages"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Allows the app to read mobile broadcast messages received by your device. Cell broadcast alerts are delivered in some locations to warn you of emergency situations. Malicious apps may interfere with the performance or operation of your device when an emergency mobile broadcast is received."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"read subscribed feeds"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Select to disable USB debugging."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Test Harness Mode enabled"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Perform a factory reset to disable Test Harness Mode."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Serial console enabled"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Performance is impacted. To disable, check bootloader."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Liquid or debris in USB port"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB port is automatically disabled. Tap to learn more."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"OK to use USB port"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Uncategorised"</string>
<string name="importance_from_user" msgid="7318955817386549931">"You set the importance of these notifications."</string>
<string name="importance_from_person" msgid="9160133597262938296">"This is important because of the people involved."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Allow <xliff:g id="APP">%1$s</xliff:g> to create a new User with <xliff:g id="ACCOUNT">%2$s</xliff:g> (a User with this account already exists)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Allow <xliff:g id="APP">%1$s</xliff:g> to create a new User with <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Add a language"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Region preference"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Type language name"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 62f294e66438..5fe1bb19a06e 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‏‎‎‎‎‎‎‎‎‎‏‎‏‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‏‎‏‏‎‎‏‏‎‏‏‎‎‎‏‏‎‏‏‎‏‏‎Allows the app to receive and process SMS messages. This means the app could monitor or delete messages sent to your device without showing them to you.‎‏‎‎‏‎"</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‎‏‎‎‎‏‏‎‏‎‎‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‏‎‎‎‏‎‏‏‎‏‎‎‏‎‏‎‎receive text messages (MMS)‎‏‎‎‏‎"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‏‎‎‏‎‏‏‎‏‎‏‎‏‎‎‏‎‏‏‎‏‎‏‎‏‎‏‎‎‎‎‏‏‏‎‎‏‏‏‏‎‎‏‏‏‎‏‏‏‎‎‏‎‎‎Allows the app to receive and process MMS messages. This means the app could monitor or delete messages sent to your device without showing them to you.‎‏‎‎‏‎"</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‎‎‏‏‎‏‎‎‏‏‎‏‎‏‏‏‎‎‎‎‎‏‎‏‎‎‏‎‎‏‏‏‎‏‎‎‎‏‎‎‏‎‏‎‏‎‎‎Forward cell broadcast messages‎‏‎‎‏‎"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‎‏‎‏‎‏‎‏‏‏‎‏‏‎‎‎‎‎‎‎‏‏‎‏‎‏‏‏‎‏‏‏‎‎‎‎‎‎‎‏‏‎‏‏‏‎‎‏‎Allows the app to bind to the cell broadcast module in order to forward cell broadcast messages as they are received. Cell broadcast alerts are delivered in some locations to warn you of emergency situations. Malicious apps may interfere with the performance or operation of your device when an emergency cell broadcast is received.‎‏‎‎‏‎"</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎‏‏‏‎‎‏‏‎‎‏‏‏‏‎‎‏‏‏‎‏‎‏‏‎‏‏‏‎‎‎‏‏‎‏‎‏‏‎‏‏‏‎‏‎‏‏‎‏‎‏‏‎‎read cell broadcast messages‎‏‎‎‏‎"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‎‎‏‎‎‎‏‎‎‏‎‎‎‏‎‎‎‎‏‎‏‏‏‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‎‏‏‏‎‎‎‏‏‎Allows the app to read cell broadcast messages received by your device. Cell broadcast alerts are delivered in some locations to warn you of emergency situations. Malicious apps may interfere with the performance or operation of your device when an emergency cell broadcast is received.‎‏‎‎‏‎"</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‎‎‎‎‎‏‎‏‏‎‏‏‏‏‏‎‏‎‎‎‎‏‏‎‏‏‎‎‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‏‏‎‏‎‏‏‎‎‏‏‎‎read subscribed feeds‎‏‎‎‏‎"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‏‎‎‏‎‎‎‏‎‏‎‎‎‎‏‎‏‎‎‎‎‏‎‎‎‏‏‎‎‎‏‏‏‎‎‏‎‎‎‎‎‏‎‏‎‏‏‏‏‎‎‎Select to disable USB debugging.‎‏‎‎‏‎"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‏‎‏‏‏‎‏‏‏‎‎‎‏‎‏‏‏‎‎‏‎‎‎‎‎‏‏‎‏‎‎‏‎‏‎‏‏‏‎‎‏‎‎‏‏‎Test Harness Mode enabled‎‏‎‎‏‎"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‎‏‎‏‏‎‏‏‎‎‏‏‎‏‏‏‏‎‎‎‎‎‎‏‏‏‎‎‏‏‏‏‎Perform a factory reset to disable Test Harness Mode.‎‏‎‎‏‎"</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‎‎‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎Serial console enabled‎‏‎‎‏‎"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‏‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‎‏‏‏‏‏‎‏‎‏‎‎‏‏‎‏‏‏‎‏‎‏‏‎‏‏‏‏‏‎‎‏‎‏‏‏‎‎‏‎Performance is impacted. To disable, check bootloader.‎‏‎‎‏‎"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‎‎‏‎‎‏‏‎‎‏‎‏‏‎‏‎‎‏‎‎‎‎‏‏‏‎‏‎‏‎‏‏‎‎‏‏‎‏‏‎‎‎‏‏‎‏‏‏‏‎‏‏‎‏‎Liquid or debris in USB port‎‏‎‎‏‎"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‎‎‎‏‏‎‏‎‎‎‎‏‏‏‎‎‎‏‎‎‏‎‏‏‎‏‎‏‎‎‏‏‏‎‏‎‏‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‎‏‎‎USB port is automatically disabled. Tap to learn more.‎‏‎‎‏‎"</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‏‏‎‎‏‎‏‎‏‎‎‎‎‏‏‎‏‎‏‏‏‏‎‎‏‎‏‏‎‎‎‏‎‎‏‏‏‏‎‏‎‎‎‎‏‎‎‎‏‎‏‎‏‎OK to use USB port‎‏‎‎‏‎"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‎‏‎‏‎‎‏‏‎‎‏‎‏‏‎‎‏‎‏‏‏‏‏‎‎‏‏‎‎‎‏‎‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‎Uncategorized‎‏‎‎‏‎"</string>
<string name="importance_from_user" msgid="7318955817386549931">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‏‎‎‏‎‎‎‏‎‎‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‎‏‏‎‎‎‎‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎‏‎‏‎‏‏‎You set the importance of these notifications.‎‏‎‎‏‎"</string>
<string name="importance_from_person" msgid="9160133597262938296">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‎‎‏‎‏‏‏‎‎‎‎This is important because of the people involved.‎‏‎‎‏‎"</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‏‏‏‎‏‎‏‎‎‎‏‎‏‏‏‎‏‎‎‎‏‎‏‎‎‏‏‏‏‎‎‏‎‎‏‏‎Allow ‎‏‎‎‏‏‎<xliff:g id="APP">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to create a new User with ‎‏‎‎‏‏‎<xliff:g id="ACCOUNT">%2$s</xliff:g>‎‏‎‎‏‏‏‎ (a User with this account already exists) ?‎‏‎‎‏‎"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‏‏‎‎‏‎‏‏‏‎‎‎‎‏‏‎‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‎‏‏‎‏‏‎‏‎‏‏‏‎‏‎‎‎‏‏‎Allow ‎‏‎‎‏‏‎<xliff:g id="APP">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to create a new User with ‎‏‎‎‏‏‎<xliff:g id="ACCOUNT">%2$s</xliff:g>‎‏‎‎‏‏‏‎ ?‎‏‎‎‏‎"</string>
<string name="language_selection_title" msgid="2680677278159281088">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‏‏‎‎‏‏‏‎‏‎‏‏‎‏‏‏‎‎‏‎‎‎‎‎‎‏‏‎‏‎‎‎‏‎‎‏‎‏‎‎‎‏‏‎‏‏‏‏‎‎‎‎‎‎‎Add a language‎‏‎‎‏‎"</string>
<string name="country_selection_title" msgid="2954859441620215513">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‎‎‎‎‎‏‏‏‎‎‎‏‎‏‎‎‎‎‎‎‎‏‏‎‏‏‎‎‏‎‏‏‏‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‏‎‎‏‎Region preference‎‏‎‎‏‎"</string>
<string name="search_language_hint" msgid="7042102592055108574">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‏‏‎‏‎‏‎‎‏‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‎‏‎‎‏‏‎‏‎‏‏‎‎‎‏‏‏‏‎‏‏‏‏‎‎Type language name‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 7346bd0b3ace..feb4aeeb5396 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Permite que la aplicación reciba y procese mensajes SMS, lo que significa que podría controlar o eliminar mensajes enviados al dispositivo sin mostrártelos."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"recibir mensajes de texto (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Permite que la aplicación reciba y procese mensajes MMS, lo que significa que podría controlar o eliminar mensajes enviados al dispositivo sin mostrártelos."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Reenviar mensajes de emisión móvil"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Permite que la app se vincule al módulo de emisión móvil para reenviar los mensajes de este tipo a medida que se reciben. En algunas ubicaciones, se envían alertas de emisión móvil para advertirte en situaciones de emergencia. Es posible que las apps maliciosas interfieran con el rendimiento o el funcionamiento de tu dispositivo cuando recibes una emisión móvil de emergencia."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"Leer mensajes de difusión móvil"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite a la aplicación leer los mensajes de difusión móvil que recibe tu dispositivo. En algunas ubicaciones, las alertas de difusión móvil se envían para informar situaciones de emergencia. Las aplicaciones maliciosas pueden afectar el rendimiento o funcionamiento de tu dispositivo cuando se recibe un un mensaje de difusión móvil de emergencia."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"leer canales suscritos"</string>
@@ -1357,10 +1359,12 @@
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Se detectó un accesorio de audio analógico"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"El dispositivo adjunto no es compatible con este teléfono. Presiona para obtener más información."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depuración por USB conectada"</string>
- <string name="adb_active_notification_message" msgid="7463062450474107752">"Presiona para desactivar la depuración USB"</string>
+ <string name="adb_active_notification_message" msgid="7463062450474107752">"Presiona para desactivar la depuración por USB"</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Seleccionar para desactivar la depuración por USB"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Se habilitó el modo de agente de prueba"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Restablece la configuración de fábrica para inhabilitar el modo de agente de prueba."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Se habilitó la consola en serie"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Afecta el rendimiento. Para inhabilitarla, verifica bootloader."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Hay líquido o suciedad en el puerto USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"El puerto USB se inhabilitó automáticamente. Presiona para obtener más información."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Se puede usar el puerto USB"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Sin categoría"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Estableciste la importancia de estas notificaciones."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Es importante debido a las personas involucradas."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"¿Quieres permitir que <xliff:g id="APP">%1$s</xliff:g> cree un usuario nuevo con <xliff:g id="ACCOUNT">%2$s</xliff:g>? (Ya existe un usuario con esta cuenta)"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"¿Deseas permitir que <xliff:g id="APP">%1$s</xliff:g> cree un usuario nuevo con <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Agregar un idioma"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Preferencia de región"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Nombre del idioma"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 6c1c99230adb..75510b3c7395 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -230,7 +230,7 @@
<string name="global_action_bug_report" msgid="7934010578922304799">"Informe de error"</string>
<string name="global_action_logout" msgid="935179188218826050">"Finalizar sesión"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Captura de pantalla"</string>
- <string name="bugreport_title" msgid="5981047024855257269">"Informe de errores"</string>
+ <string name="bugreport_title" msgid="5981047024855257269">"Informar de un error"</string>
<string name="bugreport_message" msgid="398447048750350456">"Se recopilará información sobre el estado actual de tu dispositivo y se enviará por correo electrónico. Pasarán unos minutos desde que empiece a generarse el informe de errores hasta que se envíe."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Informe interactivo"</string>
<string name="bugreport_option_interactive_summary" msgid="229299488536107968">"Usa esta opción en la mayoría de los casos. Te permite realizar un seguimiento del progreso del informe, introducir más información sobre el problema y hacer capturas de pantalla. Es posible que se omitan algunas secciones menos utilizadas y que requieran más tiempo."</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Permite que la aplicación reciba y procese mensajes MMS, lo que significa que podría utilizar este permiso para controlar o eliminar mensajes enviados al dispositivo sin mostrárselos al usuario."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"recibir mensajes de texto (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Permite que la aplicación reciba y procese mensajes MMS, lo que significa que podría utilizar este permiso para controlar o eliminar mensajes enviados al dispositivo sin mostrárselos al usuario."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Reenviar mensajes de difusión móvil"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Permite que la aplicación se vincule con el módulo de difusión móvil para reenviar los mensajes de ese tipo en cuanto se reciben. En ciertas ubicaciones se envían alertas de difusión móvil para avisar de situaciones de emergencia. Cuando se recibe una alerta de difusión móvil de emergencia, ciertas aplicaciones malintencionadas podrían interferir en el rendimiento o en el funcionamiento del dispositivo."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"leer mensajes de difusión móvil"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite que la aplicación lea mensajes de difusión móvil que haya recibido el dispositivo. Las alertas de difusión móvil se envían en algunas ubicaciones para avisar de situaciones de emergencia. Es posible que las aplicaciones malintencionadas interfieran en el rendimiento o en el funcionamiento del dispositivo si se recibe una alerta de difusión móvil de emergencia."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"leer feeds a los que está suscrito el usuario"</string>
@@ -1189,7 +1191,7 @@
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Mostrar siempre"</string>
<string name="unsupported_compile_sdk_message" msgid="4253168368781441759">"<xliff:g id="APP_NAME">%1$s</xliff:g> se diseñó para una versión incompatible de Android OS y puede que funcione de forma inesperada. Es posible que haya una versión actualizada de la aplicación."</string>
<string name="unsupported_compile_sdk_show" msgid="2681877855260970231">"Mostrar siempre"</string>
- <string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"Comprobar actualizaciones"</string>
+ <string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"Buscar actualizaciones"</string>
<string name="smv_application" msgid="3307209192155442829">"La aplicación <xliff:g id="APPLICATION">%1$s</xliff:g> (proceso <xliff:g id="PROCESS">%2$s</xliff:g>) ha infringido su política StrictMode autoaplicable."</string>
<string name="smv_process" msgid="5120397012047462446">"El proceso <xliff:g id="PROCESS">%1$s</xliff:g> ha infringido su política StrictMode autoaplicable."</string>
<string name="android_upgrading_title" product="default" msgid="7513829952443484438">"El teléfono se está actualizando…"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Seleccionar para inhabilitar la depuración USB"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Modo de agente de prueba habilitado"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Restablece los ajustes de fábrica para inhabilitar el modo de agente de prueba."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Se ha habilitado la consola en serie"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Afecta al rendimiento. Para inhabilitarlo, comprueba el bootloader."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Se ha detectado líquido o suciedad en el puerto USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"El puerto USB se ha inhabilitado automáticamente. Toca para obtener más información."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Se puede utilizar el puerto USB"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Sin clasificar"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Tú determinas la importancia de estas notificaciones."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Esto es importante por los usuarios implicados."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"¿Permitir que <xliff:g id="APP">%1$s</xliff:g> cree otro usuario con la cuenta <xliff:g id="ACCOUNT">%2$s</xliff:g>, que ya tiene uno?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"¿Permitir que <xliff:g id="APP">%1$s</xliff:g> cree otro usuario con la cuenta <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Añadir un idioma"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Preferencia de región"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Nombre de idioma"</string>
@@ -1908,7 +1910,7 @@
<string name="work_mode_off_message" msgid="5130856710614337649">"Tus aplicaciones, notificaciones, datos y otras funciones del perfil de trabajo se activarán"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Activar"</string>
<string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Esta aplicación se ha diseñado para una versión anterior de Android y es posible que no funcione correctamente. Busca actualizaciones o ponte en contacto con el desarrollador."</string>
- <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Comprobar actualizaciones"</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Buscar actualizaciones"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Tienes mensajes nuevos"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Abre la aplicación de SMS para ver el mensaje"</string>
<string name="profile_encrypted_title" msgid="4260432497586829134">"Algunas funciones limitadas"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 28d12036a9b6..0e7befdf9227 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Võimaldab rakendusel vastu võtta ja töödelda SMS-sõnumeid. See tähendab, et rakendus võib jälgida või kustutada teie seadmele saadetud sõnumeid neid teile näitamata."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"võtke vastu tekstisõnumeid (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Võimaldab rakendusel vastu võtta ja töödelda multimeediumsõnumeid. See tähendab, et rakendus võib jälgida või kustutada teie seadmele saadetud sõnumeid neid teile näitamata."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Kärjeteadete edasisaatmine"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Võimaldab rakendusel luua ühenduse kärjeteadete mooduliga, et saabunud kärjeteateid edasi saata. Kärjeteateid edastatakse mõnes asukohas eriolukorrast teavitamiseks. Pahatahtlikud rakendused võivad seadme toimivust või tööd eriolukorra kärjeteate vastuvõtmisel segada."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"mobiilsidesõnumite lugemine"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Võimaldab rakendusel lugeda seadme vastu võetud mobiilsidesõnumeid. Mobiilsidemärguandeid edastatakse mõnes asukohas eriolukorrast teavitamiseks. Pahatahtlikud rakendused võivad segada seadme toimivust või tööd eriolukorra sõnumi vastuvõtmisel."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"loe tellitud kanaleid"</string>
@@ -1139,7 +1141,7 @@
<string name="whichOpenLinksWithApp" msgid="8225991685366651614">"Linkide avamine rakendusega <xliff:g id="APPLICATION">%1$s</xliff:g>"</string>
<string name="whichOpenHostLinksWithApp" msgid="3464470639011045589">"Ava teenuse <xliff:g id="HOST">%1$s</xliff:g> lingid rakendusega <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
<string name="whichGiveAccessToApplicationLabel" msgid="6142688895536868827">"Juudep. andmine"</string>
- <string name="whichEditApplication" msgid="144727838241402655">"Muutmine:"</string>
+ <string name="whichEditApplication" msgid="144727838241402655">"Redigeeri rakendusega:"</string>
<string name="whichEditApplicationNamed" msgid="1775815530156447790">"Muutmine rakendusega %1$s"</string>
<string name="whichEditApplicationLabel" msgid="7183524181625290300">"Muuda"</string>
<string name="whichSendApplication" msgid="5803792421724377602">"Jagamine"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Valige USB silumise keelamiseks"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Testrakendirežiim on lubatud"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Testrakendirežiimi keelamiseks taastage tehaseseaded."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Seeriakonsool on lubatud"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"See mõjutab toimivust. Keelamiseks kontrollige käivituslaadurit."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB-pordis on vedelik või mustus"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB-port on automaatselt keelatud. Puudutage lisateabe saamiseks."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB-porti tohib kasutada"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Kategoriseerimata"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Teie määrasite nende märguannete tähtsuse."</string>
<string name="importance_from_person" msgid="9160133597262938296">"See on tähtis osalevate inimeste tõttu."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Kas lubada rakendusel <xliff:g id="APP">%1$s</xliff:g> luua uus kasutaja kontoga <xliff:g id="ACCOUNT">%2$s</xliff:g> (selle kontoga kasutaja on juba olemas)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Kas lubada rakendusel <xliff:g id="APP">%1$s</xliff:g> luua uus kasutaja kontoga <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Keele lisamine"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Piirkonnaeelistus"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Sisestage keele nimi"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 9460ee702856..22391dc2ba30 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -280,37 +280,37 @@
<string name="managed_profile_label" msgid="8947929265267690522">"Aldatu laneko profilera"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontaktuak"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"atzitu kontaktuak"</string>
- <string name="permgrouprequest_contacts" msgid="6032805601881764300">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari kontaktuak atzitzeko baimena eman nahi diozu?"</string>
+ <string name="permgrouprequest_contacts" msgid="6032805601881764300">"Kontaktuak atzitzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Kokapena"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"atzitu gailuaren kokapena"</string>
- <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari gailuaren kokapena atzitzeko baimena eman nahi diozu?"</string>
+ <string name="permgrouprequest_location" msgid="3788275734953323491">"Gailuaren kokapena atzitzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgrouprequestdetail_location" msgid="1347189607421252902">"Hura erabiltzen ari zarenean soilik atzituko du aplikazioak kokapena"</string>
- <string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari gailuaren kokapena &lt;b&gt;beti&lt;/b&gt; atzitzeko baimena eman nahi diozu?"</string>
+ <string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"Gailuaren kokapena &lt;b&gt;beti&lt;/b&gt; atzitzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"Aplikazioak hura darabilzunean atzi dezake kokapena"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Egutegia"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"atzitu egutegia"</string>
- <string name="permgrouprequest_calendar" msgid="289900767793189421">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari egutegia atzitzeko baimena eman nahi diozu?"</string>
+ <string name="permgrouprequest_calendar" msgid="289900767793189421">"Egutegia atzitzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS mezuak"</string>
<string name="permgroupdesc_sms" msgid="4656988620100940350">"bidali eta ikusi SMS mezuak"</string>
- <string name="permgrouprequest_sms" msgid="7168124215838204719">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari SMS mezuak bidaltzeko eta ikusteko baimena eman nahi diozu?"</string>
+ <string name="permgrouprequest_sms" msgid="7168124215838204719">"SMS mezuak bidaltzeko eta ikusteko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Memoria"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"atzitu gailuko argazkiak, multimedia-edukia eta fitxategiak"</string>
- <string name="permgrouprequest_storage" msgid="7885942926944299560">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari gailuko argazkiak, multimedia-edukia eta fitxategiak atzitzeko baimena eman nahi diozu?"</string>
+ <string name="permgrouprequest_storage" msgid="7885942926944299560">"Gailuko argazkiak, multimedia-edukia eta fitxategiak atzitzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofonoa"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"grabatu audioa"</string>
- <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari audioa grabatzeko baimena eman nahi diozu?"</string>
+ <string name="permgrouprequest_microphone" msgid="9167492350681916038">"Audioa grabatzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgrouplab_activityRecognition" msgid="1565108047054378642">"Ariketa fisikoa"</string>
<string name="permgroupdesc_activityRecognition" msgid="6949472038320473478">"ariketa fisikoak atzitu"</string>
<string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"Zure ariketa fisikoak atzitzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
<string name="permgroupdesc_camera" msgid="3250611594678347720">"atera argazkiak eta grabatu bideoak"</string>
- <string name="permgrouprequest_camera" msgid="1299833592069671756">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari argazkiak ateratzeko eta bideoak grabatzeko baimena eman nahi diozu?"</string>
+ <string name="permgrouprequest_camera" msgid="1299833592069671756">"Argazkiak ateratzeko eta bideoak grabatzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgrouplab_calllog" msgid="8798646184930388160">"Deien erregistroa"</string>
<string name="permgroupdesc_calllog" msgid="3006237336748283775">"irakurri telefonoko deien erregistroa eta idatzi bertan"</string>
- <string name="permgrouprequest_calllog" msgid="8487355309583773267">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari telefonoko deien erregistroa atzitzeko baimena eman nahi diozu?"</string>
+ <string name="permgrouprequest_calllog" msgid="8487355309583773267">"Telefonoko deien erregistroa atzitzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Telefonoa"</string>
<string name="permgroupdesc_phone" msgid="6234224354060641055">"egin eta kudeatu telefono-deiak"</string>
- <string name="permgrouprequest_phone" msgid="9166979577750581037">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari telefono-deiak egiteko eta kudeatzeko baimena eman nahi diozu?"</string>
+ <string name="permgrouprequest_phone" msgid="9166979577750581037">"Telefono-deiak egiteko eta kudeatzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgrouplab_sensors" msgid="4838614103153567532">"Gorputz-sentsoreak"</string>
<string name="permgroupdesc_sensors" msgid="7147968539346634043">"atzitu bizi-konstanteei buruzko sentsorearen datuak"</string>
<string name="permgrouprequest_sensors" msgid="6349806962814556786">"Bizi-konstanteei buruzko sentsorearen datuak atzitzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"SMS mezuak jasotzeko eta prozesatzeko baimena ematen die aplikazioei. Horrela, aplikazioak gailura bidalitako mezuak kontrola eta ezaba ditzake zuri erakutsi gabe."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"jaso testu-mezuak (MMSak)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"MMS mezuak jasotzeko eta prozesatzeko baimena ematen die aplikazioei. Horrela, aplikazioak gailura bidalitako mezuak kontrola eta ezaba ditzake zuri erakutsi gabe."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Desbideratu sare mugikor bidezko igorpen-mezuak"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Sare mugikor bidezko igorpen-modulura lotzeko baimena ematen dio aplikazioari, sare mugikor bidezko igorpen-mezuak jaso ahala desbideratu ahal izateko. Sare mugikor bidezko igorpen-alertak kokapen batzuetan entregatzen dira larrialdi-egoeren berri emateko. Sare mugikor bidezko larrialdi-igorpenak jasotzean, aplikazio maltzurrek gailuaren errendimenduari edota funtzionamenduari eragin diezaiokete."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"irakurri sare mugikor bidezko igorpen-mezuak"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Gailuak jasotako sare mugikor bidezko igorpenen mezuak irakurtzeko baimena ematen die aplikazioei. Sare mugikor bidezko igorpen-alertak kokapen batzuetan ematen dira larrialdi-egoeren berri emateko. Aplikazio gaiztoek gailuaren errendimendua edo funtzionamendua oztopa dezakete larrialdi-igorpen horietako bat jasotzen denean."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"irakurri harpidetutako jarioak"</string>
@@ -512,10 +514,10 @@
<string name="permdesc_requestPasswordComplexity" msgid="4730994229754212347">"Pantailaren blokeoaren konplexutasun-maila (handia, ertaina, txikia edo bat ere ez) jakiteko aukera ematen dio aplikazioari. Informazio horrekin, pantailaren blokeoaren luzera-barruti edo mota posiblea ondoriozta liteke. Halaber, pantailaren blokeoa maila jakin batera igotzeko iradoki diezaieke aplikazioak erabiltzaileei, baina horri ez ikusi egin eta aplikazioa erabiltzen jarraitzeko aukera dute erabiltzaileek. Kontuan izan pantailaren blokeoa ez dela gordetzen testu arrunt gisa; beraz, aplikazioak ez du jakingo pasahitz zehatza zein den."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"Erabili hardware biometrikoa"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Autentifikatzeko hardware biometrikoa erabiltzeko baimena ematen die aplikazioei."</string>
- <string name="permlab_manageFingerprint" msgid="5640858826254575638">"kudeatu erreferentzia-gako digitalen hardwarea"</string>
+ <string name="permlab_manageFingerprint" msgid="5640858826254575638">"kudeatu hatz-marken hardwarea"</string>
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Erreferentzia-gako digitalen txantiloiak gehitzeko eta ezabatzeko metodoei dei egitea baimentzen die aplikazioei."</string>
- <string name="permlab_useFingerprint" msgid="3150478619915124905">"erabili erreferentzia-gako digitalen hardwarea"</string>
- <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Autentifikatzeko erreferentzia-gako digitalen hardwarea erabiltzeko baimena ematen die aplikazioei."</string>
+ <string name="permlab_useFingerprint" msgid="3150478619915124905">"erabili hatz-marken hardwarea"</string>
+ <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Autentifikatzeko hatz-marken hardwarea erabiltzeko baimena ematen die aplikazioei."</string>
<string name="permlab_audioWrite" msgid="2661772059799779292">"musika-bilduma aldatu"</string>
<string name="permdesc_audioWrite" msgid="8888544708166230494">"Musika-bilduma aldatzeko baimena ematen die aplikazioei."</string>
<string name="permlab_videoWrite" msgid="128769316366746446">"bideo-bilduma aldatu"</string>
@@ -540,7 +542,7 @@
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Autentifikatu da hatz-marka"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Autentifikatu da aurpegia"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Autentifikatu da aurpegia; sakatu Berretsi"</string>
- <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hatz-markaren hardwarea ez dago erabilgarri."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hatz-marken hardwarea ez dago erabilgarri."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Ezin da gorde hatz-marka digitala. Kendu lehendik gordeta duzunetako bat."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Hatz-markak prozesatzeko denbora-muga gainditu da. Saiatu berriro."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Hatz-markaren eragiketa bertan behera utzi da."</string>
@@ -1260,7 +1262,7 @@
<string name="wifi_suggestion_title" msgid="6396033039578436801">"Iradokitako wifi-sareak baimendu nahi dituzu?"</string>
<string name="wifi_suggestion_content" msgid="5603992011371520746">"<xliff:g id="NAME">%s</xliff:g> aplikazioak sare batzuk iradoki ditu. Baliteke gailua automatikoki konektatzea."</string>
<string name="wifi_suggestion_action_allow_app" msgid="7978995387498669901">"Baimendu"</string>
- <string name="wifi_suggestion_action_disallow_app" msgid="6434097275967940372">"Ez. Eskerrik asko."</string>
+ <string name="wifi_suggestion_action_disallow_app" msgid="6434097275967940372">"Ez, eskerrik asko"</string>
<string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"Wi‑Fi konexioa automatikoki aktibatuko da"</string>
<string name="wifi_wakeup_onboarding_subtext" msgid="3989697580301186973">"Gordeta daukazun kalitate handiko sare batetik gertu zaudenean"</string>
<string name="wifi_wakeup_onboarding_action_disable" msgid="838648204200836028">"Ez aktibatu berriro"</string>
@@ -1362,6 +1364,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Hautatu USB arazketa desgaitzeko."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Proba-materialeko modua gaitu da"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Proba-materialaren modua desgaitzeko, berrezarri jatorrizko datuak."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Serie-kontsola gaituta dago"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Funtzionamenduari eragiten dio. Desgaitzeko, joan abiarazlera."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Likidoa edo zikinkeriak daude USB atakan"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB ataka automatikoki desgaitu da. Informazio gehiago lortzeko, sakatu hau."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Erabiltzeko moduan dago USB ataka"</string>
@@ -1548,7 +1552,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Aukera gehiago"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <string name="storage_internal" msgid="3570990907910199483">"Barneko biltegiratze partekatua"</string>
+ <string name="storage_internal" msgid="3570990907910199483">"Barneko memoria partekatua"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD txartela"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD txartela"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB bidezko unitatea"</string>
@@ -1891,10 +1895,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Kategoriarik gabea"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Zuk ezarri duzu jakinarazpen hauen garrantzia."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Garrantzitsua da eragiten dien pertsonengatik."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="ACCOUNT">%2$s</xliff:g> kontua duen erabiltzailea sortzeko baimena eman nahi diozu <xliff:g id="APP">%1$s</xliff:g> aplikazioari? (Badago kontu hori duen erabiltzaile bat)"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"<xliff:g id="ACCOUNT">%2$s</xliff:g> kontua duen erabiltzailea sortzeko baimena eman nahi diozu <xliff:g id="APP">%1$s</xliff:g> aplikazioari?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Gehitu hizkuntza"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Lurralde-hobespena"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Adierazi hizkuntza"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index ec5dbcb20151..33421c0448c8 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"به برنامه اجازه می‌دهد پیامک‌ها را دریافت و پردازش کند. این یعنی برنامه می‌تواند پیام‌های ارسالی به دستگاه شما را بدون نمایش آن‌ها به شما حذف یا کنترل کند."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"دریافت پیام‌های نوشتاری (فراپیام)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"به برنامه اجازه می‌دهد پیام‌های فراپیام را دریافت و پردازش کند. این یعنی برنامه می‌تواند پیام‌های ارسالی به دستگاه شما را بدون نمایش آن‌ها به شما حذف یا کنترل کند."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"بازارسال پیام‌های پخش سلولی"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"به برنامه امکان می‌دهد به مدول پخش سلولی متصل شود تا پیام‌های پخش سلولی را به محض دریافت بازارسال کند. هشدارهای پخش سلولی در برخی از موقعیت‌های مکانی ارسال می‌شوند تا موقعیت‌های اضطراری را به شما اعلام کنند. وقتی پخش سلولی دریافت می‌شود، ممکن است برنامه‌های مخرب در عملکرد یا کارکرد دستگاه شما اختلال ایجاد کنند."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"خواندن پیام‌های پخش سلولی"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"‏به برنامه اجازه می‎دهد پیام‌های پخش سلولی دستگاه شما را بخواند. هشدارهای پخش سلولی در برخی از موقعیت‌های مکانی تحویل داده می‎شوند تا موقعیت‌های اضطراری را به شما اعلام کنند. وقتی پخش سلولی دریافت می‎شود، ممکن است برنامه‎های مخرب در عملکرد یا کارکرد دستگاه شما اختلال ایجاد کنند."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"خواندن فیدهای مشترک"</string>
@@ -549,7 +551,7 @@
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"تلاش‌های بسیاری زیادی انجام شده است. حسگر اثر انگشت غیرفعال شد."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"دوباره امتحان کنید."</string>
<string name="fingerprint_error_no_fingerprints" msgid="7654382120628334248">"اثر انگشتی ثبت نشده است."</string>
- <string name="fingerprint_error_hw_not_present" msgid="409523969613176352">"این دستگاه حسگر اثرانگشت ندارد."</string>
+ <string name="fingerprint_error_hw_not_present" msgid="409523969613176352">"این دستگاه حسگر اثر انگشت ندارد."</string>
<string name="fingerprint_name_template" msgid="5870957565512716938">"انگشت <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"‏انتخاب کنید تا رفع عیب USB غیرفعال شود."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"«حالت مجموعه داده‌های تست» فعال شد"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"برای غیرفعال کردن «حالت مجموعه داده‌های تست»، بازنشانی کارخانه‌ای کنید."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"کنسول سریال فعال است"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"‏عملکرد تحت‌تأثیر قرار گرفته است. برای غیرفعال کردن، bootloader را بررسی کنید."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"‏مایعات یا خاکروبه در درگاه USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"‏درگاه USB به‌طور خودکار غیرفعال شده است. برای اطلاعات بیشتر، ضربه بزنید."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"‏می‌توان از درگاه USB استفاده کرد"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"دسته‌بندی‌نشده"</string>
<string name="importance_from_user" msgid="7318955817386549931">"شما اهمیت این اعلان‌ها را تنظیم می‌کنید."</string>
<string name="importance_from_person" msgid="9160133597262938296">"به دلیل افراد درگیر مهم است."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"به<xliff:g id="APP">%1$s</xliff:g> اجازه می‌دهید با <xliff:g id="ACCOUNT">%2$s</xliff:g> (کاربری با این حساب درحال‌حاضر وجود دارد) کاربری جدید ایجاد کند؟"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"به <xliff:g id="APP">%1$s</xliff:g> اجازه می‌دهید با <xliff:g id="ACCOUNT">%2$s</xliff:g> کاربری جدید ایجاد کند؟"</string>
<string name="language_selection_title" msgid="2680677278159281088">"افزودن زبان"</string>
<string name="country_selection_title" msgid="2954859441620215513">"اولویت‌های منطقه"</string>
<string name="search_language_hint" msgid="7042102592055108574">"نام زبان را تایپ کنید"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index a94253b9e68f..124a16071f4c 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -332,7 +332,7 @@
<string name="permdesc_statusBarService" msgid="716113660795976060">"Antaa sovelluksen sijaita tilapalkissa."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"laajentaa/tiivistää tilarivin"</string>
<string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Antaa sovelluksen laajentaa tai tiivistää tilarivin."</string>
- <string name="permlab_install_shortcut" msgid="4279070216371564234">"asenna pikakuvakkeita"</string>
+ <string name="permlab_install_shortcut" msgid="4279070216371564234">"asentaa pikakuvakkeita"</string>
<string name="permdesc_install_shortcut" msgid="8341295916286736996">"Antaa sovelluksen lisätä aloitusruudun pikakuvakkeita ilman käyttäjän toimia."</string>
<string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"poista pikakuvakkeita"</string>
<string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Antaa sovelluksen poistaa aloitusruudun pikakuvakkeita ilman käyttäjän toimia."</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Antaa sovelluksen vastaanottaa ja käsitellä tekstiviestejä. Sovellus voi valvoa tai poistaa laitteeseesi lähetettyjä viestejä näyttämättä niitä sinulle."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"vastaanota tekstiviestejä (multimedia)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Antaa sovelluksen vastaanottaa ja käsitellä multimediaviestejä. Sovellus voi valvoa tai poistaa laitteeseesi lähetettyjä viestejä näyttämättä niitä sinulle."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Lähetä solulähetysviestit edelleen"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Sallii sovelluksen sitoutua solulähetysmoduuliin lähettääkseen solulähetysviestejä edelleen sitä mukaa kun ne saapuvat. Solulähetysilmoitusten avulla ilmoitetaan hätätilanteista joissakin paikoissa. Haitalliset sovellukset voivat häiritä laitteen toimintaa laitteen vastaanottaessa hätätilanteeseen liittyvän solulähetysviestin."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"lue tiedotteita"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Antaa sovelluksen lukea laitteesi vastaanottamia tiedotteita. Tiedotteiden avulla ilmoitetaan hätätilanteista joissakin paikoissa. Haitalliset sovellukset voivat häiritä laitteen toimintaa laitteen vastaanottaessa hätätiedotteen."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"lukea tilattuja syötteitä"</string>
@@ -382,7 +384,7 @@
<string name="permdesc_getPackageSize" msgid="3921068154420738296">"Antaa sovelluksen noutaa sen koodin, tietojen ja välimuistin koot."</string>
<string name="permlab_writeSettings" msgid="2226195290955224730">"muokkaa järjestelmän asetuksia"</string>
<string name="permdesc_writeSettings" msgid="7775723441558907181">"Antaa sovelluksen muokata järjestelmän asetustietoja. Haitalliset sovellukset voivat vahingoittaa järjestelmän määritystä."</string>
- <string name="permlab_receiveBootCompleted" msgid="5312965565987800025">"suorita laitteen käynnistyessä"</string>
+ <string name="permlab_receiveBootCompleted" msgid="5312965565987800025">"suorittaa laitteen käynnistyessä"</string>
<string name="permdesc_receiveBootCompleted" product="tablet" msgid="7390304664116880704">"Antaa sovelluksen käynnistyä heti, kun laite on käynnistynyt. Tämä voi pidentää tablet-laitteen käynnistysaikaa ja hidastaa sen yleistä käyttöä sovelluksen ollessa aina käynnissä."</string>
<string name="permdesc_receiveBootCompleted" product="tv" msgid="6725487837446317527">"Antaa sovelluksen käynnistyä heti, kun laite on käynnistynyt. Tämä voi pidentää Android TV ‑laitteen käynnistysaikaa ja hidastaa sen yleistä käyttöä sovelluksen ollessa aina käynnissä."</string>
<string name="permdesc_receiveBootCompleted" product="default" msgid="513950589102617504">"Antaa sovelluksen käynnistyä heti, kun laite on käynnistynyt. Tämä voi pidentää puhelimen käynnistysaikaa ja hidastaa puhelimen yleistä käyttöä sovelluksen ollessa aina käynnissä."</string>
@@ -436,7 +438,7 @@
<string name="permdesc_camera" msgid="5392231870049240670">"Tämä sovellus voi ottaa kameralla kuvia ja videoita koska tahansa."</string>
<string name="permlab_systemCamera" msgid="4074081285026193898">"Salli sovellukselle tai palvelulle pääsy järjestelmän kameroihin, jotta se voi ottaa kuvia ja nauhoittaa videoita"</string>
<string name="permdesc_systemCamera" msgid="6488131672529669229">"Tämä käyttöoikeuden saanut | järjestelmäsovellus voi ottaa järjestelmän kameralla kuvia ja videoita koska tahansa. Sovelluksella on oltava myös android.permission.CAMERA-käyttöoikeus"</string>
- <string name="permlab_vibrate" msgid="7696427026057705834">"hallitse värinää"</string>
+ <string name="permlab_vibrate" msgid="7696427026057705834">"hallita värinää"</string>
<string name="permdesc_vibrate" msgid="6284989245902300945">"Antaa sovelluksen hallita värinää."</string>
<string name="permlab_callPhone" msgid="3925836347681847954">"soittaa puhelinnumeroihin suoraan"</string>
<string name="permdesc_callPhone" msgid="3740797576113760827">"Antaa sovelluksen soittaa puhelinnumeroihin kysymättä sinulta. Tämä voi aiheuttaa odottamattomia kuluja tai puheluita. Huomaa, että tämä ei anna sovellukselle lupaa soittaa hätänumeroihin. Haitalliset sovellukset voivat aiheuttaa sinulle kuluja soittamalla puheluita ilman lupaa."</string>
@@ -474,7 +476,7 @@
<string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"Antaa sovelluksen tarkastella tablet-laitteeseen tallennettuja tilejä. Näihin voivat kuulua myös asentamiesi sovelluksien luomat tilit."</string>
<string name="permdesc_getAccounts" product="tv" msgid="1394648459318596337">"Antaa sovelluksen noutaa Android TV ‑laitteen tuntemien tilien listan. Tileihin voi kuulua myös asennettujen sovellusten luomia tilejä."</string>
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Antaa sovelluksen tarkastella puhelimeen tallennettuja tilejä. Näihin voivat kuulua myös asentamiesi sovelluksien luomat tilit."</string>
- <string name="permlab_accessNetworkState" msgid="4951027964348974773">"tarkastele verkkoyhteyksiä"</string>
+ <string name="permlab_accessNetworkState" msgid="4951027964348974773">"tarkastella verkkoyhteyksiä"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Antaa sovelluksen tarkastella verkkoyhteyksiä koskevia tietoja, kuten mitä verkkoja on olemassa ja mihin on muodostettu yhteys."</string>
<string name="permlab_createNetworkSockets" msgid="7934516631384168107">"saada täydet verkon käyttöoikeudet"</string>
<string name="permdesc_createNetworkSockets" msgid="3403062187779724185">"Antaa sovelluksen luoda verkkovastakkeita ja käyttää muokattuja verkkoprotokollia. Tietoja voidaan lähettää verkkoon selaimen ja muiden sovellusten avulla, joten tätä lupaa ei tarvita tietojen lähettämiseksi."</string>
@@ -482,7 +484,7 @@
<string name="permdesc_changeNetworkState" msgid="6789123912476416214">"Antaa sovelluksen muuttaa verkkoyhteyden tilaa."</string>
<string name="permlab_changeTetherState" msgid="5952584964373017960">"internetyhteyden jakamisen muuttaminen"</string>
<string name="permdesc_changeTetherState" msgid="1524441344412319780">"Antaa sovelluksen muuttaa internetyhteyden jakamisen tilaa."</string>
- <string name="permlab_accessWifiState" msgid="5202012949247040011">"näytä Wi-Fi-yhteydet"</string>
+ <string name="permlab_accessWifiState" msgid="5202012949247040011">"nähdä Wi-Fi-yhteydet"</string>
<string name="permdesc_accessWifiState" msgid="5002798077387803726">"Antaa sovelluksen tarkastella Wi-Fi-verkkoja koskevia tietoja, kuten onko Wi-Fi käytössä ja mihin Wi-Fi-laitteisiin on muodostettu yhteys."</string>
<string name="permlab_changeWifiState" msgid="6550641188749128035">"muodosta ja katkaise Wi-Fi-yhteys"</string>
<string name="permdesc_changeWifiState" msgid="7137950297386127533">"Antaa sovelluksen yhdistää Wi-Fi-tukiasemiin tai katkaista yhteyden sekä tehdä muutoksia määritettyihin Wi-Fi-verkkoihin."</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Poista USB-vianetsintä käytöstä valitsemalla tämä."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Testikehystila käytössä"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Palauta tehdasasetukset, niin voit poistaa testikehystilan käytöstä."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Sarjakonsoli käytössä"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Tämä vaikuttaa suorituskykyyn. Jos haluat poistaa toiminnon käytöstä, tarkista käynnistysohjelma."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Nestettä tai likaa USB-portissa"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB-portti poistetaan käytöstä automaattisesti. Napauta nähdäksesi lisätietoja."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB-portin käyttö on sallittu"</string>
@@ -1629,7 +1633,7 @@
<string name="kg_pattern_instructions" msgid="398978611683075868">"Piirrä kuvio"</string>
<string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Anna SIM-kortin PIN-koodi"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Anna PIN-koodi"</string>
- <string name="kg_password_instructions" msgid="5753646556186936819">"Anna salasana"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Lisää salasana"</string>
<string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM-kortti on nyt poistettu käytöstä. Jatka antamalla PUK-koodi. Saat lisätietoja ottamalla yhteyttä operaattoriin."</string>
<string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Anna haluamasi PIN-koodi"</string>
<string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Vahvista haluamasi PIN-koodi"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Luokittelematon"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Voit valita näiden ilmoitusten tärkeyden."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Tämä on tärkeää siihen liittyvien ihmisten perusteella."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Saako <xliff:g id="APP">%1$s</xliff:g> luoda uuden käyttäjän (<xliff:g id="ACCOUNT">%2$s</xliff:g>) – tällä käyttäjällä on jo tili?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Saako <xliff:g id="APP">%1$s</xliff:g> luoda uuden käyttäjän (<xliff:g id="ACCOUNT">%2$s</xliff:g>)?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Lisää kieli"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Alueasetus"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Anna kielen nimi"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 94f6acbdd080..ecfdcd29e785 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Permet à l\'application de recevoir et de traiter les messages texte. Cette autorisation lui donne la possibilité de surveiller ou de supprimer les messages envoyés à votre appareil sans vous les montrer."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"recevoir des messages multimédias"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Permet à l\'application de recevoir et de traiter les messages multimédias. Cette autorisation lui donne la possibilité de surveiller ou de supprimer les messages envoyés à votre appareil sans vous les montrer."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Transférer les messages de diffusion cellulaire"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Permet à l\'application d\'établir un lien avec le module de diffusion cellulaire afin de transférer les messages de diffusion cellulaire à mesure de leur réception. Dans certaines régions, des alertes de diffusion cellulaire sont envoyées afin de vous avertir de situations d\'urgence. Des applications malveillantes peuvent interférer avec les performances ou le fonctionnement de votre appareil lors de la réception d\'une alerte d\'urgence par diffusion cellulaire."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"lire les messages de diffusion cellulaire"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permet à l\'application de lire les messages de diffusion cellulaire que votre appareil reçoit. Dans certaines zones géographiques, des alertes vous sont envoyées afin de vous prévenir en cas de situation d\'urgence. Des applications malveillantes peuvent venir perturber les performances ou le fonctionnement de votre appareil lors de la réception d\'un message de diffusion cellulaire."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"lire les flux auxquels vous êtes abonné"</string>
@@ -530,9 +532,9 @@
<string name="biometric_not_recognized" msgid="5770511773560736082">"Données biométriques non reconnues"</string>
<string name="biometric_error_canceled" msgid="349665227864885880">"Authentification annulée"</string>
<string name="biometric_error_device_not_secured" msgid="6583143098363528349">"Aucun NIP, schéma ou mot de passe défini"</string>
- <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Empreinte digitale partielle détectée. Veuillez essayer de nouveau."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Empreinte digitale partielle détectée. Veuillez réessayer."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Impossible de reconnaître l\'empreinte digitale. Veuillez réessayer."</string>
- <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Le capteur d\'empreintes digitales est sale. Veuillez le nettoyer et essayer de nouveau."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Le capteur d\'empreintes digitales est sale. Veuillez le nettoyer et réessayer."</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Vous avez déplacé votre doigt trop rapidement. Veuillez réessayer."</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Vous avez déplacé votre doigt trop lentement. Veuillez réessayer."</string>
<string-array name="fingerprint_acquired_vendor">
@@ -540,10 +542,10 @@
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Empreinte digitale authentifiée"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Visage authentifié"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Visage authentifié, veuillez appuyer sur le bouton Confirmer"</string>
- <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Matériel d\'empreinte numérique indisponible."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Matériel d\'empreinte digitale numérique indisponible."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"L\'empreinte digitale ne peut pas être enregistrée. Veuillez supprimer une empreinte existante."</string>
- <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Le temps attribué pour lire l\'empreinte est écoulé. Veuillez essayer de nouveau."</string>
- <string name="fingerprint_error_canceled" msgid="4402024612660774395">"Opération d\'empreinte numérique annulée."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Le temps attribué pour lire l\'empreinte digitale est écoulé. Veuillez réessayer."</string>
+ <string name="fingerprint_error_canceled" msgid="4402024612660774395">"Opération d\'empreinte digitale numérique annulée."</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"L\'opération d\'empreinte digitale a été annulée par l\'utilisateur."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Trop de tentatives. Veuillez réessayer plus tard."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Trop de tentatives. Capteur d\'empreintes digitales désactivé."</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Sélectionnez cette option pour désactiver le débogage USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Mode Logiciel de test activé"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Effectuez une réinitialisation pour désactiver le mode Logiciel de test."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"La console série est activée"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"La performance est réduite. Pour désactiver cette fonction, vérifier le programme d\'amorçage."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Liquide ou débris dans le port USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Le port USB est désactivé automatiquement. Touchez ici pour en savoir plus."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Autorisation d\'utiliser le port USB"</string>
@@ -1579,7 +1583,7 @@
<string name="expires_on" msgid="3676242949915959821">"Expire le :"</string>
<string name="serial_number" msgid="758814067660862493">"Numéro de série :"</string>
<string name="fingerprints" msgid="4516019619850763049">"Empreintes :"</string>
- <string name="sha256_fingerprint" msgid="4391271286477279263">"Empreinte SHA-256 :"</string>
+ <string name="sha256_fingerprint" msgid="4391271286477279263">"Empreinte digitale SHA-256 :"</string>
<string name="sha1_fingerprint" msgid="7930330235269404581">"Empreinte SHA-1 :"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Tout afficher"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Sélectionnez une activité"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Sans catégorie"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Vous définissez l\'importance de ces notifications."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Ces notifications sont importantes en raison des participants."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Autoriser <xliff:g id="APP">%1$s</xliff:g> à créer un utilisateur <xliff:g id="ACCOUNT">%2$s</xliff:g>? (Un utilisateur est déjà associé à ce compte)"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Autoriser <xliff:g id="APP">%1$s</xliff:g> à créer un profil d\'utilisateur avec le compte <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Ajouter une langue"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Préférences régionales"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Entrez la langue"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 11ba7dc7911b..1a44b799cbdd 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Permet à l\'application de recevoir et de traiter les SMS. Cette autorisation lui donne la possibilité de surveiller ou supprimer les messages envoyés à votre appareil sans vous les montrer."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"recevoir des messages texte (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Permet à l\'application de recevoir et de traiter les MMS. Cette autorisation lui donne la possibilité de surveiller ou supprimer les messages envoyés à votre appareil sans vous les montrer."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Transférer les messages reçus via un canal de diffusion cellulaire"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Autorise l\'application à établir une connexion avec le module de diffusion cellulaire afin de transférer les messages reçus via un canal de diffusion cellulaire. Des alertes de diffusion cellulaire sont générées dans certaines régions afin de vous avertir de situations d\'urgence. Des applications malveillantes peuvent interférer avec les performances ou le fonctionnement de votre appareil lors de la réception d\'une alerte d\'urgence par diffusion cellulaire."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"lire les messages reçus via un canal de diffusion cellulaire"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permet à l\'application de lire les messages que votre appareil reçoit via un canal de diffusion cellulaire. Dans certaines zones géographiques, des alertes vous sont envoyées afin de vous prévenir en cas de situation d\'urgence. Les applications malveillantes peuvent venir perturber les performances ou le fonctionnement de votre appareil lorsqu\'un message est reçu via un canal de diffusion cellulaire."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"lire les flux auxquels vous êtes abonné"</string>
@@ -530,7 +532,7 @@
<string name="biometric_not_recognized" msgid="5770511773560736082">"Non reconnu"</string>
<string name="biometric_error_canceled" msgid="349665227864885880">"Authentification annulée"</string>
<string name="biometric_error_device_not_secured" msgid="6583143098363528349">"Aucun code, schéma ni mot de passe n\'est défini"</string>
- <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Empreinte numérique partiellement détectée. Veuillez réessayer."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Empreinte digitale partiellement détectée. Veuillez réessayer."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Impossible de reconnaître l\'empreinte digitale. Veuillez réessayer."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Le lecteur d\'empreinte digitale est sale. Veuillez le nettoyer, puis réessayer."</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Vous avez déplacé votre doigt trop rapidement. Veuillez réessayer."</string>
@@ -540,10 +542,10 @@
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Empreinte digitale authentifiée"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Visage authentifié"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Visage authentifié, veuillez appuyer sur \"Confirmer\""</string>
- <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Matériel d\'empreinte numérique indisponible."</string>
- <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Impossible d\'enregistrer l\'empreinte numérique. Veuillez supprimer une empreinte."</string>
- <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Délai de détection de l\'empreinte numérique expiré. Veuillez réessayer."</string>
- <string name="fingerprint_error_canceled" msgid="4402024612660774395">"Opération d\'empreinte numérique annulée."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Matériel d\'empreinte digitale indisponible."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Impossible d\'enregistrer l\'empreinte digitale. Veuillez en supprimer une autre."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Délai de détection de l\'empreinte digitale expiré. Veuillez réessayer."</string>
+ <string name="fingerprint_error_canceled" msgid="4402024612660774395">"Opération d\'empreinte digitale annulée."</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Opération d\'authentification par empreinte digitale annulée par l\'utilisateur."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Trop de tentatives. Veuillez réessayer plus tard."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Trop de tentatives. Lecteur d\'empreinte digitale désactivé."</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Sélectionnez cette option pour désactiver le débogage USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Mode Atelier de test activé"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Rétablissez la configuration d\'usine pour désactiver le mode Atelier de test."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Console série activée"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Les performances sont affectées. Pour désactiver la console série, vérifiez le bootloader."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Présence de liquide ou de saletés dans le port USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Le port USB est désactivé automatiquement. Appuyez sur cette notification pour en savoir plus."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Le port USB peut être utilisé"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Sans catégorie"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Vous définissez l\'importance de ces notifications."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Ces notifications sont importantes en raison des participants."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Autoriser <xliff:g id="APP">%1$s</xliff:g> à créer un profil utilisateur avec le compte <xliff:g id="ACCOUNT">%2$s</xliff:g> (un utilisateur associé à ce compte existe déjà) ?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Autoriser <xliff:g id="APP">%1$s</xliff:g> à créer un profil utilisateur avec le compte <xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Ajouter une langue"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Préférences régionales"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Saisissez la langue"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 1becada75cac..933fbabefe90 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Permite á aplicación recibir e procesar mensaxes SMS. Isto significa que a aplicación pode supervisar ou eliminar mensaxes enviadas ao teu dispositivo sen mostrarchas."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"recibir mensaxes de texto (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Permite á aplicación recibir e procesar mensaxes MMS. Isto significa que a aplicación pode supervisar ou eliminar mensaxes enviadas ao teu dispositivo sen mostrarchas."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Reenviar mensaxes de difusión móbil"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Permite que a aplicación se vincule ao módulo de difusión móbil para reenviar as mensaxes deste tipo segundo se reciban. As alertas de difusión móbil envíanse nalgunhas localizacións para avisar de situacións de emerxencia. As aplicacións maliciosas poden interferir no rendemento ou funcionamento do teu dispositivo cando se reciba unha difusión móbil de emerxencia."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"ler mensaxes de difusión móbil"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite á aplicación ler mensaxes de difusión móbil recibidas polo teu dispositivo. As alertas de difusión móbil envíanse nalgunhas localizacións para avisar de situacións de emerxencia. É posible que aplicacións maliciosas afecten ao rendemento ou funcionamento do teu dispositivo cando se recibe unha difusión móbil de emerxencia."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"ler feeds subscritos"</string>
@@ -1362,6 +1364,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Selecciona a opción para desactivar a depuración por USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Activouse o modo de axente de proba"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Restablece a configuración de fábrica para desactivar o modo de axente de proba."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"A consola de serie está activada"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"O rendemento vese afectado. Para desactivar a consola, comproba o cargador de arranque."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Hai líquido ou residuos no porto USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"O porto USB desactivouse automaticamente. Toca para obter máis información."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Pódese utilizar o porto USB"</string>
@@ -1891,10 +1895,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Sen clasificar"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Ti defines a importancia destas notificacións."</string>
<string name="importance_from_person" msgid="9160133597262938296">"É importante polas persoas involucradas."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Queres permitir que <xliff:g id="APP">%1$s</xliff:g> cree un usuario novo con <xliff:g id="ACCOUNT">%2$s</xliff:g>? (Xa existe un usuario con esta conta)"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Queres permitir que <xliff:g id="APP">%1$s</xliff:g> cree un usuario novo con <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Engadir un idioma"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Preferencia de rexión"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Nome do idioma"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 09661c15afe8..05e5a4eb0965 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -295,10 +295,10 @@
<string name="permgrouprequest_sms" msgid="7168124215838204719">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને SMS સંદેશા મોકલવા અને જોવાની મંજૂરી આપીએ?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"સ્ટોરેજ"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"તમારા ઉપકરણ પર ફોટો, મીડિયા અને ફાઇલો ઍક્સેસ કરવાની"</string>
- <string name="permgrouprequest_storage" msgid="7885942926944299560">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને તમારા ઉપકરણ પર ફોટા, મીડિયા અને ફાઇલોને ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string>
+ <string name="permgrouprequest_storage" msgid="7885942926944299560">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને તમારા ડિવાઇસ પર ફોટા, મીડિયા અને ફાઇલોને ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"માઇક્રોફોન"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"ઑડિઓ રેકોર્ડ કરવાની"</string>
- <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને ઑડિઓ રેકૉર્ડ કરવાની મંજૂરી આપીએ?"</string>
+ <string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને ઑડિયો રેકૉર્ડ કરવાની મંજૂરી આપીએ?"</string>
<string name="permgrouplab_activityRecognition" msgid="1565108047054378642">"શારીરિક પ્રવૃત્તિ"</string>
<string name="permgroupdesc_activityRecognition" msgid="6949472038320473478">"તમારી શારીરિક પ્રવૃત્તિને ઍક્સેસ કરવી"</string>
<string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને તમારી શારીરિક પ્રવૃત્તિને ઍક્સેસ કરવાની મંજૂરી આપવી છે?"</string>
@@ -325,7 +325,7 @@
<string name="capability_title_canPerformGestures" msgid="7418984730362576862">"હાવભાવ કરો"</string>
<string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"ટૅપ, સ્વાઇપ, પિંચ કરી અને અન્ય હાવભાવ કરી શકે છે."</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"ફિંગરપ્રિન્ટ સંકેતો"</string>
- <string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"ઉપકરણના ફિંગરપ્રિન્ટ સેન્સર પર કરવામાં આવેલા સંકેતો કૅપ્ચર કરી શકે છે."</string>
+ <string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"ડિવાઇસના ફિંગરપ્રિન્ટ સેન્સર પર કરવામાં આવેલા સંકેતો કૅપ્ચર કરી શકે છે."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"સ્ટેટસ બારને અક્ષમ કરો અથવા તેમાં ફેરફાર કરો"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ઍપ્લિકેશનને સ્ટેટસ બાર અક્ષમ કરવાની અથવા સિસ્ટમ આયકન્સ ઉમેરવા અને દૂર કરવાની મંજૂરી આપે છે."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"સ્ટેટસ બારમાં બતાવો"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"ઍપ્લિકેશનને SMS સંદેશા પ્રાપ્ત કરવાની અને તેના પર પ્રક્રિયા કરવાની મંજૂરી આપે છે. આનો અર્થ એ કે ઍપ્લિકેશન તમને દર્શાવ્યા વિના તમારા ઉપકરણ પર મોકલેલ સંદેશાઓનું નિરીક્ષણ કરી શકે છે અથવા કાઢી નાખી શકે છે."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"ટેક્સ્ટ સંદેશા (MMS) પ્રાપ્ત કરો"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"ઍપ્લિકેશનને MMS સંદેશા પ્રાપ્ત કરવાની અને તેના પર પ્રક્રિયા કરવાની મંજૂરી આપે છે. આનો અર્થ એ કે ઍપ્લિકેશન તમને દર્શાવ્યા વિના તમારા ઉપકરણ પર મોકલેલ સંદેશાઓનું નિરીક્ષણ કરી શકે છે અથવા કાઢી નાખી શકે છે."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"સેલ બ્રોડકાસ્ટ સંદેશા ફૉરવર્ડ કરો"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"સેલ બ્રોડકાસ્ટ સંદેશા પ્રાપ્ત થાય કે તરત ફૉરવર્ડ કરવા માટે સેલ બ્રોડકાસ્ટ મૉડ્યૂલ સાથે પ્રતિબદ્ધ થવા બાબતે ઍપને મંજૂરી આપે છે. તમને કટોકટીની પરિસ્થિતિની ચેતવણી આપવા માટે સેલ બ્રોડકાસ્ટ અલર્ટ અમુક સ્થાનોમાં ડિલિવર કરવામાં આવે છે. કટોકટી અંગેનો સેલ બ્રોડકાસ્ટ પ્રાપ્ત થાય, ત્યારે દુર્ભાવનાપૂર્ણ ઍપ તમારા ડિવાઇસના કાર્યપ્રદર્શન અથવા ઑપરેશનમાં વિક્ષેપ પાડે તેમ બની શકે છે."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"સેલ બ્રોડકાસ્ટ સંદેશા વાંચો"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"એપ્લિકેશનને તમારા ઉપકરણ દ્વારા પ્રાપ્ત થયેલ સેલ બ્રોડકાસ્ટ સંદેશાને વાંચવાની મંજૂરી આપે છે. સેલ બ્રોડકાસ્ટ ચેતવણીઓ તમને કટોકટીની સ્થિતિઓ અંગે ચેતવવા માટે કેટલાક સ્થાનોમાં વિતરિત થાય છે. જ્યારે કટોકટીનો સેલ બ્રોડકાસ્ટ પ્રાપ્ત થાય ત્યારે દુર્ભાવનાપૂર્ણ ઍપ્લિકેશનો તમારા ઉપકરણના પ્રદર્શન અથવા ઓપરેશનમાં હસ્તક્ષેપ કરી શકે છે."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"સબ્સ્ક્રાઇબ કરેલ ફીડ્સ વાંચો"</string>
@@ -513,9 +515,9 @@
<string name="permlab_useBiometric" msgid="8837753668509919318">"બાયોમેટ્રિક હાર્ડવેરનો ઉપયોગ કરો"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"ઍપને પ્રમાણીકરણ માટે બાયોમેટ્રિક હાર્ડવેરનો ઉપયોગ કરવાની મંજૂરી આપે છે"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"ફિંગરપ્રિન્ટ હાર્ડવેરને સંચાલિત કરો"</string>
- <string name="permdesc_manageFingerprint" msgid="178208705828055464">"એપ્લિકેશનને ઉપયોગ માટે ફિંગરપ્રિન્ટ નમૂના ઉમેરવા અને કાઢી નાખવા માટે પદ્ધતિઓની વિનંતી કરવાની મંજૂરી આપે છે."</string>
+ <string name="permdesc_manageFingerprint" msgid="178208705828055464">"ઍપને ઉપયોગ માટે ફિંગરપ્રિન્ટ નમૂના ઉમેરવા અને કાઢી નાખવા માટે પદ્ધતિઓની વિનંતી કરવાની મંજૂરી આપે છે."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"ફિંગરપ્રિન્ટ હાર્ડવેરનો ઉપયોગ કરો"</string>
- <string name="permdesc_useFingerprint" msgid="9165097460730684114">"એપ્લિકેશનને પ્રમાણીકરણ માટે ફિંગરપ્રિન્ટ હાર્ડવેરનો ઉપયોગ કરવાની મંજૂરી આપે છે"</string>
+ <string name="permdesc_useFingerprint" msgid="9165097460730684114">"ઍપને પ્રમાણીકરણ માટે ફિંગરપ્રિન્ટ હાર્ડવેરનો ઉપયોગ કરવાની મંજૂરી આપે છે"</string>
<string name="permlab_audioWrite" msgid="2661772059799779292">"આપનો સંગીત સંગ્રહ સંશોધિત કરવો"</string>
<string name="permdesc_audioWrite" msgid="8888544708166230494">"એપને તમારો સંગીત સંગ્રહ સંશોધિત કરવાની મંજૂરી આપે છે."</string>
<string name="permlab_videoWrite" msgid="128769316366746446">"આપનો વિડિઓ સંગ્રહ સંશોધિત કરવો"</string>
@@ -1362,6 +1364,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB ડિબગીંગને અક્ષમ કરવા માટે પસંદ કરો."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"ટેસ્ટ હાર્નેસ મોડ ચાલુ કર્યો"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"ટેસ્ટ હાર્નેસ મોડ બંધ કરવા માટે ફૅક્ટરી રીસેટ કરો."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"સિરીયલ કન્સોલ ચાલુ થયો"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"કાર્યપ્રદર્શનને અસર થાય છે. બંધ કરવા માટે, બૂટલોડર ચેક કરો."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB પોર્ટમાં પ્રવાહી કે ધૂળ"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB પોર્ટ ઑટોમૅટિક રીતે બંધ કરવામાં આવ્યો છે. વધુ જાણવા માટે ટૅપ કરો."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB પોર્ટનો ઉપયોગ કરવો યોગ્ય છે"</string>
@@ -1580,8 +1584,8 @@
<string name="expires_on" msgid="3676242949915959821">"આ રોજ સમાપ્ત:"</string>
<string name="serial_number" msgid="758814067660862493">"શૃંખલા ક્રમાંક:"</string>
<string name="fingerprints" msgid="4516019619850763049">"ફિંગરપ્રિંટ્સ:"</string>
- <string name="sha256_fingerprint" msgid="4391271286477279263">"SHA-256 ફિંગરપ્રિંટ:"</string>
- <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 ફિંગરપ્રિંટ:"</string>
+ <string name="sha256_fingerprint" msgid="4391271286477279263">"SHA-256 ફિંગરપ્રિન્ટ:"</string>
+ <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 ફિંગરપ્રિન્ટ:"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"બધું જુઓ"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"પ્રવૃત્તિ પસંદ કરો"</string>
<string name="share_action_provider_share_with" msgid="5247684435979149216">"આની સાથે શેર કરો"</string>
@@ -1891,10 +1895,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"અવર્ગીકૃત"</string>
<string name="importance_from_user" msgid="7318955817386549931">"તમે આ સૂચનાઓનું મહત્વ સેટ કર્યું છે."</string>
<string name="importance_from_person" msgid="9160133597262938296">"શામેલ થયેલ લોકોને કારણે આ મહત્વપૂર્ણ છે."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="APP">%1$s</xliff:g>ને <xliff:g id="ACCOUNT">%2$s</xliff:g> માટે એક નવા વપરાશકર્તા બનાવવાની મંજૂરી આપીએ (આ એકાઉન્ટ માટે એક વપરાશકર્તા પહેલાંથી અસ્તિત્વમાં છે) ?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"<xliff:g id="APP">%1$s</xliff:g>ને <xliff:g id="ACCOUNT">%2$s</xliff:g> માટે એક નવા વપરાશકર્તા બનાવવાની મંજૂરી આપીએ ?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"ભાષા ઉમેરો"</string>
<string name="country_selection_title" msgid="2954859441620215513">"પ્રદેશ પસંદગી"</string>
<string name="search_language_hint" msgid="7042102592055108574">"ભાષાનું નામ ટાઇપ કરો"</string>
@@ -1932,7 +1934,7 @@
<string name="app_category_news" msgid="7496506240743986873">"સમાચાર અને સામાયિકો"</string>
<string name="app_category_maps" msgid="5878491404538024367">"નકશા અને નેવિગેશન"</string>
<string name="app_category_productivity" msgid="3742083261781538852">"ઉત્પાદકતા"</string>
- <string name="device_storage_monitor_notification_channel" msgid="3295871267414816228">"ઉપકરણ સ્ટૉરેજ"</string>
+ <string name="device_storage_monitor_notification_channel" msgid="3295871267414816228">"ડિવાઇસ સ્ટૉરેજ"</string>
<string name="adb_debugging_notification_channel_tv" msgid="5537766997350092316">"USB ડિબગિંગ"</string>
<string name="time_picker_hour_label" msgid="2979075098868106450">"કલાક"</string>
<string name="time_picker_minute_label" msgid="5168864173796598399">"મિનિટ"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index f21d138f14f0..f16ad65ded26 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -295,7 +295,7 @@
<string name="permgrouprequest_sms" msgid="7168124215838204719">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को एसएमएस (मैसेज) भेजने और देखने की अनुमति देना चाहते हैं?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"मेमोरी"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"अपने डिवाइस पर मौजूद फ़ोटो, मीडिया और फ़ाइलें ऐक्सेस करने की"</string>
- <string name="permgrouprequest_storage" msgid="7885942926944299560">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को डिवाइस पर मौजूद फ़ोटो, ऑडियो-वीडियो और फ़ाइलें ऐक्सेस करने की अनुमति दें?"</string>
+ <string name="permgrouprequest_storage" msgid="7885942926944299560">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को डिवाइस पर मौजूद फ़ोटो, ऑडियो-वीडियो, और फ़ाइलें ऐक्सेस करने की अनुमति दें?"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"माइक्रोफ़ोन"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"ऑडियो रिकॉर्ड करें"</string>
<string name="permgrouprequest_microphone" msgid="9167492350681916038">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को ऑडियो रिकॉर्ड करने की अनुमति देना चाहते हैं?"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"एप्लिकेशन को मैसेज (एसएमएस) पाने और प्रोसेस करने देता है. इसका मतलब है कि एप्लिकेशन आपके डिवाइस पर भेजे गए मैसेज की निगरानी आपको दिखाए बिना कर सकता है और उन्‍हें हटा सकता है."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"मैसेज (एमएमएस) पाएं"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"ऐप को मल्टीमीडिया मैसेज (एमएमएस) को पाने और उन पर कार्रवाई करने देता है. इसका मतलब है कि ऐप आपके डिवाइस पर भेजे गए मैसेज की निगरानी आपको दिखाए बिना कर सकता है और उन्‍हें हटा सकता है."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"सेल ब्रॉडकास्ट (CBC) मैसेज दूसरे नंबर पर भेजें"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"सेल ब्रॉडकास्ट (CBC) मैसेज आते ही उसे दूसरे नंबर पर भेजने के लिए, ऐप्लिकेशन को सेल ब्रॉडकास्ट (CBC) मॉड्यूल पर बाइंड करने की अनुमति देता है. कुछ जगहों में सेल ब्रॉडकास्ट (CBC) अलर्ट आपातकालीन स्थितियों के बारे में चेतावनी देने के लिए भेजा जाता है. नुकसान पहुंचाने वाले ऐप्लिकेशन, आपातकाल में सेल ब्रॉडकास्ट (CBC) मैसेज मिलने पर आपके डिवाइस के काम करते समय या इसके परफ़ॉर्मेंस में रुकावट पैदा कर सकते हैं."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"सेल ब्रॉडकास्ट (CBC) मैसेज पढ़ें"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"ऐप को, वो सेल ब्रॉडकास्ट (CBC) मैसेज पढ़ने देता है जो आपके डिवाइस को मिले हैं. सेल ब्रॉडकास्ट (CBC) अलर्ट कुछ स्थानों (लोकेशन) पर आपको आपातकालीन स्‍थितियों की चेतावनी देने के लिए दिए जाते हैं. आपातकालीन सेल ब्रॉडकास्ट (CBC) मिलने पर, धोखा देने वाले ऐप आपके डिवाइस के परफ़ॉर्मेंस या कार्यवाही में दखल दे सकते हैं."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"सदस्यता वाली फ़ीड पढ़ें"</string>
@@ -512,10 +514,10 @@
<string name="permdesc_requestPasswordComplexity" msgid="4730994229754212347">"यह मंज़ूरी मिलने के बाद ऐप्लिकेशन जान पाता है कि स्क्रीन लॉक कितना मुश्किल (बहुत ज़्यादा, मध्यम, कम या बिल्कुल नहीं) है. इस स्तर से यह पता चलता है कि स्क्रीन लॉक कितना लंबा या किस तरह का है. ऐप्लिकेशन उपयोगकर्ताओं को यह सुझाव भी दे सकता है कि वे स्क्रीन लॉक को एक तय लेवल तक अपडेट करें. लेकिन उपयोगकर्ता इसे बेझिझक अनदेखा करके छोड़ सकते हैं. ध्यान दें कि स्क्रीन लॉक को सादे टेक्स्ट में सेव नहीं किया जाता है इसलिए ऐप्लिकेशन को सटीक पासवर्ड पता नहीं होता है."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"बायोमीट्रिक हार्डवेयर इस्तेमाल करने दें"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"पुष्टि के लिए, ऐप्लिकेशन को बायोमीट्रिक हार्डवेयर इस्तेमाल करने की मंज़ूरी दें"</string>
- <string name="permlab_manageFingerprint" msgid="5640858826254575638">"उंगली की छाप के लिए हार्डवेयर को प्रबंधित करें"</string>
- <string name="permdesc_manageFingerprint" msgid="178208705828055464">"उंगली की छाप वाले टेम्पलेट का उपयोग करने के लिए जोड़ने और हटाने के लिए ऐप को विधियां प्रारंभ करने देती है."</string>
- <string name="permlab_useFingerprint" msgid="3150478619915124905">"उंगली की छाप के लिए हार्डवेयर का उपयोग करें"</string>
- <string name="permdesc_useFingerprint" msgid="9165097460730684114">"ऐप के प्रमाणीकरण के लिए उंगली की छाप हार्डवेयर का उपयोग करने देती है"</string>
+ <string name="permlab_manageFingerprint" msgid="5640858826254575638">"फ़िंगरप्रिंट हार्डवेयर को प्रबंधित करें"</string>
+ <string name="permdesc_manageFingerprint" msgid="178208705828055464">"फ़िंगरप्रिंट वाले टेम्पलेट का इस्तेमाल करने के लिए जोड़ने और हटाने के लिए ऐप को तरीके शुरू करने देती है."</string>
+ <string name="permlab_useFingerprint" msgid="3150478619915124905">"फ़िंगरप्रिंट हार्डवेयर का उपयोग करें"</string>
+ <string name="permdesc_useFingerprint" msgid="9165097460730684114">"ऐप के प्रमाणीकरण के लिए फ़िंगरप्रिंट हार्डवेयर का उपयोग करने देती है"</string>
<string name="permlab_audioWrite" msgid="2661772059799779292">"अपने संगीत संग्रह में बदलाव करने की अनुमति दें"</string>
<string name="permdesc_audioWrite" msgid="8888544708166230494">"इससे ऐप्लिकेशन को आपके संगीत संग्रह में बदलाव करने की मंज़ूरी दी जाती है"</string>
<string name="permlab_videoWrite" msgid="128769316366746446">"अपने वीडियो संग्रह में बदलाव करने की अनुमति दें"</string>
@@ -530,7 +532,7 @@
<string name="biometric_not_recognized" msgid="5770511773560736082">"पहचान नहीं हो पाई"</string>
<string name="biometric_error_canceled" msgid="349665227864885880">"प्रमाणीकरण रद्द किया गया"</string>
<string name="biometric_error_device_not_secured" msgid="6583143098363528349">"पिन, पैटर्न या पासवर्ड सेट नहीं है"</string>
- <string name="fingerprint_acquired_partial" msgid="735082772341716043">"आंशिक फ़िंगरप्रिंट की पहचान की गई. कृपया पुनः प्रयास करें."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"फ़िंगरप्रिंट की पहचान आंशिक तौर पर की गई. कृपया फिर से कोशिश करें."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"फ़िंगरप्रिंट प्रोसेस नहीं हो सका. कृपया दोबारा कोशिश करें."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"फ़िंगरप्रिंट सेंसर गंदा है. कृपया साफ़ करें और फिर कोशिश करें."</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"उंगली बहुत तेज़ी से चलाई गई है. कृपया फिर से कोशिश करें."</string>
@@ -541,9 +543,9 @@
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"चेहरे की पहचान की गई"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"चेहरे की पहचान की गई, कृपया पुष्टि बटन दबाएं"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"फ़िंगरप्रिंट हार्डवेयर उपलब्ध नहीं है."</string>
- <string name="fingerprint_error_no_space" msgid="1055819001126053318">"फ़िंगरप्रिंट को संग्रहित नहीं किया जा सका. कृपया कोई मौजूदा फ़िंगरप्रिंट निकालें."</string>
- <string name="fingerprint_error_timeout" msgid="3927186043737732875">"फ़िंगरप्रिंट का समय खत्म हो गया. पुनः प्रयास करें."</string>
- <string name="fingerprint_error_canceled" msgid="4402024612660774395">"फ़िंगरप्रिंट क्रियान्वयन रोक दिया गया."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"फ़िंगरप्रिंट को स्टोर नहीं किया जा सका. कृपया कोई मौजूदा फ़िंगरप्रिंट हटाएं."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"फ़िंगरप्रिंट का समय खत्म हो गया. फिर से कोशिश करें."</string>
+ <string name="fingerprint_error_canceled" msgid="4402024612660774395">"फ़िंगरप्रिंट ऑपरेशन रोक दिया गया."</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"उपयोगकर्ता ने फिंगरप्रिंट की पुष्टि की कार्रवाई रद्द कर दी है."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"बहुत ज़्यादा प्रयास कर लिए गए हैं. बाद में फिर से प्रयास करें."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"बहुत ज़्यादा कोशिशें. फ़िंगरप्रिंट सेंसर अक्षम है."</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB डीबग करना अक्षम करने के लिए चुनें."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"टेस्ट हार्नेस मोड चालू किया गया"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"टेस्ट हार्नेस मोड बंद करने के लिए फ़ैक्ट्री रीसेट करें."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"सीरियल कंसोल को चालू करें"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"परफ़ॉर्मेंस पर असर पड़ा है. बंद करने के लिए बूटलोडर चुनें."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"यूएसबी पोर्ट में तरल चीज़ या कचरा है"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"यूएसबी पोर्ट अपने आप बंद हो गया है. ज़्यादा जानने के लिए टैप करें."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"यूएसबी पोर्ट का इस्तेमाल करना सुरक्षित है"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"बिना किसी श्रेणी के"</string>
<string name="importance_from_user" msgid="7318955817386549931">"आपने इन सूचनाओं की अहमियत सेट की है."</string>
<string name="importance_from_person" msgid="9160133597262938296">"यह मौजूद व्यक्तियों के कारण महत्वपूर्ण है."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="APP">%1$s</xliff:g> को <xliff:g id="ACCOUNT">%2$s</xliff:g> के नाम से एक नया उपयोगकर्ता बनाने की अनुमति दें (इस नाम से एक खाता पहले से मौजूद है)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Allow <xliff:g id="APP">%1$s</xliff:g> to create a new User with <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"भाषा जोड़ें"</string>
<string name="country_selection_title" msgid="2954859441620215513">"क्षेत्र प्राथमिकता"</string>
<string name="search_language_hint" msgid="7042102592055108574">"भाषा का नाम लिखें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 25007d514064..45c39f2fb5af 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -347,6 +347,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Aplikaciji omogućuje primanje i obradu SMS poruka. To znači da aplikacija može nadzirati ili izbrisati poruke poslane na vaš uređaj, a da vam ih ne prikaže."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"primanje tekstnih poruka (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Aplikaciji omogućuje primanje i obradu MMS poruka. To znači da aplikacija može nadzirati ili izbrisati poruke poslane na vaš uređaj, a da vam ih ne prikaže."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Prosljeđivanje poruka emitiranja na mobitele"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Omogućuje aplikaciji da se poveže s modulom za emitiranje na mobitele kako bi prosljeđivala poruke koje se emitiraju na mobitele po njihovom primitku. Upozorenja značajke emitiranja na mobitele dostavljaju se na nekim lokacijama kako bi upozorila korisnike na hitne situacije. Zlonamjerne aplikacije mogu ometati izvršavanje ili rad vašeg uređaja kada stigne hitno emitiranje na mobitele."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"čitaj poruke koje se emitiraju unutar mobilne mreže"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Omogućuje aplikaciji čitanje poruka emitiranih unutar mobilne mreže koje prima vaš uređaj. Upozorenja koja se emitiraju na području mobilne mreže dostavljaju se na nekim lokacijama kako bi upozorila korisnike na hitne situacije. Zlonamjerne aplikacije mogu ometati izvršavanje ili rad vašeg uređaja kada stigne hitno upozorenje koje se emitira unutar mobilne mreže."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"čitanje pretplaćenih feedova"</string>
@@ -1383,6 +1385,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Odaberite da biste onemogućili rješavanje programske pogreške na USB-u."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Omogućen je način testnog okvira"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Vratite na tvorničke postavke da biste onemogućili način testnog okvira."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Serijska konzola omogućena"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Izvedba je otežana. Provjerite početni program za pokretanje da biste onemogućili konzolu."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Tekućina ili prljavština u USB priključku"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB priključak automatski je onemogućen. Dodirnite da biste saznali više."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Možete koristiti USB priključak"</string>
@@ -1924,10 +1928,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Nema kategorije"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Postavili ste važnost tih obavijesti."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Važno je zbog uključenih osoba."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Dopustiti aplikaciji <xliff:g id="APP">%1$s</xliff:g> da izradi novog korisnika s računom <xliff:g id="ACCOUNT">%2$s</xliff:g> (korisnik s ovim računom već postoji)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Dopustiti aplikaciji <xliff:g id="APP">%1$s</xliff:g> da izradi novog korisnika s računom <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Dodavanje jezika"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Postavke regije"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Unesite naziv jezika"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 0487601ac29b..aac2d76de845 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Lehetővé teszi az alkalmazás számára, hogy SMS-eket fogadjon és dolgozzon fel. Ez azt jelenti, hogy az alkalmazás megfigyelheti vagy törölheti a beérkező üzeneteket anélkül, hogy Ön látná azokat."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"szöveges üzenetek (MMS) fogadása"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Lehetővé teszi az alkalmazás számára, hogy MMS-eket fogadjon és dolgozzon fel. Ez azt jelenti, hogy az alkalmazás megfigyelheti vagy törölheti a beérkező üzeneteket anélkül, hogy Ön látná azokat."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Cellán belüli üzenetek továbbítása"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Az alkalmazás összekapcsolódhat a cellán belüli üzenetszórás moduljával, hogy az érkezésükkor továbbítani tudja a cellán belüli üzeneteket. Bizonyos helyeken figyelmeztető üzeneteket kaphat a cellán belül a vészhelyzetekről. A rosszindulatú alkalmazások vészhelyzeti cellaüzenet érkezésekor befolyásolhatják az eszköz teljesítményét és működését."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"cellán belüli üzenetek olvasása"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Lehetővé teszi az alkalmazás számára az eszközre érkező cellán belüli üzenetek olvasását. Bizonyos helyeken figyelmeztető üzeneteket kaphat a cellán belül a vészhelyzetekről. A rosszindulatú alkalmazások befolyásolhatják az eszköz teljesítményét vagy működését vészhelyzeti cellaüzenet érkezésekor."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"feliratkozott hírcsatornák olvasása"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Válassza ezt az USB hibakeresés kikapcsolásához."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Tesztelési alapkörnyezet mód engedélyezve"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"A Tesztelési alapkörnyezet mód kikapcsolásához állítsa vissza a gyári beállításokat."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Soros konzol engedélyezve"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Ez hatással van a teljesítményre. A letiltáshoz ellenőrizze a rendszerindítót."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Folyadék vagy szennyeződés az USB-portban"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB-port automatikusan letiltva. Koppintson, ha további információra van szüksége."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Az USB-port rendben használható"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Nincs kategóriába sorolva"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Ön állította be ezen értesítések fontossági szintjét."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Ez az üzenet a résztvevők miatt fontos."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Engedélyezi a(z) <xliff:g id="APP">%1$s</xliff:g> számára, hogy új felhasználót hozzon létre a(z) <xliff:g id="ACCOUNT">%2$s</xliff:g> fiókkal? (Már létezik felhasználó ezzel a fiókkal.)"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Engedélyezi a(z) <xliff:g id="APP">%1$s</xliff:g> számára, hogy új felhasználót hozzon létre a(z) <xliff:g id="ACCOUNT">%2$s</xliff:g> fiókkal?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Nyelv hozzáadása"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Régió beállítása"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Adja meg a nyelvet"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index e6223f0ce438..a5b4e30b64e7 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -230,7 +230,7 @@
<string name="global_action_bug_report" msgid="7934010578922304799">"Վրիպակի զեկույց"</string>
<string name="global_action_logout" msgid="935179188218826050">"Ավարտել աշխատաշրջանը"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Սքրինշոթ"</string>
- <string name="bugreport_title" msgid="5981047024855257269">"Վրիպակների զեկույց"</string>
+ <string name="bugreport_title" msgid="5981047024855257269">"Հաշվետվություն վրիպակի մասին"</string>
<string name="bugreport_message" msgid="398447048750350456">"Սա տեղեկություններ կհավաքագրի ձեր սարքի առկա կարգավիճակի մասին և կուղարկի այն էլեկտրոնային նամակով: Որոշակի ժամանակ կպահանջվի վրիպակի մասին զեկուցելու պահից սկսած մինչ ուղարկելը: Խնդրում ենք փոքր-ինչ համբերատար լինել:"</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Ինտերակտիվ զեկույց"</string>
<string name="bugreport_option_interactive_summary" msgid="229299488536107968">"Հիմնականում օգտագործեք այս տարբերակը: Այն ձեզ թույլ է տալիս հետևել զեկույցի ստեղծման գործընթացին, խնդրի մասին լրացուցիչ տեղեկություններ մուտքագրել և սքրինշոթներ ստեղծել: Կարող է բաց թողնել քիչ օգտագործվող որոշ բաժիններ, որոնց ստեղծումը երկար է տևում:"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Թույլ է տալիս հավելվածին ստանալ և մշակել SMS հաղորդագրությունները: Սա նշանակում է, որ հավելվածը կարող է ստուգել կամ ջնջել ձեր սարքին ուղարկված հաղորդագրությունները` առանց դրանք ձեզ ցուցադրելու:"</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"ստանալ տեքստային հաղորդագրություններ (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Թույլ է տալիս հավելվածին ստանալ և մշակել MMS հաղորդագրությունները: Սա նշանակում է, որ հավելվածը կարող է ստուգել կամ ջնջել ձեր սարքին ուղարկված հաղորդագրությունները` առանց դրանք ձեզ ցուցադրելու:"</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Բջջային հեռարձակման հաղորդագրությունների վերահասցեավորում"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Թույլ է տալիս հավելվածին կապ հաստատել բջջային հեռարձակման մոդուլի հետ՝ բնակչությանը ծանուցող հաղորդագրությունները վերահասցեավորելու համար։ Որոշ երկրներում այս հաղորդագրություններն օգտագործվում են բնակչությանը արտակարգ իրավիճակների մասին զգուշացնելու համար: Վնասարար հավելվածները կարող են խանգարել ձեր սարքի աշխատանքին, որին ուղարկվում են այս հաղորդագրությունները:"</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"կարդալ բջջային զեկուցվող հաղորդագրությունները"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Թույլ է տալիս հավելվածին կարդալ ձեր սարքի կողմից ստացված բջջային հեռարձակվող հաղորդագրությունները: Բջջային հեռարձակվող զգուշացումները ուղարկվում են որոշ վայրերում` արտակարգ իրավիճակների մասին ձեզ զգուշացնելու համար: Վնասարար հավելվածները կարող են խանգարել ձեր սարքի արդյունավետությանը կամ շահագործմանը, երբ ստացվում է արտակարգ իրավիճակի մասին բջջային հաղորդում:"</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"կարդալ բաժանորդագրված հոսքերը"</string>
@@ -366,7 +368,7 @@
<string name="permdesc_enableCarMode" msgid="4853187425751419467">"Թույլ է տալիս հավելվածին միացնել մեքենայի ռեժիմը:"</string>
<string name="permlab_killBackgroundProcesses" msgid="3914026687420177202">"փակել այլ հավելվածները"</string>
<string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"Թույլ է տալիս հավելվածին վերջ տալ այլ հավելվածների հետնաշերտի գործընթացները: Սա կարող է պատճառ դառնալ, որ այլ հավելվածները դադարեն աշխատել:"</string>
- <string name="permlab_systemAlertWindow" msgid="7238805243128138690">"Այս հավելվածը կարող է ցուցադրվել այլ հավելվածների վերևում"</string>
+ <string name="permlab_systemAlertWindow" msgid="7238805243128138690">"Այս հավելվածը կարող է ցուցադրվել այլ հավելվածների վրայից"</string>
<string name="permdesc_systemAlertWindow" msgid="2393776099672266188">"Այս հավելվածը կարող է ցուցադրվել այլ հավելվածների կամ էկրանի այլ հատվածների վերևում: Դա կարող է խոչընդոտել հավելվածի նորմալ օգտագործմանը և փոխել այլ հավելվածների տեսքը:"</string>
<string name="permlab_runInBackground" msgid="7365290743781858803">"աշխատել ֆոնում"</string>
<string name="permdesc_runInBackground" msgid="7370142232209999824">"Այս հավելվածը կարող է աշխատել ֆոնային ռեժիմում և ավելի արագ սպառել մարտկոցի լիցքը։"</string>
@@ -530,7 +532,7 @@
<string name="biometric_not_recognized" msgid="5770511773560736082">"Չհաջողվեց ճանաչել"</string>
<string name="biometric_error_canceled" msgid="349665227864885880">"Նույնականացումը չեղարկվեց"</string>
<string name="biometric_error_device_not_secured" msgid="6583143098363528349">"Ավելացրեք PIN կոդ, նախշ կամ գաղտնաբառ։"</string>
- <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Մատնահետքը հայտնաբերվել է մասամբ: Փորձեք նորից:"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Մատնահետքն ամբողջությամբ չի սկանավորվել: Փորձեք նորից:"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Չհաջողվեց մշակել մատնահետքը: Նորից փորձեք:"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Մատնահետքերի սենսորն աղտոտված է: Մաքրեք այն և փորձեք նորից:"</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Շատ արագ անցկացրիք մատը: Փորձեք նորից:"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Սեղմեք՝ USB-ով վրիպազերծումն անջատելու համար:"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Թեստային ռեժիմը միացված է"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Թեստային ռեժիմն անջատելու համար զրոյացրեք կարգավորումները։"</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Սերիական վահանակը միացված է"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Աշխատանքի արդյունավետությունը նվազում է։ Վահանակն անջատելու համար ստուգեք օպերացիոն համակարգի բեռնիչը։"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB միացքում ջուր կամ աղտ է հայտնաբերվել"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB միացքն ավտոմատ անջատվել է: Հպեք՝ ավելին իմանալու համար:"</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB միացքը կարող է օգտագործվել"</string>
@@ -1379,8 +1383,8 @@
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉՊՋՌՍՎՏՐՑՈՒՓՔԵւՕՖ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="alert_windows_notification_channel_group_name" msgid="1463953341148606396">"Ցուցադրում այլ հավելվածների վրայից"</string>
- <string name="alert_windows_notification_channel_name" msgid="3116610965549449803">"<xliff:g id="NAME">%s</xliff:g> հավելվածը ցուցադրվում է այլ հավելվածների վերևում"</string>
- <string name="alert_windows_notification_title" msgid="3697657294867638947">"<xliff:g id="NAME">%s</xliff:g> հավելվածը ցուցադրվում է այլ հավելվածների վերևում"</string>
+ <string name="alert_windows_notification_channel_name" msgid="3116610965549449803">"<xliff:g id="NAME">%s</xliff:g> հավելվածը ցուցադրվում է այլ հավելվածների վրայից"</string>
+ <string name="alert_windows_notification_title" msgid="3697657294867638947">"<xliff:g id="NAME">%s</xliff:g> հավելվածը ցուցադրվում է այլ հավելվածների վրայից"</string>
<string name="alert_windows_notification_message" msgid="8917232109522912560">"Եթե չեք ցանկանում, որ <xliff:g id="NAME">%s</xliff:g>-ն օգտագործի այս գործառույթը, հպեք՝ կարգավորումները բացելու և այն անջատելու համար։"</string>
<string name="alert_windows_notification_turn_off_action" msgid="2902891971380544651">"Անջատել"</string>
<string name="ext_media_checking_notification_title" msgid="4411133692439308924">"<xliff:g id="NAME">%s</xliff:g> հիշասարքի ստուգում…"</string>
@@ -1547,7 +1551,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Ավելի շատ ընտրանքներ"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <string name="storage_internal" msgid="3570990907910199483">"Համօգտագործվող ներքին հիշողություն"</string>
+ <string name="storage_internal" msgid="3570990907910199483">"Ներքին ընդհանուր կրիչ"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD քարտ"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"SD քարտ <xliff:g id="MANUFACTURER">%s</xliff:g>-ից"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB սարքավար"</string>
@@ -1580,7 +1584,7 @@
<string name="serial_number" msgid="758814067660862493">"Հերթական համարը`"</string>
<string name="fingerprints" msgid="4516019619850763049">"Մատնահետքերը`"</string>
<string name="sha256_fingerprint" msgid="4391271286477279263">"SHA-256 մատնահետք`"</string>
- <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1մատնահետք`"</string>
+ <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 մատնահետք`"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Տեսնել բոլորը"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Ընտրել գործունեությունը"</string>
<string name="share_action_provider_share_with" msgid="5247684435979149216">"Կիսվել"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Չդասակարգված"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Դուք սահմանել եք այս ծանուցումների կարևորությունը:"</string>
<string name="importance_from_person" msgid="9160133597262938296">"Կարևոր է, քանի որ որոշակի մարդիկ են ներգրավված:"</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Թույլատրե՞լ <xliff:g id="APP">%1$s</xliff:g> հավելվածին <xliff:g id="ACCOUNT">%2$s</xliff:g> հաշվով նոր Օգտատեր ստեղծել (նման հաշվով Օգտատեր արդեն գոյություն ունի):"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Թույլատրե՞լ <xliff:g id="APP">%1$s</xliff:g> հավելվածին <xliff:g id="ACCOUNT">%2$s</xliff:g> հաշվով նոր Օգտատեր ստեղծել:"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Ավելացնել լեզու"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Նախընտրելի տարածաշրջան"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Մուտքագրեք լեզուն"</string>
@@ -2005,7 +2007,7 @@
<string name="standby_warning_message" product="default" msgid="5222741828239073484">"Սարքը շուտով կանջատվի: Սեղմեք՝ միացրած թողնելու համար:"</string>
<string name="notification_appops_camera_active" msgid="5050283058419699771">"Տեսախցիկ"</string>
<string name="notification_appops_microphone_active" msgid="4335305527588191730">"Խոսափող"</string>
- <string name="notification_appops_overlay_active" msgid="633813008357934729">"ցուցադրվում է մյուս հավելվածների վերևում"</string>
+ <string name="notification_appops_overlay_active" msgid="633813008357934729">"ցուցադրվում է մյուս հավելվածների վրայից"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Ծանուցում լիցքավորման մասին"</string>
<string name="dynamic_mode_notification_title" msgid="508815255807182035">"Մարտկոցի լիցքը կարող է սովորականից շուտ սպառվել"</string>
<string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"Մարտկոցի կյանքը երկարացնելու համար ակտիվացվել է մարտկոցի տնտեսման ռեժիմը"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 3c6ee839dbab..228da880b0d3 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -161,7 +161,7 @@
<string name="httpErrorProxyAuth" msgid="1788207010559081331">"Autentikasi via proxy server gagal."</string>
<string name="httpErrorConnect" msgid="8714273236364640549">"Tidak dapat tersambung ke server."</string>
<string name="httpErrorIO" msgid="2340558197489302188">"Tidak dapat berkomunikasi dengan server. Coba lagi nanti."</string>
- <string name="httpErrorTimeout" msgid="4743403703762883954">"Sambungan ke server terputus."</string>
+ <string name="httpErrorTimeout" msgid="4743403703762883954">"Koneksi ke server terputus."</string>
<string name="httpErrorRedirectLoop" msgid="8679596090392779516">"Halaman ini berisi terlalu banyak pengalihan server."</string>
<string name="httpErrorUnsupportedScheme" msgid="5015730812906192208">"Protokol tidak didukung."</string>
<string name="httpErrorFailedSslHandshake" msgid="96549606000658641">"Tidak dapat membuat sambungan aman."</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Memungkinkan aplikasi menerima dan memproses pesan SMS. Ini artinya aplikasi dapat memantau atau menghapus pesan yang dikirim ke perangkat Anda tanpa menunjukkannya kepada Anda."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"terima pesan teks (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Memungkinkan aplikasi menerima dan memproses pesan MMS. Ini artinya aplikasi dapat memantau atau menghapus pesan yang dikirim ke perangkat Anda tanpa menunjukkannya kepada Anda."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Teruskan pesan cell broadcast"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Mengizinkan aplikasi mem-binding ke modul cell broadcast untuk meneruskan pesan cell broadcast saat pesan tersebut diterima. Notifikasi cell broadcast dikirim di beberapa lokasi untuk memperingatkan Anda tentang situasi darurat. Aplikasi berbahaya dapat mengganggu performa atau operasi perangkat saat cell broadcast darurat diterima."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"membaca pesan siaran seluler"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Mengizinkan aplikasi membaca pesan siaran seluler yang diterima perangkat Anda. Notifikasi siaran seluler dikirimkan di beberapa lokasi untuk memperingatkan Anda tentang situasi darurat. Aplikasi berbahaya dapat mengganggu kinerja atau operasi perangkat Anda saat siaran seluler darurat diterima."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"baca feed langganan"</string>
@@ -515,7 +517,7 @@
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"kelola hardware sidik jari"</string>
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Mengizinkan aplikasi memanggil metode untuk menambahkan dan menghapus template sidik jari untuk digunakan."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"gunakan hardware sidik jari"</string>
- <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Mengizinkan aplikasi untuk menggunakan hardware sidik jari untuk otentikasi"</string>
+ <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Mengizinkan aplikasi untuk menggunakan hardware sidik jari untuk autentikasi"</string>
<string name="permlab_audioWrite" msgid="2661772059799779292">"memodifikasi koleksi musik Anda"</string>
<string name="permdesc_audioWrite" msgid="8888544708166230494">"Mengizinkan aplikasi untuk memodifikasi koleksi musik Anda."</string>
<string name="permlab_videoWrite" msgid="128769316366746446">"memodifikasi koleksi video Anda"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Pilih untuk menonaktifkan debugging USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Mode Tes Otomatis diaktifkan"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Lakukan reset ke setelan pabrik untuk menonaktifkan Mode Tes Otomatis."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Konsol serial diaktifkan"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Performa terpengaruh. Untuk menonaktifkan, periksa bootloader."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Cairan atau kotoran di port USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Port USB otomatis dinonaktifkan. Ketuk untuk mempelajari lebih lanjut."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Boleh menggunakan port USB"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Belum dikategorikan"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Anda menyetel nilai penting notifikasi ini."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Ini penting karena orang-orang yang terlibat."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Izinkan <xliff:g id="APP">%1$s</xliff:g> membuat Pengguna baru dengan <xliff:g id="ACCOUNT">%2$s</xliff:g> (Pengguna dengan akun ini sudah ada) ?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Izinkan <xliff:g id="APP">%1$s</xliff:g> membuat Pengguna baru dengan <xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Tambahkan bahasa"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Preferensi wilayah"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Ketik nama bahasa"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 59cd4c165a50..d0f802819021 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Leyfir forriti að taka á móti og vinna úr SMS-skilaboðum. Þetta þýðir að forritið getur fylgst með eða eytt skilaboðum sem send eru í tækið án þess að birta þér þau."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"taka á móti textaskilaboðum (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Leyfir forriti að taka á móti og vinna úr MMS-skilaboðum. Þetta þýðir að forritið getur fylgst með eða eytt skilaboðum sem send eru í tækið án þess að birta þér þau."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Framsenda skilaboð frá endurvarpa"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Heimilar forritinu að bindast endurvarpseiningunni til að framsenda skilaboð frá endurvarpa þegar þau berast. Viðvaranir frá endurvarpa berast á tilteknum stöðum til að vara þig við neyðarástandi. Spilliforrit geta truflað afköst eða virkni tækisins þegar neyðarboð berast frá endurvarpa."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"lesa skilaboð frá endurvarpa"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Leyfir forriti að lesa skilaboð frá endurvarpa sem tækið móttekur. Viðvaranir frá endurvarpa berast á tilteknum stöðum til að vara þig við neyðarástandi. Spilliforrit geta truflað afköst eða virkni tækisins þegar neyðarboð berast frá endurvarpa."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"lesa strauma í áskrift"</string>
@@ -1362,6 +1364,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Veldu til að gera USB-villuleit óvirka."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Kveikt á stillingu prófunarvangs"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Núllstilltu til að slökkva á stillingu prófunarvangs."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Raðstjórnborð virkjað"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Þetta hefur áhrif á afköst. Athugaðu ræsiforritið ef þú vilt gera þetta óvirkt."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Vökvi eða óhreinindi í USB-tengi"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB-tengi er gert óvirkt sjálfkrafa. Ýttu til að fá frekari upplýsingar."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Óhætt að nota USB-tengi"</string>
@@ -1891,10 +1895,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Óflokkað"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Þú stilltir mikilvægi þessara tilkynninga."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Þetta er mikilvægt vegna fólksins sem tekur þátt í þessu."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Viltu leyfa <xliff:g id="APP">%1$s</xliff:g> að stofna nýjan notanda með <xliff:g id="ACCOUNT">%2$s</xliff:g> (notandi með þennan reikning er þegar fyrir hendi)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Leyfa <xliff:g id="APP">%1$s</xliff:g> að stofna nýjan notanda með <xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Bæta við tungumáli"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Svæðisval"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Sláðu inn heiti tungumáls"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index cbc99896202c..747d9189c8e1 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -283,7 +283,7 @@
<string name="permgrouprequest_contacts" msgid="6032805601881764300">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere ai tuoi contatti?"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Geolocalizz."</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"accedere alla posizione di questo dispositivo"</string>
- <string name="permgrouprequest_location" msgid="3788275734953323491">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere alla posizione di questo dispositivo?"</string>
+ <string name="permgrouprequest_location" msgid="3788275734953323491">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere alla posizione del dispositivo?"</string>
<string name="permgrouprequestdetail_location" msgid="1347189607421252902">"L\'app avrà accesso alla posizione soltanto quando la usi"</string>
<string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere &lt;b&gt;sempre&lt;/b&gt; alla posizione di questo dispositivo?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"L\'app al momento può accedere alla posizione soltanto mentre la usi"</string>
@@ -324,8 +324,8 @@
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controlla il livello di zoom e la posizione del display."</string>
<string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Eseguire gesti"</string>
<string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Consente di toccare, far scorrere, pizzicare ed eseguire altri gesti."</string>
- <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"Gesti con sensore di impronte digitali"</string>
- <string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"È in grado di rilevare i gesti compiuti con il sensore di impronte digitali dei dispositivi."</string>
+ <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"Gesti con sensore di impronte"</string>
+ <string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"È in grado di rilevare i gesti compiuti con il sensore di impronte dei dispositivi."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"disattivazione o modifica della barra di stato"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Consente all\'applicazione di disattivare la barra di stato o di aggiungere e rimuovere icone di sistema."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ruolo di barra di stato"</string>
@@ -344,8 +344,10 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Consente all\'applicazione di ricevere ed elaborare messaggi SMS. Significa che l\'applicazione potrebbe monitorare o eliminare i messaggi inviati al tuo dispositivo senza mostrarteli."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"ricezione messaggi di testo (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Consente all\'applicazione di ricevere ed elaborare messaggi MMS. Significa che l\'applicazione potrebbe monitorare o eliminare i messaggi inviati al tuo dispositivo senza mostrarteli."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Inoltro di messaggi cell broadcast"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Consente all\'app di essere associata al modulo di cell broadcast al fine di inoltrare i messaggi di cell broadcast man mano che arrivano. Gli avvisi cell broadcast vengono trasmessi in alcune località per avvertire di situazioni di emergenza. Le app dannose potrebbero interferire con le prestazioni o con il funzionamento del dispositivo quando si riceve un messaggio cell broadcast di emergenza."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"lettura di messaggi cell broadcast"</string>
- <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Consente all\'applicazione di leggere i messaggi cell broadcast ricevuti dal dispositivo. Gli avvisi cell broadcast vengono trasmessi in alcune località per avvertire di eventuali situazioni di emergenza. Le applicazioni dannose potrebbero interferire con il rendimento o con il funzionamento del dispositivo quando si riceve un messaggio cell broadcast di emergenza."</string>
+ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Consente all\'applicazione di leggere i messaggi cell broadcast ricevuti dal dispositivo. Gli avvisi cell broadcast vengono trasmessi in alcune località per avvertire di eventuali situazioni di emergenza. Le app dannose potrebbero interferire con le prestazioni o con il funzionamento del dispositivo quando si riceve un messaggio cell broadcast di emergenza."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"lettura feed sottoscritti"</string>
<string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Consente all\'applicazione di ottenere dettagli sui feed attualmente sincronizzati."</string>
<string name="permlab_sendSms" msgid="7544599214260982981">"invio e visualizzazione di SMS"</string>
@@ -512,10 +514,10 @@
<string name="permdesc_requestPasswordComplexity" msgid="4730994229754212347">"Consente all\'app di conoscere il livello di complessità del blocco schermo (alto, medio, basso o nessuno), che indica l\'intervallo di caratteri possibile e il tipo di blocco schermo. L\'app può inoltre suggerire agli utenti di aggiornare il blocco schermo a un livello specifico di complessità, ma gli utenti possono ignorare liberamente il suggerimento e uscire. Tieni presente che il blocco schermo non viene memorizzato come testo non crittografato, quindi l\'app non conosce la password esatta."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"utilizzo di hardware biometrico"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Consente all\'app di utilizzare hardware biometrico per eseguire l\'autenticazione"</string>
- <string name="permlab_manageFingerprint" msgid="5640858826254575638">"gestione di hardware per il riconoscimento delle impronte digitali"</string>
- <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Consente all\'app di richiamare metodi per aggiungere e rimuovere modelli di impronte digitali da utilizzare."</string>
- <string name="permlab_useFingerprint" msgid="3150478619915124905">"utilizzo di hardware per il riconoscimento delle impronte digitali"</string>
- <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Consente all\'app di utilizzare l\'hardware per il riconoscimento delle impronte digitali per eseguire l\'autenticazione"</string>
+ <string name="permlab_manageFingerprint" msgid="5640858826254575638">"gestione di hardware per il riconoscimento delle impronte"</string>
+ <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Consente all\'app di richiamare metodi per aggiungere e rimuovere modelli di impronte da utilizzare."</string>
+ <string name="permlab_useFingerprint" msgid="3150478619915124905">"utilizzo di hardware per il riconoscimento delle impronte"</string>
+ <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Consente all\'app di utilizzare l\'hardware per il riconoscimento delle impronte per eseguire l\'autenticazione"</string>
<string name="permlab_audioWrite" msgid="2661772059799779292">"modifica della tua raccolta musicale"</string>
<string name="permdesc_audioWrite" msgid="8888544708166230494">"Consente all\'app di modificare la tua raccolta musicale."</string>
<string name="permlab_videoWrite" msgid="128769316366746446">"modifica della tua raccolta di video"</string>
@@ -530,30 +532,30 @@
<string name="biometric_not_recognized" msgid="5770511773560736082">"Non riconosciuto"</string>
<string name="biometric_error_canceled" msgid="349665227864885880">"Autenticazione annullata"</string>
<string name="biometric_error_device_not_secured" msgid="6583143098363528349">"Non hai impostato PIN, sequenza o password"</string>
- <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Rilevata impronta digitale parziale. Riprova."</string>
- <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Impossibile elaborare l\'impronta digitale. Riprova."</string>
- <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Il sensore di impronte digitali è sporco. Puliscilo e riprova."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Rilevata impronta parziale. Riprova."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Impossibile elaborare l\'impronta. Riprova."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Il sensore di impronte è sporco. Puliscilo e riprova."</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Movimento del dito troppo rapido. Riprova."</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Movimento del dito troppo lento. Riprova."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_authenticated" msgid="5309333983002526448">"Impronta digitale autenticata"</string>
+ <string name="fingerprint_authenticated" msgid="5309333983002526448">"Impronta autenticata"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Volto autenticato"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Volto autenticato, premi Conferma"</string>
- <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardware per l\'impronta digitale non disponibile."</string>
- <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Impossibile memorizzare l\'impronta digitale. Rimuovi un\'impronta esistente."</string>
- <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Timeout impronta digitale. Riprova."</string>
- <string name="fingerprint_error_canceled" msgid="4402024612660774395">"Operazione associata all\'impronta digitale annullata."</string>
- <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Operazione di autenticazione dell\'impronta digitale annullata dall\'utente."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardware per l\'impronta non disponibile."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Impossibile memorizzare l\'impronta. Rimuovi un\'impronta esistente."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Timeout impronta. Riprova."</string>
+ <string name="fingerprint_error_canceled" msgid="4402024612660774395">"Operazione associata all\'impronta annullata."</string>
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Operazione di autenticazione dell\'impronta annullata dall\'utente."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Troppi tentativi. Riprova più tardi."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Troppi tentativi. Sensore di impronte digitali disattivato."</string>
+ <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Troppi tentativi. Sensore di impronte disattivato."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Riprova."</string>
<string name="fingerprint_error_no_fingerprints" msgid="7654382120628334248">"Nessuna impronta digitale registrata."</string>
- <string name="fingerprint_error_hw_not_present" msgid="409523969613176352">"Questo dispositivo non dispone di sensore di impronte digitali."</string>
+ <string name="fingerprint_error_hw_not_present" msgid="409523969613176352">"Questo dispositivo non dispone di sensore di impronte."</string>
<string name="fingerprint_name_template" msgid="5870957565512716938">"Dito <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
- <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icona dell\'impronta digitale"</string>
+ <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icona dell\'impronta"</string>
<string name="permlab_manageFace" msgid="7262837876352591553">"gestione dell\'hardware per Sblocco col sorriso"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Consente all\'app di richiamare i metodi per aggiungere e rimuovere i modelli di volti."</string>
<string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"utilizzo dell\'hardware per Sblocco col sorriso"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Seleziona per disattivare il debug USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Modalità test harness attivata"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Ripristina le impostazioni di fabbrica per disattivare la modalità test harness."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Console seriale attivata"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Ci sono conseguenze sulle prestazioni. Per disattivare, seleziona il bootloader."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Liquidi o detriti nella porta USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"La porta USB viene disattivata automaticamente. Tocca per avere ulteriori informazioni."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Utilizzo porta USB consentito"</string>
@@ -1579,7 +1583,7 @@
<string name="expires_on" msgid="3676242949915959821">"Scade il:"</string>
<string name="serial_number" msgid="758814067660862493">"Numero di serie:"</string>
<string name="fingerprints" msgid="4516019619850763049">"Fingerprint:"</string>
- <string name="sha256_fingerprint" msgid="4391271286477279263">"Fingerprint SHA-256:"</string>
+ <string name="sha256_fingerprint" msgid="4391271286477279263">"Impronta SHA-256:"</string>
<string name="sha1_fingerprint" msgid="7930330235269404581">"Fingerprint SHA-1:"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Mostra tutto"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Scegli attività"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Senza categoria"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Stabilisci tu l\'importanza di queste notifiche."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Importante a causa delle persone coinvolte."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Consentire a <xliff:g id="APP">%1$s</xliff:g> di creare un nuovo utente con l\'account <xliff:g id="ACCOUNT">%2$s</xliff:g> (esiste già un utente con questo account)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Consentire a <xliff:g id="APP">%1$s</xliff:g> di creare un nuovo utente con l\'account <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Aggiungi una lingua"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Area geografica preferita"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Digita nome lingua"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 0a721a82ee2e..4c12e432b6da 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -330,7 +330,7 @@
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"קבע את המרחק מהתצוגה ואת מיקום התצוגה."</string>
<string name="capability_title_canPerformGestures" msgid="7418984730362576862">"ביצוע תנועות"</string>
<string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"יכול להקיש, להחליק, לעשות תנועת צביטה ולבצע תנועות אחרות."</string>
- <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"תנועות"</string>
+ <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"תנועות של טביעות אצבעות"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"אפשרות לזהות תנועות בזמן נגיעה בחיישן טביעות האצבע של המכשיר."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"השבת או שנה את שורת המצב"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"מאפשר לאפליקציה להשבית את שורת המצב או להוסיף ולהסיר סמלי מערכת."</string>
@@ -350,6 +350,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"‏מאפשר לאפליקציה לקבל ולעבד הודעות SMS. משמעות הדבר היא שהאפליקציה יכולה לעקוב אחר הודעות שנשלחו למכשיר או למחוק אותן מבלי להציג לך אותן."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"‏קבלת הודעות טקסט (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"‏מאפשר לאפליקציה לקבל ולעבד הודעות MMS. משמעות הדבר היא שהאפליקציה יכולה לעקוב אחר הודעות שנשלחו למכשיר או למחוק אותן מבלי להציג לך אותן."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"העברת הודעות של שידור סלולרי"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"מאפשרת לאפליקציה להתחייב למודול של השידור הסלולרי כדי להעביר הודעות של שידור סלולרי כשהן מתקבלות. התראות שידור סלולרי נשלחות במקומות מסוימים כדי להזהיר אותך מפני מצבי חירום. אפליקציות זדוניות עשויות להפריע לביצועים או לפעולה של המכשיר כאשר מתקבל שידור חירום סלולרי."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"קריאת הודעות שידור סלולרי"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"מאפשר לאפליקציה לקרוא הודעות שידור סלולרי שהתקבלו במכשיר שלך. התראות שידור סלולרי נשלחות במקומות מסוימים על מנת להזהיר אותך מפני מצבי חירום. אפליקציות זדוניות עשויות להפריע לביצועים או לפעולה של המכשיר שלך כאשר מתקבל שידור חירום סלולרי."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"קרא עדכוני מנויים"</string>
@@ -536,9 +538,9 @@
<string name="biometric_not_recognized" msgid="5770511773560736082">"לא זוהתה"</string>
<string name="biometric_error_canceled" msgid="349665227864885880">"האימות בוטל"</string>
<string name="biometric_error_device_not_secured" msgid="6583143098363528349">"עוד לא הוגדרו קוד גישה, קו ביטול נעילה או סיסמה"</string>
- <string name="fingerprint_acquired_partial" msgid="735082772341716043">"זוהתה טביעת אצבע חלקית. נסה שוב."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"זוהתה טביעת אצבע חלקית. אפשר לנסות שוב."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"לא ניתן היה לעבד את טביעת האצבע. נסה שוב."</string>
- <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"החיישן של טביעות האצבעות מלוכלך. נקה אותו ונסה שוב."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"החיישן של טביעות האצבעות מלוכלך. צריך לנקות אותו ולנסות שוב."</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"הזזת את האצבע מהר מדי. נסה שוב."</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"הזזת את האצבע לאט מדי. נסה שוב."</string>
<string-array name="fingerprint_acquired_vendor">
@@ -546,9 +548,9 @@
<string name="fingerprint_authenticated" msgid="5309333983002526448">"טביעת האצבע אומתה"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"זיהוי הפנים בוצע"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"זיהוי הפנים בוצע. יש ללחוץ על אישור"</string>
- <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"החומרה בשביל טביעת אצבע אינה זמינה."</string>
- <string name="fingerprint_error_no_space" msgid="1055819001126053318">"לא ניתן לאחסן טביעת אצבע. הסר טביעת אצבע קיימת."</string>
- <string name="fingerprint_error_timeout" msgid="3927186043737732875">"חלף הזמן הקצוב לטביעת אצבע. נסה שוב."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"החומרה בשביל טביעות אצבעות אינה זמינה."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"לא ניתן לאחסן טביעת אצבע. יש להסיר טביעת אצבע קיימת."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"חלף הזמן הקצוב לטביעת אצבע. אפשר לנסות שוב."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"פעולת טביעת האצבע בוטלה."</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"פעולת טביעת האצבע בוטלה בידי המשתמש."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"יותר מדי ניסיונות. נסה שוב מאוחר יותר."</string>
@@ -1405,6 +1407,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"‏בחר להשבית ניפוי באגים ב-USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"מצב מסגרת בדיקה הופעל"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"כדי להשבית את מצב מסגרת בדיקה צריך לאפס להגדרות היצרן."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"קונסולה סדרתית מופעלת"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"קיימת השפעה על הביצועים. כדי להשבית, יש לבדוק את תוכנת האתחול."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"‏יש נוזלים או חלקיקים ביציאת ה-USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"‏יציאת ה-USB הושבתה באופן אוטומטי. יש להקיש לקבלת מידע נוסף."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"‏ניתן להשתמש ביציאת ה-USB"</string>
@@ -1958,10 +1962,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"ללא שיוך לקטגוריה"</string>
<string name="importance_from_user" msgid="7318955817386549931">"עליך להגדיר את החשיבות של ההתראות האלה."</string>
<string name="importance_from_person" msgid="9160133597262938296">"ההודעה חשובה בשל האנשים המעורבים."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"האם לאפשר לאפליקציה <xliff:g id="APP">%1$s</xliff:g> ליצור משתמש חדש באמצעות <xliff:g id="ACCOUNT">%2$s</xliff:g> (כבר קיים משתמש לחשבון הזה) ?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"האם לאפשר לאפליקציה <xliff:g id="APP">%1$s</xliff:g> ליצור משתמש חדש באמצעות <xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"הוספת שפה"</string>
<string name="country_selection_title" msgid="2954859441620215513">"העדפת אזור"</string>
<string name="search_language_hint" msgid="7042102592055108574">"הקלד שם שפה"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index fe45c1fa9486..98600b8cb590 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"SMSメッセージの受信と処理をアプリに許可します。これにより、アプリがデバイスに届いたメッセージを表示することなく監視または削除できるようになります。"</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"テキストメッセージ(MMS)の受信"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"MMSメッセージの受信と処理をアプリに許可します。これにより、アプリがデバイスに届いたメッセージを表示することなく監視または削除できるようになります。"</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"緊急速報メールの転送"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"受信した緊急速報メールを転送するために、緊急速報メール モジュールにバインドすることをこのアプリに許可します。緊急速報メールは、緊急事態を警告する目的で一部の地域に配信されます。緊急速報メールの受信時に、悪意のあるアプリによってデバイスの動作や処理が妨害される恐れがあります。"</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"緊急速報メール SMS の読み取り"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"デバイスで受信した緊急速報メール SMS の読み取りをアプリに許可します。緊急速報メールは、緊急事態を警告する目的で一部の地域に配信されます。緊急速報メールの受信時に、悪意のあるアプリによってデバイスの動作や処理が妨害される恐れがあります。"</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"登録したフィードの読み取り"</string>
@@ -1189,7 +1191,7 @@
<string name="unsupported_display_size_show" msgid="7969129195360353041">"常に表示"</string>
<string name="unsupported_compile_sdk_message" msgid="4253168368781441759">"<xliff:g id="APP_NAME">%1$s</xliff:g> は、互換性のない Android OS バージョン用にビルドされているため、予期しない動作が発生するおそれがあります。アプリの更新バージョンを利用できる場合があります。"</string>
<string name="unsupported_compile_sdk_show" msgid="2681877855260970231">"常に表示"</string>
- <string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"アップデートをチェック"</string>
+ <string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"アップデートを確認"</string>
<string name="smv_application" msgid="3307209192155442829">"アプリ「<xliff:g id="APPLICATION">%1$s</xliff:g>」(プロセス「<xliff:g id="PROCESS">%2$s</xliff:g>」)でStrictModeポリシー違反がありました。"</string>
<string name="smv_process" msgid="5120397012047462446">"プロセス<xliff:g id="PROCESS">%1$s</xliff:g>でStrictModeポリシー違反がありました。"</string>
<string name="android_upgrading_title" product="default" msgid="7513829952443484438">"スマートフォンの更新中…"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USBデバッグを無効にする場合に選択します。"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"テストハーネス モード有効"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"出荷時設定にリセットしてテストハーネス モードを無効にしてください。"</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"シリアル コンソールは有効です"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"シリアル コンソールを有効にすると、パフォーマンスに影響します。無効にするには、ブートローダーをチェックしてください。"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB ポート内の液体やゴミ"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB ポートが自動的に無効になりました。タップして詳細をご確認ください。"</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB ポートを安全に使用できます"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"カテゴリなし"</string>
<string name="importance_from_user" msgid="7318955817386549931">"このような通知の重要度を設定します。"</string>
<string name="importance_from_person" msgid="9160133597262938296">"関係するユーザーのため、この設定は重要です。"</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="APP">%1$s</xliff:g> が <xliff:g id="ACCOUNT">%2$s</xliff:g> で新しいユーザーを作成できるようにしますか?(このアカウントのユーザーはすでに存在します)"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"<xliff:g id="APP">%1$s</xliff:g> が <xliff:g id="ACCOUNT">%2$s</xliff:g> で新しいユーザーを作成できるようにしますか?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"言語を追加"</string>
<string name="country_selection_title" msgid="2954859441620215513">"地域設定"</string>
<string name="search_language_hint" msgid="7042102592055108574">"言語名を入力"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 00afe0263bf5..cc8ecc2b9e4e 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"აპს შეეძლება SMS შეტყობინებების მიღება და დამუშავება. ეს ნიშნავს, რომ აპს შეეძლება თქვენ მოწყობილობაზე გამოგზავნილი შეტყობინებების მონიტორინგი და მათი წაშლა თქვენთვის ჩვენების გარეშე."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"ტექსტური შეტყობინებების (MMS) მიღება"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"აპს შეეძლება MMS შეტყობინებების მიღება და დამუშავება. ეს ნიშნავს, რომ აპს შეეძლება შეტყობინებების მონიტორინგი და მათი წაშლა თქვენთვის ჩვენების გარეშე."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Cell broadcast შეტყობინებების გადამისამართება"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"საშუალებას აძლევს აპს, დაუკავშირდეს cell broadcast მოდულს იმისთვის, რომ გადაამისამართოს cell broadcast შეტყობინებები მათი მიღებისთანავე. Cell broadcast გაფრთხილებები მიეწოდება ზოგიერთ მდებარეობაზე საგანგებო სიტუაციების შესახებ გაფრთხილების მიზნით. საგანგებო cell broadcast-ის მიღების დროს, მავნე აპებმა შეიძლება ხელი შეუშალონ თქვენი მოწყობილობის ფუნქციონირებას ან ოპერაციებს."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"მასიური დაგზავნის შეტყობინებების წაკითხვა"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"აპს შეეძლება, წაიკითხოს თქვენს მოწყობილობაზე გამოგზავნილი ქსელის სამაუწყებლო შეტყობინებები. სამაუწყებლო გაფრთხილებები მოგეწოდებათ ზოგიერთ ადგილზე ექსტრემალური სიტუაციების შესახებ გასაფრთხილებლად. ქსელის გადაუდებელი შეტყონიბენის მიღების დროს მავნე აპებმა შეიძლება ხელი შეუშალონ თქვენი მოწყობილობის ფუნქციონირებას ან ოპერაციებს."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"გამოწერილი არხების წაკითხვა"</string>
@@ -513,9 +515,9 @@
<string name="permlab_useBiometric" msgid="8837753668509919318">"ბიომეტრიული აპარატის გამოყენება"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"საშუალებას აძლევს აპს, ავტორიზაციისთვის გამოიყენოს ბიომეტრიული აპარატი"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"თითის ანაბეჭდის აპარატის მართვა"</string>
- <string name="permdesc_manageFingerprint" msgid="178208705828055464">"საშუალებას აძლევს აპლიკაციას დაამატოს ან ამოშალოს გამოსაყენებელი თითის ანაბეჭდის ნიმუშები,"</string>
+ <string name="permdesc_manageFingerprint" msgid="178208705828055464">"საშუალებას აძლევს აპლიკაციას დაამატოს ან ამოშალოს გამოსაყენებელი თითის ანაბეჭდის ნიმუშები."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"თითის ანაბეჭდის აპარატის გამოყენება"</string>
- <string name="permdesc_useFingerprint" msgid="9165097460730684114">"საშუალებას აძლევს აპლიკაციას გამოიყენოს ავტენთიფიკაციისათვის თითის ანაბეჭდის აპარატი"</string>
+ <string name="permdesc_useFingerprint" msgid="9165097460730684114">"საშუალებას აძლევს აპლიკაციას ავტორიზაციისთვის გამოიყენოს თითის ანაბეჭდის აპარატი"</string>
<string name="permlab_audioWrite" msgid="2661772059799779292">"თქვენი მუსიკალური კოლექციის შეცვლა"</string>
<string name="permdesc_audioWrite" msgid="8888544708166230494">"აპი შეძლებს თქვენი მუსიკალური კოლექციის შეცვლას."</string>
<string name="permlab_videoWrite" msgid="128769316366746446">"თქვენი ვიდეოკოლექციის შეცვლა"</string>
@@ -531,7 +533,7 @@
<string name="biometric_error_canceled" msgid="349665227864885880">"ავტორიზაცია გაუქმდა"</string>
<string name="biometric_error_device_not_secured" msgid="6583143098363528349">"PIN-კოდი, ნიმუში ან პაროლი დაყენებული არ არის"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"აღმოჩენილია თითის ნაწილობრივი ანაბეჭდი. გთხოვთ, სცადოთ ხელახლა."</string>
- <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"თითის ანაბეჭდი ვერ მუშავდება. გთხოვთ, სცადოთ ხელახლა."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"თითის ანაბეჭდის დამუშავება ვერ მოხერხდა. გთხოვთ, ცადოთ ხელახლა."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"თითის ანაბეჭდის სენსორი დაბინძურებულია. გთხოვთ, გაასუფთაოთ და სცადოთ ხელახლა."</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"თითის აღება მეტისმეტად სწრაფად მოხდა. გთხოვთ, სცადოთ ხელახლა."</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"თითის აღება მეტისმეტად ნელა მოხდა. გთხოვთ, სცადოთ ხელახლა."</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"მონიშნეთ რათა შეწყვიტოთ USB-ის გამართვა"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"რეჟიმი „გარემო ტესტირებისთვის“ ჩართულია"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"დააბრუნეთ ქარხნული პარამეტრები „გარემო ტესტირებისთვის“ რეჟიმის გასათიშად."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"სერიული კონსოლი ჩართულია"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"მუშაობა შეფერხებულია. გასათიშად მონიშნეთ ჩამტვირთავი."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB პორტში აღმოჩენილია სითხე ან ჭუჭყი"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB პორტი ავტომატურად გათიშულია. შეეხეთ დამატებითი ინფორმაციისთვის."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"თანხმობა USB პორტის გამოყენებაზე"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"კატეგორიის გარეშე"</string>
<string name="importance_from_user" msgid="7318955817386549931">"ამ შეტყობინებების მნიშვნელობის დონე განისაზღვრა თქვენ მიერ."</string>
<string name="importance_from_person" msgid="9160133597262938296">"მნიშვნელოვანია ჩართული მომხმარებლების გამო."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"მიეცეს უფლება <xliff:g id="APP">%1$s</xliff:g>-ს <xliff:g id="ACCOUNT">%2$s</xliff:g>-ის მეშვეობით ახალი მომხმარებელი შექმნას (ამ ანგარიშის მქონე მომხმარებელი უკვე არსებობს)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"მიეცეს უფლება <xliff:g id="APP">%1$s</xliff:g>-ს <xliff:g id="ACCOUNT">%2$s</xliff:g>-ის მეშვეობით ახალი მომხმარებელი შექმნას?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"ენის დამატება"</string>
<string name="country_selection_title" msgid="2954859441620215513">"რეგიონის პარამეტრები"</string>
<string name="search_language_hint" msgid="7042102592055108574">"აკრიფეთ ენის სახელი"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 81f512b6a011..db9fad24f1ef 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Қолданбаға SMS хабарларын алу және өңдеу мүмкіндігін береді. Бұл қолданба құрылғыңызға жіберілген хабарларды сізге көрсетпестен қабылдай және жоя алады дегенді білдіреді."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"мәтін хабарларын алу (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Қолданбаға MMS хабарларын алу және өңдеу мүмкіндігін береді. Бұл қолданба құрылғыңызға жіберілген хабарларды сізге көрсетпестен қабылдай және жоя алады дегенді білдіреді."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Ұялы таратылым хабарларының бағытын өзгерту"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Ұялы таратылым хабарлары алынғаннан кейін, олардың бағытын өзгерту үшін қолданбаға ұялы таратылым модулімен байланыстыруға мүмкіндік береді. Ұялы таратылым ескертулері кей аймақтарда төтенше жағдайлар туралы хабарлау үшін беріледі. Төтенше жағдай туралы ұялы таратылым хабары алынғаннан кейін, зиянды қолданбалар құрылғы жұмысына кедергі келтіруі мүмкін."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"ұялы хабар тарату хабарларын оқу"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Қолданбаға ұялы таратылым хабарларын оқу мүмкіндігін береді. Ұялы таратылым дабылдары кейбір аймақтарда төтенше жағдай туралы ескерту үшін қолданылады. Төтенше ұялы хабарлар келгенде залалды қолданбалар құрылғының жұмысына кедергі жасауы мүмкін."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"жазылған ағындарды оқу"</string>
@@ -1362,6 +1364,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB түзетуін өшіру үшін таңдаңыз."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Сынақ бағдарламасы режимі қосылды"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Сынақ бағдарламасы режимін өшіру үшін зауыттық күйіне қайтарыңыз."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Сериялық консоль қосылды"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Өнімділікке әсер етеді. Өшіру үшін жүктегішті тексеріңіз."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB портына сұйықтық немесе қоқыс кірді"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB порты автоматты түрде өшірілді. Толығырақ ақпарат алу үшін түртіңіз."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB портын пайдалана беруге болады"</string>
@@ -1548,7 +1552,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Басқа опциялар"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <string name="storage_internal" msgid="3570990907910199483">"Ішкі ортақ қойма"</string>
+ <string name="storage_internal" msgid="3570990907910199483">"Ішкі ортақ жад"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD картасы"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD картасы"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB дискі"</string>
@@ -1891,10 +1895,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Санатқа жатқызылмаған"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Сіз осы хабарландырулардың маңыздылығын орнатасыз."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Қатысты адамдарға байланысты бұл маңызды."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="APP">%1$s</xliff:g> қолданбасына <xliff:g id="ACCOUNT">%2$s</xliff:g> есептік жазбасы бар жаңа пайдаланушы (мұндай есептік жазбаға ие пайдаланушы бұрыннан бар) жасауға рұқсат етілсін бе?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"<xliff:g id="APP">%1$s</xliff:g> қолданбасына <xliff:g id="ACCOUNT">%2$s</xliff:g> есептік жазбасы бар жаңа пайдаланушы жасауға рұқсат етілсін бе?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Тілді қосу"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Аймақ параметрі"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Тіл атауын теріңіз"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 539747b9652b..021da3ea4a42 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -325,7 +325,7 @@
<string name="capability_title_canPerformGestures" msgid="7418984730362576862">"ធ្វើកាយវិការ"</string>
<string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"អាចប៉ះ អូស ច្បិច និងធ្វើកាយវិការផ្សេងទៀត"</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"ចលនា​ស្នាមម្រាមដៃ"</string>
- <string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"អាចថត​ចលនា​ដែលមាន​សកម្មភាព​នៅលើ​ឧបករណ៍​ចាប់​ស្នាម​ម្រាមដៃ​របស់ឧបករណ៍បាន។"</string>
+ <string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"អាចចាប់យក​ចលនា​ដែលធ្វើនៅលើ​នៅលើ​ឧបករណ៍​ចាប់​ស្នាម​ម្រាមដៃ​របស់ឧបករណ៍បាន។"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"បិទ ឬ​កែ​របារ​ស្ថានភាព"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ឲ្យ​កម្មវិធី​បិទ​របារ​ស្ថានភាព ឬ​បន្ថែម និង​លុប​រូប​តំណាង​ប្រព័ន្ធ។"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ធ្វើជារបារស្ថានភាព"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"ឲ្យ​កម្មវិធី​ទទួល និង​ដំណើរការ​​សារ MMS ។ មាន​ន័យ​ថា កម្មវិធី​អាច​ត្រួតពិនិត្យ​ ឬ​លុប​សារ​ដែល​បាន​ផ្ញើ​ទៅ​ឧបករណ៍​របស់​អ្នក ដោយ​​មិន​បង្ហាញ​អ្នក។"</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"ទទួល​សារ​អត្ថបទ (MMS​)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"ឲ្យ​កម្មវិធី​ទទួល និង​ដំណើរការ​​សារ​ MMS ។ វា​មាន​ន័យ​ថា កម្មវិធី​អាច​តាមដាន​ ឬ​លុប​សារ​ដែល​បាន​ផ្ញើ​ទៅ​ឧបករណ៍​របស់​អ្នក​ដោយ​មិន​បង្ហាញ​ពួកវា។"</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"បញ្ជូន​សារ​ផ្សាយចល័ត​បន្ត"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"អនុញ្ញាតឱ្យ​កម្មវិធី​ភ្ជាប់ទៅ​ម៉ូឌុល​ការផ្សាយចល័ត ដើម្បីបញ្ជូន​សារផ្សាយ​ចល័តបន្ត នៅពេល​ទទួលបាន​សារទាំងនោះ។ ការជូនដំណឹងអំពី​ការផ្សាយចល័ត​ត្រូវបានបញ្ជូនទៅ​ទីតាំងមួយចំនួន ដើម្បីព្រមាន​អ្នក​អំពីស្ថានភាព​អាសន្ន។ កម្មវិធី​គ្រោះថ្នាក់​អាច​រំខាន​ដល់ដំណើរការ ឬប្រតិបត្តិការ​ឧបករណ៍​របស់អ្នក នៅពេល​ទទួលបានការផ្សាយ​ចល័តពេលមានអាសន្ន​។"</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"អាន​សារ​ប្រកាស​ចល័ត"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"ឲ្យ​កម្មវិធី​អាន​សារ​ប្រកាស​ការ​ហៅ​ដែល​ឧបករណ៍​របស់​​អ្នក​បាន​ទទួល។ ការ​ជូន​ដំណឹង​ប្រកាស​ចល័ត​ត្រូវ​បាន​បញ្ជូន​ទៅ​ទីតាំង​មួយ​ចំនួន ដើម្បី​ព្រមាន​អ្នក​អំពី​ស្ថានភាព​អាសន្ន។ កម្មវិធី​ព្យាបាទ​អាច​ជ្រៀតជ្រែក​ការ​អនុវត្ត ឬ​ប្រតិបត្តិការ​ឧបករណ៍​របស់​អ្នក​​ពេល​ទទួល​ការ​ប្រកាស​ចល័ត​ពេល​អាសន្ន។"</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"អាន​អត្ថបទ​ព័ត៌មាន​បាន​ជាវ"</string>
@@ -513,7 +515,7 @@
<string name="permlab_useBiometric" msgid="8837753668509919318">"ប្រើ​ឧបករណ៍​ស្កេន​ស្នាមម្រាមដៃ"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"អនុញ្ញាត​ឱ្យ​កម្មវិធី​ប្រើ​ឧបករណ៍​ស្កេន​ស្នាមម្រាមដៃ​សម្រាប់​ការផ្ទៀងផ្ទាត់"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"គ្រប់គ្រងផ្នែករឹងស្នាមម្រាមដៃ"</string>
- <string name="permdesc_manageFingerprint" msgid="178208705828055464">"អនុញ្ញាតឲ្យកម្មវិធីប្រើវិធីសាស្ត្របន្ថែម និងលុបពុម្ពម្រាមដៃសម្រាប់ប្រើប្រាស់។"</string>
+ <string name="permdesc_manageFingerprint" msgid="178208705828055464">"អនុញ្ញាតឲ្យកម្មវិធីប្រើវិធីនានា ដើម្បីបញ្ចូល និងលុបទម្រង់គំរូស្នាមម្រាមដៃសម្រាប់ប្រើប្រាស់។"</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"ប្រើផ្នែករឹងស្នាមម្រាមដៃ"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"អនុញ្ញាតឲ្យកម្មវិធីប្រើផ្នែករឹងស្នាមម្រាមដៃសម្រាប់ការផ្ទៀងផ្ទាត់"</string>
<string name="permlab_audioWrite" msgid="2661772059799779292">"កែប្រែ​បណ្ដុំ​តន្ត្រី​របស់​អ្នក"</string>
@@ -532,7 +534,7 @@
<string name="biometric_error_device_not_secured" msgid="6583143098363528349">"គ្មាន​ការកំណត់​កូដ pin លំនាំ ឬពាក្យសម្ងាត់​ទេ"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"បានផ្តិតយកស្នាមម្រាមដៃមិនពេញលក្ខណៈ។ សូមព្យាយាមម្តងទៀត។"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"មិនអាចដំណើរការស្នាមម្រាមដៃបានទេ។ សូមព្យាយាមម្តងទៀត។"</string>
- <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ឧបករណ៍ផ្តិតម្រាមដៃប្រឡាក់ហើយ។ សូមសម្អាត ហើយព្យាយាមម្តងទៀត។"</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ឧបករណ៍ចាប់ស្នាមម្រាមដៃគឺប្រឡាក់។ សូមសម្អាត រួចព្យាយាមម្តងទៀត។"</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"ម្រាមដៃផ្លាស់ទីលឿនពេក។ សូមព្យាយាមម្តងទៀត។"</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"ចលនាម្រាមដៃយឺតពេកហើយ។ សូមព្យាយាមម្តងទៀត។"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -542,7 +544,7 @@
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"បានផ្ទៀងផ្ទាត់​មុខ សូម​ចុច​បញ្ជាក់"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ផ្នែករឹងស្នាមម្រាមដៃមិនមានទេ។"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"មិនអាចផ្ទុកស្នាមម្រាមដៃទេ។ សូមយកស្នាមម្រាមដៃដែលមានស្រាប់ចេញ។"</string>
- <string name="fingerprint_error_timeout" msgid="3927186043737732875">"ការផ្តិតម្រាមដៃបានអស់ពេល។ សូមព្យាយាមម្តងទៀត។"</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"ស្នាមម្រាមដៃបានអស់ម៉ោង។ សូមព្យាយាមម្តងទៀត។"</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"បានបោះបង់ប្រតិបត្តិការស្នាមម្រាមដៃ។"</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"ប្រតិបត្តិការ​ស្នាម​ម្រាម​ដៃ​ត្រូវ​បាន​បោះ​បង់​ដោយ​អ្នក​ប្រើប្រាស់។"</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"ព្យាយាមចូលច្រើនពេកហើយ។ សូមព្យាយាមម្តងទៀតពេលក្រោយ។"</string>
@@ -553,7 +555,7 @@
<string name="fingerprint_name_template" msgid="5870957565512716938">"ម្រាមដៃ <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
- <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"រូបតំណាងស្នាមម្រាមដៃ"</string>
+ <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"រូបស្នាមម្រាមដៃ"</string>
<string name="permlab_manageFace" msgid="7262837876352591553">"គ្រប់គ្រង​ហាតវែរ​ដោះសោតាមទម្រង់មុខ"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"អនុញ្ញាតឱ្យកម្មវិធីប្រើវិធីសាស្ត្រដើម្បី​បញ្ចូល និងលុបទម្រង់​គំរូ​ផ្ទៃមុខសម្រាប់ប្រើប្រាស់។"</string>
<string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"ប្រើ​ហាតវែរ​ដោះសោ​តាមទម្រង់មុខ"</string>
@@ -1161,7 +1163,7 @@
<string name="clearDefaultHintMsg" msgid="3252584689512077257">"សម្អាត​លំនាំដើម​ក្នុង​ការកំណត់​ប្រព័ន្ធ &gt; កម្មវិធី &gt; ទាញ​យក។"</string>
<string name="chooseActivity" msgid="7486876147751803333">"ជ្រើស​សកម្មភាព​​"</string>
<string name="chooseUsbActivity" msgid="6894748416073583509">"ជ្រើស​កម្មវិធី​សម្រាប់​ឧបករណ៍​យូអេសប៊ី"</string>
- <string name="noApplications" msgid="2991814273936504689">"គ្មាន​កម្មវិធី​អាច​អនុវត្ត​សកម្មភាព​នេះ។"</string>
+ <string name="noApplications" msgid="2991814273936504689">"មិន​មាន​​កម្មវិធី​ដែលអាចធ្វើ​សកម្មភាពនេះ​បាន​ទេ។"</string>
<string name="aerr_application" msgid="250320989337856518">"<xliff:g id="APPLICATION">%1$s</xliff:g> បានឈប់"</string>
<string name="aerr_process" msgid="6201597323218674729">"<xliff:g id="PROCESS">%1$s</xliff:g> បានឈប់"</string>
<string name="aerr_application_repeated" msgid="3146328699537439573">"<xliff:g id="APPLICATION">%1$s</xliff:g> ឈប់ដំណើរការម្តងហើយម្តងទៀត"</string>
@@ -1363,6 +1365,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"ជ្រើស​រើស ដើម្បី​បិទ​ការ​កែ​កំហុសតាម USB ។"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"បាន​បើក​មុខងារប្រមូលធ្វើតេស្ត"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"ធ្វើការកំណត់ដូចដើមឡើងវិញ ដើម្បី​បិទ​មុខងារប្រមូលធ្វើតេស្ត។"</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"កុងសូល​ស៊េរី​ត្រូវបានបើក"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"ប្រតិបត្តិការ​ទទួលរង​ការប៉ះពាល់។ សូម​ពិនិត្យមើល​កម្មវិធី​ដំណើរការ​ប្រព័ន្ធ ដើម្បី​បិទ។"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"មានទឹក ឬ​កម្ទេចផ្សេងៗ​នៅក្នុងរន្ធ USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"រន្ធ USB ត្រូវបាន​បិទ​ដោយ​ស្វ័យប្រវត្តិ។ សូមចុច​ដើម្បី​ស្វែងយល់​បន្ថែម។"</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"អាច​ប្រើរន្ធ USB បាន"</string>
@@ -1512,7 +1516,7 @@
<string name="sync_do_nothing" msgid="3743764740430821845">"មិន​ធ្វើអ្វី​ទេ​ឥឡូវ"</string>
<string name="choose_account_label" msgid="5655203089746423927">"ជ្រើស​គណនី"</string>
<string name="add_account_label" msgid="2935267344849993553">"បញ្ចូល​គណនី​"</string>
- <string name="add_account_button_label" msgid="3611982894853435874">"បន្ថែម​គណនី"</string>
+ <string name="add_account_button_label" msgid="3611982894853435874">"បញ្ចូល​គណនី"</string>
<string name="number_picker_increment_button" msgid="2412072272832284313">"បង្កើន"</string>
<string name="number_picker_decrement_button" msgid="476050778386779067">"បន្ថយ"</string>
<string name="number_picker_increment_scroll_mode" msgid="5259126567490114216">"<xliff:g id="VALUE">%s</xliff:g> ប៉ះ និងសង្កត់ឲ្យជាប់"</string>
@@ -1581,8 +1585,8 @@
<string name="expires_on" msgid="3676242949915959821">"ផុត​កំណត់​នៅ៖"</string>
<string name="serial_number" msgid="758814067660862493">"លេខ​ស៊េរី៖"</string>
<string name="fingerprints" msgid="4516019619850763049">"ស្នាម​ម្រាមដៃ​៖"</string>
- <string name="sha256_fingerprint" msgid="4391271286477279263">"ស្នាម​ម្រាមដៃ SHA​-256 ​៖"</string>
- <string name="sha1_fingerprint" msgid="7930330235269404581">"ស្នាម​ម្រាម​ដៃ SHA-1 ៖"</string>
+ <string name="sha256_fingerprint" msgid="4391271286477279263">"ស្នាម​ម្រាមដៃ SHA​-256៖"</string>
+ <string name="sha1_fingerprint" msgid="7930330235269404581">"ស្នាម​ម្រាម​ដៃ SHA-1៖"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"មើល​ទាំងអស់"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"ជ្រើស​សកម្មភាព"</string>
<string name="share_action_provider_share_with" msgid="5247684435979149216">"ចែករំលែក​ជា​មួយ"</string>
@@ -1892,10 +1896,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"មិន​​បែងចែក​ប្រភេទ"</string>
<string name="importance_from_user" msgid="7318955817386549931">"អ្នកបានកំណត់សារៈសំខាន់នៃការជូនដំណឹងទាំងនេះ"</string>
<string name="importance_from_person" msgid="9160133597262938296">"វាមានសារៈសំខាន់ដោយសារតែមនុស្សដែលពាក់ព័ន្ធ"</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"អនុញ្ញាតឱ្យ <xliff:g id="APP">%1$s</xliff:g> បង្កើតអ្នកប្រើប្រាស់​ថ្មីដោយប្រើ <xliff:g id="ACCOUNT">%2$s</xliff:g> (អ្នកប្រើប្រាស់ដែលមានគណនីនេះមានរួចហើយ) ដែរឬទេ?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"អនុញ្ញាតឱ្យ <xliff:g id="APP">%1$s</xliff:g> បង្កើតអ្នកប្រើប្រាស់​ថ្មីដោយប្រើ <xliff:g id="ACCOUNT">%2$s</xliff:g> ដែរឬទេ?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"បន្ថែមភាសា"</string>
<string name="country_selection_title" msgid="2954859441620215513">"ចំណូលចិត្តតំបន់"</string>
<string name="search_language_hint" msgid="7042102592055108574">"វាយបញ្ចូលឈ្មោះភាសា"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index c7d0feae9b34..edcb6d1dfee0 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -324,8 +324,8 @@
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ಪ್ರದರ್ಶನದ ಝೂಮ್ ಮಟ್ಟ ಮತ್ತು ಸ್ಥಾನ ನಿರ್ಧಾರವನ್ನು ನಿಯಂತ್ರಿಸಿ."</string>
<string name="capability_title_canPerformGestures" msgid="7418984730362576862">"ಗೆಸ್ಚರ್‌ಗಳನ್ನು ಮಾಡಿ"</string>
<string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"ಟ್ಯಾಪ್ ಮಾಡಬಹುದು, ಸ್ವೈಪ್ ಮಾಡಬಹುದು, ಪಿಂಚ್ ಮಾಡಬಹುದು ಮತ್ತು ಇತರ ಗೆಸ್ಚರ್‌ಗಳನ್ನು ಮಾಡಬಹುದು."</string>
- <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್‌ ಸೂಚಕಗಳು"</string>
- <string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"ಸಾಧನದ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್‌ನಲ್ಲಿ ನಡೆಸಿದ ಸೂಚಕಗಳನ್ನು ಕ್ಯಾಪ್ಚರ್ ಮಾಡಿ."</string>
+ <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್‌ ಗೆಶ್ಚರ್‌ಗಳು"</string>
+ <string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"ಸಾಧನದ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್‌ನಲ್ಲಿ ನಡೆಸಿದ ಗೆಶ್ಚರ್‌ಗಳನ್ನು ಕ್ಯಾಪ್ಚರ್ ಮಾಡಿ."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"ಸ್ಥಿತಿ ಪಟ್ಟಿಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ ಇಲ್ಲವೇ ಮಾರ್ಪಡಿಸಿ"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ಸ್ಥಿತಿ ಪಟ್ಟಿಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಅಥವಾ ಸೇರಿಸಲು ಮತ್ತು ಸಿಸ್ಟಂ ಐಕಾನ್‌ಗಳನ್ನು ತೆಗೆದುಹಾಕಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅವಕಾಶ ನೀಡುತ್ತದೆ."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ಸ್ಥಿತಿ ಪಟ್ಟಿಯಾಗಿರಲು"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"SMS ಸಂದೇಶಗಳನ್ನು ಸ್ವೀಕರಿಸಲು ಮತ್ತು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಅಪ್ಲಿಕೇಶನ್‍‍ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ. ಇದರರ್ಥ, ನಿಮ್ಮ ಸಾಧನಕ್ಕೆ ಕಳುಹಿಸಲಾಗಿರುವ ಸಂದೇಶಗಳನ್ನು ನಿಮಗೆ ತೋರಿಸದೆಯೇ, ಅಪ್ಲಿಕೇಶನ್ ಅವುಗಳನ್ನು ಮಾನಿಟರ್ ಮಾಡಬಹುದು ಅಥವಾ ಅಳಿಸಬಹುದು."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"ಪಠ್ಯ ಸಂದೇಶಗಳನ್ನು ಸ್ವೀಕರಿಸಿ (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"MMS ಸಂದೇಶಗಳನ್ನು ಸ್ವೀಕರಿಸಲು ಮತ್ತು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಅಪ್ಲಿಕೇಶನ್‍‍ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ. ಇದರರ್ಥ, ನಿಮ್ಮ ಸಾಧನಕ್ಕೆ ಕಳುಹಿಸಲಾಗಿರುವ ಸಂದೇಶಗಳನ್ನು ನಿಮಗೆ ತೋರಿಸದೆಯೇ, ಅಪ್ಲಿಕೇಶನ್ ಅವುಗಳನ್ನು ಮಾನಿಟರ್ ಮಾಡಬಹುದು ಅಥವಾ ಅಳಿಸಬಹುದು."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"ಸೆಲ್ ಪ್ರಸಾರ ಸಂದೇಶಗಳನ್ನು ಫಾರ್ವರ್ಡ್ ಮಾಡಿ"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"ಸೆಲ್ ಪ್ರಸಾರವು ಸಂದೇಶಗಳನ್ನು ಸ್ವೀಕರಿಸಿದ ರೀತಿಯಲ್ಲಿಯೇ ಫಾರ್ವರ್ಡ್ ಮಾಡಲು, ಸೆಲ್ ಪ್ರಸಾರ ಮಾಡ್ಯುಲ್‌ ಅನ್ನು ಪ್ರತಿಬಂಧಿಸಲು ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಕೆಲವು ಸ್ಥಳಗಳಲ್ಲಿ ತುರ್ತು ಸ್ಥಿತಿಗಳ ಕುರಿತು ನಿಮಗೆ ಎಚ್ಚರಿಸಲು ಸೆಲ್ ಪ್ರಸಾರದ ಎಚ್ಚರಿಕೆಗಳನ್ನು ಕಳುಹಿಸಲಾಗುತ್ತದೆ. ತುರ್ತು ಸೆಲ್‌ ಪ್ರಸಾರವನ್ನು ಸ್ವೀಕರಿಸಿದಾಗ ನಿಮ್ಮ ಸಾಧನದ ಕಾರ್ಯಾಚರಣೆ ಅಥವಾ ಕಾರ್ಯಕ್ಷಮತೆಗೆ ದುರುದ್ದೇಶಪೂರಿತ ಆ್ಯಪ್‌ಗಳು ಹಸ್ತಕ್ಷೇಪ ಮಾಡಬಹುದು."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"ಸೆಲ್ ಪ್ರಸಾರದ ಸಂದೇಶಗಳನ್ನು ಓದಿರಿ"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"ನಿಮ್ಮ ಸಾಧನದಿಂದ ಸ್ವೀಕರಿಸಿದ ಸೆಲ್ ಪ್ರಸಾರ ಸಂದೇಶಗಳನ್ನು ರೀಡ್ ಮಾಡಲು ಅಪ್ಲಿಕೇಶನ್‌‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಸೆಲ್ ಪ್ರಸಾರ ಎಚ್ಚರಿಕೆಗಳನ್ನು ತುರ್ತು ಸಂದರ್ಭಗಳಲ್ಲಿ ನಿಮಗೆ ಎಚ್ಚರಿಸುವ ಸಲುವಾಗಿ ಕೆಲವು ಸ್ಥಳಗಳಲ್ಲಿ ವಿತರಿಸಲಾಗುತ್ತದೆ. ದುರುದ್ದೇಶಪೂರಿತ ಅಪ್ಲಿಕೇಶನ್‌‌‌ಗಳು ತುರ್ತು ಸೆಲ್ ಪ್ರಸಾರವನ್ನು ಸ್ವೀಕರಿಸುವಾಗ, ನಿಮ್ಮ ಸಾಧನದ ಕಾರ್ಯಕ್ಷಮತೆ ಇಲ್ಲವೇ ಕಾರ್ಯಾಚರಣೆಯಲ್ಲಿ ಹಸ್ತಕ್ಷೇಪ ಮಾಡಬಹುದು."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"ಚಂದಾದಾರ ಫೀಡ್‌ಗಳನ್ನು ಓದಿ"</string>
@@ -512,10 +514,10 @@
<string name="permdesc_requestPasswordComplexity" msgid="4730994229754212347">"ಆ್ಯಪ್‌ಗೆ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಸಂಕೀರ್ಣತೆ ಮಟ್ಟವನ್ನು (ಅಧಿಕ, ಮಧ್ಯಮ, ಕಡಿಮೆ ಅಥವಾ ಯಾವುದೂ ಅಲ್ಲ) ತಿಳಿದುಕೊಳ್ಳಲು ಅನುಮತಿಸುತ್ತದೆ, ಅದು ಉದ್ದದ ಸಂಭವನೀಯ ಶ್ರೇಣಿ ಮತ್ತು ಸ್ಕ್ರೀನ್ ಲಾಕ್‌ನ ವಿಧವನ್ನು ಸೂಚಿಸುತ್ತದೆ. ಬಳಕೆದಾರರು ನಿರ್ದಿಷ್ಟ ಹಂತದವರೆಗೆ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅನ್ನು ಅಪ್‌ಡೇಟ್ ಮಾಡಬಹುದು, ಆದರೆ ಅವರು ಅದನ್ನು ನಿರ್ಲಕ್ಷಿಸಬಹುದು ಮತ್ತು ಅದರಿಂದ ಹೊರಬರಬಹುದು ಎಂಬುದನ್ನು ಸಹ ಆ್ಯಪ್ ಬಳಕೆದಾರರಿಗೆ ಸೂಚಿಸುತ್ತದೆ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅನ್ನು ಪಠ್ಯದ ರೂಪದಲ್ಲಿ ಸಂಗ್ರಹಿಸಲಾಗಿರುವುದಿಲ್ಲ, ಇದರಿಂದಾಗಿ ಆ್ಯಪ್‌ಗೆ ಸರಿಯಾದ ಪಾಸ್‌ವರ್ಡ್ ಗೊತ್ತಿರುವುದಿಲ್ಲ ಎಂಬುದನ್ನು ಗಮನಿಸಿ."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"ಬಯೋಮೆಟ್ರಿಕ್ ಹಾರ್ಡ್‌ವೇರ್‌ ಬಳಸಿ"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"ಪ್ರಮಾಣೀಕರಣಕ್ಕಾಗಿ ಬಯೋಮೆಟ್ರಿಕ್ ಹಾರ್ಡ್‌ವೇರ್ ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ"</string>
- <string name="permlab_manageFingerprint" msgid="5640858826254575638">"ಬೆರಳಚ್ಚು ಹಾರ್ಡ್‌ವೇರ್ ನಿರ್ವಹಿಸಿ"</string>
- <string name="permdesc_manageFingerprint" msgid="178208705828055464">"ಬಳಕೆಗೆ ಬೆರಳಚ್ಚು ಟೆಂಪ್ಲೇಟ್‌ಗಳನ್ನು ಸೇರಿಸಲು ಮತ್ತು ಅಳಿಸಲು ವಿಧಾನಗಳನ್ನು ಮನವಿ ಮಾಡಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
- <string name="permlab_useFingerprint" msgid="3150478619915124905">"ಬೆರಳಚ್ಚು ಹಾರ್ಡ್‌ವೇರ್ ಬಳಸಿ"</string>
- <string name="permdesc_useFingerprint" msgid="9165097460730684114">"ಪ್ರಮಾಣೀಕರಣಕ್ಕಾಗಿ ಬೆರಳಚ್ಚು ಹಾರ್ಡ್‌ವೇರ್ ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ"</string>
+ <string name="permlab_manageFingerprint" msgid="5640858826254575638">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಹಾರ್ಡ್‌ವೇರ್ ನಿರ್ವಹಿಸಿ"</string>
+ <string name="permdesc_manageFingerprint" msgid="178208705828055464">"ಬಳಕೆಗೆ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಟೆಂಪ್ಲೇಟ್‌ಗಳನ್ನು ಸೇರಿಸಲು ಮತ್ತು ಅಳಿಸಲು ವಿಧಾನಗಳನ್ನು ಮನವಿ ಮಾಡಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
+ <string name="permlab_useFingerprint" msgid="3150478619915124905">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಹಾರ್ಡ್‌ವೇರ್ ಬಳಸಿ"</string>
+ <string name="permdesc_useFingerprint" msgid="9165097460730684114">"ಪ್ರಮಾಣೀಕರಣಕ್ಕಾಗಿ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಹಾರ್ಡ್‌ವೇರ್ ಬಳಸಲು ಆ್ಯಪ್‍‍ಗೆ ಅನುಮತಿಸುತ್ತದೆ"</string>
<string name="permlab_audioWrite" msgid="2661772059799779292">"ನಿಮ್ಮ ಸಂಗೀತ ಸಂಗ್ರಹಣೆಯನ್ನು ಮಾರ್ಪಡಿಸಿ"</string>
<string name="permdesc_audioWrite" msgid="8888544708166230494">"ನಿಮ್ಮ ಸಂಗೀತ ಸಂಗ್ರಹಣೆಯನ್ನು ಮಾರ್ಪಡಿಸಲು ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_videoWrite" msgid="128769316366746446">"ನಿಮ್ಮ ವೀಡಿಯೊ ಸಂಗ್ರಹಣೆಯನ್ನು ಮಾರ್ಪಡಿಸಿ"</string>
@@ -530,9 +532,9 @@
<string name="biometric_not_recognized" msgid="5770511773560736082">"ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string>
<string name="biometric_error_canceled" msgid="349665227864885880">"ಪ್ರಮಾಣೀಕರಣವನ್ನು ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="biometric_error_device_not_secured" msgid="6583143098363528349">"ಪಿನ್, ಪ್ಯಾಟರ್ನ್ ಅಥವಾ ಪಾಸ್‌ವರ್ಡ್ ಸೆಟ್ ಮಾಡಿಲ್ಲ"</string>
- <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ಭಾಗಶಃ ಬೆರಳಚ್ಚು ಪತ್ತೆಯಾಗಿದೆ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
- <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ಬೆರಳಚ್ಚು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
- <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ಬೆರಳಚ್ಚು ಸೆನ್ಸರ್ ಮಲಿನಗೊಂಡಿದೆ. ದಯವಿಟ್ಟು ಅದನ್ನು ಸ್ವಚ್ಛಗೊಳಿಸಿ ಹಾಗೂ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ಭಾಗಶಃ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಪತ್ತೆಯಾಗಿದೆ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್ ಮಲಿನಗೊಂಡಿದೆ. ದಯವಿಟ್ಟು ಅದನ್ನು ಸ್ವಚ್ಛಗೊಳಿಸಿ ಹಾಗೂ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"ಬೆರಳನ್ನು ಅತಿ ವೇಗವಾಗಿ ಸರಿಸಲಾಗಿದೆ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"ಬೆರಳನ್ನು ತುಂಬಾ ನಿಧಾನವಾಗಿ ಸರಿಸಲಾಗಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string-array name="fingerprint_acquired_vendor">
@@ -540,20 +542,20 @@
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್ನು ಪ್ರಮಾಣೀಕರಣ ಮಾಡಲಾಗಿದೆ"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"ಮುಖವನ್ನು ದೃಢೀಕರಿಸಲಾಗಿದೆ"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"ಮುಖವನ್ನು ದೃಢೀಕರಿಸಲಾಗಿದೆ, ದೃಢೀಕರಣವನ್ನು ಒತ್ತಿ"</string>
- <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ಬೆರಳಚ್ಚು ಹಾರ್ಡ್‌ವೇರ್‌ ಲಭ್ಯವಿಲ್ಲ."</string>
- <string name="fingerprint_error_no_space" msgid="1055819001126053318">"ಬೆರಳಚ್ಚು ಸಂಗ್ರಹಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಬೆರಳಚ್ಚು ತೆಗೆದುಹಾಕಿ."</string>
- <string name="fingerprint_error_timeout" msgid="3927186043737732875">"ಬೆರಳಚ್ಚು ಅವಧಿ ಮೀರಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
- <string name="fingerprint_error_canceled" msgid="4402024612660774395">"ಬೆರಳಚ್ಚು ಕಾರ್ಯಾಚರಣೆಯನ್ನು ರದ್ದುಮಾಡಲಾಗಿದೆ."</string>
- <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"ಬಳಕೆದಾರರಿಂದ ಫಿಂಗರ್‌ ಫ್ರಿಂಟ್‌ ಕಾರ್ಯಾಚರಣೆಯನ್ನು ರದ್ದುಪಡಿಸಲಾಗಿದೆ."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಹಾರ್ಡ್‌ವೇರ್‌ ಲಭ್ಯವಿಲ್ಲ."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸಂಗ್ರಹಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್ನು ತೆಗೆದುಹಾಕಿ."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅವಧಿ ಮೀರಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="fingerprint_error_canceled" msgid="4402024612660774395">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಕಾರ್ಯಾಚರಣೆಯನ್ನು ರದ್ದುಮಾಡಲಾಗಿದೆ."</string>
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"ಬಳಕೆದಾರರು ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಕಾರ್ಯಾಚರಣೆಯನ್ನು ರದ್ದುಪಡಿಸಿದ್ದಾರೆ."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"ಹಲವಾರು ಪ್ರಯತ್ನಗಳು. ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"ಹಲವು ಬಾರಿ ಪ್ರಯತ್ನಿಸಿದ್ದೀರಿ. ಫಿಂಗರ್‌ ಫ್ರಿಂಟ್‌ ಸೆನ್ಸಾರ್ ನಿಷ್ಕ್ರಿಯಗೊಂಡಿದೆ."</string>
+ <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"ಹಲವು ಬಾರಿ ಪ್ರಯತ್ನಿಸಿದ್ದೀರಿ. ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್ ನಿಷ್ಕ್ರಿಯಗೊಂಡಿದೆ."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="fingerprint_error_no_fingerprints" msgid="7654382120628334248">"ಯಾವುದೇ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್‌ ಅನ್ನು ನೋಂದಣಿ ಮಾಡಿಲ್ಲ."</string>
<string name="fingerprint_error_hw_not_present" msgid="409523969613176352">"ಈ ಸಾಧನವು ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್‌‌ ಅನ್ನು ಹೊಂದಿಲ್ಲ."</string>
<string name="fingerprint_name_template" msgid="5870957565512716938">"ಫಿಂಗರ್ <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
- <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ಬೆರಳಚ್ಚು ಐಕಾನ್"</string>
+ <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಐಕಾನ್"</string>
<string name="permlab_manageFace" msgid="7262837876352591553">"ಫೇಸ್ ಅನ್‌ಲಾಕ್ ಹಾರ್ಡ್‌ವೇರ್ ಅನ್ನು ನಿರ್ವಹಿಸಿ"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"ಬಳಕೆಗೆ ಮುಖದ ಟೆಂಪ್ಲೇಟ್‌ಗಳನ್ನು ಸೇರಿಸಲು ಮತ್ತು ಅಳಿಸಲು ವಿಧಾನಗಳನ್ನು ಮನವಿ ಮಾಡಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"ಫೇಸ್ ಅನ್‌ಲಾಕ್ ಹಾರ್ಡ್‌ವೇರ್ ಬಳಸಿ"</string>
@@ -1362,6 +1364,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB ಡೀಬಗ್‌ ಮಾಡುವಿಕೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಆಯ್ಕೆ ಮಾಡಿ."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"ಸ್ವಯಂ ಪರೀಕ್ಷೆಯಾಗುವಿಕೆ ಮೋಡ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"ಸ್ವಯಂ ಪರೀಕ್ಷೆಯಾಗುವಿಕೆ ಮೋಡ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಫ್ಯಾಕ್ಟರಿ ರಿಸೆಟ್ ಮಾಡಬೇಕು."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"ಸರಣಿ ಕನ್ಸೋಲ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"ಕಾರ್ಯಕ್ಷಮತೆಯ ಮೇಲೆ ಪರಿಣಾಮ ಬೀರುತ್ತದೆ. ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು, ಬೂಟ್‌ಲೋಡರ್ ಅನ್ನು ಪರಿಶೀಲಿಸಿ."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB ಪೋರ್ಟ್‌ನಲ್ಲಿ ದ್ರವ ಅಥವಾ ಧೂಳಿನ ಕಣಗಳಿವೆ"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB ಪೋರ್ಟ್ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ನಿಷ್ಕ್ರಿಯಗೊಂಡಿದೆ. ಇನ್ನಷ್ಟು ತಿಳಿಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB ಪೋರ್ಟ್ ಬಳಸಲು ಸರಿ ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಿ"</string>
@@ -1581,7 +1585,7 @@
<string name="serial_number" msgid="758814067660862493">"ಕ್ರಮ ಸಂಖ್ಯೆ:"</string>
<string name="fingerprints" msgid="4516019619850763049">"ಫಿಂಗರ್ ಪ್ರಿಂಟ್‌ಗಳು:"</string>
<string name="sha256_fingerprint" msgid="4391271286477279263">"SHA-256 ಬೆರಳಚ್ಚು:"</string>
- <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 ಬೆರಳಚ್ಚು:"</string>
+ <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 ಫಿಂಗರ್‌ಪ್ರಿಂಟ್:"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"ಎಲ್ಲವನ್ನೂ ನೋಡಿ"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"ಚಟುವಟಿಕೆಯನ್ನು ಆರಿಸಿ"</string>
<string name="share_action_provider_share_with" msgid="5247684435979149216">"ಹಂಚಿಕೊಳ್ಳಿ"</string>
@@ -1891,10 +1895,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"ವರ್ಗೀಕರಿಸದಿರುವುದು"</string>
<string name="importance_from_user" msgid="7318955817386549931">"ನೀವು ಈ ಅಧಿಸೂಚನೆಗಳ ಪ್ರಾಮುಖ್ಯತೆಯನ್ನು ಹೊಂದಿಸಿರುವಿರಿ."</string>
<string name="importance_from_person" msgid="9160133597262938296">"ಜನರು ತೊಡಗಿಕೊಂಡಿರುವ ಕಾರಣ ಇದು ಅತ್ಯಂತ ಪ್ರಮುಖವಾಗಿದೆ."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="ACCOUNT">%2$s</xliff:g> (ಈ ಖಾತೆಯ ಬಳಕೆದಾರರು ಈಗಾಗಲೇ ಅಸ್ತಿತ್ವದಲ್ಲಿದ್ದಾರೆ) ಮೂಲಕ ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲು <xliff:g id="APP">%1$s</xliff:g> ಗೆ ಅನುಮತಿಸಬೇಕೆ ?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"<xliff:g id="ACCOUNT">%2$s</xliff:g> ಮೂಲಕ ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲು <xliff:g id="APP">%1$s</xliff:g> ಗೆ ಅನುಮತಿಸುವುದೇ ?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"ಭಾಷೆ ಸೇರಿಸಿ"</string>
<string name="country_selection_title" msgid="2954859441620215513">"ಪ್ರದೇಶ ಪ್ರಾಶಸ್ತ್ಯ"</string>
<string name="search_language_hint" msgid="7042102592055108574">"ಭಾಷೆ ಹೆಸರನ್ನು ಟೈಪ್ ಮಾಡಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index c0dd8b127514..2f06beeacc6b 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"앱이 SMS 메시지를 수신하고 처리할 수 있도록 허용합니다. 앱이 사용자에게 표시하지 않고 기기로 전송된 메시지를 확인 또는 삭제할 수도 있습니다."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"문자 메시지 받기(MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"앱이 MMS 메시지를 수신하고 처리할 수 있도록 허용합니다. 앱이 사용자에게 표시하지 않고 기기로 전송된 메시지를 확인 또는 삭제할 수도 있습니다."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"셀 브로드캐스트 메시지 전달"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"셀 브로드캐스트 메시지를 수신하자마자 전달하기 위해 앱이 셀 브로드캐스트 모듈에 연결하도록 허용합니다. 비상 상황임을 알리기 위해 일부 지역에서 셀 브로드캐스트 경고가 전달됩니다. 비상 셀 브로드캐스트를 수신할 때 악성 앱이 기기의 성능이나 작동을 방해할 수 있습니다."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"셀 브로드캐스트 메시지 읽기"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"앱이 기기가 수신한 셀 브로드캐스트 메시지를 읽을 수 있도록 합니다. 비상 상황임을 알리기 위해 일부 지역에서 셀 브로드캐스트 경고가 전달됩니다. 비상 셀 브로드캐스트를 수신할 때 악성 앱이 기기의 성능이나 작동을 방해할 수 있습니다."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"가입된 피드 읽기"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB 디버깅을 사용하지 않으려면 선택합니다."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"테스트 하네스 모드 사용 설정됨"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"테스트 하네스 모드를 사용 중지하려면 초기화하세요."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"시리얼 콘솔 사용 설정됨"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"성능에 영향을 미쳤습니다. 사용 중지하려면 부트로더를 확인하세요."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB 포트에서 액체 또는 이물질 감지됨"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB 포트가 자동으로 사용 중지되었습니다. 자세한 내용을 보려면 탭하세요."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB 포트를 사용해도 좋음"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"지정된 카테고리 없음"</string>
<string name="importance_from_user" msgid="7318955817386549931">"이러한 알림의 중요도를 설정했습니다."</string>
<string name="importance_from_person" msgid="9160133597262938296">"관련된 사용자가 있으므로 중요합니다."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="APP">%1$s</xliff:g>에서 <xliff:g id="ACCOUNT">%2$s</xliff:g> 계정으로 신규 사용자를 만들도록 허용하시겠습니까? 이 계정으로 등록된 사용자가 이미 존재합니다."</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"<xliff:g id="APP">%1$s</xliff:g>에서 <xliff:g id="ACCOUNT">%2$s</xliff:g> 계정으로 신규 사용자를 만들도록 허용하시겠습니까?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"언어 추가"</string>
<string name="country_selection_title" msgid="2954859441620215513">"지역 환경설정"</string>
<string name="search_language_hint" msgid="7042102592055108574">"언어 이름 입력"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 91825630f34e..2353ed09c9e3 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -283,7 +283,7 @@
<string name="permgrouprequest_contacts" msgid="6032805601881764300">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна байланыштарыңызды пайдаланууга уруксат берилсинби?"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Жайгашкан жер"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"түзмөктүн жайгашкан жерин аныктоого"</string>
- <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосу бул түзмөктүн кайда жүргөнүн көрүп турганга уруксат бересизби?"</string>
+ <string name="permgrouprequest_location" msgid="3788275734953323491">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосу бул түзмөктүн кайда жүргөнүн көрүп турсунбу?"</string>
<string name="permgrouprequestdetail_location" msgid="1347189607421252902">"Колдонмону колдонуп жаткан маалда гана, ал сиздин кайда жүргөнүңүздү билип турат."</string>
<string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосу бул түзмөктүн жүргөн жерин &lt;b&gt;ар дайым&lt;/b&gt; билип турсунбу?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"Колдонмону пайдаланып жаткан учурда гана ал жайгашкан жерди көрө алат"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Колдонмого SMS билдирүүлөрүн кабыл алууга жана аларды иштетип чыгууга уруксат берет. Бул, колдонмо сизге билгизбестен түзмөгүңүзгө жөнөтүлгөн билдирүүлөрдү мониторлой же жок кыла алат дегенди билдирет."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"билдирүүлөрдү (MMS) кабыл алуу"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Колдонмого MMS билдирүүлөрүн кабыл алууга жана аларды иштетип чыгууга уруксат берет. Бул, колдонмо сизге билгизбестен түзмөгүңүзгө жөнөтүлгөн билдирүүлөрдү мониторлой же жок кыла алат дегенди билдирет."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Уюк жөнөтүүлөрүнүн билдирүүлөрүн башка номерге багыттоо"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Уюк жөнөтүүлөрүнүн билдирүүлөрү келген сайын башка номерге багыттап туруу үчүн колдонмого уюк жөнөтүүлөрүнүн модулу менен байланышууга уруксат берет. Шашылыш уюк жөнөтүүлөрү кээ бир жерлердеги өзгөчө кырдаалдар тууралуу сизге эскертүү үчүн жөнөтүлөт. Зыянкеч колдонмолор шашылыш уюк жөнөтүүлөрү кабыл алынганда түзмөктүн майнаптуулугуна же иштешине жолтоо болушу мүмкүн."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"уюктук берүү билдирүүлөрүн окуу"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Колдонмого түзмөгүңүз кабыл алган уюк берүүнүн билдирүүлөрүн окууга жол берет. Шашылыш эскертүү билдирүүлөрү кээ бир жерлердеги өзгөчө кырдаалдар тууралу сизди эскертүү үчүн жөнөтүлөт. Зыяндуу колдономолор шашылыш эскертүүлөр берилип жатканда, сиздин түзмөктүн иштешине жолтоо болушу мүмкүн."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"жазылган түрмөктөрдү окуу"</string>
@@ -537,14 +539,14 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Манжа өтө жай жылды. Кайра аракет кылыңыз."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_authenticated" msgid="5309333983002526448">"Манжа изинин аныктыгы текшерилди"</string>
+ <string name="fingerprint_authenticated" msgid="5309333983002526448">"Манжа изи текшерилди"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Жүздүн аныктыгы текшерилди"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Жүздүн аныктыгы текшерилди, эми \"Ырастоону\" басыңыз"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Манжа изинин аппараттык камсыздоосу жеткиликтүү эмес."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Манжа изин сактоо мүмкүн эмес. Учурдагы манжа изин алып салыңыз."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Манжа изин күтүү мөөнөтү бүттү. Кайра аракет кылыңыз."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Манжа изи иш-аракети жокко чыгарылды."</string>
- <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Манжа изи менен аныктыгын текшерүүнү колдонуучу жокко чыгарды."</string>
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Манжа изи операциясын колдонуучу жокко чыгарды."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Аракеттер өтө көп болду. Кийинчерээк кайра аракет кылыңыз."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Өтө көп жолу аракет жасадыңыз. Манжа изинин сенсору өчүрүлдү."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Кайра бир аракеттениңиз."</string>
@@ -1075,7 +1077,7 @@
<string name="Midnight" msgid="5630806906897892201">"Түн ортосу"</string>
<string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<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="1846071997616654124">"<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="6876518925844129331">"Бардыгын тандоо"</string>
+ <string name="selectAll" msgid="6876518925844129331">"Баарын тандоо"</string>
<string name="cut" msgid="3092569408438626261">"Кесүү"</string>
<string name="copy" msgid="2681946229533511987">"Көчүрүү"</string>
<string name="failed_to_copy_to_clipboard" msgid="1833662432489814471">"Алмашуу буферине көчүрүлбөй калды"</string>
@@ -1315,7 +1317,7 @@
<string name="sms_control_title" msgid="7296612781128917719">"SMS билдирүүлөр жөнөтүлүүдө"</string>
<string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; көп SMS билдирүүлөрдү жөнөтүп жатат. Бул колдонмо билдирүүлөрдү жөнөтө берсинби?"</string>
<string name="sms_control_yes" msgid="3663725993855816807">"Ооба"</string>
- <string name="sms_control_no" msgid="625438561395534982">"Жок"</string>
+ <string name="sms_control_no" msgid="625438561395534982">"Тыюу салынат"</string>
<string name="sms_short_code_confirm_message" msgid="1645436466285310855">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt; номуруна билдирүү жөнөткөнү жатат."</string>
<string name="sms_short_code_details" msgid="5873295990846059400">"Бул уюлдук эсебиңиздеги төлөмдөргө "<b>"алып келиши мүмкүн"</b>"."</string>
<string name="sms_premium_short_code_details" msgid="7869234868023975"><b>"Бул уюлдук эсебиңиздеги төлөмдөргө алып келет."</b></string>
@@ -1357,12 +1359,14 @@
<string name="usb_power_notification_message" msgid="4647527153291917218">"Туташкан түзмөк кубатталууда. Дагы параметрлерди көрүү үчүн таптап коюңуз."</string>
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Аналогдук аудио жабдуу табылды"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Тиркелген түзмөк бул телефонго шайкеш келбейт. Көбүрөөк маалымат алуу үчүн таптап коюңуз."</string>
- <string name="adb_active_notification_title" msgid="6729044778949189918">"Мүчүлүштүктөрдү USB аркылуу оңдоо иштетилген"</string>
- <string name="adb_active_notification_message" msgid="7463062450474107752">"USB арклуу мүчүлштктрдү оңдоону өчрүү үчүн басып коюңуз"</string>
+ <string name="adb_active_notification_title" msgid="6729044778949189918">"Мүчүлүштүктөрдү USB аркылуу оңдоо иштеп жатат"</string>
+ <string name="adb_active_notification_message" msgid="7463062450474107752">"Өчүрүү үчүн тийип коюңуз"</string>
<!-- no translation found for adb_active_notification_message (8470296818270110396) -->
<skip />
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Сыноо программасынын режими иштетилди"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Сыноо программасынын режимин өчүрүү үчүн, баштапкы жөндөөлөргө кайтарыңыз."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Сериялык консоль иштетилди"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Майнаптуулугуна таасири тиет. Аны өчүрүү үчүн операциялык тутумду жүктөгүчтү текшериңиз."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB портунда суюктук же урандылар бар"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB порт автоматтык түрдө өчүрүлдү. Кененирээк маалымат алуу үчүн таптап коюңуз."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB портун колдонууга болот"</string>
@@ -1451,7 +1455,7 @@
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Бул өтүнүчкө уруксат бересизби?"</string>
<string name="grant_permissions_header_text" msgid="6874497408201826708">"Жетки талабы"</string>
<string name="allow" msgid="7225948811296386551">"Уруксат берүү"</string>
- <string name="deny" msgid="2081879885755434506">"Жок"</string>
+ <string name="deny" msgid="2081879885755434506">"Уруксат берилбейт"</string>
<string name="permission_request_notification_title" msgid="6486759795926237907">"Уруксат талап кылуу"</string>
<string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"Кийинки эсепке\nуруксат талап кылынууда: <xliff:g id="ACCOUNT">%s</xliff:g>."</string>
<string name="forward_intent_to_owner" msgid="1207197447013960896">"Бул колдонмо жумуш профилиңиздин сыртында колдонулуп жатат"</string>
@@ -1583,7 +1587,7 @@
<string name="fingerprints" msgid="4516019619850763049">"Манжа издери:"</string>
<string name="sha256_fingerprint" msgid="4391271286477279263">"SHA-256 манжа изи:"</string>
<string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 манжа изи:"</string>
- <string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Бардыгын көрүү"</string>
+ <string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Баарын көрүү"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Аракетти тандаңыз"</string>
<string name="share_action_provider_share_with" msgid="5247684435979149216">"Төмөнкү менен бөлүшүү"</string>
<string name="sending" msgid="3245653681008218030">"Жөнөтүлүүдө…"</string>
@@ -1892,10 +1896,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Категорияларга бөлүнгөн эмес"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Бул эскертмелердин маанилүүлүгүн белгиледиңиз."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Булар сиз үчүн маанилүү адамдар."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="APP">%1$s</xliff:g> колдонмосуна <xliff:g id="ACCOUNT">%2$s</xliff:g> аккаунту менен жаңы колдонуучу түзүүгө уруксат бересизби (мындай аккаунту бар колдонуучу мурунтан эле бар)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"<xliff:g id="APP">%1$s</xliff:g> колдонмосуна <xliff:g id="ACCOUNT">%2$s</xliff:g> аккаунту менен жаңы колдонуучу түзүүгө уруксат бересизби?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Тил кошуу"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Чөлкөмдүк жөндөөлөр"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Тилди киргизиңиз"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 0189c081b2b8..a83fae1f55d8 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"ອະນຸຍາດໃຫ້ແອັບຯຮັບ ແລະປະມວນຜົນຂໍ້ຄວາມ SMS. ນີ້ໝາຍຄວາມວ່າແອັບຯສາມາດຕິດຕາມ ຫຼືລຶບຂໍ້ຄວາມທີ່ສົ່ງເຂົ້າອຸປະກອນຂອງທ່ານ ໂດຍທີ່ບໍ່ສະແດງພວກມັນໃຫ້ທ່ານເຫັນ."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"ຮັບຂໍ້ຄວາມ (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"ອະນຸຍາດໃຫ້ແອັບຯ ຮັບແລະປະມວນຜົນຂໍ້ຄວາມ MMS. ນີ້ໝາຍຄວາມວ່າແອັບຯສາມາດຕິດຕາມ ຫຼືລຶບຂໍ້ຄວາມທີ່ສົ່ງເຂົ້າອຸປະກອນຂອງທ່ານ ໂດຍທີ່ບໍ່ສະແດງພວກມັນໃຫ້ທ່ານເຫັນ."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"ສົ່ງຕໍ່ຂໍ້ຄວາມການກະຈາຍສັນຍານໂທລະສັບ"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"ອະນຸຍາດໃຫ້ແອັບຜູກມັດກັບໂມດູນການກະຈາຍສັນຍານໂທລະສັບເພື່ອສົ່ງຕໍ່ຂໍ້ຄວາມການກະຈາຍສັນຍານໂທລະສັບເມື່ອໄດ້ຮັບມາ. ການເຕືອນການກະຈາຍສັນຍານໂທລະສັບແມ່ນຖືກຈັດສົ່ງໃນບາງສະຖານທີ່ເພື່ອເຕືອນທ່ານໃນກໍລະນີມີເຫດການສຸກເສີນເກີດຂຶ້ນ. ແອັບທີ່ເປັນອັນຕະລາຍອາດລົບກວນປະສິດທິພາບ ຫຼື ການເຮັດວຽກຂອງອຸປະກອນທ່ານເມື່ອໄດ້ຮັບການກະຈາຍສັນຍານໂທລະສັບສຸກເສີນ."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"ອ່ານຂໍ້ຄວາມກະຈາຍສັນຍານຂອງເສົາສັນຍານ"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"ອະນຸຍາດໃຫ້ແອັບຯ ສາມາດອ່ານຂໍ້ຄວາມແຈ້ງເຕືອນເຫດສຸກເສີນ ທີ່ໄດ້ຮັບໂດຍອຸປະກອນຂອງທ່ານ. ການແຈ້ງເຕືອນສຸກເສີນທີ່ມີໃຫ້ບໍລິການໃນບາງພື້ນທີ່ ເພື່ອແຈ້ງເຕືອນໃຫ້ທ່ານຮູ້ເຖິງສະຖານະການສຸກເສີນ. ແອັບພລິເຄຊັນທີ່ເປັນອັນຕະລາຍອາດລົບກວນປະສິດທິພາບ ຫຼືການດຳເນີນງານຂອງອຸປະກອນຂອງທ່ານ ເມື່ອໄດ້ການຮັບແຈ້ງເຕືອນສຸກເສີນຈາກສະຖານີມືຖື."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"ອ່ານຂໍ້ມູນຟີດທີ່ສະໝັກໄວ້"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"ເລືອກເພື່ອປິດການດີບັ໊ກຜ່ານ USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"ເປີດໃຊ້ໂໝດ Test Harness ແລ້ວ"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"ດຳເນີນການຣີເຊັດເປັນຄ່າຈາກໂຮງງານເພື່ອປິດການນຳໃຊ້ໂໝດ Test Harness."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"ເປີດນຳໃຊ້ຊີຣຽວຄອນໂຊແລ້ວ"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"ມີຜົນກະທົບກັບປະສິດທິພາບ. ເພື່ອປິດການນຳໃຊ້, ໃຫ້ກວດສອບ bootloader ເບິ່ງ."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"ມີຂອງແຫລວ ຫຼື ເສດດິນໃນຜອດ USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"ປິດຜອດ USB ໂດຍອັດຕະໂນມັດແລ້ວ. ແຕະເພື່ອສຶກສາເພີ່ມເຕີມ."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"ສາມາດໃຊ້ຜອດ USB ໄດ້"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"ບໍ່​ມີ​ໝວດ​ໝູ່"</string>
<string name="importance_from_user" msgid="7318955817386549931">"ທ່ານຕັ້ງຄວາມສຳຄັນຂອງການແຈ້ງເຕືອນເຫຼົ່ານີ້."</string>
<string name="importance_from_person" msgid="9160133597262938296">"ຂໍ້ຄວາມນີ້ສຳຄັນເນື່ອງຈາກບຸກຄົນທີ່ກ່ຽວຂ້ອງ."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"ອະນຸຍາດໃຫ້ <xliff:g id="APP">%1$s</xliff:g> ສ້າງຜູ້ໃຊ້ໃໝ່ກັບ <xliff:g id="ACCOUNT">%2$s</xliff:g> ໄດ້ບໍ່ (ມີຜູ້ໃຊ້ທີ່ໃຊ້ບັນຊີນີ້ຢູ່ກ່ອນແລ້ວ) ?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"ອະນຸຍາດໃຫ້ <xliff:g id="APP">%1$s</xliff:g> ສ້າງຜູ້ໃຊ້ໃໝ່ກັບ <xliff:g id="ACCOUNT">%2$s</xliff:g> ໄດ້ບໍ?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"ເພີ່ມພາສາ"</string>
<string name="country_selection_title" msgid="2954859441620215513">"ການຕັ້ງຄ່າພາກພື້ນ"</string>
<string name="search_language_hint" msgid="7042102592055108574">"ພິມຊື່ພາສາ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 0d29c84f6560..fdd5d2aac1ec 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -350,6 +350,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Leidžiama programai gauti ir apdoroti SMS pranešimus. Tai reiškia, kad programa gali stebėti ir ištrinti į jūsų įrenginį siunčiamus pranešimus jums jų neparodžiusi."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"gauti teksto pranešimus (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Leidžiama programai gauti ir apdoroti MMS pranešimus. Tai reiškia, kad programa gali stebėti ir ištrinti į jūsų įrenginį siunčiamus pranešimus jums jų neparodžiusi."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Persiųsti mobiliuoju transliuojamus pranešimus"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Programai leidžiama susaistyti transliacijos mobiliuoju modulį, kad būtų galima persiųsti mobiliuoju transliuojamus pranešimus, kai jie gaunami. Transliacijos mobiliuoju įspėjimai pristatomi kai kuriose vietovėse, kad būtų galima įspėti apie kritines situacijas. Kai gaunamas mobiliuoju transliuojamas pranešimas apie kritinę situaciją, kenkėjiškos programos gali trukdyti įrenginiui veikti ar jį naudoti."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"skaityti mobiliuoju transliuojamus pranešimus"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Programai leidžiama skaityti mobiliuoju transliuojamus pranešimus, gaunamus jūsų įrenginyje. Mobiliuoju transliuojami įspėjimai pristatomi kai kuriose vietose, kad įspėtų apie kritines situacijas. Kai gaunamas mobiliuoju transliuojamas pranešimas apie kritinę situaciją, kenkėjiškos programos gali trukdyti įrenginiui veikti ar jį naudoti."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"skaityti prenumeruojamus tiekimus"</string>
@@ -1405,6 +1407,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Pasirinkite, kas išjungtumėte USB derinimą."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Testavimo sistemos režimas įgalintas"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Atkurkite gamyklinius duomenis, kad išjungtumėte testavimo sistemos režimą."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Serijos pultas įgalintas"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Našumas paveiktas. Norėdami išjungti, patikrinkite paleidyklę."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB prievade yra skysčių ar smulkių dalelių"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB prievadas automatiškai išjungtas. Palieskite, kad sužinotumėte daugiau."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Saugu naudoti USB prievadą"</string>
@@ -1958,10 +1962,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Be kategorijos"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Galite nustatyti šių pranešimų svarbą."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Tai svarbu dėl susijusių žmonių."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Leisti „<xliff:g id="APP">%1$s</xliff:g>“ kurti naują <xliff:g id="ACCOUNT">%2$s</xliff:g> naudotoją (šią paskyrą naudojantis naudotojas jau yra)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Leisti „<xliff:g id="APP">%1$s</xliff:g>“ kurti naują <xliff:g id="ACCOUNT">%2$s</xliff:g> naudotoją?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Pridėkite kalbą"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Regiono nuostata"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Įveskite kalbos pav."</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 2547ba92f9c5..d1b7764362e0 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -347,6 +347,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Ļauj lietotnei saņemt un apstrādāt īsziņas. Tas nozīmē, ka lietotne var pārraudzīt vai dzēst uz jūsu ierīci nosūtītos ziņojumus, neparādot tos jums."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"saņemt ziņojumus (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Ļauj lietotnei saņemt un apstrādāt multiziņas. Tas nozīmē, ka lietotne var pārraudzīt vai dzēst uz jūsu ierīci nosūtītos ziņojumus, neparādot tos jums."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Pārsūtīt šūnu apraides ziņojumus"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Ļauj piesaistīt lietotni šūnu apraides modulim, lai pārsūtītu šūnu apraides ziņojumus, tiklīdz tie tiek saņemti. Šūnu apraides brīdinājumi tiek piegādāti noteiktās atrašanās vietās, lai brīdinātu jūs par ārkārtas situācijām. Ļaunprātīgas lietotnes var traucēt ierīces veiktspēju vai darbības, kad ir saņemts ārkārtas šūnas apraides ziņojums."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"šūnu apraides ziņojumu lasīšana"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Ļauj lietotnei lasīt ierīcē saņemtos šūnu apraides ziņojumus. Šūnu apraides brīdinājumi tiek piegādāti dažās atrašanās vietās, lai brīdinātu jūs par ārkārtas situācijām. Ļaunprātīgas lietotnes var traucēt ierīces veiktspēju vai darbības, kad ir saņemts ārkārtas šūnas apraides ziņojums."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"lasīt abonētās plūsmas"</string>
@@ -1383,6 +1385,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Atlasiet, lai atspējotu USB atkļūdošanu."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Drošības pārbaudes režīms ir iespējots"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Lai atspējotu drošības pārbaudes režīmu, veiciet rūpnīcas datu atiestatīšanu."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Seriālā konsole ir iespējota"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Tiek ietekmēta veiktspēja. Lai atspējotu, pārbaudiet operētājsistēmu ielādes rīku."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB pieslēgvietā ir šķidrums vai daļiņas"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB pieslēgvieta ir automātiski atspējota. Pieskarieties, lai uzzinātu vairāk."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB pieslēgvietu drīkst izmantot"</string>
@@ -1924,10 +1928,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Nav kategorijas"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Jūs iestatījāt šo paziņojumu svarīguma līmeni."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Tas ir svarīgi iesaistīto personu dēļ."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Vai atļaut lietotnei <xliff:g id="APP">%1$s</xliff:g> izveidot jaunu lietotāju, izmantojot e-pasta adresi <xliff:g id="ACCOUNT">%2$s</xliff:g> (lietotājs ar šādu kontu jau pastāv)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Vai atļaut lietotnei <xliff:g id="APP">%1$s</xliff:g> izveidot jaunu lietotāju, izmantojot e-pasta adresi <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Pievienot valodu"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Reģiona preference"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Ierakstiet valodas nosaukumu"</string>
diff --git a/core/res/res/values-mcc310-mnc030-da/strings.xml b/core/res/res/values-mcc310-mnc030-da/strings.xml
index c45e3be4bd7a..3dbda050d8c0 100644
--- a/core/res/res/values-mcc310-mnc030-da/strings.xml
+++ b/core/res/res/values-mcc310-mnc030-da/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="6575159319460304530">"SIM-kortet er ikke aktiveret MM#2"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="6575159319460304530">"SIM-kortet er ikke provisioneret MM#2"</string>
<string name="mmcc_illegal_ms" msgid="1930079814544869756">"SIM-kort er ikke tilladt MM#3"</string>
<string name="mmcc_illegal_me" msgid="2995576894416087107">"Telefonen har ikke adgangstilladelse MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc170-da/strings.xml b/core/res/res/values-mcc310-mnc170-da/strings.xml
index 8580fb355bd1..6a4d3e833cf4 100644
--- a/core/res/res/values-mcc310-mnc170-da/strings.xml
+++ b/core/res/res/values-mcc310-mnc170-da/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="210168420192421012">"SIM-kortet er ikke aktiveret MM#2"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="210168420192421012">"SIM-kortet er ikke provisioneret MM#2"</string>
<string name="mmcc_illegal_ms" msgid="1130721094178658338">"SIM-kort er ikke tilladt MM#3"</string>
<string name="mmcc_illegal_me" msgid="3173546391131606065">"Telefonen har ikke adgangstilladelse MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc280-da/strings.xml b/core/res/res/values-mcc310-mnc280-da/strings.xml
index 6a8e40b2f462..919ac32eef00 100644
--- a/core/res/res/values-mcc310-mnc280-da/strings.xml
+++ b/core/res/res/values-mcc310-mnc280-da/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="6638755728961013003">"SIM-kortet er ikke aktiveret MM#2"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="6638755728961013003">"SIM-kortet er ikke provisioneret MM#2"</string>
<string name="mmcc_illegal_ms" msgid="5562215652599183258">"SIM-kort er ikke tilladt MM#3"</string>
<string name="mmcc_illegal_me" msgid="822496463303720579">"Telefonen har ikke adgangstilladelse MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc380-da/strings.xml b/core/res/res/values-mcc310-mnc380-da/strings.xml
index cd4c796edff3..8b8ac1a02e0d 100644
--- a/core/res/res/values-mcc310-mnc380-da/strings.xml
+++ b/core/res/res/values-mcc310-mnc380-da/strings.xml
@@ -20,6 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="6933439408719203102">"SIM-kortet er ikke aktiveret MM#2"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="6933439408719203102">"SIM-kortet er ikke provisioneret MM#2"</string>
<string name="mmcc_illegal_ms" msgid="6367773216941648568">"SIM-kort er ikke tilladt MM#3"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc410-da/strings.xml b/core/res/res/values-mcc310-mnc410-da/strings.xml
index 3c5b9eedb6d9..3c9ab9c87ee2 100644
--- a/core/res/res/values-mcc310-mnc410-da/strings.xml
+++ b/core/res/res/values-mcc310-mnc410-da/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="376893116792604964">"SIM-kortet er ikke aktiveret MM#2"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="376893116792604964">"SIM-kortet er ikke provisioneret MM#2"</string>
<string name="mmcc_illegal_ms" msgid="1593063035884873292">"SIM-kort er ikke tilladt MM#3"</string>
<string name="mmcc_illegal_me" msgid="4477688981805467729">"Telefonen har ikke adgangstilladelse MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc560-da/strings.xml b/core/res/res/values-mcc310-mnc560-da/strings.xml
index 95c214ba657d..aac050c7eda8 100644
--- a/core/res/res/values-mcc310-mnc560-da/strings.xml
+++ b/core/res/res/values-mcc310-mnc560-da/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="2976453378311251765">"SIM-kortet er ikke aktiveret MM#2"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="2976453378311251765">"SIM-kortet er ikke provisioneret MM#2"</string>
<string name="mmcc_illegal_ms" msgid="2519618694918727742">"SIM-kort er ikke tilladt MM#3"</string>
<string name="mmcc_illegal_me" msgid="7030488670186895244">"Telefonen har ikke adgangstilladelse MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc950-da/strings.xml b/core/res/res/values-mcc310-mnc950-da/strings.xml
index 97c801b3494c..e8ab75ce6272 100644
--- a/core/res/res/values-mcc310-mnc950-da/strings.xml
+++ b/core/res/res/values-mcc310-mnc950-da/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="94675382531896663">"SIM-kortet er ikke aktiveret MM#2"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="94675382531896663">"SIM-kortet er ikke provisioneret MM#2"</string>
<string name="mmcc_illegal_ms" msgid="2418195136279399212">"SIM-kort er ikke tilladt MM#3"</string>
<string name="mmcc_illegal_me" msgid="8920048244573695129">"Telefonen har ikke adgangstilladelse MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc311-mnc180-da/strings.xml b/core/res/res/values-mcc311-mnc180-da/strings.xml
index 5bcc43db1ad2..624a1c037462 100644
--- a/core/res/res/values-mcc311-mnc180-da/strings.xml
+++ b/core/res/res/values-mcc311-mnc180-da/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="531930017979728896">"SIM-kortet er ikke aktiveret MM#2"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="531930017979728896">"SIM-kortet er ikke provisioneret MM#2"</string>
<string name="mmcc_illegal_ms" msgid="97745044956236881">"SIM-kort er ikke tilladt MM#3"</string>
<string name="mmcc_illegal_me" msgid="5766888847785331904">"Telefonen har ikke adgangstilladelse MM#6"</string>
</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 9949eaa7ce7b..d30c8f2c70a8 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -283,7 +283,7 @@
<string name="permgrouprequest_contacts" msgid="6032805601881764300">"Дали да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до контактите?"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Локација"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"пристапува до локацијата на овој уред"</string>
- <string name="permgrouprequest_location" msgid="3788275734953323491">"Дали да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до локацијата на уредот?"</string>
+ <string name="permgrouprequest_location" msgid="3788275734953323491">"Дали да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до локацијата на уредов?"</string>
<string name="permgrouprequestdetail_location" msgid="1347189607421252902">"Апликацијата ќе има пристап до локацијата само додека ја користите"</string>
<string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до локацијата на уредов &lt;b&gt;во секое време&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"Апликацијата во моментов може да пристапува до локацијата само додека ја користите"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Овозможува апликацијата да прима и да обработува SMS пораки. Тоа значи дека апликацијата може да следи или да брише пораки испратени до вашиот уред без да ви ги прикаже вам."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"прими текстуални пораки (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Овозможува апликацијата да прима и да обработува MMS пораки. Тоа значи дека апликацијата може да следи или да брише пораки испратени до вашиот уред без да ви ги прикаже вам."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Проследување пораки за мобилен пренос"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Дозволува апликацијата да се врзе со модулот за мобилен пренос за да проследува пораки за мобилен пренос штом ќе се примат. Предупредувањата за мобилно емитување се доставуваат на некои локации за да ве предупредат на итни ситуации. Злонамерните апликации може да пречат во ефикасноста или работењето на вашиот уред кога се прима емитување за итен случај."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"прочитај пораки за мобилно емитување"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Овозможува апликациите да ги читаат пораките за мобилно емитување што ги прима вашиот уред. Предупредувањата за мобилно емитување се доставуваат на некои локации, за да ве предупредат на итни ситуации. Злонамерните апликации може да пречат во ефикасноста или работењето на вашиот уред кога се прима емитување за итен случај."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"читај претплатени навестувања на содржина"</string>
@@ -540,10 +542,10 @@
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Отпечатокот е проверен"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Лицето е проверено"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Лицето е проверено, притиснете го копчето „Потврди“"</string>
- <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Хардвер за отпечаток од прст не е достапен."</string>
- <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Отпечатокот не може да се складира. Отстранете го постоечкиот отпечаток."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Хардверот за отпечатоци не е достапен."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Отпечатокот не може да се складира. Отстранете некој од постојните отпечатоци."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Се достигна времето на истекување на отпечатокот. Обидете се повторно."</string>
- <string name="fingerprint_error_canceled" msgid="4402024612660774395">"Операцијата со отпечаток од прст се откажа."</string>
+ <string name="fingerprint_error_canceled" msgid="4402024612660774395">"Операцијата со отпечаток се откажа."</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Корисникот ја откажа потврдата со отпечаток."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Премногу обиди. Обидете се повторно подоцна."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Премногу обиди. Сензорот за отпечатоци е оневозможен."</string>
@@ -1362,6 +1364,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Изберете за да се оневозможи отстранување грешки на USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Овозможен е режимот на рамка за тестирање"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Извршете фабричко ресетирање за да го оневозможите режимот на рамка за тестирање."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Сериската конзола е овозможена"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Перформансите се засегнати. За да оневозможите, проверете го подигнувачот."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Течност или нечистотија во USB-портата"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB-портата е автоматски оневозможена. Допрете за да дознаете повеќе."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Во ред е да се користи USB-порта"</string>
@@ -1893,10 +1897,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Некатегоризирано"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Ја поставивте важноста на известувањава."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Ова е важно заради луѓето кои се вклучени."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Дозволувате <xliff:g id="APP">%1$s</xliff:g> да создаде нов корисник со <xliff:g id="ACCOUNT">%2$s</xliff:g>? (Веќе постои корисник со оваа сметка.)"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Дозволувате <xliff:g id="APP">%1$s</xliff:g> да создаде нов корисник со <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Додај јазик"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Претпочитувања за регион"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Внеси име на јазик"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index f71115806013..5a604fbe92ab 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -325,7 +325,7 @@
<string name="capability_title_canPerformGestures" msgid="7418984730362576862">"ജെസ്‌റ്ററുകൾ നിർവഹിക്കുക"</string>
<string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"ടാപ്പുചെയ്യാനോ സ്വൈപ്പുചെയ്യാനോ പിഞ്ചുചെയ്യാനോ മറ്റ് ജെസ്‌റ്ററുകൾ നിർവഹിക്കാനോ കഴിയും."</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"ഫിംഗർപ്രിന്റ് ജെസ്‌റ്ററുകൾ"</string>
- <string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"ഉപകരണത്തിന്റെ വിരലടയാള സെൻസറിൽ ചെയ്‌ത ജെസ്‌റ്ററുകൾ ക്യാപ്‌ചർ ചെയ്യാനാകും."</string>
+ <string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"ഉപകരണത്തിന്റെ ഫിംഗർപ്രിന്റ് സെൻസറിൽ ചെയ്‌ത ജെസ്‌റ്ററുകൾ ക്യാപ്‌ചർ ചെയ്യാനാകും."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"സ്റ്റാറ്റസ് ബാർ പ്രവർത്തനരഹിതമാക്കുക അല്ലെങ്കിൽ പരിഷ്‌ക്കരിക്കുക"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"നില ബാർ പ്രവർത്തരഹിതമാക്കുന്നതിന് അല്ലെങ്കിൽ സിസ്‌റ്റം ഐക്കണുകൾ ചേർക്കുന്നതിനും നീക്കംചെയ്യുന്നതിനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"സ്റ്റാറ്റസ് ബാർ ആയിരിക്കുക"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"SMS സന്ദേശങ്ങൾ നേടാനും പ്രോസസ്സുചെയ്യാനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. നിങ്ങളുടെ ഉപകരണത്തിലേക്ക് അയയ്‌ക്കുന്ന സന്ദേശങ്ങൾ നിങ്ങൾക്ക് ദൃശ്യമാക്കാതെ തന്നെ അപ്ലിക്കേഷന് നിരീക്ഷിക്കാനോ ഇല്ലാതാക്കാനോ കഴിയുമെന്നാണ് ഇതിനർത്ഥം."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"വാചക സന്ദേശം നേടുക (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"MMS സന്ദേശങ്ങൾ നേടാനും പ്രോസസ്സുചെയ്യാനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. നിങ്ങളുടെ ഉപകരണത്തിലേക്ക് അയയ്‌ക്കുന്ന സന്ദേശങ്ങൾ നിങ്ങൾക്ക് ദൃശ്യമാക്കാതെ തന്നെ അപ്ലിക്കേഷന് നിരീക്ഷിക്കാനോ ഇല്ലാതാക്കാനോ കഴിയുമെന്നാണ് ഇതിനർത്ഥം."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"ബ്രോഡ്‌കാസ്‌റ്റ് സന്ദേശങ്ങൾ കൈമാറുക"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"സ്വീകരിക്കുന്ന മുറയ്ക്ക് ബ്രോഡ്‌കാസ്‌റ്റ് സന്ദേശങ്ങൾ കൈമാറുന്നതിന് സെൽ ബ്രോഡ്‌കാസ്‌റ്റ് മോഡ്യൂളിലേക്ക് ബൈൻഡ് ചെയ്യാൻ ആപ്പിനെ അനുവദിക്കുന്നു. അടിയന്തര സാഹചര്യങ്ങളെ കുറിച്ച് നിങ്ങൾക്ക് മുന്നറിയിപ്പ് നൽകുന്നതിന് ചില ലൊക്കേഷനുകളിൽ സെൽ ബ്രോഡ്‌കാസ്‌റ്റ് അലേർട്ടുകൾ ഡെലിവറി ചെയ്യപ്പെടുന്നു. ഒരു അടിയന്തര സെൽ ബ്രോഡ്‌കാസ്റ്റ് ലഭിക്കുമ്പോൾ ക്ഷുദ്രകരമായ അപ്ലിക്കേഷനുകൾ നിങ്ങളുടെ ഉപകരണത്തിന്റെ പ്രകടനത്തെയോ പ്രവർത്തനത്തെയോ തടസപ്പെടുത്താനിടയുണ്ട്."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"സെൽ പ്രക്ഷേപണ സന്ദേശങ്ങൾ റീഡുചെയ്യുക"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"നിങ്ങളുടെ ഉപകരണത്തിൽ ലഭിച്ച സെൽ പ്രക്ഷേപണ സന്ദേശങ്ങൾ റീഡുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. അടിയന്തര സാഹചര്യങ്ങളെക്കുറിച്ച് നിങ്ങൾക്ക് മുന്നറിയിപ്പ് നൽകാനായി ചില ലൊക്കേഷനുകളിൽ നൽകപ്പെടുന്നവയാണ് സെൽ പ്രക്ഷേപണ അലേർട്ടുകൾ. ഒരു അടിയന്തര സെൽ പ്രക്ഷേപണം ലഭിക്കുമ്പോൾ, ക്ഷുദ്രകരമായ അപ്ലിക്കേഷനുകൾ നിങ്ങളുടെ ഉപകരണത്തിന്റെ പ്രകടനമോ പ്രവർത്തനമോ തടസ്സപ്പെടുത്താനിടയുണ്ട്."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"സബ്‌സ്ക്രൈബ് ചെയ്‌ത ഫീഡുകൾ വായിക്കുക"</string>
@@ -513,9 +515,9 @@
<string name="permlab_useBiometric" msgid="8837753668509919318">"ബയോമെട്രിക് ഹാർ‌ഡ്‌വെയർ ഉപയോഗിക്കുക"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"പരിശോധിച്ചുറപ്പിക്കുന്നതിനായി, ബയോമെട്രിക് ഹാർഡ്‌വെയർ ഉപയോഗിക്കാൻ ആപ്പിനെ അനുവദിക്കുക"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"ഫിംഗർപ്രിന്റ് ഹാർഡ്‌വെയർ നിയന്ത്രിക്കുക"</string>
- <string name="permdesc_manageFingerprint" msgid="178208705828055464">"ഉപയോഗിക്കാനായി വിരലടയാള ടെംപ്ലേറ്റുകൾ ചേർക്കാനും ഇല്ലാതാക്കാനുമുള്ള രീതികൾ അഭ്യർത്ഥിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
+ <string name="permdesc_manageFingerprint" msgid="178208705828055464">"ഉപയോഗിക്കാനായി ഫിംഗർപ്രിന്റ് ടെംപ്ലേറ്റുകൾ ചേർക്കാനും ഇല്ലാതാക്കാനുമുള്ള രീതികൾ അഭ്യർത്ഥിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"ഫിംഗർപ്രിന്റ് ഹാർഡ്‌വെയർ ഉപയോഗിക്കുക"</string>
- <string name="permdesc_useFingerprint" msgid="9165097460730684114">"പ്രാമാണീകരണത്തിനായി വിരലടയാളം ഉപയോഗിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു"</string>
+ <string name="permdesc_useFingerprint" msgid="9165097460730684114">"പ്രാമാണീകരണത്തിനായി ഫിംഗർപ്രിന്റ് ഉപയോഗിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു"</string>
<string name="permlab_audioWrite" msgid="2661772059799779292">"നിങ്ങളുടെ സംഗീത ശേഖരം പരിഷ്‌കരിക്കുക"</string>
<string name="permdesc_audioWrite" msgid="8888544708166230494">"നിങ്ങളുടെ സംഗീത ശേഖരം പരിഷ്‌ക്കരിക്കുന്നതിന് ആപ്പിനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_videoWrite" msgid="128769316366746446">"നിങ്ങളുടെ വീഡിയോ ശേഖരം പരിഷ്‌കരിക്കുക"</string>
@@ -530,9 +532,9 @@
<string name="biometric_not_recognized" msgid="5770511773560736082">"തിരിച്ചറിഞ്ഞില്ല"</string>
<string name="biometric_error_canceled" msgid="349665227864885880">"പരിശോധിച്ചുറപ്പിക്കൽ റദ്ദാക്കി"</string>
<string name="biometric_error_device_not_secured" msgid="6583143098363528349">"പിന്നോ പാറ്റേണോ പാസ്‌വേഡോ സജ്ജീകരിച്ചിട്ടില്ല"</string>
- <string name="fingerprint_acquired_partial" msgid="735082772341716043">"വിരലടയാളം ഭാഗികമായി തിരിച്ചറിഞ്ഞു. വീണ്ടും ശ്രമിക്കുക."</string>
- <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"വിരലടയാളം പ്രോസസ് ചെയ്യാനായില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
- <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"വിരലടയാള സെൻസറിൽ ചെളിയുണ്ട്. അത് വൃത്തിയാക്കി വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ഫിംഗർപ്രിന്റ് ഭാഗികമായി തിരിച്ചറിഞ്ഞു. വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ഫിംഗർപ്രിന്റ് പ്രോസസ് ചെയ്യാനായില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ഫിംഗർപ്രിന്റ് സെൻസറിൽ ചെളിയുണ്ട്. അത് വൃത്തിയാക്കി വീണ്ടും ശ്രമിക്കുക."</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"വിരൽ വളരെ വേഗത്തിൽ നീക്കി. വീണ്ടും ശ്രമിക്കുക."</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"വിരൽ വളരെ പതുക്കെ നീക്കി. വീണ്ടും ശ്രമിക്കുക."</string>
<string-array name="fingerprint_acquired_vendor">
@@ -541,19 +543,19 @@
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"മുഖം പരിശോധിച്ചുറപ്പിച്ചു"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"മുഖം പരിശോധിച്ചുറപ്പിച്ചു, സ്ഥിരീകരിക്കുക അമർത്തുക"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ഫിംഗർപ്രിന്റ് ഹാർഡ്‌വെയർ ലഭ്യമല്ല."</string>
- <string name="fingerprint_error_no_space" msgid="1055819001126053318">"വിരലടയാളം സംഭരിക്കാനാവില്ല. നിലവിലുള്ള വിരലടയാളം നീക്കംചെയ്യുക."</string>
- <string name="fingerprint_error_timeout" msgid="3927186043737732875">"വിരലടയാളം നൽകേണ്ട സമയം കഴിഞ്ഞു. വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"ഫിംഗർപ്രിന്റ് സംഭരിക്കാനാവില്ല. നിലവിലുള്ള ഫിംഗർപ്രിന്റ് നീക്കംചെയ്യുക."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"ഫിംഗർപ്രിന്റ് നൽകേണ്ട സമയം കഴിഞ്ഞു. വീണ്ടും ശ്രമിക്കുക."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"ഫിംഗർപ്രിന്റ് പ്രവർത്തനം റദ്ദാക്കി."</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"ഉപയോക്താവ് റദ്ദാക്കിയ ഫിംഗർപ്രിന്‍റ് പ്രവർത്തനം."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"നിരവധി തവണ ശ്രമിച്ചു. പിന്നീട് വീണ്ടും ശ്രമിക്കുക."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"നിരവധി തവണ ശ്രമിച്ചതിനാൽ, വിരലടയാള സെൻസർ പ്രവർത്തനരഹിതമായി."</string>
+ <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"നിരവധി തവണ ശ്രമിച്ചതിനാൽ, ഫിംഗർപ്രിന്റ് സെൻസർ പ്രവർത്തനരഹിതമായി."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"വീണ്ടും ശ്രമിക്കൂ."</string>
<string name="fingerprint_error_no_fingerprints" msgid="7654382120628334248">"വിരലടയാളങ്ങൾ എൻറോൾ ചെയ്തിട്ടില്ല."</string>
- <string name="fingerprint_error_hw_not_present" msgid="409523969613176352">"ഈ ഉപകരണത്തിൽ വിരലടയാള സെൻസറില്ല."</string>
+ <string name="fingerprint_error_hw_not_present" msgid="409523969613176352">"ഈ ഉപകരണത്തിൽ ഫിംഗർപ്രിന്റ് സെൻസറില്ല."</string>
<string name="fingerprint_name_template" msgid="5870957565512716938">"കൈവിരൽ <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
- <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"വിരലടയാള ഐക്കൺ"</string>
+ <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ഫിംഗർപ്രിന്റ് ഐക്കൺ"</string>
<string name="permlab_manageFace" msgid="7262837876352591553">"മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് ഹാർഡ്‌വെയർ മാനേജ് ചെയ്യുക"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"ഉപയോഗിക്കാനായി, മുഖത്തിന്റെ ടെംപ്ലേറ്റുകൾ ചേർക്കാനും ഇല്ലാതാക്കാനുമുള്ള രീതികൾ അഭ്യർത്ഥിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് ഹാർഡ്‌വെയർ ഉപയോഗിക്കുക"</string>
@@ -1362,6 +1364,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB ഡീബഗ്ഗുചെയ്യൽ പ്രവർത്തനരഹിതമാക്കാൻ തിരഞ്ഞെടുക്കുക."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"പരിശോധനാ സംവിധാനങ്ങൾ മോഡ് പ്രവർത്തനക്ഷമമാക്കി"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"പരിശോധനാ സംവിധാന മോഡ് പ്രവർത്തനരഹിതമാക്കാൻ ഫാക്‌ടറി പുനഃക്രമീകരണം നിർവഹിക്കുക."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"സീരിയൽ കൺസോൾ പ്രവർത്തനക്ഷമമാക്കി"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"പ്രകടനത്തെ ബാധിച്ചു. പ്രവർത്തനരഹിതമാക്കാൻ, ബൂട്ട് ലോഡർ പരിശോധിക്കുക."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB പോർട്ടിൽ ദ്രാവകമോ പൊടിയോ കണ്ടെത്തി"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB പോർട്ടർ സ്വയമേവ പ്രവർത്തനരഹിതമായി. കൂടുതലറിയാൻ ടാപ്പ് ചെയ്യുക."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"ഇനി USB പോർട്ട് ഉപയോഗിക്കാം"</string>
@@ -1581,7 +1585,7 @@
<string name="serial_number" msgid="758814067660862493">"സീരിയൽ നമ്പർ:"</string>
<string name="fingerprints" msgid="4516019619850763049">"വിരലടയാളങ്ങൾ:"</string>
<string name="sha256_fingerprint" msgid="4391271286477279263">"SHA-256 വിരലടയാളം:"</string>
- <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA വിരലടയാളം:"</string>
+ <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA ഫിംഗർപ്രിന്റ്:"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"എല്ലാം കാണുക"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"പ്രവർത്തനം തിരഞ്ഞെടുക്കുക"</string>
<string name="share_action_provider_share_with" msgid="5247684435979149216">"ഇവരുമായി പങ്കിടുക"</string>
@@ -1891,10 +1895,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"വർഗ്ഗീകരിച്ചിട്ടില്ലാത്ത"</string>
<string name="importance_from_user" msgid="7318955817386549931">"ഈ അറിയിപ്പുകളുടെ പ്രാധാന്യം നിങ്ങൾ സജ്ജീകരിച്ചു."</string>
<string name="importance_from_person" msgid="9160133597262938296">"ഉൾപ്പെട്ടിട്ടുള്ള ആളുകളെ കണക്കിലെടുക്കുമ്പോള്‍ ഇത് പ്രധാനപ്പെട്ടതാണ്‌."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="ACCOUNT">%2$s</xliff:g> എന്ന അക്കൗണ്ട് (ഈ അക്കൗണ്ട് ഉപയോഗിക്കുന്ന ഒരു ഉപയോക്താവ് നിലവിലുണ്ട്) ഉപയോഗിച്ച് പുതിയ ഉപയോക്താവിനെ സൃഷ്‌ടിക്കാൻ <xliff:g id="APP">%1$s</xliff:g> എന്നതിനെ അനുവദിക്കണോ?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"<xliff:g id="ACCOUNT">%2$s</xliff:g> എന്ന അക്കൗണ്ട് ഉപയോഗിച്ച് പുതിയ ഉപയോക്താവിനെ സൃഷ്‌ടിക്കാൻ <xliff:g id="APP">%1$s</xliff:g> എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"ഒരു ഭാഷ ചേർക്കുക"</string>
<string name="country_selection_title" msgid="2954859441620215513">"മേഖലാ മുൻഗണന"</string>
<string name="search_language_hint" msgid="7042102592055108574">"ഭാഷയുടെ പേര് ടൈപ്പുചെയ്യുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 75700287a2fa..f4c6b60ce942 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Апп нь SMS мессежийг хүлээн авах болон гүйцэтгэх боломжтой. Ингэснээр апп нь таны төхөөрөмжрүү илгээсэн мессежийг танд үзүүлэхгүйгээр хянах болон устгаж чадна."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"текст мессеж(МMS) хүлээж авах"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Апп нь MMS мессежийг хүлээн авах болон гүйцэтгэх боломжтой. Ингэснээр апп нь таны төхөөрөмжрүү илгээсэн мессежийг танд үзүүлэхгүйгээр хянах болон устгаж чадна."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Гар утсанд масс мессеж түгээх онцлогийн мессежийг шилжүүлэх"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Гар утсанд масс мессеж түгээх онцлогийн мессежийг хүлээн авах үед түүнийг шилжүүлэх зорилгоор аппад гар утсанд масс мессеж түгээх модультай холбогдохыг зөвшөөрөх Гар утсанд масс мессеж түгээх онцлогийн сэрэмжлүүлэг нь онцгой нөхцөл байдлын тухай танд анхааруулахын тулд зарим байршилд хүрдэг. Гар утсанд масс мессеж түгээх онцлогийн илгээх онцгой нөхцөл байдлын тухай мессежийг хүлээн авах үед хортой апп таны төхөөрөмжийн гүйцэтгэл эсвэл ажиллагаанд саад учруулж болзошгүй."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"үүрэн өргөн дамжууллын мессеж унших"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Апп нь таны төхөөрөмжийн хүлээн авсан үүрэн өргөн дамжуулах мессежийг унших боломжтой. Үүрэн өргөн дамжууллын мэдэгдэл нь яаралтай нөхцөл байдлыг анхааруулах зорилгоор зарим байршлуудад хүрдэг. Хортой апп нь яаралтай үүрэн өргөн дамжууллыг хүлээн авсан үед таны төхөөрөмжийн ажиллагаа болон чадамжид нөлөөлөх боломжтой."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"бүртгүүлсэн хангамжийг унших"</string>
@@ -512,10 +514,10 @@
<string name="permdesc_requestPasswordComplexity" msgid="4730994229754212347">"Аппад дэлгэцийн түгжээний боломжтой уртын хэмжээ болон төрлийг заадаг дэлгэцийн түгжээний төвөгтэй байдлын түвшнийг (өндөр, дундаж, бага эсвэл байхгүй) мэдэж авахыг зөвшөөрдөг. Түүнчлэн, апп хэрэглэгчдэд дэлгэцийн түгжээг тодорхой түвшинд шинэчлэхийг санал болгох боломжтой хэдий ч хэрэглэгч үүнийг чөлөөтэй үл хэрэгсэж, орхих боломжтой. Дэлгэцийн түгжээг ил бичвэрээр хадгалдаггүй тул энэ апп тодорхой нууц үгийг мэддэггүй болохыг анхаарна уу."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"биометрийн техник хангамжийг ашиглах"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Aппад биометрийн техник хангамжийг баталгаажуулалтад ашиглахыг зөвшөөрдөг"</string>
- <string name="permlab_manageFingerprint" msgid="5640858826254575638">"хурууны хээний програм хангамжийг удирдах"</string>
- <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Хурууны хээний загварыг нэмэх эсвэл усгтах үйлдлийг хийх зөвшөөрлийг програмд олгодог."</string>
- <string name="permlab_useFingerprint" msgid="3150478619915124905">"хурууны хээний програм хангамжийг ашиглах"</string>
- <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Баталгаажуулалт хийх зорилгоор хурууны хээний апп хамгамжийг ашиглах зөвшөөрлийг аппд олгодог"</string>
+ <string name="permlab_manageFingerprint" msgid="5640858826254575638">"хурууны хээний төхөөрөмжийг удирдах"</string>
+ <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Хурууны хээний загвар нэмэх эсвэл устгах үйлдэл хийх зөвшөөрлийг аппд олгодог."</string>
+ <string name="permlab_useFingerprint" msgid="3150478619915124905">"хурууны хээний төхөөрөмж ашиглах"</string>
+ <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Баталгаажуулалт хийх зорилгоор хурууны хээний төхөөрөмжийг ашиглах зөвшөөрлийг аппд олгодог"</string>
<string name="permlab_audioWrite" msgid="2661772059799779292">"хөгжмийн цуглуулгаа тохируулах"</string>
<string name="permdesc_audioWrite" msgid="8888544708166230494">"Таны хөгжмийн цуглуулгыг тохируулах зөвшөөрлийг аппад олгодог."</string>
<string name="permlab_videoWrite" msgid="128769316366746446">"видео цуглуулгаа тохируулах"</string>
@@ -532,7 +534,7 @@
<string name="biometric_error_device_not_secured" msgid="6583143098363528349">"Тохируулсан пин, хээ эсвэл нууц үг алга"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Хурууны хээг дутуу уншуулсан байна. Дахин оролдоно уу."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Хурууны хээ боловсруулж чадахгүй байна. Дахин оролдоно уу."</string>
- <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Хурууны хээ мэдрэгч бохирдсон байна. Та цэвэрлэсний дараагаар дахин оролдоно уу."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Хурууны хээ мэдрэгч бохирдсон байна. Цэвэрлэсний дараа дахин оролдоно уу."</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Хуруугаа хэт хурдан хөдөлгөсөн байна. Дахин оролдоно уу."</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Хуруу хэт удаан хөдөлгөсөн байна. Дахин оролдоно уу."</string>
<string-array name="fingerprint_acquired_vendor">
@@ -540,11 +542,11 @@
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Хурууны хээг нотолсон"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Царайг баталгаажууллаа"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Царайг баталгаажууллаа. Баталгаажуулах товчлуурыг дарна уу"</string>
- <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Хурууны хээний тоног төхөөрөмж бэлэн бус байна."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Хурууны хээний төхөөрөмж бэлэн бус байна."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Хурууны хээг хадгалах боломжгүй байна. Одоо байгаа хурууны хээг арилгана уу."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Хурууны хээ оруулах хугацаа өнгөрсөн байна. Дахин оруулна уу."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Хурууны хээний бүртгэл амжилтгүй боллоо."</string>
- <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Хэрэглэгч хурууны хээн баталгаажуулалтыг цуцалсан байна."</string>
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Хэрэглэгч хурууны хээний баталгаажуулалтыг цуцалсан байна."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Хэтэрхий олон оролдлоо. Түр хүлээгээд дахин оролдоно уу."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Хэт олон удаа оролдсон тул хурууны хээ мэдрэгчийг идэвхгүй болголоо."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Дахин оролдно уу."</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB дебаг хийхийг идэвхгүй болгох бол сонгоно уу."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Туршилтын цогц горимыг идэвхжүүлсэн"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Туршилтын цогц горимыг идэвхгүй болгохын тулд үйлдвэрийн төлөвт шинэчилнэ үү."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Цуваа консолыг идэвхжүүлсэн"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Гүйцэтгэлд нөлөөлнө. Идэвхгүй болгохын тулд эхэлж ачаалагчийг шалгана уу."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB порт дээрх шингэн зүйл эсвэл бохирдол"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB портыг автоматаар идэвхгүй болгосон байна. Дэлгэрэнгүй мэдээлэл авахын тулд товшино уу."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB портыг ашиглахад зүгээр"</string>
@@ -1510,7 +1514,7 @@
<string name="sync_do_nothing" msgid="3743764740430821845">"Одоо юу ч хийхгүй"</string>
<string name="choose_account_label" msgid="5655203089746423927">"Бүртгэл сонгох"</string>
<string name="add_account_label" msgid="2935267344849993553">"Бүртгэл нэмэх"</string>
- <string name="add_account_button_label" msgid="3611982894853435874">"Аккаунт нэмэх"</string>
+ <string name="add_account_button_label" msgid="3611982894853435874">"Бүртгэл нэмэх"</string>
<string name="number_picker_increment_button" msgid="2412072272832284313">"Өсөх"</string>
<string name="number_picker_decrement_button" msgid="476050778386779067">"Бууруулах"</string>
<string name="number_picker_increment_scroll_mode" msgid="5259126567490114216">"<xliff:g id="VALUE">%s</xliff:g> хүрээд, хүлээнэ үү."</string>
@@ -1547,7 +1551,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Нэмэлт сонголтууд"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <string name="storage_internal" msgid="3570990907910199483">"Дотоод хуваалцсан санах ой"</string>
+ <string name="storage_internal" msgid="3570990907910199483">"Дундын дотоод хадгалах сан"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD карт"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD карт"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB диск"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Ангилаагүй"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Та эдгээр мэдэгдлийн ач холбогдлыг тогтоосон."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Оролцсон хүмүүсээс шалтгаалан энэ нь өндөр ач холбогдолтой."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="APP">%1$s</xliff:g>-д <xliff:g id="ACCOUNT">%2$s</xliff:g>-тай (ийм бүртгэлтэй хэрэглэгч аль хэдийн байна) шинэ хэрэглэгч үүсгэхийг зөвшөөрөх үү ?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"<xliff:g id="APP">%1$s</xliff:g>-д <xliff:g id="ACCOUNT">%2$s</xliff:g>-тай шинэ хэрэглэгч үүсгэхийг зөвшөөрөх үү?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Хэл нэмэх"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Бүс нутгийн тохиргоо"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Улсын хэлийг бичнэ үү"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index e22bca049ece..4fb2347f5641 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"SMS मेसेज प्राप्त करण्याची आणि त्यावर प्रक्रिया करण्याची अ‍ॅप ला अनुमती देते. म्हणजेच अ‍ॅप आपल्या डीव्हाइसवर पाठविलेले मेसेज तुम्हाला न दर्शवता त्यांचे परीक्षण करू किंवा ते हटवू शकतो."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"मजकूर मेसेज मिळवा (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"MMS मेसेज प्राप्त करण्यास आणि त्यावर प्रक्रिया करण्यास अ‍ॅप ला अनुमती देते. म्हणजेच अ‍ॅप आपल्या डिव्हाइसवर पाठविलेले मेसेज तुम्हाला न दर्शवता त्यांचे परीक्षण करू किंवा ते हटवू शकतो."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"सेल प्रसारण मेसेज फॉरवर्ड करा"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"सेल प्रसारण मेसेज मिळाल्यानंतर ते फॉरवर्ड करण्यासाठी अॅपला सेल प्रसारण मॉड्यूलमध्ये प्रतिबद्ध करण्याची अनुमती देते. काही स्थानांमध्ये तुम्हाला आणीबाणीच्या परिस्थीतींची चेतावणी देण्यासाठी सेल प्रसारण सूचना वितरित केल्या जातात. दुर्भावनापूर्ण अॅप्स आणीबाणी सेल प्रसारण मिळवतात तेव्हा ती तुमच्या डिव्हाइसच्या परफॉर्मन्समध्ये किंवा कामामध्ये कदाचित व्यत्यय आणू शकतात."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"सेल प्रसारण मेसेज वाचा"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"आपल्या डिव्हाइसद्वारे प्राप्त केलेले सेल प्रसारण मेसेज वाचण्यासाठी अ‍ॅप ला अनुमती देते. काही स्थानांमध्ये तुम्हाला आणीबाणीच्या परिस्थितीची चेतावणी देण्यासाठी सेल प्रसारण सूचना वितरीत केल्या जातात. आणीबाणी सेल प्रसारण प्राप्त होते तेव्हा आपल्या डिव्हाइसच्या कार्यप्रदर्शनात किंवा कार्यात दुर्भावनापूर्ण अ‍ॅप्स व्यत्यय आणू शकतात."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"सदस्यता घेतलेली फीड वाचा"</string>
@@ -999,7 +1001,7 @@
<string name="weeks" msgid="6509623834583944518">"आठवडे"</string>
<string name="year" msgid="4001118221013892076">"वर्ष"</string>
<string name="years" msgid="6881577717993213522">"वर्षे"</string>
- <string name="now_string_shortest" msgid="8912796667087856402">"आत्ता"</string>
+ <string name="now_string_shortest" msgid="8912796667087856402">"आता"</string>
<plurals name="duration_minutes_shortest" formatted="false" msgid="3957499975064245495">
<item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g>मि</item>
<item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g>मि</item>
@@ -1362,6 +1364,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB डीबगिंग बंद करण्यासाठी निवडा."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"टेस्ट हार्नेस मोड सुरू केला आहे"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"टेस्ट हार्नेस मोड बंद करण्यासाठी फॅक्टरी रीसेट करा."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"सिरीअल कन्सोल सुरू केला आहे"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"परफॉर्मन्सवर परिणाम होतो. बंद करण्यासाठी, बूटलोडर तपासा."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB पोर्ट मध्ये ओलावा किंवा धूळ आहे"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB पोर्ट आपोआप बंद होईल. अधिक जाणून घेण्यासाठी टॅप करा."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB पोर्ट वापरण्यासाठी ठीक आहे"</string>
@@ -1891,10 +1895,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"वर्गीकरण न केलेले"</string>
<string name="importance_from_user" msgid="7318955817386549931">"तुम्ही या सूचनांचे महत्त्व सेट केले."</string>
<string name="importance_from_person" msgid="9160133597262938296">"सामील असलेल्या लोकांमुळे हे महत्वाचे आहे."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="ACCOUNT">%2$s</xliff:g> सह नवीन वापरकर्ता तयार करण्याची (हे खाते असलेला वापरकर्ता आधीपासून अस्तित्वात आहे) <xliff:g id="APP">%1$s</xliff:g> ला अनुमती द्यायची आहे का?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"<xliff:g id="ACCOUNT">%2$s</xliff:g> सह नवीन वापरकर्ता तयार करण्याची <xliff:g id="APP">%1$s</xliff:g> ला अनुमती द्यायची आहे का?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"एक भाषा जोडा"</string>
<string name="country_selection_title" msgid="2954859441620215513">"प्रदेश प्राधान्य"</string>
<string name="search_language_hint" msgid="7042102592055108574">"भाषा नाव टाइप करा"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 35e36587ad9c..4c811fde4ae7 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Membenarkan apl menerima dan memproses mesej SMS. Ini bermakna apl boleh memantau atau memadam mesej yang dihantar ke peranti anda tanpa menunjukkannya kepada anda."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"terima mesej teks (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Membenarkan apl menerima dan memproses mesej MMS. Ini bermakna apl boleh memantau atau memadam mesej yang dihantar ke peranti anda tanpa menunjukkannya kepada anda."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Kirim semula mesej siaran sel"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Membenarkan apl terikat pada modul siaran sel untuk mengirim semula mesej siaran sel apabila diterima. Makluman siaran sel dihantar di sesetengah lokasi untuk memberi amaran kepada anda tentang situasi kecemasan. Apl hasad boleh mengganggu prestasi atau operasi peranti anda apabila siaran sel kecemasan diterima."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"baca mesej siaran sel"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Membolehkan apl membaca mesej siaran sel yang diterima oleh peranti anda. Isyarat siaran sel dihantar di beberapa lokasi untuk memberi amaran kepada anda tentang situasi kecemasan. Apl hasad boleh mengganggu prestasi atau operasi peranti anda apabila siaran sel kecemasan diterima."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"baca suapan langganan"</string>
@@ -542,11 +544,11 @@
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Wajah disahkan, sila tekan sahkan"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Perkakasan cap jari tidak tersedia."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Cap jari tidak dapat disimpan. Sila alih keluar cap jari sedia ada."</string>
- <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Tamat masa cap jari dicapai. Cuba lagi."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Sudah tamat masa untuk cap jari. Cuba lagi"</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Pengendalian cap jari dibatalkan."</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Pengendalian cap jari dibatalkan oleh pengguna."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Terlalu banyak percubaan. Cuba sebentar lagi."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Terlalu banyak percubaan. Penderia cap jadi dilumpuhkan."</string>
+ <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Terlalu banyak percubaan. Penderia cap jari dilumpuhkan."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Cuba lagi."</string>
<string name="fingerprint_error_no_fingerprints" msgid="7654382120628334248">"Tiada cap jari didaftarkan."</string>
<string name="fingerprint_error_hw_not_present" msgid="409523969613176352">"Peranti ini tiada penderia cap jari."</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Pilih untuk melumpuhkan penyahpepijatan USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Mod Abah-abah Ujian didayakan"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Laksanakan tetapan semula kilang untuk melumpuhkan Mod Abah-abah Ujian."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Konsol bersiri didayakan"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Prestasi terjejas. Untuk melumpuhkan, semak pemuat but."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Cecair atau serpihan dalam port USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Port USB dilumpuhkan secara automatik. Ketik untuk mengetahui lebih lanjut."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"OK untuk menggunakan port USB"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Tidak dikategorikan"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Anda menetapkan kepentingan pemberitahuan ini."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Mesej ini penting disebabkan orang yang terlibat."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Benarkan <xliff:g id="APP">%1$s</xliff:g> membuat Pengguna baharu dengan <xliff:g id="ACCOUNT">%2$s</xliff:g> (Pengguna dengan akaun ini sudah wujud) ?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Benarkan <xliff:g id="APP">%1$s</xliff:g> membuat Pengguna baharu dengan <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Tambahkan bahasa"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Pilihan wilayah"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Taipkan nama bahasa"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index d69b7d1fcbbb..38a33bf6ed86 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -324,7 +324,7 @@
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"မျက်နှာပြင် ဇူးမ်အရွယ်နှင့် နေရာချထားခြင်းကို ထိန်းချုပ်ပါသည်။"</string>
<string name="capability_title_canPerformGestures" msgid="7418984730362576862">"လက်ဟန်များ အသုံးပြုပါ"</string>
<string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"တို့ခြင်း၊ ပွတ်ဆွဲခြင်း၊ နှင့် အခြား လက်ဟန်များကို အသုံးပြုနိုင်ပါသည်။"</string>
- <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"လက်ဗွေရာများ"</string>
+ <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"လက်ဗွေဟန်များ"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"စက်ပစ္စည်း၏ လက်ဗွေအာရုံခံကိရိယာတွင် လုပ်ဆောင်ထားသည့် လက်ဟန်များကို မှတ်သားထားနိုင်သည်။"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"အခြေအနေပြဘားအား အလုပ်မလုပ်ခိုင်းရန်သို့မဟုတ် မွမ်းမံရန်"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"အက်ပ်အား အခြေအနေပြ ဘားကို ပိတ်ခွင့် သို့မဟတ် စနစ် အိုင်ကွန်များကို ထည့်ခြင်း ဖယ်ရှားခြင်း ပြုလုပ်ခွင့် ပြုသည်။"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"အပလီကေးရှင်းအား စာတိုများ လက်ခံခြင်း၊ ဆောင်ရွက်ခြင်း ခွင့်ပြုပါ။ ဤခွင့်ပြုချက်တွင် အပလီကေးရှင်းအနေဖြင့် သင် လက်ခံရရှိသော စာများအား သင့်အား မပြပဲစောင့်ကြည့်ခွင့်နှင့် ဖျက်ပစ်ခွင့်များ ပါဝင်ပါသည်။"</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"စာပို့ခြင်းအား လက်ခံရယူခြင်း (ရုပ်သံစာ)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"အပလီကေးရှင်းအား ရုပ်သံစာများ လက်ခံခြင်း၊ ဆောင်ရွက်ခြင်း ခွင့်ပြုပါ။ ဤခွင့်ပြုချက်တွင် အပလီကေးရှင်းအနေဖြင့် သင် လက်ခံရရှိသော စာများအား သင့်အား မပြပဲစောင့်ကြည့်ခွင့်နှင့် ဖျက်ပစ်ခွင့်များ ပါဝင်ပါသည်။"</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"စာတို ဖြန့်ဝေခြင်းစနစ်သုံး မက်ဆေ့ဂျ်များကို ထပ်ဆင့်ပို့ခြင်း"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"စာတို ဖြန့်ဝေခြင်းစနစ် မော်ဂျူးကိုပေါင်းရန် အက်ပ်များအား ခွင့်ပြုသည်။ ၎င်းမှာ စာတို ဖြန့်ဝေခြင်းစနစ်သုံး မက်ဆေ့ဂျ်များကို လက်ခံရရှိသည့်အတိုင်း ထပ်ဆင့်ပို့ရန် ဖြစ်သည်။ အချို့တည်နေရာများတွင် သင့်အား အရေးပေါ်အခြေအနေများကို သတိပေးရန် စာတို ဖြန့်ဝေခြင်းစနစ်သုံး သတိပေးချက်များကို ပေးပို့သည်။ အရေးပေါ် စာတို ဖြန့်ဝေခြင်းကို ရရှိသည့်အခါ သံသယဖြစ်နိုင်ဖွယ်ရှိသည့် အက်ပ်များက သင့်စက်၏ စွမ်းဆောင်ရည်နှင့် အော်ပရေးရှင်းတို့ကို အနှောင့်အယှက်ပေးနိုင်သည်။"</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"စာတိုများ ဖြန့်ဝေခြင်းစနစ်အား ဖတ်ခြင်း"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"အပလီကေးရှင်းကို သင်၏ စက်ပစ္စည်းမှ လက်ခံရရှိသော အများလွှင့်ထုတ်ချက်များကို ဖတ်ရန် ခွင့်ပြုသည်။ အများလွှင့်ထုတ်ချက်များသည် အရေးပေါ်အခြေအနေများကို သင့်အား သတိပေးရန် အချို့ နေရာများတွင် ပို့ပေးသည်။ အရေးပေါ်သတိပေးချက် ထုတ်လွှင့်ချက်ကို လက်ခံရရှိချိန်တွင်အန္တရာယ် ဖြစ်စေနိုင်သော အပလီကေးရှင်းများသည် သင့်စက်ပစ္စည်း၏ လုပ်ငန်းလည်ပတ်မှုနှင့် စွမ်းဆောင်မှုကို ဝင်စွက်ဖက်နိုင်သည်။"</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"အမည်သွင်းထားသောဖိဖ့်များကို ဖတ်ခြင်း"</string>
@@ -512,10 +514,10 @@
<string name="permdesc_requestPasswordComplexity" msgid="4730994229754212347">"ဖုန်းမျက်နှာပြင်လော့ခ်၏ ရှုပ်ထွေးမှုအဆင့် (မြင့်၊ အလယ်အလတ်၊ နိမ့် သို့မဟုတ် မရှိ) အား လေ့လာရန် အက်ပ်ကို ခွင့်ပြုသည်။ ၎င်းက သတ်မှတ်ထားနိုင်သော ဖုန်းမျက်နှာပြင်လော့ခ်၏ စာလုံးရေနှင့် အမျိုးအစားကို ညွှန်ပြပေးသည်။ အသုံးပြုသူများအနေနှင့် ဖုန်းမျက်နှာပြင်လော့ခ်ကို အတိုင်းအတာတစ်ခုအထိ အဆင့်မြှင့်ရန် အက်ပ်က အကြံပြုနိုင်သည်။ သို့သော်လည်း အသုံးပြုသူများက ၎င်းကို ဂရုပြုမနေဘဲ လွတ်လပ်စွာ ကြည့်ရှုနိုင်ပါသည်။ ဖုန်းမျက်နှာပြင်လော့ခ်ကို စာသားအတိုင်း သိမ်းမထားသဖြင့် အက်ပ်သည် စကားဝှက်အစစ်ကို မသိနိုင်ကြောင်း သတိပြုပါ။"</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"ဇီဝဗေဒဆိုင်ရာ အချက်အလက်သုံး ကွန်ပျူတာဆိုင်ရာ စက်ပစ္စည်းကို အသုံးပြုရန်"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"အထောက်အထားစိစစ်ခြင်းအတွက် ဇီဝဗေဒဆိုင်ရာ သတင်းအချက်အလက်များသုံးသည့် ကွန်ပျူတာဆိုင်ရာ စက်ပစ္စည်းကို အသုံးပြုရန် အက်ပ်ကို ခွင့်ပြုသည်"</string>
- <string name="permlab_manageFingerprint" msgid="5640858826254575638">"လက်ဗွေရာပစ္စည်းကို စီမံမည်"</string>
- <string name="permdesc_manageFingerprint" msgid="178208705828055464">"အသုံးပြုရန်အတွက် လက်ဗွေရာပုံစံများကို ပေါင်းထည့်ရန် သို့မဟုတ် ဖျက်ရန်နည်းလမ်းများကို အပ်ဖ်အား အသုံးပြုခွင့်ပြုသည်။"</string>
- <string name="permlab_useFingerprint" msgid="3150478619915124905">"လက်ဗွေရာပစ္စည်းကို အသုံးပြုမည်"</string>
- <string name="permdesc_useFingerprint" msgid="9165097460730684114">"စစ်မှန်ကြောင်းအထောက်အထားပြသခြင်းအတွက် လက်ဗွေရာပစ္စည်းကို အသုံးပြုရန် အပ်ဖ်အားခွင့်ပြုသည်။"</string>
+ <string name="permlab_manageFingerprint" msgid="5640858826254575638">"လက်ဗွေစစ်ပစ္စည်းကို စီမံခြင်း"</string>
+ <string name="permdesc_manageFingerprint" msgid="178208705828055464">"အသုံးပြုရန်အတွက် လက်ဗွေပုံစံများကို ပေါင်းထည့်ရန် သို့မဟုတ် ဖျက်ရန်နည်းလမ်းများကို အပ်က်အား သုံးခွင့်ပြုသည်။"</string>
+ <string name="permlab_useFingerprint" msgid="3150478619915124905">"လက်ဗွေ စက်ပစ္စည်းကို အသုံးပြုခြင်း"</string>
+ <string name="permdesc_useFingerprint" msgid="9165097460730684114">"အထောက်အထားစိစစ်ရန်အတွက် အက်ပ်ကို လက်ဗွေစစ်ပစ္စည်း သုံးခွင့်ပြုသည်"</string>
<string name="permlab_audioWrite" msgid="2661772059799779292">"သင့်တေးဂီတ စုစည်းမှုကို ပြုပြင်ခြင်း"</string>
<string name="permdesc_audioWrite" msgid="8888544708166230494">"အက်ပ်အား သင့်တေးဂီတစုစည်းမှုကို ပြုပြင်ခွင့်ပေးသည်။"</string>
<string name="permlab_videoWrite" msgid="128769316366746446">"သင့်ဗီဒီယို စုစည်းမှုကို ပြုပြင်ခြင်း"</string>
@@ -530,8 +532,8 @@
<string name="biometric_not_recognized" msgid="5770511773560736082">"မသိ"</string>
<string name="biometric_error_canceled" msgid="349665227864885880">"အထောက်အထားစိစစ်ခြင်းကို ပယ်ဖျက်လိုက်သည်"</string>
<string name="biometric_error_device_not_secured" msgid="6583143098363528349">"ပင်နံပါတ်၊ လော့ခ်ပုံစံ သို့မဟုတ် စကားဝှက် သတ်မှတ်မထားပါ"</string>
- <string name="fingerprint_acquired_partial" msgid="735082772341716043">"လက်ဗွေရဦ တစ်ပိုင်းတစ်စ တွေ့ရှိသည်။ ကျေးဇူးပြု၍ ထပ်မံကြိုးစားပါ။"</string>
- <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"လက်ဗွေရာယူခြင်း မဆောင်ရွက်နိုင်ပါ။ ထပ်စမ်းကြည့်ပါ။"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"လက်ဗွေ တစ်ပိုင်းတစ်စ တွေ့ရှိသည်။ ကျေးဇူးပြု၍ ထပ်စမ်းကြည့်ပါ။"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"လက်ဗွေယူ၍ မရပါ။ ထပ်စမ်းကြည့်ပါ။"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"လက်ဗွေဖတ်ကိရိယာ ညစ်ပေနေသည်။ ကျေးဇူးပြု၍ သန့်ရှင်းလိုက်ပြီး ပြန်စမ်းကြည့်ပါ။"</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"လက်ညှိုး အလွန်မြန်ဆန်စွာ ရွေ့ခဲ့သည်။ ထပ်မံ ကြိုးစားပါ။"</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"လက်ညှိုးအလွန်နှေးကွေးစွာ ရွေ့ခဲ့သည်။ ကျေးဇူးပြု၍ ထပ်မံကြိုးစားပါ။"</string>
@@ -540,20 +542,20 @@
<string name="fingerprint_authenticated" msgid="5309333983002526448">"လက်ဗွေကို အထောက်အထား စိစစ်ပြီးပါပြီ"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"မျက်နှာ အထောက်အထားစိစစ်ပြီးပြီ"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"မျက်နှာ အထောက်အထားစိစစ်ပြီးပြီ၊ အတည်ပြုရန်ကို နှိပ်ပါ"</string>
- <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"လက်ဗွေရာ ဟာ့ဒ်ဝဲ မရနိုင်ပါ။"</string>
- <string name="fingerprint_error_no_space" msgid="1055819001126053318">"လက်ဗွေရာ သိုလှောင်၍မရပါ။ ကျေးဇူးပြု၍ ရှိပြီးလက်ဗွေရာအား ဖယ်ရှားပါ။"</string>
- <string name="fingerprint_error_timeout" msgid="3927186043737732875">"လက်ဗွေရာအချိန်ကုန် သွားပါသည်။ ထပ်မံကြိုးစားပါ။"</string>
- <string name="fingerprint_error_canceled" msgid="4402024612660774395">"လက်ဗွေရာ လုပ်ငန်း ဖျက်သိမ်းခဲ့၏။"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"လက်ဗွေ စက်ပစ္စည်းမရနိုင်ပါ။"</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"လက်ဗွေ သိုလှောင်၍မရပါ။ လက်ရှိ လက်ဗွေကို ဖယ်ရှားပါ။"</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"လက်ဗွေယူချိန်ကုန် သွားပါသည်။ ထပ်စမ်းကြည့်ပါ။"</string>
+ <string name="fingerprint_error_canceled" msgid="4402024612660774395">"လက်ဗွေယူခြင်း ပယ်ဖျက်လိုက်သည်။"</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"လက်ဗွေဖြင့် အထောက်အထားစိစစ်ခြင်းကို အသုံးပြုသူက ပယ်ဖျက်ထားသည်။"</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"ကြိုးစာမှု အကြိမ်များနေ၏။ နောက်မှ ထပ်မံကြိုးစားပါ။"</string>
- <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"အကြိမ်အရေအတွက် အလွန်များနေပါပြီ။ လက်ဗွေဖတ်စနစ်ကို ပိတ်ထားပါသည်။"</string>
+ <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"အကြိမ်အရေအတွက် အလွန်များနေပါပြီ။ လက်ဗွေအာရုံခံကိရိယာ ပိတ်ထားပါသည်။"</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"ပြန်ကြိုးစားပါ"</string>
<string name="fingerprint_error_no_fingerprints" msgid="7654382120628334248">"မည်သည့် လက်ဗွေကိုမျှ ထည့်သွင်းမထားပါ။"</string>
<string name="fingerprint_error_hw_not_present" msgid="409523969613176352">"ဤစက်တွင် လက်ဗွေအာရုံခံကိရိယာ မရှိပါ။"</string>
<string name="fingerprint_name_template" msgid="5870957565512716938">"လက်ချောင်း <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
- <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"လက်ဗွေ အိုင်ကွန်"</string>
+ <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"လက်ဗွေ သင်္ကေတ"</string>
<string name="permlab_manageFace" msgid="7262837876352591553">"မျက်နှာမှတ် သော့ဖွင့်ခြင်း စက်ပစ္စည်းကို စီမံခြင်း"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"အသုံးပြုရန်အတွက် မျက်နှာပုံစံထည့်ရန် (သို့) ဖျက်ရန်နည်းလမ်းကို အက်ပ်အား သုံးခွင့်ပြုသည်။"</string>
<string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"မျက်နှာမှတ် သော့ဖွင့်ခြင်း စက်ပစ္စည်းကို သုံးပါ"</string>
@@ -1159,7 +1161,7 @@
<string name="clearDefaultHintMsg" msgid="3252584689512077257">"စနစ် ဆက်တင် ထဲမှာ ပုံသေကို ရှင်းလိုက်ပါ &gt; အက်ပ်များ &gt; ဒေါင်းလုဒ် လုပ်ပြီး။"</string>
<string name="chooseActivity" msgid="7486876147751803333">"လုပ်စရာ တစ်ခု ရွေးချယ်ပါ"</string>
<string name="chooseUsbActivity" msgid="6894748416073583509">"USB ကိရိယာ အတွက် အက်ပ်တစ်ခု ရွေးပါ"</string>
- <string name="noApplications" msgid="2991814273936504689">"ဘယ် အက်ပ်ကမှ ဒီ လုပ်ဆောင်ချက်ကို မလုပ်ကိုင်နိုင်ပါ။"</string>
+ <string name="noApplications" msgid="2991814273936504689">"မည်သည့်အက်ပ်ကမျှ ဤလုပ်ဆောင်ချက်ကို မလုပ်ကိုင်နိုင်ပါ။"</string>
<string name="aerr_application" msgid="250320989337856518">"<xliff:g id="APPLICATION">%1$s</xliff:g> ရပ်သွားပါပြီ"</string>
<string name="aerr_process" msgid="6201597323218674729">"<xliff:g id="PROCESS">%1$s</xliff:g> ရပ်တန့်သွားပါပြီ"</string>
<string name="aerr_application_repeated" msgid="3146328699537439573">"<xliff:g id="APPLICATION">%1$s</xliff:g> သည်ထပ်တလဲလဲ ရပ်တန့်နေပါသည်"</string>
@@ -1362,6 +1364,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB ဖြင့် အမှားရှာပြင်ခြင်းကို ပိတ်ရန် ရွေးပါ။"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"\'စမ်းသပ်ခြင်းစနစ်မုဒ်\' ဖွင့်ထားသည်"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"\'စမ်းသပ်ခြင်းစနစ် မုဒ်\' ကိုပိတ်ရန် စက်ရုံထုတ်အတိုင်း ပြင်ဆင်သတ်မှတ်ပါ။"</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"အမှတ်စဉ် ကွန်ဆိုးလ်ကို ဖွင့်ထားသည်"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"စွမ်းဆောင်ရည်အပေါ် သက်ရောက်မှုရှိနိုင်ပါသည်။ ပိတ်ရန် bootloader ကို စစ်ဆေးပါ။"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB ပို့တ်တွင် အရည် သို့မဟုတ် အမှိုက်စ ရှိနေသည်"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB ပို့တ်ကို အလိုအလျောက် ပိတ်ထားသည်။ ပိုမိုလေ့လာရန် တို့ပါ။"</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB ပို့တ်ကို အသုံးပြုနိုင်သည်"</string>
@@ -1449,8 +1453,8 @@
<string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"အောက်ပါထဲက အက်ပ်တစ်ခု သို့မဟုတ် ပိုလျက် သင်၏ အကောင့်ကို၊ ယခု နှင့် အနာဂတ်မှာ ရယူအသုံးချရန် ခွင့်ပြုချက်ကို တောင်းထားသည်။"</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"ဤတောင်းခံမှုအားခွင့်ပြုမည်လား"</string>
<string name="grant_permissions_header_text" msgid="6874497408201826708">"သုံးစွဲခွင့် တောင်းဆိုရန်"</string>
- <string name="allow" msgid="7225948811296386551">"ခွင့်ပြုသည်"</string>
- <string name="deny" msgid="2081879885755434506">"ငြင်းပယ်သည်"</string>
+ <string name="allow" msgid="7225948811296386551">"ခွင့်ပြုရန်"</string>
+ <string name="deny" msgid="2081879885755434506">"ငြင်းပယ်ရန်"</string>
<string name="permission_request_notification_title" msgid="6486759795926237907">"ခွင့်ပြုချက် တောင်းခံထားခြင်း"</string>
<string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"အကောင့် <xliff:g id="ACCOUNT">%s</xliff:g> အတွက် \n ခွင့်ပြုချက် တောင်းခံထားပြီး"</string>
<string name="forward_intent_to_owner" msgid="1207197447013960896">"သင်သည် ဒီအက်ပ်ကို သင့်အလုပ်ပရိုဖိုင် ပြင်ပတွင် အသုံးပြုနေ၏"</string>
@@ -1580,8 +1584,8 @@
<string name="expires_on" msgid="3676242949915959821">"သက်တမ်းကုန်ဆုံးရက်-"</string>
<string name="serial_number" msgid="758814067660862493">"အစဉ်လိုက်နံပါတ်"</string>
<string name="fingerprints" msgid="4516019619850763049">"လက်ပွေများ"</string>
- <string name="sha256_fingerprint" msgid="4391271286477279263">"SHA-256 လက်ပွေ"</string>
- <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 လက်ပွေ"</string>
+ <string name="sha256_fingerprint" msgid="4391271286477279263">"SHA-256 လက်ဗွေ-"</string>
+ <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 လက်ဗွေ-"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"အားလုံးကို ကြည့်ရန်"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"လုပ်ဆောင်ချက်ကို ရွေးရန်"</string>
<string name="share_action_provider_share_with" msgid="5247684435979149216">"...နှင့် မျှဝေရန်"</string>
@@ -1891,10 +1895,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"အမျိုးအစားမခွဲရသေးပါ"</string>
<string name="importance_from_user" msgid="7318955817386549931">"ဤသတိပေးချက်များ၏ အရေးပါမှုကိုသတ်မှတ်ပြီးပါပြီ။"</string>
<string name="importance_from_person" msgid="9160133597262938296">"ပါဝင်သည့်လူများကြောင့် အရေးပါပါသည်။"</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="ACCOUNT">%2$s</xliff:g> ဖြင့်အသုံးပြုသူအသစ်ကို <xliff:g id="APP">%1$s</xliff:g> အား ဖန်တီးခွင့်ပြုလိုပါသလား (ဤအကောင့်ဖြင့် အသုံးပြုသူ ရှိနှင့်ပြီးဖြစ်သည်) ။"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"<xliff:g id="ACCOUNT">%2$s</xliff:g> ဖြင့်အသုံးပြုသူအသစ်ကို <xliff:g id="APP">%1$s</xliff:g> အား ဖန်တီးခွင့်ပြုလိုပါသလား ။"</string>
<string name="language_selection_title" msgid="2680677278159281088">"ဘာသာစကားတစ်ခု ထည့်ပါ"</string>
<string name="country_selection_title" msgid="2954859441620215513">"ဒေသရွေးချယ်မှု"</string>
<string name="search_language_hint" msgid="7042102592055108574">"ဘာသာစကားအမည် ထည့်ပါ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index a59717047add..2d4a5f525ee1 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -226,7 +226,7 @@
<string name="global_actions" product="default" msgid="2406416831541615258">"Telefoninnstillinger"</string>
<string name="global_action_lock" msgid="2844945191792119712">"Lås skjermen"</string>
<string name="global_action_power_off" msgid="4471879440839879722">"Slå av"</string>
- <string name="global_action_emergency" msgid="7112311161137421166">"Nødsituasjon"</string>
+ <string name="global_action_emergency" msgid="7112311161137421166">"Nødssituasjon"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Feilrapport"</string>
<string name="global_action_logout" msgid="935179188218826050">"Avslutt økten"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Skjermdump"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Lar appen motta og behandle tekstmeldinger. Dette betyr at appen kan overvåke eller slette meldinger som er sendt til enheten din uten at du har sett dem."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"motta tekstmeldinger (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Lar appen motta og behandle multimediemeldinger. Dette betyr at appen kan overvåke eller slette meldinger som er sendt til enheten din uten at du har sett dem."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Videresend kringkastede meldinger"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Tillat at denne appen binder seg til modulen for kringkastede meldinger for å videresende kringkastede meldinger når de mottas. Kringkastede varsler leveres noen steder for å advare deg om nødssituasjoner. Skadelige apper kan forstyrre ytelsen eller funksjonen til enheten din når en kringkastet nødmelding mottas."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"lese kringkastede meldinger"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Tillater at appen kan lese kringkastede meldinger enheten din mottar. Kringkastede varsler leveres noen steder for å advare deg om nødssituasjoner. Skadelige apper kan forstyrre ytelsen eller funksjonen til enheten din når en kringkastet nødmelding mottas."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"lese abonnement på nyhetskilder"</string>
@@ -815,7 +817,7 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Trykk på menyknappen for å låse opp eller ringe et nødnummer."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Trykk på menyknappen for å låse opp."</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Tegn mønster for å låse opp"</string>
- <string name="lockscreen_emergency_call" msgid="5298642613417801888">"Nødsituasjon"</string>
+ <string name="lockscreen_emergency_call" msgid="5298642613417801888">"Nødssituasjon"</string>
<string name="lockscreen_return_to_call" msgid="5244259785500040021">"Tilbake til samtale"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Riktig!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Prøv på nytt"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Velg for å deaktivere USB-debugging."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Testrammeverk-modus er slått på"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Tilbakestill enheten til fabrikkstandard for å slå av Testrammeverk-modus."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Seriekonsollen er aktivert"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Ytelsen er påvirket. Sjekk oppstartsinnlasteren for å deaktivere."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Væske eller rusk i USB-porten"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB-porten deaktiveres automatisk. Trykk for å finne ut mer."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Trygt å bruke USB-porten"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Uten kategori"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Du angir viktigheten for disse varslene."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Dette er viktig på grunn av folkene som er involvert."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Vil du la <xliff:g id="APP">%1$s</xliff:g> opprette en ny bruker med <xliff:g id="ACCOUNT">%2$s</xliff:g> (en bruker med denne kontoen eksisterer allerede)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Vil du la <xliff:g id="APP">%1$s</xliff:g> opprette en ny bruker med <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Legg til et språk"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Regionsinnstilling"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Skriv inn språknavn"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 0cbaaa023c94..dc44d2a2f659 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -325,7 +325,7 @@
<string name="capability_title_canPerformGestures" msgid="7418984730362576862">"इसाराहरू सम्बन्धी कार्य गर्नुहोस्"</string>
<string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"ट्याप, स्वाइप गर्न, थिच्न र अन्य इसाराहरू सम्बन्धी कार्य गर्न सक्छ"</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"फिंगरप्रिन्टका इसाराहरू"</string>
- <string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"यसले यन्त्रक‍ो फिङ्गरप्रिन्टसम्बन्धी सेन्सरमा गरिएका इसाराहरूलाई खिच्‍न सक्छ।"</string>
+ <string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"यसले यन्त्रक‍ो फिंगरप्रिन्टसम्बन्धी सेन्सरमा गरिएका इसाराहरूलाई खिच्‍न सक्छ।"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"स्थिति पट्टिलाई अक्षम वा संशोधित गर्नुहोस्"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"स्थिति पट्टि असक्षम पार्न वा प्रणाली आइकनहरू थप्न र हटाउन अनुप्रयोगलाई अनुमति दिन्छ।"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"वस्तुस्थिति पट्टी हुन दिनुहोस्"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"अनुप्रयोगलाई SMS सन्देशहरू प्राप्त गर्न र प्रक्रिया गर्न अनुमति दिन्छ। यसको मतलब अनुप्रयोगले तपाईंको उपकरणमा पठाइएको सन्देशहरू तपाईंलाई नदेखाईनै मोनिटर गर्न वा मेटाउन सक्दछ।"</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"पाठ सन्देश (MMS) प्राप्त गर्नुहोस्"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"अनुप्रयोगलाई MMS सन्देशहरू प्राप्त गर्न र प्रकृया गर्न अनुमति दिन्छ। यसको मतलब अनुप्रयोगले तपाईंको उपकरणमा पठाइएको सन्देशहरू तपाईंलाई नदेखाईनै मोनिटर गर्न वा मेटाउन सक्दछ।"</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"मोबाइल प्रसारणसम्बन्धी सन्देशहरू फर्वार्ड गर्नुहोस्"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"मोबाइल प्रसारणसम्बन्धी सन्देशहरू प्राप्त हुनासाथै तिनीहरूलाई फर्वार्ड गर्नका लागि यसले अनुप्रयोगलाई मोबाइल प्रसारण मोड्युलमा जोडिने अनुमति दिन्छ। तपाईंलाई कतिपय स्थानमा आपत्कालीन अवस्थाका बारेमा जानकारी दिनका लागि मोबाइल प्रसारणसम्बन्धी अलर्टहरू पठाइन्छ। हानिकारक अनुप्रयोगहरूले आपत्कालीन मोबाइल प्रसारण प्राप्त हुँदा तपाईंको यन्त्रलाई कार्य सम्पादन गर्ने वा सञ्चालित हुने क्रममा हस्तक्षेप गर्न सक्छन्।"</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"सेल प्रसारित सन्देशहरू पढ्नुहोस्"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"तपाईंको उपकरणद्वारा प्राप्त सेल प्रसारण सन्देशहरू अनुप्रयोगलाई पढ्न अनुमति दिन्छ। सेल प्रसारण चेतावनीहरू केही स्थानहरूमा तपाईंलाई आपतकालीन गतिविधिहरूको बारेमा सचेत गराउन गरिएका छन्। खराब अनुप्रयोगहरूले एउटा आपतकालीन सेल प्रसारण प्राप्त गर्दछ जब तपाईंको उपकरणको प्रदर्शन वा अपरेशनको साथ हस्तक्षेप गर्न सक्दछन्।"</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"सदस्य बनाइका फिडहरू पढ्नुहोस्"</string>
@@ -512,10 +514,10 @@
<string name="permdesc_requestPasswordComplexity" msgid="4730994229754212347">"यसले अनुप्रयोगलाई स्क्रिन लकको जटिलताको स्तर (उच्च, मध्यम, न्यून वा कुनै पनि होइन) थाहा पाउने अनुमति दिन्छ जसले स्क्रिन लकको लम्बाइको सम्भावित दायरा र त्यसको प्रकारलाई जनाउँछ। यसै गरी, यो अनुप्रयोगले प्रयोगकर्ताहरूलाई स्क्रिन लक अद्यावधिक गर्ने सुझाव पनि दिन सक्छ तर प्रयोगकर्ताहरू उक्त सुझावको बेवास्ता गरी बाहिर निस्कन सक्छन्। स्क्रिन लक सादा पाठको ढाँचामा भण्डारण नगरिने हुँदा यो अनुप्रयोगलाई वास्तविक पासवर्ड थाहा नहुने कुराको हेक्का राख्नुहोस्।"</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"बायोमेट्रिक हार्डवेयर प्रयोग गर्नुहोस्‌"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"अनुप्रयोगलाई प्रमाणीकरणका लागि बायोमेट्रिक हार्डवेयर प्रयोग गर्न अनुमति दिन्छ"</string>
- <string name="permlab_manageFingerprint" msgid="5640858826254575638">"औठाछाप हार्डवेयर व्यवस्थापन गर्नुहोस्"</string>
- <string name="permdesc_manageFingerprint" msgid="178208705828055464">"अनुप्रयोगलाई प्रयोगको लागि औठाछाप टेम्प्लेट थप्न र मेटाउने तरिका आह्वान गर्न अनुमति दिन्छ।"</string>
- <string name="permlab_useFingerprint" msgid="3150478619915124905">"औठाछाप हार्डवेयर प्रयोग गर्नुहोस्"</string>
- <string name="permdesc_useFingerprint" msgid="9165097460730684114">"अनुप्रयोगलाई प्रमाणीकरणको लागि औठाछाप हार्डवेयर प्रयोग गर्न अनुमति दिन्छ"</string>
+ <string name="permlab_manageFingerprint" msgid="5640858826254575638">"फिंगरप्रिन्ट हार्डवेयर व्यवस्थापन गर्नुहोस्"</string>
+ <string name="permdesc_manageFingerprint" msgid="178208705828055464">"अनुप्रयोगलाई प्रयोगको लागि फिंगरप्रिन्ट टेम्प्लेट थप्न र मेटाउने तरिका आह्वान गर्न अनुमति दिन्छ।"</string>
+ <string name="permlab_useFingerprint" msgid="3150478619915124905">"फिंगरप्रिन्ट हार्डवेयर प्रयोग गर्नुहोस्"</string>
+ <string name="permdesc_useFingerprint" msgid="9165097460730684114">"यो अनुप्रयोगलाई प्रमाणीकरणको लागि फिंगरप्रिन्ट हार्डवेयर प्रयोग गर्न अनुमति दिन्छ"</string>
<string name="permlab_audioWrite" msgid="2661772059799779292">"आफ्नो सङ्गीतको सङ्ग्रह परिमार्जन गर्नुहोस्"</string>
<string name="permdesc_audioWrite" msgid="8888544708166230494">"यसले अनुप्रयोगलाई तपाईंको सङ्गीतको सङ्ग्रह परिमार्जन गर्न दिन्छ।"</string>
<string name="permlab_videoWrite" msgid="128769316366746446">"आफ्नो भिडियोको सङ्ग्रह परिमार्जन गर्नुहोस्"</string>
@@ -530,9 +532,9 @@
<string name="biometric_not_recognized" msgid="5770511773560736082">"पहिचान भएन"</string>
<string name="biometric_error_canceled" msgid="349665227864885880">"प्रमाणीकरण रद्द गरियो"</string>
<string name="biometric_error_device_not_secured" msgid="6583143098363528349">"कुनै पनि PIN, ढाँचा वा पासवर्ड सेट गरिएको छैन"</string>
- <string name="fingerprint_acquired_partial" msgid="735082772341716043">"आंशिक औठाछाप पत्ता लाग्यो। कृपया फेरि प्रयास गर्नुहोस्।"</string>
- <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"औठाछाप प्रशोधन गर्न सकिएन। कृपया फेरि प्रयास गर्नुहोस्।"</string>
- <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"औँठाछाप सेन्सर फोहोर छ। कृपया सफा गरेर फेरि प्रयास गर्नुहोस्।"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"आंशिक फिंगरप्रिन्ट पत्ता लाग्यो। कृपया फेरि प्रयास गर्नुहोस्।"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"फिंगरप्रिन्ट प्रशोधन गर्न सकिएन। कृपया फेरि प्रयास गर्नुहोस्।"</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"फिंगरप्रिन्ट सेन्सर फोहोर छ। कृपया सफा गरेर फेरि प्रयास गर्नुहोस्।"</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"औंला धेरै छिटो चलाइयो। पुन: प्रयास गर्नुहोस्।"</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"औंला निकै सुस्त सारियो। कृपया फेरि प्रयास गर्नुहोस्।"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -541,9 +543,9 @@
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"अनुहार प्रमाणीकरण गरियो"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"अनुहार प्रमाणीकरण गरियो, कृपया पुष्टि गर्नुहोस् थिच्नुहोस्"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"औँठाछाप हार्डवेयर उपलब्ध छैन।"</string>
- <string name="fingerprint_error_no_space" msgid="1055819001126053318">"औँठाछाप भण्डारण गर्न सकिँदैन। कृपया अवस्थित औठाछाप हटाउनुहोस्।"</string>
- <string name="fingerprint_error_timeout" msgid="3927186043737732875">"औँठाछापको समय सकिएको छ। फेरि प्रयास गर्नुहोस्।"</string>
- <string name="fingerprint_error_canceled" msgid="4402024612660774395">"औँठाछाप सञ्चालन रद्द गरियो।"</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"फिंगरप्रिन्ट भण्डारण गर्न सकिँदैन। कृपया अहिलेको फिंगरप्रिन्ट हटाउनुहोस्।"</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"फिंगरप्रिन्ट समय सकिएको छ। फेरि प्रयास गर्नुहोस्।"</string>
+ <string name="fingerprint_error_canceled" msgid="4402024612660774395">"फिंगरप्रिन्ट सञ्चालन रद्द गरियो।"</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"प्रयोगकर्ताले फिंगरप्रिन्टसम्बन्धी कारबाही रद्द गर्नुभयो।"</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"धेरै प्रयासहरू। केहि समय पछि पुन: प्रयास गर्नुहोला"</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"अत्यन्त धेरै प्रयासहरू। फिंगरप्रिन्ट सेन्सरलाई असक्षम पारियो।"</string>
@@ -1367,6 +1369,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB डिबगिङलाई असक्षम पार्न ट्याप गर्नुहोस्।"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"परीक्षण प्याकेज मोड सक्षम पारियो"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"परीक्षण प्याकेज मोड असक्षम पार्न फ्याक्ट्री रिसेट गर्नुहोस्।"</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"क्रमसम्बन्धी कन्सोल सक्षम पारियो"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"कार्यसम्पादनमा प्रभाव परेको छ। यसलाई असक्षम पार्न बुटलोडरको जाँच गर्नुहोस्।"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB पोर्टमा तरल पदार्थ वा धुलो भएको कुरा पत्ता लाग्यो"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB पोर्ट स्वतः असक्षम पारियो। थप जान्न ट्याप गर्नुहोस्।"</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB पोर्ट प्रयोग गर्दा हुन्छ"</string>
@@ -1586,7 +1590,7 @@
<string name="serial_number" msgid="758814067660862493">"क्रम संख्या:"</string>
<string name="fingerprints" msgid="4516019619850763049">"औँठाछापहरू:"</string>
<string name="sha256_fingerprint" msgid="4391271286477279263">"SHA-२५६ औंठाछाप:"</string>
- <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 औंलाछाप:"</string>
+ <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 फिंगरप्रिन्ट:"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"सबै हेर्नुहोस्"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"गतिविधि छनौट गर्नुहोस्"</string>
<string name="share_action_provider_share_with" msgid="5247684435979149216">"साझेदारी गर्नुहोस्..."</string>
@@ -1896,10 +1900,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"वर्गीकरण नगरिएको"</string>
<string name="importance_from_user" msgid="7318955817386549931">"तपाईंले यी सूचनाहरूको महत्त्व सेट गर्नुहोस् ।"</string>
<string name="importance_from_person" msgid="9160133597262938296">"यसमा सङ्लग्न भएका मानिसहरूको कारणले गर्दा यो महत्वपूर्ण छ।"</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="ACCOUNT">%2$s</xliff:g> (यस खाताको प्रयोगकर्ता पहिले नै अवस्थित छ) मा नयाँ प्रयोगकर्ता सिर्जना गर्न <xliff:g id="APP">%1$s</xliff:g> लाई अनुमति दिने हो?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"<xliff:g id="ACCOUNT">%2$s</xliff:g> मा नयाँ प्रयोगकर्ता सिर्जना गर्न <xliff:g id="APP">%1$s</xliff:g> लाई अनुमति दिने हो?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"भाषा थप्नुहोस्"</string>
<string name="country_selection_title" msgid="2954859441620215513">"क्षेत्रको प्राथमिकता"</string>
<string name="search_language_hint" msgid="7042102592055108574">"भाषाको नाम टाइप गर्नुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index e898389ed6e7..5c30c41f806e 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Hiermee kan de app sms-berichten ontvangen en verwerken. Dit betekent dat de app berichten die naar je apparaat zijn verzonden, kan bijhouden of verwijderen zonder deze aan u weer te geven."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"tekstberichten (MMS) ontvangen"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Hiermee kan de app MMS-berichten ontvangen en verwerken. Dit betekent dat de app berichten die naar je apparaat zijn verzonden, kan bijhouden of verwijderen zonder deze aan u weer te geven."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Cell broadcast-berichten doorsturen"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Hiermee kan de app de module voor cell broadcasts binden om cell broadcast-berichten door te sturen als die worden ontvangen. Cell broadcast-waarschuwingen worden op bepaalde locaties verzonden om je te waarschuwen voor noodsituaties. Schadelijke apps kunnen de prestaties of verwerking van je apparaat verstoren als een bericht met een noodmelding wordt ontvangen."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"infodienstberichten lezen"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Toestaan dat de app infodienstberichten leest die worden ontvangen op je apparaat. Infodienstberichten worden verzonden naar bepaalde locaties om u te waarschjeen voor noodsituaties. Schadelijke apps kunnen de prestaties of verwerking van je apparaat verstoren wanneer een infodienstbericht wordt ontvangen."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"Geabonneerde feeds lezen"</string>
@@ -491,9 +493,9 @@
<string name="permdesc_changeWifiMulticastState" product="tv" msgid="9115646511110555589">"Hiermee kan de app pakketten ontvangen die via multicastadressen naar alle apparaten in een wifi-netwerk worden verzonden, niet alleen naar je Android TV-apparaat. Het stroomgebruik ligt hierbij hoger dan in de niet-multicastmodus."</string>
<string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"Hiermee kan de app pakketten ontvangen die via multicastadressen naar alle apparaten in een wifi-netwerk worden verzonden, niet alleen naar je telefoon. Het stroomgebruik ligt hierbij hoger dan in de niet-multicastmodus."</string>
<string name="permlab_bluetoothAdmin" msgid="6006967373935926659">"Bluetooth-instellingen openen"</string>
- <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"Hiermee kan de app de lokale Bluetooth-tablet configureren en externe apparaten zoeken en koppelen."</string>
+ <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"Hiermee kan de app de lokale bluetooth-tablet configureren en externe apparaten zoeken en koppelen."</string>
<string name="permdesc_bluetoothAdmin" product="tv" msgid="3174333400857321862">"Hiermee kan de app Bluetooth op je Android TV-apparaat configureren en externe apparaten zoeken en koppelen."</string>
- <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Hiermee kan de app de lokale Bluetooth-telefoon configureren en externe apparaten zoeken en koppelen."</string>
+ <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Hiermee kan de app de lokale bluetooth-telefoon configureren en externe apparaten zoeken en koppelen."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"WiMAX-verbinding maken en verbreken"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Hiermee kan de app bepalen of WiMAX is ingeschakeld en informatie bekijken over alle WiMAX-netwerken waarmee verbinding is gemaakt."</string>
<string name="permlab_changeWimaxState" msgid="340465839241528618">"WiMAX-status wijzigen"</string>
@@ -1223,10 +1225,10 @@
<string name="sendText" msgid="5209874571959469142">"Een actie voor tekst selecteren"</string>
<string name="volume_ringtone" msgid="6885421406845734650">"Ringtonevolume"</string>
<string name="volume_music" msgid="5421651157138628171">"Mediavolume"</string>
- <string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Afspelen via Bluetooth"</string>
+ <string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Afspelen via bluetooth"</string>
<string name="volume_music_hint_silent_ringtone_selected" msgid="8310739960973156272">"Stille ringtone ingesteld"</string>
<string name="volume_call" msgid="3941680041282788711">"Volume inkomend gesprek"</string>
- <string name="volume_bluetooth_call" msgid="2002891926351151534">"Volume tijdens gesprek in Bluetooth-modus"</string>
+ <string name="volume_bluetooth_call" msgid="2002891926351151534">"Volume tijdens gesprek in bluetooth-modus"</string>
<string name="volume_alarm" msgid="1985191616042689100">"Wekkervolume"</string>
<string name="volume_notification" msgid="2422265656744276715">"Meldingsvolume"</string>
<string name="volume_unknown" msgid="1400219669770445902">"Volume"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Selecteer deze optie om USB-foutopsporing uit te schakelen."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Test harness-modus is ingeschakeld"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Reset de fabrieksinstellingen om de test harness-modus uit te schakelen."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Seriële console ingeschakeld"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Dit is van invloed op de prestaties. Controleer de bootloader om dit uit te schakelen."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Vloeistof of vuil in USB-poort"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB-poort is automatisch uitgeschakeld. Tik voor meer informatie."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB-poort kan worden gebruikt"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Geen categorie"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Je stelt het belang van deze meldingen in."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Dit is belangrijk vanwege de betrokken mensen."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Toestaan dat <xliff:g id="APP">%1$s</xliff:g> een nieuwe gebruiker met <xliff:g id="ACCOUNT">%2$s</xliff:g> maakt (er is al een gebruiker met dit account)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Toestaan dat <xliff:g id="APP">%1$s</xliff:g> een nieuwe gebruiker met <xliff:g id="ACCOUNT">%2$s</xliff:g> maakt?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Een taal toevoegen"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Regiovoorkeur"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Typ een taalnaam"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index bd6d2cc8f635..ac85a401da17 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -324,8 +324,8 @@
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ଡିସପ୍ଲେର ଜୁମ୍‍ ସ୍ତର ଓ ପୋଜିସନିଙ୍ଗ ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ।"</string>
<string name="capability_title_canPerformGestures" msgid="7418984730362576862">"ଜେଶ୍ଚର୍‍ କରନ୍ତୁ"</string>
<string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"ଟାପ୍‍, ସ୍ୱାଇପ୍‍, ପିଞ୍ଚ ଓ ଅନ୍ୟାନ୍ୟ ଜେଶ୍ଚର୍‍ ସମ୍ପାଦନ କରିପାରିବ।"</string>
- <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ଜେଶ୍ଚର"</string>
- <string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"ଡିଭାଇସ୍‌ର ଆଙ୍ଗୁଠି ଚିହ୍ନ ସେନସର୍ ଉପରେ ଜେଶ୍ଚର୍‍ କ୍ୟାପଚର୍‍ କାର୍ଯ୍ୟ କରାଯାଇପାରିବ।"</string>
+ <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"ଟିପଚିହ୍ନ ଜେଶ୍ଚର"</string>
+ <string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"ଡିଭାଇସ୍‌ର ଟିପଚିହ୍ନ ସେନସର୍ ଉପରେ ଜେଶ୍ଚର୍‍ କ୍ୟାପଚର୍‍ କାର୍ଯ୍ୟ କରାଯାଇପାରିବ।"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"ଷ୍ଟାଟସ୍‌ ବାର୍‌କୁ ଅକ୍ଷମ କିମ୍ୱା ସଂଶୋଧନ କରନ୍ତୁ"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ଆପ୍‍କୁ, ସ୍ଥିତି ବାର୍‍ ଅକ୍ଷମ କରିବାକୁ କିମ୍ବା ସିଷ୍ଟମ୍‍ ଆଇକନ୍‍ ଯୋଡ଼ିବା କିମ୍ବା ବାହାର କରିବାକୁ ଦେଇଥାଏ।"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ଷ୍ଟାଟସ୍‍ ବାର୍‍ ରହିବାକୁ ଦିଅନ୍ତୁ"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"SMS ମେସେଜ୍‌ ପ୍ରାପ୍ତ କରିବାକୁ ତଥା ପ୍ରକ୍ରିୟା କରାଇବାକୁ ଆପ୍‍ଟିକୁ ଅନୁମତି ଦିଏ। ଏହାର ଅର୍ଥ ହେଉଛି, ଆପଣଙ୍କ ଡିଭାଇସ୍‍କୁ ପଠାଯାଇଥିବା ମେସେଜ୍‍ ଆପଣଙ୍କୁ ନଦେଖାଇ ଆପ୍‍ଟି ମନିଟର୍‍ କିମ୍ବା ଡିଲିଟ୍‍ କରିପାରେ।"</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"ଟେକ୍ସଟ୍‍ ମେସେଜ୍‍ (MMS) ପ୍ରାପ୍ତ କରନ୍ତୁ"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"MMS ମେସେଜ୍‌ ପ୍ରାପ୍ତ କରିବାକୁ ତଥା ପ୍ରକ୍ରିୟା କରାଇବାକୁ ଆପ୍‍ଟିକୁ ଅନୁମତି ଦିଏ। ଏହାର ଅର୍ଥ, ଆପଣଙ୍କ ଡିଭାଇସ୍‍କୁ ପଠାଯାଇଥିବା ମେସେଜ୍‍ ଆପଣଙ୍କୁ ନଦେଖାଇ ଆପ୍‍ଟି ମନିଟର୍‍ କିମ୍ବା ଡିଲିଟ୍‍ କରିପାରେ।"</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"ସେଲ୍ ପ୍ରସାରଣ ମେସେଜ୍ ଫର୍‍ୱାର୍ଡ କରନ୍ତୁ"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"ସେଲ୍ ପ୍ରସାରଣ ମେସେଜ୍ ପ୍ରାପ୍ତ ହେବା ପରେ ସେଗୁଡ଼ିକୁ ଫର୍‍ୱାର୍ଡ କରିବା ପାଇଁ ଆପ୍‍କୁ ସେଲ୍ ପ୍ରସାରଣ ମଡ୍ୟୁଲ୍ ସହିତ ସଂଯୁକ୍ତ କରିବାକୁ ଅନୁମତି ଦିଏ। ଜରୁରୀକାଳୀନ ପରିସ୍ଥିତିରେ ଆପଣଙ୍କୁ ଚେତାବନୀ ଦେବା ପାଇଁ କିଛି ଲୋକେସନ୍‍‍ରେ ସେଲ୍ ପ୍ରସାରଣ ଆଲର୍ଟ ବିତରଣ କରାଯାଇଥାଏ। ଏକ ଜରୁରୀକାଳୀନ ସେଲ୍ ପ୍ରସାରଣ ପ୍ରାପ୍ତ ହେବା ସମୟରେ କିଛି କ୍ଷତିକାରକ ଆପ୍ସ ହୁଏତ ଆପଣଙ୍କର ଡିଭାଇସ୍‍ର କାର୍ଯ୍ୟଦକ୍ଷତା କିମ୍ବା କାର୍ଯ୍ୟ ପ୍ରକ୍ରିୟାରେ ହସ୍ତକ୍ଷେପ କରିପାରେ।"</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"ସେଲ୍‍ ବ୍ରଡ୍‍କାଷ୍ଟ ମେସେଜ୍‍ ପଢ଼ନ୍ତୁ"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"ଆପଣଙ୍କ ଡିଭାଇସ୍‍ରେ ପ୍ରାପ୍ତ ହୋଇଥିବା ସେଲ୍‍ ବ୍ରଡ୍‍କାଷ୍ଟ ମେସେଜ୍‍ ପଢିବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦିଏ। ଜରୁରୀକାଳୀନ ଅବସ୍ଥା ବିଷୟରେ ଆପଣଙ୍କୁ ସତର୍କ କରାଇବାକୁ କିଛି ଲୋକେଶନ୍‍ରେ ସେଲ୍‍ ବ୍ରଡ୍‍କାଷ୍ଟ ସତର୍କ ଡେଲିଭର୍ କରାଯାଇଥାଏ। ଏକ ଜରୁରୀକାଳୀନ ସେଲ୍‍ ବ୍ରଡ୍‍କାଷ୍ଟ ପ୍ରାପ୍ତ ହେବାପରେ ହାନୀକାରକ ଆପ୍‍ ଆପଣଙ୍କ ଡିଭାଇସ୍‍ର କାର୍ଯ୍ୟକ୍ଷମତା କିମ୍ବା ସଞ୍ଚାଳନାରେ ବାଧା ପହଞ୍ଚାଇପାରନ୍ତି।"</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"ସବସ୍କ୍ରାଇବ୍ ହୋଇଥିବା ଫୀଡ୍‌କୁ ପଢ଼ନ୍ତୁ"</string>
@@ -512,10 +514,10 @@
<string name="permdesc_requestPasswordComplexity" msgid="4730994229754212347">"ସ୍କ୍ରିନ୍ ଲକ୍‌ର ଜଟିଳତା ସ୍ତର (ଉଚ୍ଚ, ମଧ୍ୟମ, ନିମ୍ନ କିମ୍ବା କିଛିନୁହେଁ), ଜାଣିବାକୁ ଆପ୍‌କୁ ଅନୁମତି ଦିଅନ୍ତୁ, ଯାହା ସ୍କ୍ରିନ୍ ଲକ୍‌ର ସମ୍ଭାବ୍ୟ ପରିସୀମାର ଲମ୍ବ ଏବଂ ପ୍ରକାର ସୂଚୀତ କରେ। ଆପ୍ ଏହା ମଧ୍ୟ ଉପଯୋଗକର୍ତ୍ତାମାନଙ୍କୁ ପରାମର୍ଶ ଦେଇପାରେ ଯେ ସେମାନେ ସ୍କ୍ରିନ୍ ଲକ୍‌କୁ ଏକ ନିର୍ଦ୍ଧିଷ୍ଟ ସ୍ତର ପର୍ଯ୍ୟନ୍ତ ଅପ୍‌ଡେଟ୍ କରିପାରନ୍ତି, କିନ୍ତୁ ଉପଯୋଗକର୍ତ୍ତାମାନେ ନିଜ ଇଚ୍ଛାରେ ଏହାକୁ ଉପେକ୍ଷା ଏବଂ ନାଭିଗେଟ୍ କରିପାରିବେ। ଧ୍ୟାନ ଦିଅନ୍ତୁ ଯେ, ସ୍କ୍ରିନ୍ ଲକ୍ ସରଳ ଟେକ୍ସଟ୍‌ରେ ଷ୍ଟୋର୍ କରାଯାଇନଥାଏ ତେଣୁ ଆପ୍ ସଠିକ୍ ପାସ୍‌‍ୱର୍ଡ ଜାଣିପାରି ନଥାଏ।"</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"ବାୟୋମେଟ୍ରିକ୍‌ ହାର୍ଡୱେର୍‌ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"ସ୍ୱୀକୃତି ପାଇଁ ବାୟୋମେଟ୍ରିକ୍‌ ହାର୍ଡୱେର୍‌ ବ୍ୟବହାର କରିବାକୁ ଅନୁମତି ଦିଏ"</string>
- <string name="permlab_manageFingerprint" msgid="5640858826254575638">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ହାର୍ଡୱେର୍‍ ପରିଚାଳନା କରନ୍ତୁ"</string>
- <string name="permdesc_manageFingerprint" msgid="178208705828055464">"ବ୍ୟବହାର କରିବା ପାଇଁ ଆଙ୍ଗୁଠି ଚିହ୍ନ ଯୋଡ଼ିବାକୁ ଓ ଡିଲିଟ୍‍ କରିବାକୁ ଆପକୁ ବିଧି ଆରମ୍ଭ କରିବାକୁ ଦେଇଥାଏ।"</string>
- <string name="permlab_useFingerprint" msgid="3150478619915124905">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ହାର୍ଡୱେର୍‍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
- <string name="permdesc_useFingerprint" msgid="9165097460730684114">"ସ୍ୱୀକୃତି ପାଇଁ ଆଙ୍ଗୁଠି ଚିହ୍ନ ହାର୍ଡୱେର୍‍ ବ୍ୟବହାର କରିବାକୁ ଅନୁମତି ଦିଏ"</string>
+ <string name="permlab_manageFingerprint" msgid="5640858826254575638">"ଟିପଚିହ୍ନ ହାର୍ଡୱେର୍‍ ପରିଚାଳନା କରନ୍ତୁ"</string>
+ <string name="permdesc_manageFingerprint" msgid="178208705828055464">"ବ୍ୟବହାର କରିବା ପାଇଁ ଟିପଚିହ୍ନ ଟେମ୍ପ୍ଲେଟ୍ ଯୋଡ଼ିବାକୁ ଓ ଡିଲିଟ୍‍ କରିବାକୁ ଆପକୁ ପ୍ରକ୍ରିୟା ଆରମ୍ଭ କରିବାକୁ ଦେଇଥାଏ।"</string>
+ <string name="permlab_useFingerprint" msgid="3150478619915124905">"ଟିପଚିହ୍ନ ହାର୍ଡୱେର୍‍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+ <string name="permdesc_useFingerprint" msgid="9165097460730684114">"ପ୍ରମାଣିକରଣ ପାଇଁ ଟିପଚିହ୍ନ ହାର୍ଡୱେର୍‍ ବ୍ୟବହାର କରିବାକୁ ଅନୁମତି ଦିଏ"</string>
<string name="permlab_audioWrite" msgid="2661772059799779292">"ଆପଣଙ୍କ ସଙ୍ଗୀତ ସଂଗ୍ରହ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
<string name="permdesc_audioWrite" msgid="8888544708166230494">"ଆପଣ ଆପଣଙ୍କ ସଙ୍ଗୀତ ସଂଗ୍ରହ ପରିବର୍ତ୍ତନ କରିବାକୁ ଆପ୍‍କୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
<string name="permlab_videoWrite" msgid="128769316366746446">"ଆପଣଙ୍କ ଭିଡିଓ ସଂଗ୍ରହ ସଂଶୋଧନ କରନ୍ତୁ"</string>
@@ -530,30 +532,30 @@
<string name="biometric_not_recognized" msgid="5770511773560736082">"ଚିହ୍ନଟ ହେଲାନାହିଁ"</string>
<string name="biometric_error_canceled" msgid="349665227864885880">"ପ୍ରାମାଣିକତାକୁ ବାତିଲ୍ କରାଯାଇଛି"</string>
<string name="biometric_error_device_not_secured" msgid="6583143098363528349">"କୌଣସି ପିନ୍, ପେଟେର୍ନ ବା ପାସ୍‍ୱର୍ଡ ସେଟ୍ ନାହିଁ"</string>
- <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ଆଂଶିକ ଚିହ୍ନଟ ହେଲା। ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ଆଂଶିକ ଟିପଚିହ୍ନ ଚିହ୍ନଟ ହେଲା। ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ଟିପଚିହ୍ନ ପ୍ରୋସେସ୍‍ କରାଯାଇପାରିଲା ନାହିଁ। ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ଟିପଚିହ୍ନ ସେନ୍ସର୍‍ ମଇଳା ହୋଇଯାଇଛି। ଦୟାକରି ସଫା କରନ୍ତୁ ଓ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"ଆଙ୍ଗୁଠି ବହୁତ ଜୋର୍‌ରେ ଚଲାଗଲା। ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"ଆଙ୍ଗୁଠି ଖୁବ୍‍ ଧୀରେ ନିଆଗଲା। ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_authenticated" msgid="5309333983002526448">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ପ୍ରମାଣୀକୃତ ହେଲା"</string>
+ <string name="fingerprint_authenticated" msgid="5309333983002526448">"ଟିପଚିହ୍ନ ପ୍ରମାଣିତ ହେଲା"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"ମୁହଁ ଚିହ୍ନଟ ହୋଇଛି"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"ମୁହଁ ଚିହ୍ନଟ ହୋଇଛି, ଦୟାକରି ସୁନିଶ୍ଚିତ ଦବାନ୍ତୁ"</string>
- <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ହାର୍ଡୱେର୍‍ ଉପଲବ୍ଧ ନାହିଁ।"</string>
- <string name="fingerprint_error_no_space" msgid="1055819001126053318">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ଷ୍ଟୋର୍‍ କରାଯାଇପାରିବ ନାହିଁ। ଦୟାକରି ପୂର୍ବରୁ ଥିବା ଆଙ୍ଗୁଠି ଚିହ୍ନକୁ ବାହାର କରିଦିଅନ୍ତୁ।"</string>
- <string name="fingerprint_error_timeout" msgid="3927186043737732875">"ଆଙ୍ଗୁଠି ଚିହ୍ନର ସମୟ ଶେଷ ହେଲା । ପୁଣିଥରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
- <string name="fingerprint_error_canceled" msgid="4402024612660774395">"ଆଙ୍ଗୁଠି ଚିହ୍ନ କାର୍ଯ୍ୟ କ୍ୟାନ୍ସଲ୍‍ କରାଗଲା।"</string>
- <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"ୟୁଜର୍‌ଙ୍କ ଦ୍ଵାରା ଆଙ୍ଗୁଠି ଚିହ୍ନ ନେବା କାମକୁ କ୍ୟାନ୍ସଲ୍ କରିଦିଆଯାଇଛି।"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ଟିପଚିହ୍ନ ହାର୍ଡୱେର୍‍ ଉପଲବ୍ଧ ନାହିଁ।"</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"ଟିପଚିହ୍ନ ଷ୍ଟୋର୍‍ କରାଯାଇପାରିବ ନାହିଁ। ଦୟାକରି ପୂର୍ବରୁ ଥିବା ଟିପଚିହ୍ନକୁ କାଢ଼ି ଦିଅନ୍ତୁ।"</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"ଟିପଚିହ୍ନର ସମୟ ଶେଷ ହେଲା । ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+ <string name="fingerprint_error_canceled" msgid="4402024612660774395">"ଟିପଚିହ୍ନ କାର୍ଯ୍ୟ ବାତିଲ୍ କରାଗଲା।"</string>
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"ଉପଯୋଗକର୍ତ୍ତା ଟିପଚିହ୍ନ କାର୍ଯ୍ୟ ବାତିଲ୍ କରିଛନ୍ତି।"</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"ବହୁତ ପ୍ରୟାସ କରାଗଲା। ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
- <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"ବହୁତ ଥର ଭୁଲ ପ୍ରୟାସ କରିଛନ୍ତି। ଆଙ୍ଗୁଠି ଚିହ୍ନ ସେନ୍ସର୍‍ ଅକ୍ଷମ କରାଗଲା।"</string>
+ <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"ବହୁଥର ପ୍ରୟାସ କରିଛନ୍ତି। ଟିପଚିହ୍ନ ସେନ୍ସର୍‍ ଅକ୍ଷମ କରାଯାଇଛି।"</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="fingerprint_error_no_fingerprints" msgid="7654382120628334248">"କୌଣସି ଆଙ୍ଗୁଠି ଚିହ୍ନ ପଞ୍ଜୀକୃତ ହୋଇନାହିଁ।"</string>
<string name="fingerprint_error_hw_not_present" msgid="409523969613176352">"ଏହି ଡିଭାଇସ୍‌ରେ ଟିପଚିହ୍ନ ସେନ୍‍ସର୍ ନାହିଁ।"</string>
<string name="fingerprint_name_template" msgid="5870957565512716938">"ଆଙ୍ଗୁଠି <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
- <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ଆଇକନ୍"</string>
+ <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ଟିପଚିହ୍ନ ଆଇକନ୍"</string>
<string name="permlab_manageFace" msgid="7262837876352591553">"ଫେସ୍ ଅନ୍‌ଲକ୍ ହାର୍ଡୱେର୍ ପରିଚାଳନା କରନ୍ତୁ"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"ବ୍ୟବହାର ପାଇଁ ଆପ୍‍କୁ ଫେସିଆଲ୍‍ ଟେମ୍ପଲେଟ୍‍ ଯୋଡିବା ଓ ଡିଲିଟ୍‍ ର ପଦ୍ଧତି ପାଇଁ ଅନୁମତି ଦିଅନ୍ତୁ।"</string>
<string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"ଫେସ୍ ଅନ୍‌ଲକ୍ ହାର୍ଡୱେର୍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB ଡିବଗିଙ୍ଗକୁ ଅକ୍ଷମ କରିବା ପାଇଁ ଚୟନ କରନ୍ତୁ।"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"ଟେଷ୍ଟ ହାର୍ନେସ୍ ମୋଡ୍ ସକ୍ଷମ ଅଛି"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"ଟେଷ୍ଟ ହାର୍‌ନେସ୍ ମୋଡ୍ ଅକ୍ଷମ କରିବାକୁ ଏକ ଫ୍ୟାକ୍ଟରୀ ରିସେଟ୍ କରନ୍ତୁ।"</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"କ୍ରମିକ କନ୍‍‍ସୋଲ୍‍କୁ ସକ୍ଷମ କରାଯାଇଛି"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"କାର୍ଯ୍ୟଦକ୍ଷତା ପ୍ରଭାବିତ ହୋଇଛି। ଅକ୍ଷମ କରିବା ପାଇଁ, ବୁଟ୍‌ଲୋଡର୍‍ର ଯାଞ୍ଚ କରନ୍ତୁ।"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB ପୋର୍ଟରେ ତରଳ ପଦାର୍ଥ ବା ଧୂଳି"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB ପୋର୍ଟ ସ୍ୱଚାଳିତ ଭାବେ ଅକ୍ଷମ ହୋଇଛି। ଅଧିକ ଜାଣିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ।"</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB ପୋର୍ଟ ବ୍ୟବହାର କରିବା ପାଇଁ ଠିକ୍ ଅଟେ"</string>
@@ -1580,7 +1584,7 @@
<string name="serial_number" msgid="758814067660862493">"କ୍ରମିକ ସଂଖ୍ୟା:"</string>
<string name="fingerprints" msgid="4516019619850763049">"ଆଙ୍ଗୁଠି ଚିହ୍ନ:"</string>
<string name="sha256_fingerprint" msgid="4391271286477279263">"SHA-256 ଆଙ୍ଗୁଠି ଚିହ୍ନ:"</string>
- <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 ଆଙ୍ଗୁଠି ଚିହ୍ନ:"</string>
+ <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 ଟିପଚିହ୍ନ:"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"ସମସ୍ତ ଦେଖନ୍ତୁ"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"ଗତିବିଧି ଚୟନ କରନ୍ତୁ"</string>
<string name="share_action_provider_share_with" msgid="5247684435979149216">"ଏହାଙ୍କ ସହ ସେୟାର୍‍ କରନ୍ତୁ"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"ଅବର୍ଗୀକୃତ"</string>
<string name="importance_from_user" msgid="7318955817386549931">"ଏହି ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ପ୍ରମୁଖତା ଆପଣ ସେଟ୍‍ କରନ୍ତି।"</string>
<string name="importance_from_person" msgid="9160133597262938296">"ସମ୍ପୃକ୍ତ ଲୋକଙ୍କ କାରଣରୁ ଏହା ଗୁରୁତ୍ୱପୂର୍ଣ୍ଣ ଅଟେ।"</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="APP">%1$s</xliff:g>ରେ ଏକ ନୂଆ ଉପଯୋଗକର୍ତ୍ତା ତିଆରି କରିବା ପାଇଁ <xliff:g id="ACCOUNT">%2$s</xliff:g>କୁ (ପୂର୍ବରୁ ଏହି ଆକାଉଣ୍ଟ ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ନାମରେ ଅଛି) ଅନୁମତି ଦେବେ?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"<xliff:g id="APP">%1$s</xliff:g>ରେ ଏକ ନୂଆ ଉପଯୋଗକର୍ତ୍ତା ତିଆରି କରିବା ପାଇଁ <xliff:g id="ACCOUNT">%2$s</xliff:g>କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"ଏକ ଭାଷା ଯୋଡ଼ନ୍ତୁ"</string>
<string name="country_selection_title" msgid="2954859441620215513">"ପସନ୍ଦର ଅଞ୍ଚଳ"</string>
<string name="search_language_hint" msgid="7042102592055108574">"ଭାଷାର ନାମ ଟାଇପ୍‍ କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index a9e93d485bfc..177c7bce4001 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"ਐਪ ਨੂੰ SMS ਸੁਨੇਹੇ ਪ੍ਰਾਪਤ ਕਰਨ ਅਤੇ ਉਹਨਾਂ ਦੀ ਪ੍ਰਕਿਰਿਆ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਸਦਾ ਮਤਲਬ ਹੈ ਕਿ ਐਪ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਤੇ ਭੇਜੇ ਗਏ ਸੁਨੇਹਿਆਂ ਨੂੰ ਤੁਹਾਨੂੰ ਦਿਖਾਏ ਬਿਨਾਂ ਨਿਰੀਖਣ ਕਰ ਸਕਦੀ ਹੈ ਜਾਂ ਮਿਟਾ ਸਕਦੀ ਹੈ।"</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"ਟੈਕਸਟ ਸੁਨੇਹੇ (MMS) ਪੜ੍ਹੋ"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"ਐਪ ਨੂੰ MMS ਸੁਨੇਹੇ ਪ੍ਰਾਪਤ ਕਰਨ ਅਤੇ ਉਹਨਾਂ ਦੀ ਪ੍ਰਕਿਰਿਆ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਸਦਾ ਮਤਲਬ ਹੈ ਕਿ ਐਪ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਤੇ ਭੇਜੇ ਗਏ ਸੁਨੇਹਿਆਂ ਨੂੰ ਤੁਹਾਨੂੰ ਦਿਖਾਏ ਬਿਨਾਂ ਨਿਰੀਖਣ ਕਰ ਸਕਦੀ ਹੈ ਜਾਂ ਮਿਟਾ ਸਕਦੀ ਹੈ।"</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"ਸੈੱਲ ਪ੍ਰਸਾਰਨ ਸੁਨੇਹਿਆਂ ਨੂੰ ਅੱਗੇ ਭੇਜੋ"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"ਐਪ ਨੂੰ ਸੈੱਲ ਪ੍ਰਸਾਰਨ ਸੁਨੇਹਿਆਂ ਦੇ ਪ੍ਰਾਪਤ ਹੁੰਦੇ ਹੀ ਉਹਨਾਂ ਨੂੰ ਅੱਗੇ ਭੇਜਣ ਲਈ ਸੈੱਲ ਪ੍ਰਸਾਰਨ ਮਾਡਿਊਲ ਨਾਲ ਜੋੜਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿੱਤੀ ਜਾਂਦੀ ਹੈ। ਸੈੱਲ ਪ੍ਰਸਾਰਨ ਸੁਚੇਤਨਾਵਾਂ ਤੁਹਾਨੂੰ ਸੰਕਟਕਾਲੀ ਸਥਿਤੀਆਂ ਦੀ ਚਿਤਾਵਨੀ ਦੇਣ ਲਈ ਕੁਝ ਟਿਕਾਣਿਆਂ \'ਤੇ ਪ੍ਰਦਾਨ ਕੀਤੀਆਂ ਜਾਂਦੀਆਂ ਹਨ। ਭੈੜੀਆਂ ਐਪਾਂ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੀ ਕਾਰਗੁਜ਼ਾਰੀ ਜਾਂ ਓਪਰੇਸ਼ਨ ਵਿੱਚ ਵਿਘਨ ਪਾ ਸਕਦੀਆਂ ਹਨ ਜਦੋਂ ਇੱਕ ਸੰਕਟਕਾਲੀ ਸੈੱਲ ਪ੍ਰਸਾਰਨ ਪ੍ਰਾਪਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ।"</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"ਸੈਲ ਪ੍ਰਸਾਰਨ ਸੁਨੇਹੇ ਪੜ੍ਹੋ"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"ਐਪ ਨੂੰ ਤੁਹਾਡੀ ਡੀਵਾਈਸ ਵੱਲੋਂ ਪ੍ਰਾਪਤ ਕੀਤੇ ਸੈੱਲ ਪ੍ਰਸਾਰਣ ਸੁਨੇਹੇ ਪੜ੍ਹਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਸੈੱਲ ਪ੍ਰਸਾਰਣ ਚਿਤਾਵਨੀਆਂ ਤੁਹਾਨੂੰ ਸੰਕਟਕਾਲੀਨ ਸਥਿਤੀਆਂ ਦੀ ਚਿਤਾਵਨੀ ਦੇਣ ਲਈ ਕੁਝ ਨਿਰਧਾਰਤ ਟਿਕਾਣਿਆਂ ਤੇ ਪ੍ਰਦਾਨ ਕੀਤੀਆਂ ਜਾਂਦੀਆਂ ਹਨ। ਖਰਾਬ ਐਪਾਂ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੇ ਪ੍ਰਦਰਸ਼ਨ ਜਾਂ ਓਪਰੇਸ਼ਨ ਵਿੱਚ ਵਿਘਨ ਪਾ ਸਕਦੀਆਂ ਹਨ ਜਦੋਂ ਇੱਕ ਸੰਕਟਕਾਲੀਨ ਸੈੱਲ ਪ੍ਰਸਾਰਣ ਪ੍ਰਾਪਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ।"</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"ਸਬਸਕ੍ਰਾਈਬ ਕੀਤੇ ਫੀਡਸ ਪੜ੍ਹੋ"</string>
@@ -540,10 +542,10 @@
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਪ੍ਰਮਾਣਿਤ ਹੋਇਆ"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"ਚਿਹਰਾ ਪੁਸ਼ਟੀਕਰਨ"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"ਚਿਹਰਾ ਪੁਸ਼ਟੀਕਰਨ, ਕਿਰਪਾ ਕਰਕੇ \'ਪੁਸ਼ਟੀ ਕਰੋ\' ਦਬਾਓ"</string>
- <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਹਾਰਡਵੇਅਰ ਉਪਲਬਧ ਨਹੀਂ।"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਹਾਰਡਵੇਅਰ ਉਪਲਬਧ ਨਹੀਂ ਹੈ।"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸਟੋਰ ਨਹੀਂ ਕੀਤਾ ਸਕਦਾ। ਕਿਰਪਾ ਕਰਕੇ ਇੱਕ ਮੌਜੂਦਾ ਫਿੰਗਰਪ੍ਰਿੰਟ ਹਟਾਓ।"</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਸਮਾਂ ਸਮਾਪਤ ਹੋ ਗਿਆ ਹੈ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
- <string name="fingerprint_error_canceled" msgid="4402024612660774395">"ਫਿੰਗਰ"</string>
+ <string name="fingerprint_error_canceled" msgid="4402024612660774395">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਓਪਰੇਸ਼ਨ ਰੱਦ ਕੀਤਾ ਗਿਆ।"</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਓਪਰੇਸ਼ਨ ਵਰਤੋਂਕਾਰ ਵੱਲੋਂ ਰੱਦ ਕੀਤਾ ਗਿਆ।"</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"ਬਹੁਤ ਸਾਰੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ. ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"ਹੱਦੋਂ ਵੱਧ ਕੋਸ਼ਿਸ਼ਾਂ। ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ।"</string>
@@ -1362,6 +1364,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB ਡੀਬੱਗਿੰਗ ਅਯੋਗ ਬਣਾਉਣ ਲਈ ਚੁਣੋ।"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"ਟੈਸਟ ਹਾਰਨੈੱਸ ਮੋਡ ਚਾਲੂ ਹੈ"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"ਟੈਸਟ ਹਾਰਨੈੱਸ ਮੋਡ ਬੰਦ ਕਰਨ ਲਈ ਫੈਕਟਰੀ ਰੀਸੈੱਟ ਕਰੋ।"</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"ਸੀਰੀਅਲ ਕੰਸੋਲ ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"ਕਾਰਗੁਜ਼ਾਰੀ ਪ੍ਰਭਾਵਿਤ ਹੋਈ ਹੈ। ਬੰਦ ਕਰਨ ਲਈ, ਬੂਟਲੋਡਰ ਦੇਖੋ।"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB ਪੋਰਟ ਵਿੱਚ ਪਾਣੀ ਜਾਂ ਧੂੜ-ਮਿੱਟੀ"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB ਪੋਰਟ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਬੰਦ ਕੀਤਾ ਗਿਆ। ਹੋਰ ਜਾਣਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB ਪੋਰਟ ਵਰਤਣ ਲਈ ਠੀਕ ਹੈ"</string>
@@ -1891,10 +1895,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"ਗੈਰ-ਸ਼੍ਰੇਣੀਕਿਰਤ"</string>
<string name="importance_from_user" msgid="7318955817386549931">"ਤੁਸੀਂ ਇਹਨਾਂ ਸੂਚਨਾਵਾਂ ਦੀ ਮਹੱਤਤਾ ਸੈੱਟ ਕੀਤੀ।"</string>
<string name="importance_from_person" msgid="9160133597262938296">"ਇਹ ਸ਼ਾਮਲ ਲੋਕਾਂ ਦੇ ਕਾਰਨ ਮਹੱਤਵਪੂਰਨ ਹੈ।"</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"ਕੀ <xliff:g id="APP">%1$s</xliff:g> ਨੂੰ <xliff:g id="ACCOUNT">%2$s</xliff:g> ਨਾਲ ਨਵਾਂ ਵਰਤੋਂਕਾਰ ਬਣਾਉਣ ਦੀ ਇਜਾਜ਼ਤ ਦੇਣੀ ਹੈ (ਇਸ ਖਾਤੇ ਨਾਲ ਇੱਕ ਵਰਤੋਂਕਾਰ ਪਹਿਲਾਂ ਤੋਂ ਹੀ ਮੌਜੂਦ ਹੈ)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"ਕੀ <xliff:g id="APP">%1$s</xliff:g> ਨੂੰ <xliff:g id="ACCOUNT">%2$s</xliff:g> ਨਾਲ ਨਵਾਂ ਵਰਤੋਂਕਾਰ ਬਣਾਉਣ ਦੀ ਇਜਾਜ਼ਤ ਦੇਣੀ ਹੈ?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"ਇੱਕ ਭਾਸ਼ਾ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="country_selection_title" msgid="2954859441620215513">"ਖੇਤਰ ਤਰਜੀਹ"</string>
<string name="search_language_hint" msgid="7042102592055108574">"ਭਾਸ਼ਾ ਨਾਮ ਟਾਈਪ ਕਰੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 537089ce640f..1f2430cde8b5 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -234,7 +234,7 @@
<string name="global_action_bug_report" msgid="7934010578922304799">"Zgłoszenie błędu"</string>
<string name="global_action_logout" msgid="935179188218826050">"Zakończ sesję"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Zrzut ekranu"</string>
- <string name="bugreport_title" msgid="5981047024855257269">"Zgłoszenie błędu"</string>
+ <string name="bugreport_title" msgid="5981047024855257269">"Zgłoś błąd"</string>
<string name="bugreport_message" msgid="398447048750350456">"Informacje o bieżącym stanie urządzenia zostaną zebrane i wysłane e-mailem. Przygotowanie zgłoszenia błędu do wysłania chwilę potrwa, więc zachowaj cierpliwość."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Raport interaktywny"</string>
<string name="bugreport_option_interactive_summary" msgid="229299488536107968">"Używaj tej opcji w większości przypadków. Umożliwia śledzenie postępów raportu, podanie dodatkowych szczegółów problemu i wykonanie zrzutów ekranu. Raport może pomijać niektóre rzadko używane sekcje, których utworzenie zajmuje dużo czasu."</string>
@@ -350,6 +350,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Pozwala aplikacji na odbieranie i przetwarzanie SMS-ów. To oznacza, że aplikacja będzie mogła bez Twojej wiedzy monitorować i usuwać wiadomości wysyłane do Twojego urządzenia."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"odbieranie wiadomości tekstowych (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Pozwala aplikacji na odbieranie i przetwarzanie MMS-ów. To oznacza, że aplikacja będzie mogła bez Twojej wiedzy monitorować i usuwać wiadomości wysyłane do Twojego urządzenia."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Przekaż komunikaty z sieci komórkowej"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Zezwala aplikacji powiązać się z modułem komunikatów z sieci komórkowej, aby przekazywać je w momencie, w którym są otrzymywane. W niektórych lokalizacjach komunikaty alarmowe z sieci komórkowej są dostarczane, aby ostrzec Cię o sytuacjach zagrożenia. Złośliwe aplikacje mogą wpływać na działanie urządzenia lub zakłócać je po nadejściu komunikatu alarmowego z sieci komórkowej."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"odczyt komunikatów z sieci komórkowej"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Zezwala aplikacji na odczyt komunikatów z sieci komórkowej odebranych na urządzeniu. Komunikaty alarmowe z sieci komórkowej są dostarczane w niektórych lokalizacjach w celu ostrzeżenia Cię o sytuacjach zagrożenia. Złośliwe aplikacje mogą wpływać na wydajność lub zakłócać działanie urządzenia po odebraniu komunikatu alarmowego z sieci komórkowej."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"czytanie subskrybowanych źródeł"</string>
@@ -548,7 +550,7 @@
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Twarz rozpoznana, kliknij Potwierdź"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Czytnik linii papilarnych nie jest dostępny."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Nie można zapisać odcisku palca. Usuń istniejący odcisk palca."</string>
- <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Osiągnięto limit czasu odczytu linii papilarnych. Spróbuj ponownie."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Osiągnięto limit czasu odczytu odcisków palców. Spróbuj ponownie."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Odczyt odcisku palca został anulowany."</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Odczyt odcisku palca został anulowany przez użytkownika."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Zbyt wiele prób. Spróbuj ponownie później."</string>
@@ -1405,6 +1407,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Wybierz, aby wyłączyć debugowanie USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Tryb jarzma testowego został włączony"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Przywróć ustawienia fabryczne, by wyłączyć tryb jarzma testowego."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Konsola szeregowa włączona"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Wpływa na wydajność. Aby wyłączyć, sprawdź program rozruchowy."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Wilgoć lub brud w porcie USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Port USB został automatycznie wyłączony. Kliknij, by dowiedzieć się więcej."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Możesz używać portu USB"</string>
@@ -1958,10 +1962,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Bez kategorii"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Ustawiłeś ważność tych powiadomień."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Ta wiadomość jest ważna ze względu na osoby uczestniczące w wątku."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Zezwolić aplikacji <xliff:g id="APP">%1$s</xliff:g> na utworzenie nowego użytkownika dla konta <xliff:g id="ACCOUNT">%2$s</xliff:g> (użytkownik dla tego konta już istnieje)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Zezwolić aplikacji <xliff:g id="APP">%1$s</xliff:g> na utworzenie nowego użytkownika dla konta <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Dodaj język"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Ustawienie regionu"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Wpisz nazwę języka"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index ba8285fa79d9..05967afc6fd9 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -237,8 +237,8 @@
<string name="bugreport_option_full_title" msgid="6354382025840076439">"Relatório completo"</string>
<string name="bugreport_option_full_summary" msgid="7210859858969115745">"Use esta opção para ter o mínimo de interferência do sistema quando seu dispositivo não estiver respondendo ou estiver muito lento, ou quando você precisar de todas as seções de relatórios. Ela não permite que você informe mais detalhes ou faça capturas de tela adicionais."</string>
<plurals name="bugreport_countdown" formatted="false" msgid="6878900193900090368">
- <item quantity="one">Capturas de tela para o relatório do bug serão feitas em <xliff:g id="NUMBER_1">%d</xliff:g> segundos.</item>
- <item quantity="other">Capturas de tela para o relatório do bug serão feitas em <xliff:g id="NUMBER_1">%d</xliff:g> segundos.</item>
+ <item quantity="one">Capturas de tela para o relatório de bug serão feitas em <xliff:g id="NUMBER_1">%d</xliff:g> segundos.</item>
+ <item quantity="other">Capturas de tela para o relatório de bug serão feitas em <xliff:g id="NUMBER_1">%d</xliff:g> segundos.</item>
</plurals>
<string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Modo silencioso"</string>
<string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Som DESATIVADO"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Permite que o app receba e processe mensagens SMS. Isso significa que o app pode monitorar ou excluir mensagens enviadas para o dispositivo sem mostrá-las para você."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"receber mensagens de texto (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Permite que o app receba e processe mensagens MMS. Isso significa que o app pode monitorar ou excluir as mensagens enviadas para o dispositivo sem mostrá-las para você."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Encaminhar mensagens de transmissão celular"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Permite que o app se vincule ao módulo de transmissão celular para encaminhar mensagens de transmissão celular assim que elas forem recebidas. Alertas de transmissão celular são recebidos em alguns locais para avisar sobre situações de emergência. Apps maliciosos podem interferir no desempenho ou funcionamento do dispositivo quando uma transmissão celular de emergência é recebida."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"ler mensagens de difusão celular"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite que o app leia mensagens de difusão celular recebidas por seu dispositivo. Alertas de difusão celular são recebidos em alguns locais para avisar você de situações de emergência. Apps maliciosos podem interferir no desempenho ou funcionamento de seu dispositivo quando uma difusão celular de emergência é recebida."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"ler feeds inscritos"</string>
@@ -1361,14 +1363,16 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Selecione para desativar a depuração USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Modo Arcabouço de testes ativado"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Realize uma redefinição para configuração original para desativar o modo Arcabouço de testes."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Console serial ativado"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"O desempenho foi impactado. Para desativar, verifique o carregador de inicialização."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Líquido ou detrito na porta USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"A porta USB é desativada automaticamente. Toque para saber mais."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"É seguro usar a porta USB"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Não há mais líquidos ou detritos detectados no smartphone."</string>
- <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Gerando relatório do bug..."</string>
- <string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Compartilhar relatório do bug?"</string>
- <string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Compartilhando relatório do bug…"</string>
- <string name="share_remote_bugreport_notification_message_finished" msgid="6029609949340992866">"Seu administrador solicitou um relatório do bug para ajudar a resolver problemas deste dispositivo. É possível que apps e dados sejam compartilhados."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Gerando relatório de bug..."</string>
+ <string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Compartilhar relatório de bug?"</string>
+ <string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Compartilhando relatório de bug…"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="6029609949340992866">"Seu administrador solicitou um relatório de bug para ajudar a resolver problemas deste dispositivo. É possível que apps e dados sejam compartilhados."</string>
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"COMPARTILHAR"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"RECUSAR"</string>
<string name="select_input_method" msgid="4653387336791222978">"Selecione o método de entrada"</string>
@@ -1547,7 +1551,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Mais opções"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <string name="storage_internal" msgid="3570990907910199483">"Armazenamento interno compartilhado"</string>
+ <string name="storage_internal" msgid="3570990907910199483">"Divisão interna de armazenamento"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Cartão SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Cartão SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Drive USB"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Sem classificação"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Você definiu a importância dessas notificações."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Isso é importante por causa das pessoas envolvidas."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Permitir que o app <xliff:g id="APP">%1$s</xliff:g> crie um novo usuário com <xliff:g id="ACCOUNT">%2$s</xliff:g> (já existe um usuário com essa conta)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Permitir que o app <xliff:g id="APP">%1$s</xliff:g> crie um novo usuário com <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Adicionar um idioma"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Preferência de região"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Digitar nome do idioma"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index c7df3a7d848c..400d5762acca 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -229,7 +229,7 @@
<string name="global_action_emergency" msgid="7112311161137421166">"Emergência"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Relatório de erros"</string>
<string name="global_action_logout" msgid="935179188218826050">"Terminar sessão"</string>
- <string name="global_action_screenshot" msgid="8329831278085426283">"Captura de ecrã"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Capt. ecrã"</string>
<string name="bugreport_title" msgid="5981047024855257269">"Relatório de erro"</string>
<string name="bugreport_message" msgid="398447048750350456">"Será recolhida informação sobre o estado atual do seu dispositivo a enviar através de uma mensagem de email. Demorará algum tempo até que o relatório de erro esteja pronto para ser enviado. Aguarde um pouco."</string>
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Relatório interativo"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Permite que a aplicação receba e processe mensagens SMS. Isto significa que a aplicação poderá monitorizar ou eliminar mensagens enviadas para o seu dispositivo sem as apresentar."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"receber mensagens de texto (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Permite que a aplicação receba e processe mensagens MMS. Isto significa que a aplicação poderá monitorizar ou eliminar mensagens enviadas para o seu dispositivo sem as apresentar."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Encaminhar mensagens de difusão celular"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Permite que a aplicação se vincule ao módulo de difusão celular para encaminhar mensagens de difusão celular à medida que são recebidas. Os alertas de difusão celular são fornecidos em algumas localizações para avisar sobre situações de emergência. As aplicações maliciosas podem interferir com o desempenho ou funcionamento do seu dispositivo quando for recebida uma difusão celular de emergência."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"ler mensagens de transmissão celular"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite que a aplicação leia mensagens de transmissão celular recebidas pelo seu dispositivo. Os alertas de transmissão celular são fornecidos em algumas localizações para avisá-lo sobre situações de emergência. As aplicações maliciosas podem interferir com o desempenho ou funcionamento do seu dispositivo quando for recebida uma transmissão celular de emergência."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"ler feeds subscritos"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Selecione para desativar a depuração por USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Modo de estrutura de teste ativado"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Efetue uma reposição de dados de fábrica para desativar o Modo de estrutura de teste."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Consola de série ativada"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"O desempenho é afetado. Para desativar, selecione o carregador de arranque."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Líquido ou resíduos na porta USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"A porta USB é automaticamente desativada. Toque para saber mais."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"É seguro utilizar a porta USB"</string>
@@ -1508,7 +1512,7 @@
<string name="sync_really_delete" msgid="2572600103122596243">"Eliminar os itens"</string>
<string name="sync_undo_deletes" msgid="2941317360600338602">"Anular as eliminações"</string>
<string name="sync_do_nothing" msgid="3743764740430821845">"Não fazer nada por agora"</string>
- <string name="choose_account_label" msgid="5655203089746423927">"Selecionar uma conta"</string>
+ <string name="choose_account_label" msgid="5655203089746423927">"Selecione uma conta"</string>
<string name="add_account_label" msgid="2935267344849993553">"Adicionar uma conta"</string>
<string name="add_account_button_label" msgid="3611982894853435874">"Adicionar conta"</string>
<string name="number_picker_increment_button" msgid="2412072272832284313">"Aumentar"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Sem categoria"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Definiu a importância destas notificações."</string>
<string name="importance_from_person" msgid="9160133597262938296">"É importante devido às pessoas envolvidas."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Pretende permitir que a aplicação <xliff:g id="APP">%1$s</xliff:g> crie um novo utilizador com a conta <xliff:g id="ACCOUNT">%2$s</xliff:g> (já existe um utilizador com esta conta)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Pretende permitir que a aplicação <xliff:g id="APP">%1$s</xliff:g> crie um novo utilizador com a conta <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Adicionar um idioma"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Preferência de região"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Intr. nome do idioma"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index ba8285fa79d9..05967afc6fd9 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -237,8 +237,8 @@
<string name="bugreport_option_full_title" msgid="6354382025840076439">"Relatório completo"</string>
<string name="bugreport_option_full_summary" msgid="7210859858969115745">"Use esta opção para ter o mínimo de interferência do sistema quando seu dispositivo não estiver respondendo ou estiver muito lento, ou quando você precisar de todas as seções de relatórios. Ela não permite que você informe mais detalhes ou faça capturas de tela adicionais."</string>
<plurals name="bugreport_countdown" formatted="false" msgid="6878900193900090368">
- <item quantity="one">Capturas de tela para o relatório do bug serão feitas em <xliff:g id="NUMBER_1">%d</xliff:g> segundos.</item>
- <item quantity="other">Capturas de tela para o relatório do bug serão feitas em <xliff:g id="NUMBER_1">%d</xliff:g> segundos.</item>
+ <item quantity="one">Capturas de tela para o relatório de bug serão feitas em <xliff:g id="NUMBER_1">%d</xliff:g> segundos.</item>
+ <item quantity="other">Capturas de tela para o relatório de bug serão feitas em <xliff:g id="NUMBER_1">%d</xliff:g> segundos.</item>
</plurals>
<string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Modo silencioso"</string>
<string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Som DESATIVADO"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Permite que o app receba e processe mensagens SMS. Isso significa que o app pode monitorar ou excluir mensagens enviadas para o dispositivo sem mostrá-las para você."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"receber mensagens de texto (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Permite que o app receba e processe mensagens MMS. Isso significa que o app pode monitorar ou excluir as mensagens enviadas para o dispositivo sem mostrá-las para você."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Encaminhar mensagens de transmissão celular"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Permite que o app se vincule ao módulo de transmissão celular para encaminhar mensagens de transmissão celular assim que elas forem recebidas. Alertas de transmissão celular são recebidos em alguns locais para avisar sobre situações de emergência. Apps maliciosos podem interferir no desempenho ou funcionamento do dispositivo quando uma transmissão celular de emergência é recebida."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"ler mensagens de difusão celular"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite que o app leia mensagens de difusão celular recebidas por seu dispositivo. Alertas de difusão celular são recebidos em alguns locais para avisar você de situações de emergência. Apps maliciosos podem interferir no desempenho ou funcionamento de seu dispositivo quando uma difusão celular de emergência é recebida."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"ler feeds inscritos"</string>
@@ -1361,14 +1363,16 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Selecione para desativar a depuração USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Modo Arcabouço de testes ativado"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Realize uma redefinição para configuração original para desativar o modo Arcabouço de testes."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Console serial ativado"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"O desempenho foi impactado. Para desativar, verifique o carregador de inicialização."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Líquido ou detrito na porta USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"A porta USB é desativada automaticamente. Toque para saber mais."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"É seguro usar a porta USB"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Não há mais líquidos ou detritos detectados no smartphone."</string>
- <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Gerando relatório do bug..."</string>
- <string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Compartilhar relatório do bug?"</string>
- <string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Compartilhando relatório do bug…"</string>
- <string name="share_remote_bugreport_notification_message_finished" msgid="6029609949340992866">"Seu administrador solicitou um relatório do bug para ajudar a resolver problemas deste dispositivo. É possível que apps e dados sejam compartilhados."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Gerando relatório de bug..."</string>
+ <string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Compartilhar relatório de bug?"</string>
+ <string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Compartilhando relatório de bug…"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="6029609949340992866">"Seu administrador solicitou um relatório de bug para ajudar a resolver problemas deste dispositivo. É possível que apps e dados sejam compartilhados."</string>
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"COMPARTILHAR"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"RECUSAR"</string>
<string name="select_input_method" msgid="4653387336791222978">"Selecione o método de entrada"</string>
@@ -1547,7 +1551,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Mais opções"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <string name="storage_internal" msgid="3570990907910199483">"Armazenamento interno compartilhado"</string>
+ <string name="storage_internal" msgid="3570990907910199483">"Divisão interna de armazenamento"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Cartão SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Cartão SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Drive USB"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Sem classificação"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Você definiu a importância dessas notificações."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Isso é importante por causa das pessoas envolvidas."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Permitir que o app <xliff:g id="APP">%1$s</xliff:g> crie um novo usuário com <xliff:g id="ACCOUNT">%2$s</xliff:g> (já existe um usuário com essa conta)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Permitir que o app <xliff:g id="APP">%1$s</xliff:g> crie um novo usuário com <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Adicionar um idioma"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Preferência de região"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Digitar nome do idioma"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 72772288d947..27e6ec06596f 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -347,6 +347,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Permite aplicației să primească și să proceseze mesaje SMS. Acest lucru înseamnă că aplicația ar putea monitoriza sau șterge mesajele trimise pe dispozitivul dvs. fără a vi le arăta."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"primește mesaje text (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Permite aplicației să primească și să proceseze mesaje MMS. Acest lucru înseamnă că aplicația ar putea monitoriza sau șterge mesajele trimise pe dispozitivul dvs. fără a vi le arăta."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Redirecționează mesajele cu transmisie celulară"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Permite aplicației să se conecteze la modulul de transmisie celulară pentru a redirecționa mesajele cu transmisie celulară pe măsură ce le primește. Alertele cu transmisie celulară sunt difuzate în unele locații pentru a vă avertiza cu privire la situațiile de urgență. Aplicațiile rău intenționate pot afecta performanța sau funcționarea dispozitivului dvs. când este primită o transmisie celulară de urgență."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"citește mesajele cu transmisie celulară"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite aplicației să citească mesajele primite prin transmisie celulară de dispozitivul dvs. Alertele cu transmisie celulară sunt difuzate în unele locații pentru a vă avertiza cu privire la situațiile de urgență. Aplicațiile rău intenționate pot afecta performanța sau funcționarea dispozitivului dvs. când este primită o transmisie celulară de urgență."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"citire feeduri abonat"</string>
@@ -1383,6 +1385,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Selectați pentru a dezactiva remedierea erorilor prin USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Modul Set de testare este activat"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Reveniți la setările din fabrică pentru a dezactiva modul Set de testare."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Consola din serie este activată"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Performanța este afectată. Pentru a dezactiva, verificați programul bootloader."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Lichide sau reziduuri în portul USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Portul USB este dezactivat automat. Atingeți ca să aflați mai multe."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Portul USB poate fi folosit"</string>
@@ -1924,10 +1928,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Neclasificate"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Dvs. setați importanța acestor notificări."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Notificarea este importantă având în vedere persoanele implicate."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Permiteți ca <xliff:g id="APP">%1$s</xliff:g> să creeze un nou utilizator folosind <xliff:g id="ACCOUNT">%2$s</xliff:g>? (există deja un utilizator cu acest cont)"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Permiteți ca <xliff:g id="APP">%1$s</xliff:g> să creeze un nou utilizator folosind <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Adăugați o limbă"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Regiunea preferată"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Numele limbii"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index e458e4644ab2..6390b95bfaf5 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -350,6 +350,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Приложение сможет получать и обрабатывать SMS. Это значит, что оно сможет отслеживать и удалять отправленные на ваше устройство сообщения, не показывая их."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"Прием MMS-сообщений"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Приложение сможет получать и обрабатывать MMS. Это значит, что оно сможет отслеживать и удалять отправленные на ваше устройство сообщения, не показывая их."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Пересылка сообщений для оповещения населения"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Приложение сможет выполнить привязку к модулю оповещения населения, чтобы пересылать сообщения широковещательных SMS-служб сразу после их получения. В некоторых странах эти сообщения используются для информирования об экстренных ситуациях. Вредоносное ПО может помешать работе устройства, на которое поступают такие сообщения."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"Читать сообщения массовой рассылки"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Приложение получит доступ к сообщениям широковещательных SMS-служб, которые в некоторых странах используются для информирования населения об экстренных ситуациях. Вредоносные программы могут помешать работе устройства, на которое поступают такие сообщения."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"Просмотр фидов пользователя"</string>
@@ -382,7 +384,7 @@
<string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"Приложение сможет постоянно хранить свои компоненты в памяти. Это может уменьшить объем памяти, доступный другим приложениям, и замедлить работу устройства."</string>
<string name="permdesc_persistentActivity" product="tv" msgid="47072473951071734">"Приложение сможет постоянно хранить свои компоненты в памяти. Это может уменьшить объем памяти, доступный другим приложениям, и замедлить работу устройства Android TV."</string>
<string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"Приложение сможет постоянно хранить свои компоненты в памяти. Это может уменьшить объем памяти, доступный другим приложениям, и замедлить работу устройства."</string>
- <string name="permlab_foregroundService" msgid="3310786367649133115">"запускать активные сервисы"</string>
+ <string name="permlab_foregroundService" msgid="3310786367649133115">"Запуск активных сервисов"</string>
<string name="permdesc_foregroundService" msgid="6471634326171344622">"Разрешить приложению использовать активные сервисы."</string>
<string name="permlab_getPackageSize" msgid="7472921768357981986">"Вычисление объема памяти приложений"</string>
<string name="permdesc_getPackageSize" msgid="3921068154420738296">"Приложение сможет получать сведения о размере кода, данных и кеша."</string>
@@ -518,7 +520,7 @@
<string name="permdesc_requestPasswordComplexity" msgid="4730994229754212347">"Приложение получит доступ к сведениям об уровне сложности блокировки экрана (высокий, средний, низкий или отсутствует), в том числе о типе блокировки и длине пароля. Кроме того, оно сможет предлагать пользователям повысить уровень сложности блокировки. Эти рекомендации необязательны. Обратите внимание, что пароль не хранится в виде открытого текста и недоступен приложению."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"Использование биометрического оборудования"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Приложение сможет использовать биометрическое оборудование для аутентификации"</string>
- <string name="permlab_manageFingerprint" msgid="5640858826254575638">"управление сканером отпечатков"</string>
+ <string name="permlab_manageFingerprint" msgid="5640858826254575638">"управление сканером отпечатков пальцев"</string>
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"Приложение сможет добавлять и удалять шаблоны отпечатков пальцев."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"Использование сканера отпечатков"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Приложение сможет использовать сканер отпечатков пальцев для аутентификации."</string>
@@ -1405,6 +1407,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Нажмите, чтобы отключить отладку по USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Тестовый режим включен"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Чтобы отключить тестовый режим, сбросьте настройки до заводских."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Консоль последовательного порта включена"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Производительность устройства снижена. Чтобы отключить консоль, перейдите в загрузчик операционной системы."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"В USB-порт попала вода или грязь"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB-порт был автоматически отключен. Нажмите, чтобы узнать подробности."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB-порт можно использовать"</string>
@@ -1958,10 +1962,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Без категории"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Вы определяете важность этих уведомлений."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Важное (люди)"</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Разрешить приложению \"<xliff:g id="APP">%1$s</xliff:g>\" создать нового пользователя с аккаунтом <xliff:g id="ACCOUNT">%2$s</xliff:g> (пользователь с этим аккаунтом уже существует)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Разрешить приложению \"<xliff:g id="APP">%1$s</xliff:g>\" создать нового пользователя с аккаунтом <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Добавьте язык"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Региональные настройки"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Введите язык"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 3e0f582ba87e..fc69d0e95340 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"SMS පණිවිඩ ලැබීමට සහ ක්‍රියාත්මක කිරීමට යෙදුමට අවසර දෙන්න. මෙහි තේරුම යෙදුමට ඔබගේ උපාංගයට ලැබෙන පණිවිඩ අධීක්ෂණය කිරීමට හැකිවීම වන අතර, ඒවා ඔබට නොපෙන්වා මකා දැමීමටද හැකි වීමයි."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"පෙළ පණිවුඩ ලබාගන්න (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"MMS පණිවිඩ සොයා ලබාගැනීමට සහ ක්‍රියාත්මක කිරීමට යෙදුමට අවසර දෙන්න. යෙදුම නිරීක්ෂණය කරනු ලබන අතර ඔබට ලැබුන පණිවිඩ පෙන්වීමෙන් තොරවම මකා දැමිය හැකි බව මෙමඟින් අදහස් කරයි."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"සෙල් විකාශන පණිවිඩ යොමු කිරීම"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"සෙල් විකාශන පණිවිඩ ලැබුණු විට ඒවා යොමු කිරීම සඳහා සෙල් විකාශන මොඩියුලයට බැඳීමට යෙදුමට ඉඩ දෙයි. හදිසි අවස්ථා පිළිබඳව ඔබට අනතුරු ඇඟවීම සඳහා සෙල් විකාශන ඇඟවීම් සමහර ස්ථානවල ලබා දෙනු ලැබේ. හදිසි සෙල් විකාශනයක් ලැබෙන අවස්ථාවකදී, අනිෂ්ට යෙදුම්වලින් ඔබගේ උපාංග කාර්ය සාධනයට හෝ මෙහෙයුමට බාධා සිදු විය හැකිය."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"සෙල් ප්‍රචාරණ පණිවිඩ කියවීම"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"ඔබගේ උපාංගයට ලැබුණු සෙල් විකාශන පණිවිඩ කියවීමට යෙදුමට අවසර දෙන්න. ඔබට හදිසි අවස්ථාවන් පිළිබඳ අනතුරු ඇඟවීමට සෙල් විකාශන පණිවිඩ ඇතැම් ස්ථානවල සිට යවනු ලබයි. හදිසි සෙල් විකාශන ලැබෙන අවස්ථාවකදී, අනිෂ්ට යෙදුම් මඟින් ඔබගේ උපාංගයට කාර්ය සාධනයට හෝ ක්‍රියකරණයට බාධා සිදුවිය හැක."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"දායක වූ සංග්‍රහ කියවීම"</string>
@@ -1363,6 +1365,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB නිදොස්කරණය අබල කිරීමට තෝරන්න."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"පුරක පරීක්‍ෂා ප්‍රකාරය සබලයි"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"පුරක පරීක්‍ෂා ප්‍රකාරය අබල කිරීමට කර්මාන්තශාලා යළි සැකසීමක් ඉටු කරන්න."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"අනුක්‍රමික කොන්සෝලය සබලයි"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"කාර්ය සාධනය බලපෑමට ලක් වී ඇත. අබල කිරීමට, ආරම්භකය පරීක්ෂා කරන්න."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB තොට තුළ ද්‍රව හෝ කුණු"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB තොට ස්වයංක්‍රීයව අබල කෙරේ. තවත් දැන ගැනීමට තට්ටු කරන්න."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB තොට භාවිත කළාට කමක් නැත"</string>
@@ -1581,7 +1585,7 @@
<string name="expires_on" msgid="3676242949915959821">"කල් ඉකුත් වන්නේ:"</string>
<string name="serial_number" msgid="758814067660862493">"අනුක්‍රමාංකය:"</string>
<string name="fingerprints" msgid="4516019619850763049">"ඇඟිලි සලකුණු:"</string>
- <string name="sha256_fingerprint" msgid="4391271286477279263">"SHA-256 ඇඟිලිසලකුණ:"</string>
+ <string name="sha256_fingerprint" msgid="4391271286477279263">"SHA-256 ඇඟිලි සලකුණ:"</string>
<string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 ඇඟිලි සලකුණ:"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"සියල්ල බලන්න"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"ක්‍රියාකාරකම තෝරන්න"</string>
@@ -1892,10 +1896,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"වර්ගීකරණය නොකළ"</string>
<string name="importance_from_user" msgid="7318955817386549931">"ඔබ මෙම දැනුම්දීම්වල වැදගත්කම සකසා ඇත."</string>
<string name="importance_from_person" msgid="9160133597262938296">"සම්බන්ධ වූ පුද්ගලයන් නිසා මෙය වැදගත් වේ."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="APP">%1$s</xliff:g> හට <xliff:g id="ACCOUNT">%2$s</xliff:g> සමගින් නව පරිශීලකයෙකු සෑදීමට ඉඩ දෙන්නද (මෙම ගිණුම සහිත පරිශීලකයෙකු දැනටමත් සිටී) ?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"<xliff:g id="APP">%1$s</xliff:g> හට <xliff:g id="ACCOUNT">%2$s</xliff:g> සමගින් නව පරිශීලකයෙකු සෑදීමට ඉඩ දෙන්නද ?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"භාෂාවක් එක් කරන්න"</string>
<string name="country_selection_title" msgid="2954859441620215513">"ප්‍රදේශ මනාපය"</string>
<string name="search_language_hint" msgid="7042102592055108574">"භාෂා නම ටයිප් කරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 015cd91d60c9..59e8b5e042a1 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -330,7 +330,7 @@
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Ovládajte umiestnenie a úroveň priblíženia obrazovky."</string>
<string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Gestá"</string>
<string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Je možné použiť klepnutie, prejdenie, stiahnutie prstami a ďalšie gestá."</string>
- <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"Gestá odtlačkom prstu"</string>
+ <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"Gestá odtlačkom prsta"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"Dokáže zaznamenať gestá na senzore odtlačkov prstov zariadenia."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"zakázanie alebo zmeny stavového riadka"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Umožňuje aplikácii vypnúť stavový riadok alebo pridať a odstrániť systémové ikony."</string>
@@ -350,6 +350,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Umožňuje aplikácii prijímať a spracovávať správy SMS. Znamená to, že aplikácia môže sledovať správy odoslané na vaše zariadenie alebo ich odstrániť bez toho, aby sa vám zobrazili."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"prijímať textové správy (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Umožňuje aplikácii prijímať a spracovávať správy MMS. Znamená to, že aplikácia môže sledovať správy odoslané na vaše zariadenie alebo ich odstrániť bez toho, aby sa vám zobrazili."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Preposielanie správ informačných služieb"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Umožňuje aplikácii spojiť sa s modulom správ informačných služieb s cieľom preposielať prichádzajúce správy informačných služieb. Správy informačných služieb sa doručujú na určitých miestach a upozorňujú na tiesňové situácie. Škodlivé aplikácie môžu pri prijatí správy informačnej služby narušiť výkonnosť alebo prevádzku vášho zariadenia."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"čítať správy informačných služieb"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Umožňuje aplikácii čítať správy informačných služieb prijaté vaším zariadením. Správy informačných služieb sa doručujú na určitých miestach a upozorňujú na tiesňové situácie. Škodlivé aplikácie môžu pri prijatí správy informačnej služby narušiť výkonnosť alebo prevádzku vášho zariadenia."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"čítať odoberané informačné kanály"</string>
@@ -538,17 +540,17 @@
<string name="biometric_error_device_not_secured" msgid="6583143098363528349">"Nie je nastavený PIN, vzor ani heslo"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Podarilo sa rozpoznať iba časť odtlačku prsta. Skúste to znova."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Odtlačok prsta sa nepodarilo spracovať. Skúste to znova."</string>
- <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Snímač odtlačkov je špinavý. Vyčistite ho a skúste to znova."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Snímač odtlačkov prstov je špinavý. Vyčistite ho a skúste to znova."</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Pohli ste prstom príliš rýchlo. Skúste to znova."</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Pohli ste prstom príliš pomaly. Skúste to znova."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_authenticated" msgid="5309333983002526448">"Odtlačok bol overený"</string>
+ <string name="fingerprint_authenticated" msgid="5309333983002526448">"Odtlačok prsta bol overený"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Tvár bola overená"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Tvár bola overená, stlačte tlačidlo potvrdenia"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardvér na snímanie odtlačku prsta nie je k dispozícii"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Odtlačok prsta nie je možné uložiť. Odstráňte existujúci odtlačok."</string>
- <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Časový limit rozpoznania odtlačku vypršal. Skúste to znova."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Časový limit rozpoznania odtlačku prsta vypršal. Skúste to znova."</string>
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Operácia týkajúca sa odtlačku prsta bola zrušená"</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Overenie odtlačku prsta zrušil používateľ."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Príliš veľa pokusov. Skúste to znova neskôr."</string>
@@ -1405,6 +1407,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Vyberte, ak chcete zakázať ladenie cez USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Režim správcu testov je aktivovaný"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Ak chcete zakázať režim správcu testov, obnovte výrobné nastavenia."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Sériová konzola je povolená"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Ovplyvňuje výkon. Ak ju chcete zakázať, skontrolujte zavádzací program systému."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Tekutina alebo nečistoty v porte USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Port USB je automaticky deaktivovaný. Ďalšie informácie zobrazíte klepnutím."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Port USB môžete použiť"</string>
@@ -1958,10 +1962,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Nekategorizované"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Nastavili ste dôležitosť týchto upozornení."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Táto správa je dôležitá vzhľadom na osoby, ktorých sa to týka."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Chcete povoliť aplikácii <xliff:g id="APP">%1$s</xliff:g> vytvoriť nového používateľa pomocou účtu <xliff:g id="ACCOUNT">%2$s</xliff:g> (používateľ s týmto účtom už existuje)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Chcete povoliť aplikácii <xliff:g id="APP">%1$s</xliff:g> vytvoriť nového používateľa pomocou účtu <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Pridať jazyk"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Preferovaný región"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Zadajte názov jazyka"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 54da1dc2d7c8..c49d5ffbc4d2 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -350,6 +350,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Aplikaciji omogoča prejemanje in obdelavo SMS-ov. S tem lahko aplikacija nadzoruje ali izbriše sporočila, poslana v napravo, ne da bi vam jih pokazala."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"prejemanje sporočil (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Aplikaciji omogoča prejemanje in obdelavo MMS-ov. S tem lahko aplikacija nadzoruje ali izbriše sporočila, poslana v napravo, ne da bi vam jih pokazala."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Posredovanje sporočil oddaj v celici"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Aplikaciji omogoča povezovanje z modulom za oddaje v celici, da posreduje sporočila oddaj v celici, takoj ko jih prejme. Na nekaterih lokacijah so opozorila oddaj v celici dostavljena, da vas opozorijo na izredne razmere. Zlonamerne aplikacije lahko vplivajo na delovanje naprave, ko prejme sporočilo oddaje v celici."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"branje sporočil oddaje v celici"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Omogoča aplikaciji branje sporočil oddaje v celici, ki jih prejme naprava. Opozorila oddaje v celici so dostavljena na nekaterih lokacijah, da vas opozorijo na izredne razmere. Zlonamerne aplikacije lahko vplivajo na delovanje naprave, ko dobi sporočilo oddaje v celici."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"branje naročenih virov"</string>
@@ -1405,6 +1407,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Izberite, če želite onemogočiti iskanje in odpravljanje napak prek vrat USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Način preizkusnega ogrodja je omogočen"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Če želite onemogočiti način preizkusnega ogrodja, ponastavite napravo na tovarniške nastavitve."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Serijska konzola je omogočena"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Učinkovitost delovanja je slabša. Uporabo konzole lahko onemogočite v zagonskem nalagalniku."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"V vratih USB je tekočina ali umazanija"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Vrata USB so samodejno onemogočena. Dotaknite se, če želite izvedeti več."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Vrata USB so varna za uporabo"</string>
@@ -1958,10 +1962,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Nekategorizirano"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Vi določite raven pomembnosti teh obvestil."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Pomembno zaradi udeleženih ljudi."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Ali aplikaciji <xliff:g id="APP">%1$s</xliff:g> dovolite, da ustvari novega uporabnika za račun <xliff:g id="ACCOUNT">%2$s</xliff:g> (uporabnik s tem računom že obstaja)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Ali aplikaciji <xliff:g id="APP">%1$s</xliff:g> dovolite, da ustvari novega uporabnika za račun <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Dodajanje jezika"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Nastavitev območja"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Vnesite ime jezika"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 48ae4d79554e..045c7d5d8405 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Lejon aplikacionin të marrë dhe përpunojë mesazhe SMS. Kjo do të thotë se aplikacioni mund të monitorojë ose fshijë mesazhe të dërguara në pajisjen tënde, pa t\'i treguar ato."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"prano mesazhe në tekst (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Lejon aplikacionin të marrë dhe përpunojë mesazhe MMS. Kjo do të thotë se aplikacioni mund të monitorojë ose fshijë mesazhe të dërguara në pajisjen tënde, pa t\'i treguar ato."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Transmeto mesazhet e transmetimit celular"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Lejon që aplikacioni të lidhet me modulin e transmetimit celular për t\'i transferuar mesazhet e transmetimit celular menjëherë kur merren. Sinjalizimet e transmetimit celular dërgohen në disa vendndodhje për të të paralajmëruar për situata urgjente. Aplikacionet keqdashëse mund të ndërhyjnë në cilësinë e funksionimit ose në veprimin e pajisjes sate kur merret një transmetim celular urgjent."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"lexo mesazhet e transmetimit të qelizës"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Lejon aplikacionin të lexojë mesazhet e transmetimit të qelizës, të marra nga pajisja jote. Alarmet e transmetimit të qelizës dërgohen në disa vendndodhje për të të paralajmëruar në situata urgjente. Aplikacionet keqdashëse mund të ndërhyjnë në veprimtarinë ose operacionin e pajisjes tënde kur merret një transmetim urgjent i qelizës."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"lexo informacione të abonuara"</string>
@@ -549,7 +551,7 @@
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Shumë përpjekje. Sensori i gjurmës së gishtit u çaktivizua."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Provo përsëri."</string>
<string name="fingerprint_error_no_fingerprints" msgid="7654382120628334248">"Nuk ka asnjë gjurmë gishti të regjistruar."</string>
- <string name="fingerprint_error_hw_not_present" msgid="409523969613176352">"Kjo pajisje nuk ka një sensor të gjurmës së gishtit."</string>
+ <string name="fingerprint_error_hw_not_present" msgid="409523969613176352">"Kjo pajisje nuk ka sensor të gjurmës së gishtit."</string>
<string name="fingerprint_name_template" msgid="5870957565512716938">"Gishti <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
@@ -1189,7 +1191,7 @@
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Shfaq gjithmonë"</string>
<string name="unsupported_compile_sdk_message" msgid="4253168368781441759">"<xliff:g id="APP_NAME">%1$s</xliff:g> është ndërtuar për një version të papërputhshëm të sistemit operativ Android dhe mund të shfaqë sjellje të papritura. Mund të ofrohet një version i përditësuar i aplikacionit."</string>
<string name="unsupported_compile_sdk_show" msgid="2681877855260970231">"Shfaq gjithnjë"</string>
- <string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"Kliko për përditësim"</string>
+ <string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"Kontrollo për përditësim"</string>
<string name="smv_application" msgid="3307209192155442829">"Aplikacioni <xliff:g id="APPLICATION">%1$s</xliff:g> (procesi <xliff:g id="PROCESS">%2$s</xliff:g>) ka shkelur politikën e tij të vetë-imponuar \"Modaliteti i ashpër\" (StrictMode)."</string>
<string name="smv_process" msgid="5120397012047462446">"Procesi <xliff:g id="PROCESS">%1$s</xliff:g> ka shkelur politikën e tij të vetë-imponuar \"Modaliteti i rreptë\" (StrictMode)"</string>
<string name="android_upgrading_title" product="default" msgid="7513829952443484438">"Telefoni po përditësohet…"</string>
@@ -1357,11 +1359,13 @@
<string name="usb_power_notification_message" msgid="4647527153291917218">"Pajisja e lidhur po karikohet. Trokit për opsione të tjera."</string>
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"U zbulua aksesor i audios analoge"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Pajisja e bashkuar nuk është e pajtueshme me këtë telefon. Trokit për të mësuar më shumë."</string>
- <string name="adb_active_notification_title" msgid="6729044778949189918">"Korrigjuesi i USB-së i lidhur"</string>
+ <string name="adb_active_notification_title" msgid="6729044778949189918">"Korrigjuesi i USB-së është i lidhur"</string>
<string name="adb_active_notification_message" msgid="7463062450474107752">"Trokit për të çaktivizuar korrigjimin e USB-së"</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Përzgjidhe për të çaktivizuar korrigjimin e gabimeve të USB-së"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Modaliteti i lidhjes së testimit është aktivizuar"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"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="4955436518220103382">"Paneli komandues i serisë është aktivizuar"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Ndikohet cilësia e funksionimit. Për ta çaktivizuar, kontrollo ngarkuesin e sistemit."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Lëngje ose papastërti në portën e USB-së"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Porta e USB-së është çaktivizuar automatikisht. Trokit për të mësuar më shumë."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Në rregulloj për përdorimin e portës USB"</string>
@@ -1580,8 +1584,8 @@
<string name="expires_on" msgid="3676242949915959821">"Skadon më:"</string>
<string name="serial_number" msgid="758814067660862493">"Numri serik:"</string>
<string name="fingerprints" msgid="4516019619850763049">"Shenjat e gishtave:"</string>
- <string name="sha256_fingerprint" msgid="4391271286477279263">"Shenja e gishtit SHA-256:"</string>
- <string name="sha1_fingerprint" msgid="7930330235269404581">"Shenja e gishtit SHA-1:"</string>
+ <string name="sha256_fingerprint" msgid="4391271286477279263">"Gjurma e gishtit SHA-256:"</string>
+ <string name="sha1_fingerprint" msgid="7930330235269404581">"Gjurma e gishtit SHA-1:"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Shikoji të gjitha"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Zgjidh aktivitetin"</string>
<string name="share_action_provider_share_with" msgid="5247684435979149216">"Shpërnda publikisht me"</string>
@@ -1891,10 +1895,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"E pakategorizuara"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Ke caktuar rëndësinë e këtyre njoftimeve."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Është i rëndësishëm për shkak të personave të përfshirë."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Të lejohet <xliff:g id="APP">%1$s</xliff:g> që të krijojë një përdorues të ri me <xliff:g id="ACCOUNT">%2$s</xliff:g> (një përdorues me këtë llogari ekziston tashmë) ?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Të lejohet <xliff:g id="APP">%1$s</xliff:g> që të krijojë një përdorues të ri me <xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Shto një gjuhë"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Preferenca e rajonit"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Shkruaj emrin e gjuhës"</string>
@@ -1909,7 +1911,7 @@
<string name="work_mode_off_message" msgid="5130856710614337649">"Aplikacionet e punës, njoftimet, të dhënat e tua dhe funksionet e tjera të profilit të punës do të aktivizohen"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Aktivizo"</string>
<string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Ky aplikacion është ndërtuar për një version më të vjetër të Android dhe mund të mos funksionojë mirë. Provo të kontrollosh për përditësime ose kontakto me zhvilluesin."</string>
- <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Kliko për përditësim"</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Kontrollo për përditësim"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Ke mesazhe të reja"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Hap aplikacionin SMS për ta parë"</string>
<string name="profile_encrypted_title" msgid="4260432497586829134">"Disa funksione mund të jenë të kufizuara"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 75fc1ce6732b..0d8acc69e5e4 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -27,7 +27,7 @@
<string name="terabyteShort" msgid="231613018159186962">"TB"</string>
<string name="petabyteShort" msgid="5637816680144990219">"PB"</string>
<string name="fileSizeSuffix" msgid="8897567456150907538">"<xliff:g id="NUMBER">%1$s</xliff:g> <xliff:g id="UNIT">%2$s</xliff:g>"</string>
- <string name="untitled" msgid="4638956954852782576">"&lt;Без наслова&gt;"</string>
+ <string name="untitled" msgid="4638956954852782576">"&lt;Без имена&gt;"</string>
<string name="emptyPhoneNumber" msgid="7694063042079676517">"(Нема броја телефона)"</string>
<string name="unknownName" msgid="6867811765370350269">"Непознато"</string>
<string name="defaultVoiceMailAlphaTag" msgid="2660020990097733077">"Гласовна пошта"</string>
@@ -347,6 +347,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Дозвољава апликацији да прима и обрађује SMS поруке. То значи да апликација може да надгледа или брише поруке које се шаљу уређају, а да вам их не прикаже."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"пријем текстуалних порука (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Дозвољава апликацији да прима и обрађује MMS поруке. То значи да апликација може да надгледа или брише поруке које се шаљу уређају, а да вам их не прикаже."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Прослеђивање порука за мобилне уређаје на локалитету"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Дозвољава апликацији да се везује за модул порука за мобилне уређаје на локалитету да би прослеђивала поруке за мобилне уређаје на локалитету онако како су примљене. Обавештења порука за мобилне уређаје на локалитету се на неким локацијама примају као упозорења на хитне случајеве. Злонамерне апликације могу да утичу на учинак или ометају рад уређаја када се прими порука о хитном случају за мобилне уређаје на локалитету."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"читање порука инфо сервиса"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Омогућава апликацији да чита поруке инфо сервиса које уређај прима. Упозорења инфо сервиса се на неким локацијама примају као упозорења на хитне случајеве. Злонамерне апликације могу да утичу на учинак или ометају функционисање уређаја када се прими порука инфо сервиса о хитном случају."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"читање пријављених фидова"</string>
@@ -1383,6 +1385,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Изаберите да бисте онемогућили отклањања грешака са USB-а."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Омогућен је режим пробног коришћења"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Обавите ресетовање на фабричка подешавања да бисте онемогућили режим пробног коришћења."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Серијска конзола је омогућена"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Учинак је смањен. Да бисте онемогући конзолу, проверите покретачки програм."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Течност или нечистоћа у USB порту"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB порт је аутоматски искључен. Додирните да бисте сазнали више."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Коришћење USB порта је дозвољено"</string>
@@ -1602,8 +1606,8 @@
<string name="expires_on" msgid="3676242949915959821">"Истиче:"</string>
<string name="serial_number" msgid="758814067660862493">"Серијски број:"</string>
<string name="fingerprints" msgid="4516019619850763049">"Дигитални отисци:"</string>
- <string name="sha256_fingerprint" msgid="4391271286477279263">"SHA-256 дигитални отисак:"</string>
- <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 дигитални отисак:"</string>
+ <string name="sha256_fingerprint" msgid="4391271286477279263">"SHA-256 отисак прста:"</string>
+ <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 отисак прста:"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Прикажи све"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Избор активности"</string>
<string name="share_action_provider_share_with" msgid="5247684435979149216">"Дели са"</string>
@@ -1924,10 +1928,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Некатегоризовано"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Ви подешавате важност ових обавештења."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Ово је важно због људи који учествују."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Желите ли да дозволите да <xliff:g id="APP">%1$s</xliff:g> направи новог корисника са налогом <xliff:g id="ACCOUNT">%2$s</xliff:g> (корисник са тим налогом већ постоји)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Желите ли да дозволите да <xliff:g id="APP">%1$s</xliff:g> направи новог корисника са налогом <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Додајте језик"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Подешавање региона"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Унесите назив језика"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index fdd0b7b7267a..dba5ebb2e134 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Tillåter att appen tar emot och hanterar SMS. Detta innebär att appen kan övervaka eller ta bort meddelanden som skickats till enheten utan att visa dem för dig."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"ta emot textmeddelanden (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Tillåter att appen tar emot och hanterar MMS-meddelanden. Detta innebär att appen kan övervaka eller ta bort meddelanden som skickats till enheten utan att visa dem för dig."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Vidarebefordra massutskick via sms"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Tillåter att appen binds till cellsändningsmodulen så att massutskick via sms kan vidarebefordras vid mottagandet. I vissa områden används massutskick via sms för att varna om nödsituationer. Skadliga appar kan påverka enhetens prestanda eller funktioner när ett massutskick via sms tas emot."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"läsa SMS-meddelanden"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Appen tillåts läsa SMS som skickas till din enhet. På vissa platser skickas SMS för att varna för nödsituationer. Skadliga appar kan påverka enhetens prestanda eller funktionalitet när du får ett meddelande om en nödsituation via SMS."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"läsa flöden som du prenumererar på"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Välj för att inaktivera USB-felsökning."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Läget för testverktyg har aktiverats"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Inaktivera testverktygsläget genom att göra en återställning till standardinställningarna."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Seriekonsolen är aktiverad"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Prestandan påverkas. Inaktivera via starthanteraren."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Vätska eller smuts i USB-porten"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB-porten har inaktiverats automatiskt. Tryck för att läsa mer."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Nu kan du använda USB-porten"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Okategoriserad"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Du anger hur viktiga aviseringarna är."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Detta är viktigt på grund av personerna som deltar."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Tillåter du att <xliff:g id="APP">%1$s</xliff:g> skapar en ny användare för <xliff:g id="ACCOUNT">%2$s</xliff:g> (det finns redan en användare med det här kontot)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Tillåter du att <xliff:g id="APP">%1$s</xliff:g> skapar en ny användare för <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Lägg till ett språk"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Regionsinställningar"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Ange språket"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index c8c582f334a9..89e3231ff8d3 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Inaruhusu programu kupokea na kuchakata ujumbe wa SMS. Hii inamaanisha programu hii inaweza kuchunguza na kufuta ujumbe uliotumwa katika kifaa chako bila ya kukuonyesha."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"pokea ujumbe wa maandishi wa MMS"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Inaruhusu programu kupokea na kuchakata ujumbe medianwai (MMS). Hii inamaanisha uwezo wa kuchunguza na kufuta ujumbe uliotumwa kwa kifaa chako bila ya kukuonyesha."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Sambaza ujumbe wa matangazo ya simu"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Huruhusu programu ipachikwe katika sehemu ya matangazo ya simu ili isambaze ujumbe wa matangazo ya simu unapopokewa. Arifa za matangazo ya simu huwasilishwa katika maeneo mengine ili kukuonya juu ya hali za dharura. Huenda programu hasidi zikatatiza utendaji au shughuli ya kifaa chako matangazo ya simu ya dharura yanapopokewa."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"soma mawasiliano ya matangazo ya simu"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Huruhusu programu kusoma mawasiliano ya matangazo ya simu yaliyoingia kwenye kifaa chako. Arifa za matangazo ya simu huwasilishwa katika maeneo mengine ili kukuonya juu ya hali za dharura. Huenda programu hasidi zikatatiza utendajikazi au shughuli ya kifaa chako wakati matangazo ya simu ya dharura yameingia."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"kusoma mipasho kutoka vyanzo unavyofuatilia"</string>
@@ -512,9 +514,9 @@
<string name="permdesc_requestPasswordComplexity" msgid="4730994229754212347">"Huruhusu programu kupata maelezo kuhusu kiwango cha uchangamano wa kufunga skrini (juu, wastani, chini au hakuna), ambacho huashiria urefu unaowezekana na aina ya kufunga skrini. Programu pia inaweza kumpendekezea mtumiaji asasishe mbinu ya kufunga skrini iwe ya kiwango fulani lakini mtumiaji anaweza kuamua kupuuza na kuendelea. Kumbuka kuwa maelezo ya kufunga skrini hayahifadhiwi kama maandishi, hivyo programu haitambui nenosiri mahususi."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"tumia maunzi ya kibiolojia"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Huruhusu programu itumie maunzi ya kibiolojia katika uthibitishaji"</string>
- <string name="permlab_manageFingerprint" msgid="5640858826254575638">"dhibiti maunzi ya kitambulisho"</string>
- <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Huruhusu programu kuomba njia za kuongeza na kufuta violezo vya kitambulisho kwa matumizi."</string>
- <string name="permlab_useFingerprint" msgid="3150478619915124905">"tumia maunzi ya kitambulisho"</string>
+ <string name="permlab_manageFingerprint" msgid="5640858826254575638">"dhibiti maunzi ya alama ya kidole"</string>
+ <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Huruhusu programu kuomba njia za kuongeza na kufuta violezo vya alama ya kidole kwa matumizi."</string>
+ <string name="permlab_useFingerprint" msgid="3150478619915124905">"tumia maunzi ya alama ya kidole"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"Huruhusu programu kutumia maunzi ya kitambulisho kwa uthibitisho"</string>
<string name="permlab_audioWrite" msgid="2661772059799779292">"kubadilisha mkusanyiko wako wa muziki"</string>
<string name="permdesc_audioWrite" msgid="8888544708166230494">"Inaruhusu programu kubadilisha mkusanyiko wako wa muziki."</string>
@@ -530,7 +532,7 @@
<string name="biometric_not_recognized" msgid="5770511773560736082">"Hayatambuliki"</string>
<string name="biometric_error_canceled" msgid="349665227864885880">"Imeghairi uthibitishaji"</string>
<string name="biometric_error_device_not_secured" msgid="6583143098363528349">"Hujaweka pin, mchoro au nenosiri"</string>
- <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Kitambuzi kimegundua sehemu ya kitambulisho. Tafadhali jaribu tena."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Kitambuzi kimegundua sehemu ya alama ya kidole. Tafadhali jaribu tena."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Imeshindwa kuchakata alama ya kidole. Tafadhali jaribu tena."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Kitambuzi alama ya kidole ni kichafu. Tafadhali kisafishe na ujaribu tena."</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Ulisogeza kidole kwa kasi mno. Tafadhali jaribu tena."</string>
@@ -540,10 +542,10 @@
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Imethibitisha alama ya kidole"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Uso umethibitishwa"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Uso umethibitishwa, tafadhali bonyeza thibitisha"</string>
- <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Maunzi ya kitambulisho hayapatikani."</string>
- <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Kitambulisho hakiwezi kuhifadhiwa. Tafadhali ondoa kitambulisho kilichopo."</string>
- <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Muda wa kitambulisho umekwisha. Jaribu tena."</string>
- <string name="fingerprint_error_canceled" msgid="4402024612660774395">"Utendaji wa kitambulisho imeghairiwa."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Maunzi ya alama ya kidole hayapatikani."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Alama ya kidole haiwezi kuhifadhiwa. Tafadhali ondoa alama ya kidole iliyopo."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Muda wa kuweka alama ya kidole umekwisha. Jaribu tena."</string>
+ <string name="fingerprint_error_canceled" msgid="4402024612660774395">"Mchakato wa alama ya kidole umeghairiwa."</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Mtumiaji ameghairi uthibitishaji wa alama ya kidole."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Majaribio mengi mno. Jaribu tena baadaye."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Majaribio mengi mno. Kitambua alama ya kidole kimezimwa."</string>
@@ -553,7 +555,7 @@
<string name="fingerprint_name_template" msgid="5870957565512716938">"Kidole cha <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
- <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Aikoni ya kitambulisho"</string>
+ <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Aikoni ya alama ya kidole"</string>
<string name="permlab_manageFace" msgid="7262837876352591553">"dhibiti maunzi ya kufungua kwa uso"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Huruhusu programu iombe njia za kuongeza na kufuta violezo vya uso vitakavyotumiwa."</string>
<string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"tumia maunzi ya kufungua kwa uso"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Chagua ili kulemaza utatuaji USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Hali ya Muunganisho wa Majaribio imewashwa"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Rejesha mipangilio iliyotoka nayo kiwandani ili uzime hali ya Muunganisho wa Majaribio."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Muunganisho kupitia mlango umewashwa"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Utendaji unaathirika. lli uzime, teua programu ya kuwasha mfumo wa uendeshaji."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Unyevu au uchafu katika mlango wa USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Mlango wa USB umezimwa kiotomatiki. Gusa ili upate maelezo zaidi."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Ni sawa kutumia mlango wa USB"</string>
@@ -1547,7 +1551,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Chaguo zaidi"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <string name="storage_internal" msgid="3570990907910199483">"Hifadhi ya ndani inayoshirikiwa"</string>
+ <string name="storage_internal" msgid="3570990907910199483">"Hifadhi ya ndani ya pamoja"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Kadi ya SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Kadi ya SD iliyotengenezwa na <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Hifadhi ya USB"</string>
@@ -1579,8 +1583,8 @@
<string name="expires_on" msgid="3676242949915959821">"Inaisha muda mnamo:"</string>
<string name="serial_number" msgid="758814067660862493">"Nambari ya ufuatiliaji:"</string>
<string name="fingerprints" msgid="4516019619850763049">"Alazama za Vidole:"</string>
- <string name="sha256_fingerprint" msgid="4391271286477279263">"Alama ya kidole ya SHA-256:"</string>
- <string name="sha1_fingerprint" msgid="7930330235269404581">"Alama ya kidole ya SHA-1:"</string>
+ <string name="sha256_fingerprint" msgid="4391271286477279263">"Kitambulisho dijitali cha SHA-256:"</string>
+ <string name="sha1_fingerprint" msgid="7930330235269404581">"Kitambulisho dijitali cha SHA-1:"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Angalia zote"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Chagua shughuli"</string>
<string name="share_action_provider_share_with" msgid="5247684435979149216">"Shiriki na"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Ambazo aina haijabainishwa"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Uliweka mipangilio ya umuhimu wa arifa hizi."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Hii ni muhimu kwa sababu ya watu waliohusika."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Ruhusu <xliff:g id="APP">%1$s</xliff:g> iweke Mtumiaji mpya ikitumia <xliff:g id="ACCOUNT">%2$s</xliff:g> (Je, tayari kuna mtumiaji anayetumia akaunti hii)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Ungependa kuruhusu <xliff:g id="APP">%1$s</xliff:g> iweke Mtumiaji mpya ikitumia <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Ongeza lugha"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Mapendeleo ya eneo"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Weka jina la lugha"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 0956aad3003f..0ed1421bb92b 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -220,7 +220,7 @@
<string name="reboot_safemode_title" msgid="7054509914500140361">"பாதுகாப்பான பயன்முறைக்கு மீண்டும் தொடங்கவும்"</string>
<string name="reboot_safemode_confirm" msgid="55293944502784668">"பாதுகாப்பான பயன்முறைக்குச் செல்ல மீண்டும் துவக்க விரும்புகிறீர்களா? நீங்கள் நிறுவிய எல்லா மூன்றாம் தரப்பு பயன்பாடுகளையும் இது முடக்கும். நீங்கள் மீண்டும் மறுதொடக்கம் செய்யும்போது அவை மீட்டமைக்கப்படும்."</string>
<string name="recent_tasks_title" msgid="3691764623638127888">"சமீபத்தியவை"</string>
- <string name="no_recent_tasks" msgid="8794906658732193473">"சமீபத்திய பயன்பாடுகள் எதுவுமில்லை."</string>
+ <string name="no_recent_tasks" msgid="8794906658732193473">"சமீபத்திய ஆப்ஸ் எதுவுமில்லை."</string>
<string name="global_actions" product="tablet" msgid="408477140088053665">"டேப்லெட் விருப்பங்கள்"</string>
<string name="global_actions" product="tv" msgid="9091480417912345975">"Android TV விருப்பத்தேர்வுகள்"</string>
<string name="global_actions" product="default" msgid="2406416831541615258">"தொலைபேசி விருப்பங்கள்"</string>
@@ -271,7 +271,7 @@
<string name="notification_channel_heavy_weight_app" msgid="6218742927792852607">"ஆப்ஸ் இயங்குகிறது"</string>
<string name="notification_channel_foreground_service" msgid="3931987440602669158">"பேட்டரியைப் பயன்படுத்தும் ஆப்ஸ்"</string>
<string name="foreground_service_app_in_background" msgid="1060198778219731292">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் பேட்டரியைப் பயன்படுத்துகிறது"</string>
- <string name="foreground_service_apps_in_background" msgid="7175032677643332242">"<xliff:g id="NUMBER">%1$d</xliff:g> பயன்பாடுகள் பேட்டரியைப் பயன்படுத்துகின்றன"</string>
+ <string name="foreground_service_apps_in_background" msgid="7175032677643332242">"<xliff:g id="NUMBER">%1$d</xliff:g> ஆப்ஸ் பேட்டரியைப் பயன்படுத்துகின்றன"</string>
<string name="foreground_service_tap_for_details" msgid="372046743534354644">"பேட்டரி மற்றும் டேட்டா உபயோக விவரங்களைக் காண, தட்டவும்"</string>
<string name="foreground_service_multiple_separator" msgid="4021901567939866542">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="2788228061547930246">"பாதுகாப்பு பயன்முறை"</string>
@@ -344,12 +344,14 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"SMS செய்திகளைப் பெற, செயற்படுத்தப் ஆப்ஸை அனுமதிக்கிறது. இதற்கு அர்த்தம் உங்கள் சாதனத்திற்கு அனுப்பப்படும் செய்திகளை உங்களுக்குக் காட்டாமல் கண்காணிப்பதற்கு அல்லது நீக்குவதற்கு ஆப்ஸால் முடியும் என்பதாகும்."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"உரைச் செய்திகளை (MMS) பெறுதல்"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"MMS செய்திகளைப் பெற, செயற்படுத்தப் ஆப்ஸை அனுமதிக்கிறது. இதற்கு அர்த்தம் உங்கள் சாதனத்திற்கு அனுப்பப்படும் செய்திகளை உங்களுக்குக் காட்டாமல் கண்காணிக்கவோ, நீக்கவோ ஆப்ஸால் முடியும் என்பதாகும்."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"செல் பிராட்காஸ்ட் மெசேஜ்களை முன்னனுப்பு"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"செல் பிராட்காஸ்ட் மெசேஜ்களைப் பெறும்போதெல்லாம் அவற்றை முன்னனுப்பும் பொருட்டு, ஆப்ஸை செல் பிராட்காஸ்ட் மாடியூலோடு இணைக்கும். சில இடங்களில் அவசர சூழ்நிலைகளின் போது உங்களை எச்சரிக்க செல் பிராட்காஸ்ட் விழிப்பூட்டல்கள் அனுப்பப்படும். அவசரநிலை செல் பிராட்காஸ்ட்டைப் பெறும்போது, தீங்கிழைக்கும் ஆப்ஸ் உங்கள் சாதனத்தின் செயல்திறனுக்கோ செயல்பாட்டிற்கோ இடையூறு விளைவிக்கக்கூடும்."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"செல் அலைபரப்புச் செய்திகளைப் படித்தல்"</string>
- <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"உங்கள் சாதனத்தில் பெறப்படும் செல் அலைபரப்புச் செய்திகளைப் படிப்பதற்குப் ஆப்ஸை அனுமதிக்கிறது. அவசரநிலை சூழ்நிலைகளை உங்களுக்கு எச்சரிக்கைச் செய்வதற்கு சில இடங்களில் செல் அலைபரப்பு விழிப்பூட்டல்கள் வழங்கப்படும். அவசரநிலை மொபைல் அலைபரப்புப் பெறப்படும்போது உங்கள் சாதனத்தின் செயல்திறன் அல்லது செயல்பாட்டுடன் தீங்கிழைக்கும் பயன்பாடுகள் அதைத் தடுக்கலாம்."</string>
+ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"உங்கள் சாதனத்தில் பெறப்படும் செல் அலைபரப்புச் செய்திகளைப் படிப்பதற்குப் ஆப்ஸை அனுமதிக்கிறது. அவசரநிலை சூழ்நிலைகளை உங்களுக்கு எச்சரிக்கைச் செய்வதற்கு சில இடங்களில் செல் அலைபரப்பு விழிப்பூட்டல்கள் வழங்கப்படும். அவசரநிலை மொபைல் அலைபரப்புப் பெறப்படும்போது உங்கள் சாதனத்தின் செயல்திறன் அல்லது செயல்பாட்டுடன் தீங்கிழைக்கும் ஆப்ஸ் அதைத் தடுக்கலாம்."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"குழுசேர்ந்த ஊட்டங்களைப் படித்தல்"</string>
<string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"தற்போது ஒத்திசைந்த ஊட்டங்களைப் பற்றிய விவரங்களைப் பெற ஆப்ஸை அனுமதிக்கிறது."</string>
<string name="permlab_sendSms" msgid="7544599214260982981">"SMS செய்திகளை அனுப்புதல் மற்றும் பார்த்தல்"</string>
- <string name="permdesc_sendSms" msgid="7094729298204937667">"SMS செய்திகளை அனுப்ப ஆப்ஸை அனுமதிக்கிறது. இதற்கு எதிர்பாராத பேமெண்ட்கள் விதிக்கப்படலாம். தீங்கு விளைவிக்கும் பயன்பாடுகள் உங்களின் உறுதிப்படுத்தல் எதுவுமின்றி செய்திகளை அனுப்பி உங்களுக்குக் கட்டணம் விதிக்கலாம்."</string>
+ <string name="permdesc_sendSms" msgid="7094729298204937667">"SMS செய்திகளை அனுப்ப ஆப்ஸை அனுமதிக்கிறது. இதற்கு எதிர்பாராத பேமெண்ட்கள் விதிக்கப்படலாம். தீங்கு விளைவிக்கும் ஆப்ஸ் உங்களின் உறுதிப்படுத்தல் எதுவுமின்றி செய்திகளை அனுப்பி உங்களுக்குக் கட்டணம் விதிக்கலாம்."</string>
<string name="permlab_readSms" msgid="8745086572213270480">"உங்கள் உரைச் செய்திகளை (SMS அல்லது MMS) படித்தல்"</string>
<string name="permdesc_readSms" product="tablet" msgid="4741697454888074891">"இந்த ஆப்ஸ் உங்கள் டேப்லெட்டில் சேமிக்கப்பட்டுள்ள எல்லா SMS (உரை) செய்திகளையும் படிக்கலாம்."</string>
<string name="permdesc_readSms" product="tv" msgid="9106832390302749856">"உங்கள் Android TVயில் சேமித்துள்ள அனைத்து மெசேஜ்களையும் இந்த ஆப்ஸால் தெரிந்துகொள்ள முடியும்."</string>
@@ -357,7 +359,7 @@
<string name="permlab_receiveWapPush" msgid="5991398711936590410">"உரைச் செய்திகளைப் (WAP) பெறுதல்"</string>
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"WAP செய்திகளைப் பெற, செயற்படுத்தப் ஆப்ஸை அனுமதிக்கிறது. உங்களுக்கு அனுப்பப்படும் செய்திகளை உங்களுக்குக் காட்டாமல் கண்காணிக்க அல்லது நீக்குவதற்கான திறன் இந்த அனுமதியில் உள்ளடங்கும்."</string>
<string name="permlab_getTasks" msgid="6466095396623933906">"இயங்கும் ஆப்ஸை மீட்டெடுத்தல்"</string>
- <string name="permdesc_getTasks" msgid="7454215995847658102">"நடப்பில் மற்றும் சமீபத்தில் இயங்கும் காரியங்களின் தகவலைப் பெற ஆப்ஸை அனுமதிக்கிறது. சாதனத்தில் எந்தப் பயன்பாடுகள் பயன்படுத்தப்படுகின்றன என்பது குறித்த தகவலைக் கண்டறிய ஆப்ஸை இது அனுமதிக்கலாம்."</string>
+ <string name="permdesc_getTasks" msgid="7454215995847658102">"நடப்பில் மற்றும் சமீபத்தில் இயங்கும் காரியங்களின் தகவலைப் பெற ஆப்ஸை அனுமதிக்கிறது. சாதனத்தில் எந்த ஆப்ஸ் பயன்படுத்தப்படுகின்றன என்பது குறித்த தகவலைக் கண்டறிய ஆப்ஸை இது அனுமதிக்கலாம்."</string>
<string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"சுயவிவரத்தையும் சாதன உரிமையாளர்களையும் நிர்வகித்தல்"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"சுயவிவர உரிமையாளர்களையும் சாதன உரிமையாளரையும் அமைக்க, ஆப்ஸை அனுமதிக்கிறது."</string>
<string name="permlab_reorderTasks" msgid="2018575526934422779">"இயங்கும் ஆப்ஸை மறுவரிசைப்படுத்தல்"</string>
@@ -365,9 +367,9 @@
<string name="permlab_enableCarMode" msgid="5684504058192921098">"கார் பயன்முறையை இயக்குதல்"</string>
<string name="permdesc_enableCarMode" msgid="4853187425751419467">"கார் முறையை இயக்க, ஆப்ஸை அனுமதிக்கிறது."</string>
<string name="permlab_killBackgroundProcesses" msgid="3914026687420177202">"பிற ஆப்ஸை மூடுதல்"</string>
- <string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"பிற ஆப்ஸின் பின்புலச் செயல்முறைகளை நிறுத்த ஆப்ஸை அனுமதிக்கிறது. இதனால் பிற பயன்பாடுகள் இயங்குவதை நிறுத்தலாம்."</string>
+ <string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"பிற ஆப்ஸின் பின்புலச் செயல்முறைகளை நிறுத்த ஆப்ஸை அனுமதிக்கிறது. இதனால் பிற ஆப்ஸ் இயங்குவதை நிறுத்தலாம்."</string>
<string name="permlab_systemAlertWindow" msgid="7238805243128138690">"இந்த ஆப்ஸ் பிற ஆப்ஸின் மேலே தோன்றலாம்"</string>
- <string name="permdesc_systemAlertWindow" msgid="2393776099672266188">"இந்த ஆப்ஸ் பிற ஆப்ஸின் மேலே அல்லது திரையின் பிற பகுதிகளில் தோன்றலாம். இது வழக்கமான ஆப்ஸ் உபயோகத்தில் குறுக்கிட்டு, பிற பயன்பாடுகள் தோன்றும் விதத்தை மாற்றக்கூடும்."</string>
+ <string name="permdesc_systemAlertWindow" msgid="2393776099672266188">"இந்த ஆப்ஸ் பிற ஆப்ஸின் மேலே அல்லது திரையின் பிற பகுதிகளில் தோன்றலாம். இது வழக்கமான ஆப்ஸ் உபயோகத்தில் குறுக்கிட்டு, பிற ஆப்ஸ் தோன்றும் விதத்தை மாற்றக்கூடும்."</string>
<string name="permlab_runInBackground" msgid="7365290743781858803">"பின்னணியில் இயக்கு"</string>
<string name="permdesc_runInBackground" msgid="7370142232209999824">"இந்த ஆப்ஸ், பின்னணியில் இயங்கலாம். இதனால் பேட்டரி விரைவாகத் தீர்ந்துவிடக்கூடும்."</string>
<string name="permlab_useDataInBackground" msgid="8694951340794341809">"பின்னணியில் தரவைப் பயன்படுத்து"</string>
@@ -381,7 +383,7 @@
<string name="permlab_getPackageSize" msgid="7472921768357981986">"பயன்பாட்டுச் சேமிப்பு இடத்தை அளவிடல்"</string>
<string name="permdesc_getPackageSize" msgid="3921068154420738296">"ஆப்ஸ், அதன் குறியீடு, தரவு, மற்றும் தற்காலிகச் சேமிப்பு அளவுகளை மீட்டெடுக்க அனுமதிக்கிறது"</string>
<string name="permlab_writeSettings" msgid="2226195290955224730">"சாதன அமைப்புகளை மாற்றுதல்"</string>
- <string name="permdesc_writeSettings" msgid="7775723441558907181">"முறைமையின் அமைப்பு தரவைத் திருத்த, ஆப்ஸை அனுமதிக்கிறது. தீங்குவிளைவிக்கும் பயன்பாடுகள், முறைமையின் உள்ளமைவைச் சிதைக்கலாம்."</string>
+ <string name="permdesc_writeSettings" msgid="7775723441558907181">"முறைமையின் அமைப்பு தரவைத் திருத்த, ஆப்ஸை அனுமதிக்கிறது. தீங்குவிளைவிக்கும் ஆப்ஸ், முறைமையின் உள்ளமைவைச் சிதைக்கலாம்."</string>
<string name="permlab_receiveBootCompleted" msgid="5312965565987800025">"தொடக்கத்தில் இயக்குதல்"</string>
<string name="permdesc_receiveBootCompleted" product="tablet" msgid="7390304664116880704">"மறுஇயக்கம் முடிந்தது, விரைவில் தானாகவே தொடங்க, ஆப்ஸை அனுமதிக்கிறது. இதனால் டேப்லெட் நீண்ட நேரம் கழித்து தொடங்கும் மற்றும் எப்போதும் இயங்குகின்ற டேப்லெட்டின் ஒட்டுமொத்தச் செயல்பாட்டையும் தாமதமாகும்."</string>
<string name="permdesc_receiveBootCompleted" product="tv" msgid="6725487837446317527">"சாதனம் தொடங்கியவுடன் ஆப்ஸைத் தானாகவே தொடங்க அனுமதிக்கும். இது Android TV தொடங்குவதற்கான நேரத்தைத் தாமதமாக்குவதோடு எப்போதும் இயங்கிக்கொண்டிருப்பதன் மூலம் ஒட்டுமொத்த சாதனத்தின் வேகத்தைக் குறைக்கும்."</string>
@@ -391,9 +393,9 @@
<string name="permdesc_broadcastSticky" product="tv" msgid="5029460344724532288">"வலைபரப்பு முடிந்த பின்னரும் தங்கிவிடும் ஸ்டிக்கி வலைபரப்புகளை அனுப்ப ஆப்ஸை அனுமதிக்கும். அளவுக்கதிகமான உபயோகம் Android TVயின் வேகத்தைக் குறைக்கவோ நிலையற்றதாகவோ ஆக்கக்கூடும். இதனால் அதிகமான நினைவகம் பயன்படுத்தப்படும்."</string>
<string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"அலைபரப்பு முடிந்த பின்னும் இருக்கும், தொடர்ந்து அணுகத்தக்க அலைபரப்பை அனுப்பப் ஆப்ஸை அனுமதிக்கிறது. அதிகமாகப் பயன்படுத்தினால், மொபைலானது நினைவகத்தை மிக அதிகமாகப் பயன்படுத்துவதால் வேகம் குறைந்ததாகவும், நிலையற்றதாகவும் ஆகலாம்."</string>
<string name="permlab_readContacts" msgid="8348481131899886131">"உங்கள் தொடர்புகளைப் படித்தல்"</string>
- <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"குறிப்பிட்டவர்களுடன் நீங்கள் அழைத்த, மின்னஞ்சல் அனுப்பிய அல்லது வேறு வழியில் தொடர்புகொண்டதின் எண்ணிக்கை உட்பட, உங்கள் டேப்லெட்டில் சேமிக்கப்பட்ட உங்கள் தொடர்புகள் குறித்த தரவைப் படிக்க ஆப்ஸை அனுமதிக்கிறது. இந்த அனுமதி, உங்கள் தொடர்பு தரவைச் சேமிக்க ஆப்ஸை அனுமதிக்கிறது, மேலும் தீங்கிழைக்கும் பயன்பாடுகள் உங்களுக்குத் தெரியாமல் தொடர்பு தரவைப் பகிரலாம்."</string>
+ <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"குறிப்பிட்டவர்களுடன் நீங்கள் அழைத்த, மின்னஞ்சல் அனுப்பிய அல்லது வேறு வழியில் தொடர்புகொண்டதின் எண்ணிக்கை உட்பட, உங்கள் டேப்லெட்டில் சேமிக்கப்பட்ட உங்கள் தொடர்புகள் குறித்த தரவைப் படிக்க ஆப்ஸை அனுமதிக்கிறது. இந்த அனுமதி, உங்கள் தொடர்பு தரவைச் சேமிக்க ஆப்ஸை அனுமதிக்கிறது, மேலும் தீங்கிழைக்கும் ஆப்ஸ் உங்களுக்குத் தெரியாமல் தொடர்பு தரவைப் பகிரலாம்."</string>
<string name="permdesc_readContacts" product="tv" msgid="3890061004911027912">"உங்கள் Android TVயில் சேமித்துள்ள தொடர்புகள் பற்றிய தரவைத் தெரிந்துகொள்ள ஆப்ஸை அனுமதிக்கும். குறிப்பிட்ட தனிநபரை எத்தனை முறை அழைத்தீர்கள், பிறவழிகளில் தொடர்புகொண்டீர்கள் அல்லது அவருக்கு எத்தனை முறை மின்னஞ்சல் அனுப்பினீர்கள் என்பதும் இதில் அடங்கும். இது உங்கள் தொடர்புத் தரவைச் சேமிக்க ஆப்ஸை அனுமதிக்கும், அத்துடன் தீங்குவிளைவிக்கும் ஆப்ஸ் உங்களுக்குத் தெரியாமல் தொடர்புத் தரவைப் பகிரக்கூடும்."</string>
- <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"குறிப்பிட்டவர்களுடன் நீங்கள் அழைத்த, மின்னஞ்சல் அனுப்பிய அல்லது வேறு வழியில் தொடர்புகொண்ட எண்ணிக்கை உட்பட, உங்கள் மொபைலில் சேமிக்கப்பட்ட உங்கள் தொடர்புகள் குறித்த தரவைப் படிக்க ஆப்ஸை அனுமதிக்கிறது. இந்த அனுமதி, உங்கள் தொடர்பு தரவைச் சேமிக்க ஆப்ஸை அனுமதிக்கிறது, மேலும் தீங்கிழைக்கும் பயன்பாடுகள் உங்களுக்குத் தெரியாமல் தொடர்பு தரவைப் பகிரலாம்."</string>
+ <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"குறிப்பிட்டவர்களுடன் நீங்கள் அழைத்த, மின்னஞ்சல் அனுப்பிய அல்லது வேறு வழியில் தொடர்புகொண்ட எண்ணிக்கை உட்பட, உங்கள் மொபைலில் சேமிக்கப்பட்ட உங்கள் தொடர்புகள் குறித்த தரவைப் படிக்க ஆப்ஸை அனுமதிக்கிறது. இந்த அனுமதி, உங்கள் தொடர்பு தரவைச் சேமிக்க ஆப்ஸை அனுமதிக்கிறது, மேலும் தீங்கிழைக்கும் ஆப்ஸ் உங்களுக்குத் தெரியாமல் தொடர்பு தரவைப் பகிரலாம்."</string>
<string name="permlab_writeContacts" msgid="5107492086416793544">"உங்கள் தொடர்புகளை மாற்றுதல்"</string>
<string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"குறிப்பிட்ட தொடர்புகளுடன் நீங்கள் அழைத்த, மின்னஞ்சல் அனுப்பிய அல்லது வேறு வழியில் தொடர்புகொண்டதின் எண்ணிக்கை உள்பட, உங்கள் டேப்லெட்டில் சேமிக்கப்பட்ட உங்கள் தொடர்புகள் குறித்த தரவைத் திருத்த ஆப்ஸை அனுமதிக்கிறது. இந்த அனுமதியானது தொடர்புத் தரவை நீக்க ஆப்ஸை அனுமதிக்கிறது."</string>
<string name="permdesc_writeContacts" product="tv" msgid="307929337692573341">"உங்கள் Android TVயில் சேமித்துள்ள தொடர்புகள் பற்றிய தரவை மாற்ற ஆப்ஸை அனுமதிக்கும். குறிப்பிட்ட தொடர்பை எத்தனை முறை அழைத்தீர்கள், பிறவழிகளில் தொடர்புகொண்டீர்கள் அல்லது அவருக்கு எத்தனை முறை மின்னஞ்சல் அனுப்பினீர்கள் என்பதும் இதில் அடங்கும். தொடர்புத் தரவை நீக்க ஆப்ஸை இது அனுமதிக்கும்."</string>
@@ -401,9 +403,9 @@
<string name="permlab_readCallLog" msgid="3478133184624102739">"அழைப்புப் பதிவைப் படித்தல்"</string>
<string name="permdesc_readCallLog" msgid="3204122446463552146">"இந்த ஆப்ஸ் உங்கள் அழைப்பு வரலாற்றைப் படிக்கலாம்."</string>
<string name="permlab_writeCallLog" msgid="8552045664743499354">"அழைப்புப் பதிவை எழுதுதல்"</string>
- <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"உள்வரும் மற்றும் வெளிச்செல்லும் அழைப்புகள் குறித்த தகவல் உள்பட உங்கள் டேப்லெட்டின் அழைப்புப் பதிவைத் திருத்துவதற்குப் ஆப்ஸை அனுமதிக்கிறது. உங்கள் அழைப்பின் பதிவை அழிக்க அல்லது திருத்த தீங்கு விளைவிக்கும் பயன்பாடுகள் இதைப் பயன்படுத்தலாம்."</string>
+ <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"உள்வரும் மற்றும் வெளிச்செல்லும் அழைப்புகள் குறித்த தகவல் உள்பட உங்கள் டேப்லெட்டின் அழைப்புப் பதிவைத் திருத்துவதற்குப் ஆப்ஸை அனுமதிக்கிறது. உங்கள் அழைப்பின் பதிவை அழிக்க அல்லது திருத்த தீங்கு விளைவிக்கும் ஆப்ஸ் இதைப் பயன்படுத்தலாம்."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="7939219462637746280">"உள்வரும், வெளிச்செல்லும் அழைப்புகள் குறித்த தகவல் உட்பட உங்கள் Android TVயின் அழைப்புப் பதிவைத் திருத்த ஆப்ஸை அனுமதிக்கும். உங்கள் அழைப்புப் பதிவை அழிக்கவோ திருத்தவோ தீங்கு விளைவிக்கும் ஆப்ஸ் இதைப் பயன்படுத்தக்கூடும்."</string>
- <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"உள்வரும் மற்றும் வெளிச்செல்லும் அழைப்புகள் குறித்த தகவல் உள்பட உங்கள் மொபைல் அழைப்புப் பதிவைத் திருத்துவதற்குப் ஆப்ஸை அனுமதிக்கிறது. உங்கள் அழைப்பின் பதிவை அழிக்க அல்லது திருத்த தீங்கு விளைவிக்கும் பயன்பாடுகள் இதைப் பயன்படுத்தலாம்."</string>
+ <string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"உள்வரும் மற்றும் வெளிச்செல்லும் அழைப்புகள் குறித்த தகவல் உள்பட உங்கள் மொபைல் அழைப்புப் பதிவைத் திருத்துவதற்குப் ஆப்ஸை அனுமதிக்கிறது. உங்கள் அழைப்பின் பதிவை அழிக்க அல்லது திருத்த தீங்கு விளைவிக்கும் ஆப்ஸ் இதைப் பயன்படுத்தலாம்."</string>
<string name="permlab_bodySensors" msgid="4683341291818520277">"உடல் உணர்விகளை (இதயத் துடிப்பு மானிட்டர்கள் போன்றவை) அணுகுதல்"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"உங்கள் இதயத்துடிப்பு விகிதம் போன்ற உங்கள் உடல்நிலையைக் கண்காணிக்கும் உணர்விகளில் இருந்து தரவை அணுக ஆப்ஸை அனுமதிக்கும்."</string>
<string name="permlab_readCalendar" msgid="6716116972752441641">"கேலெண்டர் நிகழ்வுகளையும் விவரங்களையும் படிக்கலாம்"</string>
@@ -439,7 +441,7 @@
<string name="permlab_vibrate" msgid="7696427026057705834">"அதிர்வைக் கட்டுப்படுத்துதல்"</string>
<string name="permdesc_vibrate" msgid="6284989245902300945">"அதிர்வைக் கட்டுப்படுத்தப் ஆப்ஸை அனுமதிக்கிறது."</string>
<string name="permlab_callPhone" msgid="3925836347681847954">"தொலைபேசி எண்களை நேரடியாக அழைத்தல்"</string>
- <string name="permdesc_callPhone" msgid="3740797576113760827">"உங்கள் தலையீட்டின்றி மொபைல் எண்களை அழைக்கப் ஆப்ஸை அனுமதிக்கிறது. இதன் விளைவாக எதிர்பாராத கட்டணங்களோ அழைப்புகளோ ஏற்படலாம். அவசரகால எண்களை அழைக்க இது ஆப்ஸை அனுமதிக்காது என்பதை நினைவில்கொள்ளவும். தீங்கிழைக்கும் பயன்பாடுகள், உங்கள் உறுதிப்படுத்தல் இன்றி அழைப்புகளைச் செய்வதால் உங்களுக்குச் செலவு ஏற்படக்கூடும்."</string>
+ <string name="permdesc_callPhone" msgid="3740797576113760827">"உங்கள் தலையீட்டின்றி மொபைல் எண்களை அழைக்கப் ஆப்ஸை அனுமதிக்கிறது. இதன் விளைவாக எதிர்பாராத கட்டணங்களோ அழைப்புகளோ ஏற்படலாம். அவசரகால எண்களை அழைக்க இது ஆப்ஸை அனுமதிக்காது என்பதை நினைவில்கொள்ளவும். தீங்கிழைக்கும் ஆப்ஸ், உங்கள் உறுதிப்படுத்தல் இன்றி அழைப்புகளைச் செய்வதால் உங்களுக்குச் செலவு ஏற்படக்கூடும்."</string>
<string name="permlab_accessImsCallService" msgid="3574943847181793918">"IMS அழைப்புச் சேவையை அணுகுதல்"</string>
<string name="permdesc_accessImsCallService" msgid="8992884015198298775">"உங்கள் குறுக்கீடின்றி IMS சேவையைப் பயன்படுத்தி அழைப்பதற்கு, ஆப்ஸை அனுமதிக்கும்."</string>
<string name="permlab_readPhoneState" msgid="9178228524507610486">"மொபைல் நிலை மற்றும் அடையாளத்தைப் படித்தல்"</string>
@@ -939,15 +941,15 @@
<string name="permlab_readHistoryBookmarks" msgid="3775265775405106983">"உங்கள் இணையப் புத்தக்கக்குறிகள் மற்றும் வரலாற்றைப் படித்தல்"</string>
<string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"உலாவி மூலம் பார்வையிட்ட எல்லா URLகளின் வரலாற்றையும், உலாவியில் குறிக்கப்பட்ட எல்லா புத்தகக்குறிகளையும் படிக்கப் ஆப்ஸை அனுமதிக்கிறது. குறிப்பு: மூன்றாம் தரப்பு உலாவிகள் அல்லது இணைய உலாவல் திறன்களுடன் கூடிய பிற பயன்பாடுகளால் இந்த அனுமதி செயற்படுத்தப்படாமல் போகலாம்."</string>
<string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"இணையப் புத்தகக்குறிகளையும், வரலாற்றையும் எழுதுதல்"</string>
- <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"உங்கள் டேப்லெட்டில் சேமிக்கப்பட்ட உலாவியின் வரலாறு அல்லது புத்தகக்குறிகளைத் திருத்த ஆப்ஸை அனுமதிக்கிறது. இது உலாவியின் தரவை அழிக்கவோ, திருத்தவோ ஆப்ஸை அனுமதிக்கலாம். குறிப்பு: இணைய உலாவல் செயல்திறன்கள் மூலம் மூன்றாம் தரப்பு உலாவிகள் அல்லது பிற பயன்பாடுகள் இந்த அனுமதியைச் செயற்படுத்த முடியாது."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"உங்கள் டேப்லெட்டில் சேமிக்கப்பட்ட உலாவியின் வரலாறு அல்லது புத்தகக்குறிகளைத் திருத்த ஆப்ஸை அனுமதிக்கிறது. இது உலாவியின் தரவை அழிக்கவோ, திருத்தவோ ஆப்ஸை அனுமதிக்கலாம். குறிப்பு: இணைய உலாவல் செயல்திறன்கள் மூலம் மூன்றாம் தரப்பு உலாவிகள் அல்லது பிற ஆப்ஸ் இந்த அனுமதியைச் செயற்படுத்த முடியாது."</string>
<string name="permdesc_writeHistoryBookmarks" product="tv" msgid="6340829212433680418">"Android TVயில் சேமித்துள்ள உலாவியின் மூலம் பார்க்கப்பட்ட தளங்களையோ புக்மார்க்குகளையோ திருத்த ஆப்ஸை அனுமதிக்கும். இது உலாவியின் தரவை அழிக்கவோ திருத்தவோ ஆப்ஸை அனுமதிக்கக்கூடும். கவனத்திற்கு: மூன்றாம் தரப்பு உலாவிகளோ இணைய உலாவல் திறன்களுடன் கூடிய பிற ஆப்ஸோ இந்த அனுமதியைச் செயல்படுத்த முடியாது."</string>
- <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"உங்கள் மொபைலில் சேமிக்கப்பட்ட உலாவியின் வரலாறு அல்லது புத்தகக்குறிகளைத் திருத்த ஆப்ஸை அனுமதிக்கிறது. இது உலாவியின் தரவை அழிக்கவோ, திருத்தவோ ஆப்ஸை அனுமதிக்கலாம். குறிப்பு: இணைய உலாவல் செயல்திறன்கள் மூலம் மூன்றாம் தரப்பு உலாவிகள் அல்லது பிற பயன்பாடுகள் இந்த அனுமதியைச் செயற்படுத்த முடியாது."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"உங்கள் மொபைலில் சேமிக்கப்பட்ட உலாவியின் வரலாறு அல்லது புத்தகக்குறிகளைத் திருத்த ஆப்ஸை அனுமதிக்கிறது. இது உலாவியின் தரவை அழிக்கவோ, திருத்தவோ ஆப்ஸை அனுமதிக்கலாம். குறிப்பு: இணைய உலாவல் செயல்திறன்கள் மூலம் மூன்றாம் தரப்பு உலாவிகள் அல்லது பிற ஆப்ஸ் இந்த அனுமதியைச் செயற்படுத்த முடியாது."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"அலாரத்தை அமைத்தல்"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"நிறுவிய அலார கடிகாரப் பயன்பாட்டில் அலாரத்தை அமைக்க, ஆப்ஸை அனுமதிக்கிறது. சில அலார கடிகார பயன்பாடுகளில் இந்த அம்சம் இல்லாமல் இருக்கலாம்."</string>
<string name="permlab_addVoicemail" msgid="5525660026090959044">"குரலஞ்சலைச் சேர்த்தல்"</string>
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"குரலஞ்சல் இன்பாக்ஸில் செய்திகளைச் சேர்க்க, ஆப்ஸை அனுமதிக்கிறது."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"உலாவியின் புவியியல் இருப்பிடம் சார்ந்த அனுமதிகளைத் திருத்துதல்"</string>
- <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"உலாவியின் புவியியல் இருப்பிடம் சார்ந்த அனுமதிகளைத் திருத்த, ஆப்ஸை அனுமதிக்கிறது. இடத் தகவலை தன்னிச்சையான இணையதளங்களுக்கு அனுப்புவதை அனுமதிக்க, தீங்குவிளைவிக்கும் பயன்பாடுகள் இதைப் பயன்படுத்தலாம்."</string>
+ <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"உலாவியின் புவியியல் இருப்பிடம் சார்ந்த அனுமதிகளைத் திருத்த, ஆப்ஸை அனுமதிக்கிறது. இடத் தகவலை தன்னிச்சையான இணையதளங்களுக்கு அனுப்புவதை அனுமதிக்க, தீங்குவிளைவிக்கும் ஆப்ஸ் இதைப் பயன்படுத்தலாம்."</string>
<string name="save_password_message" msgid="767344687139195790">"இந்தக் கடவுச்சொல்லை உலாவி நினைவில்கொள்ள விரும்புகிறீர்களா?"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"இப்போது இல்லை"</string>
<string name="save_password_remember" msgid="6491879678996749466">"நினைவில்கொள்"</string>
@@ -1156,10 +1158,10 @@
<string name="whichImageCaptureApplicationLabel" msgid="6390303445371527066">"படமெடு"</string>
<string name="alwaysUse" msgid="4583018368000610438">"இந்தச் செயலுக்கு இயல்பாகப் பயன்படுத்து."</string>
<string name="use_a_different_app" msgid="8134926230585710243">"வேறு ஆப்ஸைப் பயன்படுத்தவும்"</string>
- <string name="clearDefaultHintMsg" msgid="3252584689512077257">"முறைமை அமைப்பு &gt; பயன்பாடுகள் &gt; பதிவிறக்கியவை என்பதில் உள்ள இயல்பை அழிக்கவும்."</string>
+ <string name="clearDefaultHintMsg" msgid="3252584689512077257">"முறைமை அமைப்பு &gt; ஆப்ஸ் &gt; பதிவிறக்கியவை என்பதில் உள்ள இயல்பை அழிக்கவும்."</string>
<string name="chooseActivity" msgid="7486876147751803333">"செயலைத் தேர்ந்தெடுக்கவும்"</string>
<string name="chooseUsbActivity" msgid="6894748416073583509">"USB சாதனத்திற்கான பயன்பாட்டைத் தேர்வுசெய்க"</string>
- <string name="noApplications" msgid="2991814273936504689">"இந்தச் செயலைச் செய்ய பயன்பாடுகள் எதுவுமில்லை."</string>
+ <string name="noApplications" msgid="2991814273936504689">"இந்தச் செயலைச் செய்ய ஆப்ஸ் எதுவுமில்லை."</string>
<string name="aerr_application" msgid="250320989337856518">"<xliff:g id="APPLICATION">%1$s</xliff:g> செயலிழந்தது"</string>
<string name="aerr_process" msgid="6201597323218674729">"<xliff:g id="PROCESS">%1$s</xliff:g> செயலிழந்தது"</string>
<string name="aerr_application_repeated" msgid="3146328699537439573">"<xliff:g id="APPLICATION">%1$s</xliff:g> தொடர்ந்து செயலிழக்கிறது"</string>
@@ -1184,7 +1186,7 @@
<string name="launch_warning_original" msgid="188102023021668683">"<xliff:g id="APP_NAME">%1$s</xliff:g> உண்மையாக வெளியிடப்பட்டது."</string>
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"அளவு"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"எப்போதும் காட்டு"</string>
- <string name="screen_compat_mode_hint" msgid="1064524084543304459">"சிஸ்டம் அமைப்பு &gt; பயன்பாடுகள் &gt; பதிவிறக்கம் என்பதில் இதை மீண்டும் இயக்கவும்."</string>
+ <string name="screen_compat_mode_hint" msgid="1064524084543304459">"சிஸ்டம் அமைப்பு &gt; ஆப்ஸ் &gt; பதிவிறக்கம் என்பதில் இதை மீண்டும் இயக்கவும்."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"தற்போதைய திரை அளவு அமைப்பை <xliff:g id="APP_NAME">%1$s</xliff:g> ஆதரிக்காததால், அது வழக்கத்திற்கு மாறாகச் செயல்படக்கூடும்."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"எப்போதும் காட்டு"</string>
<string name="unsupported_compile_sdk_message" msgid="4253168368781441759">"<xliff:g id="APP_NAME">%1$s</xliff:g> பயன்பாடானது, இந்தச் சாதனத்தின் Android OSக்கு இணக்கமற்ற பதிப்பிற்காக உருவாக்கப்பட்டதால், இதில் சரியாகச் செயல்படாது. இந்த ஆப்ஸின் புதுப்பிக்கப்பட்ட பதிப்பானது தற்போது கிடைக்கக்கூடும்."</string>
@@ -1204,7 +1206,7 @@
<string name="app_upgrading_toast" msgid="3008139776215597053">"<xliff:g id="APPLICATION">%1$s</xliff:g>ஐ மேம்படுத்துகிறது…"</string>
<string name="android_upgrading_apk" msgid="7904042682111526169">"<xliff:g id="NUMBER_0">%1$d</xliff:g> / <xliff:g id="NUMBER_1">%2$d</xliff:g> ஆப்ஸை ஒருங்கிணைக்கிறது."</string>
<string name="android_preparing_apk" msgid="8162599310274079154">"<xliff:g id="APPNAME">%1$s</xliff:g>ஐத் தயார்செய்கிறது."</string>
- <string name="android_upgrading_starting_apps" msgid="451464516346926713">"பயன்பாடுகள் தொடங்கப்படுகின்றன."</string>
+ <string name="android_upgrading_starting_apps" msgid="451464516346926713">"ஆப்ஸ் தொடங்கப்படுகின்றன."</string>
<string name="android_upgrading_complete" msgid="1405954754112999229">"துவக்குதலை முடிக்கிறது."</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> இயங்குகிறது"</string>
<string name="heavy_weight_notification_detail" msgid="2304833848484424985">"கேமிற்குச் செல்ல, தட்டவும்"</string>
@@ -1322,7 +1324,7 @@
<string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"அனுப்பு"</string>
<string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"ரத்துசெய்"</string>
<string name="sms_short_code_remember_choice" msgid="5289538592272218136">"எனது விருப்பத்தேர்வை நினைவில்கொள்"</string>
- <string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"அமைப்பு &gt; பயன்பாடுகள் என்பதில் பிறகு நீங்கள் மாற்றலாம்"</string>
+ <string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"அமைப்பு &gt; ஆப்ஸ் என்பதில் பிறகு நீங்கள் மாற்றலாம்"</string>
<string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"எப்போதும் அனுமதி"</string>
<string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"ஒருபோதும் அனுமதிக்காதே"</string>
<string name="sim_removed_title" msgid="6227712319223226185">"சிம் கார்டு அகற்றப்பட்டது"</string>
@@ -1346,7 +1348,7 @@
<string name="no_permissions" msgid="7283357728219338112">"அனுமதிகள் தேவையில்லை"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"இதனால் நீங்கள் கட்டணம் செலுத்த வேண்டியிருக்கலாம்"</string>
<string name="dlg_ok" msgid="7376953167039865701">"சரி"</string>
- <string name="usb_charging_notification_title" msgid="1595122345358177163">"USB மூலமாக இந்தச் சாதனம் சார்ஜ் ஆகிறது"</string>
+ <string name="usb_charging_notification_title" msgid="1595122345358177163">"USBயில் சாதனம் சார்ஜ் ஆகிறது"</string>
<string name="usb_supplying_notification_title" msgid="4631045789893086181">"USB மூலமாக இணைக்கப்பட்ட சாதனம் சார்ஜ் ஆகிறது"</string>
<string name="usb_mtp_notification_title" msgid="4238227258391151029">"USB மூலமாக ஃபைல் பரிமாற்றம் ஆன் செய்யப்பட்டது"</string>
<string name="usb_ptp_notification_title" msgid="5425857879922006878">"USB மூலமாக PTP பயன்முறை ஆன் செய்யப்பட்டது"</string>
@@ -1358,10 +1360,12 @@
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"அனலாக் ஆடியோ துணைக்கருவி கண்டறியப்பட்டது"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"இணைத்துள்ள சாதனமானது இந்த மொபைலுடன் இணங்கவில்லை. மேலும் அறிய, தட்டவும்."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB பிழைதிருத்தம் இணைக்கப்பட்டது"</string>
- <string name="adb_active_notification_message" msgid="7463062450474107752">"USB பிழைதிருத்தத்தை ஆஃப் செய்ய, தட்டவும்"</string>
+ <string name="adb_active_notification_message" msgid="7463062450474107752">"USB பிழைதிருத்தத்தை ஆஃப் செய்ய தட்டவும்"</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB பிழைதிருத்தத்தை முடக்க, தேர்ந்தெடுக்கவும்."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"\'தன்னியக்க சோதனைப்\' பயன்முறை இயக்கப்பட்டது"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"’தன்னியக்க சோதனைப்\' பயன்முறையை முடக்க ஆரம்பநிலைக்கு மீட்டமைக்கவும்."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"சீரியல் கன்சோல் இயக்கப்பட்டது"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"செயல்திறன் பாதிக்கப்பட்டுள்ளது. முடக்குவதற்கு பூட்லோடரைத் தேர்வுசெய்யவும்."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB போர்ட்டில் சேதம் உள்ளது"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB போர்ட் தானாகவே முடக்கப்பட்டது மேலும் அறிய, தட்டவும்."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB போர்ட்டைப் பயன்படுத்தலாம்"</string>
@@ -1446,7 +1450,7 @@
<string name="ime_action_default" msgid="2840921885558045721">"செயலாக்கு"</string>
<string name="dial_number_using" msgid="5789176425167573586">"<xliff:g id="NUMBER">%s</xliff:g> ஐப் பயன்படுத்தி\nஅழை"</string>
<string name="create_contact_using" msgid="4947405226788104538">"<xliff:g id="NUMBER">%s</xliff:g> ஐப்\nபயன்படுத்தி தொடர்பை உருவாக்கு"</string>
- <string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"பின்வரும் ஒன்று அல்லது அதற்கு மேற்பட்ட பயன்பாடுகள், இப்போதும் எதிர்காலத்திலும் உங்கள் கணக்கை அணுகுவதற்கான அனுமதியைக் கோருகின்றன."</string>
+ <string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"பின்வரும் ஒன்று அல்லது அதற்கு மேற்பட்ட ஆப்ஸ், இப்போதும் எதிர்காலத்திலும் உங்கள் கணக்கை அணுகுவதற்கான அனுமதியைக் கோருகின்றன."</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"இந்தக் கோரிக்கையை அனுமதிக்க விரும்புகிறீர்களா?"</string>
<string name="grant_permissions_header_text" msgid="6874497408201826708">"‍அணுகல் கோரிக்கை"</string>
<string name="allow" msgid="7225948811296386551">"அனுமதி"</string>
@@ -1580,8 +1584,8 @@
<string name="expires_on" msgid="3676242949915959821">"காலாவதியாவது:"</string>
<string name="serial_number" msgid="758814067660862493">"வரிசை எண்:"</string>
<string name="fingerprints" msgid="4516019619850763049">"கைரேகைகள்:"</string>
- <string name="sha256_fingerprint" msgid="4391271286477279263">"SHA-256 கைரேகை:"</string>
- <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 கைரேகை:"</string>
+ <string name="sha256_fingerprint" msgid="4391271286477279263">"SHA-256 ஃபிங்கர்பிரிண்ட்:"</string>
+ <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 ஃபிங்கர்பிரிண்ட்:"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"எல்லாம் காட்டு"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"செயல்பாட்டைத் தேர்வுசெய்க"</string>
<string name="share_action_provider_share_with" msgid="5247684435979149216">"இதனுடன் பகிர்"</string>
@@ -1891,10 +1895,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"வகைப்படுத்தப்படாதவை"</string>
<string name="importance_from_user" msgid="7318955817386549931">"இந்த அறிவிப்புகளின் முக்கியத்துவத்தை அமைத்துள்ளீர்கள்."</string>
<string name="importance_from_person" msgid="9160133597262938296">"ஈடுபட்டுள்ளவர்களின் காரணமாக, இது முக்கியமானது."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="ACCOUNT">%2$s</xliff:g> மூலம் புதிய பயனரை உருவாக்க <xliff:g id="APP">%1$s</xliff:g> ஆப்ஸை அனுமதிக்கவா (இந்தக் கணக்கில் ஏற்கெனவே ஒரு பயனர் உள்ளார்) ?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"<xliff:g id="ACCOUNT">%2$s</xliff:g> மூலம் புதிய பயனரை உருவாக்க <xliff:g id="APP">%1$s</xliff:g> ஆப்ஸை அனுமதிக்கவா?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"மொழியைச் சேர்"</string>
<string name="country_selection_title" msgid="2954859441620215513">"மண்டல விருப்பம்"</string>
<string name="search_language_hint" msgid="7042102592055108574">"மொழி பெயரை உள்ளிடுக"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index a8550f860cc9..72b83c4d944c 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"SMS సందేశాలను స్వీకరించడానికి మరియు ప్రాసెస్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. యాప్ మీ డివైజ్‌కు పంపబడిన సందేశాలను మీకు చూపకుండానే పర్యవేక్షించగలదని లేదా తొలగించగలదని దీని అర్థం."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"వచన సందేశాలను (MMS) స్వీకరించడం"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"MMS సందేశాలను స్వీకరించడానికి మరియు ప్రాసెస్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. యాప్ మీ డివైజ్‌కు పంపబడిన సందేశాలను మీకు చూపకుండానే పర్యవేక్షించగలదని లేదా తొలగించగలదని దీని అర్థం."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"సెల్ ప్రసార సందేశాలను ఫార్వర్డ్ చేయడం"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"సెల్ ప్రసార సందేశాలను అందుకుంటే, వాటిని ఫార్వర్డ్ చేసే విధంగా సెల్ ప్రసార మాడ్యూల్‌కు కట్టుబడి ఉండటానికి యాప్‌ను అనుమతిస్తుంది. సెల్ ప్రసార హెచ్చరికలు అత్యవసర పరిస్థితుల గురించి మిమ్మల్ని హెచ్చరించడానికి కొన్ని స్థానాల్లో అందించబడతాయి. అత్యవసర సెల్ ప్రసారం అందుకున్నప్పుడు హానికరమైన యాప్‌లు మీ పరికరం యొక్క పనితీరు లేదా నిర్వహణకు అంతరాయం కలిగించవచ్చు."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"సెల్ ప్రసార సందేశాలను చదవడం"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"మీ పరికరం స్వీకరించిన సెల్ ప్రసార సందేశాలను చదవడానికి యాప్‌ను అనుమతిస్తుంది. సెల్ ప్రసార హెచ్చరికలు అత్యవసర పరిస్థితుల గురించి మిమ్మల్ని హెచ్చరించడానికి కొన్ని స్థానాల్లో అందించబడతాయి. అత్యవసర సెల్ ప్రసారం స్వీకరించినప్పుడు హానికరమైన యాప్‌లు మీ పరికరం యొక్క పనితీరు లేదా నిర్వహణకు అంతరాయం కలిగించవచ్చు."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"చందా చేయబడిన ఫీడ్‌లను చదవడం"</string>
@@ -513,9 +515,9 @@
<string name="permlab_useBiometric" msgid="8837753668509919318">"బయోమెట్రిక్ హార్డ్‌వేర్‌ని ఉపయోగించు"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"ప్రమాణీకరణ కోసం బయోమెట్రిక్ హార్డ్‌వేర్‌ను ఉపయోగించడానికి యాప్‌ని అనుమతిస్తుంది"</string>
<string name="permlab_manageFingerprint" msgid="5640858826254575638">"వేలిముద్ర హార్డ్‌వేర్‌ని నిర్వహించడానికి అనుమతి"</string>
- <string name="permdesc_manageFingerprint" msgid="178208705828055464">"వినియోగం కోసం వేలిముద్ర టెంప్లేట్‌లను జోడించే మరియు తొలగించే పద్ధతులను అమలు చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+ <string name="permdesc_manageFingerprint" msgid="178208705828055464">"వినియోగం కోసం వేలిముద్ర టెంప్లేట్‌లను జోడించే, తొలగించే పద్ధతులను అమలు చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"వేలిముద్ర హార్డ్‌వేర్‌ని ఉపయోగించడానికి అనుమతి"</string>
- <string name="permdesc_useFingerprint" msgid="9165097460730684114">"ప్రామాణీకరణ కోసం వేలిముద్ర హార్డ్‌వేర్‌ను ఉపయోగించడానికి అనువర్తనాన్ని అనుమతిస్తుంది"</string>
+ <string name="permdesc_useFingerprint" msgid="9165097460730684114">"ప్రామాణీకరణ కోసం వేలిముద్ర హార్డ్‌వేర్‌ను ఉపయోగించడానికి యాప్‌ను అనుమతిస్తుంది"</string>
<string name="permlab_audioWrite" msgid="2661772059799779292">"మీ సంగీత సేకరణను సవరించండి"</string>
<string name="permdesc_audioWrite" msgid="8888544708166230494">"మీ సంగీత సేకరణని సవరించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_videoWrite" msgid="128769316366746446">"మీ వీడియో సేకరణను సవరించండి"</string>
@@ -1330,7 +1332,7 @@
<string name="sim_done_button" msgid="827949989369963775">"పూర్తయింది"</string>
<string name="sim_added_title" msgid="3719670512889674693">"సిమ్ కార్డు జోడించబడింది"</string>
<string name="sim_added_message" msgid="6599945301141050216">"మొబైల్ నెట్‌వర్క్‌ను యాక్సెస్ చేయడానికి మీ పరికరాన్ని పునఃప్రారంభించండి."</string>
- <string name="sim_restart_button" msgid="4722407842815232347">"పునఃప్రారంభించు"</string>
+ <string name="sim_restart_button" msgid="4722407842815232347">"రీస్టార్ట్ చేయి"</string>
<string name="install_carrier_app_notification_title" msgid="9056007111024059888">"మొబైల్ సేవను సక్రియం చేయండి"</string>
<string name="install_carrier_app_notification_text" msgid="3346681446158696001">"మీ కొత్త SIMని సక్రియం చేయడానికి క్యారియర్ యాప్‌ను డౌన్‌లోడ్ చేయండి"</string>
<string name="install_carrier_app_notification_text_app_name" msgid="1196505084835248137">"మీ కొత్త SIMని సక్రియం చేయడం కోసం <xliff:g id="APP_NAME">%1$s</xliff:g> యాప్‌ని డౌన్‌లోడ్ చేయండి"</string>
@@ -1362,6 +1364,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"డీబగ్గింగ్‌ని నిలిపివేయడానికి ఎంచుకోండి."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"పరీక్ష నియంత్రణ మోడ్ ప్రారంభించబడింది"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"పరీక్ష నియంత్రణ మోడ్‌ను నిలిపివేయడానికి ఫ్యాక్టరీ రీసెట్‍‌ను అమలు చేయండి."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"సీరియల్ కన్సోల్ ప్రారంభించబడింది"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"పని తీరు ప్రభావితమైంది. నిలిపివేయడానికి, బూట్‌లోడర్‌ను తనిఖీ చేయండి."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB పోర్ట్‌లో ద్రవ లేదా వ్యర్థ పదార్థాలు ఉన్నాయి"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB పోర్ట్ ఆటోమేటిక్‌గా నిలిపివేయబడింది. మరింత తెలుసుకోవడానికి నొక్కండి."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB పోర్ట్‌ను ఉపయోగించడం సురక్షితం"</string>
@@ -1891,10 +1895,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"వర్గీకరించబడలేదు"</string>
<string name="importance_from_user" msgid="7318955817386549931">"మీరు ఈ నోటిఫికేషన్‌ల ప్రాముఖ్యతను సెట్ చేసారు."</string>
<string name="importance_from_person" msgid="9160133597262938296">"ఇందులో పేర్కొనబడిన వ్యక్తులను బట్టి ఇది చాలా ముఖ్యమైనది."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="ACCOUNT">%2$s</xliff:g>తో కొత్త వినియోగదారుని సృష్టించడానికి <xliff:g id="APP">%1$s</xliff:g>ను అనుమతించాలా (ఈ ఖాతాతో ఇప్పటికే ఒక వినియోగదారు ఉన్నారు) ?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"<xliff:g id="ACCOUNT">%2$s</xliff:g>తో కొత్త వినియోగదారుని సృష్టించడానికి <xliff:g id="APP">%1$s</xliff:g>ను అనుమతించాలా?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"భాషను జోడించండి"</string>
<string name="country_selection_title" msgid="2954859441620215513">"ప్రాంతం ప్రాధాన్యత"</string>
<string name="search_language_hint" msgid="7042102592055108574">"భాష పేరును టైప్ చేయండి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index f2376d7959d2..caaa32d5f358 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -283,7 +283,7 @@
<string name="permgrouprequest_contacts" msgid="6032805601881764300">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงรายชื่อติดต่อไหม"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"ตำแหน่ง"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"เข้าถึงตำแหน่งของอุปกรณ์นี้"</string>
- <string name="permgrouprequest_location" msgid="3788275734953323491">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงตำแหน่งของอุปกรณ์นี้ไหม"</string>
+ <string name="permgrouprequest_location" msgid="3788275734953323491">"อนุญาตให้&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;เข้าถึงตำแหน่งของอุปกรณ์นี้ไหม"</string>
<string name="permgrouprequestdetail_location" msgid="1347189607421252902">"แอปจะมีสิทธิ์เข้าถึงตำแหน่งในขณะที่คุณใช้แอปเท่านั้น"</string>
<string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงตำแหน่งของอุปกรณ์นี้&lt;b&gt;ตลอดเวลา&lt;/b&gt;ไหม"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"ปัจจุบันแอปมีสิทธิ์เข้าถึงตำแหน่งในขณะที่คุณใช้แอปเท่านั้น"</string>
@@ -298,7 +298,7 @@
<string name="permgrouprequest_storage" msgid="7885942926944299560">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงรูปภาพ สื่อ และไฟล์ในอุปกรณ์ไหม"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"ไมโครโฟน"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"บันทึกเสียง"</string>
- <string name="permgrouprequest_microphone" msgid="9167492350681916038">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; บันทึกเสียงไหม"</string>
+ <string name="permgrouprequest_microphone" msgid="9167492350681916038">"อนุญาตให้&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;บันทึกเสียงไหม"</string>
<string name="permgrouplab_activityRecognition" msgid="1565108047054378642">"การเคลื่อนไหวร่างกาย"</string>
<string name="permgroupdesc_activityRecognition" msgid="6949472038320473478">"เข้าถึงกิจกรรมการเคลื่อนไหวร่างกายของคุณ"</string>
<string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงกิจกรรมการเคลื่อนไหวร่างกายของคุณไหม"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"อนุญาตให้แอปพลิเคชันรับและประมวลผลข้อความ SMS ซึ่งหมายความว่าแอปพลิเคชันจะสามารถตรวจสอบหรือลบข้อความที่ส่งมายังอุปกรณ์ของคุณได้โดยไม่ต้องแสดงให้คุณเห็น"</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"รับข้อความ (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"อนุญาตให้แอปพลิเคชันรับและประมวลผลข้อความ MMS ซึ่งหมายความว่าแอปพลิเคชันจะสามารถตรวจสอบหรือลบข้อความที่ส่งมายังอุปกรณ์ของคุณได้โดยไม่ต้องแสดงให้คุณเห็น"</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"ส่งต่อข้อความจากการส่งข้อมูลเตือนภัยทางมือถือ (CB)"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"อนุญาตให้แอปเชื่อมโยงกับโมดูลการส่งข้อมูลเตือนภัยทางมือถือ (CB) เพื่อส่งต่อข้อความจากการส่งข้อมูลเตือนภัยทางมือถือ (CB) ทันทีที่ได้รับ ระบบจะส่งการแจ้งเตือนจากการส่งข้อมูลเตือนภัยทางมือถือ (CB) ในบางตำแหน่งเพื่อแจ้งเตือนคุณเกี่ยวกับสถานการณ์ฉุกเฉิน แอปที่เป็นอันตรายอาจรบกวนประสิทธิภาพหรือการทำงานของอุปกรณ์เมื่อได้รับการส่งข้อมูลเตือนภัยทางมือถือ (CB) ในกรณีฉุกเฉิน"</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"อ่านข้อความที่ได้รับจากสถานีมือถือ"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"อนุญาตให้แอปอ่านข้อความจากสถานีมือถือที่อุปกรณ์ได้รับ การแจ้งเตือนทางมือถือมีให้บริการในบางพื้นที่ โดยจะแจ้งเตือนคุณเกี่ยวกับสถานการณ์ฉุกเฉิน แอปที่เป็นอันตรายอาจเข้าแทรกแซงการทำงานของอุปกรณ์เมื่อได้รับข้อความแจ้งเตือนฉุกเฉิน"</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"อ่านฟีดข้อมูลที่สมัครไว้"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"เลือกเพื่อปิดใช้งานการแก้ไขข้อบกพร่อง USB"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"โหมดโปรแกรมทดสอบอัตโนมัติเปิดใช้อยู่"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"รีเซ็ตเป็นค่าเริ่มต้นเพื่อปิดใช้โหมดโปรแกรมทดสอบอัตโนมัติ"</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"เปิดใช้คอนโซลการเรียงอันดับแล้ว"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"ประสิทธิภาพได้รับผลกระทบ ตรวจสอบ Bootloader เพื่อปิดใช้งาน"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"มีของเหลวหรือฝุ่นละอองในพอร์ต USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"พอร์ต USB ปิดใช้โดยอัตโนมัติ แตะเพื่อดูข้อมูลเพิ่มเติม"</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"ใช้พอร์ต USB ได้แล้ว"</string>
@@ -1547,7 +1551,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"ตัวเลือกเพิ่มเติม"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <string name="storage_internal" msgid="3570990907910199483">"ที่จัดเก็บข้อมูลที่ใช้ร่วมกันภายใน"</string>
+ <string name="storage_internal" msgid="3570990907910199483">"พื้นที่จัดเก็บข้อมูลที่ใช้ร่วมกันภายใน"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"การ์ด SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"การ์ด SD ของ <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"ไดรฟ์ USB"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"ไม่จัดอยู่ในหมวดหมู่ใดๆ"</string>
<string name="importance_from_user" msgid="7318955817386549931">"คุณตั้งค่าความสำคัญของการแจ้งเตือนเหล่านี้"</string>
<string name="importance_from_person" msgid="9160133597262938296">"ข้อความนี้สำคัญเนื่องจากบุคคลที่เกี่ยวข้อง"</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"อนุญาตให้ <xliff:g id="APP">%1$s</xliff:g> สร้างผู้ใช้ใหม่ด้วย <xliff:g id="ACCOUNT">%2$s</xliff:g> ไหม (มีผู้ใช้ที่มีบัญชีนี้อยู่แล้ว)"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"อนุญาตให้ <xliff:g id="APP">%1$s</xliff:g> สร้างผู้ใช้ใหม่ด้วย <xliff:g id="ACCOUNT">%2$s</xliff:g> ไหม"</string>
<string name="language_selection_title" msgid="2680677278159281088">"เพิ่มภาษา"</string>
<string name="country_selection_title" msgid="2954859441620215513">"ค่ากำหนดภูมิภาค"</string>
<string name="search_language_hint" msgid="7042102592055108574">"พิมพ์ชื่อภาษา"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 9acd72aaeaf0..01cf28f48c86 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Pinapayagan ang app na tumanggap at magproseso ng mga mensaheng SMS. Nangangahulugan ito na maaaring sumubaybay o magtanggal ang app ng mga mensaheng ipinapadala sa iyong device nang hindi ipinapakita ang mga ito sa iyo."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"tumanggap ng mga text message (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Pinapayagan ang app na tumanggap at magproseso ng mga mensaheng MMS. Nangangahulugan ito na maaaring sumubaybay o magtanggal ang app ng mga mensaheng ipinapadala sa iyong device nang hindi ipinapakita ang mga ito sa iyo."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Magpasa ng mga mensahe ng cell broadcast"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Nagbibigay-daan sa app na mag-bind sa module ng cell broadcast para makapagpasa ng mga mensahe ng cell broadcast pagkatanggap sa mga ito. Inihahatid ang mga alerto ng cell broadcast sa ilang lokasyon para balaan ka tungkol sa mga emergency na sitwasyon. Posibleng makasagabal ang mga nakakahamak na app sa performance o pagpapatakbo ng iyong device kapag nakatanggap ito ng emergency na cell broadcast."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"basahin ang mga mensahe ng cell broadcast"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Binibigyang-daan ang app na magbasa ng mga mensahe ng cell broadcast na natanggap ng iyong device. Inihahatid ang mga alerto ng cell broadcast sa ilang lokasyon upang balaan ka tungkol sa mga emergency na sitwasyon. Maaaring makaabala ang nakakahamak na apps sa performance o pagpapatakbo ng iyong device kapag nakatanggap ng emergency na cell broadcast."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"magbasa ng mga na-subscribe na feed"</string>
@@ -1345,7 +1347,7 @@
<string name="no_permissions" msgid="7283357728219338112">"Walang mga kinakailangang pahintulot"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"maaari itong magdulot ng gastos sa iyo"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="1595122345358177163">"China-charge ang device na ito sa pamamagitan ng USB"</string>
+ <string name="usb_charging_notification_title" msgid="1595122345358177163">"China-charge ang device sa USB"</string>
<string name="usb_supplying_notification_title" msgid="4631045789893086181">"China-charge ang nakakonektang device sa pamamagitan ng USB"</string>
<string name="usb_mtp_notification_title" msgid="4238227258391151029">"Na-on ang paglipat ng USB file"</string>
<string name="usb_ptp_notification_title" msgid="5425857879922006878">"Na-on ang PTP sa pamamagitan ng USB"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Piliin upang i-disable ang debugging ng USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Naka-enable ang Test Harness Mode"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Mag-factory reset para i-disable ang Test Harness Mode."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Naka-enable ang serial console"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Naaapektuhan ang performance. Para i-disable, lagyan ng check ang bootloader."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Liquid o debris sa USB port"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Awtomatikong na-disable ang USB port. Mag-tap para matuto pa."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Ayos na gamitin ang USB port"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Di-nakategorya"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Ikaw ang magtatakda sa kahalagahan ng mga notification na ito."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Mahalaga ito dahil sa mga taong kasangkot."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Payagan ang <xliff:g id="APP">%1$s</xliff:g> na gumawa ng bagong User sa <xliff:g id="ACCOUNT">%2$s</xliff:g> (mayroon nang User sa account na ito) ?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Payagan ang <xliff:g id="APP">%1$s</xliff:g> na gumawa ng bagong User sa <xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Magdagdag ng wika"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Kagustuhan sa rehiyon"</string>
<string name="search_language_hint" msgid="7042102592055108574">"I-type ang wika"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index e2bda9fc602e..d3e9d938ffe3 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Uygulamaya SMS iletilerini alma ve işleme izni verir. Bu izin, uygulamanın cihazınıza gönderilen iletileri takip edip size göstermeden silebileceği anlamına gelir."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"kısa mesajları (MMS) al"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Uygulamaya MMS iletilerini alma ve işleme izni verir. Bu izin, uygulamanın cihazınıza gönderilen iletileri takip edip size göstermeden silebileceği anlamına gelir."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Hücre yayını mesajlarını yönlendirme"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Uygulamanın hücre yayını mesajları geldiğinde bunları yönlendirmek için hücre yayını modülüne bağlanmasına izin verir. Hücre yayını uyarıları bazı konumlarda acil durumlar hakkında sizi uyarmak için kullanılır. Zararlı uygulamalar acil durum hücre yayını alındığında cihazınızın performansını ve çalışmasını olumsuz etkileyebilir."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"hücre yayını mesajlarını oku"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Uygulamaya, cihazınız tarafından alınan hücre yayını mesajlarını okuma izni verir. Hücre yayını uyarıları bazı yerlerde acil durumlar konusunda sizi uyarmak için gönderilir. Kötü amaçlı uygulamalar acil hücre yayını alındığında cihazınızın performansına ya da çalışmasına engel olabilir."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"abone olunan yayınları okuma"</string>
@@ -546,7 +548,7 @@
<string name="fingerprint_error_canceled" msgid="4402024612660774395">"Parmak izi işlemi iptal edildi."</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Parmak izi işlemi kullanıcı tarafından iptal edildi."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Çok fazla deneme yapıldı. Daha sonra tekrar deneyin."</string>
- <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Çok fazla deneme yapıldı. Parmak izi sensörü devre dışı bıraıldı."</string>
+ <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Çok fazla deneme yapıldı. Parmak izi sensörü devre dışı bırakıldı."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Tekrar deneyin."</string>
<string name="fingerprint_error_no_fingerprints" msgid="7654382120628334248">"Parmak izi kaydedilmedi."</string>
<string name="fingerprint_error_hw_not_present" msgid="409523969613176352">"Bu cihazda parmak izi sensörü yok."</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB hata ayıklamasını devre dışı bırakmak için seçin."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Test Bandı Modu etkin"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Test Bandı Modu\'nu devre dışı bırakmak için cihazı fabrika ayarlarına sıfırlayın."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Seri konsol etkinleştirildi"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Performans etkilendi. Devre dışı bırakmak için bootloader\'ı kontrol edin."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB bağlantı noktasında sıvı veya toz var"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB bağlantı noktası otomatik olarak devre dışı bırakıldı. Daha fazla bilgi için dokunun."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB bağlantı noktasını kullanabilirsiniz"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Kategorize edilmemiş"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Bu bildirimlerin önem derecesini ayarladınız."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Bu, dahil olan kişiler nedeniyle önemlidir."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="APP">%1$s</xliff:g> uygulamasının <xliff:g id="ACCOUNT">%2$s</xliff:g> hesabına sahip yeni bir Kullanıcı eklemesine izin verilsin mi (bu hesaba sahip bir kullanıcı zaten var)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"<xliff:g id="APP">%1$s</xliff:g> uygulamasının <xliff:g id="ACCOUNT">%2$s</xliff:g> hesabına sahip yeni bir Kullanıcı eklemesine izin verilsin mi?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Dil ekleyin"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Bölge tercihi"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Dil adını yazın"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index ee5257554ac9..88350a29180b 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -350,6 +350,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Дозволяє програмі отримувати й обробляти SMS-повідомлення. Це означає, що програма може відстежувати чи видаляти повідомлення, надіслані на ваш пристрій, навіть не показуючи їх вам."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"отримувати текстові повідомлення (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Дозволяє програмі отримувати й обробляти MMS-повідомлення. Це означає, що програма може відстежувати чи видаляти повідомлення, надіслані на ваш пристрій, навіть не показуючи їх вам."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Переадресувати повідомлення Cell Broadcast"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Дозволяє додатку зв\'язуватися з модулем Cell Broadcast, щоб переадресувати відповідні вхідні повідомлення. У деяких місцеположеннях сповіщення Cell Broadcast надсилаються для попередження про надзвичайні ситуації. Після повідомлення Cell Broadcast шкідливі додатки можуть перешкоджати роботі вашого пристрою."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"читати широкомовні повідомлення мережі"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Дозволяє програмі читати широкомовні повідомлення мережі, отримані пристроєм. Широкомовні сповіщення мережі надсилаються в деяких країнах для попередження про надзвичайні ситуації. Шкідливі програми можуть втручатися у швидкодію чи роботу пристрою під час отримання широкомовного повідомлення мережі про надзвичайну ситуацію."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"читати підписані канали"</string>
@@ -518,10 +520,10 @@
<string name="permdesc_requestPasswordComplexity" msgid="4730994229754212347">"Дозволяє додатку визначати рівень складності способу блокування екрана (високий, середній, низький або нульовий). Враховуються кількість символів і тип блокування. Додаток також може пропонувати вибрати складніший тип блокування екрана, але це не обов’язково робити. Примітка: оскільки пароль зашифровано, додаток його не знає."</string>
<string name="permlab_useBiometric" msgid="8837753668509919318">"використовувати біометричне апаратне забезпечення"</string>
<string name="permdesc_useBiometric" msgid="8389855232721612926">"Додаток може використовувати біометричне апаратне забезпечення для автентифікації"</string>
- <string name="permlab_manageFingerprint" msgid="5640858826254575638">"керувати апаратним забезпеченням для цифрових відбитків"</string>
- <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Увімкнути в додатку функції для додавання й видалення шаблонів цифрових відбитків."</string>
- <string name="permlab_useFingerprint" msgid="3150478619915124905">"використання сканера цифрових відбитків"</string>
- <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Дозволити додатку використовувати апаратне забезпечення для автентифікації"</string>
+ <string name="permlab_manageFingerprint" msgid="5640858826254575638">"керувати сканером відбитків пальців"</string>
+ <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Увімкнути в додатку функції для додавання й видалення шаблонів відбитків пальців."</string>
+ <string name="permlab_useFingerprint" msgid="3150478619915124905">"використання сканера відбитків пальців"</string>
+ <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Дозволити додатку використовувати сканер відбитків пальців для автентифікації"</string>
<string name="permlab_audioWrite" msgid="2661772059799779292">"змінювати колекцію музики"</string>
<string name="permdesc_audioWrite" msgid="8888544708166230494">"Додаток зможе змінювати вашу колекцію музики."</string>
<string name="permlab_videoWrite" msgid="128769316366746446">"змінювати колекцію відео"</string>
@@ -536,20 +538,20 @@
<string name="biometric_not_recognized" msgid="5770511773560736082">"Не розпізнано"</string>
<string name="biometric_error_canceled" msgid="349665227864885880">"Автентифікацію скасовано"</string>
<string name="biometric_error_device_not_secured" msgid="6583143098363528349">"Не вказано PIN-код, ключ або пароль"</string>
- <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Відбиток розпізнано частково. Повторіть спробу."</string>
- <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Не вдалось обробити відбиток. Повторіть спробу."</string>
- <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Датчик відбитків забруднився. Очистьте його та повторіть спробу."</string>
+ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Відбиток пальця розпізнано частково. Повторіть спробу."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Не вдалось обробити відбиток пальця. Повторіть спробу."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Датчик відбитків пальців забруднився. Очистьте його та повторіть спробу."</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Ви забрали палець надто швидко. Повторіть спробу."</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Ви провели пальцем надто повільно. Повторіть спробу."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_authenticated" msgid="5309333983002526448">"Відбиток автентифіковано"</string>
+ <string name="fingerprint_authenticated" msgid="5309333983002526448">"Відбиток пальця автентифіковано"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Обличчя автентифіковано"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Обличчя автентифіковано. Натисніть \"Підтвердити\""</string>
- <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Апаратне забезпечення для сканування відбитка недоступне."</string>
- <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Не вдалося зберегти відбиток. Видаліть наявний відбиток."</string>
- <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Час очікування відбитка минув. Повторіть спробу."</string>
- <string name="fingerprint_error_canceled" msgid="4402024612660774395">"Дію з відбитком скасовано."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Сканер відбитків пальців недоступний."</string>
+ <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Не вдалося зберегти відбиток пальця. Видаліть наявний відбиток."</string>
+ <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Час очікування відбитка пальця минув. Повторіть спробу."</string>
+ <string name="fingerprint_error_canceled" msgid="4402024612660774395">"Дію з відбитком пальця скасовано."</string>
<string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Користувач скасував дію з відбитком пальця."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Забагато спроб. Спробуйте пізніше."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Забагато спроб. Сканер відбитків пальців вимкнено."</string>
@@ -1405,6 +1407,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Виберіть, щоб вимкнути налагодження за USB"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Увімкнено режим автоматизованого тестування"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Щоб вимкнути режим автоматизованого тестування, відновіть заводські налаштування."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Послідовну консоль увімкнено"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Продуктивність зазнала впливу. Щоб вимкнути, перевірте завантажувач операційної системи."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Рідина або сміття в USB-порту"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB-порт автоматично вимкнено. Торкніться, щоб дізнатися більше."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Можна використовувати USB-порт"</string>
@@ -1958,10 +1962,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Без категорії"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Ви вказуєте пріоритет цих сповіщень."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Важливе з огляду на учасників."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Дозволити додатку <xliff:g id="APP">%1$s</xliff:g> створити нового користувача з обліковим записом <xliff:g id="ACCOUNT">%2$s</xliff:g> (користувач із таким обліковим записом уже існує)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Дозволити додатку <xliff:g id="APP">%1$s</xliff:g> створити нового користувача з обліковим записом <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Додати мову"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Вибір регіону"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Введіть назву мови"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 5cc8db70decc..e5015ea0ec1b 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -324,7 +324,7 @@
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ڈسپلے کے زوم کی سطح اور پوزیشن کو کنٹرول کریں۔"</string>
<string name="capability_title_canPerformGestures" msgid="7418984730362576862">"اشارے انجام دینے کی"</string>
<string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"تھپتھپانا، سوائپ کرنا، چٹکی بھرنا اور دیگر اشارے انجام دے سکتی ہے"</string>
- <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"فنگرپرنٹ کے اشارے"</string>
+ <string name="capability_title_canCaptureFingerprintGestures" msgid="6309568287512278670">"فنگر پرنٹ کے اشارے"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="4386487962402228670">"آلہ کے فنگر پرنٹ سینسر پر کیے گئے اشاروں کو کیپچر کر سکتا ہے۔"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"اسٹیٹس بار کو غیر فعال یا اس میں ترمیم کریں"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ایپ کو اسٹیٹس بار غیر فعال کرنے یا سسٹم آئیکنز شامل کرنے اور ہٹانے کی اجازت دیتا ہے۔"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"‏ایپ کو SMS پیغامات حاصل اور ان پر کارروائی کرنے کی اجازت دیتا ہے۔ اس کا مطلب ہے کہ ایپ آپ کے آلے پر مرسلہ پیغامات آپ کو دکھائے بغیر ان پر نگاہ رکھ یا انہیں حذف کرسکتی ہے۔"</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"‏متنی پیغامات (MMS) حاصل کریں"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"‏ایپ کو MMS پیغامات حاصل اور ان پر کارروائی کرنے کی اجازت دیتا ہے۔ اس کا مطلب ہے کہ ایپ آپ کے آلے پر مرسلہ پیغامات آپ کو دکھائے بغیر ان پر نگاہ رکھ یا انہیں حذف کرسکتی ہے۔"</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"سیل کے نشریاتی پیغامات فارورڈ کریں"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"سیل کی نشریاتی پیغامات کے موصول ہوتے ہی فارورڈ کرنے کے لیے ایپ کو سیل کے نشریاتی ماڈیول میں پابندی لگانے کی اجازت دیں۔ سیل کی نشریاتی الرٹس آپ کو ہنگامی حالات سے مطلع کرنے کیلئے کچھ مقامات میں مہیا کی جاتی ہیں۔ نقصان دہ ایپس کوئی ہنگامی سیل براڈ کاسٹ موصول ہونے پر آپ کے آلے کی کارکردگی یا عمل میں مداخلت کر سکتی ہیں۔"</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"سیل کے نشریاتی پیغامات پڑھیں"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"ایپ کو آپ کے آلے کو موصولہ سیل کے نشریاتی پیغامات پڑھنے کی اجازت دیتا ہے۔ سیل کی نشریاتی الرٹس آپ کو ہنگامی حالات سے مطلع کرنے کیلئے کچھ مقامات میں مہیا کی جاتی ہیں۔ نقصان دہ ایپس کوئی ہنگامی سیل کا نشریہ موصول ہونے پر آپ کے آلے کی کارکردگی یا عمل میں خلل ڈال سکتی ہیں۔"</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"سبسکرائب کردہ فیڈز پڑھیں"</string>
@@ -1362,6 +1364,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"‏USB ڈیبگ کرنے کو غیر فعال کرنے کیلئے منتخب کریں۔"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"ٹیسٹ ہارنیس موڈ فعال ہے"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"ٹیسٹ ہارنیس موڈ غیر فعال کرنے کے لیے فیکٹری ری سیٹ کریں۔"</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"شمار کونسول فعال ہے"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"کارکردگی پر اثر پڑا ہے۔ غیر فعال کرنے کے ليے، بوٹ لوڈر چیک کریں۔"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"‏USB پورٹ میں سیال یا دھول ہے"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"‏USB پورٹ خودکار طور پر غیر فعال کر دیا گیا۔ مزید جاننے کیلئے تھپتھپائیں۔"</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"‏USB پورٹ کا استعمال ٹھیک ہے"</string>
@@ -1891,10 +1895,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"غیر زمرہ بند"</string>
<string name="importance_from_user" msgid="7318955817386549931">"ان اطلاعات کی اہمیت آپ مقرر کرتے ہیں۔"</string>
<string name="importance_from_person" msgid="9160133597262938296">"اس میں موجود لوگوں کی وجہ سے یہ اہم ہے۔"</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="APP">%1$s</xliff:g> کو <xliff:g id="ACCOUNT">%2$s</xliff:g> کے ساتھ ایک نیا صارف بنانے کی اجازت دیں (اس اکاؤنٹ کے ساتھ ایک صارف پہلے سے موجود ہے) ؟"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"<xliff:g id="ACCOUNT">%2$s</xliff:g> کے ساتھ نئے صارف کو تخلیق کرنے کے لیے <xliff:g id="APP">%1$s</xliff:g> کو اجازت دیں ؟"</string>
<string name="language_selection_title" msgid="2680677278159281088">"ایک زبان شامل کریں"</string>
<string name="country_selection_title" msgid="2954859441620215513">"علاقہ کی ترجیح"</string>
<string name="search_language_hint" msgid="7042102592055108574">"زبان کا نام ٹائپ کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index cb5191dd82c9..88997476b9fd 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Ilovaga SMS xabarlarini qabul qilish va va ularni qayta ishlash uchun ruxsat beradi. Bu sizga yuborilgan xabarlarni ilova sizga ko‘rsatmasdan kuzatishi va o‘chirishi mumkinligini bildiradi."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"MMS xabarlarni olish"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Ilovaga MMS xabarlarini qabul qilish va ularni qayta ishlash uchun ruxsat beradi. Bu sizga yuborilgan xabarlarni ilova sizga ko‘rsatmasdan kuzatishi va o‘chirishi mumkinligini bildiradi."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Aholini ogohlantirish xabarlarini uzatish"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Qabul qilingan aholini ogohlantirish xabarlarini shu holicha uzatish uchun ilovani aholini ogohlantirish moduliga bogʻlash imkonini beradi. Ilovaga ayrim mamlakatlarda aholini favqulodda vaziyatlarda ogohlantirish uchun yuboriladigan tarqatma xabarlarni oʻqish uchun ruxsat beradi. Zararli dasturlar bunday xabarlar kelayotgan qurilmaning ishlashiga xalaqit qilishi mumkin."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"uyali tarmoq operatori xabarlarini o‘qish"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Ilovaga qurilmangiz tomonidan qabul qilingan uyali tarmoq operatori xabarlarini o‘qish uchun ruxsat beradi. Uyali tarmoq operatorining ogohlantiruvchi xabarlari ba’zi manzillarga favqulodda holatlar haqida ogohlantirish uchun jo‘natiladi. Zararli ilovalar uyali tarmoq orqali favqulodda xabar qabul qilinganda qurilmangizning ish faoliyati yoki amallariga xalaqit qilishi mumkin"</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"obunalarni o‘qish"</string>
@@ -532,7 +534,7 @@
<string name="biometric_error_device_not_secured" msgid="6583143098363528349">"PIN kod, grafik kalit yoki parol sozlanmagan"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Barmoq izi qisman aniqlandi. Qayta urinib ko‘ring."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Barmoq izi aniqlanmadi. Qaytadan urining."</string>
- <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Skanerni tozalab, keyin qaytadan urining."</string>
+ <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Barmoq izi skanerini tozalab, keyin qaytadan urining."</string>
<string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Barmoq juda tez harakatlandi. Qaytadan urining."</string>
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Barmoq juda sekin harakatlandi. Qayta urinib ko‘ring."</string>
<string-array name="fingerprint_acquired_vendor">
@@ -1362,6 +1364,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB orqali nosozliklarni tuzatishni o‘chirib qo‘yish uchun bosing."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Xavfsizlik sinovi rejimi yoqildi"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Xavfsizlik sinovi rejimini faolsizlantirish uchun zavod sozlamalariga qaytaring."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Davomiy port terminali yoqildi"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Samaradorlik pasaydi. Terminalni faolsizlantirish uchun operatsion tizim yuklagichini oching."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB portda suyuqlik yoki parcha bor"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB port avtomatik tarzda faolsizlashtirildi. Batafsil axborot olish uchun bosing."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB portdan foydalanish mumkin"</string>
@@ -1891,10 +1895,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Turkumlanmagan"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Siz ushbu bildirishnomalarning muhimligini belgilagansiz."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Bu odamlar siz uchun muhim."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"<xliff:g id="APP">%1$s</xliff:g> ilovasiga <xliff:g id="ACCOUNT">%2$s</xliff:g> hisobi bilan yangi foydalanuvchi yaratishiga ruxsat berilsinmi (bunday hisobdagi foydalanuvchi allaqachon mavjud) ?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"<xliff:g id="APP">%1$s</xliff:g> ilovasiga <xliff:g id="ACCOUNT">%2$s</xliff:g> hisobi bilan yangi foydalanuvchi yaratishiga ruxsat berilsinmi ?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Til qoʻshish"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Hudud sozlamalari"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Til nomini kiriting"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index e41dc4374714..fc8994e4b42a 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Cho phép ứng dụng nhận và xử lý tin nhắn SMS. Điều này có nghĩa là ứng dụng có thể theo dõi hoặc xóa tin nhắn được gửi đến thiết bị của bạn mà không hiển thị chúng cho bạn."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"nhận tin nhắn văn bản (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Cho phép ứng dụng nhận và xử lý tin nhắn MMS. Điều này có nghĩa là ứng dụng có thể theo dõi hoặc xóa tin nhắn được gửi đến thiết bị của bạn mà không hiển thị chúng cho bạn."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Chuyển tiếp tin nhắn truyền phát trên di động"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Cho phép ứng dụng liên kết với mô-đun truyền phát trên di động để chuyển tiếp tin nhắn truyền phát trên di động ngay khi nhận được. Ở một số vị trí, thông báo truyền phát trên di động sẽ được gửi nhằm cảnh báo cho bạn về các tình huống khẩn cấp. Các ứng dụng độc hại có thể ảnh hưởng đến hiệu suất hoặc hoạt động của thiết bị khi nhận được tin nhắn truyền phát trên di động lúc khẩn cấp."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"đọc tin nhắn quảng bá"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Cho phép ứng dụng đọc tin nhắn quảng bá mà thiết bị của bạn nhận được. Tin nhắn quảng bá cảnh báo được gửi ở một số địa điểm nhằm cảnh báo cho bạn về các tình huống khẩn cấp. Các ứng dụng độc hại có thể gây ảnh hưởng đến hiệu suất hoặc hoạt động của thiết bị của bạn khi nhận được tin nhắn quảng bá khẩn cấp."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"đọc nguồn cấp dữ liệu đã đăng ký"</string>
@@ -731,7 +733,7 @@
</string-array>
<string name="phoneTypeCustom" msgid="1644738059053355820">"Tùy chỉnh"</string>
<string name="phoneTypeHome" msgid="2570923463033985887">"Nhà riêng"</string>
- <string name="phoneTypeMobile" msgid="6501463557754751037">"Di Động"</string>
+ <string name="phoneTypeMobile" msgid="6501463557754751037">"Di động"</string>
<string name="phoneTypeWork" msgid="8863939667059911633">"Cơ quan"</string>
<string name="phoneTypeFaxWork" msgid="3517792160008890912">"Số fax cơ quan"</string>
<string name="phoneTypeFaxHome" msgid="2067265972322971467">"Số fax nhà riêng"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Chọn để tắt chế độ gỡ lỗi qua USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Đã bật Chế độ khai thác kiểm thử"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Khôi phục cài đặt gốc để tắt Chế độ khai thác kiểm thử."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"Đã bật bảng điều khiển cổng nối tiếp"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Hiệu suất sẽ bị ảnh hưởng. Để tắt, hãy chọn trình tải khởi động."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Có chất lỏng hoặc mảnh vỡ trong cổng USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Cổng USB đã tự động tắt. Nhấn để tìm hiểu thêm."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Có thể sử dụng cổng USB"</string>
@@ -1579,8 +1583,8 @@
<string name="expires_on" msgid="3676242949915959821">"Hết hạn vào:"</string>
<string name="serial_number" msgid="758814067660862493">"Số sê-ri:"</string>
<string name="fingerprints" msgid="4516019619850763049">"Tệp tham chiếu:"</string>
- <string name="sha256_fingerprint" msgid="4391271286477279263">"Tệp tham chiếu SHA-256:"</string>
- <string name="sha1_fingerprint" msgid="7930330235269404581">"Tệp tham chiếu SHA-1:"</string>
+ <string name="sha256_fingerprint" msgid="4391271286477279263">"Vân tay SHA-256:"</string>
+ <string name="sha1_fingerprint" msgid="7930330235269404581">"Vân tay SHA-1:"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Xem tất cả"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Chọn hoạt động"</string>
<string name="share_action_provider_share_with" msgid="5247684435979149216">"Chia sẻ với"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Chưa được phân loại"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Bạn đặt tầm quan trọng của các thông báo này."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Thông báo này quan trọng vì những người có liên quan."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Cho phép <xliff:g id="APP">%1$s</xliff:g> tạo người dùng mới bằng <xliff:g id="ACCOUNT">%2$s</xliff:g> (đã tồn tại người dùng có tài khoản này)?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Cho phép <xliff:g id="APP">%1$s</xliff:g> tạo người dùng mới bằng <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Thêm ngôn ngữ"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Tùy chọn khu vực"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Nhập tên ngôn ngữ"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index ba39f1e22e2c..56d815357288 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"允许该应用接收和处理短信。这就意味着,该应用可能会监视发送到您设备的短信,或删除发送到您设备的短信而不向您显示。"</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"接收讯息(彩信)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"允许该应用接收和处理彩信。这就意味着,该应用可能会监视发送到您设备的彩信,或删除发送到您设备的彩信而不向您显示。"</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"转发小区广播消息"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"允许应用绑定到小区广播模块,以便及时转发收到的小区广播消息。小区广播消息是在某些地区发送的、用于发布紧急情况警告的提醒信息。恶意应用可能会在您的设备收到紧急小区广播时干扰设备的性能或操作。"</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"读取小区广播消息"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"允许应用读取您的设备收到的小区广播消息。小区广播消息是在某些地区发送的、用于发布紧急情况警告的提醒信息。恶意应用可能会在您收到小区紧急广播时干扰您设备的性能或操作。"</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"读取订阅的供稿"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"选择即可停用 USB 调试功能。"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"自动化测试框架模式已启用"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"恢复出厂设置以停用自动化测试框架模式。"</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"已启用序列控制台"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"性能受到影响。要停用,请查看引导加载程序。"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB 端口中有液体或碎屑"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB 端口已自动停用。点按即可了解详情。"</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"允许使用 USB 端口"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"未分类"</string>
<string name="importance_from_user" msgid="7318955817386549931">"这些通知的重要程度由您来设置。"</string>
<string name="importance_from_person" msgid="9160133597262938296">"这条通知涉及特定的人,因此被归为重要通知。"</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"允许<xliff:g id="APP">%1$s</xliff:g>使用 <xliff:g id="ACCOUNT">%2$s</xliff:g>(目前已有用户使用此帐号)创建新用户吗?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"允许<xliff:g id="APP">%1$s</xliff:g>使用 <xliff:g id="ACCOUNT">%2$s</xliff:g> 创建新用户吗?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"添加语言"</string>
<string name="country_selection_title" msgid="2954859441620215513">"区域偏好设置"</string>
<string name="search_language_hint" msgid="7042102592055108574">"输入语言名称"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index f0fce71e9d8c..c8360aa7439e 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -201,7 +201,7 @@
<string name="turn_on_radio" msgid="3912793092339962371">"開啟無線網絡"</string>
<string name="turn_off_radio" msgid="8198784949987062346">"關閉無線網絡"</string>
<string name="screen_lock" msgid="799094655496098153">"螢幕鎖定"</string>
- <string name="power_off" msgid="4266614107412865048">"關閉"</string>
+ <string name="power_off" msgid="4266614107412865048">"關機"</string>
<string name="silent_mode_silent" msgid="319298163018473078">"鈴聲關閉"</string>
<string name="silent_mode_vibrate" msgid="7072043388581551395">"鈴聲震動"</string>
<string name="silent_mode_ring" msgid="8592241816194074353">"鈴聲開啟"</string>
@@ -225,7 +225,7 @@
<string name="global_actions" product="tv" msgid="9091480417912345975">"Android TV 選項"</string>
<string name="global_actions" product="default" msgid="2406416831541615258">"手機選項"</string>
<string name="global_action_lock" msgid="2844945191792119712">"螢幕鎖定"</string>
- <string name="global_action_power_off" msgid="4471879440839879722">"關閉"</string>
+ <string name="global_action_power_off" msgid="4471879440839879722">"關機"</string>
<string name="global_action_emergency" msgid="7112311161137421166">"緊急"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"錯誤報告"</string>
<string name="global_action_logout" msgid="935179188218826050">"結束工作階段"</string>
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"允許應用程式接收和處理短訊。這表示應用程式可監控傳送至您裝置的訊息,或在您閱讀訊息前擅自刪除訊息。"</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"接收短訊 (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"允許應用程式接收和處理 MMS 訊息。這表示應用程式可監控傳送至您裝置的訊息,或在您閱讀訊息前擅自刪除訊息。"</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"轉寄區域廣播訊息"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"允許應用程式繫結至區域廣播模組,以在收到區域廣播訊息時轉寄訊息。在某些地點,系統會發出區域廣播通知,提示您有緊急狀況發生。惡意應用程式可能會在裝置收到緊急區域廣播時,干擾裝置的效能或運作。"</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"讀取區域廣播訊息"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"允許應用程式讀取您裝置接收的區域廣播訊息。某些地點會發出區域廣播警報,警告您發生緊急狀況。惡意應用程式可能會在裝置收到緊急區域廣播時,干擾裝置的性能或運作。"</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"讀取訂閱的資訊提供"</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"選取即可停用 USB 偵錯。"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"已啟用測試工具模式"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"請將裝置回復原廠設定,以停用測試工具模式。"</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"已啟用序列控制器"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"效能受到影響,勾選啟動程式即可停用。"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB 連接埠中有液體或碎片"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB 連接埠已自動停用。輕按即可瞭解詳情。"</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB 連接埠可安全使用"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"未分類"</string>
<string name="importance_from_user" msgid="7318955817386549931">"您可以設定這些通知的重要性。"</string>
<string name="importance_from_person" msgid="9160133597262938296">"列為重要的原因:涉及的人。"</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"要允許 <xliff:g id="APP">%1$s</xliff:g> 使用 <xliff:g id="ACCOUNT">%2$s</xliff:g> 建立新使用者 (此帳戶目前已有此使用者) 嗎?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"要允許 <xliff:g id="APP">%1$s</xliff:g> 使用 <xliff:g id="ACCOUNT">%2$s</xliff:g> 建立新使用者嗎?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"新增語言"</string>
<string name="country_selection_title" msgid="2954859441620215513">"地區偏好設定"</string>
<string name="search_language_hint" msgid="7042102592055108574">"輸入語言名稱"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 1f132087ede3..23b68e1412a2 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"允許應用程式接收和處理簡訊。這項設定可讓應用程式監控傳送至你裝置的訊息,或在你閱讀訊息前擅自刪除訊息。"</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"接收簡訊 (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"允許應用程式接收和處理多媒體訊息。這項設定可讓應用程式監控傳送至你裝置的訊息,或在你閱讀訊息前擅自刪除訊息。"</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"轉送區域廣播訊息"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"允許應用程式繫結至區域廣播模組,以便轉送收到的區域廣播訊息。某些地點會發出區域廣播警示,警告你有緊急狀況發生。請注意,惡意應用程式可能會在裝置收到緊急區域廣播時,干擾裝置的效能或運作。"</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"讀取區域廣播訊息"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"允許應用程式讀取你裝置收到的區域廣播訊息。某些地點會發出區域廣播警示,警告你有緊急狀況發生。請注意,惡意應用程式可能會在裝置收到緊急區域廣播時,干擾裝置的效能或運作。"</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"讀取訂閱資訊提供"</string>
@@ -1361,10 +1363,12 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"選取這個選項以停用 USB 偵錯功能。"</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"測試控管工具模式已啟用"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"恢復原廠設定以停用測試控管工具模式。"</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"已啟用序列主控台"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"效能已受到影響。如要停用,請檢查系統啟動載入程式。"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB 連接埠中有液體或灰塵"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"系統已自動停用 USB 連接埠。輕觸即可瞭解詳情。"</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"現在可以使用 USB 連接埠"</string>
- <string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"手機目前無法偵測液體或灰塵。"</string>
+ <string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"手機目前沒有偵測到液體或灰塵。"</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"正在接收錯誤報告…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"要分享錯誤報告嗎?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"正在分享錯誤報告…"</string>
@@ -1580,7 +1584,7 @@
<string name="serial_number" msgid="758814067660862493">"序號:"</string>
<string name="fingerprints" msgid="4516019619850763049">"指紋"</string>
<string name="sha256_fingerprint" msgid="4391271286477279263">"SHA-256 指紋"</string>
- <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 指紋"</string>
+ <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 指紋:"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"全部顯示"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"選擇活動"</string>
<string name="share_action_provider_share_with" msgid="5247684435979149216">"分享活動"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"未分類"</string>
<string name="importance_from_user" msgid="7318955817386549931">"這些通知的重要性由你決定。"</string>
<string name="importance_from_person" msgid="9160133597262938296">"這則通知涉及特定人士,因此被歸為重要通知。"</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"要允許「<xliff:g id="APP">%1$s</xliff:g>」替 <xliff:g id="ACCOUNT">%2$s</xliff:g> (這個帳戶目前已有使用者) 建立新使用者嗎?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"要允許「<xliff:g id="APP">%1$s</xliff:g>」替 <xliff:g id="ACCOUNT">%2$s</xliff:g> 建立新使用者嗎?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"新增語言"</string>
<string name="country_selection_title" msgid="2954859441620215513">"地區偏好設定"</string>
<string name="search_language_hint" msgid="7042102592055108574">"請輸入語言名稱"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index f707fccef3d9..dac7c106d2f5 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -344,6 +344,8 @@
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Ivumela uhlelo lokusebenza ukuthola nokucubungula imilayezo ye-SMS. Loku kuchaza ukuthi uhlelo lokusebenza lungangamela noma lesuse imilayezo ethunyelwe kudivayisi yakho ngaphandle kokukubonisa yona."</string>
<string name="permlab_receiveMms" msgid="1821317344668257098">"thola imiyalezo ebhaliwe (i-MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Ivumela uhlelo lokusebenza ukuthola nokucubungula imilayezo ye-MMS. Loku kuchaza ukuthi uhlelo lokusebenza lungangamela noma lesuse imilayezo ethunyelwe kudivayisi yakho ngaphandle kokukubonisa yona."</string>
+ <string name="permlab_bindCellBroadcastService" msgid="4468585041824555604">"Dlulisela imilayezo yokusakaza kweselula"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="9073440260695196089">"Ivumela uhlelo lokusebenza ukuthi luboshezelwe kumojuli yokusakaza kweselula ukuze kudluliselwe imilayezo yokusakaza yeselula njengoba itholwa. Izexwayiso zokusakaza kweselula zilethwa kwezinye izindawo ukuze zikuxwayise ngezimo zesimo esiphuthumayo. Izinhlelo zokusebenza ezinobungozi zingaphazamisa ngokusebenza noma ukusetshenziswa kwedivayisi yakho uma ukusakaza kweselula kwesimo esiphuthumayo kwamukelwa."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"funda imilayezo yokusakaza yeselula"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Ivumela uhlelo lokusebenza ukufunda imilayezo yokusakaza yeselula etholwe idivayisi yakho. Izaziso zokusakaza zeselula zilethwa kwezinye izindawo ukukuxwayisa ngezimo ezisheshayo. Izinhlelo zokusebenza ezingalungile zingaphazamisana nokusebenza noma umsebenzi wedivayisi yakho uma ukusakaza kweselula kwesimo esisheshayo kutholwa."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"funda izifunzo ezikhokhelwayo"</string>
@@ -537,14 +539,14 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Umnwe uhanjiswe kancane kakhulu. Sicela uzame futhi."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_authenticated" msgid="5309333983002526448">"Isingxivizo somunwe sigunyaziwe"</string>
+ <string name="fingerprint_authenticated" msgid="5309333983002526448">"Izigxivizo zeminwe zigunyaziwe"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Ubuso bufakazelwe ubuqiniso"</string>
<string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Ukuqinisekiswa kobuso, sicela ucindezele okuthi qinisekisa"</string>
- <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Izingxenyekazi zekhompuyutha zezingxivizo zeminwe azitholakali."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Izingxenyekazi zekhompuyutha zezigxivizo zeminwe azitholakali."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Izigxivizo zeminwe azikwazi ukugcinwa. Sicela ususe izigxivizo zeminwe ezikhona."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Kufinyelelwe isikhathi sokuvala sezigxivizo zeminwe. Zama futhi"</string>
- <string name="fingerprint_error_canceled" msgid="4402024612660774395">"Ukusebenza kwezingxivizo zeminwe kukhanseliwe."</string>
- <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Umsebenzi wesigxivizo somunwe sikhanselwe umsebenzisi."</string>
+ <string name="fingerprint_error_canceled" msgid="4402024612660774395">"Ukusebenza kwezigxivizo zeminwe kukhanseliwe."</string>
+ <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"Umsebenzi wezigxivizo zomunwe ukhanselwe umsebenzisi."</string>
<string name="fingerprint_error_lockout" msgid="5536934748136933450">"Imizamo eminingi kakhulu. Zama futhi emuva kwesikhathi."</string>
<string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"Imizamo eminingi kakhulu. Inzwa yezigxivizo zeminwe ikhutshaziwe."</string>
<string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"Zama futhi."</string>
@@ -1361,6 +1363,8 @@
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Khetha ukuvimbela ukulungisa iphutha le-USB."</string>
<string name="test_harness_mode_notification_title" msgid="2216359742631914387">"Imodi yokuhlola i-harness inikwe amandla"</string>
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Yenza ukusetha kabusha kwasekuqaleni ukuze ukhubaze imodi yokuqina yokuhlola."</string>
+ <string name="console_running_notification_title" msgid="4955436518220103382">"I-serial console inikwe amandla"</string>
+ <string name="console_running_notification_message" msgid="1331995933976263865">"Ukusebenza kuyathinteka. Ukuze ukhubaze, hlola i-bootloader."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Uketshezi noma ama-debris kumbobo ye-USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Imbobo ye-USB inqanyulwa ngokuzenzakalela. Thepha ukuze ufunde kabanzi."</string>
<string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"KULUNGILE ukusebenzisa imbobo ye-USB"</string>
@@ -1890,10 +1894,8 @@
<string name="default_notification_channel_label" msgid="5929663562028088222">"Akufakwanga esigabeni"</string>
<string name="importance_from_user" msgid="7318955817386549931">"Usethe ukubaluleka kwalezi zaziso."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Lokhu kubalulekile ngenxa yabantu ababandakanyekayo."</string>
- <!-- no translation found for user_creation_account_exists (6559477114648176531) -->
- <skip />
- <!-- no translation found for user_creation_adding (9089159170398841763) -->
- <skip />
+ <string name="user_creation_account_exists" msgid="6559477114648176531">"Vumela i-<xliff:g id="APP">%1$s</xliff:g> ukuthi idale umsebenzisi omusha nge-<xliff:g id="ACCOUNT">%2$s</xliff:g> (Umsebenzisi onale akhawunti usevele ukhona) ?"</string>
+ <string name="user_creation_adding" msgid="9089159170398841763">"Vumela i-<xliff:g id="APP">%1$s</xliff:g> ukuthi idale umsebenzisi omusha nge-<xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Engeza ulwimi"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Okuncamelayo kwesifunda"</string>
<string name="search_language_hint" msgid="7042102592055108574">"Thayipha igama lolimi"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 0c9ae85eab30..acaaeec7b2ee 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3701,6 +3701,8 @@
<flag name="flagRequestFingerprintGestures" value="0x00000200" />
<!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK}. -->
<flag name="flagRequestShortcutWarningDialogSpokenFeedback" value="0x00000400" />
+ <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_HANDLE_SHORTCUT}. -->
+ <flag name="flagHandleShortcut" value="0x00000800" />
</attr>
<!-- Component name of an activity that allows the user to modify
the settings for this service. This setting cannot be changed at runtime. -->
@@ -3755,6 +3757,16 @@
<attr name="summary" />
</declare-styleable>
+ <!-- Use <code>accessibility-shortcut-target</code> as the root tag of the XML resource that
+ describes an activity, which is referenced from the
+ <code>android.accessibilityshortcut.target</code> meta-data entry. -->
+ <declare-styleable name="AccessibilityShortcutTarget">
+ <!-- Short description of the target of accessibility shortcut purpose or behavior.-->
+ <attr name="description" />
+ <!-- Brief summary of the target of accessibility shortcut purpose or behavior. -->
+ <attr name="summary" />
+ </declare-styleable>
+
<!-- Use <code>print-service</code> as the root tag of the XML resource that
describes an {@link android.printservice.PrintService} service, which is
referenced from its {@link android.printservice.PrintService#SERVICE_META_DATA}
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 3ea8a778ba9b..ffcfe4310f06 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -295,6 +295,12 @@
<!-- Additional flag from base permission type: this permission can be automatically
granted to the system app predictor -->
<flag name="appPredictor" value="0x200000" />
+ <!-- Additional flag from base permission type: this permission can be automatically
+ granted to the system telephony apps -->
+ <flag name="telephony" value="0x400000" />
+ <!-- Additional flag from base permission type: this permission can be automatically
+ granted to the system wifi app-->
+ <flag name="wifi" value="0x800000" />
</attr>
<!-- Flags indicating more context for a permission group. -->
@@ -349,10 +355,10 @@
will be given a single shared user ID, so they can for example run
in the same process. Note that for them to actually get the same
user ID, they must also be signed with the same signature.
- @deprecated Shared user id's cause non-deterministic behaviour within the
- package manager. As such, it's use is discouraged, deprecated, and will
- be removed altogether in a future version of Android. Instead, proper
- communication mechanisms such as services and providers should be used
+ @deprecated Shared user IDs cause non-deterministic behavior within the
+ package manager. As such, its use is strongly discouraged and may be
+ removed in a future version of Android. Instead, apps should use proper
+ communication mechanisms, such as services and content providers,
to facilitate interoperability between shared components. -->
<attr name="sharedUserId" format="string" />
@@ -2840,6 +2846,9 @@
<!-- The name of the overlayable whose resources will be overlaid. -->
<attr name="targetName" />
+
+ <!-- The xml file that defines the target id to overlay value mappings. -->
+ <attr name="resourcesMap" format="reference" />
</declare-styleable>
<!-- Declaration of an {@link android.content.Intent} object in XML. May
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7594afe27f2f..159517c79285 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -387,6 +387,12 @@
</string-array>
<!-- List of regexpressions describing the interface (if any) that represent tetherable
+ Wifi P2P interfaces. If the device doesn't want to support tethering over Wifi P2p this
+ should be empty. An example would be "p2p-p2p.*" -->
+ <string-array translatable="false" name="config_tether_wifi_p2p_regexs">
+ </string-array>
+
+ <!-- List of regexpressions describing the interface (if any) that represent tetherable
WiMAX interfaces. If the device doesn't want to support tethering over Wifi this
should be empty. An example would be "softap.*" -->
<string-array translatable="false" name="config_tether_wimax_regexs">
@@ -433,6 +439,18 @@
-->
</string-array>
+ <!-- Configuration of network interfaces that support WakeOnLAN -->
+ <string-array translatable="false" name="config_wakeonlan_supported_interfaces">
+ <!--
+ <item>wlan0</item>
+ <item>eth0</item>
+ -->
+ </string-array>
+
+ <!-- Package name for the default CellBroadcastService module [DO NOT TRANSLATE] -->
+ <string name="cellbroadcast_default_package" translatable="false">com.android.cellbroadcastservice
+ </string>
+
<!-- If the mobile hotspot feature requires provisioning, a package name and class name
can be provided to launch a supported application that provisions the devices.
@@ -749,6 +767,9 @@
<!-- Indicates that p2p MAC randomization is supported on this device -->
<bool translatable="false" name="config_wifi_p2p_mac_randomization_supported">false</bool>
+ <!-- Indicates that AP mode MAC randomization is supported on this device -->
+ <bool translatable="false" name="config_wifi_ap_mac_randomization_supported">true</bool>
+
<!-- flag for activating paranoid MAC randomization on a limited set of SSIDs -->
<bool translatable="false" name="config_wifi_aggressive_randomization_ssid_whitelist_enabled">false</bool>
@@ -2533,6 +2554,16 @@
will be locked. -->
<bool name="config_multiuserDelayUserDataLocking">false</bool>
+ <!-- Whether to only install system packages on a user if they're whitelisted for that user
+ type. These are flags and can be freely combined.
+ 0 (0b000) - disable whitelist (install all system packages; no logging)
+ 1 (0b001) - enforce (only install system packages if they are whitelisted)
+ 2 (0b010) - log (log when a non-whitelisted package is run)
+ 4 (0b100) - treat any package not mentioned in the whitelist file as implicitly whitelisted
+ Note: This list must be kept current with PACKAGE_WHITELIST_MODE_PROP in
+ frameworks/base/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java -->
+ <integer name="config_userTypePackageWhitelistMode">5</integer> <!-- 0b101 -->
+
<!-- Whether UI for multi user should be shown -->
<bool name="config_enableMultiUserUI">false</bool>
@@ -3530,6 +3561,9 @@
<!-- emergency call number for the emergency affordance -->
<string name="config_emergency_call_number" translatable="false">112</string>
+ <!-- Package name that provides Emergency Dialer -->
+ <string name="config_emergency_dialer_package">com.android.phone</string>
+
<!-- Do not translate. Mcc codes whose existence trigger the presence of emergency
affordances-->
<integer-array name="config_emergency_mcc_codes" translatable="false">
@@ -3674,6 +3708,21 @@
-->
<string name="config_defaultWellbeingPackage" translatable="false"></string>
+ <!-- The package name for the system telephony apps.
+ This package must be trusted, as it will be granted with permissions with special telephony
+ protection level. Note, framework by default support multiple telephony apps, each package
+ name is separated by comma.
+ Example: "com.android.phone,com.android.stk,com.android.providers.telephony"
+ -->
+ <string name="config_telephonyPackages" translatable="false">"com.android.phone,com.android.stk,com.android.providers.telephony,com.android.ons"</string>
+
+ <!-- The package name for the default system wifi app.
+ This package must be trusted, as it has the permissions to control wifi
+ connectivity on the device.
+ Example: "com.android.wifi"
+ -->
+ <string name="config_wifiPackage" translatable="false">"com.android.wifi"</string>
+
<!-- The component name for the default system attention service.
This service must be trusted, as it can be activated without explicit consent of the user.
See android.attention.AttentionManagerService.
@@ -4304,11 +4353,11 @@
<!-- Trigger a warning for notifications with RemoteView objects that are larger in bytes than
this value (default 1MB)-->
- <integer name="config_notificationWarnRemoteViewSizeBytes">1000000</integer>
+ <integer name="config_notificationWarnRemoteViewSizeBytes">2000000</integer>
<!-- Strip notification RemoteView objects that are larger in bytes than this value (also log)
(default 2MB) -->
- <integer name="config_notificationStripRemoteViewSizeBytes">2000000</integer>
+ <integer name="config_notificationStripRemoteViewSizeBytes">5000000</integer>
<!-- Contains a blacklist of apps that should not get pre-installed carrier app permission
grants, even if the UICC claims that the app should be privileged. See b/138150105 -->
@@ -4331,6 +4380,10 @@
to have a confirmed way to dismiss the keyboard when desired. -->
<bool name="config_automotiveHideNavBarForKeyboard">false</bool>
+ <!-- Whether or not to show the built-in charging animation when the device begins charging
+ wirelessly. -->
+ <bool name="config_showBuiltinWirelessChargingAnim">true</bool>
+
<!-- pdp data retry for cause 29, 33 and 55-->
<bool name="config_pdp_retry_for_29_33_55_enabled">false</bool>
<!--pdp data reject retry delay can be configured here -->
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 609659b62948..9791241a52af 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -41,6 +41,8 @@
<dimen name="quick_qs_offset_height">48dp</dimen>
<!-- Total height of QQS (quick_qs_offset_height + 128) -->
<dimen name="quick_qs_total_height">176dp</dimen>
+ <!-- Total height of QQS with two rows to fit media player (quick_qs_offset_height + 176) -->
+ <dimen name="quick_qs_total_height_with_media">224dp</dimen>
<!-- Height of the bottom navigation / system bar. -->
<dimen name="navigation_bar_height">48dp</dimen>
<!-- Height of the bottom navigation bar in portrait; often the same as @dimen/navigation_bar_height -->
@@ -750,7 +752,7 @@
<dimen name="seekbar_thumb_exclusion_max_size">48dp</dimen>
- <!-- chooser (sharesheet) spacing -->
+ <!-- chooser/resolver (sharesheet) spacing -->
<dimen name="chooser_corner_radius">8dp</dimen>
<dimen name="chooser_row_text_option_translate">25dp</dimen>
<dimen name="chooser_view_spacing">18dp</dimen>
@@ -759,11 +761,15 @@
<dimen name="chooser_preview_image_font_size">20sp</dimen>
<dimen name="chooser_preview_image_border">1dp</dimen>
<dimen name="chooser_preview_width">-1px</dimen>
- <dimen name="resolver_icon_size">42dp</dimen>
- <dimen name="resolver_button_bar_spacing">8dp</dimen>
- <dimen name="resolver_badge_size">18dp</dimen>
<dimen name="chooser_target_width">90dp</dimen>
<dimen name="chooser_header_scroll_elevation">4dp</dimen>
<dimen name="chooser_max_collapsed_height">288dp</dimen>
<dimen name="chooser_direct_share_label_placeholder_max_width">72dp</dimen>
+ <dimen name="resolver_icon_size">32dp</dimen>
+ <dimen name="resolver_button_bar_spacing">8dp</dimen>
+ <dimen name="resolver_badge_size">18dp</dimen>
+ <dimen name="resolver_icon_margin">16dp</dimen>
+ <dimen name="resolver_small_margin">18dp</dimen>
+ <dimen name="resolver_edge_margin">24dp</dimen>
+ <dimen name="resolver_elevation">1dp</dimen>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 96c0cf3fd86a..9724c41c1b16 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3000,6 +3000,8 @@
<public-group type="attr" first-id="0x01010607">
<public name="importantForContentCapture" />
<public name="forceQueryable" />
+ <!-- @hide @SystemApi -->
+ <public name="resourcesMap" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d2989d9a8ab6..d39d50739e1d 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -890,6 +890,16 @@
messages. This means the app could monitor or delete messages sent to your
device without showing them to you.</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this.[CHAR LIMIT=NONE] -->
+ <string name="permlab_bindCellBroadcastService">Forward cell broadcast messages</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] -->
+ <string name="permdesc_bindCellBroadcastService">Allows the app to bind to the
+ cell broadcast module in order to forward cell broadcast messages
+ as they are received. Cell broadcast alerts are delivered in some
+ locations to warn you of emergency situations. Malicious apps may
+ interfere with the performance or operation of your device when an
+ emergency cell broadcast is received.</string>
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_readCellBroadcasts">read cell broadcast messages</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -3430,6 +3440,15 @@
<!-- A notification is shown when the user connects to a Wi-Fi network and the system detects that that network has no Internet access. This is the notification's message. -->
<string name="wifi_no_internet_detailed">Tap for options</string>
+ <!-- A notification is shown when the user connects to a mobile network without internet access. This is the notification's title. -->
+ <string name="mobile_no_internet">Mobile network has no internet access</string>
+
+ <!-- A notification is shown when the user connects to a non-mobile and non-wifi network without internet access. This is the notification's title. -->
+ <string name="other_networks_no_internet">Network has no internet access</string>
+
+ <!-- A notification is shown when connected network without internet due to private dns validation failed. This is the notification's message. [CHAR LIMIT=NONE] -->
+ <string name="private_dns_broken_detailed">Private DNS server cannot be accessed</string>
+
<!-- A notification is shown after the user logs in to a captive portal network, to indicate that the network should now have internet connectivity. This is the message of notification. [CHAR LIMIT=50] -->
<string name="captive_portal_logged_in_detailed">Connected</string>
<!-- A notification is shown when the user connects to a network that doesn't have access to some services (e.g. Push notifications may not work). This is the notification's title. [CHAR LIMIT=50] -->
@@ -3629,6 +3648,11 @@
<!-- Message of notification shown when Test Harness Mode is enabled. [CHAR LIMIT=NONE] -->
<string name="test_harness_mode_notification_message">Perform a factory reset to disable Test Harness Mode.</string>
+ <!-- Title of notification shown when serial console is enabled. [CHAR LIMIT=NONE] -->
+ <string name="console_running_notification_title">Serial console enabled</string>
+ <!-- Message of notification shown when serial console is enabled. [CHAR LIMIT=NONE] -->
+ <string name="console_running_notification_message">Performance is impacted. To disable, check bootloader.</string>
+
<!-- Title of notification shown when contaminant is detected on the USB port. [CHAR LIMIT=NONE] -->
<string name="usb_contaminant_detected_title">Liquid or debris in USB port</string>
<!-- Message of notification shown when contaminant is detected on the USB port. [CHAR LIMIT=NONE] -->
@@ -5190,6 +5214,8 @@
<string name="autofill_save_no">No thanks</string>
<!-- Label for the autofill update button [CHAR LIMIT=NONE] -->
<string name="autofill_update_yes">Update</string>
+ <!-- Label for the autofill continue button [CHAR LIMIT=NONE] -->
+ <string name="autofill_continue_yes">Continue</string>
<!-- Label for the type of data being saved for autofill when it represent user credentials with a password [CHAR LIMIT=NONE] -->
<string name="autofill_save_type_password">password</string>
@@ -5197,6 +5223,12 @@
<string name="autofill_save_type_address">address</string>
<!-- Label for the type of data being saved for autofill when it represents a credit card [CHAR LIMIT=NONE] -->
<string name="autofill_save_type_credit_card">credit card</string>
+ <!-- Label for the type of data being saved for autofill when it represents a debit card [CHAR LIMIT=NONE] -->
+ <string name="autofill_save_type_debit_card">debit card</string>
+ <!-- Label for the type of data being saved for autofill when it represents a payment card [CHAR LIMIT=NONE] -->
+ <string name="autofill_save_type_payment_card">payment card</string>
+ <!-- Label for the type of data being saved for autofill when it represents a card that does not a specified type or cannot identify what the type is for [CHAR LIMIT=NONE] -->
+ <string name="autofill_save_type_generic_card">card</string>
<!-- Label for the type of data being saved for autofill when it represents an username [CHAR LIMIT=NONE] -->
<string name="autofill_save_type_username">username</string>
<!-- Label for the type of data being saved for autofill when it represents an email address [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a0b399654abc..5de931aad563 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -512,6 +512,7 @@
<java-symbol type="integer" name="config_multiuserMaximumUsers" />
<java-symbol type="integer" name="config_multiuserMaxRunningUsers" />
<java-symbol type="bool" name="config_multiuserDelayUserDataLocking" />
+ <java-symbol type="integer" name="config_userTypePackageWhitelistMode"/>
<java-symbol type="integer" name="config_safe_media_volume_index" />
<java-symbol type="integer" name="config_safe_media_volume_usb_mB" />
<java-symbol type="integer" name="config_mobile_mtu" />
@@ -593,9 +594,12 @@
<java-symbol type="string" name="menu_space_shortcut_label" />
<java-symbol type="string" name="menu_shift_shortcut_label" />
<java-symbol type="string" name="menu_sym_shortcut_label" />
+ <java-symbol type="string" name="mobile_no_internet" />
<java-symbol type="string" name="notification_title" />
+ <java-symbol type="string" name="other_networks_no_internet" />
<java-symbol type="string" name="permission_request_notification_with_subtitle" />
<java-symbol type="string" name="prepend_shortcut_label" />
+ <java-symbol type="string" name="private_dns_broken_detailed" />
<java-symbol type="string" name="paste_as_plain_text" />
<java-symbol type="string" name="replace" />
<java-symbol type="string" name="undo" />
@@ -747,6 +751,8 @@
<java-symbol type="string" name="config_default_dns_server" />
<java-symbol type="string" name="config_ethernet_iface_regex" />
<java-symbol type="array" name="config_ethernet_interfaces" />
+ <java-symbol type="array" name="config_wakeonlan_supported_interfaces" />
+ <java-symbol type="string" name="cellbroadcast_default_package" />
<java-symbol type="string" name="config_forceVoiceInteractionServicePackage" />
<java-symbol type="string" name="config_mms_user_agent" />
<java-symbol type="string" name="config_mms_user_agent_profile_url" />
@@ -1787,6 +1793,7 @@
<java-symbol type="dimen" name="display_cutout_touchable_region_size" />
<java-symbol type="dimen" name="quick_qs_offset_height" />
<java-symbol type="dimen" name="quick_qs_total_height" />
+ <java-symbol type="dimen" name="quick_qs_total_height_with_media" />
<java-symbol type="drawable" name="ic_jog_dial_sound_off" />
<java-symbol type="drawable" name="ic_jog_dial_sound_on" />
<java-symbol type="drawable" name="ic_jog_dial_unlock" />
@@ -1896,15 +1903,14 @@
<java-symbol type="anim" name="screen_rotate_180_enter" />
<java-symbol type="anim" name="screen_rotate_180_exit" />
<java-symbol type="anim" name="screen_rotate_180_frame" />
+ <java-symbol type="anim" name="screen_rotate_alpha"/>
<java-symbol type="anim" name="screen_rotate_finish_enter" />
<java-symbol type="anim" name="screen_rotate_finish_exit" />
<java-symbol type="anim" name="screen_rotate_finish_frame" />
<java-symbol type="anim" name="screen_rotate_minus_90_enter" />
<java-symbol type="anim" name="screen_rotate_minus_90_exit" />
- <java-symbol type="anim" name="screen_rotate_minus_90_frame" />
<java-symbol type="anim" name="screen_rotate_plus_90_enter" />
<java-symbol type="anim" name="screen_rotate_plus_90_exit" />
- <java-symbol type="anim" name="screen_rotate_plus_90_frame" />
<java-symbol type="anim" name="screen_rotate_start_enter" />
<java-symbol type="anim" name="screen_rotate_start_exit" />
<java-symbol type="anim" name="screen_rotate_start_frame" />
@@ -1934,6 +1940,7 @@
<java-symbol type="bool" name="config_tether_upstream_automatic" />
<java-symbol type="array" name="config_tether_usb_regexs" />
<java-symbol type="array" name="config_tether_wifi_regexs" />
+ <java-symbol type="array" name="config_tether_wifi_p2p_regexs" />
<java-symbol type="array" name="config_usbHostBlacklist" />
<java-symbol type="array" name="config_serialPorts" />
<java-symbol type="array" name="radioAttributes" />
@@ -1984,6 +1991,7 @@
<java-symbol type="bool" name="config_wifi_local_only_hotspot_5ghz" />
<java-symbol type="bool" name="config_wifi_connected_mac_randomization_supported" />
<java-symbol type="bool" name="config_wifi_p2p_mac_randomization_supported" />
+ <java-symbol type="bool" name="config_wifi_ap_mac_randomization_supported" />
<java-symbol type="bool" name="config_wifi_aggressive_randomization_ssid_whitelist_enabled" />
<java-symbol type="bool" name="config_wifi_link_probing_supported" />
<java-symbol type="bool" name="config_wifi_fast_bss_transition_enabled" />
@@ -2109,6 +2117,8 @@
<java-symbol type="string" name="adb_active_notification_title" />
<java-symbol type="string" name="test_harness_mode_notification_title" />
<java-symbol type="string" name="test_harness_mode_notification_message" />
+ <java-symbol type="string" name="console_running_notification_title" />
+ <java-symbol type="string" name="console_running_notification_message" />
<java-symbol type="string" name="taking_remote_bugreport_notification_title" />
<java-symbol type="string" name="share_remote_bugreport_notification_title" />
<java-symbol type="string" name="sharing_remote_bugreport_notification_title" />
@@ -3177,6 +3187,7 @@
<java-symbol type="string" name="global_action_emergency" />
<java-symbol type="string" name="config_emergency_call_number" />
+ <java-symbol type="string" name="config_emergency_dialer_package" />
<java-symbol type="array" name="config_emergency_mcc_codes" />
<java-symbol type="string" name="config_dozeDoubleTapSensorType" />
@@ -3366,6 +3377,7 @@
<java-symbol type="string" name="autofill_update_title_with_2types" />
<java-symbol type="string" name="autofill_update_title_with_3types" />
<java-symbol type="string" name="autofill_update_yes" />
+ <java-symbol type="string" name="autofill_continue_yes" />
<java-symbol type="string" name="autofill_save_accessibility_title " />
<java-symbol type="string" name="autofill_save_title" />
<java-symbol type="string" name="autofill_save_title_with_type" />
@@ -3376,6 +3388,9 @@
<java-symbol type="string" name="autofill_save_type_password" />
<java-symbol type="string" name="autofill_save_type_address" />
<java-symbol type="string" name="autofill_save_type_credit_card" />
+ <java-symbol type="string" name="autofill_save_type_debit_card" />
+ <java-symbol type="string" name="autofill_save_type_payment_card" />
+ <java-symbol type="string" name="autofill_save_type_generic_card" />
<java-symbol type="string" name="autofill_save_type_username" />
<java-symbol type="string" name="autofill_save_type_email_address" />
<java-symbol type="drawable" name="autofill_dataset_picker_background" />
@@ -3481,6 +3496,8 @@
<java-symbol type="string" name="config_defaultAutofillService" />
<java-symbol type="string" name="config_defaultTextClassifierPackage" />
<java-symbol type="string" name="config_defaultWellbeingPackage" />
+ <java-symbol type="string" name="config_telephonyPackages" />
+ <java-symbol type="string" name="config_wifiPackage" />
<java-symbol type="string" name="config_defaultContentCaptureService" />
<java-symbol type="string" name="config_defaultAugmentedAutofillService" />
<java-symbol type="string" name="config_defaultAppPredictionService" />
@@ -3844,6 +3861,10 @@
<java-symbol type="dimen" name="resolver_icon_size"/>
<java-symbol type="dimen" name="resolver_badge_size"/>
<java-symbol type="dimen" name="resolver_button_bar_spacing"/>
+ <java-symbol type="dimen" name="resolver_icon_margin"/>
+ <java-symbol type="dimen" name="resolver_small_margin"/>
+ <java-symbol type="dimen" name="resolver_edge_margin"/>
+ <java-symbol type="dimen" name="resolver_elevation"/>
<!-- For DropBox -->
<java-symbol type="integer" name="config_dropboxLowPriorityBroadcastRateLimitPeriod" />
@@ -3882,6 +3903,8 @@
<java-symbol type="bool" name="config_automotiveHideNavBarForKeyboard" />
+ <java-symbol type="bool" name="config_showBuiltinWirelessChargingAnim" />
+
<!-- For Pdn throttle feature -->
<java-symbol type="bool" name="config_pdp_retry_for_29_33_55_enabled" />
<java-symbol type="integer" name="data_retry_delay" />
diff --git a/core/tests/PlatformCompatFramework/Android.bp b/core/tests/PlatformCompatFramework/Android.bp
new file mode 100644
index 000000000000..33802650fa12
--- /dev/null
+++ b/core/tests/PlatformCompatFramework/Android.bp
@@ -0,0 +1,14 @@
+android_test {
+ name: "PlatformCompatFrameworkTests",
+ // Include all test java files.
+ srcs: ["src/**/*.java"],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ static_libs: [
+ "junit",
+ "androidx.test.rules",
+ ],
+ platform_apis: true,
+}
diff --git a/core/tests/PlatformCompatFramework/AndroidManifest.xml b/core/tests/PlatformCompatFramework/AndroidManifest.xml
new file mode 100644
index 000000000000..4a6383b77000
--- /dev/null
+++ b/core/tests/PlatformCompatFramework/AndroidManifest.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.internal.compat">
+ <application android:label="ChangeReporterTest">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.internal.compat"/>
+</manifest> \ No newline at end of file
diff --git a/core/tests/PlatformCompatFramework/OWNERS b/core/tests/PlatformCompatFramework/OWNERS
new file mode 100644
index 000000000000..2b7cdb0cbce9
--- /dev/null
+++ b/core/tests/PlatformCompatFramework/OWNERS
@@ -0,0 +1,7 @@
+# Use this reviewer by default.
+platform-compat-eng+reviews@google.com
+
+andreionea@google.com
+atrost@google.com
+mathewi@google.com
+satayev@google.com
diff --git a/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java b/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java
new file mode 100644
index 000000000000..09bbe37bf292
--- /dev/null
+++ b/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.compat;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class ChangeReporterTest {
+ @Test
+ public void testStatsLogOnce() {
+ ChangeReporter reporter = new ChangeReporter(0);
+ int myUid = 1022, otherUid = 1023;
+ long myChangeId = 500L, otherChangeId = 600L;
+ int myState = 1, otherState = 2;
+
+ assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+ reporter.reportChange(myUid, myChangeId, myState);
+
+ // Same report will not be logged again.
+ assertFalse(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+ // Other reports will be logged.
+ assertTrue(reporter.shouldWriteToStatsLog(otherUid, myChangeId, myState));
+ assertTrue(reporter.shouldWriteToStatsLog(myUid, otherChangeId, myState));
+ assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, otherState));
+ }
+
+ @Test
+ public void testStatsLogAfterReset() {
+ ChangeReporter reporter = new ChangeReporter(0);
+ int myUid = 1022;
+ long myChangeId = 500L;
+ int myState = 1;
+
+ assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+ reporter.reportChange(myUid, myChangeId, myState);
+
+ // Same report will not be logged again.
+ assertFalse(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+ reporter.resetReportedChanges(myUid);
+
+ // Same report will be logged again after reset.
+ assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+ }
+
+ @Test
+ public void testDebugLogOnce() {
+ ChangeReporter reporter = new ChangeReporter(0);
+ int myUid = 1022, otherUid = 1023;
+ long myChangeId = 500L, otherChangeId = 600L;
+ int myState = 1, otherState = 2;
+
+ assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
+ reporter.reportChange(myUid, myChangeId, myState);
+
+ // Same report will not be logged again.
+ assertFalse(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
+ // Other reports will be logged.
+ assertTrue(reporter.shouldWriteToDebug(otherUid, myChangeId, myState));
+ assertTrue(reporter.shouldWriteToDebug(myUid, otherChangeId, myState));
+ assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, otherState));
+ }
+
+ @Test
+ public void testDebugLogAfterReset() {
+ ChangeReporter reporter = new ChangeReporter(0);
+ int myUid = 1022;
+ long myChangeId = 500L;
+ int myState = 1;
+
+ assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
+ reporter.reportChange(myUid, myChangeId, myState);
+
+ // Same report will not be logged again.
+ assertFalse(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
+ reporter.resetReportedChanges(myUid);
+
+ // Same report will be logged again after reset.
+ assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
+ }
+
+ @Test
+ public void testDebugLogWithLogAll() {
+ ChangeReporter reporter = new ChangeReporter(0);
+ int myUid = 1022;
+ long myChangeId = 500L;
+ int myState = 1;
+
+ assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
+ reporter.reportChange(myUid, myChangeId, myState);
+
+ reporter.startDebugLogAll();
+ // Same report will be logged again.
+ assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
+ assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
+
+ reporter.stopDebugLogAll();
+ assertFalse(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
+ }
+}
diff --git a/core/tests/ResourceLoaderTests/Android.bp b/core/tests/ResourceLoaderTests/Android.bp
new file mode 100644
index 000000000000..53db8322f7b8
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/Android.bp
@@ -0,0 +1,63 @@
+//
+// 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.
+//
+
+android_test {
+ name: "FrameworksResourceLoaderTests",
+ srcs: [
+ "src/**/*.kt"
+ ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ static_libs: [
+ "androidx.test.espresso.core",
+ "androidx.test.ext.junit",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "mockito-target-minus-junit4",
+ "truth-prebuilt",
+ ],
+ resource_zips: [ ":FrameworksResourceLoaderTestsAssets" ],
+ test_suites: ["device-tests"],
+ sdk_version: "test_current",
+ aaptflags: [
+ "--no-compress",
+ ],
+ data: [
+ ":FrameworksResourceLoaderTestsOverlay",
+ ":FrameworksResourceLoaderTestsSplitOne",
+ ":FrameworksResourceLoaderTestsSplitTwo",
+ ],
+ java_resources: [ "NonAsset.txt" ]
+}
+
+filegroup {
+ name: "FrameworksResourceLoaderTestsResources",
+ srcs: ["resources"],
+}
+
+genrule {
+ name: "FrameworksResourceLoaderTestsAssets",
+ srcs: [
+ ":framework-res",
+ ":FrameworksResourceLoaderTestsResources",
+ ],
+ tools: [ ":aapt2", ":soong_zip" ],
+ tool_files: [ "resources/compileAndLink.sh" ],
+ cmd: "$(location resources/compileAndLink.sh) $(location :aapt2) $(location :soong_zip) $(genDir) $(in) $(in)",
+ out: [ "out.zip" ]
+}
diff --git a/core/tests/ResourceLoaderTests/AndroidManifest.xml b/core/tests/ResourceLoaderTests/AndroidManifest.xml
new file mode 100644
index 000000000000..00b4ccbd8030
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?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
+ -->
+
+<!-- Split loading is tested separately, so this must be marked isolated -->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.content.res.loader.test"
+ android:isolatedSplits="true"
+ >
+
+ <uses-sdk android:minSdkVersion="29"/>
+
+ <application>
+ <uses-library android:name="android.test.runner"/>
+
+ <activity
+ android:name=".TestActivity"
+ android:configChanges="orientation"
+ />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:label="ResourceLoaderTests"
+ android:targetPackage="android.content.res.loader.test"
+ />
+
+</manifest>
diff --git a/core/tests/ResourceLoaderTests/AndroidTest.xml b/core/tests/ResourceLoaderTests/AndroidTest.xml
new file mode 100644
index 000000000000..702151d01110
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/AndroidTest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<configuration description="Test module config for ResourceLoaderTests">
+ <option name="test-tag" value="ResourceLoaderTests" />
+
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="cleanup-apks" value="true" />
+ <!-- The following value cannot be multi-line as whitespace is parsed by the installer -->
+ <option name="split-apk-file-names"
+ value="FrameworksResourceLoaderTests.apk,FrameworksResourceLoaderTestsSplitOne.apk,FrameworksResourceLoaderTestsSplitTwo.apk" />
+ <option name="test-file-name" value="FrameworksResourceLoaderTestsOverlay.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command"
+ value="cmd overlay disable android.content.res.loader.test.overlay" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="android.content.res.loader.test" />
+ </test>
+</configuration>
diff --git a/core/tests/ResourceLoaderTests/NonAsset.txt b/core/tests/ResourceLoaderTests/NonAsset.txt
new file mode 100644
index 000000000000..5c0b2cc98d64
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/NonAsset.txt
@@ -0,0 +1 @@
+Outside assets directory
diff --git a/core/tests/ResourceLoaderTests/SplitOne/Android.bp b/core/tests/ResourceLoaderTests/SplitOne/Android.bp
new file mode 100644
index 000000000000..897897fbf254
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/SplitOne/Android.bp
@@ -0,0 +1,19 @@
+//
+// 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.
+//
+
+android_test_helper_app {
+ name: "FrameworksResourceLoaderTestsSplitOne"
+}
diff --git a/core/tests/ResourceLoaderTests/SplitOne/AndroidManifest.xml b/core/tests/ResourceLoaderTests/SplitOne/AndroidManifest.xml
new file mode 100644
index 000000000000..b14bd8600f31
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/SplitOne/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?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.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.content.res.loader.test"
+ split="split_one"
+ >
+
+ <uses-sdk android:minSdkVersion="1" android:targetSdkVersion="1" />
+ <application android:hasCode="false" />
+
+</manifest>
diff --git a/core/tests/ResourceLoaderTests/SplitOne/res/values/string_split_one.xml b/core/tests/ResourceLoaderTests/SplitOne/res/values/string_split_one.xml
new file mode 100644
index 000000000000..3c215ebc287c
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/SplitOne/res/values/string_split_one.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources>
+ <public type="string" name="split_overlaid" id="0x7f040001" />
+ <string name="split_overlaid">Split ONE Overlaid</string>
+</resources>
diff --git a/core/tests/ResourceLoaderTests/assets/Asset.txt b/core/tests/ResourceLoaderTests/assets/Asset.txt
new file mode 100644
index 000000000000..03f9a0fd146a
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/assets/Asset.txt
@@ -0,0 +1 @@
+In assets directory
diff --git a/core/tests/ResourceLoaderTests/lib/kotlin-reflect-sources.jar b/core/tests/ResourceLoaderTests/lib/kotlin-reflect-sources.jar
new file mode 100644
index 000000000000..a12e33a34aee
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/lib/kotlin-reflect-sources.jar
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/lib/kotlin-reflect.jar b/core/tests/ResourceLoaderTests/lib/kotlin-reflect.jar
new file mode 100644
index 000000000000..182cbabadfe6
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/lib/kotlin-reflect.jar
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk7-sources.jar b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk7-sources.jar
new file mode 100644
index 000000000000..e6b5f15b8a57
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk7-sources.jar
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk7.jar b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk7.jar
new file mode 100644
index 000000000000..e9c743c60289
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk7.jar
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk8-sources.jar b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk8-sources.jar
new file mode 100644
index 000000000000..cd0536042662
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk8-sources.jar
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk8.jar b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk8.jar
new file mode 100644
index 000000000000..dc8aa90385fd
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-jdk8.jar
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-sources.jar b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-sources.jar
new file mode 100644
index 000000000000..8a672bac4685
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib-sources.jar
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/lib/kotlin-stdlib.jar b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib.jar
new file mode 100644
index 000000000000..56f3d1e385e4
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/lib/kotlin-stdlib.jar
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/lib/kotlin-test-sources.jar b/core/tests/ResourceLoaderTests/lib/kotlin-test-sources.jar
new file mode 100644
index 000000000000..663d3128dd54
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/lib/kotlin-test-sources.jar
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/lib/kotlin-test.jar b/core/tests/ResourceLoaderTests/lib/kotlin-test.jar
new file mode 100644
index 000000000000..5f6e4b8cc988
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/lib/kotlin-test.jar
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/overlay/Android.bp b/core/tests/ResourceLoaderTests/overlay/Android.bp
new file mode 100644
index 000000000000..63e7e61d797a
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/overlay/Android.bp
@@ -0,0 +1,20 @@
+// 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.
+
+android_test {
+ name: "FrameworksResourceLoaderTestsOverlay",
+ sdk_version: "current",
+
+ aaptflags: ["--no-resource-removal"],
+}
diff --git a/core/tests/ResourceLoaderTests/overlay/AndroidManifest.xml b/core/tests/ResourceLoaderTests/overlay/AndroidManifest.xml
new file mode 100644
index 000000000000..942f7da9aa27
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/overlay/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?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.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.content.res.loader.test.overlay"
+ >
+
+ <application android:hasCode="false" />
+
+ <overlay android:targetPackage="android.content.res.loader.test" />
+
+</manifest>
diff --git a/core/tests/ResourceLoaderTests/overlay/res/values/strings.xml b/core/tests/ResourceLoaderTests/overlay/res/values/strings.xml
new file mode 100644
index 000000000000..348bb353611a
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/overlay/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ -->
+
+<resources>
+
+ <string name="loader_path_change_test">Overlaid</string>
+
+</resources>
diff --git a/core/tests/ResourceLoaderTests/res/drawable-nodpi/non_asset_bitmap.png b/core/tests/ResourceLoaderTests/res/drawable-nodpi/non_asset_bitmap.png
new file mode 100644
index 000000000000..efd71ee039e2
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/res/drawable-nodpi/non_asset_bitmap.png
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/res/drawable-nodpi/non_asset_drawable.xml b/core/tests/ResourceLoaderTests/res/drawable-nodpi/non_asset_drawable.xml
new file mode 100644
index 000000000000..d1211c50a203
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/res/drawable-nodpi/non_asset_drawable.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<color
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="#B2D2F2"
+ />
diff --git a/core/tests/ResourceLoaderTests/res/layout/layout.xml b/core/tests/ResourceLoaderTests/res/layout/layout.xml
new file mode 100644
index 000000000000..d59059b453d6
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/res/layout/layout.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ />
+
diff --git a/core/tests/ResourceLoaderTests/res/values/strings.xml b/core/tests/ResourceLoaderTests/res/values/strings.xml
new file mode 100644
index 000000000000..28b8f73d45a6
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/res/values/strings.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+
+<resources>
+
+ <string name="loader_path_change_test">Not overlaid</string>
+ <string name="split_overlaid">Not overlaid</string>
+
+</resources>
diff --git a/core/tests/ResourceLoaderTests/resources/AndroidManifestApp.xml b/core/tests/ResourceLoaderTests/resources/AndroidManifestApp.xml
new file mode 100644
index 000000000000..5dd8a966e2b7
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/AndroidManifestApp.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.content.res.loader.test"
+ >
+
+ <uses-sdk android:minSdkVersion="1" android:targetSdkVersion="1" />
+ <application/>
+
+</manifest>
diff --git a/core/tests/ResourceLoaderTests/resources/AndroidManifestFramework.xml b/core/tests/ResourceLoaderTests/resources/AndroidManifestFramework.xml
new file mode 100644
index 000000000000..5a92ae9e662b
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/AndroidManifestFramework.xml
@@ -0,0 +1,27 @@
+<?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.
+ -->
+
+<!-- Mocks the framework package name so that AAPT2 assigns the correct package -->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android"
+ >
+
+ <uses-sdk android:minSdkVersion="1" android:targetSdkVersion="1" />
+ <application/>
+
+</manifest>
diff --git a/core/tests/ResourceLoaderTests/resources/compileAndLink.sh b/core/tests/ResourceLoaderTests/resources/compileAndLink.sh
new file mode 100755
index 000000000000..885f681f4261
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/compileAndLink.sh
@@ -0,0 +1,108 @@
+#!/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.
+
+aapt2=$1
+soong_zip=$2
+genDir=$3
+FRAMEWORK_RES_APK=$4
+inDir=$5
+
+# (String name, boolean retainFiles = false, String... files)
+function compileAndLink {
+ moduleName=$1
+ mkdir "$genDir"/out/"$moduleName"
+
+ args=""
+ for arg in "${@:4}"; do
+ if [[ $arg == res* ]]; then
+ args="$args $inDir/$arg"
+ else
+ args="$args $arg"
+ fi
+ done
+
+ $aapt2 compile -o "$genDir"/out/"$moduleName" $args
+
+ $aapt2 link \
+ -I "$FRAMEWORK_RES_APK" \
+ --manifest "$inDir"/"$3" \
+ -o "$genDir"/out/"$moduleName"/apk.apk \
+ "$genDir"/out/"$moduleName"/*.flat \
+ --no-compress
+
+ unzip -qq "$genDir"/out/"$moduleName"/apk.apk -d "$genDir"/out/"$moduleName"/unzip
+
+ if [[ "$2" == "APK_WITHOUT_FILE" || "$2" == "BOTH_WITHOUT_FILE" ]]; then
+ zip -q -d "$genDir"/out/"$moduleName"/apk.apk "res/*"
+ cp "$genDir"/out/"$moduleName"/apk.apk "$genDir"/output/raw/"$moduleName"Apk.apk
+ elif [[ "$2" == "APK" || "$2" == "BOTH" ]]; then
+ cp "$genDir"/out/"$moduleName"/apk.apk "$genDir"/output/raw/"$moduleName"Apk.apk
+ fi
+
+ if [[ "$2" == "ARSC" || "$2" == "BOTH" || "$2" == "BOTH_WITHOUT_FILE" ]]; then
+ zip -d "$genDir"/out/"$moduleName"/apk.apk "res/*"
+ cp "$genDir"/out/"$moduleName"/unzip/resources.arsc "$genDir"/output/raw/"$moduleName"Arsc.arsc
+ fi
+}
+
+rm -r "$genDir"/out
+rm -r "$genDir"/output
+rm -r "$genDir"/temp
+
+mkdir "$genDir"/out
+mkdir -p "$genDir"/output/raw
+mkdir -p "$genDir"/temp/res/drawable-nodpi
+mkdir -p "$genDir"/temp/res/layout
+
+compileAndLink stringOne BOTH AndroidManifestFramework.xml res/values/string_one.xml
+compileAndLink stringTwo BOTH AndroidManifestFramework.xml res/values/string_two.xml
+
+compileAndLink dimenOne BOTH AndroidManifestFramework.xml res/values/dimen_one.xml
+compileAndLink dimenTwo BOTH AndroidManifestFramework.xml res/values/dimen_two.xml
+
+compileAndLink drawableMdpiWithoutFile BOTH_WITHOUT_FILE AndroidManifestFramework.xml res/values/drawable_one.xml res/drawable-mdpi/ic_delete.png
+compileAndLink drawableMdpiWithFile APK AndroidManifestFramework.xml res/values/drawable_one.xml res/drawable-mdpi/ic_delete.png
+
+compileAndLink layoutWithoutFile BOTH_WITHOUT_FILE AndroidManifestFramework.xml res/values/activity_list_item_id.xml res/layout/activity_list_item.xml
+compileAndLink layoutWithFile APK AndroidManifestFramework.xml res/values/activity_list_item_id.xml res/layout/activity_list_item.xml
+
+cp -f "$inDir"/res/layout/layout_one.xml "$genDir"/temp/res/layout/layout.xml
+compileAndLink layoutOne ARSC AndroidManifestApp.xml "$genDir"/temp/res/layout/layout.xml res/values/layout_id.xml
+cp -f "$genDir"/out/layoutOne/unzip/res/layout/layout.xml "$genDir"/output/raw/layoutOne.xml
+
+cp -f "$inDir"/res/layout/layout_two.xml "$genDir"/temp/res/layout/layout.xml
+compileAndLink layoutTwo ARSC AndroidManifestApp.xml "$genDir"/temp/res/layout/layout.xml res/values/layout_id.xml
+cp -f "$genDir"/out/layoutTwo/unzip/res/layout/layout.xml "$genDir"/output/raw/layoutTwo.xml
+
+drawableNoDpi="/res/drawable-nodpi"
+inDirDrawableNoDpi="$inDir$drawableNoDpi"
+
+cp -f "$inDirDrawableNoDpi"/nonAssetDrawableOne.xml "$genDir"/temp/res/drawable-nodpi/non_asset_drawable.xml
+compileAndLink nonAssetDrawableOne ARSC AndroidManifestApp.xml "$genDir"/temp/res/drawable-nodpi/non_asset_drawable.xml res/values/non_asset_drawable_id.xml
+cp -f "$genDir"/out/nonAssetDrawableOne/unzip/res/drawable-nodpi-v4/non_asset_drawable.xml "$genDir"/output/raw/nonAssetDrawableOne.xml
+
+cp -f "$inDirDrawableNoDpi"/nonAssetDrawableTwo.xml "$genDir"/temp/res/drawable-nodpi/non_asset_drawable.xml
+compileAndLink nonAssetDrawableTwo ARSC AndroidManifestApp.xml "$genDir"/temp/res/drawable-nodpi/non_asset_drawable.xml res/values/non_asset_drawable_id.xml
+cp -f "$genDir"/out/nonAssetDrawableTwo/unzip/res/drawable-nodpi-v4/non_asset_drawable.xml "$genDir"/output/raw/nonAssetDrawableTwo.xml
+
+cp -f "$inDirDrawableNoDpi"/nonAssetBitmapGreen.png "$genDir"/temp/res/drawable-nodpi/non_asset_bitmap.png
+compileAndLink nonAssetBitmapGreen BOTH AndroidManifestApp.xml "$genDir"/temp/res/drawable-nodpi/non_asset_bitmap.png res/values/non_asset_bitmap_id.xml
+cp -f "$genDir"/out/nonAssetBitmapGreen/unzip/res/drawable-nodpi-v4/non_asset_bitmap.png "$genDir"/output/raw/nonAssetBitmapGreen.png
+
+cp -f "$inDirDrawableNoDpi"/nonAssetBitmapBlue.png "$genDir"/temp/res/drawable-nodpi/non_asset_bitmap.png
+compileAndLink nonAssetBitmapBlue ARSC AndroidManifestApp.xml "$genDir"/temp/res/drawable-nodpi/non_asset_bitmap.png res/values/non_asset_bitmap_id.xml
+cp -f "$genDir"/out/nonAssetBitmapBlue/unzip/res/drawable-nodpi-v4/non_asset_bitmap.png "$genDir"/output/raw/nonAssetBitmapBlue.png
+
+$soong_zip -o "$genDir"/out.zip -C "$genDir"/output/ -D "$genDir"/output/
diff --git a/core/tests/ResourceLoaderTests/resources/res/drawable-mdpi/ic_delete.png b/core/tests/ResourceLoaderTests/resources/res/drawable-mdpi/ic_delete.png
new file mode 100644
index 000000000000..f3e53d7596c1
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/drawable-mdpi/ic_delete.png
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapBlue.png b/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapBlue.png
new file mode 100644
index 000000000000..5231d175569e
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapBlue.png
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapGreen.png b/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapGreen.png
new file mode 100644
index 000000000000..671d6d00be31
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetBitmapGreen.png
Binary files differ
diff --git a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableOne.xml b/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableOne.xml
new file mode 100644
index 000000000000..f1a93d2d2f21
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableOne.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<color
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="#A3C3E3"
+ />
diff --git a/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableTwo.xml b/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableTwo.xml
new file mode 100644
index 000000000000..7c455a57fb0b
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/drawable-nodpi/nonAssetDrawableTwo.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<color
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="#3A3C3E"
+ />
diff --git a/core/tests/ResourceLoaderTests/resources/res/layout/activity_list_item.xml b/core/tests/ResourceLoaderTests/resources/res/layout/activity_list_item.xml
new file mode 100644
index 000000000000..d59059b453d6
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/layout/activity_list_item.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ />
+
diff --git a/core/tests/ResourceLoaderTests/resources/res/layout/layout_one.xml b/core/tests/ResourceLoaderTests/resources/res/layout/layout_one.xml
new file mode 100644
index 000000000000..ede3838be8de
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/layout/layout_one.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ />
+
diff --git a/core/tests/ResourceLoaderTests/resources/res/layout/layout_two.xml b/core/tests/ResourceLoaderTests/resources/res/layout/layout_two.xml
new file mode 100644
index 000000000000..d8bff90d56d8
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/layout/layout_two.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ />
+
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/activity_list_item_id.xml b/core/tests/ResourceLoaderTests/resources/res/values/activity_list_item_id.xml
new file mode 100644
index 000000000000..a552431e23be
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/values/activity_list_item_id.xml
@@ -0,0 +1,20 @@
+<?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
+ -->
+
+<resources>
+ <public type="layout" name="activity_list_item" id="0x01090000" />
+</resources>
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/dimen_one.xml b/core/tests/ResourceLoaderTests/resources/res/values/dimen_one.xml
new file mode 100644
index 000000000000..69ecf2316284
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/values/dimen_one.xml
@@ -0,0 +1,21 @@
+<?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
+ -->
+
+<resources>
+ <public type="dimen" name="app_icon_size" id="0x01050000" />
+ <dimen name="app_icon_size">564716dp</dimen>
+</resources>
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/dimen_two.xml b/core/tests/ResourceLoaderTests/resources/res/values/dimen_two.xml
new file mode 100644
index 000000000000..4d55deffbd2a
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/values/dimen_two.xml
@@ -0,0 +1,21 @@
+<?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
+ -->
+
+<resources>
+ <public type="dimen" name="app_icon_size" id="0x01050000" />
+ <dimen name="app_icon_size">565717dp</dimen>
+</resources>
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/drawable_one.xml b/core/tests/ResourceLoaderTests/resources/res/values/drawable_one.xml
new file mode 100644
index 000000000000..b5b4dfd22231
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/values/drawable_one.xml
@@ -0,0 +1,20 @@
+<?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
+ -->
+
+<resources>
+ <public type="drawable" name="ic_delete" id="0x0108001d" />
+</resources>
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/layout_id.xml b/core/tests/ResourceLoaderTests/resources/res/values/layout_id.xml
new file mode 100644
index 000000000000..4962a07bc8c7
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/values/layout_id.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources>
+ <public type="layout" name="layout" id="0x7f020000" />
+</resources>
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/non_asset_bitmap_id.xml b/core/tests/ResourceLoaderTests/resources/res/values/non_asset_bitmap_id.xml
new file mode 100644
index 000000000000..38b152beb76f
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/values/non_asset_bitmap_id.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources>
+ <public type="drawable" name="non_asset_bitmap" id="0x7f010000" />
+</resources>
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/non_asset_drawable_id.xml b/core/tests/ResourceLoaderTests/resources/res/values/non_asset_drawable_id.xml
new file mode 100644
index 000000000000..bdd6f58e5824
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/values/non_asset_drawable_id.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources>
+ <public type="drawable" name="non_asset_drawable" id="0x7f010001" />
+</resources>
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/string_one.xml b/core/tests/ResourceLoaderTests/resources/res/values/string_one.xml
new file mode 100644
index 000000000000..4fc52723946e
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/values/string_one.xml
@@ -0,0 +1,21 @@
+<?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
+ -->
+
+<resources>
+ <public type="string" name="cancel" id="0x01040000" />
+ <string name="cancel">SomeRidiculouslyUnlikelyStringOne</string>
+</resources>
diff --git a/core/tests/ResourceLoaderTests/resources/res/values/string_two.xml b/core/tests/ResourceLoaderTests/resources/res/values/string_two.xml
new file mode 100644
index 000000000000..3604d7b21cf5
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/resources/res/values/string_two.xml
@@ -0,0 +1,21 @@
+<?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
+ -->
+
+<resources>
+ <public type="string" name="cancel" id="0x01040000" />
+ <string name="cancel">SomeRidiculouslyUnlikelyStringTwo</string>
+</resources>
diff --git a/core/tests/ResourceLoaderTests/splits/Android.bp b/core/tests/ResourceLoaderTests/splits/Android.bp
new file mode 100644
index 000000000000..4582808934df
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/splits/Android.bp
@@ -0,0 +1,19 @@
+//
+// 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.
+//
+
+android_test_helper_app {
+ name: "FrameworksResourceLoaderTestsSplitTwo"
+}
diff --git a/core/tests/ResourceLoaderTests/splits/AndroidManifest.xml b/core/tests/ResourceLoaderTests/splits/AndroidManifest.xml
new file mode 100644
index 000000000000..aad8c27a1a3b
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/splits/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?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.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.content.res.loader.test"
+ split="split_two"
+ >
+
+ <uses-sdk android:minSdkVersion="1" android:targetSdkVersion="1" />
+ <application android:hasCode="false" />
+
+</manifest>
diff --git a/core/tests/ResourceLoaderTests/splits/res/values/string_split_two.xml b/core/tests/ResourceLoaderTests/splits/res/values/string_split_two.xml
new file mode 100644
index 000000000000..a367063dd43e
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/splits/res/values/string_split_two.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources>
+ <public type="string" name="split_overlaid" id="0x7f040001" />
+ <string name="split_overlaid">Split TWO Overlaid</string>
+</resources>
diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/DirectoryResourceLoaderTest.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/DirectoryResourceLoaderTest.kt
new file mode 100644
index 000000000000..b1bdc967e68f
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/DirectoryResourceLoaderTest.kt
@@ -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 android.content.res.loader.test
+
+import android.content.res.loader.DirectoryResourceLoader
+import android.content.res.loader.ResourceLoader
+import android.graphics.Color
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.ColorDrawable
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestName
+import java.io.File
+
+class DirectoryResourceLoaderTest : ResourceLoaderTestBase() {
+
+ @get:Rule
+ val testName = TestName()
+
+ private lateinit var testDir: File
+ private lateinit var loader: ResourceLoader
+
+ @Before
+ fun setUpTestDir() {
+ testDir = context.filesDir.resolve("DirectoryResourceLoaderTest_${testName.methodName}")
+ loader = DirectoryResourceLoader(testDir)
+ }
+
+ @After
+ fun deleteTestFiles() {
+ testDir.deleteRecursively()
+ }
+
+ @Test
+ fun loadDrawableXml() {
+ "nonAssetDrawableOne" writeTo "res/drawable-nodpi-v4/non_asset_drawable.xml"
+ val provider = openArsc("nonAssetDrawableOne")
+
+ fun getValue() = (resources.getDrawable(R.drawable.non_asset_drawable) as ColorDrawable)
+ .color
+
+ assertThat(getValue()).isEqualTo(Color.parseColor("#B2D2F2"))
+
+ addLoader(loader to provider)
+
+ assertThat(getValue()).isEqualTo(Color.parseColor("#A3C3E3"))
+ }
+
+ @Test
+ fun loadDrawableBitmap() {
+ "nonAssetBitmapGreen" writeTo "res/drawable-nodpi-v4/non_asset_bitmap.png"
+ val provider = openArsc("nonAssetBitmapGreen")
+
+ fun getValue() = (resources.getDrawable(R.drawable.non_asset_bitmap) as BitmapDrawable)
+ .bitmap.getColor(0, 0).toArgb()
+
+ assertThat(getValue()).isEqualTo(Color.RED)
+
+ addLoader(loader to provider)
+
+ assertThat(getValue()).isEqualTo(Color.GREEN)
+ }
+
+ @Test
+ fun loadXml() {
+ "layoutOne" writeTo "res/layout/layout.xml"
+ val provider = openArsc("layoutOne")
+
+ fun getValue() = resources.getLayout(R.layout.layout).advanceToRoot().name
+
+ assertThat(getValue()).isEqualTo("FrameLayout")
+
+ addLoader(loader to provider)
+
+ assertThat(getValue()).isEqualTo("RelativeLayout")
+ }
+
+ private infix fun String.writeTo(path: String) {
+ val testFile = testDir.resolve(path)
+ testFile.parentFile!!.mkdirs()
+ resources.openRawResource(rawFile(this))
+ .copyTo(testFile.outputStream())
+ }
+}
diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderAssetTest.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderAssetTest.kt
new file mode 100644
index 000000000000..a6a83789c082
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderAssetTest.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res.loader.test
+
+import android.content.res.AssetManager
+import android.content.res.loader.DirectoryResourceLoader
+import android.content.res.loader.ResourceLoader
+import android.content.res.loader.ResourcesProvider
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestName
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.mock
+import java.io.File
+import java.io.FileNotFoundException
+import java.io.IOException
+import java.nio.file.Paths
+
+@RunWith(Parameterized::class)
+class ResourceLoaderAssetTest : ResourceLoaderTestBase() {
+
+ companion object {
+ private const val BASE_TEST_PATH = "android/content/res/loader/test/file.txt"
+ private const val TEST_TEXT = "some text"
+
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun parameters(): Array<Array<out Any?>> {
+ val fromInputStream: ResourceLoader.(String) -> Any? = {
+ loadAsset(eq(it), anyInt())
+ }
+
+ val fromFileDescriptor: ResourceLoader.(String) -> Any? = {
+ loadAssetFd(eq(it))
+ }
+
+ val openAsset: AssetManager.() -> String? = {
+ open(BASE_TEST_PATH).reader().readText()
+ }
+
+ val openNonAsset: AssetManager.() -> String? = {
+ openNonAssetFd(BASE_TEST_PATH).readText()
+ }
+
+ return arrayOf(
+ arrayOf("assets", fromInputStream, openAsset),
+ arrayOf("", fromFileDescriptor, openNonAsset)
+ )
+ }
+ }
+
+ @get:Rule
+ val testName = TestName()
+
+ @JvmField
+ @field:Parameterized.Parameter(0)
+ var prefix: String? = null
+
+ @field:Parameterized.Parameter(1)
+ lateinit var loadAssetFunction: ResourceLoader.(String) -> Any?
+
+ @field:Parameterized.Parameter(2)
+ lateinit var openAssetFunction: AssetManager.() -> String?
+
+ private val testPath: String
+ get() = Paths.get(prefix.orEmpty(), BASE_TEST_PATH).toString()
+
+ private fun ResourceLoader.loadAsset() = loadAssetFunction(testPath)
+
+ private fun AssetManager.openAsset() = openAssetFunction()
+
+ private lateinit var testDir: File
+
+ @Before
+ fun setUpTestDir() {
+ testDir = context.filesDir.resolve("DirectoryResourceLoaderTest_${testName.methodName}")
+ testDir.resolve(testPath).apply { parentFile!!.mkdirs() }.writeText(TEST_TEXT)
+ }
+
+ @Test
+ fun multipleLoadersSearchesBackwards() {
+ // DirectoryResourceLoader relies on a private field and can't be spied directly, so wrap it
+ val loader = DirectoryResourceLoader(testDir)
+ val loaderWrapper = mock(ResourceLoader::class.java).apply {
+ doAnswer { loader.loadAsset(it.arguments[0] as String, it.arguments[1] as Int) }
+ .`when`(this).loadAsset(anyString(), anyInt())
+ doAnswer { loader.loadAssetFd(it.arguments[0] as String) }
+ .`when`(this).loadAssetFd(anyString())
+ }
+
+ val one = loaderWrapper to ResourcesProvider.empty()
+ val two = mockLoader {
+ doReturn(null).`when`(it).loadAsset()
+ }
+
+ addLoader(one, two)
+
+ assertOpenedAsset()
+ inOrder(two.first, one.first).apply {
+ verify(two.first).loadAsset()
+ verify(one.first).loadAsset()
+ }
+ }
+
+ @Test(expected = FileNotFoundException::class)
+ fun failToFindThrowsFileNotFound() {
+ val one = mockLoader {
+ doReturn(null).`when`(it).loadAsset()
+ }
+ val two = mockLoader {
+ doReturn(null).`when`(it).loadAsset()
+ }
+
+ addLoader(one, two)
+
+ assertOpenedAsset()
+ }
+
+ @Test
+ fun throwingIOExceptionIsSkipped() {
+ val one = DirectoryResourceLoader(testDir) to ResourcesProvider.empty()
+ val two = mockLoader {
+ doAnswer { throw IOException() }.`when`(it).loadAsset()
+ }
+
+ addLoader(one, two)
+
+ assertOpenedAsset()
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun throwingNonIOExceptionCausesFailure() {
+ val one = DirectoryResourceLoader(testDir) to ResourcesProvider.empty()
+ val two = mockLoader {
+ doAnswer { throw IllegalStateException() }.`when`(it).loadAsset()
+ }
+
+ addLoader(one, two)
+
+ assertOpenedAsset()
+ }
+
+ private fun assertOpenedAsset() {
+ assertThat(resources.assets.openAsset()).isEqualTo(TEST_TEXT)
+ }
+}
diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderChangesTest.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderChangesTest.kt
new file mode 100644
index 000000000000..e01e254b1f16
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderChangesTest.kt
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res.loader.test
+
+import android.app.Activity
+import android.app.Instrumentation
+import android.app.UiAutomation
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.graphics.Color
+import android.os.Bundle
+import android.os.ParcelFileDescriptor
+import android.widget.FrameLayout
+import androidx.test.InstrumentationRegistry
+import androidx.test.rule.ActivityTestRule
+import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry
+import androidx.test.runner.lifecycle.Stage
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import java.util.Arrays
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.Executor
+import java.util.concurrent.FutureTask
+import java.util.concurrent.TimeUnit
+
+// @Ignore("UiAutomation is crashing with not connected, not sure why")
+@RunWith(Parameterized::class)
+class ResourceLoaderChangesTest : ResourceLoaderTestBase() {
+
+ companion object {
+ private const val TIMEOUT = 30L
+ private const val OVERLAY_PACKAGE = "android.content.res.loader.test.overlay"
+
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun data() = arrayOf(DataType.APK, DataType.ARSC)
+ }
+
+ @field:Parameterized.Parameter(0)
+ override lateinit var dataType: DataType
+
+ @get:Rule
+ val activityRule: ActivityTestRule<TestActivity> =
+ ActivityTestRule<TestActivity>(TestActivity::class.java, false, true)
+
+ // Redirect to the Activity's resources
+ override val resources: Resources
+ get() = activityRule.getActivity().resources
+
+ private val activity: TestActivity
+ get() = activityRule.getActivity()
+
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+
+ @Before
+ @After
+ fun disableOverlay() {
+// enableOverlay(OVERLAY_PACKAGE, false)
+ }
+
+ @Test
+ fun activityRecreate() = verifySameBeforeAndAfter {
+ val oldActivity = activity
+ var newActivity: Activity? = null
+ instrumentation.runOnMainSync { oldActivity.recreate() }
+ instrumentation.waitForIdleSync()
+ instrumentation.runOnMainSync {
+ newActivity = ActivityLifecycleMonitorRegistry.getInstance()
+ .getActivitiesInStage(Stage.RESUMED)
+ .single()
+ }
+
+ assertThat(newActivity).isNotNull()
+ assertThat(newActivity).isNotSameAs(oldActivity)
+
+ // Return the new resources to assert on
+ return@verifySameBeforeAndAfter newActivity!!.resources
+ }
+
+ @Test
+ fun activityHandledOrientationChange() = verifySameBeforeAndAfter {
+ val latch = CountDownLatch(1)
+ val oldConfig = Configuration().apply { setTo(resources.configuration) }
+ var changedConfig: Configuration? = null
+
+ activity.callback = object : TestActivity.Callback {
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ changedConfig = newConfig
+ latch.countDown()
+ }
+ }
+
+ val isPortrait = resources.displayMetrics.run { widthPixels < heightPixels }
+ val newRotation = if (isPortrait) {
+ UiAutomation.ROTATION_FREEZE_90
+ } else {
+ UiAutomation.ROTATION_FREEZE_0
+ }
+
+ instrumentation.uiAutomation.setRotation(newRotation)
+
+ assertThat(latch.await(TIMEOUT, TimeUnit.SECONDS)).isTrue()
+ assertThat(changedConfig).isNotEqualTo(oldConfig)
+ return@verifySameBeforeAndAfter activity.resources
+ }
+
+ @Test
+ fun enableOverlayCausingPathChange() = verifySameBeforeAndAfter {
+ assertThat(getString(R.string.loader_path_change_test)).isEqualTo("Not overlaid")
+
+ enableOverlay(OVERLAY_PACKAGE, true)
+
+ assertThat(getString(R.string.loader_path_change_test)).isEqualTo("Overlaid")
+
+ return@verifySameBeforeAndAfter activity.resources
+ }
+
+ @Test
+ fun enableOverlayChildContextUnaffected() {
+ val childContext = activity.createConfigurationContext(Configuration())
+ val childResources = childContext.resources
+ val originalValue = childResources.getString(android.R.string.cancel)
+ assertThat(childResources.getString(R.string.loader_path_change_test))
+ .isEqualTo("Not overlaid")
+
+ verifySameBeforeAndAfter {
+ enableOverlay(OVERLAY_PACKAGE, true)
+ return@verifySameBeforeAndAfter activity.resources
+ }
+
+ // Loader not applied, but overlay change propagated
+ assertThat(childResources.getString(android.R.string.cancel)).isEqualTo(originalValue)
+ assertThat(childResources.getString(R.string.loader_path_change_test))
+ .isEqualTo("Overlaid")
+ }
+
+ // All these tests assert for the exact same loaders/values, so extract that logic out
+ private fun verifySameBeforeAndAfter(block: () -> Resources) {
+ // TODO(chiuwinson): atest doesn't work with @Ignore, UiAutomation not connected error
+ Assume.assumeFalse(true)
+
+ val originalValue = resources.getString(android.R.string.cancel)
+
+ val loader = "stringOne".openLoader()
+ addLoader(loader)
+
+ val oldLoaders = resources.loaders
+ val oldValue = resources.getString(android.R.string.cancel)
+
+ assertThat(oldValue).isNotEqualTo(originalValue)
+
+ val newResources = block()
+
+ val newLoaders = newResources.loaders
+ val newValue = newResources.getString(android.R.string.cancel)
+
+ assertThat(newValue).isEqualTo(oldValue)
+ assertThat(newLoaders).isEqualTo(oldLoaders)
+ }
+
+ // Copied from overlaytests LocalOverlayManager
+ private fun enableOverlay(packageName: String, enable: Boolean) {
+ val executor = Executor { Thread(it).start() }
+ val pattern = (if (enable) "[x]" else "[ ]") + " " + packageName
+ if (executeShellCommand("cmd overlay list").contains(pattern)) {
+ // nothing to do, overlay already in the requested state
+ return
+ }
+
+ val oldApkPaths = resources.assets.apkPaths
+ val task = FutureTask {
+ while (true) {
+ if (!Arrays.equals(oldApkPaths, resources.assets.apkPaths)) {
+ return@FutureTask true
+ }
+ Thread.sleep(10)
+ }
+
+ @Suppress("UNREACHABLE_CODE")
+ return@FutureTask false
+ }
+
+ val command = if (enable) "enable" else "disable"
+ executeShellCommand("cmd overlay $command $packageName")
+ executor.execute(task)
+ assertThat(task.get(TIMEOUT, TimeUnit.SECONDS)).isTrue()
+ }
+
+ private fun executeShellCommand(command: String): String {
+ val uiAutomation = instrumentation.uiAutomation
+ val pfd = uiAutomation.executeShellCommand(command)
+ return ParcelFileDescriptor.AutoCloseInputStream(pfd).use { it.reader().readText() }
+ }
+}
+
+class TestActivity : Activity() {
+
+ var callback: Callback? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(FrameLayout(this).apply {
+ setBackgroundColor(Color.BLUE)
+ })
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ callback?.onConfigurationChanged(newConfig)
+ }
+
+ interface Callback {
+ fun onConfigurationChanged(newConfig: Configuration)
+ }
+}
diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderDrawableTest.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderDrawableTest.kt
new file mode 100644
index 000000000000..09fd27e02b59
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderDrawableTest.kt
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.content.res.loader.test
+
+import android.content.res.Resources
+import android.content.res.loader.ResourceLoader
+import android.content.res.loader.ResourcesProvider
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import com.google.common.truth.Truth.assertThat
+import org.hamcrest.CoreMatchers.not
+import org.junit.Assume.assumeThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.any
+import org.mockito.Mockito.argThat
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+@RunWith(Parameterized::class)
+class ResourceLoaderDrawableTest : ResourceLoaderTestBase() {
+
+ companion object {
+
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun data() = arrayOf(DataType.APK, DataType.ARSC)
+ }
+
+ @field:Parameterized.Parameter(0)
+ override lateinit var dataType: DataType
+
+ @Test
+ fun matchingConfig() {
+ val original = getDrawable(android.R.drawable.ic_delete)
+ val loader = "drawableMdpiWithoutFile".openLoader()
+ `when`(loader.first.loadDrawable(any(), anyInt(), anyInt(), any()))
+ .thenReturn(ColorDrawable(Color.BLUE))
+
+ addLoader(loader)
+
+ updateConfiguration { densityDpi = 160 /* mdpi */ }
+
+ val drawable = getDrawable(android.R.drawable.ic_delete)
+
+ loader.verifyLoadDrawableCalled()
+
+ assertThat(drawable).isNotEqualTo(original)
+ assertThat(drawable).isInstanceOf(ColorDrawable::class.java)
+ assertThat((drawable as ColorDrawable).color).isEqualTo(Color.BLUE)
+ }
+
+ @Test
+ fun worseConfig() {
+ val loader = "drawableMdpiWithoutFile".openLoader()
+ addLoader(loader)
+
+ updateConfiguration { densityDpi = 480 /* xhdpi */ }
+
+ getDrawable(android.R.drawable.ic_delete)
+
+ verify(loader.first, never()).loadDrawable(any(), anyInt(), anyInt(), any())
+ }
+
+ @Test
+ fun multipleLoaders() {
+ val original = getDrawable(android.R.drawable.ic_delete)
+ val loaderOne = "drawableMdpiWithoutFile".openLoader()
+ val loaderTwo = "drawableMdpiWithoutFile".openLoader()
+
+ `when`(loaderTwo.first.loadDrawable(any(), anyInt(), anyInt(), any()))
+ .thenReturn(ColorDrawable(Color.BLUE))
+
+ addLoader(loaderOne, loaderTwo)
+
+ updateConfiguration { densityDpi = 160 /* mdpi */ }
+
+ val drawable = getDrawable(android.R.drawable.ic_delete)
+ loaderOne.verifyLoadDrawableNotCalled()
+ loaderTwo.verifyLoadDrawableCalled()
+
+ assertThat(drawable).isNotEqualTo(original)
+ assertThat(drawable).isInstanceOf(ColorDrawable::class.java)
+ assertThat((drawable as ColorDrawable).color).isEqualTo(Color.BLUE)
+ }
+
+ @Test(expected = Resources.NotFoundException::class)
+ fun multipleLoadersNoReturnWithoutFile() {
+ val loaderOne = "drawableMdpiWithoutFile".openLoader()
+ val loaderTwo = "drawableMdpiWithoutFile".openLoader()
+
+ addLoader(loaderOne, loaderTwo)
+
+ updateConfiguration { densityDpi = 160 /* mdpi */ }
+
+ try {
+ getDrawable(android.R.drawable.ic_delete)
+ } finally {
+ // We expect the call to fail because at least the loader won't resolve the overridden
+ // drawable, but we should still verify that both loaders were called before allowing
+ // the exception to propagate.
+ loaderOne.verifyLoadDrawableNotCalled()
+ loaderTwo.verifyLoadDrawableCalled()
+ }
+ }
+
+ @Test
+ fun multipleLoadersReturnWithFile() {
+ // Can't return a file if an ARSC
+ assumeThat(dataType, not(DataType.ARSC))
+
+ val original = getDrawable(android.R.drawable.ic_delete)
+ val loaderOne = "drawableMdpiWithFile".openLoader()
+ val loaderTwo = "drawableMdpiWithFile".openLoader()
+
+ addLoader(loaderOne, loaderTwo)
+
+ updateConfiguration { densityDpi = 160 /* mdpi */ }
+
+ val drawable = getDrawable(android.R.drawable.ic_delete)
+ loaderOne.verifyLoadDrawableNotCalled()
+ loaderTwo.verifyLoadDrawableCalled()
+
+ assertThat(drawable).isNotNull()
+ assertThat(drawable).isInstanceOf(original.javaClass)
+ }
+
+ @Test
+ fun unhandledResourceIgnoresLoaders() {
+ val loader = "drawableMdpiWithoutFile".openLoader()
+ `when`(loader.first.loadDrawable(any(), anyInt(), anyInt(), any()))
+ .thenReturn(ColorDrawable(Color.BLUE))
+ addLoader(loader)
+
+ getDrawable(android.R.drawable.ic_menu_add)
+
+ loader.verifyLoadDrawableNotCalled()
+
+ getDrawable(android.R.drawable.ic_delete)
+
+ loader.verifyLoadDrawableCalled()
+ }
+
+ private fun Pair<ResourceLoader, ResourcesProvider>.verifyLoadDrawableCalled() {
+ verify(first).loadDrawable(
+ argThat {
+ it.density == 160 &&
+ it.resourceId == android.R.drawable.ic_delete &&
+ it.string == "res/drawable-mdpi-v4/ic_delete.png"
+ },
+ eq(android.R.drawable.ic_delete),
+ eq(0),
+ any()
+ )
+ }
+
+ private fun Pair<ResourceLoader, ResourcesProvider>.verifyLoadDrawableNotCalled() {
+ verify(first, never()).loadDrawable(
+ any(),
+ anyInt(),
+ anyInt(),
+ any()
+ )
+ }
+}
diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderLayoutTest.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderLayoutTest.kt
new file mode 100644
index 000000000000..1ec209486c18
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderLayoutTest.kt
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.content.res.loader.test
+
+import android.content.res.Resources
+import android.content.res.XmlResourceParser
+import android.content.res.loader.ResourceLoader
+import android.content.res.loader.ResourcesProvider
+import com.google.common.truth.Truth.assertThat
+import org.hamcrest.CoreMatchers.not
+import org.junit.Assume.assumeThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+@RunWith(Parameterized::class)
+class ResourceLoaderLayoutTest : ResourceLoaderTestBase() {
+
+ companion object {
+
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun data() = arrayOf(DataType.APK, DataType.ARSC)
+ }
+
+ @field:Parameterized.Parameter(0)
+ override lateinit var dataType: DataType
+
+ @Test
+ fun singleLoader() {
+ val original = getLayout(android.R.layout.activity_list_item)
+ val mockXml = mock(XmlResourceParser::class.java)
+ val loader = "layoutWithoutFile".openLoader()
+ `when`(loader.first.loadXmlResourceParser(any(), anyInt()))
+ .thenReturn(mockXml)
+
+ addLoader(loader)
+
+ val layout = getLayout(android.R.layout.activity_list_item)
+ loader.verifyLoadLayoutCalled()
+
+ assertThat(layout).isNotEqualTo(original)
+ assertThat(layout).isSameAs(mockXml)
+ }
+
+ @Test
+ fun multipleLoaders() {
+ val original = getLayout(android.R.layout.activity_list_item)
+ val loaderOne = "layoutWithoutFile".openLoader()
+ val loaderTwo = "layoutWithoutFile".openLoader()
+
+ val mockXml = mock(XmlResourceParser::class.java)
+ `when`(loaderTwo.first.loadXmlResourceParser(any(), anyInt()))
+ .thenReturn(mockXml)
+
+ addLoader(loaderOne, loaderTwo)
+
+ val layout = getLayout(android.R.layout.activity_list_item)
+ loaderOne.verifyLoadLayoutNotCalled()
+ loaderTwo.verifyLoadLayoutCalled()
+
+ assertThat(layout).isNotEqualTo(original)
+ assertThat(layout).isSameAs(mockXml)
+ }
+
+ @Test(expected = Resources.NotFoundException::class)
+ fun multipleLoadersNoReturnWithoutFile() {
+ val loaderOne = "layoutWithoutFile".openLoader()
+ val loaderTwo = "layoutWithoutFile".openLoader()
+
+ addLoader(loaderOne, loaderTwo)
+
+ try {
+ getLayout(android.R.layout.activity_list_item)
+ } finally {
+ // We expect the call to fail because at least one loader must resolve the overridden
+ // layout, but we should still verify that both loaders were called before allowing
+ // the exception to propagate.
+ loaderOne.verifyLoadLayoutNotCalled()
+ loaderTwo.verifyLoadLayoutCalled()
+ }
+ }
+
+ @Test
+ fun multipleLoadersReturnWithFile() {
+ // Can't return a file if an ARSC
+ assumeThat(dataType, not(DataType.ARSC))
+
+ val loaderOne = "layoutWithFile".openLoader()
+ val loaderTwo = "layoutWithFile".openLoader()
+
+ addLoader(loaderOne, loaderTwo)
+
+ val xml = getLayout(android.R.layout.activity_list_item)
+ loaderOne.verifyLoadLayoutNotCalled()
+ loaderTwo.verifyLoadLayoutCalled()
+
+ assertThat(xml).isNotNull()
+ }
+
+ @Test
+ fun unhandledResourceIgnoresLoaders() {
+ val loader = "layoutWithoutFile".openLoader()
+ val mockXml = mock(XmlResourceParser::class.java)
+ `when`(loader.first.loadXmlResourceParser(any(), anyInt()))
+ .thenReturn(mockXml)
+ addLoader(loader)
+
+ getLayout(android.R.layout.preference_category)
+
+ verify(loader.first, never())
+ .loadXmlResourceParser(anyString(), anyInt())
+
+ getLayout(android.R.layout.activity_list_item)
+
+ loader.verifyLoadLayoutCalled()
+ }
+
+ private fun Pair<ResourceLoader, ResourcesProvider>.verifyLoadLayoutCalled() {
+ verify(first).loadXmlResourceParser(
+ "res/layout/activity_list_item.xml",
+ android.R.layout.activity_list_item
+ )
+ }
+
+ private fun Pair<ResourceLoader, ResourcesProvider>.verifyLoadLayoutNotCalled() {
+ verify(first, never()).loadXmlResourceParser(
+ anyString(),
+ anyInt()
+ )
+ }
+}
diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderTestBase.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderTestBase.kt
new file mode 100644
index 000000000000..5af453d526e4
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderTestBase.kt
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.content.res.loader.test
+
+import android.content.Context
+import android.content.res.AssetManager
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.content.res.loader.ResourceLoader
+import android.content.res.loader.ResourcesProvider
+import android.os.ParcelFileDescriptor
+import android.util.TypedValue
+import androidx.annotation.DimenRes
+import androidx.annotation.DrawableRes
+import androidx.annotation.LayoutRes
+import androidx.annotation.StringRes
+import androidx.test.InstrumentationRegistry
+import org.junit.After
+import org.junit.Before
+import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.argThat
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import java.io.Closeable
+
+abstract class ResourceLoaderTestBase {
+
+ open lateinit var dataType: DataType
+
+ protected lateinit var context: Context
+ protected open val resources: Resources
+ get() = context.resources
+ protected open val assets: AssetManager
+ get() = resources.assets
+
+ // Track opened streams and ResourcesProviders to close them after testing
+ private val openedObjects = mutableListOf<Closeable>()
+
+ @Before
+ fun setUpBase() {
+ context = InstrumentationRegistry.getTargetContext()
+ }
+
+ @After
+ fun removeAllLoaders() {
+ resources.setLoaders(null)
+ openedObjects.forEach {
+ try {
+ it.close()
+ } catch (ignored: Exception) {
+ }
+ }
+ }
+
+ protected fun getString(@StringRes stringRes: Int, debugLog: Boolean = false) =
+ logResolution(debugLog) { getString(stringRes) }
+
+ protected fun getDrawable(@DrawableRes drawableRes: Int, debugLog: Boolean = false) =
+ logResolution(debugLog) { getDrawable(drawableRes) }
+
+ protected fun getLayout(@LayoutRes layoutRes: Int, debugLog: Boolean = false) =
+ logResolution(debugLog) { getLayout(layoutRes) }
+
+ protected fun getDimensionPixelSize(@DimenRes dimenRes: Int, debugLog: Boolean = false) =
+ logResolution(debugLog) { getDimensionPixelSize(dimenRes) }
+
+ private fun <T> logResolution(debugLog: Boolean = false, block: Resources.() -> T): T {
+ if (debugLog) {
+ resources.assets.setResourceResolutionLoggingEnabled(true)
+ }
+
+ var thrown = false
+
+ try {
+ return resources.block()
+ } catch (t: Throwable) {
+ // No good way to log to test output other than throwing an exception
+ if (debugLog) {
+ thrown = true
+ throw IllegalStateException(resources.assets.lastResourceResolution, t)
+ } else {
+ throw t
+ }
+ } finally {
+ if (!thrown && debugLog) {
+ throw IllegalStateException(resources.assets.lastResourceResolution)
+ }
+ }
+ }
+
+ protected fun updateConfiguration(block: Configuration.() -> Unit) {
+ val configuration = Configuration().apply {
+ setTo(resources.configuration)
+ block()
+ }
+
+ resources.updateConfiguration(configuration, resources.displayMetrics)
+ }
+
+ protected fun String.openLoader(
+ dataType: DataType = this@ResourceLoaderTestBase.dataType
+ ): Pair<ResourceLoader, ResourcesProvider> = when (dataType) {
+ DataType.APK -> {
+ mock(ResourceLoader::class.java) to context.copiedRawFile("${this}Apk").use {
+ ResourcesProvider.loadFromApk(it)
+ }.also { openedObjects += it }
+ }
+ DataType.ARSC -> {
+ mock(ResourceLoader::class.java) to openArsc(this)
+ }
+ DataType.SPLIT -> {
+ mock(ResourceLoader::class.java) to ResourcesProvider.loadFromSplit(context, this)
+ }
+ DataType.ASSET -> mockLoader {
+ doAnswer { byteInputStream() }.`when`(it)
+ .loadAsset(eq("assets/Asset.txt"), anyInt())
+ }
+ DataType.ASSET_FD -> mockLoader {
+ doAnswer {
+ val file = context.filesDir.resolve("Asset.txt")
+ file.writeText(this)
+ ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)
+ }.`when`(it).loadAssetFd("assets/Asset.txt")
+ }
+ DataType.NON_ASSET -> mockLoader {
+ doAnswer {
+ val file = context.filesDir.resolve("NonAsset.txt")
+ file.writeText(this)
+ ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)
+ }.`when`(it).loadAssetFd("NonAsset.txt")
+ }
+ DataType.NON_ASSET_DRAWABLE -> mockLoader(openArsc(this)) {
+ doReturn(null).`when`(it).loadDrawable(argThat { value ->
+ value.type == TypedValue.TYPE_STRING &&
+ value.resourceId == 0x7f010001 &&
+ value.string == "res/drawable-nodpi-v4/non_asset_drawable.xml"
+ }, eq(0x7f010001), anyInt(), ArgumentMatchers.any())
+
+ doAnswer { context.copiedRawFile(this) }.`when`(it)
+ .loadAssetFd("res/drawable-nodpi-v4/non_asset_drawable.xml")
+ }
+ DataType.NON_ASSET_BITMAP -> mockLoader(openArsc(this)) {
+ doReturn(null).`when`(it).loadDrawable(argThat { value ->
+ value.type == TypedValue.TYPE_STRING &&
+ value.resourceId == 0x7f010000 &&
+ value.string == "res/drawable-nodpi-v4/non_asset_bitmap.png"
+ }, eq(0x7f010000), anyInt(), ArgumentMatchers.any())
+
+ doAnswer { resources.openRawResourceFd(rawFile(this)).createInputStream() }
+ .`when`(it)
+ .loadAsset(eq("res/drawable-nodpi-v4/non_asset_bitmap.png"), anyInt())
+ }
+ DataType.NON_ASSET_LAYOUT -> mockLoader(openArsc(this)) {
+ doReturn(null).`when`(it)
+ .loadXmlResourceParser("res/layout/layout.xml", 0x7f020000)
+
+ doAnswer { context.copiedRawFile(this) }.`when`(it)
+ .loadAssetFd("res/layout/layout.xml")
+ }
+ }
+
+ protected fun mockLoader(
+ provider: ResourcesProvider = ResourcesProvider.empty(),
+ block: (ResourceLoader) -> Unit = {}
+ ): Pair<ResourceLoader, ResourcesProvider> {
+ return mock(ResourceLoader::class.java, Utils.ANSWER_THROWS)
+ .apply(block) to provider
+ }
+
+ protected fun openArsc(rawName: String): ResourcesProvider {
+ return context.copiedRawFile("${rawName}Arsc")
+ .use { ResourcesProvider.loadFromArsc(it) }
+ .also { openedObjects += it }
+ }
+
+ // This specifically uses addLoader so both behaviors are tested
+ protected fun addLoader(vararg pairs: Pair<out ResourceLoader, ResourcesProvider>) {
+ pairs.forEach { resources.addLoader(it.first, it.second) }
+ }
+
+ protected fun setLoaders(vararg pairs: Pair<out ResourceLoader, ResourcesProvider>) {
+ resources.setLoaders(pairs.map { android.util.Pair(it.first, it.second) })
+ }
+
+ protected fun addLoader(pair: Pair<out ResourceLoader, ResourcesProvider>, index: Int) {
+ resources.addLoader(pair.first, pair.second, index)
+ }
+
+ protected fun removeLoader(vararg pairs: Pair<out ResourceLoader, ResourcesProvider>) {
+ pairs.forEach { resources.removeLoader(it.first) }
+ }
+
+ protected fun getLoaders(): MutableList<Pair<ResourceLoader, ResourcesProvider>> {
+ // Cast instead of toMutableList to maintain the same object
+ return resources.getLoaders() as MutableList<Pair<ResourceLoader, ResourcesProvider>>
+ }
+
+ enum class DataType {
+ APK,
+ ARSC,
+ SPLIT,
+ ASSET,
+ ASSET_FD,
+ NON_ASSET,
+ NON_ASSET_DRAWABLE,
+ NON_ASSET_BITMAP,
+ NON_ASSET_LAYOUT,
+ }
+}
diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderValuesTest.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderValuesTest.kt
new file mode 100644
index 000000000000..017552a02152
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderValuesTest.kt
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.content.res.loader.test
+
+import android.graphics.Color
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.ColorDrawable
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Tests generic ResourceLoader behavior. Intentionally abstract in its test methodology because
+ * the behavior being verified isn't specific to any resource type. As long as it can pass an
+ * equals check.
+ *
+ * Currently tests strings and dimens since String and any Number seemed most relevant to verify.
+ */
+@RunWith(Parameterized::class)
+class ResourceLoaderValuesTest : ResourceLoaderTestBase() {
+
+ companion object {
+ @Parameterized.Parameters(name = "{1} {0}")
+ @JvmStatic
+ fun parameters(): Array<Any> {
+ val parameters = mutableListOf<Parameter>()
+
+ // R.string
+ parameters += Parameter(
+ { getString(android.R.string.cancel) },
+ "stringOne", { "SomeRidiculouslyUnlikelyStringOne" },
+ "stringTwo", { "SomeRidiculouslyUnlikelyStringTwo" },
+ listOf(DataType.APK, DataType.ARSC)
+ )
+
+ // R.dimen
+ parameters += Parameter(
+ { resources.getDimensionPixelSize(android.R.dimen.app_icon_size) },
+ "dimenOne", { 564716.dpToPx(resources) },
+ "dimenTwo", { 565717.dpToPx(resources) },
+ listOf(DataType.APK, DataType.ARSC)
+ )
+
+ // File in the assets directory
+ parameters += Parameter(
+ { assets.open("Asset.txt").reader().readText() },
+ "assetOne", { "assetOne" },
+ "assetTwo", { "assetTwo" },
+ listOf(DataType.ASSET)
+ )
+
+ // From assets directory returning file descriptor
+ parameters += Parameter(
+ { assets.openFd("Asset.txt").readText() },
+ "assetOne", { "assetOne" },
+ "assetTwo", { "assetTwo" },
+ listOf(DataType.ASSET_FD)
+ )
+
+ // From root directory returning file descriptor
+ parameters += Parameter(
+ { assets.openNonAssetFd("NonAsset.txt").readText() },
+ "NonAssetOne", { "NonAssetOne" },
+ "NonAssetTwo", { "NonAssetTwo" },
+ listOf(DataType.NON_ASSET)
+ )
+
+ // Asset as compiled XML drawable
+ parameters += Parameter(
+ { (getDrawable(R.drawable.non_asset_drawable) as ColorDrawable).color },
+ "nonAssetDrawableOne", { Color.parseColor("#A3C3E3") },
+ "nonAssetDrawableTwo", { Color.parseColor("#3A3C3E") },
+ listOf(DataType.NON_ASSET_DRAWABLE)
+ )
+
+ // Asset as compiled bitmap drawable
+ parameters += Parameter(
+ {
+ (getDrawable(R.drawable.non_asset_bitmap) as BitmapDrawable)
+ .bitmap.getColor(0, 0).toArgb()
+ },
+ "nonAssetBitmapGreen", { Color.GREEN },
+ "nonAssetBitmapBlue", { Color.BLUE },
+ listOf(DataType.NON_ASSET_BITMAP)
+ )
+
+ // Asset as compiled XML layout
+ parameters += Parameter(
+ { getLayout(R.layout.layout).advanceToRoot().name },
+ "layoutOne", { "RelativeLayout" },
+ "layoutTwo", { "LinearLayout" },
+ listOf(DataType.NON_ASSET_LAYOUT)
+ )
+
+ // Isolated resource split
+ parameters += Parameter(
+ { getString(R.string.split_overlaid) },
+ "split_one", { "Split ONE Overlaid" },
+ "split_two", { "Split TWO Overlaid" },
+ listOf(DataType.SPLIT)
+ )
+
+ return parameters.flatMap { parameter ->
+ parameter.dataTypes.map { dataType ->
+ arrayOf(dataType, parameter)
+ }
+ }.toTypedArray()
+ }
+ }
+
+ @Suppress("LateinitVarOverridesLateinitVar")
+ @field:Parameterized.Parameter(0)
+ override lateinit var dataType: DataType
+
+ @field:Parameterized.Parameter(1)
+ lateinit var parameter: Parameter
+
+ private val valueOne by lazy { parameter.valueOne(this) }
+ private val valueTwo by lazy { parameter.valueTwo(this) }
+
+ private fun openOne() = parameter.loaderOne.openLoader()
+ private fun openTwo() = parameter.loaderTwo.openLoader()
+
+ // Class method for syntax highlighting purposes
+ private fun getValue() = parameter.getValue(this)
+
+ @Test
+ fun verifyValueUniqueness() {
+ // Ensure the parameters are valid in case of coding errors
+ assertNotEquals(valueOne, getValue())
+ assertNotEquals(valueTwo, getValue())
+ assertNotEquals(valueOne, valueTwo)
+ }
+
+ @Test
+ fun addMultipleLoaders() {
+ val originalValue = getValue()
+ val testOne = openOne()
+ val testTwo = openTwo()
+
+ addLoader(testOne, testTwo)
+
+ assertEquals(valueTwo, getValue())
+
+ removeLoader(testTwo)
+
+ assertEquals(valueOne, getValue())
+
+ removeLoader(testOne)
+
+ assertEquals(originalValue, getValue())
+ }
+
+ @Test
+ fun setMultipleLoaders() {
+ val originalValue = getValue()
+ val testOne = openOne()
+ val testTwo = openTwo()
+
+ setLoaders(testOne, testTwo)
+
+ assertEquals(valueTwo, getValue())
+
+ removeLoader(testTwo)
+
+ assertEquals(valueOne, getValue())
+
+ setLoaders()
+
+ assertEquals(originalValue, getValue())
+ }
+
+ @Test
+ fun getLoadersContainsAll() {
+ val testOne = openOne()
+ val testTwo = openTwo()
+
+ addLoader(testOne, testTwo)
+
+ assertThat(getLoaders()).containsAllOf(testOne, testTwo)
+ }
+
+ @Test
+ fun getLoadersDoesNotLeakMutability() {
+ val originalValue = getValue()
+ val testOne = openOne()
+ val testTwo = openTwo()
+
+ addLoader(testOne)
+
+ assertEquals(valueOne, getValue())
+
+ val loaders = getLoaders()
+ loaders += testTwo
+
+ assertEquals(valueOne, getValue())
+
+ removeLoader(testOne)
+
+ assertEquals(originalValue, getValue())
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun alreadyAddedThrows() {
+ val testOne = openOne()
+ val testTwo = openTwo()
+
+ addLoader(testOne)
+ addLoader(testTwo)
+ addLoader(testOne)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun alreadyAddedAndSetThrows() {
+ val testOne = openOne()
+ val testTwo = openTwo()
+
+ addLoader(testOne)
+ addLoader(testTwo)
+ setLoaders(testTwo)
+ }
+
+ @Test
+ fun repeatedRemoveSucceeds() {
+ val originalValue = getValue()
+ val testOne = openOne()
+
+ addLoader(testOne)
+
+ assertNotEquals(originalValue, getValue())
+
+ removeLoader(testOne)
+
+ assertEquals(originalValue, getValue())
+
+ removeLoader(testOne)
+
+ assertEquals(originalValue, getValue())
+ }
+
+ @Test
+ fun addToFront() {
+ val testOne = openOne()
+ val testTwo = openTwo()
+
+ addLoader(testOne)
+
+ assertEquals(valueOne, getValue())
+
+ addLoader(testTwo, 0)
+
+ assertEquals(valueOne, getValue())
+
+ // Remove top loader, so previously added to front should now resolve
+ removeLoader(testOne)
+ assertEquals(valueTwo, getValue())
+ }
+
+ @Test
+ fun addToEnd() {
+ val testOne = openOne()
+ val testTwo = openTwo()
+
+ addLoader(testOne)
+
+ assertEquals(valueOne, getValue())
+
+ addLoader(testTwo, 1)
+
+ assertEquals(valueTwo, getValue())
+ }
+
+ @Test(expected = IndexOutOfBoundsException::class)
+ fun addPastEnd() {
+ val testOne = openOne()
+ val testTwo = openTwo()
+
+ addLoader(testOne)
+
+ assertEquals(valueOne, getValue())
+
+ addLoader(testTwo, 2)
+ }
+
+ @Test(expected = IndexOutOfBoundsException::class)
+ fun addBeforeFront() {
+ val testOne = openOne()
+ val testTwo = openTwo()
+
+ addLoader(testOne)
+
+ assertEquals(valueOne, getValue())
+
+ addLoader(testTwo, -1)
+ }
+
+ @Test
+ fun reorder() {
+ val originalValue = getValue()
+ val testOne = openOne()
+ val testTwo = openTwo()
+
+ addLoader(testOne, testTwo)
+
+ assertEquals(valueTwo, getValue())
+
+ removeLoader(testOne)
+
+ assertEquals(valueTwo, getValue())
+
+ addLoader(testOne)
+
+ assertEquals(valueOne, getValue())
+
+ removeLoader(testTwo)
+
+ assertEquals(valueOne, getValue())
+
+ removeLoader(testOne)
+
+ assertEquals(originalValue, getValue())
+ }
+
+ data class Parameter(
+ val getValue: ResourceLoaderValuesTest.() -> Any,
+ val loaderOne: String,
+ val valueOne: ResourceLoaderValuesTest.() -> Any,
+ val loaderTwo: String,
+ val valueTwo: ResourceLoaderValuesTest.() -> Any,
+ val dataTypes: List<DataType>
+ ) {
+ override fun toString(): String {
+ val prefix = loaderOne.commonPrefixWith(loaderTwo)
+ return "$prefix${loaderOne.removePrefix(prefix)}|${loaderTwo.removePrefix(prefix)}"
+ }
+ }
+}
diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/Utils.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/Utils.kt
new file mode 100644
index 000000000000..df2d09adf503
--- /dev/null
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/Utils.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res.loader.test
+
+import android.content.Context
+import android.content.res.AssetFileDescriptor
+import android.content.res.Resources
+import android.os.ParcelFileDescriptor
+import android.util.TypedValue
+import org.mockito.Answers
+import org.mockito.stubbing.Answer
+import org.xmlpull.v1.XmlPullParser
+import java.io.File
+
+// Enforce use of [android.util.Pair] instead of Kotlin's so it matches the ResourceLoader APIs
+typealias Pair<F, S> = android.util.Pair<F, S>
+infix fun <A, B> A.to(that: B): Pair<A, B> = Pair.create(this, that)!!
+
+object Utils {
+ val ANSWER_THROWS = Answer<Any> {
+ when (val name = it.method.name) {
+ "toString" -> return@Answer Answers.CALLS_REAL_METHODS.answer(it)
+ else -> throw UnsupportedOperationException("$name with " +
+ "${it.arguments?.joinToString()} should not be called")
+ }
+ }
+}
+
+fun Int.dpToPx(resources: Resources) = TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ this.toFloat(),
+ resources.displayMetrics
+).toInt()
+
+fun AssetFileDescriptor.readText() = createInputStream().reader().readText()
+
+fun rawFile(fileName: String) = R.raw::class.java.getDeclaredField(fileName).getInt(null)
+
+fun XmlPullParser.advanceToRoot() = apply {
+ while (next() != XmlPullParser.START_TAG) {
+ // Empty
+ }
+}
+
+fun Context.copiedRawFile(fileName: String): ParcelFileDescriptor {
+ return resources.openRawResourceFd(rawFile(fileName)).use { asset ->
+ // AssetManager doesn't expose a direct file descriptor to the asset, so copy it to
+ // an individual file so one can be created manually.
+ val copiedFile = File(filesDir, fileName)
+ asset.createInputStream().use { input ->
+ copiedFile.outputStream().use { output ->
+ input.copyTo(output)
+ }
+ }
+
+ ParcelFileDescriptor.open(copiedFile, ParcelFileDescriptor.MODE_READ_WRITE)
+ }
+}
diff --git a/core/tests/bugreports/Android.bp b/core/tests/bugreports/Android.bp
index d3bf0dd7a7e8..e9d5bb135e02 100644
--- a/core/tests/bugreports/Android.bp
+++ b/core/tests/bugreports/Android.bp
@@ -20,7 +20,6 @@ android_test {
"android.test.base",
],
static_libs: ["androidx.test.rules", "truth-prebuilt"],
- test_suites: ["general-tests"],
sdk_version: "test_current",
platform_apis: true,
}
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 24172098491d..c7e54f3fcd1b 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1155,7 +1155,10 @@
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ <category android:name="android.intent.category.ACCESSIBILITY_SHORTCUT_TARGET" />
</intent-filter>
+ <meta-data android:name="android.accessibilityshortcut.target"
+ android:resource="@xml/accessibility_shortcut_test_activity"/>
</activity>
<!-- Activity-level metadata -->
diff --git a/core/tests/coretests/res/values/strings.xml b/core/tests/coretests/res/values/strings.xml
index ef915bba1013..f630188e54dc 100644
--- a/core/tests/coretests/res/values/strings.xml
+++ b/core/tests/coretests/res/values/strings.xml
@@ -142,4 +142,10 @@
<!-- ResourcesLocaleResolutionTest -->
<string name="dummy_string">dummy string</string>
+
+ <!-- Description of the accessibility shortcut [CHAR LIMIT=NONE] -->
+ <string name="accessibility_shortcut_description">Accessibility shortcut description</string>
+
+ <!-- Summary of the accessibility shortcut [CHAR LIMIT=NONE] -->
+ <string name="accessibility_shortcut_summary">Accessibility shortcut summary</string>
</resources>
diff --git a/core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml b/core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml
new file mode 100644
index 000000000000..60e29989ef0d
--- /dev/null
+++ b/core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml
@@ -0,0 +1,22 @@
+<?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.
+ -->
+
+<accessibility-shortcut-target xmlns:android="http://schemas.android.com/apk/res/android"
+ android:description="@string/accessibility_shortcut_description"
+ android:summary="@string/accessibility_shortcut_summary"
+/> \ No newline at end of file
diff --git a/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java b/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java
new file mode 100644
index 000000000000..abaeb0a11a8d
--- /dev/null
+++ b/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accessibilityservice;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityTestActivity;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+/**
+ * AccessibilityShortcutInfo can only be created by system. Verify the instance creation and
+ * basic function here.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityShortcutInfoTest {
+ private Context mTargetContext;
+ private PackageManager mPackageManager;
+ private AccessibilityShortcutInfo mShortcutInfo;
+
+ @Before
+ public void setUp() {
+ mTargetContext = InstrumentationRegistry.getInstrumentation()
+ .getTargetContext();
+ mPackageManager = mTargetContext.getPackageManager();
+
+ final ComponentName testShortcutName = new ComponentName(mTargetContext,
+ AccessibilityTestActivity.class);
+ final AccessibilityManager accessibilityManager = (AccessibilityManager) mTargetContext
+ .getSystemService(Context.ACCESSIBILITY_SERVICE);
+ final List<AccessibilityShortcutInfo> infoList = accessibilityManager
+ .getInstalledAccessibilityShortcutListAsUser(
+ mTargetContext, mTargetContext.getUserId());
+ for (AccessibilityShortcutInfo info : infoList) {
+ final ActivityInfo activityInfo = info.getActivityInfo();
+ final ComponentName name = new ComponentName(
+ activityInfo.packageName, activityInfo.name);
+ if (name.equals(testShortcutName)) {
+ mShortcutInfo = info;
+ break;
+ }
+ }
+
+ assertNotNull("Can't find " + testShortcutName, mShortcutInfo);
+ }
+
+ @Test
+ public void testDescription() {
+ final String description = mTargetContext.getResources()
+ .getString(R.string.accessibility_shortcut_description);
+
+ assertNotNull("Can't find description string", description);
+ assertThat("Description is not correct",
+ mShortcutInfo.loadDescription(mPackageManager), is(description));
+ }
+
+ @Test
+ public void testSummary() {
+ final String summary = mTargetContext.getResources()
+ .getString(R.string.accessibility_shortcut_summary);
+
+ assertNotNull("Can't find summary string", summary);
+ assertThat("Summary is not correct",
+ mShortcutInfo.loadSummary(mPackageManager), is(summary));
+ }
+}
diff --git a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
index 4ae9494aa362..fb0dd46a52f0 100644
--- a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
+++ b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
@@ -20,52 +20,44 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
import static android.app.admin.PasswordMetrics.complexityLevelToMinQuality;
-import static android.app.admin.PasswordMetrics.getActualRequiredQuality;
-import static android.app.admin.PasswordMetrics.getMinimumMetrics;
-import static android.app.admin.PasswordMetrics.getTargetQualityMetrics;
import static android.app.admin.PasswordMetrics.sanitizeComplexityLevel;
+import static android.app.admin.PasswordMetrics.validatePasswordMetrics;
+
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.widget.PasswordValidationError;
+
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
/** Unit tests for {@link PasswordMetrics}. */
@RunWith(AndroidJUnit4.class)
@SmallTest
+@Presubmit
public class PasswordMetricsTest {
-
- @Test
- public void testIsDefault() {
- final PasswordMetrics metrics = new PasswordMetrics();
- assertEquals(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, metrics.quality);
- assertEquals(0, metrics.length);
- assertEquals(0, metrics.letters);
- assertEquals(0, metrics.upperCase);
- assertEquals(0, metrics.lowerCase);
- assertEquals(0, metrics.numeric);
- assertEquals(0, metrics.symbols);
- assertEquals(0, metrics.nonLetter);
- }
-
@Test
public void testParceling() {
- final int quality = 0;
+ final int credType = CREDENTIAL_TYPE_PASSWORD;
final int length = 1;
final int letters = 2;
final int upperCase = 3;
@@ -73,20 +65,21 @@ public class PasswordMetricsTest {
final int numeric = 5;
final int symbols = 6;
final int nonLetter = 7;
+ final int nonNumeric = 8;
+ final int seqLength = 9;
final Parcel parcel = Parcel.obtain();
- final PasswordMetrics metrics;
+ PasswordMetrics metrics = new PasswordMetrics(credType, length, letters, upperCase,
+ lowerCase, numeric, symbols, nonLetter, nonNumeric, seqLength);
try {
- new PasswordMetrics(
- quality, length, letters, upperCase, lowerCase, numeric, symbols, nonLetter)
- .writeToParcel(parcel, 0);
+ metrics.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
metrics = PasswordMetrics.CREATOR.createFromParcel(parcel);
} finally {
parcel.recycle();
}
- assertEquals(quality, metrics.quality);
+ assertEquals(credType, metrics.credType);
assertEquals(length, metrics.length);
assertEquals(letters, metrics.letters);
assertEquals(upperCase, metrics.upperCase);
@@ -94,7 +87,8 @@ public class PasswordMetricsTest {
assertEquals(numeric, metrics.numeric);
assertEquals(symbols, metrics.symbols);
assertEquals(nonLetter, metrics.nonLetter);
-
+ assertEquals(nonNumeric, metrics.nonNumeric);
+ assertEquals(seqLength, metrics.seqLength);
}
@Test
@@ -111,23 +105,6 @@ public class PasswordMetricsTest {
}
@Test
- public void testComputeForPassword_quality() {
- assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC,
- PasswordMetrics.computeForPassword("a1".getBytes()).quality);
- assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC,
- PasswordMetrics.computeForPassword("a".getBytes()).quality);
- assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC,
- PasswordMetrics.computeForPassword("*~&%$".getBytes()).quality);
- assertEquals(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX,
- PasswordMetrics.computeForPassword("1".getBytes()).quality);
- // contains a long sequence so isn't complex
- assertEquals(PASSWORD_QUALITY_NUMERIC,
- PasswordMetrics.computeForPassword("1234".getBytes()).quality);
- assertEquals(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
- PasswordMetrics.computeForPassword("".getBytes()).quality);
- }
-
- @Test
public void testMaxLengthSequence() {
assertEquals(4, PasswordMetrics.maxLengthSequence("1234".getBytes()));
assertEquals(5, PasswordMetrics.maxLengthSequence("13579".getBytes()));
@@ -142,69 +119,15 @@ public class PasswordMetricsTest {
}
@Test
- public void testEquals() {
- PasswordMetrics metrics0 = new PasswordMetrics();
- PasswordMetrics metrics1 = new PasswordMetrics();
- assertNotEquals(metrics0, null);
- assertNotEquals(metrics0, new Object());
- assertEquals(metrics0, metrics0);
- assertEquals(metrics0, metrics1);
-
- assertEquals(new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, 4),
- new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, 4));
-
- assertNotEquals(new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, 4),
- new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, 5));
-
- assertNotEquals(new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, 4),
- new PasswordMetrics(PASSWORD_QUALITY_COMPLEX, 4));
-
- metrics0 = PasswordMetrics.computeForPassword("1234abcd,./".getBytes());
- metrics1 = PasswordMetrics.computeForPassword("1234abcd,./".getBytes());
- assertEquals(metrics0, metrics1);
- metrics1.letters++;
- assertNotEquals(metrics0, metrics1);
- metrics1.letters--;
- metrics1.upperCase++;
- assertNotEquals(metrics0, metrics1);
- metrics1.upperCase--;
- metrics1.lowerCase++;
- assertNotEquals(metrics0, metrics1);
- metrics1.lowerCase--;
- metrics1.numeric++;
- assertNotEquals(metrics0, metrics1);
- metrics1.numeric--;
- metrics1.symbols++;
- assertNotEquals(metrics0, metrics1);
- metrics1.symbols--;
- metrics1.nonLetter++;
- assertNotEquals(metrics0, metrics1);
- metrics1.nonLetter--;
- assertEquals(metrics0, metrics1);
-
-
- }
-
- @Test
- public void testConstructQuality() {
- PasswordMetrics expected = new PasswordMetrics();
- expected.quality = PASSWORD_QUALITY_COMPLEX;
-
- PasswordMetrics actual = new PasswordMetrics(PASSWORD_QUALITY_COMPLEX);
-
- assertEquals(expected, actual);
- }
-
- @Test
public void testDetermineComplexity_none() {
assertEquals(PASSWORD_COMPLEXITY_NONE,
- PasswordMetrics.computeForPassword("".getBytes()).determineComplexity());
+ new PasswordMetrics(CREDENTIAL_TYPE_NONE).determineComplexity());
}
@Test
public void testDetermineComplexity_lowSomething() {
assertEquals(PASSWORD_COMPLEXITY_LOW,
- new PasswordMetrics(PASSWORD_QUALITY_SOMETHING).determineComplexity());
+ new PasswordMetrics(CREDENTIAL_TYPE_PATTERN).determineComplexity());
}
@Test
@@ -324,122 +247,84 @@ public class PasswordMetricsTest {
}
@Test
- public void testGetTargetQualityMetrics_noneComplexityReturnsDefaultMetrics() {
- PasswordMetrics metrics =
- getTargetQualityMetrics(PASSWORD_COMPLEXITY_NONE, PASSWORD_QUALITY_ALPHANUMERIC);
-
- assertTrue(metrics.isDefault());
- }
-
- @Test
- public void testGetTargetQualityMetrics_qualityNotAllowedReturnsMinQualityMetrics() {
- PasswordMetrics metrics =
- getTargetQualityMetrics(PASSWORD_COMPLEXITY_MEDIUM, PASSWORD_QUALITY_NUMERIC);
-
- assertEquals(PASSWORD_QUALITY_NUMERIC_COMPLEX, metrics.quality);
- assertEquals(/* expected= */ 4, metrics.length);
- }
-
- @Test
- public void testGetTargetQualityMetrics_highComplexityNumericComplex() {
- PasswordMetrics metrics = getTargetQualityMetrics(
- PASSWORD_COMPLEXITY_HIGH, PASSWORD_QUALITY_NUMERIC_COMPLEX);
-
- assertEquals(PASSWORD_QUALITY_NUMERIC_COMPLEX, metrics.quality);
- assertEquals(/* expected= */ 8, metrics.length);
- }
-
- @Test
- public void testGetTargetQualityMetrics_mediumComplexityAlphabetic() {
- PasswordMetrics metrics = getTargetQualityMetrics(
- PASSWORD_COMPLEXITY_MEDIUM, PASSWORD_QUALITY_ALPHABETIC);
-
- assertEquals(PASSWORD_QUALITY_ALPHABETIC, metrics.quality);
- assertEquals(/* expected= */ 4, metrics.length);
- }
-
- @Test
- public void testGetTargetQualityMetrics_lowComplexityAlphanumeric() {
- PasswordMetrics metrics = getTargetQualityMetrics(
- PASSWORD_COMPLEXITY_MEDIUM, PASSWORD_QUALITY_ALPHANUMERIC);
-
- assertEquals(PASSWORD_QUALITY_ALPHANUMERIC, metrics.quality);
- assertEquals(/* expected= */ 4, metrics.length);
- }
-
- @Test
- public void testGetActualRequiredQuality_nonComplex() {
- int actual = getActualRequiredQuality(
- PASSWORD_QUALITY_NUMERIC_COMPLEX,
- /* requiresNumeric= */ false,
- /* requiresLettersOrSymbols= */ false);
-
- assertEquals(PASSWORD_QUALITY_NUMERIC_COMPLEX, actual);
- }
-
- @Test
- public void testGetActualRequiredQuality_complexRequiresNone() {
- int actual = getActualRequiredQuality(
- PASSWORD_QUALITY_COMPLEX,
- /* requiresNumeric= */ false,
- /* requiresLettersOrSymbols= */ false);
-
- assertEquals(PASSWORD_QUALITY_UNSPECIFIED, actual);
- }
-
- @Test
- public void testGetActualRequiredQuality_complexRequiresNumeric() {
- int actual = getActualRequiredQuality(
- PASSWORD_QUALITY_COMPLEX,
- /* requiresNumeric= */ true,
- /* requiresLettersOrSymbols= */ false);
-
- assertEquals(PASSWORD_QUALITY_NUMERIC, actual);
- }
-
- @Test
- public void testGetActualRequiredQuality_complexRequiresLetters() {
- int actual = getActualRequiredQuality(
- PASSWORD_QUALITY_COMPLEX,
- /* requiresNumeric= */ false,
- /* requiresLettersOrSymbols= */ true);
-
- assertEquals(PASSWORD_QUALITY_ALPHABETIC, actual);
- }
-
- @Test
- public void testGetActualRequiredQuality_complexRequiresNumericAndLetters() {
- int actual = getActualRequiredQuality(
- PASSWORD_QUALITY_COMPLEX,
- /* requiresNumeric= */ true,
- /* requiresLettersOrSymbols= */ true);
-
- assertEquals(PASSWORD_QUALITY_ALPHANUMERIC, actual);
- }
-
- @Test
- public void testGetMinimumMetrics_userInputStricter() {
- PasswordMetrics metrics = getMinimumMetrics(
- PASSWORD_COMPLEXITY_HIGH,
- PASSWORD_QUALITY_ALPHANUMERIC,
- PASSWORD_QUALITY_NUMERIC,
- /* requiresNumeric= */ false,
- /* requiresLettersOrSymbols= */ false);
-
- assertEquals(PASSWORD_QUALITY_ALPHANUMERIC, metrics.quality);
- assertEquals(/* expected= */ 6, metrics.length);
- }
+ public void testMerge_single() {
+ PasswordMetrics metrics = new PasswordMetrics(CREDENTIAL_TYPE_PASSWORD);
+ assertEquals(CREDENTIAL_TYPE_PASSWORD,
+ PasswordMetrics.merge(Collections.singletonList(metrics)).credType);
+ }
+
+ @Test
+ public void testMerge_credentialTypes() {
+ PasswordMetrics none = new PasswordMetrics(CREDENTIAL_TYPE_NONE);
+ PasswordMetrics pattern = new PasswordMetrics(CREDENTIAL_TYPE_PATTERN);
+ PasswordMetrics password = new PasswordMetrics(CREDENTIAL_TYPE_PASSWORD);
+ assertEquals(CREDENTIAL_TYPE_PATTERN,
+ PasswordMetrics.merge(Arrays.asList(new PasswordMetrics[]{none, pattern}))
+ .credType);
+ assertEquals(CREDENTIAL_TYPE_PASSWORD,
+ PasswordMetrics.merge(Arrays.asList(new PasswordMetrics[]{none, password}))
+ .credType);
+ assertEquals(CREDENTIAL_TYPE_PASSWORD,
+ PasswordMetrics.merge(Arrays.asList(new PasswordMetrics[]{password, pattern}))
+ .credType);
+ }
+
+ @Test
+ public void testValidatePasswordMetrics_credentialTypes() {
+ PasswordMetrics none = new PasswordMetrics(CREDENTIAL_TYPE_NONE);
+ PasswordMetrics pattern = new PasswordMetrics(CREDENTIAL_TYPE_PATTERN);
+ PasswordMetrics password = new PasswordMetrics(CREDENTIAL_TYPE_PASSWORD);
+
+ // To pass minimal length check.
+ password.length = 4;
+
+ // No errors expected, credential is of stronger or equal type.
+ assertValidationErrors(
+ validatePasswordMetrics(none, PASSWORD_COMPLEXITY_NONE, false, none));
+ assertValidationErrors(
+ validatePasswordMetrics(none, PASSWORD_COMPLEXITY_NONE, false, pattern));
+ assertValidationErrors(
+ validatePasswordMetrics(none, PASSWORD_COMPLEXITY_NONE, false, password));
+ assertValidationErrors(
+ validatePasswordMetrics(pattern, PASSWORD_COMPLEXITY_NONE, false, pattern));
+ assertValidationErrors(
+ validatePasswordMetrics(pattern, PASSWORD_COMPLEXITY_NONE, false, password));
+ assertValidationErrors(
+ validatePasswordMetrics(password, PASSWORD_COMPLEXITY_NONE, false, password));
+
+ // Now actual credential type is weaker than required:
+ assertValidationErrors(
+ validatePasswordMetrics(pattern, PASSWORD_COMPLEXITY_NONE, false, none),
+ PasswordValidationError.WEAK_CREDENTIAL_TYPE, 0);
+ assertValidationErrors(
+ validatePasswordMetrics(password, PASSWORD_COMPLEXITY_NONE, false, none),
+ PasswordValidationError.WEAK_CREDENTIAL_TYPE, 0);
+ assertValidationErrors(
+ validatePasswordMetrics(password, PASSWORD_COMPLEXITY_NONE, false, pattern),
+ PasswordValidationError.WEAK_CREDENTIAL_TYPE, 0);
+ }
+
+ /**
+ * @param expected sequense of validation error codes followed by requirement values, must have
+ * even number of elements. Empty means no errors.
+ */
+ private void assertValidationErrors(
+ List<PasswordValidationError> actualErrors, int... expected) {
+ assertEquals("Test programming error: content shoud have even number of elements",
+ 0, expected.length % 2);
+ assertEquals("wrong number of validation errors", expected.length / 2, actualErrors.size());
+ HashMap<Integer, Integer> errorMap = new HashMap<>();
+ for (PasswordValidationError error : actualErrors) {
+ errorMap.put(error.errorCode, error.requirement);
+ }
- @Test
- public void testGetMinimumMetrics_actualRequiredQualityStricter() {
- PasswordMetrics metrics = getMinimumMetrics(
- PASSWORD_COMPLEXITY_HIGH,
- PASSWORD_QUALITY_UNSPECIFIED,
- PASSWORD_QUALITY_NUMERIC,
- /* requiresNumeric= */ false,
- /* requiresLettersOrSymbols= */ false);
-
- assertEquals(PASSWORD_QUALITY_NUMERIC_COMPLEX, metrics.quality);
- assertEquals(/* expected= */ 8, metrics.length);
+ for (int i = 0; i < expected.length / 2; i++) {
+ final int expectedError = expected[i * 2];
+ final int expectedRequirement = expected[i * 2 + 1];
+ assertTrue("error expected but not reported: " + expectedError,
+ errorMap.containsKey(expectedError));
+ assertEquals("unexpected requirement for error: " + expectedError,
+ Integer.valueOf(expectedRequirement), errorMap.get(expectedError));
+ }
}
}
diff --git a/core/tests/coretests/src/android/app/admin/PasswordPolicyTest.java b/core/tests/coretests/src/android/app/admin/PasswordPolicyTest.java
new file mode 100644
index 000000000000..e951054e6558
--- /dev/null
+++ b/core/tests/coretests/src/android/app/admin/PasswordPolicyTest.java
@@ -0,0 +1,196 @@
+/*
+ * 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.devicepolicy;
+
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.admin.PasswordMetrics;
+import android.app.admin.PasswordPolicy;
+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;
+
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class PasswordPolicyTest {
+
+ public static final int TEST_VALUE = 10;
+
+ @Test
+ public void testGetMinMetrics_unspecified() {
+ PasswordPolicy policy = testPolicy(PASSWORD_QUALITY_UNSPECIFIED);
+ PasswordMetrics minMetrics = policy.getMinMetrics();
+ assertEquals(CREDENTIAL_TYPE_NONE, minMetrics.credType);
+ assertEquals(0, minMetrics.length);
+ assertEquals(0, minMetrics.numeric);
+ }
+
+ @Test
+ public void testGetMinMetrics_something() {
+ PasswordPolicy policy = testPolicy(PASSWORD_QUALITY_SOMETHING);
+ PasswordMetrics minMetrics = policy.getMinMetrics();
+ assertEquals(CREDENTIAL_TYPE_PATTERN, minMetrics.credType);
+ assertEquals(0, minMetrics.length);
+ assertEquals(0, minMetrics.numeric);
+ }
+
+ @Test
+ public void testGetMinMetrics_biometricWeak() {
+ PasswordPolicy policy = testPolicy(PASSWORD_QUALITY_BIOMETRIC_WEAK);
+ PasswordMetrics minMetrics = policy.getMinMetrics();
+ assertEquals(CREDENTIAL_TYPE_PATTERN, minMetrics.credType);
+ assertEquals(0, minMetrics.length);
+ assertEquals(0, minMetrics.numeric);
+ }
+
+ @Test
+ public void testGetMinMetrics_numeric() {
+ PasswordPolicy policy = testPolicy(PASSWORD_QUALITY_NUMERIC);
+ PasswordMetrics minMetrics = policy.getMinMetrics();
+ assertEquals(CREDENTIAL_TYPE_PASSWORD, minMetrics.credType);
+ assertEquals(TEST_VALUE, minMetrics.length);
+ assertEquals(0, minMetrics.numeric); // numeric can doesn't really require digits.
+ assertEquals(0, minMetrics.letters);
+ assertEquals(0, minMetrics.lowerCase);
+ assertEquals(0, minMetrics.upperCase);
+ assertEquals(0, minMetrics.symbols);
+ assertEquals(0, minMetrics.nonLetter);
+ assertEquals(0, minMetrics.nonNumeric);
+ assertEquals(Integer.MAX_VALUE, minMetrics.seqLength);
+ }
+
+ @Test
+ public void testGetMinMetrics_numericDefaultLength() {
+ PasswordPolicy policy = testPolicy(PASSWORD_QUALITY_NUMERIC);
+ policy.length = 0; // reset to default
+ PasswordMetrics minMetrics = policy.getMinMetrics();
+ assertEquals(0, minMetrics.length);
+ }
+
+ @Test
+ public void testGetMinMetrics_numericComplex() {
+ PasswordPolicy policy = testPolicy(PASSWORD_QUALITY_NUMERIC_COMPLEX);
+ PasswordMetrics minMetrics = policy.getMinMetrics();
+ assertEquals(CREDENTIAL_TYPE_PASSWORD, minMetrics.credType);
+ assertEquals(TEST_VALUE, minMetrics.length);
+ assertEquals(0, minMetrics.numeric);
+ assertEquals(0, minMetrics.letters);
+ assertEquals(0, minMetrics.lowerCase);
+ assertEquals(0, minMetrics.upperCase);
+ assertEquals(0, minMetrics.symbols);
+ assertEquals(0, minMetrics.nonLetter);
+ assertEquals(0, minMetrics.nonNumeric);
+ assertEquals(PasswordMetrics.MAX_ALLOWED_SEQUENCE, minMetrics.seqLength);
+ }
+
+ @Test
+ public void testGetMinMetrics_alphabetic() {
+ PasswordPolicy policy = testPolicy(PASSWORD_QUALITY_ALPHABETIC);
+ PasswordMetrics minMetrics = policy.getMinMetrics();
+ assertEquals(CREDENTIAL_TYPE_PASSWORD, minMetrics.credType);
+ assertEquals(TEST_VALUE, minMetrics.length);
+ assertEquals(0, minMetrics.numeric);
+ assertEquals(0, minMetrics.letters);
+ assertEquals(0, minMetrics.lowerCase);
+ assertEquals(0, minMetrics.upperCase);
+ assertEquals(0, minMetrics.symbols);
+ assertEquals(0, minMetrics.nonLetter);
+ assertEquals(1, minMetrics.nonNumeric);
+ assertEquals(Integer.MAX_VALUE, minMetrics.seqLength);
+ }
+
+ @Test
+ public void testGetMinMetrics_alphanumeric() {
+ PasswordPolicy policy = testPolicy(PASSWORD_QUALITY_ALPHANUMERIC);
+ PasswordMetrics minMetrics = policy.getMinMetrics();
+ assertEquals(CREDENTIAL_TYPE_PASSWORD, minMetrics.credType);
+ assertEquals(TEST_VALUE, minMetrics.length);
+ assertEquals(1, minMetrics.numeric);
+ assertEquals(0, minMetrics.letters);
+ assertEquals(0, minMetrics.lowerCase);
+ assertEquals(0, minMetrics.upperCase);
+ assertEquals(0, minMetrics.symbols);
+ assertEquals(0, minMetrics.nonLetter);
+ assertEquals(1, minMetrics.nonNumeric);
+ assertEquals(Integer.MAX_VALUE, minMetrics.seqLength);
+ }
+
+ @Test
+ public void testGetMinMetrics_complex() {
+ PasswordPolicy policy = testPolicy(PASSWORD_QUALITY_COMPLEX);
+ PasswordMetrics minMetrics = policy.getMinMetrics();
+ assertEquals(CREDENTIAL_TYPE_PASSWORD, minMetrics.credType);
+ assertEquals(TEST_VALUE, minMetrics.length);
+ assertEquals(TEST_VALUE, minMetrics.letters);
+ assertEquals(TEST_VALUE, minMetrics.lowerCase);
+ assertEquals(TEST_VALUE, minMetrics.upperCase);
+ assertEquals(TEST_VALUE, minMetrics.symbols);
+ assertEquals(TEST_VALUE, minMetrics.numeric);
+ assertEquals(TEST_VALUE, minMetrics.nonLetter);
+ assertEquals(0, minMetrics.nonNumeric);
+ assertEquals(Integer.MAX_VALUE, minMetrics.seqLength);
+ }
+
+ @Test
+ public void testGetMinMetrics_complexDefault() {
+ PasswordPolicy policy = new PasswordPolicy();
+ policy.quality = PASSWORD_QUALITY_COMPLEX;
+ PasswordMetrics minMetrics = policy.getMinMetrics();
+ assertEquals(CREDENTIAL_TYPE_PASSWORD, minMetrics.credType);
+ assertEquals(0, minMetrics.length);
+ assertEquals(1, minMetrics.letters);
+ assertEquals(0, minMetrics.lowerCase);
+ assertEquals(0, minMetrics.upperCase);
+ assertEquals(1, minMetrics.symbols);
+ assertEquals(1, minMetrics.numeric);
+ assertEquals(0, minMetrics.nonLetter);
+ assertEquals(0, minMetrics.nonNumeric);
+ assertEquals(Integer.MAX_VALUE, minMetrics.seqLength);
+ }
+
+ private PasswordPolicy testPolicy(int quality) {
+ PasswordPolicy result = new PasswordPolicy();
+ result.quality = quality;
+ result.length = TEST_VALUE;
+ result.letters = TEST_VALUE;
+ result.lowerCase = TEST_VALUE;
+ result.upperCase = TEST_VALUE;
+ result.numeric = TEST_VALUE;
+ result.symbols = TEST_VALUE;
+ result.nonLetter = TEST_VALUE;
+ return result;
+ }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 51da0c871c4d..39bf7421b15e 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -611,6 +611,10 @@ public class TransactionParcelTests {
}
@Override
+ public void attachStartupAgents(String s) throws RemoteException {
+ }
+
+ @Override
public void scheduleApplicationInfoChanged(ApplicationInfo applicationInfo)
throws RemoteException {
}
diff --git a/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
new file mode 100644
index 000000000000..1b5ad8868a01
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+package android.app.timedetector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.TimestampedValue;
+
+import org.junit.Test;
+
+public class PhoneTimeSuggestionTest {
+ private static final int PHONE_ID = 99999;
+
+ @Test
+ public void testEquals() {
+ PhoneTimeSuggestion one =
+ new PhoneTimeSuggestion(PHONE_ID, new TimestampedValue<>(1111L, 2222L));
+ assertEquals(one, one);
+
+ PhoneTimeSuggestion two =
+ new PhoneTimeSuggestion(PHONE_ID, new TimestampedValue<>(1111L, 2222L));
+ assertEquals(one, two);
+ assertEquals(two, one);
+
+ PhoneTimeSuggestion three =
+ new PhoneTimeSuggestion(PHONE_ID + 1, new TimestampedValue<>(1111L, 2222L));
+ 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);
+ }
+
+ @Test
+ public void testParcelable() {
+ PhoneTimeSuggestion one =
+ new PhoneTimeSuggestion(PHONE_ID, new TimestampedValue<>(1111L, 2222L));
+ assertEquals(one, roundTripParcelable(one));
+
+ // DebugInfo should also be stored (but is not checked by equals()
+ one.addDebugInfo("This is debug info");
+ PhoneTimeSuggestion two = roundTripParcelable(one);
+ assertEquals(one.getDebugInfo(), two.getDebugInfo());
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T extends Parcelable> T roundTripParcelable(T one) {
+ Parcel parcel = Parcel.obtain();
+ parcel.writeTypedObject(one, 0);
+ parcel.setDataPosition(0);
+
+ T toReturn = (T) parcel.readTypedObject(PhoneTimeSuggestion.CREATOR);
+ parcel.recycle();
+ return toReturn;
+ }
+}
diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
index 6ec3dc923efd..0ac00b8e9bbc 100644
--- a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
+++ b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
@@ -173,7 +173,7 @@ public class UsageStatsTest {
left.update("com.test.activity1", 400000, ACTIVITY_STOPPED, 1);
assertEquals(left.mLastTimeUsed, 350000);
assertEquals(left.mLastTimeVisible, 400000);
- assertEquals(left.mActivities.get(1), ACTIVITY_STOPPED);
+ assertTrue(left.mActivities.indexOfKey(1) < 0);
assertEquals(left.mTotalTimeInForeground, 350000 - 200000);
assertEquals(left.mTotalTimeVisible, 400000 - 200000);
@@ -231,7 +231,7 @@ public class UsageStatsTest {
left.update("com.test.activity1", 400000, ACTIVITY_STOPPED, 1);
assertEquals(left.mLastTimeUsed, 300000);
assertEquals(left.mLastTimeVisible, 400000);
- assertEquals(left.mActivities.get(1), ACTIVITY_STOPPED);
+ assertTrue(left.mActivities.indexOfKey(1) < 0);
assertEquals(left.mTotalTimeInForeground, 300000 - 200000);
assertEquals(left.mTotalTimeVisible, 400000 - 100000);
}
@@ -249,7 +249,7 @@ public class UsageStatsTest {
left.update("com.test.activity1", 200000, ACTIVITY_STOPPED, 1);
assertEquals(left.mLastTimeUsed, 200000);
assertEquals(left.mLastTimeVisible, 200000);
- assertEquals(left.mActivities.get(1), ACTIVITY_STOPPED);
+ assertTrue(left.mActivities.indexOfKey(1) < 0);
assertEquals(left.mTotalTimeInForeground, 200000 - 100000);
assertEquals(left.mTotalTimeVisible, 200000 - 100000);
@@ -359,14 +359,14 @@ public class UsageStatsTest {
left.update("com.test.activity1", 550000, ACTIVITY_STOPPED, 1);
assertEquals(left.mLastTimeUsed, 450000);
assertEquals(left.mLastTimeVisible, 550000);
- assertEquals(left.mActivities.get(1), ACTIVITY_STOPPED);
+ assertTrue(left.mActivities.indexOfKey(1) < 0);
assertEquals(left.mTotalTimeInForeground, 350000);
assertEquals(left.mTotalTimeVisible, 350000 + 100000 /*550000 - 450000*/);
left.update("com.test.activity2", 650000, ACTIVITY_STOPPED, 2);
assertEquals(left.mLastTimeUsed, 450000);
assertEquals(left.mLastTimeVisible, 650000);
- assertEquals(left.mActivities.get(2), ACTIVITY_STOPPED);
+ assertTrue(left.mActivities.indexOfKey(2) < 0);
assertEquals(left.mTotalTimeInForeground, 350000);
assertEquals(left.mTotalTimeVisible, 450000 + 100000 /*650000 - 550000*/);
}
diff --git a/core/tests/coretests/src/android/content/pm/AndroidTelephonyCommonUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidTelephonyCommonUpdaterTest.java
deleted file mode 100644
index 8ab9ddbee6d8..000000000000
--- a/core/tests/coretests/src/android/content/pm/AndroidTelephonyCommonUpdaterTest.java
+++ /dev/null
@@ -1,140 +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 android.content.pm;
-
-import static android.content.pm.PackageBuilder.builder;
-import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_BASE;
-import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_MANAGER;
-import static android.content.pm.SharedLibraryNames.ANDROID_TELEPHONY_COMMON;
-
-import android.os.Build;
-import androidx.test.filters.SmallTest;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/**
- * Test for {@link AndroidHidlUpdater}
- */
-@SmallTest
-@RunWith(JUnit4.class)
-public class AndroidTelephonyCommonUpdaterTest extends PackageSharedLibraryUpdaterTest {
-
- private static final String OTHER_LIBRARY = "other.library";
- private static final String PHONE_UID = "android.uid.phone";
-
- @Test
- public void targeted_at_Q() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.Q);
-
- PackageBuilder after = builder().targetSdkVersion(Build.VERSION_CODES.Q)
- .requiredLibraries(ANDROID_TELEPHONY_COMMON);
-
- // Should add telephony-common libraries
- checkBackwardsCompatibility(before, after);
- }
-
- @Test
- public void targeted_at_Q_phoneUID() {
- PackageBuilder before = builder().setSharedUid(PHONE_UID)
- .targetSdkVersion(Build.VERSION_CODES.Q);
-
- // Should add telephony-common libraries
- PackageBuilder after = builder().setSharedUid(PHONE_UID)
- .targetSdkVersion(Build.VERSION_CODES.Q)
- .requiredLibraries(ANDROID_TELEPHONY_COMMON);
-
- checkBackwardsCompatibility(before, after);
- }
-
- @Test
- public void targeted_at_Q_not_empty_usesLibraries() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.Q)
- .requiredLibraries(OTHER_LIBRARY);
-
- // no change
- checkBackwardsCompatibility(before, before);
- }
-
- @Test
- public void targeted_at_Q_not_empty_usesLibraries_phoneUID() {
- PackageBuilder before = builder().setSharedUid(PHONE_UID)
- .targetSdkVersion(Build.VERSION_CODES.Q)
- .requiredLibraries(OTHER_LIBRARY);
-
- // The telephony-common jars should be added at the start of the list because it
- // is not on the bootclasspath and the package targets pre-R.
- PackageBuilder after = builder().setSharedUid(PHONE_UID)
- .targetSdkVersion(Build.VERSION_CODES.Q)
- .requiredLibraries(ANDROID_TELEPHONY_COMMON, OTHER_LIBRARY);
-
- checkBackwardsCompatibility(before, after);
- }
-
- @Test
- public void targeted_at_R_in_usesLibraries() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.Q + 1)
- .requiredLibraries(ANDROID_TELEPHONY_COMMON);
-
- PackageBuilder after = builder()
- .targetSdkVersion(Build.VERSION_CODES.Q + 1);
-
- // Libraries are removed because they are not available for apps target >= R and not run
- // on phone-uid
- checkBackwardsCompatibility(before, after);
- }
-
- @Test
- public void targeted_at_Q_in_usesLibraries() {
- PackageBuilder before = builder().asSystemApp()
- .targetSdkVersion(Build.VERSION_CODES.Q)
- .requiredLibraries(ANDROID_TELEPHONY_COMMON);
-
- // No change is required because the package explicitly requests the telephony libraries
- // and is targeted at the current version so does not need backwards compatibility.
- checkBackwardsCompatibility(before, before);
- }
-
-
- @Test
- public void targeted_at_R_in_usesOptionalLibraries() {
- PackageBuilder before = builder().targetSdkVersion(Build.VERSION_CODES.Q + 1)
- .optionalLibraries(ANDROID_TELEPHONY_COMMON);
-
- // Dependency is removed, it is not available.
- PackageBuilder after = builder().targetSdkVersion(Build.VERSION_CODES.Q + 1);
-
- // Libraries are removed because they are not available for apps targeting Q+
- checkBackwardsCompatibility(before, after);
- }
-
- @Test
- public void targeted_at_R() {
- PackageBuilder before = builder()
- .targetSdkVersion(Build.VERSION_CODES.Q + 1);
-
- // no change
- checkBackwardsCompatibility(before, before);
- }
-
- private void checkBackwardsCompatibility(PackageBuilder before, PackageBuilder after) {
- checkBackwardsCompatibility(before, after, AndroidTelephonyCommonUpdater::new);
- }
-}
diff --git a/core/tests/coretests/src/android/content/pm/PackageBuilder.java b/core/tests/coretests/src/android/content/pm/PackageBuilder.java
index f3a56e2814e4..f7544af43461 100644
--- a/core/tests/coretests/src/android/content/pm/PackageBuilder.java
+++ b/core/tests/coretests/src/android/content/pm/PackageBuilder.java
@@ -37,8 +37,6 @@ class PackageBuilder {
private ArrayList<String> mOptionalLibraries;
- private String mSharedUid;
-
public static PackageBuilder builder() {
return new PackageBuilder();
}
@@ -49,7 +47,6 @@ class PackageBuilder {
pkg.applicationInfo.flags = mFlags;
pkg.usesLibraries = mRequiredLibraries;
pkg.usesOptionalLibraries = mOptionalLibraries;
- pkg.mSharedUserId = mSharedUid;
return pkg;
}
@@ -58,11 +55,6 @@ class PackageBuilder {
return this;
}
- PackageBuilder setSharedUid(String uid) {
- this.mSharedUid = uid;
- return this;
- }
-
PackageBuilder asSystemApp() {
this.mFlags |= ApplicationInfo.FLAG_SYSTEM;
return this;
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index 23fabcec0c9b..77b7f2a3ce6c 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -17,6 +17,7 @@
package android.provider;
import static android.provider.DeviceConfig.OnPropertiesChangedListener;
+import static android.provider.DeviceConfig.Properties;
import static com.google.common.truth.Truth.assertThat;
@@ -42,27 +43,33 @@ import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class DeviceConfigTest {
- // TODO(b/109919982): Migrate tests to CTS
- private static final String sNamespace = "namespace1";
- private static final String sKey = "key1";
- private static final String sValue = "value1";
private static final long WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS = 2000; // 2 sec
+ private static final String DEFAULT_VALUE = "test_default_value";
+ private static final String NAMESPACE = "namespace1";
+ private static final String KEY = "key1";
+ private static final String KEY2 = "key2";
+ private static final String KEY3 = "key3";
+ private static final String VALUE = "value1";
+ private static final String VALUE2 = "value2";
+ private static final String VALUE3 = "value3";
@After
public void cleanUp() {
- deleteViaContentProvider(sNamespace, sKey);
+ deleteViaContentProvider(NAMESPACE, KEY);
+ deleteViaContentProvider(NAMESPACE, KEY2);
+ deleteViaContentProvider(NAMESPACE, KEY3);
}
@Test
public void getProperty_empty() {
- String result = DeviceConfig.getProperty(sNamespace, sKey);
+ String result = DeviceConfig.getProperty(NAMESPACE, KEY);
assertThat(result).isNull();
}
@Test
public void getProperty_nullNamespace() {
try {
- DeviceConfig.getProperty(null, sKey);
+ DeviceConfig.getProperty(null, KEY);
Assert.fail("Null namespace should have resulted in an NPE.");
} catch (NullPointerException e) {
// expected
@@ -72,7 +79,7 @@ public class DeviceConfigTest {
@Test
public void getProperty_nullName() {
try {
- DeviceConfig.getProperty(sNamespace, null);
+ DeviceConfig.getProperty(NAMESPACE, null);
Assert.fail("Null name should have resulted in an NPE.");
} catch (NullPointerException e) {
// expected
@@ -82,13 +89,13 @@ public class DeviceConfigTest {
@Test
public void getString_empty() {
final String default_value = "default_value";
- final String result = DeviceConfig.getString(sNamespace, sKey, default_value);
+ final String result = DeviceConfig.getString(NAMESPACE, KEY, default_value);
assertThat(result).isEqualTo(default_value);
}
@Test
public void getString_nullDefault() {
- final String result = DeviceConfig.getString(sNamespace, sKey, null);
+ final String result = DeviceConfig.getString(NAMESPACE, KEY, null);
assertThat(result).isNull();
}
@@ -96,16 +103,16 @@ public class DeviceConfigTest {
public void getString_nonEmpty() {
final String value = "new_value";
final String default_value = "default";
- DeviceConfig.setProperty(sNamespace, sKey, value, false);
+ DeviceConfig.setProperty(NAMESPACE, KEY, value, false);
- final String result = DeviceConfig.getString(sNamespace, sKey, default_value);
+ final String result = DeviceConfig.getString(NAMESPACE, KEY, default_value);
assertThat(result).isEqualTo(value);
}
@Test
public void getString_nullNamespace() {
try {
- DeviceConfig.getString(null, sKey, "default_value");
+ DeviceConfig.getString(null, KEY, "default_value");
Assert.fail("Null namespace should have resulted in an NPE.");
} catch (NullPointerException e) {
// expected
@@ -115,7 +122,7 @@ public class DeviceConfigTest {
@Test
public void getString_nullName() {
try {
- DeviceConfig.getString(sNamespace, null, "default_value");
+ DeviceConfig.getString(NAMESPACE, null, "default_value");
Assert.fail("Null name should have resulted in an NPE.");
} catch (NullPointerException e) {
// expected
@@ -125,7 +132,7 @@ public class DeviceConfigTest {
@Test
public void getBoolean_empty() {
final boolean default_value = true;
- final boolean result = DeviceConfig.getBoolean(sNamespace, sKey, default_value);
+ final boolean result = DeviceConfig.getBoolean(NAMESPACE, KEY, default_value);
assertThat(result).isEqualTo(default_value);
}
@@ -133,18 +140,18 @@ public class DeviceConfigTest {
public void getBoolean_valid() {
final boolean value = true;
final boolean default_value = false;
- DeviceConfig.setProperty(sNamespace, sKey, String.valueOf(value), false);
+ DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
- final boolean result = DeviceConfig.getBoolean(sNamespace, sKey, default_value);
+ final boolean result = DeviceConfig.getBoolean(NAMESPACE, KEY, default_value);
assertThat(result).isEqualTo(value);
}
@Test
public void getBoolean_invalid() {
final boolean default_value = true;
- DeviceConfig.setProperty(sNamespace, sKey, "not_a_boolean", false);
+ DeviceConfig.setProperty(NAMESPACE, KEY, "not_a_boolean", false);
- final boolean result = DeviceConfig.getBoolean(sNamespace, sKey, default_value);
+ final boolean result = DeviceConfig.getBoolean(NAMESPACE, KEY, default_value);
// Anything non-null other than case insensitive "true" parses to false.
assertThat(result).isFalse();
}
@@ -152,7 +159,7 @@ public class DeviceConfigTest {
@Test
public void getBoolean_nullNamespace() {
try {
- DeviceConfig.getBoolean(null, sKey, false);
+ DeviceConfig.getBoolean(null, KEY, false);
Assert.fail("Null namespace should have resulted in an NPE.");
} catch (NullPointerException e) {
// expected
@@ -162,7 +169,7 @@ public class DeviceConfigTest {
@Test
public void getBoolean_nullName() {
try {
- DeviceConfig.getBoolean(sNamespace, null, false);
+ DeviceConfig.getBoolean(NAMESPACE, null, false);
Assert.fail("Null name should have resulted in an NPE.");
} catch (NullPointerException e) {
// expected
@@ -172,7 +179,7 @@ public class DeviceConfigTest {
@Test
public void getInt_empty() {
final int default_value = 999;
- final int result = DeviceConfig.getInt(sNamespace, sKey, default_value);
+ final int result = DeviceConfig.getInt(NAMESPACE, KEY, default_value);
assertThat(result).isEqualTo(default_value);
}
@@ -180,18 +187,18 @@ public class DeviceConfigTest {
public void getInt_valid() {
final int value = 123;
final int default_value = 999;
- DeviceConfig.setProperty(sNamespace, sKey, String.valueOf(value), false);
+ DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
- final int result = DeviceConfig.getInt(sNamespace, sKey, default_value);
+ final int result = DeviceConfig.getInt(NAMESPACE, KEY, default_value);
assertThat(result).isEqualTo(value);
}
@Test
public void getInt_invalid() {
final int default_value = 999;
- DeviceConfig.setProperty(sNamespace, sKey, "not_an_int", false);
+ DeviceConfig.setProperty(NAMESPACE, KEY, "not_an_int", false);
- final int result = DeviceConfig.getInt(sNamespace, sKey, default_value);
+ final int result = DeviceConfig.getInt(NAMESPACE, KEY, default_value);
// Failure to parse results in using the default value
assertThat(result).isEqualTo(default_value);
}
@@ -199,7 +206,7 @@ public class DeviceConfigTest {
@Test
public void getInt_nullNamespace() {
try {
- DeviceConfig.getInt(null, sKey, 0);
+ DeviceConfig.getInt(null, KEY, 0);
Assert.fail("Null namespace should have resulted in an NPE.");
} catch (NullPointerException e) {
// expected
@@ -209,7 +216,7 @@ public class DeviceConfigTest {
@Test
public void getInt_nullName() {
try {
- DeviceConfig.getInt(sNamespace, null, 0);
+ DeviceConfig.getInt(NAMESPACE, null, 0);
Assert.fail("Null name should have resulted in an NPE.");
} catch (NullPointerException e) {
// expected
@@ -219,7 +226,7 @@ public class DeviceConfigTest {
@Test
public void getLong_empty() {
final long default_value = 123456;
- final long result = DeviceConfig.getLong(sNamespace, sKey, default_value);
+ final long result = DeviceConfig.getLong(NAMESPACE, KEY, default_value);
assertThat(result).isEqualTo(default_value);
}
@@ -227,18 +234,18 @@ public class DeviceConfigTest {
public void getLong_valid() {
final long value = 456789;
final long default_value = 123456;
- DeviceConfig.setProperty(sNamespace, sKey, String.valueOf(value), false);
+ DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
- final long result = DeviceConfig.getLong(sNamespace, sKey, default_value);
+ final long result = DeviceConfig.getLong(NAMESPACE, KEY, default_value);
assertThat(result).isEqualTo(value);
}
@Test
public void getLong_invalid() {
final long default_value = 123456;
- DeviceConfig.setProperty(sNamespace, sKey, "not_a_long", false);
+ DeviceConfig.setProperty(NAMESPACE, KEY, "not_a_long", false);
- final long result = DeviceConfig.getLong(sNamespace, sKey, default_value);
+ final long result = DeviceConfig.getLong(NAMESPACE, KEY, default_value);
// Failure to parse results in using the default value
assertThat(result).isEqualTo(default_value);
}
@@ -246,7 +253,7 @@ public class DeviceConfigTest {
@Test
public void getLong_nullNamespace() {
try {
- DeviceConfig.getLong(null, sKey, 0);
+ DeviceConfig.getLong(null, KEY, 0);
Assert.fail("Null namespace should have resulted in an NPE.");
} catch (NullPointerException e) {
// expected
@@ -256,7 +263,7 @@ public class DeviceConfigTest {
@Test
public void getLong_nullName() {
try {
- DeviceConfig.getLong(sNamespace, null, 0);
+ DeviceConfig.getLong(NAMESPACE, null, 0);
Assert.fail("Null name should have resulted in an NPE.");
} catch (NullPointerException e) {
// expected
@@ -266,7 +273,7 @@ public class DeviceConfigTest {
@Test
public void getFloat_empty() {
final float default_value = 123.456f;
- final float result = DeviceConfig.getFloat(sNamespace, sKey, default_value);
+ final float result = DeviceConfig.getFloat(NAMESPACE, KEY, default_value);
assertThat(result).isEqualTo(default_value);
}
@@ -274,18 +281,18 @@ public class DeviceConfigTest {
public void getFloat_valid() {
final float value = 456.789f;
final float default_value = 123.456f;
- DeviceConfig.setProperty(sNamespace, sKey, String.valueOf(value), false);
+ DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
- final float result = DeviceConfig.getFloat(sNamespace, sKey, default_value);
+ final float result = DeviceConfig.getFloat(NAMESPACE, KEY, default_value);
assertThat(result).isEqualTo(value);
}
@Test
public void getFloat_invalid() {
final float default_value = 123.456f;
- DeviceConfig.setProperty(sNamespace, sKey, "not_a_float", false);
+ DeviceConfig.setProperty(NAMESPACE, KEY, "not_a_float", false);
- final float result = DeviceConfig.getFloat(sNamespace, sKey, default_value);
+ final float result = DeviceConfig.getFloat(NAMESPACE, KEY, default_value);
// Failure to parse results in using the default value
assertThat(result).isEqualTo(default_value);
}
@@ -293,7 +300,7 @@ public class DeviceConfigTest {
@Test
public void getFloat_nullNamespace() {
try {
- DeviceConfig.getFloat(null, sKey, 0);
+ DeviceConfig.getFloat(null, KEY, 0);
Assert.fail("Null namespace should have resulted in an NPE.");
} catch (NullPointerException e) {
// expected
@@ -303,7 +310,7 @@ public class DeviceConfigTest {
@Test
public void getFloat_nullName() {
try {
- DeviceConfig.getFloat(sNamespace, null, 0);
+ DeviceConfig.getFloat(NAMESPACE, null, 0);
Assert.fail("Null name should have resulted in an NPE.");
} catch (NullPointerException e) {
// expected
@@ -313,7 +320,7 @@ public class DeviceConfigTest {
@Test
public void setProperty_nullNamespace() {
try {
- DeviceConfig.setProperty(null, sKey, sValue, false);
+ DeviceConfig.setProperty(null, KEY, VALUE, false);
Assert.fail("Null namespace should have resulted in an NPE.");
} catch (NullPointerException e) {
// expected
@@ -323,7 +330,7 @@ public class DeviceConfigTest {
@Test
public void setProperty_nullName() {
try {
- DeviceConfig.setProperty(sNamespace, null, sValue, false);
+ DeviceConfig.setProperty(NAMESPACE, null, VALUE, false);
Assert.fail("Null name should have resulted in an NPE.");
} catch (NullPointerException e) {
// expected
@@ -332,16 +339,16 @@ public class DeviceConfigTest {
@Test
public void setAndGetProperty_sameNamespace() {
- DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
- String result = DeviceConfig.getProperty(sNamespace, sKey);
- assertThat(result).isEqualTo(sValue);
+ DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
+ String result = DeviceConfig.getProperty(NAMESPACE, KEY);
+ assertThat(result).isEqualTo(VALUE);
}
@Test
public void setAndGetProperty_differentNamespace() {
String newNamespace = "namespace2";
- DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
- String result = DeviceConfig.getProperty(newNamespace, sKey);
+ DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
+ String result = DeviceConfig.getProperty(newNamespace, KEY);
assertThat(result).isNull();
}
@@ -349,41 +356,147 @@ public class DeviceConfigTest {
public void setAndGetProperty_multipleNamespaces() {
String newNamespace = "namespace2";
String newValue = "value2";
- DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
- DeviceConfig.setProperty(newNamespace, sKey, newValue, false);
- String result = DeviceConfig.getProperty(sNamespace, sKey);
- assertThat(result).isEqualTo(sValue);
- result = DeviceConfig.getProperty(newNamespace, sKey);
+ DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
+ DeviceConfig.setProperty(newNamespace, KEY, newValue, false);
+ String result = DeviceConfig.getProperty(NAMESPACE, KEY);
+ assertThat(result).isEqualTo(VALUE);
+ result = DeviceConfig.getProperty(newNamespace, KEY);
assertThat(result).isEqualTo(newValue);
// clean up
- deleteViaContentProvider(newNamespace, sKey);
+ deleteViaContentProvider(newNamespace, KEY);
}
@Test
public void setAndGetProperty_overrideValue() {
String newValue = "value2";
- DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
- DeviceConfig.setProperty(sNamespace, sKey, newValue, false);
- String result = DeviceConfig.getProperty(sNamespace, sKey);
+ DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
+ DeviceConfig.setProperty(NAMESPACE, KEY, newValue, false);
+ String result = DeviceConfig.getProperty(NAMESPACE, KEY);
assertThat(result).isEqualTo(newValue);
}
@Test
+ public void getProperties_fullNamespace() {
+ Properties properties = DeviceConfig.getProperties(NAMESPACE);
+ assertThat(properties.getKeyset()).isEmpty();
+
+ DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
+ DeviceConfig.setProperty(NAMESPACE, KEY2, VALUE2, false);
+ properties = DeviceConfig.getProperties(NAMESPACE);
+ assertThat(properties.getKeyset()).containsExactly(KEY, KEY2);
+ assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE);
+ assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2);
+
+ DeviceConfig.setProperty(NAMESPACE, KEY, VALUE3, false);
+ properties = DeviceConfig.getProperties(NAMESPACE);
+ assertThat(properties.getKeyset()).containsExactly(KEY, KEY2);
+ assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE3);
+ assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2);
+ }
+
+ @Test
+ public void getProperties_getString() {
+ DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
+ DeviceConfig.setProperty(NAMESPACE, KEY2, VALUE2, false);
+
+ Properties properties = DeviceConfig.getProperties(NAMESPACE, KEY, KEY2);
+ assertThat(properties.getKeyset()).containsExactly(KEY, KEY2);
+ assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE);
+ assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2);
+ }
+
+ @Test
+ public void getProperties_getBoolean() {
+ DeviceConfig.setProperty(NAMESPACE, KEY, "true", false);
+ DeviceConfig.setProperty(NAMESPACE, KEY2, "false", false);
+ DeviceConfig.setProperty(NAMESPACE, KEY3, "not a valid boolean", false);
+
+ Properties properties = DeviceConfig.getProperties(NAMESPACE, KEY, KEY2, KEY3);
+ assertThat(properties.getKeyset()).containsExactly(KEY, KEY2, KEY3);
+ assertThat(properties.getBoolean(KEY, true)).isTrue();
+ assertThat(properties.getBoolean(KEY, false)).isTrue();
+ assertThat(properties.getBoolean(KEY2, true)).isFalse();
+ assertThat(properties.getBoolean(KEY2, false)).isFalse();
+ // KEY3 was set to garbage, anything nonnull but "true" will parse as false
+ assertThat(properties.getBoolean(KEY3, true)).isFalse();
+ assertThat(properties.getBoolean(KEY3, false)).isFalse();
+ // If a key was not set, it will return the default value
+ assertThat(properties.getBoolean("missing_key", true)).isTrue();
+ assertThat(properties.getBoolean("missing_key", false)).isFalse();
+ }
+
+ @Test
+ public void getProperties_getInt() {
+ final int value = 101;
+
+ DeviceConfig.setProperty(NAMESPACE, KEY, Integer.toString(value), false);
+ DeviceConfig.setProperty(NAMESPACE, KEY2, "not a valid int", false);
+
+ Properties properties = DeviceConfig.getProperties(NAMESPACE, KEY, KEY2);
+ assertThat(properties.getKeyset()).containsExactly(KEY, KEY2);
+ assertThat(properties.getInt(KEY, -1)).isEqualTo(value);
+ // KEY2 was set to garbage, the default value is returned if an int cannot be parsed
+ assertThat(properties.getInt(KEY2, -1)).isEqualTo(-1);
+ }
+
+ @Test
+ public void getProperties_getFloat() {
+ final float value = 101.010f;
+
+ DeviceConfig.setProperty(NAMESPACE, KEY, Float.toString(value), false);
+ DeviceConfig.setProperty(NAMESPACE, KEY2, "not a valid float", false);
+
+ Properties properties = DeviceConfig.getProperties(NAMESPACE, KEY, KEY2);
+ assertThat(properties.getKeyset()).containsExactly(KEY, KEY2);
+ assertThat(properties.getFloat(KEY, -1.0f)).isEqualTo(value);
+ // KEY2 was set to garbage, the default value is returned if a float cannot be parsed
+ assertThat(properties.getFloat(KEY2, -1.0f)).isEqualTo(-1.0f);
+ }
+
+ @Test
+ public void getProperties_getLong() {
+ final long value = 101;
+
+ DeviceConfig.setProperty(NAMESPACE, KEY, Long.toString(value), false);
+ DeviceConfig.setProperty(NAMESPACE, KEY2, "not a valid long", false);
+
+ Properties properties = DeviceConfig.getProperties(NAMESPACE, KEY, KEY2);
+ assertThat(properties.getKeyset()).containsExactly(KEY, KEY2);
+ assertThat(properties.getLong(KEY, -1)).isEqualTo(value);
+ // KEY2 was set to garbage, the default value is returned if a long cannot be parsed
+ assertThat(properties.getLong(KEY2, -1)).isEqualTo(-1);
+ }
+
+ @Test
+ public void getProperties_defaults() {
+ DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
+ DeviceConfig.setProperty(NAMESPACE, KEY3, VALUE3, false);
+
+ Properties properties = DeviceConfig.getProperties(NAMESPACE, KEY, KEY2);
+ assertThat(properties.getKeyset()).containsExactly(KEY);
+ assertThat(properties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE);
+ // not set in DeviceConfig, but requested in getProperties
+ assertThat(properties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE);
+ // set in DeviceConfig, but not requested in getProperties
+ assertThat(properties.getString(KEY3, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE);
+ }
+
+ @Test
public void testOnPropertiesChangedListener() throws InterruptedException {
final CountDownLatch countDownLatch = new CountDownLatch(1);
OnPropertiesChangedListener changeListener = (properties) -> {
- assertThat(properties.getNamespace()).isEqualTo(sNamespace);
- assertThat(properties.getKeyset()).contains(sKey);
- assertThat(properties.getString(sKey, "default_value")).isEqualTo(sValue);
+ assertThat(properties.getNamespace()).isEqualTo(NAMESPACE);
+ assertThat(properties.getKeyset()).contains(KEY);
+ assertThat(properties.getString(KEY, "default_value")).isEqualTo(VALUE);
countDownLatch.countDown();
};
try {
- DeviceConfig.addOnPropertiesChangedListener(sNamespace,
+ DeviceConfig.addOnPropertiesChangedListener(NAMESPACE,
ActivityThread.currentApplication().getMainExecutor(), changeListener);
- DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
+ DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, false);
assertThat(countDownLatch.await(
WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
} catch (InterruptedException e) {
diff --git a/core/tests/coretests/src/android/util/TimestampedValueTest.java b/core/tests/coretests/src/android/util/TimestampedValueTest.java
index 6e3ab796c5d3..6fc2400316c2 100644
--- a/core/tests/coretests/src/android/util/TimestampedValueTest.java
+++ b/core/tests/coretests/src/android/util/TimestampedValueTest.java
@@ -55,12 +55,12 @@ public class TimestampedValueTest {
TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello");
Parcel parcel = Parcel.obtain();
try {
- TimestampedValue.writeToParcel(parcel, stringValue);
+ parcel.writeParcelable(stringValue, 0);
parcel.setDataPosition(0);
TimestampedValue<String> stringValueCopy =
- TimestampedValue.readFromParcel(parcel, null /* classLoader */, String.class);
+ parcel.readParcelable(null /* classLoader */);
assertEquals(stringValue, stringValueCopy);
} finally {
parcel.recycle();
@@ -72,12 +72,12 @@ public class TimestampedValueTest {
TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello");
Parcel parcel = Parcel.obtain();
try {
- TimestampedValue.writeToParcel(parcel, stringValue);
+ parcel.writeParcelable(stringValue, 0);
parcel.setDataPosition(0);
- TimestampedValue<Object> stringValueCopy =
- TimestampedValue.readFromParcel(parcel, null /* classLoader */, Object.class);
+ TimestampedValue<String> stringValueCopy =
+ parcel.readParcelable(null /* classLoader */);
assertEquals(stringValue, stringValueCopy);
} finally {
parcel.recycle();
@@ -85,15 +85,15 @@ public class TimestampedValueTest {
}
@Test
- public void testParceling_valueClassIncompatible() {
- TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello");
+ public void testParceling_valueClassNotParcelable() {
+ // This class is not one supported by Parcel.writeValue().
+ class NotParcelable {}
+
+ TimestampedValue<NotParcelable> notParcelableValue =
+ new TimestampedValue<>(1000, new NotParcelable());
Parcel parcel = Parcel.obtain();
try {
- TimestampedValue.writeToParcel(parcel, stringValue);
-
- parcel.setDataPosition(0);
-
- TimestampedValue.readFromParcel(parcel, null /* classLoader */, Double.class);
+ parcel.writeParcelable(notParcelableValue, 0);
fail();
} catch (RuntimeException expected) {
} finally {
@@ -106,12 +106,11 @@ public class TimestampedValueTest {
TimestampedValue<String> nullValue = new TimestampedValue<>(1000, null);
Parcel parcel = Parcel.obtain();
try {
- TimestampedValue.writeToParcel(parcel, nullValue);
+ parcel.writeParcelable(nullValue, 0);
parcel.setDataPosition(0);
- TimestampedValue<Object> nullValueCopy =
- TimestampedValue.readFromParcel(parcel, null /* classLoader */, String.class);
+ TimestampedValue<String> nullValueCopy = parcel.readParcelable(null /* classLoader */);
assertEquals(nullValue, nullValueCopy);
} finally {
parcel.recycle();
diff --git a/core/tests/coretests/src/android/view/InsetsFlagsTest.java b/core/tests/coretests/src/android/view/InsetsFlagsTest.java
new file mode 100644
index 000000000000..7d4445ba456a
--- /dev/null
+++ b/core/tests/coretests/src/android/view/InsetsFlagsTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+
+import static android.view.InsetsFlags.getAppearance;
+import static android.view.View.NAVIGATION_BAR_TRANSLUCENT;
+import static android.view.View.NAVIGATION_BAR_TRANSPARENT;
+import static android.view.View.STATUS_BAR_TRANSLUCENT;
+import static android.view.View.STATUS_BAR_TRANSPARENT;
+import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_SIDE_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_TOP_BAR;
+import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_SIDE_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_TOP_BAR;
+
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+import android.view.WindowInsetsController.Appearance;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link InsetsFlags}.
+ *
+ * <p>Build/Install/Run:
+ * atest FrameworksCoreTests:InsetsFlagsTest
+ *
+ * <p>This test class is a part of Window Manager Service tests and specified in
+ * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class InsetsFlagsTest {
+
+ @Test
+ public void testGetAppearance() {
+ assertContainsAppearance(APPEARANCE_LOW_PROFILE_BARS, SYSTEM_UI_FLAG_LOW_PROFILE);
+ assertContainsAppearance(APPEARANCE_LIGHT_TOP_BAR, SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+ assertContainsAppearance(APPEARANCE_LIGHT_SIDE_BARS, SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
+ assertContainsAppearance(APPEARANCE_OPAQUE_TOP_BAR,
+ 0xffffffff & ~(STATUS_BAR_TRANSLUCENT | STATUS_BAR_TRANSPARENT));
+ assertContainsAppearance(APPEARANCE_OPAQUE_SIDE_BARS,
+ 0xffffffff & ~(NAVIGATION_BAR_TRANSLUCENT | NAVIGATION_BAR_TRANSPARENT));
+ }
+
+ void assertContainsAppearance(@Appearance int appearance, int systemUiVisibility) {
+ assertTrue((getAppearance(systemUiVisibility) & appearance) == appearance);
+ }
+}
diff --git a/core/tests/coretests/src/android/view/ViewGroupTest.java b/core/tests/coretests/src/android/view/ViewGroupTest.java
index 979a839d2ffe..506cc2d3ff97 100644
--- a/core/tests/coretests/src/android/view/ViewGroupTest.java
+++ b/core/tests/coretests/src/android/view/ViewGroupTest.java
@@ -16,9 +16,14 @@
package android.view;
-import static androidx.test.InstrumentationRegistry.getContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import android.content.Context;
import android.graphics.Region;
@@ -39,6 +44,60 @@ import org.junit.Test;
@SmallTest
public class ViewGroupTest {
+ @Test
+ public void testDispatchMouseEventsUnderCursor() {
+ final Context context = getInstrumentation().getContext();
+ final TestView viewGroup = new TestView(context, 0 /* left */, 0 /* top */,
+ 200 /* right */, 200 /* bottom */);
+ final TestView viewA = spy(new TestView(context, 0 /* left */, 0 /* top */,
+ 100 /* right */, 200 /* bottom */));
+ final TestView viewB = spy(new TestView(context, 100 /* left */, 0 /* top */,
+ 200 /* right */, 200 /* bottom */));
+
+ viewGroup.addView(viewA);
+ viewGroup.addView(viewB);
+
+ // Make sure all of them handle touch events dispatched to them.
+ doReturn(true).when(viewA).dispatchTouchEvent(any());
+ doReturn(true).when(viewB).dispatchTouchEvent(any());
+
+ MotionEvent.PointerProperties[] properties = new MotionEvent.PointerProperties[2];
+ properties[0] = new MotionEvent.PointerProperties();
+ properties[0].id = 0;
+ properties[0].toolType = MotionEvent.TOOL_TYPE_FINGER;
+ properties[1] = new MotionEvent.PointerProperties();
+ properties[1].id = 1;
+ properties[1].toolType = MotionEvent.TOOL_TYPE_FINGER;
+
+ MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[2];
+ coords[0] = new MotionEvent.PointerCoords();
+ coords[0].x = 80;
+ coords[0].y = 100;
+ coords[1] = new MotionEvent.PointerCoords();
+ coords[1].x = 240;
+ coords[1].y = 100;
+
+ MotionEvent event;
+ // Make sure the down event is active with a pointer which coordinate is different from the
+ // cursor position, which is the midpoint of all 2 pointers above.
+ event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */, MotionEvent.ACTION_DOWN,
+ 2 /* pointerCount */, properties, coords, 0 /* metaState */, 0 /* buttonState */,
+ 0 /* xPrecision */, 0 /* yPrecision */, 0 /* deviceId */, 0 /* edgeFlags */,
+ InputDevice.SOURCE_MOUSE, 0 /* flags */);
+ viewGroup.dispatchTouchEvent(event);
+ verify(viewB).dispatchTouchEvent(event);
+
+ event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
+ MotionEvent.ACTION_POINTER_DOWN | (1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT),
+ 2 /* pointerCount */, properties, coords, 0 /* metaState */, 0 /* buttonState */,
+ 0 /* xPrecision */, 0 /* yPrecision */, 0 /* deviceId */, 0 /* edgeFlags */,
+ InputDevice.SOURCE_MOUSE, 0 /* flags */);
+ viewGroup.dispatchTouchEvent(event);
+ verify(viewB).dispatchTouchEvent(event);
+
+ verify(viewA, never()).dispatchTouchEvent(any());
+ }
+
/**
* Test if {@link ViewGroup#subtractObscuredTouchableRegion} works as expected.
*
@@ -59,7 +118,7 @@ public class ViewGroupTest {
*/
@Test
public void testSubtractObscuredTouchableRegion() {
- final Context context = getContext();
+ final Context context = getInstrumentation().getContext();
final TestView viewA = new TestView(context, 8 /* right */);
final TestView viewB = new TestView(context, 6 /* right */);
final TestView viewC = new TestView(context, 10 /* right */);
@@ -119,10 +178,14 @@ public class ViewGroupTest {
(contain ? "" : " not"), x), contain, region.contains(x, 0 /* y */));
}
- private static class TestView extends ViewGroup {
+ public static class TestView extends ViewGroup {
TestView(Context context, int right) {
+ this(context, 0 /* left */, 0 /* top */, right, 1 /* bottom */);
+ }
+
+ TestView(Context context, int left, int top, int right, int bottom) {
super(context);
- setFrame(0 /* left */, 0 /* top */, right, 1 /* bottom */);
+ setFrame(left, top, right, bottom);
}
@Override
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 682416c58c72..3586216ad421 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -23,8 +23,6 @@ import android.graphics.Region;
import android.os.Bundle;
import android.os.IBinder;
-import java.util.List;
-
/**
* Stub implementation of IAccessibilityServiceConnection so each test doesn't need to implement
* all of the methods
@@ -73,7 +71,7 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon
return null;
}
- public List<AccessibilityWindowInfo> getWindows() {
+ public AccessibilityWindowInfo.WindowListSparseArray getWindows() {
return null;
}
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index c5da54936653..02a88fc8aecb 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -162,8 +162,13 @@ public class ContentCaptureSessionTest {
}
@Override
- public void internalNotifySessionLifecycle(boolean started) {
- throw new UnsupportedOperationException("Should not have been called");
+ void internalNotifySessionResumed() {
+ throw new UnsupportedOperationException("should not have been called");
+ }
+
+ @Override
+ void internalNotifySessionPaused() {
+ throw new UnsupportedOperationException("should not have been called");
}
@Override
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index abee1da2ed7a..7b4054348642 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -345,9 +345,9 @@ public class AccessibilityShortcutControllerTest {
accessibilityShortcutController.performAccessibilityShortcut();
accessibilityShortcutController.performAccessibilityShortcut();
verify(mToast).show();
- assertEquals(WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS,
+ assertEquals(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
mLayoutParams.privateFlags
- & WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS);
+ & WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
verify(mAccessibilityManagerService, times(1)).performAccessibilityShortcut();
}
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 cc6eeedaab46..5ea91da98fd3 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -284,7 +284,7 @@ public class ChooserActivityTest {
waitForIdle();
UsageStatsManager usm = activity.getUsageStatsManager();
verify(sOverrides.resolverListController, times(1))
- .sort(Mockito.any(List.class));
+ .topK(Mockito.any(List.class), Mockito.anyInt());
assertThat(activity.getIsSelected(), is(false));
sOverrides.onSafelyStartCallback = targetInfo -> {
return true;
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
index fcec00e34278..6218fa9fbac8 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
@@ -41,6 +41,8 @@ import android.util.ArrayMap;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -112,13 +114,30 @@ public class ResolverListControllerTest {
when(mMockContext.getSystemService(Context.USAGE_STATS_SERVICE)).thenReturn(mUsm);
mController = new ResolverListController(mMockContext, mMockPackageManager, sendIntent,
refererPackage, UserHandle.USER_CURRENT);
- mController.sort(new ArrayList<ResolverActivity.ResolvedComponentInfo>());
+ mController.sort(new ArrayList<ResolvedComponentInfo>());
long beforeReport = getCount(mUsm, packageName, action, annotation);
mController.updateChooserCounts(packageName, UserHandle.USER_CURRENT, action);
long afterReport = getCount(mUsm, packageName, action, annotation);
assertThat(afterReport, is(beforeReport + 1l));
}
+ @Test
+ public void topKEqualsToSort() {
+ String annotation = "test_annotation";
+ Intent sendIntent = createSendImageIntent(annotation);
+ String refererPackage = "test_referer_package";
+ List<ResolvedComponentInfo> resolvedComponents = createResolvedComponentsForTest(15);
+ mUsm = new UsageStatsManager(mMockContext, mMockService);
+ when(mMockContext.getSystemService(Context.USAGE_STATS_SERVICE)).thenReturn(mUsm);
+ mController = new ResolverListController(mMockContext, mMockPackageManager, sendIntent,
+ refererPackage, UserHandle.USER_CURRENT);
+ List<ResolvedComponentInfo> topKList = new ArrayList<>(resolvedComponents);
+ mController.topK(topKList, 5);
+ List<ResolvedComponentInfo> sortList = new ArrayList<>(topKList);
+ mController.sort(sortList);
+ assertThat(sortList.subList(0, 5), is(topKList.subList(0, 5)));
+ }
+
private UsageStats initStats(String packageName, String action,
String annotation, int count) {
ArrayMap<String, ArrayMap<String, Integer>> chooserCounts = new ArrayMap<>();
@@ -156,4 +175,12 @@ public class ResolverListControllerTest {
}
return packageStats.mChooserCounts.get(action).getOrDefault(annotation, 0);
}
-} \ No newline at end of file
+
+ private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) {
+ List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
+ for (int i = 0; i < numberOfResults; i++) {
+ infoList.add(ResolverDataProvider.createResolvedComponentInfo(i));
+ }
+ return infoList;
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
index b93c3a7a17e2..0be5009f85f0 100644
--- a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
+++ b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
@@ -48,7 +48,6 @@ public class RegisterStatusBarResultTest {
final RegisterStatusBarResult original = new RegisterStatusBarResult(iconMap,
0x2 /* disabledFlags1 */,
0x4 /* systemUiVisibility */,
- true /* menuVisible */,
0x8 /* imeWindowVis */,
0x10 /* imeBackDisposition */,
false /* showImeSwitcher */,
@@ -58,7 +57,9 @@ public class RegisterStatusBarResultTest {
new Binder() /* imeToken */,
new Rect(0x100, 0x200, 0x400, 0x800) /* fullscreenStackBounds */,
new Rect(0x1000, 0x2000, 0x4000, 0x8000) /* dockedStackBounds */,
- true /* navbarColorManagedByIme */);
+ true /* navbarColorManagedByIme */,
+ true /* appFullscreen */,
+ true /* appImmersive */);
final RegisterStatusBarResult copy = clone(original);
@@ -69,7 +70,6 @@ public class RegisterStatusBarResultTest {
assertThat(copy.mDisabledFlags1).isEqualTo(original.mDisabledFlags1);
assertThat(copy.mSystemUiVisibility).isEqualTo(original.mSystemUiVisibility);
- assertThat(copy.mMenuVisible).isEqualTo(original.mMenuVisible);
assertThat(copy.mImeWindowVis).isEqualTo(original.mImeWindowVis);
assertThat(copy.mImeBackDisposition).isEqualTo(original.mImeBackDisposition);
assertThat(copy.mShowImeSwitcher).isEqualTo(original.mShowImeSwitcher);
@@ -82,6 +82,8 @@ public class RegisterStatusBarResultTest {
assertThat(copy.mFullscreenStackBounds).isEqualTo(original.mFullscreenStackBounds);
assertThat(copy.mDockedStackBounds).isEqualTo(original.mDockedStackBounds);
assertThat(copy.mNavbarColorManagedByIme).isEqualTo(original.mNavbarColorManagedByIme);
+ assertThat(copy.mAppFullscreen).isEqualTo(original.mAppFullscreen);
+ assertThat(copy.mAppImmersive).isEqualTo(original.mAppImmersive);
}
private RegisterStatusBarResult clone(RegisterStatusBarResult original) {
diff --git a/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java b/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java
new file mode 100644
index 000000000000..05bab1c185de
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+
+import android.test.AndroidTestCase;
+
+import java.util.Arrays;
+
+
+public class LockscreenCredentialTest extends AndroidTestCase {
+
+ public void testEmptyCredential() {
+ LockscreenCredential empty = LockscreenCredential.createNone();
+
+ assertTrue(empty.isNone());
+ assertEquals(0, empty.size());
+ assertNotNull(empty.getCredential());
+
+ assertFalse(empty.isPin());
+ assertFalse(empty.isPassword());
+ assertFalse(empty.isPattern());
+ }
+
+ public void testPinCredential() {
+ LockscreenCredential pin = LockscreenCredential.createPin("3456");
+
+ assertTrue(pin.isPin());
+ assertEquals(4, pin.size());
+ assertTrue(Arrays.equals("3456".getBytes(), pin.getCredential()));
+
+ assertFalse(pin.isNone());
+ assertFalse(pin.isPassword());
+ assertFalse(pin.isPattern());
+ }
+
+ public void testPasswordCredential() {
+ LockscreenCredential password = LockscreenCredential.createPassword("password");
+
+ assertTrue(password.isPassword());
+ assertEquals(8, password.size());
+ assertTrue(Arrays.equals("password".getBytes(), password.getCredential()));
+
+ assertFalse(password.isNone());
+ assertFalse(password.isPin());
+ assertFalse(password.isPattern());
+ }
+
+ public void testPatternCredential() {
+ LockscreenCredential pattern = LockscreenCredential.createPattern(Arrays.asList(
+ LockPatternView.Cell.of(0, 0),
+ LockPatternView.Cell.of(0, 1),
+ LockPatternView.Cell.of(0, 2),
+ LockPatternView.Cell.of(1, 2),
+ LockPatternView.Cell.of(2, 2)
+ ));
+
+ assertTrue(pattern.isPattern());
+ assertEquals(5, pattern.size());
+ assertTrue(Arrays.equals("12369".getBytes(), pattern.getCredential()));
+
+ assertFalse(pattern.isNone());
+ assertFalse(pattern.isPin());
+ assertFalse(pattern.isPassword());
+ }
+
+ public void testPasswordOrNoneCredential() {
+ assertEquals(LockscreenCredential.createNone(),
+ LockscreenCredential.createPasswordOrNone(null));
+ assertEquals(LockscreenCredential.createNone(),
+ LockscreenCredential.createPasswordOrNone(""));
+ assertEquals(LockscreenCredential.createPassword("abcd"),
+ LockscreenCredential.createPasswordOrNone("abcd"));
+ }
+
+ public void testPinOrNoneCredential() {
+ assertEquals(LockscreenCredential.createNone(),
+ LockscreenCredential.createPinOrNone(null));
+ assertEquals(LockscreenCredential.createNone(),
+ LockscreenCredential.createPinOrNone(""));
+ assertEquals(LockscreenCredential.createPin("1357"),
+ LockscreenCredential.createPinOrNone("1357"));
+ }
+
+ public void testSanitize() {
+ LockscreenCredential password = LockscreenCredential.createPassword("password");
+ password.zeroize();
+
+ try {
+ password.isNone();
+ fail("Sanitized credential still accessible");
+ } catch (IllegalStateException expected) { }
+ try {
+ password.isPattern();
+ fail("Sanitized credential still accessible");
+ } catch (IllegalStateException expected) { }
+ try {
+ password.isPin();
+ fail("Sanitized credential still accessible");
+ } catch (IllegalStateException expected) { }
+ try {
+ password.isPassword();
+ fail("Sanitized credential still accessible");
+ } catch (IllegalStateException expected) { }
+ try {
+ password.size();
+ fail("Sanitized credential still accessible");
+ } catch (IllegalStateException expected) { }
+ try {
+ password.getCredential();
+ fail("Sanitized credential still accessible");
+ } catch (IllegalStateException expected) { }
+ }
+
+ public void testEquals() {
+ assertEquals(LockscreenCredential.createNone(), LockscreenCredential.createNone());
+ assertEquals(LockscreenCredential.createPassword("1234"),
+ LockscreenCredential.createPassword("1234"));
+ assertEquals(LockscreenCredential.createPin("4321"),
+ LockscreenCredential.createPin("4321"));
+ assertEquals(createPattern("1234"), createPattern("1234"));
+
+ assertNotSame(LockscreenCredential.createPassword("1234"),
+ LockscreenCredential.createNone());
+ assertNotSame(LockscreenCredential.createPassword("1234"),
+ LockscreenCredential.createPassword("4321"));
+ assertNotSame(LockscreenCredential.createPassword("1234"),
+ createPattern("1234"));
+ assertNotSame(LockscreenCredential.createPassword("1234"),
+ LockscreenCredential.createPin("1234"));
+
+ assertNotSame(LockscreenCredential.createPin("1111"),
+ LockscreenCredential.createNone());
+ assertNotSame(LockscreenCredential.createPin("1111"),
+ LockscreenCredential.createPin("2222"));
+ assertNotSame(LockscreenCredential.createPin("1111"),
+ createPattern("1111"));
+ assertNotSame(LockscreenCredential.createPin("1111"),
+ LockscreenCredential.createPassword("1111"));
+
+ assertNotSame(createPattern("5678"),
+ LockscreenCredential.createNone());
+ assertNotSame(createPattern("5678"),
+ createPattern("1234"));
+ assertNotSame(createPattern("5678"),
+ LockscreenCredential.createPassword("5678"));
+ assertNotSame(createPattern("5678"),
+ LockscreenCredential.createPin("5678"));
+ }
+
+ public void testDuplicate() {
+ LockscreenCredential credential;
+
+ credential = LockscreenCredential.createNone();
+ assertEquals(credential, credential.duplicate());
+ credential = LockscreenCredential.createPassword("abcd");
+ assertEquals(credential, credential.duplicate());
+ credential = LockscreenCredential.createPin("1234");
+ assertEquals(credential, credential.duplicate());
+ credential = createPattern("5678");
+ assertEquals(credential, credential.duplicate());
+ }
+
+ private LockscreenCredential createPattern(String patternString) {
+ return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
+ patternString.getBytes()));
+ }
+}
diff --git a/core/tests/featureflagtests/OWNERS b/core/tests/featureflagtests/OWNERS
index 1a8fd2b62f9a..2ff4f5ab8807 100644
--- a/core/tests/featureflagtests/OWNERS
+++ b/core/tests/featureflagtests/OWNERS
@@ -1,2 +1,2 @@
sbasi@google.com
-zhfan@google.com \ No newline at end of file
+tmfang@google.com \ No newline at end of file
diff --git a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
index 9913531cdf13..50e8474e8d52 100644
--- a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
@@ -62,7 +62,9 @@ public class LockPatternUtilsTest {
Settings.Global.putInt(cr, Settings.Global.DEVICE_DEMO_MODE, deviceDemoMode);
final ILockSettings ils = Mockito.mock(ILockSettings.class);
- when(ils.havePassword(DEMO_USER_ID)).thenReturn(isSecure);
+ when(ils.getCredentialType(DEMO_USER_ID)).thenReturn(
+ isSecure ? LockPatternUtils.CREDENTIAL_TYPE_PASSWORD
+ : LockPatternUtils.CREDENTIAL_TYPE_NONE);
when(ils.getLong("lockscreen.password_type", PASSWORD_QUALITY_UNSPECIFIED, DEMO_USER_ID))
.thenReturn((long) PASSWORD_QUALITY_MANAGED);
// TODO(b/63758238): stop spying the class under test
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 4493f3a8dddc..befa63760a49 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -22,6 +22,12 @@ prebuilt_etc {
}
prebuilt_etc {
+ name: "preinstalled-packages-platform.xml",
+ sub_dir: "sysconfig",
+ src: "preinstalled-packages-platform.xml",
+}
+
+prebuilt_etc {
name: "hiddenapi-package-whitelist.xml",
sub_dir: "sysconfig",
src: "hiddenapi-package-whitelist.xml",
@@ -133,3 +139,8 @@ prebuilt_etc {
sub_dir: "permissions",
src: "com.android.timezone.updater.xml",
}
+
+filegroup {
+ name: "services.core.protolog.json",
+ srcs: ["services.core.protolog.json"],
+}
diff --git a/data/etc/TEST_MAPPING b/data/etc/TEST_MAPPING
new file mode 100644
index 000000000000..1a5db2f192cf
--- /dev/null
+++ b/data/etc/TEST_MAPPING
@@ -0,0 +1,13 @@
+{
+ "presubmit": [
+ {
+ "file_patterns": ["(/|^)platform.xml"],
+ "name": "CtsPermission2TestCases",
+ "options": [
+ {
+ "include-filter": "android.permission2.cts.RuntimePermissionProperties"
+ }
+ ]
+ }
+ ]
+}
diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml
index d36a82684e9e..61281eea7134 100644
--- a/data/etc/car/com.google.android.car.kitchensink.xml
+++ b/data/etc/car/com.google.android.car.kitchensink.xml
@@ -16,17 +16,30 @@
-->
<permissions>
<privapp-permissions package="com.google.android.car.kitchensink">
+ <permission name="android.permission.ACCESS_NETWORK_STATE"/>
+ <permission name="android.permission.ACCESS_WIFI_STATE"/>
+ <permission name="android.permission.ACTIVITY_EMBEDDING"/>
+ <permission name="android.permission.INJECT_EVENTS"/>
+ <!-- use for CarServiceUnitTest and CarServiceTest -->
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <!-- use for CarServiceUnitTest -->
+ <permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
<permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
<permission name="android.permission.LOCATION_HARDWARE"/>
<permission name="android.permission.MANAGE_USB"/>
<permission name="android.permission.MANAGE_USERS"/>
+ <!-- use for CarServiceTest -->
+ <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
<permission name="android.permission.MODIFY_AUDIO_ROUTING"/>
<permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.PROVIDE_TRUST_AGENT"/>
+ <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
<permission name="android.permission.REAL_GET_TASKS"/>
<permission name="android.permission.READ_LOGS"/>
<permission name="android.permission.REBOOT"/>
+ <!-- use for CarServiceTest -->
+ <permission name="android.permission.SET_ACTIVITY_WATCHER"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/framework-sysconfig.xml b/data/etc/framework-sysconfig.xml
index bf369c4d5984..67c9e177eb30 100755
--- a/data/etc/framework-sysconfig.xml
+++ b/data/etc/framework-sysconfig.xml
@@ -29,6 +29,8 @@
'service' attribute here is a flattened ComponentName string. -->
<backup-transport-whitelisted-service
service="com.android.localtransport/.LocalTransportService" />
+ <backup-transport-whitelisted-service
+ service="com.android.encryptedlocaltransport/.EncryptedLocalTransportService" />
<!-- Whitelist Shell to use the bugreport API -->
<bugreport-whitelisted package="com.android.shell" />
diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml
index 054f68ba9e5c..07a5617009d5 100644
--- a/data/etc/hiddenapi-package-whitelist.xml
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -53,6 +53,10 @@ Do NOT include any apps that are updatable via Play Store!
<hidden-api-whitelisted-app package="com.android.providers.media" />
<hidden-api-whitelisted-app package="com.android.providers.tv" />
<hidden-api-whitelisted-app package="com.android.providers.userdictionary" />
+ <!-- TODO (b/141954427): Remove networkstack -->
+ <hidden-api-whitelisted-app package="com.android.networkstack" />
+ <!-- TODO (b/141954427): Remove wifistack -->
+ <hidden-api-whitelisted-app package="com.android.wifi" />
<hidden-api-whitelisted-app package="com.android.smspush" />
<hidden-api-whitelisted-app package="com.android.spare_parts" />
<hidden-api-whitelisted-app package="com.android.statementservice" />
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index d66930abd30b..dceb243972e2 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -206,6 +206,10 @@
targetSdk="29">
<new-permission name="android.permission.ACCESS_BACKGROUND_LOCATION" />
</split-permission>
+ <split-permission name="android.permission.READ_EXTERNAL_STORAGE"
+ targetSdk="29">
+ <new-permission name="android.permission.ACCESS_MEDIA_LOCATION" />
+ </split-permission>
<!-- This is a list of all the libraries available for application
code to link against. -->
@@ -225,8 +229,6 @@
<library name="android.hidl.manager-V1.0-java"
file="/system/framework/android.hidl.manager-V1.0-java.jar"
dependency="android.hidl.base-V1.0-java" />
- <library name="telephony-common"
- file="/system/framework/telephony-common.jar" />
<!-- These are the standard packages that are white-listed to always have internet
access while in power save mode, even if they aren't in the foreground. -->
diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml
new file mode 100644
index 000000000000..ccd8b5bb5347
--- /dev/null
+++ b/data/etc/preinstalled-packages-platform.xml
@@ -0,0 +1,90 @@
+<?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.
+-->
+
+<!--
+This XML file declares which system packages should be initially installed for new users based on
+the type of user. All system packages on the device should ideally have an entry in an xml file
+(keys by its manifest name).
+
+Main user-types (every user will be at least one of these types) are:
+ SYSTEM (user 0)
+ FULL (any non-profile human user)
+ PROFILE (profile human user)
+
+Additional optional types are: GUEST, RESTRICTED, MANAGED_PROFILE, EPHEMERAL, DEMO
+
+The meaning of each of these user types is delineated by flags in
+frameworks/base/core/java/android/content/pm/UserInfo.java.
+See frameworks/base/services/core/java/com/android/server/pm/UserSystemPackageInstaller#getFlagsFromUserTypes
+
+The following three examples should cover most normal cases:
+
+1. For a system package to be pre-installed only in user 0:
+
+ <install-in-user-type package="com.android.example">
+ <install-in user-type="SYSTEM">
+ </install-in-user-type>
+
+
+2. For a system package to be pre-installed on all human users (e.g. a web browser), i.e. to be
+installed on any user of type type FULL or PROFILE (since this covers all human users):
+
+ <install-in-user-type package="com.android.example">
+ <install-in user-type="FULL">
+ <install-in user-type="PROFILE">
+ </install-in-user-type>
+
+
+3. For a system package to be pre-installed on all human users except for profile users (e.g. a
+wallpaper app, since profiles cannot display wallpaper):
+
+ <install-in-user-type package="com.android.example">
+ <install-in user-type="FULL">
+ </install-in-user-type>
+
+
+Some system packages truly are required to be on all users, regardless of type, in which case use:
+ <install-in-user-type package="com.android.example">
+ <install-in user-type="SYSTEM">
+ <install-in user-type="FULL">
+ <install-in user-type="PROFILE">
+ </install-in-user-type>
+
+More fine-grained options are also available (see below). Additionally, packages can blacklist
+user types. Blacklists override any whitelisting (in any file).
+E.g.
+ <install-in-user-type package="com.android.example">
+ <install-in user-type="FULL" />
+ <do-not-install-in user-type="GUEST" />
+ </install-in-user-type>
+
+If a user is of type FULL and GUEST, this package will NOT be installed, because the
+'do-not-install-in' takes precedence over 'install-in'.
+
+The way that a device treats system packages that do not have any entry (for any user type) at all
+is determined by the config resource value config_userTypePackageWhitelistMode.
+See frameworks/base/core/res/res/values/config.xml#config_userTypePackageWhitelistMode.
+
+Changes to the whitelist during system updates can result in installing new system packages
+to pre-existing users, but cannot uninstall system packages from pre-existing users.
+-->
+<config>
+ <install-in-user-type package="com.android.providers.settings">
+ <install-in user-type="SYSTEM" />
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+</config>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 89523d6bbefb..ac742e217ceb 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -50,6 +50,12 @@ applications that come with the platform
<permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
</privapp-permissions>
+ <privapp-permissions package="com.android.cellbroadcastservice">
+ <permission name="android.permission.MODIFY_PHONE_STATE"/>
+ <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+ <permission name="android.permission.RECEIVE_EMERGENCY_BROADCAST"/>
+ </privapp-permissions>
+
<privapp-permissions package="com.android.externalstorage">
<permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
@@ -121,12 +127,14 @@ applications that come with the platform
<permission name="android.permission.APPROVE_INCIDENT_REPORTS"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
+ <permission name="android.permission.PACKAGE_USAGE_STATS" />
</privapp-permissions>
<privapp-permissions package="com.android.phone">
<permission name="android.permission.ACCESS_IMS_CALL_SERVICE"/>
<permission name="android.permission.BIND_CARRIER_MESSAGING_SERVICE"/>
<permission name="android.permission.BIND_CARRIER_SERVICES"/>
+ <permission name="android.permission.BIND_CELL_BROADCAST_SERVICE"/>
<permission name="android.permission.BIND_IMS_SERVICE"/>
<permission name="android.permission.BIND_TELEPHONY_DATA_SERVICE"/>
<permission name="android.permission.BIND_VISUAL_VOICEMAIL_SERVICE"/>
@@ -353,11 +361,12 @@ applications that come with the platform
<permission name="android.permission.MANAGE_DYNAMIC_SYSTEM"/>
</privapp-permissions>
- <privapp-permissions package="com.android.server.wifistack">
+ <privapp-permissions package="com.android.wifi">
<permission name="android.permission.CHANGE_CONFIGURATION"/>
<permission name="android.permission.CONNECTIVITY_INTERNAL"/>
<permission name="android.permission.DUMP"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <permission name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
<permission name="android.permission.LOCAL_MAC_ADDRESS"/>
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.PACKAGE_USAGE_STATS"/>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
new file mode 100644
index 000000000000..012ffcc28e56
--- /dev/null
+++ b/data/etc/services.core.protolog.json
@@ -0,0 +1,2158 @@
+{
+ "version": "1.0.0",
+ "messages": {
+ "-2146181682": {
+ "message": "Releasing screen wakelock, obscured by %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_KEEP_SCREEN_ON",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-2138637148": {
+ "message": "Clearing focused app, displayId=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_FOCUS_LIGHT",
+ "at": "com\/android\/server\/wm\/ActivityDisplay.java"
+ },
+ "-2127842445": {
+ "message": "Clearing startingData for token=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-2109936758": {
+ "message": "removeAppToken make exiting: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-2109864870": {
+ "message": "app-release(): mOuter=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
+ "-2072089308": {
+ "message": "Attempted to add window with token that is a sub-window: %s. Aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-2039580386": {
+ "message": "Attempted to add input method window with unknown token %s. Aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-2024464438": {
+ "message": "app-onAnimationFinished(): mOuter=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
+ "-2012562539": {
+ "message": "startAnimation(): Notify animation start:",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
+ "-2002500255": {
+ "message": "Defer removing snapshot surface in %dms",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/TaskSnapshotSurface.java"
+ },
+ "-1991255017": {
+ "message": "Drawing snapshot surface sizeMismatch=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/TaskSnapshotSurface.java"
+ },
+ "-1976930686": {
+ "message": "Attempted to add Accessibility overlay window with bad token %s. Aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1963461591": {
+ "message": "Removing %s from %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1958209312": {
+ "message": "Clear freezing of %s: hidden=%b freezing=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-1953668890": {
+ "message": "Can't start recents animation, nextAppTransition=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimation.java"
+ },
+ "-1949279037": {
+ "message": "Attempted to add input method window with bad token %s. Aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1939358269": {
+ "message": "mRecentScreenshotAnimator finish",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimationController.java"
+ },
+ "-1938839202": {
+ "message": "SURFACE LEAK DESTROY: %s",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
+ "-1938204785": {
+ "message": "Moving existing starting %s from %s to %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-1915280162": {
+ "message": "Attempted to add wallpaper window with bad token %s. Aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1884933373": {
+ "message": "enableScreenAfterBoot: mDisplayEnabled=%b mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_BOOT",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1878839956": {
+ "message": "Marking app token %s with replacing windows.",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-1872288685": {
+ "message": "applyAnimation: anim=%s nextAppTransition=%s transit=%s isEntrance=%b Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
+ "at": "com\/android\/server\/wm\/AppTransition.java"
+ },
+ "-1868124841": {
+ "message": "screenOnEarly=%b, awake=%b, currentAppOrientation=%d, orientationSensorEnabled=%b, keyguardDrawComplete=%b, windowManagerDrawComplete=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayRotation.java"
+ },
+ "-1862269827": {
+ "message": "applyAnimation: anim=%s transit=%s isEntrance=%b Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
+ "at": "com\/android\/server\/wm\/AppTransition.java"
+ },
+ "-1847087163": {
+ "message": "TRANSIT_TASK_OPEN_BEHIND, adding %s to mOpeningApps",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-1838803135": {
+ "message": "Attempted to set windowing mode to a display that does not exist: %d",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1824578273": {
+ "message": "Reporting new frame to %s: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_RESIZE",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
+ "-1822611824": {
+ "message": "\tRemove token=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
+ "-1797409732": {
+ "message": "Skipping %s because %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_FOCUS",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
+ "-1782453012": {
+ "message": "Checking theme of starting window: 0x%x",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-1770075711": {
+ "message": "Adding window client %s that is dead, aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1768557332": {
+ "message": "removeWallpaperAnimation()",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimationController.java"
+ },
+ "-1750206390": {
+ "message": "Exception thrown when creating surface for client %s (%s). %s",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1747461042": {
+ "message": "set mOrientationChanging of %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
+ "-1741065110": {
+ "message": "No app is requesting an orientation, return %d for display id=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
+ "-1730156332": {
+ "message": "Display id=%d rotation changed to %d from %d, lastOrientation=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayRotation.java"
+ },
+ "-1715268616": {
+ "message": "Last window, removing starting window %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-1710206702": {
+ "message": "Display id=%d is frozen while keyguard locked, return %d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
+ "-1698815688": {
+ "message": "Resetting app token %s of replacing window marks.",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-1661704580": {
+ "message": "Attempted to set replacing window on non-existing app token %s",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1632122349": {
+ "message": "Changing surface while display frozen: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1596995693": {
+ "message": "startAnimation",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
+ "-1587841219": {
+ "message": "Focus moving from %s to %s displayId=%d",
+ "level": "INFO",
+ "group": "WM_DEBUG_FOCUS_LIGHT",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1568331821": {
+ "message": "Enabling listeners",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayRotation.java"
+ },
+ "-1545962566": {
+ "message": "View server did not start",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1539974875": {
+ "message": "removeAppToken: %s delayed=%b Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-1526645239": {
+ "message": "Timeout waiting for drawn: undrawn=%s",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1515151503": {
+ "message": ">>> OPEN TRANSACTION removeReplacedWindows",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/RootWindowContainer.java"
+ },
+ "-1499134947": {
+ "message": "Removing starting %s from %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-1497837552": {
+ "message": "onAnimationFinished(): mPendingAnimations=%d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
+ "-1483752006": {
+ "message": " THUMBNAIL %s: CREATE",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/AppWindowThumbnail.java"
+ },
+ "-1471946192": {
+ "message": "Marking app token %s with replacing child windows.",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-1456549051": {
+ "message": "setClientHidden: %s clientHidden=%b Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-1455600136": {
+ "message": "Attempted to add Dream window with unknown token %s. Aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1443029505": {
+ "message": "SAFE MODE ENABLED (menu=%d s=%d dpad=%d trackball=%d)",
+ "level": "INFO",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1434147454": {
+ "message": "cleanupAnimation(): Notify animation finished mPendingAnimations=%d reorderMode=%d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimationController.java"
+ },
+ "-1427184084": {
+ "message": "addWindow: New client %s: window=%s Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1413901262": {
+ "message": "startRecentsActivity(): intent=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimation.java"
+ },
+ "-1391944764": {
+ "message": "SURFACE DESTROY: %s. %s",
+ "level": "INFO",
+ "group": "WM_SHOW_SURFACE_ALLOC",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
+ "-1389772804": {
+ "message": "Attempted to add voice interaction window with bad token %s. Aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1352076759": {
+ "message": "Removing app token: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-1350198040": {
+ "message": "hideBootMessagesLocked: mDisplayEnabled=%b mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_BOOT",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1340540100": {
+ "message": "Creating SnapshotStartingData",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-1292329638": {
+ "message": "Added starting %s: startingWindow=%s startingView=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-1270731689": {
+ "message": "Attempted to set replacing window on app token with no content %s",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1270148832": {
+ "message": "Resize start waiting for draw, mDrawState=DRAW_PENDING in %s, surfaceController %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_RESIZE",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
+ "-1263554915": {
+ "message": "Attempted to add Dream window with bad token %s. Aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1259022216": {
+ "message": "SURFACE HIDE ( %s ): %s",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
+ },
+ "-1257821162": {
+ "message": "OUT SURFACE %s: copied",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1219773477": {
+ "message": "setInputConsumerEnabled(%s): mCanceled=%b",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimationController.java"
+ },
+ "-1207757583": {
+ "message": "startAnimation(): Notify animation start: %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimationController.java"
+ },
+ "-1176488860": {
+ "message": "SURFACE isSecure=%b: %s",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
+ },
+ "-1156118957": {
+ "message": "Updated config=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimation.java"
+ },
+ "-1144293044": {
+ "message": "SURFACE SET FREEZE LAYER: %s",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
+ "-1130891072": {
+ "message": "Orientation continue waiting for draw in %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
+ "-1130868271": {
+ "message": "Resizing %s WITH DRAW PENDING",
+ "level": "INFO",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
+ "-1128015008": {
+ "message": "Schedule remove starting %s startingWindow=%s startingView=%s Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-1117599386": {
+ "message": "Deferring rotation, display is not enabled.",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayRotation.java"
+ },
+ "-1113134997": {
+ "message": "Attempted to add application window with unknown token %s. Aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1103716954": {
+ "message": "Not removing %s due to exit animation",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
+ "-1103115659": {
+ "message": "Performing post-rotate rotation",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/RootWindowContainer.java"
+ },
+ "-1099052739": {
+ "message": "\tAdd token=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
+ "-1089874824": {
+ "message": "SURFACE SHOW (performLayout): %s",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
+ },
+ "-1088782910": {
+ "message": "Translucent=%s Floating=%s ShowWallpaper=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-1076978367": {
+ "message": "thawRotation: mRotation=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1060365734": {
+ "message": "Attempted to add QS dialog window with bad token %s. Aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1047945589": {
+ "message": "Remove client=%x, surfaceController=%s Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_FOCUS",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
+ "-1044506655": {
+ "message": "New transit away from wallpaper: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransitionController.java"
+ },
+ "-1042574499": {
+ "message": "Attempted to add Accessibility overlay window with unknown token %s. Aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-1009117329": {
+ "message": "isFetchingAppTransitionSpecs=true",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransitionController.java"
+ },
+ "-993378225": {
+ "message": "finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING %s in %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_DRAW",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
+ "-986746907": {
+ "message": "Starting window removed %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
+ "-931184679": {
+ "message": "Changing app %s hidden=%b performLayout=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-928291778": {
+ "message": "applyAnimation: anim=%s nextAppTransition=%d transit=%s Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
+ "at": "com\/android\/server\/wm\/AppTransition.java"
+ },
+ "-916108501": {
+ "message": "Adding %s to %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
+ "-914253865": {
+ "message": "Attempted to add voice interaction window with unknown token %s. Aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-883738232": {
+ "message": "Adding more than one toast window for UID at a time.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-874446906": {
+ "message": "showBootMessage: msg=%s always=%b mAllowBootMessages=%b mShowingBootMessages=%b mSystemBooted=%b. %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_BOOT",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-861859917": {
+ "message": "Attempted to add window to a display that does not exist: %d. Aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-861707633": {
+ "message": "Destroying surface %s called by %s",
+ "level": "INFO",
+ "group": "WM_SHOW_SURFACE_ALLOC",
+ "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
+ },
+ "-856025122": {
+ "message": "SURFACE transparentRegionHint=%s: %s",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-853404763": {
+ "message": "\twallpaper=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
+ "-853226675": {
+ "message": "Attempted to add window with exiting application token .%s Aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-809771899": {
+ "message": "findFocusedWindow: Reached focused app=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_FOCUS_LIGHT",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
+ "-793346159": {
+ "message": "New transit into wallpaper: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransitionController.java"
+ },
+ "-784959154": {
+ "message": "Attempted to add private presentation window to a non-private display. Aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-783405930": {
+ "message": "Performing post-rotate rotation",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-771282525": {
+ "message": "Losing focus: %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_FOCUS_LIGHT",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-771177730": {
+ "message": "Removing focused app token:%s displayId=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_FOCUS_LIGHT",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-760801764": {
+ "message": "onAnimationCancelled",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java"
+ },
+ "-754503024": {
+ "message": "Relayout %s: oldVis=%d newVis=%d. %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_SCREEN_ON",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-747671114": {
+ "message": "Failed looking up window callers=%s",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-714291355": {
+ "message": "Losing delayed focus: %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_FOCUS_LIGHT",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-694710814": {
+ "message": "Pausing rotation during drag",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DragState.java"
+ },
+ "-687185281": {
+ "message": "New topFocusedDisplayId=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_FOCUS_LIGHT",
+ "at": "com\/android\/server\/wm\/RootWindowContainer.java"
+ },
+ "-666510420": {
+ "message": "With display frozen, orientationChangeComplete=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/RootWindowContainer.java"
+ },
+ "-653156702": {
+ "message": "createAppAnimations()",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
+ "-650040763": {
+ "message": "rotationForOrientation(orient=%d, last=%d); user=%d %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayRotation.java"
+ },
+ "-635082269": {
+ "message": "******** booted=%b msg=%b haveBoot=%b haveApp=%b haveWall=%b wallEnabled=%b haveKeyguard=%b",
+ "level": "INFO",
+ "group": "WM_DEBUG_SCREEN_ON",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
+ "-622997754": {
+ "message": "postWindowRemoveCleanupLocked: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-618015844": {
+ "message": "performEnableScreen: mDisplayEnabled=%b mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b mOnlyCore=%b. %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_BOOT",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-583031528": {
+ "message": "%s",
+ "level": "INFO",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-576070986": {
+ "message": "Performing post-rotate rotation after seamless rotation",
+ "level": "INFO",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayRotation.java"
+ },
+ "-554834595": {
+ "message": "Display id=%d is frozen, return %d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
+ "-549028919": {
+ "message": "enableScreenIfNeededLocked: mDisplayEnabled=%b mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_BOOT",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-545190927": {
+ "message": "<<< CLOSE TRANSACTION animate",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowAnimator.java"
+ },
+ "-519504830": {
+ "message": "applyAnimation: anim=%s nextAppTransition=ANIM_CUSTOM transit=%s isEntrance=%b Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
+ "at": "com\/android\/server\/wm\/AppTransition.java"
+ },
+ "-507657818": {
+ "message": "Window %s is already added",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-496681057": {
+ "message": "Attempted to get remove mode of a display that does not exist: %d",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-481924678": {
+ "message": "handleNotObscuredLocked w: %s, w.mHasSurface: %b, w.isOnScreen(): %b, w.isDisplayedLw(): %b, w.mAttrs.userActivityTimeout: %d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_KEEP_SCREEN_ON",
+ "at": "com\/android\/server\/wm\/RootWindowContainer.java"
+ },
+ "-477481651": {
+ "message": "SURFACE DESTROY PENDING: %s. %s",
+ "level": "INFO",
+ "group": "WM_SHOW_SURFACE_ALLOC",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
+ "-445944810": {
+ "message": "finish(%b): mCanceled=%b",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimationController.java"
+ },
+ "-444624452": {
+ "message": "REPARENT from: %s to: %s",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
+ },
+ "-443173857": {
+ "message": "Moving pending starting from %s to %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-439951996": {
+ "message": "Disabling listeners",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayRotation.java"
+ },
+ "-415912575": {
+ "message": "setTask: %s at top.",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-415865166": {
+ "message": "findFocusedWindow: Found new focus @ %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_FOCUS_LIGHT",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
+ "-405536909": {
+ "message": "Removing snapshot surface",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/TaskSnapshotSurface.java"
+ },
+ "-393505149": {
+ "message": "unable to update pointer icon",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-386552155": {
+ "message": "Attempted to set system decors flag to a display that does not exist: %d",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-379068494": {
+ "message": "unknownApps is not empty: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransitionController.java"
+ },
+ "-371630969": {
+ "message": "New wallpaper target=%s, oldWallpaper=%s, openingApps=%s, closingApps=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransitionController.java"
+ },
+ "-344488673": {
+ "message": "Finishing drawing window %s: mDrawState=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
+ "-324085783": {
+ "message": "SURFACE CROP %s: %s",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
+ },
+ "-322035974": {
+ "message": "App freeze timeout expired.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-320419645": {
+ "message": "Removing replaced window: %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
+ "-253016819": {
+ "message": "applyAnimation: transition animation is disabled or skipped. atoken=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-251259736": {
+ "message": "No longer freezing: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "-198463978": {
+ "message": "updateRotationUnchecked: alwaysSendConfiguration=%b forceRelayout=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-193782861": {
+ "message": "Final remove of window: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_MOVEMENT",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-167822951": {
+ "message": "Attempted to add starting window to token with already existing starting window",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-129722369": {
+ "message": "New transit: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransitionController.java"
+ },
+ "-121104356": {
+ "message": "Remove %s: mSurfaceController=%s mAnimatingExit=%b mRemoveOnExit=%b mHasSurface=%b surfaceShowing=%b animating=%b app-animation=%b mWillReplaceWindow=%b inPendingTransaction=%b mDisplayFrozen=%b callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
+ "-116086365": {
+ "message": "******************** ENABLING SCREEN!",
+ "level": "INFO",
+ "group": "WM_DEBUG_SCREEN_ON",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-106400104": {
+ "message": "Preload recents with %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimation.java"
+ },
+ "-96848838": {
+ "message": "Gaining focus: %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_FOCUS_LIGHT",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-87705714": {
+ "message": "findFocusedWindow: focusedApp=null using new focus @ %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_FOCUS_LIGHT",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
+ "-87703044": {
+ "message": "Boot completed: SurfaceFlinger is dead!",
+ "level": "ERROR",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "-86763148": {
+ "message": " KILL SURFACE SESSION %s",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/Session.java"
+ },
+ "-29233992": {
+ "message": "SURFACE CLEAR CROP: %s",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
+ },
+ "-7343917": {
+ "message": "onAnimationFinished(): targetStack=%s targetActivity=%s mRestoreTargetBehindStack=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimation.java"
+ },
+ "9803449": {
+ "message": "startFreezingDisplayLocked: exitAnim=%d enterAnim=%d called by %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "10608884": {
+ "message": " FREEZE %s: CREATE",
+ "level": "INFO",
+ "group": "WM_SHOW_SURFACE_ALLOC",
+ "at": "com\/android\/server\/wm\/ScreenRotationAnimation.java"
+ },
+ "11060725": {
+ "message": "Attempted to get system decors flag of a display that does not exist: %d",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "17696244": {
+ "message": "startAnimation(): mPendingStart=%b mCanceled=%b",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimationController.java"
+ },
+ "38267433": {
+ "message": "Attempted to reset replacing window on non-existing app token %s",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "44438983": {
+ "message": "performLayout: Activity exiting now removed %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
+ "45285419": {
+ "message": "startingWindow was set but startingSurface==null, couldn't remove",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "51200510": {
+ "message": " BLACK %s: DESTROY",
+ "level": "INFO",
+ "group": "WM_SHOW_SURFACE_ALLOC",
+ "at": "com\/android\/server\/wm\/BlackFrame.java"
+ },
+ "51628177": {
+ "message": "Attempted to get windowing mode of a display that does not exist: %d",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "83950285": {
+ "message": "removeAnimation(%d)",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimationController.java"
+ },
+ "91350919": {
+ "message": "Attempted to set IME flag to a display that does not exist: %d",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "95281111": {
+ "message": "Attempted to get IME flag of a display that does not exist: %d",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "95902367": {
+ "message": "Relayout of %s: focusMayChange=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_FOCUS",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "100936473": {
+ "message": "Wallpaper animation!",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransitionController.java"
+ },
+ "108170907": {
+ "message": "Add starting %s: startingData=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "123161180": {
+ "message": "SEVER CHILDREN",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
+ },
+ "146871307": {
+ "message": "Tried to remove starting window but startingWindow was null: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "150351993": {
+ "message": "addWindow: %s startingWindow=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "152914409": {
+ "message": " BLACK %s: CREATE layer=%d",
+ "level": "INFO",
+ "group": "WM_SHOW_SURFACE_ALLOC",
+ "at": "com\/android\/server\/wm\/BlackFrame.java"
+ },
+ "184362060": {
+ "message": "screenshotTask(%d): mCanceled=%b",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimationController.java"
+ },
+ "186668272": {
+ "message": "Now changing app %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransitionController.java"
+ },
+ "194124419": {
+ "message": "goodToGo(): Animation finished already, canceled=%s mPendingAnimations=%d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
+ "221540118": {
+ "message": "mUserActivityTimeout set to %d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_KEEP_SCREEN_ON",
+ "at": "com\/android\/server\/wm\/RootWindowContainer.java"
+ },
+ "241961619": {
+ "message": "Adding %s to %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/WindowToken.java"
+ },
+ "246676969": {
+ "message": "Attempted to add window with non-application token .%s Aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "248210157": {
+ "message": "Finishing remote animation",
+ "level": "INFO",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
+ "254883724": {
+ "message": "addWindowToken: Attempted to add binder token: %s for already created window token: %s displayId=%d",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "255692476": {
+ "message": "**** GOOD TO GO",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransitionController.java"
+ },
+ "269576220": {
+ "message": "Resuming rotation after drag",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DragState.java"
+ },
+ "274773837": {
+ "message": "applyAnimation: anim=%s nextAppTransition=ANIM_CLIP_REVEAL transit=%s Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
+ "at": "com\/android\/server\/wm\/AppTransition.java"
+ },
+ "285317231": {
+ "message": "Input focus has changed to %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_FOCUS_LIGHT",
+ "at": "com\/android\/server\/wm\/InputMonitor.java"
+ },
+ "288485303": {
+ "message": "Attempted to set remove mode to a display that does not exist: %d",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "289967521": {
+ "message": "Check opening app=%s: allDrawn=%b startingDisplayed=%b startingMoved=%b isRelaunching()=%b startingWindow=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransitionController.java"
+ },
+ "292904800": {
+ "message": "Deferring rotation, animation in progress.",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayRotation.java"
+ },
+ "302992539": {
+ "message": "addAnimation(%s)",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimationController.java"
+ },
+ "309039362": {
+ "message": "SURFACE MATRIX [%f,%f,%f,%f]: %s",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
+ },
+ "342460966": {
+ "message": "DRAG %s: pos=(%d,%d)",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/DragState.java"
+ },
+ "344795667": {
+ "message": "*** APP TRANSITION TIMEOUT. displayId=%d isTransitionSet()=%b mOpeningApps.size()=%d mClosingApps.size()=%d mChangingApps.size()=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransition.java"
+ },
+ "355720268": {
+ "message": "stopFreezingDisplayLocked: Unfreezing now",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "358613119": {
+ "message": "setAppVisibility(%s, visible=%b): %s hidden=%b hiddenRequested=%b Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "371641947": {
+ "message": "Window Manager Crash %s",
+ "level": "WTF",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "372792199": {
+ "message": "Non-null activity for system window of rootType=%d",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "374972436": {
+ "message": "performEnableScreen: Waiting for anim complete",
+ "level": "INFO",
+ "group": "WM_DEBUG_BOOT",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "385096046": {
+ "message": "Delaying loss of focus...",
+ "level": "INFO",
+ "group": "WM_DEBUG_FOCUS_LIGHT",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "393054329": {
+ "message": "reParentWindowToken: removing window token=%s from task=%s",
+ "level": "INFO",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "399841913": {
+ "message": "SURFACE RECOVER DESTROY: %s",
+ "level": "INFO",
+ "group": "WM_SHOW_SURFACE_ALLOC",
+ "at": "com\/android\/server\/wm\/RootWindowContainer.java"
+ },
+ "417311568": {
+ "message": "onResize: Resizing %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RESIZE",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
+ "424524729": {
+ "message": "Attempted to add wallpaper window with unknown token %s. Aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "435494046": {
+ "message": "Attempted to add window to a display for which the application does not have access: %d. Aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "457951957": {
+ "message": "\tNot visible=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java"
+ },
+ "463993897": {
+ "message": "Aborted waiting for drawn: %s",
+ "level": "WARN",
+ "group": "WM_DEBUG_SCREEN_ON",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "474000473": {
+ "message": "No stack above target stack=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimation.java"
+ },
+ "481370485": {
+ "message": "Computed rotation=%d for display id=%d based on lastOrientation=%d and oldRotation=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayRotation.java"
+ },
+ "490877640": {
+ "message": "onStackOrderChanged(): stack=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimation.java"
+ },
+ "495032901": {
+ "message": "Expected target stack=%s to restored behind stack=%s but it is behind stack=%s",
+ "level": "WARN",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimation.java"
+ },
+ "508887531": {
+ "message": "applyAnimation voice: anim=%s transit=%s isEntrance=%b Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
+ "at": "com\/android\/server\/wm\/AppTransition.java"
+ },
+ "539077569": {
+ "message": "Clear freezing of %s force=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "557227556": {
+ "message": "onAnimationFinished(): Notify animation finished:",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
+ "558823034": {
+ "message": "SURFACE isOpaque=%b: %s",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
+ },
+ "585096182": {
+ "message": "SURFACE isColorSpaceAgnostic=%b: %s",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
+ },
+ "594260577": {
+ "message": "createWallpaperAnimations()",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
+ "600140673": {
+ "message": "checkBootAnimationComplete: Waiting for anim complete",
+ "level": "INFO",
+ "group": "WM_DEBUG_BOOT",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "608694300": {
+ "message": " NEW SURFACE SESSION %s",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/Session.java"
+ },
+ "620368427": {
+ "message": "******* TELLING SURFACE FLINGER WE ARE BOOTED!",
+ "level": "INFO",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "628276090": {
+ "message": "Delaying app transition for screen rotation animation to finish",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransitionController.java"
+ },
+ "631792420": {
+ "message": "Attempted to add window with token that is not a window: %s. Aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "633654009": {
+ "message": "SURFACE POS (setPositionInTransaction) @ (%f,%f): %s",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
+ },
+ "644675193": {
+ "message": "Real start recents",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimation.java"
+ },
+ "646155519": {
+ "message": "Started intent=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimation.java"
+ },
+ "662572728": {
+ "message": "Attempted to add a toast window with bad token %s. Aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "665256544": {
+ "message": "All windows drawn!",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_SCREEN_ON",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "676824470": {
+ "message": "Test completed successfully: %b %d %o %x %e %g %f %% %s.",
+ "level": "ERROR",
+ "group": "TEST_GROUP",
+ "at": "com\/android\/server\/wm\/ProtoLogGroup.java"
+ },
+ "685047360": {
+ "message": "Resizing window %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_RESIZE",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
+ "690411811": {
+ "message": "goodToGo(): No apps to animate",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
+ "693423992": {
+ "message": "setAnimationLocked: setting mFocusMayChange true",
+ "level": "INFO",
+ "group": "WM_DEBUG_FOCUS_LIGHT",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
+ "704998117": {
+ "message": "Failed to create surface control for %s",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "745391677": {
+ "message": " CREATE SURFACE %s IN SESSION %s: pid=%d format=%d flags=0x%x \/ %s",
+ "level": "INFO",
+ "group": "WM_SHOW_SURFACE_ALLOC",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
+ "765395228": {
+ "message": "onAnimationFinished(): controller=%s reorderMode=%d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimation.java"
+ },
+ "789829331": {
+ "message": "Aborted starting %s: removed=%b startingData=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "791468751": {
+ "message": "Pausing rotation during re-position",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/TaskPositioner.java"
+ },
+ "794570322": {
+ "message": "Now closing app %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransitionController.java"
+ },
+ "806891543": {
+ "message": "Setting mOrientationChangeComplete=true because wtoken %s numInteresting=%d numDrawn=%d",
+ "level": "INFO",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "815803557": {
+ "message": "applyAnimation: atoken=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "829434921": {
+ "message": "Draw state now committed in %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
+ "835814848": {
+ "message": "%s",
+ "level": "INFO",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
+ "845234215": {
+ "message": "App is requesting an orientation, return %d for display id=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
+ "853091290": {
+ "message": "Moved stack=%s behind stack=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimation.java"
+ },
+ "857751535": {
+ "message": "commitVisibility: %s: hidden=%b hiddenRequested=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "873914452": {
+ "message": "goodToGo()",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
+ "892244061": {
+ "message": "Waiting for drawn %s: removed=%b visible=%b mHasSurface=%b drawState=%d",
+ "level": "INFO",
+ "group": "WM_DEBUG_SCREEN_ON",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "898863925": {
+ "message": "Attempted to add QS dialog window with unknown token %s. Aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "913494177": {
+ "message": "removeAllWindowsIfPossible: removing win=%s",
+ "level": "WARN",
+ "group": "WM_DEBUG_WINDOW_MOVEMENT",
+ "at": "com\/android\/server\/wm\/WindowToken.java"
+ },
+ "916191774": {
+ "message": "Orientation change complete in %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
+ "917739349": {
+ "message": "Set focused app to: %s moveFocusNow=%b displayId=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_FOCUS_LIGHT",
+ "at": "com\/android\/server\/wm\/ActivityDisplay.java"
+ },
+ "954470154": {
+ "message": "FORCED DISPLAY SCALING DISABLED",
+ "level": "INFO",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "1001904964": {
+ "message": "***** BOOT TIMEOUT: forcing display enabled",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "1004585481": {
+ "message": "%s forcing orientation to %d for display id=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
+ "1051545910": {
+ "message": "Exit animation finished in %s: remove=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
+ "1073230342": {
+ "message": "startAnimation",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java"
+ },
+ "1089714158": {
+ "message": " FREEZE %s: DESTROY",
+ "level": "INFO",
+ "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",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "1115417974": {
+ "message": "FORCED DISPLAY SIZE: %dx%d",
+ "level": "INFO",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "1140424002": {
+ "message": "Finished screen turning on...",
+ "level": "INFO",
+ "group": "WM_DEBUG_SCREEN_ON",
+ "at": "com\/android\/server\/wm\/DisplayPolicy.java"
+ },
+ "1160771501": {
+ "message": "Resize reasons for w=%s: %s surfaceResized=%b configChanged=%b dragResizingChanged=%b reportOrientationChanged=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_RESIZE",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
+ "1166381079": {
+ "message": "Execute app transition: %s, displayId: %d Callers=%s",
+ "level": "WARN",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
+ "1208313423": {
+ "message": "addWindowToken: Attempted to add token: %s for non-exiting displayId=%d",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "1219600119": {
+ "message": "addWindow: win=%s Callers=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_FOCUS",
+ "at": "com\/android\/server\/wm\/WindowToken.java"
+ },
+ "1220075598": {
+ "message": "SURFACE SIZE %dx%d: %s",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
+ },
+ "1224184681": {
+ "message": "No longer Stopped: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "1224307091": {
+ "message": "checkBootAnimationComplete: Animation complete!",
+ "level": "INFO",
+ "group": "WM_DEBUG_BOOT",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "1254403969": {
+ "message": "Surface returned was null: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "1288731814": {
+ "message": "WindowState.hideLw: setting mFocusMayChange true",
+ "level": "INFO",
+ "group": "WM_DEBUG_FOCUS_LIGHT",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
+ "1325649102": {
+ "message": "Bad requesting window %s",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "1329340614": {
+ "message": "Orientation not waiting for draw in %s, surfaceController %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
+ "1331177619": {
+ "message": "Attempted to add a toast window with unknown token %s. Aborting.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "1358462645": {
+ "message": "Looking for focus: %s, flags=%d, canReceive=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_FOCUS",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
+ "1358786604": {
+ "message": "No thumbnail header bitmap for: %d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "1364498663": {
+ "message": "notifyAppResumed: wasStopped=%b %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "1401700824": {
+ "message": "Window drawn win=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_SCREEN_ON",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "1417601133": {
+ "message": "Enqueueing ADD_STARTING",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "1422781269": {
+ "message": "Resuming rotation after re-position",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/TaskPositioner.java"
+ },
+ "1423418408": {
+ "message": "unable to restore pointer icon",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "1423592961": {
+ "message": "<<< CLOSE TRANSACTION removeReplacedWindows",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/RootWindowContainer.java"
+ },
+ "1430336882": {
+ "message": "findFocusedWindow: focusedApp windows not focusable using new focus @ %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_FOCUS_LIGHT",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
+ "1434383382": {
+ "message": "Attempted to get flag of a display that does not exist: %d",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "1448683958": {
+ "message": "Override pending remote transitionSet=%b adapter=%s",
+ "level": "INFO",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransition.java"
+ },
+ "1457990604": {
+ "message": "applyAnimation: anim=%s nextAppTransition=ANIM_CUSTOM_IN_PLACE transit=%s Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
+ "at": "com\/android\/server\/wm\/AppTransition.java"
+ },
+ "1469292670": {
+ "message": "Changing focus from %s to %s displayId=%d Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_FOCUS_LIGHT",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
+ "1495525537": {
+ "message": "createWallpaperAnimations()",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimationController.java"
+ },
+ "1497304204": {
+ "message": "Deferring rotation, rotation is paused.",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayRotation.java"
+ },
+ "1504168072": {
+ "message": "removeIfPossible: %s callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
+ "1515161239": {
+ "message": "removeDeadWindows: %s",
+ "level": "WARN",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "1518495446": {
+ "message": "removeWindowToken: Attempted to remove non-existing token: %s",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "1519757176": {
+ "message": "setHomeApp(%s)",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimationController.java"
+ },
+ "1521476038": {
+ "message": "Attempted to set flag to a display that does not exist: %d",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "1525976603": {
+ "message": "cancelAnimation(): reason=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimationController.java"
+ },
+ "1531527061": {
+ "message": "createAnimationAdapter(): token=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
+ "1563755163": {
+ "message": "Permission Denial: %s from pid=%d, uid=%d requires %s",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "1573332272": {
+ "message": "Display id=%d selected orientation %d, got rotation %d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayRotation.java"
+ },
+ "1577579529": {
+ "message": "win=%s destroySurfaces: appStopped=%b win.mWindowRemovalAllowed=%b win.mRemoveOnExit=%b",
+ "level": "ERROR",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
+ "1589610525": {
+ "message": "applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS: anim=%s transit=%s isEntrance=true Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
+ "at": "com\/android\/server\/wm\/AppTransition.java"
+ },
+ "1628345525": {
+ "message": "Now opening app %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransitionController.java"
+ },
+ "1634557978": {
+ "message": "**** Dismissing screen rotation animation",
+ "level": "INFO",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "1635462459": {
+ "message": "onMovedByResize: Moving %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RESIZE",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
+ "1653210583": {
+ "message": "Removing app %s delayed=%b animation=%s animating=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "1671994402": {
+ "message": "Nulling last startingData",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "1677260366": {
+ "message": "Finish starting %s: first real window is shown, no animation",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "1720229827": {
+ "message": "Creating animation bounds layer",
+ "level": "INFO",
+ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "1720696061": {
+ "message": "Adding window to Display that has been removed.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "1739298851": {
+ "message": "removeWindowToken: Attempted to remove token: %s for non-exiting displayId=%d",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "1742235936": {
+ "message": "Removing startingView=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "1747941491": {
+ "message": "SURFACE controller=%s alpha=%f matrix=[%f*%f,%f*%f][%f*%f,%f*%f]: %s",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
+ "1756082882": {
+ "message": "Orientation change skips hidden %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
+ },
+ "1762317752": {
+ "message": "Expected target stack=%s to be top most but found stack=%s",
+ "level": "WARN",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RecentsAnimation.java"
+ },
+ "1774661765": {
+ "message": "Devices still not ready after waiting %d milliseconds before attempting to detect safe mode.",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "1804869745": {
+ "message": "Loading animation for app transition. transit=%s enter=%b frame=%s insets=%s surfaceInsets=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "1836214582": {
+ "message": "startingData was nulled out before handling mAddStartingWindow: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "1836306327": {
+ "message": "Skipping set freeze of %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "1853793312": {
+ "message": "Notify removed startingWindow %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "1865125884": {
+ "message": "finishScreenTurningOn: mAwake=%b, mScreenOnEarly=%b, mScreenOnFully=%b, mKeyguardDrawComplete=%b, mWindowManagerDrawComplete=%b",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_SCREEN_ON",
+ "at": "com\/android\/server\/wm\/DisplayPolicy.java"
+ },
+ "1865246212": {
+ "message": "\tapp=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
+ "1866772666": {
+ "message": "SAFE MODE not enabled",
+ "level": "INFO",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "1891501279": {
+ "message": "cancelAnimation(): reason=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
+ "1903353011": {
+ "message": "notifyAppStopped: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "1921821199": {
+ "message": "Preserving %s until the new one is added",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
+ "1947239194": {
+ "message": "Deferring rotation, still finishing previous rotation",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayRotation.java"
+ },
+ "1964565370": {
+ "message": "Starting remote animation",
+ "level": "INFO",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
+ },
+ "1966564525": {
+ "message": "Set freezing of %s: hidden=%b freezing=%b hiddenRequested=%b. %s",
+ "level": "INFO",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "1984470582": {
+ "message": "Creating TaskScreenshotAnimatable: task: %s width: %d height: %d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_RECENTS_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/TaskScreenshotAnimatable.java"
+ },
+ "1984782949": {
+ "message": ">>> OPEN TRANSACTION animate",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowAnimator.java"
+ },
+ "1995048598": {
+ "message": "reparent: moving app token=%s to task=%d at %d",
+ "level": "INFO",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "2016061474": {
+ "message": "Prepare app transition: transit=%s %s alwaysKeepCurrent=%b displayId=%d Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransition.java"
+ },
+ "2018454757": {
+ "message": "WS.removeImmediately: %s Already removed...",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
+ "2018852077": {
+ "message": "Creating SplashScreenStartingData",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STARTING_WINDOW",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
+ "2028163120": {
+ "message": "applyAnimation: anim=%s nextAppTransition=ANIM_SCALE_UP transit=%s isEntrance=%s Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
+ "at": "com\/android\/server\/wm\/AppTransition.java"
+ },
+ "2034780299": {
+ "message": "CHECK_IF_BOOT_ANIMATION_FINISHED:",
+ "level": "INFO",
+ "group": "WM_DEBUG_BOOT",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "2045641491": {
+ "message": "Checking %d opening apps (frozen=%b timeout=%b)...",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransitionController.java"
+ },
+ "2057434754": {
+ "message": "\tvisible=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java"
+ },
+ "2083556954": {
+ "message": "Set mOrientationChanging of %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
+ "2086878461": {
+ "message": "Could not send command %s with parameters %s. %s",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "2088592090": {
+ "message": "handleNotObscuredLocked: %s was holding screen wakelock but no longer has FLAG_KEEP_SCREEN_ON!!! called by%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_KEEP_SCREEN_ON",
+ "at": "com\/android\/server\/wm\/RootWindowContainer.java"
+ },
+ "2096635066": {
+ "message": "Acquiring screen wakelock due to %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_KEEP_SCREEN_ON",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
+ "2114149926": {
+ "message": "Not removing %s because app died while it's visible",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
+ "2128604122": {
+ "message": "findFocusedWindow: No focusable windows.",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_FOCUS_LIGHT",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
+ "2128917433": {
+ "message": "onProposedRotationChanged, rotation=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayRotation.java"
+ },
+ "2137411379": {
+ "message": "applyAnimation: anim=%s animAttr=0x%x transit=%s isEntrance=%b Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
+ "at": "com\/android\/server\/wm\/AppTransition.java"
+ }
+ },
+ "groups": {
+ "TEST_GROUP": {
+ "tag": "WindowManagetProtoLogTest"
+ },
+ "WM_DEBUG_ADD_REMOVE": {
+ "tag": "WindowManager"
+ },
+ "WM_DEBUG_APP_TRANSITIONS": {
+ "tag": "WindowManager"
+ },
+ "WM_DEBUG_APP_TRANSITIONS_ANIM": {
+ "tag": "WindowManager"
+ },
+ "WM_DEBUG_BOOT": {
+ "tag": "WindowManager"
+ },
+ "WM_DEBUG_DRAW": {
+ "tag": "WindowManager"
+ },
+ "WM_DEBUG_FOCUS": {
+ "tag": "WindowManager"
+ },
+ "WM_DEBUG_FOCUS_LIGHT": {
+ "tag": "WindowManager"
+ },
+ "WM_DEBUG_KEEP_SCREEN_ON": {
+ "tag": "WindowManager"
+ },
+ "WM_DEBUG_ORIENTATION": {
+ "tag": "WindowManager"
+ },
+ "WM_DEBUG_RECENTS_ANIMATIONS": {
+ "tag": "WindowManager"
+ },
+ "WM_DEBUG_REMOTE_ANIMATIONS": {
+ "tag": "WindowManager"
+ },
+ "WM_DEBUG_RESIZE": {
+ "tag": "WindowManager"
+ },
+ "WM_DEBUG_SCREEN_ON": {
+ "tag": "WindowManager"
+ },
+ "WM_DEBUG_STARTING_WINDOW": {
+ "tag": "WindowManager"
+ },
+ "WM_DEBUG_WINDOW_MOVEMENT": {
+ "tag": "WindowManager"
+ },
+ "WM_ERROR": {
+ "tag": "WindowManager"
+ },
+ "WM_SHOW_SURFACE_ALLOC": {
+ "tag": "WindowManager"
+ },
+ "WM_SHOW_TRANSACTIONS": {
+ "tag": "WindowManager"
+ }
+ }
+}
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index 454dceb9c82c..4226e0882538 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -83,8 +83,8 @@ font_src_files :=
################################
# Copies the font configuration file into system/etc for the product as fonts.xml.
-# In the case where $(ADDITIONAL_FONTS_FILE) is defined, the content of $(ADDITIONAL_FONTS_FILE)
-# is added to the $(AOSP_FONTS_FILE).
+# Additional fonts should be installed to /product/fonts/ alongside a corresponding
+# fonts_customiztion.xml in /product/etc/
include $(CLEAR_VARS)
LOCAL_MODULE := fonts.xml
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 072beae8baf7..c6920977f6b9 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -323,14 +323,16 @@
<font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font>
</family>
<family lang="und-Mymr" variant="elegant">
- <font weight="400" style="normal">NotoSansMyanmar-Regular-ZawDecode.ttf</font>
- <font weight="700" style="normal">NotoSansMyanmar-Bold-ZawDecode.ttf</font>
+ <font weight="400" style="normal">NotoSansMyanmar-Regular.otf</font>
+ <font weight="500" style="normal">NotoSansMyanmar-Medium.otf</font>
+ <font weight="700" style="normal">NotoSansMyanmar-Bold.otf</font>
<font weight="400" style="normal" fallbackFor="serif">NotoSerifMyanmar-Regular.otf</font>
<font weight="700" style="normal" fallbackFor="serif">NotoSerifMyanmar-Bold.otf</font>
</family>
<family lang="und-Mymr" variant="compact">
- <font weight="400" style="normal">NotoSansMyanmarUI-Regular-ZawDecode.ttf</font>
- <font weight="700" style="normal">NotoSansMyanmarUI-Bold-ZawDecode.ttf</font>
+ <font weight="400" style="normal">NotoSansMyanmarUI-Regular.otf</font>
+ <font weight="500" style="normal">NotoSansMyanmarUI-Medium.otf</font>
+ <font weight="700" style="normal">NotoSansMyanmarUI-Bold.otf</font>
</family>
<family lang="und-Thaa">
<font weight="400" style="normal">NotoSansThaana-Regular.ttf</font>
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
new file mode 100644
index 000000000000..8c6a9371d53b
--- /dev/null
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.view.Surface;
+import android.view.SurfaceControl;
+
+/**
+ * @hide
+ */
+public final class BLASTBufferQueue {
+ // Note: This field is accessed by native code.
+ private long mNativeObject; // BLASTBufferQueue*
+
+ private static native long nativeCreate(long surfaceControl, long width, long height);
+ private static native void nativeDestroy(long ptr);
+ private static native Surface nativeGetSurface(long ptr);
+ private static native void nativeSetNextTransaction(long ptr, long transactionPtr);
+ private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height);
+
+ /** Create a new connection with the surface flinger. */
+ public BLASTBufferQueue(SurfaceControl sc, int width, int height) {
+ mNativeObject = nativeCreate(sc.mNativeObject, width, height);
+ }
+
+ public void destroy() {
+ nativeDestroy(mNativeObject);
+ }
+
+ public Surface getSurface() {
+ return nativeGetSurface(mNativeObject);
+ }
+
+ public void setNextTransaction(SurfaceControl.Transaction t) {
+ nativeSetNextTransaction(mNativeObject, t.mNativeObject);
+ }
+
+ public void update(SurfaceControl sc, int width, int height) {
+ nativeUpdate(mNativeObject, sc.mNativeObject, width, height);
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (mNativeObject != 0) {
+ nativeDestroy(mNativeObject);
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+}
+
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 44710178da5e..d900a42b1e66 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -1359,9 +1359,44 @@ public final class Bitmap implements Parcelable {
* Specifies the known formats a bitmap can be compressed into
*/
public enum CompressFormat {
- JPEG (0),
- PNG (1),
- WEBP (2);
+ /**
+ * Compress to the JPEG format. {@code quality} of {@code 0} means
+ * compress for the smallest size. {@code 100} means compress for max
+ * visual quality.
+ */
+ JPEG (0),
+ /**
+ * Compress to the PNG format. PNG is lossless, so {@code quality} is
+ * ignored.
+ */
+ PNG (1),
+ /**
+ * Compress to the WEBP format. {@code quality} of {@code 0} means
+ * compress for the smallest size. {@code 100} means compress for max
+ * visual quality. As of {@link android.os.Build.VERSION_CODES#Q}, a
+ * value of {@code 100} results in a file in the lossless WEBP format.
+ * Otherwise the file will be in the lossy WEBP format.
+ *
+ * @deprecated in favor of the more explicit
+ * {@link CompressFormat#WEBP_LOSSY} and
+ * {@link CompressFormat#WEBP_LOSSLESS}.
+ */
+ @Deprecated
+ WEBP (2),
+ /**
+ * Compress to the WEBP lossy format. {@code quality} of {@code 0} means
+ * compress for the smallest size. {@code 100} means compress for max
+ * visual quality.
+ */
+ WEBP_LOSSY (3),
+ /**
+ * Compress to the WEBP lossless format. {@code quality} refers to how
+ * much effort to put into compression. A value of {@code 0} means to
+ * compress quickly, resulting in a relatively large file size.
+ * {@code 100} means to spend more time compressing, resulting in a
+ * smaller file.
+ */
+ WEBP_LOSSLESS (4);
CompressFormat(int nativeInt) {
this.nativeInt = nativeInt;
@@ -1385,10 +1420,8 @@ public final class Bitmap implements Parcelable {
* pixels).
*
* @param format The format of the compressed image
- * @param quality Hint to the compressor, 0-100. 0 meaning compress for
- * small size, 100 meaning compress for max quality. Some
- * formats, like PNG which is lossless, will ignore the
- * quality setting
+ * @param quality Hint to the compressor, 0-100. The value is interpreted
+ * differently depending on the {@link CompressFormat}.
* @param stream The outputstream to write the compressed data.
* @return true if successfully compressed to the specified stream.
*/
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 9c4b5e8b0165..06d4fbdd85b1 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -1380,9 +1380,9 @@ public abstract class ColorSpace {
*/
@NonNull
static ColorSpace get(@IntRange(from = MIN_ID, to = MAX_ID) int index) {
- if (index < 0 || index >= Named.values().length) {
+ if (index < 0 || index >= sNamedColorSpaces.length) {
throw new IllegalArgumentException("Invalid ID, must be in the range [0.." +
- Named.values().length + ")");
+ sNamedColorSpaces.length + ")");
}
return sNamedColorSpaces[index];
}
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 150a941c061e..6619dba159c2 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -286,6 +286,9 @@ public final class ImageDecoder implements AutoCloseable {
return createFromStream(is, true, preferAnimation, this);
}
+ if (assetFd == null) {
+ throw new FileNotFoundException(mUri.toString());
+ }
return createFromAssetFileDescriptor(assetFd, preferAnimation, this);
}
}
@@ -341,6 +344,9 @@ public final class ImageDecoder implements AutoCloseable {
@NonNull
private static ImageDecoder createFromAssetFileDescriptor(@NonNull AssetFileDescriptor assetFd,
boolean preferAnimation, Source source) throws IOException {
+ if (assetFd == null) {
+ throw new FileNotFoundException();
+ }
final FileDescriptor fd = assetFd.getFileDescriptor();
final long offset = assetFd.getStartOffset();
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 0e635c774c84..17e3b4465130 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -1380,7 +1380,22 @@ public final class RenderNode {
* @return Approximate memory usage in bytes.
*/
public @BytesLong long computeApproximateMemoryUsage() {
- return nGetDebugSize(mNativeRenderNode);
+ return nGetUsageSize(mNativeRenderNode);
+ }
+
+ /**
+ * Gets the approximate amount of memory allocated for the RenderNode for debug purposes.
+ * Does not include the memory allocated by any child RenderNodes nor any bitmaps, only the
+ * memory allocated for this RenderNode and any data it owns.
+ *
+ * The difference between this and {@link #computeApproximateMemoryUsage()} is this includes
+ * memory allocated but not used. In particular structures such as DisplayLists are similar
+ * to things like ArrayLists - they need to resize as commands are added to them. As such,
+ * memory used can be less than memory allocated.
+ *
+ * @hide */
+ public @BytesLong long computeApproximateMemoryAllocated() {
+ return nGetAllocatedSize(mNativeRenderNode);
}
/**
@@ -1485,7 +1500,8 @@ public final class RenderNode {
private static native void nOutput(long renderNode);
- private static native int nGetDebugSize(long renderNode);
+ private static native int nGetUsageSize(long renderNode);
+ private static native int nGetAllocatedSize(long renderNode);
private static native void nRequestPositionUpdates(long renderNode,
PositionUpdateListener callback);
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index b9945cc735d8..c6586ecfceb9 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -97,6 +97,14 @@ import java.lang.annotation.RetentionPolicy;
* @attr ref android.R.styleable#GradientDrawablePadding_bottom
*/
public class GradientDrawable extends Drawable {
+
+ /**
+ * Flag to determine if we should wrap negative gradient angle measurements
+ * for API levels that support it
+ * @hide
+ */
+ public static boolean sWrapNegativeAngleMeasurements = true;
+
/**
* Shape is a rectangle, possibly with rounded corners
*/
@@ -151,6 +159,9 @@ public class GradientDrawable extends Drawable {
/** Radius is a fraction of the bounds size. */
private static final int RADIUS_TYPE_FRACTION_PARENT = 2;
+ /** Default orientation for GradientDrawable **/
+ private static final Orientation DEFAULT_ORIENTATION = Orientation.TOP_BOTTOM;
+
/** @hide */
@IntDef({RADIUS_TYPE_PIXELS, RADIUS_TYPE_FRACTION, RADIUS_TYPE_FRACTION_PARENT})
@Retention(RetentionPolicy.SOURCE)
@@ -207,7 +218,7 @@ public class GradientDrawable extends Drawable {
}
public GradientDrawable() {
- this(new GradientState(Orientation.TOP_BOTTOM, null), null);
+ this(new GradientState(DEFAULT_ORIENTATION, null), null);
}
/**
@@ -637,7 +648,7 @@ public class GradientDrawable extends Drawable {
* @see #setOrientation(Orientation)
*/
public Orientation getOrientation() {
- return mGradientState.getOrientation();
+ return mGradientState.mOrientation;
}
/**
@@ -653,7 +664,7 @@ public class GradientDrawable extends Drawable {
* @see #getOrientation()
*/
public void setOrientation(Orientation orientation) {
- mGradientState.setOrientation(orientation);
+ mGradientState.mOrientation = orientation;
mGradientIsDirty = true;
invalidateSelf();
}
@@ -1270,7 +1281,7 @@ public class GradientDrawable extends Drawable {
if (st.mGradient == LINEAR_GRADIENT) {
final float level = st.mUseLevel ? getLevel() / 10000.0f : 1.0f;
- switch (st.getOrientation()) {
+ switch (st.mOrientation) {
case TOP_BOTTOM:
x0 = r.left; y0 = r.top;
x1 = x0; y1 = level * r.bottom;
@@ -1757,7 +1768,49 @@ public class GradientDrawable extends Drawable {
}
int angle = (int) a.getFloat(R.styleable.GradientDrawableGradient_angle, st.mAngle);
- st.mAngle = ((angle % 360) + 360) % 360; // offset negative angle measures
+
+ // GradientDrawable historically has not parsed negative angle measurements and always
+ // stays on the default orientation for API levels older than Q.
+ // Only configure the orientation if the angle is greater than zero.
+ // Otherwise fallback on Orientation.TOP_BOTTOM
+ // In Android Q and later, actually wrap the negative angle measurement to the correct
+ // value
+ if (sWrapNegativeAngleMeasurements) {
+ st.mAngle = ((angle % 360) + 360) % 360; // offset negative angle measures
+ } else {
+ st.mAngle = angle % 360;
+ }
+
+ if (st.mAngle >= 0) {
+ switch (st.mAngle) {
+ case 0:
+ st.mOrientation = Orientation.LEFT_RIGHT;
+ break;
+ case 45:
+ st.mOrientation = Orientation.BL_TR;
+ break;
+ case 90:
+ st.mOrientation = Orientation.BOTTOM_TOP;
+ break;
+ case 135:
+ st.mOrientation = Orientation.BR_TL;
+ break;
+ case 180:
+ st.mOrientation = Orientation.RIGHT_LEFT;
+ break;
+ case 225:
+ st.mOrientation = Orientation.TR_BL;
+ break;
+ case 270:
+ st.mOrientation = Orientation.TOP_BOTTOM;
+ break;
+ case 315:
+ st.mOrientation = Orientation.TL_BR;
+ break;
+ }
+ } else {
+ st.mOrientation = DEFAULT_ORIENTATION;
+ }
final TypedValue tv = a.peekValue(R.styleable.GradientDrawableGradient_gradientRadius);
if (tv != null) {
@@ -1981,7 +2034,7 @@ public class GradientDrawable extends Drawable {
int[] mAttrPadding;
public GradientState(Orientation orientation, int[] gradientColors) {
- setOrientation(orientation);
+ mOrientation = orientation;
setGradientColors(gradientColors);
}
@@ -2184,93 +2237,11 @@ public class GradientDrawable extends Drawable {
mCenterY = y;
}
- public void setOrientation(Orientation orientation) {
- // Update the angle here so that subsequent attempts to obtain the orientation
- // from the angle overwrite previously configured values during inflation
- mAngle = getAngleFromOrientation(orientation);
- mOrientation = orientation;
- }
-
@NonNull
public Orientation getOrientation() {
- updateGradientStateOrientation();
return mOrientation;
}
- /**
- * Update the orientation of the gradient based on the given angle only if the type is
- * {@link #LINEAR_GRADIENT}
- */
- private void updateGradientStateOrientation() {
- if (mGradient == LINEAR_GRADIENT) {
- int angle = mAngle;
- if (angle % 45 != 0) {
- throw new IllegalArgumentException("Linear gradient requires 'angle' attribute "
- + "to be a multiple of 45");
- }
-
- Orientation orientation;
- switch (angle) {
- case 0:
- orientation = Orientation.LEFT_RIGHT;
- break;
- case 45:
- orientation = Orientation.BL_TR;
- break;
- case 90:
- orientation = Orientation.BOTTOM_TOP;
- break;
- case 135:
- orientation = Orientation.BR_TL;
- break;
- case 180:
- orientation = Orientation.RIGHT_LEFT;
- break;
- case 225:
- orientation = Orientation.TR_BL;
- break;
- case 270:
- orientation = Orientation.TOP_BOTTOM;
- break;
- case 315:
- orientation = Orientation.TL_BR;
- break;
- default:
- // Should not get here as exception is thrown above if angle is not multiple
- // of 45 degrees
- orientation = Orientation.LEFT_RIGHT;
- break;
- }
- mOrientation = orientation;
- }
- }
-
- private int getAngleFromOrientation(@Nullable Orientation orientation) {
- if (orientation != null) {
- switch (orientation) {
- default:
- case LEFT_RIGHT:
- return 0;
- case BL_TR:
- return 45;
- case BOTTOM_TOP:
- return 90;
- case BR_TL:
- return 135;
- case RIGHT_LEFT:
- return 180;
- case TR_BL:
- return 225;
- case TOP_BOTTOM:
- return 270;
- case TL_BR:
- return 315;
- }
- } else {
- return 0;
- }
- }
-
public void setGradientColors(@Nullable int[] colors) {
mGradientColors = colors;
mSolidColors = null;
diff --git a/graphics/java/android/graphics/pdf/TEST_MAPPING b/graphics/java/android/graphics/pdf/TEST_MAPPING
new file mode 100644
index 000000000000..d763598f5ba0
--- /dev/null
+++ b/graphics/java/android/graphics/pdf/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsPdfTestCases"
+ }
+ ]
+}
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 1829d2f406b4..254456cea536 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -343,6 +343,16 @@ public final class KeyChain {
public static final int KEY_ATTESTATION_FAILURE = 4;
/**
+ * Used by DPC or delegated app in
+ * {@link android.app.admin.DeviceAdminReceiver#onChoosePrivateKeyAlias} or
+ * {@link android.app.admin.DelegatedAdminReceiver#onChoosePrivateKeyAlias} to identify that
+ * the requesting app is not granted access to any key, and nor will the user be able to grant
+ * access manually.
+ */
+ public static final String KEY_ALIAS_SELECTION_DENIED =
+ "android:alias-selection-denied";
+
+ /**
* Returns an {@code Intent} that can be used for credential
* installation. The intent may be used without any extras, in
* which case the user will be able to install credentials from
diff --git a/keystore/java/android/security/keystore/AttestationUtils.java b/keystore/java/android/security/keystore/AttestationUtils.java
index bd497c1e4efa..94499ce24ed0 100644
--- a/keystore/java/android/security/keystore/AttestationUtils.java
+++ b/keystore/java/android/security/keystore/AttestationUtils.java
@@ -74,6 +74,13 @@ public abstract class AttestationUtils {
public static final int ID_TYPE_MEID = 3;
/**
+ * Specifies that the device should attest its MEIDs. For use with {@link #attestDeviceIds}.
+ *
+ * @see #attestDeviceIds
+ */
+ public static final int USE_INDIVIDUAL_ATTESTATION = 4;
+
+ /**
* Creates an array of X509Certificates from the provided KeymasterCertificateChain.
*
* @hide Only called by the DevicePolicyManager.
@@ -196,6 +203,13 @@ public abstract class AttestationUtils {
meid.getBytes(StandardCharsets.UTF_8));
break;
}
+ case USE_INDIVIDUAL_ATTESTATION: {
+ //TODO: Add the Keymaster tag for requesting the use of individual
+ //attestation certificate, which should be
+ //KeymasterDefs.KM_TAG_DEVICE_UNIQUE_ATTESTATION
+ attestArgs.addBoolean(720);
+ break;
+ }
default:
throw new IllegalArgumentException("Unknown device ID type " + idType);
}
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index eeaefc5b157c..4f52a8800a74 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -166,7 +166,11 @@ cc_test {
static_libs: common_test_libs + ["liblog", "libz"],
},
},
- data: ["tests/data/**/*.apk"],
+ data: [
+ "tests/data/**/*.apk",
+ "tests/data/**/*.arsc",
+ "tests/data/**/*.idmap",
+ ],
test_suites: ["device-tests"],
}
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index cf2ef3070385..16dbbf61351a 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -42,12 +42,16 @@ static const std::string kResourcesArsc("resources.arsc");
ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle,
const std::string& path,
- time_t last_mod_time)
- : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path), last_mod_time_(last_mod_time) {
+ time_t last_mod_time,
+ bool for_loader)
+ : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path), last_mod_time_(last_mod_time),
+ for_loader_(for_loader) {
}
-std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system) {
- return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, false /*load_as_shared_library*/);
+std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system,
+ bool for_loader) {
+ return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, false /*load_as_shared_library*/,
+ for_loader);
}
std::unique_ptr<const ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path,
@@ -71,14 +75,26 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap
return {};
}
return LoadImpl({} /*fd*/, loaded_idmap->OverlayApkPath(), std::move(idmap_asset),
- std::move(loaded_idmap), system, false /*load_as_shared_library*/);
+ std::move(loaded_idmap), system, true /*load_as_shared_library*/);
}
std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(unique_fd fd,
const std::string& friendly_name,
- bool system, bool force_shared_lib) {
+ bool system, bool force_shared_lib,
+ bool for_loader) {
return LoadImpl(std::move(fd), friendly_name, nullptr /*idmap_asset*/, nullptr /*loaded_idmap*/,
- system, force_shared_lib);
+ system, force_shared_lib, for_loader);
+}
+
+std::unique_ptr<const ApkAssets> ApkAssets::LoadArsc(const std::string& path,
+ bool for_loader) {
+ return LoadArscImpl({} /*fd*/, path, for_loader);
+}
+
+std::unique_ptr<const ApkAssets> ApkAssets::LoadArsc(unique_fd fd,
+ const std::string& friendly_name,
+ bool for_loader) {
+ return LoadArscImpl(std::move(fd), friendly_name, for_loader);
}
std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) {
@@ -104,7 +120,8 @@ std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) {
std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
unique_fd fd, const std::string& path, std::unique_ptr<Asset> idmap_asset,
- std::unique_ptr<const LoadedIdmap> loaded_idmap, bool system, bool load_as_shared_library) {
+ std::unique_ptr<const LoadedIdmap> loaded_idmap, bool system, bool load_as_shared_library,
+ bool for_loader) {
::ZipArchiveHandle unmanaged_handle;
int32_t result;
if (fd >= 0) {
@@ -123,7 +140,8 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
time_t last_mod_time = getFileModDate(path.c_str());
// Wrap the handle in a unique_ptr so it gets automatically closed.
- std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time));
+ std::unique_ptr<ApkAssets>
+ loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time, for_loader));
// Find the resource table.
::ZipEntry entry;
@@ -147,12 +165,14 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
// Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid.
loaded_apk->idmap_asset_ = std::move(idmap_asset);
+ loaded_apk->loaded_idmap_ = std::move(loaded_idmap);
const StringPiece data(
reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
loaded_apk->resources_asset_->getLength());
loaded_apk->loaded_arsc_ =
- LoadedArsc::Load(data, loaded_idmap.get(), system, load_as_shared_library);
+ LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(), system, load_as_shared_library,
+ for_loader);
if (loaded_apk->loaded_arsc_ == nullptr) {
LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'.";
return {};
@@ -162,8 +182,53 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
return std::move(loaded_apk);
}
+std::unique_ptr<const ApkAssets> ApkAssets::LoadArscImpl(unique_fd fd,
+ const std::string& path,
+ bool for_loader) {
+ std::unique_ptr<Asset> resources_asset;
+
+ if (fd >= 0) {
+ resources_asset = std::unique_ptr<Asset>(Asset::createFromFd(fd.release(), nullptr,
+ Asset::AccessMode::ACCESS_BUFFER));
+ } else {
+ resources_asset = CreateAssetFromFile(path);
+ }
+
+ if (resources_asset == nullptr) {
+ LOG(ERROR) << "Failed to open ARSC '" << path;
+ return {};
+ }
+
+ time_t last_mod_time = getFileModDate(path.c_str());
+
+ std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(nullptr, path, last_mod_time, for_loader));
+ loaded_apk->resources_asset_ = std::move(resources_asset);
+
+ const StringPiece data(
+ reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
+ loaded_apk->resources_asset_->getLength());
+ loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, false, false, for_loader);
+ if (loaded_apk->loaded_arsc_ == nullptr) {
+ LOG(ERROR) << "Failed to load '" << kResourcesArsc << path;
+ return {};
+ }
+
+ // Need to force a move for mingw32.
+ return std::move(loaded_apk);
+}
+
+std::unique_ptr<const ApkAssets> ApkAssets::LoadEmpty(bool for_loader) {
+ std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(nullptr, "", -1, for_loader));
+ loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
+ // Need to force a move for mingw32.
+ return std::move(loaded_apk);
+}
+
std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode mode) const {
- CHECK(zip_handle_ != nullptr);
+ // If this is a resource loader from an .arsc, there will be no zip handle
+ if (zip_handle_ == nullptr) {
+ return {};
+ }
::ZipEntry entry;
int32_t result = ::FindEntry(zip_handle_.get(), path, &entry);
@@ -205,7 +270,10 @@ std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMod
bool ApkAssets::ForEachFile(const std::string& root_path,
const std::function<void(const StringPiece&, FileType)>& f) const {
- CHECK(zip_handle_ != nullptr);
+ // If this is a resource loader from an .arsc, there will be no zip handle
+ if (zip_handle_ == nullptr) {
+ return false;
+ }
std::string root_path_full = root_path;
if (root_path_full.back() != '/') {
@@ -252,6 +320,11 @@ bool ApkAssets::ForEachFile(const std::string& root_path,
}
bool ApkAssets::IsUpToDate() const {
+ // Loaders are invalidated by the app, not the system, so assume up to date
+ if (for_loader_) {
+ return true;
+ }
+
return last_mod_time_ == getFileModDate(path_.c_str());
}
diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp
index 92125c9da8bb..c132f343713f 100644
--- a/libs/androidfw/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -133,14 +133,24 @@ Asset::Asset(void)
*/
/*static*/ Asset* Asset::createFromFile(const char* fileName, AccessMode mode)
{
+ return createFromFd(open(fileName, O_RDONLY | O_BINARY), fileName, mode);
+}
+
+/*
+ * Create a new Asset from a file on disk. There is a fair chance that
+ * the file doesn't actually exist.
+ *
+ * We can use "mode" to decide how we want to go about it.
+ */
+/*static*/ Asset* Asset::createFromFd(const int fd, const char* fileName, AccessMode mode)
+{
+ if (fd < 0) {
+ return NULL;
+ }
+
_FileAsset* pAsset;
status_t result;
off64_t length;
- int fd;
-
- fd = open(fileName, O_RDONLY | O_BINARY);
- if (fd < 0)
- return NULL;
/*
* Under Linux, the lseek fails if we actually opened a directory. To
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 01caf011f644..ca4143f3e215 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -25,6 +25,7 @@
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
+#include "androidfw/ResourceUtils.h"
#include "androidfw/Util.h"
#include "utils/ByteOrder.h"
#include "utils/Trace.h"
@@ -35,15 +36,13 @@
#endif
#endif
-#include "androidfw/ResourceUtils.h"
-
namespace android {
struct FindEntryResult {
// A pointer to the resource table entry for this resource.
// If the size of the entry is > sizeof(ResTable_entry), it can be cast to
// a ResTable_map_entry and processed as a bag/map.
- const ResTable_entry* entry;
+ ResTable_entry_handle entry;
// The configuration for which the resulting entry was defined. This is already swapped to host
// endianness.
@@ -55,6 +54,9 @@ struct FindEntryResult {
// The dynamic package ID map for the package from which this resource came from.
const DynamicRefTable* dynamic_ref_table;
+ // The package name of the resource.
+ const std::string* package_name;
+
// The string pool reference to the type's name. This uses a different string pool than
// the global string pool, but this is hidden from the caller.
StringPoolRef type_string_ref;
@@ -83,11 +85,15 @@ void AssetManager2::BuildDynamicRefTable() {
package_groups_.clear();
package_ids_.fill(0xff);
+ // A mapping from apk assets path to the runtime package id of its first loaded package.
+ std::unordered_map<std::string, uint8_t> apk_assets_package_ids;
+
// 0x01 is reserved for the android package.
int next_package_id = 0x02;
const size_t apk_assets_count = apk_assets_.size();
for (size_t i = 0; i < apk_assets_count; i++) {
- const LoadedArsc* loaded_arsc = apk_assets_[i]->GetLoadedArsc();
+ const ApkAssets* apk_assets = apk_assets_[i];
+ const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc();
for (const std::unique_ptr<const LoadedPackage>& package : loaded_arsc->GetPackages()) {
// Get the package ID or assign one if a shared library.
@@ -103,9 +109,37 @@ void AssetManager2::BuildDynamicRefTable() {
if (idx == 0xff) {
package_ids_[package_id] = idx = static_cast<uint8_t>(package_groups_.size());
package_groups_.push_back({});
- DynamicRefTable& ref_table = package_groups_.back().dynamic_ref_table;
- ref_table.mAssignedPackageId = package_id;
- ref_table.mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f;
+
+ if (apk_assets->IsOverlay()) {
+ // The target package must precede the overlay package in the apk assets paths in order
+ // to take effect.
+ const auto& loaded_idmap = apk_assets->GetLoadedIdmap();
+ auto target_package_iter = apk_assets_package_ids.find(loaded_idmap->TargetApkPath());
+ if (target_package_iter != apk_assets_package_ids.end()) {
+ const uint8_t target_package_id = target_package_iter->second;
+ const uint8_t target_idx = package_ids_[target_package_id];
+ CHECK(target_idx != 0xff) << "overlay added to apk_assets_package_ids but does not"
+ << " have an assigned package group";
+
+ PackageGroup& target_package_group = package_groups_[target_idx];
+
+ // Create a special dynamic reference table for the overlay to rewite references to
+ // overlay resources as references to the target resources they overlay.
+ auto overlay_table = std::make_shared<OverlayDynamicRefTable>(
+ loaded_idmap->GetOverlayDynamicRefTable(target_package_id));
+ package_groups_.back().dynamic_ref_table = overlay_table;
+
+ // Add the overlay resource map to the target package's set of overlays.
+ target_package_group.overlays_.push_back(
+ ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id,
+ overlay_table.get()),
+ static_cast<ApkAssetsCookie>(i)});
+ }
+ }
+
+ DynamicRefTable* ref_table = package_groups_.back().dynamic_ref_table.get();
+ ref_table->mAssignedPackageId = package_id;
+ ref_table->mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f;
}
PackageGroup* package_group = &package_groups_[idx];
@@ -116,9 +150,11 @@ void AssetManager2::BuildDynamicRefTable() {
// Add the package name -> build time ID mappings.
for (const DynamicPackageEntry& entry : package->GetDynamicPackageMap()) {
String16 package_name(entry.package_name.c_str(), entry.package_name.size());
- package_group->dynamic_ref_table.mEntries.replaceValueFor(
+ package_group->dynamic_ref_table->mEntries.replaceValueFor(
package_name, static_cast<uint8_t>(entry.package_id));
}
+
+ apk_assets_package_ids.insert(std::make_pair(apk_assets->GetPath(), package_id));
}
}
@@ -127,8 +163,8 @@ void AssetManager2::BuildDynamicRefTable() {
for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) {
const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName();
for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) {
- iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()),
- iter->dynamic_ref_table.mAssignedPackageId);
+ iter2->dynamic_ref_table->addMapping(String16(package_name.c_str(), package_name.size()),
+ iter->dynamic_ref_table->mAssignedPackageId);
}
}
}
@@ -161,13 +197,13 @@ void AssetManager2::DumpToLog() const {
(loaded_package->IsDynamic() ? " dynamic" : ""));
}
LOG(INFO) << base::StringPrintf("PG (%02x): ",
- package_group.dynamic_ref_table.mAssignedPackageId)
+ package_group.dynamic_ref_table->mAssignedPackageId)
<< list;
for (size_t i = 0; i < 256; i++) {
- if (package_group.dynamic_ref_table.mLookupTable[i] != 0) {
+ if (package_group.dynamic_ref_table->mLookupTable[i] != 0) {
LOG(INFO) << base::StringPrintf(" e[0x%02x] -> 0x%02x", (uint8_t) i,
- package_group.dynamic_ref_table.mLookupTable[i]);
+ package_group.dynamic_ref_table->mLookupTable[i]);
}
}
}
@@ -189,14 +225,15 @@ const DynamicRefTable* AssetManager2::GetDynamicRefTableForPackage(uint32_t pack
if (idx == 0xff) {
return nullptr;
}
- return &package_groups_[idx].dynamic_ref_table;
+ return package_groups_[idx].dynamic_ref_table.get();
}
-const DynamicRefTable* AssetManager2::GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const {
+std::shared_ptr<const DynamicRefTable> AssetManager2::GetDynamicRefTableForCookie(
+ ApkAssetsCookie cookie) const {
for (const PackageGroup& package_group : package_groups_) {
for (const ApkAssetsCookie& package_cookie : package_group.cookies_) {
if (package_cookie == cookie) {
- return &package_group.dynamic_ref_table;
+ return package_group.dynamic_ref_table;
}
}
}
@@ -224,6 +261,62 @@ const std::unordered_map<std::string, std::string>*
return &loaded_package->GetOverlayableMap();
}
+bool AssetManager2::GetOverlayablesToString(const android::StringPiece& package_name,
+ std::string* out) const {
+ uint8_t package_id = 0U;
+ for (const auto& apk_assets : apk_assets_) {
+ const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc();
+ if (loaded_arsc == nullptr) {
+ continue;
+ }
+
+ const auto& loaded_packages = loaded_arsc->GetPackages();
+ if (loaded_packages.empty()) {
+ continue;
+ }
+
+ const auto& loaded_package = loaded_packages[0];
+ if (loaded_package->GetPackageName() == package_name) {
+ package_id = GetAssignedPackageId(loaded_package.get());
+ break;
+ }
+ }
+
+ if (package_id == 0U) {
+ ANDROID_LOG(ERROR) << base::StringPrintf("No package with name '%s", package_name.data());
+ return false;
+ }
+
+ const size_t idx = package_ids_[package_id];
+ if (idx == 0xff) {
+ return false;
+ }
+
+ std::string output;
+ for (const ConfiguredPackage& package : package_groups_[idx].packages_) {
+ const LoadedPackage* loaded_package = package.loaded_package_;
+ for (auto it = loaded_package->begin(); it != loaded_package->end(); it++) {
+ const OverlayableInfo* info = loaded_package->GetOverlayableInfo(*it);
+ if (info != nullptr) {
+ ResourceName res_name;
+ if (!GetResourceName(*it, &res_name)) {
+ ANDROID_LOG(ERROR) << base::StringPrintf(
+ "Unable to retrieve name of overlayable resource 0x%08x", *it);
+ return false;
+ }
+
+ const std::string name = ToFormattedResourceString(&res_name);
+ output.append(base::StringPrintf(
+ "resource='%s' overlayable='%s' actor='%s' policy='0x%08x'\n",
+ name.c_str(), info->name.c_str(), info->actor.c_str(), info->policy_flags));
+ }
+ }
+ }
+
+ *out = std::move(output);
+ return true;
+}
+
void AssetManager2::SetConfiguration(const ResTable_config& configuration) {
const int diff = configuration_.diff(configuration);
configuration_ = configuration;
@@ -234,21 +327,45 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) {
}
}
+std::set<std::string> AssetManager2::GetNonSystemOverlayPaths() const {
+ std::set<std::string> non_system_overlays;
+ for (const PackageGroup& package_group : package_groups_) {
+ bool found_system_package = false;
+ for (const ConfiguredPackage& package : package_group.packages_) {
+ if (package.loaded_package_->IsSystem()) {
+ found_system_package = true;
+ break;
+ }
+ }
+
+ if (!found_system_package) {
+ for (const ConfiguredOverlay& overlay : package_group.overlays_) {
+ non_system_overlays.insert(apk_assets_[overlay.cookie]->GetPath());
+ }
+ }
+ }
+
+ return non_system_overlays;
+}
+
std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system,
bool exclude_mipmap) const {
ATRACE_NAME("AssetManager::GetResourceConfigurations");
+ const auto non_system_overlays =
+ (exclude_system) ? GetNonSystemOverlayPaths() : std::set<std::string>();
+
std::set<ResTable_config> configurations;
for (const PackageGroup& package_group : package_groups_) {
- bool found_system_package = false;
- for (const ConfiguredPackage& package : package_group.packages_) {
+ for (size_t i = 0; i < package_group.packages_.size(); i++) {
+ const ConfiguredPackage& package = package_group.packages_[i];
if (exclude_system && package.loaded_package_->IsSystem()) {
- found_system_package = true;
continue;
}
- if (exclude_system && package.loaded_package_->IsOverlay() && found_system_package) {
- // Overlays must appear after the target package to take effect. Any overlay found in the
- // same package as a system package is able to overlay system resources.
+ auto apk_assets = apk_assets_[package_group.cookies_[i]];
+ if (exclude_system && apk_assets->IsOverlay()
+ && non_system_overlays.find(apk_assets->GetPath()) == non_system_overlays.end()) {
+ // Exclude overlays that target system resources.
continue;
}
@@ -262,17 +379,20 @@ std::set<std::string> AssetManager2::GetResourceLocales(bool exclude_system,
bool merge_equivalent_languages) const {
ATRACE_NAME("AssetManager::GetResourceLocales");
std::set<std::string> locales;
+ const auto non_system_overlays =
+ (exclude_system) ? GetNonSystemOverlayPaths() : std::set<std::string>();
+
for (const PackageGroup& package_group : package_groups_) {
- bool found_system_package = false;
- for (const ConfiguredPackage& package : package_group.packages_) {
+ for (size_t i = 0; i < package_group.packages_.size(); i++) {
+ const ConfiguredPackage& package = package_group.packages_[i];
if (exclude_system && package.loaded_package_->IsSystem()) {
- found_system_package = true;
continue;
}
- if (exclude_system && package.loaded_package_->IsOverlay() && found_system_package) {
- // Overlays must appear after the target package to take effect. Any overlay found in the
- // same package as a system package is able to overlay system resources.
+ auto apk_assets = apk_assets_[package_group.cookies_[i]];
+ if (exclude_system && apk_assets->IsOverlay()
+ && non_system_overlays.find(apk_assets->GetPath()) == non_system_overlays.end()) {
+ // Exclude overlays that target system resources.
continue;
}
@@ -368,6 +488,12 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
bool /*stop_at_first_match*/,
bool ignore_configuration,
FindEntryResult* out_entry) const {
+ if (resource_resolution_logging_enabled_) {
+ // Clear the last logged resource resolution.
+ ResetResourceResolution();
+ last_resolution_.resid = resid;
+ }
+
// Might use this if density_override != 0.
ResTable_config density_override_config;
@@ -379,6 +505,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
desired_config = &density_override_config;
}
+ // Retrieve the package group from the package id of the resource id.
if (!is_valid_resid(resid)) {
LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid);
return kInvalidCookie;
@@ -387,8 +514,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
const uint32_t package_id = get_package_id(resid);
const uint8_t type_idx = get_type_id(resid) - 1;
const uint16_t entry_idx = get_entry_id(resid);
-
- const uint8_t package_idx = package_ids_[package_id];
+ uint8_t package_idx = package_ids_[package_id];
if (package_idx == 0xff) {
ANDROID_LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.",
package_id, resid);
@@ -396,8 +522,71 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
}
const PackageGroup& package_group = package_groups_[package_idx];
- const size_t package_count = package_group.packages_.size();
+ ApkAssetsCookie cookie = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config,
+ false /* stop_at_first_match */,
+ ignore_configuration, out_entry);
+ if (UNLIKELY(cookie == kInvalidCookie)) {
+ return kInvalidCookie;
+ }
+
+ if (!apk_assets_[cookie]->IsLoader()) {
+ for (const auto& id_map : package_group.overlays_) {
+ auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid);
+ if (!overlay_entry) {
+ // No id map entry exists for this target resource.
+ continue;
+ }
+
+ if (overlay_entry.IsTableEntry()) {
+ // The target resource is overlaid by an inline value not represented by a resource.
+ out_entry->entry = overlay_entry.GetTableEntry();
+ out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
+ cookie = id_map.cookie;
+ continue;
+ }
+
+ FindEntryResult overlay_result;
+ ApkAssetsCookie overlay_cookie = FindEntry(overlay_entry.GetResourceId(), density_override,
+ false /* stop_at_first_match */,
+ ignore_configuration, &overlay_result);
+ if (UNLIKELY(overlay_cookie == kInvalidCookie)) {
+ continue;
+ }
+ if (!overlay_result.config.isBetterThan(out_entry->config, desired_config)
+ && overlay_result.config.compare(out_entry->config) != 0) {
+ // The configuration of the entry for the overlay must be equal to or better than the target
+ // configuration to be chosen as the better value.
+ continue;
+ }
+
+ cookie = overlay_cookie;
+ out_entry->entry = std::move(overlay_result.entry);
+ out_entry->config = overlay_result.config;
+ out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
+ if (resource_resolution_logging_enabled_) {
+ last_resolution_.steps.push_back(
+ Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result.config.toString(),
+ &package_group.packages_[0].loaded_package_->GetPackageName()});
+ }
+ }
+ }
+
+ if (resource_resolution_logging_enabled_) {
+ last_resolution_.cookie = cookie;
+ last_resolution_.type_string_ref = out_entry->type_string_ref;
+ last_resolution_.entry_string_ref = out_entry->entry_string_ref;
+ }
+
+ return cookie;
+}
+
+ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_group,
+ uint8_t type_idx, uint16_t entry_idx,
+ const ResTable_config& desired_config,
+ bool /*stop_at_first_match*/,
+ bool ignore_configuration,
+ FindEntryResult* out_entry) const {
ApkAssetsCookie best_cookie = kInvalidCookie;
const LoadedPackage* best_package = nullptr;
const ResTable_type* best_type = nullptr;
@@ -406,13 +595,14 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
uint32_t best_offset = 0u;
uint32_t type_flags = 0u;
- Resolution::Step::Type resolution_type;
+ Resolution::Step::Type resolution_type = Resolution::Step::Type::NO_ENTRY;
std::vector<Resolution::Step> resolution_steps;
// If desired_config is the same as the set configuration, then we can use our filtered list
// and we don't need to match the configurations, since they already matched.
- const bool use_fast_path = !ignore_configuration && desired_config == &configuration_;
+ const bool use_fast_path = !ignore_configuration && &desired_config == &configuration_;
+ const size_t package_count = package_group.packages_.size();
for (size_t pi = 0; pi < package_count; pi++) {
const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi];
const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_;
@@ -425,20 +615,10 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
continue;
}
- uint16_t local_entry_idx = entry_idx;
-
- // If there is an IDMAP supplied with this package, translate the entry ID.
- if (type_spec->idmap_entries != nullptr) {
- if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) {
- // There is no mapping, so the resource is not meant to be in this overlay package.
- continue;
- }
- }
-
- type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx);
-
- // If the package is an overlay, then even configurations that are the same MUST be chosen.
- const bool package_is_overlay = loaded_package->IsOverlay();
+ // If the package is an overlay or custom loader,
+ // then even configurations that are the same MUST be chosen.
+ const bool package_is_loader = loaded_package->IsCustomLoader();
+ type_flags |= type_spec->GetFlagsForEntryIndex(entry_idx);
if (use_fast_path) {
const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
@@ -451,19 +631,37 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
// configurations that do NOT match have been filtered-out.
if (best_config == nullptr) {
resolution_type = Resolution::Step::Type::INITIAL;
- } else if (this_config.isBetterThan(*best_config, desired_config)) {
- resolution_type = Resolution::Step::Type::BETTER_MATCH;
- } else if (package_is_overlay && this_config.compare(*best_config) == 0) {
- resolution_type = Resolution::Step::Type::OVERLAID;
+ } else if (this_config.isBetterThan(*best_config, &desired_config)) {
+ resolution_type = (package_is_loader) ? Resolution::Step::Type::BETTER_MATCH_LOADER
+ : Resolution::Step::Type::BETTER_MATCH;
+ } else if (package_is_loader && this_config.compare(*best_config) == 0) {
+ resolution_type = Resolution::Step::Type::OVERLAID_LOADER;
} else {
+ if (resource_resolution_logging_enabled_) {
+ resolution_type = (package_is_loader) ? Resolution::Step::Type::SKIPPED_LOADER
+ : Resolution::Step::Type::SKIPPED;
+ resolution_steps.push_back(Resolution::Step{resolution_type,
+ this_config.toString(),
+ &loaded_package->GetPackageName()});
+ }
continue;
}
// The configuration matches and is better than the previous selection.
// Find the entry value if it exists for this configuration.
const ResTable_type* type = filtered_group.types[i];
- const uint32_t offset = LoadedPackage::GetEntryOffset(type, local_entry_idx);
+ const uint32_t offset = LoadedPackage::GetEntryOffset(type, entry_idx);
if (offset == ResTable_type::NO_ENTRY) {
+ if (resource_resolution_logging_enabled_) {
+ if (package_is_loader) {
+ resolution_type = Resolution::Step::Type::NO_ENTRY_LOADER;
+ } else {
+ resolution_type = Resolution::Step::Type::NO_ENTRY;
+ }
+ resolution_steps.push_back(Resolution::Step{resolution_type,
+ this_config.toString(),
+ &loaded_package->GetPackageName()});
+ }
continue;
}
@@ -474,9 +672,9 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
best_offset = offset;
if (resource_resolution_logging_enabled_) {
- resolution_steps.push_back(Resolution::Step{resolution_type,
- this_config.toString(),
- &loaded_package->GetPackageName()});
+ last_resolution_.steps.push_back(Resolution::Step{resolution_type,
+ this_config.toString(),
+ &loaded_package->GetPackageName()});
}
}
} else {
@@ -491,16 +689,17 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
if (!ignore_configuration) {
this_config.copyFromDtoH((*iter)->config);
- if (!this_config.match(*desired_config)) {
+ if (!this_config.match(desired_config)) {
continue;
}
if (best_config == nullptr) {
resolution_type = Resolution::Step::Type::INITIAL;
- } else if (this_config.isBetterThan(*best_config, desired_config)) {
- resolution_type = Resolution::Step::Type::BETTER_MATCH;
- } else if (package_is_overlay && this_config.compare(*best_config) == 0) {
- resolution_type = Resolution::Step::Type::OVERLAID;
+ } else if (this_config.isBetterThan(*best_config, &desired_config)) {
+ resolution_type = (package_is_loader) ? Resolution::Step::Type::BETTER_MATCH_LOADER
+ : Resolution::Step::Type::BETTER_MATCH;
+ } else if (package_is_loader && this_config.compare(*best_config) == 0) {
+ resolution_type = Resolution::Step::Type::OVERLAID_LOADER;
} else {
continue;
}
@@ -508,7 +707,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
// The configuration matches and is better than the previous selection.
// Find the entry value if it exists for this configuration.
- const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx);
+ const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, entry_idx);
if (offset == ResTable_type::NO_ENTRY) {
continue;
}
@@ -526,9 +725,9 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
}
if (resource_resolution_logging_enabled_) {
- resolution_steps.push_back(Resolution::Step{resolution_type,
- this_config.toString(),
- &loaded_package->GetPackageName()});
+ last_resolution_.steps.push_back(Resolution::Step{resolution_type,
+ this_config.toString(),
+ &loaded_package->GetPackageName()});
}
}
}
@@ -543,38 +742,30 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
return kInvalidCookie;
}
- out_entry->entry = best_entry;
+ out_entry->entry = ResTable_entry_handle::unmanaged(best_entry);
out_entry->config = *best_config;
out_entry->type_flags = type_flags;
+ out_entry->package_name = &best_package->GetPackageName();
out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1);
out_entry->entry_string_ref =
- StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
- out_entry->dynamic_ref_table = &package_group.dynamic_ref_table;
-
- if (resource_resolution_logging_enabled_) {
- last_resolution.resid = resid;
- last_resolution.cookie = best_cookie;
- last_resolution.steps = resolution_steps;
-
- // Cache only the type/entry refs since that's all that's needed to build name
- last_resolution.type_string_ref =
- StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1);
- last_resolution.entry_string_ref =
- StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
- }
+ StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
+ out_entry->dynamic_ref_table = package_group.dynamic_ref_table.get();
return best_cookie;
}
+void AssetManager2::ResetResourceResolution() const {
+ last_resolution_.cookie = kInvalidCookie;
+ last_resolution_.resid = 0;
+ last_resolution_.steps.clear();
+ last_resolution_.type_string_ref = StringPoolRef();
+ last_resolution_.entry_string_ref = StringPoolRef();
+}
+
void AssetManager2::SetResourceResolutionLoggingEnabled(bool enabled) {
resource_resolution_logging_enabled_ = enabled;
-
if (!enabled) {
- last_resolution.cookie = kInvalidCookie;
- last_resolution.resid = 0;
- last_resolution.steps.clear();
- last_resolution.type_string_ref = StringPoolRef();
- last_resolution.entry_string_ref = StringPoolRef();
+ ResetResourceResolution();
}
}
@@ -584,24 +775,24 @@ std::string AssetManager2::GetLastResourceResolution() const {
return std::string();
}
- auto cookie = last_resolution.cookie;
+ auto cookie = last_resolution_.cookie;
if (cookie == kInvalidCookie) {
LOG(ERROR) << "AssetManager hasn't resolved a resource to read resolution path.";
return std::string();
}
- uint32_t resid = last_resolution.resid;
- std::vector<Resolution::Step>& steps = last_resolution.steps;
+ uint32_t resid = last_resolution_.resid;
+ std::vector<Resolution::Step>& steps = last_resolution_.steps;
ResourceName resource_name;
std::string resource_name_string;
const LoadedPackage* package =
- apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid));
+ apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid));
if (package != nullptr) {
- ToResourceName(last_resolution.type_string_ref,
- last_resolution.entry_string_ref,
+ ToResourceName(last_resolution_.type_string_ref,
+ last_resolution_.entry_string_ref,
package->GetPackageName(),
&resource_name);
resource_name_string = ToFormattedResourceString(&resource_name);
@@ -622,9 +813,27 @@ std::string AssetManager2::GetLastResourceResolution() const {
case Resolution::Step::Type::BETTER_MATCH:
prefix = "Found better";
break;
+ case Resolution::Step::Type::BETTER_MATCH_LOADER:
+ prefix = "Found better in loader";
+ break;
case Resolution::Step::Type::OVERLAID:
prefix = "Overlaid";
break;
+ case Resolution::Step::Type::OVERLAID_LOADER:
+ prefix = "Overlaid by loader";
+ break;
+ case Resolution::Step::Type::SKIPPED:
+ prefix = "Skipped";
+ break;
+ case Resolution::Step::Type::SKIPPED_LOADER:
+ prefix = "Skipped loader";
+ break;
+ case Resolution::Step::Type::NO_ENTRY:
+ prefix = "No entry";
+ break;
+ case Resolution::Step::Type::NO_ENTRY_LOADER:
+ prefix = "No entry for loader";
+ break;
}
if (!prefix.empty()) {
@@ -648,25 +857,9 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) cons
return false;
}
- const uint8_t package_idx = package_ids_[get_package_id(resid)];
- if (package_idx == 0xff) {
- LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.",
- get_package_id(resid), resid);
- return false;
- }
-
- const PackageGroup& package_group = package_groups_[package_idx];
- auto cookie_iter = std::find(package_group.cookies_.begin(),
- package_group.cookies_.end(), cookie);
- if (cookie_iter == package_group.cookies_.end()) {
- return false;
- }
-
- long package_pos = std::distance(package_group.cookies_.begin(), cookie_iter);
- const LoadedPackage* package = package_group.packages_[package_pos].loaded_package_;
return ToResourceName(entry.type_string_ref,
entry.entry_string_ref,
- package->GetPackageName(),
+ *entry.package_name,
out_name);
}
@@ -693,7 +886,8 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
return kInvalidCookie;
}
- if (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) {
+ const ResTable_entry* table_entry = *entry.entry;
+ if (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) {
if (!may_be_bag) {
LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid);
return kInvalidCookie;
@@ -708,7 +902,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
}
const Res_value* device_value = reinterpret_cast<const Res_value*>(
- reinterpret_cast<const uint8_t*>(entry.entry) + dtohs(entry.entry->size));
+ reinterpret_cast<const uint8_t*>(table_entry) + dtohs(table_entry->size));
out_value->copyFrom_dtoh(*device_value);
// Convert the package ID to the runtime assigned package ID.
@@ -789,13 +983,14 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>&
// Check that the size of the entry header is at least as big as
// the desired ResTable_map_entry. Also verify that the entry
// was intended to be a map.
- if (dtohs(entry.entry->size) < sizeof(ResTable_map_entry) ||
- (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) {
+ const ResTable_entry* table_entry = *entry.entry;
+ if (dtohs(table_entry->size) < sizeof(ResTable_map_entry) ||
+ (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) {
// Not a bag, nothing to do.
return nullptr;
}
- const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry.entry);
+ const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(table_entry);
const ResTable_map* map_entry =
reinterpret_cast<const ResTable_map*>(reinterpret_cast<const uint8_t*>(map) + map->size);
const ResTable_map* const map_entry_end = map_entry + dtohl(map->count);
@@ -1020,7 +1215,7 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name,
}
if (resid != 0u) {
- return fix_package_id(resid, package_group.dynamic_ref_table.mAssignedPackageId);
+ return fix_package_id(resid, package_group.dynamic_ref_table->mAssignedPackageId);
}
}
}
@@ -1073,11 +1268,11 @@ void AssetManager2::InvalidateCaches(uint32_t diff) {
}
}
-uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) {
+uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const {
for (auto& package_group : package_groups_) {
for (auto& package2 : package_group.packages_) {
if (package2.loaded_package_ == package) {
- return package_group.dynamic_ref_table.mAssignedPackageId;
+ return package_group.dynamic_ref_table->mAssignedPackageId;
}
}
}
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index 7c1ee5cd7cfa..2b69c923597f 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -20,6 +20,8 @@
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
+#include "androidfw/ResourceTypes.h"
+#include "androidfw/Util.h"
#include "utils/ByteOrder.h"
#include "utils/Trace.h"
@@ -29,40 +31,124 @@
#endif
#endif
-#include "androidfw/ResourceTypes.h"
-
using ::android::base::StringPrintf;
namespace android {
-constexpr static inline bool is_valid_package_id(uint16_t id) {
- return id != 0 && id <= 255;
+static bool compare_target_entries(const Idmap_target_entry &e1, const uint32_t target_id) {
+ return dtohl(e1.target_id) < target_id;
}
-constexpr static inline bool is_valid_type_id(uint16_t id) {
- // Type IDs and package IDs have the same constraints in the IDMAP.
- return is_valid_package_id(id);
+static bool compare_overlay_entries(const Idmap_overlay_entry& e1, const uint32_t overlay_id) {
+ return dtohl(e1.overlay_id) < overlay_id;
}
-bool LoadedIdmap::Lookup(const IdmapEntry_header* header, uint16_t input_entry_id,
- uint16_t* output_entry_id) {
- if (input_entry_id < dtohs(header->entry_id_offset)) {
- // After applying the offset, the entry is not present.
- return false;
+OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap)
+ : data_header_(loaded_idmap->data_header_),
+ idmap_string_pool_(loaded_idmap->string_pool_.get()) { };
+
+OverlayStringPool::~OverlayStringPool() {
+ uninit();
+}
+
+const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) const {
+ const size_t offset = dtohl(data_header_->string_pool_index_offset);
+ if (idmap_string_pool_ != nullptr && idx >= size() && idx >= offset) {
+ return idmap_string_pool_->stringAt(idx - offset, outLen);
}
- input_entry_id -= dtohs(header->entry_id_offset);
- if (input_entry_id >= dtohs(header->entry_count)) {
- // The entry is not present.
- return false;
+ return ResStringPool::stringAt(idx, outLen);
+}
+
+const char* OverlayStringPool::string8At(size_t idx, size_t* outLen) const {
+ const size_t offset = dtohl(data_header_->string_pool_index_offset);
+ if (idmap_string_pool_ != nullptr && idx >= size() && idx >= offset) {
+ return idmap_string_pool_->string8At(idx - offset, outLen);
}
- uint32_t result = dtohl(header->entries[input_entry_id]);
- if (result == 0xffffffffu) {
- return false;
+ return ResStringPool::string8At(idx, outLen);
+}
+
+OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header,
+ const Idmap_overlay_entry* entries,
+ uint8_t target_assigned_package_id)
+ : data_header_(data_header),
+ entries_(entries),
+ target_assigned_package_id_(target_assigned_package_id) { };
+
+status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const {
+ const Idmap_overlay_entry* first_entry = entries_;
+ const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count);
+ auto entry = std::lower_bound(first_entry, end_entry, *resId, compare_overlay_entries);
+
+ if (entry == end_entry || dtohl(entry->overlay_id) != *resId) {
+ // A mapping for the target resource id could not be found.
+ return DynamicRefTable::lookupResourceId(resId);
}
- *output_entry_id = static_cast<uint16_t>(result);
- return true;
+
+ *resId = (0x00FFFFFFU & dtohl(entry->target_id))
+ | (((uint32_t) target_assigned_package_id_) << 24);
+ return NO_ERROR;
+}
+
+status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) const {
+ return DynamicRefTable::lookupResourceId(resId);
+}
+
+IdmapResMap::IdmapResMap(const Idmap_data_header* data_header,
+ const Idmap_target_entry* entries,
+ uint8_t target_assigned_package_id,
+ const OverlayDynamicRefTable* overlay_ref_table)
+ : data_header_(data_header),
+ entries_(entries),
+ target_assigned_package_id_(target_assigned_package_id),
+ overlay_ref_table_(overlay_ref_table) { };
+
+IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
+ if ((target_res_id >> 24) != target_assigned_package_id_) {
+ // The resource id must have the same package id as the target package.
+ return {};
+ }
+
+ // The resource ids encoded within the idmap are build-time resource ids.
+ target_res_id = (0x00FFFFFFU & target_res_id)
+ | (((uint32_t) data_header_->target_package_id) << 24);
+
+ const Idmap_target_entry* first_entry = entries_;
+ const Idmap_target_entry* end_entry = entries_ + dtohl(data_header_->target_entry_count);
+ auto entry = std::lower_bound(first_entry, end_entry, target_res_id, compare_target_entries);
+
+ if (entry == end_entry || dtohl(entry->target_id) != target_res_id) {
+ // A mapping for the target resource id could not be found.
+ return {};
+ }
+
+ // A reference should be treated as an alias of the resource. Instead of returning the table
+ // entry, return the alias resource id to look up. The alias resource might not reside within the
+ // overlay package, so the resource id must be fixed with the dynamic reference table of the
+ // overlay before returning.
+ if (entry->type == Res_value::TYPE_REFERENCE
+ || entry->type == Res_value::TYPE_DYNAMIC_REFERENCE) {
+ uint32_t overlay_resource_id = dtohl(entry->value);
+
+ // Lookup the resource without rewriting the overlay resource id back to the target resource id
+ // being looked up.
+ overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id);
+ return Result(overlay_resource_id);
+ }
+
+ // Copy the type and value into the ResTable_entry structure needed by asset manager.
+ uint16_t malloc_size = sizeof(ResTable_entry) + sizeof(Res_value);
+ auto table_entry = reinterpret_cast<ResTable_entry*>(malloc(malloc_size));
+ memset(table_entry, 0, malloc_size);
+ table_entry->size = htods(sizeof(ResTable_entry));
+
+ auto table_value = reinterpret_cast<Res_value*>(reinterpret_cast<uint8_t*>(table_entry)
+ + sizeof(ResTable_entry));
+ table_value->dataType = entry->type;
+ table_value->data = entry->value;
+
+ return Result(ResTable_entry_handle::managed(table_entry));
}
static bool is_word_aligned(const void* data) {
@@ -95,24 +181,26 @@ static bool IsValidIdmapHeader(const StringPiece& data) {
return false;
}
- if (!is_valid_package_id(dtohs(header->target_package_id))) {
- LOG(ERROR) << StringPrintf("Target package ID in Idmap is invalid: 0x%02x",
- dtohs(header->target_package_id));
- return false;
- }
-
- if (dtohs(header->type_count) > 255) {
- LOG(ERROR) << StringPrintf("Idmap has too many type mappings (was %d, max 255)",
- (int)dtohs(header->type_count));
- return false;
- }
return true;
}
-LoadedIdmap::LoadedIdmap(const Idmap_header* header) : header_(header) {
+LoadedIdmap::LoadedIdmap(const Idmap_header* header,
+ const Idmap_data_header* data_header,
+ const Idmap_target_entry* target_entries,
+ const Idmap_overlay_entry* overlay_entries,
+ ResStringPool* string_pool) : header_(header),
+ data_header_(data_header),
+ target_entries_(target_entries),
+ overlay_entries_(overlay_entries),
+ string_pool_(string_pool) {
+
size_t length = strnlen(reinterpret_cast<const char*>(header_->overlay_path),
arraysize(header_->overlay_path));
overlay_apk_path_.assign(reinterpret_cast<const char*>(header_->overlay_path), length);
+
+ length = strnlen(reinterpret_cast<const char*>(header_->target_path),
+ arraysize(header_->target_path));
+ target_apk_path_.assign(reinterpret_cast<const char*>(header_->target_path), length);
}
std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_data) {
@@ -121,70 +209,67 @@ std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_da
return {};
}
- const Idmap_header* header = reinterpret_cast<const Idmap_header*>(idmap_data.data());
-
- // Can't use make_unique because LoadedImpl constructor is private.
- std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>(new LoadedIdmap(header));
-
+ auto header = reinterpret_cast<const Idmap_header*>(idmap_data.data());
const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data()) + sizeof(*header);
size_t data_size = idmap_data.size() - sizeof(*header);
- size_t type_maps_encountered = 0u;
- while (data_size >= sizeof(IdmapEntry_header)) {
- if (!is_word_aligned(data_ptr)) {
- LOG(ERROR) << "Type mapping in Idmap is not word aligned";
- return {};
- }
-
- // Validate the type IDs.
- const IdmapEntry_header* entry_header = reinterpret_cast<const IdmapEntry_header*>(data_ptr);
- if (!is_valid_type_id(dtohs(entry_header->target_type_id)) || !is_valid_type_id(dtohs(entry_header->overlay_type_id))) {
- LOG(ERROR) << StringPrintf("Invalid type map (0x%02x -> 0x%02x)",
- dtohs(entry_header->target_type_id),
- dtohs(entry_header->overlay_type_id));
- return {};
- }
+ // Currently idmap2 can only generate one data block.
+ auto data_header = reinterpret_cast<const Idmap_data_header*>(data_ptr);
+ data_ptr += sizeof(*data_header);
+ data_size -= sizeof(*data_header);
+
+ // Make sure there is enough space for the target entries declared in the header.
+ const auto target_entries = reinterpret_cast<const Idmap_target_entry*>(data_ptr);
+ if (data_size / sizeof(Idmap_target_entry) <
+ static_cast<size_t>(dtohl(data_header->target_entry_count))) {
+ LOG(ERROR) << StringPrintf("Idmap too small for the number of target entries (%d)",
+ (int)dtohl(data_header->target_entry_count));
+ return {};
+ }
- // Make sure there is enough space for the entries declared in the header.
- if ((data_size - sizeof(*entry_header)) / sizeof(uint32_t) <
- static_cast<size_t>(dtohs(entry_header->entry_count))) {
- LOG(ERROR) << StringPrintf("Idmap too small for the number of entries (%d)",
- (int)dtohs(entry_header->entry_count));
- return {};
- }
+ // Advance the data pointer past the target entries.
+ const size_t target_entry_size_bytes =
+ (dtohl(data_header->target_entry_count) * sizeof(Idmap_target_entry));
+ data_ptr += target_entry_size_bytes;
+ data_size -= target_entry_size_bytes;
+
+ // Make sure there is enough space for the overlay entries declared in the header.
+ const auto overlay_entries = reinterpret_cast<const Idmap_overlay_entry*>(data_ptr);
+ if (data_size / sizeof(Idmap_overlay_entry) <
+ static_cast<size_t>(dtohl(data_header->overlay_entry_count))) {
+ LOG(ERROR) << StringPrintf("Idmap too small for the number of overlay entries (%d)",
+ (int)dtohl(data_header->overlay_entry_count));
+ return {};
+ }
- // Only add a non-empty overlay.
- if (dtohs(entry_header->entry_count != 0)) {
- loaded_idmap->type_map_[static_cast<uint8_t>(dtohs(entry_header->overlay_type_id))] =
- entry_header;
- }
+ // Advance the data pointer past the target entries.
+ const size_t overlay_entry_size_bytes =
+ (dtohl(data_header->overlay_entry_count) * sizeof(Idmap_overlay_entry));
+ data_ptr += overlay_entry_size_bytes;
+ data_size -= overlay_entry_size_bytes;
- const size_t entry_size_bytes =
- sizeof(*entry_header) + (dtohs(entry_header->entry_count) * sizeof(uint32_t));
- data_ptr += entry_size_bytes;
- data_size -= entry_size_bytes;
- type_maps_encountered++;
+ // Read the idmap string pool that holds the value of inline string entries.
+ if (data_size < dtohl(data_header->string_pool_length)) {
+ LOG(ERROR) << StringPrintf("Idmap too small for string pool (length %d)",
+ (int)dtohl(data_header->string_pool_length));
+ return {};
}
- // Verify that we parsed all the type maps.
- if (type_maps_encountered != static_cast<size_t>(dtohs(header->type_count))) {
- LOG(ERROR) << "Parsed " << type_maps_encountered << " type maps but expected "
- << (int)dtohs(header->type_count);
- return {};
+ auto idmap_string_pool = util::make_unique<ResStringPool>();
+ if (dtohl(data_header->string_pool_length) > 0) {
+ status_t err = idmap_string_pool->setTo(data_ptr, dtohl(data_header->string_pool_length));
+ if (err != NO_ERROR) {
+ LOG(ERROR) << "idmap string pool corrupt.";
+ return {};
+ }
}
- return std::move(loaded_idmap);
-}
-uint8_t LoadedIdmap::TargetPackageId() const {
- return static_cast<uint8_t>(dtohs(header_->target_package_id));
-}
+ // Can't use make_unique because LoadedImpl constructor is private.
+ std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>(
+ new LoadedIdmap(header, data_header, target_entries, overlay_entries,
+ idmap_string_pool.release()));
-const IdmapEntry_header* LoadedIdmap::GetEntryMapForType(uint8_t type_id) const {
- auto iter = type_map_.find(type_id);
- if (iter != type_map_.end()) {
- return iter->second;
- }
- return nullptr;
+ return std::move(loaded_idmap);
}
} // namespace android
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 72873abc6a42..c8962416d082 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -51,9 +51,8 @@ namespace {
// the Type structs.
class TypeSpecPtrBuilder {
public:
- explicit TypeSpecPtrBuilder(const ResTable_typeSpec* header,
- const IdmapEntry_header* idmap_header)
- : header_(header), idmap_header_(idmap_header) {
+ explicit TypeSpecPtrBuilder(const ResTable_typeSpec* header)
+ : header_(header) {
}
void AddType(const ResTable_type* type) {
@@ -70,7 +69,6 @@ class TypeSpecPtrBuilder {
TypeSpec* type_spec =
(TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType)));
type_spec->type_spec = header_;
- type_spec->idmap_entries = idmap_header_;
type_spec->type_count = types_.size();
memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType));
return TypeSpecPtr(type_spec);
@@ -80,7 +78,6 @@ class TypeSpecPtrBuilder {
DISALLOW_COPY_AND_ASSIGN(TypeSpecPtrBuilder);
const ResTable_typeSpec* header_;
- const IdmapEntry_header* idmap_header_;
std::vector<const ResTable_type*> types_;
};
@@ -400,8 +397,9 @@ const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const {
}
std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
- const LoadedIdmap* loaded_idmap,
- bool system, bool load_as_shared_library) {
+ bool system,
+ bool load_as_shared_library,
+ bool for_loader) {
ATRACE_NAME("LoadedPackage::Load");
std::unique_ptr<LoadedPackage> loaded_package(new LoadedPackage());
@@ -424,10 +422,8 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
loaded_package->dynamic_ = true;
}
- if (loaded_idmap != nullptr) {
- // This is an overlay and so it needs to pretend to be the target package.
- loaded_package->package_id_ = loaded_idmap->TargetPackageId();
- loaded_package->overlay_ = true;
+ if (for_loader) {
+ loaded_package->custom_loader_ = true;
}
if (header->header.headerSize >= sizeof(ResTable_package)) {
@@ -511,16 +507,9 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
return {};
}
- // If this is an overlay, associate the mapping of this type to the target type
- // from the IDMAP.
- const IdmapEntry_header* idmap_entry_header = nullptr;
- if (loaded_idmap != nullptr) {
- idmap_entry_header = loaded_idmap->GetEntryMapForType(type_spec->id);
- }
-
std::unique_ptr<TypeSpecPtrBuilder>& builder_ptr = type_builder_map[type_spec->id - 1];
if (builder_ptr == nullptr) {
- builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec, idmap_entry_header);
+ builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec);
loaded_package->resource_ids_.set(type_spec->id, entry_count);
} else {
LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x",
@@ -681,28 +670,24 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
return {};
}
- // We only add the type to the package if there is no IDMAP, or if the type is
- // overlaying something.
- if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) {
- // If this is an overlay, insert it at the target type ID.
- if (type_spec_ptr->idmap_entries != nullptr) {
- type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1;
- }
- loaded_package->type_specs_.editItemAt(type_idx) = std::move(type_spec_ptr);
- }
+ loaded_package->type_specs_.editItemAt(type_idx) = std::move(type_spec_ptr);
}
return std::move(loaded_package);
}
bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
- bool load_as_shared_library) {
+ bool load_as_shared_library, bool for_loader) {
const ResTable_header* header = chunk.header<ResTable_header>();
if (header == nullptr) {
LOG(ERROR) << "RES_TABLE_TYPE too small.";
return false;
}
+ if (loaded_idmap != nullptr) {
+ global_string_pool_ = util::make_unique<OverlayStringPool>(loaded_idmap);
+ }
+
const size_t package_count = dtohl(header->packageCount);
size_t packages_seen = 0;
@@ -714,9 +699,9 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
switch (child_chunk.type()) {
case RES_STRING_POOL_TYPE:
// Only use the first string pool. Ignore others.
- if (global_string_pool_.getError() == NO_INIT) {
- status_t err = global_string_pool_.setTo(child_chunk.header<ResStringPool_header>(),
- child_chunk.size());
+ if (global_string_pool_->getError() == NO_INIT) {
+ status_t err = global_string_pool_->setTo(child_chunk.header<ResStringPool_header>(),
+ child_chunk.size());
if (err != NO_ERROR) {
LOG(ERROR) << "RES_STRING_POOL_TYPE corrupt.";
return false;
@@ -735,7 +720,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
packages_seen++;
std::unique_ptr<const LoadedPackage> loaded_package =
- LoadedPackage::Load(child_chunk, loaded_idmap, system_, load_as_shared_library);
+ LoadedPackage::Load(child_chunk, system_, load_as_shared_library, for_loader);
if (!loaded_package) {
return false;
}
@@ -758,9 +743,11 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
}
std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const StringPiece& data,
- const LoadedIdmap* loaded_idmap, bool system,
- bool load_as_shared_library) {
- ATRACE_NAME("LoadedArsc::LoadTable");
+ const LoadedIdmap* loaded_idmap,
+ bool system,
+ bool load_as_shared_library,
+ bool for_loader) {
+ ATRACE_NAME("LoadedArsc::Load");
// Not using make_unique because the constructor is private.
std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc());
@@ -771,7 +758,10 @@ std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const StringPiece& data,
const Chunk chunk = iter.Next();
switch (chunk.type()) {
case RES_TABLE_TYPE:
- if (!loaded_arsc->LoadTable(chunk, loaded_idmap, load_as_shared_library)) {
+ if (!loaded_arsc->LoadTable(chunk,
+ loaded_idmap,
+ load_as_shared_library,
+ for_loader)) {
return {};
}
break;
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 535386920265..4d7e5dfea4f7 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1063,6 +1063,11 @@ size_t ResStringPool::bytes() const
return (mError == NO_ERROR) ? mHeader->header.size : 0;
}
+const void* ResStringPool::data() const
+{
+ return mHeader;
+}
+
bool ResStringPool::isSorted() const
{
return (mHeader->flags&ResStringPool_header::SORTED_FLAG)!=0;
@@ -1358,11 +1363,10 @@ int32_t ResXMLParser::getAttributeData(size_t idx) const
(((const uint8_t*)tag)
+ dtohs(tag->attributeStart)
+ (dtohs(tag->attributeSize)*idx));
- if (attr->typedValue.dataType != Res_value::TYPE_DYNAMIC_REFERENCE ||
- mTree.mDynamicRefTable == NULL) {
+ if (mTree.mDynamicRefTable == NULL ||
+ !mTree.mDynamicRefTable->requiresLookup(&attr->typedValue)) {
return dtohl(attr->typedValue.data);
}
-
uint32_t data = dtohl(attr->typedValue.data);
if (mTree.mDynamicRefTable->lookupResourceId(&data) == NO_ERROR) {
return data;
@@ -1608,10 +1612,9 @@ uint32_t ResXMLParser::getSourceResourceId() const
static volatile int32_t gCount = 0;
-ResXMLTree::ResXMLTree(const DynamicRefTable* dynamicRefTable)
+ResXMLTree::ResXMLTree(std::shared_ptr<const DynamicRefTable> dynamicRefTable)
: ResXMLParser(*this)
- , mDynamicRefTable((dynamicRefTable != nullptr) ? dynamicRefTable->clone()
- : std::unique_ptr<DynamicRefTable>(nullptr))
+ , mDynamicRefTable(std::move(dynamicRefTable))
, mError(NO_INIT), mOwnedData(NULL)
{
if (kDebugResXMLTree) {
@@ -1622,7 +1625,7 @@ ResXMLTree::ResXMLTree(const DynamicRefTable* dynamicRefTable)
ResXMLTree::ResXMLTree()
: ResXMLParser(*this)
- , mDynamicRefTable(std::unique_ptr<DynamicRefTable>(nullptr))
+ , mDynamicRefTable(nullptr)
, mError(NO_INIT), mOwnedData(NULL)
{
if (kDebugResXMLTree) {
@@ -4784,7 +4787,7 @@ void ResTable::setParameters(const ResTable_config* params)
packageGroup->clearBagCache();
// Find which configurations match the set of parameters. This allows for a much
- // faster lookup in getEntry() if the set of values is narrowed down.
+ // faster lookup in Lookup() if the set of values is narrowed down.
for (size_t t = 0; t < packageGroup->types.size(); t++) {
if (packageGroup->types[t].isEmpty()) {
continue;
@@ -6892,13 +6895,6 @@ DynamicRefTable::DynamicRefTable(uint8_t packageId, bool appAsLib)
mLookupTable[SYS_PACKAGE_ID] = SYS_PACKAGE_ID;
}
-std::unique_ptr<DynamicRefTable> DynamicRefTable::clone() const {
- std::unique_ptr<DynamicRefTable> clone = std::unique_ptr<DynamicRefTable>(
- new DynamicRefTable(mAssignedPackageId, mAppAsLib));
- clone->addMappings(*this);
- return clone;
-}
-
status_t DynamicRefTable::load(const ResTable_lib_header* const header)
{
const uint32_t entryCount = dtohl(header->count);
@@ -7015,21 +7011,29 @@ status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
return NO_ERROR;
}
+bool DynamicRefTable::requiresLookup(const Res_value* value) const {
+ // Only resolve non-dynamic references and attributes if the package is loaded as a
+ // library or if a shared library is attempting to retrieve its own resource
+ if ((value->dataType == Res_value::TYPE_REFERENCE ||
+ value->dataType == Res_value::TYPE_ATTRIBUTE) &&
+ (mAppAsLib || (Res_GETPACKAGE(value->data) + 1) == 0)) {
+ return true;
+ }
+ return value->dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE ||
+ value->dataType == Res_value::TYPE_DYNAMIC_REFERENCE;
+}
+
status_t DynamicRefTable::lookupResourceValue(Res_value* value) const {
+ if (!requiresLookup(value)) {
+ return NO_ERROR;
+ }
+
uint8_t resolvedType = Res_value::TYPE_REFERENCE;
switch (value->dataType) {
case Res_value::TYPE_ATTRIBUTE:
resolvedType = Res_value::TYPE_ATTRIBUTE;
FALLTHROUGH_INTENDED;
case Res_value::TYPE_REFERENCE:
- // Only resolve non-dynamic references and attributes if the package is loaded as a
- // library or if a shared library is attempting to retrieve its own resource
- if (!(mAppAsLib || (Res_GETPACKAGE(value->data) + 1) == 0)) {
- return NO_ERROR;
- }
-
- // If the package is loaded as shared library, the resource reference
- // also need to be fixed.
break;
case Res_value::TYPE_DYNAMIC_ATTRIBUTE:
resolvedType = Res_value::TYPE_ATTRIBUTE;
diff --git a/libs/androidfw/TEST_MAPPING b/libs/androidfw/TEST_MAPPING
index a58b47fcff9d..d1a6a5c18299 100644
--- a/libs/androidfw/TEST_MAPPING
+++ b/libs/androidfw/TEST_MAPPING
@@ -3,6 +3,9 @@
{
"name": "libandroidfw_tests",
"host": true
+ },
+ {
+ "name": "FrameworksResourceLoaderTests"
}
]
} \ No newline at end of file
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index 49fc82bff11e..20472872263e 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -24,6 +24,7 @@
#include "android-base/unique_fd.h"
#include "androidfw/Asset.h"
+#include "androidfw/Idmap.h"
#include "androidfw/LoadedArsc.h"
#include "androidfw/misc.h"
@@ -40,7 +41,8 @@ class ApkAssets {
// Creates an ApkAssets.
// If `system` is true, the package is marked as a system package, and allows some functions to
// filter out this package when computing what configurations/resources are available.
- static std::unique_ptr<const ApkAssets> Load(const std::string& path, bool system = false);
+ static std::unique_ptr<const ApkAssets> Load(const std::string& path, bool system = false,
+ bool for_loader = false);
// Creates an ApkAssets, but forces any package with ID 0x7f to be loaded as a shared library.
// If `system` is true, the package is marked as a system package, and allows some functions to
@@ -63,7 +65,21 @@ class ApkAssets {
// If `force_shared_lib` is true, any package with ID 0x7f is loaded as a shared library.
static std::unique_ptr<const ApkAssets> LoadFromFd(base::unique_fd fd,
const std::string& friendly_name, bool system,
- bool force_shared_lib);
+ bool force_shared_lib,
+ bool for_loader = false);
+
+ // Creates an empty wrapper ApkAssets from the given path which points to an .arsc.
+ static std::unique_ptr<const ApkAssets> LoadArsc(const std::string& path,
+ bool for_loader = false);
+
+ // Creates an empty wrapper ApkAssets from the given file descriptor which points to an .arsc,
+ // Takes ownership of the file descriptor.
+ static std::unique_ptr<const ApkAssets> LoadArsc(base::unique_fd fd,
+ const std::string& friendly_name,
+ bool resource_loader = false);
+
+ // Creates a totally empty ApkAssets with no resources table and no file entries.
+ static std::unique_ptr<const ApkAssets> LoadEmpty(bool resource_loader = false);
std::unique_ptr<Asset> Open(const std::string& path,
Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const;
@@ -80,33 +96,51 @@ class ApkAssets {
return loaded_arsc_.get();
}
+ inline const LoadedIdmap* GetLoadedIdmap() const {
+ return loaded_idmap_.get();
+ }
+
inline bool IsOverlay() const {
return idmap_asset_.get() != nullptr;
}
+ inline bool IsLoader() const {
+ return for_loader_;
+ }
+
bool IsUpToDate() const;
+ // Creates an Asset from any file on the file system.
+ static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path);
+
private:
DISALLOW_COPY_AND_ASSIGN(ApkAssets);
static std::unique_ptr<const ApkAssets> LoadImpl(base::unique_fd fd, const std::string& path,
std::unique_ptr<Asset> idmap_asset,
std::unique_ptr<const LoadedIdmap> loaded_idmap,
- bool system, bool load_as_shared_library);
+ bool system, bool load_as_shared_library,
+ bool resource_loader = false);
- // Creates an Asset from any file on the file system.
- static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path);
+ static std::unique_ptr<const ApkAssets> LoadArscImpl(base::unique_fd fd,
+ const std::string& path,
+ bool resource_loader = false);
- ApkAssets(ZipArchiveHandle unmanaged_handle, const std::string& path, time_t last_mod_time);
+ ApkAssets(ZipArchiveHandle unmanaged_handle,
+ const std::string& path,
+ time_t last_mod_time,
+ bool for_loader = false);
- using ZipArchivePtr = std::unique_ptr<ZipArchive, void(*)(ZipArchiveHandle)>;
+ using ZipArchivePtr = std::unique_ptr<ZipArchive, void (*)(ZipArchiveHandle)>;
ZipArchivePtr zip_handle_;
const std::string path_;
time_t last_mod_time_;
+ bool for_loader_;
std::unique_ptr<Asset> resources_asset_;
std::unique_ptr<Asset> idmap_asset_;
std::unique_ptr<const LoadedArsc> loaded_arsc_;
+ std::unique_ptr<const LoadedIdmap> loaded_idmap_;
};
} // namespace android
diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h
index 9d12a35395c9..053dbb7864c6 100644
--- a/libs/androidfw/include/androidfw/Asset.h
+++ b/libs/androidfw/include/androidfw/Asset.h
@@ -121,6 +121,11 @@ public:
*/
const char* getAssetSource(void) const { return mAssetSource.string(); }
+ /*
+ * Create the asset from a file descriptor.
+ */
+ static Asset* createFromFd(const int fd, const char* fileName, AccessMode mode);
+
protected:
/*
* Adds this Asset to the global Asset list for debugging and
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 1e2b36cb1703..20e40234b418 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -122,7 +122,11 @@ class AssetManager2 {
// Returns the DynamicRefTable for the ApkAssets represented by the cookie.
// This may be nullptr if the APK represented by `cookie` has no resource table.
- const DynamicRefTable* GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const;
+ std::shared_ptr<const DynamicRefTable> GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const;
+
+ // Returns a string representation of the overlayable API of a package.
+ bool GetOverlayablesToString(const android::StringPiece& package_name,
+ std::string* out) const;
const std::unordered_map<std::string, std::string>*
GetOverlayableMapForPackage(uint32_t package_id) const;
@@ -232,12 +236,14 @@ class AssetManager2 {
ResTable_config* in_out_selected_config, uint32_t* in_out_flags,
uint32_t* out_last_reference) const;
- // Enables or disables resource resolution logging. Clears stored steps when
- // disabled.
+ // Resets the resource resolution structures in preparation for the next resource retrieval.
+ void ResetResourceResolution() const;
+
+ // Enables or disables resource resolution logging. Clears stored steps when disabled.
void SetResourceResolutionLoggingEnabled(bool enabled);
- // Returns formatted log of last resource resolution path, or empty if no
- // resource has been resolved yet.
+ // Returns formatted log of last resource resolution path, or empty if no resource has been
+ // resolved yet.
std::string GetLastResourceResolution() const;
const std::vector<uint32_t> GetBagResIdStack(uint32_t resid);
@@ -260,7 +266,7 @@ class AssetManager2 {
void ForEachPackage(const std::function<bool(const std::string&, uint8_t)> func) const {
for (const PackageGroup& package_group : package_groups_) {
if (!func(package_group.packages_.front().loaded_package_->GetPackageName(),
- package_group.dynamic_ref_table.mAssignedPackageId)) {
+ package_group.dynamic_ref_table->mAssignedPackageId)) {
return;
}
}
@@ -271,6 +277,50 @@ class AssetManager2 {
private:
DISALLOW_COPY_AND_ASSIGN(AssetManager2);
+ // A collection of configurations and their associated ResTable_type that match the current
+ // AssetManager configuration.
+ struct FilteredConfigGroup {
+ std::vector<ResTable_config> configurations;
+ std::vector<const ResTable_type*> types;
+ };
+
+ // Represents an single package.
+ struct ConfiguredPackage {
+ // A pointer to the immutable, loaded package info.
+ const LoadedPackage* loaded_package_;
+
+ // A mutable AssetManager-specific list of configurations that match the AssetManager's
+ // current configuration. This is used as an optimization to avoid checking every single
+ // candidate configuration when looking up resources.
+ ByteBucketArray<FilteredConfigGroup> filtered_configs_;
+ };
+
+ // Represents a Runtime Resource Overlay that overlays resources in the logical package.
+ struct ConfiguredOverlay {
+ // The set of package groups that overlay this package group.
+ IdmapResMap overlay_res_maps_;
+
+ // The cookie of the overlay assets.
+ ApkAssetsCookie cookie;
+ };
+
+ // Represents a logical package, which can be made up of many individual packages. Each package
+ // in a PackageGroup shares the same package name and package ID.
+ struct PackageGroup {
+ // The set of packages that make-up this group.
+ std::vector<ConfiguredPackage> packages_;
+
+ // The cookies associated with each package in the group. They share the same order as
+ // packages_.
+ std::vector<ApkAssetsCookie> cookies_;
+
+ // Runtime Resource Overlays that overlay resources in this package group.
+ std::vector<ConfiguredOverlay> overlays_;
+
+ // A library reference table that contains build-package ID to runtime-package ID mappings.
+ std::shared_ptr<DynamicRefTable> dynamic_ref_table = std::make_shared<DynamicRefTable>();
+ };
+
// Finds the best entry for `resid` from the set of ApkAssets. The entry can be a simple
// Res_value, or a complex map/bag type. If successful, it is available in `out_entry`.
// Returns kInvalidCookie on failure. Otherwise, the return value is the cookie associated with
@@ -291,6 +341,11 @@ class AssetManager2 {
ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match,
bool ignore_configuration, FindEntryResult* out_entry) const;
+ ApkAssetsCookie FindEntryInternal(const PackageGroup& package_group, uint8_t type_idx,
+ uint16_t entry_idx, const ResTable_config& desired_config,
+ bool /*stop_at_first_match*/,
+ bool ignore_configuration, FindEntryResult* out_entry) const;
+
// Assigns package IDs to all shared library ApkAssets.
// Should be called whenever the ApkAssets are changed.
void BuildDynamicRefTable();
@@ -303,49 +358,20 @@ class AssetManager2 {
// This should always be called when mutating the AssetManager's configuration or ApkAssets set.
void RebuildFilterList(bool filter_incompatible_configs = true);
+ // Retrieves the APK paths of overlays that overlay non-system packages.
+ std::set<std::string> GetNonSystemOverlayPaths() const;
+
// AssetManager2::GetBag(resid) wraps this function to track which resource ids have already
// been seen while traversing bag parents.
const ResolvedBag* GetBag(uint32_t resid, std::vector<uint32_t>& child_resids);
// Retrieve the assigned package id of the package if loaded into this AssetManager
- uint8_t GetAssignedPackageId(const LoadedPackage* package);
+ uint8_t GetAssignedPackageId(const LoadedPackage* package) const;
// The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
// have a longer lifetime.
std::vector<const ApkAssets*> apk_assets_;
- // A collection of configurations and their associated ResTable_type that match the current
- // AssetManager configuration.
- struct FilteredConfigGroup {
- std::vector<ResTable_config> configurations;
- std::vector<const ResTable_type*> types;
- };
-
- // Represents an single package.
- struct ConfiguredPackage {
- // A pointer to the immutable, loaded package info.
- const LoadedPackage* loaded_package_;
-
- // A mutable AssetManager-specific list of configurations that match the AssetManager's
- // current configuration. This is used as an optimization to avoid checking every single
- // candidate configuration when looking up resources.
- ByteBucketArray<FilteredConfigGroup> filtered_configs_;
- };
-
- // Represents a logical package, which can be made up of many individual packages. Each package
- // in a PackageGroup shares the same package name and package ID.
- struct PackageGroup {
- // The set of packages that make-up this group.
- std::vector<ConfiguredPackage> packages_;
-
- // The cookies associated with each package in the group. They share the same order as
- // packages_.
- std::vector<ApkAssetsCookie> cookies_;
-
- // A library reference table that contains build-package ID to runtime-package ID mappings.
- DynamicRefTable dynamic_ref_table;
- };
-
// DynamicRefTables for shared library package resolution.
// These are ordered according to apk_assets_. The mappings may change depending on what is
// in apk_assets_, therefore they must be stored in the AssetManager and not in the
@@ -378,7 +404,13 @@ class AssetManager2 {
enum class Type {
INITIAL,
BETTER_MATCH,
- OVERLAID
+ BETTER_MATCH_LOADER,
+ OVERLAID,
+ OVERLAID_LOADER,
+ SKIPPED,
+ SKIPPED_LOADER,
+ NO_ENTRY,
+ NO_ENTRY_LOADER,
};
// Marks what kind of override this step was.
@@ -408,7 +440,7 @@ class AssetManager2 {
};
// Record of the last resolved resource's resolution path.
- mutable Resolution last_resolution;
+ mutable Resolution last_resolution_;
};
class Theme {
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index fd02e6f63b74..ab4c9c204842 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -20,20 +20,122 @@
#include <memory>
#include <string>
#include <unordered_map>
+#include <variant>
#include "android-base/macros.h"
-
#include "androidfw/StringPiece.h"
+#include "androidfw/ResourceTypes.h"
+#include "utils/ByteOrder.h"
namespace android {
-struct Idmap_header;
-struct IdmapEntry_header;
+class LoadedIdmap;
+class IdmapResMap;
+
+// A string pool for overlay apk assets. The string pool holds the strings of the overlay resources
+// table and additionally allows for loading strings from the idmap string pool. The idmap string
+// pool strings are offset after the end of the overlay resource table string pool entries so
+// queries for strings defined inline in the idmap do not conflict with queries for overlay
+// resource table strings.
+class OverlayStringPool : public ResStringPool {
+ public:
+ virtual ~OverlayStringPool();
+ virtual const char16_t* stringAt(size_t idx, size_t* outLen) const;
+ virtual const char* string8At(size_t idx, size_t* outLen) const;
+
+ explicit OverlayStringPool(const LoadedIdmap* loaded_idmap);
+ private:
+ const Idmap_data_header* data_header_;
+ const ResStringPool* idmap_string_pool_;
+};
+
+// A dynamic reference table for loaded overlay packages that rewrites the resource id of overlay
+// resources to the resource id of corresponding target resources.
+class OverlayDynamicRefTable : public DynamicRefTable {
+ public:
+ virtual ~OverlayDynamicRefTable() = default;
+ virtual status_t lookupResourceId(uint32_t* resId) const;
+
+ private:
+ explicit OverlayDynamicRefTable(const Idmap_data_header* data_header,
+ const Idmap_overlay_entry* entries,
+ uint8_t target_assigned_package_id);
+
+ // Rewrites a compile-time overlay resource id to the runtime resource id of corresponding target
+ // resource.
+ virtual status_t lookupResourceIdNoRewrite(uint32_t* resId) const;
+
+ const Idmap_data_header* data_header_;
+ const Idmap_overlay_entry* entries_;
+ const int8_t target_assigned_package_id_;
+
+ friend LoadedIdmap;
+ friend IdmapResMap;
+};
+
+// A mapping of target resource ids to a values or resource ids that should overlay the target.
+class IdmapResMap {
+ public:
+ // Represents the result of a idmap lookup. The result can be one of three possibillities:
+ // 1) The result is a resource id which represents the overlay resource that should act as an
+ // alias of the target resource.
+ // 2) The result is a table entry which overlays the type and value of the target resource.
+ // 3) The result is neither and the target resource is not overlaid.
+ class Result {
+ public:
+ Result() : data_(nullptr) {};
+ explicit Result(uint32_t value) : data_(value) {};
+ explicit Result(ResTable_entry_handle&& value) : data_(value) { };
+
+ // Returns `true` if the resource is overlaid.
+ inline explicit operator bool() const {
+ return !std::get_if<nullptr_t>(&data_);
+ }
+
+ inline bool IsResourceId() const {
+ return std::get_if<uint32_t>(&data_);
+ }
+
+ inline uint32_t GetResourceId() const {
+ return *std::get_if<uint32_t>(&data_);
+ }
+
+ inline bool IsTableEntry() const {
+ return std::get_if<ResTable_entry_handle>(&data_);
+ }
+
+ inline const ResTable_entry_handle& GetTableEntry() const {
+ return *std::get_if<ResTable_entry_handle>(&data_);
+ }
+
+ private:
+ std::variant<uint32_t, nullptr_t, ResTable_entry_handle> data_;
+ };
+
+ // Looks up the value that overlays the target resource id.
+ Result Lookup(uint32_t target_res_id) const;
+
+ inline const OverlayDynamicRefTable* GetOverlayDynamicRefTable() const {
+ return overlay_ref_table_;
+ }
+
+ private:
+ explicit IdmapResMap(const Idmap_data_header* data_header,
+ const Idmap_target_entry* entries,
+ uint8_t target_assigned_package_id,
+ const OverlayDynamicRefTable* overlay_ref_table);
+
+ const Idmap_data_header* data_header_;
+ const Idmap_target_entry* entries_;
+ const uint8_t target_assigned_package_id_;
+ const OverlayDynamicRefTable* overlay_ref_table_;
+
+ friend LoadedIdmap;
+};
// Represents a loaded/parsed IDMAP for a Runtime Resource Overlay (RRO).
-// An RRO and its target APK have different resource IDs assigned to their resources. Overlaying
-// a resource is done by resource name. An IDMAP is a generated mapping between the resource IDs
-// of the RRO and the target APK for each resource with the same name.
+// An RRO and its target APK have different resource IDs assigned to their resources.
+// An IDMAP is a generated mapping between the resource IDs of the RRO and the target APK.
// A LoadedIdmap can be set alongside the overlay's LoadedArsc to allow the overlay ApkAssets to
// masquerade as the target ApkAssets resources.
class LoadedIdmap {
@@ -41,34 +143,52 @@ class LoadedIdmap {
// Loads an IDMAP from a chunk of memory. Returns nullptr if the IDMAP data was malformed.
static std::unique_ptr<const LoadedIdmap> Load(const StringPiece& idmap_data);
- // Performs a lookup of the expected entry ID for the given IDMAP entry header.
- // Returns true if the mapping exists and fills `output_entry_id` with the result.
- static bool Lookup(const IdmapEntry_header* header, uint16_t input_entry_id,
- uint16_t* output_entry_id);
-
- // Returns the package ID for which this overlay should apply.
- uint8_t TargetPackageId() const;
-
// Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was generated.
inline const std::string& OverlayApkPath() const {
return overlay_apk_path_;
}
- // Returns the mapping of target entry ID to overlay entry ID for the given target type.
- const IdmapEntry_header* GetEntryMapForType(uint8_t type_id) const;
+ // Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was generated.
+ inline const std::string& TargetApkPath() const {
+ return target_apk_path_;
+ }
+
+ // Returns a mapping from target resource ids to overlay values.
+ inline const IdmapResMap GetTargetResourcesMap(
+ uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table) const {
+ return IdmapResMap(data_header_, target_entries_, target_assigned_package_id,
+ overlay_ref_table);
+ }
+
+ // Returns a dynamic reference table for a loaded overlay package.
+ inline const OverlayDynamicRefTable GetOverlayDynamicRefTable(
+ uint8_t target_assigned_package_id) const {
+ return OverlayDynamicRefTable(data_header_, overlay_entries_, target_assigned_package_id);
+ }
protected:
// Exposed as protected so that tests can subclass and mock this class out.
LoadedIdmap() = default;
- const Idmap_header* header_ = nullptr;
+ const Idmap_header* header_;
+ const Idmap_data_header* data_header_;
+ const Idmap_target_entry* target_entries_;
+ const Idmap_overlay_entry* overlay_entries_;
+ const std::unique_ptr<ResStringPool> string_pool_;
+
std::string overlay_apk_path_;
- std::unordered_map<uint8_t, const IdmapEntry_header*> type_map_;
+ std::string target_apk_path_;
private:
DISALLOW_COPY_AND_ASSIGN(LoadedIdmap);
- explicit LoadedIdmap(const Idmap_header* header);
+ explicit LoadedIdmap(const Idmap_header* header,
+ const Idmap_data_header* data_header,
+ const Idmap_target_entry* target_entries,
+ const Idmap_overlay_entry* overlay_entries,
+ ResStringPool* string_pool);
+
+ friend OverlayStringPool;
};
} // namespace android
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 950f5413f550..ba1beaa7827c 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -51,10 +51,6 @@ struct TypeSpec {
// and under which configurations it varies.
const ResTable_typeSpec* type_spec;
- // Pointer to the mmapped data where the IDMAP mappings for this type
- // exist. May be nullptr if no IDMAP exists.
- const IdmapEntry_header* idmap_entries;
-
// The number of types that follow this struct.
// There is a type for each configuration that entries are defined for.
size_t type_count;
@@ -135,9 +131,9 @@ class LoadedPackage {
return iterator(this, resource_ids_.size() + 1, 0);
}
- static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk,
- const LoadedIdmap* loaded_idmap, bool system,
- bool load_as_shared_library);
+ static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk, bool system,
+ bool load_as_shared_library,
+ bool load_as_custom_loader);
~LoadedPackage();
@@ -182,9 +178,9 @@ class LoadedPackage {
return system_;
}
- // Returns true if this package is from an overlay ApkAssets.
- inline bool IsOverlay() const {
- return overlay_;
+ // Returns true if this package is a custom loader and should behave like an overlay
+ inline bool IsCustomLoader() const {
+ return custom_loader_;
}
// Returns the map of package name to package ID used in this LoadedPackage. At runtime, a
@@ -216,9 +212,6 @@ class LoadedPackage {
const TypeSpecPtr& ptr = type_specs_[i];
if (ptr != nullptr) {
uint8_t type_id = ptr->type_spec->id;
- if (ptr->idmap_entries != nullptr) {
- type_id = ptr->idmap_entries->target_type_id;
- }
f(ptr.get(), type_id - 1);
}
}
@@ -259,7 +252,7 @@ class LoadedPackage {
int type_id_offset_ = 0;
bool dynamic_ = false;
bool system_ = false;
- bool overlay_ = false;
+ bool custom_loader_ = false;
bool defines_overlayable_ = false;
ByteBucketArray<TypeSpecPtr> type_specs_;
@@ -282,7 +275,8 @@ class LoadedArsc {
static std::unique_ptr<const LoadedArsc> Load(const StringPiece& data,
const LoadedIdmap* loaded_idmap = nullptr,
bool system = false,
- bool load_as_shared_library = false);
+ bool load_as_shared_library = false,
+ bool for_loader = false);
// Create an empty LoadedArsc. This is used when an APK has no resources.arsc.
static std::unique_ptr<const LoadedArsc> CreateEmpty();
@@ -290,7 +284,7 @@ class LoadedArsc {
// Returns the string pool where all string resource values
// (Res_value::dataType == Res_value::TYPE_STRING) are indexed.
inline const ResStringPool* GetStringPool() const {
- return &global_string_pool_;
+ return global_string_pool_.get();
}
// Gets a pointer to the package with the specified package ID, or nullptr if no such package
@@ -311,9 +305,17 @@ class LoadedArsc {
DISALLOW_COPY_AND_ASSIGN(LoadedArsc);
LoadedArsc() = default;
- bool LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library);
+ bool LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library,
+ bool for_loader);
+
+ static std::unique_ptr<const LoadedArsc> LoadData(std::unique_ptr<LoadedArsc>& loaded_arsc,
+ const char* data,
+ size_t length,
+ const LoadedIdmap* loaded_idmap = nullptr,
+ bool load_as_shared_library = false,
+ bool for_loader = false);
- ResStringPool global_string_pool_;
+ std::unique_ptr<ResStringPool> global_string_pool_ = util::make_unique<ResStringPool>();
std::vector<std::unique_ptr<const LoadedPackage>> packages_;
bool system_ = false;
};
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index fc635aaeb0d8..b20e6579fda7 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -39,7 +39,7 @@
namespace android {
constexpr const static uint32_t kIdmapMagic = 0x504D4449u;
-constexpr const static uint32_t kIdmapCurrentVersion = 0x00000001u;
+constexpr const static uint32_t kIdmapCurrentVersion = 0x00000002u;
/**
* In C++11, char16_t is defined as *at least* 16 bits. We do a lot of
@@ -492,7 +492,7 @@ class ResStringPool
public:
ResStringPool();
ResStringPool(const void* data, size_t size, bool copyData=false);
- ~ResStringPool();
+ virtual ~ResStringPool();
void setToEmpty();
status_t setTo(const void* data, size_t size, bool copyData=false);
@@ -506,10 +506,10 @@ public:
inline const char16_t* stringAt(const ResStringPool_ref& ref, size_t* outLen) const {
return stringAt(ref.index, outLen);
}
- const char16_t* stringAt(size_t idx, size_t* outLen) const;
+ virtual const char16_t* stringAt(size_t idx, size_t* outLen) const;
// Note: returns null if the string pool is not UTF8.
- const char* string8At(size_t idx, size_t* outLen) const;
+ virtual const char* string8At(size_t idx, size_t* outLen) const;
// Return string whether the pool is UTF8 or UTF16. Does not allow you
// to distinguish null.
@@ -523,6 +523,8 @@ public:
size_t size() const;
size_t styleCount() const;
size_t bytes() const;
+ const void* data() const;
+
bool isSorted() const;
bool isUTF8() const;
@@ -810,7 +812,7 @@ public:
* The tree stores a clone of the specified DynamicRefTable, so any changes to the original
* DynamicRefTable will not affect this tree after instantiation.
**/
- explicit ResXMLTree(const DynamicRefTable* dynamicRefTable);
+ explicit ResXMLTree(std::shared_ptr<const DynamicRefTable> dynamicRefTable);
ResXMLTree();
~ResXMLTree();
@@ -825,7 +827,7 @@ private:
status_t validateNode(const ResXMLTree_node* node) const;
- std::unique_ptr<const DynamicRefTable> mDynamicRefTable;
+ std::shared_ptr<const DynamicRefTable> mDynamicRefTable;
status_t mError;
void* mOwnedData;
@@ -1582,6 +1584,50 @@ struct ResTable_map
Res_value value;
};
+
+// A ResTable_entry variant that either holds an unmanaged pointer to a constant ResTable_entry or
+// holds a ResTable_entry which is tied to the lifetime of the handle.
+class ResTable_entry_handle {
+ public:
+ ResTable_entry_handle() = default;
+
+ ResTable_entry_handle(const ResTable_entry_handle& handle) {
+ entry_ = handle.entry_;
+ }
+
+ ResTable_entry_handle(ResTable_entry_handle&& handle) noexcept {
+ entry_ = handle.entry_;
+ }
+
+ inline static ResTable_entry_handle managed(ResTable_entry* entry) {
+ return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry));
+ }
+
+ inline static ResTable_entry_handle unmanaged(const ResTable_entry* entry) {
+ return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry, [](auto /*p */){}));
+ }
+
+ inline ResTable_entry_handle& operator=(const ResTable_entry_handle& handle) noexcept {
+ entry_ = handle.entry_;
+ return *this;
+ }
+
+ inline ResTable_entry_handle& operator=(ResTable_entry_handle&& handle) noexcept {
+ entry_ = handle.entry_;
+ return *this;
+ }
+
+ inline const ResTable_entry* operator*() & {
+ return entry_.get();
+ }
+
+ private:
+ explicit ResTable_entry_handle(std::shared_ptr<const ResTable_entry> entry)
+ : entry_(std::move(entry)) { }
+
+ std::shared_ptr<const ResTable_entry> entry_;
+};
+
/**
* A package-id to package name mapping for any shared libraries used
* in this resource table. The package-id's encoded in this resource
@@ -1666,7 +1712,8 @@ struct ResTable_overlayable_policy_header
uint32_t entry_count;
};
-struct alignas(uint32_t) Idmap_header {
+#pragma pack(push, 1)
+struct Idmap_header {
// Always 0x504D4449 ('IDMP')
uint32_t magic;
@@ -1677,18 +1724,28 @@ struct alignas(uint32_t) Idmap_header {
uint8_t target_path[256];
uint8_t overlay_path[256];
+};
- uint16_t target_package_id;
- uint16_t type_count;
-} __attribute__((packed));
+struct Idmap_data_header {
+ uint8_t target_package_id;
+ uint8_t overlay_package_id;
+ uint32_t target_entry_count;
+ uint32_t overlay_entry_count;
+ uint32_t string_pool_index_offset;
+ uint32_t string_pool_length;
+};
-struct alignas(uint32_t) IdmapEntry_header {
- uint16_t target_type_id;
- uint16_t overlay_type_id;
- uint16_t entry_count;
- uint16_t entry_id_offset;
- uint32_t entries[0];
-} __attribute__((packed));
+struct Idmap_target_entry {
+ uint32_t target_id;
+ uint8_t type;
+ uint32_t value;
+};
+
+struct Idmap_overlay_entry {
+ uint32_t overlay_id;
+ uint32_t target_id;
+};
+#pragma pack(pop)
class AssetManager2;
@@ -1706,6 +1763,7 @@ class DynamicRefTable
public:
DynamicRefTable();
DynamicRefTable(uint8_t packageId, bool appAsLib);
+ virtual ~DynamicRefTable() = default;
// Loads an unmapped reference table from the package.
status_t load(const ResTable_lib_header* const header);
@@ -1719,12 +1777,12 @@ public:
void addMapping(uint8_t buildPackageId, uint8_t runtimePackageId);
- // Creates a new clone of the reference table
- std::unique_ptr<DynamicRefTable> clone() const;
+ // Returns whether or not the value must be looked up.
+ bool requiresLookup(const Res_value* value) const;
// Performs the actual conversion of build-time resource ID to run-time
// resource ID.
- status_t lookupResourceId(uint32_t* resId) const;
+ virtual status_t lookupResourceId(uint32_t* resId) const;
status_t lookupResourceValue(Res_value* value) const;
inline const KeyedVector<String16, uint8_t>& entries() const {
diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp
index e2b9f0040989..0f2ee6fb968e 100644
--- a/libs/androidfw/tests/ApkAssets_test.cpp
+++ b/libs/androidfw/tests/ApkAssets_test.cpp
@@ -79,39 +79,6 @@ TEST(ApkAssetsTest, LoadApkAsSharedLibrary) {
EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic());
}
-TEST(ApkAssetsTest, LoadApkWithIdmap) {
- std::string contents;
- ResTable target_table;
- const std::string target_path = GetTestDataPath() + "/basic/basic.apk";
- ASSERT_TRUE(ReadFileFromZipToString(target_path, "resources.arsc", &contents));
- ASSERT_THAT(target_table.add(contents.data(), contents.size(), 0, true /*copyData*/),
- Eq(NO_ERROR));
-
- ResTable overlay_table;
- const std::string overlay_path = GetTestDataPath() + "/overlay/overlay.apk";
- ASSERT_TRUE(ReadFileFromZipToString(overlay_path, "resources.arsc", &contents));
- ASSERT_THAT(overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/),
- Eq(NO_ERROR));
-
- util::unique_cptr<void> idmap_data;
- void* temp_data;
- size_t idmap_len;
-
- ASSERT_THAT(target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(),
- overlay_path.c_str(), &temp_data, &idmap_len),
- Eq(NO_ERROR));
- idmap_data.reset(temp_data);
-
- TemporaryFile tf;
- ASSERT_TRUE(base::WriteFully(tf.fd, idmap_data.get(), idmap_len));
- close(tf.fd);
-
- // Open something so that the destructor of TemporaryFile closes a valid fd.
- tf.fd = open("/dev/null", O_WRONLY);
-
- ASSERT_THAT(ApkAssets::LoadOverlay(tf.path), NotNull());
-}
-
TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) {
std::unique_ptr<const ApkAssets> loaded_apk =
ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 40c8e46e4d84..b3190be8caf1 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -707,7 +707,7 @@ TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) {
EXPECT_EQ("", resultDisabled);
}
-TEST_F(AssetManager2Test, GetOverlayableMap) {
+TEST_F(AssetManager2Test, GetOverlayablesToString) {
ResTable_config desired_config;
memset(&desired_config, 0, sizeof(desired_config));
@@ -718,9 +718,17 @@ TEST_F(AssetManager2Test, GetOverlayableMap) {
const auto map = assetmanager.GetOverlayableMapForPackage(0x7f);
ASSERT_NE(nullptr, map);
- ASSERT_EQ(2, map->size());
+ ASSERT_EQ(3, map->size());
ASSERT_EQ(map->at("OverlayableResources1"), "overlay://theme");
ASSERT_EQ(map->at("OverlayableResources2"), "overlay://com.android.overlayable");
+ ASSERT_EQ(map->at("OverlayableResources3"), "");
+
+ std::string api;
+ ASSERT_TRUE(assetmanager.GetOverlayablesToString("com.android.overlayable", &api));
+ ASSERT_EQ(api.find("not_overlayable"), std::string::npos);
+ ASSERT_NE(api.find("resource='com.android.overlayable:string/overlayable2' overlayable='OverlayableResources1' actor='overlay://theme' policy='0x0000000a'\n"),
+ std::string::npos);
+
}
} // namespace android
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index 10b83a75304d..b679672ab34e 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -14,114 +14,231 @@
* limitations under the License.
*/
+#include "android-base/file.h"
+#include "androidfw/ApkAssets.h"
+#include "androidfw/AssetManager2.h"
#include "androidfw/ResourceTypes.h"
#include "utils/String16.h"
#include "utils/String8.h"
#include "TestHelpers.h"
-#include "data/basic/R.h"
+#include "data/overlay/R.h"
+#include "data/overlayable/R.h"
+#include "data/system/R.h"
-using ::com::android::basic::R;
+namespace overlay = com::android::overlay;
+namespace overlayable = com::android::overlayable;
namespace android {
+namespace {
+
class IdmapTest : public ::testing::Test {
protected:
void SetUp() override {
- std::string contents;
- ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc",
- &contents));
- ASSERT_EQ(NO_ERROR, target_table_.add(contents.data(), contents.size(), 0, true));
-
- ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk",
- "resources.arsc", &overlay_data_));
- ResTable overlay_table;
- ASSERT_EQ(NO_ERROR, overlay_table.add(overlay_data_.data(), overlay_data_.size()));
-
- char target_name[256] = "com.android.basic";
- ASSERT_EQ(NO_ERROR, overlay_table.createIdmap(target_table_, 0, 0, target_name, target_name,
- &data_, &data_size_));
- }
+ // Move to the test data directory so the idmap can locate the overlay APK.
+ std::string original_path = base::GetExecutableDirectory();
+ chdir(GetTestDataPath().c_str());
+
+ system_assets_ = ApkAssets::Load("system/system.apk");
+ ASSERT_NE(nullptr, system_assets_);
- void TearDown() override {
- ::free(data_);
+ overlay_assets_ = ApkAssets::LoadOverlay("overlay/overlay.idmap");
+ ASSERT_NE(nullptr, overlay_assets_);
+
+ overlayable_assets_ = ApkAssets::Load("overlayable/overlayable.apk");
+ ASSERT_NE(nullptr, overlayable_assets_);
+ chdir(original_path.c_str());
}
- ResTable target_table_;
- std::string overlay_data_;
- void* data_ = nullptr;
- size_t data_size_ = 0;
+ protected:
+ std::unique_ptr<const ApkAssets> system_assets_;
+ std::unique_ptr<const ApkAssets> overlay_assets_;
+ std::unique_ptr<const ApkAssets> overlayable_assets_;
};
-TEST_F(IdmapTest, CanLoadIdmap) {
- ASSERT_EQ(NO_ERROR,
- target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_));
+std::string GetStringFromApkAssets(const AssetManager2& asset_manager, const Res_value& value,
+ ApkAssetsCookie cookie) {
+ auto assets = asset_manager.GetApkAssets();
+ const ResStringPool* string_pool = assets[cookie]->GetLoadedArsc()->GetStringPool();
+ return GetStringFromPool(string_pool, value.data);
+}
+
}
TEST_F(IdmapTest, OverlayOverridesResourceValue) {
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
Res_value val;
- ssize_t block = target_table_.getResource(R::string::test2, &val, false);
- ASSERT_GE(block, 0);
- ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
- const ResStringPool* pool = target_table_.getTableStringBlock(block);
- ASSERT_TRUE(pool != NULL);
- ASSERT_LT(val.data, pool->size());
-
- size_t str_len;
- const char16_t* target_str16 = pool->stringAt(val.data, &str_len);
- ASSERT_TRUE(target_str16 != NULL);
- ASSERT_EQ(String16("test2"), String16(target_str16, str_len));
-
- ASSERT_EQ(NO_ERROR,
- target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_));
-
- ssize_t new_block = target_table_.getResource(R::string::test2, &val, false);
- ASSERT_GE(new_block, 0);
- ASSERT_NE(block, new_block);
- ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
- pool = target_table_.getTableStringBlock(new_block);
- ASSERT_TRUE(pool != NULL);
- ASSERT_LT(val.data, pool->size());
-
- target_str16 = pool->stringAt(val.data, &str_len);
- ASSERT_TRUE(target_str16 != NULL);
- ASSERT_EQ(String16("test2-overlay"), String16(target_str16, str_len));
+ ResTable_config config;
+ uint32_t flags;
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable5,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ ASSERT_EQ(cookie, 2U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
+ ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "Overlay One");
}
-TEST_F(IdmapTest, OverlaidResourceHasSameName) {
- ASSERT_EQ(NO_ERROR,
- target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_));
+TEST_F(IdmapTest, OverlayOverridesResourceValueUsingDifferentPackage) {
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
+ Res_value val;
+ ResTable_config config;
+ uint32_t flags;
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable10,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ ASSERT_EQ(cookie, 0U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
+ ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "yes");
+}
+
+TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInternalResource) {
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
+ Res_value val;
+ ResTable_config config;
+ uint32_t flags;
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable8,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ ASSERT_EQ(cookie, 2U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_REFERENCE);
+ ASSERT_EQ(val.data, (overlay::R::string::internal & 0x00ffffff) | (0x02 << 24));
+}
+
+TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineInteger) {
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
+ Res_value val;
+ ResTable_config config;
+ uint32_t flags;
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::integer::config_integer,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ ASSERT_EQ(cookie, 2U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_INT_DEC);
+ ASSERT_EQ(val.data, 42);
+}
+
+TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineString) {
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
+ Res_value val;
+ ResTable_config config;
+ uint32_t flags;
+
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable11,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ ASSERT_EQ(cookie, 2U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
+ ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "Hardcoded string");
+}
- ResTable::resource_name res_name;
- ASSERT_TRUE(target_table_.getResourceName(R::array::integerArray1, true, &res_name));
+TEST_F(IdmapTest, OverlayOverridesResourceValueUsingOverlayingResource) {
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
+ Res_value val;
+ ResTable_config config;
+ uint32_t flags;
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable9,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ ASSERT_EQ(cookie, 2U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_REFERENCE);
+ ASSERT_EQ(val.data, overlayable::R::string::overlayable7);
+}
- ASSERT_TRUE(res_name.package != NULL);
- ASSERT_TRUE(res_name.type != NULL);
- ASSERT_TRUE(res_name.name8 != NULL);
+TEST_F(IdmapTest, OverlayOverridesXmlParser) {
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
+ Res_value val;
+ ResTable_config config;
+ uint32_t flags;
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::layout::hello_view,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ ASSERT_EQ(cookie, 2U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
+ ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "res/layout/hello_view.xml");
+
+ auto asset = asset_manager.OpenNonAsset("res/layout/hello_view.xml", cookie,
+ Asset::ACCESS_RANDOM);
+ auto dynamic_ref_table = asset_manager.GetDynamicRefTableForCookie(cookie);
+ auto xml_tree = util::make_unique<ResXMLTree>(std::move(dynamic_ref_table));
+ status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), false);
+ ASSERT_EQ(err, NO_ERROR);
+
+ while (xml_tree->next() != ResXMLParser::START_TAG) { }
+
+ // The resource id of @id/hello_view should be rewritten to the resource id/hello_view within the
+ // target.
+ ASSERT_EQ(xml_tree->getAttributeNameResID(0), 0x010100d0 /* android:attr/id */);
+ ASSERT_EQ(xml_tree->getAttributeDataType(0), Res_value::TYPE_REFERENCE);
+ ASSERT_EQ(xml_tree->getAttributeData(0), overlayable::R::id::hello_view);
+
+ // The resource id of @android:string/yes should not be rewritten even though it overlays
+ // string/overlayable10 in the target.
+ ASSERT_EQ(xml_tree->getAttributeNameResID(1), 0x0101014f /* android:attr/text */);
+ ASSERT_EQ(xml_tree->getAttributeDataType(1), Res_value::TYPE_REFERENCE);
+ ASSERT_EQ(xml_tree->getAttributeData(1), 0x01040013 /* android:string/yes */);
+
+ // The resource id of the attribute within the overlay should be rewritten to the resource id of
+ // the attribute in the target.
+ ASSERT_EQ(xml_tree->getAttributeNameResID(2), overlayable::R::attr::max_lines);
+ ASSERT_EQ(xml_tree->getAttributeDataType(2), Res_value::TYPE_INT_DEC);
+ ASSERT_EQ(xml_tree->getAttributeData(2), 4);
+}
- EXPECT_EQ(String16("com.android.basic"), String16(res_name.package, res_name.packageLen));
- EXPECT_EQ(String16("array"), String16(res_name.type, res_name.typeLen));
- EXPECT_EQ(String8("integerArray1"), String8(res_name.name8, res_name.nameLen));
+TEST_F(IdmapTest, OverlaidResourceHasSameName) {
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(),
+ overlay_assets_.get()});
+
+ AssetManager2::ResourceName name;
+ ASSERT_TRUE(asset_manager.GetResourceName(overlayable::R::string::overlayable9, &name));
+ ASSERT_EQ(std::string(name.package), "com.android.overlayable");
+ ASSERT_EQ(String16(name.type16), u"string");
+ ASSERT_EQ(std::string(name.entry), "overlayable9");
}
-constexpr const uint32_t kNonOverlaidResourceId = 0x7fff0000u;
+TEST_F(IdmapTest, OverlayLoaderInterop) {
+ std::string contents;
+ auto loader_assets = ApkAssets::LoadArsc(GetTestDataPath() + "/loader/resources.arsc",
+ /* for_loader */ true);
-TEST_F(IdmapTest, OverlayDoesNotIncludeNonOverlaidResources) {
- // First check that the resource we're trying to not include when overlaid is present when
- // the overlay is loaded as a standalone APK.
- ResTable table;
- ASSERT_EQ(NO_ERROR, table.add(overlay_data_.data(), overlay_data_.size(), 0, true));
+ AssetManager2 asset_manager;
+ asset_manager.SetApkAssets({overlayable_assets_.get(), loader_assets.get(),
+ overlay_assets_.get()});
Res_value val;
- ssize_t block = table.getResource(kNonOverlaidResourceId, &val, false /*mayBeBag*/);
- ASSERT_GE(block, 0);
-
- // Now add the overlay and verify that the unoverlaid resource is gone.
- ASSERT_EQ(NO_ERROR,
- target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_));
- block = target_table_.getResource(kNonOverlaidResourceId, &val, false /*mayBeBag*/);
- ASSERT_LT(block, 0);
+ ResTable_config config;
+ uint32_t flags;
+ ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable11,
+ false /* may_be_bag */,
+ 0 /* density_override */, &val, &config,
+ &flags);
+ std::cout << asset_manager.GetLastResourceResolution();
+ ASSERT_EQ(cookie, 1U);
+ ASSERT_EQ(val.dataType, Res_value::TYPE_STRING);
+ ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "loader");
}
} // namespace
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index d58e8d20c8aa..82dd33523c75 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -25,6 +25,7 @@
#include "data/overlayable/R.h"
#include "data/sparse/R.h"
#include "data/styles/R.h"
+#include "data/system/R.h"
namespace app = com::android::app;
namespace basic = com::android::basic;
@@ -143,7 +144,7 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) {
"resources.arsc", &contents));
std::unique_ptr<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/,
+ LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, false /*system*/,
true /*load_as_shared_library*/);
ASSERT_THAT(loaded_arsc, NotNull());
@@ -221,67 +222,13 @@ TEST(LoadedArscTest, LoadOutOfOrderTypeSpecs) {
ASSERT_THAT(type_spec->types[0], NotNull());
}
-class MockLoadedIdmap : public LoadedIdmap {
- public:
- MockLoadedIdmap() : LoadedIdmap() {
- local_header_.magic = kIdmapMagic;
- local_header_.version = kIdmapCurrentVersion;
- local_header_.target_package_id = 0x08;
- local_header_.type_count = 1;
- header_ = &local_header_;
-
- entry_header = util::unique_cptr<IdmapEntry_header>(
- (IdmapEntry_header*)::malloc(sizeof(IdmapEntry_header) + sizeof(uint32_t)));
- entry_header->target_type_id = 0x03;
- entry_header->overlay_type_id = 0x02;
- entry_header->entry_id_offset = 1;
- entry_header->entry_count = 1;
- entry_header->entries[0] = 0x00000000u;
- type_map_[entry_header->overlay_type_id] = entry_header.get();
- }
-
- private:
- Idmap_header local_header_;
- util::unique_cptr<IdmapEntry_header> entry_header;
-};
-
-TEST(LoadedArscTest, LoadOverlay) {
- std::string contents;
- ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc",
- &contents));
-
- MockLoadedIdmap loaded_idmap;
-
- std::unique_ptr<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(StringPiece(contents), &loaded_idmap);
- ASSERT_THAT(loaded_arsc, NotNull());
-
- const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u);
- ASSERT_THAT(package, NotNull());
-
- const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0x03u - 1);
- ASSERT_THAT(type_spec, NotNull());
- ASSERT_THAT(type_spec->type_count, Ge(1u));
- ASSERT_THAT(type_spec->types[0], NotNull());
-
- // The entry being overlaid doesn't exist at the original entry index.
- ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0001u), IsNull());
-
- // Since this is an overlay, the actual entry ID must be mapped.
- ASSERT_THAT(type_spec->idmap_entries, NotNull());
- uint16_t target_entry_id = 0u;
- ASSERT_TRUE(LoadedIdmap::Lookup(type_spec->idmap_entries, 0x0001u, &target_entry_id));
- ASSERT_THAT(target_entry_id, Eq(0x0u));
- ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull());
-}
-
TEST(LoadedArscTest, LoadOverlayable) {
std::string contents;
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk",
"resources.arsc", &contents));
std::unique_ptr<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/,
+ LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, false /*system*/,
false /*load_as_shared_library*/);
ASSERT_THAT(loaded_arsc, NotNull());
@@ -382,9 +329,42 @@ TEST(LoadedArscTest, GetOverlayableMap) {
ASSERT_EQ(std::string("com.android.overlayable"), packages[0]->GetPackageName());
const auto map = packages[0]->GetOverlayableMap();
- ASSERT_EQ(2, map.size());
+ ASSERT_EQ(3, map.size());
ASSERT_EQ(map.at("OverlayableResources1"), "overlay://theme");
ASSERT_EQ(map.at("OverlayableResources2"), "overlay://com.android.overlayable");
+ ASSERT_EQ(map.at("OverlayableResources3"), "");
+}
+
+TEST(LoadedArscTest, LoadCustomLoader) {
+ std::string contents;
+
+ std::unique_ptr<Asset>
+ asset = ApkAssets::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc");
+
+ const StringPiece data(
+ reinterpret_cast<const char*>(asset->getBuffer(true /*wordAligned*/)),
+ asset->getLength());
+
+ std::unique_ptr<const LoadedArsc> loaded_arsc =
+ LoadedArsc::Load(data, nullptr, false, false, true);
+ ASSERT_THAT(loaded_arsc, NotNull());
+
+ const LoadedPackage* package =
+ loaded_arsc->GetPackageById(get_package_id(overlayable::R::string::overlayable11));
+ ASSERT_THAT(package, NotNull());
+ EXPECT_THAT(package->GetPackageName(), StrEq("com.android.loader"));
+ EXPECT_THAT(package->GetPackageId(), Eq(0x7f));
+
+ const uint8_t type_index = get_type_id(overlayable::R::string::overlayable11) - 1;
+ const uint16_t entry_index = get_entry_id(overlayable::R::string::overlayable11);
+
+ const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
+ ASSERT_THAT(type_spec, NotNull());
+ ASSERT_THAT(type_spec->type_count, Ge(1u));
+
+ const ResTable_type* type = type_spec->types[0];
+ ASSERT_THAT(type, NotNull());
+ ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull());
}
// structs with size fields (like Res_value, ResTable_entry) should be
diff --git a/libs/androidfw/tests/data/loader/AndroidManifest.xml b/libs/androidfw/tests/data/loader/AndroidManifest.xml
new file mode 100644
index 000000000000..4c0bb47f59bf
--- /dev/null
+++ b/libs/androidfw/tests/data/loader/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<manifest package="com.android.loader">
+ <application>
+ </application>
+</manifest>
diff --git a/libs/androidfw/tests/data/loader/build b/libs/androidfw/tests/data/loader/build
new file mode 100755
index 000000000000..457ec51ffe69
--- /dev/null
+++ b/libs/androidfw/tests/data/loader/build
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# 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.
+#
+
+set -e
+
+FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk
+
+rm resources.arsc
+aapt2 compile --dir res -o compiled.flata
+aapt2 link -I $FRAMEWORK_RES_APK --manifest AndroidManifest.xml -o loader.apk compiled.flata
+unzip loader.apk resources.arsc
+rm loader.apk
+rm compiled.flata
diff --git a/libs/androidfw/tests/data/loader/res/values/public.xml b/libs/androidfw/tests/data/loader/res/values/public.xml
new file mode 100644
index 000000000000..3293229104ce
--- /dev/null
+++ b/libs/androidfw/tests/data/loader/res/values/public.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<resources>
+ <public type="string" name="overlayable11" id="0x7f01000b" />
+</resources> \ No newline at end of file
diff --git a/libs/androidfw/tests/data/loader/res/values/values.xml b/libs/androidfw/tests/data/loader/res/values/values.xml
new file mode 100644
index 000000000000..0653536508f8
--- /dev/null
+++ b/libs/androidfw/tests/data/loader/res/values/values.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<resources>
+ <string name="overlayable11">loader</string>
+</resources>
diff --git a/libs/androidfw/tests/data/loader/resources.arsc b/libs/androidfw/tests/data/loader/resources.arsc
new file mode 100644
index 000000000000..2bdb288dbcab
--- /dev/null
+++ b/libs/androidfw/tests/data/loader/resources.arsc
Binary files differ
diff --git a/libs/androidfw/tests/data/overlay/AndroidManifest.xml b/libs/androidfw/tests/data/overlay/AndroidManifest.xml
index a56ac18e900b..28a11489fae0 100644
--- a/libs/androidfw/tests/data/overlay/AndroidManifest.xml
+++ b/libs/androidfw/tests/data/overlay/AndroidManifest.xml
@@ -15,7 +15,9 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.test.basic">
- <application>
- </application>
+ package="com.android.test.overlay">
+ <overlay
+ android:targetPackage="com.android.test.basic"
+ android:targetName="OverlayableResources3"
+ android:resourcesMap="@xml/overlays"/>
</manifest>
diff --git a/libs/androidfw/tests/data/overlay/R.h b/libs/androidfw/tests/data/overlay/R.h
new file mode 100644
index 000000000000..f3dbed29d7ee
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/R.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#ifndef TESTS_DATA_OVERLAY_R_H_
+#define TESTS_DATA_OVERLAY_R_H_
+
+#include <cstdint>
+
+namespace com {
+namespace android {
+namespace overlay {
+
+struct R {
+ struct string {
+ enum : uint32_t {
+ internal = 0x7f040000,
+ };
+ };
+};
+
+} // namespace overlay
+} // namespace android
+} // namespace com
+
+#endif /* TESTS_DATA_OVERLAY_R_H_ */
diff --git a/libs/androidfw/tests/data/overlay/build b/libs/androidfw/tests/data/overlay/build
index 716b1bd9db64..99dfd63051a7 100755
--- a/libs/androidfw/tests/data/overlay/build
+++ b/libs/androidfw/tests/data/overlay/build
@@ -17,6 +17,15 @@
set -e
+FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk
+
aapt2 compile --dir res -o compiled.flata
-aapt2 link --manifest AndroidManifest.xml -o overlay.apk compiled.flata
+aapt2 link -I $FRAMEWORK_RES_APK --manifest AndroidManifest.xml -o overlay.apk compiled.flata \
+ --no-auto-version
rm compiled.flata
+
+# Navigate back a directory so the idmap can find the overlays in the test data directory when being
+# loaded during testing.
+cd ../
+idmap2 create --target-apk-path overlayable/overlayable.apk \
+ --overlay-apk-path overlay/overlay.apk --idmap-path overlay/overlay.idmap
diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk
index d37874dcbb40..c594b8e67f28 100644
--- a/libs/androidfw/tests/data/overlay/overlay.apk
+++ b/libs/androidfw/tests/data/overlay/overlay.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap
new file mode 100644
index 000000000000..27cf792ff7e2
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/overlay.idmap
Binary files differ
diff --git a/libs/androidfw/tests/data/overlay/res/layout/hello_view.xml b/libs/androidfw/tests/data/overlay/res/layout/hello_view.xml
new file mode 100644
index 000000000000..54dc6c09acf1
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/res/layout/hello_view.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/hello_view"
+ android:text="@android:string/yes"
+ app:max_lines="4"/> \ No newline at end of file
diff --git a/libs/androidfw/tests/data/overlay/res/values/values.xml b/libs/androidfw/tests/data/overlay/res/values/values.xml
index 8e4417e457d1..ba018ec19e9f 100644
--- a/libs/androidfw/tests/data/overlay/res/values/values.xml
+++ b/libs/androidfw/tests/data/overlay/res/values/values.xml
@@ -13,13 +13,11 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<resources>
- <string name="test2">test2-overlay</string>
- <integer-array name="integerArray1">
- <item>10</item>
- <item>11</item>
- </integer-array>
- <public type="animator" name="unoverlaid" id="0x7fff0000" />
- <item type="animator" name="unoverlaid">@null</item>
+ <string name="overlay1">Overlay One</string>
+ <string name="overlay2">Overlay Two</string>
+ <string name="overlay3">@string/internal</string>
+ <string name="overlay4">@string/overlay2</string>
+ <string name="internal">Internal</string>
+ <attr name="max_lines" format="integer" />
</resources>
diff --git a/libs/androidfw/tests/data/overlay/res/xml/overlays.xml b/libs/androidfw/tests/data/overlay/res/xml/overlays.xml
new file mode 100644
index 000000000000..9eca2faa76f4
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/res/xml/overlays.xml
@@ -0,0 +1,47 @@
+<?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 language governing permissions and
+ limitations under the License.
+-->
+<overlay>
+ <!-- Overlays string/overlayable5 with the string "Overlay One". -->
+ <item target="string/overlayable5" value="@string/overlay1"/>
+
+ <!-- Overlays string/overlayable6 and string/overlayable7 with the string "Overlay Two". -->
+ <item target="string/overlayable7" value="@string/overlay2" />
+ <item target="string/overlayable6" value="@string/overlay2" />
+
+ <!-- Overlays string/overlayable8 with a reference to @string/internal. -->
+ <item target="string/overlayable8" value="@string/overlay3" />
+
+ <!-- Overlays string/overlayable9 with a reference to @string/overlay2. The reference to
+ @string/overlay2 should be rewritten to @string/overlayable7 in the target. -->
+ <item target="string/overlayable9" value="@string/overlay4" />
+
+ <!-- Overlays string/overlayable10 with the string "yes". -->
+ <item target="string/overlayable10" value="@android:string/yes" />
+
+ <!-- Overlays string/overlayable11 with the string "Hardcoded string". -->
+ <item target="string/overlayable11" value="Hardcoded string" />
+
+ <!-- Overlays string/overlayable10 with the string "yes". -->
+ <item target="integer/config_int" value="42" />
+
+ <!-- @attr/max_lines and @id/hello_view should be rewritten to @attr/max_lines and
+ @id/hello_view in the target. -->
+ <item target="layout/hello_view" value="@layout/hello_view" />
+ <item target="attr/max_lines" value="@attr/max_lines" />
+ <item target="id/hello_view" value="@id/hello_view" />
+</overlay>
+
+
diff --git a/libs/androidfw/tests/data/overlayable/R.h b/libs/androidfw/tests/data/overlayable/R.h
index e46e264da318..35125a62ff4b 100644
--- a/libs/androidfw/tests/data/overlayable/R.h
+++ b/libs/androidfw/tests/data/overlayable/R.h
@@ -31,6 +31,43 @@ struct R {
overlayable2 = 0x7f010002,
overlayable3 = 0x7f010003,
overlayable4 = 0x7f010004,
+ overlayable5 = 0x7f010005,
+ overlayable6 = 0x7f010006,
+ overlayable7 = 0x7f010007,
+ overlayable8 = 0x7f010008,
+ overlayable9 = 0x7f010009,
+ overlayable10 = 0x7f01000a,
+ overlayable11 = 0x7f01000b,
+ };
+ };
+
+ struct attr {
+ enum : uint32_t {
+ max_lines = 0x7f020000,
+ };
+ };
+
+ struct boolean {
+ enum : uint32_t {
+ config_bool = 0x7f030000,
+ };
+ };
+
+ struct id {
+ enum : uint32_t {
+ hello_view = 0x7f040000,
+ };
+ };
+
+ struct integer {
+ enum : uint32_t {
+ config_integer = 0x7f050000,
+ };
+ };
+
+ struct layout {
+ enum : uint32_t {
+ hello_view = 0x7f060000,
};
};
};
diff --git a/libs/androidfw/tests/data/overlayable/build b/libs/androidfw/tests/data/overlayable/build
index 98fdc5101160..0aa97d639c30 100755
--- a/libs/androidfw/tests/data/overlayable/build
+++ b/libs/androidfw/tests/data/overlayable/build
@@ -17,6 +17,9 @@
set -e
+FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk
+
aapt2 compile --dir res -o compiled.flata
-aapt2 link --manifest AndroidManifest.xml -o overlayable.apk compiled.flata
+aapt2 link -I $FRAMEWORK_RES_APK --manifest AndroidManifest.xml -o overlayable.apk compiled.flata \
+ --no-auto-version
rm compiled.flata
diff --git a/libs/androidfw/tests/data/overlayable/overlayable.apk b/libs/androidfw/tests/data/overlayable/overlayable.apk
index 047e6afde86b..9dc9c15f68a9 100644
--- a/libs/androidfw/tests/data/overlayable/overlayable.apk
+++ b/libs/androidfw/tests/data/overlayable/overlayable.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml b/libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml
new file mode 100644
index 000000000000..268118a91bff
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/res/layout/hello_view.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/hello_view"
+ android:text="None"
+ app:max_lines="0"/> \ No newline at end of file
diff --git a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml
index fcdbe94466c1..b3e8f7d8e84b 100644
--- a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml
+++ b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml
@@ -15,27 +15,41 @@
-->
<resources>
-<overlayable name="OverlayableResources1" actor="overlay://theme">
- <!-- Any overlay can overlay the value of @string/overlayable1 -->
- <item type="string" name="overlayable1" />
+ <overlayable name="OverlayableResources1" actor="overlay://theme">
+ <!-- Any overlay on the product or system partition can overlay the value of
+ @string/overlayable2 -->
+ <policy type="product|system">
+ <item type="string" name="overlayable2" />
+ </policy>
- <!-- Any overlay on the product or system partition can overlay the value of
- @string/overlayable2 -->
- <policy type="product|system">
- <item type="string" name="overlayable2" />
- </policy>
+ <!-- Any overlay can overlay the value of @string/overlayable4 -->
+ <policy type="public">
+ <item type="string" name="overlayable1" />
+ <item type="string" name="overlayable4" />
+ </policy>
+ </overlayable>
- <!-- Any overlay can overlay the value of @string/overlayable4 -->
- <policy type="public">
- <item type="string" name="overlayable4" />
- </policy>
-</overlayable>
+ <overlayable name="OverlayableResources2" actor="overlay://com.android.overlayable">
+ <!-- Any overlay on the vendor or product partition can overlay the value of
+ @string/overlayable3 -->
+ <policy type="vendor|product">
+ <item type="string" name="overlayable3" />
+ </policy>
+ </overlayable>
-<overlayable name="OverlayableResources2" actor="overlay://com.android.overlayable">
- <!-- Any overlay on the vendor or product partition can overlay the value of
- @string/overlayable3 -->
- <policy type="vendor|product">
- <item type="string" name="overlayable3" />
- </policy>
-</overlayable>
+ <overlayable name="OverlayableResources3">
+ <policy type="public">
+ <item type="string" name="overlayable5" />
+ <item type="string" name="overlayable6" />
+ <item type="string" name="overlayable7" />
+ <item type="string" name="overlayable8" />
+ <item type="string" name="overlayable9" />
+ <item type="string" name="overlayable10" />
+ <item type="string" name="overlayable11" />
+ <item type="integer" name="config_int" />
+ <item type="id" name="hello_view" />
+ <item type="attr" name="max_lines" />
+ <item type="layout" name="hello_view" />
+ </policy>
+ </overlayable>
</resources> \ No newline at end of file
diff --git a/libs/androidfw/tests/data/overlayable/res/values/public.xml b/libs/androidfw/tests/data/overlayable/res/values/public.xml
index 5676d7cc64c9..042a311b2b88 100644
--- a/libs/androidfw/tests/data/overlayable/res/values/public.xml
+++ b/libs/androidfw/tests/data/overlayable/res/values/public.xml
@@ -20,4 +20,21 @@
<public type="string" name="overlayable2" id="0x7f010002" />
<public type="string" name="overlayable3" id="0x7f010003" />
<public type="string" name="overlayable4" id="0x7f010004" />
+ <public type="string" name="overlayable5" id="0x7f010005" />
+ <public type="string" name="overlayable6" id="0x7f010006" />
+ <public type="string" name="overlayable7" id="0x7f010007" />
+ <public type="string" name="overlayable8" id="0x7f010008" />
+ <public type="string" name="overlayable9" id="0x7f010009" />
+ <public type="string" name="overlayable10" id="0x7f01000a" />
+ <public type="string" name="overlayable11" id="0x7f01000b" />
+
+ <public type="attr" name="max_lines" id="0x7f020000" />
+
+ <public type="bool" name="config_bool" id="0x7f030000" />
+
+ <public type="id" name="hello_view" id="0x7f040000" />
+
+ <public type="integer" name="config_int" id="0x7f050000" />
+
+ <public type="layout" name="hello_view" id="0x7f060000" />
</resources> \ No newline at end of file
diff --git a/libs/androidfw/tests/data/overlayable/res/values/values.xml b/libs/androidfw/tests/data/overlayable/res/values/values.xml
index a86b31282bc9..235772d35fd0 100644
--- a/libs/androidfw/tests/data/overlayable/res/values/values.xml
+++ b/libs/androidfw/tests/data/overlayable/res/values/values.xml
@@ -20,4 +20,15 @@
<string name="overlayable2">Overlayable Two</string>
<string name="overlayable3">Overlayable Three</string>
<string name="overlayable4">Overlayable Four</string>
+ <string name="overlayable5">Overlayable Five</string>
+ <string name="overlayable6">Overlayable Six</string>
+ <string name="overlayable7">Overlayable Seven</string>
+ <string name="overlayable8">Overlayable Eight</string>
+ <string name="overlayable9">Overlayable Nine</string>
+ <string name="overlayable10">Overlayable Ten</string>
+ <string name="overlayable11">Overlayable Eleven</string>
+
+ <integer name="config_int">0</integer>
+ <bool name="config_bool">false</bool>
+ <attr name="max_lines" format="integer" />
</resources>
diff --git a/libs/androidfw/tests/data/system/R.h b/libs/androidfw/tests/data/system/R.h
index becb38830fb3..c0160c0f78a9 100644
--- a/libs/androidfw/tests/data/system/R.h
+++ b/libs/androidfw/tests/data/system/R.h
@@ -40,6 +40,13 @@ struct R {
number = 0x01030000, // sv
};
};
+
+ struct string {
+ enum : uint32_t {
+ no = 0x01040009,
+ yes = 0x01040013,
+ };
+ };
};
} // namespace android
diff --git a/libs/androidfw/tests/data/system/res/values/public.xml b/libs/androidfw/tests/data/system/res/values/public.xml
new file mode 100644
index 000000000000..077874d0b0fe
--- /dev/null
+++ b/libs/androidfw/tests/data/system/res/values/public.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<resources>
+ <public type="string" name="no" id="0x01040009" />
+ <public type="string" name="yes" id="0x01040013" />
+</resources> \ No newline at end of file
diff --git a/libs/androidfw/tests/data/system/res/values/values.xml b/libs/androidfw/tests/data/system/res/values/values.xml
new file mode 100644
index 000000000000..0629c1d13795
--- /dev/null
+++ b/libs/androidfw/tests/data/system/res/values/values.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <string name="yes">yes</string>
+ <string name="no">no</string>
+</resources> \ No newline at end of file
diff --git a/libs/androidfw/tests/data/system/system.apk b/libs/androidfw/tests/data/system/system.apk
index 9045d6c4de21..1f7e00733366 100644
--- a/libs/androidfw/tests/data/system/system.apk
+++ b/libs/androidfw/tests/data/system/system.apk
Binary files differ
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index ae90f117d448..61b72cf9d193 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -26,8 +26,10 @@ cc_defaults {
// a problem
"-Wno-free-nonheap-object",
- // clang's warning is broken, see: https://llvm.org/bugs/show_bug.cgi?id=21629
- "-Wno-missing-braces",
+ // Clang is producing non-determistic binary when the new pass manager is
+ // enabled. Disable the new PM as a temporary workaround.
+ // b/142372146
+ "-fno-experimental-new-pass-manager",
],
include_dirs: [
diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h
index 7d0b6877a71a..030a20f31c42 100644
--- a/libs/hwui/DamageAccumulator.h
+++ b/libs/hwui/DamageAccumulator.h
@@ -27,7 +27,7 @@
// Smaller than INT_MIN/INT_MAX because we offset these values
// and thus don't want to be adding offsets to INT_MAX, that's bad
#define DIRTY_MIN (-0x7ffffff - 1)
-#define DIRTY_MAX (0x7ffffff)
+#define DIRTY_MAX (0x8000000)
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index a79b7c00ba04..322eff24dd34 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -69,6 +69,7 @@ public:
bool hasText() const { return mHasText; }
size_t usedSize() const { return fUsed; }
+ size_t allocatedSize() const { return fReserved; }
private:
friend class RecordingCanvas;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 8eb5e3d3dfbc..6761435a8171 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -108,7 +108,7 @@ void RenderNode::output(std::ostream& output, uint32_t level) {
output << std::endl;
}
-int RenderNode::getDebugSize() {
+int RenderNode::getUsageSize() {
int size = sizeof(RenderNode);
if (mStagingDisplayList) {
size += mStagingDisplayList->getUsedSize();
@@ -119,6 +119,18 @@ int RenderNode::getDebugSize() {
return size;
}
+int RenderNode::getAllocatedSize() {
+ int size = sizeof(RenderNode);
+ if (mStagingDisplayList) {
+ size += mStagingDisplayList->getAllocatedSize();
+ }
+ if (mDisplayList && mDisplayList != mStagingDisplayList) {
+ size += mDisplayList->getAllocatedSize();
+ }
+ return size;
+}
+
+
void RenderNode::prepareTree(TreeInfo& info) {
ATRACE_CALL();
LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing");
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index c6db7f1ba60d..d55e5b0ce836 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -102,7 +102,8 @@ public:
ANDROID_API void setStagingDisplayList(DisplayList* newData);
ANDROID_API void output();
- ANDROID_API int getDebugSize();
+ ANDROID_API int getUsageSize();
+ ANDROID_API int getAllocatedSize();
bool isRenderable() const { return mDisplayList && !mDisplayList->isEmpty(); }
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index b0173846e582..f839213e9007 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -43,41 +43,28 @@ static bool shouldFilterRect(const SkMatrix& matrix, const SkRect& srcRect, cons
if (!matrix.rectStaysRect()) return true;
SkRect dstDevRect = matrix.mapRect(dstRect);
float dstW, dstH;
- bool requiresIntegerTranslate = false;
if (MathUtils::isZero(matrix.getScaleX()) && MathUtils::isZero(matrix.getScaleY())) {
// Has a 90 or 270 degree rotation, although total matrix may also have scale factors
// in m10 and m01. Those scalings are automatically handled by mapRect so comparing
// dimensions is sufficient, but swap width and height comparison.
dstW = dstDevRect.height();
dstH = dstDevRect.width();
- requiresIntegerTranslate = true;
} else {
// Handle H/V flips or 180 rotation matrices. Axes may have been mirrored, but
// dimensions are still safe to compare directly.
dstW = dstDevRect.width();
dstH = dstDevRect.height();
- requiresIntegerTranslate =
- matrix.getScaleX() < -NON_ZERO_EPSILON || matrix.getScaleY() < -NON_ZERO_EPSILON;
}
if (!(MathUtils::areEqual(dstW, srcRect.width()) &&
MathUtils::areEqual(dstH, srcRect.height()))) {
return true;
}
- if (requiresIntegerTranslate) {
- // Device rect and source rect should be integer aligned to ensure there's no difference
- // in how nearest-neighbor sampling is resolved.
- return !(isIntegerAligned(srcRect.x()) &&
- isIntegerAligned(srcRect.y()) &&
- isIntegerAligned(dstDevRect.x()) &&
- isIntegerAligned(dstDevRect.y()));
- } else {
- // As long as src and device rects are translated by the same fractional amount,
- // filtering won't be needed
- return !(MathUtils::areEqual(SkScalarFraction(srcRect.x()),
- SkScalarFraction(dstDevRect.x())) &&
- MathUtils::areEqual(SkScalarFraction(srcRect.y()),
- SkScalarFraction(dstDevRect.y())));
- }
+ // Device rect and source rect should be integer aligned to ensure there's no difference
+ // in how nearest-neighbor sampling is resolved.
+ return !(isIntegerAligned(srcRect.x()) &&
+ isIntegerAligned(srcRect.y()) &&
+ isIntegerAligned(dstDevRect.x()) &&
+ isIntegerAligned(dstDevRect.y()));
}
bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer,
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index e3c3273a726a..cdd00db9afdc 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -47,6 +47,7 @@ class FunctorDrawable;
class SkiaDisplayList {
public:
size_t getUsedSize() { return allocator.usedSize() + mDisplayList.usedSize(); }
+ size_t getAllocatedSize() { return allocator.allocatedSize() + mDisplayList.allocatedSize(); }
~SkiaDisplayList() {
/* Given that we are using a LinearStdAllocator to store some of the
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 67c181b452bb..3010206cdc5b 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -569,6 +569,7 @@ void SkiaPipeline::renderOverdraw(const LayerUpdateQueue& layers, const SkRect&
// Set up the overdraw canvas.
SkImageInfo offscreenInfo = SkImageInfo::MakeA8(surface->width(), surface->height());
sk_sp<SkSurface> offscreen = surface->makeSurface(offscreenInfo);
+ LOG_ALWAYS_FATAL_IF(!offscreen, "Failed to create offscreen SkSurface for overdraw viz.");
SkOverdrawCanvas overdrawCanvas(offscreen->getCanvas());
// Fake a redraw to replay the draw commands. This will increment the alpha channel
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index fc268138e071..8eb81533fda8 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -39,7 +39,7 @@ namespace renderthread {
// to the screen resolution. This is meant to be a conservative default based on
// that analysis. The 4.0f is used because the default pixel format is assumed to
// be ARGB_8888.
-#define SURFACE_SIZE_MULTIPLIER (12.0f * 4.0f)
+#define SURFACE_SIZE_MULTIPLIER (5.0f * 4.0f)
#define BACKGROUND_RETENTION_PERCENTAGE (0.5f)
CacheManager::CacheManager(const DisplayInfo& display)
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 684dc22ee4d1..30cc007d454b 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -491,9 +491,9 @@ void CanvasContext::draw() {
swap.dequeueDuration = 0;
} else {
swap.dequeueDuration =
- us2ns(ANativeWindow_getLastDequeueDuration(mNativeSurface.get()));
+ ANativeWindow_getLastDequeueDuration(mNativeSurface.get());
}
- swap.queueDuration = us2ns(ANativeWindow_getLastQueueDuration(mNativeSurface.get()));
+ swap.queueDuration = ANativeWindow_getLastQueueDuration(mNativeSurface.get());
} else {
swap.dequeueDuration = 0;
swap.queueDuration = 0;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 8a76d6b3fc7a..6e3e43af8c6f 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -242,7 +242,8 @@ private:
nsecs_t queueDuration;
};
- RingBuffer<SwapHistory, 3> mSwapHistory;
+ // Need at least 4 because we do quad buffer. Add a 5th for good measure.
+ RingBuffer<SwapHistory, 5> mSwapHistory;
int64_t mFrameNumber = -1;
// last vsync for a dropped frame due to stuffed queue
diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h
index 9c4a1be4b269..539e6544ef02 100644
--- a/libs/hwui/utils/LinearAllocator.h
+++ b/libs/hwui/utils/LinearAllocator.h
@@ -115,6 +115,7 @@ public:
* wasted)
*/
size_t usedSize() const { return mTotalAllocated - mWastedSpace; }
+ size_t allocatedSize() const { return mTotalAllocated; }
private:
LinearAllocator(const LinearAllocator& other);
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 16f2917f8df8..6bb896fd7b29 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -20,11 +20,11 @@ cc_library_shared {
],
shared_libs: [
+ "libandroid_runtime",
"libbinder",
"libcutils",
"liblog",
"libutils",
- "libhwui",
"libgui",
"libui",
"libinput",
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index abf083789c23..e4348f2a9b21 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -24,12 +24,6 @@
#include <log/log.h>
-#include <SkBitmap.h>
-#include <SkCanvas.h>
-#include <SkColor.h>
-#include <SkPaint.h>
-#include <SkBlendMode.h>
-
namespace android {
// --- WeakLooperCallback ---
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index fd386e9f7a8a..804644c230b9 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -23,11 +23,9 @@
#include <utils/String8.h>
#include <gui/Surface.h>
-#include <SkBitmap.h>
-#include <SkCanvas.h>
-#include <SkColor.h>
-#include <SkPaint.h>
-
+#include <android/graphics/bitmap.h>
+#include <android/graphics/canvas.h>
+#include <android/graphics/paint.h>
#include <android/native_window.h>
namespace android {
@@ -132,8 +130,8 @@ void SpriteController::doUpdateSprites() {
SpriteUpdate& update = updates.editItemAt(i);
if (update.state.surfaceControl == NULL && update.state.wantSurfaceVisible()) {
- update.state.surfaceWidth = update.state.icon.bitmap.width();
- update.state.surfaceHeight = update.state.icon.bitmap.height();
+ update.state.surfaceWidth = update.state.icon.bitmap.getInfo().width;
+ update.state.surfaceHeight = update.state.icon.bitmap.getInfo().height;
update.state.surfaceDrawn = false;
update.state.surfaceVisible = false;
update.state.surfaceControl = obtainSurface(
@@ -154,8 +152,8 @@ void SpriteController::doUpdateSprites() {
}
if (update.state.wantSurfaceVisible()) {
- int32_t desiredWidth = update.state.icon.bitmap.width();
- int32_t desiredHeight = update.state.icon.bitmap.height();
+ int32_t desiredWidth = update.state.icon.bitmap.getInfo().width;
+ int32_t desiredHeight = update.state.icon.bitmap.getInfo().height;
if (update.state.surfaceWidth < desiredWidth
|| update.state.surfaceHeight < desiredHeight) {
needApplyTransaction = true;
@@ -201,26 +199,22 @@ void SpriteController::doUpdateSprites() {
if (status) {
ALOGE("Error %d locking sprite surface before drawing.", status);
} else {
- SkBitmap surfaceBitmap;
- ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
- surfaceBitmap.installPixels(SkImageInfo::MakeN32Premul(outBuffer.width, outBuffer.height),
- outBuffer.bits, bpr);
+ graphics::Paint paint;
+ paint.setBlendMode(ABLEND_MODE_SRC);
- SkCanvas surfaceCanvas(surfaceBitmap);
+ graphics::Canvas canvas(outBuffer, (int32_t) surface->getBuffersDataSpace());
+ canvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint);
- SkPaint paint;
- paint.setBlendMode(SkBlendMode::kSrc);
- surfaceCanvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint);
+ const int iconWidth = update.state.icon.bitmap.getInfo().width;
+ const int iconHeight = update.state.icon.bitmap.getInfo().height;
- if (outBuffer.width > update.state.icon.bitmap.width()) {
- paint.setColor(0); // transparent fill color
- surfaceCanvas.drawRect(SkRect::MakeLTRB(update.state.icon.bitmap.width(), 0,
- outBuffer.width, update.state.icon.bitmap.height()), paint);
+ if (outBuffer.width > iconWidth) {
+ paint.setBlendMode(ABLEND_MODE_CLEAR); // clear to transparent
+ canvas.drawRect({iconWidth, 0, outBuffer.width, iconHeight}, paint);
}
- if (outBuffer.height > update.state.icon.bitmap.height()) {
- paint.setColor(0); // transparent fill color
- surfaceCanvas.drawRect(SkRect::MakeLTRB(0, update.state.icon.bitmap.height(),
- outBuffer.width, outBuffer.height), paint);
+ if (outBuffer.height > iconHeight) {
+ paint.setBlendMode(ABLEND_MODE_CLEAR); // clear to transparent
+ canvas.drawRect({0, iconHeight, outBuffer.width, outBuffer.height}, paint);
}
status = surface->unlockAndPost();
@@ -398,12 +392,7 @@ void SpriteController::SpriteImpl::setIcon(const SpriteIcon& icon) {
uint32_t dirty;
if (icon.isValid()) {
- SkBitmap* bitmapCopy = &mLocked.state.icon.bitmap;
- if (bitmapCopy->tryAllocPixels(icon.bitmap.info().makeColorType(kN32_SkColorType))) {
- icon.bitmap.readPixels(bitmapCopy->info(), bitmapCopy->getPixels(),
- bitmapCopy->rowBytes(), 0, 0);
- }
-
+ mLocked.state.icon.bitmap = icon.bitmap.copy(ANDROID_BITMAP_FORMAT_RGBA_8888);
if (!mLocked.state.icon.isValid()
|| mLocked.state.icon.hotSpotX != icon.hotSpotX
|| mLocked.state.icon.hotSpotY != icon.hotSpotY) {
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 79a904f5fe65..2513544d4bdf 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -20,10 +20,9 @@
#include <utils/RefBase.h>
#include <utils/Looper.h>
+#include <android/graphics/bitmap.h>
#include <gui/SurfaceComposerClient.h>
-#include <SkBitmap.h>
-
namespace android {
/*
@@ -56,21 +55,16 @@ struct SpriteTransformationMatrix {
*/
struct SpriteIcon {
inline SpriteIcon() : style(0), hotSpotX(0), hotSpotY(0) { }
- inline SpriteIcon(const SkBitmap& bitmap, int32_t style, float hotSpotX, float hotSpotY) :
+ inline SpriteIcon(const graphics::Bitmap& bitmap, int32_t style, float hotSpotX, float hotSpotY) :
bitmap(bitmap), style(style), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { }
- SkBitmap bitmap;
+ graphics::Bitmap bitmap;
int32_t style;
float hotSpotX;
float hotSpotY;
inline SpriteIcon copy() const {
- SkBitmap bitmapCopy;
- if (bitmapCopy.tryAllocPixels(bitmap.info().makeColorType(kN32_SkColorType))) {
- bitmap.readPixels(bitmapCopy.info(), bitmapCopy.getPixels(), bitmapCopy.rowBytes(),
- 0, 0);
- }
- return SpriteIcon(bitmapCopy, style, hotSpotX, hotSpotY);
+ return SpriteIcon(bitmap.copy(ANDROID_BITMAP_FORMAT_RGBA_8888), style, hotSpotX, hotSpotY);
}
inline void reset() {
@@ -81,7 +75,7 @@ struct SpriteIcon {
}
inline bool isValid() const {
- return !bitmap.isNull() && !bitmap.empty();
+ return bitmap.isValid() && !bitmap.isEmpty();
}
};
@@ -183,7 +177,7 @@ private:
* This structure is designed so that it can be copied during updates so that
* surfaces can be resized and redrawn without blocking the client by holding a lock
* on the sprites for a long time.
- * Note that the SkBitmap holds a reference to a shared (and immutable) pixel ref. */
+ * Note that the SpriteIcon holds a reference to a shared (and immutable) bitmap. */
struct SpriteState {
inline SpriteState() :
dirty(0), visible(false),
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index e83b2a78d180..b1e3d6fe845a 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -18,9 +18,9 @@ cc_test {
"PointerController_test.cpp",
],
shared_libs: [
+ "libandroid_runtime",
"libinputservice",
"libgui",
- "libhwui",
"libutils",
],
static_libs: [
diff --git a/location/java/android/location/AbstractListenerManager.java b/location/java/android/location/AbstractListenerManager.java
new file mode 100644
index 000000000000..944ebf937dc8
--- /dev/null
+++ b/location/java/android/location/AbstractListenerManager.java
@@ -0,0 +1,141 @@
+/*
+ * 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.location;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * A base class to manage listeners that have a 1:N -> source:listener relationship.
+ *
+ * @hide
+ */
+abstract class AbstractListenerManager<T> {
+
+ private static class Registration<T> {
+ private final Executor mExecutor;
+ @Nullable private volatile T mListener;
+
+ private Registration(Executor executor, T listener) {
+ Preconditions.checkArgument(listener != null, "invalid null listener/callback");
+ Preconditions.checkArgument(executor != null, "invalid null executor");
+ mExecutor = executor;
+ mListener = listener;
+ }
+
+ private void unregister() {
+ mListener = null;
+ }
+
+ private void execute(Consumer<T> operation) {
+ mExecutor.execute(() -> {
+ T listener = mListener;
+ if (listener == null) {
+ return;
+ }
+
+ // we may be under the binder identity if a direct executor is used
+ long identity = Binder.clearCallingIdentity();
+ try {
+ operation.accept(listener);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ });
+ }
+ }
+
+ @GuardedBy("mListeners")
+ private final ArrayMap<Object, Registration<T>> mListeners = new ArrayMap<>();
+
+ public boolean addListener(@NonNull T listener, @NonNull Handler handler)
+ throws RemoteException {
+ return addInternal(listener, handler);
+ }
+
+ public boolean addListener(@NonNull T listener, @NonNull Executor executor)
+ throws RemoteException {
+ return addInternal(listener, executor);
+ }
+
+ protected final boolean addInternal(@NonNull Object listener, @NonNull Handler handler)
+ throws RemoteException {
+ return addInternal(listener, new HandlerExecutor(handler));
+ }
+
+ protected final boolean addInternal(@NonNull Object listener, @NonNull Executor executor)
+ throws RemoteException {
+ Preconditions.checkArgument(listener != null, "invalid null listener/callback");
+ return addInternal(listener, new Registration<>(executor, convertKey(listener)));
+ }
+
+ private boolean addInternal(Object key, Registration<T> registration) throws RemoteException {
+ Preconditions.checkNotNull(registration);
+
+ synchronized (mListeners) {
+ if (mListeners.isEmpty() && !registerService()) {
+ return false;
+ }
+ Registration<T> oldRegistration = mListeners.put(key, registration);
+ if (oldRegistration != null) {
+ oldRegistration.unregister();
+ }
+ return true;
+ }
+ }
+
+ public void removeListener(Object listener) throws RemoteException {
+ synchronized (mListeners) {
+ Registration<T> oldRegistration = mListeners.remove(listener);
+ if (oldRegistration == null) {
+ return;
+ }
+ oldRegistration.unregister();
+
+ if (mListeners.isEmpty()) {
+ unregisterService();
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected T convertKey(@NonNull Object listener) {
+ return (T) listener;
+ }
+
+ protected abstract boolean registerService() throws RemoteException;
+ protected abstract void unregisterService() throws RemoteException;
+
+ protected void execute(Consumer<T> operation) {
+ synchronized (mListeners) {
+ for (Registration<T> registration : mListeners.values()) {
+ registration.execute(operation);
+ }
+ }
+ }
+}
diff --git a/location/java/android/location/BatchedLocationCallbackTransport.java b/location/java/android/location/BatchedLocationCallbackTransport.java
deleted file mode 100644
index e00f855e9302..000000000000
--- a/location/java/android/location/BatchedLocationCallbackTransport.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.location;
-
-import android.content.Context;
-import android.os.RemoteException;
-
-import java.util.List;
-
-/**
- * A handler class to manage transport callbacks for {@link BatchedLocationCallback}.
- *
- * @hide
- */
-class BatchedLocationCallbackTransport
- extends LocalListenerHelper<BatchedLocationCallback> {
- private final ILocationManager mLocationManager;
-
- private final IBatchedLocationCallback mCallbackTransport = new CallbackTransport();
-
- public BatchedLocationCallbackTransport(Context context, ILocationManager locationManager) {
- super(context, "BatchedLocationCallbackTransport");
- mLocationManager = locationManager;
- }
-
- @Override
- protected boolean registerWithServer() throws RemoteException {
- return mLocationManager.addGnssBatchingCallback(
- mCallbackTransport,
- getContext().getPackageName());
- }
-
- @Override
- protected void unregisterFromServer() throws RemoteException {
- mLocationManager.removeGnssBatchingCallback();
- }
-
- private class CallbackTransport extends IBatchedLocationCallback.Stub {
- @Override
- public void onLocationBatch(final List<Location> locations) {
- ListenerOperation<BatchedLocationCallback> operation =
- new ListenerOperation<BatchedLocationCallback>() {
- @Override
- public void execute(BatchedLocationCallback callback)
- throws RemoteException {
- callback.onLocationBatch(locations);
- }
- };
- foreach(operation);
- }
- }
-}
diff --git a/location/java/android/location/GnssMeasurementCallbackTransport.java b/location/java/android/location/GnssMeasurementCallbackTransport.java
deleted file mode 100644
index 8cb8c0b78da1..000000000000
--- a/location/java/android/location/GnssMeasurementCallbackTransport.java
+++ /dev/null
@@ -1,97 +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.location;
-
-import android.content.Context;
-import android.os.RemoteException;
-
-import com.android.internal.util.Preconditions;
-
-/**
- * A handler class to manage transport callbacks for {@link GnssMeasurementsEvent.Callback}.
- *
- * @hide
- */
-class GnssMeasurementCallbackTransport
- extends LocalListenerHelper<GnssMeasurementsEvent.Callback> {
- private static final String TAG = "GnssMeasCbTransport";
- private final ILocationManager mLocationManager;
-
- private final IGnssMeasurementsListener mListenerTransport = new ListenerTransport();
-
- public GnssMeasurementCallbackTransport(Context context, ILocationManager locationManager) {
- super(context, TAG);
- mLocationManager = locationManager;
- }
-
- @Override
- protected boolean registerWithServer() throws RemoteException {
- return mLocationManager.addGnssMeasurementsListener(
- mListenerTransport,
- getContext().getPackageName());
- }
-
- @Override
- protected void unregisterFromServer() throws RemoteException {
- mLocationManager.removeGnssMeasurementsListener(mListenerTransport);
- }
-
- /**
- * Injects GNSS measurement corrections into the GNSS chipset.
- *
- * @param measurementCorrections a {@link GnssMeasurementCorrections} object with the GNSS
- * measurement corrections to be injected into the GNSS chipset.
- */
- protected void injectGnssMeasurementCorrections(
- GnssMeasurementCorrections measurementCorrections) throws RemoteException {
- Preconditions.checkNotNull(measurementCorrections);
- mLocationManager.injectGnssMeasurementCorrections(
- measurementCorrections, getContext().getPackageName());
- }
-
- protected long getGnssCapabilities() throws RemoteException {
- return mLocationManager.getGnssCapabilities(getContext().getPackageName());
- }
-
- private class ListenerTransport extends IGnssMeasurementsListener.Stub {
- @Override
- public void onGnssMeasurementsReceived(final GnssMeasurementsEvent event) {
- ListenerOperation<GnssMeasurementsEvent.Callback> operation =
- new ListenerOperation<GnssMeasurementsEvent.Callback>() {
- @Override
- public void execute(GnssMeasurementsEvent.Callback callback)
- throws RemoteException {
- callback.onGnssMeasurementsReceived(event);
- }
- };
- foreach(operation);
- }
-
- @Override
- public void onStatusChanged(final int status) {
- ListenerOperation<GnssMeasurementsEvent.Callback> operation =
- new ListenerOperation<GnssMeasurementsEvent.Callback>() {
- @Override
- public void execute(GnssMeasurementsEvent.Callback callback)
- throws RemoteException {
- callback.onStatusChanged(status);
- }
- };
- foreach(operation);
- }
- }
-}
diff --git a/location/java/android/location/GnssNavigationMessageCallbackTransport.java b/location/java/android/location/GnssNavigationMessageCallbackTransport.java
deleted file mode 100644
index 1eafd02e52be..000000000000
--- a/location/java/android/location/GnssNavigationMessageCallbackTransport.java
+++ /dev/null
@@ -1,79 +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.location;
-
-import android.content.Context;
-import android.os.RemoteException;
-
-/**
- * A handler class to manage transport callback for {@link GnssNavigationMessage.Callback}.
- *
- * @hide
- */
-class GnssNavigationMessageCallbackTransport
- extends LocalListenerHelper<GnssNavigationMessage.Callback> {
- private final ILocationManager mLocationManager;
-
- private final IGnssNavigationMessageListener mListenerTransport = new ListenerTransport();
-
- public GnssNavigationMessageCallbackTransport(
- Context context,
- ILocationManager locationManager) {
- super(context, "GnssNavigationMessageCallbackTransport");
- mLocationManager = locationManager;
- }
-
- @Override
- protected boolean registerWithServer() throws RemoteException {
- return mLocationManager.addGnssNavigationMessageListener(
- mListenerTransport,
- getContext().getPackageName());
- }
-
- @Override
- protected void unregisterFromServer() throws RemoteException {
- mLocationManager.removeGnssNavigationMessageListener(mListenerTransport);
- }
-
- private class ListenerTransport extends IGnssNavigationMessageListener.Stub {
- @Override
- public void onGnssNavigationMessageReceived(final GnssNavigationMessage event) {
- ListenerOperation<GnssNavigationMessage.Callback> operation =
- new ListenerOperation<GnssNavigationMessage.Callback>() {
- @Override
- public void execute(GnssNavigationMessage.Callback callback)
- throws RemoteException {
- callback.onGnssNavigationMessageReceived(event);
- }
- };
- foreach(operation);
- }
-
- @Override
- public void onStatusChanged(final int status) {
- ListenerOperation<GnssNavigationMessage.Callback> operation =
- new ListenerOperation<GnssNavigationMessage.Callback>() {
- @Override
- public void execute(GnssNavigationMessage.Callback callback)
- throws RemoteException {
- callback.onStatusChanged(status);
- }
- };
- foreach(operation);
- }
- }
-}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index d06ba12f5e2a..79bec920c10e 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -31,6 +31,7 @@ import android.location.Location;
import android.location.LocationRequest;
import android.location.LocationTime;
import android.os.Bundle;
+import android.os.ICancellationSignal;
import com.android.internal.location.ProviderProperties;
@@ -41,17 +42,23 @@ import com.android.internal.location.ProviderProperties;
*/
interface ILocationManager
{
+ Location getLastLocation(in LocationRequest request, String packageName, String featureId);
+ boolean getCurrentLocation(in LocationRequest request,
+ in ICancellationSignal cancellationSignal, in ILocationListener listener,
+ String packageName, String featureId, String listenerIdentifier);
+
void requestLocationUpdates(in LocationRequest request, in ILocationListener listener,
- in PendingIntent intent, String packageName);
+ in PendingIntent intent, String packageName, String featureId,
+ String listenerIdentifier);
void removeUpdates(in ILocationListener listener, in PendingIntent intent, String packageName);
void requestGeofence(in LocationRequest request, in Geofence geofence,
- in PendingIntent intent, String packageName);
+ in PendingIntent intent, String packageName, String featureId,
+ String listenerIdentifier);
void removeGeofence(in Geofence fence, in PendingIntent intent, String packageName);
- Location getLastLocation(in LocationRequest request, String packageName);
-
- boolean registerGnssStatusCallback(IGnssStatusListener callback, String packageName);
+ boolean registerGnssStatusCallback(IGnssStatusListener callback, String packageName,
+ String featureId);
void unregisterGnssStatusCallback(IGnssStatusListener callback);
boolean geocoderIsPresent();
@@ -64,22 +71,23 @@ interface ILocationManager
boolean sendNiResponse(int notifId, int userResponse);
- boolean addGnssMeasurementsListener(in IGnssMeasurementsListener listener, in String packageName);
+ boolean addGnssMeasurementsListener(in IGnssMeasurementsListener listener,
+ String packageName, String featureId, String listenerIdentifier);
void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections,
in String packageName);
long getGnssCapabilities(in String packageName);
void removeGnssMeasurementsListener(in IGnssMeasurementsListener listener);
- boolean addGnssNavigationMessageListener(
- in IGnssNavigationMessageListener listener,
- in String packageName);
+ boolean addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener,
+ String packageName, String featureId, String listenerIdentifier);
void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener);
int getGnssYearOfHardware();
String getGnssHardwareModelName();
int getGnssBatchSize(String packageName);
- boolean addGnssBatchingCallback(in IBatchedLocationCallback callback, String packageName);
+ boolean addGnssBatchingCallback(in IBatchedLocationCallback callback, String packageName,
+ String featureId, String listenerIdentifier);
void removeGnssBatchingCallback();
boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName);
void flushGnssBatch(String packageName);
@@ -92,6 +100,7 @@ interface ILocationManager
String getBestProvider(in Criteria criteria, boolean enabledOnly);
ProviderProperties getProviderProperties(String provider);
boolean isProviderPackage(String packageName);
+ List<String> getProviderPackages(String provider);
void setExtraLocationControllerPackage(String packageName);
String getExtraLocationControllerPackage();
diff --git a/location/java/android/location/LocalListenerHelper.java b/location/java/android/location/LocalListenerHelper.java
deleted file mode 100644
index 592d01d2fed6..000000000000
--- a/location/java/android/location/LocalListenerHelper.java
+++ /dev/null
@@ -1,134 +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.location;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.internal.util.Preconditions;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * A base handler class to manage transport and local listeners.
- *
- * @hide
- */
-abstract class LocalListenerHelper<TListener> {
- private final HashMap<TListener, Handler> mListeners = new HashMap<>();
-
- private final String mTag;
- private final Context mContext;
-
- protected LocalListenerHelper(Context context, String name) {
- Preconditions.checkNotNull(name);
- mContext = context;
- mTag = name;
- }
-
- /**
- * Adds a {@param listener} to the list of listeners on which callbacks will be executed. The
- * execution will happen on the {@param handler} thread or alternatively in the callback thread
- * if a {@code null} handler value is passed.
- */
- public boolean add(@NonNull TListener listener, Handler handler) {
- Preconditions.checkNotNull(listener);
- synchronized (mListeners) {
- // we need to register with the service first, because we need to find out if the
- // service will actually support the request before we attempt anything
- if (mListeners.isEmpty()) {
- boolean registeredWithService;
- try {
- registeredWithService = registerWithServer();
- } catch (RemoteException e) {
- Log.e(mTag, "Error handling first listener.", e);
- return false;
- }
- if (!registeredWithService) {
- Log.e(mTag, "Unable to register listener transport.");
- return false;
- }
- }
- if (mListeners.containsKey(listener)) {
- return true;
- }
- mListeners.put(listener, handler);
- return true;
- }
- }
-
- public void remove(@NonNull TListener listener) {
- Preconditions.checkNotNull(listener);
- synchronized (mListeners) {
- boolean removed = mListeners.containsKey(listener);
- mListeners.remove(listener);
- boolean isLastRemoved = removed && mListeners.isEmpty();
- if (isLastRemoved) {
- try {
- unregisterFromServer();
- } catch (RemoteException e) {
- Log.v(mTag, "Error handling last listener removal", e);
- }
- }
- }
- }
-
- protected abstract boolean registerWithServer() throws RemoteException;
- protected abstract void unregisterFromServer() throws RemoteException;
-
- protected interface ListenerOperation<TListener> {
- void execute(TListener listener) throws RemoteException;
- }
-
- protected Context getContext() {
- return mContext;
- }
-
- private void executeOperation(ListenerOperation<TListener> operation, TListener listener) {
- try {
- operation.execute(listener);
- } catch (RemoteException e) {
- Log.e(mTag, "Error in monitored listener.", e);
- // don't return, give a fair chance to all listeners to receive the event
- }
- }
-
- protected void foreach(final ListenerOperation<TListener> operation) {
- Collection<Map.Entry<TListener, Handler>> listeners;
- synchronized (mListeners) {
- listeners = new ArrayList<>(mListeners.entrySet());
- }
- for (final Map.Entry<TListener, Handler> listener : listeners) {
- if (listener.getValue() == null) {
- executeOperation(operation, listener.getKey());
- } else {
- listener.getValue().post(new Runnable() {
- @Override
- public void run() {
- executeOperation(operation, listener.getKey());
- }
- });
- }
- }
- }
-}
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index 9c36d76cf370..27274d12b1de 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -16,6 +16,7 @@
package android.location;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
@@ -79,6 +80,8 @@ public class Location implements Parcelable {
*
* @hide
*/
+ @TestApi
+ @SystemApi
public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
/**
@@ -1208,14 +1211,16 @@ public class Location implements Parcelable {
}
/**
- * Attaches an extra {@link Location} to this Location.
+ * Attaches an extra {@link Location} to this Location. This is useful for location providers
+ * to set the {@link #EXTRA_NO_GPS_LOCATION} extra to provide coarse locations for clients.
*
* @param key the key associated with the Location extra
* @param value the Location to attach
* @hide
*/
- @UnsupportedAppUsage
- public void setExtraLocation(String key, Location value) {
+ @TestApi
+ @SystemApi
+ public void setExtraLocation(@Nullable String key, @Nullable Location value) {
if (mExtras == null) {
mExtras = new Bundle();
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index d021e8e419fe..8fd0c498f04b 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -20,93 +20,83 @@ 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 android.Manifest;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
+import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageManager;
+import android.os.Binder;
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.Looper;
-import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.location.ProviderProperties;
import com.android.internal.util.Preconditions;
-import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.function.Consumer;
/**
- * This class provides access to the system location services. These
- * services allow applications to obtain periodic updates of the
- * device's geographical location, or to fire an application-specified
- * {@link Intent} when the device enters the proximity of a given
- * geographical location.
+ * This class provides access to the system location services. These services allow applications to
+ * obtain periodic updates of the device's geographical location, or to be notified when the device
+ * enters the proximity of a given geographical location.
*
- * <p class="note">Unless noted, all Location API methods require
- * the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} or
- * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permissions.
- * If your application only has the coarse permission then it will not have
- * access to the GPS or passive location providers. Other providers will still
- * return location results, but the update rate will be throttled and the exact
- * location will be obfuscated to a coarse level of accuracy.
+ * <p class="note">Unless noted, all Location API methods require the {@link
+ * android.Manifest.permission#ACCESS_COARSE_LOCATION} or {@link
+ * android.Manifest.permission#ACCESS_FINE_LOCATION} permissions. If your application only has the
+ * coarse permission then it will not have access to fine location providers. Other providers will
+ * still return location results, but the exact location will be obfuscated to a coarse level of
+ * accuracy.
*/
+@SuppressWarnings({"deprecation", "DeprecatedIsStillUsed"})
@SystemService(Context.LOCATION_SERVICE)
@RequiresFeature(PackageManager.FEATURE_LOCATION)
public class LocationManager {
- private static final String TAG = "LocationManager";
- private final Context mContext;
- @UnsupportedAppUsage
- private final ILocationManager mService;
- private final GnssMeasurementCallbackTransport mGnssMeasurementCallbackTransport;
- private final GnssNavigationMessageCallbackTransport mGnssNavigationMessageCallbackTransport;
- private final BatchedLocationCallbackTransport mBatchedLocationCallbackTransport;
- private final ArrayMap<GnssStatus.Callback, GnssStatusListenerTransport> mGnssStatusListeners =
- new ArrayMap<>();
- private final ArrayMap<OnNmeaMessageListener, GnssStatusListenerTransport> mGnssNmeaListeners =
- new ArrayMap<>();
- private final ArrayMap<GpsStatus.Listener, GnssStatusListenerTransport> mGpsStatusListeners =
- new ArrayMap<>();
- // volatile + GnssStatus final-fields pattern to avoid a partially published object
- private volatile GnssStatus mGnssStatus;
- private int mTimeToFirstFix;
+ private static final String TAG = "LocationManager";
/**
* Name of the network location provider.
- * <p>This provider determines location based on
- * availability of cell tower and WiFi access points. Results are retrieved
- * by means of a network lookup.
+ *
+ * <p>This provider determines location based on nearby of cell tower and WiFi access points.
+ * Results are retrieved by means of a network lookup.
*/
public static final String NETWORK_PROVIDER = "network";
/**
- * Name of the GPS location provider.
+ * Name of the GNSS location provider.
*
- * <p>This provider determines location using
- * satellites. Depending on conditions, this provider may take a while to return
- * a location fix. Requires the permission
- * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.
+ * <p>This provider determines location using GNSS satellites. Depending on conditions, this
+ * provider may take a while to return a location fix. Requires the
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
*
- * <p> The extras Bundle for the GPS location provider can contain the
- * following key/value pairs:
+ * <p>The extras Bundle for the GPS location provider can contain the following key/value pairs:
* <ul>
* <li> satellites - the number of satellites used to derive the fix
* </ul>
@@ -114,26 +104,24 @@ public class LocationManager {
public static final String GPS_PROVIDER = "gps";
/**
- * A special location provider for receiving locations without actually initiating
- * a location fix.
+ * A special location provider for receiving locations without actually initiating a location
+ * fix.
*
- * <p>This provider can be used to passively receive location updates
- * when other applications or services request them without actually requesting
- * the locations yourself. This provider will return locations generated by other
- * providers. You can query the {@link Location#getProvider()} method to determine
- * the origin of the location update. Requires the permission
- * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}, although if the GPS is
- * not enabled this provider might only return coarse fixes.
+ * <p>This provider can be used to passively receive location updates when other applications or
+ * services request them without actually requesting the locations yourself. This provider will
+ * only return locations generated by other providers. You can query the
+ * {@link Location#getProvider()} method to determine the actual provider that supplied the
+ * location update. Requires the {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
+ * permission, although there is no guarantee of fine locations.
*/
public static final String PASSIVE_PROVIDER = "passive";
/**
- * Name of the Fused location provider.
+ * The fused location provider.
*
- * <p>This provider combines inputs for all possible location sources
- * to provide the best possible Location fix. It is implicitly
- * used for all API's that involve the {@link LocationRequest}
- * object.
+ * <p>This provider combines may combine inputs from several location sources to provide the
+ * best possible location fix. It is implicitly used for all API's that involve the
+ * {@link LocationRequest} object.
*
* @hide
*/
@@ -148,23 +136,27 @@ public class LocationManager {
/**
* This key is no longer in use.
*
- * Key used for a Bundle extra holding an Integer status value
- * when a status change is broadcast using a PendingIntent.
+ * <p>Key used for a Bundle extra holding an Integer status value when a status change is
+ * broadcast using a PendingIntent.
*
- * @deprecated Status changes are deprecated and no longer broadcast.
+ * @deprecated Status changes are deprecated and no longer broadcast from Android Q onwards.
*/
@Deprecated
public static final String KEY_STATUS_CHANGED = "status";
/**
- * Key used for a Bundle extra holding an Boolean status value
- * when a provider enabled/disabled event is broadcast using a PendingIntent.
+ * Key used for an extra holding a boolean enabled/disabled status value when a provider
+ * enabled/disabled event is broadcast using a PendingIntent.
+ *
+ * @see #requestLocationUpdates(String, long, float, PendingIntent)
*/
public static final String KEY_PROVIDER_ENABLED = "providerEnabled";
/**
- * Key used for a Bundle extra holding a Location value
- * when a location change is broadcast using a PendingIntent.
+ * Key used for an extra holding a {@link Location} value when a location change is broadcast
+ * using a PendingIntent.
+ *
+ * @see #requestLocationUpdates(String, long, float, PendingIntent)
*/
public static final String KEY_LOCATION_CHANGED = "location";
@@ -238,112 +230,33 @@ public class LocationManager {
public static final String METADATA_SETTINGS_FOOTER_STRING =
"com.android.settings.location.FOOTER_STRING";
- // Map from LocationListeners to their associated ListenerTransport objects
- private final ArrayMap<LocationListener, ListenerTransport> mListeners = new ArrayMap<>();
-
- private class ListenerTransport extends ILocationListener.Stub {
- private static final int TYPE_LOCATION_CHANGED = 1;
- private static final int TYPE_STATUS_CHANGED = 2;
- private static final int TYPE_PROVIDER_ENABLED = 3;
- private static final int TYPE_PROVIDER_DISABLED = 4;
-
- private LocationListener mListener;
- private final Handler mListenerHandler;
-
- ListenerTransport(LocationListener listener, Looper looper) {
- mListener = listener;
-
- if (looper == null) {
- mListenerHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- _handleMessage(msg);
- }
- };
- } else {
- mListenerHandler = new Handler(looper) {
- @Override
- public void handleMessage(Message msg) {
- _handleMessage(msg);
- }
- };
- }
- }
+ private static final long GET_CURRENT_LOCATION_TIMEOUT_MS = 30 * 1000;
- @Override
- public void onLocationChanged(Location location) {
- Message msg = Message.obtain();
- msg.what = TYPE_LOCATION_CHANGED;
- msg.obj = location;
- sendCallbackMessage(msg);
- }
-
- @Override
- public void onStatusChanged(String provider, int status, Bundle extras) {
- Message msg = Message.obtain();
- msg.what = TYPE_STATUS_CHANGED;
- Bundle b = new Bundle();
- b.putString("provider", provider);
- b.putInt("status", status);
- if (extras != null) {
- b.putBundle("extras", extras);
- }
- msg.obj = b;
- sendCallbackMessage(msg);
- }
-
- @Override
- public void onProviderEnabled(String provider) {
- Message msg = Message.obtain();
- msg.what = TYPE_PROVIDER_ENABLED;
- msg.obj = provider;
- sendCallbackMessage(msg);
- }
+ private final Context mContext;
- @Override
- public void onProviderDisabled(String provider) {
- Message msg = Message.obtain();
- msg.what = TYPE_PROVIDER_DISABLED;
- msg.obj = provider;
- sendCallbackMessage(msg);
- }
+ @UnsupportedAppUsage
+ private final ILocationManager mService;
- private void sendCallbackMessage(Message msg) {
- if (!mListenerHandler.sendMessage(msg)) {
- locationCallbackFinished();
- }
- }
+ @GuardedBy("mListeners")
+ private final ArrayMap<LocationListener, LocationListenerTransport> mListeners =
+ new ArrayMap<>();
- private void _handleMessage(Message msg) {
- switch (msg.what) {
- case TYPE_LOCATION_CHANGED:
- Location location = new Location((Location) msg.obj);
- mListener.onLocationChanged(location);
- break;
- case TYPE_STATUS_CHANGED:
- Bundle b = (Bundle) msg.obj;
- String provider = b.getString("provider");
- int status = b.getInt("status");
- Bundle extras = b.getBundle("extras");
- mListener.onStatusChanged(provider, status, extras);
- break;
- case TYPE_PROVIDER_ENABLED:
- mListener.onProviderEnabled((String) msg.obj);
- break;
- case TYPE_PROVIDER_DISABLED:
- mListener.onProviderDisabled((String) msg.obj);
- break;
- }
- locationCallbackFinished();
- }
+ @GuardedBy("mBatchedLocationCallbackManager")
+ private final BatchedLocationCallbackManager mBatchedLocationCallbackManager =
+ new BatchedLocationCallbackManager();
+ private final GnssStatusListenerManager
+ mGnssStatusListenerManager = new GnssStatusListenerManager();
+ private final GnssMeasurementsListenerManager mGnssMeasurementsListenerManager =
+ new GnssMeasurementsListenerManager();
+ private final GnssNavigationMessageListenerManager mGnssNavigationMessageListenerTransport =
+ new GnssNavigationMessageListenerManager();
- private void locationCallbackFinished() {
- try {
- mService.locationCallbackFinished(this);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
+ /**
+ * @hide
+ */
+ public LocationManager(@NonNull Context context, @NonNull ILocationManager service) {
+ mService = service;
+ mContext = context;
}
/**
@@ -371,417 +284,427 @@ public class LocationManager {
}
/**
- * @hide - hide this constructor because it has a parameter
- * of type ILocationManager, which is a system private class. The
- * right way to create an instance of this class is using the
- * factory Context.getSystemService.
+ * Returns the extra location controller package on the device.
+ *
+ * @hide
*/
- public LocationManager(@NonNull Context context, @NonNull ILocationManager service) {
- mService = service;
- mContext = context;
- mGnssMeasurementCallbackTransport =
- new GnssMeasurementCallbackTransport(mContext, mService);
- mGnssNavigationMessageCallbackTransport =
- new GnssNavigationMessageCallbackTransport(mContext, mService);
- mBatchedLocationCallbackTransport =
- new BatchedLocationCallbackTransport(mContext, mService);
-
+ @SystemApi
+ public @Nullable String getExtraLocationControllerPackage() {
+ try {
+ return mService.getExtraLocationControllerPackage();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return null;
+ }
}
- private LocationProvider createProvider(String name, ProviderProperties properties) {
- return new LocationProvider(name, properties);
+ /**
+ * Set the extra location controller package for location services on the device.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+ public void setExtraLocationControllerPackage(@Nullable String packageName) {
+ try {
+ mService.setExtraLocationControllerPackage(packageName);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
}
/**
- * Returns a list of the names of all known location providers.
- * <p>All providers are returned, including ones that are not permitted to
- * be accessed by the calling activity or are currently disabled.
+ * Set whether the extra location controller package is currently enabled on the device.
*
- * @return list of Strings containing names of the provider
+ * @hide
*/
- public @NonNull List<String> getAllProviders() {
+ @SystemApi
+ @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+ public void setExtraLocationControllerPackageEnabled(boolean enabled) {
try {
- return mService.getAllProviders();
+ mService.setExtraLocationControllerPackageEnabled(enabled);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ e.rethrowFromSystemServer();
}
}
/**
- * Returns a list of the names of location providers.
+ * Returns whether extra location controller package is currently enabled on the device.
*
- * @param enabledOnly if true then only the providers which are currently
- * enabled are returned.
- * @return list of Strings containing names of the providers
+ * @hide
*/
- public @NonNull List<String> getProviders(boolean enabledOnly) {
+ @SystemApi
+ public boolean isExtraLocationControllerPackageEnabled() {
try {
- return mService.getProviders(null, enabledOnly);
+ return mService.isExtraLocationControllerPackageEnabled();
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ e.rethrowFromSystemServer();
+ return false;
}
}
/**
- * Returns the information associated with the location provider of the
- * given name, or null if no provider exists by that name.
+ * Set the extra location controller package for location services on the device.
*
- * @param name the provider name
- * @return a LocationProvider, or null
+ * @removed
+ * @deprecated Use {@link #setExtraLocationControllerPackage} instead.
+ * @hide
+ */
+ @Deprecated
+ @SystemApi
+ @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+ public void setLocationControllerExtraPackage(String packageName) {
+ try {
+ mService.setExtraLocationControllerPackage(packageName);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set whether the extra location controller package is currently enabled on the device.
*
- * @throws IllegalArgumentException if name is null or does not exist
- * @throws SecurityException if the caller is not permitted to access the
- * given provider.
+ * @removed
+ * @deprecated Use {@link #setExtraLocationControllerPackageEnabled} instead.
+ * @hide
*/
- public @Nullable LocationProvider getProvider(@NonNull String name) {
- checkProvider(name);
+ @SystemApi
+ @Deprecated
+ @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+ public void setLocationControllerExtraPackageEnabled(boolean enabled) {
try {
- ProviderProperties properties = mService.getProviderProperties(name);
- if (properties == null) {
- return null;
- }
- return createProvider(name, properties);
+ mService.setExtraLocationControllerPackageEnabled(enabled);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ e.rethrowFromSystemServer();
}
}
/**
- * Returns a list of the names of LocationProviders that satisfy the given
- * criteria, or null if none do. Only providers that are permitted to be
- * accessed by the calling activity will be returned.
+ * Returns the current enabled/disabled state of location. To listen for changes, see
+ * {@link #MODE_CHANGED_ACTION}.
*
- * @param criteria the criteria that the returned providers must match
- * @param enabledOnly if true then only the providers which are currently
- * enabled are returned.
- * @return list of Strings containing names of the providers
+ * @return true if location is enabled and false if location is disabled.
*/
- public @NonNull List<String> getProviders(@NonNull Criteria criteria, boolean enabledOnly) {
- checkCriteria(criteria);
+ public boolean isLocationEnabled() {
+ return isLocationEnabledForUser(Process.myUserHandle());
+ }
+
+ /**
+ * Returns the current enabled/disabled state of location for the given user.
+ *
+ * @param userHandle the user to query
+ * @return true if location is enabled and false if location is disabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isLocationEnabledForUser(@NonNull UserHandle userHandle) {
try {
- return mService.getProviders(criteria, enabledOnly);
+ return mService.isLocationEnabledForUser(userHandle.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Returns the name of the provider that best meets the given criteria. Only providers
- * that are permitted to be accessed by the calling activity will be
- * returned. If several providers meet the criteria, the one with the best
- * accuracy is returned. If no provider meets the criteria,
- * the criteria are loosened in the following sequence:
+ * Enables or disables location for the given user.
*
- * <ul>
- * <li> power requirement
- * <li> accuracy
- * <li> bearing
- * <li> speed
- * <li> altitude
- * </ul>
+ * @param enabled true to enable location and false to disable location.
+ * @param userHandle the user to set
*
- * <p> Note that the requirement on monetary cost is not removed
- * in this process.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(WRITE_SECURE_SETTINGS)
+ public void setLocationEnabledForUser(boolean enabled, @NonNull UserHandle userHandle) {
+ Settings.Secure.putIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.LOCATION_MODE,
+ enabled
+ ? Settings.Secure.LOCATION_MODE_ON
+ : Settings.Secure.LOCATION_MODE_OFF,
+ userHandle.getIdentifier());
+ }
+
+ /**
+ * Returns the current enabled/disabled status of the given provider. To listen for changes, see
+ * {@link #PROVIDERS_CHANGED_ACTION}.
*
- * @param criteria the criteria that need to be matched
- * @param enabledOnly if true then only a provider that is currently enabled is returned
- * @return name of the provider that best matches the requirements
+ * Before API version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this method would throw
+ * {@link SecurityException} if the location permissions were not sufficient to use the
+ * specified provider.
+ *
+ * @param provider the name of the provider
+ * @return true if the provider exists and is enabled
+ *
+ * @throws IllegalArgumentException if provider is null
*/
- public @Nullable String getBestProvider(@NonNull Criteria criteria, boolean enabledOnly) {
- checkCriteria(criteria);
+ public boolean isProviderEnabled(@NonNull String provider) {
+ return isProviderEnabledForUser(provider, Process.myUserHandle());
+ }
+
+ /**
+ * Returns the current enabled/disabled status of the given provider and user. Callers should
+ * prefer {@link #isLocationEnabledForUser(UserHandle)} unless they depend on provider-specific
+ * APIs.
+ *
+ * Before API version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this method would throw
+ * {@link SecurityException} if the location permissions were not sufficient to use the
+ * specified provider.
+ *
+ * @param provider the name of the provider
+ * @param userHandle the user to query
+ * @return true if the provider exists and is enabled
+ *
+ * @throws IllegalArgumentException if provider is null
+ * @hide
+ */
+ @SystemApi
+ public boolean isProviderEnabledForUser(
+ @NonNull String provider, @NonNull UserHandle userHandle) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+
try {
- return mService.getBestProvider(criteria, enabledOnly);
+ return mService.isProviderEnabledForUser(provider, userHandle.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Register for location updates using the named provider, and a
- * pending intent.
+ * Method for enabling or disabling a single location provider. This method is deprecated and
+ * functions as a best effort. It should not be relied on in any meaningful sense as providers
+ * may no longer be enabled or disabled by clients.
*
- * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
- * for more detail on how to use this method.
+ * @param provider the name of the provider
+ * @param enabled true to enable the provider. false to disable the provider
+ * @param userHandle the user to set
+ * @return true if the value was set, false otherwise
*
- * @param provider the name of the provider with which to register
- * @param minTime minimum time interval between location updates, in milliseconds
- * @param minDistance minimum distance between location updates, in meters
- * @param listener a {@link LocationListener} whose
- * {@link LocationListener#onLocationChanged} method will be called for
- * each location update
+ * @throws IllegalArgumentException if provider is null
+ * @deprecated Do not manipulate providers individually, use
+ * {@link #setLocationEnabledForUser(boolean, UserHandle)} instead.
+ * @hide
+ */
+ @Deprecated
+ @SystemApi
+ @RequiresPermission(WRITE_SECURE_SETTINGS)
+ public boolean setProviderEnabledForUser(
+ @NonNull String provider, boolean enabled, @NonNull UserHandle userHandle) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+
+ return Settings.Secure.putStringForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+ (enabled ? "+" : "-") + provider,
+ userHandle.getIdentifier());
+ }
+
+ /**
+ * Gets the last known location from the fused provider, or null if there is no last known
+ * location. The returned location may be quite old in some circumstances, so the age of the
+ * location should always be checked.
*
- * @throws IllegalArgumentException if provider is null or doesn't exist
- * on this device
- * @throws IllegalArgumentException if listener is null
- * @throws RuntimeException if the calling thread has no Looper
- * @throws SecurityException if no suitable permission is present
+ * @return the last known location, or null if not available
+ * @throws SecurityException if no suitable location permission is present
+ *
+ * @hide
*/
+ @Nullable
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
- public void requestLocationUpdates(@NonNull String provider, long minTime, float minDistance,
- @NonNull LocationListener listener) {
- android.util.SeempLog.record(47);
- checkProvider(provider);
- checkListener(listener);
-
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(
- provider, minTime, minDistance, false);
- requestLocationUpdates(request, listener, null, null);
+ public Location getLastLocation() {
+ try {
+ return mService.getLastLocation(null, mContext.getPackageName(),
+ mContext.getFeatureId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
- * Register for location updates using the named provider, and a callback on
- * the specified looper thread.
+ * Gets the last known location from the given provider, or null if there is no last known
+ * location. The returned location may be quite old in some circumstances, so the age of the
+ * location should always be checked.
*
- * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
- * for more detail on how to use this method.
+ * <p>This will never activate sensors to compute a new location, and will only ever return a
+ * cached location.
*
- * @param provider the name of the provider with which to register
- * @param minTime minimum time interval between location updates, in milliseconds
- * @param minDistance minimum distance between location updates, in meters
- * @param listener a {@link LocationListener} whose
- * {@link LocationListener#onLocationChanged} method will be called for
- * each location update
- * @param looper a Looper object whose message queue will be used to
- * implement the callback mechanism, or null to make callbacks on the calling
- * thread
+ * <p>See also {@link #getCurrentLocation(String, CancellationSignal, Executor, Consumer)} which
+ * will always attempt to return a current location, but will potentially use additional power
+ * in the course of the attempt as compared to this method.
*
- * @throws IllegalArgumentException if provider is null or doesn't exist
- * @throws IllegalArgumentException if listener is null
+ * @param provider the name of the provider
+ * @return the last known location for the given provider, or null if not available
* @throws SecurityException if no suitable permission is present
+ * @throws IllegalArgumentException if provider is null or doesn't exist
*/
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
- public void requestLocationUpdates(@NonNull String provider, long minTime, float minDistance,
- @NonNull LocationListener listener, @Nullable Looper looper) {
- android.util.SeempLog.record(47);
- checkProvider(provider);
- checkListener(listener);
+ @Nullable
+ public Location getLastKnownLocation(@NonNull String provider) {
+ android.util.SeempLog.record(46);
+ Preconditions.checkArgument(provider != null, "invalid null provider");
LocationRequest request = LocationRequest.createFromDeprecatedProvider(
- provider, minTime, minDistance, false);
- requestLocationUpdates(request, listener, looper, null);
+ provider, 0, 0, true);
+
+ try {
+ return mService.getLastLocation(request, mContext.getPackageName(),
+ mContext.getFeatureId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
- * Register for location updates using a Criteria, and a callback
- * on the specified looper thread.
- *
- * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
- * for more detail on how to use this method.
+ * Create a string that allows an app to identify a listener
*
- * @param minTime minimum time interval between location updates, in milliseconds
- * @param minDistance minimum distance between location updates, in meters
- * @param criteria contains parameters for the location manager to choose the
- * appropriate provider and parameters to compute the location
- * @param listener a {@link LocationListener} whose
- * {@link LocationListener#onLocationChanged} method will be called for
- * each location update
- * @param looper a Looper object whose message queue will be used to
- * implement the callback mechanism, or null to make callbacks on the calling
- * thread
+ * @param listener The listener
*
- * @throws IllegalArgumentException if criteria is null
- * @throws IllegalArgumentException if listener is null
- * @throws SecurityException if no suitable permission is present
+ * @return A identifying string
*/
- @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
- public void requestLocationUpdates(long minTime, float minDistance, @NonNull Criteria criteria,
- @NonNull LocationListener listener, @Nullable Looper looper) {
- android.util.SeempLog.record(47);
- checkCriteria(criteria);
- checkListener(listener);
-
- LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
- criteria, minTime, minDistance, false);
- requestLocationUpdates(request, listener, looper, null);
+ private static String getListenerIdentifier(@NonNull Object listener) {
+ return listener.getClass().getName()
+ + '@'
+ + Integer.toHexString(System.identityHashCode(listener));
}
/**
- * Register for location updates using the named provider, and a
- * pending intent.
+ * Asynchronously returns a single current location fix. This may activate sensors in order to
+ * compute a new location, unlike {@link #getLastKnownLocation(String)}, which will only return
+ * a cached fix if available. The given callback will be invoked once and only once, either with
+ * a valid location fix or with a null location fix if the provider was unable to generate a
+ * valid location.
*
- * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
- * for more detail on how to use this method.
+ * <p>A client may supply an optional {@link CancellationSignal}. If this is used to cancel the
+ * operation, no callback should be expected after the cancellation.
*
- * @param provider the name of the provider with which to register
- * @param minTime minimum time interval between location updates, in milliseconds
- * @param minDistance minimum distance between location updates, in meters
- * @param intent a {@link PendingIntent} to be sent for each location update
+ * <p>This method may return locations from the very recent past (on the order of several
+ * seconds), but will never return older locations (for example, several minutes old or older).
+ * Clients may rely upon the guarantee that if this method returns a location, it will represent
+ * the best estimation of the location of the device in the present moment.
+ *
+ * <p>Clients calling this method from the background may notice that the method fails to
+ * determine a valid location fix more often than while in the foreground. Background
+ * applications may be throttled in their location accesses to some degree.
+ *
+ * @param provider the name of the provider with which to register
+ * @param cancellationSignal an optional signal that allows for cancelling this call
+ * @param executor the callback will take place on this {@link Executor}
+ * @param consumer the callback invoked with either a {@link Location} or null
*
* @throws IllegalArgumentException if provider is null or doesn't exist
- * on this device
- * @throws IllegalArgumentException if intent is null
- * @throws SecurityException if no suitable permission is present
+ * @throws IllegalArgumentException if executor is null
+ * @throws IllegalArgumentException if consumer is null
+ * @throws SecurityException if no suitable permission is present
*/
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
- public void requestLocationUpdates(@NonNull String provider, long minTime, float minDistance,
- @NonNull PendingIntent intent) {
- android.util.SeempLog.record(47);
- checkProvider(provider);
- checkPendingIntent(intent);
+ public void getCurrentLocation(@NonNull String provider,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Location> consumer) {
+ getCurrentLocation(LocationRequest.createFromDeprecatedProvider(provider, 0, 0, true),
+ cancellationSignal, executor, consumer);
+ }
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(
- provider, minTime, minDistance, false);
- requestLocationUpdates(request, null, null, intent);
- }
-
- /**
- * Register for location updates using a Criteria and pending intent.
- *
- * <p>The <code>requestLocationUpdates()</code> and
- * <code>requestSingleUpdate()</code> register the current activity to be
- * updated periodically by the named provider, or by the provider matching
- * the specified {@link Criteria}, with location and status updates.
- *
- * <p> It may take a while to receive the first location update. If
- * an immediate location is required, applications may use the
- * {@link #getLastKnownLocation(String)} method.
- *
- * <p> Location updates are received either by {@link LocationListener}
- * callbacks, or by broadcast intents to a supplied {@link PendingIntent}.
- *
- * <p> If the caller supplied a pending intent, then location updates
- * are sent with a key of {@link #KEY_LOCATION_CHANGED} and a
- * {@link android.location.Location} value.
- *
- * <p> The location update interval can be controlled using the minTime parameter.
- * The elapsed time between location updates will never be less than
- * minTime, although it can be more depending on the Location Provider
- * implementation and the update interval requested by other applications.
- *
- * <p> Choosing a sensible value for minTime is important to conserve
- * battery life. Each location update requires power from
- * GPS, WIFI, Cell and other radios. Select a minTime value as high as
- * possible while still providing a reasonable user experience.
- * If your application is not in the foreground and showing
- * location to the user then your application should avoid using an active
- * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
- * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
- * or greater. If your application is in the foreground and showing
- * location to the user then it is appropriate to select a faster
- * update interval.
- *
- * <p> The minDistance parameter can also be used to control the
- * frequency of location updates. If it is greater than 0 then the
- * location provider will only send your application an update when
- * the location has changed by at least minDistance meters, AND
- * at least minTime milliseconds have passed. However it is more
- * difficult for location providers to save power using the minDistance
- * parameter, so minTime should be the primary tool to conserving battery
- * life.
- *
- * <p> If your application wants to passively observe location
- * updates triggered by other applications, but not consume
- * any additional power otherwise, then use the {@link #PASSIVE_PROVIDER}
- * This provider does not actively turn on or modify active location
- * providers, so you do not need to be as careful about minTime and
- * minDistance. However if your application performs heavy work
- * on a location update (such as network activity) then you should
- * select non-zero values for minTime and/or minDistance to rate-limit
- * your update frequency in the case another application enables a
- * location provider with extremely fast updates.
- *
- * <p>In case the provider is disabled by the user, updates will stop,
- * and a provider availability update will be sent.
- * As soon as the provider is enabled again,
- * location updates will immediately resume and a provider availability
- * update sent. Providers can also send status updates, at any time,
- * with extra's specific to the provider. If a callback was supplied
- * then status and availability updates are via
- * {@link LocationListener#onProviderDisabled},
- * {@link LocationListener#onProviderEnabled} or
- * {@link LocationListener#onStatusChanged}. Alternately, if a
- * pending intent was supplied then status and availability updates
- * are broadcast intents with extra keys of
- * {@link #KEY_PROVIDER_ENABLED} or {@link #KEY_STATUS_CHANGED}.
- *
- * <p> If a {@link LocationListener} is used but with no Looper specified
- * then the calling thread must already
- * be a {@link android.os.Looper} thread such as the main thread of the
- * calling Activity. If a Looper is specified with a {@link LocationListener}
- * then callbacks are made on the supplied Looper thread.
- *
- * <p> When location callbacks are invoked, the system will hold a wakelock
- * on your application's behalf for some period of time, but not
- * indefinitely. If your application requires a long running wakelock
- * within the location callback, you should acquire it yourself.
- *
- * <p class="note"> Prior to Jellybean, the minTime parameter was
- * only a hint, and some location provider implementations ignored it.
- * From Jellybean and onwards it is mandatory for Android compatible
- * devices to observe both the minTime and minDistance parameters.
- *
- * @param minTime minimum time interval between location updates, in milliseconds
- * @param minDistance minimum distance between location updates, in meters
- * @param criteria contains parameters for the location manager to choose the
- * appropriate provider and parameters to compute the location
- * @param intent a {@link PendingIntent} to be sent for each location update
+ /**
+ * Asynchronously returns a single current location fix based on the given
+ * {@link LocationRequest}.
*
- * @throws IllegalArgumentException if criteria is null
- * @throws IllegalArgumentException if intent is null
- * @throws SecurityException if no suitable permission is present
+ * <p>See {@link #getCurrentLocation(String, CancellationSignal, Executor, Consumer)} for more
+ * information.
+ *
+ * @param locationRequest the location request containing location parameters
+ * @param cancellationSignal an optional signal that allows for cancelling this call
+ * @param executor the callback will take place on this {@link Executor}
+ * @param consumer the callback invoked with either a {@link Location} or null
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if executor is null
+ * @throws IllegalArgumentException if consumer is null
+ * @throws SecurityException if no suitable permission is present
+ * @hide
*/
+ @SystemApi
+ @TestApi
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
- public void requestLocationUpdates(long minTime, float minDistance, @NonNull Criteria criteria,
- @NonNull PendingIntent intent) {
- android.util.SeempLog.record(47);
- checkCriteria(criteria);
- checkPendingIntent(intent);
+ public void getCurrentLocation(@NonNull LocationRequest locationRequest,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Location> consumer) {
+ LocationRequest currentLocationRequest = new LocationRequest(locationRequest)
+ .setNumUpdates(1).setExpireIn(GET_CURRENT_LOCATION_TIMEOUT_MS);
- LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
- criteria, minTime, minDistance, false);
- requestLocationUpdates(request, null, null, intent);
+ GetCurrentLocationTransport listenerTransport = new GetCurrentLocationTransport(executor,
+ consumer);
+
+ if (cancellationSignal != null) {
+ cancellationSignal.throwIfCanceled();
+ }
+
+ ICancellationSignal remoteCancellationSignal = CancellationSignal.createTransport();
+
+ try {
+ if (mService.getCurrentLocation(currentLocationRequest, remoteCancellationSignal,
+ listenerTransport, mContext.getPackageName(), mContext.getFeatureId(),
+ getListenerIdentifier(consumer))) {
+ listenerTransport.register(mContext.getSystemService(AlarmManager.class),
+ remoteCancellationSignal);
+ if (cancellationSignal != null) {
+ cancellationSignal.setOnCancelListener(listenerTransport::cancel);
+ }
+ } else {
+ listenerTransport.fail();
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
- * Register for a single location update using the named provider and
- * a callback.
+ * Register for a single location update using the named provider and a callback.
*
- * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
- * for more detail on how to use this method.
+ * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener, Looper)} for
+ * more detail on how to use this method.
*
* @param provider the name of the provider with which to register
- * @param listener a {@link LocationListener} whose
- * {@link LocationListener#onLocationChanged} method will be called when
- * the location update is available
- * @param looper a Looper object whose message queue will be used to
- * implement the callback mechanism, or null to make callbacks on the calling
- * thread
+ * @param listener the listener to receive location updates
+ * @param looper the looper handling listener callbacks, or null to use the looper of the
+ * calling thread
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if listener is null
- * @throws SecurityException if no suitable permission is present
+ * @throws SecurityException if no suitable permission is present
+ * @deprecated Use {@link #getCurrentLocation(String, CancellationSignal, Executor, Consumer)}
+ * instead as it does not carry a risk of extreme battery drain.
*/
+ @Deprecated
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void requestSingleUpdate(
@NonNull String provider, @NonNull LocationListener listener, @Nullable Looper looper) {
android.util.SeempLog.record(64);
- checkProvider(provider);
- checkListener(listener);
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+ Preconditions.checkArgument(listener != null, "invalid null listener");
LocationRequest request = LocationRequest.createFromDeprecatedProvider(
provider, 0, 0, true);
- requestLocationUpdates(request, listener, looper, null);
+ requestLocationUpdates(request, listener, looper);
}
/**
- * Register for a single location update using a Criteria and
- * a callback.
+ * Register for a single location update using a Criteria and a callback.
*
- * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
- * for more detail on how to use this method.
+ * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)} for more detail
+ * on how to use this method.
*
- * @param criteria contains parameters for the location manager to choose the
- * appropriate provider and parameters to compute the location
- * @param listener a {@link LocationListener} whose
- * {@link LocationListener#onLocationChanged} method will be called when
- * the location update is available
- * @param looper a Looper object whose message queue will be used to
- * implement the callback mechanism, or null to make callbacks on the calling
- * thread
+ * @param criteria contains parameters to choose the appropriate provider for location updates
+ * @param listener the listener to receive location updates
+ * @param looper the looper handling listener callbacks, or null to use the looper of the
+ * calling thread
*
* @throws IllegalArgumentException if criteria is null
* @throws IllegalArgumentException if listener is null
- * @throws SecurityException if no suitable permission is present
+ * @throws SecurityException if no suitable permission is present
+ * @deprecated Use {@link #getCurrentLocation(String, CancellationSignal, Executor, Consumer)}
+ * instead as it does not carry a risk of extreme battery drain.
*/
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void requestSingleUpdate(
@@ -789,675 +712,732 @@ public class LocationManager {
@NonNull LocationListener listener,
@Nullable Looper looper) {
android.util.SeempLog.record(64);
- checkCriteria(criteria);
- checkListener(listener);
+ Preconditions.checkArgument(criteria != null, "invalid null criteria");
+ Preconditions.checkArgument(listener != null, "invalid null listener");
LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
criteria, 0, 0, true);
- requestLocationUpdates(request, listener, looper, null);
+ requestLocationUpdates(request, listener, looper);
}
/**
* Register for a single location update using a named provider and pending intent.
*
- * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
- * for more detail on how to use this method.
+ * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)} for more detail
+ * on how to use this method.
*
- * @param provider the name of the provider with which to register
- * @param intent a {@link PendingIntent} to be sent for the location update
+ * @param provider the name of the provider with which to register
+ * @param pendingIntent the pending intent to send location updates
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if intent is null
- * @throws SecurityException if no suitable permission is present
+ * @throws SecurityException if no suitable permission is present
+ * @deprecated Use {@link #getCurrentLocation(String, CancellationSignal, Executor, Consumer)}
+ * instead as it does not carry a risk of extreme battery drain.
*/
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
- public void requestSingleUpdate(@NonNull String provider, @NonNull PendingIntent intent) {
+ public void requestSingleUpdate(@NonNull String provider,
+ @NonNull PendingIntent pendingIntent) {
android.util.SeempLog.record(64);
- checkProvider(provider);
- checkPendingIntent(intent);
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+ checkPendingIntent(pendingIntent);
LocationRequest request = LocationRequest.createFromDeprecatedProvider(
provider, 0, 0, true);
- requestLocationUpdates(request, null, null, intent);
+ requestLocationUpdates(request, pendingIntent);
}
/**
* Register for a single location update using a Criteria and pending intent.
*
- * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
- * for more detail on how to use this method.
+ * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)} for more detail
+ * on how to use this method.
*
- * @param criteria contains parameters for the location manager to choose the
- * appropriate provider and parameters to compute the location
- * @param intent a {@link PendingIntent} to be sent for the location update
+ * @param criteria contains parameters to choose the appropriate provider for location
+ * updates
+ * @param pendingIntent the pending intent to send location updates
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if intent is null
- * @throws SecurityException if no suitable permission is present
+ * @throws SecurityException if no suitable permission is present
+ * @deprecated Use {@link #getCurrentLocation(String, CancellationSignal, Executor, Consumer)}
+ * instead as it does not carry a risk of extreme battery drain.
*/
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
- public void requestSingleUpdate(@NonNull Criteria criteria, @NonNull PendingIntent intent) {
+ public void requestSingleUpdate(@NonNull Criteria criteria,
+ @NonNull PendingIntent pendingIntent) {
android.util.SeempLog.record(64);
- checkCriteria(criteria);
- checkPendingIntent(intent);
+ Preconditions.checkArgument(criteria != null, "invalid null criteria");
+ checkPendingIntent(pendingIntent);
LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
criteria, 0, 0, true);
- requestLocationUpdates(request, null, null, intent);
+ requestLocationUpdates(request, pendingIntent);
+ }
+
+ /**
+ * Register for location updates from the given provider with the given arguments. {@link
+ * LocationListener} callbacks will take place on the given {@link Looper} or {@link Executor}.
+ * If a null {@link Looper} is supplied, the Looper of the calling thread will be used instead.
+ * Only one request can be registered for each unique listener, so any subsequent requests with
+ * the same listener will overwrite all associated arguments.
+ *
+ * <p> It may take a while to receive the first location update. If an immediate location is
+ * required, applications may use the {@link #getLastKnownLocation(String)} method.
+ *
+ * <p> The location update interval can be controlled using the minimum time parameter. The
+ * elapsed time between location updates will never be less than this parameter, although it may
+ * be more depending on location availability and other factors. Choosing a sensible value for
+ * the minimum time parameter is important to conserve battery life. Every location update
+ * requires power from a variety of sensors. Select a minimum time parameter as high as possible
+ * while still providing a reasonable user experience. If your application is not in the
+ * foreground and showing location to the user then your application should consider switching
+ * to the {@link #PASSIVE_PROVIDER} instead.
+ *
+ * <p> The minimum distance parameter can also be used to control the frequency of location
+ * updates. If it is greater than 0 then the location provider will only send your application
+ * an update when the location has changed by at least minDistance meters, AND when the minimum
+ * time has elapsed. However it is more difficult for location providers to save power using the
+ * minimum distance parameter, so the minimum time parameter should be the primary tool for
+ * conserving battery life.
+ *
+ * <p> If your application wants to passively observe location updates triggered by other
+ * applications, but not consume any additional power otherwise, then use the {@link
+ * #PASSIVE_PROVIDER}. This provider does not turn on or modify active location providers, so
+ * you do not need to be as careful about minimum time and minimum distance parameters. However,
+ * if your application performs heavy work on a location update (such as network activity) then
+ * you should select non-zero values for the parameters to rate-limit your update frequency in
+ * the case another application enables a location provider with extremely fast updates.
+ *
+ * <p>In case the provider you have selected is disabled, location updates will cease, and a
+ * provider availability update will be sent. As soon as the provider is enabled again, another
+ * provider availability update will be sent and location updates will immediately resume.
+ *
+ * <p> When location callbacks are invoked, the system will hold a wakelock on your
+ * application's behalf for some period of time, but not indefinitely. If your application
+ * requires a long running wakelock within the location callback, you should acquire it
+ * yourself.
+ *
+ * <p class="note"> Prior to Jellybean, the minTime parameter was only a hint, and some location
+ * provider implementations ignored it. For Jellybean and onwards however, it is mandatory for
+ * Android compatible devices to observe both the minTime and minDistance parameters.
+ *
+ * <p>To unregister for location updates, use {@link #removeUpdates(LocationListener)}.
+ *
+ * @param provider the name of the provider with which to register
+ * @param minTimeMs minimum time interval between location updates in milliseconds
+ * @param minDistanceM minimum distance between location updates in meters
+ * @param listener the listener to receive location updates
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if listener is null
+ * @throws RuntimeException if the calling thread has no Looper
+ * @throws SecurityException if no suitable permission is present
+ */
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestLocationUpdates(@NonNull String provider, long minTimeMs, float minDistanceM,
+ @NonNull LocationListener listener) {
+ android.util.SeempLog.record(47);
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+ Preconditions.checkArgument(listener != null, "invalid null listener");
+
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, minTimeMs, minDistanceM, false);
+ requestLocationUpdates(request, listener, null);
}
/**
- * Register for fused location updates using a LocationRequest and callback.
- *
- * <p>Upon a location update, the system delivers the new {@link Location} to the
- * provided {@link LocationListener}, by calling its {@link
- * LocationListener#onLocationChanged} method.</p>
- *
- * <p>The system will automatically select and enable the best providers
- * to compute a location for your application. It may use only passive
- * locations, or just a single location source, or it may fuse together
- * multiple location sources in order to produce the best possible
- * result, depending on the quality of service requested in the
- * {@link LocationRequest}.
+ * Register for location updates using the named provider, and a callback on
+ * the specified {@link Looper}.
*
- * <p>LocationRequest can be null, in which case the system will choose
- * default, low power parameters for location updates. You will occasionally
- * receive location updates as available, without a major power impact on the
- * system. If your application just needs an occasional location update
- * without any strict demands, then pass a null LocationRequest.
- *
- * <p>Only one LocationRequest can be registered for each unique callback
- * or pending intent. So a subsequent request with the same callback or
- * pending intent will over-write the previous LocationRequest.
- *
- * <p> If a pending intent is supplied then location updates
- * are sent with a key of {@link #KEY_LOCATION_CHANGED} and a
- * {@link android.location.Location} value. If a callback is supplied
- * then location updates are made using the
- * {@link LocationListener#onLocationChanged} callback, on the specified
- * Looper thread. If a {@link LocationListener} is used
- * but with a null Looper then the calling thread must already
- * be a {@link android.os.Looper} thread (such as the main thread) and
- * callbacks will occur on this thread.
- *
- * <p> Provider status updates and availability updates are deprecated
- * because the system is performing provider fusion on the applications
- * behalf. So {@link LocationListener#onProviderDisabled},
- * {@link LocationListener#onProviderEnabled}, {@link LocationListener#onStatusChanged}
- * will not be called, and intents with extra keys of
- * {@link #KEY_PROVIDER_ENABLED} or {@link #KEY_STATUS_CHANGED} will not
- * be received.
- *
- * <p> To unregister for Location updates, use: {@link #removeUpdates(LocationListener)}.
+ * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)}
+ * for more detail on how this method works.
*
- * @param request quality of service required, null for default low power
- * @param listener a {@link LocationListener} whose
- * {@link LocationListener#onLocationChanged} method will be called when
- * the location update is available
- * @param looper a Looper object whose message queue will be used to
- * implement the callback mechanism, or null to make callbacks on the calling
- * thread
+ * @param provider the name of the provider with which to register
+ * @param minTimeMs minimum time interval between location updates in milliseconds
+ * @param minDistanceM minimum distance between location updates in meters
+ * @param listener the listener to receive location updates
+ * @param looper the looper handling listener callbacks, or null to use the looper of the
+ * calling thread
*
+ * @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if listener is null
* @throws SecurityException if no suitable permission is present
- *
- * @hide
*/
- @SystemApi
- @TestApi
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
- public void requestLocationUpdates(
- @NonNull LocationRequest request,
- @NonNull LocationListener listener,
- @Nullable Looper looper) {
+ public void requestLocationUpdates(@NonNull String provider, long minTimeMs, float minDistanceM,
+ @NonNull LocationListener listener, @Nullable Looper looper) {
android.util.SeempLog.record(47);
- checkListener(listener);
- requestLocationUpdates(request, listener, looper, null);
- }
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+ Preconditions.checkArgument(listener != null, "invalid null listener");
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, minTimeMs, minDistanceM, false);
+ requestLocationUpdates(request, listener, looper);
+ }
/**
- * Register for fused location updates using a LocationRequest and a pending intent.
- *
- * <p>Upon a location update, the system delivers the new {@link Location} with your provided
- * {@link PendingIntent}, as the value for {@link LocationManager#KEY_LOCATION_CHANGED}
- * in the intent's extras.</p>
- *
- * <p> To unregister for Location updates, use: {@link #removeUpdates(PendingIntent)}.
+ * Register for location updates using the named provider, and a callback on
+ * the specified {@link Executor}.
*
- * <p> See {@link #requestLocationUpdates(LocationRequest, LocationListener, Looper)}
- * for more detail.
+ * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)}
+ * for more detail on how this method works.
*
- * @param request quality of service required, null for default low power
- * @param intent a {@link PendingIntent} to be sent for the location update
+ * @param provider the name of the provider with which to register
+ * @param minTimeMs minimum time interval between location updates in milliseconds
+ * @param minDistanceM minimum distance between location updates in meters
+ * @param executor the executor handling listener callbacks
+ * @param listener the listener to receive location updates
*
- * @throws IllegalArgumentException if intent is null
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if executor is null
+ * @throws IllegalArgumentException if listener is null
* @throws SecurityException if no suitable permission is present
- *
- * @hide
*/
- @SystemApi
- @TestApi
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void requestLocationUpdates(
- @NonNull LocationRequest request, @NonNull PendingIntent intent) {
+ @NonNull String provider,
+ long minTimeMs,
+ float minDistanceM,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull LocationListener listener) {
android.util.SeempLog.record(47);
- checkPendingIntent(intent);
- requestLocationUpdates(request, null, null, intent);
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, minTimeMs, minDistanceM, false);
+ requestLocationUpdates(request, executor, listener);
}
/**
- * Set the last known location with a new location.
+ * Register for location updates using a provider selected through the given Criteria, and a
+ * callback on the specified {@link Looper}.
*
- * <p>A privileged client can inject a {@link Location} if it has a better estimate of what
- * the recent location is. This is especially useful when the device boots up and the GPS
- * chipset is in the process of getting the first fix. If the client has cached the location,
- * it can inject the {@link Location}, so if an app requests for a {@link Location} from {@link
- * #getLastKnownLocation(String)}, the location information is still useful before getting
- * the first fix.</p>
- *
- * <p> Useful in products like Auto.
+ * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)}
+ * for more detail on how this method works.
*
- * @param newLocation newly available {@link Location} object
- * @return true if update was successful, false if not
+ * @param minTimeMs minimum time interval between location updates in milliseconds
+ * @param minDistanceM minimum distance between location updates in meters
+ * @param criteria contains parameters to choose the appropriate provider for location updates
+ * @param listener the listener to receive location updates
*
+ * @throws IllegalArgumentException if criteria is null
+ * @throws IllegalArgumentException if listener is null
* @throws SecurityException if no suitable permission is present
- *
- * @hide
*/
- @RequiresPermission(allOf = {LOCATION_HARDWARE, ACCESS_FINE_LOCATION})
- public boolean injectLocation(@NonNull Location newLocation) {
- try {
- return mService.injectLocation(newLocation);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- private ListenerTransport wrapListener(LocationListener listener, Looper looper) {
- if (listener == null) return null;
- synchronized (mListeners) {
- ListenerTransport transport = mListeners.get(listener);
- if (transport == null) {
- transport = new ListenerTransport(listener, looper);
- }
- mListeners.put(listener, transport);
- return transport;
- }
- }
-
- @UnsupportedAppUsage
- private void requestLocationUpdates(LocationRequest request, LocationListener listener,
- Looper looper, PendingIntent intent) {
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestLocationUpdates(long minTimeMs, float minDistanceM,
+ @NonNull Criteria criteria, @NonNull LocationListener listener,
+ @Nullable Looper looper) {
android.util.SeempLog.record(47);
+ Preconditions.checkArgument(criteria != null, "invalid null criteria");
+ Preconditions.checkArgument(listener != null, "invalid null listener");
- String packageName = mContext.getPackageName();
-
- // wrap the listener class
- ListenerTransport transport = wrapListener(listener, looper);
-
- try {
- mService.requestLocationUpdates(request, transport, intent, packageName);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
+ criteria, minTimeMs, minDistanceM, false);
+ requestLocationUpdates(request, listener, looper);
}
/**
- * Removes all location updates for the specified LocationListener.
+ * Register for location updates using a provider selected through the given Criteria, and a
+ * callback on the specified {@link Executor}.
+ *
+ * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)}
+ * for more detail on how this method works.
*
- * <p>Following this call, updates will no longer
- * occur for this listener.
+ * @param minTimeMs minimum time interval between location updates in milliseconds
+ * @param minDistanceM minimum distance between location updates in meters
+ * @param criteria contains parameters to choose the appropriate provider for location updates
+ * @param executor the executor handling listener callbacks
+ * @param listener the listener to receive location updates
*
- * @param listener listener object that no longer needs location updates
+ * @throws IllegalArgumentException if criteria is null
+ * @throws IllegalArgumentException if executor is null
* @throws IllegalArgumentException if listener is null
+ * @throws SecurityException if no suitable permission is present
*/
- public void removeUpdates(@NonNull LocationListener listener) {
- checkListener(listener);
- String packageName = mContext.getPackageName();
-
- ListenerTransport transport;
- synchronized (mListeners) {
- transport = mListeners.remove(listener);
- }
- if (transport == null) return;
-
- try {
- mService.removeUpdates(transport, null, packageName);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestLocationUpdates(
+ long minTimeMs,
+ float minDistanceM,
+ @NonNull Criteria criteria,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull LocationListener listener) {
+ android.util.SeempLog.record(47);
+ LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
+ criteria, minTimeMs, minDistanceM, false);
+ requestLocationUpdates(request, executor, listener);
}
/**
- * Removes all location updates for the specified pending intent.
+ * Register for location updates using the named provider, and callbacks delivered via the
+ * provided {@link PendingIntent}.
*
- * <p>Following this call, updates will no longer for this pending intent.
+ * <p>The delivered pending intents will contain extras with the callback information. The keys
+ * used for the extras are {@link #KEY_LOCATION_CHANGED} and {@link #KEY_PROVIDER_ENABLED}. See
+ * the documentation for each respective extra key for information on the values.
*
- * @param intent pending intent object that no longer needs location updates
- * @throws IllegalArgumentException if intent is null
+ * <p>To unregister for location updates, use {@link #removeUpdates(PendingIntent)}.
+ *
+ * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)}
+ * for more detail on how this method works.
+ *
+ * @param provider the name of the provider with which to register
+ * @param minTimeMs minimum time interval between location updates in milliseconds
+ * @param minDistanceM minimum distance between location updates in meters
+ * @param pendingIntent the pending intent to send location updates
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if pendingIntent is null
+ * @throws SecurityException if no suitable permission is present
*/
- public void removeUpdates(@NonNull PendingIntent intent) {
- checkPendingIntent(intent);
- String packageName = mContext.getPackageName();
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestLocationUpdates(@NonNull String provider, long minTimeMs, float minDistanceM,
+ @NonNull PendingIntent pendingIntent) {
+ android.util.SeempLog.record(47);
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+ checkPendingIntent(pendingIntent);
- try {
- mService.removeUpdates(null, intent, packageName);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, minTimeMs, minDistanceM, false);
+ requestLocationUpdates(request, pendingIntent);
}
/**
- * Set a proximity alert for the location given by the position
- * (latitude, longitude) and the given radius.
+ * Register for location updates using a provider selected through the given Criteria, and
+ * callbacks delivered via the provided {@link PendingIntent}.
*
- * <p> When the device
- * detects that it has entered or exited the area surrounding the
- * location, the given PendingIntent will be used to create an Intent
- * to be fired.
+ * <p>See {@link #requestLocationUpdates(String, long, float, PendingIntent)} for more detail on
+ * how this method works.
*
- * <p> The fired Intent will have a boolean extra added with key
- * {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is
- * entering the proximity region; if false, it is exiting.
+ * @param minTimeMs minimum time interval between location updates in milliseconds
+ * @param minDistanceM minimum distance between location updates in meters
+ * @param criteria contains parameters to choose the appropriate provider for location updates
+ * @param pendingIntent the pending intent to send location updates
*
- * <p> Due to the approximate nature of position estimation, if the
- * device passes through the given area briefly, it is possible
- * that no Intent will be fired. Similarly, an Intent could be
- * fired if the device passes very close to the given area but
- * does not actually enter it.
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if pendingIntent is null
+ * @throws SecurityException if no suitable permission is present
+ */
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestLocationUpdates(long minTimeMs, float minDistanceM,
+ @NonNull Criteria criteria, @NonNull PendingIntent pendingIntent) {
+ android.util.SeempLog.record(47);
+ Preconditions.checkArgument(criteria != null, "invalid null criteria");
+ checkPendingIntent(pendingIntent);
+
+ LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
+ criteria, minTimeMs, minDistanceM, false);
+ requestLocationUpdates(request, pendingIntent);
+ }
+
+ /**
+ * Register for location updates using a {@link LocationRequest}, and a callback on the
+ * specified {@link Looper}.
*
- * <p> After the number of milliseconds given by the expiration
- * parameter, the location manager will delete this proximity
- * alert and no longer monitor it. A value of -1 indicates that
- * there should be no expiration time.
+ * <p>The system will automatically select and enable the best provider based on the given
+ * {@link LocationRequest}. The LocationRequest can be null, in which case the system will
+ * choose default low power parameters for location updates, but this is heavily discouraged,
+ * and an explicit LocationRequest should always be provided.
*
- * <p> Internally, this method uses both {@link #NETWORK_PROVIDER}
- * and {@link #GPS_PROVIDER}.
+ * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)}
+ * for more detail on how this method works.
*
- * <p>Before API version 17, this method could be used with
- * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
- * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
- * From API version 17 and onwards, this method requires
- * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
+ * @param locationRequest the location request containing location parameters
+ * @param listener the listener to receive location updates
+ * @param looper the looper handling listener callbacks, or null to use the looper of the
+ * calling thread
*
- * @param latitude the latitude of the central point of the
- * alert region
- * @param longitude the longitude of the central point of the
- * alert region
- * @param radius the radius of the central point of the
- * alert region, in meters
- * @param expiration time for this proximity alert, in milliseconds,
- * or -1 to indicate no expiration
- * @param intent a PendingIntent that will be used to generate an Intent to
- * fire when entry to or exit from the alert region is detected
+ * @throws IllegalArgumentException if listener is null
+ * @throws SecurityException if no suitable permission is present
*
- * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
- * permission is not present
+ * @hide
*/
+ @SystemApi
+ @TestApi
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
- public void addProximityAlert(double latitude, double longitude, float radius, long expiration,
- @NonNull PendingIntent intent) {
- android.util.SeempLog.record(45);
- checkPendingIntent(intent);
- if (expiration < 0) expiration = Long.MAX_VALUE;
-
- Geofence fence = Geofence.createCircle(latitude, longitude, radius);
- LocationRequest request = new LocationRequest().setExpireIn(expiration);
- try {
- mService.requestGeofence(request, fence, intent, mContext.getPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ public void requestLocationUpdates(
+ @Nullable LocationRequest locationRequest,
+ @NonNull LocationListener listener,
+ @Nullable Looper looper) {
+ android.util.SeempLog.record(47);
+ Handler handler = looper == null ? new Handler() : new Handler(looper);
+ requestLocationUpdates(locationRequest, new HandlerExecutor(handler), listener);
}
/**
- * Add a geofence with the specified LocationRequest quality of service.
+ * Register for location updates using a {@link LocationRequest}, and a callback on the
+ * specified {@link Executor}.
*
- * <p> When the device
- * detects that it has entered or exited the area surrounding the
- * location, the given PendingIntent will be used to create an Intent
- * to be fired.
+ * <p>See {@link #requestLocationUpdates(LocationRequest, LocationListener, Looper)} for more
+ * detail on how this method works.
*
- * <p> The fired Intent will have a boolean extra added with key
- * {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is
- * entering the proximity region; if false, it is exiting.
+ * @param locationRequest the location request containing location parameters
+ * @param executor the executor handling listener callbacks
+ * @param listener the listener to receive location updates
*
- * <p> The geofence engine fuses results from all location providers to
- * provide the best balance between accuracy and power. Applications
- * can choose the quality of service required using the
- * {@link LocationRequest} object. If it is null then a default,
- * low power geo-fencing implementation is used. It is possible to cross
- * a geo-fence without notification, but the system will do its best
- * to detect, using {@link LocationRequest} as a hint to trade-off
- * accuracy and power.
- *
- * <p> The power required by the geofence engine can depend on many factors,
- * such as quality and interval requested in {@link LocationRequest},
- * distance to nearest geofence and current device velocity.
- *
- * @param request quality of service required, null for default low power
- * @param fence a geographical description of the geofence area
- * @param intent pending intent to receive geofence updates
- *
- * @throws IllegalArgumentException if fence is null
- * @throws IllegalArgumentException if intent is null
- * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
- * permission is not present
+ * @throws IllegalArgumentException if executor is null
+ * @throws IllegalArgumentException if listener is null
+ * @throws SecurityException if no suitable permission is present
*
* @hide
*/
+ @SystemApi
+ @TestApi
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
- public void addGeofence(
- @NonNull LocationRequest request,
- @NonNull Geofence fence,
- @NonNull PendingIntent intent) {
- checkPendingIntent(intent);
- checkGeofence(fence);
+ public void requestLocationUpdates(
+ @Nullable LocationRequest locationRequest,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull LocationListener listener) {
+ android.util.SeempLog.record(47);
+ synchronized (mListeners) {
+ LocationListenerTransport transport = mListeners.get(listener);
+ if (transport != null) {
+ transport.unregister();
+ } else {
+ transport = new LocationListenerTransport(listener);
+ mListeners.put(listener, transport);
+ }
+ transport.register(executor);
- try {
- mService.requestGeofence(request, fence, intent, mContext.getPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ boolean registered = false;
+ try {
+ mService.requestLocationUpdates(locationRequest, transport, null,
+ mContext.getPackageName(), mContext.getFeatureId(),
+ getListenerIdentifier(listener));
+ registered = true;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } finally {
+ if (!registered) {
+ // allow gc after exception
+ transport.unregister();
+ mListeners.remove(listener);
+ }
+ }
}
}
/**
- * Removes the proximity alert with the given PendingIntent.
+ * Register for location updates using a {@link LocationRequest}, and callbacks delivered via
+ * the provided {@link PendingIntent}.
*
- * <p>Before API version 17, this method could be used with
- * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
- * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
- * From API version 17 and onwards, this method requires
- * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
+ * <p>See {@link #requestLocationUpdates(LocationRequest, LocationListener, Looper)} and
+ * {@link #requestLocationUpdates(String, long, float, PendingIntent)} for more detail on how
+ * this method works.
*
- * @param intent the PendingIntent that no longer needs to be notified of
- * proximity alerts
+ * @param locationRequest the location request containing location parameters
+ * @param pendingIntent the pending intent to send location updates
*
- * @throws IllegalArgumentException if intent is null
- * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
- * permission is not present
+ * @throws IllegalArgumentException if pendingIntent is null
+ * @throws SecurityException if no suitable permission is present
+ *
+ * @hide
*/
- public void removeProximityAlert(@NonNull PendingIntent intent) {
- checkPendingIntent(intent);
- String packageName = mContext.getPackageName();
+ @SystemApi
+ @TestApi
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestLocationUpdates(
+ @Nullable LocationRequest locationRequest,
+ @NonNull PendingIntent pendingIntent) {
+ android.util.SeempLog.record(47);
+ Preconditions.checkArgument(locationRequest != null, "invalid null location request");
+ Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent");
+ if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN) {
+ Preconditions.checkArgument(pendingIntent.isTargetedToPackage(),
+ "pending intent must be targeted to package");
+ }
try {
- mService.removeGeofence(null, intent, packageName);
+ mService.requestLocationUpdates(locationRequest, null, pendingIntent,
+ mContext.getPackageName(), mContext.getFeatureId(),
+ getListenerIdentifier(pendingIntent));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Remove a single geofence.
+ * Set the last known location with a new location.
*
- * <p>This removes only the specified geofence associated with the
- * specified pending intent. All other geofences remain unchanged.
+ * <p>A privileged client can inject a {@link Location} if it has a better estimate of what
+ * the recent location is. This is especially useful when the device boots up and the GPS
+ * chipset is in the process of getting the first fix. If the client has cached the location,
+ * it can inject the {@link Location}, so if an app requests for a {@link Location} from {@link
+ * #getLastKnownLocation(String)}, the location information is still useful before getting
+ * the first fix.
*
- * @param fence a geofence previously passed to {@link #addGeofence}
- * @param intent a pending intent previously passed to {@link #addGeofence}
+ * @param location newly available {@link Location} object
+ * @return true if the location was successfully injected, false otherwise
*
- * @throws IllegalArgumentException if fence is null
- * @throws IllegalArgumentException if intent is null
- * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
- * permission is not present
+ * @throws IllegalArgumentException if location is null
+ * @throws SecurityException if permissions are not present
*
* @hide
*/
- public void removeGeofence(@NonNull Geofence fence, @NonNull PendingIntent intent) {
- checkPendingIntent(intent);
- checkGeofence(fence);
- String packageName = mContext.getPackageName();
+ @RequiresPermission(allOf = {LOCATION_HARDWARE, ACCESS_FINE_LOCATION})
+ public boolean injectLocation(@NonNull Location location) {
+ if (location == null) {
+ IllegalArgumentException e = new IllegalArgumentException("invalid null location");
+ if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R) {
+ throw e;
+ } else {
+ Log.w(TAG, e);
+ return false;
+ }
+ }
try {
- mService.removeGeofence(fence, intent, packageName);
+ return mService.injectLocation(location);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Remove all geofences registered to the specified pending intent.
+ * Removes location updates for the specified {@link LocationListener}. Following this call,
+ * the listener will no longer receive location updates.
*
- * @param intent a pending intent previously passed to {@link #addGeofence}
+ * @param listener listener that no longer needs location updates
*
- * @throws IllegalArgumentException if intent is null
- * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
- * permission is not present
+ * @throws IllegalArgumentException if listener is null
+ */
+ public void removeUpdates(@NonNull LocationListener listener) {
+ Preconditions.checkArgument(listener != null, "invalid null listener");
+
+ synchronized (mListeners) {
+ LocationListenerTransport transport = mListeners.remove(listener);
+ if (transport == null) {
+ return;
+ }
+ transport.unregister();
+
+ try {
+ mService.removeUpdates(transport, null, mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Removes location updates for the specified {@link PendingIntent}. Following this call, the
+ * PendingIntent will no longer receive location updates.
*
- * @hide
+ * @param pendingIntent pending intent that no longer needs location updates
+ *
+ * @throws IllegalArgumentException if pendingIntent is null
*/
- public void removeAllGeofences(@NonNull PendingIntent intent) {
- checkPendingIntent(intent);
- String packageName = mContext.getPackageName();
+ public void removeUpdates(@NonNull PendingIntent pendingIntent) {
+ Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent");
try {
- mService.removeGeofence(null, intent, packageName);
+ mService.removeUpdates(null, pendingIntent, mContext.getPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Returns the current enabled/disabled state of location. To listen for changes, see
- * {@link #MODE_CHANGED_ACTION}.
+ * Returns a list of the names of all known location providers. All providers are returned,
+ * including ones that are not permitted to be accessed by the calling activity or are currently
+ * disabled.
*
- * @return true if location is enabled and false if location is disabled.
+ * @return list of provider names
*/
- public boolean isLocationEnabled() {
- return isLocationEnabledForUser(Process.myUserHandle());
+ public @NonNull List<String> getAllProviders() {
+ try {
+ return mService.getAllProviders();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
- * Returns the current enabled/disabled state of location.
- *
- * @param userHandle the user to query
- * @return true if location is enabled and false if location is disabled.
+ * Returns a list of the names of location providers. Only providers that the caller has
+ * permission to access will be returned.
*
- * @hide
+ * @param enabledOnly if true then only enabled providers are included
+ * @return list of provider names
*/
- @SystemApi
- public boolean isLocationEnabledForUser(@NonNull UserHandle userHandle) {
+ public @NonNull List<String> getProviders(boolean enabledOnly) {
try {
- return mService.isLocationEnabledForUser(userHandle.getIdentifier());
+ return mService.getProviders(null, enabledOnly);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Enables or disables the location setting.
+ * Returns a list of the names of providers that satisfy the given criteria. Only providers that
+ * the caller has permission to access will be returned.
*
- * @param enabled true to enable location and false to disable location.
- * @param userHandle the user to set
+ * @param criteria the criteria that providers must match
+ * @param enabledOnly if true then only enabled providers are included
+ * @return list of provider names
*
- * @hide
+ * @throws IllegalArgumentException if criteria is null
*/
- @SystemApi
- @TestApi
- @RequiresPermission(WRITE_SECURE_SETTINGS)
- public void setLocationEnabledForUser(boolean enabled, @NonNull UserHandle userHandle) {
- Settings.Secure.putIntForUser(
- mContext.getContentResolver(),
- Settings.Secure.LOCATION_MODE,
- enabled
- ? Settings.Secure.LOCATION_MODE_ON
- : Settings.Secure.LOCATION_MODE_OFF,
- userHandle.getIdentifier());
+ public @NonNull List<String> getProviders(@NonNull Criteria criteria, boolean enabledOnly) {
+ Preconditions.checkArgument(criteria != null, "invalid null criteria");
+
+ try {
+ return mService.getProviders(criteria, enabledOnly);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
- * Returns the current enabled/disabled status of the given provider. To listen for changes, see
- * {@link #PROVIDERS_CHANGED_ACTION}.
+ * Returns the name of the provider that best meets the given criteria. Only providers that are
+ * permitted to be accessed by the caller will be returned. If several providers meet the
+ * criteria, the one with the best accuracy is returned. If no provider meets the criteria, the
+ * criteria are loosened in the following order:
*
- * Before API version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this method would throw
- * {@link SecurityException} if the location permissions were not sufficient to use the
- * specified provider.
+ * <ul>
+ * <li> power requirement
+ * <li> accuracy
+ * <li> bearing
+ * <li> speed
+ * <li> altitude
+ * </ul>
*
- * @param provider the name of the provider
- * @return true if the provider exists and is enabled
+ * <p> Note that the requirement on monetary cost is not removed in this process.
*
- * @throws IllegalArgumentException if provider is null
+ * @param criteria the criteria that need to be matched
+ * @param enabledOnly if true then only enabled providers are included
+ * @return name of the provider that best matches the criteria, or null if none match
+ *
+ * @throws IllegalArgumentException if criteria is null
*/
- public boolean isProviderEnabled(@NonNull String provider) {
- return isProviderEnabledForUser(provider, Process.myUserHandle());
+ public @Nullable String getBestProvider(@NonNull Criteria criteria, boolean enabledOnly) {
+ Preconditions.checkArgument(criteria != null, "invalid null criteria");
+
+ try {
+ return mService.getBestProvider(criteria, enabledOnly);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
- * Returns the current enabled/disabled status of the given provider and user. Callers should
- * prefer {@link #isLocationEnabledForUser(UserHandle)} unless they depend on provider-specific
- * APIs.
+ * Returns the information about the location provider with the given name, or null if no
+ * provider exists by that name.
*
- * Before API version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this method would throw
- * {@link SecurityException} if the location permissions were not sufficient to use the
- * specified provider.
- *
- * @param provider the name of the provider
- * @param userHandle the user to query
- * @return true if the provider exists and is enabled
+ * @param provider the provider name
+ * @return location provider information, or null if provider does not exist
*
* @throws IllegalArgumentException if provider is null
- * @hide
*/
- @SystemApi
- public boolean isProviderEnabledForUser(
- @NonNull String provider, @NonNull UserHandle userHandle) {
- checkProvider(provider);
-
+ public @Nullable LocationProvider getProvider(@NonNull String provider) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
try {
- return mService.isProviderEnabledForUser(provider, userHandle.getIdentifier());
+ ProviderProperties properties = mService.getProviderProperties(provider);
+ if (properties == null) {
+ return null;
+ }
+ return new LocationProvider(provider, properties);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Method for enabling or disabling a single location provider. This method is deprecated and
- * functions as a best effort. It should not be relied on in any meaningful sense as providers
- * may no longer be enabled or disabled by clients.
- *
- * @param provider the name of the provider
- * @param enabled true to enable the provider. false to disable the provider
- * @param userHandle the user to set
- * @return true if the value was set, false otherwise
+ * Returns true if the given package name matches a location provider package, and false
+ * otherwise.
*
- * @throws IllegalArgumentException if provider is null
- * @deprecated Do not manipulate providers individually, use
- * {@link #setLocationEnabledForUser(boolean, UserHandle)} instead.
* @hide
*/
- @Deprecated
@SystemApi
- @RequiresPermission(WRITE_SECURE_SETTINGS)
- public boolean setProviderEnabledForUser(
- @NonNull String provider, boolean enabled, @NonNull UserHandle userHandle) {
- checkProvider(provider);
-
- return Settings.Secure.putStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- (enabled ? "+" : "-") + provider,
- userHandle.getIdentifier());
+ @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
+ public boolean isProviderPackage(@NonNull String packageName) {
+ try {
+ return mService.isProviderPackage(packageName);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return false;
+ }
}
/**
- * Get the last known location.
- *
- * <p>This location could be very old so use
- * {@link Location#getElapsedRealtimeNanos} to calculate its age. It can
- * also return null if no previous location is available.
- *
- * <p>Always returns immediately.
- *
- * @return The last known location, or null if not available
- * @throws SecurityException if no suitable permission is present
+ * Returns a list of packages associated with the given provider,
+ * and an empty list if no package is associated with the provider.
*
* @hide
*/
+ @TestApi
+ @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
@Nullable
- public Location getLastLocation() {
- String packageName = mContext.getPackageName();
-
+ public List<String> getProviderPackages(@NonNull String provider) {
try {
- return mService.getLastLocation(null, packageName);
+ return mService.getProviderPackages(provider);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ e.rethrowFromSystemServer();
+ return Collections.emptyList();
}
}
/**
- * Returns a Location indicating the data from the last known
- * location fix obtained from the given provider.
- *
- * <p> This can be done
- * without starting the provider. Note that this location could
- * be out-of-date, for example if the device was turned off and
- * moved to another location.
- *
- * <p> If the provider is currently disabled, null is returned.
- *
- * @param provider the name of the provider
- * @return the last known location for the provider, or null
+ * Sends additional commands to a location provider. Can be used to support provider specific
+ * extensions to the Location Manager API.
*
- * @throws SecurityException if no suitable permission is present
- * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @param provider name of the location provider
+ * @param command name of the command to send to the provider
+ * @param extras optional arguments for the command, or null
+ * @return true always, the return value may be ignored
*/
- @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
- @Nullable
- public Location getLastKnownLocation(@NonNull String provider) {
- android.util.SeempLog.record(46);
- checkProvider(provider);
- String packageName = mContext.getPackageName();
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(
- provider, 0, 0, true);
+ public boolean sendExtraCommand(
+ @NonNull String provider, @NonNull String command, @Nullable Bundle extras) {
+ android.util.SeempLog.record(48);
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+ Preconditions.checkArgument(command != null, "invalid null command");
try {
- return mService.getLastLocation(request, packageName);
+ return mService.sendExtraCommand(provider, command, extras);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Creates a mock location provider and adds it to the set of active providers.
+ * Creates a test location provider and adds it to the set of active providers. This provider
+ * will replace any provider with the same name that exists prior to this call.
*
- * @param name the provider name
+ * @param provider the provider name
*
+ * @throws IllegalArgumentException if provider is null
* @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
* mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
* allowed} for your app.
- * @throws IllegalArgumentException if a provider with the given name already exists
*/
public void addTestProvider(
- @NonNull String name, boolean requiresNetwork, boolean requiresSatellite,
+ @NonNull String provider, boolean requiresNetwork, boolean requiresSatellite,
boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+
ProviderProperties properties = new ProviderProperties(requiresNetwork,
requiresSatellite, requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed,
supportsBearing, powerRequirement, accuracy);
- if (name.matches(LocationProvider.BAD_CHARS_REGEX)) {
- throw new IllegalArgumentException("provider name contains illegal character: " + name);
- }
-
try {
- mService.addTestProvider(name, properties, mContext.getOpPackageName());
+ mService.addTestProvider(provider, properties, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Removes the mock location provider with the given name.
+ * Removes the test location provider with the given name or does nothing if no such test
+ * location provider exists.
*
* @param provider the provider name
*
+ * @throws IllegalArgumentException if provider is null
* @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
* mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
* allowed} for your app.
- * @throws IllegalArgumentException if no provider with the given name exists
*/
public void removeTestProvider(@NonNull String provider) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+
try {
mService.removeTestProvider(provider, mContext.getOpPackageName());
} catch (RemoteException e) {
@@ -1466,60 +1446,53 @@ public class LocationManager {
}
/**
- * Sets a mock location for the given provider.
- * <p>This location will be used in place of any actual location from the provider.
- * The location object must have a minimum number of fields set to be
- * considered a valid LocationProvider Location, as per documentation
- * on {@link Location} class.
+ * Sets a new location for the given test provider. This location will be identiable as a mock
+ * location to all clients via {@link Location#isFromMockProvider()}.
+ *
+ * <p>The location object must have a minimum number of fields set to be considered valid, as
+ * per documentation on {@link Location} class.
*
* @param provider the provider name
- * @param loc the mock location
+ * @param location the mock location
*
* @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
* mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
* allowed} for your app.
- * @throws IllegalArgumentException if no provider with the given name exists
- * @throws IllegalArgumentException if the location is incomplete
+ * @throws IllegalArgumentException if the provider is null or not a test provider
+ * @throws IllegalArgumentException if the location is null or incomplete
*/
- public void setTestProviderLocation(@NonNull String provider, @NonNull Location loc) {
- if (!loc.isComplete()) {
+ public void setTestProviderLocation(@NonNull String provider, @NonNull Location location) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+ Preconditions.checkArgument(location != null, "invalid null location");
+
+ if (!location.isComplete()) {
IllegalArgumentException e = new IllegalArgumentException(
- "Incomplete location object, missing timestamp or accuracy? " + loc);
+ "Incomplete location object, missing timestamp or accuracy? " + location);
if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN) {
- // just log on old platform (for backwards compatibility)
Log.w(TAG, e);
- loc.makeComplete();
+ location.makeComplete();
} else {
- // really throw it!
throw e;
}
}
try {
- mService.setTestProviderLocation(provider, loc, mContext.getOpPackageName());
+ mService.setTestProviderLocation(provider, location, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Removes any mock location associated with the given provider.
- *
- * @param provider the provider name
- *
- * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
- * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
- * allowed} for your app.
- * @throws IllegalArgumentException if no provider with the given name exists
+ * Does nothing.
*
- * @deprecated This function has always been a no-op, and may be removed in the future.
+ * @deprecated This method has always been a no-op, and may be removed in the future.
*/
@Deprecated
public void clearTestProviderLocation(@NonNull String provider) {}
/**
- * Sets a mock enabled value for the given provider. This value will be used in place
- * of any actual value from the provider.
+ * Sets the given test provider to be enabled or disabled.
*
* @param provider the provider name
* @param enabled the mock enabled value
@@ -1527,9 +1500,11 @@ public class LocationManager {
* @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
* mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
* allowed} for your app.
- * @throws IllegalArgumentException if no provider with the given name exists
+ * @throws IllegalArgumentException if provider is null or not a test provider
*/
public void setTestProviderEnabled(@NonNull String provider, boolean enabled) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+
try {
mService.setTestProviderEnabled(provider, enabled, mContext.getOpPackageName());
} catch (RemoteException e) {
@@ -1538,14 +1513,8 @@ public class LocationManager {
}
/**
- * Removes any mock enabled value associated with the given provider.
- *
- * @param provider the provider name
- *
- * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
- * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
- * allowed} for your app.
- * @throws IllegalArgumentException if no provider with the given name exists
+ * Equivalent to calling {@link #setTestProviderEnabled(String, boolean)} to disable a test
+ * provider.
*
* @deprecated Use {@link #setTestProviderEnabled(String, boolean)} instead.
*/
@@ -1579,7 +1548,7 @@ public class LocationManager {
@TestApi
@NonNull
public List<LocationRequest> getTestProviderCurrentRequests(String providerName) {
- checkProvider(providerName);
+ Preconditions.checkArgument(providerName != null, "invalid null provider");
try {
return mService.getTestProviderCurrentRequests(providerName,
mContext.getOpPackageName());
@@ -1588,200 +1557,328 @@ public class LocationManager {
}
}
- // --- GPS-specific support ---
-
- // This class is used to send Gnss status events to the client's specific thread.
- private class GnssStatusListenerTransport extends IGnssStatusListener.Stub {
-
- private final GnssStatus.Callback mGnssCallback;
- private final OnNmeaMessageListener mGnssNmeaListener;
-
- private class GnssHandler extends Handler {
- GnssHandler(Handler handler) {
- super(handler != null ? handler.getLooper() : Looper.myLooper());
- }
+ /**
+ * Set a proximity alert for the location given by the position
+ * (latitude, longitude) and the given radius.
+ *
+ * <p> When the device
+ * detects that it has entered or exited the area surrounding the
+ * location, the given PendingIntent will be used to create an Intent
+ * to be fired.
+ *
+ * <p> The fired Intent will have a boolean extra added with key
+ * {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is
+ * entering the proximity region; if false, it is exiting.
+ *
+ * <p> Due to the approximate nature of position estimation, if the
+ * device passes through the given area briefly, it is possible
+ * that no Intent will be fired. Similarly, an Intent could be
+ * fired if the device passes very close to the given area but
+ * does not actually enter it.
+ *
+ * <p> After the number of milliseconds given by the expiration
+ * parameter, the location manager will delete this proximity
+ * alert and no longer monitor it. A value of -1 indicates that
+ * there should be no expiration time.
+ *
+ * <p> Internally, this method uses both {@link #NETWORK_PROVIDER}
+ * and {@link #GPS_PROVIDER}.
+ *
+ * <p>Before API version 17, this method could be used with
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
+ * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+ * From API version 17 and onwards, this method requires
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
+ *
+ * @param latitude the latitude of the central point of the
+ * alert region
+ * @param longitude the longitude of the central point of the
+ * alert region
+ * @param radius the radius of the central point of the
+ * alert region, in meters
+ * @param expiration time for this proximity alert, in milliseconds,
+ * or -1 to indicate no expiration
+ * @param intent a PendingIntent that will be used to generate an Intent to
+ * fire when entry to or exit from the alert region is detected
+ *
+ * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
+ * permission is not present
+ */
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void addProximityAlert(double latitude, double longitude, float radius, long expiration,
+ @NonNull PendingIntent intent) {
+ android.util.SeempLog.record(45);
+ checkPendingIntent(intent);
+ if (expiration < 0) expiration = Long.MAX_VALUE;
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case NMEA_RECEIVED:
- synchronized (mNmeaBuffer) {
- for (Nmea nmea : mNmeaBuffer) {
- mGnssNmeaListener.onNmeaMessage(nmea.mNmea, nmea.mTimestamp);
- }
- mNmeaBuffer.clear();
- }
- break;
- case GNSS_EVENT_STARTED:
- mGnssCallback.onStarted();
- break;
- case GNSS_EVENT_STOPPED:
- mGnssCallback.onStopped();
- break;
- case GNSS_EVENT_FIRST_FIX:
- mGnssCallback.onFirstFix(mTimeToFirstFix);
- break;
- case GNSS_EVENT_SATELLITE_STATUS:
- mGnssCallback.onSatelliteStatusChanged(mGnssStatus);
- break;
- default:
- break;
- }
- }
+ Geofence fence = Geofence.createCircle(latitude, longitude, radius);
+ LocationRequest request = new LocationRequest().setExpireIn(expiration);
+ try {
+ mService.requestGeofence(request, fence, intent, mContext.getPackageName(),
+ mContext.getFeatureId(), getListenerIdentifier(intent));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
+ }
- private final Handler mGnssHandler;
-
- private static final int NMEA_RECEIVED = 1;
- private static final int GNSS_EVENT_STARTED = 2;
- private static final int GNSS_EVENT_STOPPED = 3;
- private static final int GNSS_EVENT_FIRST_FIX = 4;
- private static final int GNSS_EVENT_SATELLITE_STATUS = 5;
-
- private class Nmea {
- long mTimestamp;
- String mNmea;
+ /**
+ * Removes the proximity alert with the given PendingIntent.
+ *
+ * <p>Before API version 17, this method could be used with
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
+ * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+ * From API version 17 and onwards, this method requires
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
+ *
+ * @param intent the PendingIntent that no longer needs to be notified of
+ * proximity alerts
+ *
+ * @throws IllegalArgumentException if intent is null
+ * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
+ * permission is not present
+ */
+ public void removeProximityAlert(@NonNull PendingIntent intent) {
+ checkPendingIntent(intent);
- Nmea(long timestamp, String nmea) {
- mTimestamp = timestamp;
- mNmea = nmea;
- }
+ try {
+ mService.removeGeofence(null, intent, mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- private final ArrayList<Nmea> mNmeaBuffer;
+ }
- GnssStatusListenerTransport(GnssStatus.Callback callback, Handler handler) {
- mGnssCallback = callback;
- mGnssHandler = new GnssHandler(handler);
- mGnssNmeaListener = null;
- mNmeaBuffer = null;
- }
+ /**
+ * Add a geofence with the specified LocationRequest quality of service.
+ *
+ * <p> When the device
+ * detects that it has entered or exited the area surrounding the
+ * location, the given PendingIntent will be used to create an Intent
+ * to be fired.
+ *
+ * <p> The fired Intent will have a boolean extra added with key
+ * {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is
+ * entering the proximity region; if false, it is exiting.
+ *
+ * <p> The geofence engine fuses results from all location providers to
+ * provide the best balance between accuracy and power. Applications
+ * can choose the quality of service required using the
+ * {@link LocationRequest} object. If it is null then a default,
+ * low power geo-fencing implementation is used. It is possible to cross
+ * a geo-fence without notification, but the system will do its best
+ * to detect, using {@link LocationRequest} as a hint to trade-off
+ * accuracy and power.
+ *
+ * <p> The power required by the geofence engine can depend on many factors,
+ * such as quality and interval requested in {@link LocationRequest},
+ * distance to nearest geofence and current device velocity.
+ *
+ * @param request quality of service required, null for default low power
+ * @param fence a geographical description of the geofence area
+ * @param intent pending intent to receive geofence updates
+ *
+ * @throws IllegalArgumentException if fence is null
+ * @throws IllegalArgumentException if intent is null
+ * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
+ * permission is not present
+ *
+ * @hide
+ */
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void addGeofence(
+ @NonNull LocationRequest request,
+ @NonNull Geofence fence,
+ @NonNull PendingIntent intent) {
+ checkPendingIntent(intent);
+ Preconditions.checkArgument(fence != null, "invalid null geofence");
- GnssStatusListenerTransport(OnNmeaMessageListener listener, Handler handler) {
- mGnssCallback = null;
- mGnssHandler = new GnssHandler(handler);
- mGnssNmeaListener = listener;
- mNmeaBuffer = new ArrayList<>();
+ try {
+ mService.requestGeofence(request, fence, intent, mContext.getPackageName(),
+ mContext.getFeatureId(), getListenerIdentifier(intent));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
+ }
- GnssStatusListenerTransport(GpsStatus.Listener listener, Handler handler) {
- mGnssHandler = new GnssHandler(handler);
- mNmeaBuffer = null;
- mGnssCallback = listener != null ? new GnssStatus.Callback() {
- @Override
- public void onStarted() {
- listener.onGpsStatusChanged(GpsStatus.GPS_EVENT_STARTED);
- }
+ /**
+ * Remove a single geofence.
+ *
+ * <p>This removes only the specified geofence associated with the
+ * specified pending intent. All other geofences remain unchanged.
+ *
+ * @param fence a geofence previously passed to {@link #addGeofence}
+ * @param intent a pending intent previously passed to {@link #addGeofence}
+ *
+ * @throws IllegalArgumentException if fence is null
+ * @throws IllegalArgumentException if intent is null
+ * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
+ * permission is not present
+ *
+ * @hide
+ */
+ public void removeGeofence(@NonNull Geofence fence, @NonNull PendingIntent intent) {
+ checkPendingIntent(intent);
+ Preconditions.checkArgument(fence != null, "invalid null geofence");
- @Override
- public void onStopped() {
- listener.onGpsStatusChanged(GpsStatus.GPS_EVENT_STOPPED);
- }
+ try {
+ mService.removeGeofence(fence, intent, mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
- @Override
- public void onFirstFix(int ttff) {
- listener.onGpsStatusChanged(GpsStatus.GPS_EVENT_FIRST_FIX);
- }
+ /**
+ * Remove all geofences registered to the specified pending intent.
+ *
+ * @param intent a pending intent previously passed to {@link #addGeofence}
+ *
+ * @throws IllegalArgumentException if intent is null
+ * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
+ * permission is not present
+ *
+ * @hide
+ */
+ public void removeAllGeofences(@NonNull PendingIntent intent) {
+ checkPendingIntent(intent);
- @Override
- public void onSatelliteStatusChanged(GnssStatus status) {
- listener.onGpsStatusChanged(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
- }
- } : null;
- mGnssNmeaListener = null;
+ try {
+ mService.removeGeofence(null, intent, mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
+ }
- @Override
- public void onGnssStarted() {
- if (mGnssCallback != null) {
- mGnssHandler.obtainMessage(GNSS_EVENT_STARTED).sendToTarget();
- }
- }
+ // ================= GNSS APIs =================
- @Override
- public void onGnssStopped() {
- if (mGnssCallback != null) {
- mGnssHandler.obtainMessage(GNSS_EVENT_STOPPED).sendToTarget();
+ /**
+ * Returns the supported capabilities of the GNSS chipset.
+ *
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public @NonNull GnssCapabilities getGnssCapabilities() {
+ try {
+ long gnssCapabilities = mService.getGnssCapabilities(mContext.getPackageName());
+ if (gnssCapabilities == GnssCapabilities.INVALID_CAPABILITIES) {
+ gnssCapabilities = 0L;
}
+ return GnssCapabilities.of(gnssCapabilities);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
+ }
- @Override
- public void onFirstFix(int ttff) {
- if (mGnssCallback != null) {
- mTimeToFirstFix = ttff;
- mGnssHandler.obtainMessage(GNSS_EVENT_FIRST_FIX).sendToTarget();
- }
+ /**
+ * Returns the model year of the GNSS hardware and software build. More details, such as build
+ * date, may be available in {@link #getGnssHardwareModelName()}. May return 0 if the model year
+ * is less than 2016.
+ */
+ public int getGnssYearOfHardware() {
+ try {
+ return mService.getGnssYearOfHardware();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
+ }
- @Override
- public void onSvStatusChanged(int svCount, int[] prnWithFlags,
- float[] cn0s, float[] elevations, float[] azimuths, float[] carrierFreqs) {
- if (mGnssCallback != null) {
- mGnssStatus = new GnssStatus(svCount, prnWithFlags, cn0s, elevations, azimuths,
- carrierFreqs);
-
- mGnssHandler.removeMessages(GNSS_EVENT_SATELLITE_STATUS);
- mGnssHandler.obtainMessage(GNSS_EVENT_SATELLITE_STATUS).sendToTarget();
- }
+ /**
+ * Returns the Model Name (including Vendor and Hardware/Software Version) of the GNSS hardware
+ * driver.
+ *
+ * <p> No device-specific serial number or ID is returned from this API.
+ *
+ * <p> Will return null when the GNSS hardware abstraction layer does not support providing
+ * this value.
+ */
+ @Nullable
+ public String getGnssHardwareModelName() {
+ try {
+ return mService.getGnssHardwareModelName();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
+ }
- @Override
- public void onNmeaReceived(long timestamp, String nmea) {
- if (mGnssNmeaListener != null) {
- synchronized (mNmeaBuffer) {
- mNmeaBuffer.add(new Nmea(timestamp, nmea));
- }
-
- mGnssHandler.removeMessages(NMEA_RECEIVED);
- mGnssHandler.obtainMessage(NMEA_RECEIVED).sendToTarget();
- }
+ /**
+ * Retrieves information about the current status of the GPS engine. This should only be called
+ * from within the {@link GpsStatus.Listener#onGpsStatusChanged} callback to ensure that the
+ * data is copied atomically.
+ *
+ * The caller may either pass in an existing {@link GpsStatus} object to be overwritten, or pass
+ * null to create a new {@link GpsStatus} object.
+ *
+ * @param status object containing GPS status details, or null.
+ * @return status object containing updated GPS status.
+ *
+ * @deprecated GpsStatus APIs are deprecated, use {@link GnssStatus} APIs instead.
+ */
+ @Deprecated
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public @Nullable GpsStatus getGpsStatus(@Nullable GpsStatus status) {
+ if (status == null) {
+ status = new GpsStatus();
}
+ // When mGnssStatus is null, that means that this method is called outside
+ // onGpsStatusChanged(). Return an empty status to maintain backwards compatibility.
+ GnssStatus gnssStatus = mGnssStatusListenerManager.getGnssStatus();
+ int ttff = mGnssStatusListenerManager.getTtff();
+ if (gnssStatus != null) {
+ status.setStatus(gnssStatus, ttff);
+ }
+ return status;
}
/**
* Adds a GPS status listener.
*
* @param listener GPS status listener object to register
- *
* @return true if the listener was successfully added
- *
* @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
- * @deprecated use {@link #registerGnssStatusCallback(GnssStatus.Callback)} instead.
+ *
+ * @deprecated use {@link #registerGnssStatusCallback(GnssStatus.Callback)} instead. No longer
+ * supported in apps targeting R and above.
*/
@Deprecated
@RequiresPermission(ACCESS_FINE_LOCATION)
public boolean addGpsStatusListener(GpsStatus.Listener listener) {
android.util.SeempLog.record(43);
- boolean result;
-
- if (mGpsStatusListeners.get(listener) != null) {
- return true;
+ UnsupportedOperationException ex = new UnsupportedOperationException(
+ "GpsStatus APIs not supported in R and above, use GnssStatus APIs instead");
+ if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R) {
+ throw ex;
+ } else {
+ Log.w(TAG, ex);
}
+
try {
- GnssStatusListenerTransport transport = new GnssStatusListenerTransport(listener, null);
- result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
- if (result) {
- mGpsStatusListeners.put(listener, transport);
- }
+ return mGnssStatusListenerManager.addListener(listener, new Handler());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
-
- return result;
}
/**
* Removes a GPS status listener.
*
* @param listener GPS status listener object to remove
- * @deprecated use {@link #unregisterGnssStatusCallback(GnssStatus.Callback)} instead.
+ *
+ * @deprecated use {@link #unregisterGnssStatusCallback(GnssStatus.Callback)} instead. No longer
+ * supported in apps targeting R and above.
*/
@Deprecated
public void removeGpsStatusListener(GpsStatus.Listener listener) {
+ UnsupportedOperationException ex = new UnsupportedOperationException(
+ "GpsStatus APIs not supported in R and above, use GnssStatus APIs instead");
+ if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R) {
+ throw ex;
+ } else {
+ Log.w(TAG, ex);
+ }
+
try {
- GnssStatusListenerTransport transport = mGpsStatusListeners.remove(listener);
- if (transport != null) {
- mService.unregisterGnssStatusCallback(transport);
- }
+ mGnssStatusListenerManager.removeListener(listener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1791,11 +1888,14 @@ public class LocationManager {
* Registers a GNSS status callback.
*
* @param callback GNSS status callback object to register
- *
* @return true if the listener was successfully added
*
* @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+ *
+ * @deprecated Use {@link #registerGnssStatusCallback(GnssStatus.Callback, Handler)} or {@link
+ * #registerGnssStatusCallback(Executor, GnssStatus.Callback)} instead.
*/
+ @Deprecated
@RequiresPermission(ACCESS_FINE_LOCATION)
public boolean registerGnssStatusCallback(@NonNull GnssStatus.Callback callback) {
return registerGnssStatusCallback(callback, null);
@@ -1805,33 +1905,46 @@ public class LocationManager {
* Registers a GNSS status callback.
*
* @param callback GNSS status callback object to register
- * @param handler the handler that the callback runs on.
- *
+ * @param handler a handler with a looper that the callback runs on
* @return true if the listener was successfully added
*
+ * @throws IllegalArgumentException if callback is null
* @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
*/
@RequiresPermission(ACCESS_FINE_LOCATION)
public boolean registerGnssStatusCallback(
@NonNull GnssStatus.Callback callback, @Nullable Handler handler) {
- boolean result;
- synchronized (mGnssStatusListeners) {
- if (mGnssStatusListeners.get(callback) != null) {
- return true;
- }
- try {
- GnssStatusListenerTransport transport =
- new GnssStatusListenerTransport(callback, handler);
- result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
- if (result) {
- mGnssStatusListeners.put(callback, transport);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ if (handler == null) {
+ handler = new Handler();
}
- return result;
+ try {
+ return mGnssStatusListenerManager.addListener(callback, handler);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Registers a GNSS status callback.
+ *
+ * @param executor the executor that the callback runs on
+ * @param callback GNSS status callback object to register
+ * @return true if the listener was successfully added
+ *
+ * @throws IllegalArgumentException if executor is null
+ * @throws IllegalArgumentException if callback is null
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+ */
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean registerGnssStatusCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull GnssStatus.Callback callback) {
+ try {
+ return mGnssStatusListenerManager.addListener(callback, executor);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -1840,26 +1953,16 @@ public class LocationManager {
* @param callback GNSS status callback object to remove
*/
public void unregisterGnssStatusCallback(@NonNull GnssStatus.Callback callback) {
- synchronized (mGnssStatusListeners) {
- try {
- GnssStatusListenerTransport transport = mGnssStatusListeners.remove(callback);
- if (transport != null) {
- mService.unregisterGnssStatusCallback(transport);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ try {
+ mGnssStatusListenerManager.removeListener(callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
/**
- * Adds an NMEA listener.
- *
- * @param listener a {@link GpsStatus.NmeaListener} object to register
- *
- * @return true if the listener was successfully added
+ * No-op method to keep backward-compatibility.
*
- * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
* @deprecated use {@link #addNmeaListener(OnNmeaMessageListener)} instead.
* @removed
*/
@@ -1871,9 +1974,8 @@ public class LocationManager {
}
/**
- * Removes an NMEA listener.
+ * No-op method to keep backward-compatibility.
*
- * @param listener a {@link GpsStatus.NmeaListener} object to remove
* @deprecated use {@link #removeNmeaListener(OnNmeaMessageListener)} instead.
* @removed
*/
@@ -1884,11 +1986,12 @@ public class LocationManager {
* Adds an NMEA listener.
*
* @param listener a {@link OnNmeaMessageListener} object to register
- *
* @return true if the listener was successfully added
- *
* @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+ * @deprecated Use {@link #addNmeaListener(OnNmeaMessageListener, Handler)} or {@link
+ * #addNmeaListener(Executor, OnNmeaMessageListener)} instead.
*/
+ @Deprecated
@RequiresPermission(ACCESS_FINE_LOCATION)
public boolean addNmeaListener(@NonNull OnNmeaMessageListener listener) {
return addNmeaListener(listener, null);
@@ -1898,33 +2001,45 @@ public class LocationManager {
* Adds an NMEA listener.
*
* @param listener a {@link OnNmeaMessageListener} object to register
- * @param handler the handler that the listener runs on.
- *
+ * @param handler a handler with the looper that the listener runs on.
* @return true if the listener was successfully added
*
+ * @throws IllegalArgumentException if listener is null
* @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
*/
@RequiresPermission(ACCESS_FINE_LOCATION)
public boolean addNmeaListener(
@NonNull OnNmeaMessageListener listener, @Nullable Handler handler) {
- boolean result;
-
- if (mGnssNmeaListeners.get(listener) != null) {
- // listener is already registered
- return true;
+ if (handler == null) {
+ handler = new Handler();
}
try {
- GnssStatusListenerTransport transport =
- new GnssStatusListenerTransport(listener, handler);
- result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
- if (result) {
- mGnssNmeaListeners.put(listener, transport);
- }
+ return mGnssStatusListenerManager.addListener(listener, handler);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+ }
- return result;
+ /**
+ * Adds an NMEA listener.
+ *
+ * @param listener a {@link OnNmeaMessageListener} object to register
+ * @param executor the {@link Executor} that the listener runs on.
+ * @return true if the listener was successfully added
+ *
+ * @throws IllegalArgumentException if executor is null
+ * @throws IllegalArgumentException if listener is null
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+ */
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean addNmeaListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnNmeaMessageListener listener) {
+ try {
+ return mGnssStatusListenerManager.addListener(listener, executor);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -1934,10 +2049,7 @@ public class LocationManager {
*/
public void removeNmeaListener(@NonNull OnNmeaMessageListener listener) {
try {
- GnssStatusListenerTransport transport = mGnssNmeaListeners.remove(listener);
- if (transport != null) {
- mService.unregisterGnssStatusCallback(transport);
- }
+ mGnssStatusListenerManager.removeListener(listener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1945,133 +2057,148 @@ public class LocationManager {
/**
* No-op method to keep backward-compatibility.
- * Don't use it. Use {@link #registerGnssMeasurementsCallback} instead.
+ *
* @hide
- * @deprecated Not supported anymore.
+ * @deprecated Use {@link #registerGnssMeasurementsCallback} instead.
* @removed
*/
@Deprecated
@SystemApi
- @SuppressLint("Doclava125")
public boolean addGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) {
return false;
}
/**
- * Registers a GPS Measurement callback.
+ * No-op method to keep backward-compatibility.
+ *
+ * @hide
+ * @deprecated Use {@link #unregisterGnssMeasurementsCallback} instead.
+ * @removed
+ */
+ @Deprecated
+ @SystemApi
+ public void removeGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) {}
+
+ /**
+ * Registers a GPS Measurement callback which will run on a binder thread.
*
* @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
* @return {@code true} if the callback was added successfully, {@code false} otherwise.
+ * @deprecated Use {@link
+ * #registerGnssMeasurementsCallback(GnssMeasurementsEvent.Callback, Handler)} or {@link
+ * #registerGnssMeasurementsCallback(Executor, GnssMeasurementsEvent.Callback)} instead.
*/
+ @Deprecated
@RequiresPermission(ACCESS_FINE_LOCATION)
public boolean registerGnssMeasurementsCallback(
@NonNull GnssMeasurementsEvent.Callback callback) {
- return registerGnssMeasurementsCallback(callback, null);
+ return registerGnssMeasurementsCallback(Runnable::run, callback);
}
/**
* Registers a GPS Measurement callback.
*
* @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
- * @param handler the handler that the callback runs on.
+ * @param handler the handler that the callback runs on.
* @return {@code true} if the callback was added successfully, {@code false} otherwise.
+ *
+ * @throws IllegalArgumentException if callback is null
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
*/
@RequiresPermission(ACCESS_FINE_LOCATION)
public boolean registerGnssMeasurementsCallback(
@NonNull GnssMeasurementsEvent.Callback callback, @Nullable Handler handler) {
- return mGnssMeasurementCallbackTransport.add(callback, handler);
+ if (handler == null) {
+ handler = new Handler();
+ }
+ try {
+ return mGnssMeasurementsListenerManager.addListener(callback, handler);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
- * Injects GNSS measurement corrections into the GNSS chipset.
+ * Registers a GPS Measurement callback.
*
- * @param measurementCorrections a {@link GnssMeasurementCorrections} object with the GNSS
- * measurement corrections to be injected into the GNSS chipset.
- * @hide
+ * @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
+ * @param executor the executor that the callback runs on.
+ * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+ *
+ * @throws IllegalArgumentException if executor is null
+ * @throws IllegalArgumentException if callback is null
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
*/
- @SystemApi
@RequiresPermission(ACCESS_FINE_LOCATION)
- public void injectGnssMeasurementCorrections(
- @NonNull GnssMeasurementCorrections measurementCorrections) {
+ public boolean registerGnssMeasurementsCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull GnssMeasurementsEvent.Callback callback) {
try {
- mGnssMeasurementCallbackTransport.injectGnssMeasurementCorrections(
- measurementCorrections);
+ return mGnssMeasurementsListenerManager.addListener(callback, executor);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Returns the supported capabilities of the GNSS chipset.
+ * Injects GNSS measurement corrections into the GNSS chipset.
*
- * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present.
+ * @param measurementCorrections a {@link GnssMeasurementCorrections} object with the GNSS
+ * measurement corrections to be injected into the GNSS chipset.
*
+ * @throws IllegalArgumentException if measurementCorrections is null
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
* @hide
*/
@SystemApi
@RequiresPermission(ACCESS_FINE_LOCATION)
- public @NonNull GnssCapabilities getGnssCapabilities() {
+ public void injectGnssMeasurementCorrections(
+ @NonNull GnssMeasurementCorrections measurementCorrections) {
+ Preconditions.checkArgument(measurementCorrections != null);
try {
- long gnssCapabilities = mGnssMeasurementCallbackTransport.getGnssCapabilities();
- if (gnssCapabilities == GnssCapabilities.INVALID_CAPABILITIES) {
- gnssCapabilities = 0L;
- }
- return GnssCapabilities.of(gnssCapabilities);
+ mService.injectGnssMeasurementCorrections(
+ measurementCorrections, mContext.getPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * No-op method to keep backward-compatibility. Don't use it. Use {@link
- * #unregisterGnssMeasurementsCallback} instead.
- *
- * @hide
- * @deprecated use {@link #unregisterGnssMeasurementsCallback(GnssMeasurementsEvent.Callback)}
- * instead.
- * @removed
- */
- @Deprecated
- @SystemApi
- @SuppressLint("Doclava125")
- public void removeGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) {}
-
- /**
* Unregisters a GPS Measurement callback.
*
* @param callback a {@link GnssMeasurementsEvent.Callback} object to remove.
*/
public void unregisterGnssMeasurementsCallback(
@NonNull GnssMeasurementsEvent.Callback callback) {
- mGnssMeasurementCallbackTransport.remove(callback);
+ try {
+ mGnssMeasurementsListenerManager.removeListener(callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
* No-op method to keep backward-compatibility.
- * Don't use it. Use {@link #registerGnssNavigationMessageCallback} instead.
+ *
* @hide
- * @deprecated Not supported anymore.
+ * @deprecated Use {@link #registerGnssNavigationMessageCallback} instead.
* @removed
*/
@Deprecated
@SystemApi
- @SuppressLint("Doclava125")
public boolean addGpsNavigationMessageListener(GpsNavigationMessageEvent.Listener listener) {
return false;
}
/**
* No-op method to keep backward-compatibility.
- * Don't use it. Use {@link #unregisterGnssNavigationMessageCallback} instead.
+ *
* @hide
- * @deprecated use
- * {@link #unregisterGnssNavigationMessageCallback(GnssNavigationMessage.Callback)}
- * instead
+ * @deprecated Use {@link #unregisterGnssNavigationMessageCallback} instead.
* @removed
*/
@Deprecated
@SystemApi
- @SuppressLint("Doclava125")
public void removeGpsNavigationMessageListener(GpsNavigationMessageEvent.Listener listener) {}
/**
@@ -2079,7 +2206,11 @@ public class LocationManager {
*
* @param callback a {@link GnssNavigationMessage.Callback} object to register.
* @return {@code true} if the callback was added successfully, {@code false} otherwise.
+ * @deprecated Use {@link
+ * #registerGnssNavigationMessageCallback(GnssNavigationMessage.Callback, Handler)} or {@link
+ * #registerGnssNavigationMessageCallback(Executor, GnssNavigationMessage.Callback)} instead.
*/
+ @Deprecated
public boolean registerGnssNavigationMessageCallback(
@NonNull GnssNavigationMessage.Callback callback) {
return registerGnssNavigationMessageCallback(callback, null);
@@ -2089,78 +2220,57 @@ public class LocationManager {
* Registers a GNSS Navigation Message callback.
*
* @param callback a {@link GnssNavigationMessage.Callback} object to register.
- * @param handler the handler that the callback runs on.
+ * @param handler the handler that the callback runs on.
* @return {@code true} if the callback was added successfully, {@code false} otherwise.
+ *
+ * @throws IllegalArgumentException if callback is null
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
*/
@RequiresPermission(ACCESS_FINE_LOCATION)
public boolean registerGnssNavigationMessageCallback(
@NonNull GnssNavigationMessage.Callback callback, @Nullable Handler handler) {
- return mGnssNavigationMessageCallbackTransport.add(callback, handler);
- }
-
- /**
- * Unregisters a GNSS Navigation Message callback.
- *
- * @param callback a {@link GnssNavigationMessage.Callback} object to remove.
- */
- public void unregisterGnssNavigationMessageCallback(
- @NonNull GnssNavigationMessage.Callback callback) {
- mGnssNavigationMessageCallbackTransport.remove(callback);
- }
-
- /**
- * Retrieves information about the current status of the GPS engine.
- * This should only be called from the {@link GpsStatus.Listener#onGpsStatusChanged}
- * callback to ensure that the data is copied atomically.
- *
- * The caller may either pass in a {@link GpsStatus} object to set with the latest
- * status information, or pass null to create a new {@link GpsStatus} object.
- *
- * @param status object containing GPS status details, or null.
- * @return status object containing updated GPS status.
- */
- @Deprecated
- @RequiresPermission(ACCESS_FINE_LOCATION)
- public @Nullable GpsStatus getGpsStatus(@Nullable GpsStatus status) {
- if (status == null) {
- status = new GpsStatus();
+ if (handler == null) {
+ handler = new Handler();
}
- // When mGnssStatus is null, that means that this method is called outside
- // onGpsStatusChanged(). Return an empty status to maintain backwards compatibility.
- if (mGnssStatus != null) {
- status.setStatus(mGnssStatus, mTimeToFirstFix);
+
+ try {
+ return mGnssNavigationMessageListenerTransport.addListener(callback, handler);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- return status;
}
/**
- * Returns the model year of the GNSS hardware and software build.
+ * Registers a GNSS Navigation Message callback.
*
- * <p> More details, such as build date, may be available in {@link #getGnssHardwareModelName()}.
+ * @param callback a {@link GnssNavigationMessage.Callback} object to register.
+ * @param executor the looper that the callback runs on.
+ * @return {@code true} if the callback was added successfully, {@code false} otherwise.
*
- * <p> May return 0 if the model year is less than 2016.
+ * @throws IllegalArgumentException if executor is null
+ * @throws IllegalArgumentException if callback is null
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
*/
- public int getGnssYearOfHardware() {
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean registerGnssNavigationMessageCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull GnssNavigationMessage.Callback callback) {
try {
- return mService.getGnssYearOfHardware();
+ return mGnssNavigationMessageListenerTransport.addListener(callback, executor);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Returns the Model Name (including Vendor and Hardware/Software Version) of the GNSS hardware
- * driver.
- *
- * <p> No device-specific serial number or ID is returned from this API.
+ * Unregisters a GNSS Navigation Message callback.
*
- * <p> Will return null when the GNSS hardware abstraction layer does not support providing
- * this value.
+ * @param callback a {@link GnssNavigationMessage.Callback} object to remove.
*/
- @Nullable
- public String getGnssHardwareModelName() {
+ public void unregisterGnssNavigationMessageCallback(
+ @NonNull GnssNavigationMessage.Callback callback) {
try {
- return mService.getGnssHardwareModelName();
+ mGnssNavigationMessageListenerTransport.removeListener(callback);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2208,12 +2318,20 @@ public class LocationManager {
@RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
public boolean registerGnssBatchedLocationCallback(long periodNanos, boolean wakeOnFifoFull,
@NonNull BatchedLocationCallback callback, @Nullable Handler handler) {
- mBatchedLocationCallbackTransport.add(callback, handler);
+ if (handler == null) {
+ handler = new Handler();
+ }
- try {
- return mService.startGnssBatch(periodNanos, wakeOnFifoFull, mContext.getPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ synchronized (mBatchedLocationCallbackManager) {
+ try {
+ if (mBatchedLocationCallbackManager.addListener(callback, handler)) {
+ return mService.startGnssBatch(periodNanos, wakeOnFifoFull,
+ mContext.getPackageName());
+ }
+ return false;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}
@@ -2247,35 +2365,14 @@ public class LocationManager {
@RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
public boolean unregisterGnssBatchedLocationCallback(
@NonNull BatchedLocationCallback callback) {
-
- mBatchedLocationCallbackTransport.remove(callback);
-
- try {
- return mService.stopGnssBatch();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Sends additional commands to a location provider. Can be used to support provider specific
- * extensions to the Location Manager API.
- *
- * @param provider name of the location provider.
- * @param command name of the command to send to the provider.
- * @param extras optional arguments for the command (or null).
- * @return true always
- */
- public boolean sendExtraCommand(
- @NonNull String provider, @NonNull String command, @Nullable Bundle extras) {
- android.util.SeempLog.record(48);
- Preconditions.checkArgument(provider != null, "invalid null provider");
- Preconditions.checkArgument(command != null, "invalid null command");
-
- try {
- return mService.sendExtraCommand(provider, command, extras);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ synchronized (mBatchedLocationCallbackManager) {
+ try {
+ mBatchedLocationCallbackManager.removeListener(callback);
+ mService.stopGnssBatch();
+ return true;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}
@@ -2294,156 +2391,536 @@ public class LocationManager {
}
}
- private static void checkProvider(String provider) {
- if (provider == null) {
- throw new IllegalArgumentException("invalid provider: " + provider);
+ private void checkPendingIntent(PendingIntent pendingIntent) {
+ Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent");
+ if (!pendingIntent.isTargetedToPackage()) {
+ IllegalArgumentException e = new IllegalArgumentException(
+ "invalid pending intent - must be targeted to package");
+ if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN) {
+ throw e;
+ } else {
+ Log.w(TAG, e);
+ }
}
}
- private static void checkCriteria(Criteria criteria) {
- if (criteria == null) {
- throw new IllegalArgumentException("invalid criteria: " + criteria);
+ private static class GetCurrentLocationTransport extends ILocationListener.Stub implements
+ AlarmManager.OnAlarmListener {
+
+ @GuardedBy("this")
+ @Nullable
+ private Executor mExecutor;
+
+ @GuardedBy("this")
+ @Nullable
+ private Consumer<Location> mConsumer;
+
+ @GuardedBy("this")
+ @Nullable
+ private AlarmManager mAlarmManager;
+
+ @GuardedBy("this")
+ @Nullable
+ private ICancellationSignal mRemoteCancellationSignal;
+
+ private GetCurrentLocationTransport(Executor executor, Consumer<Location> consumer) {
+ Preconditions.checkArgument(executor != null, "illegal null executor");
+ Preconditions.checkArgument(consumer != null, "illegal null consumer");
+ mExecutor = executor;
+ mConsumer = consumer;
+ mAlarmManager = null;
+ mRemoteCancellationSignal = null;
}
- }
- private static void checkListener(LocationListener listener) {
- if (listener == null) {
- throw new IllegalArgumentException("invalid listener: " + listener);
+ public synchronized void register(AlarmManager alarmManager,
+ ICancellationSignal remoteCancellationSignal) {
+ if (mConsumer == null) {
+ return;
+ }
+
+ mAlarmManager = alarmManager;
+ mAlarmManager.set(
+ ELAPSED_REALTIME,
+ SystemClock.elapsedRealtime() + GET_CURRENT_LOCATION_TIMEOUT_MS,
+ "GetCurrentLocation",
+ this,
+ null);
+
+ mRemoteCancellationSignal = remoteCancellationSignal;
+ }
+
+ public synchronized void cancel() {
+ mExecutor = null;
+ mConsumer = null;
+
+ if (mAlarmManager != null) {
+ mAlarmManager.cancel(this);
+ mAlarmManager = null;
+ }
+
+ if (mRemoteCancellationSignal != null) {
+ try {
+ mRemoteCancellationSignal.cancel();
+ } catch (RemoteException e) {
+ // ignore
+ }
+ mRemoteCancellationSignal = null;
+ }
+ }
+
+ public void fail() {
+ deliverResult(null);
+ }
+
+ @Override
+ public void onAlarm() {
+ synchronized (this) {
+ // save ourselves a pointless x-process call to cancel the alarm
+ mAlarmManager = null;
+ }
+
+ deliverResult(null);
+ }
+
+ @Override
+ public void onLocationChanged(Location location) {
+ synchronized (this) {
+ // save ourselves a pointless x-process call to cancel the location request
+ mRemoteCancellationSignal = null;
+ }
+
+ deliverResult(location);
+ }
+
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) {}
+
+ @Override
+ public void onProviderEnabled(String provider) {}
+
+ @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);
+ }
+
+ private synchronized void deliverResult(@Nullable Location location) {
+ if (mExecutor == null) {
+ return;
+ }
+
+ mExecutor.execute(() -> {
+ Consumer<Location> consumer;
+ synchronized (GetCurrentLocationTransport.this) {
+ if (mConsumer == null) {
+ return;
+ }
+ consumer = mConsumer;
+ cancel();
+ }
+
+ consumer.accept(location);
+ });
}
}
- private void checkPendingIntent(PendingIntent intent) {
- if (intent == null) {
- throw new IllegalArgumentException("invalid pending intent: " + intent);
+ private class LocationListenerTransport extends ILocationListener.Stub {
+
+ private final LocationListener mListener;
+ @Nullable private volatile Executor mExecutor = null;
+
+ private LocationListenerTransport(@NonNull LocationListener listener) {
+ Preconditions.checkArgument(listener != null, "invalid null listener");
+ mListener = listener;
}
- if (!intent.isTargetedToPackage()) {
- IllegalArgumentException e = new IllegalArgumentException(
- "pending intent must be targeted to package");
- if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN) {
+
+ public LocationListener getKey() {
+ return mListener;
+ }
+
+ public void register(@NonNull Executor executor) {
+ Preconditions.checkArgument(executor != null, "invalid null executor");
+ mExecutor = executor;
+ }
+
+ public void unregister() {
+ mExecutor = null;
+ }
+
+ @Override
+ public void onLocationChanged(Location location) {
+ Executor currentExecutor = mExecutor;
+ if (currentExecutor == null) {
+ return;
+ }
+
+ try {
+ currentExecutor.execute(() -> {
+ try {
+ if (currentExecutor != mExecutor) {
+ return;
+ }
+
+ // we may be under the binder identity if a direct executor is used
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mListener.onLocationChanged(location);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } finally {
+ locationCallbackFinished();
+ }
+ });
+ } catch (RejectedExecutionException e) {
+ locationCallbackFinished();
throw e;
- } else {
- Log.w(TAG, e);
}
}
- }
- private static void checkGeofence(Geofence fence) {
- if (fence == null) {
- throw new IllegalArgumentException("invalid geofence: " + fence);
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ Executor currentExecutor = mExecutor;
+ if (currentExecutor == null) {
+ return;
+ }
+
+ try {
+ currentExecutor.execute(() -> {
+ try {
+ if (currentExecutor != mExecutor) {
+ return;
+ }
+
+ // we may be under the binder identity if a direct executor is used
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mListener.onStatusChanged(provider, status, extras);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } finally {
+ locationCallbackFinished();
+ }
+ });
+ } catch (RejectedExecutionException e) {
+ locationCallbackFinished();
+ throw e;
+ }
}
- }
- /**
- * Returns true if the given package name matches a location provider package, and false
- * otherwise.
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
- public boolean isProviderPackage(@NonNull String packageName) {
- try {
- return mService.isProviderPackage(packageName);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- return false;
+ @Override
+ public void onProviderEnabled(String provider) {
+ Executor currentExecutor = mExecutor;
+ if (currentExecutor == null) {
+ return;
+ }
+
+ try {
+ currentExecutor.execute(() -> {
+ try {
+ if (currentExecutor != mExecutor) {
+ return;
+ }
+
+ // we may be under the binder identity if a direct executor is used
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mListener.onProviderEnabled(provider);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } finally {
+ locationCallbackFinished();
+ }
+ });
+ } catch (RejectedExecutionException e) {
+ locationCallbackFinished();
+ throw e;
+ }
}
- }
- /**
- * Set the extra location controller package for location services on the device.
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
- public void setExtraLocationControllerPackage(@Nullable String packageName) {
- try {
- mService.setExtraLocationControllerPackage(packageName);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ @Override
+ public void onProviderDisabled(String provider) {
+ Executor currentExecutor = mExecutor;
+ if (currentExecutor == null) {
+ return;
+ }
+
+ try {
+ currentExecutor.execute(() -> {
+ try {
+ if (currentExecutor != mExecutor) {
+ return;
+ }
+
+ // we may be under the binder identity if a direct executor is used
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mListener.onProviderDisabled(provider);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } finally {
+ locationCallbackFinished();
+ }
+ });
+ } catch (RejectedExecutionException e) {
+ locationCallbackFinished();
+ throw e;
+ }
}
- }
- /**
- * Set the extra location controller package for location services on the device.
- *
- * @removed
- * @deprecated Use {@link #setExtraLocationControllerPackage} instead.
- * @hide
- */
- @Deprecated
- @SystemApi
- @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
- public void setLocationControllerExtraPackage(String packageName) {
- try {
- mService.setExtraLocationControllerPackage(packageName);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ private void locationCallbackFinished() {
+ try {
+ mService.locationCallbackFinished(this);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}
- /**
- * Returns the extra location controller package on the device.
- *
- * @hide
- */
- @SystemApi
- public @Nullable String getExtraLocationControllerPackage() {
- try {
- return mService.getExtraLocationControllerPackage();
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- return null;
+ private static class NmeaAdapter extends GnssStatus.Callback implements OnNmeaMessageListener {
+
+ private final OnNmeaMessageListener mListener;
+
+ private NmeaAdapter(OnNmeaMessageListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void onNmeaMessage(String message, long timestamp) {
+ mListener.onNmeaMessage(message, timestamp);
}
}
- /**
- * Set whether the extra location controller package is currently enabled on the device.
- *
- * @removed
- * @deprecated Use {@link #setExtraLocationControllerPackageEnabled} instead.
- * @hide
- */
- @SystemApi
- @Deprecated
- @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
- public void setLocationControllerExtraPackageEnabled(boolean enabled) {
- try {
- mService.setExtraLocationControllerPackageEnabled(enabled);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ private class GnssStatusListenerManager extends
+ AbstractListenerManager<GnssStatus.Callback> {
+
+ @Nullable
+ private IGnssStatusListener mListenerTransport;
+
+ @Nullable
+ private volatile GnssStatus mGnssStatus;
+ private volatile int mTtff;
+
+ public GnssStatus getGnssStatus() {
+ return mGnssStatus;
+ }
+
+ public int getTtff() {
+ return mTtff;
+ }
+
+ public boolean addListener(@NonNull GpsStatus.Listener listener, @NonNull Handler handler)
+ throws RemoteException {
+ return addInternal(listener, handler);
+ }
+
+ public boolean addListener(@NonNull OnNmeaMessageListener listener,
+ @NonNull Handler handler)
+ throws RemoteException {
+ return addInternal(listener, handler);
+ }
+
+ public boolean addListener(@NonNull OnNmeaMessageListener listener,
+ @NonNull Executor executor)
+ throws RemoteException {
+ return addInternal(listener, executor);
+ }
+
+ @Override
+ protected GnssStatus.Callback convertKey(Object listener) {
+ if (listener instanceof GnssStatus.Callback) {
+ return (GnssStatus.Callback) listener;
+ } else if (listener instanceof GpsStatus.Listener) {
+ return new GnssStatus.Callback() {
+ private final GpsStatus.Listener mGpsListener = (GpsStatus.Listener) listener;
+
+ @Override
+ public void onStarted() {
+ mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_STARTED);
+ }
+
+ @Override
+ public void onStopped() {
+ mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_STOPPED);
+ }
+
+ @Override
+ public void onFirstFix(int ttffMillis) {
+ mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_FIRST_FIX);
+ }
+
+ @Override
+ public void onSatelliteStatusChanged(GnssStatus status) {
+ mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
+ }
+ };
+ } else if (listener instanceof OnNmeaMessageListener) {
+ return new NmeaAdapter((OnNmeaMessageListener) listener);
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ @Override
+ protected boolean registerService() throws RemoteException {
+ Preconditions.checkState(mListenerTransport == null);
+
+ mListenerTransport = new GnssStatusListener();
+ return mService.registerGnssStatusCallback(mListenerTransport,
+ mContext.getPackageName(), mContext.getFeatureId());
+ }
+
+ @Override
+ protected void unregisterService() throws RemoteException {
+ Preconditions.checkState(mListenerTransport != null);
+
+ mService.unregisterGnssStatusCallback(mListenerTransport);
+ mListenerTransport = null;
+ }
+
+ private class GnssStatusListener extends IGnssStatusListener.Stub {
+ @Override
+ public void onGnssStarted() {
+ execute(GnssStatus.Callback::onStarted);
+ }
+
+ @Override
+ public void onGnssStopped() {
+ execute(GnssStatus.Callback::onStopped);
+ }
+
+ @Override
+ public void onFirstFix(int ttff) {
+ mTtff = ttff;
+ execute((callback) -> callback.onFirstFix(ttff));
+ }
+
+ @Override
+ public void onSvStatusChanged(int svCount, int[] svidWithFlags, float[] cn0s,
+ float[] elevations, float[] azimuths, float[] carrierFreqs) {
+ GnssStatus localStatus = new GnssStatus(svCount, svidWithFlags, cn0s, elevations,
+ azimuths, carrierFreqs);
+ mGnssStatus = localStatus;
+ execute((callback) -> callback.onSatelliteStatusChanged(localStatus));
+ }
+
+ @Override
+ public void onNmeaReceived(long timestamp, String nmea) {
+ execute((callback) -> {
+ if (callback instanceof NmeaAdapter) {
+ ((NmeaAdapter) callback).onNmeaMessage(nmea, timestamp);
+ }
+ });
+ }
}
}
- /**
- * Set whether the extra location controller package is currently enabled on the device.
- *
- * @hide
- */
- @SystemApi
- @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
- public void setExtraLocationControllerPackageEnabled(boolean enabled) {
- try {
- mService.setExtraLocationControllerPackageEnabled(enabled);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ private class GnssMeasurementsListenerManager extends
+ AbstractListenerManager<GnssMeasurementsEvent.Callback> {
+
+ @Nullable
+ private IGnssMeasurementsListener mListenerTransport;
+
+ @Override
+ protected boolean registerService() throws RemoteException {
+ Preconditions.checkState(mListenerTransport == null);
+
+ mListenerTransport = new GnssMeasurementsListener();
+ return mService.addGnssMeasurementsListener(mListenerTransport,
+ mContext.getPackageName(), mContext.getFeatureId(),
+ "gnss measurement callback");
+ }
+
+ @Override
+ protected void unregisterService() throws RemoteException {
+ Preconditions.checkState(mListenerTransport != null);
+
+ mService.removeGnssMeasurementsListener(mListenerTransport);
+ mListenerTransport = null;
+ }
+
+ private class GnssMeasurementsListener extends IGnssMeasurementsListener.Stub {
+ @Override
+ public void onGnssMeasurementsReceived(final GnssMeasurementsEvent event) {
+ execute((callback) -> callback.onGnssMeasurementsReceived(event));
+ }
+
+ @Override
+ public void onStatusChanged(int status) {
+ execute((callback) -> callback.onStatusChanged(status));
+ }
}
}
- /**
- * Returns whether extra location controller package is currently enabled on the device.
- *
- * @hide
- */
- @SystemApi
- public boolean isExtraLocationControllerPackageEnabled() {
- try {
- return mService.isExtraLocationControllerPackageEnabled();
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- return false;
+ private class GnssNavigationMessageListenerManager extends
+ AbstractListenerManager<GnssNavigationMessage.Callback> {
+
+ @Nullable
+ private IGnssNavigationMessageListener mListenerTransport;
+
+ @Override
+ protected boolean registerService() throws RemoteException {
+ Preconditions.checkState(mListenerTransport == null);
+
+ mListenerTransport = new GnssNavigationMessageListener();
+ return mService.addGnssNavigationMessageListener(mListenerTransport,
+ mContext.getPackageName(), mContext.getFeatureId(),
+ "gnss navigation callback");
+ }
+
+ @Override
+ protected void unregisterService() throws RemoteException {
+ Preconditions.checkState(mListenerTransport != null);
+
+ mService.removeGnssNavigationMessageListener(mListenerTransport);
+ mListenerTransport = null;
+ }
+
+ private class GnssNavigationMessageListener extends IGnssNavigationMessageListener.Stub {
+ @Override
+ public void onGnssNavigationMessageReceived(GnssNavigationMessage event) {
+ execute((listener) -> listener.onGnssNavigationMessageReceived(event));
+ }
+
+ @Override
+ public void onStatusChanged(int status) {
+ execute((listener) -> listener.onStatusChanged(status));
+ }
}
}
+ private class BatchedLocationCallbackManager extends
+ AbstractListenerManager<BatchedLocationCallback> {
+
+ @Nullable
+ private IBatchedLocationCallback mListenerTransport;
+
+ @Override
+ protected boolean registerService() throws RemoteException {
+ Preconditions.checkState(mListenerTransport == null);
+
+ mListenerTransport = new BatchedLocationCallback();
+ return mService.addGnssBatchingCallback(mListenerTransport, mContext.getPackageName(),
+ mContext.getFeatureId(), "batched location callback");
+ }
+
+ @Override
+ protected void unregisterService() throws RemoteException {
+ Preconditions.checkState(mListenerTransport != null);
+
+ mService.removeGnssBatchingCallback();
+ mListenerTransport = null;
+ }
+
+ private class BatchedLocationCallback extends IBatchedLocationCallback.Stub {
+ @Override
+ public void onLocationBatch(List<Location> locations) {
+ execute((listener) -> listener.onLocationBatch(locations));
+ }
+ }
+ }
}
diff --git a/location/java/android/location/LocationProvider.java b/location/java/android/location/LocationProvider.java
index b69a9d79e5b8..52a03b61d21c 100644
--- a/location/java/android/location/LocationProvider.java
+++ b/location/java/android/location/LocationProvider.java
@@ -53,28 +53,10 @@ public class LocationProvider {
@Deprecated
public static final int AVAILABLE = 2;
- /**
- * A regular expression matching characters that may not appear
- * in the name of a LocationProvider
- * @hide
- */
- public static final String BAD_CHARS_REGEX = "[^a-zA-Z0-9]";
-
private final String mName;
private final ProviderProperties mProperties;
- /**
- * Constructs a LocationProvider with the given name. Provider names must
- * consist only of the characters [a-zA-Z0-9].
- *
- * @throws IllegalArgumentException if name contains an illegal character
- *
- * @hide
- */
- public LocationProvider(String name, ProviderProperties properties) {
- if (name.matches(BAD_CHARS_REGEX)) {
- throw new IllegalArgumentException("provider name contains illegal character: " + name);
- }
+ LocationProvider(String name, ProviderProperties properties) {
mName = name;
mProperties = properties;
}
diff --git a/location/lib/Android.bp b/location/lib/Android.bp
index b36aa036daba..fe0f669508eb 100644
--- a/location/lib/Android.bp
+++ b/location/lib/Android.bp
@@ -16,12 +16,11 @@
java_sdk_library {
name: "com.android.location.provider",
- srcs: [
- "java/**/*.java",
- ":framework-all-sources",
- ],
+ srcs: ["java/**/*.java"],
+ api_srcs: [":framework-all-sources"],
libs: [
"androidx.annotation_annotation",
+ "framework-all",
],
api_packages: ["com.android.location.provider"],
}
diff --git a/media/Android.bp b/media/Android.bp
index a59b3e76faed..2f75e4458ef5 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -63,26 +63,6 @@ filegroup {
path: "apex/java",
}
-filegroup {
- name: "mediaplayer2-srcs",
- srcs: [
- "apex/java/android/media/CloseGuard.java",
- "apex/java/android/media/DataSourceCallback.java",
- "apex/java/android/media/DataSourceDesc.java",
- "apex/java/android/media/UriDataSourceDesc.java",
- "apex/java/android/media/FileDataSourceDesc.java",
- "apex/java/android/media/Media2Utils.java",
- "apex/java/android/media/MediaPlayer2Utils.java",
- "apex/java/android/media/MediaPlayer2.java",
- "apex/java/android/media/Media2HTTPService.java",
- "apex/java/android/media/Media2HTTPConnection.java",
- "apex/java/android/media/RoutingDelegate.java",
- "apex/java/android/media/BufferingParams.java",
- "apex/java/android/media/ProxyDataSourceCallback.java",
- ],
- path: "apex/java",
-}
-
metalava_updatable_media_args = " --error UnhiddenSystemApi " +
"--hide RequiresPermission " +
"--hide MissingPermission --hide BroadcastBehavior " +
diff --git a/media/apex/java/android/media/DataSourceDesc.java b/media/apex/java/android/media/DataSourceDesc.java
deleted file mode 100644
index 9a9c74aba2c7..000000000000
--- a/media/apex/java/android/media/DataSourceDesc.java
+++ /dev/null
@@ -1,384 +0,0 @@
-/*
- * Copyright 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 android.media;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-
-import java.net.CookieHandler;
-import java.net.CookieManager;
-import java.net.HttpCookie;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Data source descriptor.
- *
- * Used by {@link MediaPlayer2#setDataSource}, {@link MediaPlayer2#setNextDataSource} and
- * {@link MediaPlayer2#setNextDataSources} to set data source for playback.
- *
- * @hide
- */
-public class DataSourceDesc {
- // intentionally less than long.MAX_VALUE
- static final long LONG_MAX = 0x7ffffffffffffffL;
-
- // keep consistent with native code
- public static final long LONG_MAX_TIME_MS = LONG_MAX / 1000;
- /**
- * @hide
- */
- public static final long LONG_MAX_TIME_US = LONG_MAX_TIME_MS * 1000;
-
- public static final long POSITION_UNKNOWN = LONG_MAX_TIME_MS;
-
- private String mMediaId;
- private long mStartPositionMs = 0;
- private long mEndPositionMs = POSITION_UNKNOWN;
-
- DataSourceDesc(String mediaId, long startPositionMs, long endPositionMs) {
- mMediaId = mediaId;
- mStartPositionMs = startPositionMs;
- mEndPositionMs = endPositionMs;
- }
-
- /**
- * Releases the resources held by this {@code DataSourceDesc} object.
- */
- void close() {
- }
-
- // Have to declare protected for finalize() since it is protected
- // in the base class Object.
- @Override
- protected void finalize() throws Throwable {
- close();
- }
-
- /**
- * Return the media Id of data source.
- * @return the media Id of data source
- */
- public @Nullable String getMediaId() {
- return mMediaId;
- }
-
- /**
- * Return the position in milliseconds at which the playback will start.
- * @return the position in milliseconds at which the playback will start
- */
- public long getStartPosition() {
- return mStartPositionMs;
- }
-
- /**
- * Return the position in milliseconds at which the playback will end.
- * {@link #POSITION_UNKNOWN} means ending at the end of source content.
- * @return the position in milliseconds at which the playback will end
- */
- public long getEndPosition() {
- return mEndPositionMs;
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder("DataSourceDesc{");
- sb.append("mMediaId=").append(mMediaId);
- sb.append(", mStartPositionMs=").append(mStartPositionMs);
- sb.append(", mEndPositionMs=").append(mEndPositionMs);
- sb.append('}');
- return sb.toString();
- }
-
- /**
- * Builder for {@link DataSourceDesc}.
- * <p>
- * Here is an example where <code>Builder</code> is used to define the
- * {@link DataSourceDesc} to be used by a {@link MediaPlayer2} instance:
- *
- * <pre class="prettyprint">
- * DataSourceDesc newDSD = new DataSourceDesc.Builder()
- * .setDataSource(context, uri, headers, cookies)
- * .setStartPosition(1000)
- * .setEndPosition(15000)
- * .build();
- * mediaplayer2.setDataSourceDesc(newDSD);
- * </pre>
- */
- public static final class Builder {
- private static final int SOURCE_TYPE_UNKNOWN = 0;
- private static final int SOURCE_TYPE_URI = 1;
- private static final int SOURCE_TYPE_FILE = 2;
-
- private int mSourceType = SOURCE_TYPE_UNKNOWN;
- private String mMediaId;
- private long mStartPositionMs = 0;
- private long mEndPositionMs = POSITION_UNKNOWN;
-
- // For UriDataSourceDesc
- private Uri mUri;
- private Map<String, String> mHeader;
- private List<HttpCookie> mCookies;
-
- // For FileDataSourceDesc
- private ParcelFileDescriptor mPFD;
- private long mOffset = 0;
- private long mLength = FileDataSourceDesc.FD_LENGTH_UNKNOWN;
-
- /**
- * Constructs a new BuilderBase with the defaults.
- */
- public Builder() {
- }
-
- /**
- * Constructs a new BuilderBase from a given {@link DataSourceDesc} instance
- * @param dsd the {@link DataSourceDesc} object whose data will be reused
- * in the new BuilderBase.
- */
- public Builder(@Nullable DataSourceDesc dsd) {
- if (dsd == null) {
- return;
- }
- mMediaId = dsd.mMediaId;
- mStartPositionMs = dsd.mStartPositionMs;
- mEndPositionMs = dsd.mEndPositionMs;
- if (dsd instanceof FileDataSourceDesc) {
- mSourceType = SOURCE_TYPE_FILE;
- mPFD = ((FileDataSourceDesc) dsd).getParcelFileDescriptor();
- mOffset = ((FileDataSourceDesc) dsd).getOffset();
- mLength = ((FileDataSourceDesc) dsd).getLength();
- } else if (dsd instanceof UriDataSourceDesc) {
- mSourceType = SOURCE_TYPE_URI;
- mUri = ((UriDataSourceDesc) dsd).getUri();
- mHeader = ((UriDataSourceDesc) dsd).getHeaders();
- mCookies = ((UriDataSourceDesc) dsd).getCookies();
- } else {
- throw new IllegalStateException("Unknown source type:" + mSourceType);
- }
- }
-
- /**
- * Sets all fields that have been set in the {@link DataSourceDesc} object.
- * <code>IllegalStateException</code> will be thrown if there is conflict between fields.
- *
- * @return {@link DataSourceDesc}
- */
- @NonNull
- public DataSourceDesc build() {
- if (mSourceType == SOURCE_TYPE_UNKNOWN) {
- throw new IllegalStateException("Source is not set.");
- }
- if (mStartPositionMs > mEndPositionMs) {
- throw new IllegalStateException("Illegal start/end position: "
- + mStartPositionMs + " : " + mEndPositionMs);
- }
-
- DataSourceDesc desc;
- if (mSourceType == SOURCE_TYPE_FILE) {
- desc = new FileDataSourceDesc(
- mMediaId, mStartPositionMs, mEndPositionMs, mPFD, mOffset, mLength);
- } else if (mSourceType == SOURCE_TYPE_URI) {
- desc = new UriDataSourceDesc(
- mMediaId, mStartPositionMs, mEndPositionMs, mUri, mHeader, mCookies);
- } else {
- throw new IllegalStateException("Unknown source type:" + mSourceType);
- }
- return desc;
- }
-
- /**
- * Sets the media Id of this data source.
- *
- * @param mediaId the media Id of this data source
- * @return the same Builder instance.
- */
- @NonNull
- public Builder setMediaId(@Nullable String mediaId) {
- mMediaId = mediaId;
- return this;
- }
-
- /**
- * Sets the start position in milliseconds at which the playback will start.
- * Any negative number is treated as 0.
- *
- * @param position the start position in milliseconds at which the playback will start
- * @return the same Builder instance.
- *
- */
- @NonNull
- public Builder setStartPosition(long position) {
- if (position < 0) {
- position = 0;
- }
- mStartPositionMs = position;
- return this;
- }
-
- /**
- * Sets the end position in milliseconds at which the playback will end.
- * Any negative number is treated as maximum duration {@link #LONG_MAX_TIME_MS}
- * of the data source
- *
- * @param position the end position in milliseconds at which the playback will end
- * @return the same Builder instance.
- */
- @NonNull
- public Builder setEndPosition(long position) {
- if (position < 0) {
- position = LONG_MAX_TIME_MS;
- }
- mEndPositionMs = position;
- return this;
- }
-
- /**
- * Sets the data source as a content Uri.
- *
- * @param uri the Content URI of the data you want to play
- * @return the same Builder instance.
- * @throws NullPointerException if context or uri is null.
- */
- @NonNull
- public Builder setDataSource(@NonNull Uri uri) {
- setSourceType(SOURCE_TYPE_URI);
- Media2Utils.checkArgument(uri != null, "uri cannot be null");
- mUri = uri;
- return this;
- }
-
- /**
- * Sets the data source as a content Uri.
- *
- * To provide cookies for the subsequent HTTP requests, you can install your own default
- * cookie handler and use other variants of setDataSource APIs instead. Alternatively, you
- * can use this API to pass the cookies as a list of HttpCookie. If the app has not
- * installed a CookieHandler already, {@link MediaPlayer2} will create a CookieManager
- * and populates its CookieStore with the provided cookies when this data source is passed
- * to {@link MediaPlayer2}. If the app has installed its own handler already, the handler
- * is required to be of CookieManager type such that {@link MediaPlayer2} can update the
- * manager’s CookieStore.
- *
- * <p><strong>Note</strong> that the cross domain redirection is allowed by default,
- * but that can be changed with key/value pairs through the headers parameter with
- * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to
- * disallow or allow cross domain redirection.
- *
- * @param uri the Content URI of the data you want to play
- * @param headers the headers to be sent together with the request for the data
- * The headers must not include cookies. Instead, use the cookies param.
- * @param cookies the cookies to be sent together with the request
- * @return the same Builder instance.
- * @throws NullPointerException if context or uri is null.
- * @throws IllegalArgumentException if the cookie handler is not of CookieManager type
- * when cookies are provided.
- */
- @NonNull
- public Builder setDataSource(@NonNull Uri uri, @Nullable Map<String, String> headers,
- @Nullable List<HttpCookie> cookies) {
- setSourceType(SOURCE_TYPE_URI);
- Media2Utils.checkArgument(uri != null, "uri cannot be null");
- if (cookies != null) {
- CookieHandler cookieHandler = CookieHandler.getDefault();
- if (cookieHandler != null && !(cookieHandler instanceof CookieManager)) {
- throw new IllegalArgumentException(
- "The cookie handler has to be of CookieManager type "
- + "when cookies are provided.");
- }
- }
-
- mUri = uri;
- if (headers != null) {
- mHeader = new HashMap<String, String>(headers);
- }
- if (cookies != null) {
- mCookies = new ArrayList<HttpCookie>(cookies);
- }
- return this;
- }
-
- /**
- * Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be
- * seekable (N.B. a LocalSocket is not seekable). When the {@link DataSourceDesc}
- * created by this builder is passed to {@link MediaPlayer2} via
- * {@link MediaPlayer2#setDataSource},
- * {@link MediaPlayer2#setNextDataSource} or
- * {@link MediaPlayer2#setNextDataSources}, MediaPlayer2 will
- * close the ParcelFileDescriptor.
- *
- * @param pfd the ParcelFileDescriptor for the file to play
- * @return the same Builder instance.
- * @throws NullPointerException if pfd is null.
- */
- @NonNull
- public Builder setDataSource(@NonNull ParcelFileDescriptor pfd) {
- setSourceType(SOURCE_TYPE_FILE);
- Media2Utils.checkArgument(pfd != null, "pfd cannot be null.");
- mPFD = pfd;
- return this;
- }
-
- /**
- * Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be
- * seekable (N.B. a LocalSocket is not seekable). When the {@link DataSourceDesc}
- * created by this builder is passed to {@link MediaPlayer2} via
- * {@link MediaPlayer2#setDataSource},
- * {@link MediaPlayer2#setNextDataSource} or
- * {@link MediaPlayer2#setNextDataSources}, MediaPlayer2 will
- * close the ParcelFileDescriptor.
- *
- * Any negative number for offset is treated as 0.
- * Any negative number for length is treated as maximum length of the data source.
- *
- * @param pfd the ParcelFileDescriptor for the file to play
- * @param offset the offset into the file where the data to be played starts, in bytes
- * @param length the length in bytes of the data to be played
- * @return the same Builder instance.
- * @throws NullPointerException if pfd is null.
- */
- @NonNull
- public Builder setDataSource(
- @NonNull ParcelFileDescriptor pfd, long offset, long length) {
- setSourceType(SOURCE_TYPE_FILE);
- if (pfd == null) {
- throw new NullPointerException("pfd cannot be null.");
- }
- if (offset < 0) {
- offset = 0;
- }
- if (length < 0) {
- length = FileDataSourceDesc.FD_LENGTH_UNKNOWN;
- }
- mPFD = pfd;
- mOffset = offset;
- mLength = length;
- return this;
- }
-
- private void setSourceType(int type) {
- if (mSourceType != SOURCE_TYPE_UNKNOWN) {
- throw new IllegalStateException("Source is already set. type=" + mSourceType);
- }
- mSourceType = type;
- }
- }
-}
diff --git a/media/apex/java/android/media/FileDataSourceDesc.java b/media/apex/java/android/media/FileDataSourceDesc.java
deleted file mode 100644
index 2aa2cb7eb1bb..000000000000
--- a/media/apex/java/android/media/FileDataSourceDesc.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright 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 android.media;
-
-import android.annotation.NonNull;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-
-import java.io.IOException;
-
-/**
- * Structure of data source descriptor for sources using file descriptor.
- *
- * Used by {@link MediaPlayer2#setDataSource}, {@link MediaPlayer2#setNextDataSource} and
- * {@link MediaPlayer2#setNextDataSources} to set data source for playback.
- *
- * <p>Users should use {@link Builder} to create {@link FileDataSourceDesc}.
- * @hide
- */
-public class FileDataSourceDesc extends DataSourceDesc {
- private static final String TAG = "FileDataSourceDesc";
-
- /**
- * Used when the length of file descriptor is unknown.
- *
- * @see #getLength()
- */
- public static final long FD_LENGTH_UNKNOWN = LONG_MAX;
-
- private ParcelFileDescriptor mPFD;
- private long mOffset = 0;
- private long mLength = FD_LENGTH_UNKNOWN;
- private int mCount = 0;
- private boolean mClosed = false;
-
- FileDataSourceDesc(String mediaId, long startPositionMs, long endPositionMs,
- ParcelFileDescriptor pfd, long offset, long length) {
- super(mediaId, startPositionMs, endPositionMs);
- mPFD = pfd;
- mOffset = offset;
- mLength = length;
- }
-
- /**
- * Releases the resources held by this {@code FileDataSourceDesc} object.
- */
- @Override
- void close() {
- super.close();
- decCount();
- }
-
- /**
- * Decrements usage count by {@link MediaPlayer2}.
- * If this is the last usage, also releases the file descriptor held by this
- * {@code FileDataSourceDesc} object.
- */
- void decCount() {
- synchronized (this) {
- --mCount;
- if (mCount > 0) {
- return;
- }
-
- try {
- mPFD.close();
- mClosed = true;
- } catch (IOException e) {
- Log.e(TAG, "failed to close pfd: " + e);
- }
- }
- }
-
- /**
- * Increments usage count by {@link MediaPlayer2} if PFD has not been closed.
- */
- void incCount() {
- synchronized (this) {
- if (!mClosed) {
- ++mCount;
- }
- }
- }
-
- /**
- * Return the status of underline ParcelFileDescriptor
- * @return true if underline ParcelFileDescriptor is closed, false otherwise.
- */
- boolean isPFDClosed() {
- synchronized (this) {
- return mClosed;
- }
- }
-
- /**
- * Return the ParcelFileDescriptor of this data source.
- * @return the ParcelFileDescriptor of this data source
- */
- public @NonNull ParcelFileDescriptor getParcelFileDescriptor() {
- return mPFD;
- }
-
- /**
- * Return the offset associated with the ParcelFileDescriptor of this data source.
- * It's meaningful only when it has been set by the {@link Builder}.
- * @return the offset associated with the ParcelFileDescriptor of this data source
- */
- public long getOffset() {
- return mOffset;
- }
-
- /**
- * Return the content length associated with the ParcelFileDescriptor of this data source.
- * {@link #FD_LENGTH_UNKNOWN} means same as the length of source content.
- * @return the content length associated with the ParcelFileDescriptor of this data source
- */
- public long getLength() {
- return mLength;
- }
-}
diff --git a/media/apex/java/android/media/Media2HTTPConnection.java b/media/apex/java/android/media/Media2HTTPConnection.java
deleted file mode 100644
index a369a62b39db..000000000000
--- a/media/apex/java/android/media/Media2HTTPConnection.java
+++ /dev/null
@@ -1,385 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import static android.media.MediaPlayer2.MEDIA_ERROR_UNSUPPORTED;
-
-import android.os.StrictMode;
-import android.util.Log;
-
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.CookieHandler;
-import java.net.HttpURLConnection;
-import java.net.InetAddress;
-import java.net.MalformedURLException;
-import java.net.NoRouteToHostException;
-import java.net.ProtocolException;
-import java.net.Proxy;
-import java.net.URL;
-import java.net.UnknownHostException;
-import java.net.UnknownServiceException;
-import java.util.HashMap;
-import java.util.Map;
-
-/** @hide */
-public class Media2HTTPConnection {
- private static final String TAG = "Media2HTTPConnection";
- private static final boolean VERBOSE = false;
-
- // connection timeout - 30 sec
- private static final int CONNECT_TIMEOUT_MS = 30 * 1000;
-
- private long mCurrentOffset = -1;
- private URL mURL = null;
- private Map<String, String> mHeaders = null;
- private HttpURLConnection mConnection = null;
- private long mTotalSize = -1;
- private InputStream mInputStream = null;
-
- private boolean mAllowCrossDomainRedirect = true;
- private boolean mAllowCrossProtocolRedirect = true;
-
- // from com.squareup.okhttp.internal.http
- private final static int HTTP_TEMP_REDIRECT = 307;
- private final static int MAX_REDIRECTS = 20;
-
- public Media2HTTPConnection() {
- CookieHandler cookieHandler = CookieHandler.getDefault();
- if (cookieHandler == null) {
- Log.w(TAG, "Media2HTTPConnection: Unexpected. No CookieHandler found.");
- }
- }
-
- public boolean connect(String uri, String headers) {
- if (VERBOSE) {
- Log.d(TAG, "connect: uri=" + uri + ", headers=" + headers);
- }
-
- try {
- disconnect();
- mAllowCrossDomainRedirect = true;
- mURL = new URL(uri);
- mHeaders = convertHeaderStringToMap(headers);
- } catch (MalformedURLException e) {
- return false;
- }
-
- return true;
- }
-
- private boolean parseBoolean(String val) {
- try {
- return Long.parseLong(val) != 0;
- } catch (NumberFormatException e) {
- return "true".equalsIgnoreCase(val) ||
- "yes".equalsIgnoreCase(val);
- }
- }
-
- /* returns true iff header is internal */
- private boolean filterOutInternalHeaders(String key, String val) {
- if ("android-allow-cross-domain-redirect".equalsIgnoreCase(key)) {
- mAllowCrossDomainRedirect = parseBoolean(val);
- // cross-protocol redirects are also controlled by this flag
- mAllowCrossProtocolRedirect = mAllowCrossDomainRedirect;
- } else {
- return false;
- }
- return true;
- }
-
- private Map<String, String> convertHeaderStringToMap(String headers) {
- HashMap<String, String> map = new HashMap<String, String>();
-
- String[] pairs = headers.split("\r\n");
- for (String pair : pairs) {
- int colonPos = pair.indexOf(":");
- if (colonPos >= 0) {
- String key = pair.substring(0, colonPos);
- String val = pair.substring(colonPos + 1);
-
- if (!filterOutInternalHeaders(key, val)) {
- map.put(key, val);
- }
- }
- }
-
- return map;
- }
-
- public void disconnect() {
- teardownConnection();
- mHeaders = null;
- mURL = null;
- }
-
- private void teardownConnection() {
- if (mConnection != null) {
- if (mInputStream != null) {
- try {
- mInputStream.close();
- } catch (IOException e) {
- }
- mInputStream = null;
- }
-
- mConnection.disconnect();
- mConnection = null;
-
- mCurrentOffset = -1;
- }
- }
-
- private static final boolean isLocalHost(URL url) {
- if (url == null) {
- return false;
- }
-
- String host = url.getHost();
-
- if (host == null) {
- return false;
- }
-
- try {
- if (host.equalsIgnoreCase("localhost")) {
- return true;
- }
- if (InetAddress.getByName(host).isLoopbackAddress()) {
- return true;
- }
- } catch (IllegalArgumentException | UnknownHostException e) {
- }
- return false;
- }
-
- private void seekTo(long offset) throws IOException {
- teardownConnection();
-
- try {
- int response;
- int redirectCount = 0;
-
- URL url = mURL;
-
- // do not use any proxy for localhost (127.0.0.1)
- boolean noProxy = isLocalHost(url);
-
- while (true) {
- if (noProxy) {
- mConnection = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
- } else {
- mConnection = (HttpURLConnection)url.openConnection();
- }
- mConnection.setConnectTimeout(CONNECT_TIMEOUT_MS);
-
- // handle redirects ourselves if we do not allow cross-domain redirect
- mConnection.setInstanceFollowRedirects(mAllowCrossDomainRedirect);
-
- if (mHeaders != null) {
- for (Map.Entry<String, String> entry : mHeaders.entrySet()) {
- mConnection.setRequestProperty(
- entry.getKey(), entry.getValue());
- }
- }
-
- if (offset > 0) {
- mConnection.setRequestProperty(
- "Range", "bytes=" + offset + "-");
- }
-
- response = mConnection.getResponseCode();
- if (response != HttpURLConnection.HTTP_MULT_CHOICE &&
- response != HttpURLConnection.HTTP_MOVED_PERM &&
- response != HttpURLConnection.HTTP_MOVED_TEMP &&
- response != HttpURLConnection.HTTP_SEE_OTHER &&
- response != HTTP_TEMP_REDIRECT) {
- // not a redirect, or redirect handled by HttpURLConnection
- break;
- }
-
- if (++redirectCount > MAX_REDIRECTS) {
- throw new NoRouteToHostException("Too many redirects: " + redirectCount);
- }
-
- String method = mConnection.getRequestMethod();
- if (response == HTTP_TEMP_REDIRECT &&
- !method.equals("GET") && !method.equals("HEAD")) {
- // "If the 307 status code is received in response to a
- // request other than GET or HEAD, the user agent MUST NOT
- // automatically redirect the request"
- throw new NoRouteToHostException("Invalid redirect");
- }
- String location = mConnection.getHeaderField("Location");
- if (location == null) {
- throw new NoRouteToHostException("Invalid redirect");
- }
- url = new URL(mURL /* TRICKY: don't use url! */, location);
- if (!url.getProtocol().equals("https") &&
- !url.getProtocol().equals("http")) {
- throw new NoRouteToHostException("Unsupported protocol redirect");
- }
- boolean sameProtocol = mURL.getProtocol().equals(url.getProtocol());
- if (!mAllowCrossProtocolRedirect && !sameProtocol) {
- throw new NoRouteToHostException("Cross-protocol redirects are disallowed");
- }
- boolean sameHost = mURL.getHost().equals(url.getHost());
- if (!mAllowCrossDomainRedirect && !sameHost) {
- throw new NoRouteToHostException("Cross-domain redirects are disallowed");
- }
-
- if (response != HTTP_TEMP_REDIRECT) {
- // update effective URL, unless it is a Temporary Redirect
- mURL = url;
- }
- }
-
- if (mAllowCrossDomainRedirect) {
- // remember the current, potentially redirected URL if redirects
- // were handled by HttpURLConnection
- mURL = mConnection.getURL();
- }
-
- if (response == HttpURLConnection.HTTP_PARTIAL) {
- // Partial content, we cannot just use getContentLength
- // because what we want is not just the length of the range
- // returned but the size of the full content if available.
-
- String contentRange =
- mConnection.getHeaderField("Content-Range");
-
- mTotalSize = -1;
- if (contentRange != null) {
- // format is "bytes xxx-yyy/zzz
- // where "zzz" is the total number of bytes of the
- // content or '*' if unknown.
-
- int lastSlashPos = contentRange.lastIndexOf('/');
- if (lastSlashPos >= 0) {
- String total =
- contentRange.substring(lastSlashPos + 1);
-
- try {
- mTotalSize = Long.parseLong(total);
- } catch (NumberFormatException e) {
- }
- }
- }
- } else if (response != HttpURLConnection.HTTP_OK) {
- throw new IOException();
- } else {
- mTotalSize = mConnection.getContentLength();
- }
-
- if (offset > 0 && response != HttpURLConnection.HTTP_PARTIAL) {
- // Some servers simply ignore "Range" requests and serve
- // data from the start of the content.
- throw new ProtocolException();
- }
-
- mInputStream =
- new BufferedInputStream(mConnection.getInputStream());
-
- mCurrentOffset = offset;
- } catch (IOException e) {
- mTotalSize = -1;
- teardownConnection();
- mCurrentOffset = -1;
-
- throw e;
- }
- }
-
- public int readAt(long offset, byte[] data, int size) {
- StrictMode.ThreadPolicy policy =
- new StrictMode.ThreadPolicy.Builder().permitAll().build();
-
- StrictMode.setThreadPolicy(policy);
-
- try {
- if (offset != mCurrentOffset) {
- seekTo(offset);
- }
-
- int n = mInputStream.read(data, 0, size);
-
- if (n == -1) {
- // InputStream signals EOS using a -1 result, our semantics
- // are to return a 0-length read.
- n = 0;
- }
-
- mCurrentOffset += n;
-
- if (VERBOSE) {
- Log.d(TAG, "readAt " + offset + " / " + size + " => " + n);
- }
-
- return n;
- } catch (ProtocolException e) {
- Log.w(TAG, "readAt " + offset + " / " + size + " => " + e);
- return MEDIA_ERROR_UNSUPPORTED;
- } catch (NoRouteToHostException e) {
- Log.w(TAG, "readAt " + offset + " / " + size + " => " + e);
- return MEDIA_ERROR_UNSUPPORTED;
- } catch (UnknownServiceException e) {
- Log.w(TAG, "readAt " + offset + " / " + size + " => " + e);
- return MEDIA_ERROR_UNSUPPORTED;
- } catch (IOException e) {
- if (VERBOSE) {
- Log.d(TAG, "readAt " + offset + " / " + size + " => -1");
- }
- return -1;
- } catch (Exception e) {
- if (VERBOSE) {
- Log.d(TAG, "unknown exception " + e);
- Log.d(TAG, "readAt " + offset + " / " + size + " => -1");
- }
- return -1;
- }
- }
-
- public long getSize() {
- if (mConnection == null) {
- try {
- seekTo(0);
- } catch (IOException e) {
- return -1;
- }
- }
-
- return mTotalSize;
- }
-
- public String getMIMEType() {
- if (mConnection == null) {
- try {
- seekTo(0);
- } catch (IOException e) {
- return "application/octet-stream";
- }
- }
-
- return mConnection.getContentType();
- }
-
- public String getUri() {
- return mURL.toString();
- }
-}
diff --git a/media/apex/java/android/media/Media2HTTPService.java b/media/apex/java/android/media/Media2HTTPService.java
deleted file mode 100644
index 0d46ce404831..000000000000
--- a/media/apex/java/android/media/Media2HTTPService.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.util.Log;
-
-import java.net.HttpCookie;
-import java.util.List;
-
-/** @hide */
-public class Media2HTTPService {
- private static final String TAG = "Media2HTTPService";
- private List<HttpCookie> mCookies;
- private Boolean mCookieStoreInitialized = new Boolean(false);
-
- public Media2HTTPService(List<HttpCookie> cookies) {
- mCookies = cookies;
- Log.v(TAG, "Media2HTTPService(" + this + "): Cookies: " + cookies);
- }
-
- public Media2HTTPConnection makeHTTPConnection() {
-
- synchronized (mCookieStoreInitialized) {
- Media2Utils.storeCookies(mCookies);
- }
-
- return new Media2HTTPConnection();
- }
-
- /* package private */ static Media2HTTPService createHTTPService(String path) {
- return createHTTPService(path, null);
- }
-
- // when cookies are provided
- static Media2HTTPService createHTTPService(String path, List<HttpCookie> cookies) {
- if (path.startsWith("http://") || path.startsWith("https://")) {
- return (new Media2HTTPService(cookies));
- } else if (path.startsWith("widevine://")) {
- Log.d(TAG, "Widevine classic is no longer supported");
- }
-
- return null;
- }
-}
diff --git a/media/apex/java/android/media/Media2Utils.java b/media/apex/java/android/media/Media2Utils.java
deleted file mode 100644
index a87e9676d017..000000000000
--- a/media/apex/java/android/media/Media2Utils.java
+++ /dev/null
@@ -1,78 +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 android.media;
-
-import android.util.Log;
-
-import java.net.CookieHandler;
-import java.net.CookieManager;
-import java.net.CookieStore;
-import java.net.HttpCookie;
-import java.util.List;
-
-/** @hide */
-public class Media2Utils {
- private static final String TAG = "Media2Utils";
-
- private Media2Utils() {
- }
-
- /**
- * Ensures that an expression checking an argument is true.
- *
- * @param expression the expression to check
- * @param errorMessage the exception message to use if the check fails; will
- * be converted to a string using {@link String#valueOf(Object)}
- * @throws IllegalArgumentException if {@code expression} is false
- */
- public static void checkArgument(boolean expression, String errorMessage) {
- if (!expression) {
- throw new IllegalArgumentException(errorMessage);
- }
- }
-
- public static synchronized void storeCookies(List<HttpCookie> cookies) {
- CookieHandler cookieHandler = CookieHandler.getDefault();
- if (cookieHandler == null) {
- cookieHandler = new CookieManager();
- CookieHandler.setDefault(cookieHandler);
- Log.v(TAG, "storeCookies: CookieManager created: " + cookieHandler);
- } else {
- Log.v(TAG, "storeCookies: CookieHandler (" + cookieHandler + ") exists.");
- }
-
- if (cookies != null) {
- if (cookieHandler instanceof CookieManager) {
- CookieManager cookieManager = (CookieManager)cookieHandler;
- CookieStore store = cookieManager.getCookieStore();
- for (HttpCookie cookie : cookies) {
- try {
- store.add(null, cookie);
- } catch (Exception e) {
- Log.v(TAG, "storeCookies: CookieStore.add" + cookie, e);
- }
- }
- } else {
- Log.w(TAG, "storeCookies: The installed CookieHandler is not a CookieManager."
- + " Can’t add the provided cookies to the cookie store.");
- }
- } // cookies
-
- Log.v(TAG, "storeCookies: cookieHandler: " + cookieHandler + " Cookies: " + cookies);
-
- }
-}
diff --git a/media/apex/java/android/media/MediaPlayer2.java b/media/apex/java/android/media/MediaPlayer2.java
deleted file mode 100644
index 614d737e3758..000000000000
--- a/media/apex/java/android/media/MediaPlayer2.java
+++ /dev/null
@@ -1,5507 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.annotation.CallbackExecutor;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.StringDef;
-import android.annotation.TestApi;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.graphics.Rect;
-import android.graphics.SurfaceTexture;
-import android.media.MediaDrm.KeyRequest;
-import android.media.MediaPlayer2.DrmInfo;
-import android.media.MediaPlayer2Proto.PlayerMessage;
-import android.media.MediaPlayer2Proto.Value;
-import android.media.protobuf.InvalidProtocolBufferException;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.ParcelFileDescriptor;
-import android.os.PersistableBundle;
-import android.os.PowerManager;
-import android.util.Log;
-import android.util.Pair;
-import android.util.Size;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
-import java.net.HttpCookie;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.nio.ByteOrder;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Queue;
-import java.util.UUID;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.RejectedExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
-
-/**
- * MediaPlayer2 class can be used to control playback of audio/video files and streams.
- *
- * <p>
- * This API is not generally intended for third party application developers.
- * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
- * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
- * for consistent behavior across all devices.
- *
- * <p>Topics covered here are:
- * <ol>
- * <li><a href="#PlayerStates">Player states</a>
- * <li><a href="#InvalidStates">Invalid method calls</a>
- * <li><a href="#Permissions">Permissions</a>
- * <li><a href="#Callbacks">Callbacks</a>
- * </ol>
- *
- *
- * <h3 id="PlayerStates">Player states</h3>
- *
- * <p>The playback control of audio/video files is managed as a state machine.</p>
- * <p><div style="text-align:center;"><img src="../../../images/mediaplayer2_state_diagram.png"
- * alt="MediaPlayer2 State diagram"
- * border="0" /></div></p>
- * <p>The MediaPlayer2 object has five states:</p>
- * <ol>
- * <li><p>{@link #PLAYER_STATE_IDLE}: MediaPlayer2 is in the <strong>Idle</strong>
- * state after it's created, or after calling {@link #reset()}.</p>
- *
- * <p>While in this state, you should call
- * {@link #setDataSource setDataSource}. It is a good
- * programming practice to register an {@link EventCallback#onCallCompleted onCallCompleted}
- * <a href="#Callbacks">callback</a> and watch for {@link #CALL_STATUS_BAD_VALUE} and
- * {@link #CALL_STATUS_ERROR_IO}, which might be caused by <code>setDataSource</code>.
- * </p>
- *
- * <p>Calling {@link #prepare()} transfers a MediaPlayer2 object to
- * the <strong>Prepared</strong> state. Note
- * that {@link #prepare()} is asynchronous. When the preparation completes,
- * if you register an {@link EventCallback#onInfo onInfo} <a href="#Callbacks">callback</a>,
- * the player executes the callback
- * with {@link #MEDIA_INFO_PREPARED} and transitions to the
- * <strong>Prepared</strong> state.</p>
- * </li>
- *
- * <li>{@link #PLAYER_STATE_PREPARED}: A MediaPlayer object must be in the
- * <strong>Prepared</strong> state before playback can be started for the first time.
- * While in this state, you can set player properties
- * such as audio/sound volume and looping by invoking the corresponding set methods.
- * Calling {@link #play()} transfers a MediaPlayer2 object to
- * the <strong>Playing</strong> state.
- * </li>
- *
- * <li>{@link #PLAYER_STATE_PLAYING}:
- * <p>The player plays the data source while in this state.
- * If you register an {@link EventCallback#onInfo onInfo} <a href="#Callbacks">callback</a>,
- * the player regularly executes the callback with
- * {@link #MEDIA_INFO_BUFFERING_UPDATE}.
- * This allows applications to keep track of the buffering status
- * while streaming audio/video.</p>
- *
- * <p> When the playback reaches the end of stream, the behavior depends on whether or
- * not you've enabled looping by calling {@link #loopCurrent}:</p>
- * <ul>
- * <li>If the looping mode was set to <code>false</code>, the player will transfer
- * to the <strong>Paused</strong> state. If you registered an {@link EventCallback#onInfo
- * onInfo} <a href="#Callbacks">callback</a>
- * the player calls the callback with {@link #MEDIA_INFO_DATA_SOURCE_END} and enters
- * the <strong>Paused</strong> state.
- * </li>
- * <li>If the looping mode was set to <code>true</code>,
- * the MediaPlayer2 object remains in the <strong>Playing</strong> state and replays its
- * data source from the beginning.</li>
- * </ul>
- * </li>
- *
- * <li>{@link #PLAYER_STATE_PAUSED}: Audio/video playback pauses while in this state.
- * Call {@link #play()} to resume playback from the position where it paused.</li>
- *
- * <li>{@link #PLAYER_STATE_ERROR}: <p>In general, playback might fail due to various
- * reasons such as unsupported audio/video format, poorly interleaved
- * audio/video, resolution too high, streaming timeout, and others.
- * In addition, due to programming errors, a playback
- * control operation might be performed from an <a href="#InvalidStates">invalid state</a>.
- * In these cases the player transitions to the <strong>Error</strong> state.</p>
- *
- * <p>If you register an {@link EventCallback#onError onError}}
- * <a href="#Callbacks">callback</a>,
- * the callback will be performed when entering the state. When programming errors happen,
- * such as calling {@link #prepare()} and
- * {@link #setDataSource} methods
- * from an <a href="#InvalidStates">invalid state</a>, the callback is called with
- * {@link #CALL_STATUS_INVALID_OPERATION}. The MediaPlayer2 object enters the
- * <strong>Error</strong> state whether or not a callback exists. </p>
- *
- * <p>To recover from an error and reuse a MediaPlayer2 object that is in the <strong>
- * Error</strong> state,
- * call {@link #reset()}. The object will return to the <strong>Idle</strong>
- * state and all state information will be lost.</p>
- * </li>
- * </ol>
- *
- * <p>You should follow these best practices when coding an app that uses MediaPlayer2:</p>
- *
- * <ul>
- *
- * <li>Use <a href="#Callbacks">callbacks</a> to respond to state changes and errors.</li>
- *
- * <li>When a MediaPlayer2 object is no longer being used, call {@link #close()} as soon as
- * possible to release the resources used by the internal player engine associated with the
- * MediaPlayer2. Failure to call {@link #close()} may cause subsequent instances of
- * MediaPlayer2 objects to fallback to software implementations or fail altogether.
- * You cannot use MediaPlayer2
- * after you call {@link #close()}. There is no way to bring it back to any other state.</li>
- *
- * <li>The current playback position can be retrieved with a call to
- * {@link #getCurrentPosition()},
- * which is helpful for applications such as a Music player that need to keep track of the playback
- * progress.</li>
- *
- * <li>The playback position can be adjusted with a call to {@link #seekTo}. Although the
- * asynchronous {@link #seekTo} call returns right away, the actual seek operation may take a
- * while to finish, especially for audio/video being streamed. If you register an
- * {@link EventCallback#onCallCompleted onCallCompleted} <a href="#Callbacks">callback</a>,
- * the callback is
- * called When the seek operation completes with {@link #CALL_COMPLETED_SEEK_TO}.</li>
- *
- * <li>You can call {@link #seekTo} from the <strong>Paused</strong> state.
- * In this case, if you are playing a video stream and
- * the requested position is valid one video frame is displayed.</li>
- *
- * </ul>
- *
- * <h3 id="InvalidStates">Invalid method calls</h3>
- *
- * <p>The only methods you safely call from the <strong>Error</strong> state are
- * {@link #close},
- * {@link #reset},
- * {@link #notifyWhenCommandLabelReached},
- * {@link #clearPendingCommands},
- * {@link #registerEventCallback},
- * {@link #unregisterEventCallback}
- * and {@link #getState}.
- * Any other methods might throw an exception, return meaningless data, or invoke a
- * {@link EventCallback#onCallCompleted onCallCompleted} with an error code.</p>
- *
- * <p>Most methods can be called from any non-Error state. They will either perform their work or
- * silently have no effect. The following table lists the methods that will invoke a
- * {@link EventCallback#onCallCompleted onCallCompleted} with an error code
- * or throw an exception when they are called from the associated invalid states.</p>
- *
- * <table border="0" cellspacing="0" cellpadding="0">
- * <tr><th>Method Name</th>
- * <th>Invalid States</th></tr>
- *
- * <tr><td>setDataSource</td> <td>{Prepared, Paused, Playing}</td></tr>
- * <tr><td>prepare</td> <td>{Prepared, Paused, Playing}</td></tr>
- * <tr><td>play</td> <td>{Idle}</td></tr>
- * <tr><td>pause</td> <td>{Idle}</td></tr>
- * <tr><td>seekTo</td> <td>{Idle}</td></tr>
- * <tr><td>getCurrentPosition</td> <td>{Idle}</td></tr>
- * <tr><td>getDuration</td> <td>{Idle}</td></tr>
- * <tr><td>getBufferedPosition</td> <td>{Idle}</td></tr>
- * <tr><td>getTrackInfo</td> <td>{Idle}</td></tr>
- * <tr><td>getSelectedTrack</td> <td>{Idle}</td></tr>
- * <tr><td>selectTrack</td> <td>{Idle}</td></tr>
- * <tr><td>deselectTrack</td> <td>{Idle}</td></tr>
- * </table>
- *
- * <h3 id="Permissions">Permissions</h3>
- * <p>This class requires the {@link android.Manifest.permission#INTERNET} permission
- * when used with network-based content.
- *
- * <h3 id="Callbacks">Callbacks</h3>
- * <p>Many errors do not result in a transition to the <strong>Error</strong> state.
- * It is good programming practice to register callback listeners using
- * {@link #registerEventCallback}.
- * You can receive a callback at any time and from any state.</p>
- *
- * <p>If it's important for your app to respond to state changes (for instance, to update the
- * controls on a transport UI), you should register an
- * {@link EventCallback#onCallCompleted onCallCompleted} and
- * detect state change commands by testing the <code>what</code> parameter for a callback from one
- * of the state transition methods: {@link #CALL_COMPLETED_PREPARE}, {@link #CALL_COMPLETED_PLAY},
- * and {@link #CALL_COMPLETED_PAUSE}.
- * Then check the <code>status</code> parameter. The value {@link #CALL_STATUS_NO_ERROR} indicates a
- * successful transition. Any other value will be an error. Call {@link #getState()} to
- * determine the current state. </p>
- *
- * @hide
- */
-public class MediaPlayer2 implements AutoCloseable, AudioRouting {
- static {
- System.loadLibrary("media2_jni");
- native_init();
- }
-
- private static native void native_init();
-
- private static final int NEXT_SOURCE_STATE_ERROR = -1;
- private static final int NEXT_SOURCE_STATE_INIT = 0;
- private static final int NEXT_SOURCE_STATE_PREPARING = 1;
- private static final int NEXT_SOURCE_STATE_PREPARED = 2;
-
- private static final String TAG = "MediaPlayer2";
-
- private Context mContext;
-
- private long mNativeContext; // accessed by native methods
- private long mNativeSurfaceTexture; // accessed by native methods
- private int mListenerContext; // accessed by native methods
- private SurfaceHolder mSurfaceHolder;
- private PowerManager.WakeLock mWakeLock = null;
- private boolean mScreenOnWhilePlaying;
- private boolean mStayAwake;
-
- private final Object mSrcLock = new Object();
- //--- guarded by |mSrcLock| start
- private SourceInfo mCurrentSourceInfo;
- private final Queue<SourceInfo> mNextSourceInfos = new ConcurrentLinkedQueue<>();
- //--- guarded by |mSrcLock| end
- private final AtomicLong mSrcIdGenerator = new AtomicLong(0);
-
- private volatile float mVolume = 1.0f;
- private Size mVideoSize = new Size(0, 0);
-
- private static ExecutorService sDrmThreadPool = Executors.newCachedThreadPool();
-
- // Creating a dummy audio track, used for keeping session id alive
- private final Object mSessionIdLock = new Object();
- @GuardedBy("mSessionIdLock")
- private AudioTrack mDummyAudioTrack;
-
- private HandlerThread mHandlerThread;
- private final TaskHandler mTaskHandler;
- private final Object mTaskLock = new Object();
- @GuardedBy("mTaskLock")
- private final List<Task> mPendingTasks = new LinkedList<>();
- @GuardedBy("mTaskLock")
- private Task mCurrentTask;
- private final AtomicLong mTaskIdGenerator = new AtomicLong(0);
-
- @GuardedBy("mTaskLock")
- boolean mIsPreviousCommandSeekTo = false;
- // |mPreviousSeekPos| and |mPreviousSeekMode| are valid only when |mIsPreviousCommandSeekTo|
- // is true, and they are accessed on |mHandlerThread| only.
- long mPreviousSeekPos = -1;
- int mPreviousSeekMode = SEEK_PREVIOUS_SYNC;
-
- @GuardedBy("this")
- private boolean mReleased;
-
- private final CloseGuard mGuard = CloseGuard.get();
-
- /**
- * Default constructor.
- * <p>When done with the MediaPlayer2, you should call {@link #close()},
- * to free the resources. If not released, too many MediaPlayer2 instances may
- * result in an exception.</p>
- */
- public MediaPlayer2(@NonNull Context context) {
- mGuard.open("close");
-
- mContext = context;
- mHandlerThread = new HandlerThread("MediaPlayer2TaskThread");
- mHandlerThread.start();
- Looper looper = mHandlerThread.getLooper();
- mTaskHandler = new TaskHandler(this, looper);
- AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- int sessionId = am.generateAudioSessionId();
- keepAudioSessionIdAlive(sessionId);
-
- /* Native setup requires a weak reference to our object.
- * It's easier to create it here than in C++.
- */
- native_setup(sessionId, new WeakReference<MediaPlayer2>(this));
- }
-
- private native void native_setup(int sessionId, Object mediaplayer2This);
-
- /**
- * Releases the resources held by this {@code MediaPlayer2} object.
- *
- * It is considered good practice to call this method when you're
- * done using the MediaPlayer2. In particular, whenever an Activity
- * of an application is paused (its onPause() method is called),
- * or stopped (its onStop() method is called), this method should be
- * invoked to release the MediaPlayer2 object, unless the application
- * has a special need to keep the object around. In addition to
- * unnecessary resources (such as memory and instances of codecs)
- * being held, failure to call this method immediately if a
- * MediaPlayer2 object is no longer needed may also lead to
- * continuous battery consumption for mobile devices, and playback
- * failure for other applications if no multiple instances of the
- * same codec are supported on a device. Even if multiple instances
- * of the same codec are supported, some performance degradation
- * may be expected when unnecessary multiple instances are used
- * at the same time.
- *
- * {@code close()} may be safely called after a prior {@code close()}.
- * This class implements the Java {@code AutoCloseable} interface and
- * may be used with try-with-resources.
- */
- // This is a synchronous call.
- @Override
- public void close() {
- synchronized (mGuard) {
- mGuard.close();
- }
- release();
- }
-
- private synchronized void release() {
- if (mReleased) {
- return;
- }
- stayAwake(false);
- updateSurfaceScreenOn();
- synchronized (mEventCbLock) {
- mEventCallbackRecords.clear();
- }
- if (mHandlerThread != null) {
- mHandlerThread.quitSafely();
- mHandlerThread = null;
- }
-
- clearSourceInfos();
-
- // Modular DRM clean up
- synchronized (mDrmEventCallbackLock) {
- mDrmEventCallback = null;
- }
- clearMediaDrmObjects();
-
- native_release();
-
- synchronized (mSessionIdLock) {
- mDummyAudioTrack.release();
- }
-
- mReleased = true;
- }
-
- void clearMediaDrmObjects() {
- Collection<MediaDrm> drmObjs = mDrmObjs.values();
- synchronized (mDrmObjs) {
- for (MediaDrm drmObj : drmObjs) {
- drmObj.close();
- }
- mDrmObjs.clear();
- }
- }
-
- private native void native_release();
-
- // Have to declare protected for finalize() since it is protected
- // in the base class Object.
- @Override
- protected void finalize() throws Throwable {
- if (mGuard != null) {
- mGuard.warnIfOpen();
- }
-
- close();
- native_finalize();
- }
-
- private native void native_finalize();
-
- /**
- * Resets the MediaPlayer2 to its uninitialized state. After calling
- * this method, you will have to initialize it again by setting the
- * data source and calling prepare().
- */
- // This is a synchronous call.
- public void reset() {
- clearSourceInfos();
- clearMediaDrmObjects();
-
- stayAwake(false);
- native_reset();
-
- AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
- int sessionId = am.generateAudioSessionId();
- keepAudioSessionIdAlive(sessionId);
-
- // make sure none of the listeners get called anymore
- if (mTaskHandler != null) {
- mTaskHandler.removeCallbacksAndMessages(null);
- }
-
- }
-
- private native void native_reset();
-
- /**
- * Starts or resumes playback. If playback had previously been paused,
- * playback will continue from where it was paused. If playback had
- * reached end of stream and been paused, or never started before,
- * playback will start at the beginning.
- *
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- */
- // This is an asynchronous call.
- public @NonNull Object play() {
- return addTask(new Task(CALL_COMPLETED_PLAY, false) {
- @Override
- void process() {
- stayAwake(true);
- native_start();
- }
- });
- }
-
- private native void native_start() throws IllegalStateException;
-
- /**
- * Prepares the player for playback, asynchronously.
- *
- * After setting the datasource and the display surface, you need to call prepare().
- *
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- */
- // This is an asynchronous call.
- public @NonNull Object prepare() {
- return addTask(new Task(CALL_COMPLETED_PREPARE, true) {
- @Override
- void process() {
- native_prepare();
- }
- });
- }
-
- private native void native_prepare();
-
- /**
- * Pauses playback. Call play() to resume.
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- */
- // This is an asynchronous call.
- public @NonNull Object pause() {
- return addTask(new Task(CALL_COMPLETED_PAUSE, false) {
- @Override
- void process() {
- stayAwake(false);
-
- native_pause();
- }
- });
- }
-
- private native void native_pause() throws IllegalStateException;
-
- /**
- * Tries to play next data source if applicable.
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- */
- // This is an asynchronous call.
- public @NonNull Object skipToNext() {
- return addTask(new Task(CALL_COMPLETED_SKIP_TO_NEXT, false) {
- @Override
- void process() {
- if (getState() == PLAYER_STATE_PLAYING) {
- native_pause();
- }
- playNextDataSource();
- }
- });
- }
-
- /**
- * Gets the current playback position.
- *
- * @return the current position in milliseconds
- */
- public native long getCurrentPosition();
-
- /**
- * Gets the duration of the current data source.
- * Same as {@link #getDuration(DataSourceDesc)} with
- * {@code dsd = getCurrentDataSource()}.
- *
- * @return the duration in milliseconds, if no duration is available
- * (for example, if streaming live content), -1 is returned.
- * @throws NullPointerException if current data source is null
- */
- public long getDuration() {
- return getDuration(getCurrentDataSource());
- }
-
- /**
- * Gets the duration of the dsd.
- *
- * @param dsd the descriptor of data source of which you want to get duration
- * @return the duration in milliseconds, if no duration is available
- * (for example, if streaming live content), -1 is returned.
- * @throws NullPointerException if dsd is null
- */
- public long getDuration(@NonNull DataSourceDesc dsd) {
- if (dsd == null) {
- throw new NullPointerException("non-null dsd is expected");
- }
- SourceInfo sourceInfo = getSourceInfo(dsd);
- if (sourceInfo == null) {
- return -1;
- }
-
- return native_getDuration(sourceInfo.mId);
- }
-
- private native long native_getDuration(long srcId);
-
- /**
- * Gets the buffered media source position of current data source.
- * Same as {@link #getBufferedPosition(DataSourceDesc)} with
- * {@code dsd = getCurrentDataSource()}.
- *
- * @return the current buffered media source position in milliseconds
- * @throws NullPointerException if current data source is null
- */
- public long getBufferedPosition() {
- return getBufferedPosition(getCurrentDataSource());
- }
-
- /**
- * Gets the buffered media source position of given dsd.
- * For example a buffering update of 8000 milliseconds when 5000 milliseconds of the content
- * has already been played indicates that the next 3000 milliseconds of the
- * content to play has been buffered.
- *
- * @param dsd the descriptor of data source of which you want to get buffered position
- * @return the current buffered media source position in milliseconds
- * @throws NullPointerException if dsd is null
- */
- public long getBufferedPosition(@NonNull DataSourceDesc dsd) {
- if (dsd == null) {
- throw new NullPointerException("non-null dsd is expected");
- }
- SourceInfo sourceInfo = getSourceInfo(dsd);
- if (sourceInfo == null) {
- return 0;
- }
-
- // Use cached buffered percent for now.
- int bufferedPercentage = sourceInfo.mBufferedPercentage.get();
-
- long duration = getDuration(dsd);
- if (duration < 0) {
- duration = 0;
- }
-
- return duration * bufferedPercentage / 100;
- }
-
- /**
- * MediaPlayer2 has not been prepared or just has been reset.
- * In this state, MediaPlayer2 doesn't fetch data.
- */
- public static final int PLAYER_STATE_IDLE = 1001;
-
- /**
- * MediaPlayer2 has been just prepared.
- * In this state, MediaPlayer2 just fetches data from media source,
- * but doesn't actively render data.
- */
- public static final int PLAYER_STATE_PREPARED = 1002;
-
- /**
- * MediaPlayer2 is paused.
- * In this state, MediaPlayer2 has allocated resources to construct playback
- * pipeline, but it doesn't actively render data.
- */
- public static final int PLAYER_STATE_PAUSED = 1003;
-
- /**
- * MediaPlayer2 is actively playing back data.
- */
- public static final int PLAYER_STATE_PLAYING = 1004;
-
- /**
- * MediaPlayer2 has hit some fatal error and cannot continue playback.
- */
- public static final int PLAYER_STATE_ERROR = 1005;
-
- /**
- * @hide
- */
- @IntDef(flag = false, prefix = "MEDIAPLAYER2_STATE", value = {
- PLAYER_STATE_IDLE,
- PLAYER_STATE_PREPARED,
- PLAYER_STATE_PAUSED,
- PLAYER_STATE_PLAYING,
- PLAYER_STATE_ERROR })
- @Retention(RetentionPolicy.SOURCE)
- public @interface MediaPlayer2State {}
-
- /**
- * Gets the current player state.
- *
- * @return the current player state.
- */
- public @MediaPlayer2State int getState() {
- return native_getState();
- }
-
- private native int native_getState();
-
- /**
- * Sets the audio attributes for this MediaPlayer2.
- * See {@link AudioAttributes} for how to build and configure an instance of this class.
- * You must call this method before {@link #play()} and {@link #pause()} in order
- * for the audio attributes to become effective thereafter.
- * @param attributes a non-null set of audio attributes
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- */
- // This is an asynchronous call.
- public @NonNull Object setAudioAttributes(@NonNull AudioAttributes attributes) {
- return addTask(new Task(CALL_COMPLETED_SET_AUDIO_ATTRIBUTES, false) {
- @Override
- void process() {
- if (attributes == null) {
- final String msg = "Cannot set AudioAttributes to null";
- throw new IllegalArgumentException(msg);
- }
- native_setAudioAttributes(attributes);
- }
- });
- }
-
- // return true if the parameter is set successfully, false otherwise
- private native boolean native_setAudioAttributes(AudioAttributes audioAttributes);
-
- /**
- * Gets the audio attributes for this MediaPlayer2.
- * @return attributes a set of audio attributes
- */
- public @NonNull AudioAttributes getAudioAttributes() {
- return native_getAudioAttributes();
- }
-
- private native AudioAttributes native_getAudioAttributes();
-
- /**
- * Sets the data source as described by a DataSourceDesc.
- * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor}
- * in the {@link FileDataSourceDesc} will be closed by the player.
- *
- * @param dsd the descriptor of data source you want to play
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- */
- // This is an asynchronous call.
- public @NonNull Object setDataSource(@NonNull DataSourceDesc dsd) {
- return addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
- @Override
- void process() throws IOException {
- checkDataSourceDesc(dsd);
- int state = getState();
- try {
- if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) {
- throw new IllegalStateException("called in wrong state " + state);
- }
-
- synchronized (mSrcLock) {
- setCurrentSourceInfo_l(new SourceInfo(dsd));
- handleDataSource(true /* isCurrent */, dsd, mCurrentSourceInfo.mId);
- }
- } finally {
- dsd.close();
- }
- }
-
- });
- }
-
- /**
- * Sets a single data source as described by a DataSourceDesc which will be played
- * after current data source is finished.
- * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor}
- * in the {@link FileDataSourceDesc} will be closed by the player.
- *
- * @param dsd the descriptor of data source you want to play after current one
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- */
- // This is an asynchronous call.
- public @NonNull Object setNextDataSource(@NonNull DataSourceDesc dsd) {
- return addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
- @Override
- void process() {
- checkDataSourceDesc(dsd);
- synchronized (mSrcLock) {
- clearNextSourceInfos_l();
- mNextSourceInfos.add(new SourceInfo(dsd));
- }
- prepareNextDataSource();
- }
- });
- }
-
- /**
- * Sets a list of data sources to be played sequentially after current data source is done.
- * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor}
- * in the {@link FileDataSourceDesc} will be closed by the player.
- *
- * @param dsds the list of data sources you want to play after current one
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- */
- // This is an asynchronous call.
- public @NonNull Object setNextDataSources(@NonNull List<DataSourceDesc> dsds) {
- return addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCES, false) {
- @Override
- void process() {
- if (dsds == null || dsds.size() == 0) {
- throw new IllegalArgumentException("data source list cannot be null or empty.");
- }
- boolean hasError = false;
- for (DataSourceDesc dsd : dsds) {
- if (dsd == null) {
- hasError = true;
- continue;
- }
- if (dsd instanceof FileDataSourceDesc) {
- FileDataSourceDesc fdsd = (FileDataSourceDesc) dsd;
- if (fdsd.isPFDClosed()) {
- hasError = true;
- continue;
- }
-
- fdsd.incCount();
- }
- }
- if (hasError) {
- for (DataSourceDesc dsd : dsds) {
- if (dsd != null) {
- dsd.close();
- }
- }
- throw new IllegalArgumentException("invalid data source list");
- }
-
- synchronized (mSrcLock) {
- clearNextSourceInfos_l();
- for (DataSourceDesc dsd : dsds) {
- mNextSourceInfos.add(new SourceInfo(dsd));
- }
- }
- prepareNextDataSource();
- }
- });
- }
-
- // throws IllegalArgumentException if dsd is null or underline PFD of dsd has been closed.
- private void checkDataSourceDesc(DataSourceDesc dsd) {
- if (dsd == null) {
- throw new IllegalArgumentException("dsd is expected to be non null");
- }
- if (dsd instanceof FileDataSourceDesc) {
- FileDataSourceDesc fdsd = (FileDataSourceDesc) dsd;
- if (fdsd.isPFDClosed()) {
- throw new IllegalArgumentException("the underline FileDescriptor has been closed");
- }
- fdsd.incCount();
- }
- }
-
- /**
- * Removes all data sources pending to be played.
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- */
- // This is an asynchronous call.
- public @NonNull Object clearNextDataSources() {
- return addTask(new Task(CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES, false) {
- @Override
- void process() {
- synchronized (mSrcLock) {
- clearNextSourceInfos_l();
- }
- }
- });
- }
-
- /**
- * Gets the current data source as described by a DataSourceDesc.
- *
- * @return the current DataSourceDesc
- */
- public @Nullable DataSourceDesc getCurrentDataSource() {
- synchronized (mSrcLock) {
- return mCurrentSourceInfo == null ? null : mCurrentSourceInfo.mDSD;
- }
- }
-
- private void handleDataSource(boolean isCurrent, @NonNull DataSourceDesc dsd, long srcId)
- throws IOException {
- Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
-
- if (dsd instanceof FileDataSourceDesc) {
- FileDataSourceDesc fileDSD = (FileDataSourceDesc) dsd;
- ParcelFileDescriptor pfd = fileDSD.getParcelFileDescriptor();
- if (pfd.getStatSize() == -1) {
- // Underlying pipeline doesn't understand '-1' size. Create a wrapper for
- // translation.
- // TODO: Make native code handle '-1' size.
- handleDataSource(isCurrent,
- srcId,
- new ProxyDataSourceCallback(pfd),
- fileDSD.getStartPosition(),
- fileDSD.getEndPosition());
- } else {
- handleDataSource(isCurrent,
- srcId,
- pfd,
- fileDSD.getOffset(),
- fileDSD.getLength(),
- fileDSD.getStartPosition(),
- fileDSD.getEndPosition());
- }
- } else if (dsd instanceof UriDataSourceDesc) {
- UriDataSourceDesc uriDSD = (UriDataSourceDesc) dsd;
- handleDataSource(isCurrent,
- srcId,
- mContext,
- uriDSD.getUri(),
- uriDSD.getHeaders(),
- uriDSD.getCookies(),
- uriDSD.getStartPosition(),
- uriDSD.getEndPosition());
- } else {
- throw new IllegalArgumentException("Unsupported DataSourceDesc. " + dsd.toString());
- }
- }
-
- /**
- * To provide cookies for the subsequent HTTP requests, you can install your own default cookie
- * handler and use other variants of setDataSource APIs instead. Alternatively, you can use
- * this API to pass the cookies as a list of HttpCookie. If the app has not installed
- * a CookieHandler already, this API creates a CookieManager and populates its CookieStore with
- * the provided cookies. If the app has installed its own handler already, this API requires the
- * handler to be of CookieManager type such that the API can update the manager’s CookieStore.
- *
- * <p><strong>Note</strong> that the cross domain redirection is allowed by default,
- * but that can be changed with key/value pairs through the headers parameter with
- * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to
- * disallow or allow cross domain redirection.
- *
- * @throws IllegalArgumentException if cookies are provided and the installed handler is not
- * a CookieManager
- * @throws IllegalStateException if it is called in an invalid state
- * @throws NullPointerException if context or uri is null
- * @throws IOException if uri has a file scheme and an I/O error occurs
- */
- private void handleDataSource(
- boolean isCurrent, long srcId,
- @NonNull Context context, @NonNull Uri uri,
- @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies,
- long startPos, long endPos)
- throws IOException {
- // The context and URI usually belong to the calling user. Get a resolver for that user.
- final ContentResolver resolver = context.getContentResolver();
- final String scheme = uri.getScheme();
- if (ContentResolver.SCHEME_FILE.equals(scheme)) {
- handleDataSource(isCurrent, srcId, uri.getPath(), null, null, startPos, endPos);
- return;
- }
-
- final int ringToneType = RingtoneManager.getDefaultType(uri);
- try {
- AssetFileDescriptor afd;
- // Try requested Uri locally first
- if (ContentResolver.SCHEME_CONTENT.equals(scheme) && ringToneType != -1) {
- afd = RingtoneManager.openDefaultRingtoneUri(context, uri);
- if (attemptDataSource(isCurrent, srcId, afd, startPos, endPos)) {
- return;
- }
- final Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(
- context, ringToneType);
- afd = resolver.openAssetFileDescriptor(actualUri, "r");
- } else {
- afd = resolver.openAssetFileDescriptor(uri, "r");
- }
- if (attemptDataSource(isCurrent, srcId, afd, startPos, endPos)) {
- return;
- }
- } catch (NullPointerException | SecurityException | IOException ex) {
- Log.w(TAG, "Couldn't open " + uri == null ? "null uri" : uri.toSafeString(), ex);
- // Fallback to media server
- }
- handleDataSource(isCurrent, srcId, uri.toString(), headers, cookies, startPos, endPos);
- }
-
- private boolean attemptDataSource(boolean isCurrent, long srcId, AssetFileDescriptor afd,
- long startPos, long endPos) throws IOException {
- try {
- if (afd.getDeclaredLength() < 0) {
- handleDataSource(isCurrent,
- srcId,
- ParcelFileDescriptor.dup(afd.getFileDescriptor()),
- 0,
- DataSourceDesc.LONG_MAX,
- startPos,
- endPos);
- } else {
- handleDataSource(isCurrent,
- srcId,
- ParcelFileDescriptor.dup(afd.getFileDescriptor()),
- afd.getStartOffset(),
- afd.getDeclaredLength(),
- startPos,
- endPos);
- }
- return true;
- } catch (NullPointerException | SecurityException | IOException ex) {
- Log.w(TAG, "Couldn't open srcId:" + srcId + ": " + ex);
- return false;
- } finally {
- if (afd != null) {
- afd.close();
- }
- }
- }
-
- private void handleDataSource(
- boolean isCurrent, long srcId,
- String path, Map<String, String> headers, List<HttpCookie> cookies,
- long startPos, long endPos)
- throws IOException {
- String[] keys = null;
- String[] values = null;
-
- if (headers != null) {
- keys = new String[headers.size()];
- values = new String[headers.size()];
-
- int i = 0;
- for (Map.Entry<String, String> entry: headers.entrySet()) {
- keys[i] = entry.getKey();
- values[i] = entry.getValue();
- ++i;
- }
- }
- handleDataSource(isCurrent, srcId, path, keys, values, cookies, startPos, endPos);
- }
-
- private void handleDataSource(boolean isCurrent, long srcId,
- String path, String[] keys, String[] values, List<HttpCookie> cookies,
- long startPos, long endPos)
- throws IOException {
- final Uri uri = Uri.parse(path);
- final String scheme = uri.getScheme();
- if ("file".equals(scheme)) {
- path = uri.getPath();
- } else if (scheme != null) {
- // handle non-file sources
- Media2Utils.storeCookies(cookies);
- nativeHandleDataSourceUrl(
- isCurrent,
- srcId,
- Media2HTTPService.createHTTPService(path),
- path,
- keys,
- values,
- startPos,
- endPos);
- return;
- }
-
- final File file = new File(path);
- if (file.exists()) {
- FileInputStream is = new FileInputStream(file);
- FileDescriptor fd = is.getFD();
- handleDataSource(isCurrent, srcId, ParcelFileDescriptor.dup(fd),
- 0, DataSourceDesc.LONG_MAX, startPos, endPos);
- is.close();
- } else {
- throw new IOException("handleDataSource failed.");
- }
- }
-
- private native void nativeHandleDataSourceUrl(
- boolean isCurrent, long srcId,
- Media2HTTPService httpService, String path, String[] keys, String[] values,
- long startPos, long endPos)
- throws IOException;
-
- /**
- * Sets the data source (FileDescriptor) to use. The FileDescriptor must be
- * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility
- * to close the file descriptor. It is safe to do so as soon as this call returns.
- *
- * @throws IllegalStateException if it is called in an invalid state
- * @throws IllegalArgumentException if fd is not a valid FileDescriptor
- * @throws IOException if fd can not be read
- */
- private void handleDataSource(
- boolean isCurrent, long srcId,
- ParcelFileDescriptor pfd, long offset, long length,
- long startPos, long endPos) throws IOException {
- nativeHandleDataSourceFD(isCurrent, srcId, pfd.getFileDescriptor(), offset, length,
- startPos, endPos);
- }
-
- private native void nativeHandleDataSourceFD(boolean isCurrent, long srcId,
- FileDescriptor fd, long offset, long length,
- long startPos, long endPos) throws IOException;
-
- /**
- * @throws IllegalStateException if it is called in an invalid state
- * @throws IllegalArgumentException if dataSource is not a valid DataSourceCallback
- */
- private void handleDataSource(boolean isCurrent, long srcId, DataSourceCallback dataSource,
- long startPos, long endPos) {
- nativeHandleDataSourceCallback(isCurrent, srcId, dataSource, startPos, endPos);
- }
-
- private native void nativeHandleDataSourceCallback(
- boolean isCurrent, long srcId, DataSourceCallback dataSource,
- long startPos, long endPos);
-
- // return true if there is a next data source, false otherwise.
- // This function should be always called on |mHandlerThread|.
- private boolean prepareNextDataSource() {
- HandlerThread handlerThread = mHandlerThread;
- if (handlerThread != null && Looper.myLooper() != handlerThread.getLooper()) {
- Log.e(TAG, "prepareNextDataSource: called on wrong looper");
- }
-
- boolean hasNextDSD;
- int state = getState();
- synchronized (mSrcLock) {
- hasNextDSD = !mNextSourceInfos.isEmpty();
- if (state == PLAYER_STATE_ERROR || state == PLAYER_STATE_IDLE) {
- // Current source has not been prepared yet.
- return hasNextDSD;
- }
-
- SourceInfo nextSource = mNextSourceInfos.peek();
- if (!hasNextDSD || nextSource.mStateAsNextSource != NEXT_SOURCE_STATE_INIT) {
- // There is no next source or it's in preparing or prepared state.
- return hasNextDSD;
- }
-
- try {
- nextSource.mStateAsNextSource = NEXT_SOURCE_STATE_PREPARING;
- handleDataSource(false /* isCurrent */, nextSource.mDSD, nextSource.mId);
- } catch (Exception e) {
- Message msg = mTaskHandler.obtainMessage(
- MEDIA_ERROR, MEDIA_ERROR_IO, MEDIA_ERROR_UNKNOWN, null);
- mTaskHandler.handleMessage(msg, nextSource.mId);
-
- SourceInfo nextSourceInfo = mNextSourceInfos.poll();
- if (nextSource != null) {
- nextSourceInfo.close();
- }
- return prepareNextDataSource();
- }
- }
- return hasNextDSD;
- }
-
- // This function should be always called on |mHandlerThread|.
- private void playNextDataSource() {
- HandlerThread handlerThread = mHandlerThread;
- if (handlerThread != null && Looper.myLooper() != handlerThread.getLooper()) {
- Log.e(TAG, "playNextDataSource: called on wrong looper");
- }
-
- boolean hasNextDSD = false;
- synchronized (mSrcLock) {
- if (!mNextSourceInfos.isEmpty()) {
- hasNextDSD = true;
- SourceInfo nextSourceInfo = mNextSourceInfos.peek();
- if (nextSourceInfo.mStateAsNextSource == NEXT_SOURCE_STATE_PREPARED) {
- // Switch to next source only when it has been prepared.
- setCurrentSourceInfo_l(mNextSourceInfos.poll());
-
- long srcId = mCurrentSourceInfo.mId;
- try {
- nativePlayNextDataSource(srcId);
- } catch (Exception e) {
- Message msg2 = mTaskHandler.obtainMessage(
- MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
- mTaskHandler.handleMessage(msg2, srcId);
- // Keep |mNextSourcePlayPending|
- hasNextDSD = prepareNextDataSource();
- }
- if (hasNextDSD) {
- stayAwake(true);
-
- // Now a new current src is playing.
- // Wait for MEDIA_INFO_DATA_SOURCE_START to prepare next source.
- }
- } else if (nextSourceInfo.mStateAsNextSource == NEXT_SOURCE_STATE_INIT) {
- hasNextDSD = prepareNextDataSource();
- }
- }
- }
-
- if (!hasNextDSD) {
- sendEvent(new EventNotifier() {
- @Override
- public void notify(EventCallback callback) {
- callback.onInfo(
- MediaPlayer2.this, null, MEDIA_INFO_DATA_SOURCE_LIST_END, 0);
- }
- });
- }
- }
-
- private native void nativePlayNextDataSource(long srcId);
-
- /**
- * Configures the player to loop on the current data source.
- * @param loop true if the current data source is meant to loop.
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- */
- // This is an asynchronous call.
- public @NonNull Object loopCurrent(boolean loop) {
- return addTask(new Task(CALL_COMPLETED_LOOP_CURRENT, false) {
- @Override
- void process() {
- setLooping(loop);
- }
- });
- }
-
- private native void setLooping(boolean looping);
-
- /**
- * Sets the volume of the audio of the media to play, expressed as a linear multiplier
- * on the audio samples.
- * Note that this volume is specific to the player, and is separate from stream volume
- * used across the platform.<br>
- * A value of 0.0f indicates muting, a value of 1.0f is the nominal unattenuated and unamplified
- * gain. See {@link #getMaxPlayerVolume()} for the volume range supported by this player.
- * @param volume a value between 0.0f and {@link #getMaxPlayerVolume()}.
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- */
- // This is an asynchronous call.
- public @NonNull Object setPlayerVolume(float volume) {
- return addTask(new Task(CALL_COMPLETED_SET_PLAYER_VOLUME, false) {
- @Override
- void process() {
- mVolume = volume;
- native_setVolume(volume);
- }
- });
- }
-
- private native void native_setVolume(float volume);
-
- /**
- * Returns the current volume of this player.
- * Note that it does not take into account the associated stream volume.
- * @return the player volume.
- */
- public float getPlayerVolume() {
- return mVolume;
- }
-
- /**
- * @return the maximum volume that can be used in {@link #setPlayerVolume(float)}.
- */
- public float getMaxPlayerVolume() {
- return 1.0f;
- }
-
- /**
- * Insert a task in the command queue to help the client to identify whether a batch
- * of commands has been finished. When this command is processed, a notification
- * {@link EventCallback#onCommandLabelReached onCommandLabelReached} will be fired with the
- * given {@code label}.
- *
- * @see EventCallback#onCommandLabelReached
- *
- * @param label An application specific Object used to help to identify the completeness
- * of a batch of commands.
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- */
- // This is an asynchronous call.
- public @NonNull Object notifyWhenCommandLabelReached(@NonNull Object label) {
- return addTask(new Task(CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED, false) {
- @Override
- void process() {
- sendEvent(new EventNotifier() {
- @Override
- public void notify(EventCallback callback) {
- callback.onCommandLabelReached(
- MediaPlayer2.this, label);
- }
- });
- }
- });
- }
-
- /**
- * Sets the {@link SurfaceHolder} to use for displaying the video
- * portion of the media.
- *
- * Either a surface holder or surface must be set if a display or video sink
- * is needed. Not calling this method or {@link #setSurface(Surface)}
- * when playing back a video will result in only the audio track being played.
- * A null surface holder or surface will result in only the audio track being
- * played.
- *
- * @param sh the SurfaceHolder to use for video display
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- */
- public @NonNull Object setDisplay(@Nullable SurfaceHolder sh) {
- return addTask(new Task(CALL_COMPLETED_SET_DISPLAY, false) {
- @Override
- void process() {
- mSurfaceHolder = sh;
- Surface surface;
- if (sh != null) {
- surface = sh.getSurface();
- } else {
- surface = null;
- }
- native_setVideoSurface(surface);
- updateSurfaceScreenOn();
- }
- });
- }
-
- /**
- * Sets the {@link Surface} to be used as the sink for the video portion of
- * the media. Setting a
- * Surface will un-set any Surface or SurfaceHolder that was previously set.
- * A null surface will result in only the audio track being played.
- *
- * If the Surface sends frames to a {@link SurfaceTexture}, the timestamps
- * returned from {@link SurfaceTexture#getTimestamp()} will have an
- * unspecified zero point. These timestamps cannot be directly compared
- * between different media sources, different instances of the same media
- * source, or multiple runs of the same program. The timestamp is normally
- * monotonically increasing and is unaffected by time-of-day adjustments,
- * but it is reset when the position is set.
- *
- * @param surface The {@link Surface} to be used for the video portion of
- * the media.
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- */
- // This is an asynchronous call.
- public @NonNull Object setSurface(@Nullable Surface surface) {
- return addTask(new Task(CALL_COMPLETED_SET_SURFACE, false) {
- @Override
- void process() {
- if (mScreenOnWhilePlaying && surface != null) {
- Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for Surface");
- }
- mSurfaceHolder = null;
- native_setVideoSurface(surface);
- updateSurfaceScreenOn();
- }
- });
- }
-
- private native void native_setVideoSurface(Surface surface);
-
- /**
- * Set the low-level power management behavior for this MediaPlayer2. This
- * can be used when the MediaPlayer2 is not playing through a SurfaceHolder
- * set with {@link #setDisplay(SurfaceHolder)} and thus can use the
- * high-level {@link #setScreenOnWhilePlaying(boolean)} feature.
- *
- * <p>This function has the MediaPlayer2 access the low-level power manager
- * service to control the device's power usage while playing is occurring.
- * The parameter is a {@link android.os.PowerManager.WakeLock}.
- * Use of this method requires {@link android.Manifest.permission#WAKE_LOCK}
- * permission.
- * By default, no attempt is made to keep the device awake during playback.
- *
- * @param wakeLock the power wake lock used during playback.
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- * @see android.os.PowerManager
- */
- // This is an asynchronous call.
- public @NonNull Object setWakeLock(@NonNull PowerManager.WakeLock wakeLock) {
- return addTask(new Task(CALL_COMPLETED_SET_WAKE_LOCK, false) {
- @Override
- void process() {
- boolean wasHeld = false;
-
- if (mWakeLock != null) {
- if (mWakeLock.isHeld()) {
- wasHeld = true;
- mWakeLock.release();
- }
- }
-
- mWakeLock = wakeLock;
- if (mWakeLock != null) {
- mWakeLock.setReferenceCounted(false);
- if (wasHeld) {
- mWakeLock.acquire();
- }
- }
- }
- });
- }
-
- /**
- * Control whether we should use the attached SurfaceHolder to keep the
- * screen on while video playback is occurring. This is the preferred
- * method over {@link #setWakeLock} where possible, since it doesn't
- * require that the application have permission for low-level wake lock
- * access.
- *
- * @param screenOn Supply true to keep the screen on, false to allow it to turn off.
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- */
- // This is an asynchronous call.
- public @NonNull Object setScreenOnWhilePlaying(boolean screenOn) {
- return addTask(new Task(CALL_COMPLETED_SET_SCREEN_ON_WHILE_PLAYING, false) {
- @Override
- void process() {
- if (mScreenOnWhilePlaying != screenOn) {
- if (screenOn && mSurfaceHolder == null) {
- Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective"
- + " without a SurfaceHolder");
- }
- mScreenOnWhilePlaying = screenOn;
- updateSurfaceScreenOn();
- }
- }
- });
- }
-
- private void stayAwake(boolean awake) {
- if (mWakeLock != null) {
- if (awake && !mWakeLock.isHeld()) {
- mWakeLock.acquire();
- } else if (!awake && mWakeLock.isHeld()) {
- mWakeLock.release();
- }
- }
- mStayAwake = awake;
- updateSurfaceScreenOn();
- }
-
- private void updateSurfaceScreenOn() {
- if (mSurfaceHolder != null) {
- mSurfaceHolder.setKeepScreenOn(mScreenOnWhilePlaying && mStayAwake);
- }
- }
-
- /**
- * Cancels a pending command.
- *
- * @param token the command to be canceled. This is the returned Object when command is issued.
- * @return {@code false} if the task could not be cancelled; {@code true} otherwise.
- * @throws IllegalArgumentException if argument token is null.
- */
- // This is a synchronous call.
- public boolean cancelCommand(@NonNull Object token) {
- if (token == null) {
- throw new IllegalArgumentException("command token should not be null");
- }
- synchronized (mTaskLock) {
- return mPendingTasks.remove(token);
- }
- }
-
- /**
- * Discards all pending commands.
- */
- // This is a synchronous call.
- public void clearPendingCommands() {
- synchronized (mTaskLock) {
- mPendingTasks.clear();
- }
- }
-
- //--------------------------------------------------------------------------
- // Explicit Routing
- //--------------------
- private AudioDeviceInfo mPreferredDevice = null;
-
- /**
- * Specifies an audio device (via an {@link AudioDeviceInfo} object) to route
- * the output from this MediaPlayer2.
- * @param deviceInfo The {@link AudioDeviceInfo} specifying the audio sink or source.
- * If deviceInfo is null, default routing is restored.
- * @return true if succesful, false if the specified {@link AudioDeviceInfo} is non-null and
- * does not correspond to a valid audio device.
- */
- // This is a synchronous call.
- @Override
- public boolean setPreferredDevice(@Nullable AudioDeviceInfo deviceInfo) {
- boolean status = native_setPreferredDevice(deviceInfo);
- if (status) {
- synchronized (this) {
- mPreferredDevice = deviceInfo;
- }
- }
- return status;
- }
-
- private native boolean native_setPreferredDevice(AudioDeviceInfo device);
-
- /**
- * Returns the selected output specified by {@link #setPreferredDevice}. Note that this
- * is not guaranteed to correspond to the actual device being used for playback.
- */
- @Override
- public @Nullable AudioDeviceInfo getPreferredDevice() {
- synchronized (this) {
- return mPreferredDevice;
- }
- }
-
- /**
- * Returns an {@link AudioDeviceInfo} identifying the current routing of this MediaPlayer2
- * Note: The query is only valid if the MediaPlayer2 is currently playing.
- * If the player is not playing, the returned device can be null or correspond to previously
- * selected device when the player was last active.
- */
- @Override
- public @Nullable native AudioDeviceInfo getRoutedDevice();
-
- /**
- * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing
- * changes on this MediaPlayer2.
- * @param listener The {@link AudioRouting.OnRoutingChangedListener} interface to receive
- * notifications of rerouting events.
- * @param handler Specifies the {@link Handler} object for the thread on which to execute
- * the callback. If <code>null</code>, the handler on the main looper will be used.
- */
- // This is a synchronous call.
- @Override
- public void addOnRoutingChangedListener(@NonNull AudioRouting.OnRoutingChangedListener listener,
- @Nullable Handler handler) {
- if (listener == null) {
- throw new IllegalArgumentException("addOnRoutingChangedListener: listener is NULL");
- }
- RoutingDelegate routingDelegate = new RoutingDelegate(this, listener, handler);
- native_addDeviceCallback(routingDelegate);
- }
-
- private native void native_addDeviceCallback(RoutingDelegate rd);
-
- /**
- * Removes an {@link AudioRouting.OnRoutingChangedListener} which has been previously added
- * to receive rerouting notifications.
- * @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface
- * to remove.
- */
- // This is a synchronous call.
- @Override
- public void removeOnRoutingChangedListener(
- @NonNull AudioRouting.OnRoutingChangedListener listener) {
- if (listener == null) {
- throw new IllegalArgumentException("removeOnRoutingChangedListener: listener is NULL");
- }
- native_removeDeviceCallback(listener);
- }
-
- private native void native_removeDeviceCallback(
- AudioRouting.OnRoutingChangedListener listener);
-
- /**
- * Returns the size of the video.
- *
- * @return the size of the video. The width and height of size could be 0 if there is no video,
- * or the size has not been determined yet.
- * The {@code EventCallback} can be registered via
- * {@link #registerEventCallback(Executor, EventCallback)} to provide a
- * notification {@code EventCallback.onVideoSizeChanged} when the size
- * is available.
- */
- public @NonNull Size getVideoSize() {
- return mVideoSize;
- }
-
- /**
- * Return Metrics data about the current player.
- *
- * @return a {@link PersistableBundle} containing the set of attributes and values
- * available for the media being handled by this instance of MediaPlayer2
- * The attributes are descibed in {@link MetricsConstants}.
- *
- * Additional vendor-specific fields may also be present in the return value.
- */
- public @Nullable PersistableBundle getMetrics() {
- PersistableBundle bundle = native_getMetrics();
- return bundle;
- }
-
- private native PersistableBundle native_getMetrics();
-
- /**
- * Gets the current buffering management params used by the source component.
- * Calling it only after {@code setDataSource} has been called.
- * Each type of data source might have different set of default params.
- *
- * @return the current buffering management params used by the source component.
- * @throws IllegalStateException if the internal player engine has not been
- * initialized, or {@code setDataSource} has not been called.
- */
- // TODO: make it public when ready
- @NonNull
- native BufferingParams getBufferingParams();
-
- /**
- * Sets buffering management params.
- * The object sets its internal BufferingParams to the input, except that the input is
- * invalid or not supported.
- * Call it only after {@code setDataSource} has been called.
- * The input is a hint to MediaPlayer2.
- *
- * @param params the buffering management params.
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- */
- // TODO: make it public when ready
- // This is an asynchronous call.
- @NonNull Object setBufferingParams(@NonNull BufferingParams params) {
- return addTask(new Task(CALL_COMPLETED_SET_BUFFERING_PARAMS, false) {
- @Override
- void process() {
- Media2Utils.checkArgument(params != null, "the BufferingParams cannot be null");
- native_setBufferingParams(params);
- }
- });
- }
-
- private native void native_setBufferingParams(@NonNull BufferingParams params);
-
- /**
- * Sets playback rate using {@link PlaybackParams}. The object sets its internal
- * PlaybackParams to the input. This allows the object to resume at previous speed
- * when play() is called. Speed of zero is not allowed. Calling it does not change
- * the object state.
- *
- * @param params the playback params.
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- */
- // This is an asynchronous call.
- public @NonNull Object setPlaybackParams(@NonNull PlaybackParams params) {
- return addTask(new Task(CALL_COMPLETED_SET_PLAYBACK_PARAMS, false) {
- @Override
- void process() {
- Media2Utils.checkArgument(params != null, "the PlaybackParams cannot be null");
- native_setPlaybackParams(params);
- }
- });
- }
-
- private native void native_setPlaybackParams(@NonNull PlaybackParams params);
-
- /**
- * Gets the playback params, containing the current playback rate.
- *
- * @return the playback params.
- * @throws IllegalStateException if the internal player engine has not been initialized.
- */
- @NonNull
- public native PlaybackParams getPlaybackParams();
-
- /**
- * Sets A/V sync mode.
- *
- * @param params the A/V sync params to apply
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- */
- // This is an asynchronous call.
- public @NonNull Object setSyncParams(@NonNull SyncParams params) {
- return addTask(new Task(CALL_COMPLETED_SET_SYNC_PARAMS, false) {
- @Override
- void process() {
- Media2Utils.checkArgument(params != null, "the SyncParams cannot be null");
- native_setSyncParams(params);
- }
- });
- }
-
- private native void native_setSyncParams(@NonNull SyncParams params);
-
- /**
- * Gets the A/V sync mode.
- *
- * @return the A/V sync params
- * @throws IllegalStateException if the internal player engine has not been initialized.
- */
- @NonNull
- public native SyncParams getSyncParams();
-
- /**
- * Moves the media to specified time position.
- * Same as {@link #seekTo(long, int)} with {@code mode = SEEK_PREVIOUS_SYNC}.
- *
- * @param msec the offset in milliseconds from the start to seek to
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- */
- // This is an asynchronous call.
- public @NonNull Object seekTo(long msec) {
- return seekTo(msec, SEEK_PREVIOUS_SYNC /* mode */);
- }
-
- /**
- * Seek modes used in method seekTo(long, int) to move media position
- * to a specified location.
- *
- * Do not change these mode values without updating their counterparts
- * in include/media/IMediaSource.h!
- */
- /**
- * This mode is used with {@link #seekTo(long, int)} to move media position to
- * a sync (or key) frame associated with a data source that is located
- * right before or at the given time.
- *
- * @see #seekTo(long, int)
- */
- public static final int SEEK_PREVIOUS_SYNC = 0x00;
- /**
- * This mode is used with {@link #seekTo(long, int)} to move media position to
- * a sync (or key) frame associated with a data source that is located
- * right after or at the given time.
- *
- * @see #seekTo(long, int)
- */
- public static final int SEEK_NEXT_SYNC = 0x01;
- /**
- * This mode is used with {@link #seekTo(long, int)} to move media position to
- * a sync (or key) frame associated with a data source that is located
- * closest to (in time) or at the given time.
- *
- * @see #seekTo(long, int)
- */
- public static final int SEEK_CLOSEST_SYNC = 0x02;
- /**
- * This mode is used with {@link #seekTo(long, int)} to move media position to
- * a frame (not necessarily a key frame) associated with a data source that
- * is located closest to or at the given time.
- *
- * @see #seekTo(long, int)
- */
- public static final int SEEK_CLOSEST = 0x03;
-
- /** @hide */
- @IntDef(flag = false, prefix = "SEEK", value = {
- SEEK_PREVIOUS_SYNC,
- SEEK_NEXT_SYNC,
- SEEK_CLOSEST_SYNC,
- SEEK_CLOSEST,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface SeekMode {}
-
- /**
- * Moves the media to specified time position by considering the given mode.
- * <p>
- * When seekTo is finished, the user will be notified via
- * {@link EventCallback#onCallCompleted} with {@link #CALL_COMPLETED_SEEK_TO}.
- * There is at most one active seekTo processed at any time. If there is a to-be-completed
- * seekTo, new seekTo requests will be queued in such a way that only the last request
- * is kept. When current seekTo is completed, the queued request will be processed if
- * that request is different from just-finished seekTo operation, i.e., the requested
- * position or mode is different.
- *
- * @param msec the offset in milliseconds from the start to seek to.
- * When seeking to the given time position, there is no guarantee that the data source
- * has a frame located at the position. When this happens, a frame nearby will be rendered.
- * If msec is negative, time position zero will be used.
- * If msec is larger than duration, duration will be used.
- * @param mode the mode indicating where exactly to seek to.
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- */
- // This is an asynchronous call.
- public @NonNull Object seekTo(long msec, @SeekMode int mode) {
- return addTask(new Task(CALL_COMPLETED_SEEK_TO, true) {
- @Override
- void process() {
- if (mode < SEEK_PREVIOUS_SYNC || mode > SEEK_CLOSEST) {
- final String msg = "Illegal seek mode: " + mode;
- throw new IllegalArgumentException(msg);
- }
- // TODO: pass long to native, instead of truncating here.
- long posMs = msec;
- if (posMs > Integer.MAX_VALUE) {
- Log.w(TAG, "seekTo offset " + posMs + " is too large, cap to "
- + Integer.MAX_VALUE);
- posMs = Integer.MAX_VALUE;
- } else if (posMs < Integer.MIN_VALUE) {
- Log.w(TAG, "seekTo offset " + posMs + " is too small, cap to "
- + Integer.MIN_VALUE);
- posMs = Integer.MIN_VALUE;
- }
-
- synchronized (mTaskLock) {
- if (mIsPreviousCommandSeekTo
- && mPreviousSeekPos == posMs
- && mPreviousSeekMode == mode) {
- throw new CommandSkippedException(
- "same as previous seekTo");
- }
- }
-
- native_seekTo(posMs, mode);
-
- synchronized (mTaskLock) {
- mIsPreviousCommandSeekTo = true;
- mPreviousSeekPos = posMs;
- mPreviousSeekMode = mode;
- }
- }
- });
- }
-
- private native void native_seekTo(long msec, int mode);
-
- /**
- * Get current playback position as a {@link MediaTimestamp}.
- * <p>
- * The MediaTimestamp represents how the media time correlates to the system time in
- * a linear fashion using an anchor and a clock rate. During regular playback, the media
- * time moves fairly constantly (though the anchor frame may be rebased to a current
- * system time, the linear correlation stays steady). Therefore, this method does not
- * need to be called often.
- * <p>
- * To help users get current playback position, this method always anchors the timestamp
- * to the current {@link System#nanoTime system time}, so
- * {@link MediaTimestamp#getAnchorMediaTimeUs} can be used as current playback position.
- *
- * @return a MediaTimestamp object if a timestamp is available, or {@code null} if no timestamp
- * is available, e.g. because the media player has not been initialized.
- *
- * @see MediaTimestamp
- */
- @Nullable
- public MediaTimestamp getTimestamp() {
- try {
- // TODO: get the timestamp from native side
- return new MediaTimestamp(
- getCurrentPosition() * 1000L,
- System.nanoTime(),
- getState() == PLAYER_STATE_PLAYING ? getPlaybackParams().getSpeed() : 0.f);
- } catch (IllegalStateException e) {
- return null;
- }
- }
-
- /**
- * Checks whether the MediaPlayer2 is looping or non-looping.
- *
- * @return true if the MediaPlayer2 is currently looping, false otherwise
- */
- // This is a synchronous call.
- public native boolean isLooping();
-
- /**
- * Sets the audio session ID.
- *
- * @param sessionId the audio session ID.
- * The audio session ID is a system wide unique identifier for the audio stream played by
- * this MediaPlayer2 instance.
- * The primary use of the audio session ID is to associate audio effects to a particular
- * instance of MediaPlayer2: if an audio session ID is provided when creating an audio effect,
- * this effect will be applied only to the audio content of media players within the same
- * audio session and not to the output mix.
- * When created, a MediaPlayer2 instance automatically generates its own audio session ID.
- * However, it is possible to force this player to be part of an already existing audio session
- * by calling this method.
- * This method must be called when player is in {@link #PLAYER_STATE_IDLE} or
- * {@link #PLAYER_STATE_PREPARED} state in order to have sessionId take effect.
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- */
- // This is an asynchronous call.
- public @NonNull Object setAudioSessionId(int sessionId) {
- final AudioTrack dummyAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100,
- AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, 2,
- AudioTrack.MODE_STATIC, sessionId);
- return addTask(new Task(CALL_COMPLETED_SET_AUDIO_SESSION_ID, false) {
- @Override
- void process() {
- keepAudioSessionIdAlive(dummyAudioTrack);
- native_setAudioSessionId(sessionId);
- }
- });
- }
-
- private native void native_setAudioSessionId(int sessionId);
-
- /**
- * Returns the audio session ID.
- *
- * @return the audio session ID. {@see #setAudioSessionId(int)}
- * Note that the audio session ID is 0 only if a problem occured when the MediaPlayer2 was
- * contructed.
- */
- // This is a synchronous call.
- public native int getAudioSessionId();
-
- /**
- * Attaches an auxiliary effect to the player. A typical auxiliary effect is a reverberation
- * effect which can be applied on any sound source that directs a certain amount of its
- * energy to this effect. This amount is defined by setAuxEffectSendLevel().
- * See {@link #setAuxEffectSendLevel(float)}.
- * <p>After creating an auxiliary effect (e.g.
- * {@link android.media.audiofx.EnvironmentalReverb}), retrieve its ID with
- * {@link android.media.audiofx.AudioEffect#getId()} and use it when calling this method
- * to attach the player to the effect.
- * <p>To detach the effect from the player, call this method with a null effect id.
- * <p>This method must be called after one of the overloaded <code> setDataSource </code>
- * methods.
- * @param effectId system wide unique id of the effect to attach
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- */
- // This is an asynchronous call.
- public @NonNull Object attachAuxEffect(int effectId) {
- return addTask(new Task(CALL_COMPLETED_ATTACH_AUX_EFFECT, false) {
- @Override
- void process() {
- native_attachAuxEffect(effectId);
- }
- });
- }
-
- private native void native_attachAuxEffect(int effectId);
-
- /**
- * Sets the send level of the player to the attached auxiliary effect.
- * See {@link #attachAuxEffect(int)}. The level value range is 0 to 1.0.
- * <p>By default the send level is 0, so even if an effect is attached to the player
- * this method must be called for the effect to be applied.
- * <p>Note that the passed level value is a raw scalar. UI controls should be scaled
- * logarithmically: the gain applied by audio framework ranges from -72dB to 0dB,
- * so an appropriate conversion from linear UI input x to level is:
- * x == 0 -> level = 0
- * 0 < x <= R -> level = 10^(72*(x-R)/20/R)
- * @param level send level scalar
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- */
- // This is an asynchronous call.
- public @NonNull Object setAuxEffectSendLevel(float level) {
- return addTask(new Task(CALL_COMPLETED_SET_AUX_EFFECT_SEND_LEVEL, false) {
- @Override
- void process() {
- native_setAuxEffectSendLevel(level);
- }
- });
- }
-
- private native void native_setAuxEffectSendLevel(float level);
-
- private static native void native_stream_event_onTearDown(
- long nativeCallbackPtr, long userDataPtr);
- private static native void native_stream_event_onStreamPresentationEnd(
- long nativeCallbackPtr, long userDataPtr);
- private static native void native_stream_event_onStreamDataRequest(
- long jAudioTrackPtr, long nativeCallbackPtr, long userDataPtr);
-
- /* Do not change these values (starting with INVOKE_ID) without updating
- * their counterparts in include/media/mediaplayer2.h!
- */
- private static final int INVOKE_ID_GET_TRACK_INFO = 1;
- private static final int INVOKE_ID_ADD_EXTERNAL_SOURCE = 2;
- private static final int INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3;
- private static final int INVOKE_ID_SELECT_TRACK = 4;
- private static final int INVOKE_ID_DESELECT_TRACK = 5;
- private static final int INVOKE_ID_GET_SELECTED_TRACK = 7;
-
- /**
- * Invoke a generic method on the native player using opaque protocol
- * buffer message for the request and reply. Both payloads' format is a
- * convention between the java caller and the native player.
- *
- * @param msg PlayerMessage for the extension.
- *
- * @return PlayerMessage with the data returned by the
- * native player.
- */
- private PlayerMessage invoke(PlayerMessage msg) {
- byte[] ret = native_invoke(msg.toByteArray());
- if (ret == null) {
- return null;
- }
- try {
- return PlayerMessage.parseFrom(ret);
- } catch (InvalidProtocolBufferException e) {
- return null;
- }
- }
-
- private native byte[] native_invoke(byte[] request);
-
- /**
- * @hide
- */
- @IntDef(flag = false, prefix = "MEDIA_TRACK_TYPE", value = {
- TrackInfo.MEDIA_TRACK_TYPE_VIDEO,
- TrackInfo.MEDIA_TRACK_TYPE_AUDIO,
- TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface TrackType {}
-
- /**
- * Class for MediaPlayer2 to return each audio/video/subtitle track's metadata.
- *
- * @see MediaPlayer2#getTrackInfo
- */
- public static class TrackInfo {
- /**
- * Gets the track type.
- * @return TrackType which indicates if the track is video, audio, timed text.
- */
- public int getTrackType() {
- return mTrackType;
- }
-
- /**
- * Gets the language code of the track.
- * @return a language code in either way of ISO-639-1 or ISO-639-2.
- * When the language is unknown or could not be determined,
- * ISO-639-2 language code, "und", is returned.
- */
- public @NonNull String getLanguage() {
- String language = mFormat.getString(MediaFormat.KEY_LANGUAGE);
- return language == null ? "und" : language;
- }
-
- /**
- * Gets the {@link MediaFormat} of the track. If the format is
- * unknown or could not be determined, null is returned.
- */
- public @Nullable MediaFormat getFormat() {
- if (mTrackType == MEDIA_TRACK_TYPE_TIMEDTEXT
- || mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
- return mFormat;
- }
- return null;
- }
-
- public static final int MEDIA_TRACK_TYPE_UNKNOWN = 0;
- public static final int MEDIA_TRACK_TYPE_VIDEO = 1;
- public static final int MEDIA_TRACK_TYPE_AUDIO = 2;
-
- /** @hide */
- public static final int MEDIA_TRACK_TYPE_TIMEDTEXT = 3;
-
- public static final int MEDIA_TRACK_TYPE_SUBTITLE = 4;
- public static final int MEDIA_TRACK_TYPE_METADATA = 5;
-
- final int mId;
- final int mTrackType;
- final MediaFormat mFormat;
-
- static TrackInfo create(int idx, Iterator<Value> in) {
- int trackType = in.next().getInt32Value();
- // TODO: build the full MediaFormat; currently we are using createSubtitleFormat
- // even for audio/video tracks, meaning we only set the mime and language.
- String mime = in.next().getStringValue();
- String language = in.next().getStringValue();
- MediaFormat format = MediaFormat.createSubtitleFormat(mime, language);
-
- if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) {
- format.setInteger(MediaFormat.KEY_IS_AUTOSELECT, in.next().getInt32Value());
- format.setInteger(MediaFormat.KEY_IS_DEFAULT, in.next().getInt32Value());
- format.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, in.next().getInt32Value());
- }
- return new TrackInfo(idx, trackType, format);
- }
-
- /** @hide */
- TrackInfo(int id, int type, MediaFormat format) {
- mId = id;
- mTrackType = type;
- mFormat = format;
- }
-
- @Override
- public String toString() {
- StringBuilder out = new StringBuilder(128);
- out.append(getClass().getName());
- out.append('{');
- switch (mTrackType) {
- case MEDIA_TRACK_TYPE_VIDEO:
- out.append("VIDEO");
- break;
- case MEDIA_TRACK_TYPE_AUDIO:
- out.append("AUDIO");
- break;
- case MEDIA_TRACK_TYPE_TIMEDTEXT:
- out.append("TIMEDTEXT");
- break;
- case MEDIA_TRACK_TYPE_SUBTITLE:
- out.append("SUBTITLE");
- break;
- default:
- out.append("UNKNOWN");
- break;
- }
- out.append(", " + mFormat.toString());
- out.append("}");
- return out.toString();
- }
- };
-
- /**
- * Returns a List of track information of current data source.
- * Same as {@link #getTrackInfo(DataSourceDesc)} with
- * {@code dsd = getCurrentDataSource()}.
- *
- * @return List of track info. The total number of tracks is the array length.
- * Must be called again if an external timed text source has been added after
- * addTimedTextSource method is called.
- * @throws IllegalStateException if it is called in an invalid state.
- * @throws NullPointerException if current data source is null
- */
- public @NonNull List<TrackInfo> getTrackInfo() {
- return getTrackInfo(getCurrentDataSource());
- }
-
- /**
- * Returns a List of track information.
- *
- * @param dsd the descriptor of data source of which you want to get track info
- * @return List of track info. The total number of tracks is the array length.
- * Must be called again if an external timed text source has been added after
- * addTimedTextSource method is called.
- * @throws IllegalStateException if it is called in an invalid state.
- * @throws NullPointerException if dsd is null
- */
- public @NonNull List<TrackInfo> getTrackInfo(@NonNull DataSourceDesc dsd) {
- if (dsd == null) {
- throw new NullPointerException("non-null dsd is expected");
- }
- SourceInfo sourceInfo = getSourceInfo(dsd);
- if (sourceInfo == null) {
- return new ArrayList<TrackInfo>(0);
- }
-
- TrackInfo[] trackInfo = getInbandTrackInfo(sourceInfo);
- return (trackInfo != null ? Arrays.asList(trackInfo) : new ArrayList<TrackInfo>(0));
- }
-
- private TrackInfo[] getInbandTrackInfo(SourceInfo sourceInfo) throws IllegalStateException {
- PlayerMessage request = PlayerMessage.newBuilder()
- .addValues(Value.newBuilder().setInt32Value(INVOKE_ID_GET_TRACK_INFO))
- .addValues(Value.newBuilder().setInt64Value(sourceInfo.mId))
- .build();
- PlayerMessage response = invoke(request);
- if (response == null) {
- return null;
- }
- Iterator<Value> in = response.getValuesList().iterator();
- int size = in.next().getInt32Value();
- if (size == 0) {
- return null;
- }
- TrackInfo[] trackInfo = new TrackInfo[size];
- for (int i = 0; i < size; ++i) {
- trackInfo[i] = TrackInfo.create(i, in);
- }
- return trackInfo;
- }
-
- /**
- * Returns the index of the audio, video, or subtitle track currently selected for playback.
- * The return value is an index into the array returned by {@link #getTrackInfo}, and can
- * be used in calls to {@link #selectTrack(TrackInfo)} or {@link #deselectTrack(TrackInfo)}.
- * Same as {@link #getSelectedTrack(DataSourceDesc, int)} with
- * {@code dsd = getCurrentDataSource()}.
- *
- * @param trackType should be one of {@link TrackInfo#MEDIA_TRACK_TYPE_VIDEO},
- * {@link TrackInfo#MEDIA_TRACK_TYPE_AUDIO}, or
- * {@link TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE}
- * @return metadata corresponding to the audio, video, or subtitle track currently selected for
- * playback; {@code null} is returned when there is no selected track for {@code trackType} or
- * when {@code trackType} is not one of audio, video, or subtitle.
- * @throws IllegalStateException if called after {@link #close()}
- * @throws NullPointerException if current data source is null
- *
- * @see #getTrackInfo()
- * @see #selectTrack(TrackInfo)
- * @see #deselectTrack(TrackInfo)
- */
- @Nullable
- public TrackInfo getSelectedTrack(@TrackType int trackType) {
- return getSelectedTrack(getCurrentDataSource(), trackType);
- }
-
- /**
- * Returns the index of the audio, video, or subtitle track currently selected for playback.
- * The return value is an index into the array returned by {@link #getTrackInfo}, and can
- * be used in calls to {@link #selectTrack(DataSourceDesc, TrackInfo)} or
- * {@link #deselectTrack(DataSourceDesc, TrackInfo)}.
- *
- * @param dsd the descriptor of data source of which you want to get selected track
- * @param trackType should be one of {@link TrackInfo#MEDIA_TRACK_TYPE_VIDEO},
- * {@link TrackInfo#MEDIA_TRACK_TYPE_AUDIO}, or
- * {@link TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE}
- * @return metadata corresponding to the audio, video, or subtitle track currently selected for
- * playback; {@code null} is returned when there is no selected track for {@code trackType} or
- * when {@code trackType} is not one of audio, video, or subtitle.
- * @throws IllegalStateException if called after {@link #close()}
- * @throws NullPointerException if dsd is null
- *
- * @see #getTrackInfo(DataSourceDesc)
- * @see #selectTrack(DataSourceDesc, TrackInfo)
- * @see #deselectTrack(DataSourceDesc, TrackInfo)
- */
- @Nullable
- public TrackInfo getSelectedTrack(@NonNull DataSourceDesc dsd, @TrackType int trackType) {
- if (dsd == null) {
- throw new NullPointerException("non-null dsd is expected");
- }
- SourceInfo sourceInfo = getSourceInfo(dsd);
- if (sourceInfo == null) {
- return null;
- }
-
- PlayerMessage request = PlayerMessage.newBuilder()
- .addValues(Value.newBuilder().setInt32Value(INVOKE_ID_GET_SELECTED_TRACK))
- .addValues(Value.newBuilder().setInt64Value(sourceInfo.mId))
- .addValues(Value.newBuilder().setInt32Value(trackType))
- .build();
- PlayerMessage response = invoke(request);
- if (response == null) {
- return null;
- }
- // TODO: return full TrackInfo data from native player instead of index
- final int idx = response.getValues(0).getInt32Value();
- final List<TrackInfo> trackInfos = getTrackInfo(dsd);
- return trackInfos.isEmpty() ? null : trackInfos.get(idx);
- }
-
- /**
- * Selects a track of current data source.
- * Same as {@link #selectTrack(DataSourceDesc, TrackInfo)} with
- * {@code dsd = getCurrentDataSource()}.
- *
- * @param trackInfo metadata corresponding to the track to be selected. A {@code trackInfo}
- * object can be obtained from {@link #getTrackInfo()}.
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- *
- * This is an asynchronous call.
- *
- * @see MediaPlayer2#getTrackInfo()
- */
- @NonNull
- public Object selectTrack(@NonNull TrackInfo trackInfo) {
- return selectTrack(getCurrentDataSource(), trackInfo);
- }
-
- /**
- * Selects a track.
- * <p>
- * If a MediaPlayer2 is in invalid state, it throws an IllegalStateException exception.
- * If a MediaPlayer2 is in <em>Started</em> state, the selected track is presented immediately.
- * If a MediaPlayer2 is not in Started state, it just marks the track to be played.
- * </p>
- * <p>
- * In any valid state, if it is called multiple times on the same type of track (ie. Video,
- * Audio, Timed Text), the most recent one will be chosen.
- * </p>
- * <p>
- * The first audio and video tracks are selected by default if available, even though
- * this method is not called. However, no timed text track will be selected until
- * this function is called.
- * </p>
- * <p>
- * Currently, only timed text tracks or audio tracks can be selected via this method.
- * In addition, the support for selecting an audio track at runtime is pretty limited
- * in that an audio track can only be selected in the <em>Prepared</em> state.
- * </p>
- * @param dsd the descriptor of data source of which you want to select track
- * @param trackInfo metadata corresponding to the track to be selected. A {@code trackInfo}
- * object can be obtained from {@link #getTrackInfo()}.
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- *
- * This is an asynchronous call.
- *
- * @see MediaPlayer2#getTrackInfo(DataSourceDesc)
- */
- @NonNull
- public Object selectTrack(@NonNull DataSourceDesc dsd, @NonNull TrackInfo trackInfo) {
- return addTask(new Task(CALL_COMPLETED_SELECT_TRACK, false) {
- @Override
- void process() {
- selectOrDeselectTrack(dsd, trackInfo.mId, true /* select */);
- }
- });
- }
-
- /**
- * Deselect a track of current data source.
- * Same as {@link #deselectTrack(DataSourceDesc, TrackInfo)} with
- * {@code dsd = getCurrentDataSource()}.
- *
- * @param trackInfo metadata corresponding to the track to be selected. A {@code trackInfo}
- * object can be obtained from {@link #getTrackInfo()}.
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- *
- * This is an asynchronous call.
- *
- * @see MediaPlayer2#getTrackInfo()
- */
- @NonNull
- public Object deselectTrack(@NonNull TrackInfo trackInfo) {
- return deselectTrack(getCurrentDataSource(), trackInfo);
- }
-
- /**
- * Deselect a track.
- * <p>
- * Currently, the track must be a timed text track and no audio or video tracks can be
- * deselected. If the timed text track identified by index has not been
- * selected before, it throws an exception.
- * </p>
- * @param dsd the descriptor of data source of which you want to deselect track
- * @param trackInfo metadata corresponding to the track to be selected. A {@code trackInfo}
- * object can be obtained from {@link #getTrackInfo()}.
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- *
- * This is an asynchronous call.
- *
- * @see MediaPlayer2#getTrackInfo(DataSourceDesc)
- */
- @NonNull
- public Object deselectTrack(@NonNull DataSourceDesc dsd, @NonNull TrackInfo trackInfo) {
- return addTask(new Task(CALL_COMPLETED_DESELECT_TRACK, false) {
- @Override
- void process() {
- selectOrDeselectTrack(dsd, trackInfo.mId, false /* select */);
- }
- });
- }
-
- private void selectOrDeselectTrack(@NonNull DataSourceDesc dsd, int index, boolean select) {
- if (dsd == null) {
- throw new IllegalArgumentException("non-null dsd is expected");
- }
- SourceInfo sourceInfo = getSourceInfo(dsd);
- if (sourceInfo == null) {
- return;
- }
-
- PlayerMessage request = PlayerMessage.newBuilder()
- .addValues(Value.newBuilder().setInt32Value(
- select ? INVOKE_ID_SELECT_TRACK : INVOKE_ID_DESELECT_TRACK))
- .addValues(Value.newBuilder().setInt64Value(sourceInfo.mId))
- .addValues(Value.newBuilder().setInt32Value(index))
- .build();
- invoke(request);
- }
-
- /* Do not change these values without updating their counterparts
- * in include/media/mediaplayer2.h!
- */
- private static final int MEDIA_NOP = 0; // interface test message
- private static final int MEDIA_PREPARED = 1;
- private static final int MEDIA_PLAYBACK_COMPLETE = 2;
- private static final int MEDIA_BUFFERING_UPDATE = 3;
- private static final int MEDIA_SEEK_COMPLETE = 4;
- private static final int MEDIA_SET_VIDEO_SIZE = 5;
- private static final int MEDIA_STARTED = 6;
- private static final int MEDIA_PAUSED = 7;
- private static final int MEDIA_STOPPED = 8;
- private static final int MEDIA_SKIPPED = 9;
- private static final int MEDIA_DRM_PREPARED = 10;
- private static final int MEDIA_NOTIFY_TIME = 98;
- private static final int MEDIA_TIMED_TEXT = 99;
- private static final int MEDIA_ERROR = 100;
- private static final int MEDIA_INFO = 200;
- private static final int MEDIA_SUBTITLE_DATA = 201;
- private static final int MEDIA_META_DATA = 202;
- private static final int MEDIA_DRM_INFO = 210;
-
- private class TaskHandler extends Handler {
- private MediaPlayer2 mMediaPlayer;
-
- TaskHandler(MediaPlayer2 mp, Looper looper) {
- super(looper);
- mMediaPlayer = mp;
- }
-
- @Override
- public void handleMessage(Message msg) {
- handleMessage(msg, 0);
- }
-
- public void handleMessage(Message msg, long srcId) {
- if (mMediaPlayer.mNativeContext == 0) {
- Log.w(TAG, "mediaplayer2 went away with unhandled events");
- return;
- }
- final int what = msg.arg1;
- final int extra = msg.arg2;
-
- final SourceInfo sourceInfo = getSourceInfo(srcId);
- if (sourceInfo == null) {
- return;
- }
- final DataSourceDesc dsd = sourceInfo.mDSD;
-
- switch(msg.what) {
- case MEDIA_PREPARED:
- case MEDIA_DRM_PREPARED:
- {
- sourceInfo.mPrepareBarrier--;
- if (sourceInfo.mPrepareBarrier > 0) {
- break;
- } else if (sourceInfo.mPrepareBarrier < 0) {
- Log.w(TAG, "duplicated (drm) prepared events");
- break;
- }
-
- if (dsd != null) {
- sendEvent(new EventNotifier() {
- @Override
- public void notify(EventCallback callback) {
- callback.onInfo(
- mMediaPlayer, dsd, MEDIA_INFO_PREPARED, 0);
- }
- });
- }
-
- synchronized (mSrcLock) {
- SourceInfo nextSourceInfo = mNextSourceInfos.peek();
- Log.i(TAG, "MEDIA_PREPARED: srcId=" + srcId
- + ", curSrc=" + mCurrentSourceInfo
- + ", nextSrc=" + nextSourceInfo);
-
- if (isCurrentSource(srcId)) {
- prepareNextDataSource();
- } else if (isNextSource(srcId)) {
- nextSourceInfo.mStateAsNextSource = NEXT_SOURCE_STATE_PREPARED;
- if (nextSourceInfo.mPlayPendingAsNextSource) {
- playNextDataSource();
- }
- }
- }
-
- synchronized (mTaskLock) {
- if (mCurrentTask != null
- && mCurrentTask.mMediaCallType == CALL_COMPLETED_PREPARE
- && mCurrentTask.mDSD == dsd
- && mCurrentTask.mNeedToWaitForEventToComplete) {
- mCurrentTask.sendCompleteNotification(CALL_STATUS_NO_ERROR);
- mCurrentTask = null;
- processPendingTask_l();
- }
- }
- return;
- }
-
- case MEDIA_DRM_INFO:
- {
- if (msg.obj == null) {
- Log.w(TAG, "MEDIA_DRM_INFO msg.obj=NULL");
- } else if (msg.obj instanceof byte[]) {
- // The PlayerMessage was parsed already in postEventFromNative
-
- final DrmInfo drmInfo;
- synchronized (sourceInfo) {
- if (sourceInfo.mDrmInfo != null) {
- drmInfo = sourceInfo.mDrmInfo.makeCopy();
- } else {
- drmInfo = null;
- }
- }
-
- // notifying the client outside the lock
- DrmPreparationInfo drmPrepareInfo = null;
- if (drmInfo != null) {
- try {
- drmPrepareInfo = sendDrmEventWait(
- new DrmEventNotifier<DrmPreparationInfo>() {
- @Override
- public DrmPreparationInfo notifyWait(
- DrmEventCallback callback) {
- return callback.onDrmInfo(mMediaPlayer, dsd,
- drmInfo);
- }
- });
- } catch (InterruptedException | ExecutionException
- | TimeoutException e) {
- Log.w(TAG, "Exception while waiting for DrmPreparationInfo", e);
- }
- }
- if (sourceInfo.mDrmHandle.setPreparationInfo(drmPrepareInfo)) {
- sourceInfo.mPrepareBarrier++;
- final Task prepareDrmTask;
- prepareDrmTask = newPrepareDrmTask(dsd, drmPrepareInfo.mUUID);
- mTaskHandler.post(new Runnable() {
- @Override
- public void run() {
- // Run as simple Runnable, not Task
- try {
- prepareDrmTask.process();
- } catch (NoDrmSchemeException | IOException e) {
- final String errMsg;
- errMsg = "Unexpected Exception during prepareDrm";
- throw new RuntimeException(errMsg, e);
- }
- }
- });
- } else {
- Log.w(TAG, "No valid DrmPreparationInfo set");
- }
- } else {
- Log.w(TAG, "MEDIA_DRM_INFO msg.obj of unexpected type " + msg.obj);
- }
- return;
- }
-
- case MEDIA_PLAYBACK_COMPLETE:
- {
- if (isCurrentSource(srcId)) {
- sendEvent(new EventNotifier() {
- @Override
- public void notify(EventCallback callback) {
- callback.onInfo(
- mMediaPlayer, dsd, MEDIA_INFO_DATA_SOURCE_END, 0);
- }
- });
- stayAwake(false);
-
- synchronized (mSrcLock) {
- SourceInfo nextSourceInfo = mNextSourceInfos.peek();
- if (nextSourceInfo != null) {
- nextSourceInfo.mPlayPendingAsNextSource = true;
- }
- Log.i(TAG, "MEDIA_PLAYBACK_COMPLETE: srcId=" + srcId
- + ", curSrc=" + mCurrentSourceInfo
- + ", nextSrc=" + nextSourceInfo);
- }
-
- playNextDataSource();
- }
-
- return;
- }
-
- case MEDIA_STOPPED:
- case MEDIA_STARTED:
- case MEDIA_PAUSED:
- case MEDIA_SKIPPED:
- case MEDIA_NOTIFY_TIME:
- {
- // Do nothing. The client should have enough information with
- // {@link EventCallback#onMediaTimeDiscontinuity}.
- break;
- }
-
- case MEDIA_BUFFERING_UPDATE:
- {
- final int percent = msg.arg1;
- sendEvent(new EventNotifier() {
- @Override
- public void notify(EventCallback callback) {
- callback.onInfo(
- mMediaPlayer, dsd, MEDIA_INFO_BUFFERING_UPDATE, percent);
- }
- });
-
- SourceInfo src = getSourceInfo(srcId);
- if (src != null) {
- src.mBufferedPercentage.set(percent);
- }
-
- return;
- }
-
- case MEDIA_SEEK_COMPLETE:
- {
- synchronized (mTaskLock) {
- if (!mPendingTasks.isEmpty()
- && mPendingTasks.get(0).mMediaCallType != CALL_COMPLETED_SEEK_TO
- && getState() == PLAYER_STATE_PLAYING) {
- mIsPreviousCommandSeekTo = false;
- }
-
- if (mCurrentTask != null
- && mCurrentTask.mMediaCallType == CALL_COMPLETED_SEEK_TO
- && mCurrentTask.mNeedToWaitForEventToComplete) {
- mCurrentTask.sendCompleteNotification(CALL_STATUS_NO_ERROR);
- mCurrentTask = null;
- processPendingTask_l();
- }
- }
- return;
- }
-
- case MEDIA_SET_VIDEO_SIZE:
- {
- final int width = msg.arg1;
- final int height = msg.arg2;
-
- mVideoSize = new Size(width, height);
- sendEvent(new EventNotifier() {
- @Override
- public void notify(EventCallback callback) {
- callback.onVideoSizeChanged(
- mMediaPlayer, dsd, mVideoSize);
- }
- });
- return;
- }
-
- case MEDIA_ERROR:
- {
- Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
- sendEvent(new EventNotifier() {
- @Override
- public void notify(EventCallback callback) {
- callback.onError(
- mMediaPlayer, dsd, what, extra);
- }
- });
- sendEvent(new EventNotifier() {
- @Override
- public void notify(EventCallback callback) {
- callback.onInfo(
- mMediaPlayer, dsd, MEDIA_INFO_DATA_SOURCE_END, 0);
- }
- });
- stayAwake(false);
- return;
- }
-
- case MEDIA_INFO:
- {
- switch (msg.arg1) {
- case MEDIA_INFO_VIDEO_TRACK_LAGGING:
- Log.i(TAG, "Info (" + msg.arg1 + "," + msg.arg2 + ")");
- break;
- }
-
- sendEvent(new EventNotifier() {
- @Override
- public void notify(EventCallback callback) {
- callback.onInfo(
- mMediaPlayer, dsd, what, extra);
- }
- });
-
- if (msg.arg1 == MEDIA_INFO_DATA_SOURCE_START) {
- if (isCurrentSource(srcId)) {
- prepareNextDataSource();
- }
- }
-
- // No real default action so far.
- return;
- }
-
- case MEDIA_TIMED_TEXT:
- {
- final TimedText text;
- if (msg.obj instanceof byte[]) {
- PlayerMessage playerMsg;
- try {
- playerMsg = PlayerMessage.parseFrom((byte[]) msg.obj);
- } catch (InvalidProtocolBufferException e) {
- Log.w(TAG, "Failed to parse timed text.", e);
- return;
- }
- text = TimedTextUtil.parsePlayerMessage(playerMsg);
- } else {
- text = null;
- }
-
- sendEvent(new EventNotifier() {
- @Override
- public void notify(EventCallback callback) {
- callback.onTimedText(
- mMediaPlayer, dsd, text);
- }
- });
- return;
- }
-
- case MEDIA_SUBTITLE_DATA:
- {
- if (msg.obj instanceof byte[]) {
- PlayerMessage playerMsg;
- try {
- playerMsg = PlayerMessage.parseFrom((byte[]) msg.obj);
- } catch (InvalidProtocolBufferException e) {
- Log.w(TAG, "Failed to parse subtitle data.", e);
- return;
- }
- Iterator<Value> in = playerMsg.getValuesList().iterator();
- final int trackIndex = in.next().getInt32Value();
- TrackInfo trackInfo = getTrackInfo(dsd).get(trackIndex);
- final long startTimeUs = in.next().getInt64Value();
- final long durationTimeUs = in.next().getInt64Value();
- final byte[] subData = in.next().getBytesValue().toByteArray();
- SubtitleData data = new SubtitleData(trackInfo,
- startTimeUs, durationTimeUs, subData);
- sendEvent(new EventNotifier() {
- @Override
- public void notify(EventCallback callback) {
- callback.onSubtitleData(
- mMediaPlayer, dsd, data);
- }
- });
- }
- return;
- }
-
- case MEDIA_META_DATA:
- {
- final TimedMetaData data;
- if (msg.obj instanceof byte[]) {
- PlayerMessage playerMsg;
- try {
- playerMsg = PlayerMessage.parseFrom((byte[]) msg.obj);
- } catch (InvalidProtocolBufferException e) {
- Log.w(TAG, "Failed to parse timed meta data.", e);
- return;
- }
- Iterator<Value> in = playerMsg.getValuesList().iterator();
- data = new TimedMetaData(
- in.next().getInt64Value(), // timestampUs
- in.next().getBytesValue().toByteArray()); // metaData
- } else {
- data = null;
- }
-
- sendEvent(new EventNotifier() {
- @Override
- public void notify(EventCallback callback) {
- callback.onTimedMetaDataAvailable(
- mMediaPlayer, dsd, data);
- }
- });
- return;
- }
-
- case MEDIA_NOP: // interface test message - ignore
- {
- break;
- }
-
- default:
- {
- Log.e(TAG, "Unknown message type " + msg.what);
- return;
- }
- }
- }
- }
-
- /*
- * Called from native code when an interesting event happens. This method
- * just uses the TaskHandler system to post the event back to the main app thread.
- * We use a weak reference to the original MediaPlayer2 object so that the native
- * code is safe from the object disappearing from underneath it. (This is
- * the cookie passed to native_setup().)
- */
- private static void postEventFromNative(Object mediaplayer2Ref, long srcId,
- int what, int arg1, int arg2, byte[] obj) {
- final MediaPlayer2 mp = (MediaPlayer2) ((WeakReference) mediaplayer2Ref).get();
- if (mp == null) {
- return;
- }
-
- final SourceInfo sourceInfo = mp.getSourceInfo(srcId);
- switch (what) {
- case MEDIA_DRM_INFO:
- // We need to derive mDrmInfo before prepare() returns so processing it here
- // before the notification is sent to TaskHandler below. TaskHandler runs in the
- // notification looper so its handleMessage might process the event after prepare()
- // has returned.
- Log.v(TAG, "postEventFromNative MEDIA_DRM_INFO");
- if (obj != null && sourceInfo != null) {
- PlayerMessage playerMsg;
- try {
- playerMsg = PlayerMessage.parseFrom(obj);
- } catch (InvalidProtocolBufferException e) {
- Log.w(TAG, "MEDIA_DRM_INFO failed to parse msg.obj " + obj);
- break;
- }
- DrmInfo drmInfo = DrmInfo.create(playerMsg);
- synchronized (sourceInfo) {
- sourceInfo.mDrmInfo = drmInfo;
- }
- } else {
- Log.w(TAG, "MEDIA_DRM_INFO sourceInfo " + sourceInfo
- + " msg.obj of unexpected type " + obj);
- }
- break;
-
- case MEDIA_PREPARED:
- // By this time, we've learned about DrmInfo's presence or absence. This is meant
- // mainly for prepare() use case. For prepare(), this still can run to a race
- // condition b/c MediaPlayerNative releases the prepare() lock before calling notify
- // so we also set mDrmInfoResolved in prepare().
- if (sourceInfo != null) {
- synchronized (sourceInfo) {
- sourceInfo.mDrmInfoResolved = true;
- }
- }
- break;
- }
-
- if (mp.mTaskHandler != null) {
- Message m = mp.mTaskHandler.obtainMessage(what, arg1, arg2, obj);
-
- mp.mTaskHandler.post(new Runnable() {
- @Override
- public void run() {
- mp.mTaskHandler.handleMessage(m, srcId);
- }
- });
- }
- }
-
- /**
- * Class encapsulating subtitle data, as received through the
- * {@link EventCallback#onSubtitleData} interface.
- * <p>
- * A {@link SubtitleData} object includes:
- * <ul>
- * <li> track metadadta in a {@link TrackInfo} object</li>
- * <li> the start time (in microseconds) of the data</li>
- * <li> the duration (in microseconds) of the data</li>
- * <li> the actual data.</li>
- * </ul>
- * The data is stored in a byte-array, and is encoded in one of the supported in-band
- * subtitle formats. The subtitle encoding is determined by the MIME type of the
- * {@link TrackInfo} of the subtitle track, one of
- * {@link MediaFormat#MIMETYPE_TEXT_CEA_608}, {@link MediaFormat#MIMETYPE_TEXT_CEA_708},
- * {@link MediaFormat#MIMETYPE_TEXT_VTT}.
- */
- public static final class SubtitleData {
-
- private TrackInfo mTrackInfo;
- private long mStartTimeUs;
- private long mDurationUs;
- private byte[] mData;
-
- private SubtitleData(TrackInfo trackInfo, long startTimeUs, long durationUs, byte[] data) {
- mTrackInfo = trackInfo;
- mStartTimeUs = startTimeUs;
- mDurationUs = durationUs;
- mData = (data != null ? data : new byte[0]);
- }
-
- /**
- * @return metadata of track which contains this subtitle data
- */
- @NonNull
- public TrackInfo getTrackInfo() {
- return mTrackInfo;
- }
-
- /**
- * @return media time at which the subtitle should start to be displayed in microseconds
- */
- public long getStartTimeUs() {
- return mStartTimeUs;
- }
-
- /**
- * @return the duration in microsecond during which the subtitle should be displayed
- */
- public long getDurationUs() {
- return mDurationUs;
- }
-
- /**
- * Returns the encoded data for the subtitle content.
- * Encoding format depends on the subtitle type, refer to
- * <a href="https://en.wikipedia.org/wiki/CEA-708">CEA 708</a>,
- * <a href="https://en.wikipedia.org/wiki/EIA-608">CEA/EIA 608</a> and
- * <a href="https://www.w3.org/TR/webvtt1/">WebVTT</a>, defined by the MIME type
- * of the subtitle track.
- * @return the encoded subtitle data
- */
- @NonNull
- public byte[] getData() {
- return mData;
- }
- }
-
- /**
- * Interface definition for callbacks to be invoked when the player has the corresponding
- * events.
- */
- public static class EventCallback {
- /**
- * Called to indicate the video size
- *
- * The video size (width and height) could be 0 if there was no video,
- * or the value was not determined yet.
- *
- * @param mp the MediaPlayer2 associated with this callback
- * @param dsd the DataSourceDesc of this data source
- * @param size the size of the video
- */
- public void onVideoSizeChanged(
- @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, @NonNull Size size) { }
-
- /**
- * Called to indicate an avaliable timed text
- *
- * @param mp the MediaPlayer2 associated with this callback
- * @param dsd the DataSourceDesc of this data source
- * @param text the timed text sample which contains the text
- * needed to be displayed and the display format.
- * @hide
- */
- public void onTimedText(
- @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, @NonNull TimedText text) { }
-
- /**
- * Called to indicate avaliable timed metadata
- * <p>
- * This method will be called as timed metadata is extracted from the media,
- * in the same order as it occurs in the media. The timing of this event is
- * not controlled by the associated timestamp.
- * <p>
- * Currently only HTTP live streaming data URI's embedded with timed ID3 tags generates
- * {@link TimedMetaData}.
- *
- * @see MediaPlayer2#selectTrack
- * @see MediaPlayer2.OnTimedMetaDataAvailableListener
- * @see TimedMetaData
- *
- * @param mp the MediaPlayer2 associated with this callback
- * @param dsd the DataSourceDesc of this data source
- * @param data the timed metadata sample associated with this event
- */
- public void onTimedMetaDataAvailable(
- @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
- @NonNull TimedMetaData data) { }
-
- /**
- * Called to indicate an error.
- *
- * @param mp the MediaPlayer2 the error pertains to
- * @param dsd the DataSourceDesc of this data source
- * @param what the type of error that has occurred.
- * @param extra an extra code, specific to the error. Typically
- * implementation dependent.
- */
- public void onError(
- @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
- @MediaError int what, int extra) { }
-
- /**
- * Called to indicate an info or a warning.
- *
- * @param mp the MediaPlayer2 the info pertains to.
- * @param dsd the DataSourceDesc of this data source
- * @param what the type of info or warning.
- * @param extra an extra code, specific to the info. Typically
- * implementation dependent.
- */
- public void onInfo(
- @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
- @MediaInfo int what, int extra) { }
-
- /**
- * Called to acknowledge an API call.
- *
- * @param mp the MediaPlayer2 the call was made on.
- * @param dsd the DataSourceDesc of this data source
- * @param what the enum for the API call.
- * @param status the returned status code for the call.
- */
- public void onCallCompleted(
- @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd, @CallCompleted int what,
- @CallStatus int status) { }
-
- /**
- * Called to indicate media clock has changed.
- *
- * @param mp the MediaPlayer2 the media time pertains to.
- * @param dsd the DataSourceDesc of this data source
- * @param timestamp the new media clock.
- */
- public void onMediaTimeDiscontinuity(
- @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
- @NonNull MediaTimestamp timestamp) { }
-
- /**
- * Called to indicate {@link #notifyWhenCommandLabelReached(Object)} has been processed.
- *
- * @param mp the MediaPlayer2 {@link #notifyWhenCommandLabelReached(Object)} was called on.
- * @param label the application specific Object given by
- * {@link #notifyWhenCommandLabelReached(Object)}.
- */
- public void onCommandLabelReached(@NonNull MediaPlayer2 mp, @NonNull Object label) { }
-
- /**
- * Called when when a player subtitle track has new subtitle data available.
- * @param mp the player that reports the new subtitle data
- * @param dsd the DataSourceDesc of this data source
- * @param data the subtitle data
- */
- public void onSubtitleData(
- @NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
- @NonNull SubtitleData data) { }
- }
-
- private final Object mEventCbLock = new Object();
- private ArrayList<Pair<Executor, EventCallback>> mEventCallbackRecords =
- new ArrayList<Pair<Executor, EventCallback>>();
-
- /**
- * Registers the callback to be invoked for various events covered by {@link EventCallback}.
- *
- * @param executor the executor through which the callback should be invoked
- * @param eventCallback the callback that will be run
- */
- // This is a synchronous call.
- public void registerEventCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull EventCallback eventCallback) {
- if (eventCallback == null) {
- throw new IllegalArgumentException("Illegal null EventCallback");
- }
- if (executor == null) {
- throw new IllegalArgumentException(
- "Illegal null Executor for the EventCallback");
- }
- synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- if (cb.first == executor && cb.second == eventCallback) {
- Log.w(TAG, "The callback has been registered before.");
- return;
- }
- }
- mEventCallbackRecords.add(new Pair(executor, eventCallback));
- }
- }
-
- /**
- * Unregisters the {@link EventCallback}.
- *
- * @param eventCallback the callback to be unregistered
- */
- // This is a synchronous call.
- public void unregisterEventCallback(@NonNull EventCallback eventCallback) {
- synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- if (cb.second == eventCallback) {
- mEventCallbackRecords.remove(cb);
- }
- }
- }
- }
-
- private void sendEvent(final EventNotifier notifier) {
- synchronized (mEventCbLock) {
- try {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> notifier.notify(cb.second));
- }
- } catch (RejectedExecutionException e) {
- // The executor has been shut down.
- Log.w(TAG, "The executor has been shut down. Ignoring event.");
- }
- }
- }
-
- private void sendDrmEvent(final DrmEventNotifier notifier) {
- synchronized (mDrmEventCallbackLock) {
- try {
- Pair<Executor, DrmEventCallback> cb = mDrmEventCallback;
- if (cb != null) {
- cb.first.execute(() -> notifier.notify(cb.second));
- }
- } catch (RejectedExecutionException e) {
- // The executor has been shut down.
- Log.w(TAG, "The executor has been shut down. Ignoring drm event.");
- }
- }
- }
-
- private <T> T sendDrmEventWait(final DrmEventNotifier<T> notifier)
- throws InterruptedException, ExecutionException, TimeoutException {
- return sendDrmEventWait(notifier, 0);
- }
-
- private <T> T sendDrmEventWait(final DrmEventNotifier<T> notifier, final long timeoutMs)
- throws InterruptedException, ExecutionException, TimeoutException {
- synchronized (mDrmEventCallbackLock) {
- Pair<Executor, DrmEventCallback> cb = mDrmEventCallback;
- if (cb != null) {
- CompletableFuture<T> ret = new CompletableFuture<>();
- cb.first.execute(() -> ret.complete(notifier.notifyWait(cb.second)));
- return timeoutMs <= 0 ? ret.get() : ret.get(timeoutMs, TimeUnit.MILLISECONDS);
- }
- }
- return null;
- }
-
- private interface EventNotifier {
- void notify(EventCallback callback);
- }
-
- private interface DrmEventNotifier<T> {
- default void notify(DrmEventCallback callback) { }
- default T notifyWait(DrmEventCallback callback) {
- return null;
- }
- }
-
- /* Do not change these values without updating their counterparts
- * in include/media/MediaPlayer2Types.h!
- */
- /** Unspecified media player error.
- * @see EventCallback#onError
- */
- public static final int MEDIA_ERROR_UNKNOWN = 1;
-
- /**
- * The video is streamed and its container is not valid for progressive
- * playback i.e the video's index (e.g moov atom) is not at the start of the
- * file.
- * @see EventCallback#onError
- */
- public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;
-
- /** File or network related operation errors. */
- public static final int MEDIA_ERROR_IO = -1004;
- /** Bitstream is not conforming to the related coding standard or file spec. */
- public static final int MEDIA_ERROR_MALFORMED = -1007;
- /** Bitstream is conforming to the related coding standard or file spec, but
- * the media framework does not support the feature. */
- public static final int MEDIA_ERROR_UNSUPPORTED = -1010;
- /** Some operation takes too long to complete, usually more than 3-5 seconds. */
- public static final int MEDIA_ERROR_TIMED_OUT = -110;
-
- /** Unspecified low-level system error. This value originated from UNKNOWN_ERROR in
- * system/core/include/utils/Errors.h
- * @see EventCallback#onError
- * @hide
- */
- public static final int MEDIA_ERROR_SYSTEM = -2147483648;
-
- /**
- * @hide
- */
- @IntDef(flag = false, prefix = "MEDIA_ERROR", value = {
- MEDIA_ERROR_UNKNOWN,
- MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK,
- MEDIA_ERROR_IO,
- MEDIA_ERROR_MALFORMED,
- MEDIA_ERROR_UNSUPPORTED,
- MEDIA_ERROR_TIMED_OUT,
- MEDIA_ERROR_SYSTEM
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface MediaError {}
-
- /* Do not change these values without updating their counterparts
- * in include/media/MediaPlayer2Types.h!
- */
- /** Unspecified media player info.
- * @see EventCallback#onInfo
- */
- public static final int MEDIA_INFO_UNKNOWN = 1;
-
- /** The player just started the playback of this datas source.
- * @see EventCallback#onInfo
- */
- public static final int MEDIA_INFO_DATA_SOURCE_START = 2;
-
- /** The player just pushed the very first video frame for rendering.
- * @see EventCallback#onInfo
- */
- public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3;
-
- /** The player just rendered the very first audio sample.
- * @see EventCallback#onInfo
- */
- public static final int MEDIA_INFO_AUDIO_RENDERING_START = 4;
-
- /** The player just completed the playback of this data source.
- * @see EventCallback#onInfo
- */
- public static final int MEDIA_INFO_DATA_SOURCE_END = 5;
-
- /** The player just completed the playback of all data sources set by {@link #setDataSource},
- * {@link #setNextDataSource} and {@link #setNextDataSources}.
- * @see EventCallback#onInfo
- */
- public static final int MEDIA_INFO_DATA_SOURCE_LIST_END = 6;
-
- /** The player just completed an iteration of playback loop. This event is sent only when
- * looping is enabled by {@link #loopCurrent}.
- * @see EventCallback#onInfo
- */
- public static final int MEDIA_INFO_DATA_SOURCE_REPEAT = 7;
-
- /** The player just prepared a data source.
- * @see EventCallback#onInfo
- */
- public static final int MEDIA_INFO_PREPARED = 100;
-
- /** The video is too complex for the decoder: it can't decode frames fast
- * enough. Possibly only the audio plays fine at this stage.
- * @see EventCallback#onInfo
- */
- public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700;
-
- /** MediaPlayer2 is temporarily pausing playback internally in order to
- * buffer more data.
- * @see EventCallback#onInfo
- */
- public static final int MEDIA_INFO_BUFFERING_START = 701;
-
- /** MediaPlayer2 is resuming playback after filling buffers.
- * @see EventCallback#onInfo
- */
- public static final int MEDIA_INFO_BUFFERING_END = 702;
-
- /** Estimated network bandwidth information (kbps) is available; currently this event fires
- * simultaneously as {@link #MEDIA_INFO_BUFFERING_START} and {@link #MEDIA_INFO_BUFFERING_END}
- * when playing network files.
- * @see EventCallback#onInfo
- * @hide
- */
- public static final int MEDIA_INFO_NETWORK_BANDWIDTH = 703;
-
- /**
- * Update status in buffering a media source received through progressive downloading.
- * The received buffering percentage indicates how much of the content has been buffered
- * or played. For example a buffering update of 80 percent when half the content
- * has already been played indicates that the next 30 percent of the
- * content to play has been buffered.
- *
- * The {@code extra} parameter in {@code EventCallback.onInfo} is the
- * percentage (0-100) of the content that has been buffered or played thus far.
- * @see EventCallback#onInfo
- */
- public static final int MEDIA_INFO_BUFFERING_UPDATE = 704;
-
- /** Bad interleaving means that a media has been improperly interleaved or
- * not interleaved at all, e.g has all the video samples first then all the
- * audio ones. Video is playing but a lot of disk seeks may be happening.
- * @see EventCallback#onInfo
- */
- public static final int MEDIA_INFO_BAD_INTERLEAVING = 800;
-
- /** The media cannot be seeked (e.g live stream)
- * @see EventCallback#onInfo
- */
- public static final int MEDIA_INFO_NOT_SEEKABLE = 801;
-
- /** A new set of metadata is available.
- * @see EventCallback#onInfo
- */
- public static final int MEDIA_INFO_METADATA_UPDATE = 802;
-
- /** Informs that audio is not playing. Note that playback of the video
- * is not interrupted.
- * @see EventCallback#onInfo
- */
- public static final int MEDIA_INFO_AUDIO_NOT_PLAYING = 804;
-
- /** Informs that video is not playing. Note that playback of the audio
- * is not interrupted.
- * @see EventCallback#onInfo
- */
- public static final int MEDIA_INFO_VIDEO_NOT_PLAYING = 805;
-
- /** Failed to handle timed text track properly.
- * @see EventCallback#onInfo
- *
- * {@hide}
- */
- public static final int MEDIA_INFO_TIMED_TEXT_ERROR = 900;
-
- /** Subtitle track was not supported by the media framework.
- * @see EventCallback#onInfo
- */
- public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901;
-
- /** Reading the subtitle track takes too long.
- * @see EventCallback#onInfo
- */
- public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902;
-
- /**
- * @hide
- */
- @IntDef(flag = false, prefix = "MEDIA_INFO", value = {
- MEDIA_INFO_UNKNOWN,
- MEDIA_INFO_DATA_SOURCE_START,
- MEDIA_INFO_VIDEO_RENDERING_START,
- MEDIA_INFO_AUDIO_RENDERING_START,
- MEDIA_INFO_DATA_SOURCE_END,
- MEDIA_INFO_DATA_SOURCE_LIST_END,
- MEDIA_INFO_PREPARED,
- MEDIA_INFO_VIDEO_TRACK_LAGGING,
- MEDIA_INFO_BUFFERING_START,
- MEDIA_INFO_BUFFERING_END,
- MEDIA_INFO_NETWORK_BANDWIDTH,
- MEDIA_INFO_BUFFERING_UPDATE,
- MEDIA_INFO_BAD_INTERLEAVING,
- MEDIA_INFO_NOT_SEEKABLE,
- MEDIA_INFO_METADATA_UPDATE,
- MEDIA_INFO_AUDIO_NOT_PLAYING,
- MEDIA_INFO_VIDEO_NOT_PLAYING,
- MEDIA_INFO_TIMED_TEXT_ERROR,
- MEDIA_INFO_UNSUPPORTED_SUBTITLE,
- MEDIA_INFO_SUBTITLE_TIMED_OUT
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface MediaInfo {}
-
- //--------------------------------------------------------------------------
- /** The player just completed a call {@link #attachAuxEffect}.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_ATTACH_AUX_EFFECT = 1;
-
- /** The player just completed a call {@link #deselectTrack}.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_DESELECT_TRACK = 2;
-
- /** The player just completed a call {@link #loopCurrent}.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_LOOP_CURRENT = 3;
-
- /** The player just completed a call {@link #pause}.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_PAUSE = 4;
-
- /** The player just completed a call {@link #play}.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_PLAY = 5;
-
- /** The player just completed a call {@link #prepare}.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_PREPARE = 6;
-
- /** The player just completed a call {@link #seekTo}.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_SEEK_TO = 14;
-
- /** The player just completed a call {@link #selectTrack}.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_SELECT_TRACK = 15;
-
- /** The player just completed a call {@link #setAudioAttributes}.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_SET_AUDIO_ATTRIBUTES = 16;
-
- /** The player just completed a call {@link #setAudioSessionId}.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_SET_AUDIO_SESSION_ID = 17;
-
- /** The player just completed a call {@link #setAuxEffectSendLevel}.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_SET_AUX_EFFECT_SEND_LEVEL = 18;
-
- /** The player just completed a call {@link #setDataSource}.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_SET_DATA_SOURCE = 19;
-
- /** The player just completed a call {@link #setNextDataSource}.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_SET_NEXT_DATA_SOURCE = 22;
-
- /** The player just completed a call {@link #setNextDataSources}.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_SET_NEXT_DATA_SOURCES = 23;
-
- /** The player just completed a call {@link #setPlaybackParams}.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_SET_PLAYBACK_PARAMS = 24;
-
- /** The player just completed a call {@link #setPlayerVolume}.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_SET_PLAYER_VOLUME = 26;
-
- /** The player just completed a call {@link #setSurface}.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_SET_SURFACE = 27;
-
- /** The player just completed a call {@link #setSyncParams}.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_SET_SYNC_PARAMS = 28;
-
- /** The player just completed a call {@link #skipToNext}.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_SKIP_TO_NEXT = 29;
-
- /** The player just completed a call {@link #clearNextDataSources}.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES = 30;
-
- /** The player just completed a call {@link #setBufferingParams}.
- * @see EventCallback#onCallCompleted
- * @hide
- */
- public static final int CALL_COMPLETED_SET_BUFFERING_PARAMS = 31;
-
- /** The player just completed a call {@link #setDisplay}.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_SET_DISPLAY = 33;
-
- /** The player just completed a call {@link #setWakeLock}.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_SET_WAKE_LOCK = 34;
-
- /** The player just completed a call {@link #setScreenOnWhilePlaying}.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_COMPLETED_SET_SCREEN_ON_WHILE_PLAYING = 35;
-
- /**
- * The start of the methods which have separate call complete callback.
- * @hide
- */
- public static final int SEPARATE_CALL_COMPLETED_CALLBACK_START = 1000;
-
- /** The player just completed a call {@link #notifyWhenCommandLabelReached}.
- * @see EventCallback#onCommandLabelReached
- * @hide
- */
- public static final int CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED =
- SEPARATE_CALL_COMPLETED_CALLBACK_START;
-
- /** The player just completed a call {@link #prepareDrm}.
- * @see DrmEventCallback#onDrmPrepared
- * @hide
- */
- public static final int CALL_COMPLETED_PREPARE_DRM =
- SEPARATE_CALL_COMPLETED_CALLBACK_START + 1;
-
- /**
- * @hide
- */
- @IntDef(flag = false, prefix = "CALL_COMPLETED", value = {
- CALL_COMPLETED_ATTACH_AUX_EFFECT,
- CALL_COMPLETED_DESELECT_TRACK,
- CALL_COMPLETED_LOOP_CURRENT,
- CALL_COMPLETED_PAUSE,
- CALL_COMPLETED_PLAY,
- CALL_COMPLETED_PREPARE,
- CALL_COMPLETED_SEEK_TO,
- CALL_COMPLETED_SELECT_TRACK,
- CALL_COMPLETED_SET_AUDIO_ATTRIBUTES,
- CALL_COMPLETED_SET_AUDIO_SESSION_ID,
- CALL_COMPLETED_SET_AUX_EFFECT_SEND_LEVEL,
- CALL_COMPLETED_SET_DATA_SOURCE,
- CALL_COMPLETED_SET_NEXT_DATA_SOURCE,
- CALL_COMPLETED_SET_NEXT_DATA_SOURCES,
- CALL_COMPLETED_SET_PLAYBACK_PARAMS,
- CALL_COMPLETED_SET_PLAYER_VOLUME,
- CALL_COMPLETED_SET_SURFACE,
- CALL_COMPLETED_SET_SYNC_PARAMS,
- CALL_COMPLETED_SKIP_TO_NEXT,
- CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES,
- CALL_COMPLETED_SET_BUFFERING_PARAMS,
- CALL_COMPLETED_SET_DISPLAY,
- CALL_COMPLETED_SET_WAKE_LOCK,
- CALL_COMPLETED_SET_SCREEN_ON_WHILE_PLAYING,
- CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED,
- CALL_COMPLETED_PREPARE_DRM,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface CallCompleted {}
-
- /** Status code represents that call is completed without an error.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_STATUS_NO_ERROR = 0;
-
- /** Status code represents that call is ended with an unknown error.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_STATUS_ERROR_UNKNOWN = Integer.MIN_VALUE;
-
- /** Status code represents that the player is not in valid state for the operation.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_STATUS_INVALID_OPERATION = 1;
-
- /** Status code represents that the argument is illegal.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_STATUS_BAD_VALUE = 2;
-
- /** Status code represents that the operation is not allowed.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_STATUS_PERMISSION_DENIED = 3;
-
- /** Status code represents a file or network related operation error.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_STATUS_ERROR_IO = 4;
-
- /** Status code represents that the call has been skipped. For example, a {@link #seekTo}
- * request may be skipped if it is followed by another {@link #seekTo} request.
- * @see EventCallback#onCallCompleted
- */
- public static final int CALL_STATUS_SKIPPED = 5;
-
- /** Status code represents that DRM operation is called before preparing a DRM scheme through
- * {@code prepareDrm}.
- * @see EventCallback#onCallCompleted
- */
- // TODO: change @code to @link when DRM is unhidden
- public static final int CALL_STATUS_NO_DRM_SCHEME = 6;
-
- /**
- * @hide
- */
- @IntDef(flag = false, prefix = "CALL_STATUS", value = {
- CALL_STATUS_NO_ERROR,
- CALL_STATUS_ERROR_UNKNOWN,
- CALL_STATUS_INVALID_OPERATION,
- CALL_STATUS_BAD_VALUE,
- CALL_STATUS_PERMISSION_DENIED,
- CALL_STATUS_ERROR_IO,
- CALL_STATUS_SKIPPED,
- CALL_STATUS_NO_DRM_SCHEME})
- @Retention(RetentionPolicy.SOURCE)
- public @interface CallStatus {}
-
- // Modular DRM begin
-
- /**
- * An immutable structure per {@link DataSourceDesc} with settings required to initiate a DRM
- * protected playback session.
- *
- * @see DrmPreparationInfo.Builder
- */
- public static final class DrmPreparationInfo {
-
- /**
- * Mutable builder to create a {@link MediaPlayer2.DrmPreparationInfo} object.
- *
- * {@link Builder#Builder(UUID) UUID} must not be null; {@link #setKeyType keyType}
- * must be one of {@link MediaDrm#KEY_TYPE_STREAMING} or {@link MediaDrm#KEY_TYPE_OFFLINE}.
- * <p>
- * When {@link #setKeyType keyType} is {@link MediaDrm#KEY_TYPE_STREAMING},
- * {@link #setInitData(byte[]) initData} and {@link #setMimeType(String) mimeType}
- * must not be null; When {@link #setKeyType keyType} is {@link MediaDrm#KEY_TYPE_OFFLINE},
- * {@link #setKeySetId(byte[]) keySetId} must not be null.
- */
- public static final class Builder {
-
- private final UUID mUUID;
- private byte[] mKeySetId;
- private byte[] mInitData;
- private String mMimeType;
- private int mKeyType;
- private Map<String, String> mOptionalParameters;
-
- /**
- * @param uuid UUID of the crypto scheme selected to decrypt content. An UUID can be
- * retrieved from the source listening to {@link DrmEventCallback#onDrmInfo}.
- */
- public Builder(@NonNull UUID uuid) {
- this.mUUID = uuid;
- }
-
- /**
- * Set identifier of a persisted offline key obtained from
- * {@link MediaPlayer2.DrmEventCallback#onDrmPrepared}.
- *
- * A {@code keySetId} can be used to restore persisted offline keys into a new playback
- * session of a DRM protected data source. When {@code keySetId} is set,
- * {@code initData}, {@code mimeType}, {@code keyType}, {@code optionalParameters} are
- * ignored.
- *
- * @param keySetId identifier of a persisted offline key
- * @return this
- */
- public @NonNull Builder setKeySetId(@Nullable byte[] keySetId) {
- this.mKeySetId = keySetId;
- return this;
- }
-
- /**
- * Set container-specific DRM initialization data. Its meaning is interpreted based on
- * {@code mimeType}. For example, it could contain the content ID, key ID or other data
- * obtained from the content metadata that is required to generate a
- * {@link MediaDrm.KeyRequest}.
- *
- * @param initData container-specific DRM initialization data
- * @return this
- */
- public @NonNull Builder setInitData(@Nullable byte[] initData) {
- this.mInitData = initData;
- return this;
- }
-
- /**
- * Set mime type of the content
- *
- * @param mimeType mime type to the content
- * @return this
- */
- public @NonNull Builder setMimeType(@Nullable String mimeType) {
- this.mMimeType = mimeType;
- return this;
- }
-
- /**
- * Set type of the key request. The request may be to acquire keys
- * for streaming, {@link MediaDrm#KEY_TYPE_STREAMING}, or for offline content,
- * {@link MediaDrm#KEY_TYPE_OFFLINE}. Releasing previously acquired keys
- * ({@link MediaDrm#KEY_TYPE_RELEASE}) is not allowed.
- *
- * @param keyType type of the key request
- * @return this
- */
- public @NonNull Builder setKeyType(@MediaPlayer2.MediaDrmKeyType int keyType) {
- this.mKeyType = keyType;
- return this;
- }
-
- /**
- * Set optional parameters to be included in a {@link MediaDrm.KeyRequest} message sent
- * to the license server.
- *
- * @param optionalParameters optional parameters to be included in a key request
- * @return this
- */
- public @NonNull Builder setOptionalParameters(
- @Nullable Map<String, String> optionalParameters) {
- this.mOptionalParameters = optionalParameters;
- return this;
- }
-
- /**
- * @return an immutable {@link DrmPreparationInfo} based on settings of this builder
- */
- @NonNull
- public DrmPreparationInfo build() {
- final DrmPreparationInfo info = new DrmPreparationInfo(mUUID, mKeySetId, mInitData,
- mMimeType, mKeyType, mOptionalParameters);
- if (!info.isValid()) {
- throw new IllegalArgumentException("invalid DrmPreparationInfo");
- }
- return info;
- }
-
- }
-
- private final UUID mUUID;
- private final byte[] mKeySetId;
- private final byte[] mInitData;
- private final String mMimeType;
- private final int mKeyType;
- private final Map<String, String> mOptionalParameters;
-
- private DrmPreparationInfo(UUID mUUID, byte[] mKeySetId, byte[] mInitData, String mMimeType,
- int mKeyType, Map<String, String> optionalParameters) {
- this.mUUID = mUUID;
- this.mKeySetId = mKeySetId;
- this.mInitData = mInitData;
- this.mMimeType = mMimeType;
- this.mKeyType = mKeyType;
- this.mOptionalParameters = optionalParameters;
- }
-
- boolean isValid() {
- if (mUUID == null) {
- return false;
- }
- if (mKeySetId != null) {
- // offline restore case
- return true;
- }
- if (mInitData != null && mMimeType != null) {
- // new streaming license case
- return true;
- }
- return false;
- }
-
- /**
- * @return UUID of the crypto scheme selected to decrypt content.
- */
- @NonNull
- public UUID getUuid() {
- return mUUID;
- }
-
- /**
- * @return identifier of the persisted offline key.
- */
- @Nullable
- public byte[] getKeySetId() {
- return mKeySetId;
- }
-
- /**
- * @return container-specific DRM initialization data.
- */
- @Nullable
- public byte[] getInitData() {
- return mInitData;
- }
-
- /**
- * @return mime type of the content
- */
- @Nullable
- public String getMimeType() {
- return mMimeType;
- }
-
- /**
- * @return type of the key request.
- */
- @MediaPlayer2.MediaDrmKeyType
- public int getKeyType() {
- return mKeyType;
- }
-
- /**
- * @return optional parameters to be included in the {@link MediaDrm.KeyRequest}.
- */
- @Nullable
- public Map<String, String> getOptionalParameters() {
- return mOptionalParameters;
- }
- }
-
- /**
- * Interface definition for callbacks to be invoked when the player has the corresponding
- * DRM events.
- */
- public static abstract class DrmEventCallback {
-
- /**
- * Called to indicate DRM info is available. Return a {@link DrmPreparationInfo} object that
- * bundles DRM initialization parameters.
- *
- * @param mp the {@code MediaPlayer2} associated with this callback
- * @param dsd the {@link DataSourceDesc} of this data source
- * @param drmInfo DRM info of the source including PSSH, and subset of crypto schemes
- * supported by this device
- * @return a {@link DrmPreparationInfo} object to initialize DRM playback, or null to skip
- * DRM initialization
- */
- @Nullable
- public abstract DrmPreparationInfo onDrmInfo(@NonNull MediaPlayer2 mp,
- @NonNull DataSourceDesc dsd, @NonNull DrmInfo drmInfo);
-
- /**
- * Called to give the app the opportunity to configure DRM before the session is created.
- *
- * This facilitates configuration of the properties, like 'securityLevel', which
- * has to be set after DRM scheme creation but before the DRM session is opened.
- *
- * The only allowed DRM calls in this listener are
- * {@link MediaDrm#getPropertyString(String)},
- * {@link MediaDrm#getPropertyByteArray(String)},
- * {@link MediaDrm#setPropertyString(String, String)},
- * {@link MediaDrm#setPropertyByteArray(String, byte[])},
- * {@link MediaDrm#setOnExpirationUpdateListener},
- * and {@link MediaDrm#setOnKeyStatusChangeListener}.
- *
- * @param mp the {@code MediaPlayer2} associated with this callback
- * @param dsd the {@link DataSourceDesc} of this data source
- * @param drm handle to get/set DRM properties and listeners for this data source
- */
- public void onDrmConfig(@NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
- @NonNull MediaDrm drm) { }
-
- /**
- * Called to indicate the DRM session for {@code dsd} is ready for key request/response
- *
- * @param mp the {@code MediaPlayer2} associated with this callback
- * @param dsd the {@link DataSourceDesc} of this data source
- * @param request a {@link MediaDrm.KeyRequest} prepared using the
- * {@link DrmPreparationInfo} returned from
- * {@link #onDrmInfo(MediaPlayer2, DataSourceDesc, DrmInfo)}
- * @return the response to {@code request} (from license server); returning {@code null} or
- * throwing an {@link RuntimeException} from this callback would trigger an
- * {@link EventCallback#onError}.
- */
- @NonNull
- public abstract byte[] onDrmKeyRequest(@NonNull MediaPlayer2 mp,
- @NonNull DataSourceDesc dsd, @NonNull MediaDrm.KeyRequest request);
-
- /**
- * Called to notify the client that {@code mp} is ready to decrypt DRM protected data source
- * {@code dsd} or if there is an error during DRM preparation
- *
- * @param mp the {@code MediaPlayer2} associated with this callback
- * @param dsd the {@link DataSourceDesc} of this data source
- * @param status the result of DRM preparation.
- * @param keySetId optional identifier that can be used to restore DRM playback initiated
- * with a {@link MediaDrm#KEY_TYPE_OFFLINE} key request.
- *
- * @see DrmPreparationInfo.Builder#setKeySetId(byte[])
- */
- public void onDrmPrepared(@NonNull MediaPlayer2 mp, @NonNull DataSourceDesc dsd,
- @PrepareDrmStatusCode int status, @Nullable byte[] keySetId) { }
-
- }
-
- private final Object mDrmEventCallbackLock = new Object();
- private Pair<Executor, DrmEventCallback> mDrmEventCallback;
-
- /**
- * Registers the callback to be invoked for various DRM events.
- *
- * This is a synchronous call.
- *
- * @param eventCallback the callback that will be run
- * @param executor the executor through which the callback should be invoked
- */
- public void setDrmEventCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull DrmEventCallback eventCallback) {
- if (eventCallback == null) {
- throw new IllegalArgumentException("Illegal null EventCallback");
- }
- if (executor == null) {
- throw new IllegalArgumentException(
- "Illegal null Executor for the EventCallback");
- }
- synchronized (mDrmEventCallbackLock) {
- mDrmEventCallback = new Pair<Executor, DrmEventCallback>(executor, eventCallback);
- }
- }
-
- /**
- * Clear the {@link DrmEventCallback}.
- *
- * This is a synchronous call.
- */
- public void clearDrmEventCallback() {
- synchronized (mDrmEventCallbackLock) {
- mDrmEventCallback = null;
- }
- }
-
- /**
- * A status code for {@link DrmEventCallback#onDrmPrepared} listener.
- * <p>
- *
- * DRM preparation has succeeded.
- */
- public static final int PREPARE_DRM_STATUS_SUCCESS = 0;
-
- /**
- * A status code for {@link DrmEventCallback#onDrmPrepared} listener.
- * <p>
- *
- * The device required DRM provisioning but couldn't reach the provisioning server.
- */
- public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1;
-
- /**
- * A status code for {@link DrmEventCallback#onDrmPrepared} listener.
- * <p>
- *
- * The device required DRM provisioning but the provisioning server denied the request.
- */
- public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2;
-
- /**
- * A status code for {@link DrmEventCallback#onDrmPrepared} listener.
- * <p>
- *
- * The DRM preparation has failed .
- */
- public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3;
-
- /**
- * A status code for {@link DrmEventCallback#onDrmPrepared} listener.
- * <p>
- *
- * The crypto scheme UUID is not supported by the device.
- */
- public static final int PREPARE_DRM_STATUS_UNSUPPORTED_SCHEME = 4;
-
- /**
- * A status code for {@link DrmEventCallback#onDrmPrepared} listener.
- * <p>
- *
- * The hardware resources are not available, due to being in use.
- */
- public static final int PREPARE_DRM_STATUS_RESOURCE_BUSY = 5;
-
- /**
- * A status code for {@link DrmEventCallback#onDrmPrepared} listener.
- * <p>
- *
- * Restoring persisted offline keys failed.
- */
- public static final int PREPARE_DRM_STATUS_RESTORE_ERROR = 6;
-
- /**
- * A status code for {@link DrmEventCallback#onDrmPrepared} listener.
- * <p>
- *
- * Error during key request/response exchange with license server.
- */
- public static final int PREPARE_DRM_STATUS_KEY_EXCHANGE_ERROR = 7;
-
- /** @hide */
- @IntDef(flag = false, prefix = "PREPARE_DRM_STATUS", value = {
- PREPARE_DRM_STATUS_SUCCESS,
- PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR,
- PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR,
- PREPARE_DRM_STATUS_PREPARATION_ERROR,
- PREPARE_DRM_STATUS_UNSUPPORTED_SCHEME,
- PREPARE_DRM_STATUS_RESOURCE_BUSY,
- PREPARE_DRM_STATUS_RESTORE_ERROR,
- PREPARE_DRM_STATUS_KEY_EXCHANGE_ERROR,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface PrepareDrmStatusCode {}
-
- /** @hide */
- @IntDef({
- MediaDrm.KEY_TYPE_STREAMING,
- MediaDrm.KEY_TYPE_OFFLINE,
- MediaDrm.KEY_TYPE_RELEASE,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface MediaDrmKeyType {}
-
- /** @hide */
- @StringDef({
- MediaDrm.PROPERTY_VENDOR,
- MediaDrm.PROPERTY_VERSION,
- MediaDrm.PROPERTY_DESCRIPTION,
- MediaDrm.PROPERTY_ALGORITHMS,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface MediaDrmStringProperty {}
-
- /**
- * Retrieves the DRM Info associated with the given source
- *
- * @param dsd The DRM protected data source
- *
- * @throws IllegalStateException if called before being prepared
- * @hide
- */
- @TestApi
- public DrmInfo getDrmInfo(@NonNull DataSourceDesc dsd) {
- final SourceInfo sourceInfo = getSourceInfo(dsd);
- if (sourceInfo != null) {
- DrmInfo drmInfo = null;
-
- // there is not much point if the app calls getDrmInfo within an OnDrmInfoListener;
- // regardless below returns drmInfo anyway instead of raising an exception
- synchronized (sourceInfo) {
- if (!sourceInfo.mDrmInfoResolved && sourceInfo.mDrmInfo == null) {
- final String msg = "The Player has not been prepared yet";
- Log.v(TAG, msg);
- throw new IllegalStateException(msg);
- }
-
- if (sourceInfo.mDrmInfo != null) {
- drmInfo = sourceInfo.mDrmInfo.makeCopy();
- }
- } // synchronized
-
- return drmInfo;
- }
- return null;
- }
-
- /**
- * Prepares the DRM for the given data source
- * <p>
- * If {@link DrmEventCallback} is registered, it will be called during
- * preparation to allow configuration of the DRM properties before opening the
- * DRM session. It should be used only for a series of
- * {@link #getDrmPropertyString(DataSourceDesc, String)} and
- * {@link #setDrmPropertyString(DataSourceDesc, String, String)} calls
- * and refrain from any lengthy operation.
- * <p>
- * If the device has not been provisioned before, this call also provisions the device
- * which involves accessing the provisioning server and can take a variable time to
- * complete depending on the network connectivity.
- * When needed, the provisioning will be launched in the background.
- * The listener {@link DrmEventCallback#onDrmPrepared}
- * will be called when provisioning and preparation are finished. The application should
- * check the status code returned with {@link DrmEventCallback#onDrmPrepared} to proceed.
- * <p>
- * The registered {@link DrmEventCallback#onDrmPrepared} is called to indicate the DRM
- * session being ready. The application should not make any assumption about its call
- * sequence (e.g., before or after prepareDrm returns).
- * <p>
- *
- * @param dsd The DRM protected data source
- *
- * @param uuid The UUID of the crypto scheme. If not known beforehand, it can be retrieved
- * from the source listening to {@link DrmEventCallback#onDrmInfo}.
- *
- * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
- * @hide
- */
- // This is an asynchronous call.
- @TestApi
- public @NonNull Object prepareDrm(@NonNull DataSourceDesc dsd, @NonNull UUID uuid) {
- return addTask(newPrepareDrmTask(dsd, uuid));
- }
-
- private Task newPrepareDrmTask(DataSourceDesc dsd, UUID uuid) {
- return new Task(CALL_COMPLETED_PREPARE_DRM, true) {
- @Override
- void process() {
- final SourceInfo sourceInfo = getSourceInfo(dsd);
- int status = PREPARE_DRM_STATUS_PREPARATION_ERROR;
- boolean finishPrepare = true;
-
- if (sourceInfo == null) {
- Log.e(TAG, "prepareDrm(): DataSource not found.");
- } else if (sourceInfo.mDrmInfo == null) {
- // only allowing if tied to a protected source;
- // might relax for releasing offline keys
- Log.e(TAG, "prepareDrm(): Wrong usage: The player must be prepared and "
- + "DRM info be retrieved before this call.");
- } else {
- status = PREPARE_DRM_STATUS_SUCCESS;
- }
-
- try {
- if (status == PREPARE_DRM_STATUS_SUCCESS) {
- sourceInfo.mDrmHandle.prepare(uuid);
- }
- } catch (ResourceBusyException e) {
- status = PREPARE_DRM_STATUS_RESOURCE_BUSY;
- } catch (UnsupportedSchemeException e) {
- status = PREPARE_DRM_STATUS_UNSUPPORTED_SCHEME;
- } catch (NotProvisionedException e) {
- Log.w(TAG, "prepareDrm: NotProvisionedException");
-
- // handle provisioning internally; it'll reset mPrepareDrmInProgress
- status = sourceInfo.mDrmHandle.handleProvisioninig(uuid, mTaskId);
-
- if (status == PREPARE_DRM_STATUS_SUCCESS) {
- // License will be setup in provisioning
- finishPrepare = false;
- } else {
- synchronized (sourceInfo.mDrmHandle) {
- sourceInfo.mDrmHandle.cleanDrmObj();
- }
-
- switch (status) {
- case PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR:
- Log.e(TAG, "prepareDrm: Provisioning was required but failed "
- + "due to a network error.");
- break;
-
- case PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR:
- Log.e(TAG, "prepareDrm: Provisioning was required but the request "
- + "was denied by the server.");
- break;
-
- case PREPARE_DRM_STATUS_PREPARATION_ERROR:
- default:
- Log.e(TAG, "prepareDrm: Post-provisioning preparation failed.");
- break;
- }
- }
- } catch (Exception e) {
- status = PREPARE_DRM_STATUS_PREPARATION_ERROR;
- }
-
- if (finishPrepare) {
- sourceInfo.mDrmHandle.finishPrepare(status);
- synchronized (mTaskLock) {
- mCurrentTask = null;
- processPendingTask_l();
- }
- }
-
- }
- };
- }
-
- /**
- * Releases the DRM session for the given data source
- * <p>
- * The player has to have an active DRM session and be in stopped, or prepared
- * state before this call is made.
- * A {@link #reset()} call will release the DRM session implicitly.
- *
- * @param dsd The DRM protected data source
- *
- * @throws NoDrmSchemeException if there is no active DRM session to release
- * @hide
- */
- // This is a synchronous call.
- @TestApi
- public void releaseDrm(@NonNull DataSourceDesc dsd)
- throws NoDrmSchemeException {
- final SourceInfo sourceInfo = getSourceInfo(dsd);
- if (sourceInfo != null) {
- sourceInfo.mDrmHandle.release();
- }
- }
-
- private native void native_releaseDrm(long mSrcId);
-
- /**
- * A key request/response exchange occurs between the app and a license server
- * to obtain or release keys used to decrypt the given data source.
- * <p>
- * {@code getDrmKeyRequest()} is used to obtain an opaque key request byte array that is
- * delivered to the license server. The opaque key request byte array is returned
- * in KeyRequest.data. The recommended URL to deliver the key request to is
- * returned in {@code KeyRequest.defaultUrl}.
- * <p>
- * After the app has received the key request response from the server,
- * it should deliver to the response to the DRM engine plugin using the method
- * {@link #provideDrmKeyResponse(DataSourceDesc, byte[], byte[])}.
- *
- * @param dsd the DRM protected data source
- *
- * @param keySetId is the key-set identifier of the offline keys being released when keyType is
- * {@link MediaDrm#KEY_TYPE_RELEASE}. It should be set to null for other key requests, when
- * keyType is {@link MediaDrm#KEY_TYPE_STREAMING} or {@link MediaDrm#KEY_TYPE_OFFLINE}.
- *
- * @param initData is the container-specific initialization data when the keyType is
- * {@link MediaDrm#KEY_TYPE_STREAMING} or {@link MediaDrm#KEY_TYPE_OFFLINE}. Its meaning is
- * interpreted based on the mime type provided in the mimeType parameter. It could
- * contain, for example, the content ID, key ID or other data obtained from the content
- * metadata that is required in generating the key request.
- * When the keyType is {@link MediaDrm#KEY_TYPE_RELEASE}, it should be set to null.
- *
- * @param mimeType identifies the mime type of the content
- *
- * @param keyType specifies the type of the request. The request may be to acquire
- * keys for streaming, {@link MediaDrm#KEY_TYPE_STREAMING}, or for offline content
- * {@link MediaDrm#KEY_TYPE_OFFLINE}, or to release previously acquired
- * keys ({@link MediaDrm#KEY_TYPE_RELEASE}), which are identified by a keySetId.
- *
- * @param optionalParameters are included in the key request message to
- * allow a client application to provide additional message parameters to the server.
- * This may be {@code null} if no additional parameters are to be sent.
- *
- * @throws NoDrmSchemeException if there is no active DRM session
- * @hide
- */
- @TestApi
- public MediaDrm.KeyRequest getDrmKeyRequest(
- @NonNull DataSourceDesc dsd,
- @Nullable byte[] keySetId, @Nullable byte[] initData,
- @Nullable String mimeType, @MediaDrmKeyType int keyType,
- @Nullable Map<String, String> optionalParameters)
- throws NoDrmSchemeException {
- Log.v(TAG, "getDrmKeyRequest: " +
- " keySetId: " + keySetId + " initData:" + initData + " mimeType: " + mimeType +
- " keyType: " + keyType + " optionalParameters: " + optionalParameters);
-
- final SourceInfo sourceInfo = getSourceInfo(dsd);
- if (sourceInfo != null) {
- return sourceInfo.mDrmHandle.getDrmKeyRequest(
- keySetId, initData, mimeType, keyType, optionalParameters);
- }
- return null;
- }
-
- /**
- * A key response is received from the license server by the app for the given DRM protected
- * data source, then provided to the DRM engine plugin using {@code provideDrmKeyResponse}.
- * <p>
- * When the response is for an offline key request, a key-set identifier is returned that
- * can be used to later restore the keys to a new session with the method
- * {@link #restoreDrmKeys(DataSourceDesc, byte[])}.
- * When the response is for a streaming or release request, null is returned.
- *
- * @param dsd the DRM protected data source
- *
- * @param keySetId When the response is for a release request, keySetId identifies the saved
- * key associated with the release request (i.e., the same keySetId passed to the earlier
- * {@link # getDrmKeyRequest(DataSourceDesc, byte[], byte[], String, int, Map)} call).
- * It MUST be null when the response is for either streaming or offline key requests.
- *
- * @param response the byte array response from the server
- *
- * @throws NoDrmSchemeException if there is no active DRM session
- * @throws DeniedByServerException if the response indicates that the
- * server rejected the request
- * @hide
- */
- // This is a synchronous call.
- @TestApi
- public byte[] provideDrmKeyResponse(
- @NonNull DataSourceDesc dsd,
- @Nullable byte[] keySetId, @NonNull byte[] response)
- throws NoDrmSchemeException, DeniedByServerException {
- Log.v(TAG, "provideDrmKeyResponse: keySetId: " + keySetId + " response: " + response);
-
- final SourceInfo sourceInfo = getSourceInfo(dsd);
- if (sourceInfo != null) {
- return sourceInfo.mDrmHandle.provideDrmKeyResponse(keySetId, response);
- }
- return null;
- }
-
- /**
- * Restore persisted offline keys into a new session for the given DRM protected data source.
- * {@code keySetId} identifies the keys to load, obtained from a prior call to
- * {@link #provideDrmKeyResponse(DataSourceDesc, byte[], byte[])}.
- *
- * @param dsd the DRM protected data source
- *
- * @param keySetId identifies the saved key set to restore
- *
- * @throws NoDrmSchemeException if there is no active DRM session
- * @hide
- */
- // This is a synchronous call.
- @TestApi
- public void restoreDrmKeys(
- @NonNull DataSourceDesc dsd,
- @NonNull byte[] keySetId)
- throws NoDrmSchemeException {
- Log.v(TAG, "restoreDrmKeys: keySetId: " + keySetId);
-
- final SourceInfo sourceInfo = getSourceInfo(dsd);
- if (sourceInfo != null) {
- sourceInfo.mDrmHandle.restoreDrmKeys(keySetId);
- }
- }
-
- /**
- * Read a DRM engine plugin String property value, given the DRM protected data source
- * and property name string.
- *
- * @param dsd the DRM protected data source
- *
- * @param propertyName the property name
- *
- * Standard fields names are:
- * {@link MediaDrm#PROPERTY_VENDOR}, {@link MediaDrm#PROPERTY_VERSION},
- * {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS}
- *
- * @throws NoDrmSchemeException if there is no active DRM session
- * @hide
- */
- @TestApi
- public String getDrmPropertyString(
- @NonNull DataSourceDesc dsd,
- @NonNull @MediaDrmStringProperty String propertyName)
- throws NoDrmSchemeException {
- Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName);
-
- final SourceInfo sourceInfo = getSourceInfo(dsd);
- if (sourceInfo != null) {
- return sourceInfo.mDrmHandle.getDrmPropertyString(propertyName);
- }
- return null;
- }
-
- /**
- * Set a DRM engine plugin String property value for the given data source.
- *
- * @param dsd the DRM protected data source
- * @param propertyName the property name
- * @param value the property value
- *
- * Standard fields names are:
- * {@link MediaDrm#PROPERTY_VENDOR}, {@link MediaDrm#PROPERTY_VERSION},
- * {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS}
- *
- * @throws NoDrmSchemeException if there is no active DRM session
- * @hide
- */
- // This is a synchronous call.
- @TestApi
- public void setDrmPropertyString(
- @NonNull DataSourceDesc dsd,
- @NonNull @MediaDrmStringProperty String propertyName, @NonNull String value)
- throws NoDrmSchemeException {
- // TODO: this implementation only works when dsd is the only data source
- Log.v(TAG, "setDrmPropertyString: propertyName: " + propertyName + " value: " + value);
-
- final SourceInfo sourceInfo = getSourceInfo(dsd);
- if (sourceInfo != null) {
- sourceInfo.mDrmHandle.setDrmPropertyString(propertyName, value);
- }
- }
-
- /**
- * Encapsulates the DRM properties of the source.
- */
- public static final class DrmInfo {
- private Map<UUID, byte[]> mMapPssh;
- private UUID[] mSupportedSchemes;
-
- /**
- * Returns the PSSH info of the data source for each supported DRM scheme.
- */
- public @NonNull Map<UUID, byte[]> getPssh() {
- return mMapPssh;
- }
-
- /**
- * Returns the intersection of the data source and the device DRM schemes.
- * It effectively identifies the subset of the source's DRM schemes which
- * are supported by the device too.
- */
- public @NonNull List<UUID> getSupportedSchemes() {
- return Arrays.asList(mSupportedSchemes);
- }
-
- private DrmInfo(Map<UUID, byte[]> pssh, UUID[] supportedSchemes) {
- mMapPssh = pssh;
- mSupportedSchemes = supportedSchemes;
- }
-
- private static DrmInfo create(PlayerMessage msg) {
- Log.v(TAG, "DrmInfo.create(" + msg + ")");
-
- Iterator<Value> in = msg.getValuesList().iterator();
- byte[] pssh = in.next().getBytesValue().toByteArray();
-
- Log.v(TAG, "DrmInfo.create() PSSH: " + DrmInfo.arrToHex(pssh));
- Map<UUID, byte[]> mapPssh = DrmInfo.parsePSSH(pssh, pssh.length);
- Log.v(TAG, "DrmInfo.create() PSSH: " + mapPssh);
-
- int supportedDRMsCount = in.next().getInt32Value();
- UUID[] supportedSchemes = new UUID[supportedDRMsCount];
- for (int i = 0; i < supportedDRMsCount; i++) {
- byte[] uuid = new byte[16];
- in.next().getBytesValue().copyTo(uuid, 0);
-
- supportedSchemes[i] = DrmInfo.bytesToUUID(uuid);
-
- Log.v(TAG, "DrmInfo() supportedScheme[" + i + "]: " + supportedSchemes[i]);
- }
-
- Log.v(TAG, "DrmInfo.create() psshsize: " + pssh.length
- + " supportedDRMsCount: " + supportedDRMsCount);
- return new DrmInfo(mapPssh, supportedSchemes);
- }
-
- private DrmInfo makeCopy() {
- return new DrmInfo(this.mMapPssh, this.mSupportedSchemes);
- }
-
- private static String arrToHex(byte[] bytes) {
- String out = "0x";
- for (int i = 0; i < bytes.length; i++) {
- out += String.format("%02x", bytes[i]);
- }
-
- return out;
- }
-
- private static UUID bytesToUUID(byte[] uuid) {
- long msb = 0, lsb = 0;
- for (int i = 0; i < 8; i++) {
- msb |= (((long) uuid[i] & 0xff) << (8 * (7 - i)));
- lsb |= (((long) uuid[i + 8] & 0xff) << (8 * (7 - i)));
- }
-
- return new UUID(msb, lsb);
- }
-
- private static Map<UUID, byte[]> parsePSSH(byte[] pssh, int psshsize) {
- Map<UUID, byte[]> result = new HashMap<UUID, byte[]>();
-
- final int uuidSize = 16;
- final int dataLenSize = 4;
-
- int len = psshsize;
- int numentries = 0;
- int i = 0;
-
- while (len > 0) {
- if (len < uuidSize) {
- Log.w(TAG, String.format("parsePSSH: len is too short to parse "
- + "UUID: (%d < 16) pssh: %d", len, psshsize));
- return null;
- }
-
- byte[] subset = Arrays.copyOfRange(pssh, i, i + uuidSize);
- UUID uuid = bytesToUUID(subset);
- i += uuidSize;
- len -= uuidSize;
-
- // get data length
- if (len < 4) {
- Log.w(TAG, String.format("parsePSSH: len is too short to parse "
- + "datalen: (%d < 4) pssh: %d", len, psshsize));
- return null;
- }
-
- subset = Arrays.copyOfRange(pssh, i, i + dataLenSize);
- int datalen = (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN)
- ? ((subset[3] & 0xff) << 24) | ((subset[2] & 0xff) << 16)
- | ((subset[1] & 0xff) << 8) | (subset[0] & 0xff) :
- ((subset[0] & 0xff) << 24) | ((subset[1] & 0xff) << 16)
- | ((subset[2] & 0xff) << 8) | (subset[3] & 0xff);
- i += dataLenSize;
- len -= dataLenSize;
-
- if (len < datalen) {
- Log.w(TAG, String.format("parsePSSH: len is too short to parse "
- + "data: (%d < %d) pssh: %d", len, datalen, psshsize));
- return null;
- }
-
- byte[] data = Arrays.copyOfRange(pssh, i, i + datalen);
-
- // skip the data
- i += datalen;
- len -= datalen;
-
- Log.v(TAG, String.format("parsePSSH[%d]: <%s, %s> pssh: %d",
- numentries, uuid, arrToHex(data), psshsize));
- numentries++;
- result.put(uuid, data);
- }
-
- return result;
- }
- }; // DrmInfo
-
- /**
- * Thrown when a DRM method is called when there is no active DRM session.
- * Extends MediaDrm.MediaDrmException
- */
- public static final class NoDrmSchemeException extends MediaDrmException {
- public NoDrmSchemeException(@Nullable String detailMessage) {
- super(detailMessage);
- }
- }
-
- private native void native_prepareDrm(
- long srcId, @NonNull byte[] uuid, @NonNull byte[] drmSessionId);
-
- // Instantiated from the native side
- @SuppressWarnings("unused")
- private static class StreamEventCallback extends AudioTrack.StreamEventCallback {
- public long mJAudioTrackPtr;
- public long mNativeCallbackPtr;
- public long mUserDataPtr;
-
- StreamEventCallback(long jAudioTrackPtr, long nativeCallbackPtr, long userDataPtr) {
- super();
- mJAudioTrackPtr = jAudioTrackPtr;
- mNativeCallbackPtr = nativeCallbackPtr;
- mUserDataPtr = userDataPtr;
- }
-
- @Override
- public void onTearDown(AudioTrack track) {
- native_stream_event_onTearDown(mNativeCallbackPtr, mUserDataPtr);
- }
-
- @Override
- public void onPresentationEnded(AudioTrack track) {
- native_stream_event_onStreamPresentationEnd(mNativeCallbackPtr, mUserDataPtr);
- }
-
- @Override
- public void onDataRequest(AudioTrack track, int size) {
- native_stream_event_onStreamDataRequest(
- mJAudioTrackPtr, mNativeCallbackPtr, mUserDataPtr);
- }
- }
-
- /**
- * Returns a byte[] containing the remainder of 'in', closing it when done.
- */
- private static byte[] readInputStreamFully(InputStream in) throws IOException {
- try {
- return readInputStreamFullyNoClose(in);
- } finally {
- in.close();
- }
- }
-
- /**
- * Returns a byte[] containing the remainder of 'in'.
- */
- private static byte[] readInputStreamFullyNoClose(InputStream in) throws IOException {
- ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- byte[] buffer = new byte[1024];
- int count;
- while ((count = in.read(buffer)) != -1) {
- bytes.write(buffer, 0, count);
- }
- return bytes.toByteArray();
- }
-
- private static byte[] getByteArrayFromUUID(@NonNull UUID uuid) {
- long msb = uuid.getMostSignificantBits();
- long lsb = uuid.getLeastSignificantBits();
-
- byte[] uuidBytes = new byte[16];
- for (int i = 0; i < 8; ++i) {
- uuidBytes[i] = (byte) (msb >>> (8 * (7 - i)));
- uuidBytes[8 + i] = (byte) (lsb >>> (8 * (7 - i)));
- }
-
- return uuidBytes;
- }
-
- private static class TimedTextUtil {
- // These keys must be in sync with the keys in TextDescription2.h
- private static final int KEY_START_TIME = 7; // int
- private static final int KEY_STRUCT_TEXT_POS = 14; // TextPos
- private static final int KEY_STRUCT_TEXT = 16; // Text
- private static final int KEY_GLOBAL_SETTING = 101;
- private static final int KEY_LOCAL_SETTING = 102;
-
- private static TimedText parsePlayerMessage(PlayerMessage playerMsg) {
- if (playerMsg.getValuesCount() == 0) {
- return null;
- }
-
- String textChars = null;
- Rect textBounds = null;
- Iterator<Value> in = playerMsg.getValuesList().iterator();
- int type = in.next().getInt32Value();
- if (type == KEY_LOCAL_SETTING) {
- type = in.next().getInt32Value();
- if (type != KEY_START_TIME) {
- return null;
- }
- int startTimeMs = in.next().getInt32Value();
-
- type = in.next().getInt32Value();
- if (type != KEY_STRUCT_TEXT) {
- return null;
- }
-
- byte[] text = in.next().getBytesValue().toByteArray();
- if (text == null || text.length == 0) {
- textChars = null;
- } else {
- textChars = new String(text);
- }
-
- } else if (type != KEY_GLOBAL_SETTING) {
- Log.w(TAG, "Invalid timed text key found: " + type);
- return null;
- }
- if (in.hasNext()) {
- type = in.next().getInt32Value();
- if (type == KEY_STRUCT_TEXT_POS) {
- int top = in.next().getInt32Value();
- int left = in.next().getInt32Value();
- int bottom = in.next().getInt32Value();
- int right = in.next().getInt32Value();
- textBounds = new Rect(left, top, right, bottom);
- }
- }
- return null;
- /* TimedText c-tor usage is temporarily commented out.
- * TODO(b/117527789): use SUBTITLE path for MEDIA_MIMETYPE_TEXT_3GPP track
- * and remove TimedText path from MediaPlayer2.
- return new TimedText(textChars, textBounds);
- */
- }
- }
-
- private Object addTask(Task task) {
- synchronized (mTaskLock) {
- mPendingTasks.add(task);
- processPendingTask_l();
- }
- return task;
- }
-
- @GuardedBy("mTaskLock")
- private void processPendingTask_l() {
- if (mCurrentTask != null) {
- return;
- }
- if (!mPendingTasks.isEmpty()) {
- Task task = mPendingTasks.remove(0);
- mCurrentTask = task;
- mTaskHandler.post(task);
- }
- }
-
- private abstract class Task implements Runnable {
- final long mTaskId = mTaskIdGenerator.getAndIncrement();
- private final int mMediaCallType;
- private final boolean mNeedToWaitForEventToComplete;
- private DataSourceDesc mDSD;
-
- Task(int mediaCallType, boolean needToWaitForEventToComplete) {
- mMediaCallType = mediaCallType;
- mNeedToWaitForEventToComplete = needToWaitForEventToComplete;
- }
-
- abstract void process() throws IOException, NoDrmSchemeException;
-
- @Override
- public void run() {
- int status = CALL_STATUS_NO_ERROR;
- try {
- if (mMediaCallType != CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED
- && getState() == PLAYER_STATE_ERROR) {
- status = CALL_STATUS_INVALID_OPERATION;
- } else {
- if (mMediaCallType == CALL_COMPLETED_SEEK_TO) {
- synchronized (mTaskLock) {
- if (!mPendingTasks.isEmpty()) {
- Task nextTask = mPendingTasks.get(0);
- if (nextTask.mMediaCallType == mMediaCallType) {
- throw new CommandSkippedException(
- "consecutive seekTo is skipped except last one");
- }
- }
- }
- }
- process();
- }
- } catch (IllegalStateException e) {
- status = CALL_STATUS_INVALID_OPERATION;
- } catch (IllegalArgumentException e) {
- status = CALL_STATUS_BAD_VALUE;
- } catch (SecurityException e) {
- status = CALL_STATUS_PERMISSION_DENIED;
- } catch (IOException e) {
- status = CALL_STATUS_ERROR_IO;
- } catch (NoDrmSchemeException e) {
- status = CALL_STATUS_NO_DRM_SCHEME;
- } catch (CommandSkippedException e) {
- status = CALL_STATUS_SKIPPED;
- } catch (Exception e) {
- status = CALL_STATUS_ERROR_UNKNOWN;
- }
- mDSD = getCurrentDataSource();
-
- if (mMediaCallType != CALL_COMPLETED_SEEK_TO) {
- synchronized (mTaskLock) {
- mIsPreviousCommandSeekTo = false;
- }
- }
-
- // TODO: Make native implementations asynchronous and let them send notifications.
- if (!mNeedToWaitForEventToComplete || status != CALL_STATUS_NO_ERROR) {
-
- sendCompleteNotification(status);
-
- synchronized (mTaskLock) {
- mCurrentTask = null;
- processPendingTask_l();
- }
- }
- }
-
- private void sendCompleteNotification(int status) {
- // In {@link #notifyWhenCommandLabelReached} case, a separate callback
- // {@link #onCommandLabelReached} is already called in {@code process()}.
- // CALL_COMPLETED_PREPARE_DRM is sent via DrmEventCallback#onDrmPrepared
- if (mMediaCallType == CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED
- || mMediaCallType == CALL_COMPLETED_PREPARE_DRM) {
- return;
- }
- sendEvent(new EventNotifier() {
- @Override
- public void notify(EventCallback callback) {
- callback.onCallCompleted(
- MediaPlayer2.this, mDSD, mMediaCallType, status);
- }
- });
- }
- };
-
- private final class CommandSkippedException extends RuntimeException {
- CommandSkippedException(String detailMessage) {
- super(detailMessage);
- }
- };
-
- // Modular DRM
- private final Map<UUID, MediaDrm> mDrmObjs = Collections.synchronizedMap(new HashMap<>());
- private class DrmHandle {
-
- static final int PROVISION_TIMEOUT_MS = 60000;
-
- final DataSourceDesc mDSD;
- final long mSrcId;
-
- //--- guarded by |this| start
- MediaDrm mDrmObj;
- byte[] mDrmSessionId;
- UUID mActiveDrmUUID;
- boolean mDrmConfigAllowed;
- boolean mDrmProvisioningInProgress;
- boolean mPrepareDrmInProgress;
- Future<?> mProvisionResult;
- DrmPreparationInfo mPrepareInfo;
- //--- guarded by |this| end
-
- DrmHandle(DataSourceDesc dsd, long srcId) {
- mDSD = dsd;
- mSrcId = srcId;
- }
-
- void prepare(UUID uuid) throws UnsupportedSchemeException,
- ResourceBusyException, NotProvisionedException, InterruptedException,
- ExecutionException, TimeoutException {
- Log.v(TAG, "prepareDrm: uuid: " + uuid);
-
- synchronized (this) {
- if (mActiveDrmUUID != null) {
- final String msg = "prepareDrm(): Wrong usage: There is already "
- + "an active DRM scheme with " + uuid;
- Log.e(TAG, msg);
- throw new IllegalStateException(msg);
- }
-
- if (mPrepareDrmInProgress) {
- final String msg = "prepareDrm(): Wrong usage: There is already "
- + "a pending prepareDrm call.";
- Log.e(TAG, msg);
- throw new IllegalStateException(msg);
- }
-
- if (mDrmProvisioningInProgress) {
- final String msg = "prepareDrm(): Unexpectd: Provisioning already in progress";
- Log.e(TAG, msg);
- throw new IllegalStateException(msg);
- }
-
- // shouldn't need this; just for safeguard
- cleanDrmObj();
-
- mPrepareDrmInProgress = true;
-
- try {
- // only creating the DRM object to allow pre-openSession configuration
- prepareDrm_createDrmStep(uuid);
- } catch (Exception e) {
- Log.w(TAG, "prepareDrm(): Exception ", e);
- mPrepareDrmInProgress = false;
- throw e;
- }
-
- mDrmConfigAllowed = true;
- } // synchronized
-
- // call the callback outside the lock
- sendDrmEventWait(new DrmEventNotifier<Void>() {
- @Override
- public Void notifyWait(DrmEventCallback callback) {
- callback.onDrmConfig(MediaPlayer2.this, mDSD, mDrmObj);
- return null;
- }
- });
-
- synchronized (this) {
- mDrmConfigAllowed = false;
- boolean earlyExit = false;
-
- try {
- prepareDrm_openSessionStep(uuid);
-
- this.mActiveDrmUUID = uuid;
- mPrepareDrmInProgress = false;
- } catch (IllegalStateException e) {
- final String msg = "prepareDrm(): Wrong usage: The player must be "
- + "in the prepared state to call prepareDrm().";
- Log.e(TAG, msg);
- earlyExit = true;
- mPrepareDrmInProgress = false;
- throw new IllegalStateException(msg);
- } catch (NotProvisionedException e) {
- Log.w(TAG, "prepareDrm: NotProvisionedException", e);
- throw e;
- } catch (Exception e) {
- Log.e(TAG, "prepareDrm: Exception " + e);
- earlyExit = true;
- mPrepareDrmInProgress = false;
- throw e;
- } finally {
- if (earlyExit) { // clean up object if didn't succeed
- cleanDrmObj();
- }
- } // finally
- } // synchronized
- }
-
- void prepareDrm_createDrmStep(UUID uuid)
- throws UnsupportedSchemeException {
- Log.v(TAG, "prepareDrm_createDrmStep: UUID: " + uuid);
-
- try {
- mDrmObj = mDrmObjs.computeIfAbsent(uuid, scheme -> {
- try {
- return new MediaDrm(scheme);
- } catch (UnsupportedSchemeException e) {
- throw new IllegalArgumentException(e);
- }
- });
- Log.v(TAG, "prepareDrm_createDrmStep: Created mDrmObj=" + mDrmObj);
- } catch (Exception e) { // UnsupportedSchemeException
- Log.e(TAG, "prepareDrm_createDrmStep: MediaDrm failed with " + e);
- throw e;
- }
- }
-
- void prepareDrm_openSessionStep(UUID uuid)
- throws NotProvisionedException, ResourceBusyException {
- Log.v(TAG, "prepareDrm_openSessionStep: uuid: " + uuid);
-
- // TODO:
- // don't need an open session for a future specialKeyReleaseDrm mode but we should do
- // it anyway so it raises provisioning error if needed. We'd rather handle provisioning
- // at prepareDrm/openSession rather than getDrmKeyRequest/provideDrmKeyResponse
- try {
- mDrmSessionId = mDrmObj.openSession();
- Log.v(TAG, "prepareDrm_openSessionStep: mDrmSessionId=" + mDrmSessionId);
-
- // Sending it down to native/mediaserver to create the crypto object
- // This call could simply fail due to bad player state, e.g., after play().
- final MediaPlayer2 mp2 = MediaPlayer2.this;
- mp2.native_prepareDrm(mSrcId, getByteArrayFromUUID(uuid), mDrmSessionId);
- Log.v(TAG, "prepareDrm_openSessionStep: native_prepareDrm/Crypto succeeded");
-
- } catch (Exception e) { //ResourceBusyException, NotProvisionedException
- Log.e(TAG, "prepareDrm_openSessionStep: open/crypto failed with " + e);
- throw e;
- }
-
- }
-
- int handleProvisioninig(UUID uuid, long taskId) {
- synchronized (this) {
- if (mDrmProvisioningInProgress) {
- Log.e(TAG, "handleProvisioninig: Unexpected mDrmProvisioningInProgress");
- return PREPARE_DRM_STATUS_PREPARATION_ERROR;
- }
-
- MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest();
- if (provReq == null) {
- Log.e(TAG, "handleProvisioninig: getProvisionRequest returned null.");
- return PREPARE_DRM_STATUS_PREPARATION_ERROR;
- }
-
- Log.v(TAG, "handleProvisioninig provReq "
- + " data: " + provReq.getData() + " url: " + provReq.getDefaultUrl());
-
- // networking in a background thread
- mDrmProvisioningInProgress = true;
-
- mProvisionResult = sDrmThreadPool.submit(newProvisioningTask(uuid, taskId));
-
- return PREPARE_DRM_STATUS_SUCCESS;
- }
- }
-
- void provision(UUID uuid, long taskId) {
-
- MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest();
- String urlStr = provReq.getDefaultUrl();
- urlStr += "&signedRequest=" + new String(provReq.getData());
- Log.v(TAG, "handleProvisioninig: Thread is initialised url: " + urlStr);
-
- byte[] response = null;
- boolean provisioningSucceeded = false;
- int status = PREPARE_DRM_STATUS_PREPARATION_ERROR;
- try {
- URL url = new URL(urlStr);
- final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
- try {
- connection.setRequestMethod("POST");
- connection.setDoOutput(false);
- connection.setDoInput(true);
- connection.setConnectTimeout(PROVISION_TIMEOUT_MS);
- connection.setReadTimeout(PROVISION_TIMEOUT_MS);
-
- connection.connect();
- response = readInputStreamFully(connection.getInputStream());
-
- Log.v(TAG, "handleProvisioninig: Thread run: response " +
- response.length + " " + response);
- } catch (Exception e) {
- status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
- Log.w(TAG, "handleProvisioninig: Thread run: connect " + e + " url: " + url);
- } finally {
- connection.disconnect();
- }
- } catch (Exception e) {
- status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
- Log.w(TAG, "handleProvisioninig: Thread run: openConnection " + e);
- }
-
- if (response != null) {
- try {
- mDrmObj.provideProvisionResponse(response);
- Log.v(TAG, "handleProvisioninig: Thread run: " +
- "provideProvisionResponse SUCCEEDED!");
-
- provisioningSucceeded = true;
- } catch (Exception e) {
- status = PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR;
- Log.w(TAG, "handleProvisioninig: Thread run: " +
- "provideProvisionResponse " + e);
- }
- }
-
- boolean succeeded = false;
-
- synchronized (this) {
- // continuing with prepareDrm
- if (provisioningSucceeded) {
- succeeded = resumePrepare(uuid);
- status = (succeeded) ?
- PREPARE_DRM_STATUS_SUCCESS :
- PREPARE_DRM_STATUS_PREPARATION_ERROR;
- }
- mDrmProvisioningInProgress = false;
- mPrepareDrmInProgress = false;
- if (!succeeded) {
- cleanDrmObj(); // cleaning up if it hasn't gone through while in the lock
- }
- } // synchronized
-
- // calling the callback outside the lock
- finishPrepare(status);
-
- synchronized (mTaskLock) {
- if (mCurrentTask != null
- && mCurrentTask.mTaskId == taskId
- && mCurrentTask.mMediaCallType == CALL_COMPLETED_PREPARE_DRM
- && mCurrentTask.mNeedToWaitForEventToComplete) {
- mCurrentTask = null;
- processPendingTask_l();
- }
- }
- }
-
- Runnable newProvisioningTask(UUID uuid, long taskId) {
- return new Runnable() {
- @Override
- public void run() {
- provision(uuid, taskId);
- }
- };
- }
-
- boolean resumePrepare(UUID uuid) {
- Log.v(TAG, "resumePrepareDrm: uuid: " + uuid);
-
- // mDrmLock is guaranteed to be held
- boolean success = false;
- try {
- // resuming
- prepareDrm_openSessionStep(uuid);
-
- this.mActiveDrmUUID = uuid;
-
- success = true;
- } catch (Exception e) {
- Log.w(TAG, "handleProvisioninig: Thread run native_prepareDrm resume failed:" + e);
- // mDrmObj clean up is done by the caller
- }
-
- return success;
- }
-
- synchronized boolean setPreparationInfo(DrmPreparationInfo prepareInfo) {
- if (prepareInfo == null || !prepareInfo.isValid() || mPrepareInfo != null) {
- return false;
- }
- mPrepareInfo = prepareInfo;
- return true;
- }
-
- void finishPrepare(int status) {
- if (status != PREPARE_DRM_STATUS_SUCCESS) {
- notifyPrepared(status, null);
- return;
- }
-
- if (mPrepareInfo == null) {
- // Deprecated: this can only happen when using MediaPlayer Version 1 APIs
- notifyPrepared(status, null);
- return;
- }
-
- final byte[] keySetId = mPrepareInfo.mKeySetId;
- if (keySetId != null) {
- try {
- mDrmObj.restoreKeys(mDrmSessionId, keySetId);
- notifyPrepared(PREPARE_DRM_STATUS_SUCCESS, keySetId);
- } catch (Exception e) {
- notifyPrepared(PREPARE_DRM_STATUS_RESTORE_ERROR, keySetId);
- }
- return;
- }
-
- sDrmThreadPool.submit(newKeyExchangeTask());
- }
-
- Runnable newKeyExchangeTask() {
- return new Runnable() {
- @Override
- public void run() {
- final byte[] initData = mPrepareInfo.mInitData;
- final String mimeType = mPrepareInfo.mMimeType;
- final int keyType = mPrepareInfo.mKeyType;
- final Map<String, String> optionalParams = mPrepareInfo.mOptionalParameters;
- byte[] keySetId = null;
- try {
- KeyRequest req;
- req = getDrmKeyRequest(null, initData, mimeType, keyType, optionalParams);
- byte[] response = sendDrmEventWait(new DrmEventNotifier<byte[]>() {
- @Override
- public byte[] notifyWait(DrmEventCallback callback) {
- final MediaPlayer2 mp = MediaPlayer2.this;
- return callback.onDrmKeyRequest(mp, mDSD, req);
- }
- });
- keySetId = provideDrmKeyResponse(null, response);
- } catch (Exception e) {
- }
- if (keySetId == null) {
- notifyPrepared(PREPARE_DRM_STATUS_KEY_EXCHANGE_ERROR, null);
- } else {
- notifyPrepared(PREPARE_DRM_STATUS_SUCCESS, keySetId);
- }
- }
- };
- }
-
- void notifyPrepared(final int status, byte[] keySetId) {
-
- Message msg;
- if (status == PREPARE_DRM_STATUS_SUCCESS) {
- msg = mTaskHandler.obtainMessage(
- MEDIA_DRM_PREPARED, 0, 0, null);
- } else {
- msg = mTaskHandler.obtainMessage(
- MEDIA_ERROR, status, MEDIA_ERROR_UNKNOWN, null);
- }
- mTaskHandler.post(new Runnable() {
- @Override
- public void run() {
- mTaskHandler.handleMessage(msg, mSrcId);
- }
- });
-
- sendDrmEvent(new DrmEventNotifier() {
- @Override
- public void notify(DrmEventCallback callback) {
- callback.onDrmPrepared(MediaPlayer2.this, mDSD, status,
- keySetId);
- }
- });
-
- }
-
- void cleanDrmObj() {
- // the caller holds mDrmLock
- Log.v(TAG, "cleanDrmObj: mDrmObj=" + mDrmObj + " mDrmSessionId=" + mDrmSessionId);
-
- if (mDrmSessionId != null) {
- mDrmObj.closeSession(mDrmSessionId);
- mDrmSessionId = null;
- }
- }
-
- void release() throws NoDrmSchemeException {
- synchronized (this) {
- Log.v(TAG, "releaseDrm:");
-
- if (mActiveDrmUUID == null) {
- Log.e(TAG, "releaseDrm(): No active DRM scheme to release.");
- throw new NoDrmSchemeException(
- "releaseDrm: No active DRM scheme to release.");
- }
-
- try {
- // we don't have the player's state in this layer. The below call raises
- // exception if we're in a non-stopped/prepared state.
-
- // for cleaning native/mediaserver crypto object
- native_releaseDrm(mSrcId);
-
- // for cleaning client-side MediaDrm object; only called if above has succeeded
- cleanDrmObj();
-
- this.mActiveDrmUUID = null;
- } catch (IllegalStateException e) {
- Log.w(TAG, "releaseDrm: Exception ", e);
- throw new IllegalStateException(
- "releaseDrm: The player is not in a valid state.");
- } catch (Exception e) {
- Log.e(TAG, "releaseDrm: Exception ", e);
- }
- } // synchronized
- }
-
- void cleanup() {
- synchronized (this) {
- Log.v(TAG, "cleanupDrm: " +
- " mProvisioningTask=" + mProvisionResult +
- " mPrepareDrmInProgress=" + mPrepareDrmInProgress +
- " mActiveDrmScheme=" + mActiveDrmUUID);
-
- if (mProvisionResult != null) {
- // timeout; relying on HttpUrlConnection
- try {
- mProvisionResult.get();
- }
- catch (InterruptedException | ExecutionException e) {
- Log.w(TAG, "resetDrmState: ProvThread.join Exception " + e);
- }
- }
-
- // set to false to avoid duplicate release calls
- this.mActiveDrmUUID = null;
-
- native_releaseDrm(mSrcId);
- cleanDrmObj();
- } // synchronized
- }
-
- Runnable newCleanupTask() {
- return new Runnable() {
- @Override
- public void run() {
- cleanup();
- }
- };
- }
-
- MediaDrm.KeyRequest getDrmKeyRequest(
- byte[] keySetId, byte[] initData,
- String mimeType, int keyType,
- Map<String, String> optionalParameters)
- throws NoDrmSchemeException {
- synchronized (this) {
- if (mActiveDrmUUID == null) {
- Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
- throw new NoDrmSchemeException(
- "getDrmKeyRequest: Has to set a DRM scheme first.");
- }
-
- try {
- byte[] scope = (keyType != MediaDrm.KEY_TYPE_RELEASE) ?
- mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE
- keySetId; // keySetId for KEY_TYPE_RELEASE
-
- HashMap<String, String> hmapOptionalParameters =
- (optionalParameters != null)
- ? new HashMap<String, String>(optionalParameters)
- : null;
-
- MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(
- scope, initData, mimeType, keyType, hmapOptionalParameters);
- Log.v(TAG, "getDrmKeyRequest: --> request: " + request);
-
- return request;
-
- } catch (NotProvisionedException e) {
- Log.w(TAG, "getDrmKeyRequest NotProvisionedException: " +
- "Unexpected. Shouldn't have reached here.");
- throw new IllegalStateException("getDrmKeyRequest: provisioning error.");
- } catch (Exception e) {
- Log.w(TAG, "getDrmKeyRequest Exception " + e);
- throw e;
- }
-
- }
- }
-
- byte[] provideDrmKeyResponse(byte[] keySetId, byte[] response)
- throws NoDrmSchemeException, DeniedByServerException {
- synchronized (this) {
-
- if (mActiveDrmUUID == null) {
- Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
- throw new NoDrmSchemeException(
- "getDrmKeyRequest: Has to set a DRM scheme first.");
- }
-
- try {
- byte[] scope = (keySetId == null) ?
- mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE
- keySetId; // keySetId for KEY_TYPE_RELEASE
-
- byte[] keySetResult = mDrmObj.provideKeyResponse(scope, response);
-
- Log.v(TAG, "provideDrmKeyResponse: keySetId: " + keySetId
- + " response: " + response + " --> " + keySetResult);
-
-
- return keySetResult;
-
- } catch (NotProvisionedException e) {
- Log.w(TAG, "provideDrmKeyResponse NotProvisionedException: " +
- "Unexpected. Shouldn't have reached here.");
- throw new IllegalStateException("provideDrmKeyResponse: " +
- "Unexpected provisioning error.");
- } catch (Exception e) {
- Log.w(TAG, "provideDrmKeyResponse Exception " + e);
- throw e;
- }
- }
- }
-
- void restoreDrmKeys(byte[] keySetId)
- throws NoDrmSchemeException {
- synchronized (this) {
- if (mActiveDrmUUID == null) {
- Log.w(TAG, "restoreDrmKeys NoDrmSchemeException");
- throw new NoDrmSchemeException(
- "restoreDrmKeys: Has to set a DRM scheme first.");
- }
-
- try {
- mDrmObj.restoreKeys(mDrmSessionId, keySetId);
- } catch (Exception e) {
- Log.w(TAG, "restoreKeys Exception " + e);
- throw e;
- }
- }
- }
-
- String getDrmPropertyString(String propertyName)
- throws NoDrmSchemeException {
- String v;
- synchronized (this) {
-
- if (mActiveDrmUUID == null && !mDrmConfigAllowed) {
- Log.w(TAG, "getDrmPropertyString NoDrmSchemeException");
- throw new NoDrmSchemeException(
- "getDrmPropertyString: Has to prepareDrm() first.");
- }
-
- try {
- v = mDrmObj.getPropertyString(propertyName);
- } catch (Exception e) {
- Log.w(TAG, "getDrmPropertyString Exception " + e);
- throw e;
- }
- } // synchronized
-
- Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName + " --> value: " + v);
-
- return v;
- }
-
- void setDrmPropertyString(String propertyName, String value)
- throws NoDrmSchemeException {
- synchronized (this) {
-
- if ( mActiveDrmUUID == null && !mDrmConfigAllowed ) {
- Log.w(TAG, "setDrmPropertyString NoDrmSchemeException");
- throw new NoDrmSchemeException(
- "setDrmPropertyString: Has to prepareDrm() first.");
- }
-
- try {
- mDrmObj.setPropertyString(propertyName, value);
- } catch ( Exception e ) {
- Log.w(TAG, "setDrmPropertyString Exception " + e);
- throw e;
- }
- }
- }
-
- }
-
- final class SourceInfo {
- final DataSourceDesc mDSD;
- final long mId = mSrcIdGenerator.getAndIncrement();
- AtomicInteger mBufferedPercentage = new AtomicInteger(0);
- boolean mClosed = false;
- int mPrepareBarrier = 1;
-
- // m*AsNextSource (below) only applies to pending data sources in the playlist;
- // the meanings of mCurrentSourceInfo.{mStateAsNextSource,mPlayPendingAsNextSource}
- // are undefined.
- int mStateAsNextSource = NEXT_SOURCE_STATE_INIT;
- boolean mPlayPendingAsNextSource = false;
-
- // Modular DRM
- final DrmHandle mDrmHandle;
- DrmInfo mDrmInfo;
- boolean mDrmInfoResolved;
-
- SourceInfo(DataSourceDesc dsd) {
- this.mDSD = dsd;
- mDrmHandle = new DrmHandle(dsd, mId);
- }
-
- void close() {
- synchronized (this) {
- if (!mClosed) {
- if (mDSD != null) {
- mDSD.close();
- }
- mClosed = true;
- }
- }
- }
-
- @Override
- public String toString() {
- return String.format("%s(%d)", SourceInfo.class.getName(), mId);
- }
-
- }
-
- private SourceInfo getSourceInfo(long srcId) {
- synchronized (mSrcLock) {
- if (isCurrentSource(srcId)) {
- return mCurrentSourceInfo;
- }
- if (isNextSource(srcId)) {
- return mNextSourceInfos.peek();
- }
- }
- return null;
- }
-
- private SourceInfo getSourceInfo(DataSourceDesc dsd) {
- synchronized (mSrcLock) {
- if (isCurrentSource(dsd)) {
- return mCurrentSourceInfo;
- }
- if (isNextSource(dsd)) {
- return mNextSourceInfos.peek();
- }
- }
- return null;
- }
-
- private boolean isCurrentSource(long srcId) {
- synchronized (mSrcLock) {
- return mCurrentSourceInfo != null && mCurrentSourceInfo.mId == srcId;
- }
- }
-
- private boolean isCurrentSource(DataSourceDesc dsd) {
- synchronized (mSrcLock) {
- return mCurrentSourceInfo != null && mCurrentSourceInfo.mDSD == dsd;
- }
- }
-
- private boolean isNextSource(long srcId) {
- SourceInfo nextSourceInfo = mNextSourceInfos.peek();
- return nextSourceInfo != null && nextSourceInfo.mId == srcId;
- }
-
- private boolean isNextSource(DataSourceDesc dsd) {
- SourceInfo nextSourceInfo = mNextSourceInfos.peek();
- return nextSourceInfo != null && nextSourceInfo.mDSD == dsd;
- }
-
- @GuardedBy("mSrcLock")
- private void setCurrentSourceInfo_l(SourceInfo sourceInfo) {
- cleanupSourceInfo(mCurrentSourceInfo);
- mCurrentSourceInfo = sourceInfo;
- }
-
- @GuardedBy("mSrcLock")
- private void clearNextSourceInfos_l() {
- while (!mNextSourceInfos.isEmpty()) {
- cleanupSourceInfo(mNextSourceInfos.poll());
- }
- }
-
- private void cleanupSourceInfo(SourceInfo sourceInfo) {
- if (sourceInfo != null) {
- sourceInfo.close();
- Runnable task = sourceInfo.mDrmHandle.newCleanupTask();
- sDrmThreadPool.submit(task);
- }
- }
-
- private void clearSourceInfos() {
- synchronized (mSrcLock) {
- setCurrentSourceInfo_l(null);
- clearNextSourceInfos_l();
- }
- }
-
- public static final class MetricsConstants {
- private MetricsConstants() {}
-
- /**
- * Key to extract the MIME type of the video track
- * from the {@link MediaPlayer2#getMetrics} return value.
- * The value is a String.
- */
- public static final String MIME_TYPE_VIDEO = "android.media.mediaplayer.video.mime";
-
- /**
- * Key to extract the codec being used to decode the video track
- * from the {@link MediaPlayer2#getMetrics} return value.
- * The value is a String.
- */
- public static final String CODEC_VIDEO = "android.media.mediaplayer.video.codec";
-
- /**
- * Key to extract the width (in pixels) of the video track
- * from the {@link MediaPlayer2#getMetrics} return value.
- * The value is an integer.
- */
- public static final String WIDTH = "android.media.mediaplayer.width";
-
- /**
- * Key to extract the height (in pixels) of the video track
- * from the {@link MediaPlayer2#getMetrics} return value.
- * The value is an integer.
- */
- public static final String HEIGHT = "android.media.mediaplayer.height";
-
- /**
- * Key to extract the count of video frames played
- * from the {@link MediaPlayer2#getMetrics} return value.
- * The value is an integer.
- */
- public static final String FRAMES = "android.media.mediaplayer.frames";
-
- /**
- * Key to extract the count of video frames dropped
- * from the {@link MediaPlayer2#getMetrics} return value.
- * The value is an integer.
- */
- public static final String FRAMES_DROPPED = "android.media.mediaplayer.dropped";
-
- /**
- * Key to extract the MIME type of the audio track
- * from the {@link MediaPlayer2#getMetrics} return value.
- * The value is a String.
- */
- public static final String MIME_TYPE_AUDIO = "android.media.mediaplayer.audio.mime";
-
- /**
- * Key to extract the codec being used to decode the audio track
- * from the {@link MediaPlayer2#getMetrics} return value.
- * The value is a String.
- */
- public static final String CODEC_AUDIO = "android.media.mediaplayer.audio.codec";
-
- /**
- * Key to extract the duration (in milliseconds) of the
- * media being played
- * from the {@link MediaPlayer2#getMetrics} return value.
- * The value is a long.
- */
- public static final String DURATION = "android.media.mediaplayer.durationMs";
-
- /**
- * Key to extract the playing time (in milliseconds) of the
- * media being played
- * from the {@link MediaPlayer2#getMetrics} return value.
- * The value is a long.
- */
- public static final String PLAYING = "android.media.mediaplayer.playingMs";
-
- /**
- * Key to extract the count of errors encountered while
- * playing the media
- * from the {@link MediaPlayer2#getMetrics} return value.
- * The value is an integer.
- */
- public static final String ERRORS = "android.media.mediaplayer.err";
-
- /**
- * Key to extract an (optional) error code detected while
- * playing the media
- * from the {@link MediaPlayer2#getMetrics} return value.
- * The value is an integer.
- */
- public static final String ERROR_CODE = "android.media.mediaplayer.errcode";
-
- }
-
- private void keepAudioSessionIdAlive(int sessionId) {
- synchronized (mSessionIdLock) {
- if (mDummyAudioTrack != null) {
- if (mDummyAudioTrack.getAudioSessionId() == sessionId) {
- return;
- }
- mDummyAudioTrack.release();
- }
- // TODO: parameters can be optimized
- mDummyAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100,
- AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, 2,
- AudioTrack.MODE_STATIC, sessionId);
- }
- }
-
- private void keepAudioSessionIdAlive(AudioTrack at) {
- synchronized (mSessionIdLock) {
- if (mDummyAudioTrack != null) {
- if (mDummyAudioTrack.getAudioSessionId() == at.getAudioSessionId()) {
- at.release();
- return;
- }
- mDummyAudioTrack.release();
- }
- mDummyAudioTrack = at;
- }
- }
-}
diff --git a/media/apex/java/android/media/MediaPlayer2Utils.java b/media/apex/java/android/media/MediaPlayer2Utils.java
deleted file mode 100644
index ac34260f9bcf..000000000000
--- a/media/apex/java/android/media/MediaPlayer2Utils.java
+++ /dev/null
@@ -1,43 +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 android.media;
-
-/**
- * Helper class used by native code to reduce JNI calls from native side.
- * @hide
- */
-public class MediaPlayer2Utils {
- /**
- * Returns whether audio offloading is supported for the given audio format.
- *
- * @param encoding the type of encoding defined in {@link AudioFormat}
- * @param sampleRate the sampling rate of the stream
- * @param channelMask the channel mask defined in {@link AudioFormat}
- */
- // @CalledByNative
- public static boolean isOffloadedAudioPlaybackSupported(
- int encoding, int sampleRate, int channelMask) {
- final AudioFormat format = new AudioFormat.Builder()
- .setEncoding(encoding)
- .setSampleRate(sampleRate)
- .setChannelMask(channelMask)
- .build();
- //TODO MP2 needs to pass AudioAttributes for this query, instead of using default attr
- return AudioManager.isOffloadedPlaybackSupported(format,
- (new AudioAttributes.Builder()).build());
- }
-}
diff --git a/media/apex/java/android/media/UriDataSourceDesc.java b/media/apex/java/android/media/UriDataSourceDesc.java
deleted file mode 100644
index adf7a7ddddb9..000000000000
--- a/media/apex/java/android/media/UriDataSourceDesc.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 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 android.media;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.Uri;
-
-import java.net.HttpCookie;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Structure of data source descriptor for sources using URI.
- *
- * Used by {@link MediaPlayer2#setDataSource}, {@link MediaPlayer2#setNextDataSource} and
- * {@link MediaPlayer2#setNextDataSources} to set data source for playback.
- *
- * <p>Users should use {@link Builder} to change {@link UriDataSourceDesc}.
- * @hide
- */
-public class UriDataSourceDesc extends DataSourceDesc {
- private Uri mUri;
- private Map<String, String> mHeader;
- private List<HttpCookie> mCookies;
-
- UriDataSourceDesc(String mediaId, long startPositionMs, long endPositionMs,
- Uri uri, Map<String, String> header, List<HttpCookie> cookies) {
- super(mediaId, startPositionMs, endPositionMs);
- mUri = uri;
- mHeader = header;
- mCookies = cookies;
- }
-
- /**
- * Return the Uri of this data source.
- * @return the Uri of this data source
- */
- public @NonNull Uri getUri() {
- return mUri;
- }
-
- /**
- * Return the Uri headers of this data source.
- * @return the Uri headers of this data source
- */
- public @Nullable Map<String, String> getHeaders() {
- if (mHeader == null) {
- return null;
- }
- return new HashMap<String, String>(mHeader);
- }
-
- /**
- * Return the Uri cookies of this data source.
- * @return the Uri cookies of this data source
- */
- public @Nullable List<HttpCookie> getCookies() {
- if (mCookies == null) {
- return null;
- }
- return new ArrayList<HttpCookie>(mCookies);
- }
-}
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 65d3ffc78faf..ac5a9f82bcd0 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -670,7 +670,7 @@ public final class AudioAttributes implements Parcelable {
mUsage = usage;
break;
default:
- mUsage = USAGE_UNKNOWN;
+ throw new IllegalArgumentException("Invalid usage " + usage);
}
return this;
}
@@ -696,7 +696,7 @@ public final class AudioAttributes implements Parcelable {
mContentType = contentType;
break;
default:
- mContentType = CONTENT_TYPE_UNKNOWN;
+ throw new IllegalArgumentException("Invalid content type " + contentType);
}
return this;
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index d4ea3ff5d508..f82a3ac38853 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1875,12 +1875,37 @@ public class AudioManager {
}
/**
+ * @hide
+ * Sets the microphone from switch mute on or off.
+ * <p>
+ * This method should only be used by InputManager to notify
+ * Audio Subsystem about Microphone Mute switch state.
+ *
+ * @param on set <var>true</var> to mute the microphone;
+ * <var>false</var> to turn mute off
+ */
+ @UnsupportedAppUsage
+ public void setMicrophoneMuteFromSwitch(boolean on) {
+ final IAudioService service = getService();
+ try {
+ service.setMicrophoneMuteFromSwitch(on);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Checks whether the microphone mute is on or off.
*
* @return true if microphone is muted, false if it's not
*/
public boolean isMicrophoneMute() {
- return AudioSystem.isMicrophoneMuted();
+ final IAudioService service = getService();
+ try {
+ return service.isMicrophoneMuted();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 5ef9afe3f32d..c2726b1ce501 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -16,6 +16,7 @@
package android.media;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
@@ -26,6 +27,8 @@ import android.media.audiofx.AudioEffect;
import android.media.audiopolicy.AudioMix;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Map;
@@ -439,6 +442,50 @@ public class AudioSystem
public static final int DEAD_OBJECT = -6;
public static final int WOULD_BLOCK = -7;
+ /** @hide */
+ @IntDef({
+ SUCCESS,
+ ERROR,
+ BAD_VALUE,
+ INVALID_OPERATION,
+ PERMISSION_DENIED,
+ NO_INIT,
+ DEAD_OBJECT,
+ WOULD_BLOCK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AudioSystemError {}
+
+ /**
+ * Convert an int error value to its String value for readability.
+ * Accepted error values are the java AudioSystem errors, matching android_media_AudioErrors.h,
+ * which map onto the native status_t type.
+ * @param error one of the java AudioSystem errors
+ * @return a human-readable string
+ */
+ public static String audioSystemErrorToString(@AudioSystemError int error) {
+ switch(error) {
+ case SUCCESS:
+ return "SUCCESS";
+ case ERROR:
+ return "ERROR";
+ case BAD_VALUE:
+ return "BAD_VALUE";
+ case INVALID_OPERATION:
+ return "INVALID_OPERATION";
+ case PERMISSION_DENIED:
+ return "PERMISSION_DENIED";
+ case NO_INIT:
+ return "NO_INIT";
+ case DEAD_OBJECT:
+ return "DEAD_OBJECT";
+ case WOULD_BLOCK:
+ return "WOULD_BLOCK";
+ default:
+ return ("unknown error:" + error);
+ }
+ }
+
/*
* AudioPolicyService methods
*/
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 5b535651abd9..33ddfa7849e1 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -494,6 +494,19 @@ public class ExifInterface {
// See http://www.exiv2.org/makernote.html#R11
private static final int PEF_MAKER_NOTE_SKIP_SIZE = 6;
+ // See PNG (Portable Network Graphics) Specification, Version 1.2,
+ // 3.1. PNG file signature
+ private static final byte[] PNG_SIGNATURE = new byte[] {(byte) 0x89, (byte) 0x50, (byte) 0x4e,
+ (byte) 0x47, (byte) 0x0d, (byte) 0x0a, (byte) 0x1a, (byte) 0x0a};
+ // See PNG (Portable Network Graphics) Specification, Version 1.2,
+ // 3.7. eXIf Exchangeable Image File (Exif) Profile
+ private static final byte[] PNG_CHUNK_TYPE_EXIF = new byte[]{(byte) 0x65, (byte) 0x58,
+ (byte) 0x49, (byte) 0x66};
+ private static final byte[] PNG_CHUNK_TYPE_IEND = new byte[]{(byte) 0x49, (byte) 0x45,
+ (byte) 0x4e, (byte) 0x44};
+ private static final int PNG_CHUNK_LENGTH_BYTE_LENGTH = 4;
+ private static final int PNG_CHUNK_CRC_BYTE_LENGTH = 4;
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private static SimpleDateFormat sFormatter;
private static SimpleDateFormat sFormatterTz;
@@ -1311,6 +1324,7 @@ public class ExifInterface {
private static final int IMAGE_TYPE_RW2 = 10;
private static final int IMAGE_TYPE_SRW = 11;
private static final int IMAGE_TYPE_HEIF = 12;
+ private static final int IMAGE_TYPE_PNG = 13;
static {
sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
@@ -1343,6 +1357,7 @@ public class ExifInterface {
private AssetManager.AssetInputStream mAssetInputStream;
private boolean mIsInputStream;
private int mMimeType;
+ private boolean mIsStandalone;
@UnsupportedAppUsage
private final HashMap[] mAttributes = new HashMap[EXIF_TAGS.length];
private Set<Integer> mHandledIfdOffsets = new HashSet<>(EXIF_TAGS.length);
@@ -1397,6 +1412,7 @@ public class ExifInterface {
if (fileDescriptor == null) {
throw new NullPointerException("fileDescriptor cannot be null");
}
+
mAssetInputStream = null;
mFilename = null;
// When FileDescriptor is duplicated and set to FileInputStream, ownership needs to be
@@ -1428,26 +1444,61 @@ public class ExifInterface {
/**
* Reads Exif tags from the specified image input stream. Attribute mutation is not supported
- * for input streams. The given input stream will proceed its current position. Developers
+ * for input streams. The given input stream will proceed from its current position. Developers
* should close the input stream after use.
*/
public ExifInterface(@NonNull InputStream inputStream) throws IOException {
+ this(inputStream, false);
+ }
+
+ /**
+ * Reads Exif tags from the specified standalone input stream. Standalone data refers to Exif
+ * data that exists by itself and is not contained in a file format such as jpeg or png.
+ * The format of the standalone data must follow the below structure:
+ * Exif Identifier Code ("Exif\0\0") + TIFF header + IFD data
+ * See JEITA CP-3451C Section 4.5.2 and 4.5.4 specifications for more details.
+ * <p>
+ * Attribute mutation is not supported for this constructor. The given input stream will proceed
+ * from its current position. Developers should close the input stream after use. This
+ * constructor is not intended to be used with an input stream that performs any networking
+ * operations.
+ *
+ * @throws IOException if the data does not follow the aforementioned structure.
+ */
+ @NonNull
+ public static ExifInterface fromStandalone(@NonNull InputStream inputStream)
+ throws IOException {
+ if (isStandalone(inputStream)) {
+ return new ExifInterface(inputStream, true);
+ }
+ throw new IOException("Given data does not follow the structure of a standalone exif "
+ + "data.");
+ }
+
+ private ExifInterface(@NonNull InputStream inputStream, boolean isFromStandalone)
+ throws IOException {
if (inputStream == null) {
throw new NullPointerException("inputStream cannot be null");
}
mFilename = null;
- if (inputStream instanceof AssetManager.AssetInputStream) {
- mAssetInputStream = (AssetManager.AssetInputStream) inputStream;
- mSeekableFileDescriptor = null;
- } else if (inputStream instanceof FileInputStream
- && isSeekableFD(((FileInputStream) inputStream).getFD())) {
- mAssetInputStream = null;
- mSeekableFileDescriptor = ((FileInputStream) inputStream).getFD();
- } else {
+
+ if (isFromStandalone) {
+ mIsStandalone = true;
mAssetInputStream = null;
mSeekableFileDescriptor = null;
+ } else {
+ if (inputStream instanceof AssetManager.AssetInputStream) {
+ mAssetInputStream = (AssetManager.AssetInputStream) inputStream;
+ mSeekableFileDescriptor = null;
+ } else if (inputStream instanceof FileInputStream
+ && (isSeekableFD(((FileInputStream) inputStream).getFD()))) {
+ mAssetInputStream = null;
+ mSeekableFileDescriptor = ((FileInputStream) inputStream).getFD();
+ } else {
+ mAssetInputStream = null;
+ mSeekableFileDescriptor = null;
+ }
}
- mIsInputStream = true;
loadAttributes(inputStream);
}
@@ -1784,47 +1835,57 @@ public class ExifInterface {
}
// Check file type
- in = new BufferedInputStream(in, SIGNATURE_CHECK_SIZE);
- mMimeType = getMimeType((BufferedInputStream) in);
+ if (!mIsStandalone) {
+ in = new BufferedInputStream(in, SIGNATURE_CHECK_SIZE);
+ mMimeType = getMimeType((BufferedInputStream) in);
+ }
// Create byte-ordered input stream
ByteOrderedDataInputStream inputStream = new ByteOrderedDataInputStream(in);
- switch (mMimeType) {
- case IMAGE_TYPE_JPEG: {
- getJpegAttributes(inputStream, 0, IFD_TYPE_PRIMARY); // 0 is offset
- break;
- }
- case IMAGE_TYPE_RAF: {
- getRafAttributes(inputStream);
- break;
- }
- case IMAGE_TYPE_HEIF: {
- getHeifAttributes(inputStream);
- break;
- }
- case IMAGE_TYPE_ORF: {
- getOrfAttributes(inputStream);
- break;
- }
- case IMAGE_TYPE_RW2: {
- getRw2Attributes(inputStream);
- break;
- }
- case IMAGE_TYPE_ARW:
- case IMAGE_TYPE_CR2:
- case IMAGE_TYPE_DNG:
- case IMAGE_TYPE_NEF:
- case IMAGE_TYPE_NRW:
- case IMAGE_TYPE_PEF:
- case IMAGE_TYPE_SRW:
- case IMAGE_TYPE_UNKNOWN: {
- getRawAttributes(inputStream);
- break;
- }
- default: {
- break;
+ if (!mIsStandalone) {
+ switch (mMimeType) {
+ case IMAGE_TYPE_JPEG: {
+ getJpegAttributes(inputStream, 0, IFD_TYPE_PRIMARY); // 0 is offset
+ break;
+ }
+ case IMAGE_TYPE_RAF: {
+ getRafAttributes(inputStream);
+ break;
+ }
+ case IMAGE_TYPE_HEIF: {
+ getHeifAttributes(inputStream);
+ break;
+ }
+ case IMAGE_TYPE_ORF: {
+ getOrfAttributes(inputStream);
+ break;
+ }
+ case IMAGE_TYPE_RW2: {
+ getRw2Attributes(inputStream);
+ break;
+ }
+ case IMAGE_TYPE_PNG: {
+ getPngAttributes(inputStream);
+ break;
+ }
+ case IMAGE_TYPE_ARW:
+ case IMAGE_TYPE_CR2:
+ case IMAGE_TYPE_DNG:
+ case IMAGE_TYPE_NEF:
+ case IMAGE_TYPE_NRW:
+ case IMAGE_TYPE_PEF:
+ case IMAGE_TYPE_SRW:
+ case IMAGE_TYPE_UNKNOWN: {
+ getRawAttributes(inputStream);
+ break;
+ }
+ default: {
+ break;
+ }
}
+ } else {
+ getStandaloneAttributes(inputStream);
}
// Set thumbnail image offset and length
setThumbnailData(inputStream);
@@ -2024,6 +2085,7 @@ public class ExifInterface {
if (in.skip(mThumbnailOffset) != mThumbnailOffset) {
throw new IOException("Corrupted image");
}
+ // TODO: Need to handle potential OutOfMemoryError
byte[] buffer = new byte[mThumbnailLength];
if (in.read(buffer) != mThumbnailLength) {
throw new IOException("Corrupted image");
@@ -2105,6 +2167,9 @@ public class ExifInterface {
}
if (mHasThumbnail) {
+ if (mIsStandalone) {
+ return new long[] { mThumbnailOffset + mExifOffset, mThumbnailLength };
+ }
return new long[] { mThumbnailOffset, mThumbnailLength };
} else {
return null;
@@ -2349,6 +2414,7 @@ public class ExifInterface {
// Checks the type of image file
private int getMimeType(BufferedInputStream in) throws IOException {
+ // TODO (b/142218289): Need to handle case where input stream does not support mark
in.mark(SIGNATURE_CHECK_SIZE);
byte[] signatureCheckBytes = new byte[SIGNATURE_CHECK_SIZE];
in.read(signatureCheckBytes);
@@ -2363,6 +2429,8 @@ public class ExifInterface {
return IMAGE_TYPE_ORF;
} else if (isRw2Format(signatureCheckBytes)) {
return IMAGE_TYPE_RW2;
+ } else if (isPngFormat(signatureCheckBytes)) {
+ return IMAGE_TYPE_PNG;
}
// Certain file formats (PEF) are identified in readImageFileDirectory()
return IMAGE_TYPE_UNKNOWN;
@@ -2478,16 +2546,24 @@ public class ExifInterface {
* http://fileformats.archiveteam.org/wiki/Olympus_ORF
*/
private boolean isOrfFormat(byte[] signatureCheckBytes) throws IOException {
- ByteOrderedDataInputStream signatureInputStream =
- new ByteOrderedDataInputStream(signatureCheckBytes);
- // Read byte order
- mExifByteOrder = readByteOrder(signatureInputStream);
- // Set byte order
- signatureInputStream.setByteOrder(mExifByteOrder);
+ ByteOrderedDataInputStream signatureInputStream = null;
- short orfSignature = signatureInputStream.readShort();
- if (orfSignature == ORF_SIGNATURE_1 || orfSignature == ORF_SIGNATURE_2) {
- return true;
+ try {
+ signatureInputStream = new ByteOrderedDataInputStream(signatureCheckBytes);
+
+ // Read byte order
+ mExifByteOrder = readByteOrder(signatureInputStream);
+ // Set byte order
+ signatureInputStream.setByteOrder(mExifByteOrder);
+
+ short orfSignature = signatureInputStream.readShort();
+ return orfSignature == ORF_SIGNATURE_1 || orfSignature == ORF_SIGNATURE_2;
+ } catch (Exception e) {
+ // Do nothing
+ } finally {
+ if (signatureInputStream != null) {
+ signatureInputStream.close();
+ }
}
return false;
}
@@ -2497,21 +2573,55 @@ public class ExifInterface {
* See http://lclevy.free.fr/raw/
*/
private boolean isRw2Format(byte[] signatureCheckBytes) throws IOException {
- ByteOrderedDataInputStream signatureInputStream =
- new ByteOrderedDataInputStream(signatureCheckBytes);
- // Read byte order
- mExifByteOrder = readByteOrder(signatureInputStream);
- // Set byte order
- signatureInputStream.setByteOrder(mExifByteOrder);
+ ByteOrderedDataInputStream signatureInputStream = null;
- short signatureByte = signatureInputStream.readShort();
- if (signatureByte == RW2_SIGNATURE) {
- return true;
+ try {
+ signatureInputStream = new ByteOrderedDataInputStream(signatureCheckBytes);
+
+ // Read byte order
+ mExifByteOrder = readByteOrder(signatureInputStream);
+ // Set byte order
+ signatureInputStream.setByteOrder(mExifByteOrder);
+
+ short signatureByte = signatureInputStream.readShort();
+ signatureInputStream.close();
+ return signatureByte == RW2_SIGNATURE;
+ } catch (Exception e) {
+ // Do nothing
+ } finally {
+ if (signatureInputStream != null) {
+ signatureInputStream.close();
+ }
}
return false;
}
/**
+ * PNG's file signature is first 8 bytes.
+ * See PNG (Portable Network Graphics) Specification, Version 1.2, 3.1. PNG file signature
+ */
+ private boolean isPngFormat(byte[] signatureCheckBytes) throws IOException {
+ for (int i = 0; i < PNG_SIGNATURE.length; i++) {
+ if (signatureCheckBytes[i] != PNG_SIGNATURE[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean isStandalone(InputStream inputStream) throws IOException {
+ byte[] signatureCheckBytes = new byte[IDENTIFIER_EXIF_APP1.length];
+ inputStream.read(signatureCheckBytes);
+
+ for (int i = 0; i < IDENTIFIER_EXIF_APP1.length; i++) {
+ if (signatureCheckBytes[i] != IDENTIFIER_EXIF_APP1[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* Loads EXIF attributes from a JPEG input stream.
*
* @param in The input stream that starts with the JPEG data.
@@ -2582,10 +2692,9 @@ public class ExifInterface {
final long offset = start + IDENTIFIER_EXIF_APP1.length;
final byte[] value = Arrays.copyOfRange(bytes,
IDENTIFIER_EXIF_APP1.length, bytes.length);
-
readExifSegment(value, imageType);
- // Save offset values for createJpegThumbnailBitmap() function
+ // Save offset values for handleThumbnailFromJfif() function
mExifOffset = (int) offset;
} else if (ArrayUtils.startsWith(bytes, IDENTIFIER_XMP_APP1)) {
// See XMP Specification Part 3: Storage in Files, 1.1.3 JPEG, Table 6
@@ -2667,7 +2776,7 @@ public class ExifInterface {
updateImageSizeValues(in, IFD_TYPE_THUMBNAIL);
// Check if each image data is in valid position.
- validateImages(in);
+ validateImages();
if (mMimeType == IMAGE_TYPE_PEF) {
// PEF files contain a MakerNote data, which contains the data for ColorSpace tag.
@@ -2886,6 +2995,7 @@ public class ExifInterface {
throw new IOException("Invalid identifier");
}
+ // TODO: Need to handle potential OutOfMemoryError
byte[] bytes = new byte[length];
if (in.read(bytes) != length) {
throw new IOException("Can't read exif");
@@ -2901,6 +3011,16 @@ public class ExifInterface {
}
}
+ private void getStandaloneAttributes(ByteOrderedDataInputStream in) throws IOException {
+ // TODO: Need to handle potential OutOfMemoryError
+ byte[] data = new byte[in.available()];
+ in.readFully(data);
+ readExifSegment(data, IFD_TYPE_PRIMARY);
+
+ // Save offset values for handleThumbnailFromJfif() function
+ mExifOffset = IDENTIFIER_EXIF_APP1.length;
+ }
+
/**
* ORF files contains a primary image data and a MakerNote data that contains preview/thumbnail
* images. Both data takes the form of IFDs and can therefore be read with the
@@ -3012,6 +3132,74 @@ public class ExifInterface {
}
}
+ // PNG contains the EXIF data as a Special-Purpose Chunk
+ private void getPngAttributes(ByteOrderedDataInputStream in) throws IOException {
+ if (DEBUG) {
+ Log.d(TAG, "getPngAttributes starting with: " + in);
+ }
+
+ // PNG uses Big Endian by default.
+ // See PNG (Portable Network Graphics) Specification, Version 1.2,
+ // 2.1. Integers and byte order
+ in.setByteOrder(ByteOrder.BIG_ENDIAN);
+
+ int bytesRead = 0;
+
+ // Skip the signature bytes
+ in.seek(PNG_SIGNATURE.length);
+ bytesRead += PNG_SIGNATURE.length;
+
+ try {
+ while (true) {
+ // Each chunk is made up of four parts:
+ // 1) Length: 4-byte unsigned integer indicating the number of bytes in the
+ // Chunk Data field. Excludes Chunk Type and CRC bytes.
+ // 2) Chunk Type: 4-byte chunk type code.
+ // 3) Chunk Data: The data bytes. Can be zero-length.
+ // 4) CRC: 4-byte data calculated on the preceding bytes in the chunk. Always
+ // present.
+ // --> 4 (length bytes) + 4 (type bytes) + X (data bytes) + 4 (CRC bytes)
+ // See PNG (Portable Network Graphics) Specification, Version 1.2,
+ // 3.2. Chunk layout
+ int length = in.readInt();
+ bytesRead += 4;
+
+ byte[] type = new byte[PNG_CHUNK_LENGTH_BYTE_LENGTH];
+ if (in.read(type) != type.length) {
+ throw new IOException("Encountered invalid length while parsing PNG chunk"
+ + "type");
+ }
+ bytesRead += PNG_CHUNK_LENGTH_BYTE_LENGTH;
+
+ if (Arrays.equals(type, PNG_CHUNK_TYPE_IEND)) {
+ // IEND marks the end of the image.
+ break;
+ } else if (Arrays.equals(type, PNG_CHUNK_TYPE_EXIF)) {
+ // TODO: Need to handle potential OutOfMemoryError
+ byte[] data = new byte[length];
+ if (in.read(data) != length) {
+ throw new IOException("Failed to read given length for given PNG chunk "
+ + "type: " + byteArrayToHexString(type));
+ }
+ readExifSegment(data, IFD_TYPE_PRIMARY);
+
+ validateImages();
+ break;
+ } else {
+ // Skip to next chunk
+ in.skipBytes(length + PNG_CHUNK_CRC_BYTE_LENGTH);
+ bytesRead += length + PNG_CHUNK_CRC_BYTE_LENGTH;
+ }
+ }
+ // Save offset values for handleThumbnailFromJfif() function
+ mExifOffset = bytesRead;
+ } catch (EOFException e) {
+ // Should not reach here. Will only reach here if the file is corrupted or
+ // does not follow the PNG specifications
+ throw new IOException("Encountered corrupt PNG file.");
+ }
+ }
+
// Stores a new JPEG image with EXIF attributes into a given output stream.
private void saveJpegAttributes(InputStream inputStream, OutputStream outputStream)
throws IOException {
@@ -3496,15 +3684,16 @@ public class ExifInterface {
int thumbnailOffset = jpegInterchangeFormatAttribute.getIntValue(mExifByteOrder);
int thumbnailLength = jpegInterchangeFormatLengthAttribute.getIntValue(mExifByteOrder);
- // The following code limits the size of thumbnail size not to overflow EXIF data area.
- thumbnailLength = Math.min(thumbnailLength, in.getLength() - thumbnailOffset);
if (mMimeType == IMAGE_TYPE_JPEG || mMimeType == IMAGE_TYPE_RAF
- || mMimeType == IMAGE_TYPE_RW2) {
+ || mMimeType == IMAGE_TYPE_RW2 || mMimeType == IMAGE_TYPE_PNG) {
thumbnailOffset += mExifOffset;
} else if (mMimeType == IMAGE_TYPE_ORF) {
// Update offset value since RAF files have IFD data preceding MakerNote data.
thumbnailOffset += mOrfMakerNoteOffset;
}
+ // The following code limits the size of thumbnail size not to overflow EXIF data area.
+ thumbnailLength = Math.min(thumbnailLength, in.getLength() - thumbnailOffset);
+
if (DEBUG) {
Log.d(TAG, "Setting thumbnail attributes with offset: " + thumbnailOffset
+ ", length: " + thumbnailLength);
@@ -3517,6 +3706,7 @@ public class ExifInterface {
if (mFilename == null && mAssetInputStream == null
&& mSeekableFileDescriptor == null) {
+ // TODO: Need to handle potential OutOfMemoryError
// Save the thumbnail in memory if the input doesn't support reading again.
byte[] thumbnailBytes = new byte[thumbnailLength];
in.seek(thumbnailOffset);
@@ -3550,6 +3740,7 @@ public class ExifInterface {
return;
}
+ // TODO: Need to handle potential OutOfMemoryError
// Set thumbnail byte array data for non-consecutive strip bytes
byte[] totalStripBytes =
new byte[(int) Arrays.stream(stripByteCounts).sum()];
@@ -3568,6 +3759,7 @@ public class ExifInterface {
in.seek(skipBytes);
bytesRead += skipBytes;
+ // TODO: Need to handle potential OutOfMemoryError
// Read strip bytes
byte[] stripBytes = new byte[stripByteCount];
in.read(stripBytes);
@@ -3637,12 +3829,13 @@ public class ExifInterface {
}
// Validate primary, preview, thumbnail image data by comparing image size
- private void validateImages(InputStream in) throws IOException {
+ private void validateImages() throws IOException {
// Swap images based on size (primary > preview > thumbnail)
swapBasedOnImageSize(IFD_TYPE_PRIMARY, IFD_TYPE_PREVIEW);
swapBasedOnImageSize(IFD_TYPE_PRIMARY, IFD_TYPE_THUMBNAIL);
swapBasedOnImageSize(IFD_TYPE_PREVIEW, IFD_TYPE_THUMBNAIL);
+ // TODO (b/142296453): Revise image width/height setting logic
// Check if image has PixelXDimension/PixelYDimension tags, which contain valid image
// sizes, excluding padding at the right end or bottom end of the image to make sure that
// the values are multiples of 64. See JEITA CP-3451C Table 5 and Section 4.8.1. B.
@@ -4010,6 +4203,7 @@ public class ExifInterface {
mDataInputStream = new DataInputStream(in);
mLength = mDataInputStream.available();
mPosition = 0;
+ // TODO (b/142218289): Need to handle case where input stream does not support mark
mDataInputStream.mark(mLength);
}
@@ -4025,6 +4219,7 @@ public class ExifInterface {
if (mPosition > byteCount) {
mPosition = 0;
mDataInputStream.reset();
+ // TODO (b/142218289): Need to handle case where input stream does not support mark
mDataInputStream.mark(mLength);
} else {
byteCount -= mPosition;
@@ -4367,4 +4562,12 @@ public class ExifInterface {
}
return null;
}
+
+ private static String byteArrayToHexString(byte[] bytes) {
+ StringBuilder sb = new StringBuilder(bytes.length * 2);
+ for (int i = 0; i < bytes.length; i++) {
+ sb.append(String.format("%02x", bytes[i]));
+ }
+ return sb.toString();
+ }
}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index a790441aa36e..ea1630d6988d 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -106,8 +106,12 @@ interface IAudioService {
List<AudioProductStrategy> getAudioProductStrategies();
+ boolean isMicrophoneMuted();
+
void setMicrophoneMute(boolean on, String callingPackage, int userId);
+ oneway void setMicrophoneMuteFromSwitch(boolean on);
+
void setRingerModeExternal(int ringerMode, String caller);
void setRingerModeInternal(int ringerMode, String caller);
diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl
index f132cefbfdc7..66764c73ee5c 100644
--- a/media/java/android/media/IMediaRoute2Provider.aidl
+++ b/media/java/android/media/IMediaRoute2Provider.aidl
@@ -27,4 +27,6 @@ oneway interface IMediaRoute2Provider {
void selectRoute(String packageName, String id);
void unselectRoute(String packageName, String id);
void notifyControlRequestSent(String id, in Intent request);
+ void requestSetVolume(String id, int volume);
+ void requestUpdateVolume(String id, int delta);
}
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 1b713b6ab74b..7b7a34e5151f 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -40,11 +40,14 @@ interface IMediaRouterService {
void setSelectedRoute(IMediaRouterClient client, String routeId, boolean explicit);
void requestSetVolume(IMediaRouterClient client, String routeId, int volume);
void requestUpdateVolume(IMediaRouterClient client, String routeId, int direction);
+ void setControlCategories(IMediaRouterClient client, in List<String> categories);
// Methods for media router 2
void registerClient2(IMediaRouter2Client client, String packageName);
void unregisterClient2(IMediaRouter2Client client);
void sendControlRequest(IMediaRouter2Client client, in MediaRoute2Info route, in Intent request);
+ void requestSetVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int volume);
+ void requestUpdateVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int direction);
/**
* Changes the selected route of the client.
*
@@ -52,7 +55,7 @@ interface IMediaRouterService {
* @param route the route to be selected
*/
void selectRoute2(IMediaRouter2Client client, in @nullable MediaRoute2Info route);
- void setControlCategories(IMediaRouter2Client client, in List<String> categories);
+ void setControlCategories2(IMediaRouter2Client client, in List<String> categories);
void registerManager(IMediaRouter2Manager manager, String packageName);
void unregisterManager(IMediaRouter2Manager manager);
@@ -65,4 +68,9 @@ interface IMediaRouterService {
*/
void selectClientRoute2(IMediaRouter2Manager manager, String packageName,
in @nullable MediaRoute2Info route);
+
+ void requestSetVolume2Manager(IMediaRouter2Manager manager,
+ in MediaRoute2Info route, int volume);
+ void requestUpdateVolume2Manager(IMediaRouter2Manager manager,
+ in MediaRoute2Info route, int direction);
}
diff --git a/media/java/android/media/JetPlayer.java b/media/java/android/media/JetPlayer.java
index b12e647acc4b..e85b3ff99104 100644
--- a/media/java/android/media/JetPlayer.java
+++ b/media/java/android/media/JetPlayer.java
@@ -17,18 +17,17 @@
package android.media;
-import java.io.FileDescriptor;
-import java.lang.ref.WeakReference;
-import java.lang.CloneNotSupportedException;
-
import android.annotation.UnsupportedAppUsage;
import android.content.res.AssetFileDescriptor;
-import android.os.Looper;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.util.AndroidRuntimeException;
import android.util.Log;
+import java.io.FileDescriptor;
+import java.lang.ref.WeakReference;
+
/**
* JetPlayer provides access to JET content playback and control.
*
@@ -120,6 +119,9 @@ public class JetPlayer
private static JetPlayer singletonRef;
+ static {
+ System.loadLibrary("media_jni");
+ }
//--------------------------------
// Used exclusively by native code
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 510ee442f852..b7a9ffe93bae 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -649,7 +649,7 @@ import java.util.concurrent.locks.ReentrantLock;
are not consumed by the Surface in a timely manner). Or it may be configured to not drop excessive
frames. In the latter mode if the Surface is not consuming output frames fast enough, it will
eventually block the decoder. Prior to {@link android.os.Build.VERSION_CODES#Q} the exact behavior
- was undefined, with the exception that View surfaces (SuerfaceView or TextureView) always dropped
+ was undefined, with the exception that View surfaces (SurfaceView or TextureView) always dropped
excessive frames. Since {@link android.os.Build.VERSION_CODES#Q} the default behavior is to drop
excessive frames. Applications can opt out of this behavior for non-View surfaces (such as
ImageReader or SurfaceTexture) by targeting SDK {@link android.os.Build.VERSION_CODES#Q} and
@@ -3513,6 +3513,19 @@ final public class MediaCodec {
public static final String PARAMETER_KEY_HDR10_PLUS_INFO = MediaFormat.KEY_HDR10_PLUS_INFO;
/**
+ * Enable/disable low latency decoding mode.
+ * When enabled, the decoder doesn't hold input and output data more than
+ * required by the codec standards.
+ * The value is an Integer object containing the value 1 to enable
+ * or the value 0 to disable.
+ *
+ * @see #setParameters(Bundle)
+ * @see MediaFormat#KEY_LOW_LATENCY
+ */
+ public static final String PARAMETER_KEY_LOW_LATENCY =
+ MediaFormat.KEY_LOW_LATENCY;
+
+ /**
* Communicate additional parameter changes to the component instance.
* <b>Note:</b> Some of these parameter changes may silently fail to apply.
*
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index f304f7cc5981..91d644b5db74 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -21,6 +21,7 @@ import static android.media.Utils.sortDistinctRanges;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.os.Build;
@@ -559,6 +560,14 @@ public final class MediaCodecInfo {
public static final String FEATURE_IntraRefresh = "intra-refresh";
/**
+ * <b>decoder only</b>: codec supports low latency decoding.
+ * If supported, clients can enable the low latency mode for the decoder.
+ * When the mode is enabled, the decoder doesn't hold input and output data more than
+ * required by the codec standards.
+ */
+ public static final String FEATURE_LowLatency = "low-latency";
+
+ /**
* Query codec feature capabilities.
* <p>
* These features are supported to be used by the codec. These
@@ -587,6 +596,7 @@ public final class MediaCodecInfo {
new Feature(FEATURE_FrameParsing, (1 << 4), false),
new Feature(FEATURE_MultipleFrames, (1 << 5), false),
new Feature(FEATURE_DynamicTimestamp, (1 << 6), false),
+ new Feature(FEATURE_LowLatency, (1 << 7), true),
};
private static final Feature[] encoderFeatures = {
@@ -3741,8 +3751,11 @@ public final class MediaCodecInfo {
public static final int DolbyVisionProfileDvheStn = 0x20;
public static final int DolbyVisionProfileDvheDth = 0x40;
public static final int DolbyVisionProfileDvheDtb = 0x80;
- public static final int DolbyVisionProfileDvheSt = 0x100;
- public static final int DolbyVisionProfileDvavSe = 0x200;
+ public static final int DolbyVisionProfileDvheSt = 0x100;
+ public static final int DolbyVisionProfileDvavSe = 0x200;
+ /** Dolby Vision AV1 profile */
+ @SuppressLint("AllUpper")
+ public static final int DolbyVisionProfileDvav110 = 0x400;
public static final int DolbyVisionLevelHd24 = 0x1;
public static final int DolbyVisionLevelHd30 = 0x2;
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index 06bca5bd6e95..f4957c6824a3 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -23,7 +23,7 @@ import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.mtp.MtpConstants;
-import libcore.net.MimeMap;
+import libcore.content.type.MimeMap;
import java.util.HashMap;
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 8b667f772867..8080f45642dc 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -211,6 +211,15 @@ public final class MediaFormat {
public static final String KEY_MIME = "mime";
/**
+ * An optional key describing the low latency decoding mode. This is an optional parameter
+ * that applies only to decoders. If enabled, the decoder doesn't hold input and output
+ * data more than required by the codec standards.
+ * The associated value is an integer (0 or 1): 1 when low-latency decoding is enabled,
+ * 0 otherwise. The default value is 0.
+ */
+ public static final String KEY_LOW_LATENCY = "low-latency";
+
+ /**
* A key describing the language of the content, using either ISO 639-1
* or 639-2/T codes. The associated value is a string.
*/
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 0346010f4587..cc5ddeb49813 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -224,7 +224,45 @@ public class MediaMetadataRetriever implements AutoCloseable {
* @return The meta data value associate with the given keyCode on success;
* null on failure.
*/
- public native String extractMetadata(int keyCode);
+ public native @Nullable String extractMetadata(int keyCode);
+
+ /**
+ * This method is similar to {@link #getFrameAtTime(long, int, BitmapParams)}
+ * except that the device will choose the actual {@link Bitmap.Config} to use.
+ *
+ * @param timeUs The time position where the frame will be retrieved.
+ * When retrieving the frame at the given time position, there is no
+ * guarantee that the data source has a frame located at the position.
+ * When this happens, a frame nearby will be returned. If timeUs is
+ * negative, time position and option will ignored, and any frame
+ * that the implementation considers as representative may be returned.
+ *
+ * @param option a hint on how the frame is found. Use
+ * {@link #OPTION_PREVIOUS_SYNC} if one wants to retrieve a sync frame
+ * that has a timestamp earlier than or the same as timeUs. Use
+ * {@link #OPTION_NEXT_SYNC} if one wants to retrieve a sync frame
+ * that has a timestamp later than or the same as timeUs. Use
+ * {@link #OPTION_CLOSEST_SYNC} if one wants to retrieve a sync frame
+ * that has a timestamp closest to or the same as timeUs. Use
+ * {@link #OPTION_CLOSEST} if one wants to retrieve a frame that may
+ * or may not be a sync frame but is closest to or the same as timeUs.
+ * {@link #OPTION_CLOSEST} often has larger performance overhead compared
+ * to the other options if there is no sync frame located at timeUs.
+ *
+ * @return A Bitmap containing a representative video frame, which can be null,
+ * if such a frame cannot be retrieved. {@link Bitmap#getConfig()} can
+ * be used to query the actual {@link Bitmap.Config}.
+ *
+ * @see {@link #getFrameAtTime(long, int, BitmapParams)}
+ */
+ public @Nullable Bitmap getFrameAtTime(long timeUs, @Option int option) {
+ if (option < OPTION_PREVIOUS_SYNC ||
+ option > OPTION_CLOSEST) {
+ throw new IllegalArgumentException("Unsupported option: " + option);
+ }
+
+ return _getFrameAtTime(timeUs, option, -1 /*dst_width*/, -1 /*dst_height*/, null);
+ }
/**
* Call this method after setDataSource(). This method finds a
@@ -255,16 +293,60 @@ public class MediaMetadataRetriever implements AutoCloseable {
* {@link #OPTION_CLOSEST} often has larger performance overhead compared
* to the other options if there is no sync frame located at timeUs.
*
+ * @param params BitmapParams that controls the returned bitmap config
+ * (such as pixel formats).
+ *
* @return A Bitmap containing a representative video frame, which
* can be null, if such a frame cannot be retrieved.
+ *
+ * @see {@link #getFrameAtTime(long, int)}
*/
- public Bitmap getFrameAtTime(long timeUs, @Option int option) {
+ public @Nullable Bitmap getFrameAtTime(
+ long timeUs, @Option int option, @NonNull BitmapParams params) {
if (option < OPTION_PREVIOUS_SYNC ||
option > OPTION_CLOSEST) {
throw new IllegalArgumentException("Unsupported option: " + option);
}
- return _getFrameAtTime(timeUs, option, -1 /*dst_width*/, -1 /*dst_height*/);
+ return _getFrameAtTime(timeUs, option, -1 /*dst_width*/, -1 /*dst_height*/, params);
+ }
+
+ /**
+ * This method is similar to {@link #getScaledFrameAtTime(long, int, int, int, BitmapParams)}
+ * except that the device will choose the actual {@link Bitmap.Config} to use.
+ *
+ * @param timeUs The time position in microseconds where the frame will be retrieved.
+ * When retrieving the frame at the given time position, there is no
+ * guarantee that the data source has a frame located at the position.
+ * When this happens, a frame nearby will be returned. If timeUs is
+ * negative, time position and option will ignored, and any frame
+ * that the implementation considers as representative may be returned.
+ *
+ * @param option a hint on how the frame is found. Use
+ * {@link #OPTION_PREVIOUS_SYNC} if one wants to retrieve a sync frame
+ * that has a timestamp earlier than or the same as timeUs. Use
+ * {@link #OPTION_NEXT_SYNC} if one wants to retrieve a sync frame
+ * that has a timestamp later than or the same as timeUs. Use
+ * {@link #OPTION_CLOSEST_SYNC} if one wants to retrieve a sync frame
+ * that has a timestamp closest to or the same as timeUs. Use
+ * {@link #OPTION_CLOSEST} if one wants to retrieve a frame that may
+ * or may not be a sync frame but is closest to or the same as timeUs.
+ * {@link #OPTION_CLOSEST} often has larger performance overhead compared
+ * to the other options if there is no sync frame located at timeUs.
+ *
+ * @param dstWidth expected output bitmap width
+ * @param dstHeight expected output bitmap height
+ * @return A Bitmap containing a representative video frame, which can be null,
+ * if such a frame cannot be retrieved. {@link Bitmap#getConfig()} can
+ * be used to query the actual {@link Bitmap.Config}.
+ * @throws IllegalArgumentException if passed in invalid option or width by height
+ * is less than or equal to 0.
+ * @see {@link #getScaledFrameAtTime(long, int, int, int, BitmapParams)}
+ */
+ public @Nullable Bitmap getScaledFrameAtTime(
+ long timeUs, @Option int option, int dstWidth, int dstHeight) {
+ validate(option, dstWidth, dstHeight);
+ return _getFrameAtTime(timeUs, option, dstWidth, dstHeight, null);
}
/**
@@ -297,15 +379,23 @@ public class MediaMetadataRetriever implements AutoCloseable {
*
* @param dstWidth expected output bitmap width
* @param dstHeight expected output bitmap height
+ * @param params BitmapParams that controls the returned bitmap config
+ * (such as pixel formats).
+ *
* @return A Bitmap of size not larger than dstWidth by dstHeight containing a
* scaled video frame, which can be null, if such a frame cannot be retrieved.
* @throws IllegalArgumentException if passed in invalid option or width by height
* is less than or equal to 0.
+ * @see {@link #getScaledFrameAtTime(long, int, int, int)}
*/
- public Bitmap getScaledFrameAtTime(
- long timeUs, @Option int option, int dstWidth, int dstHeight) {
- if (option < OPTION_PREVIOUS_SYNC ||
- option > OPTION_CLOSEST) {
+ public @Nullable Bitmap getScaledFrameAtTime(long timeUs, @Option int option,
+ int dstWidth, int dstHeight, @NonNull BitmapParams params) {
+ validate(option, dstWidth, dstHeight);
+ return _getFrameAtTime(timeUs, option, dstWidth, dstHeight, params);
+ }
+
+ private void validate(@Option int option, int dstWidth, int dstHeight) {
+ if (option < OPTION_PREVIOUS_SYNC || option > OPTION_CLOSEST) {
throw new IllegalArgumentException("Unsupported option: " + option);
}
if (dstWidth <= 0) {
@@ -314,8 +404,6 @@ public class MediaMetadataRetriever implements AutoCloseable {
if (dstHeight <= 0) {
throw new IllegalArgumentException("Invalid height: " + dstHeight);
}
-
- return _getFrameAtTime(timeUs, option, dstWidth, dstHeight);
}
/**
@@ -342,7 +430,7 @@ public class MediaMetadataRetriever implements AutoCloseable {
*
* @see #getFrameAtTime(long, int)
*/
- public Bitmap getFrameAtTime(long timeUs) {
+ public @Nullable Bitmap getFrameAtTime(long timeUs) {
return getFrameAtTime(timeUs, OPTION_CLOSEST_SYNC);
}
@@ -364,11 +452,13 @@ public class MediaMetadataRetriever implements AutoCloseable {
* @see #getFrameAtTime(long)
* @see #getFrameAtTime(long, int)
*/
- public Bitmap getFrameAtTime() {
- return _getFrameAtTime(-1, OPTION_CLOSEST_SYNC, -1 /*dst_width*/, -1 /*dst_height*/);
+ public @Nullable Bitmap getFrameAtTime() {
+ return _getFrameAtTime(
+ -1, OPTION_CLOSEST_SYNC, -1 /*dst_width*/, -1 /*dst_height*/, null);
}
- private native Bitmap _getFrameAtTime(long timeUs, int option, int width, int height);
+ private native Bitmap _getFrameAtTime(
+ long timeUs, int option, int width, int height, @Nullable BitmapParams params);
public static final class BitmapParams {
private Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;
@@ -438,7 +528,7 @@ public class MediaMetadataRetriever implements AutoCloseable {
* @see #getFramesAtIndex(int, int, BitmapParams)
* @see #getFramesAtIndex(int, int)
*/
- public Bitmap getFrameAtIndex(int frameIndex, @NonNull BitmapParams params) {
+ public @Nullable Bitmap getFrameAtIndex(int frameIndex, @NonNull BitmapParams params) {
List<Bitmap> bitmaps = getFramesAtIndex(frameIndex, 1, params);
return bitmaps.get(0);
}
@@ -460,7 +550,7 @@ public class MediaMetadataRetriever implements AutoCloseable {
* @see #getFramesAtIndex(int, int, BitmapParams)
* @see #getFramesAtIndex(int, int)
*/
- public Bitmap getFrameAtIndex(int frameIndex) {
+ public @Nullable Bitmap getFrameAtIndex(int frameIndex) {
List<Bitmap> bitmaps = getFramesAtIndex(frameIndex, 1);
return bitmaps.get(0);
}
@@ -563,7 +653,7 @@ public class MediaMetadataRetriever implements AutoCloseable {
* @see #getPrimaryImage(BitmapParams)
* @see #getPrimaryImage()
*/
- public Bitmap getImageAtIndex(int imageIndex, @NonNull BitmapParams params) {
+ public @Nullable Bitmap getImageAtIndex(int imageIndex, @NonNull BitmapParams params) {
return getImageAtIndexInternal(imageIndex, params);
}
@@ -601,7 +691,7 @@ public class MediaMetadataRetriever implements AutoCloseable {
* @see #getPrimaryImage(BitmapParams)
* @see #getPrimaryImage()
*/
- public Bitmap getImageAtIndex(int imageIndex) {
+ public @Nullable Bitmap getImageAtIndex(int imageIndex) {
return getImageAtIndexInternal(imageIndex, null);
}
@@ -623,7 +713,7 @@ public class MediaMetadataRetriever implements AutoCloseable {
* @see #getImageAtIndex(int)
* @see #getPrimaryImage()
*/
- public Bitmap getPrimaryImage(@NonNull BitmapParams params) {
+ public @Nullable Bitmap getPrimaryImage(@NonNull BitmapParams params) {
return getImageAtIndexInternal(-1, params);
}
@@ -639,7 +729,7 @@ public class MediaMetadataRetriever implements AutoCloseable {
* @see #getImageAtIndex(int)
* @see #getPrimaryImage(BitmapParams)
*/
- public Bitmap getPrimaryImage() {
+ public @Nullable Bitmap getPrimaryImage() {
return getImageAtIndexInternal(-1, null);
}
@@ -665,7 +755,7 @@ public class MediaMetadataRetriever implements AutoCloseable {
*
* @return null if no such graphic is found.
*/
- public byte[] getEmbeddedPicture() {
+ public @Nullable byte[] getEmbeddedPicture() {
return getEmbeddedPicture(EMBEDDED_PICTURE_TYPE_ANY);
}
@@ -938,8 +1028,6 @@ public class MediaMetadataRetriever implements AutoCloseable {
* @see MediaFormat#COLOR_STANDARD_BT601_PAL
* @see MediaFormat#COLOR_STANDARD_BT601_NTSC
* @see MediaFormat#COLOR_STANDARD_BT2020
- *
- * @hide
*/
public static final int METADATA_KEY_COLOR_STANDARD = 35;
@@ -950,8 +1038,6 @@ public class MediaMetadataRetriever implements AutoCloseable {
* @see MediaFormat#COLOR_TRANSFER_SDR_VIDEO
* @see MediaFormat#COLOR_TRANSFER_ST2084
* @see MediaFormat#COLOR_TRANSFER_HLG
- *
- * @hide
*/
public static final int METADATA_KEY_COLOR_TRANSFER = 36;
@@ -960,8 +1046,6 @@ public class MediaMetadataRetriever implements AutoCloseable {
*
* @see MediaFormat#COLOR_RANGE_LIMITED
* @see MediaFormat#COLOR_RANGE_FULL
- *
- * @hide
*/
public static final int METADATA_KEY_COLOR_RANGE = 37;
// Add more here...
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 7906fa34d844..7d107ddbe978 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -89,6 +89,10 @@ import java.util.Vector;
* of audio/video files and streams. An example on how to use the methods in
* this class can be found in {@link android.widget.VideoView}.
*
+ * <p>MediaPlayer is not thread-safe. Creation of and all access to player instances
+ * should be on the same thread. If registering <a href="#Callbacks">callbacks</a>,
+ * the thread must have a Looper.
+ *
* <p>Topics covered here are:
* <ol>
* <li><a href="#StateDiagram">State Diagram</a>
@@ -305,7 +309,7 @@ import java.util.Vector;
* </li>
* <li>When the playback reaches the end of stream, the playback completes.
* <ul>
- * <li>If the looping mode was being set to <var>true</var>with
+ * <li>If the looping mode was being set to <var>true</var> with
* {@link #setLooping(boolean)}, the MediaPlayer object shall remain in
* the <em>Started</em> state.</li>
* <li>If the looping mode was set to <var>false
@@ -554,13 +558,13 @@ import java.util.Vector;
* possible runtime errors during playback or streaming. Registration for
* these events is done by properly setting the appropriate listeners (via calls
* to
- * {@link #setOnPreparedListener(OnPreparedListener)}setOnPreparedListener,
- * {@link #setOnVideoSizeChangedListener(OnVideoSizeChangedListener)}setOnVideoSizeChangedListener,
- * {@link #setOnSeekCompleteListener(OnSeekCompleteListener)}setOnSeekCompleteListener,
- * {@link #setOnCompletionListener(OnCompletionListener)}setOnCompletionListener,
- * {@link #setOnBufferingUpdateListener(OnBufferingUpdateListener)}setOnBufferingUpdateListener,
- * {@link #setOnInfoListener(OnInfoListener)}setOnInfoListener,
- * {@link #setOnErrorListener(OnErrorListener)}setOnErrorListener, etc).
+ * {@link #setOnPreparedListener(OnPreparedListener) setOnPreparedListener},
+ * {@link #setOnVideoSizeChangedListener(OnVideoSizeChangedListener) setOnVideoSizeChangedListener},
+ * {@link #setOnSeekCompleteListener(OnSeekCompleteListener) setOnSeekCompleteListener},
+ * {@link #setOnCompletionListener(OnCompletionListener) setOnCompletionListener},
+ * {@link #setOnBufferingUpdateListener(OnBufferingUpdateListener) setOnBufferingUpdateListener},
+ * {@link #setOnInfoListener(OnInfoListener) setOnInfoListener},
+ * {@link #setOnErrorListener(OnErrorListener) setOnErrorListener}, etc).
* In order to receive the respective callback
* associated with these listeners, applications are required to create
* MediaPlayer objects on a thread with its own Looper running (main UI
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 5dcbb05184fe..59bd96ffb1bd 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -46,6 +46,21 @@ public final class MediaRoute2Info implements Parcelable {
}
};
+ /**
+ * Playback information indicating the playback volume is fixed, i&#46;e&#46; it cannot be
+ * controlled from this object. An example of fixed playback volume is a remote player,
+ * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
+ * than attenuate at the source.
+ * @see #getVolumeHandling()
+ */
+ public static final int PLAYBACK_VOLUME_FIXED = 0;
+ /**
+ * Playback information indicating the playback volume is variable and can be controlled
+ * from this object.
+ * @see #getVolumeHandling()
+ */
+ public static final int PLAYBACK_VOLUME_VARIABLE = 1;
+
@NonNull
final String mId;
@Nullable
@@ -58,9 +73,14 @@ public final class MediaRoute2Info implements Parcelable {
final String mClientPackageName;
@NonNull
final List<String> mSupportedCategories;
+ final int mVolume;
+ final int mVolumeMax;
+ final int mVolumeHandling;
@Nullable
final Bundle mExtras;
+ private final String mUniqueId;
+
MediaRoute2Info(@NonNull Builder builder) {
mId = builder.mId;
mProviderId = builder.mProviderId;
@@ -68,7 +88,11 @@ public final class MediaRoute2Info implements Parcelable {
mDescription = builder.mDescription;
mClientPackageName = builder.mClientPackageName;
mSupportedCategories = builder.mSupportedCategories;
+ mVolume = builder.mVolume;
+ mVolumeMax = builder.mVolumeMax;
+ mVolumeHandling = builder.mVolumeHandling;
mExtras = builder.mExtras;
+ mUniqueId = createUniqueId();
}
MediaRoute2Info(@NonNull Parcel in) {
@@ -78,7 +102,19 @@ public final class MediaRoute2Info implements Parcelable {
mDescription = in.readString();
mClientPackageName = in.readString();
mSupportedCategories = in.createStringArrayList();
+ mVolume = in.readInt();
+ mVolumeMax = in.readInt();
+ mVolumeHandling = in.readInt();
mExtras = in.readBundle();
+ mUniqueId = createUniqueId();
+ }
+
+ private String createUniqueId() {
+ String uniqueId = null;
+ if (mProviderId != null) {
+ uniqueId = mProviderId + ":" + mId;
+ }
+ return uniqueId;
}
/**
@@ -111,6 +147,9 @@ public final class MediaRoute2Info implements Parcelable {
&& Objects.equals(mDescription, other.mDescription)
&& Objects.equals(mClientPackageName, other.mClientPackageName)
&& Objects.equals(mSupportedCategories, other.mSupportedCategories)
+ && (mVolume == other.mVolume)
+ && (mVolumeMax == other.mVolumeMax)
+ && (mVolumeHandling == other.mVolumeHandling)
//TODO: This will be evaluated as false in most cases. Try not to.
&& Objects.equals(mExtras, other.mExtras);
}
@@ -120,13 +159,33 @@ public final class MediaRoute2Info implements Parcelable {
return Objects.hash(mId, mName, mDescription, mSupportedCategories);
}
+ /**
+ * Gets the id of the route.
+ * Use {@link #getUniqueId()} if you need a unique identifier.
+ *
+ * @see #getUniqueId()
+ */
@NonNull
public String getId() {
return mId;
}
/**
- * Gets the provider id of the route.
+ * Gets the unique id of the route. A route obtained from
+ * {@link com.android.server.media.MediaRouterService} always has a unique id.
+ *
+ * @return unique id of the route or null if it has no unique id.
+ */
+ @Nullable
+ public String getUniqueId() {
+ return mUniqueId;
+ }
+
+ /**
+ * Gets the provider id of the route. It is assigned automatically by
+ * {@link com.android.server.media.MediaRouterService}.
+ *
+ * @return provider id of the route or null if it's not set.
* @hide
*/
@Nullable
@@ -162,6 +221,29 @@ public final class MediaRoute2Info implements Parcelable {
return mSupportedCategories;
}
+ /**
+ * Gets the current volume of the route. This may be invalid if the route is not selected.
+ */
+ public int getVolume() {
+ return mVolume;
+ }
+
+ /**
+ * Gets the maximum volume of the route.
+ */
+ public int getVolumeMax() {
+ return mVolumeMax;
+ }
+
+ /**
+ * Gets information about how volume is handled on the route.
+ *
+ * @return {@link #PLAYBACK_VOLUME_FIXED} or {@link #PLAYBACK_VOLUME_VARIABLE}
+ */
+ public int getVolumeHandling() {
+ return mVolumeHandling;
+ }
+
@Nullable
public Bundle getExtras() {
return mExtras;
@@ -199,6 +281,9 @@ public final class MediaRoute2Info implements Parcelable {
dest.writeString(mDescription);
dest.writeString(mClientPackageName);
dest.writeStringList(mSupportedCategories);
+ dest.writeInt(mVolume);
+ dest.writeInt(mVolumeMax);
+ dest.writeInt(mVolumeHandling);
dest.writeBundle(mExtras);
}
@@ -209,6 +294,9 @@ public final class MediaRoute2Info implements Parcelable {
.append("id=").append(getId())
.append(", name=").append(getName())
.append(", description=").append(getDescription())
+ .append(", volume=").append(getVolume())
+ .append(", volumeMax=").append(getVolumeMax())
+ .append(", volumeHandling=").append(getVolumeHandling())
.append(", providerId=").append(getProviderId())
.append(" }");
return result.toString();
@@ -224,6 +312,9 @@ public final class MediaRoute2Info implements Parcelable {
String mDescription;
String mClientPackageName;
List<String> mSupportedCategories;
+ int mVolume;
+ int mVolumeMax;
+ int mVolumeHandling;
Bundle mExtras;
public Builder(@NonNull String id, @NonNull String name) {
@@ -251,6 +342,9 @@ public final class MediaRoute2Info implements Parcelable {
mDescription = routeInfo.mDescription;
setClientPackageName(routeInfo.mClientPackageName);
setSupportedCategories(routeInfo.mSupportedCategories);
+ setVolume(routeInfo.mVolume);
+ setVolumeMax(routeInfo.mVolumeMax);
+ setVolumeHandling(routeInfo.mVolumeHandling);
if (routeInfo.mExtras != null) {
mExtras = new Bundle(routeInfo.mExtras);
}
@@ -275,7 +369,7 @@ public final class MediaRoute2Info implements Parcelable {
@NonNull
public Builder setProviderId(@NonNull String providerId) {
if (TextUtils.isEmpty(providerId)) {
- throw new IllegalArgumentException("id must not be null or empty");
+ throw new IllegalArgumentException("providerId must not be null or empty");
}
mProviderId = providerId;
return this;
@@ -345,6 +439,32 @@ public final class MediaRoute2Info implements Parcelable {
}
/**
+ * Sets the route's current volume, or 0 if unknown.
+ */
+ @NonNull
+ public Builder setVolume(int volume) {
+ mVolume = volume;
+ return this;
+ }
+
+ /**
+ * Sets the route's maximum volume, or 0 if unknown.
+ */
+ @NonNull
+ public Builder setVolumeMax(int volumeMax) {
+ mVolumeMax = volumeMax;
+ return this;
+ }
+
+ /**
+ * Sets the route's volume handling.
+ */
+ @NonNull
+ public Builder setVolumeHandling(int volumeHandling) {
+ mVolumeHandling = volumeHandling;
+ return this;
+ }
+ /**
* Sets a bundle of extras for the route.
*/
@NonNull
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index e8e0f826e6b6..5f5d200c6f5e 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -30,7 +30,7 @@ import android.util.Log;
* @hide
*/
public abstract class MediaRoute2ProviderService extends Service {
- private static final String TAG = "MediaRouteProviderSrv";
+ private static final String TAG = "MR2ProviderService";
public static final String SERVICE_INTERFACE = "android.media.MediaRoute2ProviderService";
@@ -81,6 +81,20 @@ public abstract class MediaRoute2ProviderService extends Service {
public abstract void onControlRequest(String routeId, Intent request);
/**
+ * Called when requestSetVolume is called on a route of the provider
+ * @param routeId the id of the route
+ * @param volume the target volume
+ */
+ public abstract void onSetVolume(String routeId, int volume);
+
+ /**
+ * Called when requestUpdateVolume is called on a route of the provider
+ * @param routeId id of the route
+ * @param delta the delta to add to the current volume
+ */
+ public abstract void onUpdateVolume(String routeId, int delta);
+
+ /**
* Updates provider info and publishes routes
*/
public final void setProviderInfo(MediaRoute2ProviderInfo info) {
@@ -130,5 +144,17 @@ public abstract class MediaRoute2ProviderService extends Service {
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onControlRequest,
MediaRoute2ProviderService.this, id, request));
}
+
+ @Override
+ public void requestSetVolume(String id, int volume) {
+ mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetVolume,
+ MediaRoute2ProviderService.this, id, volume));
+ }
+
+ @Override
+ public void requestUpdateVolume(String id, int delta) {
+ mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onUpdateVolume,
+ MediaRoute2ProviderService.this, id, delta));
+ }
}
}
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index d72231f40dcf..9cb78696f19b 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -49,6 +49,8 @@ import android.view.Display;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
@@ -82,6 +84,7 @@ public class MediaRouter {
final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
final ArrayList<RouteCategory> mCategories = new ArrayList<RouteCategory>();
+ List<String> mControlCategories = Collections.emptyList();
final RouteCategory mSystemCategory;
@@ -358,6 +361,18 @@ public class MediaRouter {
return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
}
+ public void setControlCategories(Collection<String> controlCategories) {
+ List<String> newControlCategories = new ArrayList<>(controlCategories);
+ mControlCategories = newControlCategories;
+ if (mClient != null) {
+ try {
+ mMediaRouterService.setControlCategories(mClient, newControlCategories);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to set control categories.", ex);
+ }
+ }
+ }
+
private void updatePresentationDisplays(int changedDisplayId) {
final int count = mRoutes.size();
for (int i = 0; i < count; i++) {
@@ -406,6 +421,7 @@ public class MediaRouter {
try {
Client client = new Client();
mMediaRouterService.registerClientAsUser(client, mPackageName, userId);
+ mMediaRouterService.setControlCategories(client, mControlCategories);
mClient = client;
} catch (RemoteException ex) {
Log.e(TAG, "Unable to register media router client.", ex);
@@ -1302,6 +1318,19 @@ public class MediaRouter {
sStatic.rebindAsUser(userId);
}
+ /**
+ * Sets the control categories of the application.
+ * Routes that support at least one of the given control categories only exists and are handled
+ * by the media router.
+ *
+ * @hide
+ */
+ public void setControlCategories(@NonNull Collection<String> controlCategories) {
+ Objects.requireNonNull(controlCategories, "control categories must not be null");
+
+ sStatic.setControlCategories(controlCategories);
+ }
+
static void updateRoute(final RouteInfo info) {
dispatchRouteChanged(info);
}
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 8e29e34caa14..b52e2d647e5a 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -44,7 +44,7 @@ import java.util.concurrent.Executor;
* @hide
*/
public class MediaRouter2 {
- private static final String TAG = "MediaRouter";
+ private static final String TAG = "MR2";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final Object sLock = new Object();
@@ -54,7 +54,8 @@ public class MediaRouter2 {
private Context mContext;
private final IMediaRouterService mMediaRouterService;
- private CopyOnWriteArrayList<CallbackRecord> mCallbackRecords = new CopyOnWriteArrayList<>();
+ private final CopyOnWriteArrayList<CallbackRecord> mCallbackRecords =
+ new CopyOnWriteArrayList<>();
@GuardedBy("sLock")
private List<String> mControlCategories = Collections.emptyList();
@GuardedBy("sLock")
@@ -129,7 +130,7 @@ public class MediaRouter2 {
Client client = new Client();
try {
mMediaRouterService.registerClient2(client, mPackageName);
- mMediaRouterService.setControlCategories(client, mControlCategories);
+ mMediaRouterService.setControlCategories2(client, mControlCategories);
mClient = client;
} catch (RemoteException ex) {
Log.e(TAG, "Unable to register media router.", ex);
@@ -188,7 +189,7 @@ public class MediaRouter2 {
}
if (client != null) {
try {
- mMediaRouterService.setControlCategories(client, newControlCategories);
+ mMediaRouterService.setControlCategories2(client, newControlCategories);
} catch (RemoteException ex) {
Log.e(TAG, "Unable to set control categories.", ex);
}
@@ -265,6 +266,54 @@ public class MediaRouter2 {
}
}
+ /**
+ * Requests a volume change for the route asynchronously.
+ * <p>
+ * It may have no effect if the route is currently not selected.
+ * </p>
+ *
+ * @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}.
+ */
+ public void requestSetVolume(@NonNull MediaRoute2Info route, int volume) {
+ Objects.requireNonNull(route, "route must not be null");
+
+ Client client;
+ synchronized (sLock) {
+ client = mClient;
+ }
+ if (client != null) {
+ try {
+ mMediaRouterService.requestSetVolume2(client, route, volume);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to send control request.", ex);
+ }
+ }
+ }
+
+ /**
+ * Requests an incremental volume update for the route asynchronously.
+ * <p>
+ * It may have no effect if the route is currently not selected.
+ * </p>
+ *
+ * @param delta The delta to add to the current volume.
+ */
+ public void requestUpdateVolume(@NonNull MediaRoute2Info route, int delta) {
+ Objects.requireNonNull(route, "route must not be null");
+
+ Client client;
+ synchronized (sLock) {
+ client = mClient;
+ }
+ if (client != null) {
+ try {
+ mMediaRouterService.requestUpdateVolume2(client, route, delta);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to send control request.", ex);
+ }
+ }
+ }
+
@GuardedBy("mCallbackRecords")
private int findCallbackRecordIndexLocked(Callback callback) {
final int count = mCallbackRecords.size();
@@ -310,6 +359,7 @@ public class MediaRouter2 {
List<MediaRoute2Info> outRoutes) {
if (provider == null || !provider.isValid()) {
Log.w(TAG, "Ignoring invalid provider : " + provider);
+ return;
}
final Collection<MediaRoute2Info> routes = provider.getRoutes();
@@ -321,10 +371,21 @@ public class MediaRouter2 {
if (!route.supportsControlCategory(controlCategories)) {
continue;
}
+ MediaRoute2Info preRoute = findRouteById(route.getId());
+ if (!route.equals(preRoute)) {
+ notifyRouteChanged(route);
+ }
outRoutes.add(route);
}
}
+ MediaRoute2Info findRouteById(String id) {
+ for (MediaRoute2Info route : mRoutes) {
+ if (route.getId().equals(id)) return route;
+ }
+ return null;
+ }
+
void notifyRouteListChanged(List<MediaRoute2Info> routes) {
for (CallbackRecord record: mCallbackRecords) {
record.mExecutor.execute(
@@ -332,10 +393,18 @@ public class MediaRouter2 {
}
}
+ void notifyRouteChanged(MediaRoute2Info route) {
+ for (CallbackRecord record: mCallbackRecords) {
+ record.mExecutor.execute(
+ () -> record.mCallback.onRouteChanged(route));
+ }
+ }
+
/**
* Interface for receiving events about media routing changes.
*/
public static class Callback {
+ //TODO: clean up these callbacks
/**
* Called when a route is added.
*/
@@ -369,7 +438,7 @@ public class MediaRouter2 {
void notifyRoutes() {
final List<MediaRoute2Info> routes = mRoutes;
// notify only when bound to media router service.
- //TODO: Correct the condition when control category, default rotue, .. are finalized.
+ //TODO: Correct the condition when control category, default route, .. are finalized.
if (routes.size() > 0) {
mExecutor.execute(() -> mCallback.onRoutesChanged(routes));
}
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 0b645691ea3b..0d7b6ff0ea91 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -46,7 +46,7 @@ import java.util.concurrent.Executor;
* @hide
*/
public class MediaRouter2Manager {
- private static final String TAG = "MediaRouter2Manager";
+ private static final String TAG = "MR2Manager";
private static final Object sLock = new Object();
@GuardedBy("sLock")
@@ -234,6 +234,54 @@ public class MediaRouter2Manager {
}
}
+ /**
+ * Requests a volume change for the route asynchronously.
+ * <p>
+ * It may have no effect if the route is currently not selected.
+ * </p>
+ *
+ * @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}.
+ */
+ public void requestSetVolume(@NonNull MediaRoute2Info route, int volume) {
+ Objects.requireNonNull(route, "route must not be null");
+
+ Client client;
+ synchronized (sLock) {
+ client = mClient;
+ }
+ if (client != null) {
+ try {
+ mMediaRouterService.requestSetVolume2Manager(client, route, volume);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to send control request.", ex);
+ }
+ }
+ }
+
+ /**
+ * Requests an incremental volume update for the route asynchronously.
+ * <p>
+ * It may have no effect if the route is currently not selected.
+ * </p>
+ *
+ * @param delta The delta to add to the current volume.
+ */
+ public void requestUpdateVolume(@NonNull MediaRoute2Info route, int delta) {
+ Objects.requireNonNull(route, "route must not be null");
+
+ Client client;
+ synchronized (sLock) {
+ client = mClient;
+ }
+ if (client != null) {
+ try {
+ mMediaRouterService.requestUpdateVolume2Manager(client, route, delta);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to send control request.", ex);
+ }
+ }
+ }
+
int findProviderIndex(MediaRoute2ProviderInfo provider) {
final int count = mProviders.size();
for (int i = 0; i < count; i++) {
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 771628cf7b1e..ca96c9ab5670 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -17,106 +17,14 @@
package android.media;
import android.annotation.UnsupportedAppUsage;
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.database.SQLException;
-import android.drm.DrmManagerClient;
-import android.graphics.BitmapFactory;
-import android.mtp.MtpConstants;
import android.net.Uri;
import android.os.Build;
-import android.os.Environment;
import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.provider.MediaStore;
-import android.provider.MediaStore.Audio;
-import android.provider.MediaStore.Audio.Playlists;
-import android.provider.MediaStore.Files;
-import android.provider.MediaStore.Files.FileColumns;
-import android.provider.MediaStore.Images;
-import android.provider.MediaStore.Video;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-import android.sax.Element;
-import android.sax.ElementListener;
-import android.sax.RootElement;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Xml;
-
-import dalvik.system.CloseGuard;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Locale;
-import java.util.TimeZone;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
- * Internal service helper that no-one should use directly.
- *
- * The way the scan currently works is:
- * - The Java MediaScannerService creates a MediaScanner (this class), and calls
- * MediaScanner.scanDirectories on it.
- * - scanDirectories() calls the native processDirectory() for each of the specified directories.
- * - the processDirectory() JNI method wraps the provided mediascanner client in a native
- * 'MyMediaScannerClient' class, then calls processDirectory() on the native MediaScanner
- * object (which got created when the Java MediaScanner was created).
- * - native MediaScanner.processDirectory() calls
- * doProcessDirectory(), which recurses over the folder, and calls
- * native MyMediaScannerClient.scanFile() for every file whose extension matches.
- * - native MyMediaScannerClient.scanFile() calls back on Java MediaScannerClient.scanFile,
- * which calls doScanFile, which after some setup calls back down to native code, calling
- * MediaScanner.processFile().
- * - MediaScanner.processFile() calls one of several methods, depending on the type of the
- * file: parseMP3, parseMP4, parseMidi, parseOgg or parseWMA.
- * - each of these methods gets metadata key/value pairs from the file, and repeatedly
- * calls native MyMediaScannerClient.handleStringTag, which calls back up to its Java
- * counterparts in this file.
- * - Java handleStringTag() gathers the key/value pairs that it's interested in.
- * - once processFile returns and we're back in Java code in doScanFile(), it calls
- * Java MyMediaScannerClient.endFile(), which takes all the data that's been
- * gathered and inserts an entry in to the database.
- *
- * In summary:
- * Java MediaScannerService calls
- * Java MediaScanner scanDirectories, which calls
- * Java MediaScanner processDirectory (native method), which calls
- * native MediaScanner processDirectory, which calls
- * native MyMediaScannerClient scanFile, which calls
- * Java MyMediaScannerClient scanFile, which calls
- * Java MediaScannerClient doScanFile, which calls
- * Java MediaScanner processFile (native method), which calls
- * native MediaScanner processFile, which calls
- * native parseMP3, parseMP4, parseMidi, parseOgg or parseWMA, which calls
- * native MyMediaScanner handleStringTag, which calls
- * Java MyMediaScanner handleStringTag.
- * Once MediaScanner processFile returns, an entry is inserted in to the database.
- *
- * The MediaScanner class is not thread-safe, so it should only be used in a single threaded manner.
- *
- * {@hide}
- *
+ * @hide
* @deprecated this media scanner has served faithfully for many years, but it's
* become tedious to test and maintain, mainly due to the way it
* weaves obscurely between managed and native code. It's been
@@ -125,1876 +33,182 @@ import java.util.concurrent.atomic.AtomicBoolean;
*/
@Deprecated
public class MediaScanner implements AutoCloseable {
- static {
- System.loadLibrary("media_jni");
- native_init();
- }
-
- private final static String TAG = "MediaScanner";
-
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private static final String[] FILES_PRESCAN_PROJECTION = new String[] {
- Files.FileColumns._ID, // 0
- Files.FileColumns.DATA, // 1
- Files.FileColumns.FORMAT, // 2
- Files.FileColumns.DATE_MODIFIED, // 3
- Files.FileColumns.MEDIA_TYPE, // 4
- };
-
- private static final String[] ID_PROJECTION = new String[] {
- Files.FileColumns._ID,
- };
-
- private static final int FILES_PRESCAN_ID_COLUMN_INDEX = 0;
- private static final int FILES_PRESCAN_PATH_COLUMN_INDEX = 1;
- private static final int FILES_PRESCAN_FORMAT_COLUMN_INDEX = 2;
- private static final int FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX = 3;
- private static final int FILES_PRESCAN_MEDIA_TYPE_COLUMN_INDEX = 4;
-
- private static final String[] PLAYLIST_MEMBERS_PROJECTION = new String[] {
- Audio.Playlists.Members.PLAYLIST_ID, // 0
- };
-
- private static final int ID_PLAYLISTS_COLUMN_INDEX = 0;
- private static final int PATH_PLAYLISTS_COLUMN_INDEX = 1;
- private static final int DATE_MODIFIED_PLAYLISTS_COLUMN_INDEX = 2;
-
- private static final String RINGTONES_DIR = "/ringtones/";
- private static final String NOTIFICATIONS_DIR = "/notifications/";
- private static final String ALARMS_DIR = "/alarms/";
- private static final String MUSIC_DIR = "/music/";
- private static final String PODCASTS_DIR = "/podcasts/";
- private static final String AUDIOBOOKS_DIR = "/audiobooks/";
-
- public static final String SCANNED_BUILD_PREFS_NAME = "MediaScanBuild";
- public static final String LAST_INTERNAL_SCAN_FINGERPRINT = "lastScanFingerprint";
- private static final String SYSTEM_SOUNDS_DIR = Environment.getRootDirectory() + "/media/audio";
- private static final String OEM_SOUNDS_DIR = Environment.getOemDirectory() + "/media/audio";
- private static final String PRODUCT_SOUNDS_DIR = Environment.getProductDirectory() + "/media/audio";
- private static String sLastInternalScanFingerprint;
-
- private static final String[] ID3_GENRES = {
- // ID3v1 Genres
- "Blues",
- "Classic Rock",
- "Country",
- "Dance",
- "Disco",
- "Funk",
- "Grunge",
- "Hip-Hop",
- "Jazz",
- "Metal",
- "New Age",
- "Oldies",
- "Other",
- "Pop",
- "R&B",
- "Rap",
- "Reggae",
- "Rock",
- "Techno",
- "Industrial",
- "Alternative",
- "Ska",
- "Death Metal",
- "Pranks",
- "Soundtrack",
- "Euro-Techno",
- "Ambient",
- "Trip-Hop",
- "Vocal",
- "Jazz+Funk",
- "Fusion",
- "Trance",
- "Classical",
- "Instrumental",
- "Acid",
- "House",
- "Game",
- "Sound Clip",
- "Gospel",
- "Noise",
- "AlternRock",
- "Bass",
- "Soul",
- "Punk",
- "Space",
- "Meditative",
- "Instrumental Pop",
- "Instrumental Rock",
- "Ethnic",
- "Gothic",
- "Darkwave",
- "Techno-Industrial",
- "Electronic",
- "Pop-Folk",
- "Eurodance",
- "Dream",
- "Southern Rock",
- "Comedy",
- "Cult",
- "Gangsta",
- "Top 40",
- "Christian Rap",
- "Pop/Funk",
- "Jungle",
- "Native American",
- "Cabaret",
- "New Wave",
- "Psychadelic",
- "Rave",
- "Showtunes",
- "Trailer",
- "Lo-Fi",
- "Tribal",
- "Acid Punk",
- "Acid Jazz",
- "Polka",
- "Retro",
- "Musical",
- "Rock & Roll",
- "Hard Rock",
- // The following genres are Winamp extensions
- "Folk",
- "Folk-Rock",
- "National Folk",
- "Swing",
- "Fast Fusion",
- "Bebob",
- "Latin",
- "Revival",
- "Celtic",
- "Bluegrass",
- "Avantgarde",
- "Gothic Rock",
- "Progressive Rock",
- "Psychedelic Rock",
- "Symphonic Rock",
- "Slow Rock",
- "Big Band",
- "Chorus",
- "Easy Listening",
- "Acoustic",
- "Humour",
- "Speech",
- "Chanson",
- "Opera",
- "Chamber Music",
- "Sonata",
- "Symphony",
- "Booty Bass",
- "Primus",
- "Porn Groove",
- "Satire",
- "Slow Jam",
- "Club",
- "Tango",
- "Samba",
- "Folklore",
- "Ballad",
- "Power Ballad",
- "Rhythmic Soul",
- "Freestyle",
- "Duet",
- "Punk Rock",
- "Drum Solo",
- "A capella",
- "Euro-House",
- "Dance Hall",
- // The following ones seem to be fairly widely supported as well
- "Goa",
- "Drum & Bass",
- "Club-House",
- "Hardcore",
- "Terror",
- "Indie",
- "Britpop",
- null,
- "Polsk Punk",
- "Beat",
- "Christian Gangsta",
- "Heavy Metal",
- "Black Metal",
- "Crossover",
- "Contemporary Christian",
- "Christian Rock",
- "Merengue",
- "Salsa",
- "Thrash Metal",
- "Anime",
- "JPop",
- "Synthpop",
- // 148 and up don't seem to have been defined yet.
};
- private long mNativeContext;
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private final Context mContext;
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private final String mPackageName;
- private final String mVolumeName;
- private final ContentProviderClient mMediaProvider;
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private final Uri mAudioUri;
- private final Uri mVideoUri;
- private final Uri mImagesUri;
- private final Uri mPlaylistsUri;
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private final Uri mFilesUri;
- private final Uri mFilesFullUri;
- private final boolean mProcessPlaylists;
- private final boolean mProcessGenres;
- private int mMtpObjectHandle;
- private final AtomicBoolean mClosed = new AtomicBoolean();
- private final CloseGuard mCloseGuard = CloseGuard.get();
-
- /** whether to use bulk inserts or individual inserts for each item */
- private static final boolean ENABLE_BULK_INSERTS = true;
-
- // used when scanning the image database so we know whether we have to prune
- // old thumbnail files
- private int mOriginalCount;
- /** Whether the scanner has set a default sound for the ringer ringtone. */
- private boolean mDefaultRingtoneSet;
- /** Whether the scanner has set a default sound for the notification ringtone. */
- private boolean mDefaultNotificationSet;
- /** Whether the scanner has set a default sound for the alarm ringtone. */
- private boolean mDefaultAlarmSet;
- /** The filename for the default sound for the ringer ringtone. */
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private String mDefaultRingtoneFilename;
- /** The filename for the default sound for the notification ringtone. */
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private String mDefaultNotificationFilename;
- /** The filename for the default sound for the alarm ringtone. */
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private String mDefaultAlarmAlertFilename;
- /**
- * The prefix for system properties that define the default sound for
- * ringtones. Concatenate the name of the setting from Settings
- * to get the full system property.
- */
- private static final String DEFAULT_RINGTONE_PROPERTY_PREFIX = "ro.config.";
-
- private final BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options();
private static class FileEntry {
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
long mRowId;
- String mPath;
- long mLastModified;
- int mFormat;
- int mMediaType;
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
boolean mLastModifiedChanged;
- /** @deprecated kept intact for lame apps using reflection */
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
FileEntry(long rowId, String path, long lastModified, int format) {
- this(rowId, path, lastModified, format, FileColumns.MEDIA_TYPE_NONE);
- }
-
- FileEntry(long rowId, String path, long lastModified, int format, int mediaType) {
- mRowId = rowId;
- mPath = path;
- mLastModified = lastModified;
- mFormat = format;
- mMediaType = mediaType;
- mLastModifiedChanged = false;
- }
-
- @Override
- public String toString() {
- return mPath + " mRowId: " + mRowId;
+ throw new UnsupportedOperationException();
}
}
- private static class PlaylistEntry {
- String path;
- long bestmatchid;
- int bestmatchlevel;
- }
-
- private final ArrayList<PlaylistEntry> mPlaylistEntries = new ArrayList<>();
- private final ArrayList<FileEntry> mPlayLists = new ArrayList<>();
-
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private MediaInserter mMediaInserter;
- private DrmManagerClient mDrmManagerClient = null;
-
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public MediaScanner(Context c, String volumeName) {
- native_setup();
- mContext = c;
- mPackageName = c.getPackageName();
- mVolumeName = volumeName;
-
- mBitmapOptions.inSampleSize = 1;
- mBitmapOptions.inJustDecodeBounds = true;
-
- setDefaultRingtoneFileNames();
-
- mMediaProvider = mContext.getContentResolver()
- .acquireContentProviderClient(MediaStore.AUTHORITY);
-
- if (sLastInternalScanFingerprint == null) {
- final SharedPreferences scanSettings =
- mContext.getSharedPreferences(SCANNED_BUILD_PREFS_NAME, Context.MODE_PRIVATE);
- sLastInternalScanFingerprint =
- scanSettings.getString(LAST_INTERNAL_SCAN_FINGERPRINT, new String());
- }
-
- mAudioUri = Audio.Media.getContentUri(volumeName);
- mVideoUri = Video.Media.getContentUri(volumeName);
- mImagesUri = Images.Media.getContentUri(volumeName);
- mFilesUri = Files.getContentUri(volumeName);
-
- Uri filesFullUri = mFilesUri.buildUpon().appendQueryParameter("nonotify", "1").build();
- filesFullUri = MediaStore.setIncludePending(filesFullUri);
- filesFullUri = MediaStore.setIncludeTrashed(filesFullUri);
- mFilesFullUri = filesFullUri;
-
- if (!volumeName.equals("internal")) {
- // we only support playlists on external media
- mProcessPlaylists = true;
- mProcessGenres = true;
- mPlaylistsUri = Playlists.getContentUri(volumeName);
- } else {
- mProcessPlaylists = false;
- mProcessGenres = false;
- mPlaylistsUri = null;
- }
-
- final Locale locale = mContext.getResources().getConfiguration().locale;
- if (locale != null) {
- String language = locale.getLanguage();
- String country = locale.getCountry();
- if (language != null) {
- if (country != null) {
- setLocale(language + "_" + country);
- } else {
- setLocale(language);
- }
- }
- }
-
- mCloseGuard.open("close");
- }
-
- private void setDefaultRingtoneFileNames() {
- mDefaultRingtoneFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
- + Settings.System.RINGTONE);
- mDefaultNotificationFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
- + Settings.System.NOTIFICATION_SOUND);
- mDefaultAlarmAlertFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
- + Settings.System.ALARM_ALERT);
+ throw new UnsupportedOperationException();
}
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private final MyMediaScannerClient mClient = new MyMediaScannerClient();
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private boolean isDrmEnabled() {
- String prop = SystemProperties.get("drm.service.enabled");
- return prop != null && prop.equals("true");
+ throw new UnsupportedOperationException();
}
private class MyMediaScannerClient implements MediaScannerClient {
-
- private final SimpleDateFormat mDateFormatter;
-
- private String mArtist;
- private String mAlbumArtist; // use this if mArtist is missing
- private String mAlbum;
- private String mTitle;
- private String mComposer;
- private String mGenre;
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private String mMimeType;
- /** @deprecated file types no longer exist */
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private int mFileType;
- private int mTrack;
- private int mYear;
- private int mDuration;
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private String mPath;
- private long mDate;
- private long mLastModified;
- private long mFileSize;
- private String mWriter;
- private int mCompilation;
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private boolean mIsDrm;
- @UnsupportedAppUsage
- private boolean mNoMedia; // flag to suppress file from appearing in media tables
- private boolean mScanSuccess;
- private int mWidth;
- private int mHeight;
- private int mColorStandard;
- private int mColorTransfer;
- private int mColorRange;
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
+ private boolean mNoMedia;
public MyMediaScannerClient() {
- mDateFormatter = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
- mDateFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
+ throw new UnsupportedOperationException();
}
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public FileEntry beginFile(String path, String mimeType, long lastModified,
long fileSize, boolean isDirectory, boolean noMedia) {
- mMimeType = mimeType;
- mFileSize = fileSize;
- mIsDrm = false;
- mScanSuccess = true;
-
- if (!isDirectory) {
- if (!noMedia && isNoMediaFile(path)) {
- noMedia = true;
- }
- mNoMedia = noMedia;
-
- // if mimeType was not specified, compute file type based on file extension.
- if (mMimeType == null) {
- mMimeType = MediaFile.getMimeTypeForFile(path);
- }
-
- if (isDrmEnabled() && MediaFile.isDrmMimeType(mMimeType)) {
- getMimeTypeFromDrm(path);
- }
- }
-
- FileEntry entry = makeEntryFor(path);
- // add some slack to avoid a rounding error
- long delta = (entry != null) ? (lastModified - entry.mLastModified) : 0;
- boolean wasModified = delta > 1 || delta < -1;
- if (entry == null || wasModified) {
- if (wasModified) {
- entry.mLastModified = lastModified;
- } else {
- entry = new FileEntry(0, path, lastModified,
- (isDirectory ? MtpConstants.FORMAT_ASSOCIATION : 0),
- FileColumns.MEDIA_TYPE_NONE);
- }
- entry.mLastModifiedChanged = true;
- }
-
- if (mProcessPlaylists && MediaFile.isPlayListMimeType(mMimeType)) {
- mPlayLists.add(entry);
- // we don't process playlists in the main scan, so return null
- return null;
- }
-
- // clear all the metadata
- mArtist = null;
- mAlbumArtist = null;
- mAlbum = null;
- mTitle = null;
- mComposer = null;
- mGenre = null;
- mTrack = 0;
- mYear = 0;
- mDuration = 0;
- mPath = path;
- mDate = 0;
- mLastModified = lastModified;
- mWriter = null;
- mCompilation = 0;
- mWidth = 0;
- mHeight = 0;
- mColorStandard = -1;
- mColorTransfer = -1;
- mColorRange = -1;
-
- return entry;
+ throw new UnsupportedOperationException();
}
- @Override
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public void scanFile(String path, long lastModified, long fileSize,
boolean isDirectory, boolean noMedia) {
- // This is the callback funtion from native codes.
- // Log.v(TAG, "scanFile: "+path);
- doScanFile(path, null, lastModified, fileSize, isDirectory, false, noMedia);
+ throw new UnsupportedOperationException();
}
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public Uri doScanFile(String path, String mimeType, long lastModified,
long fileSize, boolean isDirectory, boolean scanAlways, boolean noMedia) {
- Uri result = null;
-// long t1 = System.currentTimeMillis();
- try {
- FileEntry entry = beginFile(path, mimeType, lastModified,
- fileSize, isDirectory, noMedia);
-
- if (entry == null) {
- return null;
- }
-
- // if this file was just inserted via mtp, set the rowid to zero
- // (even though it already exists in the database), to trigger
- // the correct code path for updating its entry
- if (mMtpObjectHandle != 0) {
- entry.mRowId = 0;
- }
-
- if (entry.mPath != null) {
- if (((!mDefaultNotificationSet &&
- doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename))
- || (!mDefaultRingtoneSet &&
- doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename))
- || (!mDefaultAlarmSet &&
- doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)))) {
- Log.w(TAG, "forcing rescan of " + entry.mPath +
- "since ringtone setting didn't finish");
- scanAlways = true;
- } else if (isSystemSoundWithMetadata(entry.mPath)
- && !Build.FINGERPRINT.equals(sLastInternalScanFingerprint)) {
- // file is located on the system partition where the date cannot be trusted:
- // rescan if the build fingerprint has changed since the last scan.
- Log.i(TAG, "forcing rescan of " + entry.mPath
- + " since build fingerprint changed");
- scanAlways = true;
- }
- }
-
- // rescan for metadata if file was modified since last scan
- if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
- if (noMedia) {
- result = endFile(entry, false, false, false, false, false, false);
- } else {
- boolean isaudio = MediaFile.isAudioMimeType(mMimeType);
- boolean isvideo = MediaFile.isVideoMimeType(mMimeType);
- boolean isimage = MediaFile.isImageMimeType(mMimeType);
-
- if (isaudio || isvideo || isimage) {
- path = Environment.maybeTranslateEmulatedPathToInternal(new File(path))
- .getAbsolutePath();
- }
-
- // we only extract metadata for audio and video files
- if (isaudio || isvideo) {
- mScanSuccess = processFile(path, mimeType, this);
- }
-
- if (isimage) {
- mScanSuccess = processImageFile(path);
- }
-
- String lowpath = path.toLowerCase(Locale.ROOT);
- boolean ringtones = mScanSuccess && (lowpath.indexOf(RINGTONES_DIR) > 0);
- boolean notifications = mScanSuccess &&
- (lowpath.indexOf(NOTIFICATIONS_DIR) > 0);
- boolean alarms = mScanSuccess && (lowpath.indexOf(ALARMS_DIR) > 0);
- boolean podcasts = mScanSuccess && (lowpath.indexOf(PODCASTS_DIR) > 0);
- boolean audiobooks = mScanSuccess && (lowpath.indexOf(AUDIOBOOKS_DIR) > 0);
- boolean music = mScanSuccess && ((lowpath.indexOf(MUSIC_DIR) > 0) ||
- (!ringtones && !notifications && !alarms && !podcasts && !audiobooks));
-
- result = endFile(entry, ringtones, notifications, alarms, podcasts,
- audiobooks, music);
- }
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
- }
-// long t2 = System.currentTimeMillis();
-// Log.v(TAG, "scanFile: " + path + " took " + (t2-t1));
- return result;
+ throw new UnsupportedOperationException();
}
- private long parseDate(String date) {
- try {
- return mDateFormatter.parse(date).getTime();
- } catch (ParseException e) {
- return 0;
- }
- }
-
- private int parseSubstring(String s, int start, int defaultValue) {
- int length = s.length();
- if (start == length) return defaultValue;
-
- char ch = s.charAt(start++);
- // return defaultValue if we have no integer at all
- if (ch < '0' || ch > '9') return defaultValue;
-
- int result = ch - '0';
- while (start < length) {
- ch = s.charAt(start++);
- if (ch < '0' || ch > '9') return result;
- result = result * 10 + (ch - '0');
- }
-
- return result;
- }
-
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public void handleStringTag(String name, String value) {
- if (name.equalsIgnoreCase("title") || name.startsWith("title;")) {
- // Don't trim() here, to preserve the special \001 character
- // used to force sorting. The media provider will trim() before
- // inserting the title in to the database.
- mTitle = value;
- } else if (name.equalsIgnoreCase("artist") || name.startsWith("artist;")) {
- mArtist = value.trim();
- } else if (name.equalsIgnoreCase("albumartist") || name.startsWith("albumartist;")
- || name.equalsIgnoreCase("band") || name.startsWith("band;")) {
- mAlbumArtist = value.trim();
- } else if (name.equalsIgnoreCase("album") || name.startsWith("album;")) {
- mAlbum = value.trim();
- } else if (name.equalsIgnoreCase("composer") || name.startsWith("composer;")) {
- mComposer = value.trim();
- } else if (mProcessGenres &&
- (name.equalsIgnoreCase("genre") || name.startsWith("genre;"))) {
- mGenre = getGenreName(value);
- } else if (name.equalsIgnoreCase("year") || name.startsWith("year;")) {
- mYear = parseSubstring(value, 0, 0);
- } else if (name.equalsIgnoreCase("tracknumber") || name.startsWith("tracknumber;")) {
- // track number might be of the form "2/12"
- // we just read the number before the slash
- int num = parseSubstring(value, 0, 0);
- mTrack = (mTrack / 1000) * 1000 + num;
- } else if (name.equalsIgnoreCase("discnumber") ||
- name.equals("set") || name.startsWith("set;")) {
- // set number might be of the form "1/3"
- // we just read the number before the slash
- int num = parseSubstring(value, 0, 0);
- mTrack = (num * 1000) + (mTrack % 1000);
- } else if (name.equalsIgnoreCase("duration")) {
- mDuration = parseSubstring(value, 0, 0);
- } else if (name.equalsIgnoreCase("writer") || name.startsWith("writer;")) {
- mWriter = value.trim();
- } else if (name.equalsIgnoreCase("compilation")) {
- mCompilation = parseSubstring(value, 0, 0);
- } else if (name.equalsIgnoreCase("isdrm")) {
- mIsDrm = (parseSubstring(value, 0, 0) == 1);
- } else if (name.equalsIgnoreCase("date")) {
- mDate = parseDate(value);
- } else if (name.equalsIgnoreCase("width")) {
- mWidth = parseSubstring(value, 0, 0);
- } else if (name.equalsIgnoreCase("height")) {
- mHeight = parseSubstring(value, 0, 0);
- } else if (name.equalsIgnoreCase("colorstandard")) {
- mColorStandard = parseSubstring(value, 0, -1);
- } else if (name.equalsIgnoreCase("colortransfer")) {
- mColorTransfer = parseSubstring(value, 0, -1);
- } else if (name.equalsIgnoreCase("colorrange")) {
- mColorRange = parseSubstring(value, 0, -1);
- } else {
- //Log.v(TAG, "unknown tag: " + name + " (" + mProcessGenres + ")");
- }
+ throw new UnsupportedOperationException();
}
- private boolean convertGenreCode(String input, String expected) {
- String output = getGenreName(input);
- if (output.equals(expected)) {
- return true;
- } else {
- Log.d(TAG, "'" + input + "' -> '" + output + "', expected '" + expected + "'");
- return false;
- }
- }
- private void testGenreNameConverter() {
- convertGenreCode("2", "Country");
- convertGenreCode("(2)", "Country");
- convertGenreCode("(2", "(2");
- convertGenreCode("2 Foo", "Country");
- convertGenreCode("(2) Foo", "Country");
- convertGenreCode("(2 Foo", "(2 Foo");
- convertGenreCode("2Foo", "2Foo");
- convertGenreCode("(2)Foo", "Country");
- convertGenreCode("200 Foo", "Foo");
- convertGenreCode("(200) Foo", "Foo");
- convertGenreCode("200Foo", "200Foo");
- convertGenreCode("(200)Foo", "Foo");
- convertGenreCode("200)Foo", "200)Foo");
- convertGenreCode("200) Foo", "200) Foo");
- }
-
- public String getGenreName(String genreTagValue) {
-
- if (genreTagValue == null) {
- return null;
- }
- final int length = genreTagValue.length();
-
- if (length > 0) {
- boolean parenthesized = false;
- StringBuffer number = new StringBuffer();
- int i = 0;
- for (; i < length; ++i) {
- char c = genreTagValue.charAt(i);
- if (i == 0 && c == '(') {
- parenthesized = true;
- } else if (Character.isDigit(c)) {
- number.append(c);
- } else {
- break;
- }
- }
- char charAfterNumber = i < length ? genreTagValue.charAt(i) : ' ';
- if ((parenthesized && charAfterNumber == ')')
- || !parenthesized && Character.isWhitespace(charAfterNumber)) {
- try {
- short genreIndex = Short.parseShort(number.toString());
- if (genreIndex >= 0) {
- if (genreIndex < ID3_GENRES.length && ID3_GENRES[genreIndex] != null) {
- return ID3_GENRES[genreIndex];
- } else if (genreIndex == 0xFF) {
- return null;
- } else if (genreIndex < 0xFF && (i + 1) < length) {
- // genre is valid but unknown,
- // if there is a string after the value we take it
- if (parenthesized && charAfterNumber == ')') {
- i++;
- }
- String ret = genreTagValue.substring(i).trim();
- if (ret.length() != 0) {
- return ret;
- }
- } else {
- // else return the number, without parentheses
- return number.toString();
- }
- }
- } catch (NumberFormatException e) {
- }
- }
- }
-
- return genreTagValue;
- }
-
- private boolean processImageFile(String path) {
- try {
- mBitmapOptions.outWidth = 0;
- mBitmapOptions.outHeight = 0;
- BitmapFactory.decodeFile(path, mBitmapOptions);
- mWidth = mBitmapOptions.outWidth;
- mHeight = mBitmapOptions.outHeight;
- return mWidth > 0 && mHeight > 0;
- } catch (Throwable th) {
- // ignore;
- }
- return false;
- }
-
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public void setMimeType(String mimeType) {
- if ("audio/mp4".equals(mMimeType) &&
- mimeType.startsWith("video")) {
- // for feature parity with Donut, we force m4a files to keep the
- // audio/mp4 mimetype, even if they are really "enhanced podcasts"
- // with a video track
- return;
- }
- mMimeType = mimeType;
+ throw new UnsupportedOperationException();
}
- /**
- * Formats the data into a values array suitable for use with the Media
- * Content Provider.
- *
- * @return a map of values
- */
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private ContentValues toValues() {
- ContentValues map = new ContentValues();
-
- map.put(MediaStore.MediaColumns.DATA, mPath);
- map.put(MediaStore.MediaColumns.TITLE, mTitle);
- map.put(MediaStore.MediaColumns.DATE_MODIFIED, mLastModified);
- map.put(MediaStore.MediaColumns.SIZE, mFileSize);
- map.put(MediaStore.MediaColumns.MIME_TYPE, mMimeType);
- map.put(MediaStore.MediaColumns.IS_DRM, mIsDrm);
- map.putNull(MediaStore.MediaColumns.HASH);
-
- String resolution = null;
- if (mWidth > 0 && mHeight > 0) {
- map.put(MediaStore.MediaColumns.WIDTH, mWidth);
- map.put(MediaStore.MediaColumns.HEIGHT, mHeight);
- resolution = mWidth + "x" + mHeight;
- }
-
- if (!mNoMedia) {
- if (MediaFile.isVideoMimeType(mMimeType)) {
- map.put(Video.Media.ARTIST, (mArtist != null && mArtist.length() > 0
- ? mArtist : MediaStore.UNKNOWN_STRING));
- map.put(Video.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0
- ? mAlbum : MediaStore.UNKNOWN_STRING));
- map.put(Video.Media.DURATION, mDuration);
- if (resolution != null) {
- map.put(Video.Media.RESOLUTION, resolution);
- }
- if (mColorStandard >= 0) {
- map.put(Video.Media.COLOR_STANDARD, mColorStandard);
- }
- if (mColorTransfer >= 0) {
- map.put(Video.Media.COLOR_TRANSFER, mColorTransfer);
- }
- if (mColorRange >= 0) {
- map.put(Video.Media.COLOR_RANGE, mColorRange);
- }
- if (mDate > 0) {
- map.put(Video.Media.DATE_TAKEN, mDate);
- }
- } else if (MediaFile.isImageMimeType(mMimeType)) {
- // FIXME - add DESCRIPTION
- } else if (MediaFile.isAudioMimeType(mMimeType)) {
- map.put(Audio.Media.ARTIST, (mArtist != null && mArtist.length() > 0) ?
- mArtist : MediaStore.UNKNOWN_STRING);
- map.put(Audio.Media.ALBUM_ARTIST, (mAlbumArtist != null &&
- mAlbumArtist.length() > 0) ? mAlbumArtist : null);
- map.put(Audio.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0) ?
- mAlbum : MediaStore.UNKNOWN_STRING);
- map.put(Audio.Media.COMPOSER, mComposer);
- map.put(Audio.Media.GENRE, mGenre);
- if (mYear != 0) {
- map.put(Audio.Media.YEAR, mYear);
- }
- map.put(Audio.Media.TRACK, mTrack);
- map.put(Audio.Media.DURATION, mDuration);
- map.put(Audio.Media.COMPILATION, mCompilation);
- }
- }
- return map;
+ throw new UnsupportedOperationException();
}
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private Uri endFile(FileEntry entry, boolean ringtones, boolean notifications,
boolean alarms, boolean podcasts, boolean audiobooks, boolean music)
throws RemoteException {
- // update database
-
- // use album artist if artist is missing
- if (mArtist == null || mArtist.length() == 0) {
- mArtist = mAlbumArtist;
- }
-
- ContentValues values = toValues();
- String title = values.getAsString(MediaStore.MediaColumns.TITLE);
- if (title == null || TextUtils.isEmpty(title.trim())) {
- title = MediaFile.getFileTitle(values.getAsString(MediaStore.MediaColumns.DATA));
- values.put(MediaStore.MediaColumns.TITLE, title);
- }
- String album = values.getAsString(Audio.Media.ALBUM);
- if (MediaStore.UNKNOWN_STRING.equals(album)) {
- album = values.getAsString(MediaStore.MediaColumns.DATA);
- // extract last path segment before file name
- int lastSlash = album.lastIndexOf('/');
- if (lastSlash >= 0) {
- int previousSlash = 0;
- while (true) {
- int idx = album.indexOf('/', previousSlash + 1);
- if (idx < 0 || idx >= lastSlash) {
- break;
- }
- previousSlash = idx;
- }
- if (previousSlash != 0) {
- album = album.substring(previousSlash + 1, lastSlash);
- values.put(Audio.Media.ALBUM, album);
- }
- }
- }
- long rowId = entry.mRowId;
- if (MediaFile.isAudioMimeType(mMimeType) && (rowId == 0 || mMtpObjectHandle != 0)) {
- // Only set these for new entries. For existing entries, they
- // may have been modified later, and we want to keep the current
- // values so that custom ringtones still show up in the ringtone
- // picker.
- values.put(Audio.Media.IS_RINGTONE, ringtones);
- values.put(Audio.Media.IS_NOTIFICATION, notifications);
- values.put(Audio.Media.IS_ALARM, alarms);
- values.put(Audio.Media.IS_MUSIC, music);
- values.put(Audio.Media.IS_PODCAST, podcasts);
- values.put(Audio.Media.IS_AUDIOBOOK, audiobooks);
- } else if (MediaFile.isExifMimeType(mMimeType) && !mNoMedia) {
- ExifInterface exif = null;
- try {
- exif = new ExifInterface(entry.mPath);
- } catch (Exception ex) {
- // exif is null
- }
- if (exif != null) {
- long time = exif.getGpsDateTime();
- if (time != -1) {
- values.put(Images.Media.DATE_TAKEN, time);
- } else {
- // If no time zone information is available, we should consider using
- // EXIF local time as taken time if the difference between file time
- // and EXIF local time is not less than 1 Day, otherwise MediaProvider
- // will use file time as taken time.
- time = exif.getDateTime();
- if (time != -1 && Math.abs(mLastModified * 1000 - time) >= 86400000) {
- values.put(Images.Media.DATE_TAKEN, time);
- }
- }
-
- int orientation = exif.getAttributeInt(
- ExifInterface.TAG_ORIENTATION, -1);
- if (orientation != -1) {
- // We only recognize a subset of orientation tag values.
- int degree;
- switch(orientation) {
- case ExifInterface.ORIENTATION_ROTATE_90:
- degree = 90;
- break;
- case ExifInterface.ORIENTATION_ROTATE_180:
- degree = 180;
- break;
- case ExifInterface.ORIENTATION_ROTATE_270:
- degree = 270;
- break;
- default:
- degree = 0;
- break;
- }
- values.put(Images.Media.ORIENTATION, degree);
- }
- }
- }
-
- Uri tableUri = mFilesUri;
- int mediaType = FileColumns.MEDIA_TYPE_NONE;
- MediaInserter inserter = mMediaInserter;
- if (!mNoMedia) {
- if (MediaFile.isVideoMimeType(mMimeType)) {
- tableUri = mVideoUri;
- mediaType = FileColumns.MEDIA_TYPE_VIDEO;
- } else if (MediaFile.isImageMimeType(mMimeType)) {
- tableUri = mImagesUri;
- mediaType = FileColumns.MEDIA_TYPE_IMAGE;
- } else if (MediaFile.isAudioMimeType(mMimeType)) {
- tableUri = mAudioUri;
- mediaType = FileColumns.MEDIA_TYPE_AUDIO;
- } else if (MediaFile.isPlayListMimeType(mMimeType)) {
- tableUri = mPlaylistsUri;
- mediaType = FileColumns.MEDIA_TYPE_PLAYLIST;
- }
- }
- Uri result = null;
- boolean needToSetSettings = false;
- // Setting a flag in order not to use bulk insert for the file related with
- // notifications, ringtones, and alarms, because the rowId of the inserted file is
- // needed.
- if (notifications && !mDefaultNotificationSet) {
- if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
- needToSetSettings = true;
- }
- } else if (ringtones && !mDefaultRingtoneSet) {
- if (TextUtils.isEmpty(mDefaultRingtoneFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename)) {
- needToSetSettings = true;
- }
- } else if (alarms && !mDefaultAlarmSet) {
- if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) {
- needToSetSettings = true;
- }
- }
-
- if (rowId == 0) {
- if (mMtpObjectHandle != 0) {
- values.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle);
- }
- if (tableUri == mFilesUri) {
- int format = entry.mFormat;
- if (format == 0) {
- format = MediaFile.getFormatCode(entry.mPath, mMimeType);
- }
- values.put(Files.FileColumns.FORMAT, format);
- }
- // New file, insert it.
- // Directories need to be inserted before the files they contain, so they
- // get priority when bulk inserting.
- // If the rowId of the inserted file is needed, it gets inserted immediately,
- // bypassing the bulk inserter.
- if (inserter == null || needToSetSettings) {
- if (inserter != null) {
- inserter.flushAll();
- }
- result = mMediaProvider.insert(tableUri, values);
- } else if (entry.mFormat == MtpConstants.FORMAT_ASSOCIATION) {
- inserter.insertwithPriority(tableUri, values);
- } else {
- inserter.insert(tableUri, values);
- }
-
- if (result != null) {
- rowId = ContentUris.parseId(result);
- entry.mRowId = rowId;
- }
- } else {
- // updated file
- result = ContentUris.withAppendedId(tableUri, rowId);
- // path should never change, and we want to avoid replacing mixed cased paths
- // with squashed lower case paths
- values.remove(MediaStore.MediaColumns.DATA);
-
- if (!mNoMedia) {
- // Changing media type must be done as separate update
- if (mediaType != entry.mMediaType) {
- final ContentValues mediaTypeValues = new ContentValues();
- mediaTypeValues.put(FileColumns.MEDIA_TYPE, mediaType);
- mMediaProvider.update(ContentUris.withAppendedId(mFilesUri, rowId),
- mediaTypeValues, null, null);
- }
- }
-
- mMediaProvider.update(result, values, null, null);
- }
-
- if(needToSetSettings) {
- if (notifications) {
- setRingtoneIfNotSet(Settings.System.NOTIFICATION_SOUND, tableUri, rowId);
- mDefaultNotificationSet = true;
- } else if (ringtones) {
- setRingtoneIfNotSet(Settings.System.RINGTONE, tableUri, rowId);
- mDefaultRingtoneSet = true;
- } else if (alarms) {
- setRingtoneIfNotSet(Settings.System.ALARM_ALERT, tableUri, rowId);
- mDefaultAlarmSet = true;
- }
- }
-
- return result;
- }
-
- private boolean doesPathHaveFilename(String path, String filename) {
- int pathFilenameStart = path.lastIndexOf(File.separatorChar) + 1;
- int filenameLength = filename.length();
- return path.regionMatches(pathFilenameStart, filename, 0, filenameLength) &&
- pathFilenameStart + filenameLength == path.length();
+ throw new UnsupportedOperationException();
}
- private void setRingtoneIfNotSet(String settingName, Uri uri, long rowId) {
- if (wasRingtoneAlreadySet(settingName)) {
- return;
- }
-
- ContentResolver cr = mContext.getContentResolver();
- String existingSettingValue = Settings.System.getString(cr, settingName);
- if (TextUtils.isEmpty(existingSettingValue)) {
- final Uri settingUri = Settings.System.getUriFor(settingName);
- final Uri ringtoneUri = ContentUris.withAppendedId(uri, rowId);
- RingtoneManager.setActualDefaultRingtoneUri(mContext,
- RingtoneManager.getDefaultType(settingUri), ringtoneUri);
- }
- Settings.System.putInt(cr, settingSetIndicatorName(settingName), 1);
- }
-
- /** @deprecated file types no longer exist */
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private int getFileTypeFromDrm(String path) {
- return 0;
+ throw new UnsupportedOperationException();
}
-
- private void getMimeTypeFromDrm(String path) {
- mMimeType = null;
-
- if (mDrmManagerClient == null) {
- mDrmManagerClient = new DrmManagerClient(mContext);
- }
-
- if (mDrmManagerClient.canHandle(path, null)) {
- mIsDrm = true;
- mMimeType = mDrmManagerClient.getOriginalMimeType(path);
- }
-
- if (mMimeType == null) {
- mMimeType = ContentResolver.MIME_TYPE_DEFAULT;
- }
- }
-
- }; // end of anonymous MediaScannerClient instance
-
- private static boolean isSystemSoundWithMetadata(String path) {
- if (path.startsWith(SYSTEM_SOUNDS_DIR + ALARMS_DIR)
- || path.startsWith(SYSTEM_SOUNDS_DIR + RINGTONES_DIR)
- || path.startsWith(SYSTEM_SOUNDS_DIR + NOTIFICATIONS_DIR)
- || path.startsWith(OEM_SOUNDS_DIR + ALARMS_DIR)
- || path.startsWith(OEM_SOUNDS_DIR + RINGTONES_DIR)
- || path.startsWith(OEM_SOUNDS_DIR + NOTIFICATIONS_DIR)
- || path.startsWith(PRODUCT_SOUNDS_DIR + ALARMS_DIR)
- || path.startsWith(PRODUCT_SOUNDS_DIR + RINGTONES_DIR)
- || path.startsWith(PRODUCT_SOUNDS_DIR + NOTIFICATIONS_DIR)) {
- return true;
- }
- return false;
}
- private String settingSetIndicatorName(String base) {
- return base + "_set";
- }
-
- private boolean wasRingtoneAlreadySet(String name) {
- ContentResolver cr = mContext.getContentResolver();
- String indicatorName = settingSetIndicatorName(name);
- try {
- return Settings.System.getInt(cr, indicatorName) != 0;
- } catch (SettingNotFoundException e) {
- return false;
- }
- }
-
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private void prescan(String filePath, boolean prescanFiles) throws RemoteException {
- Cursor c = null;
- String where = null;
- String[] selectionArgs = null;
-
- mPlayLists.clear();
-
- if (filePath != null) {
- // query for only one file
- where = MediaStore.Files.FileColumns._ID + ">?" +
- " AND " + Files.FileColumns.DATA + "=?";
- selectionArgs = new String[] { "", filePath };
- } else {
- where = MediaStore.Files.FileColumns._ID + ">?";
- selectionArgs = new String[] { "" };
- }
-
- mDefaultRingtoneSet = wasRingtoneAlreadySet(Settings.System.RINGTONE);
- mDefaultNotificationSet = wasRingtoneAlreadySet(Settings.System.NOTIFICATION_SOUND);
- mDefaultAlarmSet = wasRingtoneAlreadySet(Settings.System.ALARM_ALERT);
-
- // Tell the provider to not delete the file.
- // If the file is truly gone the delete is unnecessary, and we want to avoid
- // accidentally deleting files that are really there (this may happen if the
- // filesystem is mounted and unmounted while the scanner is running).
- Uri.Builder builder = mFilesUri.buildUpon();
- builder.appendQueryParameter(MediaStore.PARAM_DELETE_DATA, "false");
- MediaBulkDeleter deleter = new MediaBulkDeleter(mMediaProvider, builder.build());
-
- // Build the list of files from the content provider
- try {
- if (prescanFiles) {
- // First read existing files from the files table.
- // Because we'll be deleting entries for missing files as we go,
- // we need to query the database in small batches, to avoid problems
- // with CursorWindow positioning.
- long lastId = Long.MIN_VALUE;
- Uri limitUri = mFilesUri.buildUpon()
- .appendQueryParameter(MediaStore.PARAM_LIMIT, "1000").build();
-
- while (true) {
- selectionArgs[0] = "" + lastId;
- if (c != null) {
- c.close();
- c = null;
- }
- c = mMediaProvider.query(limitUri, FILES_PRESCAN_PROJECTION,
- where, selectionArgs, MediaStore.Files.FileColumns._ID, null);
- if (c == null) {
- break;
- }
-
- int num = c.getCount();
-
- if (num == 0) {
- break;
- }
- while (c.moveToNext()) {
- long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
- String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
- int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
- long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
- lastId = rowId;
-
- // Only consider entries with absolute path names.
- // This allows storing URIs in the database without the
- // media scanner removing them.
- if (path != null && path.startsWith("/")) {
- boolean exists = false;
- try {
- exists = Os.access(path, android.system.OsConstants.F_OK);
- } catch (ErrnoException e1) {
- }
- if (!exists && !MtpConstants.isAbstractObject(format)) {
- // do not delete missing playlists, since they may have been
- // modified by the user.
- // The user can delete them in the media player instead.
- // instead, clear the path and lastModified fields in the row
- String mimeType = MediaFile.getMimeTypeForFile(path);
- if (!MediaFile.isPlayListMimeType(mimeType)) {
- deleter.delete(rowId);
- if (path.toLowerCase(Locale.US).endsWith("/.nomedia")) {
- deleter.flush();
- String parent = new File(path).getParent();
- mMediaProvider.call(MediaStore.UNHIDE_CALL, parent, null);
- }
- }
- }
- }
- }
- }
- }
- }
- finally {
- if (c != null) {
- c.close();
- }
- deleter.flush();
- }
-
- // compute original size of images
- mOriginalCount = 0;
- c = mMediaProvider.query(mImagesUri, ID_PROJECTION, null, null, null, null);
- if (c != null) {
- mOriginalCount = c.getCount();
- c.close();
- }
- }
-
- static class MediaBulkDeleter {
- StringBuilder whereClause = new StringBuilder();
- ArrayList<String> whereArgs = new ArrayList<String>(100);
- final ContentProviderClient mProvider;
- final Uri mBaseUri;
-
- public MediaBulkDeleter(ContentProviderClient provider, Uri baseUri) {
- mProvider = provider;
- mBaseUri = baseUri;
- }
-
- public void delete(long id) throws RemoteException {
- if (whereClause.length() != 0) {
- whereClause.append(",");
- }
- whereClause.append("?");
- whereArgs.add("" + id);
- if (whereArgs.size() > 100) {
- flush();
- }
- }
- public void flush() throws RemoteException {
- int size = whereArgs.size();
- if (size > 0) {
- String [] foo = new String [size];
- foo = whereArgs.toArray(foo);
- int numrows = mProvider.delete(mBaseUri,
- MediaStore.MediaColumns._ID + " IN (" +
- whereClause.toString() + ")", foo);
- //Log.i("@@@@@@@@@", "rows deleted: " + numrows);
- whereClause.setLength(0);
- whereArgs.clear();
- }
- }
+ throw new UnsupportedOperationException();
}
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private void postscan(final String[] directories) throws RemoteException {
-
- // handle playlists last, after we know what media files are on the storage.
- if (mProcessPlaylists) {
- processPlayLists();
- }
-
- // allow GC to clean up
- mPlayLists.clear();
- }
-
- private void releaseResources() {
- // release the DrmManagerClient resources
- if (mDrmManagerClient != null) {
- mDrmManagerClient.close();
- mDrmManagerClient = null;
- }
- }
-
- public void scanDirectories(String[] directories) {
- try {
- long start = System.currentTimeMillis();
- prescan(null, true);
- long prescan = System.currentTimeMillis();
-
- if (ENABLE_BULK_INSERTS) {
- // create MediaInserter for bulk inserts
- mMediaInserter = new MediaInserter(mMediaProvider, 500);
- }
-
- for (int i = 0; i < directories.length; i++) {
- processDirectory(directories[i], mClient);
- }
-
- if (ENABLE_BULK_INSERTS) {
- // flush remaining inserts
- mMediaInserter.flushAll();
- mMediaInserter = null;
- }
-
- long scan = System.currentTimeMillis();
- postscan(directories);
- long end = System.currentTimeMillis();
-
- if (false) {
- Log.d(TAG, " prescan time: " + (prescan - start) + "ms\n");
- Log.d(TAG, " scan time: " + (scan - prescan) + "ms\n");
- Log.d(TAG, "postscan time: " + (end - scan) + "ms\n");
- Log.d(TAG, " total time: " + (end - start) + "ms\n");
- }
- } catch (SQLException e) {
- // this might happen if the SD card is removed while the media scanner is running
- Log.e(TAG, "SQLException in MediaScanner.scan()", e);
- } catch (UnsupportedOperationException e) {
- // this might happen if the SD card is removed while the media scanner is running
- Log.e(TAG, "UnsupportedOperationException in MediaScanner.scan()", e);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in MediaScanner.scan()", e);
- } finally {
- releaseResources();
- }
+ throw new UnsupportedOperationException();
}
- // this function is used to scan a single file
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public Uri scanSingleFile(String path, String mimeType) {
- try {
- prescan(path, true);
-
- File file = new File(path);
- if (!file.exists() || !file.canRead()) {
- return null;
- }
-
- // lastModified is in milliseconds on Files.
- long lastModifiedSeconds = file.lastModified() / 1000;
-
- // always scan the file, so we can return the content://media Uri for existing files
- return mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(),
- false, true, MediaScanner.isNoMediaPath(path));
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
- return null;
- } finally {
- releaseResources();
- }
- }
-
- private static boolean isNoMediaFile(String path) {
- File file = new File(path);
- if (file.isDirectory()) return false;
-
- // special case certain file names
- // I use regionMatches() instead of substring() below
- // to avoid memory allocation
- int lastSlash = path.lastIndexOf('/');
- if (lastSlash >= 0 && lastSlash + 2 < path.length()) {
- // ignore those ._* files created by MacOS
- if (path.regionMatches(lastSlash + 1, "._", 0, 2)) {
- return true;
- }
-
- // ignore album art files created by Windows Media Player:
- // Folder.jpg, AlbumArtSmall.jpg, AlbumArt_{...}_Large.jpg
- // and AlbumArt_{...}_Small.jpg
- if (path.regionMatches(true, path.length() - 4, ".jpg", 0, 4)) {
- if (path.regionMatches(true, lastSlash + 1, "AlbumArt_{", 0, 10) ||
- path.regionMatches(true, lastSlash + 1, "AlbumArt.", 0, 9)) {
- return true;
- }
- int length = path.length() - lastSlash - 1;
- if ((length == 17 && path.regionMatches(
- true, lastSlash + 1, "AlbumArtSmall", 0, 13)) ||
- (length == 10
- && path.regionMatches(true, lastSlash + 1, "Folder", 0, 6))) {
- return true;
- }
- }
- }
- return false;
- }
-
- private static HashMap<String,String> mNoMediaPaths = new HashMap<String,String>();
- private static HashMap<String,String> mMediaPaths = new HashMap<String,String>();
-
- /* MediaProvider calls this when a .nomedia file is added or removed */
- public static void clearMediaPathCache(boolean clearMediaPaths, boolean clearNoMediaPaths) {
- synchronized (MediaScanner.class) {
- if (clearMediaPaths) {
- mMediaPaths.clear();
- }
- if (clearNoMediaPaths) {
- mNoMediaPaths.clear();
- }
- }
+ throw new UnsupportedOperationException();
}
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public static boolean isNoMediaPath(String path) {
- if (path == null) {
- return false;
- }
- // return true if file or any parent directory has name starting with a dot
- if (path.indexOf("/.") >= 0) {
- return true;
- }
-
- int firstSlash = path.lastIndexOf('/');
- if (firstSlash <= 0) {
- return false;
- }
- String parent = path.substring(0, firstSlash);
-
- synchronized (MediaScanner.class) {
- if (mNoMediaPaths.containsKey(parent)) {
- return true;
- } else if (!mMediaPaths.containsKey(parent)) {
- // check to see if any parent directories have a ".nomedia" file
- // start from 1 so we don't bother checking in the root directory
- int offset = 1;
- while (offset >= 0) {
- int slashIndex = path.indexOf('/', offset);
- if (slashIndex > offset) {
- slashIndex++; // move past slash
- File file = new File(path.substring(0, slashIndex) + ".nomedia");
- if (file.exists()) {
- // we have a .nomedia in one of the parent directories
- mNoMediaPaths.put(parent, "");
- return true;
- }
- }
- offset = slashIndex;
- }
- mMediaPaths.put(parent, "");
- }
- }
-
- return isNoMediaFile(path);
- }
-
- public void scanMtpFile(String path, int objectHandle, int format) {
- String mimeType = MediaFile.getMimeType(path, format);
- File file = new File(path);
- long lastModifiedSeconds = file.lastModified() / 1000;
-
- if (!MediaFile.isAudioMimeType(mimeType) && !MediaFile.isVideoMimeType(mimeType) &&
- !MediaFile.isImageMimeType(mimeType) && !MediaFile.isPlayListMimeType(mimeType) &&
- !MediaFile.isDrmMimeType(mimeType)) {
-
- // no need to use the media scanner, but we need to update last modified and file size
- ContentValues values = new ContentValues();
- values.put(Files.FileColumns.SIZE, file.length());
- values.put(Files.FileColumns.DATE_MODIFIED, lastModifiedSeconds);
- try {
- String[] whereArgs = new String[] { Integer.toString(objectHandle) };
- mMediaProvider.update(Files.getMtpObjectsUri(mVolumeName), values,
- "_id=?", whereArgs);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in scanMtpFile", e);
- }
- return;
- }
-
- mMtpObjectHandle = objectHandle;
- Cursor fileList = null;
- try {
- if (MediaFile.isPlayListMimeType(mimeType)) {
- // build file cache so we can look up tracks in the playlist
- prescan(null, true);
-
- FileEntry entry = makeEntryFor(path);
- if (entry != null) {
- fileList = mMediaProvider.query(mFilesUri,
- FILES_PRESCAN_PROJECTION, null, null, null, null);
- processPlayList(entry, fileList);
- }
- } else {
- // MTP will create a file entry for us so we don't want to do it in prescan
- prescan(path, false);
-
- // always scan the file, so we can return the content://media Uri for existing files
- mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(),
- (format == MtpConstants.FORMAT_ASSOCIATION), true, isNoMediaPath(path));
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
- } finally {
- mMtpObjectHandle = 0;
- if (fileList != null) {
- fileList.close();
- }
- releaseResources();
- }
+ throw new UnsupportedOperationException();
}
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
FileEntry makeEntryFor(String path) {
- String where;
- String[] selectionArgs;
-
- Cursor c = null;
- try {
- where = Files.FileColumns.DATA + "=?";
- selectionArgs = new String[] { path };
- c = mMediaProvider.query(mFilesFullUri, FILES_PRESCAN_PROJECTION,
- where, selectionArgs, null, null);
- if (c != null && c.moveToFirst()) {
- long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
- long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
- int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
- int mediaType = c.getInt(FILES_PRESCAN_MEDIA_TYPE_COLUMN_INDEX);
- return new FileEntry(rowId, path, lastModified, format, mediaType);
- }
- } catch (RemoteException e) {
- } finally {
- if (c != null) {
- c.close();
- }
- }
- return null;
+ throw new UnsupportedOperationException();
}
- // returns the number of matching file/directory names, starting from the right
- private int matchPaths(String path1, String path2) {
- int result = 0;
- int end1 = path1.length();
- int end2 = path2.length();
-
- while (end1 > 0 && end2 > 0) {
- int slash1 = path1.lastIndexOf('/', end1 - 1);
- int slash2 = path2.lastIndexOf('/', end2 - 1);
- int backSlash1 = path1.lastIndexOf('\\', end1 - 1);
- int backSlash2 = path2.lastIndexOf('\\', end2 - 1);
- int start1 = (slash1 > backSlash1 ? slash1 : backSlash1);
- int start2 = (slash2 > backSlash2 ? slash2 : backSlash2);
- if (start1 < 0) start1 = 0; else start1++;
- if (start2 < 0) start2 = 0; else start2++;
- int length = end1 - start1;
- if (end2 - start2 != length) break;
- if (path1.regionMatches(true, start1, path2, start2, length)) {
- result++;
- end1 = start1 - 1;
- end2 = start2 - 1;
- } else break;
- }
-
- return result;
- }
-
- private boolean matchEntries(long rowId, String data) {
-
- int len = mPlaylistEntries.size();
- boolean done = true;
- for (int i = 0; i < len; i++) {
- PlaylistEntry entry = mPlaylistEntries.get(i);
- if (entry.bestmatchlevel == Integer.MAX_VALUE) {
- continue; // this entry has been matched already
- }
- done = false;
- if (data.equalsIgnoreCase(entry.path)) {
- entry.bestmatchid = rowId;
- entry.bestmatchlevel = Integer.MAX_VALUE;
- continue; // no need for path matching
- }
-
- int matchLength = matchPaths(data, entry.path);
- if (matchLength > entry.bestmatchlevel) {
- entry.bestmatchid = rowId;
- entry.bestmatchlevel = matchLength;
- }
- }
- return done;
- }
-
- private void cachePlaylistEntry(String line, String playListDirectory) {
- PlaylistEntry entry = new PlaylistEntry();
- // watch for trailing whitespace
- int entryLength = line.length();
- while (entryLength > 0 && Character.isWhitespace(line.charAt(entryLength - 1))) entryLength--;
- // path should be longer than 3 characters.
- // avoid index out of bounds errors below by returning here.
- if (entryLength < 3) return;
- if (entryLength < line.length()) line = line.substring(0, entryLength);
-
- // does entry appear to be an absolute path?
- // look for Unix or DOS absolute paths
- char ch1 = line.charAt(0);
- boolean fullPath = (ch1 == '/' ||
- (Character.isLetter(ch1) && line.charAt(1) == ':' && line.charAt(2) == '\\'));
- // if we have a relative path, combine entry with playListDirectory
- if (!fullPath)
- line = playListDirectory + line;
- entry.path = line;
- //FIXME - should we look for "../" within the path?
-
- mPlaylistEntries.add(entry);
- }
-
- private void processCachedPlaylist(Cursor fileList, ContentValues values, Uri playlistUri) {
- fileList.moveToPosition(-1);
- while (fileList.moveToNext()) {
- long rowId = fileList.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
- String data = fileList.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
- if (matchEntries(rowId, data)) {
- break;
- }
- }
-
- int len = mPlaylistEntries.size();
- int index = 0;
- for (int i = 0; i < len; i++) {
- PlaylistEntry entry = mPlaylistEntries.get(i);
- if (entry.bestmatchlevel > 0) {
- try {
- values.clear();
- values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, Integer.valueOf(index));
- values.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, Long.valueOf(entry.bestmatchid));
- mMediaProvider.insert(playlistUri, values);
- index++;
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in MediaScanner.processCachedPlaylist()", e);
- return;
- }
- }
- }
- mPlaylistEntries.clear();
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
+ private void setLocale(String locale) {
+ throw new UnsupportedOperationException();
}
- private void processM3uPlayList(String path, String playListDirectory, Uri uri,
- ContentValues values, Cursor fileList) {
- BufferedReader reader = null;
- try {
- File f = new File(path);
- if (f.exists()) {
- reader = new BufferedReader(
- new InputStreamReader(new FileInputStream(f)), 8192);
- String line = reader.readLine();
- mPlaylistEntries.clear();
- while (line != null) {
- // ignore comment lines, which begin with '#'
- if (line.length() > 0 && line.charAt(0) != '#') {
- cachePlaylistEntry(line, playListDirectory);
- }
- line = reader.readLine();
- }
-
- processCachedPlaylist(fileList, values, uri);
- }
- } catch (IOException e) {
- Log.e(TAG, "IOException in MediaScanner.processM3uPlayList()", e);
- } finally {
- try {
- if (reader != null)
- reader.close();
- } catch (IOException e) {
- Log.e(TAG, "IOException in MediaScanner.processM3uPlayList()", e);
- }
- }
- }
-
- private void processPlsPlayList(String path, String playListDirectory, Uri uri,
- ContentValues values, Cursor fileList) {
- BufferedReader reader = null;
- try {
- File f = new File(path);
- if (f.exists()) {
- reader = new BufferedReader(
- new InputStreamReader(new FileInputStream(f)), 8192);
- String line = reader.readLine();
- mPlaylistEntries.clear();
- while (line != null) {
- // ignore comment lines, which begin with '#'
- if (line.startsWith("File")) {
- int equals = line.indexOf('=');
- if (equals > 0) {
- cachePlaylistEntry(line.substring(equals + 1), playListDirectory);
- }
- }
- line = reader.readLine();
- }
-
- processCachedPlaylist(fileList, values, uri);
- }
- } catch (IOException e) {
- Log.e(TAG, "IOException in MediaScanner.processPlsPlayList()", e);
- } finally {
- try {
- if (reader != null)
- reader.close();
- } catch (IOException e) {
- Log.e(TAG, "IOException in MediaScanner.processPlsPlayList()", e);
- }
- }
- }
-
- class WplHandler implements ElementListener {
-
- final ContentHandler handler;
- String playListDirectory;
-
- public WplHandler(String playListDirectory, Uri uri, Cursor fileList) {
- this.playListDirectory = playListDirectory;
-
- RootElement root = new RootElement("smil");
- Element body = root.getChild("body");
- Element seq = body.getChild("seq");
- Element media = seq.getChild("media");
- media.setElementListener(this);
-
- this.handler = root.getContentHandler();
- }
-
- @Override
- public void start(Attributes attributes) {
- String path = attributes.getValue("", "src");
- if (path != null) {
- cachePlaylistEntry(path, playListDirectory);
- }
- }
-
- @Override
- public void end() {
- }
-
- ContentHandler getContentHandler() {
- return handler;
- }
- }
-
- private void processWplPlayList(String path, String playListDirectory, Uri uri,
- ContentValues values, Cursor fileList) {
- FileInputStream fis = null;
- try {
- File f = new File(path);
- if (f.exists()) {
- fis = new FileInputStream(f);
-
- mPlaylistEntries.clear();
- Xml.parse(fis, Xml.findEncodingByName("UTF-8"),
- new WplHandler(playListDirectory, uri, fileList).getContentHandler());
-
- processCachedPlaylist(fileList, values, uri);
- }
- } catch (SAXException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- if (fis != null)
- fis.close();
- } catch (IOException e) {
- Log.e(TAG, "IOException in MediaScanner.processWplPlayList()", e);
- }
- }
- }
-
- private void processPlayList(FileEntry entry, Cursor fileList) throws RemoteException {
- String path = entry.mPath;
- ContentValues values = new ContentValues();
- int lastSlash = path.lastIndexOf('/');
- if (lastSlash < 0) throw new IllegalArgumentException("bad path " + path);
- Uri uri, membersUri;
- long rowId = entry.mRowId;
-
- // make sure we have a name
- String name = values.getAsString(MediaStore.Audio.Playlists.NAME);
- if (name == null) {
- name = values.getAsString(MediaStore.MediaColumns.TITLE);
- if (name == null) {
- // extract name from file name
- int lastDot = path.lastIndexOf('.');
- name = (lastDot < 0 ? path.substring(lastSlash + 1)
- : path.substring(lastSlash + 1, lastDot));
- }
- }
-
- values.put(MediaStore.Audio.Playlists.NAME, name);
- values.put(MediaStore.Audio.Playlists.DATE_MODIFIED, entry.mLastModified);
-
- if (rowId == 0) {
- values.put(MediaStore.Audio.Playlists.DATA, path);
- uri = mMediaProvider.insert(mPlaylistsUri, values);
- rowId = ContentUris.parseId(uri);
- membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY);
- } else {
- uri = ContentUris.withAppendedId(mPlaylistsUri, rowId);
- mMediaProvider.update(uri, values, null, null);
-
- // delete members of existing playlist
- membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY);
- mMediaProvider.delete(membersUri, null, null);
- }
-
- String playListDirectory = path.substring(0, lastSlash + 1);
- String mimeType = MediaFile.getMimeTypeForFile(path);
- switch (mimeType) {
- case "application/vnd.ms-wpl":
- processWplPlayList(path, playListDirectory, membersUri, values, fileList);
- break;
- case "audio/x-mpegurl":
- processM3uPlayList(path, playListDirectory, membersUri, values, fileList);
- break;
- case "audio/x-scpls":
- processPlsPlayList(path, playListDirectory, membersUri, values, fileList);
- break;
- }
- }
-
- private void processPlayLists() throws RemoteException {
- Iterator<FileEntry> iterator = mPlayLists.iterator();
- Cursor fileList = null;
- try {
- // use the files uri and projection because we need the format column,
- // but restrict the query to just audio files
- fileList = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
- "media_type=2", null, null, null);
- while (iterator.hasNext()) {
- FileEntry entry = iterator.next();
- // only process playlist files if they are new or have been modified since the last scan
- if (entry.mLastModifiedChanged) {
- processPlayList(entry, fileList);
- }
- }
- } catch (RemoteException e1) {
- } finally {
- if (fileList != null) {
- fileList.close();
- }
- }
- }
-
- private native void processDirectory(String path, MediaScannerClient client);
- private native boolean processFile(String path, String mimeType, MediaScannerClient client);
- @UnsupportedAppUsage
- private native void setLocale(String locale);
-
- public native byte[] extractAlbumArt(FileDescriptor fd);
-
- private static native final void native_init();
- private native final void native_setup();
- private native final void native_finalize();
-
@Override
public void close() {
- mCloseGuard.close();
- if (mClosed.compareAndSet(false, true)) {
- mMediaProvider.close();
- native_finalize();
- }
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- if (mCloseGuard != null) {
- mCloseGuard.warnIfOpen();
- }
-
- close();
- } finally {
- super.finalize();
- }
+ throw new UnsupportedOperationException();
}
}
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 435d8d766149..9064e6891be6 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -22,6 +22,7 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.annotation.WorkerThread;
import android.app.Activity;
@@ -40,9 +41,11 @@ import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.MediaStore;
+import android.provider.MediaStore.MediaColumns;
import android.provider.Settings;
import android.provider.Settings.System;
import android.util.Log;
@@ -846,7 +849,7 @@ public class RingtoneManager {
* Adds an audio file to the list of ringtones.
*
* After making sure the given file is an audio file, copies the file to the ringtone storage,
- * and asks the {@link android.media.MediaScanner} to scan that file. This call will block until
+ * and asks the system to scan that file. This call will block until
* the scan is completed.
*
* The directory where the copied file is stored is the directory that matches the ringtone's
@@ -1097,4 +1100,62 @@ public class RingtoneManager {
return null;
}
}
+
+ /**
+ * Ensure that ringtones have been set at least once on this device. This
+ * should be called after the device has finished scanned all media on
+ * {@link MediaStore#VOLUME_INTERNAL}, so that default ringtones can be
+ * configured.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS)
+ public static void ensureDefaultRingtones(@NonNull Context context) {
+ for (int type : new int[] {
+ TYPE_RINGTONE,
+ TYPE_NOTIFICATION,
+ TYPE_ALARM,
+ }) {
+ // Skip if we've already defined it at least once, so we don't
+ // overwrite the user changing to null
+ final String setting = getDefaultRingtoneSetting(type);
+ if (Settings.System.getInt(context.getContentResolver(), setting, 0) != 0) {
+ continue;
+ }
+
+ // Try finding the scanned ringtone
+ final String filename = getDefaultRingtoneFilename(type);
+ final Uri baseUri = MediaStore.Audio.Media.INTERNAL_CONTENT_URI;
+ try (Cursor cursor = context.getContentResolver().query(baseUri,
+ new String[] { MediaColumns._ID },
+ MediaColumns.DISPLAY_NAME + "=?",
+ new String[] { filename }, null)) {
+ if (cursor.moveToFirst()) {
+ final Uri ringtoneUri = context.getContentResolver().canonicalizeOrElse(
+ ContentUris.withAppendedId(baseUri, cursor.getLong(0)));
+ RingtoneManager.setActualDefaultRingtoneUri(context, type, ringtoneUri);
+ Settings.System.putInt(context.getContentResolver(), setting, 1);
+ }
+ }
+ }
+ }
+
+ private static String getDefaultRingtoneSetting(int type) {
+ switch (type) {
+ case TYPE_RINGTONE: return "ringtone_set";
+ case TYPE_NOTIFICATION: return "notification_sound_set";
+ case TYPE_ALARM: return "alarm_alert_set";
+ default: throw new IllegalArgumentException();
+ }
+ }
+
+ private static String getDefaultRingtoneFilename(int type) {
+ switch (type) {
+ case TYPE_RINGTONE: return SystemProperties.get("ro.config.ringtone");
+ case TYPE_NOTIFICATION: return SystemProperties.get("ro.config.notification_sound");
+ case TYPE_ALARM: return SystemProperties.get("ro.config.alarm_alert");
+ default: throw new IllegalArgumentException();
+ }
+ }
}
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index fb581b532dd2..a315c1eefb52 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -139,6 +139,12 @@ public class ThumbnailUtils {
/**
* Create a thumbnail for given audio file.
+ * <p>
+ * This method should only be used for files that you have direct access to;
+ * if you'd like to work with media hosted outside your app, consider using
+ * {@link ContentResolver#loadThumbnail(Uri, Size, CancellationSignal)}
+ * which enables remote providers to efficiently cache and invalidate
+ * thumbnails.
*
* @param file The audio file.
* @param size The desired thumbnail size.
@@ -231,6 +237,12 @@ public class ThumbnailUtils {
/**
* Create a thumbnail for given image file.
+ * <p>
+ * This method should only be used for files that you have direct access to;
+ * if you'd like to work with media hosted outside your app, consider using
+ * {@link ContentResolver#loadThumbnail(Uri, Size, CancellationSignal)}
+ * which enables remote providers to efficiently cache and invalidate
+ * thumbnails.
*
* @param file The audio file.
* @param size The desired thumbnail size.
@@ -334,6 +346,12 @@ public class ThumbnailUtils {
/**
* Create a thumbnail for given video file.
+ * <p>
+ * This method should only be used for files that you have direct access to;
+ * if you'd like to work with media hosted outside your app, consider using
+ * {@link ContentResolver#loadThumbnail(Uri, Size, CancellationSignal)}
+ * which enables remote providers to efficiently cache and invalidate
+ * thumbnails.
*
* @param file The video file.
* @param size The desired thumbnail size.
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 39474e13a2d0..01f12500b77b 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -853,6 +853,10 @@ public class AudioPolicy {
Log.v(TAG, "notifyVolumeAdjust: " + adjustment);
}
}
+
+ public void notifyUnregistration() {
+ setRegistration(null);
+ }
};
//==================================================
diff --git a/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl b/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl
index 107e7cd59ca2..1d581516e7b1 100644
--- a/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl
+++ b/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl
@@ -34,4 +34,8 @@ oneway interface IAudioPolicyCallback {
// callback for volume events
void notifyVolumeAdjust(int adjustment);
+
+ // callback for unregistration (e.g. if policy couldn't automatically be re-registered after
+ // an audioserver crash)
+ void notifyUnregistration();
}
diff --git a/media/java/android/media/midi/MidiDeviceInfo.aidl b/media/java/android/media/midi/MidiDeviceInfo.aidl
index 5b2ac9b64663..a2482040526d 100644
--- a/media/java/android/media/midi/MidiDeviceInfo.aidl
+++ b/media/java/android/media/midi/MidiDeviceInfo.aidl
@@ -16,4 +16,4 @@
package android.media.midi;
-parcelable MidiDeviceInfo cpp_header "media/MidiDeviceInfo.h";
+parcelable MidiDeviceInfo cpp_header "MidiDeviceInfo.h";
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 84fe27de15ab..b4edabfb2e8b 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -4,6 +4,7 @@ cc_library_shared {
srcs: [
"android_media_ImageWriter.cpp",
"android_media_ImageReader.cpp",
+ "android_media_JetPlayer.cpp",
"android_media_MediaCrypto.cpp",
"android_media_MediaCodec.cpp",
"android_media_MediaCodecList.cpp",
@@ -17,7 +18,6 @@ cc_library_shared {
"android_media_MediaPlayer.cpp",
"android_media_MediaProfiles.cpp",
"android_media_MediaRecorder.cpp",
- "android_media_MediaScanner.cpp",
"android_media_MediaSync.cpp",
"android_media_ResampleInputStream.cpp",
"android_media_Streams.cpp",
@@ -25,15 +25,18 @@ cc_library_shared {
"android_mtp_MtpDatabase.cpp",
"android_mtp_MtpDevice.cpp",
"android_mtp_MtpServer.cpp",
+ "JetPlayer.cpp",
],
shared_libs: [
"libandroid_runtime",
+ "libaudioclient",
"libnativehelper",
"libnativewindow",
"libutils",
"libbinder",
"libmedia",
+ "libmedia_codeclist",
"libmedia_jni_utils",
"libmedia_omx",
"libmediametrics",
@@ -52,15 +55,22 @@ cc_library_shared {
"libandroidfw",
"libhidlallocatorutils",
"libhidlbase",
+ "libsonivox",
"android.hardware.cas@1.0",
"android.hardware.cas.native@1.0",
"android.hidl.memory@1.0",
"android.hidl.token@1.0-utils",
],
- header_libs: ["libhardware_headers"],
+ header_libs: [
+ "libhardware_headers",
+ "libmediadrm_headers",
+ ],
- static_libs: ["libgrallocusage"],
+ static_libs: [
+ "libgrallocusage",
+ "libmedia_midiiowrapper",
+ ],
include_dirs: [
"frameworks/base/core/jni",
@@ -113,88 +123,6 @@ cc_library_shared {
],
}
-cc_library_shared {
- name: "libmedia2_jni",
-
- srcs: [
- "android_media_DataSourceCallback.cpp",
- "android_media_MediaMetricsJNI.cpp",
- "android_media_MediaPlayer2.cpp",
- "android_media_SyncParams.cpp",
- ],
-
- shared_libs: [
- // NDK or LLNDK or NDK-compliant
- "libandroid",
- "libbinder_ndk",
- "libcgrouprc",
- "libmediandk",
- "libmediametrics",
- "libnativehelper_compat_libc++",
- "liblog",
- "libvndksupport",
- ],
-
- header_libs: [
- "libhardware_headers",
- "libnativewindow_headers",
- ],
-
- static_libs: [
- // MediaCas
- "android.hidl.allocator@1.0",
- "android.hidl.memory@1.0",
- "libhidlbase",
- "libhidlmemory",
- "libbinderthreadstate",
-
- // MediaPlayer2 implementation
- "libbase",
- "libcrypto",
- "libcutils",
- "libjsoncpp",
- "libmedia_player2_util",
- "libmediaplayer2",
- "libmediaplayer2-protos",
- "libmediandk_utils",
- "libmediautils",
- "libprocessgroup",
- "libprotobuf-cpp-lite",
- "libstagefright_esds",
- "libstagefright_foundation_without_imemory",
- "libstagefright_httplive",
- "libstagefright_id3",
- "libstagefright_mpeg2support",
- "libstagefright_nuplayer2",
- "libstagefright_player2",
- "libstagefright_rtsp_player2",
- "libstagefright_timedtext2",
- "libutils",
- "libmedia2_jni_core",
- ],
-
- group_static_libs: true,
-
- include_dirs: [
- "frameworks/base/core/jni",
- "frameworks/native/include/media/openmax",
- "system/media/camera/include",
- ],
-
- export_include_dirs: ["."],
-
- cflags: [
- "-Wall",
- "-Werror",
- "-Wno-error=deprecated-declarations",
- "-Wunused",
- "-Wunreachable-code",
- "-fvisibility=hidden",
- ],
-
- ldflags: ["-Wl,--exclude-libs=ALL,-error-limit=0"],
-}
-
subdirs = [
"audioeffect",
"soundpool",
diff --git a/media/jni/JetPlayer.cpp b/media/jni/JetPlayer.cpp
new file mode 100644
index 000000000000..358feb7f73d9
--- /dev/null
+++ b/media/jni/JetPlayer.cpp
@@ -0,0 +1,471 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "JetPlayer-C"
+
+#include <utils/Log.h>
+#include "JetPlayer.h"
+
+
+namespace android
+{
+
+static const int MIX_NUM_BUFFERS = 4;
+static const S_EAS_LIB_CONFIG* pLibConfig = NULL;
+
+//-------------------------------------------------------------------------------------------------
+JetPlayer::JetPlayer(void *javaJetPlayer, int maxTracks, int trackBufferSize) :
+ mEventCallback(NULL),
+ mJavaJetPlayerRef(javaJetPlayer),
+ mTid(-1),
+ mRender(false),
+ mPaused(false),
+ mMaxTracks(maxTracks),
+ mEasData(NULL),
+ mIoWrapper(NULL),
+ mTrackBufferSize(trackBufferSize)
+{
+ ALOGV("JetPlayer constructor");
+ mPreviousJetStatus.currentUserID = -1;
+ mPreviousJetStatus.segmentRepeatCount = -1;
+ mPreviousJetStatus.numQueuedSegments = -1;
+ mPreviousJetStatus.paused = true;
+}
+
+//-------------------------------------------------------------------------------------------------
+JetPlayer::~JetPlayer()
+{
+ ALOGV("~JetPlayer");
+ release();
+}
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::init()
+{
+ //Mutex::Autolock lock(&mMutex);
+
+ EAS_RESULT result;
+
+ // retrieve the EAS library settings
+ if (pLibConfig == NULL)
+ pLibConfig = EAS_Config();
+ if (pLibConfig == NULL) {
+ ALOGE("JetPlayer::init(): EAS library configuration could not be retrieved, aborting.");
+ return EAS_FAILURE;
+ }
+
+ // init the EAS library
+ result = EAS_Init(&mEasData);
+ if (result != EAS_SUCCESS) {
+ ALOGE("JetPlayer::init(): Error initializing Sonivox EAS library, aborting.");
+ mState = EAS_STATE_ERROR;
+ return result;
+ }
+ // init the JET library with the default app event controller range
+ result = JET_Init(mEasData, NULL, sizeof(S_JET_CONFIG));
+ if (result != EAS_SUCCESS) {
+ ALOGE("JetPlayer::init(): Error initializing JET library, aborting.");
+ mState = EAS_STATE_ERROR;
+ return result;
+ }
+
+ // create the output AudioTrack
+ mAudioTrack = new AudioTrack();
+ status_t status = mAudioTrack->set(AUDIO_STREAM_MUSIC, //TODO parameterize this
+ pLibConfig->sampleRate,
+ AUDIO_FORMAT_PCM_16_BIT,
+ audio_channel_out_mask_from_count(pLibConfig->numChannels),
+ (size_t) mTrackBufferSize,
+ AUDIO_OUTPUT_FLAG_NONE);
+ if (status != OK) {
+ ALOGE("JetPlayer::init(): Error initializing JET library; AudioTrack error %d", status);
+ mAudioTrack.clear();
+ mState = EAS_STATE_ERROR;
+ return EAS_FAILURE;
+ }
+
+ // create render and playback thread
+ {
+ Mutex::Autolock l(mMutex);
+ ALOGV("JetPlayer::init(): trying to start render thread");
+ mThread = new JetPlayerThread(this);
+ mThread->run("jetRenderThread", ANDROID_PRIORITY_AUDIO);
+ mCondition.wait(mMutex);
+ }
+ if (mTid > 0) {
+ // render thread started, we're ready
+ ALOGV("JetPlayer::init(): render thread(%d) successfully started.", mTid);
+ mState = EAS_STATE_READY;
+ } else {
+ ALOGE("JetPlayer::init(): failed to start render thread.");
+ mState = EAS_STATE_ERROR;
+ return EAS_FAILURE;
+ }
+
+ return EAS_SUCCESS;
+}
+
+void JetPlayer::setEventCallback(jetevent_callback eventCallback)
+{
+ Mutex::Autolock l(mMutex);
+ mEventCallback = eventCallback;
+}
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::release()
+{
+ ALOGV("JetPlayer::release()");
+ Mutex::Autolock lock(mMutex);
+ mPaused = true;
+ mRender = false;
+ if (mEasData) {
+ JET_Pause(mEasData);
+ JET_CloseFile(mEasData);
+ JET_Shutdown(mEasData);
+ EAS_Shutdown(mEasData);
+ }
+ delete mIoWrapper;
+ mIoWrapper = NULL;
+ if (mAudioTrack != 0) {
+ mAudioTrack->stop();
+ mAudioTrack->flush();
+ mAudioTrack.clear();
+ }
+ if (mAudioBuffer) {
+ delete mAudioBuffer;
+ mAudioBuffer = NULL;
+ }
+ mEasData = NULL;
+
+ return EAS_SUCCESS;
+}
+
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::render() {
+ EAS_RESULT result = EAS_FAILURE;
+ EAS_I32 count;
+ int temp;
+ bool audioStarted = false;
+
+ ALOGV("JetPlayer::render(): entering");
+
+ // allocate render buffer
+ mAudioBuffer =
+ new EAS_PCM[pLibConfig->mixBufferSize * pLibConfig->numChannels * MIX_NUM_BUFFERS];
+
+ // signal main thread that we started
+ {
+ Mutex::Autolock l(mMutex);
+ mTid = gettid();
+ ALOGV("JetPlayer::render(): render thread(%d) signal", mTid);
+ mCondition.signal();
+ }
+
+ while (1) {
+
+ mMutex.lock(); // [[[[[[[[ LOCK ---------------------------------------
+
+ if (mEasData == NULL) {
+ mMutex.unlock();
+ ALOGV("JetPlayer::render(): NULL EAS data, exiting render.");
+ goto threadExit;
+ }
+
+ // nothing to render, wait for client thread to wake us up
+ while (!mRender)
+ {
+ ALOGV("JetPlayer::render(): signal wait");
+ if (audioStarted) {
+ mAudioTrack->pause();
+ // we have to restart the playback once we start rendering again
+ audioStarted = false;
+ }
+ mCondition.wait(mMutex);
+ ALOGV("JetPlayer::render(): signal rx'd");
+ }
+
+ // render midi data into the input buffer
+ int num_output = 0;
+ EAS_PCM* p = mAudioBuffer;
+ for (int i = 0; i < MIX_NUM_BUFFERS; i++) {
+ result = EAS_Render(mEasData, p, pLibConfig->mixBufferSize, &count);
+ if (result != EAS_SUCCESS) {
+ ALOGE("JetPlayer::render(): EAS_Render returned error %ld", result);
+ }
+ p += count * pLibConfig->numChannels;
+ num_output += count * pLibConfig->numChannels * sizeof(EAS_PCM);
+
+ // send events that were generated (if any) to the event callback
+ fireEventsFromJetQueue();
+ }
+
+ // update playback state
+ //ALOGV("JetPlayer::render(): updating state");
+ JET_Status(mEasData, &mJetStatus);
+ fireUpdateOnStatusChange();
+ mPaused = mJetStatus.paused;
+
+ mMutex.unlock(); // UNLOCK ]]]]]]]] -----------------------------------
+
+ // check audio output track
+ if (mAudioTrack == NULL) {
+ ALOGE("JetPlayer::render(): output AudioTrack was not created");
+ goto threadExit;
+ }
+
+ // Write data to the audio hardware
+ //ALOGV("JetPlayer::render(): writing to audio output");
+ if ((temp = mAudioTrack->write(mAudioBuffer, num_output)) < 0) {
+ ALOGE("JetPlayer::render(): Error in writing:%d",temp);
+ return temp;
+ }
+
+ // start audio output if necessary
+ if (!audioStarted) {
+ ALOGV("JetPlayer::render(): starting audio playback");
+ mAudioTrack->start();
+ audioStarted = true;
+ }
+
+ }//while (1)
+
+threadExit:
+ if (mAudioTrack != NULL) {
+ mAudioTrack->stop();
+ mAudioTrack->flush();
+ }
+ delete [] mAudioBuffer;
+ mAudioBuffer = NULL;
+ mMutex.lock();
+ mTid = -1;
+ mCondition.signal();
+ mMutex.unlock();
+ return result;
+}
+
+
+//-------------------------------------------------------------------------------------------------
+// fire up an update if any of the status fields has changed
+// precondition: mMutex locked
+void JetPlayer::fireUpdateOnStatusChange()
+{
+ if ( (mJetStatus.currentUserID != mPreviousJetStatus.currentUserID)
+ ||(mJetStatus.segmentRepeatCount != mPreviousJetStatus.segmentRepeatCount) ) {
+ if (mEventCallback) {
+ mEventCallback(
+ JetPlayer::JET_USERID_UPDATE,
+ mJetStatus.currentUserID,
+ mJetStatus.segmentRepeatCount,
+ mJavaJetPlayerRef);
+ }
+ mPreviousJetStatus.currentUserID = mJetStatus.currentUserID;
+ mPreviousJetStatus.segmentRepeatCount = mJetStatus.segmentRepeatCount;
+ }
+
+ if (mJetStatus.numQueuedSegments != mPreviousJetStatus.numQueuedSegments) {
+ if (mEventCallback) {
+ mEventCallback(
+ JetPlayer::JET_NUMQUEUEDSEGMENT_UPDATE,
+ mJetStatus.numQueuedSegments,
+ -1,
+ mJavaJetPlayerRef);
+ }
+ mPreviousJetStatus.numQueuedSegments = mJetStatus.numQueuedSegments;
+ }
+
+ if (mJetStatus.paused != mPreviousJetStatus.paused) {
+ if (mEventCallback) {
+ mEventCallback(JetPlayer::JET_PAUSE_UPDATE,
+ mJetStatus.paused,
+ -1,
+ mJavaJetPlayerRef);
+ }
+ mPreviousJetStatus.paused = mJetStatus.paused;
+ }
+
+}
+
+
+//-------------------------------------------------------------------------------------------------
+// fire up all the JET events in the JET engine queue (until the queue is empty)
+// precondition: mMutex locked
+void JetPlayer::fireEventsFromJetQueue()
+{
+ if (!mEventCallback) {
+ // no callback, just empty the event queue
+ while (JET_GetEvent(mEasData, NULL, NULL)) { }
+ return;
+ }
+
+ EAS_U32 rawEvent;
+ while (JET_GetEvent(mEasData, &rawEvent, NULL)) {
+ mEventCallback(
+ JetPlayer::JET_EVENT,
+ rawEvent,
+ -1,
+ mJavaJetPlayerRef);
+ }
+}
+
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::loadFromFile(const char* path)
+{
+ ALOGV("JetPlayer::loadFromFile(): path=%s", path);
+
+ Mutex::Autolock lock(mMutex);
+
+ delete mIoWrapper;
+ mIoWrapper = new MidiIoWrapper(path);
+
+ EAS_RESULT result = JET_OpenFile(mEasData, mIoWrapper->getLocator());
+ if (result != EAS_SUCCESS)
+ mState = EAS_STATE_ERROR;
+ else
+ mState = EAS_STATE_OPEN;
+ return( result );
+}
+
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::loadFromFD(const int fd, const long long offset, const long long length)
+{
+ ALOGV("JetPlayer::loadFromFD(): fd=%d offset=%lld length=%lld", fd, offset, length);
+
+ Mutex::Autolock lock(mMutex);
+
+ delete mIoWrapper;
+ mIoWrapper = new MidiIoWrapper(fd, offset, length);
+
+ EAS_RESULT result = JET_OpenFile(mEasData, mIoWrapper->getLocator());
+ if (result != EAS_SUCCESS)
+ mState = EAS_STATE_ERROR;
+ else
+ mState = EAS_STATE_OPEN;
+ return( result );
+}
+
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::closeFile()
+{
+ Mutex::Autolock lock(mMutex);
+ return JET_CloseFile(mEasData);
+}
+
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::play()
+{
+ ALOGV("JetPlayer::play(): entering");
+ Mutex::Autolock lock(mMutex);
+
+ EAS_RESULT result = JET_Play(mEasData);
+
+ mPaused = false;
+ mRender = true;
+
+ JET_Status(mEasData, &mJetStatus);
+ this->dumpJetStatus(&mJetStatus);
+
+ fireUpdateOnStatusChange();
+
+ // wake up render thread
+ ALOGV("JetPlayer::play(): wakeup render thread");
+ mCondition.signal();
+
+ return result;
+}
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::pause()
+{
+ Mutex::Autolock lock(mMutex);
+ mPaused = true;
+ EAS_RESULT result = JET_Pause(mEasData);
+
+ mRender = false;
+
+ JET_Status(mEasData, &mJetStatus);
+ this->dumpJetStatus(&mJetStatus);
+ fireUpdateOnStatusChange();
+
+
+ return result;
+}
+
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::queueSegment(int segmentNum, int libNum, int repeatCount, int transpose,
+ EAS_U32 muteFlags, EAS_U8 userID)
+{
+ ALOGV("JetPlayer::queueSegment segmentNum=%d, libNum=%d, repeatCount=%d, transpose=%d",
+ segmentNum, libNum, repeatCount, transpose);
+ Mutex::Autolock lock(mMutex);
+ return JET_QueueSegment(mEasData, segmentNum, libNum, repeatCount, transpose, muteFlags,
+ userID);
+}
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::setMuteFlags(EAS_U32 muteFlags, bool sync)
+{
+ Mutex::Autolock lock(mMutex);
+ return JET_SetMuteFlags(mEasData, muteFlags, sync);
+}
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::setMuteFlag(int trackNum, bool muteFlag, bool sync)
+{
+ Mutex::Autolock lock(mMutex);
+ return JET_SetMuteFlag(mEasData, trackNum, muteFlag, sync);
+}
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::triggerClip(int clipId)
+{
+ ALOGV("JetPlayer::triggerClip clipId=%d", clipId);
+ Mutex::Autolock lock(mMutex);
+ return JET_TriggerClip(mEasData, clipId);
+}
+
+//-------------------------------------------------------------------------------------------------
+int JetPlayer::clearQueue()
+{
+ ALOGV("JetPlayer::clearQueue");
+ Mutex::Autolock lock(mMutex);
+ return JET_Clear_Queue(mEasData);
+}
+
+//-------------------------------------------------------------------------------------------------
+void JetPlayer::dump()
+{
+}
+
+void JetPlayer::dumpJetStatus(S_JET_STATUS* pJetStatus)
+{
+ if (pJetStatus!=NULL)
+ ALOGV(">> current JET player status: userID=%d segmentRepeatCount=%d numQueuedSegments=%d "
+ "paused=%d",
+ pJetStatus->currentUserID, pJetStatus->segmentRepeatCount,
+ pJetStatus->numQueuedSegments, pJetStatus->paused);
+ else
+ ALOGE(">> JET player status is NULL");
+}
+
+
+} // end namespace android
diff --git a/media/jni/JetPlayer.h b/media/jni/JetPlayer.h
new file mode 100644
index 000000000000..bb569bcad7be
--- /dev/null
+++ b/media/jni/JetPlayer.h
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+
+#ifndef JETPLAYER_H_
+#define JETPLAYER_H_
+
+#include <utils/threads.h>
+
+#include <libsonivox/jet.h>
+#include <libsonivox/eas_types.h>
+#include <media/AudioTrack.h>
+#include <media/MidiIoWrapper.h>
+
+
+namespace android {
+
+typedef void (*jetevent_callback)(int eventType, int val1, int val2, void *cookie);
+
+class JetPlayer {
+
+public:
+
+ // to keep in sync with the JetPlayer class constants
+ // defined in frameworks/base/media/java/android/media/JetPlayer.java
+ static const int JET_EVENT = 1;
+ static const int JET_USERID_UPDATE = 2;
+ static const int JET_NUMQUEUEDSEGMENT_UPDATE = 3;
+ static const int JET_PAUSE_UPDATE = 4;
+
+ JetPlayer(void *javaJetPlayer,
+ int maxTracks = 32,
+ int trackBufferSize = 1200);
+ ~JetPlayer();
+ int init();
+ int release();
+
+ int loadFromFile(const char* url);
+ int loadFromFD(const int fd, const long long offset, const long long length);
+ int closeFile();
+ int play();
+ int pause();
+ int queueSegment(int segmentNum, int libNum, int repeatCount, int transpose,
+ EAS_U32 muteFlags, EAS_U8 userID);
+ int setMuteFlags(EAS_U32 muteFlags, bool sync);
+ int setMuteFlag(int trackNum, bool muteFlag, bool sync);
+ int triggerClip(int clipId);
+ int clearQueue();
+
+ void setEventCallback(jetevent_callback callback);
+
+ int getMaxTracks() { return mMaxTracks; };
+
+
+private:
+ int render();
+ void fireUpdateOnStatusChange();
+ void fireEventsFromJetQueue();
+
+ JetPlayer() {} // no default constructor
+ void dump();
+ void dumpJetStatus(S_JET_STATUS* pJetStatus);
+
+ jetevent_callback mEventCallback;
+
+ void* mJavaJetPlayerRef;
+ Mutex mMutex; // mutex to sync the render and playback thread with the JET calls
+ pid_t mTid;
+ Condition mCondition;
+ volatile bool mRender;
+ bool mPaused;
+
+ EAS_STATE mState;
+ int* mMemFailedVar;
+
+ int mMaxTracks; // max number of MIDI tracks, usually 32
+ EAS_DATA_HANDLE mEasData;
+ MidiIoWrapper* mIoWrapper;
+ EAS_PCM* mAudioBuffer;// EAS renders the MIDI data into this buffer,
+ sp<AudioTrack> mAudioTrack; // and we play it in this audio track
+ int mTrackBufferSize;
+ S_JET_STATUS mJetStatus;
+ S_JET_STATUS mPreviousJetStatus;
+
+ class JetPlayerThread : public Thread {
+ public:
+ JetPlayerThread(JetPlayer *player) : mPlayer(player) {
+ }
+
+ protected:
+ virtual ~JetPlayerThread() {}
+
+ private:
+ JetPlayer *mPlayer;
+
+ bool threadLoop() {
+ int result;
+ result = mPlayer->render();
+ return false;
+ }
+
+ JetPlayerThread(const JetPlayerThread &);
+ JetPlayerThread &operator=(const JetPlayerThread &);
+ };
+
+ sp<JetPlayerThread> mThread;
+
+}; // end class JetPlayer
+
+} // end namespace android
+
+
+
+#endif /*JETPLAYER_H_*/
diff --git a/media/jni/android_media_DataSourceCallback.cpp b/media/jni/android_media_DataSourceCallback.cpp
deleted file mode 100644
index c91d4095a32f..000000000000
--- a/media/jni/android_media_DataSourceCallback.cpp
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright 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.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "JDataSourceCallback-JNI"
-#include <utils/Log.h>
-
-#include "android_media_DataSourceCallback.h"
-
-#include "log/log.h"
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-
-#include <drm/drm_framework_common.h>
-#include <mediaplayer2/JavaVMHelper.h>
-#include <media/stagefright/foundation/ADebug.h>
-#include <nativehelper/ScopedLocalRef.h>
-
-namespace android {
-
-static const size_t kBufferSize = 64 * 1024;
-
-JDataSourceCallback::JDataSourceCallback(JNIEnv* env, jobject source)
- : mJavaObjStatus(OK),
- mSizeIsCached(false),
- mCachedSize(0) {
- mDataSourceCallbackObj = env->NewGlobalRef(source);
- CHECK(mDataSourceCallbackObj != NULL);
-
- ScopedLocalRef<jclass> media2DataSourceClass(env, env->GetObjectClass(mDataSourceCallbackObj));
- CHECK(media2DataSourceClass.get() != NULL);
-
- mReadAtMethod = env->GetMethodID(media2DataSourceClass.get(), "readAt", "(J[BII)I");
- CHECK(mReadAtMethod != NULL);
- mGetSizeMethod = env->GetMethodID(media2DataSourceClass.get(), "getSize", "()J");
- CHECK(mGetSizeMethod != NULL);
- mCloseMethod = env->GetMethodID(media2DataSourceClass.get(), "close", "()V");
- CHECK(mCloseMethod != NULL);
-
- ScopedLocalRef<jbyteArray> tmp(env, env->NewByteArray(kBufferSize));
- mByteArrayObj = (jbyteArray)env->NewGlobalRef(tmp.get());
- CHECK(mByteArrayObj != NULL);
-}
-
-JDataSourceCallback::~JDataSourceCallback() {
- JNIEnv* env = JavaVMHelper::getJNIEnv();
- env->DeleteGlobalRef(mDataSourceCallbackObj);
- env->DeleteGlobalRef(mByteArrayObj);
-}
-
-status_t JDataSourceCallback::initCheck() const {
- return OK;
-}
-
-ssize_t JDataSourceCallback::readAt(off64_t offset, void *data, size_t size) {
- Mutex::Autolock lock(mLock);
-
- if (mJavaObjStatus != OK) {
- return -1;
- }
- if (size > kBufferSize) {
- size = kBufferSize;
- }
-
- JNIEnv* env = JavaVMHelper::getJNIEnv();
- jint numread = env->CallIntMethod(mDataSourceCallbackObj, mReadAtMethod,
- (jlong)offset, mByteArrayObj, (jint)0, (jint)size);
- if (env->ExceptionCheck()) {
- ALOGW("An exception occurred in readAt()");
- jniLogException(env, ANDROID_LOG_WARN, LOG_TAG);
- env->ExceptionClear();
- mJavaObjStatus = UNKNOWN_ERROR;
- return -1;
- }
- if (numread < 0) {
- if (numread != -1) {
- ALOGW("An error occurred in readAt()");
- mJavaObjStatus = UNKNOWN_ERROR;
- return -1;
- } else {
- // numread == -1 indicates EOF
- return 0;
- }
- }
- if ((size_t)numread > size) {
- ALOGE("readAt read too many bytes.");
- mJavaObjStatus = UNKNOWN_ERROR;
- return -1;
- }
-
- ALOGV("readAt %lld / %zu => %d.", (long long)offset, size, numread);
- env->GetByteArrayRegion(mByteArrayObj, 0, numread, (jbyte*)data);
- return numread;
-}
-
-status_t JDataSourceCallback::getSize(off64_t* size) {
- Mutex::Autolock lock(mLock);
-
- if (mJavaObjStatus != OK) {
- return UNKNOWN_ERROR;
- }
- if (mSizeIsCached) {
- *size = mCachedSize;
- return OK;
- }
-
- JNIEnv* env = JavaVMHelper::getJNIEnv();
- *size = env->CallLongMethod(mDataSourceCallbackObj, mGetSizeMethod);
- if (env->ExceptionCheck()) {
- ALOGW("An exception occurred in getSize()");
- jniLogException(env, ANDROID_LOG_WARN, LOG_TAG);
- env->ExceptionClear();
- // After returning an error, size shouldn't be used by callers.
- *size = UNKNOWN_ERROR;
- mJavaObjStatus = UNKNOWN_ERROR;
- return UNKNOWN_ERROR;
- }
-
- // The minimum size should be -1, which indicates unknown size.
- if (*size < 0) {
- *size = -1;
- }
-
- mCachedSize = *size;
- mSizeIsCached = true;
- return OK;
-}
-
-void JDataSourceCallback::close() {
- Mutex::Autolock lock(mLock);
-
- JNIEnv* env = JavaVMHelper::getJNIEnv();
- env->CallVoidMethod(mDataSourceCallbackObj, mCloseMethod);
- // The closed state is effectively the same as an error state.
- mJavaObjStatus = UNKNOWN_ERROR;
-}
-
-String8 JDataSourceCallback::toString() {
- return String8::format("JDataSourceCallback(pid %d, uid %d)", getpid(), getuid());
-}
-
-String8 JDataSourceCallback::getMIMEType() const {
- return String8("application/octet-stream");
-}
-
-} // namespace android
diff --git a/media/jni/android_media_DataSourceCallback.h b/media/jni/android_media_DataSourceCallback.h
deleted file mode 100644
index 5bde682754f3..000000000000
--- a/media/jni/android_media_DataSourceCallback.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#ifndef _ANDROID_MEDIA_DATASOURCECALLBACK_H_
-#define _ANDROID_MEDIA_DATASOURCECALLBACK_H_
-
-#include "jni.h"
-
-#include <media/DataSource.h>
-#include <media/stagefright/foundation/ABase.h>
-#include <utils/Errors.h>
-#include <utils/Mutex.h>
-
-namespace android {
-
-// The native counterpart to a Java android.media.DataSourceCallback. It inherits from
-// DataSource.
-//
-// If the java DataSource returns an error or throws an exception it
-// will be considered to be in a broken state, and the only further call this
-// will make is to close().
-class JDataSourceCallback : public DataSource {
-public:
- JDataSourceCallback(JNIEnv *env, jobject source);
- virtual ~JDataSourceCallback();
-
- virtual status_t initCheck() const override;
- virtual ssize_t readAt(off64_t offset, void *data, size_t size) override;
- virtual status_t getSize(off64_t *size) override;
-
- virtual String8 toString() override;
- virtual String8 getMIMEType() const override;
- virtual void close() override;
-private:
- // Protect all member variables with mLock because this object will be
- // accessed on different threads.
- Mutex mLock;
-
- // The status of the java DataSource. Set to OK unless an error occurred or
- // close() was called.
- status_t mJavaObjStatus;
- // Only call the java getSize() once so the app can't change the size on us.
- bool mSizeIsCached;
- off64_t mCachedSize;
-
- jobject mDataSourceCallbackObj;
- jmethodID mReadAtMethod;
- jmethodID mGetSizeMethod;
- jmethodID mCloseMethod;
- jbyteArray mByteArrayObj;
-
- DISALLOW_EVIL_CONSTRUCTORS(JDataSourceCallback);
-};
-
-} // namespace android
-
-#endif // _ANDROID_MEDIA_DATASOURCECALLBACK_H_
diff --git a/media/jni/android_media_JetPlayer.cpp b/media/jni/android_media_JetPlayer.cpp
new file mode 100644
index 000000000000..8a05f85e4285
--- /dev/null
+++ b/media/jni/android_media_JetPlayer.cpp
@@ -0,0 +1,538 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "JET_JNI"
+
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include "core_jni_helpers.h"
+
+#include <utils/Log.h>
+#include "JetPlayer.h"
+
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+static const char* const kClassPathName = "android/media/JetPlayer";
+
+// ----------------------------------------------------------------------------
+struct fields_t {
+ // these fields provide access from C++ to the...
+ jclass jetClass; // JetPlayer java class global ref
+ jmethodID postNativeEventInJava; // java method to post events to the Java thread from native
+ jfieldID nativePlayerInJavaObj; // stores in Java the native JetPlayer object
+};
+
+static fields_t javaJetPlayerFields;
+
+
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+
+/*
+ * This function is called from JetPlayer instance's render thread
+ */
+static void
+jetPlayerEventCallback(int what, int arg1=0, int arg2=0, void* javaTarget = NULL)
+{
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ if (env) {
+ env->CallStaticVoidMethod(
+ javaJetPlayerFields.jetClass, javaJetPlayerFields.postNativeEventInJava,
+ javaTarget,
+ what, arg1, arg2);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+ } else {
+ ALOGE("JET jetPlayerEventCallback(): No JNI env for JET event callback, can't post event.");
+ return;
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+
+static jboolean
+android_media_JetPlayer_setup(JNIEnv *env, jobject thiz, jobject weak_this,
+ jint maxTracks, jint trackBufferSize)
+{
+ //ALOGV("android_media_JetPlayer_setup(): entering.");
+ JetPlayer* lpJet = new JetPlayer(env->NewGlobalRef(weak_this), maxTracks, trackBufferSize);
+
+ EAS_RESULT result = lpJet->init();
+
+ if (result==EAS_SUCCESS) {
+ // save our newly created C++ JetPlayer in the "nativePlayerInJavaObj" field
+ // of the Java object (in mNativePlayerInJavaObj)
+ env->SetLongField(thiz, javaJetPlayerFields.nativePlayerInJavaObj, (jlong)lpJet);
+ return JNI_TRUE;
+ } else {
+ ALOGE("android_media_JetPlayer_setup(): initialization failed with EAS error code %d", (int)result);
+ delete lpJet;
+ env->SetLongField(weak_this, javaJetPlayerFields.nativePlayerInJavaObj, 0);
+ return JNI_FALSE;
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+static void
+android_media_JetPlayer_finalize(JNIEnv *env, jobject thiz)
+{
+ ALOGV("android_media_JetPlayer_finalize(): entering.");
+ JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
+ thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+ if (lpJet != NULL) {
+ lpJet->release();
+ delete lpJet;
+ }
+
+ ALOGV("android_media_JetPlayer_finalize(): exiting.");
+}
+
+
+// ----------------------------------------------------------------------------
+static void
+android_media_JetPlayer_release(JNIEnv *env, jobject thiz)
+{
+ android_media_JetPlayer_finalize(env, thiz);
+ env->SetLongField(thiz, javaJetPlayerFields.nativePlayerInJavaObj, 0);
+ ALOGV("android_media_JetPlayer_release() done");
+}
+
+
+// ----------------------------------------------------------------------------
+static jboolean
+android_media_JetPlayer_loadFromFile(JNIEnv *env, jobject thiz, jstring path)
+{
+ JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
+ thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+ if (lpJet == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve JetPlayer pointer for openFile()");
+ return JNI_FALSE;
+ }
+
+ // set up event callback function
+ lpJet->setEventCallback(jetPlayerEventCallback);
+
+ const char *pathStr = env->GetStringUTFChars(path, NULL);
+ if (pathStr == NULL) { // Out of memory
+ ALOGE("android_media_JetPlayer_openFile(): aborting, out of memory");
+ return JNI_FALSE;
+ }
+
+ ALOGV("android_media_JetPlayer_openFile(): trying to open %s", pathStr );
+ EAS_RESULT result = lpJet->loadFromFile(pathStr);
+ env->ReleaseStringUTFChars(path, pathStr);
+
+ if (result==EAS_SUCCESS) {
+ //ALOGV("android_media_JetPlayer_openFile(): file successfully opened");
+ return JNI_TRUE;
+ } else {
+ ALOGE("android_media_JetPlayer_openFile(): failed to open file with EAS error %d",
+ (int)result);
+ return JNI_FALSE;
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+static jboolean
+android_media_JetPlayer_loadFromFileD(JNIEnv *env, jobject thiz,
+ jobject fileDescriptor, jlong offset, jlong length)
+{
+ JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
+ thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+ if (lpJet == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve JetPlayer pointer for openFile()");
+ return JNI_FALSE;
+ }
+
+ // set up event callback function
+ lpJet->setEventCallback(jetPlayerEventCallback);
+
+ ALOGV("android_media_JetPlayer_openFileDescr(): trying to load JET file through its fd" );
+ EAS_RESULT result = lpJet->loadFromFD(jniGetFDFromFileDescriptor(env, fileDescriptor),
+ (long long)offset, (long long)length); // cast params to types used by EAS_FILE
+
+ if (result==EAS_SUCCESS) {
+ ALOGV("android_media_JetPlayer_openFileDescr(): file successfully opened");
+ return JNI_TRUE;
+ } else {
+ ALOGE("android_media_JetPlayer_openFileDescr(): failed to open file with EAS error %d",
+ (int)result);
+ return JNI_FALSE;
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+static jboolean
+android_media_JetPlayer_closeFile(JNIEnv *env, jobject thiz)
+{
+ JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
+ thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+ if (lpJet == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve JetPlayer pointer for closeFile()");
+ return JNI_FALSE;
+ }
+
+ if (lpJet->closeFile()==EAS_SUCCESS) {
+ //ALOGV("android_media_JetPlayer_closeFile(): file successfully closed");
+ return JNI_TRUE;
+ } else {
+ ALOGE("android_media_JetPlayer_closeFile(): failed to close file");
+ return JNI_FALSE;
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+static jboolean
+android_media_JetPlayer_play(JNIEnv *env, jobject thiz)
+{
+ JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
+ thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+ if (lpJet == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve JetPlayer pointer for play()");
+ return JNI_FALSE;
+ }
+
+ EAS_RESULT result = lpJet->play();
+ if (result==EAS_SUCCESS) {
+ //ALOGV("android_media_JetPlayer_play(): play successful");
+ return JNI_TRUE;
+ } else {
+ ALOGE("android_media_JetPlayer_play(): failed to play with EAS error code %ld",
+ result);
+ return JNI_FALSE;
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+static jboolean
+android_media_JetPlayer_pause(JNIEnv *env, jobject thiz)
+{
+ JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
+ thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+ if (lpJet == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve JetPlayer pointer for pause()");
+ return JNI_FALSE;
+ }
+
+ EAS_RESULT result = lpJet->pause();
+ if (result==EAS_SUCCESS) {
+ //ALOGV("android_media_JetPlayer_pause(): pause successful");
+ return JNI_TRUE;
+ } else {
+ if (result==EAS_ERROR_QUEUE_IS_EMPTY) {
+ ALOGV("android_media_JetPlayer_pause(): paused with an empty queue");
+ return JNI_TRUE;
+ } else
+ ALOGE("android_media_JetPlayer_pause(): failed to pause with EAS error code %ld",
+ result);
+ return JNI_FALSE;
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+static jboolean
+android_media_JetPlayer_queueSegment(JNIEnv *env, jobject thiz,
+ jint segmentNum, jint libNum, jint repeatCount, jint transpose, jint muteFlags,
+ jbyte userID)
+{
+ JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
+ thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+ if (lpJet == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve JetPlayer pointer for queueSegment()");
+ return JNI_FALSE;
+ }
+
+ EAS_RESULT result
+ = lpJet->queueSegment(segmentNum, libNum, repeatCount, transpose, muteFlags, userID);
+ if (result==EAS_SUCCESS) {
+ //ALOGV("android_media_JetPlayer_queueSegment(): segment successfully queued");
+ return JNI_TRUE;
+ } else {
+ ALOGE("android_media_JetPlayer_queueSegment(): failed with EAS error code %ld",
+ result);
+ return JNI_FALSE;
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+static jboolean
+android_media_JetPlayer_queueSegmentMuteArray(JNIEnv *env, jobject thiz,
+ jint segmentNum, jint libNum, jint repeatCount, jint transpose, jbooleanArray muteArray,
+ jbyte userID)
+{
+ JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
+ thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+ if (lpJet == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve JetPlayer pointer for queueSegmentMuteArray()");
+ return JNI_FALSE;
+ }
+
+ EAS_RESULT result=EAS_FAILURE;
+
+ jboolean *muteTracks = NULL;
+ muteTracks = env->GetBooleanArrayElements(muteArray, NULL);
+ if (muteTracks == NULL) {
+ ALOGE("android_media_JetPlayer_queueSegment(): failed to read track mute mask.");
+ return JNI_FALSE;
+ }
+
+ EAS_U32 muteMask=0;
+ int maxTracks = lpJet->getMaxTracks();
+ for (jint trackIndex=0; trackIndex<maxTracks; trackIndex++) {
+ if (muteTracks[maxTracks-1-trackIndex]==JNI_TRUE)
+ muteMask = (muteMask << 1) | 0x00000001;
+ else
+ muteMask = muteMask << 1;
+ }
+ //ALOGV("android_media_JetPlayer_queueSegmentMuteArray(): FINAL mute mask =0x%08lX", mask);
+
+ result = lpJet->queueSegment(segmentNum, libNum, repeatCount, transpose, muteMask, userID);
+
+ env->ReleaseBooleanArrayElements(muteArray, muteTracks, 0);
+ if (result==EAS_SUCCESS) {
+ //ALOGV("android_media_JetPlayer_queueSegmentMuteArray(): segment successfully queued");
+ return JNI_TRUE;
+ } else {
+ ALOGE("android_media_JetPlayer_queueSegmentMuteArray(): failed with EAS error code %ld",
+ result);
+ return JNI_FALSE;
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+static jboolean
+android_media_JetPlayer_setMuteFlags(JNIEnv *env, jobject thiz,
+ jint muteFlags /*unsigned?*/, jboolean bSync)
+{
+ JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
+ thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+ if (lpJet == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve JetPlayer pointer for setMuteFlags()");
+ return JNI_FALSE;
+ }
+
+ EAS_RESULT result;
+ result = lpJet->setMuteFlags(muteFlags, bSync==JNI_TRUE ? true : false);
+ if (result==EAS_SUCCESS) {
+ //ALOGV("android_media_JetPlayer_setMuteFlags(): mute flags successfully updated");
+ return JNI_TRUE;
+ } else {
+ ALOGE("android_media_JetPlayer_setMuteFlags(): failed with EAS error code %ld", result);
+ return JNI_FALSE;
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+static jboolean
+android_media_JetPlayer_setMuteArray(JNIEnv *env, jobject thiz,
+ jbooleanArray muteArray, jboolean bSync)
+{
+ JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
+ thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+ if (lpJet == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve JetPlayer pointer for setMuteArray()");
+ return JNI_FALSE;
+ }
+
+ EAS_RESULT result=EAS_FAILURE;
+
+ jboolean *muteTracks = NULL;
+ muteTracks = env->GetBooleanArrayElements(muteArray, NULL);
+ if (muteTracks == NULL) {
+ ALOGE("android_media_JetPlayer_setMuteArray(): failed to read track mute mask.");
+ return JNI_FALSE;
+ }
+
+ EAS_U32 muteMask=0;
+ int maxTracks = lpJet->getMaxTracks();
+ for (jint trackIndex=0; trackIndex<maxTracks; trackIndex++) {
+ if (muteTracks[maxTracks-1-trackIndex]==JNI_TRUE)
+ muteMask = (muteMask << 1) | 0x00000001;
+ else
+ muteMask = muteMask << 1;
+ }
+ //ALOGV("android_media_JetPlayer_setMuteArray(): FINAL mute mask =0x%08lX", muteMask);
+
+ result = lpJet->setMuteFlags(muteMask, bSync==JNI_TRUE ? true : false);
+
+ env->ReleaseBooleanArrayElements(muteArray, muteTracks, 0);
+ if (result==EAS_SUCCESS) {
+ //ALOGV("android_media_JetPlayer_setMuteArray(): mute flags successfully updated");
+ return JNI_TRUE;
+ } else {
+ ALOGE("android_media_JetPlayer_setMuteArray(): \
+ failed to update mute flags with EAS error code %ld", result);
+ return JNI_FALSE;
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+static jboolean
+android_media_JetPlayer_setMuteFlag(JNIEnv *env, jobject thiz,
+ jint trackId, jboolean muteFlag, jboolean bSync)
+{
+ JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
+ thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+ if (lpJet == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve JetPlayer pointer for setMuteFlag()");
+ return JNI_FALSE;
+ }
+
+ EAS_RESULT result;
+ result = lpJet->setMuteFlag(trackId,
+ muteFlag==JNI_TRUE ? true : false, bSync==JNI_TRUE ? true : false);
+ if (result==EAS_SUCCESS) {
+ //ALOGV("android_media_JetPlayer_setMuteFlag(): mute flag successfully updated for track %d", trackId);
+ return JNI_TRUE;
+ } else {
+ ALOGE("android_media_JetPlayer_setMuteFlag(): failed to update mute flag for track %d with EAS error code %ld",
+ trackId, result);
+ return JNI_FALSE;
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+static jboolean
+android_media_JetPlayer_triggerClip(JNIEnv *env, jobject thiz, jint clipId)
+{
+ JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
+ thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+ if (lpJet == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve JetPlayer pointer for triggerClip()");
+ return JNI_FALSE;
+ }
+
+ EAS_RESULT result;
+ result = lpJet->triggerClip(clipId);
+ if (result==EAS_SUCCESS) {
+ //ALOGV("android_media_JetPlayer_triggerClip(): triggerClip successful for clip %d", clipId);
+ return JNI_TRUE;
+ } else {
+ ALOGE("android_media_JetPlayer_triggerClip(): triggerClip for clip %d failed with EAS error code %ld",
+ clipId, result);
+ return JNI_FALSE;
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+static jboolean
+android_media_JetPlayer_clearQueue(JNIEnv *env, jobject thiz)
+{
+ JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
+ thiz, javaJetPlayerFields.nativePlayerInJavaObj);
+ if (lpJet == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve JetPlayer pointer for clearQueue()");
+ return JNI_FALSE;
+ }
+
+ EAS_RESULT result = lpJet->clearQueue();
+ if (result==EAS_SUCCESS) {
+ //ALOGV("android_media_JetPlayer_clearQueue(): clearQueue successful");
+ return JNI_TRUE;
+ } else {
+ ALOGE("android_media_JetPlayer_clearQueue(): clearQueue failed with EAS error code %ld",
+ result);
+ return JNI_FALSE;
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+static const JNINativeMethod gMethods[] = {
+ // name, signature, funcPtr
+ {"native_setup", "(Ljava/lang/Object;II)Z", (void *)android_media_JetPlayer_setup},
+ {"native_finalize", "()V", (void *)android_media_JetPlayer_finalize},
+ {"native_release", "()V", (void *)android_media_JetPlayer_release},
+ {"native_loadJetFromFile",
+ "(Ljava/lang/String;)Z", (void *)android_media_JetPlayer_loadFromFile},
+ {"native_loadJetFromFileD", "(Ljava/io/FileDescriptor;JJ)Z",
+ (void *)android_media_JetPlayer_loadFromFileD},
+ {"native_closeJetFile","()Z", (void *)android_media_JetPlayer_closeFile},
+ {"native_playJet", "()Z", (void *)android_media_JetPlayer_play},
+ {"native_pauseJet", "()Z", (void *)android_media_JetPlayer_pause},
+ {"native_queueJetSegment",
+ "(IIIIIB)Z", (void *)android_media_JetPlayer_queueSegment},
+ {"native_queueJetSegmentMuteArray",
+ "(IIII[ZB)Z", (void *)android_media_JetPlayer_queueSegmentMuteArray},
+ {"native_setMuteFlags","(IZ)Z", (void *)android_media_JetPlayer_setMuteFlags},
+ {"native_setMuteArray","([ZZ)Z", (void *)android_media_JetPlayer_setMuteArray},
+ {"native_setMuteFlag", "(IZZ)Z", (void *)android_media_JetPlayer_setMuteFlag},
+ {"native_triggerClip", "(I)Z", (void *)android_media_JetPlayer_triggerClip},
+ {"native_clearQueue", "()Z", (void *)android_media_JetPlayer_clearQueue},
+};
+
+#define JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME "mNativePlayerInJavaObj"
+#define JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME "postEventFromNative"
+
+
+int register_android_media_JetPlayer(JNIEnv *env)
+{
+ javaJetPlayerFields.jetClass = NULL;
+ javaJetPlayerFields.postNativeEventInJava = NULL;
+ javaJetPlayerFields.nativePlayerInJavaObj = NULL;
+
+ // Get the JetPlayer java class
+ jclass jetPlayerClass = FindClassOrDie(env, kClassPathName);
+ javaJetPlayerFields.jetClass = MakeGlobalRefOrDie(env, jetPlayerClass);
+
+ // Get the mNativePlayerInJavaObj variable field
+ javaJetPlayerFields.nativePlayerInJavaObj = GetFieldIDOrDie(env,
+ jetPlayerClass, JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME, "J");
+
+ // Get the callback to post events from this native code to Java
+ javaJetPlayerFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
+ javaJetPlayerFields.jetClass, JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME,
+ "(Ljava/lang/Object;III)V");
+
+ return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
+}
diff --git a/media/jni/android_media_MediaDrm.h b/media/jni/android_media_MediaDrm.h
index 5ebac1d61648..684069b0120a 100644
--- a/media/jni/android_media_MediaDrm.h
+++ b/media/jni/android_media_MediaDrm.h
@@ -20,15 +20,12 @@
#include "jni.h"
#include <media/stagefright/foundation/ABase.h>
-#include <media/IDrm.h>
-#include <media/IDrmClient.h>
+#include <mediadrm/IDrm.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
namespace android {
-struct IDrm;
-
class DrmListener: virtual public RefBase
{
public:
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 18fd1a01cfd6..bc4bcebaf560 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -350,9 +350,10 @@ static jobject getBitmapFromVideoFrame(
return jBitmap;
}
-static int getColorFormat(JNIEnv *env, jobject options) {
+static int getColorFormat(JNIEnv *env, jobject options,
+ int defaultPreferred = HAL_PIXEL_FORMAT_RGBA_8888) {
if (options == NULL) {
- return HAL_PIXEL_FORMAT_RGBA_8888;
+ return defaultPreferred;
}
ScopedLocalRef<jobject> inConfig(env, env->GetObjectField(options, fields.inPreferredConfig));
@@ -383,7 +384,8 @@ static SkColorType setOutColorType(JNIEnv *env, int colorFormat, jobject options
}
static jobject android_media_MediaMetadataRetriever_getFrameAtTime(
- JNIEnv *env, jobject thiz, jlong timeUs, jint option, jint dst_width, jint dst_height)
+ JNIEnv *env, jobject thiz, jlong timeUs, jint option,
+ jint dst_width, jint dst_height, jobject params)
{
ALOGV("getFrameAtTime: %lld us option: %d dst width: %d heigh: %d",
(long long)timeUs, option, dst_width, dst_height);
@@ -392,10 +394,13 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtTime(
jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
return NULL;
}
+ // For getFrameAtTime family of calls, default to HAL_PIXEL_FORMAT_RGB_565
+ // to keep the behavior consistent with older releases
+ int colorFormat = getColorFormat(env, params, HAL_PIXEL_FORMAT_RGB_565);
// Call native method to retrieve a video frame
VideoFrame *videoFrame = NULL;
- sp<IMemory> frameMemory = retriever->getFrameAtTime(timeUs, option);
+ sp<IMemory> frameMemory = retriever->getFrameAtTime(timeUs, option, colorFormat);
// TODO: Using unsecurePointer() has some associated security pitfalls
// (see declaration for details).
// Either document why it is safe in this case or address the
@@ -408,7 +413,9 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtTime(
return NULL;
}
- return getBitmapFromVideoFrame(env, videoFrame, dst_width, dst_height, kRGB_565_SkColorType);
+ SkColorType outColorType = setOutColorType(env, colorFormat, params);
+
+ return getBitmapFromVideoFrame(env, videoFrame, dst_width, dst_height, outColorType);
}
static jobject android_media_MediaMetadataRetriever_getImageAtIndex(
@@ -739,7 +746,7 @@ static const JNINativeMethod nativeMethods[] = {
(void *)android_media_MediaMetadataRetriever_setDataSourceFD},
{"_setDataSource", "(Landroid/media/MediaDataSource;)V",
(void *)android_media_MediaMetadataRetriever_setDataSourceCallback},
- {"_getFrameAtTime", "(JIII)Landroid/graphics/Bitmap;",
+ {"_getFrameAtTime", "(JIIILandroid/media/MediaMetadataRetriever$BitmapParams;)Landroid/graphics/Bitmap;",
(void *)android_media_MediaMetadataRetriever_getFrameAtTime},
{
"_getImageAtIndex",
diff --git a/media/jni/android_media_MediaMetricsJNI.cpp b/media/jni/android_media_MediaMetricsJNI.cpp
index de60b085b87d..e7487c3cbc67 100644
--- a/media/jni/android_media_MediaMetricsJNI.cpp
+++ b/media/jni/android_media_MediaMetricsJNI.cpp
@@ -23,9 +23,8 @@
#include <media/MediaAnalyticsItem.h>
-// This source file is compiled and linked into both:
+// This source file is compiled and linked into:
// core/jni/ (libandroid_runtime.so)
-// media/jni (libmedia2_jni.so)
namespace android {
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index d24edc7552ae..b4fa07bd7420 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1436,6 +1436,7 @@ static int register_android_media_MediaPlayer(JNIEnv *env)
}
extern int register_android_media_ImageReader(JNIEnv *env);
extern int register_android_media_ImageWriter(JNIEnv *env);
+extern int register_android_media_JetPlayer(JNIEnv *env);
extern int register_android_media_Crypto(JNIEnv *env);
extern int register_android_media_Drm(JNIEnv *env);
extern int register_android_media_Descrambler(JNIEnv *env);
@@ -1446,7 +1447,6 @@ extern int register_android_media_MediaHTTPConnection(JNIEnv *env);
extern int register_android_media_MediaMetadataRetriever(JNIEnv *env);
extern int register_android_media_MediaMuxer(JNIEnv *env);
extern int register_android_media_MediaRecorder(JNIEnv *env);
-extern int register_android_media_MediaScanner(JNIEnv *env);
extern int register_android_media_MediaSync(JNIEnv *env);
extern int register_android_media_ResampleInputStream(JNIEnv *env);
extern int register_android_media_MediaProfiles(JNIEnv *env);
@@ -1475,6 +1475,11 @@ jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
goto bail;
}
+ if (register_android_media_JetPlayer(env) < 0) {
+ ALOGE("ERROR: JetPlayer native registration failed");
+ goto bail;
+ }
+
if (register_android_media_MediaPlayer(env) < 0) {
ALOGE("ERROR: MediaPlayer native registration failed\n");
goto bail;
@@ -1485,11 +1490,6 @@ jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
goto bail;
}
- if (register_android_media_MediaScanner(env) < 0) {
- ALOGE("ERROR: MediaScanner native registration failed\n");
- goto bail;
- }
-
if (register_android_media_MediaMetadataRetriever(env) < 0) {
ALOGE("ERROR: MediaMetadataRetriever native registration failed\n");
goto bail;
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
deleted file mode 100644
index 306916121740..000000000000
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ /dev/null
@@ -1,1477 +0,0 @@
-/*
-**
-** Copyright 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.
-*/
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "MediaPlayer2-JNI"
-#include "utils/Log.h"
-
-#include <sys/stat.h>
-
-#include <media/AudioResamplerPublic.h>
-#include <media/DataSourceDesc.h>
-#include <media/MediaHTTPService.h>
-#include <media/MediaAnalyticsItem.h>
-#include <media/NdkWrapper.h>
-#include <media/stagefright/Utils.h>
-#include <media/stagefright/foundation/ByteUtils.h> // for FOURCC definition
-#include <mediaplayer2/JAudioTrack.h>
-#include <mediaplayer2/JavaVMHelper.h>
-#include <mediaplayer2/JMedia2HTTPService.h>
-#include <mediaplayer2/mediaplayer2.h>
-#include <stdio.h>
-#include <assert.h>
-#include <limits.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <utils/threads.h>
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-#include "android/native_window_jni.h"
-#include "log/log.h"
-#include "utils/Errors.h" // for status_t
-#include "utils/KeyedVector.h"
-#include "utils/String8.h"
-#include "android_media_BufferingParams.h"
-#include "android_media_DataSourceCallback.h"
-#include "android_media_MediaMetricsJNI.h"
-#include "android_media_PlaybackParams.h"
-#include "android_media_SyncParams.h"
-#include "android_media_VolumeShaper.h"
-
-#include "android_os_Parcel.h"
-#include "android_util_Binder.h"
-#include <binder/Parcel.h>
-
-#include "mediaplayer2.pb.h"
-
-using android::media::MediaPlayer2Proto::PlayerMessage;
-
-// Modular DRM begin
-#define FIND_CLASS(var, className) \
-var = env->FindClass(className); \
-LOG_FATAL_IF(! (var), "Unable to find class " className);
-
-#define GET_METHOD_ID(var, clazz, fieldName, fieldDescriptor) \
-var = env->GetMethodID(clazz, fieldName, fieldDescriptor); \
-LOG_FATAL_IF(! (var), "Unable to find method " fieldName);
-
-struct StateExceptionFields {
- jmethodID init;
- jclass classId;
-};
-
-static StateExceptionFields gStateExceptionFields;
-// Modular DRM end
-
-// ----------------------------------------------------------------------------
-
-using namespace android;
-
-using media::VolumeShaper;
-
-// ----------------------------------------------------------------------------
-
-struct fields_t {
- jfieldID context; // passed from Java to native, used for creating JWakeLock
- jfieldID nativeContext; // mNativeContext in MediaPlayer2.java
- jfieldID surface_texture;
-
- jmethodID post_event;
-
- jmethodID proxyConfigGetHost;
- jmethodID proxyConfigGetPort;
- jmethodID proxyConfigGetExclusionList;
-};
-static fields_t fields;
-
-static BufferingParams::fields_t gBufferingParamsFields;
-static PlaybackParams::fields_t gPlaybackParamsFields;
-static SyncParams::fields_t gSyncParamsFields;
-static VolumeShaperHelper::fields_t gVolumeShaperFields;
-
-static Mutex sLock;
-
-static bool ConvertKeyValueArraysToKeyedVector(
- JNIEnv *env, jobjectArray keys, jobjectArray values,
- KeyedVector<String8, String8>* keyedVector) {
-
- int nKeyValuePairs = 0;
- bool failed = false;
- if (keys != NULL && values != NULL) {
- nKeyValuePairs = env->GetArrayLength(keys);
- failed = (nKeyValuePairs != env->GetArrayLength(values));
- }
-
- if (!failed) {
- failed = ((keys != NULL && values == NULL) ||
- (keys == NULL && values != NULL));
- }
-
- if (failed) {
- ALOGE("keys and values arrays have different length");
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- return false;
- }
-
- for (int i = 0; i < nKeyValuePairs; ++i) {
- // No need to check on the ArrayIndexOutOfBoundsException, since
- // it won't happen here.
- jstring key = (jstring) env->GetObjectArrayElement(keys, i);
- jstring value = (jstring) env->GetObjectArrayElement(values, i);
-
- const char* keyStr = env->GetStringUTFChars(key, NULL);
- if (!keyStr) { // OutOfMemoryError
- return false;
- }
-
- const char* valueStr = env->GetStringUTFChars(value, NULL);
- if (!valueStr) { // OutOfMemoryError
- env->ReleaseStringUTFChars(key, keyStr);
- return false;
- }
-
- keyedVector->add(String8(keyStr), String8(valueStr));
-
- env->ReleaseStringUTFChars(key, keyStr);
- env->ReleaseStringUTFChars(value, valueStr);
- env->DeleteLocalRef(key);
- env->DeleteLocalRef(value);
- }
- return true;
-}
-
-// ----------------------------------------------------------------------------
-// ref-counted object for callbacks
-class JNIMediaPlayer2Listener: public MediaPlayer2Listener
-{
-public:
- JNIMediaPlayer2Listener(JNIEnv* env, jobject thiz, jobject weak_thiz);
- ~JNIMediaPlayer2Listener();
- virtual void notify(int64_t srcId, int msg, int ext1, int ext2,
- const PlayerMessage *obj = NULL) override;
-private:
- JNIMediaPlayer2Listener();
- jclass mClass; // Reference to MediaPlayer2 class
- jobject mObject; // Weak ref to MediaPlayer2 Java object to call on
-};
-
-JNIMediaPlayer2Listener::JNIMediaPlayer2Listener(JNIEnv* env, jobject thiz, jobject weak_thiz)
-{
-
- // Hold onto the MediaPlayer2 class for use in calling the static method
- // that posts events to the application thread.
- jclass clazz = env->GetObjectClass(thiz);
- if (clazz == NULL) {
- ALOGE("Can't find android/media/MediaPlayer2");
- jniThrowException(env, "java/lang/Exception", NULL);
- return;
- }
- mClass = (jclass)env->NewGlobalRef(clazz);
-
- // We use a weak reference so the MediaPlayer2 object can be garbage collected.
- // The reference is only used as a proxy for callbacks.
- mObject = env->NewGlobalRef(weak_thiz);
-}
-
-JNIMediaPlayer2Listener::~JNIMediaPlayer2Listener()
-{
- // remove global references
- JNIEnv *env = JavaVMHelper::getJNIEnv();
- env->DeleteGlobalRef(mObject);
- env->DeleteGlobalRef(mClass);
-}
-
-void JNIMediaPlayer2Listener::notify(int64_t srcId, int msg, int ext1, int ext2,
- const PlayerMessage* obj)
-{
- JNIEnv *env = JavaVMHelper::getJNIEnv();
- if (obj != NULL) {
- int size = obj->ByteSize();
- jbyte* temp = new jbyte[size];
- obj->SerializeToArray(temp, size);
-
- // return the response as a byte array.
- jbyteArray out = env->NewByteArray(size);
- env->SetByteArrayRegion(out, 0, size, temp);
- env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
- srcId, msg, ext1, ext2, out);
- delete[] temp;
- } else {
- env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
- srcId, msg, ext1, ext2, NULL);
- }
- if (env->ExceptionCheck()) {
- ALOGW("An exception occurred while notifying an event.");
- jniLogException(env, ANDROID_LOG_WARN, LOG_TAG);
- env->ExceptionClear();
- }
-}
-
-// ----------------------------------------------------------------------------
-
-static sp<MediaPlayer2> getMediaPlayer(JNIEnv* env, jobject thiz)
-{
- Mutex::Autolock l(sLock);
- MediaPlayer2* const p = (MediaPlayer2*)env->GetLongField(thiz, fields.nativeContext);
- return sp<MediaPlayer2>(p);
-}
-
-static sp<MediaPlayer2> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer2>& player)
-{
- Mutex::Autolock l(sLock);
- sp<MediaPlayer2> old = (MediaPlayer2*)env->GetLongField(thiz, fields.nativeContext);
- if (player.get()) {
- player->incStrong((void*)setMediaPlayer);
- }
- if (old != 0) {
- old->decStrong((void*)setMediaPlayer);
- }
- env->SetLongField(thiz, fields.nativeContext, (jlong)player.get());
- return old;
-}
-
-// If exception is NULL and opStatus is not OK, this method sends an error
-// event to the client application; otherwise, if exception is not NULL and
-// opStatus is not OK, this method throws the given exception to the client
-// application.
-static void process_media_player_call(
- JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
-{
- if (exception == NULL) { // Don't throw exception. Instead, send an event.
- if (opStatus != (status_t) OK) {
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp != 0) {
- int64_t srcId = 0;
- mp->getSrcId(&srcId);
- mp->notify(srcId, MEDIA2_ERROR, opStatus, 0);
- }
- }
- } else { // Throw exception!
- if ( opStatus == (status_t) INVALID_OPERATION ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- } else if ( opStatus == (status_t) BAD_VALUE ) {
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- } else if ( opStatus == (status_t) PERMISSION_DENIED ) {
- jniThrowException(env, "java/lang/SecurityException", NULL);
- } else if ( opStatus != (status_t) OK ) {
- if (strlen(message) > 230) {
- // if the message is too long, don't bother displaying the status code
- jniThrowException( env, exception, message);
- } else {
- char msg[256];
- // append the status code to the message
- sprintf(msg, "%s: status=0x%X", message, opStatus);
- jniThrowException( env, exception, msg);
- }
- }
- }
-}
-
-static void
-android_media_MediaPlayer2_handleDataSourceUrl(
- JNIEnv *env, jobject thiz, jboolean isCurrent, jlong srcId,
- jobject httpServiceObj, jstring path, jobjectArray keys, jobjectArray values,
- jlong startPos, jlong endPos) {
-
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
-
- if (path == NULL) {
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- return;
- }
-
- const char *tmp = env->GetStringUTFChars(path, NULL);
- if (tmp == NULL) { // Out of memory
- return;
- }
- ALOGV("handleDataSourceUrl: path %s, srcId %lld, start %lld, end %lld",
- tmp, (long long)srcId, (long long)startPos, (long long)endPos);
-
- if (strncmp(tmp, "content://", 10) == 0) {
- ALOGE("handleDataSourceUrl: content scheme is not supported in native code");
- jniThrowException(env, "java/io/IOException",
- "content scheme is not supported in native code");
- return;
- }
-
- sp<DataSourceDesc> dsd = new DataSourceDesc();
- dsd->mId = srcId;
- dsd->mType = DataSourceDesc::TYPE_URL;
- dsd->mUrl = tmp;
- dsd->mStartPositionMs = startPos;
- dsd->mEndPositionMs = endPos;
-
- env->ReleaseStringUTFChars(path, tmp);
- tmp = NULL;
-
- // We build a KeyedVector out of the key and val arrays
- if (!ConvertKeyValueArraysToKeyedVector(
- env, keys, values, &dsd->mHeaders)) {
- return;
- }
-
- sp<MediaHTTPService> httpService;
- if (httpServiceObj != NULL) {
- httpService = new JMedia2HTTPService(env, httpServiceObj);
- }
- dsd->mHttpService = httpService;
-
- status_t err;
- if (isCurrent) {
- err = mp->setDataSource(dsd);
- } else {
- err = mp->prepareNextDataSource(dsd);
- }
- process_media_player_call(env, thiz, err,
- "java/io/IOException", "handleDataSourceUrl failed." );
-}
-
-static void
-android_media_MediaPlayer2_handleDataSourceFD(
- JNIEnv *env, jobject thiz, jboolean isCurrent, jlong srcId,
- jobject fileDescriptor, jlong offset, jlong length,
- jlong startPos, jlong endPos) {
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
-
- if (fileDescriptor == NULL) {
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- return;
- }
- int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
- ALOGV("handleDataSourceFD: srcId=%lld, fd=%d (%s), offset=%lld, length=%lld, "
- "start=%lld, end=%lld",
- (long long)srcId, fd, nameForFd(fd).c_str(), (long long)offset, (long long)length,
- (long long)startPos, (long long)endPos);
-
- struct stat sb;
- int ret = fstat(fd, &sb);
- if (ret != 0) {
- ALOGE("handleDataSourceFD: fstat(%d) failed: %d, %s", fd, ret, strerror(errno));
- jniThrowException(env, "java/io/IOException", "handleDataSourceFD failed fstat");
- return;
- }
-
- ALOGV("st_dev = %llu", static_cast<unsigned long long>(sb.st_dev));
- ALOGV("st_mode = %u", sb.st_mode);
- ALOGV("st_uid = %lu", static_cast<unsigned long>(sb.st_uid));
- ALOGV("st_gid = %lu", static_cast<unsigned long>(sb.st_gid));
- ALOGV("st_size = %llu", static_cast<unsigned long long>(sb.st_size));
-
- if (offset >= sb.st_size) {
- ALOGE("handleDataSourceFD: offset is out of range");
- jniThrowException(env, "java/lang/IllegalArgumentException",
- "handleDataSourceFD failed, offset is out of range.");
- return;
- }
- if (offset + length > sb.st_size) {
- length = sb.st_size - offset;
- ALOGV("handleDataSourceFD: adjusted length = %lld", (long long)length);
- }
-
- sp<DataSourceDesc> dsd = new DataSourceDesc();
- dsd->mId = srcId;
- dsd->mType = DataSourceDesc::TYPE_FD;
- dsd->mFD = fd;
- dsd->mFDOffset = offset;
- dsd->mFDLength = length;
- dsd->mStartPositionMs = startPos;
- dsd->mEndPositionMs = endPos;
-
- status_t err;
- if (isCurrent) {
- err = mp->setDataSource(dsd);
- } else {
- err = mp->prepareNextDataSource(dsd);
- }
- process_media_player_call(env, thiz, err,
- "java/io/IOException", "handleDataSourceFD failed." );
-}
-
-static void
-android_media_MediaPlayer2_handleDataSourceCallback(
- JNIEnv *env, jobject thiz, jboolean isCurrent, jlong srcId, jobject dataSource,
- jlong startPos, jlong endPos)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
-
- if (dataSource == NULL) {
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- return;
- }
- sp<DataSource> callbackDataSource = new JDataSourceCallback(env, dataSource);
- sp<DataSourceDesc> dsd = new DataSourceDesc();
- dsd->mId = srcId;
- dsd->mType = DataSourceDesc::TYPE_CALLBACK;
- dsd->mCallbackSource = callbackDataSource;
- dsd->mStartPositionMs = startPos;
- dsd->mEndPositionMs = endPos;
-
- status_t err;
- if (isCurrent) {
- err = mp->setDataSource(dsd);
- } else {
- err = mp->prepareNextDataSource(dsd);
- }
- process_media_player_call(env, thiz, err,
- "java/lang/RuntimeException", "handleDataSourceCallback failed." );
-}
-
-static sp<ANativeWindowWrapper>
-getVideoSurfaceTexture(JNIEnv* env, jobject thiz) {
- ANativeWindow * const p = (ANativeWindow*)env->GetLongField(thiz, fields.surface_texture);
- return new ANativeWindowWrapper(p);
-}
-
-static void
-decVideoSurfaceRef(JNIEnv *env, jobject thiz)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL) {
- return;
- }
-
- ANativeWindow * const old_anw = (ANativeWindow*)env->GetLongField(thiz, fields.surface_texture);
- if (old_anw != NULL) {
- ANativeWindow_release(old_anw);
- env->SetLongField(thiz, fields.surface_texture, (jlong)NULL);
- }
-}
-
-static void
-setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface, jboolean mediaPlayerMustBeAlive)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL) {
- if (mediaPlayerMustBeAlive) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- }
- return;
- }
-
- decVideoSurfaceRef(env, thiz);
-
- ANativeWindow* anw = NULL;
- if (jsurface) {
- anw = ANativeWindow_fromSurface(env, jsurface);
- if (anw == NULL) {
- jniThrowException(env, "java/lang/IllegalArgumentException",
- "The surface has been released");
- return;
- }
- }
-
- env->SetLongField(thiz, fields.surface_texture, (jlong)anw);
-
- // This will fail if the media player has not been initialized yet. This
- // can be the case if setDisplay() on MediaPlayer2.java has been called
- // before setDataSource(). The redundant call to setVideoSurfaceTexture()
- // in prepare/prepare covers for this case.
- mp->setVideoSurfaceTexture(new ANativeWindowWrapper(anw));
-}
-
-static void
-android_media_MediaPlayer2_setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface)
-{
- setVideoSurface(env, thiz, jsurface, true /* mediaPlayerMustBeAlive */);
-}
-
-static jobject
-android_media_MediaPlayer2_getBufferingParams(JNIEnv *env, jobject thiz)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return NULL;
- }
-
- BufferingParams bp;
- BufferingSettings &settings = bp.settings;
- process_media_player_call(
- env, thiz, mp->getBufferingSettings(&settings),
- "java/lang/IllegalStateException", "unexpected error");
- if (env->ExceptionCheck()) {
- return nullptr;
- }
- ALOGV("getBufferingSettings:{%s}", settings.toString().string());
-
- return bp.asJobject(env, gBufferingParamsFields);
-}
-
-static void
-android_media_MediaPlayer2_setBufferingParams(JNIEnv *env, jobject thiz, jobject params)
-{
- if (params == NULL) {
- return;
- }
-
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
-
- BufferingParams bp;
- bp.fillFromJobject(env, gBufferingParamsFields, params);
- ALOGV("setBufferingParams:{%s}", bp.settings.toString().string());
-
- process_media_player_call(
- env, thiz, mp->setBufferingSettings(bp.settings),
- "java/lang/IllegalStateException", "unexpected error");
-}
-
-static void
-android_media_MediaPlayer2_playNextDataSource(JNIEnv *env, jobject thiz, jlong srcId)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
-
- process_media_player_call(env, thiz, mp->playNextDataSource((int64_t)srcId),
- "java/io/IOException", "playNextDataSource failed." );
-}
-
-static void
-android_media_MediaPlayer2_prepare(JNIEnv *env, jobject thiz)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
-
- // Handle the case where the display surface was set before the mp was
- // initialized. We try again to make it stick.
- sp<ANativeWindowWrapper> st = getVideoSurfaceTexture(env, thiz);
- mp->setVideoSurfaceTexture(st);
-
- process_media_player_call( env, thiz, mp->prepareAsync(), "java/io/IOException", "Prepare Async failed." );
-}
-
-static void
-android_media_MediaPlayer2_start(JNIEnv *env, jobject thiz)
-{
- ALOGV("start");
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
- process_media_player_call( env, thiz, mp->start(), NULL, NULL );
-}
-
-static void
-android_media_MediaPlayer2_pause(JNIEnv *env, jobject thiz)
-{
- ALOGV("pause");
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
- process_media_player_call( env, thiz, mp->pause(), NULL, NULL );
-}
-
-static void
-android_media_MediaPlayer2_setPlaybackParams(JNIEnv *env, jobject thiz, jobject params)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
-
- PlaybackParams pbp;
- pbp.fillFromJobject(env, gPlaybackParamsFields, params);
- ALOGV("setPlaybackParams: %d:%f %d:%f %d:%u %d:%u",
- pbp.speedSet, pbp.audioRate.mSpeed,
- pbp.pitchSet, pbp.audioRate.mPitch,
- pbp.audioFallbackModeSet, pbp.audioRate.mFallbackMode,
- pbp.audioStretchModeSet, pbp.audioRate.mStretchMode);
-
- AudioPlaybackRate rate;
- status_t err = mp->getPlaybackSettings(&rate);
- if (err == OK) {
- bool updatedRate = false;
- if (pbp.speedSet) {
- rate.mSpeed = pbp.audioRate.mSpeed;
- updatedRate = true;
- }
- if (pbp.pitchSet) {
- rate.mPitch = pbp.audioRate.mPitch;
- updatedRate = true;
- }
- if (pbp.audioFallbackModeSet) {
- rate.mFallbackMode = pbp.audioRate.mFallbackMode;
- updatedRate = true;
- }
- if (pbp.audioStretchModeSet) {
- rate.mStretchMode = pbp.audioRate.mStretchMode;
- updatedRate = true;
- }
- if (updatedRate) {
- err = mp->setPlaybackSettings(rate);
- }
- }
- process_media_player_call(
- env, thiz, err,
- "java/lang/IllegalStateException", "unexpected error");
-}
-
-static jobject
-android_media_MediaPlayer2_getPlaybackParams(JNIEnv *env, jobject thiz)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return NULL;
- }
-
- PlaybackParams pbp;
- AudioPlaybackRate &audioRate = pbp.audioRate;
- process_media_player_call(
- env, thiz, mp->getPlaybackSettings(&audioRate),
- "java/lang/IllegalStateException", "unexpected error");
- if (env->ExceptionCheck()) {
- return nullptr;
- }
- ALOGV("getPlaybackSettings: %f %f %d %d",
- audioRate.mSpeed, audioRate.mPitch, audioRate.mFallbackMode, audioRate.mStretchMode);
-
- pbp.speedSet = true;
- pbp.pitchSet = true;
- pbp.audioFallbackModeSet = true;
- pbp.audioStretchModeSet = true;
-
- return pbp.asJobject(env, gPlaybackParamsFields);
-}
-
-static void
-android_media_MediaPlayer2_setSyncParams(JNIEnv *env, jobject thiz, jobject params)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
-
- SyncParams scp;
- scp.fillFromJobject(env, gSyncParamsFields, params);
- ALOGV("setSyncParams: %d:%d %d:%d %d:%f %d:%f",
- scp.syncSourceSet, scp.sync.mSource,
- scp.audioAdjustModeSet, scp.sync.mAudioAdjustMode,
- scp.toleranceSet, scp.sync.mTolerance,
- scp.frameRateSet, scp.frameRate);
-
- AVSyncSettings avsync;
- float videoFrameRate;
- status_t err = mp->getSyncSettings(&avsync, &videoFrameRate);
- if (err == OK) {
- bool updatedSync = scp.frameRateSet;
- if (scp.syncSourceSet) {
- avsync.mSource = scp.sync.mSource;
- updatedSync = true;
- }
- if (scp.audioAdjustModeSet) {
- avsync.mAudioAdjustMode = scp.sync.mAudioAdjustMode;
- updatedSync = true;
- }
- if (scp.toleranceSet) {
- avsync.mTolerance = scp.sync.mTolerance;
- updatedSync = true;
- }
- if (updatedSync) {
- err = mp->setSyncSettings(avsync, scp.frameRateSet ? scp.frameRate : -1.f);
- }
- }
- process_media_player_call(
- env, thiz, err,
- "java/lang/IllegalStateException", "unexpected error");
-}
-
-static jobject
-android_media_MediaPlayer2_getSyncParams(JNIEnv *env, jobject thiz)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return NULL;
- }
-
- SyncParams scp;
- scp.frameRate = -1.f;
- process_media_player_call(
- env, thiz, mp->getSyncSettings(&scp.sync, &scp.frameRate),
- "java/lang/IllegalStateException", "unexpected error");
- if (env->ExceptionCheck()) {
- return nullptr;
- }
-
- ALOGV("getSyncSettings: %d %d %f %f",
- scp.sync.mSource, scp.sync.mAudioAdjustMode, scp.sync.mTolerance, scp.frameRate);
-
- // sanity check params
- if (scp.sync.mSource >= AVSYNC_SOURCE_MAX
- || scp.sync.mAudioAdjustMode >= AVSYNC_AUDIO_ADJUST_MODE_MAX
- || scp.sync.mTolerance < 0.f
- || scp.sync.mTolerance >= AVSYNC_TOLERANCE_MAX) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return NULL;
- }
-
- scp.syncSourceSet = true;
- scp.audioAdjustModeSet = true;
- scp.toleranceSet = true;
- scp.frameRateSet = scp.frameRate >= 0.f;
-
- return scp.asJobject(env, gSyncParamsFields);
-}
-
-static void
-android_media_MediaPlayer2_seekTo(JNIEnv *env, jobject thiz, jlong msec, jint mode)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
- ALOGV("seekTo: %lld(msec), mode=%d", (long long)msec, mode);
- process_media_player_call(env, thiz, mp->seekTo((int64_t)msec, (MediaPlayer2SeekMode)mode),
- NULL, NULL);
-}
-
-static jint
-android_media_MediaPlayer2_getState(JNIEnv *env, jobject thiz)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL) {
- return MEDIAPLAYER2_STATE_IDLE;
- }
- return (jint)mp->getState();
-}
-
-static jobject
-android_media_MediaPlayer2_native_getMetrics(JNIEnv *env, jobject thiz)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return 0;
- }
-
- char *buffer = NULL;
- size_t length = 0;
- status_t status = mp->getMetrics(&buffer, &length);
- if (status != OK) {
- ALOGD("getMetrics() failed: %d", status);
- return (jobject) NULL;
- }
-
- jobject mybundle = MediaMetricsJNI::writeAttributesToBundle(env, NULL, buffer, length);
-
- free(buffer);
-
- return mybundle;
-}
-
-static jlong
-android_media_MediaPlayer2_getCurrentPosition(JNIEnv *env, jobject thiz)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return 0;
- }
- int64_t msec;
- process_media_player_call( env, thiz, mp->getCurrentPosition(&msec), NULL, NULL );
- ALOGV("getCurrentPosition: %lld (msec)", (long long)msec);
- return (jlong) msec;
-}
-
-static jlong
-android_media_MediaPlayer2_getDuration(JNIEnv *env, jobject thiz, jlong srcId)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return 0;
- }
- int64_t msec;
- process_media_player_call( env, thiz, mp->getDuration(srcId, &msec), NULL, NULL );
- ALOGV("getDuration: %lld (msec)", (long long)msec);
- return (jlong) msec;
-}
-
-static void
-android_media_MediaPlayer2_reset(JNIEnv *env, jobject thiz)
-{
- ALOGV("reset");
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
- process_media_player_call( env, thiz, mp->reset(), NULL, NULL );
-}
-
-static jboolean
-android_media_MediaPlayer2_setAudioAttributes(JNIEnv *env, jobject thiz, jobject attributes)
-{
- ALOGV("setAudioAttributes");
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return false;
- }
- status_t err = mp->setAudioAttributes(attributes);
- return err == OK;
-}
-
-static jobject
-android_media_MediaPlayer2_getAudioAttributes(JNIEnv *env, jobject thiz)
-{
- ALOGV("getAudioAttributes");
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return NULL;
- }
-
- return mp->getAudioAttributes();
-}
-
-static void
-android_media_MediaPlayer2_setLooping(JNIEnv *env, jobject thiz, jboolean looping)
-{
- ALOGV("setLooping: %d", looping);
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
- process_media_player_call( env, thiz, mp->setLooping(looping), NULL, NULL );
-}
-
-static jboolean
-android_media_MediaPlayer2_isLooping(JNIEnv *env, jobject thiz)
-{
- ALOGV("isLooping");
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return JNI_FALSE;
- }
- return mp->isLooping() ? JNI_TRUE : JNI_FALSE;
-}
-
-static void
-android_media_MediaPlayer2_setVolume(JNIEnv *env, jobject thiz, jfloat volume)
-{
- ALOGV("setVolume: volume %f", (float) volume);
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
- process_media_player_call( env, thiz, mp->setVolume((float) volume), NULL, NULL );
-}
-
-static jbyteArray
-android_media_MediaPlayer2_invoke(JNIEnv *env, jobject thiz, jbyteArray requestData) {
- sp<MediaPlayer2> media_player = getMediaPlayer(env, thiz);
- if (media_player == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return NULL;
- }
-
- // Get the byte[] pointer and data length.
- jbyte* pData = env->GetByteArrayElements(requestData, NULL);
- jsize pDataLen = env->GetArrayLength(requestData);
-
- // Deserialize from the byte stream.
- PlayerMessage request;
- PlayerMessage response;
- request.ParseFromArray(pData, pDataLen);
-
- process_media_player_call( env, thiz, media_player->invoke(request, &response),
- "java.lang.RuntimeException", NULL );
- if (env->ExceptionCheck()) {
- return NULL;
- }
-
- int size = response.ByteSize();
- jbyte* temp = new jbyte[size];
- response.SerializeToArray(temp, size);
-
- // return the response as a byte array.
- jbyteArray out = env->NewByteArray(size);
- env->SetByteArrayRegion(out, 0, size, temp);
- delete[] temp;
-
- return out;
-}
-
-// This function gets some field IDs, which in turn causes class initialization.
-// It is called from a static block in MediaPlayer2, which won't run until the
-// first time an instance of this class is used.
-static void
-android_media_MediaPlayer2_native_init(JNIEnv *env)
-{
- jclass clazz;
-
- clazz = env->FindClass("android/media/MediaPlayer2");
- if (clazz == NULL) {
- return;
- }
-
- fields.context = env->GetFieldID(clazz, "mContext", "Landroid/content/Context;");
- if (fields.context == NULL) {
- return;
- }
-
- fields.nativeContext = env->GetFieldID(clazz, "mNativeContext", "J");
- if (fields.nativeContext == NULL) {
- return;
- }
-
- fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
- "(Ljava/lang/Object;JIII[B)V");
- if (fields.post_event == NULL) {
- return;
- }
-
- fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "J");
- if (fields.surface_texture == NULL) {
- return;
- }
-
- env->DeleteLocalRef(clazz);
-
- clazz = env->FindClass("android/net/ProxyInfo");
- if (clazz == NULL) {
- return;
- }
-
- fields.proxyConfigGetHost =
- env->GetMethodID(clazz, "getHost", "()Ljava/lang/String;");
-
- fields.proxyConfigGetPort =
- env->GetMethodID(clazz, "getPort", "()I");
-
- fields.proxyConfigGetExclusionList =
- env->GetMethodID(clazz, "getExclusionListAsString", "()Ljava/lang/String;");
-
- env->DeleteLocalRef(clazz);
-
- gBufferingParamsFields.init(env);
-
- // Modular DRM
- FIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException");
- if (clazz) {
- GET_METHOD_ID(gStateExceptionFields.init, clazz, "<init>", "(ILjava/lang/String;)V");
- gStateExceptionFields.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
-
- env->DeleteLocalRef(clazz);
- } else {
- ALOGE("JNI android_media_MediaPlayer2_native_init couldn't "
- "get clazz android/media/MediaDrm$MediaDrmStateException");
- }
-
- gPlaybackParamsFields.init(env);
- gSyncParamsFields.init(env);
- gVolumeShaperFields.init(env);
-}
-
-static void
-android_media_MediaPlayer2_native_setup(JNIEnv *env, jobject thiz,
- jint sessionId, jobject weak_this)
-{
- ALOGV("native_setup");
- jobject context = env->GetObjectField(thiz, fields.context);
- sp<MediaPlayer2> mp = MediaPlayer2::Create(sessionId, context);
- if (mp == NULL) {
- jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
- return;
- }
-
- // create new listener and give it to MediaPlayer2
- sp<JNIMediaPlayer2Listener> listener = new JNIMediaPlayer2Listener(env, thiz, weak_this);
- mp->setListener(listener);
-
- // Stow our new C++ MediaPlayer2 in an opaque field in the Java object.
- setMediaPlayer(env, thiz, mp);
-}
-
-static void
-android_media_MediaPlayer2_release(JNIEnv *env, jobject thiz)
-{
- ALOGV("release");
- decVideoSurfaceRef(env, thiz);
- sp<MediaPlayer2> mp = setMediaPlayer(env, thiz, 0);
- if (mp != NULL) {
- // this prevents native callbacks after the object is released
- mp->setListener(0);
- mp->disconnect();
- }
-}
-
-static void
-android_media_MediaPlayer2_native_finalize(JNIEnv *env, jobject thiz)
-{
- ALOGV("native_finalize");
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp != NULL) {
- ALOGW("MediaPlayer2 finalized without being released");
- }
- android_media_MediaPlayer2_release(env, thiz);
-}
-
-static void android_media_MediaPlayer2_setAudioSessionId(JNIEnv *env, jobject thiz,
- jint sessionId) {
- ALOGV("setAudioSessionId(): %d", sessionId);
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
- process_media_player_call( env, thiz, mp->setAudioSessionId((audio_session_t) sessionId), NULL,
- NULL);
-}
-
-static jint android_media_MediaPlayer2_getAudioSessionId(JNIEnv *env, jobject thiz) {
- ALOGV("getAudioSessionId()");
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return 0;
- }
-
- return (jint) mp->getAudioSessionId();
-}
-
-static void
-android_media_MediaPlayer2_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level)
-{
- ALOGV("setAuxEffectSendLevel: level %f", level);
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
- process_media_player_call( env, thiz, mp->setAuxEffectSendLevel(level), NULL, NULL );
-}
-
-static void android_media_MediaPlayer2_attachAuxEffect(JNIEnv *env, jobject thiz, jint effectId) {
- ALOGV("attachAuxEffect(): %d", effectId);
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
- process_media_player_call( env, thiz, mp->attachAuxEffect(effectId), NULL, NULL );
-}
-
-/////////////////////////////////////////////////////////////////////////////////////
-// Modular DRM begin
-
-// TODO: investigate if these can be shared with their MediaDrm counterparts
-static void throwDrmStateException(JNIEnv *env, const char *msg, status_t err)
-{
- ALOGE("Illegal DRM state exception: %s (%d)", msg, err);
-
- jobject exception = env->NewObject(gStateExceptionFields.classId,
- gStateExceptionFields.init, static_cast<int>(err),
- env->NewStringUTF(msg));
- env->Throw(static_cast<jthrowable>(exception));
-}
-
-// TODO: investigate if these can be shared with their MediaDrm counterparts
-static bool throwDrmExceptionAsNecessary(JNIEnv *env, status_t err, const char *msg = NULL)
-{
- const char *drmMessage = "Unknown DRM Msg";
-
- switch (err) {
- case ERROR_DRM_UNKNOWN:
- drmMessage = "General DRM error";
- break;
- case ERROR_DRM_NO_LICENSE:
- drmMessage = "No license";
- break;
- case ERROR_DRM_LICENSE_EXPIRED:
- drmMessage = "License expired";
- break;
- case ERROR_DRM_SESSION_NOT_OPENED:
- drmMessage = "Session not opened";
- break;
- case ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED:
- drmMessage = "Not initialized";
- break;
- case ERROR_DRM_DECRYPT:
- drmMessage = "Decrypt error";
- break;
- case ERROR_DRM_CANNOT_HANDLE:
- drmMessage = "Unsupported scheme or data format";
- break;
- case ERROR_DRM_TAMPER_DETECTED:
- drmMessage = "Invalid state";
- break;
- default:
- break;
- }
-
- String8 vendorMessage;
- if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) {
- vendorMessage = String8::format("DRM vendor-defined error: %d", err);
- drmMessage = vendorMessage.string();
- }
-
- if (err == BAD_VALUE) {
- jniThrowException(env, "java/lang/IllegalArgumentException", msg);
- return true;
- } else if (err == ERROR_DRM_NOT_PROVISIONED) {
- jniThrowException(env, "android/media/NotProvisionedException", msg);
- return true;
- } else if (err == ERROR_DRM_RESOURCE_BUSY) {
- jniThrowException(env, "android/media/ResourceBusyException", msg);
- return true;
- } else if (err == ERROR_DRM_DEVICE_REVOKED) {
- jniThrowException(env, "android/media/DeniedByServerException", msg);
- return true;
- } else if (err == DEAD_OBJECT) {
- jniThrowException(env, "android/media/MediaDrmResetException",
- "mediaserver died");
- return true;
- } else if (err != OK) {
- String8 errbuf;
- if (drmMessage != NULL) {
- if (msg == NULL) {
- msg = drmMessage;
- } else {
- errbuf = String8::format("%s: %s", msg, drmMessage);
- msg = errbuf.string();
- }
- }
- throwDrmStateException(env, msg, err);
- return true;
- }
- return false;
-}
-
-static Vector<uint8_t> JByteArrayToVector(JNIEnv *env, jbyteArray const &byteArray)
-{
- Vector<uint8_t> vector;
- size_t length = env->GetArrayLength(byteArray);
- vector.insertAt((size_t)0, length);
- env->GetByteArrayRegion(byteArray, 0, length, (jbyte *)vector.editArray());
- return vector;
-}
-
-static void android_media_MediaPlayer2_prepareDrm(JNIEnv *env, jobject thiz,
- jlong srcId, jbyteArray uuidObj, jbyteArray drmSessionIdObj)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
-
- if (uuidObj == NULL) {
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- return;
- }
-
- Vector<uint8_t> uuid = JByteArrayToVector(env, uuidObj);
-
- if (uuid.size() != 16) {
- jniThrowException(
- env,
- "java/lang/IllegalArgumentException",
- "invalid UUID size, expected 16 bytes");
- return;
- }
-
- Vector<uint8_t> drmSessionId = JByteArrayToVector(env, drmSessionIdObj);
-
- if (drmSessionId.size() == 0) {
- jniThrowException(
- env,
- "java/lang/IllegalArgumentException",
- "empty drmSessionId");
- return;
- }
-
- status_t err = mp->prepareDrm(srcId, uuid.array(), drmSessionId);
- if (err != OK) {
- if (err == INVALID_OPERATION) {
- jniThrowException(
- env,
- "java/lang/IllegalStateException",
- "The player must be in prepared state.");
- } else if (err == ERROR_DRM_CANNOT_HANDLE) {
- jniThrowException(
- env,
- "android/media/UnsupportedSchemeException",
- "Failed to instantiate drm object.");
- } else {
- throwDrmExceptionAsNecessary(env, err, "Failed to prepare DRM scheme");
- }
- }
-}
-
-static void android_media_MediaPlayer2_releaseDrm(JNIEnv *env, jobject thiz, jlong srcId)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
-
- status_t err = mp->releaseDrm(srcId);
- if (err != OK) {
- if (err == INVALID_OPERATION) {
- jniThrowException(
- env,
- "java/lang/IllegalStateException",
- "Can not release DRM in an active player state.");
- }
- }
-}
-// Modular DRM end
-// ----------------------------------------------------------------------------
-
-/////////////////////////////////////////////////////////////////////////////////////
-// AudioRouting begin
-static jboolean android_media_MediaPlayer2_setPreferredDevice(JNIEnv *env, jobject thiz, jobject device)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL) {
- return false;
- }
- return mp->setPreferredDevice(device) == NO_ERROR;
-}
-
-static jobject android_media_MediaPlayer2_getRoutedDevice(JNIEnv *env, jobject thiz)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL) {
- return nullptr;
- }
- return mp->getRoutedDevice();
-}
-
-static void android_media_MediaPlayer2_addDeviceCallback(
- JNIEnv* env, jobject thiz, jobject routingDelegate)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL) {
- return;
- }
-
- status_t status = mp->addAudioDeviceCallback(routingDelegate);
- if (status != NO_ERROR) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- ALOGE("enable device callback failed: %d", status);
- }
-}
-
-static void android_media_MediaPlayer2_removeDeviceCallback(
- JNIEnv* env, jobject thiz, jobject listener)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL) {
- return;
- }
-
- status_t status = mp->removeAudioDeviceCallback(listener);
- if (status != NO_ERROR) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- ALOGE("enable device callback failed: %d", status);
- }
-}
-
-// AudioRouting end
-// ----------------------------------------------------------------------------
-
-/////////////////////////////////////////////////////////////////////////////////////
-// AudioTrack.StreamEventCallback begin
-static void android_media_MediaPlayer2_native_on_tear_down(JNIEnv *env __unused,
- jobject thiz __unused, jlong callbackPtr, jlong userDataPtr)
-{
- JAudioTrack::callback_t callback = (JAudioTrack::callback_t) callbackPtr;
- if (callback != NULL) {
- callback(JAudioTrack::EVENT_NEW_IAUDIOTRACK, (void *) userDataPtr, NULL);
- }
-}
-
-static void android_media_MediaPlayer2_native_on_stream_presentation_end(JNIEnv *env __unused,
- jobject thiz __unused, jlong callbackPtr, jlong userDataPtr)
-{
- JAudioTrack::callback_t callback = (JAudioTrack::callback_t) callbackPtr;
- if (callback != NULL) {
- callback(JAudioTrack::EVENT_STREAM_END, (void *) userDataPtr, NULL);
- }
-}
-
-static void android_media_MediaPlayer2_native_on_stream_data_request(JNIEnv *env __unused,
- jobject thiz __unused, jlong jAudioTrackPtr, jlong callbackPtr, jlong userDataPtr)
-{
- JAudioTrack::callback_t callback = (JAudioTrack::callback_t) callbackPtr;
- JAudioTrack* track = (JAudioTrack *) jAudioTrackPtr;
- if (callback != NULL && track != NULL) {
- JAudioTrack::Buffer* buffer = new JAudioTrack::Buffer();
-
- size_t bufferSizeInFrames = track->frameCount();
- audio_format_t format = track->format();
-
- size_t bufferSizeInBytes;
- if (audio_has_proportional_frames(format)) {
- bufferSizeInBytes =
- bufferSizeInFrames * audio_bytes_per_sample(format) * track->channelCount();
- } else {
- // See Javadoc of AudioTrack::getBufferSizeInFrames().
- bufferSizeInBytes = bufferSizeInFrames;
- }
-
- uint8_t* byteBuffer = new uint8_t[bufferSizeInBytes];
- buffer->mSize = bufferSizeInBytes;
- buffer->mData = (void *) byteBuffer;
-
- callback(JAudioTrack::EVENT_MORE_DATA, (void *) userDataPtr, buffer);
-
- if (buffer->mSize > 0 && buffer->mData == byteBuffer) {
- track->write(buffer->mData, buffer->mSize, true /* Blocking */);
- }
-
- delete[] byteBuffer;
- delete buffer;
- }
-}
-
-
-// AudioTrack.StreamEventCallback end
-// ----------------------------------------------------------------------------
-
-static const JNINativeMethod gMethods[] = {
- {
- "nativeHandleDataSourceUrl",
- "(ZJLandroid/media/Media2HTTPService;Ljava/lang/String;[Ljava/lang/String;"
- "[Ljava/lang/String;JJ)V",
- (void *)android_media_MediaPlayer2_handleDataSourceUrl
- },
- {
- "nativeHandleDataSourceFD",
- "(ZJLjava/io/FileDescriptor;JJJJ)V",
- (void *)android_media_MediaPlayer2_handleDataSourceFD
- },
- {
- "nativeHandleDataSourceCallback",
- "(ZJLandroid/media/DataSourceCallback;JJ)V",
- (void *)android_media_MediaPlayer2_handleDataSourceCallback
- },
- {"nativePlayNextDataSource", "(J)V", (void *)android_media_MediaPlayer2_playNextDataSource},
- {"native_setVideoSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaPlayer2_setVideoSurface},
- {"getBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer2_getBufferingParams},
- {"native_setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer2_setBufferingParams},
- {"native_prepare", "()V", (void *)android_media_MediaPlayer2_prepare},
- {"native_start", "()V", (void *)android_media_MediaPlayer2_start},
- {"native_getState", "()I", (void *)android_media_MediaPlayer2_getState},
- {"native_getMetrics", "()Landroid/os/PersistableBundle;", (void *)android_media_MediaPlayer2_native_getMetrics},
- {"native_setPlaybackParams", "(Landroid/media/PlaybackParams;)V", (void *)android_media_MediaPlayer2_setPlaybackParams},
- {"getPlaybackParams", "()Landroid/media/PlaybackParams;", (void *)android_media_MediaPlayer2_getPlaybackParams},
- {"native_setSyncParams", "(Landroid/media/SyncParams;)V", (void *)android_media_MediaPlayer2_setSyncParams},
- {"getSyncParams", "()Landroid/media/SyncParams;", (void *)android_media_MediaPlayer2_getSyncParams},
- {"native_seekTo", "(JI)V", (void *)android_media_MediaPlayer2_seekTo},
- {"native_pause", "()V", (void *)android_media_MediaPlayer2_pause},
- {"getCurrentPosition", "()J", (void *)android_media_MediaPlayer2_getCurrentPosition},
- {"native_getDuration", "(J)J", (void *)android_media_MediaPlayer2_getDuration},
- {"native_release", "()V", (void *)android_media_MediaPlayer2_release},
- {"native_reset", "()V", (void *)android_media_MediaPlayer2_reset},
- {"native_setAudioAttributes", "(Landroid/media/AudioAttributes;)Z", (void *)android_media_MediaPlayer2_setAudioAttributes},
- {"native_getAudioAttributes", "()Landroid/media/AudioAttributes;", (void *)android_media_MediaPlayer2_getAudioAttributes},
- {"setLooping", "(Z)V", (void *)android_media_MediaPlayer2_setLooping},
- {"isLooping", "()Z", (void *)android_media_MediaPlayer2_isLooping},
- {"native_setVolume", "(F)V", (void *)android_media_MediaPlayer2_setVolume},
- {"native_invoke", "([B)[B", (void *)android_media_MediaPlayer2_invoke},
- {"native_init", "()V", (void *)android_media_MediaPlayer2_native_init},
- {"native_setup", "(ILjava/lang/Object;)V", (void *)android_media_MediaPlayer2_native_setup},
- {"native_finalize", "()V", (void *)android_media_MediaPlayer2_native_finalize},
- {"getAudioSessionId", "()I", (void *)android_media_MediaPlayer2_getAudioSessionId},
- {"native_setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer2_setAudioSessionId},
- {"native_setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer2_setAuxEffectSendLevel},
- {"native_attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer2_attachAuxEffect},
- // Modular DRM
- { "native_prepareDrm", "(J[B[B)V", (void *)android_media_MediaPlayer2_prepareDrm },
- { "native_releaseDrm", "(J)V", (void *)android_media_MediaPlayer2_releaseDrm },
-
- // AudioRouting
- {"native_setPreferredDevice", "(Landroid/media/AudioDeviceInfo;)Z", (void *)android_media_MediaPlayer2_setPreferredDevice},
- {"getRoutedDevice", "()Landroid/media/AudioDeviceInfo;", (void *)android_media_MediaPlayer2_getRoutedDevice},
- {"native_addDeviceCallback", "(Landroid/media/RoutingDelegate;)V", (void *)android_media_MediaPlayer2_addDeviceCallback},
- {"native_removeDeviceCallback", "(Landroid/media/AudioRouting$OnRoutingChangedListener;)V",
- (void *)android_media_MediaPlayer2_removeDeviceCallback},
-
- // StreamEventCallback for JAudioTrack
- {"native_stream_event_onTearDown", "(JJ)V", (void *)android_media_MediaPlayer2_native_on_tear_down},
- {"native_stream_event_onStreamPresentationEnd", "(JJ)V", (void *)android_media_MediaPlayer2_native_on_stream_presentation_end},
- {"native_stream_event_onStreamDataRequest", "(JJJ)V", (void *)android_media_MediaPlayer2_native_on_stream_data_request},
-};
-
-// This function only registers the native methods
-static int register_android_media_MediaPlayer2(JNIEnv *env)
-{
- return jniRegisterNativeMethods(env, "android/media/MediaPlayer2", gMethods, NELEM(gMethods));
-}
-
-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("ERROR: GetEnv failed\n");
- goto bail;
- }
- assert(env != NULL);
-
- if (register_android_media_MediaPlayer2(env) < 0) {
- ALOGE("ERROR: MediaPlayer2 native registration failed\n");
- goto bail;
- }
-
- JavaVMHelper::setJavaVM(vm);
-
- /* success -- return valid version number */
- result = JNI_VERSION_1_4;
-
-bail:
- return result;
-}
-
-// KTHXBYE
diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp
deleted file mode 100644
index 58044c0307eb..000000000000
--- a/media/jni/android_media_MediaScanner.cpp
+++ /dev/null
@@ -1,468 +0,0 @@
-/*
-**
-** Copyright 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.
-*/
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "MediaScannerJNI"
-#include <utils/Log.h>
-#include <utils/threads.h>
-#include <media/mediascanner.h>
-#include <media/stagefright/StagefrightMediaScanner.h>
-#include <private/media/VideoFrame.h>
-
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-#include "android_runtime/AndroidRuntime.h"
-#include "android_runtime/Log.h"
-#include <android-base/macros.h> // for FALLTHROUGH_INTENDED
-
-using namespace android;
-
-
-static const char* const kClassMediaScannerClient =
- "android/media/MediaScannerClient";
-
-static const char* const kClassMediaScanner =
- "android/media/MediaScanner";
-
-static const char* const kRunTimeException =
- "java/lang/RuntimeException";
-
-static const char* const kIllegalArgumentException =
- "java/lang/IllegalArgumentException";
-
-struct fields_t {
- jfieldID context;
-};
-static fields_t fields;
-
-static status_t checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
- if (env->ExceptionCheck()) {
- ALOGE("An exception was thrown by callback '%s'.", methodName);
- LOGE_EX(env);
- env->ExceptionClear();
- return UNKNOWN_ERROR;
- }
- return OK;
-}
-
-// stolen from dalvik/vm/checkJni.cpp
-static bool isValidUtf8(const char* bytes) {
- while (*bytes != '\0') {
- unsigned char utf8 = *(bytes++);
- // Switch on the high four bits.
- switch (utf8 >> 4) {
- case 0x00:
- case 0x01:
- case 0x02:
- case 0x03:
- case 0x04:
- case 0x05:
- case 0x06:
- case 0x07:
- // Bit pattern 0xxx. No need for any extra bytes.
- break;
- case 0x08:
- case 0x09:
- case 0x0a:
- case 0x0b:
- case 0x0f:
- /*
- * Bit pattern 10xx or 1111, which are illegal start bytes.
- * Note: 1111 is valid for normal UTF-8, but not the
- * modified UTF-8 used here.
- */
- return false;
- case 0x0e:
- // Bit pattern 1110, so there are two additional bytes.
- utf8 = *(bytes++);
- if ((utf8 & 0xc0) != 0x80) {
- return false;
- }
- // Fall through to take care of the final byte.
- FALLTHROUGH_INTENDED;
- case 0x0c:
- case 0x0d:
- // Bit pattern 110x, so there is one additional byte.
- utf8 = *(bytes++);
- if ((utf8 & 0xc0) != 0x80) {
- return false;
- }
- break;
- }
- }
- return true;
-}
-
-class MyMediaScannerClient : public MediaScannerClient
-{
-public:
- MyMediaScannerClient(JNIEnv *env, jobject client)
- : mEnv(env),
- mClient(env->NewGlobalRef(client)),
- mScanFileMethodID(0),
- mHandleStringTagMethodID(0),
- mSetMimeTypeMethodID(0)
- {
- ALOGV("MyMediaScannerClient constructor");
- jclass mediaScannerClientInterface =
- env->FindClass(kClassMediaScannerClient);
-
- if (mediaScannerClientInterface == NULL) {
- ALOGE("Class %s not found", kClassMediaScannerClient);
- } else {
- mScanFileMethodID = env->GetMethodID(
- mediaScannerClientInterface,
- "scanFile",
- "(Ljava/lang/String;JJZZ)V");
-
- mHandleStringTagMethodID = env->GetMethodID(
- mediaScannerClientInterface,
- "handleStringTag",
- "(Ljava/lang/String;Ljava/lang/String;)V");
-
- mSetMimeTypeMethodID = env->GetMethodID(
- mediaScannerClientInterface,
- "setMimeType",
- "(Ljava/lang/String;)V");
- }
- }
-
- virtual ~MyMediaScannerClient()
- {
- ALOGV("MyMediaScannerClient destructor");
- mEnv->DeleteGlobalRef(mClient);
- }
-
- virtual status_t scanFile(const char* path, long long lastModified,
- long long fileSize, bool isDirectory, bool noMedia)
- {
- ALOGV("scanFile: path(%s), time(%lld), size(%lld) and isDir(%d)",
- path, lastModified, fileSize, isDirectory);
-
- jstring pathStr;
- if ((pathStr = mEnv->NewStringUTF(path)) == NULL) {
- mEnv->ExceptionClear();
- return NO_MEMORY;
- }
-
- mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified,
- fileSize, isDirectory, noMedia);
-
- mEnv->DeleteLocalRef(pathStr);
- return checkAndClearExceptionFromCallback(mEnv, "scanFile");
- }
-
- virtual status_t handleStringTag(const char* name, const char* value)
- {
- ALOGV("handleStringTag: name(%s) and value(%s)", name, value);
- jstring nameStr, valueStr;
- if ((nameStr = mEnv->NewStringUTF(name)) == NULL) {
- mEnv->ExceptionClear();
- return NO_MEMORY;
- }
- char *cleaned = NULL;
- if (!isValidUtf8(value)) {
- cleaned = strdup(value);
- char *chp = cleaned;
- char ch;
- while ((ch = *chp)) {
- if (ch & 0x80) {
- *chp = '?';
- }
- chp++;
- }
- value = cleaned;
- }
- valueStr = mEnv->NewStringUTF(value);
- free(cleaned);
- if (valueStr == NULL) {
- mEnv->DeleteLocalRef(nameStr);
- mEnv->ExceptionClear();
- return NO_MEMORY;
- }
-
- mEnv->CallVoidMethod(
- mClient, mHandleStringTagMethodID, nameStr, valueStr);
-
- mEnv->DeleteLocalRef(nameStr);
- mEnv->DeleteLocalRef(valueStr);
- return checkAndClearExceptionFromCallback(mEnv, "handleStringTag");
- }
-
- virtual status_t setMimeType(const char* mimeType)
- {
- ALOGV("setMimeType: %s", mimeType);
- jstring mimeTypeStr;
- if ((mimeTypeStr = mEnv->NewStringUTF(mimeType)) == NULL) {
- mEnv->ExceptionClear();
- return NO_MEMORY;
- }
-
- mEnv->CallVoidMethod(mClient, mSetMimeTypeMethodID, mimeTypeStr);
-
- mEnv->DeleteLocalRef(mimeTypeStr);
- return checkAndClearExceptionFromCallback(mEnv, "setMimeType");
- }
-
-private:
- JNIEnv *mEnv;
- jobject mClient;
- jmethodID mScanFileMethodID;
- jmethodID mHandleStringTagMethodID;
- jmethodID mSetMimeTypeMethodID;
-};
-
-
-static MediaScanner *getNativeScanner_l(JNIEnv* env, jobject thiz)
-{
- return (MediaScanner *) env->GetLongField(thiz, fields.context);
-}
-
-static void setNativeScanner_l(JNIEnv* env, jobject thiz, MediaScanner *s)
-{
- env->SetLongField(thiz, fields.context, (jlong)s);
-}
-
-static void
-android_media_MediaScanner_processDirectory(
- JNIEnv *env, jobject thiz, jstring path, jobject client)
-{
- ALOGV("processDirectory");
- MediaScanner *mp = getNativeScanner_l(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, kRunTimeException, "No scanner available");
- return;
- }
-
- if (path == NULL) {
- jniThrowException(env, kIllegalArgumentException, NULL);
- return;
- }
-
- const char *pathStr = env->GetStringUTFChars(path, NULL);
- if (pathStr == NULL) { // Out of memory
- return;
- }
-
- MyMediaScannerClient myClient(env, client);
- MediaScanResult result = mp->processDirectory(pathStr, myClient);
- if (result == MEDIA_SCAN_RESULT_ERROR) {
- ALOGE("An error occurred while scanning directory '%s'.", pathStr);
- }
- env->ReleaseStringUTFChars(path, pathStr);
-}
-
-static jboolean
-android_media_MediaScanner_processFile(
- JNIEnv *env, jobject thiz, jstring path,
- jstring mimeType, jobject client)
-{
- ALOGV("processFile");
-
- // Lock already hold by processDirectory
- MediaScanner *mp = getNativeScanner_l(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, kRunTimeException, "No scanner available");
- return false;
- }
-
- if (path == NULL) {
- jniThrowException(env, kIllegalArgumentException, NULL);
- return false;
- }
-
- const char *pathStr = env->GetStringUTFChars(path, NULL);
- if (pathStr == NULL) { // Out of memory
- return false;
- }
-
- const char *mimeTypeStr =
- (mimeType ? env->GetStringUTFChars(mimeType, NULL) : NULL);
- if (mimeType && mimeTypeStr == NULL) { // Out of memory
- // ReleaseStringUTFChars can be called with an exception pending.
- env->ReleaseStringUTFChars(path, pathStr);
- return false;
- }
-
- MyMediaScannerClient myClient(env, client);
- MediaScanResult result = mp->processFile(pathStr, mimeTypeStr, myClient);
- if (result == MEDIA_SCAN_RESULT_ERROR) {
- ALOGE("An error occurred while scanning file '%s'.", pathStr);
- }
- env->ReleaseStringUTFChars(path, pathStr);
- if (mimeType) {
- env->ReleaseStringUTFChars(mimeType, mimeTypeStr);
- }
- return result != MEDIA_SCAN_RESULT_ERROR;
-}
-
-static void
-android_media_MediaScanner_setLocale(
- JNIEnv *env, jobject thiz, jstring locale)
-{
- ALOGV("setLocale");
- MediaScanner *mp = getNativeScanner_l(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, kRunTimeException, "No scanner available");
- return;
- }
-
- if (locale == NULL) {
- jniThrowException(env, kIllegalArgumentException, NULL);
- return;
- }
- const char *localeStr = env->GetStringUTFChars(locale, NULL);
- if (localeStr == NULL) { // Out of memory
- return;
- }
- mp->setLocale(localeStr);
-
- env->ReleaseStringUTFChars(locale, localeStr);
-}
-
-static jbyteArray
-android_media_MediaScanner_extractAlbumArt(
- JNIEnv *env, jobject thiz, jobject fileDescriptor)
-{
- ALOGV("extractAlbumArt");
- MediaScanner *mp = getNativeScanner_l(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, kRunTimeException, "No scanner available");
- return NULL;
- }
-
- if (fileDescriptor == NULL) {
- jniThrowException(env, kIllegalArgumentException, NULL);
- return NULL;
- }
-
- int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
- MediaAlbumArt* mediaAlbumArt = mp->extractAlbumArt(fd);
- if (mediaAlbumArt == NULL) {
- return NULL;
- }
-
- jbyteArray array = env->NewByteArray(mediaAlbumArt->size());
- if (array != NULL) {
- const jbyte* data =
- reinterpret_cast<const jbyte*>(mediaAlbumArt->data());
- env->SetByteArrayRegion(array, 0, mediaAlbumArt->size(), data);
- }
-
- free(mediaAlbumArt);
- // if NewByteArray() returned NULL, an out-of-memory
- // exception will have been raised. I just want to
- // return null in that case.
- env->ExceptionClear();
- return array;
-}
-
-// This function gets a field ID, which in turn causes class initialization.
-// It is called from a static block in MediaScanner, which won't run until the
-// first time an instance of this class is used.
-static void
-android_media_MediaScanner_native_init(JNIEnv *env)
-{
- ALOGV("native_init");
- jclass clazz = env->FindClass(kClassMediaScanner);
- if (clazz == NULL) {
- return;
- }
-
- fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
- if (fields.context == NULL) {
- return;
- }
-}
-
-static void
-android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz)
-{
- ALOGV("native_setup");
- MediaScanner *mp = new StagefrightMediaScanner;
-
- if (mp == NULL) {
- jniThrowException(env, kRunTimeException, "Out of memory");
- return;
- }
-
- env->SetLongField(thiz, fields.context, (jlong)mp);
-}
-
-static void
-android_media_MediaScanner_native_finalize(JNIEnv *env, jobject thiz)
-{
- ALOGV("native_finalize");
- MediaScanner *mp = getNativeScanner_l(env, thiz);
- if (mp == 0) {
- return;
- }
- delete mp;
- setNativeScanner_l(env, thiz, 0);
-}
-
-static const JNINativeMethod gMethods[] = {
- {
- "processDirectory",
- "(Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
- (void *)android_media_MediaScanner_processDirectory
- },
-
- {
- "processFile",
- "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)Z",
- (void *)android_media_MediaScanner_processFile
- },
-
- {
- "setLocale",
- "(Ljava/lang/String;)V",
- (void *)android_media_MediaScanner_setLocale
- },
-
- {
- "extractAlbumArt",
- "(Ljava/io/FileDescriptor;)[B",
- (void *)android_media_MediaScanner_extractAlbumArt
- },
-
- {
- "native_init",
- "()V",
- (void *)android_media_MediaScanner_native_init
- },
-
- {
- "native_setup",
- "()V",
- (void *)android_media_MediaScanner_native_setup
- },
-
- {
- "native_finalize",
- "()V",
- (void *)android_media_MediaScanner_native_finalize
- },
-};
-
-// This function only registers the native methods, and is called from
-// JNI_OnLoad in android_media_MediaPlayer.cpp
-int register_android_media_MediaScanner(JNIEnv *env)
-{
- return AndroidRuntime::registerNativeMethods(env,
- kClassMediaScanner, gMethods, NELEM(gMethods));
-}
diff --git a/media/jni/soundpool/Android.bp b/media/jni/soundpool/Android.bp
index 35b7b01e207c..e1945dddaa0b 100644
--- a/media/jni/soundpool/Android.bp
+++ b/media/jni/soundpool/Android.bp
@@ -3,11 +3,16 @@ cc_library_shared {
srcs: [
"android_media_SoundPool.cpp",
+ "Sound.cpp",
+ "SoundDecoder.cpp",
+ "SoundManager.cpp",
"SoundPool.cpp",
- "SoundPoolThread.cpp",
+ "Stream.cpp",
+ "StreamManager.cpp",
],
shared_libs: [
+ "libaudioutils",
"liblog",
"libcutils",
"libutils",
diff --git a/media/jni/soundpool/Sound.cpp b/media/jni/soundpool/Sound.cpp
new file mode 100644
index 000000000000..0bbc3e46b044
--- /dev/null
+++ b/media/jni/soundpool/Sound.cpp
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoundPool::Sound"
+#include <utils/Log.h>
+
+#include "Sound.h"
+
+#include <media/NdkMediaCodec.h>
+#include <media/NdkMediaExtractor.h>
+#include <media/NdkMediaFormat.h>
+
+namespace android::soundpool {
+
+constexpr uint32_t kMaxSampleRate = 192000;
+constexpr size_t kDefaultHeapSize = 1024 * 1024; // 1MB (compatible with low mem devices)
+
+Sound::Sound(int32_t soundID, int fd, int64_t offset, int64_t length)
+ : mSoundID(soundID)
+ , mFd(dup(fd))
+ , mOffset(offset)
+ , mLength(length)
+{
+ ALOGV("%s(soundID=%d, fd=%d, offset=%lld, length=%lld)",
+ __func__, soundID, fd, (long long)offset, (long long)length);
+ ALOGW_IF(mFd == -1, "Unable to dup descriptor %d", fd);
+}
+
+Sound::~Sound()
+{
+ ALOGV("%s(soundID=%d, fd=%d)", __func__, mSoundID, mFd.get());
+}
+
+static status_t decode(int fd, int64_t offset, int64_t length,
+ uint32_t *rate, int32_t *channelCount, audio_format_t *audioFormat,
+ audio_channel_mask_t *channelMask, sp<MemoryHeapBase> heap,
+ size_t *sizeInBytes) {
+ ALOGV("%s(fd=%d, offset=%lld, length=%lld, ...)",
+ __func__, fd, (long long)offset, (long long)length);
+ std::unique_ptr<AMediaExtractor, decltype(&AMediaExtractor_delete)> ex{
+ AMediaExtractor_new(), &AMediaExtractor_delete};
+ status_t err = AMediaExtractor_setDataSourceFd(ex.get(), fd, offset, length);
+
+ if (err != AMEDIA_OK) {
+ return err;
+ }
+
+ *audioFormat = AUDIO_FORMAT_PCM_16_BIT; // default format for audio codecs.
+ const size_t numTracks = AMediaExtractor_getTrackCount(ex.get());
+ for (size_t i = 0; i < numTracks; i++) {
+ std::unique_ptr<AMediaFormat, decltype(&AMediaFormat_delete)> format{
+ AMediaExtractor_getTrackFormat(ex.get(), i), &AMediaFormat_delete};
+ const char *mime;
+ if (!AMediaFormat_getString(format.get(), AMEDIAFORMAT_KEY_MIME, &mime)) {
+ return UNKNOWN_ERROR;
+ }
+ if (strncmp(mime, "audio/", 6) == 0) {
+ std::unique_ptr<AMediaCodec, decltype(&AMediaCodec_delete)> codec{
+ AMediaCodec_createDecoderByType(mime), &AMediaCodec_delete};
+ if (codec == nullptr
+ || AMediaCodec_configure(codec.get(), format.get(),
+ nullptr /* window */, nullptr /* drm */, 0 /* flags */) != AMEDIA_OK
+ || AMediaCodec_start(codec.get()) != AMEDIA_OK
+ || AMediaExtractor_selectTrack(ex.get(), i) != AMEDIA_OK) {
+ return UNKNOWN_ERROR;
+ }
+
+ bool sawInputEOS = false;
+ bool sawOutputEOS = false;
+ uint8_t* writePos = static_cast<uint8_t*>(heap->getBase());
+ size_t available = heap->getSize();
+ size_t written = 0;
+ format.reset(AMediaCodec_getOutputFormat(codec.get())); // update format.
+
+ while (!sawOutputEOS) {
+ if (!sawInputEOS) {
+ ssize_t bufidx = AMediaCodec_dequeueInputBuffer(codec.get(), 5000);
+ ALOGV("%s: input buffer %zd", __func__, bufidx);
+ if (bufidx >= 0) {
+ size_t bufsize;
+ uint8_t * const buf = AMediaCodec_getInputBuffer(
+ codec.get(), bufidx, &bufsize);
+ if (buf == nullptr) {
+ ALOGE("%s: AMediaCodec_getInputBuffer returned nullptr, short decode",
+ __func__);
+ break;
+ }
+ int sampleSize = AMediaExtractor_readSampleData(ex.get(), buf, bufsize);
+ ALOGV("%s: read %d", __func__, sampleSize);
+ if (sampleSize < 0) {
+ sampleSize = 0;
+ sawInputEOS = true;
+ ALOGV("%s: EOS", __func__);
+ }
+ const int64_t presentationTimeUs = AMediaExtractor_getSampleTime(ex.get());
+
+ const media_status_t mstatus = AMediaCodec_queueInputBuffer(
+ codec.get(), bufidx,
+ 0 /* offset */, sampleSize, presentationTimeUs,
+ sawInputEOS ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
+ if (mstatus != AMEDIA_OK) {
+ // AMEDIA_ERROR_UNKNOWN == { -ERANGE -EINVAL -EACCES }
+ ALOGE("%s: AMediaCodec_queueInputBuffer returned status %d,"
+ "short decode",
+ __func__, (int)mstatus);
+ break;
+ }
+ (void)AMediaExtractor_advance(ex.get());
+ }
+ }
+
+ AMediaCodecBufferInfo info;
+ const int status = AMediaCodec_dequeueOutputBuffer(codec.get(), &info, 1);
+ ALOGV("%s: dequeueoutput returned: %d", __func__, status);
+ if (status >= 0) {
+ if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
+ ALOGV("%s: output EOS", __func__);
+ sawOutputEOS = true;
+ }
+ ALOGV("%s: got decoded buffer size %d", __func__, info.size);
+
+ const uint8_t * const buf = AMediaCodec_getOutputBuffer(
+ codec.get(), status, nullptr /* out_size */);
+ if (buf == nullptr) {
+ ALOGE("%s: AMediaCodec_getOutputBuffer returned nullptr, short decode",
+ __func__);
+ break;
+ }
+ const size_t dataSize = std::min((size_t)info.size, available);
+ memcpy(writePos, buf + info.offset, dataSize);
+ writePos += dataSize;
+ written += dataSize;
+ available -= dataSize;
+ const media_status_t mstatus = AMediaCodec_releaseOutputBuffer(
+ codec.get(), status, false /* render */);
+ if (mstatus != AMEDIA_OK) {
+ // AMEDIA_ERROR_UNKNOWN == { -ERANGE -EINVAL -EACCES }
+ ALOGE("%s: AMediaCodec_releaseOutputBuffer"
+ " returned status %d, short decode",
+ __func__, (int)mstatus);
+ break;
+ }
+ if (available == 0) {
+ // there might be more data, but there's no space for it
+ sawOutputEOS = true;
+ }
+ } else if (status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
+ ALOGV("%s: output buffers changed", __func__);
+ } else if (status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
+ format.reset(AMediaCodec_getOutputFormat(codec.get())); // update format
+ ALOGV("%s: format changed to: %s",
+ __func__, AMediaFormat_toString(format.get()));
+ } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+ ALOGV("%s: no output buffer right now", __func__);
+ } else if (status <= AMEDIA_ERROR_BASE) {
+ ALOGE("%s: decode error: %d", __func__, status);
+ break;
+ } else {
+ ALOGV("%s: unexpected info code: %d", __func__, status);
+ }
+ }
+
+ (void)AMediaCodec_stop(codec.get());
+ if (!AMediaFormat_getInt32(
+ format.get(), AMEDIAFORMAT_KEY_SAMPLE_RATE, (int32_t*) rate) ||
+ !AMediaFormat_getInt32(
+ format.get(), AMEDIAFORMAT_KEY_CHANNEL_COUNT, channelCount)) {
+ return UNKNOWN_ERROR;
+ }
+ if (!AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_CHANNEL_MASK,
+ (int32_t*) channelMask)) {
+ *channelMask = AUDIO_CHANNEL_NONE;
+ }
+ *sizeInBytes = written;
+ return OK;
+ }
+ }
+ return UNKNOWN_ERROR;
+}
+
+status_t Sound::doLoad()
+{
+ ALOGV("%s()", __func__);
+ status_t status = NO_INIT;
+ if (mFd.get() != -1) {
+ mHeap = new MemoryHeapBase(kDefaultHeapSize);
+
+ ALOGV("%s: start decode", __func__);
+ uint32_t sampleRate;
+ int32_t channelCount;
+ audio_format_t format;
+ audio_channel_mask_t channelMask;
+ status_t status = decode(mFd.get(), mOffset, mLength, &sampleRate, &channelCount, &format,
+ &channelMask, mHeap, &mSizeInBytes);
+ ALOGV("%s: close(%d)", __func__, mFd.get());
+ mFd.reset(); // close
+
+ if (status != NO_ERROR) {
+ ALOGE("%s: unable to load sound", __func__);
+ } else if (sampleRate > kMaxSampleRate) {
+ ALOGE("%s: sample rate (%u) out of range", __func__, sampleRate);
+ status = BAD_VALUE;
+ } else if (channelCount < 1 || channelCount > FCC_8) {
+ ALOGE("%s: sample channel count (%d) out of range", __func__, channelCount);
+ status = BAD_VALUE;
+ } else {
+ // Correctly loaded, proper parameters
+ ALOGV("%s: pointer = %p, sizeInBytes = %zu, sampleRate = %u, channelCount = %d",
+ __func__, mHeap->getBase(), mSizeInBytes, sampleRate, channelCount);
+ mData = new MemoryBase(mHeap, 0, mSizeInBytes);
+ mSampleRate = sampleRate;
+ mChannelCount = channelCount;
+ mFormat = format;
+ mChannelMask = channelMask;
+ mState = READY; // this should be last, as it is an atomic sync point
+ return NO_ERROR;
+ }
+ } else {
+ ALOGE("%s: uninitialized fd, dup failed", __func__);
+ }
+ // ERROR handling
+ mHeap.clear();
+ mState = DECODE_ERROR; // this should be last, as it is an atomic sync point
+ return status;
+}
+
+} // namespace android::soundpool
diff --git a/media/jni/soundpool/Sound.h b/media/jni/soundpool/Sound.h
new file mode 100644
index 000000000000..efe940a7bd68
--- /dev/null
+++ b/media/jni/soundpool/Sound.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/unique_fd.h>
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
+#include <system/audio.h>
+
+namespace android::soundpool {
+
+class SoundDecoder;
+
+/**
+ * Sound is a resource used by SoundPool, referenced by soundID.
+ *
+ * After loading, it is effectively const so no locking required.
+ * However, in order to guarantee that all the values have been
+ * written properly and read properly, we use the mState as an atomic synchronization
+ * point. So if getState() shows READY, then all the other getters may
+ * be safely read.
+ *
+ * Technical details:
+ * We access the mState atomic value through memory_order_seq_cst
+ *
+ * https://en.cppreference.com/w/cpp/atomic/memory_order
+ *
+ * which provides memory barriers. So if the last value written by the SoundDecoder
+ * is mState, then the compiler ensures no other prior writes by SoundDecoder will be
+ * reordered afterwards, and memory barrier is placed (as necessary) to ensure the
+ * cache is visible to other processors.
+ *
+ * Likewise, if the first value read by SoundPool is mState,
+ * the compiler ensures no reads for that thread will be reordered before mState is read,
+ * and a memory barrier is placed (as necessary) to ensure that the cache is properly
+ * updated with other processor's writes before reading.
+ *
+ * See https://developer.android.com/training/articles/smp for discussions about
+ * the variant load-acquire, store-release semantics.
+ */
+class Sound {
+ friend SoundDecoder; // calls doLoad().
+
+public:
+ enum sound_state : int32_t { LOADING, READY, DECODE_ERROR };
+ // A sound starts in the LOADING state and transitions only once
+ // to either READY or DECODE_ERROR when doLoad() is called.
+
+ Sound(int soundID, int fd, int64_t offset, int64_t length);
+ ~Sound();
+
+ int32_t getSoundID() const { return mSoundID; }
+ int32_t getChannelCount() const { return mChannelCount; }
+ uint32_t getSampleRate() const { return mSampleRate; }
+ audio_format_t getFormat() const { return mFormat; }
+ audio_channel_mask_t getChannelMask() const { return mChannelMask; }
+ size_t getSizeInBytes() const { return mSizeInBytes; }
+ sound_state getState() const { return mState; }
+ uint8_t* getData() const { return static_cast<uint8_t*>(mData->unsecurePointer()); }
+ sp<IMemory> getIMemory() const { return mData; }
+
+private:
+ status_t doLoad(); // only SoundDecoder accesses this.
+
+ size_t mSizeInBytes = 0;
+ const int32_t mSoundID;
+ uint32_t mSampleRate = 0;
+ std::atomic<sound_state> mState = LOADING; // used as synchronization point
+ int32_t mChannelCount = 0;
+ audio_format_t mFormat = AUDIO_FORMAT_INVALID;
+ audio_channel_mask_t mChannelMask = AUDIO_CHANNEL_NONE;
+ base::unique_fd mFd; // initialized in constructor, reset to -1 after loading
+ const int64_t mOffset; // int64_t to match java long, see off64_t
+ const int64_t mLength; // int64_t to match java long, see off64_t
+ sp<IMemory> mData;
+ sp<MemoryHeapBase> mHeap;
+};
+
+} // namespace android::soundpool
diff --git a/media/jni/soundpool/SoundDecoder.cpp b/media/jni/soundpool/SoundDecoder.cpp
new file mode 100644
index 000000000000..12200ef03aad
--- /dev/null
+++ b/media/jni/soundpool/SoundDecoder.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoundPool::SoundDecoder"
+#include "utils/Log.h"
+
+#include "SoundDecoder.h"
+
+namespace android::soundpool {
+
+// Maximum Samples that can be background decoded before we block the caller.
+static constexpr size_t kMaxQueueSize = 128;
+
+// The amount of time we wait for a new Sound decode request
+// before the SoundDecoder thread closes.
+static constexpr int32_t kWaitTimeBeforeCloseMs = 1000;
+
+SoundDecoder::SoundDecoder(SoundManager* soundManager, size_t threads)
+ : mSoundManager(soundManager)
+{
+ ALOGV("%s(%p, %zu)", __func__, soundManager, threads);
+ // ThreadPool is created, but we don't launch any threads.
+ mThreadPool = std::make_unique<ThreadPool>(
+ std::min(threads, (size_t)std::thread::hardware_concurrency()),
+ "SoundDecoder_");
+}
+
+SoundDecoder::~SoundDecoder()
+{
+ ALOGV("%s()", __func__);
+ quit();
+}
+
+void SoundDecoder::quit()
+{
+ ALOGV("%s()", __func__);
+ {
+ std::lock_guard lock(mLock);
+ mQuit = true;
+ mQueueSpaceAvailable.notify_all(); // notify all load waiters
+ mQueueDataAvailable.notify_all(); // notify all worker threads
+ }
+ mThreadPool->quit();
+}
+
+void SoundDecoder::run(int32_t id __unused /* ALOGV only */)
+{
+ ALOGV("%s(%d): entering", __func__, id);
+ std::unique_lock lock(mLock);
+ while (!mQuit) {
+ if (mSoundIDs.size() == 0) {
+ ALOGV("%s(%d): waiting", __func__, id);
+ mQueueDataAvailable.wait_for(
+ lock, std::chrono::duration<int32_t, std::milli>(kWaitTimeBeforeCloseMs));
+ if (mSoundIDs.size() == 0) {
+ break; // no new sound, exit this thread.
+ }
+ continue;
+ }
+ const int32_t soundID = mSoundIDs.front();
+ mSoundIDs.pop_front();
+ mQueueSpaceAvailable.notify_one();
+ ALOGV("%s(%d): processing soundID: %d size: %zu", __func__, id, soundID, mSoundIDs.size());
+ lock.unlock();
+ std::shared_ptr<Sound> sound = mSoundManager->findSound(soundID);
+ status_t status = NO_INIT;
+ if (sound.get() != nullptr) {
+ status = sound->doLoad();
+ }
+ ALOGV("%s(%d): notifying loaded soundID:%d status:%d", __func__, id, soundID, status);
+ mSoundManager->notify(SoundPoolEvent(SoundPoolEvent::SOUND_LOADED, soundID, status));
+ lock.lock();
+ }
+ ALOGV("%s(%d): exiting", __func__, id);
+}
+
+void SoundDecoder::loadSound(int32_t soundID)
+{
+ ALOGV("%s(%d)", __func__, soundID);
+ size_t pendingSounds;
+ {
+ std::unique_lock lock(mLock);
+ while (mSoundIDs.size() == kMaxQueueSize) {
+ if (mQuit) return;
+ ALOGV("%s: waiting soundID: %d size: %zu", __func__, soundID, mSoundIDs.size());
+ mQueueSpaceAvailable.wait(lock);
+ }
+ if (mQuit) return;
+ mSoundIDs.push_back(soundID);
+ mQueueDataAvailable.notify_one();
+ ALOGV("%s: adding soundID: %d size: %zu", __func__, soundID, mSoundIDs.size());
+ pendingSounds = mSoundIDs.size();
+ }
+ // Launch threads as needed. The "as needed" is weakly consistent as we release mLock.
+ if (pendingSounds > mThreadPool->getActiveThreadCount()) {
+ const int32_t id __unused = mThreadPool->launch([this](int32_t id) { run(id); });
+ ALOGV_IF(id != 0, "%s: launched thread %d", __func__, id);
+ }
+}
+
+} // end namespace android::soundpool
diff --git a/media/jni/soundpool/SoundDecoder.h b/media/jni/soundpool/SoundDecoder.h
new file mode 100644
index 000000000000..1288943e86e3
--- /dev/null
+++ b/media/jni/soundpool/SoundDecoder.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "SoundPool.h"
+
+#include <deque>
+#include <mutex>
+
+namespace android::soundpool {
+
+/**
+ * SoundDecoder handles background decoding tasks.
+ */
+class SoundDecoder {
+public:
+ SoundDecoder(SoundManager* soundManager, size_t threads);
+ ~SoundDecoder();
+ void loadSound(int32_t soundID);
+ void quit();
+
+private:
+ void run(int32_t id); // The decode thread function.
+
+ SoundManager* const mSoundManager; // set in constructor, has own lock
+ std::unique_ptr<ThreadPool> mThreadPool; // set in constructor, has own lock
+
+ std::mutex mLock;
+ std::condition_variable mQueueSpaceAvailable;
+ std::condition_variable mQueueDataAvailable;
+
+ std::deque<int32_t> mSoundIDs; // GUARDED_BY(mLock);
+ bool mQuit = false; // GUARDED_BY(mLock);
+};
+
+} // end namespace android::soundpool
+
diff --git a/media/jni/soundpool/SoundManager.cpp b/media/jni/soundpool/SoundManager.cpp
new file mode 100644
index 000000000000..3c625bf3bb7f
--- /dev/null
+++ b/media/jni/soundpool/SoundManager.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoundPool::SoundManager"
+#include <utils/Log.h>
+
+#include "SoundManager.h"
+
+#include <thread>
+
+#include "SoundDecoder.h"
+
+namespace android::soundpool {
+
+static const size_t kDecoderThreads = std::thread::hardware_concurrency() >= 4 ? 2 : 1;
+
+SoundManager::SoundManager()
+ : mDecoder{std::make_unique<SoundDecoder>(this, kDecoderThreads)}
+{
+ ALOGV("%s()", __func__);
+}
+
+SoundManager::~SoundManager()
+{
+ ALOGV("%s()", __func__);
+ mDecoder->quit();
+
+ std::lock_guard lock(mSoundManagerLock);
+ mSounds.clear();
+}
+
+int32_t SoundManager::load(int fd, int64_t offset, int64_t length, int32_t priority __unused)
+{
+ ALOGV("%s(fd=%d, offset=%lld, length=%lld, priority=%d)",
+ __func__, fd, (long long)offset, (long long)length, priority);
+ int32_t soundID;
+ {
+ std::lock_guard lock(mSoundManagerLock);
+ // mNextSoundID is always positive and does not "integer overflow"
+ do {
+ mNextSoundID = mNextSoundID == INT32_MAX ? 1 : mNextSoundID + 1;
+ } while (findSound_l(mNextSoundID) != nullptr);
+ soundID = mNextSoundID;
+ auto sound = std::make_shared<Sound>(soundID, fd, offset, length);
+ mSounds.emplace(soundID, sound);
+ }
+ // mDecoder->loadSound() must be called outside of mSoundManagerLock.
+ // mDecoder->loadSound() may block on mDecoder message queue space;
+ // the message queue emptying may block on SoundManager::findSound().
+ //
+ // It is theoretically possible that sound loads might decode out-of-order.
+ mDecoder->loadSound(soundID);
+ return soundID;
+}
+
+bool SoundManager::unload(int32_t soundID)
+{
+ ALOGV("%s(soundID=%d)", __func__, soundID);
+ std::lock_guard lock(mSoundManagerLock);
+ return mSounds.erase(soundID) > 0; // erase() returns number of sounds removed.
+}
+
+std::shared_ptr<Sound> SoundManager::findSound(int32_t soundID) const
+{
+ std::lock_guard lock(mSoundManagerLock);
+ return findSound_l(soundID);
+}
+
+std::shared_ptr<Sound> SoundManager::findSound_l(int32_t soundID) const
+{
+ auto it = mSounds.find(soundID);
+ return it != mSounds.end() ? it->second : nullptr;
+}
+
+void SoundManager::setCallback(SoundPool *soundPool, SoundPoolCallback* callback, void* user)
+{
+ mCallbackHandler.setCallback(soundPool, callback, user);
+}
+
+void SoundManager::notify(SoundPoolEvent event)
+{
+ mCallbackHandler.notify(event);
+}
+
+void* SoundManager::getUserData() const
+{
+ return mCallbackHandler.getUserData();
+}
+
+} // namespace android::soundpool
diff --git a/media/jni/soundpool/SoundManager.h b/media/jni/soundpool/SoundManager.h
new file mode 100644
index 000000000000..9201e78132f4
--- /dev/null
+++ b/media/jni/soundpool/SoundManager.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Sound.h"
+
+#include <mutex>
+#include <unordered_map>
+
+namespace android {
+
+class SoundPool;
+
+// for queued events
+class SoundPoolEvent {
+public:
+ explicit SoundPoolEvent(int msg, int arg1 = 0, int arg2 = 0) :
+ mMsg(msg), mArg1(arg1), mArg2(arg2) {}
+ const int mMsg; // MessageType
+ const int mArg1; // soundID
+ const int mArg2; // status
+ enum MessageType { INVALID, SOUND_LOADED };
+};
+
+// callback function prototype
+typedef void SoundPoolCallback(SoundPoolEvent event, SoundPool* soundPool, void* user);
+
+} // namespace android
+
+namespace android::soundpool {
+
+// This class manages Sounds for the SoundPool.
+class SoundManager {
+public:
+ SoundManager();
+ ~SoundManager();
+
+ // Matches corresponding SoundPool API functions
+ int32_t load(int fd, int64_t offset, int64_t length, int32_t priority);
+ bool unload(int32_t soundID);
+ void setCallback(SoundPool* soundPool, SoundPoolCallback* callback, void* user);
+ void* getUserData() const;
+
+ // SoundPool and SoundDecoder access
+ std::shared_ptr<Sound> findSound(int32_t soundID) const;
+
+ // from the SoundDecoder
+ void notify(SoundPoolEvent event);
+
+private:
+
+ // CallbackHandler is used to manage notifications back to the app when a sound
+ // has been loaded. It uses a recursive lock to allow setting the callback
+ // during the callback.
+ class CallbackHandler {
+ public:
+ void setCallback(SoundPool *soundPool, SoundPoolCallback* callback, void* userData)
+ {
+ std::lock_guard<std::recursive_mutex> lock(mCallbackLock);
+ mSoundPool = soundPool;
+ mCallback = callback;
+ mUserData = userData;
+ }
+ void notify(SoundPoolEvent event) const
+ {
+ std::lock_guard<std::recursive_mutex> lock(mCallbackLock);
+ if (mCallback != nullptr) {
+ mCallback(event, mSoundPool, mUserData);
+ // Note: mCallback may call setCallback().
+ // so mCallback, mUserData may have changed.
+ }
+ }
+ void* getUserData() const
+ {
+ std::lock_guard<std::recursive_mutex> lock(mCallbackLock);
+ return mUserData;
+ }
+ private:
+ mutable std::recursive_mutex mCallbackLock; // allow mCallback to setCallback().
+ SoundPool* mSoundPool = nullptr; // GUARDED_BY(mCallbackLock)
+ SoundPoolCallback* mCallback = nullptr; // GUARDED_BY(mCallbackLock)
+ void* mUserData = nullptr; // GUARDED_BY(mCallbackLock)
+ };
+
+ std::shared_ptr<Sound> findSound_l(int32_t soundID) const;
+
+ // The following variables are initialized in constructor and can be accessed anytime.
+ CallbackHandler mCallbackHandler; // has its own lock
+ const std::unique_ptr<SoundDecoder> mDecoder; // has its own lock
+
+ mutable std::mutex mSoundManagerLock;
+ std::unordered_map<int, std::shared_ptr<Sound>> mSounds; // GUARDED_BY(mSoundManagerLock)
+ int32_t mNextSoundID = 0; // GUARDED_BY(mSoundManagerLock)
+};
+
+} // namespace android::soundpool
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index 102bbf0e5931..ac44843859f6 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -16,1124 +16,230 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "SoundPool"
-
-#include <chrono>
-#include <inttypes.h>
-#include <thread>
#include <utils/Log.h>
-#define USE_SHARED_MEM_BUFFER
+#include <algorithm>
+#include <thread>
-#include <media/AudioTrack.h>
#include "SoundPool.h"
-#include "SoundPoolThread.h"
-#include <media/NdkMediaCodec.h>
-#include <media/NdkMediaExtractor.h>
-#include <media/NdkMediaFormat.h>
namespace android
{
-int kDefaultBufferCount = 4;
-uint32_t kMaxSampleRate = 48000;
-uint32_t kDefaultSampleRate = 44100;
-uint32_t kDefaultFrameCount = 1200;
-size_t kDefaultHeapSize = 1024 * 1024; // 1MB
-
-
-SoundPool::SoundPool(int maxChannels, const audio_attributes_t* pAttributes)
-{
- ALOGV("SoundPool constructor: maxChannels=%d, attr.usage=%d, attr.flags=0x%x, attr.tags=%s",
- maxChannels, pAttributes->usage, pAttributes->flags, pAttributes->tags);
-
- // check limits
- mMaxChannels = maxChannels;
- if (mMaxChannels < 1) {
- mMaxChannels = 1;
- }
- else if (mMaxChannels > 32) {
- mMaxChannels = 32;
- }
- ALOGW_IF(maxChannels != mMaxChannels, "App requested %d channels", maxChannels);
-
- mQuit = false;
- mMuted = false;
- mDecodeThread = 0;
- memcpy(&mAttributes, pAttributes, sizeof(audio_attributes_t));
- mAllocated = 0;
- mNextSampleID = 0;
- mNextChannelID = 0;
-
- mCallback = 0;
- mUserData = 0;
-
- mChannelPool = new SoundChannel[mMaxChannels];
- for (int i = 0; i < mMaxChannels; ++i) {
- mChannelPool[i].init(this);
- mChannels.push_back(&mChannelPool[i]);
- }
-
- // start decode thread
- startThreads();
-}
-
-SoundPool::~SoundPool()
-{
- ALOGV("SoundPool destructor");
- mDecodeThread->quit();
- quit();
-
- Mutex::Autolock lock(&mLock);
+// kManagerThreads = 1 historically.
+// Not really necessary to have more than one, but it does speed things up by about
+// 25% having 2 threads instead of 1 when playing many sounds. Having many threads
+// could starve other AudioFlinger clients with SoundPool activity. It may also cause
+// issues with app loading, e.g. Camera.
+static const size_t kStreamManagerThreads = std::thread::hardware_concurrency() >= 4 ? 2 : 1;
- mChannels.clear();
- if (mChannelPool)
- delete [] mChannelPool;
- // clean up samples
- ALOGV("clear samples");
- mSamples.clear();
+// kUseApiLock = true prior to R.
+// Set to true to prevent multiple users access internal to the SoundPool API.
+// Set to false to make the SoundPool methods weakly consistent. When set to false,
+// only AutoPause and AutoResume are locked, which are the only two methods that
+// require API level locking for consistency.
+static constexpr bool kUseApiLock = false;
- if (mDecodeThread)
- delete mDecodeThread;
-}
+namespace {
+// Check input arguments to SoundPool - return "true" to reject request.
-void SoundPool::addToRestartList(SoundChannel* channel)
+bool checkVolume(float *leftVolume, float *rightVolume)
{
- Mutex::Autolock lock(&mRestartLock);
- if (!mQuit) {
- mRestart.push_back(channel);
- mCondition.signal();
+ if (*leftVolume != std::clamp(*leftVolume, 0.f, 1.f) ||
+ *rightVolume != std::clamp(*rightVolume, 0.f, 1.f)) {
+ ALOGI("volume l=%f r=%f out of (0.f, 1.f) bounds, using 1.f", *leftVolume, *rightVolume);
+ // for backward compatibility use 1.f.
+ *leftVolume = *rightVolume = 1.f;
}
+ return false;
}
-void SoundPool::addToStopList(SoundChannel* channel)
+bool checkRate(float *rate)
{
- Mutex::Autolock lock(&mRestartLock);
- if (!mQuit) {
- mStop.push_back(channel);
- mCondition.signal();
+ if (*rate != std::clamp(*rate, 0.125f, 8.f)) {
+ ALOGI("rate %f out of (0.125f, 8.f) bounds, clamping", *rate);
+ // for backward compatibility just clamp
+ *rate = std::clamp(*rate, 0.125f, 8.f);
}
+ return false;
}
-int SoundPool::beginThread(void* arg)
-{
- SoundPool* p = (SoundPool*)arg;
- return p->run();
-}
-
-int SoundPool::run()
+bool checkPriority(int32_t *priority)
{
- mRestartLock.lock();
- while (!mQuit) {
- mCondition.wait(mRestartLock);
- ALOGV("awake");
- if (mQuit) break;
-
- while (!mStop.empty()) {
- SoundChannel* channel;
- ALOGV("Getting channel from stop list");
- List<SoundChannel* >::iterator iter = mStop.begin();
- channel = *iter;
- mStop.erase(iter);
- mRestartLock.unlock();
- if (channel != 0) {
- Mutex::Autolock lock(&mLock);
- channel->stop();
- }
- mRestartLock.lock();
- if (mQuit) break;
- }
-
- while (!mRestart.empty()) {
- SoundChannel* channel;
- ALOGV("Getting channel from list");
- List<SoundChannel*>::iterator iter = mRestart.begin();
- channel = *iter;
- mRestart.erase(iter);
- mRestartLock.unlock();
- if (channel != 0) {
- Mutex::Autolock lock(&mLock);
- channel->nextEvent();
- }
- mRestartLock.lock();
- if (mQuit) break;
- }
+ if (*priority < 0) {
+ ALOGI("negative priority %d, should be >= 0.", *priority);
+ // for backward compatibility, ignore.
}
-
- mStop.clear();
- mRestart.clear();
- mCondition.signal();
- mRestartLock.unlock();
- ALOGV("goodbye");
- return 0;
-}
-
-void SoundPool::quit()
-{
- mRestartLock.lock();
- mQuit = true;
- mCondition.signal();
- mCondition.wait(mRestartLock);
- ALOGV("return from quit");
- mRestartLock.unlock();
-}
-
-bool SoundPool::startThreads()
-{
- createThreadEtc(beginThread, this, "SoundPool");
- if (mDecodeThread == NULL)
- mDecodeThread = new SoundPoolThread(this);
- return mDecodeThread != NULL;
+ return false;
}
-sp<Sample> SoundPool::findSample(int sampleID)
+bool checkLoop(int32_t *loop)
{
- Mutex::Autolock lock(&mLock);
- return findSample_l(sampleID);
+ if (*loop < -1) {
+ ALOGI("loop %d, should be >= -1", *loop);
+ *loop = -1;
+ }
+ return false;
}
-sp<Sample> SoundPool::findSample_l(int sampleID)
-{
- return mSamples.valueFor(sampleID);
-}
+} // namespace
-SoundChannel* SoundPool::findChannel(int channelID)
+SoundPool::SoundPool(int32_t maxStreams, const audio_attributes_t* attributes)
+ : mStreamManager(maxStreams, kStreamManagerThreads, attributes)
{
- for (int i = 0; i < mMaxChannels; ++i) {
- if (mChannelPool[i].channelID() == channelID) {
- return &mChannelPool[i];
- }
- }
- return NULL;
+ ALOGV("%s(maxStreams=%d, attr={ content_type=%d, usage=%d, flags=0x%x, tags=%s })",
+ __func__, maxStreams,
+ attributes->content_type, attributes->usage, attributes->flags, attributes->tags);
}
-SoundChannel* SoundPool::findNextChannel(int channelID)
+SoundPool::~SoundPool()
{
- for (int i = 0; i < mMaxChannels; ++i) {
- if (mChannelPool[i].nextChannelID() == channelID) {
- return &mChannelPool[i];
- }
- }
- return NULL;
+ ALOGV("%s()", __func__);
}
-int SoundPool::load(int fd, int64_t offset, int64_t length, int priority __unused)
+int32_t SoundPool::load(int fd, int64_t offset, int64_t length, int32_t priority)
{
- ALOGV("load: fd=%d, offset=%" PRId64 ", length=%" PRId64 ", priority=%d",
- fd, offset, length, priority);
- int sampleID;
- {
- Mutex::Autolock lock(&mLock);
- sampleID = ++mNextSampleID;
- sp<Sample> sample = new Sample(sampleID, fd, offset, length);
- mSamples.add(sampleID, sample);
- sample->startLoad();
- }
- // mDecodeThread->loadSample() must be called outside of mLock.
- // mDecodeThread->loadSample() may block on mDecodeThread message queue space;
- // the message queue emptying may block on SoundPool::findSample().
- //
- // It theoretically possible that sample loads might decode out-of-order.
- mDecodeThread->loadSample(sampleID);
- return sampleID;
+ ALOGV("%s(fd=%d, offset=%lld, length=%lld, priority=%d)",
+ __func__, fd, (long long)offset, (long long)length, priority);
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ return mSoundManager.load(fd, offset, length, priority);
}
-bool SoundPool::unload(int sampleID)
+bool SoundPool::unload(int32_t soundID)
{
- ALOGV("unload: sampleID=%d", sampleID);
- Mutex::Autolock lock(&mLock);
- return mSamples.removeItem(sampleID) >= 0; // removeItem() returns index or BAD_VALUE
+ ALOGV("%s(%d)", __func__, soundID);
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ return mSoundManager.unload(soundID);
}
-int SoundPool::play(int sampleID, float leftVolume, float rightVolume,
- int priority, int loop, float rate)
+int32_t SoundPool::play(int32_t soundID, float leftVolume, float rightVolume,
+ int32_t priority, int32_t loop, float rate)
{
- ALOGV("play sampleID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f",
- sampleID, leftVolume, rightVolume, priority, loop, rate);
- SoundChannel* channel;
- int channelID;
-
- Mutex::Autolock lock(&mLock);
-
- if (mQuit) {
- return 0;
- }
- // is sample ready?
- sp<Sample> sample(findSample_l(sampleID));
- if ((sample == 0) || (sample->state() != Sample::READY)) {
- ALOGW(" sample %d not READY", sampleID);
- return 0;
- }
+ ALOGV("%s(soundID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f)",
+ __func__, soundID, leftVolume, rightVolume, priority, loop, rate);
- dump();
+ // New for R: check arguments to ensure track can be created.
+ // If SoundPool defers the creation of the AudioTrack to the StreamManager thread,
+ // the failure to create may not be visible to the caller, so this precheck is needed.
+ if (checkVolume(&leftVolume, &rightVolume)
+ || checkPriority(&priority)
+ || checkLoop(&loop)
+ || checkRate(&rate)) return 0;
- // allocate a channel
- channel = allocateChannel_l(priority, sampleID);
-
- // no channel allocated - return 0
- if (!channel) {
- ALOGV("No channel allocated");
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ const std::shared_ptr<soundpool::Sound> sound = mSoundManager.findSound(soundID);
+ if (sound == nullptr || sound->getState() != soundpool::Sound::READY) {
+ ALOGW("%s soundID %d not READY", __func__, soundID);
return 0;
}
- channelID = ++mNextChannelID;
-
- ALOGV("play channel %p state = %d", channel, channel->state());
- channel->play(sample, channelID, leftVolume, rightVolume, priority, loop, rate);
- return channelID;
-}
-
-SoundChannel* SoundPool::allocateChannel_l(int priority, int sampleID)
-{
- List<SoundChannel*>::iterator iter;
- SoundChannel* channel = NULL;
-
- // check if channel for given sampleID still available
- if (!mChannels.empty()) {
- for (iter = mChannels.begin(); iter != mChannels.end(); ++iter) {
- if (sampleID == (*iter)->getPrevSampleID() && (*iter)->state() == SoundChannel::IDLE) {
- channel = *iter;
- mChannels.erase(iter);
- ALOGV("Allocated recycled channel for same sampleID");
- break;
- }
- }
- }
-
- // allocate any channel
- if (!channel && !mChannels.empty()) {
- iter = mChannels.begin();
- if (priority >= (*iter)->priority()) {
- channel = *iter;
- mChannels.erase(iter);
- ALOGV("Allocated active channel");
- }
- }
-
- // update priority and put it back in the list
- if (channel) {
- channel->setPriority(priority);
- for (iter = mChannels.begin(); iter != mChannels.end(); ++iter) {
- if (priority < (*iter)->priority()) {
- break;
- }
- }
- mChannels.insert(iter, channel);
- }
- return channel;
-}
-
-// move a channel from its current position to the front of the list
-void SoundPool::moveToFront_l(SoundChannel* channel)
-{
- for (List<SoundChannel*>::iterator iter = mChannels.begin(); iter != mChannels.end(); ++iter) {
- if (*iter == channel) {
- mChannels.erase(iter);
- mChannels.push_front(channel);
- break;
- }
- }
-}
-
-void SoundPool::pause(int channelID)
-{
- ALOGV("pause(%d)", channelID);
- Mutex::Autolock lock(&mLock);
- SoundChannel* channel = findChannel(channelID);
- if (channel) {
- channel->pause();
- }
+ const int32_t streamID = mStreamManager.queueForPlay(
+ sound, soundID, leftVolume, rightVolume, priority, loop, rate);
+ ALOGV("%s returned %d", __func__, streamID);
+ return streamID;
}
void SoundPool::autoPause()
{
- ALOGV("autoPause()");
- Mutex::Autolock lock(&mLock);
- for (int i = 0; i < mMaxChannels; ++i) {
- SoundChannel* channel = &mChannelPool[i];
- channel->autoPause();
- }
+ ALOGV("%s()", __func__);
+ auto apiLock = std::make_unique<std::lock_guard<std::mutex>>(mApiLock);
+ mStreamManager.forEach([](soundpool::Stream *stream) { stream->autoPause(); });
}
-void SoundPool::resume(int channelID)
+void SoundPool::autoResume()
{
- ALOGV("resume(%d)", channelID);
- Mutex::Autolock lock(&mLock);
- SoundChannel* channel = findChannel(channelID);
- if (channel) {
- channel->resume();
- }
+ ALOGV("%s()", __func__);
+ auto apiLock = std::make_unique<std::lock_guard<std::mutex>>(mApiLock);
+ mStreamManager.forEach([](soundpool::Stream *stream) { stream->autoResume(); });
}
void SoundPool::mute(bool muting)
{
- ALOGV("mute(%d)", muting);
- Mutex::Autolock lock(&mLock);
- mMuted = muting;
- if (!mChannels.empty()) {
- for (List<SoundChannel*>::iterator iter = mChannels.begin();
- iter != mChannels.end(); ++iter) {
- (*iter)->mute(muting);
- }
- }
+ ALOGV("%s(%d)", __func__, muting);
+ auto apiLock = std::make_unique<std::lock_guard<std::mutex>>(mApiLock);
+ mStreamManager.forEach([=](soundpool::Stream *stream) { stream->mute(muting); });
}
-void SoundPool::autoResume()
+void SoundPool::pause(int32_t streamID)
{
- ALOGV("autoResume()");
- Mutex::Autolock lock(&mLock);
- for (int i = 0; i < mMaxChannels; ++i) {
- SoundChannel* channel = &mChannelPool[i];
- channel->autoResume();
+ ALOGV("%s(%d)", __func__, streamID);
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ if (soundpool::Stream* stream = mStreamManager.findStream(streamID)) {
+ stream->pause(streamID);
}
}
-void SoundPool::stop(int channelID)
+void SoundPool::resume(int32_t streamID)
{
- ALOGV("stop(%d)", channelID);
- Mutex::Autolock lock(&mLock);
- SoundChannel* channel = findChannel(channelID);
- if (channel) {
- channel->stop();
- } else {
- channel = findNextChannel(channelID);
- if (channel)
- channel->clearNextEvent();
+ ALOGV("%s(%d)", __func__, streamID);
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ if (soundpool::Stream* stream = mStreamManager.findStream(streamID)) {
+ stream->resume(streamID);
}
}
-void SoundPool::setVolume(int channelID, float leftVolume, float rightVolume)
+void SoundPool::stop(int32_t streamID)
{
- Mutex::Autolock lock(&mLock);
- SoundChannel* channel = findChannel(channelID);
- if (channel) {
- channel->setVolume(leftVolume, rightVolume);
+ ALOGV("%s(%d)", __func__, streamID);
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ soundpool::Stream* stream = mStreamManager.findStream(streamID);
+ if (stream != nullptr && stream->requestStop(streamID)) {
+ mStreamManager.moveToRestartQueue(stream);
}
}
-void SoundPool::setPriority(int channelID, int priority)
+void SoundPool::setVolume(int32_t streamID, float leftVolume, float rightVolume)
{
- ALOGV("setPriority(%d, %d)", channelID, priority);
- Mutex::Autolock lock(&mLock);
- SoundChannel* channel = findChannel(channelID);
- if (channel) {
- channel->setPriority(priority);
+ ALOGV("%s(%d, %f %f)", __func__, streamID, leftVolume, rightVolume);
+ if (checkVolume(&leftVolume, &rightVolume)) return;
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ if (soundpool::Stream* stream = mStreamManager.findStream(streamID)) {
+ stream->setVolume(streamID, leftVolume, rightVolume);
}
}
-void SoundPool::setLoop(int channelID, int loop)
+void SoundPool::setPriority(int32_t streamID, int32_t priority)
{
- ALOGV("setLoop(%d, %d)", channelID, loop);
- Mutex::Autolock lock(&mLock);
- SoundChannel* channel = findChannel(channelID);
- if (channel) {
- channel->setLoop(loop);
+ ALOGV("%s(%d, %d)", __func__, streamID, priority);
+ if (checkPriority(&priority)) return;
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ if (soundpool::Stream* stream = mStreamManager.findStream(streamID)) {
+ stream->setPriority(streamID, priority);
}
}
-void SoundPool::setRate(int channelID, float rate)
+void SoundPool::setLoop(int32_t streamID, int32_t loop)
{
- ALOGV("setRate(%d, %f)", channelID, rate);
- Mutex::Autolock lock(&mLock);
- SoundChannel* channel = findChannel(channelID);
- if (channel) {
- channel->setRate(rate);
+ ALOGV("%s(%d, %d)", __func__, streamID, loop);
+ if (checkLoop(&loop)) return;
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ if (soundpool::Stream* stream = mStreamManager.findStream(streamID)) {
+ stream->setLoop(streamID, loop);
}
}
-// call with lock held
-void SoundPool::done_l(SoundChannel* channel)
+void SoundPool::setRate(int32_t streamID, float rate)
{
- ALOGV("done_l(%d)", channel->channelID());
- // if "stolen", play next event
- if (channel->nextChannelID() != 0) {
- ALOGV("add to restart list");
- addToRestartList(channel);
- }
-
- // return to idle state
- else {
- ALOGV("move to front");
- moveToFront_l(channel);
+ ALOGV("%s(%d, %f)", __func__, streamID, rate);
+ if (checkRate(&rate)) return;
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ if (soundpool::Stream* stream = mStreamManager.findStream(streamID)) {
+ stream->setRate(streamID, rate);
}
}
void SoundPool::setCallback(SoundPoolCallback* callback, void* user)
{
- Mutex::Autolock lock(&mCallbackLock);
- mCallback = callback;
- mUserData = user;
-}
-
-void SoundPool::notify(SoundPoolEvent event)
-{
- Mutex::Autolock lock(&mCallbackLock);
- if (mCallback != NULL) {
- mCallback(event, this, mUserData);
- }
-}
-
-void SoundPool::dump()
-{
- for (int i = 0; i < mMaxChannels; ++i) {
- mChannelPool[i].dump();
- }
-}
-
-
-Sample::Sample(int sampleID, int fd, int64_t offset, int64_t length)
-{
- init();
- mSampleID = sampleID;
- mFd = dup(fd);
- mOffset = offset;
- mLength = length;
- ALOGV("create sampleID=%d, fd=%d, offset=%" PRId64 " length=%" PRId64,
- mSampleID, mFd, mLength, mOffset);
-}
-
-void Sample::init()
-{
- mSize = 0;
- mRefCount = 0;
- mSampleID = 0;
- mState = UNLOADED;
- mFd = -1;
- mOffset = 0;
- mLength = 0;
-}
-
-Sample::~Sample()
-{
- ALOGV("Sample::destructor sampleID=%d, fd=%d", mSampleID, mFd);
- if (mFd > 0) {
- ALOGV("close(%d)", mFd);
- ::close(mFd);
- }
-}
-
-static status_t decode(int fd, int64_t offset, int64_t length,
- uint32_t *rate, int *numChannels, audio_format_t *audioFormat,
- audio_channel_mask_t *channelMask, sp<MemoryHeapBase> heap,
- size_t *memsize) {
-
- ALOGV("fd %d, offset %" PRId64 ", size %" PRId64, fd, offset, length);
- AMediaExtractor *ex = AMediaExtractor_new();
- status_t err = AMediaExtractor_setDataSourceFd(ex, fd, offset, length);
-
- if (err != AMEDIA_OK) {
- AMediaExtractor_delete(ex);
- return err;
- }
-
- *audioFormat = AUDIO_FORMAT_PCM_16_BIT;
-
- size_t numTracks = AMediaExtractor_getTrackCount(ex);
- for (size_t i = 0; i < numTracks; i++) {
- AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);
- const char *mime;
- if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {
- AMediaExtractor_delete(ex);
- AMediaFormat_delete(format);
- return UNKNOWN_ERROR;
- }
- if (strncmp(mime, "audio/", 6) == 0) {
-
- AMediaCodec *codec = AMediaCodec_createDecoderByType(mime);
- if (codec == NULL
- || AMediaCodec_configure(codec, format,
- NULL /* window */, NULL /* drm */, 0 /* flags */) != AMEDIA_OK
- || AMediaCodec_start(codec) != AMEDIA_OK
- || AMediaExtractor_selectTrack(ex, i) != AMEDIA_OK) {
- AMediaExtractor_delete(ex);
- AMediaCodec_delete(codec);
- AMediaFormat_delete(format);
- return UNKNOWN_ERROR;
- }
-
- bool sawInputEOS = false;
- bool sawOutputEOS = false;
- uint8_t* writePos = static_cast<uint8_t*>(heap->getBase());
- size_t available = heap->getSize();
- size_t written = 0;
-
- AMediaFormat_delete(format);
- format = AMediaCodec_getOutputFormat(codec);
-
- while (!sawOutputEOS) {
- if (!sawInputEOS) {
- ssize_t bufidx = AMediaCodec_dequeueInputBuffer(codec, 5000);
- ALOGV("input buffer %zd", bufidx);
- if (bufidx >= 0) {
- size_t bufsize;
- uint8_t *buf = AMediaCodec_getInputBuffer(codec, bufidx, &bufsize);
- if (buf == nullptr) {
- ALOGE("AMediaCodec_getInputBuffer returned nullptr, short decode");
- break;
- }
- int sampleSize = AMediaExtractor_readSampleData(ex, buf, bufsize);
- ALOGV("read %d", sampleSize);
- if (sampleSize < 0) {
- sampleSize = 0;
- sawInputEOS = true;
- ALOGV("EOS");
- }
- int64_t presentationTimeUs = AMediaExtractor_getSampleTime(ex);
-
- media_status_t mstatus = AMediaCodec_queueInputBuffer(codec, bufidx,
- 0 /* offset */, sampleSize, presentationTimeUs,
- sawInputEOS ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
- if (mstatus != AMEDIA_OK) {
- // AMEDIA_ERROR_UNKNOWN == { -ERANGE -EINVAL -EACCES }
- ALOGE("AMediaCodec_queueInputBuffer returned status %d, short decode",
- (int)mstatus);
- break;
- }
- (void)AMediaExtractor_advance(ex);
- }
- }
-
- AMediaCodecBufferInfo info;
- int status = AMediaCodec_dequeueOutputBuffer(codec, &info, 1);
- ALOGV("dequeueoutput returned: %d", status);
- if (status >= 0) {
- if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
- ALOGV("output EOS");
- sawOutputEOS = true;
- }
- ALOGV("got decoded buffer size %d", info.size);
-
- uint8_t *buf = AMediaCodec_getOutputBuffer(codec, status, NULL /* out_size */);
- if (buf == nullptr) {
- ALOGE("AMediaCodec_getOutputBuffer returned nullptr, short decode");
- break;
- }
- size_t dataSize = info.size;
- if (dataSize > available) {
- dataSize = available;
- }
- memcpy(writePos, buf + info.offset, dataSize);
- writePos += dataSize;
- written += dataSize;
- available -= dataSize;
- media_status_t mstatus = AMediaCodec_releaseOutputBuffer(
- codec, status, false /* render */);
- if (mstatus != AMEDIA_OK) {
- // AMEDIA_ERROR_UNKNOWN == { -ERANGE -EINVAL -EACCES }
- ALOGE("AMediaCodec_releaseOutputBuffer returned status %d, short decode",
- (int)mstatus);
- break;
- }
- if (available == 0) {
- // there might be more data, but there's no space for it
- sawOutputEOS = true;
- }
- } else if (status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
- ALOGV("output buffers changed");
- } else if (status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
- AMediaFormat_delete(format);
- format = AMediaCodec_getOutputFormat(codec);
- ALOGV("format changed to: %s", AMediaFormat_toString(format));
- } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
- ALOGV("no output buffer right now");
- } else if (status <= AMEDIA_ERROR_BASE) {
- ALOGE("decode error: %d", status);
- break;
- } else {
- ALOGV("unexpected info code: %d", status);
- }
- }
-
- (void)AMediaCodec_stop(codec);
- (void)AMediaCodec_delete(codec);
- (void)AMediaExtractor_delete(ex);
- if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, (int32_t*) rate) ||
- !AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, numChannels)) {
- (void)AMediaFormat_delete(format);
- return UNKNOWN_ERROR;
- }
- if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_MASK,
- (int32_t*) channelMask)) {
- *channelMask = AUDIO_CHANNEL_NONE;
- }
- (void)AMediaFormat_delete(format);
- *memsize = written;
- return OK;
- }
- (void)AMediaFormat_delete(format);
- }
- (void)AMediaExtractor_delete(ex);
- return UNKNOWN_ERROR;
-}
-
-status_t Sample::doLoad()
-{
- uint32_t sampleRate;
- int numChannels;
- audio_format_t format;
- audio_channel_mask_t channelMask;
- status_t status;
- mHeap = new MemoryHeapBase(kDefaultHeapSize);
-
- ALOGV("Start decode");
- status = decode(mFd, mOffset, mLength, &sampleRate, &numChannels, &format,
- &channelMask, mHeap, &mSize);
- ALOGV("close(%d)", mFd);
- ::close(mFd);
- mFd = -1;
- if (status != NO_ERROR) {
- ALOGE("Unable to load sample");
- goto error;
- }
- ALOGV("pointer = %p, size = %zu, sampleRate = %u, numChannels = %d",
- mHeap->getBase(), mSize, sampleRate, numChannels);
-
- if (sampleRate > kMaxSampleRate) {
- ALOGE("Sample rate (%u) out of range", sampleRate);
- status = BAD_VALUE;
- goto error;
- }
-
- if ((numChannels < 1) || (numChannels > FCC_8)) {
- ALOGE("Sample channel count (%d) out of range", numChannels);
- status = BAD_VALUE;
- goto error;
- }
-
- mData = new MemoryBase(mHeap, 0, mSize);
- mSampleRate = sampleRate;
- mNumChannels = numChannels;
- mFormat = format;
- mChannelMask = channelMask;
- mState = READY;
- return NO_ERROR;
-
-error:
- mHeap.clear();
- return status;
-}
-
-
-void SoundChannel::init(SoundPool* soundPool)
-{
- mSoundPool = soundPool;
- mPrevSampleID = -1;
-}
-
-// call with sound pool lock held
-void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftVolume,
- float rightVolume, int priority, int loop, float rate)
-{
- sp<AudioTrack> oldTrack;
- sp<AudioTrack> newTrack;
- status_t status = NO_ERROR;
-
- { // scope for the lock
- Mutex::Autolock lock(&mLock);
-
- ALOGV("SoundChannel::play %p: sampleID=%d, channelID=%d, leftVolume=%f, rightVolume=%f,"
- " priority=%d, loop=%d, rate=%f",
- this, sample->sampleID(), nextChannelID, leftVolume, rightVolume,
- priority, loop, rate);
-
- // if not idle, this voice is being stolen
- if (mState != IDLE) {
- ALOGV("channel %d stolen - event queued for channel %d", channelID(), nextChannelID);
- mNextEvent.set(sample, nextChannelID, leftVolume, rightVolume, priority, loop, rate);
- stop_l();
- return;
- }
-
- // initialize track
- size_t afFrameCount;
- uint32_t afSampleRate;
- audio_stream_type_t streamType =
- AudioSystem::attributesToStreamType(*mSoundPool->attributes());
- if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) {
- afFrameCount = kDefaultFrameCount;
- }
- if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
- afSampleRate = kDefaultSampleRate;
- }
- int numChannels = sample->numChannels();
- uint32_t sampleRate = uint32_t(float(sample->sampleRate()) * rate + 0.5);
- size_t frameCount = 0;
-
- if (loop) {
- const audio_format_t format = sample->format();
- const size_t frameSize = audio_is_linear_pcm(format)
- ? numChannels * audio_bytes_per_sample(format) : 1;
- frameCount = sample->size() / frameSize;
- }
-
-#ifndef USE_SHARED_MEM_BUFFER
- uint32_t totalFrames = (kDefaultBufferCount * afFrameCount * sampleRate) / afSampleRate;
- // Ensure minimum audio buffer size in case of short looped sample
- if(frameCount < totalFrames) {
- frameCount = totalFrames;
- }
-#endif
-
- // check if the existing track has the same sample id.
- if (mAudioTrack != 0 && mPrevSampleID == sample->sampleID()) {
- // the sample rate may fail to change if the audio track is a fast track.
- if (mAudioTrack->setSampleRate(sampleRate) == NO_ERROR) {
- newTrack = mAudioTrack;
- ALOGV("reusing track %p for sample %d", mAudioTrack.get(), sample->sampleID());
- }
- }
- if (newTrack == 0) {
- // mToggle toggles each time a track is started on a given channel.
- // The toggle is concatenated with the SoundChannel address and passed to AudioTrack
- // as callback user data. This enables the detection of callbacks received from the old
- // audio track while the new one is being started and avoids processing them with
- // wrong audio audio buffer size (mAudioBufferSize)
- unsigned long toggle = mToggle ^ 1;
- void *userData = (void *)((unsigned long)this | toggle);
- audio_channel_mask_t sampleChannelMask = sample->channelMask();
- // When sample contains a not none channel mask, use it as is.
- // Otherwise, use channel count to calculate channel mask.
- audio_channel_mask_t channelMask = sampleChannelMask != AUDIO_CHANNEL_NONE
- ? sampleChannelMask : audio_channel_out_mask_from_count(numChannels);
-
- // do not create a new audio track if current track is compatible with sample parameters
- #ifdef USE_SHARED_MEM_BUFFER
- newTrack = new AudioTrack(streamType, sampleRate, sample->format(),
- channelMask, sample->getIMemory(), AUDIO_OUTPUT_FLAG_FAST, callback, userData,
- 0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE,
- AudioTrack::TRANSFER_DEFAULT,
- NULL /*offloadInfo*/, -1 /*uid*/, -1 /*pid*/, mSoundPool->attributes());
- #else
- uint32_t bufferFrames = (totalFrames + (kDefaultBufferCount - 1)) / kDefaultBufferCount;
- newTrack = new AudioTrack(streamType, sampleRate, sample->format(),
- channelMask, frameCount, AUDIO_OUTPUT_FLAG_FAST, callback, userData,
- bufferFrames, AUDIO_SESSION_ALLOCATE, AudioTrack::TRANSFER_DEFAULT,
- NULL /*offloadInfo*/, -1 /*uid*/, -1 /*pid*/, mSoundPool->attributes());
- #endif
- oldTrack = mAudioTrack;
- status = newTrack->initCheck();
- if (status != NO_ERROR) {
- ALOGE("Error creating AudioTrack");
- // newTrack goes out of scope, so reference count drops to zero
- goto exit;
- }
- // From now on, AudioTrack callbacks received with previous toggle value will be ignored.
- mToggle = toggle;
- mAudioTrack = newTrack;
- ALOGV("using new track %p for sample %d", newTrack.get(), sample->sampleID());
- }
- if (mMuted) {
- newTrack->setVolume(0.0f, 0.0f);
- } else {
- newTrack->setVolume(leftVolume, rightVolume);
- }
- newTrack->setLoop(0, frameCount, loop);
- mPos = 0;
- mSample = sample;
- mChannelID = nextChannelID;
- mPriority = priority;
- mLoop = loop;
- mLeftVolume = leftVolume;
- mRightVolume = rightVolume;
- mNumChannels = numChannels;
- mRate = rate;
- clearNextEvent();
- mState = PLAYING;
- mAudioTrack->start();
- mAudioBufferSize = newTrack->frameCount()*newTrack->frameSize();
- }
-
-exit:
- ALOGV("delete oldTrack %p", oldTrack.get());
- if (status != NO_ERROR) {
- mAudioTrack.clear();
- }
-}
-
-void SoundChannel::nextEvent()
-{
- sp<Sample> sample;
- int nextChannelID;
- float leftVolume;
- float rightVolume;
- int priority;
- int loop;
- float rate;
-
- // check for valid event
- {
- Mutex::Autolock lock(&mLock);
- nextChannelID = mNextEvent.channelID();
- if (nextChannelID == 0) {
- ALOGV("stolen channel has no event");
- return;
- }
-
- sample = mNextEvent.sample();
- leftVolume = mNextEvent.leftVolume();
- rightVolume = mNextEvent.rightVolume();
- priority = mNextEvent.priority();
- loop = mNextEvent.loop();
- rate = mNextEvent.rate();
- }
-
- ALOGV("Starting stolen channel %d -> %d", channelID(), nextChannelID);
- play(sample, nextChannelID, leftVolume, rightVolume, priority, loop, rate);
-}
-
-void SoundChannel::callback(int event, void* user, void *info)
-{
- SoundChannel* channel = static_cast<SoundChannel*>((void *)((unsigned long)user & ~1));
-
- channel->process(event, info, (unsigned long)user & 1);
-}
-
-void SoundChannel::process(int event, void *info, unsigned long toggle)
-{
- //ALOGV("process(%d)", mChannelID);
-
- Mutex::Autolock lock(&mLock);
-
- AudioTrack::Buffer* b = NULL;
- if (event == AudioTrack::EVENT_MORE_DATA) {
- b = static_cast<AudioTrack::Buffer *>(info);
- }
-
- if (mToggle != toggle) {
- ALOGV("process wrong toggle %p channel %d", this, mChannelID);
- if (b != NULL) {
- b->size = 0;
- }
- return;
- }
-
- sp<Sample> sample = mSample;
-
-// ALOGV("SoundChannel::process event %d", event);
-
- if (event == AudioTrack::EVENT_MORE_DATA) {
-
- // check for stop state
- if (b->size == 0) return;
-
- if (mState == IDLE) {
- b->size = 0;
- return;
- }
-
- if (sample != 0) {
- // fill buffer
- uint8_t* q = (uint8_t*) b->i8;
- size_t count = 0;
-
- if (mPos < (int)sample->size()) {
- uint8_t* p = sample->data() + mPos;
- count = sample->size() - mPos;
- if (count > b->size) {
- count = b->size;
- }
- memcpy(q, p, count);
-// ALOGV("fill: q=%p, p=%p, mPos=%u, b->size=%u, count=%d", q, p, mPos, b->size,
-// count);
- } else if (mPos < mAudioBufferSize) {
- count = mAudioBufferSize - mPos;
- if (count > b->size) {
- count = b->size;
- }
- memset(q, 0, count);
-// ALOGV("fill extra: q=%p, mPos=%u, b->size=%u, count=%d", q, mPos, b->size, count);
- }
-
- mPos += count;
- b->size = count;
- //ALOGV("buffer=%p, [0]=%d", b->i16, b->i16[0]);
- }
- } else if (event == AudioTrack::EVENT_UNDERRUN || event == AudioTrack::EVENT_BUFFER_END) {
- ALOGV("process %p channel %d event %s",
- this, mChannelID, (event == AudioTrack::EVENT_UNDERRUN) ? "UNDERRUN" :
- "BUFFER_END");
- // Only BUFFER_END should happen as we use static tracks.
- setVolume_l(0.f, 0.f); // set volume to 0 to indicate no need to ramp volume down.
- mSoundPool->addToStopList(this);
- } else if (event == AudioTrack::EVENT_LOOP_END) {
- ALOGV("End loop %p channel %d", this, mChannelID);
- } else if (event == AudioTrack::EVENT_NEW_IAUDIOTRACK) {
- ALOGV("process %p channel %d NEW_IAUDIOTRACK", this, mChannelID);
- } else {
- ALOGW("SoundChannel::process unexpected event %d", event);
- }
-}
-
-
-// call with lock held
-bool SoundChannel::doStop_l()
-{
- if (mState != IDLE) {
- ALOGV("stop");
- if (mLeftVolume != 0.f || mRightVolume != 0.f) {
- setVolume_l(0.f, 0.f);
- if (mSoundPool->attributes()->usage != AUDIO_USAGE_GAME) {
- // Since we're forcibly halting the previously playing content,
- // we sleep here to ensure the volume is ramped down before we stop the track.
- // Ideally the sleep time is the mixer period, or an approximation thereof
- // (Fast vs Normal tracks are different).
- ALOGV("sleeping: ChannelID:%d SampleID:%d", mChannelID, mSample->sampleID());
- std::this_thread::sleep_for(std::chrono::milliseconds(20));
- }
- }
- mAudioTrack->stop();
- mPrevSampleID = mSample->sampleID();
- mSample.clear();
- mState = IDLE;
- mPriority = IDLE_PRIORITY;
- return true;
- }
- return false;
-}
-
-// call with lock held and sound pool lock held
-void SoundChannel::stop_l()
-{
- if (doStop_l()) {
- mSoundPool->done_l(this);
- }
-}
-
-// call with sound pool lock held
-void SoundChannel::stop()
-{
- bool stopped;
- {
- Mutex::Autolock lock(&mLock);
- stopped = doStop_l();
- }
-
- if (stopped) {
- mSoundPool->done_l(this);
- }
-}
-
-//FIXME: Pause is a little broken right now
-void SoundChannel::pause()
-{
- Mutex::Autolock lock(&mLock);
- if (mState == PLAYING) {
- ALOGV("pause track");
- mState = PAUSED;
- mAudioTrack->pause();
- }
-}
-
-void SoundChannel::autoPause()
-{
- Mutex::Autolock lock(&mLock);
- if (mState == PLAYING) {
- ALOGV("pause track");
- mState = PAUSED;
- mAutoPaused = true;
- mAudioTrack->pause();
- }
-}
-
-void SoundChannel::resume()
-{
- Mutex::Autolock lock(&mLock);
- if (mState == PAUSED) {
- ALOGV("resume track");
- mState = PLAYING;
- mAutoPaused = false;
- mAudioTrack->start();
- }
-}
-
-void SoundChannel::autoResume()
-{
- Mutex::Autolock lock(&mLock);
- if (mAutoPaused && (mState == PAUSED)) {
- ALOGV("resume track");
- mState = PLAYING;
- mAutoPaused = false;
- mAudioTrack->start();
- }
-}
-
-void SoundChannel::setRate(float rate)
-{
- Mutex::Autolock lock(&mLock);
- if (mAudioTrack != NULL && mSample != 0) {
- uint32_t sampleRate = uint32_t(float(mSample->sampleRate()) * rate + 0.5);
- mAudioTrack->setSampleRate(sampleRate);
- mRate = rate;
- }
-}
-
-// call with lock held
-void SoundChannel::setVolume_l(float leftVolume, float rightVolume)
-{
- mLeftVolume = leftVolume;
- mRightVolume = rightVolume;
- if (mAudioTrack != NULL && !mMuted)
- mAudioTrack->setVolume(leftVolume, rightVolume);
-}
-
-void SoundChannel::setVolume(float leftVolume, float rightVolume)
-{
- Mutex::Autolock lock(&mLock);
- setVolume_l(leftVolume, rightVolume);
-}
-
-void SoundChannel::mute(bool muting)
-{
- Mutex::Autolock lock(&mLock);
- mMuted = muting;
- if (mAudioTrack != NULL) {
- if (mMuted) {
- mAudioTrack->setVolume(0.0f, 0.0f);
- } else {
- mAudioTrack->setVolume(mLeftVolume, mRightVolume);
- }
- }
-}
-
-void SoundChannel::setLoop(int loop)
-{
- Mutex::Autolock lock(&mLock);
- if (mAudioTrack != NULL && mSample != 0) {
- uint32_t loopEnd = mSample->size()/mNumChannels/
- ((mSample->format() == AUDIO_FORMAT_PCM_16_BIT) ? sizeof(int16_t) : sizeof(uint8_t));
- mAudioTrack->setLoop(0, loopEnd, loop);
- mLoop = loop;
- }
-}
-
-SoundChannel::~SoundChannel()
-{
- ALOGV("SoundChannel destructor %p", this);
- {
- Mutex::Autolock lock(&mLock);
- clearNextEvent();
- doStop_l();
- }
- // do not call AudioTrack destructor with mLock held as it will wait for the AudioTrack
- // callback thread to exit which may need to execute process() and acquire the mLock.
- mAudioTrack.clear();
-}
-
-void SoundChannel::dump()
-{
- ALOGV("mState = %d mChannelID=%d, mNumChannels=%d, mPos = %d, mPriority=%d, mLoop=%d",
- mState, mChannelID, mNumChannels, mPos, mPriority, mLoop);
+ ALOGV("%s(%p, %p)", __func__, callback, user);
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ mSoundManager.setCallback(this, callback, user);
}
-void SoundEvent::set(const sp<Sample>& sample, int channelID, float leftVolume,
- float rightVolume, int priority, int loop, float rate)
+void* SoundPool::getUserData() const
{
- mSample = sample;
- mChannelID = channelID;
- mLeftVolume = leftVolume;
- mRightVolume = rightVolume;
- mPriority = priority;
- mLoop = loop;
- mRate =rate;
+ ALOGV("%s()", __func__);
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ return mSoundManager.getUserData();
}
} // end namespace android
diff --git a/media/jni/soundpool/SoundPool.h b/media/jni/soundpool/SoundPool.h
index 01e4faae6f6c..d5b16ef629cd 100644
--- a/media/jni/soundpool/SoundPool.h
+++ b/media/jni/soundpool/SoundPool.h
@@ -14,227 +14,59 @@
* limitations under the License.
*/
-#ifndef SOUNDPOOL_H_
-#define SOUNDPOOL_H_
+#pragma once
-#include <utils/threads.h>
-#include <utils/List.h>
-#include <utils/Vector.h>
-#include <utils/KeyedVector.h>
-#include <media/AudioTrack.h>
-#include <binder/MemoryHeapBase.h>
-#include <binder/MemoryBase.h>
+#include "SoundManager.h"
+#include "StreamManager.h"
namespace android {
-static const int IDLE_PRIORITY = -1;
-
-// forward declarations
-class SoundEvent;
-class SoundPoolThread;
-class SoundPool;
-
-// for queued events
-class SoundPoolEvent {
-public:
- explicit SoundPoolEvent(int msg, int arg1=0, int arg2=0) :
- mMsg(msg), mArg1(arg1), mArg2(arg2) {}
- int mMsg;
- int mArg1;
- int mArg2;
- enum MessageType { INVALID, SAMPLE_LOADED };
-};
-
-// callback function prototype
-typedef void SoundPoolCallback(SoundPoolEvent event, SoundPool* soundPool, void* user);
-
-// tracks samples used by application
-class Sample : public RefBase {
-public:
- enum sample_state { UNLOADED, LOADING, READY, UNLOADING };
- Sample(int sampleID, int fd, int64_t offset, int64_t length);
- ~Sample();
- int sampleID() { return mSampleID; }
- int numChannels() { return mNumChannels; }
- int sampleRate() { return mSampleRate; }
- audio_format_t format() { return mFormat; }
- audio_channel_mask_t channelMask() { return mChannelMask; }
- size_t size() { return mSize; }
- int state() { return mState; }
- uint8_t* data() { return static_cast<uint8_t*>(mData->unsecurePointer()); }
- status_t doLoad();
- void startLoad() { mState = LOADING; }
- sp<IMemory> getIMemory() { return mData; }
-
-private:
- void init();
-
- size_t mSize;
- volatile int32_t mRefCount;
- uint16_t mSampleID;
- uint16_t mSampleRate;
- uint8_t mState;
- uint8_t mNumChannels;
- audio_format_t mFormat;
- audio_channel_mask_t mChannelMask;
- int mFd;
- int64_t mOffset;
- int64_t mLength;
- sp<IMemory> mData;
- sp<MemoryHeapBase> mHeap;
-};
-
-// stores pending events for stolen channels
-class SoundEvent
-{
-public:
- SoundEvent() : mChannelID(0), mLeftVolume(0), mRightVolume(0),
- mPriority(IDLE_PRIORITY), mLoop(0), mRate(0) {}
- void set(const sp<Sample>& sample, int channelID, float leftVolume,
- float rightVolume, int priority, int loop, float rate);
- sp<Sample> sample() { return mSample; }
- int channelID() { return mChannelID; }
- float leftVolume() { return mLeftVolume; }
- float rightVolume() { return mRightVolume; }
- int priority() { return mPriority; }
- int loop() { return mLoop; }
- float rate() { return mRate; }
- void clear() { mChannelID = 0; mSample.clear(); }
-
-protected:
- sp<Sample> mSample;
- int mChannelID;
- float mLeftVolume;
- float mRightVolume;
- int mPriority;
- int mLoop;
- float mRate;
-};
-
-// for channels aka AudioTracks
-class SoundChannel : public SoundEvent {
-public:
- enum state { IDLE, RESUMING, STOPPING, PAUSED, PLAYING };
- SoundChannel() : mState(IDLE), mNumChannels(1),
- mPos(0), mToggle(0), mAutoPaused(false), mMuted(false) {}
- ~SoundChannel();
- void init(SoundPool* soundPool);
- void play(const sp<Sample>& sample, int channelID, float leftVolume, float rightVolume,
- int priority, int loop, float rate);
- void setVolume_l(float leftVolume, float rightVolume);
- void setVolume(float leftVolume, float rightVolume);
- void mute(bool muting);
- void stop_l();
- void stop();
- void pause();
- void autoPause();
- void resume();
- void autoResume();
- void setRate(float rate);
- int state() { return mState; }
- void setPriority(int priority) { mPriority = priority; }
- void setLoop(int loop);
- int numChannels() { return mNumChannels; }
- void clearNextEvent() { mNextEvent.clear(); }
- void nextEvent();
- int nextChannelID() { return mNextEvent.channelID(); }
- void dump();
- int getPrevSampleID(void) { return mPrevSampleID; }
-
-private:
- static void callback(int event, void* user, void *info);
- void process(int event, void *info, unsigned long toggle);
- bool doStop_l();
-
- SoundPool* mSoundPool;
- sp<AudioTrack> mAudioTrack;
- SoundEvent mNextEvent;
- Mutex mLock;
- int mState;
- int mNumChannels;
- int mPos;
- int mAudioBufferSize;
- unsigned long mToggle;
- bool mAutoPaused;
- int mPrevSampleID;
- bool mMuted;
-};
-
-// application object for managing a pool of sounds
+/**
+ * Native class for Java SoundPool, manages a pool of sounds.
+ *
+ * See the Android SoundPool Java documentation for description of valid values.
+ * https://developer.android.com/reference/android/media/SoundPool
+ */
class SoundPool {
- friend class SoundPoolThread;
- friend class SoundChannel;
public:
- SoundPool(int maxChannels, const audio_attributes_t* pAttributes);
+ SoundPool(int32_t maxStreams, const audio_attributes_t* attributes);
~SoundPool();
- int load(int fd, int64_t offset, int64_t length, int priority);
- bool unload(int sampleID);
- int play(int sampleID, float leftVolume, float rightVolume, int priority,
- int loop, float rate);
- void pause(int channelID);
- void mute(bool muting);
+
+ // SoundPool Java API support
+ int32_t load(int fd, int64_t offset, int64_t length, int32_t priority);
+ bool unload(int32_t soundID);
+ int32_t play(int32_t soundID, float leftVolume, float rightVolume, int32_t priority,
+ int32_t loop, float rate);
+ void pause(int32_t streamID);
void autoPause();
- void resume(int channelID);
+ void resume(int32_t streamID);
void autoResume();
- void stop(int channelID);
- void setVolume(int channelID, float leftVolume, float rightVolume);
- void setPriority(int channelID, int priority);
- void setLoop(int channelID, int loop);
- void setRate(int channelID, float rate);
- const audio_attributes_t* attributes() { return &mAttributes; }
-
- // called from SoundPoolThread
- void sampleLoaded(int sampleID);
- sp<Sample> findSample(int sampleID);
-
- // called from AudioTrack thread
- void done_l(SoundChannel* channel);
-
- // callback function
+ void stop(int32_t streamID);
+ void setVolume(int32_t streamID, float leftVolume, float rightVolume);
+ void setPriority(int32_t streamID, int32_t priority);
+ void setLoop(int32_t streamID, int32_t loop);
+ void setRate(int32_t streamID, float rate);
void setCallback(SoundPoolCallback* callback, void* user);
- void* getUserData() { return mUserData; }
+ void* getUserData() const;
-private:
- SoundPool() {} // no default constructor
- bool startThreads();
- sp<Sample> findSample_l(int sampleID);
- SoundChannel* findChannel (int channelID);
- SoundChannel* findNextChannel (int channelID);
- SoundChannel* allocateChannel_l(int priority, int sampleID);
- void moveToFront_l(SoundChannel* channel);
- void notify(SoundPoolEvent event);
- void dump();
-
- // restart thread
- void addToRestartList(SoundChannel* channel);
- void addToStopList(SoundChannel* channel);
- static int beginThread(void* arg);
- int run();
- void quit();
+ // not exposed in the public Java API, used for internal playerSetVolume() muting.
+ void mute(bool muting);
- Mutex mLock;
- Mutex mRestartLock;
- Condition mCondition;
- SoundPoolThread* mDecodeThread;
- SoundChannel* mChannelPool;
- List<SoundChannel*> mChannels;
- List<SoundChannel*> mRestart;
- List<SoundChannel*> mStop;
- DefaultKeyedVector< int, sp<Sample> > mSamples;
- int mMaxChannels;
- audio_attributes_t mAttributes;
- int mAllocated;
- int mNextSampleID;
- int mNextChannelID;
- bool mQuit;
- bool mMuted;
+private:
- // callback
- Mutex mCallbackLock;
- SoundPoolCallback* mCallback;
- void* mUserData;
+ // Constructor initialized variables
+ // Can access without lock as they are internally locked,
+ // though care needs to be taken that the final result composed of
+ // individually consistent actions are consistent.
+ soundpool::SoundManager mSoundManager;
+ soundpool::StreamManager mStreamManager;
+
+ // mApiLock serializes SoundPool application calls (configurable by kUseApiLock).
+ // It only locks at the SoundPool layer and not below. At this level,
+ // mApiLock is only required for autoPause() and autoResume() to prevent zippering
+ // of the individual pauses and resumes, and mute() for self-interaction with itself.
+ // It is optional for all other apis.
+ mutable std::mutex mApiLock;
};
} // end namespace android
-
-#endif /*SOUNDPOOL_H_*/
diff --git a/media/jni/soundpool/SoundPoolThread.cpp b/media/jni/soundpool/SoundPoolThread.cpp
deleted file mode 100644
index ba3b482935dd..000000000000
--- a/media/jni/soundpool/SoundPoolThread.cpp
+++ /dev/null
@@ -1,114 +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.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "SoundPoolThread"
-#include "utils/Log.h"
-
-#include "SoundPoolThread.h"
-
-namespace android {
-
-void SoundPoolThread::write(SoundPoolMsg msg) {
- Mutex::Autolock lock(&mLock);
- while (mMsgQueue.size() >= maxMessages) {
- mCondition.wait(mLock);
- }
-
- // if thread is quitting, don't add to queue
- if (mRunning) {
- mMsgQueue.push(msg);
- mCondition.signal();
- }
-}
-
-const SoundPoolMsg SoundPoolThread::read() {
- Mutex::Autolock lock(&mLock);
- while (mMsgQueue.size() == 0) {
- mCondition.wait(mLock);
- }
- SoundPoolMsg msg = mMsgQueue[0];
- mMsgQueue.removeAt(0);
- mCondition.signal();
- return msg;
-}
-
-void SoundPoolThread::quit() {
- Mutex::Autolock lock(&mLock);
- if (mRunning) {
- mRunning = false;
- mMsgQueue.clear();
- mMsgQueue.push(SoundPoolMsg(SoundPoolMsg::KILL, 0));
- mCondition.signal();
- mCondition.wait(mLock);
- }
- ALOGV("return from quit");
-}
-
-SoundPoolThread::SoundPoolThread(SoundPool* soundPool) :
- mSoundPool(soundPool)
-{
- mMsgQueue.setCapacity(maxMessages);
- if (createThreadEtc(beginThread, this, "SoundPoolThread")) {
- mRunning = true;
- }
-}
-
-SoundPoolThread::~SoundPoolThread()
-{
- quit();
-}
-
-int SoundPoolThread::beginThread(void* arg) {
- ALOGV("beginThread");
- SoundPoolThread* soundPoolThread = (SoundPoolThread*)arg;
- return soundPoolThread->run();
-}
-
-int SoundPoolThread::run() {
- ALOGV("run");
- for (;;) {
- SoundPoolMsg msg = read();
- ALOGV("Got message m=%d, mData=%d", msg.mMessageType, msg.mData);
- switch (msg.mMessageType) {
- case SoundPoolMsg::KILL:
- ALOGV("goodbye");
- return NO_ERROR;
- case SoundPoolMsg::LOAD_SAMPLE:
- doLoadSample(msg.mData);
- break;
- default:
- ALOGW("run: Unrecognized message %d\n",
- msg.mMessageType);
- break;
- }
- }
-}
-
-void SoundPoolThread::loadSample(int sampleID) {
- write(SoundPoolMsg(SoundPoolMsg::LOAD_SAMPLE, sampleID));
-}
-
-void SoundPoolThread::doLoadSample(int sampleID) {
- sp <Sample> sample = mSoundPool->findSample(sampleID);
- status_t status = -1;
- if (sample != 0) {
- status = sample->doLoad();
- }
- mSoundPool->notify(SoundPoolEvent(SoundPoolEvent::SAMPLE_LOADED, sampleID, status));
-}
-
-} // end namespace android
diff --git a/media/jni/soundpool/SoundPoolThread.h b/media/jni/soundpool/SoundPoolThread.h
deleted file mode 100644
index 7b3e1dda0a23..000000000000
--- a/media/jni/soundpool/SoundPoolThread.h
+++ /dev/null
@@ -1,66 +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.
- */
-
-#ifndef SOUNDPOOLTHREAD_H_
-#define SOUNDPOOLTHREAD_H_
-
-#include <utils/threads.h>
-#include <utils/Vector.h>
-#include <media/AudioTrack.h>
-
-#include "SoundPool.h"
-
-namespace android {
-
-class SoundPoolMsg {
-public:
- enum MessageType { INVALID, KILL, LOAD_SAMPLE };
- SoundPoolMsg() : mMessageType(INVALID), mData(0) {}
- SoundPoolMsg(MessageType MessageType, int data) :
- mMessageType(MessageType), mData(data) {}
- uint16_t mMessageType;
- uint16_t mData;
-};
-
-/*
- * This class handles background requests from the SoundPool
- */
-class SoundPoolThread {
-public:
- explicit SoundPoolThread(SoundPool* SoundPool);
- ~SoundPoolThread();
- void loadSample(int sampleID);
- void quit();
- void write(SoundPoolMsg msg);
-
-private:
- static const size_t maxMessages = 128;
-
- static int beginThread(void* arg);
- int run();
- void doLoadSample(int sampleID);
- const SoundPoolMsg read();
-
- Mutex mLock;
- Condition mCondition;
- Vector<SoundPoolMsg> mMsgQueue;
- SoundPool* mSoundPool;
- bool mRunning;
-};
-
-} // end namespace android
-
-#endif /*SOUNDPOOLTHREAD_H_*/
diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp
new file mode 100644
index 000000000000..e7d4d9093bb8
--- /dev/null
+++ b/media/jni/soundpool/Stream.cpp
@@ -0,0 +1,448 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoundPool::Stream"
+#include <utils/Log.h>
+
+#include "Stream.h"
+
+#include "StreamManager.h"
+
+namespace android::soundpool {
+
+Stream::~Stream()
+{
+ ALOGV("%s(%p)", __func__, this);
+}
+
+void Stream::autoPause()
+{
+ std::lock_guard lock(mLock);
+ if (mState == PLAYING) {
+ ALOGV("%s: track streamID: %d", __func__, (int)mStreamID);
+ mState = PAUSED;
+ mAutoPaused = true;
+ if (mAudioTrack != nullptr) {
+ mAudioTrack->pause();
+ }
+ }
+}
+
+void Stream::autoResume()
+{
+ std::lock_guard lock(mLock);
+ if (mAutoPaused) {
+ if (mState == PAUSED) {
+ ALOGV("%s: track streamID: %d", __func__, (int)mStreamID);
+ mState = PLAYING;
+ if (mAudioTrack != nullptr) {
+ mAudioTrack->start();
+ }
+ }
+ mAutoPaused = false; // New for R: always reset autopause (consistent with API spec).
+ }
+}
+
+void Stream::mute(bool muting)
+{
+ std::lock_guard lock(mLock);
+ mMuted = muting;
+ if (mAudioTrack != nullptr) {
+ if (mMuted) {
+ mAudioTrack->setVolume(0.0f, 0.0f);
+ } else {
+ mAudioTrack->setVolume(mLeftVolume, mRightVolume);
+ }
+ }
+}
+
+void Stream::pause(int32_t streamID)
+{
+ std::lock_guard lock(mLock);
+ if (streamID == mStreamID) {
+ if (mState == PLAYING) {
+ ALOGV("%s: track streamID: %d", __func__, streamID);
+ mState = PAUSED;
+ if (mAudioTrack != nullptr) {
+ mAudioTrack->pause();
+ }
+ }
+ }
+}
+
+void Stream::resume(int32_t streamID)
+{
+ std::lock_guard lock(mLock);
+ if (streamID == mStreamID) {
+ if (mState == PAUSED) {
+ ALOGV("%s: track streamID: %d", __func__, streamID);
+ mState = PLAYING;
+ if (mAudioTrack != nullptr) {
+ mAudioTrack->start();
+ }
+ mAutoPaused = false; // TODO: is this right? (ambiguous per spec), move outside?
+ }
+ }
+}
+
+void Stream::setRate(int32_t streamID, float rate)
+{
+ std::lock_guard lock(mLock);
+ if (streamID == mStreamID) {
+ mRate = rate;
+ if (mAudioTrack != nullptr && mSound != nullptr) {
+ const uint32_t sampleRate = uint32_t(float(mSound->getSampleRate()) * rate + 0.5);
+ mAudioTrack->setSampleRate(sampleRate);
+ }
+ }
+}
+
+void Stream::setVolume_l(float leftVolume, float rightVolume)
+{
+ mLeftVolume = leftVolume;
+ mRightVolume = rightVolume;
+ if (mAudioTrack != nullptr && !mMuted) {
+ mAudioTrack->setVolume(leftVolume, rightVolume);
+ }
+}
+
+void Stream::setVolume(int32_t streamID, float leftVolume, float rightVolume)
+{
+ std::lock_guard lock(mLock);
+ if (streamID == mStreamID) {
+ setVolume_l(leftVolume, rightVolume);
+ }
+}
+
+void Stream::setPriority(int32_t streamID, int32_t priority)
+{
+ std::lock_guard lock(mLock);
+ if (streamID == mStreamID) {
+ mPriority = priority;
+ }
+}
+
+void Stream::setLoop(int32_t streamID, int32_t loop)
+{
+ std::lock_guard lock(mLock);
+ if (streamID == mStreamID) {
+ if (mAudioTrack != nullptr && mSound != nullptr) {
+ const uint32_t loopEnd = mSound->getSizeInBytes() / mSound->getChannelCount() /
+ (mSound->getFormat() == AUDIO_FORMAT_PCM_16_BIT
+ ? sizeof(int16_t) : sizeof(uint8_t));
+ mAudioTrack->setLoop(0, loopEnd, loop);
+ }
+ mLoop = loop;
+ }
+}
+
+void Stream::setPlay(
+ int32_t streamID, const std::shared_ptr<Sound> &sound, int32_t soundID,
+ float leftVolume, float rightVolume, int32_t priority, int32_t loop, float rate)
+{
+ std::lock_guard lock(mLock);
+ // We must be idle, or we must be repurposing a pending Stream.
+ LOG_ALWAYS_FATAL_IF(mState != IDLE && mAudioTrack != nullptr, "State %d must be IDLE", mState);
+ mSound = sound;
+ mSoundID = soundID;
+ mLeftVolume = leftVolume;
+ mRightVolume = rightVolume;
+ mPriority = priority;
+ mLoop = loop;
+ mRate = rate;
+ mState = PLAYING;
+ mAutoPaused = false; // New for R (consistent with Java API spec).
+ mStreamID = streamID; // prefer this to be the last, as it is an atomic sync point
+}
+
+void Stream::setStopTimeNs(int64_t stopTimeNs)
+{
+ std::lock_guard lock(mLock);
+ mStopTimeNs = stopTimeNs;
+}
+
+bool Stream::requestStop(int32_t streamID)
+{
+ std::lock_guard lock(mLock);
+ if (streamID == mStreamID) {
+ if (mAudioTrack != nullptr) {
+ if (mState == PLAYING && !mMuted && (mLeftVolume != 0.f || mRightVolume != 0.f)) {
+ setVolume_l(0.f, 0.f);
+ mStopTimeNs = systemTime() + kStopWaitTimeNs;
+ } else {
+ mStopTimeNs = systemTime();
+ }
+ return true; // must be queued on the restart list.
+ }
+ stop_l();
+ }
+ return false;
+}
+
+void Stream::stop()
+{
+ std::lock_guard lock(mLock);
+ stop_l();
+}
+
+void Stream::stop_l()
+{
+ if (mState != IDLE) {
+ if (mAudioTrack != nullptr) {
+ mAudioTrack->stop();
+ }
+ mSound.reset();
+ mState = IDLE;
+ }
+}
+
+void Stream::clearAudioTrack()
+{
+ // This will invoke the destructor which waits for the AudioTrack thread to join,
+ // and is currently the only safe way to ensure there are no callbacks afterwards.
+ mAudioTrack.clear();
+}
+
+Stream* Stream::getPairStream() const
+{
+ return mStreamManager->getPairStream(this);
+}
+
+Stream* Stream::playPairStream() {
+ Stream* pairStream = getPairStream();
+ LOG_ALWAYS_FATAL_IF(pairStream == nullptr, "No pair stream!");
+ sp<AudioTrack> releaseTracks[2];
+ {
+ // TODO: Do we really want to force a simultaneous synchronization between
+ // the stream and its pair?
+
+ // note locking order - the paired stream is obtained before the queued stream.
+ // we can invert the locking order, but it is slightly more optimal to do it this way.
+ std::lock_guard lockp(pairStream->mLock);
+ if (pairStream->mSound == nullptr) {
+ return nullptr; // no pair sound
+ }
+ {
+ std::lock_guard lock(mLock);
+ LOG_ALWAYS_FATAL_IF(mState != IDLE, "State: %d must be IDLE", mState);
+ // TODO: do we want a specific set() here?
+ pairStream->mAudioTrack = mAudioTrack;
+ pairStream->mSoundID = mSoundID; // optimization to reuse AudioTrack.
+ pairStream->mToggle = mToggle;
+ pairStream->mAutoPaused = mAutoPaused; // save autopause state
+ pairStream->mMuted = mMuted;
+ mAudioTrack.clear(); // the pair owns the audiotrack.
+ mSound.reset();
+ mSoundID = 0;
+ }
+ // TODO: do we need a specific play_l() anymore?
+ const int pairState = pairStream->mState;
+ pairStream->play_l(pairStream->mSound, pairStream->mStreamID,
+ pairStream->mLeftVolume, pairStream->mRightVolume, pairStream->mPriority,
+ pairStream->mLoop, pairStream->mRate, releaseTracks);
+ if (pairStream->mState == IDLE) {
+ return nullptr; // AudioTrack error
+ }
+ if (pairState == PAUSED) { // reestablish pause
+ pairStream->mState = PAUSED;
+ pairStream->mAudioTrack->pause();
+ }
+ }
+ // release tracks outside of Stream lock
+ return pairStream;
+}
+
+void Stream::play_l(const std::shared_ptr<Sound>& sound, int32_t nextStreamID,
+ float leftVolume, float rightVolume, int32_t priority, int32_t loop, float rate,
+ sp<AudioTrack> releaseTracks[2])
+{
+ // These tracks are released without the lock.
+ sp<AudioTrack> &oldTrack = releaseTracks[0];
+ sp<AudioTrack> &newTrack = releaseTracks[1];
+ status_t status = NO_ERROR;
+
+ {
+ ALOGV("%s(%p)(soundID=%d, streamID=%d, leftVolume=%f, rightVolume=%f,"
+ " priority=%d, loop=%d, rate=%f)",
+ __func__, this, sound->getSoundID(), nextStreamID, leftVolume, rightVolume,
+ priority, loop, rate);
+
+ // initialize track
+ const audio_stream_type_t streamType =
+ AudioSystem::attributesToStreamType(*mStreamManager->getAttributes());
+ const int32_t channelCount = sound->getChannelCount();
+ const uint32_t sampleRate = uint32_t(float(sound->getSampleRate()) * rate + 0.5);
+ size_t frameCount = 0;
+
+ if (loop) {
+ const audio_format_t format = sound->getFormat();
+ const size_t frameSize = audio_is_linear_pcm(format)
+ ? channelCount * audio_bytes_per_sample(format) : 1;
+ frameCount = sound->getSizeInBytes() / frameSize;
+ }
+
+ // check if the existing track has the same sound id.
+ if (mAudioTrack != nullptr && mSoundID == sound->getSoundID()) {
+ // the sample rate may fail to change if the audio track is a fast track.
+ if (mAudioTrack->setSampleRate(sampleRate) == NO_ERROR) {
+ newTrack = mAudioTrack;
+ ALOGV("%s: reusing track %p for sound %d",
+ __func__, mAudioTrack.get(), sound->getSoundID());
+ }
+ }
+ if (newTrack == 0) {
+ // mToggle toggles each time a track is started on a given stream.
+ // The toggle is concatenated with the Stream address and passed to AudioTrack
+ // as callback user data. This enables the detection of callbacks received from the old
+ // audio track while the new one is being started and avoids processing them with
+ // wrong audio audio buffer size (mAudioBufferSize)
+ auto toggle = mToggle ^ 1;
+ void* userData = (void*)((uintptr_t)this | toggle);
+ audio_channel_mask_t soundChannelMask = sound->getChannelMask();
+ // When sound contains a valid channel mask, use it as is.
+ // Otherwise, use stream count to calculate channel mask.
+ audio_channel_mask_t channelMask = soundChannelMask != AUDIO_CHANNEL_NONE
+ ? soundChannelMask : audio_channel_out_mask_from_count(channelCount);
+
+ // do not create a new audio track if current track is compatible with sound parameters
+
+ newTrack = new AudioTrack(streamType, sampleRate, sound->getFormat(),
+ channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_FAST,
+ staticCallback, userData,
+ 0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE,
+ AudioTrack::TRANSFER_DEFAULT,
+ nullptr /*offloadInfo*/, -1 /*uid*/, -1 /*pid*/,
+ mStreamManager->getAttributes());
+
+ oldTrack = mAudioTrack;
+ status = newTrack->initCheck();
+ if (status != NO_ERROR) {
+ ALOGE("%s: error creating AudioTrack", __func__);
+ // newTrack goes out of scope, so reference count drops to zero
+ goto exit;
+ }
+ // From now on, AudioTrack callbacks received with previous toggle value will be ignored.
+ mToggle = toggle;
+ mAudioTrack = newTrack;
+ ALOGV("%s: using new track %p for sound %d",
+ __func__, newTrack.get(), sound->getSoundID());
+ }
+ if (mMuted) {
+ newTrack->setVolume(0.0f, 0.0f);
+ } else {
+ newTrack->setVolume(leftVolume, rightVolume);
+ }
+ newTrack->setLoop(0, frameCount, loop);
+ mAudioTrack->start();
+ mSound = sound;
+ mSoundID = sound->getSoundID();
+ mPriority = priority;
+ mLoop = loop;
+ mLeftVolume = leftVolume;
+ mRightVolume = rightVolume;
+ mRate = rate;
+ mState = PLAYING;
+ mStopTimeNs = 0;
+ mStreamID = nextStreamID; // prefer this to be the last, as it is an atomic sync point
+ }
+
+exit:
+ ALOGV("%s: delete oldTrack %p", __func__, oldTrack.get());
+ if (status != NO_ERROR) {
+ // TODO: should we consider keeping the soundID if the old track is OK?
+ // Do not attempt to restart this track (should we remove the stream id?)
+ mState = IDLE;
+ mSoundID = 0;
+ mSound.reset();
+ mAudioTrack.clear(); // actual release from releaseTracks[]
+ }
+}
+
+/* static */
+void Stream::staticCallback(int event, void* user, void* info)
+{
+ const uintptr_t userAsInt = (uintptr_t)user;
+ Stream* stream = reinterpret_cast<Stream*>(userAsInt & ~1);
+ stream->callback(event, info, userAsInt & 1, 0 /* tries */);
+}
+
+void Stream::callback(int event, void* info, int toggle, int tries)
+{
+ ALOGV("%s streamID %d", __func__, (int)mStreamID);
+ int32_t activeStreamIDToRestart = 0;
+ {
+ std::unique_lock lock(mLock);
+
+ if (mAudioTrack == nullptr) {
+ // The AudioTrack is either with this stream or its pair.
+ // if this swaps a few times, the toggle is bound to be wrong, so we fail then.
+ //
+ // TODO: Modify AudioTrack callbacks to avoid the hacky toggle and retry
+ // logic here.
+ if (tries < 3) {
+ lock.unlock();
+ getPairStream()->callback(event, info, toggle, tries + 1);
+ } else {
+ ALOGW("%s streamID %d cannot find track", __func__, (int)mStreamID);
+ }
+ return;
+ }
+ if (mToggle != toggle) {
+ ALOGD("%s streamID %d wrong toggle", __func__, (int)mStreamID);
+ return;
+ }
+ switch (event) {
+ case AudioTrack::EVENT_MORE_DATA:
+ ALOGW("%s streamID %d Invalid EVENT_MORE_DATA for static track",
+ __func__, (int)mStreamID);
+ break;
+ case AudioTrack::EVENT_UNDERRUN:
+ ALOGW("%s streamID %d Invalid EVENT_UNDERRUN for static track",
+ __func__, (int)mStreamID);
+ break;
+ case AudioTrack::EVENT_BUFFER_END:
+ ALOGV("%s streamID %d EVENT_BUFFER_END", __func__, (int)mStreamID);
+ if (mState != IDLE) {
+ activeStreamIDToRestart = mStreamID;
+ mStopTimeNs = systemTime();
+ }
+ break;
+ case AudioTrack::EVENT_LOOP_END:
+ ALOGV("%s streamID %d EVENT_LOOP_END", __func__, (int)mStreamID);
+ break;
+ case AudioTrack::EVENT_NEW_IAUDIOTRACK:
+ ALOGV("%s streamID %d NEW_IAUDIOTRACK", __func__, (int)mStreamID);
+ break;
+ default:
+ ALOGW("%s streamID %d Invalid event %d", __func__, (int)mStreamID, event);
+ break;
+ }
+ } // lock ends here. This is on the callback thread, no need to be precise.
+ if (activeStreamIDToRestart > 0) {
+ // Restart only if a particular streamID is still current and active.
+ ALOGV("%s: moveToRestartQueue %d", __func__, activeStreamIDToRestart);
+ mStreamManager->moveToRestartQueue(this, activeStreamIDToRestart);
+ }
+}
+
+void Stream::dump() const
+{
+ ALOGV("mPairStream=%p, mState=%d, mStreamID=%d, mSoundID=%d, mPriority=%d, mLoop=%d",
+ getPairStream(), mState, (int)mStreamID, mSoundID, mPriority, mLoop);
+}
+
+} // namespace android::soundpool
diff --git a/media/jni/soundpool/Stream.h b/media/jni/soundpool/Stream.h
new file mode 100644
index 000000000000..82d2690e2965
--- /dev/null
+++ b/media/jni/soundpool/Stream.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Sound.h"
+
+#include <audio_utils/clock.h>
+#include <media/AudioTrack.h>
+
+namespace android::soundpool {
+
+// This is the amount of time to wait after stop is called when stealing an
+// AudioTrack to allow the sound to ramp down. If this is 0, glitches
+// may occur when stealing an AudioTrack.
+inline constexpr int64_t kStopWaitTimeNs = 20 * NANOS_PER_MILLISECOND;
+
+inline constexpr size_t kCacheLineSize = 64; /* std::hardware_constructive_interference_size */
+
+class StreamManager; // forward decl
+
+/**
+ * A Stream is associated with a StreamID exposed to the app to play a Sound.
+ *
+ * The Stream uses monitor locking strategy on mLock.
+ * https://en.wikipedia.org/wiki/Monitor_(synchronization)
+ *
+ * where public methods are guarded by a lock (as needed)
+ *
+ * For Java equivalent APIs, see
+ * https://developer.android.com/reference/android/media/SoundPool
+ *
+ * Streams are paired by the StreamManager, so one stream in the pair may be "stopping"
+ * while the other stream of the pair has been prepared to run
+ * (and the streamID returned to the app) pending its pair to be stopped.
+ * The pair of a Stream may be obtained by calling getPairStream(),
+ * where this->getPairStream()->getPairStream() == this; (pair is a commutative relationship).
+ *
+ * playPairStream() and getPairPriority() access the paired stream.
+ * See also StreamManager.h for details of physical layout implications of paired streams.
+ */
+class alignas(kCacheLineSize) Stream {
+public:
+ enum state { IDLE, PAUSED, PLAYING };
+ // The PAUSED, PLAYING state directly corresponds to the AudioTrack state of an active Stream.
+ //
+ // The IDLE state indicates an inactive Stream. An IDLE Stream may have a non-nullptr
+ // AudioTrack, which may be recycled for use if the SoundID matches the next Stream playback.
+ //
+ // PAUSED -> PLAYING through resume() (see also autoResume())
+ // PLAYING -> PAUSED through pause() (see also autoPause())
+ //
+ // IDLE is the initial state of a Stream and also when a stream becomes inactive.
+ // {PAUSED, PLAYING} -> IDLE through stop() (or if the Sound finishes playing)
+ // IDLE -> PLAYING through play(). (there is no way to start a Stream in paused mode).
+
+ ~Stream();
+ void setStreamManager(StreamManager* streamManager) { // non-nullptr
+ mStreamManager = streamManager; // set in StreamManager constructor, not changed
+ }
+
+ // The following methods are monitor locked by mLock.
+ //
+ // For methods taking a streamID:
+ // if the streamID matches the Stream's mStreamID, then method proceeds
+ // else the command is ignored with no effect.
+
+ // returns true if the stream needs to be explicitly stopped.
+ bool requestStop(int32_t streamID);
+ void stop(); // explicit stop(), typically called from the worker thread.
+ void clearAudioTrack();
+ void pause(int32_t streamID);
+ void autoPause(); // see the Java SoundPool.autoPause documentation for details.
+ void resume(int32_t streamID);
+ void autoResume();
+ void mute(bool muting);
+ void dump() const;
+
+ // returns the pair stream if successful, nullptr otherwise
+ Stream* playPairStream();
+
+ // These parameters are explicitly checked in the SoundPool class
+ // so never deviate from the Java API specified values.
+ void setVolume(int32_t streamID, float leftVolume, float rightVolume);
+ void setRate(int32_t streamID, float rate);
+ void setPriority(int32_t streamID, int priority);
+ void setLoop(int32_t streamID, int loop);
+ void setPlay(int32_t streamID, const std::shared_ptr<Sound> &sound, int32_t soundID,
+ float leftVolume, float rightVolume, int32_t priority, int32_t loop, float rate);
+ void setStopTimeNs(int64_t stopTimeNs); // systemTime() clock monotonic.
+
+ // The following getters are not locked and have weak consistency.
+ // These are considered advisory only - being stale is of nuisance.
+ int32_t getPriority() const { return mPriority; }
+ int32_t getPairPriority() const { return getPairStream()->getPriority(); }
+ int64_t getStopTimeNs() const { return mStopTimeNs; }
+
+ int32_t getStreamID() const { return mStreamID; } // Can change with setPlay()
+ int32_t getSoundID() const { return mSoundID; } // Can change with play_l()
+ bool hasSound() const { return mSound.get() != nullptr; }
+
+ Stream* getPairStream() const; // this never changes. See top of header.
+
+private:
+ void play_l(const std::shared_ptr<Sound>& sound, int streamID,
+ float leftVolume, float rightVolume, int priority, int loop, float rate,
+ sp<AudioTrack> releaseTracks[2]);
+ void stop_l();
+ void setVolume_l(float leftVolume, float rightVolume);
+
+ // For use with AudioTrack callback.
+ static void staticCallback(int event, void* user, void* info);
+ void callback(int event, void* info, int toggle, int tries);
+
+ // StreamManager should be set on construction and not changed.
+ // release mLock before calling into StreamManager
+ StreamManager* mStreamManager = nullptr;
+
+ mutable std::mutex mLock;
+ std::atomic_int32_t mStreamID = 0; // Note: valid streamIDs are always positive.
+ int mState = IDLE;
+ std::shared_ptr<Sound> mSound; // Non-null if playing.
+ int32_t mSoundID = 0; // The sound ID associated with the AudioTrack.
+ float mLeftVolume = 0.f;
+ float mRightVolume = 0.f;
+ int32_t mPriority = INT32_MIN;
+ int32_t mLoop = 0;
+ float mRate = 0.f;
+ bool mAutoPaused = false;
+ bool mMuted = false;
+
+ sp<AudioTrack> mAudioTrack;
+ int mToggle = 0;
+ int64_t mStopTimeNs = 0; // if nonzero, time to wait for stop.
+};
+
+} // namespace android::soundpool
diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp
new file mode 100644
index 000000000000..8928c47cf4bb
--- /dev/null
+++ b/media/jni/soundpool/StreamManager.cpp
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoundPool::StreamManager"
+#include <utils/Log.h>
+
+#include "StreamManager.h"
+
+#include <audio_utils/clock.h>
+#include <audio_utils/roundup.h>
+
+namespace android::soundpool {
+
+// kMaxStreams is number that should be less than the current AudioTrack max per UID of 40.
+// It is the maximum number of AudioTrack resources allowed in the SoundPool.
+// We suggest a value at least 4 or greater to allow CTS tests to pass.
+static constexpr int32_t kMaxStreams = 32;
+
+// kStealActiveStream_OldestFirst = false historically (Q and earlier)
+// Changing to true could break app expectations but could change behavior beneficially.
+// In R, we change this to true, as it is the correct way per SoundPool documentation.
+static constexpr bool kStealActiveStream_OldestFirst = true;
+
+// kPlayOnCallingThread = true prior to R.
+// Changing to false means calls to play() are almost instantaneous instead of taking around
+// ~10ms to launch the AudioTrack. It is perhaps 100x faster.
+static constexpr bool kPlayOnCallingThread = false;
+
+// Amount of time for a StreamManager thread to wait before closing.
+static constexpr int64_t kWaitTimeBeforeCloseNs = 9 * NANOS_PER_SECOND;
+
+////////////
+
+StreamMap::StreamMap(int32_t streams) {
+ ALOGV("%s(%d)", __func__, streams);
+ if (streams > kMaxStreams) {
+ ALOGW("%s: requested %d streams, clamping to %d", __func__, streams, kMaxStreams);
+ streams = kMaxStreams;
+ } else if (streams < 1) {
+ ALOGW("%s: requested %d streams, clamping to 1", __func__, streams);
+ streams = 1;
+ }
+ mStreamPoolSize = streams * 2;
+ mStreamPool.reset(new Stream[mStreamPoolSize]);
+ // we use a perfect hash table with 2x size to map StreamIDs to Stream pointers.
+ mPerfectHash = std::make_unique<PerfectHash<int32_t, Stream *>>(roundup(mStreamPoolSize * 2));
+}
+
+Stream* StreamMap::findStream(int32_t streamID) const
+{
+ Stream *stream = lookupStreamFromId(streamID);
+ return stream != nullptr && stream->getStreamID() == streamID ? stream : nullptr;
+}
+
+size_t StreamMap::streamPosition(const Stream* stream) const
+{
+ ptrdiff_t index = stream - mStreamPool.get();
+ LOG_ALWAYS_FATAL_IF(index < 0 || index >= mStreamPoolSize,
+ "%s: stream position out of range: %td", __func__, index);
+ return (size_t)index;
+}
+
+Stream* StreamMap::lookupStreamFromId(int32_t streamID) const
+{
+ return streamID > 0 ? mPerfectHash->getValue(streamID).load() : nullptr;
+}
+
+int32_t StreamMap::getNextIdForStream(Stream* stream) const {
+ // even though it is const, it mutates the internal hash table.
+ const int32_t id = mPerfectHash->generateKey(
+ stream,
+ [] (Stream *stream) {
+ return stream == nullptr ? 0 : stream->getStreamID();
+ }, /* getKforV() */
+ stream->getStreamID() /* oldID */);
+ return id;
+}
+
+////////////
+
+StreamManager::StreamManager(
+ int32_t streams, size_t threads, const audio_attributes_t* attributes)
+ : StreamMap(streams)
+ , mAttributes(*attributes)
+{
+ ALOGV("%s(%d, %zu, ...)", __func__, streams, threads);
+ forEach([this](Stream *stream) {
+ stream->setStreamManager(this);
+ if ((streamPosition(stream) & 1) == 0) { // put the first stream of pair as available.
+ mAvailableStreams.insert(stream);
+ }
+ });
+
+ mThreadPool = std::make_unique<ThreadPool>(
+ std::min(threads, (size_t)std::thread::hardware_concurrency()),
+ "SoundPool_");
+}
+
+StreamManager::~StreamManager()
+{
+ ALOGV("%s", __func__);
+ {
+ std::unique_lock lock(mStreamManagerLock);
+ mQuit = true;
+ mStreamManagerCondition.notify_all();
+ }
+ mThreadPool->quit();
+
+ // call stop on the stream pool
+ forEach([](Stream *stream) { stream->stop(); });
+
+ // This invokes the destructor on the AudioTracks -
+ // we do it here to ensure that AudioTrack callbacks will not occur
+ // afterwards.
+ forEach([](Stream *stream) { stream->clearAudioTrack(); });
+}
+
+
+int32_t StreamManager::queueForPlay(const std::shared_ptr<Sound> &sound,
+ int32_t soundID, float leftVolume, float rightVolume,
+ int32_t priority, int32_t loop, float rate)
+{
+ ALOGV("%s(sound=%p, soundID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f)",
+ __func__, sound.get(), soundID, leftVolume, rightVolume, priority, loop, rate);
+ bool launchThread = false;
+ int32_t streamID = 0;
+
+ { // for lock
+ std::unique_lock lock(mStreamManagerLock);
+ Stream *newStream = nullptr;
+ bool fromAvailableQueue = false;
+ ALOGV("%s: mStreamManagerLock lock acquired", __func__);
+
+ sanityCheckQueue_l();
+ // find an available stream, prefer one that has matching sound id.
+ if (mAvailableStreams.size() > 0) {
+ newStream = *mAvailableStreams.begin();
+ for (auto stream : mAvailableStreams) {
+ if (stream->getSoundID() == soundID) {
+ newStream = stream;
+ break;
+ }
+ }
+ if (newStream != nullptr) {
+ newStream->setStopTimeNs(systemTime());
+ }
+ fromAvailableQueue = true;
+ }
+
+ // also look in the streams restarting (if the paired stream doesn't have a pending play)
+ if (newStream == nullptr || newStream->getSoundID() != soundID) {
+ for (auto [unused , stream] : mRestartStreams) {
+ if (!stream->getPairStream()->hasSound()) {
+ if (stream->getSoundID() == soundID) {
+ newStream = stream;
+ break;
+ } else if (newStream == nullptr) {
+ newStream = stream;
+ }
+ }
+ }
+ }
+
+ // no available streams, look for one to steal from the active list
+ if (newStream == nullptr) {
+ for (auto stream : mActiveStreams) {
+ if (stream->getPriority() <= priority) {
+ if (newStream == nullptr
+ || newStream->getPriority() > stream->getPriority()) {
+ newStream = stream;
+ }
+ }
+ }
+ if (newStream != nullptr) { // we need to mute as it is still playing.
+ (void)newStream->requestStop(newStream->getStreamID());
+ }
+ }
+
+ // none found, look for a stream that is restarting, evict one.
+ if (newStream == nullptr) {
+ for (auto [unused, stream] : mRestartStreams) {
+ if (stream->getPairPriority() <= priority) {
+ newStream = stream;
+ break;
+ }
+ }
+ }
+
+ // DO NOT LOOK into mProcessingStreams as those are held by the StreamManager threads.
+
+ if (newStream == nullptr) {
+ ALOGD("%s: unable to find stream, returning 0", __func__);
+ return 0; // unable to find available stream
+ }
+
+ Stream *pairStream = newStream->getPairStream();
+ streamID = getNextIdForStream(pairStream);
+ pairStream->setPlay(
+ streamID, sound, soundID, leftVolume, rightVolume, priority, loop, rate);
+ if (fromAvailableQueue && kPlayOnCallingThread) {
+ removeFromQueues_l(newStream);
+ mProcessingStreams.emplace(newStream);
+ lock.unlock();
+ if (Stream* nextStream = newStream->playPairStream()) {
+ lock.lock();
+ ALOGV("%s: starting streamID:%d", __func__, nextStream->getStreamID());
+ addToActiveQueue_l(nextStream);
+ } else {
+ lock.lock();
+ mAvailableStreams.insert(newStream);
+ streamID = 0;
+ }
+ mProcessingStreams.erase(newStream);
+ } else {
+ launchThread = moveToRestartQueue_l(newStream) && needMoreThreads_l();
+ }
+ sanityCheckQueue_l();
+ ALOGV("%s: mStreamManagerLock released", __func__);
+ } // lock
+
+ if (launchThread) {
+ const int32_t id __unused = mThreadPool->launch([this](int32_t id) { run(id); });
+ ALOGV_IF(id != 0, "%s: launched thread %d", __func__, id);
+ }
+ ALOGV("%s: returning %d", __func__, streamID);
+ return streamID;
+}
+
+void StreamManager::moveToRestartQueue(
+ Stream* stream, int32_t activeStreamIDToMatch)
+{
+ ALOGV("%s(stream(ID)=%d, activeStreamIDToMatch=%d)",
+ __func__, stream->getStreamID(), activeStreamIDToMatch);
+ bool restart;
+ {
+ std::lock_guard lock(mStreamManagerLock);
+ sanityCheckQueue_l();
+ if (mProcessingStreams.count(stream) > 0 ||
+ mProcessingStreams.count(stream->getPairStream()) > 0) {
+ ALOGD("%s: attempting to restart processing stream(%d)",
+ __func__, stream->getStreamID());
+ restart = false;
+ } else {
+ moveToRestartQueue_l(stream, activeStreamIDToMatch);
+ restart = needMoreThreads_l();
+ }
+ sanityCheckQueue_l();
+ }
+ if (restart) {
+ const int32_t id __unused = mThreadPool->launch([this](int32_t id) { run(id); });
+ ALOGV_IF(id != 0, "%s: launched thread %d", __func__, id);
+ }
+}
+
+bool StreamManager::moveToRestartQueue_l(
+ Stream* stream, int32_t activeStreamIDToMatch)
+{
+ ALOGV("%s(stream(ID)=%d, activeStreamIDToMatch=%d)",
+ __func__, stream->getStreamID(), activeStreamIDToMatch);
+ if (activeStreamIDToMatch > 0 && stream->getStreamID() != activeStreamIDToMatch) {
+ return false;
+ }
+ const ssize_t found = removeFromQueues_l(stream, activeStreamIDToMatch);
+ if (found < 0) return false;
+
+ LOG_ALWAYS_FATAL_IF(found > 1, "stream on %zd > 1 stream lists", found);
+
+ addToRestartQueue_l(stream);
+ mStreamManagerCondition.notify_one();
+ return true;
+}
+
+ssize_t StreamManager::removeFromQueues_l(
+ Stream* stream, int32_t activeStreamIDToMatch) {
+ size_t found = 0;
+ for (auto it = mActiveStreams.begin(); it != mActiveStreams.end(); ++it) {
+ if (*it == stream) {
+ mActiveStreams.erase(it); // we erase the iterator and break (otherwise it not safe).
+ ++found;
+ break;
+ }
+ }
+ // activeStreamIDToMatch is nonzero indicates we proceed only if found.
+ if (found == 0 && activeStreamIDToMatch > 0) {
+ return -1; // special code: not present on active streams, ignore restart request
+ }
+
+ for (auto it = mRestartStreams.begin(); it != mRestartStreams.end(); ++it) {
+ if (it->second == stream) {
+ mRestartStreams.erase(it);
+ ++found;
+ break;
+ }
+ }
+ found += mAvailableStreams.erase(stream);
+
+ // streams on mProcessingStreams are undergoing processing by the StreamManager thread
+ // and do not participate in normal stream migration.
+ return found;
+}
+
+void StreamManager::addToRestartQueue_l(Stream *stream) {
+ mRestartStreams.emplace(stream->getStopTimeNs(), stream);
+}
+
+void StreamManager::addToActiveQueue_l(Stream *stream) {
+ if (kStealActiveStream_OldestFirst) {
+ mActiveStreams.push_back(stream); // oldest to newest
+ } else {
+ mActiveStreams.push_front(stream); // newest to oldest
+ }
+}
+
+void StreamManager::run(int32_t id)
+{
+ ALOGV("%s(%d) entering", __func__, id);
+ int64_t waitTimeNs = kWaitTimeBeforeCloseNs;
+ std::unique_lock lock(mStreamManagerLock);
+ while (!mQuit) {
+ mStreamManagerCondition.wait_for(
+ lock, std::chrono::duration<int64_t, std::nano>(waitTimeNs));
+ ALOGV("%s(%d) awake", __func__, id);
+
+ sanityCheckQueue_l();
+
+ if (mQuit || (mRestartStreams.empty() && waitTimeNs == kWaitTimeBeforeCloseNs)) {
+ break; // end the thread
+ }
+
+ waitTimeNs = kWaitTimeBeforeCloseNs;
+ while (!mQuit && !mRestartStreams.empty()) {
+ const nsecs_t nowNs = systemTime();
+ auto it = mRestartStreams.begin();
+ Stream* const stream = it->second;
+ const int64_t diffNs = stream->getStopTimeNs() - nowNs;
+ if (diffNs > 0) {
+ waitTimeNs = std::min(waitTimeNs, diffNs);
+ break;
+ }
+ mRestartStreams.erase(it);
+ mProcessingStreams.emplace(stream);
+ lock.unlock();
+ stream->stop();
+ ALOGV("%s(%d) stopping streamID:%d", __func__, id, stream->getStreamID());
+ if (Stream* nextStream = stream->playPairStream()) {
+ ALOGV("%s(%d) starting streamID:%d", __func__, id, nextStream->getStreamID());
+ lock.lock();
+ if (nextStream->getStopTimeNs() > 0) {
+ // the next stream was stopped before we can move it to the active queue.
+ ALOGV("%s(%d) stopping started streamID:%d",
+ __func__, id, nextStream->getStreamID());
+ moveToRestartQueue_l(nextStream);
+ } else {
+ addToActiveQueue_l(nextStream);
+ }
+ } else {
+ lock.lock();
+ mAvailableStreams.insert(stream);
+ }
+ mProcessingStreams.erase(stream);
+ sanityCheckQueue_l();
+ }
+ }
+ ALOGV("%s(%d) exiting", __func__, id);
+}
+
+void StreamManager::dump() const
+{
+ forEach([](const Stream *stream) { stream->dump(); });
+}
+
+void StreamManager::sanityCheckQueue_l() const
+{
+ // We want to preserve the invariant that each stream pair is exactly on one of the queues.
+ const size_t availableStreams = mAvailableStreams.size();
+ const size_t restartStreams = mRestartStreams.size();
+ const size_t activeStreams = mActiveStreams.size();
+ const size_t processingStreams = mProcessingStreams.size();
+ const size_t managedStreams = availableStreams + restartStreams + activeStreams
+ + processingStreams;
+ const size_t totalStreams = getStreamMapSize() >> 1;
+ LOG_ALWAYS_FATAL_IF(managedStreams != totalStreams,
+ "%s: mAvailableStreams:%zu + mRestartStreams:%zu + "
+ "mActiveStreams:%zu + mProcessingStreams:%zu = %zu != total streams %zu",
+ __func__, availableStreams, restartStreams, activeStreams, processingStreams,
+ managedStreams, totalStreams);
+ ALOGV("%s: mAvailableStreams:%zu + mRestartStreams:%zu + "
+ "mActiveStreams:%zu + mProcessingStreams:%zu = %zu (total streams: %zu)",
+ __func__, availableStreams, restartStreams, activeStreams, processingStreams,
+ managedStreams, totalStreams);
+}
+
+} // namespace android::soundpool
diff --git a/media/jni/soundpool/StreamManager.h b/media/jni/soundpool/StreamManager.h
new file mode 100644
index 000000000000..8c98ac992f75
--- /dev/null
+++ b/media/jni/soundpool/StreamManager.h
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Stream.h"
+
+#include <condition_variable>
+#include <future>
+#include <list>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <unordered_set>
+#include <vector>
+
+#include <utils/AndroidThreads.h>
+
+namespace android::soundpool {
+
+// TODO: Move helper classes to a utility file, with separate test.
+
+/**
+ * JavaThread is used like std::thread but for threads that may call the JVM.
+ *
+ * std::thread does not easily attach to the JVM. We need JVM capable threads
+ * from createThreadEtc() since android binder call optimization may attempt to
+ * call back into Java if the SoundPool runs in system server.
+ *
+ *
+ * No locking is required - the member variables are inherently thread-safe.
+ */
+class JavaThread {
+public:
+ JavaThread(std::function<void()> f, const char *name)
+ : mF{std::move(f)} {
+ createThreadEtc(staticFunction, this, name);
+ }
+
+ JavaThread(JavaThread &&) = delete; // uses "this" ptr, not moveable.
+
+ void join() const {
+ mFuture.wait();
+ }
+
+ bool isClosed() const {
+ return mIsClosed;
+ }
+
+private:
+ static int staticFunction(void *data) {
+ JavaThread *jt = static_cast<JavaThread *>(data);
+ jt->mF();
+ jt->mIsClosed = true;
+ jt->mPromise.set_value();
+ return 0;
+ }
+
+ // No locking is provided as these variables are initialized in the constructor
+ // and the members referenced are thread-safe objects.
+ // (mFuture.wait() can block multiple threads.)
+ // Note the order of member variables is reversed for destructor.
+ const std::function<void()> mF;
+ // Used in join() to block until the thread completes.
+ // See https://en.cppreference.com/w/cpp/thread/promise for the void specialization of
+ // promise.
+ std::promise<void> mPromise;
+ std::future<void> mFuture{mPromise.get_future()};
+ std::atomic_bool mIsClosed = false;
+};
+
+/**
+ * The ThreadPool manages thread lifetimes of SoundPool worker threads.
+ *
+ * TODO: the (eventual) goal of ThreadPool is to transparently and cooperatively
+ * maximize CPU utilization while avoiding starvation of other applications.
+ * Some possibilities:
+ *
+ * We should create worker threads when we have SoundPool work and the system is idle.
+ * CPU cycles are "use-it-or-lose-it" when the system is idle.
+ *
+ * We should adjust the priority of worker threads so that the second (and subsequent) worker
+ * threads have lower priority (should we try to promote priority also?).
+ *
+ * We should throttle the spawning of new worker threads, spacing over time, to avoid
+ * creating too many new threads all at once, on initialization.
+ */
+class ThreadPool {
+public:
+ ThreadPool(size_t maxThreadCount, std::string name)
+ : mMaxThreadCount(maxThreadCount)
+ , mName{std::move(name)} { }
+
+ ~ThreadPool() { quit(); }
+
+ size_t getActiveThreadCount() const { return mActiveThreadCount; }
+ size_t getMaxThreadCount() const { return mMaxThreadCount; }
+
+ void quit() {
+ std::list<std::unique_ptr<JavaThread>> threads;
+ {
+ std::lock_guard lock(mThreadLock);
+ if (mQuit) return; // already joined.
+ mQuit = true;
+ threads = std::move(mThreads);
+ mThreads.clear();
+ }
+ // mQuit set under lock, no more threads will be created.
+ for (auto &thread : threads) {
+ thread->join();
+ thread.reset();
+ }
+ LOG_ALWAYS_FATAL_IF(mActiveThreadCount != 0,
+ "Invalid Active Threads: %zu", (size_t)mActiveThreadCount);
+ }
+
+ // returns a non-zero id if successful, the id is to help logging messages.
+ int32_t launch(std::function<void(int32_t /* id */)> f) {
+ std::list<std::unique_ptr<JavaThread>> threadsToRelease; // release outside of lock.
+ std::lock_guard lock(mThreadLock);
+ if (mQuit) return 0; // ignore if we have quit
+
+ // clean up threads.
+ for (auto it = mThreads.begin(); it != mThreads.end(); ) {
+ if ((*it)->isClosed()) {
+ threadsToRelease.emplace_back(std::move(*it));
+ it = mThreads.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ const size_t threadCount = mThreads.size();
+ if (threadCount < mMaxThreadCount) {
+ // if the id wraps, we don't care about collisions. it's just for logging.
+ mNextThreadId = mNextThreadId == INT32_MAX ? 1 : ++mNextThreadId;
+ const int32_t id = mNextThreadId;
+ mThreads.emplace_back(std::make_unique<JavaThread>(
+ [this, id, mf = std::move(f)] { mf(id); --mActiveThreadCount; },
+ (mName + std::to_string(id)).c_str()));
+ ++mActiveThreadCount;
+ return id;
+ }
+ return 0;
+ }
+
+ // TODO: launch only if load average is low.
+ // This gets the load average
+ // See also std::thread::hardware_concurrency() for the concurrent capability.
+ static double getLoadAvg() {
+ double loadAvg[1];
+ if (getloadavg(loadAvg, std::size(loadAvg)) > 0) {
+ return loadAvg[0];
+ }
+ return -1.;
+ }
+
+private:
+ const size_t mMaxThreadCount;
+ const std::string mName;
+
+ std::atomic_size_t mActiveThreadCount = 0;
+
+ std::mutex mThreadLock;
+ bool mQuit = false; // GUARDED_BY(mThreadLock)
+ int32_t mNextThreadId = 0; // GUARDED_BY(mThreadLock)
+ std::list<std::unique_ptr<JavaThread>> mThreads; // GUARDED_BY(mThreadLock)
+};
+
+/**
+ * A Perfect HashTable for IDs (key) to pointers (value).
+ *
+ * There are no collisions. Why? because we generate the IDs for you to look up :-).
+ *
+ * The goal of this hash table is to map an integer ID handle > 0 to a pointer.
+ * We give these IDs in monotonic order (though we may skip if it were to cause a collision).
+ *
+ * The size of the hashtable must be large enough to accommodate the max number of keys.
+ * We suggest 2x.
+ *
+ * Readers are lockless
+ * Single writer could be lockless, but we allow multiple writers through an internal lock.
+ *
+ * For the Key type K, valid keys generated are > 0 (signed or unsigned)
+ * For the Value type V, values are pointers - nullptr means empty.
+ */
+template <typename K, typename V>
+class PerfectHash {
+public:
+ PerfectHash(size_t hashCapacity)
+ : mHashCapacity(hashCapacity)
+ , mK2V{new std::atomic<V>[hashCapacity]()} {
+ }
+
+ // Generate a key for a value V.
+ // There is a testing function getKforV() which checks what the value reports as its key.
+ //
+ // Calls back into getKforV under lock.
+ //
+ // We expect that the hashCapacity is 2x the number of stored keys in order
+ // to have one or two tries to find an empty slot
+ K generateKey(V value, std::function<K(V)> getKforV, K oldKey = 0) {
+ std::lock_guard lock(mHashLock);
+ // try to remove the old key.
+ if (oldKey > 0) { // key valid
+ const V v = getValue(oldKey);
+ if (v != nullptr) { // value still valid
+ const K atPosition = getKforV(v);
+ if (atPosition < 0 || // invalid value
+ atPosition == oldKey || // value's key still valid and matches old key
+ ((atPosition ^ oldKey) & (mHashCapacity - 1)) != 0) { // stale key entry
+ getValue(oldKey) = nullptr; // invalidate
+ }
+ } // else if value is invalid, no need to invalidate.
+ }
+ // check if we are invalidating only.
+ if (value == nullptr) return 0;
+ // now insert the new value and return the key.
+ size_t tries = 0;
+ for (; tries < mHashCapacity; ++tries) {
+ mNextKey = mNextKey == std::numeric_limits<K>::max() ? 1 : mNextKey + 1;
+ const V v = getValue(mNextKey);
+ //ALOGD("tries: %zu, key:%d value:%p", tries, (int)mNextKey, v);
+ if (v == nullptr) break; // empty
+ const K atPosition = getKforV(v);
+ //ALOGD("tries: %zu key atPosition:%d", tries, (int)atPosition);
+ if (atPosition < 0 || // invalid value
+ ((atPosition ^ mNextKey) & (mHashCapacity - 1)) != 0) { // stale key entry
+ break;
+ }
+ }
+ LOG_ALWAYS_FATAL_IF(tries == mHashCapacity, "hash table overflow!");
+ //ALOGD("%s: found after %zu tries", __func__, tries);
+ getValue(mNextKey) = value;
+ return mNextKey;
+ }
+
+ std::atomic<V> &getValue(K key) { return mK2V[key & (mHashCapacity - 1)]; }
+ const std::atomic_int32_t &getValue(K key) const { return mK2V[key & (mHashCapacity - 1)]; }
+
+private:
+ mutable std::mutex mHashLock;
+ const size_t mHashCapacity; // size of mK2V no lock needed.
+ std::unique_ptr<std::atomic<V>[]> mK2V; // no lock needed for read access.
+ K mNextKey{}; // GUARDED_BY(mHashLock)
+};
+
+/**
+ * StreamMap contains the all the valid streams available to SoundPool.
+ *
+ * There is no Lock required for this class because the streams are
+ * allocated in the constructor, the lookup is lockless, and the Streams
+ * returned are locked internally.
+ *
+ * The lookup uses a perfect hash.
+ * It is possible to use a lockless hash table or to use a stripe-locked concurrent
+ * hashmap for essentially lock-free lookup.
+ *
+ * This follows Map-Reduce parallelism model.
+ * https://en.wikipedia.org/wiki/MapReduce
+ *
+ * Conceivably the forEach could be parallelized using std::for_each with a
+ * std::execution::par policy.
+ *
+ * https://en.cppreference.com/w/cpp/algorithm/for_each
+ */
+class StreamMap {
+public:
+ explicit StreamMap(int32_t streams);
+
+ // Returns the stream associated with streamID or nullptr if not found.
+ // This need not be locked.
+ // The stream ID will never migrate to another Stream, but it may change
+ // underneath you. The Stream operations that take a streamID will confirm
+ // that the streamID matches under the Stream lock before executing otherwise
+ // it ignores the command as stale.
+ Stream* findStream(int32_t streamID) const;
+
+ // Iterates through the stream pool applying the function f.
+ // Since this enumerates over every single stream, it is unlocked.
+ //
+ // See related: https://en.cppreference.com/w/cpp/algorithm/for_each
+ void forEach(std::function<void(const Stream *)>f) const {
+ for (size_t i = 0; i < mStreamPoolSize; ++i) {
+ f(&mStreamPool[i]);
+ }
+ }
+
+ void forEach(std::function<void(Stream *)>f) {
+ for (size_t i = 0; i < mStreamPoolSize; ++i) {
+ f(&mStreamPool[i]);
+ }
+ }
+
+ // Returns the pair stream for a given Stream.
+ // This need not be locked as it is a property of the pointer address.
+ Stream* getPairStream(const Stream* stream) const {
+ const size_t index = streamPosition(stream);
+ return &mStreamPool[index ^ 1];
+ }
+
+ // find the position of the stream in mStreamPool array.
+ size_t streamPosition(const Stream* stream) const; // no lock needed
+
+ size_t getStreamMapSize() const {
+ return mStreamPoolSize;
+ }
+
+ // find the next valid ID for a stream and store in hash table.
+ int32_t getNextIdForStream(Stream* stream) const;
+
+private:
+
+ // use the hash table to attempt to find the stream.
+ // nullptr is returned if the lookup fails.
+ Stream* lookupStreamFromId(int32_t streamID) const;
+
+ // The stream pool is initialized in the constructor, effectively const.
+ // no locking required for access.
+ //
+ // The constructor parameter "streams" results in streams pairs of streams.
+ // We have twice as many streams because we wish to return a streamID "handle"
+ // back to the app immediately, while we may be stopping the other stream in the
+ // pair to get its AudioTrack :-).
+ //
+ // Of the stream pair, only one of the streams may have an AudioTrack.
+ // The fixed association of a stream pair allows callbacks from the AudioTrack
+ // to be associated properly to either one or the other of the stream pair.
+ //
+ // TODO: The stream pair arrangement can be removed if we have better AudioTrack
+ // callback handling (being able to remove and change the callback after construction).
+ //
+ // Streams may be accessed anytime off of the stream pool
+ // as there is internal locking on each stream.
+ std::unique_ptr<Stream[]> mStreamPool; // no lock needed for access.
+ size_t mStreamPoolSize; // no lock needed for access.
+
+ // In order to find the Stream from a StreamID, we could do a linear lookup in mStreamPool.
+ // As an alternative, one could use stripe-locked or lock-free concurrent hashtables.
+ //
+ // When considering linear search vs hashmap, verify the typical use-case size.
+ // Linear search is faster than std::unordered_map (circa 2018) for less than 40 elements.
+ // [ Skarupke, M. (2018), "You Can Do Better than std::unordered_map: New and Recent
+ // Improvements to Hash Table Performance." C++Now 2018. cppnow.org, see
+ // https://www.youtube.com/watch?v=M2fKMP47slQ ]
+ //
+ // Here, we use a PerfectHash of Id to Stream *, since we can control the
+ // StreamID returned to the user. This allows O(1) read access to mStreamPool lock-free.
+ //
+ // We prefer that the next stream ID is monotonic for aesthetic reasons
+ // (if we didn't care about monotonicity, a simple method is to apply a generation count
+ // to each stream in the unused upper bits of its index in mStreamPool for the id).
+ //
+ std::unique_ptr<PerfectHash<int32_t, Stream *>> mPerfectHash;
+};
+
+/**
+ * StreamManager is used to manage the streams (accessed by StreamID from Java).
+ *
+ * Locking order (proceeds from application to component).
+ * SoundPool mApiLock (if needed) -> StreamManager mStreamManagerLock
+ * -> pair Stream mLock -> queued Stream mLock
+ */
+class StreamManager : public StreamMap {
+public:
+ // Note: the SoundPool pointer is only used for stream initialization.
+ // It is not stored in StreamManager.
+ StreamManager(int32_t streams, size_t threads, const audio_attributes_t* attributes);
+ ~StreamManager();
+
+ // Returns positive streamID on success, 0 on failure. This is locked.
+ int32_t queueForPlay(const std::shared_ptr<Sound> &sound,
+ int32_t soundID, float leftVolume, float rightVolume,
+ int32_t priority, int32_t loop, float rate);
+
+ ///////////////////////////////////////////////////////////////////////
+ // Called from soundpool::Stream
+
+ const audio_attributes_t* getAttributes() const { return &mAttributes; }
+
+ // Moves the stream to the restart queue (called upon BUFFER_END of the static track)
+ // this is locked internally.
+ // If activeStreamIDToMatch is nonzero, it will only move to the restart queue
+ // if the streamIDToMatch is found on the active queue.
+ void moveToRestartQueue(Stream* stream, int32_t activeStreamIDToMatch = 0);
+
+private:
+
+ void run(int32_t id); // worker thread, takes lock internally.
+ void dump() const; // no lock needed
+
+ // returns true if more worker threads are needed.
+ bool needMoreThreads_l() {
+ return mRestartStreams.size() > 0 &&
+ (mThreadPool->getActiveThreadCount() == 0
+ || std::distance(mRestartStreams.begin(),
+ mRestartStreams.upper_bound(systemTime()))
+ > (ptrdiff_t)mThreadPool->getActiveThreadCount());
+ }
+
+ // returns true if the stream was added.
+ bool moveToRestartQueue_l(Stream* stream, int32_t activeStreamIDToMatch = 0);
+ // returns number of queues the stream was removed from (should be 0 or 1);
+ // a special code of -1 is returned if activeStreamIDToMatch is > 0 and
+ // the stream wasn't found on the active queue.
+ ssize_t removeFromQueues_l(Stream* stream, int32_t activeStreamIDToMatch = 0);
+ void addToRestartQueue_l(Stream *stream);
+ void addToActiveQueue_l(Stream *stream);
+ void sanityCheckQueue_l() const;
+
+ const audio_attributes_t mAttributes;
+ std::unique_ptr<ThreadPool> mThreadPool; // locked internally
+
+ // mStreamManagerLock is used to lock access for transitions between the
+ // 4 stream queues by the Manager Thread or by the user initiated play().
+ // A stream pair has exactly one stream on exactly one of the queues.
+ std::mutex mStreamManagerLock;
+ std::condition_variable mStreamManagerCondition;
+
+ bool mQuit = false; // GUARDED_BY(mStreamManagerLock)
+
+ // There are constructor arg "streams" pairs of streams, only one of each
+ // pair on the 4 stream queues below. The other stream in the pair serves as
+ // placeholder to accumulate user changes, pending actual availability of the
+ // AudioTrack, as it may be in use, requiring stop-then-restart.
+ //
+ // The 4 queues are implemented in the appropriate STL container based on perceived
+ // optimality.
+
+ // 1) mRestartStreams: Streams awaiting stop.
+ // The paired stream may be active (but with no AudioTrack), and will be restarted
+ // with an active AudioTrack when the current stream is stopped.
+ std::multimap<int64_t /* stopTimeNs */, Stream*>
+ mRestartStreams; // GUARDED_BY(mStreamManagerLock)
+
+ // 2) mActiveStreams: Streams that are active.
+ // The paired stream will be inactive.
+ // This is in order of specified by kStealActiveStream_OldestFirst
+ std::list<Stream*> mActiveStreams; // GUARDED_BY(mStreamManagerLock)
+
+ // 3) mAvailableStreams: Streams that are inactive.
+ // The paired stream will also be inactive.
+ // No particular order.
+ std::unordered_set<Stream*> mAvailableStreams; // GUARDED_BY(mStreamManagerLock)
+
+ // 4) mProcessingStreams: Streams that are being processed by the ManagerThreads
+ // When on this queue, the stream and its pair are not available for stealing.
+ // Each ManagerThread will have at most one stream on the mProcessingStreams queue.
+ // The paired stream may be active or restarting.
+ // No particular order.
+ std::unordered_set<Stream*> mProcessingStreams; // GUARDED_BY(mStreamManagerLock)
+};
+
+} // namespace android::soundpool
diff --git a/media/lib/signer/Android.bp b/media/lib/signer/Android.bp
index 2286c5379e1a..6b03e4de57d7 100644
--- a/media/lib/signer/Android.bp
+++ b/media/lib/signer/Android.bp
@@ -16,9 +16,8 @@
java_sdk_library {
name: "com.android.mediadrm.signer",
- srcs: [
- "java/**/*.java",
- ":framework-all-sources",
- ],
+ srcs: ["java/**/*.java"],
+ api_srcs: [":framework-all-sources"],
+ libs: ["framework-all"],
api_packages: ["com.android.mediadrm.signer"],
}
diff --git a/media/native/midi/Android.bp b/media/native/midi/Android.bp
index a0d20506613e..2da45b6a78be 100644
--- a/media/native/midi/Android.bp
+++ b/media/native/midi/Android.bp
@@ -17,6 +17,7 @@ cc_library_shared {
srcs: [
"amidi.cpp",
+ "MidiDeviceInfo.cpp",
":IMidiDeviceServer.aidl",
],
@@ -31,12 +32,14 @@ cc_library_shared {
"-fvisibility=hidden",
],
+ header_libs: [
+ "media_ndk_headers",
+ ],
+
shared_libs: [
"liblog",
"libbinder",
"libutils",
- "libmedia",
- "libmediandk",
"libandroid_runtime",
],
diff --git a/media/native/midi/MidiDeviceInfo.cpp b/media/native/midi/MidiDeviceInfo.cpp
new file mode 100644
index 000000000000..ac68d26c935b
--- /dev/null
+++ b/media/native/midi/MidiDeviceInfo.cpp
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "MidiDeviceInfo"
+
+#include <MidiDeviceInfo.h>
+
+#include <binder/Parcel.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+#include <utils/String16.h>
+
+namespace android {
+namespace media {
+namespace midi {
+
+// The constant values need to be kept in sync with MidiDeviceInfo.java.
+// static
+const char* const MidiDeviceInfo::PROPERTY_NAME = "name";
+const char* const MidiDeviceInfo::PROPERTY_MANUFACTURER = "manufacturer";
+const char* const MidiDeviceInfo::PROPERTY_PRODUCT = "product";
+const char* const MidiDeviceInfo::PROPERTY_VERSION = "version";
+const char* const MidiDeviceInfo::PROPERTY_SERIAL_NUMBER = "serial_number";
+const char* const MidiDeviceInfo::PROPERTY_ALSA_CARD = "alsa_card";
+const char* const MidiDeviceInfo::PROPERTY_ALSA_DEVICE = "alsa_device";
+
+String16 MidiDeviceInfo::getProperty(const char* propertyName) {
+ String16 value;
+ if (mProperties.getString(String16(propertyName), &value)) {
+ return value;
+ } else {
+ return String16();
+ }
+}
+
+#define RETURN_IF_FAILED(calledOnce) \
+ { \
+ status_t returnStatus = calledOnce; \
+ if (returnStatus) { \
+ ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
+ return returnStatus; \
+ } \
+ }
+
+status_t MidiDeviceInfo::writeToParcel(Parcel* parcel) const {
+ // Needs to be kept in sync with code in MidiDeviceInfo.java
+ RETURN_IF_FAILED(parcel->writeInt32(mType));
+ RETURN_IF_FAILED(parcel->writeInt32(mId));
+ RETURN_IF_FAILED(parcel->writeInt32((int32_t)mInputPortNames.size()));
+ RETURN_IF_FAILED(parcel->writeInt32((int32_t)mOutputPortNames.size()));
+ RETURN_IF_FAILED(writeStringVector(parcel, mInputPortNames));
+ RETURN_IF_FAILED(writeStringVector(parcel, mOutputPortNames));
+ RETURN_IF_FAILED(parcel->writeInt32(mIsPrivate ? 1 : 0));
+ RETURN_IF_FAILED(mProperties.writeToParcel(parcel));
+ // This corresponds to "extra" properties written by Java code
+ RETURN_IF_FAILED(mProperties.writeToParcel(parcel));
+ return OK;
+}
+
+status_t MidiDeviceInfo::readFromParcel(const Parcel* parcel) {
+ // Needs to be kept in sync with code in MidiDeviceInfo.java
+ RETURN_IF_FAILED(parcel->readInt32(&mType));
+ RETURN_IF_FAILED(parcel->readInt32(&mId));
+ int32_t inputPortCount;
+ RETURN_IF_FAILED(parcel->readInt32(&inputPortCount));
+ int32_t outputPortCount;
+ RETURN_IF_FAILED(parcel->readInt32(&outputPortCount));
+ RETURN_IF_FAILED(readStringVector(parcel, &mInputPortNames, inputPortCount));
+ RETURN_IF_FAILED(readStringVector(parcel, &mOutputPortNames, outputPortCount));
+ int32_t isPrivate;
+ RETURN_IF_FAILED(parcel->readInt32(&isPrivate));
+ mIsPrivate = isPrivate == 1;
+ RETURN_IF_FAILED(mProperties.readFromParcel(parcel));
+ // Ignore "extra" properties as they may contain Java Parcelables
+ return OK;
+}
+
+status_t MidiDeviceInfo::readStringVector(
+ const Parcel* parcel, Vector<String16> *vectorPtr, size_t defaultLength) {
+ std::unique_ptr<std::vector<std::unique_ptr<String16>>> v;
+ status_t result = parcel->readString16Vector(&v);
+ if (result != OK) return result;
+ vectorPtr->clear();
+ if (v.get() != nullptr) {
+ for (const auto& iter : *v) {
+ if (iter.get() != nullptr) {
+ vectorPtr->push_back(*iter);
+ } else {
+ vectorPtr->push_back(String16());
+ }
+ }
+ } else {
+ vectorPtr->resize(defaultLength);
+ }
+ return OK;
+}
+
+status_t MidiDeviceInfo::writeStringVector(Parcel* parcel, const Vector<String16>& vector) const {
+ std::vector<String16> v;
+ for (size_t i = 0; i < vector.size(); ++i) {
+ v.push_back(vector[i]);
+ }
+ return parcel->writeString16Vector(v);
+}
+
+// Vector does not define operator==
+static inline bool areVectorsEqual(const Vector<String16>& lhs, const Vector<String16>& rhs) {
+ if (lhs.size() != rhs.size()) return false;
+ for (size_t i = 0; i < lhs.size(); ++i) {
+ if (lhs[i] != rhs[i]) return false;
+ }
+ return true;
+}
+
+bool operator==(const MidiDeviceInfo& lhs, const MidiDeviceInfo& rhs) {
+ return (lhs.mType == rhs.mType && lhs.mId == rhs.mId &&
+ areVectorsEqual(lhs.mInputPortNames, rhs.mInputPortNames) &&
+ areVectorsEqual(lhs.mOutputPortNames, rhs.mOutputPortNames) &&
+ lhs.mProperties == rhs.mProperties &&
+ lhs.mIsPrivate == rhs.mIsPrivate);
+}
+
+} // namespace midi
+} // namespace media
+} // namespace android
diff --git a/media/native/midi/MidiDeviceInfo.h b/media/native/midi/MidiDeviceInfo.h
new file mode 100644
index 000000000000..5b4a241323d7
--- /dev/null
+++ b/media/native/midi/MidiDeviceInfo.h
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_MEDIA_MIDI_DEVICE_INFO_H
+#define ANDROID_MEDIA_MIDI_DEVICE_INFO_H
+
+#include <binder/Parcelable.h>
+#include <binder/PersistableBundle.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+namespace android {
+namespace media {
+namespace midi {
+
+class MidiDeviceInfo : public Parcelable {
+public:
+ MidiDeviceInfo() = default;
+ virtual ~MidiDeviceInfo() = default;
+ MidiDeviceInfo(const MidiDeviceInfo& midiDeviceInfo) = default;
+
+ status_t writeToParcel(Parcel* parcel) const override;
+ status_t readFromParcel(const Parcel* parcel) override;
+
+ int getType() const { return mType; }
+ int getUid() const { return mId; }
+ bool isPrivate() const { return mIsPrivate; }
+ const Vector<String16>& getInputPortNames() const { return mInputPortNames; }
+ const Vector<String16>& getOutputPortNames() const { return mOutputPortNames; }
+ String16 getProperty(const char* propertyName);
+
+ // The constants need to be kept in sync with MidiDeviceInfo.java
+ enum {
+ TYPE_USB = 1,
+ TYPE_VIRTUAL = 2,
+ TYPE_BLUETOOTH = 3,
+ };
+ static const char* const PROPERTY_NAME;
+ static const char* const PROPERTY_MANUFACTURER;
+ static const char* const PROPERTY_PRODUCT;
+ static const char* const PROPERTY_VERSION;
+ static const char* const PROPERTY_SERIAL_NUMBER;
+ static const char* const PROPERTY_ALSA_CARD;
+ static const char* const PROPERTY_ALSA_DEVICE;
+
+ friend bool operator==(const MidiDeviceInfo& lhs, const MidiDeviceInfo& rhs);
+ friend bool operator!=(const MidiDeviceInfo& lhs, const MidiDeviceInfo& rhs) {
+ return !(lhs == rhs);
+ }
+
+private:
+ status_t readStringVector(
+ const Parcel* parcel, Vector<String16> *vectorPtr, size_t defaultLength);
+ status_t writeStringVector(Parcel* parcel, const Vector<String16>& vector) const;
+
+ int32_t mType;
+ int32_t mId;
+ Vector<String16> mInputPortNames;
+ Vector<String16> mOutputPortNames;
+ os::PersistableBundle mProperties;
+ bool mIsPrivate;
+};
+
+} // namespace midi
+} // namespace media
+} // namespace android
+
+#endif // ANDROID_MEDIA_MIDI_DEVICE_INFO_H
diff --git a/media/native/midi/amidi.cpp b/media/native/midi/amidi.cpp
index 46f28152cd6d..35c4d42a0aa8 100644
--- a/media/native/midi/amidi.cpp
+++ b/media/native/midi/amidi.cpp
@@ -26,7 +26,7 @@
#include <core_jni_helpers.h>
#include "android/media/midi/BpMidiDeviceServer.h"
-#include "media/MidiDeviceInfo.h"
+#include "MidiDeviceInfo.h"
#include "include/amidi/AMidi.h"
#include "amidi_internal.h"
diff --git a/media/proto/Android.bp b/media/proto/Android.bp
deleted file mode 100644
index 2dc0d579c0da..000000000000
--- a/media/proto/Android.bp
+++ /dev/null
@@ -1,20 +0,0 @@
-java_library_static {
- name: "mediaplayer2-protos",
- host_supported: true,
- proto: {
- type: "lite",
- },
- srcs: ["mediaplayer2.proto"],
- jarjar_rules: "jarjar-rules.txt",
- sdk_version: "28",
-}
-
-cc_library_static {
- name: "libmediaplayer2-protos",
- host_supported: true,
- proto: {
- export_proto_headers: true,
- type: "lite",
- },
- srcs: ["mediaplayer2.proto"],
-}
diff --git a/media/proto/jarjar-rules.txt b/media/proto/jarjar-rules.txt
deleted file mode 100644
index e73f86dddac1..000000000000
--- a/media/proto/jarjar-rules.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-rule com.google.protobuf.** android.media.protobuf.@1
-
diff --git a/media/proto/mediaplayer2.proto b/media/proto/mediaplayer2.proto
deleted file mode 100644
index 6287d6cd326d..000000000000
--- a/media/proto/mediaplayer2.proto
+++ /dev/null
@@ -1,53 +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.
- */
-
-syntax = "proto2";
-
-option optimize_for = LITE_RUNTIME;
-
-// C++ namespace: android::media:MediaPlayer2Proto:
-package android.media.MediaPlayer2Proto;
-
-option java_package = "android.media";
-option java_outer_classname = "MediaPlayer2Proto";
-
-message Value {
- // The kind of value.
- oneof kind {
- // Represents a boolean value.
- bool bool_value = 1;
- // Represents an int32 value.
- int32 int32_value = 2;
- // Represents an uint32 value.
- uint32 uint32_value = 3;
- // Represents an int64 value.
- int64 int64_value = 4;
- // Represents an uint64 value.
- uint64 uint64_value = 5;
- // Represents a float value.
- double float_value = 6;
- // Represents a double value.
- double double_value = 7;
- // Represents a string value.
- string string_value = 8;
- // Represents a bytes value.
- bytes bytes_value = 9;
- }
-}
-
-message PlayerMessage {
- repeated Value values = 1;
-}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaFileTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaFileTest.java
index 38f0175579ad..481f4796951d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaFileTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaFileTest.java
@@ -31,7 +31,7 @@ import android.mtp.MtpConstants;
import androidx.test.runner.AndroidJUnit4;
-import libcore.net.MimeMap;
+import libcore.content.type.MimeMap;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
index 1267aa88a939..f4f8d0b73658 100644
--- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
@@ -26,7 +26,7 @@ import java.util.HashMap;
import java.util.Map;
public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService {
- private static final String TAG = "SampleMediaRoute2Serv";
+ private static final String TAG = "SampleMR2ProviderSvc";
public static final String ROUTE_ID1 = "route_id1";
public static final String ROUTE_NAME1 = "Sample Route 1";
@@ -36,6 +36,12 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
public static final String ROUTE_ID_SPECIAL_CATEGORY = "route_special_category";
public static final String ROUTE_NAME_SPECIAL_CATEGORY = "Special Category Route";
+ public static final int VOLUME_MAX = 100;
+ public static final String ROUTE_ID_FIXED_VOLUME = "route_fixed_volume";
+ public static final String ROUTE_NAME_FIXED_VOLUME = "Fixed Volume Route";
+ public static final String ROUTE_ID_VARIABLE_VOLUME = "route_variable_volume";
+ public static final String ROUTE_NAME_VARIABLE_VOLUME = "Variable Volume Route";
+
public static final String ACTION_REMOVE_ROUTE =
"com.android.mediarouteprovider.action_remove_route";
@@ -58,9 +64,23 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
.addSupportedCategory(CATEGORY_SAMPLE)
.addSupportedCategory(CATEGORY_SPECIAL)
.build();
+ MediaRoute2Info fixedVolumeRoute =
+ new MediaRoute2Info.Builder(ROUTE_ID_FIXED_VOLUME, ROUTE_NAME_FIXED_VOLUME)
+ .addSupportedCategory(CATEGORY_SAMPLE)
+ .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_FIXED)
+ .build();
+ MediaRoute2Info variableVolumeRoute =
+ new MediaRoute2Info.Builder(ROUTE_ID_VARIABLE_VOLUME, ROUTE_NAME_VARIABLE_VOLUME)
+ .addSupportedCategory(CATEGORY_SAMPLE)
+ .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
+ .setVolumeMax(VOLUME_MAX)
+ .build();
+
mRoutes.put(route1.getId(), route1);
mRoutes.put(route2.getId(), route2);
mRoutes.put(routeSpecial.getId(), routeSpecial);
+ mRoutes.put(fixedVolumeRoute.getId(), fixedVolumeRoute);
+ mRoutes.put(variableVolumeRoute.getId(), variableVolumeRoute);
}
@Override
@@ -110,6 +130,33 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
}
}
+ @Override
+ public void onSetVolume(String routeId, int volume) {
+ MediaRoute2Info route = mRoutes.get(routeId);
+ if (route == null) {
+ return;
+ }
+ volume = Math.min(volume, Math.max(0, route.getVolumeMax()));
+ mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
+ .setVolume(volume)
+ .build());
+ publishRoutes();
+ }
+
+ @Override
+ public void onUpdateVolume(String routeId, int delta) {
+ MediaRoute2Info route = mRoutes.get(routeId);
+ if (route == null) {
+ return;
+ }
+ int volume = route.getVolume() + delta;
+ volume = Math.min(volume, Math.max(0, route.getVolumeMax()));
+ mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
+ .setVolume(volume)
+ .build());
+ publishRoutes();
+ }
+
void publishRoutes() {
MediaRoute2ProviderInfo info = new MediaRoute2ProviderInfo.Builder()
.addRoutes(mRoutes.values())
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index 3abf0a4941e6..ca43d04573f3 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -16,6 +16,9 @@
package com.android.mediaroutertest;
+import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
+import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
@@ -27,6 +30,7 @@ import static org.mockito.Mockito.verify;
import android.content.Context;
import android.content.Intent;
import android.media.MediaRoute2Info;
+import android.media.MediaRouter;
import android.media.MediaRouter2;
import android.media.MediaRouter2Manager;
import android.support.test.InstrumentationRegistry;
@@ -43,8 +47,10 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -60,6 +66,12 @@ public class MediaRouterManagerTest {
public static final String ROUTE_ID_SPECIAL_CATEGORY = "route_special_category";
public static final String ROUTE_NAME_SPECIAL_CATEGORY = "Special Category Route";
+ public static final int VOLUME_MAX = 100;
+ public static final String ROUTE_ID_FIXED_VOLUME = "route_fixed_volume";
+ public static final String ROUTE_NAME_FIXED_VOLUME = "Fixed Volume Route";
+ public static final String ROUTE_ID_VARIABLE_VOLUME = "route_variable_volume";
+ public static final String ROUTE_NAME_VARIABLE_VOLUME = "Variable Volume Route";
+
public static final String ACTION_REMOVE_ROUTE =
"com.android.mediarouteprovider.action_remove_route";
@@ -72,7 +84,8 @@ public class MediaRouterManagerTest {
private Context mContext;
private MediaRouter2Manager mManager;
- private MediaRouter2 mRouter;
+ private MediaRouter mRouter;
+ private MediaRouter2 mRouter2;
private Executor mExecutor;
private String mPackageName;
@@ -89,13 +102,14 @@ public class MediaRouterManagerTest {
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
mManager = MediaRouter2Manager.getInstance(mContext);
- mRouter = MediaRouter2.getInstance(mContext);
+ mRouter = (MediaRouter) mContext.getSystemService(Context.MEDIA_ROUTER_SERVICE);
+ mRouter2 = MediaRouter2.getInstance(mContext);
//TODO: If we need to support thread pool executors, change this to thread pool executor.
mExecutor = Executors.newSingleThreadExecutor();
mPackageName = mContext.getPackageName();
}
- //TODO: Move to a seperate file
+ //TODO: Move to a separate file
@Test
public void testMediaRoute2Info() {
MediaRoute2Info routeInfo1 = new MediaRoute2Info.Builder("id", "name")
@@ -131,12 +145,12 @@ public class MediaRouterManagerTest {
//TODO: Figure out a more proper way to test.
// (Control requests shouldn't be used in this way.)
- mRouter.setControlCategories(CONTROL_CATEGORIES_ALL);
- mRouter.registerCallback(mExecutor, mockRouterCallback);
- mRouter.sendControlRequest(
+ mRouter2.setControlCategories(CONTROL_CATEGORIES_ALL);
+ mRouter2.registerCallback(mExecutor, mockRouterCallback);
+ mRouter2.sendControlRequest(
new MediaRoute2Info.Builder(ROUTE_ID2, ROUTE_NAME2).build(),
new Intent(ACTION_REMOVE_ROUTE));
- mRouter.unregisterCallback(mockRouterCallback);
+ mRouter2.unregisterCallback(mockRouterCallback);
verify(mockCallback, timeout(TIMEOUT_MS)).onRouteRemoved(argThat(
(MediaRoute2Info info) ->
@@ -148,15 +162,14 @@ public class MediaRouterManagerTest {
* Tests if we get proper routes for application that has special control category.
*/
@Test
- public void testControlCategory() throws Exception {
+ public void testControlCategoryWithMediaRouter() throws Exception {
MediaRouter2Manager.Callback mockCallback = mock(MediaRouter2Manager.Callback.class);
mManager.registerCallback(mExecutor, mockCallback);
- MediaRouter2.Callback mockRouterCallback = mock(MediaRouter2.Callback.class);
+ MediaRouter.Callback mockRouterCallback = mock(MediaRouter.Callback.class);
mRouter.setControlCategories(CONTROL_CATEGORIES_SPECIAL);
- mRouter.registerCallback(mExecutor, mockRouterCallback);
- mRouter.unregisterCallback(mockRouterCallback);
+ mRouter.addCallback(MediaRouter.ROUTE_TYPE_USER, mockRouterCallback);
verify(mockCallback, timeout(TIMEOUT_MS))
.onRoutesChanged(argThat(routes -> routes.size() > 0));
@@ -167,6 +180,24 @@ public class MediaRouterManagerTest {
Assert.assertEquals(1, routes.size());
Assert.assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
+ mRouter.removeCallback(mockRouterCallback);
+ mManager.unregisterCallback(mockCallback);
+ }
+
+ /**
+ * Tests if we get proper routes for application that has special control category.
+ */
+ @Test
+ public void testControlCategory() throws Exception {
+ MediaRouter2Manager.Callback mockCallback = mock(MediaRouter2Manager.Callback.class);
+ mManager.registerCallback(mExecutor, mockCallback);
+
+ Map<String, MediaRoute2Info> routes =
+ waitAndGetRoutesWithManager(CONTROL_CATEGORIES_SPECIAL);
+
+ Assert.assertEquals(1, routes.size());
+ Assert.assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
+
mManager.unregisterCallback(mockCallback);
}
@@ -176,65 +207,52 @@ public class MediaRouterManagerTest {
@Test
public void testGetRoutes() throws Exception {
MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
+ mRouter2.registerCallback(mExecutor, mockCallback);
+
+ Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CONTROL_CATEGORIES_SPECIAL);
- mRouter.setControlCategories(CONTROL_CATEGORIES_SPECIAL);
- mRouter.registerCallback(mExecutor, mockCallback);
- verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
- .onRoutesChanged(argThat(routes -> routes.size() > 0));
- Map<String, MediaRoute2Info> routes = createRouteMap(mRouter.getRoutes());
Assert.assertEquals(1, routes.size());
Assert.assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
- mRouter.unregisterCallback(mockCallback);
+ mRouter2.unregisterCallback(mockCallback);
}
@Test
public void testOnRouteSelected() throws Exception {
- MediaRouter2.Callback mockRouterCallback = mock(MediaRouter2.Callback.class);
+ MediaRouter2.Callback routerCallback = new MediaRouter2.Callback();
MediaRouter2Manager.Callback managerCallback = mock(MediaRouter2Manager.Callback.class);
mManager.registerCallback(mExecutor, managerCallback);
- mRouter.setControlCategories(CONTROL_CATEGORIES_ALL);
- mRouter.registerCallback(mExecutor, mockRouterCallback);
+ mRouter2.registerCallback(mExecutor, routerCallback);
- verify(managerCallback, timeout(TIMEOUT_MS))
- .onRoutesChanged(argThat(routes -> routes.size() > 0));
-
- Map<String, MediaRoute2Info> routes =
- createRouteMap(mManager.getAvailableRoutes(mPackageName));
+ Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CONTROL_CATEGORIES_ALL);
MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1);
- mManager.selectRoute(mPackageName, routeToSelect);
-
assertNotNull(routeToSelect);
+
+ mManager.selectRoute(mPackageName, routeToSelect);
verify(managerCallback, timeout(TIMEOUT_MS))
.onRouteAdded(argThat(route -> route.equals(routeToSelect)));
+ mRouter2.unregisterCallback(routerCallback);
mManager.unregisterCallback(managerCallback);
- mRouter.unregisterCallback(mockRouterCallback);
}
/**
* Tests selecting and unselecting routes of a single provider.
*/
@Test
- public void testSingleProviderSelect() {
+ public void testSingleProviderSelect() throws Exception {
MediaRouter2Manager.Callback managerCallback = mock(MediaRouter2Manager.Callback.class);
MediaRouter2.Callback routerCallback = mock(MediaRouter2.Callback.class);
mManager.registerCallback(mExecutor, managerCallback);
- mRouter.setControlCategories(CONTROL_CATEGORIES_ALL);
- mRouter.registerCallback(mExecutor, routerCallback);
-
- verify(managerCallback, timeout(TIMEOUT_MS))
- .onRoutesChanged(argThat(routes -> routes.size() > 0));
+ mRouter2.registerCallback(mExecutor, routerCallback);
- Map<String, MediaRoute2Info> routes =
- createRouteMap(mManager.getAvailableRoutes(mPackageName));
+ Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CONTROL_CATEGORIES_ALL);
mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1));
- verify(managerCallback, timeout(TIMEOUT_MS)
- )
+ verify(managerCallback, timeout(TIMEOUT_MS))
.onRouteChanged(argThat(routeInfo -> TextUtils.equals(ROUTE_ID1, routeInfo.getId())
&& TextUtils.equals(routeInfo.getClientPackageName(), mPackageName)));
@@ -248,10 +266,125 @@ public class MediaRouterManagerTest {
.onRouteChanged(argThat(routeInfo -> TextUtils.equals(ROUTE_ID2, routeInfo.getId())
&& TextUtils.equals(routeInfo.getClientPackageName(), null)));
- mRouter.unregisterCallback(routerCallback);
+ mRouter2.unregisterCallback(routerCallback);
mManager.unregisterCallback(managerCallback);
}
+ @Test
+ public void testControlVolumeWithRouter() throws Exception {
+ MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
+
+ Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CONTROL_CATEGORIES_ALL);
+ mRouter2.registerCallback(mExecutor, mockCallback);
+
+ MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
+ int originalVolume = volRoute.getVolume();
+ int deltaVolume = (originalVolume == volRoute.getVolumeMax() ? -1 : 1);
+ int targetVolume = originalVolume + deltaVolume;
+
+ mRouter2.requestSetVolume(volRoute, targetVolume);
+ verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
+ .onRouteChanged(argThat(route ->
+ route.getId().equals(volRoute.getId())
+ && route.getVolume() == targetVolume));
+
+ mRouter2.requestUpdateVolume(volRoute, -deltaVolume);
+ verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
+ .onRouteChanged(argThat(route ->
+ route.getId().equals(volRoute.getId())
+ && route.getVolume() == originalVolume));
+
+ mRouter2.unregisterCallback(mockCallback);
+ }
+
+ @Test
+ public void testControlVolumeWithManager() throws Exception {
+ MediaRouter2Manager.Callback managerCallback = mock(MediaRouter2Manager.Callback.class);
+ MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
+
+ mManager.registerCallback(mExecutor, managerCallback);
+ mRouter2.registerCallback(mExecutor, mockCallback);
+ Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CONTROL_CATEGORIES_ALL);
+
+ MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
+ int originalVolume = volRoute.getVolume();
+ int deltaVolume = (originalVolume == volRoute.getVolumeMax() ? -1 : 1);
+ int targetVolume = originalVolume + deltaVolume;
+
+ mManager.requestSetVolume(volRoute, targetVolume);
+ verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
+ .onRouteChanged(argThat(route ->
+ route.getId().equals(volRoute.getId())
+ && route.getVolume() == targetVolume));
+
+ mManager.requestUpdateVolume(volRoute, -deltaVolume);
+ verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
+ .onRouteChanged(argThat(route ->
+ route.getId().equals(volRoute.getId())
+ && route.getVolume() == originalVolume));
+
+ mRouter2.unregisterCallback(mockCallback);
+ mManager.unregisterCallback(managerCallback);
+ }
+
+ @Test
+ public void testVolumeHandling() throws Exception {
+ MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
+ mRouter2.registerCallback(mExecutor, mockCallback);
+ Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CONTROL_CATEGORIES_ALL);
+
+ MediaRoute2Info fixedVolumeRoute = routes.get(ROUTE_ID_FIXED_VOLUME);
+ MediaRoute2Info variableVolumeRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
+
+ assertEquals(PLAYBACK_VOLUME_FIXED, fixedVolumeRoute.getVolumeHandling());
+ assertEquals(PLAYBACK_VOLUME_VARIABLE, variableVolumeRoute.getVolumeHandling());
+ assertEquals(VOLUME_MAX, variableVolumeRoute.getVolumeMax());
+
+ mRouter2.unregisterCallback(mockCallback);
+ }
+
+ Map<String, MediaRoute2Info> waitAndGetRoutes(List<String> controlCategories) throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+ MediaRouter2.Callback callback = new MediaRouter2.Callback() {
+ @Override
+ public void onRoutesChanged(List<MediaRoute2Info> routes) {
+ if (routes.size() > 0) latch.countDown();
+ }
+ };
+ mRouter2.setControlCategories(controlCategories);
+ mRouter2.registerCallback(mExecutor, callback);
+ try {
+ latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ return createRouteMap(mRouter2.getRoutes());
+ } finally {
+ mRouter2.unregisterCallback(callback);
+ }
+ }
+
+ Map<String, MediaRoute2Info> waitAndGetRoutesWithManager(List<String> controlCategories)
+ throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+
+ // Dummy callback is required to send control category info.
+ MediaRouter2.Callback routerCallback = new MediaRouter2.Callback();
+ MediaRouter2Manager.Callback managerCallback = new MediaRouter2Manager.Callback() {
+ @Override
+ public void onRoutesChanged(List<MediaRoute2Info> routes) {
+ if (routes.size() > 0) latch.countDown();
+ }
+ };
+ mManager.registerCallback(mExecutor, managerCallback);
+ mRouter2.setControlCategories(controlCategories);
+ mRouter2.registerCallback(mExecutor, routerCallback);
+ try {
+ latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ return createRouteMap(mManager.getAvailableRoutes(mPackageName));
+ } finally {
+ mRouter2.unregisterCallback(routerCallback);
+ mManager.unregisterCallback(managerCallback);
+ }
+ }
+
// Helper for getting routes easily
static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) {
Map<String, MediaRoute2Info> routeMap = new HashMap<>();
diff --git a/mime/Android.bp b/mime/Android.bp
new file mode 100644
index 000000000000..23a8fbf5059c
--- /dev/null
+++ b/mime/Android.bp
@@ -0,0 +1,121 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+java_defaults {
+ name: "mimemap-defaults",
+ srcs: [
+ "java/android/content/type/DefaultMimeMapFactory.java",
+ ],
+ sdk_version: "core_platform",
+}
+
+java_library {
+ name: "mimemap",
+ defaults: ["mimemap-defaults"],
+ static_libs: ["mimemap-res.jar"],
+ visibility: [
+ "//frameworks/base:__subpackages__",
+ ],
+}
+
+java_library {
+ name: "mimemap-testing",
+ defaults: ["mimemap-defaults"],
+ static_libs: ["mimemap-testing-res.jar"],
+ jarjar_rules: "jarjar-rules.txt",
+ visibility: [
+ "//cts/tests/tests/mimemap:__subpackages__",
+ "//frameworks/base:__subpackages__",
+ ],
+}
+
+// The mimemap-res.jar and mimemap-testing-res.jar genrules produce a .jar that
+// has the resource file in a subdirectory res/ and testres/, respectively.
+// They need to be in different paths because one of them ends up in a
+// bootclasspath jar whereas the other one ends up in a test jar. Bootclasspath
+// resources hide test or application resources under the same path because
+// ClassLoader.getResource(String) consults the parent ClassLoader first.
+//
+// Further notes:
+// - the "cp" command will flatten any directory paths that occur in $(in),
+// but here they happen to already be in the root directory. If we needed
+// to preserve sub paths then we might want to zip the files first and then
+// unzip them below the new parent directory.
+// - the path names "res/" and "testres/" and duplicated in .java source files
+// (DefaultMimeMapFactory.java and MimeMapTest.java, as of October 2019).
+java_genrule {
+ name: "mimemap-res.jar",
+ tools: [
+ "soong_zip",
+ ],
+ srcs: [":mime.types.minimized"],
+ out: ["mimemap-res.jar"],
+ cmd: "mkdir $(genDir)/res/ && cp $(in) $(genDir)/res/ && $(location soong_zip) -C $(genDir) -o $(out) -D $(genDir)/res/",
+}
+
+// The same as mimemap-res.jar except that the resources are placed in a different directory.
+// They get bundled with CTS so that CTS can compare a device's MimeMap implementation vs.
+// the stock Android one from when CTS was built.
+java_genrule {
+ name: "mimemap-testing-res.jar",
+ tools: [
+ "soong_zip",
+ ],
+ srcs: [":mime.types.minimized"],
+ out: ["mimemap-testing-res.jar"],
+ cmd: "mkdir $(genDir)/testres/ && cp $(in) $(genDir)/testres/ && $(location soong_zip) -C $(genDir) -o $(out) -D $(genDir)/testres/",
+}
+
+// Combination of all *mime.types.minimized resources.
+filegroup {
+ name: "mime.types.minimized",
+ visibility: [
+ "//visibility:private",
+ ],
+ srcs: [
+ ":debian.mime.types.minimized",
+ ":android.mime.types.minimized",
+ ":vendor.mime.types.minimized",
+ ],
+}
+
+java_genrule {
+ name: "android.mime.types.minimized",
+ visibility: [
+ "//visibility:private",
+ ],
+ out: ["android.mime.types"],
+ srcs: [
+ "java-res/android.mime.types",
+ ],
+ // strip comments normalize whitepace drop empty lines
+ cmd: "awk '{gsub(/#.*$$/,\"\"); $$1=$$1; print;}' $(in) | grep ' ' > $(out)",
+}
+
+// Unlike the other *mime.types files, vendor.mime.types gets '?' prepended to
+// every field so that its mappings will never overwrite earlier mappings by
+// the other resource files. http://b/141842825
+java_genrule {
+ name: "vendor.mime.types.minimized",
+ visibility: [
+ "//visibility:private",
+ ],
+ out: ["vendor.mime.types"],
+ srcs: [
+ "java-res/vendor.mime.types",
+ ],
+ // strip comments normalize whitepace drop empty lines prepend ? to fields that are missing it
+ cmd: "awk '{gsub(/#.*$$/,\"\"); $$1=$$1; print;}' $(in) | grep ' ' | awk '{for(i=1;i<=NF;i++) { sub(/^\\??/, \"?\", $$i); }; print}' > $(out)",
+}
diff --git a/mime/TEST_MAPPING b/mime/TEST_MAPPING
new file mode 100644
index 000000000000..8daab754ea8a
--- /dev/null
+++ b/mime/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsMimeMapTestCases"
+ }
+ ]
+}
diff --git a/mime/jarjar-rules.txt b/mime/jarjar-rules.txt
new file mode 100644
index 000000000000..145d1dbf3d11
--- /dev/null
+++ b/mime/jarjar-rules.txt
@@ -0,0 +1 @@
+rule android.content.type.DefaultMimeMapFactory android.content.type.cts.StockAndroidMimeMapFactory \ No newline at end of file
diff --git a/mime/java-res/android.mime.types b/mime/java-res/android.mime.types
new file mode 100644
index 000000000000..7a5299ff1b69
--- /dev/null
+++ b/mime/java-res/android.mime.types
@@ -0,0 +1,146 @@
+
+###############################################################################
+#
+# Android-specific MIME type <-> extension mappings
+#
+# Each line below defines a mapping from one MIME type to the first of the
+# listed extensions, and from listed extension back to the MIME type.
+# A mapping overrides any previous mapping _from_ that same MIME type or
+# extension (put() semantics), unless that MIME type / extension is prefixed with '?'
+# (putIfAbsent() semantics).
+#
+#
+###############################################################################
+#
+# EXAMPLES
+#
+# A line of the form:
+#
+# ?mime ext1 ?ext2 ext3
+#
+# affects the current mappings along the lines of the following pseudo code:
+#
+# mimeToExt.putIfAbsent("mime", "ext1");
+# extToMime.put("ext1", "mime");
+# extToMime.putIfAbsent("ext2", "mime");
+# extToMime.put("ext3", "mime");
+#
+# The line:
+#
+# ?text/plain txt
+#
+# leaves any earlier mapping for "text/plain" untouched, or maps that MIME type
+# to the file extension ".txt" if there is no earlier mapping. The line also
+# sets the mapping from file extension ".txt" to be the MIME type "text/plain",
+# regardless of whether a previous mapping existed.
+#
+###############################################################################
+
+
+# File extensions that Android wants to override to point to the given MIME type.
+#
+# After processing a line of the form:
+# ?<mimeType> <extension1> <extension2>
+# If <mimeType> was not already mapped to an extension then it will be
+# mapped to <extension1>.
+# <extension1> and <extension2> are mapped (or remapped) to <mimeType>.
+
+?application/epub+zip epub
+?application/pkix-cert cer
+?application/rss+xml rss
+?application/vnd.android.ota ota
+?application/vnd.apple.mpegurl m3u8
+?application/vnd.ms-pki.stl stl
+?application/vnd.ms-powerpoint pot
+?application/vnd.ms-wpl wpl
+?application/vnd.stardivision.impress sdp
+?application/vnd.stardivision.writer vor
+?application/vnd.youtube.yt yt
+?application/x-android-drm-fl fl
+?application/x-flac flac
+?application/x-font pcf
+?application/x-mpegurl m3u m3u8
+?application/x-pem-file pem
+?application/x-pkcs12 p12 pfx
+?application/x-webarchive webarchive
+?application/x-webarchive-xml webarchivexml
+?application/x-x509-server-cert crt
+?application/x-x509-user-cert crt
+
+?audio/3gpp 3gpp
+?audio/aac-adts aac
+?audio/imelody imy
+?audio/midi rtttl xmf
+?audio/mobile-xmf mxmf
+?audio/mp4 m4a
+?audio/mpegurl m3u
+?audio/sp-midi smf
+?audio/x-matroska mka
+?audio/x-pn-realaudio ra
+
+?image/bmp bmp
+?image/heic heic
+?image/heic-sequence heics
+?image/heif heif hif
+?image/heif-sequence heifs
+?image/ico cur
+?image/webp webp
+?image/x-adobe-dng dng
+?image/x-fuji-raf raf
+?image/x-icon ico
+?image/x-nikon-nrw nrw
+?image/x-panasonic-rw2 rw2
+?image/x-pentax-pef pef
+?image/x-samsung-srw srw
+?image/x-sony-arw arw
+
+?text/comma-separated-values csv
+?text/plain diff po
+?text/rtf rtf
+?text/text phps
+?text/xml xml
+?text/x-vcard vcf
+
+?video/3gpp2 3gpp2 3g2
+?video/3gpp 3gpp
+?video/avi avi
+?video/m4v m4v
+?video/mp2p mpeg
+?video/mp2t m2ts mts
+?video/mp2ts ts
+?video/vnd.youtube.yt yt
+?video/x-webex wrf
+
+# Optional additions that should not override any previous mapping.
+
+?application/x-wifi-config ?xml
+
+# Special cases where Android has a strong opinion about mappings, so we
+# define them very last and make them override in both directions (no "?").
+#
+# Lines here are of the form:
+# <mimeType> <extension1> <extension2> ...
+#
+# After processing each line,
+# <mimeType> is mapped to <extension1>
+# <extension1>, <extension2>, ... are all mapped to <mimeType>
+# This overrides any mappings for this <mimeType> / for these extensions
+# that may have been defined earlier.
+
+application/pgp-signature pgp
+application/x-x509-ca-cert crt
+audio/aac aac
+audio/basic snd
+audio/flac flac
+audio/midi rtx
+audio/mpeg mp3 m4a m4r
+audio/x-mpegurl m3u m3u8
+image/jpeg jpg
+image/x-ms-bmp bmp
+text/plain txt
+text/x-c++hdr hpp
+text/x-c++src cpp
+video/3gpp 3gpp
+video/mpeg mpeg
+video/quicktime mov
+video/x-matroska mkv
diff --git a/mime/java-res/vendor.mime.types b/mime/java-res/vendor.mime.types
new file mode 100644
index 000000000000..1861909f1e7a
--- /dev/null
+++ b/mime/java-res/vendor.mime.types
@@ -0,0 +1,49 @@
+###############################################################################
+#
+# Vendor-specific MIME type <-> extension mappings
+#
+# Each line below defines a mapping from one MIME type to the first of the
+# listed extensions, and from listed extension back to the MIME type.
+#
+# This file can _add_ additional mappings that are not in the default set,
+# but it it cannot _modify_ (replace or remove) any platform default mapping
+# (defined in files mime.types and android.mime.types).
+#
+###############################################################################
+#
+# EXAMPLES
+#
+# A line of the form (without the leading '#''):
+#
+# mime ext1 ext2 ext3
+#
+# affects the current mappings along the lines of the following pseudo code:
+#
+# mimeToExt.putIfAbsent("mime", "ext1");
+# extToMime.putIfAbsent("ext1", "mime");
+# extToMime.putIfAbsent("ext2", "mime");
+# extToMime.putIfAbsent("ext3", "mime");
+#
+# Optionally, MIME types or extensions may be prefixed by a single '?', which
+# will be ignored. I.e., the following example lines all have the same semantics:
+#
+# mime ext1 ext2 ext3
+# ?mime ext1 ext2 ext3
+# mime ?ext1 ext2 ?ext3
+# ?mime ?ext1 ?ext2 ?ext3
+#
+# By default, this file contains no mappings (which means that the platform
+# default mapping is used unmodified).
+#
+###############################################################################
+#
+# Add your custom mappings below this line (with no "#" at the start of the line):
+
+?audio/qcelp qcp
+?audio/ac3 ac3
+?audio/eac3 ec3
+?audio/x-ape ape
+?audio/x-dsf dsf
+?audio/x-dff dff
+?audio/dsd dsd
+?video/divx divx \ No newline at end of file
diff --git a/mime/java/android/content/type/DefaultMimeMapFactory.java b/mime/java/android/content/type/DefaultMimeMapFactory.java
new file mode 100644
index 000000000000..11d20d4d6c80
--- /dev/null
+++ b/mime/java/android/content/type/DefaultMimeMapFactory.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.type;
+
+import libcore.content.type.MimeMap;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Function;
+
+/**
+ * Creates the framework default {@link MimeMap}, a bidirectional mapping
+ * between MIME types and file extensions.
+ *
+ * This default mapping is loaded from data files that start with some mappings
+ * recognized by IANA plus some custom extensions and overrides.
+ *
+ * @hide
+ */
+public class DefaultMimeMapFactory {
+
+ private DefaultMimeMapFactory() {
+ }
+
+ /**
+ * Creates and returns a new {@link MimeMap} instance that implements.
+ * Android's default mapping between MIME types and extensions.
+ */
+ public static MimeMap create() {
+ Class c = DefaultMimeMapFactory.class;
+ // The resources are placed into the res/ path by the "mimemap-res.jar" genrule.
+ return create(resourceName -> c.getResourceAsStream("/res/" + resourceName));
+ }
+
+ /**
+ * Creates a {@link MimeMap} instance whose resources are loaded from the
+ * InputStreams looked up in {@code resourceSupplier}.
+ *
+ * @hide
+ */
+ public static MimeMap create(Function<String, InputStream> resourceSupplier) {
+ MimeMap.Builder builder = MimeMap.builder();
+ // The files loaded here must be in minimized format with lines of the
+ // form "mime/type ext1 ext2 ext3", i.e. no comments, no empty lines, no
+ // leading/trailing whitespace and with a single space between entries on
+ // each line. See http://b/142267887
+ //
+ // Note: the order here matters - later entries can overwrite earlier ones
+ // (except that vendor.mime.types entries are prefixed with '?' which makes
+ // them never overwrite).
+ parseTypes(builder, resourceSupplier, "debian.mime.types");
+ parseTypes(builder, resourceSupplier, "android.mime.types");
+ parseTypes(builder, resourceSupplier, "vendor.mime.types");
+ return builder.build();
+ }
+
+ private static void parseTypes(MimeMap.Builder builder,
+ Function<String, InputStream> resourceSupplier, String resourceName) {
+ try (InputStream inputStream = Objects.requireNonNull(resourceSupplier.apply(resourceName));
+ BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
+ String line;
+ List<String> specs = new ArrayList<>(10); // re-use for each line
+ while ((line = reader.readLine()) != null) {
+ specs.clear();
+ // Lines are of the form "mimeSpec extSpec extSpec[...]" with a single space
+ // separating them and no leading/trailing spaces and no empty lines.
+ int startIdx = 0;
+ do {
+ int endIdx = line.indexOf(' ', startIdx);
+ if (endIdx < 0) {
+ endIdx = line.length();
+ }
+ String spec = line.substring(startIdx, endIdx);
+ if (spec.isEmpty()) {
+ throw new IllegalArgumentException("Malformed line: " + line);
+ }
+ specs.add(spec);
+ startIdx = endIdx + 1; // skip over the space
+ } while (startIdx < line.length());
+ builder.put(specs.get(0), specs.subList(1, specs.size()));
+ }
+ } catch (IOException | RuntimeException e) {
+ throw new RuntimeException("Failed to parse " + resourceName, e);
+ }
+ }
+
+}
diff --git a/mms/OWNERS b/mms/OWNERS
new file mode 100644
index 000000000000..ba00d5d75010
--- /dev/null
+++ b/mms/OWNERS
@@ -0,0 +1,14 @@
+set noparent
+
+tgunn@google.com
+breadley@google.com
+hallliu@google.com
+rgreenwalt@google.com
+amitmahajan@google.com
+fionaxu@google.com
+jackyu@google.com
+jminjie@google.com
+satk@google.com
+shuoq@google.com
+refuhoo@google.com
+nazaninb@google.com
diff --git a/mms/java/android/telephony/MmsManager.java b/mms/java/android/telephony/MmsManager.java
new file mode 100644
index 000000000000..4bcf04691652
--- /dev/null
+++ b/mms/java/android/telephony/MmsManager.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.app.ActivityThread;
+import android.app.PendingIntent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.telephony.IMms;
+
+/**
+ * Manages MMS operations such as sending multimedia messages.
+ * Get this object by calling the static method {@link #getInstance()}.
+ * @hide
+ */
+public class MmsManager {
+ private static final String TAG = "MmsManager";
+
+ /** Singleton object constructed during class initialization. */
+ private static final MmsManager sInstance = new MmsManager();
+
+ /**
+ * Get the MmsManager singleton instance.
+ *
+ * @return the {@link MmsManager} singleton instance.
+ */
+ public static MmsManager getInstance() {
+ return sInstance;
+ }
+
+ /**
+ * Send an MMS message
+ *
+ * @param subId the subscription id
+ * @param contentUri the content Uri from which the message pdu will be read
+ * @param locationUrl the optional location url where message should be sent to
+ * @param configOverrides the carrier-specific messaging configuration values to override for
+ * sending the message.
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is broadcast when the message
+ * is successfully sent, or failed
+ */
+ public void sendMultimediaMessage(int subId, Uri contentUri, String locationUrl,
+ Bundle configOverrides, PendingIntent sentIntent) {
+ try {
+ final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
+ if (iMms == null) {
+ return;
+ }
+
+ iMms.sendMessage(subId, ActivityThread.currentPackageName(), contentUri,
+ locationUrl, configOverrides, sentIntent);
+ } catch (RemoteException e) {
+ // Ignore it
+ }
+ }
+
+ /**
+ * Download an MMS message from carrier by a given location URL
+ *
+ * @param subId the subscription id
+ * @param locationUrl the location URL of the MMS message to be downloaded, usually obtained
+ * from the MMS WAP push notification
+ * @param contentUri the content uri to which the downloaded pdu will be written
+ * @param configOverrides the carrier-specific messaging configuration values to override for
+ * downloading the message.
+ * @param downloadedIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is downloaded, or the download is failed
+ * @throws IllegalArgumentException if locationUrl or contentUri is empty
+ */
+ public void downloadMultimediaMessage(int subId, String locationUrl, Uri contentUri,
+ Bundle configOverrides, PendingIntent downloadedIntent) {
+ try {
+ final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
+ if (iMms == null) {
+ return;
+ }
+ iMms.downloadMessage(subId, ActivityThread.currentPackageName(),
+ locationUrl, contentUri, configOverrides, downloadedIntent);
+ } catch (RemoteException e) {
+ // Ignore it
+ }
+ }
+
+ /**
+ * Get carrier-dependent configuration values.
+ *
+ * @param subId the subscription id
+ * @return bundle key/values pairs of configuration values
+ */
+ public Bundle getCarrierConfigValues(int subId) {
+ try {
+ IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
+ if (iMms != null) {
+ return iMms.getCarrierConfigValues(subId);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ return null;
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/IMms.aidl b/mms/java/com/android/internal/telephony/IMms.aidl
index fa5073ef1c7e..fa5073ef1c7e 100644
--- a/telephony/java/com/android/internal/telephony/IMms.aidl
+++ b/mms/java/com/android/internal/telephony/IMms.aidl
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 7c1af4a81f9d..91297b0de02e 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -110,3 +110,36 @@ llndk_library {
symbol_file: "libandroid_net.map.txt",
unversioned: true,
}
+
+
+// Aidl library for platform compat.
+cc_library_shared {
+ name: "lib-platform-compat-native-api",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-missing-field-initializers",
+ "-Wno-unused-variable",
+ "-Wunused-parameter",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ ],
+ aidl: {
+ local_include_dirs: ["aidl"],
+ export_aidl_headers: true,
+ },
+ srcs: [
+ ":platform-compat-native-aidl",
+ ],
+ export_include_dirs: ["aidl"],
+}
+
+filegroup {
+ name: "platform-compat-native-aidl",
+ srcs: [
+ "aidl/com/android/internal/compat/IPlatformCompatNative.aidl",
+ ],
+ path: "aidl",
+} \ No newline at end of file
diff --git a/native/android/aidl/com/android/internal/compat/IPlatformCompatNative.aidl b/native/android/aidl/com/android/internal/compat/IPlatformCompatNative.aidl
new file mode 100644
index 000000000000..347e4e8ebe4b
--- /dev/null
+++ b/native/android/aidl/com/android/internal/compat/IPlatformCompatNative.aidl
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.compat;
+
+/**
+ * Platform native private API for talking with the PlatformCompat service.
+ *
+ * <p> Should be used for gating and logging from non-app processes running cpp code.
+ * For app processes please use android.compat.Compatibility API.
+ *
+ * {@hide}
+ */
+interface IPlatformCompatNative
+{
+ /**
+ * Reports that a compatibility change is affecting an app process now.
+ *
+ * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, String)},
+ * you do not need to call this API directly. The change will be reported for you.
+ *
+ * @param changeId The ID of the compatibility change taking effect.
+ * @param userId The ID of the user that the operation is done for.
+ * @param packageName The package name of the app in question.
+ */
+ void reportChangeByPackageName(long changeId, @utf8InCpp String packageName, int userId);
+
+ /**
+ * Reports that a compatibility change is affecting an app process now.
+ *
+ * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, int)},
+ * you do not need to call this API directly. The change will be reported for you.
+ *
+ * @param changeId The ID of the compatibility change taking effect.
+ * @param uid The UID of the app in question.
+ */
+ void reportChangeByUid(long changeId, int uid);
+
+ /**
+ * Query if a given compatibility change is enabled for an app process. This method should
+ * be called when implementing functionality on behalf of the affected app.
+ *
+ * <p>Returns {@code true} if there is no installed package by the provided package name.
+ *
+ * <p>If this method returns {@code true}, the calling code should implement the compatibility
+ * change, resulting in differing behaviour compared to earlier releases. If this method
+ * returns
+ * {@code false}, the calling code should behave as it did in earlier releases.
+ *
+ * <p>It will also report the change as {@link #reportChange(long, String)} would, so there is
+ * no need to call that method directly.
+ *
+ * @param changeId The ID of the compatibility change in question.
+ * @param packageName The package name of the app in question.
+ * @param userId The ID of the user that the operation is done for.
+ * @return {@code true} if the change is enabled for the current app.
+ */
+ boolean isChangeEnabledByPackageName(long changeId, @utf8InCpp String packageName, int userId);
+
+ /**
+ * Query if a given compatibility change is enabled for an app process. This method should
+ * be called when implementing functionality on behalf of the affected app.
+ *
+ * <p> Returns {@code true} if there are no installed packages for the required UID, or if the
+ * change is enabled for ALL of the installed packages associated with the provided UID. Please
+ * use a more specific API if you want a different behaviour for multi-package UIDs.
+ *
+ * <p>If this method returns {@code true}, the calling code should implement the compatibility
+ * change, resulting in differing behaviour compared to earlier releases. If this method
+ * returns {@code false}, the calling code should behave as it did in earlier releases.
+ *
+ * <p>It will also report the change as {@link #reportChange(long, int)} would, so there is
+ * no need to call that method directly.
+ *
+ * @param changeId The ID of the compatibility change in question.
+ * @param uid The UID of the app in question.
+ * @return {@code true} if the change is enabled for the current app.
+ */
+ boolean isChangeEnabledByUid(long changeId, int uid);
+} \ No newline at end of file
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 53c01226ca1a..b34b31ac8439 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -294,7 +294,7 @@ void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* aSurfaceTransaction,
auto& aSurfaceControlStats = aSurfaceTransactionStats.aSurfaceControlStats;
- for (const auto& [surfaceControl, acquireTime, previousReleaseFence] : surfaceControlStats) {
+ for (const auto& [surfaceControl, acquireTime, previousReleaseFence, transformHint] : surfaceControlStats) {
ASurfaceControl* aSurfaceControl = reinterpret_cast<ASurfaceControl*>(surfaceControl.get());
aSurfaceControlStats[aSurfaceControl].acquireTime = acquireTime;
aSurfaceControlStats[aSurfaceControl].previousReleaseFence = previousReleaseFence;
diff --git a/opengl/java/android/opengl/GLUtils.java b/opengl/java/android/opengl/GLUtils.java
index ca8d5ac52021..cc46514ae96e 100644
--- a/opengl/java/android/opengl/GLUtils.java
+++ b/opengl/java/android/opengl/GLUtils.java
@@ -44,7 +44,7 @@ public final class GLUtils {
if (bitmap.isRecycled()) {
throw new IllegalArgumentException("bitmap is recycled");
}
- int result = native_getInternalFormat(bitmap.getNativeInstance());
+ int result = native_getInternalFormat(bitmap);
if (result < 0) {
throw new IllegalArgumentException("Unknown internalformat");
}
@@ -66,7 +66,7 @@ public final class GLUtils {
if (bitmap.isRecycled()) {
throw new IllegalArgumentException("bitmap is recycled");
}
- int result = native_getType(bitmap.getNativeInstance());
+ int result = native_getType(bitmap);
if (result < 0) {
throw new IllegalArgumentException("Unknown type");
}
@@ -103,8 +103,7 @@ public final class GLUtils {
if (bitmap.isRecycled()) {
throw new IllegalArgumentException("bitmap is recycled");
}
- if (native_texImage2D(target, level, internalformat, bitmap.getNativeInstance(), -1,
- border) != 0) {
+ if (native_texImage2D(target, level, internalformat, bitmap, -1, border) != 0) {
throw new IllegalArgumentException("invalid Bitmap format");
}
}
@@ -130,8 +129,7 @@ public final class GLUtils {
if (bitmap.isRecycled()) {
throw new IllegalArgumentException("bitmap is recycled");
}
- if (native_texImage2D(target, level, internalformat, bitmap.getNativeInstance(), type,
- border) != 0) {
+ if (native_texImage2D(target, level, internalformat, bitmap, type, border) != 0) {
throw new IllegalArgumentException("invalid Bitmap format");
}
}
@@ -153,7 +151,7 @@ public final class GLUtils {
if (bitmap.isRecycled()) {
throw new IllegalArgumentException("bitmap is recycled");
}
- if (native_texImage2D(target, level, -1, bitmap.getNativeInstance(), -1, border) != 0) {
+ if (native_texImage2D(target, level, -1, bitmap, -1, border) != 0) {
throw new IllegalArgumentException("invalid Bitmap format");
}
}
@@ -189,8 +187,7 @@ public final class GLUtils {
throw new IllegalArgumentException("bitmap is recycled");
}
int type = getType(bitmap);
- if (native_texSubImage2D(target, level, xoffset, yoffset, bitmap.getNativeInstance(), -1,
- type) != 0) {
+ if (native_texSubImage2D(target, level, xoffset, yoffset, bitmap, -1, type) != 0) {
throw new IllegalArgumentException("invalid Bitmap format");
}
}
@@ -214,8 +211,7 @@ public final class GLUtils {
if (bitmap.isRecycled()) {
throw new IllegalArgumentException("bitmap is recycled");
}
- if (native_texSubImage2D(target, level, xoffset, yoffset, bitmap.getNativeInstance(),
- format, type) != 0) {
+ if (native_texSubImage2D(target, level, xoffset, yoffset, bitmap, format, type) != 0) {
throw new IllegalArgumentException("invalid Bitmap format");
}
}
@@ -265,10 +261,10 @@ public final class GLUtils {
}
}
- native private static int native_getInternalFormat(long bitmapHandle);
- native private static int native_getType(long bitmapHandle);
- native private static int native_texImage2D(int target, int level, int internalformat,
- long bitmapHandle, int type, int border);
- native private static int native_texSubImage2D(int target, int level, int xoffset, int yoffset,
- long bitmapHandle, int format, int type);
+ private static native int native_getInternalFormat(Bitmap bitmap);
+ private static native int native_getType(Bitmap bitmap);
+ private static native int native_texImage2D(int target, int level, int internalformat,
+ Bitmap bitmap, int type, int border);
+ private static native int native_texSubImage2D(int target, int level, int xoffset, int yoffset,
+ Bitmap bitmap, int format, int type);
}
diff --git a/packages/BackupEncryption/Android.bp b/packages/BackupEncryption/Android.bp
index 9bcd677538eb..68e937c24a89 100644
--- a/packages/BackupEncryption/Android.bp
+++ b/packages/BackupEncryption/Android.bp
@@ -17,7 +17,7 @@
android_app {
name: "BackupEncryption",
srcs: ["src/**/*.java"],
- libs: ["backup-encryption-protos"],
+ static_libs: ["backup-encryption-protos", "backuplib"],
optimize: { enabled: false },
platform_apis: true,
certificate: "platform",
diff --git a/packages/BackupEncryption/AndroidManifest.xml b/packages/BackupEncryption/AndroidManifest.xml
index a705df5a425b..4d174e3b64d6 100644
--- a/packages/BackupEncryption/AndroidManifest.xml
+++ b/packages/BackupEncryption/AndroidManifest.xml
@@ -20,5 +20,14 @@
package="com.android.server.backup.encryption"
android:sharedUserId="android.uid.system" >
- <application android:allowBackup="false" />
+ <application android:allowBackup="false" >
+ <!-- This service does not need to be exported because it shares uid with the system server
+ which is the only client. -->
+ <service android:name=".BackupEncryptionService"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="android.encryption.BACKUP_ENCRYPTION" />
+ </intent-filter>
+ </service>
+ </application>
</manifest>
diff --git a/packages/BackupEncryption/proto/key_value_listing.proto b/packages/BackupEncryption/proto/key_value_listing.proto
new file mode 100644
index 000000000000..001e697bd804
--- /dev/null
+++ b/packages/BackupEncryption/proto/key_value_listing.proto
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+syntax = "proto2";
+
+package android_backup_crypto;
+
+option java_package = "com.android.server.backup.encryption.protos";
+option java_outer_classname = "KeyValueListingProto";
+
+// An entry of a key-value pair.
+message KeyValueEntry {
+ // Plaintext key of the key-value pair.
+ optional string key = 1;
+ // SHA-256 MAC of the plaintext of the chunk containing the pair
+ optional bytes hash = 2;
+}
+
+// Describes the key/value pairs currently in the backup blob, mapping from the
+// plaintext key to the hash of the chunk containing the pair.
+//
+// This is local state stored on the device. It is never sent to the
+// backup server. See ChunkOrdering for how the device restores the
+// key-value pairs in the correct order.
+message KeyValueListing {
+ repeated KeyValueEntry entries = 1;
+}
diff --git a/packages/BackupEncryption/proto/key_value_pair.proto b/packages/BackupEncryption/proto/key_value_pair.proto
new file mode 100644
index 000000000000..177fa3025dc8
--- /dev/null
+++ b/packages/BackupEncryption/proto/key_value_pair.proto
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+syntax = "proto2";
+
+package android_backup_crypto;
+
+option java_package = "com.android.server.backup.encryption.protos";
+option java_outer_classname = "KeyValuePairProto";
+
+// Serialized form of a key-value pair, when it is to be encrypted in a blob.
+// The backup blob for a key-value database consists of repeated encrypted
+// key-value pairs like this, in a randomized order. See ChunkOrdering for how
+// these are then reconstructed during a restore.
+message KeyValuePair {
+ optional string key = 1;
+ optional bytes value = 2;
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/BackupEncryptionService.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/BackupEncryptionService.java
new file mode 100644
index 000000000000..84fb0e62dbca
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/BackupEncryptionService.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.internal.backup.IBackupTransport;
+import com.android.server.backup.encryption.transport.IntermediateEncryptingTransport;
+import com.android.server.backup.encryption.transport.IntermediateEncryptingTransportManager;
+
+/**
+ * This service provides encryption of backup data. For an intent used to bind to this service, it
+ * provides an {@link IntermediateEncryptingTransport} which is an implementation of {@link
+ * IBackupTransport} that encrypts (or decrypts) the data when sending it (or receiving it) from the
+ * real {@link IBackupTransport}.
+ */
+public class BackupEncryptionService extends Service {
+ public static final String TAG = "BackupEncryption";
+ private static IntermediateEncryptingTransportManager sTransportManager = null;
+
+ @Override
+ public void onCreate() {
+ Log.i(TAG, "onCreate:" + this);
+ if (sTransportManager == null) {
+ Log.i(TAG, "Creating IntermediateEncryptingTransportManager");
+ sTransportManager = new IntermediateEncryptingTransportManager(this);
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.i(TAG, "onDestroy:" + this);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ // TODO (b141536117): Check connection with TransportClient.connect and return null on fail.
+ return sTransportManager.get(intent);
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ sTransportManager.cleanup(intent);
+ return false;
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/EncryptionKeyHelper.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/EncryptionKeyHelper.java
new file mode 100644
index 000000000000..2035b6605559
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/EncryptionKeyHelper.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption;
+
+import android.content.Context;
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+import android.security.keystore.recovery.RecoveryController;
+
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
+import com.android.server.backup.encryption.keys.TertiaryKeyManager;
+import com.android.server.backup.encryption.keys.TertiaryKeyRotationScheduler;
+
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+
+class EncryptionKeyHelper {
+ private static SecureRandom sSecureRandom = new SecureRandom();
+
+ private final Context mContext;
+ private final RecoverableKeyStoreSecondaryKeyManager
+ .RecoverableKeyStoreSecondaryKeyManagerProvider
+ mSecondaryKeyManagerProvider;
+
+ EncryptionKeyHelper(Context context) {
+ mContext = context;
+ mSecondaryKeyManagerProvider =
+ () ->
+ new RecoverableKeyStoreSecondaryKeyManager(
+ RecoveryController.getInstance(mContext), sSecureRandom);
+ }
+
+ RecoverableKeyStoreSecondaryKeyManager
+ .RecoverableKeyStoreSecondaryKeyManagerProvider getKeyManagerProvider() {
+ return mSecondaryKeyManagerProvider;
+ }
+
+ RecoverableKeyStoreSecondaryKey getActiveSecondaryKey()
+ throws UnrecoverableKeyException, InternalRecoveryServiceException {
+ String keyAlias = CryptoSettings.getInstance(mContext).getActiveSecondaryKeyAlias().get();
+ return mSecondaryKeyManagerProvider.get().get(keyAlias).get();
+ }
+
+ SecretKey getTertiaryKey(
+ String packageName,
+ RecoverableKeyStoreSecondaryKey secondaryKey)
+ throws IllegalBlockSizeException, InvalidAlgorithmParameterException,
+ NoSuchAlgorithmException, IOException, NoSuchPaddingException,
+ InvalidKeyException {
+ TertiaryKeyManager tertiaryKeyManager =
+ new TertiaryKeyManager(
+ mContext,
+ sSecureRandom,
+ TertiaryKeyRotationScheduler.getInstance(mContext),
+ secondaryKey,
+ packageName);
+ return tertiaryKeyManager.getKey();
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/FullBackupDataProcessor.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/FullBackupDataProcessor.java
new file mode 100644
index 000000000000..f3ab2bde086a
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/FullBackupDataProcessor.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption;
+
+import android.app.backup.BackupTransport;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/** Accepts the full backup data stream and sends it to the server. */
+public interface FullBackupDataProcessor {
+ /**
+ * Prepares the upload.
+ *
+ * <p>After this, call {@link #start()} to establish the connection.
+ *
+ * @param inputStream to read the backup data from, calling {@link #finish} or {@link #cancel}
+ * will close the stream
+ * @return {@code true} if the connection was set up successfully, otherwise {@code false}
+ */
+ boolean initiate(InputStream inputStream) throws IOException;
+
+ /**
+ * Starts the upload, establishing the connection to the server.
+ *
+ * <p>After this, call {@link #pushData(int)} to request that the processor reads data from the
+ * socket, and uploads it to the server.
+ *
+ * <p>After this you must call one of {@link #cancel()}, {@link #finish()}, {@link
+ * #handleCheckSizeRejectionZeroBytes()}, {@link #handleCheckSizeRejectionQuotaExceeded()} or
+ * {@link #handleSendBytesQuotaExceeded()} to close the upload.
+ */
+ void start();
+
+ /**
+ * Requests that the processor read {@code numBytes} from the input stream passed in {@link
+ * #initiate(InputStream)} and upload them to the server.
+ *
+ * @return {@link BackupTransport#TRANSPORT_OK} if the upload succeeds, or {@link
+ * BackupTransport#TRANSPORT_QUOTA_EXCEEDED} if the upload exceeded the server-side app size
+ * quota, or {@link BackupTransport#TRANSPORT_PACKAGE_REJECTED} for other errors.
+ */
+ int pushData(int numBytes);
+
+ /** Cancels the upload and tears down the connection. */
+ void cancel();
+
+ /**
+ * Finish the upload and tear down the connection.
+ *
+ * <p>Call this after there is no more data to push with {@link #pushData(int)}.
+ *
+ * @return One of {@link BackupTransport#TRANSPORT_OK} if the app upload succeeds, {@link
+ * BackupTransport#TRANSPORT_QUOTA_EXCEEDED} if the upload exceeded the server-side app size
+ * quota, {@link BackupTransport#TRANSPORT_ERROR} for server 500s, or {@link
+ * BackupTransport#TRANSPORT_PACKAGE_REJECTED} for other errors.
+ */
+ int finish();
+
+ /**
+ * Notifies the processor that the current upload should be terminated because the estimated
+ * size is zero.
+ */
+ void handleCheckSizeRejectionZeroBytes();
+
+ /**
+ * Notifies the processor that the current upload should be terminated because the estimated
+ * size exceeds the quota.
+ */
+ void handleCheckSizeRejectionQuotaExceeded();
+
+ /**
+ * Notifies this class that the current upload should be terminated because the quota was
+ * exceeded during upload.
+ */
+ void handleSendBytesQuotaExceeded();
+
+ /**
+ * Attaches {@link FullBackupCallbacks} which the processor will notify when the backup
+ * succeeds.
+ */
+ void attachCallbacks(FullBackupCallbacks fullBackupCallbacks);
+
+ /**
+ * Implemented by the caller of the processor to receive notification of when the backup
+ * succeeds.
+ */
+ interface FullBackupCallbacks {
+ /** The processor calls this to indicate that the current backup has succeeded. */
+ void onSuccess();
+
+ /** The processor calls this if the upload failed for a non-transient reason. */
+ void onTransferFailed();
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/FullRestoreDataProcessor.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/FullRestoreDataProcessor.java
new file mode 100644
index 000000000000..e4c40491bc2f
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/FullRestoreDataProcessor.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption;
+
+import java.io.IOException;
+
+/**
+ * Retrieves the data during a full restore, decrypting it if necessary.
+ *
+ * <p>Use {@link FullRestoreDataProcessorFactory} to construct the encrypted or unencrypted
+ * processor as appropriate during restore.
+ */
+public interface FullRestoreDataProcessor {
+ /** Return value of {@link #readNextChunk} when there is no more data to download. */
+ int END_OF_STREAM = -1;
+
+ /**
+ * Reads the next chunk of restore data and writes it to the given buffer.
+ *
+ * <p>Where necessary, will open the connection to the server and/or decrypt the backup file.
+ *
+ * <p>The implementation may retry various errors. If the retries fail it will throw the
+ * relevant exception.
+ *
+ * @return the number of bytes read, or {@link #END_OF_STREAM} if there is no more data
+ * @throws IOException when downloading from the network or writing to disk
+ */
+ int readNextChunk(byte[] buffer) throws IOException;
+
+ /**
+ * Closes the connection to the server, deletes any temporary files and optionally sends a log
+ * with the given finish type.
+ *
+ * @param finishType one of {@link FullRestoreDownloader.FinishType}
+ */
+ void finish(FullRestoreDownloader.FinishType finishType);
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/FullRestoreDownloader.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/FullRestoreDownloader.java
new file mode 100644
index 000000000000..afcca79a0027
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/FullRestoreDownloader.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption;
+
+import java.io.IOException;
+
+/** Interface for classes which will provide backup data */
+public abstract class FullRestoreDownloader {
+ /** Enum to provide information on why a download finished */
+ public enum FinishType {
+ UNKNOWN_FINISH(0),
+ // Finish the downloading and successfully write data to Android OS.
+ FINISHED(1),
+ // Download failed with any kind of exception.
+ TRANSFER_FAILURE(2),
+ // Download failed due to auth failure on the device.
+ AUTH_FAILURE(3),
+ // Aborted by Android Framework.
+ FRAMEWORK_ABORTED(4);
+
+ private int mValue;
+
+ FinishType(int value) {
+ mValue = value;
+ }
+ }
+
+ /** Get the next data chunk from the backing store */
+ public abstract int readNextChunk(byte[] buffer) throws IOException;
+
+ /** Called when we've finished restoring the data */
+ public abstract void finish(FinishType finishType);
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/KeyValueEncrypter.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/KeyValueEncrypter.java
new file mode 100644
index 000000000000..1d841b4a2c8f
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/KeyValueEncrypter.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption;
+
+import static com.android.server.backup.encryption.BackupEncryptionService.TAG;
+
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import com.android.server.backup.encryption.client.CryptoBackupServer;
+import com.android.server.backup.encryption.keys.KeyWrapUtils;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+import com.android.server.backup.encryption.tasks.EncryptedKvBackupTask;
+import com.android.server.backup.encryption.tasks.EncryptedKvRestoreTask;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+import java.util.Map;
+
+public class KeyValueEncrypter {
+ private final Context mContext;
+ private final EncryptionKeyHelper mKeyHelper;
+
+ public KeyValueEncrypter(Context context) {
+ mContext = context;
+ mKeyHelper = new EncryptionKeyHelper(mContext);
+ }
+
+ public void encryptKeyValueData(
+ String packageName, ParcelFileDescriptor inputFd, OutputStream outputStream)
+ throws Exception {
+ EncryptedKvBackupTask.EncryptedKvBackupTaskFactory backupTaskFactory =
+ new EncryptedKvBackupTask.EncryptedKvBackupTaskFactory();
+ EncryptedKvBackupTask backupTask =
+ backupTaskFactory.newInstance(
+ mContext,
+ new SecureRandom(),
+ new FileBackupServer(outputStream),
+ CryptoSettings.getInstance(mContext),
+ mKeyHelper.getKeyManagerProvider(),
+ inputFd,
+ packageName);
+ backupTask.performBackup(/* incremental */ false);
+ }
+
+ public void decryptKeyValueData(String packageName,
+ InputStream encryptedInputStream, ParcelFileDescriptor outputFd) throws Exception {
+ RecoverableKeyStoreSecondaryKey secondaryKey = mKeyHelper.getActiveSecondaryKey();
+
+ EncryptedKvRestoreTask.EncryptedKvRestoreTaskFactory restoreTaskFactory =
+ new EncryptedKvRestoreTask.EncryptedKvRestoreTaskFactory();
+ EncryptedKvRestoreTask restoreTask =
+ restoreTaskFactory.newInstance(
+ mContext,
+ mKeyHelper.getKeyManagerProvider(),
+ new InputStreamFullRestoreDownloader(encryptedInputStream),
+ secondaryKey.getAlias(),
+ KeyWrapUtils.wrap(
+ secondaryKey.getSecretKey(),
+ mKeyHelper.getTertiaryKey(packageName, secondaryKey)));
+
+ restoreTask.getRestoreData(outputFd);
+ }
+
+ // TODO(b/142455725): Extract into a commong class.
+ private static class FileBackupServer implements CryptoBackupServer {
+ private static final String EMPTY_DOC_ID = "";
+
+ private final OutputStream mOutputStream;
+
+ FileBackupServer(OutputStream outputStream) {
+ mOutputStream = outputStream;
+ }
+
+ @Override
+ public String uploadIncrementalBackup(
+ String packageName,
+ String oldDocId,
+ byte[] diffScript,
+ WrappedKeyProto.WrappedKey tertiaryKey) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String uploadNonIncrementalBackup(
+ String packageName, byte[] data, WrappedKeyProto.WrappedKey tertiaryKey) {
+ try {
+ mOutputStream.write(data);
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to write encrypted data to file: ", e);
+ }
+
+ return EMPTY_DOC_ID;
+ }
+
+ @Override
+ public void setActiveSecondaryKeyAlias(
+ String keyAlias, Map<String, WrappedKeyProto.WrappedKey> tertiaryKeys) {
+ // Do nothing.
+ }
+ }
+
+ // TODO(b/142455725): Extract into a commong class.
+ private static class InputStreamFullRestoreDownloader extends FullRestoreDownloader {
+ private final InputStream mInputStream;
+
+ InputStreamFullRestoreDownloader(InputStream inputStream) {
+ mInputStream = inputStream;
+ }
+
+ @Override
+ public int readNextChunk(byte[] buffer) throws IOException {
+ return mInputStream.read(buffer);
+ }
+
+ @Override
+ public void finish(FinishType finishType) {
+ try {
+ mInputStream.close();
+ } catch (IOException e) {
+ Log.w(TAG, "Error while reading restore data");
+ }
+ }
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/StreamUtils.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/StreamUtils.java
new file mode 100644
index 000000000000..66be25b53a62
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/StreamUtils.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/** Utility methods for dealing with Streams */
+public class StreamUtils {
+ private static final int MAX_COPY_BUFFER_SIZE = 1024; // 1k copy buffer size.
+
+ /**
+ * Close a Closeable and silently ignore any IOExceptions.
+ *
+ * @param closeable The closeable to close
+ */
+ public static void closeQuietly(Closeable closeable) {
+ try {
+ closeable.close();
+ } catch (IOException ioe) {
+ // Silently ignore
+ }
+ }
+
+ /**
+ * Copy data from an InputStream to an OutputStream upto a given number of bytes.
+ *
+ * @param in The source InputStream
+ * @param out The destination OutputStream
+ * @param limit The maximum number of bytes to copy
+ * @throws IOException Thrown if there is a problem performing the copy.
+ */
+ public static void copyStream(InputStream in, OutputStream out, int limit) throws IOException {
+ int bufferSize = Math.min(MAX_COPY_BUFFER_SIZE, limit);
+ byte[] buffer = new byte[bufferSize];
+
+ int copied = 0;
+ while (copied < limit) {
+ int maxReadSize = Math.min(bufferSize, limit - copied);
+ int read = in.read(buffer, 0, maxReadSize);
+ if (read < 0) {
+ return; // Reached the stream end before the limit
+ }
+ out.write(buffer, 0, read);
+ copied += read;
+ }
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/BackupFileBuilder.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/BackupFileBuilder.java
new file mode 100644
index 000000000000..3d3fb552bb58
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/BackupFileBuilder.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.chunking;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.Preconditions.checkState;
+
+import android.annotation.Nullable;
+import android.util.Slog;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.chunk.ChunkListingMap;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Writes batches of {@link EncryptedChunk} to a diff script, and generates the associated {@link
+ * ChunksMetadataProto.ChunkListing} and {@link ChunksMetadataProto.ChunkOrdering}.
+ */
+public class BackupFileBuilder {
+ private static final String TAG = "BackupFileBuilder";
+
+ private static final int BYTES_PER_KILOBYTE = 1024;
+
+ private final BackupWriter mBackupWriter;
+ private final EncryptedChunkEncoder mEncryptedChunkEncoder;
+ private final ChunkListingMap mOldChunkListing;
+ private final ChunksMetadataProto.ChunkListing mNewChunkListing;
+ private final ChunksMetadataProto.ChunkOrdering mChunkOrdering;
+ private final List<ChunksMetadataProto.Chunk> mKnownChunks = new ArrayList<>();
+ private final List<Integer> mKnownStarts = new ArrayList<>();
+ private final Map<ChunkHash, Long> mChunkStartPositions;
+
+ private long mNewChunksSizeBytes;
+ private boolean mFinished;
+
+ /**
+ * Constructs a new instance which writes raw data to the given {@link OutputStream}, without
+ * generating a diff.
+ *
+ * <p>This class never closes the output stream.
+ */
+ public static BackupFileBuilder createForNonIncremental(OutputStream outputStream) {
+ return new BackupFileBuilder(
+ new RawBackupWriter(outputStream), new ChunksMetadataProto.ChunkListing());
+ }
+
+ /**
+ * Constructs a new instance which writes a diff script to the given {@link OutputStream} using
+ * a {@link SingleStreamDiffScriptWriter}.
+ *
+ * <p>This class never closes the output stream.
+ *
+ * @param oldChunkListing against which the diff will be generated.
+ */
+ public static BackupFileBuilder createForIncremental(
+ OutputStream outputStream, ChunksMetadataProto.ChunkListing oldChunkListing) {
+ return new BackupFileBuilder(
+ DiffScriptBackupWriter.newInstance(outputStream), oldChunkListing);
+ }
+
+ private BackupFileBuilder(
+ BackupWriter backupWriter, ChunksMetadataProto.ChunkListing oldChunkListing) {
+ this.mBackupWriter = backupWriter;
+ // TODO(b/77188289): Use InlineLengthsEncryptedChunkEncoder for key-value backups
+ this.mEncryptedChunkEncoder = new LengthlessEncryptedChunkEncoder();
+ this.mOldChunkListing = ChunkListingMap.fromProto(oldChunkListing);
+
+ mNewChunkListing = new ChunksMetadataProto.ChunkListing();
+ mNewChunkListing.cipherType = ChunksMetadataProto.AES_256_GCM;
+ mNewChunkListing.chunkOrderingType = ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED;
+
+ mChunkOrdering = new ChunksMetadataProto.ChunkOrdering();
+ mChunkStartPositions = new HashMap<>();
+ }
+
+ /**
+ * Writes the given chunks to the output stream, and adds them to the new chunk listing and
+ * chunk ordering.
+ *
+ * <p>Sorts the chunks in lexicographical order before writing.
+ *
+ * @param allChunks The hashes of all the chunks, in the order they appear in the plaintext.
+ * @param newChunks A map from hash to {@link EncryptedChunk} containing the new chunks not
+ * present in the previous backup.
+ */
+ public void writeChunks(List<ChunkHash> allChunks, Map<ChunkHash, EncryptedChunk> newChunks)
+ throws IOException {
+ checkState(!mFinished, "Cannot write chunks after flushing.");
+
+ List<ChunkHash> sortedChunks = new ArrayList<>(allChunks);
+ Collections.sort(sortedChunks);
+ for (ChunkHash chunkHash : sortedChunks) {
+ // As we have already included this chunk in the backup file, don't add it again to
+ // deduplicate identical chunks.
+ if (!mChunkStartPositions.containsKey(chunkHash)) {
+ // getBytesWritten() gives us the start of the chunk.
+ mChunkStartPositions.put(chunkHash, mBackupWriter.getBytesWritten());
+
+ writeChunkToFileAndListing(chunkHash, newChunks);
+ }
+ }
+
+ long totalSizeKb = mBackupWriter.getBytesWritten() / BYTES_PER_KILOBYTE;
+ long newChunksSizeKb = mNewChunksSizeBytes / BYTES_PER_KILOBYTE;
+ Slog.d(
+ TAG,
+ "Total backup size: "
+ + totalSizeKb
+ + " kb, new chunks size: "
+ + newChunksSizeKb
+ + " kb");
+
+ for (ChunkHash chunkHash : allChunks) {
+ mKnownStarts.add(mChunkStartPositions.get(chunkHash).intValue());
+ }
+ }
+
+ /**
+ * Returns a new listing for all of the chunks written so far, setting the given fingerprint
+ * mixer salt (this overrides the {@link ChunksMetadataProto.ChunkListing#fingerprintMixerSalt}
+ * in the old {@link ChunksMetadataProto.ChunkListing} passed into the
+ * {@link #BackupFileBuilder).
+ */
+ public ChunksMetadataProto.ChunkListing getNewChunkListing(
+ @Nullable byte[] fingerprintMixerSalt) {
+ // TODO: b/141537803 Add check to ensure this is called only once per instance
+ mNewChunkListing.fingerprintMixerSalt =
+ fingerprintMixerSalt != null
+ ? Arrays.copyOf(fingerprintMixerSalt, fingerprintMixerSalt.length)
+ : new byte[0];
+ mNewChunkListing.chunks = mKnownChunks.toArray(new ChunksMetadataProto.Chunk[0]);
+ return mNewChunkListing;
+ }
+
+ /** Returns a new ordering for all of the chunks written so far, setting the given checksum. */
+ public ChunksMetadataProto.ChunkOrdering getNewChunkOrdering(byte[] checksum) {
+ // TODO: b/141537803 Add check to ensure this is called only once per instance
+ mChunkOrdering.starts = new int[mKnownStarts.size()];
+ for (int i = 0; i < mKnownStarts.size(); i++) {
+ mChunkOrdering.starts[i] = mKnownStarts.get(i).intValue();
+ }
+ mChunkOrdering.checksum = Arrays.copyOf(checksum, checksum.length);
+ return mChunkOrdering;
+ }
+
+ /**
+ * Finishes the backup file by writing the chunk metadata and metadata position.
+ *
+ * <p>Once this is called, calling {@link #writeChunks(List, Map)} will throw {@link
+ * IllegalStateException}.
+ */
+ public void finish(ChunksMetadataProto.ChunksMetadata metadata) throws IOException {
+ checkNotNull(metadata, "Metadata cannot be null");
+
+ long startOfMetadata = mBackupWriter.getBytesWritten();
+ mBackupWriter.writeBytes(ChunksMetadataProto.ChunksMetadata.toByteArray(metadata));
+ mBackupWriter.writeBytes(toByteArray(startOfMetadata));
+
+ mBackupWriter.flush();
+ mFinished = true;
+ }
+
+ /**
+ * Checks if the given chunk hash references an existing chunk or a new chunk, and adds this
+ * chunk to the backup file and new chunk listing.
+ */
+ private void writeChunkToFileAndListing(
+ ChunkHash chunkHash, Map<ChunkHash, EncryptedChunk> newChunks) throws IOException {
+ checkNotNull(chunkHash, "Hash cannot be null");
+
+ if (mOldChunkListing.hasChunk(chunkHash)) {
+ ChunkListingMap.Entry oldChunk = mOldChunkListing.getChunkEntry(chunkHash);
+ mBackupWriter.writeChunk(oldChunk.getStart(), oldChunk.getLength());
+
+ checkArgument(oldChunk.getLength() >= 0, "Chunk must have zero or positive length");
+ addChunk(chunkHash.getHash(), oldChunk.getLength());
+ } else if (newChunks.containsKey(chunkHash)) {
+ EncryptedChunk newChunk = newChunks.get(chunkHash);
+ mEncryptedChunkEncoder.writeChunkToWriter(mBackupWriter, newChunk);
+ int length = mEncryptedChunkEncoder.getEncodedLengthOfChunk(newChunk);
+ mNewChunksSizeBytes += length;
+
+ checkArgument(length >= 0, "Chunk must have zero or positive length");
+ addChunk(chunkHash.getHash(), length);
+ } else {
+ throw new IllegalArgumentException(
+ "Chunk did not exist in old chunks or new chunks: " + chunkHash);
+ }
+ }
+
+ private void addChunk(byte[] chunkHash, int length) {
+ ChunksMetadataProto.Chunk chunk = new ChunksMetadataProto.Chunk();
+ chunk.hash = Arrays.copyOf(chunkHash, chunkHash.length);
+ chunk.length = length;
+ mKnownChunks.add(chunk);
+ }
+
+ private static byte[] toByteArray(long value) {
+ // Note that this code needs to stay compatible with GWT, which has known
+ // bugs when narrowing byte casts of long values occur.
+ byte[] result = new byte[8];
+ for (int i = 7; i >= 0; i--) {
+ result[i] = (byte) (value & 0xffL);
+ value >>= 8;
+ }
+ return result;
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ProtoStore.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ProtoStore.java
new file mode 100644
index 000000000000..3ba5f2b741b8
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ProtoStore.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.chunking;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.AtomicFile;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
+import com.android.server.backup.encryption.protos.nano.KeyValueListingProto;
+
+import com.google.protobuf.nano.MessageNano;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Optional;
+
+/**
+ * Stores a nano proto for each package, persisting the proto to disk.
+ *
+ * <p>This is used to store {@link ChunksMetadataProto.ChunkListing}.
+ *
+ * @param <T> the type of nano proto to store.
+ */
+public class ProtoStore<T extends MessageNano> {
+ private static final String CHUNK_LISTING_FOLDER = "backup_chunk_listings";
+ private static final String KEY_VALUE_LISTING_FOLDER = "backup_kv_listings";
+
+ private static final String TAG = "BupEncProtoStore";
+
+ private final File mStoreFolder;
+ private final Class<T> mClazz;
+
+ /** Creates a new instance which stores chunk listings at the default location. */
+ public static ProtoStore<ChunksMetadataProto.ChunkListing> createChunkListingStore(
+ Context context) throws IOException {
+ return new ProtoStore<>(
+ ChunksMetadataProto.ChunkListing.class,
+ new File(context.getFilesDir().getAbsoluteFile(), CHUNK_LISTING_FOLDER));
+ }
+
+ /** Creates a new instance which stores key value listings in the default location. */
+ public static ProtoStore<KeyValueListingProto.KeyValueListing> createKeyValueListingStore(
+ Context context) throws IOException {
+ return new ProtoStore<>(
+ KeyValueListingProto.KeyValueListing.class,
+ new File(context.getFilesDir().getAbsoluteFile(), KEY_VALUE_LISTING_FOLDER));
+ }
+
+ /**
+ * Creates a new instance which stores protos in the given folder.
+ *
+ * @param storeFolder The location where the serialized form is stored.
+ */
+ @VisibleForTesting
+ ProtoStore(Class<T> clazz, File storeFolder) throws IOException {
+ mClazz = checkNotNull(clazz);
+ mStoreFolder = ensureDirectoryExistsOrThrow(storeFolder);
+ }
+
+ private static File ensureDirectoryExistsOrThrow(File directory) throws IOException {
+ if (directory.exists() && !directory.isDirectory()) {
+ throw new IOException("Store folder already exists, but isn't a directory.");
+ }
+
+ if (!directory.exists() && !directory.mkdir()) {
+ throw new IOException("Unable to create store folder.");
+ }
+
+ return directory;
+ }
+
+ /**
+ * Returns the chunk listing for the given package, or {@link Optional#empty()} if no listing
+ * exists.
+ */
+ public Optional<T> loadProto(String packageName)
+ throws IOException, IllegalAccessException, InstantiationException,
+ NoSuchMethodException, InvocationTargetException {
+ File file = getFileForPackage(packageName);
+
+ if (!file.exists()) {
+ Slog.d(
+ TAG,
+ "No chunk listing existed for " + packageName + ", returning empty listing.");
+ return Optional.empty();
+ }
+
+ AtomicFile protoStore = new AtomicFile(file);
+ byte[] data = protoStore.readFully();
+
+ Constructor<T> constructor = mClazz.getDeclaredConstructor();
+ T proto = constructor.newInstance();
+ MessageNano.mergeFrom(proto, data);
+ return Optional.of(proto);
+ }
+
+ /** Saves a proto to disk, associating it with the given package. */
+ public void saveProto(String packageName, T proto) throws IOException {
+ checkNotNull(proto);
+ File file = getFileForPackage(packageName);
+
+ try (FileOutputStream os = new FileOutputStream(file)) {
+ os.write(MessageNano.toByteArray(proto));
+ } catch (IOException e) {
+ Slog.e(
+ TAG,
+ "Exception occurred when saving the listing for "
+ + packageName
+ + ", deleting saved listing.",
+ e);
+
+ // If a problem occurred when writing the listing then it might be corrupt, so delete
+ // it.
+ file.delete();
+
+ throw e;
+ }
+ }
+
+ /** Deletes the proto for the given package, or does nothing if the package has no proto. */
+ public void deleteProto(String packageName) {
+ File file = getFileForPackage(packageName);
+ file.delete();
+ }
+
+ /** Deletes every proto of this type, for all package names. */
+ public void deleteAllProtos() {
+ File[] files = mStoreFolder.listFiles();
+
+ // We ensure that the storeFolder exists in the constructor, but check just in case it has
+ // mysteriously disappeared.
+ if (files == null) {
+ return;
+ }
+
+ for (File file : files) {
+ file.delete();
+ }
+ }
+
+ private File getFileForPackage(String packageName) {
+ checkPackageName(packageName);
+ return new File(mStoreFolder, packageName);
+ }
+
+ private static void checkPackageName(String packageName) {
+ if (TextUtils.isEmpty(packageName) || packageName.contains("/")) {
+ throw new IllegalArgumentException(
+ "Package name must not contain '/' or be empty: " + packageName);
+ }
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/client/CryptoBackupServer.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/client/CryptoBackupServer.java
new file mode 100644
index 000000000000..d7f7dc7d0472
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/client/CryptoBackupServer.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.client;
+
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+
+import java.util.Map;
+
+/**
+ * Contains methods for communicating with the parts of the backup server relevant to encryption.
+ */
+public interface CryptoBackupServer {
+ /**
+ * Uploads an incremental backup to the server.
+ *
+ * <p>Handles setting up and tearing down the connection.
+ *
+ * @param packageName the package to associate the data with
+ * @param oldDocId the id of the previous backup doc in Drive
+ * @param diffScript containing the actual backup data
+ * @param tertiaryKey the wrapped key used to encrypt this backup
+ * @return the id of the new backup doc in Drive.
+ */
+ String uploadIncrementalBackup(
+ String packageName,
+ String oldDocId,
+ byte[] diffScript,
+ WrappedKeyProto.WrappedKey tertiaryKey);
+
+ /**
+ * Uploads non-incremental backup to the server.
+ *
+ * <p>Handles setting up and tearing down the connection.
+ *
+ * @param packageName the package to associate the data with
+ * @param data the actual backup data
+ * @param tertiaryKey the wrapped key used to encrypt this backup
+ * @return the id of the new backup doc in Drive.
+ */
+ String uploadNonIncrementalBackup(
+ String packageName, byte[] data, WrappedKeyProto.WrappedKey tertiaryKey);
+
+ /**
+ * Sets the alias of the active secondary key. This is the alias used to refer to the key in the
+ * {@link java.security.KeyStore}. It is also used to key storage for tertiary keys on the
+ * backup server. Also has to upload all existing tertiary keys, wrapped with the new key.
+ *
+ * @param keyAlias The ID of the secondary key.
+ * @param tertiaryKeys The tertiary keys, wrapped with the new secondary key.
+ */
+ void setActiveSecondaryKeyAlias(
+ String keyAlias, Map<String, WrappedKeyProto.WrappedKey> tertiaryKeys);
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/client/UnexpectedActiveSecondaryOnServerException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/client/UnexpectedActiveSecondaryOnServerException.java
new file mode 100644
index 000000000000..9e31385c9525
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/client/UnexpectedActiveSecondaryOnServerException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.client;
+
+/**
+ * Error thrown when the user attempts to retrieve a key set from the server, but is asking for keys
+ * from an inactive secondary.
+ *
+ * <p>Although we could just return old keys, there is no good reason to do this. It almost
+ * certainly indicates a logic error on the client.
+ */
+public class UnexpectedActiveSecondaryOnServerException extends Exception {
+ public UnexpectedActiveSecondaryOnServerException(String message) {
+ super(message);
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutput.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutput.java
new file mode 100644
index 000000000000..56e1c053d8e3
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutput.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.kv;
+
+import static com.android.internal.util.Preconditions.checkState;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.chunking.ChunkHasher;
+import com.android.server.backup.encryption.protos.nano.KeyValuePairProto;
+import com.android.server.backup.encryption.tasks.DecryptedChunkOutput;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Builds a key value backup set from plaintext chunks. Computes a digest over the sorted SHA-256
+ * hashes of the chunks.
+ */
+public class DecryptedChunkKvOutput implements DecryptedChunkOutput {
+ @VisibleForTesting static final String DIGEST_ALGORITHM = "SHA-256";
+
+ private final ChunkHasher mChunkHasher;
+ private final List<KeyValuePairProto.KeyValuePair> mUnsortedPairs = new ArrayList<>();
+ private final List<ChunkHash> mUnsortedHashes = new ArrayList<>();
+ private boolean mClosed;
+
+ /** Constructs a new instance which computers the digest using the given hasher. */
+ public DecryptedChunkKvOutput(ChunkHasher chunkHasher) {
+ mChunkHasher = chunkHasher;
+ }
+
+ @Override
+ public DecryptedChunkOutput open() {
+ // As we don't have any resources there is nothing to open.
+ return this;
+ }
+
+ @Override
+ public void processChunk(byte[] plaintextBuffer, int length)
+ throws IOException, InvalidKeyException {
+ checkState(!mClosed, "Cannot process chunk after close()");
+ KeyValuePairProto.KeyValuePair kvPair = new KeyValuePairProto.KeyValuePair();
+ KeyValuePairProto.KeyValuePair.mergeFrom(kvPair, plaintextBuffer, 0, length);
+ mUnsortedPairs.add(kvPair);
+ // TODO(b/71492289): Update ChunkHasher to accept offset and length so we don't have to copy
+ // the buffer into a smaller array.
+ mUnsortedHashes.add(mChunkHasher.computeHash(Arrays.copyOf(plaintextBuffer, length)));
+ }
+
+ @Override
+ public void close() {
+ // As we don't have any resources there is nothing to close.
+ mClosed = true;
+ }
+
+ @Override
+ public byte[] getDigest() throws NoSuchAlgorithmException {
+ checkState(mClosed, "Must close() before getDigest()");
+ MessageDigest digest = getMessageDigest();
+ Collections.sort(mUnsortedHashes);
+ for (ChunkHash hash : mUnsortedHashes) {
+ digest.update(hash.getHash());
+ }
+ return digest.digest();
+ }
+
+ private static MessageDigest getMessageDigest() throws NoSuchAlgorithmException {
+ return MessageDigest.getInstance(DIGEST_ALGORITHM);
+ }
+
+ /**
+ * Returns the key value pairs from the backup, sorted lexicographically by key.
+ *
+ * <p>You must call {@link #close} first.
+ */
+ public List<KeyValuePairProto.KeyValuePair> getPairs() {
+ checkState(mClosed, "Must close() before getPairs()");
+ Collections.sort(
+ mUnsortedPairs,
+ new Comparator<KeyValuePairProto.KeyValuePair>() {
+ @Override
+ public int compare(
+ KeyValuePairProto.KeyValuePair o1, KeyValuePairProto.KeyValuePair o2) {
+ return o1.key.compareTo(o2.key);
+ }
+ });
+ return mUnsortedPairs;
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java
new file mode 100644
index 000000000000..b3518e144ce3
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.kv;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.protos.nano.KeyValueListingProto;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Builds a {@link KeyValueListingProto.KeyValueListing}, which is a nano proto and so has no
+ * builder.
+ */
+public class KeyValueListingBuilder {
+ private final List<KeyValueListingProto.KeyValueEntry> mEntries = new ArrayList<>();
+
+ /** Adds a new pair entry to the listing. */
+ public KeyValueListingBuilder addPair(String key, ChunkHash hash) {
+ checkArgument(key.length() != 0, "Key must have non-zero length");
+ checkNotNull(hash, "Hash must not be null");
+
+ KeyValueListingProto.KeyValueEntry entry = new KeyValueListingProto.KeyValueEntry();
+ entry.key = key;
+ entry.hash = hash.getHash();
+ mEntries.add(entry);
+
+ return this;
+ }
+
+ /** Adds all pairs contained in a map, where the map is from key to hash. */
+ public KeyValueListingBuilder addAll(Map<String, ChunkHash> map) {
+ for (Entry<String, ChunkHash> entry : map.entrySet()) {
+ addPair(entry.getKey(), entry.getValue());
+ }
+
+ return this;
+ }
+
+ /** Returns a new listing containing all the pairs added so far. */
+ public KeyValueListingProto.KeyValueListing build() {
+ if (mEntries.size() == 0) {
+ return emptyListing();
+ }
+
+ KeyValueListingProto.KeyValueListing listing = new KeyValueListingProto.KeyValueListing();
+ listing.entries = new KeyValueListingProto.KeyValueEntry[mEntries.size()];
+ mEntries.toArray(listing.entries);
+ return listing;
+ }
+
+ /** Returns a new listing which does not contain any pairs. */
+ public static KeyValueListingProto.KeyValueListing emptyListing() {
+ KeyValueListingProto.KeyValueListing listing = new KeyValueListingProto.KeyValueListing();
+ listing.entries = KeyValueListingProto.KeyValueEntry.emptyArray();
+ return listing;
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/ActiveSecondaryNotInKeychainException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/ActiveSecondaryNotInKeychainException.java
new file mode 100644
index 000000000000..2e8a61f05970
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/ActiveSecondaryNotInKeychainException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+/**
+ * Error thrown when the server's active secondary key does not exist in the user's recoverable
+ * keychain. This means the backup data cannot be decrypted, and should be wiped.
+ */
+public class ActiveSecondaryNotInKeychainException extends Exception {
+ public ActiveSecondaryNotInKeychainException(String message) {
+ super(message);
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/BackupFileDecryptorTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/BackupFileDecryptorTask.java
new file mode 100644
index 000000000000..9bf148ddc901
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/BackupFileDecryptorTask.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import android.util.Slog;
+import android.util.SparseIntArray;
+
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunkOrdering;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunksMetadata;
+
+import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.Locale;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.GCMParameterSpec;
+
+/**
+ * A backup file consists of, in order:
+ *
+ * <ul>
+ * <li>A randomly ordered sequence of encrypted chunks
+ * <li>A plaintext {@link ChunksMetadata} proto, containing the bytes of an encrypted {@link
+ * ChunkOrdering} proto.
+ * <li>A 64-bit long denoting the offset of the file at which the ChunkOrdering proto starts.
+ * </ul>
+ *
+ * <p>This task decrypts such a blob and writes the plaintext to another file.
+ *
+ * <p>The backup file has two formats to indicate the boundaries of the chunks in the encrypted
+ * file. In {@link ChunksMetadataProto#EXPLICIT_STARTS} mode the chunk ordering contains the start
+ * positions of each chunk and the decryptor outputs the chunks in the order they appeared in the
+ * plaintext file. In {@link ChunksMetadataProto#INLINE_LENGTHS} mode the length of each encrypted
+ * chunk is prepended to the chunk in the file and the decryptor outputs the chunks in no specific
+ * order.
+ *
+ * <p>{@link ChunksMetadataProto#EXPLICIT_STARTS} is for use with full backup (Currently used for
+ * all backups as b/77188289 is not implemented yet), {@link ChunksMetadataProto#INLINE_LENGTHS}
+ * will be used for kv backup (once b/77188289 is implemented) to avoid re-uploading the chunk
+ * ordering (see b/70782620).
+ */
+public class BackupFileDecryptorTask {
+ private static final String TAG = "BackupFileDecryptorTask";
+
+ private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
+ private static final int GCM_NONCE_LENGTH_BYTES = 12;
+ private static final int GCM_TAG_LENGTH_BYTES = 16;
+ private static final int BITS_PER_BYTE = 8;
+ private static final String READ_MODE = "r";
+ private static final int BYTES_PER_LONG = 64 / BITS_PER_BYTE;
+
+ private final Cipher mCipher;
+ private final SecretKey mSecretKey;
+
+ /**
+ * A new instance.
+ *
+ * @param secretKey The tertiary key used to encrypt the backup blob.
+ */
+ public BackupFileDecryptorTask(SecretKey secretKey)
+ throws NoSuchPaddingException, NoSuchAlgorithmException {
+ this.mCipher = Cipher.getInstance(CIPHER_ALGORITHM);
+ this.mSecretKey = secretKey;
+ }
+
+ /**
+ * Runs the task, reading the encrypted data from {@code input} and writing the plaintext data
+ * to {@code output}.
+ *
+ * @param inputFile The encrypted backup file.
+ * @param decryptedChunkOutput Unopened output to write the plaintext to, which this class will
+ * open and close during decryption.
+ * @throws IOException if an error occurred reading the encrypted file or writing the plaintext,
+ * or if one of the protos could not be deserialized.
+ */
+ public void decryptFile(File inputFile, DecryptedChunkOutput decryptedChunkOutput)
+ throws IOException, EncryptedRestoreException, IllegalBlockSizeException,
+ BadPaddingException, InvalidAlgorithmParameterException, InvalidKeyException,
+ ShortBufferException, NoSuchAlgorithmException {
+ RandomAccessFile input = new RandomAccessFile(inputFile, READ_MODE);
+
+ long metadataOffset = getChunksMetadataOffset(input);
+ ChunksMetadataProto.ChunksMetadata chunksMetadata =
+ getChunksMetadata(input, metadataOffset);
+ ChunkOrdering chunkOrdering = decryptChunkOrdering(chunksMetadata);
+
+ if (chunksMetadata.chunkOrderingType == ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED
+ || chunksMetadata.chunkOrderingType == ChunksMetadataProto.EXPLICIT_STARTS) {
+ Slog.d(TAG, "Using explicit starts");
+ decryptFileWithExplicitStarts(
+ input, decryptedChunkOutput, chunkOrdering, metadataOffset);
+
+ } else if (chunksMetadata.chunkOrderingType == ChunksMetadataProto.INLINE_LENGTHS) {
+ Slog.d(TAG, "Using inline lengths");
+ decryptFileWithInlineLengths(input, decryptedChunkOutput, metadataOffset);
+
+ } else {
+ throw new UnsupportedEncryptedFileException(
+ "Unknown chunk ordering type:" + chunksMetadata.chunkOrderingType);
+ }
+
+ if (!Arrays.equals(decryptedChunkOutput.getDigest(), chunkOrdering.checksum)) {
+ throw new MessageDigestMismatchException("Checksums did not match");
+ }
+ }
+
+ private void decryptFileWithExplicitStarts(
+ RandomAccessFile input,
+ DecryptedChunkOutput decryptedChunkOutput,
+ ChunkOrdering chunkOrdering,
+ long metadataOffset)
+ throws IOException, InvalidKeyException, IllegalBlockSizeException,
+ InvalidAlgorithmParameterException, ShortBufferException, BadPaddingException,
+ NoSuchAlgorithmException {
+ SparseIntArray chunkLengthsByPosition =
+ getChunkLengths(chunkOrdering.starts, (int) metadataOffset);
+ int largestChunkLength = getLargestChunkLength(chunkLengthsByPosition);
+ byte[] encryptedChunkBuffer = new byte[largestChunkLength];
+ // largestChunkLength is 0 if the backup file contains zero chunks e.g. 0 kv pairs.
+ int plaintextBufferLength =
+ Math.max(0, largestChunkLength - GCM_NONCE_LENGTH_BYTES - GCM_TAG_LENGTH_BYTES);
+ byte[] plaintextChunkBuffer = new byte[plaintextBufferLength];
+
+ try (DecryptedChunkOutput output = decryptedChunkOutput.open()) {
+ for (int start : chunkOrdering.starts) {
+ int length = chunkLengthsByPosition.get(start);
+
+ input.seek(start);
+ input.readFully(encryptedChunkBuffer, 0, length);
+ int plaintextLength =
+ decryptChunk(encryptedChunkBuffer, length, plaintextChunkBuffer);
+ outputChunk(output, plaintextChunkBuffer, plaintextLength);
+ }
+ }
+ }
+
+ private void decryptFileWithInlineLengths(
+ RandomAccessFile input, DecryptedChunkOutput decryptedChunkOutput, long metadataOffset)
+ throws MalformedEncryptedFileException, IOException, IllegalBlockSizeException,
+ BadPaddingException, InvalidAlgorithmParameterException, ShortBufferException,
+ InvalidKeyException, NoSuchAlgorithmException {
+ input.seek(0);
+ try (DecryptedChunkOutput output = decryptedChunkOutput.open()) {
+ while (input.getFilePointer() < metadataOffset) {
+ long start = input.getFilePointer();
+ int encryptedChunkLength = input.readInt();
+
+ if (encryptedChunkLength <= 0) {
+ // If the length of the encrypted chunk is not positive we will not make
+ // progress reading the file and so will loop forever.
+ throw new MalformedEncryptedFileException(
+ "Encrypted chunk length not positive:" + encryptedChunkLength);
+ }
+
+ if (start + encryptedChunkLength > metadataOffset) {
+ throw new MalformedEncryptedFileException(
+ String.format(
+ Locale.US,
+ "Encrypted chunk longer (%d) than file (%d)",
+ encryptedChunkLength,
+ metadataOffset));
+ }
+
+ byte[] plaintextChunk = new byte[encryptedChunkLength];
+ byte[] plaintext =
+ new byte
+ [encryptedChunkLength
+ - GCM_NONCE_LENGTH_BYTES
+ - GCM_TAG_LENGTH_BYTES];
+
+ input.readFully(plaintextChunk);
+
+ int plaintextChunkLength =
+ decryptChunk(plaintextChunk, encryptedChunkLength, plaintext);
+ outputChunk(output, plaintext, plaintextChunkLength);
+ }
+ }
+ }
+
+ private void outputChunk(
+ DecryptedChunkOutput output, byte[] plaintextChunkBuffer, int plaintextLength)
+ throws IOException, InvalidKeyException, NoSuchAlgorithmException {
+ output.processChunk(plaintextChunkBuffer, plaintextLength);
+ }
+
+ /**
+ * Decrypts chunk and returns the length of the plaintext.
+ *
+ * @param encryptedChunkBuffer The encrypted data, prefixed by the nonce.
+ * @param encryptedChunkBufferLength The length of the encrypted chunk (including nonce).
+ * @param plaintextChunkBuffer The buffer into which to write the plaintext chunk.
+ * @return The length of the plaintext chunk.
+ */
+ private int decryptChunk(
+ byte[] encryptedChunkBuffer,
+ int encryptedChunkBufferLength,
+ byte[] plaintextChunkBuffer)
+ throws InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException,
+ ShortBufferException, IllegalBlockSizeException {
+
+ mCipher.init(
+ Cipher.DECRYPT_MODE,
+ mSecretKey,
+ new GCMParameterSpec(
+ GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE,
+ encryptedChunkBuffer,
+ 0,
+ GCM_NONCE_LENGTH_BYTES));
+
+ return mCipher.doFinal(
+ encryptedChunkBuffer,
+ GCM_NONCE_LENGTH_BYTES,
+ encryptedChunkBufferLength - GCM_NONCE_LENGTH_BYTES,
+ plaintextChunkBuffer);
+ }
+
+ /** Given all the lengths, returns the largest length. */
+ private int getLargestChunkLength(SparseIntArray lengths) {
+ int maxSeen = 0;
+ for (int i = 0; i < lengths.size(); i++) {
+ maxSeen = Math.max(maxSeen, lengths.valueAt(i));
+ }
+ return maxSeen;
+ }
+
+ /**
+ * From a list of the starting position of each chunk in the correct order of the backup data,
+ * calculates a mapping from start position to length of that chunk.
+ *
+ * @param starts The start positions of chunks, in order.
+ * @param chunkOrderingPosition Where the {@link ChunkOrdering} proto starts, used to calculate
+ * the length of the last chunk.
+ * @return The mapping.
+ */
+ private SparseIntArray getChunkLengths(int[] starts, int chunkOrderingPosition) {
+ int[] boundaries = Arrays.copyOf(starts, starts.length + 1);
+ boundaries[boundaries.length - 1] = chunkOrderingPosition;
+ Arrays.sort(boundaries);
+
+ SparseIntArray lengths = new SparseIntArray();
+ for (int i = 0; i < boundaries.length - 1; i++) {
+ lengths.put(boundaries[i], boundaries[i + 1] - boundaries[i]);
+ }
+ return lengths;
+ }
+
+ /**
+ * Reads and decrypts the {@link ChunkOrdering} from the {@link ChunksMetadata}.
+ *
+ * @param metadata The metadata.
+ * @return The ordering.
+ * @throws InvalidProtocolBufferNanoException if there is an issue deserializing the proto.
+ */
+ private ChunkOrdering decryptChunkOrdering(ChunksMetadata metadata)
+ throws InvalidProtocolBufferNanoException, InvalidAlgorithmParameterException,
+ InvalidKeyException, BadPaddingException, IllegalBlockSizeException,
+ UnsupportedEncryptedFileException {
+ assertCryptoSupported(metadata);
+
+ mCipher.init(
+ Cipher.DECRYPT_MODE,
+ mSecretKey,
+ new GCMParameterSpec(
+ GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE,
+ metadata.chunkOrdering,
+ 0,
+ GCM_NONCE_LENGTH_BYTES));
+
+ byte[] decrypted =
+ mCipher.doFinal(
+ metadata.chunkOrdering,
+ GCM_NONCE_LENGTH_BYTES,
+ metadata.chunkOrdering.length - GCM_NONCE_LENGTH_BYTES);
+
+ return ChunkOrdering.parseFrom(decrypted);
+ }
+
+ /**
+ * Asserts that the Cipher and MessageDigest algorithms in the backup metadata are supported.
+ * For now we only support SHA-256 for checksum and 256-bit AES/GCM/NoPadding for the Cipher.
+ *
+ * @param chunksMetadata The file metadata.
+ * @throws UnsupportedEncryptedFileException if any algorithm is unsupported.
+ */
+ private void assertCryptoSupported(ChunksMetadata chunksMetadata)
+ throws UnsupportedEncryptedFileException {
+ if (chunksMetadata.checksumType != ChunksMetadataProto.SHA_256) {
+ // For now we only support SHA-256.
+ throw new UnsupportedEncryptedFileException(
+ "Unrecognized checksum type for backup (this version of backup only supports"
+ + " SHA-256): "
+ + chunksMetadata.checksumType);
+ }
+
+ if (chunksMetadata.cipherType != ChunksMetadataProto.AES_256_GCM) {
+ throw new UnsupportedEncryptedFileException(
+ "Unrecognized cipher type for backup (this version of backup only supports"
+ + " AES-256-GCM: "
+ + chunksMetadata.cipherType);
+ }
+ }
+
+ /**
+ * Reads the offset of the {@link ChunksMetadata} proto from the end of the file.
+ *
+ * @return The offset.
+ * @throws IOException if there is an error reading.
+ */
+ private long getChunksMetadataOffset(RandomAccessFile input) throws IOException {
+ input.seek(input.length() - BYTES_PER_LONG);
+ return input.readLong();
+ }
+
+ /**
+ * Reads the {@link ChunksMetadata} proto from the given position in the file.
+ *
+ * @param input The encrypted file.
+ * @param position The position where the proto starts.
+ * @return The proto.
+ * @throws IOException if there is an issue reading the file or deserializing the proto.
+ */
+ private ChunksMetadata getChunksMetadata(RandomAccessFile input, long position)
+ throws IOException, MalformedEncryptedFileException {
+ long length = input.length();
+ if (position >= length || position < 0) {
+ throw new MalformedEncryptedFileException(
+ String.format(
+ Locale.US,
+ "%d is not valid position for chunks metadata in file of %d bytes",
+ position,
+ length));
+ }
+
+ // Read chunk ordering bytes
+ input.seek(position);
+ long chunksMetadataLength = input.length() - BYTES_PER_LONG - position;
+ byte[] chunksMetadataBytes = new byte[(int) chunksMetadataLength];
+ input.readFully(chunksMetadataBytes);
+
+ try {
+ return ChunksMetadata.parseFrom(chunksMetadataBytes);
+ } catch (InvalidProtocolBufferNanoException e) {
+ throw new MalformedEncryptedFileException(
+ String.format(
+ Locale.US,
+ "Could not read chunks metadata at position %d of file of %d bytes",
+ position,
+ length));
+ }
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/ClearCryptoStateTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/ClearCryptoStateTask.java
new file mode 100644
index 000000000000..8f35db69f11e
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/ClearCryptoStateTask.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import android.content.Context;
+import android.util.Slog;
+
+import com.android.server.backup.encryption.CryptoSettings;
+import com.android.server.backup.encryption.chunking.ProtoStore;
+import com.android.server.backup.encryption.storage.BackupEncryptionDb;
+import com.android.server.backup.encryption.storage.EncryptionDbException;
+
+import java.io.IOException;
+
+/**
+ * Task to clear local crypto state.
+ *
+ * <p>Needs to run whenever the user changes their backup account.
+ */
+public class ClearCryptoStateTask {
+ private static final String TAG = "ClearCryptoStateTask";
+
+ private final Context mContext;
+ private final CryptoSettings mCryptoSettings;
+
+ /**
+ * A new instance.
+ *
+ * @param context for finding local storage.
+ * @param cryptoSettings to clear
+ */
+ public ClearCryptoStateTask(Context context, CryptoSettings cryptoSettings) {
+ mContext = context;
+ mCryptoSettings = cryptoSettings;
+ }
+
+ /** Deletes all local state for backup (not restore). */
+ public void run() {
+ Slog.d(TAG, "Clearing local crypto state.");
+ try {
+ BackupEncryptionDb.newInstance(mContext).clear();
+ } catch (EncryptionDbException e) {
+ Slog.e(TAG, "Error clearing encryption database", e);
+ }
+ mCryptoSettings.clearAllSettingsForBackup();
+ try {
+ ProtoStore.createChunkListingStore(mContext).deleteAllProtos();
+ } catch (IOException e) {
+ Slog.e(TAG, "Error clearing chunk listing store", e);
+ }
+ try {
+ ProtoStore.createKeyValueListingStore(mContext).deleteAllProtos();
+ } catch (IOException e) {
+ Slog.e(TAG, "Error clearing key-value store", e);
+ }
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java
index e3df3c1eb96f..f67f1007f632 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java
@@ -19,6 +19,7 @@ package com.android.server.backup.encryption.tasks;
import java.io.Closeable;
import java.io.IOException;
import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
/**
* Accepts the plaintext bytes of decrypted chunks and writes them to some output. Also keeps track
@@ -30,7 +31,7 @@ public interface DecryptedChunkOutput extends Closeable {
*
* @return {@code this}, to allow use with try-with-resources
*/
- DecryptedChunkOutput open() throws IOException;
+ DecryptedChunkOutput open() throws IOException, NoSuchAlgorithmException;
/**
* Writes the plaintext bytes of chunk to whatever output the implementation chooses. Also
@@ -43,12 +44,13 @@ public interface DecryptedChunkOutput extends Closeable {
* at index 0.
* @param length The length in bytes of the plaintext contained in {@code plaintextBuffer}.
*/
- void processChunk(byte[] plaintextBuffer, int length) throws IOException, InvalidKeyException;
+ void processChunk(byte[] plaintextBuffer, int length)
+ throws IOException, InvalidKeyException, NoSuchAlgorithmException;
/**
* Returns the message digest of all the chunks processed by {@link #processChunk}.
*
* <p>You must call {@link Closeable#close()} before calling this method.
*/
- byte[] getDigest();
+ byte[] getDigest() throws NoSuchAlgorithmException;
}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedBackupTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedBackupTask.java
new file mode 100644
index 000000000000..ef13f23e799d
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedBackupTask.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import android.annotation.Nullable;
+import android.annotation.TargetApi;
+import android.os.Build.VERSION_CODES;
+import android.util.Slog;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.chunking.BackupFileBuilder;
+import com.android.server.backup.encryption.chunking.EncryptedChunk;
+import com.android.server.backup.encryption.client.CryptoBackupServer;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.GCMParameterSpec;
+
+/**
+ * Task which reads encrypted chunks from a {@link BackupEncrypter}, builds a backup file and
+ * uploads it to the server.
+ */
+@TargetApi(VERSION_CODES.P)
+public class EncryptedBackupTask {
+ private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
+ private static final int GCM_NONCE_LENGTH_BYTES = 12;
+ private static final int GCM_TAG_LENGTH_BYTES = 16;
+ private static final int BITS_PER_BYTE = 8;
+
+ private static final String TAG = "EncryptedBackupTask";
+
+ private final CryptoBackupServer mCryptoBackupServer;
+ private final SecureRandom mSecureRandom;
+ private final String mPackageName;
+ private final ByteArrayOutputStream mBackupDataOutput;
+ private final BackupEncrypter mBackupEncrypter;
+ private final AtomicBoolean mCancelled;
+
+ /** Creates a new instance which reads data from the given input stream. */
+ public EncryptedBackupTask(
+ CryptoBackupServer cryptoBackupServer,
+ SecureRandom secureRandom,
+ String packageName,
+ BackupEncrypter backupEncrypter) {
+ mCryptoBackupServer = cryptoBackupServer;
+ mSecureRandom = secureRandom;
+ mPackageName = packageName;
+ mBackupEncrypter = backupEncrypter;
+
+ mBackupDataOutput = new ByteArrayOutputStream();
+ mCancelled = new AtomicBoolean(false);
+ }
+
+ /**
+ * Creates a non-incremental backup file and uploads it to the server.
+ *
+ * @param fingerprintMixerSalt Fingerprint mixer salt used for content-defined chunking during a
+ * full backup. May be {@code null} for a key-value backup.
+ */
+ public ChunksMetadataProto.ChunkListing performNonIncrementalBackup(
+ SecretKey tertiaryKey,
+ WrappedKeyProto.WrappedKey wrappedTertiaryKey,
+ @Nullable byte[] fingerprintMixerSalt)
+ throws IOException, GeneralSecurityException {
+
+ ChunksMetadataProto.ChunkListing newChunkListing =
+ performBackup(
+ tertiaryKey,
+ fingerprintMixerSalt,
+ BackupFileBuilder.createForNonIncremental(mBackupDataOutput),
+ new HashSet<>());
+
+ throwIfCancelled();
+
+ newChunkListing.documentId =
+ mCryptoBackupServer.uploadNonIncrementalBackup(
+ mPackageName, mBackupDataOutput.toByteArray(), wrappedTertiaryKey);
+
+ return newChunkListing;
+ }
+
+ /** Creates an incremental backup file and uploads it to the server. */
+ public ChunksMetadataProto.ChunkListing performIncrementalBackup(
+ SecretKey tertiaryKey,
+ WrappedKeyProto.WrappedKey wrappedTertiaryKey,
+ ChunksMetadataProto.ChunkListing oldChunkListing)
+ throws IOException, GeneralSecurityException {
+
+ ChunksMetadataProto.ChunkListing newChunkListing =
+ performBackup(
+ tertiaryKey,
+ oldChunkListing.fingerprintMixerSalt,
+ BackupFileBuilder.createForIncremental(mBackupDataOutput, oldChunkListing),
+ getChunkHashes(oldChunkListing));
+
+ throwIfCancelled();
+
+ String oldDocumentId = oldChunkListing.documentId;
+ Slog.v(TAG, "Old doc id: " + oldDocumentId);
+
+ newChunkListing.documentId =
+ mCryptoBackupServer.uploadIncrementalBackup(
+ mPackageName,
+ oldDocumentId,
+ mBackupDataOutput.toByteArray(),
+ wrappedTertiaryKey);
+ return newChunkListing;
+ }
+
+ /**
+ * Signals to the task that the backup has been cancelled. If the upload has not yet started
+ * then the task will not upload any data to the server or save the new chunk listing.
+ */
+ public void cancel() {
+ mCancelled.getAndSet(true);
+ }
+
+ private void throwIfCancelled() {
+ if (mCancelled.get()) {
+ throw new CancellationException("EncryptedBackupTask was cancelled");
+ }
+ }
+
+ private ChunksMetadataProto.ChunkListing performBackup(
+ SecretKey tertiaryKey,
+ @Nullable byte[] fingerprintMixerSalt,
+ BackupFileBuilder backupFileBuilder,
+ Set<ChunkHash> existingChunkHashes)
+ throws IOException, GeneralSecurityException {
+ BackupEncrypter.Result result =
+ mBackupEncrypter.backup(tertiaryKey, fingerprintMixerSalt, existingChunkHashes);
+ backupFileBuilder.writeChunks(result.getAllChunks(), buildChunkMap(result.getNewChunks()));
+
+ ChunksMetadataProto.ChunkOrdering chunkOrdering =
+ backupFileBuilder.getNewChunkOrdering(result.getDigest());
+ backupFileBuilder.finish(buildMetadata(tertiaryKey, chunkOrdering));
+
+ return backupFileBuilder.getNewChunkListing(fingerprintMixerSalt);
+ }
+
+ /** Returns a set containing the hashes of every chunk in the given listing. */
+ private static Set<ChunkHash> getChunkHashes(ChunksMetadataProto.ChunkListing chunkListing) {
+ Set<ChunkHash> hashes = new HashSet<>();
+ for (ChunksMetadataProto.Chunk chunk : chunkListing.chunks) {
+ hashes.add(new ChunkHash(chunk.hash));
+ }
+ return hashes;
+ }
+
+ /** Returns a map from chunk hash to chunk containing every chunk in the given list. */
+ private static Map<ChunkHash, EncryptedChunk> buildChunkMap(List<EncryptedChunk> chunks) {
+ Map<ChunkHash, EncryptedChunk> chunkMap = new HashMap<>();
+ for (EncryptedChunk chunk : chunks) {
+ chunkMap.put(chunk.key(), chunk);
+ }
+ return chunkMap;
+ }
+
+ private ChunksMetadataProto.ChunksMetadata buildMetadata(
+ SecretKey tertiaryKey, ChunksMetadataProto.ChunkOrdering chunkOrdering)
+ throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException,
+ InvalidAlgorithmParameterException, NoSuchAlgorithmException,
+ ShortBufferException, NoSuchPaddingException {
+ ChunksMetadataProto.ChunksMetadata metaData = new ChunksMetadataProto.ChunksMetadata();
+ metaData.cipherType = ChunksMetadataProto.AES_256_GCM;
+ metaData.checksumType = ChunksMetadataProto.SHA_256;
+ metaData.chunkOrdering = encryptChunkOrdering(tertiaryKey, chunkOrdering);
+ return metaData;
+ }
+
+ private byte[] encryptChunkOrdering(
+ SecretKey tertiaryKey, ChunksMetadataProto.ChunkOrdering chunkOrdering)
+ throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException,
+ NoSuchPaddingException, NoSuchAlgorithmException,
+ InvalidAlgorithmParameterException, ShortBufferException {
+ Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
+
+ byte[] nonce = generateNonce();
+
+ cipher.init(
+ Cipher.ENCRYPT_MODE,
+ tertiaryKey,
+ new GCMParameterSpec(GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE, nonce));
+
+ byte[] orderingBytes = ChunksMetadataProto.ChunkOrdering.toByteArray(chunkOrdering);
+ // We prepend the nonce to the ordering.
+ byte[] output =
+ Arrays.copyOf(
+ nonce,
+ GCM_NONCE_LENGTH_BYTES + orderingBytes.length + GCM_TAG_LENGTH_BYTES);
+
+ cipher.doFinal(
+ orderingBytes,
+ /*inputOffset=*/ 0,
+ /*inputLen=*/ orderingBytes.length,
+ output,
+ /*outputOffset=*/ GCM_NONCE_LENGTH_BYTES);
+
+ return output;
+ }
+
+ private byte[] generateNonce() {
+ byte[] nonce = new byte[GCM_NONCE_LENGTH_BYTES];
+ mSecureRandom.nextBytes(nonce);
+ return nonce;
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessor.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessor.java
new file mode 100644
index 000000000000..0baec8b0a450
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessor.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.Preconditions.checkState;
+
+import android.annotation.Nullable;
+import android.app.backup.BackupTransport;
+import android.content.Context;
+import android.util.Slog;
+
+import com.android.server.backup.encryption.FullBackupDataProcessor;
+import com.android.server.backup.encryption.StreamUtils;
+import com.android.server.backup.encryption.client.CryptoBackupServer;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.security.SecureRandom;
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+/**
+ * Accepts backup data from a {@link InputStream} and passes it to the encrypted full data backup
+ * path.
+ */
+public class EncryptedFullBackupDataProcessor implements FullBackupDataProcessor {
+
+ private static final String TAG = "EncryptedFullBackupDP";
+
+ private final Context mContext;
+ private final ExecutorService mExecutorService;
+ private final CryptoBackupServer mCryptoBackupServer;
+ private final SecureRandom mSecureRandom;
+ private final RecoverableKeyStoreSecondaryKey mSecondaryKey;
+ private final String mPackageName;
+
+ @Nullable private InputStream mInputStream;
+ @Nullable private PipedOutputStream mOutputStream;
+ @Nullable private EncryptedFullBackupTask mBackupTask;
+ @Nullable private Future<Void> mBackupTaskFuture;
+ @Nullable private FullBackupCallbacks mFullBackupCallbacks;
+
+ public EncryptedFullBackupDataProcessor(
+ Context context,
+ ExecutorService executorService,
+ CryptoBackupServer cryptoBackupServer,
+ SecureRandom secureRandom,
+ RecoverableKeyStoreSecondaryKey secondaryKey,
+ String packageName) {
+ mContext = checkNotNull(context);
+ mExecutorService = checkNotNull(executorService);
+ mCryptoBackupServer = checkNotNull(cryptoBackupServer);
+ mSecureRandom = checkNotNull(secureRandom);
+ mSecondaryKey = checkNotNull(secondaryKey);
+ mPackageName = checkNotNull(packageName);
+ }
+
+ @Override
+ public boolean initiate(InputStream inputStream) throws IOException {
+ checkState(mBackupTask == null, "initiate() twice");
+
+ this.mInputStream = inputStream;
+ mOutputStream = new PipedOutputStream();
+
+ mBackupTask =
+ EncryptedFullBackupTask.newInstance(
+ mContext,
+ mCryptoBackupServer,
+ mSecureRandom,
+ mSecondaryKey,
+ mPackageName,
+ new PipedInputStream(mOutputStream));
+
+ return true;
+ }
+
+ @Override
+ public void start() {
+ checkState(mBackupTask != null, "start() before initiate()");
+ mBackupTaskFuture = mExecutorService.submit(mBackupTask);
+ }
+
+ @Override
+ public int pushData(int numBytes) {
+ checkState(
+ mBackupTaskFuture != null && mInputStream != null && mOutputStream != null,
+ "pushData() before start()");
+
+ // If the upload has failed then stop without pushing any more bytes.
+ if (mBackupTaskFuture.isDone()) {
+ Optional<Exception> exception = getTaskException();
+ Slog.e(TAG, "Encrypted upload failed", exception.orElse(null));
+ if (exception.isPresent()) {
+ reportNetworkFailureIfNecessary(exception.get());
+
+ if (exception.get().getCause() instanceof SizeQuotaExceededException) {
+ return BackupTransport.TRANSPORT_QUOTA_EXCEEDED;
+ }
+ }
+
+ return BackupTransport.TRANSPORT_ERROR;
+ }
+
+ try {
+ StreamUtils.copyStream(mInputStream, mOutputStream, numBytes);
+ } catch (IOException e) {
+ Slog.e(TAG, "IOException when processing backup", e);
+ return BackupTransport.TRANSPORT_ERROR;
+ }
+
+ return BackupTransport.TRANSPORT_OK;
+ }
+
+ @Override
+ public void cancel() {
+ checkState(mBackupTaskFuture != null && mBackupTask != null, "cancel() before start()");
+ mBackupTask.cancel();
+ closeStreams();
+ }
+
+ @Override
+ public int finish() {
+ checkState(mBackupTaskFuture != null, "finish() before start()");
+
+ // getTaskException() waits for the task to finish. We must close the streams first, which
+ // causes the task to finish, otherwise it will block forever.
+ closeStreams();
+ Optional<Exception> exception = getTaskException();
+
+ if (exception.isPresent()) {
+ Slog.e(TAG, "Exception during encrypted full backup", exception.get());
+ reportNetworkFailureIfNecessary(exception.get());
+
+ if (exception.get().getCause() instanceof SizeQuotaExceededException) {
+ return BackupTransport.TRANSPORT_QUOTA_EXCEEDED;
+ }
+ return BackupTransport.TRANSPORT_ERROR;
+
+ } else {
+ if (mFullBackupCallbacks != null) {
+ mFullBackupCallbacks.onSuccess();
+ }
+
+ return BackupTransport.TRANSPORT_OK;
+ }
+ }
+
+ private void closeStreams() {
+ StreamUtils.closeQuietly(mInputStream);
+ StreamUtils.closeQuietly(mOutputStream);
+ }
+
+ @Override
+ public void handleCheckSizeRejectionZeroBytes() {
+ cancel();
+ }
+
+ @Override
+ public void handleCheckSizeRejectionQuotaExceeded() {
+ cancel();
+ }
+
+ @Override
+ public void handleSendBytesQuotaExceeded() {
+ cancel();
+ }
+
+ @Override
+ public void attachCallbacks(FullBackupCallbacks fullBackupCallbacks) {
+ this.mFullBackupCallbacks = fullBackupCallbacks;
+ }
+
+ private void reportNetworkFailureIfNecessary(Exception exception) {
+ if (!(exception.getCause() instanceof SizeQuotaExceededException)
+ && mFullBackupCallbacks != null) {
+ mFullBackupCallbacks.onTransferFailed();
+ }
+ }
+
+ private Optional<Exception> getTaskException() {
+ if (mBackupTaskFuture != null) {
+ try {
+ mBackupTaskFuture.get();
+ } catch (InterruptedException | ExecutionException e) {
+ return Optional.of(e);
+ }
+ }
+ return Optional.empty();
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTask.java
new file mode 100644
index 000000000000..a938d715a307
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTask.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import android.content.Context;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.backup.encryption.StreamUtils;
+import com.android.server.backup.encryption.chunking.ProtoStore;
+import com.android.server.backup.encryption.chunking.cdc.FingerprintMixer;
+import com.android.server.backup.encryption.client.CryptoBackupServer;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
+import com.android.server.backup.encryption.keys.TertiaryKeyManager;
+import com.android.server.backup.encryption.keys.TertiaryKeyRotationScheduler;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunkListing;
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.concurrent.Callable;
+
+import javax.crypto.SecretKey;
+
+/**
+ * Task which reads a stream of plaintext full backup data, chunks it, encrypts it and uploads it to
+ * the server.
+ *
+ * <p>Once the backup completes or fails, closes the input stream.
+ */
+public class EncryptedFullBackupTask implements Callable<Void> {
+ private static final String TAG = "EncryptedFullBackupTask";
+
+ private static final int MIN_CHUNK_SIZE_BYTES = 2 * 1024;
+ private static final int MAX_CHUNK_SIZE_BYTES = 64 * 1024;
+ private static final int AVERAGE_CHUNK_SIZE_BYTES = 4 * 1024;
+
+ // TODO(b/69350270): Remove this hard-coded salt and related logic once we feel confident that
+ // incremental backup has happened at least once for all existing packages/users since we moved
+ // to
+ // using a randomly generated salt.
+ //
+ // The hard-coded fingerprint mixer salt was used for a short time period before replaced by one
+ // that is randomly generated on initial non-incremental backup and stored in ChunkListing to be
+ // reused for succeeding incremental backups. If an old ChunkListing does not have a
+ // fingerprint_mixer_salt, we assume that it was last backed up before a randomly generated salt
+ // is used so we use the hardcoded salt and set ChunkListing#fingerprint_mixer_salt to this
+ // value.
+ // Eventually all backup ChunkListings will have this field set and then we can remove the
+ // default
+ // value in the code.
+ static final byte[] DEFAULT_FINGERPRINT_MIXER_SALT =
+ Arrays.copyOf(new byte[] {20, 23}, FingerprintMixer.SALT_LENGTH_BYTES);
+
+ private final ProtoStore<ChunkListing> mChunkListingStore;
+ private final TertiaryKeyManager mTertiaryKeyManager;
+ private final InputStream mInputStream;
+ private final EncryptedBackupTask mTask;
+ private final String mPackageName;
+ private final SecureRandom mSecureRandom;
+
+ /** Creates a new instance with the default min, max and average chunk sizes. */
+ public static EncryptedFullBackupTask newInstance(
+ Context context,
+ CryptoBackupServer cryptoBackupServer,
+ SecureRandom secureRandom,
+ RecoverableKeyStoreSecondaryKey secondaryKey,
+ String packageName,
+ InputStream inputStream)
+ throws IOException {
+ EncryptedBackupTask encryptedBackupTask =
+ new EncryptedBackupTask(
+ cryptoBackupServer,
+ secureRandom,
+ packageName,
+ new BackupStreamEncrypter(
+ inputStream,
+ MIN_CHUNK_SIZE_BYTES,
+ MAX_CHUNK_SIZE_BYTES,
+ AVERAGE_CHUNK_SIZE_BYTES));
+ TertiaryKeyManager tertiaryKeyManager =
+ new TertiaryKeyManager(
+ context,
+ secureRandom,
+ TertiaryKeyRotationScheduler.getInstance(context),
+ secondaryKey,
+ packageName);
+
+ return new EncryptedFullBackupTask(
+ ProtoStore.createChunkListingStore(context),
+ tertiaryKeyManager,
+ encryptedBackupTask,
+ inputStream,
+ packageName,
+ new SecureRandom());
+ }
+
+ @VisibleForTesting
+ EncryptedFullBackupTask(
+ ProtoStore<ChunkListing> chunkListingStore,
+ TertiaryKeyManager tertiaryKeyManager,
+ EncryptedBackupTask task,
+ InputStream inputStream,
+ String packageName,
+ SecureRandom secureRandom) {
+ mChunkListingStore = chunkListingStore;
+ mTertiaryKeyManager = tertiaryKeyManager;
+ mInputStream = inputStream;
+ mTask = task;
+ mPackageName = packageName;
+ mSecureRandom = secureRandom;
+ }
+
+ @Override
+ public Void call() throws Exception {
+ try {
+ Optional<ChunkListing> maybeOldChunkListing =
+ mChunkListingStore.loadProto(mPackageName);
+
+ if (maybeOldChunkListing.isPresent()) {
+ Slog.i(TAG, "Found previous chunk listing for " + mPackageName);
+ }
+
+ // If the key has been rotated then we must re-encrypt all of the backup data.
+ if (mTertiaryKeyManager.wasKeyRotated()) {
+ Slog.i(
+ TAG,
+ "Key was rotated or newly generated for "
+ + mPackageName
+ + ", so performing a full backup.");
+ maybeOldChunkListing = Optional.empty();
+ mChunkListingStore.deleteProto(mPackageName);
+ }
+
+ SecretKey tertiaryKey = mTertiaryKeyManager.getKey();
+ WrappedKeyProto.WrappedKey wrappedTertiaryKey = mTertiaryKeyManager.getWrappedKey();
+
+ ChunkListing newChunkListing;
+ if (!maybeOldChunkListing.isPresent()) {
+ byte[] fingerprintMixerSalt = new byte[FingerprintMixer.SALT_LENGTH_BYTES];
+ mSecureRandom.nextBytes(fingerprintMixerSalt);
+ newChunkListing =
+ mTask.performNonIncrementalBackup(
+ tertiaryKey, wrappedTertiaryKey, fingerprintMixerSalt);
+ } else {
+ ChunkListing oldChunkListing = maybeOldChunkListing.get();
+
+ if (oldChunkListing.fingerprintMixerSalt == null
+ || oldChunkListing.fingerprintMixerSalt.length == 0) {
+ oldChunkListing.fingerprintMixerSalt = DEFAULT_FINGERPRINT_MIXER_SALT;
+ }
+
+ newChunkListing =
+ mTask.performIncrementalBackup(
+ tertiaryKey, wrappedTertiaryKey, oldChunkListing);
+ }
+
+ mChunkListingStore.saveProto(mPackageName, newChunkListing);
+ Slog.v(TAG, "Saved chunk listing for " + mPackageName);
+ } catch (IOException e) {
+ Slog.e(TAG, "Storage exception, wiping state");
+ mChunkListingStore.deleteProto(mPackageName);
+ throw e;
+ } finally {
+ StreamUtils.closeQuietly(mInputStream);
+ }
+
+ return null;
+ }
+
+ /**
+ * Signals to the task that the backup has been cancelled. If the upload has not yet started
+ * then the task will not upload any data to the server or save the new chunk listing.
+ *
+ * <p>You must then terminate the input stream.
+ */
+ public void cancel() {
+ mTask.cancel();
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullRestoreTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullRestoreTask.java
new file mode 100644
index 000000000000..04381af561b2
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullRestoreTask.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import android.annotation.Nullable;
+import android.content.Context;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.backup.encryption.FullRestoreDataProcessor;
+import com.android.server.backup.encryption.FullRestoreDownloader;
+import com.android.server.backup.encryption.StreamUtils;
+import com.android.server.backup.encryption.chunking.DecryptedChunkFileOutput;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+
+/** Downloads the encrypted backup file, decrypts it and passes the data to backup manager. */
+public class EncryptedFullRestoreTask implements FullRestoreDataProcessor {
+ private static final String DEFAULT_TEMPORARY_FOLDER = "encrypted_restore_temp";
+ private static final String ENCRYPTED_FILE_NAME = "encrypted_restore";
+ private static final String DECRYPTED_FILE_NAME = "decrypted_restore";
+
+ private final FullRestoreToFileTask mFullRestoreToFileTask;
+ private final BackupFileDecryptorTask mBackupFileDecryptorTask;
+ private final File mEncryptedFile;
+ private final File mDecryptedFile;
+ @Nullable private InputStream mDecryptedFileInputStream;
+
+ /**
+ * Creates a new task which stores temporary files in the files directory.
+ *
+ * @param fullRestoreDownloader which will download the backup file
+ * @param tertiaryKey which the backup file is encrypted with
+ */
+ public static EncryptedFullRestoreTask newInstance(
+ Context context, FullRestoreDownloader fullRestoreDownloader, SecretKey tertiaryKey)
+ throws NoSuchAlgorithmException, NoSuchPaddingException {
+ File temporaryFolder = new File(context.getFilesDir(), DEFAULT_TEMPORARY_FOLDER);
+ temporaryFolder.mkdirs();
+ return new EncryptedFullRestoreTask(
+ temporaryFolder, fullRestoreDownloader, new BackupFileDecryptorTask(tertiaryKey));
+ }
+
+ @VisibleForTesting
+ EncryptedFullRestoreTask(
+ File temporaryFolder,
+ FullRestoreDownloader fullRestoreDownloader,
+ BackupFileDecryptorTask backupFileDecryptorTask) {
+ checkArgument(temporaryFolder.isDirectory(), "Temporary folder must be existing directory");
+
+ mEncryptedFile = new File(temporaryFolder, ENCRYPTED_FILE_NAME);
+ mDecryptedFile = new File(temporaryFolder, DECRYPTED_FILE_NAME);
+
+ mFullRestoreToFileTask = new FullRestoreToFileTask(fullRestoreDownloader);
+ mBackupFileDecryptorTask = backupFileDecryptorTask;
+ }
+
+ /**
+ * Reads the next decrypted bytes into the given buffer.
+ *
+ * <p>During the first call this method will download the backup file from the server, decrypt
+ * it and save it to disk. It will then read the bytes from the file on disk.
+ *
+ * <p>Once this method has read all the bytes of the file, the caller must call {@link #finish}
+ * to clean up.
+ *
+ * @return the number of bytes read, or {@code -1} on reaching the end of the file
+ */
+ @Override
+ public int readNextChunk(byte[] buffer) throws IOException {
+ if (mDecryptedFileInputStream == null) {
+ try {
+ mDecryptedFileInputStream = downloadAndDecryptBackup();
+ } catch (BadPaddingException
+ | InvalidKeyException
+ | NoSuchAlgorithmException
+ | IllegalBlockSizeException
+ | ShortBufferException
+ | EncryptedRestoreException
+ | InvalidAlgorithmParameterException e) {
+ throw new IOException("Encryption issue", e);
+ }
+ }
+
+ return mDecryptedFileInputStream.read(buffer);
+ }
+
+ private InputStream downloadAndDecryptBackup()
+ throws IOException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException,
+ IllegalBlockSizeException, ShortBufferException, EncryptedRestoreException,
+ InvalidAlgorithmParameterException {
+ mFullRestoreToFileTask.restoreToFile(mEncryptedFile);
+ mBackupFileDecryptorTask.decryptFile(
+ mEncryptedFile, new DecryptedChunkFileOutput(mDecryptedFile));
+ mEncryptedFile.delete();
+ return new BufferedInputStream(new FileInputStream(mDecryptedFile));
+ }
+
+ /** Cleans up temporary files. */
+ @Override
+ public void finish(FullRestoreDownloader.FinishType unusedFinishType) {
+ // The download is finished and log sent during RestoreToFileTask#restoreToFile(), so we
+ // don't need to do either of those things here.
+
+ StreamUtils.closeQuietly(mDecryptedFileInputStream);
+ mEncryptedFile.delete();
+ mDecryptedFile.delete();
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedKvBackupTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedKvBackupTask.java
new file mode 100644
index 000000000000..619438c7f6fe
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedKvBackupTask.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import android.annotation.Nullable;
+import android.app.backup.BackupDataInput;
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+import android.security.keystore.recovery.LockScreenRequiredException;
+import android.util.Pair;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.backup.encryption.CryptoSettings;
+import com.android.server.backup.encryption.chunking.ProtoStore;
+import com.android.server.backup.encryption.client.CryptoBackupServer;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
+import com.android.server.backup.encryption.keys.TertiaryKeyManager;
+import com.android.server.backup.encryption.keys.TertiaryKeyRotationScheduler;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
+import com.android.server.backup.encryption.protos.nano.KeyValueListingProto;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.util.Optional;
+
+// TODO(b/141975695): Create a base class for EncryptedKvBackupTask and EncryptedFullBackupTask.
+/** Performs encrypted key value backup, handling rotating the tertiary key as necessary. */
+public class EncryptedKvBackupTask {
+ private static final String TAG = "EncryptedKvBackupTask";
+
+ private final TertiaryKeyManager mTertiaryKeyManager;
+ private final RecoverableKeyStoreSecondaryKey mSecondaryKey;
+ private final ProtoStore<KeyValueListingProto.KeyValueListing> mKeyValueListingStore;
+ private final ProtoStore<ChunksMetadataProto.ChunkListing> mChunkListingStore;
+ private final KvBackupEncrypter mKvBackupEncrypter;
+ private final EncryptedBackupTask mEncryptedBackupTask;
+ private final String mPackageName;
+
+ /** Constructs new instances of {@link EncryptedKvBackupTask}. */
+ public static class EncryptedKvBackupTaskFactory {
+ /**
+ * Creates a new instance.
+ *
+ * <p>Either initializes encrypted backup or loads an existing secondary key as necessary.
+ *
+ * @param cryptoSettings to load secondary key state from
+ * @param fileDescriptor to read the backup data from
+ */
+ public EncryptedKvBackupTask newInstance(
+ Context context,
+ SecureRandom secureRandom,
+ CryptoBackupServer cryptoBackupServer,
+ CryptoSettings cryptoSettings,
+ RecoverableKeyStoreSecondaryKeyManager
+ .RecoverableKeyStoreSecondaryKeyManagerProvider
+ recoverableSecondaryKeyManagerProvider,
+ ParcelFileDescriptor fileDescriptor,
+ String packageName)
+ throws IOException, UnrecoverableKeyException, LockScreenRequiredException,
+ InternalRecoveryServiceException, InvalidKeyException {
+ RecoverableKeyStoreSecondaryKey secondaryKey =
+ new InitializeRecoverableSecondaryKeyTask(
+ context,
+ cryptoSettings,
+ recoverableSecondaryKeyManagerProvider.get(),
+ cryptoBackupServer)
+ .run();
+ KvBackupEncrypter backupEncrypter =
+ new KvBackupEncrypter(new BackupDataInput(fileDescriptor.getFileDescriptor()));
+ TertiaryKeyManager tertiaryKeyManager =
+ new TertiaryKeyManager(
+ context,
+ secureRandom,
+ TertiaryKeyRotationScheduler.getInstance(context),
+ secondaryKey,
+ packageName);
+
+ return new EncryptedKvBackupTask(
+ tertiaryKeyManager,
+ ProtoStore.createKeyValueListingStore(context),
+ secondaryKey,
+ ProtoStore.createChunkListingStore(context),
+ backupEncrypter,
+ new EncryptedBackupTask(
+ cryptoBackupServer, secureRandom, packageName, backupEncrypter),
+ packageName);
+ }
+ }
+
+ @VisibleForTesting
+ EncryptedKvBackupTask(
+ TertiaryKeyManager tertiaryKeyManager,
+ ProtoStore<KeyValueListingProto.KeyValueListing> keyValueListingStore,
+ RecoverableKeyStoreSecondaryKey secondaryKey,
+ ProtoStore<ChunksMetadataProto.ChunkListing> chunkListingStore,
+ KvBackupEncrypter kvBackupEncrypter,
+ EncryptedBackupTask encryptedBackupTask,
+ String packageName) {
+ mTertiaryKeyManager = tertiaryKeyManager;
+ mSecondaryKey = secondaryKey;
+ mKeyValueListingStore = keyValueListingStore;
+ mChunkListingStore = chunkListingStore;
+ mKvBackupEncrypter = kvBackupEncrypter;
+ mEncryptedBackupTask = encryptedBackupTask;
+ mPackageName = packageName;
+ }
+
+ /**
+ * Reads backup data from the file descriptor provided in the construtor, encrypts it and
+ * uploads it to the server.
+ *
+ * <p>The {@code incremental} flag indicates if the backup data provided is incremental or a
+ * complete set. Incremental backup is not possible if no previous crypto state exists, or the
+ * tertiary key must be rotated in the next backup. If the caller requests incremental backup
+ * but it is not possible, then the backup will not start and this method will throw {@link
+ * NonIncrementalBackupRequiredException}.
+ *
+ * <p>TODO(b/70704456): Update return code to indicate that we require non-incremental backup.
+ *
+ * @param incremental {@code true} if the data provided is a diff from the previous backup,
+ * {@code false} if it is a complete set
+ * @throws NonIncrementalBackupRequiredException if the caller provides an incremental backup but the task
+ * requires non-incremental backup
+ */
+ public void performBackup(boolean incremental)
+ throws GeneralSecurityException, IOException, NoSuchMethodException,
+ InstantiationException, IllegalAccessException, InvocationTargetException,
+ NonIncrementalBackupRequiredException {
+ if (mTertiaryKeyManager.wasKeyRotated()) {
+ Slog.d(TAG, "Tertiary key is new so clearing package state.");
+ deleteListings(mPackageName);
+ }
+
+ Optional<Pair<KeyValueListingProto.KeyValueListing, ChunksMetadataProto.ChunkListing>>
+ oldListings = getListingsAndEnsureConsistency(mPackageName);
+
+ if (oldListings.isPresent() && !incremental) {
+ Slog.d(
+ TAG,
+ "Non-incremental backup requested but incremental state existed, clearing it");
+ deleteListings(mPackageName);
+ oldListings = Optional.empty();
+ }
+
+ if (!oldListings.isPresent() && incremental) {
+ // If we don't have any state then we require a non-incremental backup, but this backup
+ // is incremental.
+ throw new NonIncrementalBackupRequiredException();
+ }
+
+ if (oldListings.isPresent()) {
+ mKvBackupEncrypter.setOldKeyValueListing(oldListings.get().first);
+ }
+
+ ChunksMetadataProto.ChunkListing newChunkListing;
+ if (oldListings.isPresent()) {
+ Slog.v(TAG, "Old listings existed, performing incremental backup");
+ newChunkListing =
+ mEncryptedBackupTask.performIncrementalBackup(
+ mTertiaryKeyManager.getKey(),
+ mTertiaryKeyManager.getWrappedKey(),
+ oldListings.get().second);
+ } else {
+ Slog.v(TAG, "Old listings did not exist, performing non-incremental backup");
+ // kv backups don't use this salt because they don't involve content-defined chunking.
+ byte[] fingerprintMixerSalt = null;
+ newChunkListing =
+ mEncryptedBackupTask.performNonIncrementalBackup(
+ mTertiaryKeyManager.getKey(),
+ mTertiaryKeyManager.getWrappedKey(),
+ fingerprintMixerSalt);
+ }
+
+ Slog.v(TAG, "Backup and upload succeeded, saving new listings");
+ saveListings(mPackageName, mKvBackupEncrypter.getNewKeyValueListing(), newChunkListing);
+ }
+
+ private Optional<Pair<KeyValueListingProto.KeyValueListing, ChunksMetadataProto.ChunkListing>>
+ getListingsAndEnsureConsistency(String packageName)
+ throws IOException, InvocationTargetException, NoSuchMethodException,
+ InstantiationException, IllegalAccessException {
+ Optional<KeyValueListingProto.KeyValueListing> keyValueListing =
+ mKeyValueListingStore.loadProto(packageName);
+ Optional<ChunksMetadataProto.ChunkListing> chunkListing =
+ mChunkListingStore.loadProto(packageName);
+
+ // Normally either both protos exist or neither exist, but we correct this just in case.
+ boolean bothPresent = keyValueListing.isPresent() && chunkListing.isPresent();
+ if (!bothPresent) {
+ Slog.d(
+ TAG,
+ "Both listing were not present, clearing state, key value="
+ + keyValueListing.isPresent()
+ + ", chunk="
+ + chunkListing.isPresent());
+ deleteListings(packageName);
+ return Optional.empty();
+ }
+
+ return Optional.of(Pair.create(keyValueListing.get(), chunkListing.get()));
+ }
+
+ private void saveListings(
+ String packageName,
+ KeyValueListingProto.KeyValueListing keyValueListing,
+ ChunksMetadataProto.ChunkListing chunkListing) {
+ try {
+ mKeyValueListingStore.saveProto(packageName, keyValueListing);
+ mChunkListingStore.saveProto(packageName, chunkListing);
+ } catch (IOException e) {
+ // If a problem occurred while saving either listing then they may be inconsistent, so
+ // delete
+ // both.
+ Slog.w(TAG, "Unable to save listings, deleting both for consistency", e);
+ deleteListings(packageName);
+ }
+ }
+
+ private void deleteListings(String packageName) {
+ mKeyValueListingStore.deleteProto(packageName);
+ mChunkListingStore.deleteProto(packageName);
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedKvRestoreTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedKvRestoreTask.java
new file mode 100644
index 000000000000..12b44590ebe6
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedKvRestoreTask.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import android.app.backup.BackupDataOutput;
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.backup.encryption.FullRestoreDownloader;
+import com.android.server.backup.encryption.chunking.ChunkHasher;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
+import com.android.server.backup.encryption.keys.RestoreKeyFetcher;
+import com.android.server.backup.encryption.kv.DecryptedChunkKvOutput;
+import com.android.server.backup.encryption.protos.nano.KeyValuePairProto;
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+
+/**
+ * Performs a key value restore by downloading the backup set, decrypting it and writing it to the
+ * file provided by backup manager.
+ */
+public class EncryptedKvRestoreTask {
+ private static final String ENCRYPTED_FILE_NAME = "encrypted_kv";
+
+ private final File mTemporaryFolder;
+ private final ChunkHasher mChunkHasher;
+ private final FullRestoreToFileTask mFullRestoreToFileTask;
+ private final BackupFileDecryptorTask mBackupFileDecryptorTask;
+
+ /** Constructs new instances of the task. */
+ public static class EncryptedKvRestoreTaskFactory {
+ /**
+ * Constructs a new instance.
+ *
+ * <p>Fetches the appropriate secondary key and uses this to unwrap the tertiary key. Stores
+ * temporary files in {@link Context#getFilesDir()}.
+ */
+ public EncryptedKvRestoreTask newInstance(
+ Context context,
+ RecoverableKeyStoreSecondaryKeyManager
+ .RecoverableKeyStoreSecondaryKeyManagerProvider
+ recoverableSecondaryKeyManagerProvider,
+ FullRestoreDownloader fullRestoreDownloader,
+ String secondaryKeyAlias,
+ WrappedKeyProto.WrappedKey wrappedTertiaryKey)
+ throws EncryptedRestoreException, NoSuchAlgorithmException, NoSuchPaddingException,
+ KeyException, InvalidAlgorithmParameterException {
+ SecretKey tertiaryKey =
+ RestoreKeyFetcher.unwrapTertiaryKey(
+ recoverableSecondaryKeyManagerProvider,
+ secondaryKeyAlias,
+ wrappedTertiaryKey);
+
+ return new EncryptedKvRestoreTask(
+ context.getFilesDir(),
+ new ChunkHasher(tertiaryKey),
+ new FullRestoreToFileTask(fullRestoreDownloader),
+ new BackupFileDecryptorTask(tertiaryKey));
+ }
+ }
+
+ @VisibleForTesting
+ EncryptedKvRestoreTask(
+ File temporaryFolder,
+ ChunkHasher chunkHasher,
+ FullRestoreToFileTask fullRestoreToFileTask,
+ BackupFileDecryptorTask backupFileDecryptorTask) {
+ checkArgument(
+ temporaryFolder.isDirectory(), "Temporary folder must be an existing directory");
+
+ mTemporaryFolder = temporaryFolder;
+ mChunkHasher = chunkHasher;
+ mFullRestoreToFileTask = fullRestoreToFileTask;
+ mBackupFileDecryptorTask = backupFileDecryptorTask;
+ }
+
+ /**
+ * Runs the restore, writing the pairs in lexicographical order to the given file descriptor.
+ *
+ * <p>This will block for the duration of the restore.
+ *
+ * @throws EncryptedRestoreException if there is a problem decrypting or verifying the backup
+ */
+ public void getRestoreData(ParcelFileDescriptor output)
+ throws IOException, EncryptedRestoreException, BadPaddingException,
+ InvalidAlgorithmParameterException, NoSuchAlgorithmException,
+ IllegalBlockSizeException, ShortBufferException, InvalidKeyException {
+ File encryptedFile = new File(mTemporaryFolder, ENCRYPTED_FILE_NAME);
+ try {
+ downloadDecryptAndWriteBackup(encryptedFile, output);
+ } finally {
+ encryptedFile.delete();
+ }
+ }
+
+ private void downloadDecryptAndWriteBackup(File encryptedFile, ParcelFileDescriptor output)
+ throws EncryptedRestoreException, IOException, BadPaddingException, InvalidKeyException,
+ NoSuchAlgorithmException, IllegalBlockSizeException, ShortBufferException,
+ InvalidAlgorithmParameterException {
+ mFullRestoreToFileTask.restoreToFile(encryptedFile);
+ DecryptedChunkKvOutput decryptedChunkKvOutput = new DecryptedChunkKvOutput(mChunkHasher);
+ mBackupFileDecryptorTask.decryptFile(encryptedFile, decryptedChunkKvOutput);
+
+ BackupDataOutput backupDataOutput = new BackupDataOutput(output.getFileDescriptor());
+ for (KeyValuePairProto.KeyValuePair pair : decryptedChunkKvOutput.getPairs()) {
+ backupDataOutput.writeEntityHeader(pair.key, pair.value.length);
+ backupDataOutput.writeEntityData(pair.value, pair.value.length);
+ }
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/FullRestoreToFileTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/FullRestoreToFileTask.java
new file mode 100644
index 000000000000..82f83f9b7494
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/FullRestoreToFileTask.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.backup.encryption.FullRestoreDownloader;
+import com.android.server.backup.encryption.FullRestoreDownloader.FinishType;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Reads a stream from a {@link FullRestoreDownloader} and writes it to a file for consumption by
+ * {@link BackupFileDecryptorTask}.
+ */
+public class FullRestoreToFileTask {
+ /**
+ * Maximum number of bytes which the framework can request from the full restore data stream in
+ * one call to {@link BackupTransport#getNextFullRestoreDataChunk}.
+ */
+ public static final int MAX_BYTES_FULL_RESTORE_CHUNK = 1024 * 32;
+
+ /** Returned when the end of a backup stream has been reached. */
+ private static final int END_OF_STREAM = -1;
+
+ private final FullRestoreDownloader mFullRestoreDownloader;
+ private final int mBufferSize;
+
+ /**
+ * Constructs a new instance which reads from the given package wrapper, using a buffer of size
+ * {@link #MAX_BYTES_FULL_RESTORE_CHUNK}.
+ */
+ public FullRestoreToFileTask(FullRestoreDownloader fullRestoreDownloader) {
+ this(fullRestoreDownloader, MAX_BYTES_FULL_RESTORE_CHUNK);
+ }
+
+ @VisibleForTesting
+ FullRestoreToFileTask(FullRestoreDownloader fullRestoreDownloader, int bufferSize) {
+ checkArgument(bufferSize > 0, "Buffer must have positive size");
+
+ this.mFullRestoreDownloader = fullRestoreDownloader;
+ this.mBufferSize = bufferSize;
+ }
+
+ /**
+ * Downloads the backup file from the server and writes it to the given file.
+ *
+ * <p>At the end of the download (success or failure), closes the connection and sends a
+ * Clearcut log.
+ */
+ public void restoreToFile(File targetFile) throws IOException {
+ try (BufferedOutputStream outputStream =
+ new BufferedOutputStream(new FileOutputStream(targetFile))) {
+ byte[] buffer = new byte[mBufferSize];
+ int bytesRead = mFullRestoreDownloader.readNextChunk(buffer);
+ while (bytesRead != END_OF_STREAM) {
+ outputStream.write(buffer, /* off=*/ 0, bytesRead);
+ bytesRead = mFullRestoreDownloader.readNextChunk(buffer);
+ }
+
+ outputStream.flush();
+
+ mFullRestoreDownloader.finish(FinishType.FINISHED);
+ } catch (IOException e) {
+ mFullRestoreDownloader.finish(FinishType.TRANSFER_FAILURE);
+ throw e;
+ }
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/InitializeRecoverableSecondaryKeyTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/InitializeRecoverableSecondaryKeyTask.java
new file mode 100644
index 000000000000..d436554341b8
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/InitializeRecoverableSecondaryKeyTask.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import android.content.Context;
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+import android.security.keystore.recovery.LockScreenRequiredException;
+import android.security.keystore.recovery.RecoveryController;
+import android.util.Slog;
+
+import com.android.server.backup.encryption.CryptoSettings;
+import com.android.server.backup.encryption.client.CryptoBackupServer;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
+
+import java.security.InvalidKeyException;
+import java.security.UnrecoverableKeyException;
+import java.util.Collections;
+import java.util.Optional;
+
+/**
+ * Initializes the device for encrypted backup, through generating a secondary key, and setting its
+ * alias in the settings.
+ *
+ * <p>If the device is already initialized, this is a no-op.
+ */
+public class InitializeRecoverableSecondaryKeyTask {
+ private static final String TAG = "InitializeRecoverableSecondaryKeyTask";
+
+ private final Context mContext;
+ private final CryptoSettings mCryptoSettings;
+ private final RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager;
+ private final CryptoBackupServer mBackupServer;
+
+ /**
+ * A new instance.
+ *
+ * @param cryptoSettings Settings to store the active key alias.
+ * @param secondaryKeyManager Key manager to generate the new active secondary key.
+ * @param backupServer Server with which to sync the active key alias.
+ */
+ public InitializeRecoverableSecondaryKeyTask(
+ Context context,
+ CryptoSettings cryptoSettings,
+ RecoverableKeyStoreSecondaryKeyManager secondaryKeyManager,
+ CryptoBackupServer backupServer) {
+ mContext = context;
+ mCryptoSettings = cryptoSettings;
+ mSecondaryKeyManager = secondaryKeyManager;
+ mBackupServer = backupServer;
+ }
+
+ /**
+ * Initializes the device for encrypted backup, by generating a recoverable secondary key, then
+ * sending that alias to the backup server and saving it in local settings.
+ *
+ * <p>If there is already an active secondary key then does nothing. If the active secondary key
+ * is destroyed then throws {@link InvalidKeyException}.
+ *
+ * <p>If a key rotation is pending and able to finish (i.e., the new key has synced with the
+ * remote trusted hardware module), then it completes the rotation before returning the key.
+ *
+ * @return The active secondary key.
+ * @throws InvalidKeyException if the secondary key is in a bad state.
+ */
+ public RecoverableKeyStoreSecondaryKey run()
+ throws InvalidKeyException, LockScreenRequiredException, UnrecoverableKeyException,
+ InternalRecoveryServiceException {
+ // Complete any pending key rotations
+ new RotateSecondaryKeyTask(
+ mContext,
+ mSecondaryKeyManager,
+ mBackupServer,
+ mCryptoSettings,
+ RecoveryController.getInstance(mContext))
+ .run();
+
+ return runInternal();
+ }
+
+ private RecoverableKeyStoreSecondaryKey runInternal()
+ throws InvalidKeyException, LockScreenRequiredException, UnrecoverableKeyException,
+ InternalRecoveryServiceException {
+ Optional<RecoverableKeyStoreSecondaryKey> maybeActiveKey = loadFromSetting();
+
+ if (maybeActiveKey.isPresent()) {
+ assertKeyNotDestroyed(maybeActiveKey.get());
+ Slog.d(TAG, "Secondary key already initialized: " + maybeActiveKey.get().getAlias());
+ return maybeActiveKey.get();
+ }
+
+ Slog.v(TAG, "Initializing for crypto: generating a secondary key.");
+ RecoverableKeyStoreSecondaryKey key = mSecondaryKeyManager.generate();
+
+ String alias = key.getAlias();
+ Slog.i(TAG, "Generated new secondary key " + alias);
+
+ // No tertiary keys yet as we are creating a brand new secondary (without rotation).
+ mBackupServer.setActiveSecondaryKeyAlias(alias, /*tertiaryKeys=*/ Collections.emptyMap());
+ Slog.v(TAG, "Successfully synced %s " + alias + " with server.");
+
+ mCryptoSettings.initializeWithKeyAlias(alias);
+ Slog.v(TAG, "Successfully saved " + alias + " as active secondary to disk.");
+
+ return key;
+ }
+
+ private void assertKeyNotDestroyed(RecoverableKeyStoreSecondaryKey key)
+ throws InvalidKeyException {
+ if (key.getStatus(mContext) == RecoverableKeyStoreSecondaryKey.Status.DESTROYED) {
+ throw new InvalidKeyException("Key destroyed: " + key.getAlias());
+ }
+ }
+
+ private Optional<RecoverableKeyStoreSecondaryKey> loadFromSetting()
+ throws InvalidKeyException, UnrecoverableKeyException,
+ InternalRecoveryServiceException {
+
+ // TODO: b/141856950.
+ if (!mCryptoSettings.getIsInitialized()) {
+ return Optional.empty();
+ }
+
+ Optional<String> maybeAlias = mCryptoSettings.getActiveSecondaryKeyAlias();
+ if (!maybeAlias.isPresent()) {
+ throw new InvalidKeyException(
+ "Settings said crypto was initialized, but there was no active secondary"
+ + " alias");
+ }
+
+ String alias = maybeAlias.get();
+
+ Optional<RecoverableKeyStoreSecondaryKey> key;
+ key = mSecondaryKeyManager.get(alias);
+
+ if (!key.isPresent()) {
+ throw new InvalidKeyException(
+ "Initialized with key but it was not in key store: " + alias);
+ }
+
+ return key;
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/KvBackupEncrypter.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/KvBackupEncrypter.java
new file mode 100644
index 000000000000..d20cd4c07f88
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/KvBackupEncrypter.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.android.internal.util.Preconditions.checkState;
+
+import android.annotation.Nullable;
+import android.app.backup.BackupDataInput;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.chunking.ChunkEncryptor;
+import com.android.server.backup.encryption.chunking.ChunkHasher;
+import com.android.server.backup.encryption.chunking.EncryptedChunk;
+import com.android.server.backup.encryption.kv.KeyValueListingBuilder;
+import com.android.server.backup.encryption.protos.nano.KeyValueListingProto;
+import com.android.server.backup.encryption.protos.nano.KeyValuePairProto;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.SecretKey;
+
+/**
+ * Reads key value backup data from an input, converts each pair into a chunk and encrypts the
+ * chunks.
+ *
+ * <p>The caller should pass in the key value listing from the previous backup, if there is one.
+ * This class emits chunks for both existing and new pairs, using the provided listing to
+ * determine the hashes of pairs that already exist. During the backup it computes the new listing,
+ * which the caller should store on disk and pass in at the start of the next backup.
+ *
+ * <p>Also computes the message digest, which is {@code SHA-256(chunk hashes sorted
+ * lexicographically)}.
+ */
+public class KvBackupEncrypter implements BackupEncrypter {
+ private final BackupDataInput mBackupDataInput;
+
+ private KeyValueListingProto.KeyValueListing mOldKeyValueListing;
+ @Nullable private KeyValueListingBuilder mNewKeyValueListing;
+
+ /**
+ * Constructs a new instance which reads data from the given input.
+ *
+ * <p>By default this performs non-incremental backup, call {@link #setOldKeyValueListing} to
+ * perform incremental backup.
+ */
+ public KvBackupEncrypter(BackupDataInput backupDataInput) {
+ mBackupDataInput = backupDataInput;
+ mOldKeyValueListing = KeyValueListingBuilder.emptyListing();
+ }
+
+ /** Sets the old listing to perform incremental backup against. */
+ public void setOldKeyValueListing(KeyValueListingProto.KeyValueListing oldKeyValueListing) {
+ mOldKeyValueListing = oldKeyValueListing;
+ }
+
+ @Override
+ public Result backup(
+ SecretKey secretKey,
+ @Nullable byte[] unusedFingerprintMixerSalt,
+ Set<ChunkHash> unusedExistingChunks)
+ throws IOException, GeneralSecurityException {
+ ChunkHasher chunkHasher = new ChunkHasher(secretKey);
+ ChunkEncryptor chunkEncryptor = new ChunkEncryptor(secretKey, new SecureRandom());
+ mNewKeyValueListing = new KeyValueListingBuilder();
+ List<ChunkHash> allChunks = new ArrayList<>();
+ List<EncryptedChunk> newChunks = new ArrayList<>();
+
+ Map<String, ChunkHash> existingChunksToReuse = buildPairMap(mOldKeyValueListing);
+
+ while (mBackupDataInput.readNextHeader()) {
+ String key = mBackupDataInput.getKey();
+ Optional<byte[]> value = readEntireValue(mBackupDataInput);
+
+ // As this pair exists in the new backup, we don't need to add it from the previous
+ // backup.
+ existingChunksToReuse.remove(key);
+
+ // If the value is not present then this key has been deleted.
+ if (value.isPresent()) {
+ EncryptedChunk newChunk =
+ createEncryptedChunk(chunkHasher, chunkEncryptor, key, value.get());
+ allChunks.add(newChunk.key());
+ newChunks.add(newChunk);
+ mNewKeyValueListing.addPair(key, newChunk.key());
+ }
+ }
+
+ allChunks.addAll(existingChunksToReuse.values());
+
+ mNewKeyValueListing.addAll(existingChunksToReuse);
+
+ return new Result(allChunks, newChunks, createMessageDigest(allChunks));
+ }
+
+ /**
+ * Returns a listing containing the pairs in the new backup.
+ *
+ * <p>You must call {@link #backup} first.
+ */
+ public KeyValueListingProto.KeyValueListing getNewKeyValueListing() {
+ checkState(mNewKeyValueListing != null, "Must call backup() first");
+ return mNewKeyValueListing.build();
+ }
+
+ private static Map<String, ChunkHash> buildPairMap(
+ KeyValueListingProto.KeyValueListing listing) {
+ Map<String, ChunkHash> map = new HashMap<>();
+ for (KeyValueListingProto.KeyValueEntry entry : listing.entries) {
+ map.put(entry.key, new ChunkHash(entry.hash));
+ }
+ return map;
+ }
+
+ private EncryptedChunk createEncryptedChunk(
+ ChunkHasher chunkHasher, ChunkEncryptor chunkEncryptor, String key, byte[] value)
+ throws InvalidKeyException, IllegalBlockSizeException {
+ KeyValuePairProto.KeyValuePair pair = new KeyValuePairProto.KeyValuePair();
+ pair.key = key;
+ pair.value = Arrays.copyOf(value, value.length);
+
+ byte[] plaintext = KeyValuePairProto.KeyValuePair.toByteArray(pair);
+ return chunkEncryptor.encrypt(chunkHasher.computeHash(plaintext), plaintext);
+ }
+
+ private static byte[] createMessageDigest(List<ChunkHash> allChunks)
+ throws NoSuchAlgorithmException {
+ MessageDigest messageDigest =
+ MessageDigest.getInstance(BackupEncrypter.MESSAGE_DIGEST_ALGORITHM);
+ // TODO:b/141531271 Extract sorted chunks code to utility class
+ List<ChunkHash> sortedChunks = new ArrayList<>(allChunks);
+ Collections.sort(sortedChunks);
+ for (ChunkHash hash : sortedChunks) {
+ messageDigest.update(hash.getHash());
+ }
+ return messageDigest.digest();
+ }
+
+ private static Optional<byte[]> readEntireValue(BackupDataInput input) throws IOException {
+ // A negative data size indicates that this key should be deleted.
+ if (input.getDataSize() < 0) {
+ return Optional.empty();
+ }
+
+ byte[] value = new byte[input.getDataSize()];
+ int bytesRead = 0;
+ while (bytesRead < value.length) {
+ bytesRead += input.readEntityData(value, bytesRead, value.length - bytesRead);
+ }
+ return Optional.of(value);
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/MalformedEncryptedFileException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/MalformedEncryptedFileException.java
new file mode 100644
index 000000000000..78c370b0d548
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/MalformedEncryptedFileException.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+/** Exception thrown when we cannot parse the encrypted backup file. */
+public class MalformedEncryptedFileException extends EncryptedRestoreException {
+ public MalformedEncryptedFileException(String message) {
+ super(message);
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/MessageDigestMismatchException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/MessageDigestMismatchException.java
new file mode 100644
index 000000000000..1e4f43b43e26
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/MessageDigestMismatchException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+/**
+ * Error thrown if the message digest of the plaintext backup does not match that in the {@link
+ * com.android.server.backup.encryption.protos.ChunksMetadataProto.ChunkOrdering}.
+ */
+public class MessageDigestMismatchException extends EncryptedRestoreException {
+ public MessageDigestMismatchException(String message) {
+ super(message);
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/NoActiveSecondaryKeyException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/NoActiveSecondaryKeyException.java
new file mode 100644
index 000000000000..72e8a89f1df3
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/NoActiveSecondaryKeyException.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+/**
+ * Error thrown if attempting to rotate key when there is no current active secondary key set
+ * locally. This means the device needs to re-initialize, asking the backup server what the active
+ * secondary key is.
+ */
+public class NoActiveSecondaryKeyException extends Exception {
+ public NoActiveSecondaryKeyException(String message) {
+ super(message);
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/NonIncrementalBackupRequiredException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/NonIncrementalBackupRequiredException.java
new file mode 100644
index 000000000000..a3eda7d1270f
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/NonIncrementalBackupRequiredException.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.server.backup.encryption.tasks;
+
+// TODO(141840878): Update documentation.
+/**
+ * Exception thrown when the framework provides an incremental backup but the transport requires a
+ * non-incremental backup.
+ */
+public class NonIncrementalBackupRequiredException extends Exception {}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTask.java
new file mode 100644
index 000000000000..d58cb66ef6b4
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTask.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static android.os.Build.VERSION_CODES.P;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.content.Context;
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+import android.security.keystore.recovery.RecoveryController;
+import android.util.Slog;
+
+import com.android.server.backup.encryption.CryptoSettings;
+import com.android.server.backup.encryption.client.CryptoBackupServer;
+import com.android.server.backup.encryption.keys.KeyWrapUtils;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
+import com.android.server.backup.encryption.keys.TertiaryKeyStore;
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+
+/**
+ * Finishes a rotation for a {@link
+ * com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey}.
+ */
+public class RotateSecondaryKeyTask {
+ private static final String TAG = "RotateSecondaryKeyTask";
+
+ private final Context mContext;
+ private final RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager;
+ private final CryptoBackupServer mBackupServer;
+ private final CryptoSettings mCryptoSettings;
+ private final RecoveryController mRecoveryController;
+
+ /**
+ * A new instance.
+ *
+ * @param secondaryKeyManager For loading the currently active and next secondary key.
+ * @param backupServer For loading and storing tertiary keys and for setting active secondary
+ * key.
+ * @param cryptoSettings For checking the stored aliases for the next and active key.
+ * @param recoveryController For communicating with the Framework apis.
+ */
+ public RotateSecondaryKeyTask(
+ Context context,
+ RecoverableKeyStoreSecondaryKeyManager secondaryKeyManager,
+ CryptoBackupServer backupServer,
+ CryptoSettings cryptoSettings,
+ RecoveryController recoveryController) {
+ mContext = context;
+ mSecondaryKeyManager = checkNotNull(secondaryKeyManager);
+ mCryptoSettings = checkNotNull(cryptoSettings);
+ mBackupServer = checkNotNull(backupServer);
+ mRecoveryController = checkNotNull(recoveryController);
+ }
+
+ /** Runs the task. */
+ public void run() {
+ // Never run more than one of these at the same time.
+ synchronized (RotateSecondaryKeyTask.class) {
+ runInternal();
+ }
+ }
+
+ private void runInternal() {
+ Optional<RecoverableKeyStoreSecondaryKey> maybeNextKey;
+ try {
+ maybeNextKey = getNextKey();
+ } catch (Exception e) {
+ Slog.e(TAG, "Error checking for next key", e);
+ return;
+ }
+
+ if (!maybeNextKey.isPresent()) {
+ Slog.d(TAG, "No secondary key rotation task pending. Exiting.");
+ return;
+ }
+
+ RecoverableKeyStoreSecondaryKey nextKey = maybeNextKey.get();
+ boolean isReady;
+ try {
+ isReady = isSecondaryKeyRotationReady(nextKey);
+ } catch (InternalRecoveryServiceException e) {
+ Slog.e(TAG, "Error encountered checking whether next secondary key is synced", e);
+ return;
+ }
+
+ if (!isReady) {
+ return;
+ }
+
+ try {
+ rotateToKey(nextKey);
+ } catch (Exception e) {
+ Slog.e(TAG, "Error trying to rotate to new secondary key", e);
+ }
+ }
+
+ private Optional<RecoverableKeyStoreSecondaryKey> getNextKey()
+ throws InternalRecoveryServiceException, UnrecoverableKeyException {
+ Optional<String> maybeNextAlias = mCryptoSettings.getNextSecondaryKeyAlias();
+ if (!maybeNextAlias.isPresent()) {
+ return Optional.empty();
+ }
+ return mSecondaryKeyManager.get(maybeNextAlias.get());
+ }
+
+ private boolean isSecondaryKeyRotationReady(RecoverableKeyStoreSecondaryKey nextKey)
+ throws InternalRecoveryServiceException {
+ String nextAlias = nextKey.getAlias();
+ Slog.i(TAG, "Key rotation to " + nextAlias + " is pending. Checking key sync status.");
+ int status = mRecoveryController.getRecoveryStatus(nextAlias);
+
+ if (status == RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE) {
+ Slog.e(
+ TAG,
+ "Permanent failure to sync " + nextAlias + ". Cannot possibly rotate to it.");
+ mCryptoSettings.removeNextSecondaryKeyAlias();
+ return false;
+ }
+
+ if (status == RecoveryController.RECOVERY_STATUS_SYNCED) {
+ Slog.i(TAG, "Secondary key " + nextAlias + " has now synced! Commencing rotation.");
+ } else {
+ Slog.i(TAG, "Sync still pending for " + nextAlias);
+ }
+ return status == RecoveryController.RECOVERY_STATUS_SYNCED;
+ }
+
+ /**
+ * @throws ActiveSecondaryNotInKeychainException if the currently active secondary key is not in
+ * the keychain.
+ * @throws IOException if there is an IO issue communicating with the server or loading from
+ * disk.
+ * @throws NoActiveSecondaryKeyException if there is no active key set.
+ * @throws IllegalBlockSizeException if there is an issue decrypting a tertiary key.
+ * @throws InvalidKeyException if any of the secondary keys cannot be used for wrapping or
+ * unwrapping tertiary keys.
+ */
+ private void rotateToKey(RecoverableKeyStoreSecondaryKey newSecondaryKey)
+ throws ActiveSecondaryNotInKeychainException, IOException,
+ NoActiveSecondaryKeyException, IllegalBlockSizeException, InvalidKeyException,
+ InternalRecoveryServiceException, UnrecoverableKeyException,
+ InvalidAlgorithmParameterException, NoSuchAlgorithmException,
+ NoSuchPaddingException {
+ RecoverableKeyStoreSecondaryKey activeSecondaryKey = getActiveSecondaryKey();
+ String activeSecondaryKeyAlias = activeSecondaryKey.getAlias();
+ String newSecondaryKeyAlias = newSecondaryKey.getAlias();
+ if (newSecondaryKeyAlias.equals(activeSecondaryKeyAlias)) {
+ Slog.i(TAG, activeSecondaryKeyAlias + " was already the active alias.");
+ return;
+ }
+
+ TertiaryKeyStore tertiaryKeyStore =
+ TertiaryKeyStore.newInstance(mContext, activeSecondaryKey);
+ Map<String, SecretKey> tertiaryKeys = tertiaryKeyStore.getAll();
+
+ if (tertiaryKeys.isEmpty()) {
+ Slog.i(
+ TAG,
+ "No tertiary keys for " + activeSecondaryKeyAlias + ". No need to rewrap. ");
+ mBackupServer.setActiveSecondaryKeyAlias(
+ newSecondaryKeyAlias, /*tertiaryKeys=*/ Collections.emptyMap());
+ } else {
+ Map<String, WrappedKeyProto.WrappedKey> rewrappedTertiaryKeys =
+ rewrapAll(newSecondaryKey, tertiaryKeys);
+ TertiaryKeyStore.newInstance(mContext, newSecondaryKey).putAll(rewrappedTertiaryKeys);
+ Slog.i(
+ TAG,
+ "Successfully rewrapped " + rewrappedTertiaryKeys.size() + " tertiary keys");
+ mBackupServer.setActiveSecondaryKeyAlias(newSecondaryKeyAlias, rewrappedTertiaryKeys);
+ Slog.i(
+ TAG,
+ "Successfully uploaded new set of tertiary keys to "
+ + newSecondaryKeyAlias
+ + " alias");
+ }
+
+ mCryptoSettings.setActiveSecondaryKeyAlias(newSecondaryKeyAlias);
+ mCryptoSettings.removeNextSecondaryKeyAlias();
+ try {
+ mRecoveryController.removeKey(activeSecondaryKeyAlias);
+ } catch (InternalRecoveryServiceException e) {
+ Slog.e(TAG, "Error removing old secondary key from RecoverableKeyStoreLoader", e);
+ }
+ }
+
+ private RecoverableKeyStoreSecondaryKey getActiveSecondaryKey()
+ throws NoActiveSecondaryKeyException, ActiveSecondaryNotInKeychainException,
+ InternalRecoveryServiceException, UnrecoverableKeyException {
+
+ Optional<String> activeSecondaryAlias = mCryptoSettings.getActiveSecondaryKeyAlias();
+
+ if (!activeSecondaryAlias.isPresent()) {
+ Slog.i(
+ TAG,
+ "Was asked to rotate secondary key, but local config did not have a secondary "
+ + "key alias set.");
+ throw new NoActiveSecondaryKeyException("No local active secondary key set.");
+ }
+
+ String activeSecondaryKeyAlias = activeSecondaryAlias.get();
+ Optional<RecoverableKeyStoreSecondaryKey> secondaryKey =
+ mSecondaryKeyManager.get(activeSecondaryKeyAlias);
+
+ if (!secondaryKey.isPresent()) {
+ throw new ActiveSecondaryNotInKeychainException(
+ String.format(
+ Locale.US,
+ "Had local active recoverable key alias of %s but key was not in"
+ + " user's keychain.",
+ activeSecondaryKeyAlias));
+ }
+
+ return secondaryKey.get();
+ }
+
+ /**
+ * Rewraps all the tertiary keys.
+ *
+ * @param newSecondaryKey The secondary key with which to rewrap the tertiaries.
+ * @param tertiaryKeys The tertiary keys, by package name.
+ * @return The newly wrapped tertiary keys, by package name.
+ * @throws InvalidKeyException if any key is unusable.
+ * @throws IllegalBlockSizeException if could not decrypt.
+ */
+ private Map<String, WrappedKeyProto.WrappedKey> rewrapAll(
+ RecoverableKeyStoreSecondaryKey newSecondaryKey, Map<String, SecretKey> tertiaryKeys)
+ throws InvalidKeyException, IllegalBlockSizeException, NoSuchPaddingException,
+ NoSuchAlgorithmException {
+ Map<String, WrappedKeyProto.WrappedKey> wrappedKeys = new HashMap<>();
+
+ for (String packageName : tertiaryKeys.keySet()) {
+ SecretKey tertiaryKey = tertiaryKeys.get(packageName);
+ wrappedKeys.put(
+ packageName, KeyWrapUtils.wrap(newSecondaryKey.getSecretKey(), tertiaryKey));
+ }
+
+ return wrappedKeys;
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/SizeQuotaExceededException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/SizeQuotaExceededException.java
new file mode 100644
index 000000000000..515db86b6687
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/SizeQuotaExceededException.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+/** Exception thrown when aa backup has exceeded the space allowed for that user */
+public class SizeQuotaExceededException extends RuntimeException {
+ public SizeQuotaExceededException() {
+ super("Backup size quota exceeded.");
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/UnsupportedEncryptedFileException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/UnsupportedEncryptedFileException.java
new file mode 100644
index 000000000000..9a97e3870d83
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/UnsupportedEncryptedFileException.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+/**
+ * Thrown when the backup file provided by the server uses encryption algorithms this version of
+ * backup does not support. This could happen if the backup was created with a newer version of the
+ * code.
+ */
+public class UnsupportedEncryptedFileException extends EncryptedRestoreException {
+ public UnsupportedEncryptedFileException(String message) {
+ super(message);
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransport.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransport.java
new file mode 100644
index 000000000000..c3cb335db89e
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransport.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.transport;
+
+import static com.android.server.backup.encryption.BackupEncryptionService.TAG;
+
+import android.app.backup.BackupTransport;
+import android.app.backup.RestoreDescription;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.backup.IBackupTransport;
+import com.android.server.backup.encryption.KeyValueEncrypter;
+import com.android.server.backup.transport.DelegatingTransport;
+import com.android.server.backup.transport.TransportClient;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * This is an implementation of {@link IBackupTransport} that encrypts (or decrypts) the data when
+ * sending it (or receiving it) from the {@link IBackupTransport} returned by {@link
+ * TransportClient.connect(String)}.
+ */
+public class IntermediateEncryptingTransport extends DelegatingTransport {
+ private static final String BACKUP_TEMP_DIR = "backup";
+ private static final String RESTORE_TEMP_DIR = "restore";
+
+ private final TransportClient mTransportClient;
+ private final Object mConnectLock = new Object();
+ private final Context mContext;
+ private volatile IBackupTransport mRealTransport;
+ private AtomicReference<String> mNextRestorePackage = new AtomicReference<>();
+ private final KeyValueEncrypter mKeyValueEncrypter;
+ private final boolean mShouldEncrypt;
+
+ IntermediateEncryptingTransport(
+ TransportClient transportClient, Context context, boolean shouldEncrypt) {
+ this(transportClient, context, new KeyValueEncrypter(context), shouldEncrypt);
+ }
+
+ @VisibleForTesting
+ IntermediateEncryptingTransport(
+ TransportClient transportClient, Context context, KeyValueEncrypter keyValueEncrypter,
+ boolean shouldEncrypt) {
+ mTransportClient = transportClient;
+ mContext = context;
+ mKeyValueEncrypter = keyValueEncrypter;
+ mShouldEncrypt = shouldEncrypt;
+ }
+
+ @Override
+ protected IBackupTransport getDelegate() throws RemoteException {
+ if (mRealTransport == null) {
+ connect();
+ }
+ Log.d(TAG, "real transport = " + mRealTransport.name());
+ return mRealTransport;
+ }
+
+ @Override
+ public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags)
+ throws RemoteException {
+ if (!mShouldEncrypt) {
+ return super.performBackup(packageInfo, inFd, flags);
+ }
+
+ File encryptedStorageFile = getBackupTempStorage(packageInfo.packageName);
+ if (encryptedStorageFile == null) {
+ return BackupTransport.TRANSPORT_ERROR;
+ }
+
+ // Encrypt the backup data and write it into a temp file.
+ try (OutputStream encryptedOutput = new FileOutputStream(encryptedStorageFile)) {
+ mKeyValueEncrypter.encryptKeyValueData(packageInfo.packageName, inFd,
+ encryptedOutput);
+ } catch (Throwable e) {
+ Log.e(TAG, "Failed to encrypt backup data: ", e);
+ return BackupTransport.TRANSPORT_ERROR;
+ }
+
+ // Pass the temp file to the real transport for backup.
+ try (FileInputStream encryptedInput = new FileInputStream(encryptedStorageFile)) {
+ return super.performBackup(
+ packageInfo, ParcelFileDescriptor.dup(encryptedInput.getFD()), flags);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to read encrypted data from temp storage: ", e);
+ return BackupTransport.TRANSPORT_ERROR;
+ }
+ }
+
+ @Override
+ public int getRestoreData(ParcelFileDescriptor outFd) throws RemoteException {
+ if (!mShouldEncrypt) {
+ return super.getRestoreData(outFd);
+ }
+
+ String nextRestorePackage = mNextRestorePackage.get();
+ if (nextRestorePackage == null) {
+ Log.e(TAG, "No next restore package set");
+ return BackupTransport.TRANSPORT_ERROR;
+ }
+
+ File encryptedStorageFile = getRestoreTempStorage(nextRestorePackage);
+ if (encryptedStorageFile == null) {
+ return BackupTransport.TRANSPORT_ERROR;
+ }
+
+ // Get encrypted restore data from the real transport and write it into a temp file.
+ try (FileOutputStream outputStream = new FileOutputStream(encryptedStorageFile)) {
+ int status = super.getRestoreData(ParcelFileDescriptor.dup(outputStream.getFD()));
+ if (status != BackupTransport.TRANSPORT_OK) {
+ Log.e(TAG, "Failed to read restore data from transport, status = " + status);
+ return status;
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to write encrypted data to temp storage: ", e);
+ return BackupTransport.TRANSPORT_ERROR;
+ }
+
+ // Decrypt the data and write it into the fd given by the real transport.
+ try (InputStream inputStream = new FileInputStream(encryptedStorageFile)) {
+ mKeyValueEncrypter.decryptKeyValueData(nextRestorePackage, inputStream, outFd);
+ encryptedStorageFile.delete();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to decrypt restored data: ", e);
+ return BackupTransport.TRANSPORT_ERROR;
+ }
+
+ return BackupTransport.TRANSPORT_OK;
+ }
+
+ @Override
+ public RestoreDescription nextRestorePackage() throws RemoteException {
+ if (!mShouldEncrypt) {
+ return super.nextRestorePackage();
+ }
+
+ RestoreDescription restoreDescription = super.nextRestorePackage();
+ mNextRestorePackage.set(restoreDescription.getPackageName());
+
+ return restoreDescription;
+ }
+
+ @VisibleForTesting
+ protected File getBackupTempStorage(String packageName) {
+ return getTempStorage(packageName, BACKUP_TEMP_DIR);
+ }
+
+ @VisibleForTesting
+ protected File getRestoreTempStorage(String packageName) {
+ return getTempStorage(packageName, RESTORE_TEMP_DIR);
+ }
+
+ private File getTempStorage(String packageName, String operationType) {
+ File encryptedDir = new File(mContext.getFilesDir(), operationType);
+ encryptedDir.mkdir();
+ File encryptedFile = new File(encryptedDir, packageName);
+ try {
+ encryptedFile.createNewFile();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to create temp file for encrypted data: ", e);
+ }
+ return encryptedFile;
+ }
+
+ private void connect() throws RemoteException {
+ Log.i(TAG, "connecting " + mTransportClient);
+ synchronized (mConnectLock) {
+ if (mRealTransport == null) {
+ mRealTransport = mTransportClient.connect("IntermediateEncryptingTransport");
+ if (mRealTransport == null) {
+ throw new RemoteException("Could not connect: " + mTransportClient);
+ }
+ }
+ }
+ }
+
+ @VisibleForTesting
+ TransportClient getClient() {
+ return mTransportClient;
+ }
+
+ @VisibleForTesting
+ void setNextRestorePackage(String nextRestorePackage) {
+ mNextRestorePackage.set(nextRestorePackage);
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransportManager.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransportManager.java
new file mode 100644
index 000000000000..7c4082c2a54d
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransportManager.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.transport;
+
+import static com.android.server.backup.encryption.BackupEncryptionService.TAG;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.backup.IBackupTransport;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.server.backup.transport.TransportClientManager;
+import com.android.server.backup.transport.TransportStats;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/** Handles creation and cleanup of {@link IntermediateEncryptingTransport} instances. */
+public class IntermediateEncryptingTransportManager {
+ private static final String CALLER = "IntermediateEncryptingTransportManager";
+ private final TransportClientManager mTransportClientManager;
+ private final Object mTransportsLock = new Object();
+ private final Map<ComponentName, IntermediateEncryptingTransport> mTransports = new HashMap<>();
+ private Context mContext;
+
+ @VisibleForTesting
+ IntermediateEncryptingTransportManager(TransportClientManager transportClientManager) {
+ mTransportClientManager = transportClientManager;
+ }
+
+ public IntermediateEncryptingTransportManager(Context context) {
+ this(new TransportClientManager(UserHandle.myUserId(), context, new TransportStats()));
+ mContext = context;
+ }
+
+ /**
+ * Extract the {@link ComponentName} corresponding to the real {@link IBackupTransport}, and
+ * provide a {@link IntermediateEncryptingTransport} which is an implementation of {@link
+ * IBackupTransport} that encrypts (or decrypts) the data when sending it (or receiving it) from
+ * the real {@link IBackupTransport}.
+ *
+ * @param intent {@link Intent} created with a call to {@link
+ * TransportClientManager.getEncryptingTransportIntent(ComponentName)}.
+ * @return
+ */
+ public IntermediateEncryptingTransport get(Intent intent) {
+ Intent transportIntent = TransportClientManager.getRealTransportIntent(intent);
+ Log.i(TAG, "get: intent:" + intent + " transportIntent:" + transportIntent);
+ synchronized (mTransportsLock) {
+ return mTransports.computeIfAbsent(
+ transportIntent.getComponent(), c -> create(transportIntent));
+ }
+ }
+
+ /** Create an instance of {@link IntermediateEncryptingTransport}. */
+ private IntermediateEncryptingTransport create(Intent realTransportIntent) {
+ Log.d(TAG, "create: intent:" + realTransportIntent);
+
+ LockPatternUtils patternUtils = new LockPatternUtils(mContext);
+ boolean shouldEncrypt =
+ realTransportIntent.getComponent().getClassName().contains("EncryptedLocalTransport")
+ && (patternUtils.isLockPatternEnabled(UserHandle.myUserId())
+ || patternUtils.isLockPasswordEnabled(UserHandle.myUserId()));
+
+ return new IntermediateEncryptingTransport(
+ mTransportClientManager.getTransportClient(
+ realTransportIntent.getComponent(),
+ realTransportIntent.getExtras(),
+ CALLER),
+ mContext,
+ shouldEncrypt);
+ }
+
+ /**
+ * Cleanup the {@link IntermediateEncryptingTransport} which was created by a call to {@link
+ * #get(Intent)} with this {@link Intent}.
+ */
+ public void cleanup(Intent intent) {
+ Intent transportIntent = TransportClientManager.getRealTransportIntent(intent);
+ Log.i(TAG, "cleanup: intent:" + intent + " transportIntent:" + transportIntent);
+
+ IntermediateEncryptingTransport transport;
+ synchronized (mTransportsLock) {
+ transport = mTransports.remove(transportIntent.getComponent());
+ }
+ if (transport != null) {
+ mTransportClientManager.disposeOfTransportClient(transport.getClient(), CALLER);
+ } else {
+ Log.i(TAG, "Could not find IntermediateEncryptingTransport");
+ }
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric-integration/Android.bp b/packages/BackupEncryption/test/robolectric-integration/Android.bp
new file mode 100644
index 000000000000..67365df4b28f
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric-integration/Android.bp
@@ -0,0 +1,34 @@
+// 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.
+
+android_robolectric_test {
+ name: "BackupEncryptionRoboIntegTests",
+ srcs: [
+ "src/**/*.java",
+ ],
+ java_resource_dirs: ["config"],
+ libs: [
+ "backup-encryption-protos",
+ "platform-test-annotations",
+ "testng",
+ "truth-prebuilt",
+ "BackupEncryptionRoboTests",
+ ],
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ ],
+ instrumentation_for: "BackupEncryption",
+}
diff --git a/packages/BackupEncryption/test/robolectric-integration/AndroidManifest.xml b/packages/BackupEncryption/test/robolectric-integration/AndroidManifest.xml
new file mode 100644
index 000000000000..c3930cc7c4f1
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric-integration/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ coreApp="true"
+ package="com.android.server.backup.encryption.robointeg">
+
+ <application/>
+
+</manifest>
diff --git a/packages/BackupEncryption/test/robolectric-integration/config/robolectric.properties b/packages/BackupEncryption/test/robolectric-integration/config/robolectric.properties
new file mode 100644
index 000000000000..26fceb3f84a4
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric-integration/config/robolectric.properties
@@ -0,0 +1,17 @@
+#
+# 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.
+#
+
+sdk=NEWEST_SDK
diff --git a/packages/BackupEncryption/test/robolectric-integration/src/com/android/server/backup/encryption/RoundTripTest.java b/packages/BackupEncryption/test/robolectric-integration/src/com/android/server/backup/encryption/RoundTripTest.java
new file mode 100644
index 000000000000..a432d91828cf
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric-integration/src/com/android/server/backup/encryption/RoundTripTest.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.security.keystore.recovery.InternalRecoveryServiceException;
+import android.security.keystore.recovery.RecoveryController;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.server.backup.encryption.client.CryptoBackupServer;
+import com.android.server.backup.encryption.keys.KeyWrapUtils;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
+import com.android.server.backup.encryption.keys.TertiaryKeyManager;
+import com.android.server.backup.encryption.keys.TertiaryKeyRotationScheduler;
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+import com.android.server.backup.encryption.tasks.EncryptedFullBackupTask;
+import com.android.server.backup.encryption.tasks.EncryptedFullRestoreTask;
+import com.android.server.backup.encryption.tasks.EncryptedKvBackupTask;
+import com.android.server.backup.encryption.tasks.EncryptedKvRestoreTask;
+import com.android.server.testing.shadows.DataEntity;
+import com.android.server.testing.shadows.ShadowBackupDataInput;
+import com.android.server.testing.shadows.ShadowBackupDataOutput;
+import com.android.server.testing.shadows.ShadowRecoveryController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Optional;
+import java.util.Map;
+import java.util.Set;
+
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+
+@Config(
+ shadows = {
+ ShadowBackupDataInput.class,
+ ShadowBackupDataOutput.class,
+ ShadowRecoveryController.class
+ })
+@RunWith(RobolectricTestRunner.class)
+public class RoundTripTest {
+ private static final DataEntity[] KEY_VALUE_DATA = {
+ new DataEntity("test_key_1", "test_value_1"),
+ new DataEntity("test_key_2", "test_value_2"),
+ new DataEntity("test_key_3", "test_value_3")
+ };
+
+ /** Amount of data we want to round trip in this test */
+ private static final int TEST_DATA_SIZE = 1024 * 1024; // 1MB
+
+ /** Buffer size used when reading data from the restore task */
+ private static final int READ_BUFFER_SIZE = 1024; // 1024 byte buffer.
+
+ /** Key parameters used for the secondary encryption key */
+ private static final String KEY_ALGORITHM = "AES";
+
+ private static final int KEY_SIZE_BITS = 256;
+
+ /** Package name for our test package */
+ private static final String TEST_PACKAGE_NAME = "com.android.backup.test";
+
+ /** The name we use to refer to our secondary key */
+ private static final String TEST_KEY_ALIAS = "test/backup/KEY_ALIAS";
+
+ /** Original data used for comparison after round trip */
+ private final byte[] mOriginalData = new byte[TEST_DATA_SIZE];
+
+ /** App context, used to store the key data and chunk listings */
+ private Context mContext;
+
+ /** The secondary key we're using for the test */
+ private RecoverableKeyStoreSecondaryKey mSecondaryKey;
+
+ /** Source of random material which is considered non-predictable in its' generation */
+ private final SecureRandom mSecureRandom = new SecureRandom();
+
+ private RecoverableKeyStoreSecondaryKeyManager.RecoverableKeyStoreSecondaryKeyManagerProvider
+ mSecondaryKeyManagerProvider;
+ private DummyServer mDummyServer;
+ private RecoveryController mRecoveryController;
+
+ @Mock private ParcelFileDescriptor mParcelFileDescriptor;
+
+ @Before
+ public void setUp() throws NoSuchAlgorithmException, InternalRecoveryServiceException {
+ MockitoAnnotations.initMocks(this);
+
+ ShadowBackupDataInput.reset();
+ ShadowBackupDataOutput.reset();
+
+ mContext = ApplicationProvider.getApplicationContext();
+ mSecondaryKey = new RecoverableKeyStoreSecondaryKey(TEST_KEY_ALIAS, generateAesKey());
+ mDummyServer = new DummyServer();
+ mSecondaryKeyManagerProvider =
+ () ->
+ new RecoverableKeyStoreSecondaryKeyManager(
+ RecoveryController.getInstance(mContext), mSecureRandom);
+
+ fillBuffer(mOriginalData);
+ }
+
+ @Test
+ public void testFull_nonIncrementalBackupAndRestoreAreSuccessful() throws Exception {
+ byte[] backupData = performFullBackup(mOriginalData);
+ assertThat(backupData).isNotEqualTo(mOriginalData);
+ byte[] restoredData = performFullRestore(backupData);
+ assertThat(restoredData).isEqualTo(mOriginalData);
+ }
+
+ @Test
+ public void testKeyValue_nonIncrementalBackupAndRestoreAreSuccessful() throws Exception {
+ byte[] backupData = performNonIncrementalKeyValueBackup(KEY_VALUE_DATA);
+
+ // Get the secondary key used to do backup.
+ Optional<RecoverableKeyStoreSecondaryKey> secondaryKey =
+ mSecondaryKeyManagerProvider.get().get(mDummyServer.mSecondaryKeyAlias);
+ assertThat(secondaryKey.isPresent()).isTrue();
+
+ Set<DataEntity> restoredData = performKeyValueRestore(backupData, secondaryKey.get());
+
+ assertThat(restoredData).containsExactly(KEY_VALUE_DATA).inOrder();
+ }
+
+ /** Perform a key/value backup and return the backed-up representation of the data */
+ private byte[] performNonIncrementalKeyValueBackup(DataEntity[] backupData)
+ throws Exception {
+ // Populate test key/value data.
+ for (DataEntity entity : backupData) {
+ ShadowBackupDataInput.addEntity(entity);
+ }
+
+ EncryptedKvBackupTask.EncryptedKvBackupTaskFactory backupTaskFactory =
+ new EncryptedKvBackupTask.EncryptedKvBackupTaskFactory();
+ EncryptedKvBackupTask backupTask =
+ backupTaskFactory.newInstance(
+ mContext,
+ mSecureRandom,
+ mDummyServer,
+ CryptoSettings.getInstance(mContext),
+ mSecondaryKeyManagerProvider,
+ mParcelFileDescriptor,
+ TEST_PACKAGE_NAME);
+
+ backupTask.performBackup(/* incremental */ false);
+
+ return mDummyServer.mStoredData;
+ }
+
+ /** Perform a full backup and return the backed-up representation of the data */
+ private byte[] performFullBackup(byte[] backupData) throws Exception {
+ DummyServer dummyServer = new DummyServer();
+ EncryptedFullBackupTask backupTask =
+ EncryptedFullBackupTask.newInstance(
+ mContext,
+ dummyServer,
+ mSecureRandom,
+ mSecondaryKey,
+ TEST_PACKAGE_NAME,
+ new ByteArrayInputStream(backupData));
+ backupTask.call();
+ return dummyServer.mStoredData;
+ }
+
+ private Set<DataEntity> performKeyValueRestore(
+ byte[] backupData, RecoverableKeyStoreSecondaryKey secondaryKey) throws Exception {
+ EncryptedKvRestoreTask.EncryptedKvRestoreTaskFactory restoreTaskFactory =
+ new EncryptedKvRestoreTask.EncryptedKvRestoreTaskFactory();
+ EncryptedKvRestoreTask restoreTask =
+ restoreTaskFactory.newInstance(
+ mContext,
+ mSecondaryKeyManagerProvider,
+ new FakeFullRestoreDownloader(backupData),
+ secondaryKey.getAlias(),
+ KeyWrapUtils.wrap(
+ secondaryKey.getSecretKey(), getTertiaryKey(secondaryKey)));
+ restoreTask.getRestoreData(mParcelFileDescriptor);
+ return ShadowBackupDataOutput.getEntities();
+ }
+
+ /** Perform a full restore and return the bytes obtained from the restore process */
+ private byte[] performFullRestore(byte[] backupData)
+ throws IOException, NoSuchAlgorithmException, NoSuchPaddingException,
+ InvalidAlgorithmParameterException, InvalidKeyException,
+ IllegalBlockSizeException {
+ ByteArrayOutputStream decryptedOutput = new ByteArrayOutputStream();
+
+ EncryptedFullRestoreTask restoreTask =
+ EncryptedFullRestoreTask.newInstance(
+ mContext,
+ new FakeFullRestoreDownloader(backupData),
+ getTertiaryKey(mSecondaryKey));
+
+ byte[] buffer = new byte[READ_BUFFER_SIZE];
+ int bytesRead = restoreTask.readNextChunk(buffer);
+ while (bytesRead != -1) {
+ decryptedOutput.write(buffer, 0, bytesRead);
+ bytesRead = restoreTask.readNextChunk(buffer);
+ }
+
+ return decryptedOutput.toByteArray();
+ }
+
+ /** Get the tertiary key for our test package from the key manager */
+ private SecretKey getTertiaryKey(RecoverableKeyStoreSecondaryKey secondaryKey)
+ throws IllegalBlockSizeException, InvalidAlgorithmParameterException,
+ NoSuchAlgorithmException, IOException, NoSuchPaddingException,
+ InvalidKeyException {
+ TertiaryKeyManager tertiaryKeyManager =
+ new TertiaryKeyManager(
+ mContext,
+ mSecureRandom,
+ TertiaryKeyRotationScheduler.getInstance(mContext),
+ secondaryKey,
+ TEST_PACKAGE_NAME);
+ return tertiaryKeyManager.getKey();
+ }
+
+ /** Fill a buffer with data in a predictable way */
+ private void fillBuffer(byte[] buffer) {
+ byte loopingCounter = 0;
+ for (int i = 0; i < buffer.length; i++) {
+ buffer[i] = loopingCounter;
+ loopingCounter++;
+ }
+ }
+
+ /** Generate a new, random, AES key */
+ public static SecretKey generateAesKey() throws NoSuchAlgorithmException {
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
+ keyGenerator.init(KEY_SIZE_BITS);
+ return keyGenerator.generateKey();
+ }
+
+ /**
+ * Dummy backup data endpoint. This stores the data so we can use it in subsequent test steps.
+ */
+ private static class DummyServer implements CryptoBackupServer {
+ private static final String DUMMY_DOC_ID = "DummyDoc";
+
+ byte[] mStoredData = null;
+ String mSecondaryKeyAlias;
+
+ @Override
+ public String uploadIncrementalBackup(
+ String packageName,
+ String oldDocId,
+ byte[] diffScript,
+ WrappedKeyProto.WrappedKey tertiaryKey) {
+ throw new RuntimeException("Not Implemented");
+ }
+
+ @Override
+ public String uploadNonIncrementalBackup(
+ String packageName, byte[] data, WrappedKeyProto.WrappedKey tertiaryKey) {
+ assertThat(packageName).isEqualTo(TEST_PACKAGE_NAME);
+ mStoredData = data;
+ return DUMMY_DOC_ID;
+ }
+
+ @Override
+ public void setActiveSecondaryKeyAlias(
+ String keyAlias, Map<String, WrappedKeyProto.WrappedKey> tertiaryKeys) {
+ mSecondaryKeyAlias = keyAlias;
+ }
+ }
+
+ /** Fake package wrapper which returns data from a byte array. */
+ private static class FakeFullRestoreDownloader extends FullRestoreDownloader {
+ private final ByteArrayInputStream mData;
+
+ FakeFullRestoreDownloader(byte[] data) {
+ // We override all methods of the superclass, so it does not require any collaborators.
+ super();
+ mData = new ByteArrayInputStream(data);
+ }
+
+ @Override
+ public int readNextChunk(byte[] buffer) throws IOException {
+ return mData.read(buffer);
+ }
+
+ @Override
+ public void finish(FinishType finishType) {
+ // Do nothing.
+ }
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/Android.bp b/packages/BackupEncryption/test/robolectric/Android.bp
index 4e42ce7366f0..2a36dcf0baba 100644
--- a/packages/BackupEncryption/test/robolectric/Android.bp
+++ b/packages/BackupEncryption/test/robolectric/Android.bp
@@ -16,7 +16,7 @@ android_robolectric_test {
name: "BackupEncryptionRoboTests",
srcs: [
"src/**/*.java",
- ":FrameworksServicesRoboShadows",
+// ":FrameworksServicesRoboShadows",
],
java_resource_dirs: ["config"],
libs: [
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/StreamUtilsTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/StreamUtilsTest.java
new file mode 100644
index 000000000000..a95e87e3a8b7
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/StreamUtilsTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+@RunWith(RobolectricTestRunner.class)
+public class StreamUtilsTest {
+ private static final int SOURCE_DATA_SIZE = 64;
+
+ private byte[] mSourceData;
+
+ private InputStream mSource;
+ private ByteArrayOutputStream mDestination;
+
+ @Before
+ public void setUp() {
+ mSourceData = new byte[SOURCE_DATA_SIZE];
+ for (byte i = 0; i < SOURCE_DATA_SIZE; i++) {
+ mSourceData[i] = i;
+ }
+ mSource = new ByteArrayInputStream(mSourceData);
+ mDestination = new ByteArrayOutputStream();
+ }
+
+ @Test
+ public void copyStream_copiesAllBytesIfAsked() throws IOException {
+ StreamUtils.copyStream(mSource, mDestination, mSourceData.length);
+ assertOutputHasBytes(mSourceData.length);
+ }
+
+ @Test
+ public void copyStream_stopsShortIfAsked() throws IOException {
+ StreamUtils.copyStream(mSource, mDestination, mSourceData.length - 10);
+ assertOutputHasBytes(mSourceData.length - 10);
+ }
+
+ @Test
+ public void copyStream_stopsShortIfAskedToCopyMoreThanAvailable() throws IOException {
+ StreamUtils.copyStream(mSource, mDestination, mSourceData.length + 10);
+ assertOutputHasBytes(mSourceData.length);
+ }
+
+ private void assertOutputHasBytes(int count) {
+ byte[] output = mDestination.toByteArray();
+ assertThat(output.length).isEqualTo(count);
+ for (int i = 0; i < count; i++) {
+ assertThat(output[i]).isEqualTo(mSourceData[i]);
+ }
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/BackupFileBuilderTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/BackupFileBuilderTest.java
new file mode 100644
index 000000000000..590938efe148
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/BackupFileBuilderTest.java
@@ -0,0 +1,614 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.chunking;
+
+import static com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.AES_256_GCM;
+import static com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED;
+import static com.android.server.backup.testing.CryptoTestUtils.newChunk;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static junit.framework.Assert.fail;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.testng.Assert.assertThrows;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
+import com.android.server.backup.encryption.testing.DiffScriptProcessor;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.Files;
+import com.google.common.primitives.Bytes;
+import com.google.common.primitives.Longs;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class BackupFileBuilderTest {
+ private static final String TEST_DATA_1 =
+ "I'm already there or close to [T7-9/executive level] in terms of big-picture vision";
+ private static final String TEST_DATA_2 =
+ "I was known for Real Games and should have been brought in for advice";
+ private static final String TEST_DATA_3 =
+ "Pride is rooted in the delusional belief held by all humans in an unchanging self";
+
+ private static final byte[] TEST_FINGERPRINT_MIXER_SALT =
+ Arrays.copyOf(new byte[] {22}, ChunkHash.HASH_LENGTH_BYTES);
+
+ private static final ChunkHash TEST_HASH_1 =
+ new ChunkHash(Arrays.copyOf(new byte[] {0}, EncryptedChunk.KEY_LENGTH_BYTES));
+ private static final ChunkHash TEST_HASH_2 =
+ new ChunkHash(Arrays.copyOf(new byte[] {1}, EncryptedChunk.KEY_LENGTH_BYTES));
+ private static final ChunkHash TEST_HASH_3 =
+ new ChunkHash(Arrays.copyOf(new byte[] {2}, EncryptedChunk.KEY_LENGTH_BYTES));
+
+ private static final byte[] TEST_NONCE =
+ Arrays.copyOf(new byte[] {3}, EncryptedChunk.NONCE_LENGTH_BYTES);
+
+ private static final EncryptedChunk TEST_CHUNK_1 =
+ EncryptedChunk.create(TEST_HASH_1, TEST_NONCE, TEST_DATA_1.getBytes(UTF_8));
+ private static final EncryptedChunk TEST_CHUNK_2 =
+ EncryptedChunk.create(TEST_HASH_2, TEST_NONCE, TEST_DATA_2.getBytes(UTF_8));
+ private static final EncryptedChunk TEST_CHUNK_3 =
+ EncryptedChunk.create(TEST_HASH_3, TEST_NONCE, TEST_DATA_3.getBytes(UTF_8));
+
+ private static final byte[] TEST_CHECKSUM = {1, 2, 3, 4, 5, 6};
+
+ @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+ private File mOldFile;
+ private ChunksMetadataProto.ChunkListing mOldChunkListing;
+ private EncryptedChunkEncoder mEncryptedChunkEncoder;
+
+ @Before
+ public void setUp() {
+ mEncryptedChunkEncoder = new LengthlessEncryptedChunkEncoder();
+ }
+
+ @Test
+ public void writeChunks_nonIncremental_writesCorrectRawData() throws Exception {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ BackupFileBuilder backupFileBuilder = BackupFileBuilder.createForNonIncremental(output);
+
+ backupFileBuilder.writeChunks(
+ ImmutableList.of(TEST_HASH_1, TEST_HASH_2),
+ getNewChunkMap(TEST_HASH_1, TEST_HASH_2));
+
+ byte[] actual = output.toByteArray();
+ byte[] expected =
+ Bytes.concat(
+ TEST_CHUNK_1.nonce(),
+ TEST_CHUNK_1.encryptedBytes(),
+ TEST_CHUNK_2.nonce(),
+ TEST_CHUNK_2.encryptedBytes());
+ assertThat(actual).asList().containsExactlyElementsIn(Bytes.asList(expected)).inOrder();
+ }
+
+ @Test
+ public void writeChunks_nonIncrementalWithDuplicates_writesEachChunkOnlyOnce()
+ throws Exception {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ BackupFileBuilder backupFileBuilder = BackupFileBuilder.createForNonIncremental(output);
+
+ backupFileBuilder.writeChunks(
+ ImmutableList.of(TEST_HASH_1, TEST_HASH_2, TEST_HASH_1),
+ getNewChunkMap(TEST_HASH_1, TEST_HASH_2));
+
+ byte[] actual = output.toByteArray();
+ byte[] expected =
+ Bytes.concat(
+ TEST_CHUNK_1.nonce(),
+ TEST_CHUNK_1.encryptedBytes(),
+ TEST_CHUNK_2.nonce(),
+ TEST_CHUNK_2.encryptedBytes());
+ assertThat(actual).asList().containsExactlyElementsIn(Bytes.asList(expected)).inOrder();
+ }
+
+ @Test
+ public void writeChunks_incremental_writesParsableDiffScript() throws Exception {
+ // We will insert chunk 2 in between chunks 1 and 3.
+ setUpOldBackupWithChunks(ImmutableList.of(TEST_CHUNK_1, TEST_CHUNK_3));
+ ByteArrayOutputStream diffOutputStream = new ByteArrayOutputStream();
+ BackupFileBuilder backupFileBuilder =
+ BackupFileBuilder.createForIncremental(diffOutputStream, mOldChunkListing);
+
+ backupFileBuilder.writeChunks(
+ ImmutableList.of(TEST_HASH_1, TEST_HASH_2, TEST_HASH_3),
+ getNewChunkMap(TEST_HASH_2));
+ backupFileBuilder.finish(getTestMetadata());
+
+ byte[] actual =
+ stripMetadataAndPositionFromOutput(parseDiffScript(diffOutputStream.toByteArray()));
+ byte[] expected =
+ Bytes.concat(
+ TEST_CHUNK_1.nonce(),
+ TEST_CHUNK_1.encryptedBytes(),
+ TEST_CHUNK_2.nonce(),
+ TEST_CHUNK_2.encryptedBytes(),
+ TEST_CHUNK_3.nonce(),
+ TEST_CHUNK_3.encryptedBytes());
+ assertThat(actual).asList().containsExactlyElementsIn(Bytes.asList(expected)).inOrder();
+ }
+
+ @Test
+ public void writeChunks_incrementalWithDuplicates_writesEachChunkOnlyOnce() throws Exception {
+ // We will insert chunk 2 twice in between chunks 1 and 3.
+ setUpOldBackupWithChunks(ImmutableList.of(TEST_CHUNK_1, TEST_CHUNK_3));
+ ByteArrayOutputStream diffOutputStream = new ByteArrayOutputStream();
+ BackupFileBuilder backupFileBuilder =
+ BackupFileBuilder.createForIncremental(diffOutputStream, mOldChunkListing);
+
+ backupFileBuilder.writeChunks(
+ ImmutableList.of(TEST_HASH_1, TEST_HASH_2, TEST_HASH_2, TEST_HASH_3),
+ getNewChunkMap(TEST_HASH_2));
+ backupFileBuilder.finish(getTestMetadata());
+
+ byte[] actual =
+ stripMetadataAndPositionFromOutput(parseDiffScript(diffOutputStream.toByteArray()));
+ byte[] expected =
+ Bytes.concat(
+ TEST_CHUNK_1.nonce(),
+ TEST_CHUNK_1.encryptedBytes(),
+ TEST_CHUNK_2.nonce(),
+ TEST_CHUNK_2.encryptedBytes(),
+ TEST_CHUNK_3.nonce(),
+ TEST_CHUNK_3.encryptedBytes());
+ assertThat(actual).asList().containsExactlyElementsIn(Bytes.asList(expected)).inOrder();
+ }
+
+ @Test
+ public void writeChunks_writesChunksInOrderOfHash() throws Exception {
+ setUpOldBackupWithChunks(ImmutableList.of());
+ ByteArrayOutputStream diffOutputStream = new ByteArrayOutputStream();
+ BackupFileBuilder backupFileBuilder =
+ BackupFileBuilder.createForIncremental(diffOutputStream, mOldChunkListing);
+
+ // Write chunks out of order.
+ backupFileBuilder.writeChunks(
+ ImmutableList.of(TEST_HASH_2, TEST_HASH_1),
+ getNewChunkMap(TEST_HASH_2, TEST_HASH_1));
+ backupFileBuilder.finish(getTestMetadata());
+
+ byte[] actual =
+ stripMetadataAndPositionFromOutput(parseDiffScript(diffOutputStream.toByteArray()));
+ byte[] expected =
+ Bytes.concat(
+ TEST_CHUNK_1.nonce(),
+ TEST_CHUNK_1.encryptedBytes(),
+ TEST_CHUNK_2.nonce(),
+ TEST_CHUNK_2.encryptedBytes());
+ assertThat(actual).asList().containsExactlyElementsIn(Bytes.asList(expected)).inOrder();
+ }
+
+ @Test
+ public void writeChunks_alreadyFlushed_throwsException() throws Exception {
+ BackupFileBuilder backupFileBuilder =
+ BackupFileBuilder.createForIncremental(
+ new ByteArrayOutputStream(), new ChunksMetadataProto.ChunkListing());
+ backupFileBuilder.finish(getTestMetadata());
+
+ assertThrows(
+ IllegalStateException.class,
+ () -> backupFileBuilder.writeChunks(ImmutableList.of(), getNewChunkMap()));
+ }
+
+ @Test
+ public void getNewChunkListing_hasChunksInOrderOfKey() throws Exception {
+ // We will insert chunk 2 in between chunks 1 and 3.
+ setUpOldBackupWithChunks(ImmutableList.of(TEST_CHUNK_1, TEST_CHUNK_3));
+ BackupFileBuilder backupFileBuilder =
+ BackupFileBuilder.createForIncremental(
+ new ByteArrayOutputStream(), mOldChunkListing);
+
+ // Write chunks out of order.
+ backupFileBuilder.writeChunks(
+ ImmutableList.of(TEST_HASH_1, TEST_HASH_3, TEST_HASH_2),
+ getNewChunkMap(TEST_HASH_2));
+ backupFileBuilder.finish(getTestMetadata());
+
+ ChunksMetadataProto.ChunkListing expected = expectedChunkListing();
+ ChunksMetadataProto.ChunkListing actual =
+ backupFileBuilder.getNewChunkListing(TEST_FINGERPRINT_MIXER_SALT);
+ assertListingsEqual(actual, expected);
+ }
+
+ @Test
+ public void getNewChunkListing_writeChunksInTwoBatches_returnsListingContainingAllChunks()
+ throws Exception {
+ // We will insert chunk 2 in between chunks 1 and 3.
+ setUpOldBackupWithChunks(ImmutableList.of(TEST_CHUNK_1, TEST_CHUNK_3));
+ BackupFileBuilder backupFileBuilder =
+ BackupFileBuilder.createForIncremental(
+ new ByteArrayOutputStream(), mOldChunkListing);
+
+ backupFileBuilder.writeChunks(
+ ImmutableList.of(TEST_HASH_1, TEST_HASH_2), getNewChunkMap(TEST_HASH_2));
+ backupFileBuilder.writeChunks(ImmutableList.of(TEST_HASH_3), getNewChunkMap(TEST_HASH_2));
+ backupFileBuilder.finish(getTestMetadata());
+
+ ChunksMetadataProto.ChunkListing expected = expectedChunkListing();
+ ChunksMetadataProto.ChunkListing actual =
+ backupFileBuilder.getNewChunkListing(TEST_FINGERPRINT_MIXER_SALT);
+ assertListingsEqual(actual, expected);
+ }
+
+ @Test
+ public void getNewChunkListing_writeDuplicateChunks_writesEachChunkOnlyOnce() throws Exception {
+ // We will append [2][3][3][2] onto [1].
+ setUpOldBackupWithChunks(ImmutableList.of(TEST_CHUNK_1));
+ BackupFileBuilder backupFileBuilder =
+ BackupFileBuilder.createForIncremental(
+ new ByteArrayOutputStream(), mOldChunkListing);
+
+ backupFileBuilder.writeChunks(
+ ImmutableList.of(TEST_HASH_1, TEST_HASH_2, TEST_HASH_3),
+ getNewChunkMap(TEST_HASH_3, TEST_HASH_2));
+ backupFileBuilder.writeChunks(
+ ImmutableList.of(TEST_HASH_3, TEST_HASH_2),
+ getNewChunkMap(TEST_HASH_3, TEST_HASH_2));
+ backupFileBuilder.finish(getTestMetadata());
+
+ ChunksMetadataProto.ChunkListing expected = expectedChunkListing();
+ ChunksMetadataProto.ChunkListing actual =
+ backupFileBuilder.getNewChunkListing(TEST_FINGERPRINT_MIXER_SALT);
+ assertListingsEqual(actual, expected);
+ }
+
+ @Test
+ public void getNewChunkListing_nonIncrementalWithNoSalt_doesNotThrowOnSerialisation() {
+ BackupFileBuilder backupFileBuilder =
+ BackupFileBuilder.createForNonIncremental(new ByteArrayOutputStream());
+
+ ChunksMetadataProto.ChunkListing newChunkListing =
+ backupFileBuilder.getNewChunkListing(/*fingerprintMixerSalt=*/ null);
+
+ // Does not throw.
+ ChunksMetadataProto.ChunkListing.toByteArray(newChunkListing);
+ }
+
+ @Test
+ public void getNewChunkListing_incrementalWithNoSalt_doesNotThrowOnSerialisation()
+ throws Exception {
+
+ setUpOldBackupWithChunks(ImmutableList.of());
+ BackupFileBuilder backupFileBuilder =
+ BackupFileBuilder.createForIncremental(
+ new ByteArrayOutputStream(), mOldChunkListing);
+
+ ChunksMetadataProto.ChunkListing newChunkListing =
+ backupFileBuilder.getNewChunkListing(/*fingerprintMixerSalt=*/ null);
+
+ // Does not throw.
+ ChunksMetadataProto.ChunkListing.toByteArray(newChunkListing);
+ }
+
+ @Test
+ public void getNewChunkListing_nonIncrementalWithNoSalt_hasEmptySalt() {
+ BackupFileBuilder backupFileBuilder =
+ BackupFileBuilder.createForNonIncremental(new ByteArrayOutputStream());
+
+ ChunksMetadataProto.ChunkListing newChunkListing =
+ backupFileBuilder.getNewChunkListing(/*fingerprintMixerSalt=*/ null);
+
+ assertThat(newChunkListing.fingerprintMixerSalt).isEmpty();
+ }
+
+ @Test
+ public void getNewChunkListing_incrementalWithNoSalt_hasEmptySalt() throws Exception {
+ setUpOldBackupWithChunks(ImmutableList.of());
+ BackupFileBuilder backupFileBuilder =
+ BackupFileBuilder.createForIncremental(
+ new ByteArrayOutputStream(), mOldChunkListing);
+
+ ChunksMetadataProto.ChunkListing newChunkListing =
+ backupFileBuilder.getNewChunkListing(/*fingerprintMixerSalt=*/ null);
+
+ assertThat(newChunkListing.fingerprintMixerSalt).isEmpty();
+ }
+
+ @Test
+ public void getNewChunkListing_nonIncrementalWithSalt_hasGivenSalt() {
+ BackupFileBuilder backupFileBuilder =
+ BackupFileBuilder.createForNonIncremental(new ByteArrayOutputStream());
+
+ ChunksMetadataProto.ChunkListing newChunkListing =
+ backupFileBuilder.getNewChunkListing(TEST_FINGERPRINT_MIXER_SALT);
+
+ assertThat(newChunkListing.fingerprintMixerSalt).isEqualTo(TEST_FINGERPRINT_MIXER_SALT);
+ }
+
+ @Test
+ public void getNewChunkListing_incrementalWithSalt_hasGivenSalt() throws Exception {
+ setUpOldBackupWithChunks(ImmutableList.of());
+ BackupFileBuilder backupFileBuilder =
+ BackupFileBuilder.createForIncremental(
+ new ByteArrayOutputStream(), mOldChunkListing);
+
+ ChunksMetadataProto.ChunkListing newChunkListing =
+ backupFileBuilder.getNewChunkListing(TEST_FINGERPRINT_MIXER_SALT);
+
+ assertThat(newChunkListing.fingerprintMixerSalt).isEqualTo(TEST_FINGERPRINT_MIXER_SALT);
+ }
+
+ @Test
+ public void getNewChunkListing_nonIncremental_hasCorrectCipherTypeAndChunkOrderingType() {
+ BackupFileBuilder backupFileBuilder =
+ BackupFileBuilder.createForNonIncremental(new ByteArrayOutputStream());
+
+ ChunksMetadataProto.ChunkListing newChunkListing =
+ backupFileBuilder.getNewChunkListing(/*fingerprintMixerSalt=*/ null);
+
+ assertThat(newChunkListing.cipherType).isEqualTo(ChunksMetadataProto.AES_256_GCM);
+ assertThat(newChunkListing.chunkOrderingType)
+ .isEqualTo(ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED);
+ }
+
+ @Test
+ public void getNewChunkListing_incremental_hasCorrectCipherTypeAndChunkOrderingType()
+ throws Exception {
+ setUpOldBackupWithChunks(ImmutableList.of());
+ BackupFileBuilder backupFileBuilder =
+ BackupFileBuilder.createForIncremental(
+ new ByteArrayOutputStream(), mOldChunkListing);
+
+ ChunksMetadataProto.ChunkListing newChunkListing =
+ backupFileBuilder.getNewChunkListing(/*fingerprintMixerSalt=*/ null);
+
+ assertThat(newChunkListing.cipherType).isEqualTo(ChunksMetadataProto.AES_256_GCM);
+ assertThat(newChunkListing.chunkOrderingType)
+ .isEqualTo(ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED);
+ }
+
+ @Test
+ public void getNewChunkOrdering_chunksHaveCorrectStartPositions() throws Exception {
+ BackupFileBuilder backupFileBuilder =
+ BackupFileBuilder.createForIncremental(
+ new ByteArrayOutputStream(), new ChunksMetadataProto.ChunkListing());
+
+ // Write out of order by key to check that ordering is maintained.
+ backupFileBuilder.writeChunks(
+ ImmutableList.of(TEST_HASH_1, TEST_HASH_3, TEST_HASH_2),
+ getNewChunkMap(TEST_HASH_1, TEST_HASH_3, TEST_HASH_2));
+ backupFileBuilder.finish(getTestMetadata());
+
+ ChunksMetadataProto.ChunkOrdering actual =
+ backupFileBuilder.getNewChunkOrdering(TEST_CHECKSUM);
+ // The chunks are listed in the order they are written above, but the start positions are
+ // determined by the order in the encrypted blob (which is lexicographical by key).
+ int chunk1Start = 0;
+ int chunk2Start =
+ chunk1Start + mEncryptedChunkEncoder.getEncodedLengthOfChunk(TEST_CHUNK_1);
+ int chunk3Start =
+ chunk2Start + mEncryptedChunkEncoder.getEncodedLengthOfChunk(TEST_CHUNK_2);
+
+ int[] expected = {chunk1Start, chunk3Start, chunk2Start};
+ assertThat(actual.starts.length).isEqualTo(expected.length);
+ for (int i = 0; i < actual.starts.length; i++) {
+ assertThat(expected[i]).isEqualTo(actual.starts[i]);
+ }
+ }
+
+ @Test
+ public void getNewChunkOrdering_duplicateChunks_writesDuplicates() throws Exception {
+ BackupFileBuilder backupFileBuilder =
+ BackupFileBuilder.createForIncremental(
+ new ByteArrayOutputStream(), new ChunksMetadataProto.ChunkListing());
+
+ backupFileBuilder.writeChunks(
+ ImmutableList.of(TEST_HASH_1, TEST_HASH_2, TEST_HASH_2),
+ getNewChunkMap(TEST_HASH_1, TEST_HASH_2));
+ backupFileBuilder.writeChunks(
+ ImmutableList.of(TEST_HASH_3, TEST_HASH_3), getNewChunkMap(TEST_HASH_3));
+ backupFileBuilder.finish(getTestMetadata());
+
+ ChunksMetadataProto.ChunkOrdering actual =
+ backupFileBuilder.getNewChunkOrdering(TEST_CHECKSUM);
+ int chunk1Start = 0;
+ int chunk2Start =
+ chunk1Start + mEncryptedChunkEncoder.getEncodedLengthOfChunk(TEST_CHUNK_1);
+ int chunk3Start =
+ chunk2Start + mEncryptedChunkEncoder.getEncodedLengthOfChunk(TEST_CHUNK_2);
+
+ int[] expected = {chunk1Start, chunk2Start, chunk2Start, chunk3Start, chunk3Start};
+ assertThat(actual.starts.length).isEqualTo(expected.length);
+ for (int i = 0; i < actual.starts.length; i++) {
+ assertThat(expected[i]).isEqualTo(actual.starts[i]);
+ }
+ }
+
+ @Test
+ public void getNewChunkOrdering_returnsOrderingWithChecksum() throws Exception {
+ BackupFileBuilder backupFileBuilder =
+ BackupFileBuilder.createForIncremental(
+ new ByteArrayOutputStream(), new ChunksMetadataProto.ChunkListing());
+
+ backupFileBuilder.writeChunks(ImmutableList.of(TEST_HASH_1), getNewChunkMap(TEST_HASH_1));
+ backupFileBuilder.finish(getTestMetadata());
+
+ ChunksMetadataProto.ChunkOrdering actual =
+ backupFileBuilder.getNewChunkOrdering(TEST_CHECKSUM);
+ assertThat(actual.checksum).isEqualTo(TEST_CHECKSUM);
+ }
+
+ @Test
+ public void finish_writesMetadata() throws Exception {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ BackupFileBuilder builder = BackupFileBuilder.createForNonIncremental(output);
+ ChunksMetadataProto.ChunksMetadata expectedMetadata = getTestMetadata();
+
+ builder.finish(expectedMetadata);
+
+ // The output is [metadata]+[long giving size of metadata].
+ byte[] metadataBytes =
+ Arrays.copyOfRange(output.toByteArray(), 0, output.size() - Long.BYTES);
+ ChunksMetadataProto.ChunksMetadata actualMetadata =
+ ChunksMetadataProto.ChunksMetadata.parseFrom(metadataBytes);
+ assertThat(actualMetadata.checksumType).isEqualTo(ChunksMetadataProto.SHA_256);
+ assertThat(actualMetadata.cipherType).isEqualTo(ChunksMetadataProto.AES_256_GCM);
+ }
+
+ @Test
+ public void finish_writesMetadataPosition() throws Exception {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ BackupFileBuilder builder = BackupFileBuilder.createForNonIncremental(output);
+
+ builder.writeChunks(
+ ImmutableList.of(TEST_HASH_1, TEST_HASH_2),
+ getNewChunkMap(TEST_HASH_1, TEST_HASH_2));
+ builder.writeChunks(ImmutableList.of(TEST_HASH_3), getNewChunkMap(TEST_HASH_3));
+ builder.finish(getTestMetadata());
+
+ long expectedPosition =
+ (long) mEncryptedChunkEncoder.getEncodedLengthOfChunk(TEST_CHUNK_1)
+ + mEncryptedChunkEncoder.getEncodedLengthOfChunk(TEST_CHUNK_2)
+ + mEncryptedChunkEncoder.getEncodedLengthOfChunk(TEST_CHUNK_3);
+ long actualPosition =
+ Longs.fromByteArray(
+ Arrays.copyOfRange(
+ output.toByteArray(), output.size() - Long.BYTES, output.size()));
+ assertThat(actualPosition).isEqualTo(expectedPosition);
+ }
+
+ @Test
+ public void finish_flushesOutputStream() throws Exception {
+ OutputStream diffOutputStream = mock(OutputStream.class);
+ BackupFileBuilder backupFileBuilder =
+ BackupFileBuilder.createForIncremental(
+ diffOutputStream, new ChunksMetadataProto.ChunkListing());
+
+ backupFileBuilder.writeChunks(ImmutableList.of(TEST_HASH_1), getNewChunkMap(TEST_HASH_1));
+ diffOutputStream.flush();
+
+ verify(diffOutputStream).flush();
+ }
+
+ private void setUpOldBackupWithChunks(List<EncryptedChunk> chunks) throws Exception {
+ mOldFile = mTemporaryFolder.newFile();
+ ChunksMetadataProto.ChunkListing chunkListing = new ChunksMetadataProto.ChunkListing();
+ chunkListing.fingerprintMixerSalt =
+ Arrays.copyOf(TEST_FINGERPRINT_MIXER_SALT, TEST_FINGERPRINT_MIXER_SALT.length);
+ chunkListing.cipherType = AES_256_GCM;
+ chunkListing.chunkOrderingType = CHUNK_ORDERING_TYPE_UNSPECIFIED;
+
+ List<ChunksMetadataProto.Chunk> knownChunks = new ArrayList<>();
+ try (FileOutputStream outputStream = new FileOutputStream(mOldFile)) {
+ for (EncryptedChunk chunk : chunks) {
+ // Chunks are encoded in the format [nonce]+[data].
+ outputStream.write(chunk.nonce());
+ outputStream.write(chunk.encryptedBytes());
+
+ knownChunks.add(createChunkFor(chunk));
+ }
+
+ outputStream.flush();
+ }
+
+ chunkListing.chunks = knownChunks.toArray(new ChunksMetadataProto.Chunk[0]);
+ mOldChunkListing = chunkListing;
+ }
+
+ private byte[] parseDiffScript(byte[] diffScript) throws Exception {
+ File newFile = mTemporaryFolder.newFile();
+ new DiffScriptProcessor(mOldFile, newFile).process(new ByteArrayInputStream(diffScript));
+ return Files.toByteArray(newFile);
+ }
+
+ private void assertListingsEqual(
+ ChunksMetadataProto.ChunkListing result, ChunksMetadataProto.ChunkListing expected) {
+ assertThat(result.chunks.length).isEqualTo(expected.chunks.length);
+ for (int i = 0; i < result.chunks.length; i++) {
+ assertWithMessage("Chunk " + i)
+ .that(result.chunks[i].length)
+ .isEqualTo(expected.chunks[i].length);
+ assertWithMessage("Chunk " + i)
+ .that(result.chunks[i].hash)
+ .isEqualTo(expected.chunks[i].hash);
+ }
+ }
+
+ private static ImmutableMap<ChunkHash, EncryptedChunk> getNewChunkMap(ChunkHash... hashes) {
+ ImmutableMap.Builder<ChunkHash, EncryptedChunk> builder = ImmutableMap.builder();
+ for (ChunkHash hash : hashes) {
+ if (TEST_HASH_1.equals(hash)) {
+ builder.put(TEST_HASH_1, TEST_CHUNK_1);
+ } else if (TEST_HASH_2.equals(hash)) {
+ builder.put(TEST_HASH_2, TEST_CHUNK_2);
+ } else if (TEST_HASH_3.equals(hash)) {
+ builder.put(TEST_HASH_3, TEST_CHUNK_3);
+ } else {
+ fail("Hash was not recognised: " + hash);
+ }
+ }
+ return builder.build();
+ }
+
+ private static ChunksMetadataProto.ChunksMetadata getTestMetadata() {
+ ChunksMetadataProto.ChunksMetadata metadata = new ChunksMetadataProto.ChunksMetadata();
+ metadata.checksumType = ChunksMetadataProto.SHA_256;
+ metadata.cipherType = AES_256_GCM;
+ return metadata;
+ }
+
+ private static byte[] stripMetadataAndPositionFromOutput(byte[] output) {
+ long metadataStart =
+ Longs.fromByteArray(
+ Arrays.copyOfRange(output, output.length - Long.BYTES, output.length));
+ return Arrays.copyOfRange(output, 0, (int) metadataStart);
+ }
+
+ private ChunksMetadataProto.ChunkListing expectedChunkListing() {
+ ChunksMetadataProto.ChunkListing chunkListing = new ChunksMetadataProto.ChunkListing();
+ chunkListing.fingerprintMixerSalt =
+ Arrays.copyOf(TEST_FINGERPRINT_MIXER_SALT, TEST_FINGERPRINT_MIXER_SALT.length);
+ chunkListing.cipherType = AES_256_GCM;
+ chunkListing.chunkOrderingType = CHUNK_ORDERING_TYPE_UNSPECIFIED;
+ chunkListing.chunks = new ChunksMetadataProto.Chunk[3];
+ chunkListing.chunks[0] = createChunkFor(TEST_CHUNK_1);
+ chunkListing.chunks[1] = createChunkFor(TEST_CHUNK_2);
+ chunkListing.chunks[2] = createChunkFor(TEST_CHUNK_3);
+ return chunkListing;
+ }
+
+ private ChunksMetadataProto.Chunk createChunkFor(EncryptedChunk encryptedChunk) {
+ byte[] chunkHash = encryptedChunk.key().getHash();
+ byte[] hashCopy = Arrays.copyOf(chunkHash, chunkHash.length);
+ return newChunk(hashCopy, mEncryptedChunkEncoder.getEncodedLengthOfChunk(encryptedChunk));
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ProtoStoreTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ProtoStoreTest.java
new file mode 100644
index 000000000000..d73c8e47f609
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ProtoStoreTest.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.chunking;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
+import com.android.server.backup.encryption.protos.nano.KeyValueListingProto;
+
+import com.google.common.collect.ImmutableMap;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Optional;
+
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class ProtoStoreTest {
+ private static final String TEST_KEY_1 = "test_key_1";
+ private static final ChunkHash TEST_HASH_1 =
+ new ChunkHash(Arrays.copyOf(new byte[] {1}, EncryptedChunk.KEY_LENGTH_BYTES));
+ private static final ChunkHash TEST_HASH_2 =
+ new ChunkHash(Arrays.copyOf(new byte[] {2}, EncryptedChunk.KEY_LENGTH_BYTES));
+ private static final int TEST_LENGTH_1 = 10;
+ private static final int TEST_LENGTH_2 = 18;
+
+ private static final String TEST_PACKAGE_1 = "com.example.test1";
+ private static final String TEST_PACKAGE_2 = "com.example.test2";
+
+ @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+ private File mStoreFolder;
+ private ProtoStore<ChunksMetadataProto.ChunkListing> mProtoStore;
+
+ @Before
+ public void setUp() throws Exception {
+ mStoreFolder = mTemporaryFolder.newFolder();
+ mProtoStore = new ProtoStore<>(ChunksMetadataProto.ChunkListing.class, mStoreFolder);
+ }
+
+ @Test
+ public void differentStoreTypes_operateSimultaneouslyWithoutInterfering() throws Exception {
+ ChunksMetadataProto.ChunkListing chunkListing =
+ createChunkListing(ImmutableMap.of(TEST_HASH_1, TEST_LENGTH_1));
+ KeyValueListingProto.KeyValueListing keyValueListing =
+ new KeyValueListingProto.KeyValueListing();
+ keyValueListing.entries = new KeyValueListingProto.KeyValueEntry[1];
+ keyValueListing.entries[0] = new KeyValueListingProto.KeyValueEntry();
+ keyValueListing.entries[0].key = TEST_KEY_1;
+ keyValueListing.entries[0].hash = TEST_HASH_1.getHash();
+
+ Context application = ApplicationProvider.getApplicationContext();
+ ProtoStore<ChunksMetadataProto.ChunkListing> chunkListingStore =
+ ProtoStore.createChunkListingStore(application);
+ ProtoStore<KeyValueListingProto.KeyValueListing> keyValueListingStore =
+ ProtoStore.createKeyValueListingStore(application);
+
+ chunkListingStore.saveProto(TEST_PACKAGE_1, chunkListing);
+ keyValueListingStore.saveProto(TEST_PACKAGE_1, keyValueListing);
+
+ ChunksMetadataProto.ChunkListing actualChunkListing =
+ chunkListingStore.loadProto(TEST_PACKAGE_1).get();
+ KeyValueListingProto.KeyValueListing actualKeyValueListing =
+ keyValueListingStore.loadProto(TEST_PACKAGE_1).get();
+ assertListingsEqual(actualChunkListing, chunkListing);
+ assertThat(actualKeyValueListing.entries.length).isEqualTo(1);
+ assertThat(actualKeyValueListing.entries[0].key).isEqualTo(TEST_KEY_1);
+ assertThat(actualKeyValueListing.entries[0].hash).isEqualTo(TEST_HASH_1.getHash());
+ }
+
+ @Test
+ public void construct_storeLocationIsFile_throws() throws Exception {
+ assertThrows(
+ IOException.class,
+ () ->
+ new ProtoStore<>(
+ ChunksMetadataProto.ChunkListing.class,
+ mTemporaryFolder.newFile()));
+ }
+
+ @Test
+ public void loadChunkListing_noListingExists_returnsEmptyListing() throws Exception {
+ Optional<ChunksMetadataProto.ChunkListing> chunkListing =
+ mProtoStore.loadProto(TEST_PACKAGE_1);
+ assertThat(chunkListing.isPresent()).isFalse();
+ }
+
+ @Test
+ public void loadChunkListing_listingExists_returnsExistingListing() throws Exception {
+ ChunksMetadataProto.ChunkListing expected =
+ createChunkListing(
+ ImmutableMap.of(TEST_HASH_1, TEST_LENGTH_1, TEST_HASH_2, TEST_LENGTH_2));
+ mProtoStore.saveProto(TEST_PACKAGE_1, expected);
+
+ ChunksMetadataProto.ChunkListing result = mProtoStore.loadProto(TEST_PACKAGE_1).get();
+
+ assertListingsEqual(result, expected);
+ }
+
+ @Test
+ public void loadProto_emptyPackageName_throwsException() throws Exception {
+ assertThrows(IllegalArgumentException.class, () -> mProtoStore.loadProto(""));
+ }
+
+ @Test
+ public void loadProto_nullPackageName_throwsException() throws Exception {
+ assertThrows(IllegalArgumentException.class, () -> mProtoStore.loadProto(null));
+ }
+
+ @Test
+ public void loadProto_packageNameContainsSlash_throwsException() throws Exception {
+ assertThrows(
+ IllegalArgumentException.class, () -> mProtoStore.loadProto(TEST_PACKAGE_1 + "/"));
+ }
+
+ @Test
+ public void saveProto_persistsToNewInstance() throws Exception {
+ ChunksMetadataProto.ChunkListing expected =
+ createChunkListing(
+ ImmutableMap.of(TEST_HASH_1, TEST_LENGTH_1, TEST_HASH_2, TEST_LENGTH_2));
+ mProtoStore.saveProto(TEST_PACKAGE_1, expected);
+ mProtoStore = new ProtoStore<>(ChunksMetadataProto.ChunkListing.class, mStoreFolder);
+
+ ChunksMetadataProto.ChunkListing result = mProtoStore.loadProto(TEST_PACKAGE_1).get();
+
+ assertListingsEqual(result, expected);
+ }
+
+ @Test
+ public void saveProto_emptyPackageName_throwsException() throws Exception {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> mProtoStore.saveProto("", new ChunksMetadataProto.ChunkListing()));
+ }
+
+ @Test
+ public void saveProto_nullPackageName_throwsException() throws Exception {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> mProtoStore.saveProto(null, new ChunksMetadataProto.ChunkListing()));
+ }
+
+ @Test
+ public void saveProto_packageNameContainsSlash_throwsException() throws Exception {
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ mProtoStore.saveProto(
+ TEST_PACKAGE_1 + "/", new ChunksMetadataProto.ChunkListing()));
+ }
+
+ @Test
+ public void saveProto_nullListing_throwsException() throws Exception {
+ assertThrows(NullPointerException.class, () -> mProtoStore.saveProto(TEST_PACKAGE_1, null));
+ }
+
+ @Test
+ public void deleteProto_noListingExists_doesNothing() throws Exception {
+ ChunksMetadataProto.ChunkListing listing =
+ createChunkListing(ImmutableMap.of(TEST_HASH_1, TEST_LENGTH_1));
+ mProtoStore.saveProto(TEST_PACKAGE_1, listing);
+
+ mProtoStore.deleteProto(TEST_PACKAGE_2);
+
+ assertThat(mProtoStore.loadProto(TEST_PACKAGE_1).get().chunks.length).isEqualTo(1);
+ }
+
+ @Test
+ public void deleteProto_listingExists_deletesListing() throws Exception {
+ ChunksMetadataProto.ChunkListing listing =
+ createChunkListing(ImmutableMap.of(TEST_HASH_1, TEST_LENGTH_1));
+ mProtoStore.saveProto(TEST_PACKAGE_1, listing);
+
+ mProtoStore.deleteProto(TEST_PACKAGE_1);
+
+ assertThat(mProtoStore.loadProto(TEST_PACKAGE_1).isPresent()).isFalse();
+ }
+
+ @Test
+ public void deleteAllProtos_deletesAllProtos() throws Exception {
+ ChunksMetadataProto.ChunkListing listing1 =
+ createChunkListing(ImmutableMap.of(TEST_HASH_1, TEST_LENGTH_1));
+ ChunksMetadataProto.ChunkListing listing2 =
+ createChunkListing(ImmutableMap.of(TEST_HASH_2, TEST_LENGTH_2));
+ mProtoStore.saveProto(TEST_PACKAGE_1, listing1);
+ mProtoStore.saveProto(TEST_PACKAGE_2, listing2);
+
+ mProtoStore.deleteAllProtos();
+
+ assertThat(mProtoStore.loadProto(TEST_PACKAGE_1).isPresent()).isFalse();
+ assertThat(mProtoStore.loadProto(TEST_PACKAGE_2).isPresent()).isFalse();
+ }
+
+ @Test
+ public void deleteAllProtos_folderDeleted_doesNotCrash() throws Exception {
+ mStoreFolder.delete();
+
+ mProtoStore.deleteAllProtos();
+ }
+
+ private static ChunksMetadataProto.ChunkListing createChunkListing(
+ ImmutableMap<ChunkHash, Integer> chunks) {
+ ChunksMetadataProto.ChunkListing listing = new ChunksMetadataProto.ChunkListing();
+ listing.cipherType = ChunksMetadataProto.AES_256_GCM;
+ listing.chunkOrderingType = ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED;
+
+ List<ChunksMetadataProto.Chunk> chunkProtos = new ArrayList<>();
+ for (Entry<ChunkHash, Integer> entry : chunks.entrySet()) {
+ ChunksMetadataProto.Chunk chunk = new ChunksMetadataProto.Chunk();
+ chunk.hash = entry.getKey().getHash();
+ chunk.length = entry.getValue();
+ chunkProtos.add(chunk);
+ }
+ listing.chunks = chunkProtos.toArray(new ChunksMetadataProto.Chunk[0]);
+ return listing;
+ }
+
+ private void assertListingsEqual(
+ ChunksMetadataProto.ChunkListing result, ChunksMetadataProto.ChunkListing expected) {
+ assertThat(result.chunks.length).isEqualTo(expected.chunks.length);
+ for (int i = 0; i < result.chunks.length; i++) {
+ assertWithMessage("Chunk " + i)
+ .that(result.chunks[i].length)
+ .isEqualTo(expected.chunks[i].length);
+ assertWithMessage("Chunk " + i)
+ .that(result.chunks[i].hash)
+ .isEqualTo(expected.chunks[i].hash);
+ }
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutputTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutputTest.java
new file mode 100644
index 000000000000..215e1cbc725e
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutputTest.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.kv;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.os.Debug;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.chunking.ChunkHasher;
+import com.android.server.backup.encryption.protos.nano.KeyValuePairProto;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Stream;
+
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class DecryptedChunkKvOutputTest {
+ private static final String TEST_KEY_1 = "key_1";
+ private static final String TEST_KEY_2 = "key_2";
+ private static final byte[] TEST_VALUE_1 = {1, 2, 3};
+ private static final byte[] TEST_VALUE_2 = {10, 11, 12, 13};
+ private static final byte[] TEST_PAIR_1 = toByteArray(createPair(TEST_KEY_1, TEST_VALUE_1));
+ private static final byte[] TEST_PAIR_2 = toByteArray(createPair(TEST_KEY_2, TEST_VALUE_2));
+ private static final int TEST_BUFFER_SIZE = Math.max(TEST_PAIR_1.length, TEST_PAIR_2.length);
+
+ @Mock private ChunkHasher mChunkHasher;
+ private DecryptedChunkKvOutput mOutput;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ when(mChunkHasher.computeHash(any()))
+ .thenAnswer(invocation -> fakeHash(invocation.getArgument(0)));
+ mOutput = new DecryptedChunkKvOutput(mChunkHasher);
+ }
+
+ @Test
+ public void open_returnsInstance() throws Exception {
+ assertThat(mOutput.open()).isEqualTo(mOutput);
+ }
+
+ @Test
+ public void processChunk_alreadyClosed_throws() throws Exception {
+ mOutput.open();
+ mOutput.close();
+
+ assertThrows(
+ IllegalStateException.class,
+ () -> mOutput.processChunk(TEST_PAIR_1, TEST_PAIR_1.length));
+ }
+
+ @Test
+ public void getDigest_beforeClose_throws() throws Exception {
+ // TODO: b/141356823 We should add a test which calls .open() here
+ assertThrows(IllegalStateException.class, () -> mOutput.getDigest());
+ }
+
+ @Test
+ public void getDigest_returnsDigestOfSortedHashes() throws Exception {
+ mOutput.open();
+ Debug.waitForDebugger();
+ mOutput.processChunk(Arrays.copyOf(TEST_PAIR_1, TEST_BUFFER_SIZE), TEST_PAIR_1.length);
+ mOutput.processChunk(Arrays.copyOf(TEST_PAIR_2, TEST_BUFFER_SIZE), TEST_PAIR_2.length);
+ mOutput.close();
+
+ byte[] actualDigest = mOutput.getDigest();
+
+ MessageDigest digest = MessageDigest.getInstance(DecryptedChunkKvOutput.DIGEST_ALGORITHM);
+ Stream.of(TEST_PAIR_1, TEST_PAIR_2)
+ .map(DecryptedChunkKvOutputTest::fakeHash)
+ .sorted(Comparator.naturalOrder())
+ .forEachOrdered(hash -> digest.update(hash.getHash()));
+ assertThat(actualDigest).isEqualTo(digest.digest());
+ }
+
+ @Test
+ public void getPairs_beforeClose_throws() throws Exception {
+ // TODO: b/141356823 We should add a test which calls .open() here
+ assertThrows(IllegalStateException.class, () -> mOutput.getPairs());
+ }
+
+ @Test
+ public void getPairs_returnsPairsSortedByKey() throws Exception {
+ mOutput.open();
+ // Write out of order to check that it sorts the chunks.
+ mOutput.processChunk(Arrays.copyOf(TEST_PAIR_2, TEST_BUFFER_SIZE), TEST_PAIR_2.length);
+ mOutput.processChunk(Arrays.copyOf(TEST_PAIR_1, TEST_BUFFER_SIZE), TEST_PAIR_1.length);
+ mOutput.close();
+
+ List<KeyValuePairProto.KeyValuePair> pairs = mOutput.getPairs();
+
+ assertThat(
+ isInOrder(
+ pairs,
+ Comparator.comparing(
+ (KeyValuePairProto.KeyValuePair pair) -> pair.key)))
+ .isTrue();
+ assertThat(pairs).hasSize(2);
+ assertThat(pairs.get(0).key).isEqualTo(TEST_KEY_1);
+ assertThat(pairs.get(0).value).isEqualTo(TEST_VALUE_1);
+ assertThat(pairs.get(1).key).isEqualTo(TEST_KEY_2);
+ assertThat(pairs.get(1).value).isEqualTo(TEST_VALUE_2);
+ }
+
+ private static KeyValuePairProto.KeyValuePair createPair(String key, byte[] value) {
+ KeyValuePairProto.KeyValuePair pair = new KeyValuePairProto.KeyValuePair();
+ pair.key = key;
+ pair.value = value;
+ return pair;
+ }
+
+ private boolean isInOrder(
+ List<KeyValuePairProto.KeyValuePair> list,
+ Comparator<KeyValuePairProto.KeyValuePair> comparator) {
+ if (list.size() < 2) {
+ return true;
+ }
+
+ List<KeyValuePairProto.KeyValuePair> sortedList = new ArrayList<>(list);
+ Collections.sort(sortedList, comparator);
+ return list.equals(sortedList);
+ }
+
+ private static byte[] toByteArray(KeyValuePairProto.KeyValuePair nano) {
+ return KeyValuePairProto.KeyValuePair.toByteArray(nano);
+ }
+
+ private static ChunkHash fakeHash(byte[] data) {
+ return new ChunkHash(Arrays.copyOf(data, ChunkHash.HASH_LENGTH_BYTES));
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/KeyValueListingBuilderTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/KeyValueListingBuilderTest.java
new file mode 100644
index 000000000000..acc662860528
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/KeyValueListingBuilderTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.kv;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.protos.nano.KeyValueListingProto;
+
+import com.google.common.collect.ImmutableMap;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.Arrays;
+
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class KeyValueListingBuilderTest {
+ private static final String TEST_KEY_1 = "test_key_1";
+ private static final String TEST_KEY_2 = "test_key_2";
+ private static final ChunkHash TEST_HASH_1 =
+ new ChunkHash(Arrays.copyOf(new byte[] {1, 2}, ChunkHash.HASH_LENGTH_BYTES));
+ private static final ChunkHash TEST_HASH_2 =
+ new ChunkHash(Arrays.copyOf(new byte[] {5, 6}, ChunkHash.HASH_LENGTH_BYTES));
+
+ private KeyValueListingBuilder mBuilder;
+
+ @Before
+ public void setUp() {
+ mBuilder = new KeyValueListingBuilder();
+ }
+
+ @Test
+ public void addPair_nullKey_throws() {
+ assertThrows(NullPointerException.class, () -> mBuilder.addPair(null, TEST_HASH_1));
+ }
+
+ @Test
+ public void addPair_emptyKey_throws() {
+ assertThrows(IllegalArgumentException.class, () -> mBuilder.addPair("", TEST_HASH_1));
+ }
+
+ @Test
+ public void addPair_nullHash_throws() {
+ assertThrows(NullPointerException.class, () -> mBuilder.addPair(TEST_KEY_1, null));
+ }
+
+ @Test
+ public void build_noPairs_buildsEmptyListing() {
+ KeyValueListingProto.KeyValueListing listing = mBuilder.build();
+
+ assertThat(listing.entries).isEmpty();
+ }
+
+ @Test
+ public void build_returnsCorrectListing() {
+ mBuilder.addPair(TEST_KEY_1, TEST_HASH_1);
+
+ KeyValueListingProto.KeyValueListing listing = mBuilder.build();
+
+ assertThat(listing.entries.length).isEqualTo(1);
+ assertThat(listing.entries[0].key).isEqualTo(TEST_KEY_1);
+ assertThat(listing.entries[0].hash).isEqualTo(TEST_HASH_1.getHash());
+ }
+
+ @Test
+ public void addAll_addsAllPairsInMap() {
+ ImmutableMap<String, ChunkHash> pairs =
+ new ImmutableMap.Builder<String, ChunkHash>()
+ .put(TEST_KEY_1, TEST_HASH_1)
+ .put(TEST_KEY_2, TEST_HASH_2)
+ .build();
+
+ mBuilder.addAll(pairs);
+ KeyValueListingProto.KeyValueListing listing = mBuilder.build();
+
+ assertThat(listing.entries.length).isEqualTo(2);
+ assertThat(listing.entries[0].key).isEqualTo(TEST_KEY_1);
+ assertThat(listing.entries[0].hash).isEqualTo(TEST_HASH_1.getHash());
+ assertThat(listing.entries[1].key).isEqualTo(TEST_KEY_2);
+ assertThat(listing.entries[1].hash).isEqualTo(TEST_HASH_2.getHash());
+ }
+
+ @Test
+ public void emptyListing_returnsListingWithoutAnyPairs() {
+ KeyValueListingProto.KeyValueListing emptyListing = KeyValueListingBuilder.emptyListing();
+ assertThat(emptyListing.entries).isEmpty();
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/BackupFileDecryptorTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/BackupFileDecryptorTaskTest.java
new file mode 100644
index 000000000000..07a6fd2d5b60
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/BackupFileDecryptorTaskTest.java
@@ -0,0 +1,583 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.android.server.backup.testing.CryptoTestUtils.generateAesKey;
+import static com.android.server.backup.testing.CryptoTestUtils.newChunkOrdering;
+import static com.android.server.backup.testing.CryptoTestUtils.newChunksMetadata;
+import static com.android.server.backup.testing.CryptoTestUtils.newPair;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+import static org.testng.Assert.expectThrows;
+
+import android.annotation.Nullable;
+import android.app.backup.BackupDataInput;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.chunking.ChunkHasher;
+import com.android.server.backup.encryption.chunking.DecryptedChunkFileOutput;
+import com.android.server.backup.encryption.chunking.EncryptedChunk;
+import com.android.server.backup.encryption.chunking.cdc.FingerprintMixer;
+import com.android.server.backup.encryption.kv.DecryptedChunkKvOutput;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunkOrdering;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunksMetadata;
+import com.android.server.backup.encryption.protos.nano.KeyValuePairProto.KeyValuePair;
+import com.android.server.backup.encryption.tasks.BackupEncrypter.Result;
+import com.android.server.backup.testing.CryptoTestUtils;
+import com.android.server.testing.shadows.ShadowBackupDataInput;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.protobuf.nano.MessageNano;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.RandomAccessFile;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+import javax.crypto.AEADBadTagException;
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+
+@Config(shadows = {ShadowBackupDataInput.class})
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class BackupFileDecryptorTaskTest {
+ private static final String READ_WRITE_MODE = "rw";
+ private static final int BYTES_PER_KILOBYTE = 1024;
+ private static final int MIN_CHUNK_SIZE_BYTES = 2 * BYTES_PER_KILOBYTE;
+ private static final int AVERAGE_CHUNK_SIZE_BYTES = 4 * BYTES_PER_KILOBYTE;
+ private static final int MAX_CHUNK_SIZE_BYTES = 64 * BYTES_PER_KILOBYTE;
+ private static final int BACKUP_DATA_SIZE_BYTES = 60 * BYTES_PER_KILOBYTE;
+ private static final int GCM_NONCE_LENGTH_BYTES = 12;
+ private static final int GCM_TAG_LENGTH_BYTES = 16;
+ private static final int BITS_PER_BYTE = 8;
+ private static final int CHECKSUM_LENGTH_BYTES = 256 / BITS_PER_BYTE;
+ @Nullable private static final FileDescriptor NULL_FILE_DESCRIPTOR = null;
+
+ private static final Set<KeyValuePair> TEST_KV_DATA = new HashSet<>();
+
+ static {
+ TEST_KV_DATA.add(newPair("key1", "value1"));
+ TEST_KV_DATA.add(newPair("key2", "value2"));
+ }
+
+ @Rule public final TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+ private SecretKey mTertiaryKey;
+ private SecretKey mChunkEncryptionKey;
+ private File mInputFile;
+ private File mOutputFile;
+ private DecryptedChunkOutput mFileOutput;
+ private DecryptedChunkKvOutput mKvOutput;
+ private Random mRandom;
+ private BackupFileDecryptorTask mTask;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mRandom = new Random();
+ mTertiaryKey = generateAesKey();
+ // In good situations it's always the same. We allow changing it for testing when somehow it
+ // has become mismatched that we throw an error.
+ mChunkEncryptionKey = mTertiaryKey;
+ mInputFile = mTemporaryFolder.newFile();
+ mOutputFile = mTemporaryFolder.newFile();
+ mFileOutput = new DecryptedChunkFileOutput(mOutputFile);
+ mKvOutput = new DecryptedChunkKvOutput(new ChunkHasher(mTertiaryKey));
+ mTask = new BackupFileDecryptorTask(mTertiaryKey);
+ }
+
+ @Test
+ public void decryptFile_throwsForNonExistentInput() throws Exception {
+ assertThrows(
+ FileNotFoundException.class,
+ () ->
+ mTask.decryptFile(
+ new File(mTemporaryFolder.newFolder(), "nonexistent"),
+ mFileOutput));
+ }
+
+ @Test
+ public void decryptFile_throwsForDirectoryInputFile() throws Exception {
+ assertThrows(
+ FileNotFoundException.class,
+ () -> mTask.decryptFile(mTemporaryFolder.newFolder(), mFileOutput));
+ }
+
+ @Test
+ public void decryptFile_withExplicitStarts_decryptsEncryptedData() throws Exception {
+ byte[] backupData = randomData(BACKUP_DATA_SIZE_BYTES);
+ createEncryptedFileUsingExplicitStarts(backupData);
+
+ mTask.decryptFile(mInputFile, mFileOutput);
+
+ assertThat(Files.readAllBytes(Paths.get(mOutputFile.toURI()))).isEqualTo(backupData);
+ }
+
+ @Test
+ public void decryptFile_withInlineLengths_decryptsEncryptedData() throws Exception {
+ createEncryptedFileUsingInlineLengths(
+ TEST_KV_DATA, chunkOrdering -> chunkOrdering, chunksMetadata -> chunksMetadata);
+ mTask.decryptFile(mInputFile, mKvOutput);
+ assertThat(asMap(mKvOutput.getPairs())).containsExactlyEntriesIn(asMap(TEST_KV_DATA));
+ }
+
+ @Test
+ public void decryptFile_withNoChunkOrderingType_decryptsUsingExplicitStarts() throws Exception {
+ byte[] backupData = randomData(BACKUP_DATA_SIZE_BYTES);
+ createEncryptedFileUsingExplicitStarts(
+ backupData,
+ chunkOrdering -> chunkOrdering,
+ chunksMetadata -> {
+ ChunksMetadata metadata = CryptoTestUtils.clone(chunksMetadata);
+ metadata.chunkOrderingType =
+ ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED;
+ return metadata;
+ });
+
+ mTask.decryptFile(mInputFile, mFileOutput);
+
+ assertThat(Files.readAllBytes(Paths.get(mOutputFile.toURI()))).isEqualTo(backupData);
+ }
+
+ @Test
+ public void decryptFile_withInlineLengths_throwsForZeroLengths() throws Exception {
+ createEncryptedFileUsingInlineLengths(
+ TEST_KV_DATA, chunkOrdering -> chunkOrdering, chunksMetadata -> chunksMetadata);
+
+ // Set the length of the first chunk to zero.
+ RandomAccessFile raf = new RandomAccessFile(mInputFile, READ_WRITE_MODE);
+ raf.seek(0);
+ raf.writeInt(0);
+
+ assertThrows(
+ MalformedEncryptedFileException.class,
+ () -> mTask.decryptFile(mInputFile, mKvOutput));
+ }
+
+ @Test
+ public void decryptFile_withInlineLengths_throwsForLongLengths() throws Exception {
+ createEncryptedFileUsingInlineLengths(
+ TEST_KV_DATA, chunkOrdering -> chunkOrdering, chunksMetadata -> chunksMetadata);
+
+ // Set the length of the first chunk to zero.
+ RandomAccessFile raf = new RandomAccessFile(mInputFile, READ_WRITE_MODE);
+ raf.seek(0);
+ raf.writeInt((int) mInputFile.length());
+
+ assertThrows(
+ MalformedEncryptedFileException.class,
+ () -> mTask.decryptFile(mInputFile, mKvOutput));
+ }
+
+ @Test
+ public void decryptFile_throwsForBadKey() throws Exception {
+ createEncryptedFileUsingExplicitStarts(randomData(BACKUP_DATA_SIZE_BYTES));
+
+ assertThrows(
+ AEADBadTagException.class,
+ () ->
+ new BackupFileDecryptorTask(generateAesKey())
+ .decryptFile(mInputFile, mFileOutput));
+ }
+
+ @Test
+ public void decryptFile_withExplicitStarts_throwsForMangledOrdering() throws Exception {
+ createEncryptedFileUsingExplicitStarts(
+ randomData(BACKUP_DATA_SIZE_BYTES),
+ chunkOrdering -> {
+ ChunkOrdering ordering = CryptoTestUtils.clone(chunkOrdering);
+ Arrays.sort(ordering.starts);
+ return ordering;
+ });
+
+ assertThrows(
+ MessageDigestMismatchException.class,
+ () -> mTask.decryptFile(mInputFile, mFileOutput));
+ }
+
+ @Test
+ public void decryptFile_withExplicitStarts_noChunks_returnsNoData() throws Exception {
+ byte[] backupData = randomData(/*length=*/ 0);
+ createEncryptedFileUsingExplicitStarts(
+ backupData,
+ chunkOrdering -> {
+ ChunkOrdering ordering = CryptoTestUtils.clone(chunkOrdering);
+ ordering.starts = new int[0];
+ return ordering;
+ });
+
+ mTask.decryptFile(mInputFile, mFileOutput);
+
+ assertThat(Files.readAllBytes(Paths.get(mOutputFile.toURI()))).isEqualTo(backupData);
+ }
+
+ @Test
+ public void decryptFile_throwsForMismatchedChecksum() throws Exception {
+ createEncryptedFileUsingExplicitStarts(
+ randomData(BACKUP_DATA_SIZE_BYTES),
+ chunkOrdering -> {
+ ChunkOrdering ordering = CryptoTestUtils.clone(chunkOrdering);
+ ordering.checksum =
+ Arrays.copyOf(randomData(CHECKSUM_LENGTH_BYTES), CHECKSUM_LENGTH_BYTES);
+ return ordering;
+ });
+
+ assertThrows(
+ MessageDigestMismatchException.class,
+ () -> mTask.decryptFile(mInputFile, mFileOutput));
+ }
+
+ @Test
+ public void decryptFile_throwsForBadChunksMetadataOffset() throws Exception {
+ createEncryptedFileUsingExplicitStarts(randomData(BACKUP_DATA_SIZE_BYTES));
+
+ // Replace the metadata with all 1s.
+ RandomAccessFile raf = new RandomAccessFile(mInputFile, READ_WRITE_MODE);
+ raf.seek(raf.length() - Long.BYTES);
+ int metadataOffset = (int) raf.readLong();
+ int metadataLength = (int) raf.length() - metadataOffset - Long.BYTES;
+
+ byte[] allOnes = new byte[metadataLength];
+ Arrays.fill(allOnes, (byte) 1);
+
+ raf.seek(metadataOffset);
+ raf.write(allOnes, /*off=*/ 0, metadataLength);
+
+ MalformedEncryptedFileException thrown =
+ expectThrows(
+ MalformedEncryptedFileException.class,
+ () -> mTask.decryptFile(mInputFile, mFileOutput));
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo(
+ "Could not read chunks metadata at position "
+ + metadataOffset
+ + " of file of "
+ + raf.length()
+ + " bytes");
+ }
+
+ @Test
+ public void decryptFile_throwsForChunksMetadataOffsetBeyondEndOfFile() throws Exception {
+ createEncryptedFileUsingExplicitStarts(randomData(BACKUP_DATA_SIZE_BYTES));
+
+ RandomAccessFile raf = new RandomAccessFile(mInputFile, READ_WRITE_MODE);
+ raf.seek(raf.length() - Long.BYTES);
+ raf.writeLong(raf.length());
+
+ MalformedEncryptedFileException thrown =
+ expectThrows(
+ MalformedEncryptedFileException.class,
+ () -> mTask.decryptFile(mInputFile, mFileOutput));
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo(
+ raf.length()
+ + " is not valid position for chunks metadata in file of "
+ + raf.length()
+ + " bytes");
+ }
+
+ @Test
+ public void decryptFile_throwsForChunksMetadataOffsetBeforeBeginningOfFile() throws Exception {
+ createEncryptedFileUsingExplicitStarts(randomData(BACKUP_DATA_SIZE_BYTES));
+
+ RandomAccessFile raf = new RandomAccessFile(mInputFile, READ_WRITE_MODE);
+ raf.seek(raf.length() - Long.BYTES);
+ raf.writeLong(-1);
+
+ MalformedEncryptedFileException thrown =
+ expectThrows(
+ MalformedEncryptedFileException.class,
+ () -> mTask.decryptFile(mInputFile, mFileOutput));
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo(
+ "-1 is not valid position for chunks metadata in file of "
+ + raf.length()
+ + " bytes");
+ }
+
+ @Test
+ public void decryptFile_throwsForMangledChunks() throws Exception {
+ createEncryptedFileUsingExplicitStarts(randomData(BACKUP_DATA_SIZE_BYTES));
+
+ // Mess up some bits in a random byte
+ RandomAccessFile raf = new RandomAccessFile(mInputFile, READ_WRITE_MODE);
+ raf.seek(50);
+ byte fiftiethByte = raf.readByte();
+ raf.seek(50);
+ raf.write(~fiftiethByte);
+
+ assertThrows(AEADBadTagException.class, () -> mTask.decryptFile(mInputFile, mFileOutput));
+ }
+
+ @Test
+ public void decryptFile_throwsForBadChunkEncryptionKey() throws Exception {
+ mChunkEncryptionKey = generateAesKey();
+
+ createEncryptedFileUsingExplicitStarts(randomData(BACKUP_DATA_SIZE_BYTES));
+
+ assertThrows(AEADBadTagException.class, () -> mTask.decryptFile(mInputFile, mFileOutput));
+ }
+
+ @Test
+ public void decryptFile_throwsForUnsupportedCipherType() throws Exception {
+ createEncryptedFileUsingExplicitStarts(
+ randomData(BACKUP_DATA_SIZE_BYTES),
+ chunkOrdering -> chunkOrdering,
+ chunksMetadata -> {
+ ChunksMetadata metadata = CryptoTestUtils.clone(chunksMetadata);
+ metadata.cipherType = ChunksMetadataProto.UNKNOWN_CIPHER_TYPE;
+ return metadata;
+ });
+
+ assertThrows(
+ UnsupportedEncryptedFileException.class,
+ () -> mTask.decryptFile(mInputFile, mFileOutput));
+ }
+
+ @Test
+ public void decryptFile_throwsForUnsupportedMessageDigestType() throws Exception {
+ createEncryptedFileUsingExplicitStarts(
+ randomData(BACKUP_DATA_SIZE_BYTES),
+ chunkOrdering -> chunkOrdering,
+ chunksMetadata -> {
+ ChunksMetadata metadata = CryptoTestUtils.clone(chunksMetadata);
+ metadata.checksumType = ChunksMetadataProto.UNKNOWN_CHECKSUM_TYPE;
+ return metadata;
+ });
+
+ assertThrows(
+ UnsupportedEncryptedFileException.class,
+ () -> mTask.decryptFile(mInputFile, mFileOutput));
+ }
+
+ /**
+ * Creates an encrypted backup file from the given data.
+ *
+ * @param data The plaintext content.
+ */
+ private void createEncryptedFileUsingExplicitStarts(byte[] data) throws Exception {
+ createEncryptedFileUsingExplicitStarts(data, chunkOrdering -> chunkOrdering);
+ }
+
+ /**
+ * Creates an encrypted backup file from the given data.
+ *
+ * @param data The plaintext content.
+ * @param chunkOrderingTransformer Transforms the ordering before it's encrypted.
+ */
+ private void createEncryptedFileUsingExplicitStarts(
+ byte[] data, Transformer<ChunkOrdering> chunkOrderingTransformer) throws Exception {
+ createEncryptedFileUsingExplicitStarts(
+ data, chunkOrderingTransformer, chunksMetadata -> chunksMetadata);
+ }
+
+ /**
+ * Creates an encrypted backup file from the given data in mode {@link
+ * ChunksMetadataProto#EXPLICIT_STARTS}.
+ *
+ * @param data The plaintext content.
+ * @param chunkOrderingTransformer Transforms the ordering before it's encrypted.
+ * @param chunksMetadataTransformer Transforms the metadata before it's written.
+ */
+ private void createEncryptedFileUsingExplicitStarts(
+ byte[] data,
+ Transformer<ChunkOrdering> chunkOrderingTransformer,
+ Transformer<ChunksMetadata> chunksMetadataTransformer)
+ throws Exception {
+ Result result = backupFullData(data);
+
+ ArrayList<EncryptedChunk> chunks = new ArrayList<>(result.getNewChunks());
+ Collections.shuffle(chunks);
+ HashMap<ChunkHash, Integer> startPositions = new HashMap<>();
+
+ try (FileOutputStream fos = new FileOutputStream(mInputFile);
+ DataOutputStream dos = new DataOutputStream(fos)) {
+ int position = 0;
+
+ for (EncryptedChunk chunk : chunks) {
+ startPositions.put(chunk.key(), position);
+ dos.write(chunk.nonce());
+ dos.write(chunk.encryptedBytes());
+ position += chunk.nonce().length + chunk.encryptedBytes().length;
+ }
+
+ int[] starts = new int[chunks.size()];
+ List<ChunkHash> chunkListing = result.getAllChunks();
+
+ for (int i = 0; i < chunks.size(); i++) {
+ starts[i] = startPositions.get(chunkListing.get(i));
+ }
+
+ ChunkOrdering chunkOrdering = newChunkOrdering(starts, result.getDigest());
+ chunkOrdering = chunkOrderingTransformer.accept(chunkOrdering);
+
+ ChunksMetadata metadata =
+ newChunksMetadata(
+ ChunksMetadataProto.AES_256_GCM,
+ ChunksMetadataProto.SHA_256,
+ ChunksMetadataProto.EXPLICIT_STARTS,
+ encrypt(chunkOrdering));
+ metadata = chunksMetadataTransformer.accept(metadata);
+
+ dos.write(MessageNano.toByteArray(metadata));
+ dos.writeLong(position);
+ }
+ }
+
+ /**
+ * Creates an encrypted backup file from the given data in mode {@link
+ * ChunksMetadataProto#INLINE_LENGTHS}.
+ *
+ * @param data The plaintext key value pairs to back up.
+ * @param chunkOrderingTransformer Transforms the ordering before it's encrypted.
+ * @param chunksMetadataTransformer Transforms the metadata before it's written.
+ */
+ private void createEncryptedFileUsingInlineLengths(
+ Set<KeyValuePair> data,
+ Transformer<ChunkOrdering> chunkOrderingTransformer,
+ Transformer<ChunksMetadata> chunksMetadataTransformer)
+ throws Exception {
+ Result result = backupKvData(data);
+
+ List<EncryptedChunk> chunks = new ArrayList<>(result.getNewChunks());
+ System.out.println("we have chunk count " + chunks.size());
+ Collections.shuffle(chunks);
+
+ try (FileOutputStream fos = new FileOutputStream(mInputFile);
+ DataOutputStream dos = new DataOutputStream(fos)) {
+ for (EncryptedChunk chunk : chunks) {
+ dos.writeInt(chunk.nonce().length + chunk.encryptedBytes().length);
+ dos.write(chunk.nonce());
+ dos.write(chunk.encryptedBytes());
+ }
+
+ ChunkOrdering chunkOrdering = newChunkOrdering(null, result.getDigest());
+ chunkOrdering = chunkOrderingTransformer.accept(chunkOrdering);
+
+ ChunksMetadata metadata =
+ newChunksMetadata(
+ ChunksMetadataProto.AES_256_GCM,
+ ChunksMetadataProto.SHA_256,
+ ChunksMetadataProto.INLINE_LENGTHS,
+ encrypt(chunkOrdering));
+ metadata = chunksMetadataTransformer.accept(metadata);
+
+ int metadataStart = dos.size();
+ dos.write(MessageNano.toByteArray(metadata));
+ dos.writeLong(metadataStart);
+ }
+ }
+
+ /** Performs a full backup of the given data, and returns the chunks. */
+ private BackupEncrypter.Result backupFullData(byte[] data) throws Exception {
+ BackupStreamEncrypter encrypter =
+ new BackupStreamEncrypter(
+ new ByteArrayInputStream(data),
+ MIN_CHUNK_SIZE_BYTES,
+ MAX_CHUNK_SIZE_BYTES,
+ AVERAGE_CHUNK_SIZE_BYTES);
+ return encrypter.backup(
+ mChunkEncryptionKey,
+ randomData(FingerprintMixer.SALT_LENGTH_BYTES),
+ new HashSet<>());
+ }
+
+ private Result backupKvData(Set<KeyValuePair> data) throws Exception {
+ ShadowBackupDataInput.reset();
+ for (KeyValuePair pair : data) {
+ ShadowBackupDataInput.addEntity(pair.key, pair.value);
+ }
+ KvBackupEncrypter encrypter =
+ new KvBackupEncrypter(new BackupDataInput(NULL_FILE_DESCRIPTOR));
+ return encrypter.backup(
+ mChunkEncryptionKey,
+ randomData(FingerprintMixer.SALT_LENGTH_BYTES),
+ Collections.EMPTY_SET);
+ }
+
+ /** Encrypts {@code chunkOrdering} using {@link #mTertiaryKey}. */
+ private byte[] encrypt(ChunkOrdering chunkOrdering) throws Exception {
+ Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
+ byte[] nonce = randomData(GCM_NONCE_LENGTH_BYTES);
+ cipher.init(
+ Cipher.ENCRYPT_MODE,
+ mTertiaryKey,
+ new GCMParameterSpec(GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE, nonce));
+ byte[] nanoBytes = MessageNano.toByteArray(chunkOrdering);
+ byte[] encryptedBytes = cipher.doFinal(nanoBytes);
+
+ try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ out.write(nonce);
+ out.write(encryptedBytes);
+ return out.toByteArray();
+ }
+ }
+
+ /** Returns {@code length} random bytes. */
+ private byte[] randomData(int length) {
+ byte[] data = new byte[length];
+ mRandom.nextBytes(data);
+ return data;
+ }
+
+ private static ImmutableMap<String, String> asMap(Collection<KeyValuePair> pairs) {
+ ImmutableMap.Builder<String, String> map = ImmutableMap.builder();
+ for (KeyValuePair pair : pairs) {
+ map.put(pair.key, new String(pair.value, Charset.forName("UTF-8")));
+ }
+ return map.build();
+ }
+
+ private interface Transformer<T> {
+ T accept(T t);
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/ClearCryptoStateTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/ClearCryptoStateTaskTest.java
new file mode 100644
index 000000000000..81bfce1da294
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/ClearCryptoStateTaskTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.server.backup.encryption.CryptoSettings;
+import com.android.server.backup.encryption.chunking.ProtoStore;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunkListing;
+import com.android.server.backup.encryption.protos.nano.KeyValueListingProto.KeyValueListing;
+import com.android.server.backup.encryption.storage.BackupEncryptionDb;
+import com.android.server.backup.encryption.storage.TertiaryKey;
+import com.android.server.backup.encryption.storage.TertiaryKeysTable;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class ClearCryptoStateTaskTest {
+ private static final String TEST_PACKAGE_NAME = "com.android.example";
+
+ private ClearCryptoStateTask mClearCryptoStateTask;
+ private CryptoSettings mCryptoSettings;
+ private Context mApplication;
+
+ @Before
+ public void setUp() {
+ mApplication = ApplicationProvider.getApplicationContext();
+ mCryptoSettings = spy(CryptoSettings.getInstanceForTesting(mApplication));
+ mClearCryptoStateTask = new ClearCryptoStateTask(mApplication, mCryptoSettings);
+ }
+
+ @Test
+ public void run_clearsChunkListingProtoState() throws Exception {
+ String packageName = TEST_PACKAGE_NAME;
+ ChunkListing chunkListing = new ChunkListing();
+ ProtoStore.createChunkListingStore(mApplication).saveProto(packageName, chunkListing);
+
+ mClearCryptoStateTask.run();
+
+ assertThat(
+ ProtoStore.createChunkListingStore(mApplication)
+ .loadProto(packageName)
+ .isPresent())
+ .isFalse();
+ }
+
+ @Test
+ public void run_clearsKeyValueProtoState() throws Exception {
+ String packageName = TEST_PACKAGE_NAME;
+ KeyValueListing keyValueListing = new KeyValueListing();
+ ProtoStore.createKeyValueListingStore(mApplication).saveProto(packageName, keyValueListing);
+
+ mClearCryptoStateTask.run();
+
+ assertThat(
+ ProtoStore.createKeyValueListingStore(mApplication)
+ .loadProto(packageName)
+ .isPresent())
+ .isFalse();
+ }
+
+ @Test
+ public void run_clearsTertiaryKeysTable() throws Exception {
+ String secondaryKeyAlias = "bob";
+ TertiaryKeysTable tertiaryKeysTable =
+ BackupEncryptionDb.newInstance(mApplication).getTertiaryKeysTable();
+ tertiaryKeysTable.addKey(
+ new TertiaryKey(
+ secondaryKeyAlias, "packageName", /*wrappedKeyBytes=*/ new byte[0]));
+
+ mClearCryptoStateTask.run();
+
+ assertThat(tertiaryKeysTable.getAllKeys(secondaryKeyAlias)).isEmpty();
+ }
+
+ @Test
+ public void run_clearsSettings() {
+ mCryptoSettings.setSecondaryLastRotated(100001);
+
+ mClearCryptoStateTask.run();
+
+ assertThat(mCryptoSettings.getSecondaryLastRotated().isPresent()).isFalse();
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedBackupTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedBackupTaskTest.java
new file mode 100644
index 000000000000..f6914efd6d83
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedBackupTaskTest.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.AES_256_GCM;
+import static com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED;
+import static com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.SHA_256;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.chunking.BackupFileBuilder;
+import com.android.server.backup.encryption.chunking.EncryptedChunk;
+import com.android.server.backup.encryption.chunking.EncryptedChunkEncoder;
+import com.android.server.backup.encryption.chunking.LengthlessEncryptedChunkEncoder;
+import com.android.server.backup.encryption.client.CryptoBackupServer;
+import com.android.server.backup.encryption.keys.TertiaryKeyGenerator;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunkListing;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunkOrdering;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunksMetadata;
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto.WrappedKey;
+import com.android.server.backup.encryption.tasks.BackupEncrypter.Result;
+import com.android.server.backup.testing.CryptoTestUtils;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.protobuf.nano.MessageNano;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+import java.io.OutputStream;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.concurrent.CancellationException;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+
+@Config(shadows = {EncryptedBackupTaskTest.ShadowBackupFileBuilder.class})
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class EncryptedBackupTaskTest {
+
+ private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
+ private static final int GCM_NONCE_LENGTH_BYTES = 12;
+ private static final int GCM_TAG_LENGTH_BYTES = 16;
+ private static final int BITS_PER_BYTE = 8;
+
+ private static final byte[] TEST_FINGERPRINT_MIXER_SALT =
+ Arrays.copyOf(new byte[] {22}, ChunkHash.HASH_LENGTH_BYTES);
+
+ private static final byte[] TEST_NONCE =
+ Arrays.copyOf(new byte[] {55}, EncryptedChunk.NONCE_LENGTH_BYTES);
+
+ private static final ChunkHash TEST_HASH_1 =
+ new ChunkHash(Arrays.copyOf(new byte[] {1}, ChunkHash.HASH_LENGTH_BYTES));
+ private static final ChunkHash TEST_HASH_2 =
+ new ChunkHash(Arrays.copyOf(new byte[] {2}, ChunkHash.HASH_LENGTH_BYTES));
+ private static final ChunkHash TEST_HASH_3 =
+ new ChunkHash(Arrays.copyOf(new byte[] {3}, ChunkHash.HASH_LENGTH_BYTES));
+
+ private static final EncryptedChunk TEST_CHUNK_1 =
+ EncryptedChunk.create(TEST_HASH_1, TEST_NONCE, new byte[] {1, 2, 3, 4, 5});
+ private static final EncryptedChunk TEST_CHUNK_2 =
+ EncryptedChunk.create(TEST_HASH_2, TEST_NONCE, new byte[] {6, 7, 8, 9, 10});
+ private static final EncryptedChunk TEST_CHUNK_3 =
+ EncryptedChunk.create(TEST_HASH_3, TEST_NONCE, new byte[] {11, 12, 13, 14, 15});
+
+ private static final byte[] TEST_CHECKSUM = Arrays.copyOf(new byte[] {10}, 258 / 8);
+ private static final String TEST_PACKAGE_NAME = "com.example.package";
+ private static final String TEST_OLD_DOCUMENT_ID = "old_doc_1";
+ private static final String TEST_NEW_DOCUMENT_ID = "new_doc_1";
+
+ @Captor private ArgumentCaptor<ChunksMetadata> mMetadataCaptor;
+
+ @Mock private CryptoBackupServer mCryptoBackupServer;
+ @Mock private BackupEncrypter mBackupEncrypter;
+ @Mock private BackupFileBuilder mBackupFileBuilder;
+
+ private ChunkListing mOldChunkListing;
+ private SecretKey mTertiaryKey;
+ private WrappedKey mWrappedTertiaryKey;
+ private EncryptedChunkEncoder mEncryptedChunkEncoder;
+ private EncryptedBackupTask mTask;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ SecureRandom secureRandom = new SecureRandom();
+ mTertiaryKey = new TertiaryKeyGenerator(secureRandom).generate();
+ mWrappedTertiaryKey = new WrappedKey();
+
+ mEncryptedChunkEncoder = new LengthlessEncryptedChunkEncoder();
+
+ ShadowBackupFileBuilder.sInstance = mBackupFileBuilder;
+
+ mTask =
+ new EncryptedBackupTask(
+ mCryptoBackupServer, secureRandom, TEST_PACKAGE_NAME, mBackupEncrypter);
+ }
+
+ @Test
+ public void performNonIncrementalBackup_performsBackup() throws Exception {
+ setUpWithoutExistingBackup();
+
+ // Chunk listing and ordering don't matter for this test.
+ when(mBackupFileBuilder.getNewChunkListing(any())).thenReturn(new ChunkListing());
+ when(mBackupFileBuilder.getNewChunkOrdering(TEST_CHECKSUM)).thenReturn(new ChunkOrdering());
+
+ when(mCryptoBackupServer.uploadNonIncrementalBackup(eq(TEST_PACKAGE_NAME), any(), any()))
+ .thenReturn(TEST_NEW_DOCUMENT_ID);
+
+ mTask.performNonIncrementalBackup(
+ mTertiaryKey, mWrappedTertiaryKey, TEST_FINGERPRINT_MIXER_SALT);
+
+ verify(mBackupFileBuilder)
+ .writeChunks(
+ ImmutableList.of(TEST_HASH_1, TEST_HASH_2),
+ ImmutableMap.of(TEST_HASH_1, TEST_CHUNK_1, TEST_HASH_2, TEST_CHUNK_2));
+ verify(mBackupFileBuilder).finish(any());
+ verify(mCryptoBackupServer)
+ .uploadNonIncrementalBackup(eq(TEST_PACKAGE_NAME), any(), eq(mWrappedTertiaryKey));
+ }
+
+ @Test
+ public void performIncrementalBackup_performsBackup() throws Exception {
+ setUpWithExistingBackup();
+
+ // Chunk listing and ordering don't matter for this test.
+ when(mBackupFileBuilder.getNewChunkListing(any())).thenReturn(new ChunkListing());
+ when(mBackupFileBuilder.getNewChunkOrdering(TEST_CHECKSUM)).thenReturn(new ChunkOrdering());
+
+ when(mCryptoBackupServer.uploadIncrementalBackup(
+ eq(TEST_PACKAGE_NAME), eq(TEST_OLD_DOCUMENT_ID), any(), any()))
+ .thenReturn(TEST_NEW_DOCUMENT_ID);
+
+ mTask.performIncrementalBackup(mTertiaryKey, mWrappedTertiaryKey, mOldChunkListing);
+
+ verify(mBackupFileBuilder)
+ .writeChunks(
+ ImmutableList.of(TEST_HASH_1, TEST_HASH_2, TEST_HASH_3),
+ ImmutableMap.of(TEST_HASH_2, TEST_CHUNK_2));
+ verify(mBackupFileBuilder).finish(any());
+ verify(mCryptoBackupServer)
+ .uploadIncrementalBackup(
+ eq(TEST_PACKAGE_NAME),
+ eq(TEST_OLD_DOCUMENT_ID),
+ any(),
+ eq(mWrappedTertiaryKey));
+ }
+
+ @Test
+ public void performIncrementalBackup_returnsNewChunkListingWithDocId() throws Exception {
+ setUpWithExistingBackup();
+
+ ChunkListing chunkListingWithoutDocId =
+ CryptoTestUtils.newChunkListingWithoutDocId(
+ TEST_FINGERPRINT_MIXER_SALT,
+ AES_256_GCM,
+ CHUNK_ORDERING_TYPE_UNSPECIFIED,
+ createChunkProtoFor(TEST_HASH_1, TEST_CHUNK_1),
+ createChunkProtoFor(TEST_HASH_2, TEST_CHUNK_2));
+ when(mBackupFileBuilder.getNewChunkListing(any())).thenReturn(chunkListingWithoutDocId);
+
+ // Chunk ordering doesn't matter for this test.
+ when(mBackupFileBuilder.getNewChunkOrdering(TEST_CHECKSUM)).thenReturn(new ChunkOrdering());
+
+ when(mCryptoBackupServer.uploadIncrementalBackup(
+ eq(TEST_PACKAGE_NAME), eq(TEST_OLD_DOCUMENT_ID), any(), any()))
+ .thenReturn(TEST_NEW_DOCUMENT_ID);
+
+ ChunkListing actualChunkListing =
+ mTask.performIncrementalBackup(mTertiaryKey, mWrappedTertiaryKey, mOldChunkListing);
+
+ ChunkListing expectedChunkListing = CryptoTestUtils.clone(chunkListingWithoutDocId);
+ expectedChunkListing.documentId = TEST_NEW_DOCUMENT_ID;
+ assertChunkListingsAreEqual(actualChunkListing, expectedChunkListing);
+ }
+
+ @Test
+ public void performNonIncrementalBackup_returnsNewChunkListingWithDocId() throws Exception {
+ setUpWithoutExistingBackup();
+
+ ChunkListing chunkListingWithoutDocId =
+ CryptoTestUtils.newChunkListingWithoutDocId(
+ TEST_FINGERPRINT_MIXER_SALT,
+ AES_256_GCM,
+ CHUNK_ORDERING_TYPE_UNSPECIFIED,
+ createChunkProtoFor(TEST_HASH_1, TEST_CHUNK_1),
+ createChunkProtoFor(TEST_HASH_2, TEST_CHUNK_2));
+ when(mBackupFileBuilder.getNewChunkListing(any())).thenReturn(chunkListingWithoutDocId);
+
+ // Chunk ordering doesn't matter for this test.
+ when(mBackupFileBuilder.getNewChunkOrdering(TEST_CHECKSUM)).thenReturn(new ChunkOrdering());
+
+ when(mCryptoBackupServer.uploadNonIncrementalBackup(eq(TEST_PACKAGE_NAME), any(), any()))
+ .thenReturn(TEST_NEW_DOCUMENT_ID);
+
+ ChunkListing actualChunkListing =
+ mTask.performNonIncrementalBackup(
+ mTertiaryKey, mWrappedTertiaryKey, TEST_FINGERPRINT_MIXER_SALT);
+
+ ChunkListing expectedChunkListing = CryptoTestUtils.clone(chunkListingWithoutDocId);
+ expectedChunkListing.documentId = TEST_NEW_DOCUMENT_ID;
+ assertChunkListingsAreEqual(actualChunkListing, expectedChunkListing);
+ }
+
+ @Test
+ public void performNonIncrementalBackup_buildsCorrectChunkMetadata() throws Exception {
+ setUpWithoutExistingBackup();
+
+ // Chunk listing doesn't matter for this test.
+ when(mBackupFileBuilder.getNewChunkListing(any())).thenReturn(new ChunkListing());
+
+ ChunkOrdering expectedOrdering =
+ CryptoTestUtils.newChunkOrdering(new int[10], TEST_CHECKSUM);
+ when(mBackupFileBuilder.getNewChunkOrdering(TEST_CHECKSUM)).thenReturn(expectedOrdering);
+
+ when(mCryptoBackupServer.uploadNonIncrementalBackup(eq(TEST_PACKAGE_NAME), any(), any()))
+ .thenReturn(TEST_NEW_DOCUMENT_ID);
+
+ mTask.performNonIncrementalBackup(
+ mTertiaryKey, mWrappedTertiaryKey, TEST_FINGERPRINT_MIXER_SALT);
+
+ verify(mBackupFileBuilder).finish(mMetadataCaptor.capture());
+
+ ChunksMetadata actualMetadata = mMetadataCaptor.getValue();
+ assertThat(actualMetadata.checksumType).isEqualTo(SHA_256);
+ assertThat(actualMetadata.cipherType).isEqualTo(AES_256_GCM);
+
+ ChunkOrdering actualOrdering = decryptChunkOrdering(actualMetadata.chunkOrdering);
+ assertThat(actualOrdering.checksum).isEqualTo(TEST_CHECKSUM);
+ assertThat(actualOrdering.starts).isEqualTo(expectedOrdering.starts);
+ }
+
+ @Test
+ public void cancel_incrementalBackup_doesNotUploadOrSaveChunkListing() throws Exception {
+ setUpWithExistingBackup();
+
+ // Chunk listing and ordering don't matter for this test.
+ when(mBackupFileBuilder.getNewChunkListing(any())).thenReturn(new ChunkListing());
+ when(mBackupFileBuilder.getNewChunkOrdering(TEST_CHECKSUM)).thenReturn(new ChunkOrdering());
+
+ mTask.cancel();
+ assertThrows(
+ CancellationException.class,
+ () ->
+ mTask.performIncrementalBackup(
+ mTertiaryKey, mWrappedTertiaryKey, mOldChunkListing));
+
+ verify(mCryptoBackupServer, never()).uploadIncrementalBackup(any(), any(), any(), any());
+ verify(mCryptoBackupServer, never()).uploadNonIncrementalBackup(any(), any(), any());
+ }
+
+ @Test
+ public void cancel_nonIncrementalBackup_doesNotUploadOrSaveChunkListing() throws Exception {
+ setUpWithoutExistingBackup();
+
+ // Chunk listing and ordering don't matter for this test.
+ when(mBackupFileBuilder.getNewChunkListing(any())).thenReturn(new ChunkListing());
+ when(mBackupFileBuilder.getNewChunkOrdering(TEST_CHECKSUM)).thenReturn(new ChunkOrdering());
+
+ mTask.cancel();
+ assertThrows(
+ CancellationException.class,
+ () ->
+ mTask.performNonIncrementalBackup(
+ mTertiaryKey, mWrappedTertiaryKey, TEST_FINGERPRINT_MIXER_SALT));
+
+ verify(mCryptoBackupServer, never()).uploadIncrementalBackup(any(), any(), any(), any());
+ verify(mCryptoBackupServer, never()).uploadNonIncrementalBackup(any(), any(), any());
+ }
+
+ /** Sets up a backup of [CHUNK 1][CHUNK 2] with no existing data. */
+ private void setUpWithoutExistingBackup() throws Exception {
+ Result result =
+ new Result(
+ ImmutableList.of(TEST_HASH_1, TEST_HASH_2),
+ ImmutableList.of(TEST_CHUNK_1, TEST_CHUNK_2),
+ TEST_CHECKSUM);
+ when(mBackupEncrypter.backup(any(), eq(TEST_FINGERPRINT_MIXER_SALT), eq(ImmutableSet.of())))
+ .thenReturn(result);
+ }
+
+ /**
+ * Sets up a backup of [CHUNK 1][CHUNK 2][CHUNK 3] where the previous backup contained [CHUNK
+ * 1][CHUNK 3].
+ */
+ private void setUpWithExistingBackup() throws Exception {
+ mOldChunkListing =
+ CryptoTestUtils.newChunkListing(
+ TEST_OLD_DOCUMENT_ID,
+ TEST_FINGERPRINT_MIXER_SALT,
+ AES_256_GCM,
+ CHUNK_ORDERING_TYPE_UNSPECIFIED,
+ createChunkProtoFor(TEST_HASH_1, TEST_CHUNK_1),
+ createChunkProtoFor(TEST_HASH_3, TEST_CHUNK_3));
+
+ Result result =
+ new Result(
+ ImmutableList.of(TEST_HASH_1, TEST_HASH_2, TEST_HASH_3),
+ ImmutableList.of(TEST_CHUNK_2),
+ TEST_CHECKSUM);
+ when(mBackupEncrypter.backup(
+ any(),
+ eq(TEST_FINGERPRINT_MIXER_SALT),
+ eq(ImmutableSet.of(TEST_HASH_1, TEST_HASH_3))))
+ .thenReturn(result);
+ }
+
+ private ChunksMetadataProto.Chunk createChunkProtoFor(
+ ChunkHash chunkHash, EncryptedChunk encryptedChunk) {
+ return CryptoTestUtils.newChunk(
+ chunkHash, mEncryptedChunkEncoder.getEncodedLengthOfChunk(encryptedChunk));
+ }
+
+ private ChunkOrdering decryptChunkOrdering(byte[] encryptedOrdering) throws Exception {
+ Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
+ cipher.init(
+ Cipher.DECRYPT_MODE,
+ mTertiaryKey,
+ new GCMParameterSpec(
+ GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE,
+ encryptedOrdering,
+ /*offset=*/ 0,
+ GCM_NONCE_LENGTH_BYTES));
+ byte[] decrypted =
+ cipher.doFinal(
+ encryptedOrdering,
+ GCM_NONCE_LENGTH_BYTES,
+ encryptedOrdering.length - GCM_NONCE_LENGTH_BYTES);
+ return ChunkOrdering.parseFrom(decrypted);
+ }
+
+ // This method is needed because nano protobuf generated classes dont implmenent
+ // .equals
+ private void assertChunkListingsAreEqual(ChunkListing a, ChunkListing b) {
+ byte[] aBytes = MessageNano.toByteArray(a);
+ byte[] bBytes = MessageNano.toByteArray(b);
+
+ assertThat(aBytes).isEqualTo(bBytes);
+ }
+
+ @Implements(BackupFileBuilder.class)
+ public static class ShadowBackupFileBuilder {
+
+ private static BackupFileBuilder sInstance;
+
+ @Implementation
+ public static BackupFileBuilder createForNonIncremental(OutputStream outputStream) {
+ return sInstance;
+ }
+
+ @Implementation
+ public static BackupFileBuilder createForIncremental(
+ OutputStream outputStream, ChunkListing oldChunkListing) {
+ return sInstance;
+ }
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessorTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessorTest.java
new file mode 100644
index 000000000000..675d03fb9869
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessorTest.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.testng.Assert.assertThrows;
+
+import android.annotation.Nullable;
+import android.app.backup.BackupTransport;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.server.backup.encryption.FullBackupDataProcessor;
+import com.android.server.backup.encryption.chunking.ProtoStore;
+import com.android.server.backup.encryption.client.CryptoBackupServer;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
+import com.android.server.backup.encryption.keys.TertiaryKeyManager;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
+import com.android.server.backup.encryption.testing.QueuingNonAutomaticExecutorService;
+
+import com.google.common.io.ByteStreams;
+import com.google.common.primitives.Bytes;
+
+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;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.SecureRandom;
+
+import javax.crypto.spec.SecretKeySpec;
+
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+@Config(
+ shadows = {
+ EncryptedFullBackupDataProcessorTest.ShadowEncryptedFullBackupTask.class,
+ })
+public class EncryptedFullBackupDataProcessorTest {
+
+ private static final String KEY_GENERATOR_ALGORITHM = "AES";
+
+ private static final String TEST_PACKAGE = "com.example.app1";
+ private static final byte[] TEST_DATA_1 = {1, 2, 3, 4};
+ private static final byte[] TEST_DATA_2 = {5, 6, 7, 8};
+
+ private final RecoverableKeyStoreSecondaryKey mTestSecondaryKey =
+ new RecoverableKeyStoreSecondaryKey(
+ /*alias=*/ "test_key",
+ new SecretKeySpec(
+ new byte[] {
+ 1, 2, 3,
+ },
+ KEY_GENERATOR_ALGORITHM));
+
+ private QueuingNonAutomaticExecutorService mExecutorService;
+ private FullBackupDataProcessor mFullBackupDataProcessor;
+ @Mock private FullBackupDataProcessor.FullBackupCallbacks mFullBackupCallbacks;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mExecutorService = new QueuingNonAutomaticExecutorService();
+ mFullBackupDataProcessor =
+ new EncryptedFullBackupDataProcessor(
+ ApplicationProvider.getApplicationContext(),
+ mExecutorService,
+ mock(CryptoBackupServer.class),
+ new SecureRandom(),
+ mTestSecondaryKey,
+ TEST_PACKAGE);
+ }
+
+ @After
+ public void tearDown() {
+ ShadowEncryptedFullBackupTask.reset();
+ }
+
+ @Test
+ public void initiate_callTwice_throws() throws Exception {
+ mFullBackupDataProcessor.initiate(new ByteArrayInputStream(new byte[10]));
+
+ assertThrows(
+ IllegalStateException.class,
+ () -> mFullBackupDataProcessor.initiate(new ByteArrayInputStream(new byte[10])));
+ }
+
+ @Test
+ public void pushData_writesDataToTask() throws Exception {
+ byte[] inputData = Bytes.concat(TEST_DATA_1, TEST_DATA_2);
+
+ mFullBackupDataProcessor.initiate(new ByteArrayInputStream(inputData));
+ mFullBackupDataProcessor.start();
+ mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+ mFullBackupDataProcessor.pushData(TEST_DATA_2.length);
+ finishBackupTask();
+ mFullBackupDataProcessor.finish();
+
+ byte[] result = ByteStreams.toByteArray(ShadowEncryptedFullBackupTask.sInputStream);
+ assertThat(result).isEqualTo(Bytes.concat(TEST_DATA_1, TEST_DATA_2));
+ }
+
+ @Test
+ public void pushData_noError_returnsOk() throws Exception {
+ mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
+ mFullBackupDataProcessor.start();
+ int result = mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+ finishBackupTask();
+ mFullBackupDataProcessor.finish();
+
+ assertThat(result).isEqualTo(BackupTransport.TRANSPORT_OK);
+ }
+
+ @Test
+ public void pushData_ioExceptionOnCopy_returnsError() throws Exception {
+ mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
+ mFullBackupDataProcessor.start();
+
+ // Close the stream so there's an IO error when the processor tries to write to it.
+ ShadowEncryptedFullBackupTask.sInputStream.close();
+ int result = mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+
+ finishBackupTask();
+ mFullBackupDataProcessor.finish();
+
+ assertThat(result).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+ }
+
+ @Test
+ public void pushData_exceptionDuringUpload_returnsError() throws Exception {
+ byte[] inputData = Bytes.concat(TEST_DATA_1, TEST_DATA_2);
+
+ mFullBackupDataProcessor.initiate(new ByteArrayInputStream(inputData));
+ mFullBackupDataProcessor.start();
+ mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+ finishBackupTaskWithException(new IOException("Test exception"));
+ int result = mFullBackupDataProcessor.pushData(TEST_DATA_2.length);
+
+ assertThat(result).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+ }
+
+ @Test
+ public void pushData_quotaExceptionDuringUpload_doesNotLogAndReturnsQuotaExceeded()
+ throws Exception {
+ mFullBackupDataProcessor.attachCallbacks(mFullBackupCallbacks);
+ byte[] inputData = Bytes.concat(TEST_DATA_1, TEST_DATA_2);
+
+ mFullBackupDataProcessor.initiate(new ByteArrayInputStream(inputData));
+ mFullBackupDataProcessor.start();
+ mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+ finishBackupTaskWithException(new SizeQuotaExceededException());
+ int result = mFullBackupDataProcessor.pushData(TEST_DATA_2.length);
+
+ assertThat(result).isEqualTo(BackupTransport.TRANSPORT_QUOTA_EXCEEDED);
+
+ verify(mFullBackupCallbacks, never()).onSuccess();
+ verify(mFullBackupCallbacks, never())
+ .onTransferFailed(); // FullBackupSession will handle this.
+ }
+
+ @Test
+ public void pushData_unexpectedEncryptedBackup_logs() throws Exception {
+ byte[] inputData = Bytes.concat(TEST_DATA_1, TEST_DATA_2);
+
+ mFullBackupDataProcessor.initiate(new ByteArrayInputStream(inputData));
+ mFullBackupDataProcessor.start();
+ mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+ finishBackupTaskWithException(new GeneralSecurityException());
+ int result = mFullBackupDataProcessor.pushData(TEST_DATA_2.length);
+
+ assertThat(result).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+ }
+
+ @Test
+ public void pushData_permanentExceptionDuringUpload_callsErrorCallback() throws Exception {
+ mFullBackupDataProcessor.attachCallbacks(mFullBackupCallbacks);
+ byte[] inputData = Bytes.concat(TEST_DATA_1, TEST_DATA_2);
+
+ mFullBackupDataProcessor.initiate(new ByteArrayInputStream(inputData));
+ mFullBackupDataProcessor.start();
+ mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+ finishBackupTaskWithException(new IOException());
+ mFullBackupDataProcessor.pushData(TEST_DATA_2.length);
+
+ verify(mFullBackupCallbacks, never()).onSuccess();
+ verify(mFullBackupCallbacks).onTransferFailed();
+ }
+
+ @Test
+ public void pushData_beforeInitiate_throws() {
+ assertThrows(
+ IllegalStateException.class,
+ () -> mFullBackupDataProcessor.pushData(/*numBytes=*/ 10));
+ }
+
+ @Test
+ public void cancel_cancelsTask() throws Exception {
+ mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
+ mFullBackupDataProcessor.start();
+ mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+ mFullBackupDataProcessor.cancel();
+
+ assertThat(ShadowEncryptedFullBackupTask.sCancelled).isTrue();
+ }
+
+ @Test
+ public void cancel_beforeInitiate_throws() {
+ assertThrows(IllegalStateException.class, () -> mFullBackupDataProcessor.cancel());
+ }
+
+ @Test
+ public void finish_noException_returnsTransportOk() throws Exception {
+ mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
+ mFullBackupDataProcessor.start();
+ mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+ finishBackupTask();
+ int result = mFullBackupDataProcessor.finish();
+
+ assertThat(result).isEqualTo(BackupTransport.TRANSPORT_OK);
+ }
+
+ @Test
+ public void finish_exceptionDuringUpload_returnsTransportError() throws Exception {
+ mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
+ mFullBackupDataProcessor.start();
+ mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+ finishBackupTaskWithException(new IOException("Test exception"));
+ int result = mFullBackupDataProcessor.finish();
+
+ assertThat(result).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+ }
+
+ @Test
+ public void finish_successfulBackup_callsSuccessCallback() throws Exception {
+ mFullBackupDataProcessor.attachCallbacks(mFullBackupCallbacks);
+
+ mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
+ mFullBackupDataProcessor.start();
+ mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+ finishBackupTask();
+ mFullBackupDataProcessor.finish();
+
+ verify(mFullBackupCallbacks).onSuccess();
+ verify(mFullBackupCallbacks, never()).onTransferFailed();
+ }
+
+ @Test
+ public void finish_backupFailedWithPermanentError_callsErrorCallback() throws Exception {
+ mFullBackupDataProcessor.attachCallbacks(mFullBackupCallbacks);
+
+ mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
+ mFullBackupDataProcessor.start();
+ mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+ finishBackupTaskWithException(new IOException());
+ mFullBackupDataProcessor.finish();
+
+ verify(mFullBackupCallbacks, never()).onSuccess();
+ verify(mFullBackupCallbacks).onTransferFailed();
+ }
+
+ @Test
+ public void finish_backupFailedWithQuotaException_doesNotCallbackAndReturnsQuotaExceeded()
+ throws Exception {
+ mFullBackupDataProcessor.attachCallbacks(mFullBackupCallbacks);
+
+ mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
+ mFullBackupDataProcessor.start();
+ mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+ finishBackupTaskWithException(new SizeQuotaExceededException());
+ int result = mFullBackupDataProcessor.finish();
+
+ assertThat(result).isEqualTo(BackupTransport.TRANSPORT_QUOTA_EXCEEDED);
+ verify(mFullBackupCallbacks, never()).onSuccess();
+ verify(mFullBackupCallbacks, never())
+ .onTransferFailed(); // FullBackupSession will handle this.
+ }
+
+ @Test
+ public void finish_beforeInitiate_throws() {
+ assertThrows(IllegalStateException.class, () -> mFullBackupDataProcessor.finish());
+ }
+
+ @Test
+ public void handleCheckSizeRejectionZeroBytes_cancelsTask() throws Exception {
+ mFullBackupDataProcessor.initiate(new ByteArrayInputStream(new byte[10]));
+ mFullBackupDataProcessor.start();
+ mFullBackupDataProcessor.handleCheckSizeRejectionZeroBytes();
+
+ assertThat(ShadowEncryptedFullBackupTask.sCancelled).isTrue();
+ }
+
+ @Test
+ public void handleCheckSizeRejectionQuotaExceeded_cancelsTask() throws Exception {
+ mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
+ mFullBackupDataProcessor.start();
+ mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+ mFullBackupDataProcessor.handleCheckSizeRejectionQuotaExceeded();
+
+ assertThat(ShadowEncryptedFullBackupTask.sCancelled).isTrue();
+ }
+
+ @Test
+ public void handleSendBytesQuotaExceeded_cancelsTask() throws Exception {
+ mFullBackupDataProcessor.initiate(new ByteArrayInputStream(TEST_DATA_1));
+ mFullBackupDataProcessor.start();
+ mFullBackupDataProcessor.pushData(TEST_DATA_1.length);
+ mFullBackupDataProcessor.handleSendBytesQuotaExceeded();
+
+ assertThat(ShadowEncryptedFullBackupTask.sCancelled).isTrue();
+ }
+
+ private void finishBackupTask() {
+ mExecutorService.runNext();
+ }
+
+ private void finishBackupTaskWithException(Exception exception) {
+ ShadowEncryptedFullBackupTask.sOnCallException = exception;
+ finishBackupTask();
+ }
+
+ @Implements(EncryptedFullBackupTask.class)
+ public static class ShadowEncryptedFullBackupTask {
+
+ private static InputStream sInputStream;
+ @Nullable private static Exception sOnCallException;
+ private static boolean sCancelled;
+
+ public void __constructor__(
+ ProtoStore<ChunksMetadataProto.ChunkListing> chunkListingStore,
+ TertiaryKeyManager tertiaryKeyManager,
+ EncryptedBackupTask task,
+ InputStream inputStream,
+ String packageName,
+ SecureRandom secureRandom) {
+ sInputStream = inputStream;
+ }
+
+ @Implementation
+ public Void call() throws Exception {
+ if (sOnCallException != null) {
+ throw sOnCallException;
+ }
+
+ return null;
+ }
+
+ @Implementation
+ public void cancel() {
+ sCancelled = true;
+ }
+
+ public static void reset() {
+ sOnCallException = null;
+ sCancelled = false;
+ }
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTaskTest.java
new file mode 100644
index 000000000000..096b2da10c98
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTaskTest.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.chunking.ProtoStore;
+import com.android.server.backup.encryption.chunking.cdc.FingerprintMixer;
+import com.android.server.backup.encryption.keys.TertiaryKeyManager;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.ChunkListing;
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto.WrappedKey;
+import com.android.server.backup.testing.CryptoTestUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Optional;
+
+import javax.crypto.SecretKey;
+
+@Config(shadows = {EncryptedBackupTaskTest.ShadowBackupFileBuilder.class})
+@RunWith(RobolectricTestRunner.class)
+public class EncryptedFullBackupTaskTest {
+ private static final String TEST_PACKAGE_NAME = "com.example.package";
+ private static final byte[] TEST_EXISTING_FINGERPRINT_MIXER_SALT =
+ Arrays.copyOf(new byte[] {11}, ChunkHash.HASH_LENGTH_BYTES);
+ private static final byte[] TEST_GENERATED_FINGERPRINT_MIXER_SALT =
+ Arrays.copyOf(new byte[] {22}, ChunkHash.HASH_LENGTH_BYTES);
+ private static final ChunkHash TEST_CHUNK_HASH_1 =
+ new ChunkHash(Arrays.copyOf(new byte[] {1}, ChunkHash.HASH_LENGTH_BYTES));
+ private static final ChunkHash TEST_CHUNK_HASH_2 =
+ new ChunkHash(Arrays.copyOf(new byte[] {2}, ChunkHash.HASH_LENGTH_BYTES));
+ private static final int TEST_CHUNK_LENGTH_1 = 20;
+ private static final int TEST_CHUNK_LENGTH_2 = 40;
+
+ @Mock private ProtoStore<ChunkListing> mChunkListingStore;
+ @Mock private TertiaryKeyManager mTertiaryKeyManager;
+ @Mock private InputStream mInputStream;
+ @Mock private EncryptedBackupTask mEncryptedBackupTask;
+ @Mock private SecretKey mTertiaryKey;
+ @Mock private SecureRandom mSecureRandom;
+
+ private EncryptedFullBackupTask mTask;
+ private ChunkListing mOldChunkListing;
+ private ChunkListing mNewChunkListing;
+ private WrappedKey mWrappedTertiaryKey;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mWrappedTertiaryKey = new WrappedKey();
+ when(mTertiaryKeyManager.getKey()).thenReturn(mTertiaryKey);
+ when(mTertiaryKeyManager.getWrappedKey()).thenReturn(mWrappedTertiaryKey);
+
+ mOldChunkListing =
+ CryptoTestUtils.newChunkListing(
+ /* docId */ null,
+ TEST_EXISTING_FINGERPRINT_MIXER_SALT,
+ ChunksMetadataProto.AES_256_GCM,
+ ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED,
+ CryptoTestUtils.newChunk(TEST_CHUNK_HASH_1.getHash(), TEST_CHUNK_LENGTH_1));
+ mNewChunkListing =
+ CryptoTestUtils.newChunkListing(
+ /* docId */ null,
+ /* fingerprintSalt */ null,
+ ChunksMetadataProto.AES_256_GCM,
+ ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED,
+ CryptoTestUtils.newChunk(TEST_CHUNK_HASH_1.getHash(), TEST_CHUNK_LENGTH_1),
+ CryptoTestUtils.newChunk(TEST_CHUNK_HASH_2.getHash(), TEST_CHUNK_LENGTH_2));
+ when(mEncryptedBackupTask.performNonIncrementalBackup(any(), any(), any()))
+ .thenReturn(mNewChunkListing);
+ when(mEncryptedBackupTask.performIncrementalBackup(any(), any(), any()))
+ .thenReturn(mNewChunkListing);
+ when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME)).thenReturn(Optional.empty());
+
+ doAnswer(invocation -> {
+ byte[] byteArray = (byte[]) invocation.getArguments()[0];
+ System.arraycopy(
+ TEST_GENERATED_FINGERPRINT_MIXER_SALT,
+ /* srcPos */ 0,
+ byteArray,
+ /* destPos */ 0,
+ FingerprintMixer.SALT_LENGTH_BYTES);
+ return null;
+ })
+ .when(mSecureRandom)
+ .nextBytes(any(byte[].class));
+
+ mTask =
+ new EncryptedFullBackupTask(
+ mChunkListingStore,
+ mTertiaryKeyManager,
+ mEncryptedBackupTask,
+ mInputStream,
+ TEST_PACKAGE_NAME,
+ mSecureRandom);
+ }
+
+ @Test
+ public void call_existingChunkListingButTertiaryKeyRotated_performsNonIncrementalBackup()
+ throws Exception {
+ when(mTertiaryKeyManager.wasKeyRotated()).thenReturn(true);
+ when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME))
+ .thenReturn(Optional.of(mOldChunkListing));
+
+ mTask.call();
+
+ verify(mEncryptedBackupTask)
+ .performNonIncrementalBackup(
+ eq(mTertiaryKey),
+ eq(mWrappedTertiaryKey),
+ eq(TEST_GENERATED_FINGERPRINT_MIXER_SALT));
+ }
+
+ @Test
+ public void call_noExistingChunkListing_performsNonIncrementalBackup() throws Exception {
+ when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME)).thenReturn(Optional.empty());
+ mTask.call();
+ verify(mEncryptedBackupTask)
+ .performNonIncrementalBackup(
+ eq(mTertiaryKey),
+ eq(mWrappedTertiaryKey),
+ eq(TEST_GENERATED_FINGERPRINT_MIXER_SALT));
+ }
+
+ @Test
+ public void call_existingChunkListing_performsIncrementalBackup() throws Exception {
+ when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME))
+ .thenReturn(Optional.of(mOldChunkListing));
+ mTask.call();
+ verify(mEncryptedBackupTask)
+ .performIncrementalBackup(
+ eq(mTertiaryKey), eq(mWrappedTertiaryKey), eq(mOldChunkListing));
+ }
+
+ @Test
+ public void
+ call_existingChunkListingWithNoFingerprintMixerSalt_doesntSetSaltBeforeIncBackup()
+ throws Exception {
+ mOldChunkListing.fingerprintMixerSalt = new byte[0];
+ when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME))
+ .thenReturn(Optional.of(mOldChunkListing));
+
+ mTask.call();
+
+ verify(mEncryptedBackupTask)
+ .performIncrementalBackup(
+ eq(mTertiaryKey), eq(mWrappedTertiaryKey), eq(mOldChunkListing));
+ }
+
+ @Test
+ public void call_noExistingChunkListing_storesNewChunkListing() throws Exception {
+ when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME)).thenReturn(Optional.empty());
+ mTask.call();
+ verify(mChunkListingStore).saveProto(TEST_PACKAGE_NAME, mNewChunkListing);
+ }
+
+ @Test
+ public void call_existingChunkListing_storesNewChunkListing() throws Exception {
+ when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME))
+ .thenReturn(Optional.of(mOldChunkListing));
+ mTask.call();
+ verify(mChunkListingStore).saveProto(TEST_PACKAGE_NAME, mNewChunkListing);
+ }
+
+ @Test
+ public void call_exceptionDuringBackup_doesNotSaveNewChunkListing() throws Exception {
+ when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME)).thenReturn(Optional.empty());
+ when(mEncryptedBackupTask.performNonIncrementalBackup(any(), any(), any()))
+ .thenThrow(GeneralSecurityException.class);
+
+ assertThrows(Exception.class, () -> mTask.call());
+
+ assertThat(mChunkListingStore.loadProto(TEST_PACKAGE_NAME).isPresent()).isFalse();
+ }
+
+ @Test
+ public void call_incrementalThrowsPermanentException_clearsState() throws Exception {
+ when(mChunkListingStore.loadProto(TEST_PACKAGE_NAME))
+ .thenReturn(Optional.of(mOldChunkListing));
+ when(mEncryptedBackupTask.performIncrementalBackup(any(), any(), any()))
+ .thenThrow(IOException.class);
+
+ assertThrows(IOException.class, () -> mTask.call());
+
+ verify(mChunkListingStore).deleteProto(TEST_PACKAGE_NAME);
+ }
+
+ @Test
+ public void call_closesInputStream() throws Exception {
+ mTask.call();
+ verify(mInputStream).close();
+ }
+
+ @Test
+ public void cancel_cancelsTask() throws Exception {
+ mTask.cancel();
+ verify(mEncryptedBackupTask).cancel();
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullRestoreTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullRestoreTaskTest.java
new file mode 100644
index 000000000000..0affacd114bf
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullRestoreTaskTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+
+import static java.util.stream.Collectors.toList;
+
+import com.android.server.backup.encryption.FullRestoreDownloader;
+
+import com.google.common.io.Files;
+import com.google.common.primitives.Bytes;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+
+@RunWith(RobolectricTestRunner.class)
+public class EncryptedFullRestoreTaskTest {
+ private static final int TEST_BUFFER_SIZE = 10;
+ private static final byte[] TEST_ENCRYPTED_DATA = {1, 2, 3, 4, 5, 6};
+ private static final byte[] TEST_DECRYPTED_DATA = fakeDecrypt(TEST_ENCRYPTED_DATA);
+
+ @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ @Mock private BackupFileDecryptorTask mDecryptorTask;
+
+ private File mFolder;
+ private FakeFullRestoreDownloader mFullRestorePackageWrapper;
+ private EncryptedFullRestoreTask mTask;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mFolder = temporaryFolder.newFolder();
+ mFullRestorePackageWrapper = new FakeFullRestoreDownloader(TEST_ENCRYPTED_DATA);
+
+ doAnswer(
+ invocation -> {
+ File source = invocation.getArgument(0);
+ DecryptedChunkOutput target = invocation.getArgument(1);
+ byte[] decrypted = fakeDecrypt(Files.toByteArray(source));
+ target.open();
+ target.processChunk(decrypted, decrypted.length);
+ target.close();
+ return null;
+ })
+ .when(mDecryptorTask)
+ .decryptFile(any(), any());
+
+ mTask = new EncryptedFullRestoreTask(mFolder, mFullRestorePackageWrapper, mDecryptorTask);
+ }
+
+ @Test
+ public void readNextChunk_downloadsAndDecryptsBackup() throws Exception {
+ ByteArrayOutputStream decryptedOutput = new ByteArrayOutputStream();
+
+ byte[] buffer = new byte[TEST_BUFFER_SIZE];
+ int bytesRead = mTask.readNextChunk(buffer);
+ while (bytesRead != -1) {
+ decryptedOutput.write(buffer, 0, bytesRead);
+ bytesRead = mTask.readNextChunk(buffer);
+ }
+
+ assertThat(decryptedOutput.toByteArray()).isEqualTo(TEST_DECRYPTED_DATA);
+ }
+
+ @Test
+ public void finish_deletesTemporaryFiles() throws Exception {
+ mTask.readNextChunk(new byte[10]);
+ mTask.finish(FullRestoreDownloader.FinishType.UNKNOWN_FINISH);
+
+ assertThat(mFolder.listFiles()).isEmpty();
+ }
+
+ /** Fake package wrapper which returns data from a byte array. */
+ private static class FakeFullRestoreDownloader extends FullRestoreDownloader {
+ private final ByteArrayInputStream mData;
+
+ FakeFullRestoreDownloader(byte[] data) {
+ // We override all methods of the superclass, so it does not require any collaborators.
+ super();
+ mData = new ByteArrayInputStream(data);
+ }
+
+ @Override
+ public int readNextChunk(byte[] buffer) throws IOException {
+ return mData.read(buffer);
+ }
+
+ @Override
+ public void finish(FinishType finishType) {
+ // Nothing to do.
+ }
+ }
+
+ /** Fake decrypts a byte array by subtracting 1 from each byte. */
+ private static byte[] fakeDecrypt(byte[] input) {
+ return Bytes.toArray(Bytes.asList(input).stream().map(b -> b + 1).collect(toList()));
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedKvBackupTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedKvBackupTaskTest.java
new file mode 100644
index 000000000000..fa4fef50ac1a
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedKvBackupTaskTest.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertThrows;
+
+import android.app.Application;
+import android.util.Pair;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.chunking.ProtoStore;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
+import com.android.server.backup.encryption.keys.TertiaryKeyManager;
+import com.android.server.backup.encryption.kv.KeyValueListingBuilder;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
+import com.android.server.backup.encryption.protos.nano.KeyValueListingProto;
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+import com.android.server.backup.testing.CryptoTestUtils;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.crypto.SecretKey;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class EncryptedKvBackupTaskTest {
+ private static final boolean INCREMENTAL = true;
+ private static final boolean NON_INCREMENTAL = false;
+
+ private static final String TEST_PACKAGE_1 = "com.example.app1";
+ private static final String TEST_KEY_1 = "key_1";
+ private static final String TEST_KEY_2 = "key_2";
+ private static final ChunkHash TEST_HASH_1 =
+ new ChunkHash(Arrays.copyOf(new byte[] {1}, ChunkHash.HASH_LENGTH_BYTES));
+ private static final ChunkHash TEST_HASH_2 =
+ new ChunkHash(Arrays.copyOf(new byte[] {2}, ChunkHash.HASH_LENGTH_BYTES));
+ private static final int TEST_LENGTH_1 = 200;
+ private static final int TEST_LENGTH_2 = 300;
+
+ @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+ @Captor private ArgumentCaptor<ChunksMetadataProto.ChunkListing> mChunkListingCaptor;
+
+ @Mock private TertiaryKeyManager mTertiaryKeyManager;
+ @Mock private RecoverableKeyStoreSecondaryKey mSecondaryKey;
+ @Mock private ProtoStore<KeyValueListingProto.KeyValueListing> mKeyValueListingStore;
+ @Mock private ProtoStore<ChunksMetadataProto.ChunkListing> mChunkListingStore;
+ @Mock private KvBackupEncrypter mKvBackupEncrypter;
+ @Mock private EncryptedBackupTask mEncryptedBackupTask;
+ @Mock private SecretKey mTertiaryKey;
+
+ private WrappedKeyProto.WrappedKey mWrappedTertiaryKey;
+ private KeyValueListingProto.KeyValueListing mNewKeyValueListing;
+ private ChunksMetadataProto.ChunkListing mNewChunkListing;
+ private EncryptedKvBackupTask mTask;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ Application application = ApplicationProvider.getApplicationContext();
+ mKeyValueListingStore = ProtoStore.createKeyValueListingStore(application);
+ mChunkListingStore = ProtoStore.createChunkListingStore(application);
+
+ mWrappedTertiaryKey = new WrappedKeyProto.WrappedKey();
+
+ when(mTertiaryKeyManager.wasKeyRotated()).thenReturn(false);
+ when(mTertiaryKeyManager.getKey()).thenReturn(mTertiaryKey);
+ when(mTertiaryKeyManager.getWrappedKey()).thenReturn(mWrappedTertiaryKey);
+
+ mNewKeyValueListing =
+ createKeyValueListing(
+ CryptoTestUtils.mapOf(
+ new Pair<>(TEST_KEY_1, TEST_HASH_1),
+ new Pair<>(TEST_KEY_2, TEST_HASH_2)));
+ mNewChunkListing =
+ createChunkListing(
+ CryptoTestUtils.mapOf(
+ new Pair<>(TEST_HASH_1, TEST_LENGTH_1),
+ new Pair<>(TEST_HASH_2, TEST_LENGTH_2)));
+ when(mKvBackupEncrypter.getNewKeyValueListing()).thenReturn(mNewKeyValueListing);
+ when(mEncryptedBackupTask.performIncrementalBackup(
+ eq(mTertiaryKey), eq(mWrappedTertiaryKey), any()))
+ .thenReturn(mNewChunkListing);
+ when(mEncryptedBackupTask.performNonIncrementalBackup(
+ eq(mTertiaryKey), eq(mWrappedTertiaryKey), any()))
+ .thenReturn(mNewChunkListing);
+
+ mTask =
+ new EncryptedKvBackupTask(
+ mTertiaryKeyManager,
+ mKeyValueListingStore,
+ mSecondaryKey,
+ mChunkListingStore,
+ mKvBackupEncrypter,
+ mEncryptedBackupTask,
+ TEST_PACKAGE_1);
+ }
+
+ @Test
+ public void testPerformBackup_rotationRequired_deletesListings() throws Exception {
+ mKeyValueListingStore.saveProto(
+ TEST_PACKAGE_1,
+ createKeyValueListing(CryptoTestUtils.mapOf(new Pair<>(TEST_KEY_1, TEST_HASH_1))));
+ mChunkListingStore.saveProto(
+ TEST_PACKAGE_1,
+ createChunkListing(CryptoTestUtils.mapOf(new Pair<>(TEST_HASH_1, TEST_LENGTH_1))));
+
+ when(mTertiaryKeyManager.wasKeyRotated()).thenReturn(true);
+ // Throw an IOException so it aborts before saving the new listings.
+ when(mEncryptedBackupTask.performNonIncrementalBackup(any(), any(), any()))
+ .thenThrow(IOException.class);
+
+ assertThrows(IOException.class, () -> mTask.performBackup(NON_INCREMENTAL));
+
+ assertFalse(mKeyValueListingStore.loadProto(TEST_PACKAGE_1).isPresent());
+ assertFalse(mChunkListingStore.loadProto(TEST_PACKAGE_1).isPresent());
+ }
+
+ @Test
+ public void testPerformBackup_rotationRequiredButIncremental_throws() throws Exception {
+ mKeyValueListingStore.saveProto(
+ TEST_PACKAGE_1,
+ createKeyValueListing(CryptoTestUtils.mapOf(new Pair<>(TEST_KEY_1, TEST_HASH_1))));
+ mChunkListingStore.saveProto(
+ TEST_PACKAGE_1,
+ createChunkListing(CryptoTestUtils.mapOf(new Pair<>(TEST_HASH_1, TEST_LENGTH_1))));
+
+ when(mTertiaryKeyManager.wasKeyRotated()).thenReturn(true);
+
+ assertThrows(NonIncrementalBackupRequiredException.class,
+ () -> mTask.performBackup(INCREMENTAL));
+ }
+
+ @Test
+ public void testPerformBackup_rotationRequiredAndNonIncremental_performsNonIncrementalBackup()
+ throws Exception {
+ mKeyValueListingStore.saveProto(
+ TEST_PACKAGE_1,
+ createKeyValueListing(CryptoTestUtils.mapOf(new Pair<>(TEST_KEY_1, TEST_HASH_1))));
+ mChunkListingStore.saveProto(
+ TEST_PACKAGE_1,
+ createChunkListing(CryptoTestUtils.mapOf(new Pair<>(TEST_HASH_1, TEST_LENGTH_1))));
+
+ when(mTertiaryKeyManager.wasKeyRotated()).thenReturn(true);
+
+ mTask.performBackup(NON_INCREMENTAL);
+
+ verify(mEncryptedBackupTask)
+ .performNonIncrementalBackup(eq(mTertiaryKey), eq(mWrappedTertiaryKey), any());
+ }
+
+ @Test
+ public void testPerformBackup_existingStateButNonIncremental_deletesListings() throws Exception {
+ mKeyValueListingStore.saveProto(
+ TEST_PACKAGE_1,
+ createKeyValueListing(CryptoTestUtils.mapOf(new Pair<>(TEST_KEY_1, TEST_HASH_1))));
+ mChunkListingStore.saveProto(
+ TEST_PACKAGE_1,
+ createChunkListing(CryptoTestUtils.mapOf(new Pair<>(TEST_HASH_1, TEST_LENGTH_1))));
+
+ // Throw an IOException so it aborts before saving the new listings.
+ when(mEncryptedBackupTask.performNonIncrementalBackup(any(), any(), any()))
+ .thenThrow(IOException.class);
+
+ assertThrows(IOException.class, () -> mTask.performBackup(NON_INCREMENTAL));
+
+ assertFalse(mKeyValueListingStore.loadProto(TEST_PACKAGE_1).isPresent());
+ assertFalse(mChunkListingStore.loadProto(TEST_PACKAGE_1).isPresent());
+ }
+
+ @Test
+ public void testPerformBackup_keyValueListingMissing_deletesChunkListingAndPerformsNonIncremental()
+ throws Exception {
+ mChunkListingStore.saveProto(
+ TEST_PACKAGE_1,
+ createChunkListing(CryptoTestUtils.mapOf(new Pair<>(TEST_HASH_1, TEST_LENGTH_1))));
+
+ // Throw an IOException so it aborts before saving the new listings.
+ when(mEncryptedBackupTask.performNonIncrementalBackup(any(), any(), any()))
+ .thenThrow(IOException.class);
+
+ assertThrows(IOException.class, () -> mTask.performBackup(NON_INCREMENTAL));
+
+ verify(mEncryptedBackupTask).performNonIncrementalBackup(any(), any(), any());
+ assertFalse(mKeyValueListingStore.loadProto(TEST_PACKAGE_1).isPresent());
+ assertFalse(mChunkListingStore.loadProto(TEST_PACKAGE_1).isPresent());
+ }
+
+ @Test
+ public void testPerformBackup_chunkListingMissing_deletesKeyValueListingAndPerformsNonIncremental()
+ throws Exception {
+ mKeyValueListingStore.saveProto(
+ TEST_PACKAGE_1,
+ createKeyValueListing(CryptoTestUtils.mapOf(new Pair<>(TEST_KEY_1, TEST_HASH_1))));
+
+ // Throw an IOException so it aborts before saving the new listings.
+ when(mEncryptedBackupTask.performNonIncrementalBackup(any(), any(), any()))
+ .thenThrow(IOException.class);
+
+ assertThrows(IOException.class, () -> mTask.performBackup(NON_INCREMENTAL));
+
+ verify(mEncryptedBackupTask).performNonIncrementalBackup(any(), any(), any());
+ assertFalse(mKeyValueListingStore.loadProto(TEST_PACKAGE_1).isPresent());
+ assertFalse(mChunkListingStore.loadProto(TEST_PACKAGE_1).isPresent());
+ }
+
+ @Test
+ public void testPerformBackup_existingStateAndIncremental_performsIncrementalBackup()
+ throws Exception {
+ mKeyValueListingStore.saveProto(
+ TEST_PACKAGE_1,
+ createKeyValueListing(CryptoTestUtils.mapOf(new Pair<>(TEST_KEY_1, TEST_HASH_1))));
+ ChunksMetadataProto.ChunkListing oldChunkListing =
+ createChunkListing(CryptoTestUtils.mapOf(new Pair<>(TEST_HASH_1, TEST_LENGTH_1)));
+ mChunkListingStore.saveProto(TEST_PACKAGE_1, oldChunkListing);
+
+ mTask.performBackup(INCREMENTAL);
+
+ verify(mEncryptedBackupTask)
+ .performIncrementalBackup(
+ eq(mTertiaryKey), eq(mWrappedTertiaryKey), mChunkListingCaptor.capture());
+ assertChunkListingsEqual(mChunkListingCaptor.getValue(), oldChunkListing);
+ }
+
+ @Test
+ public void testPerformBackup_noExistingStateAndNonIncremental_performsNonIncrementalBackup()
+ throws Exception {
+ mTask.performBackup(NON_INCREMENTAL);
+
+ verify(mEncryptedBackupTask)
+ .performNonIncrementalBackup(eq(mTertiaryKey), eq(mWrappedTertiaryKey), eq(null));
+ }
+
+ @Test
+ public void testPerformBackup_incremental_savesNewListings() throws Exception {
+ mKeyValueListingStore.saveProto(
+ TEST_PACKAGE_1,
+ createKeyValueListing(CryptoTestUtils.mapOf(new Pair<>(TEST_KEY_1, TEST_HASH_1))));
+ mChunkListingStore.saveProto(
+ TEST_PACKAGE_1,
+ createChunkListing(CryptoTestUtils.mapOf(new Pair<>(TEST_HASH_1, TEST_LENGTH_1))));
+
+ mTask.performBackup(INCREMENTAL);
+
+ KeyValueListingProto.KeyValueListing actualKeyValueListing =
+ mKeyValueListingStore.loadProto(TEST_PACKAGE_1).get();
+ ChunksMetadataProto.ChunkListing actualChunkListing =
+ mChunkListingStore.loadProto(TEST_PACKAGE_1).get();
+ assertKeyValueListingsEqual(actualKeyValueListing, mNewKeyValueListing);
+ assertChunkListingsEqual(actualChunkListing, mNewChunkListing);
+ }
+
+ @Test
+ public void testPerformBackup_nonIncremental_savesNewListings() throws Exception {
+ mTask.performBackup(NON_INCREMENTAL);
+
+ KeyValueListingProto.KeyValueListing actualKeyValueListing =
+ mKeyValueListingStore.loadProto(TEST_PACKAGE_1).get();
+ ChunksMetadataProto.ChunkListing actualChunkListing =
+ mChunkListingStore.loadProto(TEST_PACKAGE_1).get();
+ assertKeyValueListingsEqual(actualKeyValueListing, mNewKeyValueListing);
+ assertChunkListingsEqual(actualChunkListing, mNewChunkListing);
+ }
+
+ private static KeyValueListingProto.KeyValueListing createKeyValueListing(
+ Map<String, ChunkHash> pairs) {
+ return new KeyValueListingBuilder().addAll(pairs).build();
+ }
+
+ private static ChunksMetadataProto.ChunkListing createChunkListing(
+ Map<ChunkHash, Integer> chunks) {
+ ChunksMetadataProto.Chunk[] listingChunks = new ChunksMetadataProto.Chunk[chunks.size()];
+ int chunksAdded = 0;
+ for (Entry<ChunkHash, Integer> entry : chunks.entrySet()) {
+ listingChunks[chunksAdded] = CryptoTestUtils.newChunk(entry.getKey(), entry.getValue());
+ chunksAdded++;
+ }
+ return CryptoTestUtils.newChunkListingWithoutDocId(
+ /* fingerprintSalt */ new byte[0],
+ ChunksMetadataProto.AES_256_GCM,
+ ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED,
+ listingChunks);
+ }
+
+ private static void assertKeyValueListingsEqual(
+ KeyValueListingProto.KeyValueListing actual,
+ KeyValueListingProto.KeyValueListing expected) {
+ KeyValueListingProto.KeyValueEntry[] actualEntries = actual.entries;
+ KeyValueListingProto.KeyValueEntry[] expectedEntries = expected.entries;
+ assertThat(actualEntries.length).isEqualTo(expectedEntries.length);
+ for (int i = 0; i < actualEntries.length; i++) {
+ assertWithMessage("entry " + i)
+ .that(actualEntries[i].key)
+ .isEqualTo(expectedEntries[i].key);
+ assertWithMessage("entry " + i)
+ .that(actualEntries[i].hash)
+ .isEqualTo(expectedEntries[i].hash);
+ }
+ }
+
+ private static void assertChunkListingsEqual(
+ ChunksMetadataProto.ChunkListing actual, ChunksMetadataProto.ChunkListing expected) {
+ ChunksMetadataProto.Chunk[] actualChunks = actual.chunks;
+ ChunksMetadataProto.Chunk[] expectedChunks = expected.chunks;
+ assertThat(actualChunks.length).isEqualTo(expectedChunks.length);
+ for (int i = 0; i < actualChunks.length; i++) {
+ assertWithMessage("chunk " + i)
+ .that(actualChunks[i].hash)
+ .isEqualTo(expectedChunks[i].hash);
+ assertWithMessage("chunk " + i)
+ .that(actualChunks[i].length)
+ .isEqualTo(expectedChunks[i].length);
+ }
+ assertThat(actual.cipherType).isEqualTo(expected.cipherType);
+ assertThat(actual.documentId)
+ .isEqualTo(expected.documentId == null ? "" : expected.documentId);
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedKvRestoreTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedKvRestoreTaskTest.java
new file mode 100644
index 000000000000..6666d95d9a2d
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedKvRestoreTaskTest.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.os.ParcelFileDescriptor;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.chunking.ChunkHasher;
+import com.android.server.backup.testing.CryptoTestUtils;
+import com.android.server.testing.shadows.DataEntity;
+import com.android.server.testing.shadows.ShadowBackupDataOutput;
+
+import com.google.protobuf.nano.MessageNano;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+@Config(shadows = {ShadowBackupDataOutput.class})
+@RunWith(RobolectricTestRunner.class)
+public class EncryptedKvRestoreTaskTest {
+ private static final String TEST_KEY_1 = "test_key_1";
+ private static final String TEST_KEY_2 = "test_key_2";
+ private static final String TEST_KEY_3 = "test_key_3";
+ private static final byte[] TEST_VALUE_1 = {1, 2, 3};
+ private static final byte[] TEST_VALUE_2 = {4, 5, 6};
+ private static final byte[] TEST_VALUE_3 = {20, 25, 30, 35};
+
+ @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ private File temporaryDirectory;
+
+ @Mock private ParcelFileDescriptor mParcelFileDescriptor;
+ @Mock private ChunkHasher mChunkHasher;
+ @Mock private FullRestoreToFileTask mFullRestoreToFileTask;
+ @Mock private BackupFileDecryptorTask mBackupFileDecryptorTask;
+
+ private EncryptedKvRestoreTask task;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ when(mChunkHasher.computeHash(any()))
+ .thenAnswer(invocation -> fakeHash(invocation.getArgument(0)));
+ doAnswer(invocation -> writeTestPairsToFile(invocation.getArgument(0)))
+ .when(mFullRestoreToFileTask)
+ .restoreToFile(any());
+ doAnswer(
+ invocation ->
+ readPairsFromFile(
+ invocation.getArgument(0), invocation.getArgument(1)))
+ .when(mBackupFileDecryptorTask)
+ .decryptFile(any(), any());
+
+ temporaryDirectory = temporaryFolder.newFolder();
+ task =
+ new EncryptedKvRestoreTask(
+ temporaryDirectory,
+ mChunkHasher,
+ mFullRestoreToFileTask,
+ mBackupFileDecryptorTask);
+ }
+
+ @Test
+ public void testGetRestoreData_writesPairsToOutputInOrder() throws Exception {
+ task.getRestoreData(mParcelFileDescriptor);
+
+ assertThat(ShadowBackupDataOutput.getEntities())
+ .containsExactly(
+ new DataEntity(TEST_KEY_1, TEST_VALUE_1),
+ new DataEntity(TEST_KEY_2, TEST_VALUE_2),
+ new DataEntity(TEST_KEY_3, TEST_VALUE_3))
+ .inOrder();
+ }
+
+ @Test
+ public void testGetRestoreData_exceptionDuringDecryption_throws() throws Exception {
+ doThrow(IOException.class).when(mBackupFileDecryptorTask).decryptFile(any(), any());
+ assertThrows(IOException.class, () -> task.getRestoreData(mParcelFileDescriptor));
+ }
+
+ @Test
+ public void testGetRestoreData_exceptionDuringDownload_throws() throws Exception {
+ doThrow(IOException.class).when(mFullRestoreToFileTask).restoreToFile(any());
+ assertThrows(IOException.class, () -> task.getRestoreData(mParcelFileDescriptor));
+ }
+
+ @Test
+ public void testGetRestoreData_exceptionDuringDecryption_deletesTemporaryFiles() throws Exception {
+ doThrow(InvalidKeyException.class).when(mBackupFileDecryptorTask).decryptFile(any(), any());
+ assertThrows(InvalidKeyException.class, () -> task.getRestoreData(mParcelFileDescriptor));
+ assertThat(temporaryDirectory.listFiles()).isEmpty();
+ }
+
+ @Test
+ public void testGetRestoreData_exceptionDuringDownload_deletesTemporaryFiles() throws Exception {
+ doThrow(IOException.class).when(mFullRestoreToFileTask).restoreToFile(any());
+ assertThrows(IOException.class, () -> task.getRestoreData(mParcelFileDescriptor));
+ assertThat(temporaryDirectory.listFiles()).isEmpty();
+ }
+
+ private static Void writeTestPairsToFile(File file) throws IOException {
+ // Write the pairs out of order to check the task sorts them.
+ Set<byte[]> pairs =
+ new HashSet<>(
+ Arrays.asList(
+ createPair(TEST_KEY_1, TEST_VALUE_1),
+ createPair(TEST_KEY_3, TEST_VALUE_3),
+ createPair(TEST_KEY_2, TEST_VALUE_2)));
+
+ try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))) {
+ oos.writeObject(pairs);
+ }
+ return null;
+ }
+
+ private static Void readPairsFromFile(File file, DecryptedChunkOutput decryptedChunkOutput)
+ throws IOException, ClassNotFoundException, InvalidKeyException,
+ NoSuchAlgorithmException {
+ try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
+ DecryptedChunkOutput output = decryptedChunkOutput.open()) {
+ Set<byte[]> pairs = readPairs(ois);
+ for (byte[] pair : pairs) {
+ output.processChunk(pair, pair.length);
+ }
+ }
+
+ return null;
+ }
+
+ private static byte[] createPair(String key, byte[] value) {
+ return MessageNano.toByteArray(CryptoTestUtils.newPair(key, value));
+ }
+
+ @SuppressWarnings("unchecked") // deserialization.
+ private static Set<byte[]> readPairs(ObjectInputStream ois)
+ throws IOException, ClassNotFoundException {
+ return (Set<byte[]>) ois.readObject();
+ }
+
+ private static ChunkHash fakeHash(byte[] data) {
+ return new ChunkHash(Arrays.copyOf(data, ChunkHash.HASH_LENGTH_BYTES));
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/FullRestoreToFileTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/FullRestoreToFileTaskTest.java
new file mode 100644
index 000000000000..de8b7340ebce
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/FullRestoreToFileTaskTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.backup.encryption.FullRestoreDownloader;
+import com.android.server.backup.encryption.FullRestoreDownloader.FinishType;
+
+import com.google.common.io.Files;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.Random;
+
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class FullRestoreToFileTaskTest {
+ private static final int TEST_RANDOM_SEED = 34;
+ private static final int TEST_MAX_CHUNK_SIZE_BYTES = 5;
+ private static final int TEST_DATA_LENGTH_BYTES = TEST_MAX_CHUNK_SIZE_BYTES * 20;
+
+ @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+ private byte[] mTestData;
+ private File mTargetFile;
+ private FakeFullRestoreDownloader mFakeFullRestoreDownloader;
+ @Mock private FullRestoreDownloader mMockFullRestoreDownloader;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mTargetFile = mTemporaryFolder.newFile();
+
+ mTestData = new byte[TEST_DATA_LENGTH_BYTES];
+ new Random(TEST_RANDOM_SEED).nextBytes(mTestData);
+ mFakeFullRestoreDownloader = new FakeFullRestoreDownloader(mTestData);
+ }
+
+ private FullRestoreToFileTask createTaskWithFakeDownloader() {
+ return new FullRestoreToFileTask(mFakeFullRestoreDownloader, TEST_MAX_CHUNK_SIZE_BYTES);
+ }
+
+ private FullRestoreToFileTask createTaskWithMockDownloader() {
+ return new FullRestoreToFileTask(mMockFullRestoreDownloader, TEST_MAX_CHUNK_SIZE_BYTES);
+ }
+
+ @Test
+ public void restoreToFile_readsDataAndWritesToFile() throws Exception {
+ FullRestoreToFileTask task = createTaskWithFakeDownloader();
+ task.restoreToFile(mTargetFile);
+ assertThat(Files.toByteArray(mTargetFile)).isEqualTo(mTestData);
+ }
+
+ @Test
+ public void restoreToFile_noErrors_closesDownloaderWithFinished() throws Exception {
+ FullRestoreToFileTask task = createTaskWithMockDownloader();
+ when(mMockFullRestoreDownloader.readNextChunk(any())).thenReturn(-1);
+
+ task.restoreToFile(mTargetFile);
+
+ verify(mMockFullRestoreDownloader).finish(FinishType.FINISHED);
+ }
+
+ @Test
+ public void restoreToFile_ioException_closesDownloaderWithTransferFailure() throws Exception {
+ FullRestoreToFileTask task = createTaskWithMockDownloader();
+ when(mMockFullRestoreDownloader.readNextChunk(any())).thenThrow(IOException.class);
+
+ assertThrows(IOException.class, () -> task.restoreToFile(mTargetFile));
+
+ verify(mMockFullRestoreDownloader).finish(FinishType.TRANSFER_FAILURE);
+ }
+
+ /** Fake package wrapper which returns data from a byte array. */
+ private static class FakeFullRestoreDownloader extends FullRestoreDownloader {
+
+ private final ByteArrayInputStream mData;
+
+ FakeFullRestoreDownloader(byte[] data) {
+ // We override all methods of the superclass, so it does not require any collaborators.
+ super();
+ this.mData = new ByteArrayInputStream(data);
+ }
+
+ @Override
+ public int readNextChunk(byte[] buffer) throws IOException {
+ return mData.read(buffer);
+ }
+
+ @Override
+ public void finish(FinishType finishType) {
+ // Do nothing.
+ }
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/InitializeRecoverableSecondaryKeyTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/InitializeRecoverableSecondaryKeyTaskTest.java
new file mode 100644
index 000000000000..4a7ae03bd0db
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/InitializeRecoverableSecondaryKeyTaskTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static android.security.keystore.recovery.RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.app.Application;
+import android.security.keystore.recovery.RecoveryController;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.server.backup.encryption.CryptoSettings;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
+import com.android.server.testing.fakes.FakeCryptoBackupServer;
+import com.android.server.testing.shadows.ShadowRecoveryController;
+
+import java.security.InvalidKeyException;
+import java.security.SecureRandom;
+import java.util.Optional;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+@Config(shadows = {ShadowRecoveryController.class})
+@RunWith(RobolectricTestRunner.class)
+public class InitializeRecoverableSecondaryKeyTaskTest {
+ @Mock private CryptoSettings mMockCryptoSettings;
+
+ private Application mApplication;
+ private InitializeRecoverableSecondaryKeyTask mTask;
+ private CryptoSettings mCryptoSettings;
+ private FakeCryptoBackupServer mFakeCryptoBackupServer;
+ private RecoverableKeyStoreSecondaryKeyManager mSecondaryKeyManager;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ ShadowRecoveryController.reset();
+
+ mApplication = ApplicationProvider.getApplicationContext();
+ mFakeCryptoBackupServer = new FakeCryptoBackupServer();
+ mCryptoSettings = CryptoSettings.getInstanceForTesting(mApplication);
+ mSecondaryKeyManager =
+ new RecoverableKeyStoreSecondaryKeyManager(
+ RecoveryController.getInstance(mApplication), new SecureRandom());
+
+ mTask =
+ new InitializeRecoverableSecondaryKeyTask(
+ mApplication, mCryptoSettings, mSecondaryKeyManager, mFakeCryptoBackupServer);
+ }
+
+ @Test
+ public void testRun_generatesNewKeyInRecoveryController() throws Exception {
+ RecoverableKeyStoreSecondaryKey key = mTask.run();
+
+ assertThat(RecoveryController.getInstance(mApplication).getAliases())
+ .contains(key.getAlias());
+ }
+
+ @Test
+ public void testRun_setsAliasOnServer() throws Exception {
+ RecoverableKeyStoreSecondaryKey key = mTask.run();
+
+ assertThat(mFakeCryptoBackupServer.getActiveSecondaryKeyAlias().get())
+ .isEqualTo(key.getAlias());
+ }
+
+ @Test
+ public void testRun_setsAliasInSettings() throws Exception {
+ RecoverableKeyStoreSecondaryKey key = mTask.run();
+
+ assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get()).isEqualTo(key.getAlias());
+ }
+
+ @Test
+ public void testRun_initializesSettings() throws Exception {
+ mTask.run();
+
+ assertThat(mCryptoSettings.getIsInitialized()).isTrue();
+ }
+
+ @Test
+ public void testRun_initializeSettingsFails_throws() throws Exception {
+ useMockCryptoSettings();
+ doThrow(IllegalArgumentException.class)
+ .when(mMockCryptoSettings)
+ .initializeWithKeyAlias(any());
+
+
+ assertThrows(IllegalArgumentException.class, () -> mTask.run());
+ }
+
+ @Test
+ public void testRun_doesNotGenerateANewKeyIfOneIsAvailable() throws Exception {
+ RecoverableKeyStoreSecondaryKey key1 = mTask.run();
+ RecoverableKeyStoreSecondaryKey key2 = mTask.run();
+
+ assertThat(key1.getAlias()).isEqualTo(key2.getAlias());
+ assertThat(key2.getSecretKey()).isEqualTo(key2.getSecretKey());
+ }
+
+ @Test
+ public void testRun_existingKeyButDestroyed_throws() throws Exception {
+ RecoverableKeyStoreSecondaryKey key = mTask.run();
+ ShadowRecoveryController.setRecoveryStatus(
+ key.getAlias(), RECOVERY_STATUS_PERMANENT_FAILURE);
+
+ assertThrows(InvalidKeyException.class, () -> mTask.run());
+ }
+
+ @Test
+ public void testRun_settingsInitializedButNotSecondaryKeyAlias_throws() {
+ useMockCryptoSettings();
+ when(mMockCryptoSettings.getIsInitialized()).thenReturn(true);
+ when(mMockCryptoSettings.getActiveSecondaryKeyAlias()).thenReturn(Optional.empty());
+
+ assertThrows(InvalidKeyException.class, () -> mTask.run());
+ }
+
+ @Test
+ public void testRun_keyAliasSetButNotInStore_throws() {
+ useMockCryptoSettings();
+ when(mMockCryptoSettings.getIsInitialized()).thenReturn(true);
+ when(mMockCryptoSettings.getActiveSecondaryKeyAlias())
+ .thenReturn(Optional.of("missingAlias"));
+
+ assertThrows(InvalidKeyException.class, () -> mTask.run());
+ }
+
+ private void useMockCryptoSettings() {
+ mTask =
+ new InitializeRecoverableSecondaryKeyTask(
+ mApplication,
+ mMockCryptoSettings,
+ mSecondaryKeyManager,
+ mFakeCryptoBackupServer);
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/KvBackupEncrypterTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/KvBackupEncrypterTest.java
new file mode 100644
index 000000000000..ccfbfa4b25e9
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/KvBackupEncrypterTest.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.app.backup.BackupDataInput;
+import android.platform.test.annotations.Presubmit;
+import android.util.Pair;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.chunking.ChunkHasher;
+import com.android.server.backup.encryption.chunking.EncryptedChunk;
+import com.android.server.backup.encryption.kv.KeyValueListingBuilder;
+import com.android.server.backup.encryption.protos.nano.KeyValueListingProto.KeyValueListing;
+import com.android.server.backup.encryption.protos.nano.KeyValuePairProto.KeyValuePair;
+import com.android.server.backup.encryption.tasks.BackupEncrypter.Result;
+import com.android.server.testing.shadows.DataEntity;
+import com.android.server.testing.shadows.ShadowBackupDataInput;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Ordering;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.security.MessageDigest;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+@Config(shadows = {ShadowBackupDataInput.class})
+public class KvBackupEncrypterTest {
+ private static final String KEY_ALGORITHM = "AES";
+ private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
+ private static final int GCM_TAG_LENGTH_BYTES = 16;
+
+ private static final byte[] TEST_TERTIARY_KEY = Arrays.copyOf(new byte[0], 256 / Byte.SIZE);
+ private static final String TEST_KEY_1 = "test_key_1";
+ private static final String TEST_KEY_2 = "test_key_2";
+ private static final String TEST_KEY_3 = "test_key_3";
+ private static final byte[] TEST_VALUE_1 = {10, 11, 12};
+ private static final byte[] TEST_VALUE_2 = {13, 14, 15};
+ private static final byte[] TEST_VALUE_2B = {13, 14, 15, 16};
+ private static final byte[] TEST_VALUE_3 = {16, 17, 18};
+
+ private SecretKey mSecretKey;
+ private ChunkHasher mChunkHasher;
+
+ @Before
+ public void setUp() {
+ mSecretKey = new SecretKeySpec(TEST_TERTIARY_KEY, KEY_ALGORITHM);
+ mChunkHasher = new ChunkHasher(mSecretKey);
+
+ ShadowBackupDataInput.reset();
+ }
+
+ private KvBackupEncrypter createEncrypter(KeyValueListing keyValueListing) {
+ KvBackupEncrypter encrypter = new KvBackupEncrypter(new BackupDataInput(null));
+ encrypter.setOldKeyValueListing(keyValueListing);
+ return encrypter;
+ }
+
+ @Test
+ public void backup_noExistingBackup_encryptsAllPairs() throws Exception {
+ ShadowBackupDataInput.addEntity(TEST_KEY_1, TEST_VALUE_1);
+ ShadowBackupDataInput.addEntity(TEST_KEY_2, TEST_VALUE_2);
+
+ KeyValueListing emptyKeyValueListing = new KeyValueListingBuilder().build();
+ ImmutableSet<ChunkHash> emptyExistingChunks = ImmutableSet.of();
+ KvBackupEncrypter encrypter = createEncrypter(emptyKeyValueListing);
+
+ Result result =
+ encrypter.backup(
+ mSecretKey, /*unusedFingerprintMixerSalt=*/ null, emptyExistingChunks);
+
+ assertThat(result.getAllChunks()).hasSize(2);
+ EncryptedChunk chunk1 = result.getNewChunks().get(0);
+ EncryptedChunk chunk2 = result.getNewChunks().get(1);
+ assertThat(chunk1.key()).isEqualTo(getChunkHash(TEST_KEY_1, TEST_VALUE_1));
+ KeyValuePair pair1 = decryptChunk(chunk1);
+ assertThat(pair1.key).isEqualTo(TEST_KEY_1);
+ assertThat(pair1.value).isEqualTo(TEST_VALUE_1);
+ assertThat(chunk2.key()).isEqualTo(getChunkHash(TEST_KEY_2, TEST_VALUE_2));
+ KeyValuePair pair2 = decryptChunk(chunk2);
+ assertThat(pair2.key).isEqualTo(TEST_KEY_2);
+ assertThat(pair2.value).isEqualTo(TEST_VALUE_2);
+ }
+
+ @Test
+ public void backup_existingBackup_encryptsNewAndUpdatedPairs() throws Exception {
+ Pair<KeyValueListing, Set<ChunkHash>> initialResult = runInitialBackupOfPairs1And2();
+
+ // Update key 2 and add the new key 3.
+ ShadowBackupDataInput.reset();
+ ShadowBackupDataInput.addEntity(TEST_KEY_2, TEST_VALUE_2B);
+ ShadowBackupDataInput.addEntity(TEST_KEY_3, TEST_VALUE_3);
+
+ KvBackupEncrypter encrypter = createEncrypter(initialResult.first);
+ BackupEncrypter.Result secondResult =
+ encrypter.backup(
+ mSecretKey, /*unusedFingerprintMixerSalt=*/ null, initialResult.second);
+
+ assertThat(secondResult.getAllChunks()).hasSize(3);
+ assertThat(secondResult.getNewChunks()).hasSize(2);
+ EncryptedChunk newChunk2 = secondResult.getNewChunks().get(0);
+ EncryptedChunk newChunk3 = secondResult.getNewChunks().get(1);
+ assertThat(newChunk2.key()).isEqualTo(getChunkHash(TEST_KEY_2, TEST_VALUE_2B));
+ assertThat(decryptChunk(newChunk2).value).isEqualTo(TEST_VALUE_2B);
+ assertThat(newChunk3.key()).isEqualTo(getChunkHash(TEST_KEY_3, TEST_VALUE_3));
+ assertThat(decryptChunk(newChunk3).value).isEqualTo(TEST_VALUE_3);
+ }
+
+ @Test
+ public void backup_allChunksContainsHashesOfAllChunks() throws Exception {
+ Pair<KeyValueListing, Set<ChunkHash>> initialResult = runInitialBackupOfPairs1And2();
+
+ ShadowBackupDataInput.reset();
+ ShadowBackupDataInput.addEntity(TEST_KEY_3, TEST_VALUE_3);
+
+ KvBackupEncrypter encrypter = createEncrypter(initialResult.first);
+ BackupEncrypter.Result secondResult =
+ encrypter.backup(
+ mSecretKey, /*unusedFingerprintMixerSalt=*/ null, initialResult.second);
+
+ assertThat(secondResult.getAllChunks())
+ .containsExactly(
+ getChunkHash(TEST_KEY_1, TEST_VALUE_1),
+ getChunkHash(TEST_KEY_2, TEST_VALUE_2),
+ getChunkHash(TEST_KEY_3, TEST_VALUE_3));
+ }
+
+ @Test
+ public void backup_negativeSize_deletesKeyFromExistingBackup() throws Exception {
+ Pair<KeyValueListing, Set<ChunkHash>> initialResult = runInitialBackupOfPairs1And2();
+
+ ShadowBackupDataInput.reset();
+ ShadowBackupDataInput.addEntity(new DataEntity(TEST_KEY_2));
+
+ KvBackupEncrypter encrypter = createEncrypter(initialResult.first);
+ Result secondResult =
+ encrypter.backup(
+ mSecretKey, /*unusedFingerprintMixerSalt=*/ null, initialResult.second);
+
+ assertThat(secondResult.getAllChunks())
+ .containsExactly(getChunkHash(TEST_KEY_1, TEST_VALUE_1));
+ assertThat(secondResult.getNewChunks()).isEmpty();
+ }
+
+ @Test
+ public void backup_returnsMessageDigestOverChunkHashes() throws Exception {
+ Pair<KeyValueListing, Set<ChunkHash>> initialResult = runInitialBackupOfPairs1And2();
+
+ ShadowBackupDataInput.reset();
+ ShadowBackupDataInput.addEntity(TEST_KEY_3, TEST_VALUE_3);
+
+ KvBackupEncrypter encrypter = createEncrypter(initialResult.first);
+ Result secondResult =
+ encrypter.backup(
+ mSecretKey, /*unusedFingerprintMixerSalt=*/ null, initialResult.second);
+
+ MessageDigest messageDigest =
+ MessageDigest.getInstance(BackupEncrypter.MESSAGE_DIGEST_ALGORITHM);
+ ImmutableList<ChunkHash> sortedHashes =
+ Ordering.natural()
+ .immutableSortedCopy(
+ ImmutableList.of(
+ getChunkHash(TEST_KEY_1, TEST_VALUE_1),
+ getChunkHash(TEST_KEY_2, TEST_VALUE_2),
+ getChunkHash(TEST_KEY_3, TEST_VALUE_3)));
+ messageDigest.update(sortedHashes.get(0).getHash());
+ messageDigest.update(sortedHashes.get(1).getHash());
+ messageDigest.update(sortedHashes.get(2).getHash());
+ assertThat(secondResult.getDigest()).isEqualTo(messageDigest.digest());
+ }
+
+ @Test
+ public void getNewKeyValueListing_noExistingBackup_returnsCorrectListing() throws Exception {
+ KeyValueListing keyValueListing = runInitialBackupOfPairs1And2().first;
+
+ assertThat(keyValueListing.entries.length).isEqualTo(2);
+ assertThat(keyValueListing.entries[0].key).isEqualTo(TEST_KEY_1);
+ assertThat(keyValueListing.entries[0].hash)
+ .isEqualTo(getChunkHash(TEST_KEY_1, TEST_VALUE_1).getHash());
+ assertThat(keyValueListing.entries[1].key).isEqualTo(TEST_KEY_2);
+ assertThat(keyValueListing.entries[1].hash)
+ .isEqualTo(getChunkHash(TEST_KEY_2, TEST_VALUE_2).getHash());
+ }
+
+ @Test
+ public void getNewKeyValueListing_existingBackup_returnsCorrectListing() throws Exception {
+ Pair<KeyValueListing, Set<ChunkHash>> initialResult = runInitialBackupOfPairs1And2();
+
+ ShadowBackupDataInput.reset();
+ ShadowBackupDataInput.addEntity(TEST_KEY_2, TEST_VALUE_2B);
+ ShadowBackupDataInput.addEntity(TEST_KEY_3, TEST_VALUE_3);
+
+ KvBackupEncrypter encrypter = createEncrypter(initialResult.first);
+ encrypter.backup(mSecretKey, /*unusedFingerprintMixerSalt=*/ null, initialResult.second);
+
+ ImmutableMap<String, ChunkHash> keyValueListing =
+ listingToMap(encrypter.getNewKeyValueListing());
+ assertThat(keyValueListing).hasSize(3);
+ assertThat(keyValueListing)
+ .containsEntry(TEST_KEY_1, getChunkHash(TEST_KEY_1, TEST_VALUE_1));
+ assertThat(keyValueListing)
+ .containsEntry(TEST_KEY_2, getChunkHash(TEST_KEY_2, TEST_VALUE_2B));
+ assertThat(keyValueListing)
+ .containsEntry(TEST_KEY_3, getChunkHash(TEST_KEY_3, TEST_VALUE_3));
+ }
+
+ @Test
+ public void getNewKeyValueChunkListing_beforeBackup_throws() throws Exception {
+ KvBackupEncrypter encrypter = createEncrypter(new KeyValueListing());
+ assertThrows(IllegalStateException.class, encrypter::getNewKeyValueListing);
+ }
+
+ private ImmutableMap<String, ChunkHash> listingToMap(KeyValueListing listing) {
+ // We can't use the ImmutableMap collector directly because it isn't supported in Android
+ // guava.
+ return ImmutableMap.copyOf(
+ Arrays.stream(listing.entries)
+ .collect(
+ Collectors.toMap(
+ entry -> entry.key, entry -> new ChunkHash(entry.hash))));
+ }
+
+ private Pair<KeyValueListing, Set<ChunkHash>> runInitialBackupOfPairs1And2() throws Exception {
+ ShadowBackupDataInput.addEntity(TEST_KEY_1, TEST_VALUE_1);
+ ShadowBackupDataInput.addEntity(TEST_KEY_2, TEST_VALUE_2);
+
+ KeyValueListing initialKeyValueListing = new KeyValueListingBuilder().build();
+ ImmutableSet<ChunkHash> initialExistingChunks = ImmutableSet.of();
+ KvBackupEncrypter encrypter = createEncrypter(initialKeyValueListing);
+ Result firstResult =
+ encrypter.backup(
+ mSecretKey, /*unusedFingerprintMixerSalt=*/ null, initialExistingChunks);
+
+ return Pair.create(
+ encrypter.getNewKeyValueListing(), ImmutableSet.copyOf(firstResult.getAllChunks()));
+ }
+
+ private ChunkHash getChunkHash(String key, byte[] value) throws Exception {
+ KeyValuePair pair = new KeyValuePair();
+ pair.key = key;
+ pair.value = Arrays.copyOf(value, value.length);
+ return mChunkHasher.computeHash(KeyValuePair.toByteArray(pair));
+ }
+
+ private KeyValuePair decryptChunk(EncryptedChunk encryptedChunk) throws Exception {
+ Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
+ cipher.init(
+ Cipher.DECRYPT_MODE,
+ mSecretKey,
+ new GCMParameterSpec(GCM_TAG_LENGTH_BYTES * Byte.SIZE, encryptedChunk.nonce()));
+ byte[] decryptedBytes = cipher.doFinal(encryptedChunk.encryptedBytes());
+ return KeyValuePair.parseFrom(decryptedBytes);
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTaskTest.java
new file mode 100644
index 000000000000..cda73174a3e2
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTaskTest.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.tasks;
+
+import static com.android.server.backup.testing.CryptoTestUtils.generateAesKey;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertFalse;
+
+import android.app.Application;
+import android.platform.test.annotations.Presubmit;
+import android.security.keystore.recovery.RecoveryController;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.server.backup.encryption.CryptoSettings;
+import com.android.server.backup.encryption.keys.KeyWrapUtils;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKey;
+import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
+import com.android.server.backup.encryption.keys.TertiaryKeyStore;
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+import com.android.server.testing.fakes.FakeCryptoBackupServer;
+import com.android.server.testing.shadows.ShadowRecoveryController;
+
+import java.security.SecureRandom;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.crypto.SecretKey;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+@Config(shadows = {ShadowRecoveryController.class, ShadowRecoveryController.class})
+public class RotateSecondaryKeyTaskTest {
+ private static final String APP_1 = "app1";
+ private static final String APP_2 = "app2";
+ private static final String APP_3 = "app3";
+
+ private static final String CURRENT_SECONDARY_KEY_ALIAS =
+ "recoverablekey.alias/d524796bd07de3c2225c63d434eff698";
+ private static final String NEXT_SECONDARY_KEY_ALIAS =
+ "recoverablekey.alias/6c6d198a7f12e662b6bc45f4849db170";
+
+ private Application mApplication;
+ private RotateSecondaryKeyTask mTask;
+ private RecoveryController mRecoveryController;
+ private FakeCryptoBackupServer mBackupServer;
+ private CryptoSettings mCryptoSettings;
+ private Map<String, SecretKey> mTertiaryKeysByPackageName;
+ private RecoverableKeyStoreSecondaryKeyManager mRecoverableSecondaryKeyManager;
+
+ @Before
+ public void setUp() throws Exception {
+ mApplication = ApplicationProvider.getApplicationContext();
+
+ mTertiaryKeysByPackageName = new HashMap<>();
+ mTertiaryKeysByPackageName.put(APP_1, generateAesKey());
+ mTertiaryKeysByPackageName.put(APP_2, generateAesKey());
+ mTertiaryKeysByPackageName.put(APP_3, generateAesKey());
+
+ mRecoveryController = RecoveryController.getInstance(mApplication);
+ mRecoverableSecondaryKeyManager =
+ new RecoverableKeyStoreSecondaryKeyManager(
+ RecoveryController.getInstance(mApplication), new SecureRandom());
+ mBackupServer = new FakeCryptoBackupServer();
+ mCryptoSettings = CryptoSettings.getInstanceForTesting(mApplication);
+ addNextSecondaryKeyToRecoveryController();
+ mCryptoSettings.setNextSecondaryAlias(NEXT_SECONDARY_KEY_ALIAS);
+
+ mTask =
+ new RotateSecondaryKeyTask(
+ mApplication,
+ mRecoverableSecondaryKeyManager,
+ mBackupServer,
+ mCryptoSettings,
+ mRecoveryController);
+
+ ShadowRecoveryController.reset();
+ }
+
+ @Test
+ public void run_failsIfThereIsNoActiveSecondaryKey() throws Exception {
+ setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
+ addCurrentSecondaryKeyToRecoveryController();
+ mBackupServer.setActiveSecondaryKeyAlias(
+ CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+
+ mTask.run();
+
+ assertFalse(mCryptoSettings.getActiveSecondaryKeyAlias().isPresent());
+ }
+
+ @Test
+ public void run_failsIfActiveSecondaryIsNotInRecoveryController() throws Exception {
+ setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
+ // Have to add it first as otherwise CryptoSettings throws an exception when trying to set
+ // it
+ addCurrentSecondaryKeyToRecoveryController();
+ mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+ mBackupServer.setActiveSecondaryKeyAlias(
+ CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+
+ mTask.run();
+
+ assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get())
+ .isEqualTo(CURRENT_SECONDARY_KEY_ALIAS);
+ }
+
+ @Test
+ public void run_doesNothingIfFlagIsDisabled() throws Exception {
+ setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
+ addCurrentSecondaryKeyToRecoveryController();
+ mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+ mBackupServer.setActiveSecondaryKeyAlias(
+ CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+ addWrappedTertiaries();
+
+ mTask.run();
+
+ assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get())
+ .isEqualTo(CURRENT_SECONDARY_KEY_ALIAS);
+ }
+
+ @Test
+ public void run_setsActiveSecondary() throws Exception {
+ addNextSecondaryKeyToRecoveryController();
+ setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
+ addCurrentSecondaryKeyToRecoveryController();
+ mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+ mBackupServer.setActiveSecondaryKeyAlias(
+ CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+ addWrappedTertiaries();
+
+ mTask.run();
+
+ assertThat(mBackupServer.getActiveSecondaryKeyAlias().get())
+ .isEqualTo(NEXT_SECONDARY_KEY_ALIAS);
+ }
+
+ @Test
+ public void run_rewrapsExistingTertiaryKeys() throws Exception {
+ addNextSecondaryKeyToRecoveryController();
+ setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
+ addCurrentSecondaryKeyToRecoveryController();
+ mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+ mBackupServer.setActiveSecondaryKeyAlias(
+ CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+ addWrappedTertiaries();
+
+ mTask.run();
+
+ Map<String, WrappedKeyProto.WrappedKey> rewrappedKeys =
+ mBackupServer.getAllTertiaryKeys(NEXT_SECONDARY_KEY_ALIAS);
+ SecretKey secondaryKey = (SecretKey) mRecoveryController.getKey(NEXT_SECONDARY_KEY_ALIAS);
+ for (String packageName : mTertiaryKeysByPackageName.keySet()) {
+ WrappedKeyProto.WrappedKey rewrappedKey = rewrappedKeys.get(packageName);
+ assertThat(KeyWrapUtils.unwrap(secondaryKey, rewrappedKey))
+ .isEqualTo(mTertiaryKeysByPackageName.get(packageName));
+ }
+ }
+
+ @Test
+ public void run_persistsRewrappedKeysToDisk() throws Exception {
+ addNextSecondaryKeyToRecoveryController();
+ setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
+ addCurrentSecondaryKeyToRecoveryController();
+ mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+ mBackupServer.setActiveSecondaryKeyAlias(
+ CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+ addWrappedTertiaries();
+
+ mTask.run();
+
+ RecoverableKeyStoreSecondaryKey secondaryKey = getRecoverableKey(NEXT_SECONDARY_KEY_ALIAS);
+ Map<String, SecretKey> keys =
+ TertiaryKeyStore.newInstance(mApplication, secondaryKey).getAll();
+ for (String packageName : mTertiaryKeysByPackageName.keySet()) {
+ SecretKey tertiaryKey = mTertiaryKeysByPackageName.get(packageName);
+ SecretKey newlyWrappedKey = keys.get(packageName);
+ assertThat(tertiaryKey.getEncoded()).isEqualTo(newlyWrappedKey.getEncoded());
+ }
+ }
+
+ @Test
+ public void run_stillSetsActiveSecondaryIfNoTertiaries() throws Exception {
+ addNextSecondaryKeyToRecoveryController();
+ setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
+ addCurrentSecondaryKeyToRecoveryController();
+ mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+ mBackupServer.setActiveSecondaryKeyAlias(
+ CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+
+ mTask.run();
+
+ assertThat(mBackupServer.getActiveSecondaryKeyAlias().get())
+ .isEqualTo(NEXT_SECONDARY_KEY_ALIAS);
+ }
+
+ @Test
+ public void run_setsActiveSecondaryKeyAliasInSettings() throws Exception {
+ addNextSecondaryKeyToRecoveryController();
+ setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
+ addCurrentSecondaryKeyToRecoveryController();
+ mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+ mBackupServer.setActiveSecondaryKeyAlias(
+ CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+
+ mTask.run();
+
+ assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get())
+ .isEqualTo(NEXT_SECONDARY_KEY_ALIAS);
+ }
+
+ @Test
+ public void run_removesNextSecondaryKeyAliasInSettings() throws Exception {
+ addNextSecondaryKeyToRecoveryController();
+ setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
+ addCurrentSecondaryKeyToRecoveryController();
+ mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+ mBackupServer.setActiveSecondaryKeyAlias(
+ CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+
+ mTask.run();
+
+ assertFalse(mCryptoSettings.getNextSecondaryKeyAlias().isPresent());
+ }
+
+ @Test
+ public void run_deletesOldKeyFromRecoverableKeyStoreLoader() throws Exception {
+ addNextSecondaryKeyToRecoveryController();
+ setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNCED);
+ addCurrentSecondaryKeyToRecoveryController();
+ mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+ mBackupServer.setActiveSecondaryKeyAlias(
+ CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+
+ mTask.run();
+
+ assertThat(mRecoveryController.getKey(CURRENT_SECONDARY_KEY_ALIAS)).isNull();
+ }
+
+ @Test
+ public void run_doesNotRotateIfNoNextAlias() throws Exception {
+ addCurrentSecondaryKeyToRecoveryController();
+ mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+ mBackupServer.setActiveSecondaryKeyAlias(
+ CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+ mCryptoSettings.removeNextSecondaryKeyAlias();
+
+ mTask.run();
+
+ assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get())
+ .isEqualTo(CURRENT_SECONDARY_KEY_ALIAS);
+ assertFalse(mCryptoSettings.getNextSecondaryKeyAlias().isPresent());
+ }
+
+ @Test
+ public void run_doesNotRotateIfKeyIsNotSyncedYet() throws Exception {
+ addNextSecondaryKeyToRecoveryController();
+ setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
+ addCurrentSecondaryKeyToRecoveryController();
+ mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+ mBackupServer.setActiveSecondaryKeyAlias(
+ CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+
+ mTask.run();
+
+ assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get())
+ .isEqualTo(CURRENT_SECONDARY_KEY_ALIAS);
+ }
+
+ @Test
+ public void run_doesNotClearNextKeyIfSyncIsJustPending() throws Exception {
+ addNextSecondaryKeyToRecoveryController();
+ setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
+ addCurrentSecondaryKeyToRecoveryController();
+ mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+ mBackupServer.setActiveSecondaryKeyAlias(
+ CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+
+ mTask.run();
+
+ assertThat(mCryptoSettings.getNextSecondaryKeyAlias().get())
+ .isEqualTo(NEXT_SECONDARY_KEY_ALIAS);
+ }
+
+ @Test
+ public void run_doesNotRotateIfPermanentFailure() throws Exception {
+ addNextSecondaryKeyToRecoveryController();
+ setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE);
+ addCurrentSecondaryKeyToRecoveryController();
+ mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+ mBackupServer.setActiveSecondaryKeyAlias(
+ CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+
+ mTask.run();
+
+ assertThat(mCryptoSettings.getActiveSecondaryKeyAlias().get())
+ .isEqualTo(CURRENT_SECONDARY_KEY_ALIAS);
+ }
+
+ @Test
+ public void run_removesNextKeyIfPermanentFailure() throws Exception {
+ addNextSecondaryKeyToRecoveryController();
+ setNextKeyRecoveryStatus(RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE);
+ addCurrentSecondaryKeyToRecoveryController();
+ mCryptoSettings.setActiveSecondaryKeyAlias(CURRENT_SECONDARY_KEY_ALIAS);
+ mBackupServer.setActiveSecondaryKeyAlias(
+ CURRENT_SECONDARY_KEY_ALIAS, Collections.emptyMap());
+
+ mTask.run();
+
+ assertFalse(mCryptoSettings.getNextSecondaryKeyAlias().isPresent());
+ }
+
+ private void setNextKeyRecoveryStatus(int status) throws Exception {
+ mRecoveryController.setRecoveryStatus(NEXT_SECONDARY_KEY_ALIAS, status);
+ }
+
+ private void addCurrentSecondaryKeyToRecoveryController() throws Exception {
+ mRecoveryController.generateKey(CURRENT_SECONDARY_KEY_ALIAS);
+ }
+
+ private void addNextSecondaryKeyToRecoveryController() throws Exception {
+ mRecoveryController.generateKey(NEXT_SECONDARY_KEY_ALIAS);
+ }
+
+ private void addWrappedTertiaries() throws Exception {
+ TertiaryKeyStore tertiaryKeyStore =
+ TertiaryKeyStore.newInstance(
+ mApplication, getRecoverableKey(CURRENT_SECONDARY_KEY_ALIAS));
+
+ for (String packageName : mTertiaryKeysByPackageName.keySet()) {
+ tertiaryKeyStore.save(packageName, mTertiaryKeysByPackageName.get(packageName));
+ }
+ }
+
+ private RecoverableKeyStoreSecondaryKey getRecoverableKey(String alias) throws Exception {
+ return mRecoverableSecondaryKeyManager.get(alias).get();
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/DiffScriptProcessor.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/DiffScriptProcessor.java
new file mode 100644
index 000000000000..faddb6cf129c
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/DiffScriptProcessor.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.testing;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.util.Locale;
+import java.util.Optional;
+import java.util.Scanner;
+import java.util.regex.Pattern;
+
+/**
+ * To be used as part of a fake backup server. Processes a Scotty diff script.
+ *
+ * <p>A Scotty diff script consists of an ASCII line denoting a command, optionally followed by a
+ * range of bytes. Command format is either
+ *
+ * <ul>
+ * <li>A single 64-bit integer, followed by a new line: this denotes that the given number of
+ * bytes are to follow in the stream. These bytes should be written directly to the new file.
+ * <li>Two 64-bit integers, separated by a hyphen, followed by a new line: this says that the
+ * given range of bytes from the original file ought to be copied into the new file.
+ * </ul>
+ */
+public class DiffScriptProcessor {
+
+ private static final int COPY_BUFFER_SIZE = 1024;
+
+ private static final String READ_MODE = "r";
+ private static final Pattern VALID_COMMAND_PATTERN = Pattern.compile("^\\d+(-\\d+)?$");
+
+ private final File mInput;
+ private final File mOutput;
+ private final long mInputLength;
+
+ /**
+ * A new instance, with {@code input} as previous file, and {@code output} as new file.
+ *
+ * @param input Previous file from which ranges of bytes are to be copied. This file should be
+ * immutable.
+ * @param output Output file, to which the new data should be written.
+ * @throws IllegalArgumentException if input does not exist.
+ */
+ public DiffScriptProcessor(File input, File output) {
+ checkArgument(input.exists(), "input file did not exist.");
+ mInput = input;
+ mInputLength = input.length();
+ mOutput = checkNotNull(output);
+ }
+
+ public void process(InputStream diffScript) throws IOException, MalformedDiffScriptException {
+ RandomAccessFile randomAccessInput = new RandomAccessFile(mInput, READ_MODE);
+
+ try (FileOutputStream outputStream = new FileOutputStream(mOutput)) {
+ while (true) {
+ Optional<String> commandString = readCommand(diffScript);
+ if (!commandString.isPresent()) {
+ return;
+ }
+ Command command = Command.parse(commandString.get());
+
+ if (command.mIsRange) {
+ checkFileRange(command.mCount, command.mLimit);
+ copyRange(randomAccessInput, outputStream, command.mCount, command.mLimit);
+ } else {
+ long bytesCopied = copyBytes(diffScript, outputStream, command.mCount);
+ if (bytesCopied < command.mCount) {
+ throw new MalformedDiffScriptException(
+ String.format(
+ Locale.US,
+ "Command to copy %d bytes from diff script, but only %d"
+ + " bytes available",
+ command.mCount,
+ bytesCopied));
+ }
+ if (diffScript.read() != '\n') {
+ throw new MalformedDiffScriptException("Expected new line after bytes.");
+ }
+ }
+ }
+ }
+ }
+
+ private void checkFileRange(long start, long end) throws MalformedDiffScriptException {
+ if (end < start) {
+ throw new MalformedDiffScriptException(
+ String.format(
+ Locale.US,
+ "Command to copy %d-%d bytes from original file, but %2$d < %1$d.",
+ start,
+ end));
+ }
+
+ if (end >= mInputLength) {
+ throw new MalformedDiffScriptException(
+ String.format(
+ Locale.US,
+ "Command to copy %d-%d bytes from original file, but file is only %d"
+ + " bytes long.",
+ start,
+ end,
+ mInputLength));
+ }
+ }
+
+ /**
+ * Reads a command from the input stream.
+ *
+ * @param inputStream The input.
+ * @return Optional of command, or empty if EOF.
+ */
+ private static Optional<String> readCommand(InputStream inputStream) throws IOException {
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+
+ int b;
+ while (!isEndOfCommand(b = inputStream.read())) {
+ byteArrayOutputStream.write(b);
+ }
+
+ byte[] bytes = byteArrayOutputStream.toByteArray();
+ if (bytes.length == 0) {
+ return Optional.empty();
+ } else {
+ return Optional.of(new String(bytes, UTF_8));
+ }
+ }
+
+ /**
+ * If the given output from {@link InputStream#read()} is the end of a command - i.e., a new
+ * line or the EOF.
+ *
+ * @param b The byte or -1.
+ * @return {@code true} if ends the command.
+ */
+ private static boolean isEndOfCommand(int b) {
+ return b == -1 || b == '\n';
+ }
+
+ /**
+ * Copies {@code n} bytes from {@code inputStream} to {@code outputStream}.
+ *
+ * @return The number of bytes copied.
+ * @throws IOException if there was a problem reading or writing.
+ */
+ private static long copyBytes(InputStream inputStream, OutputStream outputStream, long n)
+ throws IOException {
+ byte[] buffer = new byte[COPY_BUFFER_SIZE];
+ long copied = 0;
+ while (n - copied > COPY_BUFFER_SIZE) {
+ long read = copyBlock(inputStream, outputStream, buffer, COPY_BUFFER_SIZE);
+ if (read <= 0) {
+ return copied;
+ }
+ }
+ while (n - copied > 0) {
+ copied += copyBlock(inputStream, outputStream, buffer, (int) (n - copied));
+ }
+ return copied;
+ }
+
+ private static long copyBlock(
+ InputStream inputStream, OutputStream outputStream, byte[] buffer, int size)
+ throws IOException {
+ int read = inputStream.read(buffer, 0, size);
+ outputStream.write(buffer, 0, read);
+ return read;
+ }
+
+ /**
+ * Copies the given range of bytes from the input file to the output stream.
+ *
+ * @param input The input file.
+ * @param output The output stream.
+ * @param start Start position in the input file.
+ * @param end End position in the output file (inclusive).
+ * @throws IOException if there was a problem reading or writing.
+ */
+ private static void copyRange(RandomAccessFile input, OutputStream output, long start, long end)
+ throws IOException {
+ input.seek(start);
+
+ // Inefficient but obviously correct. If tests become slow, optimize.
+ for (; start <= end; start++) {
+ output.write(input.read());
+ }
+ }
+
+ /** Error thrown for a malformed diff script. */
+ public static class MalformedDiffScriptException extends Exception {
+ public MalformedDiffScriptException(String message) {
+ super(message);
+ }
+ }
+
+ /**
+ * A command telling the processor either to insert n bytes, which follow, or copy n-m bytes
+ * from the original file.
+ */
+ private static class Command {
+ private final long mCount;
+ private final long mLimit;
+ private final boolean mIsRange;
+
+ private Command(long count, long limit, boolean isRange) {
+ mCount = count;
+ mLimit = limit;
+ mIsRange = isRange;
+ }
+
+ /**
+ * Attempts to parse the command string into a usable structure.
+ *
+ * @param command The command string, without a new line at the end.
+ * @throws MalformedDiffScriptException if the command is not a valid diff script command.
+ * @return The parsed command.
+ */
+ private static Command parse(String command) throws MalformedDiffScriptException {
+ if (!VALID_COMMAND_PATTERN.matcher(command).matches()) {
+ throw new MalformedDiffScriptException("Bad command: " + command);
+ }
+
+ Scanner commandScanner = new Scanner(command);
+ commandScanner.useDelimiter("-");
+ long n = commandScanner.nextLong();
+ if (!commandScanner.hasNextLong()) {
+ return new Command(n, 0L, /*isRange=*/ false);
+ }
+ long m = commandScanner.nextLong();
+ return new Command(n, m, /*isRange=*/ true);
+ }
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/QueuingNonAutomaticExecutorService.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/QueuingNonAutomaticExecutorService.java
new file mode 100644
index 000000000000..9d2272e29945
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/QueuingNonAutomaticExecutorService.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.testing;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.AbstractExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * ExecutorService which needs to be stepped through the jobs in its' queue.
+ *
+ * <p>This is a deliberately simple implementation because it's only used in testing. The queued
+ * jobs are run on the main thread to eliminate any race condition bugs.
+ */
+public class QueuingNonAutomaticExecutorService extends AbstractExecutorService {
+
+ private List<Runnable> mWaitingJobs = new ArrayList<>();
+ private int mWaitingJobCount = 0;
+
+ @Override
+ public void shutdown() {
+ mWaitingJobCount = mWaitingJobs.size();
+ mWaitingJobs = null; // This will force an error if jobs are submitted after shutdown
+ }
+
+ @Override
+ public List<Runnable> shutdownNow() {
+ List<Runnable> queuedJobs = mWaitingJobs;
+ shutdown();
+ return queuedJobs;
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return mWaitingJobs == null;
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return mWaitingJobs == null && mWaitingJobCount == 0;
+ }
+
+ @Override
+ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+ long expiry = System.currentTimeMillis() + unit.toMillis(timeout);
+ for (Runnable job : mWaitingJobs) {
+ if (System.currentTimeMillis() > expiry) {
+ return false;
+ }
+
+ job.run();
+ }
+ return true;
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ mWaitingJobs.add(command);
+ }
+
+ public void runNext() {
+ if (mWaitingJobs.isEmpty()) {
+ throw new IllegalStateException("Attempted to run jobs on an empty paused executor");
+ }
+
+ mWaitingJobs.remove(0).run();
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/CryptoTestUtils.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/CryptoTestUtils.java
index 3f3494d2c22c..b0c02ba637e0 100644
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/CryptoTestUtils.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/CryptoTestUtils.java
@@ -16,7 +16,17 @@
package com.android.server.backup.testing;
+import android.util.Pair;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
+import com.android.server.backup.encryption.protos.nano.KeyValuePairProto;
+
+import java.nio.charset.Charset;
import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Random;
import javax.crypto.KeyGenerator;
@@ -42,4 +52,126 @@ public class CryptoTestUtils {
random.nextBytes(bytes);
return bytes;
}
+
+ public static ChunksMetadataProto.Chunk newChunk(ChunkHash hash, int length) {
+ return newChunk(hash.getHash(), length);
+ }
+
+ public static ChunksMetadataProto.Chunk newChunk(byte[] hash, int length) {
+ ChunksMetadataProto.Chunk newChunk = new ChunksMetadataProto.Chunk();
+ newChunk.hash = Arrays.copyOf(hash, hash.length);
+ newChunk.length = length;
+ return newChunk;
+ }
+
+ public static ChunksMetadataProto.ChunkListing newChunkListing(
+ String docId,
+ byte[] fingerprintSalt,
+ int cipherType,
+ int orderingType,
+ ChunksMetadataProto.Chunk... chunks) {
+ ChunksMetadataProto.ChunkListing chunkListing =
+ newChunkListingWithoutDocId(fingerprintSalt, cipherType, orderingType, chunks);
+ chunkListing.documentId = docId;
+ return chunkListing;
+ }
+
+ public static ChunksMetadataProto.ChunkListing newChunkListingWithoutDocId(
+ byte[] fingerprintSalt,
+ int cipherType,
+ int orderingType,
+ ChunksMetadataProto.Chunk... chunks) {
+ ChunksMetadataProto.ChunkListing chunkListing = new ChunksMetadataProto.ChunkListing();
+ chunkListing.fingerprintMixerSalt =
+ fingerprintSalt == null
+ ? null
+ : Arrays.copyOf(fingerprintSalt, fingerprintSalt.length);
+ chunkListing.cipherType = cipherType;
+ chunkListing.chunkOrderingType = orderingType;
+ chunkListing.chunks = chunks;
+ return chunkListing;
+ }
+
+ public static ChunksMetadataProto.ChunkOrdering newChunkOrdering(
+ int[] starts, byte[] checksum) {
+ ChunksMetadataProto.ChunkOrdering chunkOrdering = new ChunksMetadataProto.ChunkOrdering();
+ chunkOrdering.starts = starts == null ? null : Arrays.copyOf(starts, starts.length);
+ chunkOrdering.checksum =
+ checksum == null ? checksum : Arrays.copyOf(checksum, checksum.length);
+ return chunkOrdering;
+ }
+
+ public static ChunksMetadataProto.ChunksMetadata newChunksMetadata(
+ int cipherType, int checksumType, int chunkOrderingType, byte[] chunkOrdering) {
+ ChunksMetadataProto.ChunksMetadata metadata = new ChunksMetadataProto.ChunksMetadata();
+ metadata.cipherType = cipherType;
+ metadata.checksumType = checksumType;
+ metadata.chunkOrdering = Arrays.copyOf(chunkOrdering, chunkOrdering.length);
+ metadata.chunkOrderingType = chunkOrderingType;
+ return metadata;
+ }
+
+ public static KeyValuePairProto.KeyValuePair newPair(String key, String value) {
+ return newPair(key, value.getBytes(Charset.forName("UTF-8")));
+ }
+
+ public static KeyValuePairProto.KeyValuePair newPair(String key, byte[] value) {
+ KeyValuePairProto.KeyValuePair newPair = new KeyValuePairProto.KeyValuePair();
+ newPair.key = key;
+ newPair.value = value;
+ return newPair;
+ }
+
+ public static ChunksMetadataProto.ChunkListing clone(
+ ChunksMetadataProto.ChunkListing original) {
+ ChunksMetadataProto.Chunk[] clonedChunks;
+ if (original.chunks == null) {
+ clonedChunks = null;
+ } else {
+ clonedChunks = new ChunksMetadataProto.Chunk[original.chunks.length];
+ for (int i = 0; i < original.chunks.length; i++) {
+ clonedChunks[i] = clone(original.chunks[i]);
+ }
+ }
+
+ return newChunkListing(
+ original.documentId,
+ original.fingerprintMixerSalt,
+ original.cipherType,
+ original.chunkOrderingType,
+ clonedChunks);
+ }
+
+ public static ChunksMetadataProto.Chunk clone(ChunksMetadataProto.Chunk original) {
+ return newChunk(original.hash, original.length);
+ }
+
+ public static ChunksMetadataProto.ChunksMetadata clone(
+ ChunksMetadataProto.ChunksMetadata original) {
+ ChunksMetadataProto.ChunksMetadata cloneMetadata = new ChunksMetadataProto.ChunksMetadata();
+ cloneMetadata.chunkOrderingType = original.chunkOrderingType;
+ cloneMetadata.chunkOrdering =
+ original.chunkOrdering == null
+ ? null
+ : Arrays.copyOf(original.chunkOrdering, original.chunkOrdering.length);
+ cloneMetadata.checksumType = original.checksumType;
+ cloneMetadata.cipherType = original.cipherType;
+ return cloneMetadata;
+ }
+
+ public static ChunksMetadataProto.ChunkOrdering clone(
+ ChunksMetadataProto.ChunkOrdering original) {
+ ChunksMetadataProto.ChunkOrdering clone = new ChunksMetadataProto.ChunkOrdering();
+ clone.starts = Arrays.copyOf(original.starts, original.starts.length);
+ clone.checksum = Arrays.copyOf(original.checksum, original.checksum.length);
+ return clone;
+ }
+
+ public static <K, V> Map<K, V> mapOf(Pair<K, V>... pairs) {
+ Map<K, V> map = new HashMap<>();
+ for (Pair<K, V> pair : pairs) {
+ map.put(pair.first, pair.second);
+ }
+ return map;
+ }
}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/TestFileUtils.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/TestFileUtils.java
new file mode 100644
index 000000000000..e5d73ba41902
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/TestFileUtils.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.google.common.io.ByteStreams;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+/** Utility methods for use in tests */
+public class TestFileUtils {
+ /** Read the contents of a file into a byte array */
+ public static byte[] toByteArray(File file) throws IOException {
+ try (FileInputStream fis = new FileInputStream(file)) {
+ return ByteStreams.toByteArray(fis);
+ }
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/fakes/FakeCryptoBackupServer.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/fakes/FakeCryptoBackupServer.java
new file mode 100644
index 000000000000..332906033b6d
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/fakes/FakeCryptoBackupServer.java
@@ -0,0 +1,90 @@
+/*
+ * 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.testing.fakes;
+
+import android.annotation.Nullable;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.backup.encryption.client.CryptoBackupServer;
+import com.android.server.backup.encryption.client.UnexpectedActiveSecondaryOnServerException;
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+
+/** Fake {@link CryptoBackupServer}, for tests. Stores tertiary keys in memory. */
+public class FakeCryptoBackupServer implements CryptoBackupServer {
+ @GuardedBy("this")
+ @Nullable
+ private String mActiveSecondaryKeyAlias;
+
+ // Secondary key alias -> (package name -> tertiary key)
+ @GuardedBy("this")
+ private Map<String, Map<String, WrappedKeyProto.WrappedKey>> mWrappedKeyStore = new HashMap<>();
+
+ @Override
+ public String uploadIncrementalBackup(
+ String packageName,
+ String oldDocId,
+ byte[] diffScript,
+ WrappedKeyProto.WrappedKey tertiaryKey) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String uploadNonIncrementalBackup(
+ String packageName, byte[] data, WrappedKeyProto.WrappedKey tertiaryKey) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public synchronized void setActiveSecondaryKeyAlias(
+ String keyAlias, Map<String, WrappedKeyProto.WrappedKey> tertiaryKeys) {
+ mActiveSecondaryKeyAlias = keyAlias;
+
+ mWrappedKeyStore.putIfAbsent(keyAlias, new HashMap<>());
+ Map<String, WrappedKeyProto.WrappedKey> keyStore = mWrappedKeyStore.get(keyAlias);
+
+ for (String packageName : tertiaryKeys.keySet()) {
+ keyStore.put(packageName, tertiaryKeys.get(packageName));
+ }
+ }
+
+ public synchronized Optional<String> getActiveSecondaryKeyAlias() {
+ return Optional.ofNullable(mActiveSecondaryKeyAlias);
+ }
+
+ public synchronized Map<String, WrappedKeyProto.WrappedKey> getAllTertiaryKeys(
+ String secondaryKeyAlias) throws UnexpectedActiveSecondaryOnServerException {
+ if (!secondaryKeyAlias.equals(mActiveSecondaryKeyAlias)) {
+ throw new UnexpectedActiveSecondaryOnServerException(
+ String.format(
+ Locale.US,
+ "Requested tertiary keys wrapped with %s but %s was active secondary.",
+ secondaryKeyAlias,
+ mActiveSecondaryKeyAlias));
+ }
+
+ if (!mWrappedKeyStore.containsKey(secondaryKeyAlias)) {
+ return Collections.emptyMap();
+ }
+ return new HashMap<>(mWrappedKeyStore.get(secondaryKeyAlias));
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/fakes/FakeCryptoBackupServerTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/fakes/FakeCryptoBackupServerTest.java
new file mode 100644
index 000000000000..4cd8333b1a5e
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/fakes/FakeCryptoBackupServerTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.testing.fakes;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertThrows;
+
+import android.util.Pair;
+
+import com.android.server.backup.encryption.client.UnexpectedActiveSecondaryOnServerException;
+import com.android.server.backup.encryption.protos.nano.WrappedKeyProto;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.nio.charset.Charset;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class FakeCryptoBackupServerTest {
+ private static final String PACKAGE_NAME_1 = "package1";
+ private static final String PACKAGE_NAME_2 = "package2";
+ private static final String PACKAGE_NAME_3 = "package3";
+ private static final WrappedKeyProto.WrappedKey PACKAGE_KEY_1 = createWrappedKey("key1");
+ private static final WrappedKeyProto.WrappedKey PACKAGE_KEY_2 = createWrappedKey("key2");
+ private static final WrappedKeyProto.WrappedKey PACKAGE_KEY_3 = createWrappedKey("key3");
+
+ private FakeCryptoBackupServer mServer;
+
+ @Before
+ public void setUp() {
+ mServer = new FakeCryptoBackupServer();
+ }
+
+ @Test
+ public void getActiveSecondaryKeyAlias_isInitiallyAbsent() throws Exception {
+ assertFalse(mServer.getActiveSecondaryKeyAlias().isPresent());
+ }
+
+ @Test
+ public void setActiveSecondaryKeyAlias_setsTheKeyAlias() throws Exception {
+ String keyAlias = "test";
+ mServer.setActiveSecondaryKeyAlias(keyAlias, Collections.emptyMap());
+ assertThat(mServer.getActiveSecondaryKeyAlias().get()).isEqualTo(keyAlias);
+ }
+
+ @Test
+ public void getAllTertiaryKeys_returnsWrappedKeys() throws Exception {
+ Map<String, WrappedKeyProto.WrappedKey> entries =
+ createKeyMap(
+ new Pair<>(PACKAGE_NAME_1, PACKAGE_KEY_1),
+ new Pair<>(PACKAGE_NAME_2, PACKAGE_KEY_2));
+ String secondaryKeyAlias = "doge";
+ mServer.setActiveSecondaryKeyAlias(secondaryKeyAlias, entries);
+
+ assertThat(mServer.getAllTertiaryKeys(secondaryKeyAlias)).containsExactlyEntriesIn(entries);
+ }
+
+ @Test
+ public void addTertiaryKeys_updatesExistingSet() throws Exception {
+ String keyId = "karlin";
+ WrappedKeyProto.WrappedKey replacementKey = createWrappedKey("some replacement bytes");
+
+ mServer.setActiveSecondaryKeyAlias(
+ keyId,
+ createKeyMap(
+ new Pair<>(PACKAGE_NAME_1, PACKAGE_KEY_1),
+ new Pair<>(PACKAGE_NAME_2, PACKAGE_KEY_2)));
+
+ mServer.setActiveSecondaryKeyAlias(
+ keyId,
+ createKeyMap(
+ new Pair<>(PACKAGE_NAME_1, replacementKey),
+ new Pair<>(PACKAGE_NAME_3, PACKAGE_KEY_3)));
+
+ assertThat(mServer.getAllTertiaryKeys(keyId))
+ .containsExactlyEntriesIn(
+ createKeyMap(
+ new Pair<>(PACKAGE_NAME_1, replacementKey),
+ new Pair<>(PACKAGE_NAME_2, PACKAGE_KEY_2),
+ new Pair<>(PACKAGE_NAME_3, PACKAGE_KEY_3)));
+ }
+
+ @Test
+ public void getAllTertiaryKeys_throwsForUnknownSecondaryKeyAlias() throws Exception {
+ assertThrows(
+ UnexpectedActiveSecondaryOnServerException.class,
+ () -> mServer.getAllTertiaryKeys("unknown"));
+ }
+
+ @Test
+ public void uploadIncrementalBackup_throwsUnsupportedOperationException() {
+ assertThrows(
+ UnsupportedOperationException.class,
+ () ->
+ mServer.uploadIncrementalBackup(
+ PACKAGE_NAME_1,
+ "docid",
+ new byte[0],
+ new WrappedKeyProto.WrappedKey()));
+ }
+
+ @Test
+ public void uploadNonIncrementalBackup_throwsUnsupportedOperationException() {
+ assertThrows(
+ UnsupportedOperationException.class,
+ () ->
+ mServer.uploadNonIncrementalBackup(
+ PACKAGE_NAME_1, new byte[0], new WrappedKeyProto.WrappedKey()));
+ }
+
+ private static WrappedKeyProto.WrappedKey createWrappedKey(String data) {
+ WrappedKeyProto.WrappedKey wrappedKey = new WrappedKeyProto.WrappedKey();
+ wrappedKey.key = data.getBytes(Charset.forName("UTF-8"));
+ return wrappedKey;
+ }
+
+ private Map<String, WrappedKeyProto.WrappedKey> createKeyMap(
+ Pair<String, WrappedKeyProto.WrappedKey>... pairs) {
+ Map<String, WrappedKeyProto.WrappedKey> map = new HashMap<>();
+ for (Pair<String, WrappedKeyProto.WrappedKey> pair : pairs) {
+ map.put(pair.first, pair.second);
+ }
+ return map;
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/DataEntity.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/DataEntity.java
new file mode 100644
index 000000000000..6d3b5e9f1d7b
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/DataEntity.java
@@ -0,0 +1,100 @@
+/*
+ * 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.testing.shadows;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+/**
+ * Represents a key value pair in {@link ShadowBackupDataInput} and {@link ShadowBackupDataOutput}.
+ */
+public class DataEntity {
+ public final String mKey;
+ public final byte[] mValue;
+ public final int mSize;
+
+ /**
+ * Constructs a pair with a string value. The value will be converted to a byte array in {@link
+ * StandardCharsets#UTF_8}.
+ */
+ public DataEntity(String key, String value) {
+ this.mKey = checkNotNull(key);
+ this.mValue = value.getBytes(StandardCharsets.UTF_8);
+ mSize = this.mValue.length;
+ }
+
+ /**
+ * Constructs a new entity with the given key but a negative size. This represents a deleted
+ * pair.
+ */
+ public DataEntity(String key) {
+ this.mKey = checkNotNull(key);
+ mSize = -1;
+ mValue = null;
+ }
+
+ /** Constructs a new entity where the size of the value is the entire array. */
+ public DataEntity(String key, byte[] value) {
+ this(key, value, value.length);
+ }
+
+ /**
+ * Constructs a new entity.
+ *
+ * @param key the key of the pair
+ * @param data the value to associate with the key
+ * @param size the length of the value in bytes
+ */
+ public DataEntity(String key, byte[] data, int size) {
+ this.mKey = checkNotNull(key);
+ this.mSize = size;
+ mValue = new byte[size];
+ for (int i = 0; i < size; i++) {
+ mValue[i] = data[i];
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ DataEntity that = (DataEntity) o;
+
+ if (mSize != that.mSize) {
+ return false;
+ }
+ if (!mKey.equals(that.mKey)) {
+ return false;
+ }
+ return Arrays.equals(mValue, that.mValue);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mKey.hashCode();
+ result = 31 * result + Arrays.hashCode(mValue);
+ result = 31 * result + mSize;
+ return result;
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowBackupDataInput.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowBackupDataInput.java
new file mode 100644
index 000000000000..7ac6ec40508d
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowBackupDataInput.java
@@ -0,0 +1,106 @@
+/*
+ * 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.testing.shadows;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import android.annotation.Nullable;
+import android.app.backup.BackupDataInput;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+import java.io.ByteArrayInputStream;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Shadow for BackupDataInput. */
+@Implements(BackupDataInput.class)
+public class ShadowBackupDataInput {
+ private static final List<DataEntity> ENTITIES = new ArrayList<>();
+ @Nullable private static IOException sReadNextHeaderException;
+
+ @Nullable private ByteArrayInputStream mCurrentEntityInputStream;
+ private int mCurrentEntity = -1;
+
+ /** Resets the shadow, clearing any entities or exception. */
+ public static void reset() {
+ ENTITIES.clear();
+ sReadNextHeaderException = null;
+ }
+
+ /** Sets the exception which the input will throw for any call to {@link #readNextHeader}. */
+ public static void setReadNextHeaderException(@Nullable IOException readNextHeaderException) {
+ ShadowBackupDataInput.sReadNextHeaderException = readNextHeaderException;
+ }
+
+ /** Adds the given entity to the input. */
+ public static void addEntity(DataEntity e) {
+ ENTITIES.add(e);
+ }
+
+ /** Adds an entity to the input with the given key and value. */
+ public static void addEntity(String key, byte[] value) {
+ ENTITIES.add(new DataEntity(key, value, value.length));
+ }
+
+ public void __constructor__(FileDescriptor fd) {}
+
+ @Implementation
+ public boolean readNextHeader() throws IOException {
+ if (sReadNextHeaderException != null) {
+ throw sReadNextHeaderException;
+ }
+
+ mCurrentEntity++;
+
+ if (mCurrentEntity >= ENTITIES.size()) {
+ return false;
+ }
+
+ byte[] value = ENTITIES.get(mCurrentEntity).mValue;
+ if (value == null) {
+ mCurrentEntityInputStream = new ByteArrayInputStream(new byte[0]);
+ } else {
+ mCurrentEntityInputStream = new ByteArrayInputStream(value);
+ }
+ return true;
+ }
+
+ @Implementation
+ public String getKey() {
+ return ENTITIES.get(mCurrentEntity).mKey;
+ }
+
+ @Implementation
+ public int getDataSize() {
+ return ENTITIES.get(mCurrentEntity).mSize;
+ }
+
+ @Implementation
+ public void skipEntityData() {
+ // Do nothing.
+ }
+
+ @Implementation
+ public int readEntityData(byte[] data, int offset, int size) {
+ checkState(mCurrentEntityInputStream != null, "Must call readNextHeader() first");
+ return mCurrentEntityInputStream.read(data, offset, size);
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java
new file mode 100644
index 000000000000..2302e555fb44
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.testing.shadows;
+
+import android.app.backup.BackupDataOutput;
+
+import java.io.FileDescriptor;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.Assert;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+/** Shadow for BackupDataOutput. */
+@Implements(BackupDataOutput.class)
+public class ShadowBackupDataOutput {
+ private static final List<DataEntity> ENTRIES = new ArrayList<>();
+
+ private String mCurrentKey;
+ private int mDataSize;
+
+ public static void reset() {
+ ENTRIES.clear();
+ }
+
+ public static Set<DataEntity> getEntities() {
+ return new LinkedHashSet<>(ENTRIES);
+ }
+
+ public void __constructor__(FileDescriptor fd) {}
+
+ public void __constructor__(FileDescriptor fd, long quota) {}
+
+ public void __constructor__(FileDescriptor fd, long quota, int transportFlags) {}
+
+ @Implementation
+ public int writeEntityHeader(String key, int size) {
+ mCurrentKey = key;
+ mDataSize = size;
+ return 0;
+ }
+
+ @Implementation
+ public int writeEntityData(byte[] data, int size) {
+ Assert.assertEquals("ShadowBackupDataOutput expects size = mDataSize", size, mDataSize);
+ ENTRIES.add(new DataEntity(mCurrentKey, data, mDataSize));
+ return 0;
+ }
+}
diff --git a/packages/BackupEncryption/test/unittest/Android.bp b/packages/BackupEncryption/test/unittest/Android.bp
new file mode 100644
index 000000000000..d7c510b57518
--- /dev/null
+++ b/packages/BackupEncryption/test/unittest/Android.bp
@@ -0,0 +1,22 @@
+android_test {
+ name: "BackupEncryptionUnitTests",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "mockito-target-minus-junit4",
+ "platform-test-annotations",
+ "truth-prebuilt",
+ "testables",
+ "testng",
+ ],
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ "android.test.runner",
+ "BackupEncryption",
+ ],
+ test_suites: ["device-tests"],
+ instrumentation_for: "BackupEncryption",
+ certificate: "platform",
+} \ No newline at end of file
diff --git a/packages/BackupEncryption/test/unittest/AndroidManifest.xml b/packages/BackupEncryption/test/unittest/AndroidManifest.xml
new file mode 100644
index 000000000000..39ac8aa32ebc
--- /dev/null
+++ b/packages/BackupEncryption/test/unittest/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.backup.encryption.unittests"
+ android:sharedUserId="android.uid.system" >
+ <application android:testOnly="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.backup.encryption"
+ android:label="Backup Encryption Unit Tests" />
+</manifest> \ No newline at end of file
diff --git a/packages/BackupEncryption/test/unittest/AndroidTest.xml b/packages/BackupEncryption/test/unittest/AndroidTest.xml
new file mode 100644
index 000000000000..c9c812a78194
--- /dev/null
+++ b/packages/BackupEncryption/test/unittest/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<configuration description="Runs Backup Encryption Unit Tests.">
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="install-arg" value="-t" />
+ <option name="test-file-name" value="BackupEncryptionUnitTests.apk" />
+ </target_preparer>
+
+ <option name="test-tag" value="BackupEncryptionUnitTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.server.backup.encryption.unittests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
diff --git a/packages/BackupEncryption/test/unittest/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransportManagerTest.java b/packages/BackupEncryption/test/unittest/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransportManagerTest.java
new file mode 100644
index 000000000000..0d43a190cd07
--- /dev/null
+++ b/packages/BackupEncryption/test/unittest/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransportManagerTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.transport;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotSame;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+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.Intent;
+import android.os.Bundle;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportClientManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class IntermediateEncryptingTransportManagerTest {
+ @Mock private TransportClient mTransportClient;
+ @Mock private TransportClientManager mTransportClientManager;
+
+ private final ComponentName mTransportComponent = new ComponentName("pkg", "class");
+ private final Bundle mExtras = new Bundle();
+ private Intent mEncryptingTransportIntent;
+ private IntermediateEncryptingTransportManager mIntermediateEncryptingTransportManager;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mExtras.putInt("test", 1);
+ mEncryptingTransportIntent =
+ TransportClientManager.getEncryptingTransportIntent(mTransportComponent)
+ .putExtras(mExtras);
+ mIntermediateEncryptingTransportManager =
+ new IntermediateEncryptingTransportManager(mTransportClientManager);
+ }
+
+ @Test
+ public void testGet_createsClientWithRealTransportComponentAndExtras() {
+ when(mTransportClientManager.getTransportClient(any(), any(), any()))
+ .thenReturn(mTransportClient);
+
+ IntermediateEncryptingTransport intermediateEncryptingTransport =
+ mIntermediateEncryptingTransportManager.get(mEncryptingTransportIntent);
+
+ assertEquals(mTransportClient, intermediateEncryptingTransport.getClient());
+ verify(mTransportClientManager, times(1))
+ .getTransportClient(eq(mTransportComponent), argThat(mExtras::kindofEquals), any());
+ verifyNoMoreInteractions(mTransportClientManager);
+ }
+
+ @Test
+ public void testGet_callTwice_returnsSameTransport() {
+ IntermediateEncryptingTransport transport1 =
+ mIntermediateEncryptingTransportManager.get(mEncryptingTransportIntent);
+ IntermediateEncryptingTransport transport2 =
+ mIntermediateEncryptingTransportManager.get(mEncryptingTransportIntent);
+
+ assertEquals(transport1, transport2);
+ }
+
+ @Test
+ public void testCleanup_disposesTransportClient() {
+ when(mTransportClientManager.getTransportClient(any(), any(), any()))
+ .thenReturn(mTransportClient);
+
+ IntermediateEncryptingTransport transport =
+ mIntermediateEncryptingTransportManager.get(mEncryptingTransportIntent);
+ mIntermediateEncryptingTransportManager.cleanup(mEncryptingTransportIntent);
+
+ verify(mTransportClientManager, times(1)).getTransportClient(any(), any(), any());
+ verify(mTransportClientManager, times(1))
+ .disposeOfTransportClient(eq(mTransportClient), any());
+ verifyNoMoreInteractions(mTransportClientManager);
+ }
+
+ @Test
+ public void testCleanup_removesCachedTransport() {
+ when(mTransportClientManager.getTransportClient(any(), any(), any()))
+ .thenReturn(mTransportClient);
+
+ IntermediateEncryptingTransport transport1 =
+ mIntermediateEncryptingTransportManager.get(mEncryptingTransportIntent);
+ mIntermediateEncryptingTransportManager.cleanup(mEncryptingTransportIntent);
+ IntermediateEncryptingTransport transport2 =
+ mIntermediateEncryptingTransportManager.get(mEncryptingTransportIntent);
+
+ assertNotSame(transport1, transport2);
+ }
+}
diff --git a/packages/BackupEncryption/test/unittest/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransportTest.java b/packages/BackupEncryption/test/unittest/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransportTest.java
new file mode 100644
index 000000000000..a85b2e4498a6
--- /dev/null
+++ b/packages/BackupEncryption/test/unittest/src/com/android/server/backup/encryption/transport/IntermediateEncryptingTransportTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.encryption.transport;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.backup.BackupTransport;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.os.ParcelFileDescriptor;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.backup.IBackupTransport;
+import com.android.server.backup.encryption.KeyValueEncrypter;
+import com.android.server.backup.transport.TransportClient;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class IntermediateEncryptingTransportTest {
+ private static final String TEST_PACKAGE_NAME = "test_package";
+
+ private IntermediateEncryptingTransport mIntermediateEncryptingTransport;
+ private final PackageInfo mTestPackage = new PackageInfo();
+
+ @Mock private IBackupTransport mRealTransport;
+ @Mock private TransportClient mTransportClient;
+ @Mock private ParcelFileDescriptor mParcelFileDescriptor;
+ @Mock private KeyValueEncrypter mKeyValueEncrypter;
+ @Mock private Context mContext;
+
+ @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+ private File mTempFile;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mIntermediateEncryptingTransport =
+ new IntermediateEncryptingTransport(
+ mTransportClient, mContext, mKeyValueEncrypter, true);
+ mTestPackage.packageName = TEST_PACKAGE_NAME;
+ mTempFile = mTemporaryFolder.newFile();
+
+ when(mTransportClient.connect(anyString())).thenReturn(mRealTransport);
+ when(mRealTransport.getRestoreData(any())).thenReturn(BackupTransport.TRANSPORT_OK);
+ }
+
+ @Test
+ public void testGetDelegate_callsConnect() throws Exception {
+ IBackupTransport ret = mIntermediateEncryptingTransport.getDelegate();
+
+ assertEquals(mRealTransport, ret);
+ verify(mTransportClient, times(1)).connect(anyString());
+ verifyNoMoreInteractions(mTransportClient);
+ }
+
+ @Test
+ public void testGetDelegate_callTwice_callsConnectOnce() throws Exception {
+ when(mTransportClient.connect(anyString())).thenReturn(mRealTransport);
+
+ IBackupTransport ret1 = mIntermediateEncryptingTransport.getDelegate();
+ IBackupTransport ret2 = mIntermediateEncryptingTransport.getDelegate();
+
+ assertEquals(mRealTransport, ret1);
+ assertEquals(mRealTransport, ret2);
+ verify(mTransportClient, times(1)).connect(anyString());
+ verifyNoMoreInteractions(mTransportClient);
+ }
+
+ @Test
+ public void testPerformBackup_shouldEncryptTrue_encryptsDataAndPassesToDelegate()
+ throws Exception {
+ mIntermediateEncryptingTransport =
+ new TestIntermediateTransport(mTransportClient, mContext, mKeyValueEncrypter, true);
+
+ mIntermediateEncryptingTransport.performBackup(mTestPackage, mParcelFileDescriptor, 0);
+
+ verify(mKeyValueEncrypter, times(1))
+ .encryptKeyValueData(eq(TEST_PACKAGE_NAME), eq(mParcelFileDescriptor), any());
+ verify(mRealTransport, times(1)).performBackup(eq(mTestPackage), any(), eq(0));
+ }
+
+ @Test
+ public void testPerformBackup_shouldEncryptFalse_doesntEncryptDataAndPassedToDelegate()
+ throws Exception {
+ mIntermediateEncryptingTransport =
+ new TestIntermediateTransport(
+ mTransportClient, mContext, mKeyValueEncrypter, false);
+
+ mIntermediateEncryptingTransport.performBackup(mTestPackage, mParcelFileDescriptor, 0);
+
+ verifyZeroInteractions(mKeyValueEncrypter);
+ verify(mRealTransport, times(1))
+ .performBackup(eq(mTestPackage), eq(mParcelFileDescriptor), eq(0));
+ }
+
+ @Test
+ public void testGetRestoreData_shouldEncryptTrue_decryptsDataAndPassesToDelegate()
+ throws Exception {
+ mIntermediateEncryptingTransport =
+ new TestIntermediateTransport(mTransportClient, mContext, mKeyValueEncrypter, true);
+ mIntermediateEncryptingTransport.setNextRestorePackage(TEST_PACKAGE_NAME);
+
+ mIntermediateEncryptingTransport.getRestoreData(mParcelFileDescriptor);
+
+ verify(mKeyValueEncrypter, times(1))
+ .decryptKeyValueData(eq(TEST_PACKAGE_NAME), any(), eq(mParcelFileDescriptor));
+ verify(mRealTransport, times(1)).getRestoreData(any());
+ }
+
+ @Test
+ public void testGetRestoreData_shouldEncryptFalse_doesntDecryptDataAndPassesToDelegate()
+ throws Exception {
+ mIntermediateEncryptingTransport =
+ new TestIntermediateTransport(
+ mTransportClient, mContext, mKeyValueEncrypter, false);
+ mIntermediateEncryptingTransport.setNextRestorePackage(TEST_PACKAGE_NAME);
+
+ mIntermediateEncryptingTransport.getRestoreData(mParcelFileDescriptor);
+
+ verifyZeroInteractions(mKeyValueEncrypter);
+ verify(mRealTransport, times(1)).getRestoreData(eq(mParcelFileDescriptor));
+ }
+
+ private final class TestIntermediateTransport extends IntermediateEncryptingTransport {
+ TestIntermediateTransport(
+ TransportClient transportClient,
+ Context context,
+ KeyValueEncrypter keyValueEncrypter,
+ boolean shouldEncrypt) {
+ super(transportClient, context, keyValueEncrypter, shouldEncrypt);
+ }
+
+ @Override
+ protected File getBackupTempStorage(String packageName) {
+ return mTempFile;
+ }
+
+ @Override
+ protected File getRestoreTempStorage(String packageName) {
+ return mTempFile;
+ }
+ }
+}
diff --git a/packages/BackupRestoreConfirmation/res/values-fi/strings.xml b/packages/BackupRestoreConfirmation/res/values-fi/strings.xml
index 4517410333c3..bc9dbde20292 100644
--- a/packages/BackupRestoreConfirmation/res/values-fi/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-fi/strings.xml
@@ -27,7 +27,7 @@
<string name="current_password_text" msgid="8268189555578298067">"Kirjoita nykyinen varmuuskopioinnin salasana alla:"</string>
<string name="device_encryption_restore_text" msgid="1570864916855208992">"Kirjoita laitteen salauksen salasana alle."</string>
<string name="device_encryption_backup_text" msgid="5866590762672844664">"Kirjoita laitteen salauksen salasana alle. Salasanaa käytetään myös varmuuskopioarkiston salaamiseen."</string>
- <string name="backup_enc_password_text" msgid="4981585714795233099">"Anna salasana kaikkien varmuuskopiotietojen salaamiseksi. Jos tämä jätetään tyhjäksi, nykyistä varmuuskopioinnin salasanaa käytetään:"</string>
+ <string name="backup_enc_password_text" msgid="4981585714795233099">"Lisää salasana kaikkien varmuuskopiotietojen salaamiseksi. Jos tämä jätetään tyhjäksi, nykyistä varmuuskopioinnin salasanaa käytetään:"</string>
<string name="backup_enc_password_optional" msgid="1350137345907579306">"Jos haluat salata kaikki varmuuskopiotiedot, kirjoita salasana alle:"</string>
<string name="backup_enc_password_required" msgid="7889652203371654149">"Koska laite on salattu, myös varmuuskopiointi on salattava. Kirjoita salasana alle:"</string>
<string name="restore_enc_password_text" msgid="6140898525580710823">"Jos palautustiedot on salattu, anna salasana alla:"</string>
diff --git a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
index 72ec8d86368d..71e74cff6ccb 100644
--- a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
@@ -17,7 +17,7 @@
*/
-->
-<com.android.systemui.statusbar.car.CarNavigationBarView
+<com.android.systemui.navigationbar.car.CarNavigationBarView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:layout_height="match_parent"
@@ -36,7 +36,7 @@
android:background="@drawable/system_bar_background"
android:animateLayoutChanges="true">
- <com.android.systemui.statusbar.car.CarNavigationButton
+ <com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/home"
android:layout_height="wrap_content"
android:layout_width="match_parent"
@@ -47,7 +47,19 @@
android:paddingBottom="30dp"
/>
- <com.android.systemui.statusbar.car.CarNavigationButton
+ <com.android.systemui.navigationbar.car.CarNavigationButton
+ android:id="@+id/grid"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ systemui:intent="intent:#Intent;component=com.android.car.home/.AppGridActivity;end"
+ systemui:longIntent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
+ android:src="@drawable/car_ic_apps"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingTop="30dp"
+ android:paddingBottom="30dp"
+ />
+
+ <com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/hvac"
android:layout_height="wrap_content"
android:layout_width="match_parent"
@@ -58,6 +70,7 @@
android:paddingTop="30dp"
android:paddingBottom="30dp"
/>
+
</LinearLayout>
<LinearLayout
@@ -78,6 +91,7 @@
android:alpha="0.7"
/>
+
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
@@ -96,4 +110,4 @@
</LinearLayout>
-</com.android.systemui.statusbar.car.CarNavigationBarView>
+</com.android.systemui.navigationbar.car.CarNavigationBarView>
diff --git a/packages/CarSystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
index 708f5955f306..f016dbfaa3b7 100644
--- a/packages/CarSystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
+++ b/packages/CarSystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
@@ -17,7 +17,7 @@
*/
-->
-<com.android.systemui.statusbar.car.CarNavigationBarView
+<com.android.systemui.navigationbar.car.CarNavigationBarView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:layout_height="match_parent"
@@ -36,7 +36,7 @@
android:background="@drawable/system_bar_background"
android:animateLayoutChanges="true">
- <com.android.systemui.statusbar.car.CarNavigationButton
+ <com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/home"
android:layout_height="wrap_content"
android:layout_width="match_parent"
@@ -47,7 +47,7 @@
android:paddingBottom="30dp"
/>
- <com.android.systemui.statusbar.car.CarNavigationButton
+ <com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/hvac"
android:layout_height="wrap_content"
android:layout_width="match_parent"
@@ -59,4 +59,4 @@
android:paddingBottom="30dp"
/>
</LinearLayout>
-</com.android.systemui.statusbar.car.CarNavigationBarView>
+</com.android.systemui.navigationbar.car.CarNavigationBarView>
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
index 897976f439b5..6c7a04f2b698 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
@@ -15,13 +15,15 @@
~ limitations under the License
-->
-<com.android.systemui.statusbar.car.CarNavigationBarView
+<com.android.systemui.navigationbar.car.CarNavigationBarView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/system_bar_background"
android:orientation="vertical">
+ <!--The 20dp padding is the difference between the background selected icon size and the ripple
+ that was chosen, thus it's a hack to make it look pretty and not an official margin value-->
<LinearLayout
android:id="@id/nav_buttons"
android:layout_width="match_parent"
@@ -31,13 +33,12 @@
android:paddingEnd="20dp"
android:gravity="center">
- <com.android.systemui.statusbar.car.CarFacetButton
+ <com.android.systemui.navigationbar.car.CarFacetButton
android:id="@+id/home"
style="@style/NavigationBarButton"
systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
systemui:icon="@drawable/car_ic_overview"
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
- systemui:longIntent="intent:#Intent;action=com.google.android.demandspace.START;end"
systemui:selectedIcon="@drawable/car_ic_overview_selected"
systemui:useMoreIcon="false"
/>
@@ -47,7 +48,7 @@
android:layout_height="match_parent"
android:layout_weight="1"/>
- <com.android.systemui.statusbar.car.CarFacetButton
+ <com.android.systemui.navigationbar.car.CarFacetButton
android:id="@+id/maps_nav"
style="@style/NavigationBarButton"
systemui:categories="android.intent.category.APP_MAPS"
@@ -62,7 +63,7 @@
android:layout_height="match_parent"
android:layout_weight="1"/>
- <com.android.systemui.statusbar.car.CarFacetButton
+ <com.android.systemui.navigationbar.car.CarFacetButton
android:id="@+id/music_nav"
style="@style/NavigationBarButton"
systemui:categories="android.intent.category.APP_MUSIC"
@@ -78,7 +79,7 @@
android:layout_height="match_parent"
android:layout_weight="1"/>
- <com.android.systemui.statusbar.car.CarFacetButton
+ <com.android.systemui.navigationbar.car.CarFacetButton
android:id="@+id/phone_nav"
style="@style/NavigationBarButton"
systemui:icon="@drawable/car_ic_phone"
@@ -93,7 +94,7 @@
android:layout_height="match_parent"
android:layout_weight="1"/>
- <com.android.systemui.statusbar.car.CarFacetButton
+ <com.android.systemui.navigationbar.car.CarFacetButton
android:id="@+id/grid_nav"
style="@style/NavigationBarButton"
systemui:componentNames="com.android.car.carlauncher/.AppGridActivity"
@@ -108,13 +109,11 @@
android:layout_height="match_parent"
android:layout_weight="1"/>
- <!-- Click handling will be initialized in CarNavigationBarView because its
- id = notifications which is treated special for the opening of the notification panel
- -->
- <com.android.systemui.statusbar.car.CarNavigationButton
+ <com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/notifications"
style="@style/NavigationBarButton"
- android:src="@drawable/car_ic_notification"
+ systemui:icon="@drawable/car_ic_notification"
+ systemui:longIntent="intent:#Intent;component=com.android.car.bugreport/.BugReportActivity;end"
systemui:selectedIcon="@drawable/car_ic_notification_selected"
systemui:useMoreIcon="false"
/>
@@ -124,13 +123,13 @@
android:layout_height="match_parent"
android:layout_weight="1"/>
- <com.android.systemui.statusbar.car.CarFacetButton
+ <com.android.systemui.navigationbar.car.AssitantButton
android:id="@+id/assist"
style="@style/NavigationBarButton"
systemui:icon="@drawable/ic_mic_white"
- systemui:intent="intent:#Intent;action=com.google.android.demandspace.START;end"
systemui:useMoreIcon="false"
/>
+
</LinearLayout>
<LinearLayout
@@ -138,10 +137,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:paddingStart="@*android:dimen/car_keyline_1"
- android:paddingEnd="@*android:dimen/car_keyline_1"
+ android:paddingStart="@dimen/car_keyline_1"
+ android:paddingEnd="@dimen/car_keyline_1"
android:gravity="center"
android:visibility="gone">
+
</LinearLayout>
-</com.android.systemui.statusbar.car.CarNavigationBarView>
+</com.android.systemui.navigationbar.car.CarNavigationBarView> \ No newline at end of file
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
index 28eba6ceade1..1cf48c5ffd0b 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
@@ -15,7 +15,7 @@
~ limitations under the License
-->
-<com.android.systemui.statusbar.car.CarNavigationBarView
+<com.android.systemui.navigationbar.car.CarNavigationBarView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
@@ -31,7 +31,7 @@
android:paddingStart="@*android:dimen/car_padding_5"
android:paddingEnd="@*android:dimen/car_padding_5">
- <com.android.systemui.statusbar.car.CarNavigationButton
+ <com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/home"
android:layout_width="@*android:dimen/car_touch_target_size"
android:layout_height="match_parent"
@@ -40,5 +40,5 @@
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
/>
</LinearLayout>
-</com.android.systemui.statusbar.car.CarNavigationBarView>
+</com.android.systemui.navigationbar.car.CarNavigationBarView>
diff --git a/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
index 72ec8d86368d..327610a892ee 100644
--- a/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
@@ -17,7 +17,7 @@
*/
-->
-<com.android.systemui.statusbar.car.CarNavigationBarView
+<com.android.systemui.navigationbar.car.CarNavigationBarView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:layout_height="match_parent"
@@ -25,6 +25,9 @@
android:orientation="vertical"
android:background="@drawable/system_bar_background">
+ <!-- phone.NavigationBarView has rot0 and rot90 but we expect the car head unit to have a fixed
+ rotation so skip this level of the hierarchy.
+ -->
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
@@ -36,7 +39,7 @@
android:background="@drawable/system_bar_background"
android:animateLayoutChanges="true">
- <com.android.systemui.statusbar.car.CarNavigationButton
+ <com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/home"
android:layout_height="wrap_content"
android:layout_width="match_parent"
@@ -47,7 +50,19 @@
android:paddingBottom="30dp"
/>
- <com.android.systemui.statusbar.car.CarNavigationButton
+ <com.android.systemui.navigationbar.car.CarNavigationButton
+ android:id="@+id/grid"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ systemui:intent="intent:#Intent;component=com.android.car.home/.AppGridActivity;launchFlags=0x14000000;end"
+ systemui:longIntent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
+ android:src="@drawable/car_ic_apps"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingTop="30dp"
+ android:paddingBottom="30dp"
+ />
+
+ <com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/hvac"
android:layout_height="wrap_content"
android:layout_width="match_parent"
@@ -58,6 +73,7 @@
android:paddingTop="30dp"
android:paddingBottom="30dp"
/>
+
</LinearLayout>
<LinearLayout
@@ -78,6 +94,7 @@
android:alpha="0.7"
/>
+
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
@@ -96,4 +113,4 @@
</LinearLayout>
-</com.android.systemui.statusbar.car.CarNavigationBarView>
+</com.android.systemui.navigationbar.car.CarNavigationBarView>
diff --git a/packages/CarSystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
index 708f5955f306..f016dbfaa3b7 100644
--- a/packages/CarSystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
+++ b/packages/CarSystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
@@ -17,7 +17,7 @@
*/
-->
-<com.android.systemui.statusbar.car.CarNavigationBarView
+<com.android.systemui.navigationbar.car.CarNavigationBarView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:layout_height="match_parent"
@@ -36,7 +36,7 @@
android:background="@drawable/system_bar_background"
android:animateLayoutChanges="true">
- <com.android.systemui.statusbar.car.CarNavigationButton
+ <com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/home"
android:layout_height="wrap_content"
android:layout_width="match_parent"
@@ -47,7 +47,7 @@
android:paddingBottom="30dp"
/>
- <com.android.systemui.statusbar.car.CarNavigationButton
+ <com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/hvac"
android:layout_height="wrap_content"
android:layout_width="match_parent"
@@ -59,4 +59,4 @@
android:paddingBottom="30dp"
/>
</LinearLayout>
-</com.android.systemui.statusbar.car.CarNavigationBarView>
+</com.android.systemui.navigationbar.car.CarNavigationBarView>
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
index cae89c16a03e..7299ddfd8688 100644
--- a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
@@ -15,7 +15,7 @@
~ limitations under the License
-->
-<com.android.systemui.statusbar.car.CarNavigationBarView
+<com.android.systemui.navigationbar.car.CarNavigationBarView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/car_top_bar"
@@ -36,7 +36,7 @@
android:layout_alignParentStart="true"
>
- <com.android.systemui.statusbar.car.CarNavigationButton
+ <com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/hvacleft"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -73,7 +73,7 @@
android:layout_height="match_parent"
android:layout_centerInParent="true"
>
- <com.android.systemui.statusbar.car.CarNavigationButton
+ <com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/qs"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -120,7 +120,7 @@
android:layout_alignParentEnd="true"
>
- <com.android.systemui.statusbar.car.CarNavigationButton
+ <com.android.systemui.navigationbar.car.CarNavigationButton
android:id="@+id/hvacright"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -150,4 +150,4 @@
</FrameLayout>
</RelativeLayout>
-</com.android.systemui.statusbar.car.CarNavigationBarView>
+</com.android.systemui.navigationbar.car.CarNavigationBarView>
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml
new file mode 100644
index 000000000000..a71567c48eaf
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml
@@ -0,0 +1,147 @@
+<?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
+ -->
+
+<com.android.systemui.navigationbar.car.CarNavigationBarView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/car_top_bar"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/system_bar_background"
+ android:orientation="vertical">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+
+ <FrameLayout
+ android:id="@+id/left_hvac_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentStart="true"
+ >
+
+ <com.android.systemui.navigationbar.car.CarNavigationButton
+ android:id="@+id/hvacleft"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@null"
+ systemui:broadcast="true"
+ systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+ />
+
+ <com.android.systemui.statusbar.hvac.AnimatedTemperatureView
+ android:id="@+id/lefttext"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingStart="@*android:dimen/car_padding_4"
+ android:paddingEnd="16dp"
+ android:gravity="center_vertical|start"
+ android:minEms="4"
+ android:textAppearance="@style/TextAppearance.CarStatus"
+ systemui:hvacAreaId="49"
+ systemui:hvacMaxText="@string/hvac_max_text"
+ systemui:hvacMaxValue="@dimen/hvac_max_value"
+ systemui:hvacMinText="@string/hvac_min_text"
+ systemui:hvacMinValue="@dimen/hvac_min_value"
+ systemui:hvacPivotOffset="60dp"
+ systemui:hvacPropertyId="358614275"
+ systemui:hvacTempFormat="%.0f\u00B0"
+ />
+ </FrameLayout>
+
+ <FrameLayout
+ android:id="@+id/clock_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_centerInParent="true">
+ <com.android.systemui.navigationbar.car.CarNavigationButton
+ android:id="@+id/qs"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@null"/>
+ <com.android.systemui.statusbar.policy.Clock
+ android:id="@+id/clock"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:elevation="5dp"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock"/>
+ </FrameLayout>
+
+ <LinearLayout
+ android:id="@+id/system_icon_area"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:layout_toEndOf="@+id/clock_container"
+ android:paddingStart="@*android:dimen/car_padding_1"
+ android:gravity="center_vertical"
+ android:orientation="horizontal"
+ >
+
+ <include
+ layout="@layout/system_icons"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingStart="4dp"
+ android:gravity="center_vertical"
+ />
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/right_hvac_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentEnd="true"
+ >
+
+ <com.android.systemui.navigationbar.car.CarNavigationButton
+ android:id="@+id/hvacright"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@null"
+ systemui:broadcast="true"
+ systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+ />
+
+ <com.android.systemui.statusbar.hvac.AnimatedTemperatureView
+ android:id="@+id/righttext"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingStart="16dp"
+ android:paddingEnd="@*android:dimen/car_padding_4"
+ android:gravity="center_vertical|end"
+ android:minEms="4"
+ android:textAppearance="@style/TextAppearance.CarStatus"
+ systemui:hvacAreaId="68"
+ systemui:hvacMaxText="@string/hvac_max_text"
+ systemui:hvacMaxValue="@dimen/hvac_max_value"
+ systemui:hvacMinText="@string/hvac_min_text"
+ systemui:hvacMinValue="@dimen/hvac_min_value"
+ systemui:hvacPivotOffset="60dp"
+ systemui:hvacPropertyId="358614275"
+ systemui:hvacTempFormat="%.0f\u00B0"
+ />
+ </FrameLayout>
+ </RelativeLayout>
+
+</com.android.systemui.navigationbar.car.CarNavigationBarView>
diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml
index e1bcc2e5f86c..37cd1d49cb85 100644
--- a/packages/CarSystemUI/res/layout/super_status_bar.xml
+++ b/packages/CarSystemUI/res/layout/super_status_bar.xml
@@ -69,10 +69,10 @@
android:visibility="gone"
/>
- <include layout="@layout/car_top_navigation_bar"
+ <FrameLayout
+ android:id="@+id/car_top_navigation_bar_container"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- />
+ android:layout_height="wrap_content"/>
</LinearLayout>
<include layout="@layout/brightness_mirror"/>
@@ -93,7 +93,7 @@
android:layout_height="match_parent"
android:visibility="invisible"/>
- <include layout="@layout/status_bar_expanded"
+ <ViewStub android:id="@+id/status_bar_expanded"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible"/>
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index 467c4a41893d..fe042fe2e17f 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -40,4 +40,31 @@
slots that may be reused for things like IME control. -->
<integer name="config_maxNotificationIcons">0</integer>
+ <!-- SystemUI Services: The classes of the stuff to start. -->
+ <string-array name="config_systemUIServiceComponents" translatable="false">
+ <item>com.android.systemui.util.NotificationChannels</item>
+ <item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item>
+ <item>com.android.systemui.keyguard.KeyguardViewMediator</item>
+ <item>com.android.systemui.recents.Recents</item>
+ <item>com.android.systemui.volume.VolumeUI</item>
+ <item>com.android.systemui.stackdivider.Divider</item>
+ <item>com.android.systemui.statusbar.phone.StatusBar</item>
+ <item>com.android.systemui.usb.StorageNotification</item>
+ <item>com.android.systemui.power.PowerUI</item>
+ <item>com.android.systemui.media.RingtonePlayer</item>
+ <item>com.android.systemui.keyboard.KeyboardUI</item>
+ <item>com.android.systemui.pip.PipUI</item>
+ <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
+ <item>@string/config_systemUIVendorServiceComponent</item>
+ <item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
+ <item>com.android.systemui.LatencyTester</item>
+ <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
+ <item>com.android.systemui.ScreenDecorations</item>
+ <item>com.android.systemui.biometrics.AuthController</item>
+ <item>com.android.systemui.SliceBroadcastRelayHandler</item>
+ <item>com.android.systemui.SizeCompatModeActivityController</item>
+ <item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
+ <item>com.android.systemui.theme.ThemeOverlayController</item>
+ <item>com.android.systemui.navigationbar.car.CarNavigationBar</item>
+ </string-array>
</resources>
diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml
index fb422aff51ef..e2da91b6d746 100644
--- a/packages/CarSystemUI/res/values/dimens.xml
+++ b/packages/CarSystemUI/res/values/dimens.xml
@@ -120,4 +120,73 @@
<dimen name="notification_shade_handle_bar_margin_bottom">10dp</dimen>
<dimen name="notification_shade_list_padding_bottom">50dp</dimen>
+ <!-- The alpha for the scrim behind the notification shade. This value is 1 so that the
+ scrim has no transparency. -->
+ <item name="scrim_behind_alpha" format="float" type="dimen">1.0</item>
+
+ <!-- The width of panel holding the notification card. -->
+ <dimen name="notification_panel_width">522dp</dimen>
+
+ <!-- The width of the quick settings panel. -1 for match_parent. -->
+ <dimen name="qs_panel_width">-1px</dimen>
+
+ <!-- Height of a small notification in the status bar-->
+ <dimen name="notification_min_height">192dp</dimen>
+
+ <!-- Height of a small notification in the status bar which was used before android N -->
+ <dimen name="notification_min_height_legacy">192dp</dimen>
+
+ <!-- Height of a large notification in the status bar -->
+ <dimen name="notification_max_height">400dp</dimen>
+
+ <!-- Height of a heads up notification in the status bar for legacy custom views -->
+ <dimen name="notification_max_heads_up_height_legacy">400dp</dimen>
+
+ <!-- Height of a heads up notification in the status bar -->
+ <dimen name="notification_max_heads_up_height">400dp</dimen>
+
+ <!-- Height of the status bar header bar -->
+ <dimen name="status_bar_header_height">54dp</dimen>
+
+ <!-- The height of the divider between the individual notifications. -->
+ <dimen name="notification_divider_height">16dp</dimen>
+
+ <!-- The height of the divider between the individual notifications when the notification
+ wants it to be increased. This value is the same as notification_divider_height so that
+ the spacing between all notifications will always be the same. -->
+ <dimen name="notification_divider_height_increased">@dimen/notification_divider_height</dimen>
+
+ <!-- The alpha of the dividing line between child notifications of a notification group. -->
+ <item name="notification_divider_alpha" format="float" type="dimen">1.0</item>
+
+ <!-- The width of each individual notification card. -->
+ <dimen name="notification_child_width">522dp</dimen>
+
+ <!-- The top margin of the notification panel. -->
+ <dimen name="notification_panel_margin_top">32dp</dimen>
+
+ <!-- The bottom margin of the panel that holds the list of notifications. -->
+ <dimen name="notification_panel_margin_bottom">@dimen/notification_divider_height</dimen>
+
+ <!-- The corner radius of the shadow behind the notification. -->
+ <dimen name="notification_shadow_radius">16dp</dimen>
+
+ <!-- The amount of space below the notification list. This value is 0 so the list scrolls
+ all the way to the bottom. -->
+ <dimen name="close_handle_underlap">0dp</dimen>
+
+ <!-- The height of the divider between the individual notifications in a notification group. -->
+ <dimen name="notification_children_container_divider_height">1dp</dimen>
+
+ <!-- The height of the header for a container containing child notifications. -->
+ <dimen name="notification_children_container_header_height">76dp</dimen>
+
+ <!-- The top margin for the notification children container in its non-expanded form. This
+ value is smaller than notification_children_container_header_height to bring the first
+ child closer so there is less wasted space. -->
+ <dimen name="notification_children_container_margin_top">68dp</dimen>
+
+ <!-- The height of the quick settings footer that holds the user switcher, settings icon,
+ etc. in the car setting.-->
+ <dimen name="qs_footer_height">74dp</dimen>
</resources>
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
new file mode 100644
index 000000000000..8e0a3eb53e6f
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import com.android.systemui.navigationbar.car.CarNavigationBar;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+
+/** Binder for car specific {@link SystemUI} modules. */
+@Module
+public abstract class CarSystemUIBinder {
+ /** */
+ @Binds
+ @IntoMap
+ @ClassKey(CarNavigationBar.class)
+ public abstract SystemUI bindCarNavigationBar(CarNavigationBar sysui);
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
index c7654e81e0b1..be4b8897d00b 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
@@ -20,11 +20,10 @@ import android.content.Context;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.statusbar.car.CarFacetButtonController;
+import com.android.systemui.dagger.SystemUIRootComponent;
+import com.android.systemui.navigationbar.car.CarFacetButtonController;
import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.volume.CarVolumeDialogComponent;
-import com.android.systemui.volume.VolumeDialogComponent;
import javax.inject.Singleton;
@@ -43,7 +42,6 @@ public class CarSystemUIFactory extends SystemUIFactory {
.contextHolder(new ContextHolder(context))
.build();
return DaggerCarSystemUIRootComponent.builder()
- .dependencyProvider(new com.android.systemui.DependencyProvider())
.contextHolder(new ContextHolder(context))
.build();
}
@@ -57,10 +55,6 @@ public class CarSystemUIFactory extends SystemUIFactory {
return new CarStatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
}
- public VolumeDialogComponent createVolumeDialogComponent(SystemUI systemUi, Context context) {
- return new CarVolumeDialogComponent(systemUi, context);
- }
-
@Singleton
@Component(modules = ContextHolder.class)
public interface CarDependencyComponent {
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 9a063aa7b791..93e553f59dcf 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -23,18 +23,22 @@ import android.content.Context;
import com.android.systemui.car.CarNotificationEntryManager;
import com.android.systemui.car.CarNotificationInterruptionStateProvider;
+import com.android.systemui.dagger.SystemUIRootComponent;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.EnhancedEstimatesImpl;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
+import com.android.systemui.statusbar.car.CarStatusBar;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.volume.CarVolumeDialogComponent;
+import com.android.systemui.volume.VolumeDialogComponent;
import javax.inject.Named;
import javax.inject.Singleton;
@@ -42,6 +46,8 @@ import javax.inject.Singleton;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
@Module
abstract class CarSystemUIModule {
@@ -94,4 +100,16 @@ abstract class CarSystemUIModule {
@Binds
abstract SystemUIRootComponent bindSystemUIRootComponent(
CarSystemUIRootComponent systemUIRootComponent);
+
+ @Binds
+ public abstract StatusBar bindStatusBar(CarStatusBar statusBar);
+
+ @Binds
+ @IntoMap
+ @ClassKey(StatusBar.class)
+ public abstract SystemUI providesStatusBar(CarStatusBar statusBar);
+
+ @Binds
+ abstract VolumeDialogComponent bindVolumeDialogComponent(
+ CarVolumeDialogComponent carVolumeDialogComponent);
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
index 264b7d52c02f..c2847c88785b 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
@@ -16,6 +16,12 @@
package com.android.systemui;
+import com.android.systemui.dagger.DependencyBinder;
+import com.android.systemui.dagger.DependencyProvider;
+import com.android.systemui.dagger.SystemServicesModule;
+import com.android.systemui.dagger.SystemUIModule;
+import com.android.systemui.dagger.SystemUIRootComponent;
+
import javax.inject.Singleton;
import dagger.Component;
@@ -26,8 +32,10 @@ import dagger.Component;
DependencyProvider.class,
DependencyBinder.class,
SystemUIFactory.ContextHolder.class,
+ SystemServicesModule.class,
SystemUIModule.class,
- CarSystemUIModule.class
+ CarSystemUIModule.class,
+ CarSystemUIBinder.class
})
interface CarSystemUIRootComponent extends SystemUIRootComponent {
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index a107dd793551..53a88a9a54e9 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -15,11 +15,12 @@
*/
package com.android.systemui.car;
-import android.content.Context;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -34,8 +35,8 @@ import javax.inject.Singleton;
public class CarNotificationEntryManager extends NotificationEntryManager {
@Inject
- public CarNotificationEntryManager(Context context) {
- super(context);
+ public CarNotificationEntryManager(NotificationData notificationData, NotifLog notifLog) {
+ super(notificationData, notifLog);
}
@Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
index afd722ba0091..447e579ece42 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
@@ -22,6 +22,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.policy.BatteryController;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -34,8 +35,9 @@ public class CarNotificationInterruptionStateProvider extends
@Inject
public CarNotificationInterruptionStateProvider(Context context,
NotificationFilter filter,
- StatusBarStateController stateController) {
- super(context, filter, stateController);
+ StatusBarStateController stateController,
+ BatteryController batteryController) {
+ super(context, filter, stateController, batteryController);
}
@Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/AssitantButton.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/AssitantButton.java
new file mode 100644
index 000000000000..c50de22fb2d8
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/AssitantButton.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.navigationbar.car;
+
+import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.util.Log;
+
+import com.android.internal.app.AssistUtils;
+import com.android.internal.app.IVoiceInteractionSessionShowCallback;
+
+/**
+ * AssitantButton is a ui component that will trigger the Voice Interaction Service.
+ */
+public class AssitantButton extends CarFacetButton {
+
+ private static final String TAG = "CarFacetButton";
+ private final AssistUtils mAssistUtils;
+ private IVoiceInteractionSessionShowCallback mShowCallback =
+ new IVoiceInteractionSessionShowCallback.Stub() {
+ @Override
+ public void onFailed() {
+ Log.w(TAG, "Failed to show VoiceInteractionSession");
+ }
+
+ @Override
+ public void onShown() {
+ Log.d(TAG, "IVoiceInteractionSessionShowCallback onShown()");
+ }
+ };
+
+ public AssitantButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mAssistUtils = new AssistUtils(context);
+ setOnClickListener(v -> {
+ showAssistant();
+ });
+ }
+
+ private void showAssistant() {
+ final Bundle args = new Bundle();
+ mAssistUtils.showSessionForActiveService(args,
+ SHOW_SOURCE_ASSIST_GESTURE, mShowCallback, /*activityToken=*/ null);
+ }
+
+ @Override
+ protected void setupIntents(TypedArray typedArray) {
+ // left blank because for the assistant button Intent will not be passed from the layout.
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButton.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButton.java
new file mode 100644
index 000000000000..c46e6e7433a3
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButton.java
@@ -0,0 +1,232 @@
+/*
+ * 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.navigationbar.car;
+
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.TypedArray;
+import android.os.Build;
+import android.os.UserHandle;
+import android.util.AttributeSet;
+import android.view.Display;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import com.android.keyguard.AlphaOptimizedImageButton;
+import com.android.systemui.CarSystemUIFactory;
+import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
+
+/**
+ * CarFacetButton is a ui component designed to be used as a shortcut for an app of a defined
+ * category. It can also render a indicator implying that there are more options of apps to launch
+ * using this component. This is done with a "More icon" currently an arrow as defined in the layout
+ * file. The class is to serve as an example.
+ *
+ * New activity will be launched on the same display as the button is on.
+ * Usage example: A button that allows a user to select a music app and indicate that there are
+ * other music apps installed.
+ */
+public class CarFacetButton extends LinearLayout {
+ private static final String FACET_FILTER_DELIMITER = ";";
+ /**
+ * Extra information to be sent to a helper to make the decision of what app to launch when
+ * clicked.
+ */
+ private static final String EXTRA_FACET_CATEGORIES = "categories";
+ private static final String EXTRA_FACET_PACKAGES = "packages";
+ private static final String EXTRA_FACET_ID = "filter_id";
+ private static final String EXTRA_FACET_LAUNCH_PICKER = "launch_picker";
+ private static final String TAG = "CarFacetButton";
+
+ private Context mContext;
+ private AlphaOptimizedImageButton mIcon;
+ private AlphaOptimizedImageButton mMoreIcon;
+ private boolean mSelected = false;
+ private String[] mComponentNames;
+ /** App categories that are to be used with this widget */
+ private String[] mFacetCategories;
+ /** App packages that are allowed to be used with this widget */
+ private String[] mFacetPackages;
+ private int mIconResourceId;
+ /**
+ * If defined in the xml this will be the icon that's rendered when the button is marked as
+ * selected
+ */
+ private int mSelectedIconResourceId;
+ private boolean mUseMoreIcon = true;
+ private float mSelectedAlpha = 1f;
+ private float mUnselectedAlpha = 1f;
+
+ public CarFacetButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+ View.inflate(context, R.layout.car_facet_button, this);
+ // extract custom attributes
+ TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton);
+ setupIntents(typedArray);
+ setupIcons(typedArray);
+ CarSystemUIFactory factory = SystemUIFactory.getInstance();
+ CarFacetButtonController carFacetButtonController = factory.getCarDependencyComponent()
+ .getCarFacetButtonController();
+ carFacetButtonController.addFacetButton(this);
+ }
+
+ /**
+ * Reads the custom attributes to setup click handlers for this component.
+ */
+ protected void setupIntents(TypedArray typedArray) {
+ String intentString = typedArray.getString(R.styleable.CarFacetButton_intent);
+ String longPressIntentString = typedArray.getString(R.styleable.CarFacetButton_longIntent);
+ String categoryString = typedArray.getString(R.styleable.CarFacetButton_categories);
+ String packageString = typedArray.getString(R.styleable.CarFacetButton_packages);
+ String componentNameString =
+ typedArray.getString(R.styleable.CarFacetButton_componentNames);
+ try {
+ final Intent intent = Intent.parseUri(intentString, Intent.URI_INTENT_SCHEME);
+ intent.putExtra(EXTRA_FACET_ID, Integer.toString(getId()));
+
+ if (packageString != null) {
+ mFacetPackages = packageString.split(FACET_FILTER_DELIMITER);
+ intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages);
+ }
+ if (categoryString != null) {
+ mFacetCategories = categoryString.split(FACET_FILTER_DELIMITER);
+ intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories);
+ }
+ if (componentNameString != null) {
+ mComponentNames = componentNameString.split(FACET_FILTER_DELIMITER);
+ }
+
+ setOnClickListener(v -> {
+ ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchDisplayId(mContext.getDisplayId());
+ intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, mSelected);
+ mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
+ mContext.sendBroadcastAsUser(
+ new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), UserHandle.CURRENT);
+ });
+
+ if (longPressIntentString != null && (Build.IS_ENG || Build.IS_USERDEBUG)) {
+ final Intent longPressIntent = Intent.parseUri(longPressIntentString,
+ Intent.URI_INTENT_SCHEME);
+ setOnLongClickListener(v -> {
+ ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchDisplayId(mContext.getDisplayId());
+ mContext.startActivityAsUser(longPressIntent, options.toBundle(),
+ UserHandle.CURRENT);
+ mContext.sendBroadcastAsUser(
+ new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), UserHandle.CURRENT);
+ return true;
+ });
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to attach intent", e);
+ }
+ }
+
+ private void setupIcons(TypedArray styledAttributes) {
+ mSelectedAlpha = styledAttributes.getFloat(
+ R.styleable.CarFacetButton_selectedAlpha, mSelectedAlpha);
+ mUnselectedAlpha = styledAttributes.getFloat(
+ R.styleable.CarFacetButton_unselectedAlpha, mUnselectedAlpha);
+ mIcon = findViewById(R.id.car_nav_button_icon);
+ mIcon.setScaleType(ImageView.ScaleType.CENTER);
+ mIcon.setClickable(false);
+ mIcon.setAlpha(mUnselectedAlpha);
+ mIconResourceId = styledAttributes.getResourceId(R.styleable.CarFacetButton_icon, 0);
+ mIcon.setImageResource(mIconResourceId);
+ mSelectedIconResourceId = styledAttributes.getResourceId(
+ R.styleable.CarFacetButton_selectedIcon, mIconResourceId);
+
+ mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
+ mMoreIcon.setClickable(false);
+ mMoreIcon.setAlpha(mSelectedAlpha);
+ mMoreIcon.setVisibility(GONE);
+ mUseMoreIcon = styledAttributes.getBoolean(R.styleable.CarFacetButton_useMoreIcon, true);
+ }
+
+ /**
+ * @return The app categories the component represents
+ */
+ public String[] getCategories() {
+ if (mFacetCategories == null) {
+ return new String[0];
+ }
+ return mFacetCategories;
+ }
+
+ /**
+ * @return The valid packages that should be considered.
+ */
+ public String[] getFacetPackages() {
+ if (mFacetPackages == null) {
+ return new String[0];
+ }
+ return mFacetPackages;
+ }
+
+ /**
+ * @return The list of component names.
+ */
+ public String[] getComponentName() {
+ if (mComponentNames == null) {
+ return new String[0];
+ }
+ return mComponentNames;
+ }
+
+ /**
+ * Updates the alpha of the icons to "selected" and shows the "More icon"
+ *
+ * @param selected true if the view must be selected, false otherwise
+ */
+ public void setSelected(boolean selected) {
+ super.setSelected(selected);
+ setSelected(selected, selected);
+ }
+
+ /**
+ * Updates the visual state to let the user know if it's been selected.
+ *
+ * @param selected true if should update the alpha of the icon to selected, false otherwise
+ * @param showMoreIcon true if the "more icon" should be shown, false otherwise. Note this
+ * is ignored if the attribute useMoreIcon is set to false
+ */
+ public void setSelected(boolean selected, boolean showMoreIcon) {
+ mSelected = selected;
+ mIcon.setAlpha(mSelected ? mSelectedAlpha : mUnselectedAlpha);
+ mIcon.setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId);
+ if (mUseMoreIcon) {
+ mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE);
+ }
+ }
+
+ /**
+ * @return The id of the display the button is on or Display.INVALID_DISPLAY if it's not yet on
+ * a display.
+ */
+ public int getDisplayId() {
+ Display display = getDisplay();
+ if (display == null) {
+ return Display.INVALID_DISPLAY;
+ }
+ return display.getDisplayId();
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButtonController.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButtonController.java
new file mode 100644
index 000000000000..30f63f052b9f
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButtonController.java
@@ -0,0 +1,206 @@
+/*
+ * 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.navigationbar.car;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.view.Display;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * CarFacetButtons placed on the nav bar are designed to have visual indication that the active
+ * application on screen is associated with it. This is basically a similar concept to a radio
+ * button group.
+ */
+@Singleton
+public class CarFacetButtonController {
+
+ protected ButtonMap mButtonsByCategory = new ButtonMap();
+ protected ButtonMap mButtonsByPackage = new ButtonMap();
+ protected ButtonMap mButtonsByComponentName = new ButtonMap();
+ protected HashSet<CarFacetButton> mSelectedFacetButtons;
+ protected Context mContext;
+
+ @Inject
+ public CarFacetButtonController(Context context) {
+ mContext = context;
+ mSelectedFacetButtons = new HashSet<>();
+ }
+
+ /**
+ * Add facet button to this controller. The expected use is for the facet button
+ * to get a reference to this controller via {@link com.android.systemui.Dependency}
+ * and self add.
+ */
+ public void addFacetButton(CarFacetButton facetButton) {
+ String[] categories = facetButton.getCategories();
+ for (int i = 0; i < categories.length; i++) {
+ mButtonsByCategory.add(categories[i], facetButton);
+ }
+
+ String[] facetPackages = facetButton.getFacetPackages();
+ for (int i = 0; i < facetPackages.length; i++) {
+ mButtonsByPackage.add(facetPackages[i], facetButton);
+ }
+ String[] componentNames = facetButton.getComponentName();
+ for (int i = 0; i < componentNames.length; i++) {
+ mButtonsByComponentName.add(componentNames[i], facetButton);
+ }
+ }
+
+ /** Removes all buttons from the button maps. */
+ public void removeAll() {
+ mButtonsByCategory.clear();
+ mButtonsByPackage.clear();
+ mButtonsByComponentName.clear();
+ mSelectedFacetButtons.clear();
+ }
+
+ /**
+ * Iterate through a view looking for CarFacetButtons and adding them to the controller if found
+ *
+ * @param v the View that may contain CarFacetButtons
+ */
+ public void addAllFacetButtons(View v) {
+ if (v instanceof CarFacetButton) {
+ addFacetButton((CarFacetButton) v);
+ } else if (v instanceof ViewGroup) {
+ ViewGroup viewGroup = (ViewGroup) v;
+ for (int i = 0; i < viewGroup.getChildCount(); i++) {
+ addAllFacetButtons(viewGroup.getChildAt(i));
+ }
+ }
+ }
+
+ /**
+ * This will unselect the currently selected CarFacetButton and determine which one should be
+ * selected next. It does this by reading the properties on the CarFacetButton and seeing if
+ * they are a match with the supplied StackInfo list.
+ * The order of selection detection is ComponentName, PackageName then Category
+ * They will then be compared with the supplied StackInfo list.
+ * The StackInfo is expected to be supplied in order of recency and StackInfo will only be used
+ * for consideration if it has the same displayId as the CarFacetButtons.
+ *
+ * @param stackInfoList of the currently running application
+ */
+ public void taskChanged(List<ActivityManager.StackInfo> stackInfoList) {
+ ActivityManager.StackInfo validStackInfo = null;
+ for (ActivityManager.StackInfo stackInfo : stackInfoList) {
+ // Find the first stack info with a topActivity in the primary display.
+ // TODO: We assume that CarFacetButton will launch an app only in the primary display.
+ // We need to extend the functionality to handle the mutliple display properly.
+ if (stackInfo.topActivity != null && stackInfo.displayId == Display.DEFAULT_DISPLAY) {
+ validStackInfo = stackInfo;
+ break;
+ }
+ }
+
+ if (validStackInfo == null) {
+ // No stack was found that was on the same display as the facet buttons thus return
+ return;
+ }
+
+ if (mSelectedFacetButtons != null) {
+ Iterator<CarFacetButton> iterator = mSelectedFacetButtons.iterator();
+ while (iterator.hasNext()) {
+ CarFacetButton carFacetButton = iterator.next();
+ if (carFacetButton.getDisplayId() == validStackInfo.displayId) {
+ carFacetButton.setSelected(false);
+ iterator.remove();
+ }
+ }
+ }
+
+ String packageName = validStackInfo.topActivity.getPackageName();
+ HashSet<CarFacetButton> facetButton =
+ findFacetButtonByComponentName(validStackInfo.topActivity);
+ if (facetButton == null) {
+ facetButton = mButtonsByPackage.get(packageName);
+ }
+
+ if (facetButton == null) {
+ String category = getPackageCategory(packageName);
+ if (category != null) {
+ facetButton = mButtonsByCategory.get(category);
+ }
+ }
+
+ if (facetButton != null) {
+ for (CarFacetButton carFacetButton : facetButton) {
+ if (carFacetButton.getDisplayId() == validStackInfo.displayId) {
+ carFacetButton.setSelected(true);
+ mSelectedFacetButtons.add(carFacetButton);
+ }
+ }
+ }
+
+ }
+
+ private HashSet<CarFacetButton> findFacetButtonByComponentName(ComponentName componentName) {
+ HashSet<CarFacetButton> buttons =
+ mButtonsByComponentName.get(componentName.flattenToShortString());
+ return (buttons != null) ? buttons :
+ mButtonsByComponentName.get(componentName.flattenToString());
+ }
+
+ protected String getPackageCategory(String packageName) {
+ PackageManager pm = mContext.getPackageManager();
+ Set<String> supportedCategories = mButtonsByCategory.keySet();
+ for (String category : supportedCategories) {
+ Intent intent = new Intent();
+ intent.setPackage(packageName);
+ intent.setAction(Intent.ACTION_MAIN);
+ intent.addCategory(category);
+ List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
+ if (list.size() > 0) {
+ // Cache this package name into facetPackageMap, so we won't have to query
+ // all categories next time this package name shows up.
+ mButtonsByPackage.put(packageName, mButtonsByCategory.get(category));
+ return category;
+ }
+ }
+ return null;
+ }
+
+ // simple multi-map
+ private static class ButtonMap extends HashMap<String, HashSet<CarFacetButton>> {
+
+ public boolean add(String key, CarFacetButton value) {
+ if (containsKey(key)) {
+ return get(key).add(value);
+ }
+ HashSet<CarFacetButton> set = new HashSet<>();
+ set.add(value);
+ put(key, set);
+ return true;
+ }
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
new file mode 100644
index 000000000000..6fba1d516c73
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
@@ -0,0 +1,381 @@
+/*
+ * 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.navigationbar.car;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.inputmethodservice.InputMethodService;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.RegisterStatusBarResult;
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+import com.android.systemui.dagger.qualifiers.MainHandler;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.NavigationBarController;
+import com.android.systemui.statusbar.car.hvac.HvacController;
+import com.android.systemui.statusbar.car.hvac.TemperatureView;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import javax.inject.Inject;
+
+import dagger.Lazy;
+
+/** Navigation bars customized for the automotive use case. */
+public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks {
+
+ private final NavigationBarViewFactory mNavigationBarViewFactory;
+ private final WindowManager mWindowManager;
+ private final DeviceProvisionedController mDeviceProvisionedController;
+ private final Lazy<FacetButtonTaskStackListener> mFacetButtonTaskStackListener;
+ private final Handler mMainHandler;
+ private final Lazy<KeyguardStateController> mKeyguardStateController;
+ private final Lazy<CarFacetButtonController> mFacetButtonController;
+ private final Lazy<NavigationBarController> mNavigationBarController;
+ private final Lazy<HvacController> mHvacController;
+
+ private IStatusBarService mBarService;
+ private CommandQueue mCommandQueue;
+ private ActivityManagerWrapper mActivityManagerWrapper;
+
+ // If the nav bar should be hidden when the soft keyboard is visible.
+ private boolean mHideNavBarForKeyboard;
+ private boolean mBottomNavBarVisible;
+
+ // Nav bar views.
+ private ViewGroup mNavigationBarWindow;
+ private ViewGroup mLeftNavigationBarWindow;
+ private ViewGroup mRightNavigationBarWindow;
+ private CarNavigationBarView mNavigationBarView;
+ private CarNavigationBarView mLeftNavigationBarView;
+ private CarNavigationBarView mRightNavigationBarView;
+
+ // To be attached to the navigation bars such that they can close the notification panel if
+ // it's open.
+ private boolean mDeviceIsSetUpForUser = true;
+
+ // Configuration values for if nav bars should be shown.
+ private boolean mShowBottom;
+ private boolean mShowLeft;
+ private boolean mShowRight;
+
+
+ @Inject
+ public CarNavigationBar(Context context,
+ NavigationBarViewFactory navigationBarViewFactory,
+ WindowManager windowManager,
+ DeviceProvisionedController deviceProvisionedController,
+ Lazy<FacetButtonTaskStackListener> facetButtonTaskStackListener,
+ @MainHandler Handler mainHandler,
+ Lazy<KeyguardStateController> keyguardStateController,
+ Lazy<CarFacetButtonController> facetButtonController,
+ Lazy<NavigationBarController> navigationBarController,
+ Lazy<HvacController> hvacController) {
+ super(context);
+ mNavigationBarViewFactory = navigationBarViewFactory;
+ mWindowManager = windowManager;
+ mDeviceProvisionedController = deviceProvisionedController;
+ mFacetButtonTaskStackListener = facetButtonTaskStackListener;
+ mMainHandler = mainHandler;
+ mKeyguardStateController = keyguardStateController;
+ mFacetButtonController = facetButtonController;
+ mNavigationBarController = navigationBarController;
+ mHvacController = hvacController;
+ }
+
+ @Override
+ public void start() {
+ // Set initial state.
+ mHideNavBarForKeyboard = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard);
+ mBottomNavBarVisible = false;
+
+ // Read configuration.
+ mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar);
+ mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar);
+ mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar);
+
+ // Get bar service.
+ mBarService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+
+ // Connect into the status bar manager service
+ mCommandQueue = getComponent(CommandQueue.class);
+ mCommandQueue.addCallback(this);
+
+ RegisterStatusBarResult result = null;
+ try {
+ result = mBarService.registerStatusBar(mCommandQueue);
+ } catch (RemoteException ex) {
+ ex.rethrowFromSystemServer();
+ }
+
+ mDeviceIsSetUpForUser = mDeviceProvisionedController.isCurrentUserSetup();
+ mDeviceProvisionedController.addCallback(
+ new DeviceProvisionedController.DeviceProvisionedListener() {
+ @Override
+ public void onUserSetupChanged() {
+ mMainHandler.post(() -> restartNavBarsIfNecessary());
+ }
+
+ @Override
+ public void onUserSwitched() {
+ mMainHandler.post(() -> restartNavBarsIfNecessary());
+ }
+ });
+
+ createNavigationBar(result);
+
+ mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
+ mActivityManagerWrapper.registerTaskStackListener(mFacetButtonTaskStackListener.get());
+
+ mHvacController.get().connectToCarService();
+ }
+
+ private void restartNavBarsIfNecessary() {
+ boolean currentUserSetup = mDeviceProvisionedController.isCurrentUserSetup();
+ if (mDeviceIsSetUpForUser != currentUserSetup) {
+ mDeviceIsSetUpForUser = currentUserSetup;
+ restartNavBars();
+ }
+ }
+
+ /**
+ * Remove all content from navbars and rebuild them. Used to allow for different nav bars
+ * before and after the device is provisioned. . Also for change of density and font size.
+ */
+ private void restartNavBars() {
+ // remove and reattach all hvac components such that we don't keep a reference to unused
+ // ui elements
+ mHvacController.get().removeAllComponents();
+ mFacetButtonController.get().removeAll();
+
+ if (mNavigationBarWindow != null) {
+ mNavigationBarWindow.removeAllViews();
+ mNavigationBarView = null;
+ }
+
+ if (mLeftNavigationBarWindow != null) {
+ mLeftNavigationBarWindow.removeAllViews();
+ mLeftNavigationBarView = null;
+ }
+
+ if (mRightNavigationBarWindow != null) {
+ mRightNavigationBarWindow.removeAllViews();
+ mRightNavigationBarView = null;
+ }
+
+ buildNavBarContent();
+ // If the UI was rebuilt (day/night change) while the keyguard was up we need to
+ // correctly respect that state.
+ if (mKeyguardStateController.get().isShowing()) {
+ updateNavBarForKeyguardContent();
+ }
+
+ // CarFacetButtonController was reset therefore we need to re-add the status bar elements
+ // to the controller.
+ // TODO(hseog): Add facet buttons in status bar to controller.
+ }
+
+ private void createNavigationBar(RegisterStatusBarResult result) {
+ buildNavBarWindows();
+ buildNavBarContent();
+ attachNavBarWindows();
+
+ // Try setting up the initial state of the nav bar if applicable.
+ if (result != null) {
+ setImeWindowStatus(Display.DEFAULT_DISPLAY, result.mImeToken,
+ result.mImeWindowVis, result.mImeBackDisposition,
+ result.mShowImeSwitcher);
+ }
+
+ // There has been a car customized nav bar on the default display, so just create nav bars
+ // on external displays.
+ mNavigationBarController.get().createNavigationBars(/* includeDefaultDisplay= */ false,
+ result);
+ }
+
+ private void buildNavBarWindows() {
+ if (mShowBottom) {
+ mNavigationBarWindow = mNavigationBarViewFactory.getBottomWindow();
+ }
+
+ if (mShowLeft) {
+ mLeftNavigationBarWindow = mNavigationBarViewFactory.getLeftWindow();
+ }
+
+ if (mShowRight) {
+ mRightNavigationBarWindow = mNavigationBarViewFactory.getRightWindow();
+ }
+ }
+
+ private void buildNavBarContent() {
+ if (mShowBottom) {
+ mNavigationBarView = mNavigationBarViewFactory.getBottomBar(mDeviceIsSetUpForUser);
+ mNavigationBarWindow.addView(mNavigationBarView);
+ addTemperatureViewToController(mNavigationBarView);
+ }
+
+ if (mShowLeft) {
+ mLeftNavigationBarView = mNavigationBarViewFactory.getLeftBar(mDeviceIsSetUpForUser);
+ mLeftNavigationBarWindow.addView(mLeftNavigationBarView);
+ addTemperatureViewToController(mLeftNavigationBarView);
+ }
+
+ if (mShowRight) {
+ mRightNavigationBarView = mNavigationBarViewFactory.getRightBar(mDeviceIsSetUpForUser);
+ mRightNavigationBarWindow.addView(mRightNavigationBarView);
+ // Add ability to toggle notification center.
+ addTemperatureViewToController(mRightNavigationBarView);
+ // Add ability to close notification center on touch.
+ }
+ }
+
+ private void addTemperatureViewToController(View v) {
+ if (v instanceof TemperatureView) {
+ mHvacController.get().addHvacTextView((TemperatureView) v);
+ } else if (v instanceof ViewGroup) {
+ ViewGroup viewGroup = (ViewGroup) v;
+ for (int i = 0; i < viewGroup.getChildCount(); i++) {
+ addTemperatureViewToController(viewGroup.getChildAt(i));
+ }
+ }
+ }
+
+ private void attachNavBarWindows() {
+ if (mShowBottom && !mBottomNavBarVisible) {
+ mBottomNavBarVisible = true;
+
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
+ 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("CarNavigationBar");
+ lp.windowAnimations = 0;
+ mWindowManager.addView(mNavigationBarWindow, lp);
+ }
+
+ if (mShowLeft) {
+ int width = mContext.getResources().getDimensionPixelSize(
+ R.dimen.car_left_navigation_bar_width);
+ WindowManager.LayoutParams leftlp = new WindowManager.LayoutParams(
+ width, ViewGroup.LayoutParams.MATCH_PARENT,
+ 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);
+ leftlp.setTitle("LeftCarNavigationBar");
+ leftlp.windowAnimations = 0;
+ leftlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
+ leftlp.gravity = Gravity.LEFT;
+ mWindowManager.addView(mLeftNavigationBarWindow, leftlp);
+ }
+ if (mShowRight) {
+ int width = mContext.getResources().getDimensionPixelSize(
+ R.dimen.car_right_navigation_bar_width);
+ WindowManager.LayoutParams rightlp = new WindowManager.LayoutParams(
+ width, ViewGroup.LayoutParams.MATCH_PARENT,
+ 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);
+ rightlp.setTitle("RightCarNavigationBar");
+ rightlp.windowAnimations = 0;
+ rightlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
+ rightlp.gravity = Gravity.RIGHT;
+ mWindowManager.addView(mRightNavigationBarWindow, rightlp);
+ }
+ }
+
+ /**
+ * We register for soft keyboard visibility events such that we can hide the navigation bar
+ * giving more screen space to the IME. Note: this is optional and controlled by
+ * {@code com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard}.
+ */
+ @Override
+ public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
+ boolean showImeSwitcher) {
+ if (!mHideNavBarForKeyboard) {
+ return;
+ }
+
+ if (mContext.getDisplay().getDisplayId() != displayId) {
+ return;
+ }
+
+ boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
+ showBottomNavBarWindow(isKeyboardVisible);
+ }
+
+ private void showBottomNavBarWindow(boolean isKeyboardVisible) {
+ if (!mShowBottom) {
+ return;
+ }
+
+ // If keyboard is visible and bottom nav bar not visible, this is the correct state, so do
+ // nothing. Same with if keyboard is not visible and bottom nav bar is visible.
+ if (isKeyboardVisible ^ mBottomNavBarVisible) {
+ return;
+ }
+
+ mNavigationBarViewFactory.getBottomWindow().setVisibility(
+ isKeyboardVisible ? View.GONE : View.VISIBLE);
+ mBottomNavBarVisible = !isKeyboardVisible;
+ }
+
+ private void updateNavBarForKeyguardContent() {
+ if (mNavigationBarView != null) {
+ mNavigationBarView.showKeyguardButtons();
+ }
+ if (mLeftNavigationBarView != null) {
+ mLeftNavigationBarView.showKeyguardButtons();
+ }
+ if (mRightNavigationBarView != null) {
+ mRightNavigationBarView.showKeyguardButtons();
+ }
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.print(" mTaskStackListener=");
+ pw.println(mFacetButtonTaskStackListener.get());
+ pw.print(" mNavigationBarView=");
+ pw.println(mNavigationBarView);
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java
new file mode 100644
index 000000000000..afb69547cb47
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java
@@ -0,0 +1,143 @@
+/*
+ * 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.navigationbar.car;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.car.CarStatusBar;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+
+/**
+ * A custom navigation bar for the automotive use case.
+ * <p>
+ * The navigation bar in the automotive use case is more like a list of shortcuts, rendered
+ * in a linear layout.
+ */
+public class CarNavigationBarView extends LinearLayout {
+ private View mNavButtons;
+ private CarNavigationButton mNotificationsButton;
+ private CarStatusBar mCarStatusBar;
+ private Context mContext;
+ private View mLockScreenButtons;
+ // used to wire in open/close gestures for notifications
+ private OnTouchListener mStatusBarWindowTouchListener;
+
+
+ public CarNavigationBarView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+ }
+
+ @Override
+ public void onFinishInflate() {
+ mNavButtons = findViewById(R.id.nav_buttons);
+ mLockScreenButtons = findViewById(R.id.lock_screen_nav_buttons);
+
+ mNotificationsButton = findViewById(R.id.notifications);
+ if (mNotificationsButton != null) {
+ mNotificationsButton.setOnClickListener(this::onNotificationsClick);
+ }
+ View mStatusIcons = findViewById(R.id.statusIcons);
+ if (mStatusIcons != null) {
+ // Attach the controllers for Status icons such as wifi and bluetooth if the standard
+ // container is in the view.
+ StatusBarIconController.DarkIconManager mDarkIconManager =
+ new StatusBarIconController.DarkIconManager(
+ mStatusIcons.findViewById(R.id.statusIcons));
+ mDarkIconManager.setShouldLog(true);
+ Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
+ }
+ // needs to be clickable so that it will receive ACTION_MOVE events
+ setClickable(true);
+ }
+
+ // Used to forward touch events even if the touch was initiated from a child component
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (mStatusBarWindowTouchListener != null) {
+ // Forward touch events to the status bar window so it can drag
+ // windows if required (Notification shade)
+ mStatusBarWindowTouchListener.onTouch(this, ev);
+ }
+ return super.onInterceptTouchEvent(ev);
+ }
+
+ public void setStatusBar(CarStatusBar carStatusBar) {
+ mCarStatusBar = carStatusBar;
+ }
+
+ /**
+ * Set a touch listener that will be called from onInterceptTouchEvent and onTouchEvent
+ *
+ * @param statusBarWindowTouchListener The listener to call from touch and intercept touch
+ */
+ public void setStatusBarWindowTouchListener(OnTouchListener statusBarWindowTouchListener) {
+ mStatusBarWindowTouchListener = statusBarWindowTouchListener;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (mStatusBarWindowTouchListener != null) {
+ mStatusBarWindowTouchListener.onTouch(this, event);
+ }
+ return super.onTouchEvent(event);
+ }
+
+ protected void onNotificationsClick(View v) {
+ mCarStatusBar.togglePanel();
+ }
+
+ /**
+ * If there are buttons declared in the layout they will be shown and the normal
+ * Nav buttons will be hidden.
+ */
+ public void showKeyguardButtons() {
+ if (mLockScreenButtons == null) {
+ return;
+ }
+ mLockScreenButtons.setVisibility(View.VISIBLE);
+ mNavButtons.setVisibility(View.GONE);
+ }
+
+ /**
+ * If there are buttons declared in the layout they will be hidden and the normal
+ * Nav buttons will be shown.
+ */
+ public void hideKeyguardButtons() {
+ if (mLockScreenButtons == null) return;
+
+ mNavButtons.setVisibility(View.VISIBLE);
+ mLockScreenButtons.setVisibility(View.GONE);
+ }
+
+ /**
+ * Toggles the notification unseen indicator on/off.
+ *
+ * @param hasUnseen true if the unseen notification count is great than 0.
+ */
+ public void toggleNotificationUnseenIndicator(Boolean hasUnseen) {
+ if (mNotificationsButton == null) return;
+
+ mNotificationsButton.setUnseen(hasUnseen);
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java
new file mode 100644
index 000000000000..707d80fc0d3b
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java
@@ -0,0 +1,160 @@
+/*
+ * 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.navigationbar.car;
+
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.TypedArray;
+import android.os.Build;
+import android.os.UserHandle;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+
+import java.net.URISyntaxException;
+
+/**
+ * CarNavigationButton is an image button that allows for a bit more configuration at the
+ * xml file level. This allows for more control via overlays instead of having to update
+ * code.
+ */
+public class CarNavigationButton extends com.android.keyguard.AlphaOptimizedImageButton {
+ private static final String TAG = "CarNavigationButton";
+
+ private static final int UNSEEN_ICON_RESOURCE_ID = R.drawable.car_ic_notification_unseen;
+ private static final int UNSEEN_SELECTED_ICON_RESOURCE_ID =
+ R.drawable.car_ic_notification_selected_unseen;
+
+ private Context mContext;
+ private String mIntent;
+ private String mLongIntent;
+ private boolean mBroadcastIntent;
+ private boolean mHasUnseen = false;
+ private boolean mSelected = false;
+ private float mSelectedAlpha = 1f;
+ private float mUnselectedAlpha = 1f;
+ private int mSelectedIconResourceId;
+ private int mIconResourceId;
+
+
+ public CarNavigationButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+
+ // CarNavigationButton attrs
+ TypedArray typedArray = context.obtainStyledAttributes(
+ attrs, R.styleable.CarNavigationButton);
+ mIntent = typedArray.getString(R.styleable.CarNavigationButton_intent);
+ mLongIntent = typedArray.getString(R.styleable.CarNavigationButton_longIntent);
+ mBroadcastIntent = typedArray.getBoolean(R.styleable.CarNavigationButton_broadcast, false);
+ mSelectedAlpha = typedArray.getFloat(
+ R.styleable.CarNavigationButton_selectedAlpha, mSelectedAlpha);
+ mUnselectedAlpha = typedArray.getFloat(
+ R.styleable.CarNavigationButton_unselectedAlpha, mUnselectedAlpha);
+ mSelectedIconResourceId = typedArray.getResourceId(
+ R.styleable.CarNavigationButton_selectedIcon, mIconResourceId);
+ mIconResourceId = typedArray.getResourceId(
+ R.styleable.CarNavigationButton_icon, 0);
+ typedArray.recycle();
+ }
+
+
+ /**
+ * After the standard inflate this then adds the xml defined intents to click and long click
+ * actions if defined.
+ */
+ @Override
+ public void onFinishInflate() {
+ super.onFinishInflate();
+ setScaleType(ImageView.ScaleType.CENTER);
+ setAlpha(mUnselectedAlpha);
+ try {
+ if (mIntent != null) {
+ final Intent intent = Intent.parseUri(mIntent, Intent.URI_INTENT_SCHEME);
+ setOnClickListener(v -> {
+ try {
+ if (mBroadcastIntent) {
+ mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
+ mContext.sendBroadcastAsUser(
+ new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS),
+ UserHandle.CURRENT);
+ return;
+ }
+ ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchDisplayId(mContext.getDisplayId());
+ mContext.startActivityAsUser(intent, options.toBundle(),
+ UserHandle.CURRENT);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to launch intent", e);
+ }
+ });
+ }
+ } catch (URISyntaxException e) {
+ throw new RuntimeException("Failed to attach intent", e);
+ }
+
+ try {
+ if (mLongIntent != null && (Build.IS_ENG || Build.IS_USERDEBUG)) {
+ final Intent intent = Intent.parseUri(mLongIntent, Intent.URI_INTENT_SCHEME);
+ setOnLongClickListener(v -> {
+ try {
+ ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchDisplayId(mContext.getDisplayId());
+ mContext.startActivityAsUser(intent, options.toBundle(),
+ UserHandle.CURRENT);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to launch intent", e);
+ }
+ // consume event either way
+ return true;
+ });
+ }
+ } catch (URISyntaxException e) {
+ throw new RuntimeException("Failed to attach long press intent", e);
+ }
+ }
+
+ /**
+ * @param selected true if should indicate if this is a selected state, false otherwise
+ */
+ public void setSelected(boolean selected) {
+ super.setSelected(selected);
+ mSelected = selected;
+ setAlpha(mSelected ? mSelectedAlpha : mUnselectedAlpha);
+ updateImage();
+ }
+
+ /**
+ * @param hasUnseen true if should indicate if this is a Unseen state, false otherwise.
+ */
+ public void setUnseen(boolean hasUnseen) {
+ mHasUnseen = hasUnseen;
+ updateImage();
+ }
+
+ private void updateImage() {
+ if (mHasUnseen) {
+ setImageResource(mSelected ? UNSEEN_SELECTED_ICON_RESOURCE_ID
+ : UNSEEN_ICON_RESOURCE_ID);
+ } else {
+ setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId);
+ }
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/FacetButtonTaskStackListener.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/FacetButtonTaskStackListener.java
new file mode 100644
index 000000000000..4925220d9cad
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/FacetButtonTaskStackListener.java
@@ -0,0 +1,65 @@
+/*
+ * 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.navigationbar.car;
+
+import android.app.ActivityTaskManager;
+import android.util.Log;
+
+import com.android.systemui.shared.system.TaskStackChangeListener;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+
+/**
+ * An implementation of TaskStackChangeListener, that listens for changes in the system
+ * task stack and notifies the navigation bar.
+ */
+@Singleton
+class FacetButtonTaskStackListener extends TaskStackChangeListener {
+ private static final String TAG = FacetButtonTaskStackListener.class.getSimpleName();
+
+ private final Lazy<CarFacetButtonController> mFacetButtonControllerLazy;
+
+ @Inject
+ FacetButtonTaskStackListener(
+ Lazy<CarFacetButtonController> carFacetButtonControllerLazy) {
+ mFacetButtonControllerLazy = carFacetButtonControllerLazy;
+ }
+
+ @Override
+ public void onTaskStackChanged() {
+ try {
+ mFacetButtonControllerLazy.get().taskChanged(
+ ActivityTaskManager.getService().getAllStackInfos());
+ } catch (Exception e) {
+ Log.e(TAG, "Getting StackInfo from activity manager failed", e);
+ }
+ }
+
+ @Override
+ public void onTaskDisplayChanged(int taskId, int newDisplayId) {
+ try {
+ mFacetButtonControllerLazy.get().taskChanged(
+ ActivityTaskManager.getService().getAllStackInfos());
+ } catch (Exception e) {
+ Log.e(TAG, "Getting StackInfo from activity manager failed", e);
+ }
+
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/NavigationBarViewFactory.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/NavigationBarViewFactory.java
new file mode 100644
index 000000000000..519b33a2f53e
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/NavigationBarViewFactory.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.navigationbar.car;
+
+import android.content.Context;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.LayoutRes;
+
+import com.android.systemui.R;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** A factory that creates and caches views for navigation bars. */
+@Singleton
+public class NavigationBarViewFactory {
+
+ private static final String TAG = NavigationBarViewFactory.class.getSimpleName();
+ private static final ArrayMap<Type, Integer> sLayoutMap = setupLayoutMapping();
+
+ private static ArrayMap<Type, Integer> setupLayoutMapping() {
+ ArrayMap<Type, Integer> map = new ArrayMap<>();
+ map.put(Type.TOP, R.layout.car_top_navigation_bar);
+ map.put(Type.TOP_UNPROVISIONED, R.layout.car_top_navigation_bar_unprovisioned);
+ map.put(Type.BOTTOM, R.layout.car_navigation_bar);
+ map.put(Type.BOTTOM_UNPROVISIONED, R.layout.car_navigation_bar_unprovisioned);
+ map.put(Type.LEFT, R.layout.car_left_navigation_bar);
+ map.put(Type.LEFT_UNPROVISIONED, R.layout.car_left_navigation_bar_unprovisioned);
+ map.put(Type.RIGHT, R.layout.car_right_navigation_bar);
+ map.put(Type.RIGHT_UNPROVISIONED, R.layout.car_right_navigation_bar_unprovisioned);
+ return map;
+ }
+
+ private final Context mContext;
+ private final ArrayMap<Type, CarNavigationBarView> mCachedViewMap = new ArrayMap<>(
+ Type.values().length);
+ private final ArrayMap<Type, ViewGroup> mCachedContainerMap = new ArrayMap<>();
+
+ /** Type of navigation bar to be created. */
+ private enum Type {
+ TOP,
+ TOP_UNPROVISIONED,
+ BOTTOM,
+ BOTTOM_UNPROVISIONED,
+ LEFT,
+ LEFT_UNPROVISIONED,
+ RIGHT,
+ RIGHT_UNPROVISIONED
+ }
+
+ @Inject
+ public NavigationBarViewFactory(Context context) {
+ mContext = context;
+ }
+
+ /** Gets the bottom window. */
+ public ViewGroup getBottomWindow() {
+ return getWindowCached(Type.BOTTOM);
+ }
+
+ /** Gets the left window. */
+ public ViewGroup getLeftWindow() {
+ return getWindowCached(Type.LEFT);
+ }
+
+ /** Gets the right window. */
+ public ViewGroup getRightWindow() {
+ return getWindowCached(Type.RIGHT);
+ }
+
+ /** Gets the top bar. */
+ public CarNavigationBarView getTopBar(boolean isSetUp) {
+ return getBar(isSetUp, Type.TOP, Type.TOP_UNPROVISIONED);
+ }
+
+ /** Gets the bottom bar. */
+ public CarNavigationBarView getBottomBar(boolean isSetUp) {
+ return getBar(isSetUp, Type.BOTTOM, Type.BOTTOM_UNPROVISIONED);
+ }
+
+ /** Gets the left bar. */
+ public CarNavigationBarView getLeftBar(boolean isSetUp) {
+ return getBar(isSetUp, Type.LEFT, Type.LEFT_UNPROVISIONED);
+ }
+
+ /** Gets the right bar. */
+ public CarNavigationBarView getRightBar(boolean isSetUp) {
+ return getBar(isSetUp, Type.RIGHT, Type.RIGHT_UNPROVISIONED);
+ }
+
+ private ViewGroup getWindowCached(Type type) {
+ if (mCachedContainerMap.containsKey(type)) {
+ return mCachedContainerMap.get(type);
+ }
+
+ ViewGroup window = (ViewGroup) View.inflate(mContext,
+ R.layout.navigation_bar_window, /* root= */ null);
+ mCachedContainerMap.put(type, window);
+ return mCachedContainerMap.get(type);
+ }
+
+ private CarNavigationBarView getBar(boolean isSetUp, Type provisioned, Type unprovisioned) {
+ CarNavigationBarView view;
+ if (isSetUp) {
+ view = getBarCached(provisioned, sLayoutMap.get(provisioned));
+ } else {
+ view = getBarCached(unprovisioned, sLayoutMap.get(unprovisioned));
+ }
+
+ if (view == null) {
+ String name = isSetUp ? provisioned.name() : unprovisioned.name();
+ Log.e(TAG, "CarStatusBar failed inflate for " + name);
+ throw new RuntimeException(
+ "Unable to build " + name + " nav bar due to missing layout");
+ }
+ return view;
+ }
+
+ private CarNavigationBarView getBarCached(Type type, @LayoutRes int barLayout) {
+ if (mCachedViewMap.containsKey(type)) {
+ return mCachedViewMap.get(type);
+ }
+
+ CarNavigationBarView view = (CarNavigationBarView) View.inflate(mContext, barLayout,
+ /* root= */ null);
+ mCachedViewMap.put(type, view);
+ return mCachedViewMap.get(type);
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/AssitantButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/AssitantButton.java
deleted file mode 100644
index b36a7da35acf..000000000000
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/AssitantButton.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.car;
-
-import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.os.Bundle;
-import android.util.AttributeSet;
-import android.util.Log;
-
-import com.android.internal.app.AssistUtils;
-import com.android.internal.app.IVoiceInteractionSessionShowCallback;
-
-/**
- * AssitantButton is a ui component that will trigger the Voice Interaction Service.
- */
-public class AssitantButton extends CarFacetButton {
-
- private static final String TAG = "CarFacetButton";
- private IVoiceInteractionSessionShowCallback mShowCallback =
- new IVoiceInteractionSessionShowCallback.Stub() {
- @Override
- public void onFailed() {
- Log.w(TAG, "Failed to show VoiceInteractionSession");
- }
-
- @Override
- public void onShown() {
- Log.d(TAG, "IVoiceInteractionSessionShowCallback onShown()");
- }
- };
-
- private final AssistUtils mAssistUtils;
-
- public AssitantButton(Context context, AttributeSet attrs) {
- super(context, attrs);
- mAssistUtils = new AssistUtils(context);
- setOnClickListener(v -> {
- showAssistant();
- });
- }
-
- private void showAssistant() {
- final Bundle args = new Bundle();
- mAssistUtils.showSessionForActiveService(args,
- SHOW_SOURCE_ASSIST_GESTURE, mShowCallback, /*activityToken=*/ null);
- }
-
- @Override
- protected void setupIntents(TypedArray typedArray){
- // left blank because for the assistant button Intent will not be passed from the layout.
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
index 58f80a4ed968..d79849ccafc6 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
@@ -257,6 +257,11 @@ public class CarBatteryController extends BroadcastReceiver implements BatteryCo
return false;
}
+ @Override
+ public boolean isAodPowerSave() {
+ return false;
+ }
+
private void notifyBatteryLevelChanged() {
for (int i = 0, size = mChangeCallbacks.size(); i < size; i++) {
mChangeCallbacks.get(i)
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
deleted file mode 100644
index 0421c3bebcfc..000000000000
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.car;
-
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.TypedArray;
-import android.os.Build;
-import android.os.UserHandle;
-import android.util.AttributeSet;
-import android.view.Display;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import com.android.keyguard.AlphaOptimizedImageButton;
-import com.android.systemui.CarSystemUIFactory;
-import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
-
-/**
- * CarFacetButton is a ui component designed to be used as a shortcut for an app of a defined
- * category. It can also render a indicator implying that there are more options of apps to launch
- * using this component. This is done with a "More icon" currently an arrow as defined in the layout
- * file. The class is to serve as an example.
- *
- * New activity will be launched on the same display as the button is on.
- * Usage example: A button that allows a user to select a music app and indicate that there are
- * other music apps installed.
- */
-public class CarFacetButton extends LinearLayout {
- private static final String FACET_FILTER_DELIMITER = ";";
- /**
- * Extra information to be sent to a helper to make the decision of what app to launch when
- * clicked.
- */
- private static final String EXTRA_FACET_CATEGORIES = "categories";
- private static final String EXTRA_FACET_PACKAGES = "packages";
- private static final String EXTRA_FACET_ID = "filter_id";
- private static final String EXTRA_FACET_LAUNCH_PICKER = "launch_picker";
- private static final String TAG = "CarFacetButton";
-
- private Context mContext;
- private AlphaOptimizedImageButton mIcon;
- private AlphaOptimizedImageButton mMoreIcon;
- private boolean mSelected = false;
- private String[] mComponentNames;
- /** App categories that are to be used with this widget */
- private String[] mFacetCategories;
- /** App packages that are allowed to be used with this widget */
- private String[] mFacetPackages;
- private int mIconResourceId;
- /**
- * If defined in the xml this will be the icon that's rendered when the button is marked as
- * selected
- */
- private int mSelectedIconResourceId;
- private boolean mUseMoreIcon = true;
- private float mSelectedAlpha = 1f;
- private float mUnselectedAlpha = 1f;
-
- public CarFacetButton(Context context, AttributeSet attrs) {
- super(context, attrs);
- mContext = context;
- View.inflate(context, R.layout.car_facet_button, this);
- // extract custom attributes
- TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton);
- setupIntents(typedArray);
- setupIcons(typedArray);
- CarSystemUIFactory factory = SystemUIFactory.getInstance();
- CarFacetButtonController carFacetButtonController = factory.getCarDependencyComponent()
- .getCarFacetButtonController();
- carFacetButtonController.addFacetButton(this);
- }
-
- /**
- * Reads the custom attributes to setup click handlers for this component.
- */
- protected void setupIntents(TypedArray typedArray) {
- String intentString = typedArray.getString(R.styleable.CarFacetButton_intent);
- String longPressIntentString = typedArray.getString(R.styleable.CarFacetButton_longIntent);
- String categoryString = typedArray.getString(R.styleable.CarFacetButton_categories);
- String packageString = typedArray.getString(R.styleable.CarFacetButton_packages);
- String componentNameString =
- typedArray.getString(R.styleable.CarFacetButton_componentNames);
- try {
- final Intent intent = Intent.parseUri(intentString, Intent.URI_INTENT_SCHEME);
- intent.putExtra(EXTRA_FACET_ID, Integer.toString(getId()));
-
- if (packageString != null) {
- mFacetPackages = packageString.split(FACET_FILTER_DELIMITER);
- intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages);
- }
- if (categoryString != null) {
- mFacetCategories = categoryString.split(FACET_FILTER_DELIMITER);
- intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories);
- }
- if (componentNameString != null) {
- mComponentNames = componentNameString.split(FACET_FILTER_DELIMITER);
- }
-
- setOnClickListener(v -> {
- ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchDisplayId(mContext.getDisplayId());
- intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, mSelected);
- mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
- mContext.sendBroadcastAsUser(
- new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), UserHandle.CURRENT);
- });
-
- if (longPressIntentString != null && (Build.IS_ENG || Build.IS_USERDEBUG)) {
- final Intent longPressIntent = Intent.parseUri(longPressIntentString,
- Intent.URI_INTENT_SCHEME);
- setOnLongClickListener(v -> {
- ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchDisplayId(mContext.getDisplayId());
- mContext.startActivityAsUser(longPressIntent, options.toBundle(),
- UserHandle.CURRENT);
- mContext.sendBroadcastAsUser(
- new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), UserHandle.CURRENT);
- return true;
- });
- }
- } catch (Exception e) {
- throw new RuntimeException("Failed to attach intent", e);
- }
- }
-
- private void setupIcons(TypedArray styledAttributes) {
- mSelectedAlpha = styledAttributes.getFloat(
- R.styleable.CarFacetButton_selectedAlpha, mSelectedAlpha);
- mUnselectedAlpha = styledAttributes.getFloat(
- R.styleable.CarFacetButton_unselectedAlpha, mUnselectedAlpha);
- mIcon = findViewById(R.id.car_nav_button_icon);
- mIcon.setScaleType(ImageView.ScaleType.CENTER);
- mIcon.setClickable(false);
- mIcon.setAlpha(mUnselectedAlpha);
- mIconResourceId = styledAttributes.getResourceId(R.styleable.CarFacetButton_icon, 0);
- mIcon.setImageResource(mIconResourceId);
- mSelectedIconResourceId = styledAttributes.getResourceId(
- R.styleable.CarFacetButton_selectedIcon, mIconResourceId);
-
- mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
- mMoreIcon.setClickable(false);
- mMoreIcon.setAlpha(mSelectedAlpha);
- mMoreIcon.setVisibility(GONE);
- mUseMoreIcon = styledAttributes.getBoolean(R.styleable.CarFacetButton_useMoreIcon, true);
- }
-
- /**
- * @return The app categories the component represents
- */
- public String[] getCategories() {
- if (mFacetCategories == null) {
- return new String[0];
- }
- return mFacetCategories;
- }
-
- /**
- * @return The valid packages that should be considered.
- */
- public String[] getFacetPackages() {
- if (mFacetPackages == null) {
- return new String[0];
- }
- return mFacetPackages;
- }
-
- /**
- * @return The list of component names.
- */
- public String[] getComponentName() {
- if (mComponentNames == null) {
- return new String[0];
- }
- return mComponentNames;
- }
-
- /**
- * Updates the alpha of the icons to "selected" and shows the "More icon"
- *
- * @param selected true if the view must be selected, false otherwise
- */
- public void setSelected(boolean selected) {
- super.setSelected(selected);
- setSelected(selected, selected);
- }
-
- /**
- * Updates the visual state to let the user know if it's been selected.
- *
- * @param selected true if should update the alpha of the icon to selected, false otherwise
- * @param showMoreIcon true if the "more icon" should be shown, false otherwise. Note this
- * is ignored if the attribute useMoreIcon is set to false
- */
- public void setSelected(boolean selected, boolean showMoreIcon) {
- mSelected = selected;
- mIcon.setAlpha(mSelected ? mSelectedAlpha : mUnselectedAlpha);
- mIcon.setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId);
- if (mUseMoreIcon) {
- mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE);
- }
- }
-
- /**
- * @return The id of the display the button is on or Display.INVALID_DISPLAY if it's not yet on
- * a display.
- */
- public int getDisplayId() {
- Display display = getDisplay();
- if (display == null) {
- return Display.INVALID_DISPLAY;
- }
- return display.getDisplayId();
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
deleted file mode 100644
index 5f99e1750bb6..000000000000
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.car;
-
-import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.view.Display;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/**
- * CarFacetButtons placed on the nav bar are designed to have visual indication that the active
- * application on screen is associated with it. This is basically a similar concept to a radio
- * button group.
- */
-@Singleton
-public class CarFacetButtonController {
-
- protected ButtonMap mButtonsByCategory = new ButtonMap();
- protected ButtonMap mButtonsByPackage = new ButtonMap();
- protected ButtonMap mButtonsByComponentName = new ButtonMap();
- protected HashSet<CarFacetButton> mSelectedFacetButtons;
- protected Context mContext;
-
- @Inject
- public CarFacetButtonController(Context context) {
- mContext = context;
- mSelectedFacetButtons = new HashSet<>();
- }
-
- /**
- * Add facet button to this controller. The expected use is for the facet button
- * to get a reference to this controller via {@link com.android.systemui.Dependency}
- * and self add.
- */
- public void addFacetButton(CarFacetButton facetButton) {
- String[] categories = facetButton.getCategories();
- for (int i = 0; i < categories.length; i++) {
- mButtonsByCategory.add(categories[i], facetButton);
- }
-
- String[] facetPackages = facetButton.getFacetPackages();
- for (int i = 0; i < facetPackages.length; i++) {
- mButtonsByPackage.add(facetPackages[i], facetButton);
- }
- String[] componentNames = facetButton.getComponentName();
- for (int i = 0; i < componentNames.length; i++) {
- mButtonsByComponentName.add(componentNames[i], facetButton);
- }
- }
-
- public void removeAll() {
- mButtonsByCategory.clear();
- mButtonsByPackage.clear();
- mButtonsByComponentName.clear();
- mSelectedFacetButtons.clear();
- }
-
- /**
- * Iterate through a view looking for CarFacetButtons and adding them to the controller if found
- *
- * @param v the View that may contain CarFacetButtons
- */
- public void addAllFacetButtons(View v) {
- if (v instanceof CarFacetButton) {
- addFacetButton((CarFacetButton) v);
- } else if (v instanceof ViewGroup) {
- ViewGroup viewGroup = (ViewGroup) v;
- for (int i = 0; i < viewGroup.getChildCount(); i++) {
- addAllFacetButtons(viewGroup.getChildAt(i));
- }
- }
- }
-
- /**
- * This will unselect the currently selected CarFacetButton and determine which one should be
- * selected next. It does this by reading the properties on the CarFacetButton and seeing if
- * they are a match with the supplied StackInfo list.
- * The order of selection detection is ComponentName, PackageName then Category
- * They will then be compared with the supplied StackInfo list.
- * The StackInfo is expected to be supplied in order of recency and StackInfo will only be used
- * for consideration if it has the same displayId as the CarFacetButtons.
- *
- * @param stackInfoList of the currently running application
- */
- public void taskChanged(List<ActivityManager.StackInfo> stackInfoList) {
- ActivityManager.StackInfo validStackInfo = null;
- for (ActivityManager.StackInfo stackInfo : stackInfoList) {
- // Find the first stack info with a topActivity in the primary display.
- // TODO: We assume that CarFacetButton will launch an app only in the primary display.
- // We need to extend the functionality to handle the mutliple display properly.
- if (stackInfo.topActivity != null && stackInfo.displayId == Display.DEFAULT_DISPLAY) {
- validStackInfo = stackInfo;
- break;
- }
- }
-
- if (validStackInfo == null) {
- // No stack was found that was on the same display as the facet buttons thus return
- return;
- }
-
- if (mSelectedFacetButtons != null) {
- Iterator<CarFacetButton> iterator = mSelectedFacetButtons.iterator();
- while(iterator.hasNext()) {
- CarFacetButton carFacetButton = iterator.next();
- if (carFacetButton.getDisplayId() == validStackInfo.displayId) {
- carFacetButton.setSelected(false);
- iterator.remove();
- }
- }
- }
-
- String packageName = validStackInfo.topActivity.getPackageName();
- HashSet<CarFacetButton> facetButton =
- findFacetButtonByComponentName(validStackInfo.topActivity);
- if (facetButton == null) {
- facetButton = mButtonsByPackage.get(packageName);
- }
-
- if (facetButton == null) {
- String category = getPackageCategory(packageName);
- if (category != null) {
- facetButton = mButtonsByCategory.get(category);
- }
- }
-
- if (facetButton != null) {
- for (CarFacetButton carFacetButton : facetButton) {
- if (carFacetButton.getDisplayId() == validStackInfo.displayId) {
- carFacetButton.setSelected(true);
- mSelectedFacetButtons.add(carFacetButton);
- }
- }
- }
-
- }
-
- private HashSet<CarFacetButton> findFacetButtonByComponentName(ComponentName componentName) {
- HashSet<CarFacetButton> buttons =
- mButtonsByComponentName.get(componentName.flattenToShortString());
- return (buttons != null) ? buttons :
- mButtonsByComponentName.get(componentName.flattenToString());
- }
-
- protected String getPackageCategory(String packageName) {
- PackageManager pm = mContext.getPackageManager();
- Set<String> supportedCategories = mButtonsByCategory.keySet();
- for (String category : supportedCategories) {
- Intent intent = new Intent();
- intent.setPackage(packageName);
- intent.setAction(Intent.ACTION_MAIN);
- intent.addCategory(category);
- List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
- if (list.size() > 0) {
- // Cache this package name into facetPackageMap, so we won't have to query
- // all categories next time this package name shows up.
- mButtonsByPackage.put(packageName, mButtonsByCategory.get(category));
- return category;
- }
- }
- return null;
- }
-
- // simple multi-map
- private static class ButtonMap extends HashMap<String, HashSet<CarFacetButton>> {
-
- public boolean add(String key, CarFacetButton value) {
- if (containsKey(key)) {
- return get(key).add(value);
- }
- HashSet<CarFacetButton> set = new HashSet<>();
- set.add(value);
- put(key, set);
- return true;
- }
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
deleted file mode 100644
index 05a41e68e972..000000000000
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * 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 com.android.systemui.statusbar.car;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.LinearLayout;
-
-import com.android.systemui.Dependency;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
-
-/**
- * A custom navigation bar for the automotive use case.
- * <p>
- * The navigation bar in the automotive use case is more like a list of shortcuts, rendered
- * in a linear layout.
- */
-class CarNavigationBarView extends LinearLayout {
- private View mNavButtons;
- private CarNavigationButton mNotificationsButton;
- private CarStatusBar mCarStatusBar;
- private Context mContext;
- private View mLockScreenButtons;
- // used to wire in open/close gestures for notifications
- private OnTouchListener mStatusBarWindowTouchListener;
-
-
- public CarNavigationBarView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mContext = context;
- }
-
- @Override
- public void onFinishInflate() {
- mNavButtons = findViewById(R.id.nav_buttons);
- mLockScreenButtons = findViewById(R.id.lock_screen_nav_buttons);
-
- mNotificationsButton = findViewById(R.id.notifications);
- if (mNotificationsButton != null) {
- mNotificationsButton.setOnClickListener(this::onNotificationsClick);
- }
- View mStatusIcons = findViewById(R.id.statusIcons);
- if (mStatusIcons != null) {
- // Attach the controllers for Status icons such as wifi and bluetooth if the standard
- // container is in the view.
- StatusBarIconController.DarkIconManager mDarkIconManager =
- new StatusBarIconController.DarkIconManager(
- mStatusIcons.findViewById(R.id.statusIcons));
- mDarkIconManager.setShouldLog(true);
- Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
- }
- // needs to be clickable so that it will receive ACTION_MOVE events
- setClickable(true);
- }
-
- // Used to forward touch events even if the touch was initiated from a child component
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (mStatusBarWindowTouchListener != null) {
- // Forward touch events to the status bar window so it can drag
- // windows if required (Notification shade)
- mStatusBarWindowTouchListener.onTouch(this, ev);
- }
- return super.onInterceptTouchEvent(ev);
- }
-
- void setStatusBar(CarStatusBar carStatusBar) {
- mCarStatusBar = carStatusBar;
- }
-
- /**
- * Set a touch listener that will be called from onInterceptTouchEvent and onTouchEvent
- *
- * @param statusBarWindowTouchListener The listener to call from touch and intercept touch
- */
- void setStatusBarWindowTouchListener(OnTouchListener statusBarWindowTouchListener) {
- mStatusBarWindowTouchListener = statusBarWindowTouchListener;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (mStatusBarWindowTouchListener != null) {
- mStatusBarWindowTouchListener.onTouch(this, event);
- }
- return super.onTouchEvent(event);
- }
-
- protected void onNotificationsClick(View v) {
- mCarStatusBar.togglePanel();
- }
-
- /**
- * If there are buttons declared in the layout they will be shown and the normal
- * Nav buttons will be hidden.
- */
- public void showKeyguardButtons() {
- if (mLockScreenButtons == null) {
- return;
- }
- mLockScreenButtons.setVisibility(View.VISIBLE);
- mNavButtons.setVisibility(View.GONE);
- }
-
- /**
- * If there are buttons declared in the layout they will be hidden and the normal
- * Nav buttons will be shown.
- */
- public void hideKeyguardButtons() {
- if (mLockScreenButtons == null) return;
-
- mNavButtons.setVisibility(View.VISIBLE);
- mLockScreenButtons.setVisibility(View.GONE);
- }
-
- /**
- * Toggles the notification unseen indicator on/off.
- *
- * @param hasUnseen true if the unseen notification count is great than 0.
- */
- void toggleNotificationUnseenIndicator(Boolean hasUnseen) {
- if (mNotificationsButton == null) return;
-
- mNotificationsButton.setUnseen(hasUnseen);
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
deleted file mode 100644
index c0dcbbcf30d4..000000000000
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.car;
-
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.TypedArray;
-import android.os.Build;
-import android.os.UserHandle;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.widget.ImageView;
-
-import com.android.systemui.R;
-
-import java.net.URISyntaxException;
-
-/**
- * CarNavigationButton is an image button that allows for a bit more configuration at the
- * xml file level. This allows for more control via overlays instead of having to update
- * code.
- */
-public class CarNavigationButton extends com.android.keyguard.AlphaOptimizedImageButton {
- private static final String TAG = "CarNavigationButton";
-
- private static final int UNSEEN_ICON_RESOURCE_ID = R.drawable.car_ic_notification_unseen;
- private static final int UNSEEN_SELECTED_ICON_RESOURCE_ID =
- R.drawable.car_ic_notification_selected_unseen;
-
- private Context mContext;
- private String mIntent;
- private String mLongIntent;
- private boolean mBroadcastIntent;
- private boolean mHasUnseen = false;
- private boolean mSelected = false;
- private float mSelectedAlpha = 1f;
- private float mUnselectedAlpha = 1f;
- private int mSelectedIconResourceId;
- private int mIconResourceId;
-
-
- public CarNavigationButton(Context context, AttributeSet attrs) {
- super(context, attrs);
- mContext = context;
-
- // CarNavigationButton attrs
- TypedArray typedArray = context.obtainStyledAttributes(
- attrs, R.styleable.CarNavigationButton);
- mIntent = typedArray.getString(R.styleable.CarNavigationButton_intent);
- mLongIntent = typedArray.getString(R.styleable.CarNavigationButton_longIntent);
- mBroadcastIntent = typedArray.getBoolean(R.styleable.CarNavigationButton_broadcast, false);
- mSelectedAlpha = typedArray.getFloat(
- R.styleable.CarNavigationButton_selectedAlpha, mSelectedAlpha);
- mUnselectedAlpha = typedArray.getFloat(
- R.styleable.CarNavigationButton_unselectedAlpha, mUnselectedAlpha);
- mSelectedIconResourceId = typedArray.getResourceId(
- R.styleable.CarNavigationButton_selectedIcon, mIconResourceId);
- mIconResourceId = typedArray.getResourceId(
- R.styleable.CarNavigationButton_icon, 0);
- typedArray.recycle();
- }
-
-
- /**
- * After the standard inflate this then adds the xml defined intents to click and long click
- * actions if defined.
- */
- @Override
- public void onFinishInflate() {
- super.onFinishInflate();
- setScaleType(ImageView.ScaleType.CENTER);
- setAlpha(mUnselectedAlpha);
- try {
- if (mIntent != null) {
- final Intent intent = Intent.parseUri(mIntent, Intent.URI_INTENT_SCHEME);
- setOnClickListener(v -> {
- try {
- if (mBroadcastIntent) {
- mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
- mContext.sendBroadcastAsUser(
- new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS),
- UserHandle.CURRENT);
- return;
- }
- ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchDisplayId(mContext.getDisplayId());
- mContext.startActivityAsUser(intent, options.toBundle(),
- UserHandle.CURRENT);
- } catch (Exception e) {
- Log.e(TAG, "Failed to launch intent", e);
- }
- });
- }
- } catch (URISyntaxException e) {
- throw new RuntimeException("Failed to attach intent", e);
- }
-
- try {
- if (mLongIntent != null && (Build.IS_ENG || Build.IS_USERDEBUG)) {
- final Intent intent = Intent.parseUri(mLongIntent, Intent.URI_INTENT_SCHEME);
- setOnLongClickListener(v -> {
- try {
- ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchDisplayId(mContext.getDisplayId());
- mContext.startActivityAsUser(intent, options.toBundle(),
- UserHandle.CURRENT);
- } catch (Exception e) {
- Log.e(TAG, "Failed to launch intent", e);
- }
- // consume event either way
- return true;
- });
- }
- } catch (URISyntaxException e) {
- throw new RuntimeException("Failed to attach long press intent", e);
- }
- }
-
- /**
- * @param selected true if should indicate if this is a selected state, false otherwise
- */
- public void setSelected(boolean selected) {
- super.setSelected(selected);
- mSelected = selected;
- setAlpha(mSelected ? mSelectedAlpha : mUnselectedAlpha);
- updateImage();
- }
-
- /**
- * @param hasUnseen true if should indicate if this is a Unseen state, false otherwise.
- */
- public void setUnseen(boolean hasUnseen) {
- mHasUnseen = hasUnseen;
- updateImage();
- }
-
- private void updateImage() {
- if (mHasUnseen) {
- setImageResource(mSelected ? UNSEEN_SELECTED_ICON_RESOURCE_ID
- : UNSEEN_ICON_RESOURCE_ID);
- } else {
- setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId);
- }
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index b0ab5b49f340..52aaf4f41eec 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -16,12 +16,13 @@
package com.android.systemui.statusbar.car;
+import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
import android.car.Car;
import android.car.drivingstate.CarDrivingStateEvent;
import android.car.drivingstate.CarUxRestrictionsManager;
@@ -29,21 +30,16 @@ import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
import android.car.trust.CarTrustAgentEnrollmentManager;
import android.content.Context;
import android.content.res.Configuration;
-import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.inputmethodservice.InputMethodService;
-import android.os.IBinder;
+import android.os.PowerManager;
+import android.util.DisplayMetrics;
import android.util.Log;
-import android.view.Display;
import android.view.GestureDetector;
-import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
import android.view.ViewTreeObserver;
-import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
@@ -57,38 +53,97 @@ import com.android.car.notification.NotificationClickHandlerFactory;
import com.android.car.notification.NotificationDataManager;
import com.android.car.notification.NotificationViewController;
import com.android.car.notification.PreprocessingManager;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.RegisterStatusBarResult;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.CarSystemUIFactory;
import com.android.systemui.Dependency;
+import com.android.systemui.ForegroundServiceController;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.SystemUIFactory;
+import com.android.systemui.UiOffloadThread;
+import com.android.systemui.appops.AppOpsController;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.classifier.FalsingLog;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.doze.DozeLog;
import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.navigationbar.car.CarFacetButtonController;
+import com.android.systemui.navigationbar.car.CarNavigationBarView;
+import com.android.systemui.navigationbar.car.NavigationBarViewFactory;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.car.CarQSFragment;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.statusbar.NavigationBarController;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationViewHierarchyManager;
+import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.car.hvac.HvacController;
import com.android.systemui.statusbar.car.hvac.TemperatureView;
+import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
+import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NewNotifPipeline;
+import com.android.systemui.statusbar.notification.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
+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.LightBarController;
+import com.android.systemui.statusbar.phone.LockscreenWallpaper;
+import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.phone.StatusBarWindowController;
+import com.android.systemui.statusbar.phone.StatusBarWindowViewController;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+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.UserSwitcherController;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.util.InjectionInflationController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Map;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import dagger.Lazy;
+
/**
- * A status bar (and navigation bar) tailored for the automotive use case.
+ * A status bar tailored for the automotive use case.
*/
public class CarStatusBar extends StatusBar implements CarBatteryController.BatteryViewHandler {
private static final String TAG = "CarStatusBar";
@@ -98,21 +153,22 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
private static final float FLING_ANIMATION_MAX_TIME = 0.5f;
// acceleration rate for the fling animation
private static final float FLING_SPEED_UP_FACTOR = 0.6f;
+ private final ScrimController mScrimController;
private float mOpeningVelocity = DEFAULT_FLING_VELOCITY;
private float mClosingVelocity = DEFAULT_FLING_VELOCITY;
- private TaskStackListenerImpl mTaskStackListener;
-
private FullscreenUserSwitcher mFullscreenUserSwitcher;
private CarBatteryController mCarBatteryController;
private BatteryMeterView mBatteryMeterView;
private Drawable mNotificationPanelBackground;
+ private ViewGroup mTopNavigationBarContainer;
private ViewGroup mNavigationBarWindow;
private ViewGroup mLeftNavigationBarWindow;
private ViewGroup mRightNavigationBarWindow;
+ private CarNavigationBarView mTopNavigationBarView;
private CarNavigationBarView mNavigationBarView;
private CarNavigationBarView mLeftNavigationBarView;
private CarNavigationBarView mRightNavigationBarView;
@@ -121,10 +177,10 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
private boolean mShowLeft;
private boolean mShowRight;
private boolean mShowBottom;
+ private final NavigationBarViewFactory mNavigationBarViewFactory;
private CarFacetButtonController mCarFacetButtonController;
- private ActivityManagerWrapper mActivityManagerWrapper;
private DeviceProvisionedController mDeviceProvisionedController;
- private boolean mDeviceIsProvisioned = true;
+ private boolean mDeviceIsSetUpForUser = true;
private HvacController mHvacController;
private DrivingStateHelper mDrivingStateHelper;
private PowerManagerHelper mPowerManagerHelper;
@@ -145,10 +201,12 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
private boolean mNotificationListAtBottom;
// Was the notification list at the bottom when the user first touched the screen
private boolean mNotificationListAtBottomAtTimeOfTouch;
+ // To be attached to the top navigation bar (i.e. status bar) to pull down the notification
+ // panel.
+ private View.OnTouchListener mTopNavBarNotificationTouchListener;
// To be attached to the navigation bars such that they can close the notification panel if
// it's open.
private View.OnTouchListener mNavBarNotificationTouchListener;
-
// Percentage from top of the screen after which the notification shade will open. This value
// will be used while opening the notification shade.
private int mSettleOpenPercentage;
@@ -159,21 +217,16 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
private int mPercentageFromBottom;
// If notification shade is animation to close or to open.
private boolean mIsNotificationAnimating;
-
// Tracks when the notification shade is being scrolled. This refers to the glass pane being
// scrolled not the recycler view.
private boolean mIsTracking;
private float mFirstTouchDownOnGlassPane;
-
// If the notification card inside the recycler view is being swiped.
private boolean mIsNotificationCardSwiping;
// If notification shade is being swiped vertically to close.
private boolean mIsSwipingVerticallyToClose;
// Whether heads-up notifications should be shown when shade is open.
private boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen;
- // If the nav bar should be hidden when the soft keyboard is visible.
- private boolean mHideNavBarForKeyboard;
- private boolean mBottomNavBarVisible;
private final CarPowerStateListener mCarPowerStateListener =
(int state) -> {
@@ -189,27 +242,159 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
}
};
+ @Inject
+ public CarStatusBar(
+ Context context,
+ FeatureFlags featureFlags,
+ LightBarController lightBarController,
+ AutoHideController autoHideController,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ StatusBarIconController statusBarIconController,
+ DozeLog dozeLog,
+ InjectionInflationController injectionInflationController,
+ PulseExpansionHandler pulseExpansionHandler,
+ NotificationWakeUpCoordinator notificationWakeUpCoordinator,
+ KeyguardBypassController keyguardBypassController,
+ KeyguardStateController keyguardStateController,
+ HeadsUpManagerPhone headsUpManagerPhone,
+ DynamicPrivacyController dynamicPrivacyController,
+ BypassHeadsUpNotifier bypassHeadsUpNotifier,
+ @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowNotificationLongPress,
+ Lazy<NewNotifPipeline> newNotifPipeline,
+ FalsingManager falsingManager,
+ BroadcastDispatcher broadcastDispatcher,
+ RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
+ NotificationGutsManager notificationGutsManager,
+ NotificationLogger notificationLogger,
+ NotificationEntryManager notificationEntryManager,
+ NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+ NotificationViewHierarchyManager notificationViewHierarchyManager,
+ ForegroundServiceController foregroundServiceController,
+ AppOpsController appOpsController,
+ KeyguardViewMediator keyguardViewMediator,
+ ZenModeController zenModeController,
+ NotificationAlertingManager notificationAlertingManager,
+ DisplayMetrics displayMetrics,
+ MetricsLogger metricsLogger,
+ UiOffloadThread uiOffloadThread,
+ 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,
+ NotificationGroupAlertTransferHelper groupAlertTransferHelper,
+ VisualStabilityManager visualStabilityManager,
+ DeviceProvisionedController deviceProvisionedController,
+ NavigationBarController navigationBarController,
+ AssistManager assistManager,
+ NotificationListener notificationListener,
+ ConfigurationController configurationController,
+ StatusBarWindowController statusBarWindowController,
+ StatusBarWindowViewController.Builder statusBarWindowViewControllerBuild,
+ NotifLog notifLog,
+ DozeParameters dozeParameters,
+ ScrimController scrimController,
+ Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
+ Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
+ DozeServiceHost dozeServiceHost,
+ PowerManager powerManager,
+ DozeScrimController dozeScrimController,
+
+ /* Car Settings injected components. */
+ NavigationBarViewFactory navigationBarViewFactory) {
+ super(
+ context,
+ featureFlags,
+ lightBarController,
+ autoHideController,
+ keyguardUpdateMonitor,
+ statusBarIconController,
+ dozeLog,
+ injectionInflationController,
+ pulseExpansionHandler,
+ notificationWakeUpCoordinator,
+ keyguardBypassController,
+ keyguardStateController,
+ headsUpManagerPhone,
+ dynamicPrivacyController,
+ bypassHeadsUpNotifier,
+ allowNotificationLongPress,
+ newNotifPipeline,
+ falsingManager,
+ broadcastDispatcher,
+ remoteInputQuickSettingsDisabler,
+ notificationGutsManager,
+ notificationLogger,
+ notificationEntryManager,
+ notificationInterruptionStateProvider,
+ notificationViewHierarchyManager,
+ foregroundServiceController,
+ appOpsController,
+ keyguardViewMediator,
+ zenModeController,
+ notificationAlertingManager,
+ displayMetrics,
+ metricsLogger,
+ uiOffloadThread,
+ notificationMediaManager,
+ lockScreenUserManager,
+ remoteInputManager,
+ userSwitcherController,
+ networkController,
+ batteryController,
+ colorExtractor,
+ screenLifecycle,
+ wakefulnessLifecycle,
+ statusBarStateController,
+ vibratorHelper,
+ bubbleController,
+ groupManager,
+ groupAlertTransferHelper,
+ visualStabilityManager,
+ deviceProvisionedController,
+ navigationBarController,
+ assistManager,
+ notificationListener,
+ configurationController,
+ statusBarWindowController,
+ statusBarWindowViewControllerBuild,
+ notifLog,
+ dozeParameters,
+ scrimController,
+ lockscreenWallpaperLazy,
+ biometricUnlockControllerLazy,
+ dozeServiceHost,
+ powerManager,
+ dozeScrimController);
+ mScrimController = scrimController;
+ mNavigationBarViewFactory = navigationBarViewFactory;
+ }
+
@Override
public void start() {
// get the provisioned state before calling the parent class since it's that flow that
// builds the nav bar
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
- mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned();
-
- // Keyboard related setup, before nav bars are created.
- mHideNavBarForKeyboard = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard);
- mBottomNavBarVisible = false;
+ mDeviceIsSetUpForUser = mDeviceProvisionedController.isCurrentUserSetup();
// Need to initialize screen lifecycle before calling super.start - before switcher is
// created.
mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
mScreenLifecycle.addObserver(mScreenObserver);
+ // Need to initialize HVAC controller before calling super.start - before system bars are
+ // created.
+ mHvacController = new HvacController(mContext);
+
super.start();
- mTaskStackListener = new TaskStackListenerImpl();
- mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
- mActivityManagerWrapper.registerTaskStackListener(mTaskStackListener);
mNotificationPanel.setScrollingEnabled(true);
mSettleOpenPercentage = mContext.getResources().getInteger(
@@ -224,25 +409,18 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
mHvacController.connectToCarService();
- CarSystemUIFactory factory = SystemUIFactory.getInstance();
- if (!mDeviceIsProvisioned) {
- mDeviceProvisionedController.addCallback(
- new DeviceProvisionedController.DeviceProvisionedListener() {
- @Override
- public void onDeviceProvisionedChanged() {
- mHandler.post(() -> {
- // on initial boot we are getting a call even though the value
- // is the same so we are confirming the reset is needed
- boolean deviceProvisioned =
- mDeviceProvisionedController.isDeviceProvisioned();
- if (mDeviceIsProvisioned != deviceProvisioned) {
- mDeviceIsProvisioned = deviceProvisioned;
- restartNavBars();
- }
- });
- }
- });
- }
+ mDeviceProvisionedController.addCallback(
+ new DeviceProvisionedController.DeviceProvisionedListener() {
+ @Override
+ public void onUserSetupChanged() {
+ mHandler.post(() -> restartNavBarsIfNecessary());
+ }
+
+ @Override
+ public void onUserSwitched() {
+ mHandler.post(() -> restartNavBarsIfNecessary());
+ }
+ });
// Register a listener for driving state changes.
mDrivingStateHelper = new DrivingStateHelper(mContext, this::onDrivingStateChanged);
@@ -254,6 +432,14 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
mSwitchToGuestTimer = new SwitchToGuestTimer(mContext);
}
+ private void restartNavBarsIfNecessary() {
+ boolean currentUserSetup = mDeviceProvisionedController.isCurrentUserSetup();
+ if (mDeviceIsSetUpForUser != currentUserSetup) {
+ mDeviceIsSetUpForUser = currentUserSetup;
+ restartNavBars();
+ }
+ }
+
/**
* Remove all content from navbars and rebuild them. Used to allow for different nav bars
* before and after the device is provisioned. . Also for change of density and font size.
@@ -262,29 +448,19 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
// remove and reattach all hvac components such that we don't keep a reference to unused
// ui elements
mHvacController.removeAllComponents();
- addTemperatureViewToController(mStatusBarWindow);
mCarFacetButtonController.removeAll();
+
if (mNavigationBarWindow != null) {
- mNavigationBarWindow.removeAllViews();
mNavigationBarView = null;
}
-
if (mLeftNavigationBarWindow != null) {
- mLeftNavigationBarWindow.removeAllViews();
mLeftNavigationBarView = null;
}
-
if (mRightNavigationBarWindow != null) {
- mRightNavigationBarWindow.removeAllViews();
mRightNavigationBarView = null;
}
buildNavBarContent();
- // If the UI was rebuilt (day/night change) while the keyguard was up we need to
- // correctly respect that state.
- if (mIsKeyguard) {
- updateNavBarForKeyguardContent();
- }
// CarFacetButtonController was reset therefore we need to re-add the status bar elements
// to the controller.
mCarFacetButtonController.addAllFacetButtons(mStatusBarWindow);
@@ -317,7 +493,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
}
}
-
@Override
public boolean hideKeyguard() {
boolean result = super.hideKeyguard();
@@ -357,7 +532,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
@Override
protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
super.makeStatusBarView(result);
- mHvacController = new HvacController(mContext);
CarSystemUIFactory factory = SystemUIFactory.getInstance();
mCarFacetButtonController = factory.getCarDependencyComponent()
@@ -382,7 +556,8 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
* touch listeners needed for opening and closing the notification panel
*/
private void connectNotificationsUI() {
- // Attached to the status bar to detect pull down of the notification shade.
+ // Attached to the top navigation bar (i.e. status bar) to detect pull down of the
+ // notification shade.
GestureDetector openGestureDetector = new GestureDetector(mContext,
new OpenNotificationGestureListener() {
@Override
@@ -415,6 +590,18 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
GestureDetector handleBarCloseNotificationGestureDetector = new GestureDetector(mContext,
new HandleBarCloseNotificationGestureListener());
+ mTopNavBarNotificationTouchListener = (v, event) -> {
+ if (!mDeviceIsSetUpForUser) {
+ return true;
+ }
+ boolean consumed = openGestureDetector.onTouchEvent(event);
+ if (consumed) {
+ return true;
+ }
+ maybeCompleteAnimation(event);
+ return true;
+ };
+
mNavBarNotificationTouchListener =
(v, event) -> {
boolean consumed = navBarCloseNotificationGestureDetector.onTouchEvent(event);
@@ -425,21 +612,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
return true;
};
- // The following are the ui elements that the user would call the status bar.
- // This will set the status bar so it they can make call backs.
- CarNavigationBarView topBar = mStatusBarWindow.findViewById(R.id.car_top_bar);
- topBar.setStatusBar(this);
- topBar.setStatusBarWindowTouchListener((v1, event1) -> {
-
- boolean consumed = openGestureDetector.onTouchEvent(event1);
- if (consumed) {
- return true;
- }
- maybeCompleteAnimation(event1);
- return true;
- }
- );
-
mNotificationClickHandlerFactory = new NotificationClickHandlerFactory(mBarService);
mNotificationClickHandlerFactory.registerClickListener((launchResult, alertEntry) -> {
if (launchResult == ActivityManager.START_TASK_TO_FRONT
@@ -616,7 +788,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
return;
}
mStatusBarWindowController.setStatusBarFocusable(false);
- mStatusBarWindow.cancelExpandHelper();
+ mStatusBarWindowViewController.cancelExpandHelper();
mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor);
animateNotificationPanel(mClosingVelocity, true);
@@ -731,197 +903,55 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
buildNavBarWindows();
buildNavBarContent();
- attachNavBarWindows();
-
- // Try setting up the initial state of the nav bar if applicable.
- if (result != null) {
- setImeWindowStatus(Display.DEFAULT_DISPLAY, result.mImeToken,
- result.mImeWindowVis, result.mImeBackDisposition,
- result.mShowImeSwitcher);
- }
-
- // There has been a car customized nav bar on the default display, so just create nav bars
- // on external displays.
- mNavigationBarController.createNavigationBars(false /* includeDefaultDisplay */, result);
}
private void buildNavBarContent() {
+ buildTopBar();
+
if (mShowBottom) {
- buildBottomBar((mDeviceIsProvisioned) ? R.layout.car_navigation_bar :
- R.layout.car_navigation_bar_unprovisioned);
+ mNavigationBarView = mNavigationBarViewFactory.getBottomBar(mDeviceIsSetUpForUser);
+ mNavigationBarView.setStatusBar(this);
+ mNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
}
if (mShowLeft) {
- buildLeft((mDeviceIsProvisioned) ? R.layout.car_left_navigation_bar :
- R.layout.car_left_navigation_bar_unprovisioned);
+ mLeftNavigationBarView = mNavigationBarViewFactory.getLeftBar(mDeviceIsSetUpForUser);
+ mLeftNavigationBarView.setStatusBar(this);
+ mLeftNavigationBarView.setStatusBarWindowTouchListener(
+ mNavBarNotificationTouchListener);
}
if (mShowRight) {
- buildRight((mDeviceIsProvisioned) ? R.layout.car_right_navigation_bar :
- R.layout.car_right_navigation_bar_unprovisioned);
+ mRightNavigationBarView = mNavigationBarViewFactory.getLeftBar(mDeviceIsSetUpForUser);
+ mRightNavigationBarView.setStatusBar(this);
+ mRightNavigationBarView.setStatusBarWindowTouchListener(
+ mNavBarNotificationTouchListener);
}
}
private void buildNavBarWindows() {
- if (mShowBottom) {
- mNavigationBarWindow = (ViewGroup) View.inflate(mContext,
- R.layout.navigation_bar_window, null);
- }
- if (mShowLeft) {
- mLeftNavigationBarWindow = (ViewGroup) View.inflate(mContext,
- R.layout.navigation_bar_window, null);
- }
- if (mShowRight) {
- mRightNavigationBarWindow = (ViewGroup) View.inflate(mContext,
- R.layout.navigation_bar_window, null);
- }
-
- }
-
- /**
- * We register for soft keyboard visibility events such that we can hide the navigation bar
- * giving more screen space to the IME. Note: this is optional and controlled by
- * {@code com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard}.
- */
- @Override
- public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
- boolean showImeSwitcher) {
- if (!mHideNavBarForKeyboard) {
- return;
- }
+ mTopNavigationBarContainer = mStatusBarWindow
+ .findViewById(R.id.car_top_navigation_bar_container);
- if (mContext.getDisplay().getDisplayId() != displayId) {
- return;
- }
-
- boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
- if (!isKeyboardVisible) {
- attachBottomNavBarWindow();
- } else {
- detachBottomNavBarWindow();
+ if (mShowBottom) {
+ mNavigationBarWindow = mNavigationBarViewFactory.getBottomWindow();
}
- }
-
- private void attachNavBarWindows() {
- attachBottomNavBarWindow();
-
if (mShowLeft) {
- int width = mContext.getResources().getDimensionPixelSize(
- R.dimen.car_left_navigation_bar_width);
- WindowManager.LayoutParams leftlp = new WindowManager.LayoutParams(
- width, LayoutParams.MATCH_PARENT,
- 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);
- leftlp.setTitle("LeftCarNavigationBar");
- leftlp.windowAnimations = 0;
- leftlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
- leftlp.gravity = Gravity.LEFT;
- mWindowManager.addView(mLeftNavigationBarWindow, leftlp);
+ mLeftNavigationBarWindow = mNavigationBarViewFactory.getLeftWindow();
}
if (mShowRight) {
- int width = mContext.getResources().getDimensionPixelSize(
- R.dimen.car_right_navigation_bar_width);
- WindowManager.LayoutParams rightlp = new WindowManager.LayoutParams(
- width, LayoutParams.MATCH_PARENT,
- 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);
- rightlp.setTitle("RightCarNavigationBar");
- rightlp.windowAnimations = 0;
- rightlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
- rightlp.gravity = Gravity.RIGHT;
- mWindowManager.addView(mRightNavigationBarWindow, rightlp);
+ mRightNavigationBarWindow = mNavigationBarViewFactory.getRightWindow();
}
}
- /**
- * Attaches the bottom nav bar window. Can be extended to modify the specific behavior of
- * attaching the bottom nav bar.
- */
- protected void attachBottomNavBarWindow() {
- if (!mShowBottom) {
- return;
- }
+ private void buildTopBar() {
+ mTopNavigationBarContainer.removeAllViews();
+ mTopNavigationBarView = mNavigationBarViewFactory.getTopBar(mDeviceIsSetUpForUser);
+ mTopNavigationBarContainer.addView(mTopNavigationBarView);
- if (mBottomNavBarVisible) {
- return;
- }
- mBottomNavBarVisible = true;
-
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
- 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("CarNavigationBar");
- lp.windowAnimations = 0;
- mWindowManager.addView(mNavigationBarWindow, lp);
- }
-
- /**
- * Detaches the bottom nav bar window. Can be extended to modify the specific behavior of
- * detaching the bottom nav bar.
- */
- protected void detachBottomNavBarWindow() {
- if (!mShowBottom) {
- return;
- }
-
- if (!mBottomNavBarVisible) {
- return;
- }
- mBottomNavBarVisible = false;
- mWindowManager.removeView(mNavigationBarWindow);
- }
-
- private void buildBottomBar(int layout) {
- // SystemUI requires that the navigation bar view have a parent. Since the regular
- // StatusBar inflates navigation_bar_window as this parent view, use the same view for the
- // CarNavigationBarView.
- View.inflate(mContext, layout, mNavigationBarWindow);
- mNavigationBarView = (CarNavigationBarView) mNavigationBarWindow.getChildAt(0);
- if (mNavigationBarView == null) {
- Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
- throw new RuntimeException("Unable to build botom nav bar due to missing layout");
- }
- mNavigationBarView.setStatusBar(this);
- addTemperatureViewToController(mNavigationBarView);
- mNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
- }
-
- private void buildLeft(int layout) {
- View.inflate(mContext, layout, mLeftNavigationBarWindow);
- mLeftNavigationBarView = (CarNavigationBarView) mLeftNavigationBarWindow.getChildAt(0);
- if (mLeftNavigationBarView == null) {
- Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
- throw new RuntimeException("Unable to build left nav bar due to missing layout");
- }
- mLeftNavigationBarView.setStatusBar(this);
- addTemperatureViewToController(mLeftNavigationBarView);
- mLeftNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
- }
-
-
- private void buildRight(int layout) {
- View.inflate(mContext, layout, mRightNavigationBarWindow);
- mRightNavigationBarView = (CarNavigationBarView) mRightNavigationBarWindow.getChildAt(0);
- if (mRightNavigationBarView == null) {
- Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
- throw new RuntimeException("Unable to build right nav bar due to missing layout");
- }
- mRightNavigationBarView.setStatusBar(this);
- addTemperatureViewToController(mRightNavigationBarView);
- mRightNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
+ mTopNavigationBarView.setStatusBar(this);
+ addTemperatureViewToController(mTopNavigationBarView);
+ mTopNavigationBarView.setStatusBarWindowTouchListener(mTopNavBarNotificationTouchListener);
}
@Override
@@ -935,8 +965,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
+ "," + mStackScroller.getScrollY());
}
- pw.print(" mTaskStackListener=");
- pw.println(mTaskStackListener);
pw.print(" mCarFacetButtonController=");
pw.println(mCarFacetButtonController);
pw.print(" mFullscreenUserSwitcher=");
@@ -945,8 +973,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
pw.println(mCarBatteryController);
pw.print(" mBatteryMeterView=");
pw.println(mBatteryMeterView);
- pw.print(" mNavigationBarView=");
- pw.println(mNavigationBarView);
if (Dependency.get(KeyguardUpdateMonitor.class) != null) {
Dependency.get(KeyguardUpdateMonitor.class).dump(fd, pw, args);
@@ -986,32 +1012,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
}
}
- /**
- * An implementation of TaskStackChangeListener, that listens for changes in the system
- * task stack and notifies the navigation bar.
- */
- private class TaskStackListenerImpl extends TaskStackChangeListener {
- @Override
- public void onTaskStackChanged() {
- try {
- mCarFacetButtonController.taskChanged(
- ActivityTaskManager.getService().getAllStackInfos());
- } catch (Exception e) {
- Log.e(TAG, "Getting StackInfo from activity manager failed", e);
- }
- }
-
- @Override
- public void onTaskDisplayChanged(int taskId, int newDisplayId) {
- try {
- mCarFacetButtonController.taskChanged(
- ActivityTaskManager.getService().getAllStackInfos());
- } catch (Exception e) {
- Log.e(TAG, "Getting StackInfo from activity manager failed", e);
- }
- }
- }
-
private void onDrivingStateChanged(CarDrivingStateEvent notUsed) {
// Check if we need to start the timer every time driving state changes.
startSwitchToGuestTimerIfDrivingOnKeyguard();
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index 827a59eddf56..3b482599b2a0 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -18,18 +18,25 @@ package com.android.systemui.statusbar.car;
import static android.content.DialogInterface.BUTTON_NEGATIVE;
import static android.content.DialogInterface.BUTTON_POSITIVE;
+import static android.os.UserManager.DISALLOW_ADD_USER;
+import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.car.userlib.CarUserManagerHelper;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.AsyncTask;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@@ -48,24 +55,33 @@ import com.android.systemui.statusbar.phone.SystemUIDialog;
import java.util.ArrayList;
import java.util.List;
+import java.util.stream.Collectors;
/**
* Displays a GridLayout with icons for the users in the system to allow switching between users.
* One of the uses of this is for the lock screen in auto.
*/
-public class UserGridRecyclerView extends RecyclerView implements
- CarUserManagerHelper.OnUsersUpdateListener {
+public class UserGridRecyclerView extends RecyclerView {
private UserSelectionListener mUserSelectionListener;
private UserAdapter mAdapter;
private CarUserManagerHelper mCarUserManagerHelper;
+ private UserManager mUserManager;
private Context mContext;
+ private final BroadcastReceiver mUserUpdateReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onUsersUpdate();
+ }
+ };
+
public UserGridRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
mCarUserManagerHelper = new CarUserManagerHelper(mContext);
+ mUserManager = UserManager.get(mContext);
- addItemDecoration(new ItemSpacingDecoration(context.getResources().getDimensionPixelSize(
+ addItemDecoration(new ItemSpacingDecoration(mContext.getResources().getDimensionPixelSize(
R.dimen.car_user_switcher_vertical_spacing_between_users)));
}
@@ -75,7 +91,7 @@ public class UserGridRecyclerView extends RecyclerView implements
@Override
public void onFinishInflate() {
super.onFinishInflate();
- mCarUserManagerHelper.registerOnUsersUpdateListener(this);
+ registerForUserEvents();
}
/**
@@ -84,7 +100,7 @@ public class UserGridRecyclerView extends RecyclerView implements
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
- mCarUserManagerHelper.unregisterOnUsersUpdateListener(this);
+ unregisterForUserEvents();
}
/**
@@ -93,12 +109,18 @@ public class UserGridRecyclerView extends RecyclerView implements
* @return the adapter
*/
public void buildAdapter() {
- List<UserRecord> userRecords = createUserRecords(mCarUserManagerHelper
- .getAllUsers());
+ List<UserRecord> userRecords = createUserRecords(getUsersForUserGrid());
mAdapter = new UserAdapter(mContext, userRecords);
super.setAdapter(mAdapter);
}
+ private List<UserInfo> getUsersForUserGrid() {
+ return mUserManager.getUsers(/* excludeDying= */ true)
+ .stream()
+ .filter(UserInfo::supportsSwitchToByUser)
+ .collect(Collectors.toList());
+ }
+
private List<UserRecord> createUserRecords(List<UserInfo> userInfoList) {
List<UserRecord> userRecords = new ArrayList<>();
@@ -114,8 +136,7 @@ public class UserGridRecyclerView extends RecyclerView implements
continue;
}
- boolean isForeground =
- mCarUserManagerHelper.getCurrentForegroundUserId() == userInfo.id;
+ boolean isForeground = ActivityManager.getCurrentUser() == userInfo.id;
UserRecord record = new UserRecord(userInfo, false /* isStartGuestSession */,
false /* isAddUser */, isForeground);
userRecords.add(record);
@@ -125,7 +146,8 @@ public class UserGridRecyclerView extends RecyclerView implements
userRecords.add(createStartGuestUserRecord());
// Add add user record if the foreground user can add users
- if (mCarUserManagerHelper.canForegroundUserAddUsers()) {
+ UserHandle fgUserHandle = UserHandle.of(ActivityManager.getCurrentUser());
+ if (!mUserManager.hasUserRestriction(DISALLOW_ADD_USER, fgUserHandle)) {
userRecords.add(createAddUserRecord());
}
@@ -133,7 +155,7 @@ public class UserGridRecyclerView extends RecyclerView implements
}
private UserRecord createForegroundUserRecord() {
- return new UserRecord(mCarUserManagerHelper.getCurrentForegroundUserInfo(),
+ return new UserRecord(mUserManager.getUserInfo(ActivityManager.getCurrentUser()),
false /* isStartGuestSession */, false /* isAddUser */, true /* isForeground */);
}
@@ -161,13 +183,30 @@ public class UserGridRecyclerView extends RecyclerView implements
mUserSelectionListener = userSelectionListener;
}
- @Override
- public void onUsersUpdate() {
+ private void onUsersUpdate() {
mAdapter.clearUsers();
- mAdapter.updateUsers(createUserRecords(mCarUserManagerHelper.getAllUsers()));
+ mAdapter.updateUsers(createUserRecords(getUsersForUserGrid()));
mAdapter.notifyDataSetChanged();
}
+ private void registerForUserEvents() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_USER_REMOVED);
+ filter.addAction(Intent.ACTION_USER_ADDED);
+ filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
+ mContext.registerReceiverAsUser(
+ mUserUpdateReceiver,
+ UserHandle.ALL, // Necessary because CarSystemUi lives in User 0
+ filter,
+ /* broadcastPermission= */ null,
+ /* scheduler= */ null);
+ }
+
+ private void unregisterForUserEvents() {
+ mContext.unregisterReceiver(mUserUpdateReceiver);
+ }
+
/**
* Adapter to populate the grid layout with the available user profiles
*/
@@ -248,7 +287,7 @@ public class UserGridRecyclerView extends RecyclerView implements
}
private void handleAddUserClicked() {
- if (mCarUserManagerHelper.isUserLimitReached()) {
+ if (!mUserManager.canAddMoreUsers()) {
mAddUserView.setEnabled(true);
showMaxUserLimitReachedDialog();
} else {
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
index 30429eda7be7..e81be1b0b186 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
@@ -38,6 +38,8 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
+import javax.inject.Inject;
+
/**
* Manages the connection to the Car service and delegates value changes to the registered
* {@link TemperatureView}s
@@ -119,6 +121,7 @@ public class HvacController {
}
};
+ @Inject
public HvacController(Context context) {
mContext = context;
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java
index ead1de2bd352..88d641e8bb36 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java
@@ -47,6 +47,7 @@ import java.util.stream.Collectors;
public class OngoingPrivacyChip extends LinearLayout implements View.OnClickListener {
private Context mContext;
+ private Handler mHandler;
private LinearLayout mIconsContainer;
private List<PrivacyItem> mPrivacyItems;
@@ -88,6 +89,7 @@ public class OngoingPrivacyChip extends LinearLayout implements View.OnClickList
private void init(Context context) {
mContext = context;
+ mHandler = new Handler(Looper.getMainLooper());
mPrivacyItems = new ArrayList<>();
sAppOpsController = Dependency.get(AppOpsController.class);
mUserManager = mContext.getSystemService(UserManager.class);
@@ -131,8 +133,7 @@ public class OngoingPrivacyChip extends LinearLayout implements View.OnClickList
@Override
public void onClick(View v) {
updatePrivacyList();
- Handler mUiHandler = new Handler(Looper.getMainLooper());
- mUiHandler.post(() -> {
+ mHandler.post(() -> {
mActivityStarter.postStartActivityDismissingKeyguard(
new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0);
});
@@ -152,21 +153,17 @@ public class OngoingPrivacyChip extends LinearLayout implements View.OnClickList
}
private void updatePrivacyList() {
- mPrivacyItems = mCurrentUserIds.stream()
+ List<PrivacyItem> privacyItems = mCurrentUserIds.stream()
.flatMap(item -> sAppOpsController.getActiveAppOpsForUser(item).stream())
.filter(Objects::nonNull)
.map(item -> toPrivacyItem(item))
.filter(Objects::nonNull)
.collect(Collectors.toList());
- mPrivacyDialogBuilder = new PrivacyDialogBuilder(mContext, mPrivacyItems);
-
- Handler refresh = new Handler(Looper.getMainLooper());
- refresh.post(new Runnable() {
- @Override
- public void run() {
- updateView();
- }
- });
+ if (!privacyItems.equals(mPrivacyItems)) {
+ mPrivacyItems = privacyItems;
+ mPrivacyDialogBuilder = new PrivacyDialogBuilder(mContext, mPrivacyItems);
+ mHandler.post(this::updateView);
+ }
}
private PrivacyItem toPrivacyItem(AppOpItem appOpItem) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java
index 5ec7a77cb305..a5d3bf7cc9c1 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java
@@ -16,28 +16,31 @@
package com.android.systemui.statusbar.car.privacy;
-import android.car.userlib.CarUserManagerHelper;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.util.Log;
+import java.util.Objects;
+
/**
* Class to hold the data for the applications that are using the AppOps permissions.
*/
public class PrivacyApplication {
private static final String TAG = "PrivacyApplication";
+ private String mPackageName;
private Drawable mIcon;
private String mApplicationName;
public PrivacyApplication(String packageName, Context context) {
+ mPackageName = packageName;
try {
- CarUserManagerHelper carUserManagerHelper = new CarUserManagerHelper(context);
ApplicationInfo app = context.getPackageManager()
.getApplicationInfoAsUser(packageName, 0,
- carUserManagerHelper.getCurrentForegroundUserId());
+ ActivityManager.getCurrentUser());
mIcon = context.getPackageManager().getApplicationIcon(app);
mApplicationName = context.getPackageManager().getApplicationLabel(app).toString();
} catch (PackageManager.NameNotFoundException e) {
@@ -59,4 +62,17 @@ public class PrivacyApplication {
public String getApplicationName() {
return mApplicationName;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PrivacyApplication that = (PrivacyApplication) o;
+ return mPackageName.equals(that.mPackageName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPackageName);
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java
index fca137392d74..d3e123ed8c25 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.car.privacy;
+import java.util.Objects;
+
/**
* Class for holding the data of each privacy item displayed in {@link OngoingPrivacyDialog}
*/
@@ -43,4 +45,18 @@ public class PrivacyItem {
public PrivacyType getPrivacyType() {
return mPrivacyType;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PrivacyItem that = (PrivacyItem) o;
+ return mPrivacyType == that.mPrivacyType
+ && mPrivacyApplication.equals(that.mPrivacyApplication);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPrivacyType, mPrivacyApplication);
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java
index 71cc19b63ac1..4d6af95b3f9c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java
@@ -19,15 +19,21 @@ package com.android.systemui.volume;
import android.content.Context;
import com.android.systemui.SystemUI;
+import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.VolumeDialog;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Allows for adding car specific dialog when the volume dialog is created.
*/
+@Singleton
public class CarVolumeDialogComponent extends VolumeDialogComponent {
- public CarVolumeDialogComponent(SystemUI sysui, Context context) {
- super(sysui, context);
+ @Inject
+ public CarVolumeDialogComponent(Context context, KeyguardViewMediator keyguardViewMediator) {
+ super(context, keyguardViewMediator);
}
protected VolumeDialog createDefault() {
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
index d0a63f058291..22c7c7a3d6af 100644
--- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -143,6 +143,7 @@ public class CarVolumeDialogImpl implements VolumeDialog {
private boolean mHovering;
private int mCurrentlyDisplayingGroupId;
private boolean mShowing;
+ private boolean mDismissing;
private boolean mExpanded;
private View mExpandIcon;
private final ServiceConnection mServiceConnection = new ServiceConnection() {
@@ -244,6 +245,7 @@ public class CarVolumeDialogImpl implements VolumeDialog {
mHovering = false;
mShowing = false;
+ mDismissing = false;
mExpanded = false;
mWindow = mDialog.getWindow();
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
@@ -335,14 +337,11 @@ public class CarVolumeDialogImpl implements VolumeDialog {
mHandler.removeMessages(H.DISMISS);
mHandler.removeMessages(H.SHOW);
- if (!mShowing) {
+ if (!mShowing || mDismissing) {
return;
}
- mListView.animate().cancel();
-
- mListView.setTranslationY(0);
- mListView.setAlpha(1);
+ mDismissing = true;
mListView.animate()
.alpha(0)
.translationY(-mListView.getHeight())
@@ -354,7 +353,7 @@ public class CarVolumeDialogImpl implements VolumeDialog {
}
mDialog.dismiss();
mShowing = false;
- mShowing = false;
+ mDismissing = false;
// if mExpandIcon is null that means user never clicked on the expanded arrow
// which implies that the dialog is still not expanded. In that case we do
// not want to reset the state
diff --git a/packages/CarrierDefaultApp/OWNERS b/packages/CarrierDefaultApp/OWNERS
index aef6a3cdf5c8..0d8e69b447e8 100644
--- a/packages/CarrierDefaultApp/OWNERS
+++ b/packages/CarrierDefaultApp/OWNERS
@@ -10,4 +10,5 @@ jminjie@google.com
satk@google.com
shuoq@google.com
refuhoo@google.com
-nazaninb@google.com \ No newline at end of file
+nazaninb@google.com
+sarahchin@google.com
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index 81c5bcd47981..642dc82c4d28 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -35,7 +35,6 @@ import android.net.Uri;
import android.net.http.SslError;
import android.os.Bundle;
import android.telephony.CarrierConfigManager;
-import android.telephony.Rlog;
import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -477,11 +476,11 @@ public class CaptivePortalLoginActivity extends Activity {
}
private static void logd(String s) {
- Rlog.d(TAG, s);
+ Log.d(TAG, s);
}
private static void loge(String s) {
- Rlog.d(TAG, s);
+ Log.d(TAG, s);
}
}
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
index 02c61d778086..46b1d5fe27be 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
@@ -19,7 +19,6 @@ import android.content.Context;
import android.content.Intent;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
-import android.telephony.Rlog;
import android.text.TextUtils;
import android.util.Log;
@@ -27,7 +26,6 @@ import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.ArrayUtils;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
/**
@@ -44,7 +42,7 @@ public class CustomConfigLoader {
private static final String INTER_GROUP_DELIMITER = "\\s*:\\s*";
private static final String TAG = CustomConfigLoader.class.getSimpleName();
- private static final boolean VDBG = Rlog.isLoggable(TAG, Log.VERBOSE);
+ private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
/**
* loads and parses the carrier config, return a list of carrier action for the given signal
@@ -70,7 +68,7 @@ public class CustomConfigLoader {
// return an empty list if no match found
List<Integer> actionList = new ArrayList<>();
if (carrierConfigManager == null) {
- Rlog.e(TAG, "load carrier config failure with carrier config manager uninitialized");
+ Log.e(TAG, "load carrier config failure with carrier config manager uninitialized");
return actionList;
}
PersistableBundle b = carrierConfigManager.getConfig();
@@ -101,8 +99,8 @@ public class CustomConfigLoader {
.EXTRA_DEFAULT_NETWORK_AVAILABLE_KEY, false));
break;
default:
- Rlog.e(TAG, "load carrier config failure with un-configured key: " +
- intent.getAction());
+ Log.e(TAG, "load carrier config failure with un-configured key: "
+ + intent.getAction());
break;
}
if (!ArrayUtils.isEmpty(configs)) {
@@ -111,12 +109,12 @@ public class CustomConfigLoader {
matchConfig(config, arg1, arg2, actionList);
if (!actionList.isEmpty()) {
// return the first match
- if (VDBG) Rlog.d(TAG, "found match action list: " + actionList.toString());
+ if (VDBG) Log.d(TAG, "found match action list: " + actionList.toString());
return actionList;
}
}
}
- Rlog.d(TAG, "no matching entry for signal: " + intent.getAction() + "arg1: " + arg1
+ Log.d(TAG, "no matching entry for signal: " + intent.getAction() + "arg1: " + arg1
+ "arg2: " + arg2);
}
return actionList;
@@ -166,7 +164,7 @@ public class CustomConfigLoader {
try {
actionList.add(Integer.parseInt(idx));
} catch (NumberFormatException e) {
- Rlog.e(TAG, "NumberFormatException(string: " + idx + " config:" + config + "): "
+ Log.e(TAG, "NumberFormatException(string: " + idx + " config:" + config + "): "
+ e);
}
}
diff --git a/packages/EncryptedLocalTransport/Android.bp b/packages/EncryptedLocalTransport/Android.bp
new file mode 100644
index 000000000000..dd30ad177d69
--- /dev/null
+++ b/packages/EncryptedLocalTransport/Android.bp
@@ -0,0 +1,27 @@
+//
+// 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.
+//
+
+android_app {
+ name: "EncryptedLocalTransport",
+ srcs: ["src/**/*.java"],
+ optimize: {
+ proguard_flags_files: ["proguard.flags"],
+ },
+ static_libs: ["LocalTransport"],
+ platform_apis: true,
+ certificate: "platform",
+ privileged: true,
+}
diff --git a/packages/EncryptedLocalTransport/AndroidManifest.xml b/packages/EncryptedLocalTransport/AndroidManifest.xml
new file mode 100644
index 000000000000..dc3617fa6d4e
--- /dev/null
+++ b/packages/EncryptedLocalTransport/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2019 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.encryptedlocaltransport"
+ android:sharedUserId="android.uid.system" >
+
+
+ <application android:allowBackup="false" >
+ <!-- This service does not need to be exported because it shares uid with the system server
+ which is the only client. -->
+ <service android:name=".EncryptedLocalTransportService"
+ android:permission="android.permission.CONFIRM_FULL_BACKUP"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="android.backup.TRANSPORT_HOST" />
+ </intent-filter>
+ </service>
+
+ </application>
+</manifest>
diff --git a/packages/EncryptedLocalTransport/proguard.flags b/packages/EncryptedLocalTransport/proguard.flags
new file mode 100644
index 000000000000..e4ce3c524e35
--- /dev/null
+++ b/packages/EncryptedLocalTransport/proguard.flags
@@ -0,0 +1,2 @@
+-keep class com.android.localTransport.EncryptedLocalTransport
+-keep class com.android.localTransport.EncryptedLocalTransportService
diff --git a/packages/EncryptedLocalTransport/src/com/android/encryptedlocaltransport/EncryptedLocalTransport.java b/packages/EncryptedLocalTransport/src/com/android/encryptedlocaltransport/EncryptedLocalTransport.java
new file mode 100644
index 000000000000..3dd453e83ab8
--- /dev/null
+++ b/packages/EncryptedLocalTransport/src/com/android/encryptedlocaltransport/EncryptedLocalTransport.java
@@ -0,0 +1,109 @@
+/*
+ * 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.encryptedlocaltransport;
+
+import android.app.backup.BackupTransport;
+import android.app.backup.RestoreDescription;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.os.ParcelFileDescriptor;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructStat;
+import android.util.Log;
+
+import com.android.localtransport.LocalTransport;
+import com.android.localtransport.LocalTransportParameters;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+
+public class EncryptedLocalTransport extends LocalTransport {
+ private static final String TAG = "EncryptedLocalTransport";
+ private static final int BACKUP_BUFFER_SIZE = 32 * 1024; // 32 KB.
+
+ public EncryptedLocalTransport(Context context,
+ LocalTransportParameters parameters) {
+ super(context, parameters);
+ }
+
+ @Override
+ public int performBackup(
+ PackageInfo packageInfo, ParcelFileDescriptor data, int flags) {
+ File packageFile;
+ try {
+ StructStat stat = Os.fstat(data.getFileDescriptor());
+ if (stat.st_size > KEY_VALUE_BACKUP_SIZE_QUOTA) {
+ Log.w(TAG, "New datastore size " + stat.st_size
+ + " exceeds quota " + KEY_VALUE_BACKUP_SIZE_QUOTA);
+ return TRANSPORT_QUOTA_EXCEEDED;
+ }
+ } catch (ErrnoException e) {
+ Log.w(TAG, "Failed to stat the backup input file: ", e);
+ return BackupTransport.TRANSPORT_ERROR;
+ }
+
+ clearBackupData(packageInfo);
+
+ try (InputStream in = new FileInputStream(data.getFileDescriptor())) {
+ packageFile = new File(mCurrentSetIncrementalDir, packageInfo.packageName);
+ Files.copy(in, packageFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to save backup data to file: ", e);
+ return BackupTransport.TRANSPORT_ERROR;
+ }
+
+ return TRANSPORT_OK;
+ }
+
+ @Override
+ public int getRestoreData(ParcelFileDescriptor outFd) {
+ if (mRestorePackages == null) {
+ throw new IllegalStateException("startRestore not called");
+ }
+ if (mRestorePackage < 0) {
+ throw new IllegalStateException("nextRestorePackage not called");
+ }
+ if (mRestoreType != RestoreDescription.TYPE_KEY_VALUE) {
+ throw new IllegalStateException("getRestoreData(fd) for non-key/value dataset");
+ }
+
+ try(OutputStream out = new FileOutputStream(outFd.getFileDescriptor())) {
+ File packageFile = new File(mRestoreSetIncrementalDir,
+ mRestorePackages[mRestorePackage].packageName);
+ Files.copy(packageFile.toPath(), out);
+ } catch (IOException e) {
+ Log.d(TAG, "Failed to transfer restore data: " + e);
+ return BackupTransport.TRANSPORT_ERROR;
+ }
+
+ return BackupTransport.TRANSPORT_OK;
+ }
+
+ @Override
+ protected boolean hasRestoreDataForPackage(String packageName) {
+ File contents = (new File(mRestoreSetIncrementalDir, packageName));
+ return contents.exists() && contents.length() != 0;
+
+ }
+}
diff --git a/packages/EncryptedLocalTransport/src/com/android/encryptedlocaltransport/EncryptedLocalTransportService.java b/packages/EncryptedLocalTransport/src/com/android/encryptedlocaltransport/EncryptedLocalTransportService.java
new file mode 100644
index 000000000000..952f90d8b11f
--- /dev/null
+++ b/packages/EncryptedLocalTransport/src/com/android/encryptedlocaltransport/EncryptedLocalTransportService.java
@@ -0,0 +1,47 @@
+/*
+ * 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.encryptedlocaltransport;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+import com.android.localtransport.LocalTransportParameters;
+
+public class EncryptedLocalTransportService extends Service {
+ private static EncryptedLocalTransport sTransport = null;
+
+ @Override
+ public void onCreate() {
+ if (sTransport == null) {
+ LocalTransportParameters parameters =
+ new LocalTransportParameters(getMainThreadHandler(), getContentResolver());
+ sTransport = new EncryptedLocalTransport(this, parameters);
+ }
+ sTransport.getParameters().start();
+ }
+
+ @Override
+ public void onDestroy() {
+ sTransport.getParameters().stop();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return sTransport.getBinder();
+ }
+}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 1b27b52f1fa1..48d34ae7ba47 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -16,6 +16,7 @@
package com.android.externalstorage;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.usage.StorageStatsManager;
import android.content.ContentResolver;
@@ -298,6 +299,53 @@ public class ExternalStorageProvider extends FileSystemProvider {
return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
}
+ /**
+ * Check that the directory is the root of storage or blocked file from tree.
+ *
+ * @param docId the docId of the directory to be checked
+ * @return true, should be blocked from tree. Otherwise, false.
+ */
+ @Override
+ protected boolean shouldBlockFromTree(@NonNull String docId) {
+ try {
+ final File dir = getFileForDocId(docId, true /* visible */).getCanonicalFile();
+ if (!dir.isDirectory()) {
+ return false;
+ }
+
+ final String path = dir.getAbsolutePath();
+
+ // Block Download folder from tree
+ if (MediaStore.Downloads.isDownloadDir(path)) {
+ return true;
+ }
+
+ final ArrayMap<String, RootInfo> roots = new ArrayMap<>();
+
+ synchronized (mRootsLock) {
+ roots.putAll(mRoots);
+ }
+
+ // block root of storage
+ for (int i = 0; i < roots.size(); i++) {
+ RootInfo rootInfo = roots.valueAt(i);
+ // skip home root
+ if (TextUtils.equals(rootInfo.rootId, ROOT_ID_HOME)) {
+ continue;
+ }
+
+ // block the root of storage
+ if (TextUtils.equals(path, rootInfo.visiblePath.getAbsolutePath())) {
+ return true;
+ }
+ }
+ return false;
+ } catch (IOException e) {
+ throw new IllegalArgumentException(
+ "Failed to determine if " + docId + " should block from tree " + ": " + e);
+ }
+ }
+
@Override
protected String getDocIdForFile(File file) throws FileNotFoundException {
return getDocIdForFileMaybeCreate(file, false);
diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
index 4408ef5a2c75..50f858eb04c1 100644
--- a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
+++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
@@ -16,6 +16,7 @@
package com.android.localtransport;
+import android.annotation.Nullable;
import android.app.backup.BackupAgent;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
@@ -71,19 +72,19 @@ public class LocalTransport extends BackupTransport {
// Size quotas at reasonable values, similar to the current cloud-storage limits
private static final long FULL_BACKUP_SIZE_QUOTA = 25 * 1024 * 1024;
- private static final long KEY_VALUE_BACKUP_SIZE_QUOTA = 5 * 1024 * 1024;
+ protected static final long KEY_VALUE_BACKUP_SIZE_QUOTA = 5 * 1024 * 1024;
private Context mContext;
private File mDataDir;
private File mCurrentSetDir;
- private File mCurrentSetIncrementalDir;
+ protected File mCurrentSetIncrementalDir;
private File mCurrentSetFullDir;
- private PackageInfo[] mRestorePackages = null;
- private int mRestorePackage = -1; // Index into mRestorePackages
- private int mRestoreType;
+ protected PackageInfo[] mRestorePackages = null;
+ protected int mRestorePackage = -1; // Index into mRestorePackages
+ protected int mRestoreType;
private File mRestoreSetDir;
- private File mRestoreSetIncrementalDir;
+ protected File mRestoreSetIncrementalDir;
private File mRestoreSetFullDir;
// Additional bookkeeping for full backup
@@ -115,7 +116,7 @@ public class LocalTransport extends BackupTransport {
makeDataDirs();
}
- LocalTransportParameters getParameters() {
+ public LocalTransportParameters getParameters() {
return mParameters;
}
@@ -142,11 +143,18 @@ public class LocalTransport extends BackupTransport {
return null;
}
+ /** @removed Replaced with dataManagementIntentLabel in the API */
public String dataManagementLabel() {
return TRANSPORT_DATA_MANAGEMENT_LABEL;
}
@Override
+ @Nullable
+ public CharSequence dataManagementIntentLabel() {
+ return TRANSPORT_DATA_MANAGEMENT_LABEL;
+ }
+
+ @Override
public String transportDirName() {
return TRANSPORT_DIR_NAME;
}
@@ -537,14 +545,14 @@ public class LocalTransport extends BackupTransport {
int bytesLeft = numBytes;
while (bytesLeft > 0) {
try {
- int nRead = mSocketInputStream.read(mFullBackupBuffer, 0, bytesLeft);
- if (nRead < 0) {
- // Something went wrong if we expect data but saw EOD
- Log.w(TAG, "Unexpected EOD; failing backup");
- return TRANSPORT_ERROR;
- }
- mFullBackupOutputStream.write(mFullBackupBuffer, 0, nRead);
- bytesLeft -= nRead;
+ int nRead = mSocketInputStream.read(mFullBackupBuffer, 0, bytesLeft);
+ if (nRead < 0) {
+ // Something went wrong if we expect data but saw EOD
+ Log.w(TAG, "Unexpected EOD; failing backup");
+ return TRANSPORT_ERROR;
+ }
+ mFullBackupOutputStream.write(mFullBackupBuffer, 0, nRead);
+ bytesLeft -= nRead;
} catch (IOException e) {
Log.e(TAG, "Error handling backup data for " + mFullTargetPackage);
return TRANSPORT_ERROR;
@@ -620,20 +628,15 @@ public class LocalTransport extends BackupTransport {
}
if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
- boolean found = false;
+ boolean found;
while (++mRestorePackage < mRestorePackages.length) {
String name = mRestorePackages[mRestorePackage].packageName;
// If we have key/value data for this package, deliver that
// skip packages where we have a data dir but no actual contents
- String[] contents = (new File(mRestoreSetIncrementalDir, name)).list();
- if (contents != null && contents.length > 0) {
- if (DEBUG) {
- Log.v(TAG, " nextRestorePackage(TYPE_KEY_VALUE) @ "
- + mRestorePackage + " = " + name);
- }
+ found = hasRestoreDataForPackage(name);
+ if (found) {
mRestoreType = RestoreDescription.TYPE_KEY_VALUE;
- found = true;
}
if (!found) {
@@ -664,6 +667,18 @@ public class LocalTransport extends BackupTransport {
return RestoreDescription.NO_MORE_PACKAGES;
}
+ protected boolean hasRestoreDataForPackage(String packageName) {
+ String[] contents = (new File(mRestoreSetIncrementalDir, packageName)).list();
+ if (contents != null && contents.length > 0) {
+ if (DEBUG) {
+ Log.v(TAG, " nextRestorePackage(TYPE_KEY_VALUE) @ "
+ + mRestorePackage + " = " + packageName);
+ }
+ return true;
+ }
+ return false;
+ }
+
@Override
public int getRestoreData(ParcelFileDescriptor outFd) {
if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
index 784be224f367..8b4db92910f5 100644
--- a/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
+++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
@@ -22,7 +22,7 @@ import android.os.Handler;
import android.provider.Settings;
import android.util.KeyValueListParser;
-class LocalTransportParameters extends KeyValueSettingObserver {
+public class LocalTransportParameters extends KeyValueSettingObserver {
private static final String TAG = "LocalTransportParams";
private static final String SETTING = Settings.Secure.BACKUP_LOCAL_TRANSPORT_PARAMETERS;
private static final String KEY_FAKE_ENCRYPTION_FLAG = "fake_encryption_flag";
@@ -31,7 +31,7 @@ class LocalTransportParameters extends KeyValueSettingObserver {
private boolean mFakeEncryptionFlag;
private boolean mIsNonIncrementalOnly;
- LocalTransportParameters(Handler handler, ContentResolver resolver) {
+ public LocalTransportParameters(Handler handler, ContentResolver resolver) {
super(handler, resolver, Settings.Secure.getUriFor(SETTING));
}
diff --git a/packages/PackageInstaller/res/values-television/themes.xml b/packages/PackageInstaller/res/values-television/themes.xml
new file mode 100644
index 000000000000..5ae4957b494d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-television/themes.xml
@@ -0,0 +1,31 @@
+<?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
+ -->
+
+<resources>
+
+ <style name="Theme.AlertDialogActivity.NoAnimation">
+ <item name="android:windowAnimationStyle">@null</item>
+ </style>
+
+ <style name="Theme.AlertDialogActivity"
+ parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
+
+ <style name="Theme.AlertDialogActivity.NoActionBar"
+ parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
+ </style>
+
+</resources>
diff --git a/packages/PrintSpooler/TEST_MAPPING b/packages/PrintSpooler/TEST_MAPPING
new file mode 100644
index 000000000000..4fa882265e53
--- /dev/null
+++ b/packages/PrintSpooler/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsPrintTestCases",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ }
+ ]
+ }
+ ]
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
index c5138c533cd0..17095063c68c 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
@@ -24,7 +24,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Loader;
import android.content.pm.ServiceInfo;
-import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
@@ -265,11 +264,6 @@ public final class FusedPrintersProvider extends Loader<List<PrinterInfo>>
onLocationChanged(lastLocation);
}
- // Jumpstart location with a single forced update
- Criteria oneTimeCriteria = new Criteria();
- oneTimeCriteria.setAccuracy(Criteria.ACCURACY_FINE);
- mLocationManager.requestSingleUpdate(oneTimeCriteria, this, Looper.getMainLooper());
-
// The contract is that if we already have a valid,
// result the we have to deliver it immediately.
(new Handler(Looper.getMainLooper())).post(new Runnable() {
@@ -423,7 +417,7 @@ public final class FusedPrintersProvider extends Loader<List<PrinterInfo>>
}
@Override
- public void onLocationChanged(Location location) {
+ public void onLocationChanged(@Nullable Location location) {
synchronized(mLocationLock) {
// We expect the user to not move too fast while printing. Hence prefer more accurate
// updates over more recent ones for LOCATION_UPDATE_MS. We add a 10% fudge factor here
diff --git a/packages/SettingsLib/SearchWidget/res/values-gl/strings.xml b/packages/SettingsLib/SearchWidget/res/values-gl/strings.xml
index e9e1ae89c654..856ee57f17b1 100644
--- a/packages/SettingsLib/SearchWidget/res/values-gl/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-gl/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="search_menu" msgid="1604061903696928905">"Buscar na configuración"</string>
+ <string name="search_menu" msgid="1604061903696928905">"Busca na configuración"</string>
</resources>
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/ActivityTile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/ActivityTile.java
new file mode 100644
index 000000000000..06a4a45b4853
--- /dev/null
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/ActivityTile.java
@@ -0,0 +1,81 @@
+/*
+ * 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.settingslib.drawer;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ComponentInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Parcel;
+import android.util.Log;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Description of a single dashboard tile which is generated from an activity.
+ */
+public class ActivityTile extends Tile {
+ private static final String TAG = "ActivityTile";
+
+ public ActivityTile(ComponentInfo info, String category) {
+ super(info, category);
+ setMetaData(info.metaData);
+ }
+
+ ActivityTile(Parcel in) {
+ super(in);
+ }
+
+ @Override
+ public int getId() {
+ return Objects.hash(getPackageName(), getComponentName());
+ }
+
+ @Override
+ public String getDescription() {
+ return getPackageName() + "/" + getComponentName();
+ }
+
+ @Override
+ protected CharSequence getComponentLabel(Context context) {
+ final PackageManager pm = context.getPackageManager();
+ final ComponentInfo info = getComponentInfo(context);
+ return info == null
+ ? null
+ : info.loadLabel(pm);
+ }
+
+ @Override
+ protected ComponentInfo getComponentInfo(Context context) {
+ if (mComponentInfo == null) {
+ final PackageManager pm = context.getApplicationContext().getPackageManager();
+ final Intent intent = getIntent();
+ final List<ResolveInfo> infoList =
+ pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
+ if (infoList != null && !infoList.isEmpty()) {
+ mComponentInfo = infoList.get(0).activityInfo;
+ setMetaData(mComponentInfo.metaData);
+ } else {
+ Log.e(TAG, "Cannot find package info for "
+ + intent.getComponent().flattenToString());
+ }
+ }
+ return mComponentInfo;
+ }
+}
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
index ca2d1ed122f9..722f734b8021 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
@@ -30,9 +30,8 @@ import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
+import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Icon;
@@ -47,13 +46,11 @@ import androidx.annotation.VisibleForTesting;
import java.util.ArrayList;
import java.util.Comparator;
-import java.util.List;
-import java.util.Objects;
/**
* Description of a single dashboard tile that the user can select.
*/
-public class Tile implements Parcelable {
+public abstract class Tile implements Parcelable {
private static final String TAG = "Tile";
@@ -64,28 +61,27 @@ public class Tile implements Parcelable {
@VisibleForTesting
long mLastUpdateTime;
- private final String mActivityPackage;
- private final String mActivityName;
+ private final String mComponentPackage;
+ private final String mComponentName;
private final Intent mIntent;
- private ActivityInfo mActivityInfo;
+ protected ComponentInfo mComponentInfo;
private CharSequence mSummaryOverride;
private Bundle mMetaData;
private String mCategory;
- public Tile(ActivityInfo activityInfo, String category) {
- mActivityInfo = activityInfo;
- mActivityPackage = mActivityInfo.packageName;
- mActivityName = mActivityInfo.name;
- mMetaData = activityInfo.metaData;
+ public Tile(ComponentInfo info, String category) {
+ mComponentInfo = info;
+ mComponentPackage = mComponentInfo.packageName;
+ mComponentName = mComponentInfo.name;
mCategory = category;
- mIntent = new Intent().setClassName(mActivityPackage, mActivityName);
+ mIntent = new Intent().setClassName(mComponentPackage, mComponentName);
}
Tile(Parcel in) {
- mActivityPackage = in.readString();
- mActivityName = in.readString();
- mIntent = new Intent().setClassName(mActivityPackage, mActivityName);
+ mComponentPackage = in.readString();
+ mComponentName = in.readString();
+ mIntent = new Intent().setClassName(mComponentPackage, mComponentName);
final int number = in.readInt();
for (int i = 0; i < number; i++) {
userHandle.add(UserHandle.CREATOR.createFromParcel(in));
@@ -101,8 +97,8 @@ public class Tile implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mActivityPackage);
- dest.writeString(mActivityName);
+ dest.writeString(mComponentPackage);
+ dest.writeString(mComponentName);
final int size = userHandle.size();
dest.writeInt(size);
for (int i = 0; i < size; i++) {
@@ -112,16 +108,22 @@ public class Tile implements Parcelable {
dest.writeBundle(mMetaData);
}
- public int getId() {
- return Objects.hash(mActivityPackage, mActivityName);
- }
+ /**
+ * Unique ID of the tile
+ */
+ public abstract int getId();
- public String getDescription() {
- return mActivityPackage + "/" + mActivityName;
- }
+ /**
+ * Human-readable description of the tile
+ */
+ public abstract String getDescription();
public String getPackageName() {
- return mActivityPackage;
+ return mComponentPackage;
+ }
+
+ public String getComponentName() {
+ return mComponentName;
}
/**
@@ -154,7 +156,7 @@ public class Tile implements Parcelable {
}
/**
- * Check whether title has order.
+ * Check whether tile has order.
*/
public boolean hasOrder() {
return mMetaData.containsKey(META_DATA_KEY_ORDER)
@@ -170,14 +172,14 @@ public class Tile implements Parcelable {
final PackageManager packageManager = context.getPackageManager();
if (mMetaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
if (mMetaData.containsKey(META_DATA_PREFERENCE_TITLE_URI)) {
- // If has as uri to provide dynamic summary, skip loading here. UI will later load
+ // If has as uri to provide dynamic title, skip loading here. UI will later load
// at tile binding time.
return null;
}
if (mMetaData.get(META_DATA_PREFERENCE_TITLE) instanceof Integer) {
try {
final Resources res =
- packageManager.getResourcesForApplication(mActivityPackage);
+ packageManager.getResourcesForApplication(mComponentPackage);
title = res.getString(mMetaData.getInt(META_DATA_PREFERENCE_TITLE));
} catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
Log.w(TAG, "Couldn't find info", e);
@@ -186,18 +188,15 @@ public class Tile implements Parcelable {
title = mMetaData.getString(META_DATA_PREFERENCE_TITLE);
}
}
- // Set the preference title to the activity's label if no
- // meta-data is found
+ // Set the preference title by the component if no meta-data is found
if (title == null) {
- final ActivityInfo activityInfo = getActivityInfo(context);
- if (activityInfo == null) {
- return null;
- }
- title = activityInfo.loadLabel(packageManager);
+ title = getComponentLabel(context);
}
return title;
}
+ protected abstract CharSequence getComponentLabel(Context context);
+
/**
* Overrides the summary. This can happen when injected tile wants to provide dynamic summary.
*/
@@ -225,7 +224,7 @@ public class Tile implements Parcelable {
if (mMetaData.get(META_DATA_PREFERENCE_SUMMARY) instanceof Integer) {
try {
final Resources res =
- packageManager.getResourcesForApplication(mActivityPackage);
+ packageManager.getResourcesForApplication(mComponentPackage);
summary = res.getString(mMetaData.getInt(META_DATA_PREFERENCE_SUMMARY));
} catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
Log.d(TAG, "Couldn't find info", e);
@@ -281,24 +280,24 @@ public class Tile implements Parcelable {
return null;
}
ensureMetadataNotStale(context);
- final ActivityInfo activityInfo = getActivityInfo(context);
- if (activityInfo == null) {
- Log.w(TAG, "Cannot find ActivityInfo for " + getDescription());
+ final ComponentInfo componentInfo = getComponentInfo(context);
+ if (componentInfo == null) {
+ Log.w(TAG, "Cannot find ComponentInfo for " + getDescription());
return null;
}
int iconResId = mMetaData.getInt(META_DATA_PREFERENCE_ICON);
// Set the icon
if (iconResId == 0) {
- // Only fallback to activityinfo.icon if metadata does not contain ICON_URI.
+ // Only fallback to componentInfo.icon if metadata does not contain ICON_URI.
// ICON_URI should be loaded in app UI when need the icon object. Handling IPC at this
// level is too complex because we don't have a strong threading contract for this class
if (!mMetaData.containsKey(META_DATA_PREFERENCE_ICON_URI)) {
- iconResId = activityInfo.icon;
+ iconResId = componentInfo.icon;
}
}
if (iconResId != 0) {
- final Icon icon = Icon.createWithResource(activityInfo.packageName, iconResId);
+ final Icon icon = Icon.createWithResource(componentInfo.packageName, iconResId);
if (isIconTintable(context)) {
final TypedArray a = context.obtainStyledAttributes(new int[]{
android.R.attr.colorControlNormal});
@@ -331,41 +330,26 @@ public class Tile implements Parcelable {
final PackageManager pm = context.getApplicationContext().getPackageManager();
try {
- final long lastUpdateTime = pm.getPackageInfo(mActivityPackage,
+ final long lastUpdateTime = pm.getPackageInfo(mComponentPackage,
PackageManager.GET_META_DATA).lastUpdateTime;
if (lastUpdateTime == mLastUpdateTime) {
// All good. Do nothing
return;
}
// App has been updated since we load metadata last time. Reload metadata.
- mActivityInfo = null;
- getActivityInfo(context);
+ mComponentInfo = null;
+ getComponentInfo(context);
mLastUpdateTime = lastUpdateTime;
} catch (PackageManager.NameNotFoundException e) {
Log.d(TAG, "Can't find package, probably uninstalled.");
}
}
- private ActivityInfo getActivityInfo(Context context) {
- if (mActivityInfo == null) {
- final PackageManager pm = context.getApplicationContext().getPackageManager();
- final Intent intent = new Intent().setClassName(mActivityPackage, mActivityName);
- final List<ResolveInfo> infoList =
- pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
- if (infoList != null && !infoList.isEmpty()) {
- mActivityInfo = infoList.get(0).activityInfo;
- mMetaData = mActivityInfo.metaData;
- } else {
- Log.e(TAG, "Cannot find package info for "
- + intent.getComponent().flattenToString());
- }
- }
- return mActivityInfo;
- }
+ protected abstract ComponentInfo getComponentInfo(Context context);
public static final Creator<Tile> CREATOR = new Creator<Tile>() {
public Tile createFromParcel(Parcel source) {
- return new Tile(source);
+ return new ActivityTile(source);
}
public Tile[] newArray(int size) {
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
index aced5ef9429a..c4df2e8860b4 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.IContentProvider;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
@@ -127,6 +128,7 @@ public class TileUtils {
*/
public static final String META_DATA_PREFERENCE_ICON_BACKGROUND_HINT =
"com.android.settings.bg.hint";
+
/**
* Name of the meta-data item that should be set in the AndroidManifest.xml
* to specify the icon background color as raw ARGB.
@@ -220,10 +222,11 @@ public class TileUtils {
public static List<DashboardCategory> getCategories(Context context,
Map<Pair<String, String>, Tile> cache) {
final long startTime = System.currentTimeMillis();
- boolean setup = Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0)
- != 0;
- ArrayList<Tile> tiles = new ArrayList<>();
- UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ final boolean setup =
+ Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0) != 0;
+ final ArrayList<Tile> tiles = new ArrayList<>();
+ final UserManager userManager = (UserManager) context.getSystemService(
+ Context.USER_SERVICE);
for (UserHandle user : userManager.getUserProfiles()) {
// TODO: Needs much optimization, too many PM queries going on here.
if (user.getIdentifier() == ActivityManager.getCurrentUser()) {
@@ -240,7 +243,7 @@ public class TileUtils {
}
}
- HashMap<String, DashboardCategory> categoryMap = new HashMap<>();
+ final HashMap<String, DashboardCategory> categoryMap = new HashMap<>();
for (Tile tile : tiles) {
final String categoryKey = tile.getCategory();
DashboardCategory category = categoryMap.get(categoryKey);
@@ -255,7 +258,7 @@ public class TileUtils {
}
category.addTile(tile);
}
- ArrayList<DashboardCategory> categories = new ArrayList<>(categoryMap.values());
+ final ArrayList<DashboardCategory> categories = new ArrayList<>(categoryMap.values());
for (DashboardCategory category : categories) {
category.sortTiles();
}
@@ -275,63 +278,90 @@ public class TileUtils {
if (requireSettings) {
intent.setPackage(SETTING_PKG);
}
+ getActivityTiles(context, user, addedCache, defaultCategory, outTiles, intent);
+ }
+
+ private static void getActivityTiles(Context context,
+ UserHandle user, Map<Pair<String, String>, Tile> addedCache,
+ String defaultCategory, List<Tile> outTiles, Intent intent) {
final PackageManager pm = context.getPackageManager();
- List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
+ final List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
PackageManager.GET_META_DATA, user.getIdentifier());
for (ResolveInfo resolved : results) {
if (!resolved.system) {
// Do not allow any app to add to settings, only system ones.
continue;
}
- ActivityInfo activityInfo = resolved.activityInfo;
- Bundle metaData = activityInfo.metaData;
- String categoryKey = defaultCategory;
-
- // Load category
- if ((metaData == null || !metaData.containsKey(EXTRA_CATEGORY_KEY))
- && categoryKey == null) {
- Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for intent "
- + intent + " missing metadata "
- + (metaData == null ? "" : EXTRA_CATEGORY_KEY));
- continue;
- } else {
- categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);
- }
+ final ActivityInfo activityInfo = resolved.activityInfo;
+ final Bundle metaData = activityInfo.metaData;
+ getTile(user, addedCache, defaultCategory, outTiles, intent, metaData, activityInfo);
+ }
+ }
- Pair<String, String> key = new Pair<>(activityInfo.packageName, activityInfo.name);
- Tile tile = addedCache.get(key);
- if (tile == null) {
- tile = new Tile(activityInfo, categoryKey);
- addedCache.put(key, tile);
- } else {
- tile.setMetaData(metaData);
- }
+ private static void getTile(UserHandle user, Map<Pair<String, String>, Tile> addedCache,
+ String defaultCategory, List<Tile> outTiles, Intent intent, Bundle metaData,
+ ComponentInfo componentInfo) {
+ String categoryKey = defaultCategory;
+ // Load category
+ if ((metaData == null || !metaData.containsKey(EXTRA_CATEGORY_KEY))
+ && categoryKey == null) {
+ Log.w(LOG_TAG, "Found " + componentInfo.name + " for intent "
+ + intent + " missing metadata "
+ + (metaData == null ? "" : EXTRA_CATEGORY_KEY));
+ return;
+ } else {
+ categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);
+ }
- if (!tile.userHandle.contains(user)) {
- tile.userHandle.add(user);
- }
- if (!outTiles.contains(tile)) {
- outTiles.add(tile);
- }
+ final Pair<String, String> key = new Pair<>(componentInfo.packageName, componentInfo.name);
+ Tile tile = addedCache.get(key);
+ if (tile == null) {
+ tile = new ActivityTile(componentInfo, categoryKey);
+ addedCache.put(key, tile);
+ } else {
+ tile.setMetaData(metaData);
+ }
+
+ if (!tile.userHandle.contains(user)) {
+ tile.userHandle.add(user);
+ }
+ if (!outTiles.contains(tile)) {
+ outTiles.add(tile);
}
}
/**
+ * Returns the complete uri from the meta data key of the tile.
+
+ * @param tile Tile which contains meta data
+ * @param metaDataKey Key mapping to the uri in meta data
+ * @return Uri associated with the key
+ */
+ public static Uri getCompleteUri(Tile tile, String metaDataKey) {
+ final String uriString = tile.getMetaData().getString(metaDataKey);
+ if (TextUtils.isEmpty(uriString)) {
+ return null;
+ }
+
+ return Uri.parse(uriString);
+ }
+
+ /**
* Gets the icon package name and resource id from content provider.
*
* @param context context
* @param packageName package name of the target activity
- * @param uriString URI for the content provider
+ * @param uri URI for the content provider
* @param providerMap Maps URI authorities to providers
* @return package name and resource id of the icon specified
*/
public static Pair<String, Integer> getIconFromUri(Context context, String packageName,
- String uriString, Map<String, IContentProvider> providerMap) {
- Bundle bundle = getBundleFromUri(context, uriString, providerMap);
+ Uri uri, Map<String, IContentProvider> providerMap) {
+ final Bundle bundle = getBundleFromUri(context, uri, providerMap, null);
if (bundle == null) {
return null;
}
- String iconPackageName = bundle.getString(EXTRA_PREFERENCE_ICON_PACKAGE);
+ final String iconPackageName = bundle.getString(EXTRA_PREFERENCE_ICON_PACKAGE);
if (TextUtils.isEmpty(iconPackageName)) {
return null;
}
@@ -342,7 +372,7 @@ public class TileUtils {
// Icon can either come from the target package or from the Settings app.
if (iconPackageName.equals(packageName)
|| iconPackageName.equals(context.getPackageName())) {
- return Pair.create(iconPackageName, bundle.getInt(META_DATA_PREFERENCE_ICON, 0));
+ return Pair.create(iconPackageName, resId);
}
return null;
}
@@ -351,34 +381,33 @@ public class TileUtils {
* Gets text associated with the input key from the content provider.
*
* @param context context
- * @param uriString URI for the content provider
+ * @param uri URI for the content provider
* @param providerMap Maps URI authorities to providers
* @param key Key mapping to the text in bundle returned by the content provider
* @return Text associated with the key, if returned by the content provider
*/
- public static String getTextFromUri(Context context, String uriString,
+ public static String getTextFromUri(Context context, Uri uri,
Map<String, IContentProvider> providerMap, String key) {
- Bundle bundle = getBundleFromUri(context, uriString, providerMap);
+ final Bundle bundle = getBundleFromUri(context, uri, providerMap, null);
return (bundle != null) ? bundle.getString(key) : null;
}
- private static Bundle getBundleFromUri(Context context, String uriString,
- Map<String, IContentProvider> providerMap) {
- if (TextUtils.isEmpty(uriString)) {
+ private static Bundle getBundleFromUri(Context context, Uri uri,
+ Map<String, IContentProvider> providerMap, Bundle bundle) {
+ if (uri == null) {
return null;
}
- Uri uri = Uri.parse(uriString);
- String method = getMethodFromUri(uri);
+ final String method = getMethodFromUri(uri);
if (TextUtils.isEmpty(method)) {
return null;
}
- IContentProvider provider = getProviderFromUri(context, uri, providerMap);
+ final IContentProvider provider = getProviderFromUri(context, uri, providerMap);
if (provider == null) {
return null;
}
try {
return provider.call(context.getPackageName(), uri.getAuthority(),
- method, uriString, null);
+ method, uri.toString(), bundle);
} catch (RemoteException e) {
return null;
}
@@ -389,7 +418,7 @@ public class TileUtils {
if (uri == null) {
return null;
}
- String authority = uri.getAuthority();
+ final String authority = uri.getAuthority();
if (TextUtils.isEmpty(authority)) {
return null;
}
@@ -400,7 +429,7 @@ public class TileUtils {
}
/** Returns the first path segment of the uri if it exists as the method, otherwise null. */
- static String getMethodFromUri(Uri uri) {
+ private static String getMethodFromUri(Uri uri) {
if (uri == null) {
return null;
}
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 4cedc93f4680..245ca140b4f0 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tablet kan binnekort afgaan (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Toestel kan binnekort afgaan (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> oor tot vol gelaai"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot vol gelaai"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> oor tot battery gelaai is"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot battery gelaai is"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Onbekend"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Laai"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"laai tans"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 3b7abdbf1eec..bfd31968bd6c 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"ጡባዊው በቅርቡ ሊዘጋ ይችላል (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"መሣሪያው በቅርቡ ሊዘጋ ይችላል (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"ሙሉ ኃይል እስኪሞላ ድረስ <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - ሙሉ ለሙሉ እስኪሞላ ድረስ <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> ኃይል እስከሚሞላ ድረስ ይቀራል"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ኃይል እስከሚሞላ ድረስ"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"ያልታወቀ"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"ኃይል በመሙላት ላይ"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"ኃይል በመሙላት ላይ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index df1ec30a0718..8b67b0b1347e 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"قد يتم إغلاق الجهاز اللوحي بعد قليل (<xliff:g id="LEVEL">%1$s</xliff:g>)."</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"قد يتم إغلاق الجهاز بعد قليل (<xliff:g id="LEVEL">%1$s</xliff:g>)."</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"يتبقى <xliff:g id="TIME">%1$s</xliff:g> لشحن البطارية بالكامل"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> حتى يكتمل الشحن"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> إلى أن يتم شحن الجهاز بالكامل"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> إلى أن يتم شحن الجهاز بالكامل"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"غير معروف"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"جارٍ الشحن"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"جارٍ الشحن"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 9c25b66cb6aa..f3ca337b261d 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"টেবলেটটো সোনকালেই বন্ধ হ\'ব পাৰে (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"ডিভাইচটো সোনকালেই বন্ধ হ\'ব পাৰে (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"সম্পূৰ্ণকৈ চ্চাৰ্জ হ\'বলৈ <xliff:g id="TIME">%1$s</xliff:g> বাকী"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> সম্পূৰ্ণৰূপে চ্চাৰ্জ হোৱা পৰ্যন্ত"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"চাৰ্জ হ’বলৈ <xliff:g id="TIME">%1$s</xliff:g> বাকী আছে"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> চাৰ্জ হ\'বলৈ"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"অজ্ঞাত"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"চাৰ্জ কৰি থকা হৈছে"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"চ্চাৰ্জ হৈ আছে"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 3a6d301b6f8d..d35dfe8255d2 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Planşet tezliklə sönə bilər (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Cihaz tezliklə sönə bilər (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Tam enerji yığmağına <xliff:g id="TIME">%1$s</xliff:g> qalıb"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tam enerji yığana kimi"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Enerjinin dolmasına <xliff:g id="TIME">%1$s</xliff:g> qalıb"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - Enerjinin dolmasına <xliff:g id="TIME">%2$s</xliff:g> qalıb"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Naməlum"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Enerji doldurma"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"enerji yığır"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index a8808fa42cca..448de4b8b7dd 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tablet će se uskoro isključiti (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Uređaj će se uskoro isključiti (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> do potpunog punjenja"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do potpunog punjenja"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Napuniće se za <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – napuniće se za <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Nepoznato"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Puni se"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"puni se"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 05d9a5d2f5db..d68c0f3e02ab 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Узровень зараду батарэі: <xliff:g id="LEVEL">%1$s</xliff:g>. Планшэт хутка спыніць працу."</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Узровень зараду батарэі: <xliff:g id="LEVEL">%1$s</xliff:g>. Прылада хутка спыніць працу."</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Да поўнай зарадкі засталося <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> да поўнай зарадкі"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Засталося <xliff:g id="TIME">%1$s</xliff:g> да поўнай зарадкі"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> да поўнай зарадкі"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Невядома"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Зарадка"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"ідзе зарадка"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 7fa5b0639073..cb99f64ba498 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Възможно е таблетът да се изключи скоро (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Възможно е устройството да се изключи скоро (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Оставащо време до пълно зареждане: <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до пълно зареждане"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Оставащо време до пълно зареждане: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до пълно зареждане"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Неизвестно"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Зарежда се"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"зарежда се"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index f1fc9f9e3d4e..f2f4f5249b74 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"ট্যাবলেটটি শীঘ্রই বন্ধ হয়ে যেতে পারে (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"ডিভাইসটি শীঘ্রই বন্ধ হয়ে যেতে পারে (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"সম্পূর্ণ চার্জ হতে <xliff:g id="TIME">%1$s</xliff:g> বাকি"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - সম্পূর্ণ চার্জ হতে <xliff:g id="TIME">%2$s</xliff:g> লাগবে"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"সম্পূর্ণ চার্জ হতে <xliff:g id="TIME">%1$s</xliff:g> বাকি আছে"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>-এ সম্পূর্ণ চার্জ হয়ে যাবে"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"অজানা"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"চার্জ হচ্ছে"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"চার্জ হচ্ছে"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 02b2a8bd8480..45b8dd99ec70 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tablet će se uskoro isključiti (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Uređaj će se uskoro isključiti (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Još <xliff:g id="TIME">%1$s</xliff:g> do potpune napunjenosti"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> do potpune napunjenosti"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Napunit će se za <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – napunit će se za <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Nepoznato"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Punjenje"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"punjenje"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 264954465981..b624df056bec 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"És possible que la tauleta s\'apagui aviat (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"És possible que el dispositiu s\'apagui aviat (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> per completar la càrrega"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> per completar la càrrega"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Desconegut"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"S\'està carregant"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"s\'està carregant"</string>
@@ -414,7 +414,7 @@
<string name="disabled" msgid="9206776641295849915">"Desactivat"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Amb permís"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Sense permís"</string>
- <string name="install_other_apps" msgid="6986686991775883017">"Instal·lar aplicacions desconegudes"</string>
+ <string name="install_other_apps" msgid="6986686991775883017">"Instal·la aplicacions desconegudes"</string>
<string name="home" msgid="3256884684164448244">"Pàgina d\'inici de configuració"</string>
<string-array name="battery_labels">
<item msgid="8494684293649631252">"0%"</item>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 525639985c5b..042e12a14967 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tablet se brzy vypne (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Zařízení se brzy vypne (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Plně se nabije za <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – plně se nabije za <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Do nabití zbývá: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do nabití"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Neznámé"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Nabíjí se"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"nabíjení"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 1884f645fcf1..4723293f302d 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Denne tablet lukker muligvis snart ned (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Enheden lukker muligvis snart ned (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"fuldt opladet om <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – fuldt opladet om <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Opladet om <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – opladet om <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Ukendt"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Oplader"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"oplader"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index d8da638fcd2b..4f5a965f9ce0 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tablet wird eventuell bald ausgeschaltet (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Gerät wird eventuell bald ausgeschaltet (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> bis zur vollständigen Aufladung"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> bis vollständig geladen"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Noch <xliff:g id="TIME">%1$s</xliff:g> bis zur Aufladung"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> bis zur Aufladung"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Unbekannt"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Wird aufgeladen"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"wird aufgeladen..."</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index b1b1be642eba..753dea8855dd 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Το tablet μπορεί να απενεργοποιηθεί σύντομα (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Η συσκευή μπορεί να απενεργοποιηθεί σύντομα (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Απομένουν <xliff:g id="TIME">%1$s</xliff:g> έως την πλήρη φόρτιση"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για πλήρη φόρτιση"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Απομένουν <xliff:g id="TIME">%1$s</xliff:g> για ολοκλήρωση της φόρτισης"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για την ολοκλήρωση της φόρτισης"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Άγνωστο"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Φόρτιση"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"φόρτιση"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 63463b1b31f1..dd3d27803db3 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tablet may shutdown soon (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Device may shutdown soon (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> left until fully charged"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> until fully charged"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> left until charged"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until charged"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Unknown"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Charging"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"charging"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 63463b1b31f1..dd3d27803db3 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tablet may shutdown soon (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Device may shutdown soon (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> left until fully charged"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> until fully charged"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> left until charged"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until charged"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Unknown"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Charging"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"charging"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 63463b1b31f1..dd3d27803db3 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tablet may shutdown soon (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Device may shutdown soon (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> left until fully charged"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> until fully charged"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> left until charged"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until charged"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Unknown"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Charging"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"charging"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 63463b1b31f1..dd3d27803db3 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tablet may shutdown soon (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Device may shutdown soon (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> left until fully charged"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> until fully charged"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> left until charged"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until charged"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Unknown"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Charging"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"charging"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index e5a0bda2fee4..d9f61d8188f1 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‏‏‎‎‏‎‎‎‏‏‏‏‏‎‎‎‏‏‎‎‎‏‎‎‎‎‏‎‏‎‏‏‏‏‎‎‏‏‏‎‏‎‎‏‏‏‏‏‎‎‎‎Tablet may shutdown soon (‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎)‎‏‎‎‏‎"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‎‎‎‏‏‎‎‎‎‏‏‎‎‏‏‎‏‎‎‏‎‏‎‏‏‎‏‎‎‎‏‏‏‎‏‏‏‎‏‏‏‎‏‎‏‏‏‎‎‎‏‏‏‎‎‎‎‏‎Device may shutdown soon (‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎)‎‏‎‎‏‎"</string>
<string name="power_charging" msgid="1779532561355864267">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‎‎‏‎‏‏‎‎‏‎‎‎‏‎‏‎‏‏‎‏‏‎‏‏‏‏‏‎‏‎‏‎‎‏‏‏‎‏‏‎‏‏‎‏‏‏‏‎‎‎‏‏‎‎‏‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - ‎‏‎‎‏‏‎<xliff:g id="STATE">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‏‏‎‏‏‏‎‎‎‏‏‎‎‎‏‎‏‎‎‏‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‎‏‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="TIME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ left until fully charged‎‏‎‎‏‎"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‎‏‎‎‎‎‎‎‎‏‎‏‎‏‎‏‏‎‎‏‏‏‏‏‏‎‎‎‎‎‏‏‎‏‏‎‏‏‏‎‎‏‏‏‎‎‎‏‎‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - ‎‏‎‎‏‏‎<xliff:g id="TIME">%2$s</xliff:g>‎‏‎‎‏‏‏‎ until fully charged‎‏‎‎‏‎"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‏‏‎‎‎‏‎‎‎‏‏‏‎‎‏‎‏‎‏‏‏‎‎‏‎‏‏‏‎‎‏‏‏‎‎‏‏‏‏‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‎‏‎‎‏‏‎<xliff:g id="TIME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ left until charged‎‏‎‎‏‎"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎‎‏‎‎‎‎‎‏‎‏‏‎‏‎‎‏‎‏‏‎‏‎‏‎‏‏‎‎‎‏‎‎‎‏‎‎‏‏‏‏‏‎‎‏‏‏‎‎‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - ‎‏‎‎‏‏‎<xliff:g id="TIME">%2$s</xliff:g>‎‏‎‎‏‏‏‎ until charged‎‏‎‎‏‎"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‎‏‎‏‏‏‎‎‎‏‏‎‎‏‎‏‏‏‎‏‏‏‏‏‎‎‎‎‎‏‏‏‎‎‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‎‎‏‏‎‎‏‏‎Unknown‎‏‎‎‏‎"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‏‏‏‏‎‏‎‏‎‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‎‏‏‏‎‎‎‏‎‏‎‏‏‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎Charging‎‏‎‎‏‎"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‏‎‎‎‎‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‏‏‎‎‏‎‏‎‏‎‏‏‏‎‎‎‏‏‎‏‏‎‏‎‏‎‎charging‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 7874aeb02996..30cb0a1f113f 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Es posible que pronto se apague la tablet (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Es posible que pronto se apague el dispositivo (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> para completar la carga"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g> para completar la carga)"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> para completar la carga"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la carga"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Desconocido"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Cargando"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"cargando"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 8baf7a21c1ae..32905dfb9213 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Es posible que el tablet se apague pronto (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Es posible que el dispositivo se apague pronto (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Tiempo restante hasta carga completa: <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la carga"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> hasta que termine de cargarse"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g> hasta que termine de cargarse)"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Desconocido"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Cargando"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"cargando"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 090b28da22f0..79b8a848f870 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tahvelarvuti võib peagi välja lülituda (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Seade võib peagi välja lülituda (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> täislaadimiseni"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> täislaadimiseni"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Täislaadimiseni on jäänud <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> täislaadimiseni"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Tundmatu"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Laadimine"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"laadimine"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index fd3936d39ed3..38ae9c243f6e 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -311,7 +311,7 @@
<string name="debug_layout" msgid="5981361776594526155">"Erakutsi diseinu-mugak"</string>
<string name="debug_layout_summary" msgid="2001775315258637682">"Erakutsi kliparen mugak, marjinak, etab."</string>
<string name="force_rtl_layout_all_locales" msgid="2259906643093138978">"Behartu eskuin-ezker norabidea"</string>
- <string name="force_rtl_layout_all_locales_summary" msgid="9192797796616132534">"Behartu pantaila-diseinuaren norabidea eskuin-ezker izatera eskualdeko ezarpen guztiekin"</string>
+ <string name="force_rtl_layout_all_locales_summary" msgid="9192797796616132534">"Behartu pantaila-diseinuaren norabidea eskuin-ezker izatera lurraldeko ezarpen guztiekin"</string>
<string name="force_msaa" msgid="7920323238677284387">"Behartu 4x MSAA"</string>
<string name="force_msaa_summary" msgid="9123553203895817537">"Gaitu 4x MSAA, OpenGL ES 2.0 aplikazioetan"</string>
<string name="show_non_rect_clip" msgid="505954950474595172">"Araztu angeluzuzenak ez diren klip-eragiketak"</string>
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Baliteke tableta laster itzaltzea (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Baliteke gailua laster itzaltzea (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> falta dira guztiz kargatu arte"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> guztiz kargatu arte"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> guztiz kargatu arte"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> guztiz kargatu arte"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Ezezaguna"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Kargatzen"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"kargatzen"</string>
diff --git a/packages/SettingsLib/res/values-fa/arrays.xml b/packages/SettingsLib/res/values-fa/arrays.xml
index 2019cafdf90e..b89ea0747700 100644
--- a/packages/SettingsLib/res/values-fa/arrays.xml
+++ b/packages/SettingsLib/res/values-fa/arrays.xml
@@ -211,7 +211,7 @@
<item msgid="1069584980746680398">"مقیاس پویانمایی 10 برابر"</item>
</string-array>
<string-array name="overlay_display_devices_entries">
- <item msgid="1606809880904982133">"هیچ‌کدام"</item>
+ <item msgid="1606809880904982133">"خالی"</item>
<item msgid="9033194758688161545">"480p"</item>
<item msgid="1025306206556583600">"‏480p (ایمن)"</item>
<item msgid="1853913333042744661">"720p"</item>
@@ -225,7 +225,7 @@
<item msgid="1311305077526792901">"‏720p،‏ 1080p (صفحه‌نمایش دوتایی)"</item>
</string-array>
<string-array name="enable_opengl_traces_entries">
- <item msgid="3191973083884253830">"هیچ‌کدام"</item>
+ <item msgid="3191973083884253830">"خالی"</item>
<item msgid="9089630089455370183">"Logcat"</item>
<item msgid="5397807424362304288">"‏Systrace (تصاویر گرافیکی)"</item>
<item msgid="1340692776955662664">"‏فراخوانی پشته در glGetError"</item>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 352c27683834..a883d6cddf47 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -21,7 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"اسکن شبکه‌ها امکان‌پذیر نیست"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"هیچ‌کدام"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"خالی"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"ذخیره‌شده"</string>
<string name="wifi_disconnected" msgid="8085419869003922556">"اتصال قطع شد"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"غیرفعال شد"</string>
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"ممکن است رایانه لوحی به‌زودی خاموش شود (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"ممکن است دستگاه به‌زودی خاموش شود (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - ‏<xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> تا شارژ شدن کامل باقی مانده است"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> مانده تا شارژ کامل"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> مانده تا شارژ کامل"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"ناشناس"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"در حال شارژ شدن"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"درحال شارژ شدن"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 0d522c4015a4..929e101bf610 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tabletti voi pian sammua (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Laite voi pian sammua (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> kunnes täynnä"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> täyteen lataukseen"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> jäljellä täyteen lataukseen"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> täyteen lataukseen"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Tuntematon"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Ladataan"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"ladataan"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 1dde8632fbc9..d3bfc96ea473 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Il se peut que la tablette s\'éteigne bientôt (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Il se peut que l\'appareil s\'éteigne bientôt (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> jusqu\'à la charge complète"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> : <xliff:g id="TIME">%2$s</xliff:g> jusqu\'à la charge complète"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> jusqu\'à la charge complète"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> : <xliff:g id="TIME">%2$s</xliff:g> jusqu\'à la charge complète"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Inconnu"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Charge en cours…"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"en cours de charge"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index cf4ee1302872..b5706cea932d 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Il est possible que la tablette s\'éteigne bientôt (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Il est possible que l\'appareil s\'éteigne bientôt (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> avant charge complète"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> jusqu\'à la charge complète"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> jusqu\'à ce que la batterie soit chargée"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> jusqu\'à la charge complète"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Inconnu"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Batterie en charge"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"en charge…"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index b998a74700b5..2aa1604cca21 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"É posible que a tableta se apague en breve (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"É posible que o dispositivo se apague en breve (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Tempo que queda para completar a carga: <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar a carga"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> para completar a carga"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar a carga"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Descoñecido"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Cargando"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"cargando"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index cf0e2016736b..c2cc4c5d9410 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"ટૅબ્લેટ થોડીક જ વારમાં બંધ થઈ શકે છે (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"ઉપકરણ થોડીક જ વારમાં બંધ થઈ શકે છે (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"સંપૂર્ણપણે ચાર્જ થવામાં <xliff:g id="TIME">%1$s</xliff:g> બાકી"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - સંપૂર્ણપણે ચાર્જ થવા માટે <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"ચાર્જ થવામાં <xliff:g id="TIME">%1$s</xliff:g> બાકી છે"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જ થવા માટે <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"અજાણ્યું"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"ચાર્જ થઈ રહ્યું છે"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"ચાર્જ થઈ રહ્યું છે"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 3a20d04d7ab2..66c7a16e1bf6 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"टैबलेट जल्दी ही बंद हो सकता है (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"डिवाइस जल्दी ही बंद हो सकता है (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"पूरी तरह से चार्ज होने में <xliff:g id="TIME">%1$s</xliff:g> शेष"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> पूरी तरह से चार्ज होने तक"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"चार्ज पूरा होने में <xliff:g id="TIME">%1$s</xliff:g> बचा है"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> में पूरा चार्ज हो जाएगा"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"अज्ञात"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"चार्ज हो रही है"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"चार्ज किया जा रहा है"</string>
@@ -414,7 +414,7 @@
<string name="disabled" msgid="9206776641295849915">"बंद किया गया"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"अनुमति है"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"अनुमति नहीं है"</string>
- <string name="install_other_apps" msgid="6986686991775883017">"अनजान ऐप्लिकेशन इंस्टॉल करने का ऐक्सेस"</string>
+ <string name="install_other_apps" msgid="6986686991775883017">"अज्ञात ऐप्लिकेशन इंस्टॉल करने का ऐक्सेस"</string>
<string name="home" msgid="3256884684164448244">"सेटिंग का होम पेज"</string>
<string-array name="battery_labels">
<item msgid="8494684293649631252">"0%"</item>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 6d957dcf60f8..0fc16f847822 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tablet bi se uskoro mogao isključiti (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Uređaj bi se uskoro mogao isključiti (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Još <xliff:g id="TIME">%1$s</xliff:g> do potpune napunjenosti"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do potpune napunjenosti"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Napunit će se za <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – napunit će se za <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Nepoznato"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Punjenje"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"punjenje"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 39b5da26f55f..1388dbb684fa 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Előfordulhat, hogy a táblagép hamarosan leáll (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Előfordulhat, hogy az eszköz hamarosan leáll (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> a teljes töltöttségig"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> a teljes feltöltésig"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> van hátra a feltöltésig"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a feltöltésig"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Ismeretlen"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Töltés"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"töltés"</string>
@@ -414,7 +414,7 @@
<string name="disabled" msgid="9206776641295849915">"Letiltva"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Engedélyezett"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Nem engedélyezett"</string>
- <string name="install_other_apps" msgid="6986686991775883017">"Új alkalmazások telepítése"</string>
+ <string name="install_other_apps" msgid="6986686991775883017">"Ismeretlen alkalmazások telepítése"</string>
<string name="home" msgid="3256884684164448244">"Beállítások kezdőlapja"</string>
<string-array name="battery_labels">
<item msgid="8494684293649631252">"0%"</item>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 16d7ea4352cf..2145adb1036e 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Պլանշետը շուտով կանջատվի (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Սարքը շուտով կանջատվի (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Մինչև լրիվ լիցքավորումը մնացել է <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> մինչև լիցքավորումը"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> մինչև լիցքավորումը"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Անհայտ"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Լիցքավորում"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"լիցքավորում"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 02c4c8c6f44d..7ebe6b7f8180 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -328,7 +328,7 @@
<string name="app_process_limit_title" msgid="4280600650253107163">"Batas proses background"</string>
<string name="show_all_anrs" msgid="4924885492787069007">"Tampilkan ANR background"</string>
<string name="show_all_anrs_summary" msgid="6636514318275139826">"Tampilkan dialog Aplikasi Tidak Merespons untuk aplikasi yang berjalan di background"</string>
- <string name="show_notification_channel_warnings" msgid="1399948193466922683">"Tampilkan peringatan channel notifikasi"</string>
+ <string name="show_notification_channel_warnings" msgid="1399948193466922683">"Tampilkan peringatan saluran notifikasi"</string>
<string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"Menampilkan peringatan di layar saat aplikasi memposting notifikasi tanpa channel yang valid"</string>
<string name="force_allow_on_external" msgid="3215759785081916381">"Paksa izinkan aplikasi di eksternal"</string>
<string name="force_allow_on_external_summary" msgid="3640752408258034689">"Membuat semua aplikasi dapat ditulis ke penyimpanan eksternal, terlepas dari nilai manifes"</string>
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tablet mungkin segera dimatikan (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Perangkat mungkin segera dimatikan (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> lagi hingga terisi penuh"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lagi terisi penuh"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Sisa <xliff:g id="TIME">%1$s</xliff:g> hingga terisi penuh"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lagi terisi penuh"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Tidak diketahui"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Mengisi daya"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"mengisi daya baterai"</string>
@@ -414,7 +414,7 @@
<string name="disabled" msgid="9206776641295849915">"Dinonaktifkan"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Diizinkan"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Tidak diizinkan"</string>
- <string name="install_other_apps" msgid="6986686991775883017">"Menginstal aplikasi yang tidak dikenal"</string>
+ <string name="install_other_apps" msgid="6986686991775883017">"Instal aplikasi tidak dikenal"</string>
<string name="home" msgid="3256884684164448244">"Layar Utama Setelan"</string>
<string-array name="battery_labels">
<item msgid="8494684293649631252">"0%"</item>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 11d69571aeeb..eede11756e9e 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Spjaldtölvan gæti slökkt á sér fljótlega (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Tækið gæti slökkt á sér fljótlega (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> þar til hleðslu er lokið"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> þar til fullri hleðslu er náð"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> að fullri hleðslu"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> að fullri hleðslu"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Óþekkt"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Í hleðslu"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"í hleðslu"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index a43e8d6aa4e4..2c64ec2e6705 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Il tablet potrebbe spegnersi a breve (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Il dispositivo potrebbe spegnersi a breve (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Tempo rimanente alla carica completa: <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> alla carica completa"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Tempo rimanente alla carica completa: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> alla carica completa"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Sconosciuta"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"In carica"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"in carica"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index a3e049c8b144..30d6a4aa1d5e 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"ייתכן שהטאבלט ייכבה בקרוב (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"ייתכן שהמכשיר ייכבה בקרוב (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g>‏ - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> עד לטעינה מלאה"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> עד לטעינה מלאה"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"נשארו <xliff:g id="TIME">%1$s</xliff:g> עד הטעינה"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> עד הטעינה"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"לא ידוע"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"בטעינה"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"בטעינה"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 50543d8bfbee..6cb5514cc582 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"タブレットの電源がもうすぐ切れます(<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"デバイスの電源がもうすぐ切れます(<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"フル充電まであと <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - フル充電まで <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"充電完了まであと <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電完了まで <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"不明"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"充電中"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"充電しています"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 8b9b6b653c6c..4c78a3864562 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"ტაბლეტი შეიძლება მალე გაითიშოს (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"მოწყობილობა შეიძლება მალე გაითიშოს (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"სრულ დატენვამდე დარჩენილია <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> — სრულ დატენვამდე დარჩა <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"დატენვამდე დარჩა <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> დატენვამდე"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"უცნობი"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"იტენება"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"იტენება"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 246e3eb3c532..ce086577ec00 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Планшет көп ұзамай өшуі мүмкін (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Құрылғы көп ұзамай өшуі мүмкін (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Толық зарядталуына <xliff:g id="TIME">%1$s</xliff:g> қалды"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – толық зарядталғанға дейін <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Зарядталғанға дейін <xliff:g id="TIME">%1$s</xliff:g> қалды"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядталғанға дейін <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Белгісіз"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Зарядталуда"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"зарядталуда"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 36cf2d473faa..43c92824015b 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"ថេប្លេត​អាចនឹង​បិទក្នុង​ពេលបន្តិច​ទៀត (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"ឧបករណ៍​អាចនឹង​បិទក្នុង​ពេលបន្តិច​ទៀត (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> ទៀតទើបសាកថ្មពេញ"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ទៀតទើបសាកថ្មពេញ"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> ទៀតទើប​ត្រូវសាក"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ទៀតទើប​ត្រូវសាក"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"មិន​ស្គាល់"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"កំពុងបញ្ចូល​ថ្ម"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"កំពុង​សាក​ថ្ម"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 97eed0fb40a4..253104b38661 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"ಟ್ಯಾಬ್ಲೆಟ್‌‌ ಶೀಘ್ರದಲ್ಲೇ ಶಟ್ ಡೌನ್ ಆಗಬಹುದು (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"ಸಾಧನವು ಶೀಘ್ರದಲ್ಲೇ ಶಟ್ ಡೌನ್ ಆಗಬಹುದು (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"ಸಂಪೂರ್ಣ ಚಾರ್ಜ್ ಆಗಲು <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಸಂಪೂರ್ಣ ಚಾರ್ಜ್ ಆಗಲು <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"ಚಾರ್ಜ್ ಆಗಲು <xliff:g id="TIME">%1$s</xliff:g> ಸಮಯ ಬಾಕಿ ಇದೆ"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಚಾರ್ಜ್ ಆಗಲು <xliff:g id="TIME">%2$s</xliff:g> ಸಮಯ ಬೇಕು"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"ಅಪರಿಚಿತ"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string>
@@ -414,7 +414,7 @@
<string name="disabled" msgid="9206776641295849915">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"ಅನುಮತಿಸಲಾಗಿದೆ"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"ಅನುಮತಿಸಲಾಗುವುದಿಲ್ಲ"</string>
- <string name="install_other_apps" msgid="6986686991775883017">"ಅಪರಿಚಿತ ಆಪ್‌ಗಳನ್ನು ಸ್ಥಾಪಿಸಿ"</string>
+ <string name="install_other_apps" msgid="6986686991775883017">"ಅಪರಿಚಿತ ಆ್ಯಪ್‍‍ಗಳನ್ನು ಸ್ಥಾಪಿಸಿ"</string>
<string name="home" msgid="3256884684164448244">"ಸೆಟ್ಟಿಂಗ್‌ಗಳ ಮುಖಪುಟ"</string>
<string-array name="battery_labels">
<item msgid="8494684293649631252">"0%"</item>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 8f8bbc1a4721..ac44c0db62a3 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"태블릿이 곧 종료될 수 있음(<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"기기가 곧 종료될 수 있음(<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"충전 완료까지 <xliff:g id="TIME">%1$s</xliff:g> 남음"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - 충전 완료까지 <xliff:g id="TIME">%2$s</xliff:g> 남음"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"충전 완료까지 <xliff:g id="TIME">%1$s</xliff:g> 남음"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - 충전 완료까지 <xliff:g id="TIME">%2$s</xliff:g> 남음"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"알 수 없음"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"충전 중"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"충전 중"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 435ce5357b60..dd1ff30faf44 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -101,7 +101,7 @@
<string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Файл өткөрүү серверине туташкан жок"</string>
<string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Киргизүү түзмөгүнө туташты"</string>
<string name="bluetooth_pan_user_profile_summary_connected" msgid="6436258151814414028">"Интернетке мүмкүнчүлүк алуу үчүн түзмөккө туташты"</string>
- <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1322694224800769308">"Жергиликтүү Интернет туташуусу түзмөк менен бөлүшүлүүдө"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1322694224800769308">"Түзмөк модем катары иштөөдө"</string>
<string name="bluetooth_pan_profile_summary_use_for" msgid="5736111170225304239">"Интернетке туташуу үчүн колдонулсун"</string>
<string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"MAP үчүн колдонуу"</string>
<string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"SIM картаны пайдалануу үчүн колдонуу"</string>
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Планшет бир аздан кийин өчүп калышы мүмкүн (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Түзмөк бир аздан кийин өчүп калышы мүмкүн (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Батарея толгонго чейин калган убакыт: <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> кийин толук кубатталат"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> кийин кубатталат"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> кийин кубатталат"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Белгисиз"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Кубатталууда"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"кубатталууда"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 1cee8fbe5a04..28e811153ea1 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"ແທັບເລັດອາດຈະປິດໃນໄວໆນີ້ (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"ອຸປະກອນອາດຈະປິດໃນໄວໆນີ້ (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> ຈົນກວ່າຈະສາກເຕັມ"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈົນກວ່າຈະສາກເຕັມ"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> ຈົນກວ່າຈະສາກເຕັມ"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈົນກວ່າຈະສາກເຕັມ"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"ບໍ່ຮູ້ຈັກ"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"ກຳລັງສາກໄຟ"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"ກຳລັງສາກໄຟ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index c10bd002eae7..93fcaa798998 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Planšetinis kompiuteris netrukus gali būti išjungtas (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Įrenginys netrukus gali būti išjungtas (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Iki visiškos įkrovos liko <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> iki visiško įkrovimo"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Iki visiškos įkrovos liko <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – iki visiškos įkrovos liko <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Nežinomas"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Kraunasi..."</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"įkraunama"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 96aebc59c556..c12d097232f4 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Iespējams, planšetdators drīz izslēgsies (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Iespējams, ierīce drīz izslēgsies (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Atlikušais laiks līdz pilnai uzlādei: <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> līdz pilnīgai uzlādei"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Vēl <xliff:g id="TIME">%1$s</xliff:g> līdz pilnai uzlādei"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai uzlādei"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Nezināms"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Uzlāde"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"notiek uzlāde"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index d93304fc3bf6..9a76a0a8e04c 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Таблетот може да се исклучи наскоро (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Уредот може да се исклучи наскоро (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Преостануваат <xliff:g id="TIME">%1$s</xliff:g> дури се наполни целосно"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> дури се наполни целосно"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Уште <xliff:g id="TIME">%1$s</xliff:g> до целосно полнење"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> до целосно полнење"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Непознато"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Се полни"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"се полни"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 7016b7f1a6e8..bb762951d437 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"ടാബ്‌ലെറ്റ് ഉടൻ ഷട്ട് ഡൗൺ ആയേക്കാം (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"ഉപകരണം ഉടൻ ഷട്ട്ഡൗൺ ആയേക്കാം (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"മുഴുവൻ ചാർജാകാൻ <xliff:g id="TIME">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - ഫുൾ ചാർജാകാൻ <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"പൂർണ്ണമായി ചാർജാവാൻ <xliff:g id="TIME">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - പൂർണ്ണമായി ചാർജാവാൻ <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"അജ്ഞാതം"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"ചാർജ് ചെയ്യുന്നു"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"ചാർജ് ചെയ്യുന്നു"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 80e78a364db1..65a8ca6c1253 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Таблет удахгүй унтарна (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Төхөөрөмж удахгүй унтарна (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Бүрэн цэнэглэх хүртэл <xliff:g id="TIME">%1$s</xliff:g> үлдсэн"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"бүрэн цэнэглэх хүртэл <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Цэнэглэх хүртэл үлдсэн <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - цэнэглэх хүртэл <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Тодорхойгүй"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Цэнэглэж байна"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"цэнэглэж байна"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 54723a80b51c..3d75ad6d158f 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"टॅबलेट लवकरच बंद होऊ शकतो (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"डिव्हाइस लवकरच बंद पडू शकते (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"पूर्णपणे चार्ज होण्यास <xliff:g id="TIME">%1$s</xliff:g> शिल्‍लक"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - पूर्णपणे चार्ज होण्यात <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> पर्यंत पूर्ण चार्ज होईल"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> पर्यंत पूर्ण चार्ज होईल"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"अज्ञात"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"चार्ज होत आहे"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"चार्ज होत आहे"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 3841a3b3f6b5..0b2a4b0879c4 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tablet mungkin ditutup tidak lama lagi (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Peranti mungkin ditutup tidak lama lagi (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> lagi sehingga dicas penuh"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga dicas penuh"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> lagi sehingga dicas penuh"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga dicas"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Tidak diketahui"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Mengecas"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"mengecas"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 38f128a8cb46..6fde69a38767 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"မကြာမီ တက်ဘလက် ပိတ်သွားနိုင်သည် (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"မကြာမီ စက်ပိတ်သွားနိုင်သည် (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"အားပြည့်ရန် <xliff:g id="TIME">%1$s</xliff:g> လိုပါသည်"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> − အားပြည့်ရန် <xliff:g id="TIME">%2$s</xliff:g> ကျန်သည်"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"အားပြည့်ရန် <xliff:g id="TIME">%1$s</xliff:g> ကျန်သည်"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားပြည့်ရန် <xliff:g id="TIME">%2$s</xliff:g> ကျန်သည်"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"မသိ"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"အားသွင်းနေပါသည်"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"အားသွင်းနေပါသည်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 144d4c249dac..66ad20e18839 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Nettbrettet slås kanskje av snart (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Enheten slås kanskje av snart (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> til det er fulladet"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til det er fulladet"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> til batteriet er fulladet"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til batteriet er fulladet"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Ukjent"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Lader"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"lader"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index b2e1cd62b963..860e9bf6b1b7 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"ट्याब्लेट चाँडै बन्द हुन सक्छ (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"यन्त्र चाँडै बन्द हुन सक्छ (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"पूर्णरूपमा चार्ज हुन <xliff:g id="TIME">%1$s</xliff:g> बाँकी"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - पूर्णरूपमा चार्ज हुन <xliff:g id="TIME">%2$s</xliff:g> बाँकी"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"पूर्ण चार्ज हुन <xliff:g id="TIME">%1$s</xliff:g> बाँकी"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"पूर्ण चार्ज हुन <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> लाग्छ"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"अज्ञात"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"चार्ज हुँदै"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"चार्ज हुँदै"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index c50b43a04724..2fff3283d5fd 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tablet wordt binnenkort mogelijk uitgeschakeld (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Apparaat wordt binnenkort mogelijk uitgeschakeld (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Nog <xliff:g id="TIME">%1$s</xliff:g> tot volledig opgeladen"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot volledig opgeladen"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Nog <xliff:g id="TIME">%1$s</xliff:g> tot opgeladen"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot opgeladen"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Onbekend"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Opladen"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"opladen"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index f9e0d39f60c1..ff2a5349f40f 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"ଖୁବ୍ ଶୀଘ୍ର ଟାବଲେଟ୍‌ଟି ବନ୍ଦ ହୋଇଯାଇପାରେ (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"ଖୁବ୍ ଶୀଘ୍ର ଡିଭାଇସ୍‌ଟି ବନ୍ଦ ହୋଇଯାଇପାରେ(<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"ସମ୍ପୂର୍ଣ୍ଣ ଚାର୍ଜ ହେବାପାଇଁ <xliff:g id="TIME">%1$s</xliff:g> ବାକି ଅଛି"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ପୂର୍ଣ୍ଣ ଚାର୍ଜ ହେବା ପର୍ଯ୍ୟନ୍ତ"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"ଚାର୍ଜ ହେବା ପାଇଁ <xliff:g id="TIME">%1$s</xliff:g> ବାକି ଅଛି"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ଚାର୍ଜ ହେବା ପର୍ଯ୍ୟନ୍ତ"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"ଅଜ୍ଞାତ"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"ଚାର୍ଜ ହେଉଛି"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"ଚାର୍ଜ ହେଉଛି"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index b5acd8e15acd..0cf95753ae18 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"ਟੈਬਲੈੱਟ ਛੇਤੀ ਹੀ ਬੰਦ ਹੋ ਸਕਦਾ ਹੈ (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"ਡੀਵਾਈਸ ਛੇਤੀ ਹੀ ਬੰਦ ਹੋ ਸਕਦਾ ਹੈ (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"ਪੂਰੀ ਤਰ੍ਹਾਂ ਚਾਰਜ ਹੋਣ ਲਈ <xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਪੂਰੀ ਤਰ੍ਹਾਂ ਚਾਰਜ ਹੋਣ ਵਿੱਚ <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"ਚਾਰਜ ਹੋਣ ਵਿੱਚ <xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ਤੱਕ ਚਾਰਜ ਹੋ ਜਾਵੇਗੀ"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"ਅਗਿਆਤ"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"ਚਾਰਜ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 60a4c53c6a62..caed8c349af7 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tablet może się wkrótce wyłączyć (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Urządzenie może się wkrótce wyłączyć (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> do pełnego naładowania"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Do naładowania <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – do naładowania <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Nieznane"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Ładowanie"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"ładowanie"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index dd0a75bbbc62..04a24f1a9175 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"O tablet pode ser desligado em breve (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"O dispositivo pode ser desligado em breve (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> restante(s) até a carga completa"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até a carga completa"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Tempo restante até a carga completa: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> até a carga completa"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Desconhecido"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Carregando"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"carregando"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index c076d328155b..a206d1ad787c 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"O tablet poderá ser encerrado em breve (<xliff:g id="LEVEL">%1$s</xliff:g>)."</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"O dispositivo poderá ser encerrado em breve (<xliff:g id="LEVEL">%1$s</xliff:g>)."</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Falta(m) <xliff:g id="TIME">%1$s</xliff:g> para concluir o carregamento"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> até ficar totalmente carregada"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Falta(m) <xliff:g id="TIME">%1$s</xliff:g> até ficar carregada"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> até ficar carregada"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Desconhecido"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"A carregar"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"a carregar…"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index dd0a75bbbc62..04a24f1a9175 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"O tablet pode ser desligado em breve (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"O dispositivo pode ser desligado em breve (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> restante(s) até a carga completa"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até a carga completa"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Tempo restante até a carga completa: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> até a carga completa"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Desconhecido"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Carregando"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"carregando"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index fb020e219cd1..76a56e51ef88 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tableta se poate închide în curând (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Dispozitivul se poate închide în curând (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Timp rămas până la încărcarea completă: <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> până la încărcarea completă"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Au mai rămas <xliff:g id="TIME">%1$s</xliff:g> până la încărcare"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> până la încărcare"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Necunoscut"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Se încarcă"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"se încarcă"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 806be6e5cdd6..6e1d29ad13d8 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Уровень заряда батареи: <xliff:g id="LEVEL">%1$s</xliff:g>. Планшет скоро завершит работу."</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Уровень заряда батареи: <xliff:g id="LEVEL">%1$s</xliff:g>. Устройство скоро завершит работу."</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Ещё <xliff:g id="TIME">%1$s</xliff:g> до полной зарядки"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> до полной зарядки"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Неизвестно"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Идет зарядка"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"заряжается"</string>
@@ -414,7 +414,7 @@
<string name="disabled" msgid="9206776641295849915">"Отключено"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Разрешено"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"Запрещено"</string>
- <string name="install_other_apps" msgid="6986686991775883017">"Установка неизвестных приложений"</string>
+ <string name="install_other_apps" msgid="6986686991775883017">"Неизвестные приложения"</string>
<string name="home" msgid="3256884684164448244">"Настройки"</string>
<string-array name="battery_labels">
<item msgid="8494684293649631252">"0 %"</item>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 925d5b48ac21..186c23adaec1 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"ටැබ්ලටය ඉක්මනින් වැසිය හැකිය (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"උපාංගය ඉක්මනින් වැසිය හැකිය (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"පූර්ණව ආරෝපණය වන තෙක් <xliff:g id="TIME">%1$s</xliff:g> ඉතිරියි"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> සම්පූර්ණයෙන් ආරෝපණය වන තෙක්"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"ආරෝපණය වන තෙක් <xliff:g id="TIME">%1$s</xliff:g> ඇත"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණය වන තෙක් <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"නොදනී"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"ආරෝපණය වෙමින්"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"ආරෝපණය වේ"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index d77e1eb2a713..9559975cb156 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tablet sa môže čoskoro vypnúť (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Zariadenie sa môže čoskoro vypnúť (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> do úplného nabitia"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Zostávajúci čas do úplného nabitia: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Neznáme"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Nabíja sa"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"nabíja sa"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 263b4d0e2eef..62e4ad1f91f4 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tablični računalnik se bo morda kmalu zaustavil (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Naprava se bo morda kmalu zaustavila (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Še <xliff:g id="TIME">%1$s</xliff:g> do polne napolnjenosti"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Še <xliff:g id="TIME">%1$s</xliff:g> do polne napolnjenosti"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do polne napolnjenosti"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Neznano"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Polnjenje"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"polnjenje"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 7a054bbec646..0498f3123543 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tableti mund të fiket së shpejti (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Pajisja mund të fiket së shpejti (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> të mbetura deri në ngarkimin e plotë"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> derisa të mbushet plotësisht"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> të mbetura për karikimin"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> deri sa të karikohen"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"I panjohur"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Po karikohet"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"po karikohet"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 0edc9850a26b..3157deed7127 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Таблет ће се ускоро искључити (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Уређај ће се ускоро искључити (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> до потпуног пуњења"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до потпуног пуњења"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Напуниће се за <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – напуниће се за <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Непознато"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Пуни се"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"пуни се"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 862fc6cad0d4..31517b8ab00a 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Surfplattan kan stängas av snart (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Enheten kan stängas av snart (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Batteriet är fulladdat om <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tills det är fulladdat"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> kvar till full laddning"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> till full laddning"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Okänd"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Laddar"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"laddas"</string>
diff --git a/packages/SettingsLib/res/values-sw/arrays.xml b/packages/SettingsLib/res/values-sw/arrays.xml
index a05797e52382..006fc9ffb952 100644
--- a/packages/SettingsLib/res/values-sw/arrays.xml
+++ b/packages/SettingsLib/res/values-sw/arrays.xml
@@ -175,7 +175,7 @@
<item msgid="1744840221860799971">"Yamezimwa"</item>
<item msgid="3054662377365844197">"Zote"</item>
<item msgid="688870735111627832">"Zote isipokuwa redio"</item>
- <item msgid="2850427388488887328">"keneli pekee"</item>
+ <item msgid="2850427388488887328">"kiini pekee"</item>
</string-array>
<string-array name="select_logpersist_summaries">
<item msgid="2216470072500521830">"Imezimwa"</item>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 12922ca2b13e..d73084ffd3e8 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Kompyuta kibao inakaribia kuzimika (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Kifaa kinakaribia kuzimika (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Imebaki <xliff:g id="TIME">%1$s</xliff:g> chaji ijae"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> hadi ijae chaji"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Zimesalia <xliff:g id="TIME">%1$s</xliff:g> hadi ijae chaji"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> hadi ijae chaji"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Haijulikani"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Inachaji"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"inachaji"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 8f72ab6f2a6c..de70d085d491 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -138,8 +138,8 @@
<string name="accessibility_wifi_security_type_none" msgid="1223747559986205423">"கடவுச்சொல் தேவைப்படாத திறந்த நெட்வொர்க்"</string>
<string name="accessibility_wifi_security_type_secured" msgid="862921720418885331">"கடவுச்சொல் தேவைப்படும் பாதுகாப்பான நெட்வொர்க்"</string>
<string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
- <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"அகற்றப்பட்ட பயன்பாடுகள்"</string>
- <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"அகற்றப்பட்ட பயன்பாடுகள் மற்றும் பயனர்கள்"</string>
+ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"அகற்றப்பட்ட ஆப்ஸ்"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"அகற்றப்பட்ட ஆப்ஸ் மற்றும் பயனர்கள்"</string>
<string name="data_usage_ota" msgid="5377889154805560860">"சிஸ்டம் புதுப்பிப்புகள்"</string>
<string name="tether_settings_title_usb" msgid="6688416425801386511">"USB டெதெரிங்"</string>
<string name="tether_settings_title_wifi" msgid="3277144155960302049">"போர்ட்டபிள் ஹாட்ஸ்பாட்"</string>
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"டேப்லெட் விரைவில் ஆஃப் ஆகக்கூடும் (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"சாதனம் விரைவில் ஆஃப் ஆகக்கூடும் (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"முழு சார்ஜாக <xliff:g id="TIME">%1$s</xliff:g>ம் ஆகும்"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - முழு சார்ஜாக <xliff:g id="TIME">%2$s</xliff:g> ஆகும்"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"முழு சார்ஜாக <xliff:g id="TIME">%1$s</xliff:g> ஆகும்"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - முழு சார்ஜாக <xliff:g id="TIME">%2$s</xliff:g> ஆகும்"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"அறியப்படாத"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"சார்ஜ் ஆகிறது"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"சார்ஜ் ஆகிறது"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index e591121d4b59..03ae94f39aef 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"టాబ్లెట్ షట్‌డౌన్ కావచ్చు (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"పరికరం త్వరలో షట్‌డౌన్ కావచ్చు (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"పూర్తిగా ఛార్జ్ కావడానికి <xliff:g id="TIME">%1$s</xliff:g> పడుతుంది"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>లో పూర్తిగా ఛార్జ్ అవుతుంది"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"ఛార్జ్ అవ్వడానికి <xliff:g id="TIME">%1$s</xliff:g> సమయం మిగిలి ఉంది"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - ఛార్జ్ అవ్వడానికి <xliff:g id="TIME">%2$s</xliff:g> పడుతుంది"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"తెలియదు"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"ఛార్జ్ అవుతోంది"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"ఛార్జ్ అవుతోంది"</string>
@@ -414,7 +414,7 @@
<string name="disabled" msgid="9206776641295849915">"నిలిపివేయబడింది"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"అనుమతించినవి"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"అనుమతించబడలేదు"</string>
- <string name="install_other_apps" msgid="6986686991775883017">"తెలియని యాప్‌లను ఇన్‌స్టాల్ చేయడం"</string>
+ <string name="install_other_apps" msgid="6986686991775883017">"తెలియని యాప్‌ల ఇన్‌స్టలేషన్"</string>
<string name="home" msgid="3256884684164448244">"సెట్టింగ్‌ల హోమ్"</string>
<string-array name="battery_labels">
<item msgid="8494684293649631252">"0%"</item>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index b3bef9de2dcc..1cfe45e72a9e 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"แท็บเล็ตอาจปิดเครื่องในไม่ช้า (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"อุปกรณ์อาจปิดเครื่องในไม่ช้า (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"อีก <xliff:g id="TIME">%1$s</xliff:g> จึงจะชาร์จเต็ม"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>จนกว่าจะชาร์จเต็ม"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"เหลือ <xliff:g id="TIME">%1$s</xliff:g> จนกว่าจะชาร์จ"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> จนกว่าจะชาร์จ"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"ไม่ทราบ"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"กำลังชาร์จ"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"กำลังชาร์จ"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 85b64381308f..8b3118b333d1 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Malapit nang mag-shutdown ang tablet (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Malapit nang mag-shut down ang device (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> ang natitira bago makumpleto ang charge"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> hanggang sa makumpleto ang charge"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> ang natitira bago matapos mag-charge"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> hanggang matapos mag-charge"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Hindi Kilala"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Nagcha-charge"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"nagcha-charge"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 6031ecaf6ed4..5891c791cec9 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Tablet kısa süre içinde kapanabilir (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Cihaz kısa süre içinde kapanabilir (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Tam olarak şarj olmasına <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tam şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Şarj olmaya <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - şarj olmaya <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Bilinmiyor"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Şarj oluyor"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"şarj oluyor"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 5f3c6a6b20a3..0bca3077a6ad 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Планшет може невдовзі вимкнутися (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Пристрій може невдовзі вимкнутися (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"До повного зарядження залишилося <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного заряду"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> до повного заряду"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного заряду"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Невідомо"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Заряджається"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"заряджається"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 82d9b3fb57f8..1e27b487ca27 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"ٹیبلیٹ جلد ہی بند ہو سکتا ہے (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"آلہ جلد ہی بند ہو سکتا ہے (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"‎<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>‎"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"پوری طرح چارج ہونے میں <xliff:g id="TIME">%1$s</xliff:g> باقی ہے"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> پوری طرح چارج ہونے تک"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"چارج ہونے میں <xliff:g id="TIME">%1$s</xliff:g> باقی"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> چارج ہونے تک"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"نامعلوم"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"چارج ہو رہا ہے"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"چارج ہو رہا ہے"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 0d3e8de0559d..487100c94feb 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Planshet tez orada o‘chib qolishi mumkin (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Qurilma tez orada o‘chib qolishi mumkin (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"To‘lishiga <xliff:g id="TIME">%1$s</xliff:g> qoldi"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> ichida to‘ladi"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> ichida toʻliq quvvat oladi"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> ichida toʻliq quvvat oladi"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Noma’lum"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Quvvat olmoqda"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"quvvat olmoqda"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 003c45751ecc..c247617b31dc 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Máy tính bảng có thể sắp tắt (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Thiết bị có thể sắp tắt (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> nữa sẽ được sạc đầy"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> nữa sẽ được sạc đầy"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"Còn <xliff:g id="TIME">%1$s</xliff:g> nữa là sạc xong"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> nữa là sạc xong"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Không xác định"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Đang sạc"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"đang sạc"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index bcf1f2df2849..dddc1071dd9b 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"平板电脑可能即将关机 (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"设备可能即将关机 (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"还需 <xliff:g id="TIME">%1$s</xliff:g>充满电"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需 <xliff:g id="TIME">%2$s</xliff:g>充满"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"还剩 <xliff:g id="TIME">%1$s</xliff:g>充满电"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>后充满电"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"未知"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"正在充电"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"正在充电"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 286765d99282..8560e22c587b 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"平板電腦可能即將關機 (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"裝置可能即將關機 (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g>後就能充滿電"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - 還需 <xliff:g id="TIME">%2$s</xliff:g>才能充滿電"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"還需 <xliff:g id="TIME">%1$s</xliff:g>才能充滿電"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - 還需 <xliff:g id="TIME">%2$s</xliff:g>才能充滿電"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"未知"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"充電中"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"正在充電"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index c64130210d37..404aa19c87c6 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"平板電腦可能即將關機 (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"裝置可能即將關機 (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"再過 <xliff:g id="TIME">%1$s</xliff:g>就能完成充電"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g>後充飽電"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽電"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"不明"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"充電中"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"充電中"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 1bfbf07d49ef..542332f10454 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -402,8 +402,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7466484148515796216">"Ithebhulethi ingacisha maduze (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="603933521600231649">"Idivayisi ingacisha maduze (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> kushiywe ishaja"</string>
- <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> kuze ligcwale ngokuphelele"</string>
+ <string name="power_remaining_charging_duration_only" msgid="6672239353042034265">"<xliff:g id="TIME">%1$s</xliff:g> esele ize ishaje"</string>
+ <string name="power_charging_duration" msgid="5850751284103249125">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ize igcwale"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Akwaziwa"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Iyashaja"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"iyashaja"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 6b541ba3f893..df4484c5ab32 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -131,6 +131,9 @@
<!-- Summary for Connected wifi network without internet -->
<string name="wifi_connected_no_internet">Connected, no internet</string>
+ <!-- Summary for connected network without internet due to private dns validation failed [CHAR LIMIT=NONE] -->
+ <string name="private_dns_broken">Private DNS server cannot be accessed</string>
+
<!-- Summary for connected wifi network with partial internet connectivity [CHAR LIMIT=50] -->
<string name="wifi_limited_connection">Limited connection</string>
@@ -1019,9 +1022,9 @@
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging -->
<string name="power_charging"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="state">%2$s</xliff:g></string>
<!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery charging -->
- <string name="power_remaining_charging_duration_only"><xliff:g id="time">%1$s</xliff:g> left until fully charged</string>
+ <string name="power_remaining_charging_duration_only"><xliff:g id="time">%1$s</xliff:g> left until charged</string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
- <string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="time">%2$s</xliff:g> until fully charged</string>
+ <string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="time">%2$s</xliff:g> until charged</string>
<!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="battery_info_status_unknown">Unknown</string>
diff --git a/packages/SettingsLib/search/Android.bp b/packages/SettingsLib/search/Android.bp
index 15c8367cf727..d398aa5c44ac 100644
--- a/packages/SettingsLib/search/Android.bp
+++ b/packages/SettingsLib/search/Android.bp
@@ -1,7 +1,9 @@
-java_library {
+android_library {
name: "SettingsLib-search",
- host_supported: true,
srcs: ["src/**/*.java"],
+
+ sdk_version: "system_current",
+ min_sdk_version: "21",
}
java_plugin {
@@ -9,9 +11,11 @@ java_plugin {
processor_class: "com.android.settingslib.search.IndexableProcessor",
static_libs: [
"javapoet-prebuilt-jar",
- "SettingsLib-search",
],
- srcs: ["processor-src/**/*.java"],
+ srcs: [
+ "processor-src/**/*.java",
+ "src/com/android/settingslib/search/SearchIndexable.java"
+ ],
java_resource_dirs: ["resources"],
}
diff --git a/packages/SettingsLib/search/AndroidManifest.xml b/packages/SettingsLib/search/AndroidManifest.xml
new file mode 100644
index 000000000000..970728365f5c
--- /dev/null
+++ b/packages/SettingsLib/search/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?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
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.search">
+
+</manifest> \ No newline at end of file
diff --git a/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java b/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java
index 10fc685015b7..5dc9061a81a0 100644
--- a/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java
+++ b/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java
@@ -143,7 +143,7 @@ public class IndexableProcessor extends AbstractProcessor {
final TypeSpec baseClass = TypeSpec.classBuilder(CLASS_BASE)
.addModifiers(Modifier.PUBLIC)
- .addSuperinterface(ClassName.get(SearchIndexableResources.class))
+ .addSuperinterface(ClassName.get(PACKAGE, "SearchIndexableResources"))
.addField(providers)
.addMethod(baseConstructorBuilder.build())
.addMethod(addIndex)
@@ -210,4 +210,4 @@ public class IndexableProcessor extends AbstractProcessor {
mFiler = processingEnvironment.getFiler();
mMessager = processingEnvironment.getMessager();
}
-}
+} \ No newline at end of file
diff --git a/packages/SettingsLib/search/src/com/android/settingslib/search/Indexable.java b/packages/SettingsLib/search/src/com/android/settingslib/search/Indexable.java
new file mode 100644
index 000000000000..e68b0d1d6798
--- /dev/null
+++ b/packages/SettingsLib/search/src/com/android/settingslib/search/Indexable.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.search;
+
+import android.content.Context;
+import android.provider.SearchIndexableResource;
+
+import java.util.List;
+
+/**
+ * Interface for classes whose instances can provide data for indexing.
+ *
+ * See {@link android.provider.SearchIndexableResource} and {@link SearchIndexableRaw}.
+ */
+public interface Indexable {
+
+ /**
+ * Interface for classes whose instances can provide data for indexing.
+ */
+ interface SearchIndexProvider {
+ /**
+ * Return a list of references for indexing.
+ *
+ * See {@link android.provider.SearchIndexableResource}
+ *
+ * @param context the context.
+ * @param enabled hint telling if the data needs to be considered into the search results
+ * or not.
+ * @return a list of {@link android.provider.SearchIndexableResource} references.
+ * Can be null.
+ */
+ List<SearchIndexableResource> getXmlResourcesToIndex(Context context, boolean enabled);
+
+ /**
+ * Return a list of raw data for indexing. See {@link SearchIndexableRaw}
+ *
+ * @param context the context.
+ * @param enabled hint telling if the data needs to be considered into the search results
+ * or not.
+ * @return a list of {@link SearchIndexableRaw} references. Can be null.
+ */
+ List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled);
+
+ /**
+ * Return a list of data keys that cannot be indexed. See {@link SearchIndexableRaw}
+ *
+ * @param context the context.
+ * @return a list of {@link SearchIndexableRaw} references. Can be null.
+ */
+ List<String> getNonIndexableKeys(Context context);
+ }
+}
diff --git a/packages/SettingsLib/search/src/com/android/settingslib/search/SearchIndexableRaw.java b/packages/SettingsLib/search/src/com/android/settingslib/search/SearchIndexableRaw.java
new file mode 100644
index 000000000000..021ca3362aab
--- /dev/null
+++ b/packages/SettingsLib/search/src/com/android/settingslib/search/SearchIndexableRaw.java
@@ -0,0 +1,64 @@
+/*
+ * 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.settingslib.search;
+
+import android.content.Context;
+import android.provider.SearchIndexableData;
+
+/**
+ * Indexable raw data for Search.
+ *
+ * This is the raw data used by the Indexer and should match its data model.
+ *
+ * See {@link Indexable} and {@link android.provider.SearchIndexableResource}.
+ */
+public class SearchIndexableRaw extends SearchIndexableData {
+
+ /**
+ * Title's raw data.
+ */
+ public String title;
+
+ /**
+ * Summary's raw data when the data is "ON".
+ */
+ public String summaryOn;
+
+ /**
+ * Summary's raw data when the data is "OFF".
+ */
+ public String summaryOff;
+
+ /**
+ * Entries associated with the raw data (when the data can have several values).
+ */
+ public String entries;
+
+ /**
+ * Keywords' raw data.
+ */
+ public String keywords;
+
+ /**
+ * Fragment's or Activity's title associated with the raw data.
+ */
+ public String screenTitle;
+
+ public SearchIndexableRaw(Context context) {
+ super(context);
+ }
+}
diff --git a/packages/SettingsLib/search/src/com/android/settingslib/search/SearchIndexableResources.java b/packages/SettingsLib/search/src/com/android/settingslib/search/SearchIndexableResources.java
index 300d360e0057..976647b3e88f 100644
--- a/packages/SettingsLib/search/src/com/android/settingslib/search/SearchIndexableResources.java
+++ b/packages/SettingsLib/search/src/com/android/settingslib/search/SearchIndexableResources.java
@@ -32,4 +32,4 @@ public interface SearchIndexableResources {
* as a device binary.
*/
void addIndex(Class indexClass);
-}
+} \ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
index a18600abf788..2b841967d4a8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
@@ -48,11 +48,21 @@ public class AccessibilityUtils {
return getEnabledServicesFromSettings(context, UserHandle.myUserId());
}
+ /**
+ * Check if the accessibility service is crashed
+ *
+ * @param packageName The package name to check
+ * @param serviceName The service name to check
+ * @param installedServiceInfos The list of installed accessibility service
+ * @return {@code true} if the accessibility service is crashed for the user.
+ * {@code false} otherwise.
+ */
public static boolean hasServiceCrashed(String packageName, String serviceName,
- List<AccessibilityServiceInfo> enabledServiceInfos) {
- for (int i = 0; i < enabledServiceInfos.size(); i++) {
- AccessibilityServiceInfo accessibilityServiceInfo = enabledServiceInfos.get(i);
- final ServiceInfo serviceInfo = enabledServiceInfos.get(i).getResolveInfo().serviceInfo;
+ List<AccessibilityServiceInfo> installedServiceInfos) {
+ for (int i = 0; i < installedServiceInfos.size(); i++) {
+ final AccessibilityServiceInfo accessibilityServiceInfo = installedServiceInfos.get(i);
+ final ServiceInfo serviceInfo =
+ installedServiceInfos.get(i).getResolveInfo().serviceInfo;
if (TextUtils.equals(serviceInfo.packageName, packageName)
&& TextUtils.equals(serviceInfo.name, serviceName)) {
return accessibilityServiceInfo.crashed;
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 6a906719d3fa..19c666459723 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -865,6 +865,10 @@ public class ApplicationsState {
void handleRebuildList() {
AppFilter filter;
Comparator<AppEntry> comparator;
+
+ if (!mResumed) {
+ return;
+ }
synchronized (mRebuildSync) {
if (!mRebuildRequested) {
return;
@@ -1069,8 +1073,8 @@ public class ApplicationsState {
}
}
if (rebuildingSessions != null) {
- for (int i = 0; i < rebuildingSessions.size(); i++) {
- rebuildingSessions.get(i).handleRebuildList();
+ for (Session session : rebuildingSessions) {
+ session.handleRebuildList();
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
index ea39317fb045..81ca9eaf8e36 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
+++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
@@ -111,8 +111,9 @@ public class RecentLocationAccesses {
for (int op : LOCATION_OPS) {
final String permission = AppOpsManager.opToPermission(op);
final int permissionFlags = pm.getPermissionFlags(permission, packageName, user);
- if (PermissionChecker.checkPermission(mContext, permission, -1, uid, packageName)
- == PermissionChecker.PERMISSION_GRANTED) {
+ if (PermissionChecker.checkPermissionForPreflight(mContext, permission,
+ PermissionChecker.PID_UNKNOWN, uid, packageName)
+ == PermissionChecker.PERMISSION_GRANTED) {
if ((permissionFlags
& PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) == 0) {
showApp = false;
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
index 60c9984e5ed4..104cc8f9841c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
+++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
@@ -110,9 +110,9 @@ public class RecentLocationApps {
final String permission = AppOpsManager.opToPermission(op);
final int permissionFlags = pm.getPermissionFlags(permission, packageName,
user);
- if (PermissionChecker.checkPermission(mContext, permission, -1, uid,
- packageName)
- == PermissionChecker.PERMISSION_GRANTED) {
+ if (PermissionChecker.checkPermissionForPreflight(mContext, permission,
+ PermissionChecker.PID_UNKNOWN, uid, packageName)
+ == PermissionChecker.PERMISSION_GRANTED) {
if ((permissionFlags
& PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED)
== 0) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
index 5c9a06f91e6a..4e052f1aeca4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
@@ -26,7 +26,7 @@ public class ThreadUtils {
private static volatile Thread sMainThread;
private static volatile Handler sMainThreadHandler;
- private static volatile ExecutorService sSingleThreadExecutor;
+ private static volatile ExecutorService sThreadExecutor;
/**
* Returns true if the current thread is the UI thread.
@@ -64,10 +64,11 @@ public class ThreadUtils {
* @Return A future of the task that can be monitored for updates or cancelled.
*/
public static Future postOnBackgroundThread(Runnable runnable) {
- if (sSingleThreadExecutor == null) {
- sSingleThreadExecutor = Executors.newSingleThreadExecutor();
+ if (sThreadExecutor == null) {
+ sThreadExecutor = Executors.newFixedThreadPool(
+ Runtime.getRuntime().availableProcessors());
}
- return sSingleThreadExecutor.submit(runnable);
+ return sThreadExecutor.submit(runnable);
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 9b9dd56ce098..8a0738d11ff3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -50,6 +50,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -1744,7 +1745,13 @@ public class AccessPoint implements Comparable<AccessPoint> {
NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY)) {
return context.getString(R.string.wifi_limited_connection);
} else if (!nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
- return context.getString(R.string.wifi_connected_no_internet);
+ final String mode = Settings.Global.getString(context.getContentResolver(),
+ Settings.Global.PRIVATE_DNS_MODE);
+ if (nc.isPrivateDnsBroken()) {
+ return context.getString(R.string.private_dns_broken);
+ } else {
+ return context.getString(R.string.wifi_connected_no_internet);
+ }
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 20f009c1bb73..e19013e9de36 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -31,6 +31,7 @@ import android.net.wifi.WifiNetworkScoreCache;
import android.net.wifi.WifiSsid;
import android.os.Handler;
import android.os.Looper;
+import android.provider.Settings;
import com.android.settingslib.R;
@@ -169,7 +170,13 @@ public class WifiStatusTracker extends ConnectivityManager.NetworkCallback {
statusLabel = mContext.getString(R.string.wifi_limited_connection);
return;
} else if (!networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
- statusLabel = mContext.getString(R.string.wifi_status_no_internet);
+ final String mode = Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.PRIVATE_DNS_MODE);
+ if (networkCapabilities.isPrivateDnsBroken()) {
+ statusLabel = mContext.getString(R.string.private_dns_broken);
+ } else {
+ statusLabel = mContext.getString(R.string.wifi_status_no_internet);
+ }
return;
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index 30c06cec6b04..423a4ecc29a3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -94,6 +94,7 @@ public class WifiUtils {
if (bssid != null) {
visibility.append(" ").append(bssid);
}
+ visibility.append(" technology = ").append(info.getWifiTechnology());
visibility.append(" rssi=").append(info.getRssi());
visibility.append(" ");
visibility.append(" score=").append(info.score);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
index f8697a19c7ab..95a4f69b287c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -269,7 +269,7 @@ public class ApplicationsStateRoboTest {
}
@Test
- public void testDefaultSessionLoadsAll() {
+ public void testDefaultSession_isResumed_LoadsAll() {
mSession.onResume();
addApp(HOME_PACKAGE_NAME, 1);
@@ -296,6 +296,19 @@ public class ApplicationsStateRoboTest {
}
@Test
+ public void testDefaultSession_isPaused_NotLoadsAll() {
+ mSession.onResume();
+
+ addApp(HOME_PACKAGE_NAME, 1);
+ addApp(LAUNCHABLE_PACKAGE_NAME, 2);
+ mSession.mResumed = false;
+ mSession.rebuild(ApplicationsState.FILTER_EVERYTHING, ApplicationsState.SIZE_COMPARATOR);
+ processAllMessages();
+
+ verify(mCallbacks, never()).onRebuildComplete(mAppEntriesCaptor.capture());
+ }
+
+ @Test
public void testCustomSessionLoadsIconsOnly() {
mSession.setSessionFlags(ApplicationsState.FLAG_SESSION_REQUEST_ICONS);
mSession.onResume();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
index 5da6205d45bd..2664ecd17dd3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
@@ -23,7 +23,6 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class)
public class TileTest {
@@ -41,7 +40,7 @@ public class TileTest {
mActivityInfo.name = "abc";
mActivityInfo.icon = com.android.internal.R.drawable.ic_plus;
mActivityInfo.metaData = new Bundle();
- mTile = new Tile(mActivityInfo, "category");
+ mTile = new ActivityTile(mActivityInfo, "category");
}
@Test
@@ -70,7 +69,7 @@ public class TileTest {
@Test
public void getIcon_noContextOrMetadata_returnNull() {
mActivityInfo.metaData = null;
- final Tile tile = new Tile(mActivityInfo, "category");
+ final Tile tile = new ActivityTile(mActivityInfo, "category");
assertThat(tile.getIcon(null)).isNull();
assertThat(tile.getIcon(RuntimeEnvironment.application)).isNull();
}
@@ -100,7 +99,7 @@ public class TileTest {
@Test
public void isIconTintable_hasMetadata_shouldReturnIconTintableMetadata() {
- final Tile tile = new Tile(mActivityInfo, "category");
+ final Tile tile = new ActivityTile(mActivityInfo, "category");
mActivityInfo.metaData.putBoolean(TileUtils.META_DATA_PREFERENCE_ICON_TINTABLE, false);
assertThat(tile.isIconTintable(RuntimeEnvironment.application)).isFalse();
@@ -111,14 +110,14 @@ public class TileTest {
@Test
public void isIconTintable_noIcon_shouldReturnFalse() {
- final Tile tile = new Tile(mActivityInfo, "category");
+ final Tile tile = new ActivityTile(mActivityInfo, "category");
assertThat(tile.isIconTintable(RuntimeEnvironment.application)).isFalse();
}
@Test
public void isIconTintable_noTintableMetadata_shouldReturnFalse() {
- final Tile tile = new Tile(mActivityInfo, "category");
+ final Tile tile = new ActivityTile(mActivityInfo, "category");
mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON, android.R.drawable.ic_info);
assertThat(tile.isIconTintable(RuntimeEnvironment.application)).isFalse();
@@ -126,7 +125,7 @@ public class TileTest {
@Test
public void getPriority_noMetadata_return0() {
- final Tile tile = new Tile(mActivityInfo, "category");
+ final Tile tile = new ActivityTile(mActivityInfo, "category");
assertThat(tile.getOrder()).isEqualTo(0);
}
@@ -135,7 +134,7 @@ public class TileTest {
public void getPriority_badMetadata_return0() {
mActivityInfo.metaData.putString(META_DATA_KEY_ORDER, "1");
- final Tile tile = new Tile(mActivityInfo, "category");
+ final Tile tile = new ActivityTile(mActivityInfo, "category");
assertThat(tile.getOrder()).isEqualTo(0);
}
@@ -144,7 +143,7 @@ public class TileTest {
public void getPriority_validMetadata_returnMetadataValue() {
mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 1);
- final Tile tile = new Tile(mActivityInfo, "category");
+ final Tile tile = new ActivityTile(mActivityInfo, "category");
assertThat(tile.getOrder()).isEqualTo(1);
}
@@ -157,7 +156,7 @@ public class TileTest {
spm.addResolveInfoForIntent(
new Intent().setClassName(mActivityInfo.packageName, mActivityInfo.name), info);
- final Tile tile = new Tile(mActivityInfo, "category");
+ final Tile tile = new ActivityTile(mActivityInfo, "category");
final long staleTimeStamp = -10000;
tile.mLastUpdateTime = staleTimeStamp;
@@ -173,8 +172,8 @@ public class TileTest {
final ShadowPackageManager spm = Shadow.extract(mContext.getPackageManager());
spm.removePackage(mActivityInfo.packageName);
- final Tile tile = new Tile(mActivityInfo, "category");
- ReflectionHelpers.setField(tile, "mActivityInfo", null);
+ final Tile tile = new ActivityTile(mActivityInfo, "category");
+ tile.mComponentInfo = null;
assertThat(tile.getTitle(RuntimeEnvironment.application)).isNull();
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java
index 5d2a0d1df96c..e936c359e281 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java
@@ -8,6 +8,7 @@ import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.app.AppOpsManager.OpEntry;
+import android.app.AppOpsManager.OpFeatureEntry;
import android.app.AppOpsManager.PackageOps;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -17,6 +18,7 @@ import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.LongSparseLongArray;
+import android.util.Pair;
import org.junit.Before;
import org.junit.Ignore;
@@ -161,7 +163,10 @@ public class RecentLocationAccessesTest {
accessTimes.put(AppOpsManager.makeKey(AppOpsManager.UID_STATE_BACKGROUND,
AppOpsManager.OP_FLAG_SELF), time);
- return new OpEntry(op, false, AppOpsManager.MODE_ALLOWED, accessTimes, null /*durations*/,
- null /*rejectTimes*/, null /* proxyUids */, null /* proxyPackages */);
+ OpFeatureEntry.Builder featureEntry = new OpFeatureEntry.Builder(false, accessTimes,
+ null /*rejectTimes*/, null /*durations*/, null /* proxyUids */,
+ null /* proxyPackages */, null /* proxyFeatureIds */);
+ return new OpEntry(op, AppOpsManager.MODE_ALLOWED,
+ new Pair[]{new Pair(null, featureEntry)});
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
index 7a553fc91ff6..3fde48b18b63 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
@@ -7,6 +7,7 @@ import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.app.AppOpsManager.OpEntry;
+import android.app.AppOpsManager.OpFeatureEntry;
import android.app.AppOpsManager.PackageOps;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -17,6 +18,7 @@ import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.LongSparseLongArray;
+import android.util.Pair;
import org.junit.Before;
import org.junit.Test;
@@ -162,7 +164,11 @@ public class RecentLocationAppsTest {
final LongSparseLongArray durations = new LongSparseLongArray();
durations.put(AppOpsManager.makeKey(AppOpsManager.UID_STATE_TOP,
AppOpsManager.OP_FLAG_SELF), duration);
- return new OpEntry(op, false, AppOpsManager.MODE_ALLOWED, accessTimes,
- null /*rejectTimes*/, durations, null /* proxyUids */, null /* proxyPackages */);
+
+ OpFeatureEntry.Builder featureEntry = new OpFeatureEntry.Builder(false, accessTimes,
+ null /*rejectTimes*/, durations, null /* proxyUids */,
+ null /* proxyPackages */, null /* proxyFeatureIds */);
+ return new OpEntry(op, AppOpsManager.MODE_ALLOWED,
+ new Pair[]{new Pair(null, featureEntry)});
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
index 26db124c0041..5114b00d2711 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
@@ -50,7 +50,7 @@ public class ThreadUtilsTest {
}
@Test
- public void testPostOnMainThread_shouldRunOnMainTread() {
+ public void testPostOnMainThread_shouldRunOnMainThread() {
TestRunnable cr = new TestRunnable();
ShadowLooper.pauseMainLooper();
ThreadUtils.postOnMainThread(cr);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java
index ed6b9b0a135e..6cbae05ce9a5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java
@@ -37,6 +37,7 @@ import android.graphics.drawable.ShapeDrawable;
import android.os.Bundle;
import com.android.settingslib.R;
+import com.android.settingslib.drawer.ActivityTile;
import com.android.settingslib.drawer.CategoryKey;
import com.android.settingslib.drawer.Tile;
@@ -84,7 +85,7 @@ public class AdaptiveIconTest {
@Test
public void setBackgroundColor_externalTileWithBackgroundColorRawValue_shouldUpdateIcon() {
- final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
+ final Tile tile = spy(new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB, 0xff0000);
doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update))
.when(tile).getIcon(mContext);
@@ -97,7 +98,7 @@ public class AdaptiveIconTest {
@Test
public void setBackgroundColor_tileWithoutBackgroundColor_shouldSetDefaultBackgroundColor() {
- final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
+ final Tile tile = spy(new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update))
.when(tile).getIcon(mContext);
final AdaptiveIcon icon = new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
@@ -110,7 +111,7 @@ public class AdaptiveIconTest {
@Test
public void onBindTile_externalTileWithBackgroundColorHint_shouldUpdateIcon() {
- final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
+ final Tile tile = spy(new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_HINT,
R.color.bt_outline_color);
doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update))
diff --git a/packages/SettingsProvider/src/android/provider/settings/OWNERS b/packages/SettingsProvider/src/android/provider/settings/OWNERS
new file mode 100644
index 000000000000..541dd8787545
--- /dev/null
+++ b/packages/SettingsProvider/src/android/provider/settings/OWNERS
@@ -0,0 +1,5 @@
+# Please reach out to Android B&R when making Settings backup changes
+alsutton@google.com
+nathch@google.com
+rthakohov@google.com
+
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/DeviceSpecificSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/DeviceSpecificSettings.java
new file mode 100644
index 000000000000..e425790110aa
--- /dev/null
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/DeviceSpecificSettings.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.settings.backup;
+
+import android.provider.Settings;
+
+/** Device specific settings list */
+public class DeviceSpecificSettings {
+ /**
+ * The settings values which should only be restored if the target device is the
+ * same as the source device
+ *
+ * NOTE: Settings are backed up and restored in the order they appear
+ * in this array. If you have one setting depending on another,
+ * make sure that they are ordered appropriately.
+ *
+ * @hide
+ */
+ public static final String[] DEVICE_SPECIFIC_SETTINGS_TO_BACKUP = {
+ Settings.Secure.DISPLAY_DENSITY_FORCED,
+ };
+}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index a9365718c71b..e9c1dc985e48 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -101,8 +101,6 @@ public class SecureSettings {
Settings.Secure.DOZE_PICK_UP_GESTURE,
Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
Settings.Secure.DOZE_TAP_SCREEN_GESTURE,
- Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
- Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT,
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED,
@@ -110,12 +108,8 @@ public class SecureSettings {
Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD,
Settings.Secure.FACE_UNLOCK_APP_ENABLED,
Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
- Settings.Secure.ASSIST_GESTURE_ENABLED,
- Settings.Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
- Settings.Secure.ASSIST_GESTURE_WAKE_ENABLED,
Settings.Secure.VR_DISPLAY_MODE,
Settings.Secure.NOTIFICATION_BADGING,
- Settings.Secure.NOTIFICATION_BUBBLES,
Settings.Secure.NOTIFICATION_DISMISS_RTL,
Settings.Secure.QS_AUTO_ADDED_TILES,
Settings.Secure.SCREENSAVER_ENABLED,
@@ -146,12 +140,9 @@ public class SecureSettings {
Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK,
Settings.Secure.UI_NIGHT_MODE,
Settings.Secure.LOCK_SCREEN_WHEN_TRUST_LOST,
- Settings.Secure.SKIP_GESTURE,
Settings.Secure.SKIP_DIRECTION,
- Settings.Secure.SILENCE_GESTURE,
Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
Settings.Secure.NAVIGATION_MODE,
- Settings.Secure.AWARE_ENABLED,
Settings.Secure.SKIP_GESTURE_COUNT,
Settings.Secure.SKIP_TOUCH_COUNT,
Settings.Secure.SILENCE_ALARMS_GESTURE_COUNT,
@@ -162,6 +153,8 @@ public class SecureSettings {
Settings.Secure.SILENCE_TIMER_TOUCH_COUNT,
Settings.Secure.DARK_MODE_DIALOG_SEEN,
Settings.Secure.GLOBAL_ACTIONS_PANEL_ENABLED,
- Settings.Secure.AWARE_LOCK_ENABLED
+ Settings.Secure.AWARE_LOCK_ENABLED,
+ Settings.Secure.AWARE_TAP_PAUSE_GESTURE_COUNT,
+ Settings.Secure.AWARE_TAP_PAUSE_TOUCH_COUNT
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 265f005ba320..ad636115a658 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -41,8 +41,8 @@ import java.util.Map;
public class SecureSettingsValidators {
/**
* All settings in {@link Secure.SETTINGS_TO_BACKUP} and {@link
- * Secure.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP} array *must* have a non-null validator, otherwise
- * they won't be restored.
+ * DeviceSpecificSettings.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP} array *must* have a non-null
+ * validator, otherwise they won't be restored.
*/
public static final Map<String, Validator> VALIDATORS = new ArrayMap<>();
@@ -158,7 +158,6 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.ASSIST_GESTURE_WAKE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.VR_DISPLAY_MODE, new DiscreteValueValidator(new String[] {"0", "1"}));
VALIDATORS.put(Secure.NOTIFICATION_BADGING, BOOLEAN_VALIDATOR);
- VALIDATORS.put(Secure.NOTIFICATION_BUBBLES, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.NOTIFICATION_DISMISS_RTL, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.QS_AUTO_ADDED_TILES, TILE_LIST_VALIDATOR);
VALIDATORS.put(Secure.SCREENSAVER_ENABLED, BOOLEAN_VALIDATOR);
@@ -222,6 +221,8 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.SILENCE_ALARMS_TOUCH_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.SILENCE_TIMER_TOUCH_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.SILENCE_CALL_TOUCH_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
+ VALIDATORS.put(Secure.AWARE_TAP_PAUSE_GESTURE_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
+ VALIDATORS.put(Secure.AWARE_TAP_PAUSE_TOUCH_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.ODI_CAPTIONS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DARK_MODE_DIALOG_SEEN, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.UI_NIGHT_MODE, new InclusiveIntegerRangeValidator(0, 2));
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 34043146cc28..56deb1ccee28 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -51,6 +51,7 @@ import com.android.internal.telephony.RILConstants;
import com.android.internal.util.XmlUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternView;
+import com.android.internal.widget.LockscreenCredential;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -1496,21 +1497,7 @@ class DatabaseHelper extends SQLiteOpenHelper {
}
if (upgradeVersion == 94) {
- // Add wireless charging started sound setting
- if (mUserHandle == UserHandle.USER_SYSTEM) {
- db.beginTransaction();
- SQLiteStatement stmt = null;
- try {
- stmt = db.compileStatement("INSERT OR REPLACE INTO global(name,value)"
- + " VALUES(?,?);");
- loadStringSetting(stmt, Settings.Global.CHARGING_STARTED_SOUND,
- R.string.def_wireless_charging_started_sound);
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- if (stmt != null) stmt.close();
- }
- }
+ // charging sound moved to SettingsProvider version 184
upgradeVersion = 95;
}
@@ -1992,8 +1979,11 @@ class DatabaseHelper extends SQLiteOpenHelper {
try {
LockPatternUtils lpu = new LockPatternUtils(mContext);
List<LockPatternView.Cell> cellPattern =
- LockPatternUtils.stringToPattern(lockPattern);
- lpu.saveLockPattern(cellPattern, null, UserHandle.USER_SYSTEM);
+ LockPatternUtils.byteArrayToPattern(lockPattern.getBytes());
+ lpu.setLockCredential(
+ LockscreenCredential.createPattern(cellPattern),
+ LockscreenCredential.createNone(),
+ UserHandle.USER_SYSTEM);
} catch (IllegalArgumentException e) {
// Don't want corrupted lock pattern to hang the reboot process
}
@@ -2558,8 +2548,6 @@ class DatabaseHelper extends SQLiteOpenHelper {
R.string.def_car_dock_sound);
loadStringSetting(stmt, Settings.Global.CAR_UNDOCK_SOUND,
R.string.def_car_undock_sound);
- loadStringSetting(stmt, Settings.Global.CHARGING_STARTED_SOUND,
- R.string.def_wireless_charging_started_sound);
loadIntegerSetting(stmt, Settings.Global.DOCK_AUDIO_MEDIA_ENABLED,
R.integer.def_dock_audio_media_enabled);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index d0ffe7ae8c7e..8fb879d3606b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -16,7 +16,6 @@
package com.android.providers.settings;
-import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.ActivityManager;
import android.content.IContentProvider;
@@ -195,8 +194,15 @@ public final class DeviceConfigService extends Binder {
: "Failed to delete " + key + " from " + namespace);
break;
case LIST:
- for (String line : list(iprovider, namespace)) {
- pout.println(line);
+ if (namespace != null) {
+ DeviceConfig.Properties properties = DeviceConfig.getProperties(namespace);
+ for (String name : properties.getKeyset()) {
+ pout.println(name + "=" + properties.getString(name, null));
+ }
+ } else {
+ for (String line : listAll(iprovider)) {
+ pout.println(line);
+ }
}
break;
case RESET:
@@ -251,16 +257,13 @@ public final class DeviceConfigService extends Binder {
return success;
}
- private List<String> list(IContentProvider provider, @Nullable String namespace) {
+ private List<String> listAll(IContentProvider provider) {
final ArrayList<String> lines = new ArrayList<>();
try {
Bundle args = new Bundle();
args.putInt(Settings.CALL_METHOD_USER_KEY,
ActivityManager.getService().getCurrentUser().id);
- if (namespace != null) {
- args.putString(Settings.CALL_METHOD_PREFIX_KEY, namespace);
- }
Bundle b = provider.call(resolveCallingPackage(), Settings.AUTHORITY,
Settings.CALL_METHOD_LIST_CONFIG, null, args);
if (b != null) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index f545fa65fb90..7e60452411cb 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -34,6 +34,7 @@ import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
import android.provider.Settings;
+import android.provider.settings.backup.DeviceSpecificSettings;
import android.provider.settings.backup.GlobalSettings;
import android.provider.settings.backup.SecureSettings;
import android.provider.settings.backup.SystemSettings;
@@ -641,7 +642,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
if (contentUri.equals(Settings.Secure.CONTENT_URI)) {
whitelist = ArrayUtils.concatElements(String.class, SecureSettings.SETTINGS_TO_BACKUP,
Settings.Secure.LEGACY_RESTORE_SETTINGS,
- Settings.Secure.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP);
+ DeviceSpecificSettings.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP);
validators = SecureSettingsValidators.VALIDATORS;
} else if (contentUri.equals(Settings.System.CONTENT_URI)) {
whitelist = ArrayUtils.concatElements(String.class, SystemSettings.SETTINGS_TO_BACKUP,
@@ -1000,7 +1001,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
getContentResolver()
.query(Settings.Secure.CONTENT_URI, PROJECTION, null, null, null)) {
return extractRelevantValues(
- cursor, Settings.Secure.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP);
+ cursor, DeviceSpecificSettings.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index a9391a54ef35..84e63be6c0ae 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -76,7 +76,7 @@ class SettingsProtoDumpUtil {
ConfigSettingsProto.RUNTIME_NATIVE_SETTINGS);
namespaceToFieldMap.put(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
ConfigSettingsProto.RUNTIME_NATIVE_BOOT_SETTINGS);
- namespaceToFieldMap.put(DeviceConfig.NAMESPACE_STORAGE,
+ namespaceToFieldMap.put(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
ConfigSettingsProto.STORAGE_SETTINGS);
namespaceToFieldMap.put(DeviceConfig.NAMESPACE_SYSTEMUI,
ConfigSettingsProto.SYSTEMUI_SETTINGS);
@@ -1350,6 +1350,9 @@ class SettingsProtoDumpUtil {
dumpSetting(s, p,
Settings.Global.CHARGING_STARTED_SOUND,
GlobalSettingsProto.Sounds.CHARGING_STARTED);
+ dumpSetting(s, p,
+ Settings.Global.WIRELESS_CHARGING_STARTED_SOUND,
+ GlobalSettingsProto.Sounds.WIRELESS_CHARGING_STARTED);
p.end(soundsToken);
final long soundTriggerToken = p.start(GlobalSettingsProto.SOUND_TRIGGER);
@@ -2050,6 +2053,12 @@ class SettingsProtoDumpUtil {
dumpSetting(s, p,
Settings.Secure.SKIP_TOUCH_COUNT,
SecureSettingsProto.Gesture.SKIP_TOUCH_COUNT);
+ dumpSetting(s, p,
+ Settings.Secure.AWARE_TAP_PAUSE_GESTURE_COUNT,
+ SecureSettingsProto.Gesture.AWARE_TAP_PAUSE_GESTURE_COUNT);
+ dumpSetting(s, p,
+ Settings.Secure.AWARE_TAP_PAUSE_TOUCH_COUNT,
+ SecureSettingsProto.Gesture.AWARE_TAP_PAUSE_TOUCH_COUNT);
p.end(gestureToken);
dumpSetting(s, p,
@@ -2245,9 +2254,6 @@ class SettingsProtoDumpUtil {
dumpSetting(s, p,
Settings.Secure.PACKAGE_VERIFIER_USER_CONSENT,
SecureSettingsProto.PackageVerifier.USER_CONSENT);
- dumpSetting(s, p,
- Settings.Secure.PACKAGE_VERIFIER_STATE,
- SecureSettingsProto.PackageVerifier.STATE);
p.end(packageVerifierToken);
final long parentalControlToken = p.start(SecureSettingsProto.PARENTAL_CONTROL);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 08588a75b6d0..40b711121309 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -56,6 +56,7 @@ import android.os.DropBoxManager;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IUserRestrictionsListener;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
@@ -65,7 +66,6 @@ import android.os.SELinux;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.UserManagerInternal;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Settings.Global;
@@ -84,7 +84,6 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.providers.settings.SettingsState.Setting;
-import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.google.android.collect.Sets;
@@ -286,8 +285,6 @@ public class SettingsProvider extends ContentProvider {
// We have to call in the user manager with no lock held,
private volatile UserManager mUserManager;
- private UserManagerInternal mUserManagerInternal;
-
// We have to call in the package manager with no lock held,
private volatile IPackageManager mPackageManager;
@@ -317,7 +314,6 @@ public class SettingsProvider extends ContentProvider {
synchronized (mLock) {
mUserManager = UserManager.get(getContext());
- mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
mPackageManager = AppGlobals.getPackageManager();
mHandlerThread = new HandlerThread(LOG_TAG,
Process.THREAD_PRIORITY_BACKGROUND);
@@ -439,10 +435,8 @@ public class SettingsProvider extends ContentProvider {
case Settings.CALL_METHOD_LIST_CONFIG: {
String prefix = getSettingPrefix(args);
- Bundle result = new Bundle();
- result.putSerializable(
- Settings.NameValueTable.VALUE, (HashMap) getAllConfigFlags(prefix));
- return result;
+ return packageValuesForCallResult(getAllConfigFlags(prefix),
+ isTrackingGeneration(args));
}
case Settings.CALL_METHOD_LIST_GLOBAL: {
@@ -904,95 +898,100 @@ public class SettingsProvider extends ContentProvider {
// TODO: The current design of settings looking different based on user restrictions
// should be reworked to keep them separate and system code should check the setting
// first followed by checking the user restriction before performing an operation.
- UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class);
- userManager.addUserRestrictionsListener((int userId, Bundle newRestrictions,
- Bundle prevRestrictions) -> {
- Set<String> changedRestrictions = getRestrictionDiff(prevRestrictions, newRestrictions);
- // We are changing the settings affected by restrictions to their current
- // value with a forced update to ensure that all cross profile dependencies
- // are taken into account. Also make sure the settings update to.. the same
- // value passes the security checks, so clear binder calling id.
- if (changedRestrictions.contains(UserManager.DISALLOW_SHARE_LOCATION)) {
- final long identity = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- Setting setting = getSecureSetting(
- Settings.Secure.LOCATION_MODE, userId);
- updateSecureSetting(Settings.Secure.LOCATION_MODE,
- setting != null ? setting.getValue() : null, null,
- true, userId, true);
- setting = getSecureSetting(
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED, userId);
- updateSecureSetting(Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- setting != null ? setting.getValue() : null, null,
- true, userId, true);
+ IUserRestrictionsListener listener = new IUserRestrictionsListener.Stub() {
+ @Override
+ public void onUserRestrictionsChanged(int userId,
+ Bundle newRestrictions, Bundle prevRestrictions) {
+ Set<String> changedRestrictions =
+ getRestrictionDiff(prevRestrictions, newRestrictions);
+ // We are changing the settings affected by restrictions to their current
+ // value with a forced update to ensure that all cross profile dependencies
+ // are taken into account. Also make sure the settings update to.. the same
+ // value passes the security checks, so clear binder calling id.
+ if (changedRestrictions.contains(UserManager.DISALLOW_SHARE_LOCATION)) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ Setting setting = getSecureSetting(
+ Settings.Secure.LOCATION_MODE, userId);
+ updateSecureSetting(Settings.Secure.LOCATION_MODE,
+ setting != null ? setting.getValue() : null, null,
+ true, userId, true);
+ setting = getSecureSetting(
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED, userId);
+ updateSecureSetting(Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+ setting != null ? setting.getValue() : null, null,
+ true, userId, true);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- } finally {
- Binder.restoreCallingIdentity(identity);
}
- }
- if (changedRestrictions.contains(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES)
- || changedRestrictions.contains(
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY)) {
- final long identity = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- Setting setting = getGlobalSetting(Settings.Global.INSTALL_NON_MARKET_APPS);
- String value = setting != null ? setting.getValue() : null;
- updateGlobalSetting(Settings.Global.INSTALL_NON_MARKET_APPS,
- value, null, true, userId, true);
+ if (changedRestrictions.contains(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES)
+ || changedRestrictions.contains(
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY)) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ Setting setting = getGlobalSetting(
+ Settings.Global.INSTALL_NON_MARKET_APPS);
+ String value = setting != null ? setting.getValue() : null;
+ updateGlobalSetting(Settings.Global.INSTALL_NON_MARKET_APPS,
+ value, null, true, userId, true);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- } finally {
- Binder.restoreCallingIdentity(identity);
}
- }
- if (changedRestrictions.contains(UserManager.DISALLOW_DEBUGGING_FEATURES)) {
- final long identity = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- Setting setting = getGlobalSetting(Settings.Global.ADB_ENABLED);
- String value = setting != null ? setting.getValue() : null;
- updateGlobalSetting(Settings.Global.ADB_ENABLED,
- value, null, true, userId, true);
+ if (changedRestrictions.contains(UserManager.DISALLOW_DEBUGGING_FEATURES)) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ Setting setting = getGlobalSetting(Settings.Global.ADB_ENABLED);
+ String value = setting != null ? setting.getValue() : null;
+ updateGlobalSetting(Settings.Global.ADB_ENABLED,
+ value, null, true, userId, true);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- } finally {
- Binder.restoreCallingIdentity(identity);
}
- }
- if (changedRestrictions.contains(UserManager.ENSURE_VERIFY_APPS)) {
- final long identity = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- Setting enable = getGlobalSetting(
- Settings.Global.PACKAGE_VERIFIER_ENABLE);
- String enableValue = enable != null ? enable.getValue() : null;
- updateGlobalSetting(Settings.Global.PACKAGE_VERIFIER_ENABLE,
- enableValue, null, true, userId, true);
- Setting include = getGlobalSetting(
- Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB);
- String includeValue = include != null ? include.getValue() : null;
- updateGlobalSetting(Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB,
- includeValue, null, true, userId, true);
+ if (changedRestrictions.contains(UserManager.ENSURE_VERIFY_APPS)) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ Setting enable = getGlobalSetting(
+ Settings.Global.PACKAGE_VERIFIER_ENABLE);
+ String enableValue = enable != null ? enable.getValue() : null;
+ updateGlobalSetting(Settings.Global.PACKAGE_VERIFIER_ENABLE,
+ enableValue, null, true, userId, true);
+ Setting include = getGlobalSetting(
+ Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB);
+ String includeValue = include != null ? include.getValue() : null;
+ updateGlobalSetting(Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB,
+ includeValue, null, true, userId, true);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- } finally {
- Binder.restoreCallingIdentity(identity);
}
- }
- if (changedRestrictions.contains(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) {
- final long identity = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- Setting setting = getGlobalSetting(
- Settings.Global.PREFERRED_NETWORK_MODE);
- String value = setting != null ? setting.getValue() : null;
- updateGlobalSetting(Settings.Global.PREFERRED_NETWORK_MODE,
- value, null, true, userId, true);
+ if (changedRestrictions.contains(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ Setting setting = getGlobalSetting(
+ Settings.Global.PREFERRED_NETWORK_MODE);
+ String value = setting != null ? setting.getValue() : null;
+ updateGlobalSetting(Settings.Global.PREFERRED_NETWORK_MODE,
+ value, null, true, userId, true);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- } finally {
- Binder.restoreCallingIdentity(identity);
}
}
- });
+ };
+ mUserManager.addUserRestrictionsListener(listener);
}
private static Set<String> getRestrictionDiff(Bundle prevRestrictions, Bundle newRestrictions) {
@@ -1076,7 +1075,7 @@ public class SettingsProvider extends ContentProvider {
return false;
}
- private Map<String, String> getAllConfigFlags(@Nullable String prefix) {
+ private HashMap<String, String> getAllConfigFlags(@Nullable String prefix) {
if (DEBUG) {
Slog.v(LOG_TAG, "getAllConfigFlags() for " + prefix);
}
@@ -1085,12 +1084,11 @@ public class SettingsProvider extends ContentProvider {
// Get the settings.
SettingsState settingsState = mSettingsRegistry.getSettingsLocked(
SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM);
-
List<String> names = getSettingsNamesLocked(SETTINGS_TYPE_CONFIG,
UserHandle.USER_SYSTEM);
final int nameCount = names.size();
- Map<String, String> flagsToValues = new HashMap<>(names.size());
+ HashMap<String, String> flagsToValues = new HashMap<>(names.size());
for (int i = 0; i < nameCount; i++) {
String name = names.get(i);
@@ -1188,6 +1186,17 @@ public class SettingsProvider extends ContentProvider {
MUTATION_OPERATION_RESET, false, mode);
}
+ private boolean isSettingRestrictedForUser(String name, int userId,
+ String value, int callerUid) {
+ final long oldId = Binder.clearCallingIdentity();
+ try {
+ return (name != null
+ && mUserManager.isSettingRestrictedForUser(name, userId, value, callerUid));
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
private boolean mutateGlobalSetting(String name, String value, String tag,
boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
int mode) {
@@ -1199,8 +1208,7 @@ public class SettingsProvider extends ContentProvider {
// If this is a setting that is currently restricted for this user, do not allow
// unrestricting changes.
- if (name != null && mUserManagerInternal.isSettingRestrictedForUser(
- name, callingUserId, value, Binder.getCallingUid())) {
+ if (isSettingRestrictedForUser(name, callingUserId, value, Binder.getCallingUid())) {
return false;
}
@@ -1508,8 +1516,7 @@ public class SettingsProvider extends ContentProvider {
// If this is a setting that is currently restricted for this user, do not allow
// unrestricting changes.
- if (name != null && mUserManagerInternal.isSettingRestrictedForUser(
- name, callingUserId, value, Binder.getCallingUid())) {
+ if (isSettingRestrictedForUser(name, callingUserId, value, Binder.getCallingUid())) {
return false;
}
@@ -1649,8 +1656,7 @@ public class SettingsProvider extends ContentProvider {
// Resolve the userId on whose behalf the call is made.
final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(runAsUserId);
- if (name != null && mUserManagerInternal.isSettingRestrictedForUser(
- name, callingUserId, value, Binder.getCallingUid())) {
+ if (isSettingRestrictedForUser(name, callingUserId, value, Binder.getCallingUid())) {
return false;
}
@@ -2057,8 +2063,7 @@ public class SettingsProvider extends ContentProvider {
"get/set setting for user", null);
}
- private Bundle packageValueForCallResult(Setting setting,
- boolean trackingGeneration) {
+ private Bundle packageValueForCallResult(Setting setting, boolean trackingGeneration) {
if (!trackingGeneration) {
if (setting == null || setting.isNull()) {
return NULL_SETTING_BUNDLE;
@@ -2073,6 +2078,21 @@ public class SettingsProvider extends ContentProvider {
return result;
}
+ private Bundle packageValuesForCallResult(HashMap<String, String> keyValues,
+ boolean trackingGeneration) {
+ Bundle result = new Bundle();
+ result.putSerializable(Settings.NameValueTable.VALUE, keyValues);
+ if (trackingGeneration) {
+ synchronized (mLock) {
+ mSettingsRegistry.mGenerationRegistry.addGenerationData(result,
+ mSettingsRegistry.getSettingsLocked(
+ SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM).mKey);
+ }
+ }
+
+ return result;
+ }
+
private static int getRequestingUserId(Bundle args) {
final int callingUserId = UserHandle.getCallingUserId();
return (args != null) ? args.getInt(Settings.CALL_METHOD_USER_KEY, callingUserId)
@@ -3173,7 +3193,7 @@ public class SettingsProvider extends ContentProvider {
} catch (SecurityException e) {
Slog.w(LOG_TAG, "Failed to notify for " + userId + ": " + uri, e);
}
- if (DEBUG || true) {
+ if (DEBUG) {
Slog.v(LOG_TAG, "Notifying for " + userId + ": " + uri);
}
} break;
@@ -3186,7 +3206,7 @@ public class SettingsProvider extends ContentProvider {
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 183;
+ private static final int SETTINGS_VERSION = 184;
private final int mUserId;
@@ -3825,23 +3845,7 @@ public class SettingsProvider extends ContentProvider {
}
if (currentVersion == 155) {
- // Version 156: Set the default value for CHARGING_STARTED_SOUND.
- final SettingsState globalSettings = getGlobalSettingsLocked();
- final String oldValue = globalSettings.getSettingLocked(
- Global.CHARGING_STARTED_SOUND).getValue();
- final String oldDefault = getContext().getResources().getString(
- R.string.def_wireless_charging_started_sound);
- if (TextUtils.equals(null, oldValue)
- || TextUtils.equals(oldValue, oldDefault)) {
- final String defaultValue = getContext().getResources().getString(
- R.string.def_charging_started_sound);
- if (!TextUtils.isEmpty(defaultValue)) {
- globalSettings.insertSettingLocked(
- Settings.Global.CHARGING_STARTED_SOUND, defaultValue,
- null, true, SettingsState.SYSTEM_PACKAGE_NAME);
- }
-
- }
+ // Version 156: migrated to version 184
currentVersion = 156;
}
@@ -4395,8 +4399,7 @@ public class SettingsProvider extends ContentProvider {
if (currentVersion == 182) {
// Remove secure bubble settings.
- getSecureSettingsLocked(userId).deleteSettingLocked(
- Secure.NOTIFICATION_BUBBLES);
+ getSecureSettingsLocked(userId).deleteSettingLocked("notification_bubbles");
// Add global bubble settings.
getGlobalSettingsLocked().insertSettingLocked(Global.NOTIFICATION_BUBBLES,
@@ -4407,6 +4410,48 @@ public class SettingsProvider extends ContentProvider {
currentVersion = 183;
}
+ if (currentVersion == 183) {
+ // Version 184: Set default values for WIRELESS_CHARGING_STARTED_SOUND
+ // and CHARGING_STARTED_SOUND
+ final SettingsState globalSettings = getGlobalSettingsLocked();
+
+ final String oldValueWireless = globalSettings.getSettingLocked(
+ Global.WIRELESS_CHARGING_STARTED_SOUND).getValue();
+ final String oldValueWired = globalSettings.getSettingLocked(
+ Global.CHARGING_STARTED_SOUND).getValue();
+
+ final String defaultValueWireless = getContext().getResources().getString(
+ R.string.def_wireless_charging_started_sound);
+ final String defaultValueWired = getContext().getResources().getString(
+ R.string.def_charging_started_sound);
+
+ // wireless charging sound
+ if (oldValueWireless == null
+ || TextUtils.equals(oldValueWireless, defaultValueWired)) {
+ if (!TextUtils.isEmpty(defaultValueWireless)) {
+ globalSettings.insertSettingLocked(
+ Global.WIRELESS_CHARGING_STARTED_SOUND, defaultValueWireless,
+ null /* tag */, true /* makeDefault */,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ } else if (!TextUtils.isEmpty(defaultValueWired)) {
+ // if the wireless sound is empty, use the wired charging sound
+ globalSettings.insertSettingLocked(
+ Global.WIRELESS_CHARGING_STARTED_SOUND, defaultValueWired,
+ null /* tag */, true /* makeDefault */,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ }
+
+ // wired charging sound
+ if (oldValueWired == null && !TextUtils.isEmpty(defaultValueWired)) {
+ globalSettings.insertSettingLocked(
+ Global.CHARGING_STARTED_SOUND, defaultValueWired,
+ null /* tag */, true /* makeDefault */,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ currentVersion = 184;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index c05c4cdf72d7..de6a3a8840a4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -24,7 +24,6 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
import android.os.Binder;
import android.os.Build;
@@ -49,7 +48,6 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
-import com.android.server.LocalServices;
import libcore.io.IoUtils;
@@ -1175,9 +1173,8 @@ final class SettingsState {
}
// If SetupWizard, done.
- PackageManagerInternal packageManagerInternal = LocalServices.getService(
- PackageManagerInternal.class);
- if (packageName.equals(packageManagerInternal.getSetupWizardPackageName())) {
+ String setupWizPackage = context.getPackageManager().getSetupWizardPackageName();
+ if (packageName.equals(setupWizPackage)) {
sSystemUids.put(uid, uid);
return true;
}
diff --git a/packages/SettingsProvider/test/src/android/provider/OWNERS b/packages/SettingsProvider/test/src/android/provider/OWNERS
new file mode 100644
index 000000000000..f3241ea9d1f9
--- /dev/null
+++ b/packages/SettingsProvider/test/src/android/provider/OWNERS
@@ -0,0 +1,4 @@
+per-file * = *
+
+# Please reach out to the Android B&R team for settings backup changes
+per-file SettingsBackupTest.java = alsutton@google.com, nathch@google.com, rthakohov@google.com
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 6ea3db366f66..179ba8ad086a 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -16,9 +16,10 @@
package android.provider;
-import static com.google.android.collect.Sets.newHashSet;
+import static android.provider.settings.backup.DeviceSpecificSettings.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP;
-import static junit.framework.Assert.assertTrue;
+import static com.google.android.collect.Sets.newHashSet;
+import static com.google.common.truth.Truth.assertWithMessage;
import static java.lang.reflect.Modifier.isFinal;
import static java.lang.reflect.Modifier.isPublic;
@@ -558,6 +559,7 @@ public class SettingsBackupTest {
Settings.Global.WIFI_WATCHDOG_ON,
Settings.Global.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON,
Settings.Global.CHARGING_STARTED_SOUND,
+ Settings.Global.WIRELESS_CHARGING_STARTED_SOUND,
Settings.Global.WINDOW_ANIMATION_SCALE,
Settings.Global.WTF_IS_FATAL,
Settings.Global.ZEN_MODE,
@@ -595,7 +597,10 @@ public class SettingsBackupTest {
Settings.Secure.ANR_SHOW_BACKGROUND,
Settings.Secure.ASSISTANT,
Settings.Secure.ASSIST_DISCLOSURE_ENABLED,
+ Settings.Secure.ASSIST_GESTURE_ENABLED,
Settings.Secure.ASSIST_GESTURE_SENSITIVITY,
+ Settings.Secure.ASSIST_GESTURE_WAKE_ENABLED,
+ Settings.Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
Settings.Secure.ASSIST_GESTURE_SETUP_COMPLETE,
Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
Settings.Secure.ASSIST_STRUCTURE_ENABLED,
@@ -655,7 +660,6 @@ public class SettingsBackupTest {
Settings.Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED,
Settings.Secure.ODI_CAPTIONS_ENABLED,
- Settings.Secure.PACKAGE_VERIFIER_STATE,
Settings.Secure.PACKAGE_VERIFIER_USER_CONSENT,
Settings.Secure.PARENTAL_CONTROL_LAST_UPDATE,
Settings.Secure.PAYMENT_SERVICE_SEARCH_URI,
@@ -721,7 +725,13 @@ public class SettingsBackupTest {
Settings.Secure.BIOMETRIC_DEBUG_ENABLED,
Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED,
Settings.Secure.FACE_UNLOCK_DIVERSITY_REQUIRED,
- Settings.Secure.MANAGED_PROVISIONING_DPC_DOWNLOADED);
+ Settings.Secure.MANAGED_PROVISIONING_DPC_DOWNLOADED,
+ Settings.Secure.AWARE_ENABLED,
+ Settings.Secure.SKIP_GESTURE,
+ Settings.Secure.SILENCE_GESTURE,
+ Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
+ Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
+ Settings.Secure.FACE_UNLOCK_RE_ENROLL);
@Test
public void systemSettingsBackedUpOrBlacklisted() {
@@ -743,7 +753,7 @@ public class SettingsBackupTest {
public void secureSettingsBackedUpOrBlacklisted() {
HashSet<String> keys = new HashSet<String>();
Collections.addAll(keys, SecureSettings.SETTINGS_TO_BACKUP);
- Collections.addAll(keys, Settings.Secure.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP);
+ Collections.addAll(keys, DEVICE_SPECIFIC_SETTINGS_TO_BACKUP);
checkSettingsBackedUpOrBlacklisted(
getCandidateSettings(Settings.Secure.class),
keys,
@@ -754,12 +764,11 @@ public class SettingsBackupTest {
Set<String> settings, Set<String> settingsToBackup, Set<String> blacklist) {
Set<String> settingsNotBackedUp = difference(settings, settingsToBackup);
Set<String> settingsNotBackedUpOrBlacklisted = difference(settingsNotBackedUp, blacklist);
- assertTrue(
- "Settings not backed up or blacklisted",
- settingsNotBackedUpOrBlacklisted.isEmpty());
+ assertWithMessage("Settings not backed up or blacklisted")
+ .that(settingsNotBackedUpOrBlacklisted).isEmpty();
- assertTrue(
- "blacklisted settings backed up", intersect(settingsToBackup, blacklist).isEmpty());
+ assertWithMessage("blacklisted settings backed up")
+ .that(intersect(settingsToBackup, blacklist)).isEmpty();
}
private static Set<String> getCandidateSettings(
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index b2ff4b3268b2..54e291f904eb 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -29,6 +29,7 @@
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_PRECISE_PHONE_STATE" />
+ <uses-permission android:name="android.permission.READ_ACTIVE_EMERGENCY_SESSION" />
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
@@ -172,6 +173,8 @@
<!-- Permissions needed to test system only camera devices -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.SYSTEM_CAMERA" />
+ <!-- Permissions needed for CTS camera test: RecordingTest.java when assuming shell id -->
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- Permission needed to enable/disable Bluetooth/Wifi -->
<uses-permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED" />
<uses-permission android:name="android.permission.MANAGE_WIFI_WHEN_WIRELESS_CONSENT_REQUIRED" />
@@ -238,29 +241,12 @@
<activity
android:name=".BugreportWarningActivity"
- android:theme="@android:style/Theme.DeviceDefault.Dialog.Alert"
+ android:theme="@*android:style/Theme.DeviceDefault.Dialog.Alert.DayNight"
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true"
android:exported="false" />
<receiver
- android:name=".BugreportReceiver"
- android:permission="android.permission.DUMP">
- <intent-filter>
- <action android:name="com.android.internal.intent.action.BUGREPORT_STARTED" />
- <action android:name="com.android.internal.intent.action.BUGREPORT_FINISHED" />
- </intent-filter>
- </receiver>
-
- <receiver
- android:name=".RemoteBugreportReceiver"
- android:permission="android.permission.DUMP">
- <intent-filter>
- <action android:name="com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED" />
- </intent-filter>
- </receiver>
-
- <receiver
android:name=".BugreportRequestedReceiver"
android:permission="android.permission.TRIGGER_SHELL_BUGREPORT">
<intent-filter>
diff --git a/packages/Shell/res/values-pt-rBR/strings.xml b/packages/Shell/res/values-pt-rBR/strings.xml
index d0373b1b9ccd..394f852d3a33 100644
--- a/packages/Shell/res/values-pt-rBR/strings.xml
+++ b/packages/Shell/res/values-pt-rBR/strings.xml
@@ -18,21 +18,21 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_notification_channel" msgid="2574150205913861141">"Relatórios de bugs"</string>
- <string name="bugreport_in_progress_title" msgid="4311705936714972757">"O relatório do bug <xliff:g id="ID">#%d</xliff:g> está sendo gerado"</string>
+ <string name="bugreport_in_progress_title" msgid="4311705936714972757">"O relatório de bug <xliff:g id="ID">#%d</xliff:g> está sendo gerado"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Relatório de bug <xliff:g id="ID">#%d</xliff:g> capturado"</string>
- <string name="bugreport_updating_title" msgid="4423539949559634214">"Adicionando detalhes ao relatório do bug"</string>
+ <string name="bugreport_updating_title" msgid="4423539949559634214">"Adicionando detalhes ao relatório de bug"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Aguarde…"</string>
<string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"O relatório de bugs será exibido no smartphone em breve"</string>
- <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Selecione para compartilhar o relatório do bug"</string>
- <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Toque para compartilhar seu relatório do bug"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Selecione para compartilhar seu relatório do bug sem uma captura de tela ou aguarde a conclusão"</string>
+ <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Selecione para compartilhar o relatório de bug"</string>
+ <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Toque para compartilhar seu relatório de bug"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Selecione para compartilhar seu relatório de bug sem uma captura de tela ou aguarde a conclusão"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Toque para compartilhar seu relatório de bug sem captura de tela ou aguarde a conclusão"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Toque para compartilhar seu relatório de bug sem captura de tela ou aguarde a conclusão"</string>
<string name="bugreport_confirm" msgid="5917407234515812495">"Os relatórios de bugs contêm dados dos diversos arquivos de registros do sistema, que podem incluir dados que você considera confidenciais (como dados de uso de apps e de local). Compartilhe relatórios de bugs somente com pessoas e apps nos quais você confia."</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Não mostrar novamente"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Relatórios de bugs"</string>
<string name="bugreport_unreadable_text" msgid="586517851044535486">"Não foi possível ler o arquivo de relatório de bug"</string>
- <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Não foi possível adicionar detalhes do relatório do bug ao arquivo ZIP"</string>
+ <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Não foi possível adicionar detalhes do relatório de bug ao arquivo ZIP"</string>
<string name="bugreport_unnamed" msgid="2800582406842092709">"sem nome"</string>
<string name="bugreport_info_action" msgid="2158204228510576227">"Detalhes"</string>
<string name="bugreport_screenshot_action" msgid="8677781721940614995">"Capturas de tela"</string>
@@ -43,5 +43,5 @@
<string name="bugreport_info_title" msgid="2306030793918239804">"Título do bug"</string>
<string name="bugreport_info_description" msgid="5072835127481627722">"Resumo do bug"</string>
<string name="save" msgid="4781509040564835759">"Salvar"</string>
- <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"Compartilhar relatório do bug"</string>
+ <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"Compartilhar relatório de bug"</string>
</resources>
diff --git a/packages/Shell/res/values-pt/strings.xml b/packages/Shell/res/values-pt/strings.xml
index d0373b1b9ccd..394f852d3a33 100644
--- a/packages/Shell/res/values-pt/strings.xml
+++ b/packages/Shell/res/values-pt/strings.xml
@@ -18,21 +18,21 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_notification_channel" msgid="2574150205913861141">"Relatórios de bugs"</string>
- <string name="bugreport_in_progress_title" msgid="4311705936714972757">"O relatório do bug <xliff:g id="ID">#%d</xliff:g> está sendo gerado"</string>
+ <string name="bugreport_in_progress_title" msgid="4311705936714972757">"O relatório de bug <xliff:g id="ID">#%d</xliff:g> está sendo gerado"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Relatório de bug <xliff:g id="ID">#%d</xliff:g> capturado"</string>
- <string name="bugreport_updating_title" msgid="4423539949559634214">"Adicionando detalhes ao relatório do bug"</string>
+ <string name="bugreport_updating_title" msgid="4423539949559634214">"Adicionando detalhes ao relatório de bug"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Aguarde…"</string>
<string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"O relatório de bugs será exibido no smartphone em breve"</string>
- <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Selecione para compartilhar o relatório do bug"</string>
- <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Toque para compartilhar seu relatório do bug"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Selecione para compartilhar seu relatório do bug sem uma captura de tela ou aguarde a conclusão"</string>
+ <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Selecione para compartilhar o relatório de bug"</string>
+ <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Toque para compartilhar seu relatório de bug"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Selecione para compartilhar seu relatório de bug sem uma captura de tela ou aguarde a conclusão"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Toque para compartilhar seu relatório de bug sem captura de tela ou aguarde a conclusão"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Toque para compartilhar seu relatório de bug sem captura de tela ou aguarde a conclusão"</string>
<string name="bugreport_confirm" msgid="5917407234515812495">"Os relatórios de bugs contêm dados dos diversos arquivos de registros do sistema, que podem incluir dados que você considera confidenciais (como dados de uso de apps e de local). Compartilhe relatórios de bugs somente com pessoas e apps nos quais você confia."</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Não mostrar novamente"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Relatórios de bugs"</string>
<string name="bugreport_unreadable_text" msgid="586517851044535486">"Não foi possível ler o arquivo de relatório de bug"</string>
- <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Não foi possível adicionar detalhes do relatório do bug ao arquivo ZIP"</string>
+ <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Não foi possível adicionar detalhes do relatório de bug ao arquivo ZIP"</string>
<string name="bugreport_unnamed" msgid="2800582406842092709">"sem nome"</string>
<string name="bugreport_info_action" msgid="2158204228510576227">"Detalhes"</string>
<string name="bugreport_screenshot_action" msgid="8677781721940614995">"Capturas de tela"</string>
@@ -43,5 +43,5 @@
<string name="bugreport_info_title" msgid="2306030793918239804">"Título do bug"</string>
<string name="bugreport_info_description" msgid="5072835127481627722">"Resumo do bug"</string>
<string name="save" msgid="4781509040564835759">"Salvar"</string>
- <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"Compartilhar relatório do bug"</string>
+ <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"Compartilhar relatório de bug"</string>
</resources>
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 602fe3ec12fd..1b35770ccbd7 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -37,6 +37,7 @@ import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
+import android.app.admin.DevicePolicyManager;
import android.content.ClipData;
import android.content.Context;
import android.content.DialogInterface;
@@ -55,16 +56,11 @@ import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
-import android.os.IDumpstate;
-import android.os.IDumpstateListener;
-import android.os.IDumpstateToken;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
-import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -79,7 +75,6 @@ import android.util.SparseArray;
import android.view.ContextThemeWrapper;
import android.view.IWindowManager;
import android.view.View;
-import android.view.View.OnFocusChangeListener;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
@@ -107,6 +102,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -119,31 +116,9 @@ import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
/**
- * Service used to keep progress of bugreport processes ({@code dumpstate} and
- * {@code BugreportManager}).
+ * Service used to trigger system bugreports.
* <p>
- * There can be 2 workflows. One workflow via ({@code dumpstate}) is:
- * <ol>
- * <li>When {@code dumpstate} starts, it sends a {@code BUGREPORT_STARTED} with a sequential id,
- * its pid, and the estimated total effort.
- * <li>{@link BugreportReceiver} receives the intent and delegates it to this service.
- * <li>Upon start, this service:
- * <ol>
- * <li>Issues a system notification so user can watch the progress (which is 0% initially).
- * <li>Polls the {@link SystemProperties} for updates on the {@code dumpstate} progress.
- * <li>If the progress changed, it updates the system notification.
- * </ol>
- * <li>As {@code dumpstate} progresses, it updates the system property.
- * <li>When {@code dumpstate} finishes, it sends a {@code BUGREPORT_FINISHED} intent.
- * <li>{@link BugreportReceiver} receives the intent and delegates it to this service, which in
- * turn:
- * <ol>
- * <li>Updates the system notification so user can share the bugreport.
- * <li>Stops monitoring that {@code dumpstate} process.
- * <li>Stops itself if it doesn't have any process left to monitor.
- * </ol>
- * </ol>
- * The second workflow using Bugreport API({@code BugreportManager}) is:
+ * The workflow uses Bugreport API({@code BugreportManager}) and is as follows:
* <ol>
* <li>System apps like Settings or SysUI broadcasts {@code BUGREPORT_REQUESTED}.
* <li>{@link BugreportRequestedReceiver} receives the intent and delegates it to this service.
@@ -157,20 +132,18 @@ public class BugreportProgressService extends Service {
private static final String TAG = "BugreportProgressService";
private static final boolean DEBUG = false;
- private static final String AUTHORITY = "com.android.shell";
+ private Intent startSelfIntent;
- // External intents sent by dumpstate.
- static final String INTENT_BUGREPORT_STARTED =
- "com.android.internal.intent.action.BUGREPORT_STARTED";
- static final String INTENT_BUGREPORT_FINISHED =
- "com.android.internal.intent.action.BUGREPORT_FINISHED";
- static final String INTENT_REMOTE_BUGREPORT_FINISHED =
- "com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED";
+ private static final String AUTHORITY = "com.android.shell";
// External intent used to trigger bugreport API.
static final String INTENT_BUGREPORT_REQUESTED =
"com.android.internal.intent.action.BUGREPORT_REQUESTED";
+ // Intent sent to notify external apps that bugreport finished
+ static final String INTENT_BUGREPORT_FINISHED =
+ "com.android.internal.intent.action.BUGREPORT_FINISHED";
+
// Internal intents used on notification actions.
static final String INTENT_BUGREPORT_CANCEL = "android.intent.action.BUGREPORT_CANCEL";
static final String INTENT_BUGREPORT_SHARE = "android.intent.action.BUGREPORT_SHARE";
@@ -183,7 +156,6 @@ public class BugreportProgressService extends Service {
static final String EXTRA_BUGREPORT_TYPE = "android.intent.extra.BUGREPORT_TYPE";
static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT";
static final String EXTRA_ID = "android.intent.extra.ID";
- static final String EXTRA_PID = "android.intent.extra.PID";
static final String EXTRA_MAX = "android.intent.extra.MAX";
static final String EXTRA_NAME = "android.intent.extra.NAME";
static final String EXTRA_TITLE = "android.intent.extra.TITLE";
@@ -213,15 +185,9 @@ public class BugreportProgressService extends Service {
*/
static final int SCREENSHOT_DELAY_SECONDS = 3;
- // TODO: will be gone once fully migrated to Binder
- /** System properties used to communicate with dumpstate progress. */
- private static final String DUMPSTATE_PREFIX = "dumpstate.";
- private static final String NAME_SUFFIX = ".name";
+ /** System property where dumpstate stores last triggered bugreport id */
private static final String PROPERTY_LAST_ID = "dumpstate.last_id";
- /** System property (and value) used to stop dumpstate. */
- // TODO: should call ActiveManager API instead
- private static final String CTL_STOP = "ctl.stop";
private static final String BUGREPORT_SERVICE = "bugreport";
/**
@@ -235,6 +201,24 @@ public class BugreportProgressService extends Service {
private static final String NOTIFICATION_CHANNEL_ID = "bugreports";
+ /**
+ * Always keep the newest 8 bugreport files.
+ */
+ private static final int MIN_KEEP_COUNT = 8;
+
+ /**
+ * Always keep bugreports taken in the last week.
+ */
+ private static final long MIN_KEEP_AGE = DateUtils.WEEK_IN_MILLIS;
+
+ private static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport";
+
+ /** Always keep just the last 3 remote bugreport's files around. */
+ private static final int REMOTE_BUGREPORT_FILES_AMOUNT = 3;
+
+ /** Always keep remote bugreport files created in the last day. */
+ private static final long REMOTE_MIN_KEEP_AGE = DateUtils.DAY_IN_MILLIS;
+
private final Object mLock = new Object();
/** Managed bugreport info (keyed by id) */
@@ -250,8 +234,6 @@ public class BugreportProgressService extends Service {
private File mScreenshotsDir;
- private boolean mUsingBugreportApi;
-
private BugreportManager mBugreportManager;
/**
@@ -281,6 +263,7 @@ public class BugreportProgressService extends Service {
mMainThreadHandler = new Handler(Looper.getMainLooper());
mServiceHandler = new ServiceHandler("BugreportProgressServiceMainThread");
mScreenshotHandler = new ScreenshotHandler("BugreportProgressServiceScreenshotThread");
+ startSelfIntent = new Intent(this, this.getClass());
mScreenshotsDir = new File(getFilesDir(), SCREENSHOT_DIR);
if (!mScreenshotsDir.exists()) {
@@ -307,6 +290,9 @@ public class BugreportProgressService extends Service {
public int onStartCommand(Intent intent, int flags, int startId) {
Log.v(TAG, "onStartCommand(): " + dumpIntent(intent));
if (intent != null) {
+ if (!intent.hasExtra(EXTRA_ORIGINAL_INTENT) && !intent.hasExtra(EXTRA_ID)) {
+ return START_NOT_STICKY;
+ }
// Handle it in a separate thread.
final Message msg = mServiceHandler.obtainMessage();
msg.what = MSG_SERVICE_COMMAND;
@@ -348,22 +334,27 @@ public class BugreportProgressService extends Service {
}
}
+ private static String getFileName(BugreportInfo info, String suffix) {
+ return String.format("%s-%s%s", info.baseName, info.name, suffix);
+ }
+
private final class BugreportCallbackImpl extends BugreportCallback {
+ @GuardedBy("mLock")
private final BugreportInfo mInfo;
- BugreportCallbackImpl(String name, @Nullable String title, @Nullable String description) {
- // pid not used in this workflow, so setting default = 0
- mInfo = new BugreportInfo(mContext, 0 /* pid */, name,
- 100 /* max progress*/, title, description);
+ BugreportCallbackImpl(BugreportInfo info) {
+ mInfo = info;
}
@Override
public void onProgress(float progress) {
- if (progress == 0) {
- trackInfoWithId();
+ synchronized (mLock) {
+ if (progress == 0) {
+ trackInfoWithIdLocked();
+ }
+ checkProgressUpdatedLocked(mInfo, (int) progress);
}
- checkProgressUpdated(mInfo, (int) progress);
}
/**
@@ -372,18 +363,21 @@ public class BugreportProgressService extends Service {
*/
@Override
public void onError(@BugreportErrorCode int errorCode) {
- trackInfoWithId();
- stopProgress(mInfo.id);
+ synchronized (mLock) {
+ trackInfoWithIdLocked();
+ stopProgressLocked(mInfo.id);
+ }
Log.e(TAG, "Bugreport API callback onError() errorCode = " + errorCode);
return;
}
@Override
public void onFinished() {
- trackInfoWithId();
- // Stop running on foreground, otherwise share notification cannot be dismissed.
- onBugreportFinished(mInfo.id);
- stopSelfWhenDone();
+ mInfo.renameBugreportFile();
+ mInfo.renameScreenshots(mScreenshotsDir);
+ synchronized (mLock) {
+ sendBugreportFinishedBroadcastLocked();
+ }
}
/**
@@ -392,7 +386,8 @@ public class BugreportProgressService extends Service {
* when dumpstate calls one of the callback functions (onProgress, onFinished, onError)
* after the id has been incremented.
*/
- private void trackInfoWithId() {
+ @GuardedBy("mLock")
+ private void trackInfoWithIdLocked() {
final int id = SystemProperties.getInt(PROPERTY_LAST_ID, 1);
if (mBugreportInfos.get(id) == null) {
mInfo.id = id;
@@ -400,6 +395,93 @@ public class BugreportProgressService extends Service {
}
return;
}
+
+ @GuardedBy("mLock")
+ private void sendBugreportFinishedBroadcastLocked() {
+ final String bugreportFilePath = mInfo.bugreportFile.getAbsolutePath();
+ if (mInfo.bugreportFile.length() == 0) {
+ Log.e(TAG, "Bugreport file empty. File path = " + bugreportFilePath);
+ return;
+ }
+ if (mInfo.type == BugreportParams.BUGREPORT_MODE_REMOTE) {
+ sendRemoteBugreportFinishedBroadcast(mContext, bugreportFilePath,
+ mInfo.bugreportFile);
+ } else {
+ trackInfoWithIdLocked();
+ cleanupOldFiles(MIN_KEEP_COUNT, MIN_KEEP_AGE);
+ final Intent intent = new Intent(INTENT_BUGREPORT_FINISHED);
+ intent.putExtra(EXTRA_BUGREPORT, bugreportFilePath);
+ intent.putExtra(EXTRA_SCREENSHOT, getScreenshotForIntent(mInfo));
+ mContext.sendBroadcast(intent, android.Manifest.permission.DUMP);
+ onBugreportFinished(mInfo);
+ }
+ }
+ }
+
+ private static void sendRemoteBugreportFinishedBroadcast(Context context,
+ String bugreportFileName, File bugreportFile) {
+ cleanupOldFiles(REMOTE_BUGREPORT_FILES_AMOUNT, REMOTE_MIN_KEEP_AGE);
+ final Intent intent = new Intent(DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH);
+ final Uri bugreportUri = getUri(context, bugreportFile);
+ final String bugreportHash = generateFileHash(bugreportFileName);
+ if (bugreportHash == null) {
+ Log.e(TAG, "Error generating file hash for remote bugreport");
+ }
+ intent.setDataAndType(bugreportUri, BUGREPORT_MIMETYPE);
+ intent.putExtra(DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH, bugreportHash);
+ intent.putExtra(EXTRA_BUGREPORT, bugreportFileName);
+ context.sendBroadcastAsUser(intent, UserHandle.SYSTEM,
+ android.Manifest.permission.DUMP);
+ }
+
+ /**
+ * Checks if screenshot array is non-empty and returns the first screenshot's path. The first
+ * screenshot is the default screenshot for the bugreport types that take it.
+ */
+ private static String getScreenshotForIntent(BugreportInfo info) {
+ if (!info.screenshotFiles.isEmpty()) {
+ final File screenshotFile = info.screenshotFiles.get(0);
+ final String screenshotFilePath = screenshotFile.getAbsolutePath();
+ return screenshotFilePath;
+ }
+ return null;
+ }
+
+ private static String generateFileHash(String fileName) {
+ String fileHash = null;
+ try {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ FileInputStream input = new FileInputStream(new File(fileName));
+ byte[] buffer = new byte[65536];
+ int size;
+ while ((size = input.read(buffer)) > 0) {
+ md.update(buffer, 0, size);
+ }
+ input.close();
+ byte[] hashBytes = md.digest();
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < hashBytes.length; i++) {
+ sb.append(String.format("%02x", hashBytes[i]));
+ }
+ fileHash = sb.toString();
+ } catch (IOException | NoSuchAlgorithmException e) {
+ Log.e(TAG, "generating file hash for bugreport file failed " + fileName, e);
+ }
+ return fileHash;
+ }
+
+ static void cleanupOldFiles(final int minCount, final long minAge) {
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ try {
+ FileUtils.deleteOlderFiles(new File(BUGREPORT_DIR), minCount, minAge);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "RuntimeException deleting old files", e);
+ }
+ return null;
+ }
+ }.execute();
}
/**
@@ -438,40 +520,24 @@ public class BugreportProgressService extends Service {
Log.v(TAG, "handleMessage(): " + dumpIntent((Intent) parcel));
final Intent intent;
if (parcel instanceof Intent) {
- // The real intent was passed to BugreportReceiver, which delegated to the service.
+ // The real intent was passed to BugreportRequestedReceiver,
+ // which delegated to the service.
intent = (Intent) parcel;
} else {
intent = (Intent) msg.obj;
}
final String action = intent.getAction();
- final int pid = intent.getIntExtra(EXTRA_PID, 0);
final int id = intent.getIntExtra(EXTRA_ID, 0);
final int max = intent.getIntExtra(EXTRA_MAX, -1);
final String name = intent.getStringExtra(EXTRA_NAME);
if (DEBUG)
- Log.v(TAG, "action: " + action + ", name: " + name + ", id: " + id + ", pid: "
- + pid + ", max: " + max);
+ Log.v(TAG, "action: " + action + ", name: " + name + ", id: " + id
+ + ", max: " + max);
switch (action) {
case INTENT_BUGREPORT_REQUESTED:
startBugreportAPI(intent);
break;
- case INTENT_BUGREPORT_STARTED:
- if (!mUsingBugreportApi && !startProgress(name, id, pid, max)) {
- stopSelfWhenDone();
- return;
- }
- break;
- case INTENT_BUGREPORT_FINISHED:
- if (!mUsingBugreportApi) {
- if (id == 0) {
- // Shouldn't happen, unless BUGREPORT_FINISHED is received
- // from a legacy, out-of-sync dumpstate process.
- Log.w(TAG, "Missing " + EXTRA_ID + " on intent " + intent);
- }
- onBugreportFinished(id, intent);
- }
- break;
case INTENT_BUGREPORT_INFO_LAUNCH:
launchBugreportInfoDialog(id);
break;
@@ -513,80 +579,47 @@ public class BugreportProgressService extends Service {
private BugreportInfo getInfo(int id) {
final BugreportInfo bugreportInfo = mBugreportInfos.get(id);
if (bugreportInfo == null) {
- Log.w(TAG, "Not monitoring process with ID " + id);
+ Log.w(TAG, "Not monitoring bugreports with ID " + id);
return null;
}
return bugreportInfo;
}
- /**
- * Creates the {@link BugreportInfo} for a process and issue a system notification to
- * indicate its progress.
- *
- * @return whether it succeeded or not.
- */
- private boolean startProgress(String name, int id, int pid, int max) {
- if (name == null) {
- Log.w(TAG, "Missing " + EXTRA_NAME + " on start intent");
- }
- if (id == -1) {
- Log.e(TAG, "Missing " + EXTRA_ID + " on start intent");
- return false;
- }
- if (pid == -1) {
- Log.e(TAG, "Missing " + EXTRA_PID + " on start intent");
- return false;
- }
- if (max <= 0) {
- Log.e(TAG, "Invalid value for extra " + EXTRA_MAX + ": " + max);
- return false;
- }
-
- final BugreportInfo info = new BugreportInfo(mContext, id, pid, name, max);
- if (mBugreportInfos.indexOfKey(id) >= 0) {
- // BUGREPORT_STARTED intent was already received; ignore it.
- Log.w(TAG, "ID " + id + " already watched");
- return true;
- }
- final DumpstateListener listener = new DumpstateListener(info);
- mBugreportInfos.put(info.id, info);
- if (listener.connect()) {
- updateProgress(info);
- return true;
- } else {
- Log.w(TAG, "not updating progress because it could not connect to dumpstate");
- return false;
- }
- }
-
- private String getBugreportName() {
+ private String getBugreportBaseName(@BugreportParams.BugreportMode int type) {
String buildId = SystemProperties.get("ro.build.id", "UNKNOWN_BUILD");
String deviceName = SystemProperties.get("ro.product.name", "UNKNOWN_DEVICE");
- String currentTimestamp = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
- return String.format("bugreport-%s-%s-%s", deviceName, buildId, currentTimestamp);
+ String typeSuffix = null;
+ if (type == BugreportParams.BUGREPORT_MODE_WIFI) {
+ typeSuffix = "wifi";
+ } else if (type == BugreportParams.BUGREPORT_MODE_TELEPHONY) {
+ typeSuffix = "telephony";
+ } else {
+ return String.format("bugreport-%s-%s", deviceName, buildId);
+ }
+ return String.format("bugreport-%s-%s-%s", deviceName, buildId, typeSuffix);
}
private void startBugreportAPI(Intent intent) {
- mUsingBugreportApi = true;
- String bugreportName = getBugreportName();
+ String shareTitle = intent.getStringExtra(EXTRA_TITLE);
+ String shareDescription = intent.getStringExtra(EXTRA_DESCRIPTION);
+ int bugreportType = intent.getIntExtra(EXTRA_BUGREPORT_TYPE,
+ BugreportParams.BUGREPORT_MODE_INTERACTIVE);
+ String baseName = getBugreportBaseName(bugreportType);
+ String name = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
- ParcelFileDescriptor bugreportFd = createReadWriteFile(BUGREPORT_DIR,
- bugreportName + ".zip");
+ BugreportInfo info = new BugreportInfo(mContext, baseName, name,
+ 100 /* max progress*/, shareTitle, shareDescription, bugreportType);
+
+ ParcelFileDescriptor bugreportFd = info.createBugreportFd();
if (bugreportFd == null) {
Log.e(TAG, "Bugreport parcel file descriptor is null.");
return;
}
- int bugreportType = intent.getIntExtra(EXTRA_BUGREPORT_TYPE,
- BugreportParams.BUGREPORT_MODE_INTERACTIVE);
- String shareTitle = intent.getStringExtra(EXTRA_TITLE);
- String shareDescription = intent.getStringExtra(EXTRA_DESCRIPTION);
-
- ParcelFileDescriptor screenshotFd = createReadWriteFile(BUGREPORT_DIR,
- bugreportName + ".png");
+ ParcelFileDescriptor screenshotFd = info.createScreenshotFd();
if (screenshotFd == null) {
Log.e(TAG, "Screenshot parcel file descriptor is null. Deleting bugreport file");
FileUtils.closeQuietly(bugreportFd);
- new File(BUGREPORT_DIR, String.format("%s.zip", bugreportName)).delete();
+ info.bugreportFile.delete();
return;
}
mBugreportManager = (BugreportManager) mContext.getSystemService(
@@ -597,8 +630,7 @@ public class BugreportProgressService extends Service {
+ " bugreport file fd: " + bugreportFd
+ " screenshot file fd: " + screenshotFd);
- BugreportCallbackImpl bugreportCallback = new BugreportCallbackImpl(bugreportName,
- shareTitle, shareDescription);
+ BugreportCallbackImpl bugreportCallback = new BugreportCallbackImpl(info);
try {
mBugreportManager.startBugreport(bugreportFd, screenshotFd,
new BugreportParams(bugreportType), executor, bugreportCallback);
@@ -611,14 +643,13 @@ public class BugreportProgressService extends Service {
}
}
- private ParcelFileDescriptor createReadWriteFile(String dirName, String fileName) {
+ private static ParcelFileDescriptor createReadWriteFile(File file) {
try {
- File f = new File(dirName, fileName);
- f.createNewFile();
- f.setReadable(true, true);
- f.setWritable(true, true);
+ file.createNewFile();
+ file.setReadable(true, true);
+ file.setWritable(true, true);
- ParcelFileDescriptor fd = ParcelFileDescriptor.open(f,
+ ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_APPEND);
return fd;
} catch (IOException e) {
@@ -711,6 +742,9 @@ public class BugreportProgressService extends Service {
} else {
mForegroundId = id;
Log.d(TAG, "Start running as foreground service on id " + mForegroundId);
+ // Explicitly starting the service so that stopForeground() does not crash
+ // Workaround for b/140997620
+ startForegroundService(startSelfIntent);
startForeground(mForegroundId, notification);
}
}
@@ -729,7 +763,8 @@ public class BugreportProgressService extends Service {
/**
* Finalizes the progress on a given bugreport and cancel its notification.
*/
- private void stopProgress(int id) {
+ @GuardedBy("mLock")
+ private void stopProgressLocked(int id) {
if (mBugreportInfos.indexOfKey(id) < 0) {
Log.w(TAG, "ID not watched: " + id);
} else {
@@ -753,14 +788,12 @@ public class BugreportProgressService extends Service {
final BugreportInfo info = getInfo(id);
if (info != null && !info.finished) {
Log.i(TAG, "Cancelling bugreport service (ID=" + id + ") on user's request");
- if (mUsingBugreportApi) {
- mBugreportManager.cancelBugreport();
- } else {
- setSystemProperty(CTL_STOP, BUGREPORT_SERVICE);
- }
+ mBugreportManager.cancelBugreport();
deleteScreenshots(info);
}
- stopProgress(id);
+ synchronized (mLock) {
+ stopProgressLocked(id);
+ }
}
/**
@@ -953,82 +986,15 @@ public class BugreportProgressService extends Service {
}
/**
- * Handles the BUGREPORT_FINISHED intent sent by {@code dumpstate}.
- */
- private void onBugreportFinished(int id, Intent intent) {
- final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
- if (bugreportFile == null) {
- // Should never happen, dumpstate always set the file.
- Log.wtf(TAG, "Missing " + EXTRA_BUGREPORT + " on intent " + intent);
- return;
- }
- final int max = intent.getIntExtra(EXTRA_MAX, -1);
- final File screenshotFile = getFileExtra(intent, EXTRA_SCREENSHOT);
- final String shareTitle = intent.getStringExtra(EXTRA_TITLE);
- final String shareDescription = intent.getStringExtra(EXTRA_DESCRIPTION);
- onBugreportFinished(id, bugreportFile, screenshotFile, shareTitle, shareDescription, max);
- }
-
- /**
- * Handles the onfinish() call by BugreportCallbackImpl using the id
- */
- private void onBugreportFinished(int id) {
- BugreportInfo info = getInfo(id);
- final File bugreportFile = new File(BUGREPORT_DIR, info.name + ".zip");
- final int max = -1; // this is to log metrics for dumpstate duration.
- File screenshotFile = new File(BUGREPORT_DIR, info.name + ".png");
- // If the screenshot file did not get populated implies this type of bugreport does not
- // need the screenshot file; setting the file to null so that empty file doesnt get shared
- if (screenshotFile.length() == 0) {
- if (screenshotFile.delete()) {
- Log.d(TAG, "screenshot file deleted successfully.");
- }
- screenshotFile = null;
- }
- // TODO: Since we are passing id to the function, it should be able to find the info linked
- // to the id and therefore use the value of shareTitle and shareDescription.
- onBugreportFinished(id, bugreportFile, screenshotFile, info.shareTitle,
- info.shareDescription, max);
- }
-
-
- /**
* Wraps up bugreport generation and triggers a notification to share the bugreport.
*/
- private void onBugreportFinished(int id, File bugreportFile, @Nullable File screenshotFile,
- String shareTitle, String shareDescription, int max) {
- mInfoDialog.onBugreportFinished();
- BugreportInfo info = getInfo(id);
- if (info == null) {
- // Happens when BUGREPORT_FINISHED was received without a BUGREPORT_STARTED first.
- Log.v(TAG, "Creating info for untracked ID " + id);
- info = new BugreportInfo(mContext, id);
- DumpstateListener dumpstateListener = new DumpstateListener(info);
- mBugreportInfos.put(id, info);
- }
- info.renameScreenshots(mScreenshotsDir);
- info.bugreportFile = bugreportFile;
- if (screenshotFile != null) {
- info.addScreenshot(screenshotFile);
- }
-
- if (max != -1) {
- MetricsLogger.histogram(this, "dumpstate_duration", max);
- info.max = max;
- }
-
- if (!TextUtils.isEmpty(shareTitle)) {
- info.title = shareTitle;
- if (!TextUtils.isEmpty(shareDescription)) {
- info.shareDescription= shareDescription;
- }
- Log.d(TAG, "Bugreport title is " + info.title + ","
- + " shareDescription is " + info.shareDescription);
- }
+ private void onBugreportFinished(BugreportInfo info) {
+ Log.d(TAG, "Bugreport finished with title: " + info.title
+ + " and shareDescription: " + info.shareDescription);
info.finished = true;
// Stop running on foreground, otherwise share notification cannot be dismissed.
- stopForegroundWhenDone(id);
+ stopForegroundWhenDone(info.id);
triggerLocalNotification(mContext, info);
}
@@ -1043,7 +1009,9 @@ public class BugreportProgressService extends Service {
if (!info.bugreportFile.exists() || !info.bugreportFile.canRead()) {
Log.e(TAG, "Could not read bugreport file " + info.bugreportFile);
Toast.makeText(context, R.string.bugreport_unreadable_text, Toast.LENGTH_LONG).show();
- stopProgress(info.id);
+ synchronized (mLock) {
+ stopProgressLocked(info.id);
+ }
return;
}
@@ -1066,7 +1034,11 @@ public class BugreportProgressService extends Service {
/**
* Build {@link Intent} that can be used to share the given bugreport.
*/
- private static Intent buildSendIntent(Context context, BugreportInfo info) {
+ private static Intent buildSendIntent(Context context, BugreportInfo info,
+ File screenshotsDir) {
+ // Rename files (if required) before sharing
+ info.renameBugreportFile();
+ info.renameScreenshots(screenshotsDir);
// Files are kept on private storage, so turn into Uris that we can
// grant temporary permissions for.
final Uri bugreportUri;
@@ -1152,10 +1124,12 @@ public class BugreportProgressService extends Service {
addDetailsToZipFile(info);
- final Intent sendIntent = buildSendIntent(mContext, info);
+ final Intent sendIntent = buildSendIntent(mContext, info, mScreenshotsDir);
if (sendIntent == null) {
Log.w(TAG, "Stopping progres on ID " + id + " because share intent could not be built");
- stopProgress(id);
+ synchronized (mLock) {
+ stopProgressLocked(id);
+ }
return;
}
@@ -1178,9 +1152,10 @@ public class BugreportProgressService extends Service {
} else {
mContext.startActivity(notifIntent);
}
-
- // ... and stop watching this process.
- stopProgress(id);
+ synchronized (mLock) {
+ // ... and stop watching this process.
+ stopProgressLocked(id);
+ }
}
static void sendShareIntent(Context context, Intent intent) {
@@ -1477,12 +1452,11 @@ public class BugreportProgressService extends Service {
}
String action = intent.getAction();
if (action == null) {
- // Happens when BugreportReceiver calls startService...
+ // Happens when startService is called...
action = "no action";
}
final StringBuilder buffer = new StringBuilder(action).append(" extras: ");
addExtra(buffer, intent, EXTRA_ID);
- addExtra(buffer, intent, EXTRA_PID);
addExtra(buffer, intent, EXTRA_MAX);
addExtra(buffer, intent, EXTRA_NAME);
addExtra(buffer, intent, EXTRA_DESCRIPTION);
@@ -1527,15 +1501,6 @@ public class BugreportProgressService extends Service {
}
/**
- * Updates the system property used by {@code dumpstate} to rename the final bugreport files.
- */
- private boolean setBugreportNameProperty(int pid, String name) {
- Log.d(TAG, "Updating bugreport name to " + name);
- final String key = DUMPSTATE_PREFIX + pid + NAME_SUFFIX;
- return setSystemProperty(key, name);
- }
-
- /**
* Updates the user-provided details of a bugreport.
*/
private void updateBugreportInfo(int id, String name, String title, String description) {
@@ -1619,30 +1584,6 @@ public class BugreportProgressService extends Service {
private AlertDialog mDialog;
private Button mOkButton;
private int mId;
- private int mPid;
-
- /**
- * Last "committed" value of the bugreport name.
- * <p>
- * Once initially set, it's only updated when user clicks the OK button.
- */
- private String mSavedName;
-
- /**
- * Last value of the bugreport name as entered by the user.
- * <p>
- * Every time it's changed the equivalent system property is changed as well, but if the
- * user clicks CANCEL, the old value (stored on {@code mSavedName} is restored.
- * <p>
- * This logic handles the corner-case scenario where {@code dumpstate} finishes after the
- * user changed the name but didn't clicked OK yet (for example, because the user is typing
- * the description). The only drawback is that if the user changes the name while
- * {@code dumpstate} is running but clicks CANCEL after it finishes, then the final name
- * will be the one that has been canceled. But when {@code dumpstate} finishes the {code
- * name} UI is disabled and the old name restored anyways, so the user will be "alerted" of
- * such drawback.
- */
- private String mTempName;
/**
* Sets its internal state and displays the dialog.
@@ -1662,18 +1603,6 @@ public class BugreportProgressService extends Service {
mInfoName = (EditText) view.findViewById(R.id.name);
mInfoTitle = (EditText) view.findViewById(R.id.title);
mInfoDescription = (EditText) view.findViewById(R.id.description);
-
- mInfoName.setOnFocusChangeListener(new OnFocusChangeListener() {
-
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- if (hasFocus) {
- return;
- }
- sanitizeName();
- }
- });
-
mDialog = new AlertDialog.Builder(themedContext)
.setView(view)
.setTitle(dialogTitle)
@@ -1688,11 +1617,6 @@ public class BugreportProgressService extends Service {
{
MetricsLogger.action(context,
MetricsEvent.ACTION_BUGREPORT_DETAILS_CANCELED);
- if (!mTempName.equals(mSavedName)) {
- // Must restore dumpstate's name since it was changed
- // before user clicked OK.
- setBugreportNameProperty(mPid, mSavedName);
- }
}
})
.create();
@@ -1711,9 +1635,7 @@ public class BugreportProgressService extends Service {
}
// Then set fields.
- mSavedName = mTempName = info.name;
mId = info.id;
- mPid = info.pid;
if (!TextUtils.isEmpty(info.name)) {
mInfoName.setText(info.name);
}
@@ -1740,7 +1662,7 @@ public class BugreportProgressService extends Service {
@Override
public void onClick(View view) {
MetricsLogger.action(context, MetricsEvent.ACTION_BUGREPORT_DETAILS_SAVED);
- sanitizeName();
+ sanitizeName(info.name);
final String name = mInfoName.getText().toString();
final String title = mInfoTitle.getText().toString();
final String description = mInfoDescription.getText().toString();
@@ -1756,9 +1678,9 @@ public class BugreportProgressService extends Service {
* Sanitizes the user-provided value for the {@code name} field, automatically replacing
* invalid characters if necessary.
*/
- private void sanitizeName() {
+ private void sanitizeName(String savedName) {
String name = mInfoName.getText().toString();
- if (name.equals(mTempName)) {
+ if (name.equals(savedName)) {
if (DEBUG) Log.v(TAG, "name didn't change, no need to sanitize: " + name);
return;
}
@@ -1778,36 +1700,6 @@ public class BugreportProgressService extends Service {
name = safeName.toString();
mInfoName.setText(name);
}
- if (mUsingBugreportApi) {
- File prevFile = new File(BUGREPORT_DIR, mTempName + ".zip");
- File newFile = new File(BUGREPORT_DIR, name + ".zip");
- if (!prevFile.renameTo(newFile)) {
- Log.e(TAG, "File rename from : " + mTempName
- + " to : " + name + " from the UI failed.");
- } else {
- Log.d(TAG, "File rename from : " + mTempName
- + " to : " + name + " from the UI succeeded.");
- }
- }
- mTempName = name;
-
- // Must update system property for the cases where dumpstate finishes
- // while the user is still entering other fields (like title or
- // description)
- setBugreportNameProperty(mPid, name);
- }
-
- /**
- * Notifies the dialog that the bugreport has finished so it disables the {@code name}
- * field.
- * <p>Once the bugreport is finished dumpstate has already generated the final files, so
- * changing the name would have no effect.
- */
- void onBugreportFinished() {
- if (mInfoName != null) {
- mInfoName.setEnabled(false);
- mInfoName.setText(mSavedName);
- }
}
void cancel() {
@@ -1829,19 +1721,26 @@ public class BugreportProgressService extends Service {
int id;
/**
- * {@code pid} of the {@code dumpstate} process generating the bugreport.
+ * Prefix name of the bugreport, this is uneditable.
+ * The baseName consists of the string "bugreport" + deviceName + buildID
+ * This will end with the string "wifi"/"telephony" for wifi/telephony bugreports.
+ * Bugreport zip file name = "<baseName>-<name>.zip"
*/
- final int pid;
+ String baseName;
/**
- * Name of the bugreport, will be used to rename the final files.
- * <p>
- * Initial value is the bugreport filename reported by {@code dumpstate}, but user can
- * change it later to a more meaningful name.
+ * Suffix name of the bugreport/screenshot, is set to timestamp initially. User can make
+ * modifications to this using interface.
*/
String name;
/**
+ * Initial value of the field name. This is required to rename the files later on, as they
+ * are created using initial value of name.
+ */
+ String initialName;
+
+ /**
* User-provided, one-line summary of the bug; when set, will be used as the subject
* of the {@link Intent#ACTION_SEND_MULTIPLE} intent.
*/
@@ -1925,41 +1824,49 @@ public class BugreportProgressService extends Service {
String shareDescription;
/**
- * Constructor for tracked bugreports - typically called upon receiving BUGREPORT_STARTED.
+ * Type of the bugreport
*/
- BugreportInfo(Context context, int id, int pid, String name, int max) {
- this(context, pid, name, max, null, null);
- this.id = id;
- }
+ int type;
/**
* Constructor for tracked bugreports - typically called upon receiving BUGREPORT_REQUESTED.
*/
- BugreportInfo(Context context, int pid, String name, int max, @Nullable String shareTitle,
- @Nullable String shareDescription) {
+ BugreportInfo(Context context, String baseName, String name, int max,
+ @Nullable String shareTitle, @Nullable String shareDescription,
+ @BugreportParams.BugreportMode int type) {
this.context = context;
- this.pid = pid;
- this.name = name;
+ this.name = this.initialName = name;
this.max = this.realMax = max;
this.shareTitle = shareTitle == null ? "" : shareTitle;
this.shareDescription = shareDescription == null ? "" : shareDescription;
+ this.type = type;
+ this.baseName = baseName;
}
- /**
- * Constructor for untracked bugreports - typically called upon receiving BUGREPORT_FINISHED
- * without a previous call to BUGREPORT_STARTED.
- */
- BugreportInfo(Context context, int id) {
- this(context, id, id, null, 0);
- this.finished = true;
+ ParcelFileDescriptor createBugreportFd() {
+ bugreportFile = new File(BUGREPORT_DIR, getFileName(this, ".zip"));
+ return createReadWriteFile(bugreportFile);
+ }
+
+ ParcelFileDescriptor createScreenshotFd() {
+ File screenshotFile = new File(BUGREPORT_DIR, getScreenshotName("default"));
+ addScreenshot(screenshotFile);
+ return createReadWriteFile(screenshotFile);
}
/**
- * Gets the name for next screenshot file.
+ * Gets the name for next user triggered screenshot file.
*/
String getPathNextScreenshot() {
screenshotCounter ++;
- return "screenshot-" + pid + "-" + screenshotCounter + ".png";
+ return getScreenshotName(Integer.toString(screenshotCounter));
+ }
+
+ /**
+ * Gets the name for screenshot file based on the suffix that is passed.
+ */
+ String getScreenshotName(String suffix) {
+ return "screenshot-" + initialName + "-" + suffix + ".png";
}
/**
@@ -1970,7 +1877,8 @@ public class BugreportProgressService extends Service {
}
/**
- * Rename all screenshots files so that they contain the user-generated name instead of pid.
+ * Rename all screenshots files so that they contain the new {@code name} instead of the
+ * {@code initialName} if user has changed it.
*/
void renameScreenshots(File screenshotDir) {
if (TextUtils.isEmpty(name)) {
@@ -1979,21 +1887,37 @@ public class BugreportProgressService extends Service {
final List<File> renamedFiles = new ArrayList<>(screenshotFiles.size());
for (File oldFile : screenshotFiles) {
final String oldName = oldFile.getName();
- final String newName = oldName.replaceFirst(Integer.toString(pid), name);
+ final String newName = oldName.replaceFirst(initialName, name);
final File newFile;
if (!newName.equals(oldName)) {
final File renamedFile = new File(screenshotDir, newName);
Log.d(TAG, "Renaming screenshot file " + oldFile + " to " + renamedFile);
newFile = oldFile.renameTo(renamedFile) ? renamedFile : oldFile;
} else {
- Log.w(TAG, "Name didn't change: " + oldName); // Shouldn't happen.
+ Log.w(TAG, "Name didn't change: " + oldName);
newFile = oldFile;
}
- renamedFiles.add(newFile);
+ if (newFile.length() > 0) {
+ renamedFiles.add(newFile);
+ } else if (newFile.delete()) {
+ Log.d(TAG, "screenshot file: " + newFile + "deleted successfully.");
+ }
}
screenshotFiles = renamedFiles;
}
+ /**
+ * Rename bugreport file to include the name given by user via UI
+ */
+ void renameBugreportFile() {
+ File newBugreportFile = new File(BUGREPORT_DIR, getFileName(this, ".zip"));
+ if (!newBugreportFile.getPath().equals(bugreportFile.getPath())) {
+ if (bugreportFile.renameTo(newBugreportFile)) {
+ bugreportFile = newBugreportFile;
+ }
+ }
+ }
+
String getFormattedLastUpdate() {
if (context == null) {
// Restored from Parcel
@@ -2011,8 +1935,9 @@ public class BugreportProgressService extends Service {
final StringBuilder builder = new StringBuilder()
.append("\tid: ").append(id)
- .append(", pid: ").append(pid)
+ .append(", baseName: ").append(baseName)
.append(", name: ").append(name)
+ .append(", initialName: ").append(initialName)
.append(", finished: ").append(finished)
.append("\n\ttitle: ").append(title)
.append("\n\tdescription: ");
@@ -2044,8 +1969,9 @@ public class BugreportProgressService extends Service {
protected BugreportInfo(Parcel in) {
context = null;
id = in.readInt();
- pid = in.readInt();
+ baseName = in.readString();
name = in.readString();
+ initialName = in.readString();
title = in.readString();
description = in.readString();
max = in.readInt();
@@ -2070,8 +1996,9 @@ public class BugreportProgressService extends Service {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
- dest.writeInt(pid);
+ dest.writeString(baseName);
dest.writeString(name);
+ dest.writeString(initialName);
dest.writeString(title);
dest.writeString(description);
dest.writeInt(max);
@@ -2123,77 +2050,8 @@ public class BugreportProgressService extends Service {
}
- private final class DumpstateListener extends IDumpstateListener.Stub
- implements DeathRecipient {
-
- private final BugreportInfo info;
- private IDumpstateToken token;
-
- DumpstateListener(BugreportInfo info) {
- this.info = info;
- }
-
- /**
- * Connects to the {@code dumpstate} binder to receive updates.
- */
- boolean connect() {
- if (token != null) {
- Log.d(TAG, "connect(): " + info.id + " already connected");
- return true;
- }
- final IBinder service = ServiceManager.getService("dumpstate");
- if (service == null) {
- Log.d(TAG, "dumpstate service not bound yet");
- return true;
- }
- final IDumpstate dumpstate = IDumpstate.Stub.asInterface(service);
- try {
- token = dumpstate.setListener("Shell", this, /* perSectionDetails= */ false);
- if (token != null) {
- token.asBinder().linkToDeath(this, 0);
- }
- } catch (Exception e) {
- Log.e(TAG, "Could not set dumpstate listener: " + e);
- }
- return token != null;
- }
-
- @Override
- public void binderDied() {
- if (!info.finished) {
- // TODO: linkToDeath() might be called BEFORE Shell received the
- // BUGREPORT_FINISHED broadcast, in which case the statements below
- // spam logcat (but are harmless).
- // The right, long-term solution is to provide an onFinished() callback
- // on IDumpstateListener and call it instead of using a broadcast.
- Log.w(TAG, "Dumpstate process died:\n" + info);
- stopProgress(info.id);
- }
- token.asBinder().unlinkToDeath(this, 0);
- }
-
- @Override
- public void onProgress(int progress) throws RemoteException {
- checkProgressUpdated(info, progress);
- }
-
- @Override
- public void onError(int errorCode) throws RemoteException {
- // TODO(b/111441001): implement
- }
-
- @Override
- public void onFinished() throws RemoteException {
- // TODO(b/111441001): implement
- }
-
- public void dump(String prefix, PrintWriter pw) {
- pw.print(prefix); pw.print("token: "); pw.println(token);
- }
-
- }
-
- private void checkProgressUpdated(BugreportInfo info, int progress) {
+ @GuardedBy("mLock")
+ private void checkProgressUpdatedLocked(BugreportInfo info, int progress) {
if (progress > CAPPED_PROGRESS) {
progress = CAPPED_PROGRESS;
}
@@ -2203,11 +2061,11 @@ public class BugreportProgressService extends Service {
private void updateProgressInfo(BugreportInfo info, int progress, int max) {
if (DEBUG) {
if (progress != info.progress) {
- Log.v(TAG, "Updating progress for PID " + info.pid + "(id: " + info.id
+ Log.v(TAG, "Updating progress for name " + info.name + "(id: " + info.id
+ ") from " + info.progress + " to " + progress);
}
if (max != info.max) {
- Log.v(TAG, "Updating max progress for PID " + info.pid + "(id: " + info.id
+ Log.v(TAG, "Updating max progress for name " + info.name + "(id: " + info.id
+ ") from " + info.max + " to " + max);
}
}
diff --git a/packages/Shell/src/com/android/shell/BugreportReceiver.java b/packages/Shell/src/com/android/shell/BugreportReceiver.java
deleted file mode 100644
index 15ce90fa6418..000000000000
--- a/packages/Shell/src/com/android/shell/BugreportReceiver.java
+++ /dev/null
@@ -1,88 +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.shell;
-
-import static com.android.shell.BugreportProgressService.EXTRA_BUGREPORT;
-import static com.android.shell.BugreportProgressService.EXTRA_ORIGINAL_INTENT;
-import static com.android.shell.BugreportProgressService.INTENT_BUGREPORT_FINISHED;
-import static com.android.shell.BugreportProgressService.getFileExtra;
-import static com.android.shell.BugreportProgressService.dumpIntent;
-
-import java.io.File;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.os.AsyncTask;
-import android.os.FileUtils;
-import android.text.format.DateUtils;
-import android.util.Log;
-
-/**
- * Receiver that handles finished bugreports, usually by attaching them to an
- * {@link Intent#ACTION_SEND_MULTIPLE}.
- */
-public class BugreportReceiver extends BroadcastReceiver {
- private static final String TAG = "BugreportReceiver";
-
- /**
- * Always keep the newest 8 bugreport files.
- */
- private static final int MIN_KEEP_COUNT = 8;
-
- /**
- * Always keep bugreports taken in the last week.
- */
- private static final long MIN_KEEP_AGE = DateUtils.WEEK_IN_MILLIS;
-
- @Override
- public void onReceive(Context context, Intent intent) {
- Log.d(TAG, "onReceive(): " + dumpIntent(intent));
- // Clean up older bugreports in background
- cleanupOldFiles(this, intent, INTENT_BUGREPORT_FINISHED, MIN_KEEP_COUNT, MIN_KEEP_AGE);
-
- // Delegate intent handling to service.
- Intent serviceIntent = new Intent(context, BugreportProgressService.class);
- serviceIntent.putExtra(EXTRA_ORIGINAL_INTENT, intent);
- context.startService(serviceIntent);
- }
-
- static void cleanupOldFiles(BroadcastReceiver br, Intent intent, String expectedAction,
- final int minCount, final long minAge) {
- if (!expectedAction.equals(intent.getAction())) {
- return;
- }
- final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
- if (bugreportFile == null || !bugreportFile.exists()) {
- Log.e(TAG, "Not deleting old files because file " + bugreportFile + " doesn't exist");
- return;
- }
- final PendingResult result = br.goAsync();
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- try {
- FileUtils.deleteOlderFiles(bugreportFile.getParentFile(), minCount, minAge);
- } catch (RuntimeException e) {
- Log.e(TAG, "RuntimeException deleting old files", e);
- }
- result.finish();
- return null;
- }
- }.execute();
- }
-}
diff --git a/packages/Shell/src/com/android/shell/RemoteBugreportReceiver.java b/packages/Shell/src/com/android/shell/RemoteBugreportReceiver.java
deleted file mode 100644
index 634c3b47c787..000000000000
--- a/packages/Shell/src/com/android/shell/RemoteBugreportReceiver.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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 com.android.shell;
-
-import static com.android.shell.BugreportProgressService.EXTRA_BUGREPORT;
-import static com.android.shell.BugreportProgressService.INTENT_REMOTE_BUGREPORT_FINISHED;
-import static com.android.shell.BugreportProgressService.getFileExtra;
-import static com.android.shell.BugreportProgressService.getUri;
-import static com.android.shell.BugreportReceiver.cleanupOldFiles;
-
-import java.io.File;
-
-import android.app.admin.DevicePolicyManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.UserHandle;
-import android.text.format.DateUtils;
-
-/**
- * Receiver that handles finished remote bugreports, by re-sending
- * the intent with appended bugreport zip file URI.
- *
- * <p> Remote bugreport never contains a screenshot.
- */
-public class RemoteBugreportReceiver extends BroadcastReceiver {
-
- private static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport";
-
- /** Always keep just the last remote bugreport's files around. */
- private static final int REMOTE_BUGREPORT_FILES_AMOUNT = 3;
-
- /** Always keep remote bugreport files created in the last day. */
- private static final long MIN_KEEP_AGE = DateUtils.DAY_IN_MILLIS;
-
- @Override
- public void onReceive(Context context, Intent intent) {
- cleanupOldFiles(this, intent, INTENT_REMOTE_BUGREPORT_FINISHED,
- REMOTE_BUGREPORT_FILES_AMOUNT, MIN_KEEP_AGE);
-
- final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
- final Uri bugreportUri = getUri(context, bugreportFile);
- final String bugreportHash = intent.getStringExtra(
- DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH);
-
- final Intent newIntent = new Intent(DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH);
- newIntent.setDataAndType(bugreportUri, BUGREPORT_MIMETYPE);
- newIntent.putExtra(DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH, bugreportHash);
- context.sendBroadcastAsUser(newIntent, UserHandle.SYSTEM,
- android.Manifest.permission.DUMP);
- }
-}
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index 3a71632cf1ca..bb298e937fbb 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -29,10 +29,8 @@ import static com.android.shell.BugreportProgressService.EXTRA_BUGREPORT;
import static com.android.shell.BugreportProgressService.EXTRA_ID;
import static com.android.shell.BugreportProgressService.EXTRA_MAX;
import static com.android.shell.BugreportProgressService.EXTRA_NAME;
-import static com.android.shell.BugreportProgressService.EXTRA_PID;
import static com.android.shell.BugreportProgressService.EXTRA_SCREENSHOT;
import static com.android.shell.BugreportProgressService.INTENT_BUGREPORT_FINISHED;
-import static com.android.shell.BugreportProgressService.INTENT_BUGREPORT_STARTED;
import static com.android.shell.BugreportProgressService.SCREENSHOT_DELAY_SECONDS;
import static org.junit.Assert.assertEquals;
@@ -145,6 +143,10 @@ public class BugreportReceiverTest {
private static final String TITLE2 = "Master of the Universe";
private static final String DESCRIPTION = "One's description...";
private static final String DESCRIPTION2 = "...is another's treasure.";
+ // TODO(b/143130523): Fix (update) tests and add to presubmit
+ private static final String EXTRA_PID = "android.intent.extra.PID";
+ private static final String INTENT_BUGREPORT_STARTED =
+ "com.android.internal.intent.action.BUGREPORT_STARTED";
private static final String NO_DESCRIPTION = null;
private static final String NO_NAME = null;
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 2aea6c42cfce..c647e54512d1 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -180,41 +180,3 @@ android_app {
required: ["privapp_whitelist_com.android.systemui"],
}
-
-// Only used for products that are shipping legacy Recents
-android_app {
- name: "SystemUIWithLegacyRecents",
- overrides: [
- "SystemUI",
- ],
-
- platform_apis: true,
- certificate: "platform",
- privileged: true,
-
- dxflags: ["--multi-dex"],
- optimize: {
- proguard_flags_files: ["proguard.flags", "legacy/recents/proguard.flags"],
- },
-
- static_libs: [
- "SystemUI-core",
- ],
- libs: [
- "telephony-common",
- "telephony-ext",
- "ims-common",
- ],
-
- kotlincflags: ["-Xjvm-default=enable"],
-
- srcs: [
- "legacy/recents/src/**/*.java",
- "legacy/recents/src/**/I*.aidl",
- ],
- resource_dirs: [
- "legacy/recents/res",
- ],
-
- manifest: "legacy/recents/AndroidManifest.xml",
-}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 403e894a68e4..b288eb7b5070 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -243,6 +243,9 @@
<!-- Permission to change the display color -->
<uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
+ <!-- Query all packages on device on R+ -->
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+
<protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 83acfa06976f..656827a80117 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -46,3 +46,6 @@ winsonc@google.com
#Android Auto
stenning@google.com
+#Android TV
+rgl@google.com
+
diff --git a/packages/SystemUI/README.md b/packages/SystemUI/README.md
index a8ce196cc359..a3d420ee4cdf 100644
--- a/packages/SystemUI/README.md
+++ b/packages/SystemUI/README.md
@@ -104,12 +104,6 @@ it should be shown.
Shows the drag handle for the divider between two apps when in split screen
mode.
-### [com.android.systemui.SystemBars](/packages/SystemUI/src/com/android/systemui/SystemBars.java)
-
-This is a proxy to the actual SystemUI for the status bar. This loads from
-config_statusBarComponent which defaults to StatusBar. (maybe this should be
-removed and copy how config_systemUiVendorServiceComponent works)
-
### [com.android.systemui.status.phone.StatusBar](/packages/SystemUI/src/com/android/systemui/status/phone/StatusBar.java)
This shows the UI for the status bar and the notification shade it contains.
diff --git a/packages/SystemUI/legacy/recents/AndroidManifest.xml b/packages/SystemUI/legacy/recents/AndroidManifest.xml
deleted file mode 100644
index 0d8b3cd26c1a..000000000000
--- a/packages/SystemUI/legacy/recents/AndroidManifest.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright (c) 2018 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- package="com.android.systemui"
- android:sharedUserId="android.uid.systemui"
- coreApp="true">
-
- <application
- android:name="com.android.systemui.SystemUIApplication">
-
- <!-- Service used by secondary users to register themselves with the system user. -->
- <service android:name=".recents.RecentsSystemUserService"
- android:exported="false"
- android:permission="com.android.systemui.permission.SELF" />
-
- <!-- Alternate Recents -->
- <activity android:name=".recents.RecentsActivity"
- android:label="@string/accessibility_desc_recent_apps"
- android:exported="false"
- android:launchMode="singleInstance"
- android:excludeFromRecents="true"
- android:stateNotNeeded="true"
- android:resumeWhilePausing="true"
- android:resizeableActivity="true"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden"
- android:theme="@style/RecentsTheme.Wallpaper">
- <intent-filter>
- <action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
- </intent-filter>
- </activity>
-
- </application>
-</manifest>
diff --git a/packages/SystemUI/legacy/recents/proguard.flags b/packages/SystemUI/legacy/recents/proguard.flags
deleted file mode 100644
index c3589491865d..000000000000
--- a/packages/SystemUI/legacy/recents/proguard.flags
+++ /dev/null
@@ -1,14 +0,0 @@
--keepclassmembers class ** {
- public void onBusEvent(**);
- public void onInterprocessBusEvent(**);
-}
--keepclassmembers class ** extends **.EventBus$InterprocessEvent {
- public <init>(android.os.Bundle);
-}
-
--keep class com.android.systemui.recents.views.TaskView {
- public int getDim();
- public void setDim(int);
- public float getTaskProgress();
- public void setTaskProgress(float);
-} \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/anim/recents_fast_toggle_app_home_exit.xml b/packages/SystemUI/legacy/recents/res/anim/recents_fast_toggle_app_home_exit.xml
deleted file mode 100644
index 69edcc757ba9..000000000000
--- a/packages/SystemUI/legacy/recents/res/anim/recents_fast_toggle_app_home_exit.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<!-- Recents Activity -->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:zAdjustment="top">
- <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:duration="250"/>
-</set>
diff --git a/packages/SystemUI/legacy/recents/res/anim/recents_from_launcher_enter.xml b/packages/SystemUI/legacy/recents/res/anim/recents_from_launcher_enter.xml
deleted file mode 100644
index 00b3dfda135e..000000000000
--- a/packages/SystemUI/legacy/recents/res/anim/recents_from_launcher_enter.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:zAdjustment="normal">
- <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/linear_out_slow_in"
- android:duration="150"/>
-</set>
diff --git a/packages/SystemUI/legacy/recents/res/anim/recents_from_launcher_exit.xml b/packages/SystemUI/legacy/recents/res/anim/recents_from_launcher_exit.xml
deleted file mode 100644
index 33831b8c0a32..000000000000
--- a/packages/SystemUI/legacy/recents/res/anim/recents_from_launcher_exit.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:zAdjustment="top">
- <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/recents_from_launcher_exit_interpolator"
- android:duration="133"/>
-</set>
diff --git a/packages/SystemUI/legacy/recents/res/anim/recents_from_unknown_enter.xml b/packages/SystemUI/legacy/recents/res/anim/recents_from_unknown_enter.xml
deleted file mode 100644
index da1dee007546..000000000000
--- a/packages/SystemUI/legacy/recents/res/anim/recents_from_unknown_enter.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:zAdjustment="top">
- <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/linear"
- android:duration="200"/>
-</set>
diff --git a/packages/SystemUI/legacy/recents/res/anim/recents_from_unknown_exit.xml b/packages/SystemUI/legacy/recents/res/anim/recents_from_unknown_exit.xml
deleted file mode 100644
index 31cf26a9fdfd..000000000000
--- a/packages/SystemUI/legacy/recents/res/anim/recents_from_unknown_exit.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:zAdjustment="normal">
- <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="200"/>
-</set>
diff --git a/packages/SystemUI/legacy/recents/res/anim/recents_launch_next_affiliated_task_bounce.xml b/packages/SystemUI/legacy/recents/res/anim/recents_launch_next_affiliated_task_bounce.xml
deleted file mode 100644
index 74f2814b2ce8..000000000000
--- a/packages/SystemUI/legacy/recents/res/anim/recents_launch_next_affiliated_task_bounce.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal">
-
-
- <translate android:fromYDelta="0" android:toYDelta="2%"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="133"/>
-
- <scale android:fromXScale="1.0" android:toXScale="0.98"
- android:fromYScale="1.0" android:toYScale="0.98"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:pivotX="50%p" android:pivotY="50%p"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="133" />
-
- <translate android:fromYDelta="0" android:toYDelta="-2%"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/recents_launch_prev_affiliated_task_bounce_ydelta"
- android:startOffset="133"
- android:duration="217"/>
-
- <scale android:fromXScale="1.0" android:toXScale="1.02040816326531"
- android:fromYScale="1.0" android:toYScale="1.02040816326531"
- android:fillEnabled="true" android:fillBefore="false" android:fillAfter="true"
- android:pivotX="50%p" android:pivotY="50%p"
- android:interpolator="@interpolator/recents_launch_next_affiliated_task_bounce_scale"
- android:startOffset="133"
- android:duration="217" />
-</set> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/anim/recents_launch_next_affiliated_task_source.xml b/packages/SystemUI/legacy/recents/res/anim/recents_launch_next_affiliated_task_source.xml
deleted file mode 100644
index f0fd68458801..000000000000
--- a/packages/SystemUI/legacy/recents/res/anim/recents_launch_next_affiliated_task_source.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal">
-
- <alpha android:fromAlpha="1.0" android:toAlpha="0.6"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/accelerate_cubic"
- android:duration="150"/>
-
- <scale android:fromXScale="1.0" android:toXScale="0.9"
- android:fromYScale="1.0" android:toYScale="0.9"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:pivotX="50%p" android:pivotY="50%p"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="300" />
-</set> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/anim/recents_launch_next_affiliated_task_target.xml b/packages/SystemUI/legacy/recents/res/anim/recents_launch_next_affiliated_task_target.xml
deleted file mode 100644
index 170ac829c3c2..000000000000
--- a/packages/SystemUI/legacy/recents/res/anim/recents_launch_next_affiliated_task_target.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top">
-
- <translate android:fromYDelta="110%" android:toYDelta="0%"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/decelerate_quint"
- android:startOffset="50"
- android:duration="250" />
-</set> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/anim/recents_launch_prev_affiliated_task_bounce.xml b/packages/SystemUI/legacy/recents/res/anim/recents_launch_prev_affiliated_task_bounce.xml
deleted file mode 100644
index b19167da3dde..000000000000
--- a/packages/SystemUI/legacy/recents/res/anim/recents_launch_prev_affiliated_task_bounce.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top">
-
- <translate android:fromYDelta="0%" android:toYDelta="10%"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="133" />
-
- <translate android:fromYDelta="0%" android:toYDelta="-10%"
- android:fillEnabled="true" android:fillBefore="false" android:fillAfter="true"
- android:interpolator="@interpolator/recents_launch_prev_affiliated_task_bounce_ydelta"
- android:startOffset="133"
- android:duration="217" />
-</set> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/anim/recents_launch_prev_affiliated_task_source.xml b/packages/SystemUI/legacy/recents/res/anim/recents_launch_prev_affiliated_task_source.xml
deleted file mode 100644
index ad5341bf8ff3..000000000000
--- a/packages/SystemUI/legacy/recents/res/anim/recents_launch_prev_affiliated_task_source.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top">
-
- <translate android:fromYDelta="0%" android:toYDelta="110%"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/accelerate_quint"
- android:duration="300" />
-</set> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/anim/recents_launch_prev_affiliated_task_target.xml b/packages/SystemUI/legacy/recents/res/anim/recents_launch_prev_affiliated_task_target.xml
deleted file mode 100644
index 7687f0286f25..000000000000
--- a/packages/SystemUI/legacy/recents/res/anim/recents_launch_prev_affiliated_task_target.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal">
-
- <alpha android:fromAlpha="0.6" android:toAlpha="1.0"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/decelerate_cubic"
- android:startOffset="75"
- android:duration="150"/>
-
- <scale android:fromXScale="0.9" android:toXScale="1.0"
- android:fromYScale="0.9" android:toYScale="1.0"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/linear_out_slow_in"
- android:pivotX="50%p" android:pivotY="50%p"
- android:startOffset="75"
- android:duration="225" />
-</set> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/anim/recents_to_launcher_enter.xml b/packages/SystemUI/legacy/recents/res/anim/recents_to_launcher_enter.xml
deleted file mode 100644
index 544ec88d2bfa..000000000000
--- a/packages/SystemUI/legacy/recents/res/anim/recents_to_launcher_enter.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:zAdjustment="normal">
- <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/recents_to_launcher_enter_interpolator"
- android:duration="133"/>
-</set>
diff --git a/packages/SystemUI/legacy/recents/res/anim/recents_to_launcher_exit.xml b/packages/SystemUI/legacy/recents/res/anim/recents_to_launcher_exit.xml
deleted file mode 100644
index 226edb85d049..000000000000
--- a/packages/SystemUI/legacy/recents/res/anim/recents_to_launcher_exit.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:zAdjustment="top">
- <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/linear_out_slow_in"
- android:duration="1"/>
-</set>
diff --git a/packages/SystemUI/legacy/recents/res/drawable-hdpi/recents_lower_gradient.9.png b/packages/SystemUI/legacy/recents/res/drawable-hdpi/recents_lower_gradient.9.png
deleted file mode 100644
index 17100f773a16..000000000000
--- a/packages/SystemUI/legacy/recents/res/drawable-hdpi/recents_lower_gradient.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/legacy/recents/res/drawable-hdpi/recents_status_gradient.9.png b/packages/SystemUI/legacy/recents/res/drawable-hdpi/recents_status_gradient.9.png
deleted file mode 100644
index e969d4c26717..000000000000
--- a/packages/SystemUI/legacy/recents/res/drawable-hdpi/recents_status_gradient.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/legacy/recents/res/drawable-mdpi/recents_lower_gradient.9.png b/packages/SystemUI/legacy/recents/res/drawable-mdpi/recents_lower_gradient.9.png
deleted file mode 100644
index b53bd8f92a00..000000000000
--- a/packages/SystemUI/legacy/recents/res/drawable-mdpi/recents_lower_gradient.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/legacy/recents/res/drawable-mdpi/recents_status_gradient.9.png b/packages/SystemUI/legacy/recents/res/drawable-mdpi/recents_status_gradient.9.png
deleted file mode 100644
index 657f710ac8d6..000000000000
--- a/packages/SystemUI/legacy/recents/res/drawable-mdpi/recents_status_gradient.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/legacy/recents/res/drawable-xhdpi/recents_lower_gradient.9.png b/packages/SystemUI/legacy/recents/res/drawable-xhdpi/recents_lower_gradient.9.png
deleted file mode 100644
index 09606f629b67..000000000000
--- a/packages/SystemUI/legacy/recents/res/drawable-xhdpi/recents_lower_gradient.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/legacy/recents/res/drawable-xhdpi/recents_status_gradient.9.png b/packages/SystemUI/legacy/recents/res/drawable-xhdpi/recents_status_gradient.9.png
deleted file mode 100644
index a444c551d430..000000000000
--- a/packages/SystemUI/legacy/recents/res/drawable-xhdpi/recents_status_gradient.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/legacy/recents/res/drawable-xxhdpi/recents_lower_gradient.9.png b/packages/SystemUI/legacy/recents/res/drawable-xxhdpi/recents_lower_gradient.9.png
deleted file mode 100644
index 427cad9f6326..000000000000
--- a/packages/SystemUI/legacy/recents/res/drawable-xxhdpi/recents_lower_gradient.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/legacy/recents/res/drawable-xxhdpi/recents_status_gradient.9.png b/packages/SystemUI/legacy/recents/res/drawable-xxhdpi/recents_status_gradient.9.png
deleted file mode 100644
index 29cf44bf381f..000000000000
--- a/packages/SystemUI/legacy/recents/res/drawable-xxhdpi/recents_status_gradient.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/legacy/recents/res/drawable-xxhdpi/recents_task_shadow.9.png b/packages/SystemUI/legacy/recents/res/drawable-xxhdpi/recents_task_shadow.9.png
deleted file mode 100644
index 36e7e45494ef..000000000000
--- a/packages/SystemUI/legacy/recents/res/drawable-xxhdpi/recents_task_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/legacy/recents/res/drawable/recents_dismiss_dark.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_dismiss_dark.xml
deleted file mode 100644
index b837ebef78eb..000000000000
--- a/packages/SystemUI/legacy/recents/res/drawable/recents_dismiss_dark.xml
+++ /dev/null
@@ -1,29 +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.
--->
-<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="@color/recents_task_bar_dark_icon_color"
- android:pathData="M18.3 5.71a.996 .996 0 0 0-1.41 0L12 10.59 7.11 5.7A.996 .996 0 1 0 5.7
-7.11L10.59 12 5.7 16.89a.996 .996 0 1 0 1.41 1.41L12 13.41l4.89 4.89a.996 .996 0
-1 0 1.41-1.41L13.41 12l4.89-4.89c.38-.38 .38 -1.02 0-1.4z" />
-<path
- android:pathData="M0 0h24v24H0z" />
-</vector> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/drawable/recents_dismiss_light.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_dismiss_light.xml
deleted file mode 100644
index 2b2081404b69..000000000000
--- a/packages/SystemUI/legacy/recents/res/drawable/recents_dismiss_light.xml
+++ /dev/null
@@ -1,29 +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.
--->
-<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="@color/recents_task_bar_light_icon_color"
- android:pathData="M18.3 5.71a.996 .996 0 0 0-1.41 0L12 10.59 7.11 5.7A.996 .996 0 1 0 5.7
-7.11L10.59 12 5.7 16.89a.996 .996 0 1 0 1.41 1.41L12 13.41l4.89 4.89a.996 .996 0
-1 0 1.41-1.41L13.41 12l4.89-4.89c.38-.38 .38 -1.02 0-1.4z" />
-<path
- android:pathData="M0 0h24v24H0z" />
-</vector> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/drawable/recents_empty.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_empty.xml
deleted file mode 100644
index 5506de1d583f..000000000000
--- a/packages/SystemUI/legacy/recents/res/drawable/recents_empty.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="100dp"
- android:height="132dp"
- android:viewportWidth="100"
- android:viewportHeight="132">
-
- <path
- android:fillColor="#5AFFFFFF"
- android:pathData="M86.91,68.67H13.09c-4.96,0-9,4.04-9,9V119c0,4.96,4.04,9,9,9h73.82c4.96,0,9-4.04,9-9V77.67
-C95.91,72.7,91.87,68.67,86.91,68.67z M27.59,77.27h26.72v3.94H27.59V77.27z
-M18.73,74.74c2.49,0,4.5,2.01,4.5,4.5
-c0,2.49-2.01,4.5-4.5,4.5s-4.5-2.01-4.5-4.5C14.23,76.75,16.24,74.74,18.73,74.74z
-M89.91,119c0,1.65-1.35,3-3,3H13.09 c-1.65,0-3-1.35-3-3V88.67h79.82V119z" />
- <path
- android:fillColor="#5AFFFFFF"
- android:pathData="M86.91,36.3H13.09c-4.96,0-9,4.04-9,9v23c1.65-1.58,3.71-2.73,6-3.28v-9.08h79.82v9.08
-c2.29,0.55,4.35,1.69,6,3.28v-23C95.91,40.34,91.87,36.3,86.91,36.3z
-M18.73,51.38c-2.49,0-4.5-2.01-4.5-4.5s2.01-4.5,4.5-4.5
-s4.5,2.01,4.5,4.5S21.22,51.38,18.73,51.38z M54.31,48.84H27.59v-3.94h26.72V48.84z" />
- <path
- android:fillColor="#5AFFFFFF"
- android:pathData="M86.91,4H13.09c-4.96,0-9,4.04-9,9v22.94c1.65-1.58,3.71-2.73,6-3.28V24h79.82v8.67
-c2.29,0.55,4.35,1.69,6,3.28V13C95.91,8.04,91.87,4,86.91,4z
-M18.73,18.5c-2.49,0-4.5-2.01-4.5-4.5s2.01-4.5,4.5-4.5
-s4.5,2.01,4.5,4.5S21.22,18.5,18.73,18.5z M54.31,15.97H27.59v-3.94h26.72V15.97z" />
- <path
- android:pathData="M 0 0 H 100 V 132 H 0 V 0 Z" />
-</vector> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/drawable/recents_grid_task_view_focus_frame_background.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_grid_task_view_focus_frame_background.xml
deleted file mode 100644
index 4987f9bcf610..000000000000
--- a/packages/SystemUI/legacy/recents/res/drawable/recents_grid_task_view_focus_frame_background.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="#61FFFFFF" />
- <corners android:radius="@dimen/recents_grid_task_view_focused_frame_rounded_corners_radius"/>
-</shape> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/drawable/recents_info_dark.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_info_dark.xml
deleted file mode 100644
index 555a69ae9123..000000000000
--- a/packages/SystemUI/legacy/recents/res/drawable/recents_info_dark.xml
+++ /dev/null
@@ -1,24 +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.
--->
-<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="@color/recents_task_bar_dark_icon_color"
- android:pathData="M12.000000,2.000000C6.500000,2.000000 2.000000,6.500000 2.000000,12.000000s4.500000,10.000000 10.000000,10.000000c5.500000,0.000000 10.000000,-4.500000 10.000000,-10.000000S17.500000,2.000000 12.000000,2.000000zM13.000000,17.000000l-2.000000,0.000000l0.000000,-6.000000l2.000000,0.000000L13.000000,17.000000zM13.000000,9.000000l-2.000000,0.000000L11.000000,7.000000l2.000000,0.000000L13.000000,9.000000z"/>
-</vector>
diff --git a/packages/SystemUI/legacy/recents/res/drawable/recents_info_light.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_info_light.xml
deleted file mode 100644
index 65e7bf5fa41d..000000000000
--- a/packages/SystemUI/legacy/recents/res/drawable/recents_info_light.xml
+++ /dev/null
@@ -1,24 +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.
--->
-<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="M12.000000,2.000000C6.500000,2.000000 2.000000,6.500000 2.000000,12.000000s4.500000,10.000000 10.000000,10.000000c5.500000,0.000000 10.000000,-4.500000 10.000000,-10.000000S17.500000,2.000000 12.000000,2.000000zM13.000000,17.000000l-2.000000,0.000000l0.000000,-6.000000l2.000000,0.000000L13.000000,17.000000zM13.000000,9.000000l-2.000000,0.000000L11.000000,7.000000l2.000000,0.000000L13.000000,9.000000z"
- android:fillColor="#FFFFFF"/>
-</vector>
diff --git a/packages/SystemUI/legacy/recents/res/drawable/recents_lock_to_app_pin.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_lock_to_app_pin.xml
deleted file mode 100644
index 317f858f662d..000000000000
--- a/packages/SystemUI/legacy/recents/res/drawable/recents_lock_to_app_pin.xml
+++ /dev/null
@@ -1,25 +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.
--->
-<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="M16.000000,12.000000L16.000000,4.000000l1.000000,0.000000L17.000000,2.000000L7.000000,2.000000l0.000000,2.000000l1.000000,0.000000l0.000000,8.000000l-2.000000,2.000000l0.000000,2.000000l5.200000,0.000000l0.000000,6.000000l1.600000,0.000000l0.000000,-6.000000L18.000000,16.000000l0.000000,-2.000000L16.000000,12.000000z"/>
-</vector>
diff --git a/packages/SystemUI/legacy/recents/res/drawable/recents_lock_to_task_button_bg.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_lock_to_task_button_bg.xml
deleted file mode 100644
index 8a8164a94122..000000000000
--- a/packages/SystemUI/legacy/recents/res/drawable/recents_lock_to_task_button_bg.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="#ff9cdfd9">
- <item>
- <shape android:shape="oval">
- <solid android:color="#9cc8c4" />
- <size android:width="@dimen/recents_lock_to_app_size"
- android:height="@dimen/recents_lock_to_app_size" />
- </shape>
- </item>
-</ripple> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/drawable/recents_low_ram_stack_button_background.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_low_ram_stack_button_background.xml
deleted file mode 100644
index bff97f6f8fe4..000000000000
--- a/packages/SystemUI/legacy/recents/res/drawable/recents_low_ram_stack_button_background.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
- ~ 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.
- -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android" >
-
- <corners android:radius="@dimen/borderless_button_radius" />
-
- <solid android:color="?attr/clearAllBackgroundColor" />
-
-</shape>
diff --git a/packages/SystemUI/legacy/recents/res/drawable/recents_move_task_fullscreen_dark.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_move_task_fullscreen_dark.xml
deleted file mode 100644
index fd468c1ff25e..000000000000
--- a/packages/SystemUI/legacy/recents/res/drawable/recents_move_task_fullscreen_dark.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<!--
-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"
- android:viewportHeight="24">
- <group
- android:translateX="-252.000000"
- android:translateY="-602.000000">
- <group
- android:translateX="109.000000"
- android:translateY="514.000000">
- <group
- android:translateX="144.000000"
- android:translateY="89.000000">
- <path
- android:strokeColor="@color/recents_task_bar_dark_icon_color"
- android:strokeWidth="2"
- android:pathData="M17,17 L5,17 L5,5 L17,5 L17,17 Z" />
- </group>
- </group>
- </group>
-</vector> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/drawable/recents_move_task_fullscreen_light.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_move_task_fullscreen_light.xml
deleted file mode 100644
index 532290637d74..000000000000
--- a/packages/SystemUI/legacy/recents/res/drawable/recents_move_task_fullscreen_light.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<!--
-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"
- android:viewportHeight="24">
- <group
- android:translateX="-252.000000"
- android:translateY="-602.000000">
- <group
- android:translateX="109.000000"
- android:translateY="514.000000">
- <group
- android:translateX="144.000000"
- android:translateY="89.000000">
- <path
- android:strokeColor="@color/recents_task_bar_light_icon_color"
- android:strokeWidth="2"
- android:pathData="M19,19 L3,19 L3,3 L19,3 L19,5 L19,18 L19,19 Z" />
- </group>
- </group>
- </group>
-</vector> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/drawable/recents_stack_action_background.xml b/packages/SystemUI/legacy/recents/res/drawable/recents_stack_action_background.xml
deleted file mode 100644
index 2a40dd0ec613..000000000000
--- a/packages/SystemUI/legacy/recents/res/drawable/recents_stack_action_background.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT 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="?android:attr/colorControlHighlight">
- <item android:id="@android:id/mask">
- <shape>
- <corners android:radius="@dimen/recents_task_view_rounded_corners_radius" />
- <solid android:color="@android:color/white" />
- </shape>
- </item>
-</ripple> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/interpolator/recents_from_launcher_exit_interpolator.xml b/packages/SystemUI/legacy/recents/res/interpolator/recents_from_launcher_exit_interpolator.xml
deleted file mode 100644
index 4a7fff67eac5..000000000000
--- a/packages/SystemUI/legacy/recents/res/interpolator/recents_from_launcher_exit_interpolator.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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.
-*/
--->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:controlX1="0"
- android:controlY1="0"
- android:controlX2="0.8"
- android:controlY2="1" />
diff --git a/packages/SystemUI/legacy/recents/res/interpolator/recents_launch_next_affiliated_task_bounce_scale.xml b/packages/SystemUI/legacy/recents/res/interpolator/recents_launch_next_affiliated_task_bounce_scale.xml
deleted file mode 100644
index c4e5d972222c..000000000000
--- a/packages/SystemUI/legacy/recents/res/interpolator/recents_launch_next_affiliated_task_bounce_scale.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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.
-*/
--->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0,0 c 0.8,0 0.2,1 1,1" />
diff --git a/packages/SystemUI/legacy/recents/res/interpolator/recents_launch_prev_affiliated_task_bounce_ydelta.xml b/packages/SystemUI/legacy/recents/res/interpolator/recents_launch_prev_affiliated_task_bounce_ydelta.xml
deleted file mode 100644
index 40a08b97160d..000000000000
--- a/packages/SystemUI/legacy/recents/res/interpolator/recents_launch_prev_affiliated_task_bounce_ydelta.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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.
-*/
--->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0,0 c 0.6,0 0.2,1 1,1" />
diff --git a/packages/SystemUI/legacy/recents/res/interpolator/recents_to_launcher_enter_interpolator.xml b/packages/SystemUI/legacy/recents/res/interpolator/recents_to_launcher_enter_interpolator.xml
deleted file mode 100644
index c61dfd87d842..000000000000
--- a/packages/SystemUI/legacy/recents/res/interpolator/recents_to_launcher_enter_interpolator.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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.
-*/
--->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:controlX1="0.4"
- android:controlY1="0"
- android:controlX2="1"
- android:controlY2="1" />
diff --git a/packages/SystemUI/legacy/recents/res/layout/recents.xml b/packages/SystemUI/legacy/recents/res/layout/recents.xml
deleted file mode 100644
index ae89631219fb..000000000000
--- a/packages/SystemUI/legacy/recents/res/layout/recents.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <!-- Recents View -->
- <com.android.systemui.recents.views.RecentsView
- android:id="@+id/recents_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- </com.android.systemui.recents.views.RecentsView>
-
- <!-- Incompatible task overlay -->
- <ViewStub android:id="@+id/incompatible_app_overlay_stub"
- android:inflatedId="@+id/incompatible_app_overlay"
- android:layout="@layout/recents_incompatible_app_overlay"
- android:layout_width="match_parent"
- android:layout_height="128dp"
- android:layout_gravity="center_horizontal|top" />
-
- <!-- Nav Bar Scrim View -->
- <ImageView
- android:id="@+id/nav_bar_scrim"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal|bottom"
- android:scaleType="fitXY"
- android:src="@drawable/recents_lower_gradient" />
-</FrameLayout>
diff --git a/packages/SystemUI/legacy/recents/res/layout/recents_empty.xml b/packages/SystemUI/legacy/recents/res/layout/recents_empty.xml
deleted file mode 100644
index d7f058ce4a9d..000000000000
--- a/packages/SystemUI/legacy/recents/res/layout/recents_empty.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<TextView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:gravity="center"
- android:drawableTop="@drawable/recents_empty"
- android:drawablePadding="25dp"
- android:textSize="16sp"
- android:drawableTint="?attr/wallpaperTextColor"
- android:textColor="?attr/wallpaperTextColor"
- android:text="@string/recents_empty_message"
- android:fontFamily="sans-serif"
- android:visibility="gone" /> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/layout/recents_grid_task_view.xml b/packages/SystemUI/legacy/recents/res/layout/recents_grid_task_view.xml
deleted file mode 100644
index 1c9b9ac5f5f2..000000000000
--- a/packages/SystemUI/legacy/recents/res/layout/recents_grid_task_view.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?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.
--->
-<com.android.systemui.recents.views.grid.GridTaskView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:focusable="true">
- <com.android.systemui.recents.views.grid.GridTaskViewThumbnail
- android:id="@+id/task_view_thumbnail"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
- <include layout="@layout/recents_task_view_header" />
-
- <!-- TODO: Move this into a view stub -->
- <include layout="@layout/recents_task_view_lock_to_app"/>
-
- <!-- The incompatible app toast -->
- <include layout="@layout/recents_task_view_incompatible_app_toast"/>
-</com.android.systemui.recents.views.grid.GridTaskView>
-
-
diff --git a/packages/SystemUI/legacy/recents/res/layout/recents_incompatible_app_overlay.xml b/packages/SystemUI/legacy/recents/res/layout/recents_incompatible_app_overlay.xml
deleted file mode 100644
index a1c1e5bd32c7..000000000000
--- a/packages/SystemUI/legacy/recents/res/layout/recents_incompatible_app_overlay.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?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"
- android:alpha="0"
- android:background="#88000000"
- android:forceHasOverlappingRendering="false">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:drawableTop="@drawable/recents_info_light"
- android:drawablePadding="8dp"
- android:text="@string/dock_non_resizeble_failed_to_dock_text"
- android:textColor="@android:color/white" />
-</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/layout/recents_low_ram_stack_action_button.xml b/packages/SystemUI/legacy/recents/res/layout/recents_low_ram_stack_action_button.xml
deleted file mode 100644
index dca891103c4e..000000000000
--- a/packages/SystemUI/legacy/recents/res/layout/recents_low_ram_stack_action_button.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?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.
- -->
-<TextView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingStart="26dp"
- android:paddingEnd="26dp"
- android:paddingTop="17dp"
- android:paddingBottom="17dp"
- android:text="@string/recents_stack_action_button_label"
- android:textSize="14sp"
- android:textColor="#FFFFFF"
- android:textAllCaps="true"
- android:fontFamily="sans-serif-medium"
- android:background="@drawable/recents_low_ram_stack_button_background"
- android:visibility="invisible"
- android:forceHasOverlappingRendering="false"
- style="?attr/clearAllStyle" />
diff --git a/packages/SystemUI/legacy/recents/res/layout/recents_search_bar.xml b/packages/SystemUI/legacy/recents/res/layout/recents_search_bar.xml
deleted file mode 100644
index 915283e30f65..000000000000
--- a/packages/SystemUI/legacy/recents/res/layout/recents_search_bar.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/search_bg_transparent">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:text="@string/recents_search_bar_label"
- android:textColor="#99ffffff"
- android:textSize="18sp"
- android:textAllCaps="true" />
-</FrameLayout>
-
diff --git a/packages/SystemUI/legacy/recents/res/layout/recents_stack_action_button.xml b/packages/SystemUI/legacy/recents/res/layout/recents_stack_action_button.xml
deleted file mode 100644
index 4707a8ca843f..000000000000
--- a/packages/SystemUI/legacy/recents/res/layout/recents_stack_action_button.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<TextView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingStart="14dp"
- android:paddingEnd="14dp"
- android:paddingTop="12dp"
- android:paddingBottom="12dp"
- android:text="@string/recents_stack_action_button_label"
- android:textSize="14sp"
- android:textColor="?attr/wallpaperTextColor"
- android:textAllCaps="true"
- android:shadowColor="#99000000"
- android:shadowDx="0"
- android:shadowDy="2"
- android:shadowRadius="5"
- android:fontFamily="sans-serif-medium"
- android:background="@drawable/recents_stack_action_background"
- android:visibility="invisible"
- android:forceHasOverlappingRendering="false"
- style="?attr/clearAllStyle" />
diff --git a/packages/SystemUI/legacy/recents/res/layout/recents_task_view.xml b/packages/SystemUI/legacy/recents/res/layout/recents_task_view.xml
deleted file mode 100644
index 015e4a2006bb..000000000000
--- a/packages/SystemUI/legacy/recents/res/layout/recents_task_view.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<com.android.systemui.recents.views.TaskView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:focusable="true">
- <com.android.systemui.recents.views.TaskViewThumbnail
- android:id="@+id/task_view_thumbnail"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
- <include layout="@layout/recents_task_view_header" />
-
- <!-- TODO: Move this into a view stub -->
- <include layout="@layout/recents_task_view_lock_to_app"/>
-
- <!-- The incompatible app toast -->
- <include layout="@layout/recents_task_view_incompatible_app_toast"/>
-</com.android.systemui.recents.views.TaskView>
-
-
diff --git a/packages/SystemUI/legacy/recents/res/layout/recents_task_view_header.xml b/packages/SystemUI/legacy/recents/res/layout/recents_task_view_header.xml
deleted file mode 100644
index 1734506dbaba..000000000000
--- a/packages/SystemUI/legacy/recents/res/layout/recents_task_view_header.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
--->
-<!-- The layouts params are calculated in TaskViewHeader.java -->
-<com.android.systemui.recents.views.TaskViewHeader
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/task_view_bar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="top|center_horizontal">
- <com.android.systemui.recents.views.FixedSizeImageView
- android:id="@+id/icon"
- android:contentDescription="@string/recents_app_info_button_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|start"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:paddingStart="16dp"
- android:paddingEnd="12dp" />
- <TextView
- android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|start"
- android:textSize="16sp"
- android:textColor="#ffffffff"
- android:text="@string/recents_empty_message"
- android:fontFamily="sans-serif-medium"
- android:singleLine="true"
- android:maxLines="1"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- android:forceHasOverlappingRendering="false" />
- <com.android.systemui.recents.views.FixedSizeImageView
- android:id="@+id/move_task"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|end"
- android:padding="@dimen/recents_task_view_header_button_padding"
- android:src="@drawable/star"
- android:background="?android:selectableItemBackground"
- android:alpha="0"
- android:visibility="gone" />
- <com.android.systemui.recents.views.FixedSizeImageView
- android:id="@+id/dismiss_task"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|end"
- android:padding="@dimen/recents_task_view_header_button_padding"
- android:src="@drawable/recents_dismiss_light"
- android:background="?android:selectableItemBackground"
- android:alpha="0"
- android:visibility="gone" />
-
- <!-- The app overlay shows as the user long-presses on the app icon -->
- <ViewStub android:id="@+id/app_overlay_stub"
- android:inflatedId="@+id/app_overlay"
- android:layout="@layout/recents_task_view_header_overlay"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-</com.android.systemui.recents.views.TaskViewHeader>
diff --git a/packages/SystemUI/legacy/recents/res/layout/recents_task_view_header_overlay.xml b/packages/SystemUI/legacy/recents/res/layout/recents_task_view_header_overlay.xml
deleted file mode 100644
index cf09b1d108ec..000000000000
--- a/packages/SystemUI/legacy/recents/res/layout/recents_task_view_header_overlay.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?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.
--->
-<!-- The layouts params are calculated in TaskViewHeader.java -->
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <com.android.systemui.recents.views.FixedSizeImageView
- android:id="@+id/app_icon"
- android:contentDescription="@string/recents_app_info_button_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|start"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:paddingStart="16dp"
- android:paddingEnd="12dp" />
- <TextView
- android:id="@+id/app_title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|start"
- android:textSize="16sp"
- android:textColor="#ffffffff"
- android:text="@string/recents_empty_message"
- android:fontFamily="sans-serif-medium"
- android:singleLine="true"
- android:maxLines="2"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal" />
- <com.android.systemui.recents.views.FixedSizeImageView
- android:id="@+id/app_info"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|end"
- android:padding="@dimen/recents_task_view_header_button_padding"
- android:background="?android:selectableItemBackground"
- android:src="@drawable/recents_info_light" />
-</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/layout/recents_task_view_header_progress_bar.xml b/packages/SystemUI/legacy/recents/res/layout/recents_task_view_header_progress_bar.xml
deleted file mode 100644
index f3526322f52d..000000000000
--- a/packages/SystemUI/legacy/recents/res/layout/recents_task_view_header_progress_bar.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?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.
--->
-<ProgressBar
- xmlns:android="http://schemas.android.com/apk/res/android"
- style="?android:attr/progressBarStyleHorizontal"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:indeterminateOnly="false"
- android:visibility="invisible" /> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/layout/recents_task_view_incompatible_app_toast.xml b/packages/SystemUI/legacy/recents/res/layout/recents_task_view_incompatible_app_toast.xml
deleted file mode 100644
index d573d6b881d2..000000000000
--- a/packages/SystemUI/legacy/recents/res/layout/recents_task_view_incompatible_app_toast.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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.
--->
-<ViewStub
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/incompatible_app_toast_stub"
- android:inflatedId="@+id/incompatible_app_toast"
- android:layout="@*android:layout/transient_notification"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|center_horizontal"
- android:layout_marginTop="48dp"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp" /> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/layout/recents_task_view_lock_to_app.xml b/packages/SystemUI/legacy/recents/res/layout/recents_task_view_lock_to_app.xml
deleted file mode 100644
index 8cece1149ce7..000000000000
--- a/packages/SystemUI/legacy/recents/res/layout/recents_task_view_lock_to_app.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?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.
--->
-<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/lock_to_app_fab"
- android:layout_width="@dimen/recents_lock_to_app_size"
- android:layout_height="@dimen/recents_lock_to_app_size"
- android:layout_gravity="bottom|end"
- android:layout_marginEnd="15dp"
- android:layout_marginBottom="15dp"
- android:translationZ="4dp"
- android:contentDescription="@string/recents_lock_to_app_button_label"
- android:background="@drawable/recents_lock_to_task_button_bg"
- android:visibility="invisible"
- android:alpha="0">
- <ImageView
- android:layout_width="@dimen/recents_lock_to_app_icon_size"
- android:layout_height="@dimen/recents_lock_to_app_icon_size"
- android:layout_gravity="center"
- android:src="@drawable/recents_lock_to_app_pin" />
-</com.android.systemui.statusbar.AlphaOptimizedFrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/values-af/strings.xml b/packages/SystemUI/legacy/recents/res/values-af/strings.xml
deleted file mode 100644
index 736c81034c06..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-af/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Oorsig."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Maak <xliff:g id="APP">%s</xliff:g> toe."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> is toegemaak."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Alle onlangse programme is toegemaak."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Maak <xliff:g id="APP">%s</xliff:g>-programinligting oop."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Begin tans <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Geen onlangse items nie"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Jy het alles toegemaak"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Programinligting"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"skermvaspen"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"soek"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Kon nie <xliff:g id="APP">%s</xliff:g> begin nie."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> is in veiligmodus gedeaktiveer."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Vee alles uit"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Sleep hierheen om verdeelde skerm te gebruik"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Verdeel horisontaal"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Verdeel vertikaal"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Verdeel gepasmaak"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Verdeel skerm na bo"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Verdeel skerm na links"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Verdeel skerm na regs"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-am/strings.xml b/packages/SystemUI/legacy/recents/res/values-am/strings.xml
deleted file mode 100644
index 2870be712779..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-am/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"አጠቃላይ እይታ።"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> አስወግድ።"</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> ተሰናብቷል።"</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"ሁሉም የቅርብ ጊዜ ማመልከቻዎች ተሰናብተዋል።"</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"የ<xliff:g id="APP">%s</xliff:g> መተግበሪያ መረጃውን ይክፈቱ።"</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> በመጀመር ላይ።"</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"ምንም የቅርብ ጊዜ ንጥሎች የሉም"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"ሁሉንም ነገር አጽድተዋል"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"የመተግበሪያ መረጃ"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ማያ ገጽ መሰካት"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"ፈልግ"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g>ን መጀመር አልተቻለም።"</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> በጥንቃቄ ሁነታ ውስጥ ታግዷል።"</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"ሁሉንም አጽዳ"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"የተከፈለ ማያ ገጽን ለመጠቀም እዚህ ላይ ይጎትቱ"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"አግድም ክፈል"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"ቁልቁል ክፈል"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"በብጁ ክፈል"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"ማያ ገጽ ወደ ላይ ክፈል"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"ማያ ገጽ ወደ ግራ ክፈል"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"ማያ ገጽ ወደ ቀኝ ክፈል"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-ar/strings.xml b/packages/SystemUI/legacy/recents/res/values-ar/strings.xml
deleted file mode 100644
index 004de41891a0..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-ar/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"النظرة عامة"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"إزالة <xliff:g id="APP">%s</xliff:g>"</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"تمَّت إزالة <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"تمَّت إزالة كل التطبيقات المستخدمة مؤخرًا."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"فتح معلومات تطبيق <xliff:g id="APP">%s</xliff:g>"</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"جارٍ بدء <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"ليست هناك عناصر تم استخدامها مؤخرًا"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"لقد محوتَ كل شيء"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"معلومات التطبيق"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"تثبيت الشاشة"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"بحث"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"تعذَّر بدء <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"تم إيقاف <xliff:g id="APP">%s</xliff:g> في الوضع الآمن."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"محو الكل"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"اسحب هنا لاستخدام وضع تقسيم الشاشة"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"تقسيم أفقي"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"تقسيم رأسي"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"تقسيم مخصَّص"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"تقسيم الشاشة بمحاذاة الجزء العلوي"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"تقسيم الشاشة بمحاذاة اليسار"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"تقسيم الشاشة بمحاذاة اليمين"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-as/strings.xml b/packages/SystemUI/legacy/recents/res/values-as/strings.xml
deleted file mode 100644
index c742dab230e6..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-as/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"অৱলোকন।"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"শেহতীয়া-ৰ তালিকাৰ পৰা <xliff:g id="APP">%s</xliff:g>ক আঁতৰাওক।"</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"শেহতীয়া-ৰ তালিকাৰ পৰা <xliff:g id="APP">%s</xliff:g>ক আঁতৰোৱা হ’ল।"</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"শেহতীয়া-ৰ তালিকাৰ পৰা সকলো এপ্লিকেশ্বন আঁতৰোৱা হ’ল।"</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> এপ্লিকেশ্বনৰ তথ্য খোলক।"</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g>ক আৰম্ভ কৰা হৈছে।"</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"কোনো শেহতীয়া বস্তু নাই"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"আপুনি সকলো খালী কৰিলে"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"এপ্লিকেশ্বনৰ তথ্য"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"স্ক্ৰীণ পিনিং"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"সন্ধান কৰক"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g>ক আৰম্ভ কৰিব পৰা নগ’ল।"</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g>টো সুৰক্ষিত ম’ডত অক্ষম কৰা হ’ল।"</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"সকলো মচক"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"বিভাজিত স্ক্ৰীণ ব্যৱহাৰ কৰিবলৈ ইয়ালৈ টানি আনি এৰক"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"আনুভূমিকভাৱে বিভাজন কৰক"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"উলম্বভাৱে বিভাজন কৰক"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"কাষ্টম বিভাজন কৰক"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"স্ক্ৰীণখনক ওপৰফাললৈ ভাগ কৰক"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"স্ক্ৰীণখনক বাওঁফাললৈ ভাগ কৰক"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"স্ক্ৰীণখনক সোঁফাললৈ ভাগ কৰক"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-az/strings.xml b/packages/SystemUI/legacy/recents/res/values-az/strings.xml
deleted file mode 100644
index 76ae02aa0f24..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-az/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"İcmal."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> tətbiqini silin."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> silindi."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Bütün son tətbiqlər silindi."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> tətbiq məlumatını açın."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> başladılır."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Ən son element yoxdur"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Hər şeyi sildiniz"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Tətbiq məlumatı"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ekran sancağı"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"axtarış"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> başladılmadı."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> təhlükəsiz rejimdə deaktiv edildi."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Hamısını silin"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Bölünmüş ekrandan istifadə etmək üçün bura sürüşdürün"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Horizontal Bölün"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Vertikal Bölün"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Fərdi Bölün"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Ekranı yuxarıya doğru bölün"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Ekranı sola doğru bölün"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Ekranı sağa doğru bölün"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/legacy/recents/res/values-b+sr+Latn/strings.xml
deleted file mode 100644
index 3117eeac77f7..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-b+sr+Latn/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Pregled."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Odbacite aplikaciju <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Aplikacija <xliff:g id="APP">%s</xliff:g> je odbačena."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Sve nedavno korišćene aplikacije su odbačene."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Otvorite informacije o aplikaciji <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Pokreće se <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Nema nedavnih stavki"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Obrisali ste sve"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informacije o aplikaciji"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"kačenje ekrana"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"pretraži"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Pokretanje aplikacije <xliff:g id="APP">%s</xliff:g> nije uspelo."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"Aplikacija <xliff:g id="APP">%s</xliff:g> je onemogućena u bezbednom režimu."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Obriši sve"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Prevucite ovde da biste koristili razdeljeni ekran"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Podeli horizontalno"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Podeli vertikalno"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Podeli prilagođeno"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Podeli ekran nagore"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Podeli ekran nalevo"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Podeli ekran nadesno"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-be/strings.xml b/packages/SystemUI/legacy/recents/res/values-be/strings.xml
deleted file mode 100644
index 812184651e6e..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-be/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Агляд."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Закрыць праграму \"<xliff:g id="APP">%s</xliff:g>\"."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Праграма \"<xliff:g id="APP">%s</xliff:g>\" закрыта."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Усе нядаўнія праграмы закрыты."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Адкрыць інфармацыю пра праграму \"<xliff:g id="APP">%s</xliff:g>\"."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Запускаецца праграма \"<xliff:g id="APP">%s</xliff:g>\"."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Няма нядаўніх элементаў"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Вы ўсё выдалілі"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Інфармацыя пра праграму"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"замацаванне экрана"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"пошук"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Не ўдалося запусціць праграму \"<xliff:g id="APP">%s</xliff:g>\"."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"Праграма \"<xliff:g id="APP">%s</xliff:g>\" адключана ў бяспечным рэжыме."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Ачысціць усё"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Перацягніце сюды, каб перайсці ў рэжым падзеленага экрана"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Падзяліць гарызантальна"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Падзяліць вертыкальна"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Падзяліць іншым чынам"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Падзяліць экран зверху"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Падзяліць экран злева"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Падзяліць экран справа"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-bg/strings.xml b/packages/SystemUI/legacy/recents/res/values-bg/strings.xml
deleted file mode 100644
index 3dda34fa00be..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-bg/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Общ преглед."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Отхвърляне на <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Приложението <xliff:g id="APP">%s</xliff:g> е отхвърлено."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Всички скорошни приложения са отхвърлени."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Отворете информацията за приложението <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> се стартира."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Няма скорошни елементи"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Изчистихте всичко"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Информация за приложението"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"фиксиране на екрана"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"търсене"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> не можа да стартира."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"Приложението <xliff:g id="APP">%s</xliff:g> е деактивирано в безопасния режим."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Изчистване на всичко"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Преместете тук с плъзгане, за да използвате режим за разделен екран"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Хоризонтално разделяне"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Вертикално разделяне"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Персонализирано разделяне"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Разделяне на екрана нагоре"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Разделяне на екрана наляво"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Разделяне на екрана надясно"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-bn/strings.xml b/packages/SystemUI/legacy/recents/res/values-bn/strings.xml
deleted file mode 100644
index b22672e1dc6a..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-bn/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"এক নজরে।"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> খারিজ করুন।"</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> খারিজ করা হয়েছে।"</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"সব সাম্প্রতিক অ্যাপ্লিকেশন খারিজ করা হয়েছে।"</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> অ্যাপ্লিকেশনের তথ্য খুলুন।"</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> শুরু করা হচ্ছে।"</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"কোনো সাম্প্রতিক আইটেম নেই"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"আপনি সবকিছু মুছে দিয়েছেন"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"অ্যাপ্লিকেশনের তথ্য"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"স্ক্রিন পিন করা"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"খুঁজুন"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> চালু করা যায়নি।"</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"নিরাপদ মোডে <xliff:g id="APP">%s</xliff:g> বন্ধ করা আছে।"</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"সবগুলি মুছে দিন"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"স্প্লিট স্ক্রিন ব্যবহার করতে এখানে টেনে আনুন"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"অনুভূমিক স্প্লিট করুন"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"উল্লম্ব স্প্লিট করুন"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"কাস্টম স্প্লিট করুন"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"স্ক্রিনটি উপরের দিকে স্প্লিট করুন"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"স্ক্রিনটি বাঁদিকে স্প্লিট করুন"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"স্ক্রিনটি ডানদিকে স্প্লিট করুন"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-bs/strings.xml b/packages/SystemUI/legacy/recents/res/values-bs/strings.xml
deleted file mode 100644
index 8e149ba800bb..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-bs/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Pregled."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Odbaci aplikaciju <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Aplikacija <xliff:g id="APP">%s</xliff:g> je odbačena."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Sve nedavno korištene aplikacije su odbačene."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Otvaranje informacija o aplikaciji <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Pokretanje aplikacije <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Nema nedavnih stavki"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Sve ste obrisali"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informacije o aplikaciji"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"kačenje ekrana"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"pretraži"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Aplikacija <xliff:g id="APP">%s</xliff:g> nije pokrenuta."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"Aplikacija <xliff:g id="APP">%s</xliff:g> je onemogućena u sigurnom načinu rada."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Obriši sve"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Povucite ovdje za korištenje podijeljenog ekrana"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Podjela po horizontali"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Podjela po vertikali"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Prilagođena podjela"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Dijeli ekran nagore"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Dijeli ekran nalijevo"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Dijeli ekran nadesno"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-ca/strings.xml b/packages/SystemUI/legacy/recents/res/values-ca/strings.xml
deleted file mode 100644
index fff525ce6fd6..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-ca/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Aplicacions recents."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Ignora <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"S\'ha ignorat <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"S\'han ignorat totes les aplicacions recents."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Obre la informació sobre l\'aplicació <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"S\'està iniciant <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"No hi ha cap element recent"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Ho has esborrat tot"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informació de l\'aplicació"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"fixació de pantalla"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"cerca"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"No s\'ha pogut iniciar <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"En mode segur, l\'aplicació <xliff:g id="APP">%s</xliff:g> està desactivada."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Esborra-ho tot"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Arrossega-ho aquí per utilitzar la pantalla dividida"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Divisió horitzontal"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Divisió vertical"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Divisió personalitzada"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Divideix la pantalla cap amunt"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Divideix la pantalla cap a l\'esquerra"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Divideix la pantalla cap a la dreta"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-cs/strings.xml b/packages/SystemUI/legacy/recents/res/values-cs/strings.xml
deleted file mode 100644
index 200f7a8aded7..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-cs/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Přehled"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Zavřít aplikaci <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Aplikace <xliff:g id="APP">%s</xliff:g> byla odebrána."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Všechny naposledy použité aplikace byly odstraněny."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Otevře informace o aplikaci <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Spouštění aplikace <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Žádné nedávné položky"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Vše je vymazáno"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informace o aplikaci"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"připnutí obrazovky"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"hledat"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Aplikaci <xliff:g id="APP">%s</xliff:g> nelze spustit."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"Aplikace <xliff:g id="APP">%s</xliff:g> je v nouzovém režimu zakázána."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Vymazat vše"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Přetáhnutím sem aktivujete rozdělenou obrazovku"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Vodorovné rozdělení"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Svislé rozdělení"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Vlastní rozdělení"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Rozdělit obrazovku nahoru"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Rozdělit obrazovku vlevo"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Rozdělit obrazovku vpravo"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-da/strings.xml b/packages/SystemUI/legacy/recents/res/values-da/strings.xml
deleted file mode 100644
index 0a1690e4dd59..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-da/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Oversigt."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Fjern <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> er fjernet."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Alle de seneste apps er fjernet."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Åbn appoplysningerne for <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> åbnes."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Ingen nye elementer"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Du har ryddet alt"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Appoplysninger"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"skærmfastholdelse"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"søg"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> kunne ikke åbnes."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> er deaktiveret i sikker tilstand."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Ryd alle"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Træk hertil for at bruge opdelt skærm"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Opdel vandret"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Opdel lodret"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Opdel brugerdefineret"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Opdelt skærm øverst"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Opdelt skærm til venstre"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Opdelt skærm til højre"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-de/strings.xml b/packages/SystemUI/legacy/recents/res/values-de/strings.xml
deleted file mode 100644
index 4a089bff6b79..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-de/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Übersicht."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> entfernen."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> wurde entfernt."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Alle kürzlich verwendeten Apps wurden entfernt."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Infos zur <xliff:g id="APP">%s</xliff:g> App öffnen."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> wird gestartet."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Keine kürzlich verwendeten Elemente"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Du hast alles gelöscht"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"App-Info"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"Bildschirm anpinnen"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"Suchen"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> konnte nicht gestartet werden."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> ist im abgesicherten Modus deaktiviert."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Alle löschen"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Hierher ziehen, um den Bildschirm zu teilen"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Geteilt – horizontal"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Geteilt – vertikal"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Geteilt – benutzerdefiniert"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Geteilten Bildschirm oben anzeigen"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Geteilten Bildschirm links anzeigen"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Geteilten Bildschirm rechts anzeigen"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-el/strings.xml b/packages/SystemUI/legacy/recents/res/values-el/strings.xml
deleted file mode 100644
index 90baf52342e8..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-el/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Επισκόπηση."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Παράβλεψη εφαρμογής <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Η εφαρμογή <xliff:g id="APP">%s</xliff:g> απορρίφθηκε."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Όλες οι πρόσφατες εφαρμογές παραβλέφθηκαν."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Άνοιγμα πληροφοριών εφαρμογής <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Έναρξη εφαρμογής <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Δεν υπάρχουν πρόσφατα στοιχεία"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Διαγράψατε όλα τα στοιχεία"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Πληροφορίες εφαρμογής"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"καρφίτσωμα οθόνης"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"αναζήτηση"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Δεν ήταν δυνατή η έναρξη της εφαρμογής <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"Η εφαρμογή <xliff:g id="APP">%s</xliff:g> έχει απενεργοποιηθεί στην ασφαλή λειτουργία."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Διαγραφή όλων"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Σύρετε εδώ για να χρησιμοποιήσετε τον διαχωρισμό οθόνης"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Οριζόντιος διαχωρισμός"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Κάθετος διαχωρισμός"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Προσαρμοσμένος διαχωρισμός"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Διαχωρισμός οθόνης στην κορυφή"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Διαχωρισμός οθόνης στα αριστερά"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Διαχωρισμός οθόνης στα δεξιά"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-en-rAU/strings.xml b/packages/SystemUI/legacy/recents/res/values-en-rAU/strings.xml
deleted file mode 100644
index af1d055d65d2..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-en-rAU/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Overview."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Dismiss <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> dismissed."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"All recent applications dismissed."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Open <xliff:g id="APP">%s</xliff:g> application info."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Starting <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"No recent items"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"You\'ve cleared everything"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Application Info"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"screen pinning"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"search"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Could not start <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> is disabled in safe-mode."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Clear all"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Drag here to use split screen"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Split Horizontal"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Split Vertical"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Split Custom"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Split screen to the top"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Split screen to the left"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Split screen to the right"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-en-rCA/strings.xml b/packages/SystemUI/legacy/recents/res/values-en-rCA/strings.xml
deleted file mode 100644
index af1d055d65d2..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-en-rCA/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Overview."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Dismiss <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> dismissed."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"All recent applications dismissed."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Open <xliff:g id="APP">%s</xliff:g> application info."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Starting <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"No recent items"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"You\'ve cleared everything"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Application Info"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"screen pinning"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"search"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Could not start <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> is disabled in safe-mode."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Clear all"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Drag here to use split screen"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Split Horizontal"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Split Vertical"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Split Custom"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Split screen to the top"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Split screen to the left"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Split screen to the right"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-en-rGB/strings.xml b/packages/SystemUI/legacy/recents/res/values-en-rGB/strings.xml
deleted file mode 100644
index af1d055d65d2..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-en-rGB/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Overview."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Dismiss <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> dismissed."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"All recent applications dismissed."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Open <xliff:g id="APP">%s</xliff:g> application info."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Starting <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"No recent items"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"You\'ve cleared everything"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Application Info"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"screen pinning"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"search"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Could not start <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> is disabled in safe-mode."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Clear all"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Drag here to use split screen"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Split Horizontal"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Split Vertical"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Split Custom"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Split screen to the top"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Split screen to the left"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Split screen to the right"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-en-rIN/strings.xml b/packages/SystemUI/legacy/recents/res/values-en-rIN/strings.xml
deleted file mode 100644
index af1d055d65d2..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-en-rIN/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Overview."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Dismiss <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> dismissed."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"All recent applications dismissed."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Open <xliff:g id="APP">%s</xliff:g> application info."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Starting <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"No recent items"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"You\'ve cleared everything"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Application Info"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"screen pinning"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"search"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Could not start <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> is disabled in safe-mode."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Clear all"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Drag here to use split screen"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Split Horizontal"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Split Vertical"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Split Custom"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Split screen to the top"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Split screen to the left"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Split screen to the right"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-en-rXC/strings.xml b/packages/SystemUI/legacy/recents/res/values-en-rXC/strings.xml
deleted file mode 100644
index ceb6b1330558..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-en-rXC/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‏‎‏‎‏‏‏‏‎‎‏‎‏‏‏‎‏‏‏‏‎‎‏‏‏‎‎‏‎‏‎‎‎‎‎‎‎‏‎‏‏‎‏‏‏‏‎‏‎‎‎‏‏‏‎‏‎Overview.‎‏‎‎‏‎"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‏‎‎‎‏‏‎‏‎‏‎‎‏‏‎‎‎‏‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‎‎‎‏‎‎‎‏‏‎‏‏‏‎‎Dismiss ‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎.‎‏‎‎‏‎"</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‎‏‏‎‎‎‏‎‏‎‏‏‎‏‏‏‎‎‎‎‎‎‎‎‎‏‏‎‏‏‏‏‎‎‏‎‎‏‏‎‎‏‏‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎ dismissed.‎‏‎‎‏‎"</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‎‏‎‏‎‎‎‏‏‏‎‏‏‏‏‎‏‎‏‎‎‏‎‎‎‎‏‎‏‎‏‎‎‎‎‎‏‎‏‏‏‎‏‏‏‎‎All recent applications dismissed.‎‏‎‎‏‎"</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‎‎‏‏‏‎‏‏‎‎‎‎‎‎‎‎‏‏‏‎‏‏‎‎‏‏‏‏‏‏‎‎‏‎‏‏‎‎‎‎‏‎‏‎‏‏‏‏‏‏‎‎‎‎Open ‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎ application info.‎‏‎‎‏‎"</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‎‎‏‏‏‏‏‏‎‎‏‏‎‎‏‏‎‏‏‏‏‎‎‏‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‏‏‎‏‏‏‏‏‎Starting ‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎.‎‏‎‎‏‎"</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‎‎‎‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‎‏‎‎‎‏‎‏‏‎‎‎‎‏‎‏‏‎‎‏‏‏‏‏‏‎No recent items‎‏‎‎‏‎"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‏‎‏‎‏‎‎‏‎‎‎‎‏‎‏‏‏‎‎‏‎‏‏‏‎‎‎‎‏‏‎‏‎‏‏‎‎‏‎‎‎‎‎‎‏‎‏‏‏‏‏‎You\'ve cleared everything‎‏‎‎‏‎"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‎‎‏‏‎‎‏‎‏‏‎‎‏‏‏‏‎‏‎‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‎‎‎‎‏‎‏‎‎Application Info‎‏‎‎‏‎"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‏‏‏‎‎‎‎‎‎‏‎‎‎‏‎‎‎‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‎‎‏‎‏‎‏‎‏‎‎‏‏‎‏‎‏‎screen pinning‎‏‎‎‏‎"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‏‎‏‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‏‏‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‏‏‎‎‏‎‎‏‎‎‏‎‏‎‏‎‏‎search‎‏‎‎‏‎"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‎‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‏‎‏‏‎‏‏‏‎‏‏‏‎‏‎‎‏‎‏‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‎Could not start ‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎.‎‏‎‎‏‎"</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‏‏‏‏‎‎‎‎‎‏‎‏‏‏‎‏‎‎‎‎‎‏‏‎‎‎‎‏‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‎‎‏‏‏‎‏‏‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎ is disabled in safe-mode.‎‏‎‎‏‎"</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‎‏‏‎‎‎‎‎‎‏‏‏‎‎‏‏‎‎‎‎‎‎‏‎‏‎‏‏‎‎‎‎‎‎‎‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‎‏‎Clear all‎‏‎‎‏‎"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‏‏‏‏‎‎‎‏‎‏‎‎‎‏‏‎‎‏‏‏‎‏‎‏‎‏‎‏‎‎‏‎‎‎‎‏‏‎‎‎‎‏‎‏‏‎‎‎‏‎‎‏‎‎‎‎Drag here to use split screen‎‏‎‎‏‎"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‏‏‏‎‏‏‏‏‎‎‏‎‏‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‏‏‎‏‎‏‏‎Split Horizontal‎‏‎‎‏‎"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‎‏‏‎‎‎‎‎‎‎‎‏‏‎‎‎‏‏‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‎‏‏‏‎‏‏‎‏‏‏‎‎‏‎‎‏‏‎‏‎Split Vertical‎‏‎‎‏‎"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‎‎‎‎‎‏‏‏‎‏‎‏‏‎‎‎‏‏‎‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‏‏‎‏‎‏‎‎‏‎‏‎‏‏‎Split Custom‎‏‎‎‏‎"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‎‏‏‏‎‎‎‎‎‎‏‎‎‏‏‎‎‎‎‎‎‎‏‏‏‎‏‏‏‎‎‎‏‎‎‏‏‏‏‎‏‏‎‎‎‏‏‏‎‎‎Split screen to the top‎‏‎‎‏‎"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‏‏‏‎‎‏‎‏‏‎‎‎‎‎‏‎‎‎‏‎‏‎‏‎‏‏‎‎‏‏‎‏‎‎‎‎‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‎Split screen to the left‎‏‎‎‏‎"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎‏‎‎‏‏‏‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‏‏‏‎‏‎‎‏‎‏‎‎‎‎‎‏‎‏‎‎Split screen to the right‎‏‎‎‏‎"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-es-rUS/strings.xml b/packages/SystemUI/legacy/recents/res/values-es-rUS/strings.xml
deleted file mode 100644
index f212b027a1ec..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-es-rUS/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Recientes"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Permite descartar <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Se descartó <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Se descartaron todas las aplicaciones recientes."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Permite abrir la información de la aplicación de <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Iniciando <xliff:g id="APP">%s</xliff:g>"</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"No hay elementos recientes"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Todo borrado"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Información de la aplicación"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"Fijar pantalla"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"Buscar"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"No se pudo iniciar <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> está inhabilitada en modo seguro."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Borrar todo"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Arrastra hasta aquí para usar la pantalla dividida"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"División horizontal"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"División vertical"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"División personalizada"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Dividir pantalla en la parte superior"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Dividir pantalla a la izquierda"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Dividir pantalla a la derecha"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-es/strings.xml b/packages/SystemUI/legacy/recents/res/values-es/strings.xml
deleted file mode 100644
index 8bcfe84a6700..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-es/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Aplicaciones recientes."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Cerrar <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Se ha ignorado la aplicación <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Se han ignorado todas las aplicaciones recientes."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Abre la información de la aplicación <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Iniciando la aplicación <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"No hay elementos recientes"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Has borrado todo"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Información de la aplicación"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"fijar pantalla"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"buscar"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"No se ha podido iniciar la aplicación <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"La aplicación <xliff:g id="APP">%s</xliff:g> se ha inhabilitado en modo seguro."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Borrar todo"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Arrastra el elemento hasta aquí para utilizar la pantalla dividida"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"División horizontal"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"División vertical"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"División personalizada"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Dividir la pantalla en la parte superior"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Dividir la pantalla a la izquierda"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Dividir la pantalla a la derecha"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-et/strings.xml b/packages/SystemUI/legacy/recents/res/values-et/strings.xml
deleted file mode 100644
index c1903af60ec9..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-et/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Ülevaade."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Rakendusest <xliff:g id="APP">%s</xliff:g> loobumine."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Rakendusest <xliff:g id="APP">%s</xliff:g> on loobutud."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Kõikidest hiljutistest rakendustest on loobutud."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Rakenduse <xliff:g id="APP">%s</xliff:g> teabe avamine."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Rakenduse <xliff:g id="APP">%s</xliff:g> käivitamine."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Hiljutisi üksusi pole"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Olete kõik ära kustutanud"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Rakenduse teave"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ekraanikuva kinnitamine"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"otsi"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Rakendust <xliff:g id="APP">%s</xliff:g> ei saanud käivitada."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"Rakendus <xliff:g id="APP">%s</xliff:g> on turvarežiimis keelatud."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Kustuta kõik"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Jagatud ekraani kasutamiseks lohistage siia"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Horisontaalne poolitamine"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Vertikaalne poolitamine"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Kohandatud poolitamine"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Poolita ekraan üles"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Poolita ekraan vasakule"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Poolita ekraan paremale"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-eu/strings.xml b/packages/SystemUI/legacy/recents/res/values-eu/strings.xml
deleted file mode 100644
index 91e250f50411..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-eu/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Ikuspegi orokorra."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Baztertu <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Baztertu da <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Baztertu dira azken aplikazio guztiak."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Ireki <xliff:g id="APP">%s</xliff:g> aplikazioari buruzko informazioa."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> abiarazten."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Ez dago azkenaldi honetako ezer"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Dena garbitu duzu"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Aplikazioaren informazioa"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"pantaila-ainguratzea"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"bilatu"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Ezin izan da abiarazi <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> desgaituta dago modu seguruan."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Garbitu guztiak"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Arrastatu hona pantaila zatitzeko"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Zatitze horizontala"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Zatitze bertikala"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Zatitze pertsonalizatua"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Zatitu pantaila eta ezarri goian"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Zatitu pantaila eta ezarri ezkerrean"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Zatitu pantaila eta ezarri eskuinean"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-fa/strings.xml b/packages/SystemUI/legacy/recents/res/values-fa/strings.xml
deleted file mode 100644
index 61e87c113774..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-fa/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"نمای کلی."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"رد کردن <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> نادیده گرفته شد."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"همه برنامه‌های اخیر رد شدند."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"باز کردن اطلاعات برنامه <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> درحال شروع به کار است."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"بدون موارد اخیر"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"همه‌چیز را پاک کرده‌اید"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"اطلاعات برنامه"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"پین کردن صفحه"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"جستجو"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> شروع نشد."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> در حالت ایمن غیرفعال است."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"پاک کردن همه"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"برای استفاده از تقسیم صفحه، به اینجا بکشید"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"تقسیم افقی"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"تقسیم عمودی"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"سفارشی کردن تقسیم"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"تقسیم کردن صفحه به بالا"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"تقسیم کردن صفحه به چپ"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"تقسیم کردن صفحه به راست"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-fi/strings.xml b/packages/SystemUI/legacy/recents/res/values-fi/strings.xml
deleted file mode 100644
index bf2e46112abc..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-fi/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Viimeisimmät"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Hylkää <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> hylättiin."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Kaikki viimeisimmät sovellukset on hylätty."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Avaa sovelluksen <xliff:g id="APP">%s</xliff:g> tiedot."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Käynnistetään <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Ei viimeaikaisia kohteita"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Kaikki on hoidettu"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Sovellustiedot"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"näytön kiinnitys"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"haku"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> ei käynnistynyt."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> on poistettu käytöstä vikasietotilassa."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Poista kaikki"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Jaa näyttö vetämällä tähän."</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Vaakasuuntainen jako"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Pystysuuntainen jako"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Oma jako"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Jaa näyttö ylös"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Jaa näyttö vasemmalle"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Jaa näyttö oikealle"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-fr-rCA/strings.xml b/packages/SystemUI/legacy/recents/res/values-fr-rCA/strings.xml
deleted file mode 100644
index e60727e38074..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-fr-rCA/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Aperçu"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Supprimer <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> supprimée."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Toutes les applications récentes ont été supprimées."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Ouvre les détails de l\'application <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Lancement de <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Aucun élément récent"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Vous avez tout effacé"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Détails de l\'application"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"épinglage d\'écran"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"rechercher"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Impossible de lancer <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> est désactivée en mode sans échec."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Tout effacer"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Glissez l\'élément ici pour utiliser l\'écran partagé"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Séparation horizontale"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Séparation verticale"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Séparation personnalisée"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Écran partagé dans le haut"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Écran partagé à la gauche"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Écran partagé à la droite"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-fr/strings.xml b/packages/SystemUI/legacy/recents/res/values-fr/strings.xml
deleted file mode 100644
index 5b0d611c2588..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-fr/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Aperçu"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Supprimer l\'application <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Application <xliff:g id="APP">%s</xliff:g> supprimée."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Toutes les applications récentes ont été supprimées."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Ouvre les informations sur l\'application <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Lancement de l\'application <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Aucun élément récent"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Vous avez tout effacé"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informations sur l\'application"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"épinglage d\'écran"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"rechercher"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Impossible de lancer l\'application <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"L\'application <xliff:g id="APP">%s</xliff:g> est désactivée en mode sécurisé."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Tout fermer"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Faire glisser ici pour utiliser l\'écran partagé"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Séparation horizontale"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Séparation verticale"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Séparation personnalisée"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Partager l\'écran en haut"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Partager l\'écran sur la gauche"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Partager l\'écran sur la droite"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-gl/strings.xml b/packages/SystemUI/legacy/recents/res/values-gl/strings.xml
deleted file mode 100644
index 008c7761c8e7..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-gl/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Visión xeral."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Rexeita <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Rexeitouse <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Rexeitáronse todas as aplicacións recentes."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Abre a información da aplicación <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Non hai elementos recentes"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Borraches todo"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Información da aplicación"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"fixación de pantalla"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"buscar"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Non se puido iniciar <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"A aplicación <xliff:g id="APP">%s</xliff:g> está desactivada no modo seguro."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Borrar todo"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Arrastra aquí para usar a pantalla dividida"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Dividir horizontalmente"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Dividir verticalmente"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Dividir de xeito personalizado"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Dividir pantalla arriba"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Dividir pantalla á esquerda"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Dividir pantalla á dereita"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-gu/strings.xml b/packages/SystemUI/legacy/recents/res/values-gu/strings.xml
deleted file mode 100644
index 33dc7e8c7ee0..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-gu/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"ઝલક."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> કાઢી નાખો."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> કાઢી નાખી."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"તાજેતરની બધી ઍપ્લિકેશનો કાઢી નાખવામાં આવી."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g>ની ઍપ્લિકેશન માહિતી ખોલો."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g>ને શરૂ કરી રહ્યાં છીએ."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"તાજેતરની કોઈ આઇટમ નથી"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"તમે બધું સાફ કર્યું"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"ઍપ્લિકેશનની માહિતી"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"સ્ક્રીન પિનિંગ"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"શોધો"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g>ને શરૂ કરી શકાઈ નથી."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"સુરક્ષિત મોડમાં <xliff:g id="APP">%s</xliff:g>ને બંધ કરવામાં આવી છે."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"બધું સાફ કરો"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"વિભાજિત સ્ક્રીનનો ઉપયોગ કરવા માટે અહીં ખેંચો"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"સ્ક્રીનને આડી વિભાજિત કરો"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"સ્ક્રીનને ઊભી વિભાજિત કરો"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"સ્ક્રીનને કસ્ટમ વિભાજિત કરો"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"સ્ક્રીનને ઉપરની તરફ વિભાજિત કરો"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"સ્ક્રીનને ડાબી તરફ વિભાજિત કરો"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"સ્ક્રીનને જમણી તરફ વિભાજિત કરો"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-hi/strings.xml b/packages/SystemUI/legacy/recents/res/values-hi/strings.xml
deleted file mode 100644
index 3f19f3378348..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-hi/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"खास जानकारी."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> को खारिज करें."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> खारिज किया गया."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"हाल के सभी ऐप्लिकेशन खारिज कर दिए गए हैं."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> ऐप्लिकेशन की जानकारी खोलें."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> शुरू किया जा रहा है."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"हाल का कोई आइटम नहीं है"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"आपने सब कुछ हटा दिया है"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"ऐप्लिकेशन की जानकारी"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"स्क्रीन पिन करना"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"खोजें"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> शुरू नहीं किया जा सका."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> को सुरक्षित-मोड में बंद किया गया."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"सभी ऐप्लिकेशन बंद करें"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"स्क्रीन को दो हिस्सों में बाँटने (स्प्लिट स्क्रीन) के लिए यहां से खींचें और छोड़ें"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"क्षैतिज रूप से दो हिस्सों में बाँटें (स्प्लिट करें)"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"लम्बवत रूप से दो हिस्सों में बाँटें (स्प्लिट करें)"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"अपने मुताबिक दो हिस्सों में बाँटें (स्प्लिट स्क्रीन करें)"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"ऊपर की ओर दूसरी स्क्रीन बनाएं"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"बाईं ओर दूसरी स्क्रीन बनाएं"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"दाईं ओर दूसरी स्क्रीन बनाएं"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-hr/strings.xml b/packages/SystemUI/legacy/recents/res/values-hr/strings.xml
deleted file mode 100644
index 88926a4f8c76..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-hr/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Pregled."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Odbacivanje aplikacije <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Odbačena je aplikacija <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Odbačene su sve nedavne aplikacije."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Otvaranje informacija o aplikaciji <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Pokreće se aplikacija <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Nema nedavnih stavki"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Izbrisali ste sve"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informacije o aplikaciji"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"prikačivanje zaslona"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"pretraživanje"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Aplikacija <xliff:g id="APP">%s</xliff:g> nije pokrenuta."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"Aplikacija <xliff:g id="APP">%s</xliff:g> onemogućena je u sigurnom načinu."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Izbriši sve"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Povucite ovdje da biste upotrebljavali podijeljeni zaslon"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Podijeli vodoravno"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Podijeli okomito"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Podijeli prilagođeno"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Podijeli zaslon na vrhu"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Podijeli zaslon slijeva"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Podijeli zaslon zdesna"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-hu/strings.xml b/packages/SystemUI/legacy/recents/res/values-hu/strings.xml
deleted file mode 100644
index d0429e76fb40..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-hu/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Áttekintés."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"A(z) <xliff:g id="APP">%s</xliff:g> elvetése."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> eltávolítva."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Az összes alkalmazás eltávolítva a nemrég használtak közül."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"A(z) <xliff:g id="APP">%s</xliff:g> alkalmazás adatainak megnyitása."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"A(z) <xliff:g id="APP">%s</xliff:g> indítása."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Nincsenek mostanában használt elemek"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Mindent törölt"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Az alkalmazás adatai"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"képernyő rögzítése"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"keresés"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Nem lehet elindítani a következőt: <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"A(z) <xliff:g id="APP">%s</xliff:g> csökkentett módban le van tiltva."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Összes törlése"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Húzza ide az osztott képernyő használatához"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Osztott vízszintes"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Osztott függőleges"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Osztott egyéni"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Osztott képernyő felülre"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Osztott képernyő balra"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Osztott képernyő jobbra"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-hy/strings.xml b/packages/SystemUI/legacy/recents/res/values-hy/strings.xml
deleted file mode 100644
index c56b691ed126..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-hy/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Համատեսք:"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Հեռացնել <xliff:g id="APP">%s</xliff:g> հավելվածը ցուցակից:"</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> հավելվածը հեռացվել է ցուցակից:"</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Բոլոր վերջին հավելվածները հեռացվել են ցուցակից:"</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Բացել <xliff:g id="APP">%s</xliff:g> հավելվածի մասին տեղեկությունները"</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> հավելվածը գործարկվում է:"</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Այստեղ դեռ ոչինչ չկա"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Ցուցակը դատարկ է"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Հավելվածի մասին"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"էկրանի ամրացում"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"որոնում"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Չհաջողվեց գործարկել <xliff:g id="APP">%s</xliff:g> հավելվածը:"</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> հավելվածը անվտանգ ռեժիմում անջատված է:"</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Ջնջել բոլորը"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Քաշեք այստեղ՝ էկրանի տրոհումն օգտագործելու համար"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Հորիզոնական տրոհում"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Ուղղահայաց տրոհում"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Հատուկ տրոհում"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Տրոհել էկրանը վերևից"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Տրոհել էկրանը ձախից"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Տրոհել էկրանն աջից"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-in/strings.xml b/packages/SystemUI/legacy/recents/res/values-in/strings.xml
deleted file mode 100644
index aa9dcfe1b197..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-in/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Ringkasan."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Hapus <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> dihapus."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Semua aplikasi yang baru dibuka telah dihapus."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Buka info aplikasi <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Memulai <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Tidak ada item yang baru dibuka"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Anda sudah menghapus semua"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Info Aplikasi"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"pin ke layar"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"telusuri"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Tidak dapat memulai <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> dinonaktifkan dalam mode aman."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Hapus semua"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Tarik ke sini untuk menggunakan layar terpisah"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Pisahkan Horizontal"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Pisahkan Vertikal"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Pisahkan Khusus"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Pisahkan layar ke atas"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Pisahkan layar ke kiri"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Pisahkan layar ke kanan"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-is/strings.xml b/packages/SystemUI/legacy/recents/res/values-is/strings.xml
deleted file mode 100644
index e0a555e63976..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-is/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Yfirlit."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Fjarlægja <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> fjarlægt."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Öll nýleg forrit fjarlægð."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Opna forritsupplýsingar <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Ræsir <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Engin nýleg atriði"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Þú hefur hreinsað allt"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Forritsupplýsingar"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"skjáfesting"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"leita"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Ekki var hægt að ræsa <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"Slökkt er á <xliff:g id="APP">%s</xliff:g> í öruggri stillingu."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Hreinsa allt"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Dragðu hingað til að skipta skjánum"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Lárétt skipting"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Lóðrétt skipting"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Sérsniðin skipting"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Skipta skjá að ofanverðu"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Skipta skjá til vinstri"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Skipta skjá til hægri"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-it/strings.xml b/packages/SystemUI/legacy/recents/res/values-it/strings.xml
deleted file mode 100644
index e04d56038ac1..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-it/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Panoramica."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Elimina <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> eliminata."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Tutte le applicazioni recenti sono state rimosse."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Mostra informazioni sull\'applicazione <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Avvio di <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Nessun elemento recente"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Hai cancellato tutto"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informazioni sull\'applicazione"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"blocco su schermo"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"cerca"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Impossibile avviare <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"L\'app <xliff:g id="APP">%s</xliff:g> è stata disattivata in modalità provvisoria."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Cancella tutto"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Trascina qui per utilizzare la modalità Schermo diviso"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Divisione in orizzontale"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Divisione in verticale"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Divisione personalizzata"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Schermo diviso in alto"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Schermo diviso a sinistra"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Schermo diviso a destra"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-iw/strings.xml b/packages/SystemUI/legacy/recents/res/values-iw/strings.xml
deleted file mode 100644
index a96c709b4be6..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-iw/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"סקירה כללית."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"הסרה של <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> הוסרה."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"כל האפליקציות האחרונות הוסרו."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"פתיחת מידע על האפליקציה <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"מפעיל את <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"אין פריטים אחרונים"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"מחקת הכול"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"מידע על האפליקציה"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"הקפאת מסך"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"חיפוש"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"לא ניתן היה להפעיל את <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> מושבתת במצב בטוח."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"ניקוי הכול"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"יש לגרור לכאן כדי להשתמש במסך מפוצל"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"פיצול אופקי"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"פיצול אנכי"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"פיצול מותאם אישית"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"פיצול מסך למעלה"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"פיצול מסך לשמאל"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"פיצול מסך לימין"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-ja/strings.xml b/packages/SystemUI/legacy/recents/res/values-ja/strings.xml
deleted file mode 100644
index 4d7524c72300..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-ja/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"最近"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g>を削除します。"</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g>を削除しました。"</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"最近のアプリをすべて削除しました。"</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g>のアプリ情報を開きます。"</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g>を開始しています。"</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"最近のアイテムはありません"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"すべてのタスクを削除しました"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"アプリ情報"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"画面固定"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"検索"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g>を開始できませんでした。"</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g>はセーフモードでは無効になります。"</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"すべて消去"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"分割画面を使用するにはここにドラッグします"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"横に分割"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"縦に分割"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"分割(カスタム)"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"画面を上に分割"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"画面を左に分割"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"画面を右に分割"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-ka/strings.xml b/packages/SystemUI/legacy/recents/res/values-ka/strings.xml
deleted file mode 100644
index 088388bfdc70..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-ka/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"მიმოხილვა"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g>-ის დახურვა."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> დაიხურა."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"ყველა ბოლოდროინდელი აპლიკაცია დაიხურა."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> აპლიკაციის ინფორმაციის გახსნა."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"მიმდინარეობს <xliff:g id="APP">%s</xliff:g>-ის გაშვება."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"ბოლოდროინდელი ერთეულები არ არის"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"ყველაფერი გასუფთავდა"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"აპლიკაციის ინფორმაცია"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ეკრანზე ჩამაგრება"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"ძიება"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g>-ის გაშვება ვერ მოხერხდა."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> გათიშულია უსაფრთხო რეჟიმში."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"ყველას გასუფთავება"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"ეკრანის გასაყოფად ჩავლებით გადმოიტანეთ აქ"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"ჰორიზონტალური გაყოფა"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"ვერტიკალური გაყოფა"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"მორგებული გაყოფა"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"ეკრანის გაყოფა ზემოთ"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"ეკრანის გაყოფა მარცხნივ"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"ეკრანის გაყოფა მარჯვნივ"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-kk/strings.xml b/packages/SystemUI/legacy/recents/res/values-kk/strings.xml
deleted file mode 100644
index 9d4e01c138ee..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-kk/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Жалпы ақпарат."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> қолданбасын өшіру."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> өшірілді."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Барлық қолданбалар \"Соңғылар\" тізімінен өшірілді."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> қолданбасы туралы ақпаратты ашу."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> іске қосылды."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Ешқандай соңғы элементтер жоқ"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Барлығын өшірдіңіз"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Қолданба туралы ақпарат"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"экранды бекіту"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"іздеу"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> іске қосылмады."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> қауіпсіз режимде өшіріледі."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Барлығын өшіру"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Экранды бөлу үшін осы жерге сүйреңіз"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Көлденеңінен бөлу"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Тігінен бөлу"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Бөлу (арнаулы)"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Экранды жоғары жағынан бөлу"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Экранды сол жағынан бөлу"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Экранды оң жағынан бөлу"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-km/strings.xml b/packages/SystemUI/legacy/recents/res/values-km/strings.xml
deleted file mode 100644
index b7bfba67bbcf..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-km/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"ទិដ្ឋភាពរួម។"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"ច្រានចោល <xliff:g id="APP">%s</xliff:g> ។"</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"បាន​ច្រានចោល <xliff:g id="APP">%s</xliff:g> ។"</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"បាន​ច្រានចោល​កម្មវិធីថ្មីៗ​ទាំងអស់។"</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"បើក​ព័ត៌មាន​កម្មវិធី <xliff:g id="APP">%s</xliff:g> ។"</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"កំពុង​ចាប់ផ្ដើម <xliff:g id="APP">%s</xliff:g> ។"</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"មិនមានធាតុថ្មីៗទេ"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"អ្នក​បានសម្អាត​អ្វីៗ​គ្រប់យ៉ាង"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"ព័ត៌មាន​កម្មវិធី"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ការ​ភ្ជាប់​អេក្រង់"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"ស្វែង​រក"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"មិន​អាច​ចាប់ផ្ដើម <xliff:g id="APP">%s</xliff:g> បានទេ។"</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> ត្រូវបាន​បិទ​ដំណើរការ​ក្នុងមុខងារ​សុវត្ថិភាព។"</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"សម្អាត​ទាំងអស់"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"អូសនៅទីនេះដើម្បីប្រើអេក្រង់បំបែក"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"បំបែកផ្តេក"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"បំបែកបញ្ឈរ"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"បំបែកផ្ទាល់ខ្លួន"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"បំបែក​អេក្រង់​ទៅ​ខាងលើ"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"បំបែក​អេក្រង់​ទៅ​ខាងឆ្វេង"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"បំបែក​អេក្រង់​ទៅ​ខាងស្តាំ"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-kn/strings.xml b/packages/SystemUI/legacy/recents/res/values-kn/strings.xml
deleted file mode 100644
index 84894c12c7e8..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-kn/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"ಸಮಗ್ರ ನೋಟ."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> ವಜಾಗೊಳಿಸಿ."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"ಇತ್ತೀಚಿನ ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳನ್ನು ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> ಆ್ಯಪ್ ಮಾಹಿತಿ ತೆರೆಯಿರಿ."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"ಯಾವುದೇ ಇತ್ತೀಚಿನ ಐಟಂಗಳಿಲ್ಲ"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"ನೀವು ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿರುವಿರಿ"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"ಆ್ಯಪ್ ಮಾಹಿತಿ"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ಸ್ಕ್ರೀನ್ ಪಿನ್ನಿಂಗ್"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"ಹುಡುಕಾಟ"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> ಪ್ರಾರಂಭಿಸಲು ಸಾದ್ಯವಾಗಲಿಲ್ಲ."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> ಅನ್ನು ಸುರಕ್ಷಿತ ಮೋಡ್‌ನಲ್ಲಿ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿ"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"ವಿಭಜಿತ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಬಳಸಲು ಇಲ್ಲಿ ಡ್ರ್ಯಾಗ್‌ ಮಾಡಿ"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"ಅಡ್ಡಲಾಗಿ ವಿಭಜಿಸಿದ"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"ಲಂಬವಾಗಿ ವಿಭಜಿಸಿದ"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"ಕಸ್ಟಮ್ ವಿಭಜಿಸಿದ"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"ಮೇಲ್ಭಾಗಕ್ಕೆ ಸ್ಕ್ರೀನ್ ಅನ್ನು ವಿಭಜಿಸಿ"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"ಎಡಕ್ಕೆ ಸ್ಕ್ರೀನ್ ಅನ್ನು ವಿಭಜಿಸಿ"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"ಬಲಕ್ಕೆ ಸ್ಕ್ರೀನ್ ಅನ್ನು ವಿಭಜಿಸಿ"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-ko/strings.xml b/packages/SystemUI/legacy/recents/res/values-ko/strings.xml
deleted file mode 100644
index ee856bd21418..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-ko/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"최근 사용"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g>을(를) 닫습니다."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> 애플리케이션을 닫았습니다."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"최근 사용한 애플리케이션을 모두 닫았습니다."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> 애플리케이션 정보를 엽니다."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g>을(를) 시작하는 중입니다."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"최근 항목이 없습니다."</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"모든 항목을 삭제했습니다."</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"애플리케이션 정보"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"화면 고정"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"검색"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g>을(를) 시작할 수 없습니다."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g>은(는) 안전 모드에서 사용 중지됩니다."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"모두 삭제"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"여기를 드래그하여 분할 화면 사용하기"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"수평 분할"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"수직 분할"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"맞춤 분할"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"위쪽으로 화면 분할"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"왼쪽으로 화면 분할"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"오른쪽으로 화면 분할"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-ky/strings.xml b/packages/SystemUI/legacy/recents/res/values-ky/strings.xml
deleted file mode 100644
index 879e492f6330..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-ky/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Сереп салуу."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> колдонмосун өчүрүү."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> колдонмосу өчүрүлдү."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Акыркы колдонмолордун баары өчүрүлдү."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> колдонмосу жөнүндө маалыматты ачыңыз."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> ачылууда."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Акыркы колдонмолор жок"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Баарын тазаладыңыз"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Колдонмо жөнүндө маалымат"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"экран кадоо"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"издөө"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> колдонмосу ачылган жок"</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> коопсуз режиминде өчүрүлдү."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Баарын тазалоо"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Экранды бөлүү үчүн бул жерге сүйрөңүз"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Туурасынан бөлүү"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Тигинен бөлүү"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Ыңгайлаштырылган бөлүү"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Экранды өйдө жакка бөлүү"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Экранды сол жакка бөлүү"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Экранды оң жакка бөлүү"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-lo/strings.xml b/packages/SystemUI/legacy/recents/res/values-lo/strings.xml
deleted file mode 100644
index 17f56b457ad7..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-lo/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"ພາບຮວມ."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"ປິດ <xliff:g id="APP">%s</xliff:g> ໄວ້."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"ປິດ <xliff:g id="APP">%s</xliff:g> ແລ້ວ."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"ທຸກແອັບພລິເຄຊັນບໍ່ດົນມານີ້ຖືກປິດໄວ້ແລ້ວ."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"ເປີດຂໍ້ມູນແອັບພລິເຄຊັນ <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"ກຳລັງເປີດ <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"ບໍ່ມີລາຍການຫຼ້າສຸດ"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"ທ່ານລຶບລ້າງທຸກຢ່າງແລ້ວ"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"ຂໍ້ມູນແອັບພລິເຄຊັນ"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ການປັກໝຸດໜ້າຈໍ"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"ຊອກຫາ"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"ບໍ່ສາມາດເລີ່ມ <xliff:g id="APP">%s</xliff:g> ໄດ້."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> ຖືກປິດໃຊ້ໃນໂໝດຄວາມມປອດໄພ."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"ລຶບລ້າງທັງໝົດ"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"ລາກມາບ່ອນນີ້ເພື່ອໃຊ້ການແບ່ງໜ້າຈໍ"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"ການແຍກລວງຂວາງ"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"ການແຍກລວງຕັ້ງ"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"ການແຍກກຳນົດເອງ"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"ແຍກໜ້າຈໍໄປທາງເທິງ"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"ແຍກໜ້າຈໍໄປທາງຊ້າຍ"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"ແຍກໜ້າຈໍໄປທາງຂວາ"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-lt/strings.xml b/packages/SystemUI/legacy/recents/res/values-lt/strings.xml
deleted file mode 100644
index 4a9eb83bf3d5..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-lt/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Apžvalga."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Atsisakyti programos „<xliff:g id="APP">%s</xliff:g>“."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Atsisakyta programos „<xliff:g id="APP">%s</xliff:g>“."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Atsisakyta visų naujausių programų."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Atidaryti programos „<xliff:g id="APP">%s</xliff:g>“ informaciją."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Paleidžiama programa „<xliff:g id="APP">%s</xliff:g>“."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Nėra jokių naujausių elementų"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Viską išvalėte"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Programos informacija"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ekrano prisegimas"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"ieškoti"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Nepavyko paleisti programos „<xliff:g id="APP">%s</xliff:g>“."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"Programa „<xliff:g id="APP">%s</xliff:g>“ išjungta saugos režimu."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Išvalyti viską"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Vilkite čia, kad naudotumėte skaidytą ekraną"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Horizontalus skaidymas"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Vertikalus skaidymas"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Tinkintas skaidymas"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Skaidyti ekraną į viršų"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Skaidyti ekraną į kairę"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Skaidyti ekraną į dešinę"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-lv/strings.xml b/packages/SystemUI/legacy/recents/res/values-lv/strings.xml
deleted file mode 100644
index 7d87e0033777..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-lv/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Pārskats."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Nerādīt lietotni <xliff:g id="APP">%s</xliff:g>"</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Vairs netiek rādīta lietotne <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Vairs netiek rādīta neviena nesen izmantotā lietojumprogramma."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Atveriet lietojumprogrammas <xliff:g id="APP">%s</xliff:g> informāciju."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Notiek lietotnes <xliff:g id="APP">%s</xliff:g> palaišana."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Nav nesenu vienumu"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Visi uzdevumi ir notīrīti"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Lietojumprogrammas informācija"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"Piespraust ekrānu"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"Meklēt"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Nevarēja palaist lietotni <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"Lietotne <xliff:g id="APP">%s</xliff:g> ir atspējota drošajā režīmā."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Notīrīt visu"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Velciet šeit, lai izmantotu ekrāna sadalīšanu"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Horizontāls dalījums"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Vertikāls dalījums"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Pielāgots dalījums"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Sadalīt ekrānu augšdaļā"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Sadalīt ekrānu kreisajā pusē"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Sadalīt ekrānu labajā pusē"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-mk/strings.xml b/packages/SystemUI/legacy/recents/res/values-mk/strings.xml
deleted file mode 100644
index d8ced0b55310..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-mk/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Преглед."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Отфрлете ја <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> е отфрлена."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Сите неодамнешни апликации се отфрлени."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Отворете информации за апликацијата <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Се стартува <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Нема неодамнешни ставки"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Избришавте сѐ"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Информации за апликацијата"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"прикачување екран"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"пребарувај"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> не можеше да се стартува."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> е оневозможена во безбеден режим."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Избриши сѐ"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Повлечете тука за да користите поделен екран"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Подели хоризонтално"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Подели вертикално"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Подели приспособено"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Подели го екранот во горниот дел"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Подели го екранот на левата страна"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Подели го екранот на десната страна"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-ml/strings.xml b/packages/SystemUI/legacy/recents/res/values-ml/strings.xml
deleted file mode 100644
index 6dd797e450e9..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-ml/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"അവലോകനം."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> ഡിസ്‌മിസ് ചെയ്യുക."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> ഡിസ്‌മിസ് ചെയ്‌തു."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"അടുത്തിടെയുള്ള എല്ലാ ആപ്പുകളും ഡിസ്‌മിസ് ചെയ്‌തു."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> ആപ്പ് വിവരങ്ങൾ തുറക്കുക."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> ആരംഭിക്കുന്നു."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"സമീപകാല ഇനങ്ങൾ ഒന്നുമില്ല"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"നിങ്ങൾ എല്ലാം മായ്ച്ചിരിക്കുന്നു"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"ആപ്പ് വിവരങ്ങൾ"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"സ്ക്രീൻ പിൻ ചെയ്യൽ"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"തിരയുക"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> ആരംഭിക്കാനായില്ല"</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"സുരക്ഷിത മോഡിൽ <xliff:g id="APP">%s</xliff:g> പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"എല്ലാം മായ്‌ക്കുക"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"സ്പ്ലിറ്റ് സ്ക്രീൻ ഉപയോഗിക്കാൻ, ഇവിടെ വലിച്ചിടുക"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"തിരശ്ചീനമായി സ്‌പ്ലിറ്റ് ചെയ്യുക"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"ലംബമായി സ്‌പ്ലിറ്റ് ചെയ്യുക"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"ഇഷ്‌ടാനുസൃതമായി സ്‌പ്ലിറ്റ് ചെയ്യുക"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"സ്ക്രീൻ മുകളിലോട്ട് സ്പ്ലിറ്റ് ചെയ്യുക"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"സ്ക്രീൻ ഇടത്തോട്ട് സ്പ്ലിറ്റ് ചെയ്യുക"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"സ്ക്രീൻ വലത്തോട്ട് സ്‌പ്ലിറ്റ് ചെയ്യുക"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-mn/strings.xml b/packages/SystemUI/legacy/recents/res/values-mn/strings.xml
deleted file mode 100644
index 205f56c233d0..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-mn/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Тойм."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g>-г үл хэрэгсэнэ үү."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g>-г үл хэрэгссэн."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Саяхны бүх аппыг үл хэрэгссэн."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> аппын мэдээллийг нээнэ үү."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g>-г эхлүүлж байна."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Саяхны зүйлс алга"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Та бүгдийг нь устгасан"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Аппын мэдээлэл"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"дэлгэц тогтоох"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"хайх"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g>-г эхлүүлж чадсангүй."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g>-г аюулгүй горимд идэвхгүй болгосон."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Бүгдийг устгах"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Хуваасан дэлгэцийг ашиглахын тулд энд чирэх"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Хэвтээ чиглэлд хуваах"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Босоо чиглэлд хуваах"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Хүссэн хэлбэрээр хуваах"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Дэлгэцийг дээд хэсэгт хуваах"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Дэлгэцийг зүүн хэсэгт хуваах"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Дэлгэцийг баруун хэсэгт хуваах"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-mr/strings.xml b/packages/SystemUI/legacy/recents/res/values-mr/strings.xml
deleted file mode 100644
index 51bce6d41422..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-mr/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"अवलोकन."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> डिसमिस करा."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> डिसमिस केले"</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"अलीकडील सर्व अॅप्लिकेशन डिसमिस झाले."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> अॅप्लिकेशन माहिती उघडा."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> सुरू करत आहे."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"कोणतेही अलीकडील आयटम नाहीत"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"तुम्ही सर्वकाही साफ केले"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"अॅप्लिकेशन माहिती"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"स्‍क्रीन पिन करणे"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"शोधा"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> सुरू करता आले नाही."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> सुरक्षित मोडमध्ये बंद केले आहे."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"सर्व साफ करा"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"स्प्लिट स्क्रीन वापर करण्यासाठी येथे ड्रॅग करा"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"आडवे स्प्लिट करा"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"उभे स्प्लिट करा"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"कस्टम स्प्लिट करा"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"स्क्रीन वर स्प्लिट करा"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"स्क्रीन डावीकडे स्प्लिट करा"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"स्क्रीन उजवीकडे स्प्लिट करा"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-ms/strings.xml b/packages/SystemUI/legacy/recents/res/values-ms/strings.xml
deleted file mode 100644
index ae4461d19b78..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-ms/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Ikhtisar."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Ketepikan <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> diketepikan."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Semua aplikasi terbaharu diketepikan."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Buka maklumat aplikasi <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Memulakan <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Tiada item terbaharu"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Anda telah mengetepikan semua item"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Maklumat Aplikasi"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"penyematan skrin"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"cari"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Tidak dapat memulakan <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> dilumpuhkan dalam mod selamat."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Kosongkan semua"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Seret ke sini untuk menggunakan skrin pisah"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Pisah Mendatar"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Pisah Menegak"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Pisah Tersuai"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Pisahkan skrin ke atas"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Pisahkan skrin ke kiri"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Pisahkan skrin ke kanan"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-my/strings.xml b/packages/SystemUI/legacy/recents/res/values-my/strings.xml
deleted file mode 100644
index 7b5870e1c123..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-my/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"အနှစ်ချုပ်။"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> ကို ပယ်မည်။"</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> ကို ဖယ်ထုတ်ထားသည်။"</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"လတ်တလော အပလီကေးရှင်းအားလုံး ဖယ်ထုတ်ထားသည်။"</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> အပလီကေးရှင်း အချက်အလက်ကို ဖွင့်မည်။"</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> ကို စတင်နေသည်။"</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"လတ်တလော ဖွင့်ထားသည်များ မရှိပါ"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"အားလုံးကို ဖယ်ရှားပြီးပါပြီ"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"အပလီကေးရှင်း အချက်အလက်"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"မျက်နှာပြင် ပင်ထိုးမှု"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"ရှာရန်"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> ကို စတင်၍ မရပါ။"</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"အန္တရာယ်ကင်းမှု စနစ်တွင် <xliff:g id="APP">%s</xliff:g> ကို ပိတ်ထားပါသည်။"</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"အားလုံး ဖယ်ရှားရန်"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းကို အသုံးပြုရန် ဤနေရာသို့ ဖိဆွဲပါ"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"အလျားလိုက် ခွဲရန်"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"ဒေါင်လိုက် ခွဲရန်"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"စိတ်ကြိုက် ခွဲရန်"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"မျက်နှာပြင်ကို အပေါ်သို့ ခွဲရန်"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"မျက်နှာပြင်ကို ဘယ်ဘက်သို့ ခွဲရန်"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"မျက်နှာပြင်ကို ညာဘက်သို့ ခွဲရန်"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-nb/strings.xml b/packages/SystemUI/legacy/recents/res/values-nb/strings.xml
deleted file mode 100644
index 176986ab5132..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-nb/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Oversikt."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Avvis <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> er avvist."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Alle nylig brukte apper er avvist."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Åpne appinformasjonen for <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Starter <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Ingen nylige elementer"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Du har fjernet alt"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Appinformasjon"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"én-appsmodus"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"søk"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Kunne ikke starte <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> er slått av i sikker modus."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Fjern alt"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Dra hit for å bruke delt skjerm"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Del horisontalt"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Del vertikalt"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Del tilpasset"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Delt skjerm øverst"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Delt skjerm til venstre"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Delt skjerm til høyre"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-ne/strings.xml b/packages/SystemUI/legacy/recents/res/values-ne/strings.xml
deleted file mode 100644
index 011383337348..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-ne/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"परिदृश्य।"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> खारेज गर्नुहोस्।"</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> खारेज गरिएको छ।"</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"हालका सबै अनुप्रयोगहरू खारेज गरियो।"</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> अनुप्रयोग सम्बन्धी जानकारी खोल्नुहोस्।"</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> सुरु गर्दै।"</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"हालसालैको कुनै पनि वस्तु छैन"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"तपाईंले सबै कुरा खाली गर्नुभएको छ"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"अनुप्रयोगको जानकारी"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"स्क्रिन पिनिसङ"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"खोज्नुहोस्"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> सुरु गर्न सकिएन।"</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> लाई सुरक्षित मोडमा असक्षम पारिएको छ।"</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"सबै हटाउनुहोस्"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"विभाजित स्क्रिनको प्रयोग गर्न यहाँ तान्नुहोस्"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"तेर्सो रूपमा विभाजन गर्नुहोस्"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"ठाडो रूपमा विभाजन गर्नुहोस्"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"आफू अनुकूल विभाजन गर्नुहोस्"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"विभाजित स्क्रिन शीर्ष स्थानमा राख्नुहोस्‌"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"विभाजित स्क्रिन बायाँतर्फ राख्नुहोस्‌"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"विभाजित स्क्रिन दायाँतर्फ राख्नुहोस्‌"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-nl/strings.xml b/packages/SystemUI/legacy/recents/res/values-nl/strings.xml
deleted file mode 100644
index 97140229319a..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-nl/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Overzicht."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> sluiten."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> verwijderd."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Alle recente apps gesloten."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"App-gegevens voor <xliff:g id="APP">%s</xliff:g> openen."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> starten."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Geen recente items"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Je hebt alles gewist"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"App-informatie"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"scherm vastzetten"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"zoeken"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Kan <xliff:g id="APP">%s</xliff:g> niet starten."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> is uitgeschakeld in de veilige modus"</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Alles wissen"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Sleep hier naartoe om het scherm te splitsen"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Horizontaal splitsen"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Verticaal splitsen"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Aangepast splitsen"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Scherm bovenaan gesplitst"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Scherm links gesplitst"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Scherm rechts gesplitst"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-or/strings.xml b/packages/SystemUI/legacy/recents/res/values-or/strings.xml
deleted file mode 100644
index 7ffcc94194a7..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-or/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"ସଂକ୍ଷିପ୍ତ ବିବରଣୀ"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> ଖାରଜ।"</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> ଖାରଜ କରିଦିଆଗଲା।"</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"ସମସ୍ତ ସମ୍ପ୍ରତି ଆପ୍ଲିକେସନ୍‍ଗୁଡ଼ିକ ଖାରଜ କରାଯାଇଛି।"</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> ଆପ୍ଲିକେସନ୍‍ ସୂଚନା ଖୋଲନ୍ତୁ।"</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> ଆରମ୍ଭ ହେଉଛି।"</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"କୌଣସି ସାମ୍ପ୍ରତିକ ଆଇଟମ୍ ନାହିଁ"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"ଆପଣ ସୁବୁକିଛି ଖାଲି କରିଦେଇଛନ୍ତି"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"ଆପ୍ଲିକେସନ୍‍ ସୂଚନା"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ସ୍କ୍ରିନ୍‌ ଲକ୍‌"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"ଖୋଜନ୍ତୁ"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> କୁ ଆରମ୍ଭ କରାଯାଇପାରିଲା ନାହିଁ।"</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> ସୁରକ୍ଷିତ-ମୋଡ୍‌ରେ ଅକ୍ଷମ ଅଟେ।"</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"ସବୁ ଖାଲି କରନ୍ତୁ"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"ସ୍ପ୍ଲିଟ୍‍ ସ୍କ୍ରିନ୍‍ ବ୍ୟବହାର କରିବା ପାଇଁ ଏଠାକୁ ଡ୍ରାଗ୍‌ କରନ୍ତୁ"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"ଭୂସମାନ୍ତରଭାବରେ ଭାଗ କରନ୍ତୁ"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"ଭୂଲମ୍ବଭାବରେ ଭାଗ କରନ୍ତୁ"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"କଷ୍ଟମ୍‍ କରି ଭାଗ କରନ୍ତୁ"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"ସ୍କ୍ରିନ୍‌କୁ ଉପର ଆଡ଼କୁ ଭାଗ କରନ୍ତୁ"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"ସ୍କ୍ରିନ୍‌କୁ ବାମ ଆଡ଼କୁ ଭାଗ କରନ୍ତୁ"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"ସ୍କ୍ରିନ୍‌କୁ ଡାହାଣ ଆଡ଼କୁ ଭାଗ କରନ୍ତୁ"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-pa/strings.xml b/packages/SystemUI/legacy/recents/res/values-pa/strings.xml
deleted file mode 100644
index 4608561fa1ce..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-pa/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"ਰੂਪ-ਰੇਖਾ।"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> ਨੂੰ ਖਾਰਜ ਕਰੋ।"</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> ਖਾਰਜ ਕੀਤੀ ਗਈ।"</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"ਸਾਰੀਆਂ ਹਾਲੀਆ ਐਪਲੀਕੇਸ਼ਨਾਂ ਖਾਰਜ ਕੀਤੀਆਂ ਗਈਆਂ।"</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> ਐਪਲੀਕੇਸ਼ਨਾਂ ਜਾਣਕਾਰੀ ਖੋਲ੍ਹੋ।"</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> ਚਾਲੂ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ।"</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"ਕੋਈ ਹਾਲੀਆ ਆਈਟਮਾਂ ਨਹੀਂ"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"ਤੁਸੀਂ ਸਭ ਕੁਝ ਸਾਫ਼ ਕਰ ਦਿੱਤਾ ਹੈ"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"ਐਪਲੀਕੇਸ਼ਨ ਜਾਣਕਾਰੀ"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ਸਕ੍ਰੀਨ ਪਿਨਿੰਗ"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"ਖੋਜ"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> ਨੂੰ ਚਾਲੂ ਨਹੀਂ ਕਰ ਸਕੇ।"</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> ਨੂੰ ਸੁਰੱਖਿਅਤ-ਮੋਡ ਵਿੱਚ ਬੰਦ ਕੀਤਾ ਗਿਆ ਹੈ।"</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"ਸਭ ਕਲੀਅਰ ਕਰੋ"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਇੱਥੇ ਘਸੀਟੋ"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"ਲੇਟਵੀਂ ਵੰਡ"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"ਖੜ੍ਹਵੀਂ ਵੰਡ"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"ਵਿਉਂਤੀ ਵੰਡ"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"ਸਕ੍ਰੀਨ ਨੂੰ ਉੱਪਰ ਵੱਲ ਵੰਡੋ"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"ਸਕ੍ਰੀਨ ਨੂੰ ਖੱਬੇ ਪਾਸੇ ਵੰਡੋ"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"ਸਕ੍ਰੀਨ ਨੂੰ ਸੱਜੇ ਪਾਸੇ ਵੰਡੋ"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-pl/strings.xml b/packages/SystemUI/legacy/recents/res/values-pl/strings.xml
deleted file mode 100644
index 50b4ad009490..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-pl/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Przegląd."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Zamknij aplikację <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Aplikacja <xliff:g id="APP">%s</xliff:g> została zamknięta."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Wszystkie ostatnie aplikacje zostały zamknięte."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Otwórz informacje o aplikacji <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Uruchamiam aplikację <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Brak ostatnich elementów"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Wszystko zostało wyczyszczone"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informacje o aplikacji"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"przypinanie ekranu"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"szukaj"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Nie udało się uruchomić aplikacji <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"Aplikacja <xliff:g id="APP">%s</xliff:g> została wyłączona w trybie bezpiecznym."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Wyczyść wszystko"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Przeciągnij tutaj, by podzielić ekran"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Podziel poziomo"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Podziel pionowo"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Podziel niestandardowo"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Podziel ekran u góry"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Podziel ekran z lewej"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Podziel ekran z prawej"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-pt-rBR/strings.xml b/packages/SystemUI/legacy/recents/res/values-pt-rBR/strings.xml
deleted file mode 100644
index b557ad2db67e..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-pt-rBR/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Visão geral."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Dispensar <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> dispensado."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Todos os aplicativos recentes foram dispensados."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Abre informações do aplicativo <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Nenhum item recente"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Você limpou tudo"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informações do aplicativo"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"Fixar tela"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"pesquisar"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Não foi possível iniciar <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"O app <xliff:g id="APP">%s</xliff:g> fica desativado no modo de segurança."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Limpar tudo"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Arraste aqui para usar a tela dividida"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Divisão horizontal"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Divisão vertical"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Divisão personalizada"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Dividir a tela para a parte superior"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Dividir a tela para a esquerda"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Dividir a tela para a direita"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-pt-rPT/strings.xml b/packages/SystemUI/legacy/recents/res/values-pt-rPT/strings.xml
deleted file mode 100644
index e62e1c62034a..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-pt-rPT/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Vista geral."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Ignorar <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Aplicação <xliff:g id="APP">%s</xliff:g> ignorada."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Todas as aplicações recentes foram ignoradas."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Abrir as informações da aplicação <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"A iniciar a aplicação <xliff:g id="APP">%s</xliff:g>…"</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Nenhum item recente"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Limpou tudo"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informações da aplicação"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"afixação no ecrã"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"pesquisar"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Não foi possível iniciar a aplicação <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"A aplicação <xliff:g id="APP">%s</xliff:g> está desativada no modo de segurança."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Limpar tudo"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Arraste aqui para utilizar o ecrã dividido"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Divisão horizontal"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Divisão vertical"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Divisão personalizada"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Ecrã dividido na parte superior"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Ecrã dividido à esquerda"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Ecrã dividido à direita"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-pt/strings.xml b/packages/SystemUI/legacy/recents/res/values-pt/strings.xml
deleted file mode 100644
index b557ad2db67e..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-pt/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Visão geral."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Dispensar <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> dispensado."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Todos os aplicativos recentes foram dispensados."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Abre informações do aplicativo <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Nenhum item recente"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Você limpou tudo"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informações do aplicativo"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"Fixar tela"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"pesquisar"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Não foi possível iniciar <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"O app <xliff:g id="APP">%s</xliff:g> fica desativado no modo de segurança."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Limpar tudo"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Arraste aqui para usar a tela dividida"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Divisão horizontal"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Divisão vertical"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Divisão personalizada"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Dividir a tela para a parte superior"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Dividir a tela para a esquerda"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Dividir a tela para a direita"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-ro/strings.xml b/packages/SystemUI/legacy/recents/res/values-ro/strings.xml
deleted file mode 100644
index 7f8f018be6bc..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-ro/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Recente."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Închideți <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> a fost închisă."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Toate aplicațiile recente au fost închise."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Deschideți informațiile despre aplicația <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Se inițiază <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Niciun element recent"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Ați șters tot"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informații despre aplicație"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"fixare pe ecran"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"căutați"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> nu a putut porni."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"Aplicația <xliff:g id="APP">%s</xliff:g> este dezactivată în modul sigur."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Ștergeți tot"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Trageți aici pentru a folosi ecranul împărțit"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Împărțiți pe orizontală"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Împărțiți pe verticală"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Împărțiți personalizat"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Împărțiți ecranul în partea de sus"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Împărțiți ecranul la stânga"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Împărțiți ecranul la dreapta"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-ru/strings.xml b/packages/SystemUI/legacy/recents/res/values-ru/strings.xml
deleted file mode 100644
index 1e988bb21502..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-ru/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Обзор."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Удалить приложение <xliff:g id="APP">%s</xliff:g> из списка."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Приложение <xliff:g id="APP">%s</xliff:g> удалено из списка."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Все недавние приложения удалены из списка."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Открыть информацию о приложении <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Запуск приложения <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Здесь пока ничего нет."</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Список пуст."</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Сведения о приложении"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"блокировка в приложении"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"поиск"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Не удалось запустить приложение \"<xliff:g id="APP">%s</xliff:g>\"."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" отключено в безопасном режиме."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Удалить все"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Перетащите сюда, чтобы разделить экран"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Разделить по горизонтали"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Разделить по вертикали"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Разделить по-другому"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Разделить экран по верхнему краю"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Разделить экран по левому краю"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Разделить экран по правому краю"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-si/strings.xml b/packages/SystemUI/legacy/recents/res/values-si/strings.xml
deleted file mode 100644
index cae835740254..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-si/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"දළ විශ්ලේෂණය."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> ඉවත ලන්න."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> ඉවත දමා ඇත."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"සියලුම මෑත යෙඳුම් ඉවත ලන ලදී."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> යෙදුම් තොරතුරු විවෘත කරයි."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> ආරම්භ කරමින්."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"මෑත අයිතම නැත"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"ඔබ සියලු දේ හිස් කර ඇත"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"යෙදුම් තොරතුරු"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"තිර ඇමිණීම"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"සෙවීම"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> ආරම්භ කළ නොහැකි විය."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> ආරක්ෂිත ප්‍රකාරය තුළ අබලයි."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"සියල්ල හිස් කරන්න"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"බෙදුම් තිරය භාවිත කිරීමට මෙතැනට අදින්න"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"තිරස්ව වෙන් කරන්න"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"සිරස්ව වෙන් කරන්න"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"අභිමත ලෙස වෙන් කරන්න"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"තිරය ඉහළට බෙදන්න"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"තිරය වමට බෙදන්න"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"තිරය දකුණට බෙදන්න"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-sk/strings.xml b/packages/SystemUI/legacy/recents/res/values-sk/strings.xml
deleted file mode 100644
index 9c3a8570d75e..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-sk/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Prehľad"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Zavrieť aplikáciu <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Aplikácia <xliff:g id="APP">%s</xliff:g> bola zrušená."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Všetky nedávne aplikácie boli zrušené."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Otvoriť informácie o aplikácii <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Spúšťa sa aplikácia <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Žiadne nedávne položky"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Vymazali ste všetko"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informácie o aplikácii"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"pripnutie obrazovky"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"hľadať"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Aplikáciu <xliff:g id="APP">%s</xliff:g> sa nepodarilo spustiť."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"Aplikácia <xliff:g id="APP">%s</xliff:g> je v núdzovom režime zakázaná."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Vymazať všetko"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Presuňte okno sem a použite tak rozdelenú obrazovku"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Rozdeliť vodorovné"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Rozdeliť zvislé"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Rozdeliť vlastné"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Rozdelená obrazovka hore"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Rozdelená obrazovka naľavo"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Rozdelená obrazovka napravo"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-sl/strings.xml b/packages/SystemUI/legacy/recents/res/values-sl/strings.xml
deleted file mode 100644
index 56b2ddb63314..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-sl/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Pregled."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Opustitev aplikacije <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Aplikacija <xliff:g id="APP">%s</xliff:g> je bila odstranjena."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Vse nedavne aplikacije so bile opuščene."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Odpiranje podatkov o aplikaciji <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Zaganjanje aplikacije <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Ni nedavnih elementov"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Vse ste počistili"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Podatki o aplikaciji"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"pripenjanje zaslona"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"išči"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Aplikacije <xliff:g id="APP">%s</xliff:g> ni bilo mogoče zagnati."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"Aplikacija <xliff:g id="APP">%s</xliff:g> je v varnem načinu onemogočena."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Počisti vse"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Povlecite sem za razdeljeni zaslon"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Razdeli vodoravno"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Razdeli navpično"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Razdeli po meri"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Razdeljen zaslon na vrhu"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Razdeljen zaslon na levi"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Razdeljen zaslon na desni"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-sq/strings.xml b/packages/SystemUI/legacy/recents/res/values-sq/strings.xml
deleted file mode 100644
index 48aab371087c..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-sq/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Përmbledhja."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Largo <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> është hequr."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Të gjitha aplikacionet e fundit u larguan."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Hap informacionin e aplikacionit <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Po nis <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Nuk ka asnjë artikull të fundit"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"I ke pastruar të gjitha"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Informacioni i aplikacionit"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"kyçja e ekranit"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"kërko"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> nuk mund të nisej."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> është i çaktivizuar në modalitetin e sigurt."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Pastroji të gjitha"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Zvarrit këtu për të përdorur ekranin e ndarë"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Horizontal i ndarë"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Vertikal i ndarë"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"I personalizuar i ndarë"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Ndaje ekranin lart"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Ndaje ekranin në të majtë"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Ndaje ekranin në të djathtë"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-sr/strings.xml b/packages/SystemUI/legacy/recents/res/values-sr/strings.xml
deleted file mode 100644
index 9d5f126181bd..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-sr/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Преглед."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Одбаците апликацију <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Апликација <xliff:g id="APP">%s</xliff:g> је одбачена."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Све недавно коришћене апликације су одбачене."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Отворите информације о апликацији <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Покреће се <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Нема недавних ставки"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Обрисали сте све"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Информације о апликацији"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"качење екрана"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"претражи"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Покретање апликације <xliff:g id="APP">%s</xliff:g> није успело."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"Апликација <xliff:g id="APP">%s</xliff:g> је онемогућена у безбедном режиму."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Обриши све"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Превуците овде да бисте користили раздељени екран"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Подели хоризонтално"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Подели вертикално"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Подели прилагођено"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Подели екран нагоре"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Подели екран налево"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Подели екран надесно"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-sv/strings.xml b/packages/SystemUI/legacy/recents/res/values-sv/strings.xml
deleted file mode 100644
index b2ee34fbd284..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-sv/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Översikt."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Ta bort <xliff:g id="APP">%s</xliff:g> från listan."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> togs bort från listan."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Alla appar har tagits bort från listan Senaste."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Öppna appinformation för <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Startar <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Listan är tom"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Du har tömt listan"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Appinformation"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"fästa skärmen"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"sök"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Det gick inte att starta appen <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> är inaktiverad i säkert läge."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Rensa alla"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Dra hit för att dela upp skärmen"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Dela vågrätt"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Dela lodrätt"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Dela anpassat"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Delad skärm till överkanten"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Delad skärm åt vänster"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Delad skärm åt höger"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-sw/strings.xml b/packages/SystemUI/legacy/recents/res/values-sw/strings.xml
deleted file mode 100644
index 49e7fb80e435..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-sw/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Muhtasari."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Ondoa <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> imeondolewa."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Programu za hivi majuzi zimeondolewa."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Fungua maelezo kuhusu programu ya <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Inaanzisha <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Hakuna vipengee vya hivi majuzi"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Umeondoa vipengee vyote"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Maelezo ya Programu"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"kubandika kwenye skirini"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"tafuta"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Imeshindwa kuanzisha <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> imezimwa katika hali salama."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Ondoa zote"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Buruta hapa ili utumie skrini iliyogawanywa"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Gawanya Mlalo"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Gawanya Wima"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Maalum Iliyogawanywa"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Gawa skrini kuelekea juu"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Gawa skrini upande wa kushoto"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Gawa skrini upande wa kulia"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-sw600dp/dimens.xml b/packages/SystemUI/legacy/recents/res/values-sw600dp/dimens.xml
deleted file mode 100644
index 20d6670cb5b9..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-sw600dp/dimens.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (c) 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
-*/
--->
-<resources>
- <!-- The offsets the tasks animate from when recents is launched while docking -->
- <dimen name="recents_task_stack_animation_launched_while_docking_offset">192dp</dimen>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-ta/strings.xml b/packages/SystemUI/legacy/recents/res/values-ta/strings.xml
deleted file mode 100644
index 91643fd01bdf..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-ta/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"மேலோட்டப் பார்வை."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> ஆப்ஸை அகற்றும்."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> அகற்றப்பட்டது."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"அனைத்துச் சமீபத்திய ஆப்ஸும் அகற்றப்பட்டன."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> ஆப்ஸ் பற்றிய தகவலைத் திறக்கும்."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> ஆப்ஸைத் தொடங்குகிறது."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"சமீபத்தியவை எதுவுமில்லை"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"அனைத்தையும் அழித்துவிட்டீர்கள்"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"ஆப்ஸ் பற்றிய தகவல்"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"திரையைப் பின் செய்"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"தேடு"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> ஆப்ஸைத் தொடங்க இயலவில்லை."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"பாதுகாப்புப் பயன்முறையில் <xliff:g id="APP">%s</xliff:g> முடக்கப்பட்டது."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"அனைத்தையும் அழி"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"\'திரைப் பிரிப்பைப்\' பயன்படுத்த இங்கே இழுக்கவும்"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"கிடைமட்டமாகப் பிரி"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"செங்குத்தாகப் பிரி"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"தனிப்பயன் விருப்பத்தில் பிரி"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"திரையை மேற்புறமாகப் பிரிக்கும்"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"திரையை இடப்புறமாகப் பிரிக்கும்"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"திரையை வலப்புறமாகப் பிரிக்கும்"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-te/strings.xml b/packages/SystemUI/legacy/recents/res/values-te/strings.xml
deleted file mode 100644
index ea4e638aa68c..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-te/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"అవలోకనం."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g>ని తీసివేయండి."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> తీసివేయబడింది."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"అన్ని ఇటీవలి యాప్‌లు తీసివేయబడ్డాయి."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> యాప్ సమాచారాన్ని తెరుస్తుంది."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g>ని ప్రారంభిస్తోంది."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"ఇటీవలి అంశాలు ఏవీ లేవు"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"మీరు అన్నింటినీ తీసివేసారు"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"యాప్ సమాచారం"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"స్క్రీన్‌కు పిన్ చేయడం"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"వెతుకు"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g>ని ప్రారంభించడం సాధ్యపడలేదు."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> సురక్షిత-మోడ్‌లో నిలిపివేయబడింది."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"అన్నీ తీసివేయి"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"విభజన స్క్రీన్‌ను ఉపయోగించడానికి ఇక్కడ లాగండి"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"అడ్డంగా విభజించు"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"నిలువుగా విభజించు"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"అనుకూలంగా విభజించు"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"స్క్రీన్‌ని ఎగువకు విభజించు"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"స్క్రీన్‌ని ఎడమ వైపుకి విభజించు"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"స్క్రీన్‌ని కుడి వైపుకి విభజించు"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-th/strings.xml b/packages/SystemUI/legacy/recents/res/values-th/strings.xml
deleted file mode 100644
index b88d05eddace..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-th/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"ภาพรวม"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"ยกเลิก <xliff:g id="APP">%s</xliff:g>"</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> ถูกนำออกไปแล้ว"</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"ปิดแอปพลิเคชันล่าสุดทั้งหมดแล้ว"</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"เปิดข้อมูลแอปพลิเคชัน <xliff:g id="APP">%s</xliff:g>"</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"กำลังเริ่มต้น <xliff:g id="APP">%s</xliff:g>"</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"ไม่มีรายการล่าสุด"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"คุณได้ล้างทุกอย่างแล้ว"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"ข้อมูลแอปพลิเคชัน"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"การตรึงหน้าจอ"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"ค้นหา"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"เริ่มใช้ <xliff:g id="APP">%s</xliff:g> ไม่ได้"</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> ปิดใช้ในโหมดปลอดภัย"</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"ล้างทั้งหมด"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"ลากมาที่นี่เพื่อใช้การแยกหน้าจอ"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"แยกในแนวนอน"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"แยกในแนวตั้ง"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"แยกแบบกำหนดเอง"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"แยกหน้าจอไปด้านบน"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"แยกหน้าจอไปทางซ้าย"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"แยกหน้าจอไปทางขวา"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-tl/strings.xml b/packages/SystemUI/legacy/recents/res/values-tl/strings.xml
deleted file mode 100644
index d940d4e5663b..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-tl/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Overview"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"I-dismiss ang <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Na-dismiss ang <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Na-dismiss ang lahat ng kamakailang application."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Buksan ang impormasyon ng <xliff:g id="APP">%s</xliff:g> application."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Sinisimulan ang <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Walang kamakailang item"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Na-clear mo ang lahat"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Impormasyon ng Application"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"pag-pin sa screen"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"hanapin"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Hindi masimulan ang <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"Naka-disable ang <xliff:g id="APP">%s</xliff:g> sa safe-mode."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"I-clear lahat"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"I-drag dito para magamit ang split screen"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"I-split Pahalang"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"I-split Patayo"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Split Custom"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"I-split ang screen pataas"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"I-split ang screen pakaliwa"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"I-split ang screen pakanan"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-tr/strings.xml b/packages/SystemUI/legacy/recents/res/values-tr/strings.xml
deleted file mode 100644
index 982c57ef79ac..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-tr/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Genel Bakış."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> uygulamasını kapatır."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> kaldırıldı."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Tüm son uygulamalar kapatıldı."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> uygulama bilgilerini açar."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> başlatılıyor."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Yeni öğe yok"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Her şeyi sildiniz"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Uygulama Bilgileri"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ekran sabitleme"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"ara"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> başlatılamadı."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g>, güvenli modda devre dışıdır."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Tümünü temizle"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Ekranı bölünmüş olarak kullanmak için buraya sürükleyin"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Yatay Ayırma"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Dikey Ayırma"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Özel Ayırma"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Ekranı yukarıya doğru böl"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Ekranı sola doğru böl"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Ekranı sağa doğru böl"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-uk/strings.xml b/packages/SystemUI/legacy/recents/res/values-uk/strings.xml
deleted file mode 100644
index 0c0b70927d1e..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-uk/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Огляд."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Закрити додаток <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Додаток <xliff:g id="APP">%s</xliff:g> закрито."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Усі останні додатки закрито."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Відкрити інформацію про додаток <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Запуск додатка <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Немає останніх елементів"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Ви очистили все"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Інформація про додаток"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"закріпити екран"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"пошук"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Не вдалося запустити додаток <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"Додаток <xliff:g id="APP">%s</xliff:g> вимкнено в безпечному режимі."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Очистити все"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Перетягніть сюди, щоб розділити екран"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Розділити горизонтально"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Розділити вертикально"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Розділити (власний варіант)"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Розділити екран угору"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Розділити екран уліво"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Розділити екран управо"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-ur/strings.xml b/packages/SystemUI/legacy/recents/res/values-ur/strings.xml
deleted file mode 100644
index 46033daebe09..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-ur/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"عمومی جائزہ۔"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"<xliff:g id="APP">%s</xliff:g> کو مسترد کریں۔"</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> کو مسترد کر دیا گیا۔"</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"سبھی حالیہ ایپلیکیشنز کو مسترد کر دیا گیا۔"</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> ایپلیکیشن کی معلومات کھولیں۔"</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> شروع ہو رہی ہے۔"</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"کوئی حالیہ آئٹم نہیں"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"آپ نے سب کچھ صاف کر دیا ہے"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"ایپلیکیشن کی معلومات"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"اسکرین کو پن کرنا"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"تلاش کریں"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> کو شروع نہیں کیا جا سکا۔"</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"محفوظ موڈ میں <xliff:g id="APP">%s</xliff:g> غیر فعال ہے۔"</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"سبھی کو ہٹائیں"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"اسپلٹ اسکرین استعمال کرنے کے لیے یہاں گھسیٹیں"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"بلحاظ افقی تقسیم کریں"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"بلحاظ عمودی تقسیم کریں"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"بلحاظ حسب ضرورت تقسیم کریں"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"اسکرین کو اوپر کی جانب تقسیم کریں"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"اسکرین کو بائیں جانب تقسیم کریں"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"اسکرین کو دائیں جانب تقسیم کریں"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-uz/strings.xml b/packages/SystemUI/legacy/recents/res/values-uz/strings.xml
deleted file mode 100644
index 6f8b153b4b5b..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-uz/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Umumiy nazar."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Olib tashlash: <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"<xliff:g id="APP">%s</xliff:g> olib tashlangan."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Yaqinda ishlatilgan barcha ilovalar olib tashlandi."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"<xliff:g id="APP">%s</xliff:g> ilovasi haqidagi axborotlarni ochadi."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"<xliff:g id="APP">%s</xliff:g> ishga tushirilmoqda."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Yaqinda ishlatilgan ilovalar yoʻq"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Hammasi tozalandi"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Ilova haqida axborot"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ekranni mahkamlash"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"qidiruv"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"<xliff:g id="APP">%s</xliff:g> ilovasi ishga tushmadi."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"Xavfsiz rejimda <xliff:g id="APP">%s</xliff:g> ilovasi yopildi."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Ha"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Ekranni boʻlish xususiyatidan foydalanish uchun bu yerga torting"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Gorizontal yoʻnalishda boʻlish"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Vertikal yoʻnalishda boʻlish"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Boshqa usulda boʻlish"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Ekranni tepaga qadash"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Ekranni chap tomonga qadash"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Ekranni oʻng tomonga qadash"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-vi/strings.xml b/packages/SystemUI/legacy/recents/res/values-vi/strings.xml
deleted file mode 100644
index aefeae92a8a4..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-vi/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Tổng quan."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Loại bỏ <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"Đã loại bỏ <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Đã loại bỏ tất cả các ứng dụng gần đây."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Mở thông tin ứng dụng <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Khởi động <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Không có mục gần đây nào"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Bạn đã xóa mọi nội dung"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Thông tin ứng dụng"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"khóa màn hình"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"tìm kiếm"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Không thể khởi động <xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g> bị tắt ở chế độ an toàn."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Xóa tất cả"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Kéo vào đây để sử dụng chế độ chia đôi màn hình"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Chia ngang"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Phân tách dọc"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Tùy chỉnh phân tách"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Chia đôi màn hình lên trên"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Chia đôi màn hình sang trái"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Chia đôi màn hình sang phải"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-zh-rCN/strings.xml b/packages/SystemUI/legacy/recents/res/values-zh-rCN/strings.xml
deleted file mode 100644
index 993bfaea9637..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-zh-rCN/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"概览。"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"移除<xliff:g id="APP">%s</xliff:g>。"</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"已移除<xliff:g id="APP">%s</xliff:g>"</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"已关闭所有最近用过的应用。"</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"打开<xliff:g id="APP">%s</xliff:g>应用信息。"</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"正在启动<xliff:g id="APP">%s</xliff:g>。"</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"近期没有任何内容"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"您已清除所有内容"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"应用信息"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"固定屏幕"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"搜索"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"无法启动<xliff:g id="APP">%s</xliff:g>。"</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"<xliff:g id="APP">%s</xliff:g>已在安全模式下停用。"</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"全部清除"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"拖动到此处即可使用分屏功能"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"水平分割"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"垂直分割"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"自定义分割"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"将屏幕分隔线移到上方"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"将屏幕分隔线移到左侧"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"将屏幕分隔线移到右侧"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-zh-rHK/strings.xml b/packages/SystemUI/legacy/recents/res/values-zh-rHK/strings.xml
deleted file mode 100644
index b93d246255cc..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-zh-rHK/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"概覽。"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"關閉「<xliff:g id="APP">%s</xliff:g>」。"</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"「<xliff:g id="APP">%s</xliff:g>」已關閉。"</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"所有最近使用的應用程式均已關閉。"</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"開啟「<xliff:g id="APP">%s</xliff:g>」應用程式的資料。"</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"正在啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"最近沒有任何項目"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"您已清除所有工作"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"應用程式資料"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"螢幕固定"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"搜尋"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"無法啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"「<xliff:g id="APP">%s</xliff:g>」在安全模式下為停用狀態。"</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"全部清除"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"拖曳這裡即可分割螢幕"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"水平分割"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"垂直分割"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"自訂分割"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"將分割畫面顯示喺頂部"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"將分割畫面顯示喺左邊"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"將分割畫面顯示喺右邊"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-zh-rTW/strings.xml b/packages/SystemUI/legacy/recents/res/values-zh-rTW/strings.xml
deleted file mode 100644
index 54d656dd63b8..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-zh-rTW/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"總覽。"</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"關閉「<xliff:g id="APP">%s</xliff:g>」。"</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"「<xliff:g id="APP">%s</xliff:g>」已關閉。"</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"最近使用的應用程式已全部關閉。"</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"開啟「<xliff:g id="APP">%s</xliff:g>」應用程式資訊。"</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"正在啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"最近沒有任何項目"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"你已清除所有工作"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"應用程式資訊"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"螢幕固定"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"搜尋"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"無法啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"「<xliff:g id="APP">%s</xliff:g>」在安全模式中為停用狀態。"</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"全部清除"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"拖曳到這裡即可使用分割畫面"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"水平分割"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"垂直分割"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"自訂分割"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"將分割畫面顯示在頂端"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"將分割畫面顯示在左邊"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"將分割畫面顯示在右邊"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values-zu/strings.xml b/packages/SystemUI/legacy/recents/res/values-zu/strings.xml
deleted file mode 100644
index 9cbc439a84d0..000000000000
--- a/packages/SystemUI/legacy/recents/res/values-zu/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_desc_recent_apps" msgid="2427210347871321373">"Buka konke."</string>
- <string name="accessibility_recents_item_will_be_dismissed" msgid="2355882496933479534">"Cashisa i-<xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_dismissed" msgid="4816790842084268400">"I-<xliff:g id="APP">%s</xliff:g> icashisiwe."</string>
- <string name="accessibility_recents_all_items_dismissed" msgid="5693205751863608046">"Zonke izinhlelo zokusebenza zakamuva zicashisiwe."</string>
- <string name="accessibility_recents_item_open_app_info" msgid="3406797323476801016">"Vula ulwazi lohlelo lokusebenza le-<xliff:g id="APP">%s</xliff:g>."</string>
- <string name="accessibility_recents_item_launched" msgid="4519918148638221791">"Iqala i-<xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_empty_message" msgid="7967713254531861311">"Azikho izinto zakamuva"</string>
- <string name="recents_empty_message_dismissed_all" msgid="1850214584987361375">"Usule yonke into"</string>
- <string name="recents_app_info_button_label" msgid="8732926607391786762">"Ulwazi lohlelo lokusebenza"</string>
- <string name="recents_lock_to_app_button_label" msgid="6087750201863853365">"ukuphina isikrini"</string>
- <string name="recents_search_bar_label" msgid="638132045925945941">"sesha"</string>
- <string name="recents_launch_error_message" msgid="9107963563503438012">"Ayikwazanga ukuqalisa i-<xliff:g id="APP">%s</xliff:g>."</string>
- <string name="recents_launch_disabled_message" msgid="826461671965217243">"I-<xliff:g id="APP">%s</xliff:g> ikhutshaziwe kumodi yokuphepha."</string>
- <string name="recents_stack_action_button_label" msgid="1974273390109881497">"Sula konke"</string>
- <string name="recents_drag_hint_message" msgid="610417221848280136">"Hudulela lapha ukuze usebenzise ukuhlukanisa kwesikrini"</string>
- <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="488987777874979435">"Hlukanisa ngokuvundlile"</string>
- <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="2498375296906391117">"Hlukanisa ngokumile"</string>
- <string name="recents_multistack_add_stack_dialog_split_custom" msgid="7368405969130304811">"Hlukanisa ngokwezifiso"</string>
- <string name="recents_accessibility_split_screen_top" msgid="8773505308411722524">"Hlukanisela isikrini phezulu"</string>
- <string name="recents_accessibility_split_screen_left" msgid="722594718192007972">"Hlukanisela isikrini ngakwesokunxele"</string>
- <string name="recents_accessibility_split_screen_right" msgid="2479764030969301514">"Hlukanisela isikrini ngakwesokudla"</string>
-</resources>
diff --git a/packages/SystemUI/legacy/recents/res/values/attrs.xml b/packages/SystemUI/legacy/recents/res/values/attrs.xml
deleted file mode 100644
index ef4cd5b959de..000000000000
--- a/packages/SystemUI/legacy/recents/res/values/attrs.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<resources>
-
- <declare-styleable name="RecentsPanelView">
- <attr name="recentItemLayout" format="reference" />
- <!-- Style for the "Clear all" button. -->
- <attr name="clearAllStyle" format="reference" />
- <attr name="clearAllBackgroundColor" format="reference" />
- </declare-styleable>
-
-</resources> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/values/colors.xml b/packages/SystemUI/legacy/recents/res/values/colors.xml
deleted file mode 100644
index 88b9b70956ef..000000000000
--- a/packages/SystemUI/legacy/recents/res/values/colors.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright 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.
- */
--->
-<resources>
- <!-- The disabled recents task bar background color. -->
- <color name="recents_task_bar_disabled_background_color">#ff676767</color>
- <!-- The default recents task bar background color. -->
- <color name="recents_task_bar_default_background_color">#ffe6e6e6</color>
- <!-- The default recents task view background color. -->
- <color name="recents_task_view_default_background_color">#fff3f3f3</color>
- <!-- The recents task bar light text color to be drawn on top of dark backgrounds. -->
- <color name="recents_task_bar_light_text_color">#ffeeeeee</color>
- <!-- The recents task bar dark text color to be drawn on top of light backgrounds. -->
- <color name="recents_task_bar_dark_text_color">#cc000000</color>
- <!-- The recents task bar light dismiss icon color to be drawn on top of dark backgrounds. -->
- <color name="recents_task_bar_light_icon_color">#ccffffff</color>
- <!-- The recents task bar dark dismiss icon color to be drawn on top of light backgrounds. -->
- <color name="recents_task_bar_dark_icon_color">#99000000</color>
- <!-- The lock to task button background color. -->
- <color name="recents_task_view_lock_to_app_button_background_color">#ffe6e6e6</color>
- <!-- The lock to task button foreground color. -->
- <color name="recents_task_view_lock_to_app_button_color">#ff666666</color>
- <!-- The background color for the freeform workspace. -->
- <color name="recents_freeform_workspace_bg_color">#33FFFFFF</color>
-
- <!-- The background color for clear all button on light backgrounds if not transparent. -->
- <color name="recents_clear_all_button_bg_light_color">#CCFFFFFF</color>
- <!-- The background color for clear all button on dark backgrounds if not transparent. -->
- <color name="recents_clear_all_button_bg_dark_color">#CC000000</color>
-
- <!-- Shadow color for the first pixels around the fake shadow for recents. -->
- <color name="fake_shadow_start_color">#44000000</color>
-
- <!-- Shadow color for the furthest pixels around the fake shadow for recents. -->
- <color name="fake_shadow_end_color">#03000000</color>
-</resources> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/values/config.xml b/packages/SystemUI/legacy/recents/res/values/config.xml
deleted file mode 100644
index 2ff9abf4003d..000000000000
--- a/packages/SystemUI/legacy/recents/res/values/config.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
- for different hardware and product builds. -->
-<resources>
-
- <!-- Component to be used as the recents implementation. Must implement the
- RecentsImplementation interface. This name is in the ComponentName flattened format
- (package/class) -->
- <string name="config_recentsComponent" translatable="false">com.android.systemui.recents.LegacyRecentsImpl</string>
-
- <!-- Whether recents should use hardware layers for its taskviews. This flag can be enabled
- for devices where the java drawing of round rects may be slow -->
- <bool name="config_recents_use_hardware_layers">false</bool>
-
- <!-- The number of app thumbnails we keep in memory -->
- <integer name="config_recents_max_thumbnail_count">10</integer>
-
- <!-- The number of app icons we keep in memory -->
- <integer name="config_recents_max_icon_count">20</integer>
-
- <!-- Whether to use cheap, less good looking shadows for recents -->
- <bool name="config_recents_fake_shadows">false</bool>
-
- <!-- The duration in seconds to wait before the dismiss buttons are shown. -->
- <integer name="recents_task_bar_dismiss_delay_seconds">1000</integer>
-
- <!-- The duration for animating the task decorations in after transitioning from an app. -->
- <integer name="recents_task_enter_from_app_duration">200</integer>
-
- <!-- The duration for animating the task decorations in after transitioning from an app. -->
- <integer name="recents_task_enter_from_affiliated_app_duration">125</integer>
-
- <!-- The duration for animating the task decorations out before transitioning to an app. -->
- <integer name="recents_task_exit_to_app_duration">125</integer>
-
- <!-- The min animation duration for animating the nav bar scrim in. -->
- <integer name="recents_nav_bar_scrim_enter_duration">400</integer>
-
- <!-- The animation duration for scrolling the stack to a particular item. -->
- <integer name="recents_animate_task_stack_scroll_duration">200</integer>
-
- <!-- The delay to enforce between each alt-tab key press. -->
- <integer name="recents_alt_tab_key_delay">200</integer>
-
- <!-- Svelte specific logic, see RecentsConfiguration.SVELTE_* constants. -->
- <integer name="recents_svelte_level">0</integer>
-
- <!-- Recents: The relative range of visible tasks from the current scroll position
- while the stack is focused. -->
- <item name="recents_layout_focused_range_min" format="float" type="integer">-3</item>
- <item name="recents_layout_focused_range_max" format="float" type="integer">2</item>
-
- <!-- Recents: The relative range of visible tasks from the current scroll position
- while the stack is not focused. -->
- <item name="recents_layout_unfocused_range_min" format="float" type="integer">-2</item>
- <item name="recents_layout_unfocused_range_max" format="float" type="integer">2.5</item>
-</resources> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/values/dimens.xml b/packages/SystemUI/legacy/recents/res/values/dimens.xml
deleted file mode 100644
index 528610e4c990..000000000000
--- a/packages/SystemUI/legacy/recents/res/values/dimens.xml
+++ /dev/null
@@ -1,110 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (c) 2006, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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>
-<!-- Recents Layout -->
-
- <!-- The amount to inset the stack, specifically at the top and the other sides. We also
- don't want this to change across configurations that Recents can be opened in, so we
- define them statically for all display sizes. -->
- <dimen name="recents_layout_min_margin">16dp</dimen>
- <dimen name="recents_layout_top_margin_phone">16dp</dimen>
- <dimen name="recents_layout_top_margin_tablet">32dp</dimen>
- <dimen name="recents_layout_top_margin_tablet_xlarge">40dp</dimen>
- <dimen name="recents_layout_bottom_margin">16dp</dimen>
- <dimen name="recents_layout_side_margin_phone">16dp</dimen>
- <dimen name="recents_layout_side_margin_tablet">48dp</dimen>
- <dimen name="recents_layout_side_margin_tablet_docked">16dp</dimen>
- <dimen name="recents_layout_side_margin_tablet_xlarge">64dp</dimen>
- <dimen name="recents_layout_side_margin_tablet_xlarge_docked">16dp</dimen>
-
- <!-- The height between the top margin and the top of the focused task. -->
- <dimen name="recents_layout_top_peek_size">48dp</dimen>
- <!-- The height between the bottom margin and the top of task in front of the focused task. -->
- <dimen name="recents_layout_bottom_peek_size">56dp</dimen>
-
- <!-- The offset from the top and bottom of the stack of the focused task. The bottom offset
- will be additionally offset by the bottom system insets since it goes under the nav bar
- in certain orientations. -->
- <dimen name="recents_layout_initial_top_offset_phone_port">128dp</dimen>
- <dimen name="recents_layout_initial_bottom_offset_phone_port">80dp</dimen>
- <dimen name="recents_layout_initial_top_offset_phone_land">72dp</dimen>
- <dimen name="recents_layout_initial_bottom_offset_phone_land">72dp</dimen>
- <dimen name="recents_layout_initial_top_offset_tablet">160dp</dimen>
- <dimen name="recents_layout_initial_bottom_offset_tablet">112dp</dimen>
-
- <!-- The min/max translationZ for the tasks in the stack. -->
- <dimen name="recents_layout_z_min">3dp</dimen>
- <dimen name="recents_layout_z_max">24dp</dimen>
-
- <!-- The margin between the freeform and stack. We also don't want this to change across
- configurations that Recents can be opened in, so we define them statically for all
- display sizes. -->
- <dimen name="recents_freeform_layout_bottom_margin">16dp</dimen>
-
- <!-- The padding between each freeform task. -->
- <dimen name="recents_freeform_layout_task_padding">8dp</dimen>
-
-<!-- Recents Views -->
-
- <!-- The height of a task view bar. This has to be large enough to cover the action bar
- height in either orientation at this smallest width. -->
- <dimen name="recents_task_view_header_height">56dp</dimen>
- <dimen name="recents_task_view_header_height_tablet_land">64dp</dimen>
-
- <!-- The padding of a button in the recents task view header. -->
- <dimen name="recents_task_view_header_button_padding">16dp</dimen>
- <dimen name="recents_task_view_header_button_padding_tablet_land">20dp</dimen>
-
- <!-- The radius of the rounded corners on a task view and its shadow (which can be larger
- to create a softer corner effect. -->
- <dimen name="recents_task_view_rounded_corners_radius">2dp</dimen>
- <dimen name="recents_task_view_shadow_rounded_corners_radius">6dp</dimen>
-
- <!-- The amount of highlight to make on each task view. -->
- <dimen name="recents_task_view_highlight">1dp</dimen>
-
- <!-- The size of the lock-to-app button and its icon. -->
- <dimen name="recents_lock_to_app_size">56dp</dimen>
- <dimen name="recents_lock_to_app_icon_size">28dp</dimen>
-
- <!-- The amount of overscroll allowed when flinging to the end of the stack. -->
- <dimen name="recents_fling_overscroll_distance">24dp</dimen>
-
- <!-- The size of the drag hint text. -->
- <dimen name="recents_drag_hint_text_size">14sp</dimen>
-
- <!-- The min alpha to apply to a task affiliation group color. -->
- <item name="recents_task_affiliation_color_min_alpha_percentage" format="float" type="dimen">0.6</item>
-
- <!-- The amount to offset when animating into an affiliate group. -->
- <dimen name="recents_task_stack_animation_affiliate_enter_offset">32dp</dimen>
-
- <!-- The offsets the tasks animate from when recents is launched while docking -->
- <dimen name="recents_task_stack_animation_launched_while_docking_offset">144dp</dimen>
-
- <!-- The amount to translate when animating the removal of a task. -->
- <dimen name="recents_task_view_remove_anim_translation_x">100dp</dimen>
-
- <!-- The alpha to apply to the recents row when it doesn't have focus -->
- <item name="recents_recents_row_dim_alpha" format="float" type="dimen">0.5</item>
-
- <!-- The speed in dp/s at which the user needs to be scrolling in recents such that we start
- loading full resolution screenshots. -->
- <dimen name="recents_fast_fling_velocity">600dp</dimen>
-
-</resources> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/values/dimens_grid.xml b/packages/SystemUI/legacy/recents/res/values/dimens_grid.xml
deleted file mode 100644
index febf65b834ee..000000000000
--- a/packages/SystemUI/legacy/recents/res/values/dimens_grid.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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>
- <dimen name="recents_grid_padding_left_right">32dp</dimen>
- <dimen name="recents_grid_padding_top_bottom">150dp</dimen>
- <dimen name="recents_grid_padding_task_view">20dp</dimen>
- <dimen name="recents_grid_task_view_header_height">44dp</dimen>
- <dimen name="recents_grid_task_view_header_button_padding">8dp</dimen>
- <dimen name="recents_grid_task_view_focused_frame_thickness">8dp</dimen>
- <dimen name="recents_grid_task_view_rounded_corners_radius">4dp</dimen>
- <dimen name="recents_grid_task_view_focused_frame_rounded_corners_radius">8dp</dimen>
-</resources>
-
diff --git a/packages/SystemUI/legacy/recents/res/values/strings.xml b/packages/SystemUI/legacy/recents/res/values/strings.xml
deleted file mode 100644
index 4b44ba969d6e..000000000000
--- a/packages/SystemUI/legacy/recents/res/values/strings.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/**
- * Copyright (c) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Content description for the recent apps panel (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_desc_recent_apps">Overview.</string>
-
- <!-- Content description to tell the user that this button will remove an application from recents -->
- <string name="accessibility_recents_item_will_be_dismissed">Dismiss <xliff:g id="app" example="Calendar">%s</xliff:g>.</string>
- <!-- Content description to tell the user an application has been removed from recents -->
- <string name="accessibility_recents_item_dismissed"><xliff:g id="app" example="Calendar">%s</xliff:g> dismissed.</string>
- <!-- Content description to tell the user all applications has been removed from recents -->
- <string name="accessibility_recents_all_items_dismissed">All recent applications dismissed.</string>
- <!-- Content description to tell the user that this button will open application info for an application in recents -->
- <string name="accessibility_recents_item_open_app_info">Open <xliff:g id="app" example="Calendar">%s</xliff:g> application info.</string>
- <!-- Content description to tell the user an application has been launched from recents -->
- <string name="accessibility_recents_item_launched">Starting <xliff:g id="app" example="Calendar">%s</xliff:g>.</string>
-
- <!-- Recents: The empty recents string. [CHAR LIMIT=NONE] -->
- <string name="recents_empty_message">No recent items</string>
- <!-- Recents: The empty recents string after dismissing all tasks. [CHAR LIMIT=NONE] -->
- <string name="recents_empty_message_dismissed_all">You\'ve cleared everything</string>
- <!-- Recents: The info panel app info button string. [CHAR LIMIT=NONE] -->
- <string name="recents_app_info_button_label">Application Info</string>
- <!-- Recents: The screen pinning button. [CHAR LIMIT=NONE] -->
- <string name="recents_lock_to_app_button_label">screen pinning</string>
- <!-- Recents: Temporary string for the button in the recents search bar. [CHAR LIMIT=NONE] -->
- <string name="recents_search_bar_label">search</string>
- <!-- Recents: Launch error string. [CHAR LIMIT=NONE] -->
- <string name="recents_launch_error_message">Could not start <xliff:g id="app" example="Calendar">%s</xliff:g>.</string>
- <!-- Recents: Launch disabled string. [CHAR LIMIT=NONE] -->
- <string name="recents_launch_disabled_message"><xliff:g id="app" example="Calendar">%s</xliff:g> is disabled in safe-mode.</string>
- <!-- Recents: Stack action button string. [CHAR LIMIT=NONE] -->
- <string name="recents_stack_action_button_label">Clear all</string>
- <!-- Recents: Hint text that shows on the drop targets to start multiwindow. [CHAR LIMIT=NONE] -->
- <string name="recents_drag_hint_message">Drag here to use split screen</string>
-
- <!-- Recents: MultiStack add stack split horizontal radio button. [CHAR LIMIT=NONE] -->
- <string name="recents_multistack_add_stack_dialog_split_horizontal">Split Horizontal</string>
- <!-- Recents: MultiStack add stack split vertical radio button. [CHAR LIMIT=NONE] -->
- <string name="recents_multistack_add_stack_dialog_split_vertical">Split Vertical</string>
- <!-- Recents: MultiStack add stack split custom radio button. [CHAR LIMIT=NONE] -->
- <string name="recents_multistack_add_stack_dialog_split_custom">Split Custom</string>
- <!-- Recents: Accessibility split to the top -->
- <string name="recents_accessibility_split_screen_top">Split screen to the top</string>
- <!-- Recents: Accessibility split to the left -->
- <string name="recents_accessibility_split_screen_left">Split screen to the left</string>
- <!-- Recents: Accessibility split to the right -->
- <string name="recents_accessibility_split_screen_right">Split screen to the right</string>
-
-</resources> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/res/values/styles.xml b/packages/SystemUI/legacy/recents/res/values/styles.xml
deleted file mode 100644
index eb16be7c95b6..000000000000
--- a/packages/SystemUI/legacy/recents/res/values/styles.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2006 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT 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">
-
- <style name="RecentsTheme" parent="@android:style/Theme.Material">
- <!-- NoTitle -->
- <item name="android:windowNoTitle">true</item>
- <!-- Misc -->
- <item name="android:statusBarColor">@android:color/transparent</item>
- <item name="android:navigationBarColor">@android:color/transparent</item>
- <item name="android:windowDrawsSystemBarBackgrounds">true</item>
- <item name="android:windowAnimationStyle">@null</item>
- <item name="android:ambientShadowAlpha">0.35</item>
- </style>
-
- <!-- Recents theme -->
- <style name="RecentsTheme.Wallpaper">
- <item name="android:windowBackground">@*android:color/transparent</item>
- <item name="android:colorBackgroundCacheHint">@null</item>
- <item name="android:windowShowWallpaper">true</item>
- <item name="android:windowDisablePreview">true</item>
- <item name="clearAllStyle">@style/ClearAllButtonDefaultMargins</item>
- <item name="clearAllBackgroundColor">@color/recents_clear_all_button_bg_dark_color</item>
- <item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item>
- <item name="wallpaperTextColorSecondary">@*android:color/secondary_text_material_dark</item>
- </style>
-
- <style name="RecentsTheme.Wallpaper.Light">
- <item name="clearAllBackgroundColor">@color/recents_clear_all_button_bg_light_color</item>
- <item name="wallpaperTextColor">@*android:color/primary_text_material_light</item>
- <item name="wallpaperTextColorSecondary">@*android:color/secondary_text_material_light</item>
- </style>
-
- <!-- Performance optimized Recents theme (no wallpaper) -->
- <style name="RecentsTheme.NoWallpaper">
- <item name="android:windowBackground">@android:color/black</item>
- <item name="wallpaperTextColor">@android:color/white</item>
- <item name="wallpaperTextColorSecondary">@android:color/white</item>
- </style>
-
- </resources> \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/Constants.java
deleted file mode 100644
index 003379fe0bf8..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/Constants.java
+++ /dev/null
@@ -1,34 +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 com.android.systemui.recents;
-
-/**
- * Constants
- */
-public class Constants {
-
- // TODO: Move into RecentsMetrics
- public static class Metrics {
- // DO NOT MODIFY THE ORDER OF THESE METRICS
- public static final int DismissSourceKeyboard = 0;
- public static final int DismissSourceSwipeGesture = 1;
- public static final int DismissSourceHeaderButton = 2;
- @Deprecated
- public static final int DismissSourceHistorySwipeGesture = 3;
- }
-
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
deleted file mode 100644
index 90c10992bc94..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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 com.android.systemui.recents;
-
-import android.graphics.Rect;
-
-/**
- * Due to the fact that RecentsActivity is per-user, we need to establish an
- * interface (this) for the system user to callback to the secondary users in
- * response to UI events coming in from the system user's SystemUI.
- */
-oneway interface IRecentsNonSystemUserCallbacks {
- void preloadRecents();
- void cancelPreloadingRecents();
- void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents, boolean animate,
- int recentsGrowTarget);
- void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
- void toggleRecents(int recentsGrowTarget);
- void onConfigurationChanged();
- void splitPrimaryTask(int topTaskId, int stackCreateMode, in Rect initialBounds);
- void onDraggingInRecents(float distanceFromTop);
- void onDraggingInRecentsEnded(float velocity);
- void showCurrentUserToast(int msgResId, int msgLength);
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
deleted file mode 100644
index e97714486dcf..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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 com.android.systemui.recents;
-
-import android.graphics.Rect;
-
-/**
- * Due to the fact that RecentsActivity is per-user, we need to establish an
- * interface (this) for the non-system user to register itself for callbacks and to
- * callback to the system user to update internal state.
- */
-oneway interface IRecentsSystemUserCallbacks {
- void registerNonSystemUserCallbacks(IBinder nonSystemUserCallbacks, int userId);
-
- void updateRecentsVisibility(boolean visible);
- void startScreenPinning(int taskId);
- void sendRecentsDrawnEvent();
- void sendDockingTopTaskEvent(in Rect initialRect);
- void sendLaunchRecentsEvent();
- void sendDockedFirstAnimationFrameEvent();
- void setWaitingForTransitionStartEvent(boolean waitingForTransitionStart);
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/LegacyRecentsImpl.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/LegacyRecentsImpl.java
deleted file mode 100644
index a150de95fcf0..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/LegacyRecentsImpl.java
+++ /dev/null
@@ -1,750 +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 com.android.systemui.recents;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS;
-
-import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.EventLog;
-import android.util.Log;
-import android.view.Display;
-import android.widget.Toast;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.EventLogConstants;
-import com.android.systemui.EventLogTags;
-import com.android.systemui.R;
-import com.android.systemui.SysUiServiceProvider;
-import com.android.systemui.pip.PipUI;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
-import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
-import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
-import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
-import com.android.systemui.recents.events.component.ExpandPipEvent;
-import com.android.systemui.recents.events.component.HidePipMenuEvent;
-import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
-import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
-import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
-import com.android.systemui.recents.events.component.ShowUserToastEvent;
-import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
-import com.android.systemui.recents.events.ui.RecentsGrowingEvent;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.stackdivider.Divider;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * An implementation of the SystemUI recents component, which supports both system and secondary
- * users.
- */
-public class LegacyRecentsImpl implements RecentsImplementation {
-
- private final static String TAG = "Recents";
-
- public final static int EVENT_BUS_PRIORITY = 1;
- public final static int BIND_TO_SYSTEM_USER_RETRY_DELAY = 5000;
-
- public final static Set<String> RECENTS_ACTIVITIES = new HashSet<>();
- static {
- RECENTS_ACTIVITIES.add(RecentsImpl.RECENTS_ACTIVITY);
- }
-
- private static final String COUNTER_WINDOW_SUPPORTED = "window_enter_supported";
- private static final String COUNTER_WINDOW_UNSUPPORTED = "window_enter_unsupported";
- private static final String COUNTER_WINDOW_INCOMPATIBLE = "window_enter_incompatible";
-
- private static SystemServicesProxy sSystemServicesProxy;
- private static RecentsDebugFlags sDebugFlags;
- private static RecentsTaskLoader sTaskLoader;
- private static RecentsConfiguration sConfiguration;
-
- private Context mContext;
- private SysUiServiceProvider mSysUiServiceProvider;
- private Handler mHandler;
- private RecentsImpl mImpl;
-
- // Only For system user, this is the callbacks instance we return to each secondary user
- private RecentsSystemUser mSystemToUserCallbacks;
-
- // Only for secondary users, this is the callbacks instance provided by the system user to make
- // calls back
- private IRecentsSystemUserCallbacks mUserToSystemCallbacks;
-
- // The set of runnables to run after binding to the system user's service.
- private final ArrayList<Runnable> mOnConnectRunnables = new ArrayList<>();
-
- // Only for secondary users, this is the death handler for the binder from the system user
- private final IBinder.DeathRecipient mUserToSystemCallbacksDeathRcpt = new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- mUserToSystemCallbacks = null;
- EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,
- EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_SYSTEM_UNBOUND,
- sSystemServicesProxy.getProcessUser());
-
- // Retry after a fixed duration
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- registerWithSystemUser();
- }
- }, BIND_TO_SYSTEM_USER_RETRY_DELAY);
- }
- };
-
- // Only for secondary users, this is the service connection we use to connect to the system user
- private final ServiceConnection mUserToSystemServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- if (service != null) {
- mUserToSystemCallbacks = IRecentsSystemUserCallbacks.Stub.asInterface(
- service);
- EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,
- EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_SYSTEM_BOUND,
- sSystemServicesProxy.getProcessUser());
-
- // Listen for system user's death, so that we can reconnect later
- try {
- service.linkToDeath(mUserToSystemCallbacksDeathRcpt, 0);
- } catch (RemoteException e) {
- Log.e(TAG, "Lost connection to (System) SystemUI", e);
- }
-
- // Run each of the queued runnables
- runAndFlushOnConnectRunnables();
- }
-
- // Unbind ourselves now that we've registered our callbacks. The
- // binder to the system user are still valid at this point.
- mContext.unbindService(this);
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- // Do nothing
- }
- };
-
- /**
- * Returns the callbacks interface that non-system users can call.
- */
- public IBinder getSystemUserCallbacks() {
- return mSystemToUserCallbacks;
- }
-
- public static RecentsTaskLoader getTaskLoader() {
- return sTaskLoader;
- }
-
-
- public static SystemServicesProxy getSystemServices() {
- return sSystemServicesProxy;
- }
-
- public static RecentsConfiguration getConfiguration() {
- return sConfiguration;
- }
-
- public static RecentsDebugFlags getDebugFlags() {
- return sDebugFlags;
- }
-
- @Override
- public void onStart(Context context, SysUiServiceProvider sysUiServiceProvider) {
- mContext = context;
- mSysUiServiceProvider = sysUiServiceProvider;
- final Resources res = mContext.getResources();
- final int defaultTaskBarBackgroundColor =
- mContext.getColor(R.color.recents_task_bar_default_background_color);
- final int defaultTaskViewBackgroundColor =
- mContext.getColor(R.color.recents_task_view_default_background_color);
- sDebugFlags = new RecentsDebugFlags();
- sSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
- sConfiguration = new RecentsConfiguration(mContext);
- sTaskLoader = new RecentsTaskLoader(mContext,
- // TODO: Once we start building the AAR, move these into the loader
- res.getInteger(R.integer.config_recents_max_thumbnail_count),
- res.getInteger(R.integer.config_recents_max_icon_count),
- res.getInteger(R.integer.recents_svelte_level));
- sTaskLoader.setDefaultColors(defaultTaskBarBackgroundColor, defaultTaskViewBackgroundColor);
- mHandler = new Handler();
- mImpl = new RecentsImpl(mContext);
-
- // Register with the event bus
- EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
- EventBus.getDefault().register(sSystemServicesProxy, EVENT_BUS_PRIORITY);
- EventBus.getDefault().register(sTaskLoader, EVENT_BUS_PRIORITY);
-
- // Due to the fact that RecentsActivity is per-user, we need to establish and interface for
- // the system user's Recents component to pass events (like show/hide/toggleRecents) to the
- // secondary user, and vice versa (like visibility change, screen pinning).
- final int processUser = sSystemServicesProxy.getProcessUser();
- if (sSystemServicesProxy.isSystemUser(processUser)) {
- // For the system user, initialize an instance of the interface that we can pass to the
- // secondary user
- mSystemToUserCallbacks = new RecentsSystemUser(mContext, mImpl);
- } else {
- // For the secondary user, bind to the primary user's service to get a persistent
- // interface to register its implementation and to later update its state
- registerWithSystemUser();
- }
- }
-
- @Override
- public void onBootCompleted() {
- mImpl.onBootCompleted();
- }
-
-
- @Override
- public void growRecents() {
- EventBus.getDefault().send(new RecentsGrowingEvent());
- }
-
- /**
- * Shows the Recents.
- */
- @Override
- public void showRecentApps(boolean triggeredFromAltTab) {
- ActivityManagerWrapper.getInstance().closeSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
- int recentsGrowTarget = getComponent(Divider.class).getView().growsRecents();
- int currentUser = sSystemServicesProxy.getCurrentUser();
- if (sSystemServicesProxy.isSystemUser(currentUser)) {
- mImpl.showRecents(triggeredFromAltTab, false /* draggingInRecents */,
- true /* animate */, recentsGrowTarget);
- } else {
- if (mSystemToUserCallbacks != null) {
- IRecentsNonSystemUserCallbacks callbacks =
- mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
- if (callbacks != null) {
- try {
- callbacks.showRecents(triggeredFromAltTab, false /* draggingInRecents */,
- true /* animate */, recentsGrowTarget);
- } catch (RemoteException e) {
- Log.e(TAG, "Callback failed", e);
- }
- } else {
- Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
- }
- }
- }
- }
-
- /**
- * Hides the Recents.
- */
- @Override
- public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
- int currentUser = sSystemServicesProxy.getCurrentUser();
- if (sSystemServicesProxy.isSystemUser(currentUser)) {
- mImpl.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
- } else {
- if (mSystemToUserCallbacks != null) {
- IRecentsNonSystemUserCallbacks callbacks =
- mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
- if (callbacks != null) {
- try {
- callbacks.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
- } catch (RemoteException e) {
- Log.e(TAG, "Callback failed", e);
- }
- } else {
- Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
- }
- }
- }
- }
-
- /**
- * Toggles the Recents activity.
- */
- @Override
- public void toggleRecentApps() {
- int growTarget = getComponent(Divider.class).getView().growsRecents();
- int currentUser = sSystemServicesProxy.getCurrentUser();
- if (sSystemServicesProxy.isSystemUser(currentUser)) {
- mImpl.toggleRecents(growTarget);
- } else {
- if (mSystemToUserCallbacks != null) {
- IRecentsNonSystemUserCallbacks callbacks =
- mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
- if (callbacks != null) {
- try {
- callbacks.toggleRecents(growTarget);
- } catch (RemoteException e) {
- Log.e(TAG, "Callback failed", e);
- }
- } else {
- Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
- }
- }
- }
- }
-
- /**
- * Preloads info for the Recents activity.
- */
- @Override
- public void preloadRecentApps() {
- int currentUser = sSystemServicesProxy.getCurrentUser();
- if (sSystemServicesProxy.isSystemUser(currentUser)) {
- mImpl.preloadRecents();
- } else {
- if (mSystemToUserCallbacks != null) {
- IRecentsNonSystemUserCallbacks callbacks =
- mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
- if (callbacks != null) {
- try {
- callbacks.preloadRecents();
- } catch (RemoteException e) {
- Log.e(TAG, "Callback failed", e);
- }
- } else {
- Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
- }
- }
- }
- }
-
- @Override
- public void cancelPreloadRecentApps() {
- int currentUser = sSystemServicesProxy.getCurrentUser();
- if (sSystemServicesProxy.isSystemUser(currentUser)) {
- mImpl.cancelPreloadingRecents();
- } else {
- if (mSystemToUserCallbacks != null) {
- IRecentsNonSystemUserCallbacks callbacks =
- mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
- if (callbacks != null) {
- try {
- callbacks.cancelPreloadingRecents();
- } catch (RemoteException e) {
- Log.e(TAG, "Callback failed", e);
- }
- } else {
- Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
- }
- }
- }
- }
-
- @Override
- public boolean splitPrimaryTask(int stackCreateMode, Rect initialBounds, int metricsDockAction) {
- Point realSize = new Point();
- if (initialBounds == null) {
- mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
- .getRealSize(realSize);
- initialBounds = new Rect(0, 0, realSize.x, realSize.y);
- }
-
- int currentUser = sSystemServicesProxy.getCurrentUser();
- ActivityManager.RunningTaskInfo runningTask =
- ActivityManagerWrapper.getInstance().getRunningTask();
- final int activityType = runningTask != null
- ? runningTask.configuration.windowConfiguration.getActivityType()
- : ACTIVITY_TYPE_UNDEFINED;
- boolean screenPinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
- boolean isRunningTaskInHomeOrRecentsStack =
- activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS;
- if (runningTask != null && !isRunningTaskInHomeOrRecentsStack && !screenPinningActive) {
- logDockAttempt(mContext, runningTask.topActivity, runningTask.resizeMode);
- if (runningTask.supportsSplitScreenMultiWindow) {
- if (metricsDockAction != -1) {
- MetricsLogger.action(mContext, metricsDockAction,
- runningTask.topActivity.flattenToShortString());
- }
- if (sSystemServicesProxy.isSystemUser(currentUser)) {
- mImpl.splitPrimaryTask(runningTask.id, stackCreateMode, initialBounds);
- } else {
- if (mSystemToUserCallbacks != null) {
- IRecentsNonSystemUserCallbacks callbacks =
- mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
- if (callbacks != null) {
- try {
- callbacks.splitPrimaryTask(runningTask.id, stackCreateMode,
- initialBounds);
- } catch (RemoteException e) {
- Log.e(TAG, "Callback failed", e);
- }
- } else {
- Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
- }
- }
- }
-
- return true;
- } else {
- EventBus.getDefault().send(new ShowUserToastEvent(
- R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT));
- return false;
- }
- } else {
- return false;
- }
- }
-
- public static void logDockAttempt(Context ctx, ComponentName activity, int resizeMode) {
- if (resizeMode == ActivityInfo.RESIZE_MODE_UNRESIZEABLE) {
- MetricsLogger.action(ctx, MetricsEvent.ACTION_WINDOW_DOCK_UNRESIZABLE,
- activity.flattenToShortString());
- }
- MetricsLogger.count(ctx, getMetricsCounterForResizeMode(resizeMode), 1);
- }
-
- private static String getMetricsCounterForResizeMode(int resizeMode) {
- switch (resizeMode) {
- case ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE:
- return COUNTER_WINDOW_UNSUPPORTED;
- case ActivityInfo.RESIZE_MODE_RESIZEABLE:
- case ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION:
- return COUNTER_WINDOW_SUPPORTED;
- default:
- return COUNTER_WINDOW_INCOMPATIBLE;
- }
- }
-
- @Override
- public void onAppTransitionFinished() {
- if (!LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- // Fallback, reset the flag once an app transition ends
- EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(
- false /* waitingForTransitionStart */));
- }
- }
-
- /**
- * Updates on configuration change.
- */
- public void onConfigurationChanged(Configuration newConfig) {
- int currentUser = sSystemServicesProxy.getCurrentUser();
- if (sSystemServicesProxy.isSystemUser(currentUser)) {
- mImpl.onConfigurationChanged();
- } else {
- if (mSystemToUserCallbacks != null) {
- IRecentsNonSystemUserCallbacks callbacks =
- mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
- if (callbacks != null) {
- try {
- callbacks.onConfigurationChanged();
- } catch (RemoteException e) {
- Log.e(TAG, "Callback failed", e);
- }
- } else {
- Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
- }
- }
- }
- }
-
- /**
- * Handle Recents activity visibility changed.
- */
- public final void onBusEvent(final RecentsVisibilityChangedEvent event) {
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- int processUser = ssp.getProcessUser();
- if (ssp.isSystemUser(processUser)) {
- mImpl.onVisibilityChanged(event.applicationContext, event.visible);
- } else {
- postToSystemUser(new Runnable() {
- @Override
- public void run() {
- try {
- mUserToSystemCallbacks.updateRecentsVisibility(event.visible);
- } catch (RemoteException e) {
- Log.e(TAG, "Callback failed", e);
- }
- }
- });
- }
-
- // This will catch the cases when a user launches from recents to another app
- // (and vice versa) that is not in the recents stack (such as home or bugreport) and it
- // would not reset the wait for transition flag. This will catch it and make sure that the
- // flag is reset.
- if (!event.visible) {
- mImpl.setWaitingForTransitionStart(false);
- }
- }
-
- public final void onBusEvent(DockedFirstAnimationFrameEvent event) {
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- int processUser = ssp.getProcessUser();
- if (ssp.isSystemUser(processUser)) {
- final Divider divider = getComponent(Divider.class);
- if (divider != null) {
- divider.onDockedFirstAnimationFrame();
- }
- } else {
- postToSystemUser(new Runnable() {
- @Override
- public void run() {
- try {
- mUserToSystemCallbacks.sendDockedFirstAnimationFrameEvent();
- } catch (RemoteException e) {
- Log.e(TAG, "Callback failed", e);
- }
- }
- });
- }
- }
-
- /**
- * Handle screen pinning request.
- */
- public final void onBusEvent(final ScreenPinningRequestEvent event) {
- int processUser = sSystemServicesProxy.getProcessUser();
- if (sSystemServicesProxy.isSystemUser(processUser)) {
- mImpl.onStartScreenPinning(event.applicationContext, event.taskId);
- } else {
- postToSystemUser(new Runnable() {
- @Override
- public void run() {
- try {
- mUserToSystemCallbacks.startScreenPinning(event.taskId);
- } catch (RemoteException e) {
- Log.e(TAG, "Callback failed", e);
- }
- }
- });
- }
- }
-
- public final void onBusEvent(final RecentsDrawnEvent event) {
- int processUser = sSystemServicesProxy.getProcessUser();
- if (sSystemServicesProxy.isSystemUser(processUser)) {
- final Divider divider = getComponent(Divider.class);
- if (divider != null) {
- divider.onRecentsDrawn();
- }
- } else {
- postToSystemUser(new Runnable() {
- @Override
- public void run() {
- try {
- mUserToSystemCallbacks.sendRecentsDrawnEvent();
- } catch (RemoteException e) {
- Log.e(TAG, "Callback failed", e);
- }
- }
- });
- }
- }
-
- public final void onBusEvent(final DockedTopTaskEvent event) {
- int processUser = sSystemServicesProxy.getProcessUser();
- if (sSystemServicesProxy.isSystemUser(processUser)) {
- final Divider divider = getComponent(Divider.class);
- if (divider != null) {
- divider.onDockedTopTask();
- }
- } else {
- postToSystemUser(new Runnable() {
- @Override
- public void run() {
- try {
- mUserToSystemCallbacks.sendDockingTopTaskEvent(event.initialRect);
- } catch (RemoteException e) {
- Log.e(TAG, "Callback failed", e);
- }
- }
- });
- }
- }
-
- public final void onBusEvent(final RecentsActivityStartingEvent event) {
- int processUser = sSystemServicesProxy.getProcessUser();
- if (sSystemServicesProxy.isSystemUser(processUser)) {
- final Divider divider = getComponent(Divider.class);
- if (divider != null) {
- divider.onRecentsActivityStarting();
- }
- } else {
- postToSystemUser(new Runnable() {
- @Override
- public void run() {
- try {
- mUserToSystemCallbacks.sendLaunchRecentsEvent();
- } catch (RemoteException e) {
- Log.e(TAG, "Callback failed", e);
- }
- }
- });
- }
- }
-
- public final void onBusEvent(LaunchTaskFailedEvent event) {
- // Reset the transition when tasks fail to launch
- mImpl.setWaitingForTransitionStart(false);
- }
-
- public final void onBusEvent(ConfigurationChangedEvent event) {
- // Update the configuration for the Recents component when the activity configuration
- // changes as well
- mImpl.onConfigurationChanged();
- }
-
- public final void onBusEvent(ShowUserToastEvent event) {
- int currentUser = sSystemServicesProxy.getCurrentUser();
- if (sSystemServicesProxy.isSystemUser(currentUser)) {
- mImpl.onShowCurrentUserToast(event.msgResId, event.msgLength);
- } else {
- if (mSystemToUserCallbacks != null) {
- IRecentsNonSystemUserCallbacks callbacks =
- mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
- if (callbacks != null) {
- try {
- callbacks.showCurrentUserToast(event.msgResId, event.msgLength);
- } catch (RemoteException e) {
- Log.e(TAG, "Callback failed", e);
- }
- } else {
- Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
- }
- }
- }
- }
-
- public final void onBusEvent(SetWaitingForTransitionStartEvent event) {
- int processUser = sSystemServicesProxy.getProcessUser();
- if (sSystemServicesProxy.isSystemUser(processUser)) {
- mImpl.setWaitingForTransitionStart(event.waitingForTransitionStart);
- } else {
- postToSystemUser(new Runnable() {
- @Override
- public void run() {
- try {
- mUserToSystemCallbacks.setWaitingForTransitionStartEvent(
- event.waitingForTransitionStart);
- } catch (RemoteException e) {
- Log.e(TAG, "Callback failed", e);
- }
- }
- });
- }
- }
-
- public final void onBusEvent(ExpandPipEvent event) {
- PipUI pipUi = getComponent(PipUI.class);
- if (pipUi == null) {
- return;
- }
- pipUi.expandPip();
- }
-
- public final void onBusEvent(HidePipMenuEvent event) {
- PipUI pipUi = getComponent(PipUI.class);
- if (pipUi == null) {
- return;
- }
- event.getAnimationTrigger().increment();
- pipUi.hidePipMenu(() -> {
- event.getAnimationTrigger().increment();
- }, () -> {
- event.getAnimationTrigger().decrement();
- });
- event.getAnimationTrigger().decrement();
- }
-
- /**
- * Attempts to register with the system user.
- */
- private void registerWithSystemUser() {
- final int processUser = sSystemServicesProxy.getProcessUser();
- postToSystemUser(new Runnable() {
- @Override
- public void run() {
- try {
- mUserToSystemCallbacks.registerNonSystemUserCallbacks(
- new RecentsImplProxy(mImpl), processUser);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to register", e);
- }
- }
- });
- }
-
- /**
- * Runs the runnable in the system user's Recents context, connecting to the service if
- * necessary.
- */
- private void postToSystemUser(final Runnable onConnectRunnable) {
- mOnConnectRunnables.add(onConnectRunnable);
- if (mUserToSystemCallbacks == null) {
- Intent systemUserServiceIntent = new Intent();
- systemUserServiceIntent.setClass(mContext, RecentsSystemUserService.class);
- boolean bound = mContext.bindServiceAsUser(systemUserServiceIntent,
- mUserToSystemServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
- EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,
- EventLogConstants.SYSUI_RECENTS_CONNECTION_USER_BIND_SERVICE,
- sSystemServicesProxy.getProcessUser());
- if (!bound) {
- // Retry after a fixed duration
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- registerWithSystemUser();
- }
- }, BIND_TO_SYSTEM_USER_RETRY_DELAY);
- }
- } else {
- runAndFlushOnConnectRunnables();
- }
- }
-
- /**
- * Runs all the queued runnables after a service connection is made.
- */
- private void runAndFlushOnConnectRunnables() {
- for (Runnable r : mOnConnectRunnables) {
- r.run();
- }
- mOnConnectRunnables.clear();
- }
-
- private <T> T getComponent(Class<T> clazz) {
- return mSysUiServiceProvider.getComponent(clazz);
- }
-
- @Override
- public void dump(PrintWriter pw) {
- pw.println("Recents");
- pw.println(" currentUserId=" + SystemServicesProxy.getInstance(mContext).getCurrentUser());
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsActivity.java
deleted file mode 100644
index a7ccc3a49073..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsActivity.java
+++ /dev/null
@@ -1,858 +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 com.android.systemui.recents;
-
-import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_HOME_KEY;
-
-import android.app.Activity;
-import android.app.ActivityOptions;
-import android.app.TaskStackBuilder;
-import android.app.WallpaperManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Configuration;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnPreDrawListener;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-
-import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.content.PackageMonitor;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.util.LatencyTracker;
-import com.android.systemui.DejankUtils;
-import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
-import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
-import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
-import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
-import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
-import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
-import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
-import com.android.systemui.recents.events.activity.ExitRecentsWindowFirstAnimationFrameEvent;
-import com.android.systemui.recents.events.activity.HideRecentsEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
-import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
-import com.android.systemui.recents.events.activity.PackagesChangedEvent;
-import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
-import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
-import com.android.systemui.recents.events.component.ActivityUnpinnedEvent;
-import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
-import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
-import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
-import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
-import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
-import com.android.systemui.recents.events.ui.HideIncompatibleAppOverlayEvent;
-import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
-import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
-import com.android.systemui.recents.events.ui.ShowIncompatibleAppOverlayEvent;
-import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
-import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
-import com.android.systemui.recents.events.ui.UserInteractionEvent;
-import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
-import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
-import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
-import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent;
-import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent.Direction;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.model.RecentsTaskLoadPlan;
-import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.utilities.Utilities;
-import com.android.systemui.recents.views.RecentsView;
-import com.android.systemui.recents.views.SystemBarScrimViews;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * The main Recents activity that is started from RecentsComponent.
- */
-public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreDrawListener,
- ColorExtractor.OnColorsChangedListener {
-
- private final static String TAG = "RecentsActivity";
- private final static boolean DEBUG = false;
-
- public final static int EVENT_BUS_PRIORITY = LegacyRecentsImpl.EVENT_BUS_PRIORITY + 1;
- public final static int INCOMPATIBLE_APP_ALPHA_DURATION = 150;
-
- private PackageMonitor mPackageMonitor = new PackageMonitor() {
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- RecentsActivity.this.onPackageChanged(packageName, getChangingUserId());
- }
-
- @Override
- public boolean onPackageChanged(String packageName, int uid, String[] components) {
- RecentsActivity.this.onPackageChanged(packageName, getChangingUserId());
- return true;
- }
-
- @Override
- public void onPackageModified(String packageName) {
- RecentsActivity.this.onPackageChanged(packageName, getChangingUserId());
- }
- };
- private Handler mHandler = new Handler();
- private long mLastTabKeyEventTime;
- private boolean mFinishedOnStartup;
- private boolean mIgnoreAltTabRelease;
- private boolean mIsVisible;
- private boolean mRecentsStartRequested;
- private Configuration mLastConfig;
-
- // Top level views
- private RecentsView mRecentsView;
- private SystemBarScrimViews mScrimViews;
- private View mIncompatibleAppOverlay;
-
- // Runnables to finish the Recents activity
- private Intent mHomeIntent;
-
- // The trigger to automatically launch the current task
- private int mFocusTimerDuration;
- private final UserInteractionEvent mUserInteractionEvent = new UserInteractionEvent();
-
- // Theme and colors
- private SysuiColorExtractor mColorExtractor;
- private boolean mUsingDarkText;
-
- /**
- * A common Runnable to finish Recents by launching Home with an animation depending on the
- * last activity launch state. Generally we always launch home when we exit Recents rather than
- * just finishing the activity since we don't know what is behind Recents in the task stack.
- */
- class LaunchHomeRunnable implements Runnable {
-
- Intent mLaunchIntent;
- ActivityOptions mOpts;
-
- /**
- * Creates a finish runnable that starts the specified intent.
- */
- public LaunchHomeRunnable(Intent launchIntent, ActivityOptions opts) {
- mLaunchIntent = launchIntent;
- mOpts = opts;
- }
-
- @Override
- public void run() {
- try {
- mHandler.post(() -> {
- ActivityOptions opts = mOpts;
- if (opts == null) {
- opts = ActivityOptions.makeCustomAnimation(RecentsActivity.this,
- R.anim.recents_to_launcher_enter, R.anim.recents_to_launcher_exit);
- }
- startActivityAsUser(mLaunchIntent, opts.toBundle(), UserHandle.CURRENT);
- });
- } catch (Exception e) {
- Log.e(TAG, getString(R.string.recents_launch_error_message, "Home"), e);
- }
- }
- }
-
- /**
- * Broadcast receiver to handle messages from the system
- */
- final BroadcastReceiver mSystemBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context ctx, Intent intent) {
- String action = intent.getAction();
- if (action.equals(Intent.ACTION_SCREEN_OFF)) {
- // When the screen turns off, dismiss Recents to Home
- dismissRecentsToHomeIfVisible(false);
- } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
- // When switching users, dismiss Recents to Home similar to screen off
- finish();
- }
- }
- };
-
- private final OnPreDrawListener mRecentsDrawnEventListener =
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
- EventBus.getDefault().post(new RecentsDrawnEvent());
- if (LatencyTracker.isEnabled(getApplicationContext())) {
- DejankUtils.postAfterTraversal(() -> LatencyTracker.getInstance(
- getApplicationContext()).onActionEnd(
- LatencyTracker.ACTION_TOGGLE_RECENTS));
- }
- DejankUtils.postAfterTraversal(() -> {
- LegacyRecentsImpl.getTaskLoader().startLoader(RecentsActivity.this);
- LegacyRecentsImpl.getTaskLoader().getHighResThumbnailLoader().setVisible(true);
- });
- return true;
- }
- };
-
- /**
- * Dismisses recents if we are already visible and the intent is to toggle the recents view.
- */
- boolean dismissRecentsToFocusedTask(int logCategory) {
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- if (ssp.isRecentsActivityVisible()) {
- // If we have a focused Task, launch that Task now
- if (mRecentsView.launchFocusedTask(logCategory)) return true;
- }
- return false;
- }
-
- /**
- * Dismisses recents back to the launch target task.
- */
- boolean dismissRecentsToLaunchTargetTaskOrHome() {
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- if (ssp.isRecentsActivityVisible()) {
- // If we have a focused Task, launch that Task now
- if (mRecentsView.launchPreviousTask()) return true;
- // If none of the other cases apply, then just go Home
- dismissRecentsToHome(true /* animateTaskViews */);
- }
- return false;
- }
-
- /**
- * Dismisses recents if we are already visible and the intent is to toggle the recents view.
- */
- boolean dismissRecentsToFocusedTaskOrHome() {
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- if (ssp.isRecentsActivityVisible()) {
- // If we have a focused Task, launch that Task now
- if (mRecentsView.launchFocusedTask(0 /* logCategory */)) return true;
- // If none of the other cases apply, then just go Home
- dismissRecentsToHome(true /* animateTaskViews */);
- return true;
- }
- return false;
- }
-
- /**
- * Dismisses Recents directly to Home without checking whether it is currently visible.
- */
- void dismissRecentsToHome(boolean animateTaskViews) {
- dismissRecentsToHome(animateTaskViews, null);
- }
-
- /**
- * Dismisses Recents directly to Home without checking whether it is currently visible.
- *
- * @param overrideAnimation If not null, will override the default animation that is based on
- * how Recents was launched.
- */
- void dismissRecentsToHome(boolean animateTaskViews, ActivityOptions overrideAnimation) {
- DismissRecentsToHomeAnimationStarted dismissEvent =
- new DismissRecentsToHomeAnimationStarted(animateTaskViews);
- dismissEvent.addPostAnimationCallback(new LaunchHomeRunnable(mHomeIntent,
- overrideAnimation));
- ActivityManagerWrapper.getInstance().closeSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
- EventBus.getDefault().send(dismissEvent);
- }
-
- /** Dismisses Recents directly to Home if we currently aren't transitioning. */
- boolean dismissRecentsToHomeIfVisible(boolean animated) {
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- if (ssp.isRecentsActivityVisible()) {
- // Return to Home
- dismissRecentsToHome(animated);
- return true;
- }
- return false;
- }
-
- /** Called with the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mFinishedOnStartup = false;
-
- // In the case that the activity starts up before the Recents component has initialized
- // (usually when debugging/pushing the SysUI apk), just finish this activity.
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- if (ssp == null) {
- mFinishedOnStartup = true;
- finish();
- return;
- }
-
- // Register this activity with the event bus
- EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
-
- // Initialize the package monitor
- mPackageMonitor.register(this, Looper.getMainLooper(), UserHandle.ALL,
- true /* externalStorage */);
-
- // Select theme based on wallpaper colors
- mColorExtractor = Dependency.get(SysuiColorExtractor.class);
- mColorExtractor.addOnColorsChangedListener(this);
- mUsingDarkText = mColorExtractor.getColors(ColorExtractor.TYPE_DARK,
- WallpaperManager.FLAG_SYSTEM).supportsDarkText();
- setTheme(mUsingDarkText ? R.style.RecentsTheme_Wallpaper_Light
- : R.style.RecentsTheme_Wallpaper);
-
- // Set the Recents layout
- setContentView(R.layout.recents);
- takeKeyEvents(true);
- mRecentsView = findViewById(R.id.recents_view);
- mScrimViews = new SystemBarScrimViews(this);
- getWindow().getAttributes().privateFlags |=
- WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
- if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
- }
-
- mLastConfig = new Configuration(Utilities.getAppConfiguration(this));
-
- // Set the window background
- mRecentsView.updateBackgroundScrim(getWindow(), isInMultiWindowMode());
-
- // Create the home intent runnable
- mHomeIntent = new Intent(Intent.ACTION_MAIN, null);
- mHomeIntent.addCategory(Intent.CATEGORY_HOME);
- mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-
- // Register the broadcast receiver to handle messages when the screen is turned off
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(Intent.ACTION_USER_SWITCHED);
- registerReceiver(mSystemBroadcastReceiver, filter);
-
- getWindow().addPrivateFlags(LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION);
- }
-
- @Override
- protected void onStart() {
- super.onStart();
-
- // Reload the stack view whenever we are made visible again
- reloadStackView();
-
- // Notify that recents is now visible
- EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
- MetricsLogger.visible(this, MetricsEvent.OVERVIEW_ACTIVITY);
-
- // Getting system scrim colors ignoring wallpaper visibility since it should never be grey.
- ColorExtractor.GradientColors systemColors = mColorExtractor.getNeutralColors();
- // We don't want to interpolate colors because we're defining the initial state.
- // Gradient should be set/ready when you open "Recents".
- mRecentsView.setScrimColors(systemColors, false);
-
- // Notify of the next draw
- mRecentsView.getViewTreeObserver().addOnPreDrawListener(mRecentsDrawnEventListener);
-
- // If Recents was restarted, then it should complete the enter animation with partially
- // reset launch state with dock, app and home set to false
- Object isRelaunching = getLastNonConfigurationInstance();
- if (isRelaunching != null && isRelaunching instanceof Boolean && (boolean) isRelaunching) {
- RecentsActivityLaunchState launchState = LegacyRecentsImpl.getConfiguration().getLaunchState();
- launchState.launchedViaDockGesture = false;
- launchState.launchedFromApp = false;
- launchState.launchedFromHome = false;
- onEnterAnimationComplete();
- }
- mRecentsStartRequested = false;
- }
-
- @Override
- public void onColorsChanged(ColorExtractor colorExtractor, int which) {
- if ((which & WallpaperManager.FLAG_SYSTEM) != 0) {
- ColorExtractor.GradientColors colors = mColorExtractor.getNeutralColors();
- boolean darkText = colors.supportsDarkText();
- if (darkText != mUsingDarkText) {
- mUsingDarkText = darkText;
- setTheme(mUsingDarkText ? R.style.RecentsTheme_Wallpaper_Light
- : R.style.RecentsTheme_Wallpaper);
- mRecentsView.reevaluateStyles();
- }
- mRecentsView.setScrimColors(colors, true /* animated */);
- }
- }
-
- /**
- * Reloads the stack views upon launching Recents.
- */
- private void reloadStackView() {
- // If the Recents component has preloaded a load plan, then use that to prevent
- // reconstructing the task stack
- RecentsTaskLoader loader = LegacyRecentsImpl.getTaskLoader();
- RecentsTaskLoadPlan loadPlan = RecentsImpl.consumeInstanceLoadPlan();
- if (loadPlan == null) {
- loadPlan = new RecentsTaskLoadPlan(this);
- }
-
- // Start loading tasks according to the load plan
- RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- if (!loadPlan.hasTasks()) {
- loader.preloadTasks(loadPlan, launchState.launchedToTaskId);
- }
-
- RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
- loadOpts.runningTaskId = launchState.launchedToTaskId;
- loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
- loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
- loader.loadTasks(loadPlan, loadOpts);
- TaskStack stack = loadPlan.getTaskStack();
- mRecentsView.onReload(stack, mIsVisible);
-
- // Update the nav bar scrim, but defer the animation until the enter-window event
- boolean animateNavBarScrim = !launchState.launchedViaDockGesture;
- mScrimViews.updateNavBarScrim(animateNavBarScrim, stack.getTaskCount() > 0, null);
-
- // If this is a new instance relaunched by AM, without going through the normal mechanisms,
- // then we have to manually trigger the enter animation state
- boolean wasLaunchedByAm = !launchState.launchedFromHome &&
- !launchState.launchedFromApp;
- if (wasLaunchedByAm) {
- EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
- }
-
- // Keep track of whether we launched from the nav bar button or via alt-tab
- if (launchState.launchedWithAltTab) {
- MetricsLogger.count(this, "overview_trigger_alttab", 1);
- } else {
- MetricsLogger.count(this, "overview_trigger_nav_btn", 1);
- }
-
- // Keep track of whether we launched from an app or from home
- if (launchState.launchedFromApp) {
- Task launchTarget = stack.getLaunchTarget();
- int launchTaskIndexInStack = launchTarget != null
- ? stack.indexOfTask(launchTarget)
- : 0;
- MetricsLogger.count(this, "overview_source_app", 1);
- // If from an app, track the stack index of the app in the stack (for affiliated tasks)
- MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack);
- } else {
- MetricsLogger.count(this, "overview_source_home", 1);
- }
-
- // Keep track of the total stack task count
- int taskCount = mRecentsView.getStack().getTaskCount();
- MetricsLogger.histogram(this, "overview_task_count", taskCount);
-
- // After we have resumed, set the visible state until the next onStop() call
- mIsVisible = true;
- }
-
- @Override
- public void onEnterAnimationComplete() {
- super.onEnterAnimationComplete();
- EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
-
- // Workaround for b/64694148: The animation started callback is not made (see
- // RecentsImpl.getThumbnailTransitionActivityOptions) so reset the transition-waiting state
- // once the enter animation has completed.
- EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false));
- }
-
- @Override
- public Object onRetainNonConfigurationInstance() {
- return true;
- }
-
- @Override
- protected void onPause() {
- super.onPause();
-
- mIgnoreAltTabRelease = false;
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
-
- // Notify of the config change
- Configuration newDeviceConfiguration = Utilities.getAppConfiguration(this);
- int numStackTasks = mRecentsView.getStack().getTaskCount();
- EventBus.getDefault().send(new ConfigurationChangedEvent(false /* fromMultiWindow */,
- mLastConfig.orientation != newDeviceConfiguration.orientation,
- mLastConfig.densityDpi != newDeviceConfiguration.densityDpi, numStackTasks > 0));
-
- mLastConfig.updateFrom(newDeviceConfiguration);
- }
-
- @Override
- public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
- super.onMultiWindowModeChanged(isInMultiWindowMode);
-
- // Set the window background
- mRecentsView.updateBackgroundScrim(getWindow(), isInMultiWindowMode);
-
- // Reload the task stack view if we are still visible to pick up the change in tasks that
- // result from entering/exiting multi-window
- if (mIsVisible) {
- reloadTaskStack(isInMultiWindowMode, true /* sendConfigChangedEvent */);
- }
- }
-
- @Override
- protected void onStop() {
- super.onStop();
-
- // Notify that recents is now hidden
- mIsVisible = false;
- EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
- MetricsLogger.hidden(this, MetricsEvent.OVERVIEW_ACTIVITY);
- LegacyRecentsImpl.getTaskLoader().getHighResThumbnailLoader().setVisible(false);
-
- // When recents starts again before onStop, do not reset launch flags so entrance animation
- // can run
- if (!isChangingConfigurations() && !mRecentsStartRequested) {
- // Workaround for b/22542869, if the RecentsActivity is started again, but without going
- // through SystemUI, we need to reset the config launch flags to ensure that we do not
- // wait on the system to send a signal that was never queued.
- RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- launchState.reset();
- }
-
- // Force a gc to attempt to clean up bitmap references more quickly (b/38258699)
- LegacyRecentsImpl.getSystemServices().gc();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
-
- // In the case that the activity finished on startup, just skip the unregistration below
- if (mFinishedOnStartup) {
- return;
- }
-
- // Unregister the system broadcast receivers
- unregisterReceiver(mSystemBroadcastReceiver);
-
- // Unregister any broadcast receivers for the task loader
- mPackageMonitor.unregister();
-
- EventBus.getDefault().unregister(this);
- }
-
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- EventBus.getDefault().register(mScrimViews, EVENT_BUS_PRIORITY);
- }
-
- @Override
- public void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- EventBus.getDefault().unregister(mScrimViews);
- }
-
- @Override
- public void onTrimMemory(int level) {
- RecentsTaskLoader loader = LegacyRecentsImpl.getTaskLoader();
- if (loader != null) {
- loader.onTrimMemory(level);
- }
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_TAB: {
- int altTabKeyDelay = getResources().getInteger(R.integer.recents_alt_tab_key_delay);
- boolean hasRepKeyTimeElapsed = (SystemClock.elapsedRealtime() -
- mLastTabKeyEventTime) > altTabKeyDelay;
- if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) {
- // Focus the next task in the stack
- final boolean backward = event.isShiftPressed();
- if (backward) {
- EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
- } else {
- EventBus.getDefault().send(new FocusNextTaskViewEvent());
- }
- mLastTabKeyEventTime = SystemClock.elapsedRealtime();
-
- // In the case of another ALT event, don't ignore the next release
- if (event.isAltPressed()) {
- mIgnoreAltTabRelease = false;
- }
- }
- return true;
- }
- case KeyEvent.KEYCODE_DPAD_UP:
- case KeyEvent.KEYCODE_DPAD_DOWN:
- case KeyEvent.KEYCODE_DPAD_LEFT:
- case KeyEvent.KEYCODE_DPAD_RIGHT: {
- final Direction direction = NavigateTaskViewEvent.getDirectionFromKeyCode(keyCode);
- EventBus.getDefault().send(new NavigateTaskViewEvent(direction));
- return true;
- }
- case KeyEvent.KEYCODE_DEL:
- case KeyEvent.KEYCODE_FORWARD_DEL: {
- if (event.getRepeatCount() <= 0) {
- EventBus.getDefault().send(new DismissFocusedTaskViewEvent());
-
- // Keep track of deletions by keyboard
- MetricsLogger.histogram(this, "overview_task_dismissed_source",
- Constants.Metrics.DismissSourceKeyboard);
- return true;
- }
- }
- default:
- break;
- }
- return super.onKeyDown(keyCode, event);
- }
-
- @Override
- public void onUserInteraction() {
- EventBus.getDefault().send(mUserInteractionEvent);
- }
-
- @Override
- public void onBackPressed() {
- // Back behaves like the recents button so just trigger a toggle event
- EventBus.getDefault().send(new ToggleRecentsEvent());
- }
-
- /**** EventBus events ****/
-
- public final void onBusEvent(ToggleRecentsEvent event) {
- RecentsActivityLaunchState launchState = LegacyRecentsImpl.getConfiguration().getLaunchState();
- if (launchState.launchedFromHome) {
- dismissRecentsToHome(true /* animateTaskViews */);
- } else {
- dismissRecentsToLaunchTargetTaskOrHome();
- }
- }
-
- public final void onBusEvent(RecentsActivityStartingEvent event) {
- mRecentsStartRequested = true;
- }
-
- public final void onBusEvent(HideRecentsEvent event) {
- if (event.triggeredFromAltTab) {
- // If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app
- if (!mIgnoreAltTabRelease) {
- dismissRecentsToFocusedTaskOrHome();
- }
- } else if (event.triggeredFromHomeKey) {
- dismissRecentsToHome(true /* animateTaskViews */);
-
- // Cancel any pending dozes
- EventBus.getDefault().send(mUserInteractionEvent);
- } else {
- // Do nothing
- }
- }
-
- public final void onBusEvent(EnterRecentsWindowLastAnimationFrameEvent event) {
- mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
- mRecentsView.invalidate();
- }
-
- public final void onBusEvent(ExitRecentsWindowFirstAnimationFrameEvent event) {
- mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
- mRecentsView.invalidate();
- }
-
- public final void onBusEvent(DockedFirstAnimationFrameEvent event) {
- mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
- mRecentsView.invalidate();
- }
-
- public final void onBusEvent(CancelEnterRecentsWindowAnimationEvent event) {
- RecentsActivityLaunchState launchState = LegacyRecentsImpl.getConfiguration().getLaunchState();
- int launchToTaskId = launchState.launchedToTaskId;
- if (launchToTaskId != -1 &&
- (event.launchTask == null || launchToTaskId != event.launchTask.key.id)) {
- ActivityManagerWrapper am = ActivityManagerWrapper.getInstance();
- am.cancelWindowTransition(launchState.launchedToTaskId);
- }
- }
-
- public final void onBusEvent(ShowApplicationInfoEvent event) {
- // Create a new task stack with the application info details activity
- Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
- Uri.fromParts("package", event.task.key.getComponent().getPackageName(), null));
- intent.setComponent(intent.resolveActivity(getPackageManager()));
- TaskStackBuilder.create(this)
- .addNextIntentWithParentStack(intent).startActivities(null,
- new UserHandle(event.task.key.userId));
-
- // Keep track of app-info invocations
- MetricsLogger.count(this, "overview_app_info", 1);
- }
-
- public final void onBusEvent(ShowIncompatibleAppOverlayEvent event) {
- if (mIncompatibleAppOverlay == null) {
- mIncompatibleAppOverlay = Utilities.findViewStubById(this,
- R.id.incompatible_app_overlay_stub).inflate();
- mIncompatibleAppOverlay.setWillNotDraw(false);
- mIncompatibleAppOverlay.setVisibility(View.VISIBLE);
- }
- mIncompatibleAppOverlay.animate()
- .alpha(1f)
- .setDuration(INCOMPATIBLE_APP_ALPHA_DURATION)
- .setInterpolator(Interpolators.ALPHA_IN)
- .start();
- }
-
- public final void onBusEvent(HideIncompatibleAppOverlayEvent event) {
- if (mIncompatibleAppOverlay != null) {
- mIncompatibleAppOverlay.animate()
- .alpha(0f)
- .setDuration(INCOMPATIBLE_APP_ALPHA_DURATION)
- .setInterpolator(Interpolators.ALPHA_OUT)
- .start();
- }
- }
-
- public final void onBusEvent(DeleteTaskDataEvent event) {
- // Remove any stored data from the loader
- RecentsTaskLoader loader = LegacyRecentsImpl.getTaskLoader();
- loader.deleteTaskData(event.task, false);
-
- // Remove the task from activity manager
- ActivityManagerWrapper.getInstance().removeTask(event.task.key.id);
- }
-
- public final void onBusEvent(TaskViewDismissedEvent event) {
- mRecentsView.updateScrimOpacity();
- }
-
- public final void onBusEvent(AllTaskViewsDismissedEvent event) {
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- if (ssp.hasDockedTask()) {
- mRecentsView.showEmptyView(event.msgResId);
- } else {
- // Just go straight home (no animation necessary because there are no more task views)
- dismissRecentsToHome(false /* animateTaskViews */);
- }
-
- // Keep track of all-deletions
- MetricsLogger.count(this, "overview_task_all_dismissed", 1);
- }
-
- public final void onBusEvent(LaunchTaskSucceededEvent event) {
- MetricsLogger.histogram(this, "overview_task_launch_index", event.taskIndexFromStackFront);
- }
-
- public final void onBusEvent(LaunchTaskFailedEvent event) {
- // Return to Home
- dismissRecentsToHome(true /* animateTaskViews */);
-
- MetricsLogger.count(this, "overview_task_launch_failed", 1);
- }
-
- public final void onBusEvent(ScreenPinningRequestEvent event) {
- MetricsLogger.count(this, "overview_screen_pinned", 1);
- }
-
- public final void onBusEvent(StackViewScrolledEvent event) {
- // Once the user has scrolled while holding alt-tab, then we should ignore the release of
- // the key
- mIgnoreAltTabRelease = true;
- }
-
- public final void onBusEvent(final DockedTopTaskEvent event) {
- mRecentsView.getViewTreeObserver().addOnPreDrawListener(mRecentsDrawnEventListener);
- mRecentsView.invalidate();
- }
-
- public final void onBusEvent(final ActivityUnpinnedEvent event) {
- if (mIsVisible) {
- // Skip the configuration change event as the PiP activity does not actually affect the
- // config of recents
- reloadTaskStack(isInMultiWindowMode(), false /* sendConfigChangedEvent */);
- }
- }
-
- private void reloadTaskStack(boolean isInMultiWindowMode, boolean sendConfigChangedEvent) {
- // Reload the task stack completely
- RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- RecentsTaskLoader loader = LegacyRecentsImpl.getTaskLoader();
- RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(this);
- loader.preloadTasks(loadPlan, -1 /* runningTaskId */);
-
- RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
- loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
- loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
- loader.loadTasks(loadPlan, loadOpts);
-
- TaskStack stack = loadPlan.getTaskStack();
- int numStackTasks = stack.getTaskCount();
- boolean showDeferredAnimation = numStackTasks > 0;
-
- if (sendConfigChangedEvent) {
- EventBus.getDefault().send(new ConfigurationChangedEvent(true /* fromMultiWindow */,
- false /* fromDeviceOrientationChange */, false /* fromDisplayDensityChange */,
- numStackTasks > 0));
- }
- EventBus.getDefault().send(new MultiWindowStateChangedEvent(isInMultiWindowMode,
- showDeferredAnimation, stack));
- }
-
- @Override
- public boolean onPreDraw() {
- mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
- return true;
- }
-
- public void onPackageChanged(String packageName, int userId) {
- LegacyRecentsImpl.getTaskLoader().onPackageChanged(packageName);
- EventBus.getDefault().send(new PackagesChangedEvent(packageName, userId));
- }
-
- @Override
- public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
- super.dump(prefix, fd, writer, args);
- EventBus.getDefault().dump(prefix, writer);
- LegacyRecentsImpl.getTaskLoader().dump(prefix, writer);
-
- String id = Integer.toHexString(System.identityHashCode(this));
-
- writer.print(prefix); writer.print(TAG);
- writer.print(" visible="); writer.print(mIsVisible ? "Y" : "N");
- writer.print(" currentTime="); writer.print(System.currentTimeMillis());
- writer.print(" [0x"); writer.print(id); writer.print("]");
- writer.println();
-
- if (mRecentsView != null) {
- mRecentsView.dump(prefix, writer);
- }
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsActivityLaunchState.java
deleted file mode 100644
index 14fda952b7ac..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ /dev/null
@@ -1,53 +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 com.android.systemui.recents;
-
-/**
- * The launch state of the RecentsActivity.
- *
- * Current Constraints:
- * - needed in onStart() before onNewIntent()
- * - needs to be reset when Recents is hidden
- * - needs to be computed in Recents component
- * - needs to be accessible by views
- */
-public class RecentsActivityLaunchState {
-
- public boolean launchedWithAltTab;
- public boolean launchedFromApp;
- // Set if the activity that we launched from entered PiP during the transition into Recents
- public boolean launchedFromPipApp;
- // Set if the next activity that quick-switch will launch is the PiP activity
- public boolean launchedWithNextPipApp;
- public boolean launchedFromHome;
- public boolean launchedViaDragGesture;
- public boolean launchedViaDockGesture;
- public int launchedToTaskId;
- public int launchedNumVisibleTasks;
- public int launchedNumVisibleThumbnails;
-
- public void reset() {
- launchedFromHome = false;
- launchedFromApp = false;
- launchedFromPipApp = false;
- launchedWithNextPipApp = false;
- launchedToTaskId = -1;
- launchedWithAltTab = false;
- launchedViaDragGesture = false;
- launchedViaDockGesture = false;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsConfiguration.java
deleted file mode 100644
index ee53734d175e..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsConfiguration.java
+++ /dev/null
@@ -1,126 +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 com.android.systemui.recents;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-
-import android.os.SystemProperties;
-
-import com.android.systemui.R;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.views.DockState;
-
-/**
- * Represents the dock regions for each orientation.
- */
-class DockRegion {
- public static DockState[] PHONE_LANDSCAPE = {
- // We only allow docking to the left in landscape for now on small devices
- DockState.LEFT
- };
- public static DockState[] PHONE_PORTRAIT = {
- // We only allow docking to the top for now on small devices
- DockState.TOP
- };
- public static DockState[] TABLET_LANDSCAPE = {
- DockState.LEFT,
- DockState.RIGHT
- };
- public static DockState[] TABLET_PORTRAIT = PHONE_PORTRAIT;
-}
-
-/**
- * Application resources that can be retrieved from the application context and are not specifically
- * tied to the current activity.
- */
-public class RecentsConfiguration {
-
- private static final int LARGE_SCREEN_MIN_DP = 600;
- private static final int XLARGE_SCREEN_MIN_DP = 720;
-
- // Launch states
- public RecentsActivityLaunchState mLaunchState = new RecentsActivityLaunchState();
-
- // Since the positions in Recents has to be calculated globally (before the RecentsActivity
- // starts), we need to calculate some resource values ourselves, instead of relying on framework
- // resources.
- public final boolean isLargeScreen;
- public final boolean isXLargeScreen;
- public final int smallestWidth;
-
- /** Misc **/
- public boolean fakeShadows;
- public int svelteLevel;
-
- // Whether this product supports Grid-based Recents. If this is field is set to true, then
- // Recents will layout task views in a grid mode when there's enough space in the screen.
- public boolean isGridEnabled;
-
- // Support for Android Recents for low ram devices. If this field is set to true, then Recents
- // will use the alternative layout.
- public boolean isLowRamDevice;
-
- // Enable drag and drop split from Recents. Disabled for low ram devices.
- public boolean dragToSplitEnabled;
-
- private final Context mAppContext;
-
- public RecentsConfiguration(Context context) {
- // Load only resources that can not change after the first load either through developer
- // settings or via multi window
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- mAppContext = context.getApplicationContext();
- Resources res = mAppContext.getResources();
- fakeShadows = res.getBoolean(R.bool.config_recents_fake_shadows);
- svelteLevel = res.getInteger(R.integer.recents_svelte_level);
- isGridEnabled = SystemProperties.getBoolean("ro.recents.grid", false);
- isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
- dragToSplitEnabled = !isLowRamDevice;
-
- float screenDensity = context.getResources().getDisplayMetrics().density;
- smallestWidth = ssp.getDeviceSmallestWidth();
- isLargeScreen = smallestWidth >= (int) (screenDensity * LARGE_SCREEN_MIN_DP);
- isXLargeScreen = smallestWidth >= (int) (screenDensity * XLARGE_SCREEN_MIN_DP);
- }
-
- /**
- * Returns the activity launch state.
- * TODO: This will be refactored out of RecentsConfiguration.
- */
- public RecentsActivityLaunchState getLaunchState() {
- return mLaunchState;
- }
-
- /**
- * Returns the preferred dock states for the current orientation.
- * @return a list of dock states for device and its orientation
- */
- public DockState[] getDockStatesForCurrentOrientation() {
- boolean isLandscape = mAppContext.getResources().getConfiguration().orientation ==
- Configuration.ORIENTATION_LANDSCAPE;
- RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
- if (config.isLargeScreen) {
- return isLandscape ? DockRegion.TABLET_LANDSCAPE : DockRegion.TABLET_PORTRAIT;
- } else {
- return isLandscape ? DockRegion.PHONE_LANDSCAPE : DockRegion.PHONE_PORTRAIT;
- }
- }
-
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsDebugFlags.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsDebugFlags.java
deleted file mode 100644
index 19185939c553..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsDebugFlags.java
+++ /dev/null
@@ -1,29 +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 com.android.systemui.recents;
-
-public class RecentsDebugFlags {
-
- public static class Static {
- // Enables debug drawing for the transition thumbnail
- public static final boolean EnableTransitionThumbnailDebugMode = false;
-
- // Disables enter and exit transitions for other tasks for low ram devices
- public static final boolean DisableRecentsLowRamEnterExitAnimation = false;
-
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsImpl.java
deleted file mode 100644
index 3e5acabfed49..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsImpl.java
+++ /dev/null
@@ -1,1118 +0,0 @@
-/*
- * 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 com.android.systemui.recents;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.view.View.MeasureSpec;
-
-import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS;
-
-import android.app.ActivityManager;
-import android.app.ActivityOptions;
-import android.app.trust.TrustManager;
-import android.content.ActivityNotFoundException;
-import android.content.ComponentCallbacks2;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.SystemClock;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.MutableBoolean;
-import android.util.Pair;
-import android.view.LayoutInflater;
-import android.view.ViewConfiguration;
-import android.view.WindowManager;
-
-import android.widget.Toast;
-
-import com.android.systemui.Dependency;
-import com.android.systemui.SysUiServiceProvider;
-import com.android.systemui.pip.phone.ForegroundThread;
-import com.google.android.collect.Lists;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.policy.DockedDividerUtils;
-import com.android.systemui.R;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
-import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
-import com.android.systemui.recents.events.activity.HideRecentsEvent;
-import com.android.systemui.recents.events.activity.LaunchMostRecentTaskRequestEvent;
-import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
-import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
-import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
-import com.android.systemui.recents.events.component.ActivityPinnedEvent;
-import com.android.systemui.recents.events.component.ActivityUnpinnedEvent;
-import com.android.systemui.recents.events.component.HidePipMenuEvent;
-import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
-import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
-import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
-import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
-import com.android.systemui.recents.events.ui.TaskSnapshotChangedEvent;
-import com.android.systemui.recents.misc.DozeTrigger;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
-import com.android.systemui.recents.model.RecentsTaskLoadPlan;
-import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
-import com.android.systemui.recents.views.TaskStackLayoutAlgorithm.VisibilityReport;
-import com.android.systemui.recents.views.TaskStackView;
-import com.android.systemui.recents.views.TaskViewHeader;
-import com.android.systemui.recents.views.TaskViewTransform;
-import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
-import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
-import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
-import com.android.systemui.shared.recents.view.RecentsTransition;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.stackdivider.DividerView;
-import com.android.systemui.statusbar.phone.StatusBar;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * An implementation of the Recents component for the current user. For secondary users, this can
- * be called remotely from the system user.
- */
-public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener {
-
- private final static String TAG = "RecentsImpl";
-
- // The minimum amount of time between each recents button press that we will handle
- private final static int MIN_TOGGLE_DELAY_MS = 350;
-
- // The duration within which the user releasing the alt tab (from when they pressed alt tab)
- // that the fast alt-tab animation will run. If the user's alt-tab takes longer than this
- // duration, then we will toggle recents after this duration.
- private final static int FAST_ALT_TAB_DELAY_MS = 225;
-
- private final static ArraySet<TaskKey> EMPTY_SET = new ArraySet<>();
-
- public final static String RECENTS_PACKAGE = "com.android.systemui";
- public final static String RECENTS_ACTIVITY = "com.android.systemui.recents.RecentsActivity";
-
- /**
- * An implementation of SysUiTaskStackChangeListener, that allows us to listen for changes to the system
- * task stacks and update recents accordingly.
- */
- class TaskStackListenerImpl extends SysUiTaskStackChangeListener {
-
- private OverviewProxyService mOverviewProxyService;
-
- public TaskStackListenerImpl() {
- mOverviewProxyService = Dependency.get(OverviewProxyService.class);
- }
-
- @Override
- public void onTaskStackChangedBackground() {
- // Skip background preloading recents in SystemUI if the overview services is bound
- if (mOverviewProxyService.isEnabled()) {
- return;
- }
-
- // Check this is for the right user
- if (!checkCurrentUserId(mContext, false /* debug */)) {
- return;
- }
-
- // Preloads the next task
- RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
- if (config.svelteLevel == RecentsTaskLoader.SVELTE_NONE) {
- Rect windowRect = getWindowRect(null /* windowRectOverride */);
- if (windowRect.isEmpty()) {
- return;
- }
-
- // Load the next task only if we aren't svelte
- ActivityManager.RunningTaskInfo runningTaskInfo =
- ActivityManagerWrapper.getInstance().getRunningTask();
- RecentsTaskLoader loader = LegacyRecentsImpl.getTaskLoader();
- RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext);
- loader.preloadTasks(plan, -1);
- TaskStack stack = plan.getTaskStack();
- RecentsActivityLaunchState launchState = new RecentsActivityLaunchState();
- RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
-
- synchronized (mBackgroundLayoutAlgorithm) {
- // This callback is made when a new activity is launched and the old one is
- // paused so ignore the current activity and try and preload the thumbnail for
- // the previous one.
- updateDummyStackViewLayout(mBackgroundLayoutAlgorithm, stack, windowRect);
-
- // Launched from app is always the worst case (in terms of how many
- // thumbnails/tasks visible)
- launchState.launchedFromApp = true;
- mBackgroundLayoutAlgorithm.update(plan.getTaskStack(), EMPTY_SET, launchState,
- -1 /* lastScrollPPresent */);
- VisibilityReport visibilityReport =
- mBackgroundLayoutAlgorithm.computeStackVisibilityReport(
- stack.getTasks());
-
- launchOpts.runningTaskId = runningTaskInfo != null ? runningTaskInfo.id : -1;
- launchOpts.numVisibleTasks = visibilityReport.numVisibleTasks;
- launchOpts.numVisibleTaskThumbnails = visibilityReport.numVisibleThumbnails;
- launchOpts.onlyLoadForCache = true;
- launchOpts.onlyLoadPausedActivities = true;
- launchOpts.loadThumbnails = true;
- }
- loader.loadTasks(plan, launchOpts);
- }
- }
-
- @Override
- public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
- // Check this is for the right user
- if (!checkCurrentUserId(mContext, false /* debug */)) {
- return;
- }
-
- // This time needs to be fetched the same way the last active time is fetched in
- // {@link TaskRecord#touchActiveTime}
- LegacyRecentsImpl.getConfiguration().getLaunchState().launchedFromPipApp = true;
- LegacyRecentsImpl.getConfiguration().getLaunchState().launchedWithNextPipApp = false;
- EventBus.getDefault().send(new ActivityPinnedEvent(taskId));
- consumeInstanceLoadPlan();
- sLastPipTime = System.currentTimeMillis();
- }
-
- @Override
- public void onActivityUnpinned() {
- // Check this is for the right user
- if (!checkCurrentUserId(mContext, false /* debug */)) {
- return;
- }
-
- EventBus.getDefault().send(new ActivityUnpinnedEvent());
- sLastPipTime = -1;
- }
-
- @Override
- public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
- // Check this is for the right user
- if (!checkCurrentUserId(mContext, false /* debug */)) {
- return;
- }
-
- EventBus.getDefault().send(new TaskSnapshotChangedEvent(taskId, snapshot));
- }
- }
-
- protected static RecentsTaskLoadPlan sInstanceLoadPlan;
- // Stores the last pinned task time
- protected static long sLastPipTime = -1;
- // Stores whether we are waiting for a transition to/from recents to start. During this time,
- // we disallow the user from manually toggling recents until the transition has started.
- private static boolean mWaitingForTransitionStart = false;
- // Stores whether or not the user toggled while we were waiting for a transition to/from
- // recents. In this case, we defer the toggle state until then and apply it immediately after.
- private static boolean mToggleFollowingTransitionStart = true;
-
- private Runnable mResetToggleFlagListener = new Runnable() {
- @Override
- public void run() {
- setWaitingForTransitionStart(false);
- }
- };
-
- private TrustManager mTrustManager;
- protected Context mContext;
- protected Handler mHandler;
- TaskStackListenerImpl mTaskStackListener;
- boolean mDraggingInRecents;
- boolean mLaunchedWhileDocking;
-
- // Task launching
- Rect mTmpBounds = new Rect();
- TaskViewTransform mTmpTransform = new TaskViewTransform();
- int mTaskBarHeight;
-
- // Header (for transition)
- TaskViewHeader mHeaderBar;
- final Object mHeaderBarLock = new Object();
- private TaskStackView mDummyStackView;
- private TaskStackLayoutAlgorithm mBackgroundLayoutAlgorithm;
-
- // Variables to keep track of if we need to start recents after binding
- protected boolean mTriggeredFromAltTab;
- protected long mLastToggleTime;
- DozeTrigger mFastAltTabTrigger = new DozeTrigger(FAST_ALT_TAB_DELAY_MS, new Runnable() {
- @Override
- public void run() {
- // When this fires, then the user has not released alt-tab for at least
- // FAST_ALT_TAB_DELAY_MS milliseconds
- showRecents(mTriggeredFromAltTab, false /* draggingInRecents */, true /* animate */,
- DividerView.INVALID_RECENTS_GROW_TARGET);
- }
- });
-
- private OverviewProxyService.OverviewProxyListener mOverviewProxyListener =
- new OverviewProxyService.OverviewProxyListener() {
- @Override
- public void onConnectionChanged(boolean isConnected) {
- if (!isConnected) {
- // Clear everything when the connection to the overview service
- LegacyRecentsImpl.getTaskLoader().onTrimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
- }
- }
- };
-
- // Used to reset the dummy stack view
- private final TaskStack mEmptyTaskStack = new TaskStack();
-
- public RecentsImpl(Context context) {
- mContext = context;
- mHandler = new Handler();
- mBackgroundLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, null);
-
- // Initialize the static foreground thread
- ForegroundThread.get();
-
- // Register the task stack listener
- mTaskStackListener = new TaskStackListenerImpl();
- ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
-
- // Initialize the static configuration resources
- mDummyStackView = new TaskStackView(mContext);
- reloadResources();
-
- mTrustManager = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
- }
-
- public void onBootCompleted() {
- // Skip preloading tasks if we are already bound to the service
- if (Dependency.get(OverviewProxyService.class).isEnabled()) {
- return;
- }
-
- // When we start, preload the data associated with the previous recent tasks.
- // We can use a new plan since the caches will be the same.
- RecentsTaskLoader loader = LegacyRecentsImpl.getTaskLoader();
- RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext);
- loader.preloadTasks(plan, -1);
- RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
- launchOpts.numVisibleTasks = loader.getIconCacheSize();
- launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize();
- launchOpts.onlyLoadForCache = true;
- loader.loadTasks(plan, launchOpts);
- }
-
- public void onConfigurationChanged() {
- reloadResources();
- mDummyStackView.reloadOnConfigurationChange();
- synchronized (mBackgroundLayoutAlgorithm) {
- mBackgroundLayoutAlgorithm.reloadOnConfigurationChange(mContext);
- }
- }
-
- /**
- * This is only called from the system user's Recents. Secondary users will instead proxy their
- * visibility change events through to the system user via
- * {@link LegacyRecentsImpl#onBusEvent(RecentsVisibilityChangedEvent)}.
- */
- public void onVisibilityChanged(Context context, boolean visible) {
- LegacyRecentsImpl.getSystemServices().setRecentsVisibility(visible);
- }
-
- /**
- * This is only called from the system user's Recents. Secondary users will instead proxy their
- * visibility change events through to the system user via
- * {@link LegacyRecentsImpl#onBusEvent(ScreenPinningRequestEvent)}.
- */
- public void onStartScreenPinning(Context context, int taskId) {
- final StatusBar statusBar = getStatusBar();
- if (statusBar != null) {
- statusBar.showScreenPinningRequest(taskId, false);
- }
- }
-
- public void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents,
- boolean animate, int growTarget) {
- final SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- final MutableBoolean isHomeStackVisible = new MutableBoolean(true);
- final boolean isRecentsVisible = LegacyRecentsImpl.getSystemServices().isRecentsActivityVisible(
- isHomeStackVisible);
- final boolean fromHome = isHomeStackVisible.value;
- final boolean launchedWhileDockingTask =
- LegacyRecentsImpl.getSystemServices().getSplitScreenPrimaryStack() != null;
-
- mTriggeredFromAltTab = triggeredFromAltTab;
- mDraggingInRecents = draggingInRecents;
- mLaunchedWhileDocking = launchedWhileDockingTask;
- if (mFastAltTabTrigger.isAsleep()) {
- // Fast alt-tab duration has elapsed, fall through to showing Recents and reset
- mFastAltTabTrigger.stopDozing();
- } else if (mFastAltTabTrigger.isDozing()) {
- // Fast alt-tab duration has not elapsed. If this is triggered by a different
- // showRecents() call, then ignore that call for now.
- // TODO: We can not handle quick tabs that happen between the initial showRecents() call
- // that started the activity and the activity starting up. The severity of this
- // is inversely proportional to the FAST_ALT_TAB_DELAY_MS duration though.
- if (!triggeredFromAltTab) {
- return;
- }
- mFastAltTabTrigger.stopDozing();
- } else if (triggeredFromAltTab) {
- // The fast alt-tab detector is not yet running, so start the trigger and wait for the
- // hideRecents() call, or for the fast alt-tab duration to elapse
- mFastAltTabTrigger.startDozing();
- return;
- }
-
- try {
- // Check if the top task is in the home stack, and start the recents activity
- final boolean forceVisible = launchedWhileDockingTask || draggingInRecents;
- if (forceVisible || !isRecentsVisible) {
- ActivityManager.RunningTaskInfo runningTask =
- ActivityManagerWrapper.getInstance().getRunningTask();
- startRecentsActivityAndDismissKeyguardIfNeeded(runningTask,
- isHomeStackVisible.value || fromHome, animate, growTarget);
- }
- } catch (ActivityNotFoundException e) {
- Log.e(TAG, "Failed to launch RecentsActivity", e);
- }
- }
-
- public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
- if (triggeredFromAltTab && mFastAltTabTrigger.isDozing()) {
- // The user has released alt-tab before the trigger has run, so just show the next
- // task immediately
- showNextTask();
-
- // Cancel the fast alt-tab trigger
- mFastAltTabTrigger.stopDozing();
- return;
- }
-
- // Defer to the activity to handle hiding recents, if it handles it, then it must still
- // be visible
- EventBus.getDefault().post(new HideRecentsEvent(triggeredFromAltTab,
- triggeredFromHomeKey));
- }
-
- public void toggleRecents(int growTarget) {
- if (ActivityManagerWrapper.getInstance().isScreenPinningActive()) {
- return;
- }
-
- // Skip this toggle if we are already waiting to trigger recents via alt-tab
- if (mFastAltTabTrigger.isDozing()) {
- return;
- }
-
- if (mWaitingForTransitionStart) {
- mToggleFollowingTransitionStart = true;
- return;
- }
-
- mDraggingInRecents = false;
- mLaunchedWhileDocking = false;
- mTriggeredFromAltTab = false;
-
- try {
- MutableBoolean isHomeStackVisible = new MutableBoolean(true);
- long elapsedTime = SystemClock.elapsedRealtime() - mLastToggleTime;
-
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- if (ssp.isRecentsActivityVisible(isHomeStackVisible)) {
- RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- if (!launchState.launchedWithAltTab) {
- if (LegacyRecentsImpl.getConfiguration().isGridEnabled) {
- // Has the user tapped quickly?
- boolean isQuickTap = elapsedTime < ViewConfiguration.getDoubleTapTimeout();
- if (isQuickTap) {
- EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
- } else {
- EventBus.getDefault().post(new LaunchMostRecentTaskRequestEvent());
- }
- } else {
- // Launch the next focused task
- EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
- }
- } else {
- // If the user has toggled it too quickly, then just eat up the event here (it's
- // better than showing a janky screenshot).
- // NOTE: Ideally, the screenshot mechanism would take the window transform into
- // account
- if (elapsedTime < MIN_TOGGLE_DELAY_MS) {
- return;
- }
-
- EventBus.getDefault().post(new ToggleRecentsEvent());
- mLastToggleTime = SystemClock.elapsedRealtime();
- }
- return;
- } else {
- // If the user has toggled it too quickly, then just eat up the event here (it's
- // better than showing a janky screenshot).
- // NOTE: Ideally, the screenshot mechanism would take the window transform into
- // account
- if (elapsedTime < MIN_TOGGLE_DELAY_MS) {
- return;
- }
-
- // Otherwise, start the recents activity
- ActivityManager.RunningTaskInfo runningTask =
- ActivityManagerWrapper.getInstance().getRunningTask();
- startRecentsActivityAndDismissKeyguardIfNeeded(runningTask,
- isHomeStackVisible.value, true /* animate */, growTarget);
-
- // Only close the other system windows if we are actually showing recents
- ActivityManagerWrapper.getInstance().closeSystemWindows(
- SYSTEM_DIALOG_REASON_RECENT_APPS);
- mLastToggleTime = SystemClock.elapsedRealtime();
- }
- } catch (ActivityNotFoundException e) {
- Log.e(TAG, "Failed to launch RecentsActivity", e);
- }
- }
-
- public void preloadRecents() {
- if (ActivityManagerWrapper.getInstance().isScreenPinningActive()) {
- return;
- }
-
- // Skip preloading recents when keyguard is showing
- final StatusBar statusBar = getStatusBar();
- if (statusBar != null && statusBar.isKeyguardShowing()) {
- return;
- }
-
- // Preload only the raw task list into a new load plan (which will be consumed by the
- // RecentsActivity) only if there is a task to animate to. Post this to ensure that we
- // don't block the touch feedback on the nav bar button which triggers this.
- mHandler.post(() -> {
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- if (!ssp.isRecentsActivityVisible(null)) {
- ActivityManager.RunningTaskInfo runningTask =
- ActivityManagerWrapper.getInstance().getRunningTask();
- if (runningTask == null) {
- return;
- }
-
- RecentsTaskLoader loader = LegacyRecentsImpl.getTaskLoader();
- sInstanceLoadPlan = new RecentsTaskLoadPlan(mContext);
- loader.preloadTasks(sInstanceLoadPlan, runningTask.id);
- TaskStack stack = sInstanceLoadPlan.getTaskStack();
- if (stack.getTaskCount() > 0) {
- // Only preload the icon (but not the thumbnail since it may not have been taken
- // for the pausing activity)
- preloadIcon(runningTask.id);
-
- // At this point, we don't know anything about the stack state. So only
- // calculate the dimensions of the thumbnail that we need for the transition
- // into Recents, but do not draw it until we construct the activity options when
- // we start Recents
- updateHeaderBarLayout(stack, null /* window rect override*/);
- }
- }
- });
- }
-
- public void cancelPreloadingRecents() {
- // Do nothing
- }
-
- public void onDraggingInRecents(float distanceFromTop) {
- EventBus.getDefault().sendOntoMainThread(new DraggingInRecentsEvent(distanceFromTop));
- }
-
- public void onDraggingInRecentsEnded(float velocity) {
- EventBus.getDefault().sendOntoMainThread(new DraggingInRecentsEndedEvent(velocity));
- }
-
- public void onShowCurrentUserToast(int msgResId, int msgLength) {
- Toast.makeText(mContext, msgResId, msgLength).show();
- }
-
- /**
- * Transitions to the next recent task in the stack.
- */
- public void showNextTask() {
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- RecentsTaskLoader loader = LegacyRecentsImpl.getTaskLoader();
- RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext);
- loader.preloadTasks(plan, -1);
- TaskStack focusedStack = plan.getTaskStack();
-
- // Return early if there are no tasks in the focused stack
- if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
-
- // Return early if there is no running task
- ActivityManager.RunningTaskInfo runningTask =
- ActivityManagerWrapper.getInstance().getRunningTask();
- if (runningTask == null) return;
-
- // Find the task in the recents list
- boolean isRunningTaskInHomeStack =
- runningTask.configuration.windowConfiguration.getActivityType()
- == ACTIVITY_TYPE_HOME;
- ArrayList<Task> tasks = focusedStack.getTasks();
- Task toTask = null;
- ActivityOptions launchOpts = null;
- int taskCount = tasks.size();
- for (int i = taskCount - 1; i >= 1; i--) {
- Task task = tasks.get(i);
- if (isRunningTaskInHomeStack) {
- toTask = tasks.get(i - 1);
- launchOpts = ActivityOptions.makeCustomAnimation(mContext,
- R.anim.recents_launch_next_affiliated_task_target,
- R.anim.recents_fast_toggle_app_home_exit);
- break;
- } else if (task.key.id == runningTask.id) {
- toTask = tasks.get(i - 1);
- launchOpts = ActivityOptions.makeCustomAnimation(mContext,
- R.anim.recents_launch_prev_affiliated_task_target,
- R.anim.recents_launch_prev_affiliated_task_source);
- break;
- }
- }
-
- // Return early if there is no next task
- if (toTask == null) {
- ssp.startInPlaceAnimationOnFrontMostApplication(
- ActivityOptions.makeCustomInPlaceAnimation(mContext,
- R.anim.recents_launch_prev_affiliated_task_bounce));
- return;
- }
-
- // Launch the task
- ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(toTask.key, launchOpts,
- null /* resultCallback */, null /* resultCallbackHandler */);
- }
-
- /**
- * Transitions to the next affiliated task.
- */
- public void showRelativeAffiliatedTask(boolean showNextTask) {
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- RecentsTaskLoader loader = LegacyRecentsImpl.getTaskLoader();
- RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext);
- loader.preloadTasks(plan, -1);
- TaskStack focusedStack = plan.getTaskStack();
-
- // Return early if there are no tasks in the focused stack
- if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
-
- // Return early if there is no running task (can't determine affiliated tasks in this case)
- ActivityManager.RunningTaskInfo runningTask =
- ActivityManagerWrapper.getInstance().getRunningTask();
- final int activityType = runningTask.configuration.windowConfiguration.getActivityType();
- if (runningTask == null) return;
- // Return early if the running task is in the home/recents stack (optimization)
- if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) return;
-
- // Find the task in the recents list
- ArrayList<Task> tasks = focusedStack.getTasks();
- Task toTask = null;
- ActivityOptions launchOpts = null;
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task task = tasks.get(i);
- if (task.key.id == runningTask.id) {
- if (showNextTask) {
- if ((i + 1) < taskCount) {
- toTask = tasks.get(i + 1);
- launchOpts = ActivityOptions.makeCustomAnimation(mContext,
- R.anim.recents_launch_next_affiliated_task_target,
- R.anim.recents_launch_next_affiliated_task_source);
- }
- } else {
- if ((i - 1) >= 0) {
- toTask = tasks.get(i - 1);
- launchOpts = ActivityOptions.makeCustomAnimation(mContext,
- R.anim.recents_launch_prev_affiliated_task_target,
- R.anim.recents_launch_prev_affiliated_task_source);
- }
- }
- break;
- }
- }
-
- // Return early if there is no next task
- if (toTask == null) {
- if (showNextTask) {
- ssp.startInPlaceAnimationOnFrontMostApplication(
- ActivityOptions.makeCustomInPlaceAnimation(mContext,
- R.anim.recents_launch_next_affiliated_task_bounce));
- } else {
- ssp.startInPlaceAnimationOnFrontMostApplication(
- ActivityOptions.makeCustomInPlaceAnimation(mContext,
- R.anim.recents_launch_prev_affiliated_task_bounce));
- }
- return;
- }
-
- // Keep track of actually launched affiliated tasks
- MetricsLogger.count(mContext, "overview_affiliated_task_launch", 1);
-
- // Launch the task
- ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(toTask.key, launchOpts,
- null /* resultListener */, null /* resultCallbackHandler */);
- }
-
- public void splitPrimaryTask(int taskId, int stackCreateMode, Rect initialBounds) {
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
-
- // Make sure we inform DividerView before we actually start the activity so we can change
- // the resize mode already.
- if (ssp.setTaskWindowingModeSplitScreenPrimary(taskId, stackCreateMode, initialBounds)) {
- EventBus.getDefault().send(new DockedTopTaskEvent(initialBounds));
- }
- }
-
- public void setWaitingForTransitionStart(boolean waitingForTransitionStart) {
- if (mWaitingForTransitionStart == waitingForTransitionStart) {
- return;
- }
-
- mWaitingForTransitionStart = waitingForTransitionStart;
- if (!waitingForTransitionStart && mToggleFollowingTransitionStart) {
- mHandler.post(() -> toggleRecents(DividerView.INVALID_RECENTS_GROW_TARGET));
- }
- mToggleFollowingTransitionStart = false;
- }
-
- /**
- * Returns the preloaded load plan and invalidates it.
- */
- public static RecentsTaskLoadPlan consumeInstanceLoadPlan() {
- RecentsTaskLoadPlan plan = sInstanceLoadPlan;
- sInstanceLoadPlan = null;
- return plan;
- }
-
- /**
- * @return the time at which a task last entered picture-in-picture.
- */
- public static long getLastPipTime() {
- return sLastPipTime;
- }
-
- /**
- * Clears the time at which a task last entered picture-in-picture.
- */
- public static void clearLastPipTime() {
- sLastPipTime = -1;
- }
-
- /**
- * Reloads all the resources for the current configuration.
- */
- private void reloadResources() {
- Resources res = mContext.getResources();
-
- mTaskBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(mContext,
- R.dimen.recents_task_view_header_height,
- R.dimen.recents_task_view_header_height,
- R.dimen.recents_task_view_header_height,
- R.dimen.recents_task_view_header_height_tablet_land,
- R.dimen.recents_task_view_header_height,
- R.dimen.recents_task_view_header_height_tablet_land,
- R.dimen.recents_grid_task_view_header_height);
-
- LayoutInflater inflater = LayoutInflater.from(mContext);
- mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
- null, false);
- mHeaderBar.setLayoutDirection(res.getConfiguration().getLayoutDirection());
- }
-
- private void updateDummyStackViewLayout(TaskStackLayoutAlgorithm stackLayout,
- TaskStack stack, Rect windowRect) {
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- Rect displayRect = ssp.getDisplayRect();
- Rect systemInsets = new Rect();
- ssp.getStableInsets(systemInsets);
-
- // When docked, the nav bar insets are consumed and the activity is measured without insets.
- // However, the window bounds include the insets, so we need to subtract them here to make
- // them identical.
- if (ssp.hasDockedTask()) {
- if (systemInsets.bottom < windowRect.height()) {
- // Only apply inset if it isn't going to cause the rect height to go negative.
- windowRect.bottom -= systemInsets.bottom;
- }
- systemInsets.bottom = 0;
- }
- calculateWindowStableInsets(systemInsets, windowRect, displayRect);
- windowRect.offsetTo(0, 0);
-
- // Rebind the header bar and draw it for the transition
- stackLayout.setSystemInsets(systemInsets);
- if (stack != null) {
- stackLayout.getTaskStackBounds(displayRect, windowRect, systemInsets.top,
- systemInsets.left, systemInsets.right, mTmpBounds);
- stackLayout.reset();
- stackLayout.initialize(displayRect, windowRect, mTmpBounds);
- }
- }
-
- private Rect getWindowRect(Rect windowRectOverride) {
- return windowRectOverride != null
- ? new Rect(windowRectOverride)
- : LegacyRecentsImpl.getSystemServices().getWindowRect();
- }
-
- /**
- * Prepares the header bar layout for the next transition, if the task view bounds has changed
- * since the last call, it will attempt to re-measure and layout the header bar to the new size.
- *
- * @param stack the stack to initialize the stack layout with
- * @param windowRectOverride the rectangle to use when calculating the stack state which can
- * be different from the current window rect if recents is resizing
- * while being launched
- */
- private void updateHeaderBarLayout(TaskStack stack, Rect windowRectOverride) {
- Rect windowRect = getWindowRect(windowRectOverride);
- int taskViewWidth = 0;
- boolean useGridLayout = mDummyStackView.useGridLayout();
- updateDummyStackViewLayout(mDummyStackView.getStackAlgorithm(), stack, windowRect);
- if (stack != null) {
- TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
- mDummyStackView.getStack().removeAllTasks(false /* notifyStackChanges */);
- mDummyStackView.setTasks(stack, false /* allowNotifyStackChanges */);
- // Get the width of a task view so that we know how wide to draw the header bar.
- if (useGridLayout) {
- TaskGridLayoutAlgorithm gridLayout = mDummyStackView.getGridAlgorithm();
- gridLayout.initialize(windowRect);
- taskViewWidth = (int) gridLayout.getTransform(0 /* taskIndex */,
- stack.getTaskCount(), new TaskViewTransform(),
- stackLayout).rect.width();
- } else {
- Rect taskViewBounds = stackLayout.getUntransformedTaskViewBounds();
- if (!taskViewBounds.isEmpty()) {
- taskViewWidth = taskViewBounds.width();
- }
- }
- }
-
- if (stack != null && taskViewWidth > 0) {
- synchronized (mHeaderBarLock) {
- if (mHeaderBar.getMeasuredWidth() != taskViewWidth ||
- mHeaderBar.getMeasuredHeight() != mTaskBarHeight) {
- if (useGridLayout) {
- mHeaderBar.setShouldDarkenBackgroundColor(true);
- mHeaderBar.setNoUserInteractionState();
- }
- mHeaderBar.forceLayout();
- mHeaderBar.measure(
- MeasureSpec.makeMeasureSpec(taskViewWidth, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(mTaskBarHeight, MeasureSpec.EXACTLY));
- }
- mHeaderBar.layout(0, 0, taskViewWidth, mTaskBarHeight);
- }
- }
- }
-
- /**
- * Given the stable insets and the rect for our window, calculates the insets that affect our
- * window.
- */
- private void calculateWindowStableInsets(Rect inOutInsets, Rect windowRect, Rect displayRect) {
-
- // Display rect without insets - available app space
- Rect appRect = new Rect(displayRect);
- appRect.inset(inOutInsets);
-
- // Our window intersected with available app space
- Rect windowRectWithInsets = new Rect(windowRect);
- windowRectWithInsets.intersect(appRect);
- inOutInsets.left = windowRectWithInsets.left - windowRect.left;
- inOutInsets.top = windowRectWithInsets.top - windowRect.top;
- inOutInsets.right = windowRect.right - windowRectWithInsets.right;
- inOutInsets.bottom = windowRect.bottom - windowRectWithInsets.bottom;
- }
-
- /**
- * Preloads the icon of a task.
- */
- private void preloadIcon(int runningTaskId) {
- // Ensure that we load the running task's icon
- RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
- launchOpts.runningTaskId = runningTaskId;
- launchOpts.loadThumbnails = false;
- launchOpts.onlyLoadForCache = true;
- LegacyRecentsImpl.getTaskLoader().loadTasks(sInstanceLoadPlan, launchOpts);
- }
-
- /**
- * Creates the activity options for a unknown state->recents transition.
- */
- protected ActivityOptions getUnknownTransitionActivityOptions() {
- return ActivityOptions.makeCustomAnimation(mContext,
- R.anim.recents_from_unknown_enter,
- R.anim.recents_from_unknown_exit,
- mHandler, null);
- }
-
- /**
- * Creates the activity options for a home->recents transition.
- */
- protected ActivityOptions getHomeTransitionActivityOptions() {
- return ActivityOptions.makeCustomAnimation(mContext,
- R.anim.recents_from_launcher_enter,
- R.anim.recents_from_launcher_exit,
- mHandler, null);
- }
-
- /**
- * Creates the activity options for an app->recents transition.
- */
- private Pair<ActivityOptions, AppTransitionAnimationSpecsFuture>
- getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo runningTask,
- Rect windowOverrideRect) {
- final boolean isLowRamDevice = LegacyRecentsImpl.getConfiguration().isLowRamDevice;
-
- // Update the destination rect
- Task toTask = new Task();
- TaskViewTransform toTransform = getThumbnailTransitionTransform(mDummyStackView, toTask,
- windowOverrideRect);
-
- RectF toTaskRect = toTransform.rect;
- AppTransitionAnimationSpecsFuture future = new AppTransitionAnimationSpecsFuture(mHandler) {
- @Override
- public List<AppTransitionAnimationSpecCompat> composeSpecs() {
- Rect rect = new Rect();
- toTaskRect.round(rect);
- Bitmap thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform);
- return Lists.newArrayList(new AppTransitionAnimationSpecCompat(toTask.key.id,
- thumbnail, rect));
- }
- };
-
- // For low end ram devices, wait for transition flag is reset when Recents entrance
- // animation is complete instead of when the transition animation starts
- return new Pair<>(RecentsTransition.createAspectScaleAnimation(mContext, mHandler,
- false /* scaleUp */, future, isLowRamDevice ? null : mResetToggleFlagListener),
- future);
- }
-
- /**
- * Returns the transition rect for the given task id.
- */
- private TaskViewTransform getThumbnailTransitionTransform(TaskStackView stackView,
- Task runningTaskOut, Rect windowOverrideRect) {
- // Find the running task in the TaskStack
- TaskStack stack = stackView.getStack();
- Task launchTask = stack.getLaunchTarget();
- if (launchTask != null) {
- runningTaskOut.copyFrom(launchTask);
- } else {
- // If no task is specified or we can not find the task just use the front most one
- launchTask = stack.getFrontMostTask();
- runningTaskOut.copyFrom(launchTask);
- }
-
- // Get the transform for the running task
- stackView.updateLayoutAlgorithm(true /* boundScroll */);
- stackView.updateToInitialState();
- stackView.getStackAlgorithm().getStackTransformScreenCoordinates(launchTask,
- stackView.getScroller().getStackScroll(), mTmpTransform, null, windowOverrideRect);
- return mTmpTransform;
- }
-
- /**
- * Draws the header of a task used for the window animation into a bitmap.
- */
- private Bitmap drawThumbnailTransitionBitmap(Task toTask,
- TaskViewTransform toTransform) {
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- int width = (int) toTransform.rect.width();
- int height = (int) toTransform.rect.height();
- if (toTransform != null && toTask.key != null && width > 0 && height > 0) {
- synchronized (mHeaderBarLock) {
- boolean disabledInSafeMode = !toTask.isSystemApp && ssp.isInSafeMode();
- mHeaderBar.onTaskViewSizeChanged(width, height);
- if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
- return RecentsTransition.drawViewIntoHardwareBitmap(width, mTaskBarHeight,
- null, 1f, 0xFFff0000);
- } else {
- // Workaround for b/27815919, reset the callback so that we do not trigger an
- // invalidate on the header bar as a result of updating the icon
- Drawable icon = mHeaderBar.getIconView().getDrawable();
- if (icon != null) {
- icon.setCallback(null);
- }
- mHeaderBar.bindToTask(toTask, false /* touchExplorationEnabled */,
- disabledInSafeMode);
- mHeaderBar.onTaskDataLoaded();
- mHeaderBar.setDimAlpha(toTransform.dimAlpha);
- return RecentsTransition.drawViewIntoHardwareBitmap(width, mTaskBarHeight,
- mHeaderBar, 1f, 0);
- }
- }
- }
- return null;
- }
-
- /**
- * Shows the recents activity after dismissing the keyguard if visible
- */
- protected void startRecentsActivityAndDismissKeyguardIfNeeded(
- final ActivityManager.RunningTaskInfo runningTask, final boolean isHomeStackVisible,
- final boolean animate, final int growTarget) {
- // Preload only if device for current user is unlocked
- final StatusBar statusBar = getStatusBar();
- if (statusBar != null && statusBar.isKeyguardShowing()) {
- statusBar.executeRunnableDismissingKeyguard(() -> {
- // Flush trustmanager before checking device locked per user when preloading
- mTrustManager.reportKeyguardShowingChanged();
- mHandler.post(() -> startRecentsActivity(runningTask, isHomeStackVisible,
- animate, growTarget));
- }, null, true /* dismissShade */, false /* afterKeyguardGone */,
- true /* deferred */);
- } else {
- startRecentsActivity(runningTask, isHomeStackVisible, animate, growTarget);
- }
- }
-
- private void startRecentsActivity(ActivityManager.RunningTaskInfo runningTask,
- boolean isHomeStackVisible, boolean animate, int growTarget) {
- RecentsTaskLoader loader = LegacyRecentsImpl.getTaskLoader();
- RecentsActivityLaunchState launchState = LegacyRecentsImpl.getConfiguration().getLaunchState();
-
- int runningTaskId = !mLaunchedWhileDocking && (runningTask != null)
- ? runningTask.id
- : -1;
-
- // In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
- // should always preload the tasks now. If we are dragging in recents, reload them as
- // the stacks might have changed.
- if (mLaunchedWhileDocking || mTriggeredFromAltTab || sInstanceLoadPlan == null) {
- // Create a new load plan if preloadRecents() was never triggered
- sInstanceLoadPlan = new RecentsTaskLoadPlan(mContext);
- }
- if (mLaunchedWhileDocking || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
- loader.preloadTasks(sInstanceLoadPlan, runningTaskId);
- }
-
- TaskStack stack = sInstanceLoadPlan.getTaskStack();
- boolean hasRecentTasks = stack.getTaskCount() > 0;
- boolean useThumbnailTransition = (runningTask != null) && !isHomeStackVisible &&
- hasRecentTasks;
-
- // Update the launch state that we need in updateHeaderBarLayout()
- launchState.launchedFromHome = !useThumbnailTransition && !mLaunchedWhileDocking;
- launchState.launchedFromApp = useThumbnailTransition || mLaunchedWhileDocking;
- launchState.launchedFromPipApp = false;
- launchState.launchedWithNextPipApp =
- stack.isNextLaunchTargetPip(RecentsImpl.getLastPipTime());
- launchState.launchedViaDockGesture = mLaunchedWhileDocking;
- launchState.launchedViaDragGesture = mDraggingInRecents;
- launchState.launchedToTaskId = runningTaskId;
- launchState.launchedWithAltTab = mTriggeredFromAltTab;
-
- // Disable toggling of recents between starting the activity and it is visible and the app
- // has started its transition into recents.
- setWaitingForTransitionStart(useThumbnailTransition);
-
- // Preload the icon (this will be a null-op if we have preloaded the icon already in
- // preloadRecents())
- preloadIcon(runningTaskId);
-
- // Update the header bar if necessary
- Rect windowOverrideRect = getWindowRectOverride(growTarget);
- updateHeaderBarLayout(stack, windowOverrideRect);
-
- // Prepare the dummy stack for the transition
- TaskStackLayoutAlgorithm.VisibilityReport stackVr =
- mDummyStackView.computeStackVisibilityReport();
-
- // Update the remaining launch state
- launchState.launchedNumVisibleTasks = stackVr.numVisibleTasks;
- launchState.launchedNumVisibleThumbnails = stackVr.numVisibleThumbnails;
-
- if (!animate) {
- startRecentsActivity(ActivityOptions.makeCustomAnimation(mContext, -1, -1),
- null /* future */);
- return;
- }
-
- Pair<ActivityOptions, AppTransitionAnimationSpecsFuture> pair;
- if (useThumbnailTransition) {
- // Try starting with a thumbnail transition
- pair = getThumbnailTransitionActivityOptions(runningTask, windowOverrideRect);
- } else {
- // If there is no thumbnail transition, but is launching from home into recents, then
- // use a quick home transition
- pair = new Pair<>(hasRecentTasks
- ? getHomeTransitionActivityOptions()
- : getUnknownTransitionActivityOptions(), null);
- }
- startRecentsActivity(pair.first, pair.second);
- mLastToggleTime = SystemClock.elapsedRealtime();
- }
-
- private Rect getWindowRectOverride(int growTarget) {
- if (growTarget == DividerView.INVALID_RECENTS_GROW_TARGET) {
- return SystemServicesProxy.getInstance(mContext).getWindowRect();
- }
- Rect result = new Rect();
- Rect displayRect = LegacyRecentsImpl.getSystemServices().getDisplayRect();
- DockedDividerUtils.calculateBoundsForPosition(growTarget, WindowManager.DOCKED_BOTTOM,
- result, displayRect.width(), displayRect.height(),
- LegacyRecentsImpl.getSystemServices().getDockedDividerSize(mContext));
- return result;
- }
-
- private StatusBar getStatusBar() {
- return SysUiServiceProvider.getComponent(mContext, StatusBar.class);
- }
-
- /**
- * Starts the recents activity.
- */
- private void startRecentsActivity(ActivityOptions opts,
- final AppTransitionAnimationSpecsFuture future) {
- Intent intent = new Intent();
- intent.setClassName(RECENTS_PACKAGE, RECENTS_ACTIVITY);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
- HidePipMenuEvent hideMenuEvent = new HidePipMenuEvent();
- hideMenuEvent.addPostAnimationCallback(() -> {
- LegacyRecentsImpl.getSystemServices().startActivityAsUserAsync(intent, opts);
- EventBus.getDefault().send(new RecentsActivityStartingEvent());
- if (future != null) {
- future.composeSpecsSynchronous();
- }
- });
- EventBus.getDefault().send(hideMenuEvent);
-
- // Once we have launched the activity, reset the dummy stack view tasks so we don't hold
- // onto references to the same tasks consumed by the activity
- mDummyStackView.setTasks(mEmptyTaskStack, false /* notifyStackChanges */);
- }
-
- /**** OnAnimationFinishedListener Implementation ****/
-
- @Override
- public void onAnimationFinished() {
- EventBus.getDefault().post(new EnterRecentsWindowLastAnimationFrameEvent());
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsImplProxy.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsImplProxy.java
deleted file mode 100644
index a1da785f2a80..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsImplProxy.java
+++ /dev/null
@@ -1,161 +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.recents;
-
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Message;
-import android.os.RemoteException;
-
-import com.android.internal.os.SomeArgs;
-
-/**
- * A proxy class which directs all methods from {@link IRecentsNonSystemUserCallbacks} to
- * {@link RecentsImpl} and makes sure they are called from the main thread.
- */
-public class RecentsImplProxy extends IRecentsNonSystemUserCallbacks.Stub {
-
- private static final int MSG_PRELOAD_RECENTS = 1;
- private static final int MSG_CANCEL_PRELOADING_RECENTS = 2;
- private static final int MSG_SHOW_RECENTS = 3;
- private static final int MSG_HIDE_RECENTS = 4;
- private static final int MSG_TOGGLE_RECENTS = 5;
- private static final int MSG_ON_CONFIGURATION_CHANGED = 6;
- private static final int MSG_DOCK_TOP_TASK = 7;
- private static final int MSG_ON_DRAGGING_IN_RECENTS = 8;
- private static final int MSG_ON_DRAGGING_IN_RECENTS_ENDED = 9;
- private static final int MSG_SHOW_USER_TOAST = 10;
-
- private RecentsImpl mImpl;
-
- public RecentsImplProxy(RecentsImpl recentsImpl) {
- mImpl = recentsImpl;
- }
-
- @Override
- public void preloadRecents() throws RemoteException {
- mHandler.sendEmptyMessage(MSG_PRELOAD_RECENTS);
- }
-
- @Override
- public void cancelPreloadingRecents() throws RemoteException {
- mHandler.sendEmptyMessage(MSG_CANCEL_PRELOADING_RECENTS);
- }
-
- @Override
- public void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents, boolean animate,
- int growTarget) throws RemoteException {
- SomeArgs args = SomeArgs.obtain();
- args.argi1 = triggeredFromAltTab ? 1 : 0;
- args.argi2 = draggingInRecents ? 1 : 0;
- args.argi3 = animate ? 1 : 0;
- args.argi4 = growTarget;
- mHandler.sendMessage(mHandler.obtainMessage(MSG_SHOW_RECENTS, args));
- }
-
- @Override
- public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey)
- throws RemoteException {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_HIDE_RECENTS, triggeredFromAltTab ? 1 :0,
- triggeredFromHomeKey ? 1 : 0));
- }
-
- @Override
- public void toggleRecents(int growTarget) throws RemoteException {
- SomeArgs args = SomeArgs.obtain();
- args.argi1 = growTarget;
- mHandler.sendMessage(mHandler.obtainMessage(MSG_TOGGLE_RECENTS, args));
- }
-
- @Override
- public void onConfigurationChanged() throws RemoteException {
- mHandler.sendEmptyMessage(MSG_ON_CONFIGURATION_CHANGED);
- }
-
- @Override
- public void splitPrimaryTask(int topTaskId, int stackCreateMode, Rect initialBounds)
- throws RemoteException {
- SomeArgs args = SomeArgs.obtain();
- args.argi1 = topTaskId;
- args.argi2 = stackCreateMode;
- args.arg1 = initialBounds;
- mHandler.sendMessage(mHandler.obtainMessage(MSG_DOCK_TOP_TASK, args));
- }
-
- @Override
- public void onDraggingInRecents(float distanceFromTop) throws RemoteException {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_DRAGGING_IN_RECENTS, distanceFromTop));
- }
-
- @Override
- public void onDraggingInRecentsEnded(float velocity) throws RemoteException {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_DRAGGING_IN_RECENTS_ENDED, velocity));
- }
-
- @Override
- public void showCurrentUserToast(int msgResId, int msgLength) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_SHOW_USER_TOAST, msgResId, msgLength));
- }
-
- private final Handler mHandler = new Handler() {
-
- @Override
- public void handleMessage(Message msg) {
- SomeArgs args;
- switch (msg.what) {
- case MSG_PRELOAD_RECENTS:
- mImpl.preloadRecents();
- break;
- case MSG_CANCEL_PRELOADING_RECENTS:
- mImpl.cancelPreloadingRecents();
- break;
- case MSG_SHOW_RECENTS:
- args = (SomeArgs) msg.obj;
- mImpl.showRecents(args.argi1 != 0, args.argi2 != 0, args.argi3 != 0,
- args.argi4);
- break;
- case MSG_HIDE_RECENTS:
- mImpl.hideRecents(msg.arg1 != 0, msg.arg2 != 0);
- break;
- case MSG_TOGGLE_RECENTS:
- args = (SomeArgs) msg.obj;
- mImpl.toggleRecents(args.argi1);
- break;
- case MSG_ON_CONFIGURATION_CHANGED:
- mImpl.onConfigurationChanged();
- break;
- case MSG_DOCK_TOP_TASK:
- args = (SomeArgs) msg.obj;
- mImpl.splitPrimaryTask(args.argi1, args.argi2 = 0,
- (Rect) args.arg1);
- break;
- case MSG_ON_DRAGGING_IN_RECENTS:
- mImpl.onDraggingInRecents((Float) msg.obj);
- break;
- case MSG_ON_DRAGGING_IN_RECENTS_ENDED:
- mImpl.onDraggingInRecentsEnded((Float) msg.obj);
- break;
- case MSG_SHOW_USER_TOAST:
- mImpl.onShowCurrentUserToast(msg.arg1, msg.arg2);
- break;
- default:
- super.handleMessage(msg);
- }
- super.handleMessage(msg);
- }
- };
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsSystemUser.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsSystemUser.java
deleted file mode 100644
index c5e9f046aa16..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsSystemUser.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * 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 com.android.systemui.recents;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.EventLog;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.systemui.EventLogConstants;
-import com.android.systemui.EventLogTags;
-import com.android.systemui.pip.phone.ForegroundThread;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
-import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
-import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
-import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
-import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
-
-/**
- * An implementation of the system user's Recents interface to be called remotely by secondary
- * users.
- */
-public class RecentsSystemUser extends IRecentsSystemUserCallbacks.Stub {
-
- private static final String TAG = "RecentsSystemUser";
-
- private Context mContext;
- private RecentsImpl mImpl;
- private final SparseArray<IRecentsNonSystemUserCallbacks> mNonSystemUserRecents =
- new SparseArray<>();
-
- public RecentsSystemUser(Context context, RecentsImpl impl) {
- mContext = context;
- mImpl = impl;
- }
-
- @Override
- public void registerNonSystemUserCallbacks(final IBinder nonSystemUserCallbacks,
- final int userId) {
- try {
- final IRecentsNonSystemUserCallbacks callback =
- IRecentsNonSystemUserCallbacks.Stub.asInterface(nonSystemUserCallbacks);
- nonSystemUserCallbacks.linkToDeath(new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- mNonSystemUserRecents.removeAt(mNonSystemUserRecents.indexOfValue(callback));
- EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,
- EventLogConstants.SYSUI_RECENTS_CONNECTION_SYSTEM_UNREGISTER_USER,
- userId);
- }
- }, 0);
- mNonSystemUserRecents.put(userId, callback);
- EventLog.writeEvent(EventLogTags.SYSUI_RECENTS_CONNECTION,
- EventLogConstants.SYSUI_RECENTS_CONNECTION_SYSTEM_REGISTER_USER, userId);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to register NonSystemUserCallbacks", e);
- }
- }
-
- public IRecentsNonSystemUserCallbacks getNonSystemUserRecentsForUser(int userId) {
- return mNonSystemUserRecents.get(userId);
- }
-
- @Override
- public void updateRecentsVisibility(boolean visible) {
- ForegroundThread.getHandler().post(() -> {
- mImpl.onVisibilityChanged(mContext, visible);
- });
- }
-
- @Override
- public void startScreenPinning(int taskId) {
- ForegroundThread.getHandler().post(() -> {
- mImpl.onStartScreenPinning(mContext, taskId);
- });
- }
-
- @Override
- public void sendRecentsDrawnEvent() {
- EventBus.getDefault().post(new RecentsDrawnEvent());
- }
-
- @Override
- public void sendDockingTopTaskEvent(Rect initialRect) throws RemoteException {
- EventBus.getDefault().post(new DockedTopTaskEvent(initialRect));
- }
-
- @Override
- public void sendLaunchRecentsEvent() throws RemoteException {
- EventBus.getDefault().post(new RecentsActivityStartingEvent());
- }
-
- @Override
- public void sendDockedFirstAnimationFrameEvent() throws RemoteException {
- EventBus.getDefault().post(new DockedFirstAnimationFrameEvent());
- }
-
- @Override
- public void setWaitingForTransitionStartEvent(boolean waitingForTransitionStart) {
- EventBus.getDefault().post(new SetWaitingForTransitionStartEvent(
- waitingForTransitionStart));
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsSystemUserService.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsSystemUserService.java
deleted file mode 100644
index b5a0181c7d56..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsSystemUserService.java
+++ /dev/null
@@ -1,53 +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.systemui.recents;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-import android.util.Log;
-
-import com.android.systemui.SysUiServiceProvider;
-
-/**
- * A strictly system-user service that is started by the secondary user's Recents (with a limited
- * lifespan), to get the interface that the secondary user's Recents can call through to the system
- * user's Recents.
- */
-public class RecentsSystemUserService extends Service {
-
- private static final String TAG = "RecentsSystemUserService";
- private static final boolean DEBUG = false;
-
- @Override
- public void onCreate() {
- super.onCreate();
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- LegacyRecentsImpl recents = SysUiServiceProvider.getComponent(this, LegacyRecentsImpl.class);
- if (DEBUG) {
- Log.d(TAG, "onBind: " + recents);
- }
- if (recents != null) {
- return recents.getSystemUserCallbacks();
- }
- return null;
- }
-}
-
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/EventBus.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/EventBus.java
deleted file mode 100644
index 177362cf60aa..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/EventBus.java
+++ /dev/null
@@ -1,763 +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 com.android.systemui.recents.events;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.util.Log;
-
-import com.android.systemui.recents.misc.ReferenceCountedTrigger;
-
-import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * Represents a subscriber, which implements various event bus handler methods.
- */
-class Subscriber {
- private WeakReference<Object> mSubscriber;
-
- long registrationTime;
-
- Subscriber(Object subscriber, long registrationTime) {
- mSubscriber = new WeakReference<>(subscriber);
- this.registrationTime = registrationTime;
- }
-
- public String toString(int priority) {
- Object sub = mSubscriber.get();
- String id = Integer.toHexString(System.identityHashCode(sub));
- return sub.getClass().getSimpleName() + " [0x" + id + ", P" + priority + "]";
- }
-
- public Object getReference() {
- return mSubscriber.get();
- }
-}
-
-/**
- * Represents an event handler with a priority.
- */
-class EventHandler {
- int priority;
- Subscriber subscriber;
- EventHandlerMethod method;
-
- EventHandler(Subscriber subscriber, EventHandlerMethod method, int priority) {
- this.subscriber = subscriber;
- this.method = method;
- this.priority = priority;
- }
-
- @Override
- public String toString() {
- return subscriber.toString(priority) + " " + method.toString();
- }
-}
-
-/**
- * Represents the low level method handling a particular event.
- */
-class EventHandlerMethod {
- private Method mMethod;
- Class<? extends EventBus.Event> eventType;
-
- EventHandlerMethod(Method method, Class<? extends EventBus.Event> eventType) {
- mMethod = method;
- mMethod.setAccessible(true);
- this.eventType = eventType;
- }
-
- public void invoke(Object target, EventBus.Event event)
- throws InvocationTargetException, IllegalAccessException {
- mMethod.invoke(target, event);
- }
-
- @Override
- public String toString() {
- return mMethod.getName() + "(" + eventType.getSimpleName() + ")";
- }
-}
-
-/**
- * A simple in-process event bus. It is simple because we can make assumptions about the state of
- * SystemUI and Recent's lifecycle.
- *
- * <p>
- * Currently, there is a single EventBus that handles {@link EventBus.Event}s for each subscriber
- * on the main application thread. Publishers can send() events to synchronously call subscribers
- * of that event, or post() events to be processed in the next run of the {@link Looper}.
- *
- * <p>
- * Subscribers must be registered with a particular EventBus before they will receive events, and
- * handler methods must match a specific signature.
- *
- * <p>
- * Event method signature:<ul>
- * <li>Methods must be public final
- * <li>Methods must return void
- * <li>Methods must be called "onBusEvent"
- * <li>Methods must take one parameter, of class type deriving from {@link EventBus.Event}
- * </ul>
- *
- * </p>
- * Each subscriber can be registered with a given priority (default 1), and events will be dispatch
- * in decreasing order of priority. For subscribers with the same priority, events will be
- * dispatched by latest registration time to earliest.
- *
- * <p>
- * Caveats:<ul>
- * <li>The EventBus keeps a {@link WeakReference} to the publisher to prevent memory leaks, so
- * there must be another strong reference to the publisher for it to not get garbage-collected and
- * continue receiving events.
- * <li>Because the event handlers are called back using reflection, the EventBus is not intended
- * for use in tight, performance criticial loops. For most user input/system callback events, this
- * is generally of low enough frequency to use the EventBus.
- * <li>Because the event handlers are called back using reflection, there will often be no
- * references to them from actual code. The proguard configuration will be need to be updated to
- * keep these extra methods:
- *
- * -keepclassmembers class ** {
- * public void onBusEvent(**);
- * public void onInterprocessBusEvent(**);
- * }
- * -keepclassmembers class ** extends **.EventBus$InterprocessEvent {
- * public <init>(android.os.Bundle);
- * }
- *
- * <li>Subscriber registration can be expensive depending on the subscriber's {@link Class}. This
- * is only done once per class type, but if possible, it is best to pre-register an instance of
- * that class beforehand or when idle.
- * <li>Each event should be sent once. Events may hold internal information about the current
- * dispatch, or may be queued to be dispatched on another thread (if posted from a non-main thread),
- * so it may be unsafe to edit, change, or re-send the event again.
- * <li>Events should follow a pattern of public-final POD (plain old data) objects, where they are
- * initialized by the constructor and read by each subscriber of that event. Subscribers should
- * never alter events as they are processed, and this enforces that pattern.
- * </ul>
- *
- * <p>
- * Future optimizations:
- * <li>throw exception/log when a subscriber loses the reference
- * <li>trace cost per registration & invocation
- * <li>trace cross-process invocation
- * <li>register(subscriber, Class&lt;?&gt;...) -- pass in exact class types you want registered
- * <li>setSubscriberEventHandlerPriority(subscriber, Class<Event>, priority)
- * <li>allow subscribers to implement interface, ie. EventBus.Subscriber, which lets then test a
- * message before invocation (ie. check if task id == this task id)
- * <li>add postOnce() which automatically debounces
- * <li>add postDelayed() which delays / postDelayedOnce() which delays and bounces
- * <li>consolidate register() and registerInterprocess()
- * <li>sendForResult&lt;ReturnType&gt;(Event) to send and get a result, but who will send the
- * result?
- * </p>
- */
-public class EventBus {
-
- private static final String TAG = "EventBus";
- private static final boolean DEBUG_TRACE_ALL = false;
-
- /**
- * An event super class that allows us to track internal event state across subscriber
- * invocations.
- *
- * Events should not be edited by subscribers.
- */
- public static class Event implements Cloneable {
- // Indicates that this event's dispatch should be traced and logged to logcat
- boolean trace;
- // Indicates that this event must be posted on the EventBus's looper thread before invocation
- boolean requiresPost;
- // Not currently exposed, allows a subscriber to cancel further dispatch of this event
- boolean cancelled;
-
- // Only accessible from derived events
- protected Event() {}
-
- /**
- * Called by the EventBus prior to dispatching this event to any subscriber of this event.
- */
- void onPreDispatch() {
- // Do nothing
- }
-
- /**
- * Called by the EventBus after dispatching this event to every subscriber of this event.
- */
- void onPostDispatch() {
- // Do nothing
- }
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
- Event evt = (Event) super.clone();
- // When cloning an event, reset the cancelled-dispatch state
- evt.cancelled = false;
- return evt;
- }
- }
-
- /**
- * An event that represents an animated state change, which allows subscribers to coordinate
- * callbacks which happen after the animation has taken place.
- *
- * Internally, it is guaranteed that increment() and decrement() will be called before and the
- * after the event is dispatched.
- */
- public static class AnimatedEvent extends Event {
-
- private final ReferenceCountedTrigger mTrigger = new ReferenceCountedTrigger();
-
- // Only accessible from derived events
- protected AnimatedEvent() {}
-
- /**
- * Returns the reference counted trigger that coordinates the animations for this event.
- */
- public ReferenceCountedTrigger getAnimationTrigger() {
- return mTrigger;
- }
-
- /**
- * Adds a callback that is guaranteed to be called after the state has changed regardless of
- * whether an actual animation took place.
- */
- public void addPostAnimationCallback(Runnable r) {
- mTrigger.addLastDecrementRunnable(r);
- }
-
- @Override
- void onPreDispatch() {
- mTrigger.increment();
- }
-
- @Override
- void onPostDispatch() {
- mTrigger.decrement();
- }
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
- throw new CloneNotSupportedException();
- }
- }
-
- /**
- * An event that can be reusable, only used for situations where we want to reduce memory
- * allocations when events are sent frequently (ie. on scroll).
- */
- public static class ReusableEvent extends Event {
-
- private int mDispatchCount;
-
- protected ReusableEvent() {}
-
- @Override
- void onPostDispatch() {
- super.onPostDispatch();
- mDispatchCount++;
- }
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
- throw new CloneNotSupportedException();
- }
- }
-
- /**
- * Proguard must also know, and keep, all methods matching this signature.
- *
- * -keepclassmembers class ** {
- * public void onBusEvent(**);
- * public void onInterprocessBusEvent(**);
- * }
- */
- private static final String METHOD_PREFIX = "onBusEvent";
-
- // The default priority of all subscribers
- private static final int DEFAULT_SUBSCRIBER_PRIORITY = 1;
-
- // Orders the handlers by priority and registration time
- private static final Comparator<EventHandler> EVENT_HANDLER_COMPARATOR = new Comparator<EventHandler>() {
- @Override
- public int compare(EventHandler h1, EventHandler h2) {
- // Rank the handlers by priority descending, followed by registration time descending.
- // aka. the later registered
- if (h1.priority != h2.priority) {
- return h2.priority - h1.priority;
- } else {
- return Long.compare(h2.subscriber.registrationTime, h1.subscriber.registrationTime);
- }
- }
- };
-
- // Used for initializing the default bus
- private static final Object sLock = new Object();
- private static volatile EventBus sDefaultBus;
-
- // The handler to post all events
- private Handler mHandler;
-
- /**
- * Map from event class -> event handler list. Keeps track of the actual mapping from event
- * to subscriber method.
- */
- private HashMap<Class<? extends Event>, ArrayList<EventHandler>> mEventTypeMap = new HashMap<>();
-
- /**
- * Map from subscriber class -> event handler method lists. Used to determine upon registration
- * of a new subscriber whether we need to read all the subscriber's methods again using
- * reflection or whether we can just add the subscriber to the event type map.
- */
- private HashMap<Class<? extends Object>, ArrayList<EventHandlerMethod>> mSubscriberTypeMap = new HashMap<>();
-
- /**
- * Set of all currently registered subscribers
- */
- private ArrayList<Subscriber> mSubscribers = new ArrayList<>();
-
- // For tracing
- private int mCallCount;
- private long mCallDurationMicros;
-
- /**
- * Private constructor to create an event bus for a given looper.
- */
- private EventBus(Looper looper) {
- mHandler = new Handler(looper);
- }
-
- /**
- * @return the default event bus for the application's main thread.
- */
- public static EventBus getDefault() {
- if (sDefaultBus == null)
- synchronized (sLock) {
- if (sDefaultBus == null) {
- if (DEBUG_TRACE_ALL) {
- logWithPid("New EventBus");
- }
- sDefaultBus = new EventBus(Looper.getMainLooper());
- }
- }
- return sDefaultBus;
- }
-
- /**
- * Registers a subscriber to receive events with the default priority.
- *
- * @param subscriber the subscriber to handle events. If this is the first instance of the
- * subscriber's class type that has been registered, the class's methods will
- * be scanned for appropriate event handler methods.
- */
- public void register(Object subscriber) {
- registerSubscriber(subscriber, DEFAULT_SUBSCRIBER_PRIORITY);
- }
-
- /**
- * Registers a subscriber to receive events with the given priority.
- *
- * @param subscriber the subscriber to handle events. If this is the first instance of the
- * subscriber's class type that has been registered, the class's methods will
- * be scanned for appropriate event handler methods.
- * @param priority the priority that this subscriber will receive events relative to other
- * subscribers
- */
- public void register(Object subscriber, int priority) {
- registerSubscriber(subscriber, priority);
- }
-
- /**
- * Remove all EventHandlers pointing to the specified subscriber. This does not remove the
- * mapping of subscriber type to event handler method, in case new instances of this subscriber
- * are registered.
- */
- public void unregister(Object subscriber) {
- if (DEBUG_TRACE_ALL) {
- logWithPid("unregister()");
- }
-
- // Fail immediately if we are being called from the non-main thread
- long callingThreadId = Thread.currentThread().getId();
- if (callingThreadId != mHandler.getLooper().getThread().getId()) {
- throw new RuntimeException("Can not unregister() a subscriber from a non-main thread.");
- }
-
- // Return early if this is not a registered subscriber
- if (!findRegisteredSubscriber(subscriber, true /* removeFoundSubscriber */)) {
- return;
- }
-
- Class<?> subscriberType = subscriber.getClass();
- ArrayList<EventHandlerMethod> subscriberMethods = mSubscriberTypeMap.get(subscriberType);
- if (subscriberMethods != null) {
- // For each of the event handlers the subscriber handles, remove all references of that
- // handler
- for (EventHandlerMethod method : subscriberMethods) {
- ArrayList<EventHandler> eventHandlers = mEventTypeMap.get(method.eventType);
- for (int i = eventHandlers.size() - 1; i >= 0; i--) {
- if (eventHandlers.get(i).subscriber.getReference() == subscriber) {
- eventHandlers.remove(i);
- }
- }
- }
- }
- }
-
- /**
- * Sends an event to the subscribers of the given event type immediately. This can only be
- * called from the same thread as the EventBus's looper thread (for the default EventBus, this
- * is the main application thread).
- */
- public void send(Event event) {
- // Fail immediately if we are being called from the non-main thread
- long callingThreadId = Thread.currentThread().getId();
- if (callingThreadId != mHandler.getLooper().getThread().getId()) {
- throw new RuntimeException("Can not send() a message from a non-main thread.");
- }
-
- if (DEBUG_TRACE_ALL) {
- logWithPid("send(" + event.getClass().getSimpleName() + ")");
- }
-
- // Reset the event's cancelled state
- event.requiresPost = false;
- event.cancelled = false;
- queueEvent(event);
- }
-
- /**
- * Post a message to the subscribers of the given event type. The messages will be posted on
- * the EventBus's looper thread (for the default EventBus, this is the main application thread).
- */
- public void post(Event event) {
- if (DEBUG_TRACE_ALL) {
- logWithPid("post(" + event.getClass().getSimpleName() + ")");
- }
-
- // Reset the event's cancelled state
- event.requiresPost = true;
- event.cancelled = false;
- queueEvent(event);
- }
-
- /**
- * If this method is called from the main thread, it will be handled directly. If this method
- * is not called from the main thread, it will be posted onto the main thread.
- */
- public void sendOntoMainThread(Event event) {
- long callingThreadId = Thread.currentThread().getId();
- if (callingThreadId != mHandler.getLooper().getThread().getId()) {
- post(event);
- } else {
- send(event);
- }
- }
-
- /**
- * @return a dump of the current state of the EventBus
- */
- public void dump(String prefix, PrintWriter writer) {
- writer.println(dumpInternal(prefix));
- }
-
- public String dumpInternal(String prefix) {
- String innerPrefix = prefix + " ";
- String innerInnerPrefix = innerPrefix + " ";
- StringBuilder output = new StringBuilder();
- output.append(prefix);
- output.append("Registered class types:");
- output.append("\n");
- ArrayList<Class<?>> subsciberTypes = new ArrayList<>(mSubscriberTypeMap.keySet());
- Collections.sort(subsciberTypes, new Comparator<Class<?>>() {
- @Override
- public int compare(Class<?> o1, Class<?> o2) {
- return o1.getSimpleName().compareTo(o2.getSimpleName());
- }
- });
- for (int i = 0; i < subsciberTypes.size(); i++) {
- Class<?> clz = subsciberTypes.get(i);
- output.append(innerPrefix);
- output.append(clz.getSimpleName());
- output.append("\n");
- }
- output.append(prefix);
- output.append("Event map:");
- output.append("\n");
- ArrayList<Class<?>> classes = new ArrayList<>(mEventTypeMap.keySet());
- Collections.sort(classes, new Comparator<Class<?>>() {
- @Override
- public int compare(Class<?> o1, Class<?> o2) {
- return o1.getSimpleName().compareTo(o2.getSimpleName());
- }
- });
- for (int i = 0; i < classes.size(); i++) {
- Class<?> clz = classes.get(i);
- output.append(innerPrefix);
- output.append(clz.getSimpleName());
- output.append(" -> ");
- output.append("\n");
- ArrayList<EventHandler> handlers = mEventTypeMap.get(clz);
- for (EventHandler handler : handlers) {
- Object subscriber = handler.subscriber.getReference();
- if (subscriber != null) {
- String id = Integer.toHexString(System.identityHashCode(subscriber));
- output.append(innerInnerPrefix);
- output.append(subscriber.getClass().getSimpleName());
- output.append(" [0x" + id + ", #" + handler.priority + "]");
- output.append("\n");
- }
- }
- }
- return output.toString();
- }
-
- /**
- * Registers a new subscriber.
- */
- private void registerSubscriber(Object subscriber, int priority) {
- // Fail immediately if we are being called from the non-main thread
- long callingThreadId = Thread.currentThread().getId();
- if (callingThreadId != mHandler.getLooper().getThread().getId()) {
- throw new RuntimeException("Can not register() a subscriber from a non-main thread.");
- }
-
- // Return immediately if this exact subscriber is already registered
- if (findRegisteredSubscriber(subscriber, false /* removeFoundSubscriber */)) {
- return;
- }
-
- long t1 = 0;
- if (DEBUG_TRACE_ALL) {
- t1 = SystemClock.currentTimeMicro();
- logWithPid("registerSubscriber(" + subscriber.getClass().getSimpleName() + ")");
- }
- Subscriber sub = new Subscriber(subscriber, SystemClock.uptimeMillis());
- Class<?> subscriberType = subscriber.getClass();
- ArrayList<EventHandlerMethod> subscriberMethods = mSubscriberTypeMap.get(subscriberType);
- if (subscriberMethods != null) {
- if (DEBUG_TRACE_ALL) {
- logWithPid("Subscriber class type already registered");
- }
-
- // If we've parsed this subscriber type before, just add to the set for all the known
- // events
- for (EventHandlerMethod method : subscriberMethods) {
- ArrayList<EventHandler> eventTypeHandlers = mEventTypeMap.get(method.eventType);
- eventTypeHandlers.add(new EventHandler(sub, method, priority));
- sortEventHandlersByPriority(eventTypeHandlers);
- }
- mSubscribers.add(sub);
- return;
- } else {
- if (DEBUG_TRACE_ALL) {
- logWithPid("Subscriber class type requires registration");
- }
-
- // If we are parsing this type from scratch, ensure we add it to the subscriber type
- // map, and pull out he handler methods below
- subscriberMethods = new ArrayList<>();
- mSubscriberTypeMap.put(subscriberType, subscriberMethods);
- mSubscribers.add(sub);
- }
-
- // Find all the valid event bus handler methods of the subscriber
- Method[] methods = subscriberType.getDeclaredMethods();
- for (Method m : methods) {
- Class<?>[] parameterTypes = m.getParameterTypes();
- if (isValidEventBusHandlerMethod(m, parameterTypes)) {
- Class<? extends Event> eventType = (Class<? extends Event>) parameterTypes[0];
- ArrayList<EventHandler> eventTypeHandlers = mEventTypeMap.get(eventType);
- if (eventTypeHandlers == null) {
- eventTypeHandlers = new ArrayList<>();
- mEventTypeMap.put(eventType, eventTypeHandlers);
- }
- EventHandlerMethod method = new EventHandlerMethod(m, eventType);
- EventHandler handler = new EventHandler(sub, method, priority);
- eventTypeHandlers.add(handler);
- subscriberMethods.add(method);
- sortEventHandlersByPriority(eventTypeHandlers);
-
- if (DEBUG_TRACE_ALL) {
- logWithPid(" * Method: " + m.getName() +
- " event: " + parameterTypes[0].getSimpleName());
- }
- }
- }
- if (DEBUG_TRACE_ALL) {
- logWithPid("Registered " + subscriber.getClass().getSimpleName() + " in " +
- (SystemClock.currentTimeMicro() - t1) + " microseconds");
- }
- }
-
- /**
- * Adds a new message.
- */
- private void queueEvent(final Event event) {
- ArrayList<EventHandler> eventHandlers = mEventTypeMap.get(event.getClass());
- if (eventHandlers == null) {
- // This is just an optimization to return early if there are no handlers. However, we
- // should still ensure that we call pre/post dispatch callbacks so that AnimatedEvents
- // are still cleaned up correctly if a listener has not been registered to handle them
- event.onPreDispatch();
- event.onPostDispatch();
- return;
- }
-
- // Prepare this event
- boolean hasPostedEvent = false;
- event.onPreDispatch();
-
- // We need to clone the list in case a subscriber unregisters itself during traversal
- // TODO: Investigate whether we can skip the object creation here
- eventHandlers = (ArrayList<EventHandler>) eventHandlers.clone();
- int eventHandlerCount = eventHandlers.size();
- for (int i = 0; i < eventHandlerCount; i++) {
- final EventHandler eventHandler = eventHandlers.get(i);
- if (eventHandler.subscriber.getReference() != null) {
- if (event.requiresPost) {
- mHandler.post(() -> processEvent(eventHandler, event));
- hasPostedEvent = true;
- } else {
- processEvent(eventHandler, event);
- }
- }
- }
-
- // Clean up after this event, deferring until all subscribers have been called
- if (hasPostedEvent) {
- mHandler.post(event::onPostDispatch);
- } else {
- event.onPostDispatch();
- }
- }
-
- /**
- * Processes and dispatches the given event to the given event handler, on the thread of whoever
- * calls this method.
- */
- private void processEvent(final EventHandler eventHandler, final Event event) {
- // Skip if the event was already cancelled
- if (event.cancelled) {
- if (event.trace || DEBUG_TRACE_ALL) {
- logWithPid("Event dispatch cancelled");
- }
- return;
- }
-
- try {
- if (event.trace || DEBUG_TRACE_ALL) {
- logWithPid(" -> " + eventHandler.toString());
- }
- Object sub = eventHandler.subscriber.getReference();
- if (sub != null) {
- long t1 = 0;
- if (DEBUG_TRACE_ALL) {
- t1 = SystemClock.currentTimeMicro();
- }
- eventHandler.method.invoke(sub, event);
- if (DEBUG_TRACE_ALL) {
- long duration = (SystemClock.currentTimeMicro() - t1);
- mCallDurationMicros += duration;
- mCallCount++;
- logWithPid(eventHandler.method.toString() + " duration: " + duration +
- " microseconds, avg: " + (mCallDurationMicros / mCallCount));
- }
- } else {
- Log.e(TAG, "Failed to deliver event to null subscriber");
- }
- } catch (IllegalAccessException e) {
- Log.e(TAG, "Failed to invoke method", e.getCause());
- } catch (InvocationTargetException e) {
- throw new RuntimeException(e.getCause());
- }
- }
-
- /**
- * Returns whether this subscriber is currently registered. If {@param removeFoundSubscriber}
- * is true, then remove the subscriber before returning.
- */
- private boolean findRegisteredSubscriber(Object subscriber, boolean removeFoundSubscriber) {
- for (int i = mSubscribers.size() - 1; i >= 0; i--) {
- Subscriber sub = mSubscribers.get(i);
- if (sub.getReference() == subscriber) {
- if (removeFoundSubscriber) {
- mSubscribers.remove(i);
- }
- return true;
- }
- }
- return false;
- }
-
- /**
- * @return whether {@param method} is a valid (normal or interprocess) event bus handler method
- */
- private boolean isValidEventBusHandlerMethod(Method method, Class<?>[] parameterTypes) {
- int modifiers = method.getModifiers();
- if (Modifier.isPublic(modifiers) &&
- Modifier.isFinal(modifiers) &&
- method.getReturnType().equals(Void.TYPE) &&
- parameterTypes.length == 1) {
- if (EventBus.Event.class.isAssignableFrom(parameterTypes[0]) &&
- method.getName().startsWith(METHOD_PREFIX)) {
- return true;
- } else {
- if (DEBUG_TRACE_ALL) {
- if (!EventBus.Event.class.isAssignableFrom(parameterTypes[0])) {
- logWithPid(" Expected method take an Event-based parameter: " + method.getName());
- }
- }
- }
- } else {
- if (DEBUG_TRACE_ALL) {
- if (!Modifier.isPublic(modifiers)) {
- logWithPid(" Expected method to be public: " + method.getName());
- } else if (!Modifier.isFinal(modifiers)) {
- logWithPid(" Expected method to be final: " + method.getName());
- } else if (!method.getReturnType().equals(Void.TYPE)) {
- logWithPid(" Expected method to return null: " + method.getName());
- }
- }
- }
- return false;
- }
-
- /**
- * Sorts the event handlers by priority and registration time.
- */
- private void sortEventHandlersByPriority(List<EventHandler> eventHandlers) {
- Collections.sort(eventHandlers, EVENT_HANDLER_COMPARATOR);
- }
-
- /**
- * Helper method to log the given {@param text} with the current process and user id.
- */
- private static void logWithPid(String text) {
- Log.d(TAG, "[" + android.os.Process.myPid() + ", u" + UserHandle.myUserId() + "] " + text);
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/AppTransitionFinishedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/AppTransitionFinishedEvent.java
deleted file mode 100644
index 4738eed3d1a1..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/AppTransitionFinishedEvent.java
+++ /dev/null
@@ -1,25 +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.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * Sent when an app transition has finished playing.
- */
-public class AppTransitionFinishedEvent extends EventBus.Event {
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java
deleted file mode 100644
index fec34e3cd23d..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/CancelEnterRecentsWindowAnimationEvent.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.Task;
-
-/**
- * This is sent when we want to cancel the enter-recents window animation for the launch task.
- */
-public class CancelEnterRecentsWindowAnimationEvent extends EventBus.Event {
-
- // This is set for the task that is launching, which allows us to ensure that we are not
- // cancelling the same task animation (it will just be overwritten instead)
- public final Task launchTask;
-
- public CancelEnterRecentsWindowAnimationEvent(Task launchTask) {
- this.launchTask = launchTask;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java
deleted file mode 100644
index 294c1e7a190f..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java
+++ /dev/null
@@ -1,38 +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.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent when the Recents activity configuration has changed.
- */
-public class ConfigurationChangedEvent extends EventBus.AnimatedEvent {
-
- public final boolean fromMultiWindow;
- public final boolean fromDeviceOrientationChange;
- public final boolean fromDisplayDensityChange;
- public final boolean hasStackTasks;
-
- public ConfigurationChangedEvent(boolean fromMultiWindow, boolean fromDeviceOrientationChange,
- boolean fromDisplayDensityChange, boolean hasStackTasks) {
- this.fromMultiWindow = fromMultiWindow;
- this.fromDeviceOrientationChange = fromDeviceOrientationChange;
- this.fromDisplayDensityChange = fromDisplayDensityChange;
- this.hasStackTasks = hasStackTasks;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/DismissRecentsToHomeAnimationStarted.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/DismissRecentsToHomeAnimationStarted.java
deleted file mode 100644
index e7be85868da2..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/DismissRecentsToHomeAnimationStarted.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent when the task animation when dismissing Recents starts.
- */
-public class DismissRecentsToHomeAnimationStarted extends EventBus.AnimatedEvent {
-
- public final boolean animated;
-
- public DismissRecentsToHomeAnimationStarted(boolean animated) {
- this.animated = animated;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/DockedFirstAnimationFrameEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/DockedFirstAnimationFrameEvent.java
deleted file mode 100644
index 32d9a70340de..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/DockedFirstAnimationFrameEvent.java
+++ /dev/null
@@ -1,25 +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.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus.Event;
-
-/**
- * Sent when the window animation has started when docking a task
- */
-public class DockedFirstAnimationFrameEvent extends Event {
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/DockedTopTaskEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/DockedTopTaskEvent.java
deleted file mode 100644
index 9e3ced3f3757..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/DockedTopTaskEvent.java
+++ /dev/null
@@ -1,34 +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.recents.events.activity;
-
-import android.graphics.Rect;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * Fires when the user invoked the gesture to dock the top/left task after we called into window
- * manager and before we start recents.
- */
-public class DockedTopTaskEvent extends EventBus.Event {
-
- public Rect initialRect;
-
- public DockedTopTaskEvent(Rect initialRect) {
- this.initialRect = initialRect;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationCompletedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationCompletedEvent.java
deleted file mode 100644
index b31f32090ac7..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationCompletedEvent.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent when the window animation into Recents completes. We use this signal to know when
- * we can start in-app animations so that they don't conflict with the window transition into
- * Recents.
- */
-public class EnterRecentsWindowAnimationCompletedEvent extends EventBus.Event {
- // Simple event
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/EnterRecentsWindowLastAnimationFrameEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/EnterRecentsWindowLastAnimationFrameEvent.java
deleted file mode 100644
index fd023d8054e5..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/EnterRecentsWindowLastAnimationFrameEvent.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-
-public class EnterRecentsWindowLastAnimationFrameEvent extends EventBus.Event {
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ExitRecentsWindowFirstAnimationFrameEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ExitRecentsWindowFirstAnimationFrameEvent.java
deleted file mode 100644
index fa806eb24ad1..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ExitRecentsWindowFirstAnimationFrameEvent.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * Event sent when the exit animation is started.
- *
- * This is sent so parts of UI can synchronize on this event and adjust their appearance. An example
- * of that is hiding the tasks when the launched application window becomes visible.
- */
-public class ExitRecentsWindowFirstAnimationFrameEvent extends EventBus.Event {
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/HideRecentsEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/HideRecentsEvent.java
deleted file mode 100644
index bf9b421ef1fb..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/HideRecentsEvent.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent when the user taps on the Home button or finishes alt-tabbing to hide the Recents
- * activity.
- */
-public class HideRecentsEvent extends EventBus.Event {
-
- public final boolean triggeredFromAltTab;
- public final boolean triggeredFromHomeKey;
-
- public HideRecentsEvent(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
- this.triggeredFromAltTab = triggeredFromAltTab;
- this.triggeredFromHomeKey = triggeredFromHomeKey;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java
deleted file mode 100644
index e4a4f592657c..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent when the stack action button should be hidden.
- */
-public class HideStackActionButtonEvent extends EventBus.Event {
-
- // Whether or not to translate the stack action button when hiding it
- public final boolean translate;
-
- public HideStackActionButtonEvent() {
- this(true);
- }
-
- public HideStackActionButtonEvent(boolean translate) {
- this.translate = translate;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchMostRecentTaskRequestEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchMostRecentTaskRequestEvent.java
deleted file mode 100644
index 24913a4c2ca6..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchMostRecentTaskRequestEvent.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This event is sent to request that the most recent task is launched.
- */
-public class LaunchMostRecentTaskRequestEvent extends EventBus.Event {
- // Simple event
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchNextTaskRequestEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchNextTaskRequestEvent.java
deleted file mode 100644
index 11604b51b4a5..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchNextTaskRequestEvent.java
+++ /dev/null
@@ -1,27 +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.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This event is sent to request that the next task is launched after a double-tap on the Recents
- * button.
- */
-public class LaunchNextTaskRequestEvent extends EventBus.Event {
- // Simple event
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskEvent.java
deleted file mode 100644
index 2409f39d3760..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskEvent.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.activity;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
-import android.graphics.Rect;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.recents.views.TaskView;
-
-/**
- * This event is sent to request that a particular task is launched.
- */
-public class LaunchTaskEvent extends EventBus.Event {
-
- public final TaskView taskView;
- public final Task task;
- public final Rect targetTaskBounds;
- public final int targetWindowingMode;
- public final int targetActivityType;
- public final boolean screenPinningRequested;
-
- public LaunchTaskEvent(TaskView taskView, Task task, Rect targetTaskBounds,
- boolean screenPinningRequested) {
- this(taskView, task, targetTaskBounds, screenPinningRequested,
- WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED);
- }
-
- public LaunchTaskEvent(TaskView taskView, Task task, Rect targetTaskBounds,
- boolean screenPinningRequested, int windowingMode, int activityType) {
- this.taskView = taskView;
- this.task = task;
- this.targetTaskBounds = targetTaskBounds;
- this.targetWindowingMode = windowingMode;
- this.targetActivityType = activityType;
- this.screenPinningRequested = screenPinningRequested;
- }
-
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskFailedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskFailedEvent.java
deleted file mode 100644
index 3a2d58c80d88..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskFailedEvent.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent when we fail to launch a task.
- */
-public class LaunchTaskFailedEvent extends EventBus.Event {
- // Simple event
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskStartedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskStartedEvent.java
deleted file mode 100644
index 3925ab1186dc..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskStartedEvent.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.views.TaskView;
-
-/**
- * This event is sent following {@link LaunchTaskEvent} after the call to the system is made to
- * start the task.
- */
-public class LaunchTaskStartedEvent extends EventBus.AnimatedEvent {
-
- public final TaskView taskView;
- public final boolean screenPinningRequested;
-
- public LaunchTaskStartedEvent(TaskView taskView, boolean screenPinningRequested) {
- this.taskView = taskView;
- this.screenPinningRequested = screenPinningRequested;
- }
-
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskSucceededEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskSucceededEvent.java
deleted file mode 100644
index ec5089feabad..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/LaunchTaskSucceededEvent.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent when we successfully launch a task.
- */
-public class LaunchTaskSucceededEvent extends EventBus.Event {
-
- public final int taskIndexFromStackFront;
-
- public LaunchTaskSucceededEvent(int taskIndexFromStackFront) {
- this.taskIndexFromStackFront = taskIndexFromStackFront;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java
deleted file mode 100644
index 64eeafa1ae17..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.model.TaskStack;
-
-/**
- * This is sent by the activity whenever the multi-window state has changed.
- */
-public class MultiWindowStateChangedEvent extends EventBus.AnimatedEvent {
-
- public final boolean inMultiWindow;
- // This flag is only used when undocking a task
- public final boolean showDeferredAnimation;
- public final TaskStack stack;
-
- public MultiWindowStateChangedEvent(boolean inMultiWindow, boolean showDeferredAnimation,
- TaskStack stack) {
- this.inMultiWindow = inMultiWindow;
- this.showDeferredAnimation = showDeferredAnimation;
- this.stack = stack;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/PackagesChangedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/PackagesChangedEvent.java
deleted file mode 100644
index 47670e03c6a1..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/PackagesChangedEvent.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.views.TaskStackView;
-import com.android.systemui.recents.RecentsActivity;
-
-/**
- * This event is sent by {@link RecentsActivity} when a package on the the system changes.
- * {@link TaskStackView}s listen for this event, and remove the tasks associated with the removed
- * packages.
- */
-public class PackagesChangedEvent extends EventBus.Event {
-
- public final String packageName;
- public final int userId;
-
- public PackagesChangedEvent(String packageName, int userId) {
- this.packageName = packageName;
- this.userId = userId;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/RecentsActivityStartingEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/RecentsActivityStartingEvent.java
deleted file mode 100644
index a2ecfe207cf9..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/RecentsActivityStartingEvent.java
+++ /dev/null
@@ -1,26 +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.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * Called after recents activity is being started, i.e. startActivity has just been called.
- */
-public class RecentsActivityStartingEvent extends EventBus.Event{
-
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ShowEmptyViewEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ShowEmptyViewEvent.java
deleted file mode 100644
index 75bfd7bde66c..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ShowEmptyViewEvent.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * Sent when the stack should be hidden and the empty view shown.
- */
-public class ShowEmptyViewEvent extends EventBus.Event {
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ShowStackActionButtonEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ShowStackActionButtonEvent.java
deleted file mode 100644
index d81f89c172b9..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ShowStackActionButtonEvent.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent when the stack action view button should be shown.
- */
-public class ShowStackActionButtonEvent extends EventBus.Event {
-
- // Whether or not to translate the stack action button when showing it
- public final boolean translate;
-
- public ShowStackActionButtonEvent(boolean translate) {
- this.translate = translate;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java
deleted file mode 100644
index 0d614e8c675c..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/TaskStackUpdatedEvent.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.model.TaskStack;
-
-/**
- * This is sent by the activity whenever the task stach has changed.
- */
-public class TaskStackUpdatedEvent extends EventBus.AnimatedEvent {
-
- /**
- * A new TaskStack instance representing the latest stack state.
- */
- public final TaskStack stack;
- public final boolean inMultiWindow;
-
- public TaskStackUpdatedEvent(TaskStack stack, boolean inMultiWindow) {
- this.stack = stack;
- this.inMultiWindow = inMultiWindow;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ToggleRecentsEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ToggleRecentsEvent.java
deleted file mode 100644
index 49655b491aca..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/ToggleRecentsEvent.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent when the user taps on the Overview button to toggle the Recents activity.
- */
-public class ToggleRecentsEvent extends EventBus.Event {
- // Simple event
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/UndockingTaskEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/UndockingTaskEvent.java
deleted file mode 100644
index d5083a8b017f..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/activity/UndockingTaskEvent.java
+++ /dev/null
@@ -1,28 +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.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * Fires when the user invoked the gesture to undock the task in the docked stack.
- */
-public class UndockingTaskEvent extends EventBus.Event {
-
- public UndockingTaskEvent() {
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ActivityPinnedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ActivityPinnedEvent.java
deleted file mode 100644
index f4d2fcff9672..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ActivityPinnedEvent.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.events.component;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent when an activity is pinned.
- */
-public class ActivityPinnedEvent extends EventBus.Event {
-
- public final int taskId;
-
- public ActivityPinnedEvent(int taskId) {
- this.taskId = taskId;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ActivityUnpinnedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ActivityUnpinnedEvent.java
deleted file mode 100644
index 48c5f0b60f51..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ActivityUnpinnedEvent.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.events.component;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent when an activity is unpinned.
- */
-public class ActivityUnpinnedEvent extends EventBus.Event {
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ExpandPipEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ExpandPipEvent.java
deleted file mode 100644
index 37266f6ff39f..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ExpandPipEvent.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.events.component;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent when the PiP should be expanded due to being relaunched.
- */
-public class ExpandPipEvent extends EventBus.Event {
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/HidePipMenuEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/HidePipMenuEvent.java
deleted file mode 100644
index ce4f207aa1d7..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/HidePipMenuEvent.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.events.component;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent when the PiP menu should be hidden.
- */
-public class HidePipMenuEvent extends EventBus.AnimatedEvent {
- // Simple event
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java
deleted file mode 100644
index 8843eb41210f..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.component;
-
-import android.content.Context;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent when the visibility of the RecentsActivity for the current user changes. Handlers
- * of this event should not alter the UI, as the activity may still be visible.
- */
-public class RecentsVisibilityChangedEvent extends EventBus.Event {
-
- public final Context applicationContext;
- public final boolean visible;
-
- public RecentsVisibilityChangedEvent(Context context, boolean visible) {
- this.applicationContext = context.getApplicationContext();
- this.visible = visible;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java
deleted file mode 100644
index d460917b6ab4..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.component;
-
-import android.content.Context;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent when we want to start screen pinning.
- */
-public class ScreenPinningRequestEvent extends EventBus.Event {
-
- public final Context applicationContext;
- public final int taskId;
-
- public ScreenPinningRequestEvent(Context context, int taskId) {
- this.applicationContext = context.getApplicationContext();
- this.taskId = taskId;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/SetWaitingForTransitionStartEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/SetWaitingForTransitionStartEvent.java
deleted file mode 100644
index d9cf5fbf645d..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/SetWaitingForTransitionStartEvent.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.events.component;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent when we are setting/resetting the flag to wait for the transition to start.
- */
-public class SetWaitingForTransitionStartEvent extends EventBus.Event {
-
- public final boolean waitingForTransitionStart;
-
- public SetWaitingForTransitionStartEvent(boolean waitingForTransitionStart) {
- this.waitingForTransitionStart = waitingForTransitionStart;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ShowUserToastEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ShowUserToastEvent.java
deleted file mode 100644
index e2b39c39b586..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/component/ShowUserToastEvent.java
+++ /dev/null
@@ -1,33 +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.recents.events.component;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent when we want to show a toast for the current user.
- */
-public class ShowUserToastEvent extends EventBus.Event {
-
- public final int msgResId;
- public final int msgLength;
-
- public ShowUserToastEvent(int msgResId, int msgLength) {
- this.msgResId = msgResId;
- this.msgLength = msgLength;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/AllTaskViewsDismissedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/AllTaskViewsDismissedEvent.java
deleted file mode 100644
index 0352161be570..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/AllTaskViewsDismissedEvent.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.ui;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent whenever all the task views in a stack have been dismissed.
- */
-public class AllTaskViewsDismissedEvent extends EventBus.Event {
-
- public final int msgResId;
-
- public AllTaskViewsDismissedEvent(int msgResId) {
- this.msgResId = msgResId;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DeleteTaskDataEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DeleteTaskDataEvent.java
deleted file mode 100644
index b52e83b81649..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DeleteTaskDataEvent.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.ui;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.Task;
-
-/**
- * This is sent when the data associated with a given {@link Task} should be deleted from the
- * system.
- */
-public class DeleteTaskDataEvent extends EventBus.Event {
-
- public final Task task;
-
- public DeleteTaskDataEvent(Task task) {
- this.task = task;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DismissAllTaskViewsEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DismissAllTaskViewsEvent.java
deleted file mode 100644
index f8b59c7c62f7..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DismissAllTaskViewsEvent.java
+++ /dev/null
@@ -1,27 +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.recents.events.ui;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.views.TaskView;
-
-/**
- * This event is sent to request that all the {@link TaskView}s are dismissed.
- */
-public class DismissAllTaskViewsEvent extends EventBus.AnimatedEvent {
- // Simple event
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DismissTaskViewEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DismissTaskViewEvent.java
deleted file mode 100644
index 1f8c6443502f..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DismissTaskViewEvent.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.ui;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.views.TaskView;
-
-/**
- * This event is sent to request that the given {@link TaskView} is dismissed.
- */
-public class DismissTaskViewEvent extends EventBus.AnimatedEvent {
-
- public final TaskView taskView;
-
- public DismissTaskViewEvent(TaskView taskView) {
- this.taskView = taskView;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DraggingInRecentsEndedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DraggingInRecentsEndedEvent.java
deleted file mode 100644
index 9be8eb1cde4e..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DraggingInRecentsEndedEvent.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.android.systemui.recents.events.ui;
-
-import com.android.systemui.recents.events.EventBus.Event;
-
-/**
- * This event is sent when the user finished dragging in recents.
- */
-public class DraggingInRecentsEndedEvent extends Event {
-
- public final float velocity;
-
- public DraggingInRecentsEndedEvent(float velocity) {
- this.velocity = velocity;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DraggingInRecentsEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DraggingInRecentsEvent.java
deleted file mode 100644
index 5e8bfd410e31..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/DraggingInRecentsEvent.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.android.systemui.recents.events.ui;
-
-import com.android.systemui.recents.events.EventBus.Event;
-
-/**
- * This event is sent when the user changed how far they are dragging in recents.
- */
-public class DraggingInRecentsEvent extends Event {
-
- public final float distanceFromTop;
-
- public DraggingInRecentsEvent(float distanceFromTop) {
- this.distanceFromTop = distanceFromTop;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/HideIncompatibleAppOverlayEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/HideIncompatibleAppOverlayEvent.java
deleted file mode 100644
index d6ef636b23a6..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/HideIncompatibleAppOverlayEvent.java
+++ /dev/null
@@ -1,26 +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.recents.events.ui;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent when a user stops draggin an incompatible app task.
- */
-public class HideIncompatibleAppOverlayEvent extends EventBus.Event {
- // Simple event
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/RecentsDrawnEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/RecentsDrawnEvent.java
deleted file mode 100644
index 548316607133..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/RecentsDrawnEvent.java
+++ /dev/null
@@ -1,26 +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.recents.events.ui;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * Fired when recents was launched and has drawn its first frame.
- */
-public class RecentsDrawnEvent extends EventBus.Event {
-
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/RecentsGrowingEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/RecentsGrowingEvent.java
deleted file mode 100644
index d9b00271b31e..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/RecentsGrowingEvent.java
+++ /dev/null
@@ -1,26 +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.recents.events.ui;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * Sent when recents is about to grow in multi-window mode when entering recents.
- */
-public class RecentsGrowingEvent extends EventBus.Event {
-
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java
deleted file mode 100644
index da19384ae93a..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.ui;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.Task;
-
-/**
- * This is sent when a user wants to show the application info for a {@link Task}.
- */
-public class ShowApplicationInfoEvent extends EventBus.Event {
-
- public final Task task;
-
- public ShowApplicationInfoEvent(Task task) {
- this.task = task;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/ShowIncompatibleAppOverlayEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/ShowIncompatibleAppOverlayEvent.java
deleted file mode 100644
index 3a4350e3a0ca..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/ShowIncompatibleAppOverlayEvent.java
+++ /dev/null
@@ -1,26 +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.recents.events.ui;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent when a user starts dragging an incompatible app task.
- */
-public class ShowIncompatibleAppOverlayEvent extends EventBus.Event {
- // Simple event
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/StackViewScrolledEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/StackViewScrolledEvent.java
deleted file mode 100644
index c4b47c08fd8c..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/StackViewScrolledEvent.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.ui;
-
-import android.util.MutableInt;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent whenever a new scroll gesture happens on a stack view.
- */
-public class StackViewScrolledEvent extends EventBus.ReusableEvent {
-
- public final MutableInt yMovement;
-
- public StackViewScrolledEvent() {
- yMovement = new MutableInt(0);
- }
-
- public void updateY(int y) {
- yMovement.value = y;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/TaskSnapshotChangedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/TaskSnapshotChangedEvent.java
deleted file mode 100644
index f08292801b62..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/TaskSnapshotChangedEvent.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.recents.events.ui;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-
-/**
- * Sent when a task snapshot has changed.
- */
-public class TaskSnapshotChangedEvent extends EventBus.Event {
-
- public final int taskId;
- public final ThumbnailData thumbnailData;
-
- public TaskSnapshotChangedEvent(int taskId, ThumbnailData thumbnailData) {
- this.taskId = taskId;
- this.thumbnailData = thumbnailData;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/TaskViewDismissedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/TaskViewDismissedEvent.java
deleted file mode 100644
index 973812454afd..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/TaskViewDismissedEvent.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.ui;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.recents.utilities.AnimationProps;
-import com.android.systemui.recents.views.TaskView;
-
-/**
- * This event is sent when a {@link TaskView} has been dismissed and is no longer visible.
- */
-public class TaskViewDismissedEvent extends EventBus.Event {
-
- public final Task task;
- public final TaskView taskView;
- public final AnimationProps animation;
-
- public TaskViewDismissedEvent(Task task, TaskView taskView, AnimationProps animation) {
- this.task = task;
- this.taskView = taskView;
- this.animation = animation;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/UserInteractionEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/UserInteractionEvent.java
deleted file mode 100644
index 39e4c1d7095c..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/UserInteractionEvent.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.ui;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent whenever the user interacts with the activity.
- */
-public class UserInteractionEvent extends EventBus.ReusableEvent {
- // Simple Event
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java
deleted file mode 100644
index cf61b1ef7637..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.ui.dragndrop;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.recents.views.DropTarget;
-
-/**
- * This event is sent when a user drags in/out of a drop target.
- */
-public class DragDropTargetChangedEvent extends EventBus.AnimatedEvent {
-
- // The task that is currently being dragged
- public final Task task;
- public final DropTarget dropTarget;
-
- public DragDropTargetChangedEvent(Task task, DropTarget dropTarget) {
- this.task = task;
- this.dropTarget = dropTarget;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragEndCancelledEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragEndCancelledEvent.java
deleted file mode 100644
index c11936eee284..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragEndCancelledEvent.java
+++ /dev/null
@@ -1,38 +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.recents.events.ui.dragndrop;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.views.TaskView;
-
-/**
- * This event is sent whenever a drag end is cancelled because of an error.
- */
-public class DragEndCancelledEvent extends EventBus.AnimatedEvent {
-
- public final TaskStack stack;
- public final Task task;
- public final TaskView taskView;
-
- public DragEndCancelledEvent(TaskStack stack, Task task, TaskView taskView) {
- this.stack = stack;
- this.task = task;
- this.taskView = taskView;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
deleted file mode 100644
index 73cbde998319..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.ui.dragndrop;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.recents.views.DropTarget;
-import com.android.systemui.recents.views.TaskView;
-
-/**
- * This event is sent whenever a drag ends.
- */
-public class DragEndEvent extends EventBus.AnimatedEvent {
-
- public final Task task;
- public final TaskView taskView;
- public final DropTarget dropTarget;
-
- public DragEndEvent(Task task, TaskView taskView, DropTarget dropTarget) {
- this.task = task;
- this.taskView = taskView;
- this.dropTarget = dropTarget;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java
deleted file mode 100644
index 021be77bcc8b..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.ui.dragndrop;
-
-import android.graphics.Point;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.recents.views.TaskView;
-
-/**
- * This event is sent whenever a drag starts.
- */
-public class DragStartEvent extends EventBus.Event {
-
- public final Task task;
- public final TaskView taskView;
- public final Point tlOffset;
- public final boolean isUserTouchInitiated;
-
- public DragStartEvent(Task task, TaskView taskView, Point tlOffset) {
- this(task, taskView, tlOffset, true);
- }
-
- public DragStartEvent(Task task, TaskView taskView, Point tlOffset,
- boolean isUserTouchInitiated) {
- this.task = task;
- this.taskView = taskView;
- this.tlOffset = tlOffset;
- this.isUserTouchInitiated = isUserTouchInitiated;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java
deleted file mode 100644
index 64ba5748bb89..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/dragndrop/DragStartInitializeDropTargetsEvent.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.ui.dragndrop;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.recents.views.RecentsViewTouchHandler;
-import com.android.systemui.recents.views.TaskView;
-
-/**
- * This event is sent by the drag manager when it requires drop targets to register themselves for
- * the current drag gesture.
- */
-public class DragStartInitializeDropTargetsEvent extends EventBus.Event {
-
- public final Task task;
- public final TaskView taskView;
- public final RecentsViewTouchHandler handler;
-
- public DragStartInitializeDropTargetsEvent(Task task, TaskView taskView,
- RecentsViewTouchHandler handler) {
- this.task = task;
- this.taskView = taskView;
- this.handler = handler;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/DismissFocusedTaskViewEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/DismissFocusedTaskViewEvent.java
deleted file mode 100644
index df740185f1e8..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/DismissFocusedTaskViewEvent.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.ui.focus;
-
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.views.TaskView;
-
-/**
- * This event is sent to request that the currently focused {@link TaskView} is dismissed.
- */
-public class DismissFocusedTaskViewEvent extends EventBus.Event {
- // Simple event
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java
deleted file mode 100644
index 171ab5e8bcca..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.ui.focus;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * Focuses the next task view in the stack.
- */
-public class FocusNextTaskViewEvent extends EventBus.Event {
- // Simple event
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/FocusPreviousTaskViewEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/FocusPreviousTaskViewEvent.java
deleted file mode 100644
index 22469e758e70..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/FocusPreviousTaskViewEvent.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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 com.android.systemui.recents.events.ui.focus;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * Focuses the previous task view in the stack.
- */
-public class FocusPreviousTaskViewEvent extends EventBus.Event {
- // Simple event
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/NavigateTaskViewEvent.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/NavigateTaskViewEvent.java
deleted file mode 100644
index 5508d269f03f..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/events/ui/focus/NavigateTaskViewEvent.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.events.ui.focus;
-
-import android.view.KeyEvent;
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * Navigates the task view by arrow keys.
- */
-public class NavigateTaskViewEvent extends EventBus.Event {
- public enum Direction {
- UNDEFINED, UP, DOWN, LEFT, RIGHT;
- }
-
- public Direction direction;
- public NavigateTaskViewEvent(Direction direction) {
- this.direction = direction;
- }
-
- public static Direction getDirectionFromKeyCode(int keyCode) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_DPAD_UP:
- return Direction.UP;
- case KeyEvent.KEYCODE_DPAD_DOWN:
- return Direction.DOWN;
- case KeyEvent.KEYCODE_DPAD_LEFT:
- return Direction.LEFT;
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- return Direction.RIGHT;
- default:
- return Direction.UNDEFINED;
- }
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/DozeTrigger.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/DozeTrigger.java
deleted file mode 100644
index 574ea03ac9bb..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/DozeTrigger.java
+++ /dev/null
@@ -1,106 +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 com.android.systemui.recents.misc;
-
-import android.os.Handler;
-import android.view.ViewDebug;
-
-/**
- * A dozer is a class that fires a trigger after it falls asleep.
- * You can occasionally poke the trigger to wake it up, but it will fall asleep if left untouched.
- */
-public class DozeTrigger {
-
- Handler mHandler;
-
- @ViewDebug.ExportedProperty(category="recents")
- boolean mIsDozing;
- @ViewDebug.ExportedProperty(category="recents")
- boolean mIsAsleep;
- @ViewDebug.ExportedProperty(category="recents")
- int mDozeDurationMilliseconds;
- Runnable mOnSleepRunnable;
-
- // Sleep-runnable
- Runnable mDozeRunnable = new Runnable() {
- @Override
- public void run() {
- mIsDozing = false;
- mIsAsleep = true;
- mOnSleepRunnable.run();
- }
- };
-
- public DozeTrigger(int dozeDurationMilliseconds, Runnable onSleepRunnable) {
- mHandler = new Handler();
- mDozeDurationMilliseconds = dozeDurationMilliseconds;
- mOnSleepRunnable = onSleepRunnable;
- }
-
- /**
- * Starts dozing and queues the onSleepRunnable to be called. This also resets the trigger flag.
- */
- public void startDozing() {
- forcePoke();
- mIsAsleep = false;
- }
-
- /**
- * Stops dozing and prevents the onSleepRunnable from being called.
- */
- public void stopDozing() {
- mHandler.removeCallbacks(mDozeRunnable);
- mIsDozing = false;
- mIsAsleep = false;
- }
-
- /**
- * Updates the duration that we have to wait until dozing triggers.
- */
- public void setDozeDuration(int duration) {
- mDozeDurationMilliseconds = duration;
- }
-
- /**
- * Poke this dozer to wake it up if it is dozing, delaying the onSleepRunnable from being
- * called for a for the doze duration.
- */
- public void poke() {
- if (mIsDozing) {
- forcePoke();
- }
- }
-
- /**
- * Poke this dozer to wake it up even if it is not currently dozing.
- */
- void forcePoke() {
- mHandler.removeCallbacks(mDozeRunnable);
- mHandler.postDelayed(mDozeRunnable, mDozeDurationMilliseconds);
- mIsDozing = true;
- }
-
- /** Returns whether we are dozing or not. */
- public boolean isDozing() {
- return mIsDozing;
- }
-
- /** Returns whether the trigger has fired at least once. */
- public boolean isAsleep() {
- return mIsAsleep;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/FreePathInterpolator.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/FreePathInterpolator.java
deleted file mode 100644
index 720c9520c074..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/FreePathInterpolator.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * 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 com.android.systemui.recents.misc;
-
-import android.graphics.Path;
-import android.view.animation.BaseInterpolator;
-import android.view.animation.Interpolator;
-
-/**
- * An interpolator that can traverse a Path. The x coordinate along the <code>Path</code>
- * is the input value and the output is the y coordinate of the line at that point.
- * This means that the Path must conform to a function <code>y = f(x)</code>.
- *
- * <p>The <code>Path</code> must not have gaps in the x direction and must not
- * loop back on itself such that there can be two points sharing the same x coordinate.
- * It is alright to have a disjoint line in the vertical direction:</p>
- * <p><blockquote><pre>
- * Path path = new Path();
- * path.lineTo(0.25f, 0.25f);
- * path.moveTo(0.25f, 0.5f);
- * path.lineTo(1f, 1f);
- * </pre></blockquote></p>
- */
-public class FreePathInterpolator extends BaseInterpolator {
-
- // This governs how accurate the approximation of the Path is.
- private static final float PRECISION = 0.002f;
-
- private float[] mX;
- private float[] mY;
- private float mArcLength;
-
- /**
- * Create an interpolator for an arbitrary <code>Path</code>.
- *
- * @param path The <code>Path</code> to use to make the line representing the interpolator.
- */
- public FreePathInterpolator(Path path) {
- initPath(path);
- }
-
- private void initPath(Path path) {
- float[] pointComponents = path.approximate(PRECISION);
-
- int numPoints = pointComponents.length / 3;
-
- mX = new float[numPoints];
- mY = new float[numPoints];
- mArcLength = 0;
- float prevX = 0;
- float prevY = 0;
- float prevFraction = 0;
- int componentIndex = 0;
- for (int i = 0; i < numPoints; i++) {
- float fraction = pointComponents[componentIndex++];
- float x = pointComponents[componentIndex++];
- float y = pointComponents[componentIndex++];
- if (fraction == prevFraction && x != prevX) {
- throw new IllegalArgumentException(
- "The Path cannot have discontinuity in the X axis.");
- }
- if (x < prevX) {
- throw new IllegalArgumentException("The Path cannot loop back on itself.");
- }
- mX[i] = x;
- mY[i] = y;
- mArcLength += Math.hypot(x - prevX, y - prevY);
- prevX = x;
- prevY = y;
- prevFraction = fraction;
- }
- }
-
- /**
- * Using the line in the Path in this interpolator that can be described as
- * <code>y = f(x)</code>, finds the y coordinate of the line given <code>t</code>
- * as the x coordinate.
- *
- * @param t Treated as the x coordinate along the line.
- * @return The y coordinate of the Path along the line where x = <code>t</code>.
- * @see Interpolator#getInterpolation(float)
- */
- @Override
- public float getInterpolation(float t) {
- int startIndex = 0;
- int endIndex = mX.length - 1;
-
- // Return early if out of bounds
- if (t <= 0) {
- return mY[startIndex];
- } else if (t >= 1) {
- return mY[endIndex];
- }
-
- // Do a binary search for the correct x to interpolate between.
- while (endIndex - startIndex > 1) {
- int midIndex = (startIndex + endIndex) / 2;
- if (t < mX[midIndex]) {
- endIndex = midIndex;
- } else {
- startIndex = midIndex;
- }
- }
-
- float xRange = mX[endIndex] - mX[startIndex];
- if (xRange == 0) {
- return mY[startIndex];
- }
-
- float tInRange = t - mX[startIndex];
- float fraction = tInRange / xRange;
-
- float startY = mY[startIndex];
- float endY = mY[endIndex];
- return startY + (fraction * (endY - startY));
- }
-
- /**
- * Finds the x that provides the given <code>y = f(x)</code>.
- *
- * @param y a value from (0,1) that is in this path.
- */
- public float getX(float y) {
- int startIndex = 0;
- int endIndex = mY.length - 1;
-
- // Return early if out of bounds
- if (y <= 0) {
- return mX[endIndex];
- } else if (y >= 1) {
- return mX[startIndex];
- }
-
- // Do a binary search for index that bounds the y
- while (endIndex - startIndex > 1) {
- int midIndex = (startIndex + endIndex) / 2;
- if (y < mY[midIndex]) {
- startIndex = midIndex;
- } else {
- endIndex = midIndex;
- }
- }
-
- float yRange = mY[endIndex] - mY[startIndex];
- if (yRange == 0) {
- return mX[startIndex];
- }
-
- float tInRange = y - mY[startIndex];
- float fraction = tInRange / yRange;
-
- float startX = mX[startIndex];
- float endX = mX[endIndex];
- return startX + (fraction * (endX - startX));
- }
-
- /**
- * Returns the arclength of the path we are interpolating.
- */
- public float getArcLength() {
- return mArcLength;
- }
-} \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
deleted file mode 100644
index 2637d8812357..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
+++ /dev/null
@@ -1,130 +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 com.android.systemui.recents.misc;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-
-import java.util.ArrayList;
-
-/**
- * A ref counted trigger that does some logic when the count is first incremented, or last
- * decremented. Not thread safe as it's not currently needed.
- */
-public class ReferenceCountedTrigger {
-
- int mCount;
- ArrayList<Runnable> mFirstIncRunnables = new ArrayList<>();
- ArrayList<Runnable> mLastDecRunnables = new ArrayList<>();
- Runnable mErrorRunnable;
-
- // Convenience runnables
- Runnable mIncrementRunnable = new Runnable() {
- @Override
- public void run() {
- increment();
- }
- };
- Runnable mDecrementRunnable = new Runnable() {
- @Override
- public void run() {
- decrement();
- }
- };
-
- public ReferenceCountedTrigger() {
- this(null, null, null);
- }
-
- public ReferenceCountedTrigger(Runnable firstIncRunnable, Runnable lastDecRunnable,
- Runnable errorRunanable) {
- if (firstIncRunnable != null) mFirstIncRunnables.add(firstIncRunnable);
- if (lastDecRunnable != null) mLastDecRunnables.add(lastDecRunnable);
- mErrorRunnable = errorRunanable;
- }
-
- /** Increments the ref count */
- public void increment() {
- if (mCount == 0 && !mFirstIncRunnables.isEmpty()) {
- int numRunnables = mFirstIncRunnables.size();
- for (int i = 0; i < numRunnables; i++) {
- mFirstIncRunnables.get(i).run();
- }
- }
- mCount++;
- }
-
- /** Convenience method to increment this trigger as a runnable */
- public Runnable incrementAsRunnable() {
- return mIncrementRunnable;
- }
-
- /** Adds a runnable to the last-decrement runnables list. */
- public void addLastDecrementRunnable(Runnable r) {
- mLastDecRunnables.add(r);
- }
-
- /** Decrements the ref count */
- public void decrement() {
- mCount--;
- if (mCount == 0) {
- flushLastDecrementRunnables();
- } else if (mCount < 0) {
- if (mErrorRunnable != null) {
- mErrorRunnable.run();
- } else {
- throw new RuntimeException("Invalid ref count");
- }
- }
- }
-
- /**
- * Runs and clears all the last-decrement runnables now.
- */
- public void flushLastDecrementRunnables() {
- if (!mLastDecRunnables.isEmpty()) {
- int numRunnables = mLastDecRunnables.size();
- for (int i = 0; i < numRunnables; i++) {
- mLastDecRunnables.get(i).run();
- }
- }
- mLastDecRunnables.clear();
- }
-
- /**
- * Convenience method to decrement this trigger as a animator listener. This listener is
- * guarded to prevent being called back multiple times, and will trigger a decrement once and
- * only once.
- */
- public Animator.AnimatorListener decrementOnAnimationEnd() {
- return new AnimatorListenerAdapter() {
- private boolean hasEnded;
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (hasEnded) return;
- decrement();
- hasEnded = true;
- }
- };
- }
-
- /** Returns the current ref count */
- public int getCount() {
- return mCount;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SysUiTaskStackChangeListener.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SysUiTaskStackChangeListener.java
deleted file mode 100644
index 5d7f1ba5eaf4..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SysUiTaskStackChangeListener.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.misc;
-
-import android.content.Context;
-
-import com.android.systemui.shared.system.TaskStackChangeListener;
-
-/**
- * An implementation of {@link TaskStackChangeListener}.
- */
-public abstract class SysUiTaskStackChangeListener extends TaskStackChangeListener {
-
- /**
- * Checks that the current user matches the user's SystemUI process.
- */
- protected final boolean checkCurrentUserId(Context context, boolean debug) {
- int currentUserId = SystemServicesProxy.getInstance(context).getCurrentUser();
- return checkCurrentUserId(currentUserId, debug);
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SystemServicesProxy.java
deleted file mode 100644
index 44354bc1d358..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ /dev/null
@@ -1,535 +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 com.android.systemui.recents.misc;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-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.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-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 android.app.ActivityManager;
-import android.app.ActivityManager.StackInfo;
-import android.app.ActivityOptions;
-import android.app.ActivityTaskManager;
-import android.app.AppGlobals;
-import android.app.IActivityManager;
-import android.app.IActivityTaskManager;
-import android.app.WindowConfiguration;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Point;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.service.dreams.DreamService;
-import android.service.dreams.IDreamManager;
-import android.util.Log;
-import android.util.MutableBoolean;
-import android.view.Display;
-import android.view.IDockedStackListener;
-import android.view.IWindowManager;
-import android.view.WindowManager;
-import android.view.WindowManager.KeyboardShortcutsReceiver;
-import android.view.WindowManagerGlobal;
-import android.view.accessibility.AccessibilityManager;
-
-import com.android.internal.app.AssistUtils;
-import com.android.internal.os.BackgroundThread;
-import com.android.systemui.Dependency;
-import com.android.systemui.UiOffloadThread;
-import com.android.systemui.recents.LegacyRecentsImpl;
-import com.android.systemui.recents.RecentsImpl;
-import com.android.systemui.statusbar.policy.UserInfoController;
-
-import java.util.List;
-
-/**
- * Acts as a shim around the real system services that we need to access data from, and provides
- * a point of injection when testing UI.
- */
-public class SystemServicesProxy {
- final static String TAG = "SystemServicesProxy";
-
- final static BitmapFactory.Options sBitmapOptions;
- static {
- sBitmapOptions = new BitmapFactory.Options();
- sBitmapOptions.inMutable = true;
- sBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
- }
-
- private static SystemServicesProxy sSystemServicesProxy;
-
- AccessibilityManager mAccm;
- ActivityManager mAm;
- IActivityManager mIam;
- IActivityTaskManager mIatm;
- PackageManager mPm;
- IPackageManager mIpm;
- private final IDreamManager mDreamManager;
- private final Context mContext;
- AssistUtils mAssistUtils;
- WindowManager mWm;
- IWindowManager mIwm;
- UserManager mUm;
- Display mDisplay;
- String mRecentsPackage;
- private int mCurrentUserId;
-
- boolean mIsSafeMode;
-
- int mDummyThumbnailWidth;
- int mDummyThumbnailHeight;
- Paint mBgProtectionPaint;
- Canvas mBgProtectionCanvas;
-
- private final Runnable mGcRunnable = new Runnable() {
- @Override
- public void run() {
- System.gc();
- System.runFinalization();
- }
- };
-
- private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
-
- private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener =
- (String name, Drawable picture, String userAccount) ->
- mCurrentUserId = mAm.getCurrentUser();
-
- /** Private constructor */
- private SystemServicesProxy(Context context) {
- mContext = context.getApplicationContext();
- mAccm = AccessibilityManager.getInstance(context);
- mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
- mIam = ActivityManager.getService();
- mIatm = ActivityTaskManager.getService();
- mPm = context.getPackageManager();
- mIpm = AppGlobals.getPackageManager();
- mAssistUtils = new AssistUtils(context);
- mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- mIwm = WindowManagerGlobal.getWindowManagerService();
- mUm = UserManager.get(context);
- mDreamManager = IDreamManager.Stub.asInterface(
- ServiceManager.checkService(DreamService.DREAM_SERVICE));
- mDisplay = mWm.getDefaultDisplay();
- mRecentsPackage = context.getPackageName();
- mIsSafeMode = mPm.isSafeMode();
- mCurrentUserId = mAm.getCurrentUser();
-
- // Get the dummy thumbnail width/heights
- Resources res = context.getResources();
- int wId = com.android.internal.R.dimen.thumbnail_width;
- int hId = com.android.internal.R.dimen.thumbnail_height;
- mDummyThumbnailWidth = res.getDimensionPixelSize(wId);
- mDummyThumbnailHeight = res.getDimensionPixelSize(hId);
-
- // Create the protection paints
- mBgProtectionPaint = new Paint();
- mBgProtectionPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
- mBgProtectionPaint.setColor(0xFFffffff);
- mBgProtectionCanvas = new Canvas();
-
- // Since SystemServicesProxy can be accessed from a per-SysUI process component, create a
- // per-process listener to keep track of the current user id to reduce the number of binder
- // calls to fetch it.
- UserInfoController userInfoController = Dependency.get(UserInfoController.class);
- userInfoController.addCallback(mOnUserInfoChangedListener);
- }
-
- /**
- * Returns the single instance of the {@link SystemServicesProxy}.
- * This should only be called on the main thread.
- */
- public static synchronized SystemServicesProxy getInstance(Context context) {
- if (sSystemServicesProxy == null) {
- sSystemServicesProxy = new SystemServicesProxy(context);
- }
- return sSystemServicesProxy;
- }
-
- /**
- * Requests a gc() from the background thread.
- */
- public void gc() {
- BackgroundThread.getHandler().post(mGcRunnable);
- }
-
- /**
- * Returns whether the recents activity is currently visible.
- */
- public boolean isRecentsActivityVisible() {
- return isRecentsActivityVisible(null);
- }
-
- /**
- * Returns whether the recents activity is currently visible.
- *
- * @param isHomeStackVisible if provided, will return whether the home stack is visible
- * regardless of the recents visibility
- *
- * TODO(winsonc): Refactor this check to just use the recents activity lifecycle
- */
- public boolean isRecentsActivityVisible(MutableBoolean isHomeStackVisible) {
- if (mIam == null) return false;
-
- try {
- List<StackInfo> stackInfos = mIatm.getAllStackInfos();
- ActivityManager.StackInfo homeStackInfo = null;
- ActivityManager.StackInfo fullscreenStackInfo = null;
- ActivityManager.StackInfo recentsStackInfo = null;
- for (int i = 0; i < stackInfos.size(); i++) {
- final StackInfo stackInfo = stackInfos.get(i);
- final WindowConfiguration winConfig = stackInfo.configuration.windowConfiguration;
- final int activityType = winConfig.getActivityType();
- final int windowingMode = winConfig.getWindowingMode();
- if (homeStackInfo == null && activityType == ACTIVITY_TYPE_HOME) {
- homeStackInfo = stackInfo;
- } else if (fullscreenStackInfo == null && activityType == ACTIVITY_TYPE_STANDARD
- && (windowingMode == WINDOWING_MODE_FULLSCREEN
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)) {
- fullscreenStackInfo = stackInfo;
- } else if (recentsStackInfo == null && activityType == ACTIVITY_TYPE_RECENTS) {
- recentsStackInfo = stackInfo;
- }
- }
- boolean homeStackVisibleNotOccluded = isStackNotOccluded(homeStackInfo,
- fullscreenStackInfo);
- boolean recentsStackVisibleNotOccluded = isStackNotOccluded(recentsStackInfo,
- fullscreenStackInfo);
- if (isHomeStackVisible != null) {
- isHomeStackVisible.value = homeStackVisibleNotOccluded;
- }
- ComponentName topActivity = recentsStackInfo != null ?
- recentsStackInfo.topActivity : null;
- return (recentsStackVisibleNotOccluded && topActivity != null
- && topActivity.getPackageName().equals(RecentsImpl.RECENTS_PACKAGE)
- && LegacyRecentsImpl.RECENTS_ACTIVITIES.contains(topActivity.getClassName()));
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- return false;
- }
-
- private boolean isStackNotOccluded(ActivityManager.StackInfo stackInfo,
- ActivityManager.StackInfo fullscreenStackInfo) {
- boolean stackVisibleNotOccluded = stackInfo == null || stackInfo.visible;
- if (fullscreenStackInfo != null && stackInfo != null) {
- boolean isFullscreenStackOccludingg = fullscreenStackInfo.visible &&
- fullscreenStackInfo.position > stackInfo.position;
- stackVisibleNotOccluded &= !isFullscreenStackOccludingg;
- }
- return stackVisibleNotOccluded;
- }
-
- /**
- * Returns whether this device is in the safe mode.
- */
- public boolean isInSafeMode() {
- return mIsSafeMode;
- }
-
- /** Moves an already resumed task to the side of the screen to initiate split screen. */
- public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode,
- Rect initialBounds) {
- if (mIatm == null) {
- return false;
- }
-
- try {
- return mIatm.setTaskWindowingModeSplitScreenPrimary(taskId, createMode,
- true /* onTop */, false /* animate */, initialBounds, true /* showRecents */);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- return false;
- }
-
- public ActivityManager.StackInfo getSplitScreenPrimaryStack() {
- try {
- return mIatm.getStackInfo(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
- } catch (RemoteException e) {
- return null;
- }
- }
-
- /**
- * @return whether there are any docked tasks for the current user.
- */
- public boolean hasDockedTask() {
- if (mIam == null) return false;
-
- ActivityManager.StackInfo stackInfo = getSplitScreenPrimaryStack();
- if (stackInfo != null) {
- int userId = getCurrentUser();
- boolean hasUserTask = false;
- for (int i = stackInfo.taskUserIds.length - 1; i >= 0 && !hasUserTask; i--) {
- hasUserTask = (stackInfo.taskUserIds[i] == userId);
- }
- return hasUserTask;
- }
- return false;
- }
-
- /**
- * Returns whether there is a soft nav bar on specified display.
- *
- * @param displayId the id of display to check if there is a software navigation bar.
- */
- public boolean hasSoftNavigationBar(int displayId) {
- try {
- return mIwm.hasNavigationBar(displayId);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- return false;
- }
-
- /**
- * Returns whether the device has a transposed nav bar (on the right of the screen) in the
- * current display orientation.
- */
- public boolean hasTransposedNavigationBar() {
- Rect insets = new Rect();
- getStableInsets(insets);
- return insets.right > 0;
- }
-
- /** Set the task's windowing mode. */
- public void setTaskWindowingMode(int taskId, int windowingMode) {
- if (mIatm == null) return;
-
- try {
- mIatm.setTaskWindowingMode(taskId, windowingMode, false /* onTop */);
- } catch (RemoteException | IllegalArgumentException e) {
- e.printStackTrace();
- }
- }
-
- /**
- * Returns whether the provided {@param userId} represents the system user.
- */
- public boolean isSystemUser(int userId) {
- return userId == UserHandle.USER_SYSTEM;
- }
-
- /**
- * Returns the current user id. Used instead of KeyguardUpdateMonitor in SystemUI components
- * that run in the non-primary SystemUI process.
- */
- public int getCurrentUser() {
- return mCurrentUserId;
- }
-
- /**
- * Returns the processes user id.
- */
- public int getProcessUser() {
- if (mUm == null) return 0;
- return mUm.getUserHandle();
- }
-
- /**
- * Returns whether touch exploration is currently enabled.
- */
- public boolean isTouchExplorationEnabled() {
- if (mAccm == null) return false;
-
- return mAccm.isEnabled() && mAccm.isTouchExplorationEnabled();
- }
-
- /**
- * Returns whether the current task is in screen-pinning mode.
- */
- public boolean isScreenPinningActive() {
- if (mIam == null) return false;
-
- try {
- return mIatm.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_PINNED;
- } catch (RemoteException e) {
- return false;
- }
- }
-
- /**
- * Returns the smallest width/height.
- */
- public int getDeviceSmallestWidth() {
- if (mDisplay == null) return 0;
-
- Point smallestSizeRange = new Point();
- Point largestSizeRange = new Point();
- mDisplay.getCurrentSizeRange(smallestSizeRange, largestSizeRange);
- return smallestSizeRange.x;
- }
-
- /**
- * Returns the current display rect in the current display orientation.
- */
- public Rect getDisplayRect() {
- Rect displayRect = new Rect();
- if (mDisplay == null) return displayRect;
-
- Point p = new Point();
- mDisplay.getRealSize(p);
- displayRect.set(0, 0, p.x, p.y);
- return displayRect;
- }
-
- /**
- * Returns the window rect for the RecentsActivity, based on the dimensions of the recents stack
- */
- public Rect getWindowRect() {
- Rect windowRect = new Rect();
- if (mIam == null) return windowRect;
-
- try {
- // Use the recents stack bounds, fallback to fullscreen stack if it is null
- ActivityManager.StackInfo stackInfo =
- mIatm.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
- if (stackInfo == null) {
- stackInfo = mIatm.getStackInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- }
- if (stackInfo != null) {
- windowRect.set(stackInfo.bounds);
- }
- } catch (RemoteException e) {
- e.printStackTrace();
- } finally {
- return windowRect;
- }
- }
-
- public void startActivityAsUserAsync(Intent intent, ActivityOptions opts) {
- mUiOffloadThread.submit(() -> mContext.startActivityAsUser(intent,
- opts != null ? opts.toBundle() : null, UserHandle.CURRENT));
- }
-
- /** Starts an in-place animation on the front most application windows. */
- public void startInPlaceAnimationOnFrontMostApplication(ActivityOptions opts) {
- if (mIam == null) return;
-
- try {
- mIatm.startInPlaceAnimationOnFrontMostApplication(
- opts == null ? null : opts.toBundle());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- public void registerDockedStackListener(IDockedStackListener listener) {
- if (mWm == null) return;
-
- try {
- mIwm.registerDockedStackListener(listener);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- /**
- * Calculates the size of the dock divider in the current orientation.
- */
- public int getDockedDividerSize(Context context) {
- Resources res = context.getResources();
- int dividerWindowWidth = res.getDimensionPixelSize(
- com.android.internal.R.dimen.docked_stack_divider_thickness);
- int dividerInsets = res.getDimensionPixelSize(
- com.android.internal.R.dimen.docked_stack_divider_insets);
- return dividerWindowWidth - 2 * dividerInsets;
- }
-
- public void requestKeyboardShortcuts(
- Context context, KeyboardShortcutsReceiver receiver, int deviceId) {
- mWm.requestAppKeyboardShortcuts(receiver, deviceId);
- }
-
- public void getStableInsets(Rect outStableInsets) {
- if (mWm == null) return;
-
- try {
- mIwm.getStableInsets(Display.DEFAULT_DISPLAY, outStableInsets);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- /**
- * Updates the visibility of recents.
- */
- public void setRecentsVisibility(final boolean visible) {
- mUiOffloadThread.submit(() -> {
- try {
- mIwm.setRecentsVisibility(visible);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to reach window manager", e);
- }
- });
- }
-
- /**
- * Updates the visibility of the picture-in-picture.
- */
- public void setPipVisibility(final boolean visible) {
- mUiOffloadThread.submit(() -> {
- try {
- mIwm.setPipVisibility(visible);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to reach window manager", e);
- }
- });
- }
-
- public boolean isDreaming() {
- try {
- return mDreamManager.isDreaming();
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to query dream manager.", e);
- }
- return false;
- }
-
- public void awakenDreamsAsync() {
- mUiOffloadThread.submit(() -> {
- try {
- mDreamManager.awaken();
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- });
- }
-
- public interface StartActivityFromRecentsResultListener {
- void onStartActivityResult(boolean succeeded);
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/BackgroundTaskLoader.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/BackgroundTaskLoader.java
deleted file mode 100644
index e85a7fb27505..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/BackgroundTaskLoader.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.recents.model;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.util.Log;
-
-import com.android.systemui.shared.recents.model.IconLoader;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-/**
- * Background task resource loader
- */
-class BackgroundTaskLoader implements Runnable {
- static String TAG = "BackgroundTaskLoader";
- static boolean DEBUG = false;
-
- private Context mContext;
- private final HandlerThread mLoadThread;
- private final Handler mLoadThreadHandler;
- private final Handler mMainThreadHandler;
-
- private final TaskResourceLoadQueue mLoadQueue;
- private final IconLoader mIconLoader;
-
- private boolean mStarted;
- private boolean mCancelled;
- private boolean mWaitingOnLoadQueue;
-
- private final OnIdleChangedListener mOnIdleChangedListener;
-
- /** Constructor, creates a new loading thread that loads task resources in the background */
- public BackgroundTaskLoader(TaskResourceLoadQueue loadQueue,
- IconLoader iconLoader, OnIdleChangedListener onIdleChangedListener) {
- mLoadQueue = loadQueue;
- mIconLoader = iconLoader;
- mMainThreadHandler = new Handler();
- mOnIdleChangedListener = onIdleChangedListener;
- mLoadThread = new HandlerThread("Recents-TaskResourceLoader",
- android.os.Process.THREAD_PRIORITY_BACKGROUND);
- mLoadThread.start();
- mLoadThreadHandler = new Handler(mLoadThread.getLooper());
- }
-
- /** Restarts the loader thread */
- void start(Context context) {
- mContext = context;
- mCancelled = false;
- if (!mStarted) {
- // Start loading on the load thread
- mStarted = true;
- mLoadThreadHandler.post(this);
- } else {
- // Notify the load thread to start loading again
- synchronized (mLoadThread) {
- mLoadThread.notifyAll();
- }
- }
- }
-
- /** Requests the loader thread to stop after the current iteration */
- void stop() {
- // Mark as cancelled for the thread to pick up
- mCancelled = true;
- // If we are waiting for the load queue for more tasks, then we can just reset the
- // Context now, since nothing is using it
- if (mWaitingOnLoadQueue) {
- mContext = null;
- }
- }
-
- @Override
- public void run() {
- while (true) {
- if (mCancelled) {
- // We have to unset the context here, since the background thread may be using it
- // when we call stop()
- mContext = null;
- // If we are cancelled, then wait until we are started again
- synchronized(mLoadThread) {
- try {
- mLoadThread.wait();
- } catch (InterruptedException ie) {
- ie.printStackTrace();
- }
- }
- } else {
- // If we've stopped the loader, then fall through to the above logic to wait on
- // the load thread
- processLoadQueueItem();
-
- // If there are no other items in the list, then just wait until something is added
- if (!mCancelled && mLoadQueue.isEmpty()) {
- synchronized(mLoadQueue) {
- try {
- mWaitingOnLoadQueue = true;
- mMainThreadHandler.post(new Runnable() {
- @Override
- public void run() {
- mOnIdleChangedListener.onIdleChanged(true);
- }
- });
- mLoadQueue.wait();
- mMainThreadHandler.post(new Runnable() {
- @Override
- public void run() {
- mOnIdleChangedListener.onIdleChanged(false);
- }
- });
- mWaitingOnLoadQueue = false;
- } catch (InterruptedException ie) {
- ie.printStackTrace();
- }
- }
- }
- }
- }
- }
-
- /**
- * This needs to be in a separate method to work around an surprising interpreter behavior:
- * The register will keep the local reference to cachedThumbnailData even if it falls out of
- * scope. Putting it into a method fixes this issue.
- */
- private void processLoadQueueItem() {
- // Load the next item from the queue
- final Task t = mLoadQueue.nextTask();
- if (t != null) {
- final Drawable icon = mIconLoader.getIcon(t);
- if (DEBUG) Log.d(TAG, "Loading thumbnail: " + t.key);
- final ThumbnailData thumbnailData =
- ActivityManagerWrapper.getInstance().getTaskThumbnail(t.key.id,
- true /* reducedResolution */);
-
- if (!mCancelled) {
- // Notify that the task data has changed
- mMainThreadHandler.post(new Runnable() {
- @Override
- public void run() {
- t.notifyTaskDataLoaded(thumbnailData, icon);
- }
- });
- }
- }
- }
-
- interface OnIdleChangedListener {
- void onIdleChanged(boolean idle);
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/FilteredTaskList.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/FilteredTaskList.java
deleted file mode 100644
index 005be75b1b97..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/FilteredTaskList.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.recents.model;
-
-import android.util.ArrayMap;
-import android.util.SparseArray;
-
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A list of filtered tasks.
- */
-class FilteredTaskList {
-
- private final ArrayList<Task> mTasks = new ArrayList<>();
- private final ArrayList<Task> mFilteredTasks = new ArrayList<>();
- private final ArrayMap<TaskKey, Integer> mFilteredTaskIndices = new ArrayMap<>();
- private TaskFilter mFilter;
-
- /** Sets the task filter, and returns whether the set of filtered tasks have changed. */
- boolean setFilter(TaskFilter filter) {
- ArrayList<Task> prevFilteredTasks = new ArrayList<>(mFilteredTasks);
- mFilter = filter;
- updateFilteredTasks();
- return !prevFilteredTasks.equals(mFilteredTasks);
- }
-
- /** Adds a new task to the task list */
- void add(Task t) {
- mTasks.add(t);
- updateFilteredTasks();
- }
-
- /** Sets the list of tasks */
- void set(List<Task> tasks) {
- mTasks.clear();
- mTasks.addAll(tasks);
- updateFilteredTasks();
- }
-
- /** Removes a task from the base list only if it is in the filtered list */
- boolean remove(Task t) {
- if (mFilteredTasks.contains(t)) {
- boolean removed = mTasks.remove(t);
- updateFilteredTasks();
- return removed;
- }
- return false;
- }
-
- /** Returns the index of this task in the list of filtered tasks */
- int indexOf(Task t) {
- if (t != null && mFilteredTaskIndices.containsKey(t.key)) {
- return mFilteredTaskIndices.get(t.key);
- }
- return -1;
- }
-
- /** Returns the size of the list of filtered tasks */
- int size() {
- return mFilteredTasks.size();
- }
-
- /** Returns whether the filtered list contains this task */
- boolean contains(Task t) {
- return mFilteredTaskIndices.containsKey(t.key);
- }
-
- /** Updates the list of filtered tasks whenever the base task list changes */
- private void updateFilteredTasks() {
- mFilteredTasks.clear();
- if (mFilter != null) {
- // Create a sparse array from task id to Task
- SparseArray<Task> taskIdMap = new SparseArray<>();
- int taskCount = mTasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task t = mTasks.get(i);
- taskIdMap.put(t.key.id, t);
- }
-
- for (int i = 0; i < taskCount; i++) {
- Task t = mTasks.get(i);
- if (mFilter.acceptTask(taskIdMap, t, i)) {
- mFilteredTasks.add(t);
- }
- }
- } else {
- mFilteredTasks.addAll(mTasks);
- }
- updateFilteredTaskIndices();
- }
-
- /** Updates the mapping of tasks to indices. */
- private void updateFilteredTaskIndices() {
- int taskCount = mFilteredTasks.size();
- mFilteredTaskIndices.clear();
- for (int i = 0; i < taskCount; i++) {
- Task t = mFilteredTasks.get(i);
- mFilteredTaskIndices.put(t.key, i);
- }
- }
-
- /** Returns the list of filtered tasks */
- ArrayList<Task> getTasks() {
- return mFilteredTasks;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/HighResThumbnailLoader.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/HighResThumbnailLoader.java
deleted file mode 100644
index 34bc334204ee..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/HighResThumbnailLoader.java
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.recents.model;
-
-import static android.os.Process.setThreadPriority;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.util.ArraySet;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.Task.TaskCallbacks;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-
-/**
- * Loader class that loads full-resolution thumbnails when appropriate.
- */
-public class HighResThumbnailLoader implements
- TaskCallbacks, BackgroundTaskLoader.OnIdleChangedListener {
-
- private final ActivityManagerWrapper mActivityManager;
-
- @GuardedBy("mLoadQueue")
- private final ArrayDeque<Task> mLoadQueue = new ArrayDeque<>();
- @GuardedBy("mLoadQueue")
- private final ArraySet<Task> mLoadingTasks = new ArraySet<>();
- @GuardedBy("mLoadQueue")
- private boolean mLoaderIdling;
-
- private final ArrayList<Task> mVisibleTasks = new ArrayList<>();
-
- private final Thread mLoadThread;
- private final Handler mMainThreadHandler;
- private final boolean mIsLowRamDevice;
- private boolean mLoading;
- private boolean mVisible;
- private boolean mFlingingFast;
- private boolean mTaskLoadQueueIdle;
-
- public HighResThumbnailLoader(ActivityManagerWrapper activityManager, Looper looper,
- boolean isLowRamDevice) {
- mActivityManager = activityManager;
- mMainThreadHandler = new Handler(looper);
- mLoadThread = new Thread(mLoader, "Recents-HighResThumbnailLoader");
- mLoadThread.start();
- mIsLowRamDevice = isLowRamDevice;
- }
-
- public void setVisible(boolean visible) {
- if (mIsLowRamDevice) {
- return;
- }
- mVisible = visible;
- updateLoading();
- }
-
- public void setFlingingFast(boolean flingingFast) {
- if (mFlingingFast == flingingFast || mIsLowRamDevice) {
- return;
- }
- mFlingingFast = flingingFast;
- updateLoading();
- }
-
- @Override
- public void onIdleChanged(boolean idle) {
- setTaskLoadQueueIdle(idle);
- }
-
- /**
- * Sets whether the other task load queue is idling. Avoid double-loading bitmaps by not
- * starting this queue until the other queue is idling.
- */
- public void setTaskLoadQueueIdle(boolean idle) {
- if (mIsLowRamDevice) {
- return;
- }
- mTaskLoadQueueIdle = idle;
- updateLoading();
- }
-
- @VisibleForTesting
- boolean isLoading() {
- return mLoading;
- }
-
- private void updateLoading() {
- setLoading(mVisible && !mFlingingFast && mTaskLoadQueueIdle);
- }
-
- private void setLoading(boolean loading) {
- if (loading == mLoading) {
- return;
- }
- synchronized (mLoadQueue) {
- mLoading = loading;
- if (!loading) {
- stopLoading();
- } else {
- startLoading();
- }
- }
- }
-
- @GuardedBy("mLoadQueue")
- private void startLoading() {
- for (int i = mVisibleTasks.size() - 1; i >= 0; i--) {
- Task t = mVisibleTasks.get(i);
- if ((t.thumbnail == null || t.thumbnail.reducedResolution)
- && !mLoadQueue.contains(t) && !mLoadingTasks.contains(t)) {
- mLoadQueue.add(t);
- }
- }
- mLoadQueue.notifyAll();
- }
-
- @GuardedBy("mLoadQueue")
- private void stopLoading() {
- mLoadQueue.clear();
- mLoadQueue.notifyAll();
- }
-
- /**
- * Needs to be called when a task becomes visible. Note that this is different from
- * {@link TaskCallbacks#onTaskDataLoaded} as this method should only be called once when it
- * becomes visible, whereas onTaskDataLoaded can be called multiple times whenever some data
- * has been updated.
- */
- public void onTaskVisible(Task t) {
- t.addCallback(this);
- mVisibleTasks.add(t);
- if ((t.thumbnail == null || t.thumbnail.reducedResolution) && mLoading) {
- synchronized (mLoadQueue) {
- mLoadQueue.add(t);
- mLoadQueue.notifyAll();
- }
- }
- }
-
- /**
- * Needs to be called when a task becomes visible. See {@link #onTaskVisible} why this is
- * different from {@link TaskCallbacks#onTaskDataUnloaded()}
- */
- public void onTaskInvisible(Task t) {
- t.removeCallback(this);
- mVisibleTasks.remove(t);
- synchronized (mLoadQueue) {
- mLoadQueue.remove(t);
- }
- }
-
- @VisibleForTesting
- void waitForLoaderIdle() {
- while (true) {
- synchronized (mLoadQueue) {
- if (mLoadQueue.isEmpty() && mLoaderIdling) {
- return;
- }
- }
- SystemClock.sleep(100);
- }
- }
-
- @Override
- public void onTaskDataLoaded(Task task, ThumbnailData thumbnailData) {
- if (thumbnailData != null && !thumbnailData.reducedResolution) {
- synchronized (mLoadQueue) {
- mLoadQueue.remove(task);
- }
- }
- }
-
- @Override
- public void onTaskDataUnloaded() {
- }
-
- @Override
- public void onTaskWindowingModeChanged() {
- }
-
- private final Runnable mLoader = new Runnable() {
-
- @Override
- public void run() {
- setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND + 1);
- while (true) {
- Task next = null;
- synchronized (mLoadQueue) {
- if (!mLoading || mLoadQueue.isEmpty()) {
- try {
- mLoaderIdling = true;
- mLoadQueue.wait();
- mLoaderIdling = false;
- } catch (InterruptedException e) {
- // Don't care.
- }
- } else {
- next = mLoadQueue.poll();
- if (next != null) {
- mLoadingTasks.add(next);
- }
- }
- }
- if (next != null) {
- loadTask(next);
- }
- }
- }
-
- private void loadTask(final Task t) {
- final ThumbnailData thumbnail = mActivityManager.getTaskThumbnail(t.key.id,
- false /* reducedResolution */);
- mMainThreadHandler.post(new Runnable() {
- @Override
- public void run() {
- synchronized (mLoadQueue) {
- mLoadingTasks.remove(t);
- }
- if (mVisibleTasks.contains(t)) {
- t.notifyTaskDataLoaded(thumbnail, t.icon);
- }
- }
- });
- }
- };
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
deleted file mode 100644
index 2df79d87b1fe..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ /dev/null
@@ -1,216 +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 com.android.systemui.recents.model;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-
-import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
-import android.app.KeyguardManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.util.SparseBooleanArray;
-
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-
-/**
- * This class stores the loading state as it goes through multiple stages of loading:
- * 1) preloadRawTasks() will load the raw set of recents tasks from the system
- * 2) preloadPlan() will construct a new task stack with all metadata and only icons and
- * thumbnails that are currently in the cache
- * 3) executePlan() will actually load and fill in the icons and thumbnails according to the load
- * options specified, such that we can transition into the Recents activity seamlessly
- */
-public class RecentsTaskLoadPlan {
-
- /** The set of conditions to preload tasks. */
- public static class PreloadOptions {
- public boolean loadTitles = true;
- }
-
- /** The set of conditions to load tasks. */
- public static class Options {
- public int runningTaskId = -1;
- public boolean loadIcons = true;
- public boolean loadThumbnails = false;
- public boolean onlyLoadForCache = false;
- public boolean onlyLoadPausedActivities = false;
- public int numVisibleTasks = 0;
- public int numVisibleTaskThumbnails = 0;
- }
-
- private final Context mContext;
- private final KeyguardManager mKeyguardManager;
-
- private List<ActivityManager.RecentTaskInfo> mRawTasks;
- private TaskStack mStack;
-
- private final SparseBooleanArray mTmpLockedUsers = new SparseBooleanArray();
-
- public RecentsTaskLoadPlan(Context context) {
- mContext = context;
- mKeyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
- }
-
- /**
- * Preloads the list of recent tasks from the system. After this call, the TaskStack will
- * have a list of all the recent tasks with their metadata, not including icons or
- * thumbnails which were not cached and have to be loaded.
- *
- * The tasks will be ordered by:
- * - least-recent to most-recent stack tasks
- *
- * Note: Do not lock, since this can be calling back to the loader, which separately also drives
- * this call (callers should synchronize on the loader before making this call).
- */
- public void preloadPlan(PreloadOptions opts, RecentsTaskLoader loader, int runningTaskId,
- int currentUserId) {
- Resources res = mContext.getResources();
- ArrayList<Task> allTasks = new ArrayList<>();
- if (mRawTasks == null) {
- mRawTasks = ActivityManagerWrapper.getInstance().getRecentTasks(
- ActivityTaskManager.getMaxRecentTasksStatic(), currentUserId);
-
- // Since the raw tasks are given in most-recent to least-recent order, we need to reverse it
- Collections.reverse(mRawTasks);
- }
-
- int taskCount = mRawTasks.size();
- for (int i = 0; i < taskCount; i++) {
- ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
-
- // Compose the task key
- final ComponentName sourceComponent = t.origActivity != null
- // Activity alias if there is one
- ? t.origActivity
- // The real activity if there is no alias (or the target if there is one)
- : t.realActivity;
- final int windowingMode = t.configuration.windowConfiguration.getWindowingMode();
- TaskKey taskKey = new TaskKey(t.persistentId, windowingMode, t.baseIntent,
- sourceComponent, t.userId, t.lastActiveTime, t.displayId);
-
- boolean isFreeformTask = windowingMode == WINDOWING_MODE_FREEFORM;
- boolean isStackTask = !isFreeformTask;
- boolean isLaunchTarget = taskKey.id == runningTaskId;
-
- ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey);
- if (info == null) {
- continue;
- }
-
- // Load the title, icon, and color
- String title = opts.loadTitles
- ? loader.getAndUpdateActivityTitle(taskKey, t.taskDescription)
- : "";
- String titleDescription = opts.loadTitles
- ? loader.getAndUpdateContentDescription(taskKey, t.taskDescription)
- : "";
- Drawable icon = isStackTask
- ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, false)
- : null;
- ThumbnailData thumbnail = loader.getAndUpdateThumbnail(taskKey,
- false /* loadIfNotCached */, false /* storeInCache */);
- int activityColor = loader.getActivityPrimaryColor(t.taskDescription);
- int backgroundColor = loader.getActivityBackgroundColor(t.taskDescription);
- boolean isSystemApp = (info != null) &&
- ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
-
- // TODO: Refactor to not do this every preload
- if (mTmpLockedUsers.indexOfKey(t.userId) < 0) {
- mTmpLockedUsers.put(t.userId, mKeyguardManager.isDeviceLocked(t.userId));
- }
- boolean isLocked = mTmpLockedUsers.get(t.userId);
-
- // Add the task to the stack
- Task task = new Task(taskKey, icon,
- thumbnail, title, titleDescription, activityColor, backgroundColor,
- isLaunchTarget, isStackTask, isSystemApp, t.supportsSplitScreenMultiWindow,
- t.taskDescription, t.resizeMode, t.topActivity, isLocked);
-
- allTasks.add(task);
- }
-
- // Initialize the stacks
- mStack = new TaskStack();
- mStack.setTasks(allTasks, false /* notifyStackChanges */);
- }
-
- /**
- * Called to apply the actual loading based on the specified conditions.
- *
- * Note: Do not lock, since this can be calling back to the loader, which separately also drives
- * this call (callers should synchronize on the loader before making this call).
- */
- public void executePlan(Options opts, RecentsTaskLoader loader) {
- Resources res = mContext.getResources();
-
- // Iterate through each of the tasks and load them according to the load conditions.
- ArrayList<Task> tasks = mStack.getTasks();
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task task = tasks.get(i);
- TaskKey taskKey = task.key;
-
- boolean isRunningTask = (task.key.id == opts.runningTaskId);
- boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks);
- boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails);
-
- // If requested, skip the running task
- if (opts.onlyLoadPausedActivities && isRunningTask) {
- continue;
- }
-
- if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
- if (task.icon == null) {
- task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription,
- true);
- }
- }
- if (opts.loadThumbnails && isVisibleThumbnail) {
- task.thumbnail = loader.getAndUpdateThumbnail(taskKey,
- true /* loadIfNotCached */, true /* storeInCache */);
- }
- }
- }
-
- /**
- * Returns the TaskStack from the preloaded list of recent tasks.
- */
- public TaskStack getTaskStack() {
- return mStack;
- }
-
- /** Returns whether there are any tasks in any stacks. */
- public boolean hasTasks() {
- if (mStack != null) {
- return mStack.getTaskCount() > 0;
- }
- return false;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/RecentsTaskLoader.java
deleted file mode 100644
index 012913a60b66..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ /dev/null
@@ -1,421 +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 com.android.systemui.recents.model;
-
-import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
-import android.content.ComponentCallbacks2;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.graphics.drawable.Drawable;
-import android.os.Looper;
-import android.os.Trace;
-import android.util.Log;
-import android.util.LruCache;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.systemui.recents.model.RecentsTaskLoadPlan.Options;
-import com.android.systemui.recents.model.RecentsTaskLoadPlan.PreloadOptions;
-import com.android.systemui.shared.recents.model.IconLoader;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.shared.recents.model.TaskKeyLruCache;
-import com.android.systemui.shared.recents.model.TaskKeyLruCache.EvictionCallback;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-import java.io.PrintWriter;
-import java.util.Map;
-
-
-/**
- * Recents task loader
- */
-public class RecentsTaskLoader {
- private static final String TAG = "RecentsTaskLoader";
- private static final boolean DEBUG = false;
-
- /** Levels of svelte in increasing severity/austerity. */
- // No svelting.
- public static final int SVELTE_NONE = 0;
- // Limit thumbnail cache to number of visible thumbnails when Recents was loaded, disable
- // caching thumbnails as you scroll.
- public static final int SVELTE_LIMIT_CACHE = 1;
- // Disable the thumbnail cache, load thumbnails asynchronously when the activity loads and
- // evict all thumbnails when hidden.
- public static final int SVELTE_DISABLE_CACHE = 2;
- // Disable all thumbnail loading.
- public static final int SVELTE_DISABLE_LOADING = 3;
-
- // This activity info LruCache is useful because it can be expensive to retrieve ActivityInfos
- // for many tasks, which we use to get the activity labels and icons. Unlike the other caches
- // below, this is per-package so we can't invalidate the items in the cache based on the last
- // active time. Instead, we rely on the PackageMonitor to keep us informed whenever a
- // package in the cache has been updated, so that we may remove it.
- private final LruCache<ComponentName, ActivityInfo> mActivityInfoCache;
- private final TaskKeyLruCache<Drawable> mIconCache;
- private final TaskKeyLruCache<String> mActivityLabelCache;
- private final TaskKeyLruCache<String> mContentDescriptionCache;
- private final TaskResourceLoadQueue mLoadQueue;
- private final IconLoader mIconLoader;
- private final BackgroundTaskLoader mLoader;
- private final HighResThumbnailLoader mHighResThumbnailLoader;
- @GuardedBy("this")
- private final TaskKeyStrongCache<ThumbnailData> mThumbnailCache = new TaskKeyStrongCache<>();
- @GuardedBy("this")
- private final TaskKeyStrongCache<ThumbnailData> mTempCache = new TaskKeyStrongCache<>();
- private final int mMaxThumbnailCacheSize;
- private final int mMaxIconCacheSize;
- private int mNumVisibleTasksLoaded;
- private int mSvelteLevel;
-
- private int mDefaultTaskBarBackgroundColor;
- private int mDefaultTaskViewBackgroundColor;
-
- private EvictionCallback mClearActivityInfoOnEviction = new EvictionCallback() {
- @Override
- public void onEntryEvicted(TaskKey key) {
- if (key != null) {
- mActivityInfoCache.remove(key.getComponent());
- }
- }
- };
-
- public RecentsTaskLoader(Context context, int maxThumbnailCacheSize, int maxIconCacheSize,
- int svelteLevel) {
- mMaxThumbnailCacheSize = maxThumbnailCacheSize;
- mMaxIconCacheSize = maxIconCacheSize;
- mSvelteLevel = svelteLevel;
-
- // Initialize the proxy, cache and loaders
- int numRecentTasks = ActivityTaskManager.getMaxRecentTasksStatic();
- mHighResThumbnailLoader = new HighResThumbnailLoader(ActivityManagerWrapper.getInstance(),
- Looper.getMainLooper(), ActivityManager.isLowRamDeviceStatic());
- mLoadQueue = new TaskResourceLoadQueue();
- mIconCache = new TaskKeyLruCache<>(mMaxIconCacheSize, mClearActivityInfoOnEviction);
- mActivityLabelCache = new TaskKeyLruCache<>(numRecentTasks, mClearActivityInfoOnEviction);
- mContentDescriptionCache = new TaskKeyLruCache<>(numRecentTasks,
- mClearActivityInfoOnEviction);
- mActivityInfoCache = new LruCache<>(numRecentTasks);
-
- mIconLoader = createNewIconLoader(context, mIconCache, mActivityInfoCache);
- mLoader = new BackgroundTaskLoader(mLoadQueue, mIconLoader, mHighResThumbnailLoader);
- }
-
- protected IconLoader createNewIconLoader(Context context,TaskKeyLruCache<Drawable> iconCache,
- LruCache<ComponentName, ActivityInfo> activityInfoCache) {
- return new IconLoader.DefaultIconLoader(context, iconCache, activityInfoCache);
- }
-
- /**
- * Sets the default task bar/view colors if none are provided by the app.
- */
- public void setDefaultColors(int defaultTaskBarBackgroundColor,
- int defaultTaskViewBackgroundColor) {
- mDefaultTaskBarBackgroundColor = defaultTaskBarBackgroundColor;
- mDefaultTaskViewBackgroundColor = defaultTaskViewBackgroundColor;
- }
-
- /** Returns the size of the app icon cache. */
- public int getIconCacheSize() {
- return mMaxIconCacheSize;
- }
-
- /** Returns the size of the thumbnail cache. */
- public int getThumbnailCacheSize() {
- return mMaxThumbnailCacheSize;
- }
-
- public HighResThumbnailLoader getHighResThumbnailLoader() {
- return mHighResThumbnailLoader;
- }
-
- /** Preloads recents tasks using the specified plan to store the output. */
- public synchronized void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId) {
- preloadTasks(plan, runningTaskId, ActivityManagerWrapper.getInstance().getCurrentUserId());
- }
-
- /** Preloads recents tasks using the specified plan to store the output. */
- public synchronized void preloadTasks(RecentsTaskLoadPlan plan, int runningTaskId,
- int currentUserId) {
- try {
- Trace.beginSection("preloadPlan");
- plan.preloadPlan(new PreloadOptions(), this, runningTaskId, currentUserId);
- } finally {
- Trace.endSection();
- }
- }
-
- /** Begins loading the heavy task data according to the specified options. */
- public synchronized void loadTasks(RecentsTaskLoadPlan plan, Options opts) {
- if (opts == null) {
- throw new RuntimeException("Requires load options");
- }
- if (opts.onlyLoadForCache && opts.loadThumbnails) {
- // If we are loading for the cache, we'd like to have the real cache only include the
- // visible thumbnails. However, we also don't want to reload already cached thumbnails.
- // Thus, we copy over the current entries into a second cache, and clear the real cache,
- // such that the real cache only contains visible thumbnails.
- mTempCache.copyEntries(mThumbnailCache);
- mThumbnailCache.evictAll();
- }
- plan.executePlan(opts, this);
- mTempCache.evictAll();
- if (!opts.onlyLoadForCache) {
- mNumVisibleTasksLoaded = opts.numVisibleTasks;
- }
- }
-
- /**
- * Acquires the task resource data directly from the cache, loading if necessary.
- */
- public void loadTaskData(Task t) {
- Drawable icon = mIconCache.getAndInvalidateIfModified(t.key);
- icon = icon != null ? icon : mIconLoader.getDefaultIcon(t.key.userId);
- mLoadQueue.addTask(t);
- t.notifyTaskDataLoaded(t.thumbnail, icon);
- }
-
- /** Releases the task resource data back into the pool. */
- public void unloadTaskData(Task t) {
- mLoadQueue.removeTask(t);
- t.notifyTaskDataUnloaded(mIconLoader.getDefaultIcon(t.key.userId));
- }
-
- /** Completely removes the resource data from the pool. */
- public void deleteTaskData(Task t, boolean notifyTaskDataUnloaded) {
- mLoadQueue.removeTask(t);
- mIconCache.remove(t.key);
- mActivityLabelCache.remove(t.key);
- mContentDescriptionCache.remove(t.key);
- if (notifyTaskDataUnloaded) {
- t.notifyTaskDataUnloaded(mIconLoader.getDefaultIcon(t.key.userId));
- }
- }
-
- /**
- * Handles signals from the system, trimming memory when requested to prevent us from running
- * out of memory.
- */
- public synchronized void onTrimMemory(int level) {
- switch (level) {
- case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
- // Stop the loader immediately when the UI is no longer visible
- stopLoader();
- mIconCache.trimToSize(Math.max(mNumVisibleTasksLoaded,
- mMaxIconCacheSize / 2));
- break;
- case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
- case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
- // We are leaving recents, so trim the data a bit
- mIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 2));
- mActivityInfoCache.trimToSize(Math.max(1,
- ActivityTaskManager.getMaxRecentTasksStatic() / 2));
- break;
- case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
- case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
- // We are going to be low on memory
- mIconCache.trimToSize(Math.max(1, mMaxIconCacheSize / 4));
- mActivityInfoCache.trimToSize(Math.max(1,
- ActivityTaskManager.getMaxRecentTasksStatic() / 4));
- break;
- case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
- case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
- // We are low on memory, so release everything
- mIconCache.evictAll();
- mActivityInfoCache.evictAll();
- // The cache is small, only clear the label cache when we are critical
- mActivityLabelCache.evictAll();
- mContentDescriptionCache.evictAll();
- mThumbnailCache.evictAll();
- break;
- default:
- break;
- }
- }
-
- public void onPackageChanged(String packageName) {
- // Remove all the cached activity infos for this package. The other caches do not need to
- // be pruned at this time, as the TaskKey expiration checks will flush them next time their
- // cached contents are requested
- Map<ComponentName, ActivityInfo> activityInfoCache = mActivityInfoCache.snapshot();
- for (ComponentName cn : activityInfoCache.keySet()) {
- if (cn.getPackageName().equals(packageName)) {
- if (DEBUG) {
- Log.d(TAG, "Removing activity info from cache: " + cn);
- }
- mActivityInfoCache.remove(cn);
- }
- }
- }
-
- /**
- * Returns the cached task label if the task key is not expired, updating the cache if it is.
- */
- String getAndUpdateActivityTitle(TaskKey taskKey, ActivityManager.TaskDescription td) {
- // Return the task description label if it exists
- if (td != null && td.getLabel() != null) {
- return td.getLabel();
- }
- // Return the cached activity label if it exists
- String label = mActivityLabelCache.getAndInvalidateIfModified(taskKey);
- if (label != null) {
- return label;
- }
- // All short paths failed, load the label from the activity info and cache it
- ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
- if (activityInfo != null) {
- label = ActivityManagerWrapper.getInstance().getBadgedActivityLabel(activityInfo,
- taskKey.userId);
- mActivityLabelCache.put(taskKey, label);
- return label;
- }
- // If the activity info does not exist or fails to load, return an empty label for now,
- // but do not cache it
- return "";
- }
-
- /**
- * Returns the cached task content description if the task key is not expired, updating the
- * cache if it is.
- */
- String getAndUpdateContentDescription(TaskKey taskKey, ActivityManager.TaskDescription td) {
- // Return the cached content description if it exists
- String label = mContentDescriptionCache.getAndInvalidateIfModified(taskKey);
- if (label != null) {
- return label;
- }
-
- // All short paths failed, load the label from the activity info and cache it
- ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
- if (activityInfo != null) {
- label = ActivityManagerWrapper.getInstance().getBadgedContentDescription(
- activityInfo, taskKey.userId, td);
- if (td == null) {
- // Only add to the cache if the task description is null, otherwise, it is possible
- // for the task description to change between calls without the last active time
- // changing (ie. between preloading and Overview starting) which would lead to stale
- // content descriptions
- // TODO: Investigate improving this
- mContentDescriptionCache.put(taskKey, label);
- }
- return label;
- }
- // If the content description does not exist, return an empty label for now, but do not
- // cache it
- return "";
- }
-
- /**
- * Returns the cached task icon if the task key is not expired, updating the cache if it is.
- */
- Drawable getAndUpdateActivityIcon(TaskKey taskKey, ActivityManager.TaskDescription td,
- boolean loadIfNotCached) {
- return mIconLoader.getAndInvalidateIfModified(taskKey, td, loadIfNotCached);
- }
-
- /**
- * Returns the cached thumbnail if the task key is not expired, updating the cache if it is.
- */
- synchronized ThumbnailData getAndUpdateThumbnail(TaskKey taskKey, boolean loadIfNotCached,
- boolean storeInCache) {
- ThumbnailData cached = mThumbnailCache.getAndInvalidateIfModified(taskKey);
- if (cached != null) {
- return cached;
- }
-
- cached = mTempCache.getAndInvalidateIfModified(taskKey);
- if (cached != null) {
- mThumbnailCache.put(taskKey, cached);
- return cached;
- }
-
- if (loadIfNotCached) {
- if (mSvelteLevel < SVELTE_DISABLE_LOADING) {
- // Load the thumbnail from the system
- ThumbnailData thumbnailData = ActivityManagerWrapper.getInstance().getTaskThumbnail(
- taskKey.id, true /* reducedResolution */);
- if (thumbnailData.thumbnail != null) {
- if (storeInCache) {
- mThumbnailCache.put(taskKey, thumbnailData);
- }
- return thumbnailData;
- }
- }
- }
-
- // We couldn't load any thumbnail
- return null;
- }
-
- /**
- * Returns the task's primary color if possible, defaulting to the default color if there is
- * no specified primary color.
- */
- int getActivityPrimaryColor(ActivityManager.TaskDescription td) {
- if (td != null && td.getPrimaryColor() != 0) {
- return td.getPrimaryColor();
- }
- return mDefaultTaskBarBackgroundColor;
- }
-
- /**
- * Returns the task's background color if possible.
- */
- int getActivityBackgroundColor(ActivityManager.TaskDescription td) {
- if (td != null && td.getBackgroundColor() != 0) {
- return td.getBackgroundColor();
- }
- return mDefaultTaskViewBackgroundColor;
- }
-
- /**
- * Returns the activity info for the given task key, retrieving one from the system if the
- * task key is expired.
- */
- ActivityInfo getAndUpdateActivityInfo(TaskKey taskKey) {
- return mIconLoader.getAndUpdateActivityInfo(taskKey);
- }
-
- /**
- * Starts loading tasks.
- */
- public void startLoader(Context ctx) {
- mLoader.start(ctx);
- }
-
- /**
- * Stops the task loader and clears all queued, pending task loads.
- */
- private void stopLoader() {
- mLoader.stop();
- mLoadQueue.clearTasks();
- }
-
- public synchronized void dump(String prefix, PrintWriter writer) {
- String innerPrefix = prefix + " ";
-
- writer.print(prefix); writer.println(TAG);
- writer.print(prefix); writer.println("Icon Cache");
- mIconCache.dump(innerPrefix, writer);
- writer.print(prefix); writer.println("Thumbnail Cache");
- mThumbnailCache.dump(innerPrefix, writer);
- writer.print(prefix); writer.println("Temp Thumbnail Cache");
- mTempCache.dump(innerPrefix, writer);
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskFilter.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskFilter.java
deleted file mode 100644
index 9b734ec719ea..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskFilter.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.recents.model;
-
-import android.util.SparseArray;
-import com.android.systemui.shared.recents.model.Task;
-
-/**
- * An interface for a task filter to query whether a particular task should show in a stack.
- */
-public interface TaskFilter {
- /** Returns whether the filter accepts the specified task */
- boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index);
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskKeyStrongCache.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskKeyStrongCache.java
deleted file mode 100644
index 27f2098868a1..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskKeyStrongCache.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.recents.model;
-
-import android.util.ArrayMap;
-
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-
-import com.android.systemui.shared.recents.model.TaskKeyCache;
-import com.android.systemui.shared.recents.model.TaskKeyLruCache;
-import java.io.PrintWriter;
-
-/**
- * Like {@link TaskKeyLruCache}, but without LRU functionality.
- */
-public class TaskKeyStrongCache<V> extends TaskKeyCache<V> {
-
- private static final String TAG = "TaskKeyCache";
-
- private final ArrayMap<Integer, V> mCache = new ArrayMap<>();
-
- public final void copyEntries(TaskKeyStrongCache<V> other) {
- for (int i = other.mKeys.size() - 1; i >= 0; i--) {
- TaskKey key = other.mKeys.valueAt(i);
- put(key, other.mCache.get(key.id));
- }
- }
-
- public void dump(String prefix, PrintWriter writer) {
- String innerPrefix = prefix + " ";
- writer.print(prefix); writer.print(TAG);
- writer.print(" numEntries="); writer.print(mKeys.size());
- writer.println();
- int keyCount = mKeys.size();
- for (int i = 0; i < keyCount; i++) {
- writer.print(innerPrefix); writer.println(mKeys.get(mKeys.keyAt(i)));
- }
- }
-
- @Override
- protected V getCacheEntry(int id) {
- return mCache.get(id);
- }
-
- @Override
- protected void putCacheEntry(int id, V value) {
- mCache.put(id, value);
- }
-
- @Override
- protected void removeCacheEntry(int id) {
- mCache.remove(id);
- }
-
- @Override
- protected void evictAllCache() {
- mCache.clear();
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskResourceLoadQueue.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskResourceLoadQueue.java
deleted file mode 100644
index fe89ad5a324d..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskResourceLoadQueue.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.recents.model;
-
-import com.android.systemui.shared.recents.model.Task;
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-/**
- * A Task load queue
- */
-class TaskResourceLoadQueue {
-
- private final ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<>();
-
- /** Adds a new task to the load queue */
- void addTask(Task t) {
- if (!mQueue.contains(t)) {
- mQueue.add(t);
- }
- synchronized(this) {
- notifyAll();
- }
- }
-
- /**
- * Retrieves the next task from the load queue, as well as whether we want that task to be
- * force reloaded.
- */
- Task nextTask() {
- return mQueue.poll();
- }
-
- /** Removes a task from the load queue */
- void removeTask(Task t) {
- mQueue.remove(t);
- }
-
- /** Clears all the tasks from the load queue */
- void clearTasks() {
- mQueue.clear();
- }
-
- /** Returns whether the load queue is empty */
- boolean isEmpty() {
- return mQueue.isEmpty();
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskStack.java
deleted file mode 100644
index 01e6ba3f35ec..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/model/TaskStack.java
+++ /dev/null
@@ -1,402 +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 com.android.systemui.recents.model;
-
-import android.content.ComponentName;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.SparseArray;
-
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.recents.utilities.AnimationProps;
-import com.android.systemui.shared.system.PackageManagerWrapper;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-
-/**
- * The task stack contains a list of multiple tasks.
- */
-public class TaskStack {
-
- private static final String TAG = "TaskStack";
-
- /** Task stack callbacks */
- public interface TaskStackCallbacks {
- /**
- * Notifies when a new task has been added to the stack.
- */
- void onStackTaskAdded(TaskStack stack, Task newTask);
-
- /**
- * Notifies when a task has been removed from the stack.
- */
- void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask,
- AnimationProps animation, boolean fromDockGesture,
- boolean dismissRecentsIfAllRemoved);
-
- /**
- * Notifies when all tasks have been removed from the stack.
- */
- void onStackTasksRemoved(TaskStack stack);
-
- /**
- * Notifies when tasks in the stack have been updated.
- */
- void onStackTasksUpdated(TaskStack stack);
- }
-
- private final ArrayList<Task> mRawTaskList = new ArrayList<>();
- private final FilteredTaskList mStackTaskList = new FilteredTaskList();
- private TaskStackCallbacks mCb;
-
- public TaskStack() {
- // Ensure that we only show stack tasks
- mStackTaskList.setFilter(new TaskFilter() {
- @Override
- public boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index) {
- return t.isStackTask;
- }
- });
- }
-
- /** Sets the callbacks for this task stack. */
- public void setCallbacks(TaskStackCallbacks cb) {
- mCb = cb;
- }
-
- /**
- * Removes a task from the stack, with an additional {@param animation} hint to the callbacks on
- * how they should update themselves.
- */
- public void removeTask(Task t, AnimationProps animation, boolean fromDockGesture) {
- removeTask(t, animation, fromDockGesture, true /* dismissRecentsIfAllRemoved */);
- }
-
- /**
- * Removes a task from the stack, with an additional {@param animation} hint to the callbacks on
- * how they should update themselves.
- */
- public void removeTask(Task t, AnimationProps animation, boolean fromDockGesture,
- boolean dismissRecentsIfAllRemoved) {
- if (mStackTaskList.contains(t)) {
- mStackTaskList.remove(t);
- Task newFrontMostTask = getFrontMostTask();
- if (mCb != null) {
- // Notify that a task has been removed
- mCb.onStackTaskRemoved(this, t, newFrontMostTask, animation,
- fromDockGesture, dismissRecentsIfAllRemoved);
- }
- }
- mRawTaskList.remove(t);
- }
-
- /**
- * Removes all tasks from the stack.
- */
- public void removeAllTasks(boolean notifyStackChanges) {
- ArrayList<Task> tasks = mStackTaskList.getTasks();
- for (int i = tasks.size() - 1; i >= 0; i--) {
- Task t = tasks.get(i);
- mStackTaskList.remove(t);
- mRawTaskList.remove(t);
- }
- if (mCb != null && notifyStackChanges) {
- // Notify that all tasks have been removed
- mCb.onStackTasksRemoved(this);
- }
- }
-
-
- /**
- * @see #setTasks(List, boolean)
- */
- public void setTasks(TaskStack stack, boolean notifyStackChanges) {
- setTasks(stack.mRawTaskList, notifyStackChanges);
- }
-
- /**
- * Sets a few tasks in one go, without calling any callbacks.
- *
- * @param tasks the new set of tasks to replace the current set.
- * @param notifyStackChanges whether or not to callback on specific changes to the list of tasks.
- */
- public void setTasks(List<Task> tasks, boolean notifyStackChanges) {
- // Compute a has set for each of the tasks
- ArrayMap<TaskKey, Task> currentTasksMap = createTaskKeyMapFromList(mRawTaskList);
- ArrayMap<TaskKey, Task> newTasksMap = createTaskKeyMapFromList(tasks);
- ArrayList<Task> addedTasks = new ArrayList<>();
- ArrayList<Task> removedTasks = new ArrayList<>();
- ArrayList<Task> allTasks = new ArrayList<>();
-
- // Disable notifications if there are no callbacks
- if (mCb == null) {
- notifyStackChanges = false;
- }
-
- // Remove any tasks that no longer exist
- int taskCount = mRawTaskList.size();
- for (int i = taskCount - 1; i >= 0; i--) {
- Task task = mRawTaskList.get(i);
- if (!newTasksMap.containsKey(task.key)) {
- if (notifyStackChanges) {
- removedTasks.add(task);
- }
- }
- }
-
- // Add any new tasks
- taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task newTask = tasks.get(i);
- Task currentTask = currentTasksMap.get(newTask.key);
- if (currentTask == null && notifyStackChanges) {
- addedTasks.add(newTask);
- } else if (currentTask != null) {
- // The current task has bound callbacks, so just copy the data from the new task
- // state and add it back into the list
- currentTask.copyFrom(newTask);
- newTask = currentTask;
- }
- allTasks.add(newTask);
- }
-
- // Sort all the tasks to ensure they are ordered correctly
- for (int i = allTasks.size() - 1; i >= 0; i--) {
- allTasks.get(i).temporarySortIndexInStack = i;
- }
-
- mStackTaskList.set(allTasks);
- mRawTaskList.clear();
- mRawTaskList.addAll(allTasks);
-
- // Only callback for the removed tasks after the stack has updated
- int removedTaskCount = removedTasks.size();
- Task newFrontMostTask = getFrontMostTask();
- for (int i = 0; i < removedTaskCount; i++) {
- mCb.onStackTaskRemoved(this, removedTasks.get(i), newFrontMostTask,
- AnimationProps.IMMEDIATE, false /* fromDockGesture */,
- true /* dismissRecentsIfAllRemoved */);
- }
-
- // Only callback for the newly added tasks after this stack has been updated
- int addedTaskCount = addedTasks.size();
- for (int i = 0; i < addedTaskCount; i++) {
- mCb.onStackTaskAdded(this, addedTasks.get(i));
- }
-
- // Notify that the task stack has been updated
- if (notifyStackChanges) {
- mCb.onStackTasksUpdated(this);
- }
- }
-
- /**
- * Gets the front-most task in the stack.
- */
- public Task getFrontMostTask() {
- ArrayList<Task> stackTasks = mStackTaskList.getTasks();
- if (stackTasks.isEmpty()) {
- return null;
- }
- return stackTasks.get(stackTasks.size() - 1);
- }
-
- /** Gets the task keys */
- public ArrayList<TaskKey> getTaskKeys() {
- ArrayList<TaskKey> taskKeys = new ArrayList<>();
- ArrayList<Task> tasks = computeAllTasksList();
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task task = tasks.get(i);
- taskKeys.add(task.key);
- }
- return taskKeys;
- }
-
- /**
- * Returns the set of "active" (non-historical) tasks in the stack that have been used recently.
- */
- public ArrayList<Task> getTasks() {
- return mStackTaskList.getTasks();
- }
-
- /**
- * Computes a set of all the active and historical tasks.
- */
- public ArrayList<Task> computeAllTasksList() {
- ArrayList<Task> tasks = new ArrayList<>();
- tasks.addAll(mStackTaskList.getTasks());
- return tasks;
- }
-
- /**
- * Returns the number of stack tasks.
- */
- public int getTaskCount() {
- return mStackTaskList.size();
- }
-
- /**
- * Returns the task in stack tasks which is the launch target.
- */
- public Task getLaunchTarget() {
- ArrayList<Task> tasks = mStackTaskList.getTasks();
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task task = tasks.get(i);
- if (task.isLaunchTarget) {
- return task;
- }
- }
- return null;
- }
-
- /**
- * Returns whether the next launch target should actually be the PiP task.
- */
- public boolean isNextLaunchTargetPip(long lastPipTime) {
- Task launchTarget = getLaunchTarget();
- Task nextLaunchTarget = getNextLaunchTargetRaw();
- if (nextLaunchTarget != null && lastPipTime > 0) {
- // If the PiP time is more recent than the next launch target, then launch the PiP task
- return lastPipTime > nextLaunchTarget.key.lastActiveTime;
- } else if (launchTarget != null && lastPipTime > 0 && getTaskCount() == 1) {
- // Otherwise, if there is no next launch target, but there is a PiP, then launch
- // the PiP task
- return true;
- }
- return false;
- }
-
- /**
- * Returns the task in stack tasks which should be launched next if Recents are toggled
- * again, or null if there is no task to be launched. Callers should check
- * {@link #isNextLaunchTargetPip(long)} before fetching the next raw launch target from the
- * stack.
- */
- public Task getNextLaunchTarget() {
- Task nextLaunchTarget = getNextLaunchTargetRaw();
- if (nextLaunchTarget != null) {
- return nextLaunchTarget;
- }
- return getTasks().get(getTaskCount() - 1);
- }
-
- private Task getNextLaunchTargetRaw() {
- int taskCount = getTaskCount();
- if (taskCount == 0) {
- return null;
- }
- int launchTaskIndex = indexOfTask(getLaunchTarget());
- if (launchTaskIndex != -1 && launchTaskIndex > 0) {
- return getTasks().get(launchTaskIndex - 1);
- }
- return null;
- }
-
- /** Returns the index of this task in this current task stack */
- public int indexOfTask(Task t) {
- return mStackTaskList.indexOf(t);
- }
-
- /** Finds the task with the specified task id. */
- public Task findTaskWithId(int taskId) {
- ArrayList<Task> tasks = computeAllTasksList();
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task task = tasks.get(i);
- if (task.key.id == taskId) {
- return task;
- }
- }
- return null;
- }
-
- /**
- * Computes the components of tasks in this stack that have been removed as a result of a change
- * in the specified package.
- */
- public ArraySet<ComponentName> computeComponentsRemoved(String packageName, int userId) {
- // Identify all the tasks that should be removed as a result of the package being removed.
- // Using a set to ensure that we callback once per unique component.
- ArraySet<ComponentName> existingComponents = new ArraySet<>();
- ArraySet<ComponentName> removedComponents = new ArraySet<>();
- ArrayList<TaskKey> taskKeys = getTaskKeys();
- int taskKeyCount = taskKeys.size();
- for (int i = 0; i < taskKeyCount; i++) {
- TaskKey t = taskKeys.get(i);
-
- // Skip if this doesn't apply to the current user
- if (t.userId != userId) continue;
-
- ComponentName cn = t.getComponent();
- if (cn.getPackageName().equals(packageName)) {
- if (existingComponents.contains(cn)) {
- // If we know that the component still exists in the package, then skip
- continue;
- }
- if (PackageManagerWrapper.getInstance().getActivityInfo(cn, userId) != null) {
- existingComponents.add(cn);
- } else {
- removedComponents.add(cn);
- }
- }
- }
- return removedComponents;
- }
-
- @Override
- public String toString() {
- String str = "Stack Tasks (" + mStackTaskList.size() + "):\n";
- ArrayList<Task> tasks = mStackTaskList.getTasks();
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- str += " " + tasks.get(i).toString() + "\n";
- }
- return str;
- }
-
- /**
- * Given a list of tasks, returns a map of each task's key to the task.
- */
- private ArrayMap<TaskKey, Task> createTaskKeyMapFromList(List<Task> tasks) {
- ArrayMap<TaskKey, Task> map = new ArrayMap<>(tasks.size());
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task task = tasks.get(i);
- map.put(task.key, task);
- }
- return map;
- }
-
- public void dump(String prefix, PrintWriter writer) {
- String innerPrefix = prefix + " ";
-
- writer.print(prefix); writer.print(TAG);
- writer.print(" numStackTasks="); writer.print(mStackTaskList.size());
- writer.println();
- ArrayList<Task> tasks = mStackTaskList.getTasks();
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- tasks.get(i).dump(innerPrefix, writer);
- }
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/utilities/AnimationProps.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/utilities/AnimationProps.java
deleted file mode 100644
index 54639985bebd..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/utilities/AnimationProps.java
+++ /dev/null
@@ -1,229 +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.recents.utilities;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ValueAnimator;
-import android.annotation.IntDef;
-import android.util.SparseArray;
-import android.util.SparseLongArray;
-import android.view.View;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-
-/**
- * The generic set of animation properties to animate a {@link View}. The animation can have
- * different interpolators, start delays and durations for each of the different properties.
- */
-public class AnimationProps {
-
- private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
- public static final AnimationProps IMMEDIATE = new AnimationProps(0, LINEAR_INTERPOLATOR);
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({ALL, TRANSLATION_X, TRANSLATION_Y, TRANSLATION_Z, ALPHA, SCALE, BOUNDS})
- public @interface PropType {}
-
- public static final int ALL = 0;
- public static final int TRANSLATION_X = 1;
- public static final int TRANSLATION_Y = 2;
- public static final int TRANSLATION_Z = 3;
- public static final int ALPHA = 4;
- public static final int SCALE = 5;
- public static final int BOUNDS = 6;
- public static final int DIM_ALPHA = 7;
-
- private SparseLongArray mPropStartDelay;
- private SparseLongArray mPropDuration;
- private SparseArray<Interpolator> mPropInterpolators;
- private Animator.AnimatorListener mListener;
-
- /**
- * The builder constructor.
- */
- public AnimationProps() {}
-
- /**
- * Creates an animation with a default {@param duration} and {@param interpolator} for all
- * properties in this animation.
- */
- public AnimationProps(int duration, Interpolator interpolator) {
- this(0, duration, interpolator, null);
- }
-
- /**
- * Creates an animation with a default {@param duration} and {@param interpolator} for all
- * properties in this animation.
- */
- public AnimationProps(int duration, Interpolator interpolator,
- Animator.AnimatorListener listener) {
- this(0, duration, interpolator, listener);
- }
-
- /**
- * Creates an animation with a default {@param startDelay}, {@param duration} and
- * {@param interpolator} for all properties in this animation.
- */
- public AnimationProps(int startDelay, int duration, Interpolator interpolator) {
- this(startDelay, duration, interpolator, null);
- }
-
- /**
- * Creates an animation with a default {@param startDelay}, {@param duration} and
- * {@param interpolator} for all properties in this animation.
- */
- public AnimationProps(int startDelay, int duration, Interpolator interpolator,
- Animator.AnimatorListener listener) {
- setStartDelay(ALL, startDelay);
- setDuration(ALL, duration);
- setInterpolator(ALL, interpolator);
- setListener(listener);
- }
-
- /**
- * Creates a new {@link AnimatorSet} that will animate the given animators. Callers need to
- * manually apply the individual animation properties for each of the animators respectively.
- */
- public AnimatorSet createAnimator(List<Animator> animators) {
- AnimatorSet anim = new AnimatorSet();
- if (mListener != null) {
- anim.addListener(mListener);
- }
- anim.playTogether(animators);
- return anim;
- }
-
- /**
- * Applies the specific start delay, duration and interpolator to the given {@param animator}
- * for the specified {@param propertyType}.
- */
- public <T extends ValueAnimator> T apply(@PropType int propertyType, T animator) {
- animator.setStartDelay(getStartDelay(propertyType));
- animator.setDuration(getDuration(propertyType));
- animator.setInterpolator(getInterpolator(propertyType));
- return animator;
- }
-
- /**
- * Sets a start delay for a specific property.
- */
- public AnimationProps setStartDelay(@PropType int propertyType, int startDelay) {
- if (mPropStartDelay == null) {
- mPropStartDelay = new SparseLongArray();
- }
- mPropStartDelay.append(propertyType, startDelay);
- return this;
- }
-
- /**
- * Returns the start delay for a specific property.
- */
- public long getStartDelay(@PropType int propertyType) {
- if (mPropStartDelay != null) {
- long startDelay = mPropStartDelay.get(propertyType, -1);
- if (startDelay != -1) {
- return startDelay;
- }
- return mPropStartDelay.get(ALL, 0);
- }
- return 0;
- }
-
- /**
- * Sets a duration for a specific property.
- */
- public AnimationProps setDuration(@PropType int propertyType, int duration) {
- if (mPropDuration == null) {
- mPropDuration = new SparseLongArray();
- }
- mPropDuration.append(propertyType, duration);
- return this;
- }
-
- /**
- * Returns the duration for a specific property.
- */
- public long getDuration(@PropType int propertyType) {
- if (mPropDuration != null) {
- long duration = mPropDuration.get(propertyType, -1);
- if (duration != -1) {
- return duration;
- }
- return mPropDuration.get(ALL, 0);
- }
- return 0;
- }
-
- /**
- * Sets an interpolator for a specific property.
- */
- public AnimationProps setInterpolator(@PropType int propertyType, Interpolator interpolator) {
- if (mPropInterpolators == null) {
- mPropInterpolators = new SparseArray<>();
- }
- mPropInterpolators.append(propertyType, interpolator);
- return this;
- }
-
- /**
- * Returns the interpolator for a specific property, falling back to the general interpolator
- * if there is no specific property interpolator.
- */
- public Interpolator getInterpolator(@PropType int propertyType) {
- if (mPropInterpolators != null) {
- Interpolator interp = mPropInterpolators.get(propertyType);
- if (interp != null) {
- return interp;
- }
- return mPropInterpolators.get(ALL, LINEAR_INTERPOLATOR);
- }
- return LINEAR_INTERPOLATOR;
- }
-
- /**
- * Sets an animator listener for this animation.
- */
- public AnimationProps setListener(Animator.AnimatorListener listener) {
- mListener = listener;
- return this;
- }
-
- /**
- * Returns the animator listener for this animation.
- */
- public Animator.AnimatorListener getListener() {
- return mListener;
- }
-
- /**
- * Returns whether this animation has any duration.
- */
- public boolean isImmediate() {
- int count = mPropDuration.size();
- for (int i = 0; i < count; i++) {
- if (mPropDuration.valueAt(i) > 0) {
- return false;
- }
- }
- return true;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/utilities/Utilities.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/utilities/Utilities.java
deleted file mode 100644
index ff58eba71e8d..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/utilities/Utilities.java
+++ /dev/null
@@ -1,339 +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 com.android.systemui.recents.utilities;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.RectEvaluator;
-import android.annotation.FloatRange;
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Trace;
-import android.util.ArraySet;
-import android.util.IntProperty;
-import android.util.Property;
-import android.util.TypedValue;
-import android.view.Surface;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.ViewRootImpl;
-import android.view.ViewStub;
-
-import com.android.systemui.shared.recents.utilities.RectFEvaluator;
-import java.util.ArrayList;
-import java.util.Collections;
-
-/* Common code */
-public class Utilities {
-
- public static final Property<Drawable, Integer> DRAWABLE_ALPHA =
- new IntProperty<Drawable>("drawableAlpha") {
- @Override
- public void setValue(Drawable object, int alpha) {
- object.setAlpha(alpha);
- }
-
- @Override
- public Integer get(Drawable object) {
- return object.getAlpha();
- }
- };
-
- public static final Property<Drawable, Rect> DRAWABLE_RECT =
- new Property<Drawable, Rect>(Rect.class, "drawableBounds") {
- @Override
- public void set(Drawable object, Rect bounds) {
- object.setBounds(bounds);
- }
-
- @Override
- public Rect get(Drawable object) {
- return object.getBounds();
- }
- };
-
- public static final RectFEvaluator RECTF_EVALUATOR = new RectFEvaluator();
- public static final RectEvaluator RECT_EVALUATOR = new RectEvaluator(new Rect());
-
- /**
- * @return the first parent walking up the view hierarchy that has the given class type.
- *
- * @param parentClass must be a class derived from {@link View}
- */
- public static <T extends View> T findParent(View v, Class<T> parentClass) {
- ViewParent parent = v.getParent();
- while (parent != null) {
- if (parentClass.isAssignableFrom(parent.getClass())) {
- return (T) parent;
- }
- parent = parent.getParent();
- }
- return null;
- }
-
- /**
- * Initializes the {@param setOut} with the given object.
- */
- public static <T> ArraySet<T> objectToSet(T obj, ArraySet<T> setOut) {
- setOut.clear();
- if (obj != null) {
- setOut.add(obj);
- }
- return setOut;
- }
-
- /**
- * Replaces the contents of {@param setOut} with the contents of the {@param array}.
- */
- public static <T> ArraySet<T> arrayToSet(T[] array, ArraySet<T> setOut) {
- setOut.clear();
- if (array != null) {
- Collections.addAll(setOut, array);
- }
- return setOut;
- }
-
- /**
- * @return the clamped {@param value} between the provided {@param min} and {@param max}.
- */
- public static int clamp(int value, int min, int max) {
- return Math.max(min, Math.min(max, value));
- }
-
- /**
- * @return the clamped {@param value} between 0 and 1.
- */
- public static float clamp01(float value) {
- return Math.max(0f, Math.min(1f, value));
- }
-
- /**
- * Scales the {@param value} to be proportionally between the {@param min} and
- * {@param max} values.
- *
- * @param value must be between 0 and 1
- */
- public static float mapRange(@FloatRange(from=0.0,to=1.0) float value, float min, float max) {
- return min + (value * (max - min));
- }
-
- /**
- * Scales the {@param value} proportionally from {@param min} and {@param max} to 0 and 1.
- *
- * @param value must be between {@param min} and {@param max}
- */
- public static float unmapRange(float value, float min, float max) {
- return (value - min) / (max - min);
- }
-
- /** Scales a rect about its centroid */
- public static void scaleRectAboutCenter(RectF r, float scale) {
- if (scale != 1.0f) {
- float cx = r.centerX();
- float cy = r.centerY();
- r.offset(-cx, -cy);
- r.left *= scale;
- r.top *= scale;
- r.right *= scale;
- r.bottom *= scale;
- r.offset(cx, cy);
- }
- }
-
- /** Returns the base color overlaid with another overlay color with a specified alpha. */
- public static int getColorWithOverlay(int baseColor, int overlayColor, float overlayAlpha) {
- return Color.rgb(
- (int) (overlayAlpha * Color.red(baseColor) +
- (1f - overlayAlpha) * Color.red(overlayColor)),
- (int) (overlayAlpha * Color.green(baseColor) +
- (1f - overlayAlpha) * Color.green(overlayColor)),
- (int) (overlayAlpha * Color.blue(baseColor) +
- (1f - overlayAlpha) * Color.blue(overlayColor)));
- }
-
- /**
- * Cancels an animation ensuring that if it has listeners, onCancel and onEnd
- * are not called.
- */
- public static void cancelAnimationWithoutCallbacks(Animator animator) {
- if (animator != null && animator.isStarted()) {
- removeAnimationListenersRecursive(animator);
- animator.cancel();
- }
- }
-
- /**
- * Recursively removes all the listeners of all children of this animator
- */
- public static void removeAnimationListenersRecursive(Animator animator) {
- if (animator instanceof AnimatorSet) {
- ArrayList<Animator> animators = ((AnimatorSet) animator).getChildAnimations();
- for (int i = animators.size() - 1; i >= 0; i--) {
- removeAnimationListenersRecursive(animators.get(i));
- }
- }
- animator.removeAllListeners();
- }
-
- /**
- * Sets the given {@link View}'s frame from its current translation.
- */
- public static void setViewFrameFromTranslation(View v) {
- RectF taskViewRect = new RectF(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
- taskViewRect.offset(v.getTranslationX(), v.getTranslationY());
- v.setTranslationX(0);
- v.setTranslationY(0);
- v.setLeftTopRightBottom((int) taskViewRect.left, (int) taskViewRect.top,
- (int) taskViewRect.right, (int) taskViewRect.bottom);
- }
-
- /**
- * Returns a view stub for the given view id.
- */
- public static ViewStub findViewStubById(View v, int stubId) {
- return (ViewStub) v.findViewById(stubId);
- }
-
- /**
- * Returns a view stub for the given view id.
- */
- public static ViewStub findViewStubById(Activity a, int stubId) {
- return (ViewStub) a.findViewById(stubId);
- }
-
- /**
- * Used for debugging, converts DP to PX.
- */
- public static float dpToPx(Resources res, float dp) {
- return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, res.getDisplayMetrics());
- }
-
- /**
- * Adds a trace event for debugging.
- */
- public static void addTraceEvent(String event) {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, event);
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
- }
-
- /**
- * Returns whether this view, or one of its descendants have accessibility focus.
- */
- public static boolean isDescendentAccessibilityFocused(View v) {
- if (v.isAccessibilityFocused()) {
- return true;
- }
-
- if (v instanceof ViewGroup) {
- ViewGroup vg = (ViewGroup) v;
- int childCount = vg.getChildCount();
- for (int i = 0; i < childCount; i++) {
- if (isDescendentAccessibilityFocused(vg.getChildAt(i))) {
- return true;
- }
- }
- }
- return false;
- }
-
- /**
- * Returns the application configuration, which is independent of the activity's current
- * configuration in multiwindow.
- */
- public static Configuration getAppConfiguration(Context context) {
- return context.getApplicationContext().getResources().getConfiguration();
- }
-
- /**
- * @return The next frame name for the specified surface or -1 if the surface is no longer
- * valid.
- */
- public static long getNextFrameNumber(Surface s) {
- return s != null && s.isValid()
- ? s.getNextFrameNumber()
- : -1;
-
- }
-
- /**
- * @return The surface for the specified view.
- */
- public static @Nullable Surface getSurface(View v) {
- ViewRootImpl viewRoot = v.getViewRootImpl();
- if (viewRoot == null) {
- return null;
- }
- return viewRoot.mSurface;
- }
-
- /**
- * Returns a lightweight dump of a rect.
- */
- public static String dumpRect(Rect r) {
- if (r == null) {
- return "N:0,0-0,0";
- }
- return r.left + "," + r.top + "-" + r.right + "," + r.bottom;
- }
-
- /**
- * Posts a runnable on a handler at the front of the queue ignoring any sync barriers.
- */
- public static void postAtFrontOfQueueAsynchronously(Handler h, Runnable r) {
- Message msg = h.obtainMessage().setCallback(r);
- h.sendMessageAtFrontOfQueue(msg);
- }
-
- /** Calculates the constrast between two colors, using the algorithm provided by the WCAG v2. */
- public static float computeContrastBetweenColors(int bg, int fg) {
- float bgR = Color.red(bg) / 255f;
- float bgG = Color.green(bg) / 255f;
- float bgB = Color.blue(bg) / 255f;
- bgR = (bgR < 0.03928f) ? bgR / 12.92f : (float) Math.pow((bgR + 0.055f) / 1.055f, 2.4f);
- bgG = (bgG < 0.03928f) ? bgG / 12.92f : (float) Math.pow((bgG + 0.055f) / 1.055f, 2.4f);
- bgB = (bgB < 0.03928f) ? bgB / 12.92f : (float) Math.pow((bgB + 0.055f) / 1.055f, 2.4f);
- float bgL = 0.2126f * bgR + 0.7152f * bgG + 0.0722f * bgB;
-
- float fgR = Color.red(fg) / 255f;
- float fgG = Color.green(fg) / 255f;
- float fgB = Color.blue(fg) / 255f;
- fgR = (fgR < 0.03928f) ? fgR / 12.92f : (float) Math.pow((fgR + 0.055f) / 1.055f, 2.4f);
- fgG = (fgG < 0.03928f) ? fgG / 12.92f : (float) Math.pow((fgG + 0.055f) / 1.055f, 2.4f);
- fgB = (fgB < 0.03928f) ? fgB / 12.92f : (float) Math.pow((fgB + 0.055f) / 1.055f, 2.4f);
- float fgL = 0.2126f * fgR + 0.7152f * fgG + 0.0722f * fgB;
-
- return Math.abs((fgL + 0.05f) / (bgL + 0.05f));
- }
-
- /**
- * @return the clamped {@param value} between the provided {@param min} and {@param max}.
- */
- public static float clamp(float value, float min, float max) {
- return Math.max(min, Math.min(max, value));
- }
-
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/AnimateableViewBounds.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/AnimateableViewBounds.java
deleted file mode 100644
index e18850639336..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/AnimateableViewBounds.java
+++ /dev/null
@@ -1,135 +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 com.android.systemui.recents.views;
-
-import android.graphics.Outline;
-import android.graphics.Rect;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-
-import com.android.systemui.recents.utilities.Utilities;
-
-/**
- * An outline provider that has a clip and outline that can be animated.
- */
-public class AnimateableViewBounds extends ViewOutlineProvider {
-
- private static final float MIN_ALPHA = 0.1f;
- private static final float MAX_ALPHA = 0.8f;
-
- protected View mSourceView;
- protected Rect mClipRect = new Rect();
- protected Rect mClipBounds = new Rect();
- protected Rect mLastClipBounds = new Rect();
- protected int mCornerRadius;
- protected float mAlpha = 1f;
-
- public AnimateableViewBounds(View source, int cornerRadius) {
- mSourceView = source;
- mCornerRadius = cornerRadius;
- }
-
- /**
- * Resets the right and bottom clip for this view.
- */
- public void reset() {
- mClipRect.set(0, 0, 0, 0);
- updateClipBounds();
- }
-
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setAlpha(Utilities.mapRange(mAlpha, MIN_ALPHA, MAX_ALPHA));
- if (mCornerRadius > 0) {
- outline.setRoundRect(mClipRect.left, mClipRect.top,
- mSourceView.getWidth() - mClipRect.right,
- mSourceView.getHeight() - mClipRect.bottom,
- mCornerRadius);
- } else {
- outline.setRect(mClipRect.left, mClipRect.top,
- mSourceView.getWidth() - mClipRect.right,
- mSourceView.getHeight() - mClipRect.bottom);
- }
- }
-
- /**
- * Sets the view outline alpha.
- */
- public void setAlpha(float alpha) {
- if (Float.compare(alpha, mAlpha) != 0) {
- mAlpha = alpha;
- // TODO, If both clip and alpha change in the same frame, only invalidate once
- mSourceView.invalidateOutline();
- }
- }
-
- /**
- * @return the outline alpha.
- */
- public float getAlpha() {
- return mAlpha;
- }
-
- /**
- * Sets the top clip.
- */
- public void setClipTop(int top) {
- mClipRect.top = top;
- updateClipBounds();
- }
-
- /**
- * @return the top clip.
- */
- public int getClipTop() {
- return mClipRect.top;
- }
-
- /**
- * Sets the bottom clip.
- */
- public void setClipBottom(int bottom) {
- mClipRect.bottom = bottom;
- updateClipBounds();
- }
-
- /**
- * @return the bottom clip.
- */
- public int getClipBottom() {
- return mClipRect.bottom;
- }
-
- /**
- * @return the clip bounds.
- */
- public Rect getClipBounds() {
- return mClipBounds;
- }
-
- protected void updateClipBounds() {
- mClipBounds.set(Math.max(0, mClipRect.left), Math.max(0, mClipRect.top),
- mSourceView.getWidth() - Math.max(0, mClipRect.right),
- mSourceView.getHeight() - Math.max(0, mClipRect.bottom));
- if (!mLastClipBounds.equals(mClipBounds)) {
- mSourceView.setClipBounds(mClipBounds);
- // TODO, If both clip and alpha change in the same frame, only invalidate once
- mSourceView.invalidateOutline();
- mLastClipBounds.set(mClipBounds);
- }
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/DockState.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/DockState.java
deleted file mode 100644
index d9c921cf3f7d..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/DockState.java
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.views;
-
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_INVALID;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
-import static android.view.WindowManager.DOCKED_TOP;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.annotation.IntDef;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.ColorDrawable;
-import android.util.IntProperty;
-import android.view.animation.Interpolator;
-
-import com.android.internal.policy.DockedDividerUtils;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.recents.LegacyRecentsImpl;
-import com.android.systemui.recents.utilities.Utilities;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-
-/**
- * The various possible dock states when dragging and dropping a task.
- */
-public class DockState implements DropTarget {
-
- public static final int DOCK_AREA_BG_COLOR = 0xFFffffff;
- public static final int DOCK_AREA_GRID_BG_COLOR = 0xFF000000;
-
- // The rotation to apply to the hint text
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({HORIZONTAL, VERTICAL})
- public @interface TextOrientation {}
- private static final int HORIZONTAL = 0;
- private static final int VERTICAL = 1;
-
- private static final int DOCK_AREA_ALPHA = 80;
- public static final DockState NONE = new DockState(DOCKED_INVALID, -1, 80, 255, HORIZONTAL,
- null, null, null);
- public static final DockState LEFT = new DockState(DOCKED_LEFT,
- SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, VERTICAL,
- new RectF(0, 0, 0.125f, 1), new RectF(0, 0, 0.125f, 1),
- new RectF(0, 0, 0.5f, 1));
- public static final DockState TOP = new DockState(DOCKED_TOP,
- SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA, 0, HORIZONTAL,
- new RectF(0, 0, 1, 0.125f), new RectF(0, 0, 1, 0.125f),
- new RectF(0, 0, 1, 0.5f));
- public static final DockState RIGHT = new DockState(DOCKED_RIGHT,
- SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, VERTICAL,
- new RectF(0.875f, 0, 1, 1), new RectF(0.875f, 0, 1, 1),
- new RectF(0.5f, 0, 1, 1));
- public static final DockState BOTTOM = new DockState(DOCKED_BOTTOM,
- SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA, 0, HORIZONTAL,
- new RectF(0, 0.875f, 1, 1), new RectF(0, 0.875f, 1, 1),
- new RectF(0, 0.5f, 1, 1));
-
- @Override
- public boolean acceptsDrop(int x, int y, int width, int height, Rect insets,
- boolean isCurrentTarget) {
- if (isCurrentTarget) {
- getMappedRect(expandedTouchDockArea, width, height, mTmpRect);
- return mTmpRect.contains(x, y);
- } else {
- getMappedRect(touchArea, width, height, mTmpRect);
- updateBoundsWithSystemInsets(mTmpRect, insets);
- return mTmpRect.contains(x, y);
- }
- }
-
- // Represents the view state of this dock state
- public static class ViewState {
- private static final IntProperty<ViewState> HINT_ALPHA =
- new IntProperty<ViewState>("drawableAlpha") {
- @Override
- public void setValue(ViewState object, int alpha) {
- object.mHintTextAlpha = alpha;
- object.dockAreaOverlay.invalidateSelf();
- }
-
- @Override
- public Integer get(ViewState object) {
- return object.mHintTextAlpha;
- }
- };
-
- public final int dockAreaAlpha;
- public final ColorDrawable dockAreaOverlay;
- public final int hintTextAlpha;
- public final int hintTextOrientation;
-
- private final int mHintTextResId;
- private String mHintText;
- private Paint mHintTextPaint;
- private Point mHintTextBounds = new Point();
- private int mHintTextAlpha = 255;
- private AnimatorSet mDockAreaOverlayAnimator;
- private Rect mTmpRect = new Rect();
-
- private ViewState(int areaAlpha, int hintAlpha, @TextOrientation int hintOrientation,
- int hintTextResId) {
- dockAreaAlpha = areaAlpha;
- dockAreaOverlay = new ColorDrawable(LegacyRecentsImpl.getConfiguration().isGridEnabled
- ? DOCK_AREA_GRID_BG_COLOR : DOCK_AREA_BG_COLOR);
- dockAreaOverlay.setAlpha(0);
- hintTextAlpha = hintAlpha;
- hintTextOrientation = hintOrientation;
- mHintTextResId = hintTextResId;
- mHintTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mHintTextPaint.setColor(Color.WHITE);
- }
-
- /**
- * Updates the view state with the given context.
- */
- public void update(Context context) {
- Resources res = context.getResources();
- mHintText = context.getString(mHintTextResId);
- mHintTextPaint.setTextSize(res.getDimensionPixelSize(
- R.dimen.recents_drag_hint_text_size));
- mHintTextPaint.getTextBounds(mHintText, 0, mHintText.length(), mTmpRect);
- mHintTextBounds.set((int) mHintTextPaint.measureText(mHintText), mTmpRect.height());
- }
-
- /**
- * Draws the current view state.
- */
- public void draw(Canvas canvas) {
- // Draw the overlay background
- if (dockAreaOverlay.getAlpha() > 0) {
- dockAreaOverlay.draw(canvas);
- }
-
- // Draw the hint text
- if (mHintTextAlpha > 0) {
- Rect bounds = dockAreaOverlay.getBounds();
- int x = bounds.left + (bounds.width() - mHintTextBounds.x) / 2;
- int y = bounds.top + (bounds.height() + mHintTextBounds.y) / 2;
- mHintTextPaint.setAlpha(mHintTextAlpha);
- if (hintTextOrientation == VERTICAL) {
- canvas.save();
- canvas.rotate(-90f, bounds.centerX(), bounds.centerY());
- }
- canvas.drawText(mHintText, x, y, mHintTextPaint);
- if (hintTextOrientation == VERTICAL) {
- canvas.restore();
- }
- }
- }
-
- /**
- * Creates a new bounds and alpha animation.
- */
- public void startAnimation(Rect bounds, int areaAlpha, int hintAlpha, int duration,
- Interpolator interpolator, boolean animateAlpha, boolean animateBounds) {
- if (mDockAreaOverlayAnimator != null) {
- mDockAreaOverlayAnimator.cancel();
- }
-
- ObjectAnimator anim;
- ArrayList<Animator> animators = new ArrayList<>();
- if (dockAreaOverlay.getAlpha() != areaAlpha) {
- if (animateAlpha) {
- anim = ObjectAnimator.ofInt(dockAreaOverlay,
- Utilities.DRAWABLE_ALPHA, dockAreaOverlay.getAlpha(), areaAlpha);
- anim.setDuration(duration);
- anim.setInterpolator(interpolator);
- animators.add(anim);
- } else {
- dockAreaOverlay.setAlpha(areaAlpha);
- }
- }
- if (mHintTextAlpha != hintAlpha) {
- if (animateAlpha) {
- anim = ObjectAnimator.ofInt(this, HINT_ALPHA, mHintTextAlpha,
- hintAlpha);
- anim.setDuration(150);
- anim.setInterpolator(hintAlpha > mHintTextAlpha
- ? Interpolators.ALPHA_IN
- : Interpolators.ALPHA_OUT);
- animators.add(anim);
- } else {
- mHintTextAlpha = hintAlpha;
- dockAreaOverlay.invalidateSelf();
- }
- }
- if (bounds != null && !dockAreaOverlay.getBounds().equals(bounds)) {
- if (animateBounds) {
- PropertyValuesHolder prop = PropertyValuesHolder.ofObject(
- Utilities.DRAWABLE_RECT, Utilities.RECT_EVALUATOR,
- new Rect(dockAreaOverlay.getBounds()), bounds);
- anim = ObjectAnimator.ofPropertyValuesHolder(dockAreaOverlay, prop);
- anim.setDuration(duration);
- anim.setInterpolator(interpolator);
- animators.add(anim);
- } else {
- dockAreaOverlay.setBounds(bounds);
- }
- }
- if (!animators.isEmpty()) {
- mDockAreaOverlayAnimator = new AnimatorSet();
- mDockAreaOverlayAnimator.playTogether(animators);
- mDockAreaOverlayAnimator.start();
- }
- }
- }
-
- public final int dockSide;
- public final int createMode;
- public final ViewState viewState;
- private final RectF touchArea;
- private final RectF dockArea;
- private final RectF expandedTouchDockArea;
- private static final Rect mTmpRect = new Rect();
-
- /**
- * @param createMode used to pass to ActivityManager to dock the task
- * @param touchArea the area in which touch will initiate this dock state
- * @param dockArea the visible dock area
- * @param expandedTouchDockArea the area in which touch will continue to dock after entering
- * the initial touch area. This is also the new dock area to
- * draw.
- */
- DockState(int dockSide, int createMode, int dockAreaAlpha, int hintTextAlpha,
- @TextOrientation int hintTextOrientation, RectF touchArea, RectF dockArea,
- RectF expandedTouchDockArea) {
- this.dockSide = dockSide;
- this.createMode = createMode;
- this.viewState = new ViewState(dockAreaAlpha, hintTextAlpha, hintTextOrientation,
- R.string.recents_drag_hint_message);
- this.dockArea = dockArea;
- this.touchArea = touchArea;
- this.expandedTouchDockArea = expandedTouchDockArea;
- }
-
- /**
- * Updates the dock state with the given context.
- */
- public void update(Context context) {
- viewState.update(context);
- }
-
- /**
- * Returns the docked task bounds with the given {@param width} and {@param height}.
- */
- public Rect getPreDockedBounds(int width, int height, Rect insets) {
- getMappedRect(dockArea, width, height, mTmpRect);
- return updateBoundsWithSystemInsets(mTmpRect, insets);
- }
-
- /**
- * Returns the expanded docked task bounds with the given {@param width} and
- * {@param height}.
- */
- public Rect getDockedBounds(int width, int height, int dividerSize, Rect insets,
- Resources res) {
- // Calculate the docked task bounds
- boolean isHorizontalDivision =
- res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
- int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
- insets, width, height, dividerSize);
- Rect newWindowBounds = new Rect();
- DockedDividerUtils.calculateBoundsForPosition(position, dockSide, newWindowBounds,
- width, height, dividerSize);
- return newWindowBounds;
- }
-
- /**
- * Returns the task stack bounds with the given {@param width} and
- * {@param height}.
- */
- public Rect getDockedTaskStackBounds(Rect displayRect, int width, int height,
- int dividerSize, Rect insets, TaskStackLayoutAlgorithm layoutAlgorithm,
- Resources res, Rect windowRectOut) {
- // Calculate the inverse docked task bounds
- boolean isHorizontalDivision =
- res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
- int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
- insets, width, height, dividerSize);
- DockedDividerUtils.calculateBoundsForPosition(position,
- DockedDividerUtils.invertDockSide(dockSide), windowRectOut, width, height,
- dividerSize);
-
- // Calculate the task stack bounds from the new window bounds
- Rect taskStackBounds = new Rect();
- // If the task stack bounds is specifically under the dock area, then ignore the top
- // inset
- int top = dockArea.bottom < 1f
- ? 0
- : insets.top;
- // For now, ignore the left insets since we always dock on the left and show Recents
- // on the right
- layoutAlgorithm.getTaskStackBounds(displayRect, windowRectOut, top, 0, insets.right,
- taskStackBounds);
- return taskStackBounds;
- }
-
- /**
- * Returns the expanded bounds in certain dock sides such that the bounds account for the
- * system insets (namely the vertical nav bar). This call modifies and returns the given
- * {@param bounds}.
- */
- private Rect updateBoundsWithSystemInsets(Rect bounds, Rect insets) {
- if (dockSide == DOCKED_LEFT) {
- bounds.right += insets.left;
- } else if (dockSide == DOCKED_RIGHT) {
- bounds.left -= insets.right;
- }
- return bounds;
- }
-
- /**
- * Returns the mapped rect to the given dimensions.
- */
- private void getMappedRect(RectF bounds, int width, int height, Rect out) {
- out.set((int) (bounds.left * width), (int) (bounds.top * height),
- (int) (bounds.right * width), (int) (bounds.bottom * height));
- }
-} \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/DropTarget.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/DropTarget.java
deleted file mode 100644
index f2a631078d3c..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/DropTarget.java
+++ /dev/null
@@ -1,31 +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 com.android.systemui.recents.views;
-
-import android.graphics.Rect;
-
-/**
- * Represents a drop target for a drag gesture.
- */
-public interface DropTarget {
-
- /**
- * Returns whether this target can accept this drop. The x,y are relative to the top level
- * RecentsView, and the width/height are of the RecentsView.
- */
- boolean acceptsDrop(int x, int y, int width, int height, Rect insets, boolean isCurrentTarget);
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/FakeShadowDrawable.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/FakeShadowDrawable.java
deleted file mode 100644
index 86b4297663cb..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/FakeShadowDrawable.java
+++ /dev/null
@@ -1,290 +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 com.android.systemui.recents.views;
-
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.LinearGradient;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PixelFormat;
-import android.graphics.RadialGradient;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Shader;
-import android.graphics.drawable.Drawable;
-import android.util.Log;
-
-import com.android.systemui.R;
-import com.android.systemui.recents.LegacyRecentsImpl;
-import com.android.systemui.recents.RecentsConfiguration;
-
-/**
- * A rounded rectangle drawable which also includes a shadow around. This is mostly copied from
- * frameworks/support/v7/cardview/eclair-mr1/android/support/v7/widget/
- * RoundRectDrawableWithShadow.java revision c42ba8c000d1e6ce85e152dfc17089a0a69e739f with a few
- * modifications to suit our needs in SystemUI.
- */
-class FakeShadowDrawable extends Drawable {
- // used to calculate content padding
- final static double COS_45 = Math.cos(Math.toRadians(45));
-
- final static float SHADOW_MULTIPLIER = 1.5f;
-
- final float mInsetShadow; // extra shadow to avoid gaps between card and shadow
-
- Paint mCornerShadowPaint;
-
- Paint mEdgeShadowPaint;
-
- final RectF mCardBounds;
-
- float mCornerRadius;
-
- Path mCornerShadowPath;
-
- // updated value with inset
- float mMaxShadowSize;
-
- // actual value set by developer
- float mRawMaxShadowSize;
-
- // multiplied value to account for shadow offset
- float mShadowSize;
-
- // actual value set by developer
- float mRawShadowSize;
-
- private boolean mDirty = true;
-
- private final int mShadowStartColor;
-
- private final int mShadowEndColor;
-
- private boolean mAddPaddingForCorners = true;
-
- /**
- * If shadow size is set to a value above max shadow, we print a warning
- */
- private boolean mPrintedShadowClipWarning = false;
-
- public FakeShadowDrawable(Resources resources, RecentsConfiguration config) {
- mShadowStartColor = resources.getColor(R.color.fake_shadow_start_color);
- mShadowEndColor = resources.getColor(R.color.fake_shadow_end_color);
- mInsetShadow = resources.getDimension(R.dimen.fake_shadow_inset);
- setShadowSize(resources.getDimensionPixelSize(R.dimen.fake_shadow_size),
- resources.getDimensionPixelSize(R.dimen.fake_shadow_size));
- mCornerShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
- mCornerShadowPaint.setStyle(Paint.Style.FILL);
- mCornerShadowPaint.setDither(true);
- mCornerRadius = LegacyRecentsImpl.getConfiguration().isGridEnabled ?
- resources.getDimensionPixelSize(
- R.dimen.recents_grid_task_view_rounded_corners_radius) :
- resources.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
- mCardBounds = new RectF();
- mEdgeShadowPaint = new Paint(mCornerShadowPaint);
- }
-
- @Override
- public void setAlpha(int alpha) {
- mCornerShadowPaint.setAlpha(alpha);
- mEdgeShadowPaint.setAlpha(alpha);
- }
-
- @Override
- protected void onBoundsChange(Rect bounds) {
- super.onBoundsChange(bounds);
- mDirty = true;
- }
-
- void setShadowSize(float shadowSize, float maxShadowSize) {
- if (shadowSize < 0 || maxShadowSize < 0) {
- throw new IllegalArgumentException("invalid shadow size");
- }
- if (shadowSize > maxShadowSize) {
- shadowSize = maxShadowSize;
- if (!mPrintedShadowClipWarning) {
- Log.w("CardView", "Shadow size is being clipped by the max shadow size. See "
- + "{CardView#setMaxCardElevation}.");
- mPrintedShadowClipWarning = true;
- }
- }
- if (mRawShadowSize == shadowSize && mRawMaxShadowSize == maxShadowSize) {
- return;
- }
- mRawShadowSize = shadowSize;
- mRawMaxShadowSize = maxShadowSize;
- mShadowSize = shadowSize * SHADOW_MULTIPLIER + mInsetShadow;
- mMaxShadowSize = maxShadowSize + mInsetShadow;
- mDirty = true;
- invalidateSelf();
- }
-
- @Override
- public boolean getPadding(Rect padding) {
- int vOffset = (int) Math.ceil(calculateVerticalPadding(mRawMaxShadowSize, mCornerRadius,
- mAddPaddingForCorners));
- int hOffset = (int) Math.ceil(calculateHorizontalPadding(mRawMaxShadowSize, mCornerRadius,
- mAddPaddingForCorners));
- padding.set(hOffset, vOffset, hOffset, vOffset);
- return true;
- }
-
- static float calculateVerticalPadding(float maxShadowSize, float cornerRadius,
- boolean addPaddingForCorners) {
- if (addPaddingForCorners) {
- return (float) (maxShadowSize * SHADOW_MULTIPLIER + (1 - COS_45) * cornerRadius);
- } else {
- return maxShadowSize * SHADOW_MULTIPLIER;
- }
- }
-
- static float calculateHorizontalPadding(float maxShadowSize, float cornerRadius,
- boolean addPaddingForCorners) {
- if (addPaddingForCorners) {
- return (float) (maxShadowSize + (1 - COS_45) * cornerRadius);
- } else {
- return maxShadowSize;
- }
- }
-
- @Override
- public void setColorFilter(ColorFilter colorFilter) {
- mCornerShadowPaint.setColorFilter(colorFilter);
- mEdgeShadowPaint.setColorFilter(colorFilter);
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.OPAQUE;
- }
-
- @Override
- public void draw(Canvas canvas) {
- if (mDirty) {
- buildComponents(getBounds());
- mDirty = false;
- }
- canvas.translate(0, mRawShadowSize / 4);
- drawShadow(canvas);
- canvas.translate(0, -mRawShadowSize / 4);
- }
-
- private void drawShadow(Canvas canvas) {
- final float edgeShadowTop = -mCornerRadius - mShadowSize;
- final float inset = mCornerRadius + mInsetShadow + mRawShadowSize / 2;
- final boolean drawHorizontalEdges = mCardBounds.width() - 2 * inset > 0;
- final boolean drawVerticalEdges = mCardBounds.height() - 2 * inset > 0;
- // LT
- int saved = canvas.save();
- canvas.translate(mCardBounds.left + inset, mCardBounds.top + inset);
- canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
- if (drawHorizontalEdges) {
- canvas.drawRect(0, edgeShadowTop,
- mCardBounds.width() - 2 * inset, -mCornerRadius,
- mEdgeShadowPaint);
- }
- canvas.restoreToCount(saved);
- // RB
- saved = canvas.save();
- canvas.translate(mCardBounds.right - inset, mCardBounds.bottom - inset);
- canvas.rotate(180f);
- canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
- if (drawHorizontalEdges) {
- canvas.drawRect(0, edgeShadowTop,
- mCardBounds.width() - 2 * inset, -mCornerRadius + mShadowSize,
- mEdgeShadowPaint);
- }
- canvas.restoreToCount(saved);
- // LB
- saved = canvas.save();
- canvas.translate(mCardBounds.left + inset, mCardBounds.bottom - inset);
- canvas.rotate(270f);
- canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
- if (drawVerticalEdges) {
- canvas.drawRect(0, edgeShadowTop,
- mCardBounds.height() - 2 * inset, -mCornerRadius, mEdgeShadowPaint);
- }
- canvas.restoreToCount(saved);
- // RT
- saved = canvas.save();
- canvas.translate(mCardBounds.right - inset, mCardBounds.top + inset);
- canvas.rotate(90f);
- canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
- if (drawVerticalEdges) {
- canvas.drawRect(0, edgeShadowTop,
- mCardBounds.height() - 2 * inset, -mCornerRadius, mEdgeShadowPaint);
- }
- canvas.restoreToCount(saved);
- }
-
- private void buildShadowCorners() {
- RectF innerBounds = new RectF(-mCornerRadius, -mCornerRadius, mCornerRadius, mCornerRadius);
- RectF outerBounds = new RectF(innerBounds);
- outerBounds.inset(-mShadowSize, -mShadowSize);
-
- if (mCornerShadowPath == null) {
- mCornerShadowPath = new Path();
- } else {
- mCornerShadowPath.reset();
- }
- mCornerShadowPath.setFillType(Path.FillType.EVEN_ODD);
- mCornerShadowPath.moveTo(-mCornerRadius, 0);
- mCornerShadowPath.rLineTo(-mShadowSize, 0);
- // outer arc
- mCornerShadowPath.arcTo(outerBounds, 180f, 90f, false);
- // inner arc
- mCornerShadowPath.arcTo(innerBounds, 270f, -90f, false);
- mCornerShadowPath.close();
-
- float startRatio = mCornerRadius / (mCornerRadius + mShadowSize);
- mCornerShadowPaint.setShader(new RadialGradient(0, 0, mCornerRadius + mShadowSize,
- new int[]{mShadowStartColor, mShadowStartColor, mShadowEndColor},
- new float[]{0f, startRatio, 1f}
- , Shader.TileMode.CLAMP));
-
- // we offset the content shadowSize/2 pixels up to make it more realistic.
- // this is why edge shadow shader has some extra space
- // When drawing bottom edge shadow, we use that extra space.
- mEdgeShadowPaint.setShader(new LinearGradient(0, -mCornerRadius + mShadowSize, 0,
- -mCornerRadius - mShadowSize,
- new int[]{mShadowStartColor, mShadowStartColor, mShadowEndColor},
- new float[]{0f, .5f, 1f}, Shader.TileMode.CLAMP));
- }
-
- private void buildComponents(Rect bounds) {
- // Card is offset SHADOW_MULTIPLIER * maxShadowSize to account for the shadow shift.
- // We could have different top-bottom offsets to avoid extra gap above but in that case
- // center aligning Views inside the CardView would be problematic.
- final float verticalOffset = mMaxShadowSize * SHADOW_MULTIPLIER;
- mCardBounds.set(bounds.left + mMaxShadowSize, bounds.top + verticalOffset,
- bounds.right - mMaxShadowSize, bounds.bottom - verticalOffset);
- buildShadowCorners();
- }
-
- float getMinWidth() {
- final float content = 2 *
- Math.max(mRawMaxShadowSize, mCornerRadius + mInsetShadow + mRawMaxShadowSize / 2);
- return content + (mRawMaxShadowSize + mInsetShadow) * 2;
- }
-
- float getMinHeight() {
- final float content = 2 * Math.max(mRawMaxShadowSize, mCornerRadius + mInsetShadow
- + mRawMaxShadowSize * SHADOW_MULTIPLIER / 2);
- return content + (mRawMaxShadowSize * SHADOW_MULTIPLIER + mInsetShadow) * 2;
- }
-} \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/FixedSizeFrameLayout.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/FixedSizeFrameLayout.java
deleted file mode 100644
index 471df6ae41fa..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/FixedSizeFrameLayout.java
+++ /dev/null
@@ -1,97 +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.recents.views;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.widget.FrameLayout;
-
-/**
- * This is an optimized FrameLayout whose layout is completely directed by its parent, and as a
- * result, does not propagate <code>requestLayout()</code> up the view hierarchy. Instead, it will
- * relayout its children with the last known layout bounds when a layout is requested from a child
- * view.
- */
-public class FixedSizeFrameLayout extends FrameLayout {
-
- private final Rect mLayoutBounds = new Rect();
-
- public FixedSizeFrameLayout(Context context) {
- super(context);
- }
-
- public FixedSizeFrameLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public FixedSizeFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- public FixedSizeFrameLayout(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- @Override
- protected final void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- measureContents(MeasureSpec.getSize(widthMeasureSpec),
- MeasureSpec.getSize(heightMeasureSpec));
- }
-
- @Override
- protected final void onLayout(boolean changed, int left, int top, int right, int bottom) {
- mLayoutBounds.set(left, top, right, bottom);
- layoutContents(mLayoutBounds, changed);
- }
-
- @Override
- public final void requestLayout() {
- // The base ViewGroup constructor attempts to call requestLayout() before this class's
- // members are initialized so we should just propagate in that case
- if (mLayoutBounds == null || mLayoutBounds.isEmpty()) {
- super.requestLayout();
- } else {
- // If we are already laid out, then just reuse the same bounds to layout the children
- // (but not itself)
- // TODO: Investigate whether we should coalesce these to the next frame if needed
- measureContents(getMeasuredWidth(), getMeasuredHeight());
- layoutContents(mLayoutBounds, false);
- }
- }
-
- /**
- * Measures the contents of this fixed layout.
- */
- protected void measureContents(int width, int height) {
- super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
- MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
- }
-
- /**
- * Lays out the contents of this fixed layout.
- */
- protected void layoutContents(Rect bounds, boolean changed) {
- super.onLayout(changed, bounds.left, bounds.top, bounds.right, bounds.bottom);
-
- int width = getMeasuredWidth();
- int height = getMeasuredHeight();
- onSizeChanged(width, height, width, height);
- }
-
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/FixedSizeImageView.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/FixedSizeImageView.java
deleted file mode 100644
index d3b5e473eb7d..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/FixedSizeImageView.java
+++ /dev/null
@@ -1,77 +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 com.android.systemui.recents.views;
-
-import android.content.Context;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-
-import com.android.systemui.statusbar.AlphaOptimizedImageView;
-
-/**
- * This is an optimized ImageView that does not trigger a <code>requestLayout()</code> or
- * <code>invalidate()</code> when setting the image to <code>null</code>.
- */
-public class FixedSizeImageView extends AlphaOptimizedImageView {
-
- private boolean mAllowRelayout = true;
- private boolean mAllowInvalidate = true;
-
- public FixedSizeImageView(Context context) {
- this(context, null);
- }
-
- public FixedSizeImageView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public FixedSizeImageView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public FixedSizeImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- @Override
- public void requestLayout() {
- if (mAllowRelayout) {
- super.requestLayout();
- }
- }
-
- @Override
- public void invalidate() {
- if (mAllowInvalidate) {
- super.invalidate();
- }
- }
-
- @Override
- public void setImageDrawable(Drawable drawable) {
- boolean isNullBitmapDrawable = (drawable instanceof BitmapDrawable) &&
- (((BitmapDrawable) drawable).getBitmap() == null);
- if (drawable == null || isNullBitmapDrawable) {
- mAllowRelayout = false;
- mAllowInvalidate = false;
- }
- super.setImageDrawable(drawable);
- mAllowRelayout = true;
- mAllowInvalidate = true;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsEntrancePathInterpolator.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsEntrancePathInterpolator.java
deleted file mode 100644
index e32da2d3f6be..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsEntrancePathInterpolator.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.views;
-
-import android.view.animation.PathInterpolator;
-
-/**
- * A helper interpolator to stagger the entrance animation in recents by offsetting the start time
- */
-public class RecentsEntrancePathInterpolator extends PathInterpolator {
- final float mStartOffsetFraction;
-
- /**
- * Create an interpolator for a cubic Bezier curve with an offset play time. The end points
- * <code>(0, 0)</code> and <code>(1, 1)</code> are assumed.
- *
- * @param controlX1 The x coordinate of the first control point of the cubic Bezier.
- * @param controlY1 The y coordinate of the first control point of the cubic Bezier.
- * @param controlX2 The x coordinate of the second control point of the cubic Bezier.
- * @param controlY2 The y coordinate of the second control point of the cubic Bezier.
- * @param startOffsetFraction The fraction from 0 to 1 to start the animation from
- */
- public RecentsEntrancePathInterpolator(float controlX1, float controlY1, float controlX2,
- float controlY2, float startOffsetFraction) {
- super(controlX1, controlY1, controlX2, controlY2);
- mStartOffsetFraction = startOffsetFraction;
- }
-
- @Override
- public float getInterpolation(float t) {
- return super.getInterpolation(t + mStartOffsetFraction);
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsTransitionComposer.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsTransitionComposer.java
deleted file mode 100644
index ce6631820fd8..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsTransitionComposer.java
+++ /dev/null
@@ -1,175 +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 com.android.systemui.recents.views;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-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 android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Rect;
-import android.util.Log;
-
-import com.android.systemui.recents.LegacyRecentsImpl;
-import com.android.systemui.recents.RecentsDebugFlags;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
-import com.android.systemui.shared.recents.view.RecentsTransition;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A helper class to create the transition app animation specs to/from Recents
- */
-public class RecentsTransitionComposer {
-
- private static final String TAG = "RecentsTransitionComposer";
-
- private Context mContext;
- private TaskViewTransform mTmpTransform = new TaskViewTransform();
-
- public RecentsTransitionComposer(Context context) {
- mContext = context;
- }
-
- /**
- * Composes a single animation spec for the given {@link TaskView}
- */
- private static AppTransitionAnimationSpecCompat composeAnimationSpec(TaskStackView stackView,
- TaskView taskView, TaskViewTransform transform, boolean addHeaderBitmap) {
- Bitmap b = null;
- if (addHeaderBitmap) {
- b = composeHeaderBitmap(taskView, transform);
- if (b == null) {
- return null;
- }
- }
-
- Rect taskRect = new Rect();
- transform.rect.round(taskRect);
- // Disable in for low ram devices because each task does in Recents does not have fullscreen
- // height (stackView height) and when transitioning to fullscreen app, the code below would
- // force the task thumbnail to full stackView height immediately causing the transition
- // jarring.
- if (!LegacyRecentsImpl.getConfiguration().isLowRamDevice && taskView.getTask() !=
- stackView.getStack().getFrontMostTask()) {
- taskRect.bottom = taskRect.top + stackView.getMeasuredHeight();
- }
- return new AppTransitionAnimationSpecCompat(taskView.getTask().key.id, b, taskRect);
- }
-
- /**
- * Composes the transition spec when docking a task, which includes a full task bitmap.
- */
- public List<AppTransitionAnimationSpecCompat> composeDockAnimationSpec(TaskView taskView,
- Rect bounds) {
- mTmpTransform.fillIn(taskView);
- Task task = taskView.getTask();
- Bitmap buffer = RecentsTransitionComposer.composeTaskBitmap(taskView, mTmpTransform);
- return Collections.singletonList(new AppTransitionAnimationSpecCompat(task.key.id, buffer,
- bounds));
- }
-
- /**
- * Composes the animation specs for all the tasks in the target stack.
- */
- public List<AppTransitionAnimationSpecCompat> composeAnimationSpecs(final Task task,
- final TaskStackView stackView, int windowingMode, int activityType, Rect windowRect) {
- // Calculate the offscreen task rect (for tasks that are not backed by views)
- TaskView taskView = stackView.getChildViewForTask(task);
- TaskStackLayoutAlgorithm stackLayout = stackView.getStackAlgorithm();
- Rect offscreenTaskRect = new Rect();
- stackLayout.getFrontOfStackTransform().rect.round(offscreenTaskRect);
-
- // If this is a full screen stack, the transition will be towards the single, full screen
- // task. We only need the transition spec for this task.
-
- // TODO: Sometimes targetStackId is not initialized after reboot, so we also have to
- // check for INVALID_STACK_ID (now WINDOWING_MODE_UNDEFINED)
- if (windowingMode == WINDOWING_MODE_FULLSCREEN
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
- || activityType == ACTIVITY_TYPE_ASSISTANT
- || windowingMode == WINDOWING_MODE_UNDEFINED) {
- List<AppTransitionAnimationSpecCompat> specs = new ArrayList<>();
- if (taskView == null) {
- specs.add(composeOffscreenAnimationSpec(task, offscreenTaskRect));
- } else {
- mTmpTransform.fillIn(taskView);
- stackLayout.transformToScreenCoordinates(mTmpTransform, windowRect);
- AppTransitionAnimationSpecCompat spec = composeAnimationSpec(stackView, taskView,
- mTmpTransform, true /* addHeaderBitmap */);
- if (spec != null) {
- specs.add(spec);
- }
- }
- return specs;
- }
- return Collections.emptyList();
- }
-
- /**
- * Composes a single animation spec for the given {@link Task}
- */
- private static AppTransitionAnimationSpecCompat composeOffscreenAnimationSpec(Task task,
- Rect taskRect) {
- return new AppTransitionAnimationSpecCompat(task.key.id, null, taskRect);
- }
-
- public static Bitmap composeTaskBitmap(TaskView taskView, TaskViewTransform transform) {
- float scale = transform.scale;
- int fromWidth = (int) (transform.rect.width() * scale);
- int fromHeight = (int) (transform.rect.height() * scale);
- if (fromWidth == 0 || fromHeight == 0) {
- Log.e(TAG, "Could not compose thumbnail for task: " + taskView.getTask() +
- " at transform: " + transform);
-
- return RecentsTransition.drawViewIntoHardwareBitmap(1, 1, null, 1f, 0x00ffffff);
- } else {
- if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
- return RecentsTransition.drawViewIntoHardwareBitmap(fromWidth, fromHeight, null, 1f,
- 0xFFff0000);
- } else {
- return RecentsTransition.drawViewIntoHardwareBitmap(fromWidth, fromHeight, taskView,
- scale, 0);
- }
- }
- }
-
- private static Bitmap composeHeaderBitmap(TaskView taskView,
- TaskViewTransform transform) {
- float scale = transform.scale;
- int headerWidth = (int) (transform.rect.width());
- int headerHeight = (int) (taskView.mHeaderView.getMeasuredHeight() * scale);
- if (headerWidth == 0 || headerHeight == 0) {
- return null;
- }
-
- if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
- return RecentsTransition.drawViewIntoHardwareBitmap(headerWidth, headerHeight, null, 1f,
- 0xFFff0000);
- } else {
- return RecentsTransition.drawViewIntoHardwareBitmap(headerWidth, headerHeight,
- taskView.mHeaderView, scale, 0);
- }
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java
deleted file mode 100644
index e60ffba435ff..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java
+++ /dev/null
@@ -1,1077 +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 com.android.systemui.recents.views;
-
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-
-import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS;
-
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.annotation.Nullable;
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Point;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.util.ArraySet;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.MathUtils;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewDebug;
-import android.view.ViewPropertyAnimator;
-import android.view.Window;
-import android.view.WindowInsets;
-import android.widget.FrameLayout;
-import android.widget.TextView;
-
-import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.colorextraction.drawable.ScrimDrawable;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settingslib.Utils;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.recents.LegacyRecentsImpl;
-import com.android.systemui.recents.RecentsActivity;
-import com.android.systemui.recents.RecentsActivityLaunchState;
-import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
-import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
-import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
-import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
-import com.android.systemui.recents.events.activity.ExitRecentsWindowFirstAnimationFrameEvent;
-import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
-import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
-import com.android.systemui.recents.events.activity.ShowEmptyViewEvent;
-import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
-import com.android.systemui.recents.events.component.ExpandPipEvent;
-import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
-import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
-import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
-import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
-import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
-import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
-import com.android.systemui.recents.misc.ReferenceCountedTrigger;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
-import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
-import com.android.systemui.shared.recents.view.RecentsTransition;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
-import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.stackdivider.WindowManagerProxy;
-import com.android.systemui.statusbar.FlingAnimationUtils;
-import com.android.systemui.statusbar.phone.ScrimController;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This view is the the top level layout that contains TaskStacks (which are laid out according
- * to their SpaceNode bounds.
- */
-public class RecentsView extends FrameLayout {
-
- private static final String TAG = "RecentsView";
-
- private static final int DEFAULT_UPDATE_SCRIM_DURATION = 200;
-
- private static final int SHOW_STACK_ACTION_BUTTON_DURATION = 134;
- private static final int HIDE_STACK_ACTION_BUTTON_DURATION = 100;
-
- private static final int BUSY_RECENTS_TASK_COUNT = 3;
-
- private Handler mHandler;
- private TaskStackView mTaskStackView;
- private TextView mStackActionButton;
- private TextView mEmptyView;
- private final float mStackButtonShadowRadius;
- private final PointF mStackButtonShadowDistance;
- private final int mStackButtonShadowColor;
-
- private boolean mAwaitingFirstLayout = true;
-
- @ViewDebug.ExportedProperty(category="recents")
- Rect mSystemInsets = new Rect();
- private int mDividerSize;
-
- private float mBusynessFactor;
- private ScrimDrawable mBackgroundScrim;
- private ColorDrawable mMultiWindowBackgroundScrim;
- private ValueAnimator mBackgroundScrimAnimator;
- private Point mTmpDisplaySize = new Point();
-
- private final AnimatorUpdateListener mUpdateBackgroundScrimAlpha = (animation) -> {
- int alpha = (Integer) animation.getAnimatedValue();
- mBackgroundScrim.setAlpha(alpha);
- mMultiWindowBackgroundScrim.setAlpha(alpha);
- };
-
- private RecentsTransitionComposer mTransitionHelper;
- @ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
- private RecentsViewTouchHandler mTouchHandler;
- private final FlingAnimationUtils mFlingAnimationUtils;
-
- public RecentsView(Context context) {
- this(context, null);
- }
-
- public RecentsView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- setWillNotDraw(false);
-
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- mHandler = new Handler();
- mTransitionHelper = new RecentsTransitionComposer(getContext());
- mDividerSize = ssp.getDockedDividerSize(context);
- mTouchHandler = new RecentsViewTouchHandler(this);
- mFlingAnimationUtils = new FlingAnimationUtils(context, 0.3f);
- mBackgroundScrim = new ScrimDrawable();
- mMultiWindowBackgroundScrim = new ColorDrawable();
-
- LayoutInflater inflater = LayoutInflater.from(context);
- mEmptyView = (TextView) inflater.inflate(R.layout.recents_empty, this, false);
- addView(mEmptyView);
-
- if (mStackActionButton != null) {
- removeView(mStackActionButton);
- }
- mStackActionButton = (TextView) inflater.inflate(LegacyRecentsImpl.getConfiguration()
- .isLowRamDevice
- ? R.layout.recents_low_ram_stack_action_button
- : R.layout.recents_stack_action_button,
- this, false);
-
- mStackButtonShadowRadius = mStackActionButton.getShadowRadius();
- mStackButtonShadowDistance = new PointF(mStackActionButton.getShadowDx(),
- mStackActionButton.getShadowDy());
- mStackButtonShadowColor = mStackActionButton.getShadowColor();
- addView(mStackActionButton);
-
- reevaluateStyles();
- }
-
- public void reevaluateStyles() {
- int textColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor);
- boolean usingDarkText = Color.luminance(textColor) < 0.5f;
-
- mEmptyView.setTextColor(textColor);
- mEmptyView.setCompoundDrawableTintList(new ColorStateList(new int[][]{
- {android.R.attr.state_enabled}}, new int[]{textColor}));
-
- if (mStackActionButton != null) {
- mStackActionButton.setTextColor(textColor);
- // Enable/disable shadow if text color is already dark.
- if (usingDarkText) {
- mStackActionButton.setShadowLayer(0, 0, 0, 0);
- } else {
- mStackActionButton.setShadowLayer(mStackButtonShadowRadius,
- mStackButtonShadowDistance.x, mStackButtonShadowDistance.y,
- mStackButtonShadowColor);
- }
- }
-
- // Let's also require dark status and nav bars if the text is dark
- int systemBarsStyle = usingDarkText ? View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR |
- View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR : 0;
-
- setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
- systemBarsStyle);
- }
-
- /**
- * Called from RecentsActivity when it is relaunched.
- */
- public void onReload(TaskStack stack, boolean isResumingFromVisible) {
- final RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
- final RecentsActivityLaunchState launchState = config.getLaunchState();
- final boolean isTaskStackEmpty = stack.getTaskCount() == 0;
-
- if (mTaskStackView == null) {
- isResumingFromVisible = false;
- mTaskStackView = new TaskStackView(getContext());
- mTaskStackView.setSystemInsets(mSystemInsets);
- addView(mTaskStackView);
- }
-
- // Reset the state
- mAwaitingFirstLayout = !isResumingFromVisible;
-
- // Update the stack
- mTaskStackView.onReload(isResumingFromVisible);
- updateStack(stack, true /* setStackViewTasks */);
- updateBusyness();
-
- if (isResumingFromVisible) {
- // If we are already visible, then restore the background scrim
- animateBackgroundScrim(getOpaqueScrimAlpha(), DEFAULT_UPDATE_SCRIM_DURATION);
- } else {
- // If we are already occluded by the app, then set the final background scrim alpha now.
- // Otherwise, defer until the enter animation completes to animate the scrim alpha with
- // the tasks for the home animation.
- if (launchState.launchedViaDockGesture || launchState.launchedFromApp
- || isTaskStackEmpty) {
- mBackgroundScrim.setAlpha((int) (getOpaqueScrimAlpha() * 255));
- } else {
- mBackgroundScrim.setAlpha(0);
- }
- mMultiWindowBackgroundScrim.setAlpha(mBackgroundScrim.getAlpha());
- }
- }
-
- /**
- * Called from RecentsActivity when the task stack is updated.
- */
- public void updateStack(TaskStack stack, boolean setStackViewTasks) {
- if (setStackViewTasks) {
- mTaskStackView.setTasks(stack, true /* allowNotifyStackChanges */);
- }
-
- // Update the top level view's visibilities
- if (stack.getTaskCount() > 0) {
- hideEmptyView();
- } else {
- showEmptyView(R.string.recents_empty_message);
- }
- }
-
- /**
- * Animates the scrim opacity based on how many tasks are visible.
- * Called from {@link RecentsActivity} when tasks are dismissed.
- */
- public void updateScrimOpacity() {
- if (updateBusyness()) {
- animateBackgroundScrim(getOpaqueScrimAlpha(), DEFAULT_UPDATE_SCRIM_DURATION);
- }
- }
-
- /**
- * Updates the busyness factor.
- *
- * @return True if it changed.
- */
- private boolean updateBusyness() {
- final int taskCount = mTaskStackView.getStack().getTaskCount();
- final float busyness = Math.min(taskCount, BUSY_RECENTS_TASK_COUNT)
- / (float) BUSY_RECENTS_TASK_COUNT;
- if (mBusynessFactor == busyness) {
- return false;
- } else {
- mBusynessFactor = busyness;
- return true;
- }
- }
-
- /**
- * Returns the current TaskStack.
- */
- public TaskStack getStack() {
- return mTaskStackView.getStack();
- }
-
- /**
- * Returns the window background scrim.
- */
- public void updateBackgroundScrim(Window window, boolean isInMultiWindow) {
- if (isInMultiWindow) {
- mBackgroundScrim.setCallback(null);
- window.setBackgroundDrawable(mMultiWindowBackgroundScrim);
- } else {
- mMultiWindowBackgroundScrim.setCallback(null);
- window.setBackgroundDrawable(mBackgroundScrim);
- }
- }
-
- /** Launches the focused task from the first stack if possible */
- public boolean launchFocusedTask(int logEvent) {
- if (mTaskStackView != null) {
- Task task = mTaskStackView.getFocusedTask();
- if (task != null) {
- TaskView taskView = mTaskStackView.getChildViewForTask(task);
- EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null, false));
-
- if (logEvent != 0) {
- MetricsLogger.action(getContext(), logEvent,
- task.key.getComponent().toString());
- }
- return true;
- }
- }
- return false;
- }
-
- /** Launches the task that recents was launched from if possible */
- public boolean launchPreviousTask() {
- if (LegacyRecentsImpl.getConfiguration().getLaunchState().launchedFromPipApp) {
- // If the app auto-entered PiP on the way to Recents, then just re-expand it
- EventBus.getDefault().send(new ExpandPipEvent());
- return true;
- }
-
- if (mTaskStackView != null) {
- Task task = getStack().getLaunchTarget();
- if (task != null) {
- TaskView taskView = mTaskStackView.getChildViewForTask(task);
- EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null, false));
- return true;
- }
- }
- return false;
- }
-
- /**
- * Hides the task stack and shows the empty view.
- */
- public void showEmptyView(int msgResId) {
- mTaskStackView.setVisibility(View.INVISIBLE);
- mEmptyView.setText(msgResId);
- mEmptyView.setVisibility(View.VISIBLE);
- mEmptyView.bringToFront();
- mStackActionButton.bringToFront();
- }
-
- /**
- * Shows the task stack and hides the empty view.
- */
- public void hideEmptyView() {
- mEmptyView.setVisibility(View.INVISIBLE);
- mTaskStackView.setVisibility(View.VISIBLE);
- mTaskStackView.bringToFront();
- mStackActionButton.bringToFront();
- }
-
- /**
- * Set the color of the scrim.
- *
- * @param scrimColors Colors to use.
- * @param animated Interpolate colors if true.
- */
- public void setScrimColors(ColorExtractor.GradientColors scrimColors, boolean animated) {
- mBackgroundScrim.setColor(scrimColors.getMainColor(), animated);
- int alpha = mMultiWindowBackgroundScrim.getAlpha();
- mMultiWindowBackgroundScrim.setColor(scrimColors.getMainColor());
- mMultiWindowBackgroundScrim.setAlpha(alpha);
- }
-
- @Override
- protected void onAttachedToWindow() {
- EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
- EventBus.getDefault().register(mTouchHandler, RecentsActivity.EVENT_BUS_PRIORITY + 2);
- super.onAttachedToWindow();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- EventBus.getDefault().unregister(this);
- EventBus.getDefault().unregister(mTouchHandler);
- }
-
- /**
- * This is called with the full size of the window since we are handling our own insets.
- */
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int width = MeasureSpec.getSize(widthMeasureSpec);
- int height = MeasureSpec.getSize(heightMeasureSpec);
-
- if (mTaskStackView.getVisibility() != GONE) {
- mTaskStackView.measure(widthMeasureSpec, heightMeasureSpec);
- }
-
- // Measure the empty view to the full size of the screen
- if (mEmptyView.getVisibility() != GONE) {
- measureChild(mEmptyView, MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
- MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
- }
-
- // Measure the stack action button within the constraints of the space above the stack
- Rect buttonBounds = mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect();
- measureChild(mStackActionButton,
- MeasureSpec.makeMeasureSpec(buttonBounds.width(), MeasureSpec.AT_MOST),
- MeasureSpec.makeMeasureSpec(buttonBounds.height(), MeasureSpec.AT_MOST));
-
- setMeasuredDimension(width, height);
- }
-
- /**
- * This is called with the full size of the window since we are handling our own insets.
- */
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- if (mTaskStackView.getVisibility() != GONE) {
- mTaskStackView.layout(left, top, left + getMeasuredWidth(), top + getMeasuredHeight());
- }
-
- // Layout the empty view
- if (mEmptyView.getVisibility() != GONE) {
- int leftRightInsets = mSystemInsets.left + mSystemInsets.right;
- int topBottomInsets = mSystemInsets.top + mSystemInsets.bottom;
- int childWidth = mEmptyView.getMeasuredWidth();
- int childHeight = mEmptyView.getMeasuredHeight();
- int childLeft = left + mSystemInsets.left +
- Math.max(0, (right - left - leftRightInsets - childWidth)) / 2;
- int childTop = top + mSystemInsets.top +
- Math.max(0, (bottom - top - topBottomInsets - childHeight)) / 2;
- mEmptyView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
- }
-
- // Needs to know the screen size since the gradient never scales up or down
- // even when bounds change.
- mContext.getDisplay().getRealSize(mTmpDisplaySize);
- mBackgroundScrim.setBounds(left, top, right, bottom);
- mMultiWindowBackgroundScrim.setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
-
- // Layout the stack action button such that its drawable is start-aligned with the
- // stack, vertically centered in the available space above the stack
- Rect buttonBounds = getStackActionButtonBoundsFromStackLayout();
- mStackActionButton.layout(buttonBounds.left, buttonBounds.top, buttonBounds.right,
- buttonBounds.bottom);
-
- if (mAwaitingFirstLayout) {
- mAwaitingFirstLayout = false;
- // If launched via dragging from the nav bar, then we should translate the whole view
- // down offscreen
- RecentsActivityLaunchState launchState = LegacyRecentsImpl.getConfiguration().getLaunchState();
- if (launchState.launchedViaDragGesture) {
- setTranslationY(getMeasuredHeight());
- } else {
- setTranslationY(0f);
- }
-
- if (LegacyRecentsImpl.getConfiguration().isLowRamDevice
- && mEmptyView.getVisibility() == View.VISIBLE) {
- animateEmptyView(true /* show */, null /* postAnimationTrigger */);
- }
- }
- }
-
- @Override
- public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- mSystemInsets.set(insets.getSystemWindowInsetsAsRect());
- mTaskStackView.setSystemInsets(mSystemInsets);
- requestLayout();
- return insets;
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- return mTouchHandler.onInterceptTouchEvent(ev);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- return mTouchHandler.onTouchEvent(ev);
- }
-
- @Override
- public void onDrawForeground(Canvas canvas) {
- super.onDrawForeground(canvas);
-
- ArrayList<DockState> visDockStates = mTouchHandler.getVisibleDockStates();
- for (int i = visDockStates.size() - 1; i >= 0; i--) {
- visDockStates.get(i).viewState.draw(canvas);
- }
- }
-
- @Override
- protected boolean verifyDrawable(Drawable who) {
- ArrayList<DockState> visDockStates = mTouchHandler.getVisibleDockStates();
- for (int i = visDockStates.size() - 1; i >= 0; i--) {
- Drawable d = visDockStates.get(i).viewState.dockAreaOverlay;
- if (d == who) {
- return true;
- }
- }
- return super.verifyDrawable(who);
- }
-
- /**** EventBus Events ****/
-
- public final void onBusEvent(LaunchTaskEvent event) {
- launchTaskFromRecents(getStack(), event.task, mTaskStackView, event.taskView,
- event.screenPinningRequested, event.targetWindowingMode, event.targetActivityType);
- if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- EventBus.getDefault().send(new HideStackActionButtonEvent(false /* translate */));
- }
- }
-
- public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
- int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION;
- // Hide the stack action button
- EventBus.getDefault().send(new HideStackActionButtonEvent());
- animateBackgroundScrim(0f, taskViewExitToHomeDuration);
-
- if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- animateEmptyView(false /* show */, event.getAnimationTrigger());
- }
- }
-
- public final void onBusEvent(DragStartEvent event) {
- updateVisibleDockRegions(LegacyRecentsImpl.getConfiguration().getDockStatesForCurrentOrientation(),
- true /* isDefaultDockState */, DockState.NONE.viewState.dockAreaAlpha,
- DockState.NONE.viewState.hintTextAlpha,
- true /* animateAlpha */, false /* animateBounds */);
-
- // Temporarily hide the stack action button without changing visibility
- if (mStackActionButton != null) {
- mStackActionButton.animate()
- .alpha(0f)
- .setDuration(HIDE_STACK_ACTION_BUTTON_DURATION)
- .setInterpolator(Interpolators.ALPHA_OUT)
- .start();
- }
- }
-
- public final void onBusEvent(DragDropTargetChangedEvent event) {
- if (event.dropTarget == null || !(event.dropTarget instanceof DockState)) {
- updateVisibleDockRegions(
- LegacyRecentsImpl.getConfiguration().getDockStatesForCurrentOrientation(),
- true /* isDefaultDockState */, DockState.NONE.viewState.dockAreaAlpha,
- DockState.NONE.viewState.hintTextAlpha,
- true /* animateAlpha */, true /* animateBounds */);
- } else {
- final DockState dockState = (DockState) event.dropTarget;
- updateVisibleDockRegions(new DockState[] {dockState},
- false /* isDefaultDockState */, -1, -1, true /* animateAlpha */,
- true /* animateBounds */);
- }
- if (mStackActionButton != null) {
- event.addPostAnimationCallback(new Runnable() {
- @Override
- public void run() {
- // Move the clear all button to its new position
- Rect buttonBounds = getStackActionButtonBoundsFromStackLayout();
- mStackActionButton.setLeftTopRightBottom(buttonBounds.left, buttonBounds.top,
- buttonBounds.right, buttonBounds.bottom);
- }
- });
- }
- }
-
- public final void onBusEvent(final DragEndEvent event) {
- // Handle the case where we drop onto a dock region
- if (event.dropTarget instanceof DockState) {
- final DockState dockState = (DockState) event.dropTarget;
-
- // Hide the dock region
- updateVisibleDockRegions(null, false /* isDefaultDockState */, -1, -1,
- false /* animateAlpha */, false /* animateBounds */);
-
- // We translated the view but we need to animate it back from the current layout-space
- // rect to its final layout-space rect
- Utilities.setViewFrameFromTranslation(event.taskView);
-
- final ActivityOptions options = ActivityOptionsCompat.makeSplitScreenOptions(
- dockState.createMode == SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT);
- if (ActivityManagerWrapper.getInstance().startActivityFromRecents(event.task.key.id,
- options)) {
- final Runnable animStartedListener = () -> {
- EventBus.getDefault().send(new DockedFirstAnimationFrameEvent());
- // Remove the task and don't bother relaying out, as all the tasks
- // will be relaid out when the stack changes on the multiwindow
- // change event
- getStack().removeTask(event.task, null, true /* fromDockGesture */);
- };
- final Rect taskRect = getTaskRect(event.taskView);
- AppTransitionAnimationSpecsFuture future = new AppTransitionAnimationSpecsFuture(
- getHandler()) {
- @Override
- public List<AppTransitionAnimationSpecCompat> composeSpecs() {
- return mTransitionHelper.composeDockAnimationSpec(event.taskView, taskRect);
- }
- };
- WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
- future, animStartedListener, getHandler(), true /* scaleUp */,
- getContext().getDisplayId());
- MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP,
- event.task.getTopComponent().flattenToShortString());
- } else {
- EventBus.getDefault().send(new DragEndCancelledEvent(getStack(), event.task,
- event.taskView));
- }
- } else {
- // Animate the overlay alpha back to 0
- updateVisibleDockRegions(null, true /* isDefaultDockState */, -1, -1,
- true /* animateAlpha */, false /* animateBounds */);
- }
-
- // Show the stack action button again without changing visibility
- if (mStackActionButton != null) {
- mStackActionButton.animate()
- .alpha(1f)
- .setDuration(SHOW_STACK_ACTION_BUTTON_DURATION)
- .setInterpolator(Interpolators.ALPHA_IN)
- .start();
- }
- }
-
- public final void onBusEvent(final DragEndCancelledEvent event) {
- // Animate the overlay alpha back to 0
- updateVisibleDockRegions(null, true /* isDefaultDockState */, -1, -1,
- true /* animateAlpha */, false /* animateBounds */);
- }
-
- private Rect getTaskRect(TaskView taskView) {
- int[] location = taskView.getLocationOnScreen();
- int viewX = location[0];
- int viewY = location[1];
- return new Rect(viewX, viewY,
- (int) (viewX + taskView.getWidth() * taskView.getScaleX()),
- (int) (viewY + taskView.getHeight() * taskView.getScaleY()));
- }
-
- public final void onBusEvent(DraggingInRecentsEvent event) {
- if (mTaskStackView.getTaskViews().size() > 0) {
- setTranslationY(event.distanceFromTop - mTaskStackView.getTaskViews().get(0).getY());
- }
- }
-
- public final void onBusEvent(DraggingInRecentsEndedEvent event) {
- ViewPropertyAnimator animator = animate();
- if (event.velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
- animator.translationY(getHeight());
- animator.withEndAction(new Runnable() {
- @Override
- public void run() {
- WindowManagerProxy.getInstance().maximizeDockedStack();
- }
- });
- mFlingAnimationUtils.apply(animator, getTranslationY(), getHeight(), event.velocity);
- } else {
- animator.translationY(0f);
- animator.setListener(null);
- mFlingAnimationUtils.apply(animator, getTranslationY(), 0, event.velocity);
- }
- animator.start();
- }
-
- public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
- RecentsActivityLaunchState launchState = LegacyRecentsImpl.getConfiguration().getLaunchState();
- if (!launchState.launchedViaDockGesture && !launchState.launchedFromApp
- && getStack().getTaskCount() > 0) {
- animateBackgroundScrim(getOpaqueScrimAlpha(),
- TaskStackAnimationHelper.ENTER_FROM_HOME_TRANSLATION_DURATION);
- }
- }
-
- public final void onBusEvent(AllTaskViewsDismissedEvent event) {
- EventBus.getDefault().send(new HideStackActionButtonEvent());
- }
-
- public final void onBusEvent(DismissAllTaskViewsEvent event) {
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- if (!ssp.hasDockedTask()) {
- // Animate the background away only if we are dismissing Recents to home
- animateBackgroundScrim(0f, DEFAULT_UPDATE_SCRIM_DURATION);
- }
- }
-
- public final void onBusEvent(ShowStackActionButtonEvent event) {
- showStackActionButton(SHOW_STACK_ACTION_BUTTON_DURATION, event.translate);
- }
-
- public final void onBusEvent(HideStackActionButtonEvent event) {
- hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, true /* translate */);
- }
-
- public final void onBusEvent(MultiWindowStateChangedEvent event) {
- updateStack(event.stack, false /* setStackViewTasks */);
- }
-
- public final void onBusEvent(ShowEmptyViewEvent event) {
- showEmptyView(R.string.recents_empty_message);
- }
-
- /**
- * Shows the stack action button.
- */
- private void showStackActionButton(final int duration, final boolean translate) {
- final ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
- if (mStackActionButton.getVisibility() == View.INVISIBLE) {
- mStackActionButton.setVisibility(View.VISIBLE);
- mStackActionButton.setAlpha(0f);
- if (translate) {
- mStackActionButton.setTranslationY(mStackActionButton.getMeasuredHeight() *
- (LegacyRecentsImpl.getConfiguration().isLowRamDevice ? 1 : -0.25f));
- } else {
- mStackActionButton.setTranslationY(0f);
- }
- postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
- @Override
- public void run() {
- if (translate) {
- mStackActionButton.animate()
- .translationY(0f);
- }
- mStackActionButton.animate()
- .alpha(1f)
- .setDuration(duration)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .start();
- }
- });
- }
- postAnimationTrigger.flushLastDecrementRunnables();
- }
-
- /**
- * Hides the stack action button.
- */
- private void hideStackActionButton(int duration, boolean translate) {
- final ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger();
- hideStackActionButton(duration, translate, postAnimationTrigger);
- postAnimationTrigger.flushLastDecrementRunnables();
- }
-
- /**
- * Hides the stack action button.
- */
- private void hideStackActionButton(int duration, boolean translate,
- final ReferenceCountedTrigger postAnimationTrigger) {
- if (mStackActionButton.getVisibility() == View.VISIBLE) {
- if (translate) {
- mStackActionButton.animate().translationY(mStackActionButton.getMeasuredHeight()
- * (LegacyRecentsImpl.getConfiguration().isLowRamDevice ? 1 : -0.25f));
- }
- mStackActionButton.animate()
- .alpha(0f)
- .setDuration(duration)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- mStackActionButton.setVisibility(View.INVISIBLE);
- postAnimationTrigger.decrement();
- }
- })
- .start();
- postAnimationTrigger.increment();
- }
- }
-
- /**
- * Animates a translation in the Y direction and fades in/out for empty view to show or hide it.
- * @param show whether to translate up and fade in the empty view to the center of the screen
- * @param postAnimationTrigger to keep track of the animation
- */
- private void animateEmptyView(boolean show, ReferenceCountedTrigger postAnimationTrigger) {
- float start = mTaskStackView.getStackAlgorithm().getTaskRect().height() / 4;
- mEmptyView.setTranslationY(show ? start : 0);
- mEmptyView.setAlpha(show ? 0f : 1f);
- ViewPropertyAnimator animator = mEmptyView.animate()
- .setDuration(150)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .translationY(show ? 0 : start)
- .alpha(show ? 1f : 0f);
-
- if (postAnimationTrigger != null) {
- animator.setListener(postAnimationTrigger.decrementOnAnimationEnd());
- postAnimationTrigger.increment();
- }
- animator.start();
- }
-
- /**
- * Updates the dock region to match the specified dock state.
- */
- private void updateVisibleDockRegions(DockState[] newDockStates,
- boolean isDefaultDockState, int overrideAreaAlpha, int overrideHintAlpha,
- boolean animateAlpha, boolean animateBounds) {
- ArraySet<DockState> newDockStatesSet = Utilities.arrayToSet(newDockStates,
- new ArraySet<DockState>());
- ArrayList<DockState> visDockStates = mTouchHandler.getVisibleDockStates();
- for (int i = visDockStates.size() - 1; i >= 0; i--) {
- DockState dockState = visDockStates.get(i);
- DockState.ViewState viewState = dockState.viewState;
- if (newDockStates == null || !newDockStatesSet.contains(dockState)) {
- // This is no longer visible, so hide it
- viewState.startAnimation(null, 0, 0, TaskStackView.SLOW_SYNC_STACK_DURATION,
- Interpolators.FAST_OUT_SLOW_IN, animateAlpha, animateBounds);
- } else {
- // This state is now visible, update the bounds and show it
- int areaAlpha = overrideAreaAlpha != -1
- ? overrideAreaAlpha
- : viewState.dockAreaAlpha;
- int hintAlpha = overrideHintAlpha != -1
- ? overrideHintAlpha
- : viewState.hintTextAlpha;
- Rect bounds = isDefaultDockState
- ? dockState.getPreDockedBounds(getMeasuredWidth(), getMeasuredHeight(),
- mSystemInsets)
- : dockState.getDockedBounds(getMeasuredWidth(), getMeasuredHeight(),
- mDividerSize, mSystemInsets, getResources());
- if (viewState.dockAreaOverlay.getCallback() != this) {
- viewState.dockAreaOverlay.setCallback(this);
- viewState.dockAreaOverlay.setBounds(bounds);
- }
- viewState.startAnimation(bounds, areaAlpha, hintAlpha,
- TaskStackView.SLOW_SYNC_STACK_DURATION, Interpolators.FAST_OUT_SLOW_IN,
- animateAlpha, animateBounds);
- }
- }
- }
-
- /**
- * Scrim alpha based on how busy recents is:
- * Scrim will be {@link ScrimController#GRADIENT_SCRIM_ALPHA} when the stack is empty,
- * and {@link ScrimController#GRADIENT_SCRIM_ALPHA_BUSY} when it's full.
- *
- * @return Alpha from 0 to 1.
- */
- private float getOpaqueScrimAlpha() {
- return MathUtils.map(0, 1, ScrimController.GRADIENT_SCRIM_ALPHA,
- ScrimController.GRADIENT_SCRIM_ALPHA_BUSY, mBusynessFactor);
- }
-
- /**
- * Animates the background scrim to the given {@param alpha}.
- */
- private void animateBackgroundScrim(float alpha, int duration) {
- Utilities.cancelAnimationWithoutCallbacks(mBackgroundScrimAnimator);
- // Calculate the absolute alpha to animate from
- final int fromAlpha = mBackgroundScrim.getAlpha();
- final int toAlpha = (int) (alpha * 255);
- mBackgroundScrimAnimator = ValueAnimator.ofInt(fromAlpha, toAlpha);
- mBackgroundScrimAnimator.setDuration(duration);
- mBackgroundScrimAnimator.setInterpolator(toAlpha > fromAlpha
- ? Interpolators.ALPHA_IN
- : Interpolators.ALPHA_OUT);
- mBackgroundScrimAnimator.addUpdateListener(mUpdateBackgroundScrimAlpha);
- mBackgroundScrimAnimator.start();
- }
-
- /**
- * @return the bounds of the stack action button.
- */
- Rect getStackActionButtonBoundsFromStackLayout() {
- Rect actionButtonRect = new Rect(
- mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect());
- int left, top;
- if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- Rect windowRect = LegacyRecentsImpl.getSystemServices().getWindowRect();
- int spaceLeft = windowRect.width() - mSystemInsets.left - mSystemInsets.right;
- left = (spaceLeft - mStackActionButton.getMeasuredWidth()) / 2 + mSystemInsets.left;
- top = windowRect.height() - (mStackActionButton.getMeasuredHeight()
- + mSystemInsets.bottom + mStackActionButton.getPaddingBottom() / 2);
- } else {
- left = isLayoutRtl()
- ? actionButtonRect.left - mStackActionButton.getPaddingLeft()
- : actionButtonRect.right + mStackActionButton.getPaddingRight()
- - mStackActionButton.getMeasuredWidth();
- top = actionButtonRect.top +
- (actionButtonRect.height() - mStackActionButton.getMeasuredHeight()) / 2;
- }
- actionButtonRect.set(left, top, left + mStackActionButton.getMeasuredWidth(),
- top + mStackActionButton.getMeasuredHeight());
- return actionButtonRect;
- }
-
- View getStackActionButton() {
- return mStackActionButton;
- }
-
- /**
- * Launches the specified {@link Task}.
- */
- public void launchTaskFromRecents(final TaskStack stack, @Nullable final Task task,
- final TaskStackView stackView, final TaskView taskView,
- final boolean screenPinningRequested, final int windowingMode, final int activityType) {
-
- final Runnable animStartedListener;
- final AppTransitionAnimationSpecsFuture transitionFuture;
- if (taskView != null) {
-
- // Fetch window rect here already in order not to be blocked on lock contention in WM
- // when the future calls it.
- final Rect windowRect = LegacyRecentsImpl.getSystemServices().getWindowRect();
- transitionFuture = new AppTransitionAnimationSpecsFuture(stackView.getHandler()) {
- @Override
- public List<AppTransitionAnimationSpecCompat> composeSpecs() {
- return mTransitionHelper.composeAnimationSpecs(task, stackView, windowingMode,
- activityType, windowRect);
- }
- };
- animStartedListener = new Runnable() {
- private boolean mHandled;
-
- @Override
- public void run() {
- if (mHandled) {
- return;
- }
- mHandled = true;
-
- // If we are launching into another task, cancel the previous task's
- // window transition
- EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
- EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
- stackView.cancelAllTaskViewAnimations();
-
- if (screenPinningRequested) {
- // Request screen pinning after the animation runs
- mHandler.postDelayed(() -> {
- EventBus.getDefault().send(new ScreenPinningRequestEvent(mContext,
- task.key.id));
- }, 350);
- }
-
- if (!LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- // Reset the state where we are waiting for the transition to start
- EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false));
- }
- }
- };
- } else {
- // This is only the case if the task is not on screen (scrolled offscreen for example)
- transitionFuture = null;
- animStartedListener = new Runnable() {
- private boolean mHandled;
-
- @Override
- public void run() {
- if (mHandled) {
- return;
- }
- mHandled = true;
-
- // If we are launching into another task, cancel the previous task's
- // window transition
- EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
- EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
- stackView.cancelAllTaskViewAnimations();
-
- if (!LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- // Reset the state where we are waiting for the transition to start
- EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false));
- }
- }
- };
- }
-
- EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(true));
- final ActivityOptions opts = RecentsTransition.createAspectScaleAnimation(mContext,
- mHandler, true /* scaleUp */, transitionFuture != null ? transitionFuture : null,
- animStartedListener);
- if (taskView == null) {
- // If there is no task view, then we do not need to worry about animating out occluding
- // task views, and we can launch immediately
- startTaskActivity(stack, task, taskView, opts, transitionFuture,
- windowingMode, activityType);
- } else {
- LaunchTaskStartedEvent launchStartedEvent = new LaunchTaskStartedEvent(taskView,
- screenPinningRequested);
- EventBus.getDefault().send(launchStartedEvent);
- startTaskActivity(stack, task, taskView, opts, transitionFuture, windowingMode,
- activityType);
- }
- ActivityManagerWrapper.getInstance().closeSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
- }
-
- /**
- * Starts the activity for the launch task.
- *
- * @param taskView this is the {@link TaskView} that we are launching from. This can be null if
- * we are toggling recents and the launch-to task is now offscreen.
- */
- private void startTaskActivity(TaskStack stack, Task task, @Nullable TaskView taskView,
- ActivityOptions opts, AppTransitionAnimationSpecsFuture transitionFuture,
- int windowingMode, int activityType) {
- ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(task.key, opts,
- windowingMode, activityType, succeeded -> {
- if (succeeded) {
- // Keep track of the index of the task launch
- int taskIndexFromFront = 0;
- int taskIndex = stack.indexOfTask(task);
- if (taskIndex > -1) {
- taskIndexFromFront = stack.getTaskCount() - taskIndex - 1;
- }
- EventBus.getDefault().send(new LaunchTaskSucceededEvent(taskIndexFromFront));
- } else {
- Log.e(TAG, mContext.getString(R.string.recents_launch_error_message, task.title));
-
- // Dismiss the task if we fail to launch it
- if (taskView != null) {
- taskView.dismissTask();
- }
-
- // Keep track of failed launches
- EventBus.getDefault().send(new LaunchTaskFailedEvent());
- }
- }, getHandler());
- if (transitionFuture != null) {
- mHandler.post(transitionFuture::composeSpecsSynchronous);
- }
- }
-
- @Override
- public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
- super.requestDisallowInterceptTouchEvent(disallowIntercept);
- mTouchHandler.cancelStackActionButtonClick();
- }
-
- public void dump(String prefix, PrintWriter writer) {
- String innerPrefix = prefix + " ";
- String id = Integer.toHexString(System.identityHashCode(this));
-
- writer.print(prefix); writer.print(TAG);
- writer.print(" awaitingFirstLayout="); writer.print(mAwaitingFirstLayout ? "Y" : "N");
- writer.print(" insets="); writer.print(Utilities.dumpRect(mSystemInsets));
- writer.print(" [0x"); writer.print(id); writer.print("]");
- writer.println();
-
- if (getStack() != null) {
- getStack().dump(innerPrefix, writer);
- }
- if (mTaskStackView != null) {
- mTaskStackView.dump(innerPrefix, writer);
- }
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
deleted file mode 100644
index 1a827d5941c0..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ /dev/null
@@ -1,286 +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 com.android.systemui.recents.views;
-
-import android.app.ActivityTaskManager;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.PointerIcon;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewDebug;
-
-import com.android.internal.policy.DividerSnapAlgorithm;
-import com.android.systemui.recents.LegacyRecentsImpl;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
-import com.android.systemui.recents.events.activity.HideRecentsEvent;
-import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
-import com.android.systemui.recents.events.ui.HideIncompatibleAppOverlayEvent;
-import com.android.systemui.recents.events.ui.ShowIncompatibleAppOverlayEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragStartInitializeDropTargetsEvent;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.recents.model.Task;
-
-import java.util.ArrayList;
-
-/**
- * Handles touch events for a RecentsView.
- */
-public class RecentsViewTouchHandler {
-
- private RecentsView mRv;
-
- @ViewDebug.ExportedProperty(deepExport=true, prefix="drag_task")
- private Task mDragTask;
- @ViewDebug.ExportedProperty(deepExport=true, prefix="drag_task_view_")
- private TaskView mTaskView;
-
- @ViewDebug.ExportedProperty(category="recents")
- private Point mTaskViewOffset = new Point();
- @ViewDebug.ExportedProperty(category="recents")
- private Point mDownPos = new Point();
- @ViewDebug.ExportedProperty(category="recents")
- private boolean mDragRequested;
- @ViewDebug.ExportedProperty(category="recents")
- private boolean mIsDragging;
- private float mDragSlop;
- private int mDeviceId = -1;
-
- private DropTarget mLastDropTarget;
- private DividerSnapAlgorithm mDividerSnapAlgorithm;
- private ArrayList<DropTarget> mDropTargets = new ArrayList<>();
- private ArrayList<DockState> mVisibleDockStates = new ArrayList<>();
-
- public RecentsViewTouchHandler(RecentsView rv) {
- mRv = rv;
- mDragSlop = ViewConfiguration.get(rv.getContext()).getScaledTouchSlop();
- updateSnapAlgorithm();
- }
-
- private void updateSnapAlgorithm() {
- Rect insets = new Rect();
- SystemServicesProxy.getInstance(mRv.getContext()).getStableInsets(insets);
- mDividerSnapAlgorithm = DividerSnapAlgorithm.create(mRv.getContext(), insets);
- }
-
- /**
- * Registers a new drop target for the current drag only.
- */
- public void registerDropTargetForCurrentDrag(DropTarget target) {
- mDropTargets.add(target);
- }
-
- /**
- * Returns the set of visible dock states for this current drag.
- */
- public ArrayList<DockState> getVisibleDockStates() {
- return mVisibleDockStates;
- }
-
- /** Touch preprocessing for handling below */
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- return handleTouchEvent(ev) || mDragRequested;
- }
-
- /** Handles touch events once we have intercepted them */
- public boolean onTouchEvent(MotionEvent ev) {
- handleTouchEvent(ev);
- if (ev.getAction() == MotionEvent.ACTION_UP && mRv.getStack().getTaskCount() == 0) {
- EventBus.getDefault().send(new HideRecentsEvent(false, true));
- }
- return true;
- }
-
- /**** Events ****/
-
- public final void onBusEvent(DragStartEvent event) {
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- mRv.getParent().requestDisallowInterceptTouchEvent(true);
- mDragRequested = true;
- // We defer starting the actual drag handling until the user moves past the drag slop
- mIsDragging = false;
- mDragTask = event.task;
- mTaskView = event.taskView;
- mDropTargets.clear();
-
- int[] recentsViewLocation = new int[2];
- mRv.getLocationInWindow(recentsViewLocation);
- mTaskViewOffset.set(mTaskView.getLeft() - recentsViewLocation[0] + event.tlOffset.x,
- mTaskView.getTop() - recentsViewLocation[1] + event.tlOffset.y);
-
- // Change space coordinates relative to the view to RecentsView when user initiates a touch
- if (event.isUserTouchInitiated) {
- float x = mDownPos.x - mTaskViewOffset.x;
- float y = mDownPos.y - mTaskViewOffset.y;
- mTaskView.setTranslationX(x);
- mTaskView.setTranslationY(y);
- }
-
- mVisibleDockStates.clear();
- if (ActivityTaskManager.supportsMultiWindow(mRv.getContext()) && !ssp.hasDockedTask()
- && mDividerSnapAlgorithm.isSplitScreenFeasible()) {
- LegacyRecentsImpl.logDockAttempt(mRv.getContext(), event.task.getTopComponent(),
- event.task.resizeMode);
- if (!event.task.isDockable) {
- EventBus.getDefault().send(new ShowIncompatibleAppOverlayEvent());
- } else {
- // Add the dock state drop targets (these take priority)
- DockState[] dockStates = LegacyRecentsImpl.getConfiguration()
- .getDockStatesForCurrentOrientation();
- for (DockState dockState : dockStates) {
- registerDropTargetForCurrentDrag(dockState);
- dockState.update(mRv.getContext());
- mVisibleDockStates.add(dockState);
- }
- }
- }
-
- // Request other drop targets to register themselves
- EventBus.getDefault().send(new DragStartInitializeDropTargetsEvent(event.task,
- event.taskView, this));
- if (mDeviceId != -1) {
- InputDevice device = InputDevice.getDevice(mDeviceId);
- if (device != null) {
- device.setPointerType(PointerIcon.TYPE_GRABBING);
- }
- }
- }
-
- public final void onBusEvent(DragEndEvent event) {
- if (!mDragTask.isDockable) {
- EventBus.getDefault().send(new HideIncompatibleAppOverlayEvent());
- }
- mDragRequested = false;
- mDragTask = null;
- mTaskView = null;
- mLastDropTarget = null;
- }
-
- public final void onBusEvent(ConfigurationChangedEvent event) {
- if (event.fromDisplayDensityChange || event.fromDeviceOrientationChange) {
- updateSnapAlgorithm();
- }
- }
-
- void cancelStackActionButtonClick() {
- mRv.getStackActionButton().setPressed(false);
- }
-
- private boolean isWithinStackActionButton(float x, float y) {
- Rect rect = mRv.getStackActionButtonBoundsFromStackLayout();
- return mRv.getStackActionButton().getVisibility() == View.VISIBLE &&
- mRv.getStackActionButton().pointInView(x - rect.left, y - rect.top, 0 /* slop */);
- }
-
- private void changeStackActionButtonDrawableHotspot(float x, float y) {
- Rect rect = mRv.getStackActionButtonBoundsFromStackLayout();
- mRv.getStackActionButton().drawableHotspotChanged(x - rect.left, y - rect.top);
- }
-
- /**
- * Handles dragging touch events
- */
- private boolean handleTouchEvent(MotionEvent ev) {
- int action = ev.getActionMasked();
- boolean consumed = false;
- float evX = ev.getX();
- float evY = ev.getY();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- mDownPos.set((int) evX, (int) evY);
- mDeviceId = ev.getDeviceId();
-
- if (isWithinStackActionButton(evX, evY)) {
- changeStackActionButtonDrawableHotspot(evX, evY);
- mRv.getStackActionButton().setPressed(true);
- }
- break;
- case MotionEvent.ACTION_MOVE: {
- float x = evX - mTaskViewOffset.x;
- float y = evY - mTaskViewOffset.y;
-
- if (mRv.getStackActionButton().isPressed() && isWithinStackActionButton(evX, evY)) {
- changeStackActionButtonDrawableHotspot(evX, evY);
- }
-
- if (mDragRequested) {
- if (!mIsDragging) {
- mIsDragging = Math.hypot(evX - mDownPos.x, evY - mDownPos.y) > mDragSlop;
- }
- if (mIsDragging) {
- int width = mRv.getMeasuredWidth();
- int height = mRv.getMeasuredHeight();
-
- DropTarget currentDropTarget = null;
-
- // Give priority to the current drop target to retain the touch handling
- if (mLastDropTarget != null) {
- if (mLastDropTarget.acceptsDrop((int) evX, (int) evY, width, height,
- mRv.mSystemInsets, true /* isCurrentTarget */)) {
- currentDropTarget = mLastDropTarget;
- }
- }
-
- // Otherwise, find the next target to handle this event
- if (currentDropTarget == null) {
- for (DropTarget target : mDropTargets) {
- if (target.acceptsDrop((int) evX, (int) evY, width, height,
- mRv.mSystemInsets, false /* isCurrentTarget */)) {
- currentDropTarget = target;
- break;
- }
- }
- }
- if (mLastDropTarget != currentDropTarget) {
- mLastDropTarget = currentDropTarget;
- EventBus.getDefault().send(new DragDropTargetChangedEvent(mDragTask,
- currentDropTarget));
- }
- }
- mTaskView.setTranslationX(x);
- mTaskView.setTranslationY(y);
- }
- break;
- }
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL: {
- if (mRv.getStackActionButton().isPressed() && isWithinStackActionButton(evX, evY)) {
- EventBus.getDefault().send(new DismissAllTaskViewsEvent());
- consumed = true;
- }
- cancelStackActionButtonClick();
- if (mDragRequested) {
- boolean cancelled = action == MotionEvent.ACTION_CANCEL;
- if (cancelled) {
- EventBus.getDefault().send(new DragDropTargetChangedEvent(mDragTask, null));
- }
- EventBus.getDefault().send(new DragEndEvent(mDragTask, mTaskView,
- !cancelled ? mLastDropTarget : null));
- break;
- }
- mDeviceId = -1;
- }
- }
- return consumed;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/SystemBarScrimViews.java
deleted file mode 100644
index 22c12b408a13..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/SystemBarScrimViews.java
+++ /dev/null
@@ -1,194 +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 com.android.systemui.recents.views;
-
-import android.content.Context;
-import android.view.View;
-
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.recents.LegacyRecentsImpl;
-import com.android.systemui.recents.RecentsActivity;
-import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
-import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
-import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
-import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
-import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
-import com.android.systemui.recents.utilities.AnimationProps;
-
-/** Manages the scrims for the various system bars. */
-public class SystemBarScrimViews {
-
- private static final int DEFAULT_ANIMATION_DURATION = 150;
-
- private Context mContext;
-
- private View mNavBarScrimView;
-
- private boolean mHasNavBarScrim;
- private boolean mShouldAnimateNavBarScrim;
- private boolean mHasTransposedNavBar;
- private boolean mHasDockedTasks;
- private int mNavBarScrimEnterDuration;
-
- public SystemBarScrimViews(RecentsActivity activity) {
- mContext = activity;
- mNavBarScrimView = activity.findViewById(R.id.nav_bar_scrim);
- mNavBarScrimView.forceHasOverlappingRendering(false);
- mNavBarScrimEnterDuration = activity.getResources().getInteger(
- R.integer.recents_nav_bar_scrim_enter_duration);
- mHasNavBarScrim = LegacyRecentsImpl.getSystemServices().hasTransposedNavigationBar();
- mHasDockedTasks = LegacyRecentsImpl.getSystemServices().hasDockedTask();
- }
-
- /**
- * Updates the nav bar scrim.
- */
- public void updateNavBarScrim(boolean animateNavBarScrim, boolean hasStackTasks,
- AnimationProps animation) {
- prepareEnterRecentsAnimation(isNavBarScrimRequired(hasStackTasks), animateNavBarScrim);
- if (animateNavBarScrim && animation != null) {
- animateNavBarScrimVisibility(true, animation);
- }
- }
-
- /**
- * Prepares the scrim views for animating when entering Recents. This will be called before
- * the first draw, unless we are updating the scrim on configuration change.
- */
- private void prepareEnterRecentsAnimation(boolean hasNavBarScrim, boolean animateNavBarScrim) {
- mHasNavBarScrim = hasNavBarScrim;
- mShouldAnimateNavBarScrim = animateNavBarScrim;
-
- mNavBarScrimView.setVisibility(mHasNavBarScrim && !mShouldAnimateNavBarScrim ?
- View.VISIBLE : View.INVISIBLE);
- }
-
- /**
- * Animates the nav bar scrim visibility.
- */
- private void animateNavBarScrimVisibility(boolean visible, AnimationProps animation) {
- int toY = 0;
- if (visible) {
- mNavBarScrimView.setVisibility(View.VISIBLE);
- mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
- } else {
- toY = mNavBarScrimView.getMeasuredHeight();
- }
- if (animation != AnimationProps.IMMEDIATE) {
- mNavBarScrimView.animate()
- .translationY(toY)
- .setDuration(animation.getDuration(AnimationProps.BOUNDS))
- .setInterpolator(animation.getInterpolator(AnimationProps.BOUNDS))
- .start();
- } else {
- mNavBarScrimView.setTranslationY(toY);
- }
- }
-
- /**
- * @return Whether to show the nav bar scrim.
- */
- private boolean isNavBarScrimRequired(boolean hasStackTasks) {
- return hasStackTasks && !mHasTransposedNavBar && !mHasDockedTasks;
- }
-
- /**** EventBus events ****/
-
- /**
- * Starts animating the scrim views when entering Recents.
- */
- public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
- if (mHasNavBarScrim) {
- AnimationProps animation = mShouldAnimateNavBarScrim
- ? new AnimationProps()
- .setDuration(AnimationProps.BOUNDS, mNavBarScrimEnterDuration)
- .setInterpolator(AnimationProps.BOUNDS, Interpolators.DECELERATE_QUINT)
- : AnimationProps.IMMEDIATE;
- animateNavBarScrimVisibility(true, animation);
- }
- }
-
- /**
- * Starts animating the scrim views when leaving Recents (either via launching a task, or
- * going home).
- */
- public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
- if (mHasNavBarScrim) {
- AnimationProps animation = createBoundsAnimation(
- TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION);
- animateNavBarScrimVisibility(false, animation);
- }
- }
-
- public final void onBusEvent(DismissAllTaskViewsEvent event) {
- if (mHasNavBarScrim) {
- AnimationProps animation = createBoundsAnimation(
- TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION);
- animateNavBarScrimVisibility(false, animation);
- }
- }
-
- public final void onBusEvent(ConfigurationChangedEvent event) {
- if (event.fromDeviceOrientationChange) {
- mHasNavBarScrim = LegacyRecentsImpl.getSystemServices().hasTransposedNavigationBar();
- }
- animateScrimToCurrentNavBarState(event.hasStackTasks);
- }
-
- public final void onBusEvent(MultiWindowStateChangedEvent event) {
- mHasDockedTasks = event.inMultiWindow;
- animateScrimToCurrentNavBarState(event.stack.getTaskCount() > 0);
- }
-
- public final void onBusEvent(final DragEndEvent event) {
- // Hide the nav bar scrims once we drop to a dock region
- if (event.dropTarget instanceof DockState) {
- animateScrimToCurrentNavBarState(false /* hasStackTasks */);
- }
- }
-
- public final void onBusEvent(final DragEndCancelledEvent event) {
- // Restore the scrims to the normal state
- animateScrimToCurrentNavBarState(event.stack.getTaskCount() > 0);
- }
-
- /**
- * Animates the scrim to match the state of the current nav bar.
- */
- private void animateScrimToCurrentNavBarState(boolean hasStackTasks) {
- boolean hasNavBarScrim = isNavBarScrimRequired(hasStackTasks);
- if (mHasNavBarScrim != hasNavBarScrim) {
- AnimationProps animation = hasNavBarScrim
- ? createBoundsAnimation(DEFAULT_ANIMATION_DURATION)
- : AnimationProps.IMMEDIATE;
- animateNavBarScrimVisibility(hasNavBarScrim, animation);
- }
- mHasNavBarScrim = hasNavBarScrim;
- }
-
- /**
- * @return a default animation to aniamte the bounds of the scrim.
- */
- private AnimationProps createBoundsAnimation(int duration) {
- return new AnimationProps()
- .setDuration(AnimationProps.BOUNDS, duration)
- .setInterpolator(AnimationProps.BOUNDS, Interpolators.FAST_OUT_SLOW_IN);
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
deleted file mode 100644
index 55749348b004..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
+++ /dev/null
@@ -1,705 +0,0 @@
-/*
- * 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 com.android.systemui.recents.views;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.util.Log;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
-
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.recents.LegacyRecentsImpl;
-import com.android.systemui.recents.RecentsActivityLaunchState;
-import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.RecentsDebugFlags;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
-import com.android.systemui.recents.misc.ReferenceCountedTrigger;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.views.lowram.TaskStackLowRamLayoutAlgorithm;
-import com.android.systemui.recents.utilities.AnimationProps;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A helper class to create task view animations for {@link TaskView}s in a {@link TaskStackView},
- * but not the contents of the {@link TaskView}s.
- */
-public class TaskStackAnimationHelper {
-
- /**
- * Callbacks from the helper to coordinate view-content animations with view animations.
- */
- public interface Callbacks {
- /**
- * Callback to prepare for the start animation for the launch target {@link TaskView}.
- */
- void onPrepareLaunchTargetForEnterAnimation();
-
- /**
- * Callback to start the animation for the launch target {@link TaskView}.
- */
- void onStartLaunchTargetEnterAnimation(TaskViewTransform transform, int duration,
- boolean screenPinningEnabled, ReferenceCountedTrigger postAnimationTrigger);
-
- /**
- * Callback to start the animation for the launch target {@link TaskView} when it is
- * launched from Recents.
- */
- void onStartLaunchTargetLaunchAnimation(int duration, boolean screenPinningRequested,
- ReferenceCountedTrigger postAnimationTrigger);
-
- /**
- * Callback to start the animation for the front {@link TaskView} if there is no launch
- * target.
- */
- void onStartFrontTaskEnterAnimation(boolean screenPinningEnabled);
- }
-
- private static final int DOUBLE_FRAME_OFFSET_MS = 33;
- private static final int FRAME_OFFSET_MS = 16;
-
- private static final int ENTER_EXIT_NUM_ANIMATING_TASKS = 5;
-
- private static final int ENTER_FROM_HOME_ALPHA_DURATION = 100;
- public static final int ENTER_FROM_HOME_TRANSLATION_DURATION = 300;
- private static final Interpolator ENTER_FROM_HOME_ALPHA_INTERPOLATOR = Interpolators.LINEAR;
-
- public static final int EXIT_TO_HOME_TRANSLATION_DURATION = 200;
- private static final Interpolator EXIT_TO_HOME_TRANSLATION_INTERPOLATOR =
- new PathInterpolator(0.4f, 0, 0.6f, 1f);
-
- private static final int DISMISS_TASK_DURATION = 175;
- private static final int DISMISS_ALL_TASKS_DURATION = 200;
- private static final Interpolator DISMISS_ALL_TRANSLATION_INTERPOLATOR =
- new PathInterpolator(0.4f, 0, 1f, 1f);
-
- private static final Interpolator FOCUS_NEXT_TASK_INTERPOLATOR =
- new PathInterpolator(0.4f, 0, 0, 1f);
- private static final Interpolator FOCUS_IN_FRONT_NEXT_TASK_INTERPOLATOR =
- new PathInterpolator(0, 0, 0, 1f);
- private static final Interpolator FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR =
- Interpolators.LINEAR_OUT_SLOW_IN;
-
- private static final Interpolator ENTER_WHILE_DOCKING_INTERPOLATOR =
- Interpolators.LINEAR_OUT_SLOW_IN;
-
- private final int mEnterAndExitFromHomeTranslationOffset;
- private TaskStackView mStackView;
-
- private TaskViewTransform mTmpTransform = new TaskViewTransform();
- private ArrayList<TaskViewTransform> mTmpCurrentTaskTransforms = new ArrayList<>();
- private ArrayList<TaskViewTransform> mTmpFinalTaskTransforms = new ArrayList<>();
-
- public TaskStackAnimationHelper(Context context, TaskStackView stackView) {
- mStackView = stackView;
- mEnterAndExitFromHomeTranslationOffset = LegacyRecentsImpl.getConfiguration().isGridEnabled
- ? 0 : DOUBLE_FRAME_OFFSET_MS;
- }
-
- /**
- * Prepares the stack views and puts them in their initial animation state while visible, before
- * the in-app enter animations start (after the window-transition completes).
- */
- public void prepareForEnterAnimation() {
- RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- Resources res = mStackView.getResources();
- Resources appResources = mStackView.getContext().getApplicationContext().getResources();
-
- TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
- TaskStackViewScroller stackScroller = mStackView.getScroller();
- TaskStack stack = mStackView.getStack();
- Task launchTargetTask = stack.getLaunchTarget();
-
- // Break early if there are no tasks
- if (stack.getTaskCount() == 0) {
- return;
- }
-
- int offscreenYOffset = stackLayout.mStackRect.height();
- int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
- R.dimen.recents_task_stack_animation_affiliate_enter_offset);
- int launchedWhileDockingOffset = res.getDimensionPixelSize(
- R.dimen.recents_task_stack_animation_launched_while_docking_offset);
- boolean isLandscape = appResources.getConfiguration().orientation
- == Configuration.ORIENTATION_LANDSCAPE;
-
- float top = 0;
- final boolean isLowRamDevice = LegacyRecentsImpl.getConfiguration().isLowRamDevice;
- if (isLowRamDevice && launchState.launchedFromApp && !launchState.launchedViaDockGesture) {
- stackLayout.getStackTransform(launchTargetTask, stackScroller.getStackScroll(),
- mTmpTransform, null /* frontTransform */);
- top = mTmpTransform.rect.top;
- }
-
- // Prepare each of the task views for their enter animation from front to back
- List<TaskView> taskViews = mStackView.getTaskViews();
- for (int i = taskViews.size() - 1; i >= 0; i--) {
- TaskView tv = taskViews.get(i);
- Task task = tv.getTask();
-
- // Get the current transform for the task, which will be used to position it offscreen
- stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
- null);
-
- if (launchState.launchedFromApp && !launchState.launchedViaDockGesture) {
- if (task.isLaunchTarget) {
- tv.onPrepareLaunchTargetForEnterAnimation();
- } else if (isLowRamDevice && i >= taskViews.size() -
- (TaskStackLowRamLayoutAlgorithm.MAX_LAYOUT_TASK_COUNT + 1)
- && !RecentsDebugFlags.Static.DisableRecentsLowRamEnterExitAnimation) {
- // Move the last 2nd and 3rd last tasks in-app animation to match the motion of
- // the last task's app transition
- stackLayout.getStackTransform(task, stackScroller.getStackScroll(),
- mTmpTransform, null);
- mTmpTransform.rect.offset(0, -top);
- mTmpTransform.alpha = 0f;
- mStackView.updateTaskViewToTransform(tv, mTmpTransform,
- AnimationProps.IMMEDIATE);
- stackLayout.getStackTransform(task, stackScroller.getStackScroll(),
- mTmpTransform, null);
- mTmpTransform.alpha = 1f;
- // Duration see {@link
- // com.android.server.wm.AppTransition#DEFAULT_APP_TRANSITION_DURATION}
- mStackView.updateTaskViewToTransform(tv, mTmpTransform,
- new AnimationProps(336, Interpolators.FAST_OUT_SLOW_IN));
- }
- } else if (launchState.launchedFromHome) {
- if (isLowRamDevice) {
- mTmpTransform.rect.offset(0, stackLayout.getTaskRect().height() / 4);
- } else {
- // Move the task view off screen (below) so we can animate it in
- mTmpTransform.rect.offset(0, offscreenYOffset);
- }
- mTmpTransform.alpha = 0f;
- mStackView.updateTaskViewToTransform(tv, mTmpTransform, AnimationProps.IMMEDIATE);
- } else if (launchState.launchedViaDockGesture) {
- int offset = isLandscape
- ? launchedWhileDockingOffset
- : (int) (offscreenYOffset * 0.9f);
- mTmpTransform.rect.offset(0, offset);
- mTmpTransform.alpha = 0f;
- mStackView.updateTaskViewToTransform(tv, mTmpTransform, AnimationProps.IMMEDIATE);
- }
- }
- }
-
- /**
- * Starts the in-app enter animation, which animates the {@link TaskView}s to their final places
- * depending on how Recents was triggered.
- */
- public void startEnterAnimation(final ReferenceCountedTrigger postAnimationTrigger) {
- RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- Resources res = mStackView.getResources();
- Resources appRes = mStackView.getContext().getApplicationContext().getResources();
-
- TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
- TaskStackViewScroller stackScroller = mStackView.getScroller();
- TaskStack stack = mStackView.getStack();
- Task launchTargetTask = stack.getLaunchTarget();
-
- // Break early if there are no tasks
- if (stack.getTaskCount() == 0) {
- return;
- }
-
- final boolean isLowRamDevice = LegacyRecentsImpl.getConfiguration().isLowRamDevice;
- int taskViewEnterFromAppDuration = res.getInteger(
- R.integer.recents_task_enter_from_app_duration);
- int taskViewEnterFromAffiliatedAppDuration = res.getInteger(
- R.integer.recents_task_enter_from_affiliated_app_duration);
- int dockGestureAnimDuration = appRes.getInteger(
- R.integer.long_press_dock_anim_duration);
-
- // Since low ram devices have an animation when entering app -> recents, do not allow
- // toggle until the animation is complete
- if (launchState.launchedFromApp && !launchState.launchedViaDockGesture && isLowRamDevice) {
- postAnimationTrigger.addLastDecrementRunnable(() -> EventBus.getDefault()
- .send(new SetWaitingForTransitionStartEvent(false)));
- }
-
- // Create enter animations for each of the views from front to back
- List<TaskView> taskViews = mStackView.getTaskViews();
- int taskViewCount = taskViews.size();
- for (int i = taskViewCount - 1; i >= 0; i--) {
- int taskIndexFromFront = taskViewCount - i - 1;
- int taskIndexFromBack = i;
- final TaskView tv = taskViews.get(i);
- Task task = tv.getTask();
-
- // Get the current transform for the task, which will be updated to the final transform
- // to animate to depending on how recents was invoked
- stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
- null);
-
- if (launchState.launchedFromApp && !launchState.launchedViaDockGesture) {
- if (task.isLaunchTarget) {
- tv.onStartLaunchTargetEnterAnimation(mTmpTransform,
- taskViewEnterFromAppDuration, mStackView.mScreenPinningEnabled,
- postAnimationTrigger);
- }
-
- } else if (launchState.launchedFromHome) {
- // Animate the tasks up, but offset the animations to be relative to the front-most
- // task animation
- final float startOffsetFraction = (float) (Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS,
- taskIndexFromFront) * mEnterAndExitFromHomeTranslationOffset) /
- ENTER_FROM_HOME_TRANSLATION_DURATION;
- AnimationProps taskAnimation = new AnimationProps()
- .setInterpolator(AnimationProps.ALPHA, ENTER_FROM_HOME_ALPHA_INTERPOLATOR)
- .setListener(postAnimationTrigger.decrementOnAnimationEnd());
- if (isLowRamDevice) {
- taskAnimation.setInterpolator(AnimationProps.BOUNDS,
- Interpolators.FAST_OUT_SLOW_IN)
- .setDuration(AnimationProps.BOUNDS, 150)
- .setDuration(AnimationProps.ALPHA, 150);
- } else {
- taskAnimation.setStartDelay(AnimationProps.ALPHA,
- Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS, taskIndexFromFront) *
- FRAME_OFFSET_MS)
- .setInterpolator(AnimationProps.BOUNDS,
- new RecentsEntrancePathInterpolator(0f, 0f, 0.2f, 1f,
- startOffsetFraction))
- .setDuration(AnimationProps.BOUNDS, ENTER_FROM_HOME_TRANSLATION_DURATION)
- .setDuration(AnimationProps.ALPHA, ENTER_FROM_HOME_ALPHA_DURATION);
- }
- postAnimationTrigger.increment();
- mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
- if (i == taskViewCount - 1) {
- tv.onStartFrontTaskEnterAnimation(mStackView.mScreenPinningEnabled);
- }
- } else if (launchState.launchedViaDockGesture) {
- // Animate the tasks up - add some delay to match the divider animation
- AnimationProps taskAnimation = new AnimationProps()
- .setDuration(AnimationProps.BOUNDS, dockGestureAnimDuration +
- (taskIndexFromBack * DOUBLE_FRAME_OFFSET_MS))
- .setInterpolator(AnimationProps.BOUNDS,
- ENTER_WHILE_DOCKING_INTERPOLATOR)
- .setStartDelay(AnimationProps.BOUNDS, 48)
- .setListener(postAnimationTrigger.decrementOnAnimationEnd());
- postAnimationTrigger.increment();
- mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
- }
- }
- }
-
- /**
- * Starts an in-app animation to hide all the task views so that we can transition back home.
- */
- public void startExitToHomeAnimation(boolean animated,
- ReferenceCountedTrigger postAnimationTrigger) {
- TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
- TaskStack stack = mStackView.getStack();
-
- // Break early if there are no tasks
- if (stack.getTaskCount() == 0) {
- return;
- }
-
- int offscreenYOffset = stackLayout.mStackRect.height();
-
- // Create the animations for each of the tasks
- List<TaskView> taskViews = mStackView.getTaskViews();
- int taskViewCount = taskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
- int taskIndexFromFront = taskViewCount - i - 1;
- TaskView tv = taskViews.get(i);
- Task task = tv.getTask();
-
- if (mStackView.isIgnoredTask(task)) {
- continue;
- }
-
- // Animate the tasks down
- AnimationProps taskAnimation;
- if (animated) {
- int delay = Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS , taskIndexFromFront) *
- mEnterAndExitFromHomeTranslationOffset;
- taskAnimation = new AnimationProps()
- .setDuration(AnimationProps.BOUNDS, EXIT_TO_HOME_TRANSLATION_DURATION)
- .setListener(postAnimationTrigger.decrementOnAnimationEnd());
- if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- taskAnimation.setInterpolator(AnimationProps.BOUNDS,
- Interpolators.FAST_OUT_SLOW_IN);
- } else {
- taskAnimation.setStartDelay(AnimationProps.BOUNDS, delay)
- .setInterpolator(AnimationProps.BOUNDS,
- EXIT_TO_HOME_TRANSLATION_INTERPOLATOR);
- }
- postAnimationTrigger.increment();
- } else {
- taskAnimation = AnimationProps.IMMEDIATE;
- }
-
- mTmpTransform.fillIn(tv);
- if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- taskAnimation.setInterpolator(AnimationProps.ALPHA,
- EXIT_TO_HOME_TRANSLATION_INTERPOLATOR)
- .setDuration(AnimationProps.ALPHA, EXIT_TO_HOME_TRANSLATION_DURATION);
- mTmpTransform.rect.offset(0, stackLayout.mTaskStackLowRamLayoutAlgorithm
- .getTaskRect().height() / 4);
- mTmpTransform.alpha = 0f;
- } else {
- mTmpTransform.rect.offset(0, offscreenYOffset);
- }
- mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
- }
- }
-
- /**
- * Starts the animation for the launching task view, hiding any tasks that might occlude the
- * window transition for the launching task.
- */
- public void startLaunchTaskAnimation(TaskView launchingTaskView, boolean screenPinningRequested,
- final ReferenceCountedTrigger postAnimationTrigger) {
- Resources res = mStackView.getResources();
-
- int taskViewExitToAppDuration = res.getInteger(
- R.integer.recents_task_exit_to_app_duration);
- int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
- R.dimen.recents_task_stack_animation_affiliate_enter_offset);
-
- Task launchingTask = launchingTaskView.getTask();
- List<TaskView> taskViews = mStackView.getTaskViews();
- int taskViewCount = taskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
- TaskView tv = taskViews.get(i);
- Task task = tv.getTask();
-
- if (tv == launchingTaskView) {
- tv.setClipViewInStack(false);
- postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
- @Override
- public void run() {
- tv.setClipViewInStack(true);
- }
- });
- tv.onStartLaunchTargetLaunchAnimation(taskViewExitToAppDuration,
- screenPinningRequested, postAnimationTrigger);
- }
- }
- }
-
- /**
- * Starts the delete animation for the specified {@link TaskView}.
- */
- public void startDeleteTaskAnimation(final TaskView deleteTaskView, boolean gridLayout,
- final ReferenceCountedTrigger postAnimationTrigger) {
- if (gridLayout) {
- startTaskGridDeleteTaskAnimation(deleteTaskView, postAnimationTrigger);
- } else {
- startTaskStackDeleteTaskAnimation(deleteTaskView, postAnimationTrigger);
- }
- }
-
- /**
- * Starts the delete animation for all the {@link TaskView}s.
- */
- public void startDeleteAllTasksAnimation(final List<TaskView> taskViews, boolean gridLayout,
- final ReferenceCountedTrigger postAnimationTrigger) {
- if (gridLayout) {
- for (int i = 0; i < taskViews.size(); i++) {
- startTaskGridDeleteTaskAnimation(taskViews.get(i), postAnimationTrigger);
- }
- } else {
- startTaskStackDeleteAllTasksAnimation(taskViews, postAnimationTrigger);
- }
- }
-
- /**
- * Starts the animation to focus the next {@link TaskView} when paging through recents.
- *
- * @return whether or not this will trigger a scroll in the stack
- */
- public boolean startScrollToFocusedTaskAnimation(Task newFocusedTask,
- boolean requestViewFocus) {
- TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
- TaskStackViewScroller stackScroller = mStackView.getScroller();
- TaskStack stack = mStackView.getStack();
-
- final float curScroll = stackScroller.getStackScroll();
- final float newScroll = stackScroller.getBoundedStackScroll(
- stackLayout.getStackScrollForTask(newFocusedTask));
- boolean willScrollToFront = newScroll > curScroll;
- boolean willScroll = Float.compare(newScroll, curScroll) != 0;
-
- // Get the current set of task transforms
- int taskViewCount = mStackView.getTaskViews().size();
- ArrayList<Task> stackTasks = stack.getTasks();
- mStackView.getCurrentTaskTransforms(stackTasks, mTmpCurrentTaskTransforms);
-
- // Pick up the newly visible views after the scroll
- mStackView.bindVisibleTaskViews(newScroll);
-
- // Update the internal state
- stackLayout.setFocusState(TaskStackLayoutAlgorithm.STATE_FOCUSED);
- stackScroller.setStackScroll(newScroll, null /* animation */);
- mStackView.cancelDeferredTaskViewLayoutAnimation();
-
- // Get the final set of task transforms
- mStackView.getLayoutTaskTransforms(newScroll, stackLayout.getFocusState(), stackTasks,
- true /* ignoreTaskOverrides */, mTmpFinalTaskTransforms);
-
- // Focus the task view
- TaskView newFocusedTaskView = mStackView.getChildViewForTask(newFocusedTask);
- if (newFocusedTaskView == null) {
- // Log the error if we have no task view, and skip the animation
- Log.e("TaskStackAnimationHelper", "b/27389156 null-task-view prebind:" + taskViewCount +
- " postbind:" + mStackView.getTaskViews().size() + " prescroll:" + curScroll +
- " postscroll: " + newScroll);
- return false;
- }
- newFocusedTaskView.setFocusedState(true, requestViewFocus);
-
- // Setup the end listener to return all the hidden views to the view pool after the
- // focus animation
- ReferenceCountedTrigger postAnimTrigger = new ReferenceCountedTrigger();
- postAnimTrigger.addLastDecrementRunnable(new Runnable() {
- @Override
- public void run() {
- mStackView.bindVisibleTaskViews(newScroll);
- }
- });
-
- List<TaskView> taskViews = mStackView.getTaskViews();
- taskViewCount = taskViews.size();
- int newFocusTaskViewIndex = taskViews.indexOf(newFocusedTaskView);
- for (int i = 0; i < taskViewCount; i++) {
- TaskView tv = taskViews.get(i);
- Task task = tv.getTask();
-
- if (mStackView.isIgnoredTask(task)) {
- continue;
- }
-
- int taskIndex = stackTasks.indexOf(task);
- TaskViewTransform fromTransform = mTmpCurrentTaskTransforms.get(taskIndex);
- TaskViewTransform toTransform = mTmpFinalTaskTransforms.get(taskIndex);
-
- // Update the task to the initial state (for the newly picked up tasks)
- mStackView.updateTaskViewToTransform(tv, fromTransform, AnimationProps.IMMEDIATE);
-
- int duration;
- Interpolator interpolator;
- if (willScrollToFront) {
- duration = calculateStaggeredAnimDuration(i);
- interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
- } else {
- if (i < newFocusTaskViewIndex) {
- duration = 150 + ((newFocusTaskViewIndex - i - 1) * 50);
- interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
- } else if (i > newFocusTaskViewIndex) {
- duration = Math.max(100, 150 - ((i - newFocusTaskViewIndex - 1) * 50));
- interpolator = FOCUS_IN_FRONT_NEXT_TASK_INTERPOLATOR;
- } else {
- duration = 200;
- interpolator = FOCUS_NEXT_TASK_INTERPOLATOR;
- }
- }
-
- AnimationProps anim = new AnimationProps()
- .setDuration(AnimationProps.BOUNDS, duration)
- .setInterpolator(AnimationProps.BOUNDS, interpolator)
- .setListener(postAnimTrigger.decrementOnAnimationEnd());
- postAnimTrigger.increment();
- mStackView.updateTaskViewToTransform(tv, toTransform, anim);
- }
- return willScroll;
- }
-
- /**
- * Starts the animation to go to the initial stack layout with a task focused. In addition, the
- * previous task will be animated in after the scroll completes.
- */
- public void startNewStackScrollAnimation(TaskStack newStack,
- ReferenceCountedTrigger animationTrigger) {
- TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
- TaskStackViewScroller stackScroller = mStackView.getScroller();
-
- // Get the current set of task transforms
- ArrayList<Task> stackTasks = newStack.getTasks();
- mStackView.getCurrentTaskTransforms(stackTasks, mTmpCurrentTaskTransforms);
-
- // Update the stack
- mStackView.setTasks(newStack, false /* allowNotifyStackChanges */);
- mStackView.updateLayoutAlgorithm(false /* boundScroll */);
-
- // Pick up the newly visible views after the scroll
- final float newScroll = stackLayout.mInitialScrollP;
- mStackView.bindVisibleTaskViews(newScroll);
-
- // Update the internal state
- stackLayout.setFocusState(TaskStackLayoutAlgorithm.STATE_UNFOCUSED);
- stackLayout.setTaskOverridesForInitialState(newStack, true /* ignoreScrollToFront */);
- stackScroller.setStackScroll(newScroll);
- mStackView.cancelDeferredTaskViewLayoutAnimation();
-
- // Get the final set of task transforms
- mStackView.getLayoutTaskTransforms(newScroll, stackLayout.getFocusState(), stackTasks,
- false /* ignoreTaskOverrides */, mTmpFinalTaskTransforms);
-
- // Hide the front most task view until the scroll is complete
- Task frontMostTask = newStack.getFrontMostTask();
- final TaskView frontMostTaskView = mStackView.getChildViewForTask(frontMostTask);
- final TaskViewTransform frontMostTransform = mTmpFinalTaskTransforms.get(
- stackTasks.indexOf(frontMostTask));
- if (frontMostTaskView != null) {
- mStackView.updateTaskViewToTransform(frontMostTaskView,
- stackLayout.getFrontOfStackTransform(), AnimationProps.IMMEDIATE);
- }
-
- // Setup the end listener to return all the hidden views to the view pool after the
- // focus animation
- animationTrigger.addLastDecrementRunnable(new Runnable() {
- @Override
- public void run() {
- mStackView.bindVisibleTaskViews(newScroll);
-
- // Now, animate in the front-most task
- if (frontMostTaskView != null) {
- mStackView.updateTaskViewToTransform(frontMostTaskView, frontMostTransform,
- new AnimationProps(75, 250, FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR));
- }
- }
- });
-
- List<TaskView> taskViews = mStackView.getTaskViews();
- int taskViewCount = taskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
- TaskView tv = taskViews.get(i);
- Task task = tv.getTask();
-
- if (mStackView.isIgnoredTask(task)) {
- continue;
- }
- if (task == frontMostTask && frontMostTaskView != null) {
- continue;
- }
-
- int taskIndex = stackTasks.indexOf(task);
- TaskViewTransform fromTransform = mTmpCurrentTaskTransforms.get(taskIndex);
- TaskViewTransform toTransform = mTmpFinalTaskTransforms.get(taskIndex);
-
- // Update the task to the initial state (for the newly picked up tasks)
- mStackView.updateTaskViewToTransform(tv, fromTransform, AnimationProps.IMMEDIATE);
-
- int duration = calculateStaggeredAnimDuration(i);
- Interpolator interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
-
- AnimationProps anim = new AnimationProps()
- .setDuration(AnimationProps.BOUNDS, duration)
- .setInterpolator(AnimationProps.BOUNDS, interpolator)
- .setListener(animationTrigger.decrementOnAnimationEnd());
- animationTrigger.increment();
- mStackView.updateTaskViewToTransform(tv, toTransform, anim);
- }
- }
-
- /**
- * Caclulates a staggered duration for {@link #startScrollToFocusedTaskAnimation} and
- * {@link #startNewStackScrollAnimation}.
- */
- private int calculateStaggeredAnimDuration(int i) {
- return Math.max(100, 100 + ((i - 1) * 50));
- }
-
- private void startTaskGridDeleteTaskAnimation(final TaskView deleteTaskView,
- final ReferenceCountedTrigger postAnimationTrigger) {
- postAnimationTrigger.increment();
- postAnimationTrigger.addLastDecrementRunnable(() -> {
- mStackView.getTouchHandler().onChildDismissed(deleteTaskView);
- });
- deleteTaskView.animate().setDuration(300).scaleX(0.9f).scaleY(0.9f).alpha(0).setListener(
- new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- postAnimationTrigger.decrement();
- }}).start();
- }
-
- private void startTaskStackDeleteTaskAnimation(final TaskView deleteTaskView,
- final ReferenceCountedTrigger postAnimationTrigger) {
- TaskStackViewTouchHandler touchHandler = mStackView.getTouchHandler();
- touchHandler.onBeginManualDrag(deleteTaskView);
-
- postAnimationTrigger.increment();
- postAnimationTrigger.addLastDecrementRunnable(() -> {
- touchHandler.onChildDismissed(deleteTaskView);
- });
-
- final float dismissSize = touchHandler.getScaledDismissSize();
- ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
- animator.setDuration(400);
- animator.addUpdateListener((animation) -> {
- float progress = (Float) animation.getAnimatedValue();
- deleteTaskView.setTranslationX(progress * dismissSize);
- touchHandler.updateSwipeProgress(deleteTaskView, true, progress);
- });
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- postAnimationTrigger.decrement();
- }
- });
- animator.start();
- }
-
- private void startTaskStackDeleteAllTasksAnimation(final List<TaskView> taskViews,
- final ReferenceCountedTrigger postAnimationTrigger) {
- TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
-
- int offscreenXOffset = mStackView.getMeasuredWidth() - stackLayout.getTaskRect().left;
-
- int taskViewCount = taskViews.size();
- for (int i = taskViewCount - 1; i >= 0; i--) {
- TaskView tv = taskViews.get(i);
- int taskIndexFromFront = taskViewCount - i - 1;
- int startDelay = taskIndexFromFront * DOUBLE_FRAME_OFFSET_MS;
-
- // Disabling clipping with the stack while the view is animating away
- tv.setClipViewInStack(false);
-
- // Compose the new animation and transform and star the animation
- AnimationProps taskAnimation = new AnimationProps(startDelay,
- DISMISS_ALL_TASKS_DURATION, DISMISS_ALL_TRANSLATION_INTERPOLATOR,
- new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- postAnimationTrigger.decrement();
-
- // Re-enable clipping with the stack (we will reuse this view)
- tv.setClipViewInStack(true);
- }
- });
- postAnimationTrigger.increment();
-
- mTmpTransform.fillIn(tv);
- mTmpTransform.rect.offset(offscreenXOffset, 0);
- mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
- }
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
deleted file mode 100644
index 58a3f12c465d..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ /dev/null
@@ -1,1283 +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 com.android.systemui.recents.views;
-
-import android.annotation.IntDef;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Path;
-import android.graphics.Rect;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
-import android.view.ViewDebug;
-
-import com.android.systemui.R;
-import com.android.systemui.recents.LegacyRecentsImpl;
-import com.android.systemui.recents.RecentsActivityLaunchState;
-import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.RecentsDebugFlags;
-import com.android.systemui.recents.misc.FreePathInterpolator;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.views.lowram.TaskStackLowRamLayoutAlgorithm;
-import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
-
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Used to describe a visible range that can be normalized to [0, 1].
- */
-class Range {
- final float relativeMin;
- final float relativeMax;
- float origin;
- float min;
- float max;
-
- public Range(float relMin, float relMax) {
- min = relativeMin = relMin;
- max = relativeMax = relMax;
- }
-
- /**
- * Offsets this range to a given absolute position.
- */
- public void offset(float x) {
- this.origin = x;
- min = x + relativeMin;
- max = x + relativeMax;
- }
-
- /**
- * Returns x normalized to the range 0 to 1 such that 0 = min, 0.5 = origin and 1 = max
- *
- * @param x is an absolute value in the same domain as origin
- */
- public float getNormalizedX(float x) {
- if (x < origin) {
- return 0.5f + 0.5f * (x - origin) / -relativeMin;
- } else {
- return 0.5f + 0.5f * (x - origin) / relativeMax;
- }
- }
-
- /**
- * Given a normalized {@param x} value in this range, projected onto the full range to get an
- * absolute value about the given {@param origin}.
- */
- public float getAbsoluteX(float normX) {
- if (normX < 0.5f) {
- return (normX - 0.5f) / 0.5f * -relativeMin;
- } else {
- return (normX - 0.5f) / 0.5f * relativeMax;
- }
- }
-
- /**
- * Returns whether a value at an absolute x would be within range.
- */
- public boolean isInRange(float absX) {
- return (absX >= Math.floor(min)) && (absX <= Math.ceil(max));
- }
-}
-
-/**
- * The layout logic for a TaskStackView. This layout needs to be able to calculate the stack layout
- * without an activity-specific context only with the information passed in. This layout can have
- * two states focused and unfocused, and in the focused state, there is a task that is displayed
- * more prominently in the stack.
- */
-public class TaskStackLayoutAlgorithm {
-
- private static final String TAG = "TaskStackLayoutAlgorithm";
-
- // The distribution of view bounds alpha
- // XXX: This is a hack because you can currently set the max alpha to be > 1f
- public static final float OUTLINE_ALPHA_MIN_VALUE = 0f;
- public static final float OUTLINE_ALPHA_MAX_VALUE = 2f;
-
- // The medium/maximum dim on the tasks
- private static final float MED_DIM = 0.15f;
- private static final float MAX_DIM = 0.25f;
-
- // The various focus states
- public static final int STATE_FOCUSED = 1;
- public static final int STATE_UNFOCUSED = 0;
-
- // The side that an offset is anchored
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({FROM_TOP, FROM_BOTTOM})
- public @interface AnchorSide {}
- private static final int FROM_TOP = 0;
- private static final int FROM_BOTTOM = 1;
-
- // The extent that we care about when calculating fractions
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({WIDTH, HEIGHT})
- public @interface Extent {}
- private static final int WIDTH = 0;
- private static final int HEIGHT = 1;
-
- public interface TaskStackLayoutAlgorithmCallbacks {
- void onFocusStateChanged(int prevFocusState, int curFocusState);
- }
-
- /**
- * @return True if we should use the grid layout.
- */
- boolean useGridLayout() {
- return LegacyRecentsImpl.getConfiguration().isGridEnabled;
- }
-
- // A report of the visibility state of the stack
- public static class VisibilityReport {
- public int numVisibleTasks;
- public int numVisibleThumbnails;
-
- public VisibilityReport(int tasks, int thumbnails) {
- numVisibleTasks = tasks;
- numVisibleThumbnails = thumbnails;
- }
- }
-
- Context mContext;
- private TaskStackLayoutAlgorithmCallbacks mCb;
-
- // The task bounds (untransformed) for layout. This rect is anchored at mTaskRoot.
- @ViewDebug.ExportedProperty(category="recents")
- public Rect mTaskRect = new Rect();
- // The stack bounds, inset from the top system insets, and runs to the bottom of the screen
- @ViewDebug.ExportedProperty(category="recents")
- public Rect mStackRect = new Rect();
- // This is the current system insets
- @ViewDebug.ExportedProperty(category="recents")
- public Rect mSystemInsets = new Rect();
-
- // The visible ranges when the stack is focused and unfocused
- private Range mUnfocusedRange;
- private Range mFocusedRange;
-
- // This is the bounds of the stack action above the stack rect
- @ViewDebug.ExportedProperty(category="recents")
- private Rect mStackActionButtonRect = new Rect();
- // The base top margin for the stack from the system insets
- @ViewDebug.ExportedProperty(category="recents")
- private int mBaseTopMargin;
- // The base side margin for the stack from the system insets
- @ViewDebug.ExportedProperty(category="recents")
- private int mBaseSideMargin;
- // The base bottom margin for the stack from the system insets
- @ViewDebug.ExportedProperty(category="recents")
- private int mBaseBottomMargin;
- private int mMinMargin;
-
- // The initial offset that the focused task is from the top
- @ViewDebug.ExportedProperty(category="recents")
- private int mInitialTopOffset;
- private int mBaseInitialTopOffset;
- // The initial offset that the launch-from task is from the bottom
- @ViewDebug.ExportedProperty(category="recents")
- private int mInitialBottomOffset;
- private int mBaseInitialBottomOffset;
-
- // The height between the top margin and the top of the focused task
- @ViewDebug.ExportedProperty(category="recents")
- private int mFocusedTopPeekHeight;
- // The height between the bottom margin and the top of task in front of the focused task
- @ViewDebug.ExportedProperty(category="recents")
- private int mFocusedBottomPeekHeight;
-
- // The offset from the bottom of the stack to the bottom of the bounds when the stack is
- // scrolled to the front
- @ViewDebug.ExportedProperty(category="recents")
- private int mStackBottomOffset;
-
- /** The height, in pixels, of each task view's title bar. */
- private int mTitleBarHeight;
-
- // The paths defining the motion of the tasks when the stack is focused and unfocused
- private Path mUnfocusedCurve;
- private Path mFocusedCurve;
- private FreePathInterpolator mUnfocusedCurveInterpolator;
- private FreePathInterpolator mFocusedCurveInterpolator;
-
- // The paths defining the distribution of the dim to apply to tasks in the stack when focused
- // and unfocused
- private Path mUnfocusedDimCurve;
- private Path mFocusedDimCurve;
- private FreePathInterpolator mUnfocusedDimCurveInterpolator;
- private FreePathInterpolator mFocusedDimCurveInterpolator;
-
- // The state of the stack focus (0..1), which controls the transition of the stack from the
- // focused to non-focused state
- @ViewDebug.ExportedProperty(category="recents")
- private int mFocusState;
-
- // The smallest scroll progress, at this value, the back most task will be visible
- @ViewDebug.ExportedProperty(category="recents")
- float mMinScrollP;
- // The largest scroll progress, at this value, the front most task will be visible above the
- // navigation bar
- @ViewDebug.ExportedProperty(category="recents")
- float mMaxScrollP;
- // The initial progress that the scroller is set when you first enter recents
- @ViewDebug.ExportedProperty(category="recents")
- float mInitialScrollP;
- // The task progress for the front-most task in the stack
- @ViewDebug.ExportedProperty(category="recents")
- float mFrontMostTaskP;
-
- // The last computed task counts
- @ViewDebug.ExportedProperty(category="recents")
- int mNumStackTasks;
-
- // The min/max z translations
- @ViewDebug.ExportedProperty(category="recents")
- int mMinTranslationZ;
- @ViewDebug.ExportedProperty(category="recents")
- public int mMaxTranslationZ;
-
- // Optimization, allows for quick lookup of task -> index
- private SparseIntArray mTaskIndexMap = new SparseIntArray();
- private SparseArray<Float> mTaskIndexOverrideMap = new SparseArray<>();
-
- TaskGridLayoutAlgorithm mTaskGridLayoutAlgorithm;
- TaskStackLowRamLayoutAlgorithm mTaskStackLowRamLayoutAlgorithm;
-
- // The transform to place TaskViews at the front and back of the stack respectively
- TaskViewTransform mBackOfStackTransform = new TaskViewTransform();
- TaskViewTransform mFrontOfStackTransform = new TaskViewTransform();
-
- public TaskStackLayoutAlgorithm(Context context, TaskStackLayoutAlgorithmCallbacks cb) {
- mContext = context;
- mCb = cb;
- mTaskGridLayoutAlgorithm = new TaskGridLayoutAlgorithm(context);
- mTaskStackLowRamLayoutAlgorithm = new TaskStackLowRamLayoutAlgorithm(context);
- reloadOnConfigurationChange(context);
- }
-
- /**
- * Reloads the layout for the current configuration.
- */
- public void reloadOnConfigurationChange(Context context) {
- Resources res = context.getResources();
- mFocusedRange = new Range(res.getFloat(R.integer.recents_layout_focused_range_min),
- res.getFloat(R.integer.recents_layout_focused_range_max));
- mUnfocusedRange = new Range(res.getFloat(R.integer.recents_layout_unfocused_range_min),
- res.getFloat(R.integer.recents_layout_unfocused_range_max));
- mFocusState = getInitialFocusState();
- mFocusedTopPeekHeight = res.getDimensionPixelSize(R.dimen.recents_layout_top_peek_size);
- mFocusedBottomPeekHeight =
- res.getDimensionPixelSize(R.dimen.recents_layout_bottom_peek_size);
- mMinTranslationZ = res.getDimensionPixelSize(R.dimen.recents_layout_z_min);
- mMaxTranslationZ = res.getDimensionPixelSize(R.dimen.recents_layout_z_max);
- mBaseInitialTopOffset = getDimensionForDevice(context,
- R.dimen.recents_layout_initial_top_offset_phone_port,
- R.dimen.recents_layout_initial_top_offset_phone_land,
- R.dimen.recents_layout_initial_top_offset_tablet,
- R.dimen.recents_layout_initial_top_offset_tablet,
- R.dimen.recents_layout_initial_top_offset_tablet,
- R.dimen.recents_layout_initial_top_offset_tablet,
- R.dimen.recents_layout_initial_top_offset_tablet);
- mBaseInitialBottomOffset = getDimensionForDevice(context,
- R.dimen.recents_layout_initial_bottom_offset_phone_port,
- R.dimen.recents_layout_initial_bottom_offset_phone_land,
- R.dimen.recents_layout_initial_bottom_offset_tablet,
- R.dimen.recents_layout_initial_bottom_offset_tablet,
- R.dimen.recents_layout_initial_bottom_offset_tablet,
- R.dimen.recents_layout_initial_bottom_offset_tablet,
- R.dimen.recents_layout_initial_bottom_offset_tablet);
- mTaskGridLayoutAlgorithm.reloadOnConfigurationChange(context);
- mTaskStackLowRamLayoutAlgorithm.reloadOnConfigurationChange(context);
- mMinMargin = res.getDimensionPixelSize(R.dimen.recents_layout_min_margin);
- mBaseTopMargin = getDimensionForDevice(context,
- R.dimen.recents_layout_top_margin_phone,
- R.dimen.recents_layout_top_margin_tablet,
- R.dimen.recents_layout_top_margin_tablet_xlarge,
- R.dimen.recents_layout_top_margin_tablet);
- mBaseSideMargin = getDimensionForDevice(context,
- R.dimen.recents_layout_side_margin_phone,
- R.dimen.recents_layout_side_margin_tablet,
- R.dimen.recents_layout_side_margin_tablet_xlarge,
- R.dimen.recents_layout_side_margin_tablet);
- mBaseBottomMargin = res.getDimensionPixelSize(R.dimen.recents_layout_bottom_margin);
- mTitleBarHeight = getDimensionForDevice(mContext,
- R.dimen.recents_task_view_header_height,
- R.dimen.recents_task_view_header_height,
- R.dimen.recents_task_view_header_height,
- R.dimen.recents_task_view_header_height_tablet_land,
- R.dimen.recents_task_view_header_height,
- R.dimen.recents_task_view_header_height_tablet_land,
- R.dimen.recents_grid_task_view_header_height);
- }
-
- /**
- * Resets this layout when the stack view is reset.
- */
- public void reset() {
- mTaskIndexOverrideMap.clear();
- setFocusState(getInitialFocusState());
- }
-
- /**
- * Sets the system insets.
- */
- public boolean setSystemInsets(Rect systemInsets) {
- boolean changed = !mSystemInsets.equals(systemInsets);
- mSystemInsets.set(systemInsets);
- mTaskGridLayoutAlgorithm.setSystemInsets(systemInsets);
- mTaskStackLowRamLayoutAlgorithm.setSystemInsets(systemInsets);
- return changed;
- }
-
- /**
- * Sets the focused state.
- */
- public void setFocusState(int focusState) {
- int prevFocusState = mFocusState;
- mFocusState = focusState;
- updateFrontBackTransforms();
- if (mCb != null) {
- mCb.onFocusStateChanged(prevFocusState, focusState);
- }
- }
-
- /**
- * Gets the focused state.
- */
- public int getFocusState() {
- return mFocusState;
- }
-
- /**
- * Computes the stack and task rects. The given task stack bounds already has the top/right
- * insets and left/right padding already applied.
- */
- public void initialize(Rect displayRect, Rect windowRect, Rect taskStackBounds) {
- Rect lastStackRect = new Rect(mStackRect);
-
- int topMargin = getScaleForExtent(windowRect, displayRect, mBaseTopMargin, mMinMargin, HEIGHT);
- int bottomMargin = getScaleForExtent(windowRect, displayRect, mBaseBottomMargin, mMinMargin,
- HEIGHT);
- mInitialTopOffset = getScaleForExtent(windowRect, displayRect, mBaseInitialTopOffset,
- mMinMargin, HEIGHT);
- mInitialBottomOffset = mBaseInitialBottomOffset;
-
- // Compute the stack bounds
- mStackBottomOffset = mSystemInsets.bottom + bottomMargin;
- mStackRect.set(taskStackBounds);
- mStackRect.top += topMargin;
-
- // The stack action button will take the full un-padded header space above the stack
- mStackActionButtonRect.set(mStackRect.left, mStackRect.top - topMargin,
- mStackRect.right, mStackRect.top + mFocusedTopPeekHeight);
-
- // Anchor the task rect top aligned to the stack rect
- int height = mStackRect.height() - mInitialTopOffset - mStackBottomOffset;
- mTaskRect.set(mStackRect.left, mStackRect.top, mStackRect.right, mStackRect.top + height);
-
- if (mTaskRect.width() <= 0 || mTaskRect.height() <= 0) {
- // Logging for b/36654830
- Log.e(TAG, "Invalid task rect: taskRect=" + mTaskRect + " stackRect=" + mStackRect
- + " displayRect=" + displayRect + " windowRect=" + windowRect
- + " taskStackBounds=" + taskStackBounds);
- }
-
- // Short circuit here if the stack rects haven't changed so we don't do all the work below
- if (!lastStackRect.equals(mStackRect)) {
- // Reinitialize the focused and unfocused curves
- mUnfocusedCurve = constructUnfocusedCurve();
- mUnfocusedCurveInterpolator = new FreePathInterpolator(mUnfocusedCurve);
- mFocusedCurve = constructFocusedCurve();
- mFocusedCurveInterpolator = new FreePathInterpolator(mFocusedCurve);
- mUnfocusedDimCurve = constructUnfocusedDimCurve();
- mUnfocusedDimCurveInterpolator = new FreePathInterpolator(mUnfocusedDimCurve);
- mFocusedDimCurve = constructFocusedDimCurve();
- mFocusedDimCurveInterpolator = new FreePathInterpolator(mFocusedDimCurve);
-
- updateFrontBackTransforms();
- }
-
- // Initialize the grid layout
- mTaskGridLayoutAlgorithm.initialize(windowRect);
- mTaskStackLowRamLayoutAlgorithm.initialize(windowRect);
- }
-
- /**
- * Computes the minimum and maximum scroll progress values and the progress values for each task
- * in the stack.
- */
- public void update(TaskStack stack, ArraySet<Task.TaskKey> ignoreTasksSet,
- RecentsActivityLaunchState launchState, float lastScrollPPercent) {
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
-
- // Clear the progress map
- mTaskIndexMap.clear();
-
- // Return early if we have no tasks
- ArrayList<Task> tasks = stack.getTasks();
- if (tasks.isEmpty()) {
- mFrontMostTaskP = 0;
- mMinScrollP = mMaxScrollP = mInitialScrollP = 0;
- mNumStackTasks = 0;
- return;
- }
-
- // Filter the set of stack tasks
- ArrayList<Task> stackTasks = new ArrayList<>();
- for (int i = 0; i < tasks.size(); i++) {
- Task task = tasks.get(i);
- if (ignoreTasksSet.contains(task.key)) {
- continue;
- }
- stackTasks.add(task);
- }
- mNumStackTasks = stackTasks.size();
-
- // Put each of the tasks in the progress map at a fixed index (does not need to actually
- // map to a scroll position, just by index)
- int taskCount = stackTasks.size();
- for (int i = 0; i < taskCount; i++) {
- Task task = stackTasks.get(i);
- mTaskIndexMap.put(task.key.id, i);
- }
-
- // Calculate the min/max/initial scroll
- Task launchTask = stack.getLaunchTarget();
- int launchTaskIndex = launchTask != null
- ? stack.indexOfTask(launchTask)
- : mNumStackTasks - 1;
- if (getInitialFocusState() == STATE_FOCUSED) {
- int maxBottomOffset = mStackBottomOffset + mTaskRect.height();
- float maxBottomNormX = getNormalizedXFromFocusedY(maxBottomOffset, FROM_BOTTOM);
- mFocusedRange.offset(0f);
- mMinScrollP = 0;
- mMaxScrollP = Math.max(mMinScrollP, (mNumStackTasks - 1) -
- Math.max(0, mFocusedRange.getAbsoluteX(maxBottomNormX)));
- if (launchState.launchedFromHome || launchState.launchedFromPipApp
- || launchState.launchedWithNextPipApp) {
- mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
- } else {
- mInitialScrollP = Utilities.clamp(launchTaskIndex - 1, mMinScrollP, mMaxScrollP);
- }
- } else if (mNumStackTasks == 1) {
- // If there is one stack task, ignore the min/max/initial scroll positions
- mMinScrollP = 0;
- mMaxScrollP = 0;
- mInitialScrollP = 0;
- } else {
- // Set the max scroll to be the point where the front most task is visible with the
- // stack bottom offset
- int maxBottomOffset = mStackBottomOffset + mTaskRect.height();
- float maxBottomNormX = getNormalizedXFromUnfocusedY(maxBottomOffset, FROM_BOTTOM);
- mUnfocusedRange.offset(0f);
- mMinScrollP = LegacyRecentsImpl.getConfiguration().isLowRamDevice
- ? mTaskStackLowRamLayoutAlgorithm.getMinScrollP()
- : 0;
- mMaxScrollP = LegacyRecentsImpl.getConfiguration().isLowRamDevice
- ? mTaskStackLowRamLayoutAlgorithm.getMaxScrollP(taskCount)
- : Math.max(mMinScrollP, (mNumStackTasks - 1) -
- Math.max(0, mUnfocusedRange.getAbsoluteX(maxBottomNormX)));
- boolean scrollToFront = launchState.launchedFromHome || launchState.launchedFromPipApp
- || launchState.launchedWithNextPipApp || launchState.launchedViaDockGesture;
-
- if (launchState.launchedWithAltTab) {
- mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
- } else if (0 <= lastScrollPPercent && lastScrollPPercent <= 1) {
- mInitialScrollP = Utilities.mapRange(lastScrollPPercent, mMinScrollP, mMaxScrollP);
- } else if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- mInitialScrollP = mTaskStackLowRamLayoutAlgorithm.getInitialScrollP(mNumStackTasks,
- scrollToFront);
- } else if (scrollToFront) {
- mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
- } else {
- // We are overriding the initial two task positions, so set the initial scroll
- // position to match the second task (aka focused task) position
- float initialTopNormX = getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP);
- mInitialScrollP = Math.max(mMinScrollP, Math.min(mMaxScrollP, (mNumStackTasks - 2))
- - Math.max(0, mUnfocusedRange.getAbsoluteX(initialTopNormX)));
- }
- }
- }
-
- /**
- * Creates task overrides to ensure the initial stack layout if necessary.
- */
- public void setTaskOverridesForInitialState(TaskStack stack, boolean ignoreScrollToFront) {
- RecentsActivityLaunchState launchState = LegacyRecentsImpl.getConfiguration().getLaunchState();
-
- mTaskIndexOverrideMap.clear();
-
- boolean scrollToFront = launchState.launchedFromHome ||
- launchState.launchedFromPipApp ||
- launchState.launchedWithNextPipApp ||
- launchState.launchedViaDockGesture;
- if (getInitialFocusState() == STATE_UNFOCUSED && mNumStackTasks > 1) {
- if (ignoreScrollToFront || (!launchState.launchedWithAltTab && !scrollToFront)) {
- // Set the initial scroll to the predefined state (which differs from the stack)
- float [] initialNormX = null;
- float minBottomTaskNormX = getNormalizedXFromUnfocusedY(mSystemInsets.bottom +
- mInitialBottomOffset, FROM_BOTTOM);
- float maxBottomTaskNormX = getNormalizedXFromUnfocusedY(mFocusedTopPeekHeight +
- mTaskRect.height() - mMinMargin, FROM_TOP);
- if (mNumStackTasks <= 2) {
- // For small stacks, position the tasks so that they are top aligned to under
- // the action button, but ensure that it is at least a certain offset from the
- // bottom of the stack
- initialNormX = new float[] {
- Math.min(maxBottomTaskNormX, minBottomTaskNormX),
- getNormalizedXFromUnfocusedY(mFocusedTopPeekHeight, FROM_TOP)
- };
- } else {
- initialNormX = new float[] {
- minBottomTaskNormX,
- getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP)
- };
- }
-
- mUnfocusedRange.offset(0f);
- List<Task> tasks = stack.getTasks();
- int taskCount = tasks.size();
- for (int i = taskCount - 1; i >= 0; i--) {
- int indexFromFront = taskCount - i - 1;
- if (indexFromFront >= initialNormX.length) {
- break;
- }
- float newTaskProgress = mInitialScrollP +
- mUnfocusedRange.getAbsoluteX(initialNormX[indexFromFront]);
- mTaskIndexOverrideMap.put(tasks.get(i).key.id, newTaskProgress);
- }
- }
- }
- }
-
- /**
- * Adds and override task progress for the given task when transitioning from focused to
- * unfocused state.
- */
- public void addUnfocusedTaskOverride(Task task, float stackScroll) {
- if (mFocusState != STATE_UNFOCUSED) {
- mFocusedRange.offset(stackScroll);
- mUnfocusedRange.offset(stackScroll);
- float focusedRangeX = mFocusedRange.getNormalizedX(mTaskIndexMap.get(task.key.id));
- float focusedY = mFocusedCurveInterpolator.getInterpolation(focusedRangeX);
- float unfocusedRangeX = mUnfocusedCurveInterpolator.getX(focusedY);
- float unfocusedTaskProgress = stackScroll + mUnfocusedRange.getAbsoluteX(unfocusedRangeX);
- if (Float.compare(focusedRangeX, unfocusedRangeX) != 0) {
- mTaskIndexOverrideMap.put(task.key.id, unfocusedTaskProgress);
- }
- }
- }
-
- /**
- * Adds and override task progress for the given task when transitioning from focused to
- * unfocused state.
- */
- public void addUnfocusedTaskOverride(TaskView taskView, float stackScroll) {
- mFocusedRange.offset(stackScroll);
- mUnfocusedRange.offset(stackScroll);
-
- Task task = taskView.getTask();
- int top = taskView.getTop() - mTaskRect.top;
- float focusedRangeX = getNormalizedXFromFocusedY(top, FROM_TOP);
- float unfocusedRangeX = getNormalizedXFromUnfocusedY(top, FROM_TOP);
- float unfocusedTaskProgress = stackScroll + mUnfocusedRange.getAbsoluteX(unfocusedRangeX);
- if (Float.compare(focusedRangeX, unfocusedRangeX) != 0) {
- mTaskIndexOverrideMap.put(task.key.id, unfocusedTaskProgress);
- }
- }
-
- public void clearUnfocusedTaskOverrides() {
- mTaskIndexOverrideMap.clear();
- }
-
- /**
- * Updates this stack when a scroll happens.
- *
- */
- public float updateFocusStateOnScroll(float lastTargetStackScroll, float targetStackScroll,
- float lastStackScroll) {
- if (targetStackScroll == lastStackScroll || LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- return targetStackScroll;
- }
-
- float deltaScroll = targetStackScroll - lastStackScroll;
- float deltaTargetScroll = targetStackScroll - lastTargetStackScroll;
- float newScroll = targetStackScroll;
- mUnfocusedRange.offset(targetStackScroll);
- for (int i = mTaskIndexOverrideMap.size() - 1; i >= 0; i--) {
- int taskId = mTaskIndexOverrideMap.keyAt(i);
- float x = mTaskIndexMap.get(taskId);
- float overrideX = mTaskIndexOverrideMap.get(taskId, 0f);
- float newOverrideX = overrideX + deltaScroll;
- if (isInvalidOverrideX(x, overrideX, newOverrideX)) {
- // Remove the override once we reach the original task index
- mTaskIndexOverrideMap.removeAt(i);
- } else if ((overrideX >= x && deltaScroll <= 0f) ||
- (overrideX <= x && deltaScroll >= 0f)) {
- // Scrolling from override x towards x, then lock the task in place
- mTaskIndexOverrideMap.put(taskId, newOverrideX);
- } else {
- // Scrolling override x away from x, we should still move the scroll towards x
- newScroll = lastStackScroll;
- newOverrideX = overrideX - deltaTargetScroll;
- if (isInvalidOverrideX(x, overrideX, newOverrideX)) {
- mTaskIndexOverrideMap.removeAt(i);
- } else{
- mTaskIndexOverrideMap.put(taskId, newOverrideX);
- }
- }
- }
- return newScroll;
- }
-
- private boolean isInvalidOverrideX(float x, float overrideX, float newOverrideX) {
- boolean outOfBounds = mUnfocusedRange.getNormalizedX(newOverrideX) < 0f ||
- mUnfocusedRange.getNormalizedX(newOverrideX) > 1f;
- return outOfBounds || (overrideX >= x && x >= newOverrideX) ||
- (overrideX <= x && x <= newOverrideX);
- }
-
- /**
- * Returns the default focus state.
- */
- public int getInitialFocusState() {
- RecentsActivityLaunchState launchState = LegacyRecentsImpl.getConfiguration().getLaunchState();
- RecentsDebugFlags debugFlags = LegacyRecentsImpl.getDebugFlags();
- if (launchState.launchedWithAltTab) {
- return STATE_FOCUSED;
- } else {
- return STATE_UNFOCUSED;
- }
- }
-
- public Rect getStackActionButtonRect() {
- return useGridLayout()
- ? mTaskGridLayoutAlgorithm.getStackActionButtonRect() : mStackActionButtonRect;
- }
-
- /**
- * Returns the TaskViewTransform that would put the task just off the back of the stack.
- */
- public TaskViewTransform getBackOfStackTransform() {
- return mBackOfStackTransform;
- }
-
- /**
- * Returns the TaskViewTransform that would put the task just off the front of the stack.
- */
- public TaskViewTransform getFrontOfStackTransform() {
- return mFrontOfStackTransform;
- }
-
- /**
- * Returns whether this stack layout has been initialized.
- */
- public boolean isInitialized() {
- return !mStackRect.isEmpty();
- }
-
- /**
- * Computes the maximum number of visible tasks and thumbnails when the scroll is at the initial
- * stack scroll. Requires that update() is called first.
- */
- public VisibilityReport computeStackVisibilityReport(ArrayList<Task> tasks) {
- if (useGridLayout()) {
- return mTaskGridLayoutAlgorithm.computeStackVisibilityReport(tasks);
- }
-
- if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- return mTaskStackLowRamLayoutAlgorithm.computeStackVisibilityReport(tasks);
- }
-
- // Ensure minimum visibility count
- if (tasks.size() <= 1) {
- return new VisibilityReport(1, 1);
- }
-
- // Otherwise, walk backwards in the stack and count the number of tasks and visible
- // thumbnails and add that to the total task count
- TaskViewTransform tmpTransform = new TaskViewTransform();
- Range currentRange = getInitialFocusState() > 0f ? mFocusedRange : mUnfocusedRange;
- currentRange.offset(mInitialScrollP);
- int taskBarHeight = mContext.getResources().getDimensionPixelSize(
- R.dimen.recents_task_view_header_height);
- int numVisibleTasks = 0;
- int numVisibleThumbnails = 0;
- float prevScreenY = Integer.MAX_VALUE;
- for (int i = tasks.size() - 1; i >= 0; i--) {
- Task task = tasks.get(i);
-
- // Skip invisible
- float taskProgress = getStackScrollForTask(task);
- if (!currentRange.isInRange(taskProgress)) {
- continue;
- }
-
- getStackTransform(taskProgress, taskProgress, mInitialScrollP, mFocusState,
- tmpTransform, null, false /* ignoreSingleTaskCase */, false /* forceUpdate */);
- float screenY = tmpTransform.rect.top;
- boolean hasVisibleThumbnail = (prevScreenY - screenY) > taskBarHeight;
- if (hasVisibleThumbnail) {
- numVisibleThumbnails++;
- numVisibleTasks++;
- prevScreenY = screenY;
- } else {
- // Once we hit the next front most task that does not have a visible thumbnail,
- // walk through remaining visible set
- for (int j = i; j >= 0; j--) {
- taskProgress = getStackScrollForTask(tasks.get(j));
- if (!currentRange.isInRange(taskProgress)) {
- break;
- }
- numVisibleTasks++;
- }
- break;
- }
- }
- return new VisibilityReport(numVisibleTasks, numVisibleThumbnails);
- }
-
- /**
- * Returns the transform for the given task. This transform is relative to the mTaskRect, which
- * is what the view is measured and laid out with.
- */
- public TaskViewTransform getStackTransform(Task task, float stackScroll,
- TaskViewTransform transformOut, TaskViewTransform frontTransform) {
- return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform,
- false /* forceUpdate */, false /* ignoreTaskOverrides */);
- }
-
- public TaskViewTransform getStackTransform(Task task, float stackScroll,
- TaskViewTransform transformOut, TaskViewTransform frontTransform,
- boolean ignoreTaskOverrides) {
- return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform,
- false /* forceUpdate */, ignoreTaskOverrides);
- }
-
- public TaskViewTransform getStackTransform(Task task, float stackScroll, int focusState,
- TaskViewTransform transformOut, TaskViewTransform frontTransform, boolean forceUpdate,
- boolean ignoreTaskOverrides) {
- if (useGridLayout()) {
- int taskIndex = mTaskIndexMap.get(task.key.id);
- int taskCount = mTaskIndexMap.size();
- mTaskGridLayoutAlgorithm.getTransform(taskIndex, taskCount, transformOut, this);
- return transformOut;
- } else if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- if (task == null) {
- transformOut.reset();
- return transformOut;
- }
- int taskIndex = mTaskIndexMap.get(task.key.id);
- mTaskStackLowRamLayoutAlgorithm.getTransform(taskIndex, stackScroll, transformOut,
- mNumStackTasks, this);
- return transformOut;
- } else {
- // Return early if we have an invalid index
- int nonOverrideTaskProgress = mTaskIndexMap.get(task.key.id, -1);
- if (task == null || nonOverrideTaskProgress == -1) {
- transformOut.reset();
- return transformOut;
- }
- float taskProgress = ignoreTaskOverrides
- ? nonOverrideTaskProgress
- : getStackScrollForTask(task);
-
- getStackTransform(taskProgress, nonOverrideTaskProgress, stackScroll, focusState,
- transformOut, frontTransform, false /* ignoreSingleTaskCase */, forceUpdate);
- return transformOut;
- }
- }
-
- /**
- * Like {@link #getStackTransform}, but in screen coordinates
- */
- public TaskViewTransform getStackTransformScreenCoordinates(Task task, float stackScroll,
- TaskViewTransform transformOut, TaskViewTransform frontTransform,
- Rect windowOverrideRect) {
- TaskViewTransform transform = getStackTransform(task, stackScroll, mFocusState,
- transformOut, frontTransform, true /* forceUpdate */,
- false /* ignoreTaskOverrides */);
- return transformToScreenCoordinates(transform, windowOverrideRect);
- }
-
- /**
- * Transforms the given {@param transformOut} to the screen coordinates, overriding the current
- * window rectangle with {@param windowOverrideRect} if non-null.
- */
- TaskViewTransform transformToScreenCoordinates(TaskViewTransform transformOut,
- Rect windowOverrideRect) {
- Rect windowRect = windowOverrideRect != null
- ? windowOverrideRect
- : LegacyRecentsImpl.getSystemServices().getWindowRect();
- transformOut.rect.offset(windowRect.left, windowRect.top);
- if (useGridLayout()) {
- // Draw the thumbnail a little lower to perfectly coincide with the view we are
- // transitioning to, where the header bar has already been drawn.
- transformOut.rect.offset(0, mTitleBarHeight);
- }
- return transformOut;
- }
-
- /**
- * Update/get the transform.
- *
- * @param ignoreSingleTaskCase When set, will ensure that the transform computed does not take
- * into account the special single-task case. This is only used
- * internally to ensure that we can calculate the transform for any
- * position in the stack.
- */
- public void getStackTransform(float taskProgress, float nonOverrideTaskProgress,
- float stackScroll, int focusState, TaskViewTransform transformOut,
- TaskViewTransform frontTransform, boolean ignoreSingleTaskCase, boolean forceUpdate) {
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
-
- // Ensure that the task is in range
- mUnfocusedRange.offset(stackScroll);
- mFocusedRange.offset(stackScroll);
- boolean unfocusedVisible = mUnfocusedRange.isInRange(taskProgress);
- boolean focusedVisible = mFocusedRange.isInRange(taskProgress);
-
- // Skip if the task is not visible
- if (!forceUpdate && !unfocusedVisible && !focusedVisible) {
- transformOut.reset();
- return;
- }
-
- // Map the absolute task progress to the normalized x at the stack scroll. We use this to
- // calculate positions along the curve.
- mUnfocusedRange.offset(stackScroll);
- mFocusedRange.offset(stackScroll);
- float unfocusedRangeX = mUnfocusedRange.getNormalizedX(taskProgress);
- float focusedRangeX = mFocusedRange.getNormalizedX(taskProgress);
-
- // Map the absolute task progress to the normalized x at the bounded stack scroll. We use
- // this to calculate bounded properties, like translationZ and outline alpha.
- float boundedStackScroll = Utilities.clamp(stackScroll, mMinScrollP, mMaxScrollP);
- mUnfocusedRange.offset(boundedStackScroll);
- mFocusedRange.offset(boundedStackScroll);
- float boundedScrollUnfocusedRangeX = mUnfocusedRange.getNormalizedX(taskProgress);
- float boundedScrollUnfocusedNonOverrideRangeX =
- mUnfocusedRange.getNormalizedX(nonOverrideTaskProgress);
-
- // Map the absolute task progress to the normalized x at the upper bounded stack scroll.
- // We use this to calculate the dim, which is bounded only on one end.
- float lowerBoundedStackScroll = Utilities.clamp(stackScroll, -Float.MAX_VALUE, mMaxScrollP);
- mUnfocusedRange.offset(lowerBoundedStackScroll);
- mFocusedRange.offset(lowerBoundedStackScroll);
- float lowerBoundedUnfocusedRangeX = mUnfocusedRange.getNormalizedX(taskProgress);
- float lowerBoundedFocusedRangeX = mFocusedRange.getNormalizedX(taskProgress);
-
- int x = (mStackRect.width() - mTaskRect.width()) / 2;
- int y;
- float z;
- float dimAlpha;
- float viewOutlineAlpha;
- if (mNumStackTasks == 1 && !ignoreSingleTaskCase) {
- // When there is exactly one task, then decouple the task from the stack and just move
- // in screen space
- float tmpP = (mMinScrollP - stackScroll) / mNumStackTasks;
- int centerYOffset = (mStackRect.top - mTaskRect.top) +
- (mStackRect.height() - mSystemInsets.bottom - mTaskRect.height()) / 2;
- y = centerYOffset + getYForDeltaP(tmpP, 0);
- z = mMaxTranslationZ;
- dimAlpha = 0f;
- viewOutlineAlpha = OUTLINE_ALPHA_MIN_VALUE +
- (OUTLINE_ALPHA_MAX_VALUE - OUTLINE_ALPHA_MIN_VALUE) / 2f;
-
- } else {
- // Otherwise, update the task to the stack layout
- int unfocusedY = (int) ((1f - mUnfocusedCurveInterpolator.getInterpolation(
- unfocusedRangeX)) * mStackRect.height());
- int focusedY = (int) ((1f - mFocusedCurveInterpolator.getInterpolation(
- focusedRangeX)) * mStackRect.height());
- float unfocusedDim = mUnfocusedDimCurveInterpolator.getInterpolation(
- lowerBoundedUnfocusedRangeX);
- float focusedDim = mFocusedDimCurveInterpolator.getInterpolation(
- lowerBoundedFocusedRangeX);
-
- // Special case, because we override the initial task positions differently for small
- // stacks, we clamp the dim to 0 in the initial position, and then only modulate the
- // dim when the task is scrolled back towards the top of the screen
- if (mNumStackTasks <= 2 && nonOverrideTaskProgress == 0f) {
- if (boundedScrollUnfocusedRangeX >= 0.5f) {
- unfocusedDim = 0f;
- } else {
- float offset = mUnfocusedDimCurveInterpolator.getInterpolation(0.5f);
- unfocusedDim -= offset;
- unfocusedDim *= MAX_DIM / (MAX_DIM - offset);
- }
- }
- y = (mStackRect.top - mTaskRect.top) +
- (int) com.android.systemui.recents.utilities.Utilities
- .mapRange(focusState, unfocusedY, focusedY);
- z = Utilities.mapRange(Utilities.clamp01(boundedScrollUnfocusedNonOverrideRangeX),
- mMinTranslationZ, mMaxTranslationZ);
- dimAlpha = com.android.systemui.recents.utilities.Utilities
- .mapRange(focusState, unfocusedDim, focusedDim);
- viewOutlineAlpha = Utilities.mapRange(Utilities.clamp01(boundedScrollUnfocusedRangeX),
- OUTLINE_ALPHA_MIN_VALUE, OUTLINE_ALPHA_MAX_VALUE);
- }
-
- // Fill out the transform
- transformOut.scale = 1f;
- transformOut.alpha = 1f;
- transformOut.translationZ = z;
- transformOut.dimAlpha = dimAlpha;
- transformOut.viewOutlineAlpha = viewOutlineAlpha;
- transformOut.rect.set(mTaskRect);
- transformOut.rect.offset(x, y);
- Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
- transformOut.visible = (transformOut.rect.top < mStackRect.bottom) &&
- (frontTransform == null || transformOut.rect.top != frontTransform.rect.top);
- }
-
- /**
- * Returns the untransformed task view bounds.
- */
- public Rect getUntransformedTaskViewBounds() {
- return new Rect(mTaskRect);
- }
-
- /**
- * Returns the scroll progress to scroll to such that the top of the task is at the top of the
- * stack.
- */
- float getStackScrollForTask(Task t) {
- Float overrideP = mTaskIndexOverrideMap.get(t.key.id, null);
- if (LegacyRecentsImpl.getConfiguration().isLowRamDevice || overrideP == null) {
- return (float) mTaskIndexMap.get(t.key.id, 0);
- }
- return overrideP;
- }
-
- /**
- * Returns the original scroll progress to scroll to such that the top of the task is at the top
- * of the stack.
- */
- float getStackScrollForTaskIgnoreOverrides(Task t) {
- return (float) mTaskIndexMap.get(t.key.id, 0);
- }
-
- /**
- * Returns the scroll progress to scroll to such that the top of the task at the initial top
- * offset (which is at the task's brightest point).
- */
- float getStackScrollForTaskAtInitialOffset(Task t) {
- if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- RecentsActivityLaunchState launchState = LegacyRecentsImpl.getConfiguration().getLaunchState();
- return mTaskStackLowRamLayoutAlgorithm.getInitialScrollP(mNumStackTasks,
- launchState.launchedFromHome || launchState.launchedFromPipApp
- || launchState.launchedWithNextPipApp);
- }
- float normX = getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP);
- mUnfocusedRange.offset(0f);
- return Utilities.clamp((float) mTaskIndexMap.get(t.key.id, 0) - Math.max(0,
- mUnfocusedRange.getAbsoluteX(normX)), mMinScrollP, mMaxScrollP);
- }
-
- /**
- * Maps a movement in screen y, relative to {@param downY}, to a movement in along the arc
- * length of the curve. We know the curve is mostly flat, so we just map the length of the
- * screen along the arc-length proportionally (1/arclength).
- */
- public float getDeltaPForY(int downY, int y) {
- if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- return mTaskStackLowRamLayoutAlgorithm.scrollToPercentage(downY - y);
- }
- float deltaP = (float) (y - downY) / mStackRect.height() *
- mUnfocusedCurveInterpolator.getArcLength();
- return -deltaP;
- }
-
- /**
- * This is the inverse of {@link #getDeltaPForY}. Given a movement along the arc length
- * of the curve, map back to the screen y.
- */
- public int getYForDeltaP(float downScrollP, float p) {
- if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- return mTaskStackLowRamLayoutAlgorithm.percentageToScroll(downScrollP - p);
- }
- int y = (int) ((p - downScrollP) * mStackRect.height() *
- (1f / mUnfocusedCurveInterpolator.getArcLength()));
- return -y;
- }
-
- /**
- * Returns the task stack bounds in the current orientation. This rect takes into account the
- * top and right system insets (but not the bottom inset) and left/right paddings, but _not_
- * the top/bottom padding or insets.
- */
- public void getTaskStackBounds(Rect displayRect, Rect windowRect, int topInset, int leftInset,
- int rightInset, Rect taskStackBounds) {
- taskStackBounds.set(windowRect.left + leftInset, windowRect.top + topInset,
- windowRect.right - rightInset, windowRect.bottom);
-
- // Ensure that the new width is at most the smaller display edge size
- int sideMargin = getScaleForExtent(windowRect, displayRect, mBaseSideMargin, mMinMargin,
- WIDTH);
- int targetStackWidth = taskStackBounds.width() - 2 * sideMargin;
- if (Utilities.getAppConfiguration(mContext).orientation
- == Configuration.ORIENTATION_LANDSCAPE) {
- // If we are in landscape, calculate the width of the stack in portrait and ensure that
- // we are not larger than that size
- Rect portraitDisplayRect = new Rect(0, 0,
- Math.min(displayRect.width(), displayRect.height()),
- Math.max(displayRect.width(), displayRect.height()));
- int portraitSideMargin = getScaleForExtent(portraitDisplayRect, portraitDisplayRect,
- mBaseSideMargin, mMinMargin, WIDTH);
- targetStackWidth = Math.min(targetStackWidth,
- portraitDisplayRect.width() - 2 * portraitSideMargin);
- }
- taskStackBounds.inset((taskStackBounds.width() - targetStackWidth) / 2, 0);
- }
-
- /**
- * Retrieves resources that are constant regardless of the current configuration of the device.
- */
- public static int getDimensionForDevice(Context ctx, int phoneResId,
- int tabletResId, int xlargeTabletResId, int gridLayoutResId) {
- return getDimensionForDevice(ctx, phoneResId, phoneResId, tabletResId, tabletResId,
- xlargeTabletResId, xlargeTabletResId, gridLayoutResId);
- }
-
- /**
- * Retrieves resources that are constant regardless of the current configuration of the device.
- */
- public static int getDimensionForDevice(Context ctx, int phonePortResId, int phoneLandResId,
- int tabletPortResId, int tabletLandResId, int xlargeTabletPortResId,
- int xlargeTabletLandResId, int gridLayoutResId) {
- RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
- Resources res = ctx.getResources();
- boolean isLandscape = Utilities.getAppConfiguration(ctx).orientation ==
- Configuration.ORIENTATION_LANDSCAPE;
- if (config.isGridEnabled) {
- return res.getDimensionPixelSize(gridLayoutResId);
- } else if (config.isXLargeScreen) {
- return res.getDimensionPixelSize(isLandscape
- ? xlargeTabletLandResId
- : xlargeTabletPortResId);
- } else if (config.isLargeScreen) {
- return res.getDimensionPixelSize(isLandscape
- ? tabletLandResId
- : tabletPortResId);
- } else {
- return res.getDimensionPixelSize(isLandscape
- ? phoneLandResId
- : phonePortResId);
- }
- }
-
- /**
- * Returns the normalized x on the unfocused curve given an absolute Y position (relative to the
- * stack height).
- */
- private float getNormalizedXFromUnfocusedY(float y, @AnchorSide int fromSide) {
- float offset = (fromSide == FROM_TOP)
- ? mStackRect.height() - y
- : y;
- float offsetPct = offset / mStackRect.height();
- return mUnfocusedCurveInterpolator.getX(offsetPct);
- }
-
- /**
- * Returns the normalized x on the focused curve given an absolute Y position (relative to the
- * stack height).
- */
- private float getNormalizedXFromFocusedY(float y, @AnchorSide int fromSide) {
- float offset = (fromSide == FROM_TOP)
- ? mStackRect.height() - y
- : y;
- float offsetPct = offset / mStackRect.height();
- return mFocusedCurveInterpolator.getX(offsetPct);
- }
-
- /**
- * Creates a new path for the focused curve.
- */
- private Path constructFocusedCurve() {
- // Initialize the focused curve. This curve is a piecewise curve composed of several
- // linear pieces that goes from (0,1) through (0.5, peek height offset),
- // (0.5, bottom task offsets), and (1,0).
- float topPeekHeightPct = (float) mFocusedTopPeekHeight / mStackRect.height();
- float bottomPeekHeightPct = (float) (mStackBottomOffset + mFocusedBottomPeekHeight) /
- mStackRect.height();
- float minBottomPeekHeightPct = (float) (mFocusedTopPeekHeight + mTaskRect.height() -
- mMinMargin) / mStackRect.height();
- Path p = new Path();
- p.moveTo(0f, 1f);
- p.lineTo(0.5f, 1f - topPeekHeightPct);
- p.lineTo(1f - (0.5f / mFocusedRange.relativeMax), Math.max(1f - minBottomPeekHeightPct,
- bottomPeekHeightPct));
- p.lineTo(1f, 0f);
- return p;
- }
-
- /**
- * Creates a new path for the unfocused curve.
- */
- private Path constructUnfocusedCurve() {
- // Initialize the unfocused curve. This curve is a piecewise curve composed of two quadradic
- // beziers that goes from (0,1) through (0.5, peek height offset) and ends at (1,0). This
- // ensures that we match the range, at which 0.5 represents the stack scroll at the current
- // task progress. Because the height offset can change depending on a resource, we compute
- // the control point of the second bezier such that between it and a first known point,
- // there is a tangent at (0.5, peek height offset).
- float cpoint1X = 0.4f;
- float cpoint1Y = 0.975f;
- float topPeekHeightPct = (float) mFocusedTopPeekHeight / mStackRect.height();
- float slope = ((1f - topPeekHeightPct) - cpoint1Y) / (0.5f - cpoint1X);
- float b = 1f - slope * cpoint1X;
- float cpoint2X = 0.65f;
- float cpoint2Y = slope * cpoint2X + b;
- Path p = new Path();
- p.moveTo(0f, 1f);
- p.cubicTo(0f, 1f, cpoint1X, cpoint1Y, 0.5f, 1f - topPeekHeightPct);
- p.cubicTo(0.5f, 1f - topPeekHeightPct, cpoint2X, cpoint2Y, 1f, 0f);
- return p;
- }
-
- /**
- * Creates a new path for the focused dim curve.
- */
- private Path constructFocusedDimCurve() {
- Path p = new Path();
- // The focused dim interpolator starts at max dim, reduces to zero at 0.5 (the focused
- // task), then goes back to max dim at the next task
- p.moveTo(0f, MAX_DIM);
- p.lineTo(0.5f, 0f);
- p.lineTo(0.5f + (0.5f / mFocusedRange.relativeMax), MAX_DIM);
- p.lineTo(1f, MAX_DIM);
- return p;
- }
-
- /**
- * Creates a new path for the unfocused dim curve.
- */
- private Path constructUnfocusedDimCurve() {
- float focusX = getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP);
- float cpoint2X = focusX + (1f - focusX) / 2;
- Path p = new Path();
- // The unfocused dim interpolator starts at max dim, reduces to zero at 0.5 (the focused
- // task), then goes back to max dim towards the front of the stack
- p.moveTo(0f, MAX_DIM);
- p.cubicTo(focusX * 0.5f, MAX_DIM, focusX * 0.75f, MAX_DIM * 0.75f, focusX, 0f);
- p.cubicTo(cpoint2X, 0f, cpoint2X, MED_DIM, 1f, MED_DIM);
- return p;
- }
-
- /**
- * Scales the given {@param value} to the scale of the {@param instance} rect relative to the
- * {@param other} rect in the {@param extent} side.
- */
- private int getScaleForExtent(Rect instance, Rect other, int value, int minValue,
- @Extent int extent) {
- if (extent == WIDTH) {
- float scale = Utilities.clamp01((float) instance.width() / other.width());
- return Math.max(minValue, (int) (scale * value));
- } else if (extent == HEIGHT) {
- float scale = Utilities.clamp01((float) instance.height() / other.height());
- return Math.max(minValue, (int) (scale * value));
- }
- return value;
- }
-
- /**
- * Updates the current transforms that would put a TaskView at the front and back of the stack.
- */
- private void updateFrontBackTransforms() {
- // Return early if we have not yet initialized
- if (mStackRect.isEmpty()) {
- return;
- }
- if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- mTaskStackLowRamLayoutAlgorithm.getBackOfStackTransform(mBackOfStackTransform, this);
- mTaskStackLowRamLayoutAlgorithm.getFrontOfStackTransform(mFrontOfStackTransform, this);
- return;
- }
-
- float min = Utilities.mapRange(mFocusState, mUnfocusedRange.relativeMin,
- mFocusedRange.relativeMin);
- float max = Utilities.mapRange(mFocusState, mUnfocusedRange.relativeMax,
- mFocusedRange.relativeMax);
- getStackTransform(min, min, 0f, mFocusState, mBackOfStackTransform, null,
- true /* ignoreSingleTaskCase */, true /* forceUpdate */);
- getStackTransform(max, max, 0f, mFocusState, mFrontOfStackTransform, null,
- true /* ignoreSingleTaskCase */, true /* forceUpdate */);
- mBackOfStackTransform.visible = true;
- mFrontOfStackTransform.visible = true;
- }
-
- /**
- * Returns the proper task rectangle according to the current grid state.
- */
- public Rect getTaskRect() {
- if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- return mTaskStackLowRamLayoutAlgorithm.getTaskRect();
- }
- return useGridLayout() ? mTaskGridLayoutAlgorithm.getTaskGridRect() : mTaskRect;
- }
-
- public void dump(String prefix, PrintWriter writer) {
- String innerPrefix = prefix + " ";
-
- writer.print(prefix); writer.print(TAG);
- writer.write(" numStackTasks="); writer.print(mNumStackTasks);
- writer.println();
-
- writer.print(innerPrefix);
- writer.print("insets="); writer.print(Utilities.dumpRect(mSystemInsets));
- writer.print(" stack="); writer.print(Utilities.dumpRect(mStackRect));
- writer.print(" task="); writer.print(Utilities.dumpRect(mTaskRect));
- writer.print(" actionButton="); writer.print(
- Utilities.dumpRect(mStackActionButtonRect));
- writer.println();
-
- writer.print(innerPrefix);
- writer.print("minScroll="); writer.print(mMinScrollP);
- writer.print(" maxScroll="); writer.print(mMaxScrollP);
- writer.print(" initialScroll="); writer.print(mInitialScrollP);
- writer.println();
-
- writer.print(innerPrefix);
- writer.print("focusState="); writer.print(mFocusState);
- writer.println();
-
- if (mTaskIndexOverrideMap.size() > 0) {
- for (int i = mTaskIndexOverrideMap.size() - 1; i >= 0; i--) {
- int taskId = mTaskIndexOverrideMap.keyAt(i);
- float x = mTaskIndexMap.get(taskId);
- float overrideX = mTaskIndexOverrideMap.get(taskId, 0f);
-
- writer.print(innerPrefix);
- writer.print("taskId= "); writer.print(taskId);
- writer.print(" x= "); writer.print(x);
- writer.print(" overrideX= "); writer.print(overrideX);
- writer.println();
- }
- }
- }
-} \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackView.java
deleted file mode 100644
index b89218c81a91..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackView.java
+++ /dev/null
@@ -1,2291 +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 com.android.systemui.recents.views;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.annotation.IntDef;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.MutableBoolean;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewDebug;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.FrameLayout;
-import android.widget.ScrollView;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.recents.LegacyRecentsImpl;
-import com.android.systemui.recents.RecentsActivity;
-import com.android.systemui.recents.RecentsActivityLaunchState;
-import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.RecentsDebugFlags;
-import com.android.systemui.recents.RecentsImpl;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
-import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
-import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
-import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
-import com.android.systemui.recents.events.activity.HideRecentsEvent;
-import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
-import com.android.systemui.recents.events.activity.LaunchMostRecentTaskRequestEvent;
-import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskEvent;
-import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
-import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
-import com.android.systemui.recents.events.activity.PackagesChangedEvent;
-import com.android.systemui.recents.events.activity.ShowEmptyViewEvent;
-import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
-import com.android.systemui.recents.events.component.ActivityPinnedEvent;
-import com.android.systemui.recents.events.component.ExpandPipEvent;
-import com.android.systemui.recents.events.component.HidePipMenuEvent;
-import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
-import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
-import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
-import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
-import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
-import com.android.systemui.recents.events.ui.RecentsGrowingEvent;
-import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
-import com.android.systemui.recents.events.ui.UserInteractionEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
-import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
-import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
-import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
-import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent;
-import com.android.systemui.recents.misc.DozeTrigger;
-import com.android.systemui.recents.misc.ReferenceCountedTrigger;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.utilities.AnimationProps;
-import com.android.systemui.recents.utilities.Utilities;
-import com.android.systemui.recents.views.grid.GridTaskView;
-import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
-import com.android.systemui.recents.views.grid.TaskViewFocusFrame;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-
-
-/* The visual representation of a task stack view */
-public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCallbacks,
- TaskView.TaskViewCallbacks, TaskStackViewScroller.TaskStackViewScrollerCallbacks,
- TaskStackLayoutAlgorithm.TaskStackLayoutAlgorithmCallbacks,
- ViewPool.ViewPoolConsumer<TaskView, Task> {
-
- private static final String TAG = "TaskStackView";
-
- // The thresholds at which to show/hide the stack action button.
- private static final float SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD = 0.3f;
- private static final float HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD = 0.3f;
-
- public static final int DEFAULT_SYNC_STACK_DURATION = 200;
- public static final int SLOW_SYNC_STACK_DURATION = 250;
- private static final int DRAG_SCALE_DURATION = 175;
- static final float DRAG_SCALE_FACTOR = 1.05f;
-
- private static final int LAUNCH_NEXT_SCROLL_BASE_DURATION = 216;
- private static final int LAUNCH_NEXT_SCROLL_INCR_DURATION = 32;
-
- // The actions to perform when resetting to initial state,
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({INITIAL_STATE_UPDATE_NONE, INITIAL_STATE_UPDATE_ALL, INITIAL_STATE_UPDATE_LAYOUT_ONLY})
- public @interface InitialStateAction {}
- /** Do not update the stack and layout to the initial state. */
- private static final int INITIAL_STATE_UPDATE_NONE = 0;
- /** Update both the stack and layout to the initial state. */
- private static final int INITIAL_STATE_UPDATE_ALL = 1;
- /** Update only the layout to the initial state. */
- private static final int INITIAL_STATE_UPDATE_LAYOUT_ONLY = 2;
-
- private LayoutInflater mInflater;
- private TaskStack mStack = new TaskStack();
- @ViewDebug.ExportedProperty(deepExport=true, prefix="layout_")
- TaskStackLayoutAlgorithm mLayoutAlgorithm;
- // The stable layout algorithm is only used to calculate the task rect with the stable bounds
- private TaskStackLayoutAlgorithm mStableLayoutAlgorithm;
- @ViewDebug.ExportedProperty(deepExport=true, prefix="scroller_")
- private TaskStackViewScroller mStackScroller;
- @ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
- private TaskStackViewTouchHandler mTouchHandler;
- private TaskStackAnimationHelper mAnimationHelper;
- private ViewPool<TaskView, Task> mViewPool;
-
- private ArrayList<TaskView> mTaskViews = new ArrayList<>();
- private ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>();
- private ArraySet<Task.TaskKey> mIgnoreTasks = new ArraySet<>();
- private AnimationProps mDeferredTaskViewLayoutAnimation = null;
-
- @ViewDebug.ExportedProperty(deepExport=true, prefix="doze_")
- private DozeTrigger mUIDozeTrigger;
- @ViewDebug.ExportedProperty(deepExport=true, prefix="focused_task_")
- private Task mFocusedTask;
-
- private int mTaskCornerRadiusPx;
- private int mDividerSize;
- private int mStartTimerIndicatorDuration;
-
- @ViewDebug.ExportedProperty(category="recents")
- private boolean mTaskViewsClipDirty = true;
- @ViewDebug.ExportedProperty(category="recents")
- private boolean mEnterAnimationComplete = false;
- @ViewDebug.ExportedProperty(category="recents")
- private boolean mStackReloaded = false;
- @ViewDebug.ExportedProperty(category="recents")
- private boolean mFinishedLayoutAfterStackReload = false;
- @ViewDebug.ExportedProperty(category="recents")
- private boolean mLaunchNextAfterFirstMeasure = false;
- @ViewDebug.ExportedProperty(category="recents")
- @InitialStateAction
- private int mInitialState = INITIAL_STATE_UPDATE_ALL;
- @ViewDebug.ExportedProperty(category="recents")
- private boolean mInMeasureLayout = false;
- @ViewDebug.ExportedProperty(category="recents")
- boolean mTouchExplorationEnabled;
- @ViewDebug.ExportedProperty(category="recents")
- boolean mScreenPinningEnabled;
-
- // The stable stack bounds are the full bounds that we were measured with from RecentsView
- @ViewDebug.ExportedProperty(category="recents")
- private Rect mStableStackBounds = new Rect();
- // The current stack bounds are dynamic and may change as the user drags and drops
- @ViewDebug.ExportedProperty(category="recents")
- private Rect mStackBounds = new Rect();
- // The current window bounds at the point we were measured
- @ViewDebug.ExportedProperty(category="recents")
- private Rect mStableWindowRect = new Rect();
- // The current window bounds are dynamic and may change as the user drags and drops
- @ViewDebug.ExportedProperty(category="recents")
- private Rect mWindowRect = new Rect();
- // The current display bounds
- @ViewDebug.ExportedProperty(category="recents")
- private Rect mDisplayRect = new Rect();
- // The current display orientation
- @ViewDebug.ExportedProperty(category="recents")
- private int mDisplayOrientation = Configuration.ORIENTATION_UNDEFINED;
-
- private Rect mTmpRect = new Rect();
- private ArrayMap<Task.TaskKey, TaskView> mTmpTaskViewMap = new ArrayMap<>();
- private List<TaskView> mTmpTaskViews = new ArrayList<>();
- private TaskViewTransform mTmpTransform = new TaskViewTransform();
- private int[] mTmpIntPair = new int[2];
- private boolean mResetToInitialStateWhenResized;
- private int mLastWidth;
- private int mLastHeight;
- private boolean mStackActionButtonVisible;
-
- // Percentage of last ScrollP from the min to max scrollP that lives after configuration changes
- private float mLastScrollPPercent = -1;
-
- // We keep track of the task view focused by user interaction and draw a frame around it in the
- // grid layout.
- private TaskViewFocusFrame mTaskViewFocusFrame;
-
- private Task mPrefetchingTask;
- private final float mFastFlingVelocity;
-
- // A convenience update listener to request updating clipping of tasks
- private ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
- new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- if (!mTaskViewsClipDirty) {
- mTaskViewsClipDirty = true;
- invalidate();
- }
- }
- };
-
- private DropTarget mStackDropTarget = new DropTarget() {
- @Override
- public boolean acceptsDrop(int x, int y, int width, int height, Rect insets,
- boolean isCurrentTarget) {
- // This drop target has a fixed bounds and should be checked last, so just fall through
- // if it is the current target
- if (!isCurrentTarget) {
- return mLayoutAlgorithm.mStackRect.contains(x, y);
- }
- return false;
- }
- };
-
- public TaskStackView(Context context) {
- super(context);
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- Resources res = context.getResources();
-
- // Set the stack first
- mStack.setCallbacks(this);
- mViewPool = new ViewPool<>(context, this);
- mInflater = LayoutInflater.from(context);
- mLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, this);
- mStableLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, null);
- mStackScroller = new TaskStackViewScroller(context, this, mLayoutAlgorithm);
- mTouchHandler = new TaskStackViewTouchHandler(
- context, this, mStackScroller, Dependency.get(FalsingManager.class));
- mAnimationHelper = new TaskStackAnimationHelper(context, this);
- mTaskCornerRadiusPx = LegacyRecentsImpl.getConfiguration().isGridEnabled ?
- res.getDimensionPixelSize(R.dimen.recents_grid_task_view_rounded_corners_radius) :
- res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
- mFastFlingVelocity = res.getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
- mDividerSize = ssp.getDockedDividerSize(context);
- mDisplayOrientation = Utilities.getAppConfiguration(mContext).orientation;
- mDisplayRect = ssp.getDisplayRect();
- mStackActionButtonVisible = false;
-
- // Create a frame to draw around the focused task view
- if (LegacyRecentsImpl.getConfiguration().isGridEnabled) {
- mTaskViewFocusFrame = new TaskViewFocusFrame(mContext, this,
- mLayoutAlgorithm.mTaskGridLayoutAlgorithm);
- addView(mTaskViewFocusFrame);
- getViewTreeObserver().addOnGlobalFocusChangeListener(mTaskViewFocusFrame);
- }
-
- int taskBarDismissDozeDelaySeconds = getResources().getInteger(
- R.integer.recents_task_bar_dismiss_delay_seconds);
- mUIDozeTrigger = new DozeTrigger(taskBarDismissDozeDelaySeconds, new Runnable() {
- @Override
- public void run() {
- // Show the task bar dismiss buttons
- List<TaskView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
- TaskView tv = taskViews.get(i);
- tv.startNoUserInteractionAnimation();
- }
- }
- });
- setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
- }
-
- @Override
- protected void onAttachedToWindow() {
- EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
- super.onAttachedToWindow();
- readSystemFlags();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- EventBus.getDefault().unregister(this);
- }
-
- /**
- * Called from RecentsActivity when it is relaunched.
- */
- void onReload(boolean isResumingFromVisible) {
- if (!isResumingFromVisible) {
- // Reset the focused task
- resetFocusedTask(getFocusedTask());
- }
-
- // Reset the state of each of the task views
- List<TaskView> taskViews = new ArrayList<>();
- taskViews.addAll(getTaskViews());
- taskViews.addAll(mViewPool.getViews());
- for (int i = taskViews.size() - 1; i >= 0; i--) {
- taskViews.get(i).onReload(isResumingFromVisible);
- }
-
- // Reset the stack state
- readSystemFlags();
- mTaskViewsClipDirty = true;
- mUIDozeTrigger.stopDozing();
- if (!isResumingFromVisible) {
- mStackScroller.reset();
- mStableLayoutAlgorithm.reset();
- mLayoutAlgorithm.reset();
- mLastScrollPPercent = -1;
- }
-
- // Since we always animate to the same place in (the initial state), always reset the stack
- // to the initial state when resuming
- mStackReloaded = true;
- mFinishedLayoutAfterStackReload = false;
- mLaunchNextAfterFirstMeasure = false;
- mInitialState = INITIAL_STATE_UPDATE_ALL;
- requestLayout();
- }
-
- /**
- * Sets the stack tasks of this TaskStackView from the given TaskStack.
- */
- public void setTasks(TaskStack stack, boolean allowNotifyStackChanges) {
- boolean isInitialized = mLayoutAlgorithm.isInitialized();
-
- // Only notify if we are already initialized, otherwise, everything will pick up all the
- // new and old tasks when we next layout
- mStack.setTasks(stack, allowNotifyStackChanges && isInitialized);
- }
-
- /** Returns the task stack. */
- public TaskStack getStack() {
- return mStack;
- }
-
- /**
- * Updates this TaskStackView to the initial state.
- */
- public void updateToInitialState() {
- mStackScroller.setStackScrollToInitialState();
- mLayoutAlgorithm.setTaskOverridesForInitialState(mStack, false /* ignoreScrollToFront */);
- }
-
- /** Updates the list of task views */
- void updateTaskViewsList() {
- mTaskViews.clear();
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View v = getChildAt(i);
- if (v instanceof TaskView) {
- mTaskViews.add((TaskView) v);
- }
- }
- }
-
- /** Gets the list of task views */
- List<TaskView> getTaskViews() {
- return mTaskViews;
- }
-
- /**
- * Returns the front most task view.
- */
- private TaskView getFrontMostTaskView() {
- List<TaskView> taskViews = getTaskViews();
- if (taskViews.isEmpty()) {
- return null;
- }
- return taskViews.get(taskViews.size() - 1);
- }
-
- /**
- * Finds the child view given a specific {@param task}.
- */
- public TaskView getChildViewForTask(Task t) {
- List<TaskView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
- TaskView tv = taskViews.get(i);
- if (tv.getTask() == t) {
- return tv;
- }
- }
- return null;
- }
-
- /** Returns the stack algorithm for this task stack. */
- public TaskStackLayoutAlgorithm getStackAlgorithm() {
- return mLayoutAlgorithm;
- }
-
- /** Returns the grid algorithm for this task stack. */
- public TaskGridLayoutAlgorithm getGridAlgorithm() {
- return mLayoutAlgorithm.mTaskGridLayoutAlgorithm;
- }
-
- /**
- * Returns the touch handler for this task stack.
- */
- public TaskStackViewTouchHandler getTouchHandler() {
- return mTouchHandler;
- }
-
- /**
- * Adds a task to the ignored set.
- */
- void addIgnoreTask(Task task) {
- mIgnoreTasks.add(task.key);
- }
-
- /**
- * Removes a task from the ignored set.
- */
- void removeIgnoreTask(Task task) {
- mIgnoreTasks.remove(task.key);
- }
-
- /**
- * Returns whether the specified {@param task} is ignored.
- */
- boolean isIgnoredTask(Task task) {
- return mIgnoreTasks.contains(task.key);
- }
-
- /**
- * Computes the task transforms at the current stack scroll for all visible tasks. If a valid
- * target stack scroll is provided (ie. is different than {@param curStackScroll}), then the
- * visible range includes all tasks at the target stack scroll. This is useful for ensure that
- * all views necessary for a transition or animation will be visible at the start.
- *
- * @param taskTransforms The set of task view transforms to reuse, this list will be sized to
- * match the size of {@param tasks}
- * @param tasks The set of tasks for which to generate transforms
- * @param curStackScroll The current stack scroll
- * @param targetStackScroll The stack scroll that we anticipate we are going to be scrolling to.
- * The range of the union of the visible views at the current and
- * target stack scrolls will be returned.
- * @param ignoreTasksSet The set of tasks to skip for purposes of calculaing the visible range.
- * Transforms will still be calculated for the ignore tasks.
- * @return the front and back most visible task indices (there may be non visible tasks in
- * between this range)
- */
- int[] computeVisibleTaskTransforms(ArrayList<TaskViewTransform> taskTransforms,
- ArrayList<Task> tasks, float curStackScroll, float targetStackScroll,
- ArraySet<Task.TaskKey> ignoreTasksSet, boolean ignoreTaskOverrides) {
- int taskCount = tasks.size();
- int[] visibleTaskRange = mTmpIntPair;
- visibleTaskRange[0] = -1;
- visibleTaskRange[1] = -1;
- boolean useTargetStackScroll = Float.compare(curStackScroll, targetStackScroll) != 0;
-
- // We can reuse the task transforms where possible to reduce object allocation
- matchTaskListSize(tasks, taskTransforms);
-
- // Update the stack transforms
- TaskViewTransform frontTransform = null;
- TaskViewTransform frontTransformAtTarget = null;
- TaskViewTransform transform = null;
- TaskViewTransform transformAtTarget = null;
- for (int i = taskCount - 1; i >= 0; i--) {
- Task task = tasks.get(i);
-
- // Calculate the current and (if necessary) the target transform for the task
- transform = mLayoutAlgorithm.getStackTransform(task, curStackScroll,
- taskTransforms.get(i), frontTransform, ignoreTaskOverrides);
- if (useTargetStackScroll && !transform.visible) {
- // If we have a target stack scroll and the task is not currently visible, then we
- // just update the transform at the new scroll
- // TODO: Optimize this
- transformAtTarget = mLayoutAlgorithm.getStackTransform(task, targetStackScroll,
- new TaskViewTransform(), frontTransformAtTarget);
- if (transformAtTarget.visible) {
- transform.copyFrom(transformAtTarget);
- }
- }
-
- // For ignore tasks, only calculate the stack transform and skip the calculation of the
- // visible stack indices
- if (ignoreTasksSet.contains(task.key)) {
- continue;
- }
-
- frontTransform = transform;
- frontTransformAtTarget = transformAtTarget;
- if (transform.visible) {
- if (visibleTaskRange[0] < 0) {
- visibleTaskRange[0] = i;
- }
- visibleTaskRange[1] = i;
- }
- }
- return visibleTaskRange;
- }
-
- /**
- * Binds the visible {@link TaskView}s at the given target scroll.
- */
- void bindVisibleTaskViews(float targetStackScroll) {
- bindVisibleTaskViews(targetStackScroll, false /* ignoreTaskOverrides */);
- }
-
- /**
- * Synchronizes the set of children {@link TaskView}s to match the visible set of tasks in the
- * current {@link TaskStack}. This call does not continue on to update their position to the
- * computed {@link TaskViewTransform}s of the visible range, but only ensures that they will
- * be added/removed from the view hierarchy and placed in the correct Z order and initial
- * position (if not currently on screen).
- *
- * @param targetStackScroll If provided, will ensure that the set of visible {@link TaskView}s
- * includes those visible at the current stack scroll, and all at the
- * target stack scroll.
- * @param ignoreTaskOverrides If set, the visible task computation will get the transforms for
- * tasks at their non-overridden task progress
- */
- void bindVisibleTaskViews(float targetStackScroll, boolean ignoreTaskOverrides) {
- // Get all the task transforms
- ArrayList<Task> tasks = mStack.getTasks();
- int[] visibleTaskRange = computeVisibleTaskTransforms(mCurrentTaskTransforms, tasks,
- mStackScroller.getStackScroll(), targetStackScroll, mIgnoreTasks,
- ignoreTaskOverrides);
-
- // Return all the invisible children to the pool
- mTmpTaskViewMap.clear();
- List<TaskView> taskViews = getTaskViews();
- int lastFocusedTaskIndex = -1;
- int taskViewCount = taskViews.size();
- for (int i = taskViewCount - 1; i >= 0; i--) {
- TaskView tv = taskViews.get(i);
- Task task = tv.getTask();
-
- // Skip ignored tasks
- if (mIgnoreTasks.contains(task.key)) {
- continue;
- }
-
- // It is possible for the set of lingering TaskViews to differ from the stack if the
- // stack was updated before the relayout. If the task view is no longer in the stack,
- // then just return it back to the view pool.
- int taskIndex = mStack.indexOfTask(task);
- TaskViewTransform transform = null;
- if (taskIndex != -1) {
- transform = mCurrentTaskTransforms.get(taskIndex);
- }
-
- if (transform != null && transform.visible) {
- mTmpTaskViewMap.put(task.key, tv);
- } else {
- if (mTouchExplorationEnabled && Utilities.isDescendentAccessibilityFocused(tv)) {
- lastFocusedTaskIndex = taskIndex;
- resetFocusedTask(task);
- }
- mViewPool.returnViewToPool(tv);
- }
- }
-
- // Pick up all the newly visible children
- for (int i = tasks.size() - 1; i >= 0; i--) {
- Task task = tasks.get(i);
- TaskViewTransform transform = mCurrentTaskTransforms.get(i);
-
- // Skip ignored tasks
- if (mIgnoreTasks.contains(task.key)) {
- continue;
- }
-
- // Skip the invisible stack tasks
- if (!transform.visible) {
- continue;
- }
-
- TaskView tv = mTmpTaskViewMap.get(task.key);
- if (tv == null) {
- tv = mViewPool.pickUpViewFromPool(task, task);
- if (transform.rect.top <= mLayoutAlgorithm.mStackRect.top) {
- updateTaskViewToTransform(tv, mLayoutAlgorithm.getBackOfStackTransform(),
- AnimationProps.IMMEDIATE);
- } else {
- updateTaskViewToTransform(tv, mLayoutAlgorithm.getFrontOfStackTransform(),
- AnimationProps.IMMEDIATE);
- }
- } else {
- // Reattach it in the right z order
- final int taskIndex = mStack.indexOfTask(task);
- final int insertIndex = findTaskViewInsertIndex(task, taskIndex);
- if (insertIndex != getTaskViews().indexOf(tv)){
- detachViewFromParent(tv);
- attachViewToParent(tv, insertIndex, tv.getLayoutParams());
- updateTaskViewsList();
- }
- }
- }
-
- updatePrefetchingTask(tasks, visibleTaskRange[0], visibleTaskRange[1]);
-
- // Update the focus if the previous focused task was returned to the view pool
- if (lastFocusedTaskIndex != -1) {
- int newFocusedTaskIndex = (lastFocusedTaskIndex < visibleTaskRange[1])
- ? visibleTaskRange[1]
- : visibleTaskRange[0];
- setFocusedTask(newFocusedTaskIndex, false /* scrollToTask */,
- true /* requestViewFocus */);
- TaskView focusedTaskView = getChildViewForTask(mFocusedTask);
- if (focusedTaskView != null) {
- focusedTaskView.requestAccessibilityFocus();
- }
- }
- }
-
- /**
- * @see #relayoutTaskViews(AnimationProps, ArrayMap<Task, AnimationProps>, boolean)
- */
- public void relayoutTaskViews(AnimationProps animation) {
- relayoutTaskViews(animation, null /* animationOverrides */,
- false /* ignoreTaskOverrides */);
- }
-
- /**
- * Relayout the the visible {@link TaskView}s to their current transforms as specified by the
- * {@link TaskStackLayoutAlgorithm} with the given {@param animation}. This call cancels any
- * animations that are current running on those task views, and will ensure that the children
- * {@link TaskView}s will match the set of visible tasks in the stack. If a {@link Task} has
- * an animation provided in {@param animationOverrides}, that will be used instead.
- */
- private void relayoutTaskViews(AnimationProps animation,
- ArrayMap<Task, AnimationProps> animationOverrides, boolean ignoreTaskOverrides) {
- // If we had a deferred animation, cancel that
- cancelDeferredTaskViewLayoutAnimation();
-
- // Synchronize the current set of TaskViews
- bindVisibleTaskViews(mStackScroller.getStackScroll(), ignoreTaskOverrides);
-
- // Animate them to their final transforms with the given animation
- List<TaskView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
- TaskView tv = taskViews.get(i);
- Task task = tv.getTask();
-
- if (mIgnoreTasks.contains(task.key)) {
- continue;
- }
-
- int taskIndex = mStack.indexOfTask(task);
- TaskViewTransform transform = mCurrentTaskTransforms.get(taskIndex);
- if (animationOverrides != null && animationOverrides.containsKey(task)) {
- animation = animationOverrides.get(task);
- }
-
- updateTaskViewToTransform(tv, transform, animation);
- }
- }
-
- /**
- * Posts an update to synchronize the {@link TaskView}s with the stack on the next frame.
- */
- void relayoutTaskViewsOnNextFrame(AnimationProps animation) {
- mDeferredTaskViewLayoutAnimation = animation;
- invalidate();
- }
-
- /**
- * Called to update a specific {@link TaskView} to a given {@link TaskViewTransform} with a
- * given set of {@link AnimationProps} properties.
- */
- public void updateTaskViewToTransform(TaskView taskView, TaskViewTransform transform,
- AnimationProps animation) {
- if (taskView.isAnimatingTo(transform)) {
- return;
- }
- taskView.cancelTransformAnimation();
- taskView.updateViewPropertiesToTaskTransform(transform, animation,
- mRequestUpdateClippingListener);
- }
-
- /**
- * Returns the current task transforms of all tasks, falling back to the stack layout if there
- * is no {@link TaskView} for the task.
- */
- public void getCurrentTaskTransforms(ArrayList<Task> tasks,
- ArrayList<TaskViewTransform> transformsOut) {
- matchTaskListSize(tasks, transformsOut);
- int focusState = mLayoutAlgorithm.getFocusState();
- for (int i = tasks.size() - 1; i >= 0; i--) {
- Task task = tasks.get(i);
- TaskViewTransform transform = transformsOut.get(i);
- TaskView tv = getChildViewForTask(task);
- if (tv != null) {
- transform.fillIn(tv);
- } else {
- mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(),
- focusState, transform, null, true /* forceUpdate */,
- false /* ignoreTaskOverrides */);
- }
- transform.visible = true;
- }
- }
-
- /**
- * Returns the task transforms for all the tasks in the stack if the stack was at the given
- * {@param stackScroll} and {@param focusState}.
- */
- public void getLayoutTaskTransforms(float stackScroll, int focusState, ArrayList<Task> tasks,
- boolean ignoreTaskOverrides, ArrayList<TaskViewTransform> transformsOut) {
- matchTaskListSize(tasks, transformsOut);
- for (int i = tasks.size() - 1; i >= 0; i--) {
- Task task = tasks.get(i);
- TaskViewTransform transform = transformsOut.get(i);
- mLayoutAlgorithm.getStackTransform(task, stackScroll, focusState, transform, null,
- true /* forceUpdate */, ignoreTaskOverrides);
- transform.visible = true;
- }
- }
-
- /**
- * Cancels the next deferred task view layout.
- */
- void cancelDeferredTaskViewLayoutAnimation() {
- mDeferredTaskViewLayoutAnimation = null;
- }
-
- /**
- * Cancels all {@link TaskView} animations.
- */
- void cancelAllTaskViewAnimations() {
- List<TaskView> taskViews = getTaskViews();
- for (int i = taskViews.size() - 1; i >= 0; i--) {
- final TaskView tv = taskViews.get(i);
- if (!mIgnoreTasks.contains(tv.getTask().key)) {
- tv.cancelTransformAnimation();
- }
- }
- }
-
- /**
- * Updates the clip for each of the task views from back to front.
- */
- private void clipTaskViews() {
- // We never clip task views in grid layout
- if (LegacyRecentsImpl.getConfiguration().isGridEnabled) {
- return;
- }
-
- // Update the clip on each task child
- List<TaskView> taskViews = getTaskViews();
- TaskView tmpTv = null;
- TaskView prevVisibleTv = null;
- int taskViewCount = taskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
- TaskView tv = taskViews.get(i);
- TaskView frontTv = null;
- int clipBottom = 0;
-
- if (isIgnoredTask(tv.getTask())) {
- // For each of the ignore tasks, update the translationZ of its TaskView to be
- // between the translationZ of the tasks immediately underneath it
- if (prevVisibleTv != null) {
- tv.setTranslationZ(Math.max(tv.getTranslationZ(),
- prevVisibleTv.getTranslationZ() + 0.1f));
- }
- }
-
- if (i < (taskViewCount - 1) && tv.shouldClipViewInStack()) {
- // Find the next view to clip against
- for (int j = i + 1; j < taskViewCount; j++) {
- tmpTv = taskViews.get(j);
-
- if (tmpTv.shouldClipViewInStack()) {
- frontTv = tmpTv;
- break;
- }
- }
-
- // Clip against the next view, this is just an approximation since we are
- // stacked and we can make assumptions about the visibility of the this
- // task relative to the ones in front of it.
- if (frontTv != null) {
- float taskBottom = tv.getBottom();
- float frontTaskTop = frontTv.getTop();
- if (frontTaskTop < taskBottom) {
- // Map the stack view space coordinate (the rects) to view space
- clipBottom = (int) (taskBottom - frontTaskTop) - mTaskCornerRadiusPx;
- }
- }
- }
- tv.getViewBounds().setClipBottom(clipBottom);
- tv.mThumbnailView.updateThumbnailVisibility(clipBottom - tv.getPaddingBottom());
- prevVisibleTv = tv;
- }
- mTaskViewsClipDirty = false;
- }
-
- public void updateLayoutAlgorithm(boolean boundScrollToNewMinMax) {
- updateLayoutAlgorithm(boundScrollToNewMinMax, LegacyRecentsImpl.getConfiguration().getLaunchState());
- }
-
- /**
- * Updates the layout algorithm min and max virtual scroll bounds.
- */
- public void updateLayoutAlgorithm(boolean boundScrollToNewMinMax,
- RecentsActivityLaunchState launchState) {
- // Compute the min and max scroll values
- mLayoutAlgorithm.update(mStack, mIgnoreTasks, launchState, mLastScrollPPercent);
-
- if (boundScrollToNewMinMax) {
- mStackScroller.boundScroll();
- }
- }
-
- /**
- * Updates the stack layout to its stable places.
- */
- private void updateLayoutToStableBounds() {
- mWindowRect.set(mStableWindowRect);
- mStackBounds.set(mStableStackBounds);
- mLayoutAlgorithm.setSystemInsets(mStableLayoutAlgorithm.mSystemInsets);
- mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds);
- updateLayoutAlgorithm(true /* boundScroll */);
- }
-
- /** Returns the scroller. */
- public TaskStackViewScroller getScroller() {
- return mStackScroller;
- }
-
- /**
- * Sets the focused task to the provided (bounded taskIndex).
- *
- * @return whether or not the stack will scroll as a part of this focus change
- */
- public boolean setFocusedTask(int taskIndex, boolean scrollToTask,
- final boolean requestViewFocus) {
- return setFocusedTask(taskIndex, scrollToTask, requestViewFocus, 0);
- }
-
- /**
- * Sets the focused task to the provided (bounded focusTaskIndex).
- *
- * @return whether or not the stack will scroll as a part of this focus change
- */
- public boolean setFocusedTask(int focusTaskIndex, boolean scrollToTask,
- boolean requestViewFocus, int timerIndicatorDuration) {
- // Find the next task to focus
- int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
- Utilities.clamp(focusTaskIndex, 0, mStack.getTaskCount() - 1) : -1;
- final Task newFocusedTask = (newFocusedTaskIndex != -1) ?
- mStack.getTasks().get(newFocusedTaskIndex) : null;
-
- // Reset the last focused task state if changed
- if (mFocusedTask != null) {
- // Cancel the timer indicator, if applicable
- if (timerIndicatorDuration > 0) {
- final TaskView tv = getChildViewForTask(mFocusedTask);
- if (tv != null) {
- tv.getHeaderView().cancelFocusTimerIndicator();
- }
- }
-
- resetFocusedTask(mFocusedTask);
- }
-
- boolean willScroll = false;
- mFocusedTask = newFocusedTask;
-
- if (newFocusedTask != null) {
- // Start the timer indicator, if applicable
- if (timerIndicatorDuration > 0) {
- final TaskView tv = getChildViewForTask(mFocusedTask);
- if (tv != null) {
- tv.getHeaderView().startFocusTimerIndicator(timerIndicatorDuration);
- } else {
- // The view is null; set a flag for later
- mStartTimerIndicatorDuration = timerIndicatorDuration;
- }
- }
-
- if (scrollToTask) {
- // Cancel any running enter animations at this point when we scroll or change focus
- if (!mEnterAnimationComplete) {
- cancelAllTaskViewAnimations();
- }
-
- mLayoutAlgorithm.clearUnfocusedTaskOverrides();
- willScroll = mAnimationHelper.startScrollToFocusedTaskAnimation(newFocusedTask,
- requestViewFocus);
- if (willScroll) {
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED);
- }
- } else {
- // Focus the task view
- TaskView newFocusedTaskView = getChildViewForTask(newFocusedTask);
- if (newFocusedTaskView != null) {
- newFocusedTaskView.setFocusedState(true, requestViewFocus);
- }
- }
- // Any time a task view gets the focus, we move the focus frame around it.
- if (mTaskViewFocusFrame != null) {
- mTaskViewFocusFrame.moveGridTaskViewFocus(getChildViewForTask(newFocusedTask));
- }
- }
- return willScroll;
- }
-
- /**
- * Sets the focused task relative to the currently focused task.
- *
- * @param forward whether to go to the next task in the stack (along the curve) or the previous
- * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
- * if the currently focused task is not a stack task, will set the focus
- * to the first visible stack task
- * @param animated determines whether to actually draw the highlight along with the change in
- * focus.
- */
- public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated) {
- setRelativeFocusedTask(forward, stackTasksOnly, animated, false, 0);
- }
-
- /**
- * Sets the focused task relative to the currently focused task.
- *
- * @param forward whether to go to the next task in the stack (along the curve) or the previous
- * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
- * if the currently focused task is not a stack task, will set the focus
- * to the first visible stack task
- * @param animated determines whether to actually draw the highlight along with the change in
- * focus.
- * @param cancelWindowAnimations if set, will attempt to cancel window animations if a scroll
- * happens.
- * @param timerIndicatorDuration the duration to initialize the auto-advance timer indicator
- */
- public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated,
- boolean cancelWindowAnimations, int timerIndicatorDuration) {
- Task focusedTask = getFocusedTask();
- int newIndex = mStack.indexOfTask(focusedTask);
- if (focusedTask != null) {
- if (stackTasksOnly) {
- List<Task> tasks = mStack.getTasks();
- // Try the next task if it is a stack task
- int tmpNewIndex = newIndex + (forward ? -1 : 1);
- if (0 <= tmpNewIndex && tmpNewIndex < tasks.size()) {
- newIndex = tmpNewIndex;
- }
- } else {
- // No restrictions, lets just move to the new task (looping forward/backwards if
- // necessary)
- int taskCount = mStack.getTaskCount();
- newIndex = (newIndex + (forward ? -1 : 1) + taskCount) % taskCount;
- }
- } else {
- // We don't have a focused task
- float stackScroll = mStackScroller.getStackScroll();
- ArrayList<Task> tasks = mStack.getTasks();
- int taskCount = tasks.size();
- if (useGridLayout()) {
- // For the grid layout, we directly set focus to the most recently used task
- // no matter we're moving forwards or backwards.
- newIndex = taskCount - 1;
- } else {
- // For the grid layout we pick a proper task to focus, according to the current
- // stack scroll.
- if (forward) {
- // Walk backwards and focus the next task smaller than the current stack scroll
- for (newIndex = taskCount - 1; newIndex >= 0; newIndex--) {
- float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
- if (Float.compare(taskP, stackScroll) <= 0) {
- break;
- }
- }
- } else {
- // Walk forwards and focus the next task larger than the current stack scroll
- for (newIndex = 0; newIndex < taskCount; newIndex++) {
- float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
- if (Float.compare(taskP, stackScroll) >= 0) {
- break;
- }
- }
- }
- }
- }
- if (newIndex != -1) {
- boolean willScroll = setFocusedTask(newIndex, true /* scrollToTask */,
- true /* requestViewFocus */, timerIndicatorDuration);
- if (willScroll && cancelWindowAnimations) {
- // As we iterate to the next/previous task, cancel any current/lagging window
- // transition animations
- EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
- }
- }
- }
-
- /**
- * Resets the focused task.
- */
- public void resetFocusedTask(Task task) {
- if (task != null) {
- TaskView tv = getChildViewForTask(task);
- if (tv != null) {
- tv.setFocusedState(false, false /* requestViewFocus */);
- }
- }
- if (mTaskViewFocusFrame != null) {
- mTaskViewFocusFrame.moveGridTaskViewFocus(null);
- }
- mFocusedTask = null;
- }
-
- /**
- * Returns the focused task.
- */
- public Task getFocusedTask() {
- return mFocusedTask;
- }
-
- /**
- * Returns the accessibility focused task.
- */
- Task getAccessibilityFocusedTask() {
- List<TaskView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
- TaskView tv = taskViews.get(i);
- if (Utilities.isDescendentAccessibilityFocused(tv)) {
- return tv.getTask();
- }
- }
- TaskView frontTv = getFrontMostTaskView();
- if (frontTv != null) {
- return frontTv.getTask();
- }
- return null;
- }
-
- @Override
- public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
- super.onInitializeAccessibilityEvent(event);
- List<TaskView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
- if (taskViewCount > 0) {
- TaskView backMostTask = taskViews.get(0);
- TaskView frontMostTask = taskViews.get(taskViewCount - 1);
- event.setFromIndex(mStack.indexOfTask(backMostTask.getTask()));
- event.setToIndex(mStack.indexOfTask(frontMostTask.getTask()));
- event.setContentDescription(frontMostTask.getTask().title);
- }
- event.setItemCount(mStack.getTaskCount());
-
- int stackHeight = mLayoutAlgorithm.mStackRect.height();
- event.setScrollY((int) (mStackScroller.getStackScroll() * stackHeight));
- event.setMaxScrollY((int) (mLayoutAlgorithm.mMaxScrollP * stackHeight));
- }
-
- @Override
- public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(info);
- List<TaskView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
- if (taskViewCount > 1) {
- // Find the accessibility focused task
- Task focusedTask = getAccessibilityFocusedTask();
- info.setScrollable(true);
- int focusedTaskIndex = mStack.indexOfTask(focusedTask);
- if (focusedTaskIndex > 0 || !mStackActionButtonVisible) {
- info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
- }
- if (0 <= focusedTaskIndex && focusedTaskIndex < mStack.getTaskCount() - 1) {
- info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
- }
- }
- }
-
- @Override
- public CharSequence getAccessibilityClassName() {
- return ScrollView.class.getName();
- }
-
- @Override
- public boolean performAccessibilityAction(int action, Bundle arguments) {
- if (super.performAccessibilityAction(action, arguments)) {
- return true;
- }
- Task focusedTask = getAccessibilityFocusedTask();
- int taskIndex = mStack.indexOfTask(focusedTask);
- if (0 <= taskIndex && taskIndex < mStack.getTaskCount()) {
- switch (action) {
- case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
- setFocusedTask(taskIndex + 1, true /* scrollToTask */, true /* requestViewFocus */,
- 0);
- return true;
- }
- case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
- setFocusedTask(taskIndex - 1, true /* scrollToTask */, true /* requestViewFocus */,
- 0);
- return true;
- }
- }
- }
- return false;
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- return mTouchHandler.onInterceptTouchEvent(ev);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- return mTouchHandler.onTouchEvent(ev);
- }
-
- @Override
- public boolean onGenericMotionEvent(MotionEvent ev) {
- return mTouchHandler.onGenericMotionEvent(ev);
- }
-
- @Override
- public void computeScroll() {
- if (mStackScroller.computeScroll()) {
- // Notify accessibility
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED);
- LegacyRecentsImpl.getTaskLoader().getHighResThumbnailLoader().setFlingingFast(
- mStackScroller.getScrollVelocity() > mFastFlingVelocity);
- }
- if (mDeferredTaskViewLayoutAnimation != null) {
- relayoutTaskViews(mDeferredTaskViewLayoutAnimation);
- mTaskViewsClipDirty = true;
- mDeferredTaskViewLayoutAnimation = null;
- }
- if (mTaskViewsClipDirty) {
- clipTaskViews();
- }
- mLastScrollPPercent = Utilities.clamp(Utilities.unmapRange(mStackScroller.getStackScroll(),
- mLayoutAlgorithm.mMinScrollP, mLayoutAlgorithm.mMaxScrollP), 0, 1);
- }
-
- /**
- * Computes the maximum number of visible tasks and thumbnails. Requires that
- * updateLayoutForStack() is called first.
- */
- public TaskStackLayoutAlgorithm.VisibilityReport computeStackVisibilityReport() {
- return mLayoutAlgorithm.computeStackVisibilityReport(mStack.getTasks());
- }
-
- /**
- * Updates the system insets.
- */
- public void setSystemInsets(Rect systemInsets) {
- boolean changed = false;
- changed |= mStableLayoutAlgorithm.setSystemInsets(systemInsets);
- changed |= mLayoutAlgorithm.setSystemInsets(systemInsets);
- if (changed) {
- requestLayout();
- }
- }
-
- /**
- * This is called with the full window width and height to allow stack view children to
- * perform the full screen transition down.
- */
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- mInMeasureLayout = true;
- int width = MeasureSpec.getSize(widthMeasureSpec);
- int height = MeasureSpec.getSize(heightMeasureSpec);
-
- // Update the stable stack bounds, but only update the current stack bounds if the stable
- // bounds have changed. This is because we may get spurious measures while dragging where
- // our current stack bounds reflect the target drop region.
- mLayoutAlgorithm.getTaskStackBounds(mDisplayRect, new Rect(0, 0, width, height),
- mLayoutAlgorithm.mSystemInsets.top, mLayoutAlgorithm.mSystemInsets.left,
- mLayoutAlgorithm.mSystemInsets.right, mTmpRect);
- if (!mTmpRect.equals(mStableStackBounds)) {
- mStableStackBounds.set(mTmpRect);
- mStackBounds.set(mTmpRect);
- mStableWindowRect.set(0, 0, width, height);
- mWindowRect.set(0, 0, width, height);
- }
-
- // Compute the rects in the stack algorithm
- mStableLayoutAlgorithm.initialize(mDisplayRect, mStableWindowRect, mStableStackBounds);
- mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds);
- updateLayoutAlgorithm(false /* boundScroll */);
-
- // If this is the first layout, then scroll to the front of the stack, then update the
- // TaskViews with the stack so that we can lay them out
- boolean resetToInitialState = (width != mLastWidth || height != mLastHeight)
- && mResetToInitialStateWhenResized;
- if (!mFinishedLayoutAfterStackReload || mInitialState != INITIAL_STATE_UPDATE_NONE
- || resetToInitialState) {
- if (mInitialState != INITIAL_STATE_UPDATE_LAYOUT_ONLY || resetToInitialState) {
- updateToInitialState();
- mResetToInitialStateWhenResized = false;
- }
- if (mFinishedLayoutAfterStackReload) {
- mInitialState = INITIAL_STATE_UPDATE_NONE;
- }
- }
- // If we got the launch-next event before the first layout pass, then re-send it after the
- // initial state has been updated
- if (mLaunchNextAfterFirstMeasure) {
- mLaunchNextAfterFirstMeasure = false;
- EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
- }
-
- // Rebind all the views, including the ignore ones
- bindVisibleTaskViews(mStackScroller.getStackScroll(), false /* ignoreTaskOverrides */);
-
- // Measure each of the TaskViews
- mTmpTaskViews.clear();
- mTmpTaskViews.addAll(getTaskViews());
- mTmpTaskViews.addAll(mViewPool.getViews());
- int taskViewCount = mTmpTaskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
- measureTaskView(mTmpTaskViews.get(i));
- }
- if (mTaskViewFocusFrame != null) {
- mTaskViewFocusFrame.measure();
- }
-
- setMeasuredDimension(width, height);
- mLastWidth = width;
- mLastHeight = height;
- mInMeasureLayout = false;
- }
-
- /**
- * Measures a TaskView.
- */
- private void measureTaskView(TaskView tv) {
- Rect padding = new Rect();
- if (tv.getBackground() != null) {
- tv.getBackground().getPadding(padding);
- }
- mTmpRect.set(mStableLayoutAlgorithm.getTaskRect());
- mTmpRect.union(mLayoutAlgorithm.getTaskRect());
- tv.measure(
- MeasureSpec.makeMeasureSpec(mTmpRect.width() + padding.left + padding.right,
- MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(mTmpRect.height() + padding.top + padding.bottom,
- MeasureSpec.EXACTLY));
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- // Layout each of the TaskViews
- mTmpTaskViews.clear();
- mTmpTaskViews.addAll(getTaskViews());
- mTmpTaskViews.addAll(mViewPool.getViews());
- int taskViewCount = mTmpTaskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
- layoutTaskView(changed, mTmpTaskViews.get(i));
- }
- if (mTaskViewFocusFrame != null) {
- mTaskViewFocusFrame.layout();
- }
-
- if (changed) {
- if (mStackScroller.isScrollOutOfBounds()) {
- mStackScroller.boundScroll();
- }
- }
-
- // Relayout all of the task views including the ignored ones
- relayoutTaskViews(AnimationProps.IMMEDIATE);
- clipTaskViews();
-
- if (!mFinishedLayoutAfterStackReload) {
- // Prepare the task enter animations (this can be called numerous times)
- mInitialState = INITIAL_STATE_UPDATE_NONE;
- onFirstLayout();
-
- if (mStackReloaded) {
- mFinishedLayoutAfterStackReload = true;
- tryStartEnterAnimation();
- }
- }
- }
-
- /**
- * Lays out a TaskView.
- */
- private void layoutTaskView(boolean changed, TaskView tv) {
- if (changed) {
- Rect padding = new Rect();
- if (tv.getBackground() != null) {
- tv.getBackground().getPadding(padding);
- }
- mTmpRect.set(mStableLayoutAlgorithm.getTaskRect());
- mTmpRect.union(mLayoutAlgorithm.getTaskRect());
- tv.cancelTransformAnimation();
- tv.layout(mTmpRect.left - padding.left, mTmpRect.top - padding.top,
- mTmpRect.right + padding.right, mTmpRect.bottom + padding.bottom);
- } else {
- // If the layout has not changed, then just lay it out again in-place
- tv.layout(tv.getLeft(), tv.getTop(), tv.getRight(), tv.getBottom());
- }
- }
-
- /** Handler for the first layout. */
- void onFirstLayout() {
- // Setup the view for the enter animation
- mAnimationHelper.prepareForEnterAnimation();
-
- // Set the task focused state without requesting view focus, and leave the focus animations
- // until after the enter-animation
- RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
-
- // We set the initial focused task view iff the following conditions are satisfied:
- // 1. Recents is showing task views in stack layout.
- // 2. Recents is launched with ALT + TAB.
- boolean setFocusOnFirstLayout = !useGridLayout() || launchState.launchedWithAltTab;
- if (setFocusOnFirstLayout) {
- int focusedTaskIndex = getInitialFocusTaskIndex(launchState, mStack.getTaskCount(),
- useGridLayout());
- if (focusedTaskIndex != -1) {
- setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
- false /* requestViewFocus */);
- }
- }
- updateStackActionButtonVisibility();
- }
-
- public boolean isTouchPointInView(float x, float y, TaskView tv) {
- mTmpRect.set(tv.getLeft(), tv.getTop(), tv.getRight(), tv.getBottom());
- mTmpRect.offset((int) tv.getTranslationX(), (int) tv.getTranslationY());
- return mTmpRect.contains((int) x, (int) y);
- }
-
- /**
- * Returns a non-ignored task in the {@param tasks} list that can be used as an achor when
- * calculating the scroll position before and after a layout change.
- */
- public Task findAnchorTask(List<Task> tasks, MutableBoolean isFrontMostTask) {
- for (int i = tasks.size() - 1; i >= 0; i--) {
- Task task = tasks.get(i);
-
- // Ignore deleting tasks
- if (isIgnoredTask(task)) {
- if (i == tasks.size() - 1) {
- isFrontMostTask.value = true;
- }
- continue;
- }
- return task;
- }
- return null;
- }
-
- /**** TaskStackCallbacks Implementation ****/
-
- @Override
- public void onStackTaskAdded(TaskStack stack, Task newTask) {
- // Update the min/max scroll and animate other task views into their new positions
- updateLayoutAlgorithm(true /* boundScroll */);
-
- // Animate all the tasks into place
- relayoutTaskViews(!mFinishedLayoutAfterStackReload
- ? AnimationProps.IMMEDIATE
- : new AnimationProps(DEFAULT_SYNC_STACK_DURATION, Interpolators.FAST_OUT_SLOW_IN));
- }
-
- /**
- * We expect that the {@link TaskView} associated with the removed task is already hidden.
- */
- @Override
- public void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask,
- AnimationProps animation, boolean fromDockGesture, boolean dismissRecentsIfAllRemoved) {
- if (mFocusedTask == removedTask) {
- resetFocusedTask(removedTask);
- }
-
- // Remove the view associated with this task, we can't rely on updateTransforms
- // to work here because the task is no longer in the list
- TaskView tv = getChildViewForTask(removedTask);
- if (tv != null) {
- mViewPool.returnViewToPool(tv);
- }
-
- // Remove the task from the ignored set
- removeIgnoreTask(removedTask);
-
- // If requested, relayout with the given animation
- if (animation != null) {
- updateLayoutAlgorithm(true /* boundScroll */);
- relayoutTaskViews(animation);
- }
-
- // Update the new front most task's action button
- if (mScreenPinningEnabled && newFrontMostTask != null) {
- TaskView frontTv = getChildViewForTask(newFrontMostTask);
- if (frontTv != null) {
- frontTv.showActionButton(true /* fadeIn */, DEFAULT_SYNC_STACK_DURATION);
- }
- }
-
- // If there are no remaining tasks, then just close recents
- if (mStack.getTaskCount() == 0) {
- if (dismissRecentsIfAllRemoved) {
- EventBus.getDefault().send(new AllTaskViewsDismissedEvent(fromDockGesture
- ? R.string.recents_empty_message
- : R.string.recents_empty_message_dismissed_all));
- } else {
- EventBus.getDefault().send(new ShowEmptyViewEvent());
- }
- }
- }
-
- @Override
- public void onStackTasksRemoved(TaskStack stack) {
- // Reset the focused task
- resetFocusedTask(getFocusedTask());
-
- // Return all the views to the pool
- List<TaskView> taskViews = new ArrayList<>();
- taskViews.addAll(getTaskViews());
- for (int i = taskViews.size() - 1; i >= 0; i--) {
- mViewPool.returnViewToPool(taskViews.get(i));
- }
-
- // Remove all the ignore tasks
- mIgnoreTasks.clear();
-
- // If there are no remaining tasks, then just close recents
- EventBus.getDefault().send(new AllTaskViewsDismissedEvent(
- R.string.recents_empty_message_dismissed_all));
- }
-
- @Override
- public void onStackTasksUpdated(TaskStack stack) {
- if (!mFinishedLayoutAfterStackReload) {
- return;
- }
-
- // Update the layout and immediately layout
- updateLayoutAlgorithm(false /* boundScroll */);
- relayoutTaskViews(AnimationProps.IMMEDIATE);
-
- // Rebind all the task views. This will not trigger new resources to be loaded
- // unless they have actually changed
- List<TaskView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
- TaskView tv = taskViews.get(i);
- bindTaskView(tv, tv.getTask());
- }
- }
-
- /**** ViewPoolConsumer Implementation ****/
-
- @Override
- public TaskView createView(Context context) {
- if (LegacyRecentsImpl.getConfiguration().isGridEnabled) {
- return (GridTaskView) mInflater.inflate(R.layout.recents_grid_task_view, this, false);
- } else {
- return (TaskView) mInflater.inflate(R.layout.recents_task_view, this, false);
- }
- }
-
- @Override
- public void onReturnViewToPool(TaskView tv) {
- final Task task = tv.getTask();
-
- // Unbind the task from the task view
- unbindTaskView(tv, task);
-
- // Reset the view properties and view state
- tv.clearAccessibilityFocus();
- tv.resetViewProperties();
- tv.setFocusedState(false, false /* requestViewFocus */);
- tv.setClipViewInStack(false);
- if (mScreenPinningEnabled) {
- tv.hideActionButton(false /* fadeOut */, 0 /* duration */, false /* scaleDown */, null);
- }
-
- // Detach the view from the hierarchy
- detachViewFromParent(tv);
- // Update the task views list after removing the task view
- updateTaskViewsList();
- }
-
- @Override
- public void onPickUpViewFromPool(TaskView tv, Task task, boolean isNewView) {
- // Find the index where this task should be placed in the stack
- int taskIndex = mStack.indexOfTask(task);
- int insertIndex = findTaskViewInsertIndex(task, taskIndex);
-
- // Add/attach the view to the hierarchy
- if (isNewView) {
- if (mInMeasureLayout) {
- // If we are measuring the layout, then just add the view normally as it will be
- // laid out during the layout pass
- addView(tv, insertIndex);
- } else {
- // Otherwise, this is from a bindVisibleTaskViews() call outside the measure/layout
- // pass, and we should layout the new child ourselves
- ViewGroup.LayoutParams params = tv.getLayoutParams();
- if (params == null) {
- params = generateDefaultLayoutParams();
- }
- addViewInLayout(tv, insertIndex, params, true /* preventRequestLayout */);
- measureTaskView(tv);
- layoutTaskView(true /* changed */, tv);
- }
- } else {
- attachViewToParent(tv, insertIndex, tv.getLayoutParams());
- }
- // Update the task views list after adding the new task view
- updateTaskViewsList();
-
- // Bind the task view to the new task
- bindTaskView(tv, task);
-
- // Set the new state for this view, including the callbacks and view clipping
- tv.setCallbacks(this);
- tv.setTouchEnabled(true);
- tv.setClipViewInStack(true);
- if (mFocusedTask == task) {
- tv.setFocusedState(true, false /* requestViewFocus */);
- if (mStartTimerIndicatorDuration > 0) {
- // The timer indicator couldn't be started before, so start it now
- tv.getHeaderView().startFocusTimerIndicator(mStartTimerIndicatorDuration);
- mStartTimerIndicatorDuration = 0;
- }
- }
-
- // Restore the action button visibility if it is the front most task view
- if (mScreenPinningEnabled && tv.getTask() == mStack.getFrontMostTask()) {
- tv.showActionButton(false /* fadeIn */, 0 /* fadeInDuration */);
- }
- }
-
- @Override
- public boolean hasPreferredData(TaskView tv, Task preferredData) {
- return (tv.getTask() == preferredData);
- }
-
- private void bindTaskView(TaskView tv, Task task) {
- // Rebind the task and request that this task's data be filled into the TaskView
- tv.onTaskBound(task, mTouchExplorationEnabled, mDisplayOrientation, mDisplayRect);
-
- // If the doze trigger has already fired, then update the state for this task view
- if (mUIDozeTrigger.isAsleep() ||
- useGridLayout() || LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- tv.setNoUserInteractionState();
- }
-
- if (task == mPrefetchingTask) {
- task.notifyTaskDataLoaded(task.thumbnail, task.icon);
- } else {
- // Load the task data
- LegacyRecentsImpl.getTaskLoader().loadTaskData(task);
- }
- LegacyRecentsImpl.getTaskLoader().getHighResThumbnailLoader().onTaskVisible(task);
- }
-
- private void unbindTaskView(TaskView tv, Task task) {
- if (task != mPrefetchingTask) {
- // Report that this task's data is no longer being used
- LegacyRecentsImpl.getTaskLoader().unloadTaskData(task);
- }
- LegacyRecentsImpl.getTaskLoader().getHighResThumbnailLoader().onTaskInvisible(task);
- }
-
- private void updatePrefetchingTask(ArrayList<Task> tasks, int frontIndex, int backIndex) {
- Task t = null;
- boolean somethingVisible = frontIndex != -1 && backIndex != -1;
- if (somethingVisible && frontIndex < tasks.size() - 1) {
- t = tasks.get(frontIndex + 1);
- }
- if (mPrefetchingTask != t) {
- if (mPrefetchingTask != null) {
- int index = tasks.indexOf(mPrefetchingTask);
- if (index < backIndex || index > frontIndex) {
- LegacyRecentsImpl.getTaskLoader().unloadTaskData(mPrefetchingTask);
- }
- }
- mPrefetchingTask = t;
- if (t != null) {
- LegacyRecentsImpl.getTaskLoader().loadTaskData(t);
- }
- }
- }
-
- private void clearPrefetchingTask() {
- if (mPrefetchingTask != null) {
- LegacyRecentsImpl.getTaskLoader().unloadTaskData(mPrefetchingTask);
- }
- mPrefetchingTask = null;
- }
-
- /**** TaskViewCallbacks Implementation ****/
-
- @Override
- public void onTaskViewClipStateChanged(TaskView tv) {
- if (!mTaskViewsClipDirty) {
- mTaskViewsClipDirty = true;
- invalidate();
- }
- }
-
- /**** TaskStackLayoutAlgorithm.TaskStackLayoutAlgorithmCallbacks ****/
-
- @Override
- public void onFocusStateChanged(int prevFocusState, int curFocusState) {
- if (mDeferredTaskViewLayoutAnimation == null) {
- mUIDozeTrigger.poke();
- relayoutTaskViewsOnNextFrame(AnimationProps.IMMEDIATE);
- }
- }
-
- /**** TaskStackViewScroller.TaskStackViewScrollerCallbacks ****/
-
- @Override
- public void onStackScrollChanged(float prevScroll, float curScroll, AnimationProps animation) {
- mUIDozeTrigger.poke();
- if (animation != null) {
- relayoutTaskViewsOnNextFrame(animation);
- }
-
- // In grid layout, the stack action button always remains visible.
- if (mEnterAnimationComplete && !useGridLayout()) {
- if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- // Show stack button when user drags down to show older tasks on low ram devices
- if (mStack.getTaskCount() > 0 && !mStackActionButtonVisible
- && mTouchHandler.mIsScrolling && curScroll - prevScroll < 0) {
- // Going up
- EventBus.getDefault().send(
- new ShowStackActionButtonEvent(true /* translate */));
- }
- return;
- }
- if (prevScroll > SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
- curScroll <= SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
- mStack.getTaskCount() > 0) {
- EventBus.getDefault().send(new ShowStackActionButtonEvent(true /* translate */));
- } else if (prevScroll < HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
- curScroll >= HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD) {
- EventBus.getDefault().send(new HideStackActionButtonEvent());
- }
- }
- }
-
- /**** EventBus Events ****/
-
- public final void onBusEvent(PackagesChangedEvent event) {
- // Compute which components need to be removed
- ArraySet<ComponentName> removedComponents = mStack.computeComponentsRemoved(
- event.packageName, event.userId);
-
- // For other tasks, just remove them directly if they no longer exist
- ArrayList<Task> tasks = mStack.getTasks();
- for (int i = tasks.size() - 1; i >= 0; i--) {
- final Task t = tasks.get(i);
- if (removedComponents.contains(t.key.getComponent())) {
- final TaskView tv = getChildViewForTask(t);
- if (tv != null) {
- // For visible children, defer removing the task until after the animation
- tv.dismissTask();
- } else {
- // Otherwise, remove the task from the stack immediately
- mStack.removeTask(t, AnimationProps.IMMEDIATE, false /* fromDockGesture */);
- }
- }
- }
- }
-
- public final void onBusEvent(LaunchTaskEvent event) {
- // Cancel any doze triggers once a task is launched
- mUIDozeTrigger.stopDozing();
- }
-
- public final void onBusEvent(LaunchMostRecentTaskRequestEvent event) {
- if (mStack.getTaskCount() > 0) {
- Task mostRecentTask = mStack.getFrontMostTask();
- launchTask(mostRecentTask);
- }
- }
-
- public final void onBusEvent(ShowStackActionButtonEvent event) {
- mStackActionButtonVisible = true;
- }
-
- public final void onBusEvent(HideStackActionButtonEvent event) {
- mStackActionButtonVisible = false;
- }
-
- public final void onBusEvent(LaunchNextTaskRequestEvent event) {
- if (!mFinishedLayoutAfterStackReload) {
- mLaunchNextAfterFirstMeasure = true;
- return;
- }
-
- if (mStack.getTaskCount() == 0) {
- if (RecentsImpl.getLastPipTime() != -1) {
- EventBus.getDefault().send(new ExpandPipEvent());
- MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK,
- "pip");
- } else {
- // If there are no tasks, then just hide recents back to home.
- EventBus.getDefault().send(new HideRecentsEvent(false, true));
- }
- return;
- }
-
- if (!LegacyRecentsImpl.getConfiguration().getLaunchState().launchedFromPipApp
- && mStack.isNextLaunchTargetPip(RecentsImpl.getLastPipTime())) {
- // If the launch task is in the pinned stack, then expand the PiP now
- EventBus.getDefault().send(new ExpandPipEvent());
- MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK, "pip");
- } else {
- final Task launchTask = mStack.getNextLaunchTarget();
- if (launchTask != null) {
- // Defer launching the task until the PiP menu has been dismissed (if it is
- // showing at all)
- HidePipMenuEvent hideMenuEvent = new HidePipMenuEvent();
- hideMenuEvent.addPostAnimationCallback(() -> {
- launchTask(launchTask);
- });
- EventBus.getDefault().send(hideMenuEvent);
- MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK,
- launchTask.key.getComponent().toString());
- }
- }
- }
-
- public final void onBusEvent(LaunchTaskStartedEvent event) {
- mAnimationHelper.startLaunchTaskAnimation(event.taskView, event.screenPinningRequested,
- event.getAnimationTrigger());
- }
-
- public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
- // Stop any scrolling
- mTouchHandler.cancelNonDismissTaskAnimations();
- mStackScroller.stopScroller();
- mStackScroller.stopBoundScrollAnimation();
- cancelDeferredTaskViewLayoutAnimation();
-
- // Start the task animations
- mAnimationHelper.startExitToHomeAnimation(event.animated, event.getAnimationTrigger());
-
- // Dismiss the grid task view focus frame
- if (mTaskViewFocusFrame != null) {
- mTaskViewFocusFrame.moveGridTaskViewFocus(null);
- }
- }
-
- public final void onBusEvent(DismissFocusedTaskViewEvent event) {
- if (mFocusedTask != null) {
- if (mTaskViewFocusFrame != null) {
- mTaskViewFocusFrame.moveGridTaskViewFocus(null);
- }
- TaskView tv = getChildViewForTask(mFocusedTask);
- if (tv != null) {
- tv.dismissTask();
- }
- resetFocusedTask(mFocusedTask);
- }
- }
-
- public final void onBusEvent(DismissTaskViewEvent event) {
- // For visible children, defer removing the task until after the animation
- mAnimationHelper.startDeleteTaskAnimation(
- event.taskView, useGridLayout(), event.getAnimationTrigger());
- }
-
- public final void onBusEvent(final DismissAllTaskViewsEvent event) {
- // Keep track of the tasks which will have their data removed
- ArrayList<Task> tasks = new ArrayList<>(mStack.getTasks());
- mAnimationHelper.startDeleteAllTasksAnimation(
- getTaskViews(), useGridLayout(), event.getAnimationTrigger());
- event.addPostAnimationCallback(new Runnable() {
- @Override
- public void run() {
- // Announce for accessibility
- announceForAccessibility(getContext().getString(
- R.string.accessibility_recents_all_items_dismissed));
-
- // Remove all tasks and delete the task data for all tasks
- mStack.removeAllTasks(true /* notifyStackChanges */);
- for (int i = tasks.size() - 1; i >= 0; i--) {
- EventBus.getDefault().send(new DeleteTaskDataEvent(tasks.get(i)));
- }
-
- MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_DISMISS_ALL);
- }
- });
-
- }
-
- public final void onBusEvent(TaskViewDismissedEvent event) {
- // Announce for accessibility
- announceForAccessibility(getContext().getString(
- R.string.accessibility_recents_item_dismissed, event.task.title));
-
- if (useGridLayout() && event.animation != null) {
- event.animation.setListener(new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animator) {
- if (mTaskViewFocusFrame != null) {
- // Resize the grid layout task view focus frame
- mTaskViewFocusFrame.resize();
- }
- }
- });
- }
-
- // Remove the task from the stack
- mStack.removeTask(event.task, event.animation, false /* fromDockGesture */);
- EventBus.getDefault().send(new DeleteTaskDataEvent(event.task));
- if (mStack.getTaskCount() > 0 && LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
- }
-
- MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_DISMISS,
- event.task.key.getComponent().toString());
- }
-
- public final void onBusEvent(FocusNextTaskViewEvent event) {
- // Stop any scrolling
- mStackScroller.stopScroller();
- mStackScroller.stopBoundScrollAnimation();
-
- setRelativeFocusedTask(true, false /* stackTasksOnly */, true /* animated */, false, 0);
- }
-
- public final void onBusEvent(FocusPreviousTaskViewEvent event) {
- // Stop any scrolling
- mStackScroller.stopScroller();
- mStackScroller.stopBoundScrollAnimation();
-
- setRelativeFocusedTask(false, false /* stackTasksOnly */, true /* animated */);
- }
-
- public final void onBusEvent(NavigateTaskViewEvent event) {
- if (useGridLayout()) {
- final int taskCount = mStack.getTaskCount();
- final int currentIndex = mStack.indexOfTask(getFocusedTask());
- final int nextIndex = mLayoutAlgorithm.mTaskGridLayoutAlgorithm.navigateFocus(taskCount,
- currentIndex, event.direction);
- setFocusedTask(nextIndex, false, true);
- } else {
- switch (event.direction) {
- case UP:
- EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
- break;
- case DOWN:
- EventBus.getDefault().send(new FocusNextTaskViewEvent());
- break;
- }
- }
- }
-
- public final void onBusEvent(UserInteractionEvent event) {
- // Poke the doze trigger on user interaction
- mUIDozeTrigger.poke();
-
- RecentsDebugFlags debugFlags = LegacyRecentsImpl.getDebugFlags();
- if (mFocusedTask != null) {
- TaskView tv = getChildViewForTask(mFocusedTask);
- if (tv != null) {
- tv.getHeaderView().cancelFocusTimerIndicator();
- }
- }
- }
-
- public final void onBusEvent(DragStartEvent event) {
- // Ensure that the drag task is not animated
- addIgnoreTask(event.task);
-
- // Enlarge the dragged view slightly
- float finalScale = event.taskView.getScaleX() * DRAG_SCALE_FACTOR;
- mLayoutAlgorithm.getStackTransform(event.task, getScroller().getStackScroll(),
- mTmpTransform, null);
- mTmpTransform.scale = finalScale;
- mTmpTransform.translationZ = mLayoutAlgorithm.mMaxTranslationZ + 1;
- mTmpTransform.dimAlpha = 0f;
- updateTaskViewToTransform(event.taskView, mTmpTransform,
- new AnimationProps(DRAG_SCALE_DURATION, Interpolators.FAST_OUT_SLOW_IN));
- }
-
- public final void onBusEvent(DragDropTargetChangedEvent event) {
- AnimationProps animation = new AnimationProps(SLOW_SYNC_STACK_DURATION,
- Interpolators.FAST_OUT_SLOW_IN);
- boolean ignoreTaskOverrides = false;
- if (event.dropTarget instanceof DockState) {
- // Calculate the new task stack bounds that matches the window size that Recents will
- // have after the drop
- final DockState dockState = (DockState) event.dropTarget;
- Rect systemInsets = new Rect(mStableLayoutAlgorithm.mSystemInsets);
- // When docked, the nav bar insets are consumed and the activity is measured without
- // insets. However, the window bounds include the insets, so we need to subtract them
- // here to make them identical.
- int height = getMeasuredHeight();
- height -= systemInsets.bottom;
- systemInsets.bottom = 0;
- mStackBounds.set(dockState.getDockedTaskStackBounds(mDisplayRect, getMeasuredWidth(),
- height, mDividerSize, systemInsets,
- mLayoutAlgorithm, getResources(), mWindowRect));
- mLayoutAlgorithm.setSystemInsets(systemInsets);
- mLayoutAlgorithm.initialize(mDisplayRect, mWindowRect, mStackBounds);
- updateLayoutAlgorithm(true /* boundScroll */);
- ignoreTaskOverrides = true;
- } else {
- // Restore the pre-drag task stack bounds, but ensure that we don't layout the dragging
- // task view, so add it back to the ignore set after updating the layout
- removeIgnoreTask(event.task);
- updateLayoutToStableBounds();
- addIgnoreTask(event.task);
- }
- relayoutTaskViews(animation, null /* animationOverrides */, ignoreTaskOverrides);
- }
-
- public final void onBusEvent(final DragEndEvent event) {
- // We don't handle drops on the dock regions
- if (event.dropTarget instanceof DockState) {
- // However, we do need to reset the overrides, since the last state of this task stack
- // view layout was ignoring task overrides (see DragDropTargetChangedEvent handler)
- mLayoutAlgorithm.clearUnfocusedTaskOverrides();
- return;
- }
-
- // Restore the task, so that relayout will apply to it below
- removeIgnoreTask(event.task);
-
- // Convert the dragging task view back to its final layout-space rect
- Utilities.setViewFrameFromTranslation(event.taskView);
-
- // Animate all the tasks into place
- ArrayMap<Task, AnimationProps> animationOverrides = new ArrayMap<>();
- animationOverrides.put(event.task, new AnimationProps(SLOW_SYNC_STACK_DURATION,
- Interpolators.FAST_OUT_SLOW_IN,
- event.getAnimationTrigger().decrementOnAnimationEnd()));
- relayoutTaskViews(new AnimationProps(SLOW_SYNC_STACK_DURATION,
- Interpolators.FAST_OUT_SLOW_IN));
- event.getAnimationTrigger().increment();
- }
-
- public final void onBusEvent(final DragEndCancelledEvent event) {
- // Restore the pre-drag task stack bounds, including the dragging task view
- removeIgnoreTask(event.task);
- updateLayoutToStableBounds();
-
- // Convert the dragging task view back to its final layout-space rect
- Utilities.setViewFrameFromTranslation(event.taskView);
-
- // Animate all the tasks into place
- ArrayMap<Task, AnimationProps> animationOverrides = new ArrayMap<>();
- animationOverrides.put(event.task, new AnimationProps(SLOW_SYNC_STACK_DURATION,
- Interpolators.FAST_OUT_SLOW_IN,
- event.getAnimationTrigger().decrementOnAnimationEnd()));
- relayoutTaskViews(new AnimationProps(SLOW_SYNC_STACK_DURATION,
- Interpolators.FAST_OUT_SLOW_IN));
- event.getAnimationTrigger().increment();
- }
-
- public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
- mEnterAnimationComplete = true;
- tryStartEnterAnimation();
- }
-
- private void tryStartEnterAnimation() {
- if (!mStackReloaded || !mFinishedLayoutAfterStackReload || !mEnterAnimationComplete) {
- return;
- }
-
- if (mStack.getTaskCount() > 0) {
- // Start the task enter animations
- ReferenceCountedTrigger trigger = new ReferenceCountedTrigger();
- mAnimationHelper.startEnterAnimation(trigger);
-
- // Add a runnable to the post animation ref counter to clear all the views
- trigger.addLastDecrementRunnable(() -> {
- // Start the dozer to trigger to trigger any UI that shows after a timeout
- mUIDozeTrigger.startDozing();
-
- // Update the focused state here -- since we only set the focused task without
- // requesting view focus in onFirstLayout(), actually request view focus and
- // animate the focused state if we are alt-tabbing now, after the window enter
- // animation is completed
- if (mFocusedTask != null) {
- RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- setFocusedTask(mStack.indexOfTask(mFocusedTask),
- false /* scrollToTask */, launchState.launchedWithAltTab);
- TaskView focusedTaskView = getChildViewForTask(mFocusedTask);
- if (mTouchExplorationEnabled && focusedTaskView != null) {
- focusedTaskView.requestAccessibilityFocus();
- }
- }
- });
- }
-
- // This flag is only used to choreograph the enter animation, so we can reset it here
- mStackReloaded = false;
- }
-
- public final void onBusEvent(final MultiWindowStateChangedEvent event) {
- if (event.inMultiWindow || !event.showDeferredAnimation) {
- setTasks(event.stack, true /* allowNotifyStackChanges */);
- } else {
- // Reset the launch state before handling the multiwindow change
- RecentsActivityLaunchState launchState = LegacyRecentsImpl.getConfiguration().getLaunchState();
- launchState.reset();
-
- // Defer until the next frame to ensure that we have received all the system insets, and
- // initial layout updates
- event.getAnimationTrigger().increment();
- post(new Runnable() {
- @Override
- public void run() {
- // Scroll the stack to the front to see the undocked task
- mAnimationHelper.startNewStackScrollAnimation(event.stack,
- event.getAnimationTrigger());
- event.getAnimationTrigger().decrement();
- }
- });
- }
- }
-
- public final void onBusEvent(ConfigurationChangedEvent event) {
- if (event.fromDeviceOrientationChange) {
- mDisplayOrientation = Utilities.getAppConfiguration(mContext).orientation;
- mDisplayRect = LegacyRecentsImpl.getSystemServices().getDisplayRect();
-
- // Always stop the scroller, otherwise, we may continue setting the stack scroll to the
- // wrong bounds in the new layout
- mStackScroller.stopScroller();
- }
- reloadOnConfigurationChange();
-
- // Notify the task views of the configuration change so they can reload their resources
- if (!event.fromMultiWindow) {
- mTmpTaskViews.clear();
- mTmpTaskViews.addAll(getTaskViews());
- mTmpTaskViews.addAll(mViewPool.getViews());
- int taskViewCount = mTmpTaskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
- mTmpTaskViews.get(i).onConfigurationChanged();
- }
- }
-
- // Update the Clear All button in case we're switching in or out of grid layout.
- updateStackActionButtonVisibility();
-
- // Trigger a new layout and update to the initial state if necessary. When entering split
- // screen, the multi-window configuration change event can happen after the stack is already
- // reloaded (but pending measure/layout), in this case, do not override the intiial state
- // and just wait for the upcoming measure/layout pass.
- if (event.fromMultiWindow && mInitialState == INITIAL_STATE_UPDATE_NONE) {
- mInitialState = INITIAL_STATE_UPDATE_LAYOUT_ONLY;
- requestLayout();
- } else if (event.fromDeviceOrientationChange) {
- mInitialState = INITIAL_STATE_UPDATE_ALL;
- requestLayout();
- }
- }
-
- public final void onBusEvent(RecentsGrowingEvent event) {
- mResetToInitialStateWhenResized = true;
- }
-
- public final void onBusEvent(RecentsVisibilityChangedEvent event) {
- if (!event.visible) {
- if (mTaskViewFocusFrame != null) {
- mTaskViewFocusFrame.moveGridTaskViewFocus(null);
- }
-
- List<TaskView> taskViews = new ArrayList<>(getTaskViews());
- for (int i = 0; i < taskViews.size(); i++) {
- mViewPool.returnViewToPool(taskViews.get(i));
- }
- clearPrefetchingTask();
-
- // We can not reset mEnterAnimationComplete in onReload() because when docking the top
- // task, we can receive the enter animation callback before onReload(), so reset it
- // here onces Recents is not visible
- mEnterAnimationComplete = false;
- }
- }
-
- public final void onBusEvent(ActivityPinnedEvent event) {
- // If an activity enters PiP while Recents is open, remove the stack task associated with
- // the new PiP task
- Task removeTask = mStack.findTaskWithId(event.taskId);
- if (removeTask != null) {
- // In this case, we remove the task, but if the last task is removed, don't dismiss
- // Recents to home
- mStack.removeTask(removeTask, AnimationProps.IMMEDIATE, false /* fromDockGesture */,
- false /* dismissRecentsIfAllRemoved */);
- }
- updateLayoutAlgorithm(false /* boundScroll */);
- updateToInitialState();
- }
-
- public void reloadOnConfigurationChange() {
- mStableLayoutAlgorithm.reloadOnConfigurationChange(getContext());
- mLayoutAlgorithm.reloadOnConfigurationChange(getContext());
- }
-
- /**
- * Returns the insert index for the task in the current set of task views. If the given task
- * is already in the task view list, then this method returns the insert index assuming it
- * is first removed at the previous index.
- *
- * @param task the task we are finding the index for
- * @param taskIndex the index of the task in the stack
- */
- private int findTaskViewInsertIndex(Task task, int taskIndex) {
- if (taskIndex != -1) {
- List<TaskView> taskViews = getTaskViews();
- boolean foundTaskView = false;
- int taskViewCount = taskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
- Task tvTask = taskViews.get(i).getTask();
- if (tvTask == task) {
- foundTaskView = true;
- } else if (taskIndex < mStack.indexOfTask(tvTask)) {
- if (foundTaskView) {
- return i - 1;
- } else {
- return i;
- }
- }
- }
- }
- return -1;
- }
-
- private void launchTask(Task task) {
- // Stop all animations
- cancelAllTaskViewAnimations();
-
- float curScroll = mStackScroller.getStackScroll();
- float targetScroll = mLayoutAlgorithm.getStackScrollForTaskAtInitialOffset(task);
- float absScrollDiff = Math.abs(targetScroll - curScroll);
- if (getChildViewForTask(task) == null || absScrollDiff > 0.35f) {
- int duration = (int) (LAUNCH_NEXT_SCROLL_BASE_DURATION +
- absScrollDiff * LAUNCH_NEXT_SCROLL_INCR_DURATION);
- mStackScroller.animateScroll(targetScroll,
- duration, new Runnable() {
- @Override
- public void run() {
- EventBus.getDefault().send(new LaunchTaskEvent(
- getChildViewForTask(task), task, null,
- false /* screenPinningRequested */));
- }
- });
- } else {
- EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(task), task, null,
- false /* screenPinningRequested */));
- }
- }
-
- /**
- * Check whether we should use the grid layout.
- */
- public boolean useGridLayout() {
- return mLayoutAlgorithm.useGridLayout();
- }
-
- /**
- * Reads current system flags related to accessibility and screen pinning.
- */
- private void readSystemFlags() {
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
- mScreenPinningEnabled = ActivityManagerWrapper.getInstance().isScreenPinningEnabled()
- && !ActivityManagerWrapper.getInstance().isLockToAppActive();
- }
-
- private void updateStackActionButtonVisibility() {
- if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- return;
- }
-
- // Always show the button in grid layout.
- if (useGridLayout() ||
- (mStackScroller.getStackScroll() < SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
- mStack.getTaskCount() > 0)) {
- EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
- } else {
- EventBus.getDefault().send(new HideStackActionButtonEvent());
- }
- }
-
- /**
- * Returns the task to focus given the current launch state.
- */
- private int getInitialFocusTaskIndex(RecentsActivityLaunchState launchState, int numTasks,
- boolean useGridLayout) {
- if (launchState.launchedFromApp) {
- if (useGridLayout) {
- // If coming from another app to the grid layout, focus the front most task
- return numTasks - 1;
- }
-
- // If coming from another app, focus the next task
- return Math.max(0, numTasks - 2);
- } else {
- // If coming from home, focus the front most task
- return numTasks - 1;
- }
- }
-
- /**
- * Updates {@param transforms} to be the same size as {@param tasks}.
- */
- private void matchTaskListSize(List<Task> tasks, List<TaskViewTransform> transforms) {
- // We can reuse the task transforms where possible to reduce object allocation
- int taskTransformCount = transforms.size();
- int taskCount = tasks.size();
- if (taskTransformCount < taskCount) {
- // If there are less transforms than tasks, then add as many transforms as necessary
- for (int i = taskTransformCount; i < taskCount; i++) {
- transforms.add(new TaskViewTransform());
- }
- } else if (taskTransformCount > taskCount) {
- // If there are more transforms than tasks, then just subset the transform list
- transforms.subList(taskCount, taskTransformCount).clear();
- }
- }
-
- public void dump(String prefix, PrintWriter writer) {
- String innerPrefix = prefix + " ";
- String id = Integer.toHexString(System.identityHashCode(this));
-
- writer.print(prefix); writer.print(TAG);
- writer.print(" hasDefRelayout=");
- writer.print(mDeferredTaskViewLayoutAnimation != null ? "Y" : "N");
- writer.print(" clipDirty="); writer.print(mTaskViewsClipDirty ? "Y" : "N");
- writer.print(" awaitingStackReload="); writer.print(mFinishedLayoutAfterStackReload ? "Y" : "N");
- writer.print(" initialState="); writer.print(mInitialState);
- writer.print(" inMeasureLayout="); writer.print(mInMeasureLayout ? "Y" : "N");
- writer.print(" enterAnimCompleted="); writer.print(mEnterAnimationComplete ? "Y" : "N");
- writer.print(" touchExplorationOn="); writer.print(mTouchExplorationEnabled ? "Y" : "N");
- writer.print(" screenPinningOn="); writer.print(mScreenPinningEnabled ? "Y" : "N");
- writer.print(" numIgnoreTasks="); writer.print(mIgnoreTasks.size());
- writer.print(" numViewPool="); writer.print(mViewPool.getViews().size());
- writer.print(" stableStackBounds="); writer.print(
- Utilities.dumpRect(mStableStackBounds));
- writer.print(" stackBounds="); writer.print(
- Utilities.dumpRect(mStackBounds));
- writer.print(" stableWindow="); writer.print(
- Utilities.dumpRect(mStableWindowRect));
- writer.print(" window="); writer.print(Utilities.dumpRect(mWindowRect));
- writer.print(" display="); writer.print(Utilities.dumpRect(mDisplayRect));
- writer.print(" orientation="); writer.print(mDisplayOrientation);
- writer.print(" [0x"); writer.print(id); writer.print("]");
- writer.println();
-
- if (mFocusedTask != null) {
- writer.print(innerPrefix);
- writer.print("Focused task: ");
- mFocusedTask.dump("", writer);
- }
-
- int numTaskViews = mTaskViews.size();
- for (int i = 0; i < numTaskViews; i++) {
- mTaskViews.get(i).dump(innerPrefix, writer);
- }
-
- mLayoutAlgorithm.dump(innerPrefix, writer);
- mStackScroller.dump(innerPrefix, writer);
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackViewScroller.java
deleted file mode 100644
index 42efe59da54c..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ /dev/null
@@ -1,347 +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 com.android.systemui.recents.views;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.util.FloatProperty;
-import android.util.Log;
-import android.util.Property;
-import android.view.ViewConfiguration;
-import android.view.ViewDebug;
-import android.widget.OverScroller;
-
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.recents.LegacyRecentsImpl;
-import com.android.systemui.recents.utilities.AnimationProps;
-import com.android.systemui.recents.utilities.Utilities;
-import com.android.systemui.recents.views.lowram.TaskStackLowRamLayoutAlgorithm;
-import com.android.systemui.statusbar.FlingAnimationUtils;
-
-import java.io.PrintWriter;
-
-/* The scrolling logic for a TaskStackView */
-public class TaskStackViewScroller {
-
- private static final String TAG = "TaskStackViewScroller";
- private static final boolean DEBUG = false;
-
- public interface TaskStackViewScrollerCallbacks {
- void onStackScrollChanged(float prevScroll, float curScroll, AnimationProps animation);
- }
-
- /**
- * A Property wrapper around the <code>stackScroll</code> functionality handled by the
- * {@link #setStackScroll(float)} and
- * {@link #getStackScroll()} methods.
- */
- private static final Property<TaskStackViewScroller, Float> STACK_SCROLL =
- new FloatProperty<TaskStackViewScroller>("stackScroll") {
- @Override
- public void setValue(TaskStackViewScroller object, float value) {
- object.setStackScroll(value);
- }
-
- @Override
- public Float get(TaskStackViewScroller object) {
- return object.getStackScroll();
- }
- };
-
- Context mContext;
- TaskStackLayoutAlgorithm mLayoutAlgorithm;
- TaskStackViewScrollerCallbacks mCb;
-
- @ViewDebug.ExportedProperty(category="recents")
- float mStackScrollP;
- @ViewDebug.ExportedProperty(category="recents")
- float mLastDeltaP = 0f;
- float mFlingDownScrollP;
- int mFlingDownY;
-
- OverScroller mScroller;
- ObjectAnimator mScrollAnimator;
- float mFinalAnimatedScroll;
-
- final FlingAnimationUtils mFlingAnimationUtils;
-
- public TaskStackViewScroller(Context context, TaskStackViewScrollerCallbacks cb,
- TaskStackLayoutAlgorithm layoutAlgorithm) {
- mContext = context;
- mCb = cb;
- mScroller = new OverScroller(context);
- if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- mScroller.setFriction(0.06f);
- }
- mLayoutAlgorithm = layoutAlgorithm;
- mFlingAnimationUtils = new FlingAnimationUtils(context, 0.3f);
- }
-
- /** Resets the task scroller. */
- void reset() {
- mStackScrollP = 0f;
- mLastDeltaP = 0f;
- }
-
- void resetDeltaScroll() {
- mLastDeltaP = 0f;
- }
-
- /** Gets the current stack scroll */
- public float getStackScroll() {
- return mStackScrollP;
- }
-
- /**
- * Sets the current stack scroll immediately.
- */
- public void setStackScroll(float s) {
- setStackScroll(s, AnimationProps.IMMEDIATE);
- }
-
- /**
- * Sets the current stack scroll immediately, and returns the difference between the target
- * scroll and the actual scroll after accounting for the effect on the focus state.
- */
- public float setDeltaStackScroll(float downP, float deltaP) {
- float targetScroll = downP + deltaP;
- float newScroll = mLayoutAlgorithm.updateFocusStateOnScroll(downP + mLastDeltaP, targetScroll,
- mStackScrollP);
- setStackScroll(newScroll, AnimationProps.IMMEDIATE);
- mLastDeltaP = deltaP;
- return newScroll - targetScroll;
- }
-
- /**
- * Sets the current stack scroll, but indicates to the callback the preferred animation to
- * update to this new scroll.
- */
- public void setStackScroll(float newScroll, AnimationProps animation) {
- float prevScroll = mStackScrollP;
- mStackScrollP = newScroll;
- if (mCb != null) {
- mCb.onStackScrollChanged(prevScroll, mStackScrollP, animation);
- }
- }
-
- /**
- * Sets the current stack scroll to the initial state when you first enter recents.
- * @return whether the stack progress changed.
- */
- public boolean setStackScrollToInitialState() {
- float prevScroll = mStackScrollP;
- setStackScroll(mLayoutAlgorithm.mInitialScrollP);
- return Float.compare(prevScroll, mStackScrollP) != 0;
- }
-
- /**
- * Starts a fling that is coordinated with the {@link TaskStackViewTouchHandler}.
- */
- public void fling(float downScrollP, int downY, int y, int velY, int minY, int maxY,
- int overscroll) {
- if (DEBUG) {
- Log.d(TAG, "fling: " + downScrollP + ", downY: " + downY + ", y: " + y +
- ", velY: " + velY + ", minY: " + minY + ", maxY: " + maxY);
- }
- mFlingDownScrollP = downScrollP;
- mFlingDownY = downY;
- mScroller.fling(0, y, 0, velY, 0, 0, minY, maxY, 0, overscroll);
- }
-
- /** Bounds the current scroll if necessary */
- public boolean boundScroll() {
- float curScroll = getStackScroll();
- float newScroll = getBoundedStackScroll(curScroll);
- if (Float.compare(newScroll, curScroll) != 0) {
- setStackScroll(newScroll);
- return true;
- }
- return false;
- }
-
- /** Returns the bounded stack scroll */
- float getBoundedStackScroll(float scroll) {
- return Utilities.clamp(scroll, mLayoutAlgorithm.mMinScrollP, mLayoutAlgorithm.mMaxScrollP);
- }
-
- /** Returns the amount that the absolute value of how much the scroll is out of bounds. */
- float getScrollAmountOutOfBounds(float scroll) {
- if (scroll < mLayoutAlgorithm.mMinScrollP) {
- return Math.abs(scroll - mLayoutAlgorithm.mMinScrollP);
- } else if (scroll > mLayoutAlgorithm.mMaxScrollP) {
- return Math.abs(scroll - mLayoutAlgorithm.mMaxScrollP);
- }
- return 0f;
- }
-
- /** Returns whether the specified scroll is out of bounds */
- boolean isScrollOutOfBounds() {
- return Float.compare(getScrollAmountOutOfBounds(mStackScrollP), 0f) != 0;
- }
-
- /**
- * Scrolls the closest task and snaps into place. Only used in recents for low ram devices.
- * @param velocity of scroll
- */
- void scrollToClosestTask(int velocity) {
- float stackScroll = getStackScroll();
-
- // Skip if not in low ram layout and if the scroll is out of min and max bounds
- if (!LegacyRecentsImpl.getConfiguration().isLowRamDevice || stackScroll < mLayoutAlgorithm.mMinScrollP
- || stackScroll > mLayoutAlgorithm.mMaxScrollP) {
- return;
- }
- TaskStackLowRamLayoutAlgorithm algorithm = mLayoutAlgorithm.mTaskStackLowRamLayoutAlgorithm;
-
- float flingThreshold = ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity();
- if (Math.abs(velocity) > flingThreshold) {
- int minY = algorithm.percentageToScroll(mLayoutAlgorithm.mMinScrollP);
- int maxY = algorithm.percentageToScroll(mLayoutAlgorithm.mMaxScrollP);
-
- // Calculate the fling and snap to closest task from final y position, computeScroll()
- // never runs when cancelled with animateScroll() and the overscroll is not calculated
- // here
- fling(0 /* downScrollP */, 0 /* downY */, algorithm.percentageToScroll(stackScroll),
- -velocity, minY, maxY, 0 /* overscroll */);
- float pos = algorithm.scrollToPercentage(mScroller.getFinalY());
-
- float newScrollP = algorithm.getClosestTaskP(pos, mLayoutAlgorithm.mNumStackTasks,
- velocity);
- ValueAnimator animator = ObjectAnimator.ofFloat(stackScroll, newScrollP);
- mFlingAnimationUtils.apply(animator, algorithm.percentageToScroll(stackScroll),
- algorithm.percentageToScroll(newScrollP), velocity);
- animateScroll(newScrollP, (int) animator.getDuration(), animator.getInterpolator(),
- null /* postRunnable */);
- } else {
- float newScrollP = algorithm.getClosestTaskP(stackScroll,
- mLayoutAlgorithm.mNumStackTasks, velocity);
- animateScroll(newScrollP, 300, Interpolators.ACCELERATE_DECELERATE,
- null /* postRunnable */);
- }
- }
-
- /** Animates the stack scroll into bounds */
- ObjectAnimator animateBoundScroll() {
- // TODO: Take duration for snap back
- float curScroll = getStackScroll();
- float newScroll = getBoundedStackScroll(curScroll);
- if (Float.compare(newScroll, curScroll) != 0) {
- // Start a new scroll animation
- animateScroll(newScroll, null /* postScrollRunnable */);
- }
- return mScrollAnimator;
- }
-
- /** Animates the stack scroll */
- void animateScroll(float newScroll, final Runnable postRunnable) {
- int duration = mContext.getResources().getInteger(
- R.integer.recents_animate_task_stack_scroll_duration);
- animateScroll(newScroll, duration, postRunnable);
- }
-
- /** Animates the stack scroll */
- void animateScroll(float newScroll, int duration, final Runnable postRunnable) {
- animateScroll(newScroll, duration, Interpolators.LINEAR_OUT_SLOW_IN, postRunnable);
- }
-
- /** Animates the stack scroll with time interpolator */
- void animateScroll(float newScroll, int duration, TimeInterpolator interpolator,
- final Runnable postRunnable) {
- ObjectAnimator an = ObjectAnimator.ofFloat(this, STACK_SCROLL, getStackScroll(), newScroll);
- an.setDuration(duration);
- an.setInterpolator(interpolator);
- animateScroll(newScroll, an, postRunnable);
- }
-
- /** Animates the stack scroll with animator */
- private void animateScroll(float newScroll, ObjectAnimator animator,
- final Runnable postRunnable) {
- // Finish any current scrolling animations
- if (mScrollAnimator != null && mScrollAnimator.isRunning()) {
- setStackScroll(mFinalAnimatedScroll);
- mScroller.forceFinished(true);
- }
- stopScroller();
- stopBoundScrollAnimation();
-
- if (Float.compare(mStackScrollP, newScroll) != 0) {
- mFinalAnimatedScroll = newScroll;
- mScrollAnimator = animator;
- mScrollAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (postRunnable != null) {
- postRunnable.run();
- }
- mScrollAnimator.removeAllListeners();
- }
- });
- mScrollAnimator.start();
- } else {
- if (postRunnable != null) {
- postRunnable.run();
- }
- }
- }
-
- /** Aborts any current stack scrolls */
- void stopBoundScrollAnimation() {
- Utilities.cancelAnimationWithoutCallbacks(mScrollAnimator);
- }
-
- /**** OverScroller ****/
-
- /** Called from the view draw, computes the next scroll. */
- boolean computeScroll() {
- if (mScroller.computeScrollOffset()) {
- float deltaP = mLayoutAlgorithm.getDeltaPForY(mFlingDownY, mScroller.getCurrY());
- mFlingDownScrollP += setDeltaStackScroll(mFlingDownScrollP, deltaP);
- if (DEBUG) {
- Log.d(TAG, "computeScroll: " + (mFlingDownScrollP + deltaP));
- }
- return true;
- }
- return false;
- }
-
- /** Returns whether the overscroller is scrolling. */
- boolean isScrolling() {
- return !mScroller.isFinished();
- }
-
- float getScrollVelocity() {
- return mScroller.getCurrVelocity();
- }
-
- /** Stops the scroller and any current fling. */
- void stopScroller() {
- if (!mScroller.isFinished()) {
- mScroller.abortAnimation();
- }
- }
-
- public void dump(String prefix, PrintWriter writer) {
- writer.print(prefix); writer.print(TAG);
- writer.print(" stackScroll:"); writer.print(mStackScrollP);
- writer.println();
- }
-} \ No newline at end of file
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
deleted file mode 100644
index a7fb4fae09ec..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ /dev/null
@@ -1,706 +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 com.android.systemui.recents.views;
-
-import android.animation.Animator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Path;
-import android.util.ArrayMap;
-import android.util.MutableBoolean;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewDebug;
-import android.view.ViewParent;
-import android.view.animation.Interpolator;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.SwipeHelper;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.recents.Constants;
-import com.android.systemui.recents.LegacyRecentsImpl;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.HideRecentsEvent;
-import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
-import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
-import com.android.systemui.recents.misc.FreePathInterpolator;
-import com.android.systemui.recents.utilities.AnimationProps;
-import com.android.systemui.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.statusbar.FlingAnimationUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Handles touch events for a TaskStackView.
- */
-class TaskStackViewTouchHandler implements SwipeHelper.Callback {
-
- private static final int INACTIVE_POINTER_ID = -1;
- private static final float CHALLENGING_SWIPE_ESCAPE_VELOCITY = 800f; // dp/sec
- // The min overscroll is the amount of task progress overscroll we want / the max overscroll
- // curve value below
- private static final float MAX_OVERSCROLL = 0.7f / 0.3f;
- private static final Interpolator OVERSCROLL_INTERP;
- static {
- Path OVERSCROLL_PATH = new Path();
- OVERSCROLL_PATH.moveTo(0, 0);
- OVERSCROLL_PATH.cubicTo(0.2f, 0.175f, 0.25f, 0.3f, 1f, 0.3f);
- OVERSCROLL_INTERP = new FreePathInterpolator(OVERSCROLL_PATH);
- }
-
- Context mContext;
- TaskStackView mSv;
- TaskStackViewScroller mScroller;
- VelocityTracker mVelocityTracker;
- FlingAnimationUtils mFlingAnimUtils;
- ValueAnimator mScrollFlingAnimator;
-
- @ViewDebug.ExportedProperty(category="recents")
- boolean mIsScrolling;
- float mDownScrollP;
- int mDownX, mDownY;
- int mLastY;
- int mActivePointerId = INACTIVE_POINTER_ID;
- int mOverscrollSize;
- TaskView mActiveTaskView = null;
-
- int mMinimumVelocity;
- int mMaximumVelocity;
- // The scroll touch slop is used to calculate when we start scrolling
- int mScrollTouchSlop;
- // Used to calculate when a tap is outside a task view rectangle.
- final int mWindowTouchSlop;
-
- private final StackViewScrolledEvent mStackViewScrolledEvent = new StackViewScrolledEvent();
-
- // The current and final set of task transforms, sized to match the list of tasks in the stack
- private ArrayList<Task> mCurrentTasks = new ArrayList<>();
- private ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>();
- private ArrayList<TaskViewTransform> mFinalTaskTransforms = new ArrayList<>();
- private ArrayMap<View, Animator> mSwipeHelperAnimations = new ArrayMap<>();
- private TaskViewTransform mTmpTransform = new TaskViewTransform();
- private float mTargetStackScroll;
-
- SwipeHelper mSwipeHelper;
- boolean mInterceptedBySwipeHelper;
-
- public TaskStackViewTouchHandler(Context context, TaskStackView sv,
- TaskStackViewScroller scroller, FalsingManager falsingManager) {
- Resources res = context.getResources();
- ViewConfiguration configuration = ViewConfiguration.get(context);
- mContext = context;
- mSv = sv;
- mScroller = scroller;
- mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
- mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
- mScrollTouchSlop = configuration.getScaledTouchSlop();
- mWindowTouchSlop = configuration.getScaledWindowTouchSlop();
- mFlingAnimUtils = new FlingAnimationUtils(context, 0.2f);
- mOverscrollSize = res.getDimensionPixelSize(R.dimen.recents_fling_overscroll_distance);
- mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, context, falsingManager) {
- @Override
- protected float getSize(View v) {
- return getScaledDismissSize();
- }
-
- @Override
- protected void prepareDismissAnimation(View v, Animator anim) {
- mSwipeHelperAnimations.put(v, anim);
- }
-
- @Override
- protected void prepareSnapBackAnimation(View v, Animator anim) {
- anim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- mSwipeHelperAnimations.put(v, anim);
- }
-
- @Override
- protected float getUnscaledEscapeVelocity() {
- return CHALLENGING_SWIPE_ESCAPE_VELOCITY;
- }
-
- @Override
- protected long getMaxEscapeAnimDuration() {
- return 700;
- }
- };
- mSwipeHelper.setDisableHardwareLayers(true);
- }
-
- /** Velocity tracker helpers */
- void initOrResetVelocityTracker() {
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- } else {
- mVelocityTracker.clear();
- }
- }
- void recycleVelocityTracker() {
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- }
-
- /** Touch preprocessing for handling below */
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- // Pass through to swipe helper if we are swiping
- mInterceptedBySwipeHelper = isSwipingEnabled() && mSwipeHelper.onInterceptTouchEvent(ev);
- if (mInterceptedBySwipeHelper) {
- return true;
- }
-
- return handleTouchEvent(ev);
- }
-
- /** Handles touch events once we have intercepted them */
- public boolean onTouchEvent(MotionEvent ev) {
- // Pass through to swipe helper if we are swiping
- if (mInterceptedBySwipeHelper && mSwipeHelper.onTouchEvent(ev)) {
- return true;
- }
-
- handleTouchEvent(ev);
- return true;
- }
-
- /**
- * Finishes all scroll-fling and non-dismissing animations currently running.
- */
- public void cancelNonDismissTaskAnimations() {
- Utilities.cancelAnimationWithoutCallbacks(mScrollFlingAnimator);
- if (!mSwipeHelperAnimations.isEmpty()) {
- // For the non-dismissing tasks, freeze the position into the task overrides
- List<TaskView> taskViews = mSv.getTaskViews();
- for (int i = taskViews.size() - 1; i >= 0; i--) {
- TaskView tv = taskViews.get(i);
-
- if (mSv.isIgnoredTask(tv.getTask())) {
- continue;
- }
-
- tv.cancelTransformAnimation();
- mSv.getStackAlgorithm().addUnfocusedTaskOverride(tv, mTargetStackScroll);
- }
- mSv.getStackAlgorithm().setFocusState(TaskStackLayoutAlgorithm.STATE_UNFOCUSED);
- // Update the scroll to the final scroll position from onBeginDrag()
- mSv.getScroller().setStackScroll(mTargetStackScroll, null);
-
- mSwipeHelperAnimations.clear();
- }
- mActiveTaskView = null;
- }
-
- private boolean handleTouchEvent(MotionEvent ev) {
- // Short circuit if we have no children
- if (mSv.getTaskViews().size() == 0) {
- return false;
- }
-
- final TaskStackLayoutAlgorithm layoutAlgorithm = mSv.mLayoutAlgorithm;
- int action = ev.getAction();
- switch (action & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_DOWN: {
- // Stop the current scroll if it is still flinging
- mScroller.stopScroller();
- mScroller.stopBoundScrollAnimation();
- mScroller.resetDeltaScroll();
- cancelNonDismissTaskAnimations();
- mSv.cancelDeferredTaskViewLayoutAnimation();
-
- // Save the touch down info
- mDownX = (int) ev.getX();
- mDownY = (int) ev.getY();
- mLastY = mDownY;
- mDownScrollP = mScroller.getStackScroll();
- mActivePointerId = ev.getPointerId(0);
- mActiveTaskView = findViewAtPoint(mDownX, mDownY);
-
- // Initialize the velocity tracker
- initOrResetVelocityTracker();
- mVelocityTracker.addMovement(ev);
- break;
- }
- case MotionEvent.ACTION_POINTER_DOWN: {
- final int index = ev.getActionIndex();
- mActivePointerId = ev.getPointerId(index);
- mDownX = (int) ev.getX(index);
- mDownY = (int) ev.getY(index);
- mLastY = mDownY;
- mDownScrollP = mScroller.getStackScroll();
- mScroller.resetDeltaScroll();
- mVelocityTracker.addMovement(ev);
- break;
- }
- case MotionEvent.ACTION_MOVE: {
- int activePointerIndex = ev.findPointerIndex(mActivePointerId);
- if (activePointerIndex == -1) {
- break;
- }
- int y = (int) ev.getY(activePointerIndex);
- int x = (int) ev.getX(activePointerIndex);
- if (!mIsScrolling) {
- int yDiff = Math.abs(y - mDownY);
- int xDiff = Math.abs(x - mDownX);
- if (Math.abs(y - mDownY) > mScrollTouchSlop && yDiff > xDiff) {
- mIsScrolling = true;
- float stackScroll = mScroller.getStackScroll();
- List<TaskView> taskViews = mSv.getTaskViews();
- for (int i = taskViews.size() - 1; i >= 0; i--) {
- layoutAlgorithm.addUnfocusedTaskOverride(taskViews.get(i).getTask(),
- stackScroll);
- }
- layoutAlgorithm.setFocusState(TaskStackLayoutAlgorithm.STATE_UNFOCUSED);
-
- // Disallow parents from intercepting touch events
- final ViewParent parent = mSv.getParent();
- if (parent != null) {
- parent.requestDisallowInterceptTouchEvent(true);
- }
-
- MetricsLogger.action(mSv.getContext(), MetricsEvent.OVERVIEW_SCROLL);
- mLastY = mDownY = y;
- }
- }
- if (mIsScrolling) {
- // If we just move linearly on the screen, then that would map to 1/arclength
- // of the curve, so just move the scroll proportional to that
- float deltaP = layoutAlgorithm.getDeltaPForY(mDownY, y);
-
- // Modulate the overscroll to prevent users from pulling the stack too far
- float minScrollP = layoutAlgorithm.mMinScrollP;
- float maxScrollP = layoutAlgorithm.mMaxScrollP;
- float curScrollP = mDownScrollP + deltaP;
- if (curScrollP < minScrollP || curScrollP > maxScrollP) {
- float clampedScrollP = Utilities.clamp(curScrollP, minScrollP, maxScrollP);
- float overscrollP = (curScrollP - clampedScrollP);
- float maxOverscroll = LegacyRecentsImpl.getConfiguration().isLowRamDevice
- ? layoutAlgorithm.mTaskStackLowRamLayoutAlgorithm.getMaxOverscroll()
- : MAX_OVERSCROLL;
- float overscrollX = Math.abs(overscrollP) / maxOverscroll;
- float interpX = OVERSCROLL_INTERP.getInterpolation(overscrollX);
- curScrollP = clampedScrollP + Math.signum(overscrollP) *
- (interpX * maxOverscroll);
- }
- mDownScrollP += mScroller.setDeltaStackScroll(mDownScrollP,
- curScrollP - mDownScrollP);
- mStackViewScrolledEvent.updateY(y - mLastY);
- EventBus.getDefault().send(mStackViewScrolledEvent);
- }
-
- mLastY = y;
- mVelocityTracker.addMovement(ev);
- break;
- }
- case MotionEvent.ACTION_POINTER_UP: {
- int pointerIndex = ev.getActionIndex();
- int pointerId = ev.getPointerId(pointerIndex);
- if (pointerId == mActivePointerId) {
- // Select a new active pointer id and reset the motion state
- final int newPointerIndex = (pointerIndex == 0) ? 1 : 0;
- mActivePointerId = ev.getPointerId(newPointerIndex);
- mDownX = (int) ev.getX(pointerIndex);
- mDownY = (int) ev.getY(pointerIndex);
- mLastY = mDownY;
- mDownScrollP = mScroller.getStackScroll();
- }
- mVelocityTracker.addMovement(ev);
- break;
- }
- case MotionEvent.ACTION_UP: {
- mVelocityTracker.addMovement(ev);
- mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- int activePointerIndex = ev.findPointerIndex(mActivePointerId);
- int y = (int) ev.getY(activePointerIndex);
- int velocity = (int) mVelocityTracker.getYVelocity(mActivePointerId);
- if (mIsScrolling) {
- if (mScroller.isScrollOutOfBounds()) {
- mScroller.animateBoundScroll();
- } else if (Math.abs(velocity) > mMinimumVelocity &&
- !LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- float minY = mDownY + layoutAlgorithm.getYForDeltaP(mDownScrollP,
- layoutAlgorithm.mMaxScrollP);
- float maxY = mDownY + layoutAlgorithm.getYForDeltaP(mDownScrollP,
- layoutAlgorithm.mMinScrollP);
- mScroller.fling(mDownScrollP, mDownY, y, velocity, (int) minY, (int) maxY,
- mOverscrollSize);
- mSv.invalidate();
- }
-
- // Reset the focused task after the user has scrolled, but we have no scrolling
- // in grid layout and therefore we don't want to reset the focus there.
- if (!mSv.mTouchExplorationEnabled && !mSv.useGridLayout()) {
- if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- mScroller.scrollToClosestTask(velocity);
- } else {
- mSv.resetFocusedTask(mSv.getFocusedTask());
- }
- }
- } else if (mActiveTaskView == null) {
- // This tap didn't start on a task.
- maybeHideRecentsFromBackgroundTap((int) ev.getX(), (int) ev.getY());
- }
-
- mActivePointerId = INACTIVE_POINTER_ID;
- mIsScrolling = false;
- recycleVelocityTracker();
- break;
- }
- case MotionEvent.ACTION_CANCEL: {
- mActivePointerId = INACTIVE_POINTER_ID;
- mIsScrolling = false;
- recycleVelocityTracker();
- break;
- }
- }
- return mIsScrolling;
- }
-
- /** Hides recents if the up event at (x, y) is a tap on the background area. */
- void maybeHideRecentsFromBackgroundTap(int x, int y) {
- // Ignore the up event if it's too far from its start position. The user might have been
- // trying to scroll or swipe.
- int dx = Math.abs(mDownX - x);
- int dy = Math.abs(mDownY - y);
- if (dx > mScrollTouchSlop || dy > mScrollTouchSlop) {
- return;
- }
-
- // Shift the tap position toward the center of the task stack and check to see if it would
- // have hit a view. The user might have tried to tap on a task and missed slightly.
- int shiftedX = x;
- if (x > (mSv.getRight() - mSv.getLeft()) / 2) {
- shiftedX -= mWindowTouchSlop;
- } else {
- shiftedX += mWindowTouchSlop;
- }
- if (findViewAtPoint(shiftedX, y) != null) {
- return;
- }
-
- // Disallow tapping above and below the stack to dismiss recents
- if (x > mSv.mLayoutAlgorithm.mStackRect.left && x < mSv.mLayoutAlgorithm.mStackRect.right) {
- return;
- }
-
- // The user intentionally tapped on the background, which is like a tap on the "desktop".
- // Hide recents and transition to the launcher.
- EventBus.getDefault().send(new HideRecentsEvent(false, true));
- }
-
- /** Handles generic motion events */
- public boolean onGenericMotionEvent(MotionEvent ev) {
- if ((ev.getSource() & InputDevice.SOURCE_CLASS_POINTER) ==
- InputDevice.SOURCE_CLASS_POINTER) {
- int action = ev.getAction();
- switch (action & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_SCROLL:
- // Find the front most task and scroll the next task to the front
- float vScroll = ev.getAxisValue(MotionEvent.AXIS_VSCROLL);
- if (vScroll > 0) {
- mSv.setRelativeFocusedTask(true, true /* stackTasksOnly */,
- false /* animated */);
- } else {
- mSv.setRelativeFocusedTask(false, true /* stackTasksOnly */,
- false /* animated */);
- }
- return true;
- }
- }
- return false;
- }
-
- /**** SwipeHelper Implementation ****/
-
- @Override
- public View getChildAtPosition(MotionEvent ev) {
- TaskView tv = findViewAtPoint((int) ev.getX(), (int) ev.getY());
- if (tv != null && canChildBeDismissed(tv)) {
- return tv;
- }
- return null;
- }
-
- @Override
- public boolean canChildBeDismissed(View v) {
- // Disallow dismissing an already dismissed task
- TaskView tv = (TaskView) v;
- Task task = tv.getTask();
- return !mSwipeHelperAnimations.containsKey(v) &&
- (mSv.getStack().indexOfTask(task) != -1);
- }
-
- /**
- * Starts a manual drag that goes through the same swipe helper path.
- */
- public void onBeginManualDrag(TaskView v) {
- mActiveTaskView = v;
- mSwipeHelperAnimations.put(v, null);
- onBeginDrag(v);
- }
-
- @Override
- public void onBeginDrag(View v) {
- TaskView tv = (TaskView) v;
-
- // Disable clipping with the stack while we are swiping
- tv.setClipViewInStack(false);
- // Disallow touch events from this task view
- tv.setTouchEnabled(false);
- // Disallow parents from intercepting touch events
- final ViewParent parent = mSv.getParent();
- if (parent != null) {
- parent.requestDisallowInterceptTouchEvent(true);
- }
-
- // Add this task to the set of tasks we are deleting
- mSv.addIgnoreTask(tv.getTask());
-
- // Determine if we are animating the other tasks while dismissing this task
- mCurrentTasks = new ArrayList<Task>(mSv.getStack().getTasks());
- MutableBoolean isFrontMostTask = new MutableBoolean(false);
- Task anchorTask = mSv.findAnchorTask(mCurrentTasks, isFrontMostTask);
- TaskStackLayoutAlgorithm layoutAlgorithm = mSv.getStackAlgorithm();
- TaskStackViewScroller stackScroller = mSv.getScroller();
- if (anchorTask != null) {
- // Get the current set of task transforms
- mSv.getCurrentTaskTransforms(mCurrentTasks, mCurrentTaskTransforms);
-
- // Get the stack scroll of the task to anchor to (since we are removing something, the
- // front most task will be our anchor task)
- float prevAnchorTaskScroll = 0;
- boolean pullStackForward = mCurrentTasks.size() > 0;
- if (pullStackForward) {
- if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- float index = layoutAlgorithm.getStackScrollForTask(anchorTask);
- prevAnchorTaskScroll = mSv.getStackAlgorithm().mTaskStackLowRamLayoutAlgorithm
- .getScrollPForTask((int) index);
- } else {
- prevAnchorTaskScroll = layoutAlgorithm.getStackScrollForTask(anchorTask);
- }
- }
-
- // Calculate where the views would be without the deleting tasks
- mSv.updateLayoutAlgorithm(false /* boundScroll */);
-
- float newStackScroll = stackScroller.getStackScroll();
- if (isFrontMostTask.value) {
- // Bound the stack scroll to pull tasks forward if necessary
- newStackScroll = stackScroller.getBoundedStackScroll(newStackScroll);
- } else if (pullStackForward) {
- // Otherwise, offset the scroll by the movement of the anchor task
- float anchorTaskScroll =
- layoutAlgorithm.getStackScrollForTaskIgnoreOverrides(anchorTask);
- if (LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- float index = layoutAlgorithm.getStackScrollForTask(anchorTask);
- anchorTaskScroll = mSv.getStackAlgorithm().mTaskStackLowRamLayoutAlgorithm
- .getScrollPForTask((int) index);
- }
- float stackScrollOffset = (anchorTaskScroll - prevAnchorTaskScroll);
- if (layoutAlgorithm.getFocusState() != TaskStackLayoutAlgorithm.STATE_FOCUSED
- && !LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- // If we are focused, we don't want the front task to move, but otherwise, we
- // allow the back task to move up, and the front task to move back
- stackScrollOffset *= 0.75f;
- }
- newStackScroll = stackScroller.getBoundedStackScroll(stackScroller.getStackScroll()
- + stackScrollOffset);
- }
-
- // Pick up the newly visible views, not including the deleting tasks
- mSv.bindVisibleTaskViews(newStackScroll, true /* ignoreTaskOverrides */);
-
- // Get the final set of task transforms (with task removed)
- mSv.getLayoutTaskTransforms(newStackScroll, TaskStackLayoutAlgorithm.STATE_UNFOCUSED,
- mCurrentTasks, true /* ignoreTaskOverrides */, mFinalTaskTransforms);
-
- // Set the target to scroll towards upon dismissal
- mTargetStackScroll = newStackScroll;
-
- /*
- * Post condition: All views that will be visible as a part of the gesture are retrieved
- * and at their initial positions. The stack is still at the current
- * scroll, but the layout is updated without the task currently being
- * dismissed. The final layout is in the unfocused stack state, which
- * will be applied when the current task is dismissed.
- */
- }
- }
-
- @Override
- public boolean updateSwipeProgress(View v, boolean dismissable, float swipeProgress) {
- // Only update the swipe progress for the surrounding tasks if the dismiss animation was not
- // preempted from a call to cancelNonDismissTaskAnimations
- if ((mActiveTaskView == v || mSwipeHelperAnimations.containsKey(v)) &&
- !LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- updateTaskViewTransforms(
- Interpolators.FAST_OUT_SLOW_IN.getInterpolation(swipeProgress));
- }
- return true;
- }
-
- /**
- * Called after the {@link TaskView} is finished animating away.
- */
- @Override
- public void onChildDismissed(View v) {
- TaskView tv = (TaskView) v;
-
- // Re-enable clipping with the stack (we will reuse this view)
- tv.setClipViewInStack(true);
- // Re-enable touch events from this task view
- tv.setTouchEnabled(true);
- // Update the scroll to the final scroll position before laying out the tasks during dismiss
- if (mSwipeHelperAnimations.containsKey(v)) {
- mSv.getScroller().setStackScroll(mTargetStackScroll, null);
- }
- // Remove the task view from the stack, ignoring the animation if we've started dragging
- // again
- EventBus.getDefault().send(new TaskViewDismissedEvent(tv.getTask(), tv,
- mSwipeHelperAnimations.containsKey(v)
- ? new AnimationProps(TaskStackView.DEFAULT_SYNC_STACK_DURATION,
- Interpolators.FAST_OUT_SLOW_IN)
- : null));
- // Only update the final scroll and layout state (set in onBeginDrag()) if the dismiss
- // animation was not preempted from a call to cancelNonDismissTaskAnimations
- if (mSwipeHelperAnimations.containsKey(v)) {
- // Update the focus state to the final focus state
- mSv.getStackAlgorithm().setFocusState(TaskStackLayoutAlgorithm.STATE_UNFOCUSED);
- mSv.getStackAlgorithm().clearUnfocusedTaskOverrides();
- // Stop tracking this deletion animation
- mSwipeHelperAnimations.remove(v);
- }
- // Keep track of deletions by keyboard
- MetricsLogger.histogram(tv.getContext(), "overview_task_dismissed_source",
- Constants.Metrics.DismissSourceSwipeGesture);
- }
-
- /**
- * Called after the {@link TaskView} is finished animating back into the list.
- * onChildDismissed() calls.
- */
- @Override
- public void onChildSnappedBack(View v, float targetLeft) {
- TaskView tv = (TaskView) v;
-
- // Re-enable clipping with the stack
- tv.setClipViewInStack(true);
- // Re-enable touch events from this task view
- tv.setTouchEnabled(true);
-
- // Stop tracking this deleting task, and update the layout to include this task again. The
- // stack scroll does not need to be reset, since the scroll has not actually changed in
- // onBeginDrag().
- mSv.removeIgnoreTask(tv.getTask());
- mSv.updateLayoutAlgorithm(false /* boundScroll */);
- mSv.relayoutTaskViews(AnimationProps.IMMEDIATE);
- mSwipeHelperAnimations.remove(v);
- }
-
- @Override
- public void onDragCancelled(View v) {
- // Do nothing
- }
-
- @Override
- public boolean isAntiFalsingNeeded() {
- return false;
- }
-
- @Override
- public float getFalsingThresholdFactor() {
- return 0;
- }
-
- /**
- * Interpolates the non-deleting tasks to their final transforms from their current transforms.
- */
- private void updateTaskViewTransforms(float dismissFraction) {
- List<TaskView> taskViews = mSv.getTaskViews();
- int taskViewCount = taskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
- TaskView tv = taskViews.get(i);
- Task task = tv.getTask();
-
- if (mSv.isIgnoredTask(task)) {
- continue;
- }
-
- int taskIndex = mCurrentTasks.indexOf(task);
- if (taskIndex == -1) {
- // If a task was added to the stack view after the start of the dismiss gesture,
- // just ignore it
- continue;
- }
-
- TaskViewTransform fromTransform = mCurrentTaskTransforms.get(taskIndex);
- TaskViewTransform toTransform = mFinalTaskTransforms.get(taskIndex);
-
- mTmpTransform.copyFrom(fromTransform);
- // We only really need to interpolate the bounds, progress and translation
- mTmpTransform.rect.set(Utilities.RECTF_EVALUATOR.evaluate(dismissFraction,
- fromTransform.rect, toTransform.rect));
- mTmpTransform.dimAlpha = fromTransform.dimAlpha + (toTransform.dimAlpha -
- fromTransform.dimAlpha) * dismissFraction;
- mTmpTransform.viewOutlineAlpha = fromTransform.viewOutlineAlpha +
- (toTransform.viewOutlineAlpha - fromTransform.viewOutlineAlpha) *
- dismissFraction;
- mTmpTransform.translationZ = fromTransform.translationZ +
- (toTransform.translationZ - fromTransform.translationZ) * dismissFraction;
-
- mSv.updateTaskViewToTransform(tv, mTmpTransform, AnimationProps.IMMEDIATE);
- }
- }
-
- /** Returns the view at the specified coordinates */
- private TaskView findViewAtPoint(int x, int y) {
- List<Task> tasks = mSv.getStack().getTasks();
- int taskCount = tasks.size();
- for (int i = taskCount - 1; i >= 0; i--) {
- TaskView tv = mSv.getChildViewForTask(tasks.get(i));
- if (tv != null && tv.getVisibility() == View.VISIBLE) {
- if (mSv.isTouchPointInView(x, y, tv)) {
- return tv;
- }
- }
- }
- return null;
- }
-
- /**
- * Returns the scaled size used to calculate the dismiss fraction.
- */
- public float getScaledDismissSize() {
- return 1.5f * Math.max(mSv.getWidth(), mSv.getHeight());
- }
-
- /**
- * Returns whether swiping is enabled.
- */
- private boolean isSwipingEnabled() {
- return !mSv.useGridLayout();
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskView.java
deleted file mode 100644
index ab0bf9570ffd..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskView.java
+++ /dev/null
@@ -1,737 +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 com.android.systemui.recents.views;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Outline;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.util.FloatProperty;
-import android.util.Property;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewDebug;
-import android.view.ViewOutlineProvider;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.recents.LegacyRecentsImpl;
-import com.android.systemui.recents.RecentsActivity;
-import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.LaunchTaskEvent;
-import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
-import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
-import com.android.systemui.recents.misc.ReferenceCountedTrigger;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.utilities.AnimationProps;
-import com.android.systemui.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
-/**
- * A {@link TaskView} represents a fixed view of a task. Because the TaskView's layout is directed
- * solely by the {@link TaskStackView}, we make it a fixed size layout which allows relayouts down
- * the view hierarchy, but not upwards from any of its children (the TaskView will relayout itself
- * with the previous bounds if any child requests layout).
- */
-public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks,
- TaskStackAnimationHelper.Callbacks, View.OnClickListener, View.OnLongClickListener {
-
- /** The TaskView callbacks */
- interface TaskViewCallbacks {
- void onTaskViewClipStateChanged(TaskView tv);
- }
-
- /**
- * The dim overlay is generally calculated from the task progress, but occasionally (like when
- * launching) needs to be animated independently of the task progress. This call is only used
- * when animating the task into Recents, when the header dim is already applied
- */
- public static final Property<TaskView, Float> DIM_ALPHA_WITHOUT_HEADER =
- new FloatProperty<TaskView>("dimAlphaWithoutHeader") {
- @Override
- public void setValue(TaskView tv, float dimAlpha) {
- tv.setDimAlphaWithoutHeader(dimAlpha);
- }
-
- @Override
- public Float get(TaskView tv) {
- return tv.getDimAlpha();
- }
- };
-
- /**
- * The dim overlay is generally calculated from the task progress, but occasionally (like when
- * launching) needs to be animated independently of the task progress.
- */
- public static final Property<TaskView, Float> DIM_ALPHA =
- new FloatProperty<TaskView>("dimAlpha") {
- @Override
- public void setValue(TaskView tv, float dimAlpha) {
- tv.setDimAlpha(dimAlpha);
- }
-
- @Override
- public Float get(TaskView tv) {
- return tv.getDimAlpha();
- }
- };
-
- /**
- * The dim overlay is generally calculated from the task progress, but occasionally (like when
- * launching) needs to be animated independently of the task progress.
- */
- public static final Property<TaskView, Float> VIEW_OUTLINE_ALPHA =
- new FloatProperty<TaskView>("viewOutlineAlpha") {
- @Override
- public void setValue(TaskView tv, float alpha) {
- tv.getViewBounds().setAlpha(alpha);
- }
-
- @Override
- public Float get(TaskView tv) {
- return tv.getViewBounds().getAlpha();
- }
- };
-
- @ViewDebug.ExportedProperty(category="recents")
- private float mDimAlpha;
- private float mActionButtonTranslationZ;
-
- @ViewDebug.ExportedProperty(deepExport=true, prefix="task_")
- private Task mTask;
- private boolean mTaskBound;
- @ViewDebug.ExportedProperty(category="recents")
- private boolean mClipViewInStack = true;
- @ViewDebug.ExportedProperty(category="recents")
- private boolean mTouchExplorationEnabled;
- @ViewDebug.ExportedProperty(category="recents")
- private boolean mIsDisabledInSafeMode;
- @ViewDebug.ExportedProperty(deepExport=true, prefix="view_bounds_")
- private AnimateableViewBounds mViewBounds;
-
- private AnimatorSet mTransformAnimation;
- private ObjectAnimator mDimAnimator;
- private ObjectAnimator mOutlineAnimator;
- private final TaskViewTransform mTargetAnimationTransform = new TaskViewTransform();
- private ArrayList<Animator> mTmpAnimators = new ArrayList<>();
-
- @ViewDebug.ExportedProperty(deepExport=true, prefix="thumbnail_")
- protected TaskViewThumbnail mThumbnailView;
- @ViewDebug.ExportedProperty(deepExport=true, prefix="header_")
- protected TaskViewHeader mHeaderView;
- private View mActionButtonView;
- private View mIncompatibleAppToastView;
- private TaskViewCallbacks mCb;
-
- @ViewDebug.ExportedProperty(category="recents")
- private Point mDownTouchPos = new Point();
-
- private Toast mDisabledAppToast;
-
- public TaskView(Context context) {
- this(context, null);
- }
-
- public TaskView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
- Resources res = context.getResources();
- mViewBounds = createOutlineProvider();
- if (config.fakeShadows) {
- setBackground(new FakeShadowDrawable(res, config));
- }
- setOutlineProvider(mViewBounds);
- setOnLongClickListener(this);
- setAccessibilityDelegate(new TaskViewAccessibilityDelegate(this));
- }
-
- /** Set callback */
- void setCallbacks(TaskViewCallbacks cb) {
- mCb = cb;
- }
-
- /**
- * Called from RecentsActivity when it is relaunched.
- */
- void onReload(boolean isResumingFromVisible) {
- resetNoUserInteractionState();
- if (!isResumingFromVisible) {
- resetViewProperties();
- }
- }
-
- /** Gets the task */
- public Task getTask() {
- return mTask;
- }
-
- /* Create an outline provider to clip and outline the view */
- protected AnimateableViewBounds createOutlineProvider() {
- return new AnimateableViewBounds(this, mContext.getResources().getDimensionPixelSize(
- R.dimen.recents_task_view_shadow_rounded_corners_radius));
- }
-
- /** Returns the view bounds. */
- AnimateableViewBounds getViewBounds() {
- return mViewBounds;
- }
-
- @Override
- protected void onFinishInflate() {
- // Bind the views
- mHeaderView = findViewById(R.id.task_view_bar);
- mThumbnailView = findViewById(R.id.task_view_thumbnail);
- mThumbnailView.updateClipToTaskBar(mHeaderView);
- mActionButtonView = findViewById(R.id.lock_to_app_fab);
- mActionButtonView.setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- // Set the outline to match the FAB background
- outline.setOval(0, 0, mActionButtonView.getWidth(), mActionButtonView.getHeight());
- outline.setAlpha(0.35f);
- }
- });
- mActionButtonView.setOnClickListener(this);
- mActionButtonTranslationZ = mActionButtonView.getTranslationZ();
- }
-
- /**
- * Update the task view when the configuration changes.
- */
- protected void onConfigurationChanged() {
- mHeaderView.onConfigurationChanged();
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- if (w > 0 && h > 0) {
- mHeaderView.onTaskViewSizeChanged(w, h);
- mThumbnailView.onTaskViewSizeChanged(w, h);
-
- mActionButtonView.setTranslationX(w - getMeasuredWidth());
- mActionButtonView.setTranslationY(h - getMeasuredHeight());
- }
- }
-
- @Override
- public boolean hasOverlappingRendering() {
- return false;
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mDownTouchPos.set((int) (ev.getX() * getScaleX()), (int) (ev.getY() * getScaleY()));
- }
- return super.onInterceptTouchEvent(ev);
- }
-
- @Override
- protected void measureContents(int width, int height) {
- int widthWithoutPadding = width - mPaddingLeft - mPaddingRight;
- int heightWithoutPadding = height - mPaddingTop - mPaddingBottom;
- int widthSpec = MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY);
- int heightSpec = MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.EXACTLY);
-
- // Measure the content
- measureChildren(widthSpec, heightSpec);
-
- setMeasuredDimension(width, height);
- }
-
- void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform,
- AnimationProps toAnimation, ValueAnimator.AnimatorUpdateListener updateCallback) {
- RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
- cancelTransformAnimation();
-
- // Compose the animations for the transform
- mTmpAnimators.clear();
- toTransform.applyToTaskView(this, mTmpAnimators, toAnimation, !config.fakeShadows);
- if (toAnimation.isImmediate()) {
- if (Float.compare(getDimAlpha(), toTransform.dimAlpha) != 0) {
- setDimAlpha(toTransform.dimAlpha);
- }
- if (Float.compare(mViewBounds.getAlpha(), toTransform.viewOutlineAlpha) != 0) {
- mViewBounds.setAlpha(toTransform.viewOutlineAlpha);
- }
- // Manually call back to the animator listener and update callback
- if (toAnimation.getListener() != null) {
- toAnimation.getListener().onAnimationEnd(null);
- }
- if (updateCallback != null) {
- updateCallback.onAnimationUpdate(null);
- }
- } else {
- // Both the progress and the update are a function of the bounds movement of the task
- if (Float.compare(getDimAlpha(), toTransform.dimAlpha) != 0) {
- mDimAnimator = ObjectAnimator.ofFloat(this, DIM_ALPHA, getDimAlpha(),
- toTransform.dimAlpha);
- mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, mDimAnimator));
- }
- if (Float.compare(mViewBounds.getAlpha(), toTransform.viewOutlineAlpha) != 0) {
- mOutlineAnimator = ObjectAnimator.ofFloat(this, VIEW_OUTLINE_ALPHA,
- mViewBounds.getAlpha(), toTransform.viewOutlineAlpha);
- mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, mOutlineAnimator));
- }
- if (updateCallback != null) {
- ValueAnimator updateCallbackAnim = ValueAnimator.ofInt(0, 1);
- updateCallbackAnim.addUpdateListener(updateCallback);
- mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, updateCallbackAnim));
- }
-
- // Create the animator
- mTransformAnimation = toAnimation.createAnimator(mTmpAnimators);
- mTransformAnimation.start();
- mTargetAnimationTransform.copyFrom(toTransform);
- }
- }
-
- /** Resets this view's properties */
- void resetViewProperties() {
- cancelTransformAnimation();
- setDimAlpha(0);
- setVisibility(View.VISIBLE);
- getViewBounds().reset();
- getHeaderView().reset();
- TaskViewTransform.reset(this);
-
- mActionButtonView.setScaleX(1f);
- mActionButtonView.setScaleY(1f);
- mActionButtonView.setAlpha(0f);
- mActionButtonView.setTranslationX(0f);
- mActionButtonView.setTranslationY(0f);
- mActionButtonView.setTranslationZ(mActionButtonTranslationZ);
- if (mIncompatibleAppToastView != null) {
- mIncompatibleAppToastView.setVisibility(View.INVISIBLE);
- }
- }
-
- /**
- * @return whether we are animating towards {@param transform}
- */
- boolean isAnimatingTo(TaskViewTransform transform) {
- return mTransformAnimation != null && mTransformAnimation.isStarted()
- && mTargetAnimationTransform.isSame(transform);
- }
-
- /**
- * Cancels any current transform animations.
- */
- public void cancelTransformAnimation() {
- cancelDimAnimationIfExists();
- Utilities.cancelAnimationWithoutCallbacks(mTransformAnimation);
- Utilities.cancelAnimationWithoutCallbacks(mOutlineAnimator);
- }
-
- private void cancelDimAnimationIfExists() {
- if (mDimAnimator != null) {
- mDimAnimator.cancel();
- }
- }
-
- /** Enables/disables handling touch on this task view. */
- public void setTouchEnabled(boolean enabled) {
- setOnClickListener(enabled ? this : null);
- }
-
- /** Animates this task view if the user does not interact with the stack after a certain time. */
- public void startNoUserInteractionAnimation() {
- mHeaderView.startNoUserInteractionAnimation();
- }
-
- /** Mark this task view that the user does has not interacted with the stack after a certain time. */
- void setNoUserInteractionState() {
- mHeaderView.setNoUserInteractionState();
- }
-
- /** Resets the state tracking that the user has not interacted with the stack after a certain time. */
- void resetNoUserInteractionState() {
- mHeaderView.resetNoUserInteractionState();
- }
-
- /** Dismisses this task. */
- void dismissTask() {
- // Animate out the view and call the callback
- final TaskView tv = this;
- DismissTaskViewEvent dismissEvent = new DismissTaskViewEvent(tv);
- dismissEvent.addPostAnimationCallback(new Runnable() {
- @Override
- public void run() {
- EventBus.getDefault().send(new TaskViewDismissedEvent(mTask, tv,
- new AnimationProps(TaskStackView.DEFAULT_SYNC_STACK_DURATION,
- Interpolators.FAST_OUT_SLOW_IN)));
- }
- });
- EventBus.getDefault().send(dismissEvent);
- }
-
- /**
- * Returns whether this view should be clipped, or any views below should clip against this
- * view.
- */
- boolean shouldClipViewInStack() {
- if (getVisibility() != View.VISIBLE || LegacyRecentsImpl.getConfiguration().isLowRamDevice) {
- return false;
- }
- return mClipViewInStack;
- }
-
- /** Sets whether this view should be clipped, or clipped against. */
- void setClipViewInStack(boolean clip) {
- if (clip != mClipViewInStack) {
- mClipViewInStack = clip;
- if (mCb != null) {
- mCb.onTaskViewClipStateChanged(this);
- }
- }
- }
-
- public TaskViewHeader getHeaderView() {
- return mHeaderView;
- }
-
- /**
- * Sets the current dim.
- */
- public void setDimAlpha(float dimAlpha) {
- mDimAlpha = dimAlpha;
- mThumbnailView.setDimAlpha(dimAlpha);
- mHeaderView.setDimAlpha(dimAlpha);
- }
-
- /**
- * Sets the current dim without updating the header's dim.
- */
- public void setDimAlphaWithoutHeader(float dimAlpha) {
- mDimAlpha = dimAlpha;
- mThumbnailView.setDimAlpha(dimAlpha);
- }
-
- /**
- * Returns the current dim.
- */
- public float getDimAlpha() {
- return mDimAlpha;
- }
-
- /**
- * Explicitly sets the focused state of this task.
- */
- public void setFocusedState(boolean isFocused, boolean requestViewFocus) {
- if (isFocused) {
- if (requestViewFocus && !isFocused()) {
- requestFocus();
- }
- } else {
- if (isAccessibilityFocused() && mTouchExplorationEnabled) {
- clearAccessibilityFocus();
- }
- }
- }
-
- /**
- * Shows the action button.
- * @param fadeIn whether or not to animate the action button in.
- * @param fadeInDuration the duration of the action button animation, only used if
- * {@param fadeIn} is true.
- */
- public void showActionButton(boolean fadeIn, int fadeInDuration) {
- mActionButtonView.setVisibility(View.VISIBLE);
-
- if (fadeIn && mActionButtonView.getAlpha() < 1f) {
- mActionButtonView.animate()
- .alpha(1f)
- .scaleX(1f)
- .scaleY(1f)
- .setDuration(fadeInDuration)
- .setInterpolator(Interpolators.ALPHA_IN)
- .start();
- } else {
- mActionButtonView.setScaleX(1f);
- mActionButtonView.setScaleY(1f);
- mActionButtonView.setAlpha(1f);
- mActionButtonView.setTranslationZ(mActionButtonTranslationZ);
- }
- }
-
- /**
- * Immediately hides the action button.
- *
- * @param fadeOut whether or not to animate the action button out.
- */
- public void hideActionButton(boolean fadeOut, int fadeOutDuration, boolean scaleDown,
- final Animator.AnimatorListener animListener) {
- if (fadeOut && mActionButtonView.getAlpha() > 0f) {
- if (scaleDown) {
- float toScale = 0.9f;
- mActionButtonView.animate()
- .scaleX(toScale)
- .scaleY(toScale);
- }
- mActionButtonView.animate()
- .alpha(0f)
- .setDuration(fadeOutDuration)
- .setInterpolator(Interpolators.ALPHA_OUT)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- if (animListener != null) {
- animListener.onAnimationEnd(null);
- }
- mActionButtonView.setVisibility(View.INVISIBLE);
- }
- })
- .start();
- } else {
- mActionButtonView.setAlpha(0f);
- mActionButtonView.setVisibility(View.INVISIBLE);
- if (animListener != null) {
- animListener.onAnimationEnd(null);
- }
- }
- }
-
- /**** TaskStackAnimationHelper.Callbacks Implementation ****/
-
- @Override
- public void onPrepareLaunchTargetForEnterAnimation() {
- // These values will be animated in when onStartLaunchTargetEnterAnimation() is called
- setDimAlphaWithoutHeader(0);
- mActionButtonView.setAlpha(0f);
- if (mIncompatibleAppToastView != null &&
- mIncompatibleAppToastView.getVisibility() == View.VISIBLE) {
- mIncompatibleAppToastView.setAlpha(0f);
- }
- }
-
- @Override
- public void onStartLaunchTargetEnterAnimation(TaskViewTransform transform, int duration,
- boolean screenPinningEnabled, ReferenceCountedTrigger postAnimationTrigger) {
- cancelDimAnimationIfExists();
-
- // Dim the view after the app window transitions down into recents
- postAnimationTrigger.increment();
- AnimationProps animation = new AnimationProps(duration, Interpolators.ALPHA_OUT);
- mDimAnimator = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this,
- DIM_ALPHA_WITHOUT_HEADER, getDimAlpha(), transform.dimAlpha));
- mDimAnimator.addListener(postAnimationTrigger.decrementOnAnimationEnd());
- mDimAnimator.start();
-
- if (screenPinningEnabled) {
- showActionButton(true /* fadeIn */, duration /* fadeInDuration */);
- }
-
- if (mIncompatibleAppToastView != null &&
- mIncompatibleAppToastView.getVisibility() == View.VISIBLE) {
- mIncompatibleAppToastView.animate()
- .alpha(1f)
- .setDuration(duration)
- .setInterpolator(Interpolators.ALPHA_IN)
- .start();
- }
- }
-
- @Override
- public void onStartLaunchTargetLaunchAnimation(int duration, boolean screenPinningRequested,
- ReferenceCountedTrigger postAnimationTrigger) {
- Utilities.cancelAnimationWithoutCallbacks(mDimAnimator);
-
- // Un-dim the view before/while launching the target
- AnimationProps animation = new AnimationProps(duration, Interpolators.ALPHA_OUT);
- mDimAnimator = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this,
- DIM_ALPHA, getDimAlpha(), 0));
- mDimAnimator.start();
-
- postAnimationTrigger.increment();
- hideActionButton(true /* fadeOut */, duration,
- !screenPinningRequested /* scaleDown */,
- postAnimationTrigger.decrementOnAnimationEnd());
- }
-
- @Override
- public void onStartFrontTaskEnterAnimation(boolean screenPinningEnabled) {
- if (screenPinningEnabled) {
- showActionButton(false /* fadeIn */, 0 /* fadeInDuration */);
- }
- }
-
- /**** TaskCallbacks Implementation ****/
-
- public void onTaskBound(Task t, boolean touchExplorationEnabled, int displayOrientation,
- Rect displayRect) {
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- mTouchExplorationEnabled = touchExplorationEnabled;
- mTask = t;
- mTaskBound = true;
- mTask.addCallback(this);
- mIsDisabledInSafeMode = !mTask.isSystemApp && ssp.isInSafeMode();
- mThumbnailView.bindToTask(mTask, mIsDisabledInSafeMode, displayOrientation, displayRect);
- mHeaderView.bindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
-
- if (!t.isDockable && ssp.hasDockedTask()) {
- if (mIncompatibleAppToastView == null) {
- mIncompatibleAppToastView = Utilities.findViewStubById(this,
- R.id.incompatible_app_toast_stub).inflate();
- TextView msg = findViewById(com.android.internal.R.id.message);
- msg.setText(R.string.dock_non_resizeble_failed_to_dock_text);
- }
- mIncompatibleAppToastView.setVisibility(View.VISIBLE);
- } else if (mIncompatibleAppToastView != null) {
- mIncompatibleAppToastView.setVisibility(View.INVISIBLE);
- }
- }
-
- @Override
- public void onTaskDataLoaded(Task task, ThumbnailData thumbnailData) {
- if (mTaskBound) {
- // Update each of the views to the new task data
- mThumbnailView.onTaskDataLoaded(thumbnailData);
- mHeaderView.onTaskDataLoaded();
- }
- }
-
- @Override
- public void onTaskDataUnloaded() {
- // Unbind each of the views from the task and remove the task callback
- mTask.removeCallback(this);
- mThumbnailView.unbindFromTask();
- mHeaderView.unbindFromTask(mTouchExplorationEnabled);
- mTaskBound = false;
- }
-
- @Override
- public void onTaskWindowingModeChanged() {
- // Force rebind the header, the thumbnail does not change due to stack changes
- mHeaderView.bindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
- mHeaderView.onTaskDataLoaded();
- }
-
- /**** View.OnClickListener Implementation ****/
-
- @Override
- public void onClick(final View v) {
- if (mIsDisabledInSafeMode) {
- Context context = getContext();
- String msg = context.getString(R.string.recents_launch_disabled_message, mTask.title);
- if (mDisabledAppToast != null) {
- mDisabledAppToast.cancel();
- }
- mDisabledAppToast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
- mDisabledAppToast.show();
- return;
- }
-
- boolean screenPinningRequested = false;
- if (v == mActionButtonView) {
- // Reset the translation of the action button before we animate it out
- mActionButtonView.setTranslationZ(0f);
- screenPinningRequested = true;
- }
- EventBus.getDefault().send(new LaunchTaskEvent(this, mTask, null, screenPinningRequested));
-
- MetricsLogger.action(v.getContext(), MetricsEvent.ACTION_OVERVIEW_SELECT,
- mTask.key.getComponent().toString());
- }
-
- /**** View.OnLongClickListener Implementation ****/
-
- @Override
- public boolean onLongClick(View v) {
- if (!LegacyRecentsImpl.getConfiguration().dragToSplitEnabled) {
- return false;
- }
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- boolean inBounds = false;
- Rect clipBounds = new Rect(mViewBounds.getClipBounds());
- if (!clipBounds.isEmpty()) {
- // If we are clipping the view to the bounds, manually do the hit test.
- clipBounds.scale(getScaleX());
- inBounds = clipBounds.contains(mDownTouchPos.x, mDownTouchPos.y);
- } else {
- // Otherwise just make sure we're within the view's bounds.
- inBounds = mDownTouchPos.x <= getWidth() && mDownTouchPos.y <= getHeight();
- }
- if (v == this && inBounds && !ssp.hasDockedTask()) {
- // Start listening for drag events
- setClipViewInStack(false);
-
- mDownTouchPos.x += ((1f - getScaleX()) * getWidth()) / 2;
- mDownTouchPos.y += ((1f - getScaleY()) * getHeight()) / 2;
-
- EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
- EventBus.getDefault().send(new DragStartEvent(mTask, this, mDownTouchPos));
- return true;
- }
- return false;
- }
-
- /**** Events ****/
-
- public final void onBusEvent(DragEndEvent event) {
- if (!(event.dropTarget instanceof DockState)) {
- event.addPostAnimationCallback(() -> {
- // Reset the clip state for the drag view after the end animation completes
- setClipViewInStack(true);
- });
- }
- EventBus.getDefault().unregister(this);
- }
-
- public final void onBusEvent(DragEndCancelledEvent event) {
- // Reset the clip state for the drag view after the cancel animation completes
- event.addPostAnimationCallback(() -> {
- setClipViewInStack(true);
- });
- }
-
- public void dump(String prefix, PrintWriter writer) {
- String innerPrefix = prefix + " ";
-
- writer.print(prefix); writer.print("TaskView");
- writer.print(" mTask="); writer.print(mTask.key.id);
- writer.println();
-
- mThumbnailView.dump(innerPrefix, writer);
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java
deleted file mode 100644
index 7bcad75cefcf..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.recents.views;
-
-import android.app.ActivityTaskManager;
-import android.content.Context;
-import android.graphics.Point;
-import android.os.Bundle;
-import android.util.SparseArray;
-import android.view.View;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import com.android.systemui.R;
-import com.android.systemui.recents.LegacyRecentsImpl;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
-import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
-
-public class TaskViewAccessibilityDelegate extends View.AccessibilityDelegate {
- private static final String TAG = "TaskViewAccessibilityDelegate";
-
- private final TaskView mTaskView;
-
- protected static final int SPLIT_TASK_TOP = R.id.action_split_task_to_top;
- protected static final int SPLIT_TASK_LEFT = R.id.action_split_task_to_left;
- protected static final int SPLIT_TASK_RIGHT = R.id.action_split_task_to_right;
-
- protected final SparseArray<AccessibilityAction> mActions = new SparseArray<>();
-
- public TaskViewAccessibilityDelegate(TaskView taskView) {
- mTaskView = taskView;
- Context context = taskView.getContext();
- mActions.put(SPLIT_TASK_TOP, new AccessibilityAction(SPLIT_TASK_TOP,
- context.getString(R.string.recents_accessibility_split_screen_top)));
- mActions.put(SPLIT_TASK_LEFT, new AccessibilityAction(SPLIT_TASK_LEFT,
- context.getString(R.string.recents_accessibility_split_screen_left)));
- mActions.put(SPLIT_TASK_RIGHT, new AccessibilityAction(SPLIT_TASK_RIGHT,
- context.getString(R.string.recents_accessibility_split_screen_right)));
- }
-
- @Override
- public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(host, info);
- if (ActivityTaskManager.supportsSplitScreenMultiWindow(mTaskView.getContext())
- && !LegacyRecentsImpl.getSystemServices().hasDockedTask()) {
- DockState[] dockStates = LegacyRecentsImpl.getConfiguration()
- .getDockStatesForCurrentOrientation();
- for (DockState dockState: dockStates) {
- if (dockState == DockState.TOP) {
- info.addAction(mActions.get(SPLIT_TASK_TOP));
- } else if (dockState == DockState.LEFT) {
- info.addAction(mActions.get(SPLIT_TASK_LEFT));
- } else if (dockState == DockState.RIGHT) {
- info.addAction(mActions.get(SPLIT_TASK_RIGHT));
- }
- }
- }
- }
-
- @Override
- public boolean performAccessibilityAction(View host, int action, Bundle args) {
- if (action == SPLIT_TASK_TOP) {
- simulateDragIntoMultiwindow(DockState.TOP);
- } else if (action == SPLIT_TASK_LEFT) {
- simulateDragIntoMultiwindow(DockState.LEFT);
- } else if (action == SPLIT_TASK_RIGHT) {
- simulateDragIntoMultiwindow(DockState.RIGHT);
- } else {
- return super.performAccessibilityAction(host, action, args);
- }
- return true;
- }
-
- /** Simulate a user drag event to split the screen to the respected side */
- private void simulateDragIntoMultiwindow(DockState dockState) {
- EventBus.getDefault().send(new DragStartEvent(mTaskView.getTask(), mTaskView,
- new Point(0,0), false /* isUserTouchInitiated */));
- EventBus.getDefault().send(new DragEndEvent(mTaskView.getTask(), mTaskView, dockState));
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewHeader.java
deleted file mode 100644
index 21c0234121c4..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ /dev/null
@@ -1,685 +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 com.android.systemui.recents.views;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.annotation.Nullable;
-import android.app.AppGlobals;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.RippleDrawable;
-import android.os.CountDownTimer;
-import androidx.core.graphics.ColorUtils;
-import android.util.AttributeSet;
-import android.util.IconDrawableFactory;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewAnimationUtils;
-import android.view.ViewDebug;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.recents.Constants;
-import com.android.systemui.recents.LegacyRecentsImpl;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.LaunchTaskEvent;
-import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.PackageManagerWrapper;
-import com.android.systemui.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
-
-/* The task bar view */
-public class TaskViewHeader extends FrameLayout
- implements View.OnClickListener, View.OnLongClickListener {
-
- private static IconDrawableFactory sDrawableFactory;
-
- private static final float HIGHLIGHT_LIGHTNESS_INCREMENT = 0.075f;
- private static final float OVERLAY_LIGHTNESS_INCREMENT = -0.0625f;
- private static final int OVERLAY_REVEAL_DURATION = 250;
- private static final long FOCUS_INDICATOR_INTERVAL_MS = 30;
-
- /**
- * A color drawable that draws a slight highlight at the top to help it stand out.
- */
- private class HighlightColorDrawable extends Drawable {
-
- private Paint mHighlightPaint = new Paint();
- private Paint mBackgroundPaint = new Paint();
- private int mColor;
- private float mDimAlpha;
-
- public HighlightColorDrawable() {
- mBackgroundPaint.setColor(Color.argb(255, 0, 0, 0));
- mBackgroundPaint.setAntiAlias(true);
- mHighlightPaint.setColor(Color.argb(255, 255, 255, 255));
- mHighlightPaint.setAntiAlias(true);
- }
-
- public void setColorAndDim(int color, float dimAlpha) {
- if (mColor != color || Float.compare(mDimAlpha, dimAlpha) != 0) {
- mColor = color;
- mDimAlpha = dimAlpha;
- if (mShouldDarkenBackgroundColor) {
- color = getSecondaryColor(color, false /* useLightOverlayColor */);
- }
- mBackgroundPaint.setColor(color);
-
- ColorUtils.colorToHSL(color, mTmpHSL);
- // TODO: Consider using the saturation of the color to adjust the lightness as well
- mTmpHSL[2] = Math.min(1f,
- mTmpHSL[2] + HIGHLIGHT_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
- mHighlightPaint.setColor(ColorUtils.HSLToColor(mTmpHSL));
-
- invalidateSelf();
- }
- }
-
- @Override
- public void setColorFilter(@Nullable ColorFilter colorFilter) {
- // Do nothing
- }
-
- @Override
- public void setAlpha(int alpha) {
- // Do nothing
- }
-
- @Override
- public void draw(Canvas canvas) {
- // Draw the highlight at the top edge (but put the bottom edge just out of view)
- canvas.drawRoundRect(0, 0, mTaskViewRect.width(),
- 2 * Math.max(mHighlightHeight, mCornerRadius),
- mCornerRadius, mCornerRadius, mHighlightPaint);
-
- // Draw the background with the rounded corners
- canvas.drawRoundRect(0, mHighlightHeight, mTaskViewRect.width(),
- getHeight() + mCornerRadius,
- mCornerRadius, mCornerRadius, mBackgroundPaint);
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.OPAQUE;
- }
-
- public int getColor() {
- return mColor;
- }
- }
-
- Task mTask;
-
- // Header views
- ImageView mIconView;
- TextView mTitleView;
- ImageView mMoveTaskButton;
- ImageView mDismissButton;
- FrameLayout mAppOverlayView;
- ImageView mAppIconView;
- ImageView mAppInfoView;
- TextView mAppTitleView;
- ProgressBar mFocusTimerIndicator;
-
- // Header drawables
- @ViewDebug.ExportedProperty(category="recents")
- Rect mTaskViewRect = new Rect();
- int mHeaderBarHeight;
- int mHeaderButtonPadding;
- int mCornerRadius;
- int mHighlightHeight;
- @ViewDebug.ExportedProperty(category="recents")
- float mDimAlpha;
- Drawable mLightDismissDrawable;
- Drawable mDarkDismissDrawable;
- Drawable mLightFullscreenIcon;
- Drawable mDarkFullscreenIcon;
- Drawable mLightInfoIcon;
- Drawable mDarkInfoIcon;
- int mTaskBarViewLightTextColor;
- int mTaskBarViewDarkTextColor;
- int mDisabledTaskBarBackgroundColor;
- String mDismissDescFormat;
- String mAppInfoDescFormat;
- int mTaskWindowingMode = WINDOWING_MODE_UNDEFINED;
-
- // Header background
- private HighlightColorDrawable mBackground;
- private HighlightColorDrawable mOverlayBackground;
- private float[] mTmpHSL = new float[3];
-
- // Header dim, which is only used when task view hardware layers are not used
- private Paint mDimLayerPaint = new Paint();
-
- // Whether the background color should be darkened to differentiate from the primary color.
- // Used in grid layout.
- private boolean mShouldDarkenBackgroundColor = false;
-
- private CountDownTimer mFocusTimerCountDown;
-
- public TaskViewHeader(Context context) {
- this(context, null);
- }
-
- public TaskViewHeader(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public TaskViewHeader(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public TaskViewHeader(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- setWillNotDraw(false);
-
- // Load the dismiss resources
- Resources res = context.getResources();
- mLightDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_light);
- mDarkDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_dark);
- mCornerRadius = LegacyRecentsImpl.getConfiguration().isGridEnabled ?
- res.getDimensionPixelSize(R.dimen.recents_grid_task_view_rounded_corners_radius) :
- res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
- mHighlightHeight = res.getDimensionPixelSize(R.dimen.recents_task_view_highlight);
- mTaskBarViewLightTextColor = context.getColor(R.color.recents_task_bar_light_text_color);
- mTaskBarViewDarkTextColor = context.getColor(R.color.recents_task_bar_dark_text_color);
- mLightFullscreenIcon = context.getDrawable(R.drawable.recents_move_task_fullscreen_light);
- mDarkFullscreenIcon = context.getDrawable(R.drawable.recents_move_task_fullscreen_dark);
- mLightInfoIcon = context.getDrawable(R.drawable.recents_info_light);
- mDarkInfoIcon = context.getDrawable(R.drawable.recents_info_dark);
- mDisabledTaskBarBackgroundColor =
- context.getColor(R.color.recents_task_bar_disabled_background_color);
- mDismissDescFormat = mContext.getString(
- R.string.accessibility_recents_item_will_be_dismissed);
- mAppInfoDescFormat = mContext.getString(R.string.accessibility_recents_item_open_app_info);
-
- // Configure the background and dim
- mBackground = new HighlightColorDrawable();
- mBackground.setColorAndDim(Color.argb(255, 0, 0, 0), 0f);
- setBackground(mBackground);
- mOverlayBackground = new HighlightColorDrawable();
- mDimLayerPaint.setColor(Color.argb(255, 0, 0, 0));
- mDimLayerPaint.setAntiAlias(true);
- }
-
- /**
- * Resets this header along with the TaskView.
- */
- public void reset() {
- hideAppOverlay(true /* immediate */);
- }
-
- @Override
- protected void onFinishInflate() {
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
-
- // Initialize the icon and description views
- mIconView = findViewById(R.id.icon);
- mIconView.setOnLongClickListener(this);
- mTitleView = findViewById(R.id.title);
- mDismissButton = findViewById(R.id.dismiss_task);
-
- onConfigurationChanged();
- }
-
- /**
- * Programmatically sets the layout params for a header bar layout. This is necessary because
- * we can't get resources based on the current configuration, but instead need to get them
- * based on the device configuration.
- */
- private void updateLayoutParams(View icon, View title, View secondaryButton, View button) {
- FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, mHeaderBarHeight, Gravity.TOP);
- setLayoutParams(lp);
- lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.START);
- icon.setLayoutParams(lp);
- lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL);
- lp.setMarginStart(mHeaderBarHeight);
- lp.setMarginEnd(mMoveTaskButton != null
- ? 2 * mHeaderBarHeight
- : mHeaderBarHeight);
- title.setLayoutParams(lp);
- if (secondaryButton != null) {
- lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.END);
- lp.setMarginEnd(mHeaderBarHeight);
- secondaryButton.setLayoutParams(lp);
- secondaryButton.setPadding(mHeaderButtonPadding, mHeaderButtonPadding,
- mHeaderButtonPadding, mHeaderButtonPadding);
- }
- lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.END);
- button.setLayoutParams(lp);
- button.setPadding(mHeaderButtonPadding, mHeaderButtonPadding, mHeaderButtonPadding,
- mHeaderButtonPadding);
- }
-
- /**
- * Update the header view when the configuration changes.
- */
- public void onConfigurationChanged() {
- // Update the dimensions of everything in the header. We do this because we need to use
- // resources for the display, and not the current configuration.
- Resources res = getResources();
- int headerBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(getContext(),
- R.dimen.recents_task_view_header_height,
- R.dimen.recents_task_view_header_height,
- R.dimen.recents_task_view_header_height,
- R.dimen.recents_task_view_header_height_tablet_land,
- R.dimen.recents_task_view_header_height,
- R.dimen.recents_task_view_header_height_tablet_land,
- R.dimen.recents_grid_task_view_header_height);
- int headerButtonPadding = TaskStackLayoutAlgorithm.getDimensionForDevice(getContext(),
- R.dimen.recents_task_view_header_button_padding,
- R.dimen.recents_task_view_header_button_padding,
- R.dimen.recents_task_view_header_button_padding,
- R.dimen.recents_task_view_header_button_padding_tablet_land,
- R.dimen.recents_task_view_header_button_padding,
- R.dimen.recents_task_view_header_button_padding_tablet_land,
- R.dimen.recents_grid_task_view_header_button_padding);
- if (headerBarHeight != mHeaderBarHeight || headerButtonPadding != mHeaderButtonPadding) {
- mHeaderBarHeight = headerBarHeight;
- mHeaderButtonPadding = headerButtonPadding;
- updateLayoutParams(mIconView, mTitleView, mMoveTaskButton, mDismissButton);
- if (mAppOverlayView != null) {
- updateLayoutParams(mAppIconView, mAppTitleView, null, mAppInfoView);
- }
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
-
- // Since we update the position of children based on the width of the parent and this view
- // recompute these changes with the new view size
- onTaskViewSizeChanged(mTaskViewRect.width(), mTaskViewRect.height());
- }
-
- /**
- * Called when the task view frame changes, allowing us to move the contents of the header
- * to match the frame changes.
- */
- public void onTaskViewSizeChanged(int width, int height) {
- mTaskViewRect.set(0, 0, width, height);
-
- boolean showTitle = true;
- boolean showMoveIcon = true;
- boolean showDismissIcon = true;
- int rightInset = width - getMeasuredWidth();
-
- mTitleView.setVisibility(showTitle ? View.VISIBLE : View.INVISIBLE);
- if (mMoveTaskButton != null) {
- mMoveTaskButton.setVisibility(showMoveIcon ? View.VISIBLE : View.INVISIBLE);
- mMoveTaskButton.setTranslationX(rightInset);
- }
- mDismissButton.setVisibility(showDismissIcon ? View.VISIBLE : View.INVISIBLE);
- mDismissButton.setTranslationX(rightInset);
-
- setLeftTopRightBottom(0, 0, width, getMeasuredHeight());
- }
-
- @Override
- public void onDrawForeground(Canvas canvas) {
- super.onDrawForeground(canvas);
-
- // Draw the dim layer with the rounded corners
- canvas.drawRoundRect(0, 0, mTaskViewRect.width(), getHeight() + mCornerRadius,
- mCornerRadius, mCornerRadius, mDimLayerPaint);
- }
-
- /** Starts the focus timer. */
- public void startFocusTimerIndicator(int duration) {
- if (mFocusTimerIndicator == null) {
- return;
- }
-
- mFocusTimerIndicator.setVisibility(View.VISIBLE);
- mFocusTimerIndicator.setMax(duration);
- mFocusTimerIndicator.setProgress(duration);
- if (mFocusTimerCountDown != null) {
- mFocusTimerCountDown.cancel();
- }
- mFocusTimerCountDown = new CountDownTimer(duration,
- FOCUS_INDICATOR_INTERVAL_MS) {
- public void onTick(long millisUntilFinished) {
- mFocusTimerIndicator.setProgress((int) millisUntilFinished);
- }
-
- public void onFinish() {
- // Do nothing
- }
- }.start();
- }
-
- /** Cancels the focus timer. */
- public void cancelFocusTimerIndicator() {
- if (mFocusTimerIndicator == null) {
- return;
- }
-
- if (mFocusTimerCountDown != null) {
- mFocusTimerCountDown.cancel();
- mFocusTimerIndicator.setProgress(0);
- mFocusTimerIndicator.setVisibility(View.INVISIBLE);
- }
- }
-
- /** Only exposed for the workaround for b/27815919. */
- public ImageView getIconView() {
- return mIconView;
- }
-
- /** Returns the secondary color for a primary color. */
- int getSecondaryColor(int primaryColor, boolean useLightOverlayColor) {
- int overlayColor = useLightOverlayColor ? Color.WHITE : Color.BLACK;
- return Utilities.getColorWithOverlay(primaryColor, overlayColor, 0.8f);
- }
-
- /**
- * Sets the dim alpha, only used when we are not using hardware layers.
- * (see RecentsConfiguration.useHardwareLayers)
- */
- public void setDimAlpha(float dimAlpha) {
- if (Float.compare(mDimAlpha, dimAlpha) != 0) {
- mDimAlpha = dimAlpha;
- mTitleView.setAlpha(1f - dimAlpha);
- updateBackgroundColor(mBackground.getColor(), dimAlpha);
- }
- }
-
- /**
- * Updates the background and highlight colors for this header.
- */
- private void updateBackgroundColor(int color, float dimAlpha) {
- if (mTask != null) {
- mBackground.setColorAndDim(color, dimAlpha);
- // TODO: Consider using the saturation of the color to adjust the lightness as well
- ColorUtils.colorToHSL(color, mTmpHSL);
- mTmpHSL[2] = Math.min(1f, mTmpHSL[2] + OVERLAY_LIGHTNESS_INCREMENT * (1.0f - dimAlpha));
- mOverlayBackground.setColorAndDim(ColorUtils.HSLToColor(mTmpHSL), dimAlpha);
- mDimLayerPaint.setAlpha((int) (dimAlpha * 255));
- invalidate();
- }
- }
-
- /**
- * Sets whether the background color should be darkened to differentiate from the primary color.
- */
- public void setShouldDarkenBackgroundColor(boolean flag) {
- mShouldDarkenBackgroundColor = flag;
- }
-
- /**
- * Binds the bar view to the task.
- */
- public void bindToTask(Task t, boolean touchExplorationEnabled, boolean disabledInSafeMode) {
- mTask = t;
-
- int primaryColor = disabledInSafeMode
- ? mDisabledTaskBarBackgroundColor
- : t.colorPrimary;
- if (mBackground.getColor() != primaryColor) {
- updateBackgroundColor(primaryColor, mDimAlpha);
- }
- if (!mTitleView.getText().toString().equals(t.title)) {
- mTitleView.setText(t.title);
- }
- mTitleView.setContentDescription(t.titleDescription);
- mTitleView.setTextColor(t.useLightOnPrimaryColor ?
- mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
- mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
- mLightDismissDrawable : mDarkDismissDrawable);
- mDismissButton.setContentDescription(String.format(mDismissDescFormat, t.titleDescription));
- mDismissButton.setOnClickListener(this);
- mDismissButton.setClickable(false);
- ((RippleDrawable) mDismissButton.getBackground()).setForceSoftware(true);
-
- // In accessibility, a single click on the focused app info button will show it
- if (touchExplorationEnabled) {
- mIconView.setContentDescription(String.format(mAppInfoDescFormat, t.titleDescription));
- mIconView.setOnClickListener(this);
- mIconView.setClickable(true);
- }
- }
-
- /**
- * Called when the bound task's data has loaded and this view should update to reflect the
- * changes.
- */
- public void onTaskDataLoaded() {
- if (mTask != null && mTask.icon != null) {
- mIconView.setImageDrawable(mTask.icon);
- }
- }
-
- /** Unbinds the bar view from the task */
- void unbindFromTask(boolean touchExplorationEnabled) {
- mTask = null;
- mIconView.setImageDrawable(null);
- if (touchExplorationEnabled) {
- mIconView.setClickable(false);
- }
- }
-
- /** Animates this task bar if the user does not interact with the stack after a certain time. */
- void startNoUserInteractionAnimation() {
- int duration = getResources().getInteger(R.integer.recents_task_enter_from_app_duration);
- mDismissButton.setVisibility(View.VISIBLE);
- mDismissButton.setClickable(true);
- if (mDismissButton.getVisibility() == VISIBLE) {
- mDismissButton.animate()
- .alpha(1f)
- .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
- .setDuration(duration)
- .start();
- } else {
- mDismissButton.setAlpha(1f);
- }
- if (mMoveTaskButton != null) {
- if (mMoveTaskButton.getVisibility() == VISIBLE) {
- mMoveTaskButton.setVisibility(View.VISIBLE);
- mMoveTaskButton.setClickable(true);
- mMoveTaskButton.animate()
- .alpha(1f)
- .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
- .setDuration(duration)
- .start();
- } else {
- mMoveTaskButton.setAlpha(1f);
- }
- }
- }
-
- /**
- * Mark this task view that the user does has not interacted with the stack after a certain
- * time.
- */
- public void setNoUserInteractionState() {
- mDismissButton.setVisibility(View.VISIBLE);
- mDismissButton.animate().cancel();
- mDismissButton.setAlpha(1f);
- mDismissButton.setClickable(true);
- if (mMoveTaskButton != null) {
- mMoveTaskButton.setVisibility(View.VISIBLE);
- mMoveTaskButton.animate().cancel();
- mMoveTaskButton.setAlpha(1f);
- mMoveTaskButton.setClickable(true);
- }
- }
-
- /**
- * Resets the state tracking that the user has not interacted with the stack after a certain
- * time.
- */
- void resetNoUserInteractionState() {
- mDismissButton.setVisibility(View.INVISIBLE);
- mDismissButton.setAlpha(0f);
- mDismissButton.setClickable(false);
- if (mMoveTaskButton != null) {
- mMoveTaskButton.setVisibility(View.INVISIBLE);
- mMoveTaskButton.setAlpha(0f);
- mMoveTaskButton.setClickable(false);
- }
- }
-
- @Override
- protected int[] onCreateDrawableState(int extraSpace) {
-
- // Don't forward our state to the drawable - we do it manually in onTaskViewFocusChanged.
- // This is to prevent layer trashing when the view is pressed.
- return new int[] {};
- }
-
- @Override
- public void onClick(View v) {
- if (v == mIconView) {
- // In accessibility, a single click on the focused app info button will show it
- EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
- } else if (v == mDismissButton) {
- TaskView tv = Utilities.findParent(this, TaskView.class);
- tv.dismissTask();
-
- // Keep track of deletions by the dismiss button
- MetricsLogger.histogram(getContext(), "overview_task_dismissed_source",
- Constants.Metrics.DismissSourceHeaderButton);
- } else if (v == mMoveTaskButton) {
- TaskView tv = Utilities.findParent(this, TaskView.class);
- EventBus.getDefault().send(new LaunchTaskEvent(tv, mTask, null, false,
- mTaskWindowingMode, ACTIVITY_TYPE_UNDEFINED));
- } else if (v == mAppInfoView) {
- EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
- } else if (v == mAppIconView) {
- hideAppOverlay(false /* immediate */);
- }
- }
-
- @Override
- public boolean onLongClick(View v) {
- if (v == mIconView) {
- showAppOverlay();
- return true;
- } else if (v == mAppIconView) {
- hideAppOverlay(false /* immediate */);
- return true;
- }
- return false;
- }
-
- /**
- * Shows the application overlay.
- */
- private void showAppOverlay() {
- // Skip early if the task is invalid
- SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
- ComponentName cn = mTask.key.getComponent();
- int userId = mTask.key.userId;
- ActivityInfo activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(cn, userId);
- if (activityInfo == null) {
- return;
- }
-
- // Inflate the overlay if necessary
- if (mAppOverlayView == null) {
- mAppOverlayView = (FrameLayout) Utilities.findViewStubById(this,
- R.id.app_overlay_stub).inflate();
- mAppOverlayView.setBackground(mOverlayBackground);
- mAppIconView = (ImageView) mAppOverlayView.findViewById(R.id.app_icon);
- mAppIconView.setOnClickListener(this);
- mAppIconView.setOnLongClickListener(this);
- mAppInfoView = (ImageView) mAppOverlayView.findViewById(R.id.app_info);
- mAppInfoView.setOnClickListener(this);
- mAppTitleView = (TextView) mAppOverlayView.findViewById(R.id.app_title);
- updateLayoutParams(mAppIconView, mAppTitleView, null, mAppInfoView);
- }
-
- // Update the overlay contents for the current app
- mAppTitleView.setText(ActivityManagerWrapper.getInstance().getBadgedApplicationLabel(
- activityInfo.applicationInfo, userId));
- mAppTitleView.setTextColor(mTask.useLightOnPrimaryColor ?
- mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
- mAppIconView.setImageDrawable(getIconDrawableFactory().getBadgedIcon(
- activityInfo.applicationInfo, userId));
- mAppInfoView.setImageDrawable(mTask.useLightOnPrimaryColor
- ? mLightInfoIcon
- : mDarkInfoIcon);
- mAppOverlayView.setVisibility(View.VISIBLE);
-
- int x = mIconView.getLeft() + mIconView.getWidth() / 2;
- int y = mIconView.getTop() + mIconView.getHeight() / 2;
- Animator revealAnim = ViewAnimationUtils.createCircularReveal(mAppOverlayView, x, y, 0,
- getWidth());
- revealAnim.setDuration(OVERLAY_REVEAL_DURATION);
- revealAnim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- revealAnim.start();
- }
-
- /**
- * Hide the application overlay.
- */
- private void hideAppOverlay(boolean immediate) {
- // Skip if we haven't even loaded the overlay yet
- if (mAppOverlayView == null) {
- return;
- }
-
- if (immediate) {
- mAppOverlayView.setVisibility(View.GONE);
- } else {
- int x = mIconView.getLeft() + mIconView.getWidth() / 2;
- int y = mIconView.getTop() + mIconView.getHeight() / 2;
- Animator revealAnim = ViewAnimationUtils.createCircularReveal(mAppOverlayView, x, y,
- getWidth(), 0);
- revealAnim.setDuration(OVERLAY_REVEAL_DURATION);
- revealAnim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- revealAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mAppOverlayView.setVisibility(View.GONE);
- }
- });
- revealAnim.start();
- }
- }
-
- private static IconDrawableFactory getIconDrawableFactory() {
- if (sDrawableFactory == null) {
- sDrawableFactory = IconDrawableFactory.newInstance(AppGlobals.getInitialApplication());
- }
- return sDrawableFactory;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewThumbnail.java
deleted file mode 100644
index 68f85a50a9d6..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ /dev/null
@@ -1,392 +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 com.android.systemui.recents.views;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.LightingColorFilter;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.Shader;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewDebug;
-
-import com.android.systemui.R;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.ui.TaskSnapshotChangedEvent;
-import com.android.systemui.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import java.io.PrintWriter;
-
-
-/**
- * The task thumbnail view. It implements an image view that allows for animating the dim and
- * alpha of the thumbnail image.
- */
-public class TaskViewThumbnail extends View {
-
- private static final ColorMatrix TMP_FILTER_COLOR_MATRIX = new ColorMatrix();
- private static final ColorMatrix TMP_BRIGHTNESS_COLOR_MATRIX = new ColorMatrix();
-
- private Task mTask;
-
- private int mDisplayOrientation = Configuration.ORIENTATION_UNDEFINED;
- private Rect mDisplayRect = new Rect();
-
- // Drawing
- @ViewDebug.ExportedProperty(category="recents")
- protected Rect mTaskViewRect = new Rect();
- @ViewDebug.ExportedProperty(category="recents")
- protected Rect mThumbnailRect = new Rect();
- @ViewDebug.ExportedProperty(category="recents")
- protected float mThumbnailScale;
- private float mFullscreenThumbnailScale = 1f;
- /** The height, in pixels, of the task view's title bar. */
- private int mTitleBarHeight;
- private boolean mSizeToFit = false;
- private boolean mOverlayHeaderOnThumbnailActionBar = true;
- private ThumbnailData mThumbnailData;
-
- protected int mCornerRadius;
- @ViewDebug.ExportedProperty(category="recents")
- private float mDimAlpha;
- private Matrix mMatrix = new Matrix();
- private Paint mDrawPaint = new Paint();
- protected Paint mLockedPaint = new Paint();
- protected Paint mBgFillPaint = new Paint();
- protected BitmapShader mBitmapShader;
- protected boolean mUserLocked = false;
- private LightingColorFilter mLightingColorFilter = new LightingColorFilter(0xffffffff, 0);
-
- // Clip the top of the thumbnail against the opaque header bar that overlaps this view
- private View mTaskBar;
-
- // Visibility optimization, if the thumbnail height is less than the height of the header
- // bar for the task view, then just mark this thumbnail view as invisible
- @ViewDebug.ExportedProperty(category="recents")
- private boolean mInvisible;
-
- @ViewDebug.ExportedProperty(category="recents")
- private boolean mDisabledInSafeMode;
-
- public TaskViewThumbnail(Context context) {
- this(context, null);
- }
-
- public TaskViewThumbnail(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public TaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public TaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- mDrawPaint.setColorFilter(mLightingColorFilter);
- mDrawPaint.setFilterBitmap(true);
- mDrawPaint.setAntiAlias(true);
- Resources res = getResources();
- mCornerRadius = res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
- mBgFillPaint.setColor(Color.WHITE);
- mLockedPaint.setColor(Color.WHITE);
- mTitleBarHeight = res.getDimensionPixelSize(R.dimen.recents_grid_task_view_header_height);
- }
-
- /**
- * Called when the task view frame changes, allowing us to move the contents of the header
- * to match the frame changes.
- */
- public void onTaskViewSizeChanged(int width, int height) {
- // Return early if the bounds have not changed
- if (mTaskViewRect.width() == width && mTaskViewRect.height() == height) {
- return;
- }
-
- mTaskViewRect.set(0, 0, width, height);
- setLeftTopRightBottom(0, 0, width, height);
- updateThumbnailMatrix();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- if (mInvisible) {
- return;
- }
-
- int viewWidth = mTaskViewRect.width();
- int viewHeight = mTaskViewRect.height();
- int thumbnailWidth = Math.min(viewWidth,
- (int) (mThumbnailRect.width() * mThumbnailScale));
- int thumbnailHeight = Math.min(viewHeight,
- (int) (mThumbnailRect.height() * mThumbnailScale));
-
- if (mUserLocked) {
- canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius,
- mLockedPaint);
- } else if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) {
- int topOffset = 0;
- if (mTaskBar != null && mOverlayHeaderOnThumbnailActionBar) {
- topOffset = mTaskBar.getHeight() - mCornerRadius;
- }
-
- // Draw the background, there will be some small overdraw with the thumbnail
- if (thumbnailWidth < viewWidth) {
- // Portrait thumbnail on a landscape task view
- canvas.drawRoundRect(Math.max(0, thumbnailWidth - mCornerRadius), topOffset,
- viewWidth, viewHeight,
- mCornerRadius, mCornerRadius, mBgFillPaint);
- }
- if (thumbnailHeight < viewHeight) {
- // Landscape thumbnail on a portrait task view
- canvas.drawRoundRect(0, Math.max(topOffset, thumbnailHeight - mCornerRadius),
- viewWidth, viewHeight,
- mCornerRadius, mCornerRadius, mBgFillPaint);
- }
-
- // Draw the thumbnail
- canvas.drawRoundRect(0, topOffset, thumbnailWidth, thumbnailHeight,
- mCornerRadius, mCornerRadius, mDrawPaint);
- } else {
- canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius,
- mBgFillPaint);
- }
- }
-
- /** Sets the thumbnail to a given bitmap. */
- void setThumbnail(ThumbnailData thumbnailData) {
- if (thumbnailData != null && thumbnailData.thumbnail != null) {
- Bitmap bm = thumbnailData.thumbnail;
- bm.prepareToDraw();
- mFullscreenThumbnailScale = thumbnailData.scale;
- mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
- mDrawPaint.setShader(mBitmapShader);
- mThumbnailRect.set(0, 0,
- bm.getWidth() - thumbnailData.insets.left - thumbnailData.insets.right,
- bm.getHeight() - thumbnailData.insets.top - thumbnailData.insets.bottom);
- mThumbnailData = thumbnailData;
- updateThumbnailMatrix();
- updateThumbnailPaintFilter();
- } else {
- mBitmapShader = null;
- mDrawPaint.setShader(null);
- mThumbnailRect.setEmpty();
- mThumbnailData = null;
- }
- }
-
- /** Updates the paint to draw the thumbnail. */
- void updateThumbnailPaintFilter() {
- if (mInvisible) {
- return;
- }
- int mul = (int) ((1.0f - mDimAlpha) * 255);
- if (mBitmapShader != null) {
- if (mDisabledInSafeMode) {
- // Brightness: C-new = C-old*(1-amount) + amount
- TMP_FILTER_COLOR_MATRIX.setSaturation(0);
- float scale = 1f - mDimAlpha;
- float[] mat = TMP_BRIGHTNESS_COLOR_MATRIX.getArray();
- mat[0] = scale;
- mat[6] = scale;
- mat[12] = scale;
- mat[4] = mDimAlpha * 255f;
- mat[9] = mDimAlpha * 255f;
- mat[14] = mDimAlpha * 255f;
- TMP_FILTER_COLOR_MATRIX.preConcat(TMP_BRIGHTNESS_COLOR_MATRIX);
- ColorMatrixColorFilter filter = new ColorMatrixColorFilter(TMP_FILTER_COLOR_MATRIX);
- mDrawPaint.setColorFilter(filter);
- mBgFillPaint.setColorFilter(filter);
- mLockedPaint.setColorFilter(filter);
- } else {
- mLightingColorFilter.setColorMultiply(Color.argb(255, mul, mul, mul));
- mDrawPaint.setColorFilter(mLightingColorFilter);
- mDrawPaint.setColor(0xFFffffff);
- mBgFillPaint.setColorFilter(mLightingColorFilter);
- mLockedPaint.setColorFilter(mLightingColorFilter);
- }
- } else {
- int grey = mul;
- mDrawPaint.setColorFilter(null);
- mDrawPaint.setColor(Color.argb(255, grey, grey, grey));
- }
- if (!mInvisible) {
- invalidate();
- }
- }
-
- /**
- * Updates the scale of the bitmap relative to this view.
- */
- public void updateThumbnailMatrix() {
- mThumbnailScale = 1f;
- if (mBitmapShader != null && mThumbnailData != null) {
- if (mTaskViewRect.isEmpty()) {
- // If we haven't measured , skip the thumbnail drawing and only draw the background
- // color
- mThumbnailScale = 0f;
- } else if (mSizeToFit) {
- // Make sure we fill the entire space regardless of the orientation.
- float viewAspectRatio = (float) mTaskViewRect.width() /
- (float) (mTaskViewRect.height() - mTitleBarHeight);
- float thumbnailAspectRatio =
- (float) mThumbnailRect.width() / (float) mThumbnailRect.height();
- if (viewAspectRatio > thumbnailAspectRatio) {
- mThumbnailScale =
- (float) mTaskViewRect.width() / (float) mThumbnailRect.width();
- } else {
- mThumbnailScale = (float) (mTaskViewRect.height() - mTitleBarHeight)
- / (float) mThumbnailRect.height();
- }
- } else {
- float invThumbnailScale = 1f / mFullscreenThumbnailScale;
- if (mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT) {
- if (mThumbnailData.orientation == Configuration.ORIENTATION_PORTRAIT) {
- // If we are in the same orientation as the screenshot, just scale it to the
- // width of the task view
- mThumbnailScale = (float) mTaskViewRect.width() / mThumbnailRect.width();
- } else {
- // Scale the landscape thumbnail up to app size, then scale that to the task
- // view size to match other portrait screenshots
- mThumbnailScale = invThumbnailScale *
- ((float) mTaskViewRect.width() / mDisplayRect.width());
- }
- } else {
- // Otherwise, scale the screenshot to fit 1:1 in the current orientation
- mThumbnailScale = invThumbnailScale;
- }
- }
- mMatrix.setTranslate(-mThumbnailData.insets.left * mFullscreenThumbnailScale,
- -mThumbnailData.insets.top * mFullscreenThumbnailScale);
- mMatrix.postScale(mThumbnailScale, mThumbnailScale);
- mBitmapShader.setLocalMatrix(mMatrix);
- }
- if (!mInvisible) {
- invalidate();
- }
- }
-
- /** Sets whether the thumbnail should be resized to fit the task view in all orientations. */
- public void setSizeToFit(boolean flag) {
- mSizeToFit = flag;
- }
-
- /**
- * Sets whether the header should overlap (and hide) the action bar in the thumbnail, or
- * be stacked just above it.
- */
- public void setOverlayHeaderOnThumbnailActionBar(boolean flag) {
- mOverlayHeaderOnThumbnailActionBar = flag;
- }
-
- /** Updates the clip rect based on the given task bar. */
- void updateClipToTaskBar(View taskBar) {
- mTaskBar = taskBar;
- invalidate();
- }
-
- /** Updates the visibility of the the thumbnail. */
- void updateThumbnailVisibility(int clipBottom) {
- boolean invisible = mTaskBar != null && (getHeight() - clipBottom) <= mTaskBar.getHeight();
- if (invisible != mInvisible) {
- mInvisible = invisible;
- if (!mInvisible) {
- updateThumbnailPaintFilter();
- }
- }
- }
-
- /**
- * Sets the dim alpha, only used when we are not using hardware layers.
- * (see RecentsConfiguration.useHardwareLayers)
- */
- public void setDimAlpha(float dimAlpha) {
- mDimAlpha = dimAlpha;
- updateThumbnailPaintFilter();
- }
-
- /**
- * Returns the {@link Paint} used to draw a task screenshot, or {@link #mLockedPaint} if the
- * thumbnail shouldn't be drawn because it belongs to a locked user.
- */
- protected Paint getDrawPaint() {
- if (mUserLocked) {
- return mLockedPaint;
- }
- return mDrawPaint;
- }
-
- /**
- * Binds the thumbnail view to the task.
- */
- void bindToTask(Task t, boolean disabledInSafeMode, int displayOrientation, Rect displayRect) {
- mTask = t;
- mDisabledInSafeMode = disabledInSafeMode;
- mDisplayOrientation = displayOrientation;
- mDisplayRect.set(displayRect);
- if (t.colorBackground != 0) {
- mBgFillPaint.setColor(t.colorBackground);
- }
- if (t.colorPrimary != 0) {
- mLockedPaint.setColor(t.colorPrimary);
- }
- mUserLocked = t.isLocked;
- EventBus.getDefault().register(this);
- }
-
- /**
- * Called when the bound task's data has loaded and this view should update to reflect the
- * changes.
- */
- void onTaskDataLoaded(ThumbnailData thumbnailData) {
- setThumbnail(thumbnailData);
- }
-
- /** Unbinds the thumbnail view from the task */
- void unbindFromTask() {
- mTask = null;
- setThumbnail(null);
- EventBus.getDefault().unregister(this);
- }
-
- public final void onBusEvent(TaskSnapshotChangedEvent event) {
- if (mTask == null || event.taskId != mTask.key.id || event.thumbnailData == null
- || event.thumbnailData.thumbnail == null) {
- return;
- }
- setThumbnail(event.thumbnailData);
- }
-
- public void dump(String prefix, PrintWriter writer) {
- writer.print(prefix); writer.print("TaskViewThumbnail");
- writer.print(" mTaskViewRect="); writer.print(Utilities.dumpRect(mTaskViewRect));
- writer.print(" mThumbnailRect="); writer.print(Utilities.dumpRect(mThumbnailRect));
- writer.print(" mThumbnailScale="); writer.print(mThumbnailScale);
- writer.print(" mDimAlpha="); writer.print(mDimAlpha);
- writer.println();
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewTransform.java
deleted file mode 100644
index 48a733663fad..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ /dev/null
@@ -1,203 +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 com.android.systemui.recents.views;
-
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.util.Property;
-import android.view.View;
-
-import com.android.systemui.recents.utilities.AnimationProps;
-import com.android.systemui.recents.utilities.Utilities;
-
-import java.util.ArrayList;
-
-/**
- * The visual properties for a {@link TaskView}.
- */
-public class TaskViewTransform {
-
- public static final Property<View, Rect> LTRB =
- new Property<View, Rect>(Rect.class, "leftTopRightBottom") {
-
- private Rect mTmpRect = new Rect();
-
- @Override
- public void set(View v, Rect ltrb) {
- v.setLeftTopRightBottom(ltrb.left, ltrb.top, ltrb.right, ltrb.bottom);
- }
-
- @Override
- public Rect get(View v) {
- mTmpRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
- return mTmpRect;
- }
- };
-
- public float translationZ = 0;
- public float scale = 1f;
- public float alpha = 1f;
- public float dimAlpha = 0f;
- public float viewOutlineAlpha = 0f;
-
- public boolean visible = false;
-
- // This is a window-space rect used for positioning the task in the stack
- public RectF rect = new RectF();
-
- /**
- * Fills int this transform from the state of the given TaskView.
- */
- public void fillIn(TaskView tv) {
- translationZ = tv.getTranslationZ();
- scale = tv.getScaleX();
- alpha = tv.getAlpha();
- visible = true;
- dimAlpha = tv.getDimAlpha();
- viewOutlineAlpha = tv.getViewBounds().getAlpha();
- rect.set(tv.getLeft(), tv.getTop(), tv.getRight(), tv.getBottom());
- }
-
- /**
- * Copies the transform state from another {@link TaskViewTransform}.
- */
- public void copyFrom(TaskViewTransform other) {
- translationZ = other.translationZ;
- scale = other.scale;
- alpha = other.alpha;
- visible = other.visible;
- dimAlpha = other.dimAlpha;
- viewOutlineAlpha = other.viewOutlineAlpha;
- rect.set(other.rect);
- }
-
- /**
- * @return whether {@param other} is the same transform as this
- */
- public boolean isSame(TaskViewTransform other) {
- return translationZ == other.translationZ
- && scale == other.scale
- && other.alpha == alpha
- && dimAlpha == other.dimAlpha
- && visible == other.visible
- && rect.equals(other.rect);
- }
-
- /**
- * Resets the current transform.
- */
- public void reset() {
- translationZ = 0;
- scale = 1f;
- alpha = 1f;
- dimAlpha = 0f;
- viewOutlineAlpha = 0f;
- visible = false;
- rect.setEmpty();
- }
-
- /** Convenience functions to compare against current property values */
- public boolean hasAlphaChangedFrom(float v) {
- return (Float.compare(alpha, v) != 0);
- }
-
- public boolean hasScaleChangedFrom(float v) {
- return (Float.compare(scale, v) != 0);
- }
-
- public boolean hasTranslationZChangedFrom(float v) {
- return (Float.compare(translationZ, v) != 0);
- }
-
- public boolean hasRectChangedFrom(View v) {
- return ((int) rect.left != v.getLeft()) || ((int) rect.right != v.getRight()) ||
- ((int) rect.top != v.getTop()) || ((int) rect.bottom != v.getBottom());
- }
-
- /**
- * Applies this transform to a view.
- */
- public void applyToTaskView(TaskView v, ArrayList<Animator> animators,
- AnimationProps animation, boolean allowShadows) {
- // Return early if not visible
- if (!visible) {
- return;
- }
-
- if (animation.isImmediate()) {
- if (allowShadows && hasTranslationZChangedFrom(v.getTranslationZ())) {
- v.setTranslationZ(translationZ);
- }
- if (hasScaleChangedFrom(v.getScaleX())) {
- v.setScaleX(scale);
- v.setScaleY(scale);
- }
- if (hasAlphaChangedFrom(v.getAlpha())) {
- v.setAlpha(alpha);
- }
- if (hasRectChangedFrom(v)) {
- v.setLeftTopRightBottom((int) rect.left, (int) rect.top, (int) rect.right,
- (int) rect.bottom);
- }
- } else {
- if (allowShadows && hasTranslationZChangedFrom(v.getTranslationZ())) {
- ObjectAnimator anim = ObjectAnimator.ofFloat(v, View.TRANSLATION_Z,
- v.getTranslationZ(), translationZ);
- animators.add(animation.apply(AnimationProps.TRANSLATION_Z, anim));
- }
- if (hasScaleChangedFrom(v.getScaleX())) {
- ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(v,
- PropertyValuesHolder.ofFloat(View.SCALE_X, v.getScaleX(), scale),
- PropertyValuesHolder.ofFloat(View.SCALE_Y, v.getScaleX(), scale));
- animators.add(animation.apply(AnimationProps.SCALE, anim));
- }
- if (hasAlphaChangedFrom(v.getAlpha())) {
- ObjectAnimator anim = ObjectAnimator.ofFloat(v, View.ALPHA, v.getAlpha(), alpha);
- animators.add(animation.apply(AnimationProps.ALPHA, anim));
- }
- if (hasRectChangedFrom(v)) {
- Rect fromViewRect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
- Rect toViewRect = new Rect();
- rect.round(toViewRect);
- ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(v,
- PropertyValuesHolder.ofObject(LTRB, Utilities.RECT_EVALUATOR,
- fromViewRect, toViewRect));
- animators.add(animation.apply(AnimationProps.BOUNDS, anim));
- }
- }
- }
-
- /** Reset the transform on a view. */
- public static void reset(TaskView v) {
- v.setTranslationX(0f);
- v.setTranslationY(0f);
- v.setTranslationZ(0f);
- v.setScaleX(1f);
- v.setScaleY(1f);
- v.setAlpha(1f);
- v.getViewBounds().setClipBottom(0);
- v.setLeftTopRightBottom(0, 0, 0, 0);
- }
-
- @Override
- public String toString() {
- return "R: " + rect + " V: " + visible;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/ViewPool.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/ViewPool.java
deleted file mode 100644
index a287fe642002..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/ViewPool.java
+++ /dev/null
@@ -1,86 +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 com.android.systemui.recents.views;
-
-import android.content.Context;
-
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-
-
-/* A view pool to manage more views than we can visibly handle */
-public class ViewPool<V, T> {
-
- /* An interface to the consumer of a view pool */
- public interface ViewPoolConsumer<V, T> {
- public V createView(Context context);
- public void onReturnViewToPool(V v);
- public void onPickUpViewFromPool(V v, T prepareData, boolean isNewView);
- public boolean hasPreferredData(V v, T preferredData);
- }
-
- Context mContext;
- ViewPoolConsumer<V, T> mViewCreator;
- LinkedList<V> mPool = new LinkedList<V>();
-
- /** Initializes the pool with a fixed predetermined pool size */
- public ViewPool(Context context, ViewPoolConsumer<V, T> viewCreator) {
- mContext = context;
- mViewCreator = viewCreator;
- }
-
- /** Returns a view into the pool */
- void returnViewToPool(V v) {
- mViewCreator.onReturnViewToPool(v);
- mPool.push(v);
- }
-
- /** Gets a view from the pool and prepares it */
- V pickUpViewFromPool(T preferredData, T prepareData) {
- V v = null;
- boolean isNewView = false;
- if (mPool.isEmpty()) {
- v = mViewCreator.createView(mContext);
- isNewView = true;
- } else {
- // Try and find a preferred view
- Iterator<V> iter = mPool.iterator();
- while (iter.hasNext()) {
- V vpv = iter.next();
- if (mViewCreator.hasPreferredData(vpv, preferredData)) {
- v = vpv;
- iter.remove();
- break;
- }
- }
- // Otherwise, just grab the first view
- if (v == null) {
- v = mPool.pop();
- }
- }
- mViewCreator.onPickUpViewFromPool(v, prepareData, isNewView);
- return v;
- }
-
- /**
- * Returns the list of views in the pool.
- */
- List<V> getViews() {
- return mPool;
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/AnimateableGridViewBounds.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/AnimateableGridViewBounds.java
deleted file mode 100644
index a029478c2045..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/AnimateableGridViewBounds.java
+++ /dev/null
@@ -1,32 +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.recents.views.grid;
-
-import android.view.View;
-import com.android.systemui.recents.views.AnimateableViewBounds;
-
-/* An outline provider for grid-based task views. */
-class AnimateableGridViewBounds extends AnimateableViewBounds {
-
- public AnimateableGridViewBounds(View source, int cornerRadius) {
- super(source, cornerRadius);
- }
-
- @Override
- protected void updateClipBounds() {
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/GridTaskView.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/GridTaskView.java
deleted file mode 100644
index 8b4700c54b00..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/GridTaskView.java
+++ /dev/null
@@ -1,72 +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.recents.views.grid;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import com.android.systemui.R;
-import com.android.systemui.recents.views.AnimateableViewBounds;
-import com.android.systemui.recents.views.TaskView;
-
-public class GridTaskView extends TaskView {
-
- /** The height, in pixels, of the header view. */
- private int mHeaderHeight;
-
- public GridTaskView(Context context) {
- this(context, null);
- }
-
- public GridTaskView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public GridTaskView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public GridTaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- mHeaderHeight = context.getResources().getDimensionPixelSize(
- R.dimen.recents_grid_task_view_header_height);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- // Show the full thumbnail and don't overlap with the header.
- mThumbnailView.setSizeToFit(true);
- mThumbnailView.setOverlayHeaderOnThumbnailActionBar(false);
- mThumbnailView.updateThumbnailMatrix();
- mThumbnailView.setTranslationY(mHeaderHeight);
- mHeaderView.setShouldDarkenBackgroundColor(true);
- }
-
- @Override
- protected AnimateableViewBounds createOutlineProvider() {
- return new AnimateableGridViewBounds(this, mContext.getResources().getDimensionPixelSize(
- R.dimen.recents_task_view_shadow_rounded_corners_radius));
- }
-
- @Override
- protected void onConfigurationChanged() {
- super.onConfigurationChanged();
- mHeaderHeight = mContext.getResources().getDimensionPixelSize(
- R.dimen.recents_grid_task_view_header_height);
- mThumbnailView.setTranslationY(mHeaderHeight);
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java
deleted file mode 100644
index 2d7cfb1ab167..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.views.grid;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Path;
-import android.util.AttributeSet;
-
-import com.android.systemui.R;
-import com.android.systemui.recents.views.TaskViewThumbnail;
-
-public class GridTaskViewThumbnail extends TaskViewThumbnail {
-
- private final Path mThumbnailOutline = new Path();
- private final Path mRestBackgroundOutline = new Path();
- // True if either this view's size or thumbnail scale has changed and mThumbnailOutline should
- // be updated.
- private boolean mUpdateThumbnailOutline = true;
-
- public GridTaskViewThumbnail(Context context) {
- this(context, null);
- }
-
- public GridTaskViewThumbnail(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public GridTaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public GridTaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- mCornerRadius = getResources().getDimensionPixelSize(
- R.dimen.recents_grid_task_view_rounded_corners_radius);
- }
-
- /**
- * Called when the task view frame changes, allowing us to move the contents of the header
- * to match the frame changes.
- */
- public void onTaskViewSizeChanged(int width, int height) {
- mUpdateThumbnailOutline = true;
- super.onTaskViewSizeChanged(width, height);
- }
-
- /**
- * Updates the scale of the bitmap relative to this view.
- */
- public void updateThumbnailMatrix() {
- mUpdateThumbnailOutline = true;
- super.updateThumbnailMatrix();
- }
-
- private void updateThumbnailOutline() {
- final int titleHeight = getResources().getDimensionPixelSize(
- R.dimen.recents_grid_task_view_header_height);
- final int viewWidth = mTaskViewRect.width();
- final int viewHeight = mTaskViewRect.height() - titleHeight;
- final int thumbnailWidth = Math.min(viewWidth,
- (int) (mThumbnailRect.width() * mThumbnailScale));
- final int thumbnailHeight = Math.min(viewHeight,
- (int) (mThumbnailRect.height() * mThumbnailScale));
-
- if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) {
- // Draw the thumbnail, we only round the bottom corners:
- //
- // outerLeft outerRight
- // <-----------------------> mRestBackgroundOutline
- // _________________________ (thumbnailWidth < viewWidth)
- // |_______________________| outerTop A ____ B
- // | | ↑ | |
- // | | | | |
- // | | | | |
- // | | | | | C
- // \_______________________/ ↓ |__/
- // mCornerRadius outerBottom E D
- //
- // mRestBackgroundOutline (thumbnailHeight < viewHeight)
- // A _________________________ B
- // | | C
- // F \_______________________/
- // E D
- final int outerLeft = 0;
- final int outerTop = 0;
- final int outerRight = outerLeft + thumbnailWidth;
- final int outerBottom = outerTop + thumbnailHeight;
- createThumbnailPath(outerLeft, outerTop, outerRight, outerBottom, mThumbnailOutline);
-
- if (thumbnailWidth < viewWidth) {
- final int l = Math.max(0, outerRight - mCornerRadius);
- final int r = outerRight;
- final int t = outerTop;
- final int b = outerBottom;
- mRestBackgroundOutline.reset();
- mRestBackgroundOutline.moveTo(l, t); // A
- mRestBackgroundOutline.lineTo(r, t); // B
- mRestBackgroundOutline.lineTo(r, b - mCornerRadius); // C
- mRestBackgroundOutline.arcTo(r - 2 * mCornerRadius, b - 2 * mCornerRadius, r, b,
- 0, 90, false); // D
- mRestBackgroundOutline.lineTo(l, b); // E
- mRestBackgroundOutline.lineTo(l, t); // A
- mRestBackgroundOutline.close();
-
- }
- if (thumbnailHeight < viewHeight) {
- final int l = outerLeft;
- final int r = outerRight;
- final int t = Math.max(0, thumbnailHeight - mCornerRadius);
- final int b = outerBottom;
- mRestBackgroundOutline.reset();
- mRestBackgroundOutline.moveTo(l, t); // A
- mRestBackgroundOutline.lineTo(r, t); // B
- mRestBackgroundOutline.lineTo(r, b - mCornerRadius); // C
- mRestBackgroundOutline.arcTo(r - 2 * mCornerRadius, b - 2 * mCornerRadius, r, b,
- 0, 90, false); // D
- mRestBackgroundOutline.lineTo(l + mCornerRadius, b); // E
- mRestBackgroundOutline.arcTo(l, b - 2 * mCornerRadius, l + 2 * mCornerRadius, b,
- 90, 90, false); // F
- mRestBackgroundOutline.lineTo(l, t); // A
- mRestBackgroundOutline.close();
- }
- } else {
- createThumbnailPath(0, 0, viewWidth, viewHeight, mThumbnailOutline);
- }
- }
-
- private void createThumbnailPath(int outerLeft, int outerTop, int outerRight, int outerBottom,
- Path outPath) {
- outPath.reset();
- outPath.moveTo(outerLeft, outerTop);
- outPath.lineTo(outerRight, outerTop);
- outPath.lineTo(outerRight, outerBottom - mCornerRadius);
- outPath.arcTo(outerRight - 2 * mCornerRadius, outerBottom - 2 * mCornerRadius, outerRight,
- outerBottom, 0, 90, false);
- outPath.lineTo(outerLeft + mCornerRadius, outerBottom);
- outPath.arcTo(outerLeft, outerBottom - 2 * mCornerRadius, outerLeft + 2 * mCornerRadius,
- outerBottom, 90, 90, false);
- outPath.lineTo(outerLeft, outerTop);
- outPath.close();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- final int titleHeight = getResources().getDimensionPixelSize(
- R.dimen.recents_grid_task_view_header_height);
- final int viewWidth = mTaskViewRect.width();
- final int viewHeight = mTaskViewRect.height() - titleHeight;
- final int thumbnailWidth = Math.min(viewWidth,
- (int) (mThumbnailRect.width() * mThumbnailScale));
- final int thumbnailHeight = Math.min(viewHeight,
- (int) (mThumbnailRect.height() * mThumbnailScale));
-
- if (mUpdateThumbnailOutline) {
- updateThumbnailOutline();
- mUpdateThumbnailOutline = false;
- }
-
- if (mUserLocked) {
- canvas.drawPath(mThumbnailOutline, mLockedPaint);
- } else if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) {
- // Draw the background, there will be some small overdraw with the thumbnail
- if (thumbnailWidth < viewWidth) {
- // Portrait thumbnail on a landscape task view
- canvas.drawPath(mRestBackgroundOutline, mBgFillPaint);
- }
- if (thumbnailHeight < viewHeight) {
- // Landscape thumbnail on a portrait task view
- canvas.drawPath(mRestBackgroundOutline, mBgFillPaint);
- }
- canvas.drawPath(mThumbnailOutline, getDrawPaint());
- } else {
- canvas.drawPath(mThumbnailOutline, mBgFillPaint);
- }
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
deleted file mode 100644
index 719eaa71f788..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
+++ /dev/null
@@ -1,325 +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.recents.views.grid;
-
-import static com.android.systemui.recents.views.TaskStackLayoutAlgorithm.*;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.view.WindowManager;
-
-import com.android.systemui.R;
-import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent.Direction;
-import com.android.systemui.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
-import com.android.systemui.recents.views.TaskViewTransform;
-
-import java.util.ArrayList;
-
-public class TaskGridLayoutAlgorithm {
-
- private final String TAG = "TaskGridLayoutAlgorithm";
- public static final int MAX_LAYOUT_TASK_COUNT = 8;
-
- /** The horizontal padding around the whole recents view. */
- private int mPaddingLeftRight;
- /** The vertical padding around the whole recents view. */
- private int mPaddingTopBottom;
- /** The padding between task views. */
- private int mPaddingTaskView;
-
- private Rect mWindowRect;
- private Point mScreenSize = new Point();
-
- private Rect mTaskGridRect;
-
- /** The height, in pixels, of each task view's title bar. */
- private int mTitleBarHeight;
-
- /** The aspect ratio of each task thumbnail, without the title bar. */
- private float mAppAspectRatio;
- private Rect mSystemInsets = new Rect();
-
- /** The thickness of the focused task view frame. */
- private int mFocusedFrameThickness;
-
- /**
- * When the amount of tasks is determined, the size and position of every task view can be
- * decided. Each instance of TaskGridRectInfo store the task view information for a certain
- * amount of tasks.
- */
- class TaskGridRectInfo {
- Rect size;
- int[] xOffsets;
- int[] yOffsets;
- int tasksPerLine;
- int lines;
-
- TaskGridRectInfo(int taskCount) {
- size = new Rect();
- xOffsets = new int[taskCount];
- yOffsets = new int[taskCount];
-
- int layoutTaskCount = Math.min(MAX_LAYOUT_TASK_COUNT, taskCount);
- tasksPerLine = getTasksPerLine(layoutTaskCount);
- lines = layoutTaskCount < 4 ? 1 : 2;
-
- // A couple of special cases.
- boolean landscapeWindow = mWindowRect.width() > mWindowRect.height();
- boolean landscapeTaskView = mAppAspectRatio > 1;
- // If we're in portrait but task views are landscape, show more lines of fewer tasks.
- if (!landscapeWindow && landscapeTaskView) {
- tasksPerLine = layoutTaskCount < 2 ? 1 : 2;
- lines = layoutTaskCount < 3 ? 1 : (
- layoutTaskCount < 5 ? 2 : (
- layoutTaskCount < 7 ? 3 : 4));
- }
- // If we're in landscape but task views are portrait, show fewer lines of more tasks.
- if (landscapeWindow && !landscapeTaskView) {
- tasksPerLine = layoutTaskCount < 7 ? layoutTaskCount : 6;
- lines = layoutTaskCount < 7 ? 1 : 2;
- }
-
- int taskWidth, taskHeight;
- int maxTaskWidth = (mWindowRect.width() - 2 * mPaddingLeftRight
- - (tasksPerLine - 1) * mPaddingTaskView) / tasksPerLine;
- int maxTaskHeight = (mWindowRect.height() - 2 * mPaddingTopBottom
- - (lines - 1) * mPaddingTaskView) / lines;
-
- if (maxTaskHeight >= maxTaskWidth / mAppAspectRatio + mTitleBarHeight) {
- // Width bound.
- taskWidth = maxTaskWidth;
- // Here we should round the height to the nearest integer.
- taskHeight = (int) (maxTaskWidth / mAppAspectRatio + mTitleBarHeight + 0.5);
- } else {
- // Height bound.
- taskHeight = maxTaskHeight;
- // Here we should round the width to the nearest integer.
- taskWidth = (int) ((taskHeight - mTitleBarHeight) * mAppAspectRatio + 0.5);
- }
- size.set(0, 0, taskWidth, taskHeight);
-
- int emptySpaceX = mWindowRect.width() - 2 * mPaddingLeftRight
- - (tasksPerLine * taskWidth) - (tasksPerLine - 1) * mPaddingTaskView;
- int emptySpaceY = mWindowRect.height() - 2 * mPaddingTopBottom
- - (lines * taskHeight) - (lines - 1) * mPaddingTaskView;
- for (int taskIndex = 0; taskIndex < taskCount; taskIndex++) {
- // We also need to invert the index in order to display the most recent tasks first.
- int taskLayoutIndex = taskCount - taskIndex - 1;
-
- int xIndex = taskLayoutIndex % tasksPerLine;
- int yIndex = taskLayoutIndex / tasksPerLine;
- xOffsets[taskIndex] = mWindowRect.left +
- emptySpaceX / 2 + mPaddingLeftRight + (taskWidth + mPaddingTaskView) * xIndex;
- yOffsets[taskIndex] = mWindowRect.top +
- emptySpaceY / 2 + mPaddingTopBottom + (taskHeight + mPaddingTaskView) * yIndex;
- }
- }
-
- private int getTasksPerLine(int taskCount) {
- switch(taskCount) {
- case 0:
- return 0;
- case 1:
- return 1;
- case 2:
- case 4:
- return 2;
- case 3:
- case 5:
- case 6:
- return 3;
- case 7:
- case 8:
- return 4;
- default:
- throw new IllegalArgumentException("Unsupported task count " + taskCount);
- }
- }
- }
-
- /**
- * We can find task view sizes and positions from mTaskGridRectInfoList[k - 1] when there
- * are k tasks.
- */
- private TaskGridRectInfo[] mTaskGridRectInfoList;
-
- public TaskGridLayoutAlgorithm(Context context) {
- reloadOnConfigurationChange(context);
- }
-
- public void reloadOnConfigurationChange(Context context) {
- Resources res = context.getResources();
- mPaddingTaskView = res.getDimensionPixelSize(R.dimen.recents_grid_padding_task_view);
- mFocusedFrameThickness = res.getDimensionPixelSize(
- R.dimen.recents_grid_task_view_focused_frame_thickness);
-
- mTaskGridRect = new Rect();
- mTitleBarHeight = res.getDimensionPixelSize(R.dimen.recents_grid_task_view_header_height);
-
- WindowManager windowManager = (WindowManager) context
- .getSystemService(Context.WINDOW_SERVICE);
- windowManager.getDefaultDisplay().getRealSize(mScreenSize);
-
- updateAppAspectRatio();
- }
-
- /**
- * Returns the proper task view transform of a certain task view, according to its index and the
- * amount of task views.
- * @param taskIndex The index of the task view whose transform we want. It's never greater
- * than {@link MAX_LAYOUT_TASK_COUNT}.
- * @param taskCount The current amount of task views.
- * @param transformOut The result transform that this method returns.
- * @param stackLayout The base stack layout algorithm.
- * @return The expected transform of the (taskIndex)th task view.
- */
- public TaskViewTransform getTransform(int taskIndex, int taskCount,
- TaskViewTransform transformOut, TaskStackLayoutAlgorithm stackLayout) {
- if (taskCount == 0) {
- transformOut.reset();
- return transformOut;
- }
-
- TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
- mTaskGridRect.set(gridInfo.size);
-
- int x = gridInfo.xOffsets[taskIndex];
- int y = gridInfo.yOffsets[taskIndex];
- float z = stackLayout.mMaxTranslationZ;
-
- // We always set the dim alpha to 0, since we don't want grid task views to dim.
- float dimAlpha = 0f;
- // We always set the alpha of the view outline to 1, to make sure the shadow is visible.
- float viewOutlineAlpha = 1f;
-
- // We also need to invert the index in order to display the most recent tasks first.
- int taskLayoutIndex = taskCount - taskIndex - 1;
- boolean isTaskViewVisible = taskLayoutIndex < MAX_LAYOUT_TASK_COUNT;
-
- // Fill out the transform
- transformOut.scale = 1f;
- transformOut.alpha = isTaskViewVisible ? 1f : 0f;
- transformOut.translationZ = z;
- transformOut.dimAlpha = dimAlpha;
- transformOut.viewOutlineAlpha = viewOutlineAlpha;
- transformOut.rect.set(mTaskGridRect);
- transformOut.rect.offset(x, y);
- Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
- // We only show the 8 most recent tasks.
- transformOut.visible = isTaskViewVisible;
- return transformOut;
- }
-
- /**
- * Return the proper task index to focus for arrow key navigation.
- * @param taskCount The amount of tasks.
- * @param currentFocusedIndex The index of the currently focused task.
- * @param direction The direction we're navigating.
- * @return The index of the task that should get the focus.
- */
- public int navigateFocus(int taskCount, int currentFocusedIndex, Direction direction) {
- if (taskCount < 1 || taskCount > MAX_LAYOUT_TASK_COUNT) {
- return -1;
- }
- if (currentFocusedIndex == -1) {
- return 0;
- }
- int newIndex = currentFocusedIndex;
- final TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
- final int currentLine = (taskCount - 1 - currentFocusedIndex) / gridInfo.tasksPerLine;
- switch (direction) {
- case UP:
- newIndex += gridInfo.tasksPerLine;
- newIndex = newIndex >= taskCount ? currentFocusedIndex : newIndex;
- break;
- case DOWN:
- newIndex -= gridInfo.tasksPerLine;
- newIndex = newIndex < 0 ? currentFocusedIndex : newIndex;
- break;
- case LEFT:
- newIndex++;
- final int leftMostIndex = (taskCount - 1) - currentLine * gridInfo.tasksPerLine;
- newIndex = newIndex > leftMostIndex ? currentFocusedIndex : newIndex;
- break;
- case RIGHT:
- newIndex--;
- int rightMostIndex =
- (taskCount - 1) - (currentLine + 1) * gridInfo.tasksPerLine + 1;
- rightMostIndex = rightMostIndex < 0 ? 0 : rightMostIndex;
- newIndex = newIndex < rightMostIndex ? currentFocusedIndex : newIndex;
- break;
- }
- return newIndex;
- }
-
- public void initialize(Rect windowRect) {
- mWindowRect = windowRect;
- // Define paddings in terms of percentage of the total area.
- mPaddingLeftRight = (int) (0.025f * Math.min(mWindowRect.width(), mWindowRect.height()));
- mPaddingTopBottom = (int) (0.1 * mWindowRect.height());
-
- // Pre-calculate the positions and offsets of task views so that we can reuse them directly
- // in the future.
- mTaskGridRectInfoList = new TaskGridRectInfo[MAX_LAYOUT_TASK_COUNT];
- for (int i = 0; i < MAX_LAYOUT_TASK_COUNT; i++) {
- mTaskGridRectInfoList[i] = new TaskGridRectInfo(i + 1);
- }
- }
-
- public void setSystemInsets(Rect systemInsets) {
- mSystemInsets = systemInsets;
- updateAppAspectRatio();
- }
-
- private void updateAppAspectRatio() {
- int usableWidth = mScreenSize.x - mSystemInsets.left - mSystemInsets.right;
- int usableHeight = mScreenSize.y - mSystemInsets.top - mSystemInsets.bottom;
- mAppAspectRatio = (float) usableWidth / (float) usableHeight;
- }
-
- public Rect getStackActionButtonRect() {
- Rect buttonRect = new Rect(mWindowRect);
- buttonRect.right -= mPaddingLeftRight;
- buttonRect.left += mPaddingLeftRight;
- buttonRect.bottom = buttonRect.top + mPaddingTopBottom;
- return buttonRect;
- }
-
- public void updateTaskGridRect(int taskCount) {
- if (taskCount > 0) {
- TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
- mTaskGridRect.set(gridInfo.size);
- }
- }
-
- public Rect getTaskGridRect() {
- return mTaskGridRect;
- }
-
- public int getFocusFrameThickness() {
- return mFocusedFrameThickness;
- }
-
- public VisibilityReport computeStackVisibilityReport(ArrayList<Task> tasks) {
- int visibleCount = Math.min(TaskGridLayoutAlgorithm.MAX_LAYOUT_TASK_COUNT, tasks.size());
- return new VisibilityReport(visibleCount, visibleCount);
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java
deleted file mode 100644
index 1655f6c83c53..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.views.grid;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.View;
-
-import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
-import com.android.systemui.R;
-import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.views.TaskStackView;
-
-public class TaskViewFocusFrame extends View implements OnGlobalFocusChangeListener {
-
- private TaskStackView mSv;
- private TaskGridLayoutAlgorithm mTaskGridLayoutAlgorithm;
- public TaskViewFocusFrame(Context context) {
- this(context, null);
- }
-
- public TaskViewFocusFrame(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public TaskViewFocusFrame(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public TaskViewFocusFrame(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- setBackground(mContext.getDrawable(
- R.drawable.recents_grid_task_view_focus_frame_background));
- setFocusable(false);
- hide();
- }
-
- public TaskViewFocusFrame(Context context, TaskStackView stackView,
- TaskGridLayoutAlgorithm taskGridLayoutAlgorithm) {
- this(context);
- mSv = stackView;
- mTaskGridLayoutAlgorithm = taskGridLayoutAlgorithm;
- }
-
- /**
- * Measure the width and height of the focus frame according to the current grid task view size.
- */
- public void measure() {
- int thickness = mTaskGridLayoutAlgorithm.getFocusFrameThickness();
- Rect rect = mTaskGridLayoutAlgorithm.getTaskGridRect();
- measure(
- MeasureSpec.makeMeasureSpec(rect.width() + thickness * 2, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(rect.height() + thickness * 2, MeasureSpec.EXACTLY));
- }
-
- /**
- * Layout the focus frame with its size.
- */
- public void layout() {
- layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
- }
-
- /**
- * Update the current size of grid task view and the focus frame.
- */
- public void resize() {
- if (mSv.useGridLayout()) {
- mTaskGridLayoutAlgorithm.updateTaskGridRect(mSv.getStack().getTaskCount());
- measure();
- requestLayout();
- }
- }
-
- /**
- * Move the task view focus frame to surround the newly focused view. If it's {@code null} or
- * it's not an instance of GridTaskView, we hide the focus frame.
- * @param newFocus The newly focused view.
- */
- public void moveGridTaskViewFocus(View newFocus) {
- if (mSv.useGridLayout()) {
- // The frame only shows up in the grid layout. It shouldn't show up in the stack
- // layout including when we're in the split screen.
- if (newFocus instanceof GridTaskView) {
- // If the focus goes to a GridTaskView, we show the frame and layout it.
- int[] location = new int[2];
- newFocus.getLocationInWindow(location);
- int thickness = mTaskGridLayoutAlgorithm.getFocusFrameThickness();
- setTranslationX(location[0] - thickness);
- setTranslationY(location[1] - thickness);
- show();
- } else {
- // If focus goes to other views, we hide the frame.
- hide();
- }
- }
- }
-
- @Override
- public void onGlobalFocusChanged(View oldFocus, View newFocus) {
- if (!mSv.useGridLayout()) {
- return;
- }
- if (newFocus == null) {
- // We're going to touch mode, unset the focus.
- moveGridTaskViewFocus(null);
- return;
- }
- if (oldFocus == null) {
- // We're returning from touch mode, set the focus to the previously focused task.
- final TaskStack stack = mSv.getStack();
- final int taskCount = stack.getTaskCount();
- final int k = stack.indexOfTask(mSv.getFocusedTask());
- final int taskIndexToFocus = k == -1 ? (taskCount - 1) : (k % taskCount);
- mSv.setFocusedTask(taskIndexToFocus, false, true);
- }
- }
-
- private void show() {
- setAlpha(1f);
- }
-
- private void hide() {
- setAlpha(0f);
- }
-}
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/lowram/TaskStackLowRamLayoutAlgorithm.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/lowram/TaskStackLowRamLayoutAlgorithm.java
deleted file mode 100644
index 15c7c87366d2..000000000000
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/lowram/TaskStackLowRamLayoutAlgorithm.java
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.views.lowram;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.view.ViewConfiguration;
-
-import com.android.systemui.R;
-import com.android.systemui.recents.LegacyRecentsImpl;
-import com.android.systemui.recents.RecentsActivityLaunchState;
-import com.android.systemui.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
-import com.android.systemui.recents.views.TaskViewTransform;
-
-import java.util.ArrayList;
-
-import static com.android.systemui.recents.views.TaskStackLayoutAlgorithm.VisibilityReport;
-
-public class TaskStackLowRamLayoutAlgorithm {
-
- private static final String TAG = "TaskStackLowRamLayoutAlgorithm";
- private static final float MAX_OVERSCROLL = 0.2f / 0.3f;
-
- public static final int MAX_LAYOUT_TASK_COUNT = 9;
- public static final int NUM_TASK_VISIBLE_LAUNCHED_FROM_HOME = 2;
- public static final int NUM_TASK_VISIBLE_LAUNCHED_FROM_APP =
- NUM_TASK_VISIBLE_LAUNCHED_FROM_HOME + 1;
- private Rect mWindowRect;
-
- private int mFlingThreshold;
- private int mPadding;
- private int mPaddingLeftRight;
- private int mTopOffset;
- private int mPaddingEndTopBottom;
- private Rect mTaskRect = new Rect();
- private Rect mSystemInsets = new Rect();
-
- public TaskStackLowRamLayoutAlgorithm(Context context) {
- reloadOnConfigurationChange(context);
- }
-
- public void reloadOnConfigurationChange(Context context) {
- mPadding = context.getResources()
- .getDimensionPixelSize(R.dimen.recents_layout_side_margin_phone);
- mFlingThreshold = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
- }
-
- public void initialize(Rect windowRect) {
- mWindowRect = windowRect;
- if (mWindowRect.height() > 0) {
- int windowHeight = mWindowRect.height() - mSystemInsets.bottom;
- int windowWidth = mWindowRect.width() - mSystemInsets.right - mSystemInsets.left;
- int width = Math.min(windowWidth, windowHeight) - mPadding * 2;
- boolean isLandscape = windowWidth > windowHeight;
- mTaskRect.set(0, 0, width, isLandscape ? width * 2 / 3 : width);
- mPaddingLeftRight = (windowWidth - mTaskRect.width()) / 2;
- mPaddingEndTopBottom = (windowHeight - mTaskRect.height()) / 2;
-
- // Compute the top offset to center tasks in the middle of the screen
- mTopOffset = (getTotalHeightOfTasks(MAX_LAYOUT_TASK_COUNT) - windowHeight) / 2;
- }
- }
-
- public void setSystemInsets(Rect systemInsets) {
- mSystemInsets = systemInsets;
- }
-
- public VisibilityReport computeStackVisibilityReport(ArrayList<Task> tasks) {
- RecentsActivityLaunchState launchState = LegacyRecentsImpl.getConfiguration().getLaunchState();
- int maxVisible = launchState.launchedFromHome || launchState.launchedFromPipApp
- || launchState.launchedWithNextPipApp
- ? NUM_TASK_VISIBLE_LAUNCHED_FROM_HOME
- : NUM_TASK_VISIBLE_LAUNCHED_FROM_APP;
- int visibleCount = Math.min(maxVisible, tasks.size());
- return new VisibilityReport(visibleCount, visibleCount);
- }
-
- public void getFrontOfStackTransform(TaskViewTransform transformOut,
- TaskStackLayoutAlgorithm stackLayout) {
- if (mWindowRect == null) {
- transformOut.reset();
- return;
- }
-
- // Calculate the static task y position 2 tasks after/below the middle/current task
- int windowHeight = mWindowRect.height() - mSystemInsets.bottom;
- int bottomOfCurrentTask = (windowHeight + mTaskRect.height()) / 2;
- int y = bottomOfCurrentTask + mTaskRect.height() + mPadding * 2;
- fillStackTransform(transformOut, y, stackLayout.mMaxTranslationZ, true);
- }
-
- public void getBackOfStackTransform(TaskViewTransform transformOut,
- TaskStackLayoutAlgorithm stackLayout) {
- if (mWindowRect == null) {
- transformOut.reset();
- return;
- }
-
- // Calculate the static task y position 2 tasks before/above the middle/current task
- int windowHeight = mWindowRect.height() - mSystemInsets.bottom;
- int topOfCurrentTask = (windowHeight - mTaskRect.height()) / 2;
- int y = topOfCurrentTask - (mTaskRect.height() + mPadding) * 2;
- fillStackTransform(transformOut, y, stackLayout.mMaxTranslationZ, true);
- }
-
- public TaskViewTransform getTransform(int taskIndex, float stackScroll,
- TaskViewTransform transformOut, int taskCount, TaskStackLayoutAlgorithm stackLayout) {
- if (taskCount == 0) {
- transformOut.reset();
- return transformOut;
- }
- boolean visible = true;
- int y;
- if (taskCount > 1) {
- y = getTaskTopFromIndex(taskIndex) - percentageToScroll(stackScroll);
-
- // Check visibility from the bottom of the task
- visible = y + mPadding + getTaskRect().height() > 0;
- } else {
- int windowHeight = mWindowRect.height() - mSystemInsets.bottom;
- y = (windowHeight - mTaskRect.height()) / 2 - percentageToScroll(stackScroll);
- }
- fillStackTransform(transformOut, y, stackLayout.mMaxTranslationZ, visible);
- return transformOut;
- }
-
- /**
- * Finds the closest task to the scroll percentage in the y axis and returns the percentage of
- * the task to scroll to.
- * @param scrollP percentage to find nearest to
- * @param numTasks number of tasks in recents stack
- * @param velocity speed of fling
- */
- public float getClosestTaskP(float scrollP, int numTasks, int velocity) {
- int y = percentageToScroll(scrollP);
-
- int lastY = getTaskTopFromIndex(0) - mPaddingEndTopBottom;
- for (int i = 1; i < numTasks; i++) {
- int taskY = getTaskTopFromIndex(i) - mPaddingEndTopBottom;
- int diff = taskY - y;
- if (diff > 0) {
- int diffPrev = Math.abs(y - lastY);
- boolean useNext = diff > diffPrev;
- if (Math.abs(velocity) > mFlingThreshold) {
- useNext = velocity > 0;
- }
- return useNext
- ? scrollToPercentage(lastY) : scrollToPercentage(taskY);
- }
- lastY = taskY;
- }
- return scrollToPercentage(lastY);
- }
-
- /**
- * Convert a scroll value to a percentage
- * @param scroll a scroll value
- * @return a percentage that represents the scroll from the total height of tasks
- */
- public float scrollToPercentage(int scroll) {
- return (float) scroll / (mTaskRect.height() + mPadding);
- }
-
- /**
- * Converts a percentage to the scroll value from the total height of tasks
- * @param p a percentage that represents the scroll value
- * @return a scroll value in pixels
- */
- public int percentageToScroll(float p) {
- return (int) (p * (mTaskRect.height() + mPadding));
- }
-
- /**
- * Get the min scroll progress for low ram layout. This computes the top position of the
- * first task and reduce by the end padding to center the first task
- * @return position of max scroll
- */
- public float getMinScrollP() {
- return getScrollPForTask(0);
- }
-
- /**
- * Get the max scroll progress for low ram layout. This computes the top position of the last
- * task and reduce by the end padding to center the last task
- * @param taskCount the amount of tasks in the recents stack
- * @return position of max scroll
- */
- public float getMaxScrollP(int taskCount) {
- return getScrollPForTask(taskCount - 1);
- }
-
- /**
- * Get the initial scroll value whether launched from home or from an app.
- * @param taskCount the amount of tasks currently in recents
- * @param fromHome if launching recents from home or not
- * @return from home it will return max value and from app it will return 2nd last task
- */
- public float getInitialScrollP(int taskCount, boolean fromHome) {
- if (fromHome) {
- return getMaxScrollP(taskCount);
- }
- if (taskCount < 2) {
- return 0;
- }
- return getScrollPForTask(taskCount - 2);
- }
-
- /**
- * Get the scroll progress for any task
- * @param taskIndex task index to get the scroll progress of
- * @return scroll progress of task
- */
- public float getScrollPForTask(int taskIndex) {
- return scrollToPercentage(getTaskTopFromIndex(taskIndex) - mPaddingEndTopBottom);
- }
-
- public Rect getTaskRect() {
- return mTaskRect;
- }
-
- public float getMaxOverscroll() {
- return MAX_OVERSCROLL;
- }
-
- private int getTaskTopFromIndex(int index) {
- return getTotalHeightOfTasks(index) - mTopOffset;
- }
-
- private int getTotalHeightOfTasks(int taskCount) {
- return taskCount * mTaskRect.height() + (taskCount + 1) * mPadding;
- }
-
- private void fillStackTransform(TaskViewTransform transformOut, int y, int translationZ,
- boolean visible) {
- transformOut.scale = 1f;
- transformOut.alpha = 1f;
- transformOut.translationZ = translationZ;
- transformOut.dimAlpha = 0f;
- transformOut.viewOutlineAlpha = 1f;
- transformOut.rect.set(getTaskRect());
- transformOut.rect.offset(mPaddingLeftRight + mSystemInsets.left, y);
- Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
- transformOut.visible = visible;
- }
-}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/HomeControlsPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/HomeControlsPlugin.java
index cac673fdbf0b..c1d4b03b6620 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/HomeControlsPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/HomeControlsPlugin.java
@@ -33,4 +33,9 @@ public interface HomeControlsPlugin extends Plugin {
* will add home controls to this space.
*/
void sendParentGroup(ViewGroup group);
+
+ /**
+ * When visible, will poll for updates.
+ */
+ void setVisible(boolean visible);
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/NotificationPersonExtractorPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NotificationPersonExtractorPlugin.java
new file mode 100644
index 000000000000..802a8dab92d9
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NotificationPersonExtractorPlugin.java
@@ -0,0 +1,72 @@
+/*
+ * 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.plugins;
+
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.graphics.drawable.Drawable;
+import android.service.notification.StatusBarNotification;
+
+import com.android.systemui.plugins.annotations.DependsOn;
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/** Custom logic that can extract a PeopleHub "person" from a notification. */
+@ProvidesInterface(
+ action = NotificationPersonExtractorPlugin.ACTION,
+ version = NotificationPersonExtractorPlugin.VERSION)
+@DependsOn(target = NotificationPersonExtractorPlugin.PersonData.class)
+public interface NotificationPersonExtractorPlugin extends Plugin {
+
+ String ACTION = "com.android.systemui.action.PEOPLE_HUB_PERSON_EXTRACTOR";
+ int VERSION = 0;
+
+ /**
+ * Attempts to extract a person from a notification. Returns {@code null} if one is not found.
+ */
+ @Nullable PersonData extractPerson(StatusBarNotification sbn);
+
+ /**
+ * Attempts to extract a person id from a notification. Returns {@code null} if one is not
+ * found.
+ *
+ * This method can be overridden in order to provide a faster implementation.
+ */
+ @Nullable
+ default String extractPersonKey(StatusBarNotification sbn) {
+ return extractPerson(sbn).key;
+ }
+
+ /** A person to be surfaced in PeopleHub. */
+ @ProvidesInterface(version = PersonData.VERSION)
+ final class PersonData {
+
+ public static final int VERSION = 0;
+
+ public final String key;
+ public final CharSequence name;
+ public final Drawable avatar;
+ public final PendingIntent clickIntent;
+
+ public PersonData(String key, CharSequence name, Drawable avatar,
+ PendingIntent clickIntent) {
+ this.key = key;
+ this.name = name;
+ this.avatar = avatar;
+ this.clickIntent = clickIntent;
+ }
+ }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index db026cad6ef5..6518924ca0c2 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -186,6 +186,8 @@ public interface QSTile {
return toStringBuilder().toString();
}
+ // Used in dumps to determine current state of a tile.
+ // This string may be used for CTS testing of tiles, so removing elements is discouraged.
protected StringBuilder toStringBuilder() {
final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('[');
sb.append(",icon=").append(icon);
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
index fe547a0a16fa..b21a9f7c4d05 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
@@ -95,9 +95,12 @@ public interface StatusBarStateController {
default void onDozeAmountChanged(float linear, float eased) {}
/**
- * Callback to be notified when the sysui visibility changes
+ * Callback to be notified when the fullscreen or immersive state changes.
+ *
+ * @param isFullscreen if any of the system bar is hidden by the focused window.
+ * @param isImmersive if the navigation bar can stay hidden when the display gets tapped.
*/
- default void onSystemUiVisibilityChanged(int visibility) {}
+ default void onFullscreenStateChanged(boolean isFullscreen, boolean isImmersive) {}
/**
* Callback to be notified when the pulsing state changes
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 22b0ab7dde4e..3e74970ee725 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -35,3 +35,7 @@
*;
}
-keep class androidx.core.app.CoreComponentFactory
+
+-keep public class * extends com.android.systemui.SystemUI {
+ public <init>(android.content.Context);
+} \ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/values-af/strings.xml b/packages/SystemUI/res-keyguard/values-af/strings.xml
index 11bcc8876b27..b9122d2a6ea5 100644
--- a/packages/SystemUI/res-keyguard/values-af/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-af/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Voer jou wagwoord in"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Verkeerde PIN-kode."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Ongeldige kaart."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Volgelaai"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Gelaai"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laai tans draadloos"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laai tans"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laai tans vinnig"</string>
diff --git a/packages/SystemUI/res-keyguard/values-am/strings.xml b/packages/SystemUI/res-keyguard/values-am/strings.xml
index 4feb9f1d2c13..4cabacaf7270 100644
--- a/packages/SystemUI/res-keyguard/values-am/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-am/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"ይለፍ ቃልዎን ያስገቡ"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"ትክክል ያልሆነ ፒን ኮድ።"</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"ልክ ያልሆነ ካርድ።"</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"ሙሉ በሙሉ ኃይል ተሞልቷል"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"ባትሪ ሞልቷል"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • በገመድ አልባ ኃይል በመሙላት ላይ"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ኃይል በመሙላት ላይ"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • በፍጥነት ኃይልን በመሙላት ላይ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index 4e163a2d331c..d0179537d860 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"أدخل كلمة المرور"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"رمز رقم التعريف الشخصي غير صحيح."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"بطاقة غير صالحة."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"تم شحن البطارية بالكامل"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"تم الشحن"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن لاسلكيًا"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن سريعًا"</string>
diff --git a/packages/SystemUI/res-keyguard/values-as/strings.xml b/packages/SystemUI/res-keyguard/values-as/strings.xml
index e225675804d2..0c7dec3ae91d 100644
--- a/packages/SystemUI/res-keyguard/values-as/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-as/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"আপোনাৰ পাছৱর্ড দিয়ক"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"ভুল পিন ক\'ড।"</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"ব্যৱহাৰৰ অযোগ্য ছিম কাৰ্ড"</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"পূৰ্ণৰূপে চ্চাৰ্জ হৈছে"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"চ্চার্জ কৰা হ’ল"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • বেতাঁৰৰ জৰিয়তে চাৰ্জ কৰি থকা হৈছে"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চ্চার্জ কৰি থকা হৈছে"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • দ্ৰুত গতিৰে চ্চাৰ্জ কৰি থকা হৈছে"</string>
diff --git a/packages/SystemUI/res-keyguard/values-az/strings.xml b/packages/SystemUI/res-keyguard/values-az/strings.xml
index c0cf129a9408..8cdf2f0e1bf8 100644
--- a/packages/SystemUI/res-keyguard/values-az/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-az/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Şifrənizi daxil edin"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Yanlış PIN kod."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Yanlış Kart."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Tam dolub"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Enerji yığdı"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Simsiz şəkildə batareya yığır"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Enerji yığır"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sürətlə enerji yığır"</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 88ca77b4a977..eeda7cecc4ee 100644
--- a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Unesite lozinku"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"PIN kôd je netačan."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Nevažeća kartica."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Napunjena je u potpunosti"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Napunjena je"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Bežično punjenje"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Puni se"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Brzo se puni"</string>
diff --git a/packages/SystemUI/res-keyguard/values-be/strings.xml b/packages/SystemUI/res-keyguard/values-be/strings.xml
index a897fb2fbf92..09cda6b05147 100644
--- a/packages/SystemUI/res-keyguard/values-be/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-be/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Увядзіце пароль"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Няправільны PIN-код."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Несапраўдная картка."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Акумулятар поўнасцю зараджаны"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Зараджаны"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ідзе бесправадная зарадка"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ідзе зарадка"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ідзе хуткая зарадка"</string>
diff --git a/packages/SystemUI/res-keyguard/values-bg/strings.xml b/packages/SystemUI/res-keyguard/values-bg/strings.xml
index eebdb9edc7f1..75942ea46da5 100644
--- a/packages/SystemUI/res-keyguard/values-bg/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bg/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Въведете паролата си"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Неправилен ПИН код."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Картата е невалидна."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Напълно заредено"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Заредена"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарежда се безжично"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарежда се"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарежда се бързо"</string>
diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml
index b544c14fb0b0..0e8189f948fe 100644
--- a/packages/SystemUI/res-keyguard/values-bn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bn/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"পাসওয়ার্ড লিখুন"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"ভুল পিন কোড দেওয়া হয়েছে।"</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"ভুল কার্ড।"</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"সম্পূর্ণ চার্জ আছে"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"চার্জ হয়েছে"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ওয়্যারলেস পদ্ধতিতে চার্জ হচ্ছে"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চার্জ হচ্ছে"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • দ্রুত চার্জ হচ্ছে"</string>
diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml
index 8547bc87a5d3..33fe43ffc1d2 100644
--- a/packages/SystemUI/res-keyguard/values-bs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Unesite lozinku"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Pogrešan PIN."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Nevažeća kartica."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Potpuno napunjen"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Napunjeno"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Bežično punjenje"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Punjenje"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Brzo punjenje"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ca/strings.xml b/packages/SystemUI/res-keyguard/values-ca/strings.xml
index ccde138c10b6..6c85fd3fe99f 100644
--- a/packages/SystemUI/res-keyguard/values-ca/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ca/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Introdueix la contrasenya"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"El codi PIN no és correcte."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"La targeta no és vàlida."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Completament carregada"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Bateria carregada"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està carregant sense fil"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està carregant"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està carregant ràpidament"</string>
diff --git a/packages/SystemUI/res-keyguard/values-cs/strings.xml b/packages/SystemUI/res-keyguard/values-cs/strings.xml
index 3adc2792ef10..e9443127d536 100644
--- a/packages/SystemUI/res-keyguard/values-cs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-cs/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Zadejte heslo"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Nesprávný kód PIN."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Neplatná karta."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Plně nabito"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Nabito"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Bezdrátové nabíjení"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíjení"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Rychlé nabíjení"</string>
diff --git a/packages/SystemUI/res-keyguard/values-da/strings.xml b/packages/SystemUI/res-keyguard/values-da/strings.xml
index 0e6a1f349d93..ac1791b9bef7 100644
--- a/packages/SystemUI/res-keyguard/values-da/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-da/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Angiv din adgangskode"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Forkert pinkode."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Ugyldigt kort."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Fuldt opladet"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Opladet"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Trådløs opladning"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Oplader"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Oplader hurtigt"</string>
diff --git a/packages/SystemUI/res-keyguard/values-de/strings.xml b/packages/SystemUI/res-keyguard/values-de/strings.xml
index d44bfbc670a1..0607c8afd62e 100644
--- a/packages/SystemUI/res-keyguard/values-de/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-de/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Passwort eingeben"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Falscher PIN-Code."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Ungültige Karte."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Vollständig aufgeladen"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Aufgeladen"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kabelloses Laden"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird geladen"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird schnell geladen"</string>
diff --git a/packages/SystemUI/res-keyguard/values-el/strings.xml b/packages/SystemUI/res-keyguard/values-el/strings.xml
index 46b1d30b865c..6997b16c03f2 100644
--- a/packages/SystemUI/res-keyguard/values-el/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-el/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Εισαγάγετε κωδικό πρόσβασης"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Λανθασμένος κωδικός PIN."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Μη έγκυρη κάρτα."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Πλήρως φορτισμένη"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Φορτίστηκε"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ασύρματη φόρτιση"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Φόρτιση"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Γρήγορη φόρτιση"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
index 5640b6d3050a..19dd191ff899 100644
--- a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Enter your password"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Incorrect PIN code."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Invalid card."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Fully charged"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Charged"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging wirelessly"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging rapidly"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
index 5836b72a0cda..acfb7e294315 100644
--- a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Enter your password"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Incorrect PIN code."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Invalid card."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Fully charged"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Charged"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging wirelessly"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging rapidly"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
index 5640b6d3050a..19dd191ff899 100644
--- a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Enter your password"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Incorrect PIN code."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Invalid card."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Fully charged"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Charged"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging wirelessly"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging rapidly"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
index 5640b6d3050a..19dd191ff899 100644
--- a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Enter your password"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Incorrect PIN code."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Invalid card."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Fully charged"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Charged"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging wirelessly"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Charging rapidly"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
index fb968b76e24b..b29bd600b029 100644
--- a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‏‎‎‏‏‏‎‎‏‎‏‎‎‏‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‎‏‎‏‎‏‏‏‎‎‏‎‏‎‎‎‏‏‏‎‎‏‎‏‎‎Enter your password‎‏‎‎‏‎"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‎‏‏‎‎‎‏‎‎‎‎‏‏‎‎‎‏‎‎‎‏‏‏‏‎‏‎‏‏‎‏‏‎‎‎‏‎‎‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‎Incorrect PIN code.‎‏‎‎‏‎"</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‏‎‎‏‏‎‎‎‎‏‏‏‎‏‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‎‏‏‏‎‎‎‎Invalid Card.‎‏‎‎‏‎"</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‎‎‏‎‏‎‎‏‏‎‏‎‏‏‎‎‏‎‎‏‏‎‏‎‎‎‏‏‎‎‏‎‎‎‎‏‎‏‏‏‎‏‎‏‎‎‎‏‎‎‏‎‏‎Fully charged‎‏‎‎‏‎"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‏‎‎‏‏‎‏‎‎‎‏‎‎‏‏‏‏‎‏‏‎‏‏‏‎‎‎‎‎‎‏‎‏‎‏‎‏‏‎‏‎‎‏‎‏‎‏‎Charged‎‏‎‎‏‎"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‎‏‏‎‎‏‎‎‏‎‏‏‏‎‏‏‎‏‎‎‏‏‎‏‎‏‏‎‏‎‏‏‎‎‎‏‏‏‎‎‏‏‎‎‎‎‏‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎ • Charging wirelessly‎‏‎‎‏‎"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‏‎‏‏‏‏‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‎‏‎‏‎‏‏‏‏‎‏‎‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‎‏‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎ • Charging‎‏‎‎‏‎"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‎‎‎‏‎‎‏‎‎‏‏‎‏‏‎‎‏‏‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‏‏‏‏‎‏‏‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎ • Charging rapidly‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
index 727cbad82ad2..aa5c4e0067ac 100644
--- a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Ingresa tu contraseña"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Código PIN incorrecto"</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Tarjeta no válida"</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Carga completa"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Cargada"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando de manera inalámbrica"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando rápidamente"</string>
diff --git a/packages/SystemUI/res-keyguard/values-es/strings.xml b/packages/SystemUI/res-keyguard/values-es/strings.xml
index 870741ea0202..5f9a4568567f 100644
--- a/packages/SystemUI/res-keyguard/values-es/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Introduce tu contraseña"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"El código PIN es incorrecto."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Tarjeta no válida."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Carga completa"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Cargada"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando sin cables"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando rápidamente"</string>
diff --git a/packages/SystemUI/res-keyguard/values-et/strings.xml b/packages/SystemUI/res-keyguard/values-et/strings.xml
index b6cac13a728d..c555cedbd317 100644
--- a/packages/SystemUI/res-keyguard/values-et/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-et/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Sisestage parool"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Vale PIN-kood."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Kehtetu kaart."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Täielikult laetud"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Laetud"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Juhtmeta laadimine"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laadimine"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kiirlaadimine"</string>
diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml
index 8ad942b5c8ce..612b2de1b037 100644
--- a/packages/SystemUI/res-keyguard/values-eu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Idatzi pasahitza"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"PIN kode hori ez da zuzena."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Txartelak ez du balio."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Erabat kargatuta"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Kargatuta"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hari gabe kargatzen"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kargatzen"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Bizkor kargatzen"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml
index 9c9de22b96d9..0bf01d999524 100644
--- a/packages/SystemUI/res-keyguard/values-fa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fa/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"گذرواژه‌تان را وارد کنید"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"کد پین اشتباه است."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"کارت نامعتبر"</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"شارژ کامل است"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"شارژ کامل شد"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • درحال شارژ بی‌سیم"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • درحال شارژ شدن"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • درحال شارژ سریع"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fi/strings.xml b/packages/SystemUI/res-keyguard/values-fi/strings.xml
index 66f1de5b292d..9b727a4daefd 100644
--- a/packages/SystemUI/res-keyguard/values-fi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fi/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Kirjoita salasana"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Väärä PIN-koodi"</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Virheellinen kortti"</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Täyteen ladattu"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Ladattu"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladataan langattomasti"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladataan"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ladataan nopeasti"</string>
@@ -74,7 +74,7 @@
<string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Anna operaattorin <xliff:g id="CARRIER">%1$s</xliff:g> SIM-kortin PIN-koodi."</string>
<string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Poista eSIM käytöstä, jos haluat käyttää laitetta ilman mobiililiittymää."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"Anna PIN-koodi."</string>
- <string name="kg_password_instructions" msgid="136952397352976538">"Anna salasana"</string>
+ <string name="kg_password_instructions" msgid="136952397352976538">"Lisää salasana"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM-kortti on nyt poistettu käytöstä. Jatka antamalla PUK-koodi. Saat lisätietoja ottamalla yhteyttä operaattoriin."</string>
<string name="kg_puk_enter_puk_hint_multi" msgid="1373131883510840794">"Operaattorin <xliff:g id="CARRIER">%1$s</xliff:g> SIM-kortti on nyt lukittu. Jatka antamalla PUK-koodi. Saat lisätietoja operaattoriltasi."</string>
<string name="kg_puk_enter_pin_hint" msgid="3137789674920391087">"Anna haluamasi PIN-koodi."</string>
diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
index 66c7c86f363e..a86cdbe8117b 100644
--- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Entrez votre mot de passe"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"NIP erroné."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Cette carte n\'est pas valide."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Complètement chargé"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Chargé"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • En recharge sans fil"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"En recharge : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"En recharge rapide : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fr/strings.xml b/packages/SystemUI/res-keyguard/values-fr/strings.xml
index d6aa5d262821..70075ecbe6f8 100644
--- a/packages/SystemUI/res-keyguard/values-fr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Saisissez votre mot de passe"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Le code est incorrect."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Carte non valide."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Complètement chargée"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Chargé"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge sans fil"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge…"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge rapide…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-gl/strings.xml b/packages/SystemUI/res-keyguard/values-gl/strings.xml
index e6b10a00ba72..9504a3e5fd96 100644
--- a/packages/SystemUI/res-keyguard/values-gl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gl/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Introduce o contrasinal"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Código PIN incorrecto"</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"A tarxeta non é válida."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Batería totalmente cargada"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Cargada"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando sen fíos"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando rapidamente"</string>
diff --git a/packages/SystemUI/res-keyguard/values-gu/strings.xml b/packages/SystemUI/res-keyguard/values-gu/strings.xml
index 025462e37813..a2370d0fcfa4 100644
--- a/packages/SystemUI/res-keyguard/values-gu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gu/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"તમારો પાસવર્ડ દાખલ કરો"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"ખોટો પિન કોડ."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"અમાન્ય કાર્ડ."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"સંપૂર્ણપણે ચાર્જ થયેલ"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"ચાર્જ થઈ ગયું"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • વાયરલેસથી ચાર્જિંગ"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ચાર્જિંગ"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ઝડપથી ચાર્જિંગ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hi/strings.xml b/packages/SystemUI/res-keyguard/values-hi/strings.xml
index 2de75e479334..f8e99a6fe3c2 100644
--- a/packages/SystemUI/res-keyguard/values-hi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hi/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"अपना पासवर्ड डालें"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"गलत पिन कोड."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"गलत कार्ड."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"पूरी तरह चार्ज हो गया"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"चार्ज हो गई है"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • वायरलेस तरीके से चार्ज हो रहा है"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्ज हो रहा है"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • तेज़ चार्ज हो रहा है"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hr/strings.xml b/packages/SystemUI/res-keyguard/values-hr/strings.xml
index c4f7bba86002..1c9396af8ec3 100644
--- a/packages/SystemUI/res-keyguard/values-hr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hr/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Unesite zaporku"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"PIN kôd nije točan."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Nevažeća kartica."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Potpuno napunjena baterija"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Napunjeno"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • bežično punjenje"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • punjenje"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • brzo punjenje"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hu/strings.xml b/packages/SystemUI/res-keyguard/values-hu/strings.xml
index 06142bc738b0..ea5ab940f6fe 100644
--- a/packages/SystemUI/res-keyguard/values-hu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hu/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Adja meg jelszavát"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Helytelen PIN-kód."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Érvénytelen kártya."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Teljesen feltöltve"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Feltöltve"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Vezeték nélküli töltés"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Töltés"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Gyors töltés"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hy/strings.xml b/packages/SystemUI/res-keyguard/values-hy/strings.xml
index f501fcc7c347..b34eced8b521 100644
--- a/packages/SystemUI/res-keyguard/values-hy/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hy/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Մուտքագրեք գաղտնաբառը"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"PIN կոդը սխալ է։"</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Սխալ քարտ"</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Ամբողջությամբ լիցքավորված է"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Լիցքավորված է"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Անլար լիցքավորում"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Լիցքավորում"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Արագ լիցքավորում"</string>
diff --git a/packages/SystemUI/res-keyguard/values-in/strings.xml b/packages/SystemUI/res-keyguard/values-in/strings.xml
index 64f396edfef8..3dd6c915bb8a 100644
--- a/packages/SystemUI/res-keyguard/values-in/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-in/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Masukkan sandi"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Kode PIN salah."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Kartu Tidak Valid"</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Terisi penuh"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Terisi"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengisi daya secara nirkabel"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengisi daya"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengisi daya dengan cepat"</string>
diff --git a/packages/SystemUI/res-keyguard/values-is/strings.xml b/packages/SystemUI/res-keyguard/values-is/strings.xml
index aa33215fc124..c09dcc6f0b4e 100644
--- a/packages/SystemUI/res-keyguard/values-is/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-is/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Sláðu inn aðgangsorðið þitt"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Rangt PIN-númer."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Ógilt kort."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Fullhlaðin"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Fullhlaðin"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Í þráðlausri hleðslu"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Í hleðslu"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hröð hleðsla"</string>
diff --git a/packages/SystemUI/res-keyguard/values-it/strings.xml b/packages/SystemUI/res-keyguard/values-it/strings.xml
index 761640c72010..3f5cfbb24380 100644
--- a/packages/SystemUI/res-keyguard/values-it/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-it/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Inserisci la password"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Codice PIN errato."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Scheda non valida."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Completamente carica"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Carico"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • In carica wireless"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • In carica"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ricarica veloce"</string>
diff --git a/packages/SystemUI/res-keyguard/values-iw/strings.xml b/packages/SystemUI/res-keyguard/values-iw/strings.xml
index e72c8081873a..95caf917e4d4 100644
--- a/packages/SystemUI/res-keyguard/values-iw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-iw/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"יש להזין סיסמה"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"קוד הגישה שגוי"</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"כרטיס לא חוקי."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"טעונה במלואה"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"הסוללה טעונה"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • בטעינה אלחוטית"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • בטעינה"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • בטעינה מהירה"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ja/strings.xml b/packages/SystemUI/res-keyguard/values-ja/strings.xml
index 27adb8c165eb..9872a076e816 100644
--- a/packages/SystemUI/res-keyguard/values-ja/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ja/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"パスワードを入力してください"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"PIN コードが無効です。"</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"無効なカードです。"</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"充電完了"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"充電が完了しました"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ワイヤレス充電中"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電中"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 急速充電中"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ka/strings.xml b/packages/SystemUI/res-keyguard/values-ka/strings.xml
index d165230613c1..c3b5047b79b5 100644
--- a/packages/SystemUI/res-keyguard/values-ka/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ka/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"შეიყვანეთ პაროლი"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"PIN-კოდი არასწორია."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"ბარათი არასწორია."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"ბოლომდე დატენილი"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"დატენილია"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • იტენება უსადენოდ"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • იტენება"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • სწრაფად იტენება"</string>
diff --git a/packages/SystemUI/res-keyguard/values-kk/strings.xml b/packages/SystemUI/res-keyguard/values-kk/strings.xml
index 85223468a0c2..e8741f9e1c32 100644
--- a/packages/SystemUI/res-keyguard/values-kk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kk/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Құпия сөзді енгізіңіз"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"PIN коды қате"</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Жарамсыз карта."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Толық зарядталды"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Зарядталды"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Сымсыз зарядталуда"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарядталуда"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Жылдам зарядталуда"</string>
diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml
index 7b4266a1b816..d48de152b608 100644
--- a/packages/SystemUI/res-keyguard/values-km/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-km/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"បញ្ចូល​ពាក្យ​សម្ងាត់​របស់អ្នក"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"កូដ PIN មិន​ត្រឹមត្រូវ​ទេ។"</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"បណ្ណមិនត្រឹមត្រូវទេ។"</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"បានសាក​ថ្មពេញ"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"បាន​សាក​ថ្ម"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុងសាកថ្ម​ឥតខ្សែ"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុង​សាកថ្ម"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុង​សាកថ្មយ៉ាង​ឆាប់រហ័ស"</string>
diff --git a/packages/SystemUI/res-keyguard/values-kn/strings.xml b/packages/SystemUI/res-keyguard/values-kn/strings.xml
index eaaa8298c8c6..7a6a0bfa8a68 100644
--- a/packages/SystemUI/res-keyguard/values-kn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kn/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"ನಿಮ್ಮ ಪಾಸ್‌ವರ್ಡ್ ನಮೂದಿಸಿ"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"ತಪ್ಪಾದ ಪಿನ್‌ ಕೋಡ್."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"ಅಮಾನ್ಯ ಕಾರ್ಡ್."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"ಪೂರ್ಣವಾಗಿ ಚಾರ್ಜ್ ಆಗಿದೆ"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"ಚಾರ್ಜ್ ಆಗಿದೆ"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ವೈರ್‌ಲೆಸ್ ಆಗಿ ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ಚಾರ್ಜ್‌ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ವೇಗವಾಗಿ ಚಾರ್ಜ್‌ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ko/strings.xml b/packages/SystemUI/res-keyguard/values-ko/strings.xml
index ca849372f202..9c1ffc8d0ae8 100644
--- a/packages/SystemUI/res-keyguard/values-ko/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ko/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"비밀번호 입력"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"잘못된 PIN 코드입니다."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"유효하지 않은 카드"</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"충전 완료"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"충전됨"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 무선 충전 중"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 충전 중"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 고속 충전 중"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ky/strings.xml b/packages/SystemUI/res-keyguard/values-ky/strings.xml
index 805a567c7639..a1060004c5c3 100644
--- a/packages/SystemUI/res-keyguard/values-ky/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ky/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Сырсөзүңүздү киргизиңиз"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"PIN-код туура эмес."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"SIM-карта жараксыз."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Толук кубатталды"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Кубатталды"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зымсыз кубатталууда"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Кубатталууда"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Тез кубатталууда"</string>
diff --git a/packages/SystemUI/res-keyguard/values-lo/strings.xml b/packages/SystemUI/res-keyguard/values-lo/strings.xml
index 1418d2750e83..e4d66a7c9774 100644
--- a/packages/SystemUI/res-keyguard/values-lo/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lo/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"ປ້ອນລະຫັດຜ່ານຂອງທ່ານ"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"ລະຫັດ PIN ບໍ່ຖືກຕ້ອງ."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"ບັດບໍ່ຖືກຕ້ອງ."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"ສາກເຕັມແລ້ວ"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"ສາກເຕັມແລ້ວ."</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ກຳ​ລັງ​ສາກ​ໄຟໄຮ້​ສາຍ"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ກຳລັງສາກ"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ກຳລັງສາກແບບດ່ວນ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-lt/strings.xml b/packages/SystemUI/res-keyguard/values-lt/strings.xml
index bbd25b2a6f9e..30e7cadffd8e 100644
--- a/packages/SystemUI/res-keyguard/values-lt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lt/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Įveskite slaptažodį"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Netinkamas PIN kodas."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Netinkama kortelė."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Visiškai įkrautas"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Įkrauta"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kraunama be laidų"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Įkraunama"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Greitai įkraunama"</string>
diff --git a/packages/SystemUI/res-keyguard/values-lv/strings.xml b/packages/SystemUI/res-keyguard/values-lv/strings.xml
index 58ca8ce3fd11..8cafcd344ff1 100644
--- a/packages/SystemUI/res-keyguard/values-lv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lv/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Ievadiet paroli"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"PIN kods nav pareizs."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Nederīga karte."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Pilnībā uzlādēts"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Akumulators uzlādēts"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Notiek bezvadu uzlāde"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Notiek uzlāde"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Notiek ātrā uzlāde"</string>
diff --git a/packages/SystemUI/res-keyguard/values-mk/strings.xml b/packages/SystemUI/res-keyguard/values-mk/strings.xml
index 1ffd8a4ecec9..e2ab170b1aef 100644
--- a/packages/SystemUI/res-keyguard/values-mk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mk/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Внесете ја лозинката"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Погрешен PIN-код."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Неважечка картичка."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Целосно полна"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Полна"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Се полни безжично"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Се полни"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Брзо полнење"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ml/strings.xml b/packages/SystemUI/res-keyguard/values-ml/strings.xml
index 1f60c7365c31..33765b4ec001 100644
--- a/packages/SystemUI/res-keyguard/values-ml/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ml/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"നിങ്ങളുടെ പാസ്‌വേഡ് നല്‍‌കുക"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"പിൻ കോഡ് തെറ്റാണ്."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"അസാധുവായ കാർഡ്."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"പൂർണ്ണമായി ചാർജ് ചെയ്‌തു"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"ചാർജായി"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • വയർലെസ്സ് ആയി ചാർജ് ചെയ്യുന്നു"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ചാർജ് ചെയ്യുന്നു"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • വേഗത്തിൽ ചാർജ് ചെയ്യുന്നു"</string>
diff --git a/packages/SystemUI/res-keyguard/values-mn/strings.xml b/packages/SystemUI/res-keyguard/values-mn/strings.xml
index 55dd70c35b66..3451fb8ef6b6 100644
--- a/packages/SystemUI/res-keyguard/values-mn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mn/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Нууц үгээ оруулна уу"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"ПИН код буруу байна."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Карт хүчингүй байна."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Бүрэн цэнэглэсэн"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Цэнэглэсэн"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Утасгүй цэнэглэж байна"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Цэнэглэж байна"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Хурдан цэнэглэж байна"</string>
diff --git a/packages/SystemUI/res-keyguard/values-mr/strings.xml b/packages/SystemUI/res-keyguard/values-mr/strings.xml
index 0ba82e0d3d90..e86fa1069276 100644
--- a/packages/SystemUI/res-keyguard/values-mr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mr/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"तुमचा पासवर्ड एंटर करा"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"चुकीचा पिन कोड."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"अवैध कार्ड."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"पूर्णपणे चार्ज"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"चार्ज झाली"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • वायरलेस पद्धतीने चार्ज करत आहे"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्ज होत आहे"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • वेगाने चार्ज होत आहे"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ms/strings.xml b/packages/SystemUI/res-keyguard/values-ms/strings.xml
index 17e105619f96..80ccd3e463e9 100644
--- a/packages/SystemUI/res-keyguard/values-ms/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ms/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Masukkan kata laluan anda"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Kod PIN salah."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Kad Tidak Sah."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Dicas penuh"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Sudah dicas"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengecas secara wayarles"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengecas"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengecas dengan cepat"</string>
diff --git a/packages/SystemUI/res-keyguard/values-my/strings.xml b/packages/SystemUI/res-keyguard/values-my/strings.xml
index 28fe2b210cea..c1fa6be55cba 100644
--- a/packages/SystemUI/res-keyguard/values-my/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-my/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"သင့်စကားဝှက် ထည့်ပါ"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"ပင်နံပါတ် မှားနေသည်။"</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"ကတ် မမှန်ကန်ပါ။"</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"အားအပြည့်သွင်းထားသည်"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"အားသွင်းပြီးပါပြီ"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ကြိုးမဲ့ အားသွင်းနေသည်"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • အားသွင်းနေသည်"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • အမြန်အားသွင်းနေသည်"</string>
diff --git a/packages/SystemUI/res-keyguard/values-nb/strings.xml b/packages/SystemUI/res-keyguard/values-nb/strings.xml
index 692dcad14e6e..af99aa795625 100644
--- a/packages/SystemUI/res-keyguard/values-nb/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nb/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Skriv inn passordet ditt"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Feil PIN-kode."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Ugyldig kort."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Fulladet"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Oppladet"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lader trådløst"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lader"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Lader raskt"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml
index 8102021247b3..82a4a198fa13 100644
--- a/packages/SystemUI/res-keyguard/values-ne/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"आफ्नो पासवर्ड प्रविष्ट गर्नु…"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"PIN कोड गलत छ।"</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"अमान्य कार्ड।"</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"पूर्ण चार्ज भएको"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"चार्ज भयो"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • तारविनै चार्ज गर्दै"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्ज गरिँदै"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • द्रुत गतिमा चार्ज गरिँदै"</string>
diff --git a/packages/SystemUI/res-keyguard/values-nl/strings.xml b/packages/SystemUI/res-keyguard/values-nl/strings.xml
index c79f2f7a536b..c533956b1368 100644
--- a/packages/SystemUI/res-keyguard/values-nl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nl/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Geef je wachtwoord op"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Onjuiste pincode."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Ongeldige kaart."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Volledig opgeladen"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Opgeladen"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Draadloos opladen"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Opladen"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Snel opladen"</string>
diff --git a/packages/SystemUI/res-keyguard/values-or/strings.xml b/packages/SystemUI/res-keyguard/values-or/strings.xml
index c28561a07fe7..258c2a8d271b 100644
--- a/packages/SystemUI/res-keyguard/values-or/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-or/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"ନିଜ ପାସ୍‌ୱର୍ଡ ଲେଖନ୍ତୁ"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"ଭୁଲ PIN କୋଡ୍।"</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"ଅମାନ୍ୟ କାର୍ଡ।"</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"ସମ୍ପୂର୍ଣ୍ଣ ଭାବରେ ଚାର୍ଜ ହୋ‍ଇଗଲା"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"ଚାର୍ଜ ହୋଇଗଲା"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"ୱାୟାର୍‍ଲେସ୍‍ଭାବରେ <xliff:g id="PERCENTAGE">%s</xliff:g> • ଚାର୍ଜ ହୋଇଛି"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ଚାର୍ଜ ହେଉଛି"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ଦ୍ରୁତ ଭାବେ ଚାର୍ଜ ହେଉଛି"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml
index 01b5d8ef4ba6..2c3f64ee3cbb 100644
--- a/packages/SystemUI/res-keyguard/values-pa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"ਆਪਣਾ ਪਾਸਵਰਡ ਦਾਖਲ ਕਰੋ"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"ਗਲਤ ਪਿੰਨ ਕੋਡ।"</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"ਅਵੈਧ ਕਾਰਡ।"</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"ਪੂਰਾ ਚਾਰਜ"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"ਚਾਰਜ ਹੋ ਗਿਆ"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਬਿਨਾਂ ਤਾਰ ਤੋਂ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਤੇਜ਼ੀ ਨਾਲ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml
index f86a0824b8aa..b88440f987e9 100644
--- a/packages/SystemUI/res-keyguard/values-pl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Wpisz hasło"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Nieprawidłowy kod PIN."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Nieprawidłowa karta."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Bateria w pełni naładowana"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Naładowana"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ładowanie bezprzewodowe"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ładowanie"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Szybkie ładowanie"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
index 436dd551df7e..ed552d49302c 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Digite sua senha"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Código PIN incorreto."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Cartão inválido."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Carga completa"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Carregada"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando sem fio"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando rapidamente"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
index 2ba4268168c0..5dd0beb77328 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Introduza a palavra-passe."</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Código PIN incorreto."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Cartão inválido."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Totalmente carregada"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Carregada"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • A carregar sem fios"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • A carregar…"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • A carregar rapidamente…"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt/strings.xml b/packages/SystemUI/res-keyguard/values-pt/strings.xml
index 436dd551df7e..ed552d49302c 100644
--- a/packages/SystemUI/res-keyguard/values-pt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Digite sua senha"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Código PIN incorreto."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Cartão inválido."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Carga completa"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Carregada"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando sem fio"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Carregando rapidamente"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml
index 6fdc95804129..13e15e33eb98 100644
--- a/packages/SystemUI/res-keyguard/values-ro/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Introduceți parola"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Cod PIN incorect."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Card nevalid"</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Complet încărcată"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Încărcată"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Se încarcă wireless"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Se încarcă"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Se încarcă rapid"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ru/strings.xml b/packages/SystemUI/res-keyguard/values-ru/strings.xml
index f9bd05bb3e21..781a3e41f0db 100644
--- a/packages/SystemUI/res-keyguard/values-ru/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ru/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Введите пароль"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Неверный PIN-код."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Ошибка SIM-карты."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Аккумулятор полностью заряжен"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Батарея заряжена"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Беспроводная зарядка"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"Идет зарядка (<xliff:g id="PERCENTAGE">%s</xliff:g>)"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"Идет быстрая зарядка (<xliff:g id="PERCENTAGE">%s</xliff:g>)"</string>
diff --git a/packages/SystemUI/res-keyguard/values-si/strings.xml b/packages/SystemUI/res-keyguard/values-si/strings.xml
index e40be057dca6..e51aa966a7e6 100644
--- a/packages/SystemUI/res-keyguard/values-si/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-si/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"ඔබේ මුරපදය ඇතුළු කරන්න"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"වැරදි PIN කේතයකි."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"වලංගු නොවන කාඩ්පත."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"සම්පූර්ණයෙන් ආරෝපණය වී ඇත"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"අරෝපිතයි"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • නොරැහැන්ව ආරෝපණ කෙරේ"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ආරෝපණය වෙමින්"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • වේගයෙන් ආරෝපණය වෙමින්"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sk/strings.xml b/packages/SystemUI/res-keyguard/values-sk/strings.xml
index e7800e955d08..4fe1e4fb8542 100644
--- a/packages/SystemUI/res-keyguard/values-sk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sk/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Zadajte heslo"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Nesprávny kód PIN."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Neplatná karta."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Úplne nabité"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Nabité"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíja sa bezdrôtovo"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíja sa"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nabíja sa rýchlo"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sl/strings.xml b/packages/SystemUI/res-keyguard/values-sl/strings.xml
index cce3a3132477..822e1627b9ea 100644
--- a/packages/SystemUI/res-keyguard/values-sl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sl/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Vnesite geslo"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Napačna koda PIN."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Neveljavna kartica"</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Popolnoma napolnjen"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Akumulator napolnjen"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • brezžično polnjenje"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • polnjenje"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • hitro polnjenje"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sq/strings.xml b/packages/SystemUI/res-keyguard/values-sq/strings.xml
index e4b37d0f1119..4636e6a653d2 100644
--- a/packages/SystemUI/res-keyguard/values-sq/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sq/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Fut fjalëkalimin"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Kodi PIN është i pasaktë."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Karta e pavlefshme."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"I ngarkuar plotësisht"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"I ngarkuar"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Po karikohet me valë"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Po karikohet"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Po karikohet me shpejtësi"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sr/strings.xml b/packages/SystemUI/res-keyguard/values-sr/strings.xml
index 1e6de94125dd..033e93ba5ecb 100644
--- a/packages/SystemUI/res-keyguard/values-sr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sr/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Унесите лозинку"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"PIN кôд је нетачан."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Неважећа картица."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Напуњена је у потпуности"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Напуњена је"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Бежично пуњење"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Пуни се"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Брзо се пуни"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sv/strings.xml b/packages/SystemUI/res-keyguard/values-sv/strings.xml
index 4cca1767a89d..8d7be977094f 100644
--- a/packages/SystemUI/res-keyguard/values-sv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sv/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Ange ditt lösenord"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Fel pinkod."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Ogiltigt kort."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Fulladdad"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Laddat"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddas trådlöst"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddas"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laddas snabbt"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sw/strings.xml b/packages/SystemUI/res-keyguard/values-sw/strings.xml
index df518596ebfa..5ab4d07224d3 100644
--- a/packages/SystemUI/res-keyguard/values-sw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sw/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Weka nenosiri lako"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Nambari ya PIN si sahihi."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Kadi si Sahihi."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Imejaa chaji"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Betri imejaa"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Inachaji bila kutumia waya"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Inachaji"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Inachaji kwa kasi"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ta/strings.xml b/packages/SystemUI/res-keyguard/values-ta/strings.xml
index 9aa1972c9954..fa75b4d74c0f 100644
--- a/packages/SystemUI/res-keyguard/values-ta/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ta/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"கடவுச்சொல்லை உள்ளிடுக"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"தவறான பின் குறியீடு."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"செல்லாத சிம் கார்டு."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"முழுவதுமாகச் சார்ஜ் ஆகிவிட்டது"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"சார்ஜ் செய்யப்பட்டது"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • வயர்லெஸ் முறையில் சார்ஜாகிறது"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • சார்ஜாகிறது"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • வேகமாகச் சார்ஜாகிறது"</string>
diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml
index 925d67302b86..ad89ba786571 100644
--- a/packages/SystemUI/res-keyguard/values-te/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-te/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"మీ పాస్‌వర్డ్‌ను నమోదు చేయండి"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"పిన్ కోడ్ తప్పు."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"చెల్లని కార్డ్."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"పూర్తిగా ఛార్జ్ చేయబడింది"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"ఛార్జ్ చేయబడింది"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • వైర్‌ లేకుండా ఛార్జ్ అవుతోంది"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ఛార్జ్ అవుతోంది"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • వేగంగా ఛార్జ్ అవుతోంది"</string>
diff --git a/packages/SystemUI/res-keyguard/values-th/strings.xml b/packages/SystemUI/res-keyguard/values-th/strings.xml
index c439c3279e93..caa60f388970 100644
--- a/packages/SystemUI/res-keyguard/values-th/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-th/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"ป้อนรหัสผ่าน"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"รหัส PIN ไม่ถูกต้อง"</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"การ์ดไม่ถูกต้อง"</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"ชาร์จเต็มแล้ว"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"ชาร์จแล้ว"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • กำลังชาร์จแบบไร้สาย"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • กำลังชาร์จ"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • กำลังชาร์จอย่างเร็ว"</string>
diff --git a/packages/SystemUI/res-keyguard/values-tl/strings.xml b/packages/SystemUI/res-keyguard/values-tl/strings.xml
index a9ca1b627019..65b87e3382ca 100644
--- a/packages/SystemUI/res-keyguard/values-tl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tl/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Ilagay ang iyong password"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Mali ang PIN code."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Di-wasto ang Card."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Puno na ang baterya"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Tapos nang mag-charge"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wireless na nagcha-charge"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Nagcha-charge"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mabilis na nagcha-charge"</string>
diff --git a/packages/SystemUI/res-keyguard/values-tr/strings.xml b/packages/SystemUI/res-keyguard/values-tr/strings.xml
index 4e8150501ff0..c35e99e5e940 100644
--- a/packages/SystemUI/res-keyguard/values-tr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tr/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Şifrenizi girin"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Yanlış PIN kodu."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Geçersiz Kart."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Tamamen şarj edildi"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Şarj oldu"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kablosuz olarak şarj ediliyor"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Şarj oluyor"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hızlı şarj oluyor"</string>
diff --git a/packages/SystemUI/res-keyguard/values-uk/strings.xml b/packages/SystemUI/res-keyguard/values-uk/strings.xml
index c683e329f17b..c2b9419d47e8 100644
--- a/packages/SystemUI/res-keyguard/values-uk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uk/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Введіть пароль"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Неправильний PIN-код."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Недійсна картка."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Повністю заряджений"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Заряджено"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Бездротове заряджання"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Заряджання"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Швидке заряджання"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml
index 96b949bccc3d..8a7409385a95 100644
--- a/packages/SystemUI/res-keyguard/values-ur/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"اپنا پاس ورڈ درج کریں"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"‏غلط PIN کوڈ۔"</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"غلط کارڈ۔"</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"مکمل طور پر چارج ہو گيا"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"چارج ہوگئی"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • وائرلیس طریقے سے چارج ہو رہا ہے"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • چارج ہو رہا ہے"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • تیزی سے چارج ہو رہا ہے"</string>
diff --git a/packages/SystemUI/res-keyguard/values-uz/strings.xml b/packages/SystemUI/res-keyguard/values-uz/strings.xml
index 80509ac2fab2..5bf38eca9990 100644
--- a/packages/SystemUI/res-keyguard/values-uz/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uz/strings.xml
@@ -33,9 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Parolni kiriting"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"PIN kodi xato."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"SIM karta yaroqsiz."</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for keyguard_charged (3316115607283493413) -->
- <skip />
+ <string name="keyguard_charged" msgid="2222329688813033109">"Batareya quvvati to‘ldi"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Simsiz quvvatlanyapti"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Quvvat olmoqda"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Tezkor quvvat olmoqda"</string>
diff --git a/packages/SystemUI/res-keyguard/values-vi/strings.xml b/packages/SystemUI/res-keyguard/values-vi/strings.xml
index b8c199897daa..c50891eb65ef 100644
--- a/packages/SystemUI/res-keyguard/values-vi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-vi/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Nhập mật khẩu của bạn"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Mã PIN không chính xác."</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Thẻ không hợp lệ."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Đã sạc đầy"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Đã sạc đầy"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc không dây"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc nhanh"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
index dfe59d761e41..3f6b89e671b7 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"输入您的密码"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"PIN 码有误。"</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"SIM 卡无效。"</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"充电完成"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"已充满电"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在无线充电"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在充电"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在快速充电"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
index 71696488fc8b..9e497320d40d 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"請輸入密碼"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"PIN 碼不正確。"</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"SIM 卡無效。"</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"充電完成"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"已完成充電"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 無線充電中"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在充電"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在快速充電"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
index f5b981c46638..6f0d2e7adfc1 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"輸入密碼"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"PIN 碼不正確。"</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"卡片無效。"</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"充電完成"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"充電完成"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 無線充電"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電中"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 快速充電中"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zu/strings.xml b/packages/SystemUI/res-keyguard/values-zu/strings.xml
index 6a7f6475809e..f60599df8c86 100644
--- a/packages/SystemUI/res-keyguard/values-zu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zu/strings.xml
@@ -33,7 +33,7 @@
<string name="keyguard_enter_your_password" msgid="5397328359341314506">"Faka iphasiwedi yakho"</string>
<string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Ikhodi ye-PIN engalungile!"</string>
<string name="keyguard_sim_error_message_short" msgid="592109500618448312">"Ikhadi elingavumelekile."</string>
- <string name="keyguard_charged" msgid="3316115607283493413">"Ishaje ngokuphelele"</string>
+ <string name="keyguard_charged" msgid="2222329688813033109">"Kushajiwe"</string>
<string name="keyguard_plugged_in_wireless" msgid="8404159927155454732">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Iyashaja ngaphandle kwentambo"</string>
<string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Iyashaja"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ishaja kaningi"</string>
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 17a7d0835f14..7528d586b0e3 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -62,7 +62,7 @@
<!-- When the lock screen is showing, the phone is plugged in and the battery is fully
charged, say that it is charged. -->
- <string name="keyguard_charged">Fully charged</string>
+ <string name="keyguard_charged">Charged</string>
<!-- When the lock screen is showing and the phone plugged in, and the battery is not fully charged, say that it's wirelessly charging. [CHAR LIMIT=50] -->
<string name="keyguard_plugged_in_wireless"><xliff:g id="percentage" example="20%">%s</xliff:g> • Charging wirelessly</string>
diff --git a/packages/SystemUI/res/drawable-xhdpi/tv_card_gradient_protection.png b/packages/SystemUI/res/drawable-xhdpi/tv_card_gradient_protection.png
new file mode 100644
index 000000000000..135dabb63069
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/tv_card_gradient_protection.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/auth_dialog_lock.xml b/packages/SystemUI/res/drawable/auth_dialog_lock.xml
new file mode 100644
index 000000000000..8146c16e4aaf
--- /dev/null
+++ b/packages/SystemUI/res/drawable/auth_dialog_lock.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="32dp"
+ android:height="32dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="?android:attr/colorAccent"
+ android:pathData="M12,15m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/>
+ <path
+ android:fillColor="?android:attr/colorAccent"
+ android:pathData="M18,8h-1.5V5.5C16.5,3.01 14.49,1 12,1S7.5,3.01 7.5,5.5V8H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10C20,8.9 19.1,8 18,8zM9.5,5.5C9.5,4.12 10.62,3 12,3c1.38,0 2.5,1.12 2.5,2.5V8h-5V5.5zM18,20H6V10h1.5h9H18V20z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/circle_red.xml b/packages/SystemUI/res/drawable/circle_red.xml
new file mode 100644
index 000000000000..fd3c125e5ab8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/circle_red.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <solid android:color="@color/red"/>
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_lightbulb_outline_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_lightbulb_outline_gm2_24px.xml
new file mode 100644
index 000000000000..87684a32e016
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_lightbulb_outline_gm2_24px.xml
@@ -0,0 +1,9 @@
+<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="M9,21c0,0.55 0.45,1 1,1h4c0.55,0 1,-0.45 1,-1v-1L9,20v1zM12,2C8.14,2 5,5.14 5,9c0,2.38 1.19,4.47 3,5.74L8,17c0,0.55 0.45,1 1,1h6c0.55,0 1,-0.45 1,-1v-2.26c1.81,-1.27 3,-3.36 3,-5.74 0,-3.86 -3.14,-7 -7,-7zM14.85,13.1l-0.85,0.6L14,16h-4v-2.3l-0.85,-0.6C7.8,12.16 7,10.63 7,9c0,-2.76 2.24,-5 5,-5s5,2.24 5,5c0,1.63 -0.8,3.16 -2.15,4.1z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/tv_bg_item_app_info.xml b/packages/SystemUI/res/drawable/tv_bg_item_app_info.xml
new file mode 100644
index 000000000000..1bbb8c3e8ef0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/tv_bg_item_app_info.xml
@@ -0,0 +1,22 @@
+<?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.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="24dp"/>
+ <solid android:color="@color/tv_audio_recording_bar_chip_background"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/tv_gradient_protection.xml b/packages/SystemUI/res/drawable/tv_gradient_protection.xml
new file mode 100644
index 000000000000..ee5cbc7e6ba0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/tv_gradient_protection.xml
@@ -0,0 +1,22 @@
+<?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.
+ -->
+
+<!-- gradient protection for cards -->
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/tv_card_gradient_protection"
+ android:tileMode="repeat"
+/>
diff --git a/packages/SystemUI/res/drawable/tv_ic_mic_white.xml b/packages/SystemUI/res/drawable/tv_ic_mic_white.xml
new file mode 100644
index 000000000000..1bea8a19c8b9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/tv_ic_mic_white.xml
@@ -0,0 +1,25 @@
+<?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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:viewportWidth="44"
+ android:viewportHeight="44"
+ android:width="44dp"
+ android:height="44dp">
+ <path
+ android:pathData="M22 25.6666667C25.0433333 25.6666667 27.4816667 23.21 27.4816667 20.1666667L27.5 9.16666667C27.5 6.12333333 25.0433333 3.66666667 22 3.66666667C18.9566667 3.66666667 16.5 6.12333333 16.5 9.16666667L16.5 20.1666667C16.5 23.21 18.9566667 25.6666667 22 25.6666667ZM31.7166667 20.1666667C31.7166667 25.6666667 27.06 29.5166667 22 29.5166667C16.94 29.5166667 12.2833333 25.6666667 12.2833333 20.1666667L9.16666667 20.1666667C9.16666667 26.4183333 14.1533333 31.5883333 20.1666667 32.4866667L20.1666667 38.5L23.8333333 38.5L23.8333333 32.4866667C29.8466667 31.6066667 34.8333333 26.4366667 34.8333333 20.1666667L31.7166667 20.1666667Z"
+ android:fillColor="@android:color/white" />
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
new file mode 100644
index 000000000000..c3fa39e5a87f
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
@@ -0,0 +1,113 @@
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<com.android.systemui.biometrics.AuthCredentialPatternView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:elevation="@dimen/biometric_dialog_elevation">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+ <ImageView
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:background="@drawable/auth_dialog_lock"/>
+
+ <TextView
+ android:id="@+id/title"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:layout_marginTop="12dp"
+ android:textSize="20sp"
+ android:gravity="center"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:layout_marginTop="8dp"
+ android:textSize="16sp"
+ android:gravity="center"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <TextView
+ android:id="@+id/description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:layout_marginTop="8dp"
+ android:gravity="center"
+ android:textSize="16sp"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+ <TextView
+ android:id="@+id/error"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:textSize="16sp"
+ android:gravity="center"
+ android:textColor="?android:attr/colorError"/>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <com.android.internal.widget.LockPatternView
+ android:id="@+id/lockPattern"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="40dp"
+ android:layout_marginRight="40dp"
+ android:layout_gravity="center"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ style="@style/LockPatternStyleBiometricPrompt"/>
+
+ </LinearLayout>
+
+</com.android.systemui.biometrics.AuthCredentialPatternView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_container_view.xml b/packages/SystemUI/res/layout/auth_container_view.xml
index 23199aacc093..3db01a4e7f3a 100644
--- a/packages/SystemUI/res/layout/auth_container_view.xml
+++ b/packages/SystemUI/res/layout/auth_container_view.xml
@@ -34,7 +34,7 @@
android:elevation="@dimen/biometric_dialog_elevation"/>
<ScrollView
- android:id="@+id/scrollview"
+ android:id="@+id/biometric_scrollview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml
new file mode 100644
index 000000000000..4aed0333e9ca
--- /dev/null
+++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml
@@ -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.
+ -->
+
+<com.android.systemui.biometrics.AuthCredentialPasswordView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center_horizontal"
+ android:elevation="@dimen/biometric_dialog_elevation">
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+ <ImageView
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:background="@drawable/auth_dialog_lock"/>
+
+ <TextView
+ android:id="@+id/title"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:layout_marginTop="12dp"
+ android:textSize="20sp"
+ android:gravity="center"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:layout_marginTop="8dp"
+ android:textSize="16sp"
+ android:gravity="center"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <TextView
+ android:id="@+id/description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:layout_marginTop="8dp"
+ android:gravity="center"
+ android:textSize="16sp"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+ <TextView
+ android:id="@+id/error"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:textSize="16sp"
+ android:gravity="center"
+ android:textColor="?android:attr/colorError"/>
+
+ <EditText
+ android:id="@+id/lockPassword"
+ android:layout_marginBottom="20dp"
+ android:layout_marginLeft="100dp"
+ android:layout_marginRight="100dp"
+ android:layout_width="208dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:inputType="textPassword"
+ android:maxLength="500"
+ android:textSize="16sp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:imeOptions="flagForceAscii"
+ style="@style/LockPatternStyleBiometricPrompt"/>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="5"/>
+
+</com.android.systemui.biometrics.AuthCredentialPasswordView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
new file mode 100644
index 000000000000..c9edcd606277
--- /dev/null
+++ b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
@@ -0,0 +1,97 @@
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<com.android.systemui.biometrics.AuthCredentialPatternView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center_horizontal"
+ android:elevation="@dimen/biometric_dialog_elevation">
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+ <ImageView
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:background="@drawable/auth_dialog_lock"/>
+
+ <TextView
+ android:id="@+id/title"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:layout_marginTop="12dp"
+ android:textSize="20sp"
+ android:gravity="center"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:layout_marginTop="8dp"
+ android:textSize="16sp"
+ android:gravity="center"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <TextView
+ android:id="@+id/description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:layout_marginTop="8dp"
+ android:gravity="center"
+ android:textSize="16sp"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="3"/>
+
+ <TextView
+ android:id="@+id/error"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:textSize="16sp"
+ android:gravity="center"
+ android:textColor="?android:attr/colorError"/>
+
+ <com.android.internal.widget.LockPatternView
+ android:id="@+id/lockPattern"
+ android:layout_marginBottom="20dp"
+ android:layout_marginLeft="40dp"
+ android:layout_marginRight="40dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ style="@style/LockPatternStyleBiometricPrompt"/>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+</com.android.systemui.biometrics.AuthCredentialPatternView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/home_controls.xml b/packages/SystemUI/res/layout/home_controls.xml
new file mode 100644
index 000000000000..bb971c206e5c
--- /dev/null
+++ b/packages/SystemUI/res/layout/home_controls.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/home_controls_layout"
+ android:layout_width="match_parent"
+ android:layout_height="125dp"
+ android:layout_gravity="@integer/notification_panel_layout_gravity"
+ android:visibility="gone"
+ android:padding="8dp"
+ android:layout_margin="5dp"
+ android:background="?android:attr/colorBackgroundFloating"
+ android:orientation="vertical">
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml
index 8ba4c9c05ba6..ba6b6956f187 100644
--- a/packages/SystemUI/res/layout/navigation_bar.xml
+++ b/packages/SystemUI/res/layout/navigation_bar.xml
@@ -24,6 +24,21 @@
android:layout_width="match_parent"
android:background="@drawable/system_bar_background">
+ <com.android.systemui.CornerHandleView
+ android:id="@+id/assist_hint_left"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_gravity="left|bottom"
+ android:rotation="270"
+ android:visibility="gone"/>
+ <com.android.systemui.CornerHandleView
+ android:id="@+id/assist_hint_right"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_gravity="right|bottom"
+ android:rotation="180"
+ android:visibility="gone"/>
+
<com.android.systemui.statusbar.phone.NavigationBarInflaterView
android:id="@+id/navigation_inflater"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/people_strip.xml b/packages/SystemUI/res/layout/people_strip.xml
new file mode 100644
index 000000000000..f0ac08bdad37
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_strip.xml
@@ -0,0 +1,239 @@
+<?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.
+ -->
+
+<com.android.systemui.statusbar.notification.stack.PeopleHubView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="105dp">
+
+ <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
+ android:id="@+id/backgroundNormal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
+ android:id="@+id/backgroundDimmed"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <LinearLayout
+ android:id="@+id/people_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp"
+ android:gravity="center"
+ android:orientation="horizontal">
+
+ <View
+ android:layout_width="8dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+
+ <LinearLayout
+ android:layout_width="70dp"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:visibility="invisible">
+
+ <ImageView
+ android:id="@+id/person_icon"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:scaleType="fitCenter"
+ />
+
+ <TextView
+ android:id="@+id/person_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:textAlignment="center"
+ />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="8dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+
+ <View
+ android:layout_width="8dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+
+ <LinearLayout
+ android:layout_width="70dp"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:visibility="invisible">
+
+ <ImageView
+ android:id="@+id/person_icon"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:scaleType="fitCenter"
+ />
+
+ <TextView
+ android:id="@+id/person_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:textAlignment="center"
+ />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="8dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+
+ <View
+ android:layout_width="8dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+
+ <LinearLayout
+ android:layout_width="70dp"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:visibility="invisible">
+
+ <ImageView
+ android:id="@+id/person_icon"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:scaleType="fitCenter"
+ />
+
+ <TextView
+ android:id="@+id/person_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:textAlignment="center"
+ />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="8dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+
+ <View
+ android:layout_width="8dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+
+ <LinearLayout
+ android:layout_width="70dp"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:visibility="invisible">
+
+ <ImageView
+ android:id="@+id/person_icon"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:scaleType="fitCenter"
+ />
+
+ <TextView
+ android:id="@+id/person_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:textAlignment="center"
+ />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="8dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+
+ <View
+ android:layout_width="8dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+
+ <LinearLayout
+ android:layout_width="70dp"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:visibility="invisible">
+
+ <ImageView
+ android:id="@+id/person_icon"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:scaleType="fitCenter"
+ />
+
+ <TextView
+ android:id="@+id/person_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:textAlignment="center"
+ />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="8dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ />
+
+ </LinearLayout>
+
+ <com.android.systemui.statusbar.notification.FakeShadowView
+ android:id="@+id/fake_shadow"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+</com.android.systemui.statusbar.notification.stack.PeopleHubView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qqs_media_panel.xml b/packages/SystemUI/res/layout/qqs_media_panel.xml
new file mode 100644
index 000000000000..1189371fc7f1
--- /dev/null
+++ b/packages/SystemUI/res/layout/qqs_media_panel.xml
@@ -0,0 +1,100 @@
+<?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
+ -->
+
+<!-- Layout for QQS media controls -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/qqs_media_controls"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:padding="10dp"
+ >
+ <!-- Top line: icon + artist name -->
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:gravity="center"
+ >
+ <com.android.internal.widget.CachingIconView
+ android:id="@+id/icon"
+ android:layout_width="15dp"
+ android:layout_height="15dp"
+ android:layout_marginEnd="5dp"
+ />
+ <TextView
+ android:id="@+id/header_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+ android:singleLine="true"
+ />
+ </LinearLayout>
+
+ <!-- Second line: song name -->
+ <TextView
+ android:id="@+id/header_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:fontFamily="@*android:string/config_bodyFontFamily"
+ android:gravity="center"/>
+
+ <!-- Bottom section: controls -->
+ <LinearLayout
+ android:id="@+id/media_actions"
+ android:orientation="horizontal"
+ android:layoutDirection="ltr"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ >
+ <ImageButton
+ style="@android:style/Widget.Material.Button.Borderless.Small"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:padding="8dp"
+ android:layout_marginEnd="2dp"
+ android:gravity="center"
+ android:visibility="gone"
+ android:id="@+id/action0"
+ />
+ <ImageButton
+ style="@android:style/Widget.Material.Button.Borderless.Small"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:padding="8dp"
+ android:layout_marginEnd="2dp"
+ android:gravity="center"
+ android:visibility="gone"
+ android:id="@+id/action1"
+ />
+ <ImageButton
+ style="@android:style/Widget.Material.Button.Borderless.Small"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:padding="8dp"
+ android:layout_marginEnd="2dp"
+ android:gravity="center"
+ android:visibility="gone"
+ android:id="@+id/action2"
+ />
+ </LinearLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_carrier_group.xml b/packages/SystemUI/res/layout/qs_carrier_group.xml
index 56efb4911cd0..f2b0606979cc 100644
--- a/packages/SystemUI/res/layout/qs_carrier_group.xml
+++ b/packages/SystemUI/res/layout/qs_carrier_group.xml
@@ -29,6 +29,9 @@
android:id="@+id/no_carrier_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:minWidth="48dp"
+ android:minHeight="48dp"
+ android:gravity="center_vertical"
android:textAppearance="@style/TextAppearance.QS.Status"
android:textDirection="locale"
android:marqueeRepeatLimit="marquee_forever"
diff --git a/packages/SystemUI/res/layout/qs_media_panel.xml b/packages/SystemUI/res/layout/qs_media_panel.xml
new file mode 100644
index 000000000000..dd422766c153
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_media_panel.xml
@@ -0,0 +1,124 @@
+<?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
+ -->
+
+<!-- Layout for media controls inside QSPanel carousel -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/qs_media_controls"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center_horizontal|fill_vertical"
+ android:padding="10dp"
+ >
+
+ <!-- placeholder for notification header -->
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/header"
+ android:padding="3dp"
+ android:layout_marginEnd="-12dp"
+ />
+
+ <!-- Top line: artist name -->
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ >
+ <TextView
+ android:id="@+id/header_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+ android:singleLine="true"
+ />
+ </LinearLayout>
+
+ <!-- Second line: song name -->
+ <TextView
+ android:id="@+id/header_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:fontFamily="@*android:string/config_bodyFontFamily"
+ android:gravity="center"/>
+
+ <!-- Bottom section: controls -->
+ <LinearLayout
+ android:id="@+id/media_actions"
+ android:orientation="horizontal"
+ android:layoutDirection="ltr"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ >
+ <ImageButton
+ style="@android:style/Widget.Material.Button.Borderless.Small"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:padding="8dp"
+ android:layout_marginEnd="2dp"
+ android:gravity="center"
+ android:visibility="gone"
+ android:id="@+id/action0"
+ />
+ <ImageButton
+ style="@android:style/Widget.Material.Button.Borderless.Small"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:padding="8dp"
+ android:layout_marginEnd="2dp"
+ android:gravity="center"
+ android:visibility="gone"
+ android:id="@+id/action1"
+ />
+ <ImageButton
+ style="@android:style/Widget.Material.Button.Borderless.Small"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:padding="8dp"
+ android:layout_marginEnd="2dp"
+ android:gravity="center"
+ android:visibility="gone"
+ android:id="@+id/action2"
+ />
+ <ImageButton
+ style="@android:style/Widget.Material.Button.Borderless.Small"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:padding="8dp"
+ android:layout_marginEnd="2dp"
+ android:gravity="center"
+ android:visibility="gone"
+ android:id="@+id/action3"
+ />
+ <ImageButton
+ style="@android:style/Widget.Material.Button.Borderless.Small"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:padding="8dp"
+ android:layout_marginEnd="2dp"
+ android:gravity="center"
+ android:visibility="gone"
+ android:id="@+id/action4"
+ />
+ </LinearLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index ed18dc728402..e99b91787072 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -43,7 +43,7 @@
<com.android.systemui.qs.QuickQSPanel
android:id="@+id/quick_qs_panel"
android:layout_width="match_parent"
- android:layout_height="48dp"
+ android:layout_height="wrap_content"
android:layout_below="@id/quick_qs_status_icons"
android:layout_marginStart="@dimen/qs_header_tile_margin_horizontal"
android:layout_marginEnd="@dimen/qs_header_tile_margin_horizontal"
diff --git a/packages/SystemUI/res/layout/rounded_corners.xml b/packages/SystemUI/res/layout/rounded_corners.xml
index b409c8f2ba5a..1849068d91b8 100644
--- a/packages/SystemUI/res/layout/rounded_corners.xml
+++ b/packages/SystemUI/res/layout/rounded_corners.xml
@@ -18,18 +18,6 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <com.android.systemui.CornerHandleView
- android:id="@+id/assist_hint_left"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_gravity="left|top"
- android:visibility="gone"/>
- <com.android.systemui.CornerHandleView
- android:id="@+id/assist_hint_right"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_gravity="right|bottom"
- android:visibility="gone"/>
<ImageView
android:id="@+id/left"
android:layout_width="12dp"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index e7c6b2574f5f..4869be11a906 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -17,14 +17,9 @@
*/
-->
-<com.android.systemui.statusbar.phone.NotificationPanelView
+<merge
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res-auto"
- android:id="@+id/notification_panel"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@android:color/transparent" >
-
+ xmlns:systemui="http://schemas.android.com/apk/res-auto">
<FrameLayout
android:id="@+id/big_clock_container"
android:layout_width="match_parent"
@@ -56,17 +51,7 @@
systemui:viewType="com.android.systemui.plugins.qs.QS" />
<!-- Temporary area to test out home controls -->
- <LinearLayout
- android:id="@+id/home_controls_layout"
- android:layout_width="match_parent"
- android:layout_height="125dp"
- android:layout_gravity="@integer/notification_panel_layout_gravity"
- android:visibility="gone"
- android:padding="8dp"
- android:layout_margin="5dp"
- android:background="?android:attr/colorBackgroundFloating"
- android:orientation="vertical">
- </LinearLayout>
+ <include layout="@layout/home_controls" />
<com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
android:id="@+id/notification_stack_scroller"
@@ -112,5 +97,4 @@
android:background="@drawable/qs_navbar_scrim" />
<include layout="@layout/status_bar_expanded_plugin_frame"/>
-
-</com.android.systemui.statusbar.phone.NotificationPanelView>
+</merge>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml b/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml
index 4849dfb777ed..7d6ff3b16db6 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml
@@ -20,10 +20,10 @@
android:id="@+id/plugin_frame"
android:theme="@style/qs_theme"
android:layout_width="@dimen/qs_panel_width"
- android:layout_height="96dp"
+ android:layout_height="105dp"
android:layout_gravity="center_horizontal"
- android:layout_marginTop="@*android:dimen/quick_qs_total_height"
+ android:layout_marginTop="@dimen/notification_side_paddings"
android:layout_marginLeft="@dimen/notification_side_paddings"
android:layout_marginRight="@dimen/notification_side_paddings"
android:visibility="gone"
- android:background="@drawable/qs_background_primary"/> \ No newline at end of file
+ android:background="@drawable/qs_background_primary"/>
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 9716a00a7f72..57834da76285 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -64,7 +64,7 @@
sysui:ignoreRightInset="true"
/>
- <include layout="@layout/status_bar_expanded"
+ <ViewStub android:id="@+id/status_bar_expanded"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
diff --git a/packages/SystemUI/res/layout/tv_item_app_info.xml b/packages/SystemUI/res/layout/tv_item_app_info.xml
new file mode 100644
index 000000000000..b40589ec80c6
--- /dev/null
+++ b/packages/SystemUI/res/layout/tv_item_app_info.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_marginLeft="8dp"
+ android:paddingHorizontal="12dp"
+ android:gravity="center_vertical"
+ android:background="@drawable/tv_bg_item_app_info">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginRight="8dp"/>
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/tv_audio_recording_bar_text"
+ android:fontFamily="sans-serif"
+ android:textSize="14sp"/>
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_status_bar_audio_recording.xml b/packages/SystemUI/res/layout/tv_status_bar_audio_recording.xml
new file mode 100644
index 000000000000..b9dffbb4de20
--- /dev/null
+++ b/packages/SystemUI/res/layout/tv_status_bar_audio_recording.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:orientation="vertical">
+
+ <!-- Gradient Protector -->
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="102.5dp"
+ android:background="@drawable/tv_gradient_protection"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="72dp"
+ android:background="@color/tv_audio_recording_bar_background"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_marginLeft="42dp"
+ android:layout_marginVertical="12dp"
+ android:padding="8dp"
+ android:background="@drawable/circle_red"
+ android:scaleType="centerInside"
+ android:src="@drawable/tv_ic_mic_white"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="24dp"
+ android:text="Audio recording by"
+ android:textColor="@color/tv_audio_recording_bar_text"
+ android:fontFamily="sans-serif"
+ android:textSize="14sp"/>
+
+ <LinearLayout
+ android:id="@+id/container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 4d451bc62efa..e86fb70b5bd6 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Stel invoer metodes op"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fisiese sleutelbord"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Gee <xliff:g id="APPLICATION">%1$s</xliff:g> toegang tot <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Laat <xliff:g id="APPLICATION">%1$s</xliff:g> toe om by <xliff:g id="USB_DEVICE">%2$s</xliff:g> in te gaan?\nOpneemtoestemming is nie aan hierdie program verleen nie, maar dit kan oudio deur hierdie USB-toestel vasvang."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Gee <xliff:g id="APPLICATION">%1$s</xliff:g> toegang tot <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Maak <xliff:g id="APPLICATION">%1$s</xliff:g> oop om <xliff:g id="USB_DEVICE">%2$s</xliff:g> te hanteer?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Maak <xliff:g id="APPLICATION">%1$s</xliff:g> oop om <xliff:g id="USB_DEVICE">%2$s</xliff:g> te hanteer?\nOpneemtoestemming is nie aan hierdie program verleen nie, maar dit kan oudio deur hierdie USB-toestel vasvang."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Maak <xliff:g id="APPLICATION">%1$s</xliff:g> oop om <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> te hanteer?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Geen geïnstalleerde programme werk met hierdie USB-toebehoorsel nie. Vind meer uit oor hierdie toebehoorsel by <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB-toebehoorsel"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Bevestig"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Tik op Bevestig om te voltooi"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Gestaaf"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Gebruik PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Gebruik patroon"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Gebruik wagwoord"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Verkeerde PIN"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Verkeerde patroon"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Verkeerde wagwoord"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Te veel verkeerde pogings.\nProbeer oor <xliff:g id="NUMBER">%d</xliff:g> sekondes weer."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Raak die vingerafdruksensor"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Vingerafdrukikoon"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Soek tans vir jou …"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Kennisgewings"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Flitslig"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Kamera in gebruik"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobiele data"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Datagebruik"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Oorblywende data"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 4d0a62eeabbc..fed9b0528f7e 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"የግቤት ስልቶችን አዘጋጅ"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"የሚዳሰስ የቁልፍ ሰሌዳ"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="APPLICATION">%1$s</xliff:g> <xliff:g id="USB_DEVICE">%2$s</xliff:g>ን እንዲደርስበት ይፈቀድለት?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="APPLICATION">%1$s</xliff:g> <xliff:g id="USB_DEVICE">%2$s</xliff:g>ን እንዲደርስ ይፈቀድለት?\nይህ መተግበሪያ የመቅዳት ፈቃድ አልተሰጠውም፣ ነገር ግን በዩኤስቢ መሣሪያ በኩል ኦዲዮን መቅዳት ይችላል።"</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g> <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>ን እንዲደርስበት ይፈቀድለት?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g>ን እንዲይዘው <xliff:g id="APPLICATION">%1$s</xliff:g> ይክፈት?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="APPLICATION">%1$s</xliff:g> ን <xliff:g id="USB_DEVICE">%2$s</xliff:g> ለማስተናገድ ይከፈት?\nይህ መተግበሪያ የቅጂ ፈቃድ አልተሰጠውም ሆኖም ግን በዩኤስቢ መሣሪያ በኩል ኦዲዮን መቅዳት ይችላል።"</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>ን እንዲይዘው <xliff:g id="APPLICATION">%1$s</xliff:g> ይክፈት?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"ምንም የተጫኑ መተግበሪያዎች ከዚህ የUSB ተቀጥላ ጋር አይሰሩም። በ<xliff:g id="URL">%1$s</xliff:g> ስለዚህ ተቀጥላ የበለጠ ለመረዳት።"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"የUSB ተቀጥላ"</string>
@@ -108,7 +110,7 @@
<string name="accessibility_phone_button" msgid="6738112589538563574">"ስልክ"</string>
<string name="accessibility_voice_assist_button" msgid="487611083884852965">"የድምጽ እርዳታ"</string>
<string name="accessibility_unlock_button" msgid="128158454631118828">"ክፈት"</string>
- <string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"አሻራን በመጠባበቅ ላይ"</string>
+ <string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"የጣት አሻራን በመጠባበቅ ላይ"</string>
<string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"የጣት አሻራዎን ሳይጠቀሙ ይክፈቱ"</string>
<string name="accessibility_scanning_face" msgid="769545173211758586">"የቅኝት ፊት"</string>
<string name="accessibility_send_smart_reply" msgid="7766727839703044493">"ላክ"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"ተረጋግጧል"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"ለማጠናቀቅ አረጋግጥን መታ ያድርጉ"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"የተረጋገጠ"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"ፒን ይጠቀሙ"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"ሥርዓተ ጥለትን ተጠቀም"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"የይለፍ ቃልን ተጠቀም"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"የተሳሳተ ፒን"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"የተሳሳተ ሥርዓተ ጥለት"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"የተሳሳተ የይለፍ ቃል"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"ከልክ በላይ ብዙ የተሳሳቱ ሙከራዎች።\nበ<xliff:g id="NUMBER">%d</xliff:g> ሰከንዶች ውስጥ እንደገና ይሞክሩ።"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"የጣት አሻራ ዳሳሹን ይንኩ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"የጣት አሻራ አዶ"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"እርስዎን በመፈለግ ላይ…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"ማሳወቂያዎች"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"የባትሪ ብርሃን"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"ካሜራ ስራ ላይ ነው"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"የተንቀሳቃሽ ስልክ ውሂብ"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"የውሂብ አጠቃቀም"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"ቀሪ ውሂብ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 787fc8d48e8f..b04df54b84a9 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"إعداد أسلوب الإدخال"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"لوحة مفاتيح فعلية"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"هل تريد السماح لتطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> بالدخول إلى <xliff:g id="USB_DEVICE">%2$s</xliff:g>؟"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"‏هل تريد السماح لتطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> بالدخول إلى <xliff:g id="USB_DEVICE">%2$s</xliff:g>؟\nلم يتم منح هذا التطبيق إذن تسجيل، ولكن يمكنه تسجيل الصوت من خلال جهاز USB هذا."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"هل تريد السماح لتطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> بالدخول إلى <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>؟"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"هل تريد فتح <xliff:g id="APPLICATION">%1$s</xliff:g> للتعامل مع <xliff:g id="USB_DEVICE">%2$s</xliff:g>؟"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"‏هل تريد فتح تطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> للتعامل مع <xliff:g id="USB_DEVICE">%2$s</xliff:g>؟\nلم يتم منح هذا التطبيق إذن تسجيل، ولكن يمكنه تسجيل الصوت من خلال جهاز USB هذا."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"هل تريد فتح تطبيق <xliff:g id="APPLICATION">%1$s</xliff:g> للتعامل مع <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>؟"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"‏لا يعمل أي تطبيق مثبت مع ملحق UEB هذا. مزيد من المعلومات عن هذا الملحق على <xliff:g id="URL">%1$s</xliff:g>."</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"‏ملحق USB"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"تمّ التأكيد."</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"يمكنك النقر على \"تأكيد\" لإكمال المهمة."</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"مصادقة"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"استخدام رقم تعريف شخصي"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"استخدام نقش"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"استخدام كلمة المرور"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"رقم تعريف شخصي خاطئ"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"نقش غير صحيح"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"كلمة مرور غير صحيحة"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"تم إجراء عدد كبير جدًا من المحاولات غير الصحيحة.\nأعد المحاولة خلال <xliff:g id="NUMBER">%d</xliff:g> ثانية."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"المس زر استشعار بصمة الإصبع"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"رمز بصمة الإصبع"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"جارٍ البحث عن وجهك…"</string>
@@ -372,6 +381,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"الإشعارات"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"الفلاش"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"الكاميرا قيد الاستخدام"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"بيانات الجوّال"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"استخدام البيانات"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"البيانات المتبقية"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index d7feb0cc7f12..7efb03cda804 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -47,8 +47,11 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ইনপুট পদ্ধতি ছেট আপ কৰক"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"বাস্তৱিক কীব\'ৰ্ড"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="USB_DEVICE">%2$s</xliff:g>ত প্ৰৱেশ কৰিবলৈ <xliff:g id="APPLICATION">%1$s</xliff:g>ক অনুমতি দিবনে?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="APPLICATION">%1$s</xliff:g>ক <xliff:g id="USB_DEVICE">%2$s</xliff:g> এক্সেছ কৰিবলৈ অনুমতি দিবনে?\nএই এপ্‌টোক ৰেকর্ড কৰাৰ অনুমতি দিয়া হোৱা নাই কিন্তু ই এই ইউএছবি ডিভাইচটোৰ জৰিয়তে অডিঅ\' ৰেকর্ড কৰিব পাৰে।"</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g>ক <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>ত প্ৰৱেশ কৰিবলৈ অনুমতি দিবনে?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g>ক ব্যৱহাৰ কৰিবলৈ <xliff:g id="APPLICATION">%1$s</xliff:g>ক খোলেনে?"</string>
+ <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) -->
+ <skip />
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>ক ব্যৱহাৰ কৰিবলৈ <xliff:g id="APPLICATION">%1$s</xliff:g>ক খোলেনে?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"ইনষ্টল হৈ থকা কোনো এপে ইউএছবি সহায়ক সামগ্ৰীটো চলাব নোৱাৰে। এই সহায়ক সামগ্ৰীৰ বিষয়ে <xliff:g id="URL">%1$s</xliff:g>ৰ জৰিয়তে অধিক জানক৷"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"ইউএছবিৰ সহায়ক সামগ্ৰী"</string>
@@ -126,6 +129,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"নিশ্চিত কৰিলে"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"সম্পূৰ্ণ কৰিবলৈ নিশ্চিত কৰক-ত টিপক"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ’ল"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"পিন ব্যৱহাৰ কৰক"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"আৰ্হি ব্যৱহাৰ কৰক"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"পাছৱৰ্ড ব্যৱহাৰ কৰক"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"ভুল পিন"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"ভুল আৰ্হি"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"ভুল পাছৱৰ্ড"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"বহুসংখ্যক ভুল প্ৰয়াস।\n<xliff:g id="NUMBER">%d</xliff:g>ছেকেণ্ডত পুনৰ চেষ্টা কৰক।"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ফিংগাৰপ্ৰিণ্ট ছেন্সৰটো স্পৰ্শ কৰক"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ফিংগাৰপ্ৰিণ্ট আইকন"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"আপোনাৰ মুখমণ্ডল বিচাৰি আছে…"</string>
@@ -364,6 +374,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"জাননীসমূহ"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ফ্লাশ্বলাইট"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"কেমেৰা ব্যৱহাৰ হৈ আছে"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"ম’বাইল ডেটা"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"ডেটা ব্যৱহাৰ"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"বাকী থকা ডেটা"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index f6cd5dc2fd4a..9d851264cf72 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Daxiletmə metodlarını ayarlayın"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fiziki klaviatura"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="APPLICATION">%1$s</xliff:g> tətbiqinə <xliff:g id="USB_DEVICE">%2$s</xliff:g> cihazına giriş icazəsi verilsin?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="APPLICATION">%1$s</xliff:g> tətbiqinə <xliff:g id="USB_DEVICE">%2$s</xliff:g> cihazına giriş icazəsi verilsin?\nBu tətbiqə qeydə almaq icazəsi verilməyib lakin, bu USB vasitəsilə səs yaza bilər."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g> tətbiqinə <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> cihazına giriş icazəsi verilsin?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> cihazını idarə etmək üçün <xliff:g id="APPLICATION">%1$s</xliff:g> tətbiqi açılsın?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> cihazını idarə etmək üçün <xliff:g id="APPLICATION">%1$s</xliff:g> tətbiqi açılsın?\nBu tətbiqə yazmaq icazəsi verilməyib, lakin, bu USB vasitəsilə səs yaza bilər."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> cihazını idarə etmək üçün <xliff:g id="APPLICATION">%1$s</xliff:g> tətbiqi açılsın?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Heç bir quraşdırılmış tətbiq bu USB aksesuar ilə işləmir. Bu aksesuar haqqında daha ətraflı məlumatı <xliff:g id="URL">%1$s</xliff:g> adresindən öyrənin"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB aksesuar"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Təsdiqləndi"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Tamamlamaq üçün \"Təsdiq edin\" seçiminə toxunun"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Doğrulandı"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"PIN istifadə edin"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Model istifadə edin"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Parol istifadə edin"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Yanlış PIN"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Yanlış model"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Yanlış parol"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Həddindən çox yanlış cəhd.\n<xliff:g id="NUMBER">%d</xliff:g> saniyəyə yenidən cəhd edin."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Barmaq izi sensoruna klikləyin"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Barmaq izi ikonası"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Siz axtarılırsınız…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Bildirişlər"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"İşartı"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Kamera istifadə olunur"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobil data"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Data istifadəsi"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Qalan data"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 2196c15354f5..07c321d68565 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Podesi metode unosa"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fizička tastatura"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Želite li da dozvolite da <xliff:g id="APPLICATION">%1$s</xliff:g> pristupa uređaju <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Želite li da dozvolite da <xliff:g id="APPLICATION">%1$s</xliff:g> pristupa uređaju <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nOva aplikacija nema dozvolu za snimanje, ali bi mogla da snima zvuk pomoću ovog USB uređaja."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Želite li da dozvolite da <xliff:g id="APPLICATION">%1$s</xliff:g> pristupa uređaju <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Želite li da otvorite aplikaciju <xliff:g id="APPLICATION">%1$s</xliff:g> da biste koristili uređaj <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Želite li da otvorite aplikaciju <xliff:g id="APPLICATION">%1$s</xliff:g> radi rukovanja uređajem <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nOva aplikacija nema dozvolu za snimanje, ali bi mogla da snima zvuk pomoću ovog USB uređaja."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Želite li da otvorite aplikaciju <xliff:g id="APPLICATION">%1$s</xliff:g> da biste koristili uređaj <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Instalirane aplikacije ne funkcionišu sa ovim USB pomoćnim uređajem. Saznajte više o njemu na adresi <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB pomoćni uređaj"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Potvrđeno"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Dodirnite Potvrdi da biste završili"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Identitet je potvrđen"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Koristite PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Koristite šablon"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Koristite lozinku"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Pogrešan PIN"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Pogrešan šablon"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Pogrešna lozinka"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Previše netačnih pokušaja.\n Probajte ponovo za <xliff:g id="NUMBER">%d</xliff:g> sek."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Dodirnite senzor za otisak prsta"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona otiska prsta"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Tražimo vas…"</string>
@@ -366,6 +375,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Obaveštenja"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Lampa"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Koristi se kamera"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobilni podaci"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Potrošnja podataka"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Preostala količina podataka"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 06ee3e19c735..ec73200afa3f 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Налада метадаў уводу"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Фізічная клавіятура"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Дазволіць праграме <xliff:g id="APPLICATION">%1$s</xliff:g> доступ да прылады <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Даць праграме \"<xliff:g id="APPLICATION">%1$s</xliff:g>\" доступ да прылады \"<xliff:g id="USB_DEVICE">%2$s</xliff:g>\"?\nУ гэтай праграмы няма дазволу на запіс, аднак яна зможа запісваць аўдыя праз гэту прыладу USB."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Дазволіць праграме <xliff:g id="APPLICATION">%1$s</xliff:g> доступ да прылады <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Адкрыць праграму <xliff:g id="APPLICATION">%1$s</xliff:g> для працы з прыладай <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Адкрыць праграму \"<xliff:g id="APPLICATION">%1$s</xliff:g>\", каб выкарыстоўваць прыладу \"<xliff:g id="USB_DEVICE">%2$s</xliff:g>\"?\nУ гэтай праграмы няма дазволу на запіс, аднак яна зможа запісваць аўдыя праз гэту USB-прыладу."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Адкрыць праграму <xliff:g id="APPLICATION">%1$s</xliff:g> для працы з прыладай <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Няма ўсталяв. прыкл. для працы з гэтай прыл. USB. Больш падраб. пра гэтую прыл.: <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB-прылада"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Пацверджана"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Націсніце \"Пацвердзіць\", каб завяршыць"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Распазнана"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Увесці PIN-код"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Выкарыстаць узор разблакіроўкі"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Выкарыстаць пароль"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Няправільны PIN-код"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Няправільны ўзор разблакіроўкі"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Няправільны пароль"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Занадта шмат няўдалых спроб.\nПаспрабуйце зноў праз <xliff:g id="NUMBER">%d</xliff:g> с."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Дакраніцеся да сканера адбіткаў пальцаў"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Значок адбіткаў пальцаў"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Ідзе пошук вашага твару…"</string>
@@ -370,6 +379,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Апавяшчэнні"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Ліхтарык"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Камера выкарыстоўваецца"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Мабільная перадача даных"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Выкарыстанне трафіка"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Засталося трафіку"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 6f37a7568ea7..6eeae8f00bfe 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Методи на въвеждане: Настройка"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Физическа клавиатура"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Да се разреши ли на <xliff:g id="APPLICATION">%1$s</xliff:g> достъп до <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Наистина ли искате да разрешите на <xliff:g id="APPLICATION">%1$s</xliff:g> достъп до <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nНа приложението не е предоставено разрешение за записване, но е възможно да запише звук чрез това USB устройство."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Да се разреши ли на <xliff:g id="APPLICATION">%1$s</xliff:g> достъп до <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Да се използва ли <xliff:g id="APPLICATION">%1$s</xliff:g> за работата с/ъс <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Искате ли да използвате <xliff:g id="APPLICATION">%1$s</xliff:g> за работа с(ъс) <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nПриложението няма разрешение за записване, но може да записва звук чрез това USB устройство."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Да се използва ли <xliff:g id="APPLICATION">%1$s</xliff:g> за работата с/ъс <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Инстал. приложения не работят с този аксесоар за USB. Научете повече на адрес <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Аксесоар за USB"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Потвърдено"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Докоснете „Потвърждаване“ за завършване"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Удостоверено"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Използване на ПИН"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Използване на фигура"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Използване на парола"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Грешен ПИН"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Грешна фигура"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Грешна парола"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Твърде много неправилни опити.\nОпитайте отново след <xliff:g id="NUMBER">%d</xliff:g> секунди."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Докоснете сензора за отпечатъци"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Икона за отпечатък"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Търсим ви…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Известия"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Фенерче"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Камерата се използва"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Мобилни данни"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Пренос на данни"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Оставащи данни"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 9c33f2be5aea..8e4e9a18f27c 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ইনপুট পদ্ধতিগুলি সেট-আপ করুন"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"ফিজিক্যাল কীবোর্ড"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="APPLICATION">%1$s</xliff:g> কে <xliff:g id="USB_DEVICE">%2$s</xliff:g> অ্যাক্সেস করতে দেবেন?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> অ্যাক্সেস করতে <xliff:g id="APPLICATION">%1$s</xliff:g>-কে কি অনুমতি দেবেন?\nএই অ্যাপকে রেকর্ড করার অনুমতি দেওয়া হয়নি কিন্তু USB ডিভাইসের মাধ্যমে সেটি অডিও রেকর্ড করতে পারে।"</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g> কে <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> অ্যাক্সেস করতে দেবেন?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ব্যবহার করার জন্য <xliff:g id="APPLICATION">%1$s</xliff:g> চালু করবেন?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> অ্যাক্সেস করার জন্য <xliff:g id="APPLICATION">%1$s</xliff:g> খুলবেন?\nএই অ্যাপকে রেকর্ড করার অনুমতি দেওয়া হয়নি কিন্তু USB ডিভাইসের মাধ্যমে সেটি অডিও রেকর্ড করতে পারে।"</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ব্যবহার করার জন্য <xliff:g id="APPLICATION">%1$s</xliff:g> চালু করবেন?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"ইনস্টল থাকা কোনো অ্যাপ্লিকেশান এই USB যন্ত্রাংশের সাথে কাজ করে না৷ <xliff:g id="URL">%1$s</xliff:g> এ এই যন্ত্রাংশের সম্পর্কে আরও জানুন৷"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB যন্ত্রাংশ"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"কনফার্ম করা হয়েছে"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"সম্পূর্ণ করতে \'কনফার্ম করুন\' বোতামে ট্যাপ করুন"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"প্রমাণীকৃত"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"পিন ব্যবহার করুন"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"প্যাটার্ন ব্যবহার করুন"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"পাসওয়ার্ড ব্যবহার করুন"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"ভুল পিন"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"ভুল প্যাটার্ন"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"ভুল পাসওয়ার্ড"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"অনেকবার ভুল চেষ্টা করা হয়েছে। \n<xliff:g id="NUMBER">%d</xliff:g> সেকেন্ডের মধ্যে আবার চেষ্টা করুন।"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"আঙ্গুলের ছাপের সেন্সর স্পর্শ করুন"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"আঙ্গুলের ছাপের আইকন"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"আপনার জন্য খোঁজা হচ্ছে…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"বিজ্ঞপ্তি"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ফ্ল্যাশলাইট"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"ক্যামেরা ব্যবহার করা হচ্ছে"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"মোবাইল ডেটা"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"ডেটার ব্যবহার"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"অবশিষ্ট ডেটা"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 2d776265df66..c5f94bdb83e4 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Postavljanje načina unosa"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fizička tastatura"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Dozvoliti aplikaciji <xliff:g id="APPLICATION">%1$s</xliff:g> pristup uređaju: <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Dozvoliti aplikaciji <xliff:g id="APPLICATION">%1$s</xliff:g> pristup uređaju <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nOvoj aplikaciji nije dato odobrenje za snimanje, ali može snimati zvuk putem ovog USB uređaja."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Dozvoliti aplikaciji <xliff:g id="APPLICATION">%1$s</xliff:g> pristup dodatku: <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Otvoriti aplikaciju <xliff:g id="APPLICATION">%1$s</xliff:g> za upravljanje uređajem: <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Želite li upravljati uređajem <xliff:g id="USB_DEVICE">%2$s</xliff:g> putem aplikacije <xliff:g id="APPLICATION">%1$s</xliff:g>?\nOvoj aplikaciji nije dato odobrenje za snimanje, ali može snimati zvuk putem ovog USB uređaja."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Otvoriti aplikaciju <xliff:g id="APPLICATION">%1$s</xliff:g> za upravljanje dodatkom: <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Nema instaliranih aplikacija za ovaj USB uređaj. Saznajte više o uređaju na <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB periferni uređaj"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Potvrđeno"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Dodirnite Potvrdi da završite"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autentificirano"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Koristi PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Koristi uzorak"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Koristi lozinku"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Pogrešan PIN kôd"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Pogrešan uzorak"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Pogrešna lozinka"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Previše pogrešnih pokušaja.\n Pokušajte ponovo za <xliff:g id="NUMBER">%d</xliff:g> s."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Dodirnite senzor za otisak prsta"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona za otisak prsta"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Tražimo vas…"</string>
@@ -366,6 +375,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Obavještenja"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Svjetiljka"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Kamera u upotrebi"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Prijenos podataka na mobilnoj mreži"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Prijenos podataka"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Preostala količina podataka"</string>
@@ -462,7 +472,7 @@
<string name="media_projection_dialog_title" msgid="8124184308671641248">"Izlaganje osjetljivih podataka za vrijeme emitiranja/snimanja"</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"Ne prikazuj opet"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Očisti sve"</string>
- <string name="manage_notifications_text" msgid="2386728145475108753">"Upravljaj"</string>
+ <string name="manage_notifications_text" msgid="2386728145475108753">"Upravljajte"</string>
<string name="notification_section_header_gentle" msgid="4372438504154095677">"Nečujna obavještenja"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Obriši sva nečujna obavještenja"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Obavještenja su pauzirana načinom rada Ne ometaj"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index ee16c58909ea..2ae91dd9bebc 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -47,8 +47,11 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configura els mètodes d\'entrada"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Teclat físic"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Vols permetre que <xliff:g id="APPLICATION">%1$s</xliff:g> accedeixi a <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Vols permetre que <xliff:g id="APPLICATION">%1$s</xliff:g> accedeixi a <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nAquesta aplicació no té permís de gravació, però pot capturar àudio a través d\'aquest dispositiu USB."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Vols permetre que <xliff:g id="APPLICATION">%1$s</xliff:g> accedeixi a <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Vols obrir <xliff:g id="APPLICATION">%1$s</xliff:g> per gestionar <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) -->
+ <skip />
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Vols obrir <xliff:g id="APPLICATION">%1$s</xliff:g> per gestionar <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Les aplicacions instal·lades no funcionen amb l\'accessori USB. Més informació: <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Accessori USB"</string>
@@ -108,8 +111,8 @@
<string name="accessibility_phone_button" msgid="6738112589538563574">"Telèfon"</string>
<string name="accessibility_voice_assist_button" msgid="487611083884852965">"Assistència per veu"</string>
<string name="accessibility_unlock_button" msgid="128158454631118828">"Desbloqueja"</string>
- <string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"S\'està esperant l\'empremta digital"</string>
- <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"Desbloqueja sense utilitzar l\'empremta digital"</string>
+ <string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"S\'està esperant l\'empremta dactilar"</string>
+ <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"Desbloqueja sense utilitzar l\'empremta dactilar"</string>
<string name="accessibility_scanning_face" msgid="769545173211758586">"S\'està escanejant la cara"</string>
<string name="accessibility_send_smart_reply" msgid="7766727839703044493">"Envia"</string>
<string name="accessibility_manage_notification" msgid="2026361503393549753">"Gestiona les notificacions"</string>
@@ -126,8 +129,15 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmat"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Toca Confirma per completar"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autenticat"</string>
- <string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Toca el sensor d\'empremtes digitals"</string>
- <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icona d\'empremta digital"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Utilitza el PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Utilitza el patró"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Utilitza la contrasenya"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN incorrecte"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Patró incorrecte"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Contrasenya incorrecta"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Has superat el nombre d\'intents incorrectes permesos.\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER">%d</xliff:g> segons."</string>
+ <string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Toca el sensor d\'empremtes dactilars"</string>
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icona d\'empremta dactilar"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"S\'està cercant la teva cara…"</string>
<string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Icona facial"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botó de zoom de compatibilitat."</string>
@@ -364,6 +374,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificacions"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Llanterna"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Càmera en ús"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Dades mòbils"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Ús de dades"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Dades restants"</string>
@@ -459,7 +470,7 @@
<string name="media_projection_dialog_title" msgid="8124184308671641248">"Es mostra informació sensible durant l\'emissió o la gravació"</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"No ho tornis a mostrar"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Esborra-ho tot"</string>
- <string name="manage_notifications_text" msgid="2386728145475108753">"Gestió"</string>
+ <string name="manage_notifications_text" msgid="2386728145475108753">"Gestiona"</string>
<string name="notification_section_header_gentle" msgid="4372438504154095677">"Notificacions silencioses"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Esborra totes les notificacions silencioses"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notificacions pausades pel mode No molestis"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index bed63db83d1a..916986f2c0a4 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Nastavit metody zadávání"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fyzická klávesnice"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Povolit aplikaci <xliff:g id="APPLICATION">%1$s</xliff:g> přístup k zařízení <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Povolit aplikaci <xliff:g id="APPLICATION">%1$s</xliff:g> přístup k zařízení <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nTato aplikace nemá oprávnění k nahrávání, ale může zaznamenávat zvuk prostřednictvím tohoto zařízení USB."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Povolit aplikaci <xliff:g id="APPLICATION">%1$s</xliff:g> přístup k zařízení <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Otevřít aplikaci <xliff:g id="APPLICATION">%1$s</xliff:g> ke správě zařízení <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Otevřít aplikaci <xliff:g id="APPLICATION">%1$s</xliff:g> ke správě zařízení <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nTato aplikace nemá oprávnění k nahrávání, ale může zaznamenávat zvuk prostřednictvím tohoto zařízení USB."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Otevřít aplikaci <xliff:g id="APPLICATION">%1$s</xliff:g> ke správě zařízení <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Žádná nainstalovaná aplikace s tímto zařízením USB nepracuje. Info. najdete na <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Periferní zařízení USB"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Potvrzeno"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Ověření dokončíte klepnutím na Potvrdit"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Ověřeno"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Použít kód PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Použít gesto"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Použít heslo"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Nesprávný kód PIN"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Nesprávné gesto"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Nesprávné heslo"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Příliš mnoho neplatných pokusů.\nZkuste to znovu za <xliff:g id="NUMBER">%d</xliff:g> s."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Dotkněte se snímače otisků prstů"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona otisku prstu"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Hledáme vás…"</string>
@@ -368,6 +377,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Oznámení"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Svítilna"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Fotoaparát se používá"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobilní data"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Využití dat"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Zbývající data"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 45ef5b14f2e9..99fa80e3e77d 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Konfigurer inputmetoder"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fysisk tastatur"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Vil du give <xliff:g id="APPLICATION">%1$s</xliff:g> adgang til <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Vil du give <xliff:g id="APPLICATION">%1$s</xliff:g> adgang til <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nDenne app har ikke fået tilladelse til at optage, men optager muligvis lyd via denne USB-enhed."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Vil du give <xliff:g id="APPLICATION">%1$s</xliff:g> adgang til <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Vil du åbne <xliff:g id="APPLICATION">%1$s</xliff:g> til håndtering af <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Vil du åbne <xliff:g id="APPLICATION">%1$s</xliff:g> for at håndtere <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nDenne app har ikke fået tilladelse til at optage, men optager muligvis lyd via denne USB-enhed."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Vil du åbne <xliff:g id="APPLICATION">%1$s</xliff:g> til håndtering af <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Ingen installerede apps fungerer sammen med USB-enheden. Få oplysninger om enheden på <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB-ekstraudstyr"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Bekræftet"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Tryk på Bekræft for at udføre"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Godkendt"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Brug pinkode"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Brug mønster"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Brug adgangskode"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Forkert pinkode"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Forkert mønster"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Forkert adgangskode"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"For mange mislykkede forsøg. \nPrøv igen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Sæt fingeren på fingeraftrykslæseren"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikon for fingeraftryk"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Forsøger at finde dig…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifikationer"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Lommelygte"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Kameraet er i brug"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobildata"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Dataforbrug"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Resterende data"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 1deceb17bf01..aced5d7f56b9 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Eingabemethoden festlegen"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Physische Tastatur"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="APPLICATION">%1$s</xliff:g> den Zugriff auf <xliff:g id="USB_DEVICE">%2$s</xliff:g> gewähren?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Der App \"<xliff:g id="APPLICATION">%1$s</xliff:g>\" Zugriff auf das Gerät \"<xliff:g id="USB_DEVICE">%2$s</xliff:g>\" geben?\nDiese App hat noch nicht die Berechtigung zum Aufnehmen erhalten, könnte jedoch Audio über dieses USB-Gerät aufnehmen."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g> den Zugriff auf <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> gewähren?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Für <xliff:g id="USB_DEVICE">%2$s</xliff:g> <xliff:g id="APPLICATION">%1$s</xliff:g> öffnen?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="APPLICATION">%1$s</xliff:g> öffnen, um <xliff:g id="USB_DEVICE">%2$s</xliff:g> zu bedienen?\nDiese App hat noch keine Berechtigung zum Aufnehmen erhalten, könnte aber Audioaufnahmen über dieses USB-Gerät machen."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Für <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> <xliff:g id="APPLICATION">%1$s</xliff:g> öffnen?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Keine installierten Apps für dieses USB-Zubehör. Weitere Informationen unter <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB-Zubehör"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Bestätigt"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Zum Abschließen auf \"Bestätigen\" tippen"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Authentifiziert"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"PIN verwenden"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Muster verwenden"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Passwort verwenden"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Falsche PIN"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Falsches Muster"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Falsches Passwort"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Zu viele Fehlversuche.\nBitte probiere es in <xliff:g id="NUMBER">%d</xliff:g> Sekunden noch einmal."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Berühre den Fingerabdrucksensor"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingerabdruck-Symbol"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Wir suchen nach dir…"</string>
@@ -368,6 +377,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Benachrichtigungen"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Taschenlampe"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Kamera wird verwendet"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobile Daten"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Datennutzung"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Verbleibende Daten"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 2b57ab58bc77..044d39644ef2 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Ρύθμιση μεθόδων εισαγωγής"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Φυσικό πληκτρολόγιο"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Να επιτρέπεται η πρόσβαση της εφαρμογής <xliff:g id="APPLICATION">%1$s</xliff:g> στη συσκευή <xliff:g id="USB_DEVICE">%2$s</xliff:g>;"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Να επιτρέπεται στην εφαρμογή <xliff:g id="APPLICATION">%1$s</xliff:g> να έχει πρόσβαση στη συσκευή <xliff:g id="USB_DEVICE">%2$s</xliff:g>;\nΔεν έχει εκχωρηθεί άδεια εγγραφής σε αυτήν την εφαρμογή, αλλά μέσω αυτής της συσκευής USB θα μπορεί να εγγράφει ήχο."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Να επιτρέπεται η πρόσβαση της εφαρμογής <xliff:g id="APPLICATION">%1$s</xliff:g> στο αξεσουάρ <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>;"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Να ανοίγει η εφαρμογή <xliff:g id="APPLICATION">%1$s</xliff:g> για τη διαχείριση της συσκευής <xliff:g id="USB_DEVICE">%2$s</xliff:g>;"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Άνοιγμα της εφαρμογής <xliff:g id="APPLICATION">%1$s</xliff:g> για τον χειρισμό της συσκευής <xliff:g id="USB_DEVICE">%2$s</xliff:g>;\nΔεν έχει εκχωρηθεί άδεια εγγραφής σε αυτήν την εφαρμογή, αλλά μέσω αυτής της συσκευής USB θα μπορεί να εγγράφει ήχο."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Να ανοίγει η εφαρμογή <xliff:g id="APPLICATION">%1$s</xliff:g> για τη διαχείριση του αξεσουάρ <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>;"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Δεν έχετε εφαρμογή που να συνεργάζεται με το αξεσουάρ USB. Για περισσότερα: <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Αξεσουάρ USB"</string>
@@ -109,7 +111,7 @@
<string name="accessibility_voice_assist_button" msgid="487611083884852965">"Φωνητική υποβοήθηση"</string>
<string name="accessibility_unlock_button" msgid="128158454631118828">"Ξεκλείδωμα"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"Αναμονή για δακτυλικό αποτύπωμα"</string>
- <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"Ξεκλείδωμα χωρίς τη χρήση του μοναδικού χαρακτηριστικού σας"</string>
+ <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"Ξεκλείδωμα χωρίς τη χρήση του δακτυλικού αποτυπώματός σας"</string>
<string name="accessibility_scanning_face" msgid="769545173211758586">"Σάρωση προσώπου"</string>
<string name="accessibility_send_smart_reply" msgid="7766727839703044493">"Αποστολή"</string>
<string name="accessibility_manage_notification" msgid="2026361503393549753">"Διαχείριση ειδοποιήσεων"</string>
@@ -126,7 +128,14 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Επιβεβαιώθηκε"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Πατήστε Επιβεβαίωση για ολοκλήρωση"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Ολοκληρώθηκε ο έλεγχος ταυτότητας"</string>
- <string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Αγγίξτε τον αισθητήρα δακτυλικών αποτυπωμάτων"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Χρήση PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Χρήση μοτίβου"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Χρήση κωδικού πρόσβασης"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Εσφαλμένο PIN"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Εσφαλμένο μοτίβο"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Εσφαλμένος κωδικός πρόσβασης"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Πάρα πολλές αποτυχημένες προσπάθειες.\nΔοκιμάστε ξανά σε <xliff:g id="NUMBER">%d</xliff:g> δευτερόλεπτα."</string>
+ <string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Αγγίξτε τον αισθητήρα δακτυλικού αποτυπώματος"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Εικονίδιο δακτυλικών αποτυπωμάτων"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Αναζήτηση για εσάς…"</string>
<string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Εικονίδιο προσώπου"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Ειδοποιήσεις"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Φακός"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Η κάμερα χρησιμοποιείται"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Δεδομένα κινητής τηλεφωνίας"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Χρήση δεδομένων"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Υπολειπόμενα δεδομένα"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 7a6271f9710b..5bf1b0f6d94f 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Set up input methods"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Physical keyboard"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Allow <xliff:g id="APPLICATION">%1$s</xliff:g> to access <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Allow <xliff:g id="APPLICATION">%1$s</xliff:g> to access <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nThis app has not been granted record permission but could capture audio through this USB device."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Allow <xliff:g id="APPLICATION">%1$s</xliff:g> to access <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Open <xliff:g id="APPLICATION">%1$s</xliff:g> to handle <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Open <xliff:g id="APPLICATION">%1$s</xliff:g> to handle <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nThis app has not been granted record permission but could capture audio through this USB device."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Open <xliff:g id="APPLICATION">%1$s</xliff:g> to handle <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"No installed apps work with this USB accessory. Learn more about this accessory at <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB accessory"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmed"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Tap Confirm to complete"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Authenticated"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Use PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Use pattern"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Use password"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Wrong PIN"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Wrong pattern"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Wrong password"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Too many incorrect attempts.\nTry again in <xliff:g id="NUMBER">%d</xliff:g> seconds."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Touch the fingerprint sensor"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingerprint icon"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Looking for you…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Torch"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Camera in use"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobile data"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Data usage"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Remaining data"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index fe19e7c21d8c..a327151d984f 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Set up input methods"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Physical keyboard"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Allow <xliff:g id="APPLICATION">%1$s</xliff:g> to access <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Allow <xliff:g id="APPLICATION">%1$s</xliff:g> to access <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nThis app has not been granted record permission but could capture audio through this USB device."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Allow <xliff:g id="APPLICATION">%1$s</xliff:g> to access <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Open <xliff:g id="APPLICATION">%1$s</xliff:g> to handle <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Open <xliff:g id="APPLICATION">%1$s</xliff:g> to handle <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nThis app has not been granted record permission but could capture audio through this USB device."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Open <xliff:g id="APPLICATION">%1$s</xliff:g> to handle <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"No installed apps work with this USB accessory. Learn more about this accessory at <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB accessory"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmed"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Tap Confirm to complete"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Authenticated"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Use PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Use pattern"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Use password"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Wrong PIN"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Wrong pattern"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Wrong password"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Too many incorrect attempts.\nTry again in <xliff:g id="NUMBER">%d</xliff:g> seconds."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Touch the fingerprint sensor"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingerprint icon"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Looking for you…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Flashlight"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Camera in use"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobile data"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Data usage"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Remaining data"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 7a6271f9710b..5bf1b0f6d94f 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Set up input methods"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Physical keyboard"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Allow <xliff:g id="APPLICATION">%1$s</xliff:g> to access <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Allow <xliff:g id="APPLICATION">%1$s</xliff:g> to access <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nThis app has not been granted record permission but could capture audio through this USB device."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Allow <xliff:g id="APPLICATION">%1$s</xliff:g> to access <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Open <xliff:g id="APPLICATION">%1$s</xliff:g> to handle <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Open <xliff:g id="APPLICATION">%1$s</xliff:g> to handle <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nThis app has not been granted record permission but could capture audio through this USB device."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Open <xliff:g id="APPLICATION">%1$s</xliff:g> to handle <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"No installed apps work with this USB accessory. Learn more about this accessory at <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB accessory"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmed"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Tap Confirm to complete"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Authenticated"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Use PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Use pattern"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Use password"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Wrong PIN"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Wrong pattern"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Wrong password"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Too many incorrect attempts.\nTry again in <xliff:g id="NUMBER">%d</xliff:g> seconds."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Touch the fingerprint sensor"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingerprint icon"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Looking for you…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Torch"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Camera in use"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobile data"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Data usage"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Remaining data"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 7a6271f9710b..5bf1b0f6d94f 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Set up input methods"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Physical keyboard"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Allow <xliff:g id="APPLICATION">%1$s</xliff:g> to access <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Allow <xliff:g id="APPLICATION">%1$s</xliff:g> to access <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nThis app has not been granted record permission but could capture audio through this USB device."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Allow <xliff:g id="APPLICATION">%1$s</xliff:g> to access <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Open <xliff:g id="APPLICATION">%1$s</xliff:g> to handle <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Open <xliff:g id="APPLICATION">%1$s</xliff:g> to handle <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nThis app has not been granted record permission but could capture audio through this USB device."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Open <xliff:g id="APPLICATION">%1$s</xliff:g> to handle <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"No installed apps work with this USB accessory. Learn more about this accessory at <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB accessory"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmed"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Tap Confirm to complete"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Authenticated"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Use PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Use pattern"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Use password"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Wrong PIN"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Wrong pattern"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Wrong password"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Too many incorrect attempts.\nTry again in <xliff:g id="NUMBER">%d</xliff:g> seconds."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Touch the fingerprint sensor"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingerprint icon"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Looking for you…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Torch"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Camera in use"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobile data"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Data usage"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Remaining data"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 32469f20ca6b..15617b392497 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‏‎‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‎‎‎‏‎‎‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎Set up input methods‎‏‎‎‏‎"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‏‏‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‎‏‏‏‏‎‎‎‎‎‏‏‏‎‏‎‎‏‎‎Physical keyboard‎‏‎‎‏‎"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‎‏‎‏‎‏‏‎‎‎‏‎‎‎‏‏‏‎‏‎‏‎‎‏‎‏‏‏‎‎‎‎‎‏‏‎‎‏‏‏‎‎‎‎‎‏‎‎‏‏‏‏‏‏‏‏‎Allow ‎‏‎‎‏‏‎<xliff:g id="APPLICATION">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to access ‎‏‎‎‏‏‎<xliff:g id="USB_DEVICE">%2$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‏‎‎‏‎‎‎‎‏‎‏‎‏‎‎‏‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‎‎‏‏‎‏‎‎‎‏‏‏‎‎‏‏‎‎‎‎‏‏‎Allow ‎‏‎‎‏‏‎<xliff:g id="APPLICATION">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to access ‎‏‎‎‏‏‎<xliff:g id="USB_DEVICE">%2$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎This app has not been granted record permission but could capture audio through this USB device.‎‏‎‎‏‎"</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‎‏‏‎‏‏‏‎‏‎‏‎‏‎‎‎‎‎‎‎‎‏‏‏‏‏‏‎‎‏‎‏‎‏‎‏‎‎‎‎‎‎‎‎‏‏‎‏‏‎‏‎‏‏‏‎Allow ‎‏‎‎‏‏‎<xliff:g id="APPLICATION">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to access ‎‏‎‎‏‏‎<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‎‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‏‏‎‎‏‎‎‎‎‎‎‎‏‏‏‎‏‎‎‏‎‎‎‎‎‎‏‎Open ‎‏‎‎‏‏‎<xliff:g id="APPLICATION">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to handle ‎‏‎‎‏‏‎<xliff:g id="USB_DEVICE">%2$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‏‏‎‏‏‎‎‎‏‏‎‏‎‎‎‏‎‎‏‎‏‏‏‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‏‎‏‏‎‎‎‏‎‏‏‏‏‎‎‎‏‎Open ‎‏‎‎‏‏‎<xliff:g id="APPLICATION">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to handle ‎‏‎‎‏‏‎<xliff:g id="USB_DEVICE">%2$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎This app has not been granted record permission but could capture audio through this USB device.‎‏‎‎‏‎"</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‎‎‏‎‎‏‎‎‎‏‎‎‏‎‏‎‎‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‎‏‎Open ‎‏‎‎‏‏‎<xliff:g id="APPLICATION">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to handle ‎‏‎‎‏‏‎<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‏‎‎‏‎‎‏‎‎‏‏‏‏‎‏‎‏‎‏‎‏‏‎‎‎‏‎‎‎‏‏‎‏‏‎‎‏‏‏‏‎‏‏‏‏‏‎No installed apps work with this USB accessory. Learn more about this accessory at ‎‏‎‎‏‏‎<xliff:g id="URL">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‎‏‏‏‎‏‏‎‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‎‎‏‎‎‏‏‎‏‎‎‎‎‏‏‎‎‎‎‏‎‏‏‏‏‎‎‎USB accessory‎‏‎‎‏‎"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‏‏‎‎‏‎‎‏‎‏‏‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‎‏‏‏‏‎‎‏‏‏‎‎‎‎‏‎‏‏‏‎‎‏‏‏‏‎Confirmed‎‏‎‎‏‎"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‏‏‏‏‎‎‎‏‎‏‏‎‎‏‏‏‏‏‎‎‏‏‎‏‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎Tap Confirm to complete‎‏‎‎‏‎"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‎‎‎‏‎‎‎‏‏‎‏‏‎‏‎‎‎‏‏‏‎‎‏‎‎‎‏‎‏‏‎‏‎‎‎‎‏‎‏‏‎‎‎‏‏‏‎‎‏‏‏‎‏‎Authenticated‎‏‎‎‏‎"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‎‎‎‏‏‏‏‏‎‎‎‏‎‎‏‎‏‎‎‏‎‎‎‏‎‎‎‏‎‏‏‏‎‎‎‎‏‎‎‎‎‎‏‏‎‎‎‏‎‎‎‏‏‏‎Use PIN‎‏‎‎‏‎"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‎‎‎‏‏‏‎‏‏‏‏‎‏‎‏‏‎‏‏‏‏‎‏‎‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‎‏‏‏‎‏‏‏‏‏‏‎‎‏‎‎Use pattern‎‏‎‎‏‎"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‏‎‎‎‏‏‎‏‎‎‎‏‏‏‏‎‏‎‎‎‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‎‎‏‎‏‎‏‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎Use password‎‏‎‎‏‎"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‎‏‎‎‏‎‏‎‏‎‎‏‎‎‏‎‏‎‎‏‏‏‎‎‎‎‏‎‎‏‎‏‏‎‏‎‏‎‎‏‏‏‏‎‎Wrong PIN‎‏‎‎‏‎"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‏‏‏‎‏‎‏‏‎‎‎‎‏‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‏‏‏‎‎‎‎‎‎‏‏‎‏‎‏‏‎‏‎‎‎‏‏‎Wrong pattern‎‏‎‎‏‎"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‎‎‎‏‎‏‏‏‎‏‏‎‎‏‎‏‎‏‏‎‏‎‎‎‏‎‎‎‏‏‏‎‎‎‏‏‎‎‎‏‎‎‎‎‎‏‎‎‏‎‏‏‏‎‎Wrong password‎‏‎‎‏‎"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‏‏‎‎‎‏‏‎‎‎‎‎‏‏‏‏‎‎‏‏‏‎‎‎‏‎‎‏‏‏‎‏‎‏‏‎‏‏‎‏‏‏‏‎‏‎‎‎‎‎‎‎‎‎Too many incorrect attempts.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Try again in ‎‏‎‎‏‏‎<xliff:g id="NUMBER">%d</xliff:g>‎‏‎‎‏‏‏‎ seconds.‎‏‎‎‏‎"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎‏‎‎‏‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‏‎‎‎‎‎‏‎Touch the fingerprint sensor‎‏‎‎‏‎"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‎‏‎‏‏‎‏‎‎‎‎‏‏‎‏‎‏‎‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‎‏‎‏‎‎Fingerprint icon‎‏‎‎‏‎"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‏‎‏‎‎‎‎‎‎‏‏‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‎‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‎‏‏‏‎‎‎‎‏‎Looking for you…‎‏‎‎‏‎"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‎‏‏‏‎‏‏‎‎‎‎‏‏‏‏‏‎‎‎‎‎‏‏‎‏‎‎‏‏‏‏‏‎‎‎‎‎‎‏‎‎‏‏‎‏‏‏‏‎‏‏‎‏‏‎Notifications‎‏‎‎‏‎"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‏‎‏‎‎‏‎‎‎‏‎‏‎‎‏‏‏‏‎‎‎‎‎‎‏‎‏‎‏‎‎‏‏‎‏‏‎‏‏‎‎‏‎‎‏‏‏‎‏‎‏‎‎Flashlight‎‏‎‎‏‎"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‎‎‎‎‏‎‏‏‏‎‎‏‏‏‎‏‏‏‎‎‏‎Camera in use‎‏‎‎‏‎"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‎‎‏‏‏‏‎‎‏‎‏‏‎‏‎‎‏‎‏‎‏‏‎‎‏‏‎‏‎‎‏‎‏‏‎‎‏‎‏‏‎‎‎‏‎‏‎‎‏‎‎‎‏‏‎Mobile data‎‏‎‎‏‎"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‎‏‎‎‎‎‏‎‎‏‏‏‎‏‎‎‎‏‏‎‎‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‏‏‎‎‎‏‏‎‎‏‏‎‎‎‎‎‏‎‎Data usage‎‏‎‎‏‎"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‏‏‏‏‎‎‏‏‎‎‏‏‏‎‏‎‏‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‎‎‏‎Remaining data‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index f29aa1e0beae..a69de3ba4e9b 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configurar métodos de intro."</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Teclado físico"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"¿Deseas permitir que <xliff:g id="APPLICATION">%1$s</xliff:g> acceda a <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"¿Quieres permitir que <xliff:g id="APPLICATION">%1$s</xliff:g> acceda a <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nLa app no tiene permiso para grabar, pero podría capturar audio mediante este dispositivo USB."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"¿Deseas permitir que <xliff:g id="APPLICATION">%1$s</xliff:g> acceda a <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"¿Deseas abrir <xliff:g id="APPLICATION">%1$s</xliff:g> para usar <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"¿Quieres abrir <xliff:g id="APPLICATION">%1$s</xliff:g> para administrar <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nLa app no tiene permiso para grabar, pero puede capturar audio mediante este dispositivo USB."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"¿Deseas abrir <xliff:g id="APPLICATION">%1$s</xliff:g> para usar <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Ninguna aplic. funciona con este accesorio USB. Más info. acerca de este en <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Accesorio USB"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmado"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Presiona Confirmar para completarla"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autenticado"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Usar PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Usar patrón"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Usar contraseña"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN incorrecto"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Patrón incorrecto"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Contraseña incorrecta"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Demasiados intentos incorrectos.\nVuelve a intentarlo en <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Toca el sensor de huellas digitales"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ícono de huella digital"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Autenticando tu rostro…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificaciones"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Linterna"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Cámara en uso"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Datos móviles"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Uso de datos"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Datos restantes"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 308809849bbb..f96f7b96d4b1 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -47,8 +47,11 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configurar métodos de entrada"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Teclado físico"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"¿Quieres permitir que <xliff:g id="APPLICATION">%1$s</xliff:g> acceda a <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"¿Quieres que <xliff:g id="APPLICATION">%1$s</xliff:g> pueda acceder a <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nEsta aplicación no tiene permisos para grabar, pero podría captar audio a través de este dispositivo USB."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"¿Quieres permitir que <xliff:g id="APPLICATION">%1$s</xliff:g> acceda a <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"¿Quieres abrir <xliff:g id="APPLICATION">%1$s</xliff:g> para utilizar <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) -->
+ <skip />
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"¿Quieres abrir <xliff:g id="APPLICATION">%1$s</xliff:g> para utilizar <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Ninguna aplicación instalada funciona con este accesorio USB. Más información: <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Accesorio USB"</string>
@@ -126,6 +129,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmada"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Toca Confirmar para completar la acción"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Se ha autenticado"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Usar PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Usar patrón"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Usar contraseña"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN incorrecto"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Patrón incorrecto"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Contraseña incorrecta"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Demasiados intentos fallidos.\n Vuelve a intentarlo en <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Toca el sensor de huellas digitales"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icono de huella digital"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Buscando tu cara…"</string>
@@ -364,6 +374,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificaciones"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Linterna"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Cámara en uso"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Datos móviles"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Uso de datos"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Datos restantes"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 51226abf1e84..b106a0101ec7 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Seadista sisestusmeetodeid"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Füüsiline klaviatuur"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Kas lubada rakendusele <xliff:g id="APPLICATION">%1$s</xliff:g> juurdepääs seadmele <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Kas lubada rakendusel <xliff:g id="APPLICATION">%1$s</xliff:g> seadmele <xliff:g id="USB_DEVICE">%2$s</xliff:g> juurde pääseda?\nSellele rakendusele pole antud salvestamise luba, kuid see saab heli jäädvustada selle USB-seadme kaudu."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Kas lubada rakendusele <xliff:g id="APPLICATION">%1$s</xliff:g> juurdepääs seadmele <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Kas avada rakendus <xliff:g id="APPLICATION">%1$s</xliff:g> seadme <xliff:g id="USB_DEVICE">%2$s</xliff:g> kasutamiseks?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Kas avada <xliff:g id="APPLICATION">%1$s</xliff:g>, et käsitseda seadet <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nSellele rakendusele pole antud salvestamise luba, kuid see saab heli jäädvustada selle USB-seadme kaudu."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Kas avada rakendus <xliff:g id="APPLICATION">%1$s</xliff:g> seadme <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> kasutamiseks?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Inst. rak. ei tööta selle USB-seadmega. Lisateavet lisaseadme kohta vt siit: <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB-lisaseade"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Kinnitatud"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Lõpuleviimiseks puudutage nuppu Kinnita"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autenditud"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Kasuta PIN-koodi"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Kasuta mustrit"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Kasuta parooli"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Vale PIN-kood"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Vale muster"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Vale parool"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Liiga palju valesid katseid.\nProovige <xliff:g id="NUMBER">%d</xliff:g> sekundi pärast uuesti."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Puudutage sõrmejäljeandurit"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Sõrmejälje ikoon"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Otsitakse teid …"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Märguanded"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Taskulamp"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Kasutusel olev kaamera"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobiilne andmeside"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Andmeside kasutus"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Järelejäänud andmemaht"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 0d2863e265fb..4e9fe24509dc 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Konfiguratu idazketa-metodoak"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Teklatu fisikoa"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> atzitzeko baimena eman nahi diozu <xliff:g id="APPLICATION">%1$s</xliff:g> aplikazioari?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> erabiltzeko baimena eman nahi diozu <xliff:g id="APPLICATION">%1$s</xliff:g> aplikazioari?\nAplikazioak ez du grabatzeko baimenik, baina baliteke USB bidezko gailu horren bidez audioa grabatzea."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> atzitzeko baimena eman nahi diozu <xliff:g id="APPLICATION">%1$s</xliff:g> aplikazioari?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="APPLICATION">%1$s</xliff:g> ireki nahi duzu <xliff:g id="USB_DEVICE">%2$s</xliff:g> kudeatzeko?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="APPLICATION">%1$s</xliff:g> ireki nahi duzu <xliff:g id="USB_DEVICE">%2$s</xliff:g> erabiltzeko?\nAplikazioak ez du grabatzeko baimenik, baina baliteke audioa grabatzea USB bidezko gailu horren bidez."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="APPLICATION">%1$s</xliff:g> ireki nahi duzu <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> kudeatzeko?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Instalatutako aplikazioek ez dute USB osagarri honekin funtzionatzen. Lortu informazio gehiago osagarriari buruz hemen: <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB osagarria"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Berretsita"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Amaitzeko, sakatu \"Berretsi\""</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autentifikatuta"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Erabili PIN kodea"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Erabili eredua"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Erabili pasahitza"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN kodea ez da zuzena"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Eredua ez da zuzena"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Pasahitza ez da zuzena"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Saiakera oker gehiegi egin dituzu.\nSaiatu berriro <xliff:g id="NUMBER">%d</xliff:g> segundo barru."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Sakatu hatz-marken sentsorea"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Hatz-markaren ikonoa"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Zure bila…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Jakinarazpenak"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Linterna"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Kamera abian da"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Datu-konexioa"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Datuen erabilera"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Geratzen diren datuak"</string>
@@ -897,7 +907,7 @@
<string name="mobile_data_disable_message" msgid="4756541658791493506">"<xliff:g id="CARRIER">%s</xliff:g> erabilita ezingo dituzu erabili datuak edo Internet. Wifi-sare baten bidez soilik konektatu ahal izango zara Internetera."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6078110473451946831">"Uneko operadorea"</string>
<string name="touch_filtered_warning" msgid="8671693809204767551">"Aplikazio bat baimen-eskaera oztopatzen ari denez, ezarpenek ezin dute egiaztatu erantzuna."</string>
- <string name="slice_permission_title" msgid="7465009437851044444">"<xliff:g id="APP_0">%1$s</xliff:g> aplikazioari <xliff:g id="APP_2">%2$s</xliff:g> aplikazioaren zatiak erakusteko baimena eman nahi diozu?"</string>
+ <string name="slice_permission_title" msgid="7465009437851044444">"<xliff:g id="APP_2">%2$s</xliff:g> aplikazioaren zatiak erakusteko baimena eman nahi diozu <xliff:g id="APP_0">%1$s</xliff:g> aplikazioari?"</string>
<string name="slice_permission_text_1" msgid="3514586565609596523">"- <xliff:g id="APP">%1$s</xliff:g> aplikazioaren informazioa irakur dezake."</string>
<string name="slice_permission_text_2" msgid="3146758297471143723">"- <xliff:g id="APP">%1$s</xliff:g> aplikazioan ekintzak gauza ditzake."</string>
<string name="slice_permission_checkbox" msgid="7986504458640562900">"Baimendu <xliff:g id="APP">%1$s</xliff:g> aplikazioari edozein aplikazioren zatiak erakustea"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index e649d809db98..1751cc9f7ba5 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"تنظیم روش‌های ورودی"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"صفحه‌کلید فیزیکی"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"به <xliff:g id="APPLICATION">%1$s</xliff:g> برای دسترسی به <xliff:g id="USB_DEVICE">%2$s</xliff:g> اجازه داده شود؟"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"‏به <xliff:g id="APPLICATION">%1$s</xliff:g> اجازه می‌دهید به <xliff:g id="USB_DEVICE">%2$s</xliff:g>دسترسی داشته باشد؟\nمجوز ضبط به این برنامه داده نشده است اما می‌تواند صدا را ازطریق این دستگاه USB ضبط کند."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"به <xliff:g id="APPLICATION">%1$s</xliff:g> برای دسترسی به <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> اجازه داده شود؟"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"برای استفاده از <xliff:g id="USB_DEVICE">%2$s</xliff:g>، <xliff:g id="APPLICATION">%1$s</xliff:g> باز شود؟"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"‏<xliff:g id="APPLICATION">%1$s</xliff:g> برای رسیدگی به <xliff:g id="USB_DEVICE">%2$s</xliff:g> باز شود؟\nمجوز ضبط به این برنامه داده نشده است اما می‌تواند صدا را ازطریق این دستگاه USB ضبط کند."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"برای استفاده از <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>، <xliff:g id="APPLICATION">%1$s</xliff:g> باز شود؟"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"‏هیچ برنامه نصب شده‌ای با این وسیله جانبی USB کار نمی‌کند. در <xliff:g id="URL">%1$s</xliff:g> دربارهٔ این وسیله جانبی اطلاعات بیشتری کسب کنید"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"‏لوازم جانبی USB"</string>
@@ -108,7 +110,7 @@
<string name="accessibility_phone_button" msgid="6738112589538563574">"تلفن"</string>
<string name="accessibility_voice_assist_button" msgid="487611083884852965">"دستیار صوتی"</string>
<string name="accessibility_unlock_button" msgid="128158454631118828">"باز کردن قفل"</string>
- <string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"در انتظار اثرانگشت"</string>
+ <string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"در انتظار اثر انگشت"</string>
<string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"باز کردن قفل بدون استفاده از اثر انگشت"</string>
<string name="accessibility_scanning_face" msgid="769545173211758586">"درحال اسکن کردن چهره"</string>
<string name="accessibility_send_smart_reply" msgid="7766727839703044493">"ارسال"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"تأیید شد"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"برای تکمیل، روی تأیید ضربه بزنید"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"راستی‌آزمایی‌شده"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"استفاده از پین"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"استفاده از الگو"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"استفاده از گذرواژه"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"پین اشتباه است"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"الگو اشتباه است"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"گذرواژه اشتباه است"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"تلاش‌های نادرست بسیاری انجام شده است.\nپس از <xliff:g id="NUMBER">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"حسگر اثر انگشت را لمس کنید"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"نماد اثر انگشت"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"درحال جستجوی شما…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"اعلان‌ها"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"چراغ قوه"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"دوربین درحال استفاده"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"داده تلفن همراه"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"مصرف داده"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"داده‌های باقی‌مانده"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index e19575428211..c6c90b38b5c6 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Määritä syöttötavat"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fyysinen näppäimistö"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Saako <xliff:g id="APPLICATION">%1$s</xliff:g> käyttöoikeuden (<xliff:g id="USB_DEVICE">%2$s</xliff:g>)?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Saako <xliff:g id="APPLICATION">%1$s</xliff:g> tämän pääsyoikeuden: <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nSovellus ei ole saanut tallennuslupaa, mutta voi tallentaa ääntä tämän USB-laitteen avulla."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Saako <xliff:g id="APPLICATION">%1$s</xliff:g> käyttöoikeuden (<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>)?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Avataanko <xliff:g id="APPLICATION">%1$s</xliff:g>, jotta <xliff:g id="USB_DEVICE">%2$s</xliff:g> voidaan ottaa käyttöön?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Avataanko <xliff:g id="APPLICATION">%1$s</xliff:g>, jotta <xliff:g id="USB_DEVICE">%2$s</xliff:g> voidaan ottaa käyttöön?\nSovellus ei ole saanut tallennuslupaa, mutta voi tallentaa ääntä tämän USB-laitteen avulla."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Avataanko <xliff:g id="APPLICATION">%1$s</xliff:g>, jotta <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> voidaan ottaa käyttöön?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Asennetut sov. eivät toimi tämän USB-laitteen kanssa. Lisätietoja laitteesta: <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB-lisälaite"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Vahvistettu"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Valitse lopuksi Vahvista"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Todennettu"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Käytä PIN-koodia"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Käytä kuviota"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Käytä salasanaa"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Väärä PIN-koodi"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Väärä kuvio"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Väärä salasana"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Liian monta virheellistä yritystä.\nYritä uudelleen <xliff:g id="NUMBER">%d</xliff:g> sekunnin kuluttua."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Kosketa sormenjälkitunnistinta"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Sormenjälkikuvake"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Etsitään kasvoja…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Ilmoitukset"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Taskulamppu"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Kamera käytössä"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobiilidata"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Datakäyttö"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Käytettävissä"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 4f28efa325a7..01427c957381 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configurer les modes de saisie"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Clavier physique"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Autoriser <xliff:g id="APPLICATION">%1$s</xliff:g> à accéder à <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Autorisé <xliff:g id="APPLICATION">%1$s</xliff:g> à accéder à <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nCette application n\'a pas été autorisée à effectuer des enregistrements, mais elle pourrait enregistrer du contenu audio par l\'intermédiaire de cet appareil USB."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Autoriser <xliff:g id="APPLICATION">%1$s</xliff:g> à accéder à <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Ouvrir <xliff:g id="APPLICATION">%1$s</xliff:g> pour utiliser <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Ouvrir <xliff:g id="APPLICATION">%1$s</xliff:g> pour gérer <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nCette application n\'a pas été autorisée à effectuer des enregistrements, mais elle pourrait enregistrer du contenu audio par l\'intermédiaire de cet appareil USB."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Ouvrir <xliff:g id="APPLICATION">%1$s</xliff:g> pour utiliser <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Aucune application installée compatible avec accessoire USB. En savoir plus sur <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Accessoire USB"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmé"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Touchez Confirmer pour terminer"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Authentifié"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Utiliser un NIP"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Utiliser un schéma"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Utiliser un mot de passe"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"NIP incorrect"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Schéma incorrect"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Mot de passe incorrect"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Trop de tentatives incorrectes. \nRéessayez dans <xliff:g id="NUMBER">%d</xliff:g> secondes."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Touchez le capteur d\'empreintes digitales"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icône d\'empreinte digitale"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Recherche de votre visage…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Lampe de poche"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"L\'appareil photo est en cours d\'utilisation"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Données cellulaires"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Utilisation de données"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Données restantes"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index a51ebb1087ea..852bd119151d 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configurer les modes de saisie"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Clavier physique"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Autoriser <xliff:g id="APPLICATION">%1$s</xliff:g> à accéder à <xliff:g id="USB_DEVICE">%2$s</xliff:g> ?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Autoriser <xliff:g id="APPLICATION">%1$s</xliff:g> à accéder à <xliff:g id="USB_DEVICE">%2$s</xliff:g> ?\nCette application n\'a pas été autorisée à effectuer des enregistrements, mais elle pourrait enregistrer du contenu audio via ce périphérique USB."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Autoriser <xliff:g id="APPLICATION">%1$s</xliff:g> à accéder à <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Ouvrir <xliff:g id="APPLICATION">%1$s</xliff:g> pour utiliser <xliff:g id="USB_DEVICE">%2$s</xliff:g> ?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Ouvrir <xliff:g id="APPLICATION">%1$s</xliff:g> pour gérer <xliff:g id="USB_DEVICE">%2$s</xliff:g> ?\nCette application n\'a pas reçu l\'autorisation d\'enregistrer des contenus audio, mais peut le faire via ce périphérique USB."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Ouvrir <xliff:g id="APPLICATION">%1$s</xliff:g> pour utiliser <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Aucune application installée compatible avec accessoire USB. En savoir plus sur <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Accessoire USB"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmé"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Appuyez sur \"Confirmer\" pour terminer"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Authentifié"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Utiliser un code PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Utiliser un schéma"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Utiliser un mot de passe"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Code incorrect"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Schéma incorrect"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Mot de passe incorrect"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Trop de tentatives incorrectes.\nVeuillez réessayer dans <xliff:g id="NUMBER">%d</xliff:g> secondes."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Appuyez sur le lecteur d\'empreinte digitale"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icône d\'empreinte digitale"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Recherche de votre visage…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Lampe de poche"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Caméra en cours d\'utilisation"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Données mobiles"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Conso des données"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Données restantes"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 7a7ebf2d57c8..885302e6e30b 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configurar métodos de entrada"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Teclado físico"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Queres permitir que <xliff:g id="APPLICATION">%1$s</xliff:g> acceda a <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Queres permitir que a aplicación <xliff:g id="APPLICATION">%1$s</xliff:g> acceda ao dispositivo (<xliff:g id="USB_DEVICE">%2$s</xliff:g>)?\nEsta aplicación non está autorizada para realizar gravacións, pero pode capturar audio a través deste dispositivo USB."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Queres permitir que <xliff:g id="APPLICATION">%1$s</xliff:g> acceda a <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Queres abrir <xliff:g id="APPLICATION">%1$s</xliff:g> para utilizar <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Queres abrir a aplicación <xliff:g id="APPLICATION">%1$s</xliff:g> para xestionar o dispositivo (<xliff:g id="USB_DEVICE">%2$s</xliff:g>)?\nEsta aplicación non está autorizada a realizar gravacións, pero pode capturar audio a través deste dispositivo USB."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Queres abrir <xliff:g id="APPLICATION">%1$s</xliff:g> para utilizar <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Ningunha aplicación instalada funciona co accesorio USB. Máis información: <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Accesorio USB"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmada"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Toca Confirmar para completar o proceso"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autenticado"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Usar PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Usar padrón"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Usar contrasinal"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"O PIN é incorrecto"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"O padrón é incorrecto"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"O contrasinal é incorrecto"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Realizáronse demasiados intentos incorrectos.\nTéntao de novo en <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Toca o sensor de impresión dixital"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icona de impresión dixital"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Buscándote…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificacións"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Lanterna"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Cámara en uso"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Datos móbiles"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Uso de datos"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Datos restantes"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 10aba2491be7..b3ec665762da 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ઇનપુટ પદ્ધતિઓ સેટ કરો"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"ભૌતિક કીબોર્ડ"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="USB_DEVICE">%2$s</xliff:g>ના ઍક્સેસ માટે <xliff:g id="APPLICATION">%1$s</xliff:g>ને મંજૂરી આપીએ?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="APPLICATION">%1$s</xliff:g>ને <xliff:g id="USB_DEVICE">%2$s</xliff:g> ઍક્સેસ કરવાની મંજૂરી આપીએ?\nઆ ઍપને રેકૉર્ડ કરવાની પરવાનગી આપવામાં આવી નથી પરંતુ તે આ USB ડિવાઇસ મારફત ઑડિયો કૅપ્ચર કરી શકે છે."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>ના ઍક્સેસ માટે <xliff:g id="APPLICATION">%1$s</xliff:g>ને મંજૂરી આપીએ?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g>ને હૅન્ડલ કરવા માટે <xliff:g id="APPLICATION">%1$s</xliff:g>ને ખોલીએ?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="USB_DEVICE">%2$s</xliff:g>ને હેન્ડલ કરવા માટે <xliff:g id="APPLICATION">%1$s</xliff:g> ખોલીએ?\nઆ ઍપને રેકૉર્ડ કરવાની પરવાનગી આપવામાં આવી નથી પરંતુ તે આ USB ડિવાઇસ મારફત ઑડિયો કૅપ્ચર કરી શકે છે."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>ને હૅન્ડલ કરવા માટે <xliff:g id="APPLICATION">%1$s</xliff:g>ને ખોલીએ?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"કોઈપણ ઇન્સ્ટોલ કરેલી ઍપ્લિકેશનો આ USB ઍક્સેસરી સાથે કામ કરતી નથી. આ ઍક્સેસરી વિશે <xliff:g id="URL">%1$s</xliff:g> પર વધુ જાણો."</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB ઍક્સેસરી"</string>
@@ -56,7 +58,7 @@
<string name="always_use_device" msgid="4015357883336738417">"જ્યારે <xliff:g id="USB_DEVICE">%2$s</xliff:g> કનેક્ટેડ હોય ત્યારે <xliff:g id="APPLICATION">%1$s</xliff:g>ને હંમેશા ખોલો"</string>
<string name="always_use_accessory" msgid="3257892669444535154">"જ્યારે <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> કનેક્ટેડ હોય ત્યારે <xliff:g id="APPLICATION">%1$s</xliff:g>ને હંમેશા ખોલો"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"USB ડિબગિંગને મંજૂરી આપીએ?"</string>
- <string name="usb_debugging_message" msgid="2220143855912376496">"કમ્પ્યુટરની RSA મુખ્ય ફિંગરપ્રિંટ આ છે:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
+ <string name="usb_debugging_message" msgid="2220143855912376496">"કમ્પ્યુટરની RSA મુખ્ય ફિંગરપ્રિન્ટ આ છે:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"હંમેશા આ કમ્પ્યુટરથી મંજૂરી આપો"</string>
<string name="usb_debugging_allow" msgid="2272145052073254852">"મંજૂરી આપો"</string>
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB ડીબગિંગની મંજૂરી નથી"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"પુષ્ટિ કરી"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"પરીક્ષણ પૂર્ણ કરવા કન્ફર્મ કરોને ટૅપ કરો"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"પ્રમાણિત"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"પિનનો ઉપયોગ કરો"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"પૅટર્નનો ઉપયોગ કરો"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"પાસવર્ડનો ઉપયોગ કરો"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"ખોટો પિન"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"ખોટી પૅટર્ન"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"ખોટો પાસવર્ડ"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"ઘણા વધારે ખોટા પ્રયત્નો. \n <xliff:g id="NUMBER">%d</xliff:g> સેકંડમાં ફરી પ્રયાસ કરો."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ફિંગરપ્રિન્ટના સેન્સરને સ્પર્શ કરો"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ફિંગરપ્રિન્ટનું આઇકન"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"તમારા માટે શોધી રહ્યાં છે..."</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"નોટિફિકેશનો"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ફ્લેશલાઇટ"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"કૅમેરાનો ઉપયોગ થાય છે"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"મોબાઇલ ડેટા"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"ડેટા વપરાશ"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"બાકી ડેટા"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 9ce8e7d016ef..56117ed0751e 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -47,8 +47,11 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"इनपुट का तरीका सेट करें"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"सामान्य कीबोर्ड"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="APPLICATION">%1$s</xliff:g> को <xliff:g id="USB_DEVICE">%2$s</xliff:g> के ऐक्सेस की अनुमति दें?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"आप <xliff:g id="APPLICATION">%1$s</xliff:g> को <xliff:g id="USB_DEVICE">%2$s</xliff:g> ऐक्सेस करने की अनुमति देना चाहते हैं?\nइस ऐप्लिकेशन को रिकॉर्ड करने की अनुमति नहीं दी गई है. हालांकि, ऐप्लिकेशन इस यूएसबी डिवाइस से ऑडियो कैप्चर कर सकता है."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g> को <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> के ऐक्सेस की अनुमति दें?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> के लिए <xliff:g id="APPLICATION">%1$s</xliff:g> खोलें?"</string>
+ <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) -->
+ <skip />
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> के लिए <xliff:g id="APPLICATION">%1$s</xliff:g> खोलें?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"इस USB सहायक डिवाइस के साथ कोई भी इंस्टॉल ऐप्स काम नहीं करता. इस सहायक डिवाइस के बारे में यहां ज़्यादा जानें: <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB सहायक साधन"</string>
@@ -126,6 +129,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"पुष्टि हो गई"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"\'पुष्टि करें\' पर टैप करके पूरा करें"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"पुष्टि हो गई"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"पिन इस्तेमाल करें"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"पैटर्न इस्तेमाल करें"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"पासवर्ड इस्तेमाल करें"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"गलत पिन"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"गलत पैटर्न"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"गलत पासवर्ड"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"आपकी कोशिशें बहुत बार गलत हुई हैं.\nआप <xliff:g id="NUMBER">%d</xliff:g> सेकंड में फिर से कोशिश कर सकते हैं."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"फ़िंगरप्रिंट सेंसर को छुएं"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"फ़िंगरप्रिंट आइकॉन"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"आपको पहचान रहा है…"</string>
@@ -364,6 +374,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"सूचनाएं"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"फ़्लैशलाइट"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"कैमरा इस्तेमाल में है"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"मोबाइल डेटा"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"डेटा खर्च"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"शेष डेटा"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 9c9b0b035f0d..96b1623b2ba3 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Postavljanje načina unosa"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fizička tipkovnica"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Želite li dopustiti aplikaciji <xliff:g id="APPLICATION">%1$s</xliff:g> pristup uređaju <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Želite li dopustiti aplikaciji <xliff:g id="APPLICATION">%1$s</xliff:g> da pristupa uređaju <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nTa aplikacija nema dopuštenje za snimanje, no mogla bi primati zvuk putem tog USB uređaja."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Želite li dopustiti aplikaciji <xliff:g id="APPLICATION">%1$s</xliff:g> pristup uređaju <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Želite li otvoriti aplikaciju <xliff:g id="APPLICATION">%1$s</xliff:g> radi upravljanja uređajem <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Želite li upravljati uređajem <xliff:g id="USB_DEVICE">%2$s</xliff:g> putem aplikacije <xliff:g id="APPLICATION">%1$s</xliff:g>?\nTa aplikacija nema dopuštenje za snimanje, no mogla bi primati zvuk putem tog USB uređaja."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Želite li otvoriti aplikaciju <xliff:g id="APPLICATION">%1$s</xliff:g> radi upravljanja uređajem <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Nijedna instalirana aplikacija ne radi s ovim USB dodatkom. Saznajte više na <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB pribor"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Potvrđeno"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Dodirnite Potvrdi za dovršetak"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autentičnost provjerena"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Koristite PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Koristite uzorak"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Koristite zaporku"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Pogrešan PIN"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Pogrešan uzorak"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Pogrešna zaporka"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Previše netočnih pokušaja.\nPokušajte ponovo za <xliff:g id="NUMBER">%d</xliff:g> s."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Dodirnite senzor otiska prsta"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona otiska prsta"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Tražimo vas…"</string>
@@ -366,6 +375,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Obavijesti"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Svjetiljka"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Upotrebljava se kamera"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobilni podaci"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Potrošnja podataka"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Preostali podaci"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 99a049ee190b..bd23d4ddc0e5 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Beviteli módok beállítása"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fizikai billentyűzet"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Engedélyezi a(z) <xliff:g id="APPLICATION">%1$s</xliff:g> számára, hogy hozzáférjen a következőhöz: <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Lehetővé teszi a(z) <xliff:g id="APPLICATION">%1$s</xliff:g> alkalmazásnak, hogy hozzáférjen a következőhöz: <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nEz az alkalmazás nem rendelkezik rögzítési engedéllyel, de ezzel az USB-eszközzel képes a hangfelvételre."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Engedélyezi a(z) <xliff:g id="APPLICATION">%1$s</xliff:g> számára, hogy hozzáférjen a következőhöz: <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Megnyitja a(z) <xliff:g id="APPLICATION">%1$s</xliff:g> alkalmazást a(z) <xliff:g id="USB_DEVICE">%2$s</xliff:g> kezeléséhez?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Megnyitja a(z) <xliff:g id="APPLICATION">%1$s</xliff:g> alkalmazást, hogy kezelje a következőt: <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nEz az alkalmazás nem rendelkezik rögzítési engedéllyel, de ezzel az USB-eszközzel képes a hangfelvételre."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Megnyitja a(z) <xliff:g id="APPLICATION">%1$s</xliff:g> alkalmazást a(z) <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> kezeléséhez?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"A telepített alkalmazások nem működnek ezzel az USB-kiegészítővel. Bővebben: <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB-kellék"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Megerősítve"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Koppintson a Megerősítés lehetőségre"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Hitelesítve"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"PIN-kód használata"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Minta használata"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Jelszó használata"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Helytelen PIN-kód"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Helytelen minta"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Helytelen jelszó"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Túl sok helytelen próbálkozás.\nPróbálja újra <xliff:g id="NUMBER">%d</xliff:g> másodperc múlva."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Érintse meg az ujjlenyomat-érzékelőt"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ujjlenyomat ikonja"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Keresem az Ön arcát…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Értesítések"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Zseblámpa"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"A kamera használatban van"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobiladatok"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Adathasználat"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Fennmaradó adatmennyiség"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 9ac0afb7cbb2..2647f2f3748e 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Կարգավորել մուտքագրման եղանակները"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Ֆիզիկական ստեղնաշար"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Թույլատրե՞լ <xliff:g id="APPLICATION">%1$s</xliff:g> հավելվածին օգտագործել <xliff:g id="USB_DEVICE">%2$s</xliff:g> լրասարքը։"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Թույլատրե՞լ <xliff:g id="APPLICATION">%1$s</xliff:g> հավելվածին օգտագործել <xliff:g id="USB_DEVICE">%2$s</xliff:g>ը։\nՀավելվածը ձայնագրելու թույլտվություն չունի, սակայն կկարողանա գրանցել ձայնն այս USB սարքի միջոցով։"</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Թույլատրե՞լ <xliff:g id="APPLICATION">%1$s</xliff:g> հավելվածին օգտագործել <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> լրասարքը։"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Բացե՞լ <xliff:g id="APPLICATION">%1$s</xliff:g> հավելվածը <xliff:g id="USB_DEVICE">%2$s</xliff:g> լրասարքը մշակելու համար։"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Բացե՞լ <xliff:g id="APPLICATION">%1$s</xliff:g> հավելվածը՝ <xliff:g id="USB_DEVICE">%2$s</xliff:g>ն օգտագործելու համար:\nՀավելվածը ձայնագրելու թույլտվություն չունի, սակայն կկարողանա գրանցել ձայնն այս USB սարքի միջոցով։"</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Բացե՞լ <xliff:g id="APPLICATION">%1$s</xliff:g> հավելվածը <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> լրասարքը մշակելու համար։"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Այս USB լրասարքի հետ ոչ մի հավելված չի աշխատում: Իմացեք ավելին այս լրասարքի մասին <xliff:g id="URL">%1$s</xliff:g>-ում"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB լրասարք"</string>
@@ -56,7 +58,7 @@
<string name="always_use_device" msgid="4015357883336738417">"Միշտ բացել <xliff:g id="APPLICATION">%1$s</xliff:g> հավելվածը, երբ <xliff:g id="USB_DEVICE">%2$s</xliff:g> լրասարքը միացված է"</string>
<string name="always_use_accessory" msgid="3257892669444535154">"Միշտ բացել <xliff:g id="APPLICATION">%1$s</xliff:g> հավելվածը, երբ <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> լրասարքը միացված է"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"Թույլատրե՞լ USB-ով վրիպազերծումը"</string>
- <string name="usb_debugging_message" msgid="2220143855912376496">"Համակարգչի RSA-ի բանալի մատնահետքն է`\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
+ <string name="usb_debugging_message" msgid="2220143855912376496">"RSA բանալու թվային մատնահետք`\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Միշտ թույլատրել այս համակարգչից"</string>
<string name="usb_debugging_allow" msgid="2272145052073254852">"Թույլատրել"</string>
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB-ով վրիպազերծումը թույլատրված չէ"</string>
@@ -109,7 +111,7 @@
<string name="accessibility_voice_assist_button" msgid="487611083884852965">"Ձայնային հուշումներ"</string>
<string name="accessibility_unlock_button" msgid="128158454631118828">"Ապակողպել"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"Մատնահետքի սպասում"</string>
- <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"Ապակողպել չօգտագործելով մատնահետքը"</string>
+ <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"Ապակողպել առանց մատնահետքի"</string>
<string name="accessibility_scanning_face" msgid="769545173211758586">"Դեմքի սկանավորում"</string>
<string name="accessibility_send_smart_reply" msgid="7766727839703044493">"Ուղարկել"</string>
<string name="accessibility_manage_notification" msgid="2026361503393549753">"Կառավարել ծանուցումները"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Հաստատվեց"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Ավարտելու համար հպեք «Հաստատել»"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Նույնականացված է"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Օգտագործել PIN կոդ"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Օգտագործել նախշ"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Օգտագործել գաղտնաբառ"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN կոդը սխալ է"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Նախշը սխալ է"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Գաղտնաբառը սխալ է"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Չափից շատ սխալ փորձ է կատարվել:\nՆորից փորձեք <xliff:g id="NUMBER">%d</xliff:g> վայրկյանից:"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Հպեք մատնահետքի սկաներին"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Մատնահետքի պատկերակ"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Դեմքի ճանաչում…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Ծանուցումներ"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Լապտեր"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Օգտագործվում է տեսախցիկը"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Բջջային ինտերնետ"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Տվյալների օգտագործումը"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Մնացած տվյալները"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 443e3b380094..d0b1045fd721 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Menyiapkan metode masukan"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Keyboard fisik"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Izinkan <xliff:g id="APPLICATION">%1$s</xliff:g> mengakses <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Izinkan <xliff:g id="APPLICATION">%1$s</xliff:g> mengakses <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nAplikasi ini belum diberi izin merekam, tetapi dapat merekam audio melalui perangkat USB ini."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Izinkan <xliff:g id="APPLICATION">%1$s</xliff:g> mengakses <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Buka <xliff:g id="APPLICATION">%1$s</xliff:g> untuk menangani <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Buka <xliff:g id="APPLICATION">%1$s</xliff:g> untuk menangani <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nAplikasi ini belum diberi izin merekam, tetapi dapat merekam audio melalui perangkat USB ini."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Buka <xliff:g id="APPLICATION">%1$s</xliff:g> untuk menangani <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Tidak ada apl terpasang yang bekerja dengan aksesori USB ini. Pelajari lebih lanjut tentang aksesori ini di <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Aksesori USB"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Dikonfirmasi"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Ketuk Konfirmasi untuk menyelesaikan"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Diautentikasi"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Gunakan PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Gunakan pola"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Gunakan sandi"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN salah"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Pola salah"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Sandi salah"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Terlalu banyak kesalahan pola.\nCoba lagi dalam <xliff:g id="NUMBER">%d</xliff:g> detik."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Sentuh sensor sidik jari"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikon sidik jari"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Mencari wajah Anda…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifikasi"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Lampu senter"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Kamera sedang digunakan"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Data seluler"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Penggunaan kuota"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Data tersisa"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index f481e48638b4..8985d2752d5a 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Setja upp innsláttaraðferðir"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Vélbúnaðarlyklaborð"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Viltu veita <xliff:g id="APPLICATION">%1$s</xliff:g> aðgang að <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Viltu veita <xliff:g id="APPLICATION">%1$s</xliff:g> aðgang að <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nÞetta forrit hefur ekki fengið heimild fyrir upptöku en gæti tekið upp hljóð í gegnum þetta USB-tæki."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Viltu veita <xliff:g id="APPLICATION">%1$s</xliff:g> aðgang að <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Viltu opna <xliff:g id="APPLICATION">%1$s</xliff:g> til að sjá um <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Viltu opna <xliff:g id="APPLICATION">%1$s</xliff:g> til að vinna með <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nÞetta forrit hefur ekki fengið heimild fyrir upptöku en gæti tekið upp hljóð í gegnum þetta USB-tæki."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Viltu opna <xliff:g id="APPLICATION">%1$s</xliff:g> til að sjá um <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Engin uppsett forrit virka með þessum USB-aukabúnaði. Frekari upplýsingar eru á <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB-aukabúnaður"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Staðfest"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Ýttu á „Staðfesta“ til að ljúka"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Auðkennt"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Nota PIN-númer"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Nota mynstur"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Nota aðgangsorð"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Rangt PIN-númer"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Rangt mynstur"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Rangt aðgangsorð"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Of margar misheppnaðar tilraunir.\nReyndu aftur eftir <xliff:g id="NUMBER">%d</xliff:g> sekúndur."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Snertu fingrafaralesarann"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Fingrafaratákn"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Leitar að þér ..."</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Tilkynningar"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Vasaljós"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Myndavél í notkun"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Farsímagögn"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Gagnanotkun"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Gögn eftir"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 718608ab3964..68169ab67dd5 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configura metodi di immissione"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Tastiera fisica"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Consentire a <xliff:g id="APPLICATION">%1$s</xliff:g> di accedere a <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Vuoi consentire all\'app <xliff:g id="APPLICATION">%1$s</xliff:g> di accedere a <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nA questa app non è stata concessa l\'autorizzazione di registrazione, ma l\'app potrebbe acquisire l\'audio tramite questo dispositivo USB."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Consentire a <xliff:g id="APPLICATION">%1$s</xliff:g> di accedere a <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Vuoi aprire <xliff:g id="APPLICATION">%1$s</xliff:g> per gestire <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Vuoi aprire <xliff:g id="APPLICATION">%1$s</xliff:g> per gestire <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nA questa app non è stata concessa l\'autorizzazione di registrazione, ma l\'app potrebbe acquisire l\'audio tramite questo dispositivo USB."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Vuoi aprire <xliff:g id="APPLICATION">%1$s</xliff:g> per gestire <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Nessuna app installata funziona con questo accessorio USB. Altre info su <xliff:g id="URL">%1$s</xliff:g>."</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Accessorio USB"</string>
@@ -108,8 +110,8 @@
<string name="accessibility_phone_button" msgid="6738112589538563574">"Telefono"</string>
<string name="accessibility_voice_assist_button" msgid="487611083884852965">"Voice Assist"</string>
<string name="accessibility_unlock_button" msgid="128158454631118828">"Sblocca"</string>
- <string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"In attesa dell\'impronta digitale"</string>
- <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"Sblocca senza utilizzare l\'impronta digitale"</string>
+ <string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"In attesa dell\'impronta"</string>
+ <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"Sblocca senza utilizzare l\'impronta"</string>
<string name="accessibility_scanning_face" msgid="769545173211758586">"Scansione del viso"</string>
<string name="accessibility_send_smart_reply" msgid="7766727839703044493">"Invia"</string>
<string name="accessibility_manage_notification" msgid="2026361503393549753">"Gestisci notifiche"</string>
@@ -126,8 +128,15 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confermato"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Tocca Conferma per completare"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autenticazione eseguita"</string>
- <string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Tocca il sensore di impronte digitali"</string>
- <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icona dell\'impronta digitale"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Utilizza PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Usa sequenza"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Utilizza password"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN errato"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Sequenza errata"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Password errata"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Troppi tentativi errati.\nRiprova tra <xliff:g id="NUMBER">%d</xliff:g> secondi."</string>
+ <string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Tocca il sensore di impronte"</string>
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icona dell\'impronta"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"In attesa del volto…"</string>
<string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Icona volto"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Pulsante zoom compatibilità."</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifiche"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Torcia"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Fotocamera in uso"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Dati mobili"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Utilizzo dati"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Dati rimanenti"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 606a0978fd99..d0594899e9cc 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -47,8 +47,11 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"הגדר שיטות קלט"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"מקלדת פיזית"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"האם לתת לאפליקציה <xliff:g id="APPLICATION">%1$s</xliff:g> גישה אל <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"‏האם לאפשר לאפליקציה <xliff:g id="APPLICATION">%1$s</xliff:g> גישה אל <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nאפליקציה זו לא קיבלה הרשאה להקליט אך יכולה לתעד אודיו באמצעות מכשיר USB זה."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"האם לתת לאפליקציה <xliff:g id="APPLICATION">%1$s</xliff:g> גישה אל <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"האם לפתוח את <xliff:g id="APPLICATION">%1$s</xliff:g> כדי לעבוד עם <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) -->
+ <skip />
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"האם לפתוח את <xliff:g id="APPLICATION">%1$s</xliff:g> כדי לעבוד עם <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"‏אין אפליקציות מותקנות הפועלות עם אביזר ה-USB. למידע נוסף על אביזר זה היכנס לכתובת <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"‏אביזר USB"</string>
@@ -126,6 +129,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"מאושר"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"יש להקיש על \'אישור\' לסיום"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"מאומת"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"שימוש בקוד אימות"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"שימוש בקו ביטול נעילה"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"שימוש בסיסמה"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"קוד אימות שגוי"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"קו ביטול נעילה שגוי"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"סיסמה שגויה"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"יותר מדי ניסיונות שגויים.\nיש לנסות שוב בעוד <xliff:g id="NUMBER">%d</xliff:g> שניות."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"יש לגעת בחיישן טביעות האצבע"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"סמל טביעת אצבע"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"מחפש אותך…"</string>
@@ -368,6 +378,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"התראות"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"פנס"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"מצלמה בשימוש"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"חבילת גלישה"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"שימוש בנתונים"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"מכסת נתונים נותרת"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 0bc500c2df71..e89ae5bf6bcd 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"入力方法をセットアップ"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"物理キーボード"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="APPLICATION">%1$s</xliff:g> に <xliff:g id="USB_DEVICE">%2$s</xliff:g> へのアクセスを許可しますか?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="APPLICATION">%1$s</xliff:g> に <xliff:g id="USB_DEVICE">%2$s</xliff:g>へのアクセスを許可しますか?\nこのアプリに録音権限は付与されていませんが、アクセスを許可すると、この USB デバイスから音声を収集できるようになります。"</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g> に <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> へのアクセスを許可しますか?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="APPLICATION">%1$s</xliff:g> を起動して <xliff:g id="USB_DEVICE">%2$s</xliff:g> を処理しますか?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="APPLICATION">%1$s</xliff:g> を開いて <xliff:g id="USB_DEVICE">%2$s</xliff:g>を利用しますか?\nこのアプリに録音権限は付与されていませんが、この USB デバイスから音声を収集できるようになります。"</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="APPLICATION">%1$s</xliff:g> を起動して <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> を処理しますか?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"このUSBアクセサリを扱うアプリはインストールされていません。詳細: <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USBアクセサリ"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"確認しました"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"完了するには [確認] をタップしてください"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"認証済み"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"PIN を使用"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"パターンを使用"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"パスワードを使用"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN が正しくありません"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"パターンが正しくありません"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"パスワードが正しくありません"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"間違えた回数が上限を超えました。\n<xliff:g id="NUMBER">%d</xliff:g> 秒後にもう一度お試しください。"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"指紋認証センサーをタップしてください"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"指紋アイコン"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"顔を認証しています…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"通知"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ライト"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"カメラを使用中"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"モバイルデータ"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"データ使用量"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"残りのデータ"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index b3c7de798693..50006dd31cfe 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"შეყვანის მეთოდების დაყენება"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"ფიზიკური კლავიატურა"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"მიეცეს <xliff:g id="APPLICATION">%1$s</xliff:g>-ს <xliff:g id="USB_DEVICE">%2$s</xliff:g>-ზე წვდომის უფლება?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"დართავთ <xliff:g id="APPLICATION">%1$s</xliff:g>-ს <xliff:g id="USB_DEVICE">%2$s</xliff:g>-ზე წვდომის ნებას?\nამ აპს არ აქვს მინიჭებული ჩაწერის ნებართვა, მაგრამ შეუძლია ჩაიწეროს აუდიო ამ USB მოწყობილობის მეშვეობით."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"მიეცეს <xliff:g id="APPLICATION">%1$s</xliff:g>-ს <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>-ზე წვდომის უფლება?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"გსურთ, გახსნათ <xliff:g id="APPLICATION">%1$s</xliff:g>, <xliff:g id="USB_DEVICE">%2$s</xliff:g>-ის გამოსაყენებლად?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"გახსნით <xliff:g id="APPLICATION">%1$s</xliff:g>-ს <xliff:g id="USB_DEVICE">%2$s</xliff:g>-ის გამოსაყენებლად?\nამ აპს არ აქვს მინიჭებული ჩაწერის ნებართვა, მაგრამ შეუძლია ჩაიწეროს აუდიო ამ USB მოწყობილობის მეშვეობით."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"გსურთ, გახსნათ <xliff:g id="APPLICATION">%1$s</xliff:g>, <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>-ის გამოსაყენებლად?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"არცერთი დაყენებული აპი არ მუშაობს ამ USB აქსესუართან. შეიტყვეთ მეტი ამ აქსესუარის შესახებ <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB აქსესუარი"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"დადასტურებული"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"დასასრულებლად შეეხეთ „დადასტურებას“"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"ავტორიზებულია"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"PIN-კოდის გამოყენება"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"ნიმუშის გამოყენება"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"პაროლის გამოყენება"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN-კოდი არასწორია"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"ნიმუში არასწორია"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"პაროლი არასწორია"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"დაფიქსირდა ძალიან ბევრი არასწორი მცდელობა.\nცადეთ ხელახლა <xliff:g id="NUMBER">%d</xliff:g> წამში."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"შეეხეთ თითის ანაბეჭდის სენსორს"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"თითის ანაბეჭდის ხატულა"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"მიმდინარეობს თქვენი ძიება…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"შეტყობინებები"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ფანარი"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"კამერა გამოიყენება"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"მობილური ინტერნეტი"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"მონაცემთა მოხმარება"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"დარჩენილი მონაცემები"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index e1fa4b2b7c8d..05e521ccbbca 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Енгізу әдістерін орнату"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Физикалық пернетақта"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="APPLICATION">%1$s</xliff:g> қолданбасына <xliff:g id="USB_DEVICE">%2$s</xliff:g> құрылғысына кіруге рұқсат берілсін бе?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="APPLICATION">%1$s</xliff:g> қолданбасына <xliff:g id="USB_DEVICE">%2$s</xliff:g> құрылғысын пайдалануға рұқсат етілсін бе?\nҚолданбаның жазу рұқсаты жоқ, бірақ осы USB құрылғысы арқылы аудио жаза алады."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g> қолданбасына <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> жабдығына кіруге рұқсат берілсін бе?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> жабдығын басқару үшін <xliff:g id="APPLICATION">%1$s</xliff:g> ашылсын ба?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> құрылғысын пайдалану үшін <xliff:g id="APPLICATION">%1$s</xliff:g> қолданбасын ашу керек пе?\nҚолданбаға жазу рұқсаты берілмеді, бірақ ол осы USB құрылғысы арқылы дыбыс жаза алады."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> жабдығын басқару үшін <xliff:g id="APPLICATION">%1$s</xliff:g> ашылсын ба?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Орнатылған қолданбалар осы USB жабдығымен жұмыс жасамайды.Жабдықты <xliff:g id="URL">%1$s</xliff:g> ден қараңыз."</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB жабдығы"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Расталды"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Аяқтау үшін \"Растау\" түймесін түртіңіз."</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Аутентификацияланған"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"PIN кодын пайдалану"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Өрнекті пайдалану"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Құпия сөзді пайдалану"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN коды қате."</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Өрнек қате."</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Құпия сөз қате."</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Тым көп қате енгізілді.\n<xliff:g id="NUMBER">%d</xliff:g> секундта әрекетті қайталаңыз."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Саусақ ізін оқу сканерін түртіңіз"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Саусақ ізі белгішесі"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Бет ізделуде…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Хабарландырулар"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Қалта шам"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Камера қолданылып жатыр"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Мобильдік деректер"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Дерек шығыны"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Қалған деректер"</string>
@@ -838,7 +848,7 @@
<string name="pip_phone_settings" msgid="8080777499521528521">"Реттеулер"</string>
<string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Жабу үшін төмен қарай сүйреңіз"</string>
<string name="pip_menu_title" msgid="4707292089961887657">"Mәзір"</string>
- <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> \"сурет ішіндегі сурет\" режимінде"</string>
+ <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> \"суреттегі сурет\" режимінде"</string>
<string name="pip_notification_message" msgid="5619512781514343311">"<xliff:g id="NAME">%s</xliff:g> деген пайдаланушының бұл мүмкіндікті пайдалануын қаламасаңыз, параметрлерді түртіп ашыңыз да, оларды өшіріңіз."</string>
<string name="pip_play" msgid="1417176722760265888">"Ойнату"</string>
<string name="pip_pause" msgid="8881063404466476571">"Кідірту"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index db93aa9a448b..a628383beafc 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"រៀបចំ​វិធីសាស្ត្រ​បញ្ចូល"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"ក្ដារ​ចុច​ពិតប្រាកដ"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"អនុញ្ញាត <xliff:g id="APPLICATION">%1$s</xliff:g> ឱ្យចូលប្រើ <xliff:g id="USB_DEVICE">%2$s</xliff:g> មែនទេ?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"អនុញ្ញាតឱ្យ <xliff:g id="APPLICATION">%1$s</xliff:g> ចូលប្រើ <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nកម្មវិធីនេះ​មិនទាន់បាន​ទទួលសិទ្ធិ​ថតសំឡេងនៅឡើយទេ ប៉ុន្តែ​អាចថត​សំឡេង​តាមរយៈ​ឧបករណ៍ USB នេះបាន។"</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"អនុញ្ញាត <xliff:g id="APPLICATION">%1$s</xliff:g> ឱ្យចូលប្រើ <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> មែនទេ?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"បើក <xliff:g id="APPLICATION">%1$s</xliff:g> ដើម្បីគ្រប់គ្រង <xliff:g id="USB_DEVICE">%2$s</xliff:g> មែនទេ?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"បើក <xliff:g id="APPLICATION">%1$s</xliff:g> ដើម្បីគ្រប់គ្រង <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nកម្មវិធីនេះ​មិនទាន់បាន​ទទួលសិទ្ធិ​ថតសំឡេង​នៅឡើយទេ ប៉ុន្តែអាច​ថតសំឡេង​តាមរយៈ​ឧបករណ៍ USB នេះ។"</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"បើក <xliff:g id="APPLICATION">%1$s</xliff:g> ដើម្បីគ្រប់គ្រង <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> មែនទេ?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"គ្មាន​កម្មវិធី​បាន​ដំឡើង​ដំណើរការ​ជា​មួយ​ឧបករណ៍​យូអេសប៊ី។ ស្វែងយល់​បន្ថែម​អំពី​ឧបករណ៍​នេះ​នៅ <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"ឧបករណ៍​យូអេសប៊ី"</string>
@@ -126,8 +128,15 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"បានបញ្ជាក់"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"ចុច \"បញ្ជាក់\" ដើម្បីបញ្ចប់"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"បាន​ផ្ទៀងផ្ទាត់"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"ប្រើកូដ PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"ប្រើ​លំនាំ"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"ប្រើពាក្យសម្ងាត់"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"កូដ PIN មិន​ត្រឹមត្រូវ​"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"លំនាំមិនត្រឹមត្រូវ"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"ពាក្យសម្ងាត់មិនត្រឹមត្រូវ"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"ការព្យាយាម​ចូលខុស​ច្រើនដងពេក។\nសូមព្យាយាម​ម្តងទៀត​ក្នុងរយៈពេល <xliff:g id="NUMBER">%d</xliff:g> វិនាទី។"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ប៉ះ​ឧបករណ៍​ចាប់ស្នាម​ម្រាមដៃ"</string>
- <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"រូបតំណាង​ស្នាម​ម្រាមដៃ"</string>
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"រូប​ស្នាម​ម្រាមដៃ"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"កំពុងស្វែងរកអ្នក…"</string>
<string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"រូប​ផ្ទៃមុខ"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ប៊ូតុង​ពង្រីក​ត្រូវ​គ្នា។"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"ការ​ជូនដំណឹង"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ពិល"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"កំពុងប្រើ​កាមេរ៉ា"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"ទិន្នន័យ​ទូរសព្ទចល័ត"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"ការ​ប្រើ​ទិន្នន័យ"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"ទិន្នន័យ​នៅសល់"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 98589ad29461..dc4674a540a4 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ಇನ್‌ಪುಟ್ ವಿಧಾನಗಳನ್ನು ಹೊಂದಿಸು"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"ಭೌತಿಕ ಕೀಬೋರ್ಡ್"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ಪ್ರವೇಶಿಸಲು <xliff:g id="APPLICATION">%1$s</xliff:g> ಅನ್ನು ಅನುಮತಿಸುವುದೇ?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ಅನ್ನು ಪ್ರವೇಶಿಸಲು <xliff:g id="APPLICATION">%1$s</xliff:g> ಅನ್ನು ಅನುಮತಿಸುವುದೇ?\nಈ ಆ್ಯಪ್‌ಗೆ ರೆಕಾರ್ಡ್ ಅನುಮತಿಯನ್ನು ನೀಡಲಾಗಿಲ್ಲ, ಆದರೆ ಈ USB ಸಾಧನದ ಮೂಲಕ ಆಡಿಯೊವನ್ನು ಸೆರೆಹಿಡಿಯಬಹುದು."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ಗೆ ಪ್ರವೇಶಿಸಲು <xliff:g id="APPLICATION">%1$s</xliff:g> ಅನ್ನು ಅನುಮತಿಸುವುದೇ?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ಅನ್ನು ನಿರ್ವಹಿಸಲು <xliff:g id="APPLICATION">%1$s</xliff:g> ಅನ್ನು ತೆರೆಯುವುದೇ?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ಅನ್ನು ನಿಯಂತ್ರಿಸಲು <xliff:g id="APPLICATION">%1$s</xliff:g> ಅನ್ನು ತೆರೆಯುವುದೇ?\nಈ ಆ್ಯಪ್‌ಗೆ ರೆಕಾರ್ಡ್ ಅನುಮತಿಯನ್ನು ನೀಡಲಾಗಿಲ್ಲ, ಆದರೆ ಈ USB ಸಾಧನದ ಮೂಲಕ ಆಡಿಯೊವನ್ನು ಸೆರೆಹಿಡಿಯಬಹುದು."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ಅನ್ನು ನಿರ್ವಹಿಸಲು <xliff:g id="APPLICATION">%1$s</xliff:g> ಅನ್ನು ತೆರೆಯುವುದೇ?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"ಆಪ್‌ಗಳು USB ಪರಿಕರದಲ್ಲಿ ಕಾರ್ಯನಿರ್ವಹಿಸುವುದಿಲ್ಲ. ಆ ಬಗ್ಗೆ <xliff:g id="URL">%1$s</xliff:g> ನಲ್ಲಿ ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB ಪರಿಕರ"</string>
@@ -56,7 +58,7 @@
<string name="always_use_device" msgid="4015357883336738417">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ಸಂಪರ್ಕಗೊಂಡಾಗ ಯಾವಾಗಲೂ <xliff:g id="APPLICATION">%1$s</xliff:g> ಅನ್ನು ತೆರೆಯಿರಿ"</string>
<string name="always_use_accessory" msgid="3257892669444535154">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ಸಂಪರ್ಕಗೊಂಡಾಗ ಯಾವಾಗಲೂ <xliff:g id="APPLICATION">%1$s</xliff:g> ಅನ್ನು ತೆರೆಯಿರಿ"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"USB ಡೀಬಗ್ ಮಾಡುವಿಕೆಯನ್ನು ಅನುಮತಿಸುವುದೇ?"</string>
- <string name="usb_debugging_message" msgid="2220143855912376496">"ಕಂಪ್ಯೂಟರ್‌ನ RSA ಕೀ ಫಿಂಗರ್ ಪ್ರಿಂಟ್ ಹೀಗಿದೆ :\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
+ <string name="usb_debugging_message" msgid="2220143855912376496">"ಕಂಪ್ಯೂಟರ್‌ನ RSA ಕೀ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಹೀಗಿದೆ :\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"ಈ ಕಂಪ್ಯೂಟರ್‌ನಿಂದ ಯಾವಾಗಲೂ ಅನುಮತಿಸಿ"</string>
<string name="usb_debugging_allow" msgid="2272145052073254852">"ಅನುಮತಿಸಿ"</string>
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB ಡೀಬಗ್ ಮಾಡುವಿಕೆಯನ್ನು ಅನುಮತಿಸಲಾಗಿಲ್ಲ"</string>
@@ -108,8 +110,8 @@
<string name="accessibility_phone_button" msgid="6738112589538563574">"ಫೋನ್"</string>
<string name="accessibility_voice_assist_button" msgid="487611083884852965">"ಧ್ವನಿ ಸಹಾಯಕ"</string>
<string name="accessibility_unlock_button" msgid="128158454631118828">"ಅನ್‌ಲಾಕ್"</string>
- <string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"ಫಿಂಗರ್ ಪ್ರಿಂಟ್ ನಿರೀಕ್ಷಿಸಲಾಗುತ್ತಿದೆ"</string>
- <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"ನಿಮ್ಮ ಬೆರಳಚ್ಚು ಬಳಸದೆಯೇ ಅನ್‌ಲಾಕ್ ಮಾಡಿ"</string>
+ <string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್‍‍ಗಾಗಿ ನಿರೀಕ್ಷಿಸಲಾಗುತ್ತಿದೆ"</string>
+ <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"ನಿಮ್ಮ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಬಳಸದೆಯೇ ಅನ್‌ಲಾಕ್ ಮಾಡಿ"</string>
<string name="accessibility_scanning_face" msgid="769545173211758586">"ಮುಖವನ್ನು ಸ್ಕ್ಯಾನ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
<string name="accessibility_send_smart_reply" msgid="7766727839703044493">"ಕಳುಹಿಸಿ"</string>
<string name="accessibility_manage_notification" msgid="2026361503393549753">"ಅಧಿಸೂಚನೆಗಳನ್ನು ನಿರ್ವಹಿಸಿ"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"ದೃಢೀಕರಿಸಲಾಗಿದೆ"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"ಪೂರ್ಣಗೊಳಿಸಲು ದೃಢೀಕರಿಸಿ ಅನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"ದೃಢೀಕರಿಸಲಾಗಿದೆ"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"ಪಿನ್ ಬಳಸಿ"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"ಪ್ಯಾಟರ್ನ್ ಬಳಸಿ"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"ಪಾಸ್‌ವರ್ಡ್ ಬಳಸಿ"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"ತಪ್ಪಾದ ಪಿನ್‌"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"ಪ್ಯಾಟರ್ನ್ ತಪ್ಪಾಗಿದೆ"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"ಪಾಸ್‌ವರ್ಡ್ ತಪ್ಪಾಗಿದೆ"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"ಹಲವಾರು ತಪ್ಪು ಪ್ರಯತ್ನಗಳು.\nಮತ್ತೆ <xliff:g id="NUMBER">%d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್‌‌ ಅನ್ನು ಸ್ಪರ್ಶಿಸಿ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಐಕಾನ್"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"ನಿಮಗಾಗಿ ಹುಡುಕಲಾಗುತ್ತಿದೆ…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"ಅಧಿಸೂಚನೆಗಳು"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ಫ್ಲಾಶ್‌ಲೈಟ್‌"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"ಕ್ಯಾಮರಾ ಬಳಕೆಯಲ್ಲಿದೆ"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"ಮೊಬೈಲ್ ಡೇಟಾ"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"ಡೇಟಾ ಬಳಕೆ"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"ಉಳಿದಿರುವ ಡೇಟಾ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 03c277e5a888..714af0c0fc6c 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"입력 방법 설정"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"물리적 키보드"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="APPLICATION">%1$s</xliff:g> 앱이 <xliff:g id="USB_DEVICE">%2$s</xliff:g>에 액세스하도록 허용하시겠습니까?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="APPLICATION">%1$s</xliff:g>에서 <xliff:g id="USB_DEVICE">%2$s</xliff:g>에 액세스하도록 허용하시겠습니까?\n이 앱에는 녹음 권한이 부여되지 않았지만, 이 USB 기기를 통해 오디오를 녹음할 수 있습니다."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g> 앱이 <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>에 액세스하도록 허용하시겠습니까?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="APPLICATION">%1$s</xliff:g> 앱을 열어 <xliff:g id="USB_DEVICE">%2$s</xliff:g>을(를) 처리하시겠습니까?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="USB_DEVICE">%2$s</xliff:g>을(를) 처리하기 위해 <xliff:g id="APPLICATION">%1$s</xliff:g>을(를) 여시겠습니까?\n이 앱에는 녹음 권한이 부여되지 않았지만, 이 USB 기기를 통해 오디오를 녹음할 수 있습니다."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="APPLICATION">%1$s</xliff:g> 앱을 열어 <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>을(를) 처리하시겠습니까?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"이 USB와 호환되는 설치 앱이 없습니다. <xliff:g id="URL">%1$s</xliff:g>에서 세부정보를 참조하세요."</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB 액세서리"</string>
@@ -109,7 +111,7 @@
<string name="accessibility_voice_assist_button" msgid="487611083884852965">"음성 지원"</string>
<string name="accessibility_unlock_button" msgid="128158454631118828">"잠금 해제"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"지문 대기 중"</string>
- <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"지문 파일을 사용하지 않고 잠금 해제"</string>
+ <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"지문을 사용하지 않고 잠금 해제"</string>
<string name="accessibility_scanning_face" msgid="769545173211758586">"얼굴 스캔 중"</string>
<string name="accessibility_send_smart_reply" msgid="7766727839703044493">"보내기"</string>
<string name="accessibility_manage_notification" msgid="2026361503393549753">"알림 관리"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"확인함"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"완료하려면 확인을 탭하세요."</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"인증됨"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"PIN 사용"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"패턴 사용"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"비밀번호 사용"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"잘못된 PIN"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"잘못된 패턴"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"잘못된 비밀번호"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"잘못된 시도 횟수가 너무 많습니다.\n<xliff:g id="NUMBER">%d</xliff:g>초 후에 다시 시도하세요."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"지문 센서를 터치하세요."</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"지문 아이콘"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"찾는 중..."</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"알림"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"손전등"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"카메라 사용 중"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"모바일 데이터"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"데이터 사용"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"잔여 데이터"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index c82e5b035d6a..820c6b32c210 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Киргизүү ыкмасын тууралоо"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Аппараттык тергич"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="APPLICATION">%1$s</xliff:g> колдонмосу <xliff:g id="USB_DEVICE">%2$s</xliff:g> түзмөгүн колдоно берсинби?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="APPLICATION">%1$s</xliff:g> колдонмосу үчүн <xliff:g id="USB_DEVICE">%2$s</xliff:g> түзмөгүнө мүмкүнчүлүк алууга уруксат бересизби?\nБул колдонмонун жаздырууга уруксаты жок, бирок бул USB түзмөгү аркылуу аудиону жаздыра алат."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g> колдонмосу <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> кабелин колдоно берсинби?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> түзмөгүнө туташуу үчүн <xliff:g id="APPLICATION">%1$s</xliff:g> колдонмосу ачылсынбы?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> кабелине туташуу үчүн <xliff:g id="APPLICATION">%1$s</xliff:g> ачылсынбы?\nБул колдонмонун жаздырууга уруксаты жок, бирок бул USB түзмөгү аркылуу аудиону жаздыра алат."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> кабелине туташуу үчүн <xliff:g id="APPLICATION">%1$s</xliff:g> колдонмосу ачылсынбы?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Эч бир орнотулган колдонмо USB аксессуар м-н иштебейт. Кенен маалыматтар: <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB шайманы"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Ырасталды"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Бүтүрүү үчүн \"Ырастоо\" баскычын басыңыз"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Аныктыгы текшерилди"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"PIN кодду колдонуу"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Графикалык ачкычты колдонуу"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Сырсөз колдонуу"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN код туура эмес"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Графикалык ачкыч туура эмес"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Сырсөз туура эмес"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Өтө көп жолу туура эмес аракет кылынды.\n<xliff:g id="NUMBER">%d</xliff:g> секунддан кийин кайра кайталаңыз."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Манжа изинин сенсорун басыңыз"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Манжа изинин сүрөтчөсү"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Жүзүңүз изделүүдө…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Билдирмелер"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Кол чырак"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Камера колдонулууда"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Мобилдик Интернет"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Дайындардын өткөрүлүшү"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Калган дайындар"</string>
@@ -902,7 +912,7 @@
<string name="slice_permission_text_2" msgid="3146758297471143723">"- <xliff:g id="APP">%1$s</xliff:g> колдонмосунда аракеттерди аткарат"</string>
<string name="slice_permission_checkbox" msgid="7986504458640562900">"<xliff:g id="APP">%1$s</xliff:g> бардык колдонмолордун үлгүлөрүн көрсөтүүгө уруксат берүү"</string>
<string name="slice_permission_allow" msgid="2340244901366722709">"Уруксат берүү"</string>
- <string name="slice_permission_deny" msgid="7683681514008048807">"Жок"</string>
+ <string name="slice_permission_deny" msgid="7683681514008048807">"Тыюу салынат"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Батареяны үнөмдөгүчтүн тартибин жөндөө үчүн басыңыз"</string>
<string name="auto_saver_text" msgid="2563289953551438248">"Батареянын кубаты түгөнүп калганда, күйгүзүлсүн"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Жок, рахмат"</string>
@@ -926,7 +936,7 @@
<string name="bubbles_settings_button_description" msgid="2970630476657287189">"<xliff:g id="APP_NAME">%1$s</xliff:g> көбүктөрүнүн жөндөөлөрү"</string>
<string name="bubbles_prompt" msgid="8807968030159469710">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосунун калкып чыкма билдирмелерине уруксат бересизби?"</string>
<string name="manage_bubbles_text" msgid="7027739766859191408">"Башкаруу"</string>
- <string name="no_bubbles" msgid="337101288173078247">"Жок"</string>
+ <string name="no_bubbles" msgid="337101288173078247">"Тыюу салынат"</string>
<string name="yes_bubbles" msgid="668809525728633841">"Уруксат берүү"</string>
<string name="ask_me_later_bubbles" msgid="2147688438402939029">"Кийинчерээк суралсын"</string>
<string name="bubble_content_description_single" msgid="1184462974339387516">"<xliff:g id="APP_NAME">%2$s</xliff:g> колдонмосунан <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 4f4d29153dbb..89d37794cdf1 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ຕັ້ງຄ່າວິທີການປ້ອນຂໍ້ມູນ"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"ແປ້ນພິມແທ້"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"ອະນຸຍາດໃຫ້ <xliff:g id="APPLICATION">%1$s</xliff:g> ເຂົ້າເຖິງ <xliff:g id="USB_DEVICE">%2$s</xliff:g> ໄດ້ບໍ?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"ອະນຸຍາດໃຫ້ <xliff:g id="APPLICATION">%1$s</xliff:g> ເຂົ້າເຖິງ <xliff:g id="USB_DEVICE">%2$s</xliff:g> ໄດ້ບໍ?\nແອັບນີ້ບໍ່ໄດ້ຮັບອະນຸາດໃຫ້ບັນທຶກໄດ້ແຕ່ສາມາດບັນທຶກສຽງໄດ້ຜ່ານອຸປະກອນ USB ນີ້."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"ອະນຸຍາດໃຫ້ <xliff:g id="APPLICATION">%1$s</xliff:g> ເຂົ້າເຖິງ <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ໄດ້ບໍ?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"ເປີດ <xliff:g id="APPLICATION">%1$s</xliff:g> ເພື່ອໃຊ້ກັບ <xliff:g id="USB_DEVICE">%2$s</xliff:g> ບໍ?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"ເປີດ <xliff:g id="APPLICATION">%1$s</xliff:g> ເພື່ອໃຊ້ <xliff:g id="USB_DEVICE">%2$s</xliff:g> ບໍ?\nແອັບນີ້ຍັງບໍ່ໄດ້ຮັບອະນຸຍາດໃຫ້ບັນທຶກເທື່ອ ແຕ່ສາມາດບັນທຶກສຽງຜ່ານອຸປະກອນ USB ນີ້ໄດ້."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"ເປີດ <xliff:g id="APPLICATION">%1$s</xliff:g> ເພື່ອໃຊ້ກັບ <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ບໍ?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"ບໍ່ມີແອັບຯໃດທີ່ຕິດຕັ້ງໄປແລ້ວ ສາມາດເຮັດວຽກຮ່ວມກັບອຸປະກອນເສີມ USB ນີ້ໄດ້. ສຶກສາເພີ່ມເຕີມກ່ຽວກັບອຸປະກອນເສີມນີ້ທີ່ <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"ອຸປະກອນເສີມ USB"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"ຢືນຢັນແລ້ວ"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"ແຕະຢືນຢັນເພື່ອສຳເລັດ"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"ຮັບຮອງຄວາມຖືກຕ້ອງແລ້ວ"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"ໃຊ້ PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"ໃຊ້ຮູບແບບ"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"ໃຊ້ລະຫັດຜ່ານ"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN ບໍ່ຖືກຕ້ອງ"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"ຮູບແບບບໍ່ຖືກຕ້ອງ"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"ລະຫັດຜ່ານບໍ່ຖືກຕ້ອງ"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"ມີ​ຄວາມ​ພະ​ຍາ​ຍາມ​ບໍ່​ຖືກ​ຕ້ອງ​ຫຼາຍ​ເທື່ອ​ເກີນ​ໄປ.\nກະລຸນາລອງ​ໃໝ່​ອີກ​ໃນ <xliff:g id="NUMBER">%d</xliff:g> ​ວິ​ນາ​ທີ."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ແຕະໃສ່ເຊັນເຊີລາຍນິ້ວມື"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ໄອຄອນລາຍນິ້ວມື"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"ກຳລັງຊອກຫາທ່ານ…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"ການແຈ້ງເຕືອນ"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"​ໄຟ​ສາຍ"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"ມີການໃຊ້ກ້ອງຖ່າຍຮູບຢູ່"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"ອິນເຕີເນັດມືຖື"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"​ການ​​ນຳ​ໃຊ້​​ຂໍ້​ມູນ"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"​ຂໍ້​ມູນ​ທີ່​ຍັງ​ເຫຼືອ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index ca7c89ec4847..208f49b3d55d 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Nustatyti įvesties metodus"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fizinė klaviatūra"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Leisti „<xliff:g id="APPLICATION">%1$s</xliff:g>“ pasiekti įrenginį (<xliff:g id="USB_DEVICE">%2$s</xliff:g>)?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Leisti „<xliff:g id="APPLICATION">%1$s</xliff:g>“ pasiekti įrenginį (<xliff:g id="USB_DEVICE">%2$s</xliff:g>)?\nŠiai programai nebuvo suteiktas leidimas įrašyti, bet ji gali užfiksuoti garsą per šį USB įrenginį."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Leisti „<xliff:g id="APPLICATION">%1$s</xliff:g>“ pasiekti įrenginį (<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>)?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Atidaryti „<xliff:g id="APPLICATION">%1$s</xliff:g>“, kad būtų galima tvarkyti įrenginį (<xliff:g id="USB_DEVICE">%2$s</xliff:g>)?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Atidaryti programą „<xliff:g id="APPLICATION">%1$s</xliff:g>“, kad ji galėtų tvarkyti „<xliff:g id="USB_DEVICE">%2$s</xliff:g>“?\nŠiai programai nebuvo suteiktas leidimas įrašyti, bet ji gali užfiksuoti garsą per šį USB įrenginį."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Atidaryti „<xliff:g id="APPLICATION">%1$s</xliff:g>“, kad būtų galima tvarkyti įrenginį (<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>)?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Su šiuo USB priedu neveiks jokios įdieg. pr. Suž. daugiau apie šį priedą adresu <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB reikmuo"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Patvirtinta"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Paliesk. „Patvirtinti“, kad užbaigtumėte"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autentifikuota"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Naudoti PIN kodą"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Naudoti atrakinimo piešinį"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Naudoti slaptažodį"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Netinkamas PIN kodas"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Netinkamas atrakinimo piešinys"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Netinkamas slaptažodis"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Per daug klaidingų bandymų.\nBandykite dar kartą po <xliff:g id="NUMBER">%d</xliff:g> sek."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Palieskite piršto antspaudo jutiklį"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Piršto antspaudo piktograma"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Ieškoma jūsų…"</string>
@@ -368,6 +377,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Pranešimai"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Žibintuvėlis"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Fotoaparatas naudojamas"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobiliojo ryšio duomenys"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Duomenų naudojimas"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Likę duomenys"</string>
@@ -466,7 +476,7 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Daugiau neberodyti"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Viską išvalyti"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Tvarkyti"</string>
- <string name="notification_section_header_gentle" msgid="4372438504154095677">"Tylūs pranešimai"</string>
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Tylieji pranešimai"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Išvalyti visus tylius pranešimus"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Pranešimai pristabdyti naudojant netrukdymo režimą"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Pradėti dabar"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 424e10427903..a96fc3b57863 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Iestatīt ievades metodes"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fiziskā tastatūra"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Vai atļaut lietotnei <xliff:g id="APPLICATION">%1$s</xliff:g> piekļūt šai ierīcei: <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Vai atļaut lietotnei <xliff:g id="APPLICATION">%1$s</xliff:g> piekļūt ierīcei “<xliff:g id="USB_DEVICE">%2$s</xliff:g>”?\nŠai lietotnei nav piešķirta ierakstīšanas atļauja, taču tā varētu tvert audio, izmantojot šo USB ierīci."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Vai atļaut lietotnei <xliff:g id="APPLICATION">%1$s</xliff:g> piekļūt šim piederumam: <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Vai atvērt lietotni <xliff:g id="APPLICATION">%1$s</xliff:g>, lai izmantotu šo ierīci: <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Vai vēlaties atvērt lietotni <xliff:g id="APPLICATION">%1$s</xliff:g>, lai pārvaldītu ierīci <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nŠai lietotnei nav piešķirta ierakstīšanas atļauja, taču tā varētu tvert audio, izmantojot šo USB ierīci."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Vai atvērt lietotni <xliff:g id="APPLICATION">%1$s</xliff:g>, lai izmantotu šo piederumu: <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Neviena no inst. liet. nedarb. ar šo USB pied. Uzz. vairāk par šo pied. vietnē <xliff:g id="URL">%1$s</xliff:g>."</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB piederums"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Apstiprināts"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Lai pabeigtu, pieskarieties Apstiprināt"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autentifikācija veikta"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Izmantot PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Izmantot kombināciju"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Izmantot paroli"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Nepareizs PIN"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Nepareiza kombinācija"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Nepareiza parole"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Pārāk daudz neveiksmīgu mēģinājumu.\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER">%d</xliff:g> sekundēm."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Pieskarieties pirksta nospieduma sensoram"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Pirksta nospieduma ikona"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Notiek jūsu sejas meklēšana…"</string>
@@ -366,6 +375,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Paziņojumi"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Zibspuldze"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Kamera tiek lietota"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobilie dati"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Datu lietojums"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Atlikušie dati"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index dd86edecb64a..f8dea7925309 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Постави методи на внес."</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Физичка тастатура"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Ќе дозволите <xliff:g id="APPLICATION">%1$s</xliff:g> да пристапува до <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Дали дозволувате <xliff:g id="APPLICATION">%1$s</xliff:g> да пристапи до <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nНа апликацијава не ѝ е доделена дозвола за снимање, но може да снима аудио преку овој USB-уред."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Ќе дозволите <xliff:g id="APPLICATION">%1$s</xliff:g> да пристапува до <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Да се отвори <xliff:g id="APPLICATION">%1$s</xliff:g> за да управува со <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Да се отвори ли <xliff:g id="APPLICATION">%1$s</xliff:g> за да се управува со <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nНа апликацијава не ѝ е доделена дозвола за снимање, но може да снима аудио преку овој USB-уред."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Да се отвори <xliff:g id="APPLICATION">%1$s</xliff:g> за да управува со <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Нема инсталирано апликации што работат со овој USB додаток. Дознајте повеќе за овој додаток на <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB додаток"</string>
@@ -126,8 +128,15 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Потврдено"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Допрете „Потврди“ за да се заврши"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Проверена"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Користи PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Користи шема"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Користи лозинка"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Погрешен PIN"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Погрешна шема"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Погрешна лозинка"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Премногу погрешни обиди.\nОбидете се повторно за <xliff:g id="NUMBER">%d</xliff:g>секунди."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Допрете го сензорот за отпечатоци"</string>
- <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Икона за отпечатоци"</string>
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Икона за отпечаток"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Ве бараме вас…"</string>
<string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Икона за лице"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Копче за компатибилност на зум."</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Известувања"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Светилка"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Камерата е во употреба"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Мобилен интернет"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Потрошен интернет"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Преостанати податоци"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 859e65b11bb0..66265cbbf2b3 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -47,8 +47,11 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ടൈപ്പുചെയ്യൽ രീതികൾ സജ്ജീകരിക്കുക"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"ഫിസിക്കൽ കീബോഡ്"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ആക്‌സസ് ചെയ്യാൻ <xliff:g id="APPLICATION">%1$s</xliff:g>-നെ അനുവദിക്കണോ?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ആക്‌സസ് ചെയ്യാൻ <xliff:g id="APPLICATION">%1$s</xliff:g> എന്നതിനെ അനുവദിക്കണോ?\nഈ ആപ്പിന് റെക്കോർഡ് അനുമതി നൽകിയിട്ടില്ല, എന്നാൽ ഈ USB ഉപകരണത്തിലൂടെ ഓഡിയോ ക്യാപ്‌ചർ ചെയ്യാനാവും."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ആക്‌സസ് ചെയ്യാൻ <xliff:g id="APPLICATION">%1$s</xliff:g>-നെ അനുവദിക്കണോ?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> കൈകാര്യം ചെയ്യാൻ <xliff:g id="APPLICATION">%1$s</xliff:g> തുറക്കണോ?"</string>
+ <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) -->
+ <skip />
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> കൈകാര്യം ചെയ്യാൻ <xliff:g id="APPLICATION">%1$s</xliff:g> തുറക്കണോ?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"ഈ USB ആക്‌സസ്സറിയിൽ ഇൻസ്‌റ്റാളുചെയ്‌തവയൊന്നും പ്രവർത്തിക്കുന്നില്ല. <xliff:g id="URL">%1$s</xliff:g>-ൽ ഇതേക്കുറിച്ച് കൂടുതലറിയുക"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB ആക്‌സസ്സറി"</string>
@@ -108,7 +111,7 @@
<string name="accessibility_phone_button" msgid="6738112589538563574">"ഫോണ്‍"</string>
<string name="accessibility_voice_assist_button" msgid="487611083884852965">"വോയ്‌സ് സഹായം"</string>
<string name="accessibility_unlock_button" msgid="128158454631118828">"അണ്‍ലോക്ക് ചെയ്യുക"</string>
- <string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"ഫിംഗർപ്രിന്റിനായി കാക്കുന്നു"</string>
+ <string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"ഫിംഗർപ്രിന്റിനായി കാത്തിരിക്കുന്നു"</string>
<string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"നിങ്ങളുടെ ഫിംഗർപ്രിന്റ് ഉപയോഗിക്കാതെ അൺലോക്കുചെയ്യുക"</string>
<string name="accessibility_scanning_face" msgid="769545173211758586">"മുഖം സ്കാൻ ചെയ്യുന്നു"</string>
<string name="accessibility_send_smart_reply" msgid="7766727839703044493">"അയയ്ക്കുക"</string>
@@ -126,8 +129,15 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"സ്ഥിരീകരിച്ചു"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"പൂർത്തിയാക്കാൻ സ്ഥിരീകരിക്കുക ടാപ്പ് ചെയ്യൂ"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"പരിശോധിച്ചുറപ്പിച്ചു"</string>
- <string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"വിരലടയാള സെൻസർ സ്‌പർശിക്കുക"</string>
- <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"വിരലടയാള ഐക്കൺ"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"പിൻ ഉപയോഗിക്കുക"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"പാറ്റേൺ ഉപയോഗിക്കുക"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"പാസ്‌വേഡ് ഉപയോഗിക്കുക"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"പിൻ തെറ്റാണ്"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"പാറ്റേൺ തെറ്റാണ്"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"പാസ്‌വേഡ് തെറ്റാണ്"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"നിരവധി തെറ്റായ ശ്രമങ്ങൾ. \n<xliff:g id="NUMBER">%d</xliff:g> സെക്കൻഡിൽ വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ഫിംഗർപ്രിന്റ് സെൻസർ സ്‌പർശിക്കുക"</string>
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ഫിംഗർപ്രിന്റ് ഐക്കൺ"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"നിങ്ങൾക്കായി തിരയുന്നു…"</string>
<string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"മുഖത്തിന്റെ ഐക്കൺ"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"അനുയോജ്യതാ സൂം ബട്ടൺ."</string>
@@ -364,6 +374,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"അറിയിപ്പുകൾ"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ടോർച്ച്"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"ക്യാമറ ഉപയോഗത്തിലാണ്"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"മൊബൈൽ ഡാറ്റ"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"ഡാറ്റാ ഉപയോഗം"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"ശേഷിക്കുന്ന ഡാറ്റ"</string>
@@ -377,7 +388,7 @@
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"സൂര്യോദയം വരെ"</string>
<string name="quick_settings_night_secondary_label_on_at" msgid="6256314040368487637">"<xliff:g id="TIME">%s</xliff:g>-ന്"</string>
<string name="quick_settings_secondary_label_until" msgid="2749196569462600150">"<xliff:g id="TIME">%s</xliff:g> വരെ"</string>
- <string name="quick_settings_ui_mode_night_label" msgid="3419947801072692538">"ഇരുണ്ട തീം"</string>
+ <string name="quick_settings_ui_mode_night_label" msgid="3419947801072692538">"ഡാർക്ക് തീം"</string>
<string name="quick_settings_ui_mode_night_label_battery_saver" msgid="7438725724589758362">"ഇരുണ്ട തീം\nബാറ്ററി ലാഭിക്കൽ"</string>
<string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC പ്രവർത്തനരഹിതമാക്കി"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 3363af7a0fd9..00e6dcbd8871 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Оруулах аргыг тохируулах"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Бодит гар"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="APPLICATION">%1$s</xliff:g>-г <xliff:g id="USB_DEVICE">%2$s</xliff:g>-д хандахыг зөвшөөрөх үү?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="APPLICATION">%1$s</xliff:g>-д <xliff:g id="USB_DEVICE">%2$s</xliff:g>-д хандахыг зөвшөөрөх үү?\nЭнэ аппад бичих зөвшөөрөл олгогдоогүй ч USB төхөөрөмжөөр дамжуулан аудио бичиж чадсан."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g>-г <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>-д хандахыг зөвшөөрөх үү?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g>-г зохицуулахын тулд <xliff:g id="APPLICATION">%1$s</xliff:g>-г нээх үү?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="USB_DEVICE">%2$s</xliff:g>-г зохицуулахын тулд <xliff:g id="APPLICATION">%1$s</xliff:g>-г нээх үү?\nЭнэ апликейшнд бичих зөвшөөрөл олгогдоогүй ч энэ USB төхөөрөмжөөр дамжуулан аудио бичиж чадсан."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>-г зохицуулахын тулд <xliff:g id="APPLICATION">%1$s</xliff:g>-г нээх үү?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Энэ USB хэрэгсэл дээр суулгасан апп ажиллаагүй байна. Энэ хэрэгслийн талаар <xliff:g id="URL">%1$s</xliff:g>-с дэлгэрэнгүй үзнэ үү."</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB төхөөрөмж"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Баталгаажсан"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Дуусгахын тулд баталгаажуулахыг товших"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Баталгаажуулагдсан"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"ПИН ашиглах"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Хээ ашиглах"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Нууц үг ашиглах"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"ПИН код буруу байна"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Хээ буруу байна"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Нууц үг буруу байна"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Хэт олон удаа буруу оруулсан байна.\n<xliff:g id="NUMBER">%d</xliff:g> секундийн дараа дахин оролдоно уу."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Хурууны хээ мэдрэгчид хүрэх"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Хурууны хээний дүрс тэмдэг"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Таныг хайж байна…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Мэдэгдэл"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Гар чийдэн"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Камерыг ашиглаж байна"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Мобайл дата"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Дата ашиглалт"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Үлдсэн дата"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index f785fe98d9c4..671060b9feff 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -47,8 +47,11 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"इनपुट पद्धती सेट करा"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"वास्तविक कीबोर्ड"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="APPLICATION">%1$s</xliff:g> ला <xliff:g id="USB_DEVICE">%2$s</xliff:g> अ‍ॅक्सेस करण्याची अनुमती द्यायची का?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="APPLICATION">%1$s</xliff:g> ला <xliff:g id="USB_DEVICE">%2$s</xliff:g> अ‍ॅक्सेस करण्याची अनुमती द्यायची का?\nया अ‍ॅपला रेकॉर्ड करण्याची परवानगी दिलेली नाही पण या USB डिव्हाइसद्वारे ऑडिओ कॅप्चर केला जाऊ शकतो."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g> ला <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> अ‍ॅक्सेस करण्याची अनुमती द्यायची का?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> हाताळण्यासाठी <xliff:g id="APPLICATION">%1$s</xliff:g> उघडायचे का?"</string>
+ <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) -->
+ <skip />
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> हाताळण्यासाठी <xliff:g id="APPLICATION">%1$s</xliff:g> उघडायचे का?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"इंस्टॉल केलेली अ‍ॅप्स या USB उपसाधनासह कार्य करत नाहीत. <xliff:g id="URL">%1$s</xliff:g> येथे या उपसाधनाविषयी अधिक जाणून घ्या"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB उपसाधन"</string>
@@ -126,6 +129,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"निश्चित केले"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"पूर्ण करण्यासाठी खात्री करा वर टॅप करा"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"ऑथेंटिकेशन केलेले"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"पिन वापरा"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"पॅटर्न वापरा"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"पासवर्ड वापरा"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"चुकीचा पिन"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"चुकीचा पॅटर्न"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"चुकीचा पासवर्ड"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"बरेच चुकीचे प्रयत्न. \n <xliff:g id="NUMBER">%d</xliff:g> सेकंदांमध्‍ये पुन्हा प्रयत्न करा."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"फिंगरप्रिंट सेन्सरला स्पर्श करा"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"फिंगरप्रिंट आयकन"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"तुमच्यासाठी शोधत आहे…"</string>
@@ -364,6 +374,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"सूचना"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"फ्लॅशलाइट"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"कॅमेरा वापरात आहे"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"मोबाइल डेटा"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"डेटा वापर"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"शिल्लक डेटा"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 352c376f8a37..265f1617c279 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Sediakan kaedah input"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Papan kekunci fizikal"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Benarkan <xliff:g id="APPLICATION">%1$s</xliff:g> mengakses <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Benarkan <xliff:g id="APPLICATION">%1$s</xliff:g> mengakses <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nApl ini belum diberikan kebenaran merakam tetapi dapat merakam audio melalui peranti USB ini."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Benarkan <xliff:g id="APPLICATION">%1$s</xliff:g> mengakses <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Buka <xliff:g id="APPLICATION">%1$s</xliff:g> untuk mengendalikan <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Buka <xliff:g id="APPLICATION">%1$s</xliff:g> untuk mengendalikan <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nApl ini belum diberikan kebenaran merakam tetapi dapat merakam audio melalui peranti USB ini."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Buka <xliff:g id="APPLICATION">%1$s</xliff:g> untuk mengendalikan <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Tiada apl yg dipsg bfungsi dgn aksesori USB ini. Ketahui lg ttg aksesori ini di <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Aksesori USB"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Disahkan"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Ketik Sahkan untuk menyelesaikan"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Disahkan"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Gunakan PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Gunakan corak"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Gunakan kata laluan"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN salah"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Corak salah"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Kata laluan salah"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Terlalu banyak percubaan yang salah.\nCuba lagi dalam masa <xliff:g id="NUMBER">%d</xliff:g> saat."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Sentuh penderia cap jari"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikon cap jari"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Mencari anda…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Pemberitahuan"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Lampu suluh"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Kamera sedang digunakan"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Data mudah alih"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Penggunaan data"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Baki data"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index d136042650ae..c06bb8cace60 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ထည့်သွင်းနည်းများ သတ်မှတ်ခြင်း"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"ခလုတ်ပါဝင်သော ကီးဘုတ်"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> အား ဝင်သုံးရန် <xliff:g id="APPLICATION">%1$s</xliff:g> ကို ခွင့်ပြုပါသလား။"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="APPLICATION">%1$s</xliff:g> အား <xliff:g id="USB_DEVICE">%2$s</xliff:g> ကို သုံးခွင့်ပြုမလား။\nဤအက်ပ်ကို အသံဖမ်းခွင့် ပေးမထားသော်လည်း ၎င်းသည် ဤ USB စက်ပစ္စည်းမှတစ်ဆင့် အသံများကို ဖမ်းယူနိုင်ပါသည်။"</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> အား ဝင်သုံးရန် <xliff:g id="APPLICATION">%1$s</xliff:g> ကို ခွင့်ပြုပါသလား။"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ဆောင်ရွက်ရန် <xliff:g id="APPLICATION">%1$s</xliff:g> ကို ဖွင့်လိုပါသလား။"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ကို သုံးရန် <xliff:g id="APPLICATION">%1$s</xliff:g> ကို ဖွင့်မလား။\nဤအက်ပ်ကို အသံဖမ်းခွင့် ပေးမထားသော်လည်း ၎င်းသည် ဤ USB စက်ပစ္စည်းမှတစ်ဆင့် အသံများကို ဖမ်းယူနိုင်ပါသည်။"</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ဆောင်ရွက်ရန် <xliff:g id="APPLICATION">%1$s</xliff:g> ကို ဖွင့်လိုပါသလား။"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"ဒီUSBပစ္စည်းနှင့်ဘယ်အပ်ပလီကေးရှင်းမှ အလုပ်မလုပ်ပါ။ ပိုမိုသိရန် <xliff:g id="URL">%1$s</xliff:g>တွင် လေ့လာပါ"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USBတွဲဖက်ပစ္စည်းများ"</string>
@@ -56,7 +58,7 @@
<string name="always_use_device" msgid="4015357883336738417">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ချိတ်ဆက်သည့်အခါ <xliff:g id="APPLICATION">%1$s</xliff:g> ကို အမြဲဖွင့်ပါ"</string>
<string name="always_use_accessory" msgid="3257892669444535154">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ချိတ်ဆက်သည့်အခါ <xliff:g id="APPLICATION">%1$s</xliff:g> ကို အမြဲဖွင့်ပါ"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"USB အမှားရှာဖွေပြင်ဆင်ခြင်း ခွင့်ပြုပါမည်လား?"</string>
- <string name="usb_debugging_message" msgid="2220143855912376496">"ဒီကွန်ပျူတာရဲ့ RSA key fingerprint ကတော့:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g> ဖြစ်ပါသည်"</string>
+ <string name="usb_debugging_message" msgid="2220143855912376496">"ဤကွန်ပျူတာ၏ RSA သော့လက်ဗွေ-\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"ဒီကွန်ပျူတာမှ အမြဲခွင့်ပြုရန်"</string>
<string name="usb_debugging_allow" msgid="2272145052073254852">"ခွင့်ပြုရန်"</string>
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB အမှားပြင်ဆင်ခြင်း ခွင့်မပြုပါ"</string>
@@ -108,8 +110,8 @@
<string name="accessibility_phone_button" msgid="6738112589538563574">"ဖုန်း"</string>
<string name="accessibility_voice_assist_button" msgid="487611083884852965">"အသံ အကူအညီ"</string>
<string name="accessibility_unlock_button" msgid="128158454631118828">"သော့ဖွင့်ရန်"</string>
- <string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"လက်ဗွေရာကို စောင့်နေပါသည်"</string>
- <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"လက်ဗွေရာ မသုံးဘဲ ဖွင့်ပါ"</string>
+ <string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"လက်ဗွေကို စောင့်နေသည်"</string>
+ <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"လက်ဗွေ မသုံးဘဲ ဖွင့်ပါ"</string>
<string name="accessibility_scanning_face" msgid="769545173211758586">"မျက်နှာ စကင်ဖတ်နေသည်"</string>
<string name="accessibility_send_smart_reply" msgid="7766727839703044493">"ပို့ရန်"</string>
<string name="accessibility_manage_notification" msgid="2026361503393549753">"အကြောင်းကြားချက်များကို စီမံရန်"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"အတည်ပြုပြီးပြီ"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"အပြီးသတ်ရန်အတွက် \'အတည်ပြုရန်\' ကို တို့ပါ"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"အထောက်အထားစိစစ်ပြီးပြီ"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"ပင်နံပါတ်သုံးရန်"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"ပုံစံကို သုံးရန်"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"စကားဝှက် သုံးရန်"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"ပင်နံပါတ် မှားနေသည်"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"ပုံစံ မှားနေသည်"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"စကားဝှက် မှားနေသည်"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"မှားသည့် အကြိမ် အရေအတွက် အလွန်များသည်။\n<xliff:g id="NUMBER">%d</xliff:g>စက္ကန့်အကြာတွင် ထပ်စမ်းကြည့်ပါ။"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"လက်ဗွေအာရုံခံကိရိယာကို တို့ပါ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"လက်ဗွေ သင်္ကေတ"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"သင့်ကို ရှာဖွေနေသည်…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"အကြောင်းကြားချက်များ"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ဖလက်ရှ်မီး"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"ကင်မရာကို သုံးနေသည်"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"မိုဘိုင်းဒေတာ"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"ဒေတာ သုံးစွဲမှု"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"ကျန်ရှိ ဒေတာ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 86f0b3804019..c213b1026836 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Konfigurer inndatametoder"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fysisk tastatur"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Vil du gi <xliff:g id="APPLICATION">%1$s</xliff:g> tilgang til <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Vil du gi <xliff:g id="APPLICATION">%1$s</xliff:g> tilgang til <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nDenne appen har ikke fått tillatelse til å spille inn, men kan ta opp lyd med denne USB-enheten."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Vil du gi <xliff:g id="APPLICATION">%1$s</xliff:g> tilgang til <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Vil du åpne <xliff:g id="APPLICATION">%1$s</xliff:g> for å behandle <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Vil du åpne <xliff:g id="APPLICATION">%1$s</xliff:g> for å håndtere <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nDenne appen har ikke fått tillatelse til å spille inn, men kan ta opp lyd med denne USB-enheten."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Vil du åpne <xliff:g id="APPLICATION">%1$s</xliff:g> for å behandle <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Ingen installerte apper støtter dette USB-tilbehøret. Les mer om tilbehøret på <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB-enhet"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Bekreftet"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Trykk på Bekreft for å fullføre"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autentisert"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Bruk PIN-kode"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Bruk mønster"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Bruk passord"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Feil PIN-kode"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Feil mønster"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Feil passord"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"For mange ugyldige forsøk.\nPrøv på nytt om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Trykk på fingeravtrykkssensoren"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikon for fingeravtrykk"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Ser etter deg …"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Varsler"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Lommelykt"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Kameraet er i bruk"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobildata"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Databruk"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Gjenværende data"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 4c17904bac50..8006279f7ebd 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -47,8 +47,11 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"इनपुट विधिहरू सेटअप गर्नुहोस्"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"वास्तविक किबोर्ड"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="APPLICATION">%1$s</xliff:g> लाई <xliff:g id="USB_DEVICE">%2$s</xliff:g> माथि पहुँच राख्ने अनुमति दिने हो?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="APPLICATION">%1$s</xliff:g> लाई <xliff:g id="USB_DEVICE">%2$s</xliff:g> माथि पहुँच राख्न अनुमति दिने हो?\nयो अनुप्रयोगलाई रेकर्ड गर्ने अनुमति प्रदान गरिएको छैन तर यसले USB यन्त्रमार्फत अडियो क्याप्चर गर्न सक्छ।"</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g> लाई <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> माथि पहुँच राख्ने अनुमति दिने हो?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> को व्यवस्थापन गर्न <xliff:g id="APPLICATION">%1$s</xliff:g> खोल्ने हो?"</string>
+ <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) -->
+ <skip />
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> को व्यवस्थापन गर्न <xliff:g id="APPLICATION">%1$s</xliff:g> खोल्ने हो?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"यस USB उपकरणसँग स्थापित अनुप्रयोग काम गर्दैन। यस उपकरणको बारेमा <xliff:g id="URL">%1$s</xliff:g> मा धेरै जान्नुहोस्"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB सहयोगी"</string>
@@ -56,7 +59,7 @@
<string name="always_use_device" msgid="4015357883336738417">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> जडान भएको बेला सधैँ <xliff:g id="APPLICATION">%1$s</xliff:g> खोल्नुहोस्"</string>
<string name="always_use_accessory" msgid="3257892669444535154">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> जडान भएको बेला सधैँ <xliff:g id="APPLICATION">%1$s</xliff:g> खोल्नुहोस्"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"USB डिबग गर्नको लागि अनुमति दिने हो?"</string>
- <string name="usb_debugging_message" msgid="2220143855912376496">"कम्प्युटरको RSA कुञ्जी औंलाछाप:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
+ <string name="usb_debugging_message" msgid="2220143855912376496">"कम्प्युटरको RSA कुञ्जीको फिंगरप्रिन्ट:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"यो कम्प्युटरबाट सधैँ अनुमति दिनुहोस्"</string>
<string name="usb_debugging_allow" msgid="2272145052073254852">"अनुमति दिनुहोस्"</string>
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB डिबग गर्न अनुमति छैन"</string>
@@ -109,7 +112,7 @@
<string name="accessibility_voice_assist_button" msgid="487611083884852965">"आवाज सहायता"</string>
<string name="accessibility_unlock_button" msgid="128158454631118828">"खोल्नुहोस्"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"फिंगरप्रिन्ट कुर्दै"</string>
- <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"तपाईँको फिंगरप्रिन्ट बिना नै अनलक गर्नुहोस्"</string>
+ <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"आफ्नो फिंगरप्रिन्ट बिना नै अनलक गर्नुहोस्"</string>
<string name="accessibility_scanning_face" msgid="769545173211758586">"अनुहार स्क्यान गर्दै"</string>
<string name="accessibility_send_smart_reply" msgid="7766727839703044493">"पठाउनुहोस्"</string>
<string name="accessibility_manage_notification" msgid="2026361503393549753">"सूचनाहरू व्यवस्थित गर्नुहोस्"</string>
@@ -126,6 +129,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"पुष्टि भयो"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"पूरा गर्नका लागि पुष्टि गर्नुहोस् नामक विकल्पमा ट्याप गर्नुहोस्"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"प्रमाणीकरण गरियो"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"PIN प्रयोग गर्नुहोस्"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"ढाँचा प्रयोग गर्नुहोस्"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"पासवर्ड प्रयोग गर्नुहोस्"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN मिलेन"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"ढाँचा मिलेन"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"पासवर्ड मिलेन"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"अत्यन्तै धेरै पटक गलत प्रयास गरिए। \n <xliff:g id="NUMBER">%d</xliff:g>सेकेन्ड पछि पुनः प्रयास गर्नुहोस्।"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"फिंगरप्रिन्ट सेन्सरमा छुनुहोस्‌"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"फिंगरप्रिन्ट जनाउने आइकन"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"तपाईंलाई खोज्दै…"</string>
@@ -364,6 +374,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"अधिसूचनाहरू"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"फ्ल्यासलाइट"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"क्यामेरा प्रयोगमा छ"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"मोबाइल डेटा"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"डेटाको प्रयोग"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"बाँकी डेटा"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index c62c579b47b7..61e4c33ebedb 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Invoermethoden instellen"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fysiek toetsenbord"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="APPLICATION">%1$s</xliff:g> toegang geven tot <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="APPLICATION">%1$s</xliff:g> toegang geven tot <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nDeze app heeft geen opnamerechten gekregen, maar zou audio kunnen vastleggen via dit USB-apparaat."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g> toegang geven tot <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="APPLICATION">%1$s</xliff:g> openen om <xliff:g id="USB_DEVICE">%2$s</xliff:g> te verwerken?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="APPLICATION">%1$s</xliff:g> openen om <xliff:g id="USB_DEVICE">%2$s</xliff:g> te verwerken?\nDeze app heeft geen opnamerechten gekregen, maar zou audio kunnen vastleggen via dit USB-apparaat."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="APPLICATION">%1$s</xliff:g> openen om <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> te verwerken?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Er werken geen geïnstalleerde apps met dit USB-accessoire. Meer informatie op: <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB-accessoire"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Bevestigd"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Tik op Bevestigen om te voltooien"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Geverifieerd"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Pincode gebruiken"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Patroon gebruiken"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Wachtwoord gebruiken"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Onjuiste pincode"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Onjuist patroon"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Onjuist wachtwoord"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Te veel onjuiste pogingen.\nProbeer het over <xliff:g id="NUMBER">%d</xliff:g> seconden opnieuw."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Raak de vingerafdruksensor aan"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Vingerafdrukpictogram"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Jouw gezicht zoeken…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Meldingen"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Zaklamp"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Camera in gebruik"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobiele data"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Datagebruik"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Resterende gegevens"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index d29592541dc1..527d69f41150 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -47,8 +47,11 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ଇନପୁଟ୍‍ ପଦ୍ଧତି ସେଟ୍‍ କରନ୍ତୁ"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"ଫିଜିକଲ୍ କୀ’ବୋର୍ଡ୍"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ଆକ୍ସେସ୍‍ କରିବାକୁ <xliff:g id="APPLICATION">%1$s</xliff:g>କୁ ଅନୁମତି ଦେବେ?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ଆକ୍ସେସ୍ କରିବାକୁ <xliff:g id="APPLICATION">%1$s</xliff:g>କୁ ଅନୁମତି ଦେବେ କି?\nଏହି ଆପ୍‌କୁ ରେକର୍ଡ କରିବାକୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ କିନ୍ତୁ ଏହି USB ଡିଭାଇସ୍ ଜରିଆରେ ଅଡିଓ କ୍ୟାପ୍ଟର୍ କରିପାରିବ।"</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ଆକ୍ସେସ୍‍ କରିବାକୁ <xliff:g id="APPLICATION">%1$s</xliff:g>କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ନିୟନ୍ତ୍ରଣ କରିବାକୁ <xliff:g id="APPLICATION">%1$s</xliff:g> ଖୋଲିବେ?"</string>
+ <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) -->
+ <skip />
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ନିୟନ୍ତ୍ରଣ କରିବାକୁ <xliff:g id="APPLICATION">%1$s</xliff:g> ଖୋଲିବେ?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"ଇନଷ୍ଟଲ୍‍ ହୋଇଥିବା କୌଣସି ଆପ୍‍ ଏହି USB ଆକ୍ସେସୋରୀରେ କାମ କରେନାହିଁ। ଏହି ଆକ୍ସେସୋରୀ ବିଷୟରେ <xliff:g id="URL">%1$s</xliff:g>ରେ ଅଧିକ ଜାଣନ୍ତୁ"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB ଆକ୍ସେସରୀ"</string>
@@ -56,7 +59,7 @@
<string name="always_use_device" msgid="4015357883336738417">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> କନେକ୍ଟ ଥିବାବେଳେ <xliff:g id="APPLICATION">%1$s</xliff:g> ସର୍ବଦା ଖୋଲନ୍ତୁ"</string>
<string name="always_use_accessory" msgid="3257892669444535154">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> କନେକ୍ଟ ଥିବାବେଳେ <xliff:g id="APPLICATION">%1$s</xliff:g> ସର୍ବଦା ଖୋଲନ୍ତୁ"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"USB ଡିବଗିଙ୍ଗ କରିବେ?"</string>
- <string name="usb_debugging_message" msgid="2220143855912376496">"କମ୍ପ୍ୟୁଟର୍‌ର RSA କୀ\' ଆଙ୍ଗୁଠି ଚିହ୍ନ ହେଉଛି:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
+ <string name="usb_debugging_message" msgid="2220143855912376496">"କମ୍ପ୍ୟୁଟର୍‌ର RSA କୀ ଫିଙ୍ଗରପ୍ରିଣ୍ଟ୍ ହେଉଛି:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"ସବୁବେଳେ ଏହି କମ୍ପ୍ୟୁଟର୍‌ରୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
<string name="usb_debugging_allow" msgid="2272145052073254852">"ଅନୁମତି ଦିଅନ୍ତୁ"</string>
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USBରେ ଡିବଗ୍‍ କରାଯାଇପାରିବ ନାହିଁ"</string>
@@ -108,8 +111,8 @@
<string name="accessibility_phone_button" msgid="6738112589538563574">"ଫୋନ୍‍"</string>
<string name="accessibility_voice_assist_button" msgid="487611083884852965">"ଭଏସ୍‌ ସହାୟକ"</string>
<string name="accessibility_unlock_button" msgid="128158454631118828">"ଅନଲକ୍‌ କରନ୍ତୁ"</string>
- <string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ପାଇଁ ଅପେକ୍ଷା କରିଛି"</string>
- <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"ଆଙ୍ଗୁଠିଚିହ୍ନ ବ୍ୟବହାର ନକରି ଅନଲକ୍‍ କରନ୍ତୁ"</string>
+ <string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"ଟିପଚିହ୍ନ ପାଇଁ ଅପେକ୍ଷା କରୁଛି"</string>
+ <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"ଆପଣଙ୍କ ଟିପଚିହ୍ନ ବ୍ୟବହାର ନକରି ଅନଲକ୍‍ କରନ୍ତୁ"</string>
<string name="accessibility_scanning_face" msgid="769545173211758586">"ଫେସ୍ ସ୍କାନିଙ୍ଗ କରାଯାଉଛି"</string>
<string name="accessibility_send_smart_reply" msgid="7766727839703044493">"ପଠାନ୍ତୁ"</string>
<string name="accessibility_manage_notification" msgid="2026361503393549753">"ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ପରିଚାଳନା କରନ୍ତୁ"</string>
@@ -126,8 +129,15 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"ସୁନିଶ୍ଚିତ କରାଯାଇଛି"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"ସମ୍ପୂର୍ଣ୍ଣ କରିବାକୁ ସୁନିଶ୍ଚିତ କରନ୍ତୁରେ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"ପ୍ରାମାଣିକତା ହୋଇଛି"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"PIN ବ୍ୟବହାର କରନ୍ତୁ"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"ପାଟର୍ନ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"ପାସ୍‌ୱାର୍ଡ୍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"ଭୁଲ PIN"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"ଭୁଲ ପାଟର୍ନ"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"ଭୁଲ ପାସ୍‌ୱାର୍ଡ"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"ଅନେକ ଥର ଭୁଲ ଚେଷ୍ଟା। \n <xliff:g id="NUMBER">%d</xliff:g>ସେକେଣ୍ଡରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ଟିପଚିହ୍ନ ସେନସର୍‌କୁ ଛୁଅଁନ୍ତୁ"</string>
- <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ଆଇକନ୍"</string>
+ <string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ଟିପଚିହ୍ନ ଆଇକନ୍"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"ଆପଣଙ୍କୁ ଚିହ୍ନଟ କରୁଛି…"</string>
<string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"ମୁହଁ ଆଇକନ୍"</string>
<string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"କମ୍ପାଟିବିଲିଟୀ ଜୁମ୍ ବଟନ୍।"</string>
@@ -364,6 +374,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"ବିଜ୍ଞପ୍ତି"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ଫ୍ଲାସ୍‍ଲାଇଟ୍"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"କ୍ୟାମେରା ବ୍ୟବହାରରେ ଅଛି"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"ମୋବାଇଲ୍‌ ଡାଟା"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"ଡାଟାର ବ୍ୟବହାର"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"ଅବଶିଷ୍ଟ ଡାଟା"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index b75deb96d768..18ddddf14ce5 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -47,8 +47,11 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ਇਨਪੁਟ ਵਿਧੀਆਂ ਸੈਟ ਅਪ ਕਰੋ"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"ਫਿਜੀਕਲ ਕੀ-ਬੋਰਡ"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"ਕੀ <xliff:g id="USB_DEVICE">%2$s</xliff:g> ਤੱਕ <xliff:g id="APPLICATION">%1$s</xliff:g> ਨੂੰ ਪਹੁੰਚ ਕਰਨ ਦੇਣੀ ਹੈ?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"ਕੀ <xliff:g id="APPLICATION">%1$s</xliff:g> ਨੂੰ <xliff:g id="USB_DEVICE">%2$s</xliff:g> ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੇਣੀ ਹੈ?\nਇਸ ਐਪ ਨੂੰ ਰਿਕਾਰਡ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੱਤੀ ਗਈ ਪਰ ਇਹ USB ਡੀਵਾਈਸ ਰਾਹੀਂ ਆਡੀਓ ਕੈਪਚਰ ਕਰ ਸਕਦੀ ਹੈ।"</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"ਕੀ <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ਤੱਕ <xliff:g id="APPLICATION">%1$s</xliff:g> ਨੂੰ ਪਹੁੰਚ ਕਰਨ ਦੇਣੀ ਹੈ?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"ਕੀ <xliff:g id="USB_DEVICE">%2$s</xliff:g> ਨੂੰ ਵਰਤਣ ਲਈ <xliff:g id="APPLICATION">%1$s</xliff:g> ਖੋਲ੍ਹਣੀ ਹੈ?"</string>
+ <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) -->
+ <skip />
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"ਕੀ <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ਨੂੰ ਵਰਤਣ ਲਈ <xliff:g id="APPLICATION">%1$s</xliff:g> ਖੋਲ੍ਹਣੀ ਹੈ?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"ਕੋਈ ਇੰਸਟੌਲ ਕੀਤੇ ਐਪਸ ਇਸ USB ਐਕਸੈਸਰੀ ਨਾਲ ਕੰਮ ਨਹੀਂ ਕਰਦੇ। <xliff:g id="URL">%1$s</xliff:g> ਤੇ ਇਸ ਐਕਸੈਸਰੀ ਬਾਰੇ ਹੋਰ ਜਾਣੋ"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB ਐਕਸੈਸਰੀ"</string>
@@ -126,6 +129,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"ਪੁਸ਼ਟੀ ਕੀਤੀ ਗਈ"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"ਪੂਰਾ ਕਰਨ ਲਈ ਪੁਸ਼ਟੀ ਕਰੋ \'ਤੇ ਟੈਪ ਕਰੋ"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"ਪ੍ਰਮਾਣਿਤ ਹੋਇਆ"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"ਪਿੰਨ ਵਰਤੋ"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"ਪੈਟਰਨ ਵਰਤੋ"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"ਪਾਸਵਰਡ ਵਰਤੋ"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"ਗਲਤ ਪਿੰਨ"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"ਗਲਤ ਪੈਟਰਨ"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"ਗਲਤ ਪਾਸਵਰਡ"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"ਬਹੁਤ ਸਾਰੀਆਂ ਗ਼ਲਤ ਕੋਸ਼ਿਸ਼ਾਂ।\n<xliff:g id="NUMBER">%d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨੂੰ ਸਪੱਰਸ਼ ਕਰੋ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਪ੍ਰਤੀਕ"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"ਤੁਹਾਡੀ ਪਛਾਣ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ…"</string>
@@ -364,6 +374,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"ਸੂਚਨਾਵਾਂ"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ਫਲੈਸ਼ਲਾਈਟ"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"ਕੈਮਰਾ ਵਰਤੋਂ ਵਿੱਚ ਹੈ"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"ਮੋਬਾਈਲ ਡਾਟਾ"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"ਡਾਟਾ ਵਰਤੋਂ"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"ਬਾਕੀ ਡਾਟਾ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index d372b3d2e2c0..44b41128d61d 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Konfiguruj metody wprowadzania"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Klawiatura fizyczna"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Zezwolić aplikacji <xliff:g id="APPLICATION">%1$s</xliff:g> na dostęp do urządzenia <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Zezwolić aplikacji <xliff:g id="APPLICATION">%1$s</xliff:g> na dostęp do urządzenia <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nTa aplikacja nie ma uprawnień do nagrywania, ale może rejestrować dźwięk za pomocą tego urządzenia USB."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Zezwolić aplikacji <xliff:g id="APPLICATION">%1$s</xliff:g> na dostęp do urządzenia <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Otworzyć aplikację <xliff:g id="APPLICATION">%1$s</xliff:g> do obsługi urządzenia <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Zezwolić aplikacji <xliff:g id="APPLICATION">%1$s</xliff:g> na obsługę urządzenia <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nTa aplikacja nie ma uprawnień do nagrywania, ale może rejestrować dźwięk za pomocą tego urządzenia USB."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Otworzyć aplikację <xliff:g id="APPLICATION">%1$s</xliff:g> do obsługi urządzenia <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Zainstalowane aplikacje nie działają z tym akcesorium USB. Więcej informacji: <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Akcesorium USB"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Potwierdzono"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Aby zakończyć, kliknij Potwierdź"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Uwierzytelniono"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Użyj kodu PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Użyj wzoru"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Użyj hasła"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Nieprawidłowy kod PIN"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Nieprawidłowy wzór"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Nieprawidłowe hasło"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Zbyt wiele nieudanych prób.\n Spróbuj ponownie za <xliff:g id="NUMBER">%d</xliff:g> s."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Dotknij czytnika linii papilarnych"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona odcisku palca"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Szukam Cię…"</string>
@@ -370,6 +379,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Powiadomienia"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Latarka"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Aparat w użyciu"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobilna transmisja danych"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Użycie danych"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Pozostały limit"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 2b761577d17d..57d64f53504e 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configurar métodos de entrada"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Teclado físico"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Permitir que o app <xliff:g id="APPLICATION">%1$s</xliff:g> acesse <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Permitir que <xliff:g id="APPLICATION">%1$s</xliff:g> acesse <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nEsse app não tem permissão de gravação, mas pode capturar áudio pelo dispositivo USB."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Permitir que o app <xliff:g id="APPLICATION">%1$s</xliff:g> acesse <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Abrir o app <xliff:g id="APPLICATION">%1$s</xliff:g> para lidar com o <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Abrir o app <xliff:g id="APPLICATION">%1$s</xliff:g> para usar o <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nEsse app não tem permissão de gravação, mas pode capturar áudio pelo dispositivo USB."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Abrir o app <xliff:g id="APPLICATION">%1$s</xliff:g> para lidar com o <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Nenhum apl. instalado funciona com o USB. Saiba mais sobre o acessório em <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Acessório USB"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmada"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Toque em \"Confirmar\" para concluir"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autenticado"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Usar PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Usar padrão"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Usar senha"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN incorreto"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Padrão incorreto"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Senha incorreta"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Excesso de tentativas incorretas.\nTente novamente em <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Toque no sensor de impressão digital"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ícone de impressão digital"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Procurando você…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificações"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Lanterna"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Câmera em uso"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Dados móveis"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Uso de dados"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Dados restantes"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 17cc86f1007e..6c8021182833 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configurar métodos introdução"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Teclado físico"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Pretende permitir que a aplicação <xliff:g id="APPLICATION">%1$s</xliff:g> aceda ao dispositivo <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Pretende permitir que <xliff:g id="APPLICATION">%1$s</xliff:g> aceda a <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nEsta aplicação não recebeu autorização de gravação, mas pode capturar áudio através deste dispositivo USB."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Pretende permitir que a aplicação <xliff:g id="APPLICATION">%1$s</xliff:g> aceda ao acessório <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Pretende abrir a aplicação <xliff:g id="APPLICATION">%1$s</xliff:g> para controlar o acessório <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Pretende abrir a aplicação <xliff:g id="APPLICATION">%1$s</xliff:g> para controlar o acessório <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nEsta aplicação não recebeu autorização de gravação, mas pode capturar áudio através deste dispositivo USB."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Pretende abrir a aplicação <xliff:g id="APPLICATION">%1$s</xliff:g> para controlar o acessório <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Nenhuma das aplicações instaladas funciona com o acessório USB. Saiba mais acerca do acessório em <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Acessório USB"</string>
@@ -68,7 +70,7 @@
<string name="learn_more" msgid="5000517160980853569">"Saiba mais"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom para preencher o ecrã"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Esticar p. caber em ec. int."</string>
- <string name="global_action_screenshot" msgid="8329831278085426283">"Captura de ecrã"</string>
+ <string name="global_action_screenshot" msgid="8329831278085426283">"Capt. ecrã"</string>
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"A guardar captura de ecrã..."</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"A guardar captura de ecrã..."</string>
<string name="screenshot_saved_title" msgid="5637073968117370753">"Captura de ecrã guardada"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmado"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Toque em Confirmar para concluir."</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autenticado"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Utilizar PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Utilizar padrão"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Utilizar palavra-passe"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN incorreto."</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Padrão incorreto."</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Palavra-passe incorreta."</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Demasiadas tentativas incorretas.\nTente novamente dentro de <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Toque no sensor de impressões digitais."</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ícone de impressão digital"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"À sua procura…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificações"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Lanterna"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Câmara em utilização"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Dados móveis"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Utilização de dados"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Dados restantes"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 2b761577d17d..57d64f53504e 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Configurar métodos de entrada"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Teclado físico"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Permitir que o app <xliff:g id="APPLICATION">%1$s</xliff:g> acesse <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Permitir que <xliff:g id="APPLICATION">%1$s</xliff:g> acesse <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nEsse app não tem permissão de gravação, mas pode capturar áudio pelo dispositivo USB."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Permitir que o app <xliff:g id="APPLICATION">%1$s</xliff:g> acesse <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Abrir o app <xliff:g id="APPLICATION">%1$s</xliff:g> para lidar com o <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Abrir o app <xliff:g id="APPLICATION">%1$s</xliff:g> para usar o <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nEsse app não tem permissão de gravação, mas pode capturar áudio pelo dispositivo USB."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Abrir o app <xliff:g id="APPLICATION">%1$s</xliff:g> para lidar com o <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Nenhum apl. instalado funciona com o USB. Saiba mais sobre o acessório em <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Acessório USB"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmada"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Toque em \"Confirmar\" para concluir"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autenticado"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Usar PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Usar padrão"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Usar senha"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN incorreto"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Padrão incorreto"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Senha incorreta"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Excesso de tentativas incorretas.\nTente novamente em <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Toque no sensor de impressão digital"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ícone de impressão digital"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Procurando você…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificações"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Lanterna"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Câmera em uso"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Dados móveis"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Uso de dados"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Dados restantes"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index db8f87debaec..a6bd29bbec58 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Setați metode introducere text"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Tastatură fizică"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Permiteți <xliff:g id="APPLICATION">%1$s</xliff:g> să acceseze <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Permiteți accesul aplicației <xliff:g id="APPLICATION">%1$s</xliff:g> la <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nPermisiunea de înregistrare nu a fost acordată aplicației, dar aceasta poate să înregistreze conținut audio prin intermediul acestui dispozitiv USB."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Permiteți <xliff:g id="APPLICATION">%1$s</xliff:g> să acceseze <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Deschideți <xliff:g id="APPLICATION">%1$s</xliff:g> ca să gestioneze <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Deschideți <xliff:g id="APPLICATION">%1$s</xliff:g> pentru a gestiona <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nPermisiunea de înregistrare nu a fost acordată aplicației, dar aceasta poate să înregistreze conținut audio prin intermediul acestui dispozitiv USB."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Deschideți <xliff:g id="APPLICATION">%1$s</xliff:g> ca să gestioneze <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Aplic. instal. nu funcț. cu acest acces. USB. Aflați despre acest accesoriu la <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Accesoriu USB"</string>
@@ -56,7 +58,7 @@
<string name="always_use_device" msgid="4015357883336738417">"Deschideți întotdeauna <xliff:g id="APPLICATION">%1$s</xliff:g> când este conectat <xliff:g id="USB_DEVICE">%2$s</xliff:g>"</string>
<string name="always_use_accessory" msgid="3257892669444535154">"Deschideți întotdeauna <xliff:g id="APPLICATION">%1$s</xliff:g> când este conectat <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"Permiteți remedierea erorilor prin USB?"</string>
- <string name="usb_debugging_message" msgid="2220143855912376496">"Amprenta digitală din cheia RSA a computerului este:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
+ <string name="usb_debugging_message" msgid="2220143855912376496">"Amprenta din cheia RSA a computerului este:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Permiteți întotdeauna de pe acest computer"</string>
<string name="usb_debugging_allow" msgid="2272145052073254852">"Permiteți"</string>
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Remedierea erorilor prin USB nu este permisă"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Confirmat"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Atingeți Confirmați pentru a finaliza"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autentificat"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Folosiți PIN-ul"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Folosiți modelul"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Folosiți parola"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN greșit"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Model greșit"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Parolă greșită"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Prea multe încercări incorecte.\nÎncercați din nou peste <xliff:g id="NUMBER">%d</xliff:g> secunde."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Atingeți senzorul de amprente"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Pictograma amprentă"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Vă căutăm…"</string>
@@ -366,6 +375,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificări"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Lanternă"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Se folosește camera foto"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Date mobile"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Utilizarea datelor"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Date rămase"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index f7527d9e0cd3..3459440251a1 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Настройка способов ввода"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Физическая клавиатура"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Предоставить приложению \"<xliff:g id="APPLICATION">%1$s</xliff:g>\" доступ к устройству \"<xliff:g id="USB_DEVICE">%2$s</xliff:g>\"?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Предоставить приложению \"<xliff:g id="APPLICATION">%1$s</xliff:g>\" доступ к устройству \"<xliff:g id="USB_DEVICE">%2$s</xliff:g>\"?\nПриложению не разрешено вести запись, однако с помощью этого USB-устройства оно может записывать звук."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Предоставить приложению \"<xliff:g id="APPLICATION">%1$s</xliff:g>\" доступ к устройству \"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>\"?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Открыть приложение \"<xliff:g id="APPLICATION">%1$s</xliff:g>\" для управления устройством \"<xliff:g id="USB_DEVICE">%2$s</xliff:g>\"?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Открыть приложение \"<xliff:g id="APPLICATION">%1$s</xliff:g>\" для использования устройства \"<xliff:g id="USB_DEVICE">%2$s</xliff:g>\"?\nПриложению не разрешено вести запись, однако с помощью этого USB-устройства оно может записывать звук."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Открыть приложение \"<xliff:g id="APPLICATION">%1$s</xliff:g>\" для управления устройством \"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>\"?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Приложения не поддерживают это USB-устройство. Подробнее о нем читайте здесь: <xliff:g id="URL">%1$s</xliff:g>."</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB-устройство"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Подтверждено"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Нажмите \"Подтвердить\""</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Аутентификация выполнена"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Использовать PIN-код"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Использовать графический ключ"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Использовать пароль"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Неверный PIN-код."</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Неверный графический ключ."</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Неверный пароль."</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Слишком много неудачных попыток.\nПовторите через <xliff:g id="NUMBER">%d</xliff:g> сек."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Прикоснитесь к сканеру отпечатков пальцев."</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Значок отпечатка пальца"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Поиск лица…"</string>
@@ -368,6 +377,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Уведомления"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Фонарик"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Используется камера"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Мобильный Интернет"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Передача данных"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Остается данных"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index c87ef5a001b5..ab3dc222b8ef 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ආදාන ක්‍රම සකසන්න"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"භෞතික යතුරු පුවරුව"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="APPLICATION">%1$s</xliff:g> හට <xliff:g id="USB_DEVICE">%2$s</xliff:g> වෙත පිවිසීමට ඉඩ දෙන්නද?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="APPLICATION">%1$s</xliff:g> වෙත ප්‍රවේශ වීමට <xliff:g id="USB_DEVICE">%2$s</xliff:g> හට ඉඩ දෙන්නද?\n මෙම යෙදුමට පටිගත කිරීම් අවසරයක් ලබා දී නොමැති නමුත් මෙම USB උපාංගය හරහා ශ්‍රව්‍ය ග්‍රහණය කර ගත හැකිය."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g> හට <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> වෙත පිවිසීමට ඉඩ දෙන්නද?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> හැසිරවීමට <xliff:g id="APPLICATION">%1$s</xliff:g> විවෘත කරන්නද?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="APPLICATION">%1$s</xliff:g> <xliff:g id="USB_DEVICE">%2$s</xliff:g> හැසිරවීමට විවෘත කරන්නද?\n මෙම යෙදුමට පටිගත කිරීම් අවසරයක් ලබා දී නොමැති නමුත් මෙම USB උපාංගය හරහා ශ්‍රව්‍ය ග්‍රහණය කර ගත හැකිය."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> හැසිරවීමට <xliff:g id="APPLICATION">%1$s</xliff:g> විවෘත කරන්නද?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"මෙම USB මෙවලම සමග ක්‍රියා කරන ස්ථාපිත යෙදුම් නොමැත. <xliff:g id="URL">%1$s</xliff:g> වලින් මෙම මෙවලම ගැන තව දැනගන්න"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB මෙවලම"</string>
@@ -56,7 +58,7 @@
<string name="always_use_device" msgid="4015357883336738417">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> සම්බන්ධ විට <xliff:g id="APPLICATION">%1$s</xliff:g> සැම විටම විවෘත කරන්න"</string>
<string name="always_use_accessory" msgid="3257892669444535154">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> සම්බන්ධ විට <xliff:g id="APPLICATION">%1$s</xliff:g> සැම විටම විවෘත කරන්න"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"USB නිදොස්කරණයට අවසර දෙනවද?"</string>
- <string name="usb_debugging_message" msgid="2220143855912376496">"මෙම පරිගණකයේ RSA යතුරු ඇඟිලි සටහන වන්නේ:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
+ <string name="usb_debugging_message" msgid="2220143855912376496">"මෙම පරිගණකයේ RSA යතුරු ඇඟිලි සලකුණ සටහන වන්නේ:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"සැම විටම මෙම පරිගණකයෙන් ඉඩ ලබා දෙන්න"</string>
<string name="usb_debugging_allow" msgid="2272145052073254852">"ඉඩ දෙන්න"</string>
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB නිදොස්කරණය වෙත අවසර නැහැ"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"තහවුරු කළා"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"සම්පූර්ණ කිරීමට තහවුරු කරන්න තට්ටු කර."</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"සත්‍යාපනය විය"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"PIN භාවිත කරන්න"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"රටාව භාවිත කරන්න"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"මුරපදය භාවිත කරන්න"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN එක වැරදියි"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"වැරදි රටාවකි"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"වැරදි මුරපදයකි"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"වැරදි උත්සාහයන් ගණන වැඩියි. තත්පර \n තත්පර <xliff:g id="NUMBER">%d</xliff:g>කින් නැවත උත්සාහ කරන්න."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"ඇඟිලි සලකුණු සංවේදකය ස්පර්ශ කරන්න"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ඇඟිලි සලකුණු නිරූපකය"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"ඔබව සොයමින්…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"දැනුම්දීම්"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"සැණෙළි ආලෝකය"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"කැමරාව භාවිතයේ ඇත"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"ජංගම දත්ත"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"දත්ත භාවිතය"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"ඉතිරි ඇති දත්ත"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 1e171a1ab055..aa004b4e2f8d 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Nastavenie metód vstupu"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fyzická klávesnica"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Povoliť aplikácii <xliff:g id="APPLICATION">%1$s</xliff:g> prístup k zariadeniu <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Povoliť aplikácii <xliff:g id="APPLICATION">%1$s</xliff:g> pristupovať k zariadeniu <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nTejto aplikácii nebolo udelené povolenie na nahrávanie, môže však snímať zvuk cez toto zariadenie USB."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Povoliť aplikácii <xliff:g id="APPLICATION">%1$s</xliff:g> prístup k zariadeniu <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Otvoriť aplikáciu <xliff:g id="APPLICATION">%1$s</xliff:g> na použitie zariadenia <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Chcete spracovávať zariadenie <xliff:g id="USB_DEVICE">%2$s</xliff:g> pomocou aplikácie <xliff:g id="APPLICATION">%1$s</xliff:g>?\nTejto aplikácii nebolo udelené povolenie na nahrávanie, ale môže nasnímať zvuk cez toto zariadenie USB."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Otvoriť aplikáciu <xliff:g id="APPLICATION">%1$s</xliff:g> na použitie zariadenia <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"S týmto zariad. USB nefunguje žiadna nainštal. aplikácia. Ďalšie informácie na <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Periférne zariadenie USB"</string>
@@ -109,7 +111,7 @@
<string name="accessibility_voice_assist_button" msgid="487611083884852965">"Hlasový asistent"</string>
<string name="accessibility_unlock_button" msgid="128158454631118828">"Odomknúť"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"Čaká sa na odtlačok prsta"</string>
- <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"Odomknúť bez použitia odtlačku"</string>
+ <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"Odomknúť bez použitia odtlačku prsta"</string>
<string name="accessibility_scanning_face" msgid="769545173211758586">"Skenovanie tváre"</string>
<string name="accessibility_send_smart_reply" msgid="7766727839703044493">"Odoslať"</string>
<string name="accessibility_manage_notification" msgid="2026361503393549753">"Spravovať upozornenia"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Potvrdené"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Overenie dokončíte klepnutím na Potvrdiť"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Overené"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Použiť PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Použiť vzor"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Použiť heslo"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Nesprávny PIN"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Nesprávny vzor"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Nesprávne heslo"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Príliš veľa nesprávnych pokusov. \nSkúste to znova o <xliff:g id="NUMBER">%d</xliff:g> s."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Dotknite sa senzora odtlačkov prstov"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona odtlačku prsta"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Hľadáme vás…"</string>
@@ -368,6 +377,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Upozornenia"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Baterka"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Fotoaparát sa používa"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobilné dáta"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Spotreba dát"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Zostávajúce údaje"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index b342ce6c0d32..03fa75142c29 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Nastavi načine vnosa"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fizična tipkovnica"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Ali aplikaciji <xliff:g id="APPLICATION">%1$s</xliff:g> dovolite dostop do dodatka USB <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Ali aplikaciji <xliff:g id="APPLICATION">%1$s</xliff:g> dovolite dostop do naprave <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nTa aplikacija sicer nima dovoljenja za snemanje, vendar bi lahko zajemala zvok prek te naprave USB."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Ali aplikaciji <xliff:g id="APPLICATION">%1$s</xliff:g> dovolite dostop do dodatka USB <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Želite odpreti aplikacijo <xliff:g id="APPLICATION">%1$s</xliff:g> za upravljanje dodatka USB <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Želite odpreti aplikacijo <xliff:g id="APPLICATION">%1$s</xliff:g> za upravljanje naprave <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nTa aplikacija sicer nima dovoljenja za snemanje, vendar bi lahko zajemala zvok prek te naprave USB."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Želite odpreti aplikacijo <xliff:g id="APPLICATION">%1$s</xliff:g> za upravljanje dodatka USB <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Namešč. prog. ne delujejo s tem dodatkom USB. Več o tem dodatku preberite na <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Dodatek USB"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Potrjeno"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Za dokončanje se dotaknite »Potrdite«"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Preverjena pristnost"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Uporabi kodo PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Uporabi vzorec"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Uporabi geslo"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Napačna koda PIN"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Napačen vzorec"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Napačno geslo"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Preveč napačnih poskusov.\nPoskusite znova čez <xliff:g id="NUMBER">%d</xliff:g> s."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Dotaknite se tipala prstnih odtisov"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona prstnih odtisov"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Preverjanje vašega obraza …"</string>
@@ -368,6 +377,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Obvestila"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Svetilka"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Fotoaparat je v uporabi"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Prenos podatkov v mobilnem omrežju"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Poraba podatkov"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Preostala količina podatkov"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 4fba77052876..21915c9ce677 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Konfiguro metodat e hyrjes"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Tastierë fizike"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Të lejohet <xliff:g id="APPLICATION">%1$s</xliff:g> të ketë qasje te <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Dëshiron të lejosh që <xliff:g id="APPLICATION">%1$s</xliff:g> të ketë qasje te <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nKëtij aplikacioni nuk i është dhënë leje për regjistrim, por mund të regjistrojë audio përmes kësaj pajisjeje USB."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Të lejohet <xliff:g id="APPLICATION">%1$s</xliff:g> të ketë qasje te <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Të hapet <xliff:g id="APPLICATION">%1$s</xliff:g> për të përdorur <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Dëshiron të hapësh <xliff:g id="APPLICATION">%1$s</xliff:g> që të përpunojë <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nKëtij aplikacioni nuk i është dhënë leje për regjistrim, por mund të regjistrojë audio përmes kësaj pajisjeje USB."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Të hapet <xliff:g id="APPLICATION">%1$s</xliff:g> për të përdorur <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Asnjë aplikacion i instaluar nuk punon me këtë ndihmës USB-je. Mëso më shumë rreth këtij ndihmësi në <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Qasja në USB"</string>
@@ -56,7 +58,7 @@
<string name="always_use_device" msgid="4015357883336738417">"Hap gjithmonë <xliff:g id="APPLICATION">%1$s</xliff:g> kur lidhet <xliff:g id="USB_DEVICE">%2$s</xliff:g>"</string>
<string name="always_use_accessory" msgid="3257892669444535154">"Hap gjithmonë <xliff:g id="APPLICATION">%1$s</xliff:g> kur lidhet <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"Të lejohet korrigjimi i USB-së?"</string>
- <string name="usb_debugging_message" msgid="2220143855912376496">"Shenja e gishtit të tastit \"RSA\" së kompjuterit është:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
+ <string name="usb_debugging_message" msgid="2220143855912376496">"Gjurma e gishtit të tastit \"RSA\" së kompjuterit është:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Lejo gjithmonë nga ky kompjuter"</string>
<string name="usb_debugging_allow" msgid="2272145052073254852">"Lejo"</string>
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Korrigjimi i USB-së nuk lejohet"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Konfirmuar"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Trokit \"Konfirmo\" për ta përfunduar"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"U vërtetua"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Përdor kodin PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Përdor motivin"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Përdor fjalëkalimin"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Kod PIN i gabuar"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Motiv i gabuar"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Fjalëkalim i gabuar"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Shumë tentativa të pasakta.\nProvo përsëri brenda <xliff:g id="NUMBER">%d</xliff:g> sekondash."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Prek sensorin e gjurmës së gishtit"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikona e gjurmës së gishtit"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Po të kërkojmë…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Njoftimet"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Elektriku"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Kamera në përdorim"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Të dhënat celulare"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Përdorimi i të dhënave"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Të dhënat e mbetura"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 310fc6fb7840..64ab59b0135c 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Подеси методе уноса"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Физичка тастатура"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Желите ли да дозволите да <xliff:g id="APPLICATION">%1$s</xliff:g> приступа уређају <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Желите ли да дозволите да <xliff:g id="APPLICATION">%1$s</xliff:g> приступа уређају <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nОва апликација нема дозволу за снимање, али би могла да снима звук помоћу овог USB уређаја."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Желите ли да дозволите да <xliff:g id="APPLICATION">%1$s</xliff:g> приступа уређају <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Желите ли да отворите апликацију <xliff:g id="APPLICATION">%1$s</xliff:g> да бисте користили уређај <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Желите ли да отворите апликацију <xliff:g id="APPLICATION">%1$s</xliff:g> ради руковања уређајем <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nОва апликација нема дозволу за снимање, али би могла да снима звук помоћу овог USB уређаја."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Желите ли да отворите апликацију <xliff:g id="APPLICATION">%1$s</xliff:g> да бисте користили уређај <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Инсталиране апликације не функционишу са овим USB помоћним уређајем. Сазнајте више о њему на адреси <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB помоћни уређај"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Потврђено"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Додирните Потврди да бисте завршили"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Идентитет је потврђен"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Користите PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Користите шаблон"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Користите лозинку"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Погрешан PIN"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Погрешан шаблон"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Погрешна лозинка"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Превише нетачних покушаја.\n Пробајте поново за <xliff:g id="NUMBER">%d</xliff:g> сек."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Додирните сензор за отисак прста"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Икона отиска прста"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Тражимо вас…"</string>
@@ -366,6 +375,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Обавештења"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Лампа"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Користи се камера"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Мобилни подаци"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Потрошња података"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Преостала количина података"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 54769cd291c5..0a65cc792382 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Konfigurera inmatningsmetoder"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fysiskt tangentbord"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Vill du ge <xliff:g id="APPLICATION">%1$s</xliff:g> åtkomst till <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Vill du ge <xliff:g id="APPLICATION">%1$s</xliff:g> åtkomst till <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nAppen har inte fått inspelningsbehörighet men kan spela in ljud via denna USB-enhet."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Vill du ge <xliff:g id="APPLICATION">%1$s</xliff:g> åtkomst till <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Vill du öppna <xliff:g id="APPLICATION">%1$s</xliff:g> och hantera <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Vill du öppna <xliff:g id="APPLICATION">%1$s</xliff:g> för att hantera <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nAppen har inte fått inspelningsbehörighet men kan spela in ljud via denna USB-enhet."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Vill du öppna <xliff:g id="APPLICATION">%1$s</xliff:g> och hantera <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Inga appar fungerar med det här USB-tillbehöret. Läs mer om det på <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB-tillbehör"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Bekräftat"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Slutför genom att trycka på Bekräfta"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Autentiserad"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Använd pinkod"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Använd grafiskt lösenord"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Använd lösenord"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Fel pinkod"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Fel grafiskt lösenord"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Fel lösenord"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"För många felaktiga försök.\nFörsök igen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Tryck på fingeravtryckssensorn"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Ikon för fingeravtryck"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Håller utkik efter dig …"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Aviseringar"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Ficklampa"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Kameran används"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobildata"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Dataanvändning"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Återstående data"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 3f9278a55941..48c1fab43934 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Weka mbinu za ingizo"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Kibodi halisi"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Ungependa kuruhusu <xliff:g id="APPLICATION">%1$s</xliff:g> ifikie <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Ungependa kuruhusu <xliff:g id="APPLICATION">%1$s</xliff:g> ifikie <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nProgramu hii haijapewa ruhusa ya kurekodi lakini inaweza kurekodi sauti kupitia kifaa hiki cha USB."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Ungependa kuruhusu <xliff:g id="APPLICATION">%1$s</xliff:g> ifikie <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Ungependa kufungua <xliff:g id="APPLICATION">%1$s</xliff:g> ili itumie <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Ungependa kufungua <xliff:g id="APPLICATION">%1$s</xliff:g> ishughulikie <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nProgramu hii haijapewa ruhusa ya kurekodi lakini inaweza kurekodi sauti kupitia kifaa hiki cha USB."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Ungependa kufungua <xliff:g id="APPLICATION">%1$s</xliff:g> ili itumie <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Hakuna programu zilizosakinishwa zinazofanya kazi na kifaa hiki cha USB. Pata maelezo zaidi kuhusu kifaa hiki kwenye <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Kifaa cha Usb"</string>
@@ -56,7 +58,7 @@
<string name="always_use_device" msgid="4015357883336738417">"Fungua <xliff:g id="APPLICATION">%1$s</xliff:g> kila wakati <xliff:g id="USB_DEVICE">%2$s</xliff:g> inaunganishwa"</string>
<string name="always_use_accessory" msgid="3257892669444535154">"Fungua <xliff:g id="APPLICATION">%1$s</xliff:g> kila wakati <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> inaunganishwa"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"Ruhusu utatuaji wa USB?"</string>
- <string name="usb_debugging_message" msgid="2220143855912376496">"Alama ya kidole ya kitufe cha RSA ya kompyuta ni:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
+ <string name="usb_debugging_message" msgid="2220143855912376496">"Kitambulisho dijitali cha kifunguo cha RSA cha kompyuta ni:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Ruhusu kutoka kwenye kompyuta hii kila wakati"</string>
<string name="usb_debugging_allow" msgid="2272145052073254852">"Ruhusu"</string>
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Utatuzi wa USB hauruhusiwi"</string>
@@ -109,7 +111,7 @@
<string name="accessibility_voice_assist_button" msgid="487611083884852965">"Mapendekezo ya Sauti"</string>
<string name="accessibility_unlock_button" msgid="128158454631118828">"Fungua"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"Inasubiri alama ya kidole"</string>
- <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"Fungua bila kutumia kitambulisho chako"</string>
+ <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"Fungua bila kutumia alama ya kidole chako"</string>
<string name="accessibility_scanning_face" msgid="769545173211758586">"Inachanganua uso"</string>
<string name="accessibility_send_smart_reply" msgid="7766727839703044493">"Tuma"</string>
<string name="accessibility_manage_notification" msgid="2026361503393549753">"Dhibiti arifa"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Imethibitishwa"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Gusa Thibitisha ili ukamilishe"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Umethibitishwa"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Tumia PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Tumia mchoro"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Tumia nenosiri"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Nambari ya PIN si sahihi"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Mchoro si sahihi"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Nenosiri si sahihi"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Majaribio mengi mno yasiyo sahihi.\nJaribu tena baada ya sekunde <xliff:g id="NUMBER">%d</xliff:g>."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Gusa kitambua alama ya kidole"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Aikoni ya alama ya kidole"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Inakutafuta…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Arifa"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Tochi"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Kamera inatumika"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Data ya simu"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Matumizi ya data"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Data iliyosalia"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 0f368899b086..7263bf642be3 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"உள்ளீட்டு முறைகளை அமை"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"கைமுறை விசைப்பலகை"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="USB_DEVICE">%2$s</xliff:g>ஐ அணுக, <xliff:g id="APPLICATION">%1$s</xliff:g> ஆப்ஸை அனுமதிக்கவா?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="USB_DEVICE">%2$s</xliff:g>ஐப் பயன்படுத்த <xliff:g id="APPLICATION">%1$s</xliff:g>ஐ அனுமதிக்கவா?\nஇந்த ஆப்ஸிற்கு ரெக்கார்டு செய்வதற்கான அனுமதி வழங்கப்படவில்லை, எனினும் இந்த USB சாதனம் மூலம் ஆடியோவைப் பதிவுசெய்யும்."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>ஐ அணுக, <xliff:g id="APPLICATION">%1$s</xliff:g> ஆப்ஸை அனுமதிக்கவா?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g>ஐக் கையாள, <xliff:g id="APPLICATION">%1$s</xliff:g> பயன்பாட்டைத் திறக்கவா?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="USB_DEVICE">%2$s</xliff:g>ஐக் கையாள <xliff:g id="APPLICATION">%1$s</xliff:g>ஐத் திறக்கவா?\nஇந்த ஆப்ஸிற்கு ரெக்கார்டு செய்வதற்கான அனுமதி வழங்கப்படவில்லை, எனினும் இந்த USB சாதனம் மூலம் ஆடியோவைப் பதிவுசெய்ய முடியும்."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>ஐக் கையாள, <xliff:g id="APPLICATION">%1$s</xliff:g> பயன்பாட்டைத் திறக்கவா?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"நிறுவிய ஆப்ஸ் எதுவும், USB துணைக்கருவியுடன் இயங்காது. <xliff:g id="URL">%1$s</xliff:g> இல் துணைக்கருவி குறித்து மேலும் அறிக"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB துணைக்கருவி"</string>
@@ -56,7 +58,7 @@
<string name="always_use_device" msgid="4015357883336738417">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> இணைக்கப்பட்டிருக்கையில், <xliff:g id="APPLICATION">%1$s</xliff:g>ஐ எப்போதும் திற"</string>
<string name="always_use_accessory" msgid="3257892669444535154">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> இணைக்கப்பட்டிருக்கையில், <xliff:g id="APPLICATION">%1$s</xliff:g>ஐ எப்போதும் திற"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"USB பிழைதிருத்தத்தை அனுமதிக்கவா?"</string>
- <string name="usb_debugging_message" msgid="2220143855912376496">"பின்வருவது கணினியின் RSA விசை கைரேகையாகும்:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
+ <string name="usb_debugging_message" msgid="2220143855912376496">"கணினியின் RSA விசை ஃபிங்கர்பிரிண்ட்:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"இந்தக் கணினியிலிருந்து எப்போதும் அனுமதி"</string>
<string name="usb_debugging_allow" msgid="2272145052073254852">"அனுமதி"</string>
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB பிழைதிருத்தம் அனுமதிக்கப்படவில்லை"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"உறுதிப்படுத்தப்பட்டது"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"முடிக்க \'உறுதிப்படுத்து\' என்பதை தட்டவும்"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"அங்கீகரிக்கப்பட்டது"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"பின்னைப் பயன்படுத்து"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"பேட்டர்னைப் பயன்படுத்து"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"கடவுச்சொல்லைப் பயன்படுத்து"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"தவறான பின்"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"தவறான பேட்டர்ன்"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"தவறான கடவுச்சொல்"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"பல தவறான முயற்சிகள்.\n<xliff:g id="NUMBER">%d</xliff:g> வினாடிகளில் மீண்டும் முயலவும்."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"கைரேகை சென்சாரைத் தொடவும்"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"கைரேகை ஐகான்"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"உங்கள் முகத்தைத் தேடுகிறது…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"அறிவிப்புகள்"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"டார்ச் லைட்"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"கேமரா உபயோகத்திலுள்ளது"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"மொபைல் டேட்டா"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"டேட்டா உபயோகம்"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"மீதமுள்ள தரவு"</string>
@@ -498,29 +508,29 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="4683248196789897964">"உங்கள் நிறுவனம், பணிக் கணக்கில் சான்றிதழ் அங்கீகாரத்தை நிறுவியுள்ளது. உங்களின் பாதுகாப்பான நெட்வொர்க் ட்ராஃபிக் கண்காணிக்கப்படலாம் அல்லது மாற்றப்படலாம்."</string>
<string name="monitoring_description_ca_certificate" msgid="7886985418413598352">"இந்தச் சாதனத்தில் சான்றிதழ் அங்கீகாரம் நிறுவப்பட்டுள்ளது. உங்களின் பாதுகாப்பான நெட்வொர்க் ட்ராஃபிக் கண்காணிக்கப்படலாம் அல்லது மாற்றப்படலாம்."</string>
<string name="monitoring_description_management_network_logging" msgid="7184005419733060736">"உங்கள் நிர்வாகி, நெட்வொர்க் பதிவெடுத்தலை இயக்கியுள்ளார். இது சாதனத்தில் ட்ராஃபிக்கைக் கண்காணிக்கும்."</string>
- <string name="monitoring_description_named_vpn" msgid="7403457334088909254">"மின்னஞ்சல்கள், பயன்பாடுகள், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="VPN_APP">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள்."</string>
- <string name="monitoring_description_two_named_vpns" msgid="4198511413729213802">"மின்னஞ்சல்கள், பயன்பாடுகள், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="VPN_APP_0">%1$s</xliff:g> மற்றும் <xliff:g id="VPN_APP_1">%2$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள்."</string>
- <string name="monitoring_description_managed_profile_named_vpn" msgid="1427905889862420559">"மின்னஞ்சல்கள், பயன்பாடுகள், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="VPN_APP">%1$s</xliff:g> உடன் உங்கள் பணிக் கணக்கு இணைக்கப்பட்டுள்ளது."</string>
- <string name="monitoring_description_personal_profile_named_vpn" msgid="3133980926929069283">"மின்னஞ்சல்கள், பயன்பாடுகள், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="VPN_APP">%1$s</xliff:g> உடன் உங்களின் தனிப்பட்ட சுயவிவரம் இணைக்கப்பட்டுள்ளது."</string>
+ <string name="monitoring_description_named_vpn" msgid="7403457334088909254">"மின்னஞ்சல்கள், ஆப்ஸ், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="VPN_APP">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள்."</string>
+ <string name="monitoring_description_two_named_vpns" msgid="4198511413729213802">"மின்னஞ்சல்கள், ஆப்ஸ், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="VPN_APP_0">%1$s</xliff:g> மற்றும் <xliff:g id="VPN_APP_1">%2$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள்."</string>
+ <string name="monitoring_description_managed_profile_named_vpn" msgid="1427905889862420559">"மின்னஞ்சல்கள், ஆப்ஸ், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="VPN_APP">%1$s</xliff:g> உடன் உங்கள் பணிக் கணக்கு இணைக்கப்பட்டுள்ளது."</string>
+ <string name="monitoring_description_personal_profile_named_vpn" msgid="3133980926929069283">"மின்னஞ்சல்கள், ஆப்ஸ், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="VPN_APP">%1$s</xliff:g> உடன் உங்களின் தனிப்பட்ட சுயவிவரம் இணைக்கப்பட்டுள்ளது."</string>
<string name="monitoring_description_do_header_generic" msgid="96588491028288691">"உங்கள் சாதனத்தை நிர்வகிப்பது: <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g>."</string>
<string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"உங்கள் சாதனத்தை நிர்வகிக்க, <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> ஆப்ஸை <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> பயன்படுத்தும்."</string>
<string name="monitoring_description_do_body" msgid="3639594537660975895">"உங்கள் நிர்வாகியால் அமைப்புகள், நிறுவன அணுகல், ஆப்ஸ், சாதனத்துடன் தொடர்புடைய டேட்டா, சாதன இருப்பிடத் தகவல் ஆகியவற்றைக் கண்காணிக்கவும் நிர்வகிக்கவும் முடியும்."</string>
<string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
<string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"மேலும் அறிக"</string>
- <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"<xliff:g id="VPN_APP">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள். இந்த ஆப்ஸால் மின்னஞ்சல்கள், பயன்பாடுகள், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்க முடியும்."</string>
+ <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"<xliff:g id="VPN_APP">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள். இந்த ஆப்ஸால் மின்னஞ்சல்கள், ஆப்ஸ், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்க முடியும்."</string>
<string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
<string name="monitoring_description_vpn_settings" msgid="6434859242636063861">"VPN அமைப்புகளைத் திற"</string>
<string name="monitoring_description_ca_cert_settings_separator" msgid="4987350385906393626">" "</string>
<string name="monitoring_description_ca_cert_settings" msgid="5489969458872997092">"நம்பகமான அனுமதிச் சான்றுகளைத் திற"</string>
<string name="monitoring_description_network_logging" msgid="7223505523384076027">"உங்கள் நிர்வாகி நெட்வொர்க் பதிவெடுத்தலை இயக்கியுள்ளார், இது சாதனத்தில் ட்ராஃபிக்கைக் கண்காணிக்கும்.\n\nமேலும் தகவலுக்கு, உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்."</string>
- <string name="monitoring_description_vpn" msgid="4445150119515393526">"VPN இணைப்பை அமைக்க, பயன்பாட்டிற்கு அனுமதி வழங்கியுள்ளீர்கள்.\n\nஇந்த ஆப்ஸால் மின்னஞ்சல்கள், பயன்பாடுகள் மற்றும் இணையதளங்கள் உட்பட, உங்கள் சாதனத்தையும் நெட்வொர்க் செயல்பாட்டையும் கண்காணிக்க முடியும்."</string>
- <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"உங்கள் பணிக் கணக்கை <xliff:g id="ORGANIZATION">%1$s</xliff:g> நிர்வகிக்கிறது.\n\nஉங்கள் நிர்வாகியால் பயன்பாடுகள், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்க முடியும்.\n\nமேலும் தகவலுக்கு, உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்.\n\nஉங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய VPN உடனும் இணைக்கப்பட்டுள்ளீர்கள்."</string>
+ <string name="monitoring_description_vpn" msgid="4445150119515393526">"VPN இணைப்பை அமைக்க, பயன்பாட்டிற்கு அனுமதி வழங்கியுள்ளீர்கள்.\n\nஇந்த ஆப்ஸால் மின்னஞ்சல்கள், ஆப்ஸ் மற்றும் இணையதளங்கள் உட்பட, உங்கள் சாதனத்தையும் நெட்வொர்க் செயல்பாட்டையும் கண்காணிக்க முடியும்."</string>
+ <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"உங்கள் பணிக் கணக்கை <xliff:g id="ORGANIZATION">%1$s</xliff:g> நிர்வகிக்கிறது.\n\nஉங்கள் நிர்வாகியால் ஆப்ஸ், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்க முடியும்.\n\nமேலும் தகவலுக்கு, உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்.\n\nஉங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய VPN உடனும் இணைக்கப்பட்டுள்ளீர்கள்."</string>
<string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
- <string name="monitoring_description_app" msgid="1828472472674709532">"மின்னஞ்சல்கள், பயன்பாடுகள், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="APPLICATION">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள்."</string>
- <string name="monitoring_description_app_personal" msgid="484599052118316268">"<xliff:g id="APPLICATION">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள். இந்த ஆப்ஸால், மின்னஞ்சல்கள், பயன்பாடுகள் மற்றும் இணையதளங்கள் உட்பட உங்கள் தனிப்பட்ட நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்க முடியும்."</string>
- <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"<xliff:g id="APPLICATION">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள். இந்த ஆப்ஸால் மின்னஞ்சல்கள், பயன்பாடுகள், இணையதளங்கள் உட்பட உங்கள் தனிப்பட்ட நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்க முடியும்."</string>
- <string name="monitoring_description_app_work" msgid="4612997849787922906">"உங்கள் பணிக் கணக்கை <xliff:g id="ORGANIZATION">%1$s</xliff:g> நிர்வகிக்கிறது. மின்னஞ்சல்கள், பயன்பாடுகள், இணையதளங்கள் உட்பட உங்கள் பணி நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="APPLICATION">%2$s</xliff:g> உடன் அது இணைக்கப்பட்டுள்ளது.\n\nமேலும் தகவலுக்கு, நிர்வாகியைத் தொடர்புகொள்ளவும்."</string>
- <string name="monitoring_description_app_personal_work" msgid="5664165460056859391">"உங்கள் பணிக் கணக்கை <xliff:g id="ORGANIZATION">%1$s</xliff:g> நிர்வகிக்கிறது. மின்னஞ்சல்கள், பயன்பாடுகள், இணையதளங்கள் உட்பட உங்கள் பணி நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> உடன் அது இணைக்கப்பட்டுள்ளது.\n\nஉங்கள் தனிப்பட்ட நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> உடனும் இணைக்கப்பட்டுள்ளீர்கள்."</string>
+ <string name="monitoring_description_app" msgid="1828472472674709532">"மின்னஞ்சல்கள், ஆப்ஸ், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="APPLICATION">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள்."</string>
+ <string name="monitoring_description_app_personal" msgid="484599052118316268">"<xliff:g id="APPLICATION">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள். இந்த ஆப்ஸால், மின்னஞ்சல்கள், ஆப்ஸ் மற்றும் இணையதளங்கள் உட்பட உங்கள் தனிப்பட்ட நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்க முடியும்."</string>
+ <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"<xliff:g id="APPLICATION">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள். இந்த ஆப்ஸால் மின்னஞ்சல்கள், ஆப்ஸ், இணையதளங்கள் உட்பட உங்கள் தனிப்பட்ட நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்க முடியும்."</string>
+ <string name="monitoring_description_app_work" msgid="4612997849787922906">"உங்கள் பணிக் கணக்கை <xliff:g id="ORGANIZATION">%1$s</xliff:g> நிர்வகிக்கிறது. மின்னஞ்சல்கள், ஆப்ஸ், இணையதளங்கள் உட்பட உங்கள் பணி நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="APPLICATION">%2$s</xliff:g> உடன் அது இணைக்கப்பட்டுள்ளது.\n\nமேலும் தகவலுக்கு, நிர்வாகியைத் தொடர்புகொள்ளவும்."</string>
+ <string name="monitoring_description_app_personal_work" msgid="5664165460056859391">"உங்கள் பணிக் கணக்கை <xliff:g id="ORGANIZATION">%1$s</xliff:g> நிர்வகிக்கிறது. மின்னஞ்சல்கள், ஆப்ஸ், இணையதளங்கள் உட்பட உங்கள் பணி நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> உடன் அது இணைக்கப்பட்டுள்ளது.\n\nஉங்கள் தனிப்பட்ட நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> உடனும் இணைக்கப்பட்டுள்ளீர்கள்."</string>
<string name="keyguard_indication_trust_unlocked" msgid="2712865815371519117">"TrustAgent இதைத் திறந்தே வைத்துள்ளது"</string>
<string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"நீங்கள் கைமுறையாகத் திறக்கும் வரை, சாதனம் பூட்டப்பட்டிருக்கும்"</string>
<string name="hidden_notifications_title" msgid="7139628534207443290">"விரைவாக அறிவிப்புகளைப் பெறுதல்"</string>
@@ -846,7 +856,7 @@
<string name="pip_skip_to_prev" msgid="1955311326688637914">"முந்தையதற்குச் செல்"</string>
<string name="thermal_shutdown_title" msgid="4458304833443861111">"வெப்பத்தினால் ஃபோன் ஆஃப் செய்யப்பட்டது"</string>
<string name="thermal_shutdown_message" msgid="9006456746902370523">"இப்போது உங்கள் ஃபோன் இயல்புநிலையில் இயங்குகிறது"</string>
- <string name="thermal_shutdown_dialog_message" msgid="566347880005304139">"உங்கள் ஃபோன் அதிகமாகச் சூடானதால், அதன் சூட்டைக் குறைக்க, ஆஃப் செய்யப்பட்டது. இப்போது உங்கள் ஃபோன் இயல்புநிலையில் இயங்குகிறது.\n\nபின்வருவனவற்றைச் செய்தால், ஃபோன் சூடாகலாம்:\n • அதிகளவு தரவைப் பயன்படுத்தும் ஆப்ஸை (எ.கா: கேமிங், வீடியோ (அ) வழிகாட்டுதல் பயன்பாடுகள்) பயன்படுத்துவது\n • பெரிய கோப்புகளைப் பதிவிறக்குவது/பதிவேற்றுவது\n • அதிக வெப்பநிலையில் ஃபோனைப் பயன்படுத்துவது"</string>
+ <string name="thermal_shutdown_dialog_message" msgid="566347880005304139">"உங்கள் ஃபோன் அதிகமாகச் சூடானதால், அதன் சூட்டைக் குறைக்க, ஆஃப் செய்யப்பட்டது. இப்போது உங்கள் ஃபோன் இயல்புநிலையில் இயங்குகிறது.\n\nபின்வருவனவற்றைச் செய்தால், ஃபோன் சூடாகலாம்:\n • அதிகளவு தரவைப் பயன்படுத்தும் ஆப்ஸை (எ.கா: கேமிங், வீடியோ (அ) வழிகாட்டுதல் ஆப்ஸ்) பயன்படுத்துவது\n • பெரிய கோப்புகளைப் பதிவிறக்குவது/பதிவேற்றுவது\n • அதிக வெப்பநிலையில் ஃபோனைப் பயன்படுத்துவது"</string>
<string name="high_temp_title" msgid="4589508026407318374">"மொபைல் சூடாகிறது"</string>
<string name="high_temp_notif_message" msgid="5642466103153429279">"மொபைலின் வெப்ப அளவு குறையும் போது, சில அம்சங்களைப் பயன்படுத்த முடியாது"</string>
<string name="high_temp_dialog_message" msgid="6840700639374113553">"உங்கள் மொபைலின் வெப்ப அளவு தானாகவே குறையும். தொடர்ந்து நீங்கள் மொபைலைப் பயன்படுத்தலாம், ஆனால் அதன் வேகம் குறைவாக இருக்கக்கூடும்.\n\nமொபைலின் வெப்ப அளவு குறைந்தவுடன், அது இயல்பு நிலையில் இயங்கும்."</string>
@@ -891,7 +901,7 @@
<string name="qs_dnd_until" msgid="3469471136280079874">"<xliff:g id="ID_1">%s</xliff:g> வரை"</string>
<string name="qs_dnd_keep" msgid="1825009164681928736">"வைத்திரு"</string>
<string name="qs_dnd_replace" msgid="8019520786644276623">"மாற்று"</string>
- <string name="running_foreground_services_title" msgid="381024150898615683">"பின்னணியில் இயங்கும் பயன்பாடுகள்"</string>
+ <string name="running_foreground_services_title" msgid="381024150898615683">"பின்னணியில் இயங்கும் ஆப்ஸ்"</string>
<string name="running_foreground_services_msg" msgid="6326247670075574355">"பேட்டரி மற்றும் டேட்டா உபயோக விவரங்களைக் காண, தட்டவும்"</string>
<string name="mobile_data_disable_title" msgid="1068272097382942231">"மொபைல் டேட்டாவை ஆஃப் செய்யவா?"</string>
<string name="mobile_data_disable_message" msgid="4756541658791493506">"<xliff:g id="CARRIER">%s</xliff:g> மூலம் டேட்டா அல்லது இணையத்தை உங்களால் பயன்படுத்த முடியாது. வைஃபை வழியாக மட்டுமே இணையத்தைப் பயன்படுத்த முடியும்."</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 62fe95b52290..af330f18fe21 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -47,8 +47,11 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ఇన్‌పుట్ పద్ధతులను సెటప్ చేయండి"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"భౌతిక కీబోర్డ్"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="USB_DEVICE">%2$s</xliff:g>ని యాక్సెస్ చేయడానికి <xliff:g id="APPLICATION">%1$s</xliff:g>ని అనుమతించాలా?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="USB_DEVICE">%2$s</xliff:g>యాక్సెస్ చేయడానికి <xliff:g id="APPLICATION">%1$s</xliff:g>ను అనుమతించాలా?\nఈ యాప్‌నకు రికార్డ్ చేసే అనుమతి మంజూరు చేయబడలేదు, కానీ ఈ USB పరికరం ద్వారా ఆడియోను క్యాప్చర్ చేయగలదు."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>ని యాక్సెస్ చేయడానికి <xliff:g id="APPLICATION">%1$s</xliff:g>ని అనుమతించాలా?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g>ని నిర్వహించడానికి <xliff:g id="APPLICATION">%1$s</xliff:g>ని తెరవాలా?"</string>
+ <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) -->
+ <skip />
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>ని నిర్వహించడానికి <xliff:g id="APPLICATION">%1$s</xliff:g>ని తెరవాలా?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"ఈ USB ఉపకరణంతో ఇన్‌స్టాల్ చేయబడిన అనువర్తనాలు ఏవీ పని చేయవు. ఈ ఉపకరణం గురించి <xliff:g id="URL">%1$s</xliff:g>లో మరింత తెలుసుకోండి"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB ఉపకరణం"</string>
@@ -126,6 +129,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"నిర్ధారించబడింది"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"పూర్తి చేయడానికి \"నిర్ధారించు\" నొక్కండి"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"ప్రామాణీకరించబడింది"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"పిన్‌ను ఉపయోగించు"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"ఆకృతిని ఉపయోగించు"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"పాస్‌వర్డ్‌ను ఉపయోగించు"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"పిన్ తప్పు"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"ఆకృతి తప్పు"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"పాస్‌వర్డ్ తప్పు"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"చాలా ఎక్కువ తప్పు ప్రయత్నాలు చేశారు.\n<xliff:g id="NUMBER">%d</xliff:g> సెకన్ల తర్వాత మళ్లీ ప్రయత్నించండి."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"వేలిముద్ర సెన్సార్‌ను తాకండి"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"వేలిముద్ర చిహ్నం"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"మీ కోసం చూస్తోంది…"</string>
@@ -364,6 +374,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"నోటిఫికేషన్‌లు"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ఫ్లాష్‌లైట్"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"కెమెరా ఉపయోగంలో ఉంది"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"మొబైల్ డేటా"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"డేటా వినియోగం"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"మిగిలిన డేటా"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index eb6810a0898a..fc062b413cff 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ตั้งค่าวิธีการป้อนข้อมูล"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"แป้นพิมพ์บนเครื่อง"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"อนุญาตให้ <xliff:g id="APPLICATION">%1$s</xliff:g> เข้าถึง <xliff:g id="USB_DEVICE">%2$s</xliff:g> ไหม"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"อนุญาตให้ <xliff:g id="APPLICATION">%1$s</xliff:g> เข้าถึง <xliff:g id="USB_DEVICE">%2$s</xliff:g> ไหม\nแอปนี้ไม่ได้รับอนุญาตให้อัดเสียงแต่จะอัดเสียงผ่านอุปกรณ์ USB นี้ได้"</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"อนุญาตให้ <xliff:g id="APPLICATION">%1$s</xliff:g> เข้าถึง <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ไหม"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"เปิด <xliff:g id="APPLICATION">%1$s</xliff:g> เพื่อจัดการ <xliff:g id="USB_DEVICE">%2$s</xliff:g> ไหม"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"เปิด <xliff:g id="APPLICATION">%1$s</xliff:g> เพื่อจัดการ <xliff:g id="USB_DEVICE">%2$s</xliff:g> ใช่ไหม\nแอปนี้ไม่ได้รับอนุญาตให้อัดเสียงแต่จะอัดเสียงผ่านอุปกรณ์ USB นี้ได้"</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"เปิด <xliff:g id="APPLICATION">%1$s</xliff:g> เพื่อจัดการ <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ไหม"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"แอปพลิเคชันที่ติดตั้งใช้กับอุปกรณ์ USB นี้ไม่ได้ ดูข้อมูลเพิ่มเติมเกี่ยวกับอุปกรณ์เสริมนี้ที่ <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"อุปกรณ์เสริม USB"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"ยืนยันแล้ว"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"แตะยืนยันเพื่อดำเนินการให้เสร็จสมบูรณ์"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"ตรวจสอบสิทธิ์แล้ว"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"ใช้ PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"ใช้รูปแบบ"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"ใช้รหัสผ่าน"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN ไม่ถูกต้อง"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"รูปแบบไม่ถูกต้อง"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"รหัสผ่านไม่ถูกต้อง"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"ดำเนินการไม่ถูกต้องหลายครั้งเกินไป\nลองอีกครั้งใน <xliff:g id="NUMBER">%d</xliff:g> วินาที"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"แตะเซ็นเซอร์ลายนิ้วมือ"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"ไอคอนลายนิ้วมือ"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"กำลังหาใบหน้าคุณ…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"การแจ้งเตือน"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ไฟฉาย"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"ใช้กล้องอยู่"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"เน็ตมือถือ"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"การใช้อินเทอร์เน็ต"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"ข้อมูลที่เหลืออยู่"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index f6abdcbcbada..37dd49fdf65c 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"I-set up paraan ng pag-input"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Aktwal na keyboard"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Payagan ang <xliff:g id="APPLICATION">%1$s</xliff:g> na ma-access ang <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Payagan ang <xliff:g id="APPLICATION">%1$s</xliff:g> na i-access ang <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nHindi nabigyan ng pahintulot ang app na ito para mag-record pero nakakapag-capture ito ng audio sa pamamagitan ng USB device na ito."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Payagan ang <xliff:g id="APPLICATION">%1$s</xliff:g> na ma-access ang <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Buksan ang <xliff:g id="APPLICATION">%1$s</xliff:g> upang pamahalaan ang <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Buksan ang <xliff:g id="APPLICATION">%1$s</xliff:g> para pangasiwaan ang <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nHindi nabigyan ng pahintulot ang app na ito para mag-record pero nakakapag-capture ito ng audio sa pamamagitan ng USB device na ito."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Buksan ang <xliff:g id="APPLICATION">%1$s</xliff:g> upang pamahalaan ang <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Wala sa mga na-install na app ang gumagana sa USB accessory na ito. Matuto pa tungkol sa accessory na ito sa <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB accessory"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Nakumpirma"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"I-tap ang Kumpirmahin para kumpletuhin"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Na-authenticate"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Gumamit ng PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Gumamit ng pattern"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Gumamit ng password"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Maling PIN"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Maling pattern"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Maling password"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Masyadong maraming maling pagsubok.\nSubukan ulit sa loob ng <xliff:g id="NUMBER">%d</xliff:g> (na) segundo."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Pindutin ang fingerprint sensor"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Icon ng fingerprint"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Hinahanap ka…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Mga Notification"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Flashlight"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Ginagamit na camera"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobile data"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Paggamit ng data"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Natitirang data"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index b5e17023b0e5..c052f4a1d73e 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Giriş yöntemlerini ayarla"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fiziksel klavye"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="APPLICATION">%1$s</xliff:g> uygulamasının <xliff:g id="USB_DEVICE">%2$s</xliff:g> cihazına erişmesine izin verilsin mi?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="APPLICATION">%1$s</xliff:g> uygulamasının <xliff:g id="USB_DEVICE">%2$s</xliff:g> cihazına erişmesine izin verilsin mi?\nBu uygulamaya kayıt izni verilmemiş ancak bu USB cihazı aracılığıyla sesleri yakalayabilir."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g> uygulamasının <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> aksesuarına erişmesine izin verilsin mi?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> cihazını işlemek için <xliff:g id="APPLICATION">%1$s</xliff:g> uygulaması açılsın mı?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> için <xliff:g id="APPLICATION">%1$s</xliff:g> uygulaması açılsın mı?\n Bu uygulamaya kayıt izni verilmemiş, ancak bu USB cihazı aracılığıyla sesleri yakalabilir."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> aksesuarını işlemek için <xliff:g id="APPLICATION">%1$s</xliff:g> uygulaması açılsın mı?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Bu USB aksesuarıyla çalışan yüklü uygulama yok. Bu aksesuar hakkında bilgi için: <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB aksesuarı"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Onaylandı"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Tamamlamak için Onayla\'ya dokunun"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Kimliği Doğrulandı"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"PIN kullan"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Deseni kullan"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Şifre kullan"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Yanlış PIN"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Yanlış desen"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Yanlış şifre"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Çok fazla yanlış giriş yapıldı.\n<xliff:g id="NUMBER">%d</xliff:g> saniye içinde tekrar deneyin."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Parmak izi sensörüne dokunun"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Parmak izi simgesi"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Yüzünüz tanınmaya çalışılıyor…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Bildirimler"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Fener"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Kamera kullanımda"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobil veri"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Veri kullanımı"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Kalan veri"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 36bec1e031b4..de6b6f218d5c 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Налаштувати методи введення"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Фізична клавіатура"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Надати додатку <xliff:g id="APPLICATION">%1$s</xliff:g> доступ до такого аксесуара: <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Надати додатку <xliff:g id="APPLICATION">%1$s</xliff:g> доступ до <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nЦей додаток не має дозволу на записування звуку, але може фіксувати його через цей USB-пристрій."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Надати додатку <xliff:g id="APPLICATION">%1$s</xliff:g> доступ до такого аксесуара: <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Відкрити додаток <xliff:g id="APPLICATION">%1$s</xliff:g>, щоб використовувати такий аксесуар: <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Працювати з пристроєм \"<xliff:g id="USB_DEVICE">%2$s</xliff:g>\" у додатку <xliff:g id="APPLICATION">%1$s</xliff:g>?\nУ цього додатка немає дозволів на запис, але він може передавати звук у USB-пристрій."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Відкрити додаток <xliff:g id="APPLICATION">%1$s</xliff:g>, щоб використовувати такий аксесуар: <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Установлені прогр. не працюють із цим аксесуаром USB. Більше про цей аксесуар: <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Пристрій USB"</string>
@@ -109,7 +111,7 @@
<string name="accessibility_voice_assist_button" msgid="487611083884852965">"Голосові підказки"</string>
<string name="accessibility_unlock_button" msgid="128158454631118828">"Розблокувати"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"Очікується відбиток пальця"</string>
- <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"Розблокувати без цифрового відбитка"</string>
+ <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"Розблокувати без відбитка пальця"</string>
<string name="accessibility_scanning_face" msgid="769545173211758586">"Сканування обличчя"</string>
<string name="accessibility_send_smart_reply" msgid="7766727839703044493">"Надіслати"</string>
<string name="accessibility_manage_notification" msgid="2026361503393549753">"Керувати сповіщеннями"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Підтверджено"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Щоб завершити, натисніть \"Підтвердити\""</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Автентифіковано"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Ввести PIN-код"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Намалювати ключ"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Ввести пароль"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Неправильний PIN-код"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Неправильний ключ"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Неправильний пароль"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Забагато невдалих спроб.\nПовторіть за <xliff:g id="NUMBER">%d</xliff:g> с."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Торкніться сканера відбитків пальців"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Значок відбитка пальця"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Пошук обличчя…"</string>
@@ -368,6 +377,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Сповіщення"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Ліхтарик"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Використовується камера"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Мобільне передавання даних"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Використання даних"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Залишилося даних"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 5fceabe93f2f..b361a4acc512 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -47,8 +47,11 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ان پٹ کے طریقوں کو ترتیب دیں"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"طبعی کی بورڈ"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="APPLICATION">%1$s</xliff:g> کو <xliff:g id="USB_DEVICE">%2$s</xliff:g> تک رسائی حاصل کرنے کی اجازت دیں؟"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"‏<xliff:g id="APPLICATION">%1$s</xliff:g> کو <xliff:g id="USB_DEVICE">%2$s</xliff:g> تک رسائی دیں؟\nاس ایپ کو ریکارڈ کی اجازت عطا نہیں کی گئی ہے مگر اس USB آلہ سے کیپچر کر سکتے ہیں۔"</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g> کو <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> تک رسائی حاصل کرنے کی اجازت دیں؟"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ہینڈل کرنے کیلئے <xliff:g id="APPLICATION">%1$s</xliff:g> کھولیں؟"</string>
+ <!-- no translation found for usb_device_confirm_prompt_warn (210658281376801521) -->
+ <skip />
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ہینڈل کرنے کیلئے <xliff:g id="APPLICATION">%1$s</xliff:g> کھولیں؟"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"‏اس USB لوازم کے ساتھ کوئی انسٹال کردہ ایپس کام نہیں کرتی ہیں۔ <xliff:g id="URL">%1$s</xliff:g> پر مزید جانیں"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"‏USB لوازم"</string>
@@ -126,6 +129,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"تصدیق شدہ"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"مکمل کرنے کیلئے \'تصدیق کریں\' تھپتھپائیں"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"تصدیق کردہ"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"‏PIN استعمال کریں"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"پیٹرن کا استعمال کریں"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"پاس ورڈ استعمال کریں"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"‏غلط PIN"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"غلط پیٹرن"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"غلط پاس ورڈ"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"کافی زیادہ غلط کوششیں کی گئیں۔\n <xliff:g id="NUMBER">%d</xliff:g> سیکنڈ بعد دوبارہ کوشش کریں۔"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"فنگر پرنٹ سینسر پر ٹچ کریں"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"فنگر پرنٹ آئیکن"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"آپ کے لیے تلاش کیا جا رہا ہے…"</string>
@@ -364,6 +374,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"اطلاعات"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"فلیش لائٹ"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"کیمرا زیر استعمال ہے"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"موبائل ڈیٹا"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"ڈیٹا کا استعمال"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"باقی ڈیٹا"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 37651a5c000d..5c077d83cf61 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Kiritish usullarini moslash"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Tashqi tugmatag"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="APPLICATION">%1$s</xliff:g> ilovasiga <xliff:g id="USB_DEVICE">%2$s</xliff:g> qurilmasidan foydalanishga ruxsat berilsinmi?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"<xliff:g id="APPLICATION">%1$s</xliff:g> ilovasiga <xliff:g id="USB_DEVICE">%2$s</xliff:g> qurilmasidan foydalanish uchun ruxsat berilsinmi?\nBu ilovaga yozib olish ruxsati berilmagan, lekin shu USB orqali ovozlarni yozib olishi mumkin."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g> ilovasiga <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> qurilmasidan foydalanishga ruxsat berilsinmi?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> bilan ishlash uchun <xliff:g id="APPLICATION">%1$s</xliff:g> ochilsinmi?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"<xliff:g id="APPLICATION">%1$s</xliff:g> ilovasiga <xliff:g id="USB_DEVICE">%2$s</xliff:g> qurilmasidan foydalanish uchun ruxsat berilsinmi?\nBu ilovaga yozib olish ruxsati berilmagan, lekin shu USB orqali ovozlarni yozib olishi mumkin."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> bilan ishlash uchun <xliff:g id="APPLICATION">%1$s</xliff:g> ochilsinmi?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Bu USB jihoz bilan ishlash uchun dastur o‘rnatilmagan.Ushbu jihoz haqida: <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB jihoz"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Tasdiqlangan"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Tasdiqlash uchun tegining"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Tasdiqlandi"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"PIN kod kiritish"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Grafik kalitdan foydalanish"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Paroldan foydalanish"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN kod xato"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Grafik kalit xato"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Parol xato"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Xato urinishlar soni oshib ketdi! \n <xliff:g id="NUMBER">%d</xliff:g> soniyadan keyin qayta urining."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Barmoq izi skaneriga tegining"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Barmoq izi belgisi"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Yuzingiz tekshirilmoqda…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Bildirishnomalar"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Fonar"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Kamera ishlatilmoqda"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Mobil internet"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Trafik sarfi"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Qolgan trafik"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index f6782659f1ed..88849c8368ca 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Thiết lập phương thức nhập"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Bàn phím vật lý"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Cho phép <xliff:g id="APPLICATION">%1$s</xliff:g> truy cập <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Cho phép <xliff:g id="APPLICATION">%1$s</xliff:g> truy cập vào <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nỨng dụng này chưa được cấp quyền ghi âm nhưng vẫn có thể ghi âm thông qua thiết bị USB này."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Cho phép <xliff:g id="APPLICATION">%1$s</xliff:g> truy cập <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Mở <xliff:g id="APPLICATION">%1$s</xliff:g> để điều khiển <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Mở <xliff:g id="APPLICATION">%1$s</xliff:g> để điều khiển <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nDù chưa được cấp quyền ghi âm, nhưng ứng dụng có thể ghi âm thông qua thiết bị USB này."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Mở <xliff:g id="APPLICATION">%1$s</xliff:g> để điều khiển <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Không có ứng dụng được cài đặt nào hoạt động với phụ kiện USB này. Tìm hiểu thêm về phụ kiện này tại <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"Phụ kiện USB"</string>
@@ -56,7 +58,7 @@
<string name="always_use_device" msgid="4015357883336738417">"Luôn mở <xliff:g id="APPLICATION">%1$s</xliff:g> khi kết nối <xliff:g id="USB_DEVICE">%2$s</xliff:g>"</string>
<string name="always_use_accessory" msgid="3257892669444535154">"Luôn mở <xliff:g id="APPLICATION">%1$s</xliff:g> khi kết nối <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>"</string>
<string name="usb_debugging_title" msgid="4513918393387141949">"Cho phép gỡ lỗi qua USB?"</string>
- <string name="usb_debugging_message" msgid="2220143855912376496">"Tệp tham chiếu khóa RSA của máy tính là:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
+ <string name="usb_debugging_message" msgid="2220143855912376496">"Vân tay khóa RSA của máy tính là:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="303335496705863070">"Luôn cho phép từ máy tính này"</string>
<string name="usb_debugging_allow" msgid="2272145052073254852">"Cho phép"</string>
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Không cho phép chế độ gỡ lỗi qua USB"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Ðã xác nhận"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Nhấn vào Xác nhận để hoàn tất"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Đã xác thực"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Dùng mã PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Dùng hình mở khóa"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Dùng mật khẩu"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Mã PIN sai"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Hình mở khóa sai"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Mật khẩu sai"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Bạn đã nhập sai quá nhiều lần.\nHãy thử lại sau <xliff:g id="NUMBER">%d</xliff:g> giây."</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Chạm vào cảm biến vân tay"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Biểu tượng vân tay"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Đang tìm kiếm bạn…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Thông báo"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Đèn pin"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Máy ảnh đang được sử dụng"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Dữ liệu di động"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Sử dụng dữ liệu"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Dữ liệu còn lại"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 05509f65047e..cdc20700a406 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"设置输入法"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"物理键盘"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"要允许<xliff:g id="APPLICATION">%1$s</xliff:g>访问<xliff:g id="USB_DEVICE">%2$s</xliff:g>吗?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"是否允许<xliff:g id="APPLICATION">%1$s</xliff:g>访问<xliff:g id="USB_DEVICE">%2$s</xliff:g>?\n此应用未获得录音权限,但能通过此 USB 设备录制音频。"</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"要允许<xliff:g id="APPLICATION">%1$s</xliff:g>访问<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>吗?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"要打开<xliff:g id="APPLICATION">%1$s</xliff:g>来处理<xliff:g id="USB_DEVICE">%2$s</xliff:g>吗?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"要打开<xliff:g id="APPLICATION">%1$s</xliff:g>来使用<xliff:g id="USB_DEVICE">%2$s</xliff:g>吗?\n此应用未获得录音权限,但能通过此 USB 设备录制音频。"</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"要打开<xliff:g id="APPLICATION">%1$s</xliff:g>来处理<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>吗?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"未安装此USB配件适用的应用。要了解此配件的详情,请访问:<xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB配件"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"已确认"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"点按“确认”即可完成"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"已经过身份验证"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"使用 PIN 码"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"使用图案"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"使用密码"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN 码错误"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"图案错误"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"密码错误"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"输错次数过多。\n请在 <xliff:g id="NUMBER">%d</xliff:g> 秒后重试。"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"请触摸指纹传感器"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"指纹图标"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"正在查找您的面孔…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"通知"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"手电筒"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"相机正在使用中"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"移动数据"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"流量使用情况"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"剩余流量"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index b57e61fa0cf6..a69bb7b855e1 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"設定輸入法"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"實體鍵盤"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"要允許「<xliff:g id="APPLICATION">%1$s</xliff:g>」存取「<xliff:g id="USB_DEVICE">%2$s</xliff:g>」嗎?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"要允許「<xliff:g id="APPLICATION">%1$s</xliff:g>」存取「<xliff:g id="USB_DEVICE">%2$s</xliff:g>」嗎?\n此應用程式尚未獲授予錄音權限,但可透過此 USB 裝置記錄音訊。"</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"要允許「<xliff:g id="APPLICATION">%1$s</xliff:g>」存取「<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>」嗎?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"要開啟「<xliff:g id="APPLICATION">%1$s</xliff:g>」處理「<xliff:g id="USB_DEVICE">%2$s</xliff:g>」嗎?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"要開啟「<xliff:g id="APPLICATION">%1$s</xliff:g>」應用程式來控制「<xliff:g id="USB_DEVICE">%2$s</xliff:g>」嗎?\n此應用程式尚未獲授予錄音權限,但可透過此 USB 裝置記錄音訊。"</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"要開啟「<xliff:g id="APPLICATION">%1$s</xliff:g>」處理「<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>」嗎?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"已安裝的應用程式均無法存取這個 USB 配件,如要進一步瞭解這個配件,請瀏覽 <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB 配件"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"已確認"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"輕按 [確定] 以完成"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"驗證咗"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"使用 PIN"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"使用圖案"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"使用密碼"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN 錯誤"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"圖案錯誤"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"密碼錯誤"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"輸入錯誤的次數太多,\n請於 <xliff:g id="NUMBER">%d</xliff:g> 秒後再試。"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"請輕觸指紋感應器"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"指紋圖示"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"正在搜尋您的臉孔…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"通知"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"電筒"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"相機使用中"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"流動數據"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"數據用量"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"剩餘資料"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 14fedbbad2c5..dedaebf679ad 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"設定輸入法"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"實體鍵盤"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"要允許「<xliff:g id="APPLICATION">%1$s</xliff:g>」存取「<xliff:g id="USB_DEVICE">%2$s</xliff:g>」嗎?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"要允許「<xliff:g id="APPLICATION">%1$s</xliff:g>」存取「<xliff:g id="USB_DEVICE">%2$s</xliff:g>」嗎?\n這個應用程式未取得錄製權限,但可以透過這部 USB 裝置錄製音訊。"</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"要允許「<xliff:g id="APPLICATION">%1$s</xliff:g>」存取「<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>」嗎?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"要開啟「<xliff:g id="APPLICATION">%1$s</xliff:g>」處理「<xliff:g id="USB_DEVICE">%2$s</xliff:g>」嗎?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"要開啟「<xliff:g id="APPLICATION">%1$s</xliff:g>」來使用「<xliff:g id="USB_DEVICE">%2$s</xliff:g>」嗎?\n這個應用程式未取得錄製內容的權限,但可以透過這部 USB 裝置錄製音訊。"</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"要開啟「<xliff:g id="APPLICATION">%1$s</xliff:g>」處理「<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>」嗎?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"已安裝的應用程式均無法存取這個 USB 配件,如要進一步瞭解這個配件,請造訪 <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"USB 配件"</string>
@@ -126,6 +128,13 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"確認完畢"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"輕觸 [確認] 完成驗證設定"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"已通過驗證"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"使用 PIN 碼"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"使用解鎖圖案"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"使用密碼"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"PIN 碼錯誤"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"圖案錯誤"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"密碼錯誤"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"錯誤次數過多,\n請於 <xliff:g id="NUMBER">%d</xliff:g> 秒後再試。"</string>
<string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"請輕觸指紋感應器"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"指紋圖示"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"正在尋找你的臉孔…"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"通知"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"手電筒"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"正在使用相機"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"行動數據"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"數據用量"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"剩餘資料"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index c158c773b03d..192bfb4dd38e 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -47,8 +47,10 @@
<string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Izilungiselelo zezindlela zokufakwayo"</string>
<string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Ukwakheka kwekhibhodi"</string>
<string name="usb_device_permission_prompt" msgid="1825685909587559679">"Vumela i-<xliff:g id="APPLICATION">%1$s</xliff:g> ukufinyelela i-<xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="1842558472039505091">"Vumela i-<xliff:g id="APPLICATION">%1$s</xliff:g> ukuthi ifinyelele ku-<xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nLolu hlelo lokusebenza alunikeziwe imvume yokurekhoda kodwa lingathatha umsindo ngale divayisi ye-USB."</string>
<string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"Vumela i-<xliff:g id="APPLICATION">%1$s</xliff:g> ukufinyelela i-<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_device_confirm_prompt" msgid="7440562274256843905">"Vula i-<xliff:g id="APPLICATION">%1$s</xliff:g> ukuze uphath i-<xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="210658281376801521">"Vula i-<xliff:g id="APPLICATION">%1$s</xliff:g> ukuze uphathe i-<xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nLolu hlelo lokusebenza alinikeziwe imvume yokurekhoda kodwa lingathatha umsindo ngale divayisi ye-USB."</string>
<string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"Vula i-<xliff:g id="APPLICATION">%1$s</xliff:g> ukuze uphath i-<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
<string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Azikho izinhlelo zokusebenza ezisebenze ngezinto ze-USB. Funda okwengeziwe ngale into kwi <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="4966265263465181372">"ama-accessory e-USB"</string>
@@ -109,7 +111,7 @@
<string name="accessibility_voice_assist_button" msgid="487611083884852965">"Isisekeli sezwi"</string>
<string name="accessibility_unlock_button" msgid="128158454631118828">"Vula"</string>
<string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"Ilindele izigxivizo zeminwe"</string>
- <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"Vula ngaphandle kokusebenzisa izigxivizo zakho zeminwe"</string>
+ <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"Vula ngaphandle kokusebenzisa izigxivizo zeminwe zakho"</string>
<string name="accessibility_scanning_face" msgid="769545173211758586">"Ukuskena ubuso"</string>
<string name="accessibility_send_smart_reply" msgid="7766727839703044493">"Thumela"</string>
<string name="accessibility_manage_notification" msgid="2026361503393549753">"Phatha izaziso"</string>
@@ -126,7 +128,14 @@
<string name="biometric_dialog_face_icon_description_confirmed" msgid="2003141400387093967">"Kuqinisekisiwe"</string>
<string name="biometric_dialog_tap_confirm" msgid="4540715260292022404">"Thepha okuthi Qinisekisa ukuze uqedele"</string>
<string name="biometric_dialog_authenticated" msgid="5918352844999713693">"Kugunyaziwe"</string>
- <string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Thinta inzwa yesigxivizo somunwe"</string>
+ <string name="biometric_dialog_use_pin" msgid="2506187927478996039">"Sebenzisa iphinikhodi"</string>
+ <string name="biometric_dialog_use_pattern" msgid="4721877831431699442">"Sebenzisa iphethini"</string>
+ <string name="biometric_dialog_use_password" msgid="3426428493718969343">"Sebenzisa iphasiwedi"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="4600590473629948574">"Iphinikhodi engalungile"</string>
+ <string name="biometric_dialog_wrong_pattern" msgid="4808369401645512099">"Iphethini engalungile"</string>
+ <string name="biometric_dialog_wrong_password" msgid="2343518162282889518">"Iphasiwedi engalungile"</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="1556206869468265728">"Imizamo eminingi kakhulu engalungile.\nZama futhi kumasekhondi angu-<xliff:g id="NUMBER">%d</xliff:g>."</string>
+ <string name="fingerprint_dialog_touch_sensor" msgid="8511557690663181761">"Thinta inzwa yesigxivizo zeminwe"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="3125122495414253226">"Isithonjana sezigxivizo zeminwe"</string>
<string name="face_dialog_looking_for_face" msgid="7049276266074494689">"Kufunwa wena…"</string>
<string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Isithonjana sobuso"</string>
@@ -364,6 +373,7 @@
</plurals>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Izaziso"</string>
<string name="quick_settings_flashlight_label" msgid="2133093497691661546">"I-Flashlight"</string>
+ <string name="quick_settings_flashlight_camera_in_use" msgid="6120370795890963385">"Ikhamera esetshenziswayo"</string>
<string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"Idatha yeselula"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Ukusetshenziswa kwedatha"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Idatha esele"</string>
diff --git a/packages/SystemUI/res/values/arrays_tv.xml b/packages/SystemUI/res/values/arrays_tv.xml
index 9197bb51e1a6..1fe6141a53b7 100644
--- a/packages/SystemUI/res/values/arrays_tv.xml
+++ b/packages/SystemUI/res/values/arrays_tv.xml
@@ -33,4 +33,8 @@
<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">
+ <item>com.google.android.katniss</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values/colors_tv.xml b/packages/SystemUI/res/values/colors_tv.xml
index 6e56d4a38d0c..db225428a348 100644
--- a/packages/SystemUI/res/values/colors_tv.xml
+++ b/packages/SystemUI/res/values/colors_tv.xml
@@ -21,4 +21,14 @@
<color name="recents_tv_card_title_text_color">#CCEEEEEE</color>
<color name="recents_tv_dismiss_text_color">#7FEEEEEE</color>
<color name="recents_tv_text_shadow_color">#7F000000</color>
+
+
+ <!-- Text color used in audio recording bar: G50 -->
+ <color name="tv_audio_recording_bar_text">#FFF8F9FA</color>
+ <!-- Background color for a chip in audio recording bar: G800 -->
+ <color name="tv_audio_recording_bar_chip_background">#FF3C4043</color>
+ <!-- Audio recording bar background color: G900 -->
+ <color name="tv_audio_recording_bar_background">#FF202124</color>
+
+ <color name="red">#FFCC0000</color>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index ca30314eadf2..5561f437fb0f 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -117,7 +117,7 @@
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
<string name="quick_settings_tiles_stock" translatable="false">
- wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night
+ wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,controls
</string>
<!-- The tiles to display in QuickSettings -->
@@ -279,7 +279,7 @@
<item>com.android.systemui.recents.Recents</item>
<item>com.android.systemui.volume.VolumeUI</item>
<item>com.android.systemui.stackdivider.Divider</item>
- <item>com.android.systemui.SystemBars</item>
+ <item>com.android.systemui.statusbar.phone.StatusBar</item>
<item>com.android.systemui.usb.StorageNotification</item>
<item>com.android.systemui.power.PowerUI</item>
<item>com.android.systemui.media.RingtonePlayer</item>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 57c6e3b8ba84..140dfbc52f89 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1009,10 +1009,15 @@
<!-- Biometric Dialog values -->
<dimen name="biometric_dialog_biometric_icon_size">64dp</dimen>
<dimen name="biometric_dialog_corner_size">4dp</dimen>
+ <!-- Y translation when showing/dismissing the dialog-->
<dimen name="biometric_dialog_animation_translation_offset">350dp</dimen>
<dimen name="biometric_dialog_border_padding">4dp</dimen>
<dimen name="biometric_dialog_elevation">1dp</dimen>
<dimen name="biometric_dialog_icon_padding">16dp</dimen>
+ <!-- Y translation for biometric contents when transitioning to device credential UI -->
+ <dimen name="biometric_dialog_medium_to_large_translation_offset">100dp</dimen>
+ <!-- Y translation for credential contents when animating in -->
+ <dimen name="biometric_dialog_credential_translation_offset">60dp</dimen>
<!-- Wireless Charging Animation values -->
<dimen name="wireless_charging_dots_radius_start">0dp</dimen>
@@ -1159,4 +1164,11 @@
<!-- Size of the RAT type for CellularTile -->
<dimen name="celltile_rat_type_size">10sp</dimen>
+
+ <dimen name="new_qs_vertical_margin">8dp</dimen>
+
+ <!-- Size of media cards in the QSPanel carousel -->
+ <dimen name="qs_media_height">150dp</dimen>
+ <dimen name="qs_media_width">350dp</dimen>
+ <dimen name="qs_media_padding">8dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 372718139886..04640f418e97 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -141,5 +141,8 @@
<!-- Global Actions Menu -->
<item type="id" name="global_actions_view" />
+
+ <!-- NotificationPanelView -->
+ <item type="id" name="notification_panel" />
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 97e2f0f6562b..008294980d94 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -118,14 +118,20 @@
<string name="status_bar_use_physical_keyboard">Physical keyboard</string>
<!-- Prompt for the USB device permission dialog [CHAR LIMIT=80] -->
- <string name="usb_device_permission_prompt">Allow <xliff:g id="application">%1$s</xliff:g> to access <xliff:g id="usb_device">%2$s</xliff:g>?</string>
+ <string name="usb_device_permission_prompt">Allow <xliff:g id="application" example= "Usb Mega Player">%1$s</xliff:g> to access <xliff:g id="usb_device" example="USB Headphones">%2$s</xliff:g>?</string>
+
+ <!-- Checkbox label for USB device dialogs with warning text for USB device dialogs. [CHAR LIMIT=200]-->
+ <string name="usb_device_permission_prompt_warn">Allow <xliff:g id="application" example= "Usb Mega Player">%1$s</xliff:g> to access <xliff:g id="usb_device" example="USB Headphones">%2$s</xliff:g>?\nThis app has not been granted record permission but could capture audio through this USB device.</string>
<!-- Prompt for the USB accessory permission dialog [CHAR LIMIT=80] -->
- <string name="usb_accessory_permission_prompt">Allow <xliff:g id="application">%1$s</xliff:g> to access <xliff:g id="usb_accessory">%2$s</xliff:g>?</string>
+ <string name="usb_accessory_permission_prompt">Allow <xliff:g id="application" example= "Usb Mega Player">%1$s</xliff:g> to access <xliff:g id="usb_accessory" example="USB Dock">%2$s</xliff:g>?</string>
<!-- Prompt for the USB device confirm dialog [CHAR LIMIT=80] -->
<string name="usb_device_confirm_prompt">Open <xliff:g id="application">%1$s</xliff:g> to handle <xliff:g id="usb_device">%2$s</xliff:g>?</string>
+ <!-- Prompt for the USB device confirm dialog with warning text for USB device dialogs. [CHAR LIMIT=200] -->
+ <string name="usb_device_confirm_prompt_warn">Open <xliff:g id="application" example= "Usb Mega Player">%1$s</xliff:g> to handle <xliff:g id="usb_device" example="USB Headphones">%2$s</xliff:g>?\nThis app has not been granted record permission but could capture audio through this USB device.</string>
+
<!-- Prompt for the USB accessory confirm dialog [CHAR LIMIT=80] -->
<string name="usb_accessory_confirm_prompt">Open <xliff:g id="application">%1$s</xliff:g> to handle <xliff:g id="usb_accessory">%2$s</xliff:g>?</string>
@@ -308,6 +314,21 @@
<!-- Talkback string when a biometric is authenticated [CHAR LIMIT=NONE] -->
<string name="biometric_dialog_authenticated">Authenticated</string>
+ <!-- Button text shown on BiometricPrompt giving the user the option to use an alternate form of authentication (Pin) [CHAR LIMIT=30] -->
+ <string name="biometric_dialog_use_pin">Use PIN</string>
+ <!-- Button text shown on BiometricPrompt giving the user the option to use an alternate form of authentication (Pattern) [CHAR LIMIT=30] -->
+ <string name="biometric_dialog_use_pattern">Use pattern</string>
+ <!-- Button text shown on BiometricPrompt giving the user the option to use an alternate form of authentication (Pass) [CHAR LIMIT=30] -->
+ <string name="biometric_dialog_use_password">Use password</string>
+ <!-- Error string shown when the user enters an incorrect PIN [CHAR LIMIT=40]-->
+ <string name="biometric_dialog_wrong_pin">Wrong PIN</string>
+ <!-- Error string shown when the user enters an incorrect pattern [CHAR LIMIT=40]-->
+ <string name="biometric_dialog_wrong_pattern">Wrong pattern</string>
+ <!-- Error string shown when the user enters an incorrect password [CHAR LIMIT=40]-->
+ <string name="biometric_dialog_wrong_password">Wrong password</string>
+ <!-- Error string shown when the user enters too many incorrect attempts [CHAR LIMIT=120]-->
+ <string name="biometric_dialog_credential_too_many_attempts">Too many incorrect attempts.\nTry again in <xliff:g id="number">%d</xliff:g> seconds.</string>
+
<!-- Message shown when the system-provided fingerprint dialog is shown, asking for authentication -->
<string name="fingerprint_dialog_touch_sensor">Touch the fingerprint sensor</string>
<!-- Content description of the fingerprint icon when the system-provided fingerprint dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
@@ -440,13 +461,13 @@
<string name="data_connection_lte_plus">LTE+</string>
<!-- Content description of the data connection type 5Ge. [CHAR LIMIT=NONE] -->
- <string name="data_connection_5ge" translate="false">5Ge</string>
+ <string name="data_connection_5ge" translatable="false">5Ge</string>
<!-- Content description of the data connection type 5G. [CHAR LIMIT=NONE] -->
- <string name="data_connection_5g" translate="false">5G</string>
+ <string name="data_connection_5g" translatable="false">5G</string>
<!-- Content description of the data connection type 5G+. [CHAR LIMIT=NONE] -->
- <string name="data_connection_5g_plus" translate="false">5G+</string>
+ <string name="data_connection_5g_plus" translatable="false">5G+</string>
<!-- Content description of the data connection type CDMA. [CHAR LIMIT=NONE] -->
<string name="data_connection_cdma">1X</string>
@@ -860,6 +881,8 @@
<string name="quick_settings_notifications_label">Notifications</string>
<!-- QuickSettings: Flashlight [CHAR LIMIT=NONE] -->
<string name="quick_settings_flashlight_label">Flashlight</string>
+ <!-- QuickSettings: Flashlight, used when it's not available due to camera in use [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_flashlight_camera_in_use">Camera in use</string>
<!-- QuickSettings: Cellular detail panel title [CHAR LIMIT=NONE] -->
<string name="quick_settings_cellular_detail_title">Mobile data</string>
<!-- QuickSettings: Cellular detail panel, data usage title [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 6374191c4d7b..96fbcbba6e8b 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -314,6 +314,12 @@
<item name="*android:errorColor">?android:attr/colorError</item>
</style>
+ <style name="LockPatternStyleBiometricPrompt">
+ <item name="*android:regularColor">?android:attr/colorForeground</item>
+ <item name="*android:successColor">?android:attr/colorForeground</item>
+ <item name="*android:errorColor">?android:attr/colorError</item>
+ </style>
+
<style name="qs_theme" parent="@*android:style/Theme.DeviceDefault.QuickSettings">
<item name="lightIconTheme">@style/QSIconTheme</item>
<item name="darkIconTheme">@style/QSIconTheme</item>
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 31a538c65dea..447181813888 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -27,4 +27,6 @@ android_library {
// Enforce that the library is built against java 7 so that there are
// no compatibility issues with launcher
java_version: "1.7",
+
+ min_sdk_version: "26",
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 9228b178c76f..1cabee1ae679 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -109,4 +109,9 @@ interface ISystemUiProxy {
* Ends the system screen pinning.
*/
void stopScreenPinning() = 17;
+
+ /**
+ * Sets the shelf height and visibility.
+ */
+ void setShelfHeight(boolean visible, int shelfHeight) = 20;
}
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 2797042ac160..fe5a57a277a4 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
@@ -69,13 +69,6 @@ public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub {
}
@Override
- public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {
- for (PinnedStackListener listener : mListeners) {
- listener.onShelfVisibilityChanged(shelfVisible, shelfHeight);
- }
- }
-
- @Override
public void onMinimizedStateChanged(boolean isMinimized) {
for (PinnedStackListener listener : mListeners) {
listener.onMinimizedStateChanged(isMinimized);
@@ -143,8 +136,6 @@ public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub {
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {}
- public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {}
-
public void onMinimizedStateChanged(boolean isMinimized) {}
public void onActionsChanged(ParceledListSlice actions) {}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index 2ef042210e67..748f356c25b9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -115,4 +115,15 @@ public class RecentsAnimationControllerCompat {
Log.e(TAG, "Failed to clean up screenshot of recents animation", e);
}
}
+
+ /**
+ * @see {{@link IRecentsAnimationController#setWillFinishToHome(boolean)}}.
+ */
+ public void setWillFinishToHome(boolean willFinishToHome) {
+ try {
+ mAnimationController.setWillFinishToHome(willFinishToHome);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to set overview reached state", e);
+ }
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
index 5ddf89c08887..6186589ab086 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
@@ -17,6 +17,7 @@
package com.android.systemui.shared.system;
import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ITaskStackListener;
import android.content.ComponentName;
import android.os.IBinder;
import android.os.UserHandle;
@@ -106,6 +107,9 @@ public abstract class TaskStackChangeListener {
*/
public void onRecentTaskListUpdated() { }
+ /** @see ITaskStackListener#onRecentTaskListFrozenChanged(boolean) */
+ public void onRecentTaskListFrozenChanged(boolean frozen) { }
+
/**
* Checks that the current user matches the process. Since
* {@link android.app.ITaskStackListener} is not multi-user aware, handlers of
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index 820057a168a0..8d823ca34b39 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -224,6 +224,12 @@ public class TaskStackChangeListeners extends TaskStackListener {
mHandler.obtainMessage(H.ON_TASK_LIST_UPDATED).sendToTarget();
}
+ @Override
+ public void onRecentTaskListFrozenChanged(boolean frozen) {
+ mHandler.obtainMessage(H.ON_TASK_LIST_FROZEN_UNFROZEN, frozen ? 1 : 0, 0 /* unused */)
+ .sendToTarget();
+ }
+
private final class H extends Handler {
private static final int ON_TASK_STACK_CHANGED = 1;
private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
@@ -247,6 +253,7 @@ public class TaskStackChangeListeners extends TaskStackListener {
private static final int ON_TASK_DISPLAY_CHANGED = 20;
private static final int ON_TASK_LIST_UPDATED = 21;
private static final int ON_SINGLE_TASK_DISPLAY_EMPTY = 22;
+ private static final int ON_TASK_LIST_FROZEN_UNFROZEN = 23;
public H(Looper looper) {
@@ -408,6 +415,12 @@ public class TaskStackChangeListeners extends TaskStackListener {
}
break;
}
+ case ON_TASK_LIST_FROZEN_UNFROZEN: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onRecentTaskListFrozenChanged(msg.arg1 != 0);
+ }
+ break;
+ }
}
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 9f1a1fafeec6..22d1675cf17e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -53,7 +53,6 @@ public class WindowManagerWrapper {
public static final int TRANSIT_WALLPAPER_INTRA_CLOSE =
WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE;
public static final int TRANSIT_TASK_OPEN_BEHIND = WindowManager.TRANSIT_TASK_OPEN_BEHIND;
- public static final int TRANSIT_TASK_IN_PLACE = WindowManager.TRANSIT_TASK_IN_PLACE;
public static final int TRANSIT_ACTIVITY_RELAUNCH = WindowManager.TRANSIT_ACTIVITY_RELAUNCH;
public static final int TRANSIT_DOCK_TASK_FROM_RECENTS =
WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
@@ -143,14 +142,6 @@ public class WindowManagerWrapper {
}
}
- public void setShelfHeight(boolean visible, int shelfHeight) {
- try {
- WindowManagerGlobal.getWindowManagerService().setShelfHeight(visible, shelfHeight);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to set shelf height");
- }
- }
-
public void setRecentsVisibility(boolean visible) {
try {
WindowManagerGlobal.getWindowManagerService().setRecentsVisibility(visible);
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
index 3410883efac7..7cc16cf014fd 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
@@ -35,6 +35,7 @@ import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.internal.telephony.IccCardConstants;
@@ -59,6 +60,7 @@ public class CarrierTextController {
private static final String TAG = "CarrierTextController";
private final boolean mIsEmergencyCallCapable;
+ private final Handler mMainHandler;
private boolean mTelephonyCapable;
private boolean mShowMissingSim;
private boolean mShowAirplaneMode;
@@ -67,6 +69,7 @@ public class CarrierTextController {
private WifiManager mWifiManager;
private boolean[] mSimErrorState;
private final int mSimSlotsNumber;
+ @Nullable // Check for nullability before dispatching
private CarrierTextCallback mCarrierTextCallback;
private Context mContext;
private CharSequence mSeparator;
@@ -76,12 +79,12 @@ public class CarrierTextController {
new WakefulnessLifecycle.Observer() {
@Override
public void onFinishedWakingUp() {
- mCarrierTextCallback.finishedWakingUp();
+ if (mCarrierTextCallback != null) mCarrierTextCallback.finishedWakingUp();
}
@Override
public void onStartedGoingToSleep() {
- mCarrierTextCallback.startedGoingToSleep();
+ if (mCarrierTextCallback != null) mCarrierTextCallback.startedGoingToSleep();
}
};
@@ -170,8 +173,9 @@ public class CarrierTextController {
mSeparator = separator;
mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
mSimSlotsNumber = ((TelephonyManager) context.getSystemService(
- Context.TELEPHONY_SERVICE)).getPhoneCount();
+ Context.TELEPHONY_SERVICE)).getSupportedModemCount();
mSimErrorState = new boolean[mSimSlotsNumber];
+ mMainHandler = Dependency.get(Dependency.MAIN_HANDLER);
}
/**
@@ -230,7 +234,12 @@ public class CarrierTextController {
if (whitelistIpcs(() -> ConnectivityManager.from(mContext).isNetworkSupported(
ConnectivityManager.TYPE_MOBILE))) {
mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
- mKeyguardUpdateMonitor.registerCallback(mCallback);
+ // Keyguard update monitor expects callbacks from main thread
+ mMainHandler.post(() -> {
+ if (mKeyguardUpdateMonitor != null) {
+ mKeyguardUpdateMonitor.registerCallback(mCallback);
+ }
+ });
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
telephonyManager.listen(mPhoneStateListener,
LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
@@ -242,7 +251,12 @@ public class CarrierTextController {
} else {
mCarrierTextCallback = null;
if (mKeyguardUpdateMonitor != null) {
- mKeyguardUpdateMonitor.removeCallback(mCallback);
+ // Keyguard update monitor expects callbacks from main thread
+ mMainHandler.post(() -> {
+ if (mKeyguardUpdateMonitor != null) {
+ mKeyguardUpdateMonitor.removeCallback(mCallback);
+ }
+ });
mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
}
telephonyManager.listen(mPhoneStateListener, LISTEN_NONE);
@@ -374,10 +388,9 @@ public class CarrierTextController {
@VisibleForTesting
protected void postToCallback(CarrierTextCallbackInfo info) {
- Handler handler = Dependency.get(Dependency.MAIN_HANDLER);
final CarrierTextCallback callback = mCarrierTextCallback;
if (callback != null) {
- handler.post(() -> callback.updateCarrierInfo(info));
+ mMainHandler.post(() -> callback.updateCarrierInfo(info));
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index 6cc881dc5320..6f955f936c9a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -33,10 +33,9 @@ import android.widget.LinearLayout;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
import com.android.systemui.R;
-import java.util.Arrays;
-
/**
* Base class for PIN and password unlock screens.
*/
@@ -132,19 +131,19 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout
protected void verifyPasswordAndUnlock() {
if (mDismissing) return; // already verified but haven't been dismissed; don't do it again.
- final byte[] entry = getPasswordText();
+ final LockscreenCredential password = getEnteredCredential();
setPasswordEntryInputEnabled(false);
if (mPendingLockCheck != null) {
mPendingLockCheck.cancel(false);
}
final int userId = KeyguardUpdateMonitor.getCurrentUser();
- if (entry.length <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
+ if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
// to avoid accidental lockout, only count attempts that are long enough to be a
// real password. This may require some tweaking.
setPasswordEntryInputEnabled(true);
onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */);
- Arrays.fill(entry, (byte) 0);
+ password.zeroize();
return;
}
@@ -152,9 +151,9 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout
LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL);
LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
}
- mPendingLockCheck = LockPatternChecker.checkPassword(
+ mPendingLockCheck = LockPatternChecker.checkCredential(
mLockPatternUtils,
- entry,
+ password,
userId,
new LockPatternChecker.OnCheckCallback() {
@@ -166,7 +165,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout
}
onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */,
true /* isValidPassword */);
- Arrays.fill(entry, (byte) 0);
+ password.zeroize();
}
@Override
@@ -181,7 +180,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout
onPasswordChecked(userId, false /* matched */, timeoutMs,
true /* isValidPassword */);
}
- Arrays.fill(entry, (byte) 0);
+ password.zeroize();
}
@Override
@@ -192,7 +191,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout
LatencyTracker.getInstance(mContext).onActionEnd(
ACTION_CHECK_CREDENTIAL_UNLOCKED);
}
- Arrays.fill(entry, (byte) 0);
+ password.zeroize();
}
});
}
@@ -224,7 +223,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout
}
protected abstract void resetPasswordText(boolean animate, boolean announce);
- protected abstract byte[] getPasswordText();
+ protected abstract LockscreenCredential getEnteredCredential();
protected abstract void setPasswordEntryEnabled(boolean enabled);
protected abstract void setPasswordEntryInputEnabled(boolean enabled);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index eaaa3ed78654..f8f3dc8d6ecd 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -36,6 +36,7 @@ import android.view.inputmethod.InputMethodSubtype;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
+import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.TextViewInputDisabler;
import com.android.systemui.R;
@@ -86,13 +87,12 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView
mSecurityMessageDisplay.setMessage("");
}
final boolean wasDisabled = mPasswordEntry.isEnabled();
- // Don't set enabled password entry & showSoftInput when PasswordEntry is invisible or in
- // pausing stage.
+ setPasswordEntryEnabled(true);
+ setPasswordEntryInputEnabled(true);
+ // Don't call showSoftInput when PasswordEntry is invisible or in pausing stage.
if (!mResumed || !mPasswordEntry.isVisibleToUser()) {
return;
}
- setPasswordEntryEnabled(true);
- setPasswordEntryInputEnabled(true);
if (wasDisabled) {
mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
}
@@ -244,8 +244,8 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView
}
@Override
- protected byte[] getPasswordText() {
- return charSequenceToByteArray(mPasswordEntry.getText());
+ protected LockscreenCredential getEnteredCredential() {
+ return LockscreenCredential.createPasswordOrNone(mPasswordEntry.getText());
}
@Override
@@ -380,18 +380,4 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView
return getContext().getString(
com.android.internal.R.string.keyguard_accessibility_password_unlock);
}
-
- /*
- * This method avoids creating a new string when getting a byte array from EditView#getText().
- */
- private static byte[] charSequenceToByteArray(CharSequence chars) {
- if (chars == null) {
- return null;
- }
- byte[] bytes = new byte[chars.length()];
- for (int i = 0; i < chars.length(); i++) {
- bytes[i] = (byte) chars.charAt(i);
- }
- return bytes;
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 9c0688e20aa0..ef48a530e914 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -39,6 +39,7 @@ import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternView;
+import com.android.internal.widget.LockscreenCredential;
import com.android.settingslib.animation.AppearAnimationCreator;
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
@@ -297,9 +298,9 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL);
LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
}
- mPendingLockCheck = LockPatternChecker.checkPattern(
+ mPendingLockCheck = LockPatternChecker.checkCredential(
mLockPatternUtils,
- pattern,
+ LockscreenCredential.createPattern(pattern),
userId,
new LockPatternChecker.OnCheckCallback() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 274f739d8c29..c67deccb1f62 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -23,6 +23,7 @@ import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
+import com.android.internal.widget.LockscreenCredential;
import com.android.systemui.R;
/**
@@ -167,8 +168,8 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
}
@Override
- protected byte[] getPasswordText() {
- return charSequenceToByteArray(mPasswordEntry.getText());
+ protected LockscreenCredential getEnteredCredential() {
+ return LockscreenCredential.createPinOrNone(mPasswordEntry.getText());
}
@Override
@@ -266,18 +267,4 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
return getContext().getString(
com.android.internal.R.string.keyguard_accessibility_pin_unlock);
}
-
- /*
- * This method avoids creating a new string when getting a byte array from EditView#getText().
- */
- private static byte[] charSequenceToByteArray(CharSequence chars) {
- if (chars == null) {
- return null;
- }
- byte[] bytes = new byte[chars.length()];
- for (int i = 0; i < chars.length(); i++) {
- bytes[i] = (byte) chars.charAt(i);
- }
- return bytes;
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 35eb272bdaef..b9fe9334d14c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -49,7 +49,7 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SystemUIFactory;
-import com.android.systemui.statusbar.phone.UnlockMethodCache;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.InjectionInflationController;
public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSecurityView {
@@ -76,7 +76,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
// How much you need to drag the bouncer to trigger an auth retry (in dps.)
private static final float MIN_DRAG_SIZE = 10;
// How much to scale the default slop by, to avoid accidental drags.
- private static final float SLOP_SCALE = 2f;
+ private static final float SLOP_SCALE = 4f;
private KeyguardSecurityModel mSecurityModel;
private LockPatternUtils mLockPatternUtils;
@@ -94,7 +94,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
private final SpringAnimation mSpringAnimation;
private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
private final KeyguardUpdateMonitor mUpdateMonitor;
- private final UnlockMethodCache mUnlockMethodCache;
+ private final KeyguardStateController mKeyguardStateController;
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private float mLastTouchY = -1;
@@ -128,14 +128,14 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mSecurityModel = new KeyguardSecurityModel(context);
+ mSecurityModel = Dependency.get(KeyguardSecurityModel.class);
mLockPatternUtils = new LockPatternUtils(context);
mUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y);
mInjectionInflationController = new InjectionInflationController(
SystemUIFactory.getInstance().getRootComponent());
- mUnlockMethodCache = UnlockMethodCache.getInstance(context);
mViewConfiguration = ViewConfiguration.get(context);
+ mKeyguardStateController = Dependency.get(KeyguardStateController.class);
}
public void setSecurityCallback(SecurityCallback callback) {
@@ -272,7 +272,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
*/
private void updateBiometricRetry() {
SecurityMode securityMode = getSecurityMode();
- mSwipeUpToRetry = mUnlockMethodCache.isFaceAuthEnabled()
+ mSwipeUpToRetry = mKeyguardStateController.isFaceAuthEnabled()
&& securityMode != SecurityMode.SimPin
&& securityMode != SecurityMode.SimPuk
&& securityMode != SecurityMode.None;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
index bb89959b7a11..1395fd9e3482 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
@@ -25,6 +25,10 @@ import com.android.internal.telephony.IccCardConstants;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.Dependency;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
public class KeyguardSecurityModel {
/**
@@ -46,6 +50,7 @@ public class KeyguardSecurityModel {
private LockPatternUtils mLockPatternUtils;
+ @Inject
KeyguardSecurityModel(Context context) {
mContext = context;
mLockPatternUtils = new LockPatternUtils(context);
@@ -57,7 +62,7 @@ public class KeyguardSecurityModel {
mLockPatternUtils = utils;
}
- SecurityMode getSecurityMode(int userId) {
+ public SecurityMode getSecurityMode(int userId) {
KeyguardUpdateMonitor monitor = Dependency.get(KeyguardUpdateMonitor.class);
if (mIsPukScreenAvailable && SubscriptionManager.isValidSubscriptionId(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index af4e61b3f6bc..5d35169cf926 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -31,6 +31,7 @@ import android.app.PendingIntent;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
+import android.graphics.text.LineBreaker;
import android.net.Uri;
import android.os.Trace;
import android.provider.Settings;
@@ -152,6 +153,7 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe
mRowWithHeaderTextSize = mContext.getResources().getDimensionPixelSize(
R.dimen.header_row_font_size);
mTitle.setOnClickListener(this);
+ mTitle.setBreakStrategy(LineBreaker.BREAK_STRATEGY_BALANCED);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 63da5339efe7..5a1c9976f021 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -22,7 +22,6 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.os.Handler;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -135,7 +134,7 @@ public class KeyguardStatusView extends GridLayout implements
super(context, attrs, defStyle);
mIActivityManager = ActivityManager.getService();
mLockPatternUtils = new LockPatternUtils(getContext());
- mHandler = new Handler(Looper.myLooper());
+ mHandler = new Handler();
onDensityOrFontScaleChanged();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index f2a359e67e29..e73a27bb4eb2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -39,7 +39,6 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import static com.android.systemui.DejankUtils.whitelistIpcs;
-import static com.android.systemui.Dependency.MAIN_LOOPER_NAME;
import android.annotation.AnyThread;
import android.annotation.MainThread;
@@ -58,6 +57,7 @@ import android.content.IntentFilter;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricSourceType;
@@ -98,7 +98,9 @@ import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.widget.LockPatternUtils;
import com.android.settingslib.WirelessUtils;
+import com.android.systemui.DejankUtils;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.MainLooper;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -116,13 +118,14 @@ import java.util.TimeZone;
import java.util.function.Consumer;
import javax.inject.Inject;
-import javax.inject.Named;
+import javax.inject.Singleton;
/**
* Watches for updates that may be interesting to the keyguard, and provides
* the up to date information as well as a registration for callbacks that care
* to be updated.
*/
+@Singleton
public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
private static final String TAG = "KeyguardUpdateMonitor";
@@ -219,7 +222,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
private final Context mContext;
private final boolean mIsPrimaryUser;
- HashMap<Integer, SimData> mSimDatas = new HashMap<Integer, SimData>();
+ HashMap<Integer, SimData> mSimDatas = new HashMap<>();
HashMap<Integer, ServiceState> mServiceStates = new HashMap<Integer, ServiceState>();
private int mRingMode;
@@ -329,6 +332,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
private SparseBooleanArray mUserIsUnlocked = new SparseBooleanArray();
private SparseBooleanArray mUserHasTrust = new SparseBooleanArray();
private SparseBooleanArray mUserTrustIsManaged = new SparseBooleanArray();
+ private SparseBooleanArray mUserTrustIsUsuallyManaged = new SparseBooleanArray();
private SparseBooleanArray mUserFingerprintAuthenticated = new SparseBooleanArray();
private SparseBooleanArray mUserFaceAuthenticated = new SparseBooleanArray();
private SparseBooleanArray mUserFaceUnlockRunning = new SparseBooleanArray();
@@ -472,6 +476,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
public void onTrustManagedChanged(boolean managed, int userId) {
checkIsHandlerThread();
mUserTrustIsManaged.put(userId, managed);
+ mUserTrustIsUsuallyManaged.put(userId, mTrustManager.isTrustUsuallyManaged(userId));
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -925,6 +930,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
return mUserTrustIsManaged.get(userId) && !isTrustDisabled(userId);
}
+ /**
+ * Cached version of {@link TrustManager#isTrustUsuallyManaged(int)}.
+ */
+ public boolean isTrustUsuallyManaged(int userId) {
+ checkIsHandlerThread();
+ return mUserTrustIsUsuallyManaged.get(userId);
+ }
+
public boolean isUnlockingWithBiometricAllowed() {
return mStrongAuthTracker.isUnlockingWithBiometricAllowed();
}
@@ -1424,6 +1437,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
}
private void handleScreenTurnedOff() {
+ final String tag = "KeyguardUpdateMonitor#handleScreenTurnedOff";
+ DejankUtils.startDetectingBlockingIpcs(tag);
checkIsHandlerThread();
mHardwareFingerprintUnavailableRetryCount = 0;
mHardwareFaceUnavailableRetryCount = 0;
@@ -1433,6 +1448,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
cb.onScreenTurnedOff();
}
}
+ DejankUtils.stopDetectingBlockingIpcs(tag);
}
private void handleDreamingStateChanged(int dreamStart) {
@@ -1474,14 +1490,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
mUserIsUnlocked.put(userId, mUserManager.isUserUnlocked(userId));
}
- private void handleUserRemoved(int userId) {
+ @VisibleForTesting
+ void handleUserRemoved(int userId) {
checkIsHandlerThread();
mUserIsUnlocked.delete(userId);
+ mUserTrustIsUsuallyManaged.delete(userId);
}
@VisibleForTesting
@Inject
- protected KeyguardUpdateMonitor(Context context, @Named(MAIN_LOOPER_NAME) Looper mainLooper) {
+ protected KeyguardUpdateMonitor(Context context, @MainLooper Looper mainLooper) {
mContext = context;
mSubscriptionManager = SubscriptionManager.from(context);
mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
@@ -1662,7 +1680,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
e.rethrowAsRuntimeException();
}
- mTrustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE);
+ mTrustManager = context.getSystemService(TrustManager.class);
mTrustManager.registerTrustListener(this);
mLockPatternUtils = new LockPatternUtils(context);
mLockPatternUtils.registerStrongAuthTracker(mStrongAuthTracker);
@@ -1697,6 +1715,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
mUserIsUnlocked.put(user, mUserManager.isUserUnlocked(user));
mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
mLogoutEnabled = mDevicePolicyManager.isLogoutEnabled();
+ List<UserInfo> allUsers = mUserManager.getUsers();
+ for (UserInfo userInfo : allUsers) {
+ mUserTrustIsUsuallyManaged.put(userInfo.id,
+ mTrustManager.isTrustUsuallyManaged(userInfo.id));
+ }
updateAirplaneModeState();
TelephonyManager telephony =
@@ -2048,6 +2071,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
*/
private void handleUserSwitching(int userId, IRemoteCallback reply) {
checkIsHandlerThread();
+ mUserTrustIsUsuallyManaged.put(userId, mTrustManager.isTrustUsuallyManaged(userId));
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -2522,8 +2546,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
@MainThread
public void reportSimUnlocked(int subId) {
if (DEBUG_SIM_STATES) Log.v(TAG, "reportSimUnlocked(subId=" + subId + ")");
- int slotId = SubscriptionManager.getSlotIndex(subId);
- handleSimStateChange(subId, slotId, State.READY);
+ handleSimStateChange(subId, getSlotId(subId), State.READY);
}
/**
@@ -2596,6 +2619,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
}
}
+ private int getSlotId(int subId) {
+ if (!mSimDatas.containsKey(subId)) {
+ refreshSimState(subId, SubscriptionManager.getSlotIndex(subId));
+ }
+ return mSimDatas.get(subId).slotId;
+ }
+
private final TaskStackChangeListener
mTaskStackListener = new TaskStackChangeListener() {
@Override
@@ -2750,7 +2780,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
for (int i = 0; i < list.size(); i++) {
final SubscriptionInfo info = list.get(i);
final int id = info.getSubscriptionId();
- int slotId = SubscriptionManager.getSlotIndex(id);
+ int slotId = getSlotId(id);
if (state == getSimState(id) && bestSlotId > slotId) {
resultId = id;
bestSlotId = slotId;
@@ -2792,7 +2822,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
private void checkIsHandlerThread() {
if (!mHandler.getLooper().isCurrentThread()) {
- Log.wtf(TAG, "must call on mHandler's thread "
+ Log.wtfStack(TAG, "must call on mHandler's thread "
+ mHandler.getLooper().getThread() + ", not " + Thread.currentThread());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityBinder.java b/packages/SystemUI/src/com/android/systemui/ActivityBinder.java
deleted file mode 100644
index 2c8a67270d94..000000000000
--- a/packages/SystemUI/src/com/android/systemui/ActivityBinder.java
+++ /dev/null
@@ -1,44 +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;
-
-import android.app.Activity;
-
-import com.android.systemui.tuner.TunerActivity;
-
-import dagger.Binds;
-import dagger.Module;
-import dagger.multibindings.ClassKey;
-import dagger.multibindings.IntoMap;
-
-/**
- * Services and Activities that are injectable should go here.
- */
-@Module
-public abstract class ActivityBinder {
- /** Inject into TunerActivity. */
- @Binds
- @IntoMap
- @ClassKey(TunerActivity.class)
- public abstract Activity bindTunerActivity(TunerActivity activity);
-
- /** Inject into ForegroundServicesDialog. */
- @Binds
- @IntoMap
- @ClassKey(ForegroundServicesDialog.class)
- public abstract Activity bindForegroundServicesDialog(ForegroundServicesDialog activity);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/ComponentBinder.java b/packages/SystemUI/src/com/android/systemui/ComponentBinder.java
deleted file mode 100644
index 3b35c61e8eb2..000000000000
--- a/packages/SystemUI/src/com/android/systemui/ComponentBinder.java
+++ /dev/null
@@ -1,31 +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;
-
-import dagger.Binds;
-import dagger.Module;
-
-/**
- * Dagger Module that collects related sub-modules together.
- */
-@Module(includes = {ActivityBinder.class, ServiceBinder.class, SystemUIBinder.class})
-public abstract class ComponentBinder {
- /** */
- @Binds
- public abstract ContextComponentHelper bindComponentHelper(
- ContextComponentResolver componentHelper);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/ContextComponentHelper.java b/packages/SystemUI/src/com/android/systemui/ContextComponentHelper.java
deleted file mode 100644
index 2cf0f8dafcad..000000000000
--- a/packages/SystemUI/src/com/android/systemui/ContextComponentHelper.java
+++ /dev/null
@@ -1,34 +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;
-
-import android.app.Activity;
-import android.app.Service;
-
-/**
- * Interface necessary to make Dagger happy. See {@link ContextComponentResolver}.
- */
-public interface ContextComponentHelper {
- /** Turns a classname into an instance of the class or returns null. */
- Activity resolveActivity(String className);
-
- /** Turns a classname into an instance of the class or returns null. */
- Service resolveService(String className);
-
- /** Turns a classname into an instance of the class or returns null. */
- SystemUI resolveSystemUI(String className);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/ContextComponentResolver.java b/packages/SystemUI/src/com/android/systemui/ContextComponentResolver.java
deleted file mode 100644
index 995263240e2d..000000000000
--- a/packages/SystemUI/src/com/android/systemui/ContextComponentResolver.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.systemui;
-
-import android.app.Activity;
-import android.app.Service;
-
-import java.util.Map;
-
-import javax.inject.Inject;
-import javax.inject.Provider;
-import javax.inject.Singleton;
-
-/**
- * Used during Service and Activity instantiation to make them injectable.
- */
-@Singleton
-public class ContextComponentResolver implements ContextComponentHelper {
- private final Map<Class<?>, Provider<Activity>> mActivityCreators;
- private final Map<Class<?>, Provider<Service>> mServiceCreators;
- private final Map<Class<?>, Provider<SystemUI>> mSystemUICreators;
-
- @Inject
- ContextComponentResolver(
- Map<Class<?>, Provider<Activity>> activityCreators,
- Map<Class<?>, Provider<Service>> serviceCreators,
- Map<Class<?>, Provider<SystemUI>> systemUICreators) {
- mActivityCreators = activityCreators;
- mServiceCreators = serviceCreators;
- mSystemUICreators = systemUICreators;
- }
-
- /**
- * Looks up the Activity class name to see if Dagger has an instance of it.
- */
- @Override
- public Activity resolveActivity(String className) {
- return resolve(className, mActivityCreators);
- }
-
- /**
- * Looks up the Service class name to see if Dagger has an instance of it.
- */
- @Override
- public Service resolveService(String className) {
- return resolve(className, mServiceCreators);
- }
-
- /**
- * Looks up the SystemUI class name to see if Dagger has an instance of it.
- */
- @Override
- public SystemUI resolveSystemUI(String className) {
- return resolve(className, mSystemUICreators);
- }
-
- private <T> T resolve(String className, Map<Class<?>, Provider<T>> creators) {
- try {
- Class<?> clazz = Class.forName(className);
- Provider<T> provider = creators.get(clazz);
- return provider == null ? null : provider.get();
- } catch (ClassNotFoundException e) {
- return null;
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 7771f8655128..486d02c207db 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -17,6 +17,7 @@ package com.android.systemui;
import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.INotificationManager;
+import android.app.IWallpaperManager;
import android.content.res.Configuration;
import android.hardware.SensorPrivacyManager;
import android.hardware.display.NightDisplayListener;
@@ -30,6 +31,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.Preconditions;
+import com.android.keyguard.KeyguardSecurityModel;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.clock.ClockManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -38,6 +40,10 @@ import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.dagger.qualifiers.BgHandler;
+import com.android.systemui.dagger.qualifiers.BgLooper;
+import com.android.systemui.dagger.qualifiers.MainHandler;
+import com.android.systemui.dagger.qualifiers.MainLooper;
import com.android.systemui.dock.DockManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.ScreenLifecycle;
@@ -76,6 +82,7 @@ import com.android.systemui.statusbar.notification.row.ChannelEditorDialogContro
import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
@@ -97,7 +104,7 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NextAlarmController;
@@ -145,16 +152,16 @@ public class Dependency {
/**
* Key for getting a the main looper.
*/
- public static final String MAIN_LOOPER_NAME = "main_looper";
+ private static final String MAIN_LOOPER_NAME = "main_looper";
/**
* Key for getting a background Looper for background work.
*/
- public static final String BG_LOOPER_NAME = "background_looper";
+ private static final String BG_LOOPER_NAME = "background_looper";
/**
* Key for getting a background Handler for background work.
*/
- public static final String BG_HANDLER_NAME = "background_handler";
+ private static final String BG_HANDLER_NAME = "background_handler";
/**
* Key for getting a Handler for receiving time tick broadcasts on.
*/
@@ -162,7 +169,7 @@ public class Dependency {
/**
* Generic handler on the main thread.
*/
- public static final String MAIN_HANDLER_NAME = "main_handler";
+ private static final String MAIN_HANDLER_NAME = "main_handler";
/**
* An email address to send memory leak reports to by default.
@@ -221,7 +228,7 @@ public class Dependency {
@Inject Lazy<FlashlightController> mFlashlightController;
@Inject Lazy<UserSwitcherController> mUserSwitcherController;
@Inject Lazy<UserInfoController> mUserInfoController;
- @Inject Lazy<KeyguardMonitor> mKeyguardMonitor;
+ @Inject Lazy<KeyguardStateController> mKeyguardMonitor;
@Inject Lazy<KeyguardUpdateMonitor> mKeyguardUpdateMonitor;
@Inject Lazy<BatteryController> mBatteryController;
@Inject Lazy<NightDisplayListener> mNightDisplayListener;
@@ -297,10 +304,10 @@ public class Dependency {
@Inject Lazy<AutoHideController> mAutoHideController;
@Inject Lazy<ForegroundServiceNotificationListener> mForegroundServiceNotificationListener;
@Inject Lazy<PrivacyItemController> mPrivacyItemController;
- @Inject @Named(BG_LOOPER_NAME) Lazy<Looper> mBgLooper;
- @Inject @Named(BG_HANDLER_NAME) Lazy<Handler> mBgHandler;
- @Inject @Named(MAIN_LOOPER_NAME) Lazy<Looper> mMainLooper;
- @Inject @Named(MAIN_HANDLER_NAME) Lazy<Handler> mMainHandler;
+ @Inject @BgLooper Lazy<Looper> mBgLooper;
+ @Inject @BgHandler Lazy<Handler> mBgHandler;
+ @Inject @MainLooper Lazy<Looper> mMainLooper;
+ @Inject @MainHandler Lazy<Handler> mMainHandler;
@Inject @Named(TIME_TICK_HANDLER_NAME) Lazy<Handler> mTimeTickHandler;
@Nullable
@Inject @Named(LEAK_REPORT_EMAIL_NAME) Lazy<String> mLeakReportEmail;
@@ -316,6 +323,9 @@ public class Dependency {
@Inject Lazy<FalsingManager> mFalsingManager;
@Inject Lazy<SysUiState> mSysUiStateFlagsContainer;
@Inject Lazy<AlarmManager> mAlarmManager;
+ @Inject Lazy<KeyguardSecurityModel> mKeyguardSecurityModel;
+ @Inject Lazy<DozeParameters> mDozeParameters;
+ @Inject Lazy<IWallpaperManager> mWallpaperManager;
@Inject
public Dependency() {
@@ -355,7 +365,7 @@ public class Dependency {
mProviders.put(FlashlightController.class, mFlashlightController::get);
- mProviders.put(KeyguardMonitor.class, mKeyguardMonitor::get);
+ mProviders.put(KeyguardStateController.class, mKeyguardMonitor::get);
mProviders.put(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor::get);
@@ -501,6 +511,9 @@ public class Dependency {
mProviders.put(FalsingManager.class, mFalsingManager::get);
mProviders.put(SysUiState.class, mSysUiStateFlagsContainer::get);
mProviders.put(AlarmManager.class, mAlarmManager::get);
+ mProviders.put(KeyguardSecurityModel.class, mKeyguardSecurityModel::get);
+ mProviders.put(DozeParameters.class, mDozeParameters::get);
+ mProviders.put(IWallpaperManager.class, mWallpaperManager::get);
// TODO(b/118592525): to support multi-display , we start to add something which is
// per-display, while others may be global. I think it's time to add
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
deleted file mode 100644
index 4df7f0d440df..000000000000
--- a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui;
-
-import com.android.systemui.appops.AppOpsController;
-import com.android.systemui.appops.AppOpsControllerImpl;
-import com.android.systemui.classifier.FalsingManagerProxy;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.VolumeDialogController;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.power.PowerNotificationWarnings;
-import com.android.systemui.power.PowerUI;
-import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.QSTileHost;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.StatusBarStateControllerImpl;
-import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
-import com.android.systemui.statusbar.phone.ManagedProfileController;
-import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
-import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BatteryControllerImpl;
-import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
-import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastControllerImpl;
-import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
-import com.android.systemui.statusbar.policy.FlashlightController;
-import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
-import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.HotspotControllerImpl;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
-import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
-import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.LocationControllerImpl;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkControllerImpl;
-import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
-import com.android.systemui.statusbar.policy.RotationLockController;
-import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
-import com.android.systemui.statusbar.policy.SecurityController;
-import com.android.systemui.statusbar.policy.SecurityControllerImpl;
-import com.android.systemui.statusbar.policy.SensorPrivacyController;
-import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl;
-import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
-import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
-import com.android.systemui.tuner.TunerService;
-import com.android.systemui.tuner.TunerServiceImpl;
-import com.android.systemui.volume.VolumeDialogControllerImpl;
-
-import dagger.Binds;
-import dagger.Module;
-
-/**
- * Maps interfaces to implementations for use with Dagger.
- */
-@Module
-public abstract class DependencyBinder {
-
- /**
- */
- @Binds
- public abstract ActivityStarter provideActivityStarter(ActivityStarterDelegate delegate);
-
- /**
- */
- @Binds
- public abstract BluetoothController provideBluetoothController(
- BluetoothControllerImpl controllerImpl);
-
- /**
- */
- @Binds
- public abstract LocationController provideLocationController(
- LocationControllerImpl controllerImpl);
-
- /**
- */
- @Binds
- public abstract RotationLockController provideRotationLockController(
- RotationLockControllerImpl controllerImpl);
-
- /**
- */
- @Binds
- public abstract NetworkController provideNetworkController(
- NetworkControllerImpl controllerImpl);
-
- /**
- */
- @Binds
- public abstract ZenModeController provideZenModeController(
- ZenModeControllerImpl controllerImpl);
-
- /**
- */
- @Binds
- public abstract HotspotController provideHotspotController(
- HotspotControllerImpl controllerImpl);
-
- /**
- */
- @Binds
- public abstract AppOpsController provideAppOpsController(
- AppOpsControllerImpl controllerImpl);
-
- /**
- */
- @Binds
- public abstract NotificationRemoteInputManager.Callback provideNotificationRemoteInputManager(
- StatusBarRemoteInputCallback callbackImpl);
-
- /**
- */
- @Binds
- public abstract CastController provideCastController(CastControllerImpl controllerImpl);
-
- /**
- */
- @Binds
- public abstract FlashlightController provideFlashlightController(
- FlashlightControllerImpl controllerImpl);
-
- /**
- */
- @Binds
- public abstract KeyguardMonitor provideKeyguardMonitor(KeyguardMonitorImpl controllerImpl);
-
- /**
- */
- @Binds
- public abstract UserInfoController provideUserInfoContrller(
- UserInfoControllerImpl controllerImpl);
-
- /**
- */
- @Binds
- public abstract BatteryController provideBatteryController(
- BatteryControllerImpl controllerImpl);
-
- /**
- */
- @Binds
- public abstract ManagedProfileController provideManagedProfileController(
- ManagedProfileControllerImpl controllerImpl);
-
- /**
- */
- @Binds
- public abstract NextAlarmController provideNextAlarmController(
- NextAlarmControllerImpl controllerImpl);
-
- /**
- */
- @Binds
- public abstract SecurityController provideSecurityController(
- SecurityControllerImpl controllerImpl);
-
- /**
- */
- @Binds
- public abstract TunerService provideTunerService(TunerServiceImpl controllerImpl);
-
- /**
- */
- @Binds
- public abstract DarkIconDispatcher provideDarkIconDispatcher(
- DarkIconDispatcherImpl controllerImpl);
-
- /**
- */
- @Binds
- public abstract StatusBarStateController provideStatusBarStateController(
- StatusBarStateControllerImpl controllerImpl);
-
- /**
- */
- @Binds
- public abstract StatusBarIconController provideStatusBarIconController(
- StatusBarIconControllerImpl controllerImpl);
-
- /**
- */
- @Binds
- public abstract ExtensionController provideExtensionController(
- ExtensionControllerImpl controllerImpl);
-
- /**
- */
- @Binds
- public abstract VolumeDialogController provideVolumeDialogController(
- VolumeDialogControllerImpl controllerImpl);
-
- /**
- */
- @Binds
- public abstract PowerUI.WarningsUI provideWarningsUi(PowerNotificationWarnings controllerImpl);
-
- /**
- */
- @Binds
- public abstract SensorPrivacyController provideSensorPrivacyControllerImpl(
- SensorPrivacyControllerImpl controllerImpl);
-
- /**
- */
- @Binds
- public abstract QSHost provideQsHost(QSTileHost controllerImpl);
-
- /**
- */
- @Binds
- public abstract FalsingManager provideFalsingmanager(FalsingManagerProxy falsingManagerImpl);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
deleted file mode 100644
index 239cbfe38975..000000000000
--- a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui;
-
-import static com.android.systemui.Dependency.BG_HANDLER_NAME;
-import static com.android.systemui.Dependency.BG_LOOPER_NAME;
-import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
-import static com.android.systemui.Dependency.MAIN_LOOPER_NAME;
-import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
-
-import android.annotation.Nullable;
-import android.app.AlarmManager;
-import android.app.INotificationManager;
-import android.content.Context;
-import android.hardware.SensorPrivacyManager;
-import android.hardware.display.NightDisplayListener;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Process;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.util.DisplayMetrics;
-import android.view.IWindowManager;
-import android.view.WindowManagerGlobal;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.systemui.plugins.PluginInitializerImpl;
-import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.shared.plugins.PluginManagerImpl;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
-import com.android.systemui.shared.system.PackageManagerWrapper;
-import com.android.systemui.statusbar.NavigationBarController;
-import com.android.systemui.statusbar.phone.AutoHideController;
-import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.DataSaverController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.util.leak.LeakDetector;
-
-import javax.inject.Named;
-import javax.inject.Singleton;
-
-import dagger.Module;
-import dagger.Provides;
-
-/**
- * Provides dependencies for the root component of sysui injection.
- * See SystemUI/docs/dagger.md
- */
-@Module
-public class DependencyProvider {
-
- @Singleton
- @Provides
- @Named(TIME_TICK_HANDLER_NAME)
- public Handler provideHandler() {
- HandlerThread thread = new HandlerThread("TimeTick");
- thread.start();
- return new Handler(thread.getLooper());
- }
-
- @Singleton
- @Provides
- @Named(BG_LOOPER_NAME)
- public Looper provideBgLooper() {
- HandlerThread thread = new HandlerThread("SysUiBg",
- Process.THREAD_PRIORITY_BACKGROUND);
- thread.start();
- return thread.getLooper();
- }
-
- /** Main Looper */
- @Singleton
- @Provides
- @Named(MAIN_LOOPER_NAME)
- public Looper provideMainLooper() {
- return Looper.getMainLooper();
- }
-
- @Singleton
- @Provides
- @Named(BG_HANDLER_NAME)
- public Handler provideBgHandler(@Named(BG_LOOPER_NAME) Looper bgLooper) {
- return new Handler(bgLooper);
- }
-
- @Singleton
- @Provides
- @Named(MAIN_HANDLER_NAME)
- public Handler provideMainHandler(@Named(MAIN_LOOPER_NAME) Looper mainLooper) {
- return new Handler(mainLooper);
- }
-
- @Singleton
- @Provides
- public DataSaverController provideDataSaverController(NetworkController networkController) {
- return networkController.getDataSaverController();
- }
-
- @Singleton
- @Provides
- @Nullable
- public LocalBluetoothManager provideLocalBluetoothController(Context context,
- @Named(BG_HANDLER_NAME) Handler bgHandler) {
- return LocalBluetoothManager.create(context, bgHandler,
- UserHandle.ALL);
- }
-
- @Singleton
- @Provides
- public MetricsLogger provideMetricsLogger() {
- return new MetricsLogger();
- }
-
- @Singleton
- @Provides
- public IWindowManager provideIWindowManager() {
- return WindowManagerGlobal.getWindowManagerService();
- }
-
- @Singleton
- @Provides
- public IStatusBarService provideIStatusBarService() {
- return IStatusBarService.Stub.asInterface(
- ServiceManager.getService(Context.STATUS_BAR_SERVICE));
- }
-
- /** */
- @Singleton
- @Provides
- public INotificationManager provideINotificationManager() {
- return INotificationManager.Stub.asInterface(
- ServiceManager.getService(Context.NOTIFICATION_SERVICE));
- }
-
- @Singleton
- @Provides
- // Single instance of DisplayMetrics, gets updated by StatusBar, but can be used
- // anywhere it is needed.
- public DisplayMetrics provideDisplayMetrics() {
- return new DisplayMetrics();
- }
-
- @Singleton
- @Provides
- public SensorPrivacyManager provideSensorPrivacyManager(Context context) {
- return context.getSystemService(SensorPrivacyManager.class);
- }
-
- @Singleton
- @Provides
- public LeakDetector provideLeakDetector() {
- return LeakDetector.create();
-
- }
-
- @Singleton
- @Provides
- public NightDisplayListener provideNightDisplayListener(Context context,
- @Named(BG_HANDLER_NAME) Handler bgHandler) {
- return new NightDisplayListener(context, bgHandler);
- }
-
- @Singleton
- @Provides
- public PluginManager providePluginManager(Context context) {
- return new PluginManagerImpl(context, new PluginInitializerImpl());
- }
-
- @Singleton
- @Provides
- public NavigationBarController provideNavigationBarController(Context context,
- @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
- return new NavigationBarController(context, mainHandler);
- }
-
- @Singleton
- @Provides
- public ConfigurationController provideConfigurationController(Context context) {
- return new ConfigurationControllerImpl(context);
- }
-
- @Singleton
- @Provides
- public AutoHideController provideAutoHideController(Context context,
- @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
- return new AutoHideController(context, mainHandler);
- }
-
- @Singleton
- @Provides
- public ActivityManagerWrapper provideActivityManagerWrapper() {
- return ActivityManagerWrapper.getInstance();
- }
-
- @Singleton
- @Provides
- public DevicePolicyManagerWrapper provideDevicePolicyManagerWrapper() {
- return DevicePolicyManagerWrapper.getInstance();
- }
-
- @Singleton
- @Provides
- public PackageManagerWrapper providePackageManagerWrapper() {
- return PackageManagerWrapper.getInstance();
- }
-
- @Singleton
- @Provides
- public DeviceProvisionedController provideDeviceProvisionedController(Context context,
- @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
- return new DeviceProvisionedControllerImpl(context, mainHandler);
- }
-
- /** */
- @Singleton
- @Provides
- public AlarmManager provideAlarmManager(Context context) {
- return context.getSystemService(AlarmManager.class);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java
index 0e079e36a175..362014f51e36 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java
@@ -50,13 +50,13 @@ public class ForegroundServiceLifetimeExtender implements NotificationLifetimeEx
@Override
public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
- if ((entry.notification.getNotification().flags
+ if ((entry.getSbn().getNotification().flags
& Notification.FLAG_FOREGROUND_SERVICE) == 0) {
return false;
}
long currentTime = System.currentTimeMillis();
- return currentTime - entry.notification.getPostTime() < MIN_FGS_TIME_MS;
+ return currentTime - entry.getSbn().getPostTime() < MIN_FGS_TIME_MS;
}
@Override
@@ -79,11 +79,13 @@ public class ForegroundServiceLifetimeExtender implements NotificationLifetimeEx
if (mManagedEntries.contains(entry)) {
mManagedEntries.remove(entry);
if (mNotificationSafeToRemoveCallback != null) {
- mNotificationSafeToRemoveCallback.onSafeToRemove(entry.key);
+ mNotificationSafeToRemoveCallback.onSafeToRemove(entry.getKey());
}
}
};
- mHandler.postDelayed(r, MIN_FGS_TIME_MS);
+ long delayAmt = MIN_FGS_TIME_MS
+ - (System.currentTimeMillis() - entry.getSbn().getPostTime());
+ mHandler.postDelayed(r, delayAmt);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
index f9d877142d21..4a3b6df6ac90 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
@@ -50,12 +50,12 @@ public class ForegroundServiceNotificationListener {
notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
public void onPendingEntryAdded(NotificationEntry entry) {
- addNotification(entry.notification, entry.getImportance());
+ addNotification(entry.getSbn(), entry.getImportance());
}
@Override
public void onPostEntryUpdated(NotificationEntry entry) {
- updateNotification(entry.notification, entry.getImportance());
+ updateNotification(entry.getSbn(), entry.getImportance());
}
@Override
@@ -63,7 +63,7 @@ public class ForegroundServiceNotificationListener {
NotificationEntry entry,
NotificationVisibility visibility,
boolean removedByUser) {
- removeNotification(entry.notification);
+ removeNotification(entry.getSbn());
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index bd91333100bd..29a7167394ae 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -20,6 +20,7 @@ import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Rect;
import android.os.HandlerThread;
+import android.os.Trace;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.util.Size;
@@ -37,6 +38,8 @@ import com.android.systemui.statusbar.phone.DozeParameters;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import javax.inject.Inject;
+
/**
* Default built-in wallpaper that simply shows a static image.
*/
@@ -48,8 +51,16 @@ public class ImageWallpaper extends WallpaperService {
private static final int DELAY_FINISH_RENDERING = 1000;
private static final int INTERVAL_WAIT_FOR_RENDERING = 100;
private static final int PATIENCE_WAIT_FOR_RENDERING = 10;
+ private static final boolean DEBUG = true;
+ private final DozeParameters mDozeParameters;
private HandlerThread mWorker;
+ @Inject
+ public ImageWallpaper(DozeParameters dozeParameters) {
+ super();
+ mDozeParameters = dozeParameters;
+ }
+
@Override
public void onCreate() {
super.onCreate();
@@ -59,7 +70,7 @@ public class ImageWallpaper extends WallpaperService {
@Override
public Engine onCreateEngine() {
- return new GLEngine(this);
+ return new GLEngine(this, mDozeParameters);
}
@Override
@@ -87,9 +98,9 @@ public class ImageWallpaper extends WallpaperService {
// This variable can only be accessed in synchronized block.
private boolean mWaitingForRendering;
- GLEngine(Context context) {
+ GLEngine(Context context, DozeParameters dozeParameters) {
mNeedTransition = ActivityManager.isHighEndGfx()
- && !DozeParameters.getInstance(context).getDisplayNeedsBlanking();
+ && !dozeParameters.getDisplayNeedsBlanking();
// We will preserve EGL context when we are in lock screen or aod
// to avoid janking in following transition, we need to release when back to home.
@@ -125,6 +136,10 @@ public class ImageWallpaper extends WallpaperService {
@Override
public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
if (!mNeedTransition) return;
+ if (DEBUG) {
+ Log.d(TAG, "onAmbientModeChanged: inAmbient=" + inAmbientMode
+ + ", duration=" + animationDuration);
+ }
mWorker.getThreadHandler().post(
() -> mRenderer.updateAmbientMode(inAmbientMode, animationDuration));
if (inAmbientMode && animationDuration == 0) {
@@ -184,17 +199,32 @@ public class ImageWallpaper extends WallpaperService {
@Override
public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
+ if (DEBUG) {
+ Log.d(TAG, "onSurfaceRedrawNeeded: mNeedRedraw=" + mNeedRedraw);
+ }
+
mWorker.getThreadHandler().post(() -> {
if (mNeedRedraw) {
- preRender();
- requestRender();
- postRender();
+ drawFrame();
mNeedRedraw = false;
}
});
}
@Override
+ public void onVisibilityChanged(boolean visible) {
+ if (DEBUG) {
+ Log.d(TAG, "wallpaper visibility changes: " + visible);
+ }
+ }
+
+ private void drawFrame() {
+ preRender();
+ requestRender();
+ postRender();
+ }
+
+ @Override
public void onStatePostChange() {
// When back to home, we try to release EGL, which is preserved in lock screen or aod.
if (mController.getState() == StatusBarState.SHADE) {
@@ -205,7 +235,9 @@ public class ImageWallpaper extends WallpaperService {
@Override
public void preRender() {
// This method should only be invoked from worker thread.
+ Trace.beginSection("ImageWallpaper#preRender");
preRenderInternal();
+ Trace.endSection();
}
private void preRenderInternal() {
@@ -240,7 +272,9 @@ public class ImageWallpaper extends WallpaperService {
@Override
public void requestRender() {
// This method should only be invoked from worker thread.
+ Trace.beginSection("ImageWallpaper#requestRender");
requestRenderInternal();
+ Trace.endSection();
}
private void requestRenderInternal() {
@@ -263,8 +297,10 @@ public class ImageWallpaper extends WallpaperService {
@Override
public void postRender() {
// This method should only be invoked from worker thread.
+ Trace.beginSection("ImageWallpaper#postRender");
notifyWaitingThread();
scheduleFinishRendering();
+ Trace.endSection();
}
private void notifyWaitingThread() {
@@ -289,12 +325,14 @@ public class ImageWallpaper extends WallpaperService {
}
private void finishRendering() {
+ Trace.beginSection("ImageWallpaper#finishRendering");
if (mEglHelper != null) {
mEglHelper.destroyEglSurface();
if (!needPreserveEglContext()) {
mEglHelper.destroyEglContext();
}
}
+ Trace.endSection();
}
private boolean needPreserveEglContext() {
@@ -310,9 +348,9 @@ public class ImageWallpaper extends WallpaperService {
boolean isHighEndGfx = ActivityManager.isHighEndGfx();
out.print(prefix); out.print("isHighEndGfx="); out.println(isHighEndGfx);
- DozeParameters dozeParameters = DozeParameters.getInstance(getApplicationContext());
out.print(prefix); out.print("displayNeedsBlanking=");
- out.println(dozeParameters != null ? dozeParameters.getDisplayNeedsBlanking() : "null");
+ out.println(
+ mDozeParameters != null ? mDozeParameters.getDisplayNeedsBlanking() : "null");
out.print(prefix); out.print("mNeedTransition="); out.println(mNeedTransition);
out.print(prefix); out.print("StatusBarState=");
diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
index 50f1b44b05b1..30a60abfcd86 100644
--- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java
+++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
@@ -16,6 +16,8 @@
package com.android.systemui;
+import static android.os.PowerManager.WAKE_REASON_UNKNOWN;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -28,18 +30,33 @@ import android.os.SystemClock;
import com.android.internal.util.LatencyTracker;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
-import com.android.systemui.statusbar.phone.StatusBar;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
/**
* Class that only runs on debuggable builds that listens to broadcasts that simulate actions in the
* system that are used for testing the latency.
*/
+@Singleton
public class LatencyTester extends SystemUI {
- private static final String ACTION_FINGERPRINT_WAKE =
+ private static final String
+ ACTION_FINGERPRINT_WAKE =
"com.android.systemui.latency.ACTION_FINGERPRINT_WAKE";
- private static final String ACTION_TURN_ON_SCREEN =
+ private static final String
+ ACTION_TURN_ON_SCREEN =
"com.android.systemui.latency.ACTION_TURN_ON_SCREEN";
+ private final BiometricUnlockController mBiometricUnlockController;
+ private final PowerManager mPowerManager;
+
+ @Inject
+ public LatencyTester(Context context, BiometricUnlockController biometricUnlockController,
+ PowerManager powerManager) {
+ super(context);
+ mBiometricUnlockController = biometricUnlockController;
+ mPowerManager = powerManager;
+ }
@Override
public void start() {
@@ -64,19 +81,17 @@ public class LatencyTester extends SystemUI {
}
private void fakeTurnOnScreen() {
- PowerManager powerManager = mContext.getSystemService(PowerManager.class);
if (LatencyTracker.isEnabled(mContext)) {
LatencyTracker.getInstance(mContext).onActionStart(
LatencyTracker.ACTION_TURN_ON_SCREEN);
}
- powerManager.wakeUp(SystemClock.uptimeMillis(), "android.policy:LATENCY_TESTS");
+ mPowerManager.wakeUp(
+ SystemClock.uptimeMillis(), WAKE_REASON_UNKNOWN, "android.policy:LATENCY_TESTS");
}
private void fakeWakeAndUnlock() {
- BiometricUnlockController biometricUnlockController = getComponent(StatusBar.class)
- .getBiometricUnlockController();
- biometricUnlockController.onBiometricAcquired(BiometricSourceType.FINGERPRINT);
- biometricUnlockController.onBiometricAuthenticated(
+ mBiometricUnlockController.onBiometricAcquired(BiometricSourceType.FINGERPRINT);
+ mBiometricUnlockController.onBiometricAuthenticated(
KeyguardUpdateMonitor.getCurrentUser(), BiometricSourceType.FINGERPRINT);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index f38b4f259c88..ad209861d273 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -25,9 +25,6 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M
import static com.android.systemui.tuner.TunablePadding.FLAG_END;
import static com.android.systemui.tuner.TunablePadding.FLAG_START;
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
import android.annotation.Dimension;
import android.app.ActivityManager;
import android.app.Fragment;
@@ -52,7 +49,6 @@ import android.os.SystemProperties;
import android.provider.Settings.Secure;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.MathUtils;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.Gravity;
@@ -64,9 +60,6 @@ import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -78,10 +71,7 @@ import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.SecureSetting;
-import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.statusbar.phone.NavigationBarTransitions;
-import com.android.systemui.statusbar.phone.NavigationModeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.tuner.TunablePadding;
import com.android.systemui.tuner.TunerService;
@@ -95,8 +85,7 @@ import java.util.List;
* An overlay that draws screen decorations in software (e.g for rounded corners or display cutout)
* for antialiasing and emulation purposes.
*/
-public class ScreenDecorations extends SystemUI implements Tunable,
- NavigationBarTransitions.DarkIntensityListener {
+public class ScreenDecorations extends SystemUI implements Tunable {
private static final boolean DEBUG = false;
private static final String TAG = "ScreenDecorations";
@@ -120,15 +109,11 @@ public class ScreenDecorations extends SystemUI implements Tunable,
private float mDensity;
private WindowManager mWindowManager;
private int mRotation;
- private boolean mAssistHintVisible;
private DisplayCutoutView mCutoutTop;
private DisplayCutoutView mCutoutBottom;
private SecureSetting mColorInversionSetting;
private boolean mPendingRotationChange;
private Handler mHandler;
- private boolean mAssistHintBlocked = false;
- private boolean mIsReceivingNavBarColor = false;
- private boolean mInGesturalMode;
/**
* Converts a set of {@link Rect}s into a {@link Region}
@@ -147,166 +132,15 @@ public class ScreenDecorations extends SystemUI implements Tunable,
return result;
}
+ public ScreenDecorations(Context context) {
+ super(context);
+ }
+
@Override
public void start() {
mHandler = startHandlerThread();
mHandler.post(this::startOnScreenDecorationsThread);
setupStatusBarPaddingIfNeeded();
- putComponent(ScreenDecorations.class, this);
- mInGesturalMode = QuickStepContract.isGesturalMode(
- Dependency.get(NavigationModeController.class)
- .addListener(this::handleNavigationModeChange));
- }
-
- @VisibleForTesting
- void handleNavigationModeChange(int navigationMode) {
- if (!mHandler.getLooper().isCurrentThread()) {
- mHandler.post(() -> handleNavigationModeChange(navigationMode));
- return;
- }
- boolean inGesturalMode = QuickStepContract.isGesturalMode(navigationMode);
- if (mInGesturalMode != inGesturalMode) {
- mInGesturalMode = inGesturalMode;
-
- if (mInGesturalMode && mOverlay == null) {
- setupDecorations();
- if (mOverlay != null) {
- updateLayoutParams();
- }
- }
- }
- }
-
- /**
- * Returns an animator that animates the given view from start to end over durationMs. Start and
- * end represent total animation progress: 0 is the start, 1 is the end, 1.1 would be an
- * overshoot.
- */
- Animator getHandleAnimator(View view, float start, float end, boolean isLeft, long durationMs,
- Interpolator interpolator) {
- // Note that lerp does allow overshoot, in cases where start and end are outside of [0,1].
- float scaleStart = MathUtils.lerp(2f, 1f, start);
- float scaleEnd = MathUtils.lerp(2f, 1f, end);
- Animator scaleX = ObjectAnimator.ofFloat(view, View.SCALE_X, scaleStart, scaleEnd);
- Animator scaleY = ObjectAnimator.ofFloat(view, View.SCALE_Y, scaleStart, scaleEnd);
- float translationStart = MathUtils.lerp(0.2f, 0f, start);
- float translationEnd = MathUtils.lerp(0.2f, 0f, end);
- int xDirection = isLeft ? -1 : 1;
- Animator translateX = ObjectAnimator.ofFloat(view, View.TRANSLATION_X,
- xDirection * translationStart * view.getWidth(),
- xDirection * translationEnd * view.getWidth());
- Animator translateY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y,
- translationStart * view.getHeight(), translationEnd * view.getHeight());
-
- AnimatorSet set = new AnimatorSet();
- set.play(scaleX).with(scaleY);
- set.play(scaleX).with(translateX);
- set.play(scaleX).with(translateY);
- set.setDuration(durationMs);
- set.setInterpolator(interpolator);
- return set;
- }
-
- private void fade(View view, boolean fadeIn, boolean isLeft) {
- if (fadeIn) {
- view.animate().cancel();
- view.setAlpha(1f);
- view.setVisibility(View.VISIBLE);
-
- // A piecewise spring-like interpolation.
- // End value in one animator call must match the start value in the next, otherwise
- // there will be a discontinuity.
- AnimatorSet anim = new AnimatorSet();
- Animator first = getHandleAnimator(view, 0, 1.1f, isLeft, 750,
- new PathInterpolator(0, 0.45f, .67f, 1f));
- Interpolator secondInterpolator = new PathInterpolator(0.33f, 0, 0.67f, 1f);
- Animator second = getHandleAnimator(view, 1.1f, 0.97f, isLeft, 400,
- secondInterpolator);
- Animator third = getHandleAnimator(view, 0.97f, 1.02f, isLeft, 400,
- secondInterpolator);
- Animator fourth = getHandleAnimator(view, 1.02f, 1f, isLeft, 400,
- secondInterpolator);
- anim.play(first).before(second);
- anim.play(second).before(third);
- anim.play(third).before(fourth);
- anim.start();
- } else {
- view.animate().cancel();
- view.animate()
- .setInterpolator(new AccelerateInterpolator(1.5f))
- .setDuration(250)
- .alpha(0f);
- }
-
- }
-
- /**
- * Controls the visibility of the assist gesture handles.
- *
- * @param visible whether the handles should be shown
- */
- public void setAssistHintVisible(boolean visible) {
- if (!mHandler.getLooper().isCurrentThread()) {
- mHandler.post(() -> setAssistHintVisible(visible));
- return;
- }
-
- if (mAssistHintBlocked && visible) {
- if (VERBOSE) {
- Log.v(TAG, "Assist hint blocked, cannot make it visible");
- }
- return;
- }
-
- if (mOverlay == null || mBottomOverlay == null) {
- return;
- }
-
- if (mAssistHintVisible != visible) {
- mAssistHintVisible = visible;
-
- CornerHandleView assistHintTopLeft = mOverlay.findViewById(R.id.assist_hint_left);
- CornerHandleView assistHintTopRight = mOverlay.findViewById(R.id.assist_hint_right);
- CornerHandleView assistHintBottomLeft = mBottomOverlay.findViewById(
- R.id.assist_hint_left);
- CornerHandleView assistHintBottomRight = mBottomOverlay.findViewById(
- R.id.assist_hint_right);
-
- switch (mRotation) {
- case RotationUtils.ROTATION_NONE:
- fade(assistHintBottomLeft, mAssistHintVisible, /* isLeft = */ true);
- fade(assistHintBottomRight, mAssistHintVisible, /* isLeft = */ false);
- break;
- case RotationUtils.ROTATION_LANDSCAPE:
- fade(assistHintTopRight, mAssistHintVisible, /* isLeft = */ true);
- fade(assistHintBottomRight, mAssistHintVisible, /* isLeft = */ false);
- break;
- case RotationUtils.ROTATION_SEASCAPE:
- fade(assistHintTopLeft, mAssistHintVisible, /* isLeft = */ false);
- fade(assistHintBottomLeft, mAssistHintVisible, /* isLeft = */ true);
- break;
- case RotationUtils.ROTATION_UPSIDE_DOWN:
- fade(assistHintTopLeft, mAssistHintVisible, /* isLeft = */ false);
- fade(assistHintTopRight, mAssistHintVisible, /* isLeft = */ true);
- break;
- }
- }
- updateWindowVisibilities();
- }
-
- /**
- * Prevents the assist hint from becoming visible even if `mAssistHintVisible` is true.
- */
- public void setAssistHintBlocked(boolean blocked) {
- if (!mHandler.getLooper().isCurrentThread()) {
- mHandler.post(() -> setAssistHintBlocked(blocked));
- return;
- }
-
- mAssistHintBlocked = blocked;
- if (mAssistHintVisible && mAssistHintBlocked) {
- hideAssistHandles();
- }
}
@VisibleForTesting
@@ -316,15 +150,11 @@ public class ScreenDecorations extends SystemUI implements Tunable,
return thread.getThreadHandler();
}
- private boolean shouldHostHandles() {
- return mInGesturalMode;
- }
-
private void startOnScreenDecorationsThread() {
mRotation = RotationUtils.getExactRotation(mContext);
mWindowManager = mContext.getSystemService(WindowManager.class);
updateRoundedCornerRadii();
- if (hasRoundedCorners() || shouldDrawCutout() || shouldHostHandles()) {
+ if (hasRoundedCorners() || shouldDrawCutout()) {
setupDecorations();
}
@@ -501,26 +331,10 @@ public class ScreenDecorations extends SystemUI implements Tunable,
if (mOverlay != null) {
updateLayoutParams();
updateViews();
- if (mAssistHintVisible) {
- // If assist handles are visible, hide them without animation and then make them
- // show once again (with corrected rotation).
- hideAssistHandles();
- setAssistHintVisible(true);
- }
}
}
}
- private void hideAssistHandles() {
- if (mOverlay != null && mBottomOverlay != null) {
- mOverlay.findViewById(R.id.assist_hint_left).setVisibility(View.GONE);
- mOverlay.findViewById(R.id.assist_hint_right).setVisibility(View.GONE);
- mBottomOverlay.findViewById(R.id.assist_hint_left).setVisibility(View.GONE);
- mBottomOverlay.findViewById(R.id.assist_hint_right).setVisibility(View.GONE);
- mAssistHintVisible = false;
- }
- }
-
private void updateRoundedCornerRadii() {
final int newRoundedDefault = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.rounded_corner_radius);
@@ -569,52 +383,12 @@ public class ScreenDecorations extends SystemUI implements Tunable,
updateView(bottomRight, Gravity.TOP | Gravity.LEFT, 0);
}
- updateAssistantHandleViews();
mCutoutTop.setRotation(mRotation);
mCutoutBottom.setRotation(mRotation);
updateWindowVisibilities();
}
- private void updateAssistantHandleViews() {
- View assistHintTopLeft = mOverlay.findViewById(R.id.assist_hint_left);
- View assistHintTopRight = mOverlay.findViewById(R.id.assist_hint_right);
- View assistHintBottomLeft = mBottomOverlay.findViewById(R.id.assist_hint_left);
- View assistHintBottomRight = mBottomOverlay.findViewById(R.id.assist_hint_right);
-
- final int assistHintVisibility = mAssistHintVisible ? View.VISIBLE : View.INVISIBLE;
-
- if (mRotation == RotationUtils.ROTATION_NONE) {
- assistHintTopLeft.setVisibility(View.GONE);
- assistHintTopRight.setVisibility(View.GONE);
- assistHintBottomLeft.setVisibility(assistHintVisibility);
- assistHintBottomRight.setVisibility(assistHintVisibility);
- updateView(assistHintBottomLeft, Gravity.BOTTOM | Gravity.LEFT, 270);
- updateView(assistHintBottomRight, Gravity.BOTTOM | Gravity.RIGHT, 180);
- } else if (mRotation == RotationUtils.ROTATION_LANDSCAPE) {
- assistHintTopLeft.setVisibility(View.GONE);
- assistHintTopRight.setVisibility(assistHintVisibility);
- assistHintBottomLeft.setVisibility(View.GONE);
- assistHintBottomRight.setVisibility(assistHintVisibility);
- updateView(assistHintTopRight, Gravity.BOTTOM | Gravity.LEFT, 270);
- updateView(assistHintBottomRight, Gravity.BOTTOM | Gravity.RIGHT, 180);
- } else if (mRotation == RotationUtils.ROTATION_UPSIDE_DOWN) {
- assistHintTopLeft.setVisibility(assistHintVisibility);
- assistHintTopRight.setVisibility(assistHintVisibility);
- assistHintBottomLeft.setVisibility(View.GONE);
- assistHintBottomRight.setVisibility(View.GONE);
- updateView(assistHintTopLeft, Gravity.BOTTOM | Gravity.LEFT, 270);
- updateView(assistHintTopRight, Gravity.BOTTOM | Gravity.RIGHT, 180);
- } else if (mRotation == RotationUtils.ROTATION_SEASCAPE) {
- assistHintTopLeft.setVisibility(assistHintVisibility);
- assistHintTopRight.setVisibility(View.GONE);
- assistHintBottomLeft.setVisibility(assistHintVisibility);
- assistHintBottomRight.setVisibility(View.GONE);
- updateView(assistHintTopLeft, Gravity.BOTTOM | Gravity.RIGHT, 180);
- updateView(assistHintBottomLeft, Gravity.BOTTOM | Gravity.LEFT, 270);
- }
- }
-
private void updateView(View v, int gravity, int rotation) {
((FrameLayout.LayoutParams) v.getLayoutParams()).gravity = gravity;
v.setRotation(rotation);
@@ -629,10 +403,7 @@ public class ScreenDecorations extends SystemUI implements Tunable,
boolean visibleForCutout = shouldDrawCutout()
&& overlay.findViewById(R.id.display_cutout).getVisibility() == View.VISIBLE;
boolean visibleForRoundedCorners = hasRoundedCorners();
- boolean visibleForHandles = overlay.findViewById(R.id.assist_hint_left).getVisibility()
- == View.VISIBLE || overlay.findViewById(R.id.assist_hint_right).getVisibility()
- == View.VISIBLE;
- overlay.setVisibility(visibleForCutout || visibleForRoundedCorners || visibleForHandles
+ overlay.setVisibility(visibleForCutout || visibleForRoundedCorners
? View.VISIBLE : View.GONE);
}
@@ -689,7 +460,7 @@ public class ScreenDecorations extends SystemUI implements Tunable,
| WindowManager.LayoutParams.FLAG_SLIPPERY
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT);
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS
+ lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
| WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
if (!DEBUG_SCREENSHOT_ROUNDED_CORNERS) {
@@ -766,31 +537,6 @@ public class ScreenDecorations extends SystemUI implements Tunable,
view.setLayoutParams(params);
}
- @Override
- public void onDarkIntensity(float darkIntensity) {
- if (!mHandler.getLooper().isCurrentThread()) {
- mHandler.post(() -> onDarkIntensity(darkIntensity));
- return;
- }
- if (mOverlay != null) {
- CornerHandleView assistHintTopLeft = mOverlay.findViewById(R.id.assist_hint_left);
- CornerHandleView assistHintTopRight = mOverlay.findViewById(R.id.assist_hint_right);
-
- assistHintTopLeft.updateDarkness(darkIntensity);
- assistHintTopRight.updateDarkness(darkIntensity);
- }
-
- if (mBottomOverlay != null) {
- CornerHandleView assistHintBottomLeft = mBottomOverlay.findViewById(
- R.id.assist_hint_left);
- CornerHandleView assistHintBottomRight = mBottomOverlay.findViewById(
- R.id.assist_hint_right);
-
- assistHintBottomLeft.updateDarkness(darkIntensity);
- assistHintBottomRight.updateDarkness(darkIntensity);
- }
- }
-
@VisibleForTesting
static class TunablePaddingTagListener implements FragmentListener {
diff --git a/packages/SystemUI/src/com/android/systemui/ServiceBinder.java b/packages/SystemUI/src/com/android/systemui/ServiceBinder.java
deleted file mode 100644
index e761a2be0b0f..000000000000
--- a/packages/SystemUI/src/com/android/systemui/ServiceBinder.java
+++ /dev/null
@@ -1,37 +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;
-
-import android.app.Service;
-
-import com.android.systemui.doze.DozeService;
-
-import dagger.Binds;
-import dagger.Module;
-import dagger.multibindings.ClassKey;
-import dagger.multibindings.IntoMap;
-
-/**
- * Services that are injectable should go here.
- */
-@Module
-public abstract class ServiceBinder {
- @Binds
- @IntoMap
- @ClassKey(DozeService.class)
- public abstract Service bindDozeService(DozeService service);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
index c54f6306ddb1..10009f5f5582 100644
--- a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
@@ -59,12 +59,13 @@ public class SizeCompatModeActivityController extends SystemUI implements Comman
/** Only show once automatically in the process life. */
private boolean mHasShownHint;
- public SizeCompatModeActivityController() {
- this(ActivityManagerWrapper.getInstance());
+ public SizeCompatModeActivityController(Context context) {
+ this(context, ActivityManagerWrapper.getInstance());
}
@VisibleForTesting
- SizeCompatModeActivityController(ActivityManagerWrapper am) {
+ SizeCompatModeActivityController(Context context, ActivityManagerWrapper am) {
+ super(context);
am.registerTaskStackListener(new TaskStackChangeListener() {
@Override
public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) {
@@ -202,7 +203,7 @@ public class SizeCompatModeActivityController extends SystemUI implements Comman
mWinParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
mWinParams.format = PixelFormat.TRANSLUCENT;
- mWinParams.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ mWinParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
mWinParams.setTitle(SizeCompatModeActivityController.class.getSimpleName()
+ context.getDisplayId());
}
diff --git a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
index b3fc69e8a49d..92fbd259b471 100644
--- a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
@@ -39,6 +39,10 @@ public class SliceBroadcastRelayHandler extends SystemUI {
private final ArrayMap<Uri, BroadcastRelay> mRelays = new ArrayMap<>();
+ public SliceBroadcastRelayHandler(Context context) {
+ super(context);
+ }
+
@Override
public void start() {
if (DEBUG) Log.d(TAG, "Start");
diff --git a/packages/SystemUI/src/com/android/systemui/SysUIToast.java b/packages/SystemUI/src/com/android/systemui/SysUIToast.java
index 8bcf0571b2d0..0f7f1bebae57 100644
--- a/packages/SystemUI/src/com/android/systemui/SysUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/SysUIToast.java
@@ -31,7 +31,7 @@ public class SysUIToast {
public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
Toast toast = Toast.makeText(context, text, duration);
toast.getWindowParams().privateFlags |=
- WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
return toast;
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemBars.java b/packages/SystemUI/src/com/android/systemui/SystemBars.java
deleted file mode 100644
index c4c0fd6da124..000000000000
--- a/packages/SystemUI/src/com/android/systemui/SystemBars.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui;
-
-import android.util.Log;
-
-import com.android.systemui.statusbar.phone.StatusBar;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Ensure a single status bar service implementation is running at all times, using the in-process
- * implementation according to the product config.
- */
-public class SystemBars extends SystemUI {
- private static final String TAG = "SystemBars";
- private static final boolean DEBUG = false;
- private static final int WAIT_FOR_BARS_TO_DIE = 500;
-
- // in-process fallback implementation, per the product config
- private SystemUI mStatusBar;
-
- @Override
- public void start() {
- if (DEBUG) Log.d(TAG, "start");
- createStatusBarFromConfig();
- }
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mStatusBar != null) {
- mStatusBar.dump(fd, pw, args);
- }
- }
-
- @Override
- public void onBootCompleted() {
- if (mStatusBar != null) {
- mStatusBar.onBootCompleted();
- }
- }
-
- private void createStatusBarFromConfig() {
- if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");
- final String clsName = mContext.getString(R.string.config_statusBarComponent);
- if (clsName == null || clsName.length() == 0) {
- throw andLog("No status bar component configured", null);
- }
- Class<?> cls = null;
- try {
- cls = mContext.getClassLoader().loadClass(clsName);
- } catch (Throwable t) {
- throw andLog("Error loading status bar component: " + clsName, t);
- }
- try {
- mStatusBar = (SystemUI) cls.newInstance();
- } catch (Throwable t) {
- throw andLog("Error creating status bar component: " + clsName, t);
- }
- mStatusBar.mContext = mContext;
- mStatusBar.mComponents = mComponents;
- if (mStatusBar instanceof StatusBar) {
- SystemUIFactory.getInstance().getRootComponent()
- .getStatusBarInjector()
- .createStatusBar((StatusBar) mStatusBar);
- }
- mStatusBar.start();
- if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
- }
-
- private RuntimeException andLog(String msg, Throwable t) {
- Log.w(TAG, msg, t);
- throw new RuntimeException(msg, t);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUI.java b/packages/SystemUI/src/com/android/systemui/SystemUI.java
index 30fbef6cbefb..75700379caca 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUI.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUI.java
@@ -26,9 +26,13 @@ import java.io.PrintWriter;
import java.util.Map;
public abstract class SystemUI implements SysUiServiceProvider {
- public Context mContext;
+ protected final Context mContext;
public Map<Class<?>, Object> mComponents;
+ public SystemUI(Context context) {
+ mContext = context;
+ }
+
public abstract void start();
protected void onConfigurationChanged(Configuration newConfig) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java
index 2c8324cafca0..746515a816b3 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java
@@ -27,6 +27,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.AppComponentFactory;
+import com.android.systemui.dagger.ContextComponentHelper;
+
import javax.inject.Inject;
/**
@@ -92,6 +94,12 @@ public class SystemUIAppComponentFactory extends AppComponentFactory {
public Activity instantiateActivityCompat(@NonNull ClassLoader cl, @NonNull String className,
@Nullable Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
+ if (mComponentHelper == null) {
+ // This shouldn't happen, but is seen on occasion.
+ // Bug filed against framework to take a look: http://b/141008541
+ SystemUIFactory.getInstance().getRootComponent().inject(
+ SystemUIAppComponentFactory.this);
+ }
Activity activity = mComponentHelper.resolveActivity(className);
if (activity != null) {
return activity;
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 56b5d080acc0..022bf06838a6 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -34,6 +34,7 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.TimingsTraceLog;
+import com.android.systemui.dagger.ContextComponentHelper;
import com.android.systemui.plugins.OverlayPlugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.plugins.PluginManager;
@@ -42,6 +43,8 @@ import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.util.NotificationChannels;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
@@ -193,18 +196,18 @@ public class SystemUIApplication extends Application implements SysUiServiceProv
try {
SystemUI obj = mComponentHelper.resolveSystemUI(clsName);
if (obj == null) {
- obj = (SystemUI) Class.forName(clsName).newInstance();
+ Constructor constructor = Class.forName(clsName).getConstructor(Context.class);
+ obj = (SystemUI) constructor.newInstance(this);
}
mServices[i] = obj;
- } catch (ClassNotFoundException ex) {
- throw new RuntimeException(ex);
- } catch (IllegalAccessException ex) {
- throw new RuntimeException(ex);
- } catch (InstantiationException ex) {
+ } catch (ClassNotFoundException
+ | NoSuchMethodException
+ | IllegalAccessException
+ | InstantiationException
+ | InvocationTargetException ex) {
throw new RuntimeException(ex);
}
- mServices[i].mContext = this;
mServices[i].mComponents = mComponents;
if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
mServices[i].start();
@@ -235,7 +238,7 @@ public class SystemUIApplication extends Application implements SysUiServiceProv
if (statusBar != null) {
plugin.setup(statusBar.getStatusBarWindow(),
statusBar.getNavigationBarView(), new Callback(plugin),
- DozeParameters.getInstance(getBaseContext()));
+ Dependency.get(DozeParameters.class));
}
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java
deleted file mode 100644
index 4531c892a022..000000000000
--- a/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java
+++ /dev/null
@@ -1,36 +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;
-
-import com.android.systemui.keyguard.KeyguardViewMediator;
-
-import dagger.Binds;
-import dagger.Module;
-import dagger.multibindings.ClassKey;
-import dagger.multibindings.IntoMap;
-
-/**
- * SystemUI objects that are injectable should go here.
- */
-@Module
-public abstract class SystemUIBinder {
- /** Inject into KeyguardViewMediator. */
- @Binds
- @IntoMap
- @ClassKey(KeyguardViewMediator.class)
- public abstract SystemUI bindKeyguardViewMediator(KeyguardViewMediator sysui);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/SystemUIDefaultModule.java
deleted file mode 100644
index 262b5ec50d83..000000000000
--- a/packages/SystemUI/src/com/android/systemui/SystemUIDefaultModule.java
+++ /dev/null
@@ -1,85 +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;
-
-import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
-import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME;
-
-import android.content.Context;
-
-import androidx.annotation.Nullable;
-
-import com.android.systemui.dock.DockManager;
-import com.android.systemui.dock.DockManagerImpl;
-import com.android.systemui.power.EnhancedEstimates;
-import com.android.systemui.power.EnhancedEstimatesImpl;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
-import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
-import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
-
-import javax.inject.Named;
-import javax.inject.Singleton;
-
-import dagger.Binds;
-import dagger.Module;
-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
-abstract class SystemUIDefaultModule {
-
- @Singleton
- @Provides
- @Named(LEAK_REPORT_EMAIL_NAME)
- @Nullable
- static String provideLeakReportEmail() {
- return null;
- }
-
- @Binds
- abstract EnhancedEstimates bindEnhancedEstimates(EnhancedEstimatesImpl enhancedEstimates);
-
- @Binds
- abstract NotificationLockscreenUserManager bindNotificationLockscreenUserManager(
- NotificationLockscreenUserManagerImpl notificationLockscreenUserManager);
-
- @Binds
- abstract DockManager bindDockManager(DockManagerImpl dockManager);
-
- @Binds
- abstract NotificationData.KeyguardEnvironment bindKeyguardEnvironment(
- KeyguardEnvironmentImpl keyguardEnvironment);
-
- @Singleton
- @Provides
- static ShadeController provideShadeController(Context context) {
- return SysUiServiceProvider.getComponent(context, StatusBar.class);
- }
-
- @Singleton
- @Provides
- @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME)
- static boolean provideAllowNotificationLongPress() {
- return true;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 8e693185ab7f..0a547b6bf051 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -17,7 +17,6 @@
package com.android.systemui;
import android.annotation.NonNull;
-import android.app.AlarmManager;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
@@ -25,33 +24,26 @@ import android.util.Log;
import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.colorextraction.ColorExtractor.GradientColors;
-import com.android.internal.util.function.TriConsumer;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
+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;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.LockIcon;
-import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
-import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.phone.ScrimState;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.UnlockMethodCache;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
-import com.android.systemui.volume.VolumeDialogComponent;
-
-import java.util.function.Consumer;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import dagger.Module;
import dagger.Provides;
@@ -118,7 +110,7 @@ public class SystemUIFactory {
protected SystemUIRootComponent buildSystemUIRootComponent(Context context) {
return DaggerSystemUIRootComponent.builder()
- .dependencyProvider(new com.android.systemui.DependencyProvider())
+ .dependencyProvider(new DependencyProvider())
.contextHolder(new ContextHolder(context))
.build();
}
@@ -136,24 +128,15 @@ public class SystemUIFactory {
LockPatternUtils lockPatternUtils, ViewGroup container,
DismissCallbackRegistry dismissCallbackRegistry,
KeyguardBouncer.BouncerExpansionCallback expansionCallback,
- FalsingManager falsingManager, KeyguardBypassController bypassController) {
+ KeyguardStateController keyguardStateController, FalsingManager falsingManager,
+ KeyguardBypassController bypassController) {
return new KeyguardBouncer(context, callback, lockPatternUtils, container,
dismissCallbackRegistry, falsingManager,
- expansionCallback, UnlockMethodCache.getInstance(context),
+ expansionCallback, keyguardStateController,
Dependency.get(KeyguardUpdateMonitor.class), bypassController,
new Handler(Looper.getMainLooper()));
}
- public ScrimController createScrimController(ScrimView scrimBehind, ScrimView scrimInFront,
- ScrimView scrimForBubble,
- LockscreenWallpaper lockscreenWallpaper,
- TriConsumer<ScrimState, Float, GradientColors> scrimStateListener,
- Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters,
- AlarmManager alarmManager, KeyguardMonitor keyguardMonitor) {
- return new ScrimController(scrimBehind, scrimInFront, scrimForBubble, scrimStateListener,
- scrimVisibleListener, dozeParameters, alarmManager, keyguardMonitor);
- }
-
public NotificationIconAreaController createNotificationIconAreaController(Context context,
StatusBar statusBar,
NotificationWakeUpCoordinator wakeUpCoordinator,
@@ -161,7 +144,8 @@ public class SystemUIFactory {
StatusBarStateController statusBarStateController) {
return new NotificationIconAreaController(context, statusBar, statusBarStateController,
wakeUpCoordinator, keyguardBypassController,
- Dependency.get(NotificationMediaManager.class));
+ Dependency.get(NotificationMediaManager.class),
+ Dependency.get(DozeParameters.class));
}
public KeyguardIndicationController createKeyguardIndicationController(Context context,
@@ -169,10 +153,6 @@ public class SystemUIFactory {
return new KeyguardIndicationController(context, indicationArea, lockIcon);
}
- public VolumeDialogComponent createVolumeDialogComponent(SystemUI systemUi, Context context) {
- return new VolumeDialogComponent(systemUi, context);
- }
-
@Module
public static class ContextHolder {
private Context mContext;
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/SystemUIModule.java
deleted file mode 100644
index b0316e22de06..000000000000
--- a/packages/SystemUI/src/com/android/systemui/SystemUIModule.java
+++ /dev/null
@@ -1,59 +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;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.pm.PackageManager;
-
-import com.android.systemui.assist.AssistModule;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.phone.KeyguardLiftController;
-import com.android.systemui.util.sensors.AsyncSensorManager;
-
-import javax.inject.Singleton;
-
-import dagger.Module;
-import dagger.Provides;
-
-/**
- * A dagger module for injecting components of System UI that are not overridden by the System UI
- * implementation.
- */
-@Module(includes = {AssistModule.class, ComponentBinder.class})
-public abstract class SystemUIModule {
-
- @Singleton
- @Provides
- @Nullable
- static KeyguardLiftController provideKeyguardLiftController(Context context,
- StatusBarStateController statusBarStateController,
- AsyncSensorManager asyncSensorManager) {
- if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
- return null;
- }
- return new KeyguardLiftController(statusBarStateController, asyncSensorManager);
- }
-
-
- @Singleton
- @Provides
- static SysUiState provideSysUiState() {
- return new SysUiState();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/SystemUIRootComponent.java
deleted file mode 100644
index c70b2fc3292a..000000000000
--- a/packages/SystemUI/src/com/android/systemui/SystemUIRootComponent.java
+++ /dev/null
@@ -1,95 +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;
-
-import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
-
-import android.content.ContentProvider;
-
-import com.android.systemui.fragments.FragmentService;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.util.InjectionInflationController;
-import com.android.systemui.util.leak.GarbageMonitor;
-
-import javax.inject.Named;
-import javax.inject.Singleton;
-
-import dagger.Component;
-
-/**
- * Root component for Dagger injection.
- */
-@Singleton
-@Component(modules = {
- DependencyProvider.class,
- DependencyBinder.class,
- SystemUIFactory.ContextHolder.class,
- SystemUIModule.class,
- SystemUIDefaultModule.class})
-public interface SystemUIRootComponent {
-
- /**
- * Creates a GarbageMonitor.
- */
- @Singleton
- ContextComponentHelper getContextComponentHelper();
-
- /**
- * Main dependency providing module.
- */
- @Singleton
- Dependency.DependencyInjector createDependency();
-
- /**
- * Injects the StatusBar.
- */
- @Singleton
- StatusBar.StatusBarInjector getStatusBarInjector();
-
- /**
- * FragmentCreator generates all Fragments that need injection.
- */
- @Singleton
- FragmentService.FragmentCreator createFragmentCreator();
-
- /**
- * ViewCreator generates all Views that need injection.
- */
- InjectionInflationController.ViewCreator createViewCreator();
-
- /**
- * Creates a GarbageMonitor.
- */
- @Singleton
- GarbageMonitor createGarbageMonitor();
-
- /**
- * Whether notification long press is allowed.
- */
- @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME)
- boolean allowNotificationLongPressName();
-
- /**
- * Member injection into the supplied argument.
- */
- void inject(SystemUIAppComponentFactory factory);
-
- /**
- * Member injection into the supplied argument.
- */
- void inject(ContentProvider contentProvider);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index 8f1fcae8e0f7..3f56ff07b24d 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -16,6 +16,7 @@
package com.android.systemui;
+import android.annotation.NonNull;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
@@ -66,7 +67,13 @@ public class SystemUIService extends Service {
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (args != null && args.length > 0 && args[0].equals("--config")) {
+ dumpConfig(pw);
+ return;
+ }
+
dumpServices(((SystemUIApplication) getApplication()).getServices(), fd, pw, args);
+ dumpConfig(pw);
}
static void dumpServices(
@@ -95,5 +102,29 @@ public class SystemUIService extends Service {
}
}
}
+
+ private void dumpConfig(@NonNull PrintWriter pw) {
+ pw.println("SystemUiServiceComponents configuration:");
+
+ pw.print("vendor component: ");
+ pw.println(getResources().getString(R.string.config_systemUIVendorServiceComponent));
+
+ dumpConfig(pw, "global", R.array.config_systemUIServiceComponents);
+ dumpConfig(pw, "per-user", R.array.config_systemUIServiceComponentsPerUser);
+ }
+
+ private void dumpConfig(@NonNull PrintWriter pw, @NonNull String type, int resId) {
+ final String[] services = getResources().getStringArray(resId);
+ pw.print(type); pw.print(": ");
+ if (services == null) {
+ pw.println("N/A");
+ return;
+ }
+ pw.print(services.length);
+ pw.println(" services");
+ for (int i = 0; i < services.length; i++) {
+ pw.print(" "); pw.print(i); pw.print(": "); pw.println(services[i]);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/VendorServices.java b/packages/SystemUI/src/com/android/systemui/VendorServices.java
index 0be6b12fa365..13d847bf93c4 100644
--- a/packages/SystemUI/src/com/android/systemui/VendorServices.java
+++ b/packages/SystemUI/src/com/android/systemui/VendorServices.java
@@ -16,11 +16,17 @@
package com.android.systemui;
+import android.content.Context;
+
/**
* Placeholder for any vendor-specific services.
*/
public class VendorServices extends SystemUI {
+ public VendorServices(Context context) {
+ super(context);
+ }
+
@Override
public void start() {
// no-op
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index ef171d305d28..f616d57e90aa 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -16,8 +16,6 @@
package com.android.systemui.appops;
-import static com.android.systemui.Dependency.BG_LOOPER_NAME;
-
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -31,6 +29,7 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
+import com.android.systemui.dagger.qualifiers.BgLooper;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -39,7 +38,6 @@ import java.util.List;
import java.util.Set;
import javax.inject.Inject;
-import javax.inject.Named;
import javax.inject.Singleton;
/**
@@ -79,7 +77,7 @@ public class AppOpsControllerImpl implements AppOpsController,
};
@Inject
- public AppOpsControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper) {
+ public AppOpsControllerImpl(Context context, @BgLooper Looper bgLooper) {
this(context, bgLooper, new PermissionFlagsCache(context));
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
index 9bdfa03408aa..4516996345b9 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
@@ -32,7 +32,6 @@ import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.DumpController;
import com.android.systemui.Dumpable;
-import com.android.systemui.ScreenDecorations;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.phone.NavigationModeController;
@@ -71,7 +70,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
private final Handler mHandler;
private final Runnable mHideHandles = this::hideHandles;
private final Runnable mShowAndGo = this::showAndGoInternal;
- private final Provider<ScreenDecorations> mScreenDecorations;
+ private final Provider<AssistHandleViewController> mAssistHandleViewController;
private final PhenotypeHelper mPhenotypeHelper;
private final Map<AssistHandleBehavior, BehaviorController> mBehaviorMap;
@@ -90,7 +89,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
Context context,
AssistUtils assistUtils,
@Named(ASSIST_HANDLE_THREAD_NAME) Handler handler,
- Provider<ScreenDecorations> screenDecorations,
+ Provider<AssistHandleViewController> assistHandleViewController,
PhenotypeHelper phenotypeHelper,
Map<AssistHandleBehavior, BehaviorController> behaviorMap,
NavigationModeController navigationModeController,
@@ -98,7 +97,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
mContext = context;
mAssistUtils = assistUtils;
mHandler = handler;
- mScreenDecorations = screenDecorations;
+ mAssistHandleViewController = assistHandleViewController;
mPhenotypeHelper = phenotypeHelper;
mBehaviorMap = behaviorMap;
@@ -193,7 +192,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
try {
setBehavior(AssistHandleBehavior.valueOf(behavior));
} catch (IllegalArgumentException | NullPointerException e) {
- Log.e(TAG, "Invalid behavior: " + behavior, e);
+ Log.e(TAG, "Invalid behavior: " + behavior);
}
}
@@ -229,12 +228,13 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
}
if (handlesUnblocked(ignoreThreshold)) {
- ScreenDecorations screenDecorations = mScreenDecorations.get();
- if (screenDecorations == null) {
- Log.w(TAG, "Couldn't show handles, ScreenDecorations unavailable");
+ mHandlesShowing = true;
+ AssistHandleViewController assistHandleViewController =
+ mAssistHandleViewController.get();
+ if (assistHandleViewController == null) {
+ Log.w(TAG, "Couldn't show handles, AssistHandleViewController unavailable");
} else {
- mHandlesShowing = true;
- screenDecorations.setAssistHintVisible(true);
+ assistHandleViewController.setAssistHintVisible(true);
}
}
}
@@ -244,13 +244,14 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac
return;
}
- ScreenDecorations screenDecorations = mScreenDecorations.get();
- if (screenDecorations == null) {
- Log.w(TAG, "Couldn't hide handles, ScreenDecorations unavailable");
+ mHandlesShowing = false;
+ mHandlesLastHiddenAt = SystemClock.elapsedRealtime();
+ AssistHandleViewController assistHandleViewController =
+ mAssistHandleViewController.get();
+ if (assistHandleViewController == null) {
+ Log.w(TAG, "Couldn't show handles, AssistHandleViewController unavailable");
} else {
- mHandlesShowing = false;
- mHandlesLastHiddenAt = SystemClock.elapsedRealtime();
- screenDecorations.setAssistHintVisible(false);
+ assistHandleViewController.setAssistHintVisible(false);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleViewController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleViewController.java
new file mode 100644
index 000000000000..5010f319f8b4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleViewController.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.assist;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.os.Handler;
+import android.util.Log;
+import android.util.MathUtils;
+import android.view.View;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.CornerHandleView;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.NavigationBarTransitions;
+
+/**
+ * A class for managing Assistant handle show, hide and animation.
+ */
+public class AssistHandleViewController implements NavigationBarTransitions.DarkIntensityListener {
+
+ private static final boolean DEBUG = false;
+ private static final String TAG = "AssistHandleViewController";
+
+ private Handler mHandler;
+ private CornerHandleView mAssistHintLeft;
+ private CornerHandleView mAssistHintRight;
+ private int mBottomOffset;
+
+ @VisibleForTesting
+ boolean mAssistHintVisible;
+ @VisibleForTesting
+ boolean mAssistHintBlocked = false;
+
+ public AssistHandleViewController(Handler handler, View navBar) {
+ mHandler = handler;
+ mAssistHintLeft = navBar.findViewById(R.id.assist_hint_left);
+ mAssistHintRight = navBar.findViewById(R.id.assist_hint_right);
+ }
+
+ @Override
+ public void onDarkIntensity(float darkIntensity) {
+ mAssistHintLeft.updateDarkness(darkIntensity);
+ mAssistHintRight.updateDarkness(darkIntensity);
+ }
+
+ /**
+ * Set the bottom offset.
+ *
+ * @param bottomOffset the bottom offset to translate.
+ */
+ public void setBottomOffset(int bottomOffset) {
+ if (mBottomOffset != bottomOffset) {
+ mBottomOffset = bottomOffset;
+ if (mAssistHintVisible) {
+ // If assist handles are visible, hide them without animation and then make them
+ // show once again (with corrected bottom offset).
+ hideAssistHandles();
+ setAssistHintVisible(true);
+ }
+ }
+ }
+
+ /**
+ * Controls the visibility of the assist gesture handles.
+ *
+ * @param visible whether the handles should be shown
+ */
+ public void setAssistHintVisible(boolean visible) {
+ if (!mHandler.getLooper().isCurrentThread()) {
+ mHandler.post(() -> setAssistHintVisible(visible));
+ return;
+ }
+
+ if (mAssistHintBlocked && visible) {
+ if (DEBUG) {
+ Log.v(TAG, "Assist hint blocked, cannot make it visible");
+ }
+ return;
+ }
+
+ if (mAssistHintVisible != visible) {
+ mAssistHintVisible = visible;
+ fade(mAssistHintLeft, mAssistHintVisible, /* isLeft = */ true);
+ fade(mAssistHintRight, mAssistHintVisible, /* isLeft = */ false);
+ }
+ }
+
+ /**
+ * Prevents the assist hint from becoming visible even if `mAssistHintVisible` is true.
+ */
+ public void setAssistHintBlocked(boolean blocked) {
+ if (!mHandler.getLooper().isCurrentThread()) {
+ mHandler.post(() -> setAssistHintBlocked(blocked));
+ return;
+ }
+
+ mAssistHintBlocked = blocked;
+ if (mAssistHintVisible && mAssistHintBlocked) {
+ hideAssistHandles();
+ }
+ }
+
+ private void hideAssistHandles() {
+ mAssistHintLeft.setVisibility(View.GONE);
+ mAssistHintRight.setVisibility(View.GONE);
+ mAssistHintVisible = false;
+ }
+
+ /**
+ * Returns an animator that animates the given view from start to end over durationMs. Start and
+ * end represent total animation progress: 0 is the start, 1 is the end, 1.1 would be an
+ * overshoot.
+ */
+ Animator getHandleAnimator(View view, float start, float end, boolean isLeft, long durationMs,
+ Interpolator interpolator) {
+ // Note that lerp does allow overshoot, in cases where start and end are outside of [0,1].
+ float scaleStart = MathUtils.lerp(2f, 1f, start);
+ float scaleEnd = MathUtils.lerp(2f, 1f, end);
+ Animator scaleX = ObjectAnimator.ofFloat(view, View.SCALE_X, scaleStart, scaleEnd);
+ Animator scaleY = ObjectAnimator.ofFloat(view, View.SCALE_Y, scaleStart, scaleEnd);
+ float translationStart = MathUtils.lerp(0.2f, 0f, start);
+ float translationEnd = MathUtils.lerp(0.2f, 0f, end);
+ int xDirection = isLeft ? -1 : 1;
+ Animator translateX = ObjectAnimator.ofFloat(view, View.TRANSLATION_X,
+ xDirection * translationStart * view.getWidth(),
+ xDirection * translationEnd * view.getWidth());
+ Animator translateY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y,
+ translationStart * view.getHeight() + mBottomOffset,
+ translationEnd * view.getHeight() + mBottomOffset);
+
+ AnimatorSet set = new AnimatorSet();
+ set.play(scaleX).with(scaleY);
+ set.play(scaleX).with(translateX);
+ set.play(scaleX).with(translateY);
+ set.setDuration(durationMs);
+ set.setInterpolator(interpolator);
+ return set;
+ }
+
+ private void fade(View view, boolean fadeIn, boolean isLeft) {
+ if (fadeIn) {
+ view.animate().cancel();
+ view.setAlpha(1f);
+ view.setVisibility(View.VISIBLE);
+
+ // A piecewise spring-like interpolation.
+ // End value in one animator call must match the start value in the next, otherwise
+ // there will be a discontinuity.
+ AnimatorSet anim = new AnimatorSet();
+ Animator first = getHandleAnimator(view, 0, 1.1f, isLeft, 750,
+ new PathInterpolator(0, 0.45f, .67f, 1f));
+ Interpolator secondInterpolator = new PathInterpolator(0.33f, 0, 0.67f, 1f);
+ Animator second = getHandleAnimator(view, 1.1f, 0.97f, isLeft, 400,
+ secondInterpolator);
+ Animator third = getHandleAnimator(view, 0.97f, 1.02f, isLeft, 400,
+ secondInterpolator);
+ Animator fourth = getHandleAnimator(view, 1.02f, 1f, isLeft, 400,
+ secondInterpolator);
+ anim.play(first).before(second);
+ anim.play(second).before(third);
+ anim.play(third).before(fourth);
+ anim.start();
+ } else {
+ view.animate().cancel();
+ view.animate()
+ .setInterpolator(new AccelerateInterpolator(1.5f))
+ .setDuration(250)
+ .alpha(0f);
+ }
+
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
index 2a82d215e44a..6f5a17dca432 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
@@ -21,11 +21,11 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
+import androidx.annotation.Nullable;
import androidx.slice.Clock;
import com.android.internal.app.AssistUtils;
-import com.android.systemui.ScreenDecorations;
-import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.statusbar.NavigationBarController;
import java.util.EnumMap;
import java.util.Map;
@@ -69,8 +69,10 @@ public abstract class AssistModule {
}
@Provides
- static ScreenDecorations provideScreenDecorations(Context context) {
- return SysUiServiceProvider.getComponent(context, ScreenDecorations.class);
+ @Nullable
+ static AssistHandleViewController provideAssistHandleViewController(
+ NavigationBarController navigationBarController) {
+ return navigationBarController.getAssistHandlerViewController();
}
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
index 0c4f05123c79..4cb1708468ea 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
@@ -17,6 +17,7 @@
package com.android.systemui.assist.ui;
import static com.android.systemui.assist.AssistManager.DISMISS_REASON_INVOCATION_CANCELLED;
+import static com.android.systemui.assist.AssistManager.INVOCATION_TYPE_GESTURE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -24,6 +25,7 @@ import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.PixelFormat;
import android.metrics.LogMaker;
+import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
@@ -36,9 +38,11 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.ScreenDecorations;
-import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.assist.AssistHandleViewController;
import com.android.systemui.assist.AssistManager;
+import com.android.systemui.statusbar.NavigationBarController;
+
+import java.util.Locale;
/**
* Default UiController implementation. Shows white edge lights along the bottom of the phone,
@@ -50,6 +54,9 @@ public class DefaultUiController implements AssistManager.UiController {
private static final long ANIM_DURATION_MS = 200;
+ private static final boolean VERBOSE = Build.TYPE.toLowerCase(Locale.ROOT).contains("debug")
+ || Build.TYPE.toLowerCase(Locale.ROOT).equals("eng");
+
protected final FrameLayout mRoot;
protected InvocationLightsView mInvocationLightsView;
@@ -114,6 +121,7 @@ public class DefaultUiController implements AssistManager.UiController {
@Override // AssistManager.UiController
public void onGestureCompletion(float velocity) {
animateInvocationCompletion(AssistManager.INVOCATION_TYPE_GESTURE, velocity);
+ logInvocationProgressMetrics(INVOCATION_TYPE_GESTURE, 1, mInvocationInProgress);
}
@Override // AssistManager.UiController
@@ -127,16 +135,28 @@ public class DefaultUiController implements AssistManager.UiController {
updateAssistHandleVisibility();
}
- protected static void logInvocationProgressMetrics(
+ protected void logInvocationProgressMetrics(
int type, float progress, boolean invocationWasInProgress) {
// Logs assistant invocation start.
+ if (progress == 1f) {
+ if (VERBOSE) {
+ Log.v(TAG, "Invocation complete: type=" + type);
+ }
+ }
if (!invocationWasInProgress && progress > 0.f) {
+ if (VERBOSE) {
+ Log.v(TAG, "Invocation started: type=" + type);
+ }
MetricsLogger.action(new LogMaker(MetricsEvent.ASSISTANT)
.setType(MetricsEvent.TYPE_ACTION)
.setSubtype(Dependency.get(AssistManager.class).toLoggingSubType(type)));
}
// Logs assistant invocation cancelled.
- if (invocationWasInProgress && progress == 0f) {
+ if ((mInvocationAnimator == null || !mInvocationAnimator.isRunning())
+ && invocationWasInProgress && progress == 0f) {
+ if (VERBOSE) {
+ Log.v(TAG, "Invocation cancelled: type=" + type);
+ }
MetricsLogger.action(new LogMaker(MetricsEvent.ASSISTANT)
.setType(MetricsEvent.TYPE_DISMISS)
.setSubtype(DISMISS_REASON_INVOCATION_CANCELLED));
@@ -144,9 +164,14 @@ public class DefaultUiController implements AssistManager.UiController {
}
private void updateAssistHandleVisibility() {
- ScreenDecorations decorations = SysUiServiceProvider.getComponent(mRoot.getContext(),
- ScreenDecorations.class);
- decorations.setAssistHintBlocked(mInvocationInProgress);
+ NavigationBarController navigationBarController =
+ Dependency.get(NavigationBarController.class);
+ AssistHandleViewController controller =
+ navigationBarController == null
+ ? null : navigationBarController.getAssistHandlerViewController();
+ if (controller != null) {
+ controller.setAssistHintBlocked(mInvocationInProgress);
+ }
}
private void attach() {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/EdgeLight.java b/packages/SystemUI/src/com/android/systemui/assist/ui/EdgeLight.java
index 9ae02c5e3104..baa3a4a938c1 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/ui/EdgeLight.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/ui/EdgeLight.java
@@ -16,6 +16,8 @@
package com.android.systemui.assist.ui;
+import android.util.Log;
+
import androidx.annotation.ColorInt;
/**
@@ -29,9 +31,12 @@ import androidx.annotation.ColorInt;
* counter-clockwise.
*/
public final class EdgeLight {
+
+ private static final String TAG = "EdgeLight";
+
@ColorInt
private int mColor;
- private float mOffset;
+ private float mStart;
private float mLength;
/** Copies a list of EdgeLights. */
@@ -45,13 +50,13 @@ public final class EdgeLight {
public EdgeLight(@ColorInt int color, float offset, float length) {
mColor = color;
- mOffset = offset;
+ mStart = offset;
mLength = length;
}
public EdgeLight(EdgeLight sourceLight) {
mColor = sourceLight.getColor();
- mOffset = sourceLight.getOffset();
+ mStart = sourceLight.getStart();
mLength = sourceLight.getLength();
}
@@ -77,23 +82,41 @@ public final class EdgeLight {
}
/**
- * Returns the current offset, in units of the total device perimeter and measured from the
- * bottom-left corner (see class description).
+ * Sets the endpoints of the edge light, both measured from the bottom-left corner (see class
+ * description). This is a convenience method to avoid separate setStart and setLength calls.
*/
- public float getOffset() {
- return mOffset;
+ public void setEndpoints(float start, float end) {
+ if (start > end) {
+ Log.e(TAG, String.format("Endpoint must be >= start (add 1 if necessary). Got [%f, %f]",
+ start, end));
+ return;
+ }
+ mStart = start;
+ mLength = end - start;
+ }
+
+ /**
+ * Returns the current starting position, in units of the total device perimeter and measured
+ * from the bottom-left corner (see class description).
+ */
+ public float getStart() {
+ return mStart;
}
/**
* Sets the current offset, in units of the total device perimeter and measured from the
* bottom-left corner (see class description).
*/
- public void setOffset(float offset) {
- mOffset = offset;
+ public void setStart(float start) {
+ mStart = start;
+ }
+
+ public float getEnd() {
+ return mStart + mLength;
}
/** Returns the center, measured from the bottom-left corner (see class description). */
public float getCenter() {
- return mOffset + (mLength / 2.f);
+ return mStart + (mLength / 2.f);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java b/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java
index bb3bd781df66..570b911cd400 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java
@@ -140,10 +140,10 @@ public class InvocationLightsView extends View
float rightStart = mGuide.getRegionWidth(PerimeterPathGuide.Region.BOTTOM)
+ (cornerLengthNormalized - arcOffsetNormalized) * (1 - progress);
- setLight(0, leftStart, lightLength);
- setLight(1, leftStart + lightLength, lightLength);
- setLight(2, rightStart - (lightLength * 2), lightLength);
- setLight(3, rightStart - lightLength, lightLength);
+ setLight(0, leftStart, leftStart + lightLength);
+ setLight(1, leftStart + lightLength, leftStart + lightLength * 2);
+ setLight(2, rightStart - (lightLength * 2), rightStart - lightLength);
+ setLight(3, rightStart - lightLength, rightStart);
setVisibility(View.VISIBLE);
}
invalidate();
@@ -155,7 +155,7 @@ public class InvocationLightsView extends View
public void hide() {
setVisibility(GONE);
for (EdgeLight light : mAssistInvocationLights) {
- light.setLength(0);
+ light.setEndpoints(0, 0);
}
attemptUnregisterNavBarListener();
}
@@ -235,12 +235,11 @@ public class InvocationLightsView extends View
}
}
- protected void setLight(int index, float offset, float length) {
+ protected void setLight(int index, float start, float end) {
if (index < 0 || index >= 4) {
Log.w(TAG, "invalid invocation light index: " + index);
}
- mAssistInvocationLights.get(index).setOffset(offset);
- mAssistInvocationLights.get(index).setLength(length);
+ mAssistInvocationLights.get(index).setEndpoints(start, end);
}
/**
@@ -268,9 +267,11 @@ public class InvocationLightsView extends View
}
private void renderLight(EdgeLight light, Canvas canvas) {
- mGuide.strokeSegment(mPath, light.getOffset(), light.getOffset() + light.getLength());
- mPaint.setColor(light.getColor());
- canvas.drawPath(mPath, mPaint);
+ if (light.getLength() > 0) {
+ mGuide.strokeSegment(mPath, light.getStart(), light.getStart() + light.getLength());
+ mPaint.setColor(light.getColor());
+ canvas.drawPath(mPath, mPaint);
+ }
}
private void attemptRegisterNavBarListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index 73bbce9c5b35..d20cd72f0712 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -16,8 +16,6 @@
package com.android.systemui.biometrics;
-import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -25,6 +23,7 @@ import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.hardware.biometrics.BiometricPrompt;
import android.os.Bundle;
@@ -34,7 +33,7 @@ import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
+import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
import android.widget.Button;
import android.widget.ImageView;
@@ -42,10 +41,13 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
/**
* Contains the Biometric views (title, subtitle, icon, buttons, etc) and its controllers.
@@ -97,6 +99,7 @@ public abstract class AuthBiometricView extends LinearLayout {
int ACTION_BUTTON_NEGATIVE = 3;
int ACTION_BUTTON_TRY_AGAIN = 4;
int ACTION_ERROR = 5;
+ int ACTION_USE_DEVICE_CREDENTIAL = 6;
/**
* When an action has occurred. The caller will only invoke this when the callback should
@@ -145,6 +148,14 @@ public abstract class AuthBiometricView extends LinearLayout {
public int getDelayAfterError() {
return BiometricPrompt.HIDE_DIALOG_DELAY;
}
+
+ public int getMediumToLargeAnimationDurationMs() {
+ return AuthDialog.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS;
+ }
+
+ public int getAnimateCredentialStartDelayMs() {
+ return AuthDialog.ANIMATE_CREDENTIAL_START_DELAY_MS;
+ }
}
private final Injector mInjector;
@@ -154,8 +165,9 @@ public abstract class AuthBiometricView extends LinearLayout {
private final int mTextColorHint;
private AuthPanelController mPanelController;
- private Bundle mBundle;
+ private Bundle mBiometricPromptBundle;
private boolean mRequireConfirmation;
+ private int mUserId;
@AuthDialog.DialogSize int mSize = AuthDialog.SIZE_UNKNOWN;
private TextView mTitleView;
@@ -212,6 +224,9 @@ public abstract class AuthBiometricView extends LinearLayout {
} else if (mSize == AuthDialog.SIZE_SMALL) {
Log.w(TAG, "Ignoring background click during small dialog");
return;
+ } else if (mSize == AuthDialog.SIZE_LARGE) {
+ Log.w(TAG, "Ignoring background click during large dialog");
+ return;
}
mCallback.onAction(Callback.ACTION_USER_CANCELED);
};
@@ -256,7 +271,7 @@ public abstract class AuthBiometricView extends LinearLayout {
}
public void setBiometricPromptBundle(Bundle bundle) {
- mBundle = bundle;
+ mBiometricPromptBundle = bundle;
}
public void setCallback(Callback callback) {
@@ -267,6 +282,10 @@ public abstract class AuthBiometricView extends LinearLayout {
backgroundView.setOnClickListener(mBackgroundClickListener);
}
+ public void setUserId(int userId) {
+ mUserId = userId;
+ }
+
public void setRequireConfirmation(boolean requireConfirmation) {
mRequireConfirmation = requireConfirmation;
}
@@ -287,7 +306,7 @@ public abstract class AuthBiometricView extends LinearLayout {
final int newHeight = mIconView.getHeight() + 2 * (int) iconPadding;
mPanelController.updateForContentDimensions(mMediumWidth, newHeight,
- false /* animate */);
+ 0 /* animateDurationMs */);
mSize = newSize;
} else if (mSize == AuthDialog.SIZE_SMALL && newSize == AuthDialog.SIZE_MEDIUM) {
@@ -305,10 +324,8 @@ public abstract class AuthBiometricView extends LinearLayout {
// Animate the text
final ValueAnimator opacityAnimator = ValueAnimator.ofFloat(0, 1);
- opacityAnimator.setDuration(AuthDialog.ANIMATE_DURATION_MS);
opacityAnimator.addUpdateListener((animation) -> {
final float opacity = (float) animation.getAnimatedValue();
-
mTitleView.setAlpha(opacity);
mIndicatorView.setAlpha(opacity);
mNegativeButton.setAlpha(opacity);
@@ -324,7 +341,7 @@ public abstract class AuthBiometricView extends LinearLayout {
// Choreograph together
final AnimatorSet as = new AnimatorSet();
- as.setDuration(AuthDialog.ANIMATE_DURATION_MS);
+ as.setDuration(AuthDialog.ANIMATE_SMALL_TO_MEDIUM_DURATION_MS);
as.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
@@ -355,11 +372,73 @@ public abstract class AuthBiometricView extends LinearLayout {
as.start();
// Animate the panel
mPanelController.updateForContentDimensions(mMediumWidth, mMediumHeight,
- true /* animate */);
+ AuthDialog.ANIMATE_SMALL_TO_MEDIUM_DURATION_MS);
} else if (newSize == AuthDialog.SIZE_MEDIUM) {
mPanelController.updateForContentDimensions(mMediumWidth, mMediumHeight,
- false /* animate */);
+ 0 /* animateDurationMs */);
mSize = newSize;
+ } else if (newSize == AuthDialog.SIZE_LARGE) {
+ final boolean isManagedProfile = Utils.isManagedProfile(mContext, mUserId);
+
+ // If it's a managed profile, animate the contents and panel down, since the credential
+ // contents will be shown on the same "layer" as the background. If it's not a managed
+ // profile, animate the contents up and expand the panel to full-screen - the credential
+ // contents will be shown on the same "layer" as the panel.
+ final float translationY = isManagedProfile ?
+ -getResources().getDimension(
+ R.dimen.biometric_dialog_animation_translation_offset)
+ : getResources().getDimension(
+ R.dimen.biometric_dialog_medium_to_large_translation_offset);
+ final AuthBiometricView biometricView = this;
+
+ // Translate at full duration
+ final ValueAnimator translationAnimator = ValueAnimator.ofFloat(
+ biometricView.getY(), biometricView.getY() - translationY);
+ translationAnimator.setDuration(mInjector.getMediumToLargeAnimationDurationMs());
+ translationAnimator.addUpdateListener((animation) -> {
+ final float translation = (float) animation.getAnimatedValue();
+ biometricView.setTranslationY(translation);
+ });
+ translationAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ if (biometricView.getParent() != null) {
+ ((ViewGroup) biometricView.getParent()).removeView(biometricView);
+ }
+ mSize = newSize;
+ }
+ });
+
+ // Opacity to 0 in half duration
+ final ValueAnimator opacityAnimator = ValueAnimator.ofFloat(1, 0);
+ opacityAnimator.setDuration(mInjector.getMediumToLargeAnimationDurationMs() / 2);
+ opacityAnimator.addUpdateListener((animation) -> {
+ final float opacity = (float) animation.getAnimatedValue();
+ biometricView.setAlpha(opacity);
+ });
+
+ if (!isManagedProfile) {
+ mPanelController.setUseFullScreen(true);
+ mPanelController.updateForContentDimensions(
+ mPanelController.getContainerWidth(),
+ mPanelController.getContainerHeight(),
+ mInjector.getMediumToLargeAnimationDurationMs());
+ }
+
+ // Start the animations together
+ AnimatorSet as = new AnimatorSet();
+ List<Animator> animators = new ArrayList<>();
+ animators.add(translationAnimator);
+ animators.add(opacityAnimator);
+ if (isManagedProfile) {
+ animators.add(mPanelController.getTranslationAnimator(translationY));
+ animators.add(mPanelController.getAlphaAnimator(0));
+ }
+ as.playTogether(animators);
+ as.setDuration(isManagedProfile ? mInjector.getMediumToLargeAnimationDurationMs()
+ : mInjector.getMediumToLargeAnimationDurationMs() * 2 / 3);
+ as.start();
} else {
Log.e(TAG, "Unknown transition from: " + mSize + " to: " + newSize);
}
@@ -528,7 +607,11 @@ public abstract class AuthBiometricView extends LinearLayout {
if (mState == STATE_PENDING_CONFIRMATION) {
mCallback.onAction(Callback.ACTION_USER_CANCELED);
} else {
- mCallback.onAction(Callback.ACTION_BUTTON_NEGATIVE);
+ if (isDeviceCredentialAllowed()) {
+ startTransitionToCredentialUI();
+ } else {
+ mCallback.onAction(Callback.ACTION_BUTTON_NEGATIVE);
+ }
}
});
@@ -544,6 +627,16 @@ public abstract class AuthBiometricView extends LinearLayout {
});
}
+ /**
+ * Kicks off the animation process and invokes the callback.
+ */
+ void startTransitionToCredentialUI() {
+ updateSize(AuthDialog.SIZE_LARGE);
+ mHandler.postDelayed(() -> {
+ mCallback.onAction(Callback.ACTION_USE_DEVICE_CREDENTIAL);
+ }, mInjector.getAnimateCredentialStartDelayMs());
+ }
+
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -556,11 +649,37 @@ public abstract class AuthBiometricView extends LinearLayout {
*/
@VisibleForTesting
void onAttachedToWindowInternal() {
- setText(mTitleView, mBundle.getString(BiometricPrompt.KEY_TITLE));
- setText(mNegativeButton, mBundle.getString(BiometricPrompt.KEY_NEGATIVE_TEXT));
+ setText(mTitleView, mBiometricPromptBundle.getString(BiometricPrompt.KEY_TITLE));
+
+ final String negativeText;
+ if (isDeviceCredentialAllowed()) {
+
+ final @Utils.CredentialType int credentialType =
+ Utils.getCredentialType(mContext, mUserId);
+ switch(credentialType) {
+ case Utils.CREDENTIAL_PIN:
+ negativeText = getResources().getString(R.string.biometric_dialog_use_pin);
+ break;
+ case Utils.CREDENTIAL_PATTERN:
+ negativeText = getResources().getString(R.string.biometric_dialog_use_pattern);
+ break;
+ case Utils.CREDENTIAL_PASSWORD:
+ negativeText = getResources().getString(R.string.biometric_dialog_use_password);
+ break;
+ default:
+ negativeText = getResources().getString(R.string.biometric_dialog_use_password);
+ break;
+ }
- setTextOrHide(mSubtitleView, mBundle.getString(BiometricPrompt.KEY_SUBTITLE));
- setTextOrHide(mDescriptionView, mBundle.getString(BiometricPrompt.KEY_DESCRIPTION));
+ } else {
+ negativeText = mBiometricPromptBundle.getString(BiometricPrompt.KEY_NEGATIVE_TEXT);
+ }
+ setText(mNegativeButton, negativeText);
+
+ setTextOrHide(mSubtitleView,
+ mBiometricPromptBundle.getString(BiometricPrompt.KEY_SUBTITLE));
+ setTextOrHide(mDescriptionView,
+ mBiometricPromptBundle.getString(BiometricPrompt.KEY_DESCRIPTION));
if (mSavedState == null) {
updateState(STATE_AUTHENTICATING_ANIMATING_IN);
@@ -655,4 +774,8 @@ public abstract class AuthBiometricView extends LinearLayout {
}
}
}
+
+ private boolean isDeviceCredentialAllowed() {
+ return Utils.isDeviceCredentialAllowed(mBiometricPromptBundle);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 6555c75f677a..f1abdb31b5f8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -24,7 +24,9 @@ import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
+import android.hardware.biometrics.Authenticator;
import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricPrompt;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -36,6 +38,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.Interpolator;
+import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
@@ -72,17 +75,20 @@ public class AuthContainerView extends LinearLayout
@interface ContainerState {}
final Config mConfig;
+ private final Injector mInjector;
private final IBinder mWindowToken = new Binder();
private final WindowManager mWindowManager;
private final AuthPanelController mPanelController;
private final Interpolator mLinearOutSlowIn;
@VisibleForTesting final BiometricCallback mBiometricCallback;
+ private final CredentialCallback mCredentialCallback;
- private final ViewGroup mContainerView;
- private final AuthBiometricView mBiometricView;
+ @VisibleForTesting final FrameLayout mFrameLayout;
+ @VisibleForTesting @Nullable AuthBiometricView mBiometricView;
+ @VisibleForTesting @Nullable AuthCredentialView mCredentialView;
private final ImageView mBackgroundView;
- private final ScrollView mScrollView;
+ @VisibleForTesting final ScrollView mBiometricScrollView;
private final View mPanelView;
private final float mTranslationY;
@@ -145,7 +151,31 @@ public class AuthContainerView extends LinearLayout
public AuthContainerView build(int modalityMask) {
mConfig.mModalityMask = modalityMask;
- return new AuthContainerView(mConfig);
+ return new AuthContainerView(mConfig, new Injector());
+ }
+ }
+
+ public static class Injector {
+ ScrollView getBiometricScrollView(FrameLayout parent) {
+ return parent.findViewById(R.id.biometric_scrollview);
+ }
+
+ FrameLayout inflateContainerView(LayoutInflater factory, ViewGroup root) {
+ return (FrameLayout) factory.inflate(
+ R.layout.auth_container_view, root, false /* attachToRoot */);
+ }
+
+ AuthPanelController getPanelController(Context context, View panelView,
+ boolean isManagedProfile) {
+ return new AuthPanelController(context, panelView, isManagedProfile);
+ }
+
+ ImageView getBackgroundView(FrameLayout parent) {
+ return parent.findViewById(R.id.background);
+ }
+
+ View getPanelView(FrameLayout parent) {
+ return parent.findViewById(R.id.panel);
}
}
@@ -155,7 +185,7 @@ public class AuthContainerView extends LinearLayout
public void onAction(int action) {
switch (action) {
case AuthBiometricView.Callback.ACTION_AUTHENTICATED:
- animateAway(AuthDialogCallback.DISMISSED_AUTHENTICATED);
+ animateAway(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED);
break;
case AuthBiometricView.Callback.ACTION_USER_CANCELED:
animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
@@ -169,17 +199,30 @@ public class AuthContainerView extends LinearLayout
case AuthBiometricView.Callback.ACTION_ERROR:
animateAway(AuthDialogCallback.DISMISSED_ERROR);
break;
+ case AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL:
+ mConfig.mCallback.onDeviceCredentialPressed();
+ addCredentialView(false /* animatePanel */, true /* animateContents */);
+ break;
default:
Log.e(TAG, "Unhandled action: " + action);
}
}
}
+ final class CredentialCallback implements AuthCredentialView.Callback {
+ @Override
+ public void onCredentialMatched() {
+ animateAway(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED);
+ }
+ }
+
@VisibleForTesting
- AuthContainerView(Config config) {
+ AuthContainerView(Config config, Injector injector) {
super(config.mContext);
mConfig = config;
+ mInjector = injector;
+
mWindowManager = mContext.getSystemService(WindowManager.class);
mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
@@ -187,51 +230,48 @@ public class AuthContainerView extends LinearLayout
.getDimension(R.dimen.biometric_dialog_animation_translation_offset);
mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
mBiometricCallback = new BiometricCallback();
+ mCredentialCallback = new CredentialCallback();
final LayoutInflater factory = LayoutInflater.from(mContext);
- mContainerView = (ViewGroup) factory.inflate(
- R.layout.auth_container_view, this, false /* attachToRoot */);
-
- mPanelView = mContainerView.findViewById(R.id.panel);
- mPanelController = new AuthPanelController(mContext, mPanelView);
-
- // TODO: Update with new controllers if multi-modal authentication can occur simultaneously
- if (config.mModalityMask == BiometricAuthenticator.TYPE_FINGERPRINT) {
- mBiometricView = (AuthBiometricFingerprintView)
- factory.inflate(R.layout.auth_biometric_fingerprint_view, null, false);
- } else if (config.mModalityMask == BiometricAuthenticator.TYPE_FACE) {
- mBiometricView = (AuthBiometricFaceView)
- factory.inflate(R.layout.auth_biometric_face_view, null, false);
- } else {
- Log.e(TAG, "Unsupported modality mask: " + config.mModalityMask);
- mBiometricView = null;
- mBackgroundView = null;
- mScrollView = null;
- return;
+ mFrameLayout = mInjector.inflateContainerView(factory, this);
+
+ final boolean isManagedProfile = Utils.isManagedProfile(mContext, mConfig.mUserId);
+
+ mPanelView = mInjector.getPanelView(mFrameLayout);
+ mPanelController = mInjector.getPanelController(mContext, mPanelView, isManagedProfile);
+
+ // Inflate biometric view only if necessary.
+ if (Utils.isBiometricAllowed(mConfig.mBiometricPromptBundle)) {
+ if (config.mModalityMask == BiometricAuthenticator.TYPE_FINGERPRINT) {
+ mBiometricView = (AuthBiometricFingerprintView)
+ factory.inflate(R.layout.auth_biometric_fingerprint_view, null, false);
+ } else if (config.mModalityMask == BiometricAuthenticator.TYPE_FACE) {
+ mBiometricView = (AuthBiometricFaceView)
+ factory.inflate(R.layout.auth_biometric_face_view, null, false);
+ } else {
+ Log.e(TAG, "Unsupported biometric modality: " + config.mModalityMask);
+ mBiometricView = null;
+ mBackgroundView = null;
+ mBiometricScrollView = null;
+ return;
+ }
}
- mBackgroundView = mContainerView.findViewById(R.id.background);
+ mBiometricScrollView = mInjector.getBiometricScrollView(mFrameLayout);
+ mBackgroundView = mInjector.getBackgroundView(mFrameLayout);
+
- UserManager userManager = mContext.getSystemService(UserManager.class);
- DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
- if (userManager.isManagedProfile(mConfig.mUserId)) {
+ if (isManagedProfile) {
final Drawable image = getResources().getDrawable(R.drawable.work_challenge_background,
mContext.getTheme());
+ final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
image.setColorFilter(dpm.getOrganizationColorForUser(mConfig.mUserId),
PorterDuff.Mode.DARKEN);
mBackgroundView.setScaleType(ImageView.ScaleType.CENTER_CROP);
mBackgroundView.setImageDrawable(image);
}
- mBiometricView.setRequireConfirmation(mConfig.mRequireConfirmation);
- mBiometricView.setPanelController(mPanelController);
- mBiometricView.setBiometricPromptBundle(config.mBiometricPromptBundle);
- mBiometricView.setCallback(mBiometricCallback);
- mBiometricView.setBackgroundView(mBackgroundView);
-
- mScrollView = mContainerView.findViewById(R.id.scrollview);
- mScrollView.addView(mBiometricView);
- addView(mContainerView);
+ addView(mFrameLayout);
setOnKeyListener((v, keyCode, event) -> {
if (keyCode != KeyEvent.KEYCODE_BACK) {
@@ -248,6 +288,53 @@ public class AuthContainerView extends LinearLayout
}
@Override
+ public boolean isAllowDeviceCredentials() {
+ return Utils.isDeviceCredentialAllowed(mConfig.mBiometricPromptBundle);
+ }
+
+ private void addBiometricView() {
+ mBiometricView.setRequireConfirmation(mConfig.mRequireConfirmation);
+ mBiometricView.setPanelController(mPanelController);
+ mBiometricView.setBiometricPromptBundle(mConfig.mBiometricPromptBundle);
+ mBiometricView.setCallback(mBiometricCallback);
+ mBiometricView.setBackgroundView(mBackgroundView);
+ mBiometricView.setUserId(mConfig.mUserId);
+ mBiometricScrollView.addView(mBiometricView);
+ }
+
+ /**
+ * Adds the credential view. When going from biometric to credential view, the biometric
+ * view starts the panel expansion animation. If the credential view is being shown first,
+ * it should own the panel expansion.
+ * @param animatePanel if the credential view needs to own the panel expansion animation
+ */
+ private void addCredentialView(boolean animatePanel, boolean animateContents) {
+ final LayoutInflater factory = LayoutInflater.from(mContext);
+ final int credentialType = Utils.getCredentialType(mContext, mConfig.mUserId);
+ switch (credentialType) {
+ case Utils.CREDENTIAL_PATTERN:
+ mCredentialView = (AuthCredentialView) factory.inflate(
+ R.layout.auth_credential_pattern_view, null, false);
+ break;
+ case Utils.CREDENTIAL_PIN:
+ case Utils.CREDENTIAL_PASSWORD:
+ mCredentialView = (AuthCredentialView) factory.inflate(
+ R.layout.auth_credential_password_view, null, false);
+ break;
+ default:
+ throw new IllegalStateException("Unknown credential type: " + credentialType);
+ }
+
+ mCredentialView.setContainerView(this);
+ mCredentialView.setUser(mConfig.mUserId);
+ mCredentialView.setCallback(mCredentialCallback);
+ mCredentialView.setBiometricPromptBundle(mConfig.mBiometricPromptBundle);
+ mCredentialView.setPanelController(mPanelController, animatePanel);
+ mCredentialView.setShouldAnimateContents(animateContents);
+ mFrameLayout.addView(mCredentialView);
+ }
+
+ @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mPanelController.setContainerDimensions(getMeasuredWidth(), getMeasuredHeight());
@@ -256,8 +343,22 @@ public class AuthContainerView extends LinearLayout
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
+ onAttachedToWindowInternal();
+ }
+
+ @VisibleForTesting
+ void onAttachedToWindowInternal() {
mWakefulnessLifecycle.addObserver(this);
+ if (Utils.isBiometricAllowed(mConfig.mBiometricPromptBundle)) {
+ addBiometricView();
+ } else if (Utils.isDeviceCredentialAllowed(mConfig.mBiometricPromptBundle)) {
+ addCredentialView(true /* animatePanel */, false /* animateContents */);
+ } else {
+ throw new IllegalStateException("Unknown configuration: "
+ + Utils.getAuthenticators(mConfig.mBiometricPromptBundle));
+ }
+
if (mConfig.mSkipIntro) {
mContainerState = STATE_SHOWING;
} else {
@@ -265,7 +366,7 @@ public class AuthContainerView extends LinearLayout
// The background panel and content are different views since we need to be able to
// animate them separately in other places.
mPanelView.setY(mTranslationY);
- mScrollView.setY(mTranslationY);
+ mBiometricScrollView.setY(mTranslationY);
setAlpha(0f);
postOnAnimation(() -> {
@@ -276,12 +377,21 @@ public class AuthContainerView extends LinearLayout
.withLayer()
.withEndAction(this::onDialogAnimatedIn)
.start();
- mScrollView.animate()
+ mBiometricScrollView.animate()
.translationY(0)
.setDuration(ANIMATION_DURATION_SHOW_MS)
.setInterpolator(mLinearOutSlowIn)
.withLayer()
.start();
+ if (mCredentialView != null && mCredentialView.isAttachedToWindow()) {
+ mCredentialView.setY(mTranslationY);
+ mCredentialView.animate()
+ .translationY(0)
+ .setDuration(ANIMATION_DURATION_SHOW_MS)
+ .setInterpolator(mLinearOutSlowIn)
+ .withLayer()
+ .start();
+ }
animate()
.alpha(1f)
.setDuration(ANIMATION_DURATION_SHOW_MS)
@@ -305,7 +415,9 @@ public class AuthContainerView extends LinearLayout
@Override
public void show(WindowManager wm, @Nullable Bundle savedState) {
- mBiometricView.restoreState(savedState);
+ if (mBiometricView != null) {
+ mBiometricView.restoreState(savedState);
+ }
wm.addView(this, getLayoutParams(mWindowToken));
}
@@ -346,7 +458,15 @@ public class AuthContainerView extends LinearLayout
@Override
public void onSaveState(@NonNull Bundle outState) {
outState.putInt(AuthDialog.KEY_CONTAINER_STATE, mContainerState);
- mBiometricView.onSaveState(outState);
+ // In the case where biometric and credential are both allowed, we can assume that
+ // biometric isn't showing if credential is showing since biometric is shown first.
+ outState.putBoolean(AuthDialog.KEY_BIOMETRIC_SHOWING,
+ mBiometricView != null && mCredentialView == null);
+ outState.putBoolean(AuthDialog.KEY_CREDENTIAL_SHOWING, mCredentialView != null);
+
+ if (mBiometricView != null) {
+ mBiometricView.onSaveState(outState);
+ }
}
@Override
@@ -354,6 +474,11 @@ public class AuthContainerView extends LinearLayout
return mConfig.mOpPackageName;
}
+ @Override
+ public void animateToCredentialUI() {
+ mBiometricView.startTransitionToCredentialUI();
+ }
+
@VisibleForTesting
void animateAway(int reason) {
animateAway(true /* sendReason */, reason);
@@ -391,12 +516,20 @@ public class AuthContainerView extends LinearLayout
.withLayer()
.withEndAction(endActionRunnable)
.start();
- mScrollView.animate()
+ mBiometricScrollView.animate()
.translationY(mTranslationY)
.setDuration(ANIMATION_DURATION_AWAY_MS)
.setInterpolator(mLinearOutSlowIn)
.withLayer()
.start();
+ if (mCredentialView != null && mCredentialView.isAttachedToWindow()) {
+ mCredentialView.animate()
+ .translationY(mTranslationY)
+ .setDuration(ANIMATION_DURATION_AWAY_MS)
+ .setInterpolator(mLinearOutSlowIn)
+ .withLayer()
+ .start();
+ }
animate()
.alpha(0f)
.setDuration(ANIMATION_DURATION_AWAY_MS)
@@ -431,7 +564,9 @@ public class AuthContainerView extends LinearLayout
return;
}
mContainerState = STATE_SHOWING;
- mBiometricView.onDialogAnimatedIn();
+ if (mBiometricView != null) {
+ mBiometricView.onDialogAnimatedIn();
+ }
}
/**
@@ -445,7 +580,7 @@ public class AuthContainerView extends LinearLayout
WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
PixelFormat.TRANSLUCENT);
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("BiometricPrompt");
lp.token = windowToken;
return lp;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index d10a3fede412..b75873100025 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -16,6 +16,9 @@
package com.android.systemui.biometrics;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
@@ -23,8 +26,12 @@ import android.app.TaskStackListener;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.hardware.biometrics.Authenticator;
+import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.IBiometricServiceReceiverInternal;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -32,6 +39,7 @@ import android.os.RemoteException;
import android.util.Log;
import android.view.WindowManager;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.systemui.SystemUI;
@@ -105,6 +113,15 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
}
@Override
+ public void onDeviceCredentialPressed() {
+ try {
+ mReceiver.onDeviceCredentialPressed();
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException when handling credential button", e);
+ }
+ }
+
+ @Override
public void onDismissed(@DismissedReason int reason) {
switch (reason) {
case AuthDialogCallback.DISMISSED_USER_CANCELED:
@@ -116,11 +133,12 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
break;
case AuthDialogCallback.DISMISSED_BUTTON_POSITIVE:
- sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CONFIRMED);
+ sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED);
break;
- case AuthDialogCallback.DISMISSED_AUTHENTICATED:
- sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CONFIRM_NOT_REQUIRED);
+ case AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED:
+ sendResultAndCleanUp(
+ BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED);
break;
case AuthDialogCallback.DISMISSED_ERROR:
@@ -131,6 +149,10 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED);
break;
+ case AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED:
+ sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED);
+ break;
+
default:
Log.e(TAG, "Unhandled reason: " + reason);
break;
@@ -156,12 +178,13 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
}
}
- public AuthController() {
- this(new Injector());
+ public AuthController(Context context) {
+ this(context, new Injector());
}
@VisibleForTesting
- AuthController(Injector injector) {
+ AuthController(Context context, Injector injector) {
+ super(context);
mInjector = injector;
}
@@ -185,16 +208,19 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
}
@Override
- public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
- int type, boolean requireConfirmation, int userId, String opPackageName) {
+ public void showAuthenticationDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
+ int biometricModality, boolean requireConfirmation, int userId, String opPackageName) {
+ final int authenticators = Utils.getAuthenticators(bundle);
+
if (DEBUG) {
- Log.d(TAG, "showBiometricDialog, type: " + type
+ Log.d(TAG, "showAuthenticationDialog, authenticators: " + authenticators
+ + ", biometricModality: " + biometricModality
+ ", requireConfirmation: " + requireConfirmation);
}
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
args.arg2 = receiver;
- args.argi1 = type;
+ args.argi1 = biometricModality;
args.arg3 = requireConfirmation;
args.argi2 = userId;
args.arg4 = opPackageName;
@@ -204,19 +230,13 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
Log.w(TAG, "mCurrentDialog: " + mCurrentDialog);
skipAnimation = true;
}
+
showDialog(args, skipAnimation, null /* savedState */);
}
@Override
- public void onBiometricAuthenticated(boolean authenticated, String failureReason) {
- if (DEBUG) Log.d(TAG, "onBiometricAuthenticated: " + authenticated
- + " reason: " + failureReason);
-
- if (authenticated) {
- mCurrentDialog.onAuthenticationSucceeded();
- } else {
- mCurrentDialog.onAuthenticationFailed(failureReason);
- }
+ public void onBiometricAuthenticated() {
+ mCurrentDialog.onAuthenticationSucceeded();
}
@Override
@@ -226,15 +246,51 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
mCurrentDialog.onHelp(message);
}
+ private String getErrorString(int modality, int error, int vendorCode) {
+ switch (modality) {
+ case TYPE_FACE:
+ return FaceManager.getErrorString(mContext, error, vendorCode);
+
+ case TYPE_FINGERPRINT:
+ return FingerprintManager.getErrorString(mContext, error, vendorCode);
+
+ default:
+ return "";
+ }
+ }
+
@Override
- public void onBiometricError(String error) {
- if (DEBUG) Log.d(TAG, "onBiometricError: " + error);
- mCurrentDialog.onError(error);
+ public void onBiometricError(int modality, int error, int vendorCode) {
+ if (DEBUG) {
+ Log.d(TAG, String.format("onBiometricError(%d, %d, %d)", modality, error, vendorCode));
+ }
+
+ final boolean isLockout = (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT)
+ || (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT);
+
+ // TODO(b/141025588): Create separate methods for handling hard and soft errors.
+ final boolean isSoftError = (error == BiometricConstants.BIOMETRIC_PAUSED_REJECTED
+ || error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT);
+
+ if (mCurrentDialog.isAllowDeviceCredentials() && isLockout) {
+ if (DEBUG) Log.d(TAG, "onBiometricError, lockout");
+ mCurrentDialog.animateToCredentialUI();
+ } else if (isSoftError) {
+ final String errorMessage = (error == BiometricConstants.BIOMETRIC_PAUSED_REJECTED)
+ ? mContext.getString(R.string.biometric_not_recognized)
+ : getErrorString(modality, error, vendorCode);
+ if (DEBUG) Log.d(TAG, "onBiometricError, soft error: " + errorMessage);
+ mCurrentDialog.onAuthenticationFailed(errorMessage);
+ } else {
+ final String errorMessage = getErrorString(modality, error, vendorCode);
+ if (DEBUG) Log.d(TAG, "onBiometricError, hard error: " + errorMessage);
+ mCurrentDialog.onError(errorMessage);
+ }
}
@Override
- public void hideBiometricDialog() {
- if (DEBUG) Log.d(TAG, "hideBiometricDialog");
+ public void hideAuthenticationDialog() {
+ if (DEBUG) Log.d(TAG, "hideAuthenticationDialog");
mCurrentDialog.dismissFromSystemServer();
}
@@ -262,7 +318,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
}
if (DEBUG) {
- Log.d(TAG, "showDialog, "
+ Log.d(TAG, "showDialog: " + args
+ " savedState: " + savedState
+ " mCurrentDialog: " + mCurrentDialog
+ " newDialog: " + newDialog
@@ -306,6 +362,15 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
// to send its pending callback immediately.
if (savedState.getInt(AuthDialog.KEY_CONTAINER_STATE)
!= AuthContainerView.STATE_ANIMATING_OUT) {
+ final boolean credentialShowing =
+ savedState.getBoolean(AuthDialog.KEY_CREDENTIAL_SHOWING);
+ if (credentialShowing) {
+ // TODO: Clean this up
+ Bundle bundle = (Bundle) mCurrentDialogArgs.arg1;
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED,
+ Authenticator.TYPE_CREDENTIAL);
+ }
+
showDialog(mCurrentDialogArgs, true /* skipAnimation */, savedState);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
new file mode 100644
index 000000000000..bebaa4b688db
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.content.Context;
+import android.text.InputType;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import com.android.internal.widget.LockPatternChecker;
+import com.android.internal.widget.LockscreenCredential;
+import com.android.systemui.R;
+
+/**
+ * Pin and Password UI
+ */
+public class AuthCredentialPasswordView extends AuthCredentialView
+ implements TextView.OnEditorActionListener {
+
+ private static final String TAG = "BiometricPrompt/AuthCredentialPasswordView";
+
+ private final InputMethodManager mImm;
+ private EditText mPasswordField;
+
+ public AuthCredentialPasswordView(Context context,
+ AttributeSet attrs) {
+ super(context, attrs);
+ mImm = mContext.getSystemService(InputMethodManager.class);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mPasswordField = findViewById(R.id.lockPassword);
+ mPasswordField.setOnEditorActionListener(this);
+ mPasswordField.setOnKeyListener((v, keyCode, event) -> {
+ if (keyCode != KeyEvent.KEYCODE_BACK) {
+ return false;
+ }
+ if (event.getAction() == KeyEvent.ACTION_UP) {
+ mContainerView.animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
+ }
+ return true;
+ });
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ if (mCredentialType == Utils.CREDENTIAL_PIN) {
+ mPasswordField.setInputType(
+ InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
+ }
+
+ // Wait a bit to focus the field so the focusable flag on the window is already set then.
+ post(() -> {
+ mPasswordField.requestFocus();
+ mImm.showSoftInput(mPasswordField, InputMethodManager.SHOW_IMPLICIT);
+ });
+ }
+
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ // Check if this was the result of hitting the enter key
+ final boolean isSoftImeEvent = event == null
+ && (actionId == EditorInfo.IME_NULL
+ || actionId == EditorInfo.IME_ACTION_DONE
+ || actionId == EditorInfo.IME_ACTION_NEXT);
+ final boolean isKeyboardEnterKey = event != null
+ && KeyEvent.isConfirmKey(event.getKeyCode())
+ && event.getAction() == KeyEvent.ACTION_DOWN;
+ if (isSoftImeEvent || isKeyboardEnterKey) {
+ checkPasswordAndUnlock();
+ return true;
+ }
+ return false;
+ }
+
+ private void checkPasswordAndUnlock() {
+ try (LockscreenCredential password = mCredentialType == Utils.CREDENTIAL_PIN
+ ? LockscreenCredential.createPinOrNone(mPasswordField.getText())
+ : LockscreenCredential.createPasswordOrNone(mPasswordField.getText())) {
+ if (password.isNone()) {
+ return;
+ }
+
+ mPendingLockCheck = LockPatternChecker.checkCredential(mLockPatternUtils,
+ password, mUserId, this::onCredentialChecked);
+ }
+ }
+
+ @Override
+ protected void onCredentialChecked(boolean matched, int timeoutMs) {
+ super.onCredentialChecked(matched, timeoutMs);
+
+ if (matched) {
+ mImm.hideSoftInputFromWindow(getWindowToken(), 0 /* flags */);
+ } else {
+ mPasswordField.setText("");
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
new file mode 100644
index 000000000000..14414a4225de
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.android.internal.widget.LockPatternChecker;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternView;
+import com.android.internal.widget.LockscreenCredential;
+import com.android.systemui.R;
+
+import java.util.List;
+
+/**
+ * Pattern UI
+ */
+public class AuthCredentialPatternView extends AuthCredentialView {
+
+ private LockPatternView mLockPatternView;
+
+ private class UnlockPatternListener implements LockPatternView.OnPatternListener {
+
+ @Override
+ public void onPatternStart() {
+
+ }
+
+ @Override
+ public void onPatternCleared() {
+
+ }
+
+ @Override
+ public void onPatternCellAdded(List<LockPatternView.Cell> pattern) {
+
+ }
+
+ @Override
+ public void onPatternDetected(List<LockPatternView.Cell> pattern) {
+ if (mPendingLockCheck != null) {
+ mPendingLockCheck.cancel(false);
+ }
+
+ mLockPatternView.setEnabled(false);
+
+ if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
+ // Pattern size is less than the minimum, do not count it as a failed attempt.
+ onPatternChecked(false /* matched */, 0 /* timeoutMs */);
+ return;
+ }
+
+ try (LockscreenCredential credential = LockscreenCredential.createPattern(pattern)) {
+ mPendingLockCheck = LockPatternChecker.checkCredential(
+ mLockPatternUtils,
+ credential,
+ mUserId,
+ this::onPatternChecked);
+ }
+ }
+
+ private void onPatternChecked(boolean matched, int timeoutMs) {
+ AuthCredentialPatternView.this.onCredentialChecked(matched, timeoutMs);
+ if (timeoutMs > 0) {
+ mLockPatternView.setEnabled(false);
+ } else {
+ mLockPatternView.setEnabled(true);
+ }
+ }
+ }
+
+ @Override
+ protected void onErrorTimeoutFinish() {
+ super.onErrorTimeoutFinish();
+ mLockPatternView.setEnabled(true);
+ }
+
+ public AuthCredentialPatternView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mLockPatternView = findViewById(R.id.lockPattern);
+ mLockPatternView.setOnPatternListener(new UnlockPatternListener());
+ mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(mUserId));
+ mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
new file mode 100644
index 000000000000..8c8611e49dfb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricPrompt;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.CountDownTimer;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+
+/**
+ * Abstract base class for Pin, Pattern, or Password authentication, for
+ * {@link BiometricPrompt.Builder#setDeviceCredentialAllowed(boolean)}
+ */
+public abstract class AuthCredentialView extends LinearLayout {
+
+ private static final String TAG = "BiometricPrompt/AuthCredentialView";
+ private static final int ERROR_DURATION_MS = 3000;
+
+ private final AccessibilityManager mAccessibilityManager;
+
+ protected final Handler mHandler;
+
+ private Bundle mBiometricPromptBundle;
+ private AuthPanelController mPanelController;
+ private boolean mShouldAnimatePanel;
+ private boolean mShouldAnimateContents;
+
+ private TextView mTitleView;
+ private TextView mSubtitleView;
+ private TextView mDescriptionView;
+ protected TextView mErrorView;
+
+ protected @Utils.CredentialType int mCredentialType;
+ protected final LockPatternUtils mLockPatternUtils;
+ protected AuthContainerView mContainerView;
+ protected Callback mCallback;
+ protected AsyncTask<?, ?, ?> mPendingLockCheck;
+ protected int mUserId;
+ protected ErrorTimer mErrorTimer;
+
+ interface Callback {
+ void onCredentialMatched();
+ }
+
+ protected static class ErrorTimer extends CountDownTimer {
+ private final TextView mErrorView;
+ private final Context mContext;
+
+ /**
+ * @param millisInFuture The number of millis in the future from the call
+ * to {@link #start()} until the countdown is done and {@link
+ * #onFinish()}
+ * is called.
+ * @param countDownInterval The interval along the way to receive
+ * {@link #onTick(long)} callbacks.
+ */
+ public ErrorTimer(Context context, long millisInFuture, long countDownInterval,
+ TextView errorView) {
+ super(millisInFuture, countDownInterval);
+ mErrorView = errorView;
+ mContext = context;
+ }
+
+ @Override
+ public void onTick(long millisUntilFinished) {
+ final int secondsCountdown = (int) (millisUntilFinished / 1000);
+ mErrorView.setText(mContext.getString(
+ R.string.biometric_dialog_credential_too_many_attempts, secondsCountdown));
+ }
+
+ @Override
+ public void onFinish() {
+ mErrorView.setText("");
+ }
+ }
+
+ protected final Runnable mClearErrorRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mErrorView.setText("");
+ }
+ };
+
+ public AuthCredentialView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ mLockPatternUtils = new LockPatternUtils(mContext);
+ mHandler = new Handler(Looper.getMainLooper());
+ mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
+ }
+
+ protected void showError(String error) {
+ mHandler.removeCallbacks(mClearErrorRunnable);
+ mErrorView.setText(error);
+ mHandler.postDelayed(mClearErrorRunnable, ERROR_DURATION_MS);
+ }
+
+ private void setTextOrHide(TextView view, String string) {
+ if (TextUtils.isEmpty(string)) {
+ view.setVisibility(View.GONE);
+ } else {
+ view.setText(string);
+ }
+
+ Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
+ }
+
+ private void setText(TextView view, String string) {
+ view.setText(string);
+ }
+
+ void setUser(int user) {
+ mUserId = user;
+ }
+
+ void setCallback(Callback callback) {
+ mCallback = callback;
+ }
+
+ void setBiometricPromptBundle(Bundle bundle) {
+ mBiometricPromptBundle = bundle;
+ }
+
+ void setPanelController(AuthPanelController panelController, boolean animatePanel) {
+ mPanelController = panelController;
+ mShouldAnimatePanel = animatePanel;
+ }
+
+ void setShouldAnimateContents(boolean animateContents) {
+ mShouldAnimateContents = animateContents;
+ }
+
+ void setContainerView(AuthContainerView containerView) {
+ mContainerView = containerView;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ mCredentialType = Utils.getCredentialType(mContext, mUserId);
+
+ setText(mTitleView, mBiometricPromptBundle.getString(BiometricPrompt.KEY_TITLE));
+ setTextOrHide(mSubtitleView,
+ mBiometricPromptBundle.getString(BiometricPrompt.KEY_SUBTITLE));
+ setTextOrHide(mDescriptionView,
+ mBiometricPromptBundle.getString(BiometricPrompt.KEY_DESCRIPTION));
+
+ // Only animate this if we're transitioning from a biometric view.
+ if (mShouldAnimateContents) {
+ setTranslationY(getResources()
+ .getDimension(R.dimen.biometric_dialog_credential_translation_offset));
+ setAlpha(0);
+
+ postOnAnimation(() -> {
+ animate().translationY(0)
+ .setDuration(AuthDialog.ANIMATE_CREDENTIAL_INITIAL_DURATION_MS)
+ .alpha(1.f)
+ .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
+ .withLayer()
+ .start();
+ });
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mErrorTimer != null) {
+ mErrorTimer.cancel();
+ }
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mTitleView = findViewById(R.id.title);
+ mSubtitleView = findViewById(R.id.subtitle);
+ mDescriptionView = findViewById(R.id.description);
+ mErrorView = findViewById(R.id.error);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+
+ if (mShouldAnimatePanel) {
+ // Credential view is always full screen.
+ mPanelController.setUseFullScreen(true);
+ mPanelController.updateForContentDimensions(mPanelController.getContainerWidth(),
+ mPanelController.getContainerHeight(), 0 /* animateDurationMs */);
+ mShouldAnimatePanel = false;
+ }
+ }
+
+ protected void onErrorTimeoutFinish() {}
+
+ protected void onCredentialChecked(boolean matched, int timeoutMs) {
+ if (matched) {
+ mClearErrorRunnable.run();
+ mCallback.onCredentialMatched();
+ } else {
+ if (timeoutMs > 0) {
+ mHandler.removeCallbacks(mClearErrorRunnable);
+ long deadline = mLockPatternUtils.setLockoutAttemptDeadline(mUserId, timeoutMs);
+ mErrorTimer = new ErrorTimer(mContext,
+ deadline - SystemClock.elapsedRealtime(),
+ LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS,
+ mErrorView) {
+ @Override
+ public void onFinish() {
+ onErrorTimeoutFinish();
+ mClearErrorRunnable.run();
+ }
+ };
+ mErrorTimer.start();
+ } else {
+ final int error;
+ switch (mCredentialType) {
+ case Utils.CREDENTIAL_PIN:
+ error = R.string.biometric_dialog_wrong_pin;
+ break;
+ case Utils.CREDENTIAL_PATTERN:
+ error = R.string.biometric_dialog_wrong_pattern;
+ break;
+ case Utils.CREDENTIAL_PASSWORD:
+ error = R.string.biometric_dialog_wrong_password;
+ break;
+ default:
+ error = R.string.biometric_dialog_wrong_password;
+ break;
+ }
+ showError(getResources().getString(error));
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
index edb29538874c..ca95f9d736fc 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
@@ -31,6 +31,8 @@ import java.lang.annotation.RetentionPolicy;
public interface AuthDialog {
String KEY_CONTAINER_STATE = "container_state";
+ String KEY_BIOMETRIC_SHOWING = "biometric_showing";
+ String KEY_CREDENTIAL_SHOWING = "credential_showing";
String KEY_BIOMETRIC_TRY_AGAIN_VISIBILITY = "try_agian_visibility";
String KEY_BIOMETRIC_STATE = "state";
@@ -40,17 +42,38 @@ public interface AuthDialog {
String KEY_BIOMETRIC_DIALOG_SIZE = "size";
int SIZE_UNKNOWN = 0;
+ /**
+ * Minimal UI, showing only biometric icon.
+ */
int SIZE_SMALL = 1;
+ /**
+ * Normal-sized biometric UI, showing title, icon, buttons, etc.
+ */
int SIZE_MEDIUM = 2;
+ /**
+ * Full-screen credential UI.
+ */
int SIZE_LARGE = 3;
@Retention(RetentionPolicy.SOURCE)
@IntDef({SIZE_UNKNOWN, SIZE_SMALL, SIZE_MEDIUM, SIZE_LARGE})
@interface DialogSize {}
/**
- * Animation duration, e.g. small to medium dialog, icon translation, etc.
+ * Animation duration, from small to medium dialog, including back panel, icon translation, etc
+ */
+ int ANIMATE_SMALL_TO_MEDIUM_DURATION_MS = 150;
+ /**
+ * Animation duration from medium to large dialog, including biometric fade out, back panel, etc
+ */
+ int ANIMATE_MEDIUM_TO_LARGE_DURATION_MS = 450;
+ /**
+ * Delay before notifying {@link AuthCredentialView} to start animating in.
+ */
+ int ANIMATE_CREDENTIAL_START_DELAY_MS = ANIMATE_MEDIUM_TO_LARGE_DURATION_MS * 2 / 3;
+ /**
+ * Animation duration when sliding in credential UI
*/
- int ANIMATE_DURATION_MS = 150;
+ int ANIMATE_CREDENTIAL_INITIAL_DURATION_MS = 150;
/**
* Show the dialog.
@@ -101,4 +124,14 @@ public interface AuthDialog {
* Get the client's package name
*/
String getOpPackageName();
+
+ /**
+ * Animate to credential UI. Typically called after biometric is locked out.
+ */
+ void animateToCredentialUI();
+
+ /**
+ * @return true if device credential is allowed.
+ */
+ boolean isAllowDeviceCredentials();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java
index 70752f5f860e..12bb1228a53b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java
@@ -27,17 +27,18 @@ public interface AuthDialogCallback {
int DISMISSED_USER_CANCELED = 1;
int DISMISSED_BUTTON_NEGATIVE = 2;
int DISMISSED_BUTTON_POSITIVE = 3;
-
- int DISMISSED_AUTHENTICATED = 4;
+ int DISMISSED_BIOMETRIC_AUTHENTICATED = 4;
int DISMISSED_ERROR = 5;
int DISMISSED_BY_SYSTEM_SERVER = 6;
+ int DISMISSED_CREDENTIAL_AUTHENTICATED = 7;
@IntDef({DISMISSED_USER_CANCELED,
DISMISSED_BUTTON_NEGATIVE,
DISMISSED_BUTTON_POSITIVE,
- DISMISSED_AUTHENTICATED,
+ DISMISSED_BIOMETRIC_AUTHENTICATED,
DISMISSED_ERROR,
- DISMISSED_BY_SYSTEM_SERVER})
+ DISMISSED_BY_SYSTEM_SERVER,
+ DISMISSED_CREDENTIAL_AUTHENTICATED})
@interface DismissedReason {}
/**
@@ -50,4 +51,9 @@ public interface AuthDialogCallback {
* Invoked when the "try again" button is clicked
*/
void onTryAgainPressed();
+
+ /**
+ * Invoked when the "use password" button is clicked
+ */
+ void onDeviceCredentialPressed();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
index 55ba0491dc1e..2b8b586961ff 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
@@ -16,15 +16,17 @@
package com.android.systemui.biometrics;
+import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.content.Context;
+import android.graphics.Color;
import android.graphics.Outline;
import android.util.Log;
import android.view.View;
import android.view.ViewOutlineProvider;
+import android.view.animation.AccelerateDecelerateInterpolator;
import com.android.systemui.R;
-import com.android.systemui.biometrics.AuthDialog;
/**
* Controls the back panel and its animations for the BiometricPrompt UI.
@@ -36,8 +38,9 @@ public class AuthPanelController extends ViewOutlineProvider {
private final Context mContext;
private final View mPanelView;
- private final float mCornerRadius;
- private final int mBiometricMargin;
+ private final boolean mIsManagedProfile;
+
+ private boolean mUseFullScreen;
private int mContainerWidth;
private int mContainerHeight;
@@ -45,14 +48,23 @@ public class AuthPanelController extends ViewOutlineProvider {
private int mContentWidth;
private int mContentHeight;
+ private float mCornerRadius;
+ private int mMargin;
+
@Override
public void getOutline(View view, Outline outline) {
final int left = (mContainerWidth - mContentWidth) / 2;
final int right = mContainerWidth - left;
+
+ // If the content fits within the container, shrink the height to wrap the content.
+ // Otherwise, set the outline to be the display size minus the margin - the content within
+ // is scrollable.
final int top = mContentHeight < mContainerHeight
- ? mContainerHeight - mContentHeight - mBiometricMargin
- : mBiometricMargin;
- final int bottom = mContainerHeight - mBiometricMargin;
+ ? mContainerHeight - mContentHeight - mMargin
+ : mMargin;
+
+ // TODO(b/139954942) Likely don't need to "+1" after we resolve the navbar styling.
+ final int bottom = mContainerHeight - mMargin + 1;
outline.setRoundRect(left, top, right, bottom, mCornerRadius);
}
@@ -64,11 +76,34 @@ public class AuthPanelController extends ViewOutlineProvider {
mContainerHeight = containerHeight;
}
- public void updateForContentDimensions(int contentWidth, int contentHeight, boolean animate) {
+ public void setUseFullScreen(boolean fullScreen) {
+ mUseFullScreen = fullScreen;
+ }
+
+ public ValueAnimator getTranslationAnimator(float relativeTranslationY) {
+ final ValueAnimator animator = ValueAnimator.ofFloat(
+ mPanelView.getY(), mPanelView.getY() - relativeTranslationY);
+ animator.addUpdateListener(animation -> {
+ final float translation = (float) animation.getAnimatedValue();
+ mPanelView.setTranslationY(translation);
+ });
+ return animator;
+ }
+
+ public ValueAnimator getAlphaAnimator(float alpha) {
+ final ValueAnimator animator = ValueAnimator.ofFloat(mPanelView.getAlpha(), alpha);
+ animator.addUpdateListener(animation -> {
+ mPanelView.setAlpha((float) animation.getAnimatedValue());
+ });
+ return animator;
+ }
+
+ public void updateForContentDimensions(int contentWidth, int contentHeight,
+ int animateDurationMs) {
if (DEBUG) {
Log.v(TAG, "Content Width: " + contentWidth
+ " Height: " + contentHeight
- + " Animate: " + animate);
+ + " Animate: " + animateDurationMs);
}
if (mContainerWidth == 0 || mContainerHeight == 0) {
@@ -76,27 +111,86 @@ public class AuthPanelController extends ViewOutlineProvider {
return;
}
- if (animate) {
+ final int margin = mUseFullScreen ? 0 : (int) mContext.getResources()
+ .getDimension(R.dimen.biometric_dialog_border_padding);
+ final float cornerRadius = mUseFullScreen ? 0 : mContext.getResources()
+ .getDimension(R.dimen.biometric_dialog_corner_size);
+
+ // When going to full-screen for managed profiles, fade away so the managed profile
+ // background behind this view becomes visible.
+ final boolean shouldFadeAway = mUseFullScreen && mIsManagedProfile;
+ final int alpha = shouldFadeAway ? 0 : 255;
+ final float elevation = shouldFadeAway ? 0 :
+ mContext.getResources().getDimension(R.dimen.biometric_dialog_elevation);
+
+ if (animateDurationMs > 0) {
+ // Animate margin
+ ValueAnimator marginAnimator = ValueAnimator.ofInt(mMargin, margin);
+ marginAnimator.addUpdateListener((animation) -> {
+ mMargin = (int) animation.getAnimatedValue();
+ });
+
+ // Animate corners
+ ValueAnimator cornerAnimator = ValueAnimator.ofFloat(mCornerRadius, cornerRadius);
+ cornerAnimator.addUpdateListener((animation) -> {
+ mCornerRadius = (float) animation.getAnimatedValue();
+ });
+
+ // Animate height
ValueAnimator heightAnimator = ValueAnimator.ofInt(mContentHeight, contentHeight);
- heightAnimator.setDuration(AuthDialog.ANIMATE_DURATION_MS);
heightAnimator.addUpdateListener((animation) -> {
mContentHeight = (int) animation.getAnimatedValue();
mPanelView.invalidateOutline();
});
heightAnimator.start();
+
+ // Animate width
+ ValueAnimator widthAnimator = ValueAnimator.ofInt(mContentWidth, contentWidth);
+ widthAnimator.addUpdateListener((animation) -> {
+ mContentWidth = (int) animation.getAnimatedValue();
+ });
+
+ // Animate background
+ ValueAnimator alphaAnimator = ValueAnimator.ofInt(
+ mPanelView.getBackground().getAlpha(), alpha);
+ alphaAnimator.addUpdateListener((animation) -> {
+ if (shouldFadeAway) {
+ mPanelView.getBackground().setAlpha((int) animation.getAnimatedValue());
+ }
+ });
+
+ // Play together
+ AnimatorSet as = new AnimatorSet();
+ as.setDuration(animateDurationMs);
+ as.setInterpolator(new AccelerateDecelerateInterpolator());
+ as.playTogether(cornerAnimator, widthAnimator, marginAnimator, alphaAnimator);
+ as.start();
+
} else {
+ mMargin = margin;
+ mCornerRadius = cornerRadius;
mContentWidth = contentWidth;
mContentHeight = contentHeight;
+ mPanelView.getBackground().setAlpha(alpha);
mPanelView.invalidateOutline();
}
}
- AuthPanelController(Context context, View panelView) {
+ int getContainerWidth() {
+ return mContainerWidth;
+ }
+
+ int getContainerHeight() {
+ return mContainerHeight;
+ }
+
+ AuthPanelController(Context context, View panelView, boolean isManagedProfile) {
mContext = context;
mPanelView = panelView;
+ mIsManagedProfile = isManagedProfile;
mCornerRadius = context.getResources()
.getDimension(R.dimen.biometric_dialog_corner_size);
- mBiometricMargin = (int) context.getResources()
+ mMargin = (int) context.getResources()
.getDimension(R.dimen.biometric_dialog_border_padding);
mPanelView.setOutlineProvider(this);
mPanelView.setClipToOutline(true);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
index e00cf6abafaa..d6f830dd2e7a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
@@ -18,14 +18,36 @@ package com.android.systemui.biometrics;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE;
+import android.annotation.IntDef;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
+import android.hardware.biometrics.Authenticator;
+import android.hardware.biometrics.BiometricPrompt;
+import android.os.Bundle;
+import android.os.UserManager;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.systemui.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
public class Utils {
+
+ public static final int CREDENTIAL_PIN = 1;
+ public static final int CREDENTIAL_PATTERN = 2;
+ public static final int CREDENTIAL_PASSWORD = 3;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({CREDENTIAL_PIN, CREDENTIAL_PATTERN, CREDENTIAL_PASSWORD})
+ @interface CredentialType {}
+
+
static float dpToPixels(Context context, float dp) {
return dp * ((float) context.getResources().getDisplayMetrics().densityDpi
/ DisplayMetrics.DENSITY_DEFAULT);
@@ -46,4 +68,41 @@ public class Utils {
view.sendAccessibilityEventUnchecked(event);
view.notifySubtreeAccessibilityStateChanged(view, view, CONTENT_CHANGE_TYPE_SUBTREE);
}
+
+ static boolean isDeviceCredentialAllowed(Bundle biometricPromptBundle) {
+ final int authenticators = getAuthenticators(biometricPromptBundle);
+ return (authenticators & Authenticator.TYPE_CREDENTIAL) != 0;
+ }
+
+ static boolean isBiometricAllowed(Bundle biometricPromptBundle) {
+ final int authenticators = getAuthenticators(biometricPromptBundle);
+ return (authenticators & Authenticator.TYPE_BIOMETRIC) != 0;
+ }
+
+ static int getAuthenticators(Bundle biometricPromptBundle) {
+ return biometricPromptBundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED);
+ }
+
+ static @CredentialType int getCredentialType(Context context, int userId) {
+ final LockPatternUtils lpu = new LockPatternUtils(context);
+ switch (lpu.getKeyguardStoredPasswordQuality(userId)) {
+ case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
+ return CREDENTIAL_PATTERN;
+ case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
+ case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
+ return CREDENTIAL_PIN;
+ case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
+ case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
+ case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
+ case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
+ return CREDENTIAL_PASSWORD;
+ default:
+ return CREDENTIAL_PASSWORD;
+ }
+ }
+
+ static boolean isManagedProfile(Context context, int userId) {
+ final UserManager userManager = context.getSystemService(UserManager.class);
+ return userManager.isManagedProfile(userId);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
index f0e8c16e650a..ff4711cb208a 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
@@ -26,13 +26,12 @@ import android.os.UserHandle
import android.util.Log
import android.util.SparseArray
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.Dependency.BG_LOOPER_NAME
-import com.android.systemui.Dependency.MAIN_HANDLER_NAME
import com.android.systemui.Dumpable
+import com.android.systemui.dagger.qualifiers.BgLooper
+import com.android.systemui.dagger.qualifiers.MainHandler
import java.io.FileDescriptor
import java.io.PrintWriter
import javax.inject.Inject
-import javax.inject.Named
import javax.inject.Singleton
data class ReceiverData(
@@ -61,8 +60,8 @@ private const val DEBUG = false
@Singleton
open class BroadcastDispatcher @Inject constructor (
private val context: Context,
- @Named(MAIN_HANDLER_NAME) private val mainHandler: Handler,
- @Named(BG_LOOPER_NAME) private val bgLooper: Looper
+ @MainHandler private val mainHandler: Handler,
+ @BgLooper private val bgLooper: Looper
) : Dumpable {
// Only modify in BG thread
@@ -75,7 +74,7 @@ open class BroadcastDispatcher @Inject constructor (
* @param filter A filter to determine what broadcasts should be dispatched to this receiver.
* It will only take into account actions and categories for filtering.
* @param handler A handler to dispatch [BroadcastReceiver.onReceive]. By default, it is the
- * main handler.
+ * main handler. Pass `null` to use the default.
* @param user A user handle to determine which broadcast should be dispatched to this receiver.
* By default, it is the current user.
*/
@@ -83,10 +82,12 @@ open class BroadcastDispatcher @Inject constructor (
fun registerReceiver(
receiver: BroadcastReceiver,
filter: IntentFilter,
- handler: Handler = mainHandler,
+ handler: Handler? = mainHandler,
user: UserHandle = context.user
) {
- this.handler.obtainMessage(MSG_ADD_RECEIVER, ReceiverData(receiver, filter, handler, user))
+ this.handler
+ .obtainMessage(MSG_ADD_RECEIVER,
+ ReceiverData(receiver, filter, handler ?: mainHandler, user))
.sendToTarget()
}
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
index d44b63e813e6..54f9950239c2 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
@@ -34,7 +34,7 @@ import java.util.concurrent.atomic.AtomicBoolean
private const val MSG_REGISTER_RECEIVER = 0
private const val MSG_UNREGISTER_RECEIVER = 1
-private const val TAG = "UniversalReceiver"
+private const val TAG = "UserBroadcastDispatcher"
private const val DEBUG = false
/**
@@ -97,7 +97,7 @@ class UserBroadcastDispatcher(
private val receiverToReceiverData = ArrayMap<BroadcastReceiver, MutableSet<ReceiverData>>()
override fun onReceive(context: Context, intent: Intent) {
- bgHandler.post(HandleBroadcastRunnable(actionsToReceivers, context, intent))
+ bgHandler.post(HandleBroadcastRunnable(actionsToReceivers, context, intent, pendingResult))
}
/**
@@ -160,7 +160,8 @@ class UserBroadcastDispatcher(
private class HandleBroadcastRunnable(
val actionsToReceivers: Map<String, Set<ReceiverData>>,
val context: Context,
- val intent: Intent
+ val intent: Intent,
+ val pendingResult: PendingResult
) : Runnable {
override fun run() {
if (DEBUG) Log.w(TAG, "Dispatching $intent")
@@ -171,6 +172,7 @@ class UserBroadcastDispatcher(
?.forEach {
it.handler.post {
if (DEBUG) Log.w(TAG, "Dispatching to ${it.receiver}")
+ it.receiver.pendingResult = pendingResult
it.receiver.onReceive(context, intent)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index c3cee35d37fb..7600b2f3ed7a 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -83,23 +83,23 @@ class Bubble {
private boolean mSuppressFlyout;
public static String groupId(NotificationEntry entry) {
- UserHandle user = entry.notification.getUser();
- return user.getIdentifier() + "|" + entry.notification.getPackageName();
+ UserHandle user = entry.getSbn().getUser();
+ return user.getIdentifier() + "|" + entry.getSbn().getPackageName();
}
/** Used in tests when no UI is required. */
@VisibleForTesting(visibility = PRIVATE)
Bubble(Context context, NotificationEntry e) {
mEntry = e;
- mKey = e.key;
- mLastUpdated = e.notification.getPostTime();
+ mKey = e.getKey();
+ mLastUpdated = e.getSbn().getPostTime();
mGroupId = groupId(e);
PackageManager pm = context.getPackageManager();
ApplicationInfo info;
try {
info = pm.getApplicationInfo(
- mEntry.notification.getPackageName(),
+ mEntry.getSbn().getPackageName(),
PackageManager.MATCH_UNINSTALLED_PACKAGES
| PackageManager.MATCH_DISABLED_COMPONENTS
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
@@ -107,10 +107,10 @@ class Bubble {
if (info != null) {
mAppName = String.valueOf(pm.getApplicationLabel(info));
}
- Drawable appIcon = pm.getApplicationIcon(mEntry.notification.getPackageName());
- mUserBadgedAppIcon = pm.getUserBadgedIcon(appIcon, mEntry.notification.getUser());
+ Drawable appIcon = pm.getApplicationIcon(mEntry.getSbn().getPackageName());
+ mUserBadgedAppIcon = pm.getUserBadgedIcon(appIcon, mEntry.getSbn().getUser());
} catch (PackageManager.NameNotFoundException unused) {
- mAppName = mEntry.notification.getPackageName();
+ mAppName = mEntry.getSbn().getPackageName();
}
}
@@ -127,7 +127,7 @@ class Bubble {
}
public String getPackageName() {
- return mEntry.notification.getPackageName();
+ return mEntry.getSbn().getPackageName();
}
public String getAppName() {
@@ -190,7 +190,7 @@ class Bubble {
void updateEntry(NotificationEntry entry) {
mEntry = entry;
- mLastUpdated = entry.notification.getPostTime();
+ mLastUpdated = entry.getSbn().getPostTime();
if (mInflated) {
mIconView.update(this);
mExpandedView.update(this);
@@ -287,7 +287,7 @@ class Bubble {
* is an ongoing bubble.
*/
boolean isOngoing() {
- int flags = mEntry.notification.getNotification().flags;
+ int flags = mEntry.getSbn().getNotification().flags;
return (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
}
@@ -296,8 +296,8 @@ class Bubble {
boolean useRes = data.getDesiredHeightResId() != 0;
if (useRes) {
return getDimenForPackageUser(context, data.getDesiredHeightResId(),
- mEntry.notification.getPackageName(),
- mEntry.notification.getUser().getIdentifier());
+ mEntry.getSbn().getPackageName(),
+ mEntry.getSbn().getUser().getIdentifier());
} else {
return data.getDesiredHeight()
* context.getResources().getDisplayMetrics().density;
@@ -316,7 +316,7 @@ class Bubble {
@Nullable
PendingIntent getBubbleIntent(Context context) {
- Notification notif = mEntry.notification.getNotification();
+ Notification notif = mEntry.getSbn().getNotification();
Notification.BubbleMetadata data = notif.getBubbleMetadata();
if (BubbleController.canLaunchInActivityView(context, mEntry) && data != null) {
return data.getIntent();
@@ -327,7 +327,7 @@ class Bubble {
Intent getSettingsIntent() {
final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
- intent.putExtra(Settings.EXTRA_APP_UID, mEntry.notification.getUid());
+ intent.putExtra(Settings.EXTRA_APP_UID, mEntry.getSbn().getUid());
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
@@ -339,7 +339,7 @@ class Bubble {
* notification, based on its type. Returns null if there should not be an update message.
*/
CharSequence getUpdateMessage(Context context) {
- final Notification underlyingNotif = mEntry.notification.getNotification();
+ final Notification underlyingNotif = mEntry.getSbn().getNotification();
final Class<? extends Notification.Style> style = underlyingNotif.getNotificationStyle();
try {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 72ada6e90cd0..0231b562ad04 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -18,7 +18,6 @@ package com.android.systemui.bubbles;
import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY;
import static android.app.Notification.FLAG_BUBBLE;
-import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
@@ -126,8 +125,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
static final int DISMISS_GROUP_CANCELLED = 9;
static final int DISMISS_INVALID_INTENT = 10;
- public static final int MAX_BUBBLES = 5; // TODO: actually enforce this
-
private final Context mContext;
private final NotificationEntryManager mNotificationEntryManager;
private final BubbleTaskStackListener mTaskStackListener;
@@ -265,7 +262,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
// More notifications could be added causing summary to no longer
// be suppressed -- in this case need to remove the key.
final String groupKey = group.summary != null
- ? group.summary.notification.getGroupKey()
+ ? group.summary.getSbn().getGroupKey()
: null;
if (!suppressed && groupKey != null
&& mBubbleData.isSummarySuppressed(groupKey)) {
@@ -349,7 +346,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
return;
}
for (NotificationEntry e : notificationData.getNotificationsForCurrentUser()) {
- if (savedBubbleKeys.contains(e.key)
+ if (savedBubbleKeys.contains(e.getKey())
&& mNotificationInterruptionStateProvider.shouldBubbleUp(e)
&& canLaunchInActivityView(mContext, e)) {
updateBubble(e, /* suppressFlyout= */ true);
@@ -448,7 +445,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
boolean isBubbleAndSuppressed = mBubbleData.hasBubbleWithKey(key)
&& !mBubbleData.getBubbleWithKey(key).showInShadeWhenBubble();
NotificationEntry entry = mNotificationEntryManager.getNotificationData().get(key);
- String groupKey = entry != null ? entry.notification.getGroupKey() : null;
+ String groupKey = entry != null ? entry.getSbn().getGroupKey() : null;
boolean isSuppressedSummary = mBubbleData.isSummarySuppressed(groupKey);
boolean isSummary = key.equals(mBubbleData.getSummaryKey(groupKey));
return (isSummary && isSuppressedSummary) || isBubbleAndSuppressed;
@@ -531,14 +528,14 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
@Override
public boolean onNotificationRemoveRequested(String key, int reason) {
NotificationEntry entry = mNotificationEntryManager.getNotificationData().get(key);
- String groupKey = entry != null ? entry.notification.getGroupKey() : null;
+ String groupKey = entry != null ? entry.getSbn().getGroupKey() : null;
ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
boolean inBubbleData = mBubbleData.hasBubbleWithKey(key);
boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey)
&& mBubbleData.getSummaryKey(groupKey).equals(key));
boolean isSummary = entry != null
- && entry.notification.getNotification().isGroupSummary();
+ && entry.getSbn().getNotification().isGroupSummary();
boolean isSummaryOfBubbles = (isSuppressedSummary || isSummary)
&& bubbleChildren != null && !bubbleChildren.isEmpty();
@@ -569,9 +566,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
bubble.setShowInShadeWhenBubble(false);
bubble.setShowBubbleDot(false);
if (mStackView != null) {
- mStackView.updateDotVisibility(entry.key);
+ mStackView.updateDotVisibility(entry.getKey());
}
- mNotificationEntryManager.updateNotifications();
+ mNotificationEntryManager.updateNotifications(
+ "BubbleController.onNotificationRemoveRequested");
return true;
} else if (!userRemovedNotif && entry != null) {
// This wasn't a user removal so we should remove the bubble as well
@@ -584,7 +582,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
private boolean handleSummaryRemovalInterception(NotificationEntry summary,
boolean userRemovedNotif) {
- String groupKey = summary.notification.getGroupKey();
+ String groupKey = summary.getSbn().getGroupKey();
ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
if (userRemovedNotif) {
@@ -605,13 +603,14 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
// If the summary was auto-generated we don't need to keep that notification around
// because apps can't cancel it; so we only intercept & suppress real summaries.
- boolean isAutogroupSummary = (summary.notification.getNotification().flags
+ boolean isAutogroupSummary = (summary.getSbn().getNotification().flags
& FLAG_AUTOGROUP_SUMMARY) != 0;
if (!isAutogroupSummary) {
- mBubbleData.addSummaryToSuppress(summary.notification.getGroupKey(),
- summary.key);
+ mBubbleData.addSummaryToSuppress(summary.getSbn().getGroupKey(),
+ summary.getKey());
// Tell shade to update for the suppression
- mNotificationEntryManager.updateNotifications();
+ mNotificationEntryManager.updateNotifications(
+ "BubbleController.handleSummaryRemovalInterception");
}
return !isAutogroupSummary;
} else {
@@ -642,11 +641,11 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
public void onPreEntryUpdated(NotificationEntry entry) {
boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
&& canLaunchInActivityView(mContext, entry);
- if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.key)) {
+ if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) {
// It was previously a bubble but no longer a bubble -- lets remove it
- removeBubble(entry.key, DISMISS_NO_LONGER_BUBBLE);
+ removeBubble(entry.getKey(), DISMISS_NO_LONGER_BUBBLE);
} else if (shouldBubble) {
- Bubble b = mBubbleData.getBubbleWithKey(entry.key);
+ Bubble b = mBubbleData.getBubbleWithKey(entry.getKey());
updateBubble(entry);
}
}
@@ -696,10 +695,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
&& !bubble.showInShadeWhenBubble()) {
// The bubble is gone & the notification is gone, time to actually remove it
mNotificationEntryManager.performRemoveNotification(
- bubble.getEntry().notification, UNDEFINED_DISMISS_REASON);
+ bubble.getEntry().getSbn(), UNDEFINED_DISMISS_REASON);
} else {
// Update the flag for SysUI
- bubble.getEntry().notification.getNotification().flags &= ~FLAG_BUBBLE;
+ bubble.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE;
// Make sure NoMan knows it's not a bubble anymore so anyone querying it
// will get right result back
@@ -713,7 +712,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
// Check if removed bubble has an associated suppressed group summary that needs
// to be removed now.
- final String groupKey = bubble.getEntry().notification.getGroupKey();
+ final String groupKey = bubble.getEntry().getSbn().getGroupKey();
if (mBubbleData.isSummarySuppressed(groupKey)
&& mBubbleData.getBubblesInGroup(groupKey).isEmpty()) {
// Time to actually remove the summary.
@@ -722,20 +721,21 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
NotificationEntry entry =
mNotificationEntryManager.getNotificationData().get(notifKey);
mNotificationEntryManager.performRemoveNotification(
- entry.notification, UNDEFINED_DISMISS_REASON);
+ entry.getSbn(), UNDEFINED_DISMISS_REASON);
}
// Check if summary should be removed from NoManGroup
NotificationEntry summary = mNotificationGroupManager.getLogicalGroupSummary(
- bubble.getEntry().notification);
+ bubble.getEntry().getSbn());
if (summary != null) {
ArrayList<NotificationEntry> summaryChildren =
- mNotificationGroupManager.getLogicalChildren(summary.notification);
- boolean isSummaryThisNotif = summary.key.equals(bubble.getEntry().key);
+ mNotificationGroupManager.getLogicalChildren(summary.getSbn());
+ boolean isSummaryThisNotif = summary.getKey().equals(
+ bubble.getEntry().getKey());
if (!isSummaryThisNotif
&& (summaryChildren == null || summaryChildren.isEmpty())) {
mNotificationEntryManager.performRemoveNotification(
- summary.notification, UNDEFINED_DISMISS_REASON);
+ summary.getSbn(), UNDEFINED_DISMISS_REASON);
}
}
}
@@ -762,7 +762,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mStackView.setExpanded(true);
}
- mNotificationEntryManager.updateNotifications();
+ mNotificationEntryManager.updateNotifications(
+ "BubbleData.Listener.applyUpdate");
updateStack();
if (DEBUG_BUBBLE_CONTROLLER) {
@@ -964,16 +965,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
+ intent);
return false;
}
- if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) {
- Log.w(TAG, "Unable to send as bubble -- activity is not documentLaunchMode=always "
- + "for intent: " + intent);
- return false;
- }
- if ((info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) {
- Log.w(TAG, "Unable to send as bubble -- activity is not embeddable for intent: "
- + intent);
- return false;
- }
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index d43e030ed9eb..4e229c00b891 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -183,8 +183,8 @@ public class BubbleData {
if (DEBUG_BUBBLE_DATA) {
Log.d(TAG, "notificationEntryUpdated: " + entry);
}
- Bubble bubble = getBubbleWithKey(entry.key);
- suppressFlyout = !entry.isVisuallyInterruptive || suppressFlyout;
+ Bubble bubble = getBubbleWithKey(entry.getKey());
+ suppressFlyout = !entry.getRanking().visuallyInterruptive() || suppressFlyout;
if (bubble == null) {
// Create a new bubble
@@ -217,7 +217,7 @@ public class BubbleData {
if (DEBUG_BUBBLE_DATA) {
Log.d(TAG, "notificationEntryRemoved: entry=" + entry + " reason=" + reason);
}
- doRemove(entry.key, reason);
+ doRemove(entry.getKey(), reason);
dispatchPendingChanges();
}
@@ -290,7 +290,7 @@ public class BubbleData {
return bubbleChildren;
}
for (Bubble b : mBubbles) {
- if (groupKey.equals(b.getEntry().notification.getGroupKey())) {
+ if (groupKey.equals(b.getEntry().getSbn().getGroupKey())) {
bubbleChildren.add(b);
}
}
@@ -633,7 +633,8 @@ public class BubbleData {
try {
deleteIntent.send();
} catch (PendingIntent.CanceledException e) {
- Log.w(TAG, "Failed to send delete intent for bubble with key: " + entry.key);
+ Log.w(TAG, "Failed to send delete intent for bubble with key: "
+ + entry.getKey());
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 521ebde7d2f0..1d9f6b27fcdd 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -16,6 +16,8 @@
package com.android.systemui.bubbles;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.view.Display.INVALID_DISPLAY;
import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW;
@@ -128,8 +130,12 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
Log.d(TAG, "onActivityViewReady: calling startActivity, "
+ "bubble=" + getBubbleKey());
}
+ Intent fillInIntent = new Intent();
+ // Apply flags to make behaviour match documentLaunchMode=always.
+ fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
+ fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
try {
- mActivityView.startActivity(mBubbleIntent, options);
+ mActivityView.startActivity(mBubbleIntent, fillInIntent, options);
} catch (RuntimeException e) {
// If there's a runtime exception here then there's something
// wrong with the intent, we can't really recover / try to populate
@@ -495,7 +501,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
if (id == R.id.settings_button) {
Intent intent = mBubble.getSettingsIntent();
mStackView.collapseStack(() -> {
- mContext.startActivityAsUser(intent, mBubble.getEntry().notification.getUser());
+ mContext.startActivityAsUser(intent, mBubble.getEntry().getSbn().getUser());
logBubbleClickEvent(mBubble,
StatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
});
@@ -603,7 +609,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
* @param action the user interaction enum.
*/
private void logBubbleClickEvent(Bubble bubble, int action) {
- StatusBarNotification notification = bubble.getEntry().notification;
+ StatusBarNotification notification = bubble.getEntry().getSbn();
StatsLog.write(StatsLog.BUBBLE_UI_CHANGED,
notification.getPackageName(),
notification.getNotification().getChannelId(),
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 31cf853dce04..e5af3897dff2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -181,7 +181,9 @@ public class BubbleStackView extends FrameLayout {
*/
private float mVerticalPosPercentBeforeRotation = -1;
+ private int mMaxBubbles;
private int mBubbleSize;
+ private int mBubbleElevation;
private int mBubblePaddingTop;
private int mBubbleTouchPadding;
private int mExpandedViewPadding;
@@ -326,7 +328,9 @@ public class BubbleStackView extends FrameLayout {
mInflater = LayoutInflater.from(context);
Resources res = getResources();
+ mMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
+ mBubbleElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
mBubbleTouchPadding = res.getDimensionPixelSize(R.dimen.bubble_touch_padding);
mExpandedAnimateXDistance =
@@ -625,7 +629,7 @@ public class BubbleStackView extends FrameLayout {
}
Bubble topBubble = mBubbleData.getBubbles().get(0);
String appName = topBubble.getAppName();
- Notification notification = topBubble.getEntry().notification.getNotification();
+ Notification notification = topBubble.getEntry().getSbn().getNotification();
CharSequence titleCharSeq = notification.extras.getCharSequence(Notification.EXTRA_TITLE);
String titleStr = getResources().getString(R.string.stream_notification);
if (titleCharSeq != null) {
@@ -1597,8 +1601,7 @@ public class BubbleStackView extends FrameLayout {
for (int i = 0; i < bubbleCount; i++) {
BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i);
bv.updateDotVisibility(true /* animate */);
- bv.setZ((BubbleController.MAX_BUBBLES
- * getResources().getDimensionPixelSize(R.dimen.bubble_elevation)) - i);
+ bv.setZ((mMaxBubbles * mBubbleElevation) - i);
// If the dot is on the left, and so is the stack, we need to change the dot position.
if (bv.getDotPositionOnLeft() == mStackOnLeftOrWillBe) {
bv.setDotPosition(!mStackOnLeftOrWillBe, animate);
@@ -1678,7 +1681,7 @@ public class BubbleStackView extends FrameLayout {
*/
private void logBubbleEvent(@Nullable Bubble bubble, int action) {
if (bubble == null || bubble.getEntry() == null
- || bubble.getEntry().notification == null) {
+ || bubble.getEntry().getSbn() == null) {
StatsLog.write(StatsLog.BUBBLE_UI_CHANGED,
null /* package name */,
null /* notification channel */,
@@ -1692,7 +1695,7 @@ public class BubbleStackView extends FrameLayout {
false /* on-going bubble */,
false /* isAppForeground (unused) */);
} else {
- StatusBarNotification notification = bubble.getEntry().notification;
+ StatusBarNotification notification = bubble.getEntry().getSbn();
StatsLog.write(StatsLog.BUBBLE_UI_CHANGED,
notification.getPackageName(),
notification.getNotification().getChannelId(),
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index 4512aa822e3b..fe4fa90a272d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -39,7 +39,6 @@ import com.android.launcher3.icons.ShadowGenerator;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
/**
* A floating object on the screen that can post message updates.
@@ -141,15 +140,6 @@ public class BubbleView extends FrameLayout {
mUserBadgedAppIcon = appIcon;
}
- /**
- * @return the {@link ExpandableNotificationRow} view to display notification content when the
- * bubble is expanded.
- */
- @Nullable
- public ExpandableNotificationRow getRowView() {
- return (mBubble != null) ? mBubble.getEntry().getRow() : null;
- }
-
/** Changes the dot's visibility to match the bubble view's state. */
void updateDotVisibility(boolean animate) {
updateDotVisibility(animate, null /* after */);
@@ -230,7 +220,7 @@ public class BubbleView extends FrameLayout {
}
// Update icon.
Notification.BubbleMetadata metadata = mBubble.getEntry().getBubbleMetadata();
- Notification n = mBubble.getEntry().notification.getNotification();
+ Notification n = mBubble.getEntry().getSbn().getNotification();
Icon ic = metadata.getIcon();
boolean needsTint = ic.getType() != Icon.TYPE_ADAPTIVE_BITMAP;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java
index cb7c998fad8e..8105c6a6e940 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java
@@ -37,7 +37,7 @@ import java.util.Locale;
* adb shell setprop debug.falsing_log true
*
* The log gets dumped as part of the SystemUI services. To dump on demand:
- * adb shell dumpsys activity service com.android.systemui SystemBars | grep -A 999 FALSING | less
+ * adb shell dumpsys activity service com.android.systemui StatusBar | grep -A 999 FALSING | less
*
* To dump into logcat:
* adb shell setprop debug.falsing_logcat true
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index 914258f48b46..db85fa0a3203 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -17,7 +17,6 @@
package com.android.systemui.classifier;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_MANAGER_ENABLED;
-import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
import android.content.Context;
import android.hardware.SensorManager;
@@ -31,6 +30,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dependency;
import com.android.systemui.classifier.brightline.BrightLineFalsingManager;
import com.android.systemui.classifier.brightline.FalsingDataProvider;
+import com.android.systemui.dagger.qualifiers.MainHandler;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.FalsingPlugin;
import com.android.systemui.plugins.PluginListener;
@@ -41,7 +41,6 @@ import com.android.systemui.util.sensors.ProximitySensor;
import java.io.PrintWriter;
import javax.inject.Inject;
-import javax.inject.Named;
import javax.inject.Singleton;
/**
@@ -62,7 +61,7 @@ public class FalsingManagerProxy implements FalsingManager {
@Inject
FalsingManagerProxy(Context context, PluginManager pluginManager,
- @Named(MAIN_HANDLER_NAME) Handler handler,
+ @MainHandler Handler handler,
ProximitySensor proximitySensor,
DeviceConfigProxy deviceConfig) {
mProximitySensor = proximitySensor;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java
index 85a4d234b5df..b726c3e2783f 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java
@@ -16,6 +16,9 @@
package com.android.systemui.classifier.brightline;
+import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
+import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
+
import android.view.MotionEvent;
import java.util.Locale;
@@ -29,6 +32,7 @@ import java.util.Locale;
class PointerCountClassifier extends FalsingClassifier {
private static final int MAX_ALLOWED_POINTERS = 1;
+ private static final int MAX_ALLOWED_POINTERS_SWIPE_DOWN = 2;
private int mMaxPointerCount;
PointerCountClassifier(FalsingDataProvider dataProvider) {
@@ -50,6 +54,10 @@ class PointerCountClassifier extends FalsingClassifier {
@Override
public boolean isFalseTouch() {
+ int interactionType = getInteractionType();
+ if (interactionType == QUICK_SETTINGS || interactionType == NOTIFICATION_DRAG_DOWN) {
+ return mMaxPointerCount > MAX_ALLOWED_POINTERS_SWIPE_DOWN;
+ }
return mMaxPointerCount > MAX_ALLOWED_POINTERS;
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/ActivityBinder.java
new file mode 100644
index 000000000000..4be610fcd9ee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ActivityBinder.java
@@ -0,0 +1,45 @@
+/*
+ * 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.dagger;
+
+import android.app.Activity;
+
+import com.android.systemui.ForegroundServicesDialog;
+import com.android.systemui.tuner.TunerActivity;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+
+/**
+ * Services and Activities that are injectable should go here.
+ */
+@Module
+public abstract class ActivityBinder {
+ /** Inject into TunerActivity. */
+ @Binds
+ @IntoMap
+ @ClassKey(TunerActivity.class)
+ public abstract Activity bindTunerActivity(TunerActivity activity);
+
+ /** Inject into ForegroundServicesDialog. */
+ @Binds
+ @IntoMap
+ @ClassKey(ForegroundServicesDialog.class)
+ public abstract Activity bindForegroundServicesDialog(ForegroundServicesDialog activity);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ComponentBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/ComponentBinder.java
new file mode 100644
index 000000000000..4e4c06e9d447
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ComponentBinder.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dagger;
+
+import dagger.Binds;
+import dagger.Module;
+
+/**
+ * Dagger Module that collects related sub-modules together.
+ */
+@Module(includes = {ActivityBinder.class, ServiceBinder.class, SystemUIBinder.class})
+public abstract class ComponentBinder {
+ /** */
+ @Binds
+ public abstract ContextComponentHelper bindComponentHelper(
+ ContextComponentResolver componentHelper);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java
new file mode 100644
index 000000000000..d6d1e418240a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java
@@ -0,0 +1,36 @@
+/*
+ * 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.dagger;
+
+import android.app.Activity;
+import android.app.Service;
+
+import com.android.systemui.SystemUI;
+
+/**
+ * Interface necessary to make Dagger happy. See {@link ContextComponentResolver}.
+ */
+public interface ContextComponentHelper {
+ /** Turns a classname into an instance of the class or returns null. */
+ Activity resolveActivity(String className);
+
+ /** Turns a classname into an instance of the class or returns null. */
+ Service resolveService(String className);
+
+ /** Turns a classname into an instance of the class or returns null. */
+ SystemUI resolveSystemUI(String className);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java
new file mode 100644
index 000000000000..d7822c9fc6da
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java
@@ -0,0 +1,82 @@
+/*
+ * 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.dagger;
+
+import android.app.Activity;
+import android.app.Service;
+
+import com.android.systemui.SystemUI;
+
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+/**
+ * Used during Service and Activity instantiation to make them injectable.
+ */
+@Singleton
+public class ContextComponentResolver implements ContextComponentHelper {
+ private final Map<Class<?>, Provider<Activity>> mActivityCreators;
+ private final Map<Class<?>, Provider<Service>> mServiceCreators;
+ private final Map<Class<?>, Provider<SystemUI>> mSystemUICreators;
+
+ @Inject
+ ContextComponentResolver(
+ Map<Class<?>, Provider<Activity>> activityCreators,
+ Map<Class<?>, Provider<Service>> serviceCreators,
+ Map<Class<?>, Provider<SystemUI>> systemUICreators) {
+ mActivityCreators = activityCreators;
+ mServiceCreators = serviceCreators;
+ mSystemUICreators = systemUICreators;
+ }
+
+ /**
+ * Looks up the Activity class name to see if Dagger has an instance of it.
+ */
+ @Override
+ public Activity resolveActivity(String className) {
+ return resolve(className, mActivityCreators);
+ }
+
+ /**
+ * Looks up the Service class name to see if Dagger has an instance of it.
+ */
+ @Override
+ public Service resolveService(String className) {
+ return resolve(className, mServiceCreators);
+ }
+
+ /**
+ * Looks up the SystemUI class name to see if Dagger has an instance of it.
+ */
+ @Override
+ public SystemUI resolveSystemUI(String className) {
+ return resolve(className, mSystemUICreators);
+ }
+
+ private <T> T resolve(String className, Map<Class<?>, Provider<T>> creators) {
+ try {
+ Class<?> clazz = Class.forName(className);
+ Provider<T> provider = creators.get(clazz);
+ return provider == null ? null : provider.get();
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
new file mode 100644
index 000000000000..9032c6fc8639
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
@@ -0,0 +1,252 @@
+/*
+ * 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.dagger;
+
+import com.android.systemui.ActivityStarterDelegate;
+import com.android.systemui.appops.AppOpsController;
+import com.android.systemui.appops.AppOpsControllerImpl;
+import com.android.systemui.classifier.FalsingManagerProxy;
+import com.android.systemui.doze.DozeHost;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.VolumeDialogController;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.power.PowerNotificationWarnings;
+import com.android.systemui.power.PowerUI;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.StatusBarStateControllerImpl;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
+import com.android.systemui.statusbar.phone.DozeServiceHost;
+import com.android.systemui.statusbar.phone.ManagedProfileController;
+import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
+import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BatteryControllerImpl;
+import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
+import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.CastControllerImpl;
+import com.android.systemui.statusbar.policy.ExtensionController;
+import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
+import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
+import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.HotspotControllerImpl;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.KeyguardStateControllerImpl;
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.LocationControllerImpl;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkControllerImpl;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
+import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.statusbar.policy.SecurityControllerImpl;
+import com.android.systemui.statusbar.policy.SensorPrivacyController;
+import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl;
+import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.tuner.TunerServiceImpl;
+import com.android.systemui.volume.VolumeDialogControllerImpl;
+
+import dagger.Binds;
+import dagger.Module;
+
+/**
+ * Maps interfaces to implementations for use with Dagger.
+ */
+@Module
+public abstract class DependencyBinder {
+
+ /**
+ */
+ @Binds
+ public abstract ActivityStarter provideActivityStarter(ActivityStarterDelegate delegate);
+
+ /**
+ */
+ @Binds
+ public abstract BluetoothController provideBluetoothController(
+ BluetoothControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract LocationController provideLocationController(
+ LocationControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract RotationLockController provideRotationLockController(
+ RotationLockControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract NetworkController provideNetworkController(
+ NetworkControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract ZenModeController provideZenModeController(
+ ZenModeControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract HotspotController provideHotspotController(
+ HotspotControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract AppOpsController provideAppOpsController(
+ AppOpsControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract NotificationRemoteInputManager.Callback provideNotificationRemoteInputManager(
+ StatusBarRemoteInputCallback callbackImpl);
+
+ /**
+ */
+ @Binds
+ public abstract CastController provideCastController(CastControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract FlashlightController provideFlashlightController(
+ FlashlightControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract KeyguardStateController provideKeyguardMonitor(
+ KeyguardStateControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract UserInfoController provideUserInfoContrller(
+ UserInfoControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract BatteryController provideBatteryController(
+ BatteryControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract ManagedProfileController provideManagedProfileController(
+ ManagedProfileControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract NextAlarmController provideNextAlarmController(
+ NextAlarmControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract SecurityController provideSecurityController(
+ SecurityControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract TunerService provideTunerService(TunerServiceImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract DarkIconDispatcher provideDarkIconDispatcher(
+ DarkIconDispatcherImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract StatusBarStateController provideStatusBarStateController(
+ StatusBarStateControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract SysuiStatusBarStateController providesSysuiStatusBarStateController(
+ StatusBarStateControllerImpl statusBarStateControllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract StatusBarIconController provideStatusBarIconController(
+ StatusBarIconControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract ExtensionController provideExtensionController(
+ ExtensionControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract VolumeDialogController provideVolumeDialogController(
+ VolumeDialogControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract PowerUI.WarningsUI provideWarningsUi(PowerNotificationWarnings controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract SensorPrivacyController provideSensorPrivacyControllerImpl(
+ SensorPrivacyControllerImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract QSHost provideQsHost(QSTileHost controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract FalsingManager provideFalsingManager(FalsingManagerProxy falsingManagerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract DozeHost provideDozeHost(DozeServiceHost dozeServiceHost);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
new file mode 100644
index 000000000000..87434f344729
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -0,0 +1,234 @@
+/*
+ * 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.dagger;
+
+import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
+
+import android.app.INotificationManager;
+import android.content.Context;
+import android.hardware.display.AmbientDisplayConfiguration;
+import android.hardware.display.NightDisplayListener;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Process;
+import android.os.ServiceManager;
+import android.util.DisplayMetrics;
+import android.view.IWindowManager;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.systemui.dagger.qualifiers.BgHandler;
+import com.android.systemui.dagger.qualifiers.BgLooper;
+import com.android.systemui.dagger.qualifiers.MainHandler;
+import com.android.systemui.dagger.qualifiers.MainLooper;
+import com.android.systemui.doze.AlwaysOnDisplayPolicy;
+import com.android.systemui.plugins.PluginInitializerImpl;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManagerImpl;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
+import com.android.systemui.statusbar.NavigationBarController;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.DataSaverController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.util.leak.LeakDetector;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Provides dependencies for the root component of sysui injection.
+ *
+ * Only SystemUI owned classes and instances should go in here. Other, framework-owned classes
+ * should go in {@link SystemServicesModule}.
+ *
+ * See SystemUI/docs/dagger.md
+ */
+@Module
+public class DependencyProvider {
+
+ @Singleton
+ @Provides
+ @Named(TIME_TICK_HANDLER_NAME)
+ public Handler provideTimeTickHandler() {
+ HandlerThread thread = new HandlerThread("TimeTick");
+ thread.start();
+ return new Handler(thread.getLooper());
+ }
+
+ @Singleton
+ @Provides
+ @BgLooper
+ public Looper provideBgLooper() {
+ HandlerThread thread = new HandlerThread("SysUiBg",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ thread.start();
+ return thread.getLooper();
+ }
+
+ /** Main Looper */
+ @Provides
+ @MainLooper
+ public Looper provideMainLooper() {
+ return Looper.getMainLooper();
+ }
+
+ @Singleton
+ @Provides
+ @BgHandler
+ public Handler provideBgHandler(@BgLooper Looper bgLooper) {
+ return new Handler(bgLooper);
+ }
+
+ @Singleton
+ @Provides
+ @MainHandler
+ public Handler provideMainHandler(@MainLooper Looper mainLooper) {
+ return new Handler(mainLooper);
+ }
+
+ /** */
+ @Provides
+ public AmbientDisplayConfiguration provideAmbientDispalyConfiguration(Context context) {
+ return new AmbientDisplayConfiguration(context);
+ }
+
+ /** */
+ @Provides
+ public Handler provideHandler() {
+ return new Handler();
+ }
+
+ @Singleton
+ @Provides
+ public DataSaverController provideDataSaverController(NetworkController networkController) {
+ return networkController.getDataSaverController();
+ }
+
+ @Singleton
+ @Provides
+ // Single instance of DisplayMetrics, gets updated by StatusBar, but can be used
+ // anywhere it is needed.
+ public DisplayMetrics provideDisplayMetrics() {
+ return new DisplayMetrics();
+ }
+
+ @Singleton
+ @Provides
+ public IStatusBarService provideIStatusBarService() {
+ return IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ }
+
+ /** */
+ @Singleton
+ @Provides
+ public INotificationManager provideINotificationManager() {
+ return INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ }
+
+ @Singleton
+ @Provides
+ public LeakDetector provideLeakDetector() {
+ return LeakDetector.create();
+
+ }
+
+ @Singleton
+ @Provides
+ public MetricsLogger provideMetricsLogger() {
+ return new MetricsLogger();
+ }
+
+ @Singleton
+ @Provides
+ public NightDisplayListener provideNightDisplayListener(Context context,
+ @BgHandler Handler bgHandler) {
+ return new NightDisplayListener(context, bgHandler);
+ }
+
+ @Singleton
+ @Provides
+ public PluginManager providePluginManager(Context context) {
+ return new PluginManagerImpl(context, new PluginInitializerImpl());
+ }
+
+ @Singleton
+ @Provides
+ public NavigationBarController provideNavigationBarController(Context context,
+ @MainHandler Handler mainHandler) {
+ return new NavigationBarController(context, mainHandler);
+ }
+
+ @Singleton
+ @Provides
+ public ConfigurationController provideConfigurationController(Context context) {
+ return new ConfigurationControllerImpl(context);
+ }
+
+ @Singleton
+ @Provides
+ public AutoHideController provideAutoHideController(Context context,
+ @MainHandler Handler mainHandler,
+ NotificationRemoteInputManager notificationRemoteInputManager,
+ IWindowManager iWindowManager) {
+ return new AutoHideController(context, mainHandler, notificationRemoteInputManager,
+ iWindowManager);
+ }
+
+ @Singleton
+ @Provides
+ public ActivityManagerWrapper provideActivityManagerWrapper() {
+ return ActivityManagerWrapper.getInstance();
+ }
+
+ @Singleton
+ @Provides
+ public DevicePolicyManagerWrapper provideDevicePolicyManagerWrapper() {
+ return DevicePolicyManagerWrapper.getInstance();
+ }
+
+ @Singleton
+ @Provides
+ public DeviceProvisionedController provideDeviceProvisionedController(Context context,
+ @MainHandler Handler mainHandler) {
+ return new DeviceProvisionedControllerImpl(context, mainHandler);
+ }
+
+ /** */
+ @Provides
+ public LockPatternUtils provideLockPatternUtils(Context context) {
+ return new LockPatternUtils(context);
+ }
+
+ /** */
+ @Provides
+ public AlwaysOnDisplayPolicy provideAlwaysOnDisplayPolicy(Context context) {
+ return new AlwaysOnDisplayPolicy(context);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ServiceBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/ServiceBinder.java
new file mode 100644
index 000000000000..1f2c0a18f928
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ServiceBinder.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dagger;
+
+import android.app.Service;
+
+import com.android.systemui.ImageWallpaper;
+import com.android.systemui.doze.DozeService;
+import com.android.systemui.keyguard.KeyguardService;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+
+/**
+ * Services that are injectable should go here.
+ */
+@Module
+public abstract class ServiceBinder {
+ /** */
+ @Binds
+ @IntoMap
+ @ClassKey(DozeService.class)
+ public abstract Service bindDozeService(DozeService service);
+
+ /** */
+ @Binds
+ @IntoMap
+ @ClassKey(ImageWallpaper.class)
+ public abstract Service bindImageWallpaper(ImageWallpaper service);
+
+ /** */
+ @Binds
+ @IntoMap
+ @ClassKey(KeyguardService.class)
+ public abstract Service bindKeyguardService(KeyguardService service);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
new file mode 100644
index 000000000000..fffba8cc1639
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
@@ -0,0 +1,130 @@
+/*
+ * 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.dagger;
+
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.app.ActivityManager;
+import android.app.AlarmManager;
+import android.app.IActivityManager;
+import android.app.IWallpaperManager;
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.SensorPrivacyManager;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.view.IWindowManager;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+
+import com.android.internal.util.LatencyTracker;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.systemui.dagger.qualifiers.BgHandler;
+import com.android.systemui.dagger.qualifiers.MainResources;
+import com.android.systemui.shared.system.PackageManagerWrapper;
+
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Provides Non-SystemUI, Framework-Owned instances to the dependency graph.
+ */
+@Module
+public class SystemServicesModule {
+
+ @Singleton
+ @Provides
+ static AlarmManager provideAlarmManager(Context context) {
+ return context.getSystemService(AlarmManager.class);
+ }
+
+ @Singleton
+ @Provides
+ static IActivityManager provideIActivityManager() {
+ return ActivityManager.getService();
+ }
+
+ @Provides
+ @Nullable
+ static IWallpaperManager provideIWallPaperManager() {
+ return IWallpaperManager.Stub.asInterface(
+ ServiceManager.getService(Context.WALLPAPER_SERVICE));
+ }
+
+ @Singleton
+ @Provides
+ static IWindowManager provideIWindowManager() {
+ return WindowManagerGlobal.getWindowManagerService();
+ }
+
+ @Singleton
+ @Provides
+ static LatencyTracker provideLatencyTracker(Context context) {
+ return LatencyTracker.getInstance(context);
+ }
+
+ @SuppressLint("MissingPermission")
+ @Singleton
+ @Provides
+ @Nullable
+ static LocalBluetoothManager provideLocalBluetoothController(Context context,
+ @BgHandler Handler bgHandler) {
+ return LocalBluetoothManager.create(context, bgHandler, UserHandle.ALL);
+ }
+
+ @Singleton
+ @Provides
+ static PackageManagerWrapper providePackageManagerWrapper() {
+ return PackageManagerWrapper.getInstance();
+ }
+
+ /** */
+ @Singleton
+ @Provides
+ static PowerManager providePowerManager(Context context) {
+ return context.getSystemService(PowerManager.class);
+ }
+
+ @Provides
+ @MainResources
+ static Resources provideResources(Context context) {
+ return context.getResources();
+ }
+
+ @Singleton
+ @Provides
+ static SensorPrivacyManager provideSensorPrivacyManager(Context context) {
+ return context.getSystemService(SensorPrivacyManager.class);
+ }
+
+ @Provides
+ static WallpaperManager provideWallpaperManager(Context context) {
+ return (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE);
+ }
+
+ @Singleton
+ @Provides
+ static WindowManager provideWindowManager(Context context) {
+ return context.getSystemService(WindowManager.class);
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
new file mode 100644
index 000000000000..71c2e389f059
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -0,0 +1,83 @@
+/*
+ * 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.dagger;
+
+import com.android.systemui.LatencyTester;
+import com.android.systemui.SystemUI;
+import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.pip.PipUI;
+import com.android.systemui.power.PowerUI;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsModule;
+import com.android.systemui.util.leak.GarbageMonitor;
+import com.android.systemui.volume.VolumeUI;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+
+/**
+ * SystemUI objects that are injectable should go here.
+ */
+@Module(includes = {RecentsModule.class})
+public abstract class SystemUIBinder {
+
+ /** Inject into GarbageMonitor.Service. */
+ @Binds
+ @IntoMap
+ @ClassKey(GarbageMonitor.Service.class)
+ public abstract SystemUI bindGarbageMonitorService(GarbageMonitor.Service service);
+
+ /** Inject into KeyguardViewMediator. */
+ @Binds
+ @IntoMap
+ @ClassKey(KeyguardViewMediator.class)
+ public abstract SystemUI bindKeyguardViewMediator(KeyguardViewMediator sysui);
+
+ /** Inject into LatencyTests. */
+ @Binds
+ @IntoMap
+ @ClassKey(LatencyTester.class)
+ public abstract SystemUI bindLatencyTester(LatencyTester sysui);
+
+ /** Inject into PipUI. */
+ @Binds
+ @IntoMap
+ @ClassKey(PipUI.class)
+ public abstract SystemUI bindPipUI(PipUI sysui);
+
+ /** Inject into PowerUI. */
+ @Binds
+ @IntoMap
+ @ClassKey(PowerUI.class)
+ public abstract SystemUI bindPowerUI(PowerUI sysui);
+
+ /** Inject into Recents. */
+ @Binds
+ @IntoMap
+ @ClassKey(Recents.class)
+ public abstract SystemUI bindRecents(Recents sysui);
+
+
+ /** Inject into VolumeUI. */
+ @Binds
+ @IntoMap
+ @ClassKey(VolumeUI.class)
+ public abstract SystemUI bindVolumeUI(VolumeUI sysui);
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
new file mode 100644
index 000000000000..c95b50b195b3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dagger;
+
+import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
+import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.SystemUI;
+import com.android.systemui.dock.DockManager;
+import com.android.systemui.dock.DockManagerImpl;
+import com.android.systemui.power.EnhancedEstimates;
+import com.android.systemui.power.EnhancedEstimatesImpl;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+
+/**
+ * A dagger module for injecting default implementations of components of System UI that may be
+ * overridden by the System UI implementation.
+ */
+@Module
+abstract class SystemUIDefaultModule {
+
+ @Singleton
+ @Provides
+ @Named(LEAK_REPORT_EMAIL_NAME)
+ @Nullable
+ static String provideLeakReportEmail() {
+ return null;
+ }
+
+ @Binds
+ abstract EnhancedEstimates bindEnhancedEstimates(EnhancedEstimatesImpl enhancedEstimates);
+
+ @Binds
+ abstract NotificationLockscreenUserManager bindNotificationLockscreenUserManager(
+ NotificationLockscreenUserManagerImpl notificationLockscreenUserManager);
+
+ @Binds
+ abstract DockManager bindDockManager(DockManagerImpl dockManager);
+
+ @Binds
+ abstract NotificationData.KeyguardEnvironment bindKeyguardEnvironment(
+ KeyguardEnvironmentImpl keyguardEnvironment);
+
+ @Binds
+ abstract ShadeController provideShadeController(StatusBar statusBar);
+
+ @Binds
+ @IntoMap
+ @ClassKey(StatusBar.class)
+ public abstract SystemUI providesStatusBar(StatusBar statusBar);
+
+ @Singleton
+ @Provides
+ @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME)
+ static boolean provideAllowNotificationLongPress() {
+ return true;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
new file mode 100644
index 000000000000..4e60f194e552
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -0,0 +1,65 @@
+/*
+ * 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.dagger;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.assist.AssistModule;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.people.PeopleHubModule;
+import com.android.systemui.statusbar.phone.KeyguardLiftController;
+import com.android.systemui.util.sensors.AsyncSensorManager;
+
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * A dagger module for injecting components of System UI that are not overridden by the System UI
+ * implementation.
+ */
+@Module(includes = {AssistModule.class,
+ ComponentBinder.class,
+ PeopleHubModule.class})
+public abstract class SystemUIModule {
+
+ @Singleton
+ @Provides
+ @Nullable
+ static KeyguardLiftController provideKeyguardLiftController(Context context,
+ StatusBarStateController statusBarStateController,
+ AsyncSensorManager asyncSensorManager,
+ KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
+ return null;
+ }
+ return new KeyguardLiftController(statusBarStateController, asyncSensorManager,
+ keyguardUpdateMonitor);
+ }
+
+
+ @Singleton
+ @Provides
+ static SysUiState provideSysUiState() {
+ return new SysUiState();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
new file mode 100644
index 000000000000..113c9c845d95
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dagger;
+
+import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
+
+import android.content.ContentProvider;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.SystemUIAppComponentFactory;
+import com.android.systemui.SystemUIFactory;
+import com.android.systemui.fragments.FragmentService;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.util.InjectionInflationController;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Component;
+
+/**
+ * Root component for Dagger injection.
+ */
+@Singleton
+@Component(modules = {
+ DependencyProvider.class,
+ DependencyBinder.class,
+ SystemServicesModule.class,
+ SystemUIFactory.ContextHolder.class,
+ SystemUIModule.class,
+ SystemUIDefaultModule.class})
+public interface SystemUIRootComponent {
+
+ /**
+ * Creates a ContextComponentHelper.
+ */
+ @Singleton
+ ContextComponentHelper getContextComponentHelper();
+
+ /**
+ * Main dependency providing module.
+ */
+ @Singleton
+ Dependency.DependencyInjector createDependency();
+
+ /**
+ * Injects the StatusBar.
+ */
+ @Singleton
+ StatusBar.StatusBarInjector getStatusBarInjector();
+
+ /**
+ * FragmentCreator generates all Fragments that need injection.
+ */
+ @Singleton
+ FragmentService.FragmentCreator createFragmentCreator();
+
+ /**
+ * ViewCreator generates all Views that need injection.
+ */
+ InjectionInflationController.ViewCreator createViewCreator();
+
+ /**
+ * Whether notification long press is allowed.
+ */
+ @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME)
+ boolean allowNotificationLongPressName();
+
+ /**
+ * Member injection into the supplied argument.
+ */
+ void inject(SystemUIAppComponentFactory factory);
+
+ /**
+ * Member injection into the supplied argument.
+ */
+ void inject(ContentProvider contentProvider);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/BgHandler.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/BgHandler.java
new file mode 100644
index 000000000000..bc6b83ba1def
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/BgHandler.java
@@ -0,0 +1,30 @@
+/*
+ * 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.dagger.qualifiers;
+
+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 BgHandler {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/BgLooper.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/BgLooper.java
new file mode 100644
index 000000000000..2aadda1215d5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/BgLooper.java
@@ -0,0 +1,30 @@
+/*
+ * 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.dagger.qualifiers;
+
+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 BgLooper {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainHandler.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainHandler.java
new file mode 100644
index 000000000000..79661fa4a738
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainHandler.java
@@ -0,0 +1,30 @@
+/*
+ * 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.dagger.qualifiers;
+
+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 MainHandler {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainLooper.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainLooper.java
new file mode 100644
index 000000000000..750d7d72035c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainLooper.java
@@ -0,0 +1,30 @@
+/*
+ * 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.dagger.qualifiers;
+
+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 MainLooper {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainResources.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainResources.java
new file mode 100644
index 000000000000..3daeda550b4c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainResources.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dagger.qualifiers;
+
+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 MainResources {
+ // TODO: use attribute to get other, non-main resources?
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
index 1d7e9eacbd2e..419fd622d1df 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
@@ -82,7 +82,7 @@ public class DozeDockHandler implements DozeMachine.Part {
private void requestPulse(State dozeState) {
if (!mDozeHost.isPulsingBlocked() && dozeState.canPulse()) {
- mMachine.requestPulse(DozeLog.PULSE_REASON_DOCKING);
+ mMachine.requestPulse(DozeEvent.PULSE_REASON_DOCKING);
}
mPulsePending = false;
}
@@ -91,7 +91,7 @@ public class DozeDockHandler implements DozeMachine.Part {
if (dozeState == State.DOZE_REQUEST_PULSE || dozeState == State.DOZE_PULSING
|| dozeState == State.DOZE_PULSING_BRIGHT) {
final int pulseReason = mMachine.getPulseReason();
- if (pulseReason == DozeLog.PULSE_REASON_DOCKING) {
+ if (pulseReason == DozeEvent.PULSE_REASON_DOCKING) {
mDozeHost.stopPulsing();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeEvent.java b/packages/SystemUI/src/com/android/systemui/doze/DozeEvent.java
new file mode 100644
index 000000000000..ea1def07a309
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeEvent.java
@@ -0,0 +1,154 @@
+/*
+ * 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.doze;
+
+import android.annotation.IntDef;
+
+import com.android.systemui.log.RichEvent;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An event related to dozing. {@link DozeLog} stores and prints these events for debugging
+ * and triaging purposes.
+ */
+public class DozeEvent extends RichEvent {
+ public static final int TOTAL_EVENT_TYPES = 19;
+
+ public DozeEvent(int logLevel, int type, String reason) {
+ super(logLevel, type, reason);
+ }
+
+ /**
+ * Event labels for each doze event
+ * Index corresponds to the integer associated with each {@link EventType}
+ */
+ @Override
+ public String[] getEventLabels() {
+ final String[] events = new String[]{
+ "PickupWakeup",
+ "PulseStart",
+ "PulseFinish",
+ "NotificationPulse",
+ "Dozing",
+ "Fling",
+ "EmergencyCall",
+ "KeyguardBouncerChanged",
+ "ScreenOn",
+ "ScreenOff",
+ "MissedTick",
+ "TimeTickScheduled",
+ "KeyguardVisibilityChanged",
+ "DozeStateChanged",
+ "WakeDisplay",
+ "ProximityResult",
+ "PulseDropped",
+ "PulseDisabledByProx",
+ "SensorTriggered"
+ };
+
+ if (events.length != TOTAL_EVENT_TYPES) {
+ throw new IllegalStateException("DozeEvent events.length should match TOTAL_EVENT_TYPES"
+ + " events.length=" + events.length
+ + " TOTAL_EVENT_LENGTH=" + TOTAL_EVENT_TYPES);
+ }
+ return events;
+ }
+
+ /**
+ * Converts the reason (integer) to a user-readable string
+ */
+ public static String reasonToString(@Reason int pulseReason) {
+ switch (pulseReason) {
+ case PULSE_REASON_INTENT: return "intent";
+ case PULSE_REASON_NOTIFICATION: return "notification";
+ case PULSE_REASON_SENSOR_SIGMOTION: return "sigmotion";
+ case REASON_SENSOR_PICKUP: return "pickup";
+ case REASON_SENSOR_DOUBLE_TAP: return "doubletap";
+ case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress";
+ case PULSE_REASON_DOCKING: return "docking";
+ case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakelockscreen";
+ case REASON_SENSOR_WAKE_UP: return "wakeup";
+ case REASON_SENSOR_TAP: return "tap";
+ default: throw new IllegalArgumentException("invalid reason: " + pulseReason);
+ }
+ }
+
+ /**
+ * Builds a DozeEvent.
+ */
+ public static class DozeEventBuilder extends RichEvent.Builder<DozeEventBuilder> {
+ @Override
+ public DozeEventBuilder getBuilder() {
+ return this;
+ }
+
+ @Override
+ public RichEvent build() {
+ return new DozeEvent(mLogLevel, mType, mReason);
+ }
+ }
+
+ @IntDef({PICKUP_WAKEUP, PULSE_START, PULSE_FINISH, NOTIFICATION_PULSE, DOZING, FLING,
+ EMERGENCY_CALL, KEYGUARD_BOUNCER_CHANGED, SCREEN_ON, SCREEN_OFF, MISSED_TICK,
+ TIME_TICK_SCHEDULED, KEYGUARD_VISIBILITY_CHANGE, DOZE_STATE_CHANGED, WAKE_DISPLAY,
+ PROXIMITY_RESULT, PULSE_DROPPED, PULSE_DISABLED_BY_PROX, SENSOR_TRIGGERED})
+ /**
+ * Types of DozeEvents
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EventType {}
+ public static final int PICKUP_WAKEUP = 0;
+ public static final int PULSE_START = 1;
+ public static final int PULSE_FINISH = 2;
+ public static final int NOTIFICATION_PULSE = 3;
+ public static final int DOZING = 4;
+ public static final int FLING = 5;
+ public static final int EMERGENCY_CALL = 6;
+ public static final int KEYGUARD_BOUNCER_CHANGED = 7;
+ public static final int SCREEN_ON = 8;
+ public static final int SCREEN_OFF = 9;
+ public static final int MISSED_TICK = 10;
+ public static final int TIME_TICK_SCHEDULED = 11;
+ public static final int KEYGUARD_VISIBILITY_CHANGE = 12;
+ public static final int DOZE_STATE_CHANGED = 13;
+ public static final int WAKE_DISPLAY = 14;
+ public static final int PROXIMITY_RESULT = 15;
+ public static final int PULSE_DROPPED = 16;
+ public static final int PULSE_DISABLED_BY_PROX = 17;
+ public static final int SENSOR_TRIGGERED = 18;
+
+ public static final int TOTAL_REASONS = 10;
+ @IntDef({PULSE_REASON_NONE, PULSE_REASON_INTENT, PULSE_REASON_NOTIFICATION,
+ PULSE_REASON_SENSOR_SIGMOTION, REASON_SENSOR_PICKUP, REASON_SENSOR_DOUBLE_TAP,
+ PULSE_REASON_SENSOR_LONG_PRESS, PULSE_REASON_DOCKING, REASON_SENSOR_WAKE_UP,
+ PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, REASON_SENSOR_TAP})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Reason {}
+ public static final int PULSE_REASON_NONE = -1;
+ public static final int PULSE_REASON_INTENT = 0;
+ public static final int PULSE_REASON_NOTIFICATION = 1;
+ public static final int PULSE_REASON_SENSOR_SIGMOTION = 2;
+ public static final int REASON_SENSOR_PICKUP = 3;
+ public static final int REASON_SENSOR_DOUBLE_TAP = 4;
+ public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5;
+ public static final int PULSE_REASON_DOCKING = 6;
+ public static final int REASON_SENSOR_WAKE_UP = 7;
+ public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 8;
+ public static final int REASON_SENSOR_TAP = 9;
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index bb8c7f1dabf8..33f68cfc1492 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -16,8 +16,10 @@
package com.android.systemui.doze;
+import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.Application;
+import android.app.IWallpaperManager;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorManager;
@@ -25,7 +27,6 @@ import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SystemUIApplication;
import com.android.systemui.dock.DockManager;
@@ -33,49 +34,84 @@ 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 {
- public 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 DelayedWakeLock.Builder mDelayedWakeLockBuilder;
+ private final Handler mHandler;
+ private final BiometricUnlockController mBiometricUnlockController;
+
+ @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,
+ DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler,
+ BiometricUnlockController biometricUnlockController) {
+ mFalsingManager = falsingManager;
+ mDozeLog = dozeLog;
+ mDozeParameters = dozeParameters;
+ mBatteryController = batteryController;
+ mAsyncSensorManager = asyncSensorManager;
+ mAlarmManager = alarmManager;
+ mWakefulnessLifecycle = wakefulnessLifecycle;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mDockManager = dockManager;
+ mWallpaperManager = wallpaperManager;
+ mProximitySensor = proximitySensor;
+ mDelayedWakeLockBuilder = delayedWakeLockBuilder;
+ mHandler = handler;
+ mBiometricUnlockController = biometricUnlockController;
}
/** Creates a DozeMachine with its parts for {@code dozeService}. */
- public DozeMachine assembleMachine(DozeService dozeService, FalsingManager falsingManager) {
- Context context = dozeService;
- AsyncSensorManager sensorManager = Dependency.get(AsyncSensorManager.class);
- AlarmManager alarmManager = context.getSystemService(AlarmManager.class);
- DockManager dockManager = Dependency.get(DockManager.class);
- WakefulnessLifecycle wakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
-
+ DozeMachine assembleMachine(DozeService dozeService) {
DozeHost host = getHost(dozeService);
- AmbientDisplayConfiguration config = new AmbientDisplayConfiguration(context);
- DozeParameters params = DozeParameters.getInstance(context);
- Handler handler = new Handler();
- WakeLock wakeLock = new DelayedWakeLock(handler,
- WakeLock.createPartial(context, "Doze"));
+ AmbientDisplayConfiguration config = new AmbientDisplayConfiguration(dozeService);
+ WakeLock wakeLock = mDelayedWakeLockBuilder.setHandler(mHandler).setTag("Doze").build();
DozeMachine.Service wrappedService = dozeService;
wrappedService = new DozeBrightnessHostForwarder(wrappedService, host);
- wrappedService = DozeScreenStatePreventingAdapter.wrapIfNeeded(wrappedService, params);
- wrappedService = DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(wrappedService,
- params);
+ wrappedService = DozeScreenStatePreventingAdapter.wrapIfNeeded(
+ wrappedService, mDozeParameters);
+ wrappedService = DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(
+ wrappedService, mDozeParameters);
DozeMachine machine = new DozeMachine(wrappedService, config, wakeLock,
- wakefulnessLifecycle);
+ mWakefulnessLifecycle, mBatteryController, mDozeLog);
machine.setParts(new DozeMachine.Part[]{
- new DozePauser(handler, machine, alarmManager, params.getPolicy()),
- new DozeFalsingManagerAdapter(falsingManager),
- createDozeTriggers(context, sensorManager, host, alarmManager, config, params,
- handler, wakeLock, machine, dockManager),
- createDozeUi(context, host, wakeLock, machine, handler, alarmManager, params),
- new DozeScreenState(wrappedService, handler, params, wakeLock),
- createDozeScreenBrightness(context, wrappedService, sensorManager, host, params,
- handler),
- new DozeWallpaperState(context, getBiometricUnlockController(dozeService)),
- new DozeDockHandler(context, machine, host, config, handler, dockManager),
+ new DozePauser(mHandler, machine, mAlarmManager, mDozeParameters.getPolicy()),
+ new DozeFalsingManagerAdapter(mFalsingManager),
+ createDozeTriggers(dozeService, mAsyncSensorManager, host, mAlarmManager, config,
+ mDozeParameters, mHandler, wakeLock, machine, mDockManager, mDozeLog),
+ createDozeUi(dozeService, host, wakeLock, machine, mHandler, mAlarmManager,
+ mDozeParameters, mDozeLog),
+ new DozeScreenState(wrappedService, mHandler, host, mDozeParameters, wakeLock),
+ createDozeScreenBrightness(dozeService, wrappedService, mAsyncSensorManager, host,
+ mDozeParameters, mHandler),
+ new DozeWallpaperState(
+ mWallpaperManager, mBiometricUnlockController, mDozeParameters),
+ new DozeDockHandler(dozeService, machine, host, config, mHandler, mDockManager),
new DozeAuthRemover(dozeService)
});
@@ -94,17 +130,19 @@ public class DozeFactory {
private DozeTriggers createDozeTriggers(Context context, AsyncSensorManager sensorManager,
DozeHost host, AlarmManager alarmManager, AmbientDisplayConfiguration config,
DozeParameters params, Handler handler, WakeLock wakeLock, DozeMachine machine,
- DockManager dockManager) {
+ DockManager dockManager, DozeLog dozeLog) {
boolean allowPulseTriggers = true;
return new DozeTriggers(context, machine, host, alarmManager, config, params,
- sensorManager, handler, wakeLock, allowPulseTriggers, dockManager);
+ sensorManager, handler, wakeLock, allowPulseTriggers, dockManager,
+ mProximitySensor, dozeLog);
+
}
private DozeMachine.Part createDozeUi(Context context, DozeHost host, WakeLock wakeLock,
DozeMachine machine, Handler handler, AlarmManager alarmManager,
- DozeParameters params) {
+ DozeParameters params, DozeLog dozeLog) {
return new DozeUi(context, alarmManager, machine, wakeLock, host, handler, params,
- Dependency.get(KeyguardUpdateMonitor.class));
+ mKeyguardUpdateMonitor, dozeLog);
}
public static DozeHost getHost(DozeService service) {
@@ -112,10 +150,4 @@ public class DozeFactory {
final SystemUIApplication app = (SystemUIApplication) appCandidate;
return app.getComponent(DozeHost.class);
}
-
- public static BiometricUnlockController getBiometricUnlockController(DozeService service) {
- Application appCandidate = service.getApplication();
- final SystemUIApplication app = (SystemUIApplication) appCandidate;
- return app.getComponent(BiometricUnlockController.class);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 07dd2cd77043..d1047e216ec4 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -63,9 +63,16 @@ public interface DozeHost {
void setDozeScreenBrightness(int value);
/**
- * Makes scrims black and changes animation durations.
+ * Fade out screen before switching off the display power mode.
+ * @param onDisplayOffCallback Executed when the display is black.
*/
- default void prepareForGentleWakeUp() {}
+ void prepareForGentleSleep(Runnable onDisplayOffCallback);
+
+ /**
+ * Cancel pending {@code onDisplayOffCallback} callback.
+ * @see #prepareForGentleSleep(Runnable)
+ */
+ void cancelGentleSleep();
void onIgnoreTouchWhilePulsing(boolean ignore);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 8fe9f929b880..2e4466d47927 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -16,282 +16,284 @@
package com.android.systemui.doze;
-import android.content.Context;
-import android.os.Build;
-import android.util.Log;
import android.util.TimeUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.Dependency;
+import com.android.systemui.DumpController;
+import com.android.systemui.log.SysuiLog;
import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
import java.util.Date;
-public class DozeLog {
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Logs doze events for debugging and triaging purposes. Logs are dumped in bugreports or on demand:
+ * adb shell dumpsys activity service com.android.systemui/.SystemUIService \
+ * dependency DumpController DozeLog
+ */
+@Singleton
+public class DozeLog extends SysuiLog {
private static final String TAG = "DozeLog";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final boolean ENABLED = true;
- private static final int SIZE = Build.IS_DEBUGGABLE ? 400 : 50;
- static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
-
- public static final int REASONS = 10;
-
- public static final int PULSE_REASON_NONE = -1;
- public static final int PULSE_REASON_INTENT = 0;
- public static final int PULSE_REASON_NOTIFICATION = 1;
- public static final int PULSE_REASON_SENSOR_SIGMOTION = 2;
- public static final int REASON_SENSOR_PICKUP = 3;
- public static final int REASON_SENSOR_DOUBLE_TAP = 4;
- public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5;
- public static final int PULSE_REASON_DOCKING = 6;
- public static final int REASON_SENSOR_WAKE_UP = 7;
- public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 8;
- public static final int REASON_SENSOR_TAP = 9;
-
- private static boolean sRegisterKeyguardCallback = true;
-
- private static long[] sTimes;
- private static String[] sMessages;
- private static int sPosition;
- private static int sCount;
- private static boolean sPulsing;
-
- private static long sSince;
- private static SummaryStats sPickupPulseNearVibrationStats;
- private static SummaryStats sPickupPulseNotNearVibrationStats;
- private static SummaryStats sNotificationPulseStats;
- private static SummaryStats sScreenOnPulsingStats;
- private static SummaryStats sScreenOnNotPulsingStats;
- private static SummaryStats sEmergencyCallStats;
- private static SummaryStats[][] sProxStats; // [reason][near/far]
-
- public static void tracePickupWakeUp(Context context, boolean withinVibrationThreshold) {
- if (!ENABLED) return;
- init(context);
- log("pickupWakeUp withinVibrationThreshold=" + withinVibrationThreshold);
- (withinVibrationThreshold ? sPickupPulseNearVibrationStats
- : sPickupPulseNotNearVibrationStats).append();
+
+ private boolean mPulsing;
+ private long mSince;
+ private SummaryStats mPickupPulseNearVibrationStats;
+ private SummaryStats mPickupPulseNotNearVibrationStats;
+ private SummaryStats mNotificationPulseStats;
+ private SummaryStats mScreenOnPulsingStats;
+ private SummaryStats mScreenOnNotPulsingStats;
+ private SummaryStats mEmergencyCallStats;
+ private SummaryStats[][] mProxStats; // [reason][near/far]
+
+ @Inject
+ public DozeLog(KeyguardUpdateMonitor keyguardUpdateMonitor, DumpController dumpController) {
+ super(dumpController, TAG, MAX_DOZE_DEBUG_LOGS, MAX_DOZE_LOGS);
+ mSince = System.currentTimeMillis();
+ mPickupPulseNearVibrationStats = new SummaryStats();
+ mPickupPulseNotNearVibrationStats = new SummaryStats();
+ mNotificationPulseStats = new SummaryStats();
+ mScreenOnPulsingStats = new SummaryStats();
+ mScreenOnNotPulsingStats = new SummaryStats();
+ mEmergencyCallStats = new SummaryStats();
+ mProxStats = new SummaryStats[DozeEvent.TOTAL_REASONS][2];
+ for (int i = 0; i < DozeEvent.TOTAL_REASONS; i++) {
+ mProxStats[i][0] = new SummaryStats();
+ mProxStats[i][1] = new SummaryStats();
+ }
+
+ if (keyguardUpdateMonitor != null) {
+ keyguardUpdateMonitor.registerCallback(mKeyguardCallback);
+ }
}
- public static void tracePulseStart(int reason) {
- if (!ENABLED) return;
- sPulsing = true;
- log("pulseStart reason=" + reasonToString(reason));
+ /**
+ * Appends pickup wakeup event to the logs
+ */
+ public void tracePickupWakeUp(boolean withinVibrationThreshold) {
+ if (log(DozeEvent.PICKUP_WAKEUP,
+ "withinVibrationThreshold=" + withinVibrationThreshold)) {
+ (withinVibrationThreshold ? mPickupPulseNearVibrationStats
+ : mPickupPulseNotNearVibrationStats).append();
+ }
}
- public static void tracePulseFinish() {
- if (!ENABLED) return;
- sPulsing = false;
- log("pulseFinish");
+ /**
+ * Appends pulse started event to the logs.
+ * @param reason why the pulse started
+ */
+ public void tracePulseStart(@DozeEvent.Reason int reason) {
+ if (log(DozeEvent.PULSE_START, DozeEvent.reasonToString(reason))) {
+ mPulsing = true;
+ }
}
- public static void traceNotificationPulse(Context context) {
- if (!ENABLED) return;
- init(context);
- log("notificationPulse");
- sNotificationPulseStats.append();
+ /**
+ * Appends pulse finished event to the logs
+ */
+ public void tracePulseFinish() {
+ if (log(DozeEvent.PULSE_FINISH)) {
+ mPulsing = false;
+ }
}
- private static void init(Context context) {
- synchronized (DozeLog.class) {
- if (sMessages == null) {
- sTimes = new long[SIZE];
- sMessages = new String[SIZE];
- sSince = System.currentTimeMillis();
- sPickupPulseNearVibrationStats = new SummaryStats();
- sPickupPulseNotNearVibrationStats = new SummaryStats();
- sNotificationPulseStats = new SummaryStats();
- sScreenOnPulsingStats = new SummaryStats();
- sScreenOnNotPulsingStats = new SummaryStats();
- sEmergencyCallStats = new SummaryStats();
- sProxStats = new SummaryStats[REASONS][2];
- for (int i = 0; i < REASONS; i++) {
- sProxStats[i][0] = new SummaryStats();
- sProxStats[i][1] = new SummaryStats();
- }
- log("init");
- if (sRegisterKeyguardCallback) {
- Dependency.get(KeyguardUpdateMonitor.class).registerCallback(sKeyguardCallback);
- }
- }
+ /**
+ * Appends pulse event to the logs
+ */
+ public void traceNotificationPulse() {
+ if (log(DozeEvent.NOTIFICATION_PULSE)) {
+ mNotificationPulseStats.append();
}
}
- public static void traceDozing(Context context, boolean dozing) {
- if (!ENABLED) return;
- sPulsing = false;
- init(context);
- log("dozing " + dozing);
+ /**
+ * Appends dozing event to the logs
+ * @param dozing true if dozing, else false
+ */
+ public void traceDozing(boolean dozing) {
+ if (log(DozeEvent.DOZING, "dozing=" + dozing)) {
+ mPulsing = false;
+ }
}
- public static void traceFling(boolean expand, boolean aboveThreshold, boolean thresholdNeeded,
+ /**
+ * Appends fling event to the logs
+ */
+ public void traceFling(boolean expand, boolean aboveThreshold, boolean thresholdNeeded,
boolean screenOnFromTouch) {
- if (!ENABLED) return;
- log("fling expand=" + expand + " aboveThreshold=" + aboveThreshold + " thresholdNeeded="
- + thresholdNeeded + " screenOnFromTouch=" + screenOnFromTouch);
+ log(DozeEvent.FLING, "expand=" + expand
+ + " aboveThreshold=" + aboveThreshold
+ + " thresholdNeeded=" + thresholdNeeded
+ + " screenOnFromTouch=" + screenOnFromTouch);
}
- public static void traceEmergencyCall() {
- if (!ENABLED) return;
- log("emergencyCall");
- sEmergencyCallStats.append();
+ /**
+ * Appends emergency call event to the logs
+ */
+ public void traceEmergencyCall() {
+ if (log(DozeEvent.EMERGENCY_CALL)) {
+ mEmergencyCallStats.append();
+ }
}
- public static void traceKeyguardBouncerChanged(boolean showing) {
- if (!ENABLED) return;
- log("bouncer " + showing);
+ /**
+ * Appends keyguard bouncer changed event to the logs
+ * @param showing true if the keyguard bouncer is showing, else false
+ */
+ public void traceKeyguardBouncerChanged(boolean showing) {
+ log(DozeEvent.KEYGUARD_BOUNCER_CHANGED, "showing=" + showing);
}
- public static void traceScreenOn() {
- if (!ENABLED) return;
- log("screenOn pulsing=" + sPulsing);
- (sPulsing ? sScreenOnPulsingStats : sScreenOnNotPulsingStats).append();
- sPulsing = false;
+ /**
+ * Appends screen-on event to the logs
+ */
+ public void traceScreenOn() {
+ if (log(DozeEvent.SCREEN_ON, "pulsing=" + mPulsing)) {
+ (mPulsing ? mScreenOnPulsingStats : mScreenOnNotPulsingStats).append();
+ mPulsing = false;
+ }
}
- public static void traceScreenOff(int why) {
- if (!ENABLED) return;
- log("screenOff why=" + why);
+ /**
+ * Appends screen-off event to the logs
+ * @param why reason the screen is off
+ */
+ public void traceScreenOff(int why) {
+ log(DozeEvent.SCREEN_OFF, "why=" + why);
}
- public static void traceMissedTick(String delay) {
- if (!ENABLED) return;
- log("missedTick by=" + delay);
+ /**
+ * Appends missed tick event to the logs
+ * @param delay of the missed tick
+ */
+ public void traceMissedTick(String delay) {
+ log(DozeEvent.MISSED_TICK, "delay=" + delay);
}
- public static void traceTimeTickScheduled(long when, long triggerAt) {
- if (!ENABLED) return;
- log("timeTickScheduled at=" + FORMAT.format(new Date(when)) + " triggerAt="
- + FORMAT.format(new Date(triggerAt)));
+ /**
+ * Appends time tick scheduled event to the logs
+ * @param when time tick scheduled at
+ * @param triggerAt time tick trigger at
+ */
+ public void traceTimeTickScheduled(long when, long triggerAt) {
+ log(DozeEvent.TIME_TICK_SCHEDULED,
+ "scheduledAt=" + DATE_FORMAT.format(new Date(when))
+ + " triggerAt=" + DATE_FORMAT.format(new Date(triggerAt)));
}
- public static void traceKeyguard(boolean showing) {
- if (!ENABLED) return;
- log("keyguard " + showing);
- if (!showing) {
- sPulsing = false;
+ /**
+ * Appends keyguard visibility change event to the logs
+ * @param showing whether the keyguard is now showing
+ */
+ public void traceKeyguard(boolean showing) {
+ if (log(DozeEvent.KEYGUARD_VISIBILITY_CHANGE, "showing=" + showing)
+ && !showing) {
+ mPulsing = false;
}
}
- public static void traceState(DozeMachine.State state) {
- if (!ENABLED) return;
- log("state " + state);
+ /**
+ * Appends doze state changed event to the logs
+ * @param state new DozeMachine state
+ */
+ public void traceState(DozeMachine.State state) {
+ log(DozeEvent.DOZE_STATE_CHANGED, state.name());
}
/**
* Appends wake-display event to the logs.
* @param wake if we're waking up or sleeping.
*/
- public static void traceWakeDisplay(boolean wake) {
- if (!ENABLED) return;
- log("wakeDisplay " + wake);
- }
-
- public static void traceProximityResult(Context context, boolean near, long millis,
- int reason) {
- if (!ENABLED) return;
- init(context);
- log("proximityResult reason=" + reasonToString(reason) + " near=" + near
- + " millis=" + millis);
- sProxStats[reason][near ? 0 : 1].append();
+ public void traceWakeDisplay(boolean wake) {
+ log(DozeEvent.WAKE_DISPLAY, "wake=" + wake);
}
- public static String reasonToString(int pulseReason) {
- switch (pulseReason) {
- case PULSE_REASON_INTENT: return "intent";
- case PULSE_REASON_NOTIFICATION: return "notification";
- case PULSE_REASON_SENSOR_SIGMOTION: return "sigmotion";
- case REASON_SENSOR_PICKUP: return "pickup";
- case REASON_SENSOR_DOUBLE_TAP: return "doubletap";
- case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress";
- case PULSE_REASON_DOCKING: return "docking";
- case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakelockscreen";
- case REASON_SENSOR_WAKE_UP: return "wakeup";
- case REASON_SENSOR_TAP: return "tap";
- default: throw new IllegalArgumentException("bad reason: " + pulseReason);
+ /**
+ * Appends proximity result event to the logs
+ * @param near true if near, else false
+ * @param millis
+ * @param reason why proximity result was triggered
+ */
+ public void traceProximityResult(boolean near, long millis, @DozeEvent.Reason int reason) {
+ if (log(DozeEvent.PROXIMITY_RESULT,
+ " reason=" + DozeEvent.reasonToString(reason)
+ + " near=" + near
+ + " millis=" + millis)) {
+ mProxStats[reason][near ? 0 : 1].append();
}
}
- public static void dump(PrintWriter pw) {
+ /**
+ * Prints doze log timeline and consolidated stats
+ * @param pw
+ */
+ public void dump(PrintWriter pw) {
synchronized (DozeLog.class) {
- if (sMessages == null) return;
- pw.println(" Doze log:");
- final int start = (sPosition - sCount + SIZE) % SIZE;
- for (int i = 0; i < sCount; i++) {
- final int j = (start + i) % SIZE;
- pw.print(" ");
- pw.print(FORMAT.format(new Date(sTimes[j])));
- pw.print(' ');
- pw.println(sMessages[j]);
- }
+ super.dump(null, pw, null); // prints timeline
+
pw.print(" Doze summary stats (for ");
- TimeUtils.formatDuration(System.currentTimeMillis() - sSince, pw);
+ TimeUtils.formatDuration(System.currentTimeMillis() - mSince, pw);
pw.println("):");
- sPickupPulseNearVibrationStats.dump(pw, "Pickup pulse (near vibration)");
- sPickupPulseNotNearVibrationStats.dump(pw, "Pickup pulse (not near vibration)");
- sNotificationPulseStats.dump(pw, "Notification pulse");
- sScreenOnPulsingStats.dump(pw, "Screen on (pulsing)");
- sScreenOnNotPulsingStats.dump(pw, "Screen on (not pulsing)");
- sEmergencyCallStats.dump(pw, "Emergency call");
- for (int i = 0; i < REASONS; i++) {
- final String reason = reasonToString(i);
- sProxStats[i][0].dump(pw, "Proximity near (" + reason + ")");
- sProxStats[i][1].dump(pw, "Proximity far (" + reason + ")");
+ mPickupPulseNearVibrationStats.dump(pw, "Pickup pulse (near vibration)");
+ mPickupPulseNotNearVibrationStats.dump(pw, "Pickup pulse (not near vibration)");
+ mNotificationPulseStats.dump(pw, "Notification pulse");
+ mScreenOnPulsingStats.dump(pw, "Screen on (pulsing)");
+ mScreenOnNotPulsingStats.dump(pw, "Screen on (not pulsing)");
+ mEmergencyCallStats.dump(pw, "Emergency call");
+ for (int i = 0; i < DozeEvent.TOTAL_REASONS; i++) {
+ final String reason = DozeEvent.reasonToString(i);
+ mProxStats[i][0].dump(pw, "Proximity near (" + reason + ")");
+ mProxStats[i][1].dump(pw, "Proximity far (" + reason + ")");
}
}
}
- private static void log(String msg) {
- synchronized (DozeLog.class) {
- if (sMessages == null) return;
- sTimes[sPosition] = System.currentTimeMillis();
- sMessages[sPosition] = msg;
- sPosition = (sPosition + 1) % SIZE;
- sCount = Math.min(sCount + 1, SIZE);
- }
- if (DEBUG) Log.d(TAG, msg);
+ private boolean log(@DozeEvent.EventType int eventType) {
+ return log(eventType, "");
}
- public static void tracePulseDropped(Context context, boolean pulsePending,
- DozeMachine.State state, boolean blocked) {
- if (!ENABLED) return;
- init(context);
- log("pulseDropped pulsePending=" + pulsePending + " state="
- + state + " blocked=" + blocked);
+ private boolean log(@DozeEvent.EventType int eventType, String msg) {
+ return super.log(new DozeEvent.DozeEventBuilder()
+ .setType(eventType)
+ .setReason(msg)
+ .build());
}
- public static void tracePulseDropped(Context context, String why) {
- if (!ENABLED) return;
- init(context);
- log("pulseDropped why=" + why);
+ /**
+ * Appends pulse dropped event to logs
+ */
+ public void tracePulseDropped(boolean pulsePending, DozeMachine.State state, boolean blocked) {
+ log(DozeEvent.PULSE_DROPPED, "pulsePending=" + pulsePending + " state="
+ + state.name() + " blocked=" + blocked);
}
- public static void tracePulseTouchDisabledByProx(Context context, boolean disabled) {
- if (!ENABLED) return;
- init(context);
- log("pulseTouchDisabledByProx " + disabled);
+ /**
+ * Appends pulse dropped event to logs
+ * @param reason why the pulse was dropped
+ */
+ public void tracePulseDropped(String reason) {
+ log(DozeEvent.PULSE_DROPPED, "why=" + reason);
}
- public static void setRegisterKeyguardCallback(boolean registerKeyguardCallback) {
- if (!ENABLED) return;
- synchronized (DozeLog.class) {
- if (sRegisterKeyguardCallback != registerKeyguardCallback && sMessages != null) {
- throw new IllegalStateException("Cannot change setRegisterKeyguardCallback "
- + "after init()");
- }
- sRegisterKeyguardCallback = registerKeyguardCallback;
- }
+ /**
+ * Appends pulse touch displayed by prox sensor event to logs
+ * @param disabled
+ */
+ public void tracePulseTouchDisabledByProx(boolean disabled) {
+ log(DozeEvent.PULSE_DISABLED_BY_PROX, "disabled=" + disabled);
}
- public static void traceSensor(Context context, int reason) {
- if (!ENABLED) return;
- init(context);
- log("sensor type=" + reasonToString(reason));
+ /**
+ * Appends sensor triggered event to logs
+ * @param reason why the sensor was triggered
+ */
+ public void traceSensor(@DozeEvent.Reason int reason) {
+ log(DozeEvent.SENSOR_TRIGGERED, "type=" + DozeEvent.reasonToString(reason));
}
- private static class SummaryStats {
+ private class SummaryStats {
private int mCount;
public void append() {
@@ -305,7 +307,7 @@ public class DozeLog {
pw.print(": n=");
pw.print(mCount);
pw.print(" (");
- final double perHr = (double) mCount / (System.currentTimeMillis() - sSince)
+ final double perHr = (double) mCount / (System.currentTimeMillis() - mSince)
* 1000 * 60 * 60;
pw.print(perHr);
pw.print("/hr)");
@@ -313,7 +315,7 @@ public class DozeLog {
}
}
- private static final KeyguardUpdateMonitorCallback sKeyguardCallback =
+ private final KeyguardUpdateMonitorCallback mKeyguardCallback =
new KeyguardUpdateMonitorCallback() {
@Override
public void onEmergencyCallAction() {
@@ -340,4 +342,7 @@ public class DozeLog {
traceKeyguard(showing);
}
};
+
+ private static final int MAX_DOZE_DEBUG_LOGS = 400;
+ private static final int MAX_DOZE_LOGS = 50;
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 93a51cc20db2..75b1d6c87800 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -27,6 +27,7 @@ import com.android.internal.util.Preconditions;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.Assert;
import com.android.systemui.util.wakelock.WakeLock;
@@ -45,6 +46,7 @@ public class DozeMachine {
static final String TAG = "DozeMachine";
static final boolean DEBUG = DozeService.DEBUG;
+ private final DozeLog mDozeLog;
private static final String REASON_CHANGE_STATE = "DozeMachine#requestState";
private static final String REASON_HELD_FOR_STATE = "DozeMachine#heldForState";
@@ -121,6 +123,7 @@ public class DozeMachine {
private final WakeLock mWakeLock;
private final AmbientDisplayConfiguration mConfig;
private final WakefulnessLifecycle mWakefulnessLifecycle;
+ private final BatteryController mBatteryController;
private Part[] mParts;
private final ArrayList<State> mQueuedRequests = new ArrayList<>();
@@ -129,11 +132,14 @@ public class DozeMachine {
private boolean mWakeLockHeldForCurrentState = false;
public DozeMachine(Service service, AmbientDisplayConfiguration config,
- WakeLock wakeLock, WakefulnessLifecycle wakefulnessLifecycle) {
+ WakeLock wakeLock, WakefulnessLifecycle wakefulnessLifecycle,
+ BatteryController batteryController, DozeLog dozeLog) {
mDozeService = service;
mConfig = config;
mWakefulnessLifecycle = wakefulnessLifecycle;
mWakeLock = wakeLock;
+ mBatteryController = batteryController;
+ mDozeLog = dozeLog;
}
/** Initializes the set of {@link Part}s. Must be called exactly once after construction. */
@@ -155,7 +161,7 @@ public class DozeMachine {
@MainThread
public void requestState(State requestedState) {
Preconditions.checkArgument(requestedState != State.DOZE_REQUEST_PULSE);
- requestState(requestedState, DozeLog.PULSE_REASON_NONE);
+ requestState(requestedState, DozeEvent.PULSE_REASON_NONE);
}
@MainThread
@@ -243,7 +249,7 @@ public class DozeMachine {
State oldState = mState;
mState = newState;
- DozeLog.traceState(newState);
+ mDozeLog.traceState(newState);
Trace.traceCounter(Trace.TRACE_TAG_APP, "doze_machine_state", newState.ordinal());
updatePulseReason(newState, oldState, pulseReason);
@@ -257,7 +263,7 @@ public class DozeMachine {
if (newState == State.DOZE_REQUEST_PULSE) {
mPulseReason = pulseReason;
} else if (oldState == State.DOZE_PULSE_DONE) {
- mPulseReason = DozeLog.PULSE_REASON_NONE;
+ mPulseReason = DozeEvent.PULSE_REASON_NONE;
}
}
@@ -316,6 +322,9 @@ 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;
@@ -349,7 +358,7 @@ public class DozeMachine {
nextState = State.DOZE;
}
- transitionTo(nextState, DozeLog.PULSE_REASON_NONE);
+ transitionTo(nextState, DozeEvent.PULSE_REASON_NONE);
break;
default:
break;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index 38ee2fed136d..e1b4f3122861 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -16,6 +16,12 @@
package com.android.systemui.doze;
+import static com.android.systemui.doze.DozeMachine.State.DOZE;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE;
+
import android.os.Handler;
import android.util.Log;
import android.view.Display;
@@ -48,21 +54,24 @@ public class DozeScreenState implements DozeMachine.Part {
private final Handler mHandler;
private final Runnable mApplyPendingScreenState = this::applyPendingScreenState;
private final DozeParameters mParameters;
+ private final DozeHost mDozeHost;
private int mPendingScreenState = Display.STATE_UNKNOWN;
private SettableWakeLock mWakeLock;
- public DozeScreenState(DozeMachine.Service service, Handler handler,
+ public DozeScreenState(DozeMachine.Service service, Handler handler, DozeHost host,
DozeParameters parameters, WakeLock wakeLock) {
mDozeService = service;
mHandler = handler;
mParameters = parameters;
+ mDozeHost = host;
mWakeLock = new SettableWakeLock(wakeLock, TAG);
}
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
int screenState = newState.screenState(mParameters);
+ mDozeHost.cancelGentleSleep();
if (newState == DozeMachine.State.FINISH) {
// Make sure not to apply the screen state after DozeService was destroyed.
@@ -79,12 +88,13 @@ public class DozeScreenState implements DozeMachine.Part {
return;
}
- boolean messagePending = mHandler.hasCallbacks(mApplyPendingScreenState);
- boolean pulseEnding = oldState == DozeMachine.State.DOZE_PULSE_DONE
- && newState == DozeMachine.State.DOZE_AOD;
- boolean turningOn = (oldState == DozeMachine.State.DOZE_AOD_PAUSED
- || oldState == DozeMachine.State.DOZE) && newState == DozeMachine.State.DOZE_AOD;
- boolean justInitialized = oldState == DozeMachine.State.INITIALIZED;
+ final boolean messagePending = mHandler.hasCallbacks(mApplyPendingScreenState);
+ final boolean pulseEnding = oldState == DOZE_PULSE_DONE && newState == DOZE_AOD;
+ final boolean turningOn = (oldState == DOZE_AOD_PAUSED
+ || oldState == DOZE) && newState == DOZE_AOD;
+ final boolean turningOff = (oldState == DOZE_AOD && newState == DOZE)
+ || (oldState == DOZE_AOD_PAUSING && newState == DOZE_AOD_PAUSED);
+ final boolean justInitialized = oldState == DozeMachine.State.INITIALIZED;
if (messagePending || justInitialized || pulseEnding || turningOn) {
// During initialization, we hide the navigation bar. That is however only applied after
// a traversal; setting the screen state here is immediate however, so it can happen
@@ -93,7 +103,7 @@ public class DozeScreenState implements DozeMachine.Part {
mPendingScreenState = screenState;
// Delay screen state transitions even longer while animations are running.
- boolean shouldDelayTransition = newState == DozeMachine.State.DOZE_AOD
+ boolean shouldDelayTransition = newState == DOZE_AOD
&& mParameters.shouldControlScreenOff() && !turningOn;
if (shouldDelayTransition) {
@@ -114,6 +124,8 @@ public class DozeScreenState implements DozeMachine.Part {
} else if (DEBUG) {
Log.d(TAG, "Pending display state change to " + screenState);
}
+ } else if (turningOff) {
+ mDozeHost.prepareForGentleSleep(() -> applyScreenState(screenState));
} else {
applyScreenState(screenState);
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 67eefc5588e3..05a234f1e431 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -42,9 +42,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
-import com.android.systemui.Dependency;
import com.android.systemui.plugins.SensorManagerPlugin;
-import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor;
@@ -82,7 +80,8 @@ public class DozeSensors {
public DozeSensors(Context context, AlarmManager alarmManager, AsyncSensorManager sensorManager,
DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
- Callback callback, Consumer<Boolean> proxCallback, AlwaysOnDisplayPolicy policy) {
+ Callback callback, Consumer<Boolean> proxCallback, AlwaysOnDisplayPolicy policy,
+ DozeLog dozeLog) {
mContext = context;
mAlarmManager = alarmManager;
mSensorManager = sensorManager;
@@ -99,59 +98,69 @@ public class DozeSensors {
mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION),
null /* setting */,
dozeParameters.getPulseOnSigMotion(),
- DozeLog.PULSE_REASON_SENSOR_SIGMOTION, false /* touchCoords */,
- false /* touchscreen */),
+ DozeEvent.PULSE_REASON_SENSOR_SIGMOTION, false /* touchCoords */,
+ false /* touchscreen */, dozeLog),
mPickupSensor = new TriggerSensor(
mSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE),
Settings.Secure.DOZE_PICK_UP_GESTURE,
true /* settingDef */,
config.dozePickupSensorAvailable(),
- DozeLog.REASON_SENSOR_PICKUP, false /* touchCoords */,
+ DozeEvent.REASON_SENSOR_PICKUP, false /* touchCoords */,
false /* touchscreen */,
- false /* ignoresSetting */),
+ false /* ignoresSetting */,
+ dozeLog),
new TriggerSensor(
findSensorWithType(config.doubleTapSensorType()),
Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
true /* configured */,
- DozeLog.REASON_SENSOR_DOUBLE_TAP,
+ DozeEvent.REASON_SENSOR_DOUBLE_TAP,
dozeParameters.doubleTapReportsTouchCoordinates(),
- true /* touchscreen */),
+ true /* touchscreen */,
+ dozeLog),
new TriggerSensor(
findSensorWithType(config.tapSensorType()),
Settings.Secure.DOZE_TAP_SCREEN_GESTURE,
true /* configured */,
- DozeLog.REASON_SENSOR_TAP,
+ DozeEvent.REASON_SENSOR_TAP,
false /* reports touch coordinates */,
- true /* touchscreen */),
+ true /* touchscreen */,
+ dozeLog),
new TriggerSensor(
findSensorWithType(config.longPressSensorType()),
Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
false /* settingDef */,
true /* configured */,
- DozeLog.PULSE_REASON_SENSOR_LONG_PRESS,
+ DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS,
true /* reports touch coordinates */,
- true /* touchscreen */),
+ true /* touchscreen */,
+ dozeLog),
new PluginSensor(
new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY),
Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
mConfig.wakeScreenGestureAvailable() && alwaysOn,
- DozeLog.REASON_SENSOR_WAKE_UP,
+ DozeEvent.REASON_SENSOR_WAKE_UP,
false /* reports touch coordinates */,
- false /* touchscreen */),
+ false /* touchscreen */,
+ dozeLog),
new PluginSensor(
new SensorManagerPlugin.Sensor(TYPE_WAKE_LOCK_SCREEN),
Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
mConfig.wakeScreenGestureAvailable(),
- DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
+ DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
false /* reports touch coordinates */,
- false /* touchscreen */, mConfig.getWakeLockScreenDebounce()),
+ false /* touchscreen */,
+ mConfig.getWakeLockScreenDebounce(),
+ dozeLog),
};
- mProximitySensor = new ProximitySensor(
- context, sensorManager, Dependency.get(PluginManager.class));
+ mProximitySensor = new ProximitySensor(context.getResources(), sensorManager);
mProximitySensor.register(
- proximityEvent -> mProxCallback.accept(!proximityEvent.getNear()));
+ proximityEvent -> {
+ if (proximityEvent != null) {
+ mProxCallback.accept(!proximityEvent.getNear());
+ }
+ });
}
/**
@@ -305,23 +314,24 @@ public class DozeSensors {
protected boolean mRegistered;
protected boolean mDisabled;
protected boolean mIgnoresSetting;
+ protected final DozeLog mDozeLog;
public TriggerSensor(Sensor sensor, String setting, boolean configured, int pulseReason,
- boolean reportsTouchCoordinates, boolean requiresTouchscreen) {
+ boolean reportsTouchCoordinates, boolean requiresTouchscreen, DozeLog dozeLog) {
this(sensor, setting, true /* settingDef */, configured, pulseReason,
- reportsTouchCoordinates, requiresTouchscreen);
+ reportsTouchCoordinates, requiresTouchscreen, dozeLog);
}
public TriggerSensor(Sensor sensor, String setting, boolean settingDef,
boolean configured, int pulseReason, boolean reportsTouchCoordinates,
- boolean requiresTouchscreen) {
+ boolean requiresTouchscreen, DozeLog dozeLog) {
this(sensor, setting, settingDef, configured, pulseReason, reportsTouchCoordinates,
- requiresTouchscreen, false /* ignoresSetting */);
+ requiresTouchscreen, false /* ignoresSetting */, dozeLog);
}
private TriggerSensor(Sensor sensor, String setting, boolean settingDef,
boolean configured, int pulseReason, boolean reportsTouchCoordinates,
- boolean requiresTouchscreen, boolean ignoresSetting) {
+ boolean requiresTouchscreen, boolean ignoresSetting, DozeLog dozeLog) {
mSensor = sensor;
mSetting = setting;
mSettingDefault = settingDef;
@@ -330,6 +340,7 @@ public class DozeSensors {
mReportsTouchCoordinates = reportsTouchCoordinates;
mRequiresTouchscreen = requiresTouchscreen;
mIgnoresSetting = ignoresSetting;
+ mDozeLog = dozeLog;
}
public void setListening(boolean listen) {
@@ -386,7 +397,7 @@ public class DozeSensors {
@Override
@AnyThread
public void onTrigger(TriggerEvent event) {
- DozeLog.traceSensor(mContext, mPulseReason);
+ mDozeLog.traceSensor(mPulseReason);
mHandler.post(mWakeLock.wrap(() -> {
if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event));
if (mSensor != null && mSensor.getType() == Sensor.TYPE_PICK_UP_GESTURE) {
@@ -442,16 +453,17 @@ public class DozeSensors {
private long mDebounce;
PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured,
- int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen) {
+ int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen,
+ DozeLog dozeLog) {
this(sensor, setting, configured, pulseReason, reportsTouchCoordinates,
- requiresTouchscreen, 0L /* debounce */);
+ requiresTouchscreen, 0L /* debounce */, dozeLog);
}
PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured,
int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen,
- long debounce) {
+ long debounce, DozeLog dozeLog) {
super(null, setting, configured, pulseReason, reportsTouchCoordinates,
- requiresTouchscreen);
+ requiresTouchscreen, dozeLog);
mPluginSensor = sensor;
mDebounce = debounce;
}
@@ -497,7 +509,7 @@ public class DozeSensors {
@Override
public void onSensorChanged(SensorManagerPlugin.SensorEvent event) {
- DozeLog.traceSensor(mContext, mPulseReason);
+ mDozeLog.traceSensor(mPulseReason);
mHandler.post(mWakeLock.wrap(() -> {
final long now = SystemClock.uptimeMillis();
if (now < mDebounceFrom + mDebounce) {
@@ -514,7 +526,7 @@ public class DozeSensors {
/**
* Called when a sensor requests a pulse
- * @param pulseReason Requesting sensor, e.g. {@link DozeLog#REASON_SENSOR_PICKUP}
+ * @param pulseReason Requesting sensor, e.g. {@link DozeEvent#REASON_SENSOR_PICKUP}
* @param screenX the location on the screen where the sensor fired or -1
* if the sensor doesn't support reporting screen locations.
* @param screenY the location on the screen where the sensor fired or -1
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index e92acfc7f219..08734d27ba55 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -22,10 +22,8 @@ import android.os.SystemClock;
import android.service.dreams.DreamService;
import android.util.Log;
-import com.android.systemui.Dependency;
import com.android.systemui.plugins.DozeServicePlugin;
import com.android.systemui.plugins.DozeServicePlugin.RequestDoze;
-import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.plugins.PluginManager;
@@ -38,16 +36,17 @@ 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 FalsingManager mFalsingManager;
+ private final DozeFactory mDozeFactory;
private DozeMachine mDozeMachine;
private DozeServicePlugin mDozePlugin;
private PluginManager mPluginManager;
@Inject
- public DozeService(FalsingManager falsingManager) {
+ public DozeService(DozeFactory dozeFactory, PluginManager pluginManager) {
setDebug(DEBUG);
- mFalsingManager = falsingManager;
+ mDozeFactory = dozeFactory;
+ mPluginManager = pluginManager;
}
@Override
@@ -60,9 +59,8 @@ public class DozeService extends DreamService
finish();
return;
}
- mPluginManager = Dependency.get(PluginManager.class);
mPluginManager.addPluginListener(this, DozeServicePlugin.class, false /* allowMultiple */);
- mDozeMachine = new DozeFactory().assembleMachine(this, mFalsingManager);
+ mDozeMachine = mDozeFactory.assembleMachine(this);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 80d4b631314a..b212884ebb98 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -67,12 +67,12 @@ public class DozeTriggers implements DozeMachine.Part {
private final Context mContext;
private final DozeMachine mMachine;
+ private final DozeLog mDozeLog;
private final DozeSensors mDozeSensors;
private final DozeHost mDozeHost;
private final AmbientDisplayConfiguration mConfig;
private final DozeParameters mDozeParameters;
private final AsyncSensorManager mSensorManager;
- private final Handler mHandler;
private final WakeLock mWakeLock;
private final boolean mAllowPulseTriggers;
private final UiModeManager mUiModeManager;
@@ -89,24 +89,24 @@ public class DozeTriggers implements DozeMachine.Part {
public DozeTriggers(Context context, DozeMachine machine, DozeHost dozeHost,
AlarmManager alarmManager, AmbientDisplayConfiguration config,
DozeParameters dozeParameters, AsyncSensorManager sensorManager, Handler handler,
- WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager) {
+ WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager,
+ ProximitySensor proximitySensor,
+ DozeLog dozeLog) {
mContext = context;
mMachine = machine;
mDozeHost = dozeHost;
mConfig = config;
mDozeParameters = dozeParameters;
mSensorManager = sensorManager;
- mHandler = handler;
mWakeLock = wakeLock;
mAllowPulseTriggers = allowPulseTriggers;
mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
config, wakeLock, this::onSensor, this::onProximityFar,
- dozeParameters.getPolicy());
+ dozeParameters.getPolicy(), dozeLog);
mUiModeManager = mContext.getSystemService(UiModeManager.class);
mDockManager = dockManager;
- mProxCheck = new ProximitySensor.ProximityCheck(
- new ProximitySensor(mContext, mSensorManager, null),
- mHandler);
+ mProxCheck = new ProximitySensor.ProximityCheck(proximitySensor, handler);
+ mDozeLog = dozeLog;
}
private void onNotification(Runnable onPulseSuppressedListener) {
@@ -116,18 +116,18 @@ public class DozeTriggers implements DozeMachine.Part {
if (!sWakeDisplaySensorState) {
Log.d(TAG, "Wake display false. Pulse denied.");
runIfNotNull(onPulseSuppressedListener);
- DozeLog.tracePulseDropped(mContext, "wakeDisplaySensor");
+ mDozeLog.tracePulseDropped("wakeDisplaySensor");
return;
}
mNotificationPulseTime = SystemClock.elapsedRealtime();
if (!mConfig.pulseOnNotificationEnabled(UserHandle.USER_CURRENT)) {
runIfNotNull(onPulseSuppressedListener);
- DozeLog.tracePulseDropped(mContext, "pulseOnNotificationsDisabled");
+ mDozeLog.tracePulseDropped("pulseOnNotificationsDisabled");
return;
}
- requestPulse(DozeLog.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */,
+ requestPulse(DozeEvent.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */,
onPulseSuppressedListener);
- DozeLog.traceNotificationPulse(mContext);
+ mDozeLog.traceNotificationPulse();
}
private static void runIfNotNull(Runnable runnable) {
@@ -148,8 +148,7 @@ public class DozeTriggers implements DozeMachine.Part {
final long start = SystemClock.uptimeMillis();
mProxCheck.check(PROXIMITY_TIMEOUT_DELAY_MS, near -> {
final long end = SystemClock.uptimeMillis();
- DozeLog.traceProximityResult(
- mContext,
+ mDozeLog.traceProximityResult(
near == null ? false : near,
end - start,
reason);
@@ -162,12 +161,12 @@ public class DozeTriggers implements DozeMachine.Part {
@VisibleForTesting
void onSensor(int pulseReason, float screenX, float screenY, float[] rawValues) {
- boolean isDoubleTap = pulseReason == DozeLog.REASON_SENSOR_DOUBLE_TAP;
- boolean isTap = pulseReason == DozeLog.REASON_SENSOR_TAP;
- boolean isPickup = pulseReason == DozeLog.REASON_SENSOR_PICKUP;
- boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
- boolean isWakeDisplay = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP;
- boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
+ boolean isDoubleTap = pulseReason == DozeEvent.REASON_SENSOR_DOUBLE_TAP;
+ boolean isTap = pulseReason == DozeEvent.REASON_SENSOR_TAP;
+ boolean isPickup = pulseReason == DozeEvent.REASON_SENSOR_PICKUP;
+ boolean isLongPress = pulseReason == DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS;
+ boolean isWakeDisplay = pulseReason == DozeEvent.REASON_SENSOR_WAKE_UP;
+ boolean isWakeLockScreen = pulseReason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
boolean wakeEvent = rawValues != null && rawValues.length > 0 && rawValues[0] != 0;
if (isWakeDisplay) {
@@ -182,7 +181,7 @@ public class DozeTriggers implements DozeMachine.Part {
}
} else {
proximityCheckThenCall((result) -> {
- if (result) {
+ if (result != null && result) {
// In pocket, drop event.
return;
}
@@ -204,7 +203,7 @@ public class DozeTriggers implements DozeMachine.Part {
SystemClock.elapsedRealtime() - mNotificationPulseTime;
final boolean withinVibrationThreshold =
timeSinceNotification < mDozeParameters.getPickupVibrationThreshold();
- DozeLog.tracePickupWakeUp(mContext, withinVibrationThreshold);
+ mDozeLog.tracePickupWakeUp(withinVibrationThreshold);
}
}
@@ -266,12 +265,12 @@ public class DozeTriggers implements DozeMachine.Part {
* transitions.
*/
private void onWakeScreen(boolean wake, @Nullable DozeMachine.State state) {
- DozeLog.traceWakeDisplay(wake);
+ mDozeLog.traceWakeDisplay(wake);
sWakeDisplaySensorState = wake;
if (wake) {
proximityCheckThenCall((result) -> {
- if (result) {
+ if (result != null && result) {
// In pocket, drop event.
return;
}
@@ -280,9 +279,9 @@ public class DozeTriggers implements DozeMachine.Part {
// Logs AOD open due to sensor wake up.
mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING)
.setType(MetricsEvent.TYPE_OPEN)
- .setSubtype(DozeLog.REASON_SENSOR_WAKE_UP));
+ .setSubtype(DozeEvent.REASON_SENSOR_WAKE_UP));
}
- }, true /* alreadyPerformedProxCheck */, DozeLog.REASON_SENSOR_WAKE_UP);
+ }, true /* alreadyPerformedProxCheck */, DozeEvent.REASON_SENSOR_WAKE_UP);
} else {
boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
@@ -291,7 +290,7 @@ public class DozeTriggers implements DozeMachine.Part {
// Logs AOD close due to sensor wake up.
mMetricsLogger.write(new LogMaker(MetricsEvent.DOZING)
.setType(MetricsEvent.TYPE_CLOSE)
- .setSubtype(DozeLog.REASON_SENSOR_WAKE_UP));
+ .setSubtype(DozeEvent.REASON_SENSOR_WAKE_UP));
}
}
}
@@ -349,7 +348,6 @@ public class DozeTriggers implements DozeMachine.Part {
private void checkTriggersAtInit() {
if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR
- || mDozeHost.isPowerSaveActive()
|| mDozeHost.isBlockingDoze()
|| !mDozeHost.isProvisioned()) {
mMachine.requestState(DozeMachine.State.FINISH);
@@ -364,14 +362,14 @@ public class DozeTriggers implements DozeMachine.Part {
// When already pulsing we're allowed to show the wallpaper directly without
// requesting a new pulse.
if (mMachine.getState() == DozeMachine.State.DOZE_PULSING
- && reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
+ && reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
mMachine.requestState(DozeMachine.State.DOZE_PULSING_BRIGHT);
return;
}
if (mPulsePending || !mAllowPulseTriggers || !canPulse()) {
if (mAllowPulseTriggers) {
- DozeLog.tracePulseDropped(mContext, mPulsePending, mMachine.getState(),
+ mDozeLog.tracePulseDropped(mPulsePending, mMachine.getState(),
mDozeHost.isPulsingBlocked());
}
runIfNotNull(onPulseSuppressedListener);
@@ -380,9 +378,9 @@ public class DozeTriggers implements DozeMachine.Part {
mPulsePending = true;
proximityCheckThenCall((result) -> {
- if (result) {
+ if (result != null && result) {
// in pocket, abort pulse
- DozeLog.tracePulseDropped(mContext, "inPocket");
+ mDozeLog.tracePulseDropped("inPocket");
mPulsePending = false;
runIfNotNull(onPulseSuppressedListener);
} else {
@@ -404,7 +402,7 @@ public class DozeTriggers implements DozeMachine.Part {
private void continuePulseRequest(int reason) {
mPulsePending = false;
if (mDozeHost.isPulsingBlocked() || !canPulse()) {
- DozeLog.tracePulseDropped(mContext, mPulsePending, mMachine.getState(),
+ mDozeLog.tracePulseDropped(mPulsePending, mMachine.getState(),
mDozeHost.isPulsingBlocked());
return;
}
@@ -428,7 +426,7 @@ public class DozeTriggers implements DozeMachine.Part {
public void onReceive(Context context, Intent intent) {
if (PULSE_ACTION.equals(intent.getAction())) {
if (DozeMachine.DEBUG) Log.d(TAG, "Received pulse intent");
- requestPulse(DozeLog.PULSE_REASON_INTENT, false, /* performedProxCheck */
+ requestPulse(DozeEvent.PULSE_REASON_INTENT, false, /* performedProxCheck */
null /* onPulseSupressedListener */);
}
if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
@@ -485,8 +483,8 @@ public class DozeTriggers implements DozeMachine.Part {
@Override
public void onPowerSaveChanged(boolean active) {
- if (active) {
- mMachine.requestState(DozeMachine.State.FINISH);
+ if (mDozeHost.isPowerSaveActive()) {
+ mMachine.requestState(DozeMachine.State.DOZE);
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 1f33af8c3f55..f1557838fd73 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -49,6 +49,7 @@ public class DozeUi implements DozeMachine.Part {
private final AlarmTimeout mTimeTicker;
private final boolean mCanAnimateTransition;
private final DozeParameters mDozeParameters;
+ private final DozeLog mDozeLog;
private boolean mKeyguardShowing;
private final KeyguardUpdateMonitorCallback mKeyguardVisibilityCallback =
@@ -65,7 +66,8 @@ public class DozeUi implements DozeMachine.Part {
public DozeUi(Context context, AlarmManager alarmManager, DozeMachine machine,
WakeLock wakeLock, DozeHost host, Handler handler,
- DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor,
+ DozeLog dozeLog) {
mContext = context;
mMachine = machine;
mWakeLock = wakeLock;
@@ -75,6 +77,7 @@ public class DozeUi implements DozeMachine.Part {
mDozeParameters = params;
mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", handler);
keyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
+ mDozeLog = dozeLog;
}
/**
@@ -83,7 +86,8 @@ public class DozeUi implements DozeMachine.Part {
*/
private void updateAnimateScreenOff() {
if (mCanAnimateTransition) {
- final boolean controlScreenOff = mDozeParameters.getAlwaysOn() && mKeyguardShowing;
+ final boolean controlScreenOff = mDozeParameters.getAlwaysOn() && mKeyguardShowing
+ && !mHost.isPowerSaveActive();
mDozeParameters.setControlScreenOffAnimation(controlScreenOff);
mHost.setAnimateScreenOff(controlScreenOff);
}
@@ -96,7 +100,7 @@ public class DozeUi implements DozeMachine.Part {
public void onPulseStarted() {
try {
mMachine.requestState(
- reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN
+ reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN
? DozeMachine.State.DOZE_PULSING_BRIGHT
: DozeMachine.State.DOZE_PULSING);
} catch (IllegalStateException e) {
@@ -131,7 +135,6 @@ public class DozeUi implements DozeMachine.Part {
break;
case DOZE:
case DOZE_AOD_PAUSED:
- mHost.prepareForGentleWakeUp();
unscheduleTimeTick();
break;
case DOZE_REQUEST_PULSE:
@@ -175,7 +178,7 @@ public class DozeUi implements DozeMachine.Part {
long delta = roundToNextMinute(time) - System.currentTimeMillis();
boolean scheduled = mTimeTicker.schedule(delta, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
if (scheduled) {
- DozeLog.traceTimeTickScheduled(time, time + delta);
+ mDozeLog.traceTimeTickScheduled(time, time + delta);
}
mLastTimeTickElapsed = SystemClock.elapsedRealtime();
}
@@ -192,7 +195,7 @@ public class DozeUi implements DozeMachine.Part {
long millisSinceLastTick = SystemClock.elapsedRealtime() - mLastTimeTickElapsed;
if (millisSinceLastTick > TIME_TICK_DEADLINE_MILLIS) {
String delay = Formatter.formatShortElapsedTime(mContext, millisSinceLastTick);
- DozeLog.traceMissedTick(delay);
+ mDozeLog.traceMissedTick(delay);
Log.e(DozeMachine.TAG, "Missed AOD time tick by " + delay);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
index 35c8b741381c..9457dc9e580b 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
@@ -17,12 +17,9 @@
package com.android.systemui.doze;
import android.app.IWallpaperManager;
-import android.content.Context;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.util.Log;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -42,17 +39,10 @@ public class DozeWallpaperState implements DozeMachine.Part {
private final BiometricUnlockController mBiometricUnlockController;
private boolean mIsAmbientMode;
- public DozeWallpaperState(Context context,
- BiometricUnlockController biometricUnlockController) {
- this(IWallpaperManager.Stub.asInterface(
- ServiceManager.getService(Context.WALLPAPER_SERVICE)),
- biometricUnlockController,
- DozeParameters.getInstance(context));
- }
-
- @VisibleForTesting
- DozeWallpaperState(IWallpaperManager wallpaperManagerService,
- BiometricUnlockController biometricUnlockController, DozeParameters parameters) {
+ public DozeWallpaperState(
+ IWallpaperManager wallpaperManagerService,
+ BiometricUnlockController biometricUnlockController,
+ DozeParameters parameters) {
mWallpaperManagerService = wallpaperManagerService;
mBiometricUnlockController = biometricUnlockController;
mDozeParameters = parameters;
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
index b4cc571be061..1b4857ebb209 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
@@ -22,7 +22,7 @@ import android.view.View;
import com.android.systemui.ConfigurationChangedReceiver;
import com.android.systemui.Dumpable;
-import com.android.systemui.SystemUIRootComponent;
+import com.android.systemui.dagger.SystemUIRootComponent;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.statusbar.phone.NavigationBarFragment;
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
index e8ef454bd466..c11127d1dc0b 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
@@ -35,6 +35,10 @@ public class GlobalActionsComponent extends SystemUI implements Callbacks, Globa
private Extension<GlobalActions> mExtension;
private IStatusBarService mBarService;
+ public GlobalActionsComponent(Context context) {
+ super(context);
+ }
+
@Override
public void start() {
mBarService = IStatusBarService.Stub.asInterface(
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index c9c6a0c6868b..22846bc02a38 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -90,14 +90,15 @@ import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.MultiListLayout;
import com.android.systemui.MultiListLayout.MultiListAdapter;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
-import com.android.systemui.statusbar.phone.UnlockMethodCache;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.EmergencyDialerConstants;
import com.android.systemui.util.leak.RotationUtils;
import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator;
@@ -187,7 +188,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
- context.registerReceiver(mBroadcastReceiver, filter);
+ Dependency.get(BroadcastDispatcher.class).registerReceiver(mBroadcastReceiver, filter);
ConnectivityManager cm = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
@@ -213,14 +214,17 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
Dependency.get(ConfigurationController.class).addCallback(this);
mActivityStarter = Dependency.get(ActivityStarter.class);
- UnlockMethodCache unlockMethodCache = UnlockMethodCache.getInstance(context);
- unlockMethodCache.addListener(
- () -> {
- if (mDialog != null && mDialog.mPanelController != null) {
- boolean locked = !unlockMethodCache.canSkipBouncer();
- mDialog.mPanelController.onDeviceLockStateChanged(locked);
- }
- });
+ KeyguardStateController keyguardStateController =
+ Dependency.get(KeyguardStateController.class);
+ keyguardStateController.addCallback(new KeyguardStateController.Callback() {
+ @Override
+ public void onUnlockedChanged() {
+ if (mDialog != null && mDialog.mPanelController != null) {
+ boolean locked = !keyguardStateController.canDismissLockScreen();
+ mDialog.mPanelController.onDeviceLockStateChanged(locked);
+ }
+ }
+ });
}
/**
@@ -665,8 +669,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
// Take an "interactive" bugreport.
MetricsLogger.action(mContext,
MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_INTERACTIVE);
- ActivityManager.getService().requestBugReport(
- ActivityManager.BUGREPORT_OPTION_INTERACTIVE);
+ ActivityManager.getService().requestInteractiveBugReport();
} catch (RemoteException e) {
}
}
@@ -683,8 +686,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
try {
// Take a "full" bugreport.
MetricsLogger.action(mContext, MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_FULL);
- ActivityManager.getService().requestBugReport(
- ActivityManager.BUGREPORT_OPTION_FULL);
+ ActivityManager.getService().requestFullBugReport();
} catch (RemoteException e) {
}
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index 6f8665a87dee..b9fe827bb1c9 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -40,14 +40,14 @@ import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks {
private static final float SHUTDOWN_SCRIM_ALPHA = 0.95f;
private final Context mContext;
- private final KeyguardMonitor mKeyguardMonitor;
+ private final KeyguardStateController mKeyguardStateController;
private final DeviceProvisionedController mDeviceProvisionedController;
private final ExtensionController.Extension<GlobalActionsPanelPlugin> mPanelExtension;
private GlobalActionsDialog mGlobalActions;
@@ -55,7 +55,7 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks
public GlobalActionsImpl(Context context) {
mContext = context;
- mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+ mKeyguardStateController = Dependency.get(KeyguardStateController.class);
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
SysUiServiceProvider.getComponent(context, CommandQueue.class).addCallback(this);
mPanelExtension = Dependency.get(ExtensionController.class)
@@ -79,7 +79,7 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks
if (mGlobalActions == null) {
mGlobalActions = new GlobalActionsDialog(mContext, manager);
}
- mGlobalActions.showDialog(mKeyguardMonitor.isShowing(),
+ mGlobalActions.showDialog(mKeyguardStateController.isShowing(),
mDeviceProvisionedController.isDeviceProvisioned(),
mPanelExtension.get());
Dependency.get(KeyguardUpdateMonitor.class).requestFaceAuth();
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
index aac721e3cb56..c6812a75b3bf 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
@@ -22,6 +22,7 @@ import static android.opengl.EGL14.EGL_CONFIG_CAVEAT;
import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION;
import static android.opengl.EGL14.EGL_DEFAULT_DISPLAY;
import static android.opengl.EGL14.EGL_DEPTH_SIZE;
+import static android.opengl.EGL14.EGL_EXTENSIONS;
import static android.opengl.EGL14.EGL_GREEN_SIZE;
import static android.opengl.EGL14.EGL_NONE;
import static android.opengl.EGL14.EGL_NO_CONTEXT;
@@ -41,6 +42,7 @@ import static android.opengl.EGL14.eglGetDisplay;
import static android.opengl.EGL14.eglGetError;
import static android.opengl.EGL14.eglInitialize;
import static android.opengl.EGL14.eglMakeCurrent;
+import static android.opengl.EGL14.eglQueryString;
import static android.opengl.EGL14.eglSwapBuffers;
import static android.opengl.EGL14.eglTerminate;
@@ -63,6 +65,8 @@ public class EglHelper {
// Below two constants make drawing at low priority, so other things can preempt our drawing.
private static final int EGL_CONTEXT_PRIORITY_LEVEL_IMG = 0x3100;
private static final int EGL_CONTEXT_PRIORITY_LOW_IMG = 0x3103;
+ private static final boolean DEBUG = true;
+ private static final String EGL_IMG_CONTEXT_PRIORITY = "EGL_IMG_context_priority";
private EGLDisplay mEglDisplay;
private EGLConfig mEglConfig;
@@ -70,6 +74,7 @@ public class EglHelper {
private EGLSurface mEglSurface;
private final int[] mEglVersion = new int[2];
private boolean mEglReady;
+ private boolean mContextPrioritySupported;
/**
* Initialize EGL and prepare EglSurface.
@@ -105,10 +110,22 @@ public class EglHelper {
return false;
}
+ mContextPrioritySupported = isContextPrioritySuppported();
+
mEglReady = true;
return true;
}
+ private boolean isContextPrioritySuppported() {
+ String[] extensions = eglQueryString(mEglDisplay, EGL_EXTENSIONS).split(" ");
+ for (String extension : extensions) {
+ if (extension.equals(EGL_IMG_CONTEXT_PRIORITY)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private EGLConfig chooseEglConfig() {
int[] configsCount = new int[1];
EGLConfig[] configs = new EGLConfig[1];
@@ -146,6 +163,10 @@ public class EglHelper {
* @return true if EglSurface is ready.
*/
public boolean createEglSurface(SurfaceHolder surfaceHolder) {
+ if (DEBUG) {
+ Log.d(TAG, "createEglSurface start");
+ }
+
if (hasEglDisplay()) {
mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null, 0);
} else {
@@ -163,6 +184,9 @@ public class EglHelper {
return false;
}
+ if (DEBUG) {
+ Log.d(TAG, "createEglSurface done");
+ }
return true;
}
@@ -190,8 +214,19 @@ public class EglHelper {
* @return true if EglContext is ready.
*/
public boolean createEglContext() {
- int[] attrib_list = new int[] {EGL_CONTEXT_CLIENT_VERSION, 2,
- EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_LOW_IMG, EGL_NONE};
+ if (DEBUG) {
+ Log.d(TAG, "createEglContext start");
+ }
+
+ int[] attrib_list = new int[5];
+ int idx = 0;
+ attrib_list[idx++] = EGL_CONTEXT_CLIENT_VERSION;
+ attrib_list[idx++] = 2;
+ if (mContextPrioritySupported) {
+ attrib_list[idx++] = EGL_CONTEXT_PRIORITY_LEVEL_IMG;
+ attrib_list[idx++] = EGL_CONTEXT_PRIORITY_LOW_IMG;
+ }
+ attrib_list[idx++] = EGL_NONE;
if (hasEglDisplay()) {
mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attrib_list, 0);
} else {
@@ -203,6 +238,10 @@ public class EglHelper {
Log.w(TAG, "eglCreateContext failed: " + GLUtils.getEGLErrorString(eglGetError()));
return false;
}
+
+ if (DEBUG) {
+ Log.d(TAG, "createEglContext done");
+ }
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
index b154e66a846e..99c55f13a8a3 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
@@ -19,6 +19,7 @@ package com.android.systemui.glwallpaper;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.util.Log;
import com.android.systemui.Interpolators;
@@ -30,6 +31,7 @@ class ImageRevealHelper {
private static final String TAG = ImageRevealHelper.class.getSimpleName();
private static final float MAX_REVEAL = 0f;
private static final float MIN_REVEAL = 1f;
+ private static final boolean DEBUG = true;
private final ValueAnimator mAnimator;
private final RevealStateListener mRevealListener;
@@ -57,6 +59,9 @@ class ImageRevealHelper {
@Override
public void onAnimationEnd(Animator animation) {
if (!mIsCanceled && mRevealListener != null) {
+ if (DEBUG) {
+ Log.d(TAG, "transition end");
+ }
mRevealListener.onRevealEnd();
}
mIsCanceled = false;
@@ -65,6 +70,9 @@ class ImageRevealHelper {
@Override
public void onAnimationStart(Animator animation) {
if (mRevealListener != null) {
+ if (DEBUG) {
+ Log.d(TAG, "transition start");
+ }
mRevealListener.onRevealStart(true /* animate */);
}
}
@@ -82,6 +90,9 @@ class ImageRevealHelper {
}
void updateAwake(boolean awake, long duration) {
+ if (DEBUG) {
+ Log.d(TAG, "updateAwake: awake=" + awake + ", duration=" + duration);
+ }
mAwake = awake;
mAnimator.setDuration(duration);
if (duration == 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
index 29606347f009..a8371e31d71c 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -46,6 +46,7 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer,
private static final String TAG = ImageWallpaperRenderer.class.getSimpleName();
private static final float SCALE_VIEWPORT_MIN = 1f;
private static final float SCALE_VIEWPORT_MAX = 1.1f;
+ private static final boolean DEBUG = true;
private final WallpaperManager mWallpaperManager;
private final ImageGLProgram mProgram;
@@ -107,6 +108,9 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer,
}
private boolean loadBitmap() {
+ if (DEBUG) {
+ Log.d(TAG, "loadBitmap: mBitmap=" + mBitmap);
+ }
if (mWallpaperManager != null && mBitmap == null) {
mBitmap = mWallpaperManager.getBitmap();
mWallpaperManager.forgetLoadedWallpaper();
@@ -119,6 +123,9 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer,
mSurfaceSize.set(0, 0, surfaceWidth, surfaceHeight);
}
}
+ if (DEBUG) {
+ Log.d(TAG, "loadBitmap done");
+ }
return mBitmap != null;
}
@@ -223,6 +230,7 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer,
out.print(prefix); out.print("mXOffset="); out.print(mXOffset);
out.print(prefix); out.print("mYOffset="); out.print(mYOffset);
out.print(prefix); out.print("threshold="); out.print(mImageProcessHelper.getThreshold());
+ out.print(prefix); out.print("mReveal="); out.print(mImageRevealHelper.getReveal());
mWallpaper.dump(prefix, fd, out, args);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index 7d52a9a08c2a..42f455af7df4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -117,6 +117,10 @@ public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeCha
private int mState;
+ public KeyboardUI(Context context) {
+ super(context);
+ }
+
@Override
public void start() {
mContext = super.mContext;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java
index b3481c52d7f2..4a33590f64d2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java
@@ -19,9 +19,13 @@ package com.android.systemui.keyguard;
import android.os.Handler;
import android.os.Message;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Dispatches the lifecycles keyguard gets from WindowManager on the main thread.
*/
+@Singleton
public class KeyguardLifecyclesDispatcher {
static final int SCREEN_TURNING_ON = 0;
@@ -37,6 +41,7 @@ public class KeyguardLifecyclesDispatcher {
private final ScreenLifecycle mScreenLifecycle;
private final WakefulnessLifecycle mWakefulnessLifecycle;
+ @Inject
public KeyguardLifecyclesDispatcher(ScreenLifecycle screenLifecycle,
WakefulnessLifecycle wakefulnessLifecycle) {
mScreenLifecycle = screenLifecycle;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 81247cd2f727..9f4056fdf65b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -33,25 +33,30 @@ import com.android.internal.policy.IKeyguardDrawnCallback;
import com.android.internal.policy.IKeyguardExitCallback;
import com.android.internal.policy.IKeyguardService;
import com.android.internal.policy.IKeyguardStateCallback;
-import com.android.systemui.Dependency;
import com.android.systemui.SystemUIApplication;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
public class KeyguardService extends Service {
static final String TAG = "KeyguardService";
static final String PERMISSION = android.Manifest.permission.CONTROL_KEYGUARD;
- private KeyguardViewMediator mKeyguardViewMediator;
- private KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher;
+ private final KeyguardViewMediator mKeyguardViewMediator;
+ private final KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher;
+
+ @Inject
+ public KeyguardService(KeyguardViewMediator keyguardViewMediator,
+ KeyguardLifecyclesDispatcher keyguardLifecyclesDispatcher) {
+ super();
+ mKeyguardViewMediator = keyguardViewMediator;
+ mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
+ }
@Override
public void onCreate() {
((SystemUIApplication) getApplication()).startServicesIfNeeded();
- mKeyguardViewMediator =
- ((SystemUIApplication) getApplication()).getComponent(KeyguardViewMediator.class);
- mKeyguardLifecyclesDispatcher = new KeyguardLifecyclesDispatcher(
- Dependency.get(ScreenLifecycle.class),
- Dependency.get(WakefulnessLifecycle.class));
-
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 1d2de6027d76..e66a9fadb937 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -60,7 +60,6 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
import com.android.systemui.util.wakelock.SettableWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
@@ -107,8 +106,8 @@ public class KeyguardSliceProvider extends SliceProvider implements
protected final Uri mMediaUri;
private final Date mCurrentTime = new Date();
private final Handler mHandler;
+ private final Handler mMediaHandler;
private final AlarmManager.OnAlarmListener mUpdateNextAlarm = this::updateNextAlarm;
- private final Object mMediaToken = new Object();
private DozeParameters mDozeParameters;
@VisibleForTesting
protected SettableWakeLock mMediaWakeLock;
@@ -174,17 +173,13 @@ public class KeyguardSliceProvider extends SliceProvider implements
}
};
- public KeyguardSliceProvider() {
- this(new Handler());
- }
-
public static KeyguardSliceProvider getAttachedInstance() {
return KeyguardSliceProvider.sInstance;
}
- @VisibleForTesting
- KeyguardSliceProvider(Handler handler) {
- mHandler = handler;
+ public KeyguardSliceProvider() {
+ mHandler = new Handler();
+ mMediaHandler = new Handler();
mSliceUri = Uri.parse(KEYGUARD_SLICE_URI);
mHeaderUri = Uri.parse(KEYGUARD_HEADER_URI);
mDateUri = Uri.parse(KEYGUARD_DATE_URI);
@@ -328,7 +323,7 @@ public class KeyguardSliceProvider extends SliceProvider implements
mContentResolver = getContext().getContentResolver();
mNextAlarmController = new NextAlarmControllerImpl(getContext());
mNextAlarmController.addCallback(this);
- mZenModeController = new ZenModeControllerImpl(getContext(), mHandler);
+ mZenModeController = Dependency.get(ZenModeController.class);
mZenModeController.addCallback(this);
mDatePattern = getContext().getString(R.string.system_ui_aod_date_pattern);
mPendingIntent = PendingIntent.getActivity(getContext(), 0, new Intent(), 0);
@@ -470,16 +465,18 @@ public class KeyguardSliceProvider extends SliceProvider implements
public void onMetadataOrStateChanged(MediaMetadata metadata, @PlaybackState.State int state) {
synchronized (this) {
boolean nextVisible = NotificationMediaManager.isPlayingState(state);
- mHandler.removeCallbacksAndMessages(mMediaToken);
+ mMediaHandler.removeCallbacksAndMessages(null);
if (mMediaIsVisible && !nextVisible && mStatusBarState != StatusBarState.SHADE) {
// We need to delay this event for a few millis when stopping to avoid jank in the
// animation. The media app might not send its update when buffering, and the slice
// would end up without a header for 0.5 second.
mMediaWakeLock.setAcquired(true);
- mHandler.postDelayed(() -> {
- updateMediaStateLocked(metadata, state);
- mMediaWakeLock.setAcquired(false);
- }, mMediaToken, 2000);
+ mMediaHandler.postDelayed(() -> {
+ synchronized (this) {
+ updateMediaStateLocked(metadata, state);
+ mMediaWakeLock.setAcquired(false);
+ }
+ }, 2000);
} else {
mMediaWakeLock.setAcquired(false);
updateMediaStateLocked(metadata, state);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 7fb6909be724..800d021c4ce7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -68,6 +68,7 @@ import android.view.WindowManagerPolicyConstants;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IKeyguardDrawnCallback;
import com.android.internal.policy.IKeyguardExitCallback;
@@ -100,6 +101,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import javax.inject.Inject;
+import javax.inject.Singleton;
/**
* Mediates requests related to the keyguard. This includes queries about the
@@ -142,6 +144,7 @@ import javax.inject.Inject;
* directly to the keyguard UI is posted to a {@link android.os.Handler} to ensure it is taken on the UI
* thread of the keyguard.
*/
+@Singleton
public class KeyguardViewMediator extends SystemUI {
private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
private static final long KEYGUARD_DONE_PENDING_TIMEOUT_MS = 3000;
@@ -232,7 +235,7 @@ public class KeyguardViewMediator extends SystemUI {
*/
private PowerManager.WakeLock mShowKeyguardWakeLock;
- private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
// these are protected by synchronized (this)
@@ -315,7 +318,7 @@ public class KeyguardViewMediator extends SystemUI {
* the keyguard.
*/
private boolean mWaitingUntilKeyguardVisible = false;
- private LockPatternUtils mLockPatternUtils;
+ private final LockPatternUtils mLockPatternUtils;
private boolean mKeyguardDonePending = false;
private boolean mHideAnimationRun = false;
private boolean mHideAnimationRunning = false;
@@ -648,29 +651,26 @@ public class KeyguardViewMediator extends SystemUI {
@Override
public int getBouncerPromptReason() {
- // TODO(b/140053364)
- return whitelistIpcs(() -> {
- int currentUser = ActivityManager.getCurrentUser();
- boolean trust = mTrustManager.isTrustUsuallyManaged(currentUser);
- boolean biometrics = mUpdateMonitor.isUnlockingWithBiometricsPossible(currentUser);
- boolean any = trust || biometrics;
- KeyguardUpdateMonitor.StrongAuthTracker strongAuthTracker =
- mUpdateMonitor.getStrongAuthTracker();
- int strongAuth = strongAuthTracker.getStrongAuthForUser(currentUser);
-
- if (any && !strongAuthTracker.hasUserAuthenticatedSinceBoot()) {
- return KeyguardSecurityView.PROMPT_REASON_RESTART;
- } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_TIMEOUT) != 0) {
- return KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
- } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW) != 0) {
- return KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
- } else if (trust && (strongAuth & SOME_AUTH_REQUIRED_AFTER_USER_REQUEST) != 0) {
- return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
- } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0) {
- return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT;
- }
- return KeyguardSecurityView.PROMPT_REASON_NONE;
- });
+ int currentUser = KeyguardUpdateMonitor.getCurrentUser();
+ boolean trust = mUpdateMonitor.isTrustUsuallyManaged(currentUser);
+ boolean biometrics = mUpdateMonitor.isUnlockingWithBiometricsPossible(currentUser);
+ boolean any = trust || biometrics;
+ KeyguardUpdateMonitor.StrongAuthTracker strongAuthTracker =
+ mUpdateMonitor.getStrongAuthTracker();
+ int strongAuth = strongAuthTracker.getStrongAuthForUser(currentUser);
+
+ if (any && !strongAuthTracker.hasUserAuthenticatedSinceBoot()) {
+ return KeyguardSecurityView.PROMPT_REASON_RESTART;
+ } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_TIMEOUT) != 0) {
+ return KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
+ } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW) != 0) {
+ return KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
+ } else if (trust && (strongAuth & SOME_AUTH_REQUIRED_AFTER_USER_REQUEST) != 0) {
+ return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
+ } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0) {
+ return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT;
+ }
+ return KeyguardSecurityView.PROMPT_REASON_NONE;
}
@Override
@@ -682,10 +682,26 @@ public class KeyguardViewMediator extends SystemUI {
};
@Inject
- public KeyguardViewMediator(FalsingManager falsingManager) {
- super();
-
+ public KeyguardViewMediator(
+ Context context,
+ FalsingManager falsingManager,
+ LockPatternUtils lockPatternUtils) {
+ this(context, falsingManager, lockPatternUtils, SystemUIFactory.getInstance());
+ }
+
+ @VisibleForTesting
+ KeyguardViewMediator(
+ Context context,
+ FalsingManager falsingManager,
+ LockPatternUtils lockPatternUtils,
+ SystemUIFactory systemUIFactory) {
+ super(context);
mFalsingManager = falsingManager;
+ mLockPatternUtils = lockPatternUtils;
+ mStatusBarKeyguardViewManager = systemUIFactory.createStatusBarKeyguardViewManager(
+ mContext,
+ mViewMediatorCallback,
+ mLockPatternUtils);
}
public void userActivity() {
@@ -699,7 +715,7 @@ public class KeyguardViewMediator extends SystemUI {
private void setupLocked() {
mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- mTrustManager = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
+ mTrustManager = mContext.getSystemService(TrustManager.class);
mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard");
mShowKeyguardWakeLock.setReferenceCounted(false);
@@ -723,7 +739,6 @@ public class KeyguardViewMediator extends SystemUI {
mUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
- mLockPatternUtils = new LockPatternUtils(mContext);
KeyguardUpdateMonitor.setCurrentUser(ActivityManager.getCurrentUser());
// Assume keyguard is showing (unless it's disabled) until we know for sure, unless Keyguard
@@ -737,9 +752,6 @@ public class KeyguardViewMediator extends SystemUI {
setShowingLocked(false /* showing */, true /* forceCallbacks */);
}
- mStatusBarKeyguardViewManager =
- SystemUIFactory.getInstance().createStatusBarKeyguardViewManager(mContext,
- mViewMediatorCallback, mLockPatternUtils);
final ContentResolver cr = mContext.getContentResolver();
mDeviceInteractive = mPM.isInteractive();
@@ -789,7 +801,6 @@ public class KeyguardViewMediator extends SystemUI {
synchronized (this) {
setupLocked();
}
- putComponent(KeyguardViewMediator.class, this);
}
/**
@@ -822,6 +833,11 @@ public class KeyguardViewMediator extends SystemUI {
mDeviceInteractive = false;
mGoingToSleep = true;
+ // Reset keyguard going away state so we can start listening for fingerprint. We
+ // explicitly DO NOT want to call mStatusBarWindowController.setKeyguardGoingAway(false)
+ // here, since that will mess with the device lock state.
+ mUpdateMonitor.setKeyguardGoingAway(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
// camera while preventing unwanted input.
@@ -1569,12 +1585,14 @@ public class KeyguardViewMediator extends SystemUI {
handleNotifyFinishedGoingToSleep();
break;
case NOTIFY_SCREEN_TURNING_ON:
- Trace.beginSection("KeyguardViewMediator#handleMessage NOTIFY_SCREEN_TURNING_ON");
+ Trace.beginSection(
+ "KeyguardViewMediator#handleMessage NOTIFY_SCREEN_TURNING_ON");
handleNotifyScreenTurningOn((IKeyguardDrawnCallback) msg.obj);
Trace.endSection();
break;
case NOTIFY_SCREEN_TURNED_ON:
- Trace.beginSection("KeyguardViewMediator#handleMessage NOTIFY_SCREEN_TURNED_ON");
+ Trace.beginSection(
+ "KeyguardViewMediator#handleMessage NOTIFY_SCREEN_TURNED_ON");
handleNotifyScreenTurnedOn();
Trace.endSection();
break;
@@ -1582,7 +1600,8 @@ public class KeyguardViewMediator extends SystemUI {
handleNotifyScreenTurnedOff();
break;
case NOTIFY_STARTED_WAKING_UP:
- Trace.beginSection("KeyguardViewMediator#handleMessage NOTIFY_STARTED_WAKING_UP");
+ Trace.beginSection(
+ "KeyguardViewMediator#handleMessage NOTIFY_STARTED_WAKING_UP");
handleNotifyStartedWakingUp();
Trace.endSection();
break;
@@ -1611,14 +1630,16 @@ public class KeyguardViewMediator extends SystemUI {
handleDismiss(message.getCallback(), message.getMessage());
break;
case START_KEYGUARD_EXIT_ANIM:
- Trace.beginSection("KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM");
+ Trace.beginSection(
+ "KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM");
StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj;
handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration);
mFalsingManager.onSucccessfulUnlock();
Trace.endSection();
break;
case KEYGUARD_DONE_PENDING_TIMEOUT:
- Trace.beginSection("KeyguardViewMediator#handleMessage KEYGUARD_DONE_PENDING_TIMEOUT");
+ Trace.beginSection("KeyguardViewMediator#handleMessage"
+ + " KEYGUARD_DONE_PENDING_TIMEOUT");
Log.w(TAG, "Timeout while waiting for activity drawn!");
Trace.endSection();
break;
@@ -1796,8 +1817,8 @@ public class KeyguardViewMediator extends SystemUI {
mHideAnimationRun = false;
adjustStatusBarLocked();
userActivity();
- mUpdateMonitor.setKeyguardGoingAway(false /* away */);
- mStatusBarWindowController.setKeyguardGoingAway(false /* goingAway */);
+ mUpdateMonitor.setKeyguardGoingAway(false);
+ mStatusBarWindowController.setKeyguardGoingAway(false);
mShowKeyguardWakeLock.release();
}
mKeyguardDisplayManager.show();
@@ -1829,8 +1850,8 @@ public class KeyguardViewMediator extends SystemUI {
.KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS;
}
- mUpdateMonitor.setKeyguardGoingAway(true /* goingAway */);
- mStatusBarWindowController.setKeyguardGoingAway(true /* goingAway */);
+ mUpdateMonitor.setKeyguardGoingAway(true);
+ mStatusBarWindowController.setKeyguardGoingAway(true);
// Don't actually hide the Keyguard at the moment, wait for window
// manager until it tells us it's safe to do so with
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
index a9fe54bae19d..4d061e18ebba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivity.java
@@ -49,6 +49,12 @@ public class WorkLockActivity extends Activity {
private static final String TAG = "WorkLockActivity";
/**
+ * Add additional extra {@link com.android.settings.password.ConfirmDeviceCredentialActivity} to
+ * enable device policy management enforcement from systemui.
+ */
+ public static final String EXTRA_FROM_WORK_LOCK_ACTIVITY = "from_work_lock_activity";
+
+ /**
* Contains a {@link TaskDescription} for the activity being covered.
*/
static final String EXTRA_TASK_DESCRIPTION =
@@ -151,6 +157,7 @@ public class WorkLockActivity extends Activity {
if (target != null) {
credential.putExtra(Intent.EXTRA_INTENT, target.getIntentSender());
+ credential.putExtra(EXTRA_FROM_WORK_LOCK_ACTIVITY, true);
}
startActivityForResult(credential, REQUEST_CODE_CONFIRM_CREDENTIALS);
diff --git a/packages/SystemUI/src/com/android/systemui/log/Event.java b/packages/SystemUI/src/com/android/systemui/log/Event.java
new file mode 100644
index 000000000000..92862a2bc74c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/Event.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Stores information about an event that occurred in SystemUI to be used for debugging and triage.
+ * Every event has a time stamp, log level and message.
+ * Events are stored in {@link SysuiLog} and can be printed in a dumpsys.
+ */
+public class Event {
+ public static final int UNINITIALIZED = -1;
+
+ @IntDef({ERROR, WARN, INFO, DEBUG, VERBOSE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Level {}
+ public static final int VERBOSE = 2;
+ public static final int DEBUG = 3;
+ public static final int INFO = 4;
+ public static final int WARN = 5;
+ public static final int ERROR = 6;
+
+ private long mTimestamp;
+ private @Level int mLogLevel = DEBUG;
+ protected String mMessage;
+
+ public Event(String message) {
+ mTimestamp = System.currentTimeMillis();
+ mMessage = message;
+ }
+
+ public Event(@Level int logLevel, String message) {
+ mTimestamp = System.currentTimeMillis();
+ mLogLevel = logLevel;
+ mMessage = message;
+ }
+
+ public String getMessage() {
+ return mMessage;
+ }
+
+ public long getTimestamp() {
+ return mTimestamp;
+ }
+
+ public @Level int getLogLevel() {
+ return mLogLevel;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/RichEvent.java b/packages/SystemUI/src/com/android/systemui/log/RichEvent.java
new file mode 100644
index 000000000000..89b7a8181c44
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/RichEvent.java
@@ -0,0 +1,107 @@
+/*
+ * 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.log;
+
+/**
+ * Stores information about an event that occurred in SystemUI to be used for debugging and triage.
+ * Every rich event has a time stamp, event type, and log level, with the option to provide the
+ * reason this event was triggered.
+ * Events are stored in {@link SysuiLog} and can be printed in a dumpsys.
+ */
+public abstract class RichEvent extends Event {
+ private final int mType;
+ private final String mReason;
+
+ /**
+ * Create a rich event that includes an event type that matches with an index in the array
+ * getEventLabels().
+ */
+ public RichEvent(@Event.Level int logLevel, int type, String reason) {
+ super(logLevel, null);
+ final int numEvents = getEventLabels().length;
+ if (type < 0 || type >= numEvents) {
+ throw new IllegalArgumentException("Unsupported event type. Events only supported"
+ + " from 0 to " + (numEvents - 1) + ", but given type=" + type);
+ }
+ mType = type;
+ mReason = reason;
+ mMessage = getEventLabels()[mType] + " " + mReason;
+ }
+
+ /**
+ * Returns an array of the event labels. The index represents the event type and the
+ * corresponding String stored at that index is the user-readable representation of that event.
+ * @return array of user readable events, where the index represents its event type constant
+ */
+ public abstract String[] getEventLabels();
+
+ public int getType() {
+ return mType;
+ }
+
+ public String getReason() {
+ return mReason;
+ }
+
+ /**
+ * Builder to build a RichEvent.
+ * @param <B> Log specific builder that is extending this builder
+ */
+ public abstract static class Builder<B extends Builder<B>> {
+ public static final int UNINITIALIZED = -1;
+
+ private B mBuilder = getBuilder();
+ protected int mType = UNINITIALIZED;
+ protected String mReason;
+ protected @Level int mLogLevel;
+
+ /**
+ * Get the log-specific builder.
+ */
+ public abstract B getBuilder();
+
+ /**
+ * Build the log-specific event.
+ */
+ public abstract RichEvent build();
+
+ /**
+ * Optional - set the log level. Defaults to DEBUG.
+ */
+ public B setLogLevel(@Level int logLevel) {
+ mLogLevel = logLevel;
+ return mBuilder;
+ }
+
+ /**
+ * Required - set the event type. These events must correspond with the events from
+ * getEventLabels().
+ */
+ public B setType(int type) {
+ mType = type;
+ return mBuilder;
+ }
+
+ /**
+ * Optional - set the reason why this event was triggered.
+ */
+ public B setReason(String reason) {
+ mReason = reason;
+ return mBuilder;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java b/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java
new file mode 100644
index 000000000000..a6e10e6b345b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java
@@ -0,0 +1,163 @@
+/*
+ * 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.log;
+
+import android.os.Build;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.DumpController;
+import com.android.systemui.Dumpable;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayDeque;
+import java.util.Locale;
+
+/**
+ * Thread-safe logger in SystemUI which prints logs to logcat and stores logs to be
+ * printed by the DumpController. This is an alternative to printing directly
+ * to avoid logs being deleted by chatty. The number of logs retained is varied based on
+ * whether the build is {@link Build.IS_DEBUGGABLE}.
+ *
+ * To manually view the logs via adb:
+ * adb shell dumpsys activity service com.android.systemui/.SystemUIService \
+ * dependency DumpController <SysuiLogId>
+ */
+public class SysuiLog implements Dumpable {
+ public static final SimpleDateFormat DATE_FORMAT =
+ new SimpleDateFormat("MM-dd HH:mm:ss", Locale.US);
+
+ private final Object mDataLock = new Object();
+ private final String mId;
+ private final int mMaxLogs;
+ private boolean mEnabled;
+
+ @VisibleForTesting protected ArrayDeque<Event> mTimeline;
+
+ /**
+ * Creates a SysuiLog
+ * To enable or disable logs, set the system property and then restart the device:
+ * adb shell setprop sysui.log.enabled.<id> true/false && adb reboot
+ * @param dumpController where to register this logger's dumpsys
+ * @param id user-readable tag for this logger
+ * @param maxDebugLogs maximum number of logs to retain when {@link sDebuggable} is true
+ * @param maxLogs maximum number of logs to retain when {@link sDebuggable} is false
+ */
+ public SysuiLog(DumpController dumpController, String id, int maxDebugLogs, int maxLogs) {
+ this(dumpController, id, sDebuggable ? maxDebugLogs : maxLogs,
+ SystemProperties.getBoolean(SYSPROP_ENABLED_PREFIX + id, DEFAULT_ENABLED));
+ }
+
+ @VisibleForTesting
+ protected SysuiLog(DumpController dumpController, String id, int maxLogs, boolean enabled) {
+ mId = id;
+ mMaxLogs = maxLogs;
+ mEnabled = enabled;
+ mTimeline = mEnabled ? new ArrayDeque<>(mMaxLogs) : null;
+ dumpController.registerDumpable(mId, this);
+ }
+
+ public SysuiLog(DumpController dumpController, String id) {
+ this(dumpController, id, DEFAULT_MAX_DEBUG_LOGS, DEFAULT_MAX_LOGS);
+ }
+
+ /**
+ * Logs an event to the timeline which can be printed by the dumpsys.
+ * May also log to logcat if enabled.
+ * @return true if event was logged, else false
+ */
+ public boolean log(Event event) {
+ if (!mEnabled) {
+ return false;
+ }
+
+ synchronized (mDataLock) {
+ if (mTimeline.size() >= mMaxLogs) {
+ mTimeline.removeFirst();
+ }
+
+ mTimeline.add(event);
+ }
+
+ if (LOG_TO_LOGCAT_ENABLED) {
+ final String strEvent = eventToString(event);
+ switch (event.getLogLevel()) {
+ case Event.VERBOSE:
+ Log.v(mId, strEvent);
+ break;
+ case Event.DEBUG:
+ Log.d(mId, strEvent);
+ break;
+ case Event.ERROR:
+ Log.e(mId, strEvent);
+ break;
+ case Event.INFO:
+ Log.i(mId, strEvent);
+ break;
+ case Event.WARN:
+ Log.w(mId, strEvent);
+ break;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @return user-readable string of the given event
+ */
+ public String eventToString(Event event) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(SysuiLog.DATE_FORMAT.format(event.getTimestamp()));
+ sb.append(" ");
+ sb.append(event.getMessage());
+ return sb.toString();
+ }
+
+ /**
+ * only call on this method if you have the mDataLock
+ */
+ private void dumpTimelineLocked(PrintWriter pw) {
+ pw.println("\tTimeline:");
+
+ for (Event event : mTimeline) {
+ pw.println("\t" + eventToString(event));
+ }
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(mId + ":");
+
+ if (mEnabled) {
+ synchronized (mDataLock) {
+ dumpTimelineLocked(pw);
+ }
+ } else {
+ pw.print(" - Logging disabled.");
+ }
+ }
+
+ private static boolean sDebuggable = Build.IS_DEBUGGABLE;
+ private static final String SYSPROP_ENABLED_PREFIX = "sysui.log.enabled.";
+ private static final boolean LOG_TO_LOGCAT_ENABLED = sDebuggable;
+ private static final boolean DEFAULT_ENABLED = sDebuggable;
+ private static final int DEFAULT_MAX_DEBUG_LOGS = 100;
+ private static final int DEFAULT_MAX_LOGS = 50;
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index aebadf936e0c..4c96de232810 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -59,6 +59,10 @@ public class RingtonePlayer extends SystemUI {
private final NotificationPlayer mAsyncPlayer = new NotificationPlayer(TAG);
private final HashMap<IBinder, Client> mClients = new HashMap<IBinder, Client>();
+ public RingtonePlayer(Context context) {
+ super(context);
+ }
+
@Override
public void start() {
mAsyncPlayer.setUsesWakeLock(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
index e0fc31bc70b8..05be4259dd3b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
@@ -27,5 +27,6 @@ public interface BasePipManager {
default void expandPip() {}
default void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {}
void onConfigurationChanged(Configuration newConfig);
+ default void setShelfHeight(boolean visible, int height) {}
default void dump(PrintWriter pw) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index 6795bff6409a..686e7db86c3b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -55,6 +55,12 @@ public class PipBoundsHandler {
private final Rect mTmpInsets = new Rect();
private final Point mTmpDisplaySize = new Point();
+ /**
+ * Tracks the destination bounds, used for any following
+ * {@link #onMovementBoundsChanged(Rect, Rect, Rect, DisplayInfo)} calculations.
+ */
+ private final Rect mLastDestinationBounds = new Rect();
+
private IPinnedStackController mPinnedStackController;
private ComponentName mLastPipComponentName;
private float mReentrySnapFraction = INVALID_SNAP_FRACTION;
@@ -120,19 +126,26 @@ public class PipBoundsHandler {
}
/**
- * Responds to IPinnedStackListener on IME visibility change.
+ * Sets both shelf visibility and its height if applicable.
+ * @return {@code true} if the internal shelf state is changed, {@code false} otherwise.
*/
- public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
- mIsImeShowing = imeVisible;
- mImeHeight = imeHeight;
+ public boolean setShelfHeight(boolean shelfVisible, int shelfHeight) {
+ final boolean shelfShowing = shelfVisible && shelfHeight > 0;
+ if (shelfShowing == mIsShelfShowing && shelfHeight == mShelfHeight) {
+ return false;
+ }
+
+ mIsShelfShowing = shelfVisible;
+ mShelfHeight = shelfHeight;
+ return true;
}
/**
- * Responds to IPinnedStackListener on shelf visibility change.
+ * Responds to IPinnedStackListener on IME visibility change.
*/
- public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {
- mIsShelfShowing = shelfVisible;
- mShelfHeight = shelfHeight;
+ public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
+ mIsImeShowing = imeVisible;
+ mImeHeight = imeHeight;
}
/**
@@ -185,6 +198,10 @@ public class PipBoundsHandler {
mLastPipComponentName = null;
}
+ public Rect getLastDestinationBounds() {
+ return mLastDestinationBounds;
+ }
+
/**
* Responds to IPinnedStackListener on {@link DisplayInfo} change.
* It will normally follow up with a
@@ -232,6 +249,7 @@ public class PipBoundsHandler {
try {
mPinnedStackController.startAnimation(destinationBounds, sourceRectHint,
-1 /* animationDuration */);
+ mLastDestinationBounds.set(destinationBounds);
} catch (RemoteException e) {
Log.e(TAG, "Failed to start PiP animation from SysUI", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
index 37c8163702cf..f1e801b3a474 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
@@ -19,6 +19,7 @@ package com.android.systemui.pip;
import static android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.UserHandle;
@@ -30,15 +31,24 @@ import com.android.systemui.statusbar.CommandQueue;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Controls the picture-in-picture window.
*/
+@Singleton
public class PipUI extends SystemUI implements CommandQueue.Callbacks {
private BasePipManager mPipManager;
private boolean mSupportsPip;
+ @Inject
+ public PipUI(Context context) {
+ super(context);
+ }
+
@Override
public void start() {
PackageManager pm = mContext.getPackageManager();
@@ -59,7 +69,6 @@ public class PipUI extends SystemUI implements CommandQueue.Callbacks {
mPipManager.initialize(mContext);
getComponent(CommandQueue.class).addCallback(this);
- putComponent(PipUI.class, this);
}
@Override
@@ -85,6 +94,14 @@ public class PipUI extends SystemUI implements CommandQueue.Callbacks {
mPipManager.onConfigurationChanged(newConfig);
}
+ public void setShelfHeight(boolean visible, int height) {
+ if (mPipManager == null) {
+ return;
+ }
+
+ mPipManager.setShelfHeight(visible, height);
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mPipManager == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
index 8224365e7b96..3f15966c7fbb 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
@@ -107,7 +107,7 @@ public class PipDismissViewController {
| LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
lp.setTitle("pip-dismiss-overlay");
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
mWindowManager.addView(mDismissView, lp);
}
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 8dfae32a1939..369073c6564d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -143,14 +143,6 @@ public class PipManager implements BasePipManager {
}
@Override
- public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {
- mHandler.post(() -> {
- mPipBoundsHandler.onShelfVisibilityChanged(shelfVisible, shelfHeight);
- mTouchHandler.onShelfVisibilityChanged(shelfVisible, shelfHeight);
- });
- }
-
- @Override
public void onMinimizedStateChanged(boolean isMinimized) {
mHandler.post(() -> {
mPipBoundsHandler.onMinimizedStateChanged(isMinimized);
@@ -161,14 +153,8 @@ public class PipManager implements BasePipManager {
@Override
public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment,
boolean fromShelfAdjustment) {
- mHandler.post(() -> {
- // Populate the inset / normal bounds and DisplayInfo from mPipBoundsHandler first.
- mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
- animatingBounds, mTmpDisplayInfo);
- mTouchHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
- animatingBounds, fromImeAdjustment, fromShelfAdjustment,
- mTmpDisplayInfo.rotation);
- });
+ mHandler.post(() -> updateMovementBounds(animatingBounds, fromImeAdjustment,
+ fromShelfAdjustment));
}
@Override
@@ -280,6 +266,31 @@ public class PipManager implements BasePipManager {
}
/**
+ * Sets both shelf visibility and its height.
+ */
+ @Override
+ public void setShelfHeight(boolean visible, int height) {
+ mHandler.post(() -> {
+ final boolean changed = mPipBoundsHandler.setShelfHeight(visible, height);
+ if (changed) {
+ mTouchHandler.onShelfVisibilityChanged(visible, height);
+ updateMovementBounds(mPipBoundsHandler.getLastDestinationBounds(),
+ false /* fromImeAdjustment */, true /* fromShelfAdjustment */);
+ }
+ });
+ }
+
+ private void updateMovementBounds(Rect animatingBounds, boolean fromImeAdjustment,
+ boolean fromShelfAdjustment) {
+ // Populate inset / normal bounds and DisplayInfo from mPipBoundsHandler first.
+ mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
+ animatingBounds, mTmpDisplayInfo);
+ mTouchHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
+ animatingBounds, fromImeAdjustment, fromShelfAdjustment,
+ mTmpDisplayInfo.rotation);
+ }
+
+ /**
* Gets an instance of {@link PipManager}.
*/
public static PipManager getInstance() {
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 4f2a6d82a08e..c452f64ff6f0 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -90,7 +90,7 @@ public class PipMenuActivity extends Activity {
public static final int MESSAGE_UPDATE_ACTIONS = 4;
public static final int MESSAGE_UPDATE_DISMISS_FRACTION = 5;
public static final int MESSAGE_ANIMATION_ENDED = 6;
- public static final int MESSAGE_TOUCH_EVENT = 7;
+ public static final int MESSAGE_POINTER_EVENT = 7;
private static final int INITIAL_DISMISS_DELAY = 3500;
private static final int POST_INTERACTION_DISMISS_DELAY = 2000;
@@ -165,9 +165,9 @@ public class PipMenuActivity extends Activity {
break;
}
- case MESSAGE_TOUCH_EVENT: {
+ case MESSAGE_POINTER_EVENT: {
final MotionEvent ev = (MotionEvent) msg.obj;
- dispatchTouchEvent(ev);
+ dispatchPointerEvent(ev);
break;
}
}
@@ -219,6 +219,9 @@ public class PipMenuActivity extends Activity {
updateFromIntent(getIntent());
setTitle(R.string.pip_menu_title);
setDisablePreviewScreenshots(true);
+
+ // Hide without an animation.
+ getWindow().setExitTransition(null);
}
@Override
@@ -247,6 +250,9 @@ public class PipMenuActivity extends Activity {
protected void onStop() {
super.onStop();
+ // In cases such as device lock, hide and finish it so that it can be recreated on the top
+ // next time it starts, see also {@link #onUserLeaveHint}
+ hideMenu();
cancelDelayedFinish();
}
@@ -266,6 +272,17 @@ public class PipMenuActivity extends Activity {
}
}
+ /**
+ * Dispatch a pointer event from {@link PipTouchHandler}.
+ */
+ private void dispatchPointerEvent(MotionEvent event) {
+ if (event.isTouchEvent()) {
+ dispatchTouchEvent(event);
+ } else {
+ dispatchGenericMotionEvent(event);
+ }
+ }
+
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (!mAllowTouches) {
@@ -285,8 +302,6 @@ public class PipMenuActivity extends Activity {
public void finish() {
notifyActivityCallback(null);
super.finish();
- // Hide without an animation (the menu should already be invisible at this point)
- overridePendingTransition(0, 0);
}
@Override
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 62c59e5842ff..b8e0b81e5e8e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -508,12 +508,12 @@ public class PipMenuActivityController {
}
/**
- * Handles touch event sent from pip input consumer.
+ * Handles a pointer event sent from pip input consumer.
*/
- void handleTouchEvent(MotionEvent ev) {
+ void handlePointerEvent(MotionEvent ev) {
if (mToActivityMessenger != null) {
Message m = Message.obtain();
- m.what = PipMenuActivity.MESSAGE_TOUCH_EVENT;
+ m.what = PipMenuActivity.MESSAGE_POINTER_EVENT;
m.obj = ev;
try {
mToActivityMessenger.send(m);
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 1f36d97ce308..f59b372762bd 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -132,6 +132,7 @@ public class PipTouchHandler {
private boolean mSendingHoverAccessibilityEvents;
private boolean mMovementWithinMinimize;
private boolean mMovementWithinDismiss;
+ private PipAccessibilityInteractionConnection mConnection;
// Touch state
private final PipTouchState mTouchState;
@@ -213,9 +214,10 @@ public class PipTouchHandler {
// Register the listener for input consumer touch events
inputConsumerController.setInputListener(this::handleTouchEvent);
inputConsumerController.setRegistrationListener(this::onRegistrationChanged);
- onRegistrationChanged(inputConsumerController.isRegistered());
mPipBoundsHandler = pipBoundsHandler;
+ mConnection = new PipAccessibilityInteractionConnection(mMotionHelper,
+ this::onAccessibilityShowMenu, mHandler);
}
public void setTouchEnabled(boolean enabled) {
@@ -339,9 +341,7 @@ public class PipTouchHandler {
private void onRegistrationChanged(boolean isRegistered) {
mAccessibilityManager.setPictureInPictureActionReplacingConnection(isRegistered
- ? new PipAccessibilityInteractionConnection(mMotionHelper,
- this::onAccessibilityShowMenu, mHandler) : null);
-
+ ? mConnection : null);
if (!isRegistered && mTouchState.isUserInteracting()) {
// If the input consumer is unregistered while the user is interacting, then we may not
// get the final TOUCH_UP event, so clean up the dismiss target as well
@@ -409,27 +409,15 @@ public class PipTouchHandler {
}
case MotionEvent.ACTION_HOVER_ENTER:
case MotionEvent.ACTION_HOVER_MOVE: {
- if (mAccessibilityManager.isEnabled() && !mSendingHoverAccessibilityEvents) {
- AccessibilityEvent event = AccessibilityEvent.obtain(
- AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
- event.setImportantForAccessibility(true);
- event.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID);
- event.setWindowId(
- AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID);
- mAccessibilityManager.sendAccessibilityEvent(event);
+ if (!shouldDeliverToMenu && !mSendingHoverAccessibilityEvents) {
+ sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
mSendingHoverAccessibilityEvents = true;
}
break;
}
case MotionEvent.ACTION_HOVER_EXIT: {
- if (mAccessibilityManager.isEnabled() && mSendingHoverAccessibilityEvents) {
- AccessibilityEvent event = AccessibilityEvent.obtain(
- AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
- event.setImportantForAccessibility(true);
- event.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID);
- event.setWindowId(
- AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID);
- mAccessibilityManager.sendAccessibilityEvent(event);
+ if (!shouldDeliverToMenu && mSendingHoverAccessibilityEvents) {
+ sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
mSendingHoverAccessibilityEvents = false;
}
break;
@@ -445,12 +433,25 @@ public class PipTouchHandler {
mMenuController.pokeMenu();
}
- mMenuController.handleTouchEvent(cloneEvent);
+ mMenuController.handlePointerEvent(cloneEvent);
}
return true;
}
+ private void sendAccessibilityHoverEvent(int type) {
+ if (!mAccessibilityManager.isEnabled()) {
+ return;
+ }
+
+ AccessibilityEvent event = AccessibilityEvent.obtain(type);
+ event.setImportantForAccessibility(true);
+ event.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID);
+ event.setWindowId(
+ AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID);
+ mAccessibilityManager.sendAccessibilityEvent(event);
+ }
+
/**
* Updates the appearance of the menu and scrim on top of the PiP while dismissing.
*/
@@ -523,6 +524,10 @@ public class PipTouchHandler {
* Sets the menu visibility.
*/
private void setMenuState(int menuState, boolean resize) {
+ if (mMenuState == menuState && !resize) {
+ return;
+ }
+
if (menuState == MENU_STATE_FULL && mMenuState != MENU_STATE_FULL) {
// Save the current snap fraction and if we do not drag or move the PiP, then
// we store back to this snap fraction. Otherwise, we'll reset the snap
@@ -571,6 +576,9 @@ public class PipTouchHandler {
}
mMenuState = menuState;
updateMovementBounds(menuState);
+ // 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.
+ onRegistrationChanged(menuState == MENU_STATE_NONE);
if (menuState != MENU_STATE_CLOSE) {
MetricsLoggerWrapper.logPictureInPictureMenuVisible(mContext, menuState == MENU_STATE_FULL);
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 75dc39722bcf..f60d9db7ac9c 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -45,6 +45,7 @@ import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.statusbar.phone.StatusBar;
import java.io.FileDescriptor;
@@ -53,6 +54,12 @@ import java.time.Duration;
import java.util.Arrays;
import java.util.concurrent.Future;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+
+@Singleton
public class PowerUI extends SystemUI {
static final String TAG = "PowerUI";
@@ -97,6 +104,16 @@ public class PowerUI extends SystemUI {
private IThermalEventListener mSkinThermalEventListener;
private IThermalEventListener mUsbThermalEventListener;
+ private final BroadcastDispatcher mBroadcastDispatcher;
+ private final Lazy<StatusBar> mStatusBarLazy;
+
+ @Inject
+ public PowerUI(Context context, BroadcastDispatcher broadcastDispatcher,
+ Lazy<StatusBar> statusBarLazy) {
+ super(context);
+ mBroadcastDispatcher = broadcastDispatcher;
+ mStatusBarLazy = statusBarLazy;
+ }
public void start() {
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -211,7 +228,7 @@ public class PowerUI extends SystemUI {
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_USER_SWITCHED);
- mContext.registerReceiver(this, filter, null, mHandler);
+ mBroadcastDispatcher.registerReceiver(this, filter, mHandler);
}
@Override
@@ -653,8 +670,7 @@ public class PowerUI extends SystemUI {
int status = temp.getStatus();
if (status >= Temperature.THROTTLING_EMERGENCY) {
- StatusBar statusBar = getComponent(StatusBar.class);
- if (statusBar != null && !statusBar.isDeviceInVrMode()) {
+ if (!mStatusBarLazy.get().isDeviceInVrMode()) {
mWarnings.showHighTemperatureWarning();
Slog.d(TAG, "SkinThermalEventListener: notifyThrottling was called "
+ ", current skin status = " + status
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index 631b8b7a14a0..22fb4c0dbdb5 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -22,25 +22,20 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
-import android.os.Handler
-import android.os.Looper
-import android.os.Message
-import android.os.UserHandle
-import android.os.UserManager
+import android.os.*
import android.provider.DeviceConfig
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
-import com.android.systemui.Dependency.BG_HANDLER_NAME
-import com.android.systemui.Dependency.MAIN_HANDLER_NAME
+import com.android.systemui.Dumpable
import com.android.systemui.R
import com.android.systemui.appops.AppOpItem
import com.android.systemui.appops.AppOpsController
-import com.android.systemui.Dumpable
+import com.android.systemui.dagger.qualifiers.BgHandler
+import com.android.systemui.dagger.qualifiers.MainHandler
import java.io.FileDescriptor
import java.io.PrintWriter
import java.lang.ref.WeakReference
import javax.inject.Inject
-import javax.inject.Named
import javax.inject.Singleton
fun isPermissionsHubEnabled() = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
@@ -50,8 +45,8 @@ fun isPermissionsHubEnabled() = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_P
class PrivacyItemController @Inject constructor(
val context: Context,
private val appOpsController: AppOpsController,
- @Named(MAIN_HANDLER_NAME) private val uiHandler: Handler,
- @Named(BG_HANDLER_NAME) private val bgHandler: Handler
+ @MainHandler private val uiHandler: Handler,
+ @BgHandler private val bgHandler: Handler
) : Dumpable {
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
new file mode 100644
index 000000000000..f710f7fc47e2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs
+
+import android.content.Context
+import android.content.res.Configuration
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.R
+import com.android.systemui.qs.TileLayout.exactly
+
+class DoubleLineTileLayout(context: Context) : ViewGroup(context), QSPanel.QSTileLayout {
+
+ protected val mRecords = ArrayList<QSPanel.TileRecord>()
+ private var _listening = false
+ private var smallTileSize = 0
+ private val twoLineHeight
+ get() = smallTileSize * 2 + cellMarginVertical
+ private var cellMarginHorizontal = 0
+ private var cellMarginVertical = 0
+
+ init {
+ isFocusableInTouchMode = true
+ clipChildren = false
+ clipToPadding = false
+
+ updateResources()
+ }
+
+ override fun addTile(tile: QSPanel.TileRecord) {
+ mRecords.add(tile)
+ tile.tile.setListening(this, _listening)
+ addTileView(tile)
+ }
+
+ protected fun addTileView(tile: QSPanel.TileRecord) {
+ addView(tile.tileView)
+ }
+
+ override fun removeTile(tile: QSPanel.TileRecord) {
+ mRecords.remove(tile)
+ tile.tile.setListening(this, false)
+ removeView(tile.tileView)
+ }
+
+ override fun removeAllViews() {
+ mRecords.forEach { it.tile.setListening(this, false) }
+ mRecords.clear()
+ super.removeAllViews()
+ }
+
+ override fun getOffsetTop(tile: QSPanel.TileRecord?) = top
+
+ override fun updateResources(): Boolean {
+ with(mContext.resources) {
+ smallTileSize = getDimensionPixelSize(R.dimen.qs_quick_tile_size)
+ cellMarginHorizontal = getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal)
+ cellMarginVertical = getDimensionPixelSize(R.dimen.new_qs_vertical_margin)
+ }
+ requestLayout()
+ return false
+ }
+
+ override fun setListening(listening: Boolean) {
+ if (_listening == listening) return
+ _listening = listening
+ for (record in mRecords) {
+ record.tile.setListening(this, listening)
+ }
+ }
+
+ override fun getNumVisibleTiles() = mRecords.size
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ updateResources()
+ }
+
+ override fun onFinishInflate() {
+ updateResources()
+ }
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ var previousView: View = this
+ var tiles = 0
+
+ mRecords.forEach {
+ val tileView = it.tileView
+ if (tileView.visibility != View.GONE) {
+ tileView.updateAccessibilityOrder(previousView)
+ previousView = tileView
+ tiles++
+ tileView.measure(exactly(smallTileSize), exactly(smallTileSize))
+ }
+ }
+
+ val height = twoLineHeight
+ val columns = tiles / 2
+ val width = paddingStart + paddingEnd +
+ columns * smallTileSize +
+ (columns - 1) * cellMarginHorizontal
+ setMeasuredDimension(width, height)
+ }
+
+ override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
+ val tiles = mRecords.filter { it.tileView.visibility != View.GONE }
+ tiles.forEachIndexed {
+ index, tile ->
+ val column = index % (tiles.size / 2)
+ val left = getLeftForColumn(column)
+ val top = if (index < tiles.size / 2) 0 else getTopBottomRow()
+ tile.tileView.layout(left, top, left + smallTileSize, top + smallTileSize)
+ }
+ }
+
+ private fun getLeftForColumn(column: Int) = column * (smallTileSize + cellMarginHorizontal)
+
+ private fun getTopBottomRow() = smallTileSize + cellMarginVertical
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 9221b6852112..a267bbb92ee7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -14,6 +14,7 @@
package com.android.systemui.qs;
+import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
@@ -267,6 +268,17 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
mAllViews.add(tileView);
count++;
}
+
+
+ int flag = Settings.System.getInt(mQsPanel.getContext().getContentResolver(),
+ "qs_media_player", 0);
+ if (flag == 1) {
+ View qsMediaView = mQsPanel.getMediaPanel();
+ View qqsMediaView = mQuickQsPanel.getMediaPlayer().getView();
+ translationXBuilder.addFloat(qsMediaView, "alpha", 0, 1);
+ translationXBuilder.addFloat(qqsMediaView, "alpha", 1, 0);
+ }
+
if (mAllowFancy) {
// Make brightness appear static position and alpha in through second half.
View brightness = mQsPanel.getBrightnessView();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
index 27a517c52458..8fe687ba297c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java
@@ -17,12 +17,15 @@
package com.android.systemui.qs;
import static com.android.systemui.Dependency.BG_HANDLER;
-import static com.android.systemui.Dependency.BG_HANDLER_NAME;
+import static com.android.systemui.Dependency.MAIN_LOOPER;
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
+import android.annotation.MainThread;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
import android.util.AttributeSet;
@@ -36,9 +39,13 @@ import androidx.annotation.VisibleForTesting;
import com.android.keyguard.CarrierTextController;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.BgHandler;
+import com.android.systemui.dagger.qualifiers.MainLooper;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.policy.NetworkController;
+import java.util.function.Consumer;
+
import javax.inject.Inject;
import javax.inject.Named;
@@ -46,7 +53,6 @@ import javax.inject.Named;
* Displays Carrier name and network status in QS
*/
public class QSCarrierGroup extends LinearLayout implements
- CarrierTextController.CarrierTextCallback,
NetworkController.SignalCallback, View.OnClickListener {
private static final String TAG = "QSCarrierGroup";
@@ -56,12 +62,14 @@ public class QSCarrierGroup extends LinearLayout implements
private static final int SIM_SLOTS = 3;
private final NetworkController mNetworkController;
private final Handler mBgHandler;
+ private final H mMainHandler;
private View[] mCarrierDividers = new View[SIM_SLOTS - 1];
private QSCarrier[] mCarrierGroups = new QSCarrier[SIM_SLOTS];
private TextView mNoSimTextView;
private final CellSignalState[] mInfos = new CellSignalState[SIM_SLOTS];
private CarrierTextController mCarrierTextController;
+ private CarrierTextController.CarrierTextCallback mCallback;
private ActivityStarter mActivityStarter;
private boolean mListening;
@@ -69,11 +77,19 @@ public class QSCarrierGroup extends LinearLayout implements
@Inject
public QSCarrierGroup(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
NetworkController networkController, ActivityStarter activityStarter,
- @Named(BG_HANDLER_NAME) Handler handler) {
+ @BgHandler Handler handler,
+ @MainLooper Looper looper) {
super(context, attrs);
mNetworkController = networkController;
mActivityStarter = activityStarter;
mBgHandler = handler;
+ mMainHandler = new H(looper, this::handleUpdateCarrierInfo, this::handleUpdateState);
+ mCallback = new Callback(mMainHandler);
+ }
+
+ @VisibleForTesting
+ protected CarrierTextController.CarrierTextCallback getCallback() {
+ return mCallback;
}
@VisibleForTesting
@@ -81,7 +97,8 @@ public class QSCarrierGroup extends LinearLayout implements
this(context, attrs,
Dependency.get(NetworkController.class),
Dependency.get(ActivityStarter.class),
- Dependency.get(BG_HANDLER));
+ Dependency.get(BG_HANDLER),
+ Dependency.get(MAIN_LOOPER));
}
@Override
@@ -136,14 +153,20 @@ public class QSCarrierGroup extends LinearLayout implements
if (mNetworkController.hasVoiceCallingFeature()) {
mNetworkController.addCallback(this);
}
- mCarrierTextController.setListening(this);
+ mCarrierTextController.setListening(mCallback);
} else {
mNetworkController.removeCallback(this);
mCarrierTextController.setListening(null);
}
}
+ @MainThread
private void handleUpdateState() {
+ if (!mMainHandler.getLooper().isCurrentThread()) {
+ mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
+ return;
+ }
+
for (int i = 0; i < SIM_SLOTS; i++) {
mCarrierGroups[i].updateState(mInfos[i]);
}
@@ -163,8 +186,13 @@ public class QSCarrierGroup extends LinearLayout implements
return SubscriptionManager.getSlotIndex(subscriptionId);
}
- @Override
- public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) {
+ @MainThread
+ private void handleUpdateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) {
+ if (!mMainHandler.getLooper().isCurrentThread()) {
+ mMainHandler.obtainMessage(H.MSG_UPDATE_CARRIER_INFO, info).sendToTarget();
+ return;
+ }
+
mNoSimTextView.setVisibility(View.GONE);
if (!info.airplaneMode && info.anySimReady) {
boolean[] slotSeen = new boolean[SIM_SLOTS];
@@ -207,7 +235,7 @@ public class QSCarrierGroup extends LinearLayout implements
mNoSimTextView.setText(info.carrierText);
mNoSimTextView.setVisibility(View.VISIBLE);
}
- handleUpdateState();
+ handleUpdateState(); // handleUpdateCarrierInfo is always called from main thread.
}
@Override
@@ -231,7 +259,7 @@ public class QSCarrierGroup extends LinearLayout implements
mInfos[slotIndex].contentDescription = statusIcon.contentDescription;
mInfos[slotIndex].typeContentDescription = typeContentDescription;
mInfos[slotIndex].roaming = roaming;
- handleUpdateState();
+ mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
}
@Override
@@ -241,7 +269,7 @@ public class QSCarrierGroup extends LinearLayout implements
mInfos[i].visible = false;
}
}
- handleUpdateState();
+ mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
}
static final class CellSignalState {
@@ -251,4 +279,47 @@ public class QSCarrierGroup extends LinearLayout implements
String typeContentDescription;
boolean roaming;
}
+
+ private static class H extends Handler {
+ private Consumer<CarrierTextController.CarrierTextCallbackInfo> mUpdateCarrierInfo;
+ private Runnable mUpdateState;
+ static final int MSG_UPDATE_CARRIER_INFO = 0;
+ static final int MSG_UPDATE_STATE = 1;
+
+ H(Looper looper,
+ Consumer<CarrierTextController.CarrierTextCallbackInfo> updateCarrierInfo,
+ Runnable updateState) {
+ super(looper);
+ mUpdateCarrierInfo = updateCarrierInfo;
+ mUpdateState = updateState;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_CARRIER_INFO:
+ mUpdateCarrierInfo.accept(
+ (CarrierTextController.CarrierTextCallbackInfo) msg.obj);
+ break;
+ case MSG_UPDATE_STATE:
+ mUpdateState.run();
+ break;
+ default:
+ super.handleMessage(msg);
+ }
+ }
+ }
+
+ private static class Callback implements CarrierTextController.CarrierTextCallback {
+ private H mMainHandler;
+
+ Callback(H handler) {
+ mMainHandler = handler;
+ }
+
+ @Override
+ public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) {
+ mMainHandler.obtainMessage(H.MSG_UPDATE_CARRIER_INFO, info).sendToTarget();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
new file mode 100644
index 000000000000..af418f6308a0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.Icon;
+import android.graphics.drawable.RippleDrawable;
+import android.media.MediaMetadata;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RemoteViews;
+import android.widget.TextView;
+
+import androidx.core.graphics.drawable.RoundedBitmapDrawable;
+import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
+
+import com.android.settingslib.media.MediaOutputSliceConstants;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.MediaTransferManager;
+
+/**
+ * Single media player for carousel in QSPanel
+ */
+public class QSMediaPlayer {
+
+ private static final String TAG = "QSMediaPlayer";
+
+ private Context mContext;
+ private LinearLayout mMediaNotifView;
+ private MediaSession.Token mToken;
+ private MediaController mController;
+ private int mWidth;
+ private int mHeight;
+
+ /**
+ *
+ * @param context
+ * @param parent
+ * @param width
+ * @param height
+ */
+ public QSMediaPlayer(Context context, ViewGroup parent, int width, int height) {
+ mContext = context;
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ mMediaNotifView = (LinearLayout) inflater.inflate(R.layout.qs_media_panel, parent, false);
+
+ mWidth = width;
+ mHeight = height;
+ }
+
+ public View getView() {
+ return mMediaNotifView;
+ }
+
+ /**
+ *
+ * @param token token for this media session
+ * @param icon app notification icon
+ * @param iconColor foreground color (for text, icons)
+ * @param bgColor background color
+ * @param actionsContainer a LinearLayout containing the media action buttons
+ * @param notif
+ */
+ public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor,
+ View actionsContainer, Notification notif) {
+ Log.d(TAG, "got media session: " + token);
+ mToken = token;
+ mController = new MediaController(mContext, token);
+ MediaMetadata mMediaMetadata = mController.getMetadata();
+ Notification.Builder builder = Notification.Builder.recoverBuilder(mContext, notif);
+
+ // Album art
+ addAlbumArtBackground(mMediaMetadata, bgColor, mWidth, mHeight);
+
+ // Reuse notification header instead of reimplementing everything
+ RemoteViews headerRemoteView = builder.makeNotificationHeader();
+ LinearLayout headerView = mMediaNotifView.findViewById(R.id.header);
+ View result = headerRemoteView.apply(mContext, headerView);
+ result.setPadding(0, 0, 0, 0);
+ LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, 75);
+ result.setLayoutParams(lp);
+ headerView.removeAllViews();
+ headerView.addView(result);
+
+ View seamless = headerView.findViewById(com.android.internal.R.id.media_seamless);
+ seamless.setVisibility(View.VISIBLE);
+
+ // App icon
+ ImageView appIcon = headerView.findViewById(com.android.internal.R.id.icon);
+ Drawable iconDrawable = icon.loadDrawable(mContext);
+ iconDrawable.setTint(iconColor);
+ appIcon.setImageDrawable(iconDrawable);
+
+ // App title
+ TextView appName = headerView.findViewById(com.android.internal.R.id.app_name_text);
+ String appNameString = builder.loadHeaderAppName();
+ appName.setText(appNameString);
+ appName.setTextColor(iconColor);
+
+ // Action
+ mMediaNotifView.setOnClickListener(v -> {
+ try {
+ notif.contentIntent.send();
+ // Also close shade
+ mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Pending intent was canceled");
+ e.printStackTrace();
+ }
+ });
+
+ // Separator
+ TextView separator = headerView.findViewById(com.android.internal.R.id.header_text_divider);
+ separator.setTextColor(iconColor);
+
+ // Album name
+ TextView albumName = headerView.findViewById(com.android.internal.R.id.header_text);
+ String albumString = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ALBUM);
+ if (!albumString.isEmpty()) {
+ albumName.setText(albumString);
+ albumName.setTextColor(iconColor);
+ albumName.setVisibility(View.VISIBLE);
+ separator.setVisibility(View.VISIBLE);
+ } else {
+ albumName.setVisibility(View.GONE);
+ separator.setVisibility(View.GONE);
+ }
+
+ // Transfer chip
+ MediaTransferManager mediaTransferManager = new MediaTransferManager(mContext);
+ View transferBackgroundView = headerView.findViewById(
+ com.android.internal.R.id.media_seamless);
+ LinearLayout viewLayout = (LinearLayout) transferBackgroundView;
+ RippleDrawable bkgDrawable = (RippleDrawable) viewLayout.getBackground();
+ GradientDrawable rect = (GradientDrawable) bkgDrawable.getDrawable(0);
+ rect.setStroke(2, iconColor);
+ rect.setColor(bgColor);
+ ImageView transferIcon = headerView.findViewById(
+ com.android.internal.R.id.media_seamless_image);
+ transferIcon.setBackgroundColor(bgColor);
+ transferIcon.setImageTintList(ColorStateList.valueOf(iconColor));
+ TextView transferText = headerView.findViewById(
+ com.android.internal.R.id.media_seamless_text);
+ transferText.setTextColor(iconColor);
+
+ ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
+ transferBackgroundView.setOnClickListener(v -> {
+ final Intent intent = new Intent()
+ .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT);
+ mActivityStarter.startActivity(intent, false, true /* dismissShade */,
+ Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ });
+
+ // Artist name
+ TextView artistText = mMediaNotifView.findViewById(R.id.header_title);
+ String artistName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
+ artistText.setText(artistName);
+ artistText.setTextColor(iconColor);
+
+ // Song name
+ TextView titleText = mMediaNotifView.findViewById(R.id.header_text);
+ String songName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
+ titleText.setText(songName);
+ titleText.setTextColor(iconColor);
+
+ // Media controls
+ LinearLayout parentActionsLayout = (LinearLayout) actionsContainer;
+ final int[] actionIds = {
+ R.id.action0,
+ R.id.action1,
+ R.id.action2,
+ R.id.action3,
+ R.id.action4
+ };
+ final int[] notifActionIds = {
+ com.android.internal.R.id.action0,
+ com.android.internal.R.id.action1,
+ com.android.internal.R.id.action2,
+ com.android.internal.R.id.action3,
+ com.android.internal.R.id.action4
+ };
+ for (int i = 0; i < parentActionsLayout.getChildCount() && i < actionIds.length; i++) {
+ ImageButton thisBtn = mMediaNotifView.findViewById(actionIds[i]);
+ ImageButton thatBtn = parentActionsLayout.findViewById(notifActionIds[i]);
+ if (thatBtn == null || thatBtn.getDrawable() == null) {
+ thisBtn.setVisibility(View.GONE);
+ continue;
+ }
+
+ Drawable thatIcon = thatBtn.getDrawable();
+ thisBtn.setImageDrawable(thatIcon.mutate());
+ thisBtn.setVisibility(View.VISIBLE);
+ thisBtn.setOnClickListener(v -> {
+ Log.d(TAG, "clicking on other button");
+ thatBtn.performClick();
+ });
+ }
+ }
+
+ public MediaSession.Token getMediaSessionToken() {
+ return mToken;
+ }
+
+ public String getMediaPlayerPackage() {
+ return mController.getPackageName();
+ }
+
+ /**
+ * Check whether the media controlled by this player is currently playing
+ * @return whether it is playing, or false if no controller information
+ */
+ public boolean isPlaying() {
+ if (mController == null) {
+ return false;
+ }
+
+ PlaybackState state = mController.getPlaybackState();
+ if (state == null) {
+ return false;
+ }
+
+ return (state.getState() == PlaybackState.STATE_PLAYING);
+ }
+
+ private void addAlbumArtBackground(MediaMetadata metadata, int bgColor, int width, int height) {
+ Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
+ if (albumArt != null) {
+
+ Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true);
+ Bitmap scaled = scaleBitmap(original, width, height);
+ Canvas canvas = new Canvas(scaled);
+
+ // Add translucent layer over album art to improve contrast
+ Paint p = new Paint();
+ p.setStyle(Paint.Style.FILL);
+ p.setColor(bgColor);
+ p.setAlpha(200);
+ canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), p);
+
+ RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create(
+ mContext.getResources(), scaled);
+ roundedDrawable.setCornerRadius(20);
+
+ mMediaNotifView.setBackground(roundedDrawable);
+ } else {
+ Log.e(TAG, "No album art available");
+ }
+ }
+
+ private Bitmap scaleBitmap(Bitmap original, int width, int height) {
+ Bitmap cropped = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(cropped);
+
+ float scale = (float) cropped.getWidth() / (float) original.getWidth();
+ float dy = (cropped.getHeight() - original.getHeight() * scale) / 2.0f;
+ Matrix transformation = new Matrix();
+ transformation.postTranslate(0, dy);
+ transformation.preScale(scale, scale);
+
+ Paint paint = new Paint();
+ paint.setFilterBitmap(true);
+ canvas.drawBitmap(original, transformation, paint);
+
+ return cropped;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 00b57da846fe..4c813482f58e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -24,14 +24,22 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.drawable.Icon;
+import android.media.session.MediaSession;
import android.metrics.LogMaker;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
import android.service.quicksettings.Tile;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import com.android.internal.logging.MetricsLogger;
@@ -49,6 +57,8 @@ import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.settings.BrightnessController;
import com.android.systemui.settings.ToggleSliderView;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.phone.NPVPluginManager;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener;
import com.android.systemui.tuner.TunerService;
@@ -78,6 +88,9 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private final QSTileRevealController mQsTileRevealController;
+ private final LinearLayout mMediaCarousel;
+ private final ArrayList<QSMediaPlayer> mMediaPlayers = new ArrayList<>();
+
protected boolean mExpanded;
protected boolean mListening;
@@ -98,6 +111,10 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
private BrightnessMirrorController mBrightnessMirrorController;
private View mDivider;
+ private FrameLayout mPluginFrame;
+ private final PluginManager mPluginManager;
+ private NPVPluginManager mNPVPluginManager;
+
public QSPanel(Context context) {
this(context, null);
}
@@ -106,9 +123,13 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
this(context, attrs, null);
}
+ public QSPanel(Context context, AttributeSet attrs, DumpController dumpController) {
+ this(context, attrs, dumpController, null);
+ }
+
@Inject
public QSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
- DumpController dumpController) {
+ DumpController dumpController, PluginManager pluginManager) {
super(context, attrs);
mContext = context;
@@ -128,6 +149,27 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
addDivider();
+ // Add media carousel
+ int flag = Settings.System.getInt(context.getContentResolver(), "qs_media_player", 0);
+ if (flag == 1) {
+ HorizontalScrollView mediaScrollView = new HorizontalScrollView(mContext);
+ mediaScrollView.setHorizontalScrollBarEnabled(false);
+ int playerHeight = (int) getResources().getDimension(R.dimen.qs_media_height);
+ int padding = (int) getResources().getDimension(R.dimen.qs_media_padding);
+ LayoutParams lpView = new LayoutParams(LayoutParams.MATCH_PARENT, playerHeight);
+ lpView.setMarginStart(padding);
+ lpView.setMarginEnd(padding);
+ addView(mediaScrollView, lpView);
+
+ LayoutParams lpCarousel = new LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.WRAP_CONTENT);
+ mMediaCarousel = new LinearLayout(mContext);
+ mMediaCarousel.setOrientation(LinearLayout.HORIZONTAL);
+ mediaScrollView.addView(mMediaCarousel, lpCarousel);
+ } else {
+ mMediaCarousel = null;
+ }
+
mFooter = new QSSecurityFooter(this, context);
addView(mFooter.getView());
@@ -136,6 +178,82 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
mDumpController = dumpController;
updateResources();
+
+ mPluginManager = pluginManager;
+ if (mPluginManager != null && Settings.System.getInt(
+ mContext.getContentResolver(), "npv_plugin_flag", 0) == 2) {
+ mPluginFrame = (FrameLayout) LayoutInflater.from(mContext).inflate(
+ R.layout.status_bar_expanded_plugin_frame, this, false);
+ addView(mPluginFrame);
+ mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager);
+ }
+
+ }
+
+ /**
+ * Add or update a player for the associated media session
+ * @param token
+ * @param icon
+ * @param iconColor
+ * @param bgColor
+ * @param actionsContainer
+ * @param notif
+ */
+ public void addMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor,
+ View actionsContainer, StatusBarNotification notif) {
+ int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0);
+ if (flag != 1) {
+ // Shouldn't happen, but just in case
+ Log.e(TAG, "Tried to add media session without player!");
+ return;
+ }
+ QSMediaPlayer player = null;
+ String packageName = notif.getPackageName();
+ for (QSMediaPlayer p : mMediaPlayers) {
+ if (p.getMediaSessionToken().equals(token)) {
+ Log.d(TAG, "a player for this session already exists");
+ player = p;
+ break;
+ }
+
+ if (packageName.equals(p.getMediaPlayerPackage())) {
+ Log.d(TAG, "found an old session for this app");
+ player = p;
+ break;
+ }
+ }
+
+ int playerHeight = (int) getResources().getDimension(R.dimen.qs_media_height);
+ int playerWidth = (int) getResources().getDimension(R.dimen.qs_media_width);
+ int padding = (int) getResources().getDimension(R.dimen.qs_media_padding);
+ LayoutParams lp = new LayoutParams(playerWidth, ViewGroup.LayoutParams.MATCH_PARENT);
+ lp.setMarginStart(padding);
+ lp.setMarginEnd(padding);
+
+ if (player == null) {
+ Log.d(TAG, "creating new player");
+
+ player = new QSMediaPlayer(mContext, this, playerWidth, playerHeight);
+
+ if (player.isPlaying()) {
+ mMediaCarousel.addView(player.getView(), 0, lp); // add in front
+ } else {
+ mMediaCarousel.addView(player.getView(), lp); // add at end
+ }
+ } else if (player.isPlaying()) {
+ // move it to the front
+ mMediaCarousel.removeView(player.getView());
+ mMediaCarousel.addView(player.getView(), 0, lp);
+ }
+
+ Log.d(TAG, "setting player session");
+ player.setMediaSession(token, icon, iconColor, bgColor, actionsContainer,
+ notif.getNotification());
+ mMediaPlayers.add(player);
+ }
+
+ protected View getMediaPanel() {
+ return mMediaCarousel;
}
protected void addDivider() {
@@ -380,6 +498,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
if (mListening) {
refreshAllTiles();
}
+ if (mNPVPluginManager != null) mNPVPluginManager.setListening(listening);
}
public void setListening(boolean listening, boolean expanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 1e763cf79240..b395c3c336d3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -30,11 +30,12 @@ import android.service.quicksettings.Tile;
import android.text.TextUtils;
import android.util.Log;
-import com.android.systemui.Dependency;
import com.android.systemui.DumpController;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.dagger.qualifiers.BgLooper;
+import com.android.systemui.dagger.qualifiers.MainHandler;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSTile;
@@ -61,7 +62,6 @@ import java.util.List;
import java.util.function.Predicate;
import javax.inject.Inject;
-import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton;
@@ -94,8 +94,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
public QSTileHost(Context context,
StatusBarIconController iconController,
QSFactoryImpl defaultFactory,
- @Named(Dependency.MAIN_HANDLER_NAME) Handler mainHandler,
- @Named(Dependency.BG_LOOPER_NAME) Looper bgLooper,
+ @MainHandler Handler mainHandler,
+ @BgLooper Looper bgLooper,
PluginManager pluginManager,
TunerService tunerService,
Provider<AutoTileManager> autoTiles,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
new file mode 100644
index 000000000000..ae66cd576765
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.media.MediaMetadata;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.core.graphics.drawable.RoundedBitmapDrawable;
+import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
+
+import com.android.systemui.R;
+
+/**
+ * QQS mini media player
+ */
+public class QuickQSMediaPlayer {
+
+ private static final String TAG = "QQSMediaPlayer";
+
+ private Context mContext;
+ private LinearLayout mMediaNotifView;
+ private MediaSession.Token mToken;
+ private MediaController mController;
+
+ /**
+ *
+ * @param context
+ * @param parent
+ */
+ public QuickQSMediaPlayer(Context context, ViewGroup parent) {
+ mContext = context;
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ mMediaNotifView = (LinearLayout) inflater.inflate(R.layout.qqs_media_panel, parent, false);
+ }
+
+ public View getView() {
+ return mMediaNotifView;
+ }
+
+ /**
+ *
+ * @param token token for this media session
+ * @param icon app notification icon
+ * @param iconColor foreground color (for text, icons)
+ * @param bgColor background color
+ * @param actionsContainer a LinearLayout containing the media action buttons
+ */
+ public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor,
+ View actionsContainer) {
+ Log.d(TAG, "Setting media session: " + token);
+ mToken = token;
+ mController = new MediaController(mContext, token);
+ MediaMetadata mMediaMetadata = mController.getMetadata();
+
+ // Album art
+ addAlbumArtBackground(mMediaMetadata, bgColor);
+
+ // App icon
+ ImageView appIcon = mMediaNotifView.findViewById(R.id.icon);
+ Drawable iconDrawable = icon.loadDrawable(mContext);
+ iconDrawable.setTint(iconColor);
+ appIcon.setImageDrawable(iconDrawable);
+
+ // Artist name
+ TextView appText = mMediaNotifView.findViewById(R.id.header_title);
+ String artistName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
+ appText.setText(artistName);
+ appText.setTextColor(iconColor);
+
+ // Song name
+ TextView titleText = mMediaNotifView.findViewById(R.id.header_text);
+ String songName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
+ titleText.setText(songName);
+ titleText.setTextColor(iconColor);
+
+ // Action buttons
+ LinearLayout parentActionsLayout = (LinearLayout) actionsContainer;
+ final int[] actionIds = {R.id.action0, R.id.action1, R.id.action2};
+
+ // TODO some apps choose different buttons to show in compact mode
+ final int[] notifActionIds = {
+ com.android.internal.R.id.action1,
+ com.android.internal.R.id.action2,
+ com.android.internal.R.id.action3
+ };
+ for (int i = 0; i < parentActionsLayout.getChildCount() && i < actionIds.length; i++) {
+ ImageButton thisBtn = mMediaNotifView.findViewById(actionIds[i]);
+ ImageButton thatBtn = parentActionsLayout.findViewById(notifActionIds[i]);
+ if (thatBtn == null || thatBtn.getDrawable() == null) {
+ thisBtn.setVisibility(View.GONE);
+ continue;
+ }
+
+ Drawable thatIcon = thatBtn.getDrawable();
+ thisBtn.setImageDrawable(thatIcon.mutate());
+ thisBtn.setVisibility(View.VISIBLE);
+
+ thisBtn.setOnClickListener(v -> {
+ Log.d(TAG, "clicking on other button");
+ thatBtn.performClick();
+ });
+ }
+ }
+
+ public MediaSession.Token getMediaSessionToken() {
+ return mToken;
+ }
+
+ /**
+ * Check whether the media controlled by this player is currently playing
+ * @return whether it is playing, or false if no controller information
+ */
+ public boolean isPlaying() {
+ if (mController == null) {
+ return false;
+ }
+
+ PlaybackState state = mController.getPlaybackState();
+ if (state == null) {
+ return false;
+ }
+
+ return (state.getState() == PlaybackState.STATE_PLAYING);
+ }
+
+ private void addAlbumArtBackground(MediaMetadata metadata, int bgColor) {
+ Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
+ if (albumArt != null) {
+ Rect bounds = new Rect();
+ mMediaNotifView.getBoundsOnScreen(bounds);
+ int width = bounds.width();
+ int height = bounds.height();
+
+ Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true);
+ Bitmap scaled = scaleBitmap(original, width, height);
+ Canvas canvas = new Canvas(scaled);
+
+ // Add translucent layer over album art to improve contrast
+ Paint p = new Paint();
+ p.setStyle(Paint.Style.FILL);
+ p.setColor(bgColor);
+ p.setAlpha(200);
+ canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), p);
+
+ RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create(
+ mContext.getResources(), scaled);
+ roundedDrawable.setCornerRadius(20);
+
+ mMediaNotifView.setBackground(roundedDrawable);
+ } else {
+ Log.e(TAG, "No album art available");
+ }
+ }
+
+ private Bitmap scaleBitmap(Bitmap original, int width, int height) {
+ Bitmap cropped = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(cropped);
+
+ float scale = (float) cropped.getWidth() / (float) original.getWidth();
+ float dy = (cropped.getHeight() - original.getHeight() * scale) / 2.0f;
+ Matrix transformation = new Matrix();
+ transformation.postTranslate(0, dy);
+ transformation.preScale(scale, scale);
+
+ Paint paint = new Paint();
+ paint.setFilterBitmap(true);
+ canvas.drawBitmap(original, transformation, paint);
+
+ return cropped;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 85aafa06961a..dcd4633a79d2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -21,6 +21,7 @@ import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEX
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
@@ -55,6 +56,7 @@ public class QuickQSPanel extends QSPanel {
private boolean mDisabledByPolicy;
private int mMaxTiles;
protected QSPanel mFullPanel;
+ private QuickQSMediaPlayer mMediaPlayer;
@Inject
public QuickQSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
@@ -69,11 +71,43 @@ public class QuickQSPanel extends QSPanel {
}
removeView((View) mTileLayout);
}
- sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
- mTileLayout = new HeaderTileLayout(context);
- mTileLayout.setListening(mListening);
- addView((View) mTileLayout, 0 /* Between brightness and footer */);
- super.setPadding(0, 0, 0, 0);
+
+ int flag = Settings.System.getInt(context.getContentResolver(), "qs_media_player", 0);
+ if (flag == 1) {
+ LinearLayout mHorizontalLinearLayout = new LinearLayout(mContext);
+ mHorizontalLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
+ mHorizontalLinearLayout.setClipChildren(false);
+ mHorizontalLinearLayout.setClipToPadding(false);
+
+ LayoutParams lp = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1);
+
+ mTileLayout = new DoubleLineTileLayout(context);
+ lp.setMarginEnd(10);
+ lp.setMarginStart(0);
+ mHorizontalLinearLayout.addView((View) mTileLayout, lp);
+
+ mMediaPlayer = new QuickQSMediaPlayer(mContext, mHorizontalLinearLayout);
+
+ lp.setMarginEnd(0);
+ lp.setMarginStart(10);
+ mHorizontalLinearLayout.addView(mMediaPlayer.getView(), lp);
+
+ sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
+
+ mTileLayout.setListening(mListening);
+ addView(mHorizontalLinearLayout, 0 /* Between brightness and footer */);
+ super.setPadding(0, 0, 0, 0);
+ } else {
+ sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
+ mTileLayout = new HeaderTileLayout(context);
+ mTileLayout.setListening(mListening);
+ addView((View) mTileLayout, 0 /* Between brightness and footer */);
+ super.setPadding(0, 0, 0, 0);
+ }
+ }
+
+ public QuickQSMediaPlayer getMediaPlayer() {
+ return mMediaPlayer;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index d20b22815805..592e3881ea97 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -257,10 +257,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements
mNextAlarmTextView.setSelected(true);
mPermissionsHubEnabled = PrivacyItemControllerKt.isPermissionsHubEnabled();
- // Change the ignored slots when DeviceConfig flag changes
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_PRIVACY,
- mContext.getMainExecutor(), mPropertiesListener);
-
}
private List<String> getIgnoredIconSlots() {
@@ -396,9 +392,15 @@ public class QuickStatusBarHeader extends RelativeLayout implements
mSystemIconsView.setLayoutParams(mSystemIconsView.getLayoutParams());
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
+
+ int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0);
if (mQsDisabled) {
lp.height = resources.getDimensionPixelSize(
com.android.internal.R.dimen.quick_qs_offset_height);
+ } else if (flag == 1) {
+ lp.height = Math.max(getMinimumHeight(),
+ resources.getDimensionPixelSize(
+ com.android.internal.R.dimen.quick_qs_total_height_with_media));
} else {
lp.height = Math.max(getMinimumHeight(),
resources.getDimensionPixelSize(
@@ -489,6 +491,9 @@ public class QuickStatusBarHeader extends RelativeLayout implements
super.onAttachedToWindow();
mStatusBarIconController.addIconGroup(mIconManager);
requestApplyInsets();
+ // Change the ignored slots when DeviceConfig flag changes
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_PRIVACY,
+ mContext.getMainExecutor(), mPropertiesListener);
}
@Override
@@ -527,6 +532,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
public void onDetachedFromWindow() {
setListening(false);
mStatusBarIconController.removeIconGroup(mIconManager);
+ DeviceConfig.removeOnPropertiesChangedListener(mPropertiesListener);
super.onDetachedFromWindow();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 14addb99c0c5..37743ec55517 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -2,6 +2,7 @@ package com.android.systemui.qs;
import android.content.Context;
import android.content.res.Resources;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
@@ -31,6 +32,9 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
private boolean mListening;
protected int mMaxAllowedRows = 3;
+ // Prototyping with less rows
+ private final boolean mLessRows;
+
public TileLayout(Context context) {
this(context, null);
}
@@ -38,7 +42,9 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
public TileLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setFocusableInTouchMode(true);
+ mLessRows = Settings.System.getInt(context.getContentResolver(), "qs_less_rows", 0) != 0;
updateResources();
+
}
@Override
@@ -89,6 +95,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
mSidePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_layout_margin_side);
mMaxAllowedRows = Math.max(1, getResources().getInteger(R.integer.quick_settings_max_rows));
+ if (mLessRows) mMaxAllowedRows = Math.max(1, mMaxAllowedRows - 1);
if (mColumns != columns) {
mColumns = columns;
requestLayout();
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 38153ecd59c9..7fb520766977 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -46,8 +46,8 @@ import com.android.systemui.qs.QSDetailClipper;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
-import com.android.systemui.statusbar.policy.KeyguardMonitor.Callback;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.KeyguardStateController.Callback;
import java.util.ArrayList;
import java.util.List;
@@ -68,7 +68,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
private final QSDetailClipper mClipper;
private final LightBarController mLightBarController;
- private KeyguardMonitor mKeyguardMonitor;
+ private KeyguardStateController mKeyguardStateController;
private final ScreenLifecycle mScreenLifecycle;
private final TileQueryHelper mTileQueryHelper;
private final View mTransparentView;
@@ -89,7 +89,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
@Inject
public QSCustomizer(Context context, AttributeSet attrs,
LightBarController lightBarController,
- KeyguardMonitor keyguardMonitor,
+ KeyguardStateController keyguardStateController,
ScreenLifecycle screenLifecycle) {
super(new ContextThemeWrapper(context, R.style.edit_theme), attrs);
@@ -124,7 +124,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
animator.setMoveDuration(TileAdapter.MOVE_DURATION);
mRecyclerView.setItemAnimator(animator);
mLightBarController = lightBarController;
- mKeyguardMonitor = keyguardMonitor;
+ mKeyguardStateController = keyguardStateController;
mScreenLifecycle = screenLifecycle;
updateNavBackDrop(getResources().getConfiguration());
}
@@ -187,7 +187,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
queryTiles();
mNotifQsContainer.setCustomizerAnimating(true);
mNotifQsContainer.setCustomizerShowing(true);
- mKeyguardMonitor.addCallback(mKeyguardCallback);
+ mKeyguardStateController.addCallback(mKeyguardCallback);
updateNavColors();
}
}
@@ -203,7 +203,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
queryTiles();
mNotifQsContainer.setCustomizerAnimating(false);
mNotifQsContainer.setCustomizerShowing(true);
- mKeyguardMonitor.addCallback(mKeyguardCallback);
+ mKeyguardStateController.addCallback(mKeyguardCallback);
updateNavColors();
}
}
@@ -227,7 +227,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
}
mNotifQsContainer.setCustomizerAnimating(animate);
mNotifQsContainer.setCustomizerShowing(false);
- mKeyguardMonitor.removeCallback(mKeyguardCallback);
+ mKeyguardStateController.removeCallback(mKeyguardCallback);
updateNavColors();
}
}
@@ -283,7 +283,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
public void saveInstanceState(Bundle outState) {
if (isShown) {
- mKeyguardMonitor.removeCallback(mKeyguardCallback);
+ mKeyguardStateController.removeCallback(mKeyguardCallback);
}
outState.putBoolean(EXTRA_QS_CUSTOMIZING, mCustomizing);
}
@@ -315,7 +315,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
@Override
public void onKeyguardShowingChanged() {
if (!isAttachedToWindow()) return;
- if (mKeyguardMonitor.isShowing() && !mOpening) {
+ if (mKeyguardStateController.isShowing() && !mOpening) {
hide();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 2542abdbef72..bd3297b3d39c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -277,20 +277,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
selectPosition(holder.getAdapterPosition(), v);
}
});
- if (mNeedsFocus) {
- // Wait for this to get laid out then set its focus.
- // Ensure that tile gets laid out so we get the callback.
- holder.mTileView.requestLayout();
- holder.mTileView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom,
- int oldLeft, int oldTop, int oldRight, int oldBottom) {
- holder.mTileView.removeOnLayoutChangeListener(this);
- holder.mTileView.requestFocus();
- }
- });
- mNeedsFocus = false;
- }
+ focusOnHolder(holder);
return;
}
@@ -330,16 +317,38 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
} else {
if (position < mEditIndex && canRemoveTiles()) {
showAccessibilityDialog(position, v);
+ } else if (position < mEditIndex && !canRemoveTiles()) {
+ startAccessibleMove(position);
} else {
startAccessibleAdd(position);
}
}
}
});
+ if (position == mAccessibilityFromIndex) {
+ focusOnHolder(holder);
+ }
}
}
}
+ private void focusOnHolder(Holder holder) {
+ if (mNeedsFocus) {
+ // Wait for this to get laid out then set its focus.
+ // Ensure that tile gets laid out so we get the callback.
+ holder.mTileView.requestLayout();
+ holder.mTileView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ holder.mTileView.removeOnLayoutChangeListener(this);
+ holder.mTileView.requestFocus();
+ }
+ });
+ mNeedsFocus = false;
+ }
+ }
+
private boolean canRemoveTiles() {
return mCurrentSpecs.size() > mMinNumTiles;
}
@@ -396,6 +405,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
mAccessibilityFromIndex = position;
mAccessibilityFromLabel = mTiles.get(position).state.label;
mAccessibilityAction = ACTION_MOVE;
+ mNeedsFocus = true;
notifyDataSetChanged();
}
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 466c8082f0b9..411980b399bd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -39,6 +39,7 @@ import android.text.format.DateUtils;
import android.util.Log;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
+import android.widget.Switch;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
@@ -82,6 +83,11 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
mTile = new Tile();
updateDefaultTileAndIcon();
mServiceManager = host.getTileServices().getTileWrapper(this);
+ if (mServiceManager.isBooleanTile()) {
+ // Replace states with BooleanState
+ resetStates();
+ }
+
mService = mServiceManager.getTileService();
mServiceManager.setTileChangeListener(this);
mUser = ActivityManager.getCurrentUser();
@@ -246,8 +252,10 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
@Override
public State newTileState() {
- State state = new State();
- return state;
+ if (mServiceManager != null && mServiceManager.isBooleanTile()) {
+ return new BooleanState();
+ }
+ return new State();
}
@Override
@@ -336,6 +344,12 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
} else {
state.contentDescription = state.label;
}
+
+ if (state instanceof BooleanState) {
+ state.expandedAccessibilityClassName = Switch.class.getName();
+ ((BooleanState) state).value = (state.state == Tile.STATE_ACTIVE);
+ }
+
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index effea6a877b8..f59e0c2d9bc2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -131,6 +131,24 @@ public class TileLifecycleManager extends BroadcastReceiver implements
}
/**
+ * Determines whether the associated TileService is a Boolean Tile.
+ *
+ * @return true if {@link TileService#META_DATA_BOOLEAN_TILE} is set to {@code true} for this
+ * tile
+ * @see TileService#META_DATA_BOOLEAN_TILE
+ */
+ public boolean isBooleanTile() {
+ try {
+ ServiceInfo info = mPackageManagerAdapter.getServiceInfo(mIntent.getComponent(),
+ PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_META_DATA);
+ return info.metaData != null
+ && info.metaData.getBoolean(TileService.META_DATA_BOOLEAN_TILE, false);
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ /**
* Binds just long enough to send any queued messages, then unbinds.
*/
public void flushMessagesAndUnbind() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
index 2a7e55fe6f8f..0b4e6485551c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -123,6 +123,10 @@ public class TileServiceManager {
return mStateManager.isActiveTile();
}
+ public boolean isBooleanTile() {
+ return mStateManager.isBooleanTile();
+ }
+
public void setShowingDialog(boolean dialog) {
mShowingDialog = dialog;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index 23f36e94abae..13cfa78b7c1e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -39,7 +39,7 @@ import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.Dependency;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.util.ArrayList;
import java.util.Collections;
@@ -296,14 +296,16 @@ public class TileServices extends IQSService.Stub {
@Override
public boolean isLocked() {
- KeyguardMonitor keyguardMonitor = Dependency.get(KeyguardMonitor.class);
- return keyguardMonitor.isShowing();
+ KeyguardStateController keyguardStateController =
+ Dependency.get(KeyguardStateController.class);
+ return keyguardStateController.isShowing();
}
@Override
public boolean isSecure() {
- KeyguardMonitor keyguardMonitor = Dependency.get(KeyguardMonitor.class);
- return keyguardMonitor.isSecure() && keyguardMonitor.isShowing();
+ KeyguardStateController keyguardStateController =
+ Dependency.get(KeyguardStateController.class);
+ return keyguardStateController.isMethodSecure() && keyguardStateController.isShowing();
}
private CustomTile getTileForToken(IBinder token) {
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 daaee4cd5336..1c8e451e52f7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -16,6 +16,7 @@ package com.android.systemui.qs.tileimpl;
import android.content.Context;
import android.os.Build;
+import android.provider.Settings;
import android.util.Log;
import android.view.ContextThemeWrapper;
@@ -32,6 +33,7 @@ import com.android.systemui.qs.tiles.BluetoothTile;
import com.android.systemui.qs.tiles.CastTile;
import com.android.systemui.qs.tiles.CellularTile;
import com.android.systemui.qs.tiles.ColorInversionTile;
+import com.android.systemui.qs.tiles.ControlsTile;
import com.android.systemui.qs.tiles.DataSaverTile;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.FlashlightTile;
@@ -58,6 +60,7 @@ public class QSFactoryImpl implements QSFactory {
private final Provider<WifiTile> mWifiTileProvider;
private final Provider<BluetoothTile> mBluetoothTileProvider;
+ private final Provider<ControlsTile> mControlsTileProvider;
private final Provider<CellularTile> mCellularTileProvider;
private final Provider<DndTile> mDndTileProvider;
private final Provider<ColorInversionTile> mColorInversionTileProvider;
@@ -81,6 +84,7 @@ public class QSFactoryImpl implements QSFactory {
@Inject
public QSFactoryImpl(Provider<WifiTile> wifiTileProvider,
Provider<BluetoothTile> bluetoothTileProvider,
+ Provider<ControlsTile> controlsTileProvider,
Provider<CellularTile> cellularTileProvider,
Provider<DndTile> dndTileProvider,
Provider<ColorInversionTile> colorInversionTileProvider,
@@ -100,6 +104,7 @@ public class QSFactoryImpl implements QSFactory {
Provider<UiModeNightTile> uiModeNightTileProvider) {
mWifiTileProvider = wifiTileProvider;
mBluetoothTileProvider = bluetoothTileProvider;
+ mControlsTileProvider = controlsTileProvider;
mCellularTileProvider = cellularTileProvider;
mDndTileProvider = dndTileProvider;
mColorInversionTileProvider = colorInversionTileProvider;
@@ -138,6 +143,11 @@ public class QSFactoryImpl implements QSFactory {
return mWifiTileProvider.get();
case "bt":
return mBluetoothTileProvider.get();
+ case "controls":
+ if (Settings.System.getInt(mHost.getContext().getContentResolver(),
+ "qs_controls_tile_enabled", 0) == 1) {
+ return mControlsTileProvider.get();
+ } else return null;
case "cell":
return mCellularTileProvider.get();
case "dnd":
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 681de378ff57..e0f26cd1a267 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -139,6 +139,11 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
mQSSettingsPanelOption = QSSettingsControllerKt.getQSSettingsPanelOption();
}
+ protected final void resetStates() {
+ mState = newTileState();
+ mTmpState = newTileState();
+ }
+
@NonNull
@Override
public Lifecycle getLifecycle() {
@@ -629,6 +634,11 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
}
}
+ /**
+ * Dumps the state of this tile along with its name.
+ *
+ * This may be used for CTS testing of tiles.
+ */
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(this.getClass().getSimpleName() + ":");
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 2f1e01421ea5..d4e9fdff1b24 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -46,7 +46,7 @@ import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.NetworkController;
import java.util.ArrayList;
@@ -62,7 +62,7 @@ public class CastTile extends QSTileImpl<BooleanState> {
private final CastController mController;
private final CastDetailAdapter mDetailAdapter;
- private final KeyguardMonitor mKeyguard;
+ private final KeyguardStateController mKeyguard;
private final NetworkController mNetworkController;
private final Callback mCallback = new Callback();
private final ActivityStarter mActivityStarter;
@@ -71,12 +71,13 @@ public class CastTile extends QSTileImpl<BooleanState> {
private static final String WFD_ENABLE = "persist.debug.wfd.enable";
@Inject
- public CastTile(QSHost host, CastController castController, KeyguardMonitor keyguardMonitor,
- NetworkController networkController, ActivityStarter activityStarter) {
+ public CastTile(QSHost host, CastController castController,
+ KeyguardStateController keyguardStateController, NetworkController networkController,
+ ActivityStarter activityStarter) {
super(host);
mController = castController;
mDetailAdapter = new CastDetailAdapter();
- mKeyguard = keyguardMonitor;
+ mKeyguard = keyguardStateController;
mNetworkController = networkController;
mActivityStarter = activityStarter;
mController.observe(this, mCallback);
@@ -266,7 +267,8 @@ public class CastTile extends QSTileImpl<BooleanState> {
}
};
- private final class Callback implements CastController.Callback, KeyguardMonitor.Callback {
+ private final class Callback implements CastController.Callback,
+ KeyguardStateController.Callback {
@Override
public void onCastDevicesChanged() {
refreshState();
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 2967cf5ea427..e32fc32d4aa0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -98,6 +98,9 @@ public class CellularTile extends QSTileImpl<SignalState> {
@Override
public Intent getLongClickIntent() {
+ if (getState().state == Tile.STATE_UNAVAILABLE) {
+ return new Intent(Settings.ACTION_WIRELESS_SETTINGS);
+ }
return getCellularSettingIntent();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ControlsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ControlsTile.java
new file mode 100644
index 000000000000..0a59618b59a1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ControlsTile.java
@@ -0,0 +1,197 @@
+/*
+ * 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 com.android.systemui.qs.tiles;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.HomeControlsPlugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.qs.DetailAdapter;
+import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.shared.plugins.PluginManager;
+
+import javax.inject.Inject;
+
+
+/**
+ * Temporary control test for prototyping
+ */
+public class ControlsTile extends QSTileImpl<BooleanState> {
+ private ControlsDetailAdapter mDetailAdapter;
+ private final ActivityStarter mActivityStarter;
+ private PluginManager mPluginManager;
+ private HomeControlsPlugin mPlugin;
+ private Intent mHomeAppIntent;
+
+ @Inject
+ public ControlsTile(QSHost host,
+ ActivityStarter activityStarter,
+ PluginManager pluginManager) {
+ super(host);
+ mActivityStarter = activityStarter;
+ mPluginManager = pluginManager;
+ mDetailAdapter = (ControlsDetailAdapter) createDetailAdapter();
+
+ mHomeAppIntent = new Intent(Intent.ACTION_VIEW);
+ mHomeAppIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mHomeAppIntent.setComponent(new ComponentName("com.google.android.apps.chromecast.app",
+ "com.google.android.apps.chromecast.app.DiscoveryActivity"));
+ }
+
+ @Override
+ public DetailAdapter getDetailAdapter() {
+ return mDetailAdapter;
+ }
+
+ @Override
+ public BooleanState newTileState() {
+ return new BooleanState();
+ }
+
+ @Override
+ public void handleSetListening(boolean listening) {
+
+ }
+
+ @Override
+ public void setDetailListening(boolean listening) {
+ if (mPlugin == null) return;
+
+ mPlugin.setVisible(listening);
+ }
+
+ @Override
+ protected void handleClick() {
+ showDetail(true);
+ }
+
+ @Override
+ public Intent getLongClickIntent() {
+ return mHomeAppIntent;
+ }
+
+ @Override
+ protected void handleSecondaryClick() {
+ showDetail(true);
+ }
+
+ @Override
+ public CharSequence getTileLabel() {
+ return "Controls";
+ }
+
+ @Override
+ protected void handleUpdateState(BooleanState state, Object arg) {
+ state.icon = ResourceIcon.get(R.drawable.ic_lightbulb_outline_gm2_24px);
+ state.label = "Controls";
+ }
+
+ @Override
+ public boolean supportsDetailView() {
+ return getDetailAdapter() != null && mQSSettingsPanelOption == QSSettingsPanel.OPEN_CLICK;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return -1;
+ }
+
+ @Override
+ protected String composeChangeAnnouncement() {
+ if (mState.value) {
+ return "On";
+ } else {
+ return "Off";
+ }
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ protected DetailAdapter createDetailAdapter() {
+ mDetailAdapter = new ControlsDetailAdapter();
+ return mDetailAdapter;
+ }
+
+ private class ControlsDetailAdapter implements DetailAdapter {
+ private View mDetailView;
+ protected LinearLayout mHomeControlsLayout;
+
+ public CharSequence getTitle() {
+ return "Controls";
+ }
+
+ public Boolean getToggleState() {
+ return null;
+ }
+
+ public boolean getToggleEnabled() {
+ return false;
+ }
+
+ public View createDetailView(Context context, View convertView, final ViewGroup parent) {
+ mHomeControlsLayout = (LinearLayout) LayoutInflater.from(context).inflate(
+ R.layout.home_controls, parent, false);
+ mHomeControlsLayout.setVisibility(View.VISIBLE);
+ mPluginManager.addPluginListener(
+ new PluginListener<HomeControlsPlugin>() {
+ @Override
+ public void onPluginConnected(HomeControlsPlugin plugin,
+ Context pluginContext) {
+ mPlugin = plugin;
+ mPlugin.sendParentGroup(mHomeControlsLayout);
+ mPlugin.setVisible(true);
+ }
+
+ @Override
+ public void onPluginDisconnected(HomeControlsPlugin plugin) {
+
+ }
+ }, HomeControlsPlugin.class, false);
+ return mHomeControlsLayout;
+ }
+
+ public Intent getSettingsIntent() {
+ return mHomeAppIntent;
+ }
+
+ public void setToggleState(boolean state) {
+
+ }
+
+ public int getMetricsCategory() {
+ return -1;
+ }
+
+ public boolean hasHeader() {
+ return false;
+ }
+ }
+}
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 2755e9880b58..dafdd89ee62c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -101,11 +101,15 @@ public class FlashlightTile extends QSTileImpl<BooleanState> implements
state.slash = new SlashState();
}
state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
+ state.secondaryLabel = "";
if (!mFlashlightController.isAvailable()) {
state.icon = mIcon;
state.slash.isSlashed = true;
+ state.secondaryLabel = mContext.getString(
+ R.string.quick_settings_flashlight_camera_in_use);
state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_flashlight_unavailable);
+ R.string.accessibility_quick_settings_flashlight_unavailable)
+ + ", " + state.secondaryLabel;
state.state = Tile.STATE_UNAVAILABLE;
return;
}
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 837ea9fc5f4e..fbdca3ba1c7b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -28,7 +28,7 @@ import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.LocationController.LocationChangeCallback;
@@ -40,16 +40,16 @@ public class LocationTile extends QSTileImpl<BooleanState> {
private final Icon mIcon = ResourceIcon.get(R.drawable.ic_location);
private final LocationController mController;
- private final KeyguardMonitor mKeyguard;
+ private final KeyguardStateController mKeyguard;
private final ActivityStarter mActivityStarter;
private final Callback mCallback = new Callback();
@Inject
public LocationTile(QSHost host, LocationController locationController,
- KeyguardMonitor keyguardMonitor, ActivityStarter activityStarter) {
+ KeyguardStateController keyguardStateController, ActivityStarter activityStarter) {
super(host);
mController = locationController;
- mKeyguard = keyguardMonitor;
+ mKeyguard = keyguardStateController;
mActivityStarter = activityStarter;
mController.observe(this, mCallback);
mKeyguard.observe(this, mCallback);
@@ -71,7 +71,7 @@ public class LocationTile extends QSTileImpl<BooleanState> {
@Override
protected void handleClick() {
- if (mKeyguard.isSecure() && mKeyguard.isShowing()) {
+ if (mKeyguard.isMethodSecure() && mKeyguard.isShowing()) {
mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
final boolean wasEnabled = mState.value;
mHost.openPanels();
@@ -97,7 +97,7 @@ public class LocationTile extends QSTileImpl<BooleanState> {
// Work around for bug 15916487: don't show location tile on top of lock screen. After the
// bug is fixed, this should be reverted to only hiding it on secure lock screens:
- // state.visible = !(mKeyguard.isSecure() && mKeyguard.isShowing());
+ // state.visible = !(mKeyguard.isMethodSecure() && mKeyguard.isShowing());
state.value = locationEnabled;
checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_SHARE_LOCATION);
if (state.disabledByPolicy == false) {
@@ -133,7 +133,7 @@ public class LocationTile extends QSTileImpl<BooleanState> {
}
private final class Callback implements LocationChangeCallback,
- KeyguardMonitor.Callback {
+ KeyguardStateController.Callback {
@Override
public void onLocationSettingsChanged(boolean enabled) {
refreshState();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 9268ee0705a2..3fc139882693 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -58,6 +58,7 @@ import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.pip.PipUI;
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
@@ -100,6 +101,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
private static final long MAX_BACKOFF_MILLIS = 10 * 60 * 1000;
private final Context mContext;
+ private final PipUI mPipUI;
private SysUiState mSysUiState;
private final Handler mHandler;
private final NavigationBarController mNavBarController;
@@ -353,6 +355,19 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
}
}
+ @Override
+ public void setShelfHeight(boolean visible, int shelfHeight) {
+ if (!verifyCaller("setShelfHeight")) {
+ return;
+ }
+ long token = Binder.clearCallingIdentity();
+ try {
+ mPipUI.setShelfHeight(visible, shelfHeight);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private boolean verifyCaller(String reason) {
final int callerId = Binder.getCallingUserHandle().getIdentifier();
if (callerId != mCurrentBoundedUserId) {
@@ -464,8 +479,9 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
public OverviewProxyService(Context context, DeviceProvisionedController provisionController,
NavigationBarController navBarController, NavigationModeController navModeController,
StatusBarWindowController statusBarWinController,
- SysUiState sysUiState) {
+ SysUiState sysUiState, PipUI pipUI) {
mContext = context;
+ mPipUI = pipUI;
mHandler = new Handler();
mNavBarController = navBarController;
mStatusBarWinController = statusBarWinController;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 0fc4fe72bd54..0a8264bcef87 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -17,29 +17,36 @@
package com.android.systemui.recents;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.provider.Settings;
-import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.statusbar.CommandQueue;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import javax.inject.Inject;
+
/**
* A proxy to a Recents implementation.
*/
public class Recents extends SystemUI implements CommandQueue.Callbacks {
- private RecentsImplementation mImpl;
+ private final RecentsImplementation mImpl;
+
+ @Inject
+ public Recents(Context context, RecentsImplementation impl) {
+ super(context);
+ mImpl = impl;
+ }
@Override
public void start() {
getComponent(CommandQueue.class).addCallback(this);
putComponent(Recents.class, this);
- mImpl = createRecentsImplementationFromConfig();
mImpl.onStart(mContext, this);
}
@@ -139,28 +146,6 @@ public class Recents extends SystemUI implements CommandQueue.Callbacks {
(Settings.Secure.getInt(cr, Settings.Secure.USER_SETUP_COMPLETE, 0) != 0);
}
- /**
- * @return The recents implementation from the config.
- */
- private RecentsImplementation createRecentsImplementationFromConfig() {
- final String clsName = mContext.getString(R.string.config_recentsComponent);
- if (clsName == null || clsName.length() == 0) {
- throw new RuntimeException("No recents component configured", null);
- }
- Class<?> cls = null;
- try {
- cls = mContext.getClassLoader().loadClass(clsName);
- } catch (Throwable t) {
- throw new RuntimeException("Error loading recents component: " + clsName, t);
- }
- try {
- RecentsImplementation impl = (RecentsImplementation) cls.newInstance();
- return impl;
- } catch (Throwable t) {
- throw new RuntimeException("Error creating recents component: " + clsName, t);
- }
- }
-
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mImpl.dump(pw);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java
new file mode 100644
index 000000000000..55552850890f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsModule.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents;
+
+import android.content.Context;
+
+import com.android.systemui.R;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Dagger injection module for {@link RecentsImplementation}
+ */
+@Module
+public class RecentsModule {
+ /**
+ * @return The {@link RecentsImplementation} from the config.
+ */
+ @Provides
+ public RecentsImplementation provideRecentsImpl(Context context) {
+ final String clsName = context.getString(R.string.config_recentsComponent);
+ if (clsName == null || clsName.length() == 0) {
+ throw new RuntimeException("No recents component configured", null);
+ }
+ Class<?> cls = null;
+ try {
+ cls = context.getClassLoader().loadClass(clsName);
+ } catch (Throwable t) {
+ throw new RuntimeException("Error loading recents component: " + clsName, t);
+ }
+ try {
+ RecentsImplementation impl = (RecentsImplementation) cls.newInstance();
+ return impl;
+ } catch (Throwable t) {
+ throw new RuntimeException("Error creating recents component: " + clsName, t);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
index d0c47345a83a..aa6444973a6f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
@@ -61,8 +61,10 @@ import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
import android.widget.TextView;
+import com.android.systemui.Dependency;
import com.android.systemui.Prefs;
import com.android.systemui.R;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
@@ -245,10 +247,15 @@ public class RecentsOnboarding {
private final View.OnAttachStateChangeListener mOnAttachStateChangeListener
= new View.OnAttachStateChangeListener() {
+
+ private final BroadcastDispatcher mBroadcastDispatcher = Dependency.get(
+ BroadcastDispatcher.class);
+
@Override
public void onViewAttachedToWindow(View view) {
if (view == mLayout) {
- mContext.registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
+ mBroadcastDispatcher.registerReceiver(mReceiver,
+ new IntentFilter(Intent.ACTION_SCREEN_OFF));
mLayoutAttachedToWindow = true;
if (view.getTag().equals(R.string.recents_swipe_up_onboarding)) {
mHasDismissedSwipeUpTip = false;
@@ -273,7 +280,7 @@ public class RecentsOnboarding {
}
mOverviewOpenedCountSinceQuickScrubTipDismiss = 0;
}
- mContext.unregisterReceiver(mReceiver);
+ mBroadcastDispatcher.unregisterReceiver(mReceiver);
}
}
};
@@ -335,10 +342,11 @@ public class RecentsOnboarding {
private void notifyOnTip(int action, int target) {
try {
IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
- if(overviewProxy != null) {
+ if (overviewProxy != null) {
overviewProxy.onTip(action, target);
}
- } catch (RemoteException e) {}
+ } catch (RemoteException e) {
+ }
}
public void onNavigationModeChanged(int mode) {
@@ -489,7 +497,7 @@ public class RecentsOnboarding {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
flags,
PixelFormat.TRANSLUCENT);
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("RecentsOnboarding");
lp.gravity = gravity;
return lp;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index c3c0d63f66c4..2d1c08719119 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -47,6 +47,7 @@ import android.widget.TextView;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.statusbar.phone.NavigationBarView;
@@ -127,7 +128,7 @@ public class ScreenPinningRequest implements View.OnClickListener,
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
lp.token = new Binder();
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("ScreenPinningConfirmation");
lp.gravity = Gravity.FILL;
return lp;
@@ -159,6 +160,8 @@ public class ScreenPinningRequest implements View.OnClickListener,
private ValueAnimator mColorAnim;
private ViewGroup mLayout;
private boolean mShowCancel;
+ private final BroadcastDispatcher mBroadcastDispatcher =
+ Dependency.get(BroadcastDispatcher.class);
public RequestWindowView(Context context, boolean showCancel) {
super(context);
@@ -212,7 +215,7 @@ public class ScreenPinningRequest implements View.OnClickListener,
IntentFilter filter = new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED);
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_SCREEN_OFF);
- mContext.registerReceiver(mReceiver, filter);
+ mBroadcastDispatcher.registerReceiver(mReceiver, filter);
}
private void inflateView(int rotation) {
@@ -313,7 +316,7 @@ public class ScreenPinningRequest implements View.OnClickListener,
@Override
public void onDetachedFromWindow() {
- mContext.unregisterReceiver(mReceiver);
+ mBroadcastDispatcher.unregisterReceiver(mReceiver);
}
protected void onConfigurationChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
index 1e0a9f157ff5..176676fcb9b3 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
@@ -152,6 +152,20 @@ public class BrightnessController implements ToggleSlider.Listener {
private final Runnable mStartListeningRunnable = new Runnable() {
@Override
public void run() {
+ if (mListening) {
+ return;
+ }
+ mListening = true;
+
+ if (mVrManager != null) {
+ try {
+ mVrManager.registerListener(mVrStateCallbacks);
+ mIsVrModeEnabled = mVrManager.getVrModeState();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to register VR mode state listener: ", e);
+ }
+ }
+
mBrightnessObserver.startObserving();
mUserTracker.startTracking();
@@ -167,6 +181,19 @@ public class BrightnessController implements ToggleSlider.Listener {
private final Runnable mStopListeningRunnable = new Runnable() {
@Override
public void run() {
+ if (!mListening) {
+ return;
+ }
+ mListening = false;
+
+ if (mVrManager != null) {
+ try {
+ mVrManager.unregisterListener(mVrStateCallbacks);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to unregister VR mode state listener: ", e);
+ }
+ }
+
mBrightnessObserver.stopObserving();
mUserTracker.stopTracking();
@@ -297,39 +324,12 @@ public class BrightnessController implements ToggleSlider.Listener {
}
public void registerCallbacks() {
- if (mListening) {
- return;
- }
-
- if (mVrManager != null) {
- try {
- mVrManager.registerListener(mVrStateCallbacks);
- mIsVrModeEnabled = mVrManager.getVrModeState();
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to register VR mode state listener: ", e);
- }
- }
-
mBackgroundHandler.post(mStartListeningRunnable);
- mListening = true;
}
/** Unregister all call backs, both to and from the controller */
public void unregisterCallbacks() {
- if (!mListening) {
- return;
- }
-
- if (mVrManager != null) {
- try {
- mVrManager.unregisterListener(mVrStateCallbacks);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to unregister VR mode state listener: ", e);
- }
- }
-
mBackgroundHandler.post(mStopListeningRunnable);
- mListening = false;
mControlValueInitialized = false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index 07675e248906..df9791d1bd37 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -19,6 +19,7 @@ package com.android.systemui.shortcut;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import android.content.Context;
import android.content.res.Configuration;
import android.os.RemoteException;
import android.util.Log;
@@ -52,6 +53,10 @@ public class ShortcutKeyDispatcher extends SystemUI
protected final long SC_DOCK_LEFT = META_MASK | KeyEvent.KEYCODE_LEFT_BRACKET;
protected final long SC_DOCK_RIGHT = META_MASK | KeyEvent.KEYCODE_RIGHT_BRACKET;
+ public ShortcutKeyDispatcher(Context context) {
+ super(context);
+ }
+
/**
* Registers a shortcut key to window manager.
* @param shortcutCode packed representation of shortcut key code and meta information
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index cd2074fd64b8..c8b2b6aee0b3 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -19,6 +19,7 @@ package com.android.systemui.stackdivider;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import android.content.Context;
import android.content.res.Configuration;
import android.os.RemoteException;
import android.util.Log;
@@ -50,6 +51,10 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks {
private boolean mHomeStackResizable = false;
private ForcedResizableInfoActivityController mForcedResizableController;
+ public Divider(Context context) {
+ super(context);
+ }
+
@Override
public void start() {
mWindowManager = new DividerWindowManager(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index 33bcefb323f8..e24a3625769c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -65,7 +65,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
Log.v(TAG, "showNotification");
}
addAlertEntry(entry);
- updateNotification(entry.key, true /* alert */);
+ updateNotification(entry.getKey(), true /* alert */);
entry.setInterruption();
}
@@ -182,7 +182,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
protected final void addAlertEntry(@NonNull NotificationEntry entry) {
AlertEntry alertEntry = createAlertEntry();
alertEntry.setEntry(entry);
- mAlertEntries.put(entry.key, alertEntry);
+ mAlertEntries.put(entry.getKey(), alertEntry);
onAlertEntryAdded(alertEntry);
entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
}
@@ -251,7 +251,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
@Override
public boolean shouldExtendLifetime(NotificationEntry entry) {
- return !canRemoveImmediately(entry.key);
+ return !canRemoveImmediately(entry.getKey());
}
@Override
@@ -260,7 +260,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
mExtendedLifetimeAlertEntries.add(entry);
// We need to make sure that entries are stopping to alert eventually, let's remove
// this as soon as possible.
- AlertEntry alertEntry = mAlertEntries.get(entry.key);
+ AlertEntry alertEntry = mAlertEntries.get(entry.getKey());
alertEntry.removeAsSoonAsPossible();
} else {
mExtendedLifetimeAlertEntries.remove(entry);
@@ -276,7 +276,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
@Nullable protected Runnable mRemoveAlertRunnable;
public void setEntry(@NonNull final NotificationEntry entry) {
- setEntry(entry, () -> removeAlertEntry(entry.key));
+ setEntry(entry, () -> removeAlertEntry(entry.getKey()));
}
public void setEntry(@NonNull final NotificationEntry entry,
@@ -332,7 +332,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
public int compareTo(@NonNull AlertEntry alertEntry) {
return (mPostTime < alertEntry.mPostTime)
? 1 : ((mPostTime == alertEntry.mPostTime)
- ? mEntry.key.compareTo(alertEntry.mEntry.key) : -1);
+ ? mEntry.getKey().compareTo(alertEntry.mEntry.getKey()) : -1);
}
public void reset() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 134d4b87a159..34f543702d96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -22,6 +22,8 @@ import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEF
import static android.inputmethodservice.InputMethodService.IME_INVISIBLE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
import static com.android.systemui.statusbar.phone.StatusBar.ONLY_CORE_APPS;
@@ -43,12 +45,17 @@ import android.os.Looper;
import android.os.Message;
import android.util.Pair;
import android.util.SparseArray;
+import android.view.InsetsFlags;
+import android.view.InsetsState.InternalInsetType;
+import android.view.View;
+import android.view.WindowInsetsController.Appearance;
import androidx.annotation.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.view.AppearanceRegion;
import com.android.systemui.SystemUI;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.policy.CallbackController;
@@ -76,7 +83,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
private static final int MSG_EXPAND_NOTIFICATIONS = 3 << MSG_SHIFT;
private static final int MSG_COLLAPSE_PANELS = 4 << MSG_SHIFT;
private static final int MSG_EXPAND_SETTINGS = 5 << MSG_SHIFT;
- private static final int MSG_SET_SYSTEMUI_VISIBILITY = 6 << MSG_SHIFT;
+ private static final int MSG_SYSTEM_BAR_APPEARANCE_CHANGED = 6 << MSG_SHIFT;
private static final int MSG_DISPLAY_READY = 7 << MSG_SHIFT;
private static final int MSG_SHOW_IME_BUTTON = 8 << MSG_SHIFT;
private static final int MSG_TOGGLE_RECENT_APPS = 9 << MSG_SHIFT;
@@ -115,6 +122,9 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT;
private static final int MSG_SHOW_PINNING_TOAST_ESCAPE = 46 << MSG_SHIFT;
private static final int MSG_RECENTS_ANIMATION_STATE_CHANGED = 47 << MSG_SHIFT;
+ private static final int MSG_SHOW_TRANSIENT = 48 << MSG_SHIFT;
+ private static final int MSG_ABORT_TRANSIENT = 49 << MSG_SHIFT;
+ private static final int MSG_TOP_APP_WINDOW_CHANGED = 50 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -160,28 +170,6 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
default void animateExpandSettingsPanel(String obj) { }
/**
- * Called to notify visibility flag changes.
- * @see IStatusBar#setSystemUiVisibility(int, int, int, int, int, Rect, Rect).
- *
- * @param displayId The id of the display to notify.
- * @param vis The visibility flags except SYSTEM_UI_FLAG_LIGHT_STATUS_BAR which will
- * be reported separately in fullscreenStackVis and dockedStackVis.
- * @param fullscreenStackVis The flags which only apply in the region of the fullscreen
- * stack, which is currently only SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
- * @param dockedStackVis The flags that only apply in the region of the docked stack, which
- * is currently only SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
- * @param mask Which flags to change.
- * @param fullscreenStackBounds The current bounds of the fullscreen stack, in screen
- * coordinates.
- * @param dockedStackBounds The current bounds of the docked stack, in screen coordinates.
- * @param navbarColorManagedByIme {@code true} if navigation bar color is managed by IME.
- */
- default void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
- int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds,
- boolean navbarColorManagedByIme) {
- }
-
- /**
* Called to notify IME window status changes.
*
* @param displayId The id of the display to notify.
@@ -270,12 +258,13 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
default void onRotationProposal(int rotation, boolean isValid) { }
- default void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
- int type, boolean requireConfirmation, int userId, String opPackageName) { }
- default void onBiometricAuthenticated(boolean authenticated, String failureReason) { }
+ default void showAuthenticationDialog(Bundle bundle,
+ IBiometricServiceReceiverInternal receiver, int biometricModality,
+ boolean requireConfirmation, int userId, String opPackageName) { }
+ default void onBiometricAuthenticated() { }
default void onBiometricHelp(String message) { }
- default void onBiometricError(String error) { }
- default void hideBiometricDialog() { }
+ default void onBiometricError(int modality, int error, int vendorCode) { }
+ default void hideAuthenticationDialog() { }
/**
* @see IStatusBar#onDisplayReady(int)
@@ -291,6 +280,28 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
* @see IStatusBar#onRecentsAnimationStateChanged(boolean)
*/
default void onRecentsAnimationStateChanged(boolean running) { }
+
+ /**
+ * @see IStatusBar#onSystemBarAppearanceChanged(int, int, AppearanceRegion[], boolean).
+ */
+ default void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance,
+ AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) { }
+
+ /**
+ * @see IStatusBar#showTransient(int, int[]).
+ */
+ default void showTransient(int displayId, @InternalInsetType int[] types) { }
+
+ /**
+ * @see IStatusBar#abortTransient(int, int[]).
+ */
+ default void abortTransient(int displayId, @InternalInsetType int[] types) { }
+
+ /**
+ * @see IStatusBar#topAppWindowChanged(int, boolean, boolean).
+ */
+ default void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) {
+ }
}
@VisibleForTesting
@@ -455,28 +466,53 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
}
}
+ // TODO(b/118118435): Remove this function after migration
@Override
public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds,
boolean navbarColorManagedByIme) {
synchronized (mLock) {
- // Don't coalesce these, since it might have one time flags set such as
- // STATUS_BAR_UNHIDE which might get lost.
+ final boolean hasDockedStack = !dockedStackBounds.isEmpty();
+ final boolean transientStatus = (vis & View.STATUS_BAR_TRANSIENT) != 0;
+ final boolean transientNavigation = (vis & View.NAVIGATION_BAR_TRANSIENT) != 0;
+ if (transientStatus && transientNavigation) {
+ showTransient(displayId, new int[]{TYPE_TOP_BAR, TYPE_NAVIGATION_BAR});
+ } else if (transientStatus) {
+ showTransient(displayId, new int[]{TYPE_TOP_BAR});
+ abortTransient(displayId, new int[]{TYPE_NAVIGATION_BAR});
+ } else if (transientNavigation) {
+ showTransient(displayId, new int[]{TYPE_NAVIGATION_BAR});
+ abortTransient(displayId, new int[]{TYPE_TOP_BAR});
+ } else {
+ abortTransient(displayId, new int[]{TYPE_TOP_BAR, TYPE_NAVIGATION_BAR});
+ }
SomeArgs args = SomeArgs.obtain();
args.argi1 = displayId;
- args.argi2 = vis;
- args.argi3 = fullscreenStackVis;
- args.argi4 = dockedStackVis;
- args.argi5 = mask;
- args.argi6 = navbarColorManagedByIme ? 1 : 0;
- args.arg1 = fullscreenStackBounds;
- args.arg2 = dockedStackBounds;
- mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, args).sendToTarget();
+ args.argi2 = InsetsFlags.getAppearance(vis);
+ args.argi3 = navbarColorManagedByIme ? 1 : 0;
+ final int fullscreenAppearance = InsetsFlags.getAppearance(fullscreenStackVis);
+ final int dockedAppearance = InsetsFlags.getAppearance(dockedStackVis);
+ args.arg1 = hasDockedStack
+ ? new AppearanceRegion[]{
+ new AppearanceRegion(fullscreenAppearance, fullscreenStackBounds),
+ new AppearanceRegion(dockedAppearance, dockedStackBounds)}
+ : new AppearanceRegion[]{
+ new AppearanceRegion(fullscreenAppearance, fullscreenStackBounds)};
+ mHandler.obtainMessage(MSG_SYSTEM_BAR_APPEARANCE_CHANGED, args).sendToTarget();
}
}
@Override
- public void topAppWindowChanged(int displayId, boolean menuVisible) { }
+ public void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) {
+ synchronized (mLock) {
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = displayId;
+ args.argi2 = isFullscreen ? 1 : 0;
+ args.argi3 = isImmersive ? 1 : 0;
+ mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED, args).sendToTarget();
+ }
+
+ }
@Override
public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
@@ -740,13 +776,13 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
}
@Override
- public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
- int type, boolean requireConfirmation, int userId, String opPackageName) {
+ public void showAuthenticationDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
+ int biometricModality, boolean requireConfirmation, int userId, String opPackageName) {
synchronized (mLock) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
args.arg2 = receiver;
- args.argi1 = type;
+ args.argi1 = biometricModality;
args.arg3 = requireConfirmation;
args.argi2 = userId;
args.arg4 = opPackageName;
@@ -756,12 +792,9 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
}
@Override
- public void onBiometricAuthenticated(boolean authenticated, String failureReason) {
+ public void onBiometricAuthenticated() {
synchronized (mLock) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = authenticated;
- args.arg2 = failureReason;
- mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED, args).sendToTarget();
+ mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED).sendToTarget();
}
}
@@ -773,14 +806,18 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
}
@Override
- public void onBiometricError(String error) {
+ public void onBiometricError(int modality, int error, int vendorCode) {
synchronized (mLock) {
- mHandler.obtainMessage(MSG_BIOMETRIC_ERROR, error).sendToTarget();
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = modality;
+ args.argi2 = error;
+ args.argi3 = vendorCode;
+ mHandler.obtainMessage(MSG_BIOMETRIC_ERROR, args).sendToTarget();
}
}
@Override
- public void hideBiometricDialog() {
+ public void hideAuthenticationDialog() {
synchronized (mLock) {
mHandler.obtainMessage(MSG_BIOMETRIC_HIDE).sendToTarget();
}
@@ -826,6 +863,33 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
}
}
+ @Override
+ public void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance,
+ AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) {
+ synchronized (mLock) {
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = displayId;
+ args.argi2 = appearance;
+ args.argi3 = navbarColorManagedByIme ? 1 : 0;
+ args.arg1 = appearanceRegions;
+ mHandler.obtainMessage(MSG_SYSTEM_BAR_APPEARANCE_CHANGED, args).sendToTarget();
+ }
+ }
+
+ @Override
+ public void showTransient(int displayId, int[] types) {
+ synchronized (mLock) {
+ mHandler.obtainMessage(MSG_SHOW_TRANSIENT, displayId, 0, types).sendToTarget();
+ }
+ }
+
+ @Override
+ public void abortTransient(int displayId, int[] types) {
+ synchronized (mLock) {
+ mHandler.obtainMessage(MSG_ABORT_TRANSIENT, displayId, 0, types).sendToTarget();
+ }
+ }
+
private final class H extends Handler {
private H(Looper l) {
super(l);
@@ -878,15 +942,6 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
mCallbacks.get(i).animateExpandSettingsPanel((String) msg.obj);
}
break;
- case MSG_SET_SYSTEMUI_VISIBILITY:
- args = (SomeArgs) msg.obj;
- for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).setSystemUiVisibility(args.argi1, args.argi2, args.argi3,
- args.argi4, args.argi5, (Rect) args.arg1, (Rect) args.arg2,
- args.argi6 == 1);
- }
- args.recycle();
- break;
case MSG_SHOW_IME_BUTTON:
args = (SomeArgs) msg.obj;
handleShowImeButton(args.argi1 /* displayId */, (IBinder) args.arg1 /* token */,
@@ -1032,10 +1087,10 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
mHandler.removeMessages(MSG_BIOMETRIC_AUTHENTICATED);
SomeArgs someArgs = (SomeArgs) msg.obj;
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).showBiometricDialog(
+ mCallbacks.get(i).showAuthenticationDialog(
(Bundle) someArgs.arg1,
(IBiometricServiceReceiverInternal) someArgs.arg2,
- someArgs.argi1 /* type */,
+ someArgs.argi1 /* biometricModality */,
(boolean) someArgs.arg3 /* requireConfirmation */,
someArgs.argi2 /* userId */,
(String) someArgs.arg4 /* opPackageName */);
@@ -1044,13 +1099,9 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
break;
}
case MSG_BIOMETRIC_AUTHENTICATED: {
- SomeArgs someArgs = (SomeArgs) msg.obj;
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onBiometricAuthenticated(
- (boolean) someArgs.arg1 /* authenticated */,
- (String) someArgs.arg2 /* failureReason */);
+ mCallbacks.get(i).onBiometricAuthenticated();
}
- someArgs.recycle();
break;
}
case MSG_BIOMETRIC_HELP:
@@ -1059,13 +1110,19 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
}
break;
case MSG_BIOMETRIC_ERROR:
+ SomeArgs someArgs = (SomeArgs) msg.obj;
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onBiometricError((String) msg.obj);
+ mCallbacks.get(i).onBiometricError(
+ someArgs.argi1 /* modality */,
+ someArgs.argi2 /* error */,
+ someArgs.argi3 /* vendorCode */
+ );
}
+ someArgs.recycle();
break;
case MSG_BIOMETRIC_HIDE:
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).hideBiometricDialog();
+ mCallbacks.get(i).hideAuthenticationDialog();
}
break;
case MSG_SHOW_CHARGING_ANIMATION:
@@ -1093,6 +1150,39 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
mCallbacks.get(i).onRecentsAnimationStateChanged(msg.arg1 > 0);
}
break;
+ case MSG_SYSTEM_BAR_APPEARANCE_CHANGED:
+ args = (SomeArgs) msg.obj;
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).onSystemBarAppearanceChanged(args.argi1, args.argi2,
+ (AppearanceRegion[]) args.arg1, args.argi3 == 1);
+ }
+ args.recycle();
+ break;
+ case MSG_SHOW_TRANSIENT: {
+ final int displayId = msg.arg1;
+ final int[] types = (int[]) msg.obj;
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).showTransient(displayId, types);
+ }
+ break;
+ }
+ case MSG_ABORT_TRANSIENT: {
+ final int displayId = msg.arg1;
+ final int[] types = (int[]) msg.obj;
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).abortTransient(displayId, types);
+ }
+ break;
+ }
+ case MSG_TOP_APP_WINDOW_CHANGED: {
+ args = (SomeArgs) msg.obj;
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).topAppWindowChanged(
+ args.argi1, args.argi2 != 0, args.argi3 != 0);
+ }
+ args.recycle();
+ break;
+ }
}
}
}
@@ -1100,6 +1190,10 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
// Need this class since CommandQueue already extends IStatusBar.Stub, so CommandQueueStart
// is needed so it can extend SystemUI.
public static class CommandQueueStart extends SystemUI {
+ public CommandQueueStart(Context context) {
+ super(context);
+ }
+
@Override
public void start() {
putComponent(CommandQueue.class, new CommandQueue(mContext));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
new file mode 100644
index 000000000000..341c49a87156
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.provider.DeviceConfig;
+import android.util.ArrayMap;
+
+import com.android.systemui.dagger.qualifiers.BgHandler;
+
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Class to manage simple DeviceConfig-based feature flags.
+ *
+ * To enable or disable a flag, run:
+ *
+ * {@code
+ * $ adb shell device_config put systemui <key> <true|false>
+* }
+ *
+ * You will probably need to restart systemui for the changes to be picked up:
+ *
+ * {@code
+ * $ adb shell am crash com.android.systemui
+ * }
+ */
+@Singleton
+public class FeatureFlags {
+ private final Map<String, Boolean> mCachedDeviceConfigFlags = new ArrayMap<>();
+
+ @Inject
+ public FeatureFlags(@BgHandler Handler bgHandler) {
+ DeviceConfig.addOnPropertiesChangedListener(
+ "systemui",
+ new HandlerExecutor(bgHandler),
+ this::onPropertiesChanged);
+ }
+
+ public boolean isNewNotifPipelineEnabled() {
+ return getDeviceConfigFlag("notification.newpipeline.enabled", false);
+ }
+
+ private void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
+ synchronized (mCachedDeviceConfigFlags) {
+ for (String key : properties.getKeyset()) {
+ mCachedDeviceConfigFlags.remove(key);
+ }
+ }
+ }
+
+ private boolean getDeviceConfigFlag(String key, boolean defaultValue) {
+ synchronized (mCachedDeviceConfigFlags) {
+ Boolean flag = mCachedDeviceConfigFlags.get(key);
+ if (flag == null) {
+ flag = DeviceConfig.getBoolean("systemui", key, defaultValue);
+ mCachedDeviceConfigFlags.put(key, flag);
+ }
+ return flag;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 156e3c072dbe..681f3abaea91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -57,8 +57,8 @@ import com.android.systemui.statusbar.phone.LockIcon;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.UnlockMethodCache;
import com.android.systemui.statusbar.policy.AccessibilityController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.util.wakelock.SettableWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
@@ -72,7 +72,7 @@ import java.util.IllegalFormatConversionException;
* Controls the indications and error messages shown on the Keyguard
*/
public class KeyguardIndicationController implements StateListener,
- UnlockMethodCache.OnUnlockMethodChangedListener {
+ KeyguardStateController.Callback {
private static final String TAG = "KeyguardIndication";
private static final boolean DEBUG_CHARGING_SPEED = false;
@@ -86,7 +86,7 @@ public class KeyguardIndicationController implements StateListener,
private final Context mContext;
private final ShadeController mShadeController;
private final AccessibilityController mAccessibilityController;
- private final UnlockMethodCache mUnlockMethodCache;
+ private final KeyguardStateController mKeyguardStateController;
private final StatusBarStateController mStatusBarStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private ViewGroup mIndicationArea;
@@ -137,7 +137,7 @@ public class KeyguardIndicationController implements StateListener,
WakeLock.createPartial(context, "Doze:KeyguardIndication"),
Dependency.get(ShadeController.class),
Dependency.get(AccessibilityController.class),
- UnlockMethodCache.getInstance(context),
+ Dependency.get(KeyguardStateController.class),
Dependency.get(StatusBarStateController.class),
Dependency.get(KeyguardUpdateMonitor.class));
}
@@ -148,14 +148,15 @@ public class KeyguardIndicationController implements StateListener,
@VisibleForTesting
KeyguardIndicationController(Context context, ViewGroup indicationArea, LockIcon lockIcon,
LockPatternUtils lockPatternUtils, WakeLock wakeLock, ShadeController shadeController,
- AccessibilityController accessibilityController, UnlockMethodCache unlockMethodCache,
+ AccessibilityController accessibilityController,
+ KeyguardStateController keyguardStateController,
StatusBarStateController statusBarStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor) {
mContext = context;
mLockIcon = lockIcon;
mShadeController = shadeController;
mAccessibilityController = accessibilityController;
- mUnlockMethodCache = unlockMethodCache;
+ mKeyguardStateController = keyguardStateController;
mStatusBarStateController = statusBarStateController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
// lock icon is not used on all form factors.
@@ -181,7 +182,7 @@ public class KeyguardIndicationController implements StateListener,
mKeyguardUpdateMonitor.registerCallback(getKeyguardCallback());
mKeyguardUpdateMonitor.registerCallback(mTickReceiver);
mStatusBarStateController.addCallback(this);
- mUnlockMethodCache.addListener(this);
+ mKeyguardStateController.addCallback(this);
}
public void setIndicationArea(ViewGroup indicationArea) {
@@ -583,7 +584,7 @@ public class KeyguardIndicationController implements StateListener,
}
@Override
- public void onUnlockMethodStateChanged() {
+ public void onUnlockedChanged() {
updateIndication(!mDozing);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
index 7bcbd3683130..1f389049f423 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar;
import static android.view.Display.DEFAULT_DISPLAY;
-import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
import static com.android.systemui.SysUiServiceProvider.getComponent;
import android.content.Context;
@@ -37,6 +36,8 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.RegisterStatusBarResult;
import com.android.systemui.Dependency;
+import com.android.systemui.assist.AssistHandleViewController;
+import com.android.systemui.dagger.qualifiers.MainHandler;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.phone.AutoHideController;
@@ -47,7 +48,6 @@ import com.android.systemui.statusbar.phone.NavigationBarView;
import com.android.systemui.statusbar.policy.BatteryController;
import javax.inject.Inject;
-import javax.inject.Named;
import javax.inject.Singleton;
@@ -66,7 +66,7 @@ public class NavigationBarController implements Callbacks {
SparseArray<NavigationBarFragment> mNavigationBars = new SparseArray<>();
@Inject
- public NavigationBarController(Context context, @Named(MAIN_HANDLER_NAME) Handler handler) {
+ public NavigationBarController(Context context, @MainHandler Handler handler) {
mContext = context;
mHandler = handler;
mDisplayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
@@ -151,9 +151,11 @@ public class NavigationBarController implements Callbacks {
// Dependency problem.
AutoHideController autoHideController = isOnDefaultDisplay
? Dependency.get(AutoHideController.class)
- : new AutoHideController(context, mHandler);
+ : new AutoHideController(context, mHandler,
+ Dependency.get(NotificationRemoteInputManager.class),
+ Dependency.get(IWindowManager.class));
navBar.setAutoHideController(autoHideController);
- navBar.restoreSystemUiVisibilityState();
+ navBar.restoreAppearanceAndTransientState();
mNavigationBars.append(displayId, navBar);
if (result != null) {
@@ -230,7 +232,15 @@ public class NavigationBarController implements Callbacks {
}
/** @return {@link NavigationBarFragment} on the default display. */
+ @Nullable
public NavigationBarFragment getDefaultNavigationBarFragment() {
return mNavigationBars.get(DEFAULT_DISPLAY);
}
+
+ /** @return {@link AssistHandleViewController} (only on the default display). */
+ @Nullable
+ public AssistHandleViewController getAssistHandlerViewController() {
+ NavigationBarFragment navBar = getDefaultNavigationBarFragment();
+ return navBar == null ? null : navBar.getAssistHandlerViewController();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 14009214bdc5..c4de2d3572bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -21,6 +21,7 @@ import static com.android.systemui.statusbar.notification.NotificationEntryManag
import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_CHILD_NOTIFICATIONS;
+import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.NotificationManager;
import android.content.ComponentName;
@@ -57,8 +58,9 @@ public class NotificationListener extends NotificationListenerWithPlugins {
private final NotificationGroupManager mGroupManager =
Dependency.get(NotificationGroupManager.class);
- private final ArrayList<NotificationSettingsListener> mSettingsListeners = new ArrayList<>();
private final Context mContext;
+ private final ArrayList<NotificationSettingsListener> mSettingsListeners = new ArrayList<>();
+ @Nullable private NotifServiceListener mDownstreamListener;
@Inject
public NotificationListener(Context context) {
@@ -69,6 +71,10 @@ public class NotificationListener extends NotificationListenerWithPlugins {
mSettingsListeners.add(listener);
}
+ public void setDownstreamListener(NotifServiceListener downstreamListener) {
+ mDownstreamListener = downstreamListener;
+ }
+
@Override
public void onListenerConnected() {
if (DEBUG) Log.d(TAG, "onListenerConnected");
@@ -81,6 +87,9 @@ public class NotificationListener extends NotificationListenerWithPlugins {
final RankingMap currentRanking = getCurrentRanking();
Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
for (StatusBarNotification sbn : notifications) {
+ if (mDownstreamListener != null) {
+ mDownstreamListener.onNotificationPosted(sbn, currentRanking);
+ }
mEntryManager.addNotification(sbn, currentRanking);
}
});
@@ -95,6 +104,11 @@ public class NotificationListener extends NotificationListenerWithPlugins {
if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {
Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
processForRemoteInput(sbn.getNotification(), mContext);
+
+ if (mDownstreamListener != null) {
+ mDownstreamListener.onNotificationPosted(sbn, rankingMap);
+ }
+
String key = sbn.getKey();
boolean isUpdate =
mEntryManager.getNotificationData().get(key) != null;
@@ -113,7 +127,7 @@ public class NotificationListener extends NotificationListenerWithPlugins {
mEntryManager.removeNotification(key, rankingMap, UNDEFINED_DISMISS_REASON);
} else {
mEntryManager.getNotificationData()
- .updateRanking(rankingMap);
+ .updateRanking(rankingMap, "onNotificationPosted");
}
return;
}
@@ -133,6 +147,9 @@ public class NotificationListener extends NotificationListenerWithPlugins {
if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) {
final String key = sbn.getKey();
Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
+ if (mDownstreamListener != null) {
+ mDownstreamListener.onNotificationRemoved(sbn, rankingMap, reason);
+ }
mEntryManager.removeNotification(key, rankingMap, reason);
});
}
@@ -149,6 +166,9 @@ public class NotificationListener extends NotificationListenerWithPlugins {
if (rankingMap != null) {
RankingMap r = onPluginRankingUpdate(rankingMap);
Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
+ if (mDownstreamListener != null) {
+ mDownstreamListener.onNotificationRankingUpdate(rankingMap);
+ }
mEntryManager.updateNotificationRanking(r);
});
}
@@ -175,4 +195,12 @@ public class NotificationListener extends NotificationListenerWithPlugins {
default void onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons) { }
}
+
+ /** Interface for listening to add/remove events that we receive from NotificationManager. */
+ public interface NotifServiceListener {
+ void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap);
+ void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap);
+ void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap, int reason);
+ void onNotificationRankingUpdate(RankingMap rankingMap);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index f782fab7e49f..7adf7af89f0d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -54,7 +54,7 @@ import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -76,7 +76,8 @@ public class NotificationLockscreenUserManagerImpl implements
private final DeviceProvisionedController mDeviceProvisionedController =
Dependency.get(DeviceProvisionedController.class);
- private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+ private final KeyguardStateController mKeyguardStateController = Dependency.get(
+ KeyguardStateController.class);
// Lazy
private NotificationEntryManager mEntryManager;
@@ -105,7 +106,7 @@ public class NotificationLockscreenUserManagerImpl implements
isCurrentProfile(getSendingUserId())) {
mUsersAllowingPrivateNotifications.clear();
updateLockscreenNotificationSetting();
- getEntryManager().updateNotifications();
+ getEntryManager().updateNotifications("ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED");
}
}
};
@@ -123,7 +124,7 @@ public class NotificationLockscreenUserManagerImpl implements
updatePublicMode();
// The filtering needs to happen before the update call below in order to make sure
// the presenter has the updated notifications from the new user
- getEntryManager().getNotificationData().filterAndSort();
+ getEntryManager().getNotificationData().filterAndSort("user switched");
mPresenter.onUserSwitched(mCurrentUserId);
for (UserChangedListener listener : mListeners) {
@@ -204,7 +205,8 @@ public class NotificationLockscreenUserManagerImpl implements
mUsersAllowingNotifications.clear();
// ... and refresh all the notifications
updateLockscreenNotificationSetting();
- getEntryManager().updateNotifications();
+ getEntryManager().updateNotifications("LOCK_SCREEN_SHOW_NOTIFICATIONS,"
+ + " or LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS change");
}
};
@@ -213,7 +215,8 @@ public class NotificationLockscreenUserManagerImpl implements
public void onChange(boolean selfChange) {
updateLockscreenNotificationSetting();
if (mDeviceProvisionedController.isDeviceProvisioned()) {
- getEntryManager().updateNotifications();
+ getEntryManager().updateNotifications("LOCK_SCREEN_ALLOW_REMOTE_INPUT"
+ + " or ZEN_MODE change");
}
}
};
@@ -320,7 +323,7 @@ public class NotificationLockscreenUserManagerImpl implements
exceedsPriorityThreshold = entry.getBucket() != BUCKET_SILENT;
} else {
exceedsPriorityThreshold =
- !getEntryManager().getNotificationData().isAmbient(entry.key);
+ !getEntryManager().getNotificationData().isAmbient(entry.getKey());
}
return mShowLockscreenNotifications && exceedsPriorityThreshold;
}
@@ -442,15 +445,15 @@ public class NotificationLockscreenUserManagerImpl implements
/** @return true if the entry needs redaction when on the lockscreen. */
public boolean needsRedaction(NotificationEntry ent) {
- int userId = ent.notification.getUserId();
+ int userId = ent.getSbn().getUserId();
boolean currentUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(mCurrentUserId);
boolean notiUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(userId);
boolean redactedLockscreen = currentUserWantsRedaction || notiUserWantsRedaction;
boolean notificationRequestsRedaction =
- ent.notification.getNotification().visibility == Notification.VISIBILITY_PRIVATE;
- boolean userForcesRedaction = packageHasVisibilityOverride(ent.notification.getKey());
+ ent.getSbn().getNotification().visibility == Notification.VISIBILITY_PRIVATE;
+ boolean userForcesRedaction = packageHasVisibilityOverride(ent.getSbn().getKey());
return userForcesRedaction || notificationRequestsRedaction && redactedLockscreen;
}
@@ -507,8 +510,8 @@ public class NotificationLockscreenUserManagerImpl implements
// asking if the keyguard is showing. We still need to check it though because showing the
// camera on the keyguard has a state of SHADE but the keyguard is still showing.
final boolean showingKeyguard = mState != StatusBarState.SHADE
- || mKeyguardMonitor.isShowing();
- final boolean devicePublic = showingKeyguard && isSecure(getCurrentUserId());
+ || mKeyguardStateController.isShowing();
+ final boolean devicePublic = showingKeyguard && mKeyguardStateController.isMethodSecure();
// Look for public mode users. Users are considered public in either case of:
@@ -523,7 +526,7 @@ public class NotificationLockscreenUserManagerImpl implements
boolean needsSeparateChallenge = whitelistIpcs(() ->
mLockPatternUtils.isSeparateProfileChallengeEnabled(userId));
if (!devicePublic && userId != getCurrentUserId()
- && needsSeparateChallenge && isSecure(userId)) {
+ && needsSeparateChallenge && mLockPatternUtils.isSecure(userId)) {
// Keyguard.isDeviceLocked is updated asynchronously, assume that all profiles
// with separate challenge are locked when keyguard is visible to avoid race.
isProfilePublic = showingKeyguard || mKeyguardManager.isDeviceLocked(userId);
@@ -531,7 +534,7 @@ public class NotificationLockscreenUserManagerImpl implements
setLockscreenPublicMode(isProfilePublic, userId);
mUsersWithSeperateWorkChallenge.put(userId, needsSeparateChallenge);
}
- getEntryManager().updateNotifications();
+ getEntryManager().updateNotifications("NotificationLockscreenUserManager.updatePublicMode");
}
@Override
@@ -545,7 +548,7 @@ public class NotificationLockscreenUserManagerImpl implements
// // asking if the keyguard is showing. We still need to check it though because showing the
// // camera on the keyguard has a state of SHADE but the keyguard is still showing.
// final boolean showingKeyguard = mState != StatusBarState.SHADE
-// || mKeyguardMonitor.isShowing();
+// || mKeyguardStateController.isShowing();
// final boolean devicePublic = showingKeyguard && isSecure(getCurrentUserId());
//
//
@@ -570,10 +573,6 @@ public class NotificationLockscreenUserManagerImpl implements
// }
// }
- private boolean isSecure(int userId) {
- return mKeyguardMonitor.isSecure() || mLockPatternUtils.isSecure(userId);
- }
-
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("NotificationLockscreenUserManager state:");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 00a12a9e4409..0988e347945c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -17,10 +17,6 @@ package com.android.systemui.statusbar;
import static com.android.systemui.Dependency.MAIN_HANDLER;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_UNLOCK_FADING;
-import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
-import static com.android.systemui.statusbar.phone.BiometricUnlockController
- .MODE_WAKE_AND_UNLOCK_PULSING;
import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_MEDIA_FAKE_ARTWORK;
import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_LOCKSCREEN_WALLPAPER;
import static com.android.systemui.statusbar.phone.StatusBar.SHOW_LOCKSCREEN_MEDIA_ARTWORK;
@@ -67,7 +63,7 @@ import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ScrimState;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -94,7 +90,8 @@ public class NotificationMediaManager implements Dumpable {
private final StatusBarStateController mStatusBarStateController
= Dependency.get(StatusBarStateController.class);
private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class);
- private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+ private final KeyguardStateController mKeyguardStateController = Dependency.get(
+ KeyguardStateController.class);
private final KeyguardBypassController mKeyguardBypassController;
private static final HashSet<Integer> PAUSED_MEDIA_STATES = new HashSet<>();
static {
@@ -207,7 +204,7 @@ public class NotificationMediaManager implements Dumpable {
NotificationEntry entry,
NotificationVisibility visibility,
boolean removedByUser) {
- onNotificationRemoved(entry.key);
+ onNotificationRemoved(entry.getKey());
}
});
@@ -287,7 +284,7 @@ public class NotificationMediaManager implements Dumpable {
if (entry.isMediaNotification()) {
final MediaSession.Token token =
- entry.notification.getNotification().extras.getParcelable(
+ entry.getSbn().getNotification().extras.getParcelable(
Notification.EXTRA_MEDIA_SESSION);
if (token != null) {
MediaController aController = new MediaController(mContext, token);
@@ -295,7 +292,7 @@ public class NotificationMediaManager implements Dumpable {
getMediaControllerPlaybackState(aController)) {
if (DEBUG_MEDIA) {
Log.v(TAG, "DEBUG_MEDIA: found mediastyle controller matching "
- + entry.notification.getKey());
+ + entry.getSbn().getKey());
}
mediaNotification = entry;
controller = aController;
@@ -324,10 +321,10 @@ public class NotificationMediaManager implements Dumpable {
for (int i = 0; i < N; i++) {
final NotificationEntry entry = activeNotifications.get(i);
- if (entry.notification.getPackageName().equals(pkg)) {
+ if (entry.getSbn().getPackageName().equals(pkg)) {
if (DEBUG_MEDIA) {
Log.v(TAG, "DEBUG_MEDIA: found controller matching "
- + entry.notification.getKey());
+ + entry.getSbn().getKey());
}
controller = aController;
mediaNotification = entry;
@@ -354,8 +351,8 @@ public class NotificationMediaManager implements Dumpable {
}
if (mediaNotification != null
- && !mediaNotification.notification.getKey().equals(mMediaNotificationKey)) {
- mMediaNotificationKey = mediaNotification.notification.getKey();
+ && !mediaNotification.getSbn().getKey().equals(mMediaNotificationKey)) {
+ mMediaNotificationKey = mediaNotification.getSbn().getKey();
if (DEBUG_MEDIA) {
Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key="
+ mMediaNotificationKey);
@@ -364,7 +361,7 @@ public class NotificationMediaManager implements Dumpable {
}
if (metaDataChanged) {
- mEntryManager.updateNotifications();
+ mEntryManager.updateNotifications("NotificationMediaManager - metaDataChanged");
}
dispatchUpdateMediaMetaData(metaDataChanged, true /* allowEnterAnimation */);
@@ -461,7 +458,7 @@ public class NotificationMediaManager implements Dumpable {
boolean wakeAndUnlock = mBiometricUnlockController != null
&& mBiometricUnlockController.isWakeAndUnlock();
- if (mKeyguardMonitor.isLaunchTransitionFadingAway() || wakeAndUnlock) {
+ if (mKeyguardStateController.isLaunchTransitionFadingAway() || wakeAndUnlock) {
mBackdrop.setVisibility(View.INVISIBLE);
Trace.endSection();
return;
@@ -599,7 +596,7 @@ public class NotificationMediaManager implements Dumpable {
boolean cannotAnimateDoze = shadeController != null
&& shadeController.isDozing()
&& !ScrimState.AOD.getAnimateChange();
- boolean needsBypassFading = mKeyguardMonitor.isBypassFadingAnimation();
+ boolean needsBypassFading = mKeyguardStateController.isBypassFadingAnimation();
if (((mBiometricUnlockController != null && mBiometricUnlockController.getMode()
== BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
|| cannotAnimateDoze) && !needsBypassFading)
@@ -626,10 +623,12 @@ public class NotificationMediaManager implements Dumpable {
mBackdropBack.setImageDrawable(null);
mHandler.post(mHideBackdropFront);
});
- if (mKeyguardMonitor.isKeyguardFadingAway()) {
+ if (mKeyguardStateController.isKeyguardFadingAway()) {
mBackdrop.animate()
- .setDuration(mKeyguardMonitor.getShortenedFadingAwayDuration())
- .setStartDelay(mKeyguardMonitor.getKeyguardFadingAwayDelay())
+ .setDuration(
+ mKeyguardStateController.getShortenedFadingAwayDuration())
+ .setStartDelay(
+ mKeyguardStateController.getKeyguardFadingAwayDelay())
.setInterpolator(Interpolators.LINEAR)
.start();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index c9050d492191..c838ac5315a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -17,8 +17,6 @@ package com.android.systemui.statusbar;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
-import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -52,6 +50,7 @@ import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.MainHandler;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -69,7 +68,6 @@ import java.util.Objects;
import java.util.Set;
import javax.inject.Inject;
-import javax.inject.Named;
import javax.inject.Singleton;
import dagger.Lazy;
@@ -262,7 +260,7 @@ public class NotificationRemoteInputManager implements Dumpable {
NotificationEntryManager notificationEntryManager,
Lazy<ShadeController> shadeController,
StatusBarStateController statusBarStateController,
- @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+ @MainHandler Handler mainHandler) {
mContext = context;
mLockscreenUserManager = lockscreenUserManager;
mSmartReplyController = smartReplyController;
@@ -293,7 +291,7 @@ public class NotificationRemoteInputManager implements Dumpable {
mSmartReplyController.stopSending(entry);
if (removedByUser && entry != null) {
- onPerformRemoveNotification(entry, entry.key);
+ onPerformRemoveNotification(entry, entry.getKey());
}
}
});
@@ -307,8 +305,8 @@ public class NotificationRemoteInputManager implements Dumpable {
@Override
public void onRemoteInputSent(NotificationEntry entry) {
if (FORCE_REMOTE_INPUT_HISTORY
- && isNotificationKeptForRemoteInputHistory(entry.key)) {
- mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key);
+ && isNotificationKeptForRemoteInputHistory(entry.getKey())) {
+ mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.getKey());
} else if (mEntriesKeptForRemoteInputActive.contains(entry)) {
// We're currently holding onto this notification, but from the apps point of
// view it is already canceled, so we'll need to cancel it on the apps behalf
@@ -316,18 +314,18 @@ public class NotificationRemoteInputManager implements Dumpable {
// bit.
mMainHandler.postDelayed(() -> {
if (mEntriesKeptForRemoteInputActive.remove(entry)) {
- mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key);
+ mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.getKey());
}
}, REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY);
}
try {
- mBarService.onNotificationDirectReplied(entry.notification.getKey());
+ mBarService.onNotificationDirectReplied(entry.getSbn().getKey());
if (entry.editedSuggestionInfo != null) {
boolean modifiedBeforeSending =
!TextUtils.equals(entry.remoteInputText,
entry.editedSuggestionInfo.originalText);
mBarService.onNotificationSmartReplySent(
- entry.notification.getKey(),
+ entry.getSbn().getKey(),
entry.editedSuggestionInfo.index,
entry.editedSuggestionInfo.originalText,
NotificationLogger
@@ -487,7 +485,7 @@ public class NotificationRemoteInputManager implements Dumpable {
NotificationEntry entry = mEntriesKeptForRemoteInputActive.valueAt(i);
mRemoteInputController.removeRemoteInput(entry, null);
if (mNotificationLifetimeFinishedCallback != null) {
- mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key);
+ mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.getKey());
}
}
mEntriesKeptForRemoteInputActive.clear();
@@ -501,14 +499,15 @@ public class NotificationRemoteInputManager implements Dumpable {
if (!FORCE_REMOTE_INPUT_HISTORY) {
return false;
}
- return (mRemoteInputController.isSpinning(entry.key) || entry.hasJustSentRemoteInput());
+ return (mRemoteInputController.isSpinning(entry.getKey())
+ || entry.hasJustSentRemoteInput());
}
public boolean shouldKeepForSmartReplyHistory(NotificationEntry entry) {
if (!FORCE_REMOTE_INPUT_HISTORY) {
return false;
}
- return mSmartReplyController.isSendingSmartReply(entry.key);
+ return mSmartReplyController.isSendingSmartReply(entry.getKey());
}
public void checkRemoteInputOutside(MotionEvent event) {
@@ -529,7 +528,7 @@ public class NotificationRemoteInputManager implements Dumpable {
@VisibleForTesting
StatusBarNotification rebuildNotificationWithRemoteInput(NotificationEntry entry,
CharSequence remoteInputText, boolean showSpinner) {
- StatusBarNotification sbn = entry.notification;
+ StatusBarNotification sbn = entry.getSbn();
Notification.Builder b = Notification.Builder
.recoverBuilder(mContext, sbn.getNotification().clone());
@@ -637,12 +636,12 @@ public class NotificationRemoteInputManager implements Dumpable {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Keeping notification around after sending remote input "
- + entry.key);
+ + entry.getKey());
}
- mKeysKeptForRemoteInputHistory.add(entry.key);
+ mKeysKeptForRemoteInputHistory.add(entry.getKey());
} else {
- mKeysKeptForRemoteInputHistory.remove(entry.key);
+ mKeysKeptForRemoteInputHistory.remove(entry.getKey());
}
}
}
@@ -675,12 +674,12 @@ public class NotificationRemoteInputManager implements Dumpable {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Keeping notification around after sending smart reply "
- + entry.key);
+ + entry.getKey());
}
- mKeysKeptForRemoteInputHistory.add(entry.key);
+ mKeysKeptForRemoteInputHistory.add(entry.getKey());
} else {
- mKeysKeptForRemoteInputHistory.remove(entry.key);
+ mKeysKeptForRemoteInputHistory.remove(entry.getKey());
mSmartReplyController.stopSending(entry);
}
}
@@ -701,7 +700,7 @@ public class NotificationRemoteInputManager implements Dumpable {
if (shouldExtend) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Keeping notification around while remote input active "
- + entry.key);
+ + entry.getKey());
}
mEntriesKeptForRemoteInputActive.add(entry);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
index 266fe8dc2708..564d8bc14c8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
@@ -54,7 +54,7 @@ public class NotificationUiAdjustment {
public static NotificationUiAdjustment extractFromNotificationEntry(
NotificationEntry entry) {
return new NotificationUiAdjustment(
- entry.key, entry.getSmartActions(), entry.getSmartReplies());
+ entry.getKey(), entry.getSmartActions(), entry.getSmartReplies());
}
public static boolean needReinflate(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 50d9bae77bea..d6b87afc53b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -16,19 +16,19 @@
package com.android.systemui.statusbar;
-import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
-
import android.content.Context;
import android.content.res.Resources;
import android.os.Handler;
import android.os.Trace;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import com.android.systemui.R;
import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.dagger.qualifiers.MainHandler;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -47,7 +47,6 @@ import java.util.List;
import java.util.Stack;
import javax.inject.Inject;
-import javax.inject.Named;
import javax.inject.Singleton;
import dagger.Lazy;
@@ -88,6 +87,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
private final BubbleController mBubbleController;
private final DynamicPrivacyController mDynamicPrivacyController;
private final KeyguardBypassController mBypassController;
+ private final Context mContext;
private NotificationPresenter mPresenter;
private NotificationListContainer mListContainer;
@@ -99,8 +99,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
private boolean mIsHandleDynamicPrivacyChangeScheduled;
@Inject
- public NotificationViewHierarchyManager(Context context,
- @Named(MAIN_HANDLER_NAME) Handler mainHandler,
+ public NotificationViewHierarchyManager(Context context, @MainHandler Handler mainHandler,
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationGroupManager groupManager,
VisualStabilityManager visualStabilityManager,
@@ -110,6 +109,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
KeyguardBypassController bypassController,
BubbleController bubbleController,
DynamicPrivacyController privacyController) {
+ mContext = context;
mHandler = mainHandler;
mLockscreenUserManager = notificationLockscreenUserManager;
mBypassController = bypassController;
@@ -146,14 +146,18 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
final int N = activeNotifications.size();
for (int i = 0; i < N; i++) {
NotificationEntry ent = activeNotifications.get(i);
+ int flag = Settings.System.getInt(mContext.getContentResolver(),
+ "qs_media_player", 0);
+ boolean hideMedia = (flag == 1);
if (ent.isRowDismissed() || ent.isRowRemoved()
- || mBubbleController.isBubbleNotificationSuppressedFromShade(ent.key)) {
+ || (ent.isMediaNotification() && hideMedia)
+ || mBubbleController.isBubbleNotificationSuppressedFromShade(ent.getKey())) {
// we don't want to update removed notifications because they could
// temporarily become children if they were isolated before.
continue;
}
- int userId = ent.notification.getUserId();
+ int userId = ent.getSbn().getUserId();
// Display public version of the notification if we need to redact.
// TODO: This area uses a lot of calls into NotificationLockscreenUserManager.
@@ -174,8 +178,8 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
currentUserId);
ent.setSensitive(sensitive, deviceSensitive);
ent.getRow().setNeedsRedaction(needsRedaction);
- if (mGroupManager.isChildInGroupWithSummary(ent.notification)) {
- NotificationEntry summary = mGroupManager.getGroupSummary(ent.notification);
+ if (mGroupManager.isChildInGroupWithSummary(ent.getSbn())) {
+ NotificationEntry summary = mGroupManager.getGroupSummary(ent.getSbn());
List<ExpandableNotificationRow> orderedChildren =
mTmpChildOrderMap.get(summary.getRow());
if (orderedChildren == null) {
@@ -382,7 +386,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
ExpandableNotificationRow row = stack.pop();
NotificationEntry entry = row.getEntry();
boolean isChildNotification =
- mGroupManager.isChildInGroupWithSummary(entry.notification);
+ mGroupManager.isChildInGroupWithSummary(entry.getSbn());
row.setOnKeyguard(onKeyguard);
@@ -394,15 +398,15 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
&& !row.isLowPriority()));
}
- int userId = entry.notification.getUserId();
+ int userId = entry.getSbn().getUserId();
boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
- entry.notification) && !entry.isRowRemoved();
+ entry.getSbn()) && !entry.isRowRemoved();
boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry);
if (!showOnKeyguard) {
// min priority notifications should show if their summary is showing
- if (mGroupManager.isChildInGroupWithSummary(entry.notification)) {
+ if (mGroupManager.isChildInGroupWithSummary(entry.getSbn())) {
NotificationEntry summary = mGroupManager.getLogicalGroupSummary(
- entry.notification);
+ entry.getSbn());
if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard(summary)) {
showOnKeyguard = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index a70dc7c0ec5d..e516af590e34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -27,7 +27,6 @@ import android.os.SystemClock
import android.view.MotionEvent
import android.view.VelocityTracker
import android.view.ViewConfiguration
-import com.android.systemui.Dependency
import com.android.systemui.Gefingerpoken
import com.android.systemui.Interpolators
@@ -58,7 +57,8 @@ constructor(
private val bypassController: KeyguardBypassController,
private val headsUpManager: HeadsUpManagerPhone,
private val roundnessManager: NotificationRoundnessManager,
- private val statusBarStateController: StatusBarStateController
+ private val statusBarStateController: StatusBarStateController,
+ private val falsingManager: FalsingManager
) : Gefingerpoken {
companion object {
private val RUBBERBAND_FACTOR_STATIC = 0.25f
@@ -99,7 +99,6 @@ constructor(
private val mTemp2 = IntArray(2)
private var mDraggedFarEnough: Boolean = false
private var mStartingChild: ExpandableView? = null
- private val mFalsingManager: FalsingManager
private var mPulsing: Boolean = false
var isWakingToShadeLocked: Boolean = false
private set
@@ -109,7 +108,7 @@ constructor(
private var velocityTracker: VelocityTracker? = null
private val isFalseTouch: Boolean
- get() = mFalsingManager.isFalseTouch
+ get() = falsingManager.isFalseTouch
var qsExpanded: Boolean = false
var pulseExpandAbortListener: Runnable? = null
var bouncerShowing: Boolean = false
@@ -118,7 +117,6 @@ constructor(
mMinDragDistance = context.resources.getDimensionPixelSize(
R.dimen.keyguard_drag_down_min_distance)
mTouchSlop = ViewConfiguration.get(context).scaledTouchSlop.toFloat()
- mFalsingManager = Dependency.get(FalsingManager::class.java)
mPowerManager = context.getSystemService(PowerManager::class.java)
}
@@ -151,7 +149,7 @@ constructor(
MotionEvent.ACTION_MOVE -> {
val h = y - mInitialTouchY
if (h > mTouchSlop && h > Math.abs(x - mInitialTouchX)) {
- mFalsingManager.onStartExpandingFromPulse()
+ falsingManager.onStartExpandingFromPulse()
isExpanding = true
captureStartingChild(mInitialTouchX, mInitialTouchY)
mInitialTouchY = y
@@ -192,7 +190,7 @@ constructor(
velocityTracker!!.computeCurrentVelocity(1000 /* units */)
val canExpand = moveDistance > 0 && velocityTracker!!.getYVelocity() > -1000 &&
statusBarStateController.state != StatusBarState.SHADE
- if (!mFalsingManager.isUnlockingDisabled && !isFalseTouch && canExpand) {
+ if (!falsingManager.isUnlockingDisabled && !isFalseTouch && canExpand) {
finishExpansion()
} else {
cancelExpansion()
@@ -297,7 +295,7 @@ constructor(
private fun cancelExpansion() {
isExpanding = false
- mFalsingManager.onExpansionFromPulseStopped()
+ falsingManager.onExpansionFromPulseStopped()
if (mStartingChild != null) {
reset(mStartingChild!!)
mStartingChild = null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index 736b9ebea5c3..7bdb21d0eac5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -58,9 +58,9 @@ public class SmartReplyController {
public void smartReplySent(NotificationEntry entry, int replyIndex, CharSequence reply,
int notificationLocation, boolean modifiedBeforeSending) {
mCallback.onSmartReplySent(entry, reply);
- mSendingKeys.add(entry.key);
+ mSendingKeys.add(entry.getKey());
try {
- mBarService.onNotificationSmartReplySent(entry.notification.getKey(), replyIndex, reply,
+ mBarService.onNotificationSmartReplySent(entry.getSbn().getKey(), replyIndex, reply,
notificationLocation, modifiedBeforeSending);
} catch (RemoteException e) {
// Nothing to do, system going down
@@ -74,14 +74,14 @@ public class SmartReplyController {
NotificationEntry entry, int actionIndex, Notification.Action action,
boolean generatedByAssistant) {
final int count = mEntryManager.getNotificationData().getActiveNotifications().size();
- final int rank = mEntryManager.getNotificationData().getRank(entry.key);
+ final int rank = mEntryManager.getNotificationData().getRank(entry.getKey());
NotificationVisibility.NotificationLocation location =
NotificationLogger.getNotificationLocation(entry);
final NotificationVisibility nv = NotificationVisibility.obtain(
- entry.key, rank, count, true, location);
+ entry.getKey(), rank, count, true, location);
try {
mBarService.onNotificationActionClick(
- entry.key, actionIndex, action, nv, generatedByAssistant);
+ entry.getKey(), actionIndex, action, nv, generatedByAssistant);
} catch (RemoteException e) {
// Nothing to do, system going down
}
@@ -101,7 +101,7 @@ public class SmartReplyController {
public void smartSuggestionsAdded(final NotificationEntry entry, int replyCount,
int actionCount, boolean generatedByAssistant, boolean editBeforeSending) {
try {
- mBarService.onNotificationSmartSuggestionsAdded(entry.notification.getKey(), replyCount,
+ mBarService.onNotificationSmartSuggestionsAdded(entry.getSbn().getKey(), replyCount,
actionCount, generatedByAssistant, editBeforeSending);
} catch (RemoteException e) {
// Nothing to do, system going down
@@ -110,7 +110,7 @@ public class SmartReplyController {
public void stopSending(final NotificationEntry entry) {
if (entry != null) {
- mSendingKeys.remove(entry.notification.getKey());
+ mSendingKeys.remove(entry.getSbn().getKey());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 8b9268e1888a..e81e5cae5bfc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -21,7 +21,6 @@ import android.animation.ValueAnimator;
import android.text.format.DateFormat;
import android.util.FloatProperty;
import android.util.Log;
-import android.view.View;
import android.view.animation.Interpolator;
import com.android.internal.annotations.GuardedBy;
@@ -80,9 +79,14 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll
private HistoricalState[] mHistoricalRecords = new HistoricalState[HISTORY_SIZE];
/**
- * Current SystemUiVisibility
+ * If any of the system bars is hidden.
*/
- private int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
+ private boolean mIsFullscreen = false;
+
+ /**
+ * If the navigation bar can stay hidden when the display gets tapped.
+ */
+ private boolean mIsImmersive = false;
/**
* If the device is currently pulsing (AOD2).
@@ -320,12 +324,13 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll
}
@Override
- public void setSystemUiVisibility(int visibility) {
- if (mSystemUiVisibility != visibility) {
- mSystemUiVisibility = visibility;
+ public void setFullscreenState(boolean isFullscreen, boolean isImmersive) {
+ if (mIsFullscreen != isFullscreen || mIsImmersive != isImmersive) {
+ mIsFullscreen = isFullscreen;
+ mIsImmersive = isImmersive;
synchronized (mListeners) {
for (RankedListener rl : new ArrayList<>(mListeners)) {
- rl.mListener.onSystemUiVisibilityChanged(mSystemUiVisibility);
+ rl.mListener.onFullscreenStateChanged(isFullscreen, isImmersive);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
index 2ad979ab64e3..07b35502478f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
@@ -111,9 +111,9 @@ public interface SysuiStatusBarStateController extends StatusBarStateController
boolean isKeyguardRequested();
/**
- * Set systemui visibility
+ * Set the fullscreen state
*/
- void setSystemUiVisibility(int visibility);
+ void setFullscreenState(boolean isFullscreen, boolean isImmersive);
/**
* Set pulsing
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 13c6f2730d08..9b312341c583 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -38,7 +38,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.NotificationPanelView;
-import com.android.systemui.statusbar.phone.StatusBarWindowView;
+import com.android.systemui.statusbar.phone.StatusBarWindowViewController;
/**
* A class that allows activities to be launched in a seamless way where the notification
@@ -55,8 +55,8 @@ public class ActivityLaunchAnimator {
private static final long LAUNCH_TIMEOUT = 500;
private final NotificationPanelView mNotificationPanel;
private final NotificationListContainer mNotificationContainer;
- private final StatusBarWindowView mStatusBarWindow;
private final float mWindowCornerRadius;
+ private final StatusBarWindowViewController mStatusBarWindowViewController;
private Callback mCallback;
private final Runnable mTimeoutRunnable = () -> {
setAnimationPending(false);
@@ -66,16 +66,17 @@ public class ActivityLaunchAnimator {
private boolean mAnimationRunning;
private boolean mIsLaunchForActivity;
- public ActivityLaunchAnimator(StatusBarWindowView statusBarWindow,
+ public ActivityLaunchAnimator(
+ StatusBarWindowViewController statusBarWindowViewController,
Callback callback,
NotificationPanelView notificationPanel,
NotificationListContainer container) {
mNotificationPanel = notificationPanel;
mNotificationContainer = container;
- mStatusBarWindow = statusBarWindow;
+ mStatusBarWindowViewController = statusBarWindowViewController;
mCallback = callback;
mWindowCornerRadius = ScreenDecorationsUtils
- .getWindowCornerRadius(statusBarWindow.getResources());
+ .getWindowCornerRadius(mStatusBarWindowViewController.getView().getResources());
}
public RemoteAnimationAdapter getLaunchAnimation(
@@ -112,11 +113,11 @@ public class ActivityLaunchAnimator {
private void setAnimationPending(boolean pending) {
mAnimationPending = pending;
- mStatusBarWindow.setExpandAnimationPending(pending);
+ mStatusBarWindowViewController.setExpandAnimationPending(pending);
if (pending) {
- mStatusBarWindow.postDelayed(mTimeoutRunnable, LAUNCH_TIMEOUT);
+ mStatusBarWindowViewController.getView().postDelayed(mTimeoutRunnable, LAUNCH_TIMEOUT);
} else {
- mStatusBarWindow.removeCallbacks(mTimeoutRunnable);
+ mStatusBarWindowViewController.getView().removeCallbacks(mTimeoutRunnable);
}
}
@@ -246,7 +247,7 @@ public class ActivityLaunchAnimator {
private void setExpandAnimationRunning(boolean running) {
mNotificationPanel.setLaunchingNotification(running);
mSourceNotification.setExpandAnimationRunning(running);
- mStatusBarWindow.setExpandAnimationRunning(running);
+ mStatusBarWindowViewController.setExpandAnimationRunning(running);
mNotificationContainer.setExpandingNotification(running ? mSourceNotification : null);
mAnimationRunning = running;
if (!running) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
index e4bd4fa1ae75..a0b144b4497c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
@@ -16,16 +16,13 @@
package com.android.systemui.statusbar.notification;
-import android.content.Context;
import android.util.ArraySet;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.UnlockMethodCache;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -34,12 +31,11 @@ import javax.inject.Singleton;
* A controller which dynamically controls the visibility of Notification content
*/
@Singleton
-public class DynamicPrivacyController implements UnlockMethodCache.OnUnlockMethodChangedListener {
+public class DynamicPrivacyController implements KeyguardStateController.Callback {
- private final UnlockMethodCache mUnlockMethodCache;
+ private final KeyguardStateController mKeyguardStateController;
private final NotificationLockscreenUserManager mLockscreenUserManager;
private final StatusBarStateController mStateController;
- private final KeyguardMonitor mKeyguardMonitor;
private ArraySet<Listener> mListeners = new ArraySet<>();
private boolean mLastDynamicUnlocked;
@@ -47,30 +43,18 @@ public class DynamicPrivacyController implements UnlockMethodCache.OnUnlockMetho
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Inject
- DynamicPrivacyController(Context context,
- KeyguardMonitor keyguardMonitor,
- NotificationLockscreenUserManager notificationLockscreenUserManager,
- StatusBarStateController stateController) {
- this(notificationLockscreenUserManager, keyguardMonitor,
- UnlockMethodCache.getInstance(context),
- stateController);
- }
-
- @VisibleForTesting
DynamicPrivacyController(NotificationLockscreenUserManager notificationLockscreenUserManager,
- KeyguardMonitor keyguardMonitor,
- UnlockMethodCache unlockMethodCache,
+ KeyguardStateController keyguardStateController,
StatusBarStateController stateController) {
mLockscreenUserManager = notificationLockscreenUserManager;
mStateController = stateController;
- mUnlockMethodCache = unlockMethodCache;
- mKeyguardMonitor = keyguardMonitor;
- mUnlockMethodCache.addListener(this);
+ mKeyguardStateController = keyguardStateController;
+ mKeyguardStateController.addCallback(this);
mLastDynamicUnlocked = isDynamicallyUnlocked();
}
@Override
- public void onUnlockMethodStateChanged() {
+ public void onUnlockedChanged() {
if (isDynamicPrivacyEnabled()) {
// We only want to notify our listeners if dynamic privacy is actually active
boolean dynamicallyUnlocked = isDynamicallyUnlocked();
@@ -92,8 +76,9 @@ public class DynamicPrivacyController implements UnlockMethodCache.OnUnlockMetho
}
public boolean isDynamicallyUnlocked() {
- return (mUnlockMethodCache.canSkipBouncer() || mKeyguardMonitor.isKeyguardGoingAway()
- || mKeyguardMonitor.isKeyguardFadingAway())
+ return (mKeyguardStateController.canDismissLockScreen()
+ || mKeyguardStateController.isKeyguardGoingAway()
+ || mKeyguardStateController.isKeyguardFadingAway())
&& isDynamicPrivacyEnabled();
}
@@ -107,7 +92,7 @@ public class DynamicPrivacyController implements UnlockMethodCache.OnUnlockMetho
*/
public boolean isInLockedDownShade() {
if (!mStatusBarKeyguardViewManager.isShowing()
- || !mUnlockMethodCache.isMethodSecure()) {
+ || !mKeyguardStateController.isMethodSecure()) {
return false;
}
int state = mStateController.getState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index f3201ec73d63..f284f737b26d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -31,6 +31,7 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.SynchronousUserSwitchObserver;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
@@ -55,16 +56,16 @@ import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.SystemUI;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.NotificationChannels;
import java.util.List;
-/** The clsss to show notification(s) of instant apps. This may show multiple notifications on
+/** The class to show notification(s) of instant apps. This may show multiple notifications on
* splitted screen.
*/
public class InstantAppNotifier extends SystemUI
- implements CommandQueue.Callbacks, KeyguardMonitor.Callback {
+ implements CommandQueue.Callbacks, KeyguardStateController.Callback {
private static final String TAG = "InstantAppNotifier";
public static final int NUM_TASKS_FOR_INSTANT_APP_INFO = 5;
@@ -72,13 +73,15 @@ public class InstantAppNotifier extends SystemUI
private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
private final ArraySet<Pair<String, Integer>> mCurrentNotifs = new ArraySet<>();
private boolean mDockedStackExists;
- private KeyguardMonitor mKeyguardMonitor;
+ private KeyguardStateController mKeyguardStateController;
- public InstantAppNotifier() {}
+ public InstantAppNotifier(Context context) {
+ super(context);
+ }
@Override
public void start() {
- mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+ mKeyguardStateController = Dependency.get(KeyguardStateController.class);
// listen for user / profile change.
try {
@@ -88,7 +91,7 @@ public class InstantAppNotifier extends SystemUI
}
SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallback(this);
- mKeyguardMonitor.addCallback(this);
+ mKeyguardStateController.addCallback(this);
DockedStackExistsListener.register(
exists -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NewNotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NewNotifPipeline.java
new file mode 100644
index 000000000000..31921a436747
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NewNotifPipeline.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import android.util.Log;
+
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Initialization code for the new notification pipeline.
+ */
+@Singleton
+public class NewNotifPipeline {
+ private final NotifCollection mNotifCollection;
+
+ @Inject
+ public NewNotifPipeline(
+ NotifCollection notifCollection) {
+ mNotifCollection = notifCollection;
+ }
+
+ /** Hooks the new pipeline up to NotificationManager */
+ public void initialize(
+ NotificationListener notificationService) {
+ mNotifCollection.attach(notificationService);
+
+ Log.d(TAG, "Notif pipeline initialized");
+ }
+
+ private static final String TAG = "NewNotifPipeline";
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
index d71d40781f2e..005f01dc8df0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
@@ -80,7 +80,7 @@ public class NotificationAlertingManager {
NotificationEntry entry,
NotificationVisibility visibility,
boolean removedByUser) {
- stopAlerting(entry.key);
+ stopAlerting(entry.getKey());
}
});
}
@@ -104,7 +104,7 @@ public class NotificationAlertingManager {
mHeadsUpManager.showNotification(entry);
if (!mShadeController.get().isDozing()) {
// Mark as seen immediately
- setNotificationShown(entry.notification);
+ setNotificationShown(entry.getSbn());
}
} else {
entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
@@ -113,16 +113,16 @@ public class NotificationAlertingManager {
}
private void updateAlertState(NotificationEntry entry) {
- boolean alertAgain = alertAgain(entry, entry.notification.getNotification());
+ boolean alertAgain = alertAgain(entry, entry.getSbn().getNotification());
boolean shouldAlert;
shouldAlert = mNotificationInterruptionStateProvider.shouldHeadsUp(entry);
- final boolean wasAlerting = mHeadsUpManager.isAlerting(entry.key);
+ final boolean wasAlerting = mHeadsUpManager.isAlerting(entry.getKey());
if (wasAlerting) {
if (shouldAlert) {
- mHeadsUpManager.updateNotification(entry.key, alertAgain);
- } else if (!mHeadsUpManager.isEntryAutoHeadsUpped(entry.key)) {
+ mHeadsUpManager.updateNotification(entry.getKey(), alertAgain);
+ } else if (!mHeadsUpManager.isEntryAutoHeadsUpped(entry.getKey())) {
// We don't want this to be interrupting anymore, let's remove it
- mHeadsUpManager.removeNotification(entry.key, false /* removeImmediately */);
+ mHeadsUpManager.removeNotification(entry.getKey(), false /* removeImmediately */);
}
} else if (shouldAlert && alertAgain) {
// This notification was updated to be alerting, show it!
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 b6b149dd049a..b4dc538f8e0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -20,8 +20,8 @@ import static android.service.notification.NotificationListenerService.REASON_ER
import android.annotation.Nullable;
import android.app.Notification;
-import android.content.Context;
import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.Log;
@@ -40,6 +40,8 @@ import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.logging.NotifEvent;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
@@ -92,6 +94,7 @@ public class NotificationEntryManager implements
private NotificationListenerService.RankingMap mLatestRankingMap;
@VisibleForTesting
protected NotificationData mNotificationData;
+ private NotifLog mNotifLog;
@VisibleForTesting
final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
@@ -107,7 +110,7 @@ public class NotificationEntryManager implements
pw.println("null");
} else {
for (NotificationEntry entry : mPendingNotifications.values()) {
- pw.println(entry.notification);
+ pw.println(entry.getSbn());
}
}
pw.println(" Lifetime-extended notifications:");
@@ -116,15 +119,18 @@ public class NotificationEntryManager implements
} else {
for (Map.Entry<NotificationEntry, NotificationLifetimeExtender> entry
: mRetainedNotifications.entrySet()) {
- pw.println(" " + entry.getKey().notification + " retained by "
+ pw.println(" " + entry.getKey().getSbn() + " retained by "
+ entry.getValue().getClass().getName());
}
}
}
@Inject
- public NotificationEntryManager(Context context) {
- mNotificationData = new NotificationData(context);
+ public NotificationEntryManager(
+ NotificationData notificationData,
+ NotifLog notifLog) {
+ mNotificationData = notificationData;
+ mNotifLog = notifLog;
}
/** Adds a {@link NotificationEntryListener}. */
@@ -132,6 +138,14 @@ public class NotificationEntryManager implements
mNotificationEntryListeners.add(listener);
}
+ /**
+ * Removes a {@link NotificationEntryListener} previously registered via
+ * {@link #addNotificationEntryListener(NotificationEntryListener)}.
+ */
+ public void removeNotificationEntryListener(NotificationEntryListener listener) {
+ mNotificationEntryListeners.remove(listener);
+ }
+
/** Sets the {@link NotificationRemoveInterceptor}. */
public void setNotificationRemoveInterceptor(NotificationRemoveInterceptor interceptor) {
mRemoveInterceptor = interceptor;
@@ -178,7 +192,7 @@ public class NotificationEntryManager implements
@Override
public void onReorderingAllowed() {
- updateNotifications();
+ updateNotifications("reordering is now allowed");
}
/**
@@ -203,15 +217,19 @@ public class NotificationEntryManager implements
return NotificationVisibility.obtain(key, rank, count, true, location);
}
- private void abortExistingInflation(String key) {
+ private void abortExistingInflation(String key, String reason) {
if (mPendingNotifications.containsKey(key)) {
NotificationEntry entry = mPendingNotifications.get(key);
entry.abortTask();
mPendingNotifications.remove(key);
+ mNotifLog.log(NotifEvent.INFLATION_ABORTED, entry.getSbn(), null,
+ "PendingNotification aborted. " + reason);
}
NotificationEntry addedEntry = mNotificationData.get(key);
if (addedEntry != null) {
addedEntry.abortTask();
+ mNotifLog.log(NotifEvent.INFLATION_ABORTED, addedEntry.getSbn(),
+ null, reason);
}
}
@@ -234,11 +252,11 @@ public class NotificationEntryManager implements
@Override
public void onAsyncInflationFinished(NotificationEntry entry,
@InflationFlag int inflatedFlags) {
- mPendingNotifications.remove(entry.key);
+ mPendingNotifications.remove(entry.getKey());
// If there was an async task started after the removal, we don't want to add it back to
// the list, otherwise we might get leaks.
if (!entry.isRowRemoved()) {
- boolean isNew = mNotificationData.get(entry.key) == null;
+ boolean isNew = mNotificationData.get(entry.getKey()) == null;
if (isNew) {
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onEntryInflated(entry, inflatedFlags);
@@ -247,7 +265,7 @@ public class NotificationEntryManager implements
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onBeforeNotificationAdded(entry);
}
- updateNotifications();
+ updateNotifications("onAsyncInflationFinished");
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onNotificationAdded(entry);
}
@@ -276,7 +294,8 @@ public class NotificationEntryManager implements
if (mRemoveInterceptor != null
&& mRemoveInterceptor.onNotificationRemoveRequested(key, reason)) {
- // Remove intercepted; skip
+ // Remove intercepted; log and skip
+ mNotifLog.log(NotifEvent.REMOVE_INTERCEPTED);
return;
}
@@ -291,13 +310,17 @@ public class NotificationEntryManager implements
if (extender.shouldExtendLifetimeForPendingNotification(pendingEntry)) {
extendLifetime(pendingEntry, extender);
lifetimeExtended = true;
+ mNotifLog.log(
+ NotifEvent.LIFETIME_EXTENDED,
+ pendingEntry.getSbn(),
+ "pendingEntry extendedBy=" + extender.toString());
}
}
}
}
if (!lifetimeExtended) {
- abortExistingInflation(key);
+ abortExistingInflation(key, "removeNotification");
}
if (entry != null) {
@@ -310,6 +333,10 @@ public class NotificationEntryManager implements
mLatestRankingMap = ranking;
extendLifetime(entry, extender);
lifetimeExtended = true;
+ mNotifLog.log(
+ NotifEvent.LIFETIME_EXTENDED,
+ entry.getSbn(),
+ "entry extendedBy=" + extender.toString());
break;
}
}
@@ -329,10 +356,12 @@ public class NotificationEntryManager implements
handleGroupSummaryRemoved(key);
mNotificationData.remove(key, ranking);
- updateNotifications();
+ updateNotifications("removeNotificationInternal");
Dependency.get(LeakDetector.class).trackGarbage(entry);
removedByUser |= entryDismissed;
+ mNotifLog.log(NotifEvent.NOTIF_REMOVED, entry.getSbn(),
+ "removedByUser=" + removedByUser);
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onEntryRemoved(entry, visibility, removedByUser);
}
@@ -353,7 +382,7 @@ public class NotificationEntryManager implements
private void handleGroupSummaryRemoved(String key) {
NotificationEntry entry = mNotificationData.get(key);
if (entry != null && entry.rowExists() && entry.isSummaryWithChildren()) {
- if (entry.notification.getOverrideGroupKey() != null && !entry.isRowDismissed()) {
+ if (entry.getSbn().getOverrideGroupKey() != null && !entry.isRowDismissed()) {
// We don't want to remove children for autobundled notifications as they are not
// always cancelled. We only remove them if they were dismissed by the user.
return;
@@ -364,7 +393,7 @@ public class NotificationEntryManager implements
}
for (int i = 0; i < childEntries.size(); i++) {
NotificationEntry childEntry = childEntries.get(i);
- boolean isForeground = (entry.notification.getNotification().flags
+ boolean isForeground = (entry.getSbn().getNotification().flags
& Notification.FLAG_FOREGROUND_SERVICE) != 0;
boolean keepForReply =
getRemoteInputManager().shouldKeepForRemoteInputHistory(childEntry)
@@ -389,8 +418,8 @@ public class NotificationEntryManager implements
Log.d(TAG, "addNotification key=" + key);
}
- mNotificationData.updateRanking(rankingMap);
- NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
+ mNotificationData.updateRanking(rankingMap, "addNotificationInternal");
+ Ranking ranking = new Ranking();
rankingMap.getRanking(key, ranking);
NotificationEntry entry = new NotificationEntry(notification, ranking);
@@ -400,9 +429,9 @@ public class NotificationEntryManager implements
requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
REASON_CANCEL));
- abortExistingInflation(key);
-
+ abortExistingInflation(key, "addNotification");
mPendingNotifications.put(key, entry);
+ mNotifLog.log(NotifEvent.NOTIF_ADDED, entry.getSbn());
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onPendingEntryAdded(entry);
}
@@ -423,7 +452,7 @@ public class NotificationEntryManager implements
if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
final String key = notification.getKey();
- abortExistingInflation(key);
+ abortExistingInflation(key, "updateNotification");
NotificationEntry entry = mNotificationData.get(key);
if (entry == null) {
return;
@@ -433,15 +462,15 @@ public class NotificationEntryManager implements
// to keep its lifetime extended.
cancelLifetimeExtension(entry);
- mNotificationData.update(entry, ranking, notification);
-
+ mNotificationData.update(entry, ranking, notification, "updateNotificationInternal");
+ mNotifLog.log(NotifEvent.NOTIF_UPDATED, entry.getSbn(), entry.getRanking());
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onPreEntryUpdated(entry);
}
requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
REASON_CANCEL));
- updateNotifications();
+ updateNotifications("updateNotificationInternal");
if (DEBUG) {
// Is this for you?
@@ -465,8 +494,12 @@ public class NotificationEntryManager implements
}
}
- public void updateNotifications() {
- mNotificationData.filterAndSort();
+ /**
+ * Update the notifications
+ * @param reason why the notifications are updating
+ */
+ public void updateNotifications(String reason) {
+ mNotificationData.filterAndSort(reason);
if (mPresenter != null) {
mPresenter.updateNotificationViews();
}
@@ -484,24 +517,24 @@ public class NotificationEntryManager implements
for (NotificationEntry entry : entries) {
NotificationUiAdjustment adjustment =
NotificationUiAdjustment.extractFromNotificationEntry(entry);
- oldAdjustments.put(entry.key, adjustment);
- oldImportances.put(entry.key, entry.getImportance());
+ oldAdjustments.put(entry.getKey(), adjustment);
+ oldImportances.put(entry.getKey(), entry.getImportance());
}
// Populate notification entries from the new rankings.
- mNotificationData.updateRanking(rankingMap);
+ mNotificationData.updateRanking(rankingMap, "updateNotificationRanking");
updateRankingOfPendingNotifications(rankingMap);
// By comparing the old and new UI adjustments, reinflate the view accordingly.
for (NotificationEntry entry : entries) {
requireBinder().onNotificationRankingUpdated(
entry,
- oldImportances.get(entry.key),
- oldAdjustments.get(entry.key),
+ oldImportances.get(entry.getKey()),
+ oldAdjustments.get(entry.getKey()),
NotificationUiAdjustment.extractFromNotificationEntry(entry));
}
- updateNotifications();
+ updateNotifications("updateNotificationRanking");
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onNotificationRankingUpdated(rankingMap);
@@ -513,10 +546,11 @@ public class NotificationEntryManager implements
if (rankingMap == null) {
return;
}
- NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
for (NotificationEntry pendingNotification : mPendingNotifications.values()) {
- rankingMap.getRanking(pendingNotification.key, ranking);
- pendingNotification.setRanking(ranking);
+ Ranking ranking = new Ranking();
+ if (rankingMap.getRanking(pendingNotification.getKey(), ranking)) {
+ pendingNotification.setRanking(ranking);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
index 5a0b88cbdc28..b1164093acdd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
@@ -85,7 +85,7 @@ public class NotificationFilter {
* @return true if the provided notification should NOT be shown right now.
*/
public boolean shouldFilterOut(NotificationEntry entry) {
- final StatusBarNotification sbn = entry.notification;
+ final StatusBarNotification sbn = entry.getSbn();
if (!(getEnvironment().isDeviceProvisioned()
|| showNotificationEvenIfUnprovisioned(sbn))) {
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
index 9362d2d5e2c9..7d09932dcad0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
@@ -39,6 +39,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import javax.inject.Inject;
@@ -63,6 +64,7 @@ public class NotificationInterruptionStateProvider {
private final Context mContext;
private final PowerManager mPowerManager;
private final IDreamManager mDreamManager;
+ private final BatteryController mBatteryController;
private NotificationPresenter mPresenter;
private HeadsUpManager mHeadsUpManager;
@@ -75,13 +77,14 @@ public class NotificationInterruptionStateProvider {
@Inject
public NotificationInterruptionStateProvider(Context context, NotificationFilter filter,
- StatusBarStateController stateController) {
+ StatusBarStateController stateController, BatteryController batteryController) {
this(context,
(PowerManager) context.getSystemService(Context.POWER_SERVICE),
IDreamManager.Stub.asInterface(
ServiceManager.checkService(DreamService.DREAM_SERVICE)),
new AmbientDisplayConfiguration(context),
filter,
+ batteryController,
stateController);
}
@@ -92,10 +95,12 @@ public class NotificationInterruptionStateProvider {
IDreamManager dreamManager,
AmbientDisplayConfiguration ambientDisplayConfiguration,
NotificationFilter notificationFilter,
+ BatteryController batteryController,
StatusBarStateController statusBarStateController) {
mContext = context;
mPowerManager = powerManager;
mDreamManager = dreamManager;
+ mBatteryController = batteryController;
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
mNotificationFilter = notificationFilter;
mStatusBarStateController = statusBarStateController;
@@ -159,7 +164,7 @@ public class NotificationInterruptionStateProvider {
* @return true if the entry should bubble up, false otherwise
*/
public boolean shouldBubbleUp(NotificationEntry entry) {
- final StatusBarNotification sbn = entry.notification;
+ final StatusBarNotification sbn = entry.getSbn();
if (!canAlertCommon(entry)) {
return false;
@@ -211,7 +216,7 @@ public class NotificationInterruptionStateProvider {
}
private boolean shouldHeadsUpWhenAwake(NotificationEntry entry) {
- StatusBarNotification sbn = entry.notification;
+ StatusBarNotification sbn = entry.getSbn();
if (!mUseHeadsUp) {
if (DEBUG_HEADS_UP) {
@@ -284,7 +289,7 @@ public class NotificationInterruptionStateProvider {
* @return true if the entry should ambient pulse, false otherwise
*/
private boolean shouldHeadsUpWhenDozing(NotificationEntry entry) {
- StatusBarNotification sbn = entry.notification;
+ StatusBarNotification sbn = entry.getSbn();
if (!mAmbientDisplayConfiguration.pulseOnNotificationEnabled(UserHandle.USER_CURRENT)) {
if (DEBUG_HEADS_UP) {
@@ -293,6 +298,13 @@ public class NotificationInterruptionStateProvider {
return false;
}
+ if (mBatteryController.isAodPowerSave()) {
+ if (DEBUG_HEADS_UP) {
+ Log.d(TAG, "No pulsing: disabled by battery saver: " + sbn.getKey());
+ }
+ return false;
+ }
+
if (!canAlertCommon(entry)) {
if (DEBUG_HEADS_UP) {
Log.d(TAG, "No pulsing: notification shouldn't alert: " + sbn.getKey());
@@ -324,7 +336,7 @@ public class NotificationInterruptionStateProvider {
*/
@VisibleForTesting
public boolean canAlertCommon(NotificationEntry entry) {
- StatusBarNotification sbn = entry.notification;
+ StatusBarNotification sbn = entry.getSbn();
if (mNotificationFilter.shouldFilterOut(entry)) {
if (DEBUG || DEBUG_HEADS_UP) {
@@ -351,7 +363,7 @@ public class NotificationInterruptionStateProvider {
*/
@VisibleForTesting
public boolean canAlertAwakeCommon(NotificationEntry entry) {
- StatusBarNotification sbn = entry.notification;
+ StatusBarNotification sbn = entry.getSbn();
if (mPresenter.isDeviceInVrMode()) {
if (DEBUG_HEADS_UP) {
@@ -411,7 +423,7 @@ public class NotificationInterruptionStateProvider {
* @return {@code true} if we should launch the full screen intent
*/
public boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry) {
- return entry.notification.getNotification().fullScreenIntent != null
+ return entry.getSbn().getNotification().fullScreenIntent != null
&& (!shouldHeadsUp(entry)
|| mStatusBarStateController.getState() == StatusBarState.KEYGUARD);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
index 769cbb7b984c..533dfb6ee4aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
@@ -73,7 +73,7 @@ public class NotificationListController {
@Override
public void onBeforeNotificationAdded(NotificationEntry entry) {
- tagForeground(entry.notification);
+ tagForeground(entry.getSbn());
}
};
@@ -81,7 +81,7 @@ public class NotificationListController {
new DeviceProvisionedListener() {
@Override
public void onDeviceProvisionedChanged() {
- mEntryManager.updateNotifications();
+ mEntryManager.updateNotifications("device provisioned changed");
}
};
@@ -106,7 +106,7 @@ public class NotificationListController {
if (foregroundKey != null) {
mEntryManager
.getNotificationData().updateAppOp(appOp, uid, pkg, foregroundKey, showIcon);
- mEntryManager.updateNotifications();
+ mEntryManager.updateNotifications("app opp changed pkg=" + pkg);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
new file mode 100644
index 000000000000..009551168010
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification
+
+import android.content.Context
+import android.provider.DeviceConfig
+
+import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT
+import com.android.systemui.util.DeviceConfigProxy
+
+import javax.inject.Inject
+
+private var sUsePeopleFiltering: Boolean? = null
+
+/**
+ * Feature controller for the NOTIFICATIONS_USE_PEOPLE_FILTERING config.
+ */
+class NotificationSectionsFeatureManager @Inject constructor(
+ val proxy: DeviceConfigProxy,
+ val context: Context
+) {
+
+ fun isFilteringEnabled(): Boolean {
+ return usePeopleFiltering(proxy)
+ }
+
+ fun getNotificationBuckets(): IntArray {
+ return when {
+ isFilteringEnabled() ->
+ intArrayOf(BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT)
+ NotificationUtils.useNewInterruptionModel(context) ->
+ intArrayOf(BUCKET_ALERTING, BUCKET_SILENT)
+ else ->
+ intArrayOf(BUCKET_ALERTING)
+ }
+ }
+
+ fun getNumberOfBuckets(): Int {
+ return getNotificationBuckets().size
+ }
+
+ @VisibleForTesting
+ fun clearCache() {
+ sUsePeopleFiltering = null
+ }
+}
+
+private fun usePeopleFiltering(proxy: DeviceConfigProxy): Boolean {
+ if (sUsePeopleFiltering == null) {
+ sUsePeopleFiltering = proxy.getBoolean(
+ DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_USE_PEOPLE_FILTERING, true)
+ }
+
+ return sUsePeopleFiltering!!
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
index dfbbf987dd96..1af47dd0f4c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
@@ -36,7 +36,6 @@ public class NotificationUtils {
private static final int[] sLocationOffset = new int[2];
@Nullable private static Boolean sUseNewInterruptionModel = null;
- @Nullable private static Boolean sUsePeopleFiltering = null;
public static boolean isGrayscale(ImageView v, ContrastColorUtil colorUtil) {
Object isGrayscale = v.getTag(R.id.icon_is_grayscale);
@@ -88,17 +87,4 @@ public class NotificationUtils {
}
return sUseNewInterruptionModel;
}
-
- /**
- * Caches and returns the value of the people filtering setting. Cannot change except through
- * process restarts.
- */
- public static boolean usePeopleFiltering(Context context) {
- if (sUsePeopleFiltering == null) {
- sUsePeopleFiltering = context.getResources().getBoolean(
- R.bool.config_usePeopleFiltering);
- }
-
- return sUsePeopleFiltering;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 2f67f90a115e..8a23e3796e9b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -37,10 +37,10 @@ import javax.inject.Singleton
@Singleton
class NotificationWakeUpCoordinator @Inject constructor(
- private val mContext: Context,
private val mHeadsUpManagerPhone: HeadsUpManagerPhone,
private val statusBarStateController: StatusBarStateController,
- private val bypassController: KeyguardBypassController)
+ private val bypassController: KeyguardBypassController,
+ private val dozeParameters: DozeParameters)
: OnHeadsUpChangedListener, StatusBarStateController.StateListener,
PanelExpansionListener {
@@ -67,7 +67,6 @@ class NotificationWakeUpCoordinator @Inject constructor(
private var mVisibilityAmount = 0.0f
private var mLinearVisibilityAmount = 0.0f
private val mEntrySetToClearWhenFinished = mutableSetOf<NotificationEntry>()
- private val mDozeParameters: DozeParameters
private var pulseExpanding: Boolean = false
private val wakeUpListeners = arrayListOf<WakeUpListener>()
private var state: Int = StatusBarState.KEYGUARD
@@ -146,7 +145,6 @@ class NotificationWakeUpCoordinator @Inject constructor(
init {
mHeadsUpManagerPhone.addListener(this)
statusBarStateController.addCallback(this)
- mDozeParameters = DozeParameters.getInstance(mContext)
addListener(object : WakeUpListener {
override fun onFullyHiddenChanged(isFullyHidden: Boolean) {
if (isFullyHidden && mNotificationsVisibleForExpansion) {
@@ -377,7 +375,7 @@ class NotificationWakeUpCoordinator @Inject constructor(
}
private fun shouldAnimateVisibility() =
- mDozeParameters.getAlwaysOn() && !mDozeParameters.getDisplayNeedsBlanking()
+ dozeParameters.getAlwaysOn() && !dozeParameters.getDisplayNeedsBlanking()
interface WakeUpListener {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index 6fe4abee6133..1daf48492ea0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.notification;
-import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
-
import android.os.Handler;
import android.os.SystemClock;
import android.view.View;
@@ -25,6 +23,7 @@ import android.view.View;
import androidx.collection.ArraySet;
import com.android.systemui.Dumpable;
+import com.android.systemui.dagger.qualifiers.MainHandler;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -35,7 +34,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import javax.inject.Inject;
-import javax.inject.Named;
import javax.inject.Singleton;
/**
@@ -64,8 +62,7 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl
@Inject
public VisualStabilityManager(
- NotificationEntryManager notificationEntryManager,
- @Named(MAIN_HANDLER_NAME) Handler handler) {
+ NotificationEntryManager notificationEntryManager, @MainHandler Handler handler) {
mHandler = handler;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/DismissedByUserStats.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/DismissedByUserStats.java
new file mode 100644
index 000000000000..ecce6ea1b211
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/DismissedByUserStats.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import android.service.notification.NotificationStats.DismissalSentiment;
+import android.service.notification.NotificationStats.DismissalSurface;
+
+import com.android.internal.statusbar.NotificationVisibility;
+
+/** Information that must be supplied when dismissing a notification on the behalf of the user. */
+public class DismissedByUserStats {
+ public final @DismissalSurface int dismissalSurface;
+ public final @DismissalSentiment int dismissalSentiment;
+ public final NotificationVisibility notificationVisibility;
+
+ public DismissedByUserStats(
+ @DismissalSurface int dismissalSurface,
+ @DismissalSentiment int dismissalSentiment,
+ NotificationVisibility notificationVisibility) {
+ this.dismissalSurface = dismissalSurface;
+ this.dismissalSentiment = dismissalSentiment;
+ this.notificationVisibility = notificationVisibility;
+ }
+}
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
new file mode 100644
index 000000000000..b5513529d7ba
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
+import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
+import static android.service.notification.NotificationListenerService.REASON_CHANNEL_BANNED;
+import static android.service.notification.NotificationListenerService.REASON_CLICK;
+import static android.service.notification.NotificationListenerService.REASON_ERROR;
+import static android.service.notification.NotificationListenerService.REASON_GROUP_OPTIMIZATION;
+import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
+import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL;
+import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL_ALL;
+import static android.service.notification.NotificationListenerService.REASON_PACKAGE_BANNED;
+import static android.service.notification.NotificationListenerService.REASON_PACKAGE_CHANGED;
+import static android.service.notification.NotificationListenerService.REASON_PACKAGE_SUSPENDED;
+import static android.service.notification.NotificationListenerService.REASON_PROFILE_TURNED_OFF;
+import static android.service.notification.NotificationListenerService.REASON_SNOOZED;
+import static android.service.notification.NotificationListenerService.REASON_TIMEOUT;
+import static android.service.notification.NotificationListenerService.REASON_UNAUTOBUNDLED;
+import static android.service.notification.NotificationListenerService.REASON_USER_STOPPED;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.IntDef;
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.StatusBarNotification;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationListener.NotifServiceListener;
+import com.android.systemui.util.Assert;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Keeps a record of all of the "active" notifications, i.e. the notifications that are currently
+ * posted to the phone. This collection is unsorted, ungrouped, and unfiltered. Just because a
+ * notification appears in this collection doesn't mean that it's currently present in the shade
+ * (notifications can be hidden for a variety of reasons). Code that cares about what notifications
+ * are *visible* right now should register listeners later in the pipeline.
+ *
+ * Each notification is represented by a {@link NotificationEntry}, which is itself made up of two
+ * parts: a {@link StatusBarNotification} and a {@link Ranking}. When notifications are updated,
+ * their underlying SBNs and Rankings are swapped out, but the enclosing NotificationEntry (and its
+ * associated key) remain the same. In general, an SBN can only be updated when the notification is
+ * reposted by the source app; Rankings are updated much more often, usually every time there is an
+ * update from any kind from NotificationManager.
+ *
+ * In general, this collection closely mirrors the list maintained by NotificationManager, but it
+ * can occasionally diverge due to lifetime extenders (see
+ * {@link #addNotificationLifetimeExtender(NotifLifetimeExtender)}).
+ *
+ * Interested parties can register listeners
+ * ({@link #addCollectionListener(NotifCollectionListener)}) to be informed when notifications are
+ * added, updated, or removed.
+ */
+@MainThread
+@Singleton
+public class NotifCollection {
+ private final IStatusBarService mStatusBarService;
+
+ private final Map<String, NotificationEntry> mNotificationSet = new ArrayMap<>();
+ private final Collection<NotificationEntry> mReadOnlyNotificationSet =
+ Collections.unmodifiableCollection(mNotificationSet.values());
+
+ @Nullable private NotifListBuilder mListBuilder;
+ private final List<NotifCollectionListener> mNotifCollectionListeners = new ArrayList<>();
+ private final List<NotifLifetimeExtender> mLifetimeExtenders = new ArrayList<>();
+
+ private boolean mAttached = false;
+ private boolean mAmDispatchingToOtherCode;
+
+ @Inject
+ public NotifCollection(IStatusBarService statusBarService) {
+ Assert.isMainThread();
+ mStatusBarService = statusBarService;
+ }
+
+ /** Initializes the NotifCollection and registers it to receive notification events. */
+ public void attach(NotificationListener listenerService) {
+ Assert.isMainThread();
+ if (mAttached) {
+ throw new RuntimeException("attach() called twice");
+ }
+ mAttached = true;
+
+ listenerService.setDownstreamListener(mNotifServiceListener);
+ }
+
+ /**
+ * Sets the class responsible for converting the collection into the list of currently-visible
+ * notifications.
+ */
+ public void setListBuilder(NotifListBuilder listBuilder) {
+ Assert.isMainThread();
+ mListBuilder = listBuilder;
+ }
+
+ /**
+ * Returns the list of "active" notifications, i.e. the notifications that are currently posted
+ * to the phone. In general, this tracks closely to the list maintained by NotificationManager,
+ * but it can diverge slightly due to lifetime extenders.
+ *
+ * The returned list is read-only, unsorted, unfiltered, and ungrouped.
+ */
+ public Collection<NotificationEntry> getNotifs() {
+ Assert.isMainThread();
+ return mReadOnlyNotificationSet;
+ }
+
+ /**
+ * Registers a listener to be informed when notifications are added, removed or updated.
+ */
+ public void addCollectionListener(NotifCollectionListener listener) {
+ Assert.isMainThread();
+ mNotifCollectionListeners.add(listener);
+ }
+
+ /**
+ * Registers a lifetime extender. Lifetime extenders can cause notifications that have been
+ * dismissed or retracted to be temporarily retained in the collection.
+ */
+ public void addNotificationLifetimeExtender(NotifLifetimeExtender extender) {
+ Assert.isMainThread();
+ checkForReentrantCall();
+ if (mLifetimeExtenders.contains(extender)) {
+ throw new IllegalArgumentException("Extender " + extender + " already added.");
+ }
+ mLifetimeExtenders.add(extender);
+ extender.setCallback(this::onEndLifetimeExtension);
+ }
+
+ /**
+ * Dismiss a notification on behalf of the user.
+ */
+ public void dismissNotification(
+ NotificationEntry entry,
+ @CancellationReason int reason,
+ @NonNull DismissedByUserStats stats) {
+ Assert.isMainThread();
+ checkNotNull(stats);
+ checkForReentrantCall();
+
+ removeNotification(entry.getKey(), null, reason, stats);
+ }
+
+ private void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
+ Assert.isMainThread();
+
+ NotificationEntry entry = mNotificationSet.get(sbn.getKey());
+
+ if (entry == null) {
+ // A new notification!
+ Log.d(TAG, "POSTED " + sbn.getKey());
+
+ entry = new NotificationEntry(sbn, requireRanking(rankingMap, sbn.getKey()));
+ mNotificationSet.put(sbn.getKey(), entry);
+ applyRanking(rankingMap);
+
+ dispatchOnEntryAdded(entry);
+
+ } else {
+ // Update to an existing entry
+ Log.d(TAG, "UPDATED " + sbn.getKey());
+
+ // Notification is updated so it is essentially re-added and thus alive again. Don't
+ // need to keep its lifetime extended.
+ cancelLifetimeExtension(entry);
+
+ entry.setSbn(sbn);
+ applyRanking(rankingMap);
+
+ dispatchOnEntryUpdated(entry);
+ }
+
+ rebuildList();
+ }
+
+ private void onNotificationRemoved(
+ StatusBarNotification sbn,
+ @Nullable RankingMap rankingMap,
+ int reason) {
+ Assert.isMainThread();
+ Log.d(TAG, "REMOVED " + sbn.getKey() + " reason=" + reason);
+ removeNotification(sbn.getKey(), rankingMap, reason, null);
+ }
+
+ private void onNotificationRankingUpdate(RankingMap rankingMap) {
+ Assert.isMainThread();
+ applyRanking(rankingMap);
+ rebuildList();
+ }
+
+ private void removeNotification(
+ String key,
+ @Nullable RankingMap rankingMap,
+ @CancellationReason int reason,
+ DismissedByUserStats dismissedByUserStats) {
+
+ NotificationEntry entry = mNotificationSet.get(key);
+ if (entry == null) {
+ throw new IllegalStateException("No notification to remove with key " + key);
+ }
+
+ entry.mLifetimeExtenders.clear();
+ mAmDispatchingToOtherCode = true;
+ for (NotifLifetimeExtender extender : mLifetimeExtenders) {
+ if (extender.shouldExtendLifetime(entry, reason)) {
+ entry.mLifetimeExtenders.add(extender);
+ }
+ }
+ mAmDispatchingToOtherCode = false;
+
+ if (!isLifetimeExtended(entry)) {
+ mNotificationSet.remove(entry.getKey());
+
+ if (dismissedByUserStats != null) {
+ try {
+ mStatusBarService.onNotificationClear(
+ entry.getSbn().getPackageName(),
+ entry.getSbn().getTag(),
+ entry.getSbn().getId(),
+ entry.getSbn().getUser().getIdentifier(),
+ entry.getSbn().getKey(),
+ dismissedByUserStats.dismissalSurface,
+ dismissedByUserStats.dismissalSentiment,
+ dismissedByUserStats.notificationVisibility);
+ } catch (RemoteException e) {
+ // system process is dead if we're here.
+ }
+ }
+
+ if (rankingMap != null) {
+ applyRanking(rankingMap);
+ }
+
+ dispatchOnEntryRemoved(entry, reason, dismissedByUserStats != null /* removedByUser */);
+ }
+
+ rebuildList();
+ }
+
+ private void applyRanking(RankingMap rankingMap) {
+ for (NotificationEntry entry : mNotificationSet.values()) {
+ if (!isLifetimeExtended(entry)) {
+ Ranking ranking = requireRanking(rankingMap, entry.getKey());
+ entry.setRanking(ranking);
+ }
+ }
+ }
+
+ private void rebuildList() {
+ if (mListBuilder != null) {
+ mListBuilder.onBuildList(mReadOnlyNotificationSet);
+ }
+ }
+
+ private void onEndLifetimeExtension(NotifLifetimeExtender extender, NotificationEntry entry) {
+ Assert.isMainThread();
+ if (!mAttached) {
+ return;
+ }
+ checkForReentrantCall();
+
+ if (!entry.mLifetimeExtenders.remove(extender)) {
+ throw new IllegalStateException(
+ String.format(
+ "Cannot end lifetime extension for extender \"%s\" (%s)",
+ extender.getName(),
+ extender));
+ }
+
+ if (!isLifetimeExtended(entry)) {
+ // TODO: This doesn't need to be undefined -- we can set either EXTENDER_EXPIRED or
+ // save the original reason
+ removeNotification(entry.getKey(), null, REASON_UNKNOWN, null);
+ }
+ }
+
+ private void cancelLifetimeExtension(NotificationEntry entry) {
+ mAmDispatchingToOtherCode = true;
+ for (NotifLifetimeExtender extender : entry.mLifetimeExtenders) {
+ extender.cancelLifetimeExtension(entry);
+ }
+ mAmDispatchingToOtherCode = false;
+ entry.mLifetimeExtenders.clear();
+ }
+
+ private boolean isLifetimeExtended(NotificationEntry entry) {
+ return entry.mLifetimeExtenders.size() > 0;
+ }
+
+ private void checkForReentrantCall() {
+ if (mAmDispatchingToOtherCode) {
+ throw new IllegalStateException("Reentrant call detected");
+ }
+ }
+
+ private static Ranking requireRanking(RankingMap rankingMap, String key) {
+ // TODO: Modify RankingMap so that we don't have to make a copy here
+ Ranking ranking = new Ranking();
+ if (!rankingMap.getRanking(key, ranking)) {
+ throw new IllegalArgumentException("Ranking map doesn't contain key: " + key);
+ }
+ return ranking;
+ }
+
+ private void dispatchOnEntryAdded(NotificationEntry entry) {
+ mAmDispatchingToOtherCode = true;
+ if (mListBuilder != null) {
+ mListBuilder.onBeginDispatchToListeners();
+ }
+ for (NotifCollectionListener listener : mNotifCollectionListeners) {
+ listener.onEntryAdded(entry);
+ }
+ mAmDispatchingToOtherCode = false;
+ }
+
+ private void dispatchOnEntryUpdated(NotificationEntry entry) {
+ mAmDispatchingToOtherCode = true;
+ if (mListBuilder != null) {
+ mListBuilder.onBeginDispatchToListeners();
+ }
+ for (NotifCollectionListener listener : mNotifCollectionListeners) {
+ listener.onEntryUpdated(entry);
+ }
+ mAmDispatchingToOtherCode = false;
+ }
+
+ private void dispatchOnEntryRemoved(
+ NotificationEntry entry,
+ @CancellationReason int reason,
+ boolean removedByUser) {
+ mAmDispatchingToOtherCode = true;
+ if (mListBuilder != null) {
+ mListBuilder.onBeginDispatchToListeners();
+ }
+ for (NotifCollectionListener listener : mNotifCollectionListeners) {
+ listener.onEntryRemoved(entry, reason, removedByUser);
+ }
+ mAmDispatchingToOtherCode = false;
+ }
+
+ private final NotifServiceListener mNotifServiceListener = new NotifServiceListener() {
+ @Override
+ public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
+ NotifCollection.this.onNotificationPosted(sbn, rankingMap);
+ }
+
+ @Override
+ public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
+ NotifCollection.this.onNotificationRemoved(sbn, rankingMap, REASON_UNKNOWN);
+ }
+
+ @Override
+ public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
+ int reason) {
+ NotifCollection.this.onNotificationRemoved(sbn, rankingMap, reason);
+ }
+
+ @Override
+ public void onNotificationRankingUpdate(RankingMap rankingMap) {
+ NotifCollection.this.onNotificationRankingUpdate(rankingMap);
+ }
+ };
+
+ private static final String TAG = "NotifCollection";
+
+ @IntDef(prefix = { "REASON_" }, value = {
+ REASON_UNKNOWN,
+ REASON_CLICK,
+ REASON_CANCEL_ALL,
+ REASON_ERROR,
+ REASON_PACKAGE_CHANGED,
+ REASON_USER_STOPPED,
+ REASON_PACKAGE_BANNED,
+ REASON_APP_CANCEL,
+ REASON_APP_CANCEL_ALL,
+ REASON_LISTENER_CANCEL,
+ REASON_LISTENER_CANCEL_ALL,
+ REASON_GROUP_SUMMARY_CANCELED,
+ REASON_GROUP_OPTIMIZATION,
+ REASON_PACKAGE_SUSPENDED,
+ REASON_PROFILE_TURNED_OFF,
+ REASON_UNAUTOBUNDLED,
+ REASON_CHANNEL_BANNED,
+ REASON_SNOOZED,
+ REASON_TIMEOUT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface CancellationReason {}
+
+ public static final int REASON_UNKNOWN = 0;
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionListener.java
new file mode 100644
index 000000000000..032620e14336
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionListener.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
+
+/**
+ * Listener interface for {@link NotifCollection}.
+ */
+public interface NotifCollectionListener {
+ /**
+ * Called whenever a notification with a new key is posted.
+ */
+ default void onEntryAdded(NotificationEntry entry) {
+ }
+
+ /**
+ * Called whenever a notification with the same key as an existing notification is posted. By
+ * the time this listener is called, the entry's SBN and Ranking will already have been updated.
+ */
+ default void onEntryUpdated(NotificationEntry entry) {
+ }
+
+ /**
+ * Called immediately after a notification has been removed from the collection.
+ */
+ default void onEntryRemoved(
+ NotificationEntry entry,
+ @CancellationReason int reason,
+ boolean removedByUser) {
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLifetimeExtender.java
new file mode 100644
index 000000000000..2c7b13866c10
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLifetimeExtender.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
+
+/**
+ * A way for other code to temporarily extend the lifetime of a notification after it has been
+ * retracted. See {@link NotifCollection#addNotificationLifetimeExtender(NotifLifetimeExtender)}.
+ */
+public interface NotifLifetimeExtender {
+ /** Name to associate with this extender (for the purposes of debugging) */
+ String getName();
+
+ /**
+ * Called on the extender immediately after it has been registered. The extender should hang on
+ * to this callback and execute it whenever it no longer needs to extend the lifetime of a
+ * notification.
+ */
+ void setCallback(OnEndLifetimeExtensionCallback callback);
+
+ /**
+ * Called by the NotifCollection whenever a notification has been retracted (by the app) or
+ * dismissed (by the user). If the extender returns true, it is considered to be extending the
+ * lifetime of that notification. Lifetime-extended notifications are kept around until all
+ * active extenders expire their extension by calling onEndLifetimeExtension(). This method is
+ * called on all lifetime extenders even if earlier ones return true (in other words, multiple
+ * lifetime extenders can be extending a notification at the same time).
+ */
+ boolean shouldExtendLifetime(NotificationEntry entry, @CancellationReason int reason);
+
+ /**
+ * Called by the NotifCollection to inform a lifetime extender that its extension of a notif
+ * is no longer valid (usually because the notif has been reposted and so no longer needs
+ * lifetime extension). The extender should clean up any references it has to the notif in
+ * question.
+ */
+ void cancelLifetimeExtension(NotificationEntry entry);
+
+ /** Callback for notifying the NotifCollection that a lifetime extension has expired. */
+ interface OnEndLifetimeExtensionCallback {
+ void onEndLifetimeExtension(NotifLifetimeExtender extender, NotificationEntry entry);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilder.java
new file mode 100644
index 000000000000..17fef6850f97
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilder.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import java.util.Collection;
+
+/**
+ * Interface for the class responsible for converting a NotifCollection into the final sorted,
+ * filtered, and grouped list of currently visible notifications.
+ */
+public interface NotifListBuilder {
+ /**
+ * Called after the NotifCollection has received an update from NotificationManager but before
+ * it dispatches any change events to its listeners. This is to inform the list builder that
+ * the first stage of the pipeline has been triggered. After events have been dispatched,
+ * onBuildList() will be called.
+ *
+ * While onBuildList() is always called after this method is called, the converse is not always
+ * true: sometimes the NotifCollection applies an update that does not need to dispatch events,
+ * in which case this method will be skipped and onBuildList will be called directly.
+ */
+ void onBeginDispatchToListeners();
+
+ /**
+ * Called by the NotifCollection to indicate that something in the collection has changed and
+ * that the list builder should regenerate the list.
+ */
+ void onBuildList(Collection<NotificationEntry> entries);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
index aacb2dd0682f..7d0ce5c4fa28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
@@ -24,7 +24,6 @@ import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Person;
-import android.content.Context;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.SnoozeCriterion;
@@ -35,7 +34,10 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
+import com.android.systemui.statusbar.notification.logging.NotifEvent;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -46,6 +48,8 @@ import java.util.Comparator;
import java.util.List;
import java.util.Objects;
+import javax.inject.Inject;
+
/**
* The list of currently displaying notifications.
*/
@@ -72,9 +76,17 @@ public class NotificationData {
private RankingMap mRankingMap;
private final Ranking mTmpRanking = new Ranking();
private final boolean mUsePeopleFiltering;
+ private final NotifLog mNotifLog;
+ private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
- public NotificationData(Context context) {
- mUsePeopleFiltering = NotificationUtils.usePeopleFiltering(context);
+ @Inject
+ public NotificationData(
+ NotificationSectionsFeatureManager sectionsFeatureManager,
+ NotifLog notifLog,
+ PeopleNotificationIdentifier peopleNotificationIdentifier) {
+ mUsePeopleFiltering = sectionsFeatureManager.isFilteringEnabled();
+ mNotifLog = notifLog;
+ mPeopleNotificationIdentifier = peopleNotificationIdentifier;
}
public void setHeadsUpManager(HeadsUpManager headsUpManager) {
@@ -86,10 +98,13 @@ public class NotificationData {
new Comparator<NotificationEntry>() {
@Override
public int compare(NotificationEntry a, NotificationEntry b) {
- final StatusBarNotification na = a.notification;
- final StatusBarNotification nb = b.notification;
- int aRank = getRank(a.key);
- int bRank = getRank(b.key);
+ final StatusBarNotification na = a.getSbn();
+ final StatusBarNotification nb = b.getSbn();
+ int aRank = getRank(a.getKey());
+ int bRank = getRank(b.getKey());
+
+ boolean aPeople = isPeopleNotification(a);
+ boolean bPeople = isPeopleNotification(b);
boolean aMedia = isImportantMedia(a);
boolean bMedia = isImportantMedia(b);
@@ -100,8 +115,8 @@ public class NotificationData {
boolean aHeadsUp = a.isRowHeadsUp();
boolean bHeadsUp = b.isRowHeadsUp();
- if (mUsePeopleFiltering && a.hasAssociatedPeople() != b.hasAssociatedPeople()) {
- return a.hasAssociatedPeople() ? -1 : 1;
+ if (mUsePeopleFiltering && aPeople != bPeople) {
+ return aPeople ? -1 : 1;
} else if (aHeadsUp != bHeadsUp) {
return aHeadsUp ? -1 : 1;
} else if (aHeadsUp) {
@@ -157,7 +172,7 @@ public class NotificationData {
final int len = mEntries.size();
for (int i = 0; i < len; i++) {
NotificationEntry entry = mEntries.valueAt(i);
- final StatusBarNotification sbn = entry.notification;
+ final StatusBarNotification sbn = entry.getSbn();
if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
continue;
}
@@ -173,11 +188,11 @@ public class NotificationData {
public void add(NotificationEntry entry) {
synchronized (mEntries) {
- mEntries.put(entry.notification.getKey(), entry);
+ mEntries.put(entry.getSbn().getKey(), entry);
}
mGroupManager.onEntryAdded(entry);
- updateRankingAndSort(mRankingMap);
+ updateRankingAndSort(mRankingMap, "addEntry=" + entry.getSbn());
}
public NotificationEntry remove(String key, RankingMap ranking) {
@@ -187,7 +202,7 @@ public class NotificationData {
}
if (removed == null) return null;
mGroupManager.onEntryRemoved(removed);
- updateRankingAndSort(ranking);
+ updateRankingAndSort(ranking, "removeEntry=" + removed.getSbn());
return removed;
}
@@ -195,15 +210,19 @@ public class NotificationData {
public void update(
NotificationEntry entry,
RankingMap ranking,
- StatusBarNotification notification) {
- updateRanking(ranking);
- final StatusBarNotification oldNotification = entry.notification;
- entry.setNotification(notification);
+ StatusBarNotification notification,
+ String reason) {
+ updateRanking(ranking, reason);
+ final StatusBarNotification oldNotification = entry.getSbn();
+ entry.setSbn(notification);
mGroupManager.onEntryUpdated(entry, oldNotification);
}
- public void updateRanking(RankingMap ranking) {
- updateRankingAndSort(ranking);
+ /**
+ * Update ranking and trigger a re-sort
+ */
+ public void updateRanking(RankingMap ranking, String reason) {
+ updateRankingAndSort(ranking, reason);
}
public void updateAppOp(int appOp, int uid, String pkg, String key, boolean showIcon) {
@@ -211,9 +230,9 @@ public class NotificationData {
final int len = mEntries.size();
for (int i = 0; i < len; i++) {
NotificationEntry entry = mEntries.valueAt(i);
- if (uid == entry.notification.getUid()
- && pkg.equals(entry.notification.getPackageName())
- && key.equals(entry.key)) {
+ if (uid == entry.getSbn().getUid()
+ && pkg.equals(entry.getSbn().getPackageName())
+ && key.equals(entry.getKey())) {
if (showIcon) {
entry.mActiveAppOps.add(appOp);
} else {
@@ -240,7 +259,7 @@ public class NotificationData {
final ArrayList<NotificationEntry> logicalChildren =
mGroupManager.getLogicalChildren(statusBarNotification);
for (NotificationEntry child : logicalChildren) {
- if (isHighPriority(child.notification)) {
+ if (isHighPriority(child.getSbn())) {
return true;
}
}
@@ -327,17 +346,17 @@ public class NotificationData {
}
private boolean isImportantMedia(NotificationEntry e) {
- int importance = e.ranking().getImportance();
- boolean media = e.key.equals(getMediaManager().getMediaNotificationKey())
+ int importance = e.getRanking().getImportance();
+ boolean media = e.getKey().equals(getMediaManager().getMediaNotificationKey())
&& importance > NotificationManager.IMPORTANCE_MIN;
return media;
}
private boolean isSystemMax(NotificationEntry e) {
- int importance = e.ranking().getImportance();
+ int importance = e.getRanking().getImportance();
boolean sys = importance >= NotificationManager.IMPORTANCE_HIGH
- && isSystemNotification(e.notification);
+ && isSystemNotification(e.getSbn());
return sys;
}
@@ -350,7 +369,7 @@ public class NotificationData {
return false;
}
- private void updateRankingAndSort(RankingMap rankingMap) {
+ private void updateRankingAndSort(RankingMap rankingMap, String reason) {
if (rankingMap != null) {
mRankingMap = rankingMap;
synchronized (mEntries) {
@@ -358,22 +377,22 @@ public class NotificationData {
for (int i = 0; i < len; i++) {
NotificationEntry entry = mEntries.valueAt(i);
Ranking newRanking = new Ranking();
- if (!getRanking(entry.key, newRanking)) {
+ if (!getRanking(entry.getKey(), newRanking)) {
continue;
}
entry.setRanking(newRanking);
- final StatusBarNotification oldSbn = entry.notification.cloneLight();
+ final StatusBarNotification oldSbn = entry.getSbn().cloneLight();
final String overrideGroupKey = newRanking.getOverrideGroupKey();
if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) {
- entry.notification.setOverrideGroupKey(overrideGroupKey);
+ entry.getSbn().setOverrideGroupKey(overrideGroupKey);
mGroupManager.onEntryUpdated(entry, oldSbn);
}
- entry.setIsHighPriority(isHighPriority(entry.notification));
+ entry.setIsHighPriority(isHighPriority(entry.getSbn()));
}
}
}
- filterAndSort();
+ filterAndSort(reason);
}
/**
@@ -391,7 +410,11 @@ public class NotificationData {
// TODO: This should not be public. Instead the Environment should notify this class when
// anything changed, and this class should call back the UI so it updates itself.
- public void filterAndSort() {
+ /**
+ * Filters and sorts the list of notification entries
+ */
+ public void filterAndSort(String reason) {
+ mNotifLog.log(NotifEvent.FILTER_AND_SORT, reason);
mSortedAndFiltered.clear();
synchronized (mEntries) {
@@ -432,7 +455,7 @@ public class NotificationData {
boolean isHeadsUp,
boolean isMedia,
boolean isSystemMax) {
- if (mUsePeopleFiltering && e.hasAssociatedPeople()) {
+ if (mUsePeopleFiltering && isPeopleNotification(e)) {
e.setBucket(BUCKET_PEOPLE);
} else if (isHeadsUp || isMedia || isSystemMax || e.isHighPriority()) {
e.setBucket(BUCKET_ALERTING);
@@ -441,6 +464,10 @@ public class NotificationData {
}
}
+ private boolean isPeopleNotification(NotificationEntry e) {
+ return mPeopleNotificationIdentifier.isPeopleNotification(e.getSbn());
+ }
+
public void dump(PrintWriter pw, String indent) {
int filteredLen = mSortedAndFiltered.size();
pw.print(indent);
@@ -466,10 +493,10 @@ public class NotificationData {
}
private void dumpEntry(PrintWriter pw, String indent, int i, NotificationEntry e) {
- getRanking(e.key, mTmpRanking);
+ getRanking(e.getKey(), mTmpRanking);
pw.print(indent);
- pw.println(" [" + i + "] key=" + e.key + " icon=" + e.icon);
- StatusBarNotification n = e.notification;
+ pw.println(" [" + i + "] key=" + e.getKey() + " icon=" + e.icon);
+ StatusBarNotification n = e.getSbn();
pw.print(indent);
pw.println(" pkg=" + n.getPackageName() + " id=" + n.getId() + " importance="
+ mTmpRanking.getImportance());
@@ -483,19 +510,6 @@ public class NotificationData {
}
/**
- * Get the current set of buckets for notification entries, as defined here
- */
- public static int[] getNotificationBuckets(Context context) {
- if (NotificationUtils.usePeopleFiltering(context)) {
- return new int[]{BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT};
- } else if (NotificationUtils.useNewInterruptionModel(context)) {
- return new int[]{BUCKET_ALERTING, BUCKET_SILENT};
- } else {
- return new int[]{BUCKET_ALERTING};
- }
- }
-
- /**
* Provides access to keyguard state and user settings dependent data.
*/
public interface KeyguardEnvironment {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index c3211e307845..71fc5490d656 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
@@ -30,6 +30,7 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICAT
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING;
import android.annotation.NonNull;
@@ -84,23 +85,30 @@ import java.util.Objects;
* clean this up in the future.
*/
public final class NotificationEntry {
- private static final long LAUNCH_COOLDOWN = 2000;
- private static final long REMOTE_INPUT_COOLDOWN = 500;
- private static final long INITIALIZATION_DELAY = 400;
- private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN;
- private static final int COLOR_INVALID = 1;
- public final String key;
- public StatusBarNotification notification;
+ private final String mKey;
+ private StatusBarNotification mSbn;
private Ranking mRanking;
- public boolean noisy;
+
+ /*
+ * Bookkeeping members
+ */
+
+ /** List of lifetime extenders that are extending the lifetime of this notification. */
+ final List<NotifLifetimeExtender> mLifetimeExtenders = new ArrayList<>();
+
+
+ /*
+ * Old members
+ * TODO: Remove every member beneath this line if possible
+ */
+
public StatusBarIconView icon;
public StatusBarIconView expandedIcon;
public StatusBarIconView centeredIcon;
public StatusBarIconView aodIcon;
private boolean interruption;
- public boolean autoRedacted; // whether the redacted notification was generated by us
public int targetSdk;
private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
public CharSequence remoteInputText;
@@ -141,12 +149,6 @@ public final class NotificationEntry {
private boolean hasSentReply;
/**
- * Whether this notification has changed in visual appearance since the previous post.
- * New notifications are interruptive by default.
- */
- public boolean isVisuallyInterruptive;
-
- /**
* Whether this notification is shown to the user as a high priority notification: visible on
* the lock screen/status bar and in the top section in the shade.
*/
@@ -161,34 +163,42 @@ public final class NotificationEntry {
public NotificationEntry(
@NonNull StatusBarNotification sbn,
@NonNull Ranking ranking) {
- this.key = sbn.getKey();
- setNotification(sbn);
+ checkNotNull(sbn);
+ checkNotNull(sbn.getKey());
+ checkNotNull(ranking);
+
+ mKey = sbn.getKey();
+ setSbn(sbn);
setRanking(ranking);
}
/** The key for this notification. Guaranteed to be immutable and unique */
- public String key() {
- return key;
+ public String getKey() {
+ return mKey;
}
/**
* The StatusBarNotification that represents one half of a NotificationEntry (the other half
* being the Ranking). This object is swapped out whenever a notification is updated.
*/
- public StatusBarNotification sbn() {
- return notification;
+ public StatusBarNotification getSbn() {
+ return mSbn;
}
/**
* Should only be called by NotificationEntryManager and friends.
* TODO: Make this package-private
*/
- public void setNotification(StatusBarNotification sbn) {
- if (sbn.getKey() != null && key != null && !sbn.getKey().equals(key)) {
+ public void setSbn(@NonNull StatusBarNotification sbn) {
+ checkNotNull(sbn);
+ checkNotNull(sbn.getKey());
+
+ if (!sbn.getKey().equals(mKey)) {
throw new IllegalArgumentException("New key " + sbn.getKey()
- + " doesn't match existing key " + key);
+ + " doesn't match existing key " + mKey);
}
- notification = sbn;
+
+ mSbn = sbn;
updatePeopleList();
}
@@ -197,7 +207,7 @@ public final class NotificationEntry {
* StatusBarNotification). This object is swapped out whenever a the ranking is updated (which
* generally occurs whenever anything changes in the notification list).
*/
- public Ranking ranking() {
+ public Ranking getRanking() {
return mRanking;
}
@@ -206,14 +216,22 @@ public final class NotificationEntry {
* TODO: Make this package-private
*/
public void setRanking(@NonNull Ranking ranking) {
- if (!ranking.getKey().equals(key)) {
+ checkNotNull(ranking);
+ checkNotNull(ranking.getKey());
+
+ if (!ranking.getKey().equals(mKey)) {
throw new IllegalArgumentException("New key " + ranking.getKey()
- + " doesn't match existing key " + key);
+ + " doesn't match existing key " + mKey);
}
+
mRanking = ranking;
- isVisuallyInterruptive = ranking.visuallyInterruptive();
}
+
+ /*
+ * Convenience getters for SBN and Ranking members
+ */
+
public NotificationChannel getChannel() {
return mRanking.getChannel();
}
@@ -261,6 +279,12 @@ public final class NotificationEntry {
}
+ /*
+ * Old methods
+ *
+ * TODO: Remove as many of these as possible
+ */
+
public void setInterruption() {
interruption = true;
}
@@ -278,13 +302,13 @@ public final class NotificationEntry {
}
public boolean isBubble() {
- return (notification.getNotification().flags & FLAG_BUBBLE) != 0;
+ return (mSbn.getNotification().flags & FLAG_BUBBLE) != 0;
}
private void updatePeopleList() {
mAssociatedPeople.clear();
- Bundle extras = notification.getNotification().extras;
+ Bundle extras = mSbn.getNotification().extras;
if (extras == null) {
return;
}
@@ -296,7 +320,7 @@ public final class NotificationEntry {
}
if (Notification.MessagingStyle.class.equals(
- notification.getNotification().getNotificationStyle())) {
+ mSbn.getNotification().getNotificationStyle())) {
final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
if (!ArrayUtils.isEmpty(messages)) {
for (Notification.MessagingStyle.Message message :
@@ -316,7 +340,7 @@ public final class NotificationEntry {
* Returns the data needed for a bubble for this notification, if it exists.
*/
public Notification.BubbleMetadata getBubbleMetadata() {
- return notification.getNotification().getBubbleMetadata();
+ return mSbn.getNotification().getBubbleMetadata();
}
/**
@@ -438,7 +462,7 @@ public final class NotificationEntry {
});
// Construct the centered icon
- if (notification.getNotification().isMediaNotification()) {
+ if (mSbn.getNotification().isMediaNotification()) {
centeredIcon = new StatusBarIconView(context,
sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
centeredIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
@@ -478,8 +502,8 @@ public final class NotificationEntry {
// Update the icon
Notification n = sbn.getNotification();
final StatusBarIcon ic = new StatusBarIcon(
- notification.getUser(),
- notification.getPackageName(),
+ mSbn.getUser(),
+ mSbn.getPackageName(),
n.getSmallIcon(),
n.iconLevel,
n.number,
@@ -503,7 +527,7 @@ public final class NotificationEntry {
public int getContrastedColor(Context context, boolean isLowPriority,
int backgroundColor) {
int rawColor = isLowPriority ? Notification.COLOR_DEFAULT :
- notification.getNotification().color;
+ mSbn.getNotification().color;
if (mCachedContrastColorIsFor == rawColor && mCachedContrastColor != COLOR_INVALID) {
return mCachedContrastColor;
}
@@ -569,7 +593,7 @@ public final class NotificationEntry {
if (!hasSentReply) {
return false;
}
- Bundle extras = notification.getNotification().extras;
+ Bundle extras = mSbn.getNotification().extras;
CharSequence[] replyTexts = extras.getCharSequenceArray(
Notification.EXTRA_REMOTE_INPUT_HISTORY);
if (!ArrayUtils.isEmpty(replyTexts)) {
@@ -771,7 +795,7 @@ public final class NotificationEntry {
* @see #canViewBeDismissed()
*/
public boolean isClearable() {
- if (notification == null || !notification.isClearable()) {
+ if (!mSbn.isClearable()) {
return false;
}
@@ -794,15 +818,15 @@ public final class NotificationEntry {
@VisibleForTesting
boolean isExemptFromDndVisualSuppression() {
- if (isNotificationBlockedByPolicy(notification.getNotification())) {
+ if (isNotificationBlockedByPolicy(mSbn.getNotification())) {
return false;
}
- if ((notification.getNotification().flags
+ if ((mSbn.getNotification().flags
& Notification.FLAG_FOREGROUND_SERVICE) != 0) {
return true;
}
- if (notification.getNotification().isMediaNotification()) {
+ if (mSbn.getNotification().isMediaNotification()) {
return true;
}
if (mIsSystemNotification != null && mIsSystemNotification) {
@@ -935,4 +959,10 @@ public final class NotificationEntry {
this.index = index;
}
}
+
+ private static final long LAUNCH_COOLDOWN = 2000;
+ private static final long REMOTE_INPUT_COOLDOWN = 500;
+ private static final long INITIALIZATION_DELAY = 400;
+ private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN;
+ private static final int COLOR_INVALID = 1;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
index 60cf995ad8d1..396c5fefd8b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
@@ -41,6 +41,8 @@ import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.NotificationClicker;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.logging.NotifEvent;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
@@ -72,6 +74,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
private final boolean mAllowLongPress;
private final KeyguardBypassController mKeyguardBypassController;
private final StatusBarStateController mStatusBarStateController;
+ private final NotifLog mNotifLog;
private NotificationRemoteInputManager mRemoteInputManager;
private NotificationPresenter mPresenter;
@@ -85,12 +88,14 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
public NotificationRowBinderImpl(Context context, boolean allowLongPress,
KeyguardBypassController keyguardBypassController,
- StatusBarStateController statusBarStateController) {
+ StatusBarStateController statusBarStateController,
+ NotifLog notifLog) {
mContext = context;
mMessagingUtil = new NotificationMessagingUtil(context);
mAllowLongPress = allowLongPress;
mKeyguardBypassController = keyguardBypassController;
mStatusBarStateController = statusBarStateController;
+ mNotifLog = notifLog;
}
private NotificationRemoteInputManager getRemoteInputManager() {
@@ -130,9 +135,9 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
throws InflationException {
ViewGroup parent = mListContainer.getViewParentForNotification(entry);
PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
- entry.notification.getUser().getIdentifier());
+ entry.getSbn().getUser().getIdentifier());
- final StatusBarNotification sbn = entry.notification;
+ final StatusBarNotification sbn = entry.getSbn();
if (entry.rowExists()) {
entry.updateIcons(mContext, sbn);
entry.reset();
@@ -143,6 +148,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
row -> {
bindRow(entry, pmUser, sbn, row, onDismissRunnable);
updateNotification(entry, pmUser, sbn, row);
+ mNotifLog.log(NotifEvent.INFLATED, sbn);
});
}
}
@@ -150,7 +156,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
private void bindRow(NotificationEntry entry, PackageManager pmUser,
StatusBarNotification sbn, ExpandableNotificationRow row,
Runnable onDismissRunnable) {
- row.setExpansionLogger(mExpansionLogger, entry.notification.getKey());
+ row.setExpansionLogger(mExpansionLogger, entry.getSbn().getKey());
row.setBypassController(mKeyguardBypassController);
row.setStatusBarStateController(mStatusBarStateController);
row.setGroupManager(mGroupManager);
@@ -207,8 +213,8 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
entry.reset();
PackageManager pmUser = StatusBar.getPackageManagerForUser(
mContext,
- entry.notification.getUser().getIdentifier());
- updateNotification(entry, pmUser, entry.notification, entry.getRow());
+ entry.getSbn().getUser().getIdentifier());
+ updateNotification(entry, pmUser, entry.getSbn(), entry.getRow());
} else {
// Once the RowInflaterTask is done, it will pick up the updated entry, so
// no-op here.
@@ -242,7 +248,6 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
// TODO: should updates to the entry be happening somewhere else?
entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
- entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
entry.setRow(row);
row.setOnActivatedListener(mPresenter);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
new file mode 100644
index 000000000000..8ebbca26fa5b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.logging;
+
+import android.annotation.IntDef;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.StatusBarNotification;
+
+import com.android.systemui.log.RichEvent;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An event related to notifications. {@link NotifLog} stores and prints these events for debugging
+ * and triaging purposes. We do not store a copy of the status bar notification nor ranking
+ * here to mitigate memory usage.
+ */
+public class NotifEvent extends RichEvent {
+ public static final int TOTAL_EVENT_TYPES = 11;
+
+ /**
+ * Creates a NotifEvent with an event type that matches with an index in the array
+ * getSupportedEvents() and {@link EventType}.
+ *
+ * The status bar notification and ranking objects are stored as shallow copies of the current
+ * state of the event when this event occurred.
+ */
+ public NotifEvent(int logLevel, int type, String reason, StatusBarNotification sbn,
+ Ranking ranking) {
+ super(logLevel, type, reason);
+ mMessage += getExtraInfo(sbn, ranking);
+ }
+
+ private String getExtraInfo(StatusBarNotification sbn, Ranking ranking) {
+ StringBuilder extraInfo = new StringBuilder();
+
+ if (sbn != null) {
+ extraInfo.append(" Sbn=");
+ extraInfo.append(sbn);
+ }
+
+ if (ranking != null) {
+ extraInfo.append(" Ranking=");
+ extraInfo.append(ranking);
+ }
+
+ return extraInfo.toString();
+ }
+
+ /**
+ * Event labels for NotifEvents
+ * Index corresponds to the {@link EventType}
+ */
+ @Override
+ public String[] getEventLabels() {
+ final String[] events = new String[]{
+ "NotifAdded",
+ "NotifRemoved",
+ "NotifUpdated",
+ "Filter",
+ "Sort",
+ "FilterAndSort",
+ "NotifVisibilityChanged",
+ "LifetimeExtended",
+ "RemoveIntercepted",
+ "InflationAborted",
+ "Inflated"
+ };
+
+ if (events.length != TOTAL_EVENT_TYPES) {
+ throw new IllegalStateException("NotifEvents events.length should match "
+ + TOTAL_EVENT_TYPES
+ + " events.length=" + events.length
+ + " TOTAL_EVENT_LENGTH=" + TOTAL_EVENT_TYPES);
+ }
+ return events;
+ }
+
+ /**
+ * Builds a NotifEvent.
+ */
+ public static class NotifEventBuilder extends RichEvent.Builder<NotifEventBuilder> {
+ private StatusBarNotification mSbn;
+ private Ranking mRanking;
+
+ @Override
+ public NotifEventBuilder getBuilder() {
+ return this;
+ }
+
+ /**
+ * Stores the status bar notification object. A shallow copy is stored in the NotifEvent's
+ * constructor.
+ */
+ public NotifEventBuilder setSbn(StatusBarNotification sbn) {
+ mSbn = sbn;
+ return this;
+ }
+
+ /**
+ * Stores the ranking object. A shallow copy is stored in the NotifEvent's
+ * constructor.
+ */
+ public NotifEventBuilder setRanking(Ranking ranking) {
+ mRanking = ranking;
+ return this;
+ }
+
+ @Override
+ public RichEvent build() {
+ return new NotifEvent(mLogLevel, mType, mReason, mSbn, mRanking);
+ }
+ }
+
+ @IntDef({NOTIF_ADDED,
+ NOTIF_REMOVED,
+ NOTIF_UPDATED,
+ FILTER,
+ SORT,
+ FILTER_AND_SORT,
+ NOTIF_VISIBILITY_CHANGED,
+ LIFETIME_EXTENDED,
+ REMOVE_INTERCEPTED,
+ INFLATION_ABORTED,
+ INFLATED
+ })
+
+ /**
+ * Types of NotifEvents
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EventType {}
+ public static final int NOTIF_ADDED = 0;
+ public static final int NOTIF_REMOVED = 1;
+ public static final int NOTIF_UPDATED = 2;
+ public static final int FILTER = 3;
+ public static final int SORT = 4;
+ public static final int FILTER_AND_SORT = 5;
+ public static final int NOTIF_VISIBILITY_CHANGED = 6;
+ public static final int LIFETIME_EXTENDED = 7;
+ // unable to remove notif - removal intercepted by {@link NotificationRemoveInterceptor}
+ public static final int REMOVE_INTERCEPTED = 8;
+ public static final int INFLATION_ABORTED = 9;
+ public static final int INFLATED = 10;
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java
new file mode 100644
index 000000000000..129283107894
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.logging;
+
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.StatusBarNotification;
+
+import com.android.systemui.DumpController;
+import com.android.systemui.log.SysuiLog;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Logs systemui notification events for debugging and triaging purposes. Logs are dumped in
+ * bugreports or on demand:
+ * adb shell dumpsys activity service com.android.systemui/.SystemUIService \
+ * dependency DumpController NotifLog
+ */
+@Singleton
+public class NotifLog extends SysuiLog {
+ private static final String TAG = "NotifLog";
+ private static final int MAX_DOZE_DEBUG_LOGS = 400;
+ private static final int MAX_DOZE_LOGS = 50;
+
+ @Inject
+ public NotifLog(DumpController dumpController) {
+ super(dumpController, TAG, MAX_DOZE_DEBUG_LOGS, MAX_DOZE_LOGS);
+ }
+
+ /**
+ * Logs a {@link NotifEvent} with a notification, ranking and message
+ * @return true if successfully logged, else false
+ */
+ public boolean log(@NotifEvent.EventType int eventType, StatusBarNotification sbn,
+ Ranking ranking, String msg) {
+ return log(new NotifEvent.NotifEventBuilder()
+ .setType(eventType)
+ .setSbn(sbn)
+ .setRanking(ranking)
+ .setReason(msg)
+ .build());
+ }
+
+ /**
+ * Logs a {@link NotifEvent}
+ * @return true if successfully logged, else false
+ */
+ public boolean log(@NotifEvent.EventType int eventType) {
+ return log(eventType, null, null, null);
+ }
+
+ /**
+ * Logs a {@link NotifEvent} with a message
+ * @return true if successfully logged, else false
+ */
+ public boolean log(@NotifEvent.EventType int eventType, String msg) {
+ return log(eventType, null, null, msg);
+ }
+
+ /**
+ * Logs a {@link NotifEvent} with a notification
+ * @return true if successfully logged, else false
+ */
+ public boolean log(@NotifEvent.EventType int eventType, StatusBarNotification sbn) {
+ return log(eventType, sbn, null, "");
+ }
+
+ /**
+ * Logs a {@link NotifEvent} with a notification
+ * @return true if successfully logged, else false
+ */
+ public boolean log(@NotifEvent.EventType int eventType, StatusBarNotification sbn, String msg) {
+ return log(eventType, sbn, null, msg);
+ }
+
+ /**
+ * Logs a {@link NotifEvent} with a ranking
+ * @return true if successfully logged, else false
+ */
+ public boolean log(@NotifEvent.EventType int eventType, Ranking ranking) {
+ return log(eventType, null, ranking, "");
+ }
+
+ /**
+ * Logs a {@link NotifEvent} with a notification and ranking
+ * @return true if successfully logged, else false
+ */
+ public boolean log(@NotifEvent.EventType int eventType, StatusBarNotification sbn,
+ Ranking ranking) {
+ return log(eventType, sbn, ranking, "");
+ }
+
+ /**
+ * Logs a {@link NotifEvent} with a notification entry
+ * @return true if successfully logged, else false
+ */
+ public boolean log(@NotifEvent.EventType int eventType, NotificationEntry entry) {
+ return log(eventType, entry.getSbn(), entry.getRanking(), "");
+ }
+
+ /**
+ * Logs a {@link NotifEvent} with a notification entry
+ * @return true if successfully logged, else false
+ */
+ public boolean log(@NotifEvent.EventType int eventType, NotificationEntry entry,
+ String msg) {
+ return log(eventType, entry.getSbn(), entry.getRanking(), msg);
+ }
+}
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 81275fda57e5..b7f408ebe557 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
@@ -126,7 +126,7 @@ public class NotificationLogger implements StateListener {
int N = activeNotifications.size();
for (int i = 0; i < N; i++) {
NotificationEntry entry = activeNotifications.get(i);
- String key = entry.notification.getKey();
+ String key = entry.getSbn().getKey();
boolean isVisible = mListContainer.isInVisibleLocation(entry);
NotificationVisibility visObj = NotificationVisibility.obtain(key, i, N, isVisible,
getNotificationLocation(entry));
@@ -214,14 +214,14 @@ public class NotificationLogger implements StateListener {
NotificationVisibility visibility,
boolean removedByUser) {
if (removedByUser && visibility != null) {
- logNotificationClear(entry.key, entry.notification, visibility);
+ logNotificationClear(entry.getKey(), entry.getSbn(), visibility);
}
- mExpansionStateLogger.onEntryRemoved(entry.key);
+ mExpansionStateLogger.onEntryRemoved(entry.getKey());
}
@Override
public void onEntryReinflated(NotificationEntry entry) {
- mExpansionStateLogger.onEntryReinflated(entry.key);
+ mExpansionStateLogger.onEntryReinflated(entry.getKey());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt
new file mode 100644
index 000000000000..2c0c9420a8c4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.people
+
+import android.app.PendingIntent
+import android.graphics.drawable.Drawable
+
+/** `ViewModel` for PeopleHub view. */
+data class PeopleHubViewModel(val people: Sequence<PersonViewModel>, val isVisible: Boolean)
+
+/** `ViewModel` for a single "Person' in PeopleHub. */
+data class PersonViewModel(
+ val name: CharSequence,
+ val icon: Drawable,
+ val onClick: () -> Unit
+)
+
+/** `Model` for PeopleHub. */
+data class PeopleHubModel(val people: Collection<PersonModel>)
+
+/** `Model` for a single "Person" in PeopleHub. */
+data class PersonModel(
+ val key: PersonKey,
+ val name: CharSequence,
+ val avatar: Drawable,
+ val clickIntent: PendingIntent
+)
+
+/** Unique identifier for a Person in PeopleHub. */
+typealias PersonKey = String \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt
new file mode 100644
index 000000000000..45709893a7b5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.people
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+abstract class PeopleHubModule {
+
+ @Binds
+ abstract fun peopleHubSectionFooterViewController(
+ impl: PeopleHubSectionFooterViewAdapterImpl
+ ): PeopleHubSectionFooterViewAdapter
+
+ @Binds
+ abstract fun peopleHubDataSource(impl: PeopleHubDataSourceImpl): DataSource<PeopleHubModel>
+
+ @Binds
+ abstract fun peopleHubViewModelFactoryDataSource(
+ impl: PeopleHubViewModelFactoryDataSourceImpl
+ ): DataSource<PeopleHubViewModelFactory>
+
+ @Binds
+ abstract fun peopleNotificationIdentifier(
+ impl: PeopleNotificationIdentifierImpl
+ ): PeopleNotificationIdentifier
+
+ @Binds
+ abstract fun notificationPersonExtractor(
+ pluginImpl: NotificationPersonExtractorPluginBoundary
+ ): NotificationPersonExtractor
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
new file mode 100644
index 000000000000..fe257d9fd252
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.people
+
+import android.app.Notification
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.ColorFilter
+import android.graphics.PixelFormat
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import android.service.notification.StatusBarNotification
+import android.util.TypedValue
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import com.android.internal.statusbar.NotificationVisibility
+import com.android.internal.widget.MessagingGroup
+import com.android.launcher3.icons.BaseIconFactory
+import com.android.systemui.R
+import com.android.systemui.plugins.NotificationPersonExtractorPlugin
+import com.android.systemui.statusbar.notification.NotificationEntryListener
+import com.android.systemui.statusbar.notification.NotificationEntryManager
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.policy.ExtensionController
+import java.util.ArrayDeque
+import javax.inject.Inject
+import javax.inject.Singleton
+
+private const val MAX_STORED_INACTIVE_PEOPLE = 10
+
+interface NotificationPersonExtractor {
+ fun extractPerson(sbn: StatusBarNotification): PersonModel?
+ fun extractPersonKey(sbn: StatusBarNotification): String?
+}
+
+@Singleton
+class NotificationPersonExtractorPluginBoundary @Inject constructor(
+ extensionController: ExtensionController,
+ private val context: Context
+) : NotificationPersonExtractor {
+
+ private var plugin: NotificationPersonExtractorPlugin? = null
+
+ init {
+ plugin = extensionController
+ .newExtension(NotificationPersonExtractorPlugin::class.java)
+ .withPlugin(NotificationPersonExtractorPlugin::class.java)
+ .withCallback { extractor ->
+ plugin = extractor
+ }
+ .build()
+ .get()
+ }
+
+ override fun extractPerson(sbn: StatusBarNotification) =
+ plugin?.extractPerson(sbn)?.let { data ->
+ val badged = addBadgeToDrawable(data.avatar, context, sbn.packageName, sbn.user)
+ PersonModel(data.key, data.name, badged, data.clickIntent)
+ }
+
+ override fun extractPersonKey(sbn: StatusBarNotification) = plugin?.extractPersonKey(sbn)
+}
+
+@Singleton
+class PeopleHubDataSourceImpl @Inject constructor(
+ private val notificationEntryManager: NotificationEntryManager,
+ private val peopleHubManager: PeopleHubManager,
+ private val extractor: NotificationPersonExtractor
+) : DataSource<PeopleHubModel> {
+
+ private val dataListeners = mutableListOf<DataListener<PeopleHubModel>>()
+
+ private val notificationEntryListener = object : NotificationEntryListener {
+ override fun onEntryInflated(entry: NotificationEntry, inflatedFlags: Int) =
+ addVisibleEntry(entry)
+
+ override fun onEntryReinflated(entry: NotificationEntry) = addVisibleEntry(entry)
+
+ override fun onPostEntryUpdated(entry: NotificationEntry) = addVisibleEntry(entry)
+
+ override fun onEntryRemoved(
+ entry: NotificationEntry,
+ visibility: NotificationVisibility?,
+ removedByUser: Boolean
+ ) = removeVisibleEntry(entry)
+ }
+
+ private fun removeVisibleEntry(entry: NotificationEntry) {
+ val key = extractor.extractPersonKey(entry.sbn) ?: entry.extractPersonKey()
+ if (key?.let(peopleHubManager::removeActivePerson) == true) {
+ updateUi()
+ }
+ }
+
+ private fun addVisibleEntry(entry: NotificationEntry) {
+ val personModel = extractor.extractPerson(entry.sbn) ?: entry.extractPerson()
+ if (personModel?.let(peopleHubManager::addActivePerson) == true) {
+ updateUi()
+ }
+ }
+
+ override fun registerListener(listener: DataListener<PeopleHubModel>): Subscription {
+ val registerWithNotificationEntryManager = dataListeners.isEmpty()
+ dataListeners.add(listener)
+ if (registerWithNotificationEntryManager) {
+ notificationEntryManager.addNotificationEntryListener(notificationEntryListener)
+ } else {
+ listener.onDataChanged(peopleHubManager.getPeopleHubModel())
+ }
+ return object : Subscription {
+ override fun unsubscribe() {
+ dataListeners.remove(listener)
+ if (dataListeners.isEmpty()) {
+ notificationEntryManager
+ .removeNotificationEntryListener(notificationEntryListener)
+ }
+ }
+ }
+ }
+
+ private fun updateUi() {
+ val model = peopleHubManager.getPeopleHubModel()
+ for (listener in dataListeners) {
+ listener.onDataChanged(model)
+ }
+ }
+}
+
+@Singleton
+class PeopleHubManager @Inject constructor() {
+
+ private val activePeople = mutableMapOf<PersonKey, PersonModel>()
+ private val inactivePeople = ArrayDeque<PersonModel>(MAX_STORED_INACTIVE_PEOPLE)
+
+ fun removeActivePerson(key: PersonKey): Boolean {
+ activePeople.remove(key)?.let { data ->
+ if (inactivePeople.size >= MAX_STORED_INACTIVE_PEOPLE) {
+ inactivePeople.removeLast()
+ }
+ inactivePeople.push(data)
+ return true
+ }
+ return false
+ }
+
+ fun addActivePerson(person: PersonModel): Boolean {
+ activePeople[person.key] = person
+ return inactivePeople.removeIf { it.key == person.key }
+ }
+
+ fun getPeopleHubModel(): PeopleHubModel = PeopleHubModel(inactivePeople)
+}
+
+private val ViewGroup.children
+ get(): Sequence<View> = sequence {
+ for (i in 0 until childCount) {
+ yield(getChildAt(i))
+ }
+ }
+
+private fun ViewGroup.childrenWithId(id: Int): Sequence<View> = children.filter { it.id == id }
+
+private fun NotificationEntry.extractPerson(): PersonModel? {
+ if (!isMessagingNotification()) {
+ return null
+ }
+ val clickIntent = sbn.notification.contentIntent
+ ?: return null
+ val extras = sbn.notification.extras
+ val name = extras.getString(Notification.EXTRA_CONVERSATION_TITLE)
+ ?: extras.getString(Notification.EXTRA_TITLE)
+ ?: return null
+ val drawable = extractAvatarFromRow(this) ?: return null
+ val badgedAvatar = addBadgeToDrawable(drawable, row.context, sbn.packageName, sbn.user)
+ return PersonModel(key, name, badgedAvatar, clickIntent)
+}
+
+private fun addBadgeToDrawable(
+ drawable: Drawable,
+ context: Context,
+ packageName: String,
+ user: UserHandle
+): Drawable {
+ val pm = context.packageManager
+ val appInfo = pm.getApplicationInfoAsUser(packageName, 0, user)
+ return object : Drawable() {
+ override fun draw(canvas: Canvas) {
+ val iconBounds = getBounds()
+ val factory = object : BaseIconFactory(
+ context,
+ 0 /* unused */,
+ iconBounds.width(),
+ true) {}
+ val badge = factory.createBadgedIconBitmap(
+ appInfo.loadIcon(pm),
+ user,
+ true,
+ appInfo.isInstantApp,
+ null)
+ val badgeDrawable = BitmapDrawable(context.resources, badge.icon)
+ .apply {
+ alpha = drawable.alpha
+ colorFilter = drawable.colorFilter
+ val badgeWidth = TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ 15f,
+ context.resources.displayMetrics
+ ).toInt()
+ setBounds(
+ iconBounds.left + (iconBounds.width() - badgeWidth),
+ iconBounds.top + (iconBounds.height() - badgeWidth),
+ iconBounds.right,
+ iconBounds.bottom)
+ }
+ drawable.bounds = iconBounds
+ drawable.draw(canvas)
+ badgeDrawable.draw(canvas)
+ }
+
+ override fun setAlpha(alpha: Int) {
+ drawable.alpha = alpha
+ }
+
+ override fun setColorFilter(colorFilter: ColorFilter?) {
+ drawable.colorFilter = colorFilter
+ }
+
+ @PixelFormat.Opacity
+ override fun getOpacity(): Int = PixelFormat.OPAQUE
+ }
+}
+
+private fun extractAvatarFromRow(entry: NotificationEntry): Drawable? =
+ entry.row
+ ?.childrenWithId(R.id.expanded)
+ ?.mapNotNull { it as? ViewGroup }
+ ?.flatMap {
+ it.childrenWithId(com.android.internal.R.id.status_bar_latest_event_content)
+ }
+ ?.mapNotNull {
+ it.findViewById<ViewGroup>(com.android.internal.R.id.notification_messaging)
+ }
+ ?.mapNotNull { messagesView ->
+ messagesView.children
+ .mapNotNull { it as? MessagingGroup }
+ .lastOrNull()
+ ?.findViewById<ImageView>(com.android.internal.R.id.message_icon)
+ ?.drawable
+ }
+ ?.firstOrNull()
+
+private fun NotificationEntry.extractPersonKey(): PersonKey? =
+ if (isMessagingNotification()) key else null
+
+private fun NotificationEntry.isMessagingNotification() =
+ sbn.notification.notificationStyle == Notification.MessagingStyle::class.java \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt
new file mode 100644
index 000000000000..5c354089dfe9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.people
+
+import android.view.View
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/** Boundary between the View and PeopleHub, as seen by the View. */
+interface PeopleHubSectionFooterViewAdapter {
+ fun bindView(viewBoundary: PeopleHubSectionFooterViewBoundary)
+}
+
+/** Abstract `View` representation of PeopleHub footer in [NotificationSectionsManager]. */
+interface PeopleHubSectionFooterViewBoundary {
+ /** View used for animating the activity launch caused by clicking a person in the hub. */
+ val associatedViewForClickAnimation: View
+
+ /** [DataListener]s for individual people in the hub. */
+ val personViewAdapters: Sequence<DataListener<PersonViewModel?>>
+
+ /** Sets the visibility of the Hub in the notification shade. */
+ fun setVisible(isVisible: Boolean)
+}
+
+/** Creates a [PeopleHubViewModel] given some additional information required from the `View`. */
+interface PeopleHubViewModelFactory {
+
+ /**
+ * Creates a [PeopleHubViewModel] that, when clicked, starts an activity using an animation
+ * involving the given [view].
+ */
+ fun createWithAssociatedClickView(view: View): PeopleHubViewModel
+}
+
+/**
+ * Wraps a [PeopleHubSectionFooterViewBoundary] in a [DataListener], and connects it to the data
+ * pipeline.
+ *
+ * @param dataSource PeopleHub data pipeline.
+ */
+@Singleton
+class PeopleHubSectionFooterViewAdapterImpl @Inject constructor(
+ private val dataSource: DataSource<@JvmSuppressWildcards PeopleHubViewModelFactory>
+) : PeopleHubSectionFooterViewAdapter {
+
+ override fun bindView(viewBoundary: PeopleHubSectionFooterViewBoundary) {
+ dataSource.registerListener(PeopleHubDataListenerImpl(viewBoundary))
+ }
+}
+
+private class PeopleHubDataListenerImpl(
+ private val viewBoundary: PeopleHubSectionFooterViewBoundary
+) : DataListener<PeopleHubViewModelFactory> {
+
+ override fun onDataChanged(data: PeopleHubViewModelFactory) {
+ val viewModel = data.createWithAssociatedClickView(
+ viewBoundary.associatedViewForClickAnimation
+ )
+ viewBoundary.setVisible(viewModel.isVisible)
+ val padded = viewModel.people + repeated(null)
+ for ((personAdapter, personModel) in viewBoundary.personViewAdapters.zip(padded)) {
+ personAdapter.onDataChanged(personModel)
+ }
+ }
+}
+
+/**
+ * Converts [PeopleHubModel]s into [PeopleHubViewModelFactory]s.
+ *
+ * This class serves as the glue between the View layer (which depends on
+ * [PeopleHubSectionFooterViewBoundary]) and the Data layer (which produces [PeopleHubModel]s).
+ */
+@Singleton
+class PeopleHubViewModelFactoryDataSourceImpl @Inject constructor(
+ private val activityStarter: ActivityStarter,
+ private val dataSource: DataSource<@JvmSuppressWildcards PeopleHubModel>
+) : DataSource<PeopleHubViewModelFactory> {
+
+ override fun registerListener(listener: DataListener<PeopleHubViewModelFactory>) =
+ dataSource.registerListener(PeopleHubModelListenerImpl(activityStarter, listener))
+}
+
+private class PeopleHubModelListenerImpl(
+ private val activityStarter: ActivityStarter,
+ private val dataListener: DataListener<PeopleHubViewModelFactory>
+) : DataListener<PeopleHubModel> {
+
+ override fun onDataChanged(data: PeopleHubModel) =
+ dataListener.onDataChanged(PeopleHubViewModelFactoryImpl(data, activityStarter))
+}
+
+private class PeopleHubViewModelFactoryImpl(
+ private val data: PeopleHubModel,
+ private val activityStarter: ActivityStarter
+) : PeopleHubViewModelFactory {
+
+ override fun createWithAssociatedClickView(view: View): PeopleHubViewModel {
+ val personViewModels = data.people.asSequence().map { personModel ->
+ val onClick = {
+ activityStarter.startPendingIntentDismissingKeyguard(
+ personModel.clickIntent,
+ null,
+ view
+ )
+ }
+ PersonViewModel(personModel.name, personModel.avatar, onClick)
+ }
+ return PeopleHubViewModel(personViewModels, data.people.isNotEmpty())
+ }
+}
+
+private fun <T> repeated(value: T): Sequence<T> = sequence {
+ while (true) {
+ yield(value)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
new file mode 100644
index 000000000000..bfd4070846f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.people
+
+import android.app.Notification
+import android.service.notification.StatusBarNotification
+import javax.inject.Inject
+import javax.inject.Singleton
+
+interface PeopleNotificationIdentifier {
+ fun isPeopleNotification(sbn: StatusBarNotification): Boolean
+}
+
+@Singleton
+class PeopleNotificationIdentifierImpl @Inject constructor(
+ private val personExtractor: NotificationPersonExtractor
+) : PeopleNotificationIdentifier {
+
+ override fun isPeopleNotification(sbn: StatusBarNotification) =
+ sbn.notification.notificationStyle == Notification.MessagingStyle::class.java ||
+ personExtractor.extractPersonKey(sbn) != null
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/ViewPipeline.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/ViewPipeline.kt
new file mode 100644
index 000000000000..3ca3792a680f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/ViewPipeline.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.people
+
+/** Boundary between a View and data pipeline, as seen by the pipeline. */
+interface DataListener<in T> {
+ fun onDataChanged(data: T)
+}
+
+/** Convert all data using the given [mapper] before invoking this [DataListener]. */
+fun <S, T> DataListener<T>.contraMap(mapper: (S) -> T): DataListener<S> = object : DataListener<S> {
+ override fun onDataChanged(data: S) = onDataChanged(mapper(data))
+}
+
+/** Boundary between a View and data pipeline, as seen by the View. */
+interface DataSource<out T> {
+ fun registerListener(listener: DataListener<T>): Subscription
+}
+
+/** Represents a registration with a [DataSource]. */
+interface Subscription {
+ /** Removes the previously registered [DataListener] from the [DataSource] */
+ fun unsubscribe()
+}
+
+/** Transform all data coming out of this [DataSource] using the given [mapper]. */
+fun <S, T> DataSource<S>.map(mapper: (S) -> T): DataSource<T> = object : DataSource<T> {
+ override fun registerListener(listener: DataListener<T>) =
+ registerListener(listener.contraMap(mapper))
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 8d7325118139..a817f54e77ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -180,6 +180,10 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
initDimens();
}
+ public FalsingManager getFalsingManager() {
+ return mFalsingManager;
+ }
+
private void updateColors() {
mNormalColor = mContext.getColor(R.color.notification_material_background_color);
mTintedRippleColor = mContext.getColor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 0f6ce2153488..536db67a8795 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -74,7 +74,6 @@ import com.android.internal.widget.CachingIconView;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
@@ -221,7 +220,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private ViewStub mGutsStub;
private boolean mIsSystemChildExpanded;
private boolean mIsPinned;
- private FalsingManager mFalsingManager;
private boolean mExpandAnimationRunning;
private AboveShelfChangedListener mAboveShelfChangedListener;
private HeadsUpManager mHeadsUpManager;
@@ -444,7 +442,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
*/
public void setEntry(@NonNull NotificationEntry entry) {
mEntry = entry;
- mStatusBarNotification = entry.notification;
+ mStatusBarNotification = entry.getSbn();
cacheIsSystemNotification();
}
@@ -1205,7 +1203,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
// Let's update our childrencontainer. This is intentionally not guarded with
// mIsSummaryWithChildren since we might have had children but not anymore.
if (mChildrenContainer != null) {
- mChildrenContainer.reInflateViews(mExpandClickListener, mEntry.notification);
+ mChildrenContainer.reInflateViews(mExpandClickListener, mEntry.getSbn());
}
if (mGuts != null) {
NotificationGuts oldGuts = mGuts;
@@ -1449,6 +1447,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
setDismissed(fromAccessibility);
if (mEntry.isClearable()) {
+ // TODO: beverlyt, log dismissal
// TODO: track dismiss sentiment
if (mOnDismissRunnable != null) {
mOnDismissRunnable.run();
@@ -1636,7 +1635,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
super(context, attrs);
- mFalsingManager = Dependency.get(FalsingManager.class); // TODO: inject into a controller.
mNotificationInflater = new NotificationContentInflater(this);
mMenuRow = new NotificationMenuRow(mContext);
mImageResolver = new NotificationInlineImageResolver(context,
@@ -2208,7 +2206,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
* @param allowChildExpansion whether a call to this method allows expanding children
*/
public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
- mFalsingManager.setNotificationExpanded();
+ getFalsingManager().setNotificationExpanded();
if (mIsSummaryWithChildren && !shouldShowPublic() && allowChildExpansion
&& !mChildrenContainer.showingAsLowPriority()) {
final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
@@ -2305,7 +2303,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private void updateRippleAllowed() {
boolean allowed = isOnKeyguard()
- || mEntry.notification.getNotification().contentIntent == null;
+ || mEntry.getSbn().getNotification().contentIntent == null;
setRippleAllowed(allowed);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
index 73093c6f471f..37f63c9779f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
@@ -139,7 +139,8 @@ public class NotificationBlockingHelperManager {
mBlockingHelperRow.setBlockingHelperShowing(false);
if (mBlockingHelperRow.isAttachedToWindow()) {
- Dependency.get(NotificationEntryManager.class).updateNotifications();
+ Dependency.get(NotificationEntryManager.class).updateNotifications(
+ "dismissCurrentBlockingHelper");
}
mBlockingHelperRow = null;
return true;
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 a612a1721c41..a91a119d4ea7 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
@@ -230,7 +230,7 @@ public class NotificationContentInflater {
}
// Only inflate the ones that are set.
reInflateFlags &= mInflationFlags;
- StatusBarNotification sbn = mRow.getEntry().notification;
+ StatusBarNotification sbn = mRow.getEntry().getSbn();
// To check if the notification has inline image and preload inline image if necessary.
mRow.getImageResolver().preloadImages(sbn.getNotification());
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 f30a8b12ab39..b12c76c750a8 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
@@ -1097,7 +1097,7 @@ public class NotificationContentView extends FrameLayout {
}
public void onNotificationUpdated(NotificationEntry entry) {
- mStatusBarNotification = entry.notification;
+ mStatusBarNotification = entry.getSbn();
mOnContentViewInactiveListeners.clear();
mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
updateAllSingleLineViews();
@@ -1179,7 +1179,7 @@ public class NotificationContentView extends FrameLayout {
: mHeadsUpInflatedSmartReplies.getSmartRepliesAndActions();
if (DEBUG) {
Log.d(TAG, String.format("Adding suggestions for %s, %d actions, and %d replies.",
- entry.notification.getKey(),
+ entry.getSbn().getKey(),
mCurrentSmartRepliesAndActions.smartActions == null ? 0 :
mCurrentSmartRepliesAndActions.smartActions.actions.size(),
mCurrentSmartRepliesAndActions.smartReplies == null ? 0 :
@@ -1253,7 +1253,7 @@ public class NotificationContentView extends FrameLayout {
}
}
if (hasRemoteInput) {
- int color = entry.notification.getNotification().color;
+ int color = entry.getSbn().getNotification().color;
if (color == Notification.COLOR_DEFAULT) {
color = mContext.getColor(R.color.default_remote_input_background);
}
@@ -1267,7 +1267,7 @@ public class NotificationContentView extends FrameLayout {
if (existingPendingIntent != null || existing.isActive()) {
// The current action could be gone, or the pending intent no longer valid.
// If we find a matching action in the new notification, focus, otherwise close.
- Notification.Action[] actions = entry.notification.getNotification().actions;
+ Notification.Action[] actions = entry.getSbn().getNotification().actions;
if (existingPendingIntent != null) {
existing.setPendingIntent(existingPendingIntent);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
index 4700baae8fab..18d436ff7659 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
@@ -421,7 +421,7 @@ public class NotificationGuts extends FrameLayout {
}
/** Listener for animations executed in {@link #animateClose(int, int, boolean)}. */
- private static class AnimateCloseListener extends AnimatorListenerAdapter {
+ private class AnimateCloseListener extends AnimatorListenerAdapter {
final View mView;
private final GutsContent mGutsContent;
@@ -433,8 +433,10 @@ public class NotificationGuts extends FrameLayout {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
- mView.setVisibility(View.GONE);
- mGutsContent.onFinishedClosing();
+ if (!isExposed()) {
+ mView.setVisibility(View.GONE);
+ mGutsContent.onFinishedClosing();
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 9d0dd6b683cf..1de2cbb982a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -100,6 +100,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
protected String mKeyToRemoveOnGutsClosed;
private StatusBar mStatusBar;
+ private Runnable mOpenRunnable;
@Inject
public NotificationGutsManager(
@@ -343,6 +344,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
public void closeAndSaveGuts(boolean removeLeavebehinds, boolean force, boolean removeControls,
int x, int y, boolean resetMenu) {
if (mNotificationGutsExposed != null) {
+ mNotificationGutsExposed.removeCallbacks(mOpenRunnable);
mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force);
}
if (resetMenu) {
@@ -445,7 +447,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
// ensure that it's laid but not visible until actually laid out
guts.setVisibility(View.INVISIBLE);
// Post to ensure the the guts are properly laid out.
- guts.post(new Runnable() {
+ mOpenRunnable = new Runnable() {
@Override
public void run() {
if (row.getWindowToken() == null) {
@@ -470,7 +472,8 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
mListContainer.onHeightChanged(row, true /* needsAnimation */);
mGutsMenuItem = menuItem;
}
- });
+ };
+ guts.post(mOpenRunnable);
return true;
}
@@ -491,15 +494,17 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
@Override
public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) {
if (shouldExtend) {
- mKeyToRemoveOnGutsClosed = entry.key;
+ mKeyToRemoveOnGutsClosed = entry.getKey();
if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Keeping notification because it's showing guts. " + entry.key);
+ Log.d(TAG, "Keeping notification because it's showing guts. " + entry.getKey());
}
} else {
- if (mKeyToRemoveOnGutsClosed != null && mKeyToRemoveOnGutsClosed.equals(entry.key)) {
+ if (mKeyToRemoveOnGutsClosed != null
+ && mKeyToRemoveOnGutsClosed.equals(entry.getKey())) {
mKeyToRemoveOnGutsClosed = null;
if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Notification that was kept for guts was updated. " + entry.key);
+ Log.d(TAG, "Notification that was kept for guts was updated. "
+ + entry.getKey());
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
index 0e0802ef233f..23643133e121 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
@@ -28,6 +28,7 @@ import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.metrics.LogMaker;
import android.os.Handler;
+import android.provider.Settings;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
@@ -43,9 +44,12 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.widget.MediaNotificationView;
import com.android.systemui.Dependency;
+import com.android.systemui.qs.QSPanel;
+import com.android.systemui.qs.QuickQSPanel;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.phone.StatusBarWindowController;
import java.util.Timer;
import java.util.TimerTask;
@@ -188,9 +192,29 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi
mActions = mView.findViewById(com.android.internal.R.id.media_actions);
mIsViewVisible = mView.isShown();
- final MediaSession.Token token = mRow.getEntry().notification.getNotification().extras
+ final MediaSession.Token token = mRow.getEntry().getSbn().getNotification().extras
.getParcelable(Notification.EXTRA_MEDIA_SESSION);
+ int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0);
+ if (flag == 1) {
+ StatusBarWindowController ctrl = Dependency.get(StatusBarWindowController.class);
+ QuickQSPanel panel = ctrl.getStatusBarView().findViewById(
+ com.android.systemui.R.id.quick_qs_panel);
+ panel.getMediaPlayer().setMediaSession(token,
+ mRow.getStatusBarNotification().getNotification().getSmallIcon(),
+ getNotificationHeader().getOriginalIconColor(),
+ mRow.getCurrentBackgroundTint(),
+ mActions);
+ QSPanel bigPanel = ctrl.getStatusBarView().findViewById(
+ com.android.systemui.R.id.quick_settings_panel);
+ bigPanel.addMediaSession(token,
+ mRow.getStatusBarNotification().getNotification().getSmallIcon(),
+ getNotificationHeader().getOriginalIconColor(),
+ mRow.getCurrentBackgroundTint(),
+ mActions,
+ mRow.getStatusBarNotification());
+ }
+
boolean showCompactSeekbar = mMediaManager.getShowCompactMediaSeekbar();
if (token == null || (COMPACT_MEDIA_TAG.equals(mView.getTag()) && !showCompactSeekbar)) {
if (mSeekBarView != null) {
@@ -431,7 +455,7 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi
* @return new LogMaker
*/
private LogMaker newLog(int event) {
- String packageName = mRow.getEntry().notification.getPackageName();
+ String packageName = mRow.getEntry().getSbn().getPackageName();
return new LogMaker(MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR)
.setType(event)
@@ -443,7 +467,7 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi
* @return new LogMaker
*/
private LogMaker newLog(int event, int subtype) {
- String packageName = mRow.getEntry().notification.getPackageName();
+ String packageName = mRow.getEntry().getSbn().getPackageName();
return new LogMaker(MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR)
.setType(event)
.setSubtype(subtype)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 3950003e64ce..c2eff8a6a776 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -63,7 +63,7 @@ public abstract class NotificationViewWrapper implements TransformableView {
return new NotificationMessagingTemplateViewWrapper(ctx, v, row);
}
Class<? extends Notification.Style> style =
- row.getEntry().notification.getNotification().getNotificationStyle();
+ row.getEntry().getSbn().getNotification().getNotificationStyle();
if (Notification.DecoratedCustomViewStyle.class.equals(style)) {
return new NotificationDecoratedCustomViewWrapper(ctx, v, row);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index f3d068a9d610..ecab188a7481 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -22,7 +22,6 @@ import android.content.Context;
import android.util.MathUtils;
import android.view.View;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
@@ -410,7 +409,7 @@ public class AmbientState {
if (!mPulsing || mHeadUpManager == null) {
return false;
}
- return mHeadUpManager.isAlerting(entry.key);
+ return mHeadUpManager.isAlerting(entry.getKey());
}
public boolean isPanelTracking() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index ec0c6348fd89..b4f7b59349d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -16,10 +16,9 @@
package com.android.systemui.statusbar.notification.stack;
-import android.content.Context;
import android.util.MathUtils;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -52,8 +51,8 @@ public class NotificationRoundnessManager implements OnHeadsUpChangedListener {
@Inject
NotificationRoundnessManager(
KeyguardBypassController keyguardBypassController,
- Context context) {
- int numberOfSections = NotificationData.getNotificationBuckets(context).length;
+ NotificationSectionsFeatureManager sectionsFeatureManager) {
+ int numberOfSections = sectionsFeatureManager.getNumberOfBuckets();
mFirstInSectionViews = new ActivatableNotificationView[numberOfSections];
mLastInSectionViews = new ActivatableNotificationView[numberOfSections];
mTmpFirstInSectionViews = new ActivatableNotificationView[numberOfSections];
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
index d0444ae81b89..54d406615ae0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
@@ -23,6 +23,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.PendingIntent;
import android.content.Intent;
import android.provider.Settings;
import android.view.LayoutInflater;
@@ -33,6 +34,10 @@ import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.people.DataListener;
+import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter;
+import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewBoundary;
+import com.android.systemui.statusbar.notification.people.PersonViewModel;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -42,6 +47,8 @@ import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.List;
+import kotlin.sequences.Sequence;
+
/**
* Manages the boundaries of the two notification sections (high priority and low priority). Also
* shows/hides the headers for those sections where appropriate.
@@ -58,11 +65,39 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
private final StatusBarStateController mStatusBarStateController;
private final ConfigurationController mConfigurationController;
private final int mNumberOfSections;
-
private boolean mInitialized = false;
+
private SectionHeaderView mGentleHeader;
private boolean mGentleHeaderVisible = false;
+ private boolean mPeopleHubVisible = false;
+ private PeopleHubView mPeopleHubView;
+ private final PeopleHubSectionFooterViewAdapter mPeopleHubViewAdapter;
+ private final PeopleHubSectionFooterViewBoundary mPeopleHubViewBoundary =
+ new PeopleHubSectionFooterViewBoundary() {
+ @Override
+ public void setVisible(boolean isVisible) {
+ if (mPeopleHubVisible != isVisible) {
+ mPeopleHubVisible = isVisible;
+ if (mInitialized) {
+ updateSectionBoundaries();
+ }
+ }
+ }
+
+ @NonNull
+ @Override
+ public View getAssociatedViewForClickAnimation() {
+ return mPeopleHubView;
+ }
+
+ @NonNull
+ @Override
+ public Sequence<DataListener<PersonViewModel>> getPersonViewAdapters() {
+ return mPeopleHubView.getPersonViewAdapters();
+ }
+ };
+
@Nullable private View.OnClickListener mOnClearGentleNotifsClickListener;
NotificationSectionsManager(
@@ -70,11 +105,13 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
ActivityStarter activityStarter,
StatusBarStateController statusBarStateController,
ConfigurationController configurationController,
+ PeopleHubSectionFooterViewAdapter peopleHubViewAdapter,
int numberOfSections) {
mParent = parent;
mActivityStarter = activityStarter;
mStatusBarStateController = statusBarStateController;
mConfigurationController = configurationController;
+ mPeopleHubViewAdapter = peopleHubViewAdapter;
mNumberOfSections = numberOfSections;
}
@@ -94,6 +131,7 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
}
mInitialized = true;
reinflateViews(layoutInflater);
+ mPeopleHubViewAdapter.bindView(mPeopleHubViewBoundary);
mConfigurationController.addCallback(mConfigurationListener);
}
@@ -101,23 +139,39 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
* Reinflates the entire notification header, including all decoration views.
*/
void reinflateViews(LayoutInflater layoutInflater) {
- int oldPos = -1;
+ int oldGentleHeaderPos = -1;
+ int oldPeopleHubPos = -1;
if (mGentleHeader != null) {
if (mGentleHeader.getTransientContainer() != null) {
mGentleHeader.getTransientContainer().removeView(mGentleHeader);
} else if (mGentleHeader.getParent() != null) {
- oldPos = mParent.indexOfChild(mGentleHeader);
+ oldGentleHeaderPos = mParent.indexOfChild(mGentleHeader);
mParent.removeView(mGentleHeader);
}
}
+ if (mPeopleHubView != null) {
+ if (mPeopleHubView.getTransientContainer() != null) {
+ mPeopleHubView.getTransientContainer().removeView(mPeopleHubView);
+ } else if (mPeopleHubView.getParent() != null) {
+ oldPeopleHubPos = mParent.indexOfChild(mPeopleHubView);
+ mParent.removeView(mPeopleHubView);
+ }
+ }
mGentleHeader = (SectionHeaderView) layoutInflater.inflate(
R.layout.status_bar_notification_section_header, mParent, false);
mGentleHeader.setOnHeaderClickListener(this::onGentleHeaderClick);
mGentleHeader.setOnClearAllClickListener(this::onClearGentleNotifsClick);
- if (oldPos != -1) {
- mParent.addView(mGentleHeader, oldPos);
+ if (oldGentleHeaderPos != -1) {
+ mParent.addView(mGentleHeader, oldGentleHeaderPos);
+ }
+
+ mPeopleHubView = (PeopleHubView) layoutInflater.inflate(
+ R.layout.people_strip, mParent, false);
+
+ if (oldPeopleHubPos != -1) {
+ mParent.addView(mPeopleHubView, oldPeopleHubPos);
}
}
@@ -145,7 +199,7 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
}
if (!begin) {
- begin = view == mGentleHeader;
+ begin = view == mGentleHeader || previous == mPeopleHubView;
}
return begin;
@@ -161,6 +215,8 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
return ((ExpandableNotificationRow) view).getEntry().getBucket();
} else if (view == mGentleHeader) {
return BUCKET_SILENT;
+ } else if (view == mPeopleHubView) {
+ return BUCKET_PEOPLE;
}
throw new IllegalArgumentException("I don't know how to find a bucket for this view :(");
@@ -175,6 +231,7 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
return;
}
+ int lastPersonIndex = -1;
int firstGentleNotifIndex = -1;
final int n = mParent.getChildCount();
@@ -183,6 +240,9 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
if (child instanceof ExpandableNotificationRow
&& child.getVisibility() != View.GONE) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ if (row.getEntry().getBucket() == BUCKET_PEOPLE) {
+ lastPersonIndex = i;
+ }
if (row.getEntry().getBucket() == BUCKET_SILENT) {
firstGentleNotifIndex = i;
break;
@@ -190,6 +250,11 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
}
}
+ if (adjustPeopleHubVisibilityAndPosition(lastPersonIndex)) {
+ // make room for peopleHub
+ firstGentleNotifIndex++;
+ }
+
adjustGentleHeaderVisibilityAndPosition(firstGentleNotifIndex);
mGentleHeader.setAreThereDismissableGentleNotifs(
@@ -232,6 +297,36 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
}
}
+ private boolean adjustPeopleHubVisibilityAndPosition(int lastPersonIndex) {
+ final boolean showPeopleHeader = mPeopleHubVisible
+ && mNumberOfSections > 2
+ && mStatusBarStateController.getState() != StatusBarState.KEYGUARD;
+ final int currentHubIndex = mParent.indexOfChild(mPeopleHubView);
+ final boolean currentlyVisible = currentHubIndex >= 0;
+ int targetIndex = lastPersonIndex + 1;
+
+ if (!showPeopleHeader) {
+ if (currentlyVisible) {
+ mParent.removeView(mPeopleHubView);
+ }
+ } else {
+ if (!currentlyVisible) {
+ if (mPeopleHubView.getTransientContainer() != null) {
+ mPeopleHubView.getTransientContainer().removeTransientView(mPeopleHubView);
+ mPeopleHubView.setTransientContainer(null);
+ }
+ mParent.addView(mPeopleHubView, targetIndex);
+ return true;
+ } else if (currentHubIndex != targetIndex - 1) {
+ if (currentHubIndex < targetIndex) {
+ targetIndex--;
+ }
+ mParent.changeViewPosition(mPeopleHubView, targetIndex);
+ }
+ }
+ return false;
+ }
+
/**
* Updates the boundaries (as tracked by their first and last views) of the priority sections.
*
@@ -284,12 +379,12 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
ActivatableNotificationView first = s.getFirstVisibleChild();
String fs = first == null ? "(null)"
: (first instanceof ExpandableNotificationRow)
- ? ((ExpandableNotificationRow) first).getEntry().key
+ ? ((ExpandableNotificationRow) first).getEntry().getKey()
: Integer.toHexString(System.identityHashCode(first));
ActivatableNotificationView last = s.getLastVisibleChild();
String ls = last == null ? "(null)"
: (last instanceof ExpandableNotificationRow)
- ? ((ExpandableNotificationRow) last).getEntry().key
+ ? ((ExpandableNotificationRow) last).getEntry().getKey()
: Integer.toHexString(System.identityHashCode(last));
android.util.Log.d(TAG, "updateSections: f=" + fs + " s=" + i);
android.util.Log.d(TAG, "updateSections: l=" + ls + " s=" + i);
@@ -324,6 +419,10 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
}
}
+ private void handlePeopleHubClick(PendingIntent pendingIntent) {
+ mActivityStarter.startPendingIntentDismissingKeyguard(pendingIntent, null, mPeopleHubView);
+ }
+
/**
* For now, declare the available notification buckets (sections) here so that other
* presentation code can decide what to do based on an entry's buckets
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index a67018ef9710..6dca7ee9e872 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -109,13 +109,14 @@ import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.ShadeViewRefactor;
import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -473,8 +474,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
private int mHeadsUpInset;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private NotificationIconAreaController mIconAreaController;
- private final NotificationLockscreenUserManager mLockscreenUserManager =
- Dependency.get(NotificationLockscreenUserManager.class);
+ private final NotificationLockscreenUserManager mLockscreenUserManager;
private final Rect mTmpRect = new Rect();
private final NotificationEntryManager mEntryManager =
Dependency.get(NotificationEntryManager.class);
@@ -497,8 +497,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
private NotificationPanelView mNotificationPanel;
private final ShadeController mShadeController = Dependency.get(ShadeController.class);
- private final NotificationGutsManager
- mNotificationGutsManager = Dependency.get(NotificationGutsManager.class);
+ private final NotificationGutsManager mNotificationGutsManager;
private final NotificationSectionsManager mSectionsManager;
private boolean mAnimateBottomOnLayout;
private float mLastSentAppear;
@@ -517,7 +516,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
StatusBarStateController statusBarStateController,
HeadsUpManagerPhone headsUpManager,
KeyguardBypassController keyguardBypassController,
- FalsingManager falsingManager) {
+ FalsingManager falsingManager,
+ NotificationLockscreenUserManager notificationLockscreenUserManager,
+ NotificationGutsManager notificationGutsManager,
+ NotificationSectionsFeatureManager sectionsFeatureManager,
+ PeopleHubSectionFooterViewAdapter peopleHubViewAdapter) {
super(context, attrs, 0, 0);
Resources res = getResources();
@@ -525,19 +528,22 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
mRoundnessManager = notificationRoundnessManager;
+ mLockscreenUserManager = notificationLockscreenUserManager;
+ mNotificationGutsManager = notificationGutsManager;
mHeadsUpManager = headsUpManager;
mHeadsUpManager.addListener(mRoundnessManager);
mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
mKeyguardBypassController = keyguardBypassController;
mFalsingManager = falsingManager;
- int[] buckets = NotificationData.getNotificationBuckets(context);
+ int[] buckets = sectionsFeatureManager.getNotificationBuckets();
mSectionsManager =
new NotificationSectionsManager(
this,
activityStarter,
statusBarStateController,
configurationController,
+ peopleHubViewAdapter,
buckets.length);
mSectionsManager.initialize(LayoutInflater.from(context));
mSectionsManager.setOnClearGentleNotifsClickListener(v -> {
@@ -599,7 +605,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
public void onPostEntryUpdated(NotificationEntry entry) {
- if (!entry.notification.isClearable()) {
+ if (!entry.getSbn().isClearable()) {
// The user may have performed a dismiss action on the notification, since it's
// not clearable we should snap it back.
snapViewIfNeeded(entry);
@@ -1624,7 +1630,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
&& mHeadsUpManager.getTopEntry().getRow() != row
&& mGroupManager.getGroupSummary(
- mHeadsUpManager.getTopEntry().notification)
+ mHeadsUpManager.getTopEntry().getSbn())
!= entry) {
continue;
}
@@ -5332,7 +5338,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
requestChildrenUpdate();
onUpdateRowStates();
- mEntryManager.updateNotifications();
+ mEntryManager.updateNotifications("StatusBar state changed");
updateVisibility();
}
@@ -5522,12 +5528,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
// TODO: This is a listener method; we shouldn't be calling it. Can we just
// call performRemoveNotification as below?
mEntryManager.removeNotification(
- rowToRemove.getEntry().key,
+ rowToRemove.getEntry().getKey(),
null /* ranking */,
NotificationListenerService.REASON_CANCEL_ALL);
} else {
mEntryManager.performRemoveNotification(
- rowToRemove.getEntry().notification,
+ rowToRemove.getEntry().getSbn(),
NotificationListenerService.REASON_CANCEL_ALL);
}
} else {
@@ -6491,12 +6497,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
@Override
public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
- mStatusBar.requestNotificationUpdate();
+ mStatusBar.requestNotificationUpdate("onGroupCreatedFromChildren");
}
@Override
public void onGroupsChanged() {
- mStatusBar.requestNotificationUpdate();
+ mStatusBar.requestNotificationUpdate("onGroupsChanged");
}
};
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
new file mode 100644
index 000000000000..e31ee024fa36
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.systemui.R
+import com.android.systemui.statusbar.notification.people.PersonViewModel
+import com.android.systemui.statusbar.notification.people.DataListener
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView
+
+class PeopleHubView(context: Context, attrs: AttributeSet) :
+ ActivatableNotificationView(context, attrs) {
+
+ private lateinit var contents: ViewGroup
+ private lateinit var personControllers: List<PersonDataListenerImpl>
+ val personViewAdapters: Sequence<DataListener<PersonViewModel?>>
+ get() = personControllers.asSequence()
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+ contents = requireViewById(R.id.people_list)
+ personControllers = (0 until contents.childCount)
+ .asSequence()
+ .mapNotNull { idx ->
+ (contents.getChildAt(idx) as? LinearLayout)?.let(::PersonDataListenerImpl)
+ }
+ .toList()
+ }
+
+ override fun getContentView(): View = contents
+
+ private inner class PersonDataListenerImpl(val viewGroup: ViewGroup) :
+ DataListener<PersonViewModel?> {
+
+ val nameView = viewGroup.requireViewById<TextView>(R.id.person_name)
+ val avatarView = viewGroup.requireViewById<ImageView>(R.id.person_icon)
+
+ override fun onDataChanged(data: PersonViewModel?) {
+ viewGroup.visibility = data?.let { View.VISIBLE } ?: View.INVISIBLE
+ nameView.text = data?.name
+ avatarView.setImageDrawable(data?.icon)
+ viewGroup.setOnClickListener { data?.onClick?.invoke() }
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
index 5912cd7b6433..f9b936763308 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
@@ -16,75 +16,52 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
-
import android.content.Context;
-import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
import android.view.IWindowManager;
import android.view.MotionEvent;
-import android.view.View;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
-import com.android.systemui.SysUiServiceProvider;
-import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.dagger.qualifiers.MainHandler;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import javax.inject.Inject;
-import javax.inject.Named;
/** A controller to control all auto-hide things. */
-public class AutoHideController implements CommandQueue.Callbacks {
+public class AutoHideController {
private static final String TAG = "AutoHideController";
private final IWindowManager mWindowManagerService;
private final Handler mHandler;
private final NotificationRemoteInputManager mRemoteInputManager;
- private final CommandQueue mCommandQueue;
private StatusBar mStatusBar;
private NavigationBarFragment mNavigationBar;
- @VisibleForTesting
- int mDisplayId;
- @VisibleForTesting
- int mSystemUiVisibility;
- // last value sent to window manager
- private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE;
+ private int mDisplayId;
private boolean mAutoHideSuspended;
- private static final long AUTOHIDE_TIMEOUT_MS = 2250;
+ private static final long AUTO_HIDE_TIMEOUT_MS = 2250;
private final Runnable mAutoHide = () -> {
- int requested = mSystemUiVisibility & ~getTransientMask();
- if (mSystemUiVisibility != requested) {
- notifySystemUiVisibilityChanged(requested);
+ if (isAnyTransientBarShown()) {
+ hideTransientBars();
}
};
@Inject
- public AutoHideController(Context context, @Named(MAIN_HANDLER_NAME) Handler handler) {
- mCommandQueue = SysUiServiceProvider.getComponent(context, CommandQueue.class);
- mCommandQueue.addCallback(this);
+ public AutoHideController(Context context, @MainHandler Handler handler,
+ NotificationRemoteInputManager notificationRemoteInputManager,
+ IWindowManager iWindowManager) {
mHandler = handler;
- mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
- mWindowManagerService = Dependency.get(IWindowManager.class);
+ mRemoteInputManager = notificationRemoteInputManager;
+ mWindowManagerService = iWindowManager;
mDisplayId = context.getDisplayId();
}
- @Override
- public void onDisplayRemoved(int displayId) {
- if (displayId == mDisplayId) {
- mCommandQueue.removeCallback(this);
- }
- }
-
void setStatusBar(StatusBar statusBar) {
mStatusBar = statusBar;
}
@@ -93,50 +70,18 @@ public class AutoHideController implements CommandQueue.Callbacks {
mNavigationBar = navigationBar;
}
- @Override
- public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
- int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds,
- boolean navbarColorManagedByIme) {
- if (displayId != mDisplayId) {
- return;
- }
- int oldVal = mSystemUiVisibility;
- int newVal = (oldVal & ~mask) | (vis & mask);
- int diff = newVal ^ oldVal;
-
- if (diff != 0) {
- mSystemUiVisibility = newVal;
-
- // ready to unhide
- if (hasStatusBar() && (vis & View.STATUS_BAR_UNHIDE) != 0) {
- mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
- }
-
- if (hasNavigationBar() && (vis & View.NAVIGATION_BAR_UNHIDE) != 0) {
- mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
- }
-
- // Re-send setSystemUiVisibility to update un-hide status.
- if (mSystemUiVisibility != newVal) {
- mCommandQueue.setSystemUiVisibility(mDisplayId, mSystemUiVisibility,
- fullscreenStackVis, dockedStackVis, mask, fullscreenStackBounds,
- dockedStackBounds, navbarColorManagedByIme);
- }
-
- notifySystemUiVisibilityChanged(mSystemUiVisibility);
- }
- }
-
- @VisibleForTesting
- void notifySystemUiVisibilityChanged(int vis) {
+ private void hideTransientBars() {
try {
- if (mLastDispatchedSystemUiVisibility != vis) {
- mWindowManagerService.statusBarVisibilityChanged(mDisplayId, vis);
- mLastDispatchedSystemUiVisibility = vis;
- }
+ mWindowManagerService.hideTransientBars(mDisplayId);
} catch (RemoteException ex) {
Log.w(TAG, "Cannot get WindowManager");
}
+ if (mStatusBar != null) {
+ mStatusBar.clearTransient();
+ }
+ if (mNavigationBar != null) {
+ mNavigationBar.clearTransient();
+ }
}
void resumeSuspendedAutoHide() {
@@ -155,13 +100,12 @@ public class AutoHideController implements CommandQueue.Callbacks {
if (checkBarModesRunnable != null) {
mHandler.removeCallbacks(checkBarModesRunnable);
}
- mAutoHideSuspended = (mSystemUiVisibility & getTransientMask()) != 0;
+ mAutoHideSuspended = isAnyTransientBarShown();
}
void touchAutoHide() {
// update transient bar auto hide
- if ((hasStatusBar() && mStatusBar.getStatusBarMode() == MODE_SEMI_TRANSPARENT)
- || hasNavigationBar() && mNavigationBar.isSemiTransparent()) {
+ if (isAnyTransientBarShown()) {
scheduleAutoHide();
} else {
cancelAutoHide();
@@ -169,9 +113,9 @@ public class AutoHideController implements CommandQueue.Callbacks {
}
private Runnable getCheckBarModesRunnable() {
- if (hasStatusBar()) {
+ if (mStatusBar != null) {
return () -> mStatusBar.checkBarModes();
- } else if (hasNavigationBar()) {
+ } else if (mNavigationBar != null) {
return () -> mNavigationBar.checkNavBarModes();
} else {
return null;
@@ -185,15 +129,14 @@ public class AutoHideController implements CommandQueue.Callbacks {
private void scheduleAutoHide() {
cancelAutoHide();
- mHandler.postDelayed(mAutoHide, AUTOHIDE_TIMEOUT_MS);
+ mHandler.postDelayed(mAutoHide, AUTO_HIDE_TIMEOUT_MS);
}
void checkUserAutoHide(MotionEvent event) {
- boolean shouldAutoHide =
- (mSystemUiVisibility & getTransientMask()) != 0 // a transient bar is revealed.
+ boolean shouldAutoHide = isAnyTransientBarShown()
&& event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar.
&& event.getX() == 0 && event.getY() == 0;
- if (hasStatusBar()) {
+ if (mStatusBar != null) {
// a touch outside both bars
shouldAutoHide &= !mRemoteInputManager.getController().isRemoteInputActive();
}
@@ -207,23 +150,8 @@ public class AutoHideController implements CommandQueue.Callbacks {
mHandler.postDelayed(mAutoHide, 350); // longer than app gesture -> flag clear
}
- private int getTransientMask() {
- int mask = 0;
- if (hasStatusBar()) {
- mask |= View.STATUS_BAR_TRANSIENT;
- }
- if (hasNavigationBar()) {
- mask |= View.NAVIGATION_BAR_TRANSIENT;
- }
- return mask;
- }
-
- boolean hasNavigationBar() {
- return mNavigationBar != null;
- }
-
- @VisibleForTesting
- boolean hasStatusBar() {
- return mStatusBar != null;
+ private boolean isAnyTransientBarShown() {
+ return (mStatusBar != null && mStatusBar.isTransientShown())
+ || mNavigationBar != null && mNavigationBar.isTransientShown();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 007c50c8765d..837517e2cc9b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -21,7 +21,7 @@ import android.os.Handler;
import android.provider.Settings.Secure;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
+import com.android.systemui.dagger.qualifiers.BgHandler;
import com.android.systemui.qs.AutoAddTracker;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.SecureSetting;
@@ -33,7 +33,6 @@ import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.HotspotController.Callback;
import javax.inject.Inject;
-import javax.inject.Named;
/**
* Manages which tiles should be automatically added to QS.
@@ -58,7 +57,7 @@ public class AutoTileManager {
@Inject
public AutoTileManager(Context context, AutoAddTracker autoAddTracker, QSTileHost host,
- @Named(Dependency.BG_HANDLER_NAME) Handler handler,
+ @BgHandler Handler handler,
HotspotController hotspotController,
DataSaverController dataSaverController,
ManagedProfileController managedProfileController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index 211a40a91101..e6731e6b8a34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -44,11 +44,11 @@ public class BarTransitions {
private static final boolean DEBUG = false;
private static final boolean DEBUG_COLORS = false;
- public static final int MODE_OPAQUE = 0;
+ public static final int MODE_TRANSPARENT = 0;
public static final int MODE_SEMI_TRANSPARENT = 1;
public static final int MODE_TRANSLUCENT = 2;
public static final int MODE_LIGHTS_OUT = 3;
- public static final int MODE_TRANSPARENT = 4;
+ public static final int MODE_OPAQUE = 4;
public static final int MODE_WARNING = 5;
public static final int MODE_LIGHTS_OUT_TRANSPARENT = 6;
@@ -72,7 +72,7 @@ public class BarTransitions {
private final View mView;
protected final BarBackgroundDrawable mBarBackground;
- private int mMode;
+ private @TransitionMode int mMode;
private boolean mAlwaysOpaque = false;
public BarTransitions(View view, int gradientResourceId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 2d012c93f42b..bd3d848eaf7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone;
import android.annotation.IntDef;
import android.content.Context;
+import android.content.res.Resources;
import android.hardware.biometrics.BiometricSourceType;
import android.metrics.LogMaker;
import android.os.Handler;
@@ -34,24 +35,28 @@ import com.android.keyguard.KeyguardConstants;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dependency;
+import com.android.systemui.dagger.qualifiers.MainResources;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import javax.inject.Inject;
+
/**
* Controller which coordinates all the biometric unlocking actions with the UI.
*/
public class BiometricUnlockController extends KeyguardUpdateMonitorCallback {
- private static final String TAG = "BiometricUnlockController";
+ private static final String TAG = "BiometricUnlockCtrl";
private static final boolean DEBUG_BIO_WAKELOCK = KeyguardConstants.DEBUG_BIOMETRIC_WAKELOCK;
private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000;
- private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock wakelock";
+ private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock:wakelock";
@IntDef(prefix = { "MODE_" }, value = {
MODE_NONE,
@@ -127,7 +132,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback {
private final KeyguardBypassController mKeyguardBypassController;
private PowerManager.WakeLock mWakeLock;
private final KeyguardUpdateMonitor mUpdateMonitor;
- private final UnlockMethodCache mUnlockMethodCache;
+ private DozeParameters mDozeParameters;
+ private final KeyguardStateController mKeyguardStateController;
private final StatusBarWindowController mStatusBarWindowController;
private final Context mContext;
private final int mWakeUpDelay;
@@ -143,36 +149,20 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback {
private boolean mHasScreenTurnedOnSinceAuthenticating;
private boolean mFadedAwayAfterWakeAndUnlock;
- private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
+ private final MetricsLogger mMetricsLogger;
- public BiometricUnlockController(Context context,
- DozeScrimController dozeScrimController,
- KeyguardViewMediator keyguardViewMediator,
- ScrimController scrimController,
- StatusBar statusBar,
- UnlockMethodCache unlockMethodCache, Handler handler,
+ @Inject
+ public BiometricUnlockController(Context context, DozeScrimController dozeScrimController,
+ KeyguardViewMediator keyguardViewMediator, ScrimController scrimController,
+ StatusBar statusBar, KeyguardStateController keyguardStateController, Handler handler,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- KeyguardBypassController keyguardBypassController) {
- this(context, dozeScrimController, keyguardViewMediator, scrimController, statusBar,
- unlockMethodCache, handler, keyguardUpdateMonitor,
- context.getResources()
- .getInteger(com.android.internal.R.integer.config_wakeUpDelayDoze),
- keyguardBypassController);
- }
-
- @VisibleForTesting
- protected BiometricUnlockController(Context context,
- DozeScrimController dozeScrimController,
- KeyguardViewMediator keyguardViewMediator,
- ScrimController scrimController,
- StatusBar statusBar,
- UnlockMethodCache unlockMethodCache, Handler handler,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- int wakeUpDelay,
- KeyguardBypassController keyguardBypassController) {
+ @MainResources Resources resources,
+ KeyguardBypassController keyguardBypassController, DozeParameters dozeParameters,
+ MetricsLogger metricsLogger) {
mContext = context;
mPowerManager = context.getSystemService(PowerManager.class);
mUpdateMonitor = keyguardUpdateMonitor;
+ mDozeParameters = dozeParameters;
mUpdateMonitor.registerCallback(this);
mMediaManager = Dependency.get(NotificationMediaManager.class);
Dependency.get(WakefulnessLifecycle.class).addObserver(mWakefulnessObserver);
@@ -182,11 +172,12 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback {
mKeyguardViewMediator = keyguardViewMediator;
mScrimController = scrimController;
mStatusBar = statusBar;
- mUnlockMethodCache = unlockMethodCache;
+ mKeyguardStateController = keyguardStateController;
mHandler = handler;
- mWakeUpDelay = wakeUpDelay;
+ mWakeUpDelay = resources.getInteger(com.android.internal.R.integer.config_wakeUpDelayDoze);
mKeyguardBypassController = keyguardBypassController;
mKeyguardBypassController.setUnlockController(this);
+ mMetricsLogger = metricsLogger;
}
public void setStatusBarKeyguardViewManager(
@@ -286,7 +277,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback {
}
// During wake and unlock, we need to draw black before waking up to avoid abrupt
// brightness changes due to display state transitions.
- boolean alwaysOnEnabled = DozeParameters.getInstance(mContext).getAlwaysOn();
+ boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn();
boolean delayWakeUp = mode == MODE_WAKE_AND_UNLOCK && alwaysOnEnabled && mWakeUpDelay > 0;
Runnable wakeUp = ()-> {
if (!wasDeviceInteractive) {
@@ -416,7 +407,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback {
return MODE_ONLY_WAKE;
} else if (mDozeScrimController.isPulsing() && unlockingAllowed) {
return MODE_WAKE_AND_UNLOCK_PULSING;
- } else if (unlockingAllowed || !mUnlockMethodCache.isMethodSecure()) {
+ } else if (unlockingAllowed || !mKeyguardStateController.isMethodSecure()) {
return MODE_WAKE_AND_UNLOCK;
} else {
return MODE_SHOW_BOUNCER;
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 d655b2fef411..e78b85e5fd57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -38,7 +38,7 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
import com.android.systemui.statusbar.policy.EncryptionHelper;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
@@ -57,7 +57,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
public static final int FADE_IN_DELAY = 50;
private PhoneStatusBarView mStatusBar;
private StatusBarStateController mStatusBarStateController;
- private KeyguardMonitor mKeyguardMonitor;
+ private KeyguardStateController mKeyguardStateController;
private NetworkController mNetworkController;
private LinearLayout mSystemIconArea;
private View mClockView;
@@ -79,7 +79,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+ mKeyguardStateController = Dependency.get(KeyguardStateController.class);
mNetworkController = Dependency.get(NetworkController.class);
mStatusBarStateController = Dependency.get(StatusBarStateController.class);
mStatusBarComponent = SysUiServiceProvider.getComponent(getContext(), StatusBar.class);
@@ -207,8 +207,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
state |= DISABLE_CLOCK;
}
- if (!mKeyguardMonitor.isLaunchTransitionFadingAway()
- && !mKeyguardMonitor.isKeyguardFadingAway()
+ if (!mKeyguardStateController.isLaunchTransitionFadingAway()
+ && !mKeyguardStateController.isKeyguardFadingAway()
&& shouldHideNotificationIcons()
&& !(mStatusBarStateController.getState() == StatusBarState.KEYGUARD
&& headsUpVisible)) {
@@ -268,7 +268,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
* don't set the clock GONE otherwise it'll mess up the animation.
*/
private int clockHiddenMode() {
- if (!mStatusBar.isClosed() && !mKeyguardMonitor.isShowing()
+ if (!mStatusBar.isClosed() && !mKeyguardStateController.isShowing()
&& !mStatusBarStateController.isDozing()) {
return View.INVISIBLE;
}
@@ -345,11 +345,11 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
.withEndAction(null);
// Synchronize the motion with the Keyguard fading if necessary.
- if (mKeyguardMonitor.isKeyguardFadingAway()) {
+ if (mKeyguardStateController.isKeyguardFadingAway()) {
v.animate()
- .setDuration(mKeyguardMonitor.getKeyguardFadingAwayDuration())
+ .setDuration(mKeyguardStateController.getKeyguardFadingAwayDuration())
.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
- .setStartDelay(mKeyguardMonitor.getKeyguardFadingAwayDelay())
+ .setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay())
.start();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java
index 0d62703cbced..78ea5c03a7b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java
@@ -100,6 +100,7 @@ public class DoubleTapHelper {
event.getY() - mActivationY);
}
if (withinDoubleTapSlop) {
+ makeInactive();
if (!mDoubleTapListener.onDoubleTap()) {
return false;
}
@@ -134,6 +135,7 @@ public class DoubleTapHelper {
if (mActivated) {
mActivated = false;
mActivationListener.onActiveChanged(false);
+ mView.removeCallbacks(mTapTimeoutRunnable);
}
}
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 bb6a38e1dcf5..bc482353753d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.phone;
-import android.content.Context;
+import android.content.res.Resources;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.PowerManager;
import android.os.SystemProperties;
@@ -24,18 +24,21 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.util.MathUtils;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.MainResources;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.doze.DozeScreenState;
import com.android.systemui.tuner.TunerService;
import java.io.PrintWriter;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Retrieve doze information
*/
+@Singleton
public class DozeParameters implements TunerService.Tunable,
com.android.systemui.plugins.statusbar.DozeParameters {
private static final int MAX_DURATION = 60 * 1000;
@@ -44,35 +47,33 @@ public class DozeParameters implements TunerService.Tunable,
public static final boolean FORCE_BLANKING =
SystemProperties.getBoolean("debug.force_blanking", false);
- private static DozeParameters sInstance;
-
- private final Context mContext;
private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
private final PowerManager mPowerManager;
private final AlwaysOnDisplayPolicy mAlwaysOnPolicy;
+ private final Resources mResources;
private boolean mDozeAlwaysOn;
private boolean mControlScreenOffAnimation;
- public static DozeParameters getInstance(Context context) {
- if (sInstance == null) {
- sInstance = new DozeParameters(context);
- }
- return sInstance;
- }
-
- @VisibleForTesting
- protected DozeParameters(Context context) {
- mContext = context.getApplicationContext();
- mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
- mAlwaysOnPolicy = new AlwaysOnDisplayPolicy(mContext);
+ @Inject
+ protected DozeParameters(
+ @MainResources Resources resources,
+ AmbientDisplayConfiguration ambientDisplayConfiguration,
+ AlwaysOnDisplayPolicy alwaysOnDisplayPolicy,
+ PowerManager powerManager,
+ TunerService tunerService) {
+ mResources = resources;
+ mAmbientDisplayConfiguration = ambientDisplayConfiguration;
+ mAlwaysOnPolicy = alwaysOnDisplayPolicy;
mControlScreenOffAnimation = !getDisplayNeedsBlanking();
- mPowerManager = mContext.getSystemService(PowerManager.class);
+ mPowerManager = powerManager;
mPowerManager.setDozeAfterScreenOff(!mControlScreenOffAnimation);
- Dependency.get(TunerService.class).addTunable(this, Settings.Secure.DOZE_ALWAYS_ON,
+ tunerService.addTunable(
+ this,
+ Settings.Secure.DOZE_ALWAYS_ON,
Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
}
@@ -95,7 +96,7 @@ public class DozeParameters implements TunerService.Tunable,
}
public boolean getDozeSuspendDisplayStateSupported() {
- return mContext.getResources().getBoolean(R.bool.doze_suspend_display_state_supported);
+ return mResources.getBoolean(R.bool.doze_suspend_display_state_supported);
}
public int getPulseDuration() {
@@ -103,7 +104,7 @@ public class DozeParameters implements TunerService.Tunable,
}
public float getScreenBrightnessDoze() {
- return mContext.getResources().getInteger(
+ return mResources.getInteger(
com.android.internal.R.integer.config_screenBrightnessDoze) / 255f;
}
@@ -173,7 +174,7 @@ public class DozeParameters implements TunerService.Tunable,
* @return {@code true} if screen needs to be completely black before a power transition.
*/
public boolean getDisplayNeedsBlanking() {
- return FORCE_BLANKING || !FORCE_NO_BLANKING && mContext.getResources().getBoolean(
+ return FORCE_BLANKING || !FORCE_NO_BLANKING && mResources.getBoolean(
com.android.internal.R.bool.config_displayBlanksAfterDoze);
}
@@ -186,33 +187,24 @@ public class DozeParameters implements TunerService.Tunable,
return;
}
mControlScreenOffAnimation = controlScreenOffAnimation;
- getPowerManager().setDozeAfterScreenOff(!controlScreenOffAnimation);
- }
-
- @VisibleForTesting
- protected PowerManager getPowerManager() {
- return mPowerManager;
+ mPowerManager.setDozeAfterScreenOff(!controlScreenOffAnimation);
}
private boolean getBoolean(String propName, int resId) {
- return SystemProperties.getBoolean(propName, mContext.getResources().getBoolean(resId));
+ return SystemProperties.getBoolean(propName, mResources.getBoolean(resId));
}
private int getInt(String propName, int resId) {
- int value = SystemProperties.getInt(propName, mContext.getResources().getInteger(resId));
+ int value = SystemProperties.getInt(propName, mResources.getInteger(resId));
return MathUtils.constrain(value, 0, MAX_DURATION);
}
- private String getString(String propName, int resId) {
- return SystemProperties.get(propName, mContext.getString(resId));
- }
-
public int getPulseVisibleDurationExtended() {
return 2 * getPulseVisibleDuration();
}
public boolean doubleTapReportsTouchCoordinates() {
- return mContext.getResources().getBoolean(R.bool.doze_double_tap_reports_touch_coordinates);
+ return mResources.getBoolean(R.bool.doze_double_tap_reports_touch_coordinates);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 60e381a776d2..1ecc4899d5e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -22,18 +22,24 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
+import com.android.systemui.doze.DozeEvent;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Controller which handles all the doze animations of the scrims.
*/
+@Singleton
public class DozeScrimController implements StateListener {
private static final String TAG = "DozeScrimController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private final DozeLog mDozeLog;
private final DozeParameters mDozeParameters;
private final Handler mHandler = new Handler();
@@ -47,7 +53,7 @@ public class DozeScrimController implements StateListener {
public void onDisplayBlanked() {
if (DEBUG) {
Log.d(TAG, "Pulse in, mDozing=" + mDozing + " mPulseReason="
- + DozeLog.reasonToString(mPulseReason));
+ + DozeEvent.reasonToString(mPulseReason));
}
if (!mDozing) {
return;
@@ -68,8 +74,8 @@ public class DozeScrimController implements StateListener {
// Notifications should time out on their own. Pulses due to notifications should
// instead be managed externally based off the notification's lifetime.
// Dock also controls the time out by self.
- if (mPulseReason != DozeLog.PULSE_REASON_NOTIFICATION
- && mPulseReason != DozeLog.PULSE_REASON_DOCKING) {
+ if (mPulseReason != DozeEvent.PULSE_REASON_NOTIFICATION
+ && mPulseReason != DozeEvent.PULSE_REASON_DOCKING) {
mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration());
mHandler.postDelayed(mPulseOutExtended,
mDozeParameters.getPulseVisibleDurationExtended());
@@ -90,14 +96,16 @@ public class DozeScrimController implements StateListener {
*/
@Override
public boolean shouldTimeoutWallpaper() {
- return mPulseReason == DozeLog.PULSE_REASON_DOCKING;
+ return mPulseReason == DozeEvent.PULSE_REASON_DOCKING;
}
};
- public DozeScrimController(DozeParameters dozeParameters) {
+ @Inject
+ public DozeScrimController(DozeParameters dozeParameters, DozeLog dozeLog) {
mDozeParameters = dozeParameters;
//Never expected to be destroyed
Dependency.get(StatusBarStateController.class).addCallback(this);
+ mDozeLog = dozeLog;
}
@VisibleForTesting
@@ -168,14 +176,14 @@ public class DozeScrimController implements StateListener {
}
private void pulseStarted() {
- DozeLog.tracePulseStart(mPulseReason);
+ mDozeLog.tracePulseStart(mPulseReason);
if (mPulseCallback != null) {
mPulseCallback.onPulseStarted();
}
}
private void pulseFinished() {
- DozeLog.tracePulseFinish();
+ mDozeLog.tracePulseFinish();
if (mPulseCallback != null) {
mPulseCallback.onPulseFinished();
mPulseCallback = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
new file mode 100644
index 000000000000..28543555bf4d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
+
+import android.annotation.NonNull;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.doze.DozeEvent;
+import com.android.systemui.doze.DozeHost;
+import com.android.systemui.doze.DozeLog;
+import com.android.systemui.doze.DozeReceiver;
+import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+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.policy.BatteryController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+
+import java.util.ArrayList;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+
+/**
+ * Implementation of DozeHost for SystemUI.
+ */
+@Singleton
+public final class DozeServiceHost implements DozeHost {
+ private static final String TAG = "DozeServiceHost";
+ private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+ private final DozeLog mDozeLog;
+ private final PowerManager mPowerManager;
+ private boolean mAnimateWakeup;
+ private boolean mAnimateScreenOff;
+ private boolean mIgnoreTouchWhilePulsing;
+ private Runnable mPendingScreenOffCallback;
+ @VisibleForTesting
+ boolean mWakeLockScreenPerformsAuth = SystemProperties.getBoolean(
+ "persist.sysui.wake_performs_auth", true);
+ private boolean mDozingRequested;
+ private boolean mDozing;
+ private boolean mPulsing;
+ private WakefulnessLifecycle mWakefulnessLifecycle;
+ private final SysuiStatusBarStateController mStatusBarStateController;
+ private final DeviceProvisionedController mDeviceProvisionedController;
+ private final HeadsUpManagerPhone mHeadsUpManagerPhone;
+ private final BatteryController mBatteryController;
+ private final ScrimController mScrimController;
+ private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
+ private BiometricUnlockController mBiometricUnlockController;
+ private final KeyguardViewMediator mKeyguardViewMediator;
+ private final AssistManager mAssistManager;
+ private final DozeScrimController mDozeScrimController;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final VisualStabilityManager mVisualStabilityManager;
+ private final PulseExpansionHandler mPulseExpansionHandler;
+ private final StatusBarWindowController mStatusBarWindowController;
+ private final NotificationWakeUpCoordinator mNotificationWakeUpCoordinator;
+ private NotificationIconAreaController mNotificationIconAreaController;
+ private StatusBarWindowViewController mStatusBarWindowViewController;
+ private StatusBarWindowView mStatusBarWindow;
+ private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ private NotificationPanelView mNotificationPanel;
+ private View mAmbientIndicationContainer;
+ private StatusBar mStatusBar;
+
+ @Inject
+ public DozeServiceHost(DozeLog dozeLog, PowerManager powerManager,
+ WakefulnessLifecycle wakefulnessLifecycle,
+ SysuiStatusBarStateController statusBarStateController,
+ DeviceProvisionedController deviceProvisionedController,
+ HeadsUpManagerPhone headsUpManagerPhone, BatteryController batteryController,
+ ScrimController scrimController,
+ Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
+ KeyguardViewMediator keyguardViewMediator,
+ AssistManager assistManager,
+ DozeScrimController dozeScrimController, KeyguardUpdateMonitor keyguardUpdateMonitor,
+ VisualStabilityManager visualStabilityManager,
+ PulseExpansionHandler pulseExpansionHandler,
+ StatusBarWindowController statusBarWindowController,
+ NotificationWakeUpCoordinator notificationWakeUpCoordinator) {
+ super();
+ mDozeLog = dozeLog;
+ mPowerManager = powerManager;
+ mWakefulnessLifecycle = wakefulnessLifecycle;
+ mStatusBarStateController = statusBarStateController;
+ mDeviceProvisionedController = deviceProvisionedController;
+ mHeadsUpManagerPhone = headsUpManagerPhone;
+ mBatteryController = batteryController;
+ mScrimController = scrimController;
+ mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
+ mKeyguardViewMediator = keyguardViewMediator;
+ mAssistManager = assistManager;
+ mDozeScrimController = dozeScrimController;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mVisualStabilityManager = visualStabilityManager;
+ mPulseExpansionHandler = pulseExpansionHandler;
+ mStatusBarWindowController = statusBarWindowController;
+ mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
+ }
+
+ // TODO: we should try to not pass status bar in here if we can avoid it.
+
+ /**
+ * Initialize instance with objects only available later during execution.
+ */
+ public void initialize(StatusBar statusBar,
+ NotificationIconAreaController notificationIconAreaController,
+ StatusBarWindowViewController statusBarWindowViewController,
+ StatusBarWindowView statusBarWindow,
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+ NotificationPanelView notificationPanel, View ambientIndicationContainer) {
+ mStatusBar = statusBar;
+ mNotificationIconAreaController = notificationIconAreaController;
+ mStatusBarWindowViewController = statusBarWindowViewController;
+ mStatusBarWindow = statusBarWindow;
+ mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+ mNotificationPanel = notificationPanel;
+ mAmbientIndicationContainer = ambientIndicationContainer;
+ mBiometricUnlockController = mBiometricUnlockControllerLazy.get();
+ }
+
+ @Override
+ public String toString() {
+ return "PSB.DozeServiceHost[mCallbacks=" + mCallbacks.size() + "]";
+ }
+
+ void firePowerSaveChanged(boolean active) {
+ for (Callback callback : mCallbacks) {
+ callback.onPowerSaveChanged(active);
+ }
+ }
+
+ void fireNotificationPulse(NotificationEntry entry) {
+ Runnable pulseSuppressedListener = () -> {
+ entry.setPulseSuppressed(true);
+ mNotificationIconAreaController.updateAodNotificationIcons();
+ };
+ for (Callback callback : mCallbacks) {
+ callback.onNotificationAlerted(pulseSuppressedListener);
+ }
+ }
+
+ boolean getDozingRequested() {
+ return mDozingRequested;
+ }
+
+ boolean isPulsing() {
+ return mPulsing;
+ }
+
+
+ @Override
+ public void addCallback(@NonNull Callback callback) {
+ mCallbacks.add(callback);
+ }
+
+ @Override
+ public void removeCallback(@NonNull Callback callback) {
+ mCallbacks.remove(callback);
+ }
+
+ @Override
+ public void startDozing() {
+ if (!mDozingRequested) {
+ mDozingRequested = true;
+ mDozeLog.traceDozing(mDozing);
+ updateDozing();
+ mStatusBar.updateIsKeyguard();
+ }
+ }
+
+ void updateDozing() {
+ // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked.
+ boolean
+ dozing =
+ mDozingRequested && mStatusBarStateController.getState() == StatusBarState.KEYGUARD
+ || mBiometricUnlockController.getMode()
+ == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
+ // When in wake-and-unlock we may not have received a change to StatusBarState
+ // but we still should not be dozing, manually set to false.
+ if (mBiometricUnlockController.getMode()
+ == BiometricUnlockController.MODE_WAKE_AND_UNLOCK) {
+ dozing = false;
+ }
+
+
+ mStatusBarStateController.setIsDozing(dozing);
+ }
+
+ @Override
+ public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) {
+ if (reason == DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS) {
+ mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
+ "com.android.systemui:LONG_PRESS");
+ mAssistManager.startAssist(new Bundle());
+ return;
+ }
+
+ if (reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
+ mScrimController.setWakeLockScreenSensorActive(true);
+ }
+
+ if (reason == DozeEvent.PULSE_REASON_DOCKING && mStatusBarWindow != null) {
+ mStatusBarWindowViewController.suppressWakeUpGesture(true);
+ }
+
+ boolean passiveAuthInterrupt = reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN
+ && mWakeLockScreenPerformsAuth;
+ // Set the state to pulsing, so ScrimController will know what to do once we ask it to
+ // execute the transition. The pulse callback will then be invoked when the scrims
+ // are black, indicating that StatusBar is ready to present the rest of the UI.
+ mPulsing = true;
+ mDozeScrimController.pulse(new PulseCallback() {
+ @Override
+ public void onPulseStarted() {
+ callback.onPulseStarted();
+ mStatusBar.updateNotificationPanelTouchState();
+ setPulsing(true);
+ }
+
+ @Override
+ public void onPulseFinished() {
+ mPulsing = false;
+ callback.onPulseFinished();
+ mStatusBar.updateNotificationPanelTouchState();
+ mScrimController.setWakeLockScreenSensorActive(false);
+ if (mStatusBarWindow != null) {
+ mStatusBarWindowViewController.suppressWakeUpGesture(false);
+ }
+ setPulsing(false);
+ }
+
+ private void setPulsing(boolean pulsing) {
+ mStatusBarStateController.setPulsing(pulsing);
+ mStatusBarKeyguardViewManager.setPulsing(pulsing);
+ mKeyguardViewMediator.setPulsing(pulsing);
+ mNotificationPanel.setPulsing(pulsing);
+ mVisualStabilityManager.setPulsing(pulsing);
+ mStatusBarWindowViewController.setPulsing(pulsing);
+ mIgnoreTouchWhilePulsing = false;
+ if (mKeyguardUpdateMonitor != null && passiveAuthInterrupt) {
+ mKeyguardUpdateMonitor.onAuthInterruptDetected(pulsing /* active */);
+ }
+ mStatusBar.updateScrimController();
+ mPulseExpansionHandler.setPulsing(pulsing);
+ mNotificationWakeUpCoordinator.setPulsing(pulsing);
+ }
+ }, reason);
+ // DozeScrimController is in pulse state, now let's ask ScrimController to start
+ // pulsing and draw the black frame, if necessary.
+ mStatusBar.updateScrimController();
+ }
+
+ @Override
+ public void stopDozing() {
+ if (mDozingRequested) {
+ mDozingRequested = false;
+ mDozeLog.traceDozing(mDozing);
+ updateDozing();
+ }
+ }
+
+ @Override
+ public void onIgnoreTouchWhilePulsing(boolean ignore) {
+ if (ignore != mIgnoreTouchWhilePulsing) {
+ mDozeLog.tracePulseTouchDisabledByProx(ignore);
+ }
+ mIgnoreTouchWhilePulsing = ignore;
+ if (mDozing && ignore) {
+ mStatusBarWindowViewController.cancelCurrentTouch();
+ }
+ }
+
+ @Override
+ public void dozeTimeTick() {
+ mNotificationPanel.dozeTimeTick();
+ if (mAmbientIndicationContainer instanceof DozeReceiver) {
+ ((DozeReceiver) mAmbientIndicationContainer).dozeTimeTick();
+ }
+ }
+
+ @Override
+ public boolean isPowerSaveActive() {
+ return mBatteryController.isAodPowerSave();
+ }
+
+ @Override
+ public boolean isPulsingBlocked() {
+ return mBiometricUnlockController.getMode()
+ == BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
+ }
+
+ @Override
+ public boolean isProvisioned() {
+ return mDeviceProvisionedController.isDeviceProvisioned()
+ && mDeviceProvisionedController.isCurrentUserSetup();
+ }
+
+ @Override
+ public boolean isBlockingDoze() {
+ if (mBiometricUnlockController.hasPendingAuthentication()) {
+ Log.i(StatusBar.TAG, "Blocking AOD because fingerprint has authenticated");
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void extendPulse(int reason) {
+ if (reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
+ mScrimController.setWakeLockScreenSensorActive(true);
+ }
+ if (mDozeScrimController.isPulsing() && mHeadsUpManagerPhone.hasNotifications()) {
+ mHeadsUpManagerPhone.extendHeadsUp();
+ } else {
+ mDozeScrimController.extendPulse();
+ }
+ }
+
+ @Override
+ public void stopPulsing() {
+ if (mDozeScrimController.isPulsing()) {
+ mDozeScrimController.pulseOutNow();
+ }
+ }
+
+ @Override
+ public void setAnimateWakeup(boolean animateWakeup) {
+ if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_AWAKE
+ || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_WAKING) {
+ // Too late to change the wakeup animation.
+ return;
+ }
+ mAnimateWakeup = animateWakeup;
+ }
+
+ @Override
+ public void setAnimateScreenOff(boolean animateScreenOff) {
+ mAnimateScreenOff = animateScreenOff;
+ }
+
+ @Override
+ public void onSlpiTap(float screenX, float screenY) {
+ if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null
+ && mAmbientIndicationContainer.getVisibility() == View.VISIBLE) {
+ int[] locationOnScreen = new int[2];
+ mAmbientIndicationContainer.getLocationOnScreen(locationOnScreen);
+ float viewX = screenX - locationOnScreen[0];
+ float viewY = screenY - locationOnScreen[1];
+ if (0 <= viewX && viewX <= mAmbientIndicationContainer.getWidth()
+ && 0 <= viewY && viewY <= mAmbientIndicationContainer.getHeight()) {
+
+ // Dispatch a tap
+ long now = SystemClock.elapsedRealtime();
+ MotionEvent ev = MotionEvent.obtain(
+ now, now, MotionEvent.ACTION_DOWN, screenX, screenY, 0);
+ mAmbientIndicationContainer.dispatchTouchEvent(ev);
+ ev.recycle();
+ ev = MotionEvent.obtain(
+ now, now, MotionEvent.ACTION_UP, screenX, screenY, 0);
+ mAmbientIndicationContainer.dispatchTouchEvent(ev);
+ ev.recycle();
+ }
+ }
+ }
+
+ @Override
+ public void setDozeScreenBrightness(int value) {
+ mStatusBarWindowController.setDozeScreenBrightness(value);
+ }
+
+ @Override
+ public void setAodDimmingScrim(float scrimOpacity) {
+ mScrimController.setAodFrontScrimAlpha(scrimOpacity);
+ }
+
+
+
+ @Override
+ public void prepareForGentleSleep(Runnable onDisplayOffCallback) {
+ if (mPendingScreenOffCallback != null) {
+ Log.w(TAG, "Overlapping onDisplayOffCallback. Ignoring previous one.");
+ }
+ mPendingScreenOffCallback = onDisplayOffCallback;
+ mStatusBar.updateScrimController();
+ }
+
+ @Override
+ public void cancelGentleSleep() {
+ mPendingScreenOffCallback = null;
+ if (mScrimController.getState() == ScrimState.OFF) {
+ mStatusBar.updateScrimController();
+ }
+ }
+
+ /**
+ * When the dozing host is waiting for scrims to fade out to change the display state.
+ */
+ boolean hasPendingScreenOffCallback() {
+ return mPendingScreenOffCallback != null;
+ }
+
+ /**
+ * Executes an nullifies the pending display state callback.
+ *
+ * @see #hasPendingScreenOffCallback()
+ * @see #prepareForGentleSleep(Runnable)
+ */
+ void executePendingScreenOffCallback() {
+ if (mPendingScreenOffCallback == null) {
+ return;
+ }
+ mPendingScreenOffCallback.run();
+ mPendingScreenOffCallback = null;
+ }
+
+ boolean shouldAnimateWakeup() {
+ return mAnimateWakeup;
+ }
+
+ boolean shouldAnimateScreenOff() {
+ return mAnimateScreenOff;
+ }
+
+ public void setDozing(boolean dozing) {
+ mDozing = dozing;
+ }
+
+ boolean getIgnoreTouchWhilePulsing() {
+ return mIgnoreTouchWhilePulsing;
+ }
+}
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 4cd3ad27ab34..1c8e832d03d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -54,9 +54,7 @@ import com.android.systemui.R;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.WindowManagerWrapper;
import java.io.PrintWriter;
import java.util.concurrent.Executor;
@@ -70,15 +68,6 @@ public class EdgeBackGestureHandler implements DisplayListener {
private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt(
"gestures.back_timeout", 250);
- private final PinnedStackListener mImeChangedListener = new PinnedStackListener() {
- @Override
- public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
- // No need to thread jump, assignments are atomic
- mImeHeight = imeVisible ? imeHeight : 0;
- // TODO: Probably cancel any existing gesture
- }
- };
-
private ISystemGestureExclusionListener mGestureExclusionListener =
new ISystemGestureExclusionListener.Stub() {
@Override
@@ -126,11 +115,10 @@ public class EdgeBackGestureHandler implements DisplayListener {
private boolean mInRejectedExclusion = false;
private boolean mIsOnLeftEdge;
- private int mImeHeight = 0;
-
private boolean mIsAttached;
private boolean mIsGesturalModeEnabled;
private boolean mIsEnabled;
+ private boolean mIsNavBarShownTransiently;
private InputMonitor mInputMonitor;
private InputEventReceiver mInputEventReceiver;
@@ -195,6 +183,10 @@ public class EdgeBackGestureHandler implements DisplayListener {
updateCurrentUserResources(currentUserContext.getResources());
}
+ public void onNavBarTransientStateChanged(boolean isTransient) {
+ mIsNavBarShownTransiently = isTransient;
+ }
+
private void disposeInputChannel() {
if (mInputEventReceiver != null) {
mInputEventReceiver.dispose();
@@ -222,7 +214,6 @@ public class EdgeBackGestureHandler implements DisplayListener {
}
if (!mIsEnabled) {
- WindowManagerWrapper.getInstance().removePinnedStackListener(mImeChangedListener);
mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
try {
@@ -239,7 +230,6 @@ public class EdgeBackGestureHandler implements DisplayListener {
mContext.getMainThreadHandler());
try {
- WindowManagerWrapper.getInstance().addPinnedStackListener(mImeChangedListener);
WindowManagerGlobal.getWindowManagerService()
.registerSystemGestureExclusionListener(
mGestureExclusionListener, mDisplayId);
@@ -267,7 +257,7 @@ public class EdgeBackGestureHandler implements DisplayListener {
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT);
mEdgePanelLp.privateFlags |=
- WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
mEdgePanelLp.setTitle(TAG + mDisplayId);
mEdgePanelLp.accessibilityTitle = mContext.getString(R.string.nav_bar_edge_panel);
mEdgePanelLp.windowAnimations = 0;
@@ -296,13 +286,16 @@ public class EdgeBackGestureHandler implements DisplayListener {
}
private boolean isWithinTouchRegion(int x, int y) {
- if (y > (mDisplaySize.y - Math.max(mImeHeight, mNavBarHeight))) {
+ // Disallow if too far from the edge
+ if (x > mEdgeWidth + mLeftInset && x < (mDisplaySize.x - mEdgeWidth - mRightInset)) {
return false;
}
- if (x > mEdgeWidth + mLeftInset && x < (mDisplaySize.x - mEdgeWidth - mRightInset)) {
- return false;
+ // Always allow if the user is in a transient sticky immersive state
+ if (mIsNavBarShownTransiently) {
+ return true;
}
+
boolean isInExcludedRegion = mExcludeRegion.contains(x, y);
if (isInExcludedRegion) {
mOverviewProxyService.notifyBackAction(false /* completed */, -1, -1,
@@ -470,7 +463,6 @@ public class EdgeBackGestureHandler implements DisplayListener {
pw.println(" mInRejectedExclusion" + mInRejectedExclusion);
pw.println(" mExcludeRegion=" + mExcludeRegion);
pw.println(" mUnrestrictedExcludeRegion=" + mUnrestrictedExcludeRegion);
- pw.println(" mImeHeight=" + mImeHeight);
pw.println(" mIsAttached=" + mIsAttached);
pw.println(" mEdgeWidth=" + mEdgeWidth);
}
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 deb314bae5c9..a7849847387d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
@@ -82,7 +82,7 @@ public class FloatingRotationButton implements RotationButton {
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(mDiameter, mDiameter,
mMargin, mMargin, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, flags,
PixelFormat.TRANSLUCENT);
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("FloatingRotationButton");
switch (mWindowManager.getDefaultDisplay().getRotation()) {
case Surface.ROTATION_0:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index f53c4e8c818e..ce96005aa306 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -39,7 +39,7 @@ import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import java.util.function.BiConsumer;
@@ -89,7 +89,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
};
private boolean mAnimationsEnabled = true;
Point mPoint;
- private KeyguardMonitor mKeyguardMonitor;
+ private KeyguardStateController mKeyguardStateController;
public HeadsUpAppearanceController(
@@ -98,9 +98,10 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
View statusbarView,
SysuiStatusBarStateController statusBarStateController,
KeyguardBypassController keyguardBypassController,
+ KeyguardStateController keyguardStateController,
NotificationWakeUpCoordinator wakeUpCoordinator) {
this(notificationIconAreaController, headsUpManager, statusBarStateController,
- keyguardBypassController, wakeUpCoordinator,
+ keyguardBypassController, wakeUpCoordinator, keyguardStateController,
statusbarView.findViewById(R.id.heads_up_status_bar_view),
statusbarView.findViewById(R.id.notification_stack_scroller),
statusbarView.findViewById(R.id.notification_panel),
@@ -116,6 +117,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
StatusBarStateController stateController,
KeyguardBypassController bypassController,
NotificationWakeUpCoordinator wakeUpCoordinator,
+ KeyguardStateController keyguardStateController,
HeadsUpStatusBarView headsUpStatusBarView,
NotificationStackScrollLayout stackScroller,
NotificationPanelView panelView,
@@ -160,7 +162,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
mWakeUpCoordinator = wakeUpCoordinator;
wakeUpCoordinator.addListener(this);
mCommandQueue = getComponent(headsUpStatusBarView.getContext(), CommandQueue.class);
- mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+ mKeyguardStateController = keyguardStateController;
}
@@ -378,7 +380,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
boolean canShow = !mIsExpanded && notificationsShown;
if (mBypassController.getBypassEnabled() &&
(mStatusBarStateController.getState() == StatusBarState.KEYGUARD
- || mKeyguardMonitor.isKeyguardGoingAway())
+ || mKeyguardStateController.isKeyguardGoingAway())
&& notificationsShown) {
canShow = true;
}
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 a7e7f085ffd7..4e06c84c30be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -29,6 +29,7 @@ import android.view.DisplayCutout;
import android.view.Gravity;
import android.view.View;
import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
import androidx.collection.ArraySet;
@@ -196,9 +197,9 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
mReleaseOnExpandFinish = false;
} else {
for (NotificationEntry entry : mEntriesToRemoveAfterExpand) {
- if (isAlerting(entry.key)) {
+ if (isAlerting(entry.getKey())) {
// Maybe the heads-up was removed already
- removeAlertEntry(entry.key);
+ removeAlertEntry(entry.getKey());
}
}
}
@@ -290,7 +291,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
*/
public void setRemoteInputActive(
@NonNull NotificationEntry entry, boolean remoteInputActive) {
- HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.key);
+ HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.getKey());
if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
headsUpEntry.remoteInputActive = remoteInputActive;
if (remoteInputActive) {
@@ -306,7 +307,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
* area if it's pinned until it's hidden again.
*/
public void setMenuShown(@NonNull NotificationEntry entry, boolean menuShown) {
- HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key);
+ HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey());
if (headsUpEntry instanceof HeadsUpEntryPhone && entry.isRowPinned()) {
((HeadsUpEntryPhone) headsUpEntry).setMenuShownPinned(menuShown);
}
@@ -374,7 +375,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
} else {
if (topEntry.isChildInGroup()) {
final NotificationEntry groupSummary =
- mGroupManager.getGroupSummary(topEntry.notification);
+ mGroupManager.getGroupSummary(topEntry.getSbn());
if (groupSummary != null) {
topEntry = groupSummary;
}
@@ -390,7 +391,12 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
}
private void updateRegionForNotch(Region region) {
- DisplayCutout cutout = mStatusBarWindowView.getRootWindowInsets().getDisplayCutout();
+ WindowInsets windowInsets = mStatusBarWindowView.getRootWindowInsets();
+ if (windowInsets == null) {
+ Log.w(TAG, "StatusBarWindowView is not attached.");
+ return;
+ }
+ DisplayCutout cutout = windowInsets.getDisplayCutout();
if (cutout == null) {
return;
}
@@ -423,9 +429,9 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
public void onReorderingAllowed() {
mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false);
for (NotificationEntry entry : mEntriesToRemoveWhenReorderingAllowed) {
- if (isAlerting(entry.key)) {
+ if (isAlerting(entry.getKey())) {
// Maybe the heads-up was removed already
- removeAlertEntry(entry.key);
+ removeAlertEntry(entry.getKey());
}
}
mEntriesToRemoveWhenReorderingAllowed.clear();
@@ -442,7 +448,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
@Override
protected void onAlertEntryRemoved(AlertEntry alertEntry) {
- mKeysToRemoveWhenLeavingKeyguard.remove(alertEntry.mEntry.key);
+ mKeysToRemoveWhenLeavingKeyguard.remove(alertEntry.mEntry.getKey());
super.onAlertEntryRemoved(alertEntry);
mEntryPool.release((HeadsUpEntryPhone) alertEntry);
}
@@ -527,9 +533,9 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
} else if (mTrackingHeadsUp) {
mEntriesToRemoveAfterExpand.add(entry);
} else if (mIsAutoHeadsUp && mStatusBarState == StatusBarState.KEYGUARD) {
- mKeysToRemoveWhenLeavingKeyguard.add(entry.key);
+ mKeysToRemoveWhenLeavingKeyguard.add(entry.getKey());
} else {
- removeAlertEntry(entry.key);
+ removeAlertEntry(entry.getKey());
}
};
@@ -547,7 +553,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
if (mEntriesToRemoveWhenReorderingAllowed.contains(mEntry)) {
mEntriesToRemoveWhenReorderingAllowed.remove(mEntry);
}
- mKeysToRemoveWhenLeavingKeyguard.remove(mEntry.key);
+ mKeysToRemoveWhenLeavingKeyguard.remove(mEntry.getKey());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 2bcbc026d561..99da47a00c0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -81,6 +81,7 @@ import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.ExtensionController.Extension;
import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.PreviewInflater;
import com.android.systemui.tuner.LockscreenFragment.LockButtonFactory;
import com.android.systemui.tuner.TunerService;
@@ -90,7 +91,7 @@ import com.android.systemui.tuner.TunerService;
* text.
*/
public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickListener,
- UnlockMethodCache.OnUnlockMethodChangedListener,
+ KeyguardStateController.Callback,
AccessibilityController.AccessibilityStateChangedCallback {
final static String TAG = "StatusBar/KeyguardBottomAreaView";
@@ -118,7 +119,10 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250;
private EmergencyCarrierArea mEmergencyCarrierArea;
- private final UnlockMethodCache mUnlockMethodCache;
+
+ private final boolean mShowLeftAffordance;
+ private final boolean mShowCameraAffordance;
+
private KeyguardAffordanceView mRightAffordanceView;
private KeyguardAffordanceView mLeftAffordanceView;
private ViewGroup mIndicationArea;
@@ -130,7 +134,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
private View mCameraPreview;
private ActivityStarter mActivityStarter;
- private LockPatternUtils mLockPatternUtils;
+ private KeyguardStateController mKeyguardStateController;
private FlashlightController mFlashlightController;
private PreviewInflater mPreviewInflater;
private AccessibilityController mAccessibilityController;
@@ -185,7 +189,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- mUnlockMethodCache = UnlockMethodCache.getInstance(getContext());
+ mShowLeftAffordance = getResources().getBoolean(R.bool.config_keyguardShowLeftAffordance);
+ mShowCameraAffordance = getResources()
+ .getBoolean(R.bool.config_keyguardShowCameraAffordance);
}
private AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() {
@@ -227,7 +233,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mLockPatternUtils = new LockPatternUtils(mContext);
mPreviewInflater = new PreviewInflater(mContext, new LockPatternUtils(mContext),
new ActivityIntentHelper(mContext));
mPreviewContainer = findViewById(R.id.preview_container);
@@ -242,6 +247,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mBurnInYOffset = getResources().getDimensionPixelSize(
R.dimen.default_burn_in_prevention_offset);
updateCameraVisibility();
+ mKeyguardStateController = Dependency.get(KeyguardStateController.class);
+ mKeyguardStateController.addCallback(this);
setClipChildren(false);
setClipToPadding(false);
inflateCameraPreview();
@@ -279,13 +286,13 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
getContext().registerReceiverAsUser(mDevicePolicyReceiver,
UserHandle.ALL, filter, null, null);
Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mUpdateMonitorCallback);
- mUnlockMethodCache.addListener(this);
+ mKeyguardStateController.addCallback(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- mUnlockMethodCache.removeListener(this);
+ mKeyguardStateController.removeCallback(this);
mAccessibilityController.removeStateChangedCallback(this);
mRightExtension.destroy();
mLeftExtension.destroy();
@@ -372,8 +379,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
// Things are not set up yet; reply hazy, ask again later
return;
}
- mRightAffordanceView.setVisibility(!mDozing && mRightButton.getIcon().isVisible
- ? View.VISIBLE : View.GONE);
+ mRightAffordanceView.setVisibility(!mDozing && mShowCameraAffordance
+ && mRightButton.getIcon().isVisible ? View.VISIBLE : View.GONE);
}
/**
@@ -385,8 +392,12 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
private void updateLeftAffordanceIcon() {
+ if (!mShowLeftAffordance || mDozing) {
+ mLeftAffordanceView.setVisibility(GONE);
+ return;
+ }
IconState state = mLeftButton.getIcon();
- mLeftAffordanceView.setVisibility(!mDozing && state.isVisible ? View.VISIBLE : View.GONE);
+ mLeftAffordanceView.setVisibility(state.isVisible ? View.VISIBLE : View.GONE);
if (state.drawable != mLeftAffordanceView.getDrawable()
|| state.tint != mLeftAffordanceView.shouldTint()) {
mLeftAffordanceView.setImageDrawable(state.drawable, state.tint);
@@ -547,7 +558,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mAssistManager.launchVoiceAssistFromKeyguard();
}
};
- if (mStatusBar.isKeyguardCurrentlySecure()) {
+ if (!mKeyguardStateController.canDismissLockScreen()) {
AsyncTask.execute(runnable);
} else {
boolean dismissShade = !TextUtils.isEmpty(mRightButtonStr)
@@ -612,7 +623,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
@Override
- public void onUnlockMethodStateChanged() {
+ public void onUnlockedChanged() {
updateCameraVisibility();
}
@@ -771,10 +782,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
@Override
public IconState getIcon() {
mLeftIsVoiceAssist = canLaunchVoiceAssist();
- final boolean showAffordance =
- getResources().getBoolean(R.bool.config_keyguardShowLeftAffordance);
if (mLeftIsVoiceAssist) {
- mIconState.isVisible = mUserSetupComplete && showAffordance;
+ mIconState.isVisible = mUserSetupComplete && mShowLeftAffordance;
if (mLeftAssistIcon == null) {
mIconState.drawable = mContext.getDrawable(R.drawable.ic_mic_26dp);
} else {
@@ -783,7 +792,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mIconState.contentDescription = mContext.getString(
R.string.accessibility_voice_assist_button);
} else {
- mIconState.isVisible = mUserSetupComplete && showAffordance && isPhoneVisible();
+ mIconState.isVisible = mUserSetupComplete && mShowLeftAffordance
+ && isPhoneVisible();
mIconState.drawable = mContext.getDrawable(
com.android.internal.R.drawable.ic_phone);
mIconState.contentDescription = mContext.getString(
@@ -806,7 +816,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
public IconState getIcon() {
boolean isCameraDisabled = (mStatusBar != null) && !mStatusBar.isCameraAllowedByAdmin();
mIconState.isVisible = !isCameraDisabled
- && getResources().getBoolean(R.bool.config_keyguardShowCameraAffordance)
+ && mShowCameraAffordance
&& mUserSetupComplete
&& resolveCameraIntent() != null;
mIconState.drawable = mContext.getDrawable(R.drawable.ic_camera_alt_24dp);
@@ -817,11 +827,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
@Override
public Intent getIntent() {
- KeyguardUpdateMonitor updateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
- boolean canSkipBouncer = updateMonitor.getUserCanSkipBouncer(
- KeyguardUpdateMonitor.getCurrentUser());
- boolean secure = mUnlockMethodCache.isMethodSecure();
- return (secure && !canSkipBouncer) ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT;
+ boolean canDismissLs = mKeyguardStateController.canDismissLockScreen();
+ boolean secure = mKeyguardStateController.isMethodSecure();
+ return (secure && !canDismissLs) ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT;
}
}
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 d7f67cef033e..f34c15c7099d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.phone;
import static com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import android.content.Context;
@@ -38,14 +37,17 @@ import android.view.WindowInsets;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardHostView;
+import com.android.keyguard.KeyguardSecurityModel;
import com.android.keyguard.KeyguardSecurityView;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.DejankUtils;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.io.PrintWriter;
@@ -69,7 +71,7 @@ public class KeyguardBouncer {
private final Handler mHandler;
private final BouncerExpansionCallback mExpansionCallback;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final UnlockMethodCache mUnlockMethodCache;
+ private final KeyguardStateController mKeyguardStateController;
private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
new KeyguardUpdateMonitorCallback() {
@Override
@@ -97,7 +99,8 @@ public class KeyguardBouncer {
public KeyguardBouncer(Context context, ViewMediatorCallback callback,
LockPatternUtils lockPatternUtils, ViewGroup container,
DismissCallbackRegistry dismissCallbackRegistry, FalsingManager falsingManager,
- BouncerExpansionCallback expansionCallback, UnlockMethodCache unlockMethodCache,
+ BouncerExpansionCallback expansionCallback,
+ KeyguardStateController keyguardStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
KeyguardBypassController keyguardBypassController, Handler handler) {
mContext = context;
@@ -109,7 +112,7 @@ public class KeyguardBouncer {
mDismissCallbackRegistry = dismissCallbackRegistry;
mExpansionCallback = expansionCallback;
mHandler = handler;
- mUnlockMethodCache = unlockMethodCache;
+ mKeyguardStateController = keyguardStateController;
mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
mKeyguardBypassController = keyguardBypassController;
}
@@ -173,7 +176,7 @@ public class KeyguardBouncer {
// Split up the work over multiple frames.
DejankUtils.removeCallbacks(mResetRunnable);
- if (mUnlockMethodCache.isFaceAuthEnabled() && !needsFullscreenBouncer()
+ if (mKeyguardStateController.isFaceAuthEnabled() && !needsFullscreenBouncer()
&& !mKeyguardUpdateMonitor.userNeedsStrongAuth()
&& !mKeyguardBypassController.getBypassEnabled()) {
mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY);
@@ -459,15 +462,9 @@ public class KeyguardBouncer {
* notifications on Keyguard, like SIM PIN/PUK.
*/
public boolean needsFullscreenBouncer() {
- // TODO(b/140059518)
- return whitelistIpcs(() -> {
- ensureView();
- if (mKeyguardView != null) {
- SecurityMode mode = mKeyguardView.getSecurityMode();
- return mode == SecurityMode.SimPin || mode == SecurityMode.SimPuk;
- }
- return false;
- });
+ SecurityMode mode = Dependency.get(KeyguardSecurityModel.class).getSecurityMode(
+ KeyguardUpdateMonitor.getCurrentUser());
+ return mode == SecurityMode.SimPin || mode == SecurityMode.SimPuk;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index 832ea9e3d72e..aca7f443ea0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -23,6 +23,7 @@ import android.provider.Settings
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.tuner.TunerService
import java.io.PrintWriter
import javax.inject.Inject
@@ -31,7 +32,7 @@ import javax.inject.Singleton
@Singleton
class KeyguardBypassController {
- private val unlockMethodCache: UnlockMethodCache
+ private val mKeyguardStateController: KeyguardStateController
private val statusBarStateController: StatusBarStateController
private var hasFaceFeature: Boolean
@@ -47,7 +48,7 @@ class KeyguardBypassController {
* If face unlock dismisses the lock screen or keeps user on keyguard for the current user.
*/
var bypassEnabled: Boolean = false
- get() = field && unlockMethodCache.isFaceAuthEnabled
+ get() = field && mKeyguardStateController.isFaceAuthEnabled
private set
var bouncerShowing: Boolean = false
@@ -66,9 +67,10 @@ class KeyguardBypassController {
context: Context,
tunerService: TunerService,
statusBarStateController: StatusBarStateController,
- lockscreenUserManager: NotificationLockscreenUserManager
+ lockscreenUserManager: NotificationLockscreenUserManager,
+ keyguardStateController: KeyguardStateController
) {
- unlockMethodCache = UnlockMethodCache.getInstance(context)
+ this.mKeyguardStateController = keyguardStateController
this.statusBarStateController = statusBarStateController
hasFaceFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
index 00b764bfbe9b..bf887044e05b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -21,17 +21,16 @@ import android.hardware.TriggerEvent
import android.hardware.TriggerEventListener
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.Dependency
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.util.Assert
import com.android.systemui.util.sensors.AsyncSensorManager
class KeyguardLiftController constructor(
private val statusBarStateController: StatusBarStateController,
- private val asyncSensorManager: AsyncSensorManager
+ private val asyncSensorManager: AsyncSensorManager,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor
) : StatusBarStateController.StateListener, KeyguardUpdateMonitorCallback() {
- private val keyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor::class.java)
private val pickupSensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE)
private var isListening = false
private var bouncerVisible = false
@@ -66,6 +65,9 @@ class KeyguardLiftController constructor(
}
private fun updateListeningState() {
+ if (pickupSensor == null) {
+ return
+ }
val onKeyguard = keyguardUpdateMonitor.isKeyguardVisible &&
!statusBarStateController.isDozing
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index b0b656a1a951..2e776e39b54c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -16,15 +16,20 @@
package com.android.systemui.statusbar.phone;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_SIDE_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_TOP_BAR;
+
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
import android.content.Context;
import android.graphics.Color;
-import android.graphics.Rect;
-import android.view.View;
+import android.view.InsetsFlags;
+import android.view.ViewDebug;
+import android.view.WindowInsetsController.Appearance;
import com.android.internal.colorextraction.ColorExtractor.GradientColors;
+import com.android.internal.view.AppearanceRegion;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher;
@@ -49,13 +54,10 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
private BiometricUnlockController mBiometricUnlockController;
private LightBarTransitionsController mNavigationBarController;
- private int mSystemUiVisibility;
- private int mFullscreenStackVisibility;
- private int mDockedStackVisibility;
- private boolean mFullscreenLight;
- private boolean mDockedLight;
- private int mLastStatusBarMode;
- private int mLastNavigationBarMode;
+ private @Appearance int mAppearance;
+ private AppearanceRegion[] mAppearanceRegions = new AppearanceRegion[0];
+ private int mStatusBarMode;
+ private int mNavigationBarMode;
private final Color mDarkModeColor;
/**
@@ -75,8 +77,6 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
*/
private boolean mForceDarkForScrim;
- private final Rect mLastFullscreenBounds = new Rect();
- private final Rect mLastDockedBounds = new Rect();
private boolean mQsCustomizing;
private boolean mDirectReplying;
@@ -101,45 +101,32 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
mBiometricUnlockController = biometricUnlockController;
}
- public void onSystemUiVisibilityChanged(int fullscreenStackVis, int dockedStackVis,
- int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, boolean sbModeChanged,
+ void onStatusBarAppearanceChanged(AppearanceRegion[] appearanceRegions, boolean sbModeChanged,
int statusBarMode, boolean navbarColorManagedByIme) {
- int oldFullscreen = mFullscreenStackVisibility;
- int newFullscreen = (oldFullscreen & ~mask) | (fullscreenStackVis & mask);
- int diffFullscreen = newFullscreen ^ oldFullscreen;
- int oldDocked = mDockedStackVisibility;
- int newDocked = (oldDocked & ~mask) | (dockedStackVis & mask);
- int diffDocked = newDocked ^ oldDocked;
- if ((diffFullscreen & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0
- || (diffDocked & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0
- || sbModeChanged
- || !mLastFullscreenBounds.equals(fullscreenStackBounds)
- || !mLastDockedBounds.equals(dockedStackBounds)) {
-
- mFullscreenLight = isLight(newFullscreen, statusBarMode,
- View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
- mDockedLight = isLight(newDocked, statusBarMode, View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
- updateStatus(fullscreenStackBounds, dockedStackBounds);
+ final int numStacks = appearanceRegions.length;
+ boolean stackAppearancesChanged = mAppearanceRegions.length != numStacks;
+ for (int i = 0; i < numStacks && !stackAppearancesChanged; i++) {
+ stackAppearancesChanged |= !appearanceRegions[i].equals(mAppearanceRegions[i]);
+ }
+ if (stackAppearancesChanged || sbModeChanged) {
+ mAppearanceRegions = appearanceRegions;
+ onStatusBarModeChanged(statusBarMode);
}
-
- mFullscreenStackVisibility = newFullscreen;
- mDockedStackVisibility = newDocked;
- mLastStatusBarMode = statusBarMode;
mNavbarColorManagedByIme = navbarColorManagedByIme;
- mLastFullscreenBounds.set(fullscreenStackBounds);
- mLastDockedBounds.set(dockedStackBounds);
}
- public void onNavigationVisibilityChanged(int vis, int mask, boolean nbModeChanged,
+ void onStatusBarModeChanged(int newBarMode) {
+ mStatusBarMode = newBarMode;
+ updateStatus();
+ }
+
+ void onNavigationBarAppearanceChanged(@Appearance int appearance, boolean nbModeChanged,
int navigationBarMode, boolean navbarColorManagedByIme) {
- int oldVis = mSystemUiVisibility;
- int newVis = (oldVis & ~mask) | (vis & mask);
- int diffVis = newVis ^ oldVis;
- if ((diffVis & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0
- || nbModeChanged) {
- boolean last = mNavigationLight;
- mHasLightNavigationBar = isLight(vis, navigationBarMode,
- View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
+ int diff = appearance ^ mAppearance;
+ if ((diff & APPEARANCE_LIGHT_SIDE_BARS) != 0 || nbModeChanged) {
+ final boolean last = mNavigationLight;
+ mHasLightNavigationBar = isLight(appearance, navigationBarMode,
+ APPEARANCE_LIGHT_SIDE_BARS);
mNavigationLight = mHasLightNavigationBar
&& (mDirectReplying && mNavbarColorManagedByIme || !mForceDarkForScrim)
&& !mQsCustomizing;
@@ -147,17 +134,20 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
updateNavigation();
}
}
- mSystemUiVisibility = newVis;
- mLastNavigationBarMode = navigationBarMode;
+ mAppearance = appearance;
+ mNavigationBarMode = navigationBarMode;
mNavbarColorManagedByIme = navbarColorManagedByIme;
}
+ void onNavigationBarModeChanged(int newBarMode) {
+ mHasLightNavigationBar = isLight(mAppearance, newBarMode, APPEARANCE_LIGHT_SIDE_BARS);
+ }
+
private void reevaluate() {
- onSystemUiVisibilityChanged(mFullscreenStackVisibility,
- mDockedStackVisibility, 0 /* mask */, mLastFullscreenBounds, mLastDockedBounds,
- true /* sbModeChange*/, mLastStatusBarMode, mNavbarColorManagedByIme);
- onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */, true /* nbModeChanged */,
- mLastNavigationBarMode, mNavbarColorManagedByIme);
+ onStatusBarAppearanceChanged(mAppearanceRegions, true /* sbModeChange */, mStatusBarMode,
+ mNavbarColorManagedByIme);
+ onNavigationBarAppearanceChanged(mAppearance, true /* nbModeChanged */,
+ mNavigationBarMode, mNavbarColorManagedByIme);
}
public void setQsCustomizing(boolean customizing) {
@@ -191,10 +181,10 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
}
}
- private boolean isLight(int vis, int barMode, int flag) {
- boolean isTransparentBar = (barMode == MODE_TRANSPARENT
+ private static boolean isLight(int appearance, int barMode, int flag) {
+ final boolean isTransparentBar = (barMode == MODE_TRANSPARENT
|| barMode == MODE_LIGHTS_OUT_TRANSPARENT);
- boolean light = (vis & flag) != 0;
+ final boolean light = (appearance & flag) != 0;
return isTransparentBar && light;
}
@@ -207,49 +197,49 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
&& unlockMode != BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
}
- private void updateStatus(Rect fullscreenStackBounds, Rect dockedStackBounds) {
- boolean hasDockedStack = !dockedStackBounds.isEmpty();
+ private void updateStatus() {
+ final int numStacks = mAppearanceRegions.length;
+ int numLightStacks = 0;
+
+ // We can only have maximum one light stack.
+ int indexLightStack = -1;
- // If both are light or fullscreen is light and there is no docked stack, all icons get
- // dark.
- if ((mFullscreenLight && mDockedLight) || (mFullscreenLight && !hasDockedStack)) {
+ for (int i = 0; i < numStacks; i++) {
+ if (isLight(mAppearanceRegions[i].getAppearance(), mStatusBarMode,
+ APPEARANCE_LIGHT_TOP_BAR)) {
+ numLightStacks++;
+ indexLightStack = i;
+ }
+ }
+
+ // If all stacks are light, all icons get dark.
+ if (numLightStacks == numStacks) {
mStatusBarIconController.setIconsDarkArea(null);
mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange());
}
- // If no one is light or the fullscreen is not light and there is no docked stack,
- // all icons become white.
- else if ((!mFullscreenLight && !mDockedLight) || (!mFullscreenLight && !hasDockedStack)) {
+ // If no one is light, all icons become white.
+ else if (numLightStacks == 0) {
mStatusBarIconController.getTransitionsController().setIconsDark(
false, animateChange());
}
// Not the same for every stack, magic!
else {
- Rect bounds = mFullscreenLight ? fullscreenStackBounds : dockedStackBounds;
- if (bounds.isEmpty()) {
- mStatusBarIconController.setIconsDarkArea(null);
- } else {
- mStatusBarIconController.setIconsDarkArea(bounds);
- }
+ mStatusBarIconController.setIconsDarkArea(
+ mAppearanceRegions[indexLightStack].getBounds());
mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange());
}
}
private void updateNavigation() {
if (mNavigationBarController != null) {
- mNavigationBarController.setIconsDark(
- mNavigationLight, animateChange());
+ mNavigationBarController.setIconsDark(mNavigationLight, animateChange());
}
}
@Override
- public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
-
- }
-
- @Override
public void onPowerSaveChanged(boolean isPowerSave) {
reevaluate();
}
@@ -257,24 +247,21 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("LightBarController: ");
- pw.print(" mSystemUiVisibility=0x"); pw.print(
- Integer.toHexString(mSystemUiVisibility));
- pw.print(" mFullscreenStackVisibility=0x"); pw.print(
- Integer.toHexString(mFullscreenStackVisibility));
- pw.print(" mDockedStackVisibility=0x"); pw.println(
- Integer.toHexString(mDockedStackVisibility));
-
- pw.print(" mFullscreenLight="); pw.print(mFullscreenLight);
- pw.print(" mDockedLight="); pw.println(mDockedLight);
-
- pw.print(" mLastFullscreenBounds="); pw.print(mLastFullscreenBounds);
- pw.print(" mLastDockedBounds="); pw.println(mLastDockedBounds);
+ pw.print(" mAppearance=0x"); pw.println(ViewDebug.flagsToString(
+ InsetsFlags.class, "appearance", mAppearance));
+ final int numStacks = mAppearanceRegions.length;
+ for (int i = 0; i < numStacks; i++) {
+ final boolean isLight = isLight(mAppearanceRegions[i].getAppearance(), mStatusBarMode,
+ APPEARANCE_LIGHT_TOP_BAR);
+ pw.print(" stack #"); pw.print(i); pw.print(": ");
+ pw.print(mAppearanceRegions[i].toString()); pw.print(" isLight="); pw.println(isLight);
+ }
pw.print(" mNavigationLight="); pw.print(mNavigationLight);
pw.print(" mHasLightNavigationBar="); pw.println(mHasLightNavigationBar);
- pw.print(" mLastStatusBarMode="); pw.print(mLastStatusBarMode);
- pw.print(" mLastNavigationBarMode="); pw.println(mLastNavigationBarMode);
+ pw.print(" mStatusBarMode="); pw.print(mStatusBarMode);
+ pw.print(" mNavigationBarMode="); pw.println(mNavigationBarMode);
pw.print(" mForceDarkForScrim="); pw.print(mForceDarkForScrim);
pw.print(" mQsCustomizing="); pw.print(mQsCustomizing);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index d7097309ce20..de660ce18263 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -31,7 +31,7 @@ import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -47,7 +47,7 @@ public class LightBarTransitionsController implements Dumpable, Callbacks,
private final Handler mHandler;
private final DarkIntensityApplier mApplier;
- private final KeyguardMonitor mKeyguardMonitor;
+ private final KeyguardStateController mKeyguardStateController;
private final StatusBarStateController mStatusBarStateController;
private boolean mTransitionDeferring;
@@ -73,7 +73,7 @@ public class LightBarTransitionsController implements Dumpable, Callbacks,
public LightBarTransitionsController(Context context, DarkIntensityApplier applier) {
mApplier = applier;
mHandler = new Handler();
- mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+ mKeyguardStateController = Dependency.get(KeyguardStateController.class);
mStatusBarStateController = Dependency.get(StatusBarStateController.class);
SysUiServiceProvider.getComponent(context, CommandQueue.class)
.addCallback(this);
@@ -101,7 +101,7 @@ public class LightBarTransitionsController implements Dumpable, Callbacks,
@Override
public void appTransitionPending(int displayId, boolean forced) {
- if (mDisplayId != displayId || mKeyguardMonitor.isKeyguardGoingAway() && !forced) {
+ if (mDisplayId != displayId || mKeyguardStateController.isKeyguardGoingAway() && !forced) {
return;
}
mTransitionPending = true;
@@ -123,7 +123,7 @@ public class LightBarTransitionsController implements Dumpable, Callbacks,
@Override
public void appTransitionStarting(int displayId, long startTime, long duration,
boolean forced) {
- if (mDisplayId != displayId || mKeyguardMonitor.isKeyguardGoingAway() && !forced) {
+ if (mDisplayId != displayId || mKeyguardStateController.isKeyguardGoingAway() && !forced) {
return;
}
if (mTransitionPending && mTintChangePending) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 67810738b17e..4927ec8a9a47 100755
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -53,7 +53,7 @@ import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.statusbar.phone.ScrimController.ScrimVisibility;
import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
@@ -68,9 +68,8 @@ import javax.inject.Named;
*/
public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChangedListener,
StatusBarStateController.StateListener, ConfigurationController.ConfigurationListener,
- UnlockMethodCache.OnUnlockMethodChangedListener,
- NotificationWakeUpCoordinator.WakeUpListener, ViewTreeObserver.OnPreDrawListener,
- OnHeadsUpChangedListener {
+ KeyguardStateController.Callback, NotificationWakeUpCoordinator.WakeUpListener,
+ ViewTreeObserver.OnPreDrawListener, OnHeadsUpChangedListener {
private static final int STATE_LOCKED = 0;
private static final int STATE_LOCK_OPEN = 1;
@@ -78,11 +77,10 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
private static final int STATE_BIOMETRICS_ERROR = 3;
private final ConfigurationController mConfigurationController;
private final StatusBarStateController mStatusBarStateController;
- private final UnlockMethodCache mUnlockMethodCache;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final AccessibilityController mAccessibilityController;
private final DockManager mDockManager;
- private final KeyguardMonitor mKeyguardMonitor;
+ private final KeyguardStateController mKeyguardStateController;
private final KeyguardBypassController mBypassController;
private final NotificationWakeUpCoordinator mWakeUpCoordinator;
private final HeadsUpManagerPhone mHeadsUpManager;
@@ -107,13 +105,13 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
private boolean mUpdatePending;
private boolean mBouncerPreHideAnimation;
- private final KeyguardMonitor.Callback mKeyguardMonitorCallback =
- new KeyguardMonitor.Callback() {
+ private final KeyguardStateController.Callback mKeyguardMonitorCallback =
+ new KeyguardStateController.Callback() {
@Override
public void onKeyguardShowingChanged() {
boolean force = false;
boolean wasShowing = mKeyguardShowing;
- mKeyguardShowing = mKeyguardMonitor.isShowing();
+ mKeyguardShowing = mKeyguardStateController.isShowing();
if (!wasShowing && mKeyguardShowing && mBlockUpdates) {
mBlockUpdates = false;
force = true;
@@ -126,7 +124,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
@Override
public void onKeyguardFadingAwayChanged() {
- if (!mKeyguardMonitor.isKeyguardFadingAway()) {
+ if (!mKeyguardStateController.isKeyguardFadingAway()) {
mBouncerPreHideAnimation = false;
if (mBlockUpdates) {
mBlockUpdates = false;
@@ -134,6 +132,11 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
}
}
}
+
+ @Override
+ public void onUnlockedChanged() {
+ update();
+ }
};
private final DockManager.DockEventListener mDockEventListener =
new DockManager.DockEventListener() {
@@ -181,19 +184,18 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
AccessibilityController accessibilityController,
KeyguardBypassController bypassController,
NotificationWakeUpCoordinator wakeUpCoordinator,
- KeyguardMonitor keyguardMonitor,
+ KeyguardStateController keyguardStateController,
@Nullable DockManager dockManager,
HeadsUpManagerPhone headsUpManager) {
super(context, attrs);
mContext = context;
- mUnlockMethodCache = UnlockMethodCache.getInstance(context);
mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
mAccessibilityController = accessibilityController;
mConfigurationController = configurationController;
mStatusBarStateController = statusBarStateController;
mBypassController = bypassController;
mWakeUpCoordinator = wakeUpCoordinator;
- mKeyguardMonitor = keyguardMonitor;
+ mKeyguardStateController = keyguardStateController;
mDockManager = dockManager;
mHeadsUpManager = headsUpManager;
}
@@ -203,9 +205,8 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
super.onAttachedToWindow();
mStatusBarStateController.addCallback(this);
mConfigurationController.addCallback(this);
- mKeyguardMonitor.addCallback(mKeyguardMonitorCallback);
+ mKeyguardStateController.addCallback(mKeyguardMonitorCallback);
mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
- mUnlockMethodCache.addListener(this);
mWakeUpCoordinator.addListener(this);
mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure();
if (mDockManager != null) {
@@ -221,9 +222,8 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
mStatusBarStateController.removeCallback(this);
mConfigurationController.removeCallback(this);
mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
- mKeyguardMonitor.removeCallback(mKeyguardMonitorCallback);
+ mKeyguardStateController.removeCallback(mKeyguardMonitorCallback);
mWakeUpCoordinator.removeListener(this);
- mUnlockMethodCache.removeListener(this);
if (mDockManager != null) {
mDockManager.removeListener(mDockEventListener);
}
@@ -370,15 +370,15 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
}
private boolean canBlockUpdates() {
- return mKeyguardShowing || mKeyguardMonitor.isKeyguardFadingAway();
+ return mKeyguardShowing || mKeyguardStateController.isKeyguardFadingAway();
}
private void updateClickability() {
if (mAccessibilityController == null) {
return;
}
- boolean canLock = mUnlockMethodCache.isMethodSecure()
- && mUnlockMethodCache.canSkipBouncer();
+ boolean canLock = mKeyguardStateController.isMethodSecure()
+ && mKeyguardStateController.canDismissLockScreen();
boolean clickToUnlock = mAccessibilityController.isAccessibilityEnabled();
setClickable(clickToUnlock);
setLongClickable(canLock && !clickToUnlock);
@@ -523,8 +523,8 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
private int getState() {
KeyguardUpdateMonitor updateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
- if ((mUnlockMethodCache.canSkipBouncer() || !mKeyguardShowing || mBouncerPreHideAnimation
- || mKeyguardMonitor.isKeyguardGoingAway()) && !mSimLocked) {
+ if ((mKeyguardStateController.canDismissLockScreen() || !mKeyguardShowing
+ || mKeyguardStateController.isKeyguardGoingAway()) && !mSimLocked) {
return STATE_LOCK_OPEN;
} else if (mTransientBiometricsError) {
return STATE_BIOMETRICS_ERROR;
@@ -582,11 +582,6 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange
update(true /* force */);
}
- @Override
- public void onUnlockMethodStateChanged() {
- update();
- }
-
/**
* We need to hide the lock whenever there's a fingerprint unlock, otherwise you'll see the
* icon on top of the black front scrim.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index e34c639a23c2..4d6b54ccfaff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -22,7 +22,6 @@ import android.app.IWallpaperManager;
import android.app.IWallpaperManagerCallback;
import android.app.WallpaperColors;
import android.app.WallpaperManager;
-import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -35,7 +34,6 @@ import android.os.AsyncTask;
import android.os.Handler;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Log;
@@ -47,9 +45,13 @@ import libcore.io.IoUtils;
import java.util.Objects;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Manages the lockscreen wallpaper.
*/
+@Singleton
public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implements Runnable {
private static final String TAG = "LockscreenWallpaper";
@@ -57,9 +59,8 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen
private final NotificationMediaManager mMediaManager =
Dependency.get(NotificationMediaManager.class);
- private final StatusBar mBar;
private final WallpaperManager mWallpaperManager;
- private final Handler mH;
+ private Handler mH;
private final KeyguardUpdateMonitor mUpdateMonitor;
private boolean mCached;
@@ -70,25 +71,32 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen
private UserHandle mSelectedUser;
private AsyncTask<Void, Void, LoaderResult> mLoader;
- public LockscreenWallpaper(Context ctx, StatusBar bar, Handler h) {
- mBar = bar;
- mH = h;
- mWallpaperManager = (WallpaperManager) ctx.getSystemService(Context.WALLPAPER_SERVICE);
+ @Inject
+ public LockscreenWallpaper(WallpaperManager wallpaperManager,
+ @Nullable IWallpaperManager iWallpaperManager,
+ KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ mWallpaperManager = wallpaperManager;
mCurrentUserId = ActivityManager.getCurrentUser();
- mUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
+ mUpdateMonitor = keyguardUpdateMonitor;
- IWallpaperManager service = IWallpaperManager.Stub.asInterface(
- ServiceManager.getService(Context.WALLPAPER_SERVICE));
- if (service != null) {
+ if (iWallpaperManager != null) {
// Service is disabled on some devices like Automotive
try {
- service.setLockWallpaperCallback(this);
+ iWallpaperManager.setLockWallpaperCallback(this);
} catch (RemoteException e) {
Log.e(TAG, "System dead?" + e);
}
}
}
+ void setHandler(Handler handler) {
+ if (mH != null) {
+ Log.wtfStack(TAG, "Handler has already been set. Trying to double initialize?");
+ return;
+ }
+ mH = handler;
+ }
+
public Bitmap getBitmap() {
if (mCached) {
return mCache;
@@ -176,6 +184,10 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen
}
private void postUpdateWallpaper() {
+ if (mH == null) {
+ Log.wtfStack(TAG, "Trying to use LockscreenWallpaper before initialization.");
+ return;
+ }
mH.removeCallbacks(this);
mH.post(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt
index 7dcc2fcfe2b2..53601babfd56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone
import android.content.Context
import android.view.View
+import android.view.ViewGroup.MarginLayoutParams
import android.widget.FrameLayout
import com.android.systemui.plugins.NPVPlugin
import com.android.systemui.plugins.PluginListener
@@ -36,6 +37,7 @@ class NPVPluginManager(
private var plugin: NPVPlugin? = null
private var animator = createAnimator()
+ private var yOffset = 0f
private fun createAnimator() = TouchAnimator.Builder()
.addFloat(parent, "alpha", 1f, 0f)
@@ -76,7 +78,7 @@ class NPVPluginManager(
}
fun setExpansion(expansion: Float, headerTranslation: Float, heightDiff: Float) {
- parent.setTranslationY(expansion * heightDiff + headerTranslation)
+ parent.setTranslationY(expansion * heightDiff + headerTranslation + yOffset)
if (!expansion.isNaN()) animator.setPosition(expansion)
}
@@ -88,5 +90,13 @@ class NPVPluginManager(
animator = createAnimator()
}
- fun getHeight() = if (plugin != null) parent.height else 0
+ fun getHeight() =
+ if (plugin != null) {
+ parent.height + (parent.getLayoutParams() as MarginLayoutParams).topMargin
+ } else 0
+
+ fun setYOffset(y: Float) {
+ yOffset = y
+ parent.setTranslationY(yOffset)
+ }
}
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 2b8c86b8c549..7030dfc4c33b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -21,6 +21,10 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.WindowType;
import static android.app.StatusBarManager.WindowVisibleState;
import static android.app.StatusBarManager.windowStateToString;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.containsType;
+import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_SIDE_BARS;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
@@ -31,7 +35,6 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OU
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE;
@@ -52,7 +55,6 @@ import android.content.IntentFilter;
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.graphics.PixelFormat;
-import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
import android.net.Uri;
import android.os.Binder;
@@ -67,12 +69,14 @@ import android.telecom.TelecomManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.Display;
+import android.view.InsetsState.InternalInsetType;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowInsetsController.Appearance;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
@@ -84,11 +88,13 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.LatencyTracker;
+import com.android.internal.view.AppearanceRegion;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.ScreenDecorations;
import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.assist.AssistHandleViewController;
import com.android.systemui.assist.AssistManager;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
import com.android.systemui.model.SysUiState;
@@ -127,7 +133,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
private static final boolean DEBUG = false;
private static final String EXTRA_DISABLE_STATE = "disabled_state";
private static final String EXTRA_DISABLE2_STATE = "disabled2_state";
- private static final String EXTRA_SYSTEM_UI_VISIBILITY = "system_ui_visibility";
+ private static final String EXTRA_APPEARANCE = "appearance";
+ private static final String EXTRA_TRANSIENT_STATE = "transient_state";
/** Allow some time inbetween the long press for back and recents. */
private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
@@ -139,6 +146,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
private final MetricsLogger mMetricsLogger;
private final DeviceProvisionedController mDeviceProvisionedController;
private final StatusBarStateController mStatusBarStateController;
+ private final NavigationModeController mNavigationModeController;
protected NavigationBarView mNavigationBarView = null;
@@ -163,18 +171,26 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
private Locale mLocale;
private int mLayoutDirection;
- private int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
+ /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int) */
+ private @Appearance int mAppearance;
+
+ private boolean mTransientShown;
private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
private LightBarController mLightBarController;
private AutoHideController mAutoHideController;
private OverviewProxyService mOverviewProxyService;
+ private final BroadcastDispatcher mBroadcastDispatcher;
+
@VisibleForTesting
public int mDisplayId;
private boolean mIsOnDefaultDisplay;
public boolean mHomeBlockedThisTouch;
- private ScreenDecorations mScreenDecorations;
+
+ /** Only for default display */
+ @Nullable
+ private AssistHandleViewController mAssistHandlerViewController;
private Handler mHandler = Dependency.get(Dependency.MAIN_HANDLER);
@@ -248,7 +264,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
AssistManager assistManager, OverviewProxyService overviewProxyService,
NavigationModeController navigationModeController,
StatusBarStateController statusBarStateController,
- SysUiState sysUiFlagsContainer) {
+ SysUiState sysUiFlagsContainer,
+ BroadcastDispatcher broadcastDispatcher) {
mAccessibilityManagerWrapper = accessibilityManagerWrapper;
mDeviceProvisionedController = deviceProvisionedController;
mStatusBarStateController = statusBarStateController;
@@ -257,7 +274,9 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
mSysUiFlagsContainer = sysUiFlagsContainer;
mAssistantAvailable = mAssistManager.getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
mOverviewProxyService = overviewProxyService;
+ mNavigationModeController = navigationModeController;
mNavBarMode = navigationModeController.addListener(this);
+ mBroadcastDispatcher = broadcastDispatcher;
}
// ----- Fragment Lifecycle Callbacks -----
@@ -285,7 +304,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
if (savedInstanceState != null) {
mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0);
mDisabledFlags2 = savedInstanceState.getInt(EXTRA_DISABLE2_STATE, 0);
- mSystemUiVisibility = savedInstanceState.getInt(EXTRA_SYSTEM_UI_VISIBILITY, 0);
+ mAppearance = savedInstanceState.getInt(EXTRA_APPEARANCE, 0);
+ mTransientShown = savedInstanceState.getBoolean(EXTRA_TRANSIENT_STATE, false);
}
mAccessibilityManagerWrapper.addCallback(mAccessibilityListener);
@@ -296,6 +316,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
@Override
public void onDestroy() {
super.onDestroy();
+ mNavigationModeController.removeListener(this);
mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener);
mContentResolver.unregisterContentObserver(mMagnificationObserver);
mContentResolver.unregisterContentObserver(mAssistContentObserver);
@@ -334,7 +355,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_USER_SWITCHED);
- getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
+ mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, Handler.getMain(),
+ UserHandle.ALL);
notifyNavigationBarScreenOn();
mOverviewProxyService.addCallback(mOverviewProxyListener);
@@ -357,22 +379,27 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
mDisabledFlags2 |= StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS;
}
setDisabled2Flags(mDisabledFlags2);
-
- mScreenDecorations = SysUiServiceProvider.getComponent(getContext(),
- ScreenDecorations.class);
- getBarTransitions().addDarkIntensityListener(mScreenDecorations);
+ if (mIsOnDefaultDisplay) {
+ mAssistHandlerViewController =
+ new AssistHandleViewController(mHandler, mNavigationBarView);
+ getBarTransitions().addDarkIntensityListener(mAssistHandlerViewController);
+ }
}
@Override
public void onDestroyView() {
super.onDestroyView();
if (mNavigationBarView != null) {
- mNavigationBarView.getBarTransitions().removeDarkIntensityListener(mScreenDecorations);
+ if (mIsOnDefaultDisplay) {
+ mNavigationBarView.getBarTransitions()
+ .removeDarkIntensityListener(mAssistHandlerViewController);
+ mAssistHandlerViewController = null;
+ }
mNavigationBarView.getBarTransitions().destroy();
mNavigationBarView.getLightTransitionsController().destroy(getContext());
}
mOverviewProxyService.removeCallback(mOverviewProxyListener);
- getContext().unregisterReceiver(mBroadcastReceiver);
+ mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
}
@Override
@@ -380,7 +407,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
super.onSaveInstanceState(outState);
outState.putInt(EXTRA_DISABLE_STATE, mDisabledFlags1);
outState.putInt(EXTRA_DISABLE2_STATE, mDisabledFlags2);
- outState.putInt(EXTRA_SYSTEM_UI_VISIBILITY, mSystemUiVisibility);
+ outState.putInt(EXTRA_APPEARANCE, mAppearance);
+ outState.putBoolean(EXTRA_TRANSIENT_STATE, mTransientShown);
if (mNavigationBarView != null) {
mNavigationBarView.getLightTransitionsController().saveState(outState);
}
@@ -501,77 +529,107 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
rotationButtonController.onRotationProposal(rotation, winRotation, isValid);
}
- /** Restores the System UI flags saved state to {@link NavigationBarFragment}. */
- public void restoreSystemUiVisibilityState() {
- final int barMode = computeBarMode(0, mSystemUiVisibility);
- if (barMode != -1) {
- mNavigationBarMode = barMode;
- }
+ /** Restores the appearance and the transient saved state to {@link NavigationBarFragment}. */
+ public void restoreAppearanceAndTransientState() {
+ final int barMode = barMode(mTransientShown, mAppearance);
+ mNavigationBarMode = barMode;
checkNavBarModes();
mAutoHideController.touchAutoHide();
- mLightBarController.onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */,
- true /* nbModeChanged */, mNavigationBarMode, false /* navbarColorManagedByIme */);
+ mLightBarController.onNavigationBarAppearanceChanged(mAppearance, true /* nbModeChanged */,
+ barMode, false /* navbarColorManagedByIme */);
}
@Override
- public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
- int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds,
- boolean navbarColorManagedByIme) {
+ public void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance,
+ AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) {
if (displayId != mDisplayId) {
return;
}
- final int oldVal = mSystemUiVisibility;
- final int newVal = (oldVal & ~mask) | (vis & mask);
- final int diff = newVal ^ oldVal;
boolean nbModeChanged = false;
- if (diff != 0) {
- mSystemUiVisibility = newVal;
-
- // update navigation bar mode
- final int nbMode = getView() == null
- ? -1 : computeBarMode(oldVal, newVal);
- nbModeChanged = nbMode != -1;
- if (nbModeChanged) {
- if (mNavigationBarMode != nbMode) {
- if (mNavigationBarMode == MODE_TRANSPARENT
- || mNavigationBarMode == MODE_LIGHTS_OUT_TRANSPARENT) {
- mNavigationBarView.hideRecentsOnboarding();
- }
- mNavigationBarMode = nbMode;
- checkNavBarModes();
- }
- mAutoHideController.touchAutoHide();
+ if (mAppearance != appearance) {
+ mAppearance = appearance;
+ if (getView() == null) {
+ return;
}
+ nbModeChanged = updateBarMode(barMode(mTransientShown, appearance));
+ }
+ mLightBarController.onNavigationBarAppearanceChanged(appearance, nbModeChanged,
+ mNavigationBarMode, navbarColorManagedByIme);
+ }
+
+ @Override
+ public void showTransient(int displayId, @InternalInsetType int[] types) {
+ if (displayId != mDisplayId) {
+ return;
+ }
+ if (!containsType(types, TYPE_NAVIGATION_BAR)) {
+ return;
+ }
+ if (!mTransientShown) {
+ mTransientShown = true;
+ handleTransientChanged();
+ }
+ }
+
+ @Override
+ public void abortTransient(int displayId, @InternalInsetType int[] types) {
+ if (displayId != mDisplayId) {
+ return;
}
- mLightBarController.onNavigationVisibilityChanged(
- vis, mask, nbModeChanged, mNavigationBarMode, navbarColorManagedByIme);
+ if (!containsType(types, TYPE_NAVIGATION_BAR)) {
+ return;
+ }
+ clearTransient();
}
- private @TransitionMode int computeBarMode(int oldVis, int newVis) {
- final int oldMode = barMode(oldVis);
- final int newMode = barMode(newVis);
- if (oldMode == newMode) {
- return -1; // no mode change
+ void clearTransient() {
+ if (mTransientShown) {
+ mTransientShown = false;
+ handleTransientChanged();
+ }
+ }
+
+ private void handleTransientChanged() {
+ if (getView() == null) {
+ return;
+ }
+ if (mNavigationBarView != null) {
+ mNavigationBarView.onTransientStateChanged(mTransientShown);
+ }
+ final int barMode = barMode(mTransientShown, mAppearance);
+ if (updateBarMode(barMode)) {
+ mLightBarController.onNavigationBarModeChanged(barMode);
+ }
+ }
+
+ // Returns true if the bar mode is changed.
+ private boolean updateBarMode(int barMode) {
+ if (mNavigationBarMode != barMode) {
+ if (mNavigationBarMode == MODE_TRANSPARENT
+ || mNavigationBarMode == MODE_LIGHTS_OUT_TRANSPARENT) {
+ mNavigationBarView.hideRecentsOnboarding();
+ }
+ mNavigationBarMode = barMode;
+ checkNavBarModes();
+ mAutoHideController.touchAutoHide();
+ return true;
}
- return newMode;
+ return false;
}
- private @TransitionMode int barMode(int vis) {
- final int lightsOutTransparent =
- View.SYSTEM_UI_FLAG_LOW_PROFILE | View.NAVIGATION_BAR_TRANSIENT;
- if ((vis & View.NAVIGATION_BAR_TRANSIENT) != 0) {
+ private static @TransitionMode int barMode(boolean isTransient, int appearance) {
+ final int lightsOutOpaque = APPEARANCE_LOW_PROFILE_BARS | APPEARANCE_OPAQUE_SIDE_BARS;
+ if (isTransient) {
return MODE_SEMI_TRANSPARENT;
- } else if ((vis & View.NAVIGATION_BAR_TRANSLUCENT) != 0) {
- return MODE_TRANSLUCENT;
- } else if ((vis & lightsOutTransparent) == lightsOutTransparent) {
- return MODE_LIGHTS_OUT_TRANSPARENT;
- } else if ((vis & View.NAVIGATION_BAR_TRANSPARENT) != 0) {
- return MODE_TRANSPARENT;
- } else if ((vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
+ } else if ((appearance & lightsOutOpaque) == lightsOutOpaque) {
return MODE_LIGHTS_OUT;
- } else {
+ } else if ((appearance & APPEARANCE_LOW_PROFILE_BARS) != 0) {
+ return MODE_LIGHTS_OUT_TRANSPARENT;
+ } else if ((appearance & APPEARANCE_OPAQUE_SIDE_BARS) != 0) {
return MODE_OPAQUE;
+ } else {
+ return MODE_TRANSPARENT;
}
}
@@ -970,8 +1028,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
mAutoHideController.setNavigationBar(this);
}
- public boolean isSemiTransparent() {
- return mNavigationBarMode == MODE_SEMI_TRANSPARENT;
+ boolean isTransientShown() {
+ return mTransientShown;
}
private void checkBarModes() {
@@ -1019,6 +1077,11 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
}
+ @Nullable
+ public AssistHandleViewController getAssistHandlerViewController() {
+ return mAssistHandlerViewController;
+ }
+
/**
* Performs transitions on navigation bar.
*
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 fa4812dc4876..4b4a35bae05d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -67,6 +67,7 @@ import com.android.systemui.DockedStackExistsListener;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.assist.AssistHandleViewController;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
@@ -75,6 +76,7 @@ import com.android.systemui.recents.RecentsOnboarding;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.policy.DeadZone;
import com.android.systemui.statusbar.policy.KeyButtonDrawable;
@@ -364,6 +366,10 @@ public class NavigationBarView extends FrameLayout implements
return super.onTouchEvent(event);
}
+ void onTransientStateChanged(boolean isTransient) {
+ mEdgeBackGestureHandler.onNavBarTransientStateChanged(isTransient);
+ }
+
void onBarTransition(int newMode) {
if (newMode == MODE_OPAQUE) {
// If the nav bar background is opaque, stop auto tinting since we know the icons are
@@ -829,7 +835,11 @@ public class NavigationBarView extends FrameLayout implements
mRecentsOnboarding.onNavigationModeChanged(mNavBarMode);
getRotateSuggestionButton().onNavigationModeChanged(mNavBarMode);
- mRegionSamplingHelper.start(mSamplingBounds);
+ if (isGesturalMode(mNavBarMode)) {
+ mRegionSamplingHelper.start(mSamplingBounds);
+ } else {
+ mRegionSamplingHelper.stop();
+ }
}
public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) {
@@ -1194,6 +1204,22 @@ public class NavigationBarView extends FrameLayout implements
// we're passing the insets onto the gesture handler since the back arrow is only
// conditionally added and doesn't always get all the insets.
mEdgeBackGestureHandler.setInsets(leftInset, rightInset);
+
+ // this allows assist handle to be drawn outside its bound so that it can align screen
+ // bottom by translating its y position.
+ final boolean shouldClip =
+ !isGesturalMode(mNavBarMode) || insets.getSystemWindowInsetBottom() == 0;
+ setClipChildren(shouldClip);
+ setClipToPadding(shouldClip);
+
+ NavigationBarController navigationBarController =
+ Dependency.get(NavigationBarController.class);
+ AssistHandleViewController controller =
+ navigationBarController == null
+ ? null : navigationBarController.getAssistHandlerViewController();
+ if (controller != null) {
+ controller.setBottomOffset(insets.getSystemWindowInsetBottom());
+ }
return super.onApplyWindowInsets(insets);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index 4d69f77e744d..2798285c073d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -105,7 +105,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
* @return true if the entry was transferred to and should inflate + alert
*/
public boolean isAlertTransferPending(@NonNull NotificationEntry entry) {
- PendingAlertInfo alertInfo = mPendingAlerts.get(entry.key);
+ PendingAlertInfo alertInfo = mPendingAlerts.get(entry.getKey());
return alertInfo != null && alertInfo.isStillValid();
}
@@ -141,7 +141,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
@Override
public void onGroupSuppressionChanged(NotificationGroup group, boolean suppressed) {
if (suppressed) {
- if (mHeadsUpManager.isAlerting(group.summary.key)) {
+ if (mHeadsUpManager.isAlerting(group.summary.getKey())) {
handleSuppressedSummaryAlerted(group.summary, mHeadsUpManager);
}
} else {
@@ -151,11 +151,11 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
return;
}
GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(mGroupManager.getGroupKey(
- group.summary.notification));
+ group.summary.getSbn()));
// Group is no longer suppressed. We should check if we need to transfer the alert
// back to the summary now that it's no longer suppressed.
if (groupAlertEntry.mAlertSummaryOnNextAddition) {
- if (!mHeadsUpManager.isAlerting(group.summary.key)) {
+ if (!mHeadsUpManager.isAlerting(group.summary.getKey())) {
alertNotificationWhenPossible(group.summary, mHeadsUpManager);
}
groupAlertEntry.mAlertSummaryOnNextAddition = false;
@@ -173,7 +173,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
private void onAlertStateChanged(NotificationEntry entry, boolean isAlerting,
AlertingNotificationManager alertManager) {
- if (isAlerting && mGroupManager.isSummaryOfSuppressedGroup(entry.notification)) {
+ if (isAlerting && mGroupManager.isSummaryOfSuppressedGroup(entry.getSbn())) {
handleSuppressedSummaryAlerted(entry, alertManager);
}
}
@@ -184,7 +184,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
// see as early as we can if we need to abort a transfer.
@Override
public void onPendingEntryAdded(NotificationEntry entry) {
- String groupKey = mGroupManager.getGroupKey(entry.notification);
+ String groupKey = mGroupManager.getGroupKey(entry.getSbn());
GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(groupKey);
if (groupAlertEntry != null) {
checkShouldTransferBack(groupAlertEntry);
@@ -195,7 +195,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
// then show the alert.
@Override
public void onEntryReinflated(NotificationEntry entry) {
- PendingAlertInfo alertInfo = mPendingAlerts.remove(entry.key);
+ PendingAlertInfo alertInfo = mPendingAlerts.remove(entry.getKey());
if (alertInfo != null) {
if (alertInfo.isStillValid()) {
alertNotificationWhenPossible(entry, mHeadsUpManager);
@@ -214,7 +214,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
// Removes any alerts pending on this entry. Note that this will not stop any inflation
// tasks started by a transfer, so this should only be used as clean-up for when
// inflation is stopped and the pending alert no longer needs to happen.
- mPendingAlerts.remove(entry.key);
+ mPendingAlerts.remove(entry.getKey());
}
};
@@ -267,10 +267,10 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
*/
private boolean isPendingNotificationInGroup(@NonNull NotificationEntry entry,
@NonNull NotificationGroup group) {
- String groupKey = mGroupManager.getGroupKey(group.summary.notification);
- return mGroupManager.isGroupChild(entry.notification)
- && Objects.equals(mGroupManager.getGroupKey(entry.notification), groupKey)
- && !group.children.containsKey(entry.key);
+ String groupKey = mGroupManager.getGroupKey(group.summary.getSbn());
+ return mGroupManager.isGroupChild(entry.getSbn())
+ && Objects.equals(mGroupManager.getGroupKey(entry.getSbn()), groupKey)
+ && !group.children.containsKey(entry.getKey());
}
/**
@@ -284,10 +284,10 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
*/
private void handleSuppressedSummaryAlerted(@NonNull NotificationEntry summary,
@NonNull AlertingNotificationManager alertManager) {
- StatusBarNotification sbn = summary.notification;
+ StatusBarNotification sbn = summary.getSbn();
GroupAlertEntry groupAlertEntry =
mGroupAlertEntries.get(mGroupManager.getGroupKey(sbn));
- if (!mGroupManager.isSummaryOfSuppressedGroup(summary.notification)
+ if (!mGroupManager.isSummaryOfSuppressedGroup(summary.getSbn())
|| !alertManager.isAlerting(sbn.getKey())
|| groupAlertEntry == null) {
return;
@@ -298,7 +298,8 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
return;
}
- NotificationEntry child = mGroupManager.getLogicalChildren(summary.notification).iterator().next();
+ NotificationEntry child =
+ mGroupManager.getLogicalChildren(summary.getSbn()).iterator().next();
if (child != null) {
if (child.getRow().keepInParent()
|| child.isRowRemoved()
@@ -306,7 +307,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
// The notification is actually already removed. No need to alert it.
return;
}
- if (!alertManager.isAlerting(child.key) && onlySummaryAlerts(summary)) {
+ if (!alertManager.isAlerting(child.getKey()) && onlySummaryAlerts(summary)) {
groupAlertEntry.mLastAlertTransferTime = SystemClock.elapsedRealtime();
}
transferAlertState(summary, child, alertManager);
@@ -324,7 +325,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
*/
private void transferAlertState(@NonNull NotificationEntry fromEntry, @NonNull NotificationEntry toEntry,
@NonNull AlertingNotificationManager alertManager) {
- alertManager.removeNotification(fromEntry.key, true /* releaseImmediately */);
+ alertManager.removeNotification(fromEntry.getKey(), true /* releaseImmediately */);
alertNotificationWhenPossible(toEntry, alertManager);
}
@@ -347,7 +348,8 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
if (!onlySummaryAlerts(summary)) {
return;
}
- ArrayList<NotificationEntry> children = mGroupManager.getLogicalChildren(summary.notification);
+ ArrayList<NotificationEntry> children = mGroupManager.getLogicalChildren(
+ summary.getSbn());
int numChildren = children.size();
int numPendingChildren = getPendingChildrenNotAlerting(groupAlertEntry.mGroup);
numChildren += numPendingChildren;
@@ -357,17 +359,18 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
boolean releasedChild = false;
for (int i = 0; i < children.size(); i++) {
NotificationEntry entry = children.get(i);
- if (onlySummaryAlerts(entry) && mHeadsUpManager.isAlerting(entry.key)) {
+ if (onlySummaryAlerts(entry) && mHeadsUpManager.isAlerting(entry.getKey())) {
releasedChild = true;
- mHeadsUpManager.removeNotification(entry.key, true /* releaseImmediately */);
+ mHeadsUpManager.removeNotification(
+ entry.getKey(), true /* releaseImmediately */);
}
- if (mPendingAlerts.containsKey(entry.key)) {
+ if (mPendingAlerts.containsKey(entry.getKey())) {
// This is the child that would've been removed if it was inflated.
releasedChild = true;
- mPendingAlerts.get(entry.key).mAbortOnInflation = true;
+ mPendingAlerts.get(entry.getKey()).mAbortOnInflation = true;
}
}
- if (releasedChild && !mHeadsUpManager.isAlerting(summary.key)) {
+ if (releasedChild && !mHeadsUpManager.isAlerting(summary.getKey())) {
boolean notifyImmediately = (numChildren - numPendingChildren) > 1;
if (notifyImmediately) {
alertNotificationWhenPossible(summary, mHeadsUpManager);
@@ -391,20 +394,20 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
@NonNull AlertingNotificationManager alertManager) {
@InflationFlag int contentFlag = alertManager.getContentFlag();
if (!entry.getRow().isInflationFlagSet(contentFlag)) {
- mPendingAlerts.put(entry.key, new PendingAlertInfo(entry));
+ mPendingAlerts.put(entry.getKey(), new PendingAlertInfo(entry));
entry.getRow().updateInflationFlag(contentFlag, true /* shouldInflate */);
entry.getRow().inflateViews();
return;
}
- if (alertManager.isAlerting(entry.key)) {
- alertManager.updateNotification(entry.key, true /* alert */);
+ if (alertManager.isAlerting(entry.getKey())) {
+ alertManager.updateNotification(entry.getKey(), true /* alert */);
} else {
alertManager.showNotification(entry);
}
}
private boolean onlySummaryAlerts(NotificationEntry entry) {
- return entry.notification.getNotification().getGroupAlertBehavior()
+ return entry.getSbn().getNotification().getGroupAlertBehavior()
== Notification.GROUP_ALERT_SUMMARY;
}
@@ -431,7 +434,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
boolean mAbortOnInflation;
PendingAlertInfo(NotificationEntry entry) {
- mOriginalNotification = entry.notification;
+ mOriginalNotification = entry.getSbn();
mEntry = entry;
}
@@ -445,11 +448,11 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
// Notification is aborted due to the transfer being explicitly cancelled
return false;
}
- if (mEntry.notification.getGroupKey() != mOriginalNotification.getGroupKey()) {
+ if (mEntry.getSbn().getGroupKey() != mOriginalNotification.getGroupKey()) {
// Groups have changed
return false;
}
- if (mEntry.notification.getNotification().isGroupSummary()
+ if (mEntry.getSbn().getNotification().isGroupSummary()
!= mOriginalNotification.getNotification().isGroupSummary()) {
// Notification has changed from group summary to not or vice versa
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index adaea9379c71..e11fc1b46a5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -103,8 +103,8 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State
}
public void onEntryRemoved(NotificationEntry removed) {
- onEntryRemovedInternal(removed, removed.notification);
- mIsolatedEntries.remove(removed.key);
+ onEntryRemovedInternal(removed, removed.getSbn());
+ mIsolatedEntries.remove(removed.getKey());
}
/**
@@ -126,7 +126,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State
return;
}
if (isGroupChild(sbn)) {
- group.children.remove(removed.key);
+ group.children.remove(removed.getKey());
} else {
group.summary = null;
}
@@ -145,7 +145,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State
if (added.isRowRemoved()) {
added.setDebugThrowable(new Throwable());
}
- final StatusBarNotification sbn = added.notification;
+ final StatusBarNotification sbn = added.getSbn();
boolean isGroupChild = isGroupChild(sbn);
String groupKey = getGroupKey(sbn);
NotificationGroup group = mGroupMap.get(groupKey);
@@ -157,17 +157,17 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State
}
}
if (isGroupChild) {
- NotificationEntry existing = group.children.get(added.key);
+ NotificationEntry existing = group.children.get(added.getKey());
if (existing != null && existing != added) {
Throwable existingThrowable = existing.getDebugThrowable();
- Log.wtf(TAG, "Inconsistent entries found with the same key " + added.key
+ Log.wtf(TAG, "Inconsistent entries found with the same key " + added.getKey()
+ "existing removed: " + existing.isRowRemoved()
+ (existingThrowable != null
? Log.getStackTraceString(existingThrowable) + "\n": "")
+ " added removed" + added.isRowRemoved()
, new Throwable());
}
- group.children.put(added.key, added);
+ group.children.put(added.getKey(), added);
updateSuppression(group);
} else {
group.summary = added;
@@ -210,7 +210,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State
group.suppressed = group.summary != null && !group.expanded
&& (childCount == 1
|| (childCount == 0
- && group.summary.notification.getNotification().isGroupSummary()
+ && group.summary.getSbn().getNotification().isGroupSummary()
&& (hasIsolatedChildren(group) || hasBubbles)));
if (prevSuppressed != group.suppressed) {
for (OnGroupChangeListener listener : mListeners) {
@@ -223,7 +223,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State
}
private boolean hasIsolatedChildren(NotificationGroup group) {
- return getNumberOfIsolatedChildren(group.summary.notification.getGroupKey()) != 0;
+ return getNumberOfIsolatedChildren(group.summary.getSbn().getGroupKey()) != 0;
}
private int getNumberOfIsolatedChildren(String groupKey) {
@@ -248,18 +248,18 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State
public void onEntryUpdated(NotificationEntry entry,
StatusBarNotification oldNotification) {
String oldKey = oldNotification.getGroupKey();
- String newKey = entry.notification.getGroupKey();
+ String newKey = entry.getSbn().getGroupKey();
boolean groupKeysChanged = !oldKey.equals(newKey);
boolean wasGroupChild = isGroupChild(oldNotification);
- boolean isGroupChild = isGroupChild(entry.notification);
+ boolean isGroupChild = isGroupChild(entry.getSbn());
mIsUpdatingUnchangedGroup = !groupKeysChanged && wasGroupChild == isGroupChild;
if (mGroupMap.get(getGroupKey(oldNotification)) != null) {
onEntryRemovedInternal(entry, oldNotification);
}
onEntryAdded(entry);
mIsUpdatingUnchangedGroup = false;
- if (isIsolated(entry.notification)) {
- mIsolatedEntries.put(entry.key, entry.notification);
+ if (isIsolated(entry.getSbn())) {
+ mIsolatedEntries.put(entry.getKey(), entry.getSbn());
if (groupKeysChanged) {
updateSuppression(mGroupMap.get(oldKey));
updateSuppression(mGroupMap.get(newKey));
@@ -284,7 +284,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State
}
NotificationEntry logicalGroupSummary = getLogicalGroupSummary(sbn);
return logicalGroupSummary != null
- && !logicalGroupSummary.notification.equals(sbn);
+ && !logicalGroupSummary.getSbn().equals(sbn);
}
private int getTotalNumberOfChildren(StatusBarNotification sbn) {
@@ -351,7 +351,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State
if (group == null || group.summary == null) {
return false;
}
- return !group.children.isEmpty() && Objects.equals(group.summary.notification, sbn);
+ return !group.children.isEmpty() && Objects.equals(group.summary.getSbn(), sbn);
}
/**
@@ -404,7 +404,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State
* will update the suppression of that group.
*/
public void updateSuppression(NotificationEntry entry) {
- NotificationGroup group = mGroupMap.get(getGroupKey(entry.notification));
+ NotificationGroup group = mGroupMap.get(getGroupKey(entry.getSbn()));
if (group != null) {
updateSuppression(group);
}
@@ -489,12 +489,12 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State
*/
private boolean shouldIsolate(NotificationEntry entry) {
- StatusBarNotification sbn = entry.notification;
+ StatusBarNotification sbn = entry.getSbn();
NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) {
return false;
}
- if (mHeadsUpManager != null && !mHeadsUpManager.isAlerting(entry.key)) {
+ if (mHeadsUpManager != null && !mHeadsUpManager.isAlerting(entry.getKey())) {
return false;
}
return (sbn.getNotification().fullScreenIntent != null
@@ -509,10 +509,10 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State
* @param entry the notification to isolate
*/
private void isolateNotification(NotificationEntry entry) {
- StatusBarNotification sbn = entry.notification;
+ StatusBarNotification sbn = entry.getSbn();
// We will be isolated now, so lets update the groups
- onEntryRemovedInternal(entry, entry.notification);
+ onEntryRemovedInternal(entry, entry.getSbn());
mIsolatedEntries.put(sbn.getKey(), sbn);
@@ -521,7 +521,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State
// even before the groupManager knows about the notification at all.
// When the notification gets added afterwards it is already isolated and therefore
// it doesn't lead to an update.
- updateSuppression(mGroupMap.get(entry.notification.getGroupKey()));
+ updateSuppression(mGroupMap.get(entry.getSbn().getGroupKey()));
for (OnGroupChangeListener listener : mListeners) {
listener.onGroupsChanged();
}
@@ -533,10 +533,10 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State
* @param entry the notification to un-isolate
*/
private void stopIsolatingNotification(NotificationEntry entry) {
- StatusBarNotification sbn = entry.notification;
+ StatusBarNotification sbn = entry.getSbn();
if (mIsolatedEntries.containsKey(sbn.getKey())) {
// not isolated anymore, we need to update the groups
- onEntryRemovedInternal(entry, entry.notification);
+ onEntryRemovedInternal(entry, entry.getSbn());
mIsolatedEntries.remove(sbn.getKey());
onEntryAdded(entry);
for (OnGroupChangeListener listener : mListeners) {
@@ -584,13 +584,13 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State
@Override
public String toString() {
String result = " summary:\n "
- + (summary != null ? summary.notification : "null")
+ + (summary != null ? summary.getSbn() : "null")
+ (summary != null && summary.getDebugThrowable() != null
? Log.getStackTraceString(summary.getDebugThrowable())
: "");
result += "\n children size: " + children.size();
for (NotificationEntry child : children.values()) {
- result += "\n " + child.notification
+ result += "\n " + child.getSbn()
+ (child.getDebugThrowable() != null
? Log.getStackTraceString(child.getDebugThrowable())
: "");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 1a3560ece1d7..1e10b6f025f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -80,11 +80,14 @@ public class NotificationIconAreaController implements DarkReceiver,
private boolean mAodIconsVisible;
private boolean mIsPulsing;
- public NotificationIconAreaController(Context context, StatusBar statusBar,
+ public NotificationIconAreaController(
+ Context context,
+ StatusBar statusBar,
StatusBarStateController statusBarStateController,
NotificationWakeUpCoordinator wakeUpCoordinator,
KeyguardBypassController keyguardBypassController,
- NotificationMediaManager notificationMediaManager) {
+ NotificationMediaManager notificationMediaManager,
+ DozeParameters dozeParameters) {
mStatusBar = statusBar;
mContrastColorUtil = ContrastColorUtil.getInstance(context);
mContext = context;
@@ -92,7 +95,7 @@ public class NotificationIconAreaController implements DarkReceiver,
mStatusBarStateController = statusBarStateController;
mStatusBarStateController.addCallback(this);
mMediaManager = notificationMediaManager;
- mDozeParameters = DozeParameters.getInstance(mContext);
+ mDozeParameters = dozeParameters;
mWakeUpCoordinator = wakeUpCoordinator;
wakeUpCoordinator.addListener(this);
mBypassController = keyguardBypassController;
@@ -244,10 +247,10 @@ public class NotificationIconAreaController implements DarkReceiver,
if (hideCenteredIcon && isCenteredNotificationIcon && !entry.isRowHeadsUp()) {
return false;
}
- if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) {
+ if (mEntryManager.getNotificationData().isAmbient(entry.getKey()) && !showAmbient) {
return false;
}
- if (hideCurrentMedia && entry.key.equals(mMediaManager.getMediaNotificationKey())) {
+ if (hideCurrentMedia && entry.getKey().equals(mMediaManager.getMediaNotificationKey())) {
return false;
}
if (!entry.isTopLevelChild()) {
@@ -533,8 +536,7 @@ public class NotificationIconAreaController implements DarkReceiver,
}
public void appearAodIcons() {
- DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
- if (dozeParameters.shouldControlScreenOff()) {
+ if (mDozeParameters.shouldControlScreenOff()) {
mAodIcons.setTranslationY(-mAodIconAppearTranslation);
mAodIcons.setAlpha(0);
animateInAodIconTranslation();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index ea113dffa658..30fe68a28ef2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -43,6 +43,7 @@ import android.hardware.biometrics.BiometricSourceType;
import android.os.PowerManager;
import android.os.SystemClock;
import android.provider.DeviceConfig;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
import android.util.MathUtils;
@@ -68,6 +69,7 @@ import com.android.systemui.DejankUtils;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.doze.DozeLog;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
import com.android.systemui.plugins.FalsingManager;
@@ -88,6 +90,7 @@ import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
@@ -102,7 +105,7 @@ import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -143,6 +146,7 @@ public class NotificationPanelView extends PanelView implements
* Fling until QS is completely hidden.
*/
public static final int FLING_HIDE = 2;
+ private final DozeParameters mDozeParameters;
private double mQqsSplitFraction;
@@ -205,11 +209,11 @@ public class NotificationPanelView extends PanelView implements
mDelayShowingKeyguardStatusBar = false;
}
};
- private final KeyguardMonitor.Callback mKeyguardMonitorCallback =
- new KeyguardMonitor.Callback() {
+ private final KeyguardStateController.Callback mKeyguardMonitorCallback =
+ new KeyguardStateController.Callback() {
@Override
public void onKeyguardFadingAwayChanged() {
- if (!mKeyguardMonitor.isKeyguardFadingAway()) {
+ if (!mKeyguardStateController.isKeyguardFadingAway()) {
mFirstBypassAttempt = false;
mDelayShowingKeyguardStatusBar = false;
}
@@ -407,14 +411,11 @@ public class NotificationPanelView extends PanelView implements
.setDuration(200)
.setAnimationFinishListener(mAnimatorListenerAdapter)
.setCustomInterpolator(PANEL_ALPHA.getProperty(), Interpolators.ALPHA_IN);
- private final NotificationEntryManager mEntryManager =
- Dependency.get(NotificationEntryManager.class);
+ private final NotificationEntryManager mEntryManager;
private final CommandQueue mCommandQueue;
- private final NotificationLockscreenUserManager mLockscreenUserManager =
- Dependency.get(NotificationLockscreenUserManager.class);
- private final ShadeController mShadeController =
- Dependency.get(ShadeController.class);
+ private final NotificationLockscreenUserManager mLockscreenUserManager;
+ private final ShadeController mShadeController;
private int mDisplayId;
/**
@@ -454,13 +455,17 @@ public class NotificationPanelView extends PanelView implements
@Inject
public NotificationPanelView(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
InjectionInflationController injectionInflationController,
- NotificationWakeUpCoordinator coordinator,
- PulseExpansionHandler pulseExpansionHandler,
+ NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler,
DynamicPrivacyController dynamicPrivacyController,
- KeyguardBypassController bypassController,
- FalsingManager falsingManager,
- PluginManager pluginManager) {
- super(context, attrs);
+ KeyguardBypassController bypassController, FalsingManager falsingManager,
+ PluginManager pluginManager, ShadeController shadeController,
+ NotificationLockscreenUserManager notificationLockscreenUserManager,
+ NotificationEntryManager notificationEntryManager,
+ KeyguardStateController keyguardStateController,
+ StatusBarStateController statusBarStateController, DozeLog dozeLog,
+ DozeParameters dozeParameters) {
+ super(context, attrs, falsingManager, dozeLog, keyguardStateController,
+ (SysuiStatusBarStateController) statusBarStateController);
setWillNotDraw(!DEBUG);
mInjectionInflationController = injectionInflationController;
mFalsingManager = falsingManager;
@@ -473,6 +478,7 @@ public class NotificationPanelView extends PanelView implements
mCommandQueue = getComponent(context, CommandQueue.class);
mDisplayId = context.getDisplayId();
mPulseExpansionHandler = pulseExpansionHandler;
+ mDozeParameters = dozeParameters;
pulseExpansionHandler.setPulseExpandAbortListener(() -> {
if (mQs != null) {
mQs.animateHeaderSlidingOut();
@@ -482,7 +488,7 @@ public class NotificationPanelView extends PanelView implements
mKeyguardBypassController = bypassController;
mUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
- mKeyguardMonitor.addCallback(mKeyguardMonitorCallback);
+ mKeyguardStateController.addCallback(mKeyguardMonitorCallback);
dynamicPrivacyController.addListener(this);
mBottomAreaShadeAlphaAnimator = ValueAnimator.ofFloat(1f, 0);
@@ -493,6 +499,11 @@ public class NotificationPanelView extends PanelView implements
mBottomAreaShadeAlphaAnimator.setDuration(160);
mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT);
mPluginManager = pluginManager;
+ mShadeController = shadeController;
+ mLockscreenUserManager = notificationLockscreenUserManager;
+ mEntryManager = notificationEntryManager;
+
+ setBackgroundColor(Color.TRANSPARENT);
}
/**
@@ -507,9 +518,11 @@ public class NotificationPanelView extends PanelView implements
mKeyguardBottomArea.setStatusBar(mStatusBar);
}
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
+ /**
+ * Call after this view has been fully inflated and had its children attached.
+ */
+ public void onChildrenAttached() {
+ loadDimens();
mKeyguardStatusBar = findViewById(R.id.keyguard_header);
mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
@@ -528,7 +541,10 @@ public class NotificationPanelView extends PanelView implements
mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim);
mLastOrientation = getResources().getConfiguration().orientation;
mPluginFrame = findViewById(R.id.plugin_frame);
- mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager);
+ if (Settings.System.getInt(
+ mContext.getContentResolver(), "npv_plugin_flag", 0) == 1) {
+ mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager);
+ }
initBottomArea();
@@ -552,7 +568,7 @@ public class NotificationPanelView extends PanelView implements
}
});
- Dependency.get(PluginManager.class).addPluginListener(
+ mPluginManager.addPluginListener(
new PluginListener<HomeControlsPlugin>() {
@Override
@@ -652,8 +668,7 @@ public class NotificationPanelView extends PanelView implements
mNotificationStackScroller.setLayoutParams(lp);
}
int sideMargin = res.getDimensionPixelOffset(R.dimen.notification_side_paddings);
- int topMargin =
- res.getDimensionPixelOffset(com.android.internal.R.dimen.quick_qs_total_height);
+ int topMargin = sideMargin;
lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams();
if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin
|| lp.rightMargin != sideMargin || lp.topMargin != topMargin) {
@@ -755,6 +770,11 @@ public class NotificationPanelView extends PanelView implements
int sideMargin = res.getDimensionPixelOffset(R.dimen.notification_side_paddings);
int topMargin =
res.getDimensionPixelOffset(com.android.internal.R.dimen.quick_qs_total_height);
+ int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0);
+ if (flag == 1) {
+ topMargin = res.getDimensionPixelOffset(
+ com.android.internal.R.dimen.quick_qs_total_height_with_media);
+ }
lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams();
if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin
|| lp.rightMargin != sideMargin || lp.topMargin != topMargin) {
@@ -766,7 +786,7 @@ public class NotificationPanelView extends PanelView implements
mPluginFrame.setLayoutParams(lp);
}
- mNPVPluginManager.replaceFrameLayout(mPluginFrame);
+ if (mNPVPluginManager != null) mNPVPluginManager.replaceFrameLayout(mPluginFrame);
}
private void initBottomArea() {
@@ -796,7 +816,10 @@ public class NotificationPanelView extends PanelView implements
int oldMaxHeight = mQsMaxExpansionHeight;
if (mQs != null) {
mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight();
- mQsMinExpansionHeight += mNPVPluginManager.getHeight();
+ if (mNPVPluginManager != null) {
+ mNPVPluginManager.setYOffset(mQsMinExpansionHeight);
+ mQsMinExpansionHeight += mNPVPluginManager.getHeight();
+ }
mQsMaxExpansionHeight = mQs.getDesiredHeight();
mNotificationStackScroller.setMaxTopPadding(
mQsMaxExpansionHeight + mQsNotificationTopPadding);
@@ -1683,7 +1706,7 @@ public class NotificationPanelView extends PanelView implements
@Override
public void onStateChanged(int statusBarState) {
boolean goingToFullShade = mStatusBarStateController.goingToFullShade();
- boolean keyguardFadingAway = mKeyguardMonitor.isKeyguardFadingAway();
+ boolean keyguardFadingAway = mKeyguardStateController.isKeyguardFadingAway();
int oldState = mBarState;
boolean keyguardShowing = statusBarState == StatusBarState.KEYGUARD;
setKeyguardStatusViewVisibility(statusBarState, keyguardFadingAway, goingToFullShade);
@@ -1701,7 +1724,7 @@ public class NotificationPanelView extends PanelView implements
&& (goingToFullShade || statusBarState == StatusBarState.SHADE_LOCKED)) {
animateKeyguardStatusBarOut();
long delay = mBarState == StatusBarState.SHADE_LOCKED
- ? 0 : mKeyguardMonitor.calculateGoingToFullShadeDelay();
+ ? 0 : mKeyguardStateController.calculateGoingToFullShadeDelay();
mQs.animateHeaderSlidingIn(delay);
} else if (oldState == StatusBarState.SHADE_LOCKED
&& statusBarState == StatusBarState.KEYGUARD) {
@@ -1778,13 +1801,13 @@ public class NotificationPanelView extends PanelView implements
private void animateKeyguardStatusBarOut() {
ValueAnimator anim = ValueAnimator.ofFloat(mKeyguardStatusBar.getAlpha(), 0f);
anim.addUpdateListener(mStatusBarAnimateAlphaListener);
- anim.setStartDelay(mKeyguardMonitor.isKeyguardFadingAway()
- ? mKeyguardMonitor.getKeyguardFadingAwayDelay()
+ anim.setStartDelay(mKeyguardStateController.isKeyguardFadingAway()
+ ? mKeyguardStateController.getKeyguardFadingAwayDelay()
: 0);
long duration;
- if (mKeyguardMonitor.isKeyguardFadingAway()) {
- duration = mKeyguardMonitor.getShortenedFadingAwayDuration();
+ if (mKeyguardStateController.isKeyguardFadingAway()) {
+ duration = mKeyguardStateController.getShortenedFadingAwayDuration();
} else {
duration = StackStateAnimator.ANIMATION_DURATION_STANDARD;
}
@@ -1831,8 +1854,8 @@ public class NotificationPanelView extends PanelView implements
if (goingToFullShade) {
mKeyguardBottomArea.animate()
.alpha(0f)
- .setStartDelay(mKeyguardMonitor.getKeyguardFadingAwayDelay())
- .setDuration(mKeyguardMonitor.getShortenedFadingAwayDuration())
+ .setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay())
+ .setDuration(mKeyguardStateController.getShortenedFadingAwayDuration())
.setInterpolator(Interpolators.ALPHA_OUT)
.withEndAction(mAnimateKeyguardBottomAreaInvisibleEndRunnable)
.start();
@@ -1860,8 +1883,8 @@ public class NotificationPanelView extends PanelView implements
.withEndAction(mAnimateKeyguardStatusViewGoneEndRunnable);
if (keyguardFadingAway) {
mKeyguardStatusView.animate()
- .setStartDelay(mKeyguardMonitor.getKeyguardFadingAwayDelay())
- .setDuration(mKeyguardMonitor.getShortenedFadingAwayDuration())
+ .setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay())
+ .setDuration(mKeyguardStateController.getShortenedFadingAwayDuration())
.start();
}
} else if (mBarState == StatusBarState.SHADE_LOCKED
@@ -1902,9 +1925,11 @@ public class NotificationPanelView extends PanelView implements
mBarState != StatusBarState.KEYGUARD && (!mQsExpanded
|| mQsExpansionFromOverscroll));
updateEmptyShadeView();
- mNPVPluginManager.changeVisibility((mBarState != StatusBarState.KEYGUARD)
- ? View.VISIBLE
- : View.INVISIBLE);
+ if (mNPVPluginManager != null) {
+ mNPVPluginManager.changeVisibility((mBarState != StatusBarState.KEYGUARD)
+ ? View.VISIBLE
+ : View.INVISIBLE);
+ }
mQsNavbarScrim.setVisibility(mBarState == StatusBarState.SHADE && mQsExpanded
&& !mStackScrollerOverscrolling && mQsScrimEnabled
? View.VISIBLE
@@ -1962,7 +1987,9 @@ public class NotificationPanelView extends PanelView implements
float qsExpansionFraction = getQsExpansionFraction();
mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation());
int heightDiff = mQs.getDesiredHeight() - mQs.getQsMinExpansionHeight();
- mNPVPluginManager.setExpansion(qsExpansionFraction, getHeaderTranslation(), heightDiff);
+ if (mNPVPluginManager != null) {
+ mNPVPluginManager.setExpansion(qsExpansionFraction, getHeaderTranslation(), heightDiff);
+ }
mNotificationStackScroller.setQsExpansionFraction(qsExpansionFraction);
}
@@ -2383,7 +2410,7 @@ public class NotificationPanelView extends PanelView implements
appearAmount = mNotificationStackScroller.calculateAppearFractionBypass();
}
startHeight = -mQs.getQsMinExpansionHeight();
- startHeight -= mNPVPluginManager.getHeight();
+ if (mNPVPluginManager != null) startHeight -= mNPVPluginManager.getHeight();
}
float translation = MathUtils.lerp(startHeight, 0,
Math.min(1.0f, appearAmount))
@@ -2527,7 +2554,7 @@ public class NotificationPanelView extends PanelView implements
mKeyguardStatusBar.setListening(listening);
if (mQs == null) return;
mQs.setListening(listening);
- mNPVPluginManager.setListening(listening);
+ if (mNPVPluginManager != null) mNPVPluginManager.setListening(listening);
}
@Override
@@ -2556,7 +2583,7 @@ public class NotificationPanelView extends PanelView implements
@Override
protected void onTrackingStarted() {
- mFalsingManager.onTrackingStarted(mStatusBar.isKeyguardCurrentlySecure());
+ mFalsingManager.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen());
super.onTrackingStarted();
if (mQsFullyExpanded) {
mQsExpandImmediate = true;
@@ -2846,7 +2873,7 @@ public class NotificationPanelView extends PanelView implements
@Override
protected boolean shouldUseDismissingAnimation() {
return mBarState != StatusBarState.SHADE
- && (!mStatusBar.isKeyguardCurrentlySecure() || !isTracking());
+ && (mKeyguardStateController.canDismissLockScreen() || !isTracking());
}
@Override
@@ -3412,9 +3439,8 @@ public class NotificationPanelView extends PanelView implements
public void setPulsing(boolean pulsing) {
mPulsing = pulsing;
- DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
- final boolean animatePulse = !dozeParameters.getDisplayNeedsBlanking()
- && dozeParameters.getAlwaysOn();
+ final boolean animatePulse = !mDozeParameters.getDisplayNeedsBlanking()
+ && mDozeParameters.getAlwaysOn();
if (animatePulse) {
mAnimateNextPositionUpdate = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 938eeafb6a8c..b3051b3dd26e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -44,13 +44,12 @@ import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import android.util.BoostFramework;
import java.io.FileDescriptor;
@@ -113,6 +112,7 @@ public abstract class PanelView extends FrameLayout {
private FlingAnimationUtils mFlingAnimationUtilsClosing;
private FlingAnimationUtils mFlingAnimationUtilsDismissing;
private final FalsingManager mFalsingManager;
+ private final DozeLog mDozeLog;
private final VibratorHelper mVibratorHelper;
/**
@@ -150,9 +150,8 @@ public abstract class PanelView extends FrameLayout {
private boolean mGestureWaitForTouchSlop;
private boolean mIgnoreXTouchSlop;
private boolean mExpandLatencyTracking;
- protected final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
- protected final SysuiStatusBarStateController mStatusBarStateController =
- (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class);
+ protected final KeyguardStateController mKeyguardStateController;
+ protected final SysuiStatusBarStateController mStatusBarStateController;
protected void onExpandingFinished() {
mBar.onExpandingFinished();
@@ -210,8 +209,12 @@ public abstract class PanelView extends FrameLayout {
mJustPeeked = true;
}
- public PanelView(Context context, AttributeSet attrs) {
+ public PanelView(Context context, AttributeSet attrs, FalsingManager falsingManager,
+ DozeLog dozeLog, KeyguardStateController keyguardStateController,
+ SysuiStatusBarStateController statusBarStateController) {
super(context, attrs);
+ mKeyguardStateController = keyguardStateController;
+ mStatusBarStateController = statusBarStateController;
mFlingAnimationUtils = new FlingAnimationUtils(context, 0.6f /* maxLengthSeconds */,
0.6f /* speedUpFactor */);
mFlingAnimationUtilsClosing = new FlingAnimationUtils(context, 0.5f /* maxLengthSeconds */,
@@ -220,7 +223,8 @@ public abstract class PanelView extends FrameLayout {
0.5f /* maxLengthSeconds */, 0.2f /* speedUpFactor */, 0.6f /* x2 */,
0.84f /* y2 */);
mBounceInterpolator = new BounceInterpolator();
- mFalsingManager = Dependency.get(FalsingManager.class); // TODO: inject into a controller.
+ mFalsingManager = falsingManager;
+ mDozeLog = dozeLog;
mNotificationsDragEnabled =
getResources().getBoolean(R.bool.config_enableNotificationShadeDrag);
mVibratorHelper = Dependency.get(VibratorHelper.class);
@@ -485,7 +489,7 @@ public abstract class PanelView extends FrameLayout {
boolean expand = flingExpands(vel, vectorVel, x, y)
|| event.getActionMasked() == MotionEvent.ACTION_CANCEL
|| forceCancel;
- DozeLog.traceFling(expand, mTouchAboveFalsingThreshold,
+ mDozeLog.traceFling(expand, mTouchAboveFalsingThreshold,
mStatusBar.isFalsingThresholdNeeded(),
mStatusBar.isWakeUpComingFromTouch());
// Log collapse gesture if on lock screen.
@@ -504,7 +508,8 @@ public abstract class PanelView extends FrameLayout {
mUpdateFlingVelocity = vel;
}
} else if (mPanelClosedOnDown && !mHeadsUpManager.hasPinnedHeadsUp() && !mTracking
- && !mStatusBar.isBouncerShowing() && !mKeyguardMonitor.isKeyguardFadingAway()) {
+ && !mStatusBar.isBouncerShowing()
+ && !mKeyguardStateController.isKeyguardFadingAway()) {
long timePassed = SystemClock.uptimeMillis() - mDownTime;
if (timePassed < ViewConfiguration.getLongPressTimeout()) {
// Lets show the user that he can actually expand the panel
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 6c27a04752a3..fabd67e9ab5d 100755
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -58,7 +58,7 @@ import com.android.systemui.statusbar.policy.DataSaverController.Listener;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.RotationLockController;
@@ -83,7 +83,7 @@ public class PhoneStatusBarPolicy
Listener,
ZenModeController.Callback,
DeviceProvisionedListener,
- KeyguardMonitor.Callback,
+ KeyguardStateController.Callback,
PrivacyItemController.Callback,
LocationController.LocationChangeCallback {
private static final String TAG = "PhoneStatusBarPolicy";
@@ -120,7 +120,7 @@ public class PhoneStatusBarPolicy
private final DataSaverController mDataSaver;
private final ZenModeController mZenController;
private final DeviceProvisionedController mProvisionedController;
- private final KeyguardMonitor mKeyguardMonitor;
+ private final KeyguardStateController mKeyguardStateController;
private final LocationController mLocationController;
private final PrivacyItemController mPrivacyItemController;
private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
@@ -154,7 +154,7 @@ public class PhoneStatusBarPolicy
mDataSaver = Dependency.get(DataSaverController.class);
mZenController = Dependency.get(ZenModeController.class);
mProvisionedController = Dependency.get(DeviceProvisionedController.class);
- mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+ mKeyguardStateController = Dependency.get(KeyguardStateController.class);
mLocationController = Dependency.get(LocationController.class);
mPrivacyItemController = Dependency.get(PrivacyItemController.class);
mSensorPrivacyController = Dependency.get(SensorPrivacyController.class);
@@ -254,7 +254,7 @@ public class PhoneStatusBarPolicy
mHotspot.addCallback(mHotspotCallback);
mNextAlarmController.addCallback(mNextAlarmCallback);
mDataSaver.addCallback(this);
- mKeyguardMonitor.addCallback(this);
+ mKeyguardStateController.addCallback(this);
mPrivacyItemController.addCallback(this);
mSensorPrivacyController.addCallback(mSensorPrivacyListener);
mLocationController.addCallback(this);
@@ -470,8 +470,8 @@ public class PhoneStatusBarPolicy
boolean isManagedProfile = mUserManager.isManagedProfile(userId);
mHandler.post(() -> {
final boolean showIcon;
- if (isManagedProfile &&
- (!mKeyguardMonitor.isShowing() || mKeyguardMonitor.isOccluded())) {
+ if (isManagedProfile && (!mKeyguardStateController.isShowing()
+ || mKeyguardStateController.isOccluded())) {
showIcon = true;
mIconController.setIcon(mSlotManagedProfile,
R.drawable.stat_sys_managed_profile_status,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java
index c1ff572bb210..1a6b415f87db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java
@@ -127,6 +127,11 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener,
updateSamplingListener();
}
+ void stopAndDestroy() {
+ stop();
+ mSamplingListener.destroy();
+ }
+
@Override
public void onViewAttachedToWindow(View view) {
updateSamplingListener();
@@ -134,9 +139,7 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener,
@Override
public void onViewDetachedFromWindow(View view) {
- // isAttachedToWindow is only changed after this call to the listeners, so let's post it
- // instead
- postUpdateSamplingListener();
+ stopAndDestroy();
}
@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 8c927799c31c..35039a0d74f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -21,7 +21,7 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.app.AlarmManager;
-import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Handler;
@@ -41,13 +41,14 @@ import com.android.internal.graphics.ColorUtils;
import com.android.internal.util.function.TriConsumer;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.Dependency;
+import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.dagger.qualifiers.MainResources;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.notification.stack.ViewState;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.AlarmTimeout;
import com.android.systemui.util.wakelock.DelayedWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
@@ -58,10 +59,14 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.function.Consumer;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Controls both the scrim behind the notifications and in front of the notifications (when a
* security method gets shown).
*/
+@Singleton
public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnColorsChangedListener,
Dumpable {
@@ -123,13 +128,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
private static final float NOT_INITIALIZED = -1;
private ScrimState mState = ScrimState.UNINITIALIZED;
- private final Context mContext;
- protected final ScrimView mScrimInFront;
- protected final ScrimView mScrimBehind;
- protected final ScrimView mScrimForBubble;
+ private ScrimView mScrimInFront;
+ private ScrimView mScrimBehind;
+ private ScrimView mScrimForBubble;
- private final UnlockMethodCache mUnlockMethodCache;
+ private final KeyguardStateController mKeyguardStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final DozeParameters mDozeParameters;
private final AlarmTimeout mTimeTicker;
@@ -140,22 +144,21 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
private GradientColors mColors;
private boolean mNeedsDrawableColorUpdate;
- protected float mScrimBehindAlpha;
- protected float mScrimBehindAlphaResValue;
- protected float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD;
+ private float mScrimBehindAlpha;
+ private float mScrimBehindAlphaResValue;
+ private float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD;
// Assuming the shade is expanded during initialization
private float mExpansionFraction = 1f;
private boolean mDarkenWhileDragging;
private boolean mExpansionAffectsAlpha = true;
- protected boolean mAnimateChange;
+ private boolean mAnimateChange;
private boolean mUpdatePending;
private boolean mTracking;
- protected long mAnimationDuration = -1;
+ private long mAnimationDuration = -1;
private long mAnimationDelay;
- private Runnable mOnAnimationFinished;
- private boolean mDeferFinishedListener;
+ private Animator.AnimatorListener mAnimatorListener;
private final Interpolator mInterpolator = new DecelerateInterpolator();
private float mInFrontAlpha = NOT_INITIALIZED;
@@ -169,7 +172,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
private boolean mWallpaperVisibilityTimedOut;
private int mScrimsVisibility;
private final TriConsumer<ScrimState, Float, GradientColors> mScrimStateListener;
- private final Consumer<Integer> mScrimVisibleListener;
+ private Consumer<Integer> mScrimVisibleListener;
private boolean mBlankScreen;
private boolean mScreenBlankingCallbackCalled;
private Callback mCallback;
@@ -184,44 +187,50 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
private boolean mWakeLockHeld;
private boolean mKeyguardOccluded;
- public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront, ScrimView scrimForBubble,
- TriConsumer<ScrimState, Float, GradientColors> scrimStateListener,
- Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters,
- AlarmManager alarmManager, KeyguardMonitor keyguardMonitor) {
- mScrimBehind = scrimBehind;
- mScrimInFront = scrimInFront;
- mScrimForBubble = scrimForBubble;
+ @Inject
+ public ScrimController(LightBarController lightBarController, DozeParameters dozeParameters,
+ AlarmManager alarmManager, KeyguardStateController keyguardStateController,
+ @MainResources Resources resources,
+ DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler,
+ KeyguardUpdateMonitor keyguardUpdateMonitor, SysuiColorExtractor sysuiColorExtractor) {
- mScrimStateListener = scrimStateListener;
- mScrimVisibleListener = scrimVisibleListener;
+ mScrimStateListener = lightBarController::setScrimState;
- mContext = scrimBehind.getContext();
- mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
- mDarkenWhileDragging = !mUnlockMethodCache.canSkipBouncer();
- mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
+ mKeyguardStateController = keyguardStateController;
+ mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen();
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mKeyguardVisibilityCallback = new KeyguardVisibilityCallback();
- mKeyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
- mScrimBehindAlphaResValue = mContext.getResources().getFloat(R.dimen.scrim_behind_alpha);
- mHandler = getHandler();
+ mScrimBehindAlphaResValue = resources.getFloat(R.dimen.scrim_behind_alpha);
+ mHandler = handler;
mTimeTicker = new AlarmTimeout(alarmManager, this::onHideWallpaperTimeout,
"hide_aod_wallpaper", mHandler);
- mWakeLock = createWakeLock();
+ mWakeLock = delayedWakeLockBuilder.setHandler(mHandler).setTag("Scrims").build();
// Scrim alpha is initially set to the value on the resource but might be changed
// to make sure that text on top of it is legible.
mScrimBehindAlpha = mScrimBehindAlphaResValue;
mDozeParameters = dozeParameters;
- keyguardMonitor.addCallback(new KeyguardMonitor.Callback() {
+ keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
public void onKeyguardFadingAwayChanged() {
- setKeyguardFadingAway(keyguardMonitor.isKeyguardFadingAway(),
- keyguardMonitor.getKeyguardFadingAwayDuration());
+ setKeyguardFadingAway(keyguardStateController.isKeyguardFadingAway(),
+ keyguardStateController.getKeyguardFadingAwayDuration());
}
});
- mColorExtractor = Dependency.get(SysuiColorExtractor.class);
+ mColorExtractor = sysuiColorExtractor;
mColorExtractor.addOnColorsChangedListener(this);
mColors = mColorExtractor.getNeutralColors();
mNeedsDrawableColorUpdate = true;
+ }
+
+ /**
+ * Attach the controller to the supplied views.
+ */
+ public void attachViews(
+ ScrimView scrimBehind, ScrimView scrimInFront, ScrimView scrimForBubble) {
+ mScrimBehind = scrimBehind;
+ mScrimInFront = scrimInFront;
+ mScrimForBubble = scrimForBubble;
final ScrimState[] states = ScrimState.values();
for (int i = 0; i < states.length; i++) {
@@ -232,8 +241,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
mScrimBehind.setDefaultFocusHighlightEnabled(false);
mScrimInFront.setDefaultFocusHighlightEnabled(false);
mScrimForBubble.setDefaultFocusHighlightEnabled(false);
-
updateScrims();
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
+ }
+
+ void setScrimVisibleListener(Consumer<Integer> listener) {
+ mScrimVisibleListener = listener;
}
public void transitionTo(ScrimState state) {
@@ -257,7 +270,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
final ScrimState oldState = mState;
mState = state;
- Trace.traceCounter(Trace.TRACE_TAG_APP, "scrim_state", mState.getIndex());
+ Trace.traceCounter(Trace.TRACE_TAG_APP, "scrim_state", mState.ordinal());
if (mCallback != null) {
mCallback.onCancelled();
@@ -311,10 +324,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
// Docking pulses may take a long time, wallpapers should also fade away after a while.
mWallpaperVisibilityTimedOut = false;
if (shouldFadeAwayWallpaper()) {
- mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(),
- AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
+ DejankUtils.postAfterTraversal(() -> {
+ mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(),
+ AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
+ });
} else {
- mTimeTicker.cancel();
+ DejankUtils.postAfterTraversal(mTimeTicker::cancel);
}
if (mKeyguardUpdateMonitor.needsSlowUnlockTransition() && mState == ScrimState.UNLOCKED) {
@@ -367,7 +382,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
public void onTrackingStarted() {
mTracking = true;
- mDarkenWhileDragging = !mUnlockMethodCache.canSkipBouncer();
+ mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen();
}
public void onExpandingFinished() {
@@ -430,8 +445,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
// and docking.
if (mWallpaperVisibilityTimedOut) {
mWallpaperVisibilityTimedOut = false;
- mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(),
- AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
+ DejankUtils.postAfterTraversal(() -> {
+ mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(),
+ AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
+ });
}
}
}
@@ -514,22 +531,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
}
/**
- * Set front scrim to black, cancelling animations, in order to prepare to fade them
- * away once the display turns on.
- */
- public void prepareForGentleWakeUp() {
- if (mState == ScrimState.AOD && mDozeParameters.getAlwaysOn()) {
- mInFrontAlpha = 1f;
- mInFrontTint = Color.BLACK;
- mBehindTint = Color.BLACK;
- mAnimateChange = false;
- updateScrims();
- mAnimateChange = true;
- mAnimationDuration = ANIMATION_DURATION_LONG;
- }
- }
-
- /**
* If the lock screen sensor is active.
*/
public void setWakeLockScreenSensorActive(boolean active) {
@@ -590,6 +591,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
setScrimAlpha(mScrimInFront, mInFrontAlpha);
setScrimAlpha(mScrimBehind, mBehindAlpha);
setScrimAlpha(mScrimForBubble, mBubbleAlpha);
+ // The animation could have all already finished, let's call onFinished just in case
+ onFinished();
dispatchScrimsVisible();
}
@@ -667,6 +670,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
private void startScrimAnimation(final View scrim, float current) {
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
+ if (mAnimatorListener != null) {
+ anim.addListener(mAnimatorListener);
+ }
final int initialScrimTint = scrim instanceof ScrimView ? ((ScrimView) scrim).getTint() :
Color.TRANSPARENT;
anim.addUpdateListener(animation -> {
@@ -688,15 +694,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
@Override
public void onAnimationEnd(Animator animation) {
+ scrim.setTag(TAG_KEY_ANIM, null);
onFinished(lastCallback);
- scrim.setTag(TAG_KEY_ANIM, null);
dispatchScrimsVisible();
-
- if (!mDeferFinishedListener && mOnAnimationFinished != null) {
- mOnAnimationFinished.run();
- mOnAnimationFinished = null;
- }
}
});
@@ -741,11 +742,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
mCallback.onStart();
}
updateScrims();
- if (mOnAnimationFinished != null && !isAnimating(mScrimInFront)
- && !isAnimating(mScrimBehind)) {
- mOnAnimationFinished.run();
- mOnAnimationFinished = null;
- }
return true;
}
@@ -754,6 +750,16 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
}
private void onFinished(Callback callback) {
+ if (isAnimating(mScrimBehind)
+ || isAnimating(mScrimInFront)
+ || isAnimating(mScrimForBubble)) {
+ if (callback != null && callback != mCallback) {
+ // Since we only notify the callback that we're finished once everything has
+ // finished, we need to make sure that any changing callbacks are also invoked
+ callback.onFinished();
+ }
+ return;
+ }
if (mWakeLockHeld) {
mWakeLock.release(TAG);
mWakeLockHeld = false;
@@ -773,6 +779,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
mInFrontTint = Color.TRANSPARENT;
mBehindTint = Color.TRANSPARENT;
mBubbleTint = Color.TRANSPARENT;
+ updateScrimColor(mScrimInFront, mInFrontAlpha, mInFrontTint);
+ updateScrimColor(mScrimBehind, mBehindAlpha, mBehindTint);
+ updateScrimColor(mScrimForBubble, mBubbleAlpha, mBubbleTint);
}
}
@@ -781,8 +790,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
}
@VisibleForTesting
- void setOnAnimationFinished(Runnable onAnimationFinished) {
- mOnAnimationFinished = onAnimationFinished;
+ void setAnimatorListener(Animator.AnimatorListener animatorListener) {
+ mAnimatorListener = animatorListener;
}
private void updateScrim(ScrimView scrim, float alpha) {
@@ -790,16 +799,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
ValueAnimator previousAnimator = ViewState.getChildTag(scrim, TAG_KEY_ANIM);
if (previousAnimator != null) {
- if (mAnimateChange) {
- // We are not done yet! Defer calling the finished listener.
- mDeferFinishedListener = true;
- }
// Previous animators should always be cancelled. Not doing so would cause
// overlap, especially on states that don't animate, leading to flickering,
// and in the worst case, an internal state that doesn't represent what
// transitionTo requested.
cancelAnimator(previousAnimator);
- mDeferFinishedListener = false;
}
if (mPendingFrameCallback != null) {
@@ -831,15 +835,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
} else {
// update the alpha directly
updateScrimColor(scrim, alpha, getCurrentScrimTint(scrim));
- onFinished();
}
- } else {
- onFinished();
}
}
- @VisibleForTesting
- protected void cancelAnimator(ValueAnimator previousAnimator) {
+ private void cancelAnimator(ValueAnimator previousAnimator) {
if (previousAnimator != null) {
previousAnimator.cancel();
}
@@ -887,11 +887,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
mScrimBehind.postOnAnimationDelayed(callback, 32 /* delayMillis */);
}
- @VisibleForTesting
- protected Handler getHandler() {
- return new Handler();
- }
-
public int getBackgroundColor() {
int color = mColors.getMainColor();
return Color.argb((int) (mScrimBehind.getViewAlpha() * Color.alpha(color)),
@@ -913,11 +908,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
scheduleUpdate();
}
- @VisibleForTesting
- protected WakeLock createWakeLock() {
- return new DelayedWakeLock(mHandler, WakeLock.createPartial(mContext, "Scrims"));
- }
-
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(" ScrimController: ");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 7463c7c232bd..13055ffb2f77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -30,12 +30,35 @@ public enum ScrimState {
/**
* Initial state.
*/
- UNINITIALIZED(-1),
+ UNINITIALIZED,
+
+ /**
+ * When turned off by sensors (prox, presence.)
+ */
+ OFF {
+ @Override
+ public void prepare(ScrimState previousState) {
+ mFrontTint = Color.BLACK;
+ mBehindTint = Color.BLACK;
+ mBubbleTint = previousState.mBubbleTint;
+
+ mFrontAlpha = 1f;
+ mBehindAlpha = 1f;
+ mBubbleAlpha = previousState.mBubbleAlpha;
+
+ mAnimationDuration = ScrimController.ANIMATION_DURATION_LONG;
+ }
+
+ @Override
+ public boolean isLowPowerState() {
+ return true;
+ }
+ },
/**
* On the lock screen.
*/
- KEYGUARD(0) {
+ KEYGUARD {
@Override
public void prepare(ScrimState previousState) {
mBlankScreen = false;
@@ -65,7 +88,7 @@ public enum ScrimState {
/**
* Showing password challenge on the keyguard.
*/
- BOUNCER(1) {
+ BOUNCER {
@Override
public void prepare(ScrimState previousState) {
mBehindAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY;
@@ -77,7 +100,7 @@ public enum ScrimState {
/**
* Showing password challenge on top of a FLAG_SHOW_WHEN_LOCKED activity.
*/
- BOUNCER_SCRIMMED(2) {
+ BOUNCER_SCRIMMED {
@Override
public void prepare(ScrimState previousState) {
mBehindAlpha = 0;
@@ -89,7 +112,7 @@ public enum ScrimState {
/**
* Changing screen brightness from quick settings.
*/
- BRIGHTNESS_MIRROR(3) {
+ BRIGHTNESS_MIRROR {
@Override
public void prepare(ScrimState previousState) {
mBehindAlpha = 0;
@@ -101,7 +124,7 @@ public enum ScrimState {
/**
* Always on display or screen off.
*/
- AOD(4) {
+ AOD {
@Override
public void prepare(ScrimState previousState) {
final boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn();
@@ -136,7 +159,7 @@ public enum ScrimState {
/**
* When phone wakes up because you received a notification.
*/
- PULSING(5) {
+ PULSING {
@Override
public void prepare(ScrimState previousState) {
mFrontAlpha = mAodFrontScrimAlpha;
@@ -164,7 +187,7 @@ public enum ScrimState {
/**
* Unlocked on top of an app (launcher or any other activity.)
*/
- UNLOCKED(6) {
+ UNLOCKED {
@Override
public void prepare(ScrimState previousState) {
// State that UI will sync to.
@@ -201,7 +224,7 @@ public enum ScrimState {
/**
* Unlocked with a bubble expanded.
*/
- BUBBLE_EXPANDED(7) {
+ BUBBLE_EXPANDED {
@Override
public void prepare(ScrimState previousState) {
mFrontTint = Color.TRANSPARENT;
@@ -237,17 +260,12 @@ public enum ScrimState {
DozeParameters mDozeParameters;
boolean mDisplayRequiresBlanking;
boolean mWallpaperSupportsAmbientMode;
- int mIndex;
boolean mHasBackdrop;
boolean mLaunchingAffordanceWithPreview;
boolean mWakeLockScreenSensorActive;
boolean mKeyguardFadingAway;
long mKeyguardFadingAwayDuration;
- ScrimState(int index) {
- mIndex = index;
- }
-
public void init(ScrimView scrimInFront, ScrimView scrimBehind, ScrimView scrimForBubble,
DozeParameters dozeParameters) {
mScrimInFront = scrimInFront;
@@ -262,10 +280,6 @@ public enum ScrimState {
public void prepare(ScrimState previousState) {
}
- public int getIndex() {
- return mIndex;
- }
-
public float getFrontAlpha() {
return mFrontAlpha;
}
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 90894b08dc7f..11080619a5d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -24,8 +24,12 @@ import static android.app.StatusBarManager.WindowType;
import static android.app.StatusBarManager.WindowVisibleState;
import static android.app.StatusBarManager.windowStateToString;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.view.InsetsFlags.getAppearance;
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static android.view.InsetsState.containsType;
+import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_TOP_BAR;
-import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.Dependency.BG_HANDLER;
import static com.android.systemui.Dependency.MAIN_HANDLER;
@@ -46,12 +50,10 @@ import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
-import android.app.AlarmManager;
import android.app.AppOpsManager;
import android.app.IWallpaperManager;
import android.app.KeyguardManager;
@@ -76,7 +78,6 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
-import android.graphics.Rect;
import android.media.AudioAttributes;
import android.metrics.LogMaker;
import android.net.Uri;
@@ -105,6 +106,7 @@ import android.util.Log;
import android.util.Slog;
import android.view.Display;
import android.view.IWindowManager;
+import android.view.InsetsState.InternalInsetType;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -113,6 +115,7 @@ import android.view.ThreadedRenderer;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
+import android.view.WindowInsetsController.Appearance;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
@@ -125,6 +128,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.RegisterStatusBarResult;
+import com.android.internal.view.AppearanceRegion;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -147,13 +151,13 @@ import com.android.systemui.SystemUIFactory;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.appops.AppOpsController;
import com.android.systemui.assist.AssistManager;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.charging.WirelessChargingAnimation;
import com.android.systemui.classifier.FalsingLog;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
-import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.fragments.ExtensionFragmentListener;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.keyguard.KeyguardSliceProvider;
@@ -178,6 +182,7 @@ import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.EmptyShadeView;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.KeyboardShortcuts;
import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -197,6 +202,7 @@ import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NewNotifPipeline;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.NotificationClicker;
@@ -208,11 +214,11 @@ import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
@@ -222,7 +228,7 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
@@ -242,11 +248,14 @@ import java.util.Map;
import javax.inject.Inject;
import javax.inject.Named;
+import javax.inject.Singleton;
+import dagger.Lazy;
import dagger.Subcomponent;
+@Singleton
public class StatusBar extends SystemUI implements DemoMode,
- ActivityStarter, OnUnlockMethodChangedListener,
+ ActivityStarter, KeyguardStateController.Callback,
OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
ColorExtractor.OnColorsChangedListener, ConfigurationListener,
StatusBarStateController.StateListener, ShadeController,
@@ -335,7 +344,7 @@ public class StatusBar extends SystemUI implements DemoMode,
/**
* The {@link StatusBarState} of the status bar.
*/
- protected int mState;
+ protected int mState; // TODO: remove this. Just use StatusBarStateController
protected boolean mBouncerShowing;
private PhoneStatusBarPolicy mIconPolicy;
@@ -344,51 +353,49 @@ public class StatusBar extends SystemUI implements DemoMode,
private VolumeComponent mVolumeComponent;
private BrightnessMirrorController mBrightnessMirrorController;
private boolean mBrightnessMirrorVisible;
- protected BiometricUnlockController mBiometricUnlockController;
- private LightBarController mLightBarController;
+ private BiometricUnlockController mBiometricUnlockController;
+ private final LightBarController mLightBarController;
+ private final Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy;
protected LockscreenWallpaper mLockscreenWallpaper;
- @VisibleForTesting
- protected AutoHideController mAutoHideController;
+ private final AutoHideController mAutoHideController;
private int mNaturalBarHeight = -1;
private final Point mCurrentDisplaySize = new Point();
+ protected StatusBarWindowViewController mStatusBarWindowViewController;
protected StatusBarWindowView mStatusBarWindow;
protected PhoneStatusBarView mStatusBarView;
private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
protected StatusBarWindowController mStatusBarWindowController;
- protected UnlockMethodCache mUnlockMethodCache;
- @VisibleForTesting
- KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@VisibleForTesting
- DozeServiceHost mDozeServiceHost = new DozeServiceHost();
+ DozeServiceHost mDozeServiceHost;
private boolean mWakeUpComingFromTouch;
private PointF mWakeUpTouchLocation;
private final Object mQueueLock = new Object();
- protected StatusBarIconController mIconController;
- @Inject
- InjectionInflationController mInjectionInflater;
- @Inject
- PulseExpansionHandler mPulseExpansionHandler;
- @Inject
- NotificationWakeUpCoordinator mWakeUpCoordinator;
- @Inject
- KeyguardBypassController mKeyguardBypassController;
- @Inject
- protected HeadsUpManagerPhone mHeadsUpManager;
- @Inject
- DynamicPrivacyController mDynamicPrivacyController;
- @Inject
- BypassHeadsUpNotifier mBypassHeadsUpNotifier;
- @Nullable
- @Inject
- protected KeyguardLiftController mKeyguardLiftController;
- @Inject
- @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME)
- boolean mAllowNotificationLongPress;
+ private final FeatureFlags mFeatureFlags;
+ private final StatusBarIconController mIconController;
+ private final DozeLog mDozeLog;
+ private final InjectionInflationController mInjectionInflater;
+ private final PulseExpansionHandler mPulseExpansionHandler;
+ private final NotificationWakeUpCoordinator mWakeUpCoordinator;
+ private final KeyguardBypassController mKeyguardBypassController;
+ private final KeyguardStateController mKeyguardStateController;
+ private final HeadsUpManagerPhone mHeadsUpManager;
+ private final DynamicPrivacyController mDynamicPrivacyController;
+ private final BypassHeadsUpNotifier mBypassHeadsUpNotifier;
+ private final boolean mAllowNotificationLongPress;
+ private final Lazy<NewNotifPipeline> mNewNotifPipeline;
+ private final FalsingManager mFalsingManager;
+ private final BroadcastDispatcher mBroadcastDispatcher;
+ private final ConfigurationController mConfigurationController;
+ private final StatusBarWindowViewController.Builder mStatusBarWindowViewControllerBuilder;
+ private final NotifLog mNotifLog;
+ private final DozeParameters mDozeParameters;
+ private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
// expanded notifications
protected NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
@@ -401,8 +408,7 @@ public class StatusBar extends SystemUI implements DemoMode,
// RemoteInputView to be activated after unlock
private View mPendingRemoteInputView;
- private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler =
- Dependency.get(RemoteInputQuickSettingsDisabler.class);
+ private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
private View mReportRejectedTouch;
@@ -411,29 +417,31 @@ public class StatusBar extends SystemUI implements DemoMode,
private final int[] mAbsPos = new int[2];
private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
- private NotificationGutsManager mGutsManager;
- protected NotificationLogger mNotificationLogger;
- protected NotificationEntryManager mEntryManager;
+ private final NotificationGutsManager mGutsManager;
+ private final NotificationLogger mNotificationLogger;
+ private final NotificationEntryManager mEntryManager;
private NotificationListController mNotificationListController;
- private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
- protected NotificationViewHierarchyManager mViewHierarchyManager;
- protected ForegroundServiceController mForegroundServiceController;
- protected AppOpsController mAppOpsController;
- protected KeyguardViewMediator mKeyguardViewMediator;
- private ZenModeController mZenController;
- private final NotificationAlertingManager mNotificationAlertingManager =
- Dependency.get(NotificationAlertingManager.class);
+ private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+ private final NotificationViewHierarchyManager mViewHierarchyManager;
+ private final ForegroundServiceController mForegroundServiceController;
+ private final AppOpsController mAppOpsController;
+ private final KeyguardViewMediator mKeyguardViewMediator;
+ private final ZenModeController mZenController;
+ private final NotificationAlertingManager mNotificationAlertingManager;
// for disabling the status bar
private int mDisabled1 = 0;
private int mDisabled2 = 0;
- // tracking calls to View.setSystemUiVisibility()
- private int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
- private final Rect mLastFullscreenStackBounds = new Rect();
- private final Rect mLastDockedStackBounds = new Rect();
+ /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int) */
+ private @Appearance int mAppearance;
- private final DisplayMetrics mDisplayMetrics = Dependency.get(DisplayMetrics.class);
+ private boolean mTransientShown;
+
+ private boolean mAppFullscreen;
+ private boolean mAppImmersive;
+
+ private final DisplayMetrics mDisplayMetrics;
// XXX: gesture research
private final GestureRecorder mGestureRec = DEBUG_GESTURES
@@ -442,7 +450,7 @@ public class StatusBar extends SystemUI implements DemoMode,
private ScreenPinningRequest mScreenPinningRequest;
- private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
+ private final MetricsLogger mMetricsLogger;
// ensure quick settings is disabled until the current user makes it through the setup wizard
@VisibleForTesting
@@ -477,16 +485,15 @@ public class StatusBar extends SystemUI implements DemoMode,
private @TransitionMode int mStatusBarMode;
private ViewMediatorCallback mKeyguardViewMediatorCallback;
- protected ScrimController mScrimController;
+ private final ScrimController mScrimController;
protected DozeScrimController mDozeScrimController;
- private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
+ private final UiOffloadThread mUiOffloadThread;
protected boolean mDozing;
- private boolean mDozingRequested;
- private NotificationMediaManager mMediaManager;
- protected NotificationLockscreenUserManager mLockscreenUserManager;
- protected NotificationRemoteInputManager mRemoteInputManager;
+ private final NotificationMediaManager mMediaManager;
+ private final NotificationLockscreenUserManager mLockscreenUserManager;
+ private final NotificationRemoteInputManager mRemoteInputManager;
private boolean mWallpaperSupported;
private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() {
@@ -501,8 +508,7 @@ public class StatusBar extends SystemUI implements DemoMode,
WallpaperInfo info = wallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT);
final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_dozeSupportsAodWallpaper);
- final boolean imageWallpaperInAmbient =
- !DozeParameters.getInstance(mContext).getDisplayNeedsBlanking();
+ final boolean imageWallpaperInAmbient = !mDozeParameters.getDisplayNeedsBlanking();
// If WallpaperInfo is null, it must be ImageWallpaper.
final boolean supportsAmbientMode = deviceSupportsAodWallpaper
&& ((info == null && imageWallpaperInAmbient)
@@ -545,7 +551,7 @@ public class StatusBar extends SystemUI implements DemoMode,
+ "mStatusBarKeyguardViewManager was null");
return;
}
- if (mKeyguardMonitor.isKeyguardFadingAway()) {
+ if (mKeyguardStateController.isKeyguardFadingAway()) {
mStatusBarKeyguardViewManager.onKeyguardFadedAway();
}
}
@@ -557,19 +563,18 @@ public class StatusBar extends SystemUI implements DemoMode,
};
private KeyguardUserSwitcher mKeyguardUserSwitcher;
- protected UserSwitcherController mUserSwitcherController;
- private NetworkController mNetworkController;
- private KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
- private BatteryController mBatteryController;
+ private final UserSwitcherController mUserSwitcherController;
+ private final NetworkController mNetworkController;
+ private final BatteryController mBatteryController;
protected boolean mPanelExpanded;
private UiModeManager mUiModeManager;
protected boolean mIsKeyguard;
private LogMaker mStatusBarStateLog;
protected NotificationIconAreaController mNotificationIconAreaController;
@Nullable private View mAmbientIndicationContainer;
- private SysuiColorExtractor mColorExtractor;
- private ScreenLifecycle mScreenLifecycle;
- @VisibleForTesting WakefulnessLifecycle mWakefulnessLifecycle;
+ private final SysuiColorExtractor mColorExtractor;
+ private final ScreenLifecycle mScreenLifecycle;
+ private final WakefulnessLifecycle mWakefulnessLifecycle;
private final View.OnClickListener mGoToLockedShadeListener = v -> {
if (mState == StatusBarState.KEYGUARD) {
@@ -578,9 +583,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
};
private boolean mNoAnimationOnNextBarModeChange;
- protected FalsingManager mFalsingManager;
- private final SysuiStatusBarStateController mStatusBarStateController =
- (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class);
+ private final SysuiStatusBarStateController mStatusBarStateController;
private final KeyguardUpdateMonitorCallback mUpdateCallback =
new KeyguardUpdateMonitorCallback() {
@@ -594,26 +597,21 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void onStrongAuthStateChanged(int userId) {
super.onStrongAuthStateChanged(userId);
- mEntryManager.updateNotifications();
+ mEntryManager.updateNotifications("onStrongAuthStateChanged");
}
};
private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private boolean mVibrateOnOpening;
- private VibratorHelper mVibratorHelper;
+ private final VibratorHelper mVibratorHelper;
private ActivityLaunchAnimator mActivityLaunchAnimator;
protected StatusBarNotificationPresenter mPresenter;
private NotificationActivityStarter mNotificationActivityStarter;
- private boolean mPulsing;
- protected BubbleController mBubbleController;
- private final BubbleController.BubbleExpandListener mBubbleExpandListener =
- (isExpanding, key) -> {
- mEntryManager.updateNotifications();
- updateScrimController();
- };
+ private final BubbleController mBubbleController;
+ private final BubbleController.BubbleExpandListener mBubbleExpandListener;
+
private ActivityIntentHelper mActivityIntentHelper;
- private ShadeController mShadeController;
@Override
public void onActiveStateChanged(int code, int uid, String packageName, boolean active) {
@@ -629,46 +627,155 @@ public class StatusBar extends SystemUI implements DemoMode,
AppOpsManager.OP_COARSE_LOCATION,
AppOpsManager.OP_FINE_LOCATION};
+ @Inject
+ public StatusBar(
+ Context context,
+ FeatureFlags featureFlags,
+ LightBarController lightBarController,
+ AutoHideController autoHideController,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ StatusBarIconController statusBarIconController,
+ DozeLog dozeLog,
+ InjectionInflationController injectionInflationController,
+ PulseExpansionHandler pulseExpansionHandler,
+ NotificationWakeUpCoordinator notificationWakeUpCoordinator,
+ KeyguardBypassController keyguardBypassController,
+ KeyguardStateController keyguardStateController,
+ HeadsUpManagerPhone headsUpManagerPhone,
+ DynamicPrivacyController dynamicPrivacyController,
+ BypassHeadsUpNotifier bypassHeadsUpNotifier,
+ @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowNotificationLongPress,
+ Lazy<NewNotifPipeline> newNotifPipeline,
+ FalsingManager falsingManager,
+ BroadcastDispatcher broadcastDispatcher,
+ RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
+ NotificationGutsManager notificationGutsManager,
+ NotificationLogger notificationLogger,
+ NotificationEntryManager notificationEntryManager,
+ NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+ NotificationViewHierarchyManager notificationViewHierarchyManager,
+ ForegroundServiceController foregroundServiceController,
+ AppOpsController appOpsController,
+ KeyguardViewMediator keyguardViewMediator,
+ ZenModeController zenModeController,
+ NotificationAlertingManager notificationAlertingManager,
+ DisplayMetrics displayMetrics,
+ MetricsLogger metricsLogger,
+ UiOffloadThread uiOffloadThread,
+ 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,
+ NotificationGroupAlertTransferHelper groupAlertTransferHelper,
+ VisualStabilityManager visualStabilityManager,
+ DeviceProvisionedController deviceProvisionedController,
+ NavigationBarController navigationBarController,
+ AssistManager assistManager,
+ NotificationListener notificationListener,
+ ConfigurationController configurationController,
+ StatusBarWindowController statusBarWindowController,
+ StatusBarWindowViewController.Builder statusBarWindowViewControllerBuilder,
+ NotifLog notifLog,
+ DozeParameters dozeParameters,
+ ScrimController scrimController,
+ Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
+ Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
+ DozeServiceHost dozeServiceHost,
+ PowerManager powerManager,
+ DozeScrimController dozeScrimController) {
+ super(context);
+ mFeatureFlags = featureFlags;
+ mLightBarController = lightBarController;
+ mAutoHideController = autoHideController;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mIconController = statusBarIconController;
+ mDozeLog = dozeLog;
+ mInjectionInflater = injectionInflationController;
+ mPulseExpansionHandler = pulseExpansionHandler;
+ mWakeUpCoordinator = notificationWakeUpCoordinator;
+ mKeyguardBypassController = keyguardBypassController;
+ mKeyguardStateController = keyguardStateController;
+ mHeadsUpManager = headsUpManagerPhone;
+ mDynamicPrivacyController = dynamicPrivacyController;
+ mBypassHeadsUpNotifier = bypassHeadsUpNotifier;
+ mAllowNotificationLongPress = allowNotificationLongPress;
+ mNewNotifPipeline = newNotifPipeline;
+ mFalsingManager = falsingManager;
+ mBroadcastDispatcher = broadcastDispatcher;
+ mRemoteInputQuickSettingsDisabler = remoteInputQuickSettingsDisabler;
+ mGutsManager = notificationGutsManager;
+ mNotificationLogger = notificationLogger;
+ mEntryManager = notificationEntryManager;
+ mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
+ mViewHierarchyManager = notificationViewHierarchyManager;
+ mForegroundServiceController = foregroundServiceController;
+ mAppOpsController = appOpsController;
+ mKeyguardViewMediator = keyguardViewMediator;
+ mZenController = zenModeController;
+ mNotificationAlertingManager = notificationAlertingManager;
+ mDisplayMetrics = displayMetrics;
+ mMetricsLogger = metricsLogger;
+ mUiOffloadThread = uiOffloadThread;
+ mMediaManager = notificationMediaManager;
+ mLockscreenUserManager = lockScreenUserManager;
+ mRemoteInputManager = remoteInputManager;
+ mUserSwitcherController = userSwitcherController;
+ mNetworkController = networkController;
+ mBatteryController = batteryController;
+ mColorExtractor = colorExtractor;
+ mScreenLifecycle = screenLifecycle;
+ mWakefulnessLifecycle = wakefulnessLifecycle;
+ mStatusBarStateController = statusBarStateController;
+ mVibratorHelper = vibratorHelper;
+ mBubbleController = bubbleController;
+ mGroupManager = groupManager;
+ mGroupAlertTransferHelper = groupAlertTransferHelper;
+ mVisualStabilityManager = visualStabilityManager;
+ mDeviceProvisionedController = deviceProvisionedController;
+ mNavigationBarController = navigationBarController;
+ mAssistManager = assistManager;
+ mNotificationListener = notificationListener;
+ mConfigurationController = configurationController;
+ mStatusBarWindowController = statusBarWindowController;
+ mStatusBarWindowViewControllerBuilder = statusBarWindowViewControllerBuilder;
+ mNotifLog = notifLog;
+ mDozeServiceHost = dozeServiceHost;
+ mPowerManager = powerManager;
+ mDozeParameters = dozeParameters;
+ mScrimController = scrimController;
+ mLockscreenWallpaperLazy = lockscreenWallpaperLazy;
+ mDozeScrimController = dozeScrimController;
+ mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
+
+ mBubbleExpandListener =
+ (isExpanding, key) -> {
+ mEntryManager.updateNotifications("onBubbleExpandChanged");
+ updateScrimController();
+ };
+ }
+
@Override
public void start() {
- mGroupManager = Dependency.get(NotificationGroupManager.class);
- mGroupAlertTransferHelper = Dependency.get(NotificationGroupAlertTransferHelper.class);
- mVisualStabilityManager = Dependency.get(VisualStabilityManager.class);
- mNotificationLogger = Dependency.get(NotificationLogger.class);
- mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
- mNotificationListener = Dependency.get(NotificationListener.class);
mNotificationListener.registerAsSystemService();
- mNetworkController = Dependency.get(NetworkController.class);
- mUserSwitcherController = Dependency.get(UserSwitcherController.class);
- mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
mScreenLifecycle.addObserver(mScreenObserver);
- mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
- mBatteryController = Dependency.get(BatteryController.class);
- mAssistManager = Dependency.get(AssistManager.class);
mUiModeManager = mContext.getSystemService(UiModeManager.class);
- mLockscreenUserManager = Dependency.get(NotificationLockscreenUserManager.class);
- mGutsManager = Dependency.get(NotificationGutsManager.class);
- mMediaManager = Dependency.get(NotificationMediaManager.class);
- mEntryManager = Dependency.get(NotificationEntryManager.class);
mBypassHeadsUpNotifier.setUp(mEntryManager);
- mNotificationInterruptionStateProvider =
- Dependency.get(NotificationInterruptionStateProvider.class);
- mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class);
- mForegroundServiceController = Dependency.get(ForegroundServiceController.class);
- mAppOpsController = Dependency.get(AppOpsController.class);
- mZenController = Dependency.get(ZenModeController.class);
- mKeyguardViewMediator = getComponent(KeyguardViewMediator.class);
- mColorExtractor = Dependency.get(SysuiColorExtractor.class);
- mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
- mNavigationBarController = Dependency.get(NavigationBarController.class);
- mBubbleController = Dependency.get(BubbleController.class);
mBubbleController.setExpandListener(mBubbleExpandListener);
mActivityIntentHelper = new ActivityIntentHelper(mContext);
KeyguardSliceProvider sliceProvider = KeyguardSliceProvider.getAttachedInstance();
if (sliceProvider != null) {
sliceProvider.initDependencies(mMediaManager, mStatusBarStateController,
- mKeyguardBypassController, DozeParameters.getInstance(mContext));
+ mKeyguardBypassController, mDozeParameters);
} else {
Log.w(TAG, "Cannot init KeyguardSliceProvider dependencies");
}
@@ -687,7 +794,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mVibrateOnOpening = mContext.getResources().getBoolean(
R.bool.config_vibrateOnIconAnimation);
- mVibratorHelper = Dependency.get(VibratorHelper.class);
DateTimeView.setReceiverHandler(Dependency.get(Dependency.TIME_TICK_HANDLER));
putComponent(StatusBar.class, this);
@@ -700,8 +806,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mAccessibilityManager = (AccessibilityManager)
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
- mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
@@ -709,7 +813,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mRecents = getComponent(Recents.class);
mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- mFalsingManager = Dependency.get(FalsingManager.class);
// Connect in to the status bar manager service
mCommandQueue = getComponent(CommandQueue.class);
@@ -740,10 +843,22 @@ public class StatusBar extends SystemUI implements DemoMode,
// Set up the initial notification state. This needs to happen before CommandQueue.disable()
setUpPresenter();
- setSystemUiVisibility(mDisplayId, result.mSystemUiVisibility,
- result.mFullscreenStackSysUiVisibility, result.mDockedStackSysUiVisibility,
- 0xffffffff, result.mFullscreenStackBounds, result.mDockedStackBounds,
- result.mNavbarColorManagedByIme);
+ if ((result.mSystemUiVisibility & View.STATUS_BAR_TRANSIENT) != 0) {
+ showTransientUnchecked();
+ }
+ final int fullscreenAppearance = getAppearance(result.mFullscreenStackSysUiVisibility);
+ final int dockedAppearance = getAppearance(result.mDockedStackSysUiVisibility);
+ final AppearanceRegion[] appearanceRegions = result.mDockedStackBounds.isEmpty()
+ ? new AppearanceRegion[]{
+ new AppearanceRegion(fullscreenAppearance, result.mFullscreenStackBounds)}
+ : new AppearanceRegion[]{
+ new AppearanceRegion(fullscreenAppearance, result.mFullscreenStackBounds),
+ new AppearanceRegion(dockedAppearance, result.mDockedStackBounds)};
+ onSystemBarAppearanceChanged(mDisplayId, getAppearance(result.mSystemUiVisibility),
+ appearanceRegions, result.mNavbarColorManagedByIme);
+ mAppFullscreen = result.mAppFullscreen;
+ mAppImmersive = result.mAppImmersive;
+
// StatusBarManagerService has a back up of IME token and it's restored here.
setImeWindowStatus(mDisplayId, result.mImeToken, result.mImeWindowVis,
result.mImeBackDisposition, result.mShowImeSwitcher);
@@ -786,18 +901,20 @@ public class StatusBar extends SystemUI implements DemoMode,
mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController);
mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconController);
- mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
- mUnlockMethodCache.addListener(this);
+ mKeyguardStateController.addCallback(this);
startKeyguard();
mKeyguardUpdateMonitor.registerCallback(mUpdateCallback);
+ mDozeServiceHost.initialize(this, mNotificationIconAreaController,
+ mStatusBarWindowViewController, mStatusBarWindow, mStatusBarKeyguardViewManager,
+ mNotificationPanel, mAmbientIndicationContainer);
putComponent(DozeHost.class, mDozeServiceHost);
mScreenPinningRequest = new ScreenPinningRequest(mContext);
Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(this);
- Dependency.get(ConfigurationController.class).addCallback(this);
+ mConfigurationController.addCallback(this);
// set the initial view visibility
Dependency.get(InitController.class).addPostInitTask(this::updateAreThereNotifications);
@@ -817,8 +934,7 @@ public class StatusBar extends SystemUI implements DemoMode,
updateTheme();
inflateStatusBarWindow(context);
- mStatusBarWindow.setService(this);
- mStatusBarWindow.setBypassController(mKeyguardBypassController);
+ mStatusBarWindowViewController.setService(this);
mStatusBarWindow.setOnTouchListener(getStatusBarWindowTouchListener());
// TODO: Deal with the ugliness that comes from having some of the statusbar broken out
@@ -830,6 +946,8 @@ public class StatusBar extends SystemUI implements DemoMode,
NotificationListContainer notifListContainer = (NotificationListContainer) mStackScroller;
mNotificationLogger.setUpWithContainer(notifListContainer);
+ // TODO: make this injectable. Currently that would create a circular dependency between
+ // NotificationIconAreaController and StatusBar.
mNotificationIconAreaController = SystemUIFactory.getInstance()
.createNotificationIconAreaController(context, this,
mWakeUpCoordinator, mKeyguardBypassController,
@@ -882,9 +1000,9 @@ public class StatusBar extends SystemUI implements DemoMode,
mHeadsUpAppearanceController = new HeadsUpAppearanceController(
mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow,
mStatusBarStateController, mKeyguardBypassController,
- mWakeUpCoordinator);
+ mKeyguardStateController, mWakeUpCoordinator);
mHeadsUpAppearanceController.readFrom(oldController);
- mStatusBarWindow.setStatusBarView(mStatusBarView);
+ mStatusBarWindowViewController.setStatusBarView(mStatusBarView);
updateAreThereNotifications();
checkBarModes();
}).getFragmentManager()
@@ -892,10 +1010,9 @@ public class StatusBar extends SystemUI implements DemoMode,
.replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
CollapsedStatusBarFragment.TAG)
.commit();
- mIconController = Dependency.get(StatusBarIconController.class);
mHeadsUpManager.setUp(mStatusBarWindow, mGroupManager, this, mVisualStabilityManager);
- Dependency.get(ConfigurationController.class).addCallback(mHeadsUpManager);
+ mConfigurationController.addCallback(mHeadsUpManager);
mHeadsUpManager.addListener(this);
mHeadsUpManager.addListener(mNotificationPanel);
mHeadsUpManager.addListener(mGroupManager);
@@ -910,7 +1027,8 @@ public class StatusBar extends SystemUI implements DemoMode,
createNavigationBar(result);
if (ENABLE_LOCKSCREEN_WALLPAPER) {
- mLockscreenWallpaper = new LockscreenWallpaper(mContext, this, mHandler);
+ mLockscreenWallpaper = mLockscreenWallpaperLazy.get();
+ mLockscreenWallpaper.setHandler(mHandler);
}
mKeyguardIndicationController =
@@ -938,31 +1056,22 @@ public class StatusBar extends SystemUI implements DemoMode,
}
});
- mAutoHideController = Dependency.get(AutoHideController.class);
mAutoHideController.setStatusBar(this);
- mLightBarController = Dependency.get(LightBarController.class);
-
ScrimView scrimBehind = mStatusBarWindow.findViewById(R.id.scrim_behind);
ScrimView scrimInFront = mStatusBarWindow.findViewById(R.id.scrim_in_front);
ScrimView scrimForBubble = mStatusBarWindow.findViewById(R.id.scrim_for_bubble);
- mScrimController = SystemUIFactory.getInstance().createScrimController(
- scrimBehind, scrimInFront, scrimForBubble, mLockscreenWallpaper,
- (state, alpha, color) -> mLightBarController.setScrimState(state, alpha, color),
- scrimsVisible -> {
- if (mStatusBarWindowController != null) {
- mStatusBarWindowController.setScrimsVisibility(scrimsVisible);
- }
- if (mStatusBarWindow != null) {
- mStatusBarWindow.onScrimVisibilityChanged(scrimsVisible);
- }
- }, DozeParameters.getInstance(mContext),
- mContext.getSystemService(AlarmManager.class),
- mKeyguardMonitor);
+ mScrimController.setScrimVisibleListener(scrimsVisible -> {
+ mStatusBarWindowController.setScrimsVisibility(scrimsVisible);
+ if (mStatusBarWindow != null) {
+ mStatusBarWindowViewController.onScrimVisibilityChanged(scrimsVisible);
+ }
+ });
+ mScrimController.attachViews(scrimBehind, scrimInFront, scrimForBubble);
+
mNotificationPanel.initDependencies(this, mGroupManager, mNotificationShelf,
mHeadsUpManager, mNotificationIconAreaController, mScrimController);
- mDozeScrimController = new DozeScrimController(DozeParameters.getInstance(context));
BackDropView backdrop = mStatusBarWindow.findViewById(R.id.backdrop);
mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front),
@@ -977,7 +1086,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
mNotificationPanel.setLaunchAffordanceListener(
- mStatusBarWindow::onShowingLaunchAffordanceChanged);
+ mStatusBarWindowViewController::onShowingLaunchAffordanceChanged);
// Set up the quick settings tile panel
View container = mStatusBarWindow.findViewById(R.id.qs_frame);
@@ -1032,11 +1141,10 @@ public class StatusBar extends SystemUI implements DemoMode,
});
}
- PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- if (!pm.isScreenOn()) {
+ if (!mPowerManager.isScreenOn()) {
mBroadcastReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
}
- mGestureWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
+ mGestureWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
"GestureWakeLock");
mVibrator = mContext.getSystemService(Vibrator.class);
int[] pattern = mContext.getResources().getIntArray(
@@ -1047,11 +1155,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
// receive broadcasts
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG);
- context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
+ registerBroadcastReceiver();
IntentFilter demoFilter = new IntentFilter();
if (DEBUG_MEDIA_FAKE_ARTWORK) {
@@ -1072,6 +1176,15 @@ public class StatusBar extends SystemUI implements DemoMode,
ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f));
}
+ @VisibleForTesting
+ protected void registerBroadcastReceiver() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG);
+ mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, null, UserHandle.ALL);
+ }
+
protected QS createDefaultQSFragment() {
return FragmentHostManager.get(mStatusBarWindow).create(QSFragment.class);
}
@@ -1079,7 +1192,7 @@ public class StatusBar extends SystemUI implements DemoMode,
private void setUpPresenter() {
// Set up the initial notification state.
mActivityLaunchAnimator = new ActivityLaunchAnimator(
- mStatusBarWindow, this, mNotificationPanel,
+ mStatusBarWindowViewController, this, mNotificationPanel,
(NotificationListContainer) mStackScroller);
final NotificationRowBinderImpl rowBinder =
@@ -1087,12 +1200,13 @@ public class StatusBar extends SystemUI implements DemoMode,
mContext,
mAllowNotificationLongPress,
mKeyguardBypassController,
- mStatusBarStateController);
+ mStatusBarStateController,
+ mNotifLog);
mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel,
mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController,
mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController,
- mNotificationAlertingManager, rowBinder);
+ mNotificationAlertingManager, rowBinder, mKeyguardStateController);
mNotificationListController =
new NotificationListController(
@@ -1108,7 +1222,6 @@ public class StatusBar extends SystemUI implements DemoMode,
final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback =
(StatusBarRemoteInputCallback) Dependency.get(
NotificationRemoteInputManager.Callback.class);
- mShadeController = Dependency.get(ShadeController.class);
final ActivityStarter activityStarter = Dependency.get(ActivityStarter.class);
mNotificationActivityStarter = new StatusBarNotificationActivityStarter(mContext,
@@ -1116,7 +1229,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mHeadsUpManager, activityStarter, mActivityLaunchAnimator,
mBarService, mStatusBarStateController, mKeyguardManager, mDreamManager,
mRemoteInputManager, mStatusBarRemoteInputCallback, mGroupManager,
- mLockscreenUserManager, mShadeController, mKeyguardMonitor,
+ mLockscreenUserManager, this, mKeyguardStateController,
mNotificationInterruptionStateProvider, mMetricsLogger,
new LockPatternUtils(mContext), Dependency.get(MAIN_HANDLER),
Dependency.get(BG_HANDLER), mActivityIntentHelper, mBubbleController);
@@ -1125,10 +1238,14 @@ public class StatusBar extends SystemUI implements DemoMode,
mEntryManager.setRowBinder(rowBinder);
rowBinder.setNotificationClicker(new NotificationClicker(
- this, Dependency.get(BubbleController.class), mNotificationActivityStarter));
+ this, mBubbleController, mNotificationActivityStarter));
mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager);
mNotificationListController.bind();
+
+ if (mFeatureFlags.isNewNotifPipelineEnabled()) {
+ mNewNotifPipeline.get().initialize(mNotificationListener);
+ }
}
/**
@@ -1153,8 +1270,8 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void wakeUpIfDozing(long time, View where, String why) {
if (mDozing) {
- PowerManager pm = mContext.getSystemService(PowerManager.class);
- pm.wakeUp(time, PowerManager.WAKE_REASON_GESTURE, "com.android.systemui:" + why);
+ mPowerManager.wakeUp(
+ time, PowerManager.WAKE_REASON_GESTURE, "com.android.systemui:" + why);
mWakeUpComingFromTouch = true;
where.getLocationInWindow(mTmpInt2);
mWakeUpTouchLocation = new PointF(mTmpInt2[0] + where.getWidth() / 2,
@@ -1250,17 +1367,16 @@ public class StatusBar extends SystemUI implements DemoMode,
protected void inflateStatusBarWindow(Context context) {
mStatusBarWindow = (StatusBarWindowView) mInjectionInflater.injectable(
LayoutInflater.from(context)).inflate(R.layout.super_status_bar, null);
+ mStatusBarWindowViewController = mStatusBarWindowViewControllerBuilder
+ .setStatusBarWindowView(mStatusBarWindow)
+ .setShadeController(this)
+ .build();
}
protected void startKeyguard() {
Trace.beginSection("StatusBar#startKeyguard");
- KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
- mBiometricUnlockController = new BiometricUnlockController(mContext,
- mDozeScrimController, keyguardViewMediator,
- mScrimController, this, UnlockMethodCache.getInstance(mContext),
- new Handler(), mKeyguardUpdateMonitor, mKeyguardBypassController);
- putComponent(BiometricUnlockController.class, mBiometricUnlockController);
- mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
+ mBiometricUnlockController = mBiometricUnlockControllerLazy.get();
+ mStatusBarKeyguardViewManager = mKeyguardViewMediator.registerStatusBar(this,
getBouncerContainer(), mNotificationPanel, mBiometricUnlockController,
mStatusBarWindow.findViewById(R.id.lock_icon_container), mStackScroller,
mKeyguardBypassController);
@@ -1270,7 +1386,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mRemoteInputManager.getController().addCallback(mStatusBarKeyguardViewManager);
mDynamicPrivacyController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
- mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
+ mKeyguardViewMediatorCallback = mKeyguardViewMediator.getViewMediatorCallback();
mLightBarController.setBiometricUnlockController(mBiometricUnlockController);
mMediaManager.setBiometricUnlockController(mBiometricUnlockController);
Dependency.get(KeyguardDismissUtil.class).setDismissHandler(this::executeWhenUnlocked);
@@ -1366,15 +1482,19 @@ public class StatusBar extends SystemUI implements DemoMode,
return mZenController.areNotificationsHiddenInShade();
}
- public void requestNotificationUpdate() {
- mEntryManager.updateNotifications();
+ /**
+ * Request a notification update
+ * @param reason why we're requesting a notification update
+ */
+ public void requestNotificationUpdate(String reason) {
+ mEntryManager.updateNotifications(reason);
}
/**
* Asks {@link KeyguardUpdateMonitor} to run face auth.
*/
public void requestFaceAuth() {
- if (!mUnlockMethodCache.canSkipBouncer()) {
+ if (!mKeyguardStateController.canDismissLockScreen()) {
mKeyguardUpdateMonitor.requestFaceAuth();
}
}
@@ -1558,9 +1678,8 @@ public class StatusBar extends SystemUI implements DemoMode,
logStateToEventlog();
}
- @Override // UnlockMethodCache.OnUnlockMethodChangedListener
- public void onUnlockMethodStateChanged() {
- // Unlock method state changed. Notify KeguardMonitor
+ @Override
+ public void onUnlockedChanged() {
updateKeyguardState();
logStateToEventlog();
}
@@ -1609,11 +1728,11 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
- mEntryManager.updateNotifications();
+ mEntryManager.updateNotifications("onHeadsUpStateChanged");
if (isDozing() && isHeadsUp) {
entry.setPulseSuppressed(false);
mDozeServiceHost.fireNotificationPulse(entry);
- if (mPulsing) {
+ if (mDozeServiceHost.isPulsing()) {
mDozeScrimController.cancelPendingPulseTimeout();
}
}
@@ -1623,10 +1742,6 @@ public class StatusBar extends SystemUI implements DemoMode,
}
}
- public boolean isKeyguardCurrentlySecure() {
- return !mUnlockMethodCache.canSkipBouncer();
- }
-
public void setPanelExpanded(boolean isExpanded) {
mPanelExpanded = isExpanded;
updateHideIconsForBouncer(false /* animate */);
@@ -1649,7 +1764,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
public boolean isPulsing() {
- return mPulsing;
+ return mDozeServiceHost.isPulsing();
}
public boolean hideStatusBarIconsWhenExpanded() {
@@ -1761,6 +1876,16 @@ public class StatusBar extends SystemUI implements DemoMode,
return mPresenter;
}
+ @VisibleForTesting
+ void setBarStateForTest(int state) {
+ mState = state;
+ }
+
+ @VisibleForTesting
+ void setUserSetupForTest(boolean userSetup) {
+ mUserSetup = userSetup;
+ }
+
/**
* All changes to the status bar and notifications funnel through here and are batched.
*/
@@ -1793,7 +1918,7 @@ public class StatusBar extends SystemUI implements DemoMode,
public void maybeEscalateHeadsUp() {
mHeadsUpManager.getAllEntries().forEach(entry -> {
- final StatusBarNotification sbn = entry.notification;
+ final StatusBarNotification sbn = entry.getSbn();
final Notification notification = sbn.getNotification();
if (notification.fullScreenIntent != null) {
if (DEBUG) {
@@ -1818,8 +1943,8 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void handleSystemKey(int key) {
if (SPEW) Log.d(TAG, "handleNavigationKey: " + key);
- if (!mCommandQueue.panelsEnabled() || !mKeyguardMonitor.isDeviceInteractive()
- || mKeyguardMonitor.isShowing() && !mKeyguardMonitor.isOccluded()) {
+ if (!mCommandQueue.panelsEnabled() || !mKeyguardUpdateMonitor.isDeviceInteractive()
+ || mKeyguardStateController.isShowing() && !mKeyguardStateController.isOccluded()) {
return;
}
@@ -1948,7 +2073,7 @@ public class StatusBar extends SystemUI implements DemoMode,
// release focus immediately to kick off focus change transition
mStatusBarWindowController.setStatusBarFocusable(false);
- mStatusBarWindow.cancelExpandHelper();
+ mStatusBarWindowViewController.cancelExpandHelper();
mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor);
} else {
mBubbleController.collapseStack();
@@ -2128,49 +2253,104 @@ public class StatusBar extends SystemUI implements DemoMode,
}
}
- @Override // CommandQueue
- public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
- int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds,
- boolean navbarColorManagedByIme) {
+ @Override
+ public void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance,
+ AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) {
if (displayId != mDisplayId) {
return;
}
- final int oldVal = mSystemUiVisibility;
- final int newVal = (oldVal&~mask) | (vis&mask);
- final int diff = newVal ^ oldVal;
- if (DEBUG) Log.d(TAG, String.format(
- "setSystemUiVisibility displayId=%d vis=%s mask=%s oldVal=%s newVal=%s diff=%s",
- displayId, Integer.toHexString(vis), Integer.toHexString(mask),
- Integer.toHexString(oldVal), Integer.toHexString(newVal),
- Integer.toHexString(diff)));
- boolean sbModeChanged = false;
- if (diff != 0) {
- mSystemUiVisibility = newVal;
+ boolean barModeChanged = false;
+ final int diff = mAppearance ^ appearance;
+ if (mAppearance != appearance) {
+ mAppearance = appearance;
// update low profile
- if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
+ if ((diff & APPEARANCE_LOW_PROFILE_BARS) != 0) {
updateAreThereNotifications();
}
+ barModeChanged = updateBarMode(barMode(mTransientShown, appearance));
+ }
+ mLightBarController.onStatusBarAppearanceChanged(appearanceRegions, barModeChanged,
+ mStatusBarMode, navbarColorManagedByIme);
+ }
- // ready to unhide
- if ((vis & View.STATUS_BAR_UNHIDE) != 0) {
- mNoAnimationOnNextBarModeChange = true;
- }
+ @Override
+ public void showTransient(int displayId, @InternalInsetType int[] types) {
+ if (displayId != mDisplayId) {
+ return;
+ }
+ if (!containsType(types, TYPE_TOP_BAR)) {
+ return;
+ }
+ showTransientUnchecked();
+ }
- // update status bar mode
- final int sbMode = computeStatusBarMode(oldVal, newVal);
+ private void showTransientUnchecked() {
+ if (!mTransientShown) {
+ mTransientShown = true;
+ mNoAnimationOnNextBarModeChange = true;
+ handleTransientChanged();
+ }
+ }
- sbModeChanged = sbMode != -1;
- if (sbModeChanged && sbMode != mStatusBarMode) {
- mStatusBarMode = sbMode;
- checkBarModes();
- mAutoHideController.touchAutoHide();
- }
- mStatusBarStateController.setSystemUiVisibility(mSystemUiVisibility);
+ @Override
+ public void abortTransient(int displayId, @InternalInsetType int[] types) {
+ if (displayId != mDisplayId) {
+ return;
+ }
+ if (!containsType(types, TYPE_TOP_BAR)) {
+ return;
}
- mLightBarController.onSystemUiVisibilityChanged(fullscreenStackVis, dockedStackVis,
- mask, fullscreenStackBounds, dockedStackBounds, sbModeChanged, mStatusBarMode,
- navbarColorManagedByIme);
+ clearTransient();
+ }
+
+ void clearTransient() {
+ if (mTransientShown) {
+ mTransientShown = false;
+ handleTransientChanged();
+ }
+ }
+
+ private void handleTransientChanged() {
+ final int barMode = barMode(mTransientShown, mAppearance);
+ if (updateBarMode(barMode)) {
+ mLightBarController.onStatusBarModeChanged(barMode);
+ }
+ }
+
+ private boolean updateBarMode(int barMode) {
+ if (mStatusBarMode != barMode) {
+ mStatusBarMode = barMode;
+ checkBarModes();
+ mAutoHideController.touchAutoHide();
+ return true;
+ }
+ return false;
+ }
+
+ private static @TransitionMode int barMode(boolean isTransient, int appearance) {
+ final int lightsOutOpaque = APPEARANCE_LOW_PROFILE_BARS | APPEARANCE_OPAQUE_TOP_BAR;
+ if (isTransient) {
+ return MODE_SEMI_TRANSPARENT;
+ } else if ((appearance & lightsOutOpaque) == lightsOutOpaque) {
+ return MODE_LIGHTS_OUT;
+ } else if ((appearance & APPEARANCE_LOW_PROFILE_BARS) != 0) {
+ return MODE_LIGHTS_OUT_TRANSPARENT;
+ } else if ((appearance & APPEARANCE_OPAQUE_TOP_BAR) != 0) {
+ return MODE_OPAQUE;
+ } else {
+ return MODE_TRANSPARENT;
+ }
+ }
+
+ @Override
+ public void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) {
+ if (displayId != mDisplayId) {
+ return;
+ }
+ mAppFullscreen = isFullscreen;
+ mAppImmersive = isImmersive;
+ mStatusBarStateController.setFullscreenState(isFullscreen, isImmersive);
}
@Override
@@ -2201,40 +2381,10 @@ public class StatusBar extends SystemUI implements DemoMode,
setInteracting(StatusBarManager.WINDOW_NAVIGATION_BAR, running);
}
- protected @TransitionMode int computeStatusBarMode(int oldVal, int newVal) {
- return computeBarMode(oldVal, newVal);
- }
-
protected BarTransitions getStatusBarTransitions() {
return mStatusBarView.getBarTransitions();
}
- protected @TransitionMode int computeBarMode(int oldVis, int newVis) {
- final int oldMode = barMode(oldVis);
- final int newMode = barMode(newVis);
- if (oldMode == newMode) {
- return -1; // no mode change
- }
- return newMode;
- }
-
- private @TransitionMode int barMode(int vis) {
- int lightsOutTransparent = View.SYSTEM_UI_FLAG_LOW_PROFILE | View.STATUS_BAR_TRANSPARENT;
- if ((vis & View.STATUS_BAR_TRANSIENT) != 0) {
- return MODE_SEMI_TRANSPARENT;
- } else if ((vis & View.STATUS_BAR_TRANSLUCENT) != 0) {
- return MODE_TRANSLUCENT;
- } else if ((vis & lightsOutTransparent) == lightsOutTransparent) {
- return MODE_LIGHTS_OUT_TRANSPARENT;
- } else if ((vis & View.STATUS_BAR_TRANSPARENT) != 0) {
- return MODE_TRANSPARENT;
- } else if ((vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
- return MODE_LIGHTS_OUT;
- } else {
- return MODE_OPAQUE;
- }
- }
-
void checkBarModes() {
if (mDemoMode) return;
if (mStatusBarView != null) checkBarMode(mStatusBarMode, mStatusBarWindowState,
@@ -2290,20 +2440,16 @@ public class StatusBar extends SystemUI implements DemoMode,
/** Returns whether the top activity is in fullscreen mode. */
public boolean inFullscreenMode() {
- return 0
- != (mSystemUiVisibility
- & (View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION));
+ return mAppFullscreen;
}
/** Returns whether the top activity is in immersive mode. */
public boolean inImmersiveMode() {
- return 0
- != (mSystemUiVisibility
- & (View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY));
+ return mAppImmersive;
}
private boolean areLightsOn() {
- return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
+ return 0 == (mAppearance & APPEARANCE_LOW_PROFILE_BARS);
}
public static String viewInfo(View v) {
@@ -2339,8 +2485,8 @@ public class StatusBar extends SystemUI implements DemoMode,
dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
}
pw.println(" StatusBarWindowView: ");
- if (mStatusBarWindow != null) {
- mStatusBarWindow.dump(fd, pw, args);
+ if (mStatusBarWindowViewController != null) {
+ mStatusBarWindowViewController.dump(fd, pw, args);
}
pw.println(" mMediaManager: ");
@@ -2369,7 +2515,7 @@ public class StatusBar extends SystemUI implements DemoMode,
final boolean lightWpTheme = mContext.getThemeResId() == R.style.Theme_SystemUI_Light;
pw.println(" light wallpaper theme: " + lightWpTheme);
- DozeLog.dump(pw);
+ mDozeLog.dump(pw);
if (mBiometricUnlockController != null) {
mBiometricUnlockController.dump(pw);
@@ -2428,10 +2574,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mLightBarController.dump(fd, pw, args);
}
- if (mUnlockMethodCache != null) {
- mUnlockMethodCache.dump(pw);
- }
-
if (mKeyguardBypassController != null) {
mKeyguardBypassController.dump(pw);
}
@@ -2440,7 +2582,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mKeyguardUpdateMonitor.dump(fd, pw, args);
}
- Dependency.get(FalsingManager.class).dump(pw);
+ mFalsingManager.dump(pw);
FalsingLog.dump(pw);
pw.println("SharedPreferences:");
@@ -2456,7 +2598,6 @@ public class StatusBar extends SystemUI implements DemoMode,
public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
makeStatusBarView(result);
- mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
mStatusBarWindowController.add(mStatusBarWindow, getStatusBarHeight());
}
@@ -2684,9 +2825,9 @@ public class StatusBar extends SystemUI implements DemoMode,
public void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancelAction,
boolean afterKeyguardGone) {
if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_ASLEEP
- && mUnlockMethodCache.canSkipBouncer()
+ && mKeyguardStateController.canDismissLockScreen()
&& !mStatusBarStateController.leaveOpenOnKeyguardHide()
- && isPulsing()) {
+ && mDozeServiceHost.isPulsing()) {
// Reuse the biometric wake-and-unlock transition if we dismiss keyguard from a pulse.
// TODO: Factor this transition out of BiometricUnlockController.
mBiometricUnlockController.startWakeAndUnlock(
@@ -2700,7 +2841,6 @@ public class StatusBar extends SystemUI implements DemoMode,
}
}
- // SystemUIService notifies SystemBars of configuration changes, which then calls down here
@Override
public void onConfigChanged(Configuration newConfig) {
updateResources();
@@ -2827,14 +2967,14 @@ public class StatusBar extends SystemUI implements DemoMode,
boolean isShowing = mStatusBarKeyguardViewManager.isShowing();
boolean isOccluded = mStatusBarKeyguardViewManager.isOccluded();
boolean isBouncerShowing = mStatusBarKeyguardViewManager.isBouncerShowing();
- boolean isSecure = mUnlockMethodCache.isMethodSecure();
- boolean canSkipBouncer = mUnlockMethodCache.canSkipBouncer();
+ boolean isSecure = mKeyguardStateController.isMethodSecure();
+ boolean unlocked = mKeyguardStateController.canDismissLockScreen();
int stateFingerprint = getLoggingFingerprint(mState,
isShowing,
isOccluded,
isBouncerShowing,
isSecure,
- canSkipBouncer);
+ unlocked);
if (stateFingerprint != mLastLoggedStateFingerprint) {
if (mStatusBarStateLog == null) {
mStatusBarStateLog = new LogMaker(MetricsEvent.VIEW_UNKNOWN);
@@ -2848,7 +2988,7 @@ public class StatusBar extends SystemUI implements DemoMode,
isOccluded ? 1 : 0,
isBouncerShowing ? 1 : 0,
isSecure ? 1 : 0,
- canSkipBouncer ? 1 : 0);
+ unlocked ? 1 : 0);
mLastLoggedStateFingerprint = stateFingerprint;
}
}
@@ -3018,7 +3158,7 @@ public class StatusBar extends SystemUI implements DemoMode,
return mState == StatusBarState.FULLSCREEN_USER_SWITCHER;
}
- private boolean updateIsKeyguard() {
+ boolean updateIsKeyguard() {
boolean wakeAndUnlocking = mBiometricUnlockController.getMode()
== BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
@@ -3026,8 +3166,8 @@ public class StatusBar extends SystemUI implements DemoMode,
// there's no surface we can show to the user. Note that the device goes fully interactive
// late in the transition, so we also allow the device to start dozing once the screen has
// turned off fully.
- boolean keyguardForDozing = mDozingRequested &&
- (!mDeviceInteractive || isGoingToSleep() && (isScreenFullyOff() || mIsKeyguard));
+ boolean keyguardForDozing = mDozeServiceHost.getDozingRequested()
+ && (!mDeviceInteractive || isGoingToSleep() && (isScreenFullyOff() || mIsKeyguard));
boolean shouldBeKeyguard = (mStatusBarStateController.isKeyguardRequested()
|| keyguardForDozing) && !wakeAndUnlocking;
if (keyguardForDozing) {
@@ -3048,7 +3188,7 @@ public class StatusBar extends SystemUI implements DemoMode,
public void showKeyguardImpl() {
mIsKeyguard = true;
- if (mKeyguardMonitor.isLaunchTransitionFadingAway()) {
+ if (mKeyguardStateController.isLaunchTransitionFadingAway()) {
mNotificationPanel.animate().cancel();
onLaunchTransitionFadingEnded();
}
@@ -3080,7 +3220,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mNotificationPanel.onAffordanceLaunchEnded();
releaseGestureWakeLock();
runLaunchTransitionEndRunnable();
- mKeyguardMonitor.setLaunchTransitionFadingAway(false);
+ mKeyguardStateController.setLaunchTransitionFadingAway(false);
mPresenter.updateMediaMetaData(true /* metaDataChanged */, true);
}
@@ -3105,7 +3245,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
mLaunchTransitionEndRunnable = endRunnable;
Runnable hideRunnable = () -> {
- mKeyguardMonitor.setLaunchTransitionFadingAway(true);
+ mKeyguardStateController.setLaunchTransitionFadingAway(true);
if (beforeFading != null) {
beforeFading.run();
}
@@ -3198,7 +3338,7 @@ public class StatusBar extends SystemUI implements DemoMode,
if (!mStatusBarStateController.isKeyguardRequested()) {
mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
}
- long delay = mKeyguardMonitor.calculateGoingToFullShadeDelay();
+ long delay = mKeyguardStateController.calculateGoingToFullShadeDelay();
mNotificationPanel.animateToFullShade(delay);
if (mDraggedDownEntry != null) {
mDraggedDownEntry.setUserLocked(false);
@@ -3240,7 +3380,7 @@ public class StatusBar extends SystemUI implements DemoMode,
public void keyguardGoingAway() {
// Treat Keyguard exit animation as an app transition to achieve nice transition for status
// bar.
- mKeyguardMonitor.notifyKeyguardGoingAway(true);
+ mKeyguardStateController.notifyKeyguardGoingAway(true);
mCommandQueue.appTransitionPending(mDisplayId, true /* forced */);
}
@@ -3260,14 +3400,14 @@ public class StatusBar extends SystemUI implements DemoMode,
mCommandQueue.appTransitionStarting(mDisplayId,
startTime - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION,
LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
- mKeyguardMonitor.notifyKeyguardFadingAway(delay, fadeoutDuration, isBypassFading);
+ mKeyguardStateController.notifyKeyguardFadingAway(delay, fadeoutDuration, isBypassFading);
}
/**
* Notifies that the Keyguard fading away animation is done.
*/
public void finishKeyguardFadingAway() {
- mKeyguardMonitor.notifyKeyguardDoneFading();
+ mKeyguardStateController.notifyKeyguardDoneFading();
mScrimController.setExpansionAffectsAlpha(true);
}
@@ -3282,7 +3422,7 @@ public class StatusBar extends SystemUI implements DemoMode,
final int themeResId = lockDarkText ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI;
if (mContext.getThemeResId() != themeResId) {
mContext.setTheme(themeResId);
- Dependency.get(ConfigurationController.class).notifyThemeChanged();
+ mConfigurationController.notifyThemeChanged();
}
}
@@ -3443,7 +3583,7 @@ public class StatusBar extends SystemUI implements DemoMode,
public void onStateChanged(int newState) {
mState = newState;
updateReportRejectedTouchVisibility();
- updateDozing();
+ mDozeServiceHost.updateDozing();
updateTheme();
mNavigationBarController.touchAutoDim(mDisplayId);
Trace.beginSection("StatusBar#updateKeyguardState");
@@ -3481,40 +3621,25 @@ public class StatusBar extends SystemUI implements DemoMode,
public void onDozingChanged(boolean isDozing) {
Trace.beginSection("StatusBar#updateDozing");
mDozing = isDozing;
+ mDozeServiceHost.setDozing(mDozing);
// Collapse the notification panel if open
- boolean dozingAnimated = mDozingRequested
- && DozeParameters.getInstance(mContext).shouldControlScreenOff();
+ boolean dozingAnimated = mDozeServiceHost.getDozingRequested()
+ && mDozeParameters.shouldControlScreenOff();
mNotificationPanel.resetViews(dozingAnimated);
updateQsExpansionEnabled();
mKeyguardViewMediator.setDozing(mDozing);
- mEntryManager.updateNotifications();
- updateDozingState();
+ mEntryManager.updateNotifications("onDozingChanged");
+ mDozeServiceHost.updateDozing();
updateScrimController();
updateReportRejectedTouchVisibility();
Trace.endSection();
}
- private void updateDozing() {
- // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked.
- boolean dozing = mDozingRequested && mState == StatusBarState.KEYGUARD
- || mBiometricUnlockController.getMode()
- == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
- // When in wake-and-unlock we may not have received a change to mState
- // but we still should not be dozing, manually set to false.
- if (mBiometricUnlockController.getMode() ==
- BiometricUnlockController.MODE_WAKE_AND_UNLOCK) {
- dozing = false;
- }
-
- mStatusBarStateController.setIsDozing(dozing);
- }
-
private void updateKeyguardState() {
- mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
- mUnlockMethodCache.isMethodSecure(),
+ mKeyguardStateController.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
mStatusBarKeyguardViewManager.isOccluded());
}
@@ -3562,7 +3687,7 @@ public class StatusBar extends SystemUI implements DemoMode,
public void onTrackingStopped(boolean expand) {
if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
- if (!expand && !mUnlockMethodCache.canSkipBouncer()) {
+ if (!expand && !mKeyguardStateController.canDismissLockScreen()) {
showBouncer(false /* scrimmed */);
}
}
@@ -3602,9 +3727,7 @@ public class StatusBar extends SystemUI implements DemoMode,
// Indicate that the group expansion is changing at this time -- this way the group
// and children backgrounds / divider animations will look correct.
entry.setGroupExpansionChanging(true);
- if (entry.notification != null) {
- userId = entry.notification.getUserId();
- }
+ userId = entry.getSbn().getUserId();
}
boolean fullShadeNeedsBouncer = !mLockscreenUserManager.
userAllowsPrivateNotificationsInPublic(mLockscreenUserManager.getCurrentUserId())
@@ -3640,7 +3763,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mBouncerShowing = bouncerShowing;
mKeyguardBypassController.setBouncerShowing(bouncerShowing);
mPulseExpansionHandler.setBouncerShowing(bouncerShowing);
- mStatusBarWindow.setBouncerShowingScrimmed(isBouncerShowingScrimmed());
+ mStatusBarWindowViewController.setBouncerShowingScrimmed(isBouncerShowingScrimmed());
if (mStatusBarView != null) mStatusBarView.setBouncerShowing(bouncerShowing);
updateHideIconsForBouncer(true /* animate */);
mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
@@ -3655,7 +3778,7 @@ public class StatusBar extends SystemUI implements DemoMode,
*/
public void collapseShade() {
if (mNotificationPanel.isTracking()) {
- mStatusBarWindow.cancelCurrentTouch();
+ mStatusBarWindowViewController.cancelCurrentTouch();
}
if (mPanelExpanded && mState == StatusBarState.SHADE) {
animateCollapsePanels();
@@ -3676,7 +3799,7 @@ public class StatusBar extends SystemUI implements DemoMode,
updateVisibleToUser();
updateNotificationPanelTouchState();
- mStatusBarWindow.cancelCurrentTouch();
+ mStatusBarWindowViewController.cancelCurrentTouch();
if (mLaunchCameraOnFinishedGoingToSleep) {
mLaunchCameraOnFinishedGoingToSleep = false;
@@ -3739,10 +3862,11 @@ public class StatusBar extends SystemUI implements DemoMode,
* collapse the panel after we expanded it, and thus we would end up with a blank
* Keyguard.
*/
- private void updateNotificationPanelTouchState() {
+ void updateNotificationPanelTouchState() {
boolean goingToSleepWithoutAnimation = isGoingToSleep()
- && !DozeParameters.getInstance(mContext).shouldControlScreenOff();
- boolean disabled = (!mDeviceInteractive && !mPulsing) || goingToSleepWithoutAnimation;
+ && !mDozeParameters.shouldControlScreenOff();
+ boolean disabled = (!mDeviceInteractive && !mDozeServiceHost.isPulsing())
+ || goingToSleepWithoutAnimation;
mNotificationPanel.setTouchAndAnimationDisabled(disabled);
mNotificationIconAreaController.setAnimationsEnabled(!disabled);
}
@@ -3787,7 +3911,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void showScreenPinningRequest(int taskId) {
- if (mKeyguardMonitor.isShowing()) {
+ if (mKeyguardStateController.isShowing()) {
// Don't allow apps to trigger this from keyguard.
return;
}
@@ -3830,8 +3954,7 @@ public class StatusBar extends SystemUI implements DemoMode,
return;
}
if (!mDeviceInteractive) {
- PowerManager pm = mContext.getSystemService(PowerManager.class);
- pm.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_CAMERA_LAUNCH,
+ mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_CAMERA_LAUNCH,
"com.android.systemui:CAMERA_GESTURE");
}
vibrateForCameraGesture();
@@ -3870,20 +3993,17 @@ public class StatusBar extends SystemUI implements DemoMode,
}
boolean isCameraAllowedByAdmin() {
- // TODO(b/140060745)
- return whitelistIpcs(() -> {
- if (mDevicePolicyManager.getCameraDisabled(null,
- mLockscreenUserManager.getCurrentUserId())) {
- return false;
- } else if (mStatusBarKeyguardViewManager == null
- || (isKeyguardShowing() && isKeyguardSecure())) {
- // Check if the admin has disabled the camera specifically for the keyguard
- return (mDevicePolicyManager.getKeyguardDisabledFeatures(null,
- mLockscreenUserManager.getCurrentUserId())
- & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) == 0;
- }
- return true;
- });
+ if (mDevicePolicyManager.getCameraDisabled(null,
+ mLockscreenUserManager.getCurrentUserId())) {
+ return false;
+ } else if (mStatusBarKeyguardViewManager == null
+ || (isKeyguardShowing() && isKeyguardSecure())) {
+ // Check if the admin has disabled the camera specifically for the keyguard
+ return (mDevicePolicyManager.getKeyguardDisabledFeatures(null,
+ mLockscreenUserManager.getCurrentUserId())
+ & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) == 0;
+ }
+ return true;
}
private boolean isGoingToSleep() {
@@ -3897,9 +4017,10 @@ public class StatusBar extends SystemUI implements DemoMode,
}
public void notifyBiometricAuthModeChanged() {
- updateDozing();
+ mDozeServiceHost.updateDozing();
updateScrimController();
- mStatusBarWindow.onBiometricAuthModeChanged(mBiometricUnlockController.isWakeAndUnlock(),
+ mStatusBarWindowViewController.onBiometricAuthModeChanged(
+ mBiometricUnlockController.isWakeAndUnlock(),
mBiometricUnlockController.isBiometricUnlock());
}
@@ -3910,7 +4031,7 @@ public class StatusBar extends SystemUI implements DemoMode,
// We don't want to end up in KEYGUARD state when we're unlocking with
// fingerprint from doze. We should cross fade directly from black.
boolean unlocking = mBiometricUnlockController.isWakeAndUnlock()
- || mKeyguardMonitor.isKeyguardFadingAway();
+ || mKeyguardStateController.isKeyguardFadingAway();
// Do not animate the scrim expansion when triggered by the fingerprint sensor.
mScrimController.setExpansionAffectsAlpha(
@@ -3932,9 +4053,16 @@ public class StatusBar extends SystemUI implements DemoMode,
mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
} else if (mBrightnessMirrorVisible) {
mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR);
- } else if (isPulsing()) {
+ } else if (mDozeServiceHost.isPulsing()) {
mScrimController.transitionTo(ScrimState.PULSING,
mDozeScrimController.getScrimCallback());
+ } else if (mDozeServiceHost.hasPendingScreenOffCallback()) {
+ mScrimController.transitionTo(ScrimState.OFF, new ScrimController.Callback() {
+ @Override
+ public void onFinished() {
+ mDozeServiceHost.executePendingScreenOffCallback();
+ }
+ });
} else if (mDozing && !unlocking) {
mScrimController.transitionTo(ScrimState.AOD);
} else if (mIsKeyguard && !unlocking) {
@@ -3955,263 +4083,8 @@ public class StatusBar extends SystemUI implements DemoMode,
return mStatusBarKeyguardViewManager.isShowing();
}
- @VisibleForTesting
- final class DozeServiceHost implements DozeHost {
- private final ArrayList<Callback> mCallbacks = new ArrayList<>();
- private boolean mAnimateWakeup;
- private boolean mAnimateScreenOff;
- private boolean mIgnoreTouchWhilePulsing;
- @VisibleForTesting
- boolean mWakeLockScreenPerformsAuth = SystemProperties.getBoolean(
- "persist.sysui.wake_performs_auth", true);
-
- @Override
- public String toString() {
- return "PSB.DozeServiceHost[mCallbacks=" + mCallbacks.size() + "]";
- }
-
- public void firePowerSaveChanged(boolean active) {
- for (Callback callback : mCallbacks) {
- callback.onPowerSaveChanged(active);
- }
- }
-
- public void fireNotificationPulse(NotificationEntry entry) {
- Runnable pulseSupressedListener = () -> {
- entry.setPulseSuppressed(true);
- mNotificationIconAreaController.updateAodNotificationIcons();
- };
- for (Callback callback : mCallbacks) {
- callback.onNotificationAlerted(pulseSupressedListener);
- }
- }
-
- @Override
- public void addCallback(@NonNull Callback callback) {
- mCallbacks.add(callback);
- }
-
- @Override
- public void removeCallback(@NonNull Callback callback) {
- mCallbacks.remove(callback);
- }
-
- @Override
- public void startDozing() {
- if (!mDozingRequested) {
- mDozingRequested = true;
- DozeLog.traceDozing(mContext, mDozing);
- updateDozing();
- updateIsKeyguard();
- }else{
- mDozingRequested = true;
- }
- }
-
- @Override
- public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) {
- if (reason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS) {
- mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
- "com.android.systemui:LONG_PRESS");
- startAssist(new Bundle());
- return;
- }
-
- if (reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
- mScrimController.setWakeLockScreenSensorActive(true);
- }
-
- if (reason == DozeLog.PULSE_REASON_DOCKING && mStatusBarWindow != null) {
- mStatusBarWindow.suppressWakeUpGesture(true);
- }
-
- boolean passiveAuthInterrupt = reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN
- && mWakeLockScreenPerformsAuth;
- // Set the state to pulsing, so ScrimController will know what to do once we ask it to
- // execute the transition. The pulse callback will then be invoked when the scrims
- // are black, indicating that StatusBar is ready to present the rest of the UI.
- mPulsing = true;
- mDozeScrimController.pulse(new PulseCallback() {
- @Override
- public void onPulseStarted() {
- callback.onPulseStarted();
- updateNotificationPanelTouchState();
- setPulsing(true);
- }
-
- @Override
- public void onPulseFinished() {
- mPulsing = false;
- callback.onPulseFinished();
- updateNotificationPanelTouchState();
- mScrimController.setWakeLockScreenSensorActive(false);
- if (mStatusBarWindow != null) {
- mStatusBarWindow.suppressWakeUpGesture(false);
- }
- setPulsing(false);
- }
-
- private void setPulsing(boolean pulsing) {
- mStatusBarStateController.setPulsing(pulsing);
- mStatusBarKeyguardViewManager.setPulsing(pulsing);
- mKeyguardViewMediator.setPulsing(pulsing);
- mNotificationPanel.setPulsing(pulsing);
- mVisualStabilityManager.setPulsing(pulsing);
- mStatusBarWindow.setPulsing(pulsing);
- mIgnoreTouchWhilePulsing = false;
- if (mKeyguardUpdateMonitor != null && passiveAuthInterrupt) {
- mKeyguardUpdateMonitor.onAuthInterruptDetected(pulsing /* active */);
- }
- updateScrimController();
- mPulseExpansionHandler.setPulsing(pulsing);
- mWakeUpCoordinator.setPulsing(pulsing);
- }
- }, reason);
- // DozeScrimController is in pulse state, now let's ask ScrimController to start
- // pulsing and draw the black frame, if necessary.
- updateScrimController();
- }
-
- @Override
- public void stopDozing() {
- if (mDozingRequested) {
- mDozingRequested = false;
- DozeLog.traceDozing(mContext, mDozing);
- updateDozing();
- }
- }
-
- @Override
- public void onIgnoreTouchWhilePulsing(boolean ignore) {
- if (ignore != mIgnoreTouchWhilePulsing) {
- DozeLog.tracePulseTouchDisabledByProx(mContext, ignore);
- }
- mIgnoreTouchWhilePulsing = ignore;
- if (isDozing() && ignore) {
- mStatusBarWindow.cancelCurrentTouch();
- }
- }
-
- @Override
- public void dozeTimeTick() {
- mNotificationPanel.dozeTimeTick();
- if (mAmbientIndicationContainer instanceof DozeReceiver) {
- ((DozeReceiver) mAmbientIndicationContainer).dozeTimeTick();
- }
- }
-
- @Override
- public boolean isPowerSaveActive() {
- return mBatteryController.isAodPowerSave();
- }
-
- @Override
- public boolean isPulsingBlocked() {
- return mBiometricUnlockController.getMode()
- == BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
- }
-
- @Override
- public boolean isProvisioned() {
- return mDeviceProvisionedController.isDeviceProvisioned()
- && mDeviceProvisionedController.isCurrentUserSetup();
- }
-
- @Override
- public boolean isBlockingDoze() {
- if (mBiometricUnlockController.hasPendingAuthentication()) {
- Log.i(TAG, "Blocking AOD because fingerprint has authenticated");
- return true;
- }
- return false;
- }
-
- @Override
- public void extendPulse(int reason) {
- if (reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
- mScrimController.setWakeLockScreenSensorActive(true);
- }
- if (mDozeScrimController.isPulsing() && mHeadsUpManager.hasNotifications()) {
- mHeadsUpManager.extendHeadsUp();
- } else {
- mDozeScrimController.extendPulse();
- }
- }
-
- @Override
- public void stopPulsing() {
- if (mDozeScrimController.isPulsing()) {
- mDozeScrimController.pulseOutNow();
- }
- }
-
- @Override
- public void setAnimateWakeup(boolean animateWakeup) {
- if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_AWAKE
- || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_WAKING) {
- // Too late to change the wakeup animation.
- return;
- }
- mAnimateWakeup = animateWakeup;
- }
-
- @Override
- public void setAnimateScreenOff(boolean animateScreenOff) {
- mAnimateScreenOff = animateScreenOff;
- }
-
- @Override
- public void onSlpiTap(float screenX, float screenY) {
- if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null
- && mAmbientIndicationContainer.getVisibility() == View.VISIBLE) {
- mAmbientIndicationContainer.getLocationOnScreen(mTmpInt2);
- float viewX = screenX - mTmpInt2[0];
- float viewY = screenY - mTmpInt2[1];
- if (0 <= viewX && viewX <= mAmbientIndicationContainer.getWidth()
- && 0 <= viewY && viewY <= mAmbientIndicationContainer.getHeight()) {
- dispatchTap(mAmbientIndicationContainer, viewX, viewY);
- }
- }
- }
-
- @Override
- public void setDozeScreenBrightness(int value) {
- mStatusBarWindowController.setDozeScreenBrightness(value);
- }
-
- @Override
- public void setAodDimmingScrim(float scrimOpacity) {
- mScrimController.setAodFrontScrimAlpha(scrimOpacity);
- }
-
- @Override
- public void prepareForGentleWakeUp() {
- mScrimController.prepareForGentleWakeUp();
- }
-
- private void dispatchTap(View view, float x, float y) {
- long now = SystemClock.elapsedRealtime();
- dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_DOWN);
- dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_UP);
- }
-
- private void dispatchTouchEvent(View view, float x, float y, long now, int action) {
- MotionEvent ev = MotionEvent.obtain(now, now, action, x, y, 0 /* meta */);
- view.dispatchTouchEvent(ev);
- ev.recycle();
- }
-
- private boolean shouldAnimateWakeup() {
- return mAnimateWakeup;
- }
-
- public boolean shouldAnimateScreenOff() {
- return mAnimateScreenOff;
- }
- }
-
public boolean shouldIgnoreTouch() {
- return isDozing() && mDozeServiceHost.mIgnoreTouchWhilePulsing;
+ return isDozing() && mDozeServiceHost.getIgnoreTouchWhilePulsing();
}
// Begin Extra BaseStatusBar methods.
@@ -4222,12 +4095,11 @@ public class StatusBar extends SystemUI implements DemoMode,
// all notifications
protected ViewGroup mStackScroller;
- protected NotificationGroupManager mGroupManager;
-
- protected NotificationGroupAlertTransferHelper mGroupAlertTransferHelper;
+ private final NotificationGroupManager mGroupManager;
+ private final NotificationGroupAlertTransferHelper mGroupAlertTransferHelper;
// handling reordering
- protected VisualStabilityManager mVisualStabilityManager;
+ private final VisualStabilityManager mVisualStabilityManager;
protected AccessibilityManager mAccessibilityManager;
@@ -4239,14 +4111,13 @@ public class StatusBar extends SystemUI implements DemoMode,
private boolean mVisibleToUser;
protected DevicePolicyManager mDevicePolicyManager;
- protected PowerManager mPowerManager;
+ private final PowerManager mPowerManager;
protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
protected KeyguardManager mKeyguardManager;
- private DeviceProvisionedController mDeviceProvisionedController
- = Dependency.get(DeviceProvisionedController.class);
+ private final DeviceProvisionedController mDeviceProvisionedController;
- protected NavigationBarController mNavigationBarController;
+ private final NavigationBarController mNavigationBarController;
// UI-specific methods
@@ -4262,7 +4133,7 @@ public class StatusBar extends SystemUI implements DemoMode,
protected NotificationShelf mNotificationShelf;
protected EmptyShadeView mEmptyShadeView;
- protected AssistManager mAssistManager;
+ private final AssistManager mAssistManager;
public boolean isDeviceInteractive() {
return mDeviceInteractive;
@@ -4321,7 +4192,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
}
- protected NotificationListener mNotificationListener;
+ private final NotificationListener mNotificationListener;
public void setNotificationSnoozed(StatusBarNotification sbn, SnoozeOption snoozeOption) {
if (snoozeOption.getSnoozeCriterion() != null) {
@@ -4457,8 +4328,7 @@ public class StatusBar extends SystemUI implements DemoMode,
executeActionDismissingKeyguard(() -> {
try {
intent.send(null, 0, null, null, null, null, getActivityOptions(
- mActivityLaunchAnimator.getLaunchAnimation(associatedView,
- mShadeController.isOccluded())));
+ mActivityLaunchAnimator.getLaunchAnimation(associatedView, isOccluded())));
} catch (PendingIntent.CanceledException e) {
// the stack trace isn't very helpful here.
// Just log the exception message.
@@ -4546,7 +4416,7 @@ public class StatusBar extends SystemUI implements DemoMode,
*/
public void onBouncerPreHideAnimation() {
mNotificationPanel.onBouncerPreHideAnimation();
- mStatusBarWindow.onBouncerPreHideAnimation();
+ mStatusBarWindowViewController.onBouncerPreHideAnimation();
}
/**
@@ -4611,8 +4481,8 @@ public class StatusBar extends SystemUI implements DemoMode,
void createStatusBar(StatusBar statusbar);
}
- public @TransitionMode int getStatusBarMode() {
- return mStatusBarMode;
+ boolean isTransientShown() {
+ return mTransientShown;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index bb8ba055276a..8683586326d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -33,6 +33,8 @@ import android.view.ViewGroup;
import android.view.ViewRootImpl;
import android.view.WindowManagerGlobal;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -56,8 +58,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback;
import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
-import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -161,12 +162,13 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private boolean mLastLockVisible;
private OnDismissAction mAfterKeyguardGoneAction;
+ private Runnable mKeyguardGoneCancelAction;
private final ArrayList<Runnable> mAfterKeyguardGoneRunnables = new ArrayList<>();
// Dismiss action to be launched when we stop dozing or the keyguard is gone.
private DismissWithActionRequest mPendingWakeupAction;
- private final KeyguardMonitorImpl mKeyguardMonitor =
- (KeyguardMonitorImpl) Dependency.get(KeyguardMonitor.class);
+ private final KeyguardStateController mKeyguardStateController = Dependency.get(
+ KeyguardStateController.class);
private final NotificationMediaManager mMediaManager =
Dependency.get(NotificationMediaManager.class);
private final SysuiStatusBarStateController mStatusBarStateController =
@@ -221,7 +223,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mBiometricUnlockController = biometricUnlockController;
mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry,
- mExpansionCallback, falsingManager, bypassController);
+ mExpansionCallback, mKeyguardStateController, falsingManager, bypassController);
mNotificationPanelView = notificationPanelView;
notificationPanelView.addExpansionListener(this);
mBypassController = bypassController;
@@ -245,7 +247,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mBouncer.setExpansion(expansion);
}
if (expansion != KeyguardBouncer.EXPANSION_HIDDEN && tracking
- && mStatusBar.isKeyguardCurrentlySecure()
+ && !mKeyguardStateController.canDismissLockScreen()
&& !mBouncer.isShowing() && !mBouncer.isAnimatingAway()) {
mBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */);
}
@@ -269,7 +271,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
boolean keyguardWithoutQs = mStatusBarStateController.getState() == StatusBarState.KEYGUARD
&& !mNotificationPanelView.isQsExpanded();
boolean lockVisible = (mBouncer.isShowing() || keyguardWithoutQs)
- && !mBouncer.isAnimatingAway() && !mKeyguardMonitor.isKeyguardFadingAway();
+ && !mBouncer.isAnimatingAway() && !mKeyguardStateController.isKeyguardFadingAway();
if (mLastLockVisible != lockVisible) {
mLastLockVisible = lockVisible;
@@ -299,8 +301,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
public void show(Bundle options) {
mShowing = true;
mStatusBarWindowController.setKeyguardShowing(true);
- mKeyguardMonitor.notifyKeyguardState(
- mShowing, mKeyguardMonitor.isSecure(), mKeyguardMonitor.isOccluded());
+ mKeyguardStateController.notifyKeyguardState(mShowing,
+ mKeyguardStateController.isOccluded());
reset(true /* hideBouncerWhenShowing */);
StatsLog.write(StatsLog.KEYGUARD_STATE_CHANGED,
StatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
@@ -329,10 +331,20 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
return false;
}
- private void hideBouncer(boolean destroyView) {
+ @VisibleForTesting
+ void hideBouncer(boolean destroyView) {
if (mBouncer == null) {
return;
}
+ if (mShowing) {
+ // If we were showing the bouncer and then aborting, we need to also clear out any
+ // potential actions unless we actually unlocked.
+ mAfterKeyguardGoneAction = null;
+ if (mKeyguardGoneCancelAction != null) {
+ mKeyguardGoneCancelAction.run();
+ mKeyguardGoneCancelAction = null;
+ }
+ }
mBouncer.hide(destroyView);
cancelPendingWakeupAction();
}
@@ -365,6 +377,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mBouncer.showWithDismissAction(r, cancelAction);
} else {
mAfterKeyguardGoneAction = r;
+ mKeyguardGoneCancelAction = cancelAction;
mBouncer.show(false /* resetSecuritySelection */);
}
}
@@ -530,8 +543,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
*/
public void hide(long startTime, long fadeoutDuration) {
mShowing = false;
- mKeyguardMonitor.notifyKeyguardState(
- mShowing, mKeyguardMonitor.isSecure(), mKeyguardMonitor.isOccluded());
+ mKeyguardStateController.notifyKeyguardState(mShowing,
+ mKeyguardStateController.isOccluded());
launchPendingWakeupAction();
if (Dependency.get(KeyguardUpdateMonitor.class).needsSlowUnlockTransition()) {
@@ -672,6 +685,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mAfterKeyguardGoneAction.onDismiss();
mAfterKeyguardGoneAction = null;
}
+ mKeyguardGoneCancelAction = null;
for (int i = 0; i < mAfterKeyguardGoneRunnables.size(); i++) {
mAfterKeyguardGoneRunnables.get(i).run();
}
@@ -739,8 +753,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
private long getNavBarShowDelay() {
- if (mKeyguardMonitor.isKeyguardFadingAway()) {
- return mKeyguardMonitor.getKeyguardFadingAwayDelay();
+ if (mKeyguardStateController.isKeyguardFadingAway()) {
+ return mKeyguardStateController.getKeyguardFadingAwayDelay();
} else if (mBouncer.isShowing()) {
return NAV_BAR_SHOW_DELAY_BOUNCER;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 320243b3ee22..64a45e16d749 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -68,7 +68,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
/**
* Status bar implementation of {@link NotificationActivityStarter}.
@@ -84,7 +84,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
private final NotificationRemoteInputManager mRemoteInputManager;
private final NotificationLockscreenUserManager mLockscreenUserManager;
private final ShadeController mShadeController;
- private final KeyguardMonitor mKeyguardMonitor;
+ private final KeyguardStateController mKeyguardStateController;
private final ActivityStarter mActivityStarter;
private final NotificationEntryManager mEntryManager;
private final StatusBarStateController mStatusBarStateController;
@@ -125,7 +125,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
NotificationGroupManager groupManager,
NotificationLockscreenUserManager lockscreenUserManager,
ShadeController shadeController,
- KeyguardMonitor keyguardMonitor,
+ KeyguardStateController keyguardStateController,
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
MetricsLogger metricsLogger,
LockPatternUtils lockPatternUtils,
@@ -145,7 +145,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mRemoteInputManager = remoteInputManager;
mLockscreenUserManager = lockscreenUserManager;
mShadeController = shadeController;
- mKeyguardMonitor = keyguardMonitor;
+ mKeyguardStateController = keyguardStateController;
mActivityStarter = activityStarter;
mEntryManager = entryManager;
mStatusBarStateController = statusBarStateController;
@@ -204,7 +204,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
&& mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(),
mLockscreenUserManager.getCurrentUserId());
final boolean wasOccluded = mShadeController.isOccluded();
- boolean showOverLockscreen = mKeyguardMonitor.isShowing() && intent != null
+ boolean showOverLockscreen = mKeyguardStateController.isShowing() && intent != null
&& mActivityIntentHelper.wouldShowOverLockscreen(intent.getIntent(),
mLockscreenUserManager.getCurrentUserId());
ActivityStarter.OnDismissAction postKeyguardAction =
@@ -245,7 +245,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
StatusBarNotification parentToCancel = null;
if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
StatusBarNotification summarySbn =
- mGroupManager.getLogicalGroupSummary(sbn).notification;
+ mGroupManager.getLogicalGroupSummary(sbn).getSbn();
if (shouldAutoCancel(summarySbn)) {
parentToCancel = summarySbn;
}
@@ -258,7 +258,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
if (showOverLockscreen) {
mShadeController.addPostCollapseAction(runnable);
mShadeController.collapsePanel(true /* animate */);
- } else if (mKeyguardMonitor.isShowing()
+ } else if (mKeyguardStateController.isShowing()
&& mShadeController.isOccluded()) {
mShadeController.addAfterKeyguardGoneRunnable(runnable);
mShadeController.collapsePanel();
@@ -310,7 +310,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
if (!TextUtils.isEmpty(entry.remoteInputText)) {
remoteInputText = entry.remoteInputText;
}
- if (!TextUtils.isEmpty(remoteInputText) && !controller.isSpinning(entry.key)) {
+ if (!TextUtils.isEmpty(remoteInputText) && !controller.isSpinning(entry.getKey())) {
fillInIntent = new Intent().putExtra(Notification.EXTRA_REMOTE_INPUT_DRAFT,
remoteInputText.toString());
}
@@ -409,11 +409,11 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
if (mNotificationInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) {
if (shouldSuppressFullScreenIntent(entry)) {
if (DEBUG) {
- Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + entry.key);
+ Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + entry.getKey());
}
} else if (entry.getImportance() < NotificationManager.IMPORTANCE_HIGH) {
if (DEBUG) {
- Log.d(TAG, "No Fullscreen intent: not important enough: " + entry.key);
+ Log.d(TAG, "No Fullscreen intent: not important enough: " + entry.getKey());
}
} else {
// Stop screensaver if the notification has a fullscreen intent.
@@ -432,8 +432,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
}
try {
EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
- entry.key);
- entry.notification.getNotification().fullScreenIntent.send();
+ entry.getKey());
+ entry.getSbn().getNotification().fullScreenIntent.send();
entry.notifyFullScreenIntentLaunched();
mMetricsLogger.count("note_fullscreen", 1);
} catch (PendingIntent.CanceledException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 4732049d635e..b01a8d4b82e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -74,7 +74,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager.O
import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.util.ArrayList;
@@ -89,7 +89,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
private final ShadeController mShadeController = Dependency.get(ShadeController.class);
private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
- private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+ private final KeyguardStateController mKeyguardStateController;
private final NotificationViewHierarchyManager mViewHierarchyManager =
Dependency.get(NotificationViewHierarchyManager.class);
private final NotificationLockscreenUserManager mLockscreenUserManager =
@@ -123,7 +123,6 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
private final DynamicPrivacyController mDynamicPrivacyController;
private boolean mReinflateNotificationsOnUserSwitched;
private boolean mDispatchUiModeChangeOnUserSwitched;
- private final UnlockMethodCache mUnlockMethodCache;
private TextView mNotificationPanelDebugText;
protected boolean mVrMode;
@@ -139,8 +138,10 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
ActivityLaunchAnimator activityLaunchAnimator,
DynamicPrivacyController dynamicPrivacyController,
NotificationAlertingManager notificationAlertingManager,
- NotificationRowBinderImpl notificationRowBinder) {
+ NotificationRowBinderImpl notificationRowBinder,
+ KeyguardStateController keyguardStateController) {
mContext = context;
+ mKeyguardStateController = keyguardStateController;
mNotificationPanel = panel;
mHeadsUpManager = headsUp;
mDynamicPrivacyController = dynamicPrivacyController;
@@ -152,7 +153,6 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mDozeScrimController = dozeScrimController;
mScrimController = scrimController;
- mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
mKeyguardManager = context.getSystemService(KeyguardManager.class);
mMaxAllowedKeyguardNotifications = context.getResources().getInteger(
R.integer.keyguard_max_notification_count);
@@ -201,7 +201,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
NotificationVisibility visibility,
boolean removedByUser) {
StatusBarNotificationPresenter.this.onNotificationRemoved(
- entry.key, entry.notification);
+ entry.getKey(), entry.getSbn());
if (removedByUser) {
maybeEndAmbientPulse();
}
@@ -366,7 +366,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
return false;
} else {
// we only allow head-up on the lockscreen if it doesn't have a fullscreen intent
- return !mKeyguardMonitor.isShowing()
+ return !mKeyguardStateController.isShowing()
|| mShadeController.isOccluded();
}
}
@@ -398,7 +398,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
public void onBindRow(NotificationEntry entry, PackageManager pmUser,
StatusBarNotification sbn, ExpandableNotificationRow row) {
row.setAboveShelfChangedListener(mAboveShelfObserver);
- row.setSecureStateProvider(mUnlockMethodCache::canSkipBouncer);
+ row.setSecureStateProvider(mKeyguardStateController::canDismissLockScreen);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 13d4b8edb8d4..1def89b3af54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -35,7 +35,6 @@ import android.view.View;
import android.view.ViewParent;
import com.android.systemui.ActivityIntentHelper;
-import com.android.systemui.Dependency;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
@@ -47,8 +46,7 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
-import com.android.systemui.statusbar.policy.RemoteInputView;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -59,18 +57,16 @@ import javax.inject.Singleton;
public class StatusBarRemoteInputCallback implements Callback, Callbacks,
StatusBarStateController.StateListener {
- private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
- private final SysuiStatusBarStateController mStatusBarStateController =
- (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class);
- private final NotificationLockscreenUserManager mLockscreenUserManager =
- Dependency.get(NotificationLockscreenUserManager.class);
- private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
+ private final KeyguardStateController mKeyguardStateController;
+ private final SysuiStatusBarStateController mStatusBarStateController;
+ private final NotificationLockscreenUserManager mLockscreenUserManager;
+ private final ActivityStarter mActivityStarter;
+ private final ShadeController mShadeController;
private final Context mContext;
private final ActivityIntentHelper mActivityIntentHelper;
private final NotificationGroupManager mGroupManager;
private View mPendingWorkRemoteInputView;
private View mPendingRemoteInputView;
- private final ShadeController mShadeController = Dependency.get(ShadeController.class);
private KeyguardManager mKeyguardManager;
private final CommandQueue mCommandQueue;
private int mDisabled2;
@@ -80,10 +76,19 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
/**
*/
@Inject
- public StatusBarRemoteInputCallback(Context context, NotificationGroupManager groupManager) {
+ public StatusBarRemoteInputCallback(Context context, NotificationGroupManager groupManager,
+ NotificationLockscreenUserManager notificationLockscreenUserManager,
+ KeyguardStateController keyguardStateController,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter, ShadeController shadeController) {
mContext = context;
mContext.registerReceiverAsUser(mChallengeReceiver, UserHandle.ALL,
new IntentFilter(ACTION_DEVICE_LOCKED_CHANGED), null, null);
+ mLockscreenUserManager = notificationLockscreenUserManager;
+ mKeyguardStateController = keyguardStateController;
+ mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController;
+ mShadeController = shadeController;
+ mActivityStarter = activityStarter;
mStatusBarStateController.addCallback(this);
mKeyguardManager = context.getSystemService(KeyguardManager.class);
mCommandQueue = getComponent(context, CommandQueue.class);
@@ -165,7 +170,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
@Override
public void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row,
View clickedView) {
- if (mKeyguardMonitor.isShowing()) {
+ if (mKeyguardStateController.isShowing()) {
onLockedRemoteInput(row, clickedView);
} else {
if (row.isChildInGroup() && !row.areChildrenExpanded()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index 724b46297e75..ca7a936d58f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -21,7 +21,6 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M
import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
-import android.app.ActivityManager;
import android.app.IActivityManager;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -39,8 +38,6 @@ import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -94,24 +91,14 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat
private final ArrayList<WeakReference<StatusBarWindowCallback>>
mCallbacks = Lists.newArrayList();
- private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class);
+ private final SysuiColorExtractor mColorExtractor;
@Inject
- public StatusBarWindowController(Context context,
- StatusBarStateController statusBarStateController,
- ConfigurationController configurationController,
- KeyguardBypassController keyguardBypassController) {
- this(context, context.getSystemService(WindowManager.class), ActivityManager.getService(),
- DozeParameters.getInstance(context), statusBarStateController,
- configurationController, keyguardBypassController);
- }
-
- @VisibleForTesting
public StatusBarWindowController(Context context, WindowManager windowManager,
IActivityManager activityManager, DozeParameters dozeParameters,
StatusBarStateController statusBarStateController,
ConfigurationController configurationController,
- KeyguardBypassController keyguardBypassController) {
+ KeyguardBypassController keyguardBypassController, SysuiColorExtractor colorExtractor) {
mContext = context;
mWindowManager = windowManager;
mActivityManager = activityManager;
@@ -120,6 +107,7 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat
mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
mLpChanged = new LayoutParams();
mKeyguardBypassController = keyguardBypassController;
+ mColorExtractor = colorExtractor;
mLockScreenDisplayTimeout = context.getResources()
.getInteger(R.integer.config_lockScreenDisplayTimeout);
((SysuiStatusBarStateController) statusBarStateController)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index a9e818df6bc3..6b513919e912 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -19,29 +19,19 @@ package com.android.systemui.statusbar.phone;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
import android.annotation.LayoutRes;
-import android.app.StatusBarManager;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Canvas;
+import android.graphics.Insets;
import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.hardware.display.AmbientDisplayConfiguration;
-import android.media.AudioManager;
-import android.media.session.MediaSessionLegacyHelper;
import android.net.Uri;
import android.os.Bundle;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.provider.Settings;
import android.util.AttributeSet;
import android.view.ActionMode;
import android.view.DisplayCutout;
-import android.view.GestureDetector;
-import android.view.InputDevice;
import android.view.InputQueue;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -53,24 +43,13 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
+import android.view.WindowInsets;
import android.view.WindowInsetsController;
import android.widget.FrameLayout;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.view.FloatingActionMode;
import com.android.internal.widget.FloatingToolbar;
-import com.android.systemui.Dependency;
-import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.DragDownHelper;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.phone.ScrimController.ScrimVisibility;
-import com.android.systemui.tuner.TunerService;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
/**
* Combined status bar and notification panel view. Also holding backdrop and scrims.
@@ -79,91 +58,26 @@ public class StatusBarWindowView extends FrameLayout {
public static final String TAG = "StatusBarWindowView";
public static final boolean DEBUG = StatusBar.DEBUG;
- private final GestureDetector mGestureDetector;
- private final StatusBarStateController mStatusBarStateController;
- private boolean mDoubleTapEnabled;
- private boolean mSingleTapEnabled;
- private DragDownHelper mDragDownHelper;
- private NotificationStackScrollLayout mStackScrollLayout;
- private NotificationPanelView mNotificationPanel;
- private View mBrightnessMirror;
- private LockIcon mLockIcon;
- private PhoneStatusBarView mStatusBarView;
-
private int mRightInset = 0;
private int mLeftInset = 0;
- private StatusBar mService;
- private final Paint mTransparentSrcPaint = new Paint();
- private FalsingManager mFalsingManager;
-
// Implements the floating action mode for TextView's Cut/Copy/Past menu. Normally provided by
// DecorView, but since this is a special window we have to roll our own.
private View mFloatingActionModeOriginatingView;
private ActionMode mFloatingActionMode;
private FloatingToolbar mFloatingToolbar;
private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener;
- private boolean mTouchCancelled;
- private boolean mTouchActive;
- private boolean mExpandAnimationRunning;
- private boolean mExpandAnimationPending;
- private boolean mSuppressingWakeUpGesture;
-
- private final GestureDetector.SimpleOnGestureListener mGestureListener =
- new GestureDetector.SimpleOnGestureListener() {
- @Override
- public boolean onSingleTapConfirmed(MotionEvent e) {
- if (mSingleTapEnabled && !mSuppressingWakeUpGesture) {
- mService.wakeUpIfDozing(SystemClock.uptimeMillis(), StatusBarWindowView.this,
- "SINGLE_TAP");
- return true;
- }
- return false;
- }
- @Override
- public boolean onDoubleTap(MotionEvent e) {
- if (mDoubleTapEnabled || mSingleTapEnabled) {
- mService.wakeUpIfDozing(SystemClock.uptimeMillis(), StatusBarWindowView.this,
- "DOUBLE_TAP");
- return true;
- }
- return false;
- }
- };
- private final TunerService.Tunable mTunable = (key, newValue) -> {
- AmbientDisplayConfiguration configuration = new AmbientDisplayConfiguration(mContext);
- switch (key) {
- case Settings.Secure.DOZE_DOUBLE_TAP_GESTURE:
- mDoubleTapEnabled = configuration.doubleTapGestureEnabled(UserHandle.USER_CURRENT);
- break;
- case Settings.Secure.DOZE_TAP_SCREEN_GESTURE:
- mSingleTapEnabled = configuration.tapGestureEnabled(UserHandle.USER_CURRENT);
- }
- };
-
- /**
- * If set to true, the current gesture started below the notch and we need to dispatch touch
- * events manually as it's outside of the regular view bounds.
- */
- private boolean mExpandingBelowNotch;
- private KeyguardBypassController mBypassController;
+ private InteractionEventHandler mInteractionEventHandler;
public StatusBarWindowView(Context context, AttributeSet attrs) {
super(context, attrs);
setMotionEventSplittingEnabled(false);
- mTransparentSrcPaint.setColor(0);
- mTransparentSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
- mFalsingManager = Dependency.get(FalsingManager.class); // TODO: inject into a controller.
- mGestureDetector = new GestureDetector(context, mGestureListener);
- mStatusBarStateController = Dependency.get(StatusBarStateController.class);
- Dependency.get(TunerService.class).addTunable(mTunable,
- Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
- Settings.Secure.DOZE_TAP_SCREEN_GESTURE);
}
@Override
- protected boolean fitSystemWindows(Rect insets) {
+ public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
+ final Insets insets = windowInsets.getMaxInsets(WindowInsets.Type.systemBars());
if (getFitsSystemWindows()) {
boolean paddingChanged = insets.top != getPaddingTop()
|| insets.bottom != getPaddingBottom();
@@ -189,9 +103,6 @@ public class StatusBarWindowView extends FrameLayout {
if (paddingChanged) {
setPadding(0, 0, 0, 0);
}
- insets.left = 0;
- insets.top = 0;
- insets.right = 0;
} else {
if (mRightInset != 0 || mLeftInset != 0) {
mRightInset = 0;
@@ -205,9 +116,8 @@ public class StatusBarWindowView extends FrameLayout {
if (changed) {
setPadding(0, 0, 0, 0);
}
- insets.top = 0;
}
- return false;
+ return windowInsets;
}
private void applyMargins() {
@@ -226,11 +136,6 @@ public class StatusBarWindowView extends FrameLayout {
}
}
- @VisibleForTesting
- protected NotificationStackScrollLayout getStackScrollLayout() {
- return mStackScrollLayout;
- }
-
@Override
public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
@@ -242,61 +147,6 @@ public class StatusBarWindowView extends FrameLayout {
}
@Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mStackScrollLayout = findViewById(R.id.notification_stack_scroller);
- mNotificationPanel = findViewById(R.id.notification_panel);
- mBrightnessMirror = findViewById(R.id.brightness_mirror);
- mLockIcon = findViewById(R.id.lock_icon);
- }
-
- @Override
- public void onViewAdded(View child) {
- super.onViewAdded(child);
- if (child.getId() == R.id.brightness_mirror) {
- mBrightnessMirror = child;
- }
- }
-
- /**
- * Propagate {@link StatusBar} pulsing state.
- */
- public void setPulsing(boolean pulsing) {
- if (mLockIcon != null) {
- mLockIcon.setPulsing(pulsing);
- }
- }
-
- /**
- * Called when the biometric authentication mode changes.
- * @param wakeAndUnlock If the type is {@link BiometricUnlockController#isWakeAndUnlock()}
- * @param isUnlock If the type is {@link BiometricUnlockController#isBiometricUnlock()} ()
- */
- public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock) {
- if (mLockIcon != null) {
- mLockIcon.onBiometricAuthModeChanged(wakeAndUnlock, isUnlock);
- }
- }
-
- public void setStatusBarView(PhoneStatusBarView statusBarView) {
- mStatusBarView = statusBarView;
- }
-
- public void setService(StatusBar service) {
- mService = service;
- NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout();
- ExpandHelper.Callback expandHelperCallback = stackScrollLayout.getExpandHelperCallback();
- DragDownHelper.DragDownCallback dragDownCallback = stackScrollLayout.getDragDownCallback();
- setDragDownHelper(new DragDownHelper(getContext(), this, expandHelperCallback,
- dragDownCallback, mFalsingManager));
- }
-
- @VisibleForTesting
- void setDragDownHelper(DragDownHelper dragDownHelper) {
- mDragDownHelper = dragDownHelper;
- }
-
- @Override
protected void onAttachedToWindow () {
super.onAttachedToWindow();
setWillNotDraw(!DEBUG);
@@ -304,152 +154,53 @@ public class StatusBarWindowView extends FrameLayout {
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
- if (mService.interceptMediaKey(event)) {
+ if (mInteractionEventHandler.interceptMediaKey(event)) {
return true;
}
+
if (super.dispatchKeyEvent(event)) {
return true;
}
- boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_BACK:
- if (!down) {
- mService.onBackPressed();
- }
- return true;
- case KeyEvent.KEYCODE_MENU:
- if (!down) {
- return mService.onMenuPressed();
- }
- case KeyEvent.KEYCODE_SPACE:
- if (!down) {
- return mService.onSpacePressed();
- }
- break;
- case KeyEvent.KEYCODE_VOLUME_DOWN:
- case KeyEvent.KEYCODE_VOLUME_UP:
- if (mService.isDozing()) {
- MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
- event, AudioManager.USE_DEFAULT_STREAM_TYPE, true);
- return true;
- }
- break;
- }
- return false;
- }
- public void setTouchActive(boolean touchActive) {
- mTouchActive = touchActive;
+ return mInteractionEventHandler.dispatchKeyEvent(event);
}
- void suppressWakeUpGesture(boolean suppress) {
- mSuppressingWakeUpGesture = suppress;
+ protected void setInteractionEventHandler(InteractionEventHandler listener) {
+ mInteractionEventHandler = listener;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
- boolean isDown = ev.getActionMasked() == MotionEvent.ACTION_DOWN;
- boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
- boolean isCancel = ev.getActionMasked() == MotionEvent.ACTION_CANCEL;
-
- // Reset manual touch dispatch state here but make sure the UP/CANCEL event still gets
- // delivered.
- boolean expandingBelowNotch = mExpandingBelowNotch;
- if (isUp || isCancel) {
- mExpandingBelowNotch = false;
- }
-
- if (!isCancel && mService.shouldIgnoreTouch()) {
- return false;
- }
- if (isDown && mNotificationPanel.isFullyCollapsed()) {
- mNotificationPanel.startExpandLatencyTracking();
- }
- if (isDown) {
- setTouchActive(true);
- mTouchCancelled = false;
- } else if (ev.getActionMasked() == MotionEvent.ACTION_UP
- || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) {
- setTouchActive(false);
- }
- if (mTouchCancelled || mExpandAnimationRunning || mExpandAnimationPending) {
- return false;
- }
- mFalsingManager.onTouchEvent(ev, getWidth(), getHeight());
- mGestureDetector.onTouchEvent(ev);
- if (mBrightnessMirror != null && mBrightnessMirror.getVisibility() == VISIBLE) {
- // Disallow new pointers while the brightness mirror is visible. This is so that you
- // can't touch anything other than the brightness slider while the mirror is showing
- // and the rest of the panel is transparent.
- if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
- return false;
- }
- }
- if (isDown) {
- getStackScrollLayout().closeControlsIfOutsideTouch(ev);
- }
- if (mService.isDozing()) {
- mService.mDozeScrimController.extendPulse();
- }
+ Boolean result = mInteractionEventHandler.handleDispatchTouchEvent(ev);
- // In case we start outside of the view bounds (below the status bar), we need to dispatch
- // the touch manually as the view system can't accomodate for touches outside of the
- // regular view bounds.
- if (isDown && ev.getY() >= mBottom) {
- mExpandingBelowNotch = true;
- expandingBelowNotch = true;
- }
- if (expandingBelowNotch) {
- return mStatusBarView.dispatchTouchEvent(ev);
- }
-
- return super.dispatchTouchEvent(ev);
+ return result != null ? result : super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
- NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout();
- if (mService.isDozing() && !mService.isPulsing()) {
- // Capture all touch events in always-on.
- return true;
- }
- boolean intercept = false;
- if (mNotificationPanel.isFullyExpanded()
- && mDragDownHelper.isDragDownEnabled()
- && !mService.isBouncerShowing()
- && !mService.isDozing()) {
- intercept = mDragDownHelper.onInterceptTouchEvent(ev);
- }
+ boolean intercept = mInteractionEventHandler.shouldInterceptTouchEvent(ev);
if (!intercept) {
- super.onInterceptTouchEvent(ev);
+ intercept = super.onInterceptTouchEvent(ev);
}
if (intercept) {
- MotionEvent cancellation = MotionEvent.obtain(ev);
- cancellation.setAction(MotionEvent.ACTION_CANCEL);
- stackScrollLayout.onInterceptTouchEvent(cancellation);
- mNotificationPanel.onInterceptTouchEvent(cancellation);
- cancellation.recycle();
+ mInteractionEventHandler.didIntercept(ev);
}
+
return intercept;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
- boolean handled = false;
- if (mService.isDozing()) {
- handled = !mService.isPulsing();
- }
- if ((mDragDownHelper.isDragDownEnabled() && !handled) || mDragDownHelper.isDraggingDown()) {
- // we still want to finish our drag down gesture when locking the screen
- handled = mDragDownHelper.onTouchEvent(ev);
- }
+ boolean handled = mInteractionEventHandler.handleTouchEvent(ev);
+
if (!handled) {
handled = super.onTouchEvent(ev);
}
- final int action = ev.getAction();
- if (!handled && (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL)) {
- mService.setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
+
+ if (!handled) {
+ mInteractionEventHandler.didNotHandleTouchEvent(ev);
}
+
return handled;
}
@@ -465,77 +216,6 @@ public class StatusBarWindowView extends FrameLayout {
}
}
- public void cancelExpandHelper() {
- NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout();
- if (stackScrollLayout != null) {
- stackScrollLayout.cancelExpandHelper();
- }
- }
-
- public void cancelCurrentTouch() {
- if (mTouchActive) {
- final long now = SystemClock.uptimeMillis();
- MotionEvent event = MotionEvent.obtain(now, now,
- MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
- event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
- dispatchTouchEvent(event);
- event.recycle();
- mTouchCancelled = true;
- }
- }
-
- public void setExpandAnimationRunning(boolean expandAnimationRunning) {
- mExpandAnimationRunning = expandAnimationRunning;
- }
-
- public void setExpandAnimationPending(boolean pending) {
- mExpandAnimationPending = pending;
- }
-
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.print(" mExpandAnimationPending="); pw.println(mExpandAnimationPending);
- pw.print(" mExpandAnimationRunning="); pw.println(mExpandAnimationRunning);
- pw.print(" mTouchCancelled="); pw.println(mTouchCancelled);
- pw.print(" mTouchActive="); pw.println(mTouchActive);
- }
-
- /**
- * Called whenever the scrims become opaque, transparent or semi-transparent.
- */
- public void onScrimVisibilityChanged(@ScrimVisibility int scrimsVisible) {
- if (mLockIcon != null) {
- mLockIcon.onScrimVisibilityChanged(scrimsVisible);
- }
- }
-
- /**
- * When we're launching an affordance, like double pressing power to open camera.
- */
- public void onShowingLaunchAffordanceChanged(boolean showing) {
- if (mLockIcon != null) {
- mLockIcon.onShowingLaunchAffordanceChanged(showing);
- }
- }
-
- public void setBypassController(KeyguardBypassController bypassController) {
- mBypassController = bypassController;
- }
-
- public void setBouncerShowingScrimmed(boolean bouncerShowing) {
- if (mLockIcon != null) {
- mLockIcon.setBouncerShowingScrimmed(bouncerShowing);
- }
- }
-
- /**
- * When {@link KeyguardBouncer} starts to be dismissed and starts to play its animation.
- */
- public void onBouncerPreHideAnimation() {
- if (mLockIcon != null) {
- mLockIcon.onBouncerPreHideAnimation();
- }
- }
-
public class LayoutParams extends FrameLayout.LayoutParams {
public boolean ignoreRightInset;
@@ -657,6 +337,35 @@ public class StatusBarWindowView extends FrameLayout {
}
}
+ interface InteractionEventHandler {
+ /**
+ * Returns a result for {@link ViewGroup#dispatchTouchEvent(MotionEvent)} or null to defer
+ * to the super method.
+ */
+ Boolean handleDispatchTouchEvent(MotionEvent ev);
+
+ /**
+ * Returns if the view should intercept the touch event.
+ *
+ * The touch event may still be interecepted if
+ * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)} decides to do so.
+ */
+ boolean shouldInterceptTouchEvent(MotionEvent ev);
+
+ /**
+ * Called when the view decides to intercept the touch event.
+ */
+ void didIntercept(MotionEvent ev);
+
+ boolean handleTouchEvent(MotionEvent ev);
+
+ void didNotHandleTouchEvent(MotionEvent ev);
+
+ boolean interceptMediaKey(KeyEvent event);
+
+ boolean dispatchKeyEvent(KeyEvent event);
+ }
+
/**
* Minimal window to satisfy FloatingToolbar.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
new file mode 100644
index 000000000000..fd3f9c803c12
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
@@ -0,0 +1,564 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.app.StatusBarManager;
+import android.hardware.display.AmbientDisplayConfiguration;
+import android.media.AudioManager;
+import android.media.session.MediaSessionLegacyHelper;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.view.GestureDetector;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewStub;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.ExpandHelper;
+import com.android.systemui.R;
+import com.android.systemui.doze.DozeLog;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.DragDownHelper;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.InjectionInflationController;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import javax.inject.Inject;
+
+/**
+ * Controller for {@link StatusBarWindowView}.
+ */
+public class StatusBarWindowViewController {
+ private final StatusBarWindowView mView;
+ private final FalsingManager mFalsingManager;
+ private final GestureDetector mGestureDetector;
+ private View mBrightnessMirror;
+ private boolean mTouchActive;
+ private boolean mTouchCancelled;
+ private boolean mExpandAnimationPending;
+ private boolean mExpandAnimationRunning;
+ private NotificationStackScrollLayout mStackScrollLayout;
+ private LockIcon mLockIcon;
+ private PhoneStatusBarView mStatusBarView;
+ private StatusBar mService;
+ private DragDownHelper mDragDownHelper;
+ private boolean mSuppressingWakeUpGesture;
+ private boolean mDoubleTapEnabled;
+ private boolean mSingleTapEnabled;
+ private boolean mExpandingBelowNotch;
+
+ private StatusBarWindowViewController(
+ StatusBarWindowView view,
+ InjectionInflationController injectionInflationController,
+ NotificationWakeUpCoordinator coordinator,
+ PulseExpansionHandler pulseExpansionHandler,
+ DynamicPrivacyController dynamicPrivacyController,
+ KeyguardBypassController bypassController,
+ FalsingManager falsingManager,
+ PluginManager pluginManager,
+ TunerService tunerService,
+ ShadeController shadeController,
+ NotificationLockscreenUserManager notificationLockscreenUserManager,
+ NotificationEntryManager notificationEntryManager,
+ KeyguardStateController keyguardStateController,
+ SysuiStatusBarStateController statusBarStateController,
+ DozeLog dozeLog,
+ DozeParameters dozeParameters) {
+ mView = view;
+ mFalsingManager = falsingManager;
+
+ // TODO: create controller for NotificationPanelView
+ NotificationPanelView notificationPanelView = new NotificationPanelView(
+ view.getContext(),
+ null,
+ injectionInflationController,
+ coordinator,
+ pulseExpansionHandler,
+ dynamicPrivacyController,
+ bypassController,
+ falsingManager,
+ pluginManager,
+ shadeController,
+ notificationLockscreenUserManager,
+ notificationEntryManager,
+ keyguardStateController,
+ statusBarStateController,
+ dozeLog,
+ dozeParameters);
+ ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+ notificationPanelView.setVisibility(View.INVISIBLE);
+ notificationPanelView.setId(R.id.notification_panel);
+ LayoutInflater li = injectionInflationController.injectable(
+ LayoutInflater.from(mView.getContext()));
+
+ li.inflate(R.layout.status_bar_expanded, notificationPanelView);
+ notificationPanelView.onChildrenAttached();
+
+ ViewStub statusBarExpanded = view.findViewById(R.id.status_bar_expanded);
+ mView.addView(notificationPanelView, mView.indexOfChild(statusBarExpanded), lp);
+ mView.removeView(statusBarExpanded);
+
+ mStackScrollLayout = mView.findViewById(R.id.notification_stack_scroller);
+ mLockIcon = mView.findViewById(R.id.lock_icon);
+ mBrightnessMirror = mView.findViewById(R.id.brightness_mirror);
+
+ TunerService.Tunable tunable = (key, newValue) -> {
+ AmbientDisplayConfiguration configuration =
+ new AmbientDisplayConfiguration(mView.getContext());
+ switch (key) {
+ case Settings.Secure.DOZE_DOUBLE_TAP_GESTURE:
+ mDoubleTapEnabled = configuration.doubleTapGestureEnabled(
+ UserHandle.USER_CURRENT);
+ break;
+ case Settings.Secure.DOZE_TAP_SCREEN_GESTURE:
+ mSingleTapEnabled = configuration.tapGestureEnabled(UserHandle.USER_CURRENT);
+ }
+ };
+ tunerService.addTunable(tunable,
+ Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
+ Settings.Secure.DOZE_TAP_SCREEN_GESTURE);
+
+ GestureDetector.SimpleOnGestureListener gestureListener =
+ new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent e) {
+ if (mSingleTapEnabled && !mSuppressingWakeUpGesture) {
+ mService.wakeUpIfDozing(
+ SystemClock.uptimeMillis(), mView, "SINGLE_TAP");
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onDoubleTap(MotionEvent e) {
+ if (mDoubleTapEnabled || mSingleTapEnabled) {
+ mService.wakeUpIfDozing(
+ SystemClock.uptimeMillis(), mView, "DOUBLE_TAP");
+ return true;
+ }
+ return false;
+ }
+ };
+ mGestureDetector = new GestureDetector(mView.getContext(), gestureListener);
+
+ mView.setInteractionEventHandler(new StatusBarWindowView.InteractionEventHandler() {
+ @Override
+ public Boolean handleDispatchTouchEvent(MotionEvent ev) {
+ boolean isDown = ev.getActionMasked() == MotionEvent.ACTION_DOWN;
+ boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
+ boolean isCancel = ev.getActionMasked() == MotionEvent.ACTION_CANCEL;
+
+ if (isUp || isCancel) {
+ mExpandingBelowNotch = false;
+ }
+
+ // Reset manual touch dispatch state here but make sure the UP/CANCEL event still
+ // gets
+ // delivered.
+
+ if (!isCancel && mService.shouldIgnoreTouch()) {
+ return false;
+ }
+ if (isDown && notificationPanelView.isFullyCollapsed()) {
+ notificationPanelView.startExpandLatencyTracking();
+ }
+ if (isDown) {
+ setTouchActive(true);
+ mTouchCancelled = false;
+ } else if (ev.getActionMasked() == MotionEvent.ACTION_UP
+ || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) {
+ setTouchActive(false);
+ }
+ if (mTouchCancelled || mExpandAnimationRunning || mExpandAnimationPending) {
+ return false;
+ }
+ mFalsingManager.onTouchEvent(ev, mView.getWidth(), mView.getHeight());
+ mGestureDetector.onTouchEvent(ev);
+ if (mBrightnessMirror != null
+ && mBrightnessMirror.getVisibility() == View.VISIBLE) {
+ // Disallow new pointers while the brightness mirror is visible. This is so that
+ // you can't touch anything other than the brightness slider while the mirror is
+ // showing and the rest of the panel is transparent.
+ if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
+ return false;
+ }
+ }
+ if (isDown) {
+ getStackScrollLayout().closeControlsIfOutsideTouch(ev);
+ }
+ if (mService.isDozing()) {
+ mService.mDozeScrimController.extendPulse();
+ }
+ // In case we start outside of the view bounds (below the status bar), we need to
+ // dispatch
+ // the touch manually as the view system can't accommodate for touches outside of
+ // the
+ // regular view bounds.
+ if (isDown && ev.getY() >= mView.getBottom()) {
+ mExpandingBelowNotch = true;
+ }
+ if (mExpandingBelowNotch) {
+ return mStatusBarView.dispatchTouchEvent(ev);
+ }
+
+ return null;
+ }
+
+ @Override
+ public boolean shouldInterceptTouchEvent(MotionEvent ev) {
+ if (mService.isDozing() && !mService.isPulsing()) {
+ // Capture all touch events in always-on.
+ return true;
+ }
+ boolean intercept = false;
+ if (notificationPanelView.isFullyExpanded()
+ && mDragDownHelper.isDragDownEnabled()
+ && !mService.isBouncerShowing()
+ && !mService.isDozing()) {
+ intercept = mDragDownHelper.onInterceptTouchEvent(ev);
+ }
+
+ return intercept;
+
+ }
+
+ @Override
+ public void didIntercept(MotionEvent ev) {
+ NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout();
+ MotionEvent cancellation = MotionEvent.obtain(ev);
+ cancellation.setAction(MotionEvent.ACTION_CANCEL);
+ stackScrollLayout.onInterceptTouchEvent(cancellation);
+ notificationPanelView.onInterceptTouchEvent(cancellation);
+ cancellation.recycle();
+ }
+
+ @Override
+ public boolean handleTouchEvent(MotionEvent ev) {
+ boolean handled = false;
+ if (mService.isDozing()) {
+ handled = !mService.isPulsing();
+ }
+ if ((mDragDownHelper.isDragDownEnabled() && !handled)
+ || mDragDownHelper.isDraggingDown()) {
+ // we still want to finish our drag down gesture when locking the screen
+ handled = mDragDownHelper.onTouchEvent(ev);
+ }
+
+ return handled;
+ }
+
+ @Override
+ public void didNotHandleTouchEvent(MotionEvent ev) {
+ final int action = ev.getActionMasked();
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ mService.setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
+ }
+ }
+
+ @Override
+ public boolean interceptMediaKey(KeyEvent event) {
+ return mService.interceptMediaKey(event);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_BACK:
+ if (!down) {
+ mService.onBackPressed();
+ }
+ return true;
+ case KeyEvent.KEYCODE_MENU:
+ if (!down) {
+ return mService.onMenuPressed();
+ }
+ break;
+ case KeyEvent.KEYCODE_SPACE:
+ if (!down) {
+ return mService.onSpacePressed();
+ }
+ break;
+ case KeyEvent.KEYCODE_VOLUME_DOWN:
+ case KeyEvent.KEYCODE_VOLUME_UP:
+ if (mService.isDozing()) {
+ MediaSessionLegacyHelper.getHelper(mView.getContext())
+ .sendVolumeKeyEvent(
+ event, AudioManager.USE_DEFAULT_STREAM_TYPE, true);
+ return true;
+ }
+ break;
+ }
+ return false;
+ }
+ });
+
+ mView.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
+ @Override
+ public void onChildViewAdded(View parent, View child) {
+ if (child.getId() == R.id.brightness_mirror) {
+ mBrightnessMirror = child;
+ }
+ }
+
+ @Override
+ public void onChildViewRemoved(View parent, View child) {
+ }
+ });
+ }
+
+ public StatusBarWindowView getView() {
+ return mView;
+ }
+
+ public void setTouchActive(boolean touchActive) {
+ mTouchActive = touchActive;
+ }
+
+ public void cancelCurrentTouch() {
+ if (mTouchActive) {
+ final long now = SystemClock.uptimeMillis();
+ MotionEvent event = MotionEvent.obtain(now, now,
+ MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
+ event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ mView.dispatchTouchEvent(event);
+ event.recycle();
+ mTouchCancelled = true;
+ }
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.print(" mExpandAnimationPending=");
+ pw.println(mExpandAnimationPending);
+ pw.print(" mExpandAnimationRunning=");
+ pw.println(mExpandAnimationRunning);
+ pw.print(" mTouchCancelled=");
+ pw.println(mTouchCancelled);
+ pw.print(" mTouchActive=");
+ pw.println(mTouchActive);
+ }
+
+ public void setExpandAnimationPending(boolean pending) {
+ mExpandAnimationPending = pending;
+ }
+
+ public void setExpandAnimationRunning(boolean running) {
+ mExpandAnimationRunning = running;
+ }
+
+ public void cancelExpandHelper() {
+ NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout();
+ if (stackScrollLayout != null) {
+ stackScrollLayout.cancelExpandHelper();
+ }
+ }
+
+ @VisibleForTesting
+ protected NotificationStackScrollLayout getStackScrollLayout() {
+ return mStackScrollLayout;
+ }
+
+ /**
+ * Called whenever the scrims become opaque, transparent or semi-transparent.
+ */
+ public void onScrimVisibilityChanged(Integer scrimsVisible) {
+ if (mLockIcon != null) {
+ mLockIcon.onScrimVisibilityChanged(scrimsVisible);
+ }
+ }
+
+ /**
+ * Propagate {@link StatusBar} pulsing state.
+ */
+ public void setPulsing(boolean pulsing) {
+ if (mLockIcon != null) {
+ mLockIcon.setPulsing(pulsing);
+ }
+ }
+
+ /**
+ * Called when the biometric authentication mode changes.
+ *
+ * @param wakeAndUnlock If the type is {@link BiometricUnlockController#isWakeAndUnlock()}
+ * @param isUnlock If the type is {@link BiometricUnlockController#isBiometricUnlock()} ()
+ */
+ public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock) {
+ if (mLockIcon != null) {
+ mLockIcon.onBiometricAuthModeChanged(wakeAndUnlock, isUnlock);
+ }
+ }
+
+ public void setStatusBarView(PhoneStatusBarView statusBarView) {
+ mStatusBarView = statusBarView;
+ }
+
+ public void setService(StatusBar statusBar) {
+ mService = statusBar;
+ NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout();
+ ExpandHelper.Callback expandHelperCallback = stackScrollLayout.getExpandHelperCallback();
+ DragDownHelper.DragDownCallback dragDownCallback = stackScrollLayout.getDragDownCallback();
+ setDragDownHelper(
+ new DragDownHelper(
+ mView.getContext(), mView, expandHelperCallback,
+ dragDownCallback, mFalsingManager));
+ }
+
+ @VisibleForTesting
+ void setDragDownHelper(DragDownHelper dragDownHelper) {
+ mDragDownHelper = dragDownHelper;
+ }
+
+ public void suppressWakeUpGesture(boolean suppress) {
+ mSuppressingWakeUpGesture = suppress;
+ }
+
+ /**
+ * When we're launching an affordance, like double pressing power to open camera.
+ */
+ public void onShowingLaunchAffordanceChanged(Boolean showing) {
+ if (mLockIcon != null) {
+ mLockIcon.onShowingLaunchAffordanceChanged(showing);
+ }
+ }
+
+ public void setBouncerShowingScrimmed(boolean bouncerShowing) {
+ if (mLockIcon != null) {
+ mLockIcon.setBouncerShowingScrimmed(bouncerShowing);
+ }
+ }
+
+ /**
+ * When {@link KeyguardBouncer} starts to be dismissed and starts to play its animation.
+ */
+ public void onBouncerPreHideAnimation() {
+ if (mLockIcon != null) {
+ mLockIcon.onBouncerPreHideAnimation();
+ }
+ }
+
+ /**
+ * Builder for {@link StatusBarWindowViewController}.
+ */
+ public static class Builder {
+ private final InjectionInflationController mInjectionInflationController;
+ private final NotificationWakeUpCoordinator mCoordinator;
+ private final PulseExpansionHandler mPulseExpansionHandler;
+ private final DynamicPrivacyController mDynamicPrivacyController;
+ private final KeyguardBypassController mBypassController;
+ private final FalsingManager mFalsingManager;
+ private final PluginManager mPluginManager;
+ private final TunerService mTunerService;
+ private final KeyguardStateController mKeyguardStateController;
+ private final SysuiStatusBarStateController mStatusBarStateController;
+ private ShadeController mShadeController;
+ private final NotificationLockscreenUserManager mNotificationLockScreenUserManager;
+ private final NotificationEntryManager mNotificationEntryManager;
+ private final DozeLog mDozeLog;
+ private final DozeParameters mDozeParameters;
+ private StatusBarWindowView mView;
+
+ @Inject
+ public Builder(
+ InjectionInflationController injectionInflationController,
+ NotificationWakeUpCoordinator coordinator,
+ PulseExpansionHandler pulseExpansionHandler,
+ DynamicPrivacyController dynamicPrivacyController,
+ KeyguardBypassController bypassController,
+ FalsingManager falsingManager,
+ PluginManager pluginManager,
+ TunerService tunerService,
+ NotificationLockscreenUserManager notificationLockscreenUserManager,
+ NotificationEntryManager notificationEntryManager,
+ KeyguardStateController keyguardStateController,
+ StatusBarStateController statusBarStateController,
+ DozeLog dozeLog,
+ DozeParameters dozeParameters) {
+ mInjectionInflationController = injectionInflationController;
+ mCoordinator = coordinator;
+ mPulseExpansionHandler = pulseExpansionHandler;
+ mDynamicPrivacyController = dynamicPrivacyController;
+ mBypassController = bypassController;
+ mFalsingManager = falsingManager;
+ mPluginManager = pluginManager;
+ mTunerService = tunerService;
+ mNotificationLockScreenUserManager = notificationLockscreenUserManager;
+ mNotificationEntryManager = notificationEntryManager;
+ mKeyguardStateController = keyguardStateController;
+ mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController;
+ mDozeLog = dozeLog;
+ mDozeParameters = dozeParameters;
+ }
+
+ /**
+ * Provide {@link StatusBarWindowView} to attach this controller to.
+ */
+ public Builder setStatusBarWindowView(StatusBarWindowView view) {
+ mView = view;
+ return this;
+ }
+
+ /**
+ * Provide {@link ShadeController} that this view needs.
+ */
+ public Builder setShadeController(ShadeController shadeController) {
+ mShadeController = shadeController;
+ return this;
+ }
+
+ /**
+ * Build a {@link StatusBarWindowView}.
+ */
+ public StatusBarWindowViewController build() {
+ return new StatusBarWindowViewController(
+ mView,
+ mInjectionInflationController,
+ mCoordinator,
+ mPulseExpansionHandler,
+ mDynamicPrivacyController,
+ mBypassController,
+ mFalsingManager,
+ mPluginManager,
+ mTunerService,
+ mShadeController,
+ mNotificationLockScreenUserManager,
+ mNotificationEntryManager,
+ mKeyguardStateController,
+ mStatusBarStateController,
+ mDozeLog,
+ mDozeParameters);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index e61a67c77602..44be6bc282a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -28,7 +28,8 @@ import android.view.WindowManager.LayoutParams;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
/**
@@ -92,15 +93,15 @@ public class SystemUIDialog extends AlertDialog {
public static void setShowForAllUsers(Dialog dialog, boolean show) {
if (show) {
dialog.getWindow().getAttributes().privateFlags |=
- WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
} else {
dialog.getWindow().getAttributes().privateFlags &=
- ~WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ ~WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
}
}
public static void setWindowOnTop(Dialog dialog) {
- if (Dependency.get(KeyguardMonitor.class).isShowing()) {
+ if (Dependency.get(KeyguardStateController.class).isShowing()) {
dialog.getWindow().setType(LayoutParams.TYPE_STATUS_BAR_PANEL);
} else {
dialog.getWindow().setType(LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
@@ -138,20 +139,21 @@ public class SystemUIDialog extends AlertDialog {
private final Dialog mDialog;
private boolean mRegistered;
+ private final BroadcastDispatcher mBroadcastDispatcher;
DismissReceiver(Dialog dialog) {
mDialog = dialog;
+ mBroadcastDispatcher = Dependency.get(BroadcastDispatcher.class);
}
void register() {
- mDialog.getContext()
- .registerReceiverAsUser(this, UserHandle.CURRENT, INTENT_FILTER, null, null);
+ mBroadcastDispatcher.registerReceiver(this, INTENT_FILTER, null, UserHandle.CURRENT);
mRegistered = true;
}
void unregister() {
if (mRegistered) {
- mDialog.getContext().unregisterReceiver(this);
+ mBroadcastDispatcher.unregisterReceiver(this);
mRegistered = false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
deleted file mode 100644
index c76f93e5ebaa..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
+++ /dev/null
@@ -1,229 +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 com.android.systemui.statusbar.phone;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.hardware.biometrics.BiometricSourceType;
-import android.os.Build;
-import android.os.Trace;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.DejankUtils;
-import com.android.systemui.Dependency;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
-/**
- * Caches whether the current unlock method is insecure, taking trust into account. This information
- * might be a little bit out of date and should not be used for actual security decisions; it should
- * be only used for visual indications.
- */
-public class UnlockMethodCache {
-
- private static UnlockMethodCache sInstance;
- private static final boolean DEBUG_AUTH_WITH_ADB = false;
- private static final String AUTH_BROADCAST_KEY = "debug_trigger_auth";
-
- private final LockPatternUtils mLockPatternUtils;
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final ArrayList<OnUnlockMethodChangedListener> mListeners = new ArrayList<>();
- /** Whether the user configured a secure unlock method (PIN, password, etc.) */
- private boolean mSecure;
- /** Whether the unlock method is currently insecure (insecure method or trusted environment) */
- private boolean mCanSkipBouncer;
- private boolean mTrustManaged;
- private boolean mTrusted;
- private boolean mDebugUnlocked = false;
- private boolean mFaceAuthEnabled;
-
- private UnlockMethodCache(Context ctx) {
- mLockPatternUtils = new LockPatternUtils(ctx);
- mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
- Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mCallback);
- update(true /* updateAlways */);
- if (Build.IS_DEBUGGABLE && DEBUG_AUTH_WITH_ADB) {
- // Watch for interesting updates
- final IntentFilter filter = new IntentFilter();
- filter.addAction(AUTH_BROADCAST_KEY);
- ctx.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (DEBUG_AUTH_WITH_ADB && AUTH_BROADCAST_KEY.equals(intent.getAction())) {
- mDebugUnlocked = !mDebugUnlocked;
- update(true /* updateAlways */);
- }
- }
- }, filter, null, null);
- }
- }
-
- public static UnlockMethodCache getInstance(Context context) {
- if (sInstance == null) {
- sInstance = new UnlockMethodCache(context);
- }
- return sInstance;
- }
-
- /**
- * @return whether the user configured a secure unlock method like PIN, password, etc.
- */
- public boolean isMethodSecure() {
- return mSecure;
- }
-
- public boolean isTrusted() {
- return mTrusted;
- }
-
- /**
- * @return whether the lockscreen is currently insecure, and the bouncer won't be shown
- */
- public boolean canSkipBouncer() {
- return mCanSkipBouncer;
- }
-
- public void addListener(OnUnlockMethodChangedListener listener) {
- mListeners.add(listener);
- }
-
- public void removeListener(OnUnlockMethodChangedListener listener) {
- mListeners.remove(listener);
- }
-
- /**
- * If there are faces enrolled and user enabled face auth on keyguard.
- */
- public boolean isFaceAuthEnabled() {
- return mFaceAuthEnabled;
- }
-
- private void update(boolean updateAlways) {
- Trace.beginSection("UnlockMethodCache#update");
- int user = KeyguardUpdateMonitor.getCurrentUser();
- boolean secure = mLockPatternUtils.isSecure(user);
- boolean canSkipBouncer = !secure || mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)
- || (Build.IS_DEBUGGABLE && DEBUG_AUTH_WITH_ADB && mDebugUnlocked);
- boolean trustManaged = mKeyguardUpdateMonitor.getUserTrustIsManaged(user);
- boolean trusted = mKeyguardUpdateMonitor.getUserHasTrust(user);
- boolean faceAuthEnabled = mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(user);
- boolean changed = secure != mSecure || canSkipBouncer != mCanSkipBouncer
- || trustManaged != mTrustManaged
- || mFaceAuthEnabled != faceAuthEnabled;
- if (changed || updateAlways) {
- mSecure = secure;
- mCanSkipBouncer = canSkipBouncer;
- mTrusted = trusted;
- mTrustManaged = trustManaged;
- mFaceAuthEnabled = faceAuthEnabled;
- Trace.endSection();
- notifyListeners();
- } else {
- Trace.endSection();
- }
- }
-
- private void notifyListeners() {
- String tag = "UnlockMethodCache#notifyListeners";
- DejankUtils.startDetectingBlockingIpcs(tag);
- for (OnUnlockMethodChangedListener listener : mListeners) {
- listener.onUnlockMethodStateChanged();
- }
- DejankUtils.stopDetectingBlockingIpcs(tag);
- }
-
- public void dump(PrintWriter pw) {
- pw.println("UnlockMethodCache");
- pw.println(" mSecure: " + mSecure);
- pw.println(" mCanSkipBouncer: " + mCanSkipBouncer);
- pw.println(" mTrustManaged: " + mTrustManaged);
- pw.println(" mTrusted: " + mTrusted);
- pw.println(" mDebugUnlocked: " + mDebugUnlocked);
- pw.println(" mFaceAuthEnabled: " + mFaceAuthEnabled);
- }
-
- private final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
- @Override
- public void onUserSwitchComplete(int userId) {
- update(false /* updateAlways */);
- }
-
- @Override
- public void onTrustChanged(int userId) {
- update(false /* updateAlways */);
- }
-
- @Override
- public void onTrustManagedChanged(int userId) {
- update(false /* updateAlways */);
- }
-
- @Override
- public void onStartedWakingUp() {
- update(false /* updateAlways */);
- }
-
- @Override
- public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
- Trace.beginSection("KeyguardUpdateMonitorCallback#onBiometricAuthenticated");
- if (!mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed()) {
- Trace.endSection();
- return;
- }
- update(false /* updateAlways */);
- Trace.endSection();
- }
-
- @Override
- public void onFaceUnlockStateChanged(boolean running, int userId) {
- update(false /* updateAlways */);
- }
-
- @Override
- public void onStrongAuthStateChanged(int userId) {
- update(false /* updateAlways */);
- }
-
- @Override
- public void onScreenTurnedOff() {
- update(false /* updateAlways */);
- }
-
- @Override
- public void onKeyguardVisibilityChanged(boolean showing) {
- update(false /* updateAlways */);
- }
-
- @Override
- public void onBiometricsCleared() {
- update(false /* alwaysUpdate */);
- }
- };
-
- public boolean isTrustManaged() {
- return mTrustManaged;
- }
-
- public static interface OnUnlockMethodChangedListener {
- void onUnlockMethodStateChanged();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 111cdd2cc32a..738d076e13c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -45,9 +45,7 @@ public interface BatteryController extends DemoMode, Dumpable,
/**
* Returns {@code true} if AOD was disabled by power saving policies.
*/
- default boolean isAodPowerSave() {
- return isPowerSave();
- }
+ boolean isAodPowerSave();
/**
* A listener that will be notified whenever a change in battery level or power save mode has
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 0a2fb2e783a9..76683b6a5e6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -16,9 +16,6 @@
package com.android.systemui.statusbar.policy;
-import static com.android.systemui.Dependency.BG_LOOPER_NAME;
-import static com.android.systemui.Dependency.MAIN_LOOPER_NAME;
-
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.bluetooth.BluetoothAdapter;
@@ -36,6 +33,8 @@ import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.systemui.dagger.qualifiers.BgLooper;
+import com.android.systemui.dagger.qualifiers.MainLooper;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -46,7 +45,6 @@ import java.util.List;
import java.util.WeakHashMap;
import javax.inject.Inject;
-import javax.inject.Named;
import javax.inject.Singleton;
/**
@@ -74,9 +72,8 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
/**
*/
@Inject
- public BluetoothControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper,
- @Named(MAIN_LOOPER_NAME) Looper mainLooper,
- @Nullable LocalBluetoothManager localBluetoothManager) {
+ public BluetoothControllerImpl(Context context, @BgLooper Looper bgLooper,
+ @MainLooper Looper mainLooper, @Nullable LocalBluetoothManager localBluetoothManager) {
mLocalBluetoothManager = localBluetoothManager;
mBgHandler = new Handler(bgLooper);
mHandler = new H(mainLooper);
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 c2c3f81527e8..b331fc3bf0ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.policy;
+import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
+
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -44,6 +46,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
import com.android.systemui.SysUiServiceProvider;
+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;
@@ -60,6 +63,9 @@ import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
+import javax.inject.Inject;
+import javax.inject.Named;
+
/**
* Digital clock for the status bar.
*/
@@ -107,15 +113,20 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C
*/
private int mNonAdaptedColor;
- public Clock(Context context) {
- this(context, null);
- }
+ private final BroadcastDispatcher mBroadcastDispatcher;
public Clock(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
+ this(context, attrs, null);
+ }
+
+ @Inject
+ public Clock(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
+ BroadcastDispatcher broadcastDispatcher) {
+ this(context, attrs, 0, broadcastDispatcher);
}
- public Clock(Context context, AttributeSet attrs, int defStyle) {
+ public Clock(Context context, AttributeSet attrs, int defStyle,
+ BroadcastDispatcher broadcastDispatcher) {
super(context, attrs, defStyle);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
@@ -134,6 +145,7 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C
mCurrentUserId = newUserId;
}
};
+ mBroadcastDispatcher = broadcastDispatcher;
}
@Override
@@ -358,11 +370,11 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C
}
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
- mContext.registerReceiver(mScreenReceiver, filter);
+ mBroadcastDispatcher.registerReceiver(mScreenReceiver, filter);
}
} else {
if (mSecondsHandler != null) {
- mContext.unregisterReceiver(mScreenReceiver);
+ mBroadcastDispatcher.unregisterReceiver(mScreenReceiver);
mSecondsHandler.removeCallbacks(mSecondTick);
mSecondsHandler = null;
updateClock();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
index 089d5c924de8..0a40b3c4831c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
@@ -14,8 +14,6 @@
package com.android.systemui.statusbar.policy;
-import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
-
import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.Context;
@@ -26,12 +24,12 @@ import android.provider.Settings.Global;
import android.provider.Settings.Secure;
import android.util.Log;
+import com.android.systemui.dagger.qualifiers.MainHandler;
import com.android.systemui.settings.CurrentUserTracker;
import java.util.ArrayList;
import javax.inject.Inject;
-import javax.inject.Named;
import javax.inject.Singleton;
/**
@@ -51,8 +49,7 @@ public class DeviceProvisionedControllerImpl extends CurrentUserTracker implemen
/**
*/
@Inject
- public DeviceProvisionedControllerImpl(Context context,
- @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+ public DeviceProvisionedControllerImpl(Context context, @MainHandler Handler mainHandler) {
super(context);
mContext = context;
mContentResolver = context.getContentResolver();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java
index cade5dc46388..3ce62396fbb5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java
@@ -57,7 +57,7 @@ public interface ExtensionController {
ExtensionBuilder<T> withCallback(Consumer<T> callback);
ExtensionBuilder<T> withUiMode(int mode, Supplier<T> def);
ExtensionBuilder<T> withFeature(String feature, Supplier<T> def);
- Extension build();
+ Extension<T> build();
}
public interface PluginConverter<T, P> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
index fd030d133be0..eeef726ace75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
@@ -135,7 +135,7 @@ public class ExtensionControllerImpl implements ExtensionController {
}
@Override
- public ExtensionController.Extension build() {
+ public ExtensionController.Extension<T> build() {
// Sort items in ascending order
Collections.sort(mExtension.mProducers, Comparator.comparingInt(Item::sortOrder));
mExtension.notifyChanged();
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 b84dc476dd6f..6828c322dea2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -117,7 +117,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager {
}
protected boolean hasFullScreenIntent(@NonNull NotificationEntry entry) {
- return entry.notification.getNotification().fullScreenIntent != null;
+ return entry.getSbn().getNotification().fullScreenIntent != null;
}
protected void setEntryPinned(
@@ -206,7 +206,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager {
public void snooze() {
for (String key : mAlertEntries.keySet()) {
AlertEntry entry = getHeadsUpEntry(key);
- String packageName = entry.mEntry.notification.getPackageName();
+ String packageName = entry.mEntry.getSbn().getPackageName();
mSnoozedPackages.put(snoozeKey(packageName, mUser),
mClock.currentTimeMillis() + mSnoozeLengthMs);
}
@@ -328,8 +328,8 @@ public abstract class HeadsUpManager extends AlertingNotificationManager {
* one should be ranked higher and 0 if they are equal.
*/
public int compare(@NonNull NotificationEntry a, @NonNull NotificationEntry b) {
- AlertEntry aEntry = getHeadsUpEntry(a.key);
- AlertEntry bEntry = getHeadsUpEntry(b.key);
+ AlertEntry aEntry = getHeadsUpEntry(a.getKey());
+ AlertEntry bEntry = getHeadsUpEntry(b.getKey());
if (aEntry == null || bEntry == null) {
return aEntry == null ? 1 : -1;
}
@@ -341,7 +341,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager {
* until it's collapsed again.
*/
public void setExpanded(@NonNull NotificationEntry entry, boolean expanded) {
- HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key);
+ HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey());
if (headsUpEntry != null && entry.isRowPinned()) {
headsUpEntry.setExpanded(expanded);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index 4df0c6051f42..e8bc7db6d5c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.policy;
-import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
-
import android.app.ActivityManager;
import android.content.Context;
import android.net.ConnectivityManager;
@@ -26,12 +24,13 @@ import android.os.Handler;
import android.os.UserManager;
import android.util.Log;
+import com.android.systemui.dagger.qualifiers.MainHandler;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import javax.inject.Inject;
-import javax.inject.Named;
import javax.inject.Singleton;
/**
@@ -56,7 +55,7 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof
/**
*/
@Inject
- public HotspotControllerImpl(Context context, @Named(MAIN_HANDLER_NAME) Handler mainHandler) {
+ public HotspotControllerImpl(Context context, @MainHandler Handler mainHandler) {
mContext = context;
mConnectivityManager =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java
index be27741b4dc8..c6ae669d5d08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java
@@ -144,13 +144,13 @@ public class InflatedSmartReplies {
return false;
}
// If we are showing the spinner we don't want to add the buttons.
- boolean showingSpinner = entry.notification.getNotification()
+ boolean showingSpinner = entry.getSbn().getNotification()
.extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false);
if (showingSpinner) {
return false;
}
// If we are keeping the notification around while sending we don't want to add the buttons.
- boolean hideSmartReplies = entry.notification.getNotification()
+ boolean hideSmartReplies = entry.getSbn().getNotification()
.extras.getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false);
if (hideSmartReplies) {
return false;
@@ -168,7 +168,7 @@ public class InflatedSmartReplies {
public static SmartRepliesAndActions chooseSmartRepliesAndActions(
SmartReplyConstants smartReplyConstants,
final NotificationEntry entry) {
- Notification notification = entry.notification.getNotification();
+ Notification notification = entry.getSbn().getNotification();
Pair<RemoteInput, Notification.Action> remoteInputActionPair =
notification.findRemoteInputActionPair(false /* freeform */);
Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair =
@@ -177,7 +177,7 @@ public class InflatedSmartReplies {
if (!smartReplyConstants.isEnabled()) {
if (DEBUG) {
Log.d(TAG, "Smart suggestions not enabled, not adding suggestions for "
- + entry.notification.getKey());
+ + entry.getSbn().getKey());
}
return new SmartRepliesAndActions(null, null);
}
@@ -271,7 +271,7 @@ public class InflatedSmartReplies {
* through the remote input.
*/
public static boolean hasFreeformRemoteInput(NotificationEntry entry) {
- Notification notification = entry.notification.getNotification();
+ Notification notification = entry.getSbn().getNotification();
return null != notification.findRemoteInputActionPair(true /* freeform */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
deleted file mode 100644
index 6dc90b9028fa..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
+++ /dev/null
@@ -1,81 +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.statusbar.policy;
-
-import com.android.systemui.statusbar.policy.KeyguardMonitor.Callback;
-
-public interface KeyguardMonitor extends CallbackController<Callback> {
-
- boolean isSecure();
- boolean isShowing();
- boolean isOccluded();
- boolean isKeyguardFadingAway();
- boolean isKeyguardGoingAway();
- boolean isLaunchTransitionFadingAway();
- long getKeyguardFadingAwayDuration();
- long getKeyguardFadingAwayDelay();
- long calculateGoingToFullShadeDelay();
-
- /**
- * @return a shortened fading away duration similar to
- * {{@link #getKeyguardFadingAwayDuration()}} which may only span half of the duration, unless
- * we're bypassing
- */
- default long getShortenedFadingAwayDuration() {
- if (isBypassFadingAnimation()) {
- return getKeyguardFadingAwayDuration();
- } else {
- return getKeyguardFadingAwayDuration() / 2;
- }
- }
-
- default boolean isDeviceInteractive() {
- return false;
- }
-
- default void setLaunchTransitionFadingAway(boolean b) {
- }
-
- default void notifyKeyguardGoingAway(boolean b) {
- }
-
- /**
- * @return {@code true} if the current fading away animation is the fast bypass fading.
- */
- default boolean isBypassFadingAnimation() {
- return false;
- }
-
- /**
- * Notifies that the Keyguard is fading away with the specified timings.
- * @param delay the precalculated animation delay in milliseconds
- * @param fadeoutDuration the duration of the exit animation, in milliseconds
- * @param isBypassFading is this a fading away animation while bypassing
- */
- default void notifyKeyguardFadingAway(long delay, long fadeoutDuration,
- boolean isBypassFading) {
- }
-
- default void notifyKeyguardDoneFading() {
- }
-
- default void notifyKeyguardState(boolean showing, boolean methodSecure, boolean occluded) {
- }
-
- interface Callback {
- default void onKeyguardShowingChanged() {}
- default void onKeyguardFadingAwayChanged() {}
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
deleted file mode 100644
index e8b0f9b2864e..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
+++ /dev/null
@@ -1,183 +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 com.android.systemui.statusbar.policy;
-
-import android.annotation.NonNull;
-import android.content.Context;
-
-import com.android.internal.util.Preconditions;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.Dependency;
-
-import java.util.ArrayList;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/**
- */
-@Singleton
-public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback
- implements KeyguardMonitor {
-
- private final ArrayList<Callback> mCallbacks = new ArrayList<>();
-
- private final Context mContext;
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-
- private boolean mShowing;
- private boolean mSecure;
- private boolean mOccluded;
-
- private boolean mListening;
- private boolean mKeyguardFadingAway;
- private long mKeyguardFadingAwayDelay;
- private long mKeyguardFadingAwayDuration;
- private boolean mKeyguardGoingAway;
- private boolean mLaunchTransitionFadingAway;
- private boolean mBypassFadingAnimation;
-
- /**
- */
- @Inject
- public KeyguardMonitorImpl(Context context) {
- mContext = context;
- mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
- }
-
- @Override
- public void addCallback(@NonNull Callback callback) {
- Preconditions.checkNotNull(callback, "Callback must not be null. b/128895449");
- mCallbacks.add(callback);
- if (mCallbacks.size() != 0 && !mListening) {
- mListening = true;
- mKeyguardUpdateMonitor.registerCallback(this);
- }
- }
-
- @Override
- public void removeCallback(@NonNull Callback callback) {
- Preconditions.checkNotNull(callback, "Callback must not be null. b/128895449");
- if (mCallbacks.remove(callback) && mCallbacks.size() == 0 && mListening) {
- mListening = false;
- mKeyguardUpdateMonitor.removeCallback(this);
- }
- }
-
- @Override
- public boolean isShowing() {
- return mShowing;
- }
-
- @Override
- public boolean isSecure() {
- return mSecure;
- }
-
- @Override
- public boolean isOccluded() {
- return mOccluded;
- }
-
- public void notifyKeyguardState(boolean showing, boolean secure, boolean occluded) {
- if (mShowing == showing && mSecure == secure && mOccluded == occluded) return;
- mShowing = showing;
- mSecure = secure;
- mOccluded = occluded;
- notifyKeyguardChanged();
- }
-
- @Override
- public void onTrustChanged(int userId) {
- notifyKeyguardChanged();
- }
-
- public boolean isDeviceInteractive() {
- return mKeyguardUpdateMonitor.isDeviceInteractive();
- }
-
- private void notifyKeyguardChanged() {
- // Copy the list to allow removal during callback.
- new ArrayList<>(mCallbacks).forEach(Callback::onKeyguardShowingChanged);
- }
-
- public void notifyKeyguardFadingAway(long delay, long fadeoutDuration, boolean isBypassFading) {
- mKeyguardFadingAwayDelay = delay;
- mKeyguardFadingAwayDuration = fadeoutDuration;
- mBypassFadingAnimation = isBypassFading;
- setKeyguardFadingAway(true);
- }
-
- private void setKeyguardFadingAway(boolean keyguardFadingAway) {
- if (mKeyguardFadingAway != keyguardFadingAway) {
- mKeyguardFadingAway = keyguardFadingAway;
- ArrayList<Callback> callbacks = new ArrayList<>(mCallbacks);
- for (int i = 0; i < callbacks.size(); i++) {
- callbacks.get(i).onKeyguardFadingAwayChanged();
- }
- }
- }
-
- public void notifyKeyguardDoneFading() {
- mKeyguardGoingAway = false;
- setKeyguardFadingAway(false);
- }
-
- @Override
- public boolean isKeyguardFadingAway() {
- return mKeyguardFadingAway;
- }
-
- @Override
- public boolean isKeyguardGoingAway() {
- return mKeyguardGoingAway;
- }
-
- @Override
- public boolean isBypassFadingAnimation() {
- return mBypassFadingAnimation;
- }
-
- @Override
- public long getKeyguardFadingAwayDelay() {
- return mKeyguardFadingAwayDelay;
- }
-
- @Override
- public long getKeyguardFadingAwayDuration() {
- return mKeyguardFadingAwayDuration;
- }
-
- @Override
- public long calculateGoingToFullShadeDelay() {
- return mKeyguardFadingAwayDelay + mKeyguardFadingAwayDuration;
- }
-
- public void notifyKeyguardGoingAway(boolean keyguardGoingAway) {
- mKeyguardGoingAway = keyguardGoingAway;
- }
-
- public void setLaunchTransitionFadingAway(boolean fadingAway) {
- mLaunchTransitionFadingAway = fadingAway;
- }
-
- @Override
- public boolean isLaunchTransitionFadingAway() {
- return mLaunchTransitionFadingAway;
- }
-} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
new file mode 100644
index 000000000000..692c34c62dd8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
@@ -0,0 +1,177 @@
+/*
+ * 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.statusbar.policy;
+
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.policy.KeyguardStateController.Callback;
+
+/**
+ * Source of truth for keyguard state: If locked, occluded, has password, trusted etc.
+ */
+public interface KeyguardStateController extends CallbackController<Callback> {
+
+ /**
+ * If the device is locked or unlocked.
+ */
+ default boolean isUnlocked() {
+ return !isShowing() || canDismissLockScreen();
+ }
+
+ /**
+ * If the lock screen is visible.
+ * The keyguard is also visible when the device is asleep or in always on mode, except when
+ * the screen timed out and the user can unlock by quickly pressing power.
+ *
+ * This is unrelated to being locked or not.
+ *
+ * @see #isUnlocked()
+ * @see #canDismissLockScreen()
+ */
+ boolean isShowing();
+
+ /**
+ * If swiping up will unlock without asking for a password.
+ * @see #isUnlocked()
+ */
+ boolean canDismissLockScreen();
+
+ /**
+ * If the device has PIN/pattern/password or a lock screen at all.
+ */
+ boolean isMethodSecure();
+
+ /**
+ * When there's an {@link android.app.Activity} on top of the keyguard, where
+ * {@link android.app.Activity#setShowWhenLocked(boolean)} is true.
+ */
+ boolean isOccluded();
+
+ /**
+ * If a {@link android.service.trust.TrustAgentService} is keeping the device unlocked.
+ * {@link #canDismissLockScreen()} is better source of truth that also considers this state.
+ */
+ boolean isTrusted();
+
+ /**
+ * If the keyguard dismissal animation is running.
+ * @see #isKeyguardGoingAway()
+ */
+ boolean isKeyguardFadingAway();
+
+ /**
+ * When the keyguard challenge was successfully solved, and {@link android.app.ActivityManager}
+ * is launching the activity that will be revealed.
+ *
+ * This also includes the animation of the keyguard being dismissed, meaning that this will
+ * return {@code true} whenever {@link #isKeyguardFadingAway()} also returns {@code true}.
+ */
+ boolean isKeyguardGoingAway();
+
+ /**
+ * @return a shortened fading away duration similar to
+ * {{@link #getKeyguardFadingAwayDuration()}} which may only span half of the duration, unless
+ * we're bypassing
+ */
+ default long getShortenedFadingAwayDuration() {
+ if (isBypassFadingAnimation()) {
+ return getKeyguardFadingAwayDuration();
+ } else {
+ return getKeyguardFadingAwayDuration() / 2;
+ }
+ }
+
+ /**
+ * @return {@code true} if the current fading away animation is the fast bypass fading.
+ */
+ default boolean isBypassFadingAnimation() {
+ return false;
+ }
+
+ /**
+ * Notifies that the Keyguard is fading away with the specified timings.
+ * @param delay the precalculated animation delay in milliseconds
+ * @param fadeoutDuration the duration of the exit animation, in milliseconds
+ * @param isBypassFading is this a fading away animation while bypassing
+ */
+ default void notifyKeyguardFadingAway(long delay, long fadeoutDuration,
+ boolean isBypassFading) {
+ }
+
+ /**
+ * If there are faces enrolled and user enabled face auth on keyguard.
+ */
+ default boolean isFaceAuthEnabled() {
+ return false;
+ }
+
+ /**
+ * If the animation that morphs a notification into an app window is playing.
+ */
+ boolean isLaunchTransitionFadingAway();
+
+ /**
+ * How long the keyguard dismissal animation should take when unlocking.
+ */
+ long getKeyguardFadingAwayDuration();
+
+ /**
+ * Delay for {@link #getKeyguardFadingAwayDuration()}.
+ */
+ long getKeyguardFadingAwayDelay();
+
+ /**
+ * Delay when going from {@link StatusBarState#KEYGUARD} to {@link StatusBarState#SHADE} or
+ * {@link StatusBarState#SHADE_LOCKED}.
+ */
+ long calculateGoingToFullShadeDelay();
+
+ /** **/
+ default void setLaunchTransitionFadingAway(boolean b) {}
+ /** **/
+ default void notifyKeyguardGoingAway(boolean b) {}
+ /** **/
+ default void notifyKeyguardDoneFading() {}
+ /** **/
+ default void notifyKeyguardState(boolean showing, boolean occluded) {}
+
+ /**
+ * Callback for authentication events.
+ */
+ interface Callback {
+ /**
+ * Called when the locked state of the device changes. The lock screen might still be
+ * showing on some cases, like when a {@link android.service.trust.TrustAgentService} is
+ * active, or face auth was triggered but the user didn't swipe up to dismiss the lock
+ * screen yet.
+ */
+ default void onUnlockedChanged() {}
+
+ /**
+ * If the lock screen is active or not. This is different from being locked, since the lock
+ * screen can be visible but unlocked by {@link android.service.trust.TrustAgentService} or
+ * face unlock.
+ *
+ * @see #isShowing()
+ */
+ default void onKeyguardShowingChanged() {}
+
+ /**
+ * Triggered when the device was just unlocked and the lock screen is being dismissed.
+ */
+ default void onKeyguardFadingAwayChanged() {}
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
new file mode 100644
index 000000000000..1cb2bd430199
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -0,0 +1,326 @@
+/*
+ * 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 com.android.systemui.statusbar.policy;
+
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.biometrics.BiometricSourceType;
+import android.os.Build;
+import android.os.Trace;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.internal.util.Preconditions;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.Dumpable;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ */
+@Singleton
+public class KeyguardStateControllerImpl implements KeyguardStateController, Dumpable {
+
+ private static final boolean DEBUG_AUTH_WITH_ADB = false;
+ private static final String AUTH_BROADCAST_KEY = "debug_trigger_auth";
+
+ private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final LockPatternUtils mLockPatternUtils;
+ private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
+ new UpdateMonitorCallback();
+
+ private boolean mCanDismissLockScreen;
+ private boolean mShowing;
+ private boolean mSecure;
+ private boolean mOccluded;
+
+ private boolean mKeyguardFadingAway;
+ private long mKeyguardFadingAwayDelay;
+ private long mKeyguardFadingAwayDuration;
+ private boolean mKeyguardGoingAway;
+ private boolean mLaunchTransitionFadingAway;
+ private boolean mBypassFadingAnimation;
+ private boolean mTrustManaged;
+ private boolean mTrusted;
+ private boolean mDebugUnlocked = false;
+ private boolean mFaceAuthEnabled;
+
+ /**
+ */
+ @Inject
+ public KeyguardStateControllerImpl(Context context,
+ KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils) {
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mLockPatternUtils = lockPatternUtils;
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
+
+ update(true /* updateAlways */);
+ if (Build.IS_DEBUGGABLE && DEBUG_AUTH_WITH_ADB) {
+ // Watch for interesting updates
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(AUTH_BROADCAST_KEY);
+ context.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG_AUTH_WITH_ADB && AUTH_BROADCAST_KEY.equals(intent.getAction())) {
+ mDebugUnlocked = !mDebugUnlocked;
+ update(true /* updateAlways */);
+ }
+ }
+ }, filter, null, null);
+ }
+ }
+
+ @Override
+ public void addCallback(@NonNull Callback callback) {
+ Preconditions.checkNotNull(callback, "Callback must not be null. b/128895449");
+ if (!mCallbacks.contains(callback)) {
+ mCallbacks.add(callback);
+ }
+ }
+
+ @Override
+ public void removeCallback(@NonNull Callback callback) {
+ Preconditions.checkNotNull(callback, "Callback must not be null. b/128895449");
+ mCallbacks.remove(callback);
+ }
+
+ @Override
+ public boolean isShowing() {
+ return mShowing;
+ }
+
+ @Override
+ public boolean isMethodSecure() {
+ return mSecure;
+ }
+
+ @Override
+ public boolean isOccluded() {
+ return mOccluded;
+ }
+
+ @Override
+ public boolean isTrusted() {
+ return mTrusted;
+ }
+
+ @Override
+ public void notifyKeyguardState(boolean showing, boolean occluded) {
+ if (mShowing == showing && mOccluded == occluded) return;
+ mShowing = showing;
+ mOccluded = occluded;
+ notifyKeyguardChanged();
+ }
+
+ private void notifyKeyguardChanged() {
+ Trace.beginSection("KeyguardStateController#notifyKeyguardChanged");
+ // Copy the list to allow removal during callback.
+ new ArrayList<>(mCallbacks).forEach(Callback::onKeyguardShowingChanged);
+ Trace.endSection();
+ }
+
+ private void notifyUnlockedChanged() {
+ Trace.beginSection("KeyguardStateController#notifyUnlockedChanged");
+ // Copy the list to allow removal during callback.
+ new ArrayList<>(mCallbacks).forEach(Callback::onUnlockedChanged);
+ Trace.endSection();
+ }
+
+ @Override
+ public void notifyKeyguardFadingAway(long delay, long fadeoutDuration, boolean isBypassFading) {
+ mKeyguardFadingAwayDelay = delay;
+ mKeyguardFadingAwayDuration = fadeoutDuration;
+ mBypassFadingAnimation = isBypassFading;
+ setKeyguardFadingAway(true);
+ }
+
+ private void setKeyguardFadingAway(boolean keyguardFadingAway) {
+ if (mKeyguardFadingAway != keyguardFadingAway) {
+ mKeyguardFadingAway = keyguardFadingAway;
+ ArrayList<Callback> callbacks = new ArrayList<>(mCallbacks);
+ for (int i = 0; i < callbacks.size(); i++) {
+ callbacks.get(i).onKeyguardFadingAwayChanged();
+ }
+ }
+ }
+
+ @Override
+ public void notifyKeyguardDoneFading() {
+ mKeyguardGoingAway = false;
+ setKeyguardFadingAway(false);
+ }
+
+ @VisibleForTesting
+ void update(boolean updateAlways) {
+ Trace.beginSection("KeyguardStateController#update");
+ int user = KeyguardUpdateMonitor.getCurrentUser();
+ boolean secure = mLockPatternUtils.isSecure(user);
+ boolean canDismissLockScreen = !secure || mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)
+ || (Build.IS_DEBUGGABLE && DEBUG_AUTH_WITH_ADB && mDebugUnlocked);
+ boolean trustManaged = mKeyguardUpdateMonitor.getUserTrustIsManaged(user);
+ boolean trusted = mKeyguardUpdateMonitor.getUserHasTrust(user);
+ boolean faceAuthEnabled = mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(user);
+ boolean changed = secure != mSecure || canDismissLockScreen != mCanDismissLockScreen
+ || trustManaged != mTrustManaged || mTrusted != trusted
+ || mFaceAuthEnabled != faceAuthEnabled;
+ if (changed || updateAlways) {
+ mSecure = secure;
+ mCanDismissLockScreen = canDismissLockScreen;
+ mTrusted = trusted;
+ mTrustManaged = trustManaged;
+ mFaceAuthEnabled = faceAuthEnabled;
+ notifyUnlockedChanged();
+ }
+ Trace.endSection();
+ }
+
+ @Override
+ public boolean canDismissLockScreen() {
+ return mCanDismissLockScreen;
+ }
+
+ @Override
+ public boolean isFaceAuthEnabled() {
+ return mFaceAuthEnabled;
+ }
+
+ @Override
+ public boolean isKeyguardFadingAway() {
+ return mKeyguardFadingAway;
+ }
+
+ @Override
+ public boolean isKeyguardGoingAway() {
+ return mKeyguardGoingAway;
+ }
+
+ @Override
+ public boolean isBypassFadingAnimation() {
+ return mBypassFadingAnimation;
+ }
+
+ @Override
+ public long getKeyguardFadingAwayDelay() {
+ return mKeyguardFadingAwayDelay;
+ }
+
+ @Override
+ public long getKeyguardFadingAwayDuration() {
+ return mKeyguardFadingAwayDuration;
+ }
+
+ @Override
+ public long calculateGoingToFullShadeDelay() {
+ return mKeyguardFadingAwayDelay + mKeyguardFadingAwayDuration;
+ }
+
+ @Override
+ public void notifyKeyguardGoingAway(boolean keyguardGoingAway) {
+ mKeyguardGoingAway = keyguardGoingAway;
+ }
+
+ @Override
+ public void setLaunchTransitionFadingAway(boolean fadingAway) {
+ mLaunchTransitionFadingAway = fadingAway;
+ }
+
+ @Override
+ public boolean isLaunchTransitionFadingAway() {
+ return mLaunchTransitionFadingAway;
+ }
+
+ /**
+ * Dumps internal state for debugging.
+ * @param pw Where to dump.
+ */
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("KeyguardStateController:");
+ pw.println(" mSecure: " + mSecure);
+ pw.println(" mCanDismissLockScreen: " + mCanDismissLockScreen);
+ pw.println(" mTrustManaged: " + mTrustManaged);
+ pw.println(" mTrusted: " + mTrusted);
+ pw.println(" mDebugUnlocked: " + mDebugUnlocked);
+ pw.println(" mFaceAuthEnabled: " + mFaceAuthEnabled);
+ }
+
+ private class UpdateMonitorCallback extends KeyguardUpdateMonitorCallback {
+ @Override
+ public void onUserSwitchComplete(int userId) {
+ update(false /* updateAlways */);
+ }
+
+ @Override
+ public void onTrustChanged(int userId) {
+ update(false /* updateAlways */);
+ notifyKeyguardChanged();
+ }
+
+ @Override
+ public void onTrustManagedChanged(int userId) {
+ update(false /* updateAlways */);
+ }
+
+ @Override
+ public void onStartedWakingUp() {
+ update(false /* updateAlways */);
+ }
+
+ @Override
+ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
+ Trace.beginSection("KeyguardUpdateMonitorCallback#onBiometricAuthenticated");
+ if (!mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed()) {
+ Trace.endSection();
+ return;
+ }
+ update(false /* updateAlways */);
+ Trace.endSection();
+ }
+
+ @Override
+ public void onFaceUnlockStateChanged(boolean running, int userId) {
+ update(false /* updateAlways */);
+ }
+
+ @Override
+ public void onStrongAuthStateChanged(int userId) {
+ update(false /* updateAlways */);
+ }
+
+ @Override
+ public void onKeyguardVisibilityChanged(boolean showing) {
+ update(false /* updateAlways */);
+ }
+
+ @Override
+ public void onBiometricsCleared() {
+ update(false /* alwaysUpdate */);
+ }
+ }
+}
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 683cdbb326dc..5a97eedd9b2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.policy;
import static com.android.settingslib.Utils.updateLocationEnabled;
-import static com.android.systemui.Dependency.BG_LOOPER_NAME;
import android.app.ActivityManager;
import android.app.AppOpsManager;
@@ -36,13 +35,13 @@ import android.provider.Settings;
import androidx.annotation.VisibleForTesting;
+import com.android.systemui.dagger.qualifiers.BgLooper;
import com.android.systemui.util.Utils;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
-import javax.inject.Named;
import javax.inject.Singleton;
/**
@@ -66,7 +65,7 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio
private final H mHandler = new H();
@Inject
- public LocationControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper) {
+ public LocationControllerImpl(Context context, @BgLooper Looper bgLooper) {
mContext = context;
// Register to listen for changes in location settings.
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 67739835a3fd..e36d28383bda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -24,7 +24,6 @@ import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OU
import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
import static com.android.internal.telephony.PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM;
-import static com.android.systemui.Dependency.BG_LOOPER_NAME;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -65,6 +64,7 @@ import com.android.systemui.ConfigurationChangedReceiver;
import com.android.systemui.DemoMode;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.BgLooper;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.statusbar.policy.MobileSignalController.MobileIconGroup;
@@ -81,7 +81,6 @@ import java.util.Locale;
import java.util.Map;
import javax.inject.Inject;
-import javax.inject.Named;
import javax.inject.Singleton;
/** Platform implementation of the network controller. **/
@@ -172,7 +171,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
* Construct this controller object and register for updates.
*/
@Inject
- public NetworkControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper,
+ public NetworkControllerImpl(Context context, @BgLooper Looper bgLooper,
DeviceProvisionedController deviceProvisionedController) {
this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE),
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE),
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 43795dc08c91..502a9bd34b12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -23,12 +23,16 @@ import android.app.ActivityManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.RemoteInput;
+import android.content.ClipDescription;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.pm.ShortcutManager;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.net.Uri;
import android.os.Bundle;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.text.Editable;
@@ -53,8 +57,13 @@ import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
+import androidx.core.view.inputmethod.EditorInfoCompat;
+import androidx.core.view.inputmethod.InputConnectionCompat;
+import androidx.core.view.inputmethod.InputContentInfoCompat;
+
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -65,6 +74,7 @@ import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewW
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.LightBarController;
+import java.util.HashMap;
import java.util.function.Consumer;
/**
@@ -88,6 +98,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
private RemoteInputController mController;
private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
+ private IStatusBarService mStatusBarManagerService;
+
private NotificationEntry mEntry;
private boolean mRemoved;
@@ -103,6 +115,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
public RemoteInputView(Context context, AttributeSet attrs) {
super(context, attrs);
mRemoteInputQuickSettingsDisabler = Dependency.get(RemoteInputQuickSettingsDisabler.class);
+ mStatusBarManagerService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
}
@Override
@@ -128,7 +142,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
if (isSoftImeEvent || isKeyboardEnterKey) {
if (mEditText.length() > 0) {
- sendRemoteInput();
+ sendRemoteInput(prepareRemoteInputFromText());
}
// Consume action to prevent IME from closing.
return true;
@@ -141,7 +155,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
mEditText.mRemoteInputView = this;
}
- private void sendRemoteInput() {
+ protected Intent prepareRemoteInputFromText() {
Bundle results = new Bundle();
results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString());
Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -153,12 +167,31 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_CHOICE);
}
+ return fillInIntent;
+ }
+
+ protected Intent prepareRemoteInputFromData(String contentType, Uri data) {
+ HashMap<String, Uri> results = new HashMap<>();
+ results.put(contentType, data);
+ try {
+ mStatusBarManagerService.grantInlineReplyUriPermission(
+ mEntry.getSbn().getKey(), data);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to grant URI permissions:" + e.getMessage(), e);
+ }
+ Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ RemoteInput.addDataResultToIntent(mRemoteInput, fillInIntent, results);
+
+ return fillInIntent;
+ }
+
+ private void sendRemoteInput(Intent intent) {
mEditText.setEnabled(false);
mSendButton.setVisibility(INVISIBLE);
mProgressBar.setVisibility(VISIBLE);
mEntry.remoteInputText = mEditText.getText();
mEntry.lastRemoteInputSent = SystemClock.elapsedRealtime();
- mController.addSpinning(mEntry.key, mToken);
+ mController.addSpinning(mEntry.getKey(), mToken);
mController.removeRemoteInput(mEntry, mToken);
mEditText.mShowImeOnInputConnection = false;
mController.remoteInputSent(mEntry);
@@ -170,17 +203,17 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
// but that's an edge case, and also because we can't always know which package will receive
// an intent, so we just reset for the publisher.
getContext().getSystemService(ShortcutManager.class).onApplicationActive(
- mEntry.notification.getPackageName(),
- mEntry.notification.getUser().getIdentifier());
+ mEntry.getSbn().getPackageName(),
+ mEntry.getSbn().getUser().getIdentifier());
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_SEND,
- mEntry.notification.getPackageName());
+ mEntry.getSbn().getPackageName());
try {
- mPendingIntent.send(mContext, 0, fillInIntent);
+ mPendingIntent.send(mContext, 0, intent);
} catch (PendingIntent.CanceledException e) {
Log.i(TAG, "Unable to send remote input result", e);
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_FAIL,
- mEntry.notification.getPackageName());
+ mEntry.getSbn().getPackageName());
}
}
@@ -195,7 +228,9 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
LayoutInflater.from(context).inflate(R.layout.remote_input, root, false);
v.mController = controller;
v.mEntry = entry;
- v.mEditText.setTextOperationUser(computeTextOperationUser(entry.notification.getUser()));
+ UserHandle user = computeTextOperationUser(entry.getSbn().getUser());
+ v.mEditText.mUser = user;
+ v.mEditText.setTextOperationUser(user);
v.setTag(VIEW_TAG);
return v;
@@ -204,7 +239,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
@Override
public void onClick(View v) {
if (v == mSendButton) {
- sendRemoteInput();
+ sendRemoteInput(prepareRemoteInputFromText());
}
}
@@ -249,7 +284,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
mRemoteInputQuickSettingsDisabler.setRemoteInputActive(false);
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_CLOSE,
- mEntry.notification.getPackageName());
+ mEntry.getSbn().getPackageName());
}
@Override
@@ -269,7 +304,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
return;
}
mController.removeRemoteInput(mEntry, mToken);
- mController.removeSpinning(mEntry.key, mToken);
+ mController.removeSpinning(mEntry.getKey(), mToken);
}
public void setPendingIntent(PendingIntent pendingIntent) {
@@ -314,7 +349,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
public void focus() {
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_OPEN,
- mEntry.notification.getPackageName());
+ mEntry.getSbn().getPackageName());
setVisibility(VISIBLE);
if (mWrapper != null) {
@@ -353,7 +388,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
mEditText.setEnabled(true);
mSendButton.setVisibility(VISIBLE);
mProgressBar.setVisibility(INVISIBLE);
- mController.removeSpinning(mEntry.key, mToken);
+ mController.removeSpinning(mEntry.getKey(), mToken);
updateSendButton();
onDefocus(false /* animate */);
@@ -505,7 +540,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
}
public boolean isSending() {
- return getVisibility() == VISIBLE && mController.isSpinning(mEntry.key, mToken);
+ return getVisibility() == VISIBLE && mController.isSpinning(mEntry.getKey(), mToken);
}
/**
@@ -518,6 +553,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
private RemoteInputView mRemoteInputView;
boolean mShowImeOnInputConnection;
private LightBarController mLightBarController;
+ UserHandle mUser;
public RemoteEditText(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -617,11 +653,47 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+ String[] allowedDataTypes = mRemoteInputView.mRemoteInput.getAllowedDataTypes()
+ .toArray(new String[0]);
+ EditorInfoCompat.setContentMimeTypes(outAttrs, allowedDataTypes);
final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
- if (mShowImeOnInputConnection && inputConnection != null) {
+ final InputConnectionCompat.OnCommitContentListener callback =
+ new InputConnectionCompat.OnCommitContentListener() {
+ @Override
+ public boolean onCommitContent(
+ InputContentInfoCompat inputContentInfoCompat, int i,
+ Bundle bundle) {
+ Uri contentUri = inputContentInfoCompat.getContentUri();
+ ClipDescription description = inputContentInfoCompat.getDescription();
+ String mimeType = null;
+ if (description != null && description.getMimeTypeCount() > 0) {
+ mimeType = description.getMimeType(0);
+ }
+ if (mimeType != null) {
+ Intent dataIntent = mRemoteInputView.prepareRemoteInputFromData(
+ mimeType, contentUri);
+ mRemoteInputView.sendRemoteInput(dataIntent);
+ }
+ return true;
+ }
+ };
+
+ InputConnection ic = inputConnection == null ? null :
+ InputConnectionCompat.createWrapper(inputConnection, outAttrs, callback);
+
+ Context userContext = null;
+ try {
+ userContext = mContext.createPackageContextAsUser(
+ mContext.getPackageName(), 0, mUser);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Unable to create user context:" + e.getMessage(), e);
+ }
+
+ if (mShowImeOnInputConnection && ic != null) {
+ Context targetContext = userContext != null ? userContext : getContext();
final InputMethodManager imm =
- getContext().getSystemService(InputMethodManager.class);
+ targetContext.getSystemService(InputMethodManager.class);
if (imm != null) {
// onCreateInputConnection is called by InputMethodManager in the middle of
// setting up the connection to the IME; wait with requesting the IME until that
@@ -636,7 +708,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
}
}
- return inputConnection;
+ return ic;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index d88ae78c5afb..6edd75b20fed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -15,8 +15,6 @@
*/
package com.android.systemui.statusbar.policy;
-import static com.android.systemui.Dependency.BG_HANDLER_NAME;
-
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
@@ -50,6 +48,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.BgHandler;
import com.android.systemui.settings.CurrentUserTracker;
import java.io.FileDescriptor;
@@ -57,7 +56,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import javax.inject.Inject;
-import javax.inject.Named;
import javax.inject.Singleton;
/**
@@ -102,7 +100,7 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi
/**
*/
@Inject
- public SecurityControllerImpl(Context context, @Named(BG_HANDLER_NAME) Handler bgHandler) {
+ public SecurityControllerImpl(Context context, @BgHandler Handler bgHandler) {
this(context, bgHandler, null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
index 655c29cbf687..347d3009c3ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.policy;
-import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
-
import android.app.RemoteInput;
import android.content.Context;
import android.content.res.Resources;
@@ -30,9 +28,9 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.MainHandler;
import javax.inject.Inject;
-import javax.inject.Named;
import javax.inject.Singleton;
@Singleton
@@ -67,7 +65,7 @@ public final class SmartReplyConstants {
private final KeyValueListParser mParser = new KeyValueListParser(',');
@Inject
- public SmartReplyConstants(@Named(MAIN_HANDLER_NAME) Handler handler, Context context) {
+ public SmartReplyConstants(@MainHandler Handler handler, Context context) {
mHandler = handler;
mContext = context;
final Resources resources = mContext.getResources();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index b5f660a84043..65bb28ffbe39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -350,7 +350,7 @@ public class SmartReplyView extends ViewGroup {
() -> {
smartReplyController.smartActionClicked(
entry, actionIndex, action, smartActions.fromAssistant);
- headsUpManager.removeNotification(entry.key, true);
+ headsUpManager.removeNotification(entry.getKey(), true);
}, entry.getRow());
if (useDelayedOnClickListener) {
onClickListener = new DelayedOnClickListener(onClickListener,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 4fa4b6c456e2..f2d2faed6a42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.policy;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import static com.android.systemui.DejankUtils.whitelistIpcs;
-import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
import android.app.ActivityManager;
import android.app.Dialog;
@@ -58,11 +57,11 @@ import com.android.systemui.Prefs;
import com.android.systemui.Prefs.Key;
import com.android.systemui.R;
import com.android.systemui.SystemUISecondaryUserService;
+import com.android.systemui.dagger.qualifiers.MainHandler;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.qs.tiles.UserDetailView;
import com.android.systemui.statusbar.phone.SystemUIDialog;
-import com.android.systemui.statusbar.phone.UnlockMethodCache;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -71,7 +70,6 @@ import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
-import javax.inject.Named;
import javax.inject.Singleton;
/**
@@ -93,7 +91,7 @@ public class UserSwitcherController implements Dumpable {
private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>();
private final GuestResumeSessionReceiver mGuestResumeSessionReceiver
= new GuestResumeSessionReceiver();
- private final KeyguardMonitor mKeyguardMonitor;
+ private final KeyguardStateController mKeyguardStateController;
protected final Handler mHandler;
private final ActivityStarter mActivityStarter;
@@ -110,13 +108,13 @@ public class UserSwitcherController implements Dumpable {
private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
@Inject
- public UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor,
- @Named(MAIN_HANDLER_NAME) Handler handler, ActivityStarter activityStarter) {
+ public UserSwitcherController(Context context, KeyguardStateController keyguardStateController,
+ @MainHandler Handler handler, ActivityStarter activityStarter) {
mContext = context;
if (!UserManager.isGuestUserEphemeral()) {
mGuestResumeSessionReceiver.register(context);
}
- mKeyguardMonitor = keyguardMonitor;
+ mKeyguardStateController = keyguardStateController;
mHandler = handler;
mActivityStarter = activityStarter;
mUserManager = UserManager.get(context);
@@ -149,7 +147,7 @@ public class UserSwitcherController implements Dumpable {
// Fetch initial values.
mSettingsObserver.onChange(false);
- keyguardMonitor.addCallback(mCallback);
+ keyguardStateController.addCallback(mCallback);
listenForCallState();
refreshUsers(UserHandle.USER_NULL);
@@ -315,7 +313,6 @@ public class UserSwitcherController implements Dumpable {
// adb shell settings put system enable_fullscreen_user_switcher 1 # Turn it on.
// Restart SystemUI or adb reboot.
final int DEFAULT = -1;
- // TODO(b/140061064)
final int overrideUseFullscreenUserSwitcher =
whitelistIpcs(() -> Settings.System.getInt(mContext.getContentResolver(),
"enable_fullscreen_user_switcher", DEFAULT));
@@ -597,20 +594,18 @@ public class UserSwitcherController implements Dumpable {
public static abstract class BaseUserAdapter extends BaseAdapter {
final UserSwitcherController mController;
- private final KeyguardMonitor mKeyguardMonitor;
- private final UnlockMethodCache mUnlockMethodCache;
+ private final KeyguardStateController mKeyguardStateController;
protected BaseUserAdapter(UserSwitcherController controller) {
mController = controller;
- mKeyguardMonitor = controller.mKeyguardMonitor;
- mUnlockMethodCache = UnlockMethodCache.getInstance(controller.mContext);
+ mKeyguardStateController = controller.mKeyguardStateController;
controller.addAdapter(new WeakReference<>(this));
}
public int getUserCount() {
- boolean secureKeyguardShowing = mKeyguardMonitor.isShowing()
- && mKeyguardMonitor.isSecure()
- && !mUnlockMethodCache.canSkipBouncer();
+ boolean secureKeyguardShowing = mKeyguardStateController.isShowing()
+ && mKeyguardStateController.isMethodSecure()
+ && !mKeyguardStateController.canDismissLockScreen();
if (!secureKeyguardShowing) {
return mController.getUsers().size();
}
@@ -630,9 +625,9 @@ public class UserSwitcherController implements Dumpable {
@Override
public int getCount() {
- boolean secureKeyguardShowing = mKeyguardMonitor.isShowing()
- && mKeyguardMonitor.isSecure()
- && !mUnlockMethodCache.canSkipBouncer();
+ boolean secureKeyguardShowing = mKeyguardStateController.isShowing()
+ && mKeyguardStateController.isMethodSecure()
+ && !mKeyguardStateController.canDismissLockScreen();
if (!secureKeyguardShowing) {
return mController.getUsers().size();
}
@@ -819,19 +814,21 @@ public class UserSwitcherController implements Dumpable {
}
};
- private final KeyguardMonitor.Callback mCallback = new KeyguardMonitor.Callback() {
- @Override
- public void onKeyguardShowingChanged() {
+ private final KeyguardStateController.Callback mCallback =
+ new KeyguardStateController.Callback() {
+ @Override
+ public void onKeyguardShowingChanged() {
- // When Keyguard is going away, we don't need to update our items immediately which
- // helps making the transition faster.
- if (!mKeyguardMonitor.isShowing()) {
- mHandler.post(UserSwitcherController.this::notifyAdapters);
- } else {
- notifyAdapters();
- }
- }
- };
+ // When Keyguard is going away, we don't need to update our items immediately
+ // which
+ // helps making the transition faster.
+ if (!mKeyguardStateController.isShowing()) {
+ mHandler.post(UserSwitcherController.this::notifyAdapters);
+ } else {
+ notifyAdapters();
+ }
+ }
+ };
private final class ExitGuestDialog extends SystemUIDialog implements
DialogInterface.OnClickListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index 29c42d24b3b4..1c7a1951b27d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.policy;
-import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
-
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.NotificationManager;
@@ -41,6 +39,7 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
+import com.android.systemui.dagger.qualifiers.MainHandler;
import com.android.systemui.qs.GlobalSetting;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.util.Utils;
@@ -51,7 +50,6 @@ import java.util.ArrayList;
import java.util.Objects;
import javax.inject.Inject;
-import javax.inject.Named;
import javax.inject.Singleton;
/** Platform implementation of the zen mode controller. **/
@@ -79,7 +77,7 @@ public class ZenModeControllerImpl extends CurrentUserTracker
private NotificationManager.Policy mConsolidatedNotificationPolicy;
@Inject
- public ZenModeControllerImpl(Context context, @Named(MAIN_HANDLER_NAME) Handler handler) {
+ public ZenModeControllerImpl(Context context, @MainHandler Handler handler) {
super(context);
mContext = context;
mModeSetting = new GlobalSetting(mContext, handler, Global.ZEN_MODE) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java
new file mode 100644
index 000000000000..d6d0a3603c25
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.tv;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.WindowManager;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+class AudioRecordingDisclosureBar {
+ private static final String TAG = "AudioRecordingDisclosureBar";
+ private static final boolean DEBUG = false;
+
+ private static final String LAYOUT_PARAMS_TITLE = "AudioRecordingDisclosureBar";
+ private static final int ANIM_DURATION_MS = 150;
+
+ private final Context mContext;
+ private final List<String> mAudioRecordingApps = new ArrayList<>();
+ private View mView;
+ private ViewGroup mAppsInfoContainer;
+
+ AudioRecordingDisclosureBar(Context context) {
+ mContext = context;
+ }
+
+ void start() {
+ // Inflate and add audio recording disclosure bar
+ createView();
+
+ // Register AppOpsManager callback
+ final AppOpsManager appOpsManager = (AppOpsManager) mContext.getSystemService(
+ Context.APP_OPS_SERVICE);
+ appOpsManager.startWatchingActive(
+ new String[]{AppOpsManager.OPSTR_RECORD_AUDIO}, mContext.getMainExecutor(),
+ new OnActiveRecordingListener());
+ }
+
+ private void createView() {
+ mView = View.inflate(mContext,
+ R.layout.tv_status_bar_audio_recording, null);
+ mAppsInfoContainer = mView.findViewById(R.id.container);
+
+ final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
+ MATCH_PARENT,
+ WRAP_CONTENT,
+ WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSLUCENT);
+ layoutParams.gravity = Gravity.BOTTOM;
+ layoutParams.setTitle(LAYOUT_PARAMS_TITLE);
+ layoutParams.packageName = mContext.getPackageName();
+
+ final WindowManager windowManager = (WindowManager) mContext.getSystemService(
+ Context.WINDOW_SERVICE);
+ windowManager.addView(mView, layoutParams);
+
+ // Set invisible first util it gains its actual size and we are able to hide it by moving
+ // off the screen
+ mView.setVisibility(View.INVISIBLE);
+ mView.getViewTreeObserver().addOnGlobalLayoutListener(
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ // Now that we get the height, we can move the bar off ("below") the screen
+ final int height = mView.getHeight();
+ mView.setTranslationY(height);
+ // ... and make it visible
+ mView.setVisibility(View.VISIBLE);
+ // Remove the observer
+ mView.getViewTreeObserver()
+ .removeOnGlobalLayoutListener(this);
+ }
+ });
+ }
+
+ private void showAudioRecordingDisclosureBar() {
+ mView.animate()
+ .translationY(0f)
+ .setDuration(ANIM_DURATION_MS)
+ .start();
+ }
+
+ private void addToAudioRecordingDisclosureBar(String packageName) {
+ final PackageManager pm = mContext.getPackageManager();
+ final ApplicationInfo appInfo;
+ try {
+ appInfo = pm.getApplicationInfo(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ return;
+ }
+ final CharSequence label = pm.getApplicationLabel(appInfo);
+ final Drawable icon = pm.getApplicationIcon(appInfo);
+
+ final View view = LayoutInflater.from(mContext).inflate(R.layout.tv_item_app_info,
+ mAppsInfoContainer, false);
+ ((TextView) view.findViewById(R.id.title)).setText(label);
+ ((ImageView) view.findViewById(R.id.icon)).setImageDrawable(icon);
+
+ mAppsInfoContainer.addView(view);
+ }
+
+ private void removeFromAudioRecordingDisclosureBar(int index) {
+ mAppsInfoContainer.removeViewAt(index);
+ }
+
+ private void hideAudioRecordingDisclosureBar() {
+ mView.animate()
+ .translationY(mView.getHeight())
+ .setDuration(ANIM_DURATION_MS)
+ .start();
+ }
+
+ private class OnActiveRecordingListener implements AppOpsManager.OnOpActiveChangedListener {
+ private final List<String> mExemptApps;
+
+ private OnActiveRecordingListener() {
+ mExemptApps = Arrays.asList(mContext.getResources().getStringArray(
+ R.array.audio_recording_disclosure_exempt_apps));
+ }
+
+ @Override
+ public void onOpActiveChanged(String op, int uid, String packageName, boolean active) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "OP_RECORD_AUDIO active change, active" + active + ", app=" + packageName);
+ }
+
+ if (mExemptApps.contains(packageName)) {
+ if (DEBUG) {
+ Log.d(TAG, "\t- exempt app");
+ }
+ return;
+ }
+
+ final boolean alreadyTracking = mAudioRecordingApps.contains(packageName);
+ if ((active && alreadyTracking) || (!active && !alreadyTracking)) {
+ if (DEBUG) {
+ Log.d(TAG, "\t- nothing changed");
+ }
+ return;
+ }
+
+ if (active) {
+ if (DEBUG) {
+ Log.d(TAG, "\t- new recording app");
+ }
+
+ if (mAudioRecordingApps.isEmpty()) {
+ showAudioRecordingDisclosureBar();
+ }
+
+ mAudioRecordingApps.add(packageName);
+ addToAudioRecordingDisclosureBar(packageName);
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "\t- not recording any more");
+ }
+
+ final int index = mAudioRecordingApps.indexOf(packageName);
+ removeFromAudioRecordingDisclosureBar(index);
+ mAudioRecordingApps.remove(index);
+
+ if (mAudioRecordingApps.isEmpty()) {
+ hideAudioRecordingDisclosureBar();
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/tv/OWNERS
new file mode 100644
index 000000000000..a601e9b86ef4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/OWNERS
@@ -0,0 +1,8 @@
+# Android TV Core Framework
+rgl@google.com
+valiiftime@google.com
+galinap@google.com
+patrikf@google.com
+robhor@google.com
+sergeynv@google.com
+
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 17d9cbe3af07..c2ed7df7cb2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -23,28 +23,37 @@ import android.os.ServiceManager;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.SystemUI;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.CommandQueue.Callbacks;
+
/**
- * Status bar implementation for "large screen" products that mostly present no on-screen nav
+ * Status bar implementation for "large screen" products that mostly present no on-screen nav.
+ * Serves as a collection of UI components, rather than showing its own UI.
+ * The following is the list of elements that constitute the TV-specific status bar:
+ * <ul>
+ * <li> {@link AudioRecordingDisclosureBar} - shown whenever applications are conducting audio
+ * recording, discloses the responsible applications </li>
+ * </ul>
*/
+public class TvStatusBar extends SystemUI implements CommandQueue.Callbacks {
-public class TvStatusBar extends SystemUI implements Callbacks {
-
- private IStatusBarService mBarService;
+ public TvStatusBar(Context context) {
+ super(context);
+ }
@Override
public void start() {
putComponent(TvStatusBar.class, this);
- CommandQueue commandQueue = getComponent(CommandQueue.class);
- commandQueue.addCallback(this);
- mBarService = IStatusBarService.Stub.asInterface(
+
+ final IStatusBarService barService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ final CommandQueue commandQueue = getComponent(CommandQueue.class);
+ commandQueue.addCallback(this);
try {
- mBarService.registerStatusBar(commandQueue);
+ barService.registerStatusBar(commandQueue);
} catch (RemoteException ex) {
// If the system process isn't there we're doomed anyway.
}
- }
+ new AudioRecordingDisclosureBar(mContext).start();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 89aa7979e7d8..9a58a355c586 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -61,6 +61,10 @@ public class ThemeOverlayController extends SystemUI {
private ThemeOverlayManager mThemeManager;
private UserManager mUserManager;
+ public ThemeOverlayController(Context context) {
+ super(context);
+ }
+
@Override
public void start() {
if (DEBUG) Log.d(TAG, "Start");
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index a55e2cf38e39..2d6027cb324d 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -15,8 +15,6 @@
*/
package com.android.systemui.tuner;
-import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
-
import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.Context;
@@ -36,6 +34,7 @@ import android.util.ArraySet;
import com.android.internal.util.ArrayUtils;
import com.android.systemui.DejankUtils;
import com.android.systemui.DemoMode;
+import com.android.systemui.dagger.qualifiers.MainHandler;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -46,7 +45,6 @@ import java.util.HashSet;
import java.util.Set;
import javax.inject.Inject;
-import javax.inject.Named;
import javax.inject.Singleton;
@@ -83,7 +81,7 @@ public class TunerServiceImpl extends TunerService {
/**
*/
@Inject
- public TunerServiceImpl(Context context, @Named(MAIN_HANDLER_NAME) Handler mainHandler,
+ public TunerServiceImpl(Context context, @MainHandler Handler mainHandler,
LeakDetector leakDetector) {
mContext = context;
mContentResolver = mContext.getContentResolver();
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index ff5bd03740bd..11885c55b51d 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -60,6 +60,10 @@ public class StorageNotification extends SystemUI {
private NotificationManager mNotificationManager;
private StorageManager mStorageManager;
+ public StorageNotification(Context context) {
+ super(context);
+ }
+
private static class MoveInfo {
public int moveId;
public Bundle extras;
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java
index 0a3e34ee951d..fd99ef389bec 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java
@@ -21,6 +21,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.PermissionChecker;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.hardware.usb.IUsbManager;
@@ -63,6 +64,7 @@ public class UsbConfirmActivity extends AlertActivity
mDevice = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
mAccessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
mResolveInfo = (ResolveInfo) intent.getParcelableExtra("rinfo");
+ String packageName = intent.getStringExtra(UsbManager.EXTRA_PACKAGE);
PackageManager packageManager = getPackageManager();
String appName = mResolveInfo.loadLabel(packageManager).toString();
@@ -74,8 +76,20 @@ public class UsbConfirmActivity extends AlertActivity
mAccessory.getDescription());
mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory);
} else {
- ap.mMessage = getString(R.string.usb_device_confirm_prompt, appName,
- mDevice.getProductName());
+ int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ boolean hasRecordPermission =
+ PermissionChecker.checkPermissionForPreflight(
+ this, android.Manifest.permission.RECORD_AUDIO, -1, uid,
+ packageName)
+ == android.content.pm.PackageManager.PERMISSION_GRANTED;
+ boolean isAudioCaptureDevice = mDevice.getHasAudioCapture();
+ boolean useRecordWarning = isAudioCaptureDevice && !hasRecordPermission;
+
+ int strID = useRecordWarning
+ ? R.string.usb_device_confirm_prompt_warn
+ : R.string.usb_device_confirm_prompt;
+
+ ap.mMessage = getString(strID, appName, mDevice.getProductName());
mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice);
}
ap.mPositiveButtonText = getString(android.R.string.ok);
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
index a46f018af816..47b56e097ec9 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
@@ -21,6 +21,7 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.PermissionChecker;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.hardware.usb.IUsbManager;
@@ -84,14 +85,27 @@ public class UsbPermissionActivity extends AlertActivity
final AlertController.AlertParams ap = mAlertParams;
ap.mTitle = appName;
if (mDevice == null) {
+ // Accessory Case
+
ap.mMessage = getString(R.string.usb_accessory_permission_prompt, appName,
mAccessory.getDescription());
mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory);
} else {
- ap.mMessage = getString(R.string.usb_device_permission_prompt, appName,
- mDevice.getProductName());
+ boolean hasRecordPermission =
+ PermissionChecker.checkPermissionForPreflight(
+ this, android.Manifest.permission.RECORD_AUDIO, -1, aInfo.uid,
+ mPackageName)
+ == android.content.pm.PackageManager.PERMISSION_GRANTED;
+ boolean isAudioCaptureDevice = mDevice.getHasAudioCapture();
+ boolean useRecordWarning = isAudioCaptureDevice && !hasRecordPermission;
+
+ int strID = useRecordWarning
+ ? R.string.usb_device_permission_prompt_warn
+ : R.string.usb_device_permission_prompt;
+ ap.mMessage = getString(strID, appName, mDevice.getProductName());
mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice);
}
+
ap.mPositiveButtonText = getString(android.R.string.ok);
ap.mNegativeButtonText = getString(android.R.string.cancel);
ap.mPositiveButtonListener = this;
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index e44e58a84dc8..5ed027d37def 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -26,7 +26,7 @@ import android.view.View;
import com.android.keyguard.KeyguardClockSwitch;
import com.android.keyguard.KeyguardMessageArea;
import com.android.keyguard.KeyguardSliceView;
-import com.android.systemui.SystemUIRootComponent;
+import com.android.systemui.dagger.SystemUIRootComponent;
import com.android.systemui.qs.QSCarrierGroup;
import com.android.systemui.qs.QSFooterImpl;
import com.android.systemui.qs.QSPanel;
@@ -37,6 +37,7 @@ import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.phone.LockIcon;
import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.policy.Clock;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -178,6 +179,11 @@ public class InjectionInflationController {
* Creates the QSCustomizer.
*/
QSCustomizer createQSCustomizer();
+
+ /**
+ * Creates a Clock.
+ */
+ Clock createClock();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
index f35af90edc3c..8c60747dffc7 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
+++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
@@ -38,6 +38,10 @@ public class NotificationChannels extends SystemUI {
public static String BATTERY = "BAT";
public static String HINTS = "HNT";
+ public NotificationChannels(Context context) {
+ super(context);
+ }
+
public static void createAll(Context context) {
final NotificationManager nm = context.getSystemService(NotificationManager.class);
final NotificationChannel batteryChannel = new NotificationChannel(BATTERY,
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/DumpTruck.java b/packages/SystemUI/src/com/android/systemui/util/leak/DumpTruck.java
index fa7af0be77f1..be5e0a0b12c5 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/DumpTruck.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/DumpTruck.java
@@ -36,6 +36,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -53,7 +54,7 @@ public class DumpTruck {
private final Context context;
private Uri hprofUri;
- private long pss;
+ private long rss;
final StringBuilder body = new StringBuilder();
public DumpTruck(Context context) {
@@ -66,7 +67,7 @@ public class DumpTruck {
* @param pids
* @return this, for chaining
*/
- public DumpTruck captureHeaps(int[] pids) {
+ public DumpTruck captureHeaps(List<Long> pids) {
final GarbageMonitor gm = Dependency.get(GarbageMonitor.class);
final File dumpDir = new File(context.getCacheDir(), FILEPROVIDER_PATH);
@@ -79,8 +80,8 @@ public class DumpTruck {
final ArrayList<String> paths = new ArrayList<String>();
final int myPid = android.os.Process.myPid();
- final int[] pids_copy = Arrays.copyOf(pids, pids.length);
- for (int pid : pids_copy) {
+ for (Long pidL : pids) {
+ final int pid = pidL.intValue();
body.append(" pid ").append(pid);
if (gm != null) {
GarbageMonitor.ProcessMemInfo info = gm.getMemInfo(pid);
@@ -88,11 +89,9 @@ public class DumpTruck {
body.append(":")
.append(" up=")
.append(info.getUptime())
- .append(" pss=")
- .append(info.currentPss)
- .append(" uss=")
- .append(info.currentUss);
- pss = info.currentPss;
+ .append(" rss=")
+ .append(info.currentRss);
+ rss = info.currentRss;
}
}
if (pid == myPid) {
@@ -147,7 +146,7 @@ public class DumpTruck {
shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
shareIntent.putExtra(Intent.EXTRA_SUBJECT,
- String.format("SystemUI memory dump (pss=%dM)", pss / 1024));
+ String.format("SystemUI memory dump (rss=%dM)", rss / 1024));
shareIntent.putExtra(Intent.EXTRA_TEXT, body.toString());
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 583f6b340d47..bff405c0bee6 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -20,7 +20,6 @@ import static android.service.quicksettings.Tile.STATE_ACTIVE;
import static android.telephony.ims.feature.ImsFeature.STATE_UNAVAILABLE;
import static com.android.internal.logging.MetricsLogger.VIEW_UNKNOWN;
-import static com.android.systemui.Dependency.BG_LOOPER_NAME;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -35,7 +34,6 @@ import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
-import android.os.Debug;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -49,7 +47,8 @@ import android.util.LongSparseArray;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
-import com.android.systemui.SystemUIFactory;
+import com.android.systemui.dagger.qualifiers.BgLooper;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -57,9 +56,9 @@ import com.android.systemui.qs.tileimpl.QSTileImpl;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
import javax.inject.Inject;
-import javax.inject.Named;
import javax.inject.Singleton;
/**
@@ -102,7 +101,6 @@ public class GarbageMonitor implements Dumpable {
private final LongSparseArray<ProcessMemInfo> mData = new LongSparseArray<>();
private final ArrayList<Long> mPids = new ArrayList<>();
- private int[] mPidsArray = new int[1];
private long mHeapLimit;
@@ -111,7 +109,7 @@ public class GarbageMonitor implements Dumpable {
@Inject
public GarbageMonitor(
Context context,
- @Named(BG_LOOPER_NAME) Looper bgLooper,
+ @BgLooper Looper bgLooper,
LeakDetector leakDetector,
LeakReporter leakReporter) {
mContext = context.getApplicationContext();
@@ -164,8 +162,8 @@ public class GarbageMonitor implements Dumpable {
return mData.get(pid);
}
- public int[] getTrackedProcesses() {
- return mPidsArray;
+ public List<Long> getTrackedProcesses() {
+ return mPids;
}
public void startTrackingProcess(long pid, String name, long start) {
@@ -173,43 +171,40 @@ public class GarbageMonitor implements Dumpable {
if (mPids.contains(pid)) return;
mPids.add(pid);
- updatePidsArrayL();
+ logPids();
mData.put(pid, new ProcessMemInfo(pid, name, start));
}
}
- private void updatePidsArrayL() {
- final int N = mPids.size();
- mPidsArray = new int[N];
- StringBuffer sb = new StringBuffer("Now tracking processes: ");
- for (int i = 0; i < N; i++) {
- final int p = mPids.get(i).intValue();
- mPidsArray[i] = p;
- sb.append(p);
- sb.append(" ");
- }
- if (DEBUG) Log.v(TAG, sb.toString());
+ private void logPids() {
+ if (DEBUG) {
+ StringBuffer sb = new StringBuffer("Now tracking processes: ");
+ for (int i = 0; i < mPids.size(); i++) {
+ final int p = mPids.get(i).intValue();
+ sb.append(" ");
+ }
+ Log.v(TAG, sb.toString());
+ }
}
private void update() {
synchronized (mPids) {
- Debug.MemoryInfo[] dinfos = mAm.getProcessMemoryInfo(mPidsArray);
- for (int i = 0; i < dinfos.length; i++) {
- Debug.MemoryInfo dinfo = dinfos[i];
- if (i > mPids.size()) {
- if (DEBUG) Log.e(TAG, "update: unknown process info received: " + dinfo);
+ for (int i = 0; i < mPids.size(); i++) {
+ final int pid = mPids.get(i).intValue();
+ // rssValues contains [VmRSS, RssFile, RssAnon, VmSwap].
+ long[] rssValues = Process.getRss(pid);
+ if (rssValues == null && rssValues.length == 0) {
+ if (DEBUG) Log.e(TAG, "update: Process.getRss() didn't provide any values.");
break;
}
- final long pid = mPids.get(i).intValue();
+ long rss = rssValues[0];
final ProcessMemInfo info = mData.get(pid);
- info.pss[info.head] = info.currentPss = dinfo.getTotalPss();
- info.uss[info.head] = info.currentUss = dinfo.getTotalPrivateDirty();
- info.head = (info.head + 1) % info.pss.length;
- if (info.currentPss > info.max) info.max = info.currentPss;
- if (info.currentUss > info.max) info.max = info.currentUss;
- if (info.currentPss == 0) {
- if (DEBUG) Log.v(TAG, "update: pid " + pid + " has pss=0, it probably died");
+ info.rss[info.head] = info.currentRss = rss;
+ info.head = (info.head + 1) % info.rss.length;
+ if (info.currentRss > info.max) info.max = info.currentRss;
+ if (info.currentRss == 0) {
+ if (DEBUG) Log.v(TAG, "update: pid " + pid + " has rss=0, it probably died");
mData.remove(pid);
}
}
@@ -217,7 +212,7 @@ public class GarbageMonitor implements Dumpable {
final long pid = mPids.get(i).intValue();
if (mData.get(pid) == null) {
mPids.remove(i);
- updatePidsArrayL();
+ logPids();
}
}
}
@@ -270,7 +265,7 @@ public class GarbageMonitor implements Dumpable {
private static class MemoryIconDrawable extends Drawable {
- long pss, limit;
+ long rss, limit;
final Drawable baseIcon;
final Paint paint = new Paint();
final float dp;
@@ -281,9 +276,9 @@ public class GarbageMonitor implements Dumpable {
paint.setColor(QSTileImpl.getColorForState(context, STATE_ACTIVE));
}
- public void setPss(long pss) {
- if (pss != this.pss) {
- this.pss = pss;
+ public void setRss(long rss) {
+ if (rss != this.rss) {
+ this.rss = rss;
invalidateSelf();
}
}
@@ -299,8 +294,8 @@ public class GarbageMonitor implements Dumpable {
public void draw(Canvas canvas) {
baseIcon.draw(canvas);
- if (limit > 0 && pss > 0) {
- float frac = Math.min(1f, (float) pss / limit);
+ if (limit > 0 && rss > 0) {
+ float frac = Math.min(1f, (float) rss / limit);
final Rect bounds = getBounds();
canvas.translate(bounds.left + 8 * dp, bounds.top + 5 * dp);
@@ -361,10 +356,10 @@ public class GarbageMonitor implements Dumpable {
}
private static class MemoryGraphIcon extends QSTile.Icon {
- long pss, limit;
+ long rss, limit;
- public void setPss(long pss) {
- this.pss = pss;
+ public void setRss(long rss) {
+ this.rss = rss;
}
public void setHeapLimit(long limit) {
@@ -374,7 +369,7 @@ public class GarbageMonitor implements Dumpable {
@Override
public Drawable getDrawable(Context context) {
final MemoryIconDrawable drawable = new MemoryIconDrawable(context);
- drawable.setPss(pss);
+ drawable.setRss(rss);
drawable.setLimit(limit);
return drawable;
}
@@ -387,13 +382,15 @@ public class GarbageMonitor implements Dumpable {
public static final boolean ADD_TO_DEFAULT_ON_DEBUGGABLE_BUILDS = true;
private final GarbageMonitor gm;
+ private final ActivityStarter mActivityStarter;
private ProcessMemInfo pmi;
private boolean dumpInProgress;
@Inject
- public MemoryTile(QSHost host) {
+ public MemoryTile(QSHost host, GarbageMonitor monitor, ActivityStarter starter) {
super(host);
- gm = SystemUIFactory.getInstance().getRootComponent().createGarbageMonitor();
+ gm = monitor;
+ mActivityStarter = starter;
}
@Override
@@ -424,7 +421,7 @@ public class GarbageMonitor implements Dumpable {
dumpInProgress = false;
refreshState();
getHost().collapsePanels();
- mContext.startActivity(shareIntent);
+ mActivityStarter.postStartActivityDismissingKeyguard(shareIntent, 0);
});
}
}.start();
@@ -462,14 +459,14 @@ public class GarbageMonitor implements Dumpable {
? "Dumping..."
: mContext.getString(R.string.heap_dump_tile_name);
if (pmi != null) {
- icon.setPss(pmi.currentPss);
+ icon.setRss(pmi.currentRss);
state.secondaryLabel =
String.format(
- "pss: %s / %s",
- formatBytes(pmi.currentPss * 1024),
+ "rss: %s / %s",
+ formatBytes(pmi.currentRss * 1024),
formatBytes(gm.mHeapLimit * 1024));
} else {
- icon.setPss(0);
+ icon.setRss(0);
state.secondaryLabel = null;
}
state.icon = icon;
@@ -479,8 +476,8 @@ public class GarbageMonitor implements Dumpable {
refreshState();
}
- public long getPss() {
- return pmi != null ? pmi.currentPss : 0;
+ public long getRss() {
+ return pmi != null ? pmi.currentRss : 0;
}
public long getHeapLimit() {
@@ -493,9 +490,8 @@ public class GarbageMonitor implements Dumpable {
public long pid;
public String name;
public long startTime;
- public long currentPss, currentUss;
- public long[] pss = new long[HEAP_TRACK_HISTORY_LEN];
- public long[] uss = new long[HEAP_TRACK_HISTORY_LEN];
+ public long currentRss;
+ public long[] rss = new long[HEAP_TRACK_HISTORY_LEN];
public long max = 1;
public int head = 0;
@@ -517,25 +513,27 @@ public class GarbageMonitor implements Dumpable {
pw.print(name.replace('"', '-'));
pw.print("\", \"start\": ");
pw.print(startTime);
- pw.print(", \"pss\": [");
- // write pss values starting from the oldest, which is pss[head], wrapping around to
- // pss[(head-1) % pss.length]
- for (int i = 0; i < pss.length; i++) {
- if (i > 0) pw.print(",");
- pw.print(pss[(head + i) % pss.length]);
- }
- pw.print("], \"uss\": [");
- for (int i = 0; i < uss.length; i++) {
+ pw.print(", \"rss\": [");
+ // write rss values starting from the oldest, which is rss[head], wrapping around to
+ // rss[(head-1) % rss.length]
+ for (int i = 0; i < rss.length; i++) {
if (i > 0) pw.print(",");
- pw.print(uss[(head + i) % uss.length]);
+ pw.print(rss[(head + i) % rss.length]);
}
pw.println("] }");
}
}
/** */
+ @Singleton
public static class Service extends SystemUI implements Dumpable {
- private GarbageMonitor mGarbageMonitor;
+ private final GarbageMonitor mGarbageMonitor;
+
+ @Inject
+ public Service(Context context, GarbageMonitor garbageMonitor) {
+ super(context);
+ mGarbageMonitor = garbageMonitor;
+ }
@Override
public void start() {
@@ -543,8 +541,6 @@ public class GarbageMonitor implements Dumpable {
Settings.Secure.getInt(
mContext.getContentResolver(), FORCE_ENABLE_LEAK_REPORTING, 0)
!= 0;
- mGarbageMonitor = SystemUIFactory.getInstance().getRootComponent()
- .createGarbageMonitor();
if (LEAK_REPORTING_ENABLED || forceEnable) {
mGarbageMonitor.startLeakMonitor();
}
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 9271bc282615..61de86698f1e 100755
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
@@ -146,9 +146,11 @@ public class AsyncSensorManager extends SensorManager
@Override
protected boolean requestTriggerSensorImpl(TriggerEventListener listener, Sensor sensor) {
- if ( sensor == null ) {
- Log.e(TAG, "sensor cannot be null \n" + Log.getStackTraceString(new Throwable()));
- return false;
+ if (listener == null) {
+ throw new IllegalArgumentException("listener cannot be null");
+ }
+ if (sensor == null) {
+ throw new IllegalArgumentException("sensor cannot be null");
}
mHandler.post(() -> {
if ( sensor == null ) {
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 c48bdde6adef..a96977a338a9 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
@@ -16,7 +16,7 @@
package com.android.systemui.util.sensors;
-import android.content.Context;
+import android.content.res.Resources;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@@ -24,8 +24,9 @@ import android.hardware.SensorManager;
import android.os.Handler;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
-import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.dagger.qualifiers.MainResources;
import java.util.ArrayList;
import java.util.List;
@@ -47,7 +48,7 @@ public class ProximitySensor {
private final float mMaxRange;
private List<ProximitySensorListener> mListeners = new ArrayList<>();
private String mTag = null;
- private ProximityEvent mLastEvent;
+ @VisibleForTesting ProximityEvent mLastEvent;
private int mSensorDelay = SensorManager.SENSOR_DELAY_NORMAL;
private boolean mPaused;
private boolean mRegistered;
@@ -64,10 +65,10 @@ public class ProximitySensor {
};
@Inject
- public ProximitySensor(
- Context context, AsyncSensorManager sensorManager, PluginManager pluginManager) {
+ public ProximitySensor(@MainResources Resources resources,
+ AsyncSensorManager sensorManager) {
mSensorManager = sensorManager;
- Sensor sensor = findBrightnessSensor(context);
+ Sensor sensor = findBrightnessSensor(resources);
if (sensor == null) {
mUsingBrightnessSensor = false;
@@ -107,8 +108,8 @@ public class ProximitySensor {
registerInternal();
}
- private Sensor findBrightnessSensor(Context context) {
- String sensorType = context.getString(R.string.doze_brightness_sensor_type);
+ private Sensor findBrightnessSensor(Resources resources) {
+ String sensorType = resources.getString(R.string.doze_brightness_sensor_type);
List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
Sensor sensor = null;
for (Sensor s : sensorList) {
@@ -146,17 +147,17 @@ public class ProximitySensor {
return false;
}
- logDebug("Using brightness sensor? " + mUsingBrightnessSensor);
mListeners.add(listener);
registerInternal();
return true;
}
- private void registerInternal() {
+ protected void registerInternal() {
if (mRegistered || mPaused || mListeners.isEmpty()) {
return;
}
+ logDebug("Using brightness sensor? " + mUsingBrightnessSensor);
logDebug("Registering sensor listener");
mRegistered = true;
mSensorManager.registerListener(mSensorEventListener, mSensor, mSensorDelay);
@@ -175,7 +176,7 @@ public class ProximitySensor {
}
}
- private void unregisterInternal() {
+ protected void unregisterInternal() {
if (!mRegistered) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
index 7991e388af7e..e4ebea9483c7 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
+++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
@@ -16,8 +16,11 @@
package com.android.systemui.util.wakelock;
+import android.content.Context;
import android.os.Handler;
+import javax.inject.Inject;
+
/**
* A wake lock that has a built in delay when releasing to give the framebuffer time to update.
*/
@@ -53,4 +56,46 @@ public class DelayedWakeLock implements WakeLock {
public String toString() {
return TO_STRING_PREFIX + mInner;
}
+
+ /**
+ * An injectable builder for {@see DelayedWakeLock} that has the context already filled in.
+ */
+ public static class Builder {
+ private final Context mContext;
+ private String mTag;
+ private Handler mHandler;
+
+ /**
+ * Constructor for DelayedWakeLock.Builder
+ */
+ @Inject
+ public Builder(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Set the tag for the WakeLock.
+ */
+ public Builder setTag(String tag) {
+ mTag = tag;
+
+ return this;
+ }
+
+ /**
+ * Set the handler for the DelayedWakeLock.
+ */
+ public Builder setHandler(Handler handler) {
+ mHandler = handler;
+
+ return this;
+ }
+
+ /**
+ * Build the DelayedWakeLock.
+ */
+ public DelayedWakeLock build() {
+ return new DelayedWakeLock(mHandler, WakeLock.createPartial(mContext, mTag));
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index d2f185a88bfd..25a5139bf661 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -27,7 +27,6 @@ import android.view.WindowManager.LayoutParams;
import com.android.settingslib.applications.InterestingConfigChanges;
import com.android.systemui.Dependency;
-import com.android.systemui.SystemUI;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.PluginDependencyProvider;
@@ -40,9 +39,13 @@ import com.android.systemui.tuner.TunerService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Implementation of VolumeComponent backed by the new volume dialog.
*/
+@Singleton
public class VolumeDialogComponent implements VolumeComponent, TunerService.Tunable,
VolumeDialogControllerImpl.UserActivityListener{
@@ -54,12 +57,12 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna
public static final boolean DEFAULT_VOLUME_UP_TO_EXIT_SILENT = false;
public static final boolean DEFAULT_DO_NOT_DISTURB_WHEN_SILENT = false;
- private final SystemUI mSysui;
protected final Context mContext;
private final VolumeDialogControllerImpl mController;
private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
| ActivityInfo.CONFIG_ASSETS_PATHS | ActivityInfo.CONFIG_UI_MODE);
+ private final KeyguardViewMediator mKeyguardViewMediator;
private VolumeDialog mDialog;
private VolumePolicy mVolumePolicy = new VolumePolicy(
DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT, // volumeDownToEnterSilent
@@ -68,9 +71,10 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna
400 // vibrateToSilentDebounce
);
- public VolumeDialogComponent(SystemUI sysui, Context context) {
- mSysui = sysui;
+ @Inject
+ public VolumeDialogComponent(Context context, KeyguardViewMediator keyguardViewMediator) {
mContext = context;
+ mKeyguardViewMediator = keyguardViewMediator;
mController = (VolumeDialogControllerImpl) Dependency.get(VolumeDialogController.class);
mController.setUserActivityListener(this);
// Allow plugins to reference the VolumeDialogController.
@@ -133,10 +137,7 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna
@Override
public void onUserActivity() {
- final KeyguardViewMediator kvm = mSysui.getComponent(KeyguardViewMediator.class);
- if (kvm != null) {
- kvm.userActivity();
- }
+ mKeyguardViewMediator.userActivity();
}
private void applyConfiguration() {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index a6b5b38fd728..2c70fb4c50ec 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -59,6 +59,7 @@ import com.android.settingslib.volume.MediaSessions;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.qs.tiles.DndTile;
@@ -137,9 +138,10 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
private UserActivityListener mUserActivityListener;
protected final VC mVolumeController = new VC();
+ protected final BroadcastDispatcher mBroadcastDispatcher;
@Inject
- public VolumeDialogControllerImpl(Context context) {
+ public VolumeDialogControllerImpl(Context context, BroadcastDispatcher broadcastDispatcher) {
mContext = context.getApplicationContext();
mNotificationManager = (NotificationManager) mContext.getSystemService(
Context.NOTIFICATION_SERVICE);
@@ -152,6 +154,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
mAudio = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
mNoMan = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
mObserver = new SettingObserver(mWorker);
+ mBroadcastDispatcher = broadcastDispatcher;
mObserver.init();
mReceiver.init();
mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
@@ -618,7 +621,9 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
.PRIORITY_CATEGORY_MEDIA) == 0;
boolean disallowSystem = (policy.priorityCategories & NotificationManager.Policy
.PRIORITY_CATEGORY_SYSTEM) == 0;
- boolean disallowRinger = ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(policy);
+ // ringer controls notifications, ringer and system sounds, so only disallow ringer changes
+ // if all relevant (notifications + ringer + system) sounds are not allowed to bypass DND
+ boolean disallowRinger = ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(policy);
if (mState.disallowAlarms == disallowAlarms
&& mState.disallowMedia == disallowMedia
&& mState.disallowRinger == disallowRinger
@@ -1004,11 +1009,11 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- mContext.registerReceiver(this, filter, null, mWorker);
+ mBroadcastDispatcher.registerReceiver(this, filter, mWorker);
}
public void destroy() {
- mContext.unregisterReceiver(this);
+ mBroadcastDispatcher.unregisterReceiver(this);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index f8cf79322b40..b74313975223 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -16,18 +16,22 @@
package com.android.systemui.volume;
+import android.content.Context;
import android.content.res.Configuration;
import android.os.Handler;
import android.util.Log;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.qs.tiles.DndTile;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
public class VolumeUI extends SystemUI {
private static final String TAG = "VolumeUI";
private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
@@ -37,6 +41,12 @@ public class VolumeUI extends SystemUI {
private boolean mEnabled;
private VolumeDialogComponent mVolumeComponent;
+ @Inject
+ public VolumeUI(Context context, VolumeDialogComponent volumeDialogComponent) {
+ super(context);
+ mVolumeComponent = volumeDialogComponent;
+ }
+
@Override
public void start() {
boolean enableVolumeUi = mContext.getResources().getBoolean(R.bool.enable_volume_ui);
@@ -45,8 +55,6 @@ public class VolumeUI extends SystemUI {
mEnabled = enableVolumeUi || enableSafetyWarning;
if (!mEnabled) return;
- mVolumeComponent = SystemUIFactory.getInstance()
- .createVolumeDialogComponent(this, mContext);
mVolumeComponent.setEnableDialogs(enableVolumeUi, enableSafetyWarning);
putComponent(VolumeComponent.class, getVolumeComponent());
setDefaultVolumeController();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
index 2e94c7c12cd8..1dd48634615b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
@@ -26,20 +26,24 @@ import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+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.Mockito.doAnswer;
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.when;
-import android.content.Context;
import android.net.ConnectivityManager;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Process;
import android.provider.Settings;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
@@ -62,6 +66,7 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
import java.util.ArrayList;
import java.util.HashMap;
@@ -105,6 +110,14 @@ public class CarrierTextControllerTest extends SysuiTestCase {
private CarrierTextController mCarrierTextController;
private TestableLooper mTestableLooper;
+ private Void checkMainThread(InvocationOnMock inv) {
+ Looper mainLooper = Dependency.get(Dependency.MAIN_HANDLER).getLooper();
+ if (!mainLooper.isCurrentThread()) {
+ fail("This call should be done from the main thread");
+ }
+ return null;
+ }
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -112,6 +125,7 @@ public class CarrierTextControllerTest extends SysuiTestCase {
mContext.addMockSystemService(WifiManager.class, mWifiManager);
mContext.addMockSystemService(ConnectivityManager.class, mConnectivityManager);
+ when(mConnectivityManager.isNetworkSupported(anyInt())).thenReturn(true);
mContext.addMockSystemService(TelephonyManager.class, mTelephonyManager);
mContext.addMockSystemService(SubscriptionManager.class, mSubscriptionManager);
mContext.getOrCreateTestableResources().addOverride(
@@ -121,19 +135,43 @@ public class CarrierTextControllerTest extends SysuiTestCase {
mDependency.injectMockDependency(WakefulnessLifecycle.class);
mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
new Handler(mTestableLooper.getLooper()));
- mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
+ mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor);
+
+ doAnswer(this::checkMainThread).when(mKeyguardUpdateMonitor)
+ .registerCallback(any(KeyguardUpdateMonitorCallback.class));
+ doAnswer(this::checkMainThread).when(mKeyguardUpdateMonitor)
+ .removeCallback(any(KeyguardUpdateMonitorCallback.class));
mCarrierTextCallbackInfo = new CarrierTextController.CarrierTextCallbackInfo("",
new CharSequence[]{}, false, new int[]{});
- when(mTelephonyManager.getPhoneCount()).thenReturn(3);
+ when(mTelephonyManager.getSupportedModemCount()).thenReturn(3);
- mCarrierTextController = new TestCarrierTextController(mContext, SEPARATOR, true, true,
- mKeyguardUpdateMonitor);
- // This should not start listening on any of the real dependencies
+ mCarrierTextController = new CarrierTextController(mContext, SEPARATOR, true, true);
+ // This should not start listening on any of the real dependencies but will test that
+ // callbacks in mKeyguardUpdateMonitor are done in the mTestableLooper thread
mCarrierTextController.setListening(mCarrierTextCallback);
}
@Test
+ public void testKeyguardUpdateMonitorCalledInMainThread() throws Exception {
+ // This test will run on the main looper (which is not the same as the looper set as MAIN
+ // for CarrierTextCallback. This will fail if calls to mKeyguardUpdateMonitor are not done
+ // through the looper set in the set up
+ HandlerThread thread = new HandlerThread("testThread",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ thread.start();
+ TestableLooper testableLooper = new TestableLooper(thread.getLooper());
+ Handler h = new Handler(testableLooper.getLooper());
+ h.post(() -> {
+ mCarrierTextController.setListening(null);
+ mCarrierTextController.setListening(mCarrierTextCallback);
+ });
+ testableLooper.processAllMessages();
+ mTestableLooper.processAllMessages();
+ thread.quitSafely();
+ }
+
+ @Test
public void testAirplaneMode() {
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
reset(mCarrierTextCallback);
@@ -466,20 +504,4 @@ public class CarrierTextControllerTest extends SysuiTestCase {
assertEquals(TEST_CARRIER + SEPARATOR + TEST_CARRIER,
captor.getValue().carrierText);
}
-
- public static class TestCarrierTextController extends CarrierTextController {
- private KeyguardUpdateMonitor mKUM;
-
- public TestCarrierTextController(Context context, CharSequence separator,
- boolean showAirplaneMode, boolean showMissingSim, KeyguardUpdateMonitor kum) {
- super(context, separator, showAirplaneMode, showMissingSim);
- mKUM = kum;
- }
-
- @Override
- public void setListening(CarrierTextCallback callback) {
- super.setListening(callback);
- mKeyguardUpdateMonitor = mKUM;
- }
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index ad7bba1e0790..57b09872f9c1 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.any;
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.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
@@ -481,6 +482,25 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
assertThat(mKeyguardUpdateMonitor.isUserUnlocked(randomUser)).isFalse();
}
+ @Test
+ public void testTrustUsuallyManaged_whenTrustChanges() {
+ int user = KeyguardUpdateMonitor.getCurrentUser();
+ when(mTrustManager.isTrustUsuallyManaged(eq(user))).thenReturn(true);
+ mKeyguardUpdateMonitor.onTrustManagedChanged(false /* managed */, user);
+ assertThat(mKeyguardUpdateMonitor.isTrustUsuallyManaged(user)).isTrue();
+ }
+
+ @Test
+ public void testTrustUsuallyManaged_resetWhenUserIsRemoved() {
+ int user = KeyguardUpdateMonitor.getCurrentUser();
+ when(mTrustManager.isTrustUsuallyManaged(eq(user))).thenReturn(true);
+ mKeyguardUpdateMonitor.onTrustManagedChanged(false /* managed */, user);
+ assertThat(mKeyguardUpdateMonitor.isTrustUsuallyManaged(user)).isTrue();
+
+ mKeyguardUpdateMonitor.handleUserRemoved(user);
+ assertThat(mKeyguardUpdateMonitor.isTrustUsuallyManaged(user)).isFalse();
+ }
+
private Intent putPhoneInfo(Intent intent, Bundle data, Boolean simInited) {
int subscription = simInited
? 1/* mock subid=1 */ : SubscriptionManager.DUMMY_SUBSCRIPTION_ID_BASE;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
index a07f25a144b1..364ee666e17d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
@@ -30,6 +30,7 @@ import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.util.Assert;
@@ -50,9 +51,10 @@ public class ExpandHelperTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
+ mDependency.injectMockDependency(NotificationMediaManager.class);
Assert.sMainLooper = TestableLooper.get(this).getLooper();
Context context = getContext();
- mRow = new NotificationTestHelper(context).createRow();
+ mRow = new NotificationTestHelper(context, mDependency).createRow();
mCallback = mock(ExpandHelper.Callback.class);
mExpandHelper = new ExpandHelper(context, mCallback, 10, 100);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 3b5e12c4ef96..c338d7031fea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -45,7 +45,6 @@ import android.testing.TestableLooper.RunWithLooper;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;
-import android.view.WindowManagerPolicyConstants;
import androidx.test.filters.SmallTest;
@@ -53,7 +52,6 @@ import com.android.systemui.R.dimen;
import com.android.systemui.ScreenDecorations.TunablePaddingTagListener;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentService;
-import com.android.systemui.statusbar.phone.NavigationModeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
import com.android.systemui.tuner.TunablePadding;
@@ -80,7 +78,6 @@ public class ScreenDecorationsTest extends SysuiTestCase {
private TunerService mTunerService;
private StatusBarWindowView mView;
private TunablePaddingService mTunablePaddingService;
- private NavigationModeController mNavigationModeController;
@Before
public void setup() {
@@ -90,8 +87,6 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mTunablePaddingService = mDependency.injectMockDependency(TunablePaddingService.class);
mTunerService = mDependency.injectMockDependency(TunerService.class);
mFragmentService = mDependency.injectMockDependency(FragmentService.class);
- mNavigationModeController = mDependency.injectMockDependency(
- NavigationModeController.class);
mStatusBar = mock(StatusBar.class);
mWindowManager = mock(WindowManager.class);
@@ -107,7 +102,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
when(mFragmentService.getFragmentHostManager(any())).thenReturn(mFragmentHostManager);
- mScreenDecorations = new ScreenDecorations() {
+ mScreenDecorations = new ScreenDecorations(mContext) {
@Override
public void start() {
super.start();
@@ -131,7 +126,6 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
}
};
- mScreenDecorations.mContext = mContext;
mScreenDecorations.mComponents = mContext.getComponents();
reset(mTunerService);
}
@@ -213,54 +207,6 @@ public class ScreenDecorationsTest extends SysuiTestCase {
}
@Test
- public void testAssistHandles() {
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.dimen.rounded_corner_radius, 0);
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.dimen.rounded_corner_radius_top, 0);
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
- mContext.getOrCreateTestableResources()
- .addOverride(dimen.rounded_corner_content_padding, 0);
- when(mNavigationModeController.addListener(any())).thenReturn(
- WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL);
-
- mScreenDecorations.start();
-
- // Add 2 windows for rounded corners (top and bottom).
- verify(mWindowManager, times(2)).addView(any(), any());
- }
-
- @Test
- public void testDelayedAssistHandles() {
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.dimen.rounded_corner_radius, 0);
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.dimen.rounded_corner_radius_top, 0);
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
- mContext.getOrCreateTestableResources()
- .addOverride(dimen.rounded_corner_content_padding, 0);
- when(mNavigationModeController.addListener(any())).thenReturn(
- WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON);
-
- mScreenDecorations.start();
-
- // No handles and no corners
- verify(mWindowManager, never()).addView(any(), any());
-
- mScreenDecorations.handleNavigationModeChange(
- WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL);
-
- // Add 2 windows for rounded corners (top and bottom).
- verify(mWindowManager, times(2)).addView(any(), any());
- }
-
- @Test
public void hasRoundedCornerOverlayFlagSet() {
assertThat(mScreenDecorations.getWindowLayoutParams().privateFlags
& PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java
index 3ea7150afca0..06999bc45e17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java
@@ -58,13 +58,12 @@ public class SizeCompatModeActivityControllerTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
doReturn(true).when(mMockButton).show();
- mController = new SizeCompatModeActivityController(mMockAm) {
+ mController = new SizeCompatModeActivityController(mContext, mMockAm) {
@Override
RestartActivityButton createRestartButton(Context context) {
return mMockButton;
};
};
- mController.mContext = mContext;
ArgumentCaptor<TaskStackChangeListener> listenerCaptor =
ArgumentCaptor.forClass(TaskStackChangeListener.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
index 19e1a5cef183..a766885297fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
@@ -35,6 +35,7 @@ import androidx.test.filters.SmallTest;
import com.android.settingslib.SliceBroadcastRelay;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -44,6 +45,14 @@ import org.mockito.ArgumentCaptor;
public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
private static final String TEST_ACTION = "com.android.systemui.action.TEST_ACTION";
+ private SliceBroadcastRelayHandler mRelayHandler;
+ private Context mSpyContext;
+ @Before
+ public void setup() {
+ mSpyContext = spy(mContext);
+
+ mRelayHandler = new SliceBroadcastRelayHandler(mSpyContext);
+ }
@Test
public void testRegister() {
@@ -52,8 +61,6 @@ public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
.authority("something")
.path("test")
.build();
- SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler();
- relayHandler.mContext = spy(mContext);
Intent intent = new Intent(SliceBroadcastRelay.ACTION_REGISTER);
intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
@@ -63,8 +70,8 @@ public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value);
intent.putExtra(SliceBroadcastRelay.EXTRA_URI, testUri);
- relayHandler.handleIntent(intent);
- verify(relayHandler.mContext).registerReceiver(any(), eq(value));
+ mRelayHandler.handleIntent(intent);
+ verify(mSpyContext).registerReceiver(any(), eq(value));
}
@Test
@@ -74,8 +81,6 @@ public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
.authority("something")
.path("test")
.build();
- SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler();
- relayHandler.mContext = spy(mContext);
Intent intent = new Intent(SliceBroadcastRelay.ACTION_REGISTER);
intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
@@ -84,14 +89,14 @@ public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
IntentFilter value = new IntentFilter(TEST_ACTION);
intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value);
- relayHandler.handleIntent(intent);
+ mRelayHandler.handleIntent(intent);
ArgumentCaptor<BroadcastReceiver> relay = ArgumentCaptor.forClass(BroadcastReceiver.class);
- verify(relayHandler.mContext).registerReceiver(relay.capture(), eq(value));
+ verify(mSpyContext).registerReceiver(relay.capture(), eq(value));
intent = new Intent(SliceBroadcastRelay.ACTION_UNREGISTER);
intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
- relayHandler.handleIntent(intent);
- verify(relayHandler.mContext).unregisterReceiver(eq(relay.getValue()));
+ mRelayHandler.handleIntent(intent);
+ verify(mSpyContext).unregisterReceiver(eq(relay.getValue()));
}
@Test
@@ -101,12 +106,10 @@ public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
.authority("something")
.path("test")
.build();
- SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler();
- relayHandler.mContext = spy(mContext);
Intent intent = new Intent(SliceBroadcastRelay.ACTION_UNREGISTER);
intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
- relayHandler.handleIntent(intent);
+ mRelayHandler.handleIntent(intent);
// No crash
}
@@ -118,9 +121,6 @@ public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
.authority("something")
.path("test")
.build();
- SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler();
- relayHandler.mContext = spy(mContext);
-
Intent intent = new Intent(SliceBroadcastRelay.ACTION_REGISTER);
intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
intent.putExtra(SliceBroadcastRelay.EXTRA_RECEIVER,
@@ -128,10 +128,10 @@ public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
IntentFilter value = new IntentFilter(TEST_ACTION);
intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value);
- relayHandler.handleIntent(intent);
+ mRelayHandler.handleIntent(intent);
ArgumentCaptor<BroadcastReceiver> relay = ArgumentCaptor.forClass(BroadcastReceiver.class);
- verify(relayHandler.mContext).registerReceiver(relay.capture(), eq(value));
- relay.getValue().onReceive(relayHandler.mContext, new Intent(TEST_ACTION));
+ verify(mSpyContext).registerReceiver(relay.capture(), eq(value));
+ relay.getValue().onReceive(mSpyContext, new Intent(TEST_ACTION));
verify(Receiver.sReceiver, timeout(2000)).onReceive(any(), any());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
index a245d4112798..cf778504190a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
@@ -24,6 +24,7 @@ import android.testing.DexmakerShareClassLoaderRule;
import androidx.test.InstrumentationRegistry;
+import com.android.systemui.assist.AssistManager;
import com.android.systemui.utils.leaks.LeakCheckedTest;
import com.android.systemui.utils.leaks.LeakCheckedTest.SysuiLeakCheck;
@@ -64,6 +65,7 @@ public abstract class SysuiBaseFragmentTest extends BaseFragmentTest {
when(inst.getTargetContext()).thenThrow(new RuntimeException(
"SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext"));
InstrumentationRegistry.registerInstance(inst, InstrumentationRegistry.getArguments());
+ mDependency.injectMockDependency(AssistManager.class);
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java
index 9c920f52d56a..fbb8e0c171cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java
@@ -38,7 +38,6 @@ import androidx.test.filters.SmallTest;
import com.android.internal.app.AssistUtils;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.systemui.DumpController;
-import com.android.systemui.ScreenDecorations;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
@@ -64,7 +63,6 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
private AssistHandleBehaviorController mAssistHandleBehaviorController;
- @Mock private ScreenDecorations mMockScreenDecorations;
@Mock private AssistUtils mMockAssistUtils;
@Mock private Handler mMockHandler;
@Mock private PhenotypeHelper mMockPhenotypeHelper;
@@ -74,6 +72,7 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
@Mock private AssistHandleBehaviorController.BehaviorController mMockTestBehavior;
@Mock private NavigationModeController mMockNavigationModeController;
@Mock private DumpController mMockDumpController;
+ @Mock private AssistHandleViewController mMockAssistHandleViewController;
@Before
public void setup() {
@@ -97,7 +96,7 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
mContext,
mMockAssistUtils,
mMockHandler,
- () -> mMockScreenDecorations,
+ () -> mMockAssistHandleViewController,
mMockPhenotypeHelper,
behaviorMap,
mMockNavigationModeController,
@@ -114,14 +113,14 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
// Arrange
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
mAssistHandleBehaviorController.showAndStay();
- reset(mMockScreenDecorations);
+ reset(mMockAssistHandleViewController);
// Act
mAssistHandleBehaviorController.hide();
// Assert
- verify(mMockScreenDecorations).setAssistHintVisible(false);
- verifyNoMoreInteractions(mMockScreenDecorations);
+ verify(mMockAssistHandleViewController).setAssistHintVisible(false);
+ verifyNoMoreInteractions(mMockAssistHandleViewController);
}
@Test
@@ -129,13 +128,13 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
// Arrange
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
mAssistHandleBehaviorController.hide();
- reset(mMockScreenDecorations);
+ reset(mMockAssistHandleViewController);
// Act
mAssistHandleBehaviorController.hide();
// Assert
- verifyNoMoreInteractions(mMockScreenDecorations);
+ verifyNoMoreInteractions(mMockAssistHandleViewController);
}
@Test
@@ -143,14 +142,14 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
// Arrange
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
mAssistHandleBehaviorController.hide();
- reset(mMockScreenDecorations);
+ reset(mMockAssistHandleViewController);
// Act
mAssistHandleBehaviorController.showAndStay();
// Assert
- verify(mMockScreenDecorations).setAssistHintVisible(true);
- verifyNoMoreInteractions(mMockScreenDecorations);
+ verify(mMockAssistHandleViewController).setAssistHintVisible(true);
+ verifyNoMoreInteractions(mMockAssistHandleViewController);
}
@Test
@@ -158,13 +157,13 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
// Arrange
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
mAssistHandleBehaviorController.showAndStay();
- reset(mMockScreenDecorations);
+ reset(mMockAssistHandleViewController);
// Act
mAssistHandleBehaviorController.showAndStay();
// Assert
- verifyNoMoreInteractions(mMockScreenDecorations);
+ verifyNoMoreInteractions(mMockAssistHandleViewController);
}
@Test
@@ -172,13 +171,13 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
// Arrange
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(null);
mAssistHandleBehaviorController.hide();
- reset(mMockScreenDecorations);
+ reset(mMockAssistHandleViewController);
// Act
mAssistHandleBehaviorController.showAndStay();
// Assert
- verifyNoMoreInteractions(mMockScreenDecorations);
+ verifyNoMoreInteractions(mMockAssistHandleViewController);
}
@Test
@@ -186,15 +185,15 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
// Arrange
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
mAssistHandleBehaviorController.hide();
- reset(mMockScreenDecorations);
+ reset(mMockAssistHandleViewController);
// Act
mAssistHandleBehaviorController.showAndGo();
// Assert
- InOrder inOrder = inOrder(mMockScreenDecorations);
- inOrder.verify(mMockScreenDecorations).setAssistHintVisible(true);
- inOrder.verify(mMockScreenDecorations).setAssistHintVisible(false);
+ InOrder inOrder = inOrder(mMockAssistHandleViewController);
+ inOrder.verify(mMockAssistHandleViewController).setAssistHintVisible(true);
+ inOrder.verify(mMockAssistHandleViewController).setAssistHintVisible(false);
inOrder.verifyNoMoreInteractions();
}
@@ -203,14 +202,14 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
// Arrange
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
mAssistHandleBehaviorController.showAndStay();
- reset(mMockScreenDecorations);
+ reset(mMockAssistHandleViewController);
// Act
mAssistHandleBehaviorController.showAndGo();
// Assert
- verify(mMockScreenDecorations).setAssistHintVisible(false);
- verifyNoMoreInteractions(mMockScreenDecorations);
+ verify(mMockAssistHandleViewController).setAssistHintVisible(false);
+ verifyNoMoreInteractions(mMockAssistHandleViewController);
}
@Test
@@ -221,13 +220,13 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
eq(SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS),
anyLong())).thenReturn(10000L);
mAssistHandleBehaviorController.showAndGo();
- reset(mMockScreenDecorations);
+ reset(mMockAssistHandleViewController);
// Act
mAssistHandleBehaviorController.showAndGo();
// Assert
- verifyNoMoreInteractions(mMockScreenDecorations);
+ verifyNoMoreInteractions(mMockAssistHandleViewController);
}
@Test
@@ -235,13 +234,13 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
// Arrange
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(null);
mAssistHandleBehaviorController.hide();
- reset(mMockScreenDecorations);
+ reset(mMockAssistHandleViewController);
// Act
mAssistHandleBehaviorController.showAndGo();
// Assert
- verifyNoMoreInteractions(mMockScreenDecorations);
+ verifyNoMoreInteractions(mMockAssistHandleViewController);
}
@Test
@@ -249,15 +248,15 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
// Arrange
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
mAssistHandleBehaviorController.hide();
- reset(mMockScreenDecorations);
+ reset(mMockAssistHandleViewController);
// Act
mAssistHandleBehaviorController.showAndGoDelayed(1000, false);
// Assert
- InOrder inOrder = inOrder(mMockScreenDecorations);
- inOrder.verify(mMockScreenDecorations).setAssistHintVisible(true);
- inOrder.verify(mMockScreenDecorations).setAssistHintVisible(false);
+ InOrder inOrder = inOrder(mMockAssistHandleViewController);
+ inOrder.verify(mMockAssistHandleViewController).setAssistHintVisible(true);
+ inOrder.verify(mMockAssistHandleViewController).setAssistHintVisible(false);
inOrder.verifyNoMoreInteractions();
}
@@ -266,14 +265,14 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
// Arrange
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
mAssistHandleBehaviorController.showAndStay();
- reset(mMockScreenDecorations);
+ reset(mMockAssistHandleViewController);
// Act
mAssistHandleBehaviorController.showAndGoDelayed(1000, false);
// Assert
- verify(mMockScreenDecorations).setAssistHintVisible(false);
- verifyNoMoreInteractions(mMockScreenDecorations);
+ verify(mMockAssistHandleViewController).setAssistHintVisible(false);
+ verifyNoMoreInteractions(mMockAssistHandleViewController);
}
@Test
@@ -281,16 +280,16 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
// Arrange
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME);
mAssistHandleBehaviorController.showAndStay();
- reset(mMockScreenDecorations);
+ reset(mMockAssistHandleViewController);
// Act
mAssistHandleBehaviorController.showAndGoDelayed(1000, true);
// Assert
- InOrder inOrder = inOrder(mMockScreenDecorations);
- inOrder.verify(mMockScreenDecorations).setAssistHintVisible(false);
- inOrder.verify(mMockScreenDecorations).setAssistHintVisible(true);
- inOrder.verify(mMockScreenDecorations).setAssistHintVisible(false);
+ InOrder inOrder = inOrder(mMockAssistHandleViewController);
+ inOrder.verify(mMockAssistHandleViewController).setAssistHintVisible(false);
+ inOrder.verify(mMockAssistHandleViewController).setAssistHintVisible(true);
+ inOrder.verify(mMockAssistHandleViewController).setAssistHintVisible(false);
inOrder.verifyNoMoreInteractions();
}
@@ -302,13 +301,13 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
eq(SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS),
anyLong())).thenReturn(10000L);
mAssistHandleBehaviorController.showAndGo();
- reset(mMockScreenDecorations);
+ reset(mMockAssistHandleViewController);
// Act
mAssistHandleBehaviorController.showAndGoDelayed(1000, false);
// Assert
- verifyNoMoreInteractions(mMockScreenDecorations);
+ verifyNoMoreInteractions(mMockAssistHandleViewController);
}
@Test
@@ -316,13 +315,13 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
// Arrange
when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(null);
mAssistHandleBehaviorController.hide();
- reset(mMockScreenDecorations);
+ reset(mMockAssistHandleViewController);
// Act
mAssistHandleBehaviorController.showAndGoDelayed(1000, false);
// Assert
- verifyNoMoreInteractions(mMockScreenDecorations);
+ verifyNoMoreInteractions(mMockAssistHandleViewController);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleViewControllerTest.java
new file mode 100644
index 000000000000..6e21ae218621
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleViewControllerTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.assist;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.when;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
+import android.view.ViewPropertyAnimator;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.CornerHandleView;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class AssistHandleViewControllerTest extends SysuiTestCase {
+
+ private AssistHandleViewController mAssistHandleViewController;
+
+ @Mock private Handler mMockHandler;
+ @Mock private Looper mMockLooper;
+ @Mock private View mMockBarView;
+ @Mock private CornerHandleView mMockAssistHint;
+ @Mock private ViewPropertyAnimator mMockAnimator;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mMockBarView.findViewById(anyInt())).thenReturn(mMockAssistHint);
+ when(mMockAssistHint.animate()).thenReturn(mMockAnimator);
+ when(mMockAnimator.setInterpolator(any())).thenReturn(mMockAnimator);
+ when(mMockAnimator.setDuration(anyLong())).thenReturn(mMockAnimator);
+ doNothing().when(mMockAnimator).cancel();
+ when(mMockHandler.getLooper()).thenReturn(mMockLooper);
+ when(mMockLooper.isCurrentThread()).thenReturn(true);
+
+ mAssistHandleViewController = new AssistHandleViewController(mMockHandler, mMockBarView);
+ }
+
+ @Test
+ public void testSetVisibleWithoutBlocked() {
+ // Act
+ mAssistHandleViewController.setAssistHintVisible(true);
+
+ // Assert
+ assertTrue(mAssistHandleViewController.mAssistHintVisible);
+ }
+
+ @Test
+ public void testSetInvisibleWithoutBlocked() {
+ // Arrange
+ mAssistHandleViewController.setAssistHintVisible(true);
+
+ // Act
+ mAssistHandleViewController.setAssistHintVisible(false);
+
+ // Assert
+ assertFalse(mAssistHandleViewController.mAssistHintVisible);
+ }
+
+ @Test
+ public void testSetVisibleWithBlocked() {
+ // Act
+ mAssistHandleViewController.setAssistHintBlocked(true);
+ mAssistHandleViewController.setAssistHintVisible(true);
+
+ // Assert
+ assertFalse(mAssistHandleViewController.mAssistHintVisible);
+ assertTrue(mAssistHandleViewController.mAssistHintBlocked);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
index 7a09137b1ff8..2c85424bac79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
@@ -24,6 +24,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.content.Context;
+import android.hardware.biometrics.Authenticator;
import android.hardware.biometrics.BiometricPrompt;
import android.os.Bundle;
import android.test.suitebuilder.annotation.SmallTest;
@@ -70,7 +71,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testOnAuthenticationSucceeded_noConfirmationRequired_sendsActionAuthenticated() {
- initDialog(mContext, mCallback, new MockInjector());
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
// The onAuthenticated runnable is posted when authentication succeeds.
mBiometricView.onAuthenticationSucceeded();
@@ -81,7 +82,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testOnAuthenticationSucceeded_confirmationRequired_updatesDialogContents() {
- initDialog(mContext, mCallback, new MockInjector());
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
mBiometricView.setRequireConfirmation(true);
mBiometricView.onAuthenticationSucceeded();
@@ -97,7 +98,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testPositiveButton_sendsActionAuthenticated() {
Button button = new Button(mContext);
- initDialog(mContext, mCallback, new MockInjector() {
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector() {
@Override
public Button getPositiveButton() {
return button;
@@ -114,7 +115,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testNegativeButton_beforeAuthentication_sendsActionButtonNegative() {
Button button = new Button(mContext);
- initDialog(mContext, mCallback, new MockInjector() {
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector() {
@Override
public Button getNegativeButton() {
return button;
@@ -131,7 +132,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testNegativeButton_whenPendingConfirmation_sendsActionUserCanceled() {
Button button = new Button(mContext);
- initDialog(mContext, mCallback, new MockInjector() {
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector() {
@Override
public Button getNegativeButton() {
return button;
@@ -149,7 +150,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testTryAgainButton_sendsActionTryAgain() {
Button button = new Button(mContext);
- initDialog(mContext, mCallback, new MockInjector() {
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector() {
@Override
public Button getTryAgainButton() {
return button;
@@ -165,7 +166,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testError_sendsActionError() {
- initDialog(mContext, mCallback, new MockInjector());
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
final String testError = "testError";
mBiometricView.onError(testError);
waitForIdleSync();
@@ -176,7 +177,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testBackgroundClicked_sendsActionUserCanceled() {
- initDialog(mContext, mCallback, new MockInjector());
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
View view = new View(mContext);
mBiometricView.setBackgroundView(view);
@@ -186,7 +187,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testBackgroundClicked_afterAuthenticated_neverSendsUserCanceled() {
- initDialog(mContext, mCallback, new MockInjector());
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
View view = new View(mContext);
mBiometricView.setBackgroundView(view);
@@ -197,8 +198,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
@Test
public void testBackgroundClicked_whenSmallDialog_neverSendsUserCanceled() {
- initDialog(mContext, mCallback, new MockInjector());
- mBiometricView.setPanelController(mPanelController);
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
mBiometricView.updateSize(AuthDialog.SIZE_SMALL);
View view = new View(mContext);
@@ -213,7 +213,7 @@ public class AuthBiometricViewTest extends SysuiTestCase {
Button tryAgainButton = new Button(mContext);
TextView indicatorView = new TextView(mContext);
- initDialog(mContext, mCallback, new MockInjector() {
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector() {
@Override
public Button getTryAgainButton() {
return tryAgainButton;
@@ -249,16 +249,18 @@ public class AuthBiometricViewTest extends SysuiTestCase {
// Create new dialog and restore the previous state into it
Button tryAgainButton2 = new Button(mContext);
TextView indicatorView2 = new TextView(mContext);
- initDialog(mContext, mCallback, state, new MockInjector() {
- @Override
- public Button getTryAgainButton() {
- return tryAgainButton2;
- }
- @Override
- public TextView getIndicatorView() {
- return indicatorView2;
- }
- });
+ initDialog(mContext, false /* allowDeviceCredential */, mCallback, state,
+ new MockInjector() {
+ @Override
+ public Button getTryAgainButton() {
+ return tryAgainButton2;
+ }
+
+ @Override
+ public TextView getIndicatorView() {
+ return indicatorView2;
+ }
+ });
mBiometricView.setRequireConfirmation(requireConfirmation);
waitForIdleSync();
@@ -271,26 +273,51 @@ public class AuthBiometricViewTest extends SysuiTestCase {
// dialog size is known.
}
- private Bundle buildBiometricPromptBundle() {
+ @Test
+ public void testNegativeButton_whenDeviceCredentialAllowed() throws InterruptedException {
+ Button negativeButton = new Button(mContext);
+ initDialog(mContext, true /* allowDeviceCredential */, mCallback, new MockInjector() {
+ @Override
+ public Button getNegativeButton() {
+ return negativeButton;
+ }
+ });
+
+ negativeButton.performClick();
+ waitForIdleSync();
+
+ verify(mCallback).onAction(AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL);
+ }
+
+ private Bundle buildBiometricPromptBundle(boolean allowDeviceCredential) {
Bundle bundle = new Bundle();
bundle.putCharSequence(BiometricPrompt.KEY_TITLE, "Title");
- bundle.putCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT, "Negative");
+ int authenticators = Authenticator.TYPE_BIOMETRIC;
+ if (allowDeviceCredential) {
+ authenticators |= Authenticator.TYPE_CREDENTIAL;
+ } else {
+ bundle.putCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT, "Negative");
+ }
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
return bundle;
}
- private void initDialog(Context context, AuthBiometricView.Callback callback,
+ private void initDialog(Context context, boolean allowDeviceCredential,
+ AuthBiometricView.Callback callback,
Bundle savedState, MockInjector injector) {
mBiometricView = new TestableBiometricView(context, null, injector);
- mBiometricView.setBiometricPromptBundle(buildBiometricPromptBundle());
+ mBiometricView.setBiometricPromptBundle(buildBiometricPromptBundle(allowDeviceCredential));
mBiometricView.setCallback(callback);
mBiometricView.restoreState(savedState);
mBiometricView.onFinishInflateInternal();
mBiometricView.onAttachedToWindowInternal();
+
+ mBiometricView.setPanelController(mPanelController);
}
- private void initDialog(Context context, AuthBiometricView.Callback callback,
- MockInjector injector) {
- initDialog(context, callback, null /* savedState */, injector);
+ private void initDialog(Context context, boolean allowDeviceCredential,
+ AuthBiometricView.Callback callback, MockInjector injector) {
+ initDialog(context, allowDeviceCredential, callback, null /* savedState */, injector);
}
private class MockInjector extends AuthBiometricView.Injector {
@@ -338,6 +365,16 @@ public class AuthBiometricViewTest extends SysuiTestCase {
public int getDelayAfterError() {
return 0; // Keep this at 0 for tests to invoke callback immediately.
}
+
+ @Override
+ public int getMediumToLargeAnimationDurationMs() {
+ return 0;
+ }
+
+ @Override
+ public int getAnimateCredentialStartDelayMs() {
+ return 0;
+ }
}
private class TestableBiometricView extends AuthBiometricView {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
index d4fc3f842e9d..990f74ae33c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
@@ -16,12 +16,30 @@
package com.android.systemui.biometrics;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import android.content.Context;
+import android.hardware.biometrics.Authenticator;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricPrompt;
+import android.os.Bundle;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ScrollView;
import com.android.systemui.SysuiTestCase;
@@ -43,22 +61,21 @@ public class AuthContainerViewTest extends SysuiTestCase {
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
-
- AuthContainerView.Config config = new AuthContainerView.Config();
- config.mContext = mContext;
- config.mCallback = mCallback;
- mAuthContainer = new TestableAuthContainer(config);
}
@Test
public void testActionAuthenticated_sendsDismissedAuthenticated() {
+ initializeContainer(Authenticator.TYPE_BIOMETRIC);
+
mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_AUTHENTICATED);
- verify(mCallback).onDismissed(eq(AuthDialogCallback.DISMISSED_AUTHENTICATED));
+ verify(mCallback).onDismissed(eq(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED));
}
@Test
public void testActionUserCanceled_sendsDismissedUserCanceled() {
+ initializeContainer(Authenticator.TYPE_BIOMETRIC);
+
mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_USER_CANCELED);
verify(mCallback).onDismissed(eq(AuthDialogCallback.DISMISSED_USER_CANCELED));
@@ -66,6 +83,8 @@ public class AuthContainerViewTest extends SysuiTestCase {
@Test
public void testActionButtonNegative_sendsDismissedButtonNegative() {
+ initializeContainer(Authenticator.TYPE_BIOMETRIC);
+
mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_BUTTON_NEGATIVE);
verify(mCallback).onDismissed(eq(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE));
@@ -73,6 +92,8 @@ public class AuthContainerViewTest extends SysuiTestCase {
@Test
public void testActionTryAgain_sendsTryAgain() {
+ initializeContainer(Authenticator.TYPE_BIOMETRIC);
+
mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN);
verify(mCallback).onTryAgainPressed();
@@ -80,14 +101,77 @@ public class AuthContainerViewTest extends SysuiTestCase {
@Test
public void testActionError_sendsDismissedError() {
+ initializeContainer(Authenticator.TYPE_BIOMETRIC);
+
mAuthContainer.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_ERROR);
verify(mCallback).onDismissed(AuthDialogCallback.DISMISSED_ERROR);
}
+ @Test
+ public void testActionUseDeviceCredential_sendsOnDeviceCredentialPressed() {
+ initializeContainer(
+ Authenticator.TYPE_BIOMETRIC | Authenticator.TYPE_CREDENTIAL);
+
+ mAuthContainer.mBiometricCallback.onAction(
+ AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL);
+ verify(mCallback).onDeviceCredentialPressed();
+
+ // Credential view is attached to the frame layout
+ waitForIdleSync();
+ assertNotNull(mAuthContainer.mCredentialView);
+ verify(mAuthContainer.mFrameLayout).addView(eq(mAuthContainer.mCredentialView));
+ }
+
+ @Test
+ public void testAnimateToCredentialUI_invokesStartTransitionToCredentialUI() {
+ initializeContainer(
+ Authenticator.TYPE_BIOMETRIC | Authenticator.TYPE_CREDENTIAL);
+
+ mAuthContainer.mBiometricView = mock(AuthBiometricView.class);
+ mAuthContainer.animateToCredentialUI();
+ verify(mAuthContainer.mBiometricView).startTransitionToCredentialUI();
+ }
+
+ @Test
+ public void testShowBiometricUI() {
+ initializeContainer(Authenticator.TYPE_BIOMETRIC);
+
+ assertNotEquals(null, mAuthContainer.mBiometricView);
+
+ mAuthContainer.onAttachedToWindowInternal();
+ verify(mAuthContainer.mBiometricScrollView).addView(mAuthContainer.mBiometricView);
+ // Credential view is not added
+ verify(mAuthContainer.mFrameLayout, never()).addView(any());
+ }
+
+ @Test
+ public void testShowCredentialUI_doesNotInflateBiometricUI() {
+ initializeContainer(Authenticator.TYPE_CREDENTIAL);
+
+ mAuthContainer.onAttachedToWindowInternal();
+
+ assertNull(null, mAuthContainer.mBiometricView);
+ assertNotNull(mAuthContainer.mCredentialView);
+ verify(mAuthContainer.mFrameLayout).addView(mAuthContainer.mCredentialView);
+ }
+
+ private void initializeContainer(int authenticators) {
+ AuthContainerView.Config config = new AuthContainerView.Config();
+ config.mContext = mContext;
+ config.mCallback = mCallback;
+ config.mModalityMask |= BiometricAuthenticator.TYPE_FINGERPRINT;
+
+ Bundle bundle = new Bundle();
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
+ config.mBiometricPromptBundle = bundle;
+
+ mAuthContainer = new TestableAuthContainer(config);
+ }
+
private class TestableAuthContainer extends AuthContainerView {
TestableAuthContainer(AuthContainerView.Config config) {
- super(config);
+ super(config, new MockInjector());
}
@Override
@@ -95,4 +179,32 @@ public class AuthContainerViewTest extends SysuiTestCase {
mConfig.mCallback.onDismissed(reason);
}
}
+
+ private final class MockInjector extends AuthContainerView.Injector {
+ @Override
+ public ScrollView getBiometricScrollView(FrameLayout parent) {
+ return mock(ScrollView.class);
+ }
+
+ @Override
+ public FrameLayout inflateContainerView(LayoutInflater factory, ViewGroup root) {
+ return mock(FrameLayout.class);
+ }
+
+ @Override
+ public AuthPanelController getPanelController(Context context, View view,
+ boolean isManagedProfile) {
+ return mock(AuthPanelController.class);
+ }
+
+ @Override
+ public ImageView getBackgroundView(FrameLayout parent) {
+ return mock(ImageView.class);
+ }
+
+ @Override
+ public View getPanelView(FrameLayout parent) {
+ return mock(View.class);
+ }
+ }
}
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 eb7be4fa6332..b089b740fc47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -22,8 +22,11 @@ import static junit.framework.TestCase.assertNotNull;
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.doAnswer;
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;
@@ -31,16 +34,22 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.IActivityTaskManager;
import android.content.ComponentName;
+import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.hardware.biometrics.Authenticator;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.IBiometricServiceReceiverInternal;
+import android.hardware.face.FaceManager;
import android.os.Bundle;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableLooper.RunWithLooper;
+import com.android.internal.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -69,7 +78,7 @@ public class AuthControllerTest extends SysuiTestCase {
@Mock
private AuthDialog mDialog2;
- private TestableBiometricDialogImpl mBiometricDialogImpl;
+ private TestableAuthController mAuthController;
@Before
@@ -83,97 +92,122 @@ public class AuthControllerTest extends SysuiTestCase {
when(context.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE))
- .thenReturn(true);
+ .thenReturn(true);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
- .thenReturn(true);
+ .thenReturn(true);
when(mDialog1.getOpPackageName()).thenReturn("Dialog1");
when(mDialog2.getOpPackageName()).thenReturn("Dialog2");
- mBiometricDialogImpl = new TestableBiometricDialogImpl(new MockInjector());
- mBiometricDialogImpl.mContext = context;
- mBiometricDialogImpl.mComponents = mContext.getComponents();
+ when(mDialog1.isAllowDeviceCredentials()).thenReturn(false);
+ when(mDialog2.isAllowDeviceCredentials()).thenReturn(false);
- mBiometricDialogImpl.start();
+ mAuthController = new TestableAuthController(context, new MockInjector());
+ mAuthController.mComponents = mContext.getComponents();
+
+ mAuthController.start();
}
// Callback tests
@Test
public void testSendsReasonUserCanceled_whenDismissedByUserCancel() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
- mBiometricDialogImpl.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED);
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED);
verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
}
@Test
public void testSendsReasonNegative_whenDismissedByButtonNegative() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
- mBiometricDialogImpl.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE);
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE);
verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
}
@Test
public void testSendsReasonConfirmed_whenDismissedByButtonPositive() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
- mBiometricDialogImpl.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE);
- verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_CONFIRMED);
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE);
+ verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED);
}
@Test
public void testSendsReasonConfirmNotRequired_whenDismissedByAuthenticated() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
- mBiometricDialogImpl.onDismissed(AuthDialogCallback.DISMISSED_AUTHENTICATED);
- verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_CONFIRM_NOT_REQUIRED);
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED);
+ verify(mReceiver).onDialogDismissed(
+ BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED);
}
@Test
public void testSendsReasonError_whenDismissedByError() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
- mBiometricDialogImpl.onDismissed(AuthDialogCallback.DISMISSED_ERROR);
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_ERROR);
verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_ERROR);
}
@Test
- public void testSendsReasonDismissedBySystemServer_whenDismissedByServer() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
- mBiometricDialogImpl.onDismissed(AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER);
+ public void testSendsReasonServerRequested_whenDismissedByServer() throws Exception {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER);
verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED);
}
+ @Test
+ public void testSendsReasonCredentialConfirmed_whenDeviceCredentialAuthenticated()
+ throws Exception {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED);
+ verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED);
+ }
+
// Statusbar tests
@Test
public void testShowInvoked_whenSystemRequested()
throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
verify(mDialog1).show(any(), any());
}
@Test
- public void testOnAuthenticationSucceededInvoked_whenSystemRequested() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
- mBiometricDialogImpl.onBiometricAuthenticated(true, null /* failureReason */);
+ public void testOnAuthenticationSucceededInvoked_whenSystemRequested() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ mAuthController.onBiometricAuthenticated();
verify(mDialog1).onAuthenticationSucceeded();
}
@Test
- public void testOnAuthenticationFailedInvoked_whenSystemRequested() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
- final String failureReason = "failure reason";
- mBiometricDialogImpl.onBiometricAuthenticated(false, failureReason);
+ public void testOnAuthenticationFailedInvoked_whenBiometricRejected() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ mAuthController.onBiometricError(BiometricAuthenticator.TYPE_NONE,
+ BiometricConstants.BIOMETRIC_PAUSED_REJECTED,
+ 0 /* vendorCode */);
+
+ ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+ verify(mDialog1).onAuthenticationFailed(captor.capture());
+
+ assertEquals(captor.getValue(), mContext.getString(R.string.biometric_not_recognized));
+ }
+
+ @Test
+ public void testOnAuthenticationFailedInvoked_whenBiometricTimedOut() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ final int error = BiometricConstants.BIOMETRIC_ERROR_TIMEOUT;
+ final int vendorCode = 0;
+ mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode);
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
verify(mDialog1).onAuthenticationFailed(captor.capture());
- assertEquals(captor.getValue(), failureReason);
+ assertEquals(captor.getValue(), FaceManager.getErrorString(mContext, error, vendorCode));
}
@Test
- public void testOnHelpInvoked_whenSystemRequested() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
+ public void testOnHelpInvoked_whenSystemRequested() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
final String helpMessage = "help";
- mBiometricDialogImpl.onBiometricHelp(helpMessage);
+ mAuthController.onBiometricHelp(helpMessage);
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
verify(mDialog1).onHelp(captor.capture());
@@ -183,41 +217,94 @@ public class AuthControllerTest extends SysuiTestCase {
@Test
public void testOnErrorInvoked_whenSystemRequested() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
- final String errMessage = "error message";
- mBiometricDialogImpl.onBiometricError(errMessage);
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ final int error = 1;
+ final int vendorCode = 0;
+ mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode);
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
verify(mDialog1).onError(captor.capture());
- assertEquals(captor.getValue(), errMessage);
+ assertEquals(captor.getValue(), FaceManager.getErrorString(mContext, error, vendorCode));
+ }
+
+ @Test
+ public void testErrorLockout_whenCredentialAllowed_AnimatesToCredentialUI() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT;
+ final int vendorCode = 0;
+
+ when(mDialog1.isAllowDeviceCredentials()).thenReturn(true);
+
+ mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode);
+ verify(mDialog1, never()).onError(anyString());
+ verify(mDialog1).animateToCredentialUI();
+ }
+
+ @Test
+ public void testErrorLockoutPermanent_whenCredentialAllowed_AnimatesToCredentialUI() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
+ final int vendorCode = 0;
+
+ when(mDialog1.isAllowDeviceCredentials()).thenReturn(true);
+
+ mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode);
+ verify(mDialog1, never()).onError(anyString());
+ verify(mDialog1).animateToCredentialUI();
}
@Test
- public void testDismissWithoutCallbackInvoked_whenSystemRequested() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
- mBiometricDialogImpl.hideBiometricDialog();
+ public void testErrorLockout_whenCredentialNotAllowed_sendsOnError() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT;
+ final int vendorCode = 0;
+
+ when(mDialog1.isAllowDeviceCredentials()).thenReturn(false);
+
+ mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode);
+ verify(mDialog1).onError(eq(FaceManager.getErrorString(mContext, error, vendorCode)));
+ verify(mDialog1, never()).animateToCredentialUI();
+ }
+
+ @Test
+ public void testErrorLockoutPermanent_whenCredentialNotAllowed_sendsOnError() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ final int error = BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
+ final int vendorCode = 0;
+
+ when(mDialog1.isAllowDeviceCredentials()).thenReturn(false);
+
+ mAuthController.onBiometricError(BiometricAuthenticator.TYPE_FACE, error, vendorCode);
+ verify(mDialog1).onError(eq(FaceManager.getErrorString(mContext, error, vendorCode)));
+ verify(mDialog1, never()).animateToCredentialUI();
+ }
+
+ @Test
+ public void testDismissWithoutCallbackInvoked_whenSystemRequested() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ mAuthController.hideAuthenticationDialog();
verify(mDialog1).dismissFromSystemServer();
}
@Test
- public void testClientNotified_whenDismissedBySystemServer() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
- mBiometricDialogImpl.hideBiometricDialog();
+ public void testClientNotified_whenDismissedBySystemServer() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ mAuthController.hideAuthenticationDialog();
verify(mDialog1).dismissFromSystemServer();
- assertNotNull(mBiometricDialogImpl.mCurrentDialog);
- assertNotNull(mBiometricDialogImpl.mReceiver);
+ assertNotNull(mAuthController.mCurrentDialog);
+ assertNotNull(mAuthController.mReceiver);
}
// Corner case tests
@Test
- public void testShowNewDialog_beforeOldDialogDismissed_SkipsAnimations() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
+ public void testShowNewDialog_beforeOldDialogDismissed_SkipsAnimations() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
verify(mDialog1).show(any(), any());
- showDialog(BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
// First dialog should be dismissed without animation
verify(mDialog1).dismissWithoutCallback(eq(false) /* animate */);
@@ -227,11 +314,20 @@ public class AuthControllerTest extends SysuiTestCase {
}
@Test
- public void testConfigurationPersists_whenOnConfigurationChanged() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
+ public void testConfigurationPersists_whenOnConfigurationChanged() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
verify(mDialog1).show(any(), any());
- mBiometricDialogImpl.onConfigurationChanged(new Configuration());
+ // Return that the UI is in "showing" state
+ doAnswer(invocation -> {
+ Object[] args = invocation.getArguments();
+ Bundle savedState = (Bundle) args[0];
+ savedState.putInt(
+ AuthDialog.KEY_CONTAINER_STATE, AuthContainerView.STATE_SHOWING);
+ return null; // onSaveState returns void
+ }).when(mDialog1).onSaveState(any());
+
+ mAuthController.onConfigurationChanged(new Configuration());
ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
verify(mDialog1).onSaveState(captor.capture());
@@ -248,37 +344,63 @@ public class AuthControllerTest extends SysuiTestCase {
}
@Test
+ public void testConfigurationPersists_whenBiometricFallbackToCredential() {
+ showDialog(Authenticator.TYPE_CREDENTIAL | Authenticator.TYPE_BIOMETRIC,
+ BiometricPrompt.TYPE_FACE);
+ verify(mDialog1).show(any(), any());
+
+ // Pretend that the UI is now showing device credential UI.
+ doAnswer(invocation -> {
+ Object[] args = invocation.getArguments();
+ Bundle savedState = (Bundle) args[0];
+ savedState.putInt(
+ AuthDialog.KEY_CONTAINER_STATE, AuthContainerView.STATE_SHOWING);
+ savedState.putBoolean(AuthDialog.KEY_CREDENTIAL_SHOWING, true);
+ return null; // onSaveState returns void
+ }).when(mDialog1).onSaveState(any());
+
+ mAuthController.onConfigurationChanged(new Configuration());
+
+ // Check that the new dialog was initialized to the credential UI.
+ ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mDialog2).show(any(), captor.capture());
+ assertEquals(Authenticator.TYPE_CREDENTIAL,
+ mAuthController.mLastBiometricPromptBundle
+ .getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
+ }
+
+ @Test
public void testClientNotified_whenTaskStackChangesDuringAuthentication() throws Exception {
- showDialog(BiometricPrompt.TYPE_FACE);
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
List<ActivityManager.RunningTaskInfo> tasks = new ArrayList<>();
ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
taskInfo.topActivity = mock(ComponentName.class);
when(taskInfo.topActivity.getPackageName()).thenReturn("other_package");
tasks.add(taskInfo);
- when(mBiometricDialogImpl.mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks);
+ when(mAuthController.mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks);
- mBiometricDialogImpl.mTaskStackListener.onTaskStackChanged();
+ mAuthController.mTaskStackListener.onTaskStackChanged();
waitForIdleSync();
- assertNull(mBiometricDialogImpl.mCurrentDialog);
- assertNull(mBiometricDialogImpl.mReceiver);
+ assertNull(mAuthController.mCurrentDialog);
+ assertNull(mAuthController.mReceiver);
verify(mDialog1).dismissWithoutCallback(true /* animate */);
verify(mReceiver).onDialogDismissed(eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL));
}
// Helpers
- private void showDialog(int type) {
- mBiometricDialogImpl.showBiometricDialog(createTestDialogBundle(),
+ private void showDialog(int authenticators, int biometricModality) {
+ mAuthController.showAuthenticationDialog(createTestDialogBundle(authenticators),
mReceiver /* receiver */,
- type,
+ biometricModality,
true /* requireConfirmation */,
0 /* userId */,
"testPackage");
}
- private Bundle createTestDialogBundle() {
+ private Bundle createTestDialogBundle(int authenticators) {
Bundle bundle = new Bundle();
bundle.putCharSequence(BiometricPrompt.KEY_TITLE, "Title");
@@ -290,20 +412,26 @@ public class AuthControllerTest extends SysuiTestCase {
// by user settings, and should be tested in BiometricService.
bundle.putBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true);
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
+
return bundle;
}
- private final class TestableBiometricDialogImpl extends AuthController {
+ private final class TestableAuthController extends AuthController {
private int mBuildCount = 0;
+ private Bundle mLastBiometricPromptBundle;
- public TestableBiometricDialogImpl(Injector injector) {
- super(injector);
+ TestableAuthController(Context context, Injector injector) {
+ super(context, injector);
}
@Override
protected AuthDialog buildDialog(Bundle biometricPromptBundle,
boolean requireConfirmation, int userId, int type, String opPackageName,
boolean skipIntro) {
+
+ mLastBiometricPromptBundle = biometricPromptBundle;
+
AuthDialog dialog;
if (mBuildCount == 0) {
dialog = mDialog1;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
index 011c2cd57588..e838d9e94a31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
@@ -70,6 +70,8 @@ class UserBroadcastDispatcherTest : SysuiTestCase() {
private lateinit var mockContext: Context
@Mock
private lateinit var mockHandler: Handler
+ @Mock
+ private lateinit var mPendingResult: BroadcastReceiver.PendingResult
@Captor
private lateinit var argumentCaptor: ArgumentCaptor<IntentFilter>
@@ -88,6 +90,7 @@ class UserBroadcastDispatcherTest : SysuiTestCase() {
universalBroadcastReceiver = UserBroadcastDispatcher(
mockContext, USER_ID, handler, testableLooper.looper)
+ universalBroadcastReceiver.pendingResult = mPendingResult
}
@Test
@@ -227,4 +230,19 @@ class UserBroadcastDispatcherTest : SysuiTestCase() {
verify(broadcastReceiver).onReceive(mockContext, intent)
verify(broadcastReceiverOther).onReceive(mockContext, intent)
}
+
+ @Test
+ fun testPendingResult() {
+ intentFilter = IntentFilter(ACTION_1)
+ universalBroadcastReceiver.registerReceiver(
+ ReceiverData(broadcastReceiver, intentFilter, handler, USER_HANDLE))
+
+ val intent = Intent(ACTION_1)
+ universalBroadcastReceiver.onReceive(mockContext, intent)
+
+ testableLooper.processAllMessages()
+
+ verify(broadcastReceiver).onReceive(mockContext, intent)
+ verify(broadcastReceiver).pendingResult = mPendingResult
+ }
}
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 448c80ef3c57..00681130074b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -33,6 +33,7 @@ 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.anyString;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -55,8 +56,10 @@ import android.widget.FrameLayout;
import androidx.test.filters.SmallTest;
+import com.android.internal.colorextraction.ColorExtractor;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -74,6 +77,7 @@ import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -90,7 +94,6 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class BubbleControllerTest extends SysuiTestCase {
-
@Mock
private NotificationEntryManager mNotificationEntryManager;
@Mock
@@ -140,6 +143,10 @@ public class BubbleControllerTest extends SysuiTestCase {
private BubbleController.BubbleExpandListener mBubbleExpandListener;
@Mock
private PendingIntent mDeleteIntent;
+ @Mock
+ private SysuiColorExtractor mColorExtractor;
+ @Mock
+ ColorExtractor.GradientColors mGradientColors;
private BubbleData mBubbleData;
@@ -149,23 +156,24 @@ public class BubbleControllerTest extends SysuiTestCase {
mStatusBarView = new FrameLayout(mContext);
mDependency.injectTestDependency(NotificationEntryManager.class, mNotificationEntryManager);
mContext.addMockSystemService(FaceManager.class, mFaceManager);
+ when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
// Bubbles get added to status bar window view
mStatusBarWindowController = new StatusBarWindowController(mContext, mWindowManager,
mActivityManager, mDozeParameters, mStatusBarStateController,
- mConfigurationController, mKeyguardBypassController);
+ mConfigurationController, mKeyguardBypassController, mColorExtractor);
mStatusBarWindowController.add(mStatusBarView, 120 /* height */);
// Need notifications for bubbles
- mNotificationTestHelper = new NotificationTestHelper(mContext);
+ mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
mNonBubbleNotifRow = mNotificationTestHelper.createRow();
// Return non-null notification data from the NEM
when(mNotificationEntryManager.getNotificationData()).thenReturn(mNotificationData);
- when(mNotificationData.get(mRow.getEntry().key)).thenReturn(mRow.getEntry());
- when(mNotificationData.getChannel(mRow.getEntry().key)).thenReturn(
+ when(mNotificationData.get(mRow.getEntry().getKey())).thenReturn(mRow.getEntry());
+ when(mNotificationData.getChannel(mRow.getEntry().getKey())).thenReturn(
mRow.getEntry().getChannel());
mZenModeConfig.suppressedVisualEffects = 0;
@@ -174,7 +182,8 @@ public class BubbleControllerTest extends SysuiTestCase {
TestableNotificationInterruptionStateProvider interruptionStateProvider =
new TestableNotificationInterruptionStateProvider(mContext,
mock(NotificationFilter.class),
- mock(StatusBarStateController.class));
+ mock(StatusBarStateController.class),
+ mock(BatteryController.class));
interruptionStateProvider.setUpWithPresenter(
mock(NotificationPresenter.class),
mock(HeadsUpManager.class),
@@ -219,15 +228,16 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testRemoveBubble() {
mBubbleController.updateBubble(mRow.getEntry());
- assertNotNull(mBubbleData.getBubbleWithKey(mRow.getEntry().key));
+ assertNotNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
assertTrue(mBubbleController.hasBubbles());
- verify(mNotificationEntryManager).updateNotifications();
+ verify(mNotificationEntryManager).updateNotifications(any());
verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
- mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleController.removeBubble(
+ mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
assertFalse(mStatusBarWindowController.getBubblesShowing());
- assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().key));
- verify(mNotificationEntryManager, times(2)).updateNotifications();
+ assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ verify(mNotificationEntryManager, times(2)).updateNotifications(anyString());
verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
}
@@ -237,36 +247,38 @@ public class BubbleControllerTest extends SysuiTestCase {
mBubbleController.updateBubble(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry().key));
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
// Make it look like dismissed notif
- mBubbleData.getBubbleWithKey(mRow.getEntry().key).setShowInShadeWhenBubble(false);
+ mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).setShowInShadeWhenBubble(false);
// Now remove the bubble
- mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleController.removeBubble(
+ mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
// Since the notif is dismissed, once the bubble is removed, performRemoveNotification gets
// called to really remove the notif
verify(mNotificationEntryManager, times(1)).performRemoveNotification(
- mRow.getEntry().notification, UNDEFINED_DISMISS_REASON);
+ mRow.getEntry().getSbn(), UNDEFINED_DISMISS_REASON);
assertFalse(mBubbleController.hasBubbles());
}
@Test
public void testDismissStack() {
mBubbleController.updateBubble(mRow.getEntry());
- verify(mNotificationEntryManager, times(1)).updateNotifications();
- assertNotNull(mBubbleData.getBubbleWithKey(mRow.getEntry().key));
+ verify(mNotificationEntryManager, times(1)).updateNotifications(any());
+ assertNotNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
mBubbleController.updateBubble(mRow2.getEntry());
- verify(mNotificationEntryManager, times(2)).updateNotifications();
- assertNotNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().key));
+ verify(mNotificationEntryManager, times(2)).updateNotifications(any());
+ assertNotNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey()));
assertTrue(mBubbleController.hasBubbles());
mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
assertFalse(mStatusBarWindowController.getBubblesShowing());
- verify(mNotificationEntryManager, times(3)).updateNotifications();
- assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().key));
- assertNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().key));
+ verify(mNotificationEntryManager, times(3)).updateNotifications(any());
+ assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ assertNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey()));
}
@Test
@@ -279,22 +291,24 @@ public class BubbleControllerTest extends SysuiTestCase {
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry().key));
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
assertFalse(mStatusBarWindowController.getBubbleExpanded());
// Expand the stack
BubbleStackView stackView = mBubbleController.getStackView();
mBubbleController.expandStack();
assertTrue(mBubbleController.isStackExpanded());
- verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
+ verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
assertTrue(mStatusBarWindowController.getBubbleExpanded());
// Make sure the notif is suppressed
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry().key));
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
// Collapse
mBubbleController.collapseStack();
- verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key);
+ verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey());
assertFalse(mBubbleController.isStackExpanded());
assertFalse(mStatusBarWindowController.getBubbleExpanded());
}
@@ -309,30 +323,33 @@ public class BubbleControllerTest extends SysuiTestCase {
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry().key));
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mRow2.getEntry().key));
+ mRow.getEntry().getKey()));
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow2.getEntry().getKey()));
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
mBubbleController.expandStack();
assertTrue(mBubbleController.isStackExpanded());
- verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key);
+ verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
// Last added is the one that is expanded
assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry());
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow2.getEntry().key));
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow2.getEntry().getKey()));
// Switch which bubble is expanded
- mBubbleController.selectBubble(mRow.getEntry().key);
- stackView.setExpandedBubble(mRow.getEntry().key);
+ mBubbleController.selectBubble(mRow.getEntry().getKey());
+ stackView.setExpandedBubble(mRow.getEntry().getKey());
assertEquals(mRow.getEntry(), stackView.getExpandedBubbleView().getEntry());
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry().key));
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
// collapse for previous bubble
- verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().key);
+ verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
// expand for selected bubble
- verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
+ verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
// Collapse
mBubbleController.collapseStack();
@@ -347,18 +364,20 @@ public class BubbleControllerTest extends SysuiTestCase {
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry().key));
- assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().key).showBubbleDot());
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
+ assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showBubbleDot());
// Expand
mBubbleController.expandStack();
assertTrue(mBubbleController.isStackExpanded());
- verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
+ verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
// Notif is suppressed after expansion
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry().key));
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
// Notif shouldn't show dot after expansion
- assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().key).showBubbleDot());
+ assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showBubbleDot());
}
@Test
@@ -369,28 +388,31 @@ public class BubbleControllerTest extends SysuiTestCase {
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry().key));
- assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().key).showBubbleDot());
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
+ assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showBubbleDot());
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
mBubbleController.expandStack();
assertTrue(mBubbleController.isStackExpanded());
- verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
+ verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
// Notif is suppressed after expansion
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry().key));
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
// Notif shouldn't show dot after expansion
- assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().key).showBubbleDot());
+ assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showBubbleDot());
// Send update
mEntryListener.onPreEntryUpdated(mRow.getEntry());
// Nothing should have changed
// Notif is suppressed after expansion
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry().key));
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
// Notif shouldn't show dot after expansion
- assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().key).showBubbleDot());
+ assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showBubbleDot());
}
@Test
@@ -407,27 +429,28 @@ public class BubbleControllerTest extends SysuiTestCase {
mBubbleController.expandStack();
assertTrue(mBubbleController.isStackExpanded());
- verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key);
+ verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
// Last added is the one that is expanded
assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry());
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow2.getEntry().key));
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow2.getEntry().getKey()));
// Dismiss currently expanded
mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey(),
BubbleController.DISMISS_USER_GESTURE);
- verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().key);
+ verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
// Make sure first bubble is selected
assertEquals(mRow.getEntry(), stackView.getExpandedBubbleView().getEntry());
- verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
+ verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
// Dismiss that one
mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey(),
BubbleController.DISMISS_USER_GESTURE);
// Make sure state changes and collapse happens
- verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key);
+ verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey());
verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
assertFalse(mBubbleController.hasBubbles());
}
@@ -444,7 +467,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Expansion shouldn't change
verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */,
- mRow.getEntry().key);
+ mRow.getEntry().getKey());
assertFalse(mBubbleController.isStackExpanded());
// # of bubbles should change
@@ -462,7 +485,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Expansion should change
verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */,
- mRow.getEntry().key);
+ mRow.getEntry().getKey());
assertTrue(mBubbleController.isStackExpanded());
// # of bubbles should change
@@ -479,7 +502,8 @@ public class BubbleControllerTest extends SysuiTestCase {
mBubbleController.updateBubble(mRow.getEntry());
// Should not be suppressed because we weren't forground
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry().key));
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
// # of bubbles should change
verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
}
@@ -494,7 +518,8 @@ public class BubbleControllerTest extends SysuiTestCase {
mBubbleController.updateBubble(mRow.getEntry());
// Notif should be suppressed because we were foreground
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry().key));
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
// # of bubbles should change
verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
@@ -502,13 +527,14 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testExpandStackAndSelectBubble_removedFirst() {
- final String key = mRow.getEntry().key;
+ final String key = mRow.getEntry().getKey();
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
// Simulate notification cancellation.
- mRemoveInterceptor.onNotificationRemoveRequested(mRow.getEntry().key, REASON_APP_CANCEL);
+ mRemoveInterceptor.onNotificationRemoveRequested(
+ mRow.getEntry().getKey(), REASON_APP_CANCEL);
mBubbleController.expandStackAndSelectBubble(key);
}
@@ -516,8 +542,9 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testMarkNewNotificationAsShowInShade() {
mEntryListener.onPendingEntryAdded(mRow.getEntry());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry().key));
- assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().key).showBubbleDot());
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
+ assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showBubbleDot());
}
@Test
@@ -532,14 +559,15 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
- mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_AGED);
+ mBubbleController.removeBubble(mRow.getEntry().getKey(), BubbleController.DISMISS_AGED);
verify(mDeleteIntent, never()).send();
}
@Test
public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
- mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleController.removeBubble(
+ mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
verify(mDeleteIntent, times(1)).send();
}
@@ -557,7 +585,7 @@ public class BubbleControllerTest extends SysuiTestCase {
mBubbleController.updateBubble(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
- mRow.getEntry().notification.getNotification().flags &= ~FLAG_BUBBLE;
+ mRow.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE;
mEntryListener.onPreEntryUpdated(mRow.getEntry());
assertFalse(mBubbleController.hasBubbles());
@@ -572,7 +600,7 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleController.hasBubbles());
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
- mRow.getEntry().key, REASON_APP_CANCEL);
+ mRow.getEntry().getKey(), REASON_APP_CANCEL);
// Cancels always remove so no need to intercept
assertFalse(intercepted);
@@ -585,15 +613,17 @@ public class BubbleControllerTest extends SysuiTestCase {
mBubbleController.updateBubble(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry().key));
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
- mRow.getEntry().key, REASON_CANCEL_ALL);
+ mRow.getEntry().getKey(), REASON_CANCEL_ALL);
// Intercept!
assertTrue(intercepted);
// Should update show in shade state
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry().key));
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
verify(mNotificationEntryManager, never()).performRemoveNotification(
any(), anyInt());
@@ -606,15 +636,17 @@ public class BubbleControllerTest extends SysuiTestCase {
mBubbleController.updateBubble(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry().key));
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
- mRow.getEntry().key, REASON_CANCEL);
+ mRow.getEntry().getKey(), REASON_CANCEL);
// Intercept!
assertTrue(intercepted);
// Should update show in shade state
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry().key));
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
verify(mNotificationEntryManager, never()).performRemoveNotification(
any(), anyInt());
@@ -627,15 +659,17 @@ public class BubbleControllerTest extends SysuiTestCase {
mBubbleController.updateBubble(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry().key));
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
// Dismiss the bubble
- mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_USER_GESTURE);
+ mBubbleController.removeBubble(
+ mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
assertFalse(mBubbleController.hasBubbles());
// Dismiss the notification
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
- mRow.getEntry().key, REASON_CANCEL);
+ mRow.getEntry().getKey(), REASON_CANCEL);
// It's no longer a bubble so we shouldn't intercept
assertFalse(intercepted);
@@ -660,8 +694,9 @@ public class BubbleControllerTest extends SysuiTestCase {
NotificationInterruptionStateProvider {
TestableNotificationInterruptionStateProvider(Context context,
- NotificationFilter filter, StatusBarStateController controller) {
- super(context, filter, controller);
+ NotificationFilter filter, StatusBarStateController controller,
+ BatteryController batteryController) {
+ super(context, filter, controller, batteryController);
mUseHeadsUp = true;
}
}
@@ -684,7 +719,7 @@ public class BubbleControllerTest extends SysuiTestCase {
*/
private void setMetadataFlags(NotificationEntry entry, int flag, boolean enableFlag) {
Notification.BubbleMetadata bubbleMetadata =
- entry.notification.getNotification().getBubbleMetadata();
+ entry.getSbn().getNotification().getBubbleMetadata();
int flags = bubbleMetadata.getFlags();
if (enableFlag) {
flags |= flag;
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 392a7cbc4d6c..67f65e611e4e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -99,7 +99,7 @@ public class BubbleDataTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
- mNotificationTestHelper = new NotificationTestHelper(mContext);
+ mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
MockitoAnnotations.initMocks(this);
mEntryA1 = createBubbleEntry(1, "a1", "package.a");
@@ -842,14 +842,14 @@ public class BubbleDataTest extends SysuiTestCase {
}
private void setPostTime(NotificationEntry entry, long postTime) {
- when(entry.notification.getPostTime()).thenReturn(postTime);
+ when(entry.getSbn().getPostTime()).thenReturn(postTime);
}
private void setOngoing(NotificationEntry entry, boolean ongoing) {
if (ongoing) {
- entry.notification.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ entry.getSbn().getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
} else {
- entry.notification.getNotification().flags &= ~Notification.FLAG_FOREGROUND_SERVICE;
+ entry.getSbn().getNotification().flags &= ~Notification.FLAG_FOREGROUND_SERVICE;
}
}
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 d011e486d2e0..3ba5d1ac79ea 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
@@ -16,6 +16,8 @@
package com.android.systemui.classifier.brightline;
+import static com.android.systemui.classifier.Classifier.UNLOCK;
+
import android.util.DisplayMetrics;
import android.view.MotionEvent;
@@ -42,6 +44,7 @@ public class ClassifierTest extends SysuiTestCase {
displayMetrics.widthPixels = 1000;
displayMetrics.heightPixels = 1000;
mDataProvider = new FalsingDataProvider(displayMetrics);
+ mDataProvider.setInteractionType(UNLOCK);
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java
index 341b74b33784..96b2028da326 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.classifier.brightline;
+import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
+
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
@@ -74,4 +76,21 @@ public class PointerCountClassifierTest extends ClassifierTest {
motionEvent.recycle();
assertThat(mClassifier.isFalseTouch(), is(true));
}
+
+ @Test
+ public void testPass_multiPointerDragDown() {
+ MotionEvent.PointerProperties[] pointerProperties =
+ MotionEvent.PointerProperties.createArray(2);
+ pointerProperties[0].id = 0;
+ pointerProperties[1].id = 1;
+ MotionEvent.PointerCoords[] pointerCoords = MotionEvent.PointerCoords.createArray(2);
+ MotionEvent motionEvent = MotionEvent.obtain(
+ 1, 1, MotionEvent.ACTION_DOWN, 2, pointerProperties, pointerCoords, 0, 0, 0, 0, 0,
+ 0,
+ 0, 0);
+ mClassifier.onTouchEvent(motionEvent);
+ motionEvent.recycle();
+ getDataProvider().setInteractionType(QUICK_SETTINGS);
+ assertThat(mClassifier.isFalseTouch(), is(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 af2de1be1d57..98ec45947f79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
@@ -20,13 +20,11 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
-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 android.app.Instrumentation;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
import android.os.Looper;
@@ -34,7 +32,6 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -43,32 +40,26 @@ import com.android.systemui.dock.DockManagerFake;
import com.android.systemui.doze.DozeMachine.State;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class DozeDockHandlerTest extends SysuiTestCase {
- private DozeDockHandler mDockHandler;
+ @Mock
private DozeMachine mMachine;
- private DozeHostFake mHost;
+ @Mock
+ private DozeHost mHost;
private AmbientDisplayConfiguration mConfig;
- private Instrumentation mInstrumentation;
private DockManagerFake mDockManagerFake;
-
- @BeforeClass
- public static void setupSuite() {
- // We can't use KeyguardUpdateMonitor from tests.
- DozeLog.setRegisterKeyguardCallback(false);
- }
+ private DozeDockHandler mDockHandler;
@Before
public void setUp() throws Exception {
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
- mMachine = mock(DozeMachine.class);
- mHost = spy(new DozeHostFake());
+ MockitoAnnotations.initMocks(this);
mConfig = DozeConfigurationUtil.createMockConfig();
doReturn(false).when(mConfig).alwaysOnEnabled(anyInt());
@@ -95,7 +86,7 @@ public class DozeDockHandlerTest extends SysuiTestCase {
mDockManagerFake.setDockEvent(DockManager.STATE_DOCKED);
- verify(mMachine).requestPulse(eq(DozeLog.PULSE_REASON_DOCKING));
+ verify(mMachine).requestPulse(eq(DozeEvent.PULSE_REASON_DOCKING));
}
@Test
@@ -105,14 +96,14 @@ public class DozeDockHandlerTest extends SysuiTestCase {
mDockManagerFake.setDockEvent(DockManager.STATE_DOCKED);
- verify(mMachine).requestPulse(eq(DozeLog.PULSE_REASON_DOCKING));
+ verify(mMachine).requestPulse(eq(DozeEvent.PULSE_REASON_DOCKING));
}
@Test
public void testOnEvent_dockedHideWhenPulsing_requestPulseOut() {
mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
when(mMachine.getState()).thenReturn(State.DOZE_PULSING);
- when(mMachine.getPulseReason()).thenReturn(DozeLog.PULSE_REASON_DOCKING);
+ when(mMachine.getPulseReason()).thenReturn(DozeEvent.PULSE_REASON_DOCKING);
mDockManagerFake.setDockEvent(DockManager.STATE_DOCKED_HIDE);
@@ -123,7 +114,7 @@ public class DozeDockHandlerTest extends SysuiTestCase {
public void testOnEvent_undockedWhenPulsing_requestPulseOut() {
mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE_PULSING);
- when(mMachine.getPulseReason()).thenReturn(DozeLog.PULSE_REASON_DOCKING);
+ when(mMachine.getPulseReason()).thenReturn(DozeEvent.PULSE_REASON_DOCKING);
mDockManagerFake.setDockEvent(DockManager.STATE_NONE);
@@ -161,7 +152,7 @@ public class DozeDockHandlerTest extends SysuiTestCase {
TestableLooper.get(this).processAllMessages();
- verify(mMachine).requestPulse(eq(DozeLog.PULSE_REASON_DOCKING));
+ verify(mMachine).requestPulse(eq(DozeEvent.PULSE_REASON_DOCKING));
}
@Test
@@ -174,7 +165,7 @@ public class DozeDockHandlerTest extends SysuiTestCase {
TestableLooper.get(this).processAllMessages();
- verify(mMachine).requestPulse(eq(DozeLog.PULSE_REASON_DOCKING));
+ verify(mMachine).requestPulse(eq(DozeEvent.PULSE_REASON_DOCKING));
}
@Test
@@ -186,7 +177,7 @@ public class DozeDockHandlerTest extends SysuiTestCase {
mDockHandler.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
- verify(mMachine, never()).requestPulse(eq(DozeLog.PULSE_REASON_DOCKING));
+ verify(mMachine, never()).requestPulse(eq(DozeEvent.PULSE_REASON_DOCKING));
}
@Test
@@ -205,7 +196,7 @@ public class DozeDockHandlerTest extends SysuiTestCase {
public void testTransitionToPulsing_whenDockedHide_requestPulseOut() {
mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE_PULSING);
- when(mMachine.getPulseReason()).thenReturn(DozeLog.PULSE_REASON_DOCKING);
+ when(mMachine.getPulseReason()).thenReturn(DozeEvent.PULSE_REASON_DOCKING);
mDockManagerFake.setDockEvent(DockManager.STATE_DOCKED_HIDE);
mDockHandler.transitionTo(DozeMachine.State.INITIALIZED, State.DOZE_PULSING);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
deleted file mode 100644
index abfa755671db..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.doze;
-
-import android.annotation.NonNull;
-
-/**
- * A rudimentary fake for DozeHost.
- */
-class DozeHostFake implements DozeHost {
- Callback callback;
- boolean pulseExtended;
- boolean animateWakeup;
- boolean animateScreenOff;
- boolean dozing;
- float doubleTapX;
- float doubleTapY;
- float aodDimmingScrimOpacity;
-
- @Override
- public void addCallback(@NonNull Callback callback) {
- this.callback = callback;
- }
-
- @Override
- public void removeCallback(@NonNull Callback callback) {
- this.callback = null;
- }
-
- @Override
- public void startDozing() {
- dozing = true;
- }
-
- @Override
- public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) {
- throw new RuntimeException("not implemented");
- }
-
- @Override
- public void stopDozing() {
- dozing = false;
- }
-
- @Override
- public void dozeTimeTick() {
- // Nothing to do in here. Real host would just update the UI.
- }
-
- @Override
- public boolean isPowerSaveActive() {
- return false;
- }
-
- @Override
- public boolean isPulsingBlocked() {
- return false;
- }
-
- @Override
- public boolean isProvisioned() {
- return false;
- }
-
- @Override
- public boolean isBlockingDoze() {
- return false;
- }
-
- @Override
- public void onIgnoreTouchWhilePulsing(boolean ignore) {
- }
-
- @Override
- public void extendPulse(int reason) {
- pulseExtended = true;
- }
-
- @Override
- public void stopPulsing() {}
-
- @Override
- public void setAnimateWakeup(boolean animateWakeup) {
- this.animateWakeup = animateWakeup;
- }
-
- @Override
- public void setAnimateScreenOff(boolean animateScreenOff) {
- this.animateScreenOff = animateScreenOff;
- }
-
- @Override
- public void onSlpiTap(float x, float y) {
- doubleTapX = y;
- doubleTapY = y;
- }
-
- @Override
- public void setDozeScreenBrightness(int value) {
- }
-
- @Override
- public void setAodDimmingScrim(float scrimOpacity) {
- aodDimmingScrimOpacity = scrimOpacity;
- }
-}
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 1e18e51bc079..bbd2ab12099f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -46,6 +46,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.wakelock.WakeLockFake;
import org.junit.Before;
@@ -63,6 +64,8 @@ public class DozeMachineTest extends SysuiTestCase {
@Mock
private WakefulnessLifecycle mWakefulnessLifecycle;
+ @Mock
+ private DozeLog mDozeLog;
private DozeServiceFake mServiceFake;
private WakeLockFake mWakeLockFake;
private AmbientDisplayConfiguration mConfigMock;
@@ -76,8 +79,8 @@ public class DozeMachineTest extends SysuiTestCase {
mConfigMock = mock(AmbientDisplayConfiguration.class);
mPartMock = mock(DozeMachine.Part.class);
- mMachine = new DozeMachine(mServiceFake, mConfigMock, mWakeLockFake, mWakefulnessLifecycle);
-
+ mMachine = new DozeMachine(mServiceFake, mConfigMock, mWakeLockFake,
+ mWakefulnessLifecycle, mock(BatteryController.class), mDozeLog);
mMachine.setParts(new DozeMachine.Part[]{mPartMock});
}
@@ -112,7 +115,7 @@ public class DozeMachineTest extends SysuiTestCase {
public void testPulseDone_goesToDoze() {
when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(false);
mMachine.requestState(INITIALIZED);
- mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+ mMachine.requestPulse(DozeEvent.PULSE_REASON_NOTIFICATION);
mMachine.requestState(DOZE_PULSING);
mMachine.requestState(DOZE_PULSE_DONE);
@@ -125,7 +128,7 @@ public class DozeMachineTest extends SysuiTestCase {
public void testPulseDone_goesToAoD() {
when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true);
mMachine.requestState(INITIALIZED);
- mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+ mMachine.requestPulse(DozeEvent.PULSE_REASON_NOTIFICATION);
mMachine.requestState(DOZE_PULSING);
mMachine.requestState(DOZE_PULSE_DONE);
@@ -169,7 +172,7 @@ public class DozeMachineTest extends SysuiTestCase {
public void testWakeLock_heldInPulseStates() {
mMachine.requestState(INITIALIZED);
- mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+ mMachine.requestPulse(DozeEvent.PULSE_REASON_NOTIFICATION);
assertTrue(mWakeLockFake.isHeld());
mMachine.requestState(DOZE_PULSING);
@@ -192,7 +195,7 @@ public class DozeMachineTest extends SysuiTestCase {
mMachine.requestState(INITIALIZED);
mMachine.requestState(DOZE);
- mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+ mMachine.requestPulse(DozeEvent.PULSE_REASON_NOTIFICATION);
mMachine.requestState(DOZE_PULSING);
mMachine.requestState(DOZE_PULSE_DONE);
@@ -204,9 +207,9 @@ public class DozeMachineTest extends SysuiTestCase {
mMachine.requestState(INITIALIZED);
mMachine.requestState(DOZE);
- mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+ mMachine.requestPulse(DozeEvent.PULSE_REASON_NOTIFICATION);
mMachine.requestState(DOZE_PULSING);
- mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+ mMachine.requestPulse(DozeEvent.PULSE_REASON_NOTIFICATION);
mMachine.requestState(DOZE_PULSE_DONE);
}
@@ -215,7 +218,7 @@ public class DozeMachineTest extends SysuiTestCase {
mMachine.requestState(INITIALIZED);
mMachine.requestState(DOZE);
- mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+ mMachine.requestPulse(DozeEvent.PULSE_REASON_NOTIFICATION);
mMachine.requestState(DOZE_PULSE_DONE);
}
@@ -228,7 +231,7 @@ public class DozeMachineTest extends SysuiTestCase {
return null;
}).when(mPartMock).transitionTo(any(), eq(DOZE_REQUEST_PULSE));
- mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+ mMachine.requestPulse(DozeEvent.PULSE_REASON_NOTIFICATION);
assertEquals(DOZE_PULSING, mMachine.getState());
}
@@ -237,9 +240,9 @@ public class DozeMachineTest extends SysuiTestCase {
public void testPulseReason_getMatchesRequest() {
mMachine.requestState(INITIALIZED);
mMachine.requestState(DOZE);
- mMachine.requestPulse(DozeLog.REASON_SENSOR_DOUBLE_TAP);
+ mMachine.requestPulse(DozeEvent.REASON_SENSOR_DOUBLE_TAP);
- assertEquals(DozeLog.REASON_SENSOR_DOUBLE_TAP, mMachine.getPulseReason());
+ assertEquals(DozeEvent.REASON_SENSOR_DOUBLE_TAP, mMachine.getPulseReason());
}
@Test
@@ -251,7 +254,7 @@ public class DozeMachineTest extends SysuiTestCase {
if (newState == DOZE_REQUEST_PULSE
|| newState == DOZE_PULSING
|| newState == DOZE_PULSE_DONE) {
- assertEquals(DozeLog.PULSE_REASON_NOTIFICATION, mMachine.getPulseReason());
+ assertEquals(DozeEvent.PULSE_REASON_NOTIFICATION, mMachine.getPulseReason());
} else {
assertTrue("unexpected state " + newState,
newState == DOZE || newState == DOZE_AOD);
@@ -259,7 +262,7 @@ public class DozeMachineTest extends SysuiTestCase {
return null;
}).when(mPartMock).transitionTo(any(), any());
- mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+ mMachine.requestPulse(DozeEvent.PULSE_REASON_NOTIFICATION);
mMachine.requestState(DOZE_PULSING);
mMachine.requestState(DOZE_PULSE_DONE);
}
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 aa62e9aca4fe..316b891080ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -30,6 +30,11 @@ import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
import android.content.Intent;
import android.os.PowerManager;
@@ -45,6 +50,8 @@ import com.android.systemui.util.sensors.FakeSensorManager;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -55,22 +62,27 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
static final int[] SENSOR_TO_OPACITY = new int[]{-1, 10, 0, 0, 0};
DozeServiceFake mServiceFake;
- DozeScreenBrightness mScreen;
FakeSensorManager.FakeGenericSensor mSensor;
FakeSensorManager mSensorManager;
- DozeHostFake mHostFake;
+ @Mock
+ DozeHost mDozeHost;
+ DozeScreenBrightness mScreen;
@Before
public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
Settings.System.putIntForUser(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS, DEFAULT_BRIGHTNESS,
UserHandle.USER_CURRENT);
+ doAnswer(invocation -> {
+ ((Runnable) invocation.getArgument(0)).run();
+ return null;
+ }).when(mDozeHost).prepareForGentleSleep(any());
mServiceFake = new DozeServiceFake();
- mHostFake = new DozeHostFake();
mSensorManager = new FakeSensorManager(mContext);
mSensor = mSensorManager.getFakeLightSensor();
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
- mSensor.getSensor(), mHostFake, null /* handler */,
+ mSensor.getSensor(), mDozeHost, null /* handler */,
DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY,
true /* debuggable */);
}
@@ -173,7 +185,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
@Test
public void testNullSensor() throws Exception {
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
- null /* sensor */, mHostFake, null /* handler */,
+ null /* sensor */, mDozeHost, null /* handler */,
DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY,
true /* debuggable */);
@@ -203,26 +215,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
mSensor.sendSensorEvent(0);
assertEquals(1, mServiceFake.screenBrightness);
- assertEquals(10/255f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
- }
-
- @Test
- public void pausingAod_softBlanks() throws Exception {
- mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
- mScreen.transitionTo(INITIALIZED, DOZE_AOD);
-
- mSensor.sendSensorEvent(2);
-
- mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING);
- mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED);
-
- assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
-
- mSensor.sendSensorEvent(0);
- assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
-
- mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD);
- assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+ verify(mDozeHost).setAodDimmingScrim(eq(10f / 255f));
}
@Test
@@ -232,8 +225,9 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING);
mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED);
+ reset(mDozeHost);
mSensor.sendSensorEvent(1);
- assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+ verify(mDozeHost).setAodDimmingScrim(eq(1f));
}
@Test
@@ -241,11 +235,12 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mScreen.transitionTo(DOZE_AOD, DOZE);
- assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+ verify(mDozeHost).setAodDimmingScrim(eq(1f));
+ reset(mDozeHost);
mScreen.transitionTo(DOZE, DOZE_AOD);
mSensor.sendSensorEvent(2);
- assertEquals(0f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+ verify(mDozeHost).setAodDimmingScrim(eq(0f));
}
@Test
@@ -260,11 +255,10 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
mSensor.sendSensorEvent(0);
+ reset(mDozeHost);
mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD);
-
mSensor.sendSensorEvent(2);
-
- assertEquals(0f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+ verify(mDozeHost).setAodDimmingScrim(eq(0f));
}
@Test
@@ -273,11 +267,11 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mSensor.sendSensorEvent(2);
-
mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING);
mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED);
- mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD);
- assertEquals(0f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+ reset(mDozeHost);
+ mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD);
+ verify(mDozeHost).setAodDimmingScrim(eq(0f));
}
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
index bfd448a2926d..b92f173e8002 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
@@ -18,6 +18,8 @@ package com.android.systemui.doze;
import static com.android.systemui.doze.DozeMachine.State.DOZE;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING;
import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSING;
import static com.android.systemui.doze.DozeMachine.State.DOZE_REQUEST_PULSE;
import static com.android.systemui.doze.DozeMachine.State.FINISH;
@@ -30,6 +32,9 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.os.Looper;
@@ -46,6 +51,7 @@ import com.android.systemui.utils.os.FakeHandler;
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;
@@ -53,12 +59,14 @@ import org.mockito.MockitoAnnotations;
@SmallTest
public class DozeScreenStateTest extends SysuiTestCase {
- DozeServiceFake mServiceFake;
- DozeScreenState mScreen;
- FakeHandler mHandlerFake;
+ private DozeServiceFake mServiceFake;
+ private FakeHandler mHandlerFake;
@Mock
- DozeParameters mDozeParameters;
- WakeLockFake mWakeLock;
+ private DozeHost mDozeHost;
+ @Mock
+ private DozeParameters mDozeParameters;
+ private WakeLockFake mWakeLock;
+ private DozeScreenState mScreen;
@Before
public void setUp() throws Exception {
@@ -68,7 +76,8 @@ public class DozeScreenStateTest extends SysuiTestCase {
mServiceFake = new DozeServiceFake();
mHandlerFake = new FakeHandler(Looper.getMainLooper());
mWakeLock = new WakeLockFake();
- mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeParameters, mWakeLock);
+ mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeHost, mDozeParameters,
+ mWakeLock);
}
@Test
@@ -183,4 +192,20 @@ public class DozeScreenStateTest extends SysuiTestCase {
assertThat(mWakeLock.isHeld(), is(false));
}
+ @Test
+ public void test_animatesPausing() {
+ ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
+ doAnswer(invocation -> null).when(mDozeHost).prepareForGentleSleep(captor.capture());
+ mHandlerFake.setMode(QUEUEING);
+
+ mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+ mScreen.transitionTo(INITIALIZED, DOZE_AOD_PAUSING);
+ mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED);
+
+ mHandlerFake.dispatchQueuedMessages();
+ verify(mDozeHost).prepareForGentleSleep(eq(captor.getValue()));
+ captor.getValue().run();
+ assertEquals(Display.STATE_OFF, mServiceFake.screenState);
+ }
+
} \ 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 ddd1685bf8bc..f2665ef3c845 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -78,6 +78,8 @@ public class DozeSensorsTest extends SysuiTestCase {
private AlwaysOnDisplayPolicy mAlwaysOnDisplayPolicy;
@Mock
private TriggerSensor mTriggerSensor;
+ @Mock
+ private DozeLog mDozeLog;
private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener;
private TestableLooper mTestableLooper;
private DozeSensors mDozeSensors;
@@ -101,14 +103,14 @@ public class DozeSensorsTest extends SysuiTestCase {
mWakeLockScreenListener.onSensorChanged(mock(SensorManagerPlugin.SensorEvent.class));
mTestableLooper.processAllMessages();
- verify(mCallback).onSensorPulse(eq(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN),
+ verify(mCallback).onSensorPulse(eq(DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN),
anyFloat(), anyFloat(), eq(null));
mDozeSensors.requestTemporaryDisable();
reset(mCallback);
mWakeLockScreenListener.onSensorChanged(mock(SensorManagerPlugin.SensorEvent.class));
mTestableLooper.processAllMessages();
- verify(mCallback, never()).onSensorPulse(eq(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN),
+ verify(mCallback, never()).onSensorPulse(eq(DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN),
anyFloat(), anyFloat(), eq(null));
}
@@ -146,7 +148,7 @@ public class DozeSensorsTest extends SysuiTestCase {
TestableDozeSensors() {
super(getContext(), mAlarmManager, mSensorManager, mDozeParameters,
mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback,
- mAlwaysOnDisplayPolicy);
+ mAlwaysOnDisplayPolicy, mDozeLog);
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 f7cd69643fc6..756227e58ec2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -20,6 +20,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -41,37 +42,39 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.sensors.FakeProximitySensor;
import com.android.systemui.util.sensors.FakeSensorManager;
+import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.wakelock.WakeLock;
import com.android.systemui.util.wakelock.WakeLockFake;
import org.junit.Before;
-import org.junit.BeforeClass;
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)
@RunWithLooper(setAsMainLooper = true)
public class DozeTriggersTest extends SysuiTestCase {
- private DozeTriggers mTriggers;
+
+ @Mock
private DozeMachine mMachine;
- private DozeHostFake mHost;
+ @Mock
+ private DozeHost mHost;
+ @Mock
+ private AlarmManager mAlarmManager;
+ private DozeTriggers mTriggers;
private FakeSensorManager mSensors;
private Sensor mTapSensor;
private DockManager mDockManagerFake;
-
- @BeforeClass
- public static void setupSuite() {
- // We can't use KeyguardUpdateMonitor from tests.
- DozeLog.setRegisterKeyguardCallback(false);
- }
+ private FakeProximitySensor mProximitySensor;
@Before
public void setUp() throws Exception {
- mMachine = mock(DozeMachine.class);
- AlarmManager alarmManager = mock(AlarmManager.class);
- mHost = spy(new DozeHostFake());
+ MockitoAnnotations.initMocks(this);
AmbientDisplayConfiguration config = DozeConfigurationUtil.createMockConfig();
DozeParameters parameters = DozeConfigurationUtil.createMockParameters();
mSensors = spy(new FakeSensorManager(mContext));
@@ -80,30 +83,35 @@ public class DozeTriggersTest extends SysuiTestCase {
mDockManagerFake = mock(DockManager.class);
AsyncSensorManager asyncSensorManager =
new AsyncSensorManager(mSensors, null, new Handler());
+ mProximitySensor = new FakeProximitySensor(getContext().getResources(), asyncSensorManager);
- mTriggers = new DozeTriggers(mContext, mMachine, mHost, alarmManager, config, parameters,
+ mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, config, parameters,
asyncSensorManager, Handler.createAsync(Looper.myLooper()), wakeLock, true,
- mDockManagerFake);
+ mDockManagerFake, mProximitySensor, mock(DozeLog.class));
waitForSensorManager();
}
@Test
public void testOnNotification_stillWorksAfterOneFailedProxCheck() throws Exception {
when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
+ ArgumentCaptor<DozeHost.Callback> captor = ArgumentCaptor.forClass(DozeHost.Callback.class);
+ doAnswer(invocation -> null).when(mHost).addCallback(captor.capture());
mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
clearInvocations(mMachine);
- mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */);
- mSensors.getFakeProximitySensor().sendProximityResult(false); /* Near */
+ mProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(true, 1));
+ captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */);
+ mProximitySensor.alertListeners();
verify(mMachine, never()).requestState(any());
verify(mMachine, never()).requestPulse(anyInt());
- mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */);
+ captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */);
waitForSensorManager();
- mSensors.getFakeProximitySensor().sendProximityResult(true); /* Far */
+ mProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(false, 2));
+ mProximitySensor.alertListeners();
verify(mMachine).requestPulse(anyInt());
}
@@ -139,6 +147,15 @@ public class DozeTriggersTest extends SysuiTestCase {
verify(mDockManagerFake).removeListener(any());
}
+ @Test
+ public void testProximitySensorNotAvailablel() {
+ mProximitySensor.setSensorAvailable(false);
+ mTriggers.onSensor(DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS, 100, 100, null);
+ mTriggers.onSensor(DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, 100, 100,
+ new float[]{1});
+ mTriggers.onSensor(DozeEvent.REASON_SENSOR_TAP, 100, 100, null);
+ }
+
private void waitForSensorManager() {
TestableLooper.get(this).processAllMessages();
}
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 25231bcbc1c4..c5bddc1f096f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
@@ -65,6 +65,8 @@ public class DozeUiTest extends SysuiTestCase {
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
private DozeHost mHost;
+ @Mock
+ private DozeLog mDozeLog;
private WakeLockFake mWakeLock;
private Handler mHandler;
private HandlerThread mHandlerThread;
@@ -80,7 +82,7 @@ public class DozeUiTest extends SysuiTestCase {
mHandler = mHandlerThread.getThreadHandler();
mDozeUi = new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler,
- mDozeParameters, mKeyguardUpdateMonitor);
+ mDozeParameters, mKeyguardUpdateMonitor, mDozeLog);
}
@After
@@ -135,7 +137,7 @@ public class DozeUiTest extends SysuiTestCase {
reset(mHost);
when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
mDozeUi = new DozeUi(mContext, mAlarmManager, mMachine, mWakeLock, mHost, mHandler,
- mDozeParameters, mKeyguardUpdateMonitor);
+ mDozeParameters, mKeyguardUpdateMonitor, mDozeLog);
// 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
new file mode 100644
index 000000000000..9312ed26c217
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard;
+
+import static android.view.WindowManagerPolicyConstants.OFF_BECAUSE_OF_USER;
+
+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 static org.mockito.Mockito.when;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.ViewMediatorCallback;
+import com.android.systemui.SystemUIFactory;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.StatusBarWindowController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+@SmallTest
+public class KeyguardViewMediatorTest extends SysuiTestCase {
+ private KeyguardViewMediator mViewMediator;
+
+ private @Mock DevicePolicyManager mDevicePolicyManager;
+ private @Mock LockPatternUtils mLockPatternUtils;
+ private @Mock KeyguardUpdateMonitor mUpdateMonitor;
+ private @Mock StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ private @Mock StatusBarWindowController mStatusBarWindowController;
+ private @Mock SystemUIFactory mSystemUIFactory;
+
+ private FalsingManagerFake mFalsingManager;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mFalsingManager = new FalsingManagerFake();
+
+ mDependency.injectTestDependency(FalsingManager.class, mFalsingManager);
+ mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mUpdateMonitor);
+ mDependency.injectTestDependency(StatusBarWindowController.class,
+ mStatusBarWindowController);
+
+ when(mLockPatternUtils.getDevicePolicyManager()).thenReturn(mDevicePolicyManager);
+ when(mSystemUIFactory.createStatusBarKeyguardViewManager(
+ any(Context.class),
+ any(ViewMediatorCallback.class),
+ any(LockPatternUtils.class))).thenReturn(mStatusBarKeyguardViewManager);
+
+ TestableLooper.get(this).runWithLooper(() -> {
+ mViewMediator = new KeyguardViewMediator(
+ mContext, mFalsingManager, mLockPatternUtils, mSystemUIFactory);
+ });
+ }
+
+ @Test
+ public void testOnGoingToSleep_UpdatesKeyguardGoingAway() {
+ mViewMediator.start();
+ mViewMediator.onStartedGoingToSleep(OFF_BECAUSE_OF_USER);
+ verify(mUpdateMonitor).setKeyguardGoingAway(false);
+ verify(mStatusBarWindowController, never()).setKeyguardGoingAway(anyBoolean());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/RichEventTest.java b/packages/SystemUI/tests/src/com/android/systemui/log/RichEventTest.java
new file mode 100644
index 000000000000..2f90641775e8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/RichEventTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.log;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class RichEventTest extends SysuiTestCase {
+
+ private static final int TOTAL_EVENT_TYPES = 1;
+
+ @Test
+ public void testCreateRichEvent_invalidType() {
+ try {
+ // indexing for events starts at 0, so TOTAL_EVENT_TYPES is an invalid type
+ new TestableRichEvent(Event.DEBUG, TOTAL_EVENT_TYPES, "msg");
+ } catch (IllegalArgumentException e) {
+ // expected
+ return;
+ }
+
+ Assert.fail("Expected an invalidArgumentException since the event type was invalid.");
+ }
+
+ @Test
+ public void testCreateRichEvent() {
+ final int eventType = 0;
+ RichEvent e = new TestableRichEvent(Event.DEBUG, eventType, "msg");
+ assertEquals(e.getType(), eventType);
+ }
+
+ class TestableRichEvent extends RichEvent {
+ TestableRichEvent(int logLevel, int type, String reason) {
+ super(logLevel, type, reason);
+ }
+
+ @Override
+ public String[] getEventLabels() {
+ return new String[]{"ACTION_NAME"};
+ }
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java b/packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java
new file mode 100644
index 000000000000..378bba1afda3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.log;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.DumpController;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class SysuiLogTest extends SysuiTestCase {
+ private static final String TEST_ID = "TestLogger";
+ private static final int MAX_LOGS = 5;
+
+ @Mock
+ private DumpController mDumpController;
+ private SysuiLog mSysuiLog;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testLogDisabled_noLogsWritten() {
+ mSysuiLog = new SysuiLog(mDumpController, TEST_ID, MAX_LOGS, false);
+ assertEquals(mSysuiLog.mTimeline, null);
+
+ mSysuiLog.log(new Event("msg"));
+ assertEquals(mSysuiLog.mTimeline, null);
+ }
+
+ @Test
+ public void testLogEnabled_logWritten() {
+ mSysuiLog = new SysuiLog(mDumpController, TEST_ID, MAX_LOGS, true);
+ assertEquals(mSysuiLog.mTimeline.size(), 0);
+
+ mSysuiLog.log(new Event("msg"));
+ assertEquals(mSysuiLog.mTimeline.size(), 1);
+ }
+
+ @Test
+ public void testMaxLogs() {
+ mSysuiLog = new SysuiLog(mDumpController, TEST_ID, MAX_LOGS, true);
+ assertEquals(mSysuiLog.mTimeline.size(), 0);
+
+ final String msg = "msg";
+ for (int i = 0; i < MAX_LOGS + 1; i++) {
+ mSysuiLog.log(new Event(msg + i));
+ }
+
+ assertEquals(mSysuiLog.mTimeline.size(), MAX_LOGS);
+
+ // check the first message (msg0) is deleted:
+ assertEquals(mSysuiLog.mTimeline.getFirst().getMessage(), msg + "1");
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index 4d95f3f474b5..47b35fdc6dc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -19,16 +19,19 @@ import static android.provider.Settings.Global.SHOW_USB_TEMPERATURE_ALARM;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.anyObject;
-import static org.mockito.Mockito.mock;
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.content.BroadcastReceiver;
import android.content.Context;
+import android.content.IntentFilter;
import android.os.BatteryManager;
+import android.os.Handler;
import android.os.IThermalEventListener;
import android.os.IThermalService;
import android.os.PowerManager;
@@ -43,6 +46,7 @@ import android.testing.TestableResources;
import com.android.settingslib.fuelgauge.Estimate;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.power.PowerUI.WarningsUI;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -55,6 +59,8 @@ import org.mockito.MockitoAnnotations;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
+import dagger.Lazy;
+
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
@@ -80,6 +86,9 @@ public class PowerUITest extends SysuiTestCase {
@Mock private IThermalService mThermalServiceMock;
private IThermalEventListener mUsbThermalEventListener;
private IThermalEventListener mSkinThermalEventListener;
+ @Mock private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock private Lazy<StatusBar> mStatusBarLazy;
+ @Mock private StatusBar mStatusBar;
@Before
public void setup() {
@@ -87,7 +96,8 @@ public class PowerUITest extends SysuiTestCase {
mMockWarnings = mDependency.injectMockDependency(WarningsUI.class);
mEnhancedEstimates = mDependency.injectMockDependency(EnhancedEstimates.class);
- mContext.putComponent(StatusBar.class, mock(StatusBar.class));
+ when(mStatusBarLazy.get()).thenReturn(mStatusBar);
+
mContext.addMockSystemService(Context.POWER_SERVICE, mPowerManager);
createPowerUi();
@@ -96,6 +106,15 @@ public class PowerUITest extends SysuiTestCase {
}
@Test
+ public void testReceiverIsRegisteredToDispatcherOnStart() {
+ mPowerUI.start();
+ verify(mBroadcastDispatcher).registerReceiver(
+ any(BroadcastReceiver.class),
+ any(IntentFilter.class),
+ any(Handler.class)); //PowerUI does not call with User
+ }
+
+ @Test
public void testSkinWarning_throttlingCritical() throws Exception {
mPowerUI.start();
@@ -667,8 +686,7 @@ public class PowerUITest extends SysuiTestCase {
}
private void createPowerUi() {
- mPowerUI = new PowerUI();
- mPowerUI.mContext = mContext;
+ mPowerUI = new PowerUI(mContext, mBroadcastDispatcher, mStatusBarLazy);
mPowerUI.mComponents = mContext.getComponents();
mPowerUI.mThermalService = mThermalServiceMock;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupTest.java
index 0683cee0f4e4..d17fd7971f56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSCarrierGroupTest.java
@@ -20,6 +20,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.os.Handler;
import android.telephony.SubscriptionManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -28,6 +29,7 @@ import android.view.LayoutInflater;
import androidx.test.filters.SmallTest;
import com.android.keyguard.CarrierTextController;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.utils.leaks.LeakCheckedTest;
@@ -45,13 +47,20 @@ import org.mockito.stubbing.Answer;
public class QSCarrierGroupTest extends LeakCheckedTest {
private QSCarrierGroup mCarrierGroup;
+ private CarrierTextController.CarrierTextCallback mCallback;
+ private TestableLooper mTestableLooper;
@Before
public void setup() throws Exception {
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
- TestableLooper.get(this).runWithLooper(
+ mTestableLooper = TestableLooper.get(this);
+ mDependency.injectTestDependency(
+ Dependency.BG_HANDLER, new Handler(mTestableLooper.getLooper()));
+ mDependency.injectTestDependency(Dependency.MAIN_LOOPER, mTestableLooper.getLooper());
+ mTestableLooper.runWithLooper(
() -> mCarrierGroup = (QSCarrierGroup) LayoutInflater.from(mContext).inflate(
R.layout.qs_carrier_group, null));
+ mCallback = mCarrierGroup.getCallback();
}
@Test // throws no Exception
@@ -72,7 +81,7 @@ public class QSCarrierGroupTest extends LeakCheckedTest {
new CharSequence[]{""},
false,
new int[]{0});
- spiedCarrierGroup.updateCarrierInfo(c1);
+ mCallback.updateCarrierInfo(c1);
// listOfCarriers length 1, subscriptionIds length 1, anySims true
CarrierTextController.CarrierTextCallbackInfo
@@ -81,7 +90,7 @@ public class QSCarrierGroupTest extends LeakCheckedTest {
new CharSequence[]{""},
true,
new int[]{0});
- spiedCarrierGroup.updateCarrierInfo(c2);
+ mCallback.updateCarrierInfo(c2);
// listOfCarriers length 2, subscriptionIds length 2, anySims false
CarrierTextController.CarrierTextCallbackInfo
@@ -90,7 +99,7 @@ public class QSCarrierGroupTest extends LeakCheckedTest {
new CharSequence[]{"", ""},
false,
new int[]{0, 1});
- spiedCarrierGroup.updateCarrierInfo(c3);
+ mCallback.updateCarrierInfo(c3);
// listOfCarriers length 2, subscriptionIds length 2, anySims true
CarrierTextController.CarrierTextCallbackInfo
@@ -99,7 +108,9 @@ public class QSCarrierGroupTest extends LeakCheckedTest {
new CharSequence[]{"", ""},
true,
new int[]{0, 1});
- spiedCarrierGroup.updateCarrierInfo(c4);
+ mCallback.updateCarrierInfo(c4);
+
+ mTestableLooper.processAllMessages();
}
@Test // throws no Exception
@@ -120,7 +131,7 @@ public class QSCarrierGroupTest extends LeakCheckedTest {
new CharSequence[]{"", ""},
false,
new int[]{0});
- spiedCarrierGroup.updateCarrierInfo(c1);
+ mCallback.updateCarrierInfo(c1);
// listOfCarriers length 2, subscriptionIds length 1, anySims true
CarrierTextController.CarrierTextCallbackInfo
@@ -129,7 +140,7 @@ public class QSCarrierGroupTest extends LeakCheckedTest {
new CharSequence[]{"", ""},
true,
new int[]{0});
- spiedCarrierGroup.updateCarrierInfo(c2);
+ mCallback.updateCarrierInfo(c2);
// listOfCarriers length 1, subscriptionIds length 2, anySims false
CarrierTextController.CarrierTextCallbackInfo
@@ -138,7 +149,7 @@ public class QSCarrierGroupTest extends LeakCheckedTest {
new CharSequence[]{""},
false,
new int[]{0, 1});
- spiedCarrierGroup.updateCarrierInfo(c3);
+ mCallback.updateCarrierInfo(c3);
// listOfCarriers length 1, subscriptionIds length 2, anySims true
CarrierTextController.CarrierTextCallbackInfo
@@ -147,7 +158,8 @@ public class QSCarrierGroupTest extends LeakCheckedTest {
new CharSequence[]{""},
true,
new int[]{0, 1});
- spiedCarrierGroup.updateCarrierInfo(c4);
+ mCallback.updateCarrierInfo(c4);
+ mTestableLooper.processAllMessages();
}
@Test // throws no Exception
@@ -161,7 +173,8 @@ public class QSCarrierGroupTest extends LeakCheckedTest {
new CharSequence[]{"", ""},
true,
new int[]{0, 1});
- spiedCarrierGroup.updateCarrierInfo(c4);
+ mCallback.updateCarrierInfo(c4);
+ mTestableLooper.processAllMessages();
}
@Test // throws no Exception
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 07fbbcf4f152..77520140b1a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -60,6 +60,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
+import java.util.Objects;
import javax.inject.Provider;
@@ -190,8 +191,7 @@ public class QSTileHostTest extends SysuiTestCase {
// changed
String newSetting = Settings.Secure.getStringForUser(getContext().getContentResolver(),
TILES_SETTING, ActivityManager.getCurrentUser());
- // newSetting is not null, as it has just been set.
- if (!newSetting.equals(previousSetting)) {
+ if (!Objects.equals(newSetting, previousSetting)) {
onTuningChanged(TILES_SETTING, newSetting);
}
}
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
new file mode 100644
index 000000000000..4becd522ebd6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.qs.external
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.ServiceInfo
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.Icon
+import android.service.quicksettings.IQSTileService
+import android.service.quicksettings.Tile
+import android.test.suitebuilder.annotation.SmallTest
+import android.view.IWindowManager
+import androidx.test.runner.AndroidJUnit4
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.QSTileHost
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.any
+import org.mockito.Mockito.mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CustomTileTest : SysuiTestCase() {
+
+ companion object {
+ const val packageName = "test_package"
+ const val className = "test_class"
+ val componentName = ComponentName(packageName, className)
+ val TILE_SPEC = CustomTile.toSpec(componentName)
+ }
+
+ @Mock private lateinit var mTileHost: QSTileHost
+ @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
+
+ private lateinit var customTile: CustomTile
+
+ @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)
+ }
+
+ @Test
+ fun testBooleanTileHasBooleanState() {
+ `when`(mTileServiceManager.isBooleanTile).thenReturn(true)
+ customTile = CustomTile.create(mTileHost, TILE_SPEC)
+
+ assertTrue(customTile.state is QSTile.BooleanState)
+ assertTrue(customTile.newTileState() is QSTile.BooleanState)
+ }
+
+ @Test
+ fun testRegularTileHasNotBooleanState() {
+ assertFalse(customTile.state is QSTile.BooleanState)
+ assertFalse(customTile.newTileState() is QSTile.BooleanState)
+ }
+
+ @Test
+ fun testValueUpdatedInBooleanTile() {
+ `when`(mTileServiceManager.isBooleanTile).thenReturn(true)
+ customTile = CustomTile.create(mTileHost, TILE_SPEC)
+ customTile.qsTile.icon = mock(Icon::class.java)
+ `when`(customTile.qsTile.icon.loadDrawable(any(Context::class.java)))
+ .thenReturn(mock(Drawable::class.java))
+
+ val state = customTile.newTileState()
+ assertTrue(state is QSTile.BooleanState)
+
+ customTile.qsTile.state = Tile.STATE_INACTIVE
+ customTile.handleUpdateState(state, null)
+ assertFalse((state as QSTile.BooleanState).value)
+
+ customTile.qsTile.state = Tile.STATE_ACTIVE
+ customTile.handleUpdateState(state, null)
+ assertTrue(state.value)
+
+ customTile.qsTile.state = Tile.STATE_UNAVAILABLE
+ customTile.handleUpdateState(state, null)
+ assertFalse(state.value)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
index f35295cf6f99..11b0c69e8a41 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
@@ -101,6 +101,7 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
defaultServiceInfo = new ServiceInfo();
defaultServiceInfo.metaData = new Bundle();
defaultServiceInfo.metaData.putBoolean(TileService.META_DATA_ACTIVE_TILE, true);
+ defaultServiceInfo.metaData.putBoolean(TileService.META_DATA_BOOLEAN_TILE, true);
}
when(mMockPackageManagerAdapter.getServiceInfo(any(), anyInt(), anyInt()))
.thenReturn(defaultServiceInfo);
@@ -237,4 +238,9 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
verifyBind(2);
verify(mMockTileService, times(2)).onStartListening();
}
+
+ @Test
+ public void testBooleanTile() throws Exception {
+ assertTrue(mStateManager.isBooleanTile());
+ }
}
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 818db878cdc0..853b2dbbc485 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
@@ -40,7 +40,7 @@ import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.NetworkController;
import org.junit.Before;
@@ -64,7 +64,7 @@ public class CastTileTest extends SysuiTestCase {
@Mock
private ActivityStarter mActivityStarter;
@Mock
- private KeyguardMonitor mKeyguard;
+ private KeyguardStateController mKeyguard;
@Mock
private NetworkController mNetworkController;
@Mock
@@ -83,7 +83,7 @@ public class CastTileTest extends SysuiTestCase {
mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
mController = mDependency.injectMockDependency(CastController.class);
mActivityStarter = mDependency.injectMockDependency(ActivityStarter.class);
- mKeyguard = mDependency.injectMockDependency(KeyguardMonitor.class);
+ mKeyguard = mDependency.injectMockDependency(KeyguardStateController.class);
mNetworkController = mDependency.injectMockDependency(NetworkController.class);
when(mHost.getContext()).thenReturn(mContext);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index cf5a12fcc2af..c6dd23262b85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -141,9 +141,9 @@ public class AlertingNotificationManagerTest extends SysuiTestCase {
public void testShowNotification_addsEntry() {
mAlertingNotificationManager.showNotification(mEntry);
- assertTrue(mAlertingNotificationManager.isAlerting(mEntry.key));
+ assertTrue(mAlertingNotificationManager.isAlerting(mEntry.getKey()));
assertTrue(mAlertingNotificationManager.hasNotifications());
- assertEquals(mEntry, mAlertingNotificationManager.getEntry(mEntry.key));
+ assertEquals(mEntry, mAlertingNotificationManager.getEntry(mEntry.getKey()));
}
@Test
@@ -155,7 +155,7 @@ public class AlertingNotificationManagerTest extends SysuiTestCase {
TestableLooper.get(this).processMessages(1);
assertFalse("Test timed out", mTimedOut);
- assertFalse(mAlertingNotificationManager.isAlerting(mEntry.key));
+ assertFalse(mAlertingNotificationManager.isAlerting(mEntry.getKey()));
}
@Test
@@ -163,9 +163,10 @@ public class AlertingNotificationManagerTest extends SysuiTestCase {
mAlertingNotificationManager.showNotification(mEntry);
// Try to remove but defer, since the notification has not been shown long enough.
- mAlertingNotificationManager.removeNotification(mEntry.key, false /* releaseImmediately */);
+ mAlertingNotificationManager.removeNotification(
+ mEntry.getKey(), false /* releaseImmediately */);
- assertTrue(mAlertingNotificationManager.isAlerting(mEntry.key));
+ assertTrue(mAlertingNotificationManager.isAlerting(mEntry.getKey()));
}
@Test
@@ -173,9 +174,10 @@ public class AlertingNotificationManagerTest extends SysuiTestCase {
mAlertingNotificationManager.showNotification(mEntry);
// Remove forcibly with releaseImmediately = true.
- mAlertingNotificationManager.removeNotification(mEntry.key, true /* releaseImmediately */);
+ mAlertingNotificationManager.removeNotification(
+ mEntry.getKey(), true /* releaseImmediately */);
- assertFalse(mAlertingNotificationManager.isAlerting(mEntry.key));
+ assertFalse(mAlertingNotificationManager.isAlerting(mEntry.getKey()));
}
@Test
@@ -199,7 +201,7 @@ public class AlertingNotificationManagerTest extends SysuiTestCase {
mAlertingNotificationManager.showNotification(mEntry);
// The entry has just been added so we should not remove immediately.
- assertFalse(mAlertingNotificationManager.canRemoveImmediately(mEntry.key));
+ assertFalse(mAlertingNotificationManager.canRemoveImmediately(mEntry.getKey()));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index b252a0d95f94..8c9ae71dc0f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -15,6 +15,8 @@
package com.android.systemui.statusbar;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Matchers.eq;
@@ -25,10 +27,12 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.content.ComponentName;
import android.graphics.Rect;
import android.os.Bundle;
+import android.view.WindowInsetsController.Appearance;
import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.view.AppearanceRegion;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
@@ -110,21 +114,56 @@ public class CommandQueueTest extends SysuiTestCase {
}
@Test
- public void testSetSystemUiVisibility() {
- Rect r = new Rect();
- mCommandQueue.setSystemUiVisibility(DEFAULT_DISPLAY, 1, 2, 3, 4, null, r, false);
+ public void testOnSystemBarAppearanceChanged() {
+ doTestOnSystemBarAppearanceChanged(DEFAULT_DISPLAY, 1,
+ new AppearanceRegion[]{new AppearanceRegion(2, new Rect())}, false);
+ }
+
+ @Test
+ public void testOnSystemBarAppearanceChangedForSecondaryDisplay() {
+ doTestOnSystemBarAppearanceChanged(SECONDARY_DISPLAY, 1,
+ new AppearanceRegion[]{new AppearanceRegion(2, new Rect())}, false);
+ }
+
+ private void doTestOnSystemBarAppearanceChanged(int displayId, @Appearance int appearance,
+ AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) {
+ mCommandQueue.onSystemBarAppearanceChanged(displayId, appearance, appearanceRegions,
+ navbarColorManagedByIme);
+ waitForIdleSync();
+ verify(mCallbacks).onSystemBarAppearanceChanged(eq(displayId), eq(appearance),
+ eq(appearanceRegions), eq(navbarColorManagedByIme));
+ }
+
+ @Test
+ public void testShowTransient() {
+ int[] types = new int[]{ TYPE_TOP_BAR, TYPE_NAVIGATION_BAR };
+ mCommandQueue.showTransient(DEFAULT_DISPLAY, types);
+ waitForIdleSync();
+ verify(mCallbacks).showTransient(eq(DEFAULT_DISPLAY), eq(types));
+ }
+
+ @Test
+ public void testShowTransientForSecondaryDisplay() {
+ int[] types = new int[]{ TYPE_TOP_BAR, TYPE_NAVIGATION_BAR };
+ mCommandQueue.showTransient(SECONDARY_DISPLAY, types);
+ waitForIdleSync();
+ verify(mCallbacks).showTransient(eq(SECONDARY_DISPLAY), eq(types));
+ }
+
+ @Test
+ public void testAbortTransient() {
+ int[] types = new int[]{ TYPE_TOP_BAR, TYPE_NAVIGATION_BAR };
+ mCommandQueue.abortTransient(DEFAULT_DISPLAY, types);
waitForIdleSync();
- verify(mCallbacks).setSystemUiVisibility(eq(DEFAULT_DISPLAY), eq(1), eq(2), eq(3), eq(4),
- eq(null), eq(r), eq(false));
+ verify(mCallbacks).abortTransient(eq(DEFAULT_DISPLAY), eq(types));
}
@Test
- public void testSetSystemUiVisibilityForSecondaryDisplay() {
- Rect r = new Rect();
- mCommandQueue.setSystemUiVisibility(SECONDARY_DISPLAY, 1, 2, 3, 4, null, r, false);
+ public void testAbortTransientForSecondaryDisplay() {
+ int[] types = new int[]{ TYPE_TOP_BAR, TYPE_NAVIGATION_BAR };
+ mCommandQueue.abortTransient(SECONDARY_DISPLAY, types);
waitForIdleSync();
- verify(mCallbacks).setSystemUiVisibility(eq(SECONDARY_DISPLAY), eq(1), eq(2), eq(3), eq(4),
- eq(null), eq(r), eq(false));
+ verify(mCallbacks).abortTransient(eq(SECONDARY_DISPLAY), eq(types));
}
@Test
@@ -367,21 +406,21 @@ public class CommandQueueTest extends SysuiTestCase {
}
@Test
- public void testShowBiometricDialog() {
+ public void testShowAuthenticationDialog() {
Bundle bundle = new Bundle();
String packageName = "test";
- mCommandQueue.showBiometricDialog(bundle, null /* receiver */, 1, true, 3, packageName);
+ mCommandQueue.showAuthenticationDialog(bundle, null /* receiver */, 1, true, 3,
+ packageName);
waitForIdleSync();
- verify(mCallbacks).showBiometricDialog(eq(bundle), eq(null), eq(1), eq(true), eq(3),
+ verify(mCallbacks).showAuthenticationDialog(eq(bundle), eq(null), eq(1), eq(true), eq(3),
eq(packageName));
}
@Test
public void testOnBiometricAuthenticated() {
- String failureReason = "test_failure_reason";
- mCommandQueue.onBiometricAuthenticated(true /* authenticated */, failureReason);
+ mCommandQueue.onBiometricAuthenticated();
waitForIdleSync();
- verify(mCallbacks).onBiometricAuthenticated(eq(true), eq(failureReason));
+ verify(mCallbacks).onBiometricAuthenticated();
}
@Test
@@ -394,16 +433,18 @@ public class CommandQueueTest extends SysuiTestCase {
@Test
public void testOnBiometricError() {
- String errorMessage = "test_error_message";
- mCommandQueue.onBiometricError(errorMessage);
+ final int modality = 1;
+ final int error = 2;
+ final int vendorCode = 3;
+ mCommandQueue.onBiometricError(modality, error, vendorCode);
waitForIdleSync();
- verify(mCallbacks).onBiometricError(eq(errorMessage));
+ verify(mCallbacks).onBiometricError(eq(modality), eq(error), eq(vendorCode));
}
@Test
- public void testHideBiometricDialog() {
- mCommandQueue.hideBiometricDialog();
+ public void testHideAuthenticationDialog() {
+ mCommandQueue.hideAuthenticationDialog();
waitForIdleSync();
- verify(mCallbacks).hideBiometricDialog();
+ verify(mCallbacks).hideAuthenticationDialog();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 0817ee908184..cf6fd4a3380f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -51,8 +51,8 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
import com.android.systemui.statusbar.phone.LockIcon;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.UnlockMethodCache;
import com.android.systemui.statusbar.policy.AccessibilityController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.wakelock.WakeLockFake;
import org.junit.Before;
@@ -79,7 +79,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
@Mock
private AccessibilityController mAccessibilityController;
@Mock
- private UnlockMethodCache mUnlockMethodCache;
+ private KeyguardStateController mKeyguardStateController;
@Mock
private StatusBarStateController mStatusBarStateController;
@Mock
@@ -111,7 +111,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
}
mController = new KeyguardIndicationController(mContext, mIndicationArea, mLockIcon,
mLockPatternUtils, mWakeLock, mShadeController, mAccessibilityController,
- mUnlockMethodCache, mStatusBarStateController, mKeyguardUpdateMonitor);
+ mKeyguardStateController, mStatusBarStateController, mKeyguardUpdateMonitor);
}
@Test
@@ -187,7 +187,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
}
@Test
- public void unlockMethodCache_listenerUpdatesIndication() {
+ public void updateMonitor_listenerUpdatesIndication() {
createController();
String restingIndication = "Resting indication";
@@ -203,14 +203,14 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
reset(mKeyguardUpdateMonitor);
when(mKeyguardUpdateMonitor.isUserUnlocked(anyInt())).thenReturn(true);
when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false);
- mController.onUnlockMethodStateChanged();
+ mController.onUnlockedChanged();
assertThat(mTextView.getText()).isEqualTo(restingIndication);
}
@Test
- public void unlockMethodCache_listener() {
+ public void updateMonitor_listener() {
createController();
- verify(mUnlockMethodCache).addListener(eq(mController));
+ verify(mKeyguardStateController).addCallback(eq(mController));
verify(mStatusBarStateController).addCallback(eq(mController));
verify(mKeyguardUpdateMonitor, times(2)).registerCallback(any());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NavigationBarControllerTest.java
index 7d2ccdc8f0a9..cfa4065aed62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NavigationBarControllerTest.java
@@ -23,22 +23,19 @@ 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.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
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;
-import android.content.Context;
-import android.hardware.display.DisplayManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.util.SparseArray;
-import android.view.Display;
-import android.view.WindowManager;
import androidx.test.filters.SmallTest;
@@ -58,7 +55,6 @@ import org.junit.runner.RunWith;
public class NavigationBarControllerTest extends SysuiTestCase {
private NavigationBarController mNavigationBarController;
- private Display mDisplay;
private NavigationBarFragment mDefaultNavBar;
private NavigationBarFragment mSecondaryNavBar;
@@ -88,37 +84,28 @@ public class NavigationBarControllerTest extends SysuiTestCase {
@After
public void tearDown() {
mNavigationBarController = null;
- mDisplay = null;
mDefaultNavBar = null;
mSecondaryNavBar = null;
}
@Test
public void testCreateNavigationBarsIncludeDefaultTrue() {
- initializeDisplayManager();
doNothing().when(mNavigationBarController).createNavigationBar(any(), any());
mNavigationBarController.createNavigationBars(true, null);
- verify(mNavigationBarController).createNavigationBar(any(Display.class), any());
+ verify(mNavigationBarController).createNavigationBar(
+ argThat(display -> display.getDisplayId() == DEFAULT_DISPLAY), any());
}
@Test
public void testCreateNavigationBarsIncludeDefaultFalse() {
- initializeDisplayManager();
doNothing().when(mNavigationBarController).createNavigationBar(any(), any());
mNavigationBarController.createNavigationBars(false, null);
- verify(mNavigationBarController, never()).createNavigationBar(any(), any());
- }
-
- private void initializeDisplayManager() {
- DisplayManager displayManager = mock(DisplayManager.class);
- mDisplay = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
- Display[] displays = {mDisplay};
- when(displayManager.getDisplays()).thenReturn(displays);
- mContext.addMockSystemService(Context.DISPLAY_SERVICE, displayManager);
+ verify(mNavigationBarController, never()).createNavigationBar(
+ argThat(display -> display.getDisplayId() == DEFAULT_DISPLAY), any());
}
// Tests if NPE occurs when call checkNavBarModes() with invalid display.
@@ -240,4 +227,10 @@ public class NavigationBarControllerTest extends SysuiTestCase {
verify(mSecondaryNavBar).disableAnimationsDuringHide(eq(500L));
}
+
+ @Test
+ public void testGetAssistHandlerViewController_noCrash() {
+ reset(mNavigationBarController.mNavigationBars);
+ mNavigationBarController.getAssistHandlerViewController();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
index a0a410dfa361..e67aa69d48ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -52,7 +52,7 @@ import org.mockito.MockitoAnnotations;
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class NonPhoneDependencyTest extends SysuiTestCase {
@Mock private NotificationPresenter mPresenter;
@Mock private NotificationListContainer mListContainer;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryHelper.java
index 33b0d2c32ba1..2420e573421a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationEntryHelper.java
@@ -36,7 +36,7 @@ public class NotificationEntryHelper {
private final NotificationEntry mTarget;
private ModifiedRankingBuilder(NotificationEntry target) {
- super(target.ranking());
+ super(target.getRanking());
mTarget = target;
}
@@ -52,14 +52,14 @@ public class NotificationEntryHelper {
private final NotificationEntry mTarget;
private ModifiedSbnBuilder(NotificationEntry target) {
- super(target.sbn());
+ super(target.getSbn());
mTarget = target;
}
@Override
public StatusBarNotification build() {
final StatusBarNotification sbn = super.build();
- mTarget.setNotification(sbn);
+ mTarget.setSbn(sbn);
return sbn;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
index 350ab5afdf95..9b860c9e0ba7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
@@ -52,6 +52,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import org.junit.Before;
@@ -84,6 +85,8 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase {
HeadsUpManager mHeadsUpManager;
@Mock
NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor;
+ @Mock
+ BatteryController mBatteryController;
private NotificationInterruptionStateProvider mNotifInterruptionStateProvider;
@@ -97,7 +100,8 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase {
mDreamManager,
mAmbientDisplayConfiguration,
mNotificationFilter,
- mStatusBarStateController);
+ mStatusBarStateController,
+ mBatteryController);
mNotifInterruptionStateProvider.setUpWithPresenter(
mPresenter,
@@ -519,7 +523,7 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase {
modifyRanking(entry)
.setCanBubble(true)
.build();
- entry.sbn().getNotification().flags |= FLAG_BUBBLE;
+ entry.getSbn().getNotification().flags |= FLAG_BUBBLE;
assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(entry)).isFalse();
}
@@ -590,17 +594,17 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase {
/**
* Testable class overriding constructor.
*/
- public class TestableNotificationInterruptionStateProvider extends
+ public static class TestableNotificationInterruptionStateProvider extends
NotificationInterruptionStateProvider {
TestableNotificationInterruptionStateProvider(Context context,
PowerManager powerManager, IDreamManager dreamManager,
AmbientDisplayConfiguration ambientDisplayConfiguration,
NotificationFilter notificationFilter,
- StatusBarStateController statusBarStateController) {
+ StatusBarStateController statusBarStateController,
+ BatteryController batteryController) {
super(context, powerManager, dreamManager, ambientDisplayConfiguration,
- notificationFilter,
- statusBarStateController);
+ notificationFilter, batteryController, statusBarStateController);
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index a02764320b6c..85a0fbd74550 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -25,6 +25,7 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -51,6 +52,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.google.android.collect.Lists;
@@ -82,6 +84,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
mDependency.injectTestDependency(DeviceProvisionedController.class,
mDeviceProvisionedController);
+ mDependency.injectMockDependency(KeyguardStateController.class);
mContext.addMockSystemService(UserManager.class, mUserManager);
mCurrentUserId = ActivityManager.getCurrentUser();
@@ -99,7 +102,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
@Test
public void testLockScreenShowNotificationsChangeUpdatesNotifications() {
mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
- verify(mEntryManager, times(1)).updateNotifications();
+ verify(mEntryManager, times(1)).updateNotifications(anyString());
}
@Test
@@ -138,7 +141,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
public void testSettingsObserverUpdatesNotifications() {
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
mLockscreenUserManager.getSettingsObserverForTest().onChange(false);
- verify(mEntryManager, times(1)).updateNotifications();
+ verify(mEntryManager, times(1)).updateNotifications(anyString());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index 852ddb2ae710..2514c9382e44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -97,7 +97,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
@Test
public void testPerformOnRemoveNotification() {
when(mController.isRemoteInputActive(mEntry)).thenReturn(true);
- mRemoteInputManager.onPerformRemoveNotification(mEntry, mEntry.key());
+ mRemoteInputManager.onPerformRemoveNotification(mEntry, mEntry.getKey());
verify(mController).removeRemoteInput(mEntry, null);
}
@@ -112,7 +112,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
@Test
public void testShouldExtendLifetime_isSpinning() {
NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY = true;
- when(mController.isSpinning(mEntry.key)).thenReturn(true);
+ when(mController.isSpinning(mEntry.getKey())).thenReturn(true);
assertTrue(mRemoteInputHistoryExtender.shouldExtendLifetime(mEntry));
}
@@ -128,7 +128,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
@Test
public void testShouldExtendLifetime_smartReplySending() {
NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY = true;
- when(mSmartReplyController.isSendingSmartReply(mEntry.key)).thenReturn(true);
+ when(mSmartReplyController.isSendingSmartReply(mEntry.getKey())).thenReturn(true);
assertTrue(mSmartReplyHistoryExtender.shouldExtendLifetime(mEntry));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index de77af8f4d14..90bd0e9936be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -42,6 +42,8 @@ import android.widget.RemoteViews;
import androidx.test.InstrumentationRegistry;
+import com.android.systemui.TestableDependency;
+import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.bubbles.BubblesTestActivity;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -51,6 +53,7 @@ import com.android.systemui.statusbar.notification.row.NotificationContentInflat
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.tests.R;
/**
@@ -75,8 +78,11 @@ public class NotificationTestHelper {
private ExpandableNotificationRow mRow;
private HeadsUpManagerPhone mHeadsUpManager;
- public NotificationTestHelper(Context context) {
+ public NotificationTestHelper(Context context, TestableDependency dependency) {
mContext = context;
+ dependency.injectMockDependency(NotificationMediaManager.class);
+ dependency.injectMockDependency(BubbleController.class);
+ dependency.injectMockDependency(StatusBarWindowController.class);
mInstrumentation = InstrumentationRegistry.getInstrumentation();
StatusBarStateController stateController = mock(StatusBarStateController.class);
mGroupManager = new NotificationGroupManager(stateController);
@@ -320,7 +326,7 @@ public class NotificationTestHelper {
.build();
entry.setRow(row);
- entry.createIcons(mContext, entry.sbn());
+ entry.createIcons(mContext, entry.getSbn());
row.setEntry(entry);
row.getNotificationInflater().addInflationFlags(extraInflationFlags);
NotificationContentInflaterTest.runThenWaitForInflation(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 9e7250412499..18649bfe68d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -103,7 +103,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager);
mDependency.injectTestDependency(ShadeController.class, mShadeController);
- mHelper = new NotificationTestHelper(mContext);
+ mHelper = new NotificationTestHelper(mContext, mDependency);
when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
@@ -143,11 +143,11 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
Lists.newArrayList(entry0, entry1, entry2));
// Set up group manager to report that they should be bundled now.
- when(mGroupManager.isChildInGroupWithSummary(entry0.notification)).thenReturn(false);
- when(mGroupManager.isChildInGroupWithSummary(entry1.notification)).thenReturn(true);
- when(mGroupManager.isChildInGroupWithSummary(entry2.notification)).thenReturn(true);
- when(mGroupManager.getGroupSummary(entry1.notification)).thenReturn(entry0);
- when(mGroupManager.getGroupSummary(entry2.notification)).thenReturn(entry0);
+ when(mGroupManager.isChildInGroupWithSummary(entry0.getSbn())).thenReturn(false);
+ when(mGroupManager.isChildInGroupWithSummary(entry1.getSbn())).thenReturn(true);
+ when(mGroupManager.isChildInGroupWithSummary(entry2.getSbn())).thenReturn(true);
+ when(mGroupManager.getGroupSummary(entry1.getSbn())).thenReturn(entry0);
+ when(mGroupManager.getGroupSummary(entry2.getSbn())).thenReturn(entry0);
// Run updateNotifications - the view hierarchy should be reorganized.
mViewHierarchyManager.updateNotificationViews();
@@ -172,9 +172,9 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
Lists.newArrayList(entry0, entry1, entry2));
// Set up group manager to report that they should not be bundled now.
- when(mGroupManager.isChildInGroupWithSummary(entry0.notification)).thenReturn(false);
- when(mGroupManager.isChildInGroupWithSummary(entry1.notification)).thenReturn(false);
- when(mGroupManager.isChildInGroupWithSummary(entry2.notification)).thenReturn(false);
+ when(mGroupManager.isChildInGroupWithSummary(entry0.getSbn())).thenReturn(false);
+ when(mGroupManager.isChildInGroupWithSummary(entry1.getSbn())).thenReturn(false);
+ when(mGroupManager.isChildInGroupWithSummary(entry2.getSbn())).thenReturn(false);
// Run updateNotifications - the view hierarchy should be reorganized.
mViewHierarchyManager.updateNotificationViews();
@@ -201,9 +201,9 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
Lists.newArrayList(entry0, entry1));
// Set up group manager to report a suppressed summary now.
- when(mGroupManager.isChildInGroupWithSummary(entry0.notification)).thenReturn(false);
- when(mGroupManager.isChildInGroupWithSummary(entry1.notification)).thenReturn(false);
- when(mGroupManager.isSummaryOfSuppressedGroup(entry0.notification)).thenReturn(true);
+ when(mGroupManager.isChildInGroupWithSummary(entry0.getSbn())).thenReturn(false);
+ when(mGroupManager.isChildInGroupWithSummary(entry1.getSbn())).thenReturn(false);
+ when(mGroupManager.isSummaryOfSuppressedGroup(entry0.getSbn())).thenReturn(true);
// Run updateNotifications - the view hierarchy should be reorganized.
mViewHierarchyManager.updateNotificationViews();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
index db2c8780e783..4103edee6255 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
@@ -47,7 +47,7 @@ public class AboveShelfObserverTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
- mNotificationTestHelper = new NotificationTestHelper(getContext());
+ mNotificationTestHelper = new NotificationTestHelper(getContext(), mDependency);
mHostLayout = new FrameLayout(getContext());
mObserver = new AboveShelfObserver(mHostLayout);
ExpandableNotificationRow row = mNotificationTestHelper.createRow();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
index 55583931af43..145a25caf95d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
@@ -17,11 +17,8 @@
package com.android.systemui.statusbar.notification;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -30,23 +27,20 @@ import android.app.ActivityManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
import android.view.RemoteAnimationAdapter;
import android.view.View;
-import android.widget.FrameLayout;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
+import com.android.systemui.statusbar.phone.StatusBarWindowViewController;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.stubbing.Answer;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -55,6 +49,8 @@ public class ActivityLaunchAnimatorTest extends SysuiTestCase {
private ActivityLaunchAnimator mLaunchAnimator;
private ActivityLaunchAnimator.Callback mCallback = mock(ActivityLaunchAnimator.Callback.class);
+ private StatusBarWindowViewController mStatusBarWindowViewController = mock(
+ StatusBarWindowViewController.class);
private StatusBarWindowView mStatusBarWindowView = mock(StatusBarWindowView.class);
private NotificationListContainer mNotificationContainer
= mock(NotificationListContainer.class);
@@ -62,10 +58,11 @@ public class ActivityLaunchAnimatorTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
+ when(mStatusBarWindowViewController.getView()).thenReturn(mStatusBarWindowView);
when(mStatusBarWindowView.getResources()).thenReturn(mContext.getResources());
when(mCallback.areLaunchAnimationsEnabled()).thenReturn(true);
mLaunchAnimator = new ActivityLaunchAnimator(
- mStatusBarWindowView,
+ mStatusBarWindowViewController,
mCallback,
mock(NotificationPanelView.class),
mNotificationContainer);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
index d804b6f5c5ee..99dc895eafff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
@@ -26,22 +26,17 @@ import static org.mockito.Mockito.when;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.UnlockMethodCache;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
-import dagger.Lazy;
-
@SmallTest
@org.junit.runner.RunWith(AndroidTestingRunner.class)
@@ -49,19 +44,17 @@ import dagger.Lazy;
public class DynamicPrivacyControllerTest extends SysuiTestCase {
private DynamicPrivacyController mDynamicPrivacyController;
- private UnlockMethodCache mCache = mock(UnlockMethodCache.class);
private NotificationLockscreenUserManager mLockScreenUserManager
= mock(NotificationLockscreenUserManager.class);
private DynamicPrivacyController.Listener mListener
= mock(DynamicPrivacyController.Listener.class);
- private KeyguardMonitor mKeyguardMonitor = mock(KeyguardMonitor.class);
+ private KeyguardStateController mKeyguardStateController = mock(KeyguardStateController.class);
@Before
public void setUp() throws Exception {
- when(mCache.canSkipBouncer()).thenReturn(false);
- when(mKeyguardMonitor.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
mDynamicPrivacyController = new DynamicPrivacyController(
- mLockScreenUserManager, mKeyguardMonitor, mCache,
+ mLockScreenUserManager, mKeyguardStateController,
mock(StatusBarStateController.class));
mDynamicPrivacyController.setStatusBarKeyguardViewManager(
mock(StatusBarKeyguardViewManager.class));
@@ -71,7 +64,7 @@ public class DynamicPrivacyControllerTest extends SysuiTestCase {
@Test
public void testDynamicFalseWhenCannotSkipBouncer() {
enableDynamicPrivacy();
- when(mCache.canSkipBouncer()).thenReturn(false);
+ when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
Assert.assertFalse("can't skip bouncer but is dynamically unlocked",
mDynamicPrivacyController.isDynamicallyUnlocked());
}
@@ -79,16 +72,16 @@ public class DynamicPrivacyControllerTest extends SysuiTestCase {
@Test
public void testDynamicTrueWhenCanSkipBouncer() {
enableDynamicPrivacy();
- when(mCache.canSkipBouncer()).thenReturn(true);
+ when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
Assert.assertTrue("Isn't dynamically unlocked even though we can skip bouncer",
mDynamicPrivacyController.isDynamicallyUnlocked());
}
@Test
public void testNotifiedWhenEnabled() {
- when(mCache.canSkipBouncer()).thenReturn(true);
+ when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
enableDynamicPrivacy();
- mDynamicPrivacyController.onUnlockMethodStateChanged();
+ mDynamicPrivacyController.onUnlockedChanged();
verify(mListener).onDynamicPrivacyChanged();
}
@@ -99,10 +92,10 @@ public class DynamicPrivacyControllerTest extends SysuiTestCase {
@Test
public void testNotNotifiedWithoutNotifications() {
- when(mCache.canSkipBouncer()).thenReturn(true);
+ when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
when(mLockScreenUserManager.shouldHideNotifications(anyInt())).thenReturn(
true);
- mDynamicPrivacyController.onUnlockMethodStateChanged();
+ mDynamicPrivacyController.onUnlockedChanged();
verifyNoMoreInteractions(mListener);
}
} \ No newline at end of file
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 30e02e6b46d2..bde7ef97b2ee 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
@@ -28,6 +28,7 @@ import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
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.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
@@ -41,7 +42,6 @@ import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Icon;
import android.os.Handler;
@@ -69,6 +69,7 @@ import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
@@ -80,6 +81,8 @@ import com.android.systemui.statusbar.notification.collection.NotificationData.K
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -145,8 +148,13 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
private class TestableNotificationEntryManager extends NotificationEntryManager {
private final CountDownLatch mCountDownLatch;
- TestableNotificationEntryManager(Context context) {
- super(context);
+ TestableNotificationEntryManager() {
+ super(
+ new NotificationData(
+ mock(NotificationSectionsFeatureManager.class),
+ mock(NotifLog.class),
+ mock(PeopleNotificationIdentifier.class)),
+ mock(NotifLog.class));
mCountDownLatch = new CountDownLatch(1);
}
@@ -225,6 +233,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
mDependency.injectTestDependency(SmartReplyController.class, mSmartReplyController);
mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment);
+ mDependency.injectMockDependency(NotificationMediaManager.class);
mCountDownLatch = new CountDownLatch(1);
@@ -246,11 +255,11 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
.setNotification(n.build())
.setUser(new UserHandle(ActivityManager.getCurrentUser()))
.build();
- mSbn = mEntry.sbn();
+ mSbn = mEntry.getSbn();
mEntry.expandedIcon = mock(StatusBarIconView.class);
- mEntryManager = new TestableNotificationEntryManager(mContext);
+ mEntryManager = new TestableNotificationEntryManager();
Dependency.get(InitController.class).executePostInitTasks();
mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager);
mEntryManager.addNotificationEntryListener(mEntryListener);
@@ -258,13 +267,16 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
NotificationRowBinderImpl notificationRowBinder =
new NotificationRowBinderImpl(mContext, true, /* allowLongPress */
- mock(KeyguardBypassController.class), mock(StatusBarStateController.class));
+ mock(KeyguardBypassController.class),
+ mock(StatusBarStateController.class),
+ mock(NotifLog.class));
notificationRowBinder.setUpWithPresenter(
mPresenter, mListContainer, mHeadsUpManager, mEntryManager, mBindCallback);
notificationRowBinder.setNotificationClicker(mock(NotificationClicker.class));
mEntryManager.setRowBinder(notificationRowBinder);
- setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
+ setUserSentiment(
+ mEntry.getKey(), NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
}
@Test
@@ -311,7 +323,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntryManager.getNotificationData().add(mEntry);
- setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
+ setUserSentiment(
+ mEntry.getKey(), NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
mEntryManager.updateNotification(mSbn, mRankingMap);
TestableLooper.get(this).processMessages(1);
@@ -335,7 +348,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
TestableLooper.get(this).processAllMessages();
NotificationData notifData = mock(NotificationData.class);
- when(notifData.get(mEntry.key)).thenReturn(mEntry);
+ when(notifData.get(mEntry.getKey())).thenReturn(mEntry);
mEntryManager.setNotificationData(notifData);
@@ -349,7 +362,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
// Ensure that update callbacks happen in correct order
InOrder order = inOrder(mEntryListener, notifData, mPresenter, mEntryListener);
order.verify(mEntryListener).onPreEntryUpdated(mEntry);
- order.verify(notifData).filterAndSort();
+ order.verify(notifData).filterAndSort(anyString());
order.verify(mPresenter).updateNotificationViews();
order.verify(mEntryListener).onPostEntryUpdated(mEntry);
@@ -407,7 +420,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntry.setRow(mRow);
mEntry.setInflationTask(mAsyncInflationTask);
mEntryManager.getNotificationData().add(mEntry);
- setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
+ setSmartActions(mEntry.getKey(), new ArrayList<>(Arrays.asList(createAction())));
mEntryManager.updateNotificationRanking(mRankingMap);
verify(mRow).setEntry(eq(mEntry));
@@ -423,7 +436,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntry.setRow(mRow);
mEntryManager.getNotificationData().add(mEntry);
- setSmartActions(mEntry.key, null);
+ setSmartActions(mEntry.getKey(), null);
mEntryManager.updateNotificationRanking(mRankingMap);
verify(mRow, never()).setEntry(eq(mEntry));
@@ -437,7 +450,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntry.setRow(null);
mEntryManager.getNotificationData().add(mEntry);
- setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
+ setSmartActions(mEntry.getKey(), new ArrayList<>(Arrays.asList(createAction())));
mEntryManager.updateNotificationRanking(mRankingMap);
verify(mRow, never()).setEntry(eq(mEntry));
@@ -451,8 +464,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
mEntry.setRow(null);
- mEntryManager.mPendingNotifications.put(mEntry.key, mEntry);
- setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
+ mEntryManager.mPendingNotifications.put(mEntry.getKey(), mEntry);
+ setSmartActions(mEntry.getKey(), new ArrayList<>(Arrays.asList(createAction())));
mEntryManager.updateNotificationRanking(mRankingMap);
verify(mRow, never()).setEntry(eq(mEntry));
@@ -472,7 +485,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntryManager.addNotificationLifetimeExtender(extender);
// WHEN the notification is removed
- mEntryManager.removeNotification(mEntry.key, mRankingMap, UNDEFINED_DISMISS_REASON);
+ mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
// THEN the extender is asked to manage the lifetime
verify(extender).setShouldManageLifetime(mEntry, true);
@@ -488,12 +501,12 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntryManager.getNotificationData().add(mEntry);
final FakeNotificationLifetimeExtender extender = new FakeNotificationLifetimeExtender();
mEntryManager.addNotificationLifetimeExtender(extender);
- mEntryManager.removeNotification(mEntry.key, mRankingMap, UNDEFINED_DISMISS_REASON);
- assertTrue(extender.isManaging(mEntry.key));
+ mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
+ assertTrue(extender.isManaging(mEntry.getKey()));
// WHEN the extender finishes its extension
extender.setExtendLifetimes(false);
- extender.getCallback().onSafeToRemove(mEntry.key);
+ extender.getCallback().onSafeToRemove(mEntry.getKey());
// THEN the notification is removed
assertNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
@@ -508,10 +521,10 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
NotificationLifetimeExtender extender = mock(NotificationLifetimeExtender.class);
when(extender.shouldExtendLifetime(mEntry)).thenReturn(true);
mEntryManager.addNotificationLifetimeExtender(extender);
- mEntryManager.removeNotification(mEntry.key, mRankingMap, UNDEFINED_DISMISS_REASON);
+ mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
// WHEN the notification is updated
- mEntryManager.updateNotification(mEntry.notification, mRankingMap);
+ mEntryManager.updateNotification(mEntry.getSbn(), mRankingMap);
// THEN the lifetime extension is canceled
verify(extender).setShouldManageLifetime(mEntry, false);
@@ -533,13 +546,13 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntryManager.addNotificationLifetimeExtender(extender2);
// GIVEN a notification was lifetime-extended and extender2 is managing it
- mEntryManager.removeNotification(mEntry.key, mRankingMap, UNDEFINED_DISMISS_REASON);
+ mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
verify(extender1, never()).setShouldManageLifetime(mEntry, true);
verify(extender2).setShouldManageLifetime(mEntry, true);
// WHEN the extender1 changes its mind and wants to extend the lifetime of the notif
when(extender1.shouldExtendLifetime(mEntry)).thenReturn(true);
- mEntryManager.removeNotification(mEntry.key, mRankingMap, UNDEFINED_DISMISS_REASON);
+ mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
// THEN extender2 stops managing the notif and extender1 starts managing it
verify(extender1).setShouldManageLifetime(mEntry, true);
@@ -563,14 +576,14 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntryManager.getNotificationData().add(mEntry);
// GIVEN interceptor that intercepts that entry
- when(mRemoveInterceptor.onNotificationRemoveRequested(eq(mEntry.key), anyInt()))
+ when(mRemoveInterceptor.onNotificationRemoveRequested(eq(mEntry.getKey()), anyInt()))
.thenReturn(true);
// WHEN the notification is removed
- mEntryManager.removeNotification(mEntry.key, mRankingMap, UNDEFINED_DISMISS_REASON);
+ mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
// THEN the interceptor intercepts & the entry is not removed & no listeners are called
- assertNotNull(mEntryManager.getNotificationData().get(mEntry.key));
+ assertNotNull(mEntryManager.getNotificationData().get(mEntry.getKey()));
verify(mEntryListener, never()).onEntryRemoved(eq(mEntry),
any(NotificationVisibility.class), anyBoolean());
}
@@ -582,14 +595,14 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntryManager.getNotificationData().add(mEntry);
// GIVEN interceptor that doesn't intercept
- when(mRemoveInterceptor.onNotificationRemoveRequested(eq(mEntry.key), anyInt()))
+ when(mRemoveInterceptor.onNotificationRemoveRequested(eq(mEntry.getKey()), anyInt()))
.thenReturn(false);
// WHEN the notification is removed
- mEntryManager.removeNotification(mEntry.key, mRankingMap, UNDEFINED_DISMISS_REASON);
+ mEntryManager.removeNotification(mEntry.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
// THEN the interceptor intercepts & the entry is not removed & no listeners are called
- assertNull(mEntryManager.getNotificationData().get(mEntry.key));
+ assertNull(mEntryManager.getNotificationData().get(mEntry.getKey()));
verify(mEntryListener, atLeastOnce()).onEntryRemoved(eq(mEntry),
any(NotificationVisibility.class), anyBoolean());
}
@@ -620,17 +633,17 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
public void setShouldManageLifetime(
@NonNull NotificationEntry entry,
boolean shouldManage) {
- final boolean hasEntry = mManagedNotifs.contains(entry.key);
+ final boolean hasEntry = mManagedNotifs.contains(entry.getKey());
if (shouldManage) {
if (hasEntry) {
- throw new RuntimeException("Already managing this entry: " + entry.key);
+ throw new RuntimeException("Already managing this entry: " + entry.getKey());
}
- mManagedNotifs.add(entry.key);
+ mManagedNotifs.add(entry.getKey());
} else {
if (!hasEntry) {
- throw new RuntimeException("Not managing this entry: " + entry.key);
+ throw new RuntimeException("Not managing this entry: " + entry.getKey());
}
- mManagedNotifs.remove(entry.key);
+ mManagedNotifs.remove(entry.getKey());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
index edd0a10672fb..d85f2752a247 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -42,6 +42,7 @@ import com.android.systemui.ForegroundServiceController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationEntryBuilder;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -94,10 +95,11 @@ public class NotificationFilterTest extends SysuiTestCase {
mDependency.injectTestDependency(NotificationGroupManager.class,
new NotificationGroupManager(mock(StatusBarStateController.class)));
mDependency.injectMockDependency(ShadeController.class);
+ mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
mDependency.injectTestDependency(NotificationData.KeyguardEnvironment.class, mEnvironment);
when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
- mRow = new NotificationTestHelper(getContext()).createRow();
+ mRow = new NotificationTestHelper(getContext(), mDependency).createRow();
mNotificationFilter = new NotificationFilter();
}
@@ -140,7 +142,7 @@ public class NotificationFilterTest extends SysuiTestCase {
public void testSuppressSystemAlertNotification() {
when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
- StatusBarNotification sbn = mRow.getEntry().notification;
+ StatusBarNotification sbn = mRow.getEntry().getSbn();
Bundle bundle = new Bundle();
bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{"something"});
sbn.getNotification().extras = bundle;
@@ -150,7 +152,7 @@ public class NotificationFilterTest extends SysuiTestCase {
@Test
public void testDoNotSuppressSystemAlertNotification() {
- StatusBarNotification sbn = mRow.getEntry().notification;
+ StatusBarNotification sbn = mRow.getEntry().getSbn();
Bundle bundle = new Bundle();
bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{"something"});
sbn.getNotification().extras = bundle;
@@ -178,7 +180,7 @@ public class NotificationFilterTest extends SysuiTestCase {
// missing extra
assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry()));
- StatusBarNotification sbn = mRow.getEntry().notification;
+ StatusBarNotification sbn = mRow.getEntry().getSbn();
Bundle bundle = new Bundle();
bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{});
sbn.getNotification().extras = bundle;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
index 8d496a72e3b2..2435bb92b08d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
@@ -21,6 +21,7 @@ import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -41,11 +42,15 @@ import com.android.systemui.ForegroundServiceController;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationEntryBuilder;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
+import com.android.systemui.util.DeviceConfigProxyFake;
import org.junit.Before;
import org.junit.Test;
@@ -73,13 +78,18 @@ public class NotificationListControllerTest extends SysuiTestCase {
private DeviceProvisionedListener mProvisionedListener;
// TODO: Remove this once EntryManager no longer needs to be mocked
- private NotificationData mNotificationData = new NotificationData(mContext);
+ private NotificationData mNotificationData =
+ new NotificationData(
+ new NotificationSectionsFeatureManager(new DeviceConfigProxyFake(), mContext),
+ mock(NotifLog.class),
+ mock(PeopleNotificationIdentifier.class));
private int mNextNotifId = 0;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
@@ -102,7 +112,7 @@ public class NotificationListControllerTest extends SysuiTestCase {
final NotificationEntry entry = buildEntry();
mEntryListener.onEntryRemoved(
entry,
- NotificationVisibility.obtain(entry.key, 0, 0, true),
+ NotificationVisibility.obtain(entry.getKey(), 0, 0, true),
false);
verify(mListContainer).cleanUpViewStateForEntry(entry);
}
@@ -110,7 +120,7 @@ public class NotificationListControllerTest extends SysuiTestCase {
@Test
public void testCallUpdateNotificationsOnDeviceProvisionedChange() {
mProvisionedListener.onDeviceProvisionedChanged();
- verify(mEntryManager).updateNotifications();
+ verify(mEntryManager).updateNotifications(anyString());
}
@Test
@@ -119,19 +129,19 @@ public class NotificationListControllerTest extends SysuiTestCase {
final NotificationEntry entry = buildEntry();
mNotificationData.add(entry);
when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
- .thenReturn(entry.key);
+ .thenReturn(entry.getKey());
// WHEN we are notified of a new app op
mController.updateNotificationsForAppOp(
AppOpsManager.OP_CAMERA,
- entry.notification.getUid(),
- entry.notification.getPackageName(),
+ entry.getSbn().getUid(),
+ entry.getSbn().getPackageName(),
true);
// THEN the app op is added to the entry
assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
- // THEN updateNotifications() is called
- verify(mEntryManager, times(1)).updateNotifications();
+ // THEN updateNotifications(TEST) is called
+ verify(mEntryManager, times(1)).updateNotifications(anyString());
}
@Test
@@ -143,8 +153,8 @@ public class NotificationListControllerTest extends SysuiTestCase {
// WHEN An unrelated notification gets a new app op
mController.updateNotificationsForAppOp(AppOpsManager.OP_CAMERA, 1000, "pkg", true);
- // THEN We never call updateNotifications()
- verify(mEntryManager, never()).updateNotifications();
+ // THEN We never call updateNotifications(TEST)
+ verify(mEntryManager, never()).updateNotifications(anyString());
}
@Test
@@ -158,10 +168,10 @@ public class NotificationListControllerTest extends SysuiTestCase {
expected.add(235);
expected.add(1);
when(mForegroundServiceController.getStandardLayoutKey(
- entry.notification.getUserId(),
- entry.notification.getPackageName())).thenReturn(entry.key);
- when(mForegroundServiceController.getAppOps(entry.notification.getUserId(),
- entry.notification.getPackageName())).thenReturn(expected);
+ entry.getSbn().getUserId(),
+ entry.getSbn().getPackageName())).thenReturn(entry.getKey());
+ when(mForegroundServiceController.getAppOps(entry.getSbn().getUserId(),
+ entry.getSbn().getPackageName())).thenReturn(expected);
// WHEN the notification is added
mEntryListener.onBeforeNotificationAdded(entry);
@@ -180,10 +190,10 @@ public class NotificationListControllerTest extends SysuiTestCase {
mNotificationData.add(entry);
when(mForegroundServiceController.getStandardLayoutKey(
- entry.notification.getUserId(),
- entry.notification.getPackageName())).thenReturn(entry.key);
- when(mForegroundServiceController.getAppOps(entry.notification.getUserId(),
- entry.notification.getPackageName())).thenReturn(null);
+ entry.getSbn().getUserId(),
+ entry.getSbn().getPackageName())).thenReturn(entry.getKey());
+ when(mForegroundServiceController.getAppOps(entry.getSbn().getUserId(),
+ entry.getSbn().getPackageName())).thenReturn(null);
// WHEN the notification is added
mEntryListener.onBeforeNotificationAdded(entry);
@@ -201,11 +211,11 @@ public class NotificationListControllerTest extends SysuiTestCase {
ops.add(3);
ops.add(235);
ops.add(1);
- when(mForegroundServiceController.getAppOps(entry.notification.getUserId(),
- entry.notification.getPackageName())).thenReturn(ops);
+ when(mForegroundServiceController.getAppOps(entry.getSbn().getUserId(),
+ entry.getSbn().getPackageName())).thenReturn(ops);
when(mForegroundServiceController.getStandardLayoutKey(
- entry.notification.getUserId(),
- entry.notification.getPackageName())).thenReturn("something else");
+ entry.getSbn().getUserId(),
+ entry.getSbn().getPackageName())).thenReturn("something else");
// WHEN the notification is added
mEntryListener.onBeforeNotificationAdded(entry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
new file mode 100644
index 000000000000..b3d0d22445b6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification
+
+import android.provider.DeviceConfig
+import android.provider.Settings
+import android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL
+import android.testing.AndroidTestingRunner
+
+import androidx.test.filters.SmallTest
+
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.DeviceConfigProxyFake
+
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class NotificationSectionsFeatureManagerTest : SysuiTestCase() {
+ var manager: NotificationSectionsFeatureManager? = null
+ val proxyFake = DeviceConfigProxyFake()
+
+ @Before
+ public fun setup() {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ NOTIFICATION_NEW_INTERRUPTION_MODEL, 1)
+ manager = NotificationSectionsFeatureManager(proxyFake, mContext)
+ manager!!.clearCache()
+ }
+
+ @Test
+ public fun testPeopleFilteringOff_newInterruptionModelOn() {
+ proxyFake.setProperty(
+ DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_USE_PEOPLE_FILTERING, "false", false)
+
+ assertFalse("People filtering should be disabled", manager!!.isFilteringEnabled())
+ assertTrue("Expecting 2 buckets when people filtering is disabled",
+ manager!!.getNumberOfBuckets() == 2)
+ }
+
+ @Test
+ public fun testPeopleFilteringOn_newInterruptionModelOn() {
+ proxyFake.setProperty(
+ DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_USE_PEOPLE_FILTERING, "true", false)
+
+ assertTrue("People filtering should be enabled", manager!!.isFilteringEnabled())
+ assertTrue("Expecting 3 buckets when people filtering is enabled",
+ manager!!.getNumberOfBuckets() == 3)
+ }
+}
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
new file mode 100644
index 000000000000..e1beb34db6bd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -0,0 +1,625 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
+import static android.service.notification.NotificationListenerService.REASON_CLICK;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_UNKNOWN;
+
+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.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.NotificationStats;
+import android.service.notification.StatusBarNotification;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.ArrayMap;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationListener.NotifServiceListener;
+import com.android.systemui.statusbar.RankingBuilder;
+import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
+import com.android.systemui.util.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+import java.util.Arrays;
+import java.util.Map;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotifCollectionTest extends SysuiTestCase {
+
+ @Mock private IStatusBarService mStatusBarService;
+ @Mock private NotificationListener mListenerService;
+ @Spy private RecordingCollectionListener mCollectionListener;
+
+ @Spy private RecordingLifetimeExtender mExtender1 = new RecordingLifetimeExtender("Extender1");
+ @Spy private RecordingLifetimeExtender mExtender2 = new RecordingLifetimeExtender("Extender2");
+ @Spy private RecordingLifetimeExtender mExtender3 = new RecordingLifetimeExtender("Extender3");
+
+ @Captor private ArgumentCaptor<NotifServiceListener> mListenerCaptor;
+ @Captor private ArgumentCaptor<NotificationEntry> mEntryCaptor;
+
+ private NotifCollection mCollection;
+ private NotifServiceListener mServiceListener;
+
+ private NoManSimulator mNoMan;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ Assert.sMainLooper = TestableLooper.get(this).getLooper();
+
+ mCollection = new NotifCollection(mStatusBarService);
+ mCollection.attach(mListenerService);
+ mCollection.addCollectionListener(mCollectionListener);
+
+ // Capture the listener object that the collection registers with the listener service so
+ // we can simulate listener service events in tests below
+ verify(mListenerService).setDownstreamListener(mListenerCaptor.capture());
+ mServiceListener = checkNotNull(mListenerCaptor.getValue());
+
+ mNoMan = new NoManSimulator(mServiceListener);
+ }
+
+ @Test
+ public void testEventDispatchedWhenNotifPosted() {
+ // WHEN a notification is posted
+ PostedNotif notif1 = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE, 3)
+ .setRank(4747));
+
+ // THEN the listener is notified
+ verify(mCollectionListener).onEntryAdded(mEntryCaptor.capture());
+
+ NotificationEntry entry = mEntryCaptor.getValue();
+ assertEquals(notif1.key, entry.getKey());
+ assertEquals(notif1.sbn, entry.getSbn());
+ assertEquals(notif1.ranking, entry.getRanking());
+ }
+
+ @Test
+ public void testEventDispatchedWhenNotifUpdated() {
+ // GIVEN a collection with one notif
+ mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3)
+ .setRank(4747));
+
+ // WHEN the notif is reposted
+ PostedNotif notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3)
+ .setRank(89));
+
+ // THEN the listener is notified
+ verify(mCollectionListener).onEntryUpdated(mEntryCaptor.capture());
+
+ NotificationEntry entry = mEntryCaptor.getValue();
+ assertEquals(notif2.key, entry.getKey());
+ assertEquals(notif2.sbn, entry.getSbn());
+ assertEquals(notif2.ranking, entry.getRanking());
+ }
+
+ @Test
+ public void testEventDispatchedWhenNotifRemoved() {
+ // GIVEN a collection with one notif
+ mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3));
+ clearInvocations(mCollectionListener);
+
+ PostedNotif notif = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
+ NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+ clearInvocations(mCollectionListener);
+
+ // WHEN a notif is retracted
+ mNoMan.retractNotif(notif.sbn, REASON_APP_CANCEL);
+
+ // THEN the listener is notified
+ verify(mCollectionListener).onEntryRemoved(entry, REASON_APP_CANCEL, false);
+ assertEquals(notif.sbn, entry.getSbn());
+ assertEquals(notif.ranking, entry.getRanking());
+ }
+
+ @Test
+ public void testRankingsAreUpdatedForOtherNotifs() {
+ // GIVEN a collection with one notif
+ PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3)
+ .setRank(47));
+ NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
+
+ // WHEN a new notif is posted, triggering a rerank
+ mNoMan.setRanking(notif1.sbn.getKey(), new RankingBuilder(notif1.ranking)
+ .setRank(56)
+ .build());
+ mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 77));
+
+ // THEN the ranking is updated on the first entry
+ assertEquals(56, entry1.getRanking().getRank());
+ }
+
+ @Test
+ public void testRankingUpdateIsProperlyIssuedToEveryone() {
+ // GIVEN a collection with a couple notifs
+ PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3)
+ .setRank(3));
+ PostedNotif notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 8)
+ .setRank(2));
+ PostedNotif notif3 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 77)
+ .setRank(1));
+
+ NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
+ NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
+ NotificationEntry entry3 = mCollectionListener.getEntry(notif3.key);
+
+ // WHEN a ranking update is delivered
+ Ranking newRanking1 = new RankingBuilder(notif1.ranking)
+ .setRank(4)
+ .setExplanation("Foo bar")
+ .build();
+ Ranking newRanking2 = new RankingBuilder(notif2.ranking)
+ .setRank(5)
+ .setExplanation("baz buzz")
+ .build();
+ Ranking newRanking3 = new RankingBuilder(notif3.ranking)
+ .setRank(6)
+ .setExplanation("Penguin pizza")
+ .build();
+
+ mNoMan.setRanking(notif1.sbn.getKey(), newRanking1);
+ mNoMan.setRanking(notif2.sbn.getKey(), newRanking2);
+ mNoMan.setRanking(notif3.sbn.getKey(), newRanking3);
+ mNoMan.issueRankingUpdate();
+
+ // THEN all of the NotifEntries have their rankings properly updated
+ assertEquals(newRanking1, entry1.getRanking());
+ assertEquals(newRanking2, entry2.getRanking());
+ assertEquals(newRanking3, entry3.getRanking());
+ }
+
+ @Test
+ public void testNotifEntriesAreNotPersistedAcrossRemovalAndReposting() {
+ // GIVEN a notification that has been posted
+ PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3));
+ NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
+
+ // WHEN the notification is retracted and then reposted
+ mNoMan.retractNotif(notif1.sbn, REASON_APP_CANCEL);
+ mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3));
+
+ // THEN the new NotificationEntry is a new object
+ NotificationEntry entry2 = mCollectionListener.getEntry(notif1.key);
+ assertNotEquals(entry2, entry1);
+ }
+
+ @Test
+ public void testDismissNotification() throws RemoteException {
+ // GIVEN a collection with a couple notifications and a lifetime extender
+ mCollection.addNotificationLifetimeExtender(mExtender1);
+
+ PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
+ PostedNotif notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88, "barTag"));
+ NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
+
+ // WHEN a notification is manually dismissed
+ DismissedByUserStats stats = new DismissedByUserStats(
+ NotificationStats.DISMISSAL_SHADE,
+ NotificationStats.DISMISS_SENTIMENT_NEUTRAL,
+ NotificationVisibility.obtain(entry2.getKey(), 7, 2, true));
+
+ mCollection.dismissNotification(entry2, REASON_CLICK, stats);
+
+ // THEN we check for lifetime extension
+ verify(mExtender1).shouldExtendLifetime(entry2, REASON_CLICK);
+
+ // THEN we send the dismissal to system server
+ verify(mStatusBarService).onNotificationClear(
+ notif2.sbn.getPackageName(),
+ notif2.sbn.getTag(),
+ 88,
+ notif2.sbn.getUser().getIdentifier(),
+ notif2.sbn.getKey(),
+ stats.dismissalSurface,
+ stats.dismissalSentiment,
+ stats.notificationVisibility);
+
+ // THEN we fire a remove event
+ verify(mCollectionListener).onEntryRemoved(entry2, REASON_CLICK, true);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testDismissingNonExistentNotificationThrows() {
+ // GIVEN a collection that originally had three notifs, but where one was dismissed
+ PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
+ PostedNotif notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
+ PostedNotif notif3 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 99));
+ NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
+ mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
+
+ // WHEN we try to dismiss a notification that isn't present
+ mCollection.dismissNotification(
+ entry2,
+ REASON_CLICK,
+ new DismissedByUserStats(0, 0, NotificationVisibility.obtain("foo", 47, 3, true)));
+
+ // THEN an exception is thrown
+ }
+
+ @Test
+ public void testLifetimeExtendersAreQueriedWhenNotifRemoved() {
+ // GIVEN a couple notifications and a few lifetime extenders
+ mExtender1.shouldExtendLifetime = true;
+ mExtender2.shouldExtendLifetime = true;
+
+ mCollection.addNotificationLifetimeExtender(mExtender1);
+ mCollection.addNotificationLifetimeExtender(mExtender2);
+ mCollection.addNotificationLifetimeExtender(mExtender3);
+
+ PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
+ PostedNotif notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
+ NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
+
+ // WHEN a notification is removed
+ mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
+
+ // THEN each extender is asked whether to extend, even if earlier ones return true
+ verify(mExtender1).shouldExtendLifetime(entry2, REASON_UNKNOWN);
+ verify(mExtender2).shouldExtendLifetime(entry2, REASON_UNKNOWN);
+ verify(mExtender3).shouldExtendLifetime(entry2, REASON_UNKNOWN);
+
+ // THEN the entry is not removed
+ assertTrue(mCollection.getNotifs().contains(entry2));
+
+ // THEN the entry properly records all extenders that returned true
+ assertEquals(Arrays.asList(mExtender1, mExtender2), entry2.mLifetimeExtenders);
+ }
+
+ @Test
+ public void testWhenLastLifetimeExtenderExpiresAllAreReQueried() {
+ // GIVEN a couple notifications and a few lifetime extenders
+ mExtender2.shouldExtendLifetime = true;
+
+ mCollection.addNotificationLifetimeExtender(mExtender1);
+ mCollection.addNotificationLifetimeExtender(mExtender2);
+ mCollection.addNotificationLifetimeExtender(mExtender3);
+
+ PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
+ PostedNotif notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
+ NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
+
+ // GIVEN a notification gets lifetime-extended by one of them
+ mNoMan.retractNotif(notif2.sbn, REASON_APP_CANCEL);
+ assertTrue(mCollection.getNotifs().contains(entry2));
+ clearInvocations(mExtender1, mExtender2, mExtender3);
+
+ // WHEN the last active extender expires (but new ones become active)
+ mExtender1.shouldExtendLifetime = true;
+ mExtender2.shouldExtendLifetime = false;
+ mExtender3.shouldExtendLifetime = true;
+ mExtender2.callback.onEndLifetimeExtension(mExtender2, entry2);
+
+ // THEN each extender is re-queried
+ verify(mExtender1).shouldExtendLifetime(entry2, REASON_UNKNOWN);
+ verify(mExtender2).shouldExtendLifetime(entry2, REASON_UNKNOWN);
+ verify(mExtender3).shouldExtendLifetime(entry2, REASON_UNKNOWN);
+
+ // THEN the entry is not removed
+ assertTrue(mCollection.getNotifs().contains(entry2));
+
+ // THEN the entry properly records all extenders that returned true
+ assertEquals(Arrays.asList(mExtender1, mExtender3), entry2.mLifetimeExtenders);
+ }
+
+ @Test
+ public void testExtendersAreNotReQueriedUntilFinalActiveExtenderExpires() {
+ // GIVEN a couple notifications and a few lifetime extenders
+ mExtender1.shouldExtendLifetime = true;
+ mExtender2.shouldExtendLifetime = true;
+
+ mCollection.addNotificationLifetimeExtender(mExtender1);
+ mCollection.addNotificationLifetimeExtender(mExtender2);
+ mCollection.addNotificationLifetimeExtender(mExtender3);
+
+ PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
+ PostedNotif notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
+ NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
+
+ // GIVEN a notification gets lifetime-extended by a couple of them
+ mNoMan.retractNotif(notif2.sbn, REASON_APP_CANCEL);
+ assertTrue(mCollection.getNotifs().contains(entry2));
+ clearInvocations(mExtender1, mExtender2, mExtender3);
+
+ // WHEN one (but not all) of the extenders expires
+ mExtender2.shouldExtendLifetime = false;
+ mExtender2.callback.onEndLifetimeExtension(mExtender2, entry2);
+
+ // THEN the entry is not removed
+ assertTrue(mCollection.getNotifs().contains(entry2));
+
+ // THEN we don't re-query the extenders
+ verify(mExtender1, never()).shouldExtendLifetime(eq(entry2), anyInt());
+ verify(mExtender2, never()).shouldExtendLifetime(eq(entry2), anyInt());
+ verify(mExtender3, never()).shouldExtendLifetime(eq(entry2), anyInt());
+
+ // THEN the entry properly records all extenders that returned true
+ assertEquals(Arrays.asList(mExtender1), entry2.mLifetimeExtenders);
+ }
+
+ @Test
+ public void testNotificationIsRemovedWhenAllLifetimeExtendersExpire() {
+ // GIVEN a couple notifications and a few lifetime extenders
+ mExtender1.shouldExtendLifetime = true;
+ mExtender2.shouldExtendLifetime = true;
+
+ mCollection.addNotificationLifetimeExtender(mExtender1);
+ mCollection.addNotificationLifetimeExtender(mExtender2);
+ mCollection.addNotificationLifetimeExtender(mExtender3);
+
+ PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
+ PostedNotif notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
+ NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
+
+ // GIVEN a notification gets lifetime-extended by a couple of them
+ mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
+ assertTrue(mCollection.getNotifs().contains(entry2));
+ clearInvocations(mExtender1, mExtender2, mExtender3);
+
+ // WHEN all of the active extenders expire
+ mExtender2.shouldExtendLifetime = false;
+ mExtender2.callback.onEndLifetimeExtension(mExtender2, entry2);
+ mExtender1.shouldExtendLifetime = false;
+ mExtender1.callback.onEndLifetimeExtension(mExtender1, entry2);
+
+ // THEN the entry removed
+ assertFalse(mCollection.getNotifs().contains(entry2));
+ verify(mCollectionListener).onEntryRemoved(entry2, REASON_UNKNOWN, false);
+ }
+
+ @Test
+ public void testLifetimeExtensionIsCanceledWhenNotifIsUpdated() {
+ // GIVEN a few lifetime extenders and a couple notifications
+ mCollection.addNotificationLifetimeExtender(mExtender1);
+ mCollection.addNotificationLifetimeExtender(mExtender2);
+ mCollection.addNotificationLifetimeExtender(mExtender3);
+
+ mExtender1.shouldExtendLifetime = true;
+ mExtender2.shouldExtendLifetime = true;
+
+ PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
+ PostedNotif notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
+ NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
+
+ // GIVEN a notification gets lifetime-extended by a couple of them
+ mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
+ assertTrue(mCollection.getNotifs().contains(entry2));
+ clearInvocations(mExtender1, mExtender2, mExtender3);
+
+ // WHEN the notification is reposted
+ mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
+
+ // THEN all of the active lifetime extenders are canceled
+ verify(mExtender1).cancelLifetimeExtension(entry2);
+ verify(mExtender2).cancelLifetimeExtension(entry2);
+
+ // THEN the notification is still present
+ assertTrue(mCollection.getNotifs().contains(entry2));
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testReentrantCallsToLifetimeExtendersThrow() {
+ // GIVEN a few lifetime extenders and a couple notifications
+ mCollection.addNotificationLifetimeExtender(mExtender1);
+ mCollection.addNotificationLifetimeExtender(mExtender2);
+ mCollection.addNotificationLifetimeExtender(mExtender3);
+
+ mExtender1.shouldExtendLifetime = true;
+ mExtender2.shouldExtendLifetime = true;
+
+ PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
+ PostedNotif notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
+ NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
+
+ // GIVEN a notification gets lifetime-extended by a couple of them
+ mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
+ assertTrue(mCollection.getNotifs().contains(entry2));
+ clearInvocations(mExtender1, mExtender2, mExtender3);
+
+ // WHEN a lifetime extender makes a reentrant call during cancelLifetimeExtension()
+ mExtender2.onCancelLifetimeExtension = () -> {
+ mExtender2.callback.onEndLifetimeExtension(mExtender2, entry2);
+ };
+ // This triggers the call to cancelLifetimeExtension()
+ mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
+
+ // THEN an exception is thrown
+ }
+
+ @Test
+ public void testRankingIsUpdatedWhenALifetimeExtendedNotifIsReposted() {
+ // GIVEN a few lifetime extenders and a couple notifications
+ mCollection.addNotificationLifetimeExtender(mExtender1);
+ mCollection.addNotificationLifetimeExtender(mExtender2);
+ mCollection.addNotificationLifetimeExtender(mExtender3);
+
+ mExtender1.shouldExtendLifetime = true;
+ mExtender2.shouldExtendLifetime = true;
+
+ PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
+ PostedNotif notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
+ NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
+
+ // GIVEN a notification gets lifetime-extended by a couple of them
+ mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
+ assertTrue(mCollection.getNotifs().contains(entry2));
+ clearInvocations(mExtender1, mExtender2, mExtender3);
+
+ // WHEN the notification is reposted
+ PostedNotif notif2a = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88)
+ .setRank(4747)
+ .setExplanation("Some new explanation"));
+
+ // THEN the notification's ranking is properly updated
+ assertEquals(notif2a.ranking, entry2.getRanking());
+ }
+
+ private static NotificationEntryBuilder buildNotif(String pkg, int id, String tag) {
+ return new NotificationEntryBuilder()
+ .setPkg(pkg)
+ .setId(id)
+ .setTag(tag);
+ }
+
+ private static NotificationEntryBuilder buildNotif(String pkg, int id) {
+ return new NotificationEntryBuilder()
+ .setPkg(pkg)
+ .setId(id);
+ }
+
+ private static class NoManSimulator {
+ private final NotifServiceListener mListener;
+ private final Map<String, Ranking> mRankings = new ArrayMap<>();
+
+ private NoManSimulator(
+ NotifServiceListener listener) {
+ mListener = listener;
+ }
+
+ PostedNotif postNotif(NotificationEntryBuilder builder) {
+ NotificationEntry entry = builder.build();
+ mRankings.put(entry.getKey(), entry.getRanking());
+ mListener.onNotificationPosted(entry.getSbn(), buildRankingMap());
+ return new PostedNotif(entry.getSbn(), entry.getRanking());
+ }
+
+ void retractNotif(StatusBarNotification sbn, int reason) {
+ assertNotNull(mRankings.remove(sbn.getKey()));
+ mListener.onNotificationRemoved(sbn, buildRankingMap(), reason);
+ }
+
+ void issueRankingUpdate() {
+ mListener.onNotificationRankingUpdate(buildRankingMap());
+ }
+
+ void setRanking(String key, Ranking ranking) {
+ mRankings.put(key, ranking);
+ }
+
+ private RankingMap buildRankingMap() {
+ return new RankingMap(mRankings.values().toArray(new Ranking[0]));
+ }
+ }
+
+ private static class PostedNotif {
+ public final String key;
+ public final StatusBarNotification sbn;
+ public final Ranking ranking;
+
+ private PostedNotif(StatusBarNotification sbn,
+ Ranking ranking) {
+ this.key = sbn.getKey();
+ this.sbn = sbn;
+ this.ranking = ranking;
+ }
+ }
+
+ private static class RecordingCollectionListener implements NotifCollectionListener {
+ private final Map<String, NotificationEntry> mLastSeenEntries = new ArrayMap<>();
+
+ @Override
+ public void onEntryAdded(NotificationEntry entry) {
+ mLastSeenEntries.put(entry.getKey(), entry);
+ }
+
+ @Override
+ public void onEntryUpdated(NotificationEntry entry) {
+ }
+
+ @Override
+ public void onEntryRemoved(NotificationEntry entry, int reason, boolean removedByUser) {
+ }
+
+ public NotificationEntry getEntry(String key) {
+ if (!mLastSeenEntries.containsKey(key)) {
+ throw new RuntimeException("Key not found: " + key);
+ }
+ return mLastSeenEntries.get(key);
+ }
+ }
+
+ private static class RecordingLifetimeExtender implements NotifLifetimeExtender {
+ private final String mName;
+
+ public @Nullable OnEndLifetimeExtensionCallback callback;
+ public boolean shouldExtendLifetime = false;
+ public @Nullable Runnable onCancelLifetimeExtension;
+
+ private RecordingLifetimeExtender(String name) {
+ mName = name;
+ }
+
+ @Override
+ public String getName() {
+ return mName;
+ }
+
+ @Override
+ public void setCallback(OnEndLifetimeExtensionCallback callback) {
+ this.callback = callback;
+ }
+
+ @Override
+ public boolean shouldExtendLifetime(
+ NotificationEntry entry,
+ @CancellationReason int reason) {
+ return shouldExtendLifetime;
+ }
+
+ @Override
+ public void cancelLifetimeExtension(NotificationEntry entry) {
+ if (onCancelLifetimeExtension != null) {
+ onCancelLifetimeExtension.run();
+ }
+ }
+ }
+
+ private static final String TEST_PACKAGE = "com.android.test.collection";
+ private static final String TEST_PACKAGE2 = "com.android.test.collection2";
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
index 657ec61dfd11..dba0174f2626 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
@@ -50,7 +50,6 @@ import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Person;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
@@ -75,10 +74,14 @@ import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationEntryBuilder;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SbnBuilder;
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -136,12 +139,14 @@ public class NotificationDataTest extends SysuiTestCase {
mDependency.injectTestDependency(NotificationGroupManager.class,
new NotificationGroupManager(mock(StatusBarStateController.class)));
mDependency.injectMockDependency(ShadeController.class);
+ mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment);
when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
- mNotificationData = new TestableNotificationData(mContext);
- mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class));
- mRow = new NotificationTestHelper(getContext()).createRow();
+ mNotificationData = new TestableNotificationData(
+ mock(NotificationSectionsFeatureManager.class));
+ mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class), "");
+ mRow = new NotificationTestHelper(getContext(), mDependency).createRow();
Dependency.get(InitController.class).executePostInitTasks();
}
@@ -149,7 +154,7 @@ public class NotificationDataTest extends SysuiTestCase {
public void testChannelSetWhenAdded() {
Bundle override = new Bundle();
override.putParcelable(OVERRIDE_CHANNEL, NOTIFICATION_CHANNEL);
- mNotificationData.rankingOverrides.put(mRow.getEntry().key, override);
+ mNotificationData.rankingOverrides.put(mRow.getEntry().getKey(), override);
mNotificationData.add(mRow.getEntry());
assertEquals(NOTIFICATION_CHANNEL, mRow.getEntry().getChannel());
}
@@ -157,10 +162,11 @@ public class NotificationDataTest extends SysuiTestCase {
@Test
public void testAllRelevantNotisTaggedWithAppOps() throws Exception {
mNotificationData.add(mRow.getEntry());
- ExpandableNotificationRow row2 = new NotificationTestHelper(getContext()).createRow();
+ ExpandableNotificationRow row2 = new NotificationTestHelper(getContext(), mDependency)
+ .createRow();
mNotificationData.add(row2.getEntry());
ExpandableNotificationRow diffPkg =
- new NotificationTestHelper(getContext()).createRow("pkg", 4000,
+ new NotificationTestHelper(getContext(), mDependency).createRow("pkg", 4000,
Process.myUserHandle());
mNotificationData.add(diffPkg.getEntry());
@@ -170,24 +176,25 @@ public class NotificationDataTest extends SysuiTestCase {
for (int op : expectedOps) {
mNotificationData.updateAppOp(op, NotificationTestHelper.UID,
- NotificationTestHelper.PKG, mRow.getEntry().key, true);
+ NotificationTestHelper.PKG, mRow.getEntry().getKey(), true);
mNotificationData.updateAppOp(op, NotificationTestHelper.UID,
- NotificationTestHelper.PKG, row2.getEntry().key, true);
+ NotificationTestHelper.PKG, row2.getEntry().getKey(), true);
}
for (int op : expectedOps) {
- assertTrue(mRow.getEntry().key + " doesn't have op " + op,
- mNotificationData.get(mRow.getEntry().key).mActiveAppOps.contains(op));
- assertTrue(row2.getEntry().key + " doesn't have op " + op,
- mNotificationData.get(row2.getEntry().key).mActiveAppOps.contains(op));
- assertFalse(diffPkg.getEntry().key + " has op " + op,
- mNotificationData.get(diffPkg.getEntry().key).mActiveAppOps.contains(op));
+ assertTrue(mRow.getEntry().getKey() + " doesn't have op " + op,
+ mNotificationData.get(mRow.getEntry().getKey()).mActiveAppOps.contains(op));
+ assertTrue(row2.getEntry().getKey() + " doesn't have op " + op,
+ mNotificationData.get(row2.getEntry().getKey()).mActiveAppOps.contains(op));
+ assertFalse(diffPkg.getEntry().getKey() + " has op " + op,
+ mNotificationData.get(diffPkg.getEntry().getKey()).mActiveAppOps.contains(op));
}
}
@Test
public void testAppOpsRemoval() throws Exception {
mNotificationData.add(mRow.getEntry());
- ExpandableNotificationRow row2 = new NotificationTestHelper(getContext()).createRow();
+ ExpandableNotificationRow row2 = new NotificationTestHelper(getContext(), mDependency)
+ .createRow();
mNotificationData.add(row2.getEntry());
ArraySet<Integer> expectedOps = new ArraySet<>();
@@ -196,22 +203,22 @@ public class NotificationDataTest extends SysuiTestCase {
for (int op : expectedOps) {
mNotificationData.updateAppOp(op, NotificationTestHelper.UID,
- NotificationTestHelper.PKG, row2.getEntry().key, true);
+ NotificationTestHelper.PKG, row2.getEntry().getKey(), true);
}
expectedOps.remove(OP_ACCEPT_HANDOVER);
mNotificationData.updateAppOp(OP_ACCEPT_HANDOVER, NotificationTestHelper.UID,
- NotificationTestHelper.PKG, row2.getEntry().key, false);
-
- assertTrue(mRow.getEntry().key + " doesn't have op " + OP_CAMERA,
- mNotificationData.get(mRow.getEntry().key).mActiveAppOps.contains(OP_CAMERA));
- assertTrue(row2.getEntry().key + " doesn't have op " + OP_CAMERA,
- mNotificationData.get(row2.getEntry().key).mActiveAppOps.contains(OP_CAMERA));
- assertFalse(mRow.getEntry().key + " has op " + OP_ACCEPT_HANDOVER,
- mNotificationData.get(mRow.getEntry().key)
+ NotificationTestHelper.PKG, row2.getEntry().getKey(), false);
+
+ assertTrue(mRow.getEntry().getKey() + " doesn't have op " + OP_CAMERA,
+ mNotificationData.get(mRow.getEntry().getKey()).mActiveAppOps.contains(OP_CAMERA));
+ assertTrue(row2.getEntry().getKey() + " doesn't have op " + OP_CAMERA,
+ mNotificationData.get(row2.getEntry().getKey()).mActiveAppOps.contains(OP_CAMERA));
+ assertFalse(mRow.getEntry().getKey() + " has op " + OP_ACCEPT_HANDOVER,
+ mNotificationData.get(mRow.getEntry().getKey())
.mActiveAppOps.contains(OP_ACCEPT_HANDOVER));
- assertFalse(row2.getEntry().key + " has op " + OP_ACCEPT_HANDOVER,
- mNotificationData.get(row2.getEntry().key)
+ assertFalse(row2.getEntry().getKey() + " has op " + OP_ACCEPT_HANDOVER,
+ mNotificationData.get(row2.getEntry().getKey())
.mActiveAppOps.contains(OP_ACCEPT_HANDOVER));
}
@@ -219,13 +226,14 @@ public class NotificationDataTest extends SysuiTestCase {
public void testGetNotificationsForCurrentUser_shouldFilterNonCurrentUserNotifications()
throws Exception {
mNotificationData.add(mRow.getEntry());
- ExpandableNotificationRow row2 = new NotificationTestHelper(getContext()).createRow();
+ ExpandableNotificationRow row2 = new NotificationTestHelper(getContext(), mDependency)
+ .createRow();
mNotificationData.add(row2.getEntry());
when(mEnvironment.isNotificationForCurrentProfiles(
- mRow.getEntry().notification)).thenReturn(false);
+ mRow.getEntry().getSbn())).thenReturn(false);
when(mEnvironment.isNotificationForCurrentProfiles(
- row2.getEntry().notification)).thenReturn(true);
+ row2.getEntry().getSbn())).thenReturn(true);
ArrayList<NotificationEntry> result =
mNotificationData.getNotificationsForCurrentUser();
@@ -237,12 +245,12 @@ public class NotificationDataTest extends SysuiTestCase {
public void testIsExemptFromDndVisualSuppression_foreground() {
initStatusBarNotification(false);
- mEntry.sbn().getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
+ mEntry.getSbn().getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
mEntry.setRow(mRow);
mNotificationData.add(mEntry);
Bundle override = new Bundle();
override.putInt(OVERRIDE_VIS_EFFECTS, 255);
- mNotificationData.rankingOverrides.put(mEntry.key, override);
+ mNotificationData.rankingOverrides.put(mEntry.getKey(), override);
assertTrue(mEntry.isExemptFromDndVisualSuppression());
assertFalse(mEntry.shouldSuppressAmbient());
@@ -251,7 +259,7 @@ public class NotificationDataTest extends SysuiTestCase {
@Test
public void testIsExemptFromDndVisualSuppression_media() {
initStatusBarNotification(false);
- Notification n = mEntry.sbn().getNotification();
+ Notification n = mEntry.getSbn().getNotification();
Notification.Builder nb = Notification.Builder.recoverBuilder(mContext, n);
nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
n = nb.build();
@@ -262,7 +270,7 @@ public class NotificationDataTest extends SysuiTestCase {
mNotificationData.add(mEntry);
Bundle override = new Bundle();
override.putInt(OVERRIDE_VIS_EFFECTS, 255);
- mNotificationData.rankingOverrides.put(mEntry.key, override);
+ mNotificationData.rankingOverrides.put(mEntry.getKey(), override);
assertTrue(mEntry.isExemptFromDndVisualSuppression());
assertFalse(mEntry.shouldSuppressAmbient());
@@ -276,7 +284,7 @@ public class NotificationDataTest extends SysuiTestCase {
mNotificationData.add(mEntry);
Bundle override = new Bundle();
override.putInt(OVERRIDE_VIS_EFFECTS, 255);
- mNotificationData.rankingOverrides.put(mEntry.key, override);
+ mNotificationData.rankingOverrides.put(mEntry.getKey(), override);
assertTrue(mEntry.isExemptFromDndVisualSuppression());
assertFalse(mEntry.shouldSuppressAmbient());
@@ -292,7 +300,7 @@ public class NotificationDataTest extends SysuiTestCase {
entry.mIsSystemNotification = true;
Bundle override = new Bundle();
override.putInt(OVERRIDE_VIS_EFFECTS, NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT);
- mNotificationData.rankingOverrides.put(entry.key, override);
+ mNotificationData.rankingOverrides.put(entry.getKey(), override);
mNotificationData.add(entry);
modifySbn(entry)
@@ -504,7 +512,7 @@ public class NotificationDataTest extends SysuiTestCase {
Bundle override = new Bundle();
override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_LOW);
override.putInt(OVERRIDE_RANK, 1);
- mNotificationData.rankingOverrides.put(a.key, override);
+ mNotificationData.rankingOverrides.put(a.getKey(), override);
Notification bN = new Notification.Builder(mContext, "test")
.setStyle(new Notification.MessagingStyle(""))
@@ -523,7 +531,7 @@ public class NotificationDataTest extends SysuiTestCase {
Bundle bOverride = new Bundle();
bOverride.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_LOW);
bOverride.putInt(OVERRIDE_RANK, 2);
- mNotificationData.rankingOverrides.put(b.key, bOverride);
+ mNotificationData.rankingOverrides.put(b.getKey(), bOverride);
assertEquals(1, mNotificationData.mRankingComparator.compare(a, b));
}
@@ -548,7 +556,7 @@ public class NotificationDataTest extends SysuiTestCase {
Bundle override = new Bundle();
override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_LOW);
override.putInt(OVERRIDE_RANK, 1);
- mNotificationData.rankingOverrides.put(a.key, override);
+ mNotificationData.rankingOverrides.put(a.getKey(), override);
Notification bN = new Notification.Builder(mContext, "test")
.setStyle(new Notification.MessagingStyle(""))
@@ -567,7 +575,7 @@ public class NotificationDataTest extends SysuiTestCase {
Bundle bOverride = new Bundle();
bOverride.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_LOW);
bOverride.putInt(OVERRIDE_RANK, 2);
- mNotificationData.rankingOverrides.put(b.key, bOverride);
+ mNotificationData.rankingOverrides.put(b.getKey(), bOverride);
assertEquals(-1, mNotificationData.mRankingComparator.compare(a, b));
}
@@ -587,7 +595,7 @@ public class NotificationDataTest extends SysuiTestCase {
Bundle override = new Bundle();
override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_DEFAULT);
- mNotificationData.rankingOverrides.put(entry.key(), override);
+ mNotificationData.rankingOverrides.put(entry.getKey(), override);
entry.setRow(mRow);
mNotificationData.add(entry);
@@ -611,7 +619,7 @@ public class NotificationDataTest extends SysuiTestCase {
Bundle override = new Bundle();
override.putInt(OVERRIDE_IMPORTANCE, IMPORTANCE_LOW);
- mNotificationData.rankingOverrides.put(entry.key(), override);
+ mNotificationData.rankingOverrides.put(entry.getKey(), override);
entry.setRow(mRow);
mNotificationData.add(entry);
@@ -631,8 +639,11 @@ public class NotificationDataTest extends SysuiTestCase {
}
public static class TestableNotificationData extends NotificationData {
- public TestableNotificationData(Context context) {
- super(context);
+ public TestableNotificationData(NotificationSectionsFeatureManager sectionsFeatureManager) {
+ super(
+ sectionsFeatureManager,
+ mock(NotifLog.class),
+ mock(PeopleNotificationIdentifier.class));
}
public static final String OVERRIDE_RANK = "r";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index 24cd056b789c..47c17ad88fcb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -113,7 +113,7 @@ public class NotificationLoggerTest extends SysuiTestCase {
@Test
public void testOnChildLocationsChangedReportsVisibilityChanged() throws Exception {
NotificationVisibility[] newlyVisibleKeys = {
- NotificationVisibility.obtain(mEntry.key, 0, 1, true)
+ NotificationVisibility.obtain(mEntry.getKey(), 0, 1, true)
};
NotificationVisibility[] noLongerVisibleKeys = {};
doAnswer(invocation -> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index c56a168a29d9..5e6c96313d36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -82,7 +82,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
- mNotificationTestHelper = new NotificationTestHelper(mContext);
+ mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
mGroupRow = mNotificationTestHelper.createGroup();
mGroupRow.setHeadsUpAnimatingAwayListener(
animatingAway -> mHeadsUpAnimatingAway = animatingAway);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
index 6d6439532912..444a6e5b4b13 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
@@ -27,6 +27,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -62,7 +63,6 @@ import org.mockito.MockitoAnnotations;
@org.junit.runner.RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
-
private NotificationBlockingHelperManager mBlockingHelperManager;
private NotificationTestHelper mHelper;
@@ -88,7 +88,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
mDependency.injectMockDependency(BubbleController.class);
- mHelper = new NotificationTestHelper(mContext);
+ mHelper = new NotificationTestHelper(mContext, mDependency);
mBlockingHelperManager = new NotificationBlockingHelperManager(mContext);
// By default, have the shade visible/expanded.
@@ -112,7 +112,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
- verify(mEntryManager, times(0)).updateNotifications();
+ verify(mEntryManager, times(0)).updateNotifications(anyString());
}
@Test
@@ -125,7 +125,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
- verify(mEntryManager).updateNotifications();
+ verify(mEntryManager).updateNotifications(anyString());
}
@Test
@@ -267,7 +267,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
- verify(mEntryManager).updateNotifications();
+ verify(mEntryManager).updateNotifications(anyString());
}
@Test
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 ccadcc35f37a..71c2e11f2fc4 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
@@ -77,7 +77,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
.setContentTitle("Title")
.setContentText("Text")
.setStyle(new Notification.BigTextStyle().bigText("big text"));
- ExpandableNotificationRow row = new NotificationTestHelper(mContext).createRow(
+ ExpandableNotificationRow row = new NotificationTestHelper(mContext, mDependency).createRow(
mBuilder.build());
mRow = spy(row);
mNotificationInflater = new NotificationContentInflater(mRow);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index db6b6130ebf1..c7877bb8f4f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -62,6 +62,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -118,9 +119,10 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
mDeviceProvisionedController);
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager);
+ mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
mHandler = Handler.createAsync(mTestableLooper.getLooper());
mContext.putComponent(StatusBar.class, mStatusBar);
- mHelper = new NotificationTestHelper(mContext);
+ mHelper = new NotificationTestHelper(mContext, mDependency);
mGutsManager = new NotificationGutsManager(mContext, mVisualStabilityManager);
mGutsManager.setUpWithPresenter(mPresenter, mStackScroller,
@@ -485,13 +487,13 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
NotificationEntry entry = createTestNotificationRow().getEntry();
mGutsManager.setShouldManageLifetime(entry, true /* shouldManage */);
- assertTrue(entry.key.equals(mGutsManager.mKeyToRemoveOnGutsClosed));
+ assertTrue(entry.getKey().equals(mGutsManager.mKeyToRemoveOnGutsClosed));
}
@Test
public void testSetShouldManageLifetime_setShouldNotManage() {
NotificationEntry entry = createTestNotificationRow().getEntry();
- mGutsManager.mKeyToRemoveOnGutsClosed = entry.key;
+ mGutsManager.mKeyToRemoveOnGutsClosed = entry.getKey();
mGutsManager.setShouldManageLifetime(entry, false /* shouldManage */);
assertNull(mGutsManager.mKeyToRemoveOnGutsClosed);
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 49a64101eb84..d280f185edd3 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
@@ -44,7 +44,7 @@ public class NotificationCustomViewWrapperTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
- mRow = new NotificationTestHelper(mContext).createRow();
+ mRow = new NotificationTestHelper(mContext, mDependency).createRow();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
index 4f844f067566..4f45f680f475 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
@@ -94,7 +94,7 @@ public class NotificationMediaTemplateViewWrapperTest extends SysuiTestCase {
mNotif = builder.build();
assertTrue(mNotif.hasMediaSession());
- mRow = new NotificationTestHelper(mContext).createRow(mNotif);
+ mRow = new NotificationTestHelper(mContext, mDependency).createRow(mNotif);
RemoteViews views = new RemoteViews(mContext.getPackageName(),
com.android.internal.R.layout.notification_template_material_big_media);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
index 546315900275..14e2fded6cdc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
@@ -50,7 +50,7 @@ public class NotificationViewWrapperTest extends SysuiTestCase {
public void setup() throws Exception {
Assert.sMainLooper = TestableLooper.get(this).getLooper();
mView = mock(View.class);
- mRow = new NotificationTestHelper(getContext()).createRow();
+ mRow = new NotificationTestHelper(getContext(), mDependency).createRow();
mNotificationViewWrapper = new TestableNotificationViewWrapper(mContext, mView, mRow);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
index 22d2585130fd..ddd2884ec311 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
@@ -45,7 +45,7 @@ public class NotificationChildrenContainerTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
- mNotificationTestHelper = new NotificationTestHelper(mContext);
+ mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
mGroup = mNotificationTestHelper.createGroup();
mChildrenContainer = mGroup.getChildrenContainer();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index addceb5def6e..34a309f1d80c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -32,10 +32,12 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.util.DeviceConfigProxy;
import org.junit.Assert;
import org.junit.Before;
@@ -64,9 +66,11 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mRoundnessManager = new NotificationRoundnessManager(mBypassController, mContext);
+ mRoundnessManager = new NotificationRoundnessManager(
+ mBypassController,
+ new NotificationSectionsFeatureManager(new DeviceConfigProxy(), mContext));
com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
- NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
+ NotificationTestHelper testHelper = new NotificationTestHelper(getContext(), mDependency);
mFirst = testHelper.createRow();
mFirst.setHeadsUpAnimatingAwayListener(animatingAway
-> mRoundnessManager.onHeadsupAnimatingAwayChanged(mFirst, animatingAway));
@@ -146,7 +150,8 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase {
createSection(mFirst, mSecond),
createSection(null, null)
});
- ExpandableNotificationRow row = new NotificationTestHelper(getContext()).createRow();
+ ExpandableNotificationRow row = new NotificationTestHelper(getContext(), mDependency)
+ .createRow();
NotificationEntry entry = mock(NotificationEntry.class);
when(entry.getRow()).thenReturn(row);
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 56ed0e3a6af3..003d80376c40 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
@@ -44,6 +44,7 @@ import com.android.systemui.ActivityStarterDelegate;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -66,6 +67,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
@Mock private ActivityStarterDelegate mActivityStarterDelegate;
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private ConfigurationController mConfigurationController;
+ @Mock private PeopleHubSectionFooterViewAdapter mPeopleHubAdapter;
private NotificationSectionsManager mSectionsManager;
@@ -77,6 +79,7 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
mActivityStarterDelegate,
mStatusBarStateController,
mConfigurationController,
+ mPeopleHubAdapter,
2);
// Required in order for the header inflation to work properly
when(mNssl.generateLayoutParams(any(AttributeSet.class)))
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 31054260eb15..012ebf728c50 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,8 +55,8 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
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.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShelf;
@@ -65,11 +65,14 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.FooterView;
import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -79,6 +82,7 @@ import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarTest.TestableNotificationEntryManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.util.DeviceConfigProxyFake;
import org.junit.After;
import org.junit.Before;
@@ -144,11 +148,10 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mDependency.injectMockDependency(ShadeController.class);
when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
- mEntryManager = new TestableNotificationEntryManager(mContext);
+ mEntryManager = new TestableNotificationEntryManager(mNotificationData);
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
Dependency.get(InitController.class).executePostInitTasks();
- mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, mHeadsUpManager,
- mNotificationData);
+ mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, mHeadsUpManager);
NotificationShelf notificationShelf = mock(NotificationShelf.class);
@@ -166,7 +169,11 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mock(SysuiStatusBarStateController.class),
mHeadsUpManager,
mKeyguardBypassController,
- new FalsingManagerFake());
+ new FalsingManagerFake(),
+ mock(NotificationLockscreenUserManager.class),
+ mock(NotificationGutsManager.class),
+ new NotificationSectionsFeatureManager(new DeviceConfigProxyFake(), mContext),
+ mock(PeopleHubSectionFooterViewAdapter.class));
mStackScroller = spy(mStackScrollerInternal);
mStackScroller.setShelf(notificationShelf);
mStackScroller.setStatusBar(mBar);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java
deleted file mode 100644
index f614354a7691..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoHideControllerTest.java
+++ /dev/null
@@ -1,119 +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.statusbar.phone;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-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 android.graphics.Rect;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.View;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.Dependency;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.CommandQueue;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/** atest AutoHideControllerTest */
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-@SmallTest
-public class AutoHideControllerTest extends SysuiTestCase {
-
- private AutoHideController mAutoHideController;
-
- private static final int FULL_MASK = 0xffffffff;
-
- @Before
- public void setUp() {
- mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
- mAutoHideController =
- spy(new AutoHideController(mContext, Dependency.get(Dependency.MAIN_HANDLER)));
- mAutoHideController.mDisplayId = DEFAULT_DISPLAY;
- mAutoHideController.mSystemUiVisibility = View.VISIBLE;
- }
-
- @After
- public void tearDown() {
- mAutoHideController = null;
- }
-
- @Test
- public void testSetSystemUiVisibilityEarlyReturnWithDifferentDisplay() {
- mAutoHideController.setSystemUiVisibility(1, 1, 2, 3, 4, null, new Rect(), false);
-
- verify(mAutoHideController, never()).notifySystemUiVisibilityChanged(anyInt());
- }
-
- @Test
- public void testSetSystemUiVisibilityEarlyReturnWithSameVisibility() {
- mAutoHideController
- .setSystemUiVisibility(
- DEFAULT_DISPLAY, View.VISIBLE, 2, 3, 4, null, new Rect(), false);
-
- verify(mAutoHideController, never()).notifySystemUiVisibilityChanged(anyInt());
- }
-
- // Test if status bar unhide status doesn't change without status bar.
- @Test
- public void testSetSystemUiVisibilityWithoutStatusBar() {
- doReturn(false).when(mAutoHideController).hasStatusBar();
- int expectedStatus = View.STATUS_BAR_UNHIDE;
- mAutoHideController.mSystemUiVisibility =
- View.SYSTEM_UI_FLAG_FULLSCREEN | View.STATUS_BAR_UNHIDE;
-
- mAutoHideController.setSystemUiVisibility(
- DEFAULT_DISPLAY, expectedStatus, 2, 3, FULL_MASK, null, new Rect(), false);
-
- assertEquals("System UI visibility should not be changed",
- expectedStatus, mAutoHideController.mSystemUiVisibility);
- verify(mAutoHideController, times(1)).notifySystemUiVisibilityChanged(eq(expectedStatus));
- }
-
- @Test
- public void testSetSystemUiVisibilityWithVisChanged() {
- doReturn(true).when(mAutoHideController).hasStatusBar();
- doReturn(true).when(mAutoHideController).hasNavigationBar();
- mAutoHideController.mSystemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
- | View.STATUS_BAR_UNHIDE
- | View.NAVIGATION_BAR_UNHIDE;
-
- mAutoHideController.setSystemUiVisibility(
- DEFAULT_DISPLAY, View.STATUS_BAR_UNHIDE | View.NAVIGATION_BAR_UNHIDE,
- 2, 3, FULL_MASK, null, new Rect(), false);
-
- int expectedStatus = View.VISIBLE;
- assertEquals(expectedStatus, mAutoHideController.mSystemUiVisibility);
- verify(mAutoHideController).notifySystemUiVisibilityChanged(eq(expectedStatus));
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index fd676111b1da..4a0b3718db6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -34,11 +34,14 @@ import android.os.UserHandle;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import android.testing.TestableResources;
+import com.android.internal.logging.MetricsLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
@@ -70,28 +73,35 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
@Mock
private StatusBar mStatusBar;
@Mock
- private UnlockMethodCache mUnlockMethodCache;
+ private KeyguardStateController mKeyguardStateController;
@Mock
private Handler mHandler;
@Mock
private KeyguardBypassController mKeyguardBypassController;
+ @Mock
+ private DozeParameters mDozeParameters;
+ @Mock
+ private MetricsLogger mMetricsLogger;
private BiometricUnlockController mBiometricUnlockController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ TestableResources res = getContext().getOrCreateTestableResources();
when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
- when(mUnlockMethodCache.isFaceAuthEnabled()).thenReturn(true);
+ when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
when(mKeyguardBypassController.onBiometricAuthenticated(any())).thenReturn(true);
when(mKeyguardBypassController.canPlaySubtleWindowAnimations()).thenReturn(true);
mContext.addMockSystemService(PowerManager.class, mPowerManager);
mDependency.injectTestDependency(NotificationMediaManager.class, mMediaManager);
mDependency.injectTestDependency(StatusBarWindowController.class,
mStatusBarWindowController);
+ res.addOverride(com.android.internal.R.integer.config_wakeUpDelayDoze, 0);
mBiometricUnlockController = new BiometricUnlockController(mContext, mDozeScrimController,
- mKeyguardViewMediator, mScrimController, mStatusBar, mUnlockMethodCache,
- mHandler, mUpdateMonitor, 0 /* wakeUpDelay */, mKeyguardBypassController);
+ mKeyguardViewMediator, mScrimController, mStatusBar, mKeyguardStateController,
+ mHandler, mUpdateMonitor, res.getResources(), mKeyguardBypassController,
+ mDozeParameters, mMetricsLogger);
mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DoubleTapHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DoubleTapHelperTest.java
new file mode 100644
index 000000000000..df1233af6406
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DoubleTapHelperTest.java
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.res.Resources;
+import android.os.SystemClock;
+import android.testing.AndroidTestingRunner;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+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 DoubleTapHelperTest extends SysuiTestCase {
+
+ private DoubleTapHelper mDoubleTapHelper;
+ private int mTouchSlop;
+ private int mDoubleTouchSlop;
+ @Mock private View mView;
+ @Mock private DoubleTapHelper.ActivationListener mActivationListener;
+ @Mock private DoubleTapHelper.DoubleTapListener mDoubleTapListener;
+ @Mock private DoubleTapHelper.SlideBackListener mSlideBackListener;
+ @Mock private DoubleTapHelper.DoubleTapLogListener mDoubleTapLogListener;
+ @Mock private Resources mResources;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ // The double tap slop has to be less than the regular slop, otherwise it has no effect.
+ mDoubleTouchSlop = mTouchSlop - 1;
+ when(mView.getContext()).thenReturn(mContext);
+ when(mView.getResources()).thenReturn(mResources);
+ when(mResources.getDimension(R.dimen.double_tap_slop))
+ .thenReturn((float) mDoubleTouchSlop);
+
+ mDoubleTapHelper = new DoubleTapHelper(mView,
+ mActivationListener,
+ mDoubleTapListener,
+ mSlideBackListener, mDoubleTapLogListener);
+ }
+
+ @Test
+ public void testDoubleTap_success() {
+ long downtimeA = SystemClock.uptimeMillis();
+ long downtimeB = downtimeA + 100;
+
+ MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+ downtimeA,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+ downtimeA + 1,
+ MotionEvent.ACTION_UP,
+ 1,
+ 1,
+ 0);
+ MotionEvent evDownB = MotionEvent.obtain(downtimeB,
+ downtimeB,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpB = MotionEvent.obtain(downtimeB,
+ downtimeB + 1,
+ MotionEvent.ACTION_UP,
+ 1,
+ 1,
+ 0);
+
+ mDoubleTapHelper.onTouchEvent(evDownA);
+ mDoubleTapHelper.onTouchEvent(evUpA);
+ verify(mActivationListener).onActiveChanged(true);
+ verify(mView).postDelayed(any(Runnable.class), anyLong());
+ verify(mDoubleTapLogListener, never()).onDoubleTapLog(anyBoolean(), anyFloat(), anyFloat());
+ verify(mDoubleTapListener, never()).onDoubleTap();
+
+ mDoubleTapHelper.onTouchEvent(evDownB);
+ mDoubleTapHelper.onTouchEvent(evUpB);
+ verify(mDoubleTapLogListener).onDoubleTapLog(true, 0, 0);
+ verify(mDoubleTapListener).onDoubleTap();
+
+ evDownA.recycle();
+ evUpA.recycle();
+ evDownB.recycle();
+ evUpB.recycle();
+ }
+
+ @Test
+ public void testSingleTap_timeout() {
+ long downtimeA = SystemClock.uptimeMillis();
+
+ MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+ downtimeA,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+ downtimeA + 1,
+ MotionEvent.ACTION_UP,
+ 1,
+ 1,
+ 0);
+
+ ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+ mDoubleTapHelper.onTouchEvent(evDownA);
+ mDoubleTapHelper.onTouchEvent(evUpA);
+ verify(mActivationListener).onActiveChanged(true);
+ verify(mView).postDelayed(runnableCaptor.capture(), anyLong());
+ runnableCaptor.getValue().run();
+ verify(mActivationListener).onActiveChanged(true);
+
+ evDownA.recycle();
+ evUpA.recycle();
+ }
+
+ @Test
+ public void testSingleTap_slop() {
+ long downtimeA = SystemClock.uptimeMillis();
+
+ MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+ downtimeA,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+ downtimeA + 1,
+ MotionEvent.ACTION_UP,
+ 1 + mTouchSlop,
+ 1,
+ 0);
+
+ ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+ mDoubleTapHelper.onTouchEvent(evDownA);
+ mDoubleTapHelper.onTouchEvent(evUpA);
+ verify(mActivationListener, never()).onActiveChanged(true);
+ verify(mDoubleTapListener, never()).onDoubleTap();
+
+ evDownA.recycle();
+ evUpA.recycle();
+ }
+
+ @Test
+ public void testDoubleTap_slop() {
+ long downtimeA = SystemClock.uptimeMillis();
+ long downtimeB = downtimeA + 100;
+
+ MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+ downtimeA,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+ downtimeA + 1,
+ MotionEvent.ACTION_UP,
+ 1,
+ 1,
+ 0);
+ MotionEvent evDownB = MotionEvent.obtain(downtimeB,
+ downtimeB,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpB = MotionEvent.obtain(downtimeB,
+ downtimeB + 1,
+ MotionEvent.ACTION_UP,
+ 1,
+ 1 + mDoubleTouchSlop,
+ 0);
+
+ mDoubleTapHelper.onTouchEvent(evDownA);
+ mDoubleTapHelper.onTouchEvent(evUpA);
+ verify(mActivationListener).onActiveChanged(true);
+ verify(mView).postDelayed(any(Runnable.class), anyLong());
+
+ mDoubleTapHelper.onTouchEvent(evDownB);
+ mDoubleTapHelper.onTouchEvent(evUpB);
+ verify(mDoubleTapLogListener).onDoubleTapLog(false, 0, mDoubleTouchSlop);
+ verify(mActivationListener).onActiveChanged(false);
+ verify(mDoubleTapListener, never()).onDoubleTap();
+
+ evDownA.recycle();
+ evUpA.recycle();
+ evDownB.recycle();
+ evUpB.recycle();
+ }
+
+ @Test
+ public void testSlideBack() {
+ long downtimeA = SystemClock.uptimeMillis();
+ long downtimeB = downtimeA + 100;
+
+ MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+ downtimeA,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+ downtimeA + 1,
+ MotionEvent.ACTION_UP,
+ 1,
+ 1,
+ 0);
+ MotionEvent evDownB = MotionEvent.obtain(downtimeB,
+ downtimeB,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpB = MotionEvent.obtain(downtimeB,
+ downtimeB + 1,
+ MotionEvent.ACTION_UP,
+ 1,
+ 1,
+ 0);
+
+ when(mSlideBackListener.onSlideBack()).thenReturn(true);
+
+ mDoubleTapHelper.onTouchEvent(evDownA);
+ mDoubleTapHelper.onTouchEvent(evUpA);
+ verify(mActivationListener, never()).onActiveChanged(true);
+ verify(mActivationListener, never()).onActiveChanged(false);
+ verify(mDoubleTapListener, never()).onDoubleTap();
+ mDoubleTapHelper.onTouchEvent(evDownB);
+ mDoubleTapHelper.onTouchEvent(evUpB);
+ verify(mActivationListener, never()).onActiveChanged(true);
+ verify(mActivationListener, never()).onActiveChanged(false);
+ verify(mDoubleTapListener, never()).onDoubleTap();
+
+ evDownA.recycle();
+ evUpA.recycle();
+ evDownB.recycle();
+ evUpB.recycle();
+ }
+
+
+ @Test
+ public void testMoreThanTwoTaps() {
+ long downtimeA = SystemClock.uptimeMillis();
+ long downtimeB = downtimeA + 100;
+ long downtimeC = downtimeB + 100;
+ long downtimeD = downtimeC + 100;
+
+ MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+ downtimeA,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+ downtimeA + 1,
+ MotionEvent.ACTION_UP,
+ 1,
+ 1,
+ 0);
+ MotionEvent evDownB = MotionEvent.obtain(downtimeB,
+ downtimeB,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpB = MotionEvent.obtain(downtimeB,
+ downtimeB + 1,
+ MotionEvent.ACTION_UP,
+ 1,
+ 1,
+ 0);
+ MotionEvent evDownC = MotionEvent.obtain(downtimeC,
+ downtimeC,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpC = MotionEvent.obtain(downtimeC,
+ downtimeC + 1,
+ MotionEvent.ACTION_UP,
+ 1,
+ 1,
+ 0);
+ MotionEvent evDownD = MotionEvent.obtain(downtimeD,
+ downtimeD,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpD = MotionEvent.obtain(downtimeD,
+ downtimeD + 1,
+ MotionEvent.ACTION_UP,
+ 1,
+ 1,
+ 0);
+
+ mDoubleTapHelper.onTouchEvent(evDownA);
+ mDoubleTapHelper.onTouchEvent(evUpA);
+ verify(mActivationListener).onActiveChanged(true);
+ verify(mView).postDelayed(any(Runnable.class), anyLong());
+ verify(mDoubleTapLogListener, never()).onDoubleTapLog(anyBoolean(), anyFloat(), anyFloat());
+ verify(mDoubleTapListener, never()).onDoubleTap();
+
+ mDoubleTapHelper.onTouchEvent(evDownB);
+ mDoubleTapHelper.onTouchEvent(evUpB);
+ verify(mDoubleTapLogListener).onDoubleTapLog(true, 0, 0);
+ verify(mDoubleTapListener).onDoubleTap();
+
+ reset(mView);
+ reset(mActivationListener);
+ reset(mDoubleTapLogListener);
+ reset(mDoubleTapListener);
+
+ mDoubleTapHelper.onTouchEvent(evDownC);
+ mDoubleTapHelper.onTouchEvent(evUpC);
+ verify(mActivationListener).onActiveChanged(true);
+ verify(mView).postDelayed(any(Runnable.class), anyLong());
+ verify(mDoubleTapLogListener, never()).onDoubleTapLog(anyBoolean(), anyFloat(), anyFloat());
+ verify(mDoubleTapListener, never()).onDoubleTap();
+
+ mDoubleTapHelper.onTouchEvent(evDownD);
+ mDoubleTapHelper.onTouchEvent(evUpD);
+ verify(mDoubleTapLogListener).onDoubleTapLog(true, 0, 0);
+ verify(mDoubleTapListener).onDoubleTap();
+
+ evDownA.recycle();
+ evUpA.recycle();
+ evDownB.recycle();
+ evUpB.recycle();
+ }
+}
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 60050b182e0f..debc840394b5 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
@@ -17,68 +17,73 @@
package com.android.systemui.statusbar.phone;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
-import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.display.AmbientDisplayConfiguration;
import android.os.PowerManager;
import android.test.suitebuilder.annotation.SmallTest;
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.tuner.TunerService;
import org.junit.Assert;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DozeParametersTest extends SysuiTestCase {
+ private DozeParameters mDozeParameters;
+
+ @Mock Resources mResources;
+ @Mock private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
+ @Mock private AlwaysOnDisplayPolicy mAlwaysOnDisplayPolicy;
+ @Mock private PowerManager mPowerManager;
+ @Mock private TunerService mTunerService;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mDozeParameters = new DozeParameters(
+ mResources,
+ mAmbientDisplayConfiguration,
+ mAlwaysOnDisplayPolicy,
+ mPowerManager,
+ mTunerService
+ );
+ }
@Test
public void test_setControlScreenOffAnimation_setsDozeAfterScreenOff_false() {
- TestableDozeParameters dozeParameters = new TestableDozeParameters(getContext());
- PowerManager mockedPowerManager = dozeParameters.getPowerManager();
- dozeParameters.setControlScreenOffAnimation(true);
- reset(mockedPowerManager);
- dozeParameters.setControlScreenOffAnimation(false);
- verify(mockedPowerManager).setDozeAfterScreenOff(eq(true));
+ mDozeParameters.setControlScreenOffAnimation(true);
+ reset(mPowerManager);
+ mDozeParameters.setControlScreenOffAnimation(false);
+ verify(mPowerManager).setDozeAfterScreenOff(eq(true));
}
@Test
public void test_setControlScreenOffAnimation_setsDozeAfterScreenOff_true() {
- TestableDozeParameters dozeParameters = new TestableDozeParameters(getContext());
- PowerManager mockedPowerManager = dozeParameters.getPowerManager();
- dozeParameters.setControlScreenOffAnimation(false);
- reset(mockedPowerManager);
- dozeParameters.setControlScreenOffAnimation(true);
- verify(dozeParameters.getPowerManager()).setDozeAfterScreenOff(eq(false));
+ mDozeParameters.setControlScreenOffAnimation(false);
+ reset(mPowerManager);
+ mDozeParameters.setControlScreenOffAnimation(true);
+ verify(mPowerManager).setDozeAfterScreenOff(eq(false));
}
@Test
public void test_getWallpaperAodDuration_when_shouldControlScreenOff() {
- TestableDozeParameters dozeParameters = new TestableDozeParameters(getContext());
- dozeParameters.setControlScreenOffAnimation(true);
- Assert.assertEquals("wallpaper hides faster when controlling screen off",
- dozeParameters.getWallpaperAodDuration(),
+ mDozeParameters.setControlScreenOffAnimation(true);
+ Assert.assertEquals(
+ "wallpaper hides faster when controlling screen off",
+ mDozeParameters.getWallpaperAodDuration(),
DozeScreenState.ENTER_DOZE_HIDE_WALLPAPER_DELAY);
}
-
- private class TestableDozeParameters extends DozeParameters {
- private PowerManager mPowerManager;
-
- TestableDozeParameters(Context context) {
- super(context);
- mPowerManager = mock(PowerManager.class);
- }
-
- @Override
- protected PowerManager getPowerManager() {
- return mPowerManager;
- }
- }
-
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
index 20c739ff579e..1ce336e5f37d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
@@ -27,6 +27,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.doze.DozeHost;
+import com.android.systemui.doze.DozeLog;
import org.junit.Before;
import org.junit.Test;
@@ -41,12 +42,14 @@ public class DozeScrimControllerTest extends SysuiTestCase {
@Mock
private DozeParameters mDozeParameters;
+ @Mock
+ private DozeLog mDozeLog;
private DozeScrimController mDozeScrimController;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mDozeScrimController = new DozeScrimController(mDozeParameters);
+ mDozeScrimController = new DozeScrimController(mDozeParameters, mDozeLog);
mDozeScrimController.setDozing(true);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
new file mode 100644
index 000000000000..b05172c6d7c2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+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.when;
+
+import android.os.PowerManager;
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.doze.DozeEvent;
+import com.android.systemui.doze.DozeHost;
+import com.android.systemui.doze.DozeLog;
+import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateControllerImpl;
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+
+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 java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+
+import dagger.Lazy;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DozeServiceHostTest extends SysuiTestCase {
+
+ private DozeServiceHost mDozeServiceHost;
+
+ @Mock private HeadsUpManagerPhone mHeadsUpManager;
+ @Mock private ScrimController mScrimController;
+ @Mock private DozeScrimController mDozeScrimController;
+ @Mock private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
+ @Mock private VisualStabilityManager mVisualStabilityManager;
+ @Mock private KeyguardViewMediator mKeyguardViewMediator;
+ @Mock private StatusBarStateControllerImpl mStatusBarStateController;
+ @Mock private BatteryController mBatteryController;
+ @Mock private DeviceProvisionedController mDeviceProvisionedController;
+ @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock private AssistManager mAssistManager;
+ @Mock private DozeLog mDozeLog;
+ @Mock private PulseExpansionHandler mPulseExpansionHandler;
+ @Mock private NotificationWakeUpCoordinator mNotificationWakeUpCoordinator;
+ @Mock private StatusBarWindowController mStatusBarWindowController;
+ @Mock private PowerManager mPowerManager;
+ @Mock private WakefulnessLifecycle mWakefullnessLifecycle;
+ @Mock private StatusBar mStatusBar;
+ @Mock private NotificationIconAreaController mNotificationIconAreaController;
+ @Mock private StatusBarWindowViewController mStatusBarWindowViewController;
+ @Mock private StatusBarWindowView mStatusBarWindow;
+ @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ @Mock private NotificationPanelView mNotificationPanel;
+ @Mock private View mAmbientIndicationContainer;
+ @Mock private BiometricUnlockController mBiometricUnlockController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mBiometricUnlockControllerLazy.get()).thenReturn(mBiometricUnlockController);
+ mDozeServiceHost = new DozeServiceHost(mDozeLog, mPowerManager, mWakefullnessLifecycle,
+ mStatusBarStateController, mDeviceProvisionedController, mHeadsUpManager,
+ mBatteryController, mScrimController, mBiometricUnlockControllerLazy,
+ mKeyguardViewMediator, mAssistManager, mDozeScrimController, mKeyguardUpdateMonitor,
+ mVisualStabilityManager, mPulseExpansionHandler, mStatusBarWindowController,
+ mNotificationWakeUpCoordinator);
+
+ mDozeServiceHost.initialize(mStatusBar, mNotificationIconAreaController,
+ mStatusBarWindowViewController, mStatusBarWindow, mStatusBarKeyguardViewManager,
+ mNotificationPanel, mAmbientIndicationContainer);
+ }
+
+ @Test
+ public void testStartStopDozing() {
+ when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
+ when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true);
+
+ assertFalse(mDozeServiceHost.getDozingRequested());
+
+ mDozeServiceHost.startDozing();
+ verify(mStatusBarStateController).setIsDozing(eq(true));
+ verify(mStatusBar).updateIsKeyguard();
+
+ mDozeServiceHost.stopDozing();
+ verify(mStatusBarStateController).setIsDozing(eq(false));
+ }
+
+
+ @Test
+ public void testPulseWhileDozing_updatesScrimController() {
+ mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
+ mStatusBar.showKeyguardImpl();
+
+ // Keep track of callback to be able to stop the pulse
+// DozeHost.PulseCallback[] pulseCallback = new DozeHost.PulseCallback[1];
+// doAnswer(invocation -> {
+// pulseCallback[0] = invocation.getArgument(0);
+// return null;
+// }).when(mDozeScrimController).pulse(any(), anyInt());
+
+ // Starting a pulse should change the scrim controller to the pulsing state
+ mDozeServiceHost.pulseWhileDozing(new DozeHost.PulseCallback() {
+ @Override
+ public void onPulseStarted() {
+ }
+
+ @Override
+ public void onPulseFinished() {
+ }
+ }, DozeEvent.PULSE_REASON_NOTIFICATION);
+
+ ArgumentCaptor<DozeHost.PulseCallback> pulseCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(DozeHost.PulseCallback.class);
+
+ verify(mDozeScrimController).pulse(
+ pulseCallbackArgumentCaptor.capture(), eq(DozeEvent.PULSE_REASON_NOTIFICATION));
+ verify(mStatusBar).updateScrimController();
+ reset(mStatusBar);
+
+ pulseCallbackArgumentCaptor.getValue().onPulseFinished();
+ assertFalse(mDozeScrimController.isPulsing());
+ verify(mStatusBar).updateScrimController();
+ }
+
+
+ @Test
+ public void testPulseWhileDozingWithDockingReason_suppressWakeUpGesture() {
+ // Keep track of callback to be able to stop the pulse
+ final DozeHost.PulseCallback[] pulseCallback = new DozeHost.PulseCallback[1];
+ doAnswer(invocation -> {
+ pulseCallback[0] = invocation.getArgument(0);
+ return null;
+ }).when(mDozeScrimController).pulse(any(), anyInt());
+
+ // Starting a pulse while docking should suppress wakeup gesture
+ mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class),
+ DozeEvent.PULSE_REASON_DOCKING);
+ verify(mStatusBarWindowViewController).suppressWakeUpGesture(eq(true));
+
+ // Ending a pulse should restore wakeup gesture
+ pulseCallback[0].onPulseFinished();
+ verify(mStatusBarWindowViewController).suppressWakeUpGesture(eq(false));
+ }
+
+ @Test
+ public void testPulseWhileDozing_notifyAuthInterrupt() {
+ HashSet<Integer> reasonsWantingAuth = new HashSet<>(
+ Collections.singletonList(DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN));
+ HashSet<Integer> reasonsSkippingAuth = new HashSet<>(
+ Arrays.asList(DozeEvent.PULSE_REASON_INTENT,
+ DozeEvent.PULSE_REASON_NOTIFICATION,
+ DozeEvent.PULSE_REASON_SENSOR_SIGMOTION,
+ DozeEvent.REASON_SENSOR_PICKUP,
+ DozeEvent.REASON_SENSOR_DOUBLE_TAP,
+ DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS,
+ DozeEvent.PULSE_REASON_DOCKING,
+ DozeEvent.REASON_SENSOR_WAKE_UP,
+ DozeEvent.REASON_SENSOR_TAP));
+ HashSet<Integer> reasonsThatDontPulse = new HashSet<>(
+ Arrays.asList(DozeEvent.REASON_SENSOR_PICKUP,
+ DozeEvent.REASON_SENSOR_DOUBLE_TAP,
+ DozeEvent.REASON_SENSOR_TAP));
+
+ doAnswer(invocation -> {
+ DozeHost.PulseCallback callback = invocation.getArgument(0);
+ callback.onPulseStarted();
+ return null;
+ }).when(mDozeScrimController).pulse(any(), anyInt());
+
+ mDozeServiceHost.mWakeLockScreenPerformsAuth = true;
+ for (int i = 0; i < DozeEvent.TOTAL_REASONS; i++) {
+ reset(mKeyguardUpdateMonitor);
+ mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class), i);
+ if (reasonsWantingAuth.contains(i)) {
+ verify(mKeyguardUpdateMonitor).onAuthInterruptDetected(eq(true));
+ } else if (reasonsSkippingAuth.contains(i) || reasonsThatDontPulse.contains(i)) {
+ verify(mKeyguardUpdateMonitor, never()).onAuthInterruptDetected(eq(true));
+ } else {
+ throw new AssertionError("Reason " + i + " isn't specified as wanting or skipping"
+ + " passive auth. Please consider how this pulse reason should behave.");
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index a38094da3e1c..0216d2effef3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -38,6 +38,7 @@ import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Assert;
import org.junit.Before;
@@ -61,11 +62,12 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
private StatusBarStateController mStatusbarStateController;
private KeyguardBypassController mBypassController;
private NotificationWakeUpCoordinator mWakeUpCoordinator;
+ private KeyguardStateController mKeyguardStateController;
@Before
public void setUp() throws Exception {
com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
- NotificationTestHelper testHelper = new NotificationTestHelper(getContext());
+ NotificationTestHelper testHelper = new NotificationTestHelper(getContext(), mDependency);
mFirst = testHelper.createRow();
mDependency.injectTestDependency(DarkIconDispatcher.class, mDarkIconDispatcher);
mHeadsUpStatusBarView = new HeadsUpStatusBarView(mContext, mock(View.class),
@@ -75,12 +77,14 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
mStatusbarStateController = mock(StatusBarStateController.class);
mBypassController = mock(KeyguardBypassController.class);
mWakeUpCoordinator = mock(NotificationWakeUpCoordinator.class);
+ mKeyguardStateController = mock(KeyguardStateController.class);
mHeadsUpAppearanceController = new HeadsUpAppearanceController(
mock(NotificationIconAreaController.class),
mHeadsUpManager,
mStatusbarStateController,
mBypassController,
mWakeUpCoordinator,
+ mKeyguardStateController,
mHeadsUpStatusBarView,
mStackScroller,
mPanelView,
@@ -158,6 +162,7 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
mStatusbarStateController,
mBypassController,
mWakeUpCoordinator,
+ mKeyguardStateController,
mHeadsUpStatusBarView,
mStackScroller,
mPanelView,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index ef9665a80848..2068f7ccc3ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -29,6 +29,7 @@ import android.view.View;
import androidx.test.filters.SmallTest;
+import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.AlertingNotificationManagerTest;
@@ -36,6 +37,7 @@ import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import org.junit.Before;
import org.junit.Rule;
@@ -85,6 +87,9 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest {
when(accessibilityMgr.getRecommendedTimeoutMillis(anyInt(), anyInt()))
.thenReturn(TEST_AUTO_DISMISS_TIME);
when(mVSManager.isReorderingAllowed()).thenReturn(true);
+ mDependency.injectMockDependency(BubbleController.class);
+ mDependency.injectMockDependency(StatusBarWindowController.class);
+ mDependency.injectMockDependency(ConfigurationController.class);
mHeadsUpManager = new TestableHeadsUpManagerPhone(mContext, mStatusBarWindowView,
mGroupManager, mBar, mVSManager, mStatusBarStateController, mBypassController);
super.setUp();
@@ -97,27 +102,27 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest {
mHeadsUpManager.snooze();
- assertTrue(mHeadsUpManager.isSnoozed(mEntry.notification.getPackageName()));
+ assertTrue(mHeadsUpManager.isSnoozed(mEntry.getSbn().getPackageName()));
}
@Test
public void testSwipedOutNotification() {
mHeadsUpManager.showNotification(mEntry);
- mHeadsUpManager.addSwipedOutNotification(mEntry.key);
+ mHeadsUpManager.addSwipedOutNotification(mEntry.getKey());
// Remove should succeed because the notification is swiped out
- mHeadsUpManager.removeNotification(mEntry.key, false /* releaseImmediately */);
+ mHeadsUpManager.removeNotification(mEntry.getKey(), false /* releaseImmediately */);
- assertFalse(mHeadsUpManager.isAlerting(mEntry.key));
+ assertFalse(mHeadsUpManager.isAlerting(mEntry.getKey()));
}
@Test
public void testCanRemoveImmediately_swipedOut() {
mHeadsUpManager.showNotification(mEntry);
- mHeadsUpManager.addSwipedOutNotification(mEntry.key);
+ mHeadsUpManager.addSwipedOutNotification(mEntry.getKey());
// Notification is swiped so it can be immediately removed.
- assertTrue(mHeadsUpManager.canRemoveImmediately(mEntry.key));
+ assertTrue(mHeadsUpManager.canRemoveImmediately(mEntry.getKey()));
}
@Test
@@ -130,7 +135,7 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest {
mHeadsUpManager.showNotification(laterEntry);
// Notification is "behind" a higher priority notification so we can remove it immediately.
- assertTrue(mHeadsUpManager.canRemoveImmediately(mEntry.key));
+ assertTrue(mHeadsUpManager.canRemoveImmediately(mEntry.getKey()));
}
@@ -138,7 +143,7 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest {
public void testExtendHeadsUp() {
mHeadsUpManager.showNotification(mEntry);
Runnable pastNormalTimeRunnable =
- () -> mLivesPastNormalTime = mHeadsUpManager.isAlerting(mEntry.key);
+ () -> mLivesPastNormalTime = mHeadsUpManager.isAlerting(mEntry.getKey());
mTestHandler.postDelayed(pastNormalTimeRunnable,
TEST_AUTO_DISMISS_TIME + mHeadsUpManager.mExtensionTime / 2);
mTestHandler.postDelayed(TEST_TIMEOUT_RUNNABLE, TEST_TIMEOUT_TIME);
@@ -150,6 +155,6 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest {
assertFalse("Test timed out", mTimedOut);
assertTrue("Pulse was not extended", mLivesPastNormalTime);
- assertFalse(mHeadsUpManager.isAlerting(mEntry.key));
+ assertFalse(mHeadsUpManager.isAlerting(mEntry.getKey()));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
index 688a6fbc6d78..2b091f297184 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
@@ -1,14 +1,11 @@
package com.android.systemui.statusbar.phone
-import androidx.test.filters.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.LayoutInflater
-
+import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.KeyguardIndicationController
-
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -17,7 +14,7 @@ import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
class KeyguardBottomAreaTest : SysuiTestCase() {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index c51263f71905..67b8e07f2bec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -52,6 +52,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Assert;
import org.junit.Before;
@@ -83,11 +84,13 @@ public class KeyguardBouncerTest extends SysuiTestCase {
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
- private UnlockMethodCache mUnlockMethodCache;
+ private KeyguardStateController mKeyguardStateController;
@Mock
private KeyguardBypassController mKeyguardBypassController;
@Mock
private Handler mHandler;
+ @Mock
+ private KeyguardSecurityModel mKeyguardSecurityModel;
private KeyguardBouncer mBouncer;
@@ -96,13 +99,17 @@ public class KeyguardBouncerTest extends SysuiTestCase {
com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
MockitoAnnotations.initMocks(this);
mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor);
+ mDependency.injectTestDependency(KeyguardSecurityModel.class, mKeyguardSecurityModel);
+ mDependency.injectMockDependency(KeyguardStateController.class);
+ when(mKeyguardSecurityModel.getSecurityMode(anyInt()))
+ .thenReturn(KeyguardSecurityModel.SecurityMode.None);
DejankUtils.setImmediate(true);
final ViewGroup container = new FrameLayout(getContext());
when(mKeyguardHostView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
when(mKeyguardHostView.getHeight()).thenReturn(500);
mBouncer = new KeyguardBouncer(getContext(), mViewMediatorCallback,
mLockPatternUtils, container, mDismissCallbackRegistry, mFalsingManager,
- mExpansionCallback, mUnlockMethodCache, mKeyguardUpdateMonitor,
+ mExpansionCallback, mKeyguardStateController, mKeyguardUpdateMonitor,
mKeyguardBypassController, mHandler) {
@Override
protected void inflateView() {
@@ -305,14 +312,6 @@ public class KeyguardBouncerTest extends SysuiTestCase {
}
@Test
- public void testNeedsFullscreenBouncer_asksKeyguardView() {
- mBouncer.ensureView();
- mBouncer.needsFullscreenBouncer();
- verify(mKeyguardHostView).getSecurityMode();
- verify(mKeyguardHostView, never()).getCurrentSecurityMode();
- }
-
- @Test
public void testIsFullscreenBouncer_asksKeyguardView() {
mBouncer.ensureView();
mBouncer.isFullscreenBouncer();
@@ -384,7 +383,7 @@ public class KeyguardBouncerTest extends SysuiTestCase {
@Test
public void testShow_delaysIfFaceAuthIsRunning() {
- when(mUnlockMethodCache.isFaceAuthEnabled()).thenReturn(true);
+ when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
mBouncer.show(true /* reset */);
ArgumentCaptor<Runnable> showRunnable = ArgumentCaptor.forClass(Runnable.class);
@@ -397,7 +396,7 @@ public class KeyguardBouncerTest extends SysuiTestCase {
@Test
public void testShow_delaysIfFaceAuthIsRunning_unlessBypass() {
- when(mUnlockMethodCache.isFaceAuthEnabled()).thenReturn(true);
+ when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
mBouncer.show(true /* reset */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
new file mode 100644
index 000000000000..6260d531a290
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_TOP_BAR;
+
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.view.AppearanceRegion;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.BatteryController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class LightBarControllerTest extends SysuiTestCase {
+
+ private LightBarTransitionsController mLightBarTransitionsController;
+ private SysuiDarkIconDispatcher mStatusBarIconController;
+ private LightBarController mLightBarController;
+
+ @Before
+ public void setup() {
+ mStatusBarIconController = mock(SysuiDarkIconDispatcher.class);
+ mLightBarTransitionsController = mock(LightBarTransitionsController.class);
+ when(mStatusBarIconController.getTransitionsController()).thenReturn(
+ mLightBarTransitionsController);
+ mLightBarController = new LightBarController(mContext, mStatusBarIconController,
+ mock(BatteryController.class));
+ }
+
+ @Test
+ public void testOnStatusBarAppearanceChanged_multipleStacks_allStacksLight() {
+ final Rect firstBounds = new Rect(0, 0, 1, 1);
+ final Rect secondBounds = new Rect(1, 0, 2, 1);
+ final AppearanceRegion[] appearanceRegions = new AppearanceRegion[]{
+ new AppearanceRegion(APPEARANCE_LIGHT_TOP_BAR, firstBounds),
+ new AppearanceRegion(APPEARANCE_LIGHT_TOP_BAR, secondBounds)
+ };
+ mLightBarController.onStatusBarAppearanceChanged(
+ appearanceRegions, true /* sbModeChanged */, MODE_TRANSPARENT,
+ false /* navbarColorManagedByIme */);
+ verify(mStatusBarIconController).setIconsDarkArea(eq(null));
+ verify(mLightBarTransitionsController).setIconsDark(eq(true), anyBoolean());
+ }
+
+ @Test
+ public void testOnStatusBarAppearanceChanged_multipleStacks_oneStackLightOneStackDark() {
+ final Rect firstBounds = new Rect(0, 0, 1, 1);
+ final Rect secondBounds = new Rect(1, 0, 2, 1);
+ final AppearanceRegion[] appearanceRegions = new AppearanceRegion[]{
+ new AppearanceRegion(APPEARANCE_LIGHT_TOP_BAR, firstBounds),
+ new AppearanceRegion(0 /* appearance */, secondBounds)
+ };
+ mLightBarController.onStatusBarAppearanceChanged(
+ appearanceRegions, true /* sbModeChanged */, MODE_TRANSPARENT,
+ false /* navbarColorManagedByIme */);
+ verify(mStatusBarIconController).setIconsDarkArea(eq(firstBounds));
+ verify(mLightBarTransitionsController).setIconsDark(eq(true), anyBoolean());
+ }
+
+ @Test
+ public void testOnStatusBarAppearanceChanged_multipleStacks_oneStackDarkOneStackLight() {
+ final Rect firstBounds = new Rect(0, 0, 1, 1);
+ final Rect secondBounds = new Rect(1, 0, 2, 1);
+ final AppearanceRegion[] appearanceRegions = new AppearanceRegion[]{
+ new AppearanceRegion(0 /* appearance */, firstBounds),
+ new AppearanceRegion(APPEARANCE_LIGHT_TOP_BAR, secondBounds)
+ };
+ mLightBarController.onStatusBarAppearanceChanged(
+ appearanceRegions, true /* sbModeChanged */, MODE_TRANSPARENT,
+ false /* navbarColorManagedByIme */);
+ verify(mStatusBarIconController).setIconsDarkArea(eq(secondBounds));
+ verify(mLightBarTransitionsController).setIconsDark(eq(true), anyBoolean());
+ }
+
+ @Test
+ public void testOnStatusBarAppearanceChanged_multipleStacks_allStacksDark() {
+ final Rect firstBounds = new Rect(0, 0, 1, 1);
+ final Rect secondBounds = new Rect(1, 0, 2, 1);
+ final AppearanceRegion[] appearanceRegions = new AppearanceRegion[]{
+ new AppearanceRegion(0 /* appearance */, firstBounds),
+ new AppearanceRegion(0 /* appearance */, secondBounds)
+ };
+ mLightBarController.onStatusBarAppearanceChanged(
+ appearanceRegions, true /* sbModeChanged */, MODE_TRANSPARENT,
+ false /* navbarColorManagedByIme */);
+ verify(mLightBarTransitionsController).setIconsDark(eq(false), anyBoolean());
+ }
+
+ @Test
+ public void testOnStatusBarAppearanceChanged_singleStack_light() {
+ final AppearanceRegion[] appearanceRegions = new AppearanceRegion[]{
+ new AppearanceRegion(APPEARANCE_LIGHT_TOP_BAR, new Rect(0, 0, 1, 1))
+ };
+ mLightBarController.onStatusBarAppearanceChanged(
+ appearanceRegions, true /* sbModeChanged */, MODE_TRANSPARENT,
+ false /* navbarColorManagedByIme */);
+ verify(mStatusBarIconController).setIconsDarkArea(eq(null));
+ verify(mLightBarTransitionsController).setIconsDark(eq(true), anyBoolean());
+ }
+
+ @Test
+ public void testOnStatusBarAppearanceChanged_singleStack_dark() {
+ final AppearanceRegion[] appearanceRegions = new AppearanceRegion[]{
+ new AppearanceRegion(0, new Rect(0, 0, 1, 1))
+ };
+ mLightBarController.onStatusBarAppearanceChanged(
+ appearanceRegions, true /* sbModeChanged */, MODE_TRANSPARENT,
+ false /* navbarColorManagedByIme */);
+ verify(mLightBarTransitionsController).setIconsDark(eq(false), anyBoolean());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
index 93fdce173f12..b1580eedc43c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
@@ -28,8 +28,10 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.LightBarTransitionsController.DarkIntensityApplier;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
@@ -50,6 +52,8 @@ public class LightBarTransitionsControllerTest extends SysuiTestCase {
public void setup() {
MockitoAnnotations.initMocks(this);
mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
+ mDependency.injectMockDependency(KeyguardStateController.class);
+ mDependency.injectMockDependency(StatusBarStateController.class);
mLightBarTransitionsController = new LightBarTransitionsController(mContext, mApplier);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java
index 81e8abf5a058..098a69f5a897 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarButtonTest.java
@@ -35,7 +35,10 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.After;
import org.junit.Before;
@@ -63,6 +66,9 @@ public class NavigationBarButtonTest extends SysuiTestCase {
(SysuiTestableContext) mContext.createDisplayContext(display);
context.putComponent(CommandQueue.class, mock(CommandQueue.class));
+ mDependency.injectMockDependency(AssistManager.class);
+ mDependency.injectMockDependency(OverviewProxyService.class);
+ mDependency.injectMockDependency(KeyguardStateController.class);
mNavBar = new NavigationBarView(context, null);
}
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 be69f5f8a844..6433376cd2a9 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
@@ -33,6 +33,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.assist.AssistManager;
import com.android.systemui.statusbar.policy.KeyButtonDrawable;
import org.junit.Before;
@@ -60,6 +61,8 @@ public class NavigationBarContextTest extends SysuiTestCase {
@Before
public void setup() {
+ mDependency.injectMockDependency(AssistManager.class);
+
mGroup = new ContextualButtonGroup(GROUP_ID);
mBtn0 = new ContextualButton(BUTTON_0_ID, ICON_RES_ID);
mBtn1 = new ContextualButton(BUTTON_1_ID, ICON_RES_ID);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
index 33d3ac848f0f..237f6ac0f420 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
@@ -24,9 +24,11 @@ import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.LayoutRes;
@@ -34,11 +36,14 @@ import android.annotation.Nullable;
import android.app.Fragment;
import android.app.FragmentController;
import android.app.FragmentHostCallback;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.IntentFilter;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
import android.testing.LeakCheck.Tracker;
import android.testing.TestableLooper;
@@ -58,6 +63,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.SysuiTestableContext;
import com.android.systemui.assist.AssistManager;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
@@ -70,9 +76,11 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController;
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()
+@RunWithLooper(setAsMainLooper = true)
@SmallTest
public class NavigationBarFragmentTest extends SysuiBaseFragmentTest {
private static final int EXTERNAL_DISPLAY_ID = 2;
@@ -85,6 +93,8 @@ public class NavigationBarFragmentTest extends SysuiBaseFragmentTest {
private OverviewProxyService mOverviewProxyService;
private CommandQueue mCommandQueue;
private SysUiState mMockSysUiState;
+ @Mock
+ private BroadcastDispatcher mBroadcastDispatcher;
private AccessibilityManagerWrapper mAccessibilityWrapper =
new AccessibilityManagerWrapper(mContext) {
@@ -112,6 +122,8 @@ public class NavigationBarFragmentTest extends SysuiBaseFragmentTest {
@Before
public void setupFragment() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
setupSysuiDependency();
createRootView();
mOverviewProxyService =
@@ -177,6 +189,18 @@ public class NavigationBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
+ public void testRegisteredWithDispatcher() {
+ mFragments.dispatchResume();
+ processAllMessages();
+
+ verify(mBroadcastDispatcher).registerReceiver(
+ any(BroadcastReceiver.class),
+ any(IntentFilter.class),
+ any(Handler.class),
+ any(UserHandle.class));
+ }
+
+ @Test
public void testSetImeWindowStatusWhenImeSwitchOnDisplay() {
// Create default & external NavBar fragment.
NavigationBarFragment defaultNavBar = (NavigationBarFragment) mFragment;
@@ -193,7 +217,7 @@ public class NavigationBarFragmentTest extends SysuiBaseFragmentTest {
// Set IME window status for default NavBar.
mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
BACK_DISPOSITION_DEFAULT, true, false);
- Handler.getMain().runWithScissors(() -> { }, 500);
+ processAllMessages();
// Verify IME window state will be updated in default NavBar & external NavBar state reset.
assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN,
@@ -204,7 +228,7 @@ public class NavigationBarFragmentTest extends SysuiBaseFragmentTest {
// Set IME window status for external NavBar.
mCommandQueue.setImeWindowStatus(EXTERNAL_DISPLAY_ID, null,
IME_VISIBLE, BACK_DISPOSITION_DEFAULT, true, false);
- Handler.getMain().runWithScissors(() -> { }, 500);
+ processAllMessages();
// Verify IME window state will be updated in external NavBar & default NavBar state reset.
assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN,
@@ -227,7 +251,8 @@ public class NavigationBarFragmentTest extends SysuiBaseFragmentTest {
mOverviewProxyService,
mock(NavigationModeController.class),
mock(StatusBarStateController.class),
- mMockSysUiState);
+ mMockSysUiState,
+ mBroadcastDispatcher);
}
private class HostCallbacksForExternalDisplay extends
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarInflaterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarInflaterViewTest.java
index cec7feb23752..991e49588417 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarInflaterViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarInflaterViewTest.java
@@ -31,6 +31,8 @@ import android.widget.FrameLayout;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.CommandQueue;
import org.junit.After;
@@ -51,6 +53,9 @@ public class NavigationBarInflaterViewTest extends SysuiTestCase {
@Before
public void setUp() {
mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
+ mDependency.injectMockDependency(AssistManager.class);
+ mDependency.injectMockDependency(OverviewProxyService.class);
+ mDependency.injectMockDependency(NavigationModeController.class);
mNavBarInflaterView = spy(new NavigationBarInflaterView(mContext, null));
doNothing().when(mNavBarInflaterView).createInflaters();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java
index bb109bd931de..1e9378aea075 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java
@@ -28,7 +28,11 @@ import android.view.IWindowManager;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
@@ -44,6 +48,12 @@ public class NavigationBarTransitionsTest extends SysuiTestCase {
@Before
public void setup() {
mDependency.injectMockDependency(IWindowManager.class);
+ mDependency.injectMockDependency(AssistManager.class);
+ mDependency.injectMockDependency(OverviewProxyService.class);
+ mDependency.injectMockDependency(NavigationModeController.class);
+ mDependency.injectMockDependency(StatusBarStateController.class);
+ mDependency.injectMockDependency(KeyguardStateController.class);
+
mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
NavigationBarView navBar = spy(new NavigationBarView(mContext, null));
when(navBar.getCurrentView()).thenReturn(navBar);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index a0d264d81c7c..5b54fba5b3b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -33,6 +33,7 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -71,6 +72,7 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
@Before
public void setup() {
+ mDependency.injectMockDependency(BubbleController.class);
mHeadsUpManager = new HeadsUpManager(mContext) {};
when(mNotificationEntryManager.getPendingNotificationsIterator())
@@ -100,8 +102,8 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
mGroupManager.onEntryAdded(childEntry);
// A suppressed summary should transfer its alert state to the child.
- assertFalse(mHeadsUpManager.isAlerting(summaryEntry.key));
- assertTrue(mHeadsUpManager.isAlerting(childEntry.key));
+ assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+ assertTrue(mHeadsUpManager.isAlerting(childEntry.getKey()));
}
@Test
@@ -118,14 +120,14 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
mGroupManager.onEntryAdded(childEntry);
// Add second child notification so that summary is no longer suppressed.
- mPendingEntries.put(childEntry2.key, childEntry2);
+ mPendingEntries.put(childEntry2.getKey(), childEntry2);
mNotificationEntryListener.onPendingEntryAdded(childEntry2);
mGroupManager.onEntryAdded(childEntry2);
// The alert state should transfer back to the summary as there is now more than one
// child and the summary should no longer be suppressed.
- assertTrue(mHeadsUpManager.isAlerting(summaryEntry.key));
- assertFalse(mHeadsUpManager.isAlerting(childEntry.key));
+ assertTrue(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+ assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
}
@Test
@@ -145,12 +147,12 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
mGroupAlertTransferHelper.onDozingChanged(true);
// Add second child notification so that summary is no longer suppressed.
- mPendingEntries.put(childEntry2.key, childEntry2);
+ mPendingEntries.put(childEntry2.getKey(), childEntry2);
mNotificationEntryListener.onPendingEntryAdded(childEntry2);
mGroupManager.onEntryAdded(childEntry2);
// Dozing changed so no reason to re-alert summary.
- assertFalse(mHeadsUpManager.isAlerting(summaryEntry.key));
+ assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
}
@Test
@@ -166,8 +168,8 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
// Alert is immediately removed from summary, but we do not show child yet either as its
// content is not inflated.
- assertFalse(mHeadsUpManager.isAlerting(summaryEntry.key));
- assertFalse(mHeadsUpManager.isAlerting(childEntry.key));
+ assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+ assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
assertTrue(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
}
@@ -187,8 +189,8 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
mNotificationEntryListener.onEntryReinflated(childEntry);
// Alert is immediately removed from summary, and we show child as its content is inflated.
- assertFalse(mHeadsUpManager.isAlerting(summaryEntry.key));
- assertTrue(mHeadsUpManager.isAlerting(childEntry.key));
+ assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+ assertTrue(mHeadsUpManager.isAlerting(childEntry.getKey()));
}
@Test
@@ -207,7 +209,7 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
mGroupManager.onEntryAdded(childEntry);
// Add second child notification so that summary is no longer suppressed.
- mPendingEntries.put(childEntry2.key, childEntry2);
+ mPendingEntries.put(childEntry2.getKey(), childEntry2);
mNotificationEntryListener.onPendingEntryAdded(childEntry2);
mGroupManager.onEntryAdded(childEntry2);
@@ -218,7 +220,7 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
verify(childEntry.getRow(), times(1)).freeContentViewWhenSafe(mHeadsUpManager
.getContentFlag());
- assertFalse(mHeadsUpManager.isAlerting(childEntry.key));
+ assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
}
@Test
@@ -253,10 +255,10 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
mGroupManager.onEntryAdded(childEntry);
// Notify that entry changed groups.
- StatusBarNotification oldNotification = childEntry.notification;
- StatusBarNotification newSbn = spy(childEntry.notification.clone());
+ StatusBarNotification oldNotification = childEntry.getSbn();
+ StatusBarNotification newSbn = spy(childEntry.getSbn().clone());
doReturn("other_group").when(newSbn).getGroupKey();
- childEntry.setNotification(newSbn);
+ childEntry.setSbn(newSbn);
mGroupManager.onEntryUpdated(childEntry, oldNotification);
assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
@@ -276,10 +278,10 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
mGroupManager.onEntryAdded(childEntry);
// Update that child to a summary.
- StatusBarNotification oldNotification = childEntry.notification;
- childEntry.setNotification(
+ StatusBarNotification oldNotification = childEntry.getSbn();
+ childEntry.setSbn(
mGroupTestHelper.createSummaryNotification(
- Notification.GROUP_ALERT_SUMMARY, 47).notification);
+ Notification.GROUP_ALERT_SUMMARY, 47).getSbn());
mGroupManager.onEntryUpdated(childEntry, oldNotification);
assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
index dd274c7c09b9..19ce1ea218c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
@@ -30,6 +30,7 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -57,6 +58,7 @@ public class NotificationGroupManagerTest extends SysuiTestCase {
@Before
public void setup() {
+ mDependency.injectMockDependency(BubbleController.class);
initializeGroupManager();
}
@@ -73,7 +75,7 @@ public class NotificationGroupManagerTest extends SysuiTestCase {
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
- assertTrue(mGroupManager.isOnlyChildInGroup(childEntry.notification));
+ assertTrue(mGroupManager.isOnlyChildInGroup(childEntry.getSbn()));
}
@Test
@@ -85,7 +87,7 @@ public class NotificationGroupManagerTest extends SysuiTestCase {
mGroupManager.onEntryAdded(childEntry);
mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
- assertTrue(mGroupManager.isChildInGroupWithSummary(childEntry.notification));
+ assertTrue(mGroupManager.isChildInGroupWithSummary(childEntry.getSbn()));
}
@Test
@@ -97,8 +99,8 @@ public class NotificationGroupManagerTest extends SysuiTestCase {
mGroupManager.onEntryAdded(childEntry);
mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
- assertTrue(mGroupManager.isSummaryOfGroup(summaryEntry.notification));
- assertEquals(summaryEntry, mGroupManager.getGroupSummary(childEntry.notification));
+ assertTrue(mGroupManager.isSummaryOfGroup(summaryEntry.getSbn()));
+ assertEquals(summaryEntry, mGroupManager.getGroupSummary(childEntry.getSbn()));
}
@Test
@@ -111,7 +113,7 @@ public class NotificationGroupManagerTest extends SysuiTestCase {
mGroupManager.onEntryRemoved(childEntry);
- assertFalse(mGroupManager.isChildInGroupWithSummary(childEntry.notification));
+ assertFalse(mGroupManager.isChildInGroupWithSummary(childEntry.getSbn()));
}
@Test
@@ -124,8 +126,8 @@ public class NotificationGroupManagerTest extends SysuiTestCase {
mGroupManager.onEntryRemoved(summaryEntry);
- assertNull(mGroupManager.getGroupSummary(childEntry.notification));
- assertFalse(mGroupManager.isSummaryOfGroup(summaryEntry.notification));
+ assertNull(mGroupManager.getGroupSummary(childEntry.getSbn()));
+ assertFalse(mGroupManager.isSummaryOfGroup(summaryEntry.getSbn()));
}
@Test
@@ -135,13 +137,13 @@ public class NotificationGroupManagerTest extends SysuiTestCase {
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
- when(mHeadsUpManager.isAlerting(childEntry.key)).thenReturn(true);
+ when(mHeadsUpManager.isAlerting(childEntry.getKey())).thenReturn(true);
mGroupManager.onHeadsUpStateChanged(childEntry, true);
// Child entries that are heads upped should be considered separate groups visually even if
// they are the same group logically
- assertEquals(childEntry, mGroupManager.getGroupSummary(childEntry.notification));
- assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(childEntry.notification));
+ assertEquals(childEntry, mGroupManager.getGroupSummary(childEntry.getSbn()));
+ assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(childEntry.getSbn()));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
index f1a7905d1d42..a49ae35a8040 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
@@ -87,7 +87,7 @@ public final class NotificationGroupTestHelper {
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
entry.setRow(row);
when(row.getEntry()).thenReturn(entry);
- when(row.getStatusBarNotification()).thenReturn(entry.sbn());
+ when(row.getStatusBarNotification()).thenReturn(entry.getSbn());
when(row.isInflationFlagSet(anyInt())).thenReturn(true);
return entry;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 219aef1ec685..4853f2023172 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
@@ -40,6 +40,8 @@ import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.doze.DozeLog;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.plugins.PluginManager;
@@ -50,10 +52,16 @@ import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.InjectionInflationController;
@@ -109,6 +117,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
private FalsingManager mFalsingManager;
@Mock
private KeyguardBypassController mKeyguardBypassController;
+ @Mock private DozeParameters mDozeParameters;
private NotificationPanelView mNotificationPanelView;
@Before
@@ -120,20 +129,23 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mDependency.injectTestDependency(StatusBarStateController.class,
mStatusBarStateController);
mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mUpdateMonitor);
- mDependency.injectMockDependency(ShadeController.class);
- mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
mDependency.injectMockDependency(ConfigurationController.class);
mDependency.injectMockDependency(ZenModeController.class);
NotificationWakeUpCoordinator coordinator =
- new NotificationWakeUpCoordinator(mContext,
+ new NotificationWakeUpCoordinator(
mock(HeadsUpManagerPhone.class),
new StatusBarStateControllerImpl(),
- mKeyguardBypassController);
- PulseExpansionHandler expansionHandler = new PulseExpansionHandler(mContext, coordinator,
+ mKeyguardBypassController,
+ mDozeParameters);
+ PulseExpansionHandler expansionHandler = new PulseExpansionHandler(
+ mContext,
+ coordinator,
mKeyguardBypassController, mHeadsUpManager,
- mock(NotificationRoundnessManager.class), mStatusBarStateController);
+ mock(NotificationRoundnessManager.class),
+ mStatusBarStateController,
+ new FalsingManagerFake());
mNotificationPanelView = new TestableNotificationPanelView(coordinator, expansionHandler,
- mKeyguardBypassController);
+ mKeyguardBypassController, mStatusBarStateController);
mNotificationPanelView.setHeadsUpManager(mHeadsUpManager);
mNotificationPanelView.setBar(mPanelBar);
@@ -210,13 +222,31 @@ public class NotificationPanelViewTest extends SysuiTestCase {
private class TestableNotificationPanelView extends NotificationPanelView {
TestableNotificationPanelView(NotificationWakeUpCoordinator coordinator,
PulseExpansionHandler expansionHandler,
- KeyguardBypassController bypassController) {
- super(NotificationPanelViewTest.this.mContext, null,
+ KeyguardBypassController bypassController,
+ SysuiStatusBarStateController statusBarStateController) {
+ super(
+ NotificationPanelViewTest.this.mContext,
+ null,
new InjectionInflationController(
SystemUIFactory.getInstance().getRootComponent()),
- coordinator, expansionHandler, mock(DynamicPrivacyController.class),
+ coordinator,
+ expansionHandler,
+ mock(DynamicPrivacyController.class),
bypassController,
- mFalsingManager, mock(PluginManager.class));
+ mFalsingManager,
+ mock(PluginManager.class),
+ mock(ShadeController.class),
+ mock(NotificationLockscreenUserManager.class),
+ new NotificationEntryManager(
+ new NotificationData(
+ mock(NotificationSectionsFeatureManager.class),
+ mock(NotifLog.class),
+ mock(PeopleNotificationIdentifier.class)),
+ mock(NotifLog.class)),
+ mock(KeyguardStateController.class),
+ statusBarStateController,
+ mock(DozeLog.class),
+ mDozeParameters);
mNotificationStackScroller = mNotificationStackScrollLayout;
mKeyguardStatusView = NotificationPanelViewTest.this.mKeyguardStatusView;
mKeyguardStatusBar = NotificationPanelViewTest.this.mKeyguardStatusBar;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 5d3cdc88aa99..85c247e11f94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -21,9 +21,11 @@ import static com.android.systemui.statusbar.phone.ScrimController.SEMI_TRANSPAR
import static com.android.systemui.statusbar.phone.ScrimController.TRANSPARENT;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -33,8 +35,8 @@ import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.animation.Animator;
-import android.animation.ValueAnimator;
import android.app.AlarmManager;
+import android.content.res.Resources;
import android.graphics.Color;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
@@ -44,12 +46,13 @@ import android.view.View;
import androidx.test.filters.SmallTest;
import com.android.internal.colorextraction.ColorExtractor.GradientColors;
-import com.android.internal.util.function.TriConsumer;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.DejankUtils;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
-import com.android.systemui.util.wakelock.WakeLock;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.wakelock.DelayedWakeLock;
import com.android.systemui.utils.os.FakeHandler;
import org.junit.After;
@@ -57,17 +60,20 @@ import org.junit.Assert;
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.Arrays;
import java.util.Collections;
import java.util.HashSet;
-import java.util.function.Consumer;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
public class ScrimControllerTest extends SysuiTestCase {
- private SynchronousScrimController mScrimController;
+ private ScrimController mScrimController;
private ScrimView mScrimBehind;
private ScrimView mScrimInFront;
private ScrimView mScrimForBubble;
@@ -75,50 +81,159 @@ public class ScrimControllerTest extends SysuiTestCase {
private float mScrimBehindAlpha;
private GradientColors mScrimInFrontColor;
private int mScrimVisibility;
- private DozeParameters mDozeParamenters;
- private WakeLock mWakeLock;
private boolean mAlwaysOnEnabled;
- private AlarmManager mAlarmManager;
private TestableLooper mLooper;
+ @Mock
+ private AlarmManager mAlarmManager;
+ @Mock
+ private DozeParameters mDozeParamenters;
+ @Mock
+ LightBarController mLightBarController;
+ @Mock
+ Resources mResources;
+ @Mock
+ DelayedWakeLock.Builder mDelayedWakeLockBuilder;
+ @Mock
+ private DelayedWakeLock mWakeLock;
+ @Mock
+ KeyguardStateController mKeyguardStateController;
+ @Mock
+ KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ private SysuiColorExtractor mSysuiColorExtractor;
+
+
+ private static class AnimatorListener implements Animator.AnimatorListener {
+ private int mNumStarts;
+ private int mNumEnds;
+ private int mNumCancels;
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mNumStarts++;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mNumEnds++;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mNumCancels++;
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+
+ }
+ public int getNumStarts() {
+ return mNumStarts;
+ }
+
+ public int getNumEnds() {
+ return mNumEnds;
+ }
+
+ public int getNumCancels() {
+ return mNumCancels;
+ }
+
+ public void reset() {
+ mNumStarts = 0;
+ mNumEnds = 0;
+ mNumCancels = 0;
+ }
+ };
+
+ private AnimatorListener mAnimatorListener = new AnimatorListener();
+
+
+ private void finishAnimationsImmediately() {
+ // Execute code that will trigger animations.
+ mScrimController.onPreDraw();
+ // Force finish all animations.
+ mLooper.processAllMessages();
+ endAnimation(mScrimBehind);
+ endAnimation(mScrimInFront);
+ endAnimation(mScrimForBubble);
+
+ Assert.assertEquals("Animators did not finish",
+ mAnimatorListener.getNumStarts(), mAnimatorListener.getNumEnds());
+ }
+
+ private void endAnimation(View scrimView) {
+ Animator animator = getAnimator(scrimView);
+ if (animator != null) {
+ animator.end();
+ }
+ }
+
+ private Animator getAnimator(View scrimView) {
+ return (Animator) scrimView.getTag(ScrimController.TAG_KEY_ANIM);
+ }
@Before
public void setup() {
+ MockitoAnnotations.initMocks(this);
+
mScrimBehind = spy(new ScrimView(getContext()));
mScrimInFront = new ScrimView(getContext());
mScrimForBubble = new ScrimView(getContext());
- mWakeLock = mock(WakeLock.class);
- mAlarmManager = mock(AlarmManager.class);
mAlwaysOnEnabled = true;
- mDozeParamenters = mock(DozeParameters.class);
mLooper = TestableLooper.get(this);
- mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
+ DejankUtils.setImmediate(true);
+
+ // ScrimController uses mScrimBehind to delay some callbacks that we should run immediately.
+ doAnswer(invocation -> {
+ ((Runnable) invocation.getArgument(0)).run();
+ return null;
+ }).when(mScrimBehind).postOnAnimationDelayed(any(Runnable.class), anyLong());
+
when(mDozeParamenters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled);
when(mDozeParamenters.getDisplayNeedsBlanking()).thenReturn(true);
- mScrimController = new SynchronousScrimController(mScrimBehind, mScrimInFront,
- mScrimForBubble,
- (scrimState, scrimBehindAlpha, scrimInFrontColor) -> {
- mScrimState = scrimState;
- mScrimBehindAlpha = scrimBehindAlpha;
- mScrimInFrontColor = scrimInFrontColor;
- },
- visible -> mScrimVisibility = visible, mDozeParamenters, mAlarmManager,
- mock(KeyguardMonitor.class));
+
+ doAnswer((Answer<Void>) invocation -> {
+ mScrimState = invocation.getArgument(0);
+ mScrimBehindAlpha = invocation.getArgument(1);
+ mScrimInFrontColor = invocation.getArgument(2);
+ return null;
+ }).when(mLightBarController).setScrimState(
+ any(ScrimState.class), anyFloat(), any(GradientColors.class));
+
+ when(mDelayedWakeLockBuilder.setHandler(any(Handler.class)))
+ .thenReturn(mDelayedWakeLockBuilder);
+ when(mDelayedWakeLockBuilder.setTag(any(String.class)))
+ .thenReturn(mDelayedWakeLockBuilder);
+ when(mDelayedWakeLockBuilder.build()).thenReturn(mWakeLock);
+
+ when(mSysuiColorExtractor.getNeutralColors()).thenReturn(new GradientColors());
+
+ mScrimController = new ScrimController(mLightBarController,
+ mDozeParamenters, mAlarmManager, mKeyguardStateController,
+ mResources, mDelayedWakeLockBuilder,
+ new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor, mSysuiColorExtractor);
+ mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
+ mScrimController.attachViews(mScrimBehind, mScrimInFront, mScrimForBubble);
+ mScrimController.setAnimatorListener(mAnimatorListener);
+
mScrimController.setHasBackdrop(false);
mScrimController.setWallpaperSupportsAmbientMode(false);
mScrimController.transitionTo(ScrimState.KEYGUARD);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
}
@After
public void tearDown() {
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
+ DejankUtils.setImmediate(false);
}
@Test
public void transitionToKeyguard() {
mScrimController.transitionTo(ScrimState.KEYGUARD);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
SEMI_TRANSPARENT /* back */,
@@ -130,9 +245,23 @@ public class ScrimControllerTest extends SysuiTestCase {
}
@Test
+ public void transitionToOff() {
+ mScrimController.transitionTo(ScrimState.OFF);
+ finishAnimationsImmediately();
+
+ assertScrimAlpha(OPAQUE /* front */,
+ OPAQUE /* back */,
+ TRANSPARENT /* bubble */);
+
+ assertScrimTint(true /* front */,
+ true /* behind */,
+ false /* bubble */);
+ }
+
+ @Test
public void transitionToAod_withRegularWallpaper() {
mScrimController.transitionTo(ScrimState.AOD);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
OPAQUE /* back */,
@@ -147,7 +276,7 @@ public class ScrimControllerTest extends SysuiTestCase {
public void transitionToAod_withAodWallpaper() {
mScrimController.setWallpaperSupportsAmbientMode(true);
mScrimController.transitionTo(ScrimState.AOD);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
TRANSPARENT /* back */,
@@ -155,7 +284,7 @@ public class ScrimControllerTest extends SysuiTestCase {
// Pulsing notification should conserve AOD wallpaper.
mScrimController.transitionTo(ScrimState.PULSING);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
TRANSPARENT /* back */,
@@ -167,7 +296,7 @@ public class ScrimControllerTest extends SysuiTestCase {
mScrimController.setHasBackdrop(true);
mScrimController.setWallpaperSupportsAmbientMode(true);
mScrimController.transitionTo(ScrimState.AOD);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
OPAQUE /* back */,
@@ -182,9 +311,9 @@ public class ScrimControllerTest extends SysuiTestCase {
public void setHasBackdrop_withAodWallpaperAndAlbumArt() {
mScrimController.setWallpaperSupportsAmbientMode(true);
mScrimController.transitionTo(ScrimState.AOD);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
mScrimController.setHasBackdrop(true);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
OPAQUE /* back */,
@@ -200,7 +329,7 @@ public class ScrimControllerTest extends SysuiTestCase {
// Assert that setting the AOD front scrim alpha doesn't take effect in a non-AOD state.
mScrimController.transitionTo(ScrimState.KEYGUARD);
mScrimController.setAodFrontScrimAlpha(0.5f);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
SEMI_TRANSPARENT /* back */,
@@ -208,7 +337,7 @@ public class ScrimControllerTest extends SysuiTestCase {
// ... but that it does take effect once we enter the AOD state.
mScrimController.transitionTo(ScrimState.AOD);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
assertScrimAlpha(SEMI_TRANSPARENT /* front */,
OPAQUE /* back */,
TRANSPARENT /* bubble */);
@@ -223,7 +352,7 @@ public class ScrimControllerTest extends SysuiTestCase {
// for a bit.
mScrimController.transitionTo(ScrimState.UNLOCKED);
mScrimController.transitionTo(ScrimState.AOD);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
assertScrimAlpha(OPAQUE /* front */,
OPAQUE /* back */,
TRANSPARENT /* bubble */);
@@ -233,7 +362,7 @@ public class ScrimControllerTest extends SysuiTestCase {
mAlwaysOnEnabled = false;
mScrimController.transitionTo(ScrimState.UNLOCKED);
mScrimController.transitionTo(ScrimState.AOD);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
mScrimController.setAodFrontScrimAlpha(0.3f);
Assert.assertEquals(ScrimState.AOD.getFrontAlpha(), mScrimInFront.getViewAlpha(), 0.001f);
Assert.assertNotEquals(0.3f, mScrimInFront.getViewAlpha(), 0.001f);
@@ -249,13 +378,13 @@ public class ScrimControllerTest extends SysuiTestCase {
// the back scrim opacity - otherwise it would hide AoD wallpapers.
mScrimController.setWallpaperSupportsAmbientMode(false);
mScrimController.transitionTo(ScrimState.AOD);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
OPAQUE /* back */,
TRANSPARENT /* bubble */);
mScrimController.transitionTo(ScrimState.PULSING);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
// Front scrim should be transparent, but tinted
// Back scrim should be semi-transparent so the user can see the wallpaper
// Pulse callback should have been invoked
@@ -269,14 +398,14 @@ public class ScrimControllerTest extends SysuiTestCase {
// ... and when ambient goes dark, front scrim should be semi-transparent
mScrimController.setAodFrontScrimAlpha(0.5f);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
// Front scrim should be semi-transparent
assertScrimAlpha(SEMI_TRANSPARENT /* front */,
OPAQUE /* back */,
TRANSPARENT /* bubble */);
mScrimController.setWakeLockScreenSensorActive(true);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
assertScrimAlpha(SEMI_TRANSPARENT /* front */,
SEMI_TRANSPARENT /* back */,
TRANSPARENT /* bubble */);
@@ -288,7 +417,7 @@ public class ScrimControllerTest extends SysuiTestCase {
@Test
public void transitionToKeyguardBouncer() {
mScrimController.transitionTo(ScrimState.BOUNCER);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
// Front scrim should be transparent
// Back scrim should be visible without tint
assertScrimAlpha(TRANSPARENT /* front */,
@@ -303,7 +432,7 @@ public class ScrimControllerTest extends SysuiTestCase {
@Test
public void transitionToBouncer() {
mScrimController.transitionTo(ScrimState.BOUNCER_SCRIMMED);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
// Front scrim should be transparent
// Back scrim should be visible without tint
assertScrimAlpha(SEMI_TRANSPARENT /* front */,
@@ -318,7 +447,7 @@ public class ScrimControllerTest extends SysuiTestCase {
public void transitionToUnlocked() {
mScrimController.setPanelExpansion(0f);
mScrimController.transitionTo(ScrimState.UNLOCKED);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
TRANSPARENT /* back */,
TRANSPARENT /* bubble */);
@@ -337,7 +466,7 @@ public class ScrimControllerTest extends SysuiTestCase {
@Test
public void transitionToBubbleExpanded() {
mScrimController.transitionTo(ScrimState.BUBBLE_EXPANDED);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
assertScrimTint(false /* front */,
false /* behind */,
@@ -357,15 +486,15 @@ public class ScrimControllerTest extends SysuiTestCase {
@Test
public void scrimStateCallback() {
mScrimController.transitionTo(ScrimState.UNLOCKED);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
Assert.assertEquals(mScrimState, ScrimState.UNLOCKED);
mScrimController.transitionTo(ScrimState.BOUNCER);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
Assert.assertEquals(mScrimState, ScrimState.BOUNCER);
mScrimController.transitionTo(ScrimState.BOUNCER_SCRIMMED);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
Assert.assertEquals(mScrimState, ScrimState.BOUNCER_SCRIMMED);
}
@@ -374,18 +503,18 @@ public class ScrimControllerTest extends SysuiTestCase {
mScrimController.setPanelExpansion(0f);
mScrimController.setPanelExpansion(0.5f);
mScrimController.transitionTo(ScrimState.UNLOCKED);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
reset(mScrimBehind);
mScrimController.setPanelExpansion(0f);
mScrimController.setPanelExpansion(1.0f);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
Assert.assertEquals("Scrim alpha should change after setPanelExpansion",
mScrimBehindAlpha, mScrimBehind.getViewAlpha(), 0.01f);
mScrimController.setPanelExpansion(0f);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
Assert.assertEquals("Scrim alpha should change after setPanelExpansion",
mScrimBehindAlpha, mScrimBehind.getViewAlpha(), 0.01f);
@@ -396,7 +525,7 @@ public class ScrimControllerTest extends SysuiTestCase {
mScrimController.setPanelExpansion(0f);
mScrimController.setPanelExpansion(0.5f);
mScrimController.transitionTo(ScrimState.UNLOCKED);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
final float scrimAlpha = mScrimBehind.getViewAlpha();
reset(mScrimBehind);
@@ -408,7 +537,7 @@ public class ScrimControllerTest extends SysuiTestCase {
mScrimController.setExpansionAffectsAlpha(true);
mScrimController.setPanelExpansion(0.1f);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
Assert.assertNotEquals("Scrim opacity should change when setExpansionAffectsAlpha "
+ "is true", scrimAlpha, mScrimBehind.getViewAlpha(), 0.01f);
}
@@ -418,7 +547,7 @@ public class ScrimControllerTest extends SysuiTestCase {
// Simulate unlock with fingerprint
mScrimController.transitionTo(ScrimState.AOD);
mScrimController.setPanelExpansion(0f);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
mScrimController.transitionTo(ScrimState.UNLOCKED);
// Immediately tinted black after the transition starts
@@ -426,13 +555,14 @@ public class ScrimControllerTest extends SysuiTestCase {
true /* behind */,
true /* bubble */);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
// All scrims should be transparent at the end of fade transition.
assertScrimAlpha(TRANSPARENT /* front */,
TRANSPARENT /* behind */,
TRANSPARENT /* bubble */);
+ // Make sure at the very end of the animation, we're reset to transparent
assertScrimTint(false /* front */,
false /* behind */,
false /* bubble */);
@@ -442,7 +572,7 @@ public class ScrimControllerTest extends SysuiTestCase {
public void scrimBlanksBeforeLeavingAod() {
// Simulate unlock with fingerprint
mScrimController.transitionTo(ScrimState.AOD);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
mScrimController.transitionTo(ScrimState.UNLOCKED,
new ScrimController.Callback() {
@Override
@@ -457,7 +587,7 @@ public class ScrimControllerTest extends SysuiTestCase {
mScrimVisibility, OPAQUE);
}
});
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
}
@Test
@@ -465,7 +595,7 @@ public class ScrimControllerTest extends SysuiTestCase {
boolean[] blanked = {false};
// Simulate unlock with fingerprint
mScrimController.transitionTo(ScrimState.PULSING);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
mScrimController.transitionTo(ScrimState.UNLOCKED,
new ScrimController.Callback() {
@Override
@@ -473,7 +603,7 @@ public class ScrimControllerTest extends SysuiTestCase {
blanked[0] = true;
}
});
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
Assert.assertTrue("Scrim should blank when unlocking from pulse.", blanked[0]);
}
@@ -497,7 +627,7 @@ public class ScrimControllerTest extends SysuiTestCase {
callOrder[2] = ++currentCall[0];
}
});
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
Assert.assertEquals("onStart called in wrong order", 1, callOrder[0]);
Assert.assertEquals("onDisplayBlanked called in wrong order", 2, callOrder[1]);
Assert.assertEquals("onFinished called in wrong order", 3, callOrder[2]);
@@ -527,21 +657,21 @@ public class ScrimControllerTest extends SysuiTestCase {
mScrimController.transitionTo(ScrimState.AOD);
verify(mWakeLock).acquire(anyString());
verify(mWakeLock, never()).release(anyString());
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
verify(mWakeLock).release(anyString());
}
@Test
public void testDoesNotHoldWakeLock_whenUnlocking() {
mScrimController.transitionTo(ScrimState.UNLOCKED);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
verifyZeroInteractions(mWakeLock);
}
@Test
public void testCallbackInvokedOnSameStateTransition() {
mScrimController.transitionTo(ScrimState.UNLOCKED);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
ScrimController.Callback callback = mock(ScrimController.Callback.class);
mScrimController.transitionTo(ScrimState.UNLOCKED, callback);
verify(callback).onFinished();
@@ -551,13 +681,13 @@ public class ScrimControllerTest extends SysuiTestCase {
public void testHoldsAodWallpaperAnimationLock() {
// Pre-conditions
mScrimController.transitionTo(ScrimState.AOD);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
reset(mWakeLock);
mScrimController.onHideWallpaperTimeout();
verify(mWakeLock).acquire(anyString());
verify(mWakeLock, never()).release(anyString());
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
verify(mWakeLock).release(anyString());
}
@@ -565,13 +695,13 @@ public class ScrimControllerTest extends SysuiTestCase {
public void testHoldsPulsingWallpaperAnimationLock() {
// Pre-conditions
mScrimController.transitionTo(ScrimState.PULSING);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
reset(mWakeLock);
mScrimController.onHideWallpaperTimeout();
verify(mWakeLock).acquire(anyString());
verify(mWakeLock, never()).release(anyString());
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
verify(mWakeLock).release(anyString());
}
@@ -620,29 +750,29 @@ public class ScrimControllerTest extends SysuiTestCase {
public void testConservesExpansionOpacityAfterTransition() {
mScrimController.transitionTo(ScrimState.UNLOCKED);
mScrimController.setPanelExpansion(0.5f);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
final float expandedAlpha = mScrimBehind.getViewAlpha();
mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
mScrimController.transitionTo(ScrimState.UNLOCKED);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
Assert.assertEquals("Scrim expansion opacity wasn't conserved when transitioning back",
expandedAlpha, mScrimBehind.getViewAlpha(), 0.01f);
}
@Test
- public void cancelsOldAnimationBeforeBlanking() {
+ public void testCancelsOldAnimationBeforeBlanking() {
mScrimController.transitionTo(ScrimState.AOD);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
// Consume whatever value we had before
- mScrimController.wasAnimationJustCancelled();
+ mAnimatorListener.reset();
mScrimController.transitionTo(ScrimState.KEYGUARD);
- mScrimController.finishAnimationsImmediately();
- Assert.assertTrue(mScrimController.wasAnimationJustCancelled());
+ finishAnimationsImmediately();
+ Assert.assertTrue("Animators not canceled", mAnimatorListener.getNumCancels() != 0);
}
@Test
@@ -661,13 +791,13 @@ public class ScrimControllerTest extends SysuiTestCase {
mScrimController.setWallpaperSupportsAmbientMode(true);
mScrimController.setKeyguardOccluded(true);
mScrimController.transitionTo(ScrimState.AOD);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
OPAQUE /* behind */,
TRANSPARENT /* bubble */);
mScrimController.transitionTo(ScrimState.PULSING);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
OPAQUE /* behind */,
TRANSPARENT /* bubble */);
@@ -677,13 +807,13 @@ public class ScrimControllerTest extends SysuiTestCase {
public void testHidesShowWhenLockedActivity_whenAlreadyInAod() {
mScrimController.setWallpaperSupportsAmbientMode(true);
mScrimController.transitionTo(ScrimState.AOD);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
TRANSPARENT /* behind */,
TRANSPARENT /* bubble */);
mScrimController.setKeyguardOccluded(true);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
OPAQUE /* behind */,
TRANSPARENT /* bubble */);
@@ -698,7 +828,7 @@ public class ScrimControllerTest extends SysuiTestCase {
continue;
}
mScrimController.transitionTo(state);
- mScrimController.finishAnimationsImmediately();
+ finishAnimationsImmediately();
Assert.assertEquals("Should be clickable unless AOD or PULSING, was: " + state,
mScrimBehind.getViewAlpha() != 0 && !eatsTouches.contains(state),
mScrimBehind.isClickable());
@@ -729,6 +859,22 @@ public class ScrimControllerTest extends SysuiTestCase {
mScrimForBubble.getDefaultFocusHighlightEnabled());
}
+ @Test
+ public void testIsLowPowerMode() {
+ HashSet<ScrimState> lowPowerModeStates = new HashSet<>(Arrays.asList(
+ ScrimState.OFF, ScrimState.AOD, ScrimState.PULSING));
+ HashSet<ScrimState> regularStates = new HashSet<>(Arrays.asList(
+ ScrimState.UNINITIALIZED, ScrimState.KEYGUARD, ScrimState.BOUNCER,
+ ScrimState.BOUNCER_SCRIMMED, ScrimState.BRIGHTNESS_MIRROR, ScrimState.UNLOCKED,
+ ScrimState.BUBBLE_EXPANDED));
+
+ for (ScrimState state : ScrimState.values()) {
+ if (!lowPowerModeStates.contains(state) && !regularStates.contains(state)) {
+ Assert.fail("Scrim state not whitelisted nor blacklisted as low power mode");
+ }
+ }
+ }
+
private void assertScrimTint(boolean front, boolean behind, boolean bubble) {
Assert.assertEquals("Tint test failed at state " + mScrimController.getState()
+ " with scrim: " + getScrimName(mScrimInFront) + " and tint: "
@@ -788,83 +934,4 @@ public class ScrimControllerTest extends SysuiTestCase {
visibility /* expected */,
mScrimVisibility);
}
-
- /**
- * Special version of ScrimController where animations have 0 duration for test purposes.
- */
- private class SynchronousScrimController extends ScrimController {
-
- private boolean mAnimationCancelled;
- boolean mOnPreDrawCalled;
-
- SynchronousScrimController(ScrimView scrimBehind, ScrimView scrimInFront,
- ScrimView scrimForBubble,
- TriConsumer<ScrimState, Float, GradientColors> scrimStateListener,
- Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters,
- AlarmManager alarmManager, KeyguardMonitor keyguardMonitor) {
- super(scrimBehind, scrimInFront, scrimForBubble, scrimStateListener,
- scrimVisibleListener, dozeParameters, alarmManager, keyguardMonitor);
- }
-
- @Override
- public boolean onPreDraw() {
- mOnPreDrawCalled = true;
- return super.onPreDraw();
- }
-
- void finishAnimationsImmediately() {
- boolean[] animationFinished = {false};
- setOnAnimationFinished(() -> animationFinished[0] = true);
- // Execute code that will trigger animations.
- onPreDraw();
- // Force finish all animations.
- mLooper.processAllMessages();
- endAnimation(mScrimBehind, TAG_KEY_ANIM);
- endAnimation(mScrimInFront, TAG_KEY_ANIM);
- endAnimation(mScrimForBubble, TAG_KEY_ANIM);
-
- if (!animationFinished[0]) {
- throw new IllegalStateException("Animation never finished");
- }
- }
-
- boolean wasAnimationJustCancelled() {
- final boolean wasCancelled = mAnimationCancelled;
- mAnimationCancelled = false;
- return wasCancelled;
- }
-
- private void endAnimation(View scrimView, int tag) {
- Animator animator = (Animator) scrimView.getTag(tag);
- if (animator != null) {
- animator.end();
- }
- }
-
- @Override
- protected void cancelAnimator(ValueAnimator previousAnimator) {
- super.cancelAnimator(previousAnimator);
- mAnimationCancelled = true;
- }
-
- @Override
- protected Handler getHandler() {
- return new FakeHandler(mLooper.getLooper());
- }
-
- @Override
- protected WakeLock createWakeLock() {
- return mWakeLock;
- }
-
- /**
- * Do not wait for a frame since we're in a test environment.
- *
- * @param callback What to execute.
- */
- @Override
- protected void doOnTheNextFrame(Runnable callback) {
- callback.run();
- }
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 3c445c80f349..3e07cff9b09b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -45,7 +45,9 @@ import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
@@ -65,6 +67,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Mock
private KeyguardBouncer mBouncer;
@Mock
+ private KeyguardStateController mKeyguardStateController;
+ @Mock
private StatusBar mStatusBar;
@Mock
private ViewGroup mContainer;
@@ -89,7 +93,9 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mDependency.injectMockDependency(StatusBarWindowController.class);
mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
+ mDependency.injectMockDependency(NotificationMediaManager.class);
mDependency.injectTestDependency(StatusBarStateController.class, mStatusBarStateController);
+ mDependency.injectTestDependency(KeyguardStateController.class, mKeyguardStateController);
when(mLockIconContainer.getParent()).thenReturn(mock(ViewGroup.class));
when(mLockIconContainer.animate()).thenReturn(mock(ViewPropertyAnimator.class,
RETURNS_DEEP_STUBS));
@@ -169,7 +175,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Test
public void onPanelExpansionChanged_showsBouncerWhenSwiping() {
- when(mStatusBar.isKeyguardCurrentlySecure()).thenReturn(true);
+ when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(0.5f /* expansion */,
true /* tracking */);
verify(mBouncer).show(eq(false), eq(false));
@@ -223,6 +229,31 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
verify(mStatusBar, never()).animateKeyguardUnoccluding();
}
+ @Test
+ public void testHiding_cancelsGoneRunnable() {
+ OnDismissAction action = mock(OnDismissAction.class);
+ Runnable cancelAction = mock(Runnable.class);
+ mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction,
+ true /* afterKeyguardGone */);
+
+ mStatusBarKeyguardViewManager.hideBouncer(true);
+ mStatusBarKeyguardViewManager.hide(0, 30);
+ verify(action, never()).onDismiss();
+ verify(cancelAction).run();
+ }
+
+ @Test
+ public void testHiding_doesntCancelWhenShowing() {
+ OnDismissAction action = mock(OnDismissAction.class);
+ Runnable cancelAction = mock(Runnable.class);
+ mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction,
+ true /* afterKeyguardGone */);
+
+ mStatusBarKeyguardViewManager.hide(0, 30);
+ verify(action).onDismiss();
+ verify(cancelAction, never()).run();
+ }
+
private class TestableStatusBarKeyguardViewManager extends StatusBarKeyguardViewManager {
public TestableStatusBarKeyguardViewManager(Context context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 5a6f27dcfaae..d8a68b0c230c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -71,7 +71,7 @@ import com.android.systemui.statusbar.notification.NotificationInterruptionState
import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
@@ -104,7 +104,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
@Mock
private ShadeController mShadeController;
@Mock
- private KeyguardMonitor mKeyguardMonitor;
+ private KeyguardStateController mKeyguardStateController;
@Mock
private Handler mHandler;
@Mock
@@ -140,7 +140,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
when(mContentIntent.getCreatorUserHandle()).thenReturn(UserHandle.of(1));
when(mContentIntent.getIntent()).thenReturn(mContentIntentInner);
- mNotificationTestHelper = new NotificationTestHelper(mContext);
+ mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
// Create standard notification with contentIntent
mNotificationRow = mNotificationTestHelper.createRow();
@@ -167,7 +167,8 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mock(StatusBarStateController.class), mock(KeyguardManager.class),
mock(IDreamManager.class), mRemoteInputManager,
mock(StatusBarRemoteInputCallback.class), mock(NotificationGroupManager.class),
- mock(NotificationLockscreenUserManager.class), mShadeController, mKeyguardMonitor,
+ mock(NotificationLockscreenUserManager.class), mShadeController,
+ mKeyguardStateController,
mock(NotificationInterruptionStateProvider.class), mock(MetricsLogger.class),
mock(LockPatternUtils.class), mHandler, mHandler, mActivityIntentHelper,
mBubbleController);
@@ -200,7 +201,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
sbn.getNotification().contentIntent = mContentIntent;
sbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
- when(mKeyguardMonitor.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mShadeController.isOccluded()).thenReturn(true);
// When
@@ -263,7 +264,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
// Given
sbn.getNotification().contentIntent = null;
- when(mKeyguardMonitor.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mShadeController.isOccluded()).thenReturn(true);
// When
@@ -293,7 +294,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
// Given
sbn.getNotification().contentIntent = mContentIntent;
- when(mKeyguardMonitor.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mShadeController.isOccluded()).thenReturn(true);
// When
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 24ec1097ea8b..c0c42ef9c1bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -35,21 +35,37 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.testing.FakeMetricsLogger;
+import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationEntryBuilder;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationViewHierarchyManager;
+import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper()
@@ -63,11 +79,33 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
@Before
public void setup() {
+ NotificationRemoteInputManager notificationRemoteInputManager =
+ mock(NotificationRemoteInputManager.class);
+ when(notificationRemoteInputManager.getController())
+ .thenReturn(mock(RemoteInputController.class));
mMetricsLogger = new FakeMetricsLogger();
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
mCommandQueue = new CommandQueue(mContext);
mContext.putComponent(CommandQueue.class, mCommandQueue);
+ mDependency.injectTestDependency(StatusBarStateController.class,
+ mock(SysuiStatusBarStateController.class));
mDependency.injectTestDependency(ShadeController.class, mShadeController);
+ mDependency.injectTestDependency(NotificationRemoteInputManager.class,
+ notificationRemoteInputManager);
+ mDependency.injectMockDependency(NotificationViewHierarchyManager.class);
+ mDependency.injectMockDependency(NotificationRemoteInputManager.Callback.class);
+ mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
+ mDependency.injectMockDependency(NotificationInterruptionStateProvider.class);
+ mDependency.injectMockDependency(NotificationMediaManager.class);
+ mDependency.injectMockDependency(VisualStabilityManager.class);
+ mDependency.injectMockDependency(NotificationGutsManager.class);
+ mDependency.injectMockDependency(StatusBarWindowController.class);
+ mDependency.injectMockDependency(InitController.class);
+ NotificationData notificationData = mock(NotificationData.class);
+ when(notificationData.getNotificationsForCurrentUser()).thenReturn(new ArrayList<>());
+ NotificationEntryManager entryManager =
+ mDependency.injectMockDependency(NotificationEntryManager.class);
+ when(entryManager.getNotificationData()).thenReturn(notificationData);
StatusBarWindowView statusBarWindowView = mock(StatusBarWindowView.class);
when(statusBarWindowView.getResources()).thenReturn(mContext.getResources());
@@ -77,7 +115,7 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
mock(DozeScrimController.class), mock(ScrimController.class),
mock(ActivityLaunchAnimator.class), mock(DynamicPrivacyController.class),
mock(NotificationAlertingManager.class),
- mock(NotificationRowBinderImpl.class));
+ mock(NotificationRowBinderImpl.class), mock(KeyguardStateController.class));
}
@Test
@@ -94,7 +132,7 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
TestableLooper.get(this).processAllMessages();
assertFalse("The panel shouldn't allow heads up while disabled",
- mStatusBar.canHeadsUp(entry, entry.sbn()));
+ mStatusBar.canHeadsUp(entry, entry.getSbn()));
}
@Test
@@ -111,7 +149,7 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
TestableLooper.get(this).processAllMessages();
assertFalse("The panel shouldn't allow heads up while notitifcation shade disabled",
- mStatusBar.canHeadsUp(entry, entry.sbn()));
+ mStatusBar.canHeadsUp(entry, entry.getSbn()));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index 4b6ca56fc8e3..a65f5a503375 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -24,18 +24,19 @@ import static org.mockito.Mockito.when;
import static org.mockito.internal.verification.VerificationModeFactory.times;
import android.content.Intent;
-import android.os.UserManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
@@ -47,14 +48,13 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
- @Mock private NotificationPresenter mPresenter;
- @Mock private UserManager mUserManager;
-
- // Dependency mocks:
@Mock private NotificationEntryManager mEntryManager;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
@Mock private ShadeController mShadeController;
@Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
+ @Mock private KeyguardStateController mKeyguardStateController;
+ @Mock private SysuiStatusBarStateController mStatusBarStateController;
+ @Mock private ActivityStarter mActivityStarter;
private int mCurrentUserId = 0;
private StatusBarRemoteInputCallback mRemoteInputCallback;
@@ -71,7 +71,9 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
mRemoteInputCallback = spy(new StatusBarRemoteInputCallback(mContext,
- mock(NotificationGroupManager.class)));
+ mock(NotificationGroupManager.class), mNotificationLockscreenUserManager,
+ mKeyguardStateController, mStatusBarStateController, mActivityStarter,
+ mShadeController));
mRemoteInputCallback.mChallengeReceiver = mRemoteInputCallback.new ChallengeReceiver();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 4eb9a3151116..66c01ca58491 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -41,7 +41,9 @@ import static org.mockito.Mockito.when;
import android.app.Notification;
import android.app.StatusBarManager;
import android.app.trust.TrustManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.IntentFilter;
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.fingerprint.FingerprintManager;
import android.metrics.LogMaker;
@@ -51,39 +53,46 @@ import android.os.IPowerManager;
import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.service.dreams.IDreamManager;
import android.support.test.metricshelper.MetricsAsserts;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import android.util.DisplayMetrics;
import android.util.SparseArray;
+import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
+import android.widget.LinearLayout;
import androidx.test.filters.SmallTest;
-import com.android.internal.logging.MetricsLogger;
+import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.testing.FakeMetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.Dependency;
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.InitController;
import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.appops.AppOpsController;
import com.android.systemui.assist.AssistManager;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.doze.DozeHost;
+import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
-import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.NotificationEntryBuilder;
@@ -92,26 +101,38 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
+import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
+import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
+import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NewNotifPipeline;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+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.UserSwitcherController;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.util.InjectionInflationController;
import org.junit.Before;
import org.junit.Test;
@@ -122,16 +143,25 @@ import org.mockito.MockitoAnnotations;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
+
+import dagger.Lazy;
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
+@RunWithLooper(setAsMainLooper = true)
public class StatusBarTest extends SysuiTestCase {
+
+ private StatusBar mStatusBar;
+ private FakeMetricsLogger mMetricsLogger;
+ private PowerManager mPowerManager;
+ private TestableNotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+ private CommandQueue mCommandQueue;
+
+ @Mock private FeatureFlags mFeatureFlags;
+ @Mock private LightBarController mLightBarController;
+ @Mock private StatusBarIconController mStatusBarIconController;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- @Mock private UnlockMethodCache mUnlockMethodCache;
+ @Mock private KeyguardStateController mKeyguardStateController;
@Mock private KeyguardIndicationController mKeyguardIndicationController;
@Mock private NotificationStackScrollLayout mStackScroller;
@Mock private HeadsUpManagerPhone mHeadsUpManager;
@@ -141,13 +171,10 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private ScrimController mScrimController;
@Mock private DozeScrimController mDozeScrimController;
@Mock private ArrayList<NotificationEntry> mNotificationList;
+ @Mock private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
@Mock private BiometricUnlockController mBiometricUnlockController;
@Mock private NotificationData mNotificationData;
- @Mock
- private NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor;
-
- // Mock dependencies:
- @Mock private NotificationViewHierarchyManager mViewHierarchyManager;
+ @Mock private NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor;
@Mock private VisualStabilityManager mVisualStabilityManager;
@Mock private NotificationListener mNotificationListener;
@Mock private KeyguardViewMediator mKeyguardViewMediator;
@@ -155,51 +182,59 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private NotificationRemoteInputManager mRemoteInputManager;
@Mock private RemoteInputController mRemoteInputController;
@Mock private StatusBarStateControllerImpl mStatusBarStateController;
+ @Mock private BatteryController mBatteryController;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
@Mock private StatusBarNotificationPresenter mNotificationPresenter;
- @Mock
- private NotificationEntryListener mEntryListener;
- @Mock
- private NotificationFilter mNotificationFilter;
- @Mock
- private NotificationAlertingManager mNotificationAlertingManager;
- @Mock
- private NotificationLogger.ExpansionStateLogger mExpansionStateLogger;
- @Mock
- private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @Mock
- private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
- @Mock
- private StatusBarWindowView mStatusBarWindowView;
-
- private TestableStatusBar mStatusBar;
- private FakeMetricsLogger mMetricsLogger;
- private PowerManager mPowerManager;
- private TestableNotificationEntryManager mEntryManager;
- private TestableNotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
- private NotificationLogger mNotificationLogger;
- private CommandQueue mCommandQueue;
+ @Mock private NotificationEntryListener mEntryListener;
+ @Mock private NotificationFilter mNotificationFilter;
+ @Mock private NotificationAlertingManager mNotificationAlertingManager;
+ @Mock private NotificationLogger.ExpansionStateLogger mExpansionStateLogger;
+ @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
+ @Mock private StatusBarWindowView mStatusBarWindowView;
+ @Mock private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock private AssistManager mAssistManager;
+ @Mock private NotificationGutsManager mNotificationGutsManager;
+ @Mock private NotificationMediaManager mNotificationMediaManager;
+ @Mock private ForegroundServiceController mForegroundServiceController;
+ @Mock private AppOpsController mAppOpsController;
+ @Mock private NavigationBarController mNavigationBarController;
+ @Mock private BypassHeadsUpNotifier mBypassHeadsUpNotifier;
+ @Mock private SysuiColorExtractor mColorExtractor;
+ @Mock private ColorExtractor.GradientColors mGradientColors;
+ @Mock private DozeLog mDozeLog;
+ @Mock private PulseExpansionHandler mPulseExpansionHandler;
+ @Mock private NotificationWakeUpCoordinator mNotificationWakeUpCoordinator;
+ @Mock private KeyguardBypassController mKeyguardBypassController;
+ @Mock private InjectionInflationController mInjectionInflationController;
+ @Mock private DynamicPrivacyController mDynamicPrivacyController;
+ @Mock private NewNotifPipeline mNewNotifPipeline;
+ @Mock private ZenModeController mZenModeController;
+ @Mock private AutoHideController mAutoHideController;
+ @Mock private NotificationViewHierarchyManager mNotificationViewHierarchyManager;
+ @Mock private UserSwitcherController mUserSwitcherController;
+ @Mock private NetworkController mNetworkController;
+ @Mock private VibratorHelper mVibratorHelper;
+ @Mock private BubbleController mBubbleController;
+ @Mock private NotificationGroupManager mGroupManager;
+ @Mock private NotificationGroupAlertTransferHelper mGroupAlertTransferHelper;
+ @Mock private StatusBarWindowController mStatusBarWindowController;
+ @Mock private NotificationIconAreaController mNotificationIconAreaController;
+ @Mock private StatusBarWindowViewController.Builder mStatusBarWindowViewControllerBuilder;
+ @Mock private StatusBarWindowViewController mStatusBarWindowViewController;
+ @Mock private NotifLog mNotifLog;
+ @Mock private DozeParameters mDozeParameters;
+ @Mock private Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy;
+ @Mock private LockscreenWallpaper mLockscreenWallpaper;
+ @Mock private DozeServiceHost mDozeServiceHost;
+ @Mock private LinearLayout mLockIconContainer;
+ @Mock private ViewMediatorCallback mKeyguardVieMediatorCallback;
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
- mDependency.injectMockDependency(AssistManager.class);
- mDependency.injectMockDependency(NotificationGutsManager.class);
- mDependency.injectMockDependency(NotificationMediaManager.class);
- mDependency.injectMockDependency(ForegroundServiceController.class);
- mDependency.injectTestDependency(NotificationViewHierarchyManager.class,
- mViewHierarchyManager);
- mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager);
- mDependency.injectTestDependency(NotificationListener.class, mNotificationListener);
- mDependency.injectTestDependency(KeyguardMonitor.class, mock(KeyguardMonitor.class));
- mDependency.injectTestDependency(AppOpsController.class, mock(AppOpsController.class));
- mDependency.injectTestDependency(StatusBarStateController.class, mStatusBarStateController);
- mDependency.injectTestDependency(DeviceProvisionedController.class,
- mDeviceProvisionedController);
- mDependency.injectMockDependency(BubbleController.class);
mDependency.injectTestDependency(NotificationFilter.class, mNotificationFilter);
- mDependency.injectTestDependency(NotificationAlertingManager.class,
- mNotificationAlertingManager);
+ mDependency.injectMockDependency(KeyguardDismissUtil.class);
IPowerManager powerManagerService = mock(IPowerManager.class);
mPowerManager = new PowerManager(mContext, powerManagerService,
@@ -208,23 +243,18 @@ public class StatusBarTest extends SysuiTestCase {
mNotificationInterruptionStateProvider =
new TestableNotificationInterruptionStateProvider(mContext, mPowerManager,
mDreamManager, mAmbientDisplayConfiguration, mNotificationFilter,
- mStatusBarStateController);
- mDependency.injectTestDependency(NotificationInterruptionStateProvider.class,
- mNotificationInterruptionStateProvider);
- mDependency.injectMockDependency(NavigationBarController.class);
+ mStatusBarStateController, mBatteryController);
mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
mMetricsLogger = new FakeMetricsLogger();
- mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
- mEntryManager = new TestableNotificationEntryManager(mContext);
- mNotificationLogger = new NotificationLogger(mNotificationListener,
- Dependency.get(UiOffloadThread.class), mEntryManager, mStatusBarStateController,
+ TestableNotificationEntryManager entryManager = new TestableNotificationEntryManager(
+ mNotificationData);
+ NotificationLogger notificationLogger = new NotificationLogger(mNotificationListener,
+ Dependency.get(UiOffloadThread.class), entryManager, mStatusBarStateController,
mExpansionStateLogger);
- mNotificationLogger.setVisibilityReporter(mock(Runnable.class));
- mDependency.injectTestDependency(NotificationLogger.class, mNotificationLogger);
- DozeLog.traceDozing(mContext, false /* dozing */);
+ notificationLogger.setVisibilityReporter(mock(Runnable.class));
mCommandQueue = mock(CommandQueue.class);
when(mCommandQueue.asBinder()).thenReturn(new Binder());
@@ -253,30 +283,120 @@ public class StatusBarTest extends SysuiTestCase {
mHeadsUpManager, mHeadsUpSuppressor);
when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
- mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache,
- mKeyguardIndicationController, mStackScroller,
- mPowerManager, mNotificationPanelView, mBarService, mNotificationListener,
- mNotificationLogger, mVisualStabilityManager, mViewHierarchyManager,
- mEntryManager, mScrimController, mBiometricUnlockController,
- mKeyguardViewMediator, mRemoteInputManager, mock(NotificationGroupManager.class),
- mock(NotificationGroupAlertTransferHelper.class), mock(FalsingManager.class),
- mock(StatusBarWindowController.class), mock(NotificationIconAreaController.class),
- mDozeScrimController, mock(NotificationShelf.class),
- mLockscreenUserManager, mCommandQueue, mNotificationPresenter,
- mock(BubbleController.class), mock(NavigationBarController.class),
- mock(AutoHideController.class), mKeyguardUpdateMonitor, mStatusBarWindowView);
- mStatusBar.mContext = mContext;
+
+ WakefulnessLifecycle wakefulnessLifecycle = new WakefulnessLifecycle();
+ wakefulnessLifecycle.dispatchStartedWakingUp();
+ wakefulnessLifecycle.dispatchFinishedWakingUp();
+
+ when(mGradientColors.supportsDarkText()).thenReturn(true);
+ when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
+ ConfigurationController configurationController = new ConfigurationControllerImpl(mContext);
+
+ when(mStatusBarWindowViewControllerBuilder.build())
+ .thenReturn(mStatusBarWindowViewController);
+
+ when(mLockscreenWallpaperLazy.get()).thenReturn(mLockscreenWallpaper);
+ when(mBiometricUnlockControllerLazy.get()).thenReturn(mBiometricUnlockController);
+
+ mStatusBar = new StatusBar(
+ mContext,
+ mFeatureFlags,
+ mLightBarController,
+ mAutoHideController,
+ mKeyguardUpdateMonitor,
+ mStatusBarIconController,
+ mDozeLog,
+ mInjectionInflationController,
+ mPulseExpansionHandler,
+ mNotificationWakeUpCoordinator,
+ mKeyguardBypassController,
+ mKeyguardStateController,
+ mHeadsUpManager,
+ mDynamicPrivacyController,
+ mBypassHeadsUpNotifier,
+ true,
+ () -> mNewNotifPipeline,
+ new FalsingManagerFake(),
+ mBroadcastDispatcher,
+ new RemoteInputQuickSettingsDisabler(
+ mContext,
+ configurationController
+ ),
+ mNotificationGutsManager,
+ notificationLogger,
+ entryManager,
+ mNotificationInterruptionStateProvider,
+ mNotificationViewHierarchyManager,
+ mForegroundServiceController,
+ mAppOpsController,
+ mKeyguardViewMediator,
+ mZenModeController,
+ mNotificationAlertingManager,
+ new DisplayMetrics(),
+ mMetricsLogger,
+ Dependency.get(UiOffloadThread.class),
+ mNotificationMediaManager,
+ mLockscreenUserManager,
+ mRemoteInputManager,
+ mUserSwitcherController,
+ mNetworkController,
+ mBatteryController,
+ mColorExtractor,
+ new ScreenLifecycle(),
+ wakefulnessLifecycle,
+ mStatusBarStateController,
+ mVibratorHelper,
+ mBubbleController,
+ mGroupManager,
+ mGroupAlertTransferHelper,
+ mVisualStabilityManager,
+ mDeviceProvisionedController,
+ mNavigationBarController,
+ mAssistManager,
+ mNotificationListener,
+ configurationController,
+ mStatusBarWindowController,
+ mStatusBarWindowViewControllerBuilder,
+ mNotifLog,
+ mDozeParameters,
+ mScrimController,
+ mLockscreenWallpaperLazy,
+ mBiometricUnlockControllerLazy,
+ mDozeServiceHost,
+ mPowerManager,
+ mDozeScrimController);
+
+ when(mStatusBarWindowView.findViewById(R.id.lock_icon_container)).thenReturn(
+ mLockIconContainer);
+
+ when(mKeyguardViewMediator.registerStatusBar(any(StatusBar.class), any(ViewGroup.class),
+ any(NotificationPanelView.class), any(BiometricUnlockController.class),
+ any(ViewGroup.class), any(ViewGroup.class), any(KeyguardBypassController.class)))
+ .thenReturn(mStatusBarKeyguardViewManager);
+
+ when(mKeyguardViewMediator.getViewMediatorCallback()).thenReturn(
+ mKeyguardVieMediatorCallback);
+
+ // TODO: we should be able to call mStatusBar.start() and have all the below values
+ // initialized automatically.
mStatusBar.mComponents = mContext.getComponents();
- SystemUIFactory.getInstance().getRootComponent()
- .getStatusBarInjector()
- .createStatusBar(mStatusBar);
- mStatusBar.setHeadsUpManager(mHeadsUpManager);
+ mStatusBar.mStatusBarWindow = mStatusBarWindowView;
+ mStatusBar.mNotificationPanel = mNotificationPanelView;
+ mStatusBar.mCommandQueue = mCommandQueue;
+ mStatusBar.mDozeScrimController = mDozeScrimController;
+ mStatusBar.mNotificationIconAreaController = mNotificationIconAreaController;
+ mStatusBar.mPresenter = mNotificationPresenter;
+ mStatusBar.mKeyguardIndicationController = mKeyguardIndicationController;
+ mStatusBar.mBarService = mBarService;
+ mStatusBar.mStackScroller = mStackScroller;
+ mStatusBar.mStatusBarWindowViewController = mStatusBarWindowViewController;
+ mStatusBar.startKeyguard();
mStatusBar.putComponent(StatusBar.class, mStatusBar);
Dependency.get(InitController.class).executePostInitTasks();
- mEntryManager.setUpForTest(mock(NotificationPresenter.class), mStackScroller,
- mHeadsUpManager, mNotificationData);
- mEntryManager.addNotificationEntryListener(mEntryListener);
- mNotificationLogger.setUpWithContainer(mStackScroller);
+ entryManager.setUpForTest(mock(NotificationPresenter.class), mStackScroller,
+ mHeadsUpManager);
+ entryManager.addNotificationEntryListener(mEntryListener);
+ notificationLogger.setUpWithContainer(mStackScroller);
}
@Test
@@ -313,11 +433,11 @@ public class StatusBarTest extends SysuiTestCase {
public void lockscreenStateMetrics_notShowing() {
// uninteresting state, except that fingerprint must be non-zero
when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
- when(mUnlockMethodCache.canSkipBouncer()).thenReturn(true);
+ when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
// interesting state
when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
- when(mUnlockMethodCache.isMethodSecure()).thenReturn(false);
+ when(mKeyguardStateController.isMethodSecure()).thenReturn(false);
mStatusBar.onKeyguardViewManagerStatesUpdated();
MetricsAsserts.assertHasLog("missing hidden insecure lockscreen log",
@@ -331,11 +451,11 @@ public class StatusBarTest extends SysuiTestCase {
public void lockscreenStateMetrics_notShowing_secure() {
// uninteresting state, except that fingerprint must be non-zero
when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
- when(mUnlockMethodCache.canSkipBouncer()).thenReturn(true);
+ when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
// interesting state
when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
- when(mUnlockMethodCache.isMethodSecure()).thenReturn(true);
+ when(mKeyguardStateController.isMethodSecure()).thenReturn(true);
mStatusBar.onKeyguardViewManagerStatesUpdated();
@@ -350,11 +470,11 @@ public class StatusBarTest extends SysuiTestCase {
public void lockscreenStateMetrics_isShowing() {
// uninteresting state, except that fingerprint must be non-zero
when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
- when(mUnlockMethodCache.canSkipBouncer()).thenReturn(true);
+ when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
// interesting state
when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
- when(mUnlockMethodCache.isMethodSecure()).thenReturn(false);
+ when(mKeyguardStateController.isMethodSecure()).thenReturn(false);
mStatusBar.onKeyguardViewManagerStatesUpdated();
@@ -369,11 +489,11 @@ public class StatusBarTest extends SysuiTestCase {
public void lockscreenStateMetrics_isShowing_secure() {
// uninteresting state, except that fingerprint must be non-zero
when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
- when(mUnlockMethodCache.canSkipBouncer()).thenReturn(true);
+ when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
// interesting state
when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
- when(mUnlockMethodCache.isMethodSecure()).thenReturn(true);
+ when(mKeyguardStateController.isMethodSecure()).thenReturn(true);
mStatusBar.onKeyguardViewManagerStatesUpdated();
@@ -388,11 +508,11 @@ public class StatusBarTest extends SysuiTestCase {
public void lockscreenStateMetrics_isShowingBouncer() {
// uninteresting state, except that fingerprint must be non-zero
when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
- when(mUnlockMethodCache.canSkipBouncer()).thenReturn(true);
+ when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
// interesting state
when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
- when(mUnlockMethodCache.isMethodSecure()).thenReturn(true);
+ when(mKeyguardStateController.isMethodSecure()).thenReturn(true);
mStatusBar.onKeyguardViewManagerStatesUpdated();
@@ -599,8 +719,8 @@ public class StatusBarTest extends SysuiTestCase {
@Test
@RunWithLooper(setAsMainLooper = true)
public void testUpdateKeyguardState_DoesNotCrash() {
- mStatusBar.mState = StatusBarState.KEYGUARD;
- when(mStatusBar.mLockscreenUserManager.getCurrentProfiles()).thenReturn(
+ mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
+ when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(
new SparseArray<>());
mStatusBar.onStateChanged(StatusBarState.SHADE);
}
@@ -635,83 +755,18 @@ public class StatusBarTest extends SysuiTestCase {
mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
mStatusBar.showKeyguardImpl();
- // Keep track of callback to be able to stop the pulse
- DozeHost.PulseCallback[] pulseCallback = new DozeHost.PulseCallback[1];
- doAnswer(invocation -> {
- pulseCallback[0] = invocation.getArgument(0);
- return null;
- }).when(mDozeScrimController).pulse(any(), anyInt());
-
// Starting a pulse should change the scrim controller to the pulsing state
- mStatusBar.mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class),
- DozeLog.PULSE_REASON_NOTIFICATION);
+ when(mDozeServiceHost.isPulsing()).thenReturn(true);
+ mStatusBar.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.PULSING), any());
// Ending a pulse should take it back to keyguard state
- pulseCallback[0].onPulseFinished();
+ when(mDozeServiceHost.isPulsing()).thenReturn(false);
+ mStatusBar.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.KEYGUARD));
}
@Test
- public void testPulseWhileDozing_notifyAuthInterrupt() {
- HashSet<Integer> reasonsWantingAuth = new HashSet<>(
- Collections.singletonList(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN));
- HashSet<Integer> reasonsSkippingAuth = new HashSet<>(
- Arrays.asList(DozeLog.PULSE_REASON_INTENT,
- DozeLog.PULSE_REASON_NOTIFICATION,
- DozeLog.PULSE_REASON_SENSOR_SIGMOTION,
- DozeLog.REASON_SENSOR_PICKUP,
- DozeLog.REASON_SENSOR_DOUBLE_TAP,
- DozeLog.PULSE_REASON_SENSOR_LONG_PRESS,
- DozeLog.PULSE_REASON_DOCKING,
- DozeLog.REASON_SENSOR_WAKE_UP,
- DozeLog.REASON_SENSOR_TAP));
- HashSet<Integer> reasonsThatDontPulse = new HashSet<>(
- Arrays.asList(DozeLog.REASON_SENSOR_PICKUP,
- DozeLog.REASON_SENSOR_DOUBLE_TAP,
- DozeLog.REASON_SENSOR_TAP));
-
- doAnswer(invocation -> {
- DozeHost.PulseCallback callback = invocation.getArgument(0);
- callback.onPulseStarted();
- return null;
- }).when(mDozeScrimController).pulse(any(), anyInt());
-
- mStatusBar.mDozeServiceHost.mWakeLockScreenPerformsAuth = true;
- for (int i = 0; i < DozeLog.REASONS; i++) {
- reset(mKeyguardUpdateMonitor);
- mStatusBar.mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class), i);
- if (reasonsWantingAuth.contains(i)) {
- verify(mKeyguardUpdateMonitor).onAuthInterruptDetected(eq(true));
- } else if (reasonsSkippingAuth.contains(i) || reasonsThatDontPulse.contains(i)) {
- verify(mKeyguardUpdateMonitor, never()).onAuthInterruptDetected(eq(true));
- } else {
- throw new AssertionError("Reason " + i + " isn't specified as wanting or skipping"
- + " passive auth. Please consider how this pulse reason should behave.");
- }
- }
- }
-
- @Test
- public void testPulseWhileDozingWithDockingReason_suppressWakeUpGesture() {
- // Keep track of callback to be able to stop the pulse
- final DozeHost.PulseCallback[] pulseCallback = new DozeHost.PulseCallback[1];
- doAnswer(invocation -> {
- pulseCallback[0] = invocation.getArgument(0);
- return null;
- }).when(mDozeScrimController).pulse(any(), anyInt());
-
- // Starting a pulse while docking should suppress wakeup gesture
- mStatusBar.mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class),
- DozeLog.PULSE_REASON_DOCKING);
- verify(mStatusBarWindowView).suppressWakeUpGesture(eq(true));
-
- // Ending a pulse should restore wakeup gesture
- pulseCallback[0].onPulseFinished();
- verify(mStatusBarWindowView).suppressWakeUpGesture(eq(false));
- }
-
- @Test
public void testSetState_changesIsFullScreenUserSwitcherState() {
mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
assertFalse(mStatusBar.isFullScreenUserSwitcherState());
@@ -731,34 +786,23 @@ public class StatusBarTest extends SysuiTestCase {
verify(mStatusBarStateController).setState(eq(StatusBarState.KEYGUARD));
// If useFullscreenUserSwitcher is true, state is set to FULLSCREEN_USER_SWITCHER.
- mStatusBar.mUserSwitcherController = mock(UserSwitcherController.class);
- when(mStatusBar.mUserSwitcherController.useFullscreenUserSwitcher()).thenReturn(true);
+ when(mUserSwitcherController.useFullscreenUserSwitcher()).thenReturn(true);
mStatusBar.showKeyguardImpl();
verify(mStatusBarStateController).setState(eq(StatusBarState.FULLSCREEN_USER_SWITCHER));
}
@Test
- public void testStartStopDozing() {
- mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
- when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true);
-
- mStatusBar.mDozeServiceHost.startDozing();
- verify(mStatusBarStateController).setIsDozing(eq(true));
-
- mStatusBar.mDozeServiceHost.stopDozing();
- verify(mStatusBarStateController).setIsDozing(eq(false));
- }
-
- @Test
public void testOnStartedWakingUp_isNotDozing() {
mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true);
- mStatusBar.mDozeServiceHost.startDozing();
- verify(mStatusBarStateController).setIsDozing(eq(true));
+ when(mDozeServiceHost.getDozingRequested()).thenReturn(true);
+ mStatusBar.updateIsKeyguard();
+ // TODO: mNotificationPanelView.expand(false) gets called twice. Should be once.
+ verify(mNotificationPanelView, times(2)).expand(eq(false));
clearInvocations(mNotificationPanelView);
mStatusBar.mWakefulnessObserver.onStartedWakingUp();
- verify(mStatusBarStateController).setIsDozing(eq(false));
+ verify(mDozeServiceHost).stopDozing();
verify(mNotificationPanelView).expand(eq(false));
}
@@ -766,7 +810,8 @@ public class StatusBarTest extends SysuiTestCase {
public void testOnStartedWakingUp_doesNotDismissBouncer_whenPulsing() {
mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true);
- mStatusBar.mDozeServiceHost.startDozing();
+ when(mDozeServiceHost.getDozingRequested()).thenReturn(true);
+ mStatusBar.updateIsKeyguard();
clearInvocations(mNotificationPanelView);
mStatusBar.setBouncerShowing(true);
@@ -774,123 +819,42 @@ public class StatusBarTest extends SysuiTestCase {
verify(mNotificationPanelView, never()).expand(anyBoolean());
}
- static class TestableStatusBar extends StatusBar {
- public TestableStatusBar(StatusBarKeyguardViewManager man,
- UnlockMethodCache unlock, KeyguardIndicationController key,
- NotificationStackScrollLayout stack,
- PowerManager pm, NotificationPanelView panelView,
- IStatusBarService barService, NotificationListener notificationListener,
- NotificationLogger notificationLogger,
- VisualStabilityManager visualStabilityManager,
- NotificationViewHierarchyManager viewHierarchyManager,
- TestableNotificationEntryManager entryManager, ScrimController scrimController,
- BiometricUnlockController biometricUnlockController,
- KeyguardViewMediator keyguardViewMediator,
- NotificationRemoteInputManager notificationRemoteInputManager,
- NotificationGroupManager notificationGroupManager,
- NotificationGroupAlertTransferHelper notificationGroupAlertTransferHelper,
- FalsingManager falsingManager,
- StatusBarWindowController statusBarWindowController,
- NotificationIconAreaController notificationIconAreaController,
- DozeScrimController dozeScrimController,
- NotificationShelf notificationShelf,
- NotificationLockscreenUserManager notificationLockscreenUserManager,
- CommandQueue commandQueue,
- StatusBarNotificationPresenter notificationPresenter,
- BubbleController bubbleController,
- NavigationBarController navBarController,
- AutoHideController autoHideController,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- StatusBarWindowView statusBarWindow) {
- mStatusBarKeyguardViewManager = man;
- mUnlockMethodCache = unlock;
- mKeyguardIndicationController = key;
- mStackScroller = stack;
- mPowerManager = pm;
- mNotificationPanel = panelView;
- mBarService = barService;
- mNotificationListener = notificationListener;
- mNotificationLogger = notificationLogger;
- mWakefulnessLifecycle = createAwakeWakefulnessLifecycle();
- mVisualStabilityManager = visualStabilityManager;
- mViewHierarchyManager = viewHierarchyManager;
- mEntryManager = entryManager;
- mScrimController = scrimController;
- mBiometricUnlockController = biometricUnlockController;
- mKeyguardViewMediator = keyguardViewMediator;
- mRemoteInputManager = notificationRemoteInputManager;
- mGroupManager = notificationGroupManager;
- mGroupAlertTransferHelper = notificationGroupAlertTransferHelper;
- mFalsingManager = falsingManager;
- mStatusBarWindowController = statusBarWindowController;
- mNotificationIconAreaController = notificationIconAreaController;
- mDozeScrimController = dozeScrimController;
- mNotificationShelf = notificationShelf;
- mLockscreenUserManager = notificationLockscreenUserManager;
- mCommandQueue = commandQueue;
- mPresenter = notificationPresenter;
- mGestureWakeLock = mock(PowerManager.WakeLock.class);
- mBubbleController = bubbleController;
- mNavigationBarController = navBarController;
- mAutoHideController = autoHideController;
- mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mStatusBarWindow = statusBarWindow;
- mDozeServiceHost.mWakeLockScreenPerformsAuth = false;
- }
-
- private WakefulnessLifecycle createAwakeWakefulnessLifecycle() {
- WakefulnessLifecycle wakefulnessLifecycle = new WakefulnessLifecycle();
- wakefulnessLifecycle.dispatchStartedWakingUp();
- wakefulnessLifecycle.dispatchFinishedWakingUp();
- return wakefulnessLifecycle;
- }
-
- @Override
- protected void updateTheme() {
- // Do nothing for now, until we have more mocking and StatusBar is smaller.
- }
-
- public void setBarStateForTest(int state) {
- mState = state;
- }
-
- void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
- mHeadsUpManager = headsUpManager;
- }
-
- public void setUserSetupForTest(boolean userSetup) {
- mUserSetup = userSetup;
- }
-
+ @Test
+ public void testRegisterBroadcastsonDispatcher() {
+ mStatusBar.registerBroadcastReceiver();
+ verify(mBroadcastDispatcher).registerReceiver(
+ any(BroadcastReceiver.class),
+ any(IntentFilter.class),
+ eq(null),
+ any(UserHandle.class));
}
public static class TestableNotificationEntryManager extends NotificationEntryManager {
- public TestableNotificationEntryManager(Context context) {
- super(context);
+ public TestableNotificationEntryManager(NotificationData notificationData) {
+ super(notificationData, mock(NotifLog.class));
}
public void setUpForTest(NotificationPresenter presenter,
NotificationListContainer listContainer,
- HeadsUpManagerPhone headsUpManager,
- NotificationData notificationData) {
+ HeadsUpManagerPhone headsUpManager) {
super.setUpWithPresenter(presenter, listContainer, headsUpManager);
- mNotificationData = notificationData;
}
}
public static class TestableNotificationInterruptionStateProvider extends
NotificationInterruptionStateProvider {
- public TestableNotificationInterruptionStateProvider(
+ TestableNotificationInterruptionStateProvider(
Context context,
PowerManager powerManager,
IDreamManager dreamManager,
AmbientDisplayConfiguration ambientDisplayConfiguration,
NotificationFilter filter,
- StatusBarStateController controller) {
+ StatusBarStateController controller,
+ BatteryController batteryController) {
super(context, powerManager, dreamManager, ambientDisplayConfiguration, filter,
- controller);
+ batteryController, controller);
mUseHeadsUp = true;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java
index 4ffaeaef77b4..a21a658348c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java
@@ -32,7 +32,9 @@ import android.view.WindowManager;
import androidx.test.filters.SmallTest;
+import com.android.internal.colorextraction.ColorExtractor;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -48,20 +50,15 @@ import org.mockito.MockitoAnnotations;
@SmallTest
public class StatusBarWindowControllerTest extends SysuiTestCase {
- @Mock
- private WindowManager mWindowManager;
- @Mock
- private DozeParameters mDozeParameters;
- @Mock
- private ViewGroup mStatusBarView;
- @Mock
- private IActivityManager mActivityManager;
- @Mock
- private SysuiStatusBarStateController mStatusBarStateController;
- @Mock
- private ConfigurationController mConfigurationController;
- @Mock
- private KeyguardBypassController mKeyguardBypassController;
+ @Mock private WindowManager mWindowManager;
+ @Mock private DozeParameters mDozeParameters;
+ @Mock private ViewGroup mStatusBarView;
+ @Mock private IActivityManager mActivityManager;
+ @Mock private SysuiStatusBarStateController mStatusBarStateController;
+ @Mock private ConfigurationController mConfigurationController;
+ @Mock private KeyguardBypassController mKeyguardBypassController;
+ @Mock private SysuiColorExtractor mColorExtractor;
+ @Mock ColorExtractor.GradientColors mGradientColors;
private StatusBarWindowController mStatusBarWindowController;
@@ -69,10 +66,11 @@ public class StatusBarWindowControllerTest extends SysuiTestCase {
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+ when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
mStatusBarWindowController = new StatusBarWindowController(mContext, mWindowManager,
mActivityManager, mDozeParameters, mStatusBarStateController,
- mConfigurationController, mKeyguardBypassController);
+ mConfigurationController, mKeyguardBypassController, mColorExtractor);
mStatusBarWindowController.add(mStatusBarView, 100 /* height */);
}
@@ -96,9 +94,6 @@ public class StatusBarWindowControllerTest extends SysuiTestCase {
@Test
public void testOnThemeChanged_doesntCrash() {
- mStatusBarWindowController = new StatusBarWindowController(mContext, mWindowManager,
- mActivityManager, mDozeParameters, mStatusBarStateController,
- mConfigurationController, mKeyguardBypassController);
mStatusBarWindowController.onThemeChanged();
}
@@ -109,9 +104,6 @@ public class StatusBarWindowControllerTest extends SysuiTestCase {
@Test
public void testSetForcePluginOpen_beforeStatusBarInitialization() {
- mStatusBarWindowController = new StatusBarWindowController(mContext, mWindowManager,
- mActivityManager, mDozeParameters, mStatusBarStateController,
- mConfigurationController, mKeyguardBypassController);
mStatusBarWindowController.setForcePluginOpen(true);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
index 00d87c399293..7c1dfa6c4f54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
@@ -16,55 +16,97 @@
package com.android.systemui.statusbar.phone;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.os.SystemClock;
+import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.MotionEvent;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-import com.android.systemui.Dependency;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.doze.DozeLog;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.DragDownHelper;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.InjectionInflationController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
-@RunWith(AndroidJUnit4.class)
-@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class StatusBarWindowViewTest extends SysuiTestCase {
private StatusBarWindowView mView;
- private StatusBar mStatusBar;
- private DragDownHelper mDragDownHelper;
- private NotificationStackScrollLayout mStackScrollLayout;
+ private StatusBarWindowViewController mController;
+
+ @Mock private NotificationWakeUpCoordinator mCoordinator;
+ @Mock private PulseExpansionHandler mPulseExpansionHandler;
+ @Mock private DynamicPrivacyController mDynamicPrivacyController;
+ @Mock private KeyguardBypassController mBypassController;
+ @Mock private PluginManager mPluginManager;
+ @Mock private TunerService mTunerService;
+ @Mock private DragDownHelper mDragDownHelper;
+ @Mock private KeyguardStateController mKeyguardStateController;
+ @Mock private SysuiStatusBarStateController mStatusBarStateController;
+ @Mock private ShadeController mShadeController;
+ @Mock private NotificationLockscreenUserManager mNotificationLockScreenUserManager;
+ @Mock private NotificationEntryManager mNotificationEntryManager;
+ @Mock private StatusBar mStatusBar;
+ @Mock private DozeLog mDozeLog;
+ @Mock private DozeParameters mDozeParameters;
@Before
public void setUp() {
- mDependency.injectMockDependency(StatusBarStateController.class);
- mView = spy(new StatusBarWindowView(getContext(), null));
- mStackScrollLayout = mock(NotificationStackScrollLayout.class);
- when(mView.getStackScrollLayout()).thenReturn(mStackScrollLayout);
- mStatusBar = mock(StatusBar.class);
- mView.setService(mStatusBar);
- mDragDownHelper = mock(DragDownHelper.class);
- mView.setDragDownHelper(mDragDownHelper);
+ MockitoAnnotations.initMocks(this);
+
+ mView = new StatusBarWindowView(getContext(), null);
+ mContext.putComponent(StatusBar.class, mStatusBar);
+ when(mStatusBar.isDozing()).thenReturn(false);
+ mDependency.injectTestDependency(ShadeController.class, mShadeController);
+
+ mController = new StatusBarWindowViewController.Builder(
+ new InjectionInflationController(
+ SystemUIFactory.getInstance().getRootComponent()),
+ mCoordinator,
+ mPulseExpansionHandler,
+ mDynamicPrivacyController,
+ mBypassController,
+ new FalsingManagerFake(),
+ mPluginManager,
+ mTunerService,
+ mNotificationLockScreenUserManager,
+ mNotificationEntryManager,
+ mKeyguardStateController,
+ mStatusBarStateController,
+ mDozeLog,
+ mDozeParameters)
+ .setShadeController(mShadeController)
+ .setStatusBarWindowView(mView)
+ .build();
+ mController.setService(mStatusBar);
+ mController.setDragDownHelper(mDragDownHelper);
+
}
@Test
- public void testDragDownHelperCalledWhenDraggingDown() throws Exception {
- when(Dependency.get(StatusBarStateController.class).getState())
- .thenReturn(StatusBarState.SHADE);
+ public void testDragDownHelperCalledWhenDraggingDown() {
when(mDragDownHelper.isDraggingDown()).thenReturn(true);
long now = SystemClock.elapsedRealtime();
MotionEvent ev = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, 0 /* x */, 0 /* y */,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
index a9a1392fb80b..589aa0353870 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
@@ -18,11 +18,9 @@ import static junit.framework.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.content.BroadcastReceiver;
-import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.testing.AndroidTestingRunner;
@@ -31,11 +29,14 @@ import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
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;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -43,13 +44,16 @@ import org.mockito.ArgumentCaptor;
public class SystemUIDialogTest extends SysuiTestCase {
private SystemUIDialog mDialog;
-
- Context mContextSpy;
+ @Mock
+ private BroadcastDispatcher mBroadcastDispatcher;
@Before
public void setup() {
- mContextSpy = spy(mContext);
- mDialog = new SystemUIDialog(mContextSpy);
+ MockitoAnnotations.initMocks(this);
+
+ mDependency.injectTestDependency(BroadcastDispatcher.class, mBroadcastDispatcher);
+
+ mDialog = new SystemUIDialog(mContext);
}
@Test
@@ -60,12 +64,12 @@ public class SystemUIDialogTest extends SysuiTestCase {
ArgumentCaptor.forClass(IntentFilter.class);
mDialog.show();
- verify(mContextSpy).registerReceiverAsUser(broadcastReceiverCaptor.capture(), any(),
- intentFilterCaptor.capture(), any(), any());
+ verify(mBroadcastDispatcher).registerReceiver(broadcastReceiverCaptor.capture(),
+ intentFilterCaptor.capture(), eq(null), any());
assertTrue(intentFilterCaptor.getValue().hasAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
mDialog.dismiss();
- verify(mContextSpy).unregisterReceiver(eq(broadcastReceiverCaptor.getValue()));
+ verify(mBroadcastDispatcher).unregisterReceiver(eq(broadcastReceiverCaptor.getValue()));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
index 6b83fed64ddf..fc7d0ce30687 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
@@ -73,7 +73,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest {
.getRecommendedTimeoutMillis(anyInt(), anyInt());
mHeadsUpManager.showNotification(mEntry);
Runnable pastNormalTimeRunnable =
- () -> mLivesPastNormalTime = mHeadsUpManager.isAlerting(mEntry.key);
+ () -> mLivesPastNormalTime = mHeadsUpManager.isAlerting(mEntry.getKey());
mTestHandler.postDelayed(pastNormalTimeRunnable,
(TEST_A11Y_AUTO_DISMISS_TIME + TEST_AUTO_DISMISS_TIME) / 2);
mTestHandler.postDelayed(TEST_TIMEOUT_RUNNABLE, TEST_A11Y_TIMEOUT_TIME);
@@ -82,7 +82,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest {
assertFalse("Test timed out", mTimedOut);
assertTrue("Heads up should live long enough", mLivesPastNormalTime);
- assertFalse(mHeadsUpManager.isAlerting(mEntry.key));
+ assertFalse(mHeadsUpManager.isAlerting(mEntry.getKey()));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyButtonViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyButtonViewTest.java
index d16dc168d74c..943674a7bf64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyButtonViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyButtonViewTest.java
@@ -40,6 +40,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.recents.OverviewProxyService;
import org.junit.Before;
import org.junit.Test;
@@ -68,6 +69,7 @@ public class KeyButtonViewTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
mBubbleController = mDependency.injectMockDependency(BubbleController.class);
+ mDependency.injectMockDependency(OverviewProxyService.class);
TestableLooper.get(this).runWithLooper(() -> {
mKeyButtonView = new KeyButtonView(mContext, null, 0, mInputManager);
});
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
new file mode 100644
index 000000000000..e57bbc1623f0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+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.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+public class KeyguardStateControllerTest extends SysuiTestCase {
+
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ private LockPatternUtils mLockPatternUtils;
+ private KeyguardStateController mKeyguardStateController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mKeyguardStateController = new KeyguardStateControllerImpl(mContext,
+ mKeyguardUpdateMonitor, mLockPatternUtils);
+ }
+
+ @Test
+ public void testAddCallback_registersListener() {
+ verify(mKeyguardUpdateMonitor).registerCallback(any());
+ }
+
+ @Test
+ public void testIsShowing() {
+ assertThat(mKeyguardStateController.isShowing()).isFalse();
+ mKeyguardStateController.notifyKeyguardState(true /* showing */, false /* occluded */);
+ assertThat(mKeyguardStateController.isShowing()).isTrue();
+ }
+
+ @Test
+ public void testIsMethodSecure() {
+ assertThat(mKeyguardStateController.isMethodSecure()).isFalse();
+
+ when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true);
+ ((KeyguardStateControllerImpl) mKeyguardStateController).update(false /* alwaysUpdate */);
+ assertThat(mKeyguardStateController.isMethodSecure()).isTrue();
+ }
+
+ @Test
+ public void testIsOccluded() {
+ assertThat(mKeyguardStateController.isOccluded()).isFalse();
+ mKeyguardStateController.notifyKeyguardState(false /* showing */, true /* occluded */);
+ assertThat(mKeyguardStateController.isOccluded()).isTrue();
+ }
+
+ @Test
+ public void testCanSkipLockScreen() {
+ // Can skip because LockPatternUtils#isSecure is false
+ assertThat(mKeyguardStateController.canDismissLockScreen()).isTrue();
+
+ // Cannot skip after there's a password/pin/pattern
+ when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true);
+ ((KeyguardStateControllerImpl) mKeyguardStateController).update(false /* alwaysUpdate */);
+ assertThat(mKeyguardStateController.canDismissLockScreen()).isFalse();
+
+ // Unless user is authenticated
+ when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(anyInt())).thenReturn(true);
+ ((KeyguardStateControllerImpl) mKeyguardStateController).update(false /* alwaysUpdate */);
+ assertThat(mKeyguardStateController.canDismissLockScreen()).isTrue();
+ }
+
+ @Test
+ public void testIsUnlocked() {
+ // Is unlocked whenever the keyguard is not showing
+ assertThat(mKeyguardStateController.isShowing()).isFalse();
+ assertThat(mKeyguardStateController.isUnlocked()).isTrue();
+
+ // Unlocked if showing, but insecure
+ mKeyguardStateController.notifyKeyguardState(true /* showing */, false /* occluded */);
+ assertThat(mKeyguardStateController.isUnlocked()).isTrue();
+
+ // Locked if showing, and requires password
+ when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true);
+ ((KeyguardStateControllerImpl) mKeyguardStateController).update(false /* alwaysUpdate */);
+ assertThat(mKeyguardStateController.isUnlocked()).isFalse();
+
+ // But unlocked after #getUserCanSkipBouncer allows it
+ when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(anyInt())).thenReturn(true);
+ ((KeyguardStateControllerImpl) mKeyguardStateController).update(false /* alwaysUpdate */);
+ assertThat(mKeyguardStateController.isUnlocked()).isTrue();
+ }
+
+ @Test
+ public void testIsTrusted() {
+ assertThat(mKeyguardStateController.isTrusted()).isFalse();
+
+ when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
+ ((KeyguardStateControllerImpl) mKeyguardStateController).update(false /* alwaysUpdate */);
+
+ assertThat(mKeyguardStateController.isTrusted()).isTrue();
+ }
+
+ @Test
+ public void testCallbacksAreInvoked() {
+ KeyguardStateController.Callback callback = mock(KeyguardStateController.Callback.class);
+ mKeyguardStateController.addCallback(callback);
+
+ when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
+ ((KeyguardStateControllerImpl) mKeyguardStateController).update(false /* alwaysUpdate */);
+
+ verify(callback).onUnlockedChanged();
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index bc468bf2fb82..390e812b3613 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -106,7 +106,8 @@ public class RemoteInputViewTest extends SysuiTestCase {
@Test
public void testSendRemoteInput_intentContainsResultsAndSource() throws Exception {
- ExpandableNotificationRow row = new NotificationTestHelper(mContext).createRow();
+ ExpandableNotificationRow row = new NotificationTestHelper(mContext, mDependency)
+ .createRow();
RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController);
setTestPendingIntent(view);
@@ -127,7 +128,7 @@ public class RemoteInputViewTest extends SysuiTestCase {
private UserHandle getTargetInputMethodUser(UserHandle fromUser, UserHandle toUser)
throws Exception {
- ExpandableNotificationRow row = new NotificationTestHelper(mContext).createRow(
+ ExpandableNotificationRow row = new NotificationTestHelper(mContext, mDependency).createRow(
DUMMY_MESSAGE_APP_PKG,
UserHandle.getUid(fromUser.getIdentifier(), DUMMY_MESSAGE_APP_ID),
toUser);
@@ -169,7 +170,8 @@ public class RemoteInputViewTest extends SysuiTestCase {
@Test
public void testNoCrashWithoutVisibilityListener() throws Exception {
- ExpandableNotificationRow row = new NotificationTestHelper(mContext).createRow();
+ ExpandableNotificationRow row = new NotificationTestHelper(mContext, mDependency)
+ .createRow();
RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController);
view.setOnVisibilityChangedListener(null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 0d56cbe84eb0..b5e4cb965de8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -52,6 +52,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.statusbar.NotificationEntryBuilder;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
@@ -115,6 +116,7 @@ public class SmartReplyViewTest extends SysuiTestCase {
});
mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
mDependency.injectMockDependency(ShadeController.class);
+ mDependency.injectMockDependency(NotificationRemoteInputManager.class);
mDependency.injectTestDependency(ActivityStarter.class, mActivityStarter);
mDependency.injectTestDependency(SmartReplyConstants.class, mConstants);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java
new file mode 100644
index 000000000000..54cb0b83fc8f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.sensors;
+
+import android.content.res.Resources;
+
+public class FakeProximitySensor extends ProximitySensor {
+ private boolean mAvailable;
+ private boolean mPaused;
+
+ public FakeProximitySensor(Resources resources, AsyncSensorManager sensorManager) {
+ super(resources, sensorManager);
+
+ mAvailable = true;
+ }
+
+ public void setSensorAvailable(boolean available) {
+ mAvailable = available;
+ }
+
+ public void setLastEvent(ProximityEvent event) {
+ mLastEvent = event;
+ }
+
+ @Override
+ public boolean getSensorAvailable() {
+ return mAvailable;
+ }
+
+ @Override
+ protected void registerInternal() {
+ // no-op
+ }
+
+ @Override
+ protected void unregisterInternal() {
+ // no-op
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java
index 1deb495b5250..0bc7868aab9c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java
@@ -92,6 +92,18 @@ public class FakeSensorManager extends SensorManager {
if (s != null) {
return s;
}
+ switch(type) {
+ case Sensor.TYPE_PROXIMITY:
+ try {
+ return createSensor(Sensor.TYPE_PROXIMITY, null);
+ } catch (Exception e) {
+ // fall through
+ }
+ break;
+ default:
+ break;
+
+ }
// Our mock sensors aren't wakeup, and it's a pain to create them that way. Instead, just
// return non-wakeup sensors if we can't find a wakeup sensor.
return getDefaultSensor(type, false /* wakeup */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java
index 6d13408058cc..37b315fec26e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java
@@ -47,7 +47,7 @@ public class ProximitySensorTest extends SysuiTestCase {
AsyncSensorManager asyncSensorManager = new AsyncSensorManager(
sensorManager, null, new Handler());
mFakeProximitySensor = sensorManager.getFakeProximitySensor();
- mProximitySensor = new ProximitySensor(getContext(), asyncSensorManager, null);
+ mProximitySensor = new ProximitySensor(getContext().getResources(), asyncSensorManager);
}
@Test
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 a843cca498a0..df76f01494f3 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
@@ -48,4 +48,9 @@ public class FakeBatteryController extends BaseLeakChecker<BatteryStateChangeCal
public boolean isPowerSave() {
return false;
}
+
+ @Override
+ public boolean isAodPowerSave() {
+ return false;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java
deleted file mode 100644
index 2fb0e0e7caf8..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java
+++ /dev/null
@@ -1,83 +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.utils.leaks;
-
-import android.testing.LeakCheck;
-
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
-
-public class FakeKeyguardMonitor implements KeyguardMonitor {
-
- private final BaseLeakChecker<Callback> mCallbackController;
-
- public FakeKeyguardMonitor(LeakCheck test) {
- mCallbackController = new BaseLeakChecker<Callback>(test, "keyguard");
- }
-
- @Override
- public void addCallback(Callback callback) {
- mCallbackController.addCallback(callback);
- }
-
- @Override
- public void removeCallback(Callback callback) {
- mCallbackController.removeCallback(callback);
- }
-
- @Override
- public boolean isSecure() {
- return false;
- }
-
- @Override
- public boolean isShowing() {
- return false;
- }
-
- @Override
- public boolean isOccluded() {
- return false;
- }
-
- @Override
- public boolean isKeyguardFadingAway() {
- return false;
- }
-
- @Override
- public boolean isKeyguardGoingAway() {
- return false;
- }
-
- @Override
- public boolean isLaunchTransitionFadingAway() {
- return false;
- }
-
- @Override
- public long getKeyguardFadingAwayDuration() {
- return 0;
- }
-
- @Override
- public long getKeyguardFadingAwayDelay() {
- return 0;
- }
-
- @Override
- public long calculateGoingToFullShadeDelay() {
- return 0;
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
new file mode 100644
index 000000000000..26cac290c9b5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
@@ -0,0 +1,95 @@
+/*
+ * 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.utils.leaks;
+
+import android.testing.LeakCheck;
+
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+public class FakeKeyguardStateController implements KeyguardStateController {
+
+ private final BaseLeakChecker<Callback> mCallbackController;
+
+ public FakeKeyguardStateController(LeakCheck test) {
+ mCallbackController = new BaseLeakChecker<Callback>(test, "keyguard");
+ }
+
+ @Override
+ public void addCallback(Callback callback) {
+ mCallbackController.addCallback(callback);
+ }
+
+ @Override
+ public void removeCallback(Callback callback) {
+ mCallbackController.removeCallback(callback);
+ }
+
+ @Override
+ public boolean isMethodSecure() {
+ return false;
+ }
+
+ @Override
+ public boolean isShowing() {
+ return false;
+ }
+
+ @Override
+ public boolean canDismissLockScreen() {
+ return false;
+ }
+
+ @Override
+ public boolean isOccluded() {
+ return false;
+ }
+
+ @Override
+ public boolean isTrusted() {
+ return false;
+ }
+
+ @Override
+ public boolean isKeyguardFadingAway() {
+ return false;
+ }
+
+ @Override
+ public boolean isKeyguardGoingAway() {
+ return false;
+ }
+
+ @Override
+ public boolean isLaunchTransitionFadingAway() {
+ return false;
+ }
+
+ @Override
+ public long getKeyguardFadingAwayDuration() {
+ return 0;
+ }
+
+ @Override
+ public long getKeyguardFadingAwayDelay() {
+ return 0;
+ }
+
+ @Override
+ public long calculateGoingToFullShadeDelay() {
+ return 0;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
index f47912623e1f..fedc08d93bc7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
@@ -14,8 +14,6 @@
package com.android.systemui.utils.leaks;
-import static org.mockito.Matchers.any;
-
import android.testing.LeakCheck;
import android.util.ArrayMap;
@@ -29,7 +27,7 @@ import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NextAlarmController;
@@ -60,7 +58,7 @@ public abstract class LeakCheckedTest extends SysuiTestCase {
HotspotController.class,
FlashlightController.class,
UserInfoController.class,
- KeyguardMonitor.class,
+ KeyguardStateController.class,
BatteryController.class,
SecurityController.class,
ManagedProfileController.class,
@@ -118,8 +116,8 @@ public abstract class LeakCheckedTest extends SysuiTestCase {
obj = new FakeFlashlightController(this);
} else if (cls == UserInfoController.class) {
obj = new FakeUserInfoController(this);
- } else if (cls == KeyguardMonitor.class) {
- obj = new FakeKeyguardMonitor(this);
+ } else if (cls == KeyguardStateController.class) {
+ obj = new FakeKeyguardStateController(this);
} else if (cls == BatteryController.class) {
obj = new FakeBatteryController(this);
} else if (cls == SecurityController.class) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
index f4d0854b2c9f..2e945f2481d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -16,41 +16,64 @@
package com.android.systemui.volume;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.session.MediaSession;
+import android.os.Handler;
+import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.keyguard.WakefulnessLifecycle;
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;
+@RunWith(AndroidTestingRunner.class)
@SmallTest
public class VolumeDialogControllerImplTest extends SysuiTestCase {
TestableVolumeDialogControllerImpl mVolumeController;
VolumeDialogControllerImpl.C mCallback;
StatusBar mStatusBar;
+ @Mock
+ private BroadcastDispatcher mBroadcastDispatcher;
@Before
public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
mCallback = mock(VolumeDialogControllerImpl.C.class);
mStatusBar = mock(StatusBar.class);
- mVolumeController = new TestableVolumeDialogControllerImpl(mContext, mCallback, mStatusBar);
+ mVolumeController = new TestableVolumeDialogControllerImpl(mContext, mCallback, mStatusBar,
+ mBroadcastDispatcher);
mVolumeController.setEnableDialogs(true, true);
}
@Test
+ public void testRegisteredWithDispatcher() {
+ verify(mBroadcastDispatcher).registerReceiver(
+ any(BroadcastReceiver.class),
+ any(IntentFilter.class),
+ any(Handler.class)); // VolumeDialogControllerImpl does not call with user
+ }
+
+ @Test
public void testVolumeChangeW_deviceNotInteractiveAOD() {
when(mStatusBar.isDeviceInteractive()).thenReturn(false);
when(mStatusBar.getWakefulnessState()).thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE);
@@ -81,7 +104,7 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
public void testVolumeChangeW_nullStatusBar() {
VolumeDialogControllerImpl.C callback = mock(VolumeDialogControllerImpl.C.class);
TestableVolumeDialogControllerImpl nullStatusBarTestableDialog = new
- TestableVolumeDialogControllerImpl(mContext, callback, null);
+ TestableVolumeDialogControllerImpl(mContext, callback, null, mBroadcastDispatcher);
nullStatusBarTestableDialog.setEnableDialogs(true, true);
nullStatusBarTestableDialog.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI);
verify(callback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
@@ -100,8 +123,9 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
}
static class TestableVolumeDialogControllerImpl extends VolumeDialogControllerImpl {
- public TestableVolumeDialogControllerImpl(Context context, C callback, StatusBar s) {
- super(context);
+ TestableVolumeDialogControllerImpl(Context context, C callback, StatusBar s,
+ BroadcastDispatcher broadcastDispatcher) {
+ super(context, broadcastDispatcher);
mCallbacks = callback;
mStatusBar = s;
}
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 5b826b1c551b..b0e401bdda8a 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -2584,15 +2584,15 @@ message MetricsEvent {
// ACTION: Logged when trampoline activity finishes.
// TIME: Indicates total time taken by trampoline activity to finish in MS.
- PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 523;
+ PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 523 [deprecated=true];
// ACTION: Logged when encryption activity finishes.
// TIME: Indicates total time taken by post encryption activity to finish in MS.
- PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 524;
+ PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 524 [deprecated=true];
// ACTION: Logged when finalization activity finishes.
// TIME: Indicates time taken by finalization activity to finish in MS.
- PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 525;
+ PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 525 [deprecated=true];
// OPEN: Settings Support > Phone/Chat -> Disclaimer
DIALOG_SUPPORT_DISCLAIMER = 526;
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index ffbf1ae38635..5712566311d7 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -230,6 +230,10 @@ message SystemMessage {
// Package: android
NOTE_TEST_HARNESS_MODE_ENABLED = 54;
+ // Inform the user that Serial Console is active.
+ // Package: android
+ NOTE_SERIAL_CONSOLE_ENABLED = 55;
+
// ADD_NEW_IDS_ABOVE_THIS_LINE
// Legacy IDs with arbitrary values appear below
// Legacy IDs existed as stable non-conflicting constants prior to the O release
@@ -249,6 +253,8 @@ message SystemMessage {
NOTE_NETWORK_LOGGED_IN = 744;
// A partial connectivity network was detected during network validation
NOTE_NETWORK_PARTIAL_CONNECTIVITY = 745;
+ // Private DNS is broken in strict mode
+ NOTE_NETWORK_PRIVATE_DNS_BROKEN = 746;
// Notify the user that their work profile has been deleted
// Package: android
diff --git a/services/Android.bp b/services/Android.bp
index 6953e862f68b..60dd8959fc39 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -47,6 +47,11 @@ java_library {
"compat-changeid-annotation-processor",
],
+ required: [
+ // Required by services.backup
+ "BackupEncryption",
+ ],
+
// Uncomment to enable output of certain warnings (deprecated, unchecked)
//javacflags: ["-Xlint"],
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 4f021ad3cee0..950fa8d4038a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -91,7 +91,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
protected final Context mContext;
protected final SystemSupport mSystemSupport;
protected final WindowManagerInternal mWindowManagerService;
- private final GlobalActionPerformer mGlobalActionPerformer;
+ private final SystemActionPerformer mSystemActionPerformer;
private final AccessibilityWindowManager mA11yWindowManager;
private final DisplayManager mDisplayManager;
private final PowerManager mPowerManager;
@@ -213,7 +213,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
Object lock, AccessibilitySecurityPolicy securityPolicy, SystemSupport systemSupport,
WindowManagerInternal windowManagerInternal,
- GlobalActionPerformer globalActionPerfomer,
+ SystemActionPerformer systemActionPerfomer,
AccessibilityWindowManager a11yWindowManager) {
mContext = context;
mWindowManagerService = windowManagerInternal;
@@ -222,7 +222,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
mAccessibilityServiceInfo = accessibilityServiceInfo;
mLock = lock;
mSecurityPolicy = securityPolicy;
- mGlobalActionPerformer = globalActionPerfomer;
+ mSystemActionPerformer = systemActionPerfomer;
mSystemSupport = systemSupport;
mInvocationHandler = new InvocationHandler(mainHandler.getLooper());
mA11yWindowManager = a11yWindowManager;
@@ -350,9 +350,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
protected abstract boolean hasRightsToCurrentUserLocked();
+ @Nullable
@Override
- public List<AccessibilityWindowInfo> getWindows() {
- ensureWindowsAvailableTimed(Display.DEFAULT_DISPLAY);
+ public AccessibilityWindowInfo.WindowListSparseArray getWindows() {
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
return null;
@@ -362,38 +362,39 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
if (!permissionGranted) {
return null;
}
- List<AccessibilityWindowInfo> internalWindowList =
- mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
- if (internalWindowList == null) {
- return null;
- }
if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
return null;
}
- List<AccessibilityWindowInfo> returnedWindowList = new ArrayList<>();
- final int windowCount = internalWindowList.size();
- for (int i = 0; i < windowCount; i++) {
- AccessibilityWindowInfo window = internalWindowList.get(i);
- AccessibilityWindowInfo windowClone =
- AccessibilityWindowInfo.obtain(window);
- windowClone.setConnectionId(mId);
- returnedWindowList.add(windowClone);
+ final AccessibilityWindowInfo.WindowListSparseArray allWindows =
+ new AccessibilityWindowInfo.WindowListSparseArray();
+ final ArrayList<Integer> displayList = mA11yWindowManager.getDisplayListLocked();
+ final int displayListCounts = displayList.size();
+ if (displayListCounts > 0) {
+ for (int i = 0; i < displayListCounts; i++) {
+ final int displayId = displayList.get(i);
+ ensureWindowsAvailableTimedLocked(displayId);
+
+ final List<AccessibilityWindowInfo> windowList = getWindowsByDisplayLocked(
+ displayId);
+ if (windowList != null) {
+ allWindows.put(displayId, windowList);
+ }
+ }
}
- return returnedWindowList;
+ return allWindows;
}
}
@Override
public AccessibilityWindowInfo getWindow(int windowId) {
- int displayId = Display.INVALID_DISPLAY;
synchronized (mLock) {
+ int displayId = Display.INVALID_DISPLAY;
if (windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
displayId = mA11yWindowManager.getDisplayIdByUserIdAndWindowIdLocked(
mSystemSupport.getCurrentUserIdLocked(), windowId);
}
- }
- ensureWindowsAvailableTimed(displayId);
- synchronized (mLock) {
+ ensureWindowsAvailableTimedLocked(displayId);
+
if (!hasRightsToCurrentUserLocked()) {
return null;
}
@@ -759,7 +760,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
return false;
}
}
- return mGlobalActionPerformer.performGlobalAction(action);
+ return mSystemActionPerformer.performSystemAction(action);
}
@Override
@@ -1316,35 +1317,33 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
*
* @param displayId The logical display id.
*/
- private void ensureWindowsAvailableTimed(int displayId) {
- synchronized (mLock) {
- if (mA11yWindowManager.getWindowListLocked(displayId) != null) {
- return;
- }
- // If we have no registered callback, update the state we
- // we may have to register one but it didn't happen yet.
- if (!mA11yWindowManager.isTrackingWindowsLocked(displayId)) {
- // Invokes client change to make sure tracking window enabled.
- mSystemSupport.onClientChangeLocked(false);
- }
- // We have no windows but do not care about them, done.
- if (!mA11yWindowManager.isTrackingWindowsLocked(displayId)) {
+ private void ensureWindowsAvailableTimedLocked(int displayId) {
+ if (mA11yWindowManager.getWindowListLocked(displayId) != null) {
+ return;
+ }
+ // If we have no registered callback, update the state we
+ // we may have to register one but it didn't happen yet.
+ if (!mA11yWindowManager.isTrackingWindowsLocked(displayId)) {
+ // Invokes client change to make sure tracking window enabled.
+ mSystemSupport.onClientChangeLocked(false);
+ }
+ // We have no windows but do not care about them, done.
+ if (!mA11yWindowManager.isTrackingWindowsLocked(displayId)) {
+ return;
+ }
+
+ // Wait for the windows with a timeout.
+ final long startMillis = SystemClock.uptimeMillis();
+ while (mA11yWindowManager.getWindowListLocked(displayId) == null) {
+ final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
+ final long remainMillis = WAIT_WINDOWS_TIMEOUT_MILLIS - elapsedMillis;
+ if (remainMillis <= 0) {
return;
}
-
- // Wait for the windows with a timeout.
- final long startMillis = SystemClock.uptimeMillis();
- while (mA11yWindowManager.getWindowListLocked(displayId) == null) {
- final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
- final long remainMillis = WAIT_WINDOWS_TIMEOUT_MILLIS - elapsedMillis;
- if (remainMillis <= 0) {
- return;
- }
- try {
- mLock.wait(remainMillis);
- } catch (InterruptedException ie) {
- /* ignore */
- }
+ try {
+ mLock.wait(remainMillis);
+ } catch (InterruptedException ie) {
+ /* ignore */
}
}
}
@@ -1442,6 +1441,24 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
interrogatingPid, interrogatingTid);
}
+ private List<AccessibilityWindowInfo> getWindowsByDisplayLocked(int displayId) {
+ final List<AccessibilityWindowInfo> internalWindowList =
+ mA11yWindowManager.getWindowListLocked(displayId);
+ if (internalWindowList == null) {
+ return null;
+ }
+ final List<AccessibilityWindowInfo> returnedWindowList = new ArrayList<>();
+ final int windowCount = internalWindowList.size();
+ for (int i = 0; i < windowCount; i++) {
+ AccessibilityWindowInfo window = internalWindowList.get(i);
+ AccessibilityWindowInfo windowClone =
+ AccessibilityWindowInfo.obtain(window);
+ windowClone.setConnectionId(mId);
+ returnedWindowList.add(windowClone);
+ }
+ return returnedWindowList;
+ }
+
public ComponentName getComponentName() {
return mComponentName;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 1f11059392a1..68e11df32d79 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -16,13 +16,6 @@
package com.android.server.accessibility;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK;
-
import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -30,6 +23,7 @@ import android.Manifest;
import android.accessibilityservice.AccessibilityGestureEvent;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityShortcutInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -49,8 +43,6 @@ import android.content.pm.PackageManager;
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;
@@ -118,7 +110,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -135,6 +126,7 @@ import java.util.function.Consumer;
*/
public class AccessibilityManagerService extends IAccessibilityManager.Stub
implements AbstractAccessibilityServiceConnection.SystemSupport,
+ AccessibilityUserState.ServiceInfoChangeListener,
AccessibilityWindowManager.AccessibilityEventSender,
AccessibilitySecurityPolicy.AccessibilityUserManager {
@@ -177,12 +169,6 @@ 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 Point mTempPoint = new Point();
-
private final PackageManager mPackageManager;
private final PowerManager mPowerManager;
@@ -197,7 +183,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private final MainHandler mMainHandler;
- private final GlobalActionPerformer mGlobalActionPerformer;
+ private final SystemActionPerformer mSystemActionPerformer;
private MagnificationController mMagnificationController;
@@ -225,7 +211,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private final RemoteCallbackList<IAccessibilityManagerClient> mGlobalClients =
new RemoteCallbackList<>();
- private final SparseArray<UserState> mUserStates = new SparseArray<>();
+ private final SparseArray<AccessibilityUserState> mUserStates = new SparseArray<>();
private final UiAutomationManager mUiAutomationManager = new UiAutomationManager(mLock);
@@ -236,7 +222,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private boolean mIsAccessibilityButtonShown;
- private UserState getCurrentUserStateLocked() {
+ private AccessibilityUserState getCurrentUserStateLocked() {
return getUserStateLocked(mCurrentUserId);
}
@@ -271,7 +257,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
mSecurityPolicy = new AccessibilitySecurityPolicy(mContext, this);
mMainHandler = new MainHandler(mContext.getMainLooper());
- mGlobalActionPerformer = new GlobalActionPerformer(mContext, mWindowManagerService);
+ mSystemActionPerformer = new SystemActionPerformer(mContext, mWindowManagerService);
mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
mWindowManagerService, this, mSecurityPolicy, this);
mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
@@ -292,6 +278,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return mIsAccessibilityButtonShown;
}
+ @Override
+ public void onServiceInfoChangedLocked(AccessibilityUserState userState) {
+ scheduleNotifyClientsOfServicesStateChangeLocked(userState);
+ }
+
@Nullable
public FingerprintGestureDispatcher getFingerprintGestureDispatcher() {
return mFingerprintGestureDispatcher;
@@ -306,40 +297,40 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private UserState getUserState(int userId) {
+ private AccessibilityUserState getUserState(int userId) {
synchronized (mLock) {
return getUserStateLocked(userId);
}
}
- private UserState getUserStateLocked(int userId) {
- UserState state = mUserStates.get(userId);
+ @NonNull
+ private AccessibilityUserState getUserStateLocked(int userId) {
+ AccessibilityUserState state = mUserStates.get(userId);
if (state == null) {
- state = new UserState(userId);
+ state = new AccessibilityUserState(userId, mContext, this);
mUserStates.put(userId, state);
}
return state;
}
boolean getBindInstantServiceAllowed(int userId) {
- final UserState userState = getUserState(userId);
- if (userState == null) return false;
- return userState.getBindInstantServiceAllowed();
+ synchronized (mLock) {
+ final AccessibilityUserState userState = getUserStateLocked(userId);
+ return userState.getBindInstantServiceAllowedLocked();
+ }
}
void setBindInstantServiceAllowed(int userId, boolean allowed) {
- UserState userState;
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.MANAGE_BIND_INSTANT_SERVICE,
+ "setBindInstantServiceAllowed");
synchronized (mLock) {
- userState = getUserState(userId);
- if (userState == null) {
- if (!allowed) {
- return;
- }
- userState = new UserState(userId);
- mUserStates.put(userId, userState);
+ final AccessibilityUserState userState = getUserStateLocked(userId);
+ if (allowed != userState.getBindInstantServiceAllowedLocked()) {
+ userState.setBindInstantServiceAllowedLocked(allowed);
+ onUserStateChangedLocked(userState);
}
}
- userState.setBindInstantServiceAllowed(allowed);
}
private void registerBroadcastReceivers() {
@@ -353,7 +344,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return;
}
// We will update when the automation service dies.
- UserState userState = getCurrentUserStateLocked();
+ AccessibilityUserState userState = getCurrentUserStateLocked();
// We have to reload the installed services since some services may
// have different attributes, resolve info (does not support equals),
// etc. Remove them then to force reload.
@@ -375,9 +366,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (userId != mCurrentUserId) {
return;
}
- UserState userState = getUserStateLocked(userId);
- boolean reboundAService = userState.mBindingServices.removeIf(
+ final AccessibilityUserState userState = getUserStateLocked(userId);
+ final boolean reboundAService = userState.getBindingServicesLocked().removeIf(
component -> component != null
+ && component.getPackageName().equals(packageName))
+ || userState.mCrashedServices.removeIf(component -> component != null
&& component.getPackageName().equals(packageName));
if (reboundAService) {
onUserStateChangedLocked(userState);
@@ -394,14 +387,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (userId != mCurrentUserId) {
return;
}
- UserState userState = getUserStateLocked(userId);
+ AccessibilityUserState userState = getUserStateLocked(userId);
Iterator<ComponentName> it = userState.mEnabledServices.iterator();
while (it.hasNext()) {
ComponentName comp = it.next();
String compPkg = comp.getPackageName();
if (compPkg.equals(packageName)) {
it.remove();
- userState.mBindingServices.remove(comp);
+ userState.getBindingServicesLocked().remove(comp);
+ userState.getCrashedServicesLocked().remove(comp);
// Update the enabled services setting.
persistComponentNamesToSettingLocked(
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
@@ -429,7 +423,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (userId != mCurrentUserId) {
return false;
}
- UserState userState = getUserStateLocked(userId);
+ AccessibilityUserState userState = getUserStateLocked(userId);
Iterator<ComponentName> it = userState.mEnabledServices.iterator();
while (it.hasNext()) {
ComponentName comp = it.next();
@@ -440,7 +434,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return true;
}
it.remove();
- userState.mBindingServices.remove(comp);
+ userState.getBindingServicesLocked().remove(comp);
persistComponentNamesToSettingLocked(
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
userState.mEnabledServices, userId);
@@ -477,7 +471,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
} else if (Intent.ACTION_USER_PRESENT.equals(action)) {
// We will update when the automation service dies.
synchronized (mLock) {
- UserState userState = getCurrentUserStateLocked();
+ AccessibilityUserState userState = getCurrentUserStateLocked();
if (readConfigurationForUserStateLocked(userState)) {
onUserStateChangedLocked(userState);
}
@@ -508,7 +502,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// If the client is from a process that runs across users such as
// the system UI or the system we add it to the global state that
// is shared across users.
- UserState userState = getUserStateLocked(resolvedUserId);
+ AccessibilityUserState userState = getUserStateLocked(resolvedUserId);
Client client = new Client(callback, Binder.getCallingUid(), userState);
if (mSecurityPolicy.isCallerInteractingAcrossUsers(userId)) {
mGlobalClients.register(callback, client);
@@ -516,7 +510,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
Slog.i(LOG_TAG, "Added global client for pid:" + Binder.getCallingPid());
}
return IntPair.of(
- userState.getClientState(),
+ getClientStateLocked(userState),
client.mLastSentRelevantEventTypes);
} else {
userState.mUserClients.register(callback, client);
@@ -528,7 +522,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
+ " and userId:" + mCurrentUserId);
}
return IntPair.of(
- (resolvedUserId == mCurrentUserId) ? userState.getClientState() : 0,
+ (resolvedUserId == mCurrentUserId) ? getClientStateLocked(userState) : 0,
client.mLastSentRelevantEventTypes);
}
}
@@ -643,7 +637,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
.resolveCallingUserIdEnforcingPermissionsLocked(userId);
// The automation service can suppress other services.
- final UserState userState = getUserStateLocked(resolvedUserId);
+ final AccessibilityUserState userState = getUserStateLocked(resolvedUserId);
if (mUiAutomationManager.suppressingAccessibilityServicesLocked()) {
return Collections.emptyList();
}
@@ -729,7 +723,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
synchronized (mLock) {
mUiAutomationManager.registerUiTestAutomationServiceLocked(owner, serviceClient,
mContext, accessibilityServiceInfo, sIdCounter++, mMainHandler,
- mSecurityPolicy, this, mWindowManagerService, mGlobalActionPerformer,
+ mSecurityPolicy, this, mWindowManagerService, mSystemActionPerformer,
mA11yWindowManager, flags);
onUserStateChangedLocked(getCurrentUserStateLocked());
}
@@ -753,15 +747,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
synchronized (mLock) {
// Set the temporary state.
- UserState userState = getCurrentUserStateLocked();
+ AccessibilityUserState userState = getCurrentUserStateLocked();
- userState.mIsTouchExplorationEnabled = touchExplorationEnabled;
- userState.mIsDisplayMagnificationEnabled = false;
- userState.mIsNavBarMagnificationEnabled = false;
- userState.mIsAutoclickEnabled = false;
+ userState.setTouchExplorationEnabledLocked(touchExplorationEnabled);
+ userState.setDisplayMagnificationEnabledLocked(false);
+ userState.setNavBarMagnificationEnabledLocked(false);
+ userState.setAutoclickEnabledLocked(false);
userState.mEnabledServices.clear();
userState.mEnabledServices.add(service);
- userState.mBindingServices.clear();
+ userState.getBindingServicesLocked().clear();
+ userState.getCrashedServicesLocked().clear();
userState.mTouchExplorationGrantedServices.clear();
userState.mTouchExplorationGrantedServices.add(service);
@@ -942,7 +937,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
// Disconnect from services for the old user.
- UserState oldUserState = getCurrentUserStateLocked();
+ AccessibilityUserState oldUserState = getCurrentUserStateLocked();
oldUserState.onSwitchToAnotherUserLocked();
// Disable the local managers for the old user.
@@ -959,7 +954,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// The user changed.
mCurrentUserId = userId;
- UserState userState = getCurrentUserStateLocked();
+ AccessibilityUserState userState = getCurrentUserStateLocked();
readConfigurationForUserStateLocked(userState);
// Even if reading did not yield change, we have to update
@@ -978,8 +973,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private void announceNewUserIfNeeded() {
synchronized (mLock) {
- UserState userState = getCurrentUserStateLocked();
- if (userState.isHandlingAccessibilityEvents()) {
+ AccessibilityUserState userState = getCurrentUserStateLocked();
+ if (userState.isHandlingAccessibilityEventsLocked()) {
UserManager userManager = (UserManager) mContext.getSystemService(
Context.USER_SERVICE);
String message = mContext.getString(R.string.user_switched,
@@ -996,7 +991,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
synchronized (mLock) {
int parentUserId = mSecurityPolicy.resolveProfileParentLocked(userId);
if (parentUserId == mCurrentUserId) {
- UserState userState = getUserStateLocked(mCurrentUserId);
+ AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
onUserStateChangedLocked(userState);
}
}
@@ -1014,7 +1009,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
readComponentNamesFromStringLocked(oldSetting, mTempComponentNameSet, false);
readComponentNamesFromStringLocked(newSetting, mTempComponentNameSet, true);
- UserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
+ AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
userState.mEnabledServices.clear();
userState.mEnabledServices.addAll(mTempComponentNameSet);
persistComponentNamesToSettingLocked(
@@ -1024,6 +1019,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
onUserStateChangedLocked(userState);
}
+ private int getClientStateLocked(AccessibilityUserState userState) {
+ return userState.getClientStateLocked(mUiAutomationManager.isUiAutomationRunningLocked());
+ }
+
private InteractionBridge getInteractionBridge() {
synchronized (mLock) {
if (mInteractionBridge == null) {
@@ -1043,7 +1042,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// gestures to avoid user frustration when different
// behavior is observed from different combinations of
// enabled accessibility services.
- UserState state = getCurrentUserStateLocked();
+ AccessibilityUserState state = getCurrentUserStateLocked();
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
AccessibilityServiceConnection service = state.mBoundServices.get(i);
if (service.mRequestTouchExplorationMode && service.mIsDefault == isDefault) {
@@ -1055,7 +1054,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
private void notifyClearAccessibilityCacheLocked() {
- UserState state = getCurrentUserStateLocked();
+ AccessibilityUserState state = getCurrentUserStateLocked();
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
AccessibilityServiceConnection service = state.mBoundServices.get(i);
service.notifyClearAccessibilityNodeInfoCache();
@@ -1064,25 +1063,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
float scale, float centerX, float centerY) {
- final UserState state = getCurrentUserStateLocked();
+ final AccessibilityUserState state = getCurrentUserStateLocked();
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
final AccessibilityServiceConnection service = state.mBoundServices.get(i);
service.notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY);
}
}
- private void notifySoftKeyboardShowModeChangedLocked(int showMode) {
- final UserState state = getCurrentUserStateLocked();
- for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
- final AccessibilityServiceConnection service = state.mBoundServices.get(i);
- service.notifySoftKeyboardShowModeChangedLocked(showMode);
- }
- }
-
private void notifyAccessibilityButtonClickedLocked(int displayId) {
- final UserState state = getCurrentUserStateLocked();
+ final AccessibilityUserState state = getCurrentUserStateLocked();
- int potentialTargets = state.mIsNavBarMagnificationEnabled ? 1 : 0;
+ int potentialTargets = state.isNavBarMagnificationEnabledLocked() ? 1 : 0;
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
final AccessibilityServiceConnection service = state.mBoundServices.get(i);
if (service.mRequestAccessibilityButton) {
@@ -1094,7 +1085,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return;
}
if (potentialTargets == 1) {
- if (state.mIsNavBarMagnificationEnabled) {
+ if (state.isNavBarMagnificationEnabledLocked()) {
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::sendAccessibilityButtonToInputFilter, this,
displayId));
@@ -1109,13 +1100,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
} else {
- if (state.mServiceAssignedToAccessibilityButton == null
- && !state.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+ if (state.getServiceAssignedToAccessibilityButtonLocked() == null
+ && !state.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::showAccessibilityButtonTargetSelection, this,
displayId));
- } else if (state.mIsNavBarMagnificationEnabled
- && state.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+ } else if (state.isNavBarMagnificationEnabledLocked()
+ && state.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::sendAccessibilityButtonToInputFilter, this,
displayId));
@@ -1124,7 +1115,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
final AccessibilityServiceConnection service = state.mBoundServices.get(i);
if (service.mRequestAccessibilityButton && (service.mComponentName.equals(
- state.mServiceAssignedToAccessibilityButton))) {
+ state.getServiceAssignedToAccessibilityButtonLocked()))) {
service.notifyAccessibilityButtonClickedLocked(displayId);
return;
}
@@ -1153,7 +1144,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
private void notifyAccessibilityButtonVisibilityChangedLocked(boolean available) {
- final UserState state = getCurrentUserStateLocked();
+ final AccessibilityUserState state = getCurrentUserStateLocked();
mIsAccessibilityButtonShown = available;
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
final AccessibilityServiceConnection clientConnection = state.mBoundServices.get(i);
@@ -1164,7 +1155,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private boolean readInstalledAccessibilityServiceLocked(UserState userState) {
+ private boolean readInstalledAccessibilityServiceLocked(AccessibilityUserState userState) {
mTempAccessibilityServiceInfoList.clear();
int flags = PackageManager.GET_SERVICES
@@ -1173,7 +1164,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
| PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
- if (userState.getBindInstantServiceAllowed()) {
+ if (userState.getBindInstantServiceAllowedLocked()) {
flags |= PackageManager.MATCH_INSTANT;
}
@@ -1191,6 +1182,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
AccessibilityServiceInfo accessibilityServiceInfo;
try {
accessibilityServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
+ if (userState.mCrashedServices.contains(serviceInfo.getComponentName())) {
+ // Restore the crashed attribute.
+ accessibilityServiceInfo.crashed = true;
+ }
mTempAccessibilityServiceInfoList.add(accessibilityServiceInfo);
} catch (XmlPullParserException | IOException xppe) {
Slog.e(LOG_TAG, "Error while initializing AccessibilityServiceInfo", xppe);
@@ -1208,7 +1203,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return false;
}
- private boolean readEnabledAccessibilityServicesLocked(UserState userState) {
+ private boolean readInstalledAccessibilityShortcutLocked(AccessibilityUserState userState) {
+ final List<AccessibilityShortcutInfo> shortcutInfos = AccessibilityManager
+ .getInstance(mContext).getInstalledAccessibilityShortcutListAsUser(
+ mContext, mCurrentUserId);
+ if (!shortcutInfos.equals(userState.mInstalledShortcuts)) {
+ userState.mInstalledShortcuts.clear();
+ userState.mInstalledShortcuts.addAll(shortcutInfos);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean readEnabledAccessibilityServicesLocked(AccessibilityUserState userState) {
mTempComponentNameSet.clear();
readComponentNamesFromSettingLocked(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
userState.mUserId, mTempComponentNameSet);
@@ -1223,7 +1230,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
private boolean readTouchExplorationGrantedAccessibilityServicesLocked(
- UserState userState) {
+ AccessibilityUserState userState) {
mTempComponentNameSet.clear();
readComponentNamesFromSettingLocked(
Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
@@ -1248,7 +1255,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event,
boolean isDefault) {
try {
- UserState state = getCurrentUserStateLocked();
+ AccessibilityUserState state = getCurrentUserStateLocked();
for (int i = 0, count = state.mBoundServices.size(); i < count; i++) {
AccessibilityServiceConnection service = state.mBoundServices.get(i);
@@ -1263,7 +1270,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private void updateRelevantEventsLocked(UserState userState) {
+ private void updateRelevantEventsLocked(AccessibilityUserState userState) {
mMainHandler.post(() -> {
broadcastToClients(userState, ignoreRemoteException(client -> {
int relevantEventTypes;
@@ -1283,7 +1290,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
});
}
- private int computeRelevantEventTypesLocked(UserState userState, Client client) {
+ private int computeRelevantEventTypesLocked(AccessibilityUserState userState, Client client) {
int relevantEventTypes = 0;
int serviceCount = userState.mBoundServices.size();
@@ -1329,20 +1336,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
private void broadcastToClients(
- UserState userState, Consumer<Client> clientAction) {
+ AccessibilityUserState userState, Consumer<Client> clientAction) {
mGlobalClients.broadcastForEachCookie(clientAction);
userState.mUserClients.broadcastForEachCookie(clientAction);
}
- private void unbindAllServicesLocked(UserState userState) {
- List<AccessibilityServiceConnection> services = userState.mBoundServices;
- for (int count = services.size(); count > 0; count--) {
- // When the service is unbound, it disappears from the list, so there's no need to
- // keep track of the index
- services.get(0).unbindLocked();
- }
- }
-
/**
* Populates a set with the {@link ComponentName}s stored in a colon
* separated value setting for a given user.
@@ -1409,7 +1407,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private void updateServicesLocked(UserState userState) {
+ private void updateServicesLocked(AccessibilityUserState userState) {
Map<ComponentName, AccessibilityServiceConnection> componentNameToServiceMap =
userState.mComponentNameToServiceMap;
boolean isUnlockingOrUnlocked = LocalServices.getService(UserManagerInternal.class)
@@ -1428,8 +1426,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
continue;
}
- // Wait for the binding if it is in process.
- if (userState.mBindingServices.contains(componentName)) {
+ // Skip the component since it may be in process or crashed.
+ if (userState.getBindingServicesLocked().contains(componentName)
+ || userState.getCrashedServicesLocked().contains(componentName)) {
continue;
}
if (userState.mEnabledServices.contains(componentName)
@@ -1437,7 +1436,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (service == null) {
service = new AccessibilityServiceConnection(userState, mContext, componentName,
installedService, sIdCounter++, mMainHandler, mLock, mSecurityPolicy,
- this, mWindowManagerService, mGlobalActionPerformer,
+ this, mWindowManagerService, mSystemActionPerformer,
mA11yWindowManager);
} else if (userState.mBoundServices.contains(service)) {
continue;
@@ -1465,15 +1464,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (audioManager != null) {
audioManager.setAccessibilityServiceUids(mTempIntArray);
}
- updateAccessibilityEnabledSetting(userState);
+ updateAccessibilityEnabledSettingLocked(userState);
}
- private void scheduleUpdateClientsIfNeededLocked(UserState userState) {
- final int clientState = userState.getClientState();
- if (userState.mLastSentClientState != clientState
+ private void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) {
+ final int clientState = getClientStateLocked(userState);
+ if (userState.getLastSentClientStateLocked() != clientState
&& (mGlobalClients.getRegisteredCallbackCount() > 0
|| userState.mUserClients.getRegisteredCallbackCount() > 0)) {
- userState.mLastSentClientState = clientState;
+ userState.setLastSentClientStateLocked(clientState);
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::sendStateToAllClients,
this, clientState, userState.mUserId));
@@ -1495,7 +1494,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
client -> client.setState(clientState)));
}
- private void scheduleNotifyClientsOfServicesStateChangeLocked(UserState userState) {
+ private void scheduleNotifyClientsOfServicesStateChangeLocked(
+ AccessibilityUserState userState) {
updateRecommendedUiTimeoutLocked(userState);
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::sendServicesStateChanged,
@@ -1514,44 +1514,45 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
client -> client.notifyServicesStateChanged(uiTimeout)));
}
- private void scheduleUpdateInputFilter(UserState userState) {
+ private void scheduleUpdateInputFilter(AccessibilityUserState userState) {
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::updateInputFilter, this, userState));
}
- private void scheduleUpdateFingerprintGestureHandling(UserState userState) {
+ private void scheduleUpdateFingerprintGestureHandling(AccessibilityUserState userState) {
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::updateFingerprintGestureHandling,
this, userState));
}
- private void updateInputFilter(UserState userState) {
+ private void updateInputFilter(AccessibilityUserState userState) {
if (mUiAutomationManager.suppressingAccessibilityServicesLocked()) return;
boolean setInputFilter = false;
AccessibilityInputFilter inputFilter = null;
synchronized (mLock) {
int flags = 0;
- if (userState.mIsDisplayMagnificationEnabled) {
+ if (userState.isDisplayMagnificationEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER;
}
- if (userState.mIsNavBarMagnificationEnabled) {
+ if (userState.isNavBarMagnificationEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER;
}
if (userHasMagnificationServicesLocked(userState)) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER;
}
// Touch exploration without accessibility makes no sense.
- if (userState.isHandlingAccessibilityEvents() && userState.mIsTouchExplorationEnabled) {
+ if (userState.isHandlingAccessibilityEventsLocked()
+ && userState.isTouchExplorationEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_TOUCH_EXPLORATION;
}
- if (userState.mIsFilterKeyEventsEnabled) {
+ if (userState.isFilterKeyEventsEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_FILTER_KEY_EVENTS;
}
- if (userState.mIsAutoclickEnabled) {
+ if (userState.isAutoclickEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_AUTOCLICK;
}
- if (userState.mIsPerformGesturesEnabled) {
+ if (userState.isPerformGesturesEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_INJECT_MOTION_EVENTS;
}
if (flags != 0) {
@@ -1584,8 +1585,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
String label = service.getServiceInfo().getResolveInfo()
.loadLabel(mContext.getPackageManager()).toString();
- final UserState userState = getCurrentUserStateLocked();
- if (userState.mIsTouchExplorationEnabled) {
+ final AccessibilityUserState userState = getCurrentUserStateLocked();
+ if (userState.isTouchExplorationEnabledLocked()) {
return;
}
if (mEnableTouchExplorationDialog != null
@@ -1595,40 +1596,40 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mEnableTouchExplorationDialog = new AlertDialog.Builder(mContext)
.setIconAttribute(android.R.attr.alertDialogIcon)
.setPositiveButton(android.R.string.ok, new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- // The user allowed the service to toggle touch exploration.
- userState.mTouchExplorationGrantedServices.add(service.mComponentName);
- persistComponentNamesToSettingLocked(
- Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
- userState.mTouchExplorationGrantedServices, userState.mUserId);
- // Enable touch exploration.
- userState.mIsTouchExplorationEnabled = true;
- final long identity = Binder.clearCallingIdentity();
- try {
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1,
- userState.mUserId);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- onUserStateChangedLocked(userState);
- }
- })
- .setNegativeButton(android.R.string.cancel, new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- }
- })
- .setTitle(R.string.enable_explore_by_touch_warning_title)
- .setMessage(mContext.getString(
- R.string.enable_explore_by_touch_warning_message, label))
- .create();
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // The user allowed the service to toggle touch exploration.
+ userState.mTouchExplorationGrantedServices.add(service.mComponentName);
+ persistComponentNamesToSettingLocked(
+ Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
+ userState.mTouchExplorationGrantedServices, userState.mUserId);
+ // Enable touch exploration.
+ userState.setTouchExplorationEnabledLocked(true);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1,
+ userState.mUserId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ onUserStateChangedLocked(userState);
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ })
+ .setTitle(R.string.enable_explore_by_touch_warning_title)
+ .setMessage(mContext.getString(
+ R.string.enable_explore_by_touch_warning_message, label))
+ .create();
mEnableTouchExplorationDialog.getWindow().setType(
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
mEnableTouchExplorationDialog.getWindow().getAttributes().privateFlags
- |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
mEnableTouchExplorationDialog.setCanceledOnTouchOutside(true);
mEnableTouchExplorationDialog.show();
}
@@ -1639,7 +1640,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*
* @param userState the new user state
*/
- private void onUserStateChangedLocked(UserState userState) {
+ private void onUserStateChangedLocked(AccessibilityUserState userState) {
// TODO: Remove this hack
mInitialized = true;
updateLegacyCapabilitiesLocked(userState);
@@ -1657,7 +1658,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
updateAccessibilityButtonTargetsLocked(userState);
}
- private void updateWindowsForAccessibilityCallbackLocked(UserState userState) {
+ private void updateWindowsForAccessibilityCallbackLocked(AccessibilityUserState userState) {
// We observe windows for accessibility only if there is at least
// one bound service that can retrieve window content that specified
// it is interested in accessing such windows. For services that are
@@ -1689,7 +1690,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private void updateLegacyCapabilitiesLocked(UserState userState) {
+ private void updateLegacyCapabilitiesLocked(AccessibilityUserState userState) {
// Up to JB-MR1 we had a white list with services that can enable touch
// exploration. When a service is first started we show a dialog to the
// use to get a permission to white list the service.
@@ -1711,20 +1712,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private void updatePerformGesturesLocked(UserState userState) {
+ private void updatePerformGesturesLocked(AccessibilityUserState userState) {
final int serviceCount = userState.mBoundServices.size();
for (int i = 0; i < serviceCount; i++) {
AccessibilityServiceConnection service = userState.mBoundServices.get(i);
if ((service.getCapabilities()
& AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES) != 0) {
- userState.mIsPerformGesturesEnabled = true;
+ userState.setPerformGesturesEnabledLocked(true);
return;
}
}
- userState.mIsPerformGesturesEnabled = false;
+ userState.setPerformGesturesEnabledLocked(false);
}
- private void updateFilterKeyEventsLocked(UserState userState) {
+ private void updateFilterKeyEventsLocked(AccessibilityUserState userState) {
final int serviceCount = userState.mBoundServices.size();
for (int i = 0; i < serviceCount; i++) {
AccessibilityServiceConnection service = userState.mBoundServices.get(i);
@@ -1732,15 +1733,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
&& (service.getCapabilities()
& AccessibilityServiceInfo
.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS) != 0) {
- userState.mIsFilterKeyEventsEnabled = true;
+ userState.setFilterKeyEventsEnabledLocked(true);
return;
}
}
- userState.mIsFilterKeyEventsEnabled = false;
+ userState.setFilterKeyEventsEnabledLocked(false);
}
- private boolean readConfigurationForUserStateLocked(UserState userState) {
+ private boolean readConfigurationForUserStateLocked(AccessibilityUserState userState) {
boolean somethingChanged = readInstalledAccessibilityServiceLocked(userState);
+ somethingChanged |= readInstalledAccessibilityShortcutLocked(userState);
somethingChanged |= readEnabledAccessibilityServicesLocked(userState);
somethingChanged |= readTouchExplorationGrantedAccessibilityServicesLocked(userState);
somethingChanged |= readTouchExplorationEnabledSettingLocked(userState);
@@ -1753,10 +1755,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return somethingChanged;
}
- private void updateAccessibilityEnabledSetting(UserState userState) {
+ private void updateAccessibilityEnabledSettingLocked(AccessibilityUserState userState) {
final long identity = Binder.clearCallingIdentity();
final boolean isA11yEnabled = mUiAutomationManager.isUiAutomationRunningLocked()
- || userState.isHandlingAccessibilityEvents();
+ || userState.isHandlingAccessibilityEventsLocked();
try {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_ENABLED,
@@ -1767,18 +1769,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private boolean readTouchExplorationEnabledSettingLocked(UserState userState) {
+ private boolean readTouchExplorationEnabledSettingLocked(AccessibilityUserState userState) {
final boolean touchExplorationEnabled = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0, userState.mUserId) == 1;
- if (touchExplorationEnabled != userState.mIsTouchExplorationEnabled) {
- userState.mIsTouchExplorationEnabled = touchExplorationEnabled;
+ if (touchExplorationEnabled != userState.isTouchExplorationEnabledLocked()) {
+ userState.setTouchExplorationEnabledLocked(touchExplorationEnabled);
return true;
}
return false;
}
- private boolean readMagnificationEnabledSettingsLocked(UserState userState) {
+ private boolean readMagnificationEnabledSettingsLocked(AccessibilityUserState userState) {
final boolean displayMagnificationEnabled = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
@@ -1787,40 +1789,40 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
0, userState.mUserId) == 1;
- if ((displayMagnificationEnabled != userState.mIsDisplayMagnificationEnabled)
- || (navBarMagnificationEnabled != userState.mIsNavBarMagnificationEnabled)) {
- userState.mIsDisplayMagnificationEnabled = displayMagnificationEnabled;
- userState.mIsNavBarMagnificationEnabled = navBarMagnificationEnabled;
+ if ((displayMagnificationEnabled != userState.isDisplayMagnificationEnabledLocked())
+ || (navBarMagnificationEnabled != userState.isNavBarMagnificationEnabledLocked())) {
+ userState.setDisplayMagnificationEnabledLocked(displayMagnificationEnabled);
+ userState.setNavBarMagnificationEnabledLocked(navBarMagnificationEnabled);
return true;
}
return false;
}
- private boolean readAutoclickEnabledSettingLocked(UserState userState) {
+ private boolean readAutoclickEnabledSettingLocked(AccessibilityUserState userState) {
final boolean autoclickEnabled = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED,
0, userState.mUserId) == 1;
- if (autoclickEnabled != userState.mIsAutoclickEnabled) {
- userState.mIsAutoclickEnabled = autoclickEnabled;
+ if (autoclickEnabled != userState.isAutoclickEnabledLocked()) {
+ userState.setAutoclickEnabledLocked(autoclickEnabled);
return true;
}
return false;
}
- private boolean readHighTextContrastEnabledSettingLocked(UserState userState) {
+ private boolean readHighTextContrastEnabledSettingLocked(AccessibilityUserState userState) {
final boolean highTextContrastEnabled = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, 0,
userState.mUserId) == 1;
- if (highTextContrastEnabled != userState.mIsTextHighContrastEnabled) {
- userState.mIsTextHighContrastEnabled = highTextContrastEnabled;
+ if (highTextContrastEnabled != userState.isTextHighContrastEnabledLocked()) {
+ userState.setTextHighContrastEnabledLocked(highTextContrastEnabled);
return true;
}
return false;
}
- private void updateTouchExplorationLocked(UserState userState) {
+ private void updateTouchExplorationLocked(AccessibilityUserState userState) {
boolean enabled = mUiAutomationManager.isTouchExplorationEnabledLocked();
final int serviceCount = userState.mBoundServices.size();
for (int i = 0; i < serviceCount; i++) {
@@ -1830,8 +1832,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
break;
}
}
- if (enabled != userState.mIsTouchExplorationEnabled) {
- userState.mIsTouchExplorationEnabled = enabled;
+ if (enabled != userState.isTouchExplorationEnabledLocked()) {
+ userState.setTouchExplorationEnabledLocked(enabled);
final long identity = Binder.clearCallingIdentity();
try {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
@@ -1843,60 +1845,61 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private boolean readAccessibilityShortcutSettingLocked(UserState userState) {
+ private boolean readAccessibilityShortcutSettingLocked(AccessibilityUserState userState) {
String componentNameToEnableString = AccessibilityShortcutController
.getTargetServiceComponentNameString(mContext, userState.mUserId);
if ((componentNameToEnableString == null) || componentNameToEnableString.isEmpty()) {
- if (userState.mServiceToEnableWithShortcut == null) {
+ if (userState.getServiceToEnableWithShortcutLocked() == null) {
return false;
}
- userState.mServiceToEnableWithShortcut = null;
+ userState.setServiceToEnableWithShortcutLocked(null);
return true;
}
ComponentName componentNameToEnable =
ComponentName.unflattenFromString(componentNameToEnableString);
if ((componentNameToEnable != null)
- && componentNameToEnable.equals(userState.mServiceToEnableWithShortcut)) {
+ && componentNameToEnable.equals(userState.getServiceToEnableWithShortcutLocked())) {
return false;
}
- userState.mServiceToEnableWithShortcut = componentNameToEnable;
+ userState.setServiceToEnableWithShortcutLocked(componentNameToEnable);
scheduleNotifyClientsOfServicesStateChangeLocked(userState);
return true;
}
- private boolean readAccessibilityButtonSettingsLocked(UserState userState) {
+ private boolean readAccessibilityButtonSettingsLocked(AccessibilityUserState userState) {
String componentId = Settings.Secure.getStringForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, userState.mUserId);
if (TextUtils.isEmpty(componentId)) {
- if ((userState.mServiceAssignedToAccessibilityButton == null)
- && !userState.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+ if ((userState.getServiceAssignedToAccessibilityButtonLocked() == null)
+ && !userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
return false;
}
- userState.mServiceAssignedToAccessibilityButton = null;
- userState.mIsNavBarMagnificationAssignedToAccessibilityButton = false;
+ userState.setServiceAssignedToAccessibilityButtonLocked(null);
+ userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(false);
return true;
}
if (componentId.equals(MagnificationController.class.getName())) {
- if (userState.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+ if (userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
return false;
}
- userState.mServiceAssignedToAccessibilityButton = null;
- userState.mIsNavBarMagnificationAssignedToAccessibilityButton = true;
+ userState.setServiceAssignedToAccessibilityButtonLocked(null);
+ userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(true);
return true;
}
ComponentName componentName = ComponentName.unflattenFromString(componentId);
- if (Objects.equals(componentName, userState.mServiceAssignedToAccessibilityButton)) {
+ if (Objects.equals(componentName,
+ userState.getServiceAssignedToAccessibilityButtonLocked())) {
return false;
}
- userState.mServiceAssignedToAccessibilityButton = componentName;
- userState.mIsNavBarMagnificationAssignedToAccessibilityButton = false;
+ userState.setServiceAssignedToAccessibilityButtonLocked(componentName);
+ userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(false);
return true;
}
- private boolean readUserRecommendedUiTimeoutSettingsLocked(UserState userState) {
+ private boolean readUserRecommendedUiTimeoutSettingsLocked(AccessibilityUserState userState) {
final int nonInteractiveUiTimeout = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS, 0,
@@ -1905,10 +1908,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS, 0,
userState.mUserId);
- if (nonInteractiveUiTimeout != userState.mUserNonInteractiveUiTimeout
- || interactiveUiTimeout != userState.mUserInteractiveUiTimeout) {
- userState.mUserNonInteractiveUiTimeout = nonInteractiveUiTimeout;
- userState.mUserInteractiveUiTimeout = interactiveUiTimeout;
+ if (nonInteractiveUiTimeout != userState.getUserNonInteractiveUiTimeoutLocked()
+ || interactiveUiTimeout != userState.getUserInteractiveUiTimeoutLocked()) {
+ userState.setUserNonInteractiveUiTimeoutLocked(nonInteractiveUiTimeout);
+ userState.setUserInteractiveUiTimeoutLocked(interactiveUiTimeout);
scheduleNotifyClientsOfServicesStateChangeLocked(userState);
return true;
}
@@ -1922,22 +1925,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*
* @param userState
*/
- private void updateAccessibilityShortcutLocked(UserState userState) {
- if (userState.mServiceToEnableWithShortcut == null) {
+ private void updateAccessibilityShortcutLocked(AccessibilityUserState userState) {
+ if (userState.getServiceToEnableWithShortcutLocked() == null) {
return;
}
boolean shortcutServiceIsInstalled =
AccessibilityShortcutController.getFrameworkShortcutFeaturesMap()
- .containsKey(userState.mServiceToEnableWithShortcut);
+ .containsKey(userState.getServiceToEnableWithShortcutLocked());
for (int i = 0; !shortcutServiceIsInstalled && (i < userState.mInstalledServices.size());
i++) {
if (userState.mInstalledServices.get(i).getComponentName()
- .equals(userState.mServiceToEnableWithShortcut)) {
+ .equals(userState.getServiceToEnableWithShortcutLocked())) {
shortcutServiceIsInstalled = true;
}
}
if (!shortcutServiceIsInstalled) {
- userState.mServiceToEnableWithShortcut = null;
+ userState.setServiceToEnableWithShortcutLocked(null);
final long identity = Binder.clearCallingIdentity();
try {
Settings.Secure.putStringForUser(mContext.getContentResolver(),
@@ -1953,7 +1956,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
private boolean canRequestAndRequestsTouchExplorationLocked(
- AccessibilityServiceConnection service, UserState userState) {
+ AccessibilityServiceConnection service, AccessibilityUserState userState) {
// Service not ready or cannot request the feature - well nothing to do.
if (!service.canReceiveEventsLocked() || !service.mRequestTouchExplorationMode) {
return false;
@@ -1983,7 +1986,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return false;
}
- private void updateMagnificationLocked(UserState userState) {
+ private void updateMagnificationLocked(AccessibilityUserState userState) {
if (userState.mUserId != mCurrentUserId) {
return;
}
@@ -1998,8 +2001,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// We would skip overlay display because it uses overlay window to simulate secondary
// displays in one display. It's not a real display and there's no input events for it.
final ArrayList<Display> displays = getValidDisplayList();
- if (userState.mIsDisplayMagnificationEnabled
- || userState.mIsNavBarMagnificationEnabled) {
+ if (userState.isDisplayMagnificationEnabledLocked()
+ || userState.isNavBarMagnificationEnabledLocked()) {
for (int i = 0; i < displays.size(); i++) {
final Display display = displays.get(i);
getMagnificationController().register(display.getDisplayId());
@@ -2023,7 +2026,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* Returns whether the specified user has any services that are capable of
* controlling magnification.
*/
- private boolean userHasMagnificationServicesLocked(UserState userState) {
+ private boolean userHasMagnificationServicesLocked(AccessibilityUserState userState) {
final List<AccessibilityServiceConnection> services = userState.mBoundServices;
for (int i = 0, count = services.size(); i < count; i++) {
final AccessibilityServiceConnection service = services.get(i);
@@ -2038,7 +2041,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* Returns whether the specified user has any services that are capable of
* controlling magnification and are actively listening for magnification updates.
*/
- private boolean userHasListeningMagnificationServicesLocked(UserState userState,
+ private boolean userHasListeningMagnificationServicesLocked(AccessibilityUserState userState,
int displayId) {
final List<AccessibilityServiceConnection> services = userState.mBoundServices;
for (int i = 0, count = services.size(); i < count; i++) {
@@ -2051,7 +2054,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return false;
}
- private void updateFingerprintGestureHandling(UserState userState) {
+ private void updateFingerprintGestureHandling(AccessibilityUserState userState) {
final List<AccessibilityServiceConnection> services;
synchronized (mLock) {
services = userState.mBoundServices;
@@ -2083,7 +2086,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private void updateAccessibilityButtonTargetsLocked(UserState userState) {
+ private void updateAccessibilityButtonTargetsLocked(AccessibilityUserState userState) {
for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
if (service.mRequestAccessibilityButton) {
@@ -2093,9 +2096,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private void updateRecommendedUiTimeoutLocked(UserState userState) {
- int newNonInteractiveUiTimeout = userState.mUserNonInteractiveUiTimeout;
- int newInteractiveUiTimeout = userState.mUserInteractiveUiTimeout;
+ private void updateRecommendedUiTimeoutLocked(AccessibilityUserState userState) {
+ int newNonInteractiveUiTimeout = userState.getUserNonInteractiveUiTimeoutLocked();
+ int newInteractiveUiTimeout = userState.getUserInteractiveUiTimeoutLocked();
// read from a11y services if user does not specify value
if (newNonInteractiveUiTimeout == 0 || newInteractiveUiTimeout == 0) {
int serviceNonInteractiveUiTimeout = 0;
@@ -2118,8 +2121,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
newInteractiveUiTimeout = serviceInteractiveUiTimeout;
}
}
- userState.mNonInteractiveUiTimeout = newNonInteractiveUiTimeout;
- userState.mInteractiveUiTimeout = newInteractiveUiTimeout;
+ userState.setNonInteractiveUiTimeoutLocked(newNonInteractiveUiTimeout);
+ userState.setInteractiveUiTimeoutLocked(newInteractiveUiTimeout);
}
@GuardedBy("mLock")
@@ -2166,8 +2169,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
final Map<ComponentName, ToggleableFrameworkFeatureInfo> frameworkFeatureMap =
AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
synchronized(mLock) {
- final UserState userState = getUserStateLocked(mCurrentUserId);
- final ComponentName serviceName = userState.mServiceToEnableWithShortcut;
+ final AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
+ final ComponentName serviceName = userState.getServiceToEnableWithShortcutLocked();
if (serviceName == null) {
return;
}
@@ -2204,11 +2207,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
"getAccessibilityShortcutService requires the MANAGE_ACCESSIBILITY permission");
}
synchronized(mLock) {
- final UserState userState = getUserStateLocked(mCurrentUserId);
- if (userState.mServiceToEnableWithShortcut == null) {
+ final AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
+ if (userState.getServiceToEnableWithShortcutLocked() == null) {
return null;
}
- return userState.mServiceToEnableWithShortcut.flattenToString();
+ return userState.getServiceToEnableWithShortcutLocked().flattenToString();
}
}
@@ -2223,7 +2226,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
userId);
setting.write(ComponentNameSet.add(setting.read(), componentName));
- UserState userState = getUserStateLocked(userId);
+ AccessibilityUserState userState = getUserStateLocked(userId);
if (userState.mEnabledServices.add(componentName)) {
onUserStateChangedLocked(userState);
}
@@ -2240,7 +2243,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
userId);
setting.write(ComponentNameSet.remove(setting.read(), componentName));
- UserState userState = getUserStateLocked(userId);
+ AccessibilityUserState userState = getUserStateLocked(userId);
if (userState.mEnabledServices.remove(componentName)) {
onUserStateChangedLocked(userState);
}
@@ -2308,14 +2311,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public long getRecommendedTimeoutMillis() {
synchronized(mLock) {
- final UserState userState = getCurrentUserStateLocked();
+ final AccessibilityUserState userState = getCurrentUserStateLocked();
return getRecommendedTimeoutMillisLocked(userState);
}
}
- private long getRecommendedTimeoutMillisLocked(UserState userState) {
- return IntPair.of(userState.mInteractiveUiTimeout,
- userState.mNonInteractiveUiTimeout);
+ private long getRecommendedTimeoutMillisLocked(AccessibilityUserState userState) {
+ return IntPair.of(userState.getInteractiveUiTimeoutLocked(),
+ userState.getNonInteractiveUiTimeoutLocked());
}
@Override
@@ -2324,78 +2327,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
synchronized (mLock) {
pw.println("ACCESSIBILITY MANAGER (dumpsys accessibility)");
pw.println();
+ pw.append("currentUserId=").append(String.valueOf(mCurrentUserId));
+ pw.println();
final int userCount = mUserStates.size();
for (int i = 0; i < userCount; i++) {
- UserState userState = mUserStates.valueAt(i);
- pw.append("User state[attributes:{id=" + userState.mUserId);
- pw.append(", currentUser=" + (userState.mUserId == mCurrentUserId));
- pw.append(", touchExplorationEnabled=" + userState.mIsTouchExplorationEnabled);
- pw.append(", displayMagnificationEnabled="
- + userState.mIsDisplayMagnificationEnabled);
- pw.append(", navBarMagnificationEnabled="
- + userState.mIsNavBarMagnificationEnabled);
- pw.append(", autoclickEnabled=" + userState.mIsAutoclickEnabled);
- pw.append(", nonInteractiveUiTimeout=" + userState.mNonInteractiveUiTimeout);
- pw.append(", interactiveUiTimeout=" + userState.mInteractiveUiTimeout);
- pw.append(", installedServiceCount=" + userState.mInstalledServices.size());
- if (mUiAutomationManager.isUiAutomationRunningLocked()) {
- pw.append(", ");
- mUiAutomationManager.dumpUiAutomationService(fd, pw, args);
- pw.println();
- }
- pw.append("}");
- pw.println();
- pw.append(" Bound services:{");
- final int serviceCount = userState.mBoundServices.size();
- for (int j = 0; j < serviceCount; j++) {
- if (j > 0) {
- pw.append(", ");
- pw.println();
- pw.append(" ");
- }
- AccessibilityServiceConnection service = userState.mBoundServices.get(j);
- service.dump(fd, pw, args);
- }
- pw.println("}");
- pw.append(" Enabled services:{");
- Iterator<ComponentName> it = userState.mEnabledServices.iterator();
- if (it.hasNext()) {
- ComponentName componentName = it.next();
- pw.append(componentName.toShortString());
- while (it.hasNext()) {
- componentName = it.next();
- pw.append(", ");
- pw.append(componentName.toShortString());
- }
- }
- pw.println("}");
- pw.append(" Binding services:{");
- it = userState.mBindingServices.iterator();
- if (it.hasNext()) {
- ComponentName componentName = it.next();
- pw.append(componentName.toShortString());
- while (it.hasNext()) {
- componentName = it.next();
- pw.append(", ");
- pw.append(componentName.toShortString());
- }
- }
- pw.println("}]");
+ mUserStates.valueAt(i).dump(fd, pw, args);
+ }
+ if (mUiAutomationManager.isUiAutomationRunningLocked()) {
+ mUiAutomationManager.dumpUiAutomationService(fd, pw, args);
pw.println();
}
mA11yWindowManager.dump(fd, pw, args);
}
}
- private void putSecureIntForUser(String key, int value, int userid) {
- final long identity = Binder.clearCallingIdentity();
- try {
- Settings.Secure.putIntForUser(mContext.getContentResolver(), key, value, userid);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
//TODO remove after refactoring KeyEventDispatcherTest
final class MainHandler extends Handler {
public static final int MSG_SEND_KEY_EVENT_TO_INPUT_FILTER = 8;
@@ -2432,7 +2377,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void onClientChangeLocked(boolean serviceInfoChanged) {
- AccessibilityManagerService.UserState userState = getUserStateLocked(mCurrentUserId);
+ AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
onUserStateChangedLocked(userState);
if (serviceInfoChanged) {
scheduleNotifyClientsOfServicesStateChangeLocked(userState);
@@ -2460,7 +2405,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT);
info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
- final UserState userState;
+ final AccessibilityUserState userState;
synchronized (mLock) {
userState = getCurrentUserStateLocked();
}
@@ -2468,7 +2413,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
userState, mContext,
COMPONENT_NAME, info, sIdCounter++, mMainHandler, mLock, mSecurityPolicy,
AccessibilityManagerService.this, mWindowManagerService,
- mGlobalActionPerformer, mA11yWindowManager) {
+ mSystemActionPerformer, mA11yWindowManager) {
@Override
public boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) {
return true;
@@ -2579,7 +2524,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (mInputFilter != null) {
mInputFilter.onDisplayChanged();
}
- UserState userState = getCurrentUserStateLocked();
+ AccessibilityUserState userState = getCurrentUserStateLocked();
if (displayId != Display.DEFAULT_DISPLAY) {
final List<AccessibilityServiceConnection> services = userState.mBoundServices;
for (int i = 0; i < services.size(); i++) {
@@ -2604,7 +2549,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (mInputFilter != null) {
mInputFilter.onDisplayChanged();
}
- UserState userState = getCurrentUserStateLocked();
+ AccessibilityUserState userState = getCurrentUserStateLocked();
if (displayId != Display.DEFAULT_DISPLAY) {
final List<AccessibilityServiceConnection> services = userState.mBoundServices;
for (int i = 0; i < services.size(); i++) {
@@ -2644,7 +2589,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
final String[] mPackageNames;
int mLastSentRelevantEventTypes;
- private Client(IAccessibilityManagerClient callback, int clientUid, UserState userState) {
+ private Client(IAccessibilityManagerClient callback, int clientUid,
+ AccessibilityUserState userState) {
mCallback = callback;
mPackageNames = mPackageManager.getPackagesForUid(clientUid);
synchronized (mLock) {
@@ -2653,314 +2599,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- public class UserState {
- public final int mUserId;
-
- // Non-transient state.
-
- public final RemoteCallbackList<IAccessibilityManagerClient> mUserClients =
- new RemoteCallbackList<>();
-
- // Transient state.
-
- public final ArrayList<AccessibilityServiceConnection> mBoundServices = new ArrayList<>();
-
- public final Map<ComponentName, AccessibilityServiceConnection> mComponentNameToServiceMap =
- new HashMap<>();
-
- public final List<AccessibilityServiceInfo> mInstalledServices =
- new ArrayList<>();
-
- private final Set<ComponentName> mBindingServices = new HashSet<>();
-
- public final Set<ComponentName> mEnabledServices = new HashSet<>();
-
- public final Set<ComponentName> mTouchExplorationGrantedServices =
- new HashSet<>();
-
- public ComponentName mServiceChangingSoftKeyboardMode;
-
- public ComponentName mServiceToEnableWithShortcut;
-
- public int mLastSentClientState = -1;
- public int mNonInteractiveUiTimeout = 0;
- public int mInteractiveUiTimeout = 0;
-
- private int mSoftKeyboardShowMode = 0;
-
- public boolean mIsNavBarMagnificationAssignedToAccessibilityButton;
- public ComponentName mServiceAssignedToAccessibilityButton;
-
- public boolean mIsTouchExplorationEnabled;
- public boolean mIsTextHighContrastEnabled;
- public boolean mIsDisplayMagnificationEnabled;
- public boolean mIsNavBarMagnificationEnabled;
- public boolean mIsAutoclickEnabled;
- public boolean mIsPerformGesturesEnabled;
- public boolean mIsFilterKeyEventsEnabled;
- public int mUserNonInteractiveUiTimeout;
- public int mUserInteractiveUiTimeout;
-
- private boolean mBindInstantServiceAllowed;
-
- public UserState(int userId) {
- mUserId = userId;
- }
-
- public int getClientState() {
- int clientState = 0;
- final boolean a11yEnabled = (mUiAutomationManager.isUiAutomationRunningLocked()
- || isHandlingAccessibilityEvents());
- if (a11yEnabled) {
- clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
- }
- // Touch exploration relies on enabled accessibility.
- if (a11yEnabled && mIsTouchExplorationEnabled) {
- clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
- }
- if (mIsTextHighContrastEnabled) {
- clientState |= AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
- }
- return clientState;
- }
-
- public boolean isHandlingAccessibilityEvents() {
- return !mBoundServices.isEmpty() || !mBindingServices.isEmpty();
- }
-
- public void onSwitchToAnotherUserLocked() {
- // Unbind all services.
- unbindAllServicesLocked(this);
-
- // Clear service management state.
- mBoundServices.clear();
- mBindingServices.clear();
-
- // Clear event management state.
- mLastSentClientState = -1;
-
- // clear UI timeout
- mNonInteractiveUiTimeout = 0;
- mInteractiveUiTimeout = 0;
-
- // Clear state persisted in settings.
- mEnabledServices.clear();
- mTouchExplorationGrantedServices.clear();
- mIsTouchExplorationEnabled = false;
- mIsDisplayMagnificationEnabled = false;
- mIsNavBarMagnificationEnabled = false;
- mServiceAssignedToAccessibilityButton = null;
- mIsNavBarMagnificationAssignedToAccessibilityButton = false;
- mIsAutoclickEnabled = false;
- mUserNonInteractiveUiTimeout = 0;
- mUserInteractiveUiTimeout = 0;
- }
-
- public void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
- if (!mBoundServices.contains(serviceConnection)) {
- serviceConnection.onAdded();
- mBoundServices.add(serviceConnection);
- mComponentNameToServiceMap.put(serviceConnection.mComponentName, serviceConnection);
- scheduleNotifyClientsOfServicesStateChangeLocked(this);
- }
- }
-
- /**
- * Removes a service.
- * There are three states to a service here: off, bound, and binding.
- * This stops tracking the service as bound.
- *
- * @param serviceConnection The service.
- */
- public void removeServiceLocked(AccessibilityServiceConnection serviceConnection) {
- mBoundServices.remove(serviceConnection);
- serviceConnection.onRemoved();
- if ((mServiceChangingSoftKeyboardMode != null)
- && (mServiceChangingSoftKeyboardMode.equals(
- serviceConnection.getServiceInfo().getComponentName()))) {
- setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
- }
- // It may be possible to bind a service twice, which confuses the map. Rebuild the map
- // to make sure we can still reach a service
- mComponentNameToServiceMap.clear();
- for (int i = 0; i < mBoundServices.size(); i++) {
- AccessibilityServiceConnection boundClient = mBoundServices.get(i);
- mComponentNameToServiceMap.put(boundClient.mComponentName, boundClient);
- }
- scheduleNotifyClientsOfServicesStateChangeLocked(this);
- }
-
- /**
- * Make sure a services disconnected but still 'on' state is reflected in UserState
- * There are three states to a service here: off, bound, and binding.
- * This drops a service from a bound state, to the binding state.
- * The binding state describes the situation where a service is on, but not bound.
- *
- * @param serviceConnection The service.
- */
- public void serviceDisconnectedLocked(AccessibilityServiceConnection serviceConnection) {
- removeServiceLocked(serviceConnection);
- mBindingServices.add(serviceConnection.getComponentName());
- }
-
- public Set<ComponentName> getBindingServicesLocked() {
- return mBindingServices;
- }
-
- /**
- * Returns enabled service list.
- */
- public Set<ComponentName> getEnabledServicesLocked() {
- return mEnabledServices;
- }
-
- public int getSoftKeyboardShowMode() {
- return mSoftKeyboardShowMode;
- }
-
- /**
- * Set the soft keyboard mode. This mode is a bit odd, as it spans multiple settings.
- * The ACCESSIBILITY_SOFT_KEYBOARD_MODE setting can be checked by the rest of the system
- * to see if it should suppress showing the IME. The SHOW_IME_WITH_HARD_KEYBOARD setting
- * setting can be changed by the user, and prevents the system from suppressing the soft
- * keyboard when the hard keyboard is connected. The hard keyboard setting needs to defer
- * to the user's preference, if they have supplied one.
- *
- * @param newMode The new mode
- * @param requester The service requesting the change, so we can undo it when the
- * service stops. Set to null if something other than a service is forcing
- * the change.
- *
- * @return Whether or not the soft keyboard mode equals the new mode after the call
- */
- public boolean setSoftKeyboardModeLocked(int newMode, @Nullable ComponentName requester) {
- if ((newMode != SHOW_MODE_AUTO) && (newMode != SHOW_MODE_HIDDEN)
- && (newMode != SHOW_MODE_IGNORE_HARD_KEYBOARD))
- {
- Slog.w(LOG_TAG, "Invalid soft keyboard mode");
- return false;
- }
- if (mSoftKeyboardShowMode == newMode) return true;
-
- if (newMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
- if (hasUserOverriddenHardKeyboardSettingLocked()) {
- // The user has specified a default for this setting
- return false;
- }
- // Save the original value. But don't do this if the value in settings is already
- // the new mode. That happens when we start up after a reboot, and we don't want
- // to overwrite the value we had from when we first started controlling the setting.
- if (getSoftKeyboardValueFromSettings() != SHOW_MODE_IGNORE_HARD_KEYBOARD) {
- setOriginalHardKeyboardValue(
- Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0);
- }
- putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1, mUserId);
- } else if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
- putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
- getOriginalHardKeyboardValue() ? 1 : 0, mUserId);
- }
-
- saveSoftKeyboardValueToSettings(newMode);
- mSoftKeyboardShowMode = newMode;
- mServiceChangingSoftKeyboardMode = requester;
- notifySoftKeyboardShowModeChangedLocked(mSoftKeyboardShowMode);
- return true;
- }
-
- /**
- * If the settings are inconsistent with the internal state, make the internal state
- * match the settings.
- */
- public void reconcileSoftKeyboardModeWithSettingsLocked() {
- final ContentResolver cr = mContext.getContentResolver();
- final boolean showWithHardKeyboardSettings =
- Settings.Secure.getInt(cr, Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0;
- if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
- if (!showWithHardKeyboardSettings) {
- // The user has overridden the setting. Respect that and prevent further changes
- // to this behavior.
- setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
- setUserOverridesHardKeyboardSettingLocked();
- }
- }
-
- // If the setting and the internal state are out of sync, set both to default
- if (getSoftKeyboardValueFromSettings() != mSoftKeyboardShowMode)
- {
- Slog.e(LOG_TAG,
- "Show IME setting inconsistent with internal state. Overwriting");
- setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
- putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
- SHOW_MODE_AUTO, mUserId);
- }
- }
-
- private void setUserOverridesHardKeyboardSettingLocked() {
- final int softKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
- putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
- softKeyboardSetting | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN,
- mUserId);
- }
-
- private boolean hasUserOverriddenHardKeyboardSettingLocked() {
- final int softKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
- return (softKeyboardSetting & SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN)
- != 0;
- }
-
- private void setOriginalHardKeyboardValue(boolean originalHardKeyboardValue) {
- final int oldSoftKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
- final int newSoftKeyboardSetting = oldSoftKeyboardSetting
- & (~SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE)
- | ((originalHardKeyboardValue) ? SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE : 0);
- putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
- newSoftKeyboardSetting, mUserId);
- }
-
- private void saveSoftKeyboardValueToSettings(int softKeyboardShowMode) {
- final int oldSoftKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
- final int newSoftKeyboardSetting = oldSoftKeyboardSetting & (~SHOW_MODE_MASK)
- | softKeyboardShowMode;
- putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
- newSoftKeyboardSetting, mUserId);
- }
-
- private int getSoftKeyboardValueFromSettings() {
- return Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
- SHOW_MODE_AUTO) & SHOW_MODE_MASK;
- }
-
- private boolean getOriginalHardKeyboardValue() {
- return (Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0)
- & SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE) != 0;
- }
-
- public boolean getBindInstantServiceAllowed() {
- synchronized (mLock) {
- return mBindInstantServiceAllowed;
- }
- }
-
- public void setBindInstantServiceAllowed(boolean allowed) {
- synchronized (mLock) {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.MANAGE_BIND_INSTANT_SERVICE,
- "setBindInstantServiceAllowed");
- if (allowed) {
- mBindInstantServiceAllowed = allowed;
- onUserStateChangedLocked(this);
- }
- }
- }
- }
-
private final class AccessibilityContentObserver extends ContentObserver {
private final Uri mTouchExplorationEnabledUri = Settings.Secure.getUriFor(
@@ -3041,7 +2679,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
synchronized (mLock) {
// Profiles share the accessibility state of the parent. Therefore,
// we are checking for changes only the parent settings.
- UserState userState = getCurrentUserStateLocked();
+ AccessibilityUserState userState = getCurrentUserStateLocked();
if (mTouchExplorationEnabledUri.equals(uri)) {
if (readTouchExplorationEnabledSettingLocked(userState)) {
@@ -3058,6 +2696,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
} else if (mEnabledAccessibilityServicesUri.equals(uri)) {
if (readEnabledAccessibilityServicesLocked(userState)) {
+ userState.updateCrashedServicesIfNeededLocked();
onUserStateChangedLocked(userState);
}
} else if (mTouchExplorationGrantedAccessibilityServicesUri.equals(uri)) {
@@ -3083,6 +2722,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|| mUserInteractiveUiTimeoutUri.equals(uri)) {
readUserRecommendedUiTimeoutSettingsLocked(userState);
}
+ // TODO(a11y shortcut): Monitor new setting keys, when user adds shortcut, and
+ // remove from the list of enabled targets anything that's been uninstalled.
}
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index d7f61e5371d5..a0a755a30cb3 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -35,7 +35,6 @@ import android.provider.Settings;
import android.util.Slog;
import android.view.Display;
-import com.android.server.accessibility.AccessibilityManagerService.UserState;
import com.android.server.wm.WindowManagerInternal;
import java.lang.ref.WeakReference;
@@ -52,29 +51,26 @@ import java.util.Set;
class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnection {
private static final String LOG_TAG = "AccessibilityServiceConnection";
/*
- Holding a weak reference so there isn't a loop of references. UserState keeps lists of bound
- and binding services. These are freed on user changes, but just in case it somehow gets lost
- the weak reference will let the memory get GCed.
+ Holding a weak reference so there isn't a loop of references. AccessibilityUserState keeps
+ lists of bound and binding services. These are freed on user changes, but just in case it
+ somehow gets lost the weak reference will let the memory get GCed.
Having the reference be null when being called is a very bad sign, but we check the condition.
*/
- final WeakReference<UserState> mUserStateWeakReference;
+ final WeakReference<AccessibilityUserState> mUserStateWeakReference;
final Intent mIntent;
private final Handler mMainHandler;
- private boolean mWasConnectedAndDied;
-
-
- public AccessibilityServiceConnection(UserState userState, Context context,
+ AccessibilityServiceConnection(AccessibilityUserState userState, Context context,
ComponentName componentName,
AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
Object lock, AccessibilitySecurityPolicy securityPolicy, SystemSupport systemSupport,
WindowManagerInternal windowManagerInternal,
- GlobalActionPerformer globalActionPerfomer, AccessibilityWindowManager awm) {
+ SystemActionPerformer systemActionPerfomer, AccessibilityWindowManager awm) {
super(context, componentName, accessibilityServiceInfo, id, mainHandler, lock,
- securityPolicy, systemSupport, windowManagerInternal, globalActionPerfomer, awm);
- mUserStateWeakReference = new WeakReference<UserState>(userState);
+ securityPolicy, systemSupport, windowManagerInternal, systemActionPerfomer, awm);
+ mUserStateWeakReference = new WeakReference<AccessibilityUserState>(userState);
mIntent = new Intent().setComponent(mComponentName);
mMainHandler = mainHandler;
mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
@@ -89,13 +85,13 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
}
public void bindLocked() {
- UserState userState = mUserStateWeakReference.get();
+ AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState == null) return;
final long identity = Binder.clearCallingIdentity();
try {
int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
| Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS;
- if (userState.getBindInstantServiceAllowed()) {
+ if (userState.getBindInstantServiceAllowedLocked()) {
flags |= Context.BIND_ALLOW_INSTANT;
}
if (mService == null && mContext.bindServiceAsUser(
@@ -109,7 +105,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
public void unbindLocked() {
mContext.unbindService(this);
- UserState userState = mUserStateWeakReference.get();
+ AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState == null) return;
userState.removeServiceLocked(this);
mSystemSupport.getMagnificationController().resetAllIfNeeded(mId);
@@ -123,7 +119,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
@Override
public void disableSelf() {
synchronized (mLock) {
- UserState userState = mUserStateWeakReference.get();
+ AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState == null) return;
if (userState.getEnabledServicesLocked().remove(mComponentName)) {
final long identity = Binder.clearCallingIdentity();
@@ -156,7 +152,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
}
}
mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service);
- UserState userState = mUserStateWeakReference.get();
+ AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState == null) return;
userState.addServiceLocked(this);
mSystemSupport.onClientChangeLocked(false);
@@ -169,20 +165,21 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
@Override
public AccessibilityServiceInfo getServiceInfo() {
- // Update crashed data
- mAccessibilityServiceInfo.crashed = mWasConnectedAndDied;
return mAccessibilityServiceInfo;
}
private void initializeService() {
IAccessibilityServiceClient serviceInterface = null;
synchronized (mLock) {
- UserState userState = mUserStateWeakReference.get();
+ AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState == null) return;
- Set<ComponentName> bindingServices = userState.getBindingServicesLocked();
- if (bindingServices.contains(mComponentName) || mWasConnectedAndDied) {
+ final Set<ComponentName> bindingServices = userState.getBindingServicesLocked();
+ final Set<ComponentName> crashedServices = userState.getCrashedServicesLocked();
+ if (bindingServices.contains(mComponentName)
+ || crashedServices.contains(mComponentName)) {
bindingServices.remove(mComponentName);
- mWasConnectedAndDied = false;
+ crashedServices.remove(mComponentName);
+ mAccessibilityServiceInfo.crashed = false;
serviceInterface = mServiceInterface;
}
// There's a chance that service is removed from enabled_accessibility_services setting
@@ -240,7 +237,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
if (!hasRightsToCurrentUserLocked()) {
return false;
}
- final UserState userState = mUserStateWeakReference.get();
+ final AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState == null) return false;
return userState.setSoftKeyboardModeLocked(showMode, mComponentName);
}
@@ -248,8 +245,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
@Override
public int getSoftKeyboardShowMode() {
- final UserState userState = mUserStateWeakReference.get();
- return (userState != null) ? userState.getSoftKeyboardShowMode() : 0;
+ final AccessibilityUserState userState = mUserStateWeakReference.get();
+ return (userState != null) ? userState.getSoftKeyboardShowModeLocked() : 0;
}
@Override
@@ -258,7 +255,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
if (!hasRightsToCurrentUserLocked()) {
return false;
}
- UserState userState = mUserStateWeakReference.get();
+ AccessibilityUserState userState = mUserStateWeakReference.get();
return (userState != null) && isAccessibilityButtonAvailableLocked(userState);
}
}
@@ -272,8 +269,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
if (!isConnectedLocked()) {
return;
}
- mWasConnectedAndDied = true;
- UserState userState = mUserStateWeakReference.get();
+ mAccessibilityServiceInfo.crashed = true;
+ AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState != null) {
userState.serviceDisconnectedLocked(this);
}
@@ -283,7 +280,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
}
}
- public boolean isAccessibilityButtonAvailableLocked(UserState userState) {
+ public boolean isAccessibilityButtonAvailableLocked(AccessibilityUserState userState) {
// If the service does not request the accessibility button, it isn't available
if (!mRequestAccessibilityButton) {
return false;
@@ -295,8 +292,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
}
// If magnification is on and assigned to the accessibility button, services cannot be
- if (userState.mIsNavBarMagnificationEnabled
- && userState.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+ if (userState.isNavBarMagnificationEnabledLocked()
+ && userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
return false;
}
@@ -314,13 +311,14 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
return true;
} else {
// With more than one active service, we derive the target from the user's settings
- if (userState.mServiceAssignedToAccessibilityButton == null) {
+ if (userState.getServiceAssignedToAccessibilityButtonLocked() == null) {
// If the user has not made an assignment, we treat the button as available to
// all services until the user interacts with the button to make an assignment
return true;
} else {
// If an assignment was made, it defines availability
- return mComponentName.equals(userState.mServiceAssignedToAccessibilityButton);
+ return mComponentName.equals(
+ userState.getServiceAssignedToAccessibilityButtonLocked());
}
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
new file mode 100644
index 000000000000..a0b9866e24d2
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -0,0 +1,616 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility;
+
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK;
+
+import android.accessibilityservice.AccessibilityService.SoftKeyboardShowMode;
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityShortcutInfo;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Binder;
+import android.os.RemoteCallbackList;
+import android.provider.Settings;
+import android.util.Slog;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManagerClient;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Class that hold states and settings per user and share between
+ * {@link AccessibilityManagerService} and {@link AccessibilityServiceConnection}.
+ */
+class AccessibilityUserState {
+ private static final String LOG_TAG = AccessibilityUserState.class.getSimpleName();
+
+ final int mUserId;
+
+ // Non-transient state.
+
+ final RemoteCallbackList<IAccessibilityManagerClient> mUserClients = new RemoteCallbackList<>();
+
+ // Transient state.
+
+ final ArrayList<AccessibilityServiceConnection> mBoundServices = new ArrayList<>();
+
+ final Map<ComponentName, AccessibilityServiceConnection> mComponentNameToServiceMap =
+ new HashMap<>();
+
+ final List<AccessibilityServiceInfo> mInstalledServices = new ArrayList<>();
+
+ final List<AccessibilityShortcutInfo> mInstalledShortcuts = new ArrayList<>();
+
+ final Set<ComponentName> mBindingServices = new HashSet<>();
+
+ final Set<ComponentName> mCrashedServices = new HashSet<>();
+
+ final Set<ComponentName> mEnabledServices = new HashSet<>();
+
+ final Set<ComponentName> mTouchExplorationGrantedServices = new HashSet<>();
+
+ private final ServiceInfoChangeListener mServiceInfoChangeListener;
+
+ private ComponentName mServiceAssignedToAccessibilityButton;
+
+ private ComponentName mServiceChangingSoftKeyboardMode;
+
+ private ComponentName mServiceToEnableWithShortcut;
+
+ private boolean mBindInstantServiceAllowed;
+ private boolean mIsAutoclickEnabled;
+ private boolean mIsDisplayMagnificationEnabled;
+ private boolean mIsFilterKeyEventsEnabled;
+ private boolean mIsNavBarMagnificationAssignedToAccessibilityButton;
+ private boolean mIsNavBarMagnificationEnabled;
+ private boolean mIsPerformGesturesEnabled;
+ private boolean mIsTextHighContrastEnabled;
+ private boolean mIsTouchExplorationEnabled;
+ private int mUserInteractiveUiTimeout;
+ private int mUserNonInteractiveUiTimeout;
+ private int mNonInteractiveUiTimeout = 0;
+ private int mInteractiveUiTimeout = 0;
+ private int mLastSentClientState = -1;
+
+ private Context mContext;
+
+ @SoftKeyboardShowMode
+ private int mSoftKeyboardShowMode = SHOW_MODE_AUTO;
+
+ interface ServiceInfoChangeListener {
+ void onServiceInfoChangedLocked(AccessibilityUserState userState);
+ }
+
+ AccessibilityUserState(int userId, @NonNull Context context,
+ @NonNull ServiceInfoChangeListener serviceInfoChangeListener) {
+ mUserId = userId;
+ mContext = context;
+ mServiceInfoChangeListener = serviceInfoChangeListener;
+ }
+
+ boolean isHandlingAccessibilityEventsLocked() {
+ return !mBoundServices.isEmpty() || !mBindingServices.isEmpty();
+ }
+
+ void onSwitchToAnotherUserLocked() {
+ // Unbind all services.
+ unbindAllServicesLocked();
+
+ // Clear service management state.
+ mBoundServices.clear();
+ mBindingServices.clear();
+ mCrashedServices.clear();
+
+ // Clear event management state.
+ mLastSentClientState = -1;
+
+ // clear UI timeout
+ mNonInteractiveUiTimeout = 0;
+ mInteractiveUiTimeout = 0;
+
+ // Clear state persisted in settings.
+ mEnabledServices.clear();
+ mTouchExplorationGrantedServices.clear();
+ mIsTouchExplorationEnabled = false;
+ mIsDisplayMagnificationEnabled = false;
+ mIsNavBarMagnificationEnabled = false;
+ mServiceAssignedToAccessibilityButton = null;
+ mIsNavBarMagnificationAssignedToAccessibilityButton = false;
+ mIsAutoclickEnabled = false;
+ mUserNonInteractiveUiTimeout = 0;
+ mUserInteractiveUiTimeout = 0;
+ }
+
+ void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
+ if (!mBoundServices.contains(serviceConnection)) {
+ serviceConnection.onAdded();
+ mBoundServices.add(serviceConnection);
+ mComponentNameToServiceMap.put(serviceConnection.getComponentName(), serviceConnection);
+ mServiceInfoChangeListener.onServiceInfoChangedLocked(this);
+ }
+ }
+
+ /**
+ * Removes a service.
+ * There are three states to a service here: off, bound, and binding.
+ * This stops tracking the service as bound.
+ *
+ * @param serviceConnection The service.
+ */
+ void removeServiceLocked(AccessibilityServiceConnection serviceConnection) {
+ mBoundServices.remove(serviceConnection);
+ serviceConnection.onRemoved();
+ if ((mServiceChangingSoftKeyboardMode != null)
+ && (mServiceChangingSoftKeyboardMode.equals(
+ serviceConnection.getServiceInfo().getComponentName()))) {
+ setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+ }
+ // It may be possible to bind a service twice, which confuses the map. Rebuild the map
+ // to make sure we can still reach a service
+ mComponentNameToServiceMap.clear();
+ for (int i = 0; i < mBoundServices.size(); i++) {
+ AccessibilityServiceConnection boundClient = mBoundServices.get(i);
+ mComponentNameToServiceMap.put(boundClient.getComponentName(), boundClient);
+ }
+ mServiceInfoChangeListener.onServiceInfoChangedLocked(this);
+ }
+
+ /**
+ * Make sure a services disconnected but still 'on' state is reflected in AccessibilityUserState
+ * There are four states to a service here: off, bound, and binding, and crashed.
+ * This drops a service from a bound state, to the crashed state.
+ * The crashed state describes the situation where a service used to be bound, but no longer is
+ * despite still being enabled.
+ *
+ * @param serviceConnection The service.
+ */
+ void serviceDisconnectedLocked(AccessibilityServiceConnection serviceConnection) {
+ removeServiceLocked(serviceConnection);
+ mCrashedServices.add(serviceConnection.getComponentName());
+ }
+
+ /**
+ * Set the soft keyboard mode. This mode is a bit odd, as it spans multiple settings.
+ * The ACCESSIBILITY_SOFT_KEYBOARD_MODE setting can be checked by the rest of the system
+ * to see if it should suppress showing the IME. The SHOW_IME_WITH_HARD_KEYBOARD setting
+ * setting can be changed by the user, and prevents the system from suppressing the soft
+ * keyboard when the hard keyboard is connected. The hard keyboard setting needs to defer
+ * to the user's preference, if they have supplied one.
+ *
+ * @param newMode The new mode
+ * @param requester The service requesting the change, so we can undo it when the
+ * service stops. Set to null if something other than a service is forcing
+ * the change.
+ *
+ * @return Whether or not the soft keyboard mode equals the new mode after the call
+ */
+ boolean setSoftKeyboardModeLocked(@SoftKeyboardShowMode int newMode,
+ @Nullable ComponentName requester) {
+ if ((newMode != SHOW_MODE_AUTO)
+ && (newMode != SHOW_MODE_HIDDEN)
+ && (newMode != SHOW_MODE_IGNORE_HARD_KEYBOARD)) {
+ Slog.w(LOG_TAG, "Invalid soft keyboard mode");
+ return false;
+ }
+ if (mSoftKeyboardShowMode == newMode) {
+ return true;
+ }
+
+ if (newMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
+ if (hasUserOverriddenHardKeyboardSetting()) {
+ // The user has specified a default for this setting
+ return false;
+ }
+ // Save the original value. But don't do this if the value in settings is already
+ // the new mode. That happens when we start up after a reboot, and we don't want
+ // to overwrite the value we had from when we first started controlling the setting.
+ if (getSoftKeyboardValueFromSettings() != SHOW_MODE_IGNORE_HARD_KEYBOARD) {
+ setOriginalHardKeyboardValue(getSecureIntForUser(
+ Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0, mUserId) != 0);
+ }
+ putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1, mUserId);
+ } else if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
+ putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
+ getOriginalHardKeyboardValue() ? 1 : 0, mUserId);
+ }
+
+ saveSoftKeyboardValueToSettings(newMode);
+ mSoftKeyboardShowMode = newMode;
+ mServiceChangingSoftKeyboardMode = requester;
+ for (int i = mBoundServices.size() - 1; i >= 0; i--) {
+ final AccessibilityServiceConnection service = mBoundServices.get(i);
+ service.notifySoftKeyboardShowModeChangedLocked(mSoftKeyboardShowMode);
+ }
+ return true;
+ }
+
+ @SoftKeyboardShowMode
+ int getSoftKeyboardShowModeLocked() {
+ return mSoftKeyboardShowMode;
+ }
+
+ /**
+ * If the settings are inconsistent with the internal state, make the internal state
+ * match the settings.
+ */
+ void reconcileSoftKeyboardModeWithSettingsLocked() {
+ final boolean showWithHardKeyboardSettings =
+ getSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0, mUserId) != 0;
+ if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
+ if (!showWithHardKeyboardSettings) {
+ // The user has overridden the setting. Respect that and prevent further changes
+ // to this behavior.
+ setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+ setUserOverridesHardKeyboardSetting();
+ }
+ }
+
+ // If the setting and the internal state are out of sync, set both to default
+ if (getSoftKeyboardValueFromSettings() != mSoftKeyboardShowMode) {
+ Slog.e(LOG_TAG, "Show IME setting inconsistent with internal state. Overwriting");
+ setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ SHOW_MODE_AUTO, mUserId);
+ }
+ }
+
+ boolean getBindInstantServiceAllowedLocked() {
+ return mBindInstantServiceAllowed;
+ }
+
+ /* Need to have a permission check on callee */
+ void setBindInstantServiceAllowedLocked(boolean allowed) {
+ mBindInstantServiceAllowed = allowed;
+ }
+
+ /**
+ * Returns binding service list.
+ */
+ Set<ComponentName> getBindingServicesLocked() {
+ return mBindingServices;
+ }
+
+ /**
+ * Returns crashed service list.
+ */
+ Set<ComponentName> getCrashedServicesLocked() {
+ return mCrashedServices;
+ }
+
+ /**
+ * Returns enabled service list.
+ */
+ Set<ComponentName> getEnabledServicesLocked() {
+ return mEnabledServices;
+ }
+
+ /**
+ * Remove service from crashed service list if users disable it.
+ */
+ void updateCrashedServicesIfNeededLocked() {
+ for (int i = 0, count = mInstalledServices.size(); i < count; i++) {
+ final AccessibilityServiceInfo installedService = mInstalledServices.get(i);
+ final ComponentName componentName = ComponentName.unflattenFromString(
+ installedService.getId());
+
+ if (mCrashedServices.contains(componentName)
+ && !mEnabledServices.contains(componentName)) {
+ // Remove it from mCrashedServices since users toggle the switch bar to retry.
+ mCrashedServices.remove(componentName);
+ }
+ }
+ }
+
+ List<AccessibilityServiceConnection> getBoundServicesLocked() {
+ return mBoundServices;
+ }
+
+ int getClientStateLocked(boolean isUiAutomationRunning) {
+ int clientState = 0;
+ final boolean a11yEnabled = isUiAutomationRunning
+ || isHandlingAccessibilityEventsLocked();
+ if (a11yEnabled) {
+ clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
+ }
+ // Touch exploration relies on enabled accessibility.
+ if (a11yEnabled && mIsTouchExplorationEnabled) {
+ clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
+ }
+ if (mIsTextHighContrastEnabled) {
+ clientState |= AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
+ }
+ return clientState;
+ }
+
+ private void setUserOverridesHardKeyboardSetting() {
+ final int softKeyboardSetting = getSecureIntForUser(
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId);
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ softKeyboardSetting | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN,
+ mUserId);
+ }
+
+ private boolean hasUserOverriddenHardKeyboardSetting() {
+ final int softKeyboardSetting = getSecureIntForUser(
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId);
+ return (softKeyboardSetting & SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN)
+ != 0;
+ }
+
+ private void setOriginalHardKeyboardValue(boolean originalHardKeyboardValue) {
+ final int oldSoftKeyboardSetting = getSecureIntForUser(
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId);
+ final int newSoftKeyboardSetting = oldSoftKeyboardSetting
+ & (~SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE)
+ | ((originalHardKeyboardValue) ? SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE : 0);
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ newSoftKeyboardSetting, mUserId);
+ }
+
+ private void saveSoftKeyboardValueToSettings(int softKeyboardShowMode) {
+ final int oldSoftKeyboardSetting = getSecureIntForUser(
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId);
+ final int newSoftKeyboardSetting = oldSoftKeyboardSetting & (~SHOW_MODE_MASK)
+ | softKeyboardShowMode;
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ newSoftKeyboardSetting, mUserId);
+ }
+
+ private int getSoftKeyboardValueFromSettings() {
+ return getSecureIntForUser(
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId)
+ & SHOW_MODE_MASK;
+ }
+
+ private boolean getOriginalHardKeyboardValue() {
+ return (getSecureIntForUser(
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId)
+ & SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE) != 0;
+ }
+
+ private void unbindAllServicesLocked() {
+ final List<AccessibilityServiceConnection> services = mBoundServices;
+ for (int count = services.size(); count > 0; count--) {
+ // When the service is unbound, it disappears from the list, so there's no need to
+ // keep track of the index
+ services.get(0).unbindLocked();
+ }
+ }
+
+ private int getSecureIntForUser(String key, int def, int userId) {
+ return Settings.Secure.getIntForUser(mContext.getContentResolver(), key, def, userId);
+ }
+
+ private void putSecureIntForUser(String key, int value, int userId) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(), key, value, userId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.append("User state[");
+ pw.println();
+ pw.append(" attributes:{id=").append(String.valueOf(mUserId));
+ pw.append(", touchExplorationEnabled=").append(String.valueOf(mIsTouchExplorationEnabled));
+ pw.append(", displayMagnificationEnabled=").append(String.valueOf(
+ mIsDisplayMagnificationEnabled));
+ pw.append(", navBarMagnificationEnabled=").append(String.valueOf(
+ mIsNavBarMagnificationEnabled));
+ pw.append(", autoclickEnabled=").append(String.valueOf(mIsAutoclickEnabled));
+ pw.append(", nonInteractiveUiTimeout=").append(String.valueOf(mNonInteractiveUiTimeout));
+ pw.append(", interactiveUiTimeout=").append(String.valueOf(mInteractiveUiTimeout));
+ pw.append(", installedServiceCount=").append(String.valueOf(mInstalledServices.size()));
+ pw.append("}");
+ pw.println();
+ pw.append(" Bound services:{");
+ final int serviceCount = mBoundServices.size();
+ for (int j = 0; j < serviceCount; j++) {
+ if (j > 0) {
+ pw.append(", ");
+ pw.println();
+ pw.append(" ");
+ }
+ AccessibilityServiceConnection service = mBoundServices.get(j);
+ service.dump(fd, pw, args);
+ }
+ pw.println("}");
+ pw.append(" Enabled services:{");
+ Iterator<ComponentName> it = mEnabledServices.iterator();
+ if (it.hasNext()) {
+ ComponentName componentName = it.next();
+ pw.append(componentName.toShortString());
+ while (it.hasNext()) {
+ componentName = it.next();
+ pw.append(", ");
+ pw.append(componentName.toShortString());
+ }
+ }
+ pw.println("}");
+ pw.append(" Binding services:{");
+ it = mBindingServices.iterator();
+ if (it.hasNext()) {
+ ComponentName componentName = it.next();
+ pw.append(componentName.toShortString());
+ while (it.hasNext()) {
+ componentName = it.next();
+ pw.append(", ");
+ pw.append(componentName.toShortString());
+ }
+ }
+ pw.println("}");
+ pw.append(" Crashed services:{");
+ it = mCrashedServices.iterator();
+ if (it.hasNext()) {
+ ComponentName componentName = it.next();
+ pw.append(componentName.toShortString());
+ while (it.hasNext()) {
+ componentName = it.next();
+ pw.append(", ");
+ pw.append(componentName.toShortString());
+ }
+ }
+ pw.println("}]");
+ }
+
+ public boolean isAutoclickEnabledLocked() {
+ return mIsAutoclickEnabled;
+ }
+
+ public void setAutoclickEnabledLocked(boolean enabled) {
+ mIsAutoclickEnabled = enabled;
+ }
+
+ public boolean isDisplayMagnificationEnabledLocked() {
+ return mIsDisplayMagnificationEnabled;
+ }
+
+ public void setDisplayMagnificationEnabledLocked(boolean enabled) {
+ mIsDisplayMagnificationEnabled = enabled;
+ }
+
+ public boolean isFilterKeyEventsEnabledLocked() {
+ return mIsFilterKeyEventsEnabled;
+ }
+
+ public void setFilterKeyEventsEnabledLocked(boolean enabled) {
+ mIsFilterKeyEventsEnabled = enabled;
+ }
+
+ public int getInteractiveUiTimeoutLocked() {
+ return mInteractiveUiTimeout;
+ }
+
+ public void setInteractiveUiTimeoutLocked(int timeout) {
+ mInteractiveUiTimeout = timeout;
+ }
+
+ public int getLastSentClientStateLocked() {
+ return mLastSentClientState;
+ }
+
+ public void setLastSentClientStateLocked(int state) {
+ mLastSentClientState = state;
+ }
+
+ public boolean isNavBarMagnificationAssignedToAccessibilityButtonLocked() {
+ return mIsNavBarMagnificationAssignedToAccessibilityButton;
+ }
+
+ public void setNavBarMagnificationAssignedToAccessibilityButtonLocked(boolean assigned) {
+ mIsNavBarMagnificationAssignedToAccessibilityButton = assigned;
+ }
+
+ public boolean isNavBarMagnificationEnabledLocked() {
+ return mIsNavBarMagnificationEnabled;
+ }
+
+ public void setNavBarMagnificationEnabledLocked(boolean enabled) {
+ mIsNavBarMagnificationEnabled = enabled;
+ }
+
+ public int getNonInteractiveUiTimeoutLocked() {
+ return mNonInteractiveUiTimeout;
+ }
+
+ public void setNonInteractiveUiTimeoutLocked(int timeout) {
+ mNonInteractiveUiTimeout = timeout;
+ }
+
+ public boolean isPerformGesturesEnabledLocked() {
+ return mIsPerformGesturesEnabled;
+ }
+
+ public void setPerformGesturesEnabledLocked(boolean enabled) {
+ mIsPerformGesturesEnabled = enabled;
+ }
+
+ public ComponentName getServiceAssignedToAccessibilityButtonLocked() {
+ return mServiceAssignedToAccessibilityButton;
+ }
+
+ public void setServiceAssignedToAccessibilityButtonLocked(ComponentName componentName) {
+ mServiceAssignedToAccessibilityButton = componentName;
+ }
+
+ public ComponentName getServiceChangingSoftKeyboardModeLocked() {
+ return mServiceChangingSoftKeyboardMode;
+ }
+
+ public void setServiceChangingSoftKeyboardModeLocked(
+ ComponentName serviceChangingSoftKeyboardMode) {
+ mServiceChangingSoftKeyboardMode = serviceChangingSoftKeyboardMode;
+ }
+
+ public ComponentName getServiceToEnableWithShortcutLocked() {
+ return mServiceToEnableWithShortcut;
+ }
+
+ public void setServiceToEnableWithShortcutLocked(ComponentName componentName) {
+ mServiceToEnableWithShortcut = componentName;
+ }
+
+ public boolean isTextHighContrastEnabledLocked() {
+ return mIsTextHighContrastEnabled;
+ }
+
+ public void setTextHighContrastEnabledLocked(boolean enabled) {
+ mIsTextHighContrastEnabled = enabled;
+ }
+
+ public boolean isTouchExplorationEnabledLocked() {
+ return mIsTouchExplorationEnabled;
+ }
+
+ public void setTouchExplorationEnabledLocked(boolean enabled) {
+ mIsTouchExplorationEnabled = enabled;
+ }
+
+ public int getUserInteractiveUiTimeoutLocked() {
+ return mUserInteractiveUiTimeout;
+ }
+
+ public void setUserInteractiveUiTimeoutLocked(int timeout) {
+ mUserInteractiveUiTimeout = timeout;
+ }
+
+ public int getUserNonInteractiveUiTimeoutLocked() {
+ return mUserNonInteractiveUiTimeout;
+ }
+
+ public void setUserNonInteractiveUiTimeoutLocked(int timeout) {
+ mUserNonInteractiveUiTimeout = timeout;
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 0038a27db384..cb858ac11b00 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -1472,11 +1472,27 @@ public class AccessibilityWindowManager {
public int getDisplayIdByUserIdAndWindowIdLocked(int userId, int windowId) {
final IBinder windowToken = getWindowTokenForUserAndWindowIdLocked(userId, windowId);
final int displayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken);
-
return displayId;
}
/**
+ * Returns the display list including all displays which are tracking windows.
+ *
+ * @return The display list.
+ */
+ public ArrayList<Integer> getDisplayListLocked() {
+ final ArrayList<Integer> displayList = new ArrayList<>();
+ final int count = mDisplayWindowsObservers.size();
+ for (int i = 0; i < count; i++) {
+ final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i);
+ if (observer != null) {
+ displayList.add(observer.mDisplayId);
+ }
+ }
+ return displayList;
+ }
+
+ /**
* Gets current input focused window token from window manager, and returns its windowId.
*
* @param userId The userId
diff --git a/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java
deleted file mode 100644
index b9b2654b93cc..000000000000
--- a/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- ** Copyright 2017, The Android Open Source Project
- **
- ** Licensed under the Apache License, Version 2.0 (the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
- **
- ** http://www.apache.org/licenses/LICENSE-2.0
- **
- ** Unless required by applicable law or agreed to in writing, software
- ** distributed under the License is distributed on an "AS IS" BASIS,
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ** See the License for the specific language governing permissions and
- ** limitations under the License.
- */
-
-package com.android.server.accessibility;
-
-import android.accessibilityservice.AccessibilityService;
-import android.app.StatusBarManager;
-import android.content.Context;
-import android.hardware.input.InputManager;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.view.InputDevice;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ScreenshotHelper;
-import com.android.server.LocalServices;
-import com.android.server.statusbar.StatusBarManagerInternal;
-import com.android.server.wm.WindowManagerInternal;
-
-import java.util.function.Supplier;
-
-/**
- * Handle the back-end of AccessibilityService#performGlobalAction
- */
-public class GlobalActionPerformer {
- private final WindowManagerInternal mWindowManagerService;
- private final Context mContext;
- private Supplier<ScreenshotHelper> mScreenshotHelperSupplier;
-
- public GlobalActionPerformer(Context context, WindowManagerInternal windowManagerInternal) {
- mContext = context;
- mWindowManagerService = windowManagerInternal;
- mScreenshotHelperSupplier = null;
- }
-
- // Used to mock ScreenshotHelper
- @VisibleForTesting
- public GlobalActionPerformer(Context context, WindowManagerInternal windowManagerInternal,
- Supplier<ScreenshotHelper> screenshotHelperSupplier) {
- this(context, windowManagerInternal);
- mScreenshotHelperSupplier = screenshotHelperSupplier;
- }
-
- public boolean performGlobalAction(int action) {
- final long identity = Binder.clearCallingIdentity();
- try {
- switch (action) {
- case AccessibilityService.GLOBAL_ACTION_BACK: {
- sendDownAndUpKeyEvents(KeyEvent.KEYCODE_BACK);
- }
- return true;
- case AccessibilityService.GLOBAL_ACTION_HOME: {
- sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HOME);
- }
- return true;
- case AccessibilityService.GLOBAL_ACTION_RECENTS: {
- return openRecents();
- }
- case AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS: {
- expandNotifications();
- }
- return true;
- case AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS: {
- expandQuickSettings();
- }
- return true;
- case AccessibilityService.GLOBAL_ACTION_POWER_DIALOG: {
- showGlobalActions();
- }
- return true;
- case AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN: {
- return toggleSplitScreen();
- }
- case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN: {
- return lockScreen();
- }
- case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT: {
- return takeScreenshot();
- }
- }
- return false;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- private void sendDownAndUpKeyEvents(int keyCode) {
- final long token = Binder.clearCallingIdentity();
-
- // Inject down.
- final long downTime = SystemClock.uptimeMillis();
- sendKeyEventIdentityCleared(keyCode, KeyEvent.ACTION_DOWN, downTime, downTime);
- sendKeyEventIdentityCleared(
- keyCode, KeyEvent.ACTION_UP, downTime, SystemClock.uptimeMillis());
-
- Binder.restoreCallingIdentity(token);
- }
-
- private void sendKeyEventIdentityCleared(int keyCode, int action, long downTime, long time) {
- KeyEvent event = KeyEvent.obtain(downTime, time, action, keyCode, 0, 0,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM,
- InputDevice.SOURCE_KEYBOARD, null);
- InputManager.getInstance()
- .injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
- event.recycle();
- }
-
- private void expandNotifications() {
- final long token = Binder.clearCallingIdentity();
-
- StatusBarManager statusBarManager = (StatusBarManager) mContext.getSystemService(
- android.app.Service.STATUS_BAR_SERVICE);
- statusBarManager.expandNotificationsPanel();
-
- Binder.restoreCallingIdentity(token);
- }
-
- private void expandQuickSettings() {
- final long token = Binder.clearCallingIdentity();
-
- StatusBarManager statusBarManager = (StatusBarManager) mContext.getSystemService(
- android.app.Service.STATUS_BAR_SERVICE);
- statusBarManager.expandSettingsPanel();
-
- Binder.restoreCallingIdentity(token);
- }
-
- private boolean openRecents() {
- final long token = Binder.clearCallingIdentity();
- try {
- StatusBarManagerInternal statusBarService = LocalServices.getService(
- StatusBarManagerInternal.class);
- if (statusBarService == null) {
- return false;
- }
- statusBarService.toggleRecentApps();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- return true;
- }
-
- private void showGlobalActions() {
- mWindowManagerService.showGlobalActions();
- }
-
- private boolean toggleSplitScreen() {
- final long token = Binder.clearCallingIdentity();
- try {
- StatusBarManagerInternal statusBarService = LocalServices.getService(
- StatusBarManagerInternal.class);
- if (statusBarService == null) {
- return false;
- }
- statusBarService.toggleSplitScreen();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- return true;
- }
-
- private boolean lockScreen() {
- mContext.getSystemService(PowerManager.class).goToSleep(SystemClock.uptimeMillis(),
- PowerManager.GO_TO_SLEEP_REASON_ACCESSIBILITY, 0);
- mWindowManagerService.lockNow();
- return true;
- }
-
- private boolean takeScreenshot() {
- ScreenshotHelper screenshotHelper = (mScreenshotHelperSupplier != null)
- ? mScreenshotHelperSupplier.get() : new ScreenshotHelper(mContext);
- screenshotHelper.takeScreenshot(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN,
- true, true, new Handler(Looper.getMainLooper()), null);
- return true;
- }
-}
diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
new file mode 100644
index 000000000000..19ac0d3c1024
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility;
+
+import android.accessibilityservice.AccessibilityService;
+import android.app.StatusBarManager;
+import android.content.Context;
+import android.hardware.input.InputManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ScreenshotHelper;
+import com.android.server.LocalServices;
+import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.wm.WindowManagerInternal;
+
+import java.util.function.Supplier;
+
+/**
+ * Handle the back-end of AccessibilityService#performGlobalAction
+ */
+public class SystemActionPerformer {
+ private final WindowManagerInternal mWindowManagerService;
+ private final Context mContext;
+ private Supplier<ScreenshotHelper> mScreenshotHelperSupplier;
+
+ public SystemActionPerformer(Context context, WindowManagerInternal windowManagerInternal) {
+ mContext = context;
+ mWindowManagerService = windowManagerInternal;
+ mScreenshotHelperSupplier = null;
+ }
+
+ // Used to mock ScreenshotHelper
+ @VisibleForTesting
+ public SystemActionPerformer(Context context, WindowManagerInternal windowManagerInternal,
+ Supplier<ScreenshotHelper> screenshotHelperSupplier) {
+ this(context, windowManagerInternal);
+ mScreenshotHelperSupplier = screenshotHelperSupplier;
+ }
+
+ /**
+ * Performe the system action matching the given action id.
+ */
+ public boolean performSystemAction(int action) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ switch (action) {
+ case AccessibilityService.GLOBAL_ACTION_BACK: {
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_BACK);
+ }
+ return true;
+ case AccessibilityService.GLOBAL_ACTION_HOME: {
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HOME);
+ }
+ return true;
+ case AccessibilityService.GLOBAL_ACTION_RECENTS: {
+ return openRecents();
+ }
+ case AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS: {
+ expandNotifications();
+ }
+ return true;
+ case AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS: {
+ expandQuickSettings();
+ }
+ return true;
+ case AccessibilityService.GLOBAL_ACTION_POWER_DIALOG: {
+ showGlobalActions();
+ }
+ return true;
+ case AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN: {
+ return toggleSplitScreen();
+ }
+ case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN: {
+ return lockScreen();
+ }
+ case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT: {
+ return takeScreenshot();
+ }
+ }
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private void sendDownAndUpKeyEvents(int keyCode) {
+ final long token = Binder.clearCallingIdentity();
+
+ // Inject down.
+ final long downTime = SystemClock.uptimeMillis();
+ sendKeyEventIdentityCleared(keyCode, KeyEvent.ACTION_DOWN, downTime, downTime);
+ sendKeyEventIdentityCleared(
+ keyCode, KeyEvent.ACTION_UP, downTime, SystemClock.uptimeMillis());
+
+ Binder.restoreCallingIdentity(token);
+ }
+
+ private void sendKeyEventIdentityCleared(int keyCode, int action, long downTime, long time) {
+ KeyEvent event = KeyEvent.obtain(downTime, time, action, keyCode, 0, 0,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM,
+ InputDevice.SOURCE_KEYBOARD, null);
+ InputManager.getInstance()
+ .injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+ event.recycle();
+ }
+
+ private void expandNotifications() {
+ final long token = Binder.clearCallingIdentity();
+
+ StatusBarManager statusBarManager = (StatusBarManager) mContext.getSystemService(
+ android.app.Service.STATUS_BAR_SERVICE);
+ statusBarManager.expandNotificationsPanel();
+
+ Binder.restoreCallingIdentity(token);
+ }
+
+ private void expandQuickSettings() {
+ final long token = Binder.clearCallingIdentity();
+
+ StatusBarManager statusBarManager = (StatusBarManager) mContext.getSystemService(
+ android.app.Service.STATUS_BAR_SERVICE);
+ statusBarManager.expandSettingsPanel();
+
+ Binder.restoreCallingIdentity(token);
+ }
+
+ private boolean openRecents() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ StatusBarManagerInternal statusBarService = LocalServices.getService(
+ StatusBarManagerInternal.class);
+ if (statusBarService == null) {
+ return false;
+ }
+ statusBarService.toggleRecentApps();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return true;
+ }
+
+ private void showGlobalActions() {
+ mWindowManagerService.showGlobalActions();
+ }
+
+ private boolean toggleSplitScreen() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ StatusBarManagerInternal statusBarService = LocalServices.getService(
+ StatusBarManagerInternal.class);
+ if (statusBarService == null) {
+ return false;
+ }
+ statusBarService.toggleSplitScreen();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return true;
+ }
+
+ private boolean lockScreen() {
+ mContext.getSystemService(PowerManager.class).goToSleep(SystemClock.uptimeMillis(),
+ PowerManager.GO_TO_SLEEP_REASON_ACCESSIBILITY, 0);
+ mWindowManagerService.lockNow();
+ return true;
+ }
+
+ private boolean takeScreenshot() {
+ ScreenshotHelper screenshotHelper = (mScreenshotHelperSupplier != null)
+ ? mScreenshotHelperSupplier.get() : new ScreenshotHelper(mContext);
+ screenshotHelper.takeScreenshot(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN,
+ true, true, new Handler(Looper.getMainLooper()), null);
+ return true;
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 79d975dac2b2..7dd4a7089954 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -87,7 +87,7 @@ class UiAutomationManager {
AccessibilitySecurityPolicy securityPolicy,
AbstractAccessibilityServiceConnection.SystemSupport systemSupport,
WindowManagerInternal windowManagerInternal,
- GlobalActionPerformer globalActionPerfomer,
+ SystemActionPerformer systemActionPerfomer,
AccessibilityWindowManager awm, int flags) {
synchronized (mLock) {
accessibilityServiceInfo.setComponentName(COMPONENT_NAME);
@@ -108,7 +108,7 @@ class UiAutomationManager {
mSystemSupport = systemSupport;
mUiAutomationService = new UiAutomationService(context, accessibilityServiceInfo, id,
mainHandler, mLock, securityPolicy, systemSupport, windowManagerInternal,
- globalActionPerfomer, awm);
+ systemActionPerfomer, awm);
mUiAutomationServiceOwner = owner;
mUiAutomationFlags = flags;
mUiAutomationServiceInfo = accessibilityServiceInfo;
@@ -226,9 +226,9 @@ class UiAutomationManager {
int id, Handler mainHandler, Object lock,
AccessibilitySecurityPolicy securityPolicy,
SystemSupport systemSupport, WindowManagerInternal windowManagerInternal,
- GlobalActionPerformer globalActionPerfomer, AccessibilityWindowManager awm) {
+ SystemActionPerformer systemActionPerfomer, AccessibilityWindowManager awm) {
super(context, COMPONENT_NAME, accessibilityServiceInfo, id, mainHandler, lock,
- securityPolicy, systemSupport, windowManagerInternal, globalActionPerfomer,
+ securityPolicy, systemSupport, windowManagerInternal, systemActionPerfomer,
awm);
mMainHandler = mainHandler;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java
index 7e8fb295d036..3dfe59e142a6 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java
@@ -408,9 +408,6 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen
cancelGesture();
}
- public boolean firstTapDetected() {
- return mFirstTapDetected;
- }
@Override
public void onLongPress(MotionEvent e) {
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 dc7a9aaf966d..5ac3b69549c1 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
@@ -72,10 +72,16 @@ class EventDispatcher {
*
* @param prototype The prototype from which to create the injected events.
* @param action The action of the event.
+ * @param rawEvent The original event prior to magnification or other transformations.
* @param pointerIdBits The bits of the pointers to send.
* @param policyFlags The policy flags associated with the event.
*/
- void sendMotionEvent(MotionEvent prototype, int action, int pointerIdBits, int policyFlags) {
+ void sendMotionEvent(
+ MotionEvent prototype,
+ int action,
+ MotionEvent rawEvent,
+ int pointerIdBits,
+ int policyFlags) {
prototype.setAction(action);
MotionEvent event = null;
@@ -105,11 +111,8 @@ class EventDispatcher {
// Make sure that the user will see the event.
policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER;
- // TODO: For now pass null for the raw event since the touch
- // explorer is the last event transformation and it does
- // not care about the raw event.
if (mReceiver != null) {
- mReceiver.onMotionEvent(event, null, policyFlags);
+ mReceiver.onMotionEvent(event, rawEvent, policyFlags);
} else {
Slog.e(LOG_TAG, "Error sending event: no receiver specified.");
}
@@ -280,7 +283,12 @@ class EventDispatcher {
if (!isInjectedPointerDown(pointerId)) {
pointerIdBits |= (1 << pointerId);
final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, i);
- sendMotionEvent(prototype, action, pointerIdBits, policyFlags);
+ sendMotionEvent(
+ prototype,
+ action,
+ mState.getLastReceivedEvent(),
+ pointerIdBits,
+ policyFlags);
}
}
}
@@ -303,7 +311,8 @@ class EventDispatcher {
}
pointerIdBits |= (1 << pointerId);
final int action = computeInjectionAction(MotionEvent.ACTION_UP, i);
- sendMotionEvent(prototype, action, pointerIdBits, policyFlags);
+ sendMotionEvent(
+ prototype, action, mState.getLastReceivedEvent(), pointerIdBits, policyFlags);
}
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index f4ac82157d04..b62e260aacad 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -183,9 +183,9 @@ public class TouchExplorer extends BaseEventStreamTransformation
private void clear() {
// If we have not received an event then we are in initial
// state. Therefore, there is not need to clean anything.
- MotionEvent event = mReceivedPointerTracker.getLastReceivedEvent();
+ MotionEvent event = mState.getLastReceivedEvent();
if (event != null) {
- clear(mReceivedPointerTracker.getLastReceivedEvent(), WindowManagerPolicy.FLAG_TRUSTED);
+ clear(event, WindowManagerPolicy.FLAG_TRUSTED);
}
}
@@ -229,7 +229,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
Slog.d(LOG_TAG, mState.toString());
}
- mReceivedPointerTracker.onMotionEvent(rawEvent);
+ mState.onReceivedMotionEvent(rawEvent);
if (mGestureDetector.onMotionEvent(event, rawEvent, policyFlags)) {
// Event was handled by the gesture detector.
@@ -250,9 +250,9 @@ public class TouchExplorer extends BaseEventStreamTransformation
} else if (mState.isTouchExploring()) {
handleMotionEventStateTouchExploring(event, rawEvent, policyFlags);
} else if (mState.isDragging()) {
- handleMotionEventStateDragging(event, policyFlags);
+ handleMotionEventStateDragging(event, rawEvent, policyFlags);
} else if (mState.isDelegating()) {
- handleMotionEventStateDelegating(event, policyFlags);
+ handleMotionEventStateDelegating(event, rawEvent, policyFlags);
} else if (mState.isGestureDetecting()) {
// Already handled.
} else {
@@ -292,7 +292,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
}
// Pointers should not be zero when running this command.
- if (mReceivedPointerTracker.getLastReceivedEvent().getPointerCount() == 0) {
+ if (mState.getLastReceivedEvent().getPointerCount() == 0) {
return;
}
// Try to use the standard accessibility API to long click
@@ -368,11 +368,15 @@ public class TouchExplorer extends BaseEventStreamTransformation
// We have just decided that the user is touch,
// exploring so start sending events.
- mSendHoverEnterAndMoveDelayed.addEvent(event);
+ mSendHoverEnterAndMoveDelayed.addEvent(event, mState.getLastReceivedEvent());
mSendHoverEnterAndMoveDelayed.forceSendAndRemove();
mSendHoverExitDelayed.cancel();
mDispatcher.sendMotionEvent(
- event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags);
+ event,
+ MotionEvent.ACTION_HOVER_MOVE,
+ mState.getLastReceivedEvent(),
+ pointerIdBits,
+ policyFlags);
return true;
}
}
@@ -387,7 +391,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
switch (event.getActionMasked()) {
// The only way to leave the clear state is for a pointer to go down.
case MotionEvent.ACTION_DOWN:
- handleActionDown(event, policyFlags);
+ handleActionDown(event, rawEvent, policyFlags);
break;
default:
// Some other nonsensical event.
@@ -399,7 +403,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
* Handles ACTION_DOWN while in the clear or touch interacting states. This event represents the
* first finger touching the screen.
*/
- private void handleActionDown(MotionEvent event, int policyFlags) {
+ private void handleActionDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
mAms.onTouchInteractionStart();
// If we still have not notified the user for the last
@@ -407,24 +411,12 @@ public class TouchExplorer extends BaseEventStreamTransformation
// we resent the delayed callback and wait again.
mSendHoverEnterAndMoveDelayed.cancel();
mSendHoverExitDelayed.cancel();
-
// If a touch exploration gesture is in progress send events for its end.
if (mState.isTouchExploring()) {
sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
}
- // Avoid duplicated TYPE_TOUCH_INTERACTION_START event when 2nd tap of double
- // tap.
- if (!mGestureDetector.firstTapDetected() && mState.isClear()) {
- mSendTouchExplorationEndDelayed.forceSendAndRemove();
- mSendTouchInteractionEndDelayed.forceSendAndRemove();
- mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_START);
- } else {
- // Let gesture to handle to avoid duplicated TYPE_TOUCH_INTERACTION_END event.
- mSendTouchInteractionEndDelayed.cancel();
- }
-
- if (!mGestureDetector.firstTapDetected() && !mState.isTouchExploring()) {
+ if (mState.isClear()) {
if (!mSendHoverEnterAndMoveDelayed.isPending()) {
// Queue a delayed transition to STATE_TOUCH_EXPLORING.
// If we do not detect that this is a gesture, delegation or drag the transition
@@ -432,11 +424,17 @@ public class TouchExplorer extends BaseEventStreamTransformation
// The idea is to avoid getting stuck in STATE_TOUCH_INTERACTING
final int pointerId = mReceivedPointerTracker.getPrimaryPointerId();
final int pointerIdBits = (1 << pointerId);
- mSendHoverEnterAndMoveDelayed.post(event, pointerIdBits, policyFlags);
+ mSendHoverEnterAndMoveDelayed.post(event, rawEvent, pointerIdBits, policyFlags);
} else {
// Cache the event until we discern exploration from gesturing.
- mSendHoverEnterAndMoveDelayed.addEvent(event);
+ mSendHoverEnterAndMoveDelayed.addEvent(event, rawEvent);
}
+ mSendTouchExplorationEndDelayed.forceSendAndRemove();
+ mSendTouchInteractionEndDelayed.forceSendAndRemove();
+ mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_START);
+ } else {
+ // Avoid duplicated TYPE_TOUCH_INTERACTION_START event when 2nd tap of double tap.
+ mSendTouchInteractionEndDelayed.cancel();
}
}
@@ -453,7 +451,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
case MotionEvent.ACTION_DOWN:
// Continue the previous interaction.
mSendTouchInteractionEndDelayed.cancel();
- handleActionDown(event, policyFlags);
+ handleActionDown(event, rawEvent, policyFlags);
break;
case MotionEvent.ACTION_POINTER_DOWN:
handleActionPointerDown();
@@ -462,7 +460,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
handleActionMoveStateTouchInteracting(event, rawEvent, policyFlags);
break;
case MotionEvent.ACTION_UP:
- handleActionUp(event, policyFlags);
+ handleActionUp(event, rawEvent, policyFlags);
break;
}
}
@@ -487,7 +485,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
handleActionMoveStateTouchExploring(event, rawEvent, policyFlags);
break;
case MotionEvent.ACTION_UP:
- handleActionUp(event, policyFlags);
+ handleActionUp(event, rawEvent, policyFlags);
break;
default:
break;
@@ -520,7 +518,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
// figure out what the user is doing.
if (mSendHoverEnterAndMoveDelayed.isPending()) {
// Cache the event until we discern exploration from gesturing.
- mSendHoverEnterAndMoveDelayed.addEvent(event);
+ mSendHoverEnterAndMoveDelayed.addEvent(event, rawEvent);
}
break;
case 2:
@@ -538,7 +536,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
mDraggingPointerId = pointerId;
event.setEdgeFlags(mReceivedPointerTracker.getLastReceivedDownEdgeFlags());
mDispatcher.sendMotionEvent(
- event, MotionEvent.ACTION_DOWN, pointerIdBits, policyFlags);
+ event, MotionEvent.ACTION_DOWN, rawEvent, pointerIdBits, policyFlags);
} else {
// Two pointers moving arbitrary are delegated to the view hierarchy.
mState.startDelegating();
@@ -558,13 +556,13 @@ public class TouchExplorer extends BaseEventStreamTransformation
* Handles ACTION_UP while in the touch interacting state. This event represents all fingers
* being lifted from the screen.
*/
- private void handleActionUp(MotionEvent event, int policyFlags) {
+ private void handleActionUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
mAms.onTouchInteractionEnd();
final int pointerId = event.getPointerId(event.getActionIndex());
final int pointerIdBits = (1 << pointerId);
if (mSendHoverEnterAndMoveDelayed.isPending()) {
// If we have not delivered the enter schedule an exit.
- mSendHoverExitDelayed.post(event, pointerIdBits, policyFlags);
+ mSendHoverExitDelayed.post(event, rawEvent, pointerIdBits, policyFlags);
} else {
// The user is touch exploring so we send events for end.
sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
@@ -588,7 +586,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
// Touch exploration.
sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags);
mDispatcher.sendMotionEvent(
- event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags);
+ event, MotionEvent.ACTION_HOVER_MOVE, rawEvent, pointerIdBits, policyFlags);
break;
case 2:
if (mSendHoverEnterAndMoveDelayed.isPending()) {
@@ -638,7 +636,8 @@ public class TouchExplorer extends BaseEventStreamTransformation
* @param event The event to be handled.
* @param policyFlags The policy flags associated with the event.
*/
- private void handleMotionEventStateDragging(MotionEvent event, int policyFlags) {
+ private void handleMotionEventStateDragging(
+ MotionEvent event, MotionEvent rawEvent, int policyFlags) {
int pointerIdBits = 0;
// Clear the dragging pointer id if it's no longer valid.
if (event.findPointerIndex(mDraggingPointerId) == -1) {
@@ -662,7 +661,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
mState.startDelegating();
if (mDraggingPointerId != INVALID_POINTER_ID) {
mDispatcher.sendMotionEvent(
- event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
+ event, MotionEvent.ACTION_UP, rawEvent, pointerIdBits, policyFlags);
}
mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
} break;
@@ -681,6 +680,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
mDispatcher.sendMotionEvent(
event,
MotionEvent.ACTION_MOVE,
+ rawEvent,
pointerIdBits,
policyFlags);
} else {
@@ -690,7 +690,11 @@ public class TouchExplorer extends BaseEventStreamTransformation
// Remove move history before send injected non-move events
event = MotionEvent.obtainNoHistory(event);
// Send an event to the end of the drag gesture.
- mDispatcher.sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits,
+ mDispatcher.sendMotionEvent(
+ event,
+ MotionEvent.ACTION_UP,
+ rawEvent,
+ pointerIdBits,
policyFlags);
// Deliver all pointers to the view hierarchy.
mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
@@ -700,7 +704,11 @@ public class TouchExplorer extends BaseEventStreamTransformation
mState.startDelegating();
event = MotionEvent.obtainNoHistory(event);
// Send an event to the end of the drag gesture.
- mDispatcher.sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits,
+ mDispatcher.sendMotionEvent(
+ event,
+ MotionEvent.ACTION_UP,
+ rawEvent,
+ pointerIdBits,
policyFlags);
// Deliver all pointers to the view hierarchy.
mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
@@ -713,7 +721,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
mDraggingPointerId = INVALID_POINTER_ID;
// Send an event to the end of the drag gesture.
mDispatcher.sendMotionEvent(
- event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
+ event, MotionEvent.ACTION_UP, rawEvent, pointerIdBits, policyFlags);
}
} break;
case MotionEvent.ACTION_UP: {
@@ -726,7 +734,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
mDraggingPointerId = INVALID_POINTER_ID;
// Send an event to the end of the drag gesture.
mDispatcher.sendMotionEvent(
- event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
+ event, MotionEvent.ACTION_UP, rawEvent, pointerIdBits, policyFlags);
}
} break;
}
@@ -738,7 +746,8 @@ public class TouchExplorer extends BaseEventStreamTransformation
* @param event The event to be handled.
* @param policyFlags The policy flags associated with the event.
*/
- private void handleMotionEventStateDelegating(MotionEvent event, int policyFlags) {
+ private void handleMotionEventStateDelegating(
+ MotionEvent event, MotionEvent rawEvent, int policyFlags) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
Slog.e(LOG_TAG, "Delegating state can only be reached if "
@@ -749,7 +758,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
case MotionEvent.ACTION_UP: {
// Deliver the event.
mDispatcher.sendMotionEvent(
- event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
+ event, event.getAction(), rawEvent, ALL_POINTER_ID_BITS, policyFlags);
// Announce the end of a the touch interaction.
mAms.onTouchInteractionEnd();
@@ -759,7 +768,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
default: {
// Deliver the event.
mDispatcher.sendMotionEvent(
- event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
+ event, event.getAction(), rawEvent, ALL_POINTER_ID_BITS, policyFlags);
}
}
}
@@ -792,7 +801,11 @@ public class TouchExplorer extends BaseEventStreamTransformation
mSendTouchExplorationEndDelayed.post();
}
mDispatcher.sendMotionEvent(
- event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits, policyFlags);
+ event,
+ MotionEvent.ACTION_HOVER_EXIT,
+ mState.getLastReceivedEvent(),
+ pointerIdBits,
+ policyFlags);
}
}
@@ -807,7 +820,11 @@ public class TouchExplorer extends BaseEventStreamTransformation
if (event != null && event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) {
final int pointerIdBits = event.getPointerIdBits();
mDispatcher.sendMotionEvent(
- event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags);
+ event,
+ MotionEvent.ACTION_HOVER_ENTER,
+ mState.getLastReceivedEvent(),
+ pointerIdBits,
+ policyFlags);
}
}
@@ -891,20 +908,23 @@ public class TouchExplorer extends BaseEventStreamTransformation
private final String LOG_TAG_SEND_HOVER_DELAYED = "SendHoverEnterAndMoveDelayed";
private final List<MotionEvent> mEvents = new ArrayList<MotionEvent>();
+ private final List<MotionEvent> mRawEvents = new ArrayList<MotionEvent>();
private int mPointerIdBits;
private int mPolicyFlags;
- public void post(MotionEvent event, int pointerIdBits, int policyFlags) {
+ public void post(
+ MotionEvent event, MotionEvent rawEvent, int pointerIdBits, int policyFlags) {
cancel();
- addEvent(event);
+ addEvent(event, rawEvent);
mPointerIdBits = pointerIdBits;
mPolicyFlags = policyFlags;
mHandler.postDelayed(this, mDetermineUserIntentTimeout);
}
- public void addEvent(MotionEvent event) {
+ public void addEvent(MotionEvent event, MotionEvent rawEvent) {
mEvents.add(MotionEvent.obtain(event));
+ mRawEvents.add(MotionEvent.obtain(rawEvent));
}
public void cancel() {
@@ -925,6 +945,10 @@ public class TouchExplorer extends BaseEventStreamTransformation
for (int i = eventCount - 1; i >= 0; i--) {
mEvents.remove(i).recycle();
}
+ final int rawEventcount = mRawEvents.size();
+ for (int i = rawEventcount - 1; i >= 0; i--) {
+ mRawEvents.remove(i).recycle();
+ }
}
public void forceSendAndRemove() {
@@ -939,10 +963,10 @@ public class TouchExplorer extends BaseEventStreamTransformation
mDispatcher.sendAccessibilityEvent(
AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
- if (!mEvents.isEmpty()) {
+ if (!mEvents.isEmpty() && !mRawEvents.isEmpty()) {
// Deliver a down event.
mDispatcher.sendMotionEvent(mEvents.get(0), MotionEvent.ACTION_HOVER_ENTER,
- mPointerIdBits, mPolicyFlags);
+ mRawEvents.get(0), mPointerIdBits, mPolicyFlags);
if (DEBUG) {
Slog.d(LOG_TAG_SEND_HOVER_DELAYED,
"Injecting motion event: ACTION_HOVER_ENTER");
@@ -952,7 +976,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
final int eventCount = mEvents.size();
for (int i = 1; i < eventCount; i++) {
mDispatcher.sendMotionEvent(mEvents.get(i), MotionEvent.ACTION_HOVER_MOVE,
- mPointerIdBits, mPolicyFlags);
+ mRawEvents.get(i), mPointerIdBits, mPolicyFlags);
if (DEBUG) {
Slog.d(LOG_TAG_SEND_HOVER_DELAYED,
"Injecting motion event: ACTION_HOVER_MOVE");
@@ -970,12 +994,15 @@ public class TouchExplorer extends BaseEventStreamTransformation
private final String LOG_TAG_SEND_HOVER_DELAYED = "SendHoverExitDelayed";
private MotionEvent mPrototype;
+ private MotionEvent mRawEvent;
private int mPointerIdBits;
private int mPolicyFlags;
- public void post(MotionEvent prototype, int pointerIdBits, int policyFlags) {
+ public void post(
+ MotionEvent prototype, MotionEvent rawEvent, int pointerIdBits, int policyFlags) {
cancel();
mPrototype = MotionEvent.obtain(prototype);
+ mRawEvent = MotionEvent.obtain(rawEvent);
mPointerIdBits = pointerIdBits;
mPolicyFlags = policyFlags;
mHandler.postDelayed(this, mDetermineUserIntentTimeout);
@@ -993,8 +1020,14 @@ public class TouchExplorer extends BaseEventStreamTransformation
}
private void clear() {
- mPrototype.recycle();
+ if (mPrototype != null) {
+ mPrototype.recycle();
+ }
+ if (mRawEvent != null) {
+ mRawEvent.recycle();
+ }
mPrototype = null;
+ mRawEvent = null;
mPointerIdBits = -1;
mPolicyFlags = 0;
}
@@ -1011,8 +1044,12 @@ public class TouchExplorer extends BaseEventStreamTransformation
Slog.d(LOG_TAG_SEND_HOVER_DELAYED, "Injecting motion event:"
+ " ACTION_HOVER_EXIT");
}
- mDispatcher.sendMotionEvent(mPrototype, MotionEvent.ACTION_HOVER_EXIT,
- mPointerIdBits, mPolicyFlags);
+ mDispatcher.sendMotionEvent(
+ mPrototype,
+ MotionEvent.ACTION_HOVER_EXIT,
+ mRawEvent,
+ mPointerIdBits,
+ mPolicyFlags);
if (!mSendTouchExplorationEndDelayed.isPending()) {
mSendTouchExplorationEndDelayed.cancel();
mSendTouchExplorationEndDelayed.post();
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 49938fa4c6b9..f463260a9d02 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
@@ -71,6 +71,7 @@ public class TouchState {
// Helper class to track received pointers.
// Todo: collapse or hide this class so multiple classes don't modify it.
private final ReceivedPointerTracker mReceivedPointerTracker;
+ private MotionEvent mLastReceivedEvent;
public TouchState() {
mReceivedPointerTracker = new ReceivedPointerTracker();
@@ -80,6 +81,10 @@ public class TouchState {
public void clear() {
setState(STATE_CLEAR);
// Reset the pointer trackers.
+ if (mLastReceivedEvent != null) {
+ mLastReceivedEvent.recycle();
+ mLastReceivedEvent = null;
+ }
mReceivedPointerTracker.clear();
}
@@ -89,6 +94,10 @@ public class TouchState {
* @param rawEvent The raw touch event.
*/
public void onReceivedMotionEvent(MotionEvent rawEvent) {
+ if (mLastReceivedEvent != null) {
+ mLastReceivedEvent.recycle();
+ }
+ mLastReceivedEvent = MotionEvent.obtain(rawEvent);
mReceivedPointerTracker.onMotionEvent(rawEvent);
}
@@ -216,6 +225,11 @@ public class TouchState {
return mReceivedPointerTracker;
}
+ /** @return The last received event. */
+ public MotionEvent getLastReceivedEvent() {
+ return mLastReceivedEvent;
+ }
+
/** 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";
@@ -232,8 +246,6 @@ public class TouchState {
// or if it goes up the next one that most recently went down.
private int mPrimaryPointerId;
- // Keep track of the last up pointer data.
- private MotionEvent mLastReceivedEvent;
ReceivedPointerTracker() {
clear();
@@ -254,11 +266,6 @@ public class TouchState {
* @param event The event to process.
*/
public void onMotionEvent(MotionEvent event) {
- if (mLastReceivedEvent != null) {
- mLastReceivedEvent.recycle();
- }
- mLastReceivedEvent = MotionEvent.obtain(event);
-
final int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN:
@@ -279,11 +286,6 @@ public class TouchState {
}
}
- /** @return The last received event. */
- public MotionEvent getLastReceivedEvent() {
- return mLastReceivedEvent;
- }
-
/** @return The number of received pointers that are down. */
public int getReceivedPointerDownCount() {
return Integer.bitCount(mReceivedPointersDown);
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index fea4e9047f83..81ce359cc078 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -631,8 +631,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
onClickIntent = mDevicePolicyManagerInternal.createShowAdminSupportIntent(
providerUserId, true);
} else {
- final SuspendDialogInfo dialogInfo = mPackageManagerInternal
- .getSuspendedDialogInfo(providerPackage, providerUserId);
+ final SuspendDialogInfo dialogInfo =
+ mPackageManagerInternal.getSuspendedDialogInfo(providerPackage,
+ suspendingPackage, providerUserId);
onClickIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(
providerPackage, suspendingPackage, dialogInfo, providerUserId);
}
diff --git a/services/art-profile b/services/art-profile
index cbc4627b2129..a0338d55c55f 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -20824,3 +20824,532 @@ Lcom/android/timezone/distro/DistroException;
Lcom/google/android/startop/iorap/IorapForwardingService$AppLaunchObserver;
Lcom/google/android/startop/iorap/IorapForwardingService$BinderConnectionHandler;
Lcom/google/android/startop/iorap/IorapForwardingService;
+HPLandroid/hardware/health/V1_0/HealthInfo;-><init>()V
+HPLandroid/hardware/health/V2_0/DiskStats;-><init>()V
+HPLcom/android/server/-$$Lambda$GnssManagerService$a17GVVAgEci0VYD4EMvKwuPLhdQ;->onUidImportance(II)V
+HPLcom/android/server/-$$Lambda$GnssManagerService$mZAgy7PA5q3tB1aq7tHsX4xM14E;->run()V
+HPLcom/android/server/-$$Lambda$LocationManagerService$GVLGDgL1Vk3AKo-zMjRmo3-OLpQ;->run()V
+HPLcom/android/server/-$$Lambda$LocationManagerService$tHPgS5c0niUhGntiX8gOnWrZpg8;->onUidImportance(II)V
+HPLcom/android/server/accessibility/AccessibilityManagerService$Client;-><init>(Lcom/android/server/accessibility/AccessibilityManagerService;Landroid/view/accessibility/IAccessibilityManagerClient;ILcom/android/server/accessibility/AccessibilityUserState;)V
+HPLcom/android/server/accessibility/AccessibilitySecurityPolicy;->resolveCallingUserIdEnforcingPermissionsLocked(I)I
+HPLcom/android/server/am/-$$Lambda$OomAdjuster$OVkqAAacT5-taN3pgDzyZj3Ymvk;->handleMessage(Landroid/os/Message;)Z
+HPLcom/android/server/am/-$$Lambda$ProcessList$vtq7LF5jIHO4t5NE03c8g7BT7Jc;->run()V
+HPLcom/android/server/am/ActivityManagerService$2;->onActivityLaunched([BI)V
+HPLcom/android/server/am/ActivityManagerService$4;->isPackageForFilter(Ljava/lang/String;Landroid/content/IntentFilter;)Z
+HPLcom/android/server/am/ActivityManagerService$4;->newResult(Landroid/content/IntentFilter;II)Ljava/lang/Object;
+HPLcom/android/server/am/ActivityManagerService$PidMap;->put(Lcom/android/server/am/ProcessRecord;)V
+HPLcom/android/server/am/ActivityManagerService;->trimApplications(Ljava/lang/String;)V
+HPLcom/android/server/am/ActivityManagerService;->updateOomAdjLocked(Lcom/android/server/am/ProcessRecord;Ljava/lang/String;)V
+HPLcom/android/server/am/ActivityManagerService;->updateOomAdjLocked(Lcom/android/server/am/ProcessRecord;ZLjava/lang/String;)Z
+HPLcom/android/server/am/HostingRecord;->getName()Ljava/lang/String;
+HPLcom/android/server/am/HostingRecord;->getType()Ljava/lang/String;
+HPLcom/android/server/am/OomAdjuster;->setAttachingSchedGroupLocked(Lcom/android/server/am/ProcessRecord;)V
+HPLcom/android/server/am/ProcessList;->startProcessLocked(Lcom/android/server/am/HostingRecord;Ljava/lang/String;Lcom/android/server/am/ProcessRecord;I[IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;J)Z
+HPLcom/android/server/am/ProcessRecord;->computeOomAdjFromActivitiesIfNecessary(Lcom/android/server/am/OomAdjuster$ComputeOomAdjWindowCallback;IZIIIII)V
+HPLcom/android/server/appop/AppOpsService$FeatureOp;->started(JII)V
+HPLcom/android/server/appop/AppOpsService$FeatureOp;->updateProxyState(JILjava/lang/String;Ljava/lang/String;)V
+HPLcom/android/server/appop/AppOpsService;->noteOperation(IILjava/lang/String;Ljava/lang/String;)I
+HPLcom/android/server/appop/AudioRestrictionManager;->checkAudioOperation(IIILjava/lang/String;)I
+HPLcom/android/server/appprediction/-$$Lambda$AppPredictionManagerService$PredictionManagerServiceStub$4yDhFef-19aMlJ-Y7O6RdjSAvnk;-><init>(Landroid/app/prediction/AppPredictionSessionId;Landroid/app/prediction/AppTargetEvent;)V
+HPLcom/android/server/appprediction/-$$Lambda$RemoteAppPredictionService$qroIh2ewx0BLP-J9XIAX2CaX8J4;-><init>(Landroid/app/prediction/AppPredictionSessionId;Landroid/app/prediction/AppTargetEvent;)V
+HPLcom/android/server/appprediction/AppPredictionManagerService$PredictionManagerServiceStub;->runForUserLocked(Ljava/lang/String;Ljava/util/function/Consumer;)V
+HPLcom/android/server/appprediction/RemoteAppPredictionService;->lambda$notifyAppTargetEvent$1(Landroid/app/prediction/AppPredictionSessionId;Landroid/app/prediction/AppTargetEvent;Landroid/service/appprediction/IPredictionService;)V
+HPLcom/android/server/audio/SoundEffectsHelper$SfxHandler;->handleMessage(Landroid/os/Message;)V
+HPLcom/android/server/autofill/AutofillManagerService$AugmentedAutofillState;->injectAugmentedAutofillInfo(Landroid/content/AutofillOptions;ILjava/lang/String;)V
+HPLcom/android/server/autofill/AutofillManagerService$LocalService;->injectDisableAppInfo(Landroid/content/AutofillOptions;ILjava/lang/String;)V
+HPLcom/android/server/compat/CompatConfig;->getDisabledChanges(Landroid/content/pm/ApplicationInfo;)[J
+HPLcom/android/server/compat/CompatConfig;->get()Lcom/android/server/compat/CompatConfig;
+HPLcom/android/server/compat/PlatformCompat;->resetReporting(Landroid/content/pm/ApplicationInfo;)V
+HPLcom/android/server/contentcapture/ContentCaptureManagerService$GlobalContentCaptureOptions;->getOptions(ILjava/lang/String;)Landroid/content/ContentCaptureOptions;
+HPLcom/android/server/input/InputManagerService;->onPointerDownOutsideFocus(Landroid/os/IBinder;)V
+HPLcom/android/server/input/InputManagerService;->registerInputChannel(Landroid/view/InputChannel;)V
+HPLcom/android/server/inputmethod/InputMethodManagerService;->getActivityViewToScreenMatrixLocked(II)Landroid/graphics/Matrix;
+HPLcom/android/server/media/projection/MediaProjectionManagerService$1;->onForegroundActivitiesChanged(IIZ)V
+HPLcom/android/server/net/NetworkPolicyManagerService;->updateRulesForPowerRestrictionsULInner(II)I
+HPLcom/android/server/pm/permission/PermissionManagerService;->checkSingleUidPermissionInternal(ILjava/lang/String;)Z
+HPLcom/android/server/policy/PermissionPolicyService$Internal;->checkStartActivity(Landroid/content/Intent;ILjava/lang/String;)Z
+HPLcom/android/server/soundtrigger/SoundTriggerHelper;->computeRecognitionRequestedLocked()Z
+HPLcom/android/server/soundtrigger/SoundTriggerHelper;->isRecognitionAllowed()Z
+HPLcom/android/server/statusbar/-$$Lambda$StatusBarManagerService$dQguzfF4tEgBOj3Pr8MpGRN8HT0;->run()V
+HPLcom/android/server/statusbar/-$$Lambda$StatusBarManagerService$u5u_W7qW5cMnzk9Qhp_oReST4Dc;->run()V
+HPLcom/android/server/statusbar/StatusBarManagerService;->setImeWindowStatus(ILandroid/os/IBinder;IIZZ)V
+HPLcom/android/server/usage/UsageStatsService;->reportEventOrAddToQueue(ILandroid/app/usage/UsageEvents$Event;)V
+HPLcom/android/server/usage/UserUsageStatsService;->checkAndGetTimeLocked()J
+HPLcom/android/server/wm/-$$Lambda$1Hjf_Nn5x4aIy9rIBTwVrtrzWFA;->apply(Ljava/lang/Object;)Ljava/lang/Object;
+HPLcom/android/server/wm/-$$Lambda$9vBfnQOmNnsc9WU80IIatZHQGKc;->get()Ljava/lang/Object;
+HPLcom/android/server/wm/-$$Lambda$ActivityRecord$BGON-BKR54yaxY8PHFXNV2xpxCM;->accept(Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$ActivityRecord$tt99EJHW_Nk5qgU9galJBIm5wXg;->run()V
+HPLcom/android/server/wm/-$$Lambda$ActivityRecord$YSVwd546vKWMiMYy7MFzg1qRiio;-><init>(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/-$$Lambda$ActivityRecord$YY5kCNb4uWg5W_2lbH3ZOqirP1g;->apply(Ljava/lang/Object;)Z
+HPLcom/android/server/wm/-$$Lambda$DisplayContent$a4EkCBfpZNIl1xfYgm2ktgndF8w;->apply(Ljava/lang/Object;)Z
+HPLcom/android/server/wm/-$$Lambda$DisplayContent$D0QJUvhaQkGgoMtOmjw5foY9F8M;->accept(Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$DisplayContent$eJsj3GR1HdCnOJrZ8_oaLP52jg0;->accept(Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$DisplayContent$SeHNTr4WUVpGmQniHULUi1ST7k8;->accept(Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$DisplayContent$sYPOy6TL-QiWuU_jcEHYn4HeFnQ;->accept(Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$DisplayPolicy$3MnyIKSHFLqhfUifWEQPNp_-J6A;->accept(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$DisplayPolicy$IOyP8YVRG92tn9u1muYWZgBbgc0;->accept(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$DisplayPolicy$J8sIwXJvltUaPM3jEGO948Bx9ig;->accept(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$DisplayPolicy$LkHee4mchNXMwNt7HLgsMzHofeE;->accept(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$DisplayPolicy$QDPgWUhyEOraWnf6a-u4mTBttdw;->accept(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$DisplayPolicy$qQY9m_Itua9TDy-Nk3zzDxvjEwE;->run()V
+HPLcom/android/server/wm/-$$Lambda$LaunchObserverRegistryImpl$QcawcFcJtEX4EhYptq_Vb4j368Y;->accept(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$LaunchObserverRegistryImpl$veRn_GhgLZLlOHOJ0ZYT6KcfYqo;->accept(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$qMFJUmfG50ZSjk7Tac67xBia0d4;->accept(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$RemoteAnimationController$74uuXaM2TqjkzYi0b8LqJdbycxA;-><init>(Lcom/android/server/wm/RemoteAnimationController;[Landroid/view/RemoteAnimationTarget;[Landroid/view/RemoteAnimationTarget;)V
+HPLcom/android/server/wm/-$$Lambda$RemoteAnimationController$74uuXaM2TqjkzYi0b8LqJdbycxA;->run()V
+HPLcom/android/server/wm/-$$Lambda$RootWindowContainer$7XcqfZjQLAbjpIyed3iDnVtZro4;->accept(Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$SurfaceAnimator$M9kRDTUpVS03LTqe-QLQz3DnMhk;-><init>(Lcom/android/server/wm/SurfaceAnimator;Lcom/android/server/wm/AnimationAdapter;Ljava/lang/Runnable;)V
+HPLcom/android/server/wm/-$$Lambda$SurfaceAnimator$M9kRDTUpVS03LTqe-QLQz3DnMhk;->run()V
+HPLcom/android/server/wm/-$$Lambda$TaskChangeNotificationController$Kz-Od_gLhLbMtGka4r78W0Gmzgo;->accept(Landroid/app/ITaskStackListener;Landroid/os/Message;)V
+HPLcom/android/server/wm/-$$Lambda$TaskPersister$xdLXwftXa6l84QTg1zpxMnmtQ0g;-><init>(Lcom/android/server/wm/TaskRecord;)V
+HPLcom/android/server/wm/-$$Lambda$uwO6wQlqU3CG7OTdH7NBCKnHs64;->accept(Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$UZl9uqUNteVgplGGEK6TMzf-7zk;->accept(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$VY87MmFWaCLMkNa2qHGaPrThyrI;->accept(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/ActivityDisplay;->alwaysCreateStack(II)Z
+HPLcom/android/server/wm/ActivityDisplay;->pauseBackStacks(ZLcom/android/server/wm/ActivityRecord;)Z
+HPLcom/android/server/wm/ActivityDisplay;->updateDisplayOverrideConfigurationLocked(Landroid/content/res/Configuration;Lcom/android/server/wm/ActivityRecord;ZLcom/android/server/wm/ActivityTaskManagerService$UpdateConfigurationResult;)Z
+HPLcom/android/server/wm/ActivityMetricsLogger;->reset(ZLcom/android/server/wm/ActivityMetricsLogger$WindowingModeTransitionInfo;Ljava/lang/String;J)V
+HPLcom/android/server/wm/ActivityRecord$AddStartingWindow;-><init>(Lcom/android/server/wm/ActivityRecord;Lcom/android/server/wm/ActivityRecord$1;)V
+HPLcom/android/server/wm/ActivityRecord$AddStartingWindow;->run()V
+HPLcom/android/server/wm/ActivityRecord$Token;-><init>(Landroid/content/Intent;)V
+HPLcom/android/server/wm/ActivityRecord;->addWindow(Lcom/android/server/wm/WindowState;)V
+HPLcom/android/server/wm/ActivityRecord;->adjustPinnedStackAndInitChangeTransitionIfNeeded(II)V
+HPLcom/android/server/wm/ActivityRecord;->asActivityRecord()Lcom/android/server/wm/ActivityRecord;
+HPLcom/android/server/wm/ActivityRecord;->checkAppWindowsReadyToShow()V
+HPLcom/android/server/wm/ActivityRecord;->checkCompleteDeferredRemoval()Z
+HPLcom/android/server/wm/ActivityRecord;->clearAnimatingFlags()V
+HPLcom/android/server/wm/ActivityRecord;->commitVisibility(Landroid/view/WindowManager$LayoutParams;ZIZZ)Z
+HPLcom/android/server/wm/ActivityRecord;->computeBounds(Landroid/graphics/Rect;Landroid/graphics/Rect;)V
+HPLcom/android/server/wm/ActivityRecord;->containsDismissKeyguardWindow()Z
+HPLcom/android/server/wm/ActivityRecord;->containsShowWhenLockedWindow()Z
+HPLcom/android/server/wm/ActivityRecord;->destroySurfaces(Z)V
+HPLcom/android/server/wm/ActivityRecord;->detachChildren()V
+HPLcom/android/server/wm/ActivityRecord;->fillsParent()Z
+HPLcom/android/server/wm/ActivityRecord;->findMainWindow()Lcom/android/server/wm/WindowState;
+HPLcom/android/server/wm/ActivityRecord;->findMainWindow(Z)Lcom/android/server/wm/WindowState;
+HPLcom/android/server/wm/ActivityRecord;->forAllWindows(Lcom/android/internal/util/ToBooleanFunction;Z)Z
+HPLcom/android/server/wm/ActivityRecord;->getAnimationLeashParent()Landroid/view/SurfaceControl;
+HPLcom/android/server/wm/ActivityRecord;->getDisplayedBounds()Landroid/graphics/Rect;
+HPLcom/android/server/wm/ActivityRecord;->getOrientation(I)I
+HPLcom/android/server/wm/ActivityRecord;->getStartingWindowType(ZZZZZZLandroid/app/ActivityManager$TaskSnapshot;)I
+HPLcom/android/server/wm/ActivityRecord;->getTask()Lcom/android/server/wm/Task;
+HPLcom/android/server/wm/ActivityRecord;->handleAlreadyVisible()V
+HPLcom/android/server/wm/ActivityRecord;->isAppAnimating()Z
+HPLcom/android/server/wm/ActivityRecord;->isClientHidden()Z
+HPLcom/android/server/wm/ActivityRecord;->isFirstChildWindowGreaterThanSecond(Lcom/android/server/wm/WindowState;Lcom/android/server/wm/WindowState;)Z
+HPLcom/android/server/wm/ActivityRecord;->isResolverOrDelegateActivity()Z
+HPLcom/android/server/wm/ActivityRecord;->isSelfAnimating()Z
+HPLcom/android/server/wm/ActivityRecord;->isVisible()Z
+HPLcom/android/server/wm/ActivityRecord;->isWaitingForTransitionStart()Z
+HPLcom/android/server/wm/ActivityRecord;->layoutLetterbox(Lcom/android/server/wm/WindowState;)V
+HPLcom/android/server/wm/ActivityRecord;->makeInvisible()V
+HPLcom/android/server/wm/ActivityRecord;->needsZBoost()Z
+HPLcom/android/server/wm/ActivityRecord;->onAnimationLeashCreated(Landroid/view/SurfaceControl$Transaction;Landroid/view/SurfaceControl;)V
+HPLcom/android/server/wm/ActivityRecord;->onAnimationLeashLost(Landroid/view/SurfaceControl$Transaction;)V
+HPLcom/android/server/wm/ActivityRecord;->onAppTransitionDone()V
+HPLcom/android/server/wm/ActivityRecord;->onDisplayChanged(Lcom/android/server/wm/DisplayContent;)V
+HPLcom/android/server/wm/ActivityRecord;->onParentChanged()V
+HPLcom/android/server/wm/ActivityRecord;->postWindowRemoveStartingWindowCleanup(Lcom/android/server/wm/WindowState;)V
+HPLcom/android/server/wm/ActivityRecord;->prepareSurfaces()V
+HPLcom/android/server/wm/ActivityRecord;->removeChild(Lcom/android/server/wm/WindowContainer;)V
+HPLcom/android/server/wm/ActivityRecord;->removeStartingWindow()V
+HPLcom/android/server/wm/ActivityRecord;->scheduleAddStartingWindow()V
+HPLcom/android/server/wm/ActivityRecord;->scheduleTopResumedActivityChanged(Z)Z
+HPLcom/android/server/wm/ActivityRecord;->setHidden(Z)V
+HPLcom/android/server/wm/ActivityRecord;->setLayer(Landroid/view/SurfaceControl$Transaction;I)V
+HPLcom/android/server/wm/ActivityRecord;->setVisibility(ZZ)V
+HPLcom/android/server/wm/ActivityRecord;->shouldDeferAnimationFinish(Ljava/lang/Runnable;)Z
+HPLcom/android/server/wm/ActivityRecord;->stopFreezingScreen(ZZ)V
+HPLcom/android/server/wm/ActivityRecord;->stopIfPossible()V
+HPLcom/android/server/wm/ActivityRecord;->transferStartingWindow(Landroid/os/IBinder;)Z
+HPLcom/android/server/wm/ActivityRecord;->updateAllDrawn()V
+HPLcom/android/server/wm/ActivityRecord;->updateDrawnWindowStates(Lcom/android/server/wm/WindowState;)Z
+HPLcom/android/server/wm/ActivityRecord;->updateLetterboxSurface(Lcom/android/server/wm/WindowState;)V
+HPLcom/android/server/wm/ActivityRecord;->updateReportedVisibilityLocked()V
+HPLcom/android/server/wm/ActivityRecord;->windowsAreFocusable()Z
+HPLcom/android/server/wm/ActivityRecord;->writeToProto(Landroid/util/proto/ProtoOutputStream;JI)V
+HPLcom/android/server/wm/ActivityStack;->removeLaunchTickMessages()V
+HPLcom/android/server/wm/ActivityStack;->removeStopTimeoutForActivity(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/ActivityStack;->scheduleLaunchTickForActivity(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/ActivityStack;->topTask()Lcom/android/server/wm/TaskRecord;
+HPLcom/android/server/wm/ActivityStarter;->startActivity(Lcom/android/server/wm/ActivityRecord;Lcom/android/server/wm/ActivityRecord;Landroid/service/voice/IVoiceInteractionSession;Lcom/android/internal/app/IVoiceInteractor;IZLandroid/app/ActivityOptions;Lcom/android/server/wm/TaskRecord;[Lcom/android/server/wm/ActivityRecord;Z)I
+HPLcom/android/server/wm/ActivityTaskManagerService;->addWindowLayoutReasons(I)V
+HPLcom/android/server/wm/ActivityTaskManagerService;->continueWindowLayout()V
+HPLcom/android/server/wm/ActivityTaskManagerService;->deferWindowLayout()V
+HPLcom/android/server/wm/ActivityTaskManagerService;->getPermissionPolicyInternal()Lcom/android/server/policy/PermissionPolicyInternal;
+HPLcom/android/server/wm/ActivityTaskManagerService;->isCrossUserAllowed(II)Z
+HPLcom/android/server/wm/ActivityTaskManagerService;->startProcessAsync(Lcom/android/server/wm/ActivityRecord;ZZLjava/lang/String;)V
+HPLcom/android/server/wm/AppTransitionController;->findAnimLayoutParamsToken(ILandroid/util/ArraySet;)Lcom/android/server/wm/ActivityRecord;
+HPLcom/android/server/wm/AppTransitionController;->getRemoteAnimationOverride(Lcom/android/server/wm/ActivityRecord;ILandroid/util/ArraySet;)Landroid/view/RemoteAnimationAdapter;
+HPLcom/android/server/wm/AppTransitionController;->getTopApp(Landroid/util/ArraySet;Z)Lcom/android/server/wm/ActivityRecord;
+HPLcom/android/server/wm/AppTransition;->getAnimationStyleResId(Landroid/view/WindowManager$LayoutParams;)I
+HPLcom/android/server/wm/AppTransition;->goodToGo(ILcom/android/server/wm/ActivityRecord;Landroid/util/ArraySet;)I
+HPLcom/android/server/wm/AppTransition;->setLastAppTransition(ILcom/android/server/wm/ActivityRecord;Lcom/android/server/wm/ActivityRecord;Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/ConfigurationContainer;->hasChild()Z
+HPLcom/android/server/wm/ConfigurationContainer;->setWindowingMode(I)V
+HPLcom/android/server/wm/DisplayContent;->computeImeTargetIfNeeded(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/DisplayContent;->getInsetsPolicy()Lcom/android/server/wm/InsetsPolicy;
+HPLcom/android/server/wm/DisplayContent;->getRotationAnimation()Lcom/android/server/wm/ScreenRotationAnimation;
+HPLcom/android/server/wm/DisplayContent;->performLayoutNoTrace(ZZ)V
+HPLcom/android/server/wm/DisplayContent;->setFocusedApp(Lcom/android/server/wm/ActivityRecord;)Z
+HPLcom/android/server/wm/DisplayContent;->updateDisplayAndOrientation(ILandroid/content/res/Configuration;)Landroid/view/DisplayInfo;
+HPLcom/android/server/wm/DisplayContent;->updateFocusedWindowLocked(IZI)Z
+HPLcom/android/server/wm/DisplayContent;->updateOrientation(Landroid/content/res/Configuration;Landroid/os/IBinder;Z)Landroid/content/res/Configuration;
+HPLcom/android/server/wm/DisplayContent;->updateOrientation()Z
+HPLcom/android/server/wm/DisplayPolicy;->addWindowLw(Lcom/android/server/wm/WindowState;Landroid/view/WindowManager$LayoutParams;)V
+HPLcom/android/server/wm/DisplayPolicy;->adjustWindowParamsLw(Lcom/android/server/wm/WindowState;Landroid/view/WindowManager$LayoutParams;II)V
+HPLcom/android/server/wm/DisplayPolicy;->configureNavBarOpacity(IZZZZZ)I
+HPLcom/android/server/wm/DisplayPolicy;->drawsBarBackground(ILcom/android/server/wm/WindowState;Lcom/android/server/wm/BarController;I)Z
+HPLcom/android/server/wm/DisplayPolicy;->drawsNavigationBarBackground(ILcom/android/server/wm/WindowState;)Z
+HPLcom/android/server/wm/DisplayPolicy;->getRefreshRatePolicy()Lcom/android/server/wm/RefreshRatePolicy;
+HPLcom/android/server/wm/DisplayPolicy;->isWindowExcludedFromContent(Lcom/android/server/wm/WindowState;)Z
+HPLcom/android/server/wm/DisplayPolicy;->selectAnimation(Lcom/android/server/wm/WindowState;I)I
+HPLcom/android/server/wm/DisplayPolicy;->updateConfigurationAndScreenSizeDependentBehaviors()V
+HPLcom/android/server/wm/DisplayRotation;->getRotation()I
+HPLcom/android/server/wm/DisplayRotation;->markForSeamlessRotation(Lcom/android/server/wm/WindowState;Z)V
+HPLcom/android/server/wm/ImeInsetsSourceProvider;->onPostInsetsDispatched()V
+HPLcom/android/server/wm/ImeInsetsSourceProvider;->onPostLayout()V
+HPLcom/android/server/wm/InputManagerCallback;->notifyFocusChanged(Landroid/os/IBinder;Landroid/os/IBinder;)Z
+HPLcom/android/server/wm/InputMonitor$UpdateInputWindows;->run()V
+HPLcom/android/server/wm/InsetsPolicy;->areSystemBarsForciblyVisible()Z
+HPLcom/android/server/wm/InsetsPolicy;->isNavBarForciblyVisible()Z
+HPLcom/android/server/wm/InsetsPolicy;->isStatusBarForciblyVisible()Z
+HPLcom/android/server/wm/InsetsStateController;->getImeSourceProvider()Lcom/android/server/wm/ImeInsetsSourceProvider;
+HPLcom/android/server/wm/InsetsStateController;->onBarControlTargetChanged(Lcom/android/server/wm/InsetsControlTarget;Lcom/android/server/wm/InsetsControlTarget;)V
+HPLcom/android/server/wm/LaunchObserverRegistryImpl;->onIntentStarted(Landroid/content/Intent;J)V
+HPLcom/android/server/wm/PinnedStackController;->resetReentrySnapFraction(Landroid/content/ComponentName;)V
+HPLcom/android/server/wm/RecentTasks;->isInVisibleRange(Lcom/android/server/wm/TaskRecord;IIZ)Z
+HPLcom/android/server/wm/RefreshRatePolicy;->getPreferredModeId(Lcom/android/server/wm/WindowState;)I
+HPLcom/android/server/wm/RemoteAnimationController$FinishedCallback;-><init>(Lcom/android/server/wm/RemoteAnimationController;)V
+HPLcom/android/server/wm/RemoteAnimationController$RemoteAnimationRecord;->getMode()I
+HPLcom/android/server/wm/RemoteAnimationController;->createAppAnimations()[Landroid/view/RemoteAnimationTarget;
+HPLcom/android/server/wm/RemoteAnimationController;->createWallpaperAnimations()[Landroid/view/RemoteAnimationTarget;
+HPLcom/android/server/wm/RemoteAnimationController;-><init>(Lcom/android/server/wm/WindowManagerService;Landroid/view/RemoteAnimationAdapter;Landroid/os/Handler;)V
+HPLcom/android/server/wm/RemoteAnimationController;->releaseFinishedCallback()V
+HPLcom/android/server/wm/RemoteAnimationController;->setRunningRemoteAnimation(Z)V
+HPLcom/android/server/wm/RemoteAnimationController;->unlinkToDeathOfRunner()V
+HPLcom/android/server/wm/RootActivityContainer;->getLaunchStack(Lcom/android/server/wm/ActivityRecord;Landroid/app/ActivityOptions;Lcom/android/server/wm/TaskRecord;ZLcom/android/server/wm/LaunchParamsController$LaunchParams;II)Lcom/android/server/wm/ActivityStack;
+HPLcom/android/server/wm/RootActivityContainer;->getRunningTasks(ILjava/util/List;IIIZZLandroid/util/ArraySet;)V
+HPLcom/android/server/wm/RootWindowContainer;->getActivityRecord(Landroid/os/IBinder;)Lcom/android/server/wm/ActivityRecord;
+HPLcom/android/server/wm/Session;->finishDrawing(Landroid/view/IWindow;Landroid/view/SurfaceControl$Transaction;)V
+HPLcom/android/server/wm/Session;->reportSystemGestureExclusionChanged(Landroid/view/IWindow;Ljava/util/List;)V
+HPLcom/android/server/wm/StartingData;-><init>(Lcom/android/server/wm/WindowManagerService;)V
+HPLcom/android/server/wm/SurfaceAnimationRunner;->startPendingAnimationsLocked()V
+HPLcom/android/server/wm/SurfaceAnimator$Animatable;->shouldDeferAnimationFinish(Ljava/lang/Runnable;)Z
+HPLcom/android/server/wm/SurfaceAnimator;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
+HPLcom/android/server/wm/Task;->addChild(Lcom/android/server/wm/ActivityRecord;I)V
+HPLcom/android/server/wm/TaskChangeNotificationController;->notifyTaskListUpdated()V
+HPLcom/android/server/wm/Task;->getTopVisibleActivity()Lcom/android/server/wm/ActivityRecord;
+HPLcom/android/server/wm/TaskLaunchParamsModifier;->canInheritWindowingModeFromSource(Lcom/android/server/wm/ActivityDisplay;Lcom/android/server/wm/ActivityRecord;)Z
+HPLcom/android/server/wm/Task;->onConfigurationChanged(Landroid/content/res/Configuration;Z)V
+HPLcom/android/server/wm/TaskPersister$TaskWriteQueueItem;-><init>(Lcom/android/server/wm/TaskRecord;Lcom/android/server/wm/ActivityTaskManagerService;)V
+HPLcom/android/server/wm/Task;->positionChildAt(Lcom/android/server/wm/ActivityRecord;I)V
+HPLcom/android/server/wm/TaskRecord;->computeFullscreenBounds(Landroid/graphics/Rect;Lcom/android/server/wm/ActivityRecord;Landroid/graphics/Rect;I)V
+HPLcom/android/server/wm/TaskRecord;->findRootIndex(Z)I
+HPLcom/android/server/wm/TaskRecord;-><init>(Lcom/android/server/wm/ActivityTaskManagerService;ILandroid/content/pm/ActivityInfo;Landroid/content/Intent;Landroid/service/voice/IVoiceInteractionSession;Lcom/android/internal/app/IVoiceInteractor;Landroid/app/ActivityManager$TaskDescription;)V
+HPLcom/android/server/wm/UnknownAppVisibilityController;->notifyAppResumedFinished(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/UnknownAppVisibilityController;->notifyRelayouted(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/WallpaperAnimationAdapter;->getLeashFinishedCallback()Lcom/android/server/wm/SurfaceAnimator$OnAnimationFinishedCallback;
+HPLcom/android/server/wm/WallpaperAnimationAdapter;->lambda$startWallpaperAnimations$0(JJLjava/util/function/Consumer;Ljava/util/ArrayList;Ljava/util/ArrayList;Lcom/android/server/wm/WallpaperWindowToken;)V
+HPLcom/android/server/wm/WallpaperAnimationAdapter;->startAnimation(Landroid/view/SurfaceControl;Landroid/view/SurfaceControl$Transaction;Lcom/android/server/wm/SurfaceAnimator$OnAnimationFinishedCallback;)V
+HPLcom/android/server/wm/WallpaperAnimationAdapter;->startWallpaperAnimations(Lcom/android/server/wm/WindowManagerService;JJLjava/util/function/Consumer;Ljava/util/ArrayList;)[Landroid/view/RemoteAnimationTarget;
+HPLcom/android/server/wm/WindowAnimator;->cancelAnimation()V
+HPLcom/android/server/wm/WindowContainer;->onAnimationLeashLost(Landroid/view/SurfaceControl$Transaction;)V
+HPLcom/android/server/wm/WindowContainer;->onParentChanged(Lcom/android/server/wm/WindowContainer$PreAssignChildLayersCallback;)V
+HPLcom/android/server/wm/WindowManagerService$LocalService;->isStackVisibleLw(I)Z
+HPLcom/android/server/wm/WindowManagerService;->prepareNoneTransitionForRelaunching(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/WindowManagerService;->prepareWindowReplacementTransition(Lcom/android/server/wm/ActivityRecord;)Z
+HPLcom/android/server/wm/WindowProcessController;->createProfilerInfoIfNeeded()Landroid/app/ProfilerInfo;
+HPLcom/android/server/wm/WindowProcessControllerMap;->put(ILcom/android/server/wm/WindowProcessController;)V
+HPLcom/android/server/wm/WindowProcessController;->onStartActivity(ILandroid/content/pm/ActivityInfo;)V
+HPLcom/android/server/wm/WindowProcessController;->setLastActivityLaunchTime(J)V
+HPLcom/android/server/wm/WindowState$UpdateReportedVisibilityResults;-><init>()V
+HPLcom/android/server/wm/WindowState;->getLastReportedConfiguration()Landroid/content/res/Configuration;
+HPLcom/android/server/wm/WindowState;->getSurfaceTouchableRegion(Landroid/view/InputWindowHandle;I)I
+HPLcom/android/server/wm/WindowState;->logExclusionRestrictions(I)V
+HPLcom/android/server/wm/WindowState;->onAnimationLeashLost(Landroid/view/SurfaceControl$Transaction;)V
+HPLcom/android/server/wm/WindowState;->relayoutVisibleWindow(II)I
+HPLcom/android/server/wm/WindowState;->updateLocationInParentDisplayIfNeeded()V
+HPLcom/android/server/wm/WindowSurfaceController;-><init>(Ljava/lang/String;IIIILcom/android/server/wm/WindowStateAnimator;II)V
+HPLcom/android/server/wm/WindowSurfacePlacer$Traverser;->run()V
+HPLcom/android/server/wm/WindowToken;-><init>(Lcom/android/server/wm/WindowManagerService;Landroid/os/IBinder;IZLcom/android/server/wm/DisplayContent;Z)V
+HPLcom/google/android/startop/iorap/IorapForwardingService$AppLaunchObserver;->onActivityLaunched([BI)V
+HPLcom/android/server/accessibility/AccessibilityManagerService;->computeRelevantEventTypesLocked(Lcom/android/server/accessibility/AccessibilityUserState;Lcom/android/server/accessibility/AccessibilityManagerService$Client;)I
+HPLcom/android/server/accessibility/AccessibilitySecurityPolicy;->resolveProfileParentLocked(I)I
+HPLcom/android/server/am/-$$Lambda$ProcessList$vtq7LF5jIHO4t5NE03c8g7BT7Jc;-><init>(Lcom/android/server/am/ProcessList;Lcom/android/server/am/ProcessRecord;Ljava/lang/String;[IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;J)V
+HPLcom/android/server/am/ActivityManagerService$2;->onActivityLaunchFinished([BJ)V
+HPLcom/android/server/am/ActivityManagerService$2;->onIntentStarted(Landroid/content/Intent;J)V
+HPLcom/android/server/am/ActivityManagerService$4;->isPackageForFilter(Ljava/lang/String;Lcom/android/server/am/BroadcastFilter;)Z
+HPLcom/android/server/am/ActivityManagerService$4;->newResult(Lcom/android/server/am/BroadcastFilter;II)Lcom/android/server/am/BroadcastFilter;
+HPLcom/android/server/am/ActivityManagerService$LocalService;->startProcess(Ljava/lang/String;Landroid/content/pm/ApplicationInfo;ZZLjava/lang/String;Landroid/content/ComponentName;)V
+HPLcom/android/server/am/ActivityManagerService;->trimApplicationsLocked(Ljava/lang/String;)V
+HPLcom/android/server/am/ProcessList;->lambda$startProcessLocked$0$ProcessList(Lcom/android/server/am/ProcessRecord;Ljava/lang/String;[IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;J)V
+HPLcom/android/server/am/ProcessRecord;->onStartActivity(IZLjava/lang/String;J)V
+HPLcom/android/server/am/ProcessRecord;->setRunningRemoteAnimation(Z)V
+HPLcom/android/server/am/ProcessRecord;->setStartParams(ILcom/android/server/am/HostingRecord;Ljava/lang/String;J)V
+HPLcom/android/server/appprediction/RemoteAppPredictionService;->lambda$notifyAppTargetEvent$1(Landroid/app/prediction/AppPredictionSessionId;Landroid/app/prediction/AppTargetEvent;Landroid/service/appprediction/IPredictionService;)V
+HPLcom/android/server/audio/SoundEffectsHelper$SfxHandler$1;-><init>(Lcom/android/server/audio/SoundEffectsHelper$SfxHandler;Landroid/os/Message;)V
+HPLcom/android/server/audio/SoundEffectsHelper;->onLoadSoundEffects(Lcom/android/server/audio/SoundEffectsHelper$OnEffectsLoadCompleteHandler;)V
+HPLcom/android/server/autofill/AutofillManagerServiceImpl;->getAppDisabledActivitiesLocked(Ljava/lang/String;)Landroid/util/ArrayMap;
+HPLcom/android/server/compat/CompatChange;->isEnabled(Landroid/content/pm/ApplicationInfo;)Z
+HPLcom/android/server/GnssManagerService;->onForegroundChanged(IZ)V
+HPLcom/android/server/LocationManagerService;->lambda$initializeLocked$4$LocationManagerService(II)V
+HPLcom/android/server/LocationManagerService;->lambda$initializeLocked$5$LocationManagerService(II)V
+HPLcom/android/server/policy/PermissionPolicyService$Internal;->isActionRemovedForCallingPackage(Landroid/content/Intent;ILjava/lang/String;)Z
+HPLcom/android/server/statusbar/-$$Lambda$StatusBarManagerService$dQguzfF4tEgBOj3Pr8MpGRN8HT0;-><init>(Lcom/android/server/statusbar/StatusBarManagerService;ILandroid/os/IBinder;IIZZ)V
+HPLcom/android/server/statusbar/StatusBarManagerService;->lambda$setImeWindowStatus$2$StatusBarManagerService(ILandroid/os/IBinder;IIZZ)V
+HPLcom/android/server/statusbar/StatusBarManagerService;->lambda$updateUiVisibilityLocked$3$StatusBarManagerService(IIIIILandroid/graphics/Rect;Landroid/graphics/Rect;Z)V
+HPLcom/android/server/wm/-$$Lambda$ActivityRecord$tt99EJHW_Nk5qgU9galJBIm5wXg;-><init>(Lcom/android/server/policy/WindowManagerPolicy$StartingSurface;)V
+HPLcom/android/server/wm/-$$Lambda$RemoteAnimationController$dP8qDptNigoqhzVtIudsX5naGu4;-><init>(Lcom/android/server/wm/RemoteAnimationController;)V
+HPLcom/android/server/wm/-$$Lambda$RemoteAnimationController$uQS8vaPKQ-E3x_9G8NCxPQmw1fw;-><init>(Lcom/android/server/wm/RemoteAnimationController;)V
+HPLcom/android/server/wm/ActivityRecord;->addToStopping(ZZLjava/lang/String;)V
+HPLcom/android/server/wm/ActivityRecord;->allDrawnStatesConsidered()Z
+HPLcom/android/server/wm/ActivityRecord;->applyAnimationLocked(Landroid/view/WindowManager$LayoutParams;IZZ)Z
+HPLcom/android/server/wm/ActivityRecord;->checkKeyguardFlagsChanged()V
+HPLcom/android/server/wm/ActivityRecord;->lambda$removeStartingWindow$3(Lcom/android/server/policy/WindowManagerPolicy$StartingSurface;)V
+HPLcom/android/server/wm/ActivityRecord;->shouldStartChangeTransition(II)Z
+HPLcom/android/server/wm/ActivityRecord;->transferStartingWindowFromHiddenAboveTokenIfNeeded()V
+HPLcom/android/server/wm/ActivityStack;->scheduleStopTimeoutForActivity(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/ActivityStack;->startPausingLocked(ZZLcom/android/server/wm/ActivityRecord;)Z
+HPLcom/android/server/wm/ActivityStackSupervisor;->getRunningTasks()Lcom/android/server/wm/RunningTasks;
+HPLcom/android/server/wm/ActivityStackSupervisor;->getSystemChooserActivity()Landroid/content/ComponentName;
+HPLcom/android/server/wm/ActivityStarter;->handleStartResult(Lcom/android/server/wm/ActivityRecord;I)Lcom/android/server/wm/ActivityStack;
+HPLcom/android/server/wm/ActivityStarter;->startActivityInner(Lcom/android/server/wm/ActivityRecord;Lcom/android/server/wm/ActivityRecord;Landroid/service/voice/IVoiceInteractionSession;Lcom/android/internal/app/IVoiceInteractor;IZLandroid/app/ActivityOptions;Lcom/android/server/wm/TaskRecord;[Lcom/android/server/wm/ActivityRecord;Z)I
+HPLcom/android/server/wm/ActivityTaskManagerService;->ensureConfigAndVisibilityAfterUpdate(Lcom/android/server/wm/ActivityRecord;I)Z
+HPLcom/android/server/wm/AnimatingActivityRegistry;->endDeferringFinished()V
+HPLcom/android/server/wm/AnimationAdapter;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
+HPLcom/android/server/wm/AppTransitionController;->lookForHighestTokenWithFilter(Landroid/util/ArraySet;Landroid/util/ArraySet;Landroid/util/ArraySet;Ljava/util/function/Predicate;)Lcom/android/server/wm/ActivityRecord;
+HPLcom/android/server/wm/DisplayContent;->findFocusedWindowIfNeeded(I)Lcom/android/server/wm/WindowState;
+HPLcom/android/server/wm/DisplayContent;->getActivityRecord(Landroid/os/IBinder;)Lcom/android/server/wm/ActivityRecord;
+HPLcom/android/server/wm/DisplayContent;->getParentWindow()Lcom/android/server/wm/WindowState;
+HPLcom/android/server/wm/DisplayContent;->lambda$new$1$DisplayContent(Lcom/android/server/wm/WindowState;)V
+HPLcom/android/server/wm/DisplayContent;->lambda$new$2$DisplayContent(Lcom/android/server/wm/WindowState;)Z
+HPLcom/android/server/wm/DisplayContent;->lambda$new$3$DisplayContent(Lcom/android/server/wm/WindowState;)V
+HPLcom/android/server/wm/DisplayContent;->lambda$updateSystemUiVisibility$20(IILcom/android/server/wm/WindowState;)V
+HPLcom/android/server/wm/DisplayContent;->logsGestureExclusionRestrictions(Lcom/android/server/wm/WindowState;)Z
+HPLcom/android/server/wm/DisplayPolicy;->getCurrentUserResources()Landroid/content/res/Resources;
+HPLcom/android/server/wm/DisplayPolicy;->getStatusBar()Lcom/android/server/wm/WindowState;
+HPLcom/android/server/wm/DisplayPolicy;->lambda$addWindowLw$3$DisplayPolicy(Lcom/android/server/wm/DisplayFrames;Lcom/android/server/wm/WindowState;Landroid/graphics/Rect;)V
+HPLcom/android/server/wm/DisplayPolicy;->lambda$addWindowLw$4$DisplayPolicy(Lcom/android/server/wm/DisplayFrames;Lcom/android/server/wm/WindowState;Landroid/graphics/Rect;)V
+HPLcom/android/server/wm/DisplayPolicy;->lambda$addWindowLw$5$DisplayPolicy(Lcom/android/server/wm/DisplayFrames;Lcom/android/server/wm/WindowState;Landroid/graphics/Rect;)V
+HPLcom/android/server/wm/DisplayPolicy;->lambda$addWindowLw$6$DisplayPolicy(Lcom/android/server/wm/DisplayFrames;Lcom/android/server/wm/WindowState;Landroid/graphics/Rect;)V
+HPLcom/android/server/wm/DisplayPolicy;->lambda$addWindowLw$7$DisplayPolicy(Lcom/android/server/wm/DisplayFrames;Lcom/android/server/wm/WindowState;Landroid/graphics/Rect;)V
+HPLcom/android/server/wm/DisplayPolicy;->lambda$updateSystemUiVisibilityLw$10$DisplayPolicy(IIILandroid/graphics/Rect;Landroid/graphics/Rect;ZLcom/android/server/wm/WindowState;Z)V
+HPLcom/android/server/wm/DisplayRotation;->updateOrientation(IZ)Z
+HPLcom/android/server/wm/DisplayRotation;->updateUserDependentConfiguration(Landroid/content/res/Resources;)V
+HPLcom/android/server/wm/HighRefreshRateBlacklist;->isBlacklisted(Ljava/lang/String;)Z
+HPLcom/android/server/wm/InputManagerCallback;->dispatchPointerCaptureChanged(Landroid/view/IWindow;Z)Z
+HPLcom/android/server/wm/InputManagerCallback;->onPointerDownOutsideFocus(Landroid/os/IBinder;)V
+HPLcom/android/server/wm/InputMonitor;->layoutInputConsumers(II)V
+HPLcom/android/server/wm/InputMonitor;->setFocusedAppLw(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/InsetsStateController;->onControlChanged(ILcom/android/server/wm/InsetsControlTarget;)V
+HPLcom/android/server/wm/LaunchObserverRegistryImpl;->onActivityLaunchFinished([BJ)V
+HPLcom/android/server/wm/RemoteAnimationController$FinishedCallback;->release()V
+HPLcom/android/server/wm/RemoteAnimationController;->lambda$goodToGo$1$RemoteAnimationController([Landroid/view/RemoteAnimationTarget;[Landroid/view/RemoteAnimationTarget;)V
+HPLcom/android/server/wm/RunningTasks;->getTasks(ILjava/util/List;IILjava/util/ArrayList;IZZLandroid/util/ArraySet;)V
+HPLcom/android/server/wm/SplashScreenStartingData;->createStartingSurface(Lcom/android/server/wm/ActivityRecord;)Lcom/android/server/policy/WindowManagerPolicy$StartingSurface;
+HPLcom/android/server/wm/SurfaceAnimator;->lambda$getFinishedCallback$0$SurfaceAnimator(Lcom/android/server/wm/AnimationAdapter;Ljava/lang/Runnable;)V
+HPLcom/android/server/wm/Task;->positionChildAt(ILcom/android/server/wm/ActivityRecord;Z)V
+HPLcom/android/server/wm/TaskRecord;->handlesOrientationChangeFromDescendant()Z
+HPLcom/android/server/wm/TaskRecord;-><init>(Lcom/android/server/wm/ActivityTaskManagerService;ILandroid/content/Intent;Landroid/content/Intent;Ljava/lang/String;Ljava/lang/String;Landroid/content/ComponentName;Landroid/content/ComponentName;ZZZIILjava/lang/String;Ljava/util/ArrayList;JZLandroid/app/ActivityManager$TaskDescription;IIIIILjava/lang/String;IZZZIILandroid/content/pm/ActivityInfo;Landroid/service/voice/IVoiceInteractionSession;Lcom/android/internal/app/IVoiceInteractor;)V
+HPLcom/android/server/wm/TaskStack;->getAnimatingActivityRegistry()Lcom/android/server/wm/AnimatingActivityRegistry;
+HPLcom/android/server/wm/WallpaperAnimationAdapter;->startWallpaperAnimations(Lcom/android/server/wm/WindowManagerService;JJLjava/util/function/Consumer;Ljava/util/ArrayList;)[Landroid/view/RemoteAnimationTarget;
+HPLcom/android/server/wm/WindowContainer$ForAllWindowsConsumerWrapper;-><init>(Lcom/android/server/wm/WindowContainer;Lcom/android/server/wm/WindowContainer$1;)V
+HPLcom/android/server/wm/WindowManagerService;->finishDrawingWindow(Lcom/android/server/wm/Session;Landroid/view/IWindow;Landroid/view/SurfaceControl$Transaction;)V
+HPLcom/android/server/wm/WindowManagerService;->reportSystemGestureExclusionChanged(Lcom/android/server/wm/Session;Landroid/view/IWindow;Ljava/util/List;)V
+HPLcom/android/server/wm/WindowProcessController;->setRunningRemoteAnimation(Z)V
+HPLcom/android/server/wm/WindowProcessController;->shouldSetProfileProc()Z
+HPLcom/android/server/wm/WindowState;->onMergedOverrideConfigurationChanged()V
+HPLcom/android/server/wm/WindowSurfacePlacer;->continueLayout(Z)V
+HPLcom/android/server/wm/WindowSurfacePlacer;->isLayoutDeferred()Z
+HPLcom/google/android/startop/iorap/IorapForwardingService$AppLaunchObserver;->onActivityLaunchFinished([BJ)V
+HPLcom/google/android/startop/iorap/IorapForwardingService$AppLaunchObserver;->onIntentStarted(Landroid/content/Intent;J)V
+HPLcom/google/android/startop/iorap/IorapForwardingService;->invokeRemote(Lcom/google/android/startop/iorap/IIorap;Lcom/google/android/startop/iorap/IorapForwardingService$RemoteRunnable;)Z
+HPLcom/android/server/am/HostingRecord;-><init>(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;IZ)V
+HPLcom/android/server/am/ProcessList;->startProcessLocked(Ljava/lang/String;Landroid/content/pm/ApplicationInfo;ZILcom/android/server/am/HostingRecord;ZZIZLjava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/Runnable;)Lcom/android/server/am/ProcessRecord;
+HPLcom/android/server/am/ProcessRecord;->updateProcessInfo(ZZZ)V
+HPLcom/android/server/appprediction/RemoteAppPredictionService;->lambda$notifyAppTargetEvent$1(Landroid/app/prediction/AppPredictionSessionId;Landroid/app/prediction/AppTargetEvent;Landroid/service/appprediction/IPredictionService;)V
+HPLcom/android/server/audio/SoundEffectsHelper$SfxHandler$1;->run(Z)V
+HPLcom/android/server/input/InputManagerService$InputDevicesChangedListenerRecord;-><init>(Lcom/android/server/input/InputManagerService;ILandroid/hardware/input/IInputDevicesChangedListener;)V
+HPLcom/android/server/pm/PackageManagerServiceUtils;->enforceShellRestriction(Landroid/os/UserManagerInternal;Ljava/lang/String;II)V
+HPLcom/android/server/statusbar/StatusBarManagerService$1;->setSystemUiVisibility(IIIIILandroid/graphics/Rect;Landroid/graphics/Rect;ZLjava/lang/String;)V
+HPLcom/android/server/wm/-$$Lambda$AppTransitionController$o_nkoN7a-ZHaSAgJCQZcboKz9Ig;->test(Ljava/lang/Object;)Z
+HPLcom/android/server/wm/-$$Lambda$AppTransitionController$z5kCoexPNTWFncmRBfeXr6HA2JA;->test(Ljava/lang/Object;)Z
+HPLcom/android/server/wm/-$$Lambda$TaskPersister$8TcnoL7JFvpj8NzBRg91ns5JOBw;-><init>(Lcom/android/server/wm/TaskRecord;)V
+HPLcom/android/server/wm/ActivityRecord;->getAnimationBounds(I)Landroid/graphics/Rect;
+HPLcom/android/server/wm/ActivityRecord;->logStartActivity(ILcom/android/server/wm/TaskRecord;)V
+HPLcom/android/server/wm/ActivityRecord;->shouldAnimate(I)Z
+HPLcom/android/server/wm/ActivityStack;->getRunningTasks(Ljava/util/List;IIIZZLandroid/util/ArraySet;)V
+HPLcom/android/server/wm/ActivityStarter;->computeTargetTask(Lcom/android/server/wm/ActivityRecord;)Lcom/android/server/wm/TaskRecord;
+HPLcom/android/server/wm/ActivityStarter;->deliverToCurrentTopIfNeeded(Lcom/android/server/wm/ActivityStack;)I
+HPLcom/android/server/wm/ActivityStarter;->isAllowedToStart(Lcom/android/server/wm/ActivityRecord;ZLcom/android/server/wm/TaskRecord;)I
+HPLcom/android/server/wm/ActivityStarter;->setInitialState(Lcom/android/server/wm/ActivityRecord;Landroid/app/ActivityOptions;Lcom/android/server/wm/TaskRecord;ZILcom/android/server/wm/ActivityRecord;Landroid/service/voice/IVoiceInteractionSession;Lcom/android/internal/app/IVoiceInteractor;Z)V
+HPLcom/android/server/wm/ActivityStarter;->setNewTask(Lcom/android/server/wm/TaskRecord;)V
+HPLcom/android/server/wm/AnimationAdapter;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
+HPLcom/android/server/wm/DisplayRotation;->updateRotationUnchecked(Z)Z
+HPLcom/android/server/wm/RemoteAnimationController;->createRemoteAnimationRecord(Lcom/android/server/wm/ActivityRecord;Landroid/graphics/Point;Landroid/graphics/Rect;Landroid/graphics/Rect;)Lcom/android/server/wm/RemoteAnimationController$RemoteAnimationRecord;
+HPLcom/android/server/wm/RemoteAnimationController;->linkToDeathOfRunner()V
+HPLcom/android/server/wm/RemoteAnimationController;->writeStartDebugStatement()V
+HPLcom/android/server/wm/TaskRecord;->setMinDimensions(Landroid/content/pm/ActivityInfo;)V
+HPLcom/android/server/wm/WallpaperAnimationAdapter;->startWallpaperAnimations(Lcom/android/server/wm/WindowManagerService;JJLjava/util/function/Consumer;Ljava/util/ArrayList;)[Landroid/view/RemoteAnimationTarget;
+HPLcom/android/server/wm/WindowStateAnimator;->finishDrawingLocked(Landroid/view/SurfaceControl$Transaction;)Z
+HPLcom/google/android/startop/iorap/-$$Lambda$IorapForwardingService$AppLaunchObserver$6ikxM-3KospNGDidAY7yA-rECHw;->run(Lcom/google/android/startop/iorap/IIorap;)V
+HPLcom/google/android/startop/iorap/-$$Lambda$IorapForwardingService$AppLaunchObserver$B9wq4q5y7qahY6TuLMO_s8nPIwY;->run(Lcom/google/android/startop/iorap/IIorap;)V
+HPLcom/google/android/startop/iorap/-$$Lambda$IorapForwardingService$AppLaunchObserver$J1AHa-Qs75WQr3stjbN97THbudE;->run(Lcom/google/android/startop/iorap/IIorap;)V
+HPLcom/android/server/am/ProcessList;->newProcessRecordLocked(Landroid/content/pm/ApplicationInfo;Ljava/lang/String;ZILcom/android/server/am/HostingRecord;)Lcom/android/server/am/ProcessRecord;
+HPLcom/android/server/appprediction/RemoteAppPredictionService;->lambda$notifyAppTargetEvent$1(Landroid/app/prediction/AppPredictionSessionId;Landroid/app/prediction/AppTargetEvent;Landroid/service/appprediction/IPredictionService;)V
+HPLcom/android/server/audio/SoundEffectsHelper;->onPlaySoundEffect(II)V
+HPLcom/android/server/LocationManagerService;->isThrottlingExemptLocked(Lcom/android/server/location/CallerIdentity;)Z
+HPLcom/android/server/statusbar/StatusBarManagerService;->setSystemUiVisibility(IIIIILandroid/graphics/Rect;Landroid/graphics/Rect;ZLjava/lang/String;)V
+HPLcom/android/server/wm/AnimationAdapter;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
+HPLcom/android/server/wm/RemoteAnimationController$RemoteAnimationAdapterWrapper;->dump(Ljava/io/PrintWriter;Ljava/lang/String;)V
+HPLcom/android/server/wm/RemoteAnimationController$RemoteAnimationRecord;-><init>(Lcom/android/server/wm/RemoteAnimationController;Lcom/android/server/wm/ActivityRecord;Landroid/graphics/Point;Landroid/graphics/Rect;Landroid/graphics/Rect;)V
+HPLcom/android/server/wm/WindowProcessController;->setBoundClientUids(Landroid/util/ArraySet;)V
+HPLcom/google/android/startop/iorap/IorapForwardingService$AppLaunchObserver;->lambda$onActivityLaunched$2$IorapForwardingService$AppLaunchObserver([BILcom/google/android/startop/iorap/IIorap;)V
+HPLcom/google/android/startop/iorap/IorapForwardingService$AppLaunchObserver;->lambda$onActivityLaunchFinished$4$IorapForwardingService$AppLaunchObserver([BJLcom/google/android/startop/iorap/IIorap;)V
+HPLcom/google/android/startop/iorap/IorapForwardingService$AppLaunchObserver;->lambda$onIntentStarted$0$IorapForwardingService$AppLaunchObserver(Landroid/content/Intent;JLcom/google/android/startop/iorap/IIorap;)V
+HPLcom/android/server/statusbar/StatusBarManagerService;->updateUiVisibilityLocked(IIIIILandroid/graphics/Rect;Landroid/graphics/Rect;Z)V
+HPLcom/android/server/wm/AnimationAdapter;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
+HPLcom/google/android/startop/iorap/IIorap$Stub$Proxy;->onAppLaunchEvent(Lcom/google/android/startop/iorap/RequestId;Lcom/google/android/startop/iorap/AppLaunchEvent;)V
+HPLcom/google/android/startop/iorap/RequestId;->nextValueForSequence()Lcom/google/android/startop/iorap/RequestId;
+HPLcom/android/server/AlarmManagerService;->decrementAlarmCount(II)V
+HPLcom/android/server/am/ActivityManagerService$4;->newArray(I)[Landroid/content/IntentFilter;
+HPLcom/android/server/am/ActivityManagerService$PidMap;->remove(Lcom/android/server/am/ProcessRecord;)V
+HPLcom/android/server/am/ActivityManagerService;->updateOomAdjLocked(Ljava/lang/String;)V
+HPLcom/android/server/am/ActivityManagerShellCommand$1;-><init>(Lcom/android/server/am/ActivityManagerShellCommand;)V
+HPLcom/android/server/am/ActivityManagerShellCommand;-><init>(Lcom/android/server/am/ActivityManagerService;Z)V
+HPLcom/android/server/am/ActivityManagerShellCommand;->runStartActivity(Ljava/io/PrintWriter;)I
+HPLcom/android/server/am/HostingRecord;-><init>(Ljava/lang/String;Landroid/content/ComponentName;I)V
+HPLcom/android/server/am/PendingIntentController;->registerIntentSenderCancelListener(Landroid/content/IIntentSender;Lcom/android/internal/os/IResultReceiver;)V
+HPLcom/android/server/appprediction/-$$Lambda$AppPredictionManagerService$PredictionManagerServiceStub$vSY20eQq5y5FXrxhhqOTcEmezTs;-><init>(Landroid/app/prediction/AppPredictionSessionId;)V
+HPLcom/android/server/appprediction/-$$Lambda$RemoteAppPredictionService$9DCowUTEF8fYuBlWGxOmP5hTAWA;-><init>(Landroid/app/prediction/AppPredictionSessionId;)V
+HPLcom/android/server/appprediction/RemoteAppPredictionService;->lambda$requestPredictionUpdate$6(Landroid/app/prediction/AppPredictionSessionId;Landroid/service/appprediction/IPredictionService;)V
+HPLcom/android/server/autofill/-$$Lambda$AutofillManagerService$1$1-WNu3tTkxodB_LsZ7dGIlvrPN0;->visit(Ljava/lang/Object;)V
+HPLcom/android/server/autofill/ui/-$$Lambda$AutoFillUI$56AC3ykfo4h_e2LSjdkJ3XQn370;-><init>(Lcom/android/server/autofill/ui/AutoFillUI;Lcom/android/server/autofill/ui/AutoFillUI$AutoFillUiCallback;)V
+HPLcom/android/server/contentsuggestions/-$$Lambda$RemoteContentSuggestionsService$Enqw46SYVKFK9F2xX4qUcIu5_3I;->run(Landroid/os/IInterface;)V
+HPLcom/android/server/contentsuggestions/-$$Lambda$RemoteContentSuggestionsService$eoGnQ2MDLLnW1UBX6wxNE1VBLAk;->run(Landroid/os/IInterface;)V
+HPLcom/android/server/contentsuggestions/-$$Lambda$RemoteContentSuggestionsService$VKh1DoMPNSPjPfnVGdsInmxuqzc;->run(Landroid/os/IInterface;)V
+HPLcom/android/server/contentsuggestions/-$$Lambda$RemoteContentSuggestionsService$yUTbcaYlZCYTmagCkNJ3i2VCkY4;->run(Landroid/os/IInterface;)V
+HPLcom/android/server/contentsuggestions/RemoteContentSuggestionsService;->getServiceInterface(Landroid/os/IBinder;)Landroid/os/IInterface;
+HPLcom/android/server/display/AutomaticBrightnessController;->updateAutoBrightness(ZZ)V
+HPLcom/android/server/display/DisplayPowerController$BrightnessReason;->setModifier(I)V
+HPLcom/android/server/display/DisplayPowerController$BrightnessReason;->setReason(I)V
+HPLcom/android/server/display/DisplayPowerController$BrightnessReason;->toString(I)Ljava/lang/String;
+HPLcom/android/server/display/DisplayPowerController$BrightnessReason;->toString()Ljava/lang/String;
+HPLcom/android/server/media/projection/MediaProjectionManagerService$1;->onProcessDied(II)V
+HPLcom/android/server/net/NetworkPolicyManagerService;->removeUidStateUL(I)Z
+HPLcom/android/server/pm/permission/PermissionManagerService;->addOnPermissionsChangeListener(Landroid/permission/IOnPermissionsChangeListener;)V
+HPLcom/android/server/protolog/ProtoLogImpl;->getSingleInstance()Lcom/android/server/protolog/ProtoLogImpl;
+HPLcom/android/server/soundtrigger/SoundTriggerHelper$PowerSaveModeListener;-><init>(Lcom/android/server/soundtrigger/SoundTriggerHelper;)V
+HPLcom/android/server/statusbar/-$$Lambda$StatusBarManagerService$uF0ibEnnXe7Lxunxb98QQLJjgZM;->run()V
+HPLcom/android/server/statusbar/StatusBarManagerService$UiState;->setSystemUiState(IIILandroid/graphics/Rect;Landroid/graphics/Rect;Z)V
+HPLcom/android/server/statusbar/StatusBarManagerService$UiState;->systemUiStateEquals(IIILandroid/graphics/Rect;Landroid/graphics/Rect;Z)Z
+HPLcom/android/server/usage/UsageStatsDatabase;->filterStats(Lcom/android/server/usage/IntervalStats;)V
+HPLcom/android/server/VibratorService$VibrationInfo;-><init>(JLandroid/os/VibrationEffect;Landroid/os/VibrationEffect;Landroid/media/AudioAttributes;ILjava/lang/String;Ljava/lang/String;)V
+HPLcom/android/server/VibratorService;->getAppOpMode(Lcom/android/server/VibratorService$Vibration;)I
+HPLcom/android/server/VibratorService;->getCurrentIntensityLocked(Lcom/android/server/VibratorService$Vibration;)I
+HPLcom/android/server/VibratorService;->isAllowedToVibrateLocked(Lcom/android/server/VibratorService$Vibration;)Z
+HPLcom/android/server/VibratorService;->vibrate(ILjava/lang/String;Landroid/os/VibrationEffect;Landroid/media/AudioAttributes;Ljava/lang/String;Landroid/os/IBinder;)V
+HPLcom/android/server/wm/-$$Lambda$ActivityRecord$g49I60MBbnNkxHlgA-NR7ALwWTQ;->apply(Ljava/lang/Object;)Z
+HPLcom/android/server/wm/-$$Lambda$ActivityRecord$pBc6yUdDV5IrUd9vt6oCz6QzpiE;->accept(Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$BEx3OWenCvYAaV5h_J2ZkZXhEcY;->accept(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$DisplayPolicy$Zbxkj4wIhcDki6VwBh1kWmSmxqM;-><init>(Lcom/android/server/wm/DisplayPolicy;IIILandroid/graphics/Rect;Landroid/graphics/Rect;ZLcom/android/server/wm/WindowState;I[Lcom/android/internal/view/AppearanceRegion;ZZ)V
+HPLcom/android/server/wm/-$$Lambda$DisplayPolicy$Zbxkj4wIhcDki6VwBh1kWmSmxqM;->run()V
+HPLcom/android/server/wm/-$$Lambda$TaskChangeNotificationController$UexNbaqPy0mc3VxTw2coCctHho8;->accept(Landroid/app/ITaskStackListener;Landroid/os/Message;)V
+HPLcom/android/server/wm/ActivityMetricsLogger;->allWindowsDrawn()Z
+HPLcom/android/server/wm/ActivityRecord;->createAnimationBoundsLayer(Landroid/view/SurfaceControl$Transaction;)Landroid/view/SurfaceControl;
+HPLcom/android/server/wm/ActivityRecord;->destroyed(Ljava/lang/String;)V
+HPLcom/android/server/wm/ActivityRecord;->forAllWindowsUnchecked(Lcom/android/internal/util/ToBooleanFunction;Z)Z
+HPLcom/android/server/wm/ActivityRecord;->getTopFullscreenWindow()Lcom/android/server/wm/WindowState;
+HPLcom/android/server/wm/ActivityRecord;->loadAnimation(Landroid/view/WindowManager$LayoutParams;IZZ)Landroid/view/animation/Animation;
+HPLcom/android/server/wm/ActivityRecord;->notifyAppStopped()V
+HPLcom/android/server/wm/ActivityRecord;->onFirstWindowDrawn(Lcom/android/server/wm/WindowState;Lcom/android/server/wm/WindowStateAnimator;)V
+HPLcom/android/server/wm/ActivityRecord;->onRemovedFromDisplay()V
+HPLcom/android/server/wm/ActivityRecord;->removeDeadWindows()V
+HPLcom/android/server/wm/ActivityRecord;->setClientHidden(Z)V
+HPLcom/android/server/wm/ActivityRecord;->stopFreezingScreenLocked(Z)V
+HPLcom/android/server/wm/ActivityStack;->removeTimeoutsForActivity(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/ActivityStarter;->recycleTask(Lcom/android/server/wm/TaskRecord;Lcom/android/server/wm/ActivityRecord;Lcom/android/server/wm/ActivityRecord;[Lcom/android/server/wm/ActivityRecord;)I
+HPLcom/android/server/wm/ActivityTaskManagerService;->getTaskSnapshot(IZZ)Landroid/app/ActivityManager$TaskSnapshot;
+HPLcom/android/server/wm/AnimationAdapter;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
+HPLcom/android/server/wm/DisplayContent;->getWindowCornerRadius()F
+HPLcom/android/server/wm/DisplayPolicy;->convertNonDecorInsetsToStableInsets(Landroid/graphics/Rect;I)V
+HPLcom/android/server/wm/DisplayPolicy;->getInsetsPolicy()Lcom/android/server/wm/InsetsPolicy;
+HPLcom/android/server/wm/DisplayPolicy;->updateLightStatusBarAppearanceLw(ILcom/android/server/wm/WindowState;Lcom/android/server/wm/WindowState;)I
+HPLcom/android/server/wm/InsetsPolicy;->getInsetsForDispatch(Lcom/android/server/wm/WindowState;)Landroid/view/InsetsState;
+HPLcom/android/server/wm/InsetsPolicy;->isHidden(I)Z
+HPLcom/android/server/wm/InsetsPolicy;->updateBarControlTarget(Lcom/android/server/wm/WindowState;)V
+HPLcom/android/server/wm/Task;->getTopFullscreenActivity()Lcom/android/server/wm/ActivityRecord;
+HPLcom/android/server/wm/TaskRecord;->removeTaskActivitiesLocked(Ljava/lang/String;)V
+HPLcom/android/server/wm/Task;->removeChild(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/TaskSnapshotCache$CacheEntry;-><init>(Landroid/app/ActivityManager$TaskSnapshot;Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/TaskSnapshotController;->createTaskSnapshot(Lcom/android/server/wm/Task;F)Landroid/view/SurfaceControl$ScreenshotGraphicBuffer;
+HPLcom/android/server/wm/TaskSnapshotController;->findAppTokenForSnapshot(Lcom/android/server/wm/Task;)Lcom/android/server/wm/ActivityRecord;
+HPLcom/android/server/wm/TaskSnapshotPersister$DeleteWriteQueueItem;-><init>(Lcom/android/server/wm/TaskSnapshotPersister;II)V
+HPLcom/android/server/wm/TaskSnapshotPersister$StoreWriteQueueItem;-><init>(Lcom/android/server/wm/TaskSnapshotPersister;IILandroid/app/ActivityManager$TaskSnapshot;)V
+HPLcom/android/server/wm/TaskSnapshotPersister$StoreWriteQueueItem;->onQueuedLocked()V
+HPLcom/android/server/wm/WallpaperAnimationAdapter;->lambda$startWallpaperAnimations$0(JJLjava/util/function/Consumer;Ljava/util/ArrayList;Ljava/util/ArrayList;Lcom/android/server/wm/WallpaperWindowToken;)V
+HPLcom/android/server/wm/WallpaperAnimationAdapter;->startWallpaperAnimations(Lcom/android/server/wm/WindowManagerService;JJLjava/util/function/Consumer;Ljava/util/ArrayList;)[Landroid/view/RemoteAnimationTarget;
+HPLcom/android/server/wm/WindowAnimationSpec;->findTranslateAnimation(Landroid/view/animation/Animation;)Landroid/view/animation/TranslateAnimation;
+HPLcom/android/server/wm/WindowProcessControllerMap;->removeProcessFromUidMap(Lcom/android/server/wm/WindowProcessController;)V
+HPLcom/android/server/wm/WindowProcessController;->updateProcessInfo(ZZZ)V
+HPLcom/android/server/wm/WindowState$DeathRecipient;-><init>(Lcom/android/server/wm/WindowState;Lcom/android/server/wm/WindowState$1;)V
+HPLcom/android/server/wm/WindowState$WindowId;-><init>(Lcom/android/server/wm/WindowState;Lcom/android/server/wm/WindowState$1;)V
+HPLcom/google/android/startop/iorap/AppLaunchEvent$ActivityLaunched;->writeToParcelImpl(Landroid/os/Parcel;I)V
+HPLcom/google/android/startop/iorap/AppLaunchEvent$ActivityLaunchFinished;->writeToParcelImpl(Landroid/os/Parcel;I)V
+HPLcom/google/android/startop/iorap/AppLaunchEvent$IntentStarted;->writeToParcelImpl(Landroid/os/Parcel;I)V
+HPLcom/google/android/startop/iorap/AppLaunchEvent;->getTypeIndex()I
+HPLcom/android/server/pm/PackageManagerService;->access$7600(Lcom/android/server/pm/PackageManagerService;Landroid/content/Intent;Ljava/lang/String;IIZI)Landroid/content/pm/ResolveInfo;
+HPLcom/android/server/pm/PackageManagerService;->access$8100(Lcom/android/server/pm/PackageManagerService;II)Z
+HPLcom/android/server/wm/ActivityRecord$Token;->access$100(Lcom/android/server/wm/ActivityRecord$Token;Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/RemoteAnimationController$RemoteAnimationAdapterWrapper;->access$000(Lcom/android/server/wm/RemoteAnimationController$RemoteAnimationAdapterWrapper;)Lcom/android/server/wm/SurfaceAnimator$OnAnimationFinishedCallback;
+HPLcom/android/server/wm/RemoteAnimationController$RemoteAnimationAdapterWrapper;->access$400(Lcom/android/server/wm/RemoteAnimationController$RemoteAnimationAdapterWrapper;)Landroid/graphics/Point;
+HPLcom/android/server/wm/RemoteAnimationController$RemoteAnimationAdapterWrapper;->access$500(Lcom/android/server/wm/RemoteAnimationController$RemoteAnimationAdapterWrapper;)Landroid/graphics/Rect;
+HPLcom/android/server/wm/RemoteAnimationController;->access$100(Lcom/android/server/wm/RemoteAnimationController;)V
+HPLcom/android/server/wm/RemoteAnimationController;->access$300(Lcom/android/server/wm/RemoteAnimationController;)Landroid/view/RemoteAnimationAdapter;
+HPLcom/android/server/wm/TaskPersister$TaskWriteQueueItem;->access$200(Lcom/android/server/wm/TaskPersister$TaskWriteQueueItem;)Lcom/android/server/wm/TaskRecord;
+HPLcom/android/server/wm/TaskPersister;->access$000(I)Ljava/io/File;
+HPLcom/android/server/wm/WindowContainer;->access$100(Lcom/android/server/wm/WindowContainer;)Landroid/util/Pools$SynchronizedPool;
+HPLcom/android/server/wm/WindowManagerService;->access$1600(Lcom/android/server/wm/WindowManagerService;Landroid/os/IBinder;)V
+HPLcom/android/server/pm/ShortcutUser;->logSharingShortcutStats(Lcom/android/internal/logging/MetricsLogger;)V
+HPLcom/android/server/statusbar/StatusBarManagerService;->lambda$topAppWindowChanged$1$StatusBarManagerService(IZZ)V
+HPLcom/android/server/wm/ActivityRecord$Token;->attach(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/AnimationAdapter;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
+HPLcom/android/server/wm/DisplayPolicy;->lambda$updateSystemUiVisibilityLw$10$DisplayPolicy(IIILandroid/graphics/Rect;Landroid/graphics/Rect;ZLcom/android/server/wm/WindowState;I[Lcom/android/internal/view/AppearanceRegion;ZZ)V
+HPLcom/android/server/wm/InsetsSourceProvider;->hasWindow()Z
+HPLcom/android/server/wm/InsetsStateController;->onBarControlTargetChanged(Lcom/android/server/wm/InsetsControlTarget;Lcom/android/server/wm/InsetsControlTarget;Lcom/android/server/wm/InsetsControlTarget;Lcom/android/server/wm/InsetsControlTarget;)V
+HPLcom/android/server/wm/InsetsStateController;->peekSourceProvider(I)Lcom/android/server/wm/InsetsSourceProvider;
+HPLcom/android/server/wm/TaskPersister$TaskWriteQueueItem;->access$200(Lcom/android/server/wm/TaskPersister$TaskWriteQueueItem;)Lcom/android/server/wm/TaskRecord;
+HPLcom/android/server/wm/WindowContainer;->access$100(Lcom/android/server/wm/WindowContainer;)Landroid/util/Pools$SynchronizedPool;
+HPLcom/android/server/-$$Lambda$AlarmManagerService$2$Eo-D98J-N9R2METkD-12gPs320c;-><init>(Lcom/android/server/AlarmManagerService$2;Landroid/app/IAlarmCompleteListener;)V
+HPLcom/android/server/AlarmManagerService;->access$100(Lcom/android/server/AlarmManagerService;)Lcom/android/server/AlarmManagerService$Injector;
+HPLcom/android/server/AlarmManagerService;->access$2102(Lcom/android/server/AlarmManagerService;J)J
+HPLcom/android/server/AlarmManagerService;->access$2202(Lcom/android/server/AlarmManagerService;J)J
+HPLcom/android/server/AlarmManagerService;->access$2300(Lcom/android/server/AlarmManagerService;)V
+HPLcom/android/server/AlarmManagerService;->access$2400(Lcom/android/server/AlarmManagerService;Lcom/android/server/AlarmManagerService$Alarm;)Z
+HPLcom/android/server/AlarmManagerService;->isExemptFromAppStandby(Lcom/android/server/AlarmManagerService$Alarm;)Z
+HPLcom/android/server/appwidget/AppWidgetServiceImpl$HostId;-><init>(IILjava/lang/String;)V
+HPLcom/android/server/content/ContentService$ObserverCall;->run()V
+HPLcom/android/server/statusbar/-$$Lambda$StatusBarManagerService$uF0ibEnnXe7Lxunxb98QQLJjgZM;-><init>(Lcom/android/server/statusbar/StatusBarManagerService;IZZ)V
+HPLcom/android/server/statusbar/StatusBarManagerService$1;->topAppWindowChanged(IZZ)V
+HPLcom/android/server/statusbar/StatusBarManagerService$UiState;->access$1600(Lcom/android/server/statusbar/StatusBarManagerService$UiState;Z)V
+HPLcom/android/server/statusbar/StatusBarManagerService$UiState;->access$1700(Lcom/android/server/statusbar/StatusBarManagerService$UiState;Z)V
+HPLcom/android/server/statusbar/StatusBarManagerService$UiState;->setFullscreen(Z)V
+HPLcom/android/server/statusbar/StatusBarManagerService$UiState;->setImmersive(Z)V
+HPLcom/android/server/statusbar/StatusBarManagerService;->access$600(Lcom/android/server/statusbar/StatusBarManagerService;IZZ)V
+HPLcom/android/server/statusbar/StatusBarManagerService;->enforceStatusBar()V
+HPLcom/android/server/statusbar/StatusBarManagerService;->getUiState(I)Lcom/android/server/statusbar/StatusBarManagerService$UiState;
+HPLcom/android/server/statusbar/StatusBarManagerService;->topAppWindowChanged(IZZ)V
+HPLcom/android/server/wm/InsetsStateController;->onControlFakeTargetChanged(ILcom/android/server/wm/InsetsControlTarget;)V
+HPLcom/android/server/wm/LocalAnimationAdapter$AnimationSpec;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
+HPLcom/android/server/wm/LocalAnimationAdapter;->writeToProto(Landroid/util/proto/ProtoOutputStream;)V
+HPLcom/android/server/wm/PersisterQueue;->access$100(Lcom/android/server/wm/PersisterQueue;)Ljava/util/ArrayList;
+HPLcom/android/server/wm/PersisterQueue;->access$200(Lcom/android/server/wm/PersisterQueue;)Ljava/util/ArrayList;
+HPLcom/android/server/wm/PersisterQueue;->access$300(Lcom/android/server/wm/PersisterQueue;)V
+HPLcom/android/server/wm/TaskSnapshotPersister;->access$100(Lcom/android/server/wm/TaskSnapshotPersister;)Ljava/lang/Object;
+HPLcom/android/server/wm/TaskSnapshotPersister;->access$200(Lcom/android/server/wm/TaskSnapshotPersister;)Z
+HPLcom/android/server/wm/TaskSnapshotPersister;->access$300(Lcom/android/server/wm/TaskSnapshotPersister;)Ljava/util/ArrayDeque;
+HPLcom/android/server/wm/TaskSnapshotPersister;->access$402(Lcom/android/server/wm/TaskSnapshotPersister;Z)Z
+HPLcom/android/server/wm/WindowAnimationSpec;->findTranslateAnimation(Landroid/view/animation/Animation;)Landroid/view/animation/TranslateAnimation;
+HPLcom/android/server/wm/WindowAnimationSpec;->writeToProtoInner(Landroid/util/proto/ProtoOutputStream;)V
+HPLcom/android/server/wm/WindowContainer;->access$100(Lcom/android/server/wm/WindowContainer;)Landroid/util/Pools$SynchronizedPool;
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 8e200196050c..1fc47516864b 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -78,6 +78,8 @@ final class SaveUi {
private static final int THEME_ID_DARK =
com.android.internal.R.style.Theme_DeviceDefault_Autofill_Save;
+ private static final int SCROLL_BAR_DEFAULT_DELAY_BEFORE_FADE_MS = 500;
+
public interface OnSaveListener {
void onSave();
void onCancel(IntentSender listener);
@@ -195,8 +197,20 @@ final class SaveUi {
if ((type & SaveInfo.SAVE_DATA_TYPE_ADDRESS) != 0) {
types.add(context.getString(R.string.autofill_save_type_address));
}
- if ((type & SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD) != 0) {
+
+ // fallback to generic card type if set multiple types
+ final int cardTypeMask = SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD
+ | SaveInfo.SAVE_DATA_TYPE_DEBIT_CARD
+ | SaveInfo.SAVE_DATA_TYPE_PAYMENT_CARD;
+ final int count = Integer.bitCount(type & cardTypeMask);
+ if (count > 1 || (type & SaveInfo.SAVE_DATA_TYPE_GENERIC_CARD) != 0) {
+ types.add(context.getString(R.string.autofill_save_type_generic_card));
+ } else if ((type & SaveInfo.SAVE_DATA_TYPE_PAYMENT_CARD) != 0) {
+ types.add(context.getString(R.string.autofill_save_type_payment_card));
+ } else if ((type & SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD) != 0) {
types.add(context.getString(R.string.autofill_save_type_credit_card));
+ } else if ((type & SaveInfo.SAVE_DATA_TYPE_DEBIT_CARD) != 0) {
+ types.add(context.getString(R.string.autofill_save_type_debit_card));
}
if ((type & SaveInfo.SAVE_DATA_TYPE_USERNAME) != 0) {
types.add(context.getString(R.string.autofill_save_type_username));
@@ -252,6 +266,8 @@ final class SaveUi {
new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
subtitleContainer.setVisibility(View.VISIBLE);
+ subtitleContainer.setScrollBarDefaultDelayBeforeFade(
+ SCROLL_BAR_DEFAULT_DELAY_BEFORE_FADE_MS);
}
if (sDebug) Slog.d(TAG, "on constructor: title=" + mTitle + ", subTitle=" + mSubTitle);
}
@@ -265,7 +281,9 @@ final class SaveUi {
noButton.setOnClickListener((v) -> mListener.onCancel(info.getNegativeActionListener()));
final TextView yesButton = view.findViewById(R.id.autofill_save_yes);
- if (isUpdate) {
+ if (info.getPositiveActionStyle() == SaveInfo.POSITIVE_BUTTON_STYLE_CONTINUE) {
+ yesButton.setText(R.string.autofill_continue_yes);
+ } else if (isUpdate) {
yesButton.setText(R.string.autofill_update_yes);
}
yesButton.setOnClickListener((v) -> mListener.onSave());
@@ -282,7 +300,7 @@ final class SaveUi {
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
- window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS);
+ window.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
window.setGravity(Gravity.BOTTOM | Gravity.CENTER);
window.setCloseOnTouchOutside(true);
@@ -429,6 +447,9 @@ final class SaveUi {
saveUiView.findViewById(R.id.autofill_save_custom_subtitle);
subtitleContainer.addView(customSubtitleView);
subtitleContainer.setVisibility(View.VISIBLE);
+ subtitleContainer.setScrollBarDefaultDelayBeforeFade(
+ SCROLL_BAR_DEFAULT_DELAY_BEFORE_FADE_MS);
+
return true;
} catch (Exception e) {
Slog.e(TAG, "Error applying custom description. ", e);
diff --git a/services/backup/Android.bp b/services/backup/Android.bp
index ef03d83d4916..a3b0c891d00a 100644
--- a/services/backup/Android.bp
+++ b/services/backup/Android.bp
@@ -2,4 +2,5 @@ java_library_static {
name: "services.backup",
srcs: ["java/**/*.java"],
libs: ["services.core"],
+ static_libs: ["backuplib"],
}
diff --git a/services/backup/backuplib/Android.bp b/services/backup/backuplib/Android.bp
new file mode 100644
index 000000000000..7b194a0923c2
--- /dev/null
+++ b/services/backup/backuplib/Android.bp
@@ -0,0 +1,5 @@
+java_library {
+ name: "backuplib",
+ srcs: ["java/**/*.java"],
+ libs: ["services.core"],
+}
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
index 30ce4cf2fd3f..30ce4cf2fd3f 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/DelegatingTransport.java b/services/backup/backuplib/java/com/android/server/backup/transport/DelegatingTransport.java
new file mode 100644
index 000000000000..ab870803e60d
--- /dev/null
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/DelegatingTransport.java
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.transport;
+
+import android.app.backup.BackupAgent;
+import android.app.backup.BackupTransport;
+import android.app.backup.RestoreDescription;
+import android.app.backup.RestoreSet;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+
+import com.android.internal.backup.IBackupTransport;
+
+/**
+ * Delegates all transport methods to the delegate() implemented in the derived class.
+ */
+public abstract class DelegatingTransport extends IBackupTransport.Stub {
+ protected abstract IBackupTransport getDelegate() throws RemoteException;
+
+ /**
+ * Ask the transport for the name under which it should be registered. This will
+ * typically be its host service's component name, but need not be.
+ */
+ @Override
+ public String name() throws RemoteException {
+ return getDelegate().name();
+ }
+
+ /**
+ * Ask the transport for an Intent that can be used to launch any internal
+ * configuration Activity that it wishes to present. For example, the transport
+ * may offer a UI for allowing the user to supply login credentials for the
+ * transport's off-device backend.
+ *
+ * If the transport does not supply any user-facing configuration UI, it should
+ * return null from this method.
+ *
+ * @return An Intent that can be passed to Context.startActivity() in order to
+ * launch the transport's configuration UI. This method will return null
+ * if the transport does not offer any user-facing configuration UI.
+ */
+ @Override
+ public Intent configurationIntent() throws RemoteException {
+ return getDelegate().configurationIntent();
+ }
+
+ /**
+ * On demand, supply a one-line string that can be shown to the user that
+ * describes the current backend destination. For example, a transport that
+ * can potentially associate backup data with arbitrary user accounts should
+ * include the name of the currently-active account here.
+ *
+ * @return A string describing the destination to which the transport is currently
+ * sending data. This method should not return null.
+ */
+ @Override
+ public String currentDestinationString() throws RemoteException {
+ return getDelegate().currentDestinationString();
+ }
+
+ /**
+ * Ask the transport for an Intent that can be used to launch a more detailed
+ * secondary data management activity. For example, the configuration intent might
+ * be one for allowing the user to select which account they wish to associate
+ * their backups with, and the management intent might be one which presents a
+ * UI for managing the data on the backend.
+ *
+ * <p>In the Settings UI, the configuration intent will typically be invoked
+ * when the user taps on the preferences item labeled with the current
+ * destination string, and the management intent will be placed in an overflow
+ * menu labelled with the management label string.
+ *
+ * <p>If the transport does not supply any user-facing data management
+ * UI, then it should return {@code null} from this method.
+ *
+ * @return An intent that can be passed to Context.startActivity() in order to
+ * launch the transport's data-management UI. This method will return
+ * {@code null} if the transport does not offer any user-facing data
+ * management UI.
+ */
+ @Override
+ public Intent dataManagementIntent() throws RemoteException {
+ return getDelegate().dataManagementIntent();
+ }
+
+ /**
+ * On demand, supply a short {@link CharSequence} that can be shown to the user as the
+ * label on
+ * an overflow menu item used to invoke the data management UI.
+ *
+ * @return A {@link CharSequence} to be used as the label for the transport's data management
+ * affordance. If the transport supplies a data management intent, this
+ * method must not return {@code null}.
+ */
+ @Override
+ public CharSequence dataManagementIntentLabel() throws RemoteException {
+ return getDelegate().dataManagementIntentLabel();
+ }
+
+ /**
+ * Ask the transport where, on local device storage, to keep backup state blobs.
+ * This is per-transport so that mock transports used for testing can coexist with
+ * "live" backup services without interfering with the live bookkeeping. The
+ * returned string should be a name that is expected to be unambiguous among all
+ * available backup transports; the name of the class implementing the transport
+ * is a good choice. This MUST be constant.
+ *
+ * @return A unique name, suitable for use as a file or directory name, that the
+ * Backup Manager could use to disambiguate state files associated with
+ * different backup transports.
+ */
+ @Override
+ public String transportDirName() throws RemoteException {
+ return getDelegate().transportDirName();
+ }
+
+ /**
+ * Verify that this is a suitable time for a backup pass. This should return zero
+ * if a backup is reasonable right now, some positive value otherwise. This method
+ * will be called outside of the {@link #startSession}/{@link #endSession} pair.
+ *
+ * <p>If this is not a suitable time for a backup, the transport should return a
+ * backoff delay, in milliseconds, after which the Backup Manager should try again.
+ *
+ * @return Zero if this is a suitable time for a backup pass, or a positive time delay
+ * in milliseconds to suggest deferring the backup pass for a while.
+ */
+ @Override
+ public long requestBackupTime() throws RemoteException {
+ return getDelegate().requestBackupTime();
+ }
+
+ /**
+ * Initialize the server side storage for this device, erasing all stored data.
+ * The transport may send the request immediately, or may buffer it. After
+ * this is called, {@link #finishBackup} must be called to ensure the request
+ * is sent and received successfully.
+ *
+ * @return One of {@link BackupConstants#TRANSPORT_OK} (OK so far) or
+ * {@link BackupConstants#TRANSPORT_ERROR} (on network error or other failure).
+ */
+ @Override
+ public int initializeDevice() throws RemoteException {
+ return getDelegate().initializeDevice();
+ }
+
+ /**
+ * Send one application's data to the backup destination. The transport may send
+ * the data immediately, or may buffer it. After this is called, {@link #finishBackup}
+ * must be called to ensure the data is sent and recorded successfully.
+ *
+ * @param packageInfo The identity of the application whose data is being backed up.
+ * This specifically includes the signature list for the package.
+ * @param inFd Descriptor of file with data that resulted from invoking the application's
+ * BackupService.doBackup() method. This may be a pipe rather than a file on
+ * persistent media, so it may not be seekable.
+ * @param flags Some of {@link BackupTransport#FLAG_USER_INITIATED}.
+ * @return one of {@link BackupConstants#TRANSPORT_OK} (OK so far),
+ * {@link BackupConstants#TRANSPORT_ERROR} (on network error or other failure), or
+ * {@link BackupConstants#TRANSPORT_NOT_INITIALIZED} (if the backend dataset has
+ * become lost due to inactive expiry or some other reason and needs re-initializing)
+ */
+ @Override
+ public int performBackup(PackageInfo packageInfo,
+ ParcelFileDescriptor inFd, int flags) throws RemoteException {
+ return getDelegate().performBackup(packageInfo, inFd, flags);
+ }
+
+ /**
+ * Erase the give application's data from the backup destination. This clears
+ * out the given package's data from the current backup set, making it as though
+ * the app had never yet been backed up. After this is called, {@link finishBackup}
+ * must be called to ensure that the operation is recorded successfully.
+ *
+ * @return the same error codes as {@link #performBackup}.
+ * @param packageInfo
+ */
+ @Override
+ public int clearBackupData(PackageInfo packageInfo) throws RemoteException {
+ return getDelegate().clearBackupData(packageInfo);
+ }
+
+ /**
+ * Finish sending application data to the backup destination. This must be
+ * called after {@link #performBackup} or {@link clearBackupData} to ensure that
+ * all data is sent. Only when this method returns true can a backup be assumed
+ * to have succeeded.
+ *
+ * @return the same error codes as {@link #performBackup}.
+ */
+ @Override
+ public int finishBackup() throws RemoteException {
+ return getDelegate().finishBackup();
+ }
+
+ /**
+ * Get the set of all backups currently available over this transport.
+ *
+ * @return Descriptions of the set of restore images available for this device,
+ * or null if an error occurred (the attempt should be rescheduled).
+ **/
+ @Override
+ public RestoreSet[] getAvailableRestoreSets() throws RemoteException {
+ return getDelegate().getAvailableRestoreSets();
+ }
+
+ /**
+ * Get the identifying token of the backup set currently being stored from
+ * this device. This is used in the case of applications wishing to restore
+ * their last-known-good data.
+ *
+ * @return A token that can be passed to {@link #startRestore}, or 0 if there
+ * is no backup set available corresponding to the current device state.
+ */
+ @Override
+ public long getCurrentRestoreSet() throws RemoteException {
+ return getDelegate().getCurrentRestoreSet();
+ }
+
+ /**
+ * Start restoring application data from backup. After calling this function,
+ * alternate calls to {@link #nextRestorePackage} and {@link #nextRestoreData}
+ * to walk through the actual application data.
+ *
+ * @param token A backup token as returned by {@link #getAvailableRestoreSets}
+ * or {@link #getCurrentRestoreSet}.
+ * @param packages List of applications to restore (if data is available).
+ * Application data will be restored in the order given.
+ * @return One of {@link BackupConstants#TRANSPORT_OK} (OK so far, call
+ * {@link #nextRestorePackage}) or {@link BackupConstants#TRANSPORT_ERROR}
+ * (an error occurred, the restore should be aborted and rescheduled).
+ */
+ @Override
+ public int startRestore(long token, PackageInfo[] packages) throws RemoteException {
+ return getDelegate().startRestore(token, packages);
+ }
+
+ /**
+ * Get the package name of the next application with data in the backup store, plus
+ * a description of the structure of the restored archive: either TYPE_KEY_VALUE for
+ * an original-API key/value dataset, or TYPE_FULL_STREAM for a tarball-type archive stream.
+ *
+ * <p>If the package name in the returned RestoreDescription object is the singleton
+ * {@link RestoreDescription#NO_MORE_PACKAGES}, it indicates that no further data is available
+ * in the current restore session: all packages described in startRestore() have been
+ * processed.
+ *
+ * <p>If this method returns {@code null}, it means that a transport-level error has
+ * occurred and the entire restore operation should be abandoned.
+ *
+ * @return A RestoreDescription object containing the name of one of the packages
+ * supplied to {@link #startRestore} plus an indicator of the data type of that
+ * restore data; or {@link RestoreDescription#NO_MORE_PACKAGES} to indicate that
+ * no more packages can be restored in this session; or {@code null} to indicate
+ * a transport-level error.
+ */
+ @Override
+ public RestoreDescription nextRestorePackage() throws RemoteException {
+ return getDelegate().nextRestorePackage();
+ }
+
+ /**
+ * Get the data for the application returned by {@link #nextRestorePackage}.
+ *
+ * @param outFd An open, writable file into which the backup data should be stored.
+ * @return the same error codes as {@link #startRestore}.
+ */
+ @Override
+ public int getRestoreData(ParcelFileDescriptor outFd) throws RemoteException {
+ return getDelegate().getRestoreData(outFd);
+ }
+
+ /**
+ * End a restore session (aborting any in-process data transfer as necessary),
+ * freeing any resources and connections used during the restore process.
+ */
+ @Override
+ public void finishRestore() throws RemoteException {
+ getDelegate().finishRestore();
+ }
+
+ @Override
+ public long requestFullBackupTime() throws RemoteException {
+ return getDelegate().requestFullBackupTime();
+ }
+
+ @Override
+ public int performFullBackup(PackageInfo targetPackage,
+ ParcelFileDescriptor socket, int flags) throws RemoteException {
+ return getDelegate().performFullBackup(targetPackage, socket, flags);
+ }
+
+ @Override
+ public int checkFullBackupSize(long size) throws RemoteException {
+ return getDelegate().checkFullBackupSize(size);
+ }
+
+ @Override
+ public int sendBackupData(int numBytes) throws RemoteException {
+ return getDelegate().sendBackupData(numBytes);
+ }
+
+ @Override
+ public void cancelFullBackup() throws RemoteException {
+ getDelegate().cancelFullBackup();
+ }
+
+ /**
+ * Ask the transport whether this app is eligible for backup.
+ *
+ * @param targetPackage The identity of the application.
+ * @param isFullBackup If set, transport should check if app is eligible for full data backup,
+ * otherwise to check if eligible for key-value backup.
+ * @return Whether this app is eligible for backup.
+ */
+ @Override
+ public boolean isAppEligibleForBackup(PackageInfo targetPackage,
+ boolean isFullBackup) throws RemoteException {
+ return getDelegate().isAppEligibleForBackup(targetPackage, isFullBackup);
+ }
+
+ /**
+ * Ask the transport about current quota for backup size of the package.
+ *
+ * @param packageName ID of package to provide the quota.
+ * @param isFullBackup If set, transport should return limit for full data backup, otherwise
+ * for key-value backup.
+ * @return Current limit on full data backup size in bytes.
+ */
+ @Override
+ public long getBackupQuota(String packageName, boolean isFullBackup) throws RemoteException {
+ return getDelegate().getBackupQuota(packageName, isFullBackup);
+ }
+
+ /**
+ * Ask the transport to provide data for the "current" package being restored. This
+ * is the package that was just reported by {@link #nextRestorePackage()} as having
+ * {@link RestoreDescription#TYPE_FULL_STREAM} data.
+ *
+ * The transport writes some data to the socket supplied to this call, and returns
+ * the number of bytes written. The system will then read that many bytes and
+ * stream them to the application's agent for restore, then will call this method again
+ * to receive the next chunk of the archive. This sequence will be repeated until the
+ * transport returns zero indicating that all of the package's data has been delivered
+ * (or returns a negative value indicating some sort of hard error condition at the
+ * transport level).
+ *
+ * <p>After this method returns zero, the system will then call
+ * {@link #getNextFullRestorePackage()} to begin the restore process for the next
+ * application, and the sequence begins again.
+ *
+ * <p>The transport should always close this socket when returning from this method.
+ * Do not cache this socket across multiple calls or you may leak file descriptors.
+ *
+ * @param socket The file descriptor that the transport will use for delivering the
+ * streamed archive. The transport must close this socket in all cases when returning
+ * from this method.
+ * @return 0 when no more data for the current package is available. A positive value
+ * indicates the presence of that many bytes to be delivered to the app. Any negative
+ * return value is treated as equivalent to {@link BackupTransport#TRANSPORT_ERROR},
+ * indicating a fatal error condition that precludes further restore operations
+ * on the current dataset.
+ */
+ @Override
+ public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) throws RemoteException {
+ return getDelegate().getNextFullRestoreDataChunk(socket);
+ }
+
+ /**
+ * If the OS encounters an error while processing {@link RestoreDescription#TYPE_FULL_STREAM}
+ * data for restore, it will invoke this method to tell the transport that it should
+ * abandon the data download for the current package. The OS will then either call
+ * {@link #nextRestorePackage()} again to move on to restoring the next package in the
+ * set being iterated over, or will call {@link #finishRestore()} to shut down the restore
+ * operation.
+ *
+ * @return {@link #TRANSPORT_OK} if the transport was successful in shutting down the
+ * current stream cleanly, or {@link #TRANSPORT_ERROR} to indicate a serious
+ * transport-level failure. If the transport reports an error here, the entire restore
+ * operation will immediately be finished with no further attempts to restore app data.
+ */
+ @Override
+ public int abortFullRestore() throws RemoteException {
+ return getDelegate().abortFullRestore();
+ }
+
+ /**
+ * Returns flags with additional information about the transport, which is accessible to the
+ * {@link BackupAgent}. This allows the agent to decide what to backup or
+ * restore based on properties of the transport.
+ *
+ * <p>For supported flags see {@link BackupAgent}.
+ */
+ @Override
+ public int getTransportFlags() throws RemoteException {
+ return getDelegate().getTransportFlags();
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/transport/OnTransportRegisteredListener.java b/services/backup/backuplib/java/com/android/server/backup/transport/OnTransportRegisteredListener.java
index 391ec2d7f294..391ec2d7f294 100644
--- a/services/backup/java/com/android/server/backup/transport/OnTransportRegisteredListener.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/OnTransportRegisteredListener.java
diff --git a/services/backup/java/com/android/server/backup/transport/TransportClient.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java
index 7c5a57c004e4..7c5a57c004e4 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportClient.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportClientManager.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportClientManager.java
new file mode 100644
index 000000000000..72b1ee741d95
--- /dev/null
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportClientManager.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.transport;
+
+import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPORT_HOST;
+import static com.android.server.backup.transport.TransportUtils.formatMessage;
+
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import com.android.internal.backup.IBackupTransport;
+import com.android.server.backup.TransportManager;
+import com.android.server.backup.transport.TransportUtils.Priority;
+
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.function.Function;
+
+/**
+ * Manages the creation and disposal of {@link TransportClient}s. The only class that should use
+ * this is {@link TransportManager}, all the other usages should go to {@link TransportManager}.
+ */
+public class TransportClientManager {
+ private static final String TAG = "TransportClientManager";
+ private static final String SERVICE_ACTION_ENCRYPTING_TRANSPORT =
+ "android.encryption.BACKUP_ENCRYPTION";
+ private static final ComponentName ENCRYPTING_TRANSPORT = new ComponentName(
+ "com.android.server.backup.encryption",
+ "com.android.server.backup.encryption.BackupEncryptionService");
+ private static final String ENCRYPTING_TRANSPORT_REAL_TRANSPORT_KEY = "transport";
+
+ private final @UserIdInt int mUserId;
+ private final Context mContext;
+ private final TransportStats mTransportStats;
+ private final Object mTransportClientsLock = new Object();
+ private int mTransportClientsCreated = 0;
+ private Map<TransportClient, String> mTransportClientsCallerMap = new WeakHashMap<>();
+ private final Function<ComponentName, Intent> mIntentFunction;
+
+ /**
+ * Return an {@link Intent} which resolves to an intermediate {@link IBackupTransport} that
+ * encrypts (or decrypts) the data when sending it (or receiving it) from the {@link
+ * IBackupTransport} for the given {@link ComponentName}.
+ */
+ public static Intent getEncryptingTransportIntent(ComponentName tranportComponent) {
+ return new Intent(SERVICE_ACTION_ENCRYPTING_TRANSPORT)
+ .setComponent(ENCRYPTING_TRANSPORT)
+ .putExtra(ENCRYPTING_TRANSPORT_REAL_TRANSPORT_KEY, tranportComponent);
+ }
+
+ /**
+ * Return an {@link Intent} which resolves to the {@link IBackupTransport} for the {@link
+ * ComponentName}.
+ */
+ private static Intent getRealTransportIntent(ComponentName transportComponent) {
+ return new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(transportComponent);
+ }
+
+ /**
+ * Given a {@link Intent} originally created by {@link
+ * #getEncryptingTransportIntent(ComponentName)}, returns the {@link Intent} which resolves to
+ * the {@link IBackupTransport} for that {@link ComponentName}.
+ */
+ public static Intent getRealTransportIntent(Intent encryptingTransportIntent) {
+ ComponentName transportComponent = encryptingTransportIntent.getParcelableExtra(
+ ENCRYPTING_TRANSPORT_REAL_TRANSPORT_KEY);
+ Intent intent = getRealTransportIntent(transportComponent)
+ .putExtras(encryptingTransportIntent.getExtras());
+ intent.removeExtra(ENCRYPTING_TRANSPORT_REAL_TRANSPORT_KEY);
+ return intent;
+ }
+
+ /**
+ * Create a {@link TransportClientManager} such that {@link #getTransportClient(ComponentName,
+ * Bundle, String)} returns a {@link TransportClient} which connects to an intermediate {@link
+ * IBackupTransport} that encrypts (or decrypts) the data when sending it (or receiving it) from
+ * the {@link IBackupTransport} for the given {@link ComponentName}.
+ */
+ public static TransportClientManager createEncryptingClientManager(@UserIdInt int userId,
+ Context context, TransportStats transportStats) {
+ return new TransportClientManager(userId, context, transportStats,
+ TransportClientManager::getEncryptingTransportIntent);
+ }
+
+ public TransportClientManager(@UserIdInt int userId, Context context,
+ TransportStats transportStats) {
+ this(userId, context, transportStats, TransportClientManager::getRealTransportIntent);
+ }
+
+ private TransportClientManager(@UserIdInt int userId, Context context,
+ TransportStats transportStats, Function<ComponentName, Intent> intentFunction) {
+ mUserId = userId;
+ mContext = context;
+ mTransportStats = transportStats;
+ mIntentFunction = intentFunction;
+ }
+
+ /**
+ * Retrieves a {@link TransportClient} for the transport identified by {@param
+ * transportComponent}.
+ *
+ * @param transportComponent The {@link ComponentName} of the transport.
+ * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
+ * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
+ * details.
+ * @return A {@link TransportClient}.
+ */
+ public TransportClient getTransportClient(ComponentName transportComponent, String caller) {
+ return getTransportClient(transportComponent, null, caller);
+ }
+
+ /**
+ * Retrieves a {@link TransportClient} for the transport identified by {@param
+ * transportComponent} whose binding intent will have the {@param extras} extras.
+ *
+ * @param transportComponent The {@link ComponentName} of the transport.
+ * @param extras A {@link Bundle} of extras to pass to the binding intent.
+ * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
+ * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
+ * details.
+ * @return A {@link TransportClient}.
+ */
+ public TransportClient getTransportClient(
+ ComponentName transportComponent, @Nullable Bundle extras, String caller) {
+ Intent bindIntent = mIntentFunction.apply(transportComponent);
+ if (extras != null) {
+ bindIntent.putExtras(extras);
+ }
+ return getTransportClient(transportComponent, caller, bindIntent);
+ }
+
+ private TransportClient getTransportClient(
+ ComponentName transportComponent, String caller, Intent bindIntent) {
+ synchronized (mTransportClientsLock) {
+ TransportClient transportClient =
+ new TransportClient(
+ mUserId,
+ mContext,
+ mTransportStats,
+ bindIntent,
+ transportComponent,
+ Integer.toString(mTransportClientsCreated),
+ caller);
+ mTransportClientsCallerMap.put(transportClient, caller);
+ mTransportClientsCreated++;
+ TransportUtils.log(
+ Priority.DEBUG,
+ TAG,
+ formatMessage(null, caller, "Retrieving " + transportClient));
+ return transportClient;
+ }
+ }
+
+ /**
+ * Disposes of the {@link TransportClient}.
+ *
+ * @param transportClient The {@link TransportClient} to be disposed of.
+ * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
+ * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
+ * details.
+ */
+ public void disposeOfTransportClient(TransportClient transportClient, String caller) {
+ transportClient.unbind(caller);
+ transportClient.markAsDisposed();
+ synchronized (mTransportClientsLock) {
+ TransportUtils.log(
+ Priority.DEBUG,
+ TAG,
+ formatMessage(null, caller, "Disposing of " + transportClient));
+ mTransportClientsCallerMap.remove(transportClient);
+ }
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println("Transport clients created: " + mTransportClientsCreated);
+ synchronized (mTransportClientsLock) {
+ pw.println("Current transport clients: " + mTransportClientsCallerMap.size());
+ for (TransportClient transportClient : mTransportClientsCallerMap.keySet()) {
+ String caller = mTransportClientsCallerMap.get(transportClient);
+ pw.println(" " + transportClient + " [" + caller + "]");
+ for (String logEntry : transportClient.getLogBuffer()) {
+ pw.println(" " + logEntry);
+ }
+ }
+ }
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/transport/TransportConnectionListener.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnectionListener.java
index 1ccffd01d12c..1ccffd01d12c 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportConnectionListener.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportConnectionListener.java
diff --git a/services/backup/java/com/android/server/backup/transport/TransportNotAvailableException.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportNotAvailableException.java
index c08eb7f4a54e..c08eb7f4a54e 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportNotAvailableException.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportNotAvailableException.java
diff --git a/services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportNotRegisteredException.java
index 02766deeb7e2..02766deeb7e2 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportNotRegisteredException.java
diff --git a/services/backup/java/com/android/server/backup/transport/TransportStats.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportStats.java
index bd84782122ad..bd84782122ad 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportStats.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportStats.java
diff --git a/services/backup/java/com/android/server/backup/transport/TransportUtils.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportUtils.java
index 766d77bd639c..766d77bd639c 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportUtils.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportUtils.java
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index e5e11ea2253e..ac006df7f475 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -239,7 +239,6 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
private final KeyValueBackupReporter mReporter;
private final OnTaskFinishedListener mTaskFinishedListener;
private final boolean mUserInitiated;
- private final boolean mNonIncremental;
private final int mCurrentOpToken;
private final int mUserId;
private final File mStateDirectory;
@@ -264,6 +263,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
// and at least one of the packages had data. Used to avoid updating current token for
// empty backups.
private boolean mHasDataToBackup;
+ private boolean mNonIncremental;
/**
* This {@link ConditionVariable} is used to signal that the cancel operation has been
@@ -412,6 +412,11 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
try {
IBackupTransport transport = mTransportClient.connectOrThrow("KVBT.startTask()");
String transportName = transport.name();
+ if (transportName.contains("EncryptedLocalTransport")) {
+ // Temporary code for EiTF POC. Only supports non-incremental backups.
+ mNonIncremental = true;
+ }
+
mReporter.onTransportReady(transportName);
// If we haven't stored PM metadata yet, we must initialize the transport.
diff --git a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java b/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
deleted file mode 100644
index a4e9b1091bed..000000000000
--- a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.backup.transport;
-
-import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPORT_HOST;
-import static com.android.server.backup.transport.TransportUtils.formatMessage;
-
-import android.annotation.UserIdInt;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-
-import com.android.server.backup.TransportManager;
-import com.android.server.backup.transport.TransportUtils.Priority;
-
-import java.io.PrintWriter;
-import java.util.Map;
-import java.util.WeakHashMap;
-
-/**
- * Manages the creation and disposal of {@link TransportClient}s. The only class that should use
- * this is {@link TransportManager}, all the other usages should go to {@link TransportManager}.
- */
-public class TransportClientManager {
- private static final String TAG = "TransportClientManager";
-
- private final @UserIdInt int mUserId;
- private final Context mContext;
- private final TransportStats mTransportStats;
- private final Object mTransportClientsLock = new Object();
- private int mTransportClientsCreated = 0;
- private Map<TransportClient, String> mTransportClientsCallerMap = new WeakHashMap<>();
-
- public TransportClientManager(@UserIdInt int userId, Context context,
- TransportStats transportStats) {
- mUserId = userId;
- mContext = context;
- mTransportStats = transportStats;
- }
-
- /**
- * Retrieves a {@link TransportClient} for the transport identified by {@param
- * transportComponent}.
- *
- * @param transportComponent The {@link ComponentName} of the transport.
- * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
- * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
- * details.
- * @return A {@link TransportClient}.
- */
- public TransportClient getTransportClient(ComponentName transportComponent, String caller) {
- Intent bindIntent =
- new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(transportComponent);
-
- return getTransportClient(transportComponent, caller, bindIntent);
- }
-
- /**
- * Retrieves a {@link TransportClient} for the transport identified by {@param
- * transportComponent} whose binding intent will have the {@param extras} extras.
- *
- * @param transportComponent The {@link ComponentName} of the transport.
- * @param extras A {@link Bundle} of extras to pass to the binding intent.
- * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
- * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
- * details.
- * @return A {@link TransportClient}.
- */
- public TransportClient getTransportClient(
- ComponentName transportComponent, Bundle extras, String caller) {
- Intent bindIntent =
- new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(transportComponent);
- bindIntent.putExtras(extras);
-
- return getTransportClient(transportComponent, caller, bindIntent);
- }
-
- private TransportClient getTransportClient(
- ComponentName transportComponent, String caller, Intent bindIntent) {
- synchronized (mTransportClientsLock) {
- TransportClient transportClient =
- new TransportClient(
- mUserId,
- mContext,
- mTransportStats,
- bindIntent,
- transportComponent,
- Integer.toString(mTransportClientsCreated),
- caller);
- mTransportClientsCallerMap.put(transportClient, caller);
- mTransportClientsCreated++;
- TransportUtils.log(
- Priority.DEBUG,
- TAG,
- formatMessage(null, caller, "Retrieving " + transportClient));
- return transportClient;
- }
- }
-
- /**
- * Disposes of the {@link TransportClient}.
- *
- * @param transportClient The {@link TransportClient} to be disposed of.
- * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
- * {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
- * details.
- */
- public void disposeOfTransportClient(TransportClient transportClient, String caller) {
- transportClient.unbind(caller);
- transportClient.markAsDisposed();
- synchronized (mTransportClientsLock) {
- TransportUtils.log(
- Priority.DEBUG,
- TAG,
- formatMessage(null, caller, "Disposing of " + transportClient));
- mTransportClientsCallerMap.remove(transportClient);
- }
- }
-
- public void dump(PrintWriter pw) {
- pw.println("Transport clients created: " + mTransportClientsCreated);
- synchronized (mTransportClientsLock) {
- pw.println("Current transport clients: " + mTransportClientsCallerMap.size());
- for (TransportClient transportClient : mTransportClientsCallerMap.keySet()) {
- String caller = mTransportClientsCallerMap.get(transportClient);
- pw.println(" " + transportClient + " [" + caller + "]");
- for (String logEntry : transportClient.getLogBuffer()) {
- pw.println(" " + logEntry);
- }
- }
- }
- }
-}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index c8dbb363bc36..27824afb8d46 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -18,11 +18,13 @@ package com.android.server.contentcapture;
import static android.Manifest.permission.MANAGE_CONTENT_CAPTURE;
import static android.content.Context.CONTENT_CAPTURE_MANAGER_SERVICE;
+import static android.service.contentcapture.ContentCaptureService.setClientState;
import static android.view.contentcapture.ContentCaptureHelper.toList;
import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE;
import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_OK;
import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_SECURITY_EXCEPTION;
import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_TRUE;
+import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED;
import static com.android.internal.util.SyncResultReceiver.bundleFor;
@@ -520,6 +522,17 @@ public final class ContentCaptureManagerService extends
return true;
}
+ @GuardedBy("mLock")
+ private boolean isDefaultServiceLocked(int userId) {
+ final String defaultServiceName = mServiceNameResolver.getDefaultServiceName(userId);
+ if (defaultServiceName == null) {
+ return false;
+ }
+
+ final String currentServiceName = mServiceNameResolver.getServiceName(userId);
+ return defaultServiceName.equals(currentServiceName);
+ }
+
@Override // from AbstractMasterSystemService
protected void dumpLocked(String prefix, PrintWriter pw) {
super.dumpLocked(prefix, pw);
@@ -557,6 +570,10 @@ public final class ContentCaptureManagerService extends
synchronized (mLock) {
final ContentCapturePerUserService service = getServiceForUserLocked(userId);
+ if (!isDefaultServiceLocked(userId) && !isCalledByServiceLocked("startSession()")) {
+ setClientState(result, STATE_DISABLED, /* binder= */ null);
+ return;
+ }
service.startSessionLocked(activityToken, activityPresentationInfo, sessionId,
Binder.getCallingUid(), flags, result);
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 9f0bf17734a6..eca37dae8373 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -1,3 +1,63 @@
+java_library {
+ name: "protolog-common",
+ srcs: [
+ "java/com/android/server/protolog/common/**/*.java",
+ ],
+ host_supported: true,
+}
+
+java_library {
+ name: "services.core.wm.protologgroups",
+ srcs: [
+ "java/com/android/server/wm/ProtoLogGroup.java",
+ ],
+ static_libs: ["protolog-common"],
+}
+
+genrule {
+ name: "services.core.protologsrc",
+ srcs: [":services.core.wm.protologgroups", "java/**/*.java"],
+ tools: ["protologtool"],
+ cmd: "$(location protologtool) transform-protolog-calls " +
+ "--protolog-class com.android.server.protolog.common.ProtoLog " +
+ "--protolog-impl-class com.android.server.protolog.ProtoLogImpl " +
+ "--protolog-cache-class 'com.android.server.protolog.ProtoLog$$Cache' " +
+ "--loggroups-class com.android.server.wm.ProtoLogGroup " +
+ "--loggroups-jar $(location :services.core.wm.protologgroups) " +
+ "--output-srcjar $(out) " +
+ "$(locations java/**/*.java)",
+ out: ["services.core.protolog.srcjar"],
+}
+
+genrule {
+ name: "generate-protolog.json",
+ srcs: [":services.core.wm.protologgroups", "java/**/*.java"],
+ tools: ["protologtool"],
+ cmd: "$(location protologtool) generate-viewer-config " +
+ "--protolog-class com.android.server.protolog.common.ProtoLog " +
+ "--loggroups-class com.android.server.wm.ProtoLogGroup " +
+ "--loggroups-jar $(location :services.core.wm.protologgroups) " +
+ "--viewer-conf $(out) " +
+ "$(locations java/**/*.java)",
+ out: ["services.core.protolog.json"],
+}
+
+genrule {
+ name: "checked-protolog.json",
+ srcs: [
+ ":generate-protolog.json",
+ ":services.core.protolog.json",
+ ],
+ cmd: "cp $(location :generate-protolog.json) $(out) && " +
+ "{ ! (diff $(out) $(location :services.core.protolog.json) | grep -q '^<') || " +
+ "{ echo -e '\\n\\n################################################################\\n#\\n" +
+ "# ERROR: ProtoLog viewer config is stale. To update it, run:\\n#\\n" +
+ "# cp $(location :generate-protolog.json) " +
+ "$(location :services.core.protolog.json)\\n#\\n" +
+ "################################################################\\n\\n' >&2 && false; } }",
+ out: ["services.core.protolog.json"],
+}
+
java_library_static {
name: "services.core.unboosted",
@@ -12,7 +72,7 @@ java_library_static {
],
},
srcs: [
- "java/**/*.java",
+ ":services.core.protologsrc",
":dumpstate_aidl",
":idmap2_aidl",
":installd_aidl",
@@ -34,6 +94,7 @@ java_library_static {
required: [
"gps_debug.conf",
+ "protolog.conf.json.gz",
],
static_libs: [
@@ -48,7 +109,7 @@ java_library_static {
"android.hardware.biometrics.fingerprint-V2.1-java",
"android.hardware.oemlock-V1.0-java",
"android.hardware.tetheroffload.control-V1.0-java",
- "android.hardware.vibrator-V1.0-java",
+ "android.hardware.vibrator-V1.4-java",
"android.hardware.configstore-V1.0-java",
"android.hardware.contexthub-V1.0-java",
"android.hidl.manager-V1.2-java",
@@ -82,3 +143,16 @@ prebuilt_etc {
name: "gps_debug.conf",
src: "java/com/android/server/location/gps_debug.conf",
}
+
+genrule {
+ name: "services.core.json.gz",
+ srcs: [":checked-protolog.json"],
+ out: ["services.core.protolog.json.gz"],
+ cmd: "$(location minigzip) -c < $(in) > $(out)",
+ tools: ["minigzip"],
+}
+
+prebuilt_etc {
+ name: "protolog.conf.json.gz",
+ src: ":services.core.json.gz",
+}
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
new file mode 100644
index 000000000000..dc24cffa54a4
--- /dev/null
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -0,0 +1,812 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.AppIdInt;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.content.pm.PackageManager.ComponentInfoFlags;
+import android.content.pm.PackageManager.PackageInfoFlags;
+import android.content.pm.PackageManager.ResolveInfoFlags;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.util.ArraySet;
+import android.util.SparseArray;
+
+import com.android.server.pm.PackageList;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Package manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class PackageManagerInternal {
+ public static final int PACKAGE_SYSTEM = 0;
+ public static final int PACKAGE_SETUP_WIZARD = 1;
+ public static final int PACKAGE_INSTALLER = 2;
+ public static final int PACKAGE_VERIFIER = 3;
+ public static final int PACKAGE_BROWSER = 4;
+ public static final int PACKAGE_SYSTEM_TEXT_CLASSIFIER = 5;
+ public static final int PACKAGE_PERMISSION_CONTROLLER = 6;
+ public static final int PACKAGE_WELLBEING = 7;
+ public static final int PACKAGE_DOCUMENTER = 8;
+ public static final int PACKAGE_CONFIGURATOR = 9;
+ public static final int PACKAGE_INCIDENT_REPORT_APPROVER = 10;
+ public static final int PACKAGE_APP_PREDICTOR = 11;
+ public static final int PACKAGE_TELEPHONY = 12;
+ public static final int PACKAGE_WIFI = 13;
+ @IntDef(value = {
+ PACKAGE_SYSTEM,
+ PACKAGE_SETUP_WIZARD,
+ PACKAGE_INSTALLER,
+ PACKAGE_VERIFIER,
+ PACKAGE_BROWSER,
+ PACKAGE_SYSTEM_TEXT_CLASSIFIER,
+ PACKAGE_PERMISSION_CONTROLLER,
+ PACKAGE_WELLBEING,
+ PACKAGE_DOCUMENTER,
+ PACKAGE_CONFIGURATOR,
+ PACKAGE_INCIDENT_REPORT_APPROVER,
+ PACKAGE_APP_PREDICTOR,
+ PACKAGE_TELEPHONY,
+ PACKAGE_WIFI,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface KnownPackage {}
+
+ /** Observer called whenever the list of packages changes */
+ public interface PackageListObserver {
+ /** A package was added to the system. */
+ void onPackageAdded(@NonNull String packageName, int uid);
+ /** A package was changed - either installed for a specific user or updated. */
+ default void onPackageChanged(@NonNull String packageName, int uid) {}
+ /** A package was removed from the system. */
+ void onPackageRemoved(@NonNull String packageName, int uid);
+ }
+
+ /**
+ * Called when the package for the default SMS handler changed
+ *
+ * @param packageName the new sms package
+ * @param userId user for which the change was made
+ */
+ public void onDefaultSmsAppChanged(String packageName, int userId) {}
+
+ /**
+ * Called when the package for the default sim call manager changed
+ *
+ * @param packageName the new sms package
+ * @param userId user for which the change was made
+ */
+ public void onDefaultSimCallManagerAppChanged(String packageName, int userId) {}
+
+ /**
+ * Sets a list of apps to keep in PM's internal data structures and as APKs even if no user has
+ * currently installed it. The apps are not preloaded.
+ * @param packageList List of package names to keep cached.
+ */
+ public abstract void setKeepUninstalledPackages(List<String> packageList);
+
+ /**
+ * Gets whether some of the permissions used by this package require a user
+ * review before any of the app components can run.
+ * @param packageName The package name for which to check.
+ * @param userId The user under which to check.
+ * @return True a permissions review is required.
+ */
+ public abstract boolean isPermissionsReviewRequired(String packageName, int userId);
+
+ /**
+ * Retrieve all of the information we know about a particular package/application.
+ * @param filterCallingUid The results will be filtered in the context of this UID instead
+ * of the calling UID.
+ * @see PackageManager#getPackageInfo(String, int)
+ */
+ public abstract PackageInfo getPackageInfo(String packageName,
+ @PackageInfoFlags int flags, int filterCallingUid, int userId);
+
+ /**
+ * Return a List of all application packages that are installed on the
+ * device, for a specific user. If flag GET_UNINSTALLED_PACKAGES has been
+ * set, a list of all applications including those deleted with
+ * {@code DONT_DELETE_DATA} (partially installed apps with data directory)
+ * will be returned.
+ *
+ * @param flags Additional option flags to modify the data returned.
+ * @param userId The user for whom the installed applications are to be
+ * listed
+ * @param callingUid The uid of the original caller app
+ * @return A List of ApplicationInfo objects, one for each installed
+ * application. In the unlikely case there are no installed
+ * packages, an empty list is returned. If flag
+ * {@code MATCH_UNINSTALLED_PACKAGES} is set, the application
+ * information is retrieved from the list of uninstalled
+ * applications (which includes installed applications as well as
+ * applications with data directory i.e. applications which had been
+ * deleted with {@code DONT_DELETE_DATA} flag set).
+ */
+ public abstract List<ApplicationInfo> getInstalledApplications(
+ @ApplicationInfoFlags int flags, @UserIdInt int userId, int callingUid);
+
+ /**
+ * Retrieve launcher extras for a suspended package provided to the system in
+ * {@link PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
+ * PersistableBundle, String)}.
+ *
+ * @param packageName The package for which to return launcher extras.
+ * @param userId The user for which to check.
+ * @return The launcher extras.
+ *
+ * @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
+ * PersistableBundle, String)
+ * @see PackageManager#isPackageSuspended()
+ */
+ public abstract Bundle getSuspendedPackageLauncherExtras(String packageName,
+ int userId);
+
+ /**
+ * Internal api to query the suspended state of a package.
+ * @param packageName The package to check.
+ * @param userId The user id to check for.
+ * @return {@code true} if the package is suspended, {@code false} otherwise.
+ * @see PackageManager#isPackageSuspended(String)
+ */
+ public abstract boolean isPackageSuspended(String packageName, int userId);
+
+ /**
+ * Get the name of the package that suspended the given package. Packages can be suspended by
+ * device administrators or apps holding {@link android.Manifest.permission#MANAGE_USERS} or
+ * {@link android.Manifest.permission#SUSPEND_APPS}.
+ *
+ * @param suspendedPackage The package that has been suspended.
+ * @param userId The user for which to check.
+ * @return Name of the package that suspended the given package. Returns {@code null} if the
+ * given package is not currently suspended and the platform package name - i.e.
+ * {@code "android"} - if the package was suspended by a device admin.
+ */
+ public abstract String getSuspendingPackage(String suspendedPackage, int userId);
+
+ /**
+ * Get the information describing the dialog to be shown to the user when they try to launch a
+ * suspended application.
+ *
+ * @param suspendedPackage The package that has been suspended.
+ * @param suspendingPackage
+ * @param userId The user for which to check.
+ * @return A {@link SuspendDialogInfo} object describing the dialog to be shown.
+ */
+ @Nullable
+ public abstract SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage,
+ String suspendingPackage, int userId);
+
+ /**
+ * Gets any distraction flags set via
+ * {@link PackageManager#setDistractingPackageRestrictions(String[], int)}
+ *
+ * @param packageName
+ * @param userId
+ * @return A bitwise OR of any of the {@link PackageManager.DistractionRestriction}
+ */
+ public abstract @PackageManager.DistractionRestriction int getDistractingPackageRestrictions(
+ String packageName, int userId);
+
+ /**
+ * Do a straight uid lookup for the given package/application in the given user.
+ * @see PackageManager#getPackageUidAsUser(String, int, int)
+ * @return The app's uid, or < 0 if the package was not found in that user
+ */
+ public abstract int getPackageUid(String packageName,
+ @PackageInfoFlags int flags, int userId);
+
+ /**
+ * Retrieve all of the information we know about a particular package/application.
+ * @param filterCallingUid The results will be filtered in the context of this UID instead
+ * of the calling UID.
+ * @see PackageManager#getApplicationInfo(String, int)
+ */
+ public abstract ApplicationInfo getApplicationInfo(String packageName,
+ @ApplicationInfoFlags int flags, int filterCallingUid, int userId);
+
+ /**
+ * Retrieve all of the information we know about a particular activity class.
+ * @param filterCallingUid The results will be filtered in the context of this UID instead
+ * of the calling UID.
+ * @see PackageManager#getActivityInfo(ComponentName, int)
+ */
+ public abstract ActivityInfo getActivityInfo(ComponentName component,
+ @ComponentInfoFlags int flags, int filterCallingUid, int userId);
+
+ /**
+ * Retrieve all activities that can be performed for the given intent.
+ * @param filterCallingUid The results will be filtered in the context of this UID instead
+ * of the calling UID.
+ * @see PackageManager#queryIntentActivities(Intent, int)
+ */
+ public abstract List<ResolveInfo> queryIntentActivities(Intent intent,
+ @ResolveInfoFlags int flags, int filterCallingUid, int userId);
+
+ /**
+ * Retrieve all services that can be performed for the given intent.
+ * @see PackageManager#queryIntentServices(Intent, int)
+ */
+ public abstract List<ResolveInfo> queryIntentServices(
+ Intent intent, int flags, int callingUid, int userId);
+
+ /**
+ * Interface to {@link com.android.server.pm.PackageManagerService#getHomeActivitiesAsUser}.
+ */
+ public abstract ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
+ int userId);
+
+ /**
+ * @return The default home activity component name.
+ */
+ public abstract ComponentName getDefaultHomeActivity(int userId);
+
+ /**
+ * Called by DeviceOwnerManagerService to set the package names of device owner and profile
+ * owners.
+ */
+ public abstract void setDeviceAndProfileOwnerPackages(
+ int deviceOwnerUserId, String deviceOwner, SparseArray<String> profileOwners);
+
+ /**
+ * Returns {@code true} if a given package can't be wiped. Otherwise, returns {@code false}.
+ */
+ public abstract boolean isPackageDataProtected(int userId, String packageName);
+
+ /**
+ * Returns {@code true} if a given package's state is protected, e.g. it cannot be force
+ * stopped, suspended, disabled or hidden. Otherwise, returns {@code false}.
+ */
+ public abstract boolean isPackageStateProtected(String packageName, int userId);
+
+ /**
+ * Returns {@code true} if a given package is installed as ephemeral. Otherwise, returns
+ * {@code false}.
+ */
+ public abstract boolean isPackageEphemeral(int userId, String packageName);
+
+ /**
+ * Gets whether the package was ever launched.
+ * @param packageName The package name.
+ * @param userId The user for which to check.
+ * @return Whether was launched.
+ * @throws IllegalArgumentException if the package is not found
+ */
+ public abstract boolean wasPackageEverLaunched(String packageName, int userId);
+
+ /**
+ * Retrieve the official name associated with a uid. This name is
+ * guaranteed to never change, though it is possible for the underlying
+ * uid to be changed. That is, if you are storing information about
+ * uids in persistent storage, you should use the string returned
+ * by this function instead of the raw uid.
+ *
+ * @param uid The uid for which you would like to retrieve a name.
+ * @return Returns a unique name for the given uid, or null if the
+ * uid is not currently assigned.
+ */
+ public abstract String getNameForUid(int uid);
+
+ /**
+ * Marks a package as installed (or not installed) for a given user.
+ *
+ * @param pkg the package whose installation is to be set
+ * @param userId the user for whom to set it
+ * @param installed the new installed state
+ * @return true if the installed state changed as a result
+ */
+ public abstract boolean setInstalled(PackageParser.Package pkg,
+ @UserIdInt int userId, boolean installed);
+
+ /**
+ * Request to perform the second phase of ephemeral resolution.
+ * @param responseObj The response of the first phase of ephemeral resolution
+ * @param origIntent The original intent that triggered ephemeral resolution
+ * @param resolvedType The resolved type of the intent
+ * @param callingPackage The name of the package requesting the ephemeral application
+ * @param verificationBundle Optional bundle to pass to the installer for additional
+ * verification
+ * @param userId The ID of the user that triggered ephemeral resolution
+ */
+ public abstract void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
+ Intent origIntent, String resolvedType, String callingPackage,
+ Bundle verificationBundle, int userId);
+
+ /**
+ * Grants implicit access based on an interaction between two apps. This grants the target app
+ * access to the calling application's package metadata.
+ * <p>
+ * When an application explicitly tries to interact with another application [via an
+ * activity, service or provider that is either declared in the caller's
+ * manifest via the {@code <queries>} tag or has been exposed via the target apps manifest using
+ * the {@code visibleToInstantApp} attribute], the target application must be able to see
+ * metadata about the calling app. If the calling application uses an implicit intent [ie
+ * action VIEW, category BROWSABLE], it remains hidden from the launched app.
+ * <p>
+ * @param userId the user
+ * @param intent the intent that triggered the grant
+ * @param callingUid The uid of the calling application
+ * @param targetAppId The app ID of the target application
+ */
+ public abstract void grantImplicitAccess(
+ @UserIdInt int userId, Intent intent, int callingUid,
+ @AppIdInt int targetAppId);
+
+ public abstract boolean isInstantAppInstallerComponent(ComponentName component);
+ /**
+ * Prunes instant apps and state associated with uninstalled
+ * instant apps according to the current platform policy.
+ */
+ public abstract void pruneInstantApps();
+
+ /**
+ * @return The SetupWizard package name.
+ */
+ public abstract String getSetupWizardPackageName();
+
+ public interface ExternalSourcesPolicy {
+
+ int USER_TRUSTED = 0; // User has trusted the package to install apps
+ int USER_BLOCKED = 1; // User has blocked the package to install apps
+ int USER_DEFAULT = 2; // Default code to use when user response is unavailable
+
+ /**
+ * Checks the user preference for whether a package is trusted to request installs through
+ * package installer
+ *
+ * @param packageName The package to check for
+ * @param uid the uid in which the package is running
+ * @return {@link #USER_TRUSTED} if the user has trusted the package, {@link #USER_BLOCKED}
+ * if user has blocked requests from the package, {@link #USER_DEFAULT} if the user response
+ * is not yet available
+ */
+ int getPackageTrustedToInstallApps(String packageName, int uid);
+ }
+
+ public abstract void setExternalSourcesPolicy(ExternalSourcesPolicy policy);
+
+ /**
+ * Return true if the given package is a persistent app process.
+ */
+ public abstract boolean isPackagePersistent(String packageName);
+
+ /**
+ * Returns whether or not the given package represents a legacy system application released
+ * prior to runtime permissions.
+ */
+ public abstract boolean isLegacySystemApp(PackageParser.Package pkg);
+
+ /**
+ * Get all overlay packages for a user.
+ * @param userId The user for which to get the overlays.
+ * @return A list of overlay packages. An empty list is returned if the
+ * user has no installed overlay packages.
+ */
+ public abstract List<PackageInfo> getOverlayPackages(int userId);
+
+ /**
+ * Get the names of all target packages for a user.
+ * @param userId The user for which to get the package names.
+ * @return A list of target package names. This list includes the "android" package.
+ */
+ public abstract List<String> getTargetPackageNames(int userId);
+
+ /**
+ * Set which overlay to use for a package.
+ * @param userId The user for which to update the overlays.
+ * @param targetPackageName The package name of the package for which to update the overlays.
+ * @param overlayPackageNames The complete list of overlay packages that should be enabled for
+ * the target. Previously enabled overlays not specified in the list
+ * will be disabled. Pass in null or an empty list to disable
+ * all overlays. The order of the items is significant if several
+ * overlays modify the same resource.
+ * @return true if all packages names were known by the package manager, false otherwise
+ */
+ public abstract boolean setEnabledOverlayPackages(int userId, String targetPackageName,
+ List<String> overlayPackageNames);
+
+ /**
+ * Resolves an activity intent, allowing instant apps to be resolved.
+ */
+ public abstract ResolveInfo resolveIntent(Intent intent, String resolvedType,
+ int flags, int userId, boolean resolveForStart, int filterCallingUid);
+
+ /**
+ * Resolves a service intent, allowing instant apps to be resolved.
+ */
+ public abstract ResolveInfo resolveService(Intent intent, String resolvedType,
+ int flags, int userId, int callingUid);
+
+ /**
+ * Resolves a content provider intent.
+ */
+ public abstract ProviderInfo resolveContentProvider(String name, int flags, int userId);
+
+ /**
+ * Track the creator of a new isolated uid.
+ * @param isolatedUid The newly created isolated uid.
+ * @param ownerUid The uid of the app that created the isolated process.
+ */
+ public abstract void addIsolatedUid(int isolatedUid, int ownerUid);
+
+ /**
+ * Track removal of an isolated uid.
+ * @param isolatedUid isolated uid that is no longer being used.
+ */
+ public abstract void removeIsolatedUid(int isolatedUid);
+
+ /**
+ * Return the taget SDK version for the app with the given UID.
+ */
+ public abstract int getUidTargetSdkVersion(int uid);
+
+ /**
+ * Return the taget SDK version for the app with the given package name.
+ */
+ public abstract int getPackageTargetSdkVersion(String packageName);
+
+ /** Whether the binder caller can access instant apps. */
+ public abstract boolean canAccessInstantApps(int callingUid, int userId);
+
+ /** Whether the binder caller can access the given component. */
+ public abstract boolean canAccessComponent(int callingUid, ComponentName component, int userId);
+
+ /**
+ * Returns {@code true} if a given package has instant application meta-data.
+ * Otherwise, returns {@code false}. Meta-data is state (eg. cookie, app icon, etc)
+ * associated with an instant app. It may be kept after the instant app has been uninstalled.
+ */
+ public abstract boolean hasInstantApplicationMetadata(String packageName, int userId);
+
+ /**
+ * Updates a package last used time.
+ */
+ public abstract void notifyPackageUse(String packageName, int reason);
+
+ /**
+ * Returns a package object for the given package name.
+ */
+ public abstract @Nullable PackageParser.Package getPackage(@NonNull String packageName);
+
+ /**
+ * Returns a package for the given UID. If the UID is part of a shared user ID, one
+ * of the packages will be chosen to be returned.
+ */
+ public abstract @Nullable PackageParser.Package getPackage(int uid);
+
+ /**
+ * Returns a list without a change observer.
+ *
+ * @see #getPackageList(PackageListObserver)
+ */
+ public @NonNull PackageList getPackageList() {
+ return getPackageList(null);
+ }
+
+ /**
+ * Returns the list of packages installed at the time of the method call.
+ * <p>The given observer is notified when the list of installed packages
+ * changes [eg. a package was installed or uninstalled]. It will not be
+ * notified if a package is updated.
+ * <p>The package list will not be updated automatically as packages are
+ * installed / uninstalled. Any changes must be handled within the observer.
+ */
+ public abstract @NonNull PackageList getPackageList(@Nullable PackageListObserver observer);
+
+ /**
+ * Removes the observer.
+ * <p>Generally not needed. {@link #getPackageList(PackageListObserver)} will automatically
+ * remove the observer.
+ * <p>Does nothing if the observer isn't currently registered.
+ * <p>Observers are notified asynchronously and it's possible for an observer to be
+ * invoked after its been removed.
+ */
+ public abstract void removePackageListObserver(@NonNull PackageListObserver observer);
+
+ /**
+ * Returns a package object for the disabled system package name.
+ */
+ public abstract @Nullable PackageParser.Package getDisabledSystemPackage(
+ @NonNull String packageName);
+
+ /**
+ * Returns the package name for the disabled system package.
+ *
+ * This is equivalent to
+ * {@link #getDisabledSystemPackage(String)}.{@link PackageParser.Package#packageName}
+ */
+ public abstract @Nullable String getDisabledSystemPackageName(@NonNull String packageName);
+
+ /**
+ * Returns whether or not the component is the resolver activity.
+ */
+ public abstract boolean isResolveActivityComponent(@NonNull ComponentInfo component);
+
+
+ /**
+ * Returns a list of package names for a known package
+ */
+ public abstract @NonNull String[] getKnownPackageNames(
+ @KnownPackage int knownPackage, int userId);
+
+ /**
+ * Returns whether the package is an instant app.
+ */
+ public abstract boolean isInstantApp(String packageName, int userId);
+
+ /**
+ * Returns whether the package is an instant app.
+ */
+ public abstract @Nullable String getInstantAppPackageName(int uid);
+
+ /**
+ * Returns whether or not access to the application should be filtered.
+ * <p>
+ * Access may be limited based upon whether the calling or target applications
+ * are instant applications.
+ *
+ * @see #canAccessInstantApps
+ */
+ public abstract boolean filterAppAccess(
+ @NonNull PackageParser.Package pkg, int callingUid, int userId);
+
+ /**
+ * Returns whether or not access to the application should be filtered.
+ *
+ * @see #filterAppAccess(android.content.pm.PackageParser.Package, int, int)
+ */
+ public abstract boolean filterAppAccess(
+ @NonNull String packageName, int callingUid, int userId);
+
+ /** Returns whether the given package was signed by the platform */
+ public abstract boolean isPlatformSigned(String pkg);
+
+ /**
+ * Returns true if it's still safe to restore data backed up from this app's version
+ * that was signed with restoringFromSigHash.
+ */
+ public abstract boolean isDataRestoreSafe(@NonNull byte[] restoringFromSigHash,
+ @NonNull String packageName);
+
+ /**
+ * Returns true if it's still safe to restore data backed up from this app's version
+ * that was signed with restoringFromSig.
+ */
+ public abstract boolean isDataRestoreSafe(@NonNull Signature restoringFromSig,
+ @NonNull String packageName);
+
+ /**
+ * Returns {@code true} if the the signing information for {@code clientUid} is sufficient
+ * to gain access gated by {@code capability}. This can happen if the two UIDs have the
+ * same signing information, if the signing information {@code clientUid} indicates that
+ * it has the signing certificate for {@code serverUid} in its signing history (if it was
+ * previously signed by it), or if the signing certificate for {@code clientUid} is in the
+ * signing history for {@code serverUid} and with the {@code capability} specified.
+ */
+ public abstract boolean hasSignatureCapability(int serverUid, int clientUid,
+ @PackageParser.SigningDetails.CertCapabilities int capability);
+
+ /**
+ * Get appIds of all available apps which specified android:sharedUserId in the manifest.
+ *
+ * @return a SparseArray mapping from appId to it's sharedUserId.
+ */
+ public abstract SparseArray<String> getAppsWithSharedUserIds();
+
+ /**
+ * Get the value of attribute android:sharedUserId for the given packageName if specified,
+ * otherwise {@code null}.
+ */
+ public abstract String getSharedUserIdForPackage(@NonNull String packageName);
+
+ /**
+ * Get all packages which specified the given sharedUserId as android:sharedUserId attribute
+ * or an empty array if no package specified it.
+ */
+ public abstract String[] getPackagesForSharedUserId(@NonNull String sharedUserId, int userId);
+
+ /**
+ * Return if device is currently in a "core" boot environment, typically
+ * used to support full-disk encryption. Only apps marked with
+ * {@code coreApp} attribute are available.
+ */
+ public abstract boolean isOnlyCoreApps();
+
+ /**
+ * Make a best-effort attempt to provide the requested free disk space by
+ * deleting cached files.
+ *
+ * @throws IOException if the request was unable to be fulfilled.
+ */
+ public abstract void freeStorage(String volumeUuid, long bytes, int storageFlags)
+ throws IOException;
+
+ /** Returns {@code true} if the specified component is enabled and matches the given flags. */
+ public abstract boolean isEnabledAndMatches(@NonNull ComponentInfo info, int flags, int userId);
+
+ /** Returns {@code true} if the given user requires extra badging for icons. */
+ public abstract boolean userNeedsBadging(int userId);
+
+ /**
+ * Perform the given action for each package.
+ * Note that packages lock will be held while performin the actions.
+ *
+ * @param actionLocked action to be performed
+ */
+ public abstract void forEachPackage(Consumer<PackageParser.Package> actionLocked);
+
+ /**
+ * Perform the given action for each installed package for a user.
+ * Note that packages lock will be held while performin the actions.
+ */
+ public abstract void forEachInstalledPackage(
+ @NonNull Consumer<PackageParser.Package> actionLocked, @UserIdInt int userId);
+
+ /** Returns the list of enabled components */
+ public abstract ArraySet<String> getEnabledComponents(String packageName, int userId);
+
+ /** Returns the list of disabled components */
+ public abstract ArraySet<String> getDisabledComponents(String packageName, int userId);
+
+ /** Returns whether the given package is enabled for the given user */
+ public abstract @PackageManager.EnabledState int getApplicationEnabledState(
+ String packageName, int userId);
+
+ /**
+ * Extra field name for the token of a request to enable rollback for a
+ * package.
+ */
+ public static final String EXTRA_ENABLE_ROLLBACK_TOKEN =
+ "android.content.pm.extra.ENABLE_ROLLBACK_TOKEN";
+
+ /**
+ * Extra field name for the installFlags of a request to enable rollback
+ * for a package.
+ */
+ public static final String EXTRA_ENABLE_ROLLBACK_INSTALL_FLAGS =
+ "android.content.pm.extra.ENABLE_ROLLBACK_INSTALL_FLAGS";
+
+ /**
+ * Extra field name for the user id an install is associated with when
+ * enabling rollback.
+ */
+ public static final String EXTRA_ENABLE_ROLLBACK_USER =
+ "android.content.pm.extra.ENABLE_ROLLBACK_USER";
+
+ /**
+ * Used as the {@code enableRollbackCode} argument for
+ * {@link PackageManagerInternal#setEnableRollbackCode} to indicate that
+ * enabling rollback succeeded.
+ */
+ public static final int ENABLE_ROLLBACK_SUCCEEDED = 1;
+
+ /**
+ * Used as the {@code enableRollbackCode} argument for
+ * {@link PackageManagerInternal#setEnableRollbackCode} to indicate that
+ * enabling rollback failed.
+ */
+ public static final int ENABLE_ROLLBACK_FAILED = -1;
+
+ /**
+ * Allows the rollback manager listening to the
+ * {@link Intent#ACTION_PACKAGE_ENABLE_ROLLBACK enable rollback broadcast}
+ * to respond to the package manager. The response must include the
+ * {@code enableRollbackCode} which is one of
+ * {@link PackageManager#ENABLE_ROLLBACK_SUCCEEDED} or
+ * {@link PackageManager#ENABLE_ROLLBACK_FAILED}.
+ *
+ * @param token pending package identifier as passed via the
+ * {@link PackageManager#EXTRA_ENABLE_ROLLBACK_TOKEN} Intent extra.
+ * @param enableRollbackCode the status code result of enabling rollback
+ * @throws SecurityException if the caller does not have the
+ * PACKAGE_ROLLBACK_AGENT permission.
+ */
+ public abstract void setEnableRollbackCode(int token, int enableRollbackCode);
+
+ /**
+ * Ask the package manager to compile layouts in the given package.
+ */
+ public abstract boolean compileLayouts(String packageName);
+
+ /*
+ * Inform the package manager that the pending package install identified by
+ * {@code token} can be completed.
+ */
+ public abstract void finishPackageInstall(int token, boolean didLaunch);
+
+ /**
+ * Remove the default browser stored in the legacy package settings.
+ *
+ * @param userId the user id
+ *
+ * @return the package name of the default browser, or {@code null} if none
+ */
+ @Nullable
+ public abstract String removeLegacyDefaultBrowserPackageName(int userId);
+
+ /**
+ * Returns {@code true} if given {@code packageName} is an apex package.
+ */
+ public abstract boolean isApexPackage(String packageName);
+
+ /**
+ * Uninstalls given {@code packageName}.
+ *
+ * @param packageName apex package to uninstall.
+ * @param versionCode version of a package to uninstall.
+ * @param userId user to uninstall apex package for. Must be
+ * {@link android.os.UserHandle#USER_ALL}, otherwise failure will be reported.
+ * @param intentSender a {@link IntentSender} to send result of an uninstall to.
+ */
+ public abstract void uninstallApex(String packageName, long versionCode, int userId,
+ IntentSender intentSender);
+
+ /**
+ * Get fingerprint of build that updated the runtime permissions for a user.
+ *
+ * @param userId The user to update
+ * @param fingerPrint The fingerprint to set
+ */
+ public abstract void setRuntimePermissionsFingerPrint(@NonNull String fingerPrint,
+ @UserIdInt int userId);
+
+ /**
+ * Migrates legacy obb data to its new location.
+ */
+ public abstract void migrateLegacyObbData();
+
+ /**
+ * Writes all package manager settings to disk. If {@code async} is {@code true}, the
+ * settings are written at some point in the future. Otherwise, the call blocks until
+ * the settings have been written.
+ */
+ public abstract void writeSettings(boolean async);
+
+ /**
+ * Writes all permission settings for the given set of users to disk. If {@code async}
+ * is {@code true}, the settings are written at some point in the future. Otherwise,
+ * the call blocks until the settings have been written.
+ */
+ public abstract void writePermissionSettings(@NonNull @UserIdInt int[] userIds, boolean async);
+
+ /**
+ * Returns {@code true} if the caller is the installer of record for the given package.
+ * Otherwise, {@code false}.
+ */
+ public abstract boolean isCallerInstallerOfRecord(
+ @NonNull PackageParser.Package pkg, int callingUid);
+
+ /** Returns whether or not default runtime permissions are granted for the given user */
+ public abstract boolean areDefaultRuntimePermissionsGranted(@UserIdInt int userId);
+
+ /** Sets the enforcement of reading external storage */
+ public abstract void setReadExternalStorageEnforced(boolean enforced);
+}
diff --git a/services/core/java/android/os/UserManagerInternal.java b/services/core/java/android/os/UserManagerInternal.java
new file mode 100644
index 000000000000..e5f8b49c3f0c
--- /dev/null
+++ b/services/core/java/android/os/UserManagerInternal.java
@@ -0,0 +1,250 @@
+/*
+ * 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.graphics.Bitmap;
+
+/**
+ * @hide Only for use within the system server.
+ */
+public abstract class UserManagerInternal {
+ public static final int CAMERA_NOT_DISABLED = 0;
+ public static final int CAMERA_DISABLED_LOCALLY = 1;
+ public static final int CAMERA_DISABLED_GLOBALLY = 2;
+
+ public interface UserRestrictionsListener {
+ /**
+ * Called when a user restriction changes.
+ *
+ * @param userId target user id
+ * @param newRestrictions new user restrictions
+ * @param prevRestrictions user restrictions that were previously set
+ */
+ void onUserRestrictionsChanged(int userId, Bundle newRestrictions, Bundle prevRestrictions);
+ }
+
+ /**
+ * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to set
+ * restrictions enforced by the user.
+ *
+ * @param userId target user id for the local restrictions.
+ * @param restrictions a bundle of user restrictions.
+ * @param isDeviceOwner whether {@code userId} corresponds to device owner user id.
+ * @param cameraRestrictionScope is camera disabled and if so what is the scope of restriction.
+ * Should be one of {@link #CAMERA_NOT_DISABLED}, {@link #CAMERA_DISABLED_LOCALLY} or
+ * {@link #CAMERA_DISABLED_GLOBALLY}
+ */
+ public abstract void setDevicePolicyUserRestrictions(int userId, @Nullable Bundle restrictions,
+ boolean isDeviceOwner, int cameraRestrictionScope);
+
+ /**
+ * Returns the "base" user restrictions.
+ *
+ * Used by {@link com.android.server.devicepolicy.DevicePolicyManagerService} for upgrading
+ * from MNC.
+ */
+ public abstract Bundle getBaseUserRestrictions(int userId);
+
+ /**
+ * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} for upgrading
+ * from MNC.
+ */
+ public abstract void setBaseUserRestrictionsByDpmsForMigration(int userId,
+ Bundle baseRestrictions);
+
+ /** Return a user restriction. */
+ public abstract boolean getUserRestriction(int userId, String key);
+
+ /** Adds a listener to user restriction changes. */
+ public abstract void addUserRestrictionsListener(UserRestrictionsListener listener);
+
+ /** Remove a {@link UserRestrictionsListener}. */
+ public abstract void removeUserRestrictionsListener(UserRestrictionsListener listener);
+
+ /**
+ * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to update
+ * whether the device is managed by device owner.
+ */
+ public abstract void setDeviceManaged(boolean isManaged);
+
+ /**
+ * Returns whether the device is managed by device owner.
+ */
+ public abstract boolean isDeviceManaged();
+
+ /**
+ * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to update
+ * whether the user is managed by profile owner.
+ */
+ public abstract void setUserManaged(int userId, boolean isManaged);
+
+ /**
+ * whether a profile owner manages this user.
+ */
+ public abstract boolean isUserManaged(int userId);
+
+ /**
+ * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to omit
+ * restriction check, because DevicePolicyManager must always be able to set user icon
+ * regardless of any restriction.
+ * Also called by {@link com.android.server.pm.UserManagerService} because the logic of setting
+ * the icon is in this method.
+ */
+ public abstract void setUserIcon(int userId, Bitmap bitmap);
+
+ /**
+ * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to inform the
+ * user manager whether all users should be created ephemeral.
+ */
+ public abstract void setForceEphemeralUsers(boolean forceEphemeralUsers);
+
+ /**
+ * Switches to the system user and deletes all other users.
+ *
+ * <p>Called by the {@link com.android.server.devicepolicy.DevicePolicyManagerService} when
+ * the force-ephemeral-users policy is toggled on to make sure there are no pre-existing
+ * non-ephemeral users left.
+ */
+ public abstract void removeAllUsers();
+
+ /**
+ * Called by the activity manager when the ephemeral user goes to background and its removal
+ * starts as a result.
+ *
+ * <p>It marks the ephemeral user as disabled in order to prevent it from being re-entered
+ * before its removal finishes.
+ *
+ * @param userId the ID of the ephemeral user.
+ */
+ public abstract void onEphemeralUserStop(int userId);
+
+ /**
+ * Same as UserManager.createUser(), but bypasses the check for
+ * {@link UserManager#DISALLOW_ADD_USER} and {@link UserManager#DISALLOW_ADD_MANAGED_PROFILE}
+ *
+ * <p>Called by the {@link com.android.server.devicepolicy.DevicePolicyManagerService} when
+ * createAndManageUser is called by the device owner.
+ */
+ public abstract UserInfo createUserEvenWhenDisallowed(String name, int flags,
+ String[] disallowedPackages);
+
+ /**
+ * Same as {@link UserManager#removeUser(int userId)}, but bypasses the check for
+ * {@link UserManager#DISALLOW_REMOVE_USER} and
+ * {@link UserManager#DISALLOW_REMOVE_MANAGED_PROFILE} and does not require the
+ * {@link android.Manifest.permission#MANAGE_USERS} permission.
+ */
+ public abstract boolean removeUserEvenWhenDisallowed(int userId);
+
+ /**
+ * Return whether the given user is running in an
+ * {@code UserState.STATE_RUNNING_UNLOCKING} or
+ * {@code UserState.STATE_RUNNING_UNLOCKED} state.
+ */
+ public abstract boolean isUserUnlockingOrUnlocked(int userId);
+
+ /**
+ * Return whether the given user is running in an
+ * {@code UserState.STATE_RUNNING_UNLOCKED} state.
+ */
+ public abstract boolean isUserUnlocked(int userId);
+
+ /**
+ * Returns whether the given user is running
+ */
+ public abstract boolean isUserRunning(int userId);
+
+ /**
+ * Returns whether the given user is initialized
+ */
+ public abstract boolean isUserInitialized(int userId);
+
+ /**
+ * Returns whether the given user exists
+ */
+ public abstract boolean exists(int userId);
+
+ /**
+ * Set user's running state
+ */
+ public abstract void setUserState(int userId, int userState);
+
+ /**
+ * Remove user's running state
+ */
+ public abstract void removeUserState(int userId);
+
+ /**
+ * Returns an array of user ids. This array is cached in UserManagerService and passed as a
+ * reference, so do not modify the returned array.
+ *
+ * @return the array of user ids.
+ */
+ public abstract int[] getUserIds();
+
+ /**
+ * Checks if the {@code callingUserId} and {@code targetUserId} are same or in same group
+ * and that the {@code callingUserId} is not a managed profile and
+ * {@code targetUserId} is enabled.
+ *
+ * @return TRUE if the {@code callingUserId} can access {@code targetUserId}. FALSE
+ * otherwise
+ *
+ * @throws SecurityException if the calling user and {@code targetUser} are not in the same
+ * group and {@code throwSecurityException} is true, otherwise if will simply return false.
+ */
+ public abstract boolean isProfileAccessible(int callingUserId, int targetUserId,
+ String debugMsg, boolean throwSecurityException);
+
+ /**
+ * If {@code userId} is of a managed profile, return the parent user ID. Otherwise return
+ * itself.
+ */
+ public abstract int getProfileParentId(int userId);
+
+ /**
+ * Checks whether changing a setting to a value is prohibited by the corresponding user
+ * restriction.
+ *
+ * <p>See also {@link com.android.server.pm.UserRestrictionsUtils#applyUserRestriction(
+ * Context, int, String, boolean)}, which should be in sync with this method.
+ *
+ * @return {@code true} if the change is prohibited, {@code false} if the change is allowed.
+ *
+ * @hide
+ */
+ public abstract boolean isSettingRestrictedForUser(String setting, int userId, String value,
+ int callingUid);
+
+ /** @return a specific user restriction that's in effect currently. */
+ public abstract boolean hasUserRestriction(String restriction, int userId);
+
+ /**
+ * Gets an {@link UserInfo} for the given {@code userId}, or {@code null} if not
+ * found.
+ */
+ public abstract @Nullable UserInfo getUserInfo(@UserIdInt int userId);
+
+ /**
+ * Gets all {@link UserInfo UserInfos}.
+ */
+ public abstract @NonNull UserInfo[] getUserInfos();
+}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 9a97ddb3e3a7..b41e95fee15c 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -47,6 +47,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionInfo;
import android.database.ContentObserver;
import android.net.Uri;
+import android.os.BatteryManager;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -1564,6 +1565,7 @@ class AlarmManagerService extends SystemService {
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, UserHandle.ALL);
mClockReceiver = mInjector.getClockReceiver(this);
+ new ChargingReceiver();
new InteractiveStateReceiver();
new UninstallReceiver();
@@ -4148,7 +4150,7 @@ class AlarmManagerService extends SystemService {
public static final int LISTENER_TIMEOUT = 3;
public static final int REPORT_ALARMS_ACTIVE = 4;
public static final int APP_STANDBY_BUCKET_CHANGED = 5;
- public static final int APP_STANDBY_PAROLE_CHANGED = 6;
+ public static final int CHARGING_STATUS_CHANGED = 6;
public static final int REMOVE_FOR_STOPPED = 7;
public static final int UNREGISTER_CANCEL_LISTENER = 8;
@@ -4206,7 +4208,7 @@ class AlarmManagerService extends SystemService {
}
break;
- case APP_STANDBY_PAROLE_CHANGED:
+ case CHARGING_STATUS_CHANGED:
synchronized (mLock) {
mAppStandbyParole = (Boolean) msg.obj;
if (reorderAlarmsBasedOnStandbyBuckets(null)) {
@@ -4247,6 +4249,37 @@ class AlarmManagerService extends SystemService {
}
}
+ @VisibleForTesting
+ class ChargingReceiver extends BroadcastReceiver {
+ ChargingReceiver() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BatteryManager.ACTION_CHARGING);
+ filter.addAction(BatteryManager.ACTION_DISCHARGING);
+ getContext().registerReceiver(this, filter);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ final boolean charging;
+ if (BatteryManager.ACTION_CHARGING.equals(action)) {
+ if (DEBUG_STANDBY) {
+ Slog.d(TAG, "Device is charging.");
+ }
+ charging = true;
+ } else {
+ if (DEBUG_STANDBY) {
+ Slog.d(TAG, "Disconnected from power.");
+ }
+ charging = false;
+ }
+ mHandler.removeMessages(AlarmHandler.CHARGING_STATUS_CHANGED);
+ mHandler.obtainMessage(AlarmHandler.CHARGING_STATUS_CHANGED, charging)
+ .sendToTarget();
+ }
+ }
+
+ @VisibleForTesting
class ClockReceiver extends BroadcastReceiver {
public ClockReceiver() {
IntentFilter filter = new IntentFilter();
@@ -4429,7 +4462,7 @@ class AlarmManagerService extends SystemService {
@Override public void onUidCachedChanged(int uid, boolean cached) {
}
- };
+ }
/**
* Tracking of app assignments to standby buckets
@@ -4447,18 +4480,7 @@ class AlarmManagerService extends SystemService {
mHandler.obtainMessage(AlarmHandler.APP_STANDBY_BUCKET_CHANGED, userId, -1, packageName)
.sendToTarget();
}
-
- @Override
- public void onParoleStateChanged(boolean isParoleOn) {
- if (DEBUG_STANDBY) {
- Slog.d(TAG, "Global parole state now " + (isParoleOn ? "ON" : "OFF"));
- }
- mHandler.removeMessages(AlarmHandler.APP_STANDBY_BUCKET_CHANGED);
- mHandler.removeMessages(AlarmHandler.APP_STANDBY_PAROLE_CHANGED);
- mHandler.obtainMessage(AlarmHandler.APP_STANDBY_PAROLE_CHANGED,
- Boolean.valueOf(isParoleOn)).sendToTarget();
- }
- };
+ }
private final Listener mForceAppStandbyListener = new Listener() {
@Override
diff --git a/services/core/java/com/android/server/AnimationThread.java b/services/core/java/com/android/server/AnimationThread.java
index c607b1e058bc..fad743eafdaa 100644
--- a/services/core/java/com/android/server/AnimationThread.java
+++ b/services/core/java/com/android/server/AnimationThread.java
@@ -64,7 +64,7 @@ public final class AnimationThread extends ServiceThread {
*/
@VisibleForTesting
public static void dispose() {
- synchronized (DisplayThread.class) {
+ synchronized (AnimationThread.class) {
if (sInstance == null) {
return;
}
diff --git a/services/core/java/com/android/server/AppStateTracker.java b/services/core/java/com/android/server/AppStateTracker.java
index 2c67c5046c6d..da760b6f7ffd 100644
--- a/services/core/java/com/android/server/AppStateTracker.java
+++ b/services/core/java/com/android/server/AppStateTracker.java
@@ -71,8 +71,7 @@ import java.util.List;
* - Temporary power save whitelist
* - Global "force all apps standby" mode enforced by battery saver.
*
- * Test:
- atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
+ * Test: atest com.android.server.AppStateTrackerTest
*/
public class AppStateTracker {
private static final String TAG = "AppStateTracker";
@@ -710,10 +709,6 @@ public class AppStateTracker {
mHandler.notifyExemptChanged();
}
}
-
- @Override
- public void onParoleStateChanged(boolean isParoleOn) {
- }
}
private Listener[] cloneListeners() {
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index d18b4f64f039..a33fcd557369 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -720,6 +720,8 @@ public final class BatteryService extends SystemService {
event.putInt(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
event.putInt(BatteryManager.EXTRA_PLUGGED, mPlugType);
event.putInt(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltage);
+ event.putInt(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperature);
+ event.putInt(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounter);
event.putLong(BatteryManager.EXTRA_EVENT_TIMESTAMP, now);
boolean queueWasEmpty = mBatteryLevelsEventQueue.isEmpty();
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 42ade4644388..ee8e3f14ed8c 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -26,6 +26,7 @@ import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.ConnectivityManager.isNetworkTypeValid;
+import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
@@ -502,7 +503,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
* arg1 = One of the NETWORK_TESTED_RESULT_* constants.
* arg2 = NetID.
*/
- public static final int EVENT_NETWORK_TESTED = 41;
+ private static final int EVENT_NETWORK_TESTED = 41;
/**
* Event for NetworkMonitor/NetworkAgentInfo to inform ConnectivityService that the private DNS
@@ -510,7 +511,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
* obj = PrivateDnsConfig
* arg2 = netid
*/
- public static final int EVENT_PRIVATE_DNS_CONFIG_RESOLVED = 42;
+ private static final int EVENT_PRIVATE_DNS_CONFIG_RESOLVED = 42;
/**
* Request ConnectivityService display provisioning notification.
@@ -518,12 +519,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
* arg2 = NetID.
* obj = Intent to be launched when notification selected by user, null if !arg1.
*/
- public static final int EVENT_PROVISIONING_NOTIFICATION = 43;
+ private static final int EVENT_PROVISIONING_NOTIFICATION = 43;
/**
* This event can handle dismissing notification by given network id.
*/
- public static final int EVENT_TIMEOUT_NOTIFICATION = 44;
+ private static final int EVENT_TIMEOUT_NOTIFICATION = 44;
/**
* Used to specify whether a network should be used even if connectivity is partial.
@@ -535,16 +536,25 @@ public class ConnectivityService extends IConnectivityManager.Stub
private static final int EVENT_SET_ACCEPT_PARTIAL_CONNECTIVITY = 45;
/**
+ * Event for NetworkMonitor to inform ConnectivityService that the probe status has changed.
+ * Both of the arguments are bitmasks, and the value of bits come from
+ * INetworkMonitor.NETWORK_VALIDATION_PROBE_*.
+ * arg1 = A bitmask to describe which probes are completed.
+ * arg2 = A bitmask to describe which probes are successful.
+ */
+ public static final int EVENT_PROBE_STATUS_CHANGED = 46;
+
+ /**
* Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
* should be shown.
*/
- public static final int PROVISIONING_NOTIFICATION_SHOW = 1;
+ private static final int PROVISIONING_NOTIFICATION_SHOW = 1;
/**
* Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
* should be hidden.
*/
- public static final int PROVISIONING_NOTIFICATION_HIDE = 0;
+ private static final int PROVISIONING_NOTIFICATION_HIDE = 0;
private static final int EVENT_UPDATE_TCP_BUFFER_FOR_5G = 160;
@@ -605,6 +615,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
// the set of network types that can only be enabled by system/sig apps
private List mProtectedNetworks;
+ private Set<String> mWolSupportedInterfaces;
+
private TelephonyManager mTelephonyManager;
private SubscriptionManager mSubscriptionManager;
@@ -1085,6 +1097,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
+ mWolSupportedInterfaces = new ArraySet(
+ mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_wakeonlan_supported_interfaces));
+
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mTethering = deps.makeTethering(mContext, mNMS, mStatsService, mPolicyManager,
@@ -1173,7 +1189,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
final NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
- netCap.addCapability(NET_CAPABILITY_NOT_RESTRICTED);
netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
netCap.setSingleUid(uid);
return netCap;
@@ -1183,7 +1198,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
int transportType, NetworkRequest.Type type) {
final NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
- netCap.addCapability(NET_CAPABILITY_NOT_RESTRICTED);
if (transportType > -1) {
netCap.addTransportType(transportType);
}
@@ -1968,7 +1982,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- return mPolicyManagerInternal.isUidNetworkingBlocked(uid, uidRules,
+ return NetworkPolicyManagerInternal.isUidNetworkingBlocked(uid, uidRules,
isNetworkMetered, isBackgroundRestricted);
}
@@ -2234,7 +2248,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
final String iface = networkAgent.linkProperties.getInterfaceName();
final int timeout;
- int type = ConnectivityManager.TYPE_NONE;
+ final int type;
if (networkAgent.networkCapabilities.hasTransport(
NetworkCapabilities.TRANSPORT_CELLULAR)) {
@@ -2249,11 +2263,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
15);
type = ConnectivityManager.TYPE_WIFI;
} else {
- // do not track any other networks
- timeout = 0;
+ return; // do not track any other networks
}
- if (timeout > 0 && iface != null && type != ConnectivityManager.TYPE_NONE) {
+ if (timeout > 0 && iface != null) {
try {
mNMS.addIdleTimer(iface, timeout, type);
} catch (Exception e) {
@@ -2329,7 +2342,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
@VisibleForTesting
protected static final String DEFAULT_TCP_BUFFER_SIZES = "4096,87380,110208,4096,16384,110208";
- private static final String DEFAULT_TCP_RWND_KEY = "net.tcp.default_init_rwnd";
private void updateTcpBufferSizes(NetworkAgentInfo nai) {
if (isDefaultNetwork(nai) == false) {
@@ -2417,7 +2429,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
@Override
- protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
+ @Nullable String[] args) {
PriorityDump.dump(mPriorityDumper, fd, writer, args);
}
@@ -2702,6 +2715,41 @@ public class ConnectivityService extends IConnectivityManager.Stub
switch (msg.what) {
default:
return false;
+ case EVENT_PROBE_STATUS_CHANGED: {
+ final Integer netId = (Integer) msg.obj;
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(netId);
+ if (nai == null) {
+ break;
+ }
+ final boolean probePrivateDnsCompleted =
+ ((msg.arg1 & NETWORK_VALIDATION_PROBE_PRIVDNS) != 0);
+ final boolean privateDnsBroken =
+ ((msg.arg2 & NETWORK_VALIDATION_PROBE_PRIVDNS) == 0);
+ if (probePrivateDnsCompleted) {
+ if (nai.networkCapabilities.isPrivateDnsBroken() != privateDnsBroken) {
+ nai.networkCapabilities.setPrivateDnsBroken(privateDnsBroken);
+ final int oldScore = nai.getCurrentScore();
+ updateCapabilities(oldScore, nai, nai.networkCapabilities);
+ }
+ // Only show the notification when the private DNS is broken and the
+ // PRIVATE_DNS_BROKEN notification hasn't shown since last valid.
+ if (privateDnsBroken && !nai.networkMisc.hasShownBroken) {
+ showNetworkNotification(nai, NotificationType.PRIVATE_DNS_BROKEN);
+ }
+ nai.networkMisc.hasShownBroken = privateDnsBroken;
+ } else if (nai.networkCapabilities.isPrivateDnsBroken()) {
+ // If probePrivateDnsCompleted is false but nai.networkCapabilities says
+ // private DNS is broken, it means this network is being reevaluated.
+ // Either probing private DNS is not necessary any more or it hasn't been
+ // done yet. In either case, the networkCapabilities should be updated to
+ // reflect the new status.
+ nai.networkCapabilities.setPrivateDnsBroken(false);
+ final int oldScore = nai.getCurrentScore();
+ updateCapabilities(oldScore, nai, nai.networkCapabilities);
+ nai.networkMisc.hasShownBroken = false;
+ }
+ break;
+ }
case EVENT_NETWORK_TESTED: {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
if (nai == null) break;
@@ -2744,14 +2792,20 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
if (valid) {
handleFreshlyValidatedNetwork(nai);
- // Clear NO_INTERNET, PARTIAL_CONNECTIVITY and LOST_INTERNET
- // notifications if network becomes valid.
+ // Clear NO_INTERNET, PRIVATE_DNS_BROKEN, PARTIAL_CONNECTIVITY and
+ // LOST_INTERNET notifications if network becomes valid.
mNotifier.clearNotification(nai.network.netId,
NotificationType.NO_INTERNET);
mNotifier.clearNotification(nai.network.netId,
NotificationType.LOST_INTERNET);
mNotifier.clearNotification(nai.network.netId,
NotificationType.PARTIAL_CONNECTIVITY);
+ mNotifier.clearNotification(nai.network.netId,
+ NotificationType.PRIVATE_DNS_BROKEN);
+ // If network becomes valid, the hasShownBroken should be reset for
+ // that network so that the notification will be fired when the private
+ // DNS is broken again.
+ nai.networkMisc.hasShownBroken = false;
}
} else if (partialConnectivityChanged) {
updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
@@ -2879,7 +2933,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
private NetworkMonitorCallbacks(NetworkAgentInfo nai) {
mNetId = nai.network.netId;
- mNai = new AutodestructReference(nai);
+ mNai = new AutodestructReference<>(nai);
}
@Override
@@ -2902,6 +2956,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
@Override
+ public void notifyProbeStatusChanged(int probesCompleted, int probesSucceeded) {
+ mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(
+ EVENT_PROBE_STATUS_CHANGED,
+ probesCompleted, probesSucceeded, new Integer(mNetId)));
+ }
+
+ @Override
public void showProvisioningNotification(String action, String packageName) {
final Intent intent = new Intent(action);
intent.setPackage(packageName);
@@ -3315,7 +3376,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
final NetworkRequestInfo nri = mNetworkRequests.get(request);
if (nri != null) {
- if (Process.SYSTEM_UID != callingUid && nri.mUid != callingUid) {
+ if (Process.SYSTEM_UID != callingUid && Process.NETWORK_STACK_UID != callingUid
+ && nri.mUid != callingUid) {
log(String.format("UID %d attempted to %s for unowned request %s",
callingUid, requestedOperation, nri));
return null;
@@ -3721,6 +3783,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
// High priority because it is only displayed for explicitly selected networks.
highPriority = true;
break;
+ case PRIVATE_DNS_BROKEN:
+ action = Settings.ACTION_WIRELESS_SETTINGS;
+ // High priority because we should let user know why there is no internet.
+ highPriority = true;
+ break;
case LOST_INTERNET:
action = ConnectivityManager.ACTION_PROMPT_LOST_VALIDATION;
// High priority because it could help the user avoid unexpected data usage.
@@ -3738,7 +3805,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
Intent intent = new Intent(action);
- if (type != NotificationType.LOGGED_IN) {
+ if (type != NotificationType.LOGGED_IN && type != NotificationType.PRIVATE_DNS_BROKEN) {
intent.setData(Uri.fromParts("netId", Integer.toString(nai.network.netId), null));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClassName("com.android.settings",
@@ -4345,7 +4412,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
public void onChange(boolean selfChange, Uri uri) {
final Integer what = mUriEventMap.get(uri);
if (what != null) {
- mHandler.obtainMessage(what.intValue()).sendToTarget();
+ mHandler.obtainMessage(what).sendToTarget();
} else {
loge("No matching event to send for URI=" + uri);
}
@@ -4782,12 +4849,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
private static final String ATTR_MNC = "mnc";
private String getProvisioningUrlBaseFromFile() {
- FileReader fileReader = null;
- XmlPullParser parser = null;
+ XmlPullParser parser;
Configuration config = mContext.getResources().getConfiguration();
- try {
- fileReader = new FileReader(mProvisioningUrlFile);
+ try (FileReader fileReader = new FileReader(mProvisioningUrlFile)) {
parser = Xml.newPullParser();
parser.setInput(fileReader);
XmlUtils.beginDocument(parser, TAG_PROVISIONING_URLS);
@@ -4822,12 +4887,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
loge("Xml parser exception reading Carrier Provisioning Urls file: " + e);
} catch (IOException e) {
loge("I/O exception reading Carrier Provisioning Urls file: " + e);
- } finally {
- if (fileReader != null) {
- try {
- fileReader.close();
- } catch (IOException e) {}
- }
}
return null;
}
@@ -5157,8 +5216,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- // This checks that the passed capabilities either do not request a specific SSID/SignalStrength
- // , or the calling app has permission to do so.
+ // This checks that the passed capabilities either do not request a
+ // specific SSID/SignalStrength, or the calling app has permission to do so.
private void ensureSufficientPermissionsForRequest(NetworkCapabilities nc,
int callerPid, int callerUid) {
if (null != nc.getSSID() && !checkSettingsPermission(callerPid, callerUid)) {
@@ -5219,6 +5278,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
ns.assertValidFromUid(Binder.getCallingUid());
}
+ private void ensureValid(NetworkCapabilities nc) {
+ ensureValidNetworkSpecifier(nc);
+ if (nc.isPrivateDnsBroken()) {
+ throw new IllegalArgumentException("Can't request broken private DNS");
+ }
+ }
+
@Override
public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
Messenger messenger, int timeoutMs, IBinder binder, int legacyType) {
@@ -5252,7 +5318,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (timeoutMs < 0) {
throw new IllegalArgumentException("Bad timeout specified");
}
- ensureValidNetworkSpecifier(networkCapabilities);
+ ensureValid(networkCapabilities);
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
nextNetworkRequestId(), type);
@@ -5291,7 +5357,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
final int uid = Binder.getCallingUid();
Integer uidReqs = mBandwidthRequests.get(uid);
if (uidReqs == null) {
- uidReqs = new Integer(0);
+ uidReqs = 0;
}
mBandwidthRequests.put(uid, ++uidReqs);
}
@@ -5394,7 +5460,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// There is no need to do this for requests because an app without CHANGE_NETWORK_STATE
// can't request networks.
restrictBackgroundRequestForCaller(nc);
- ensureValidNetworkSpecifier(nc);
+ ensureValid(nc);
NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
NetworkRequest.Type.LISTEN);
@@ -5412,7 +5478,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (!hasWifiNetworkListenPermission(networkCapabilities)) {
enforceAccessPermission();
}
- ensureValidNetworkSpecifier(networkCapabilities);
+ ensureValid(networkCapabilities);
ensureSufficientPermissionsForRequest(networkCapabilities,
Binder.getCallingPid(), Binder.getCallingUid());
@@ -5625,7 +5691,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties newLp,
- LinkProperties oldLp) {
+ @NonNull LinkProperties oldLp) {
int netId = networkAgent.network.netId;
// The NetworkAgentInfo does not know whether clatd is running on its network or not, or
@@ -5661,6 +5727,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
} else {
updateProxy(newLp, oldLp);
}
+
+ updateWakeOnLan(newLp);
+
// TODO - move this check to cover the whole function
if (!Objects.equals(newLp, oldLp)) {
synchronized (networkAgent) {
@@ -5740,7 +5809,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
*/
private boolean updateRoutes(LinkProperties newLp, LinkProperties oldLp, int netId) {
// Compare the route diff to determine which routes should be added and removed.
- CompareResult<RouteInfo> routeDiff = new CompareResult<RouteInfo>(
+ CompareResult<RouteInfo> routeDiff = new CompareResult<>(
oldLp != null ? oldLp.getAllRoutes() : null,
newLp != null ? newLp.getAllRoutes() : null);
@@ -5759,7 +5828,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
for (RouteInfo route : routeDiff.added) {
- if (route.hasGateway() == false) continue;
+ if (!route.hasGateway()) continue;
if (VDBG || DDBG) log("Adding Route [" + route + "] to network " + netId);
try {
mNMS.addRoute(netId, route);
@@ -5831,6 +5900,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
+ private void updateWakeOnLan(@NonNull LinkProperties lp) {
+ lp.setWakeOnLanSupported(mWolSupportedInterfaces.contains(lp.getInterfaceName()));
+ }
+
private int getNetworkPermission(NetworkCapabilities nc) {
if (!nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
return INetd.PERMISSION_SYSTEM;
@@ -5891,6 +5964,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
} else {
newNc.removeCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY);
}
+ newNc.setPrivateDnsBroken(nai.networkCapabilities.isPrivateDnsBroken());
return newNc;
}
@@ -5988,8 +6062,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
* 3. the VPN is fully-routed
* 4. the VPN interface is non-null
*
- * @See INetd#firewallAddUidInterfaceRules
- * @See INetd#firewallRemoveUidInterfaceRules
+ * @see INetd#firewallAddUidInterfaceRules
+ * @see INetd#firewallRemoveUidInterfaceRules
*/
private boolean requiresVpnIsolation(@NonNull NetworkAgentInfo nai, NetworkCapabilities nc,
LinkProperties lp) {
@@ -7147,9 +7221,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
@Override
- public void onShellCommand(FileDescriptor in, FileDescriptor out,
- FileDescriptor err, String[] args, ShellCallback callback,
- ResultReceiver resultReceiver) {
+ public void onShellCommand(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
+ FileDescriptor err, @NonNull String[] args, ShellCallback callback,
+ @NonNull ResultReceiver resultReceiver) {
(new ShellCmd()).exec(this, in, out, err, args, callback, resultReceiver);
}
diff --git a/services/core/java/com/android/server/ContextHubSystemService.java b/services/core/java/com/android/server/ContextHubSystemService.java
index 110847dd54c8..c6853a5119ec 100644
--- a/services/core/java/com/android/server/ContextHubSystemService.java
+++ b/services/core/java/com/android/server/ContextHubSystemService.java
@@ -16,12 +16,12 @@
package com.android.server;
-import com.android.internal.util.ConcurrentUtils;
-import com.android.server.location.ContextHubService;
-import com.android.server.SystemServerInitThreadPool;
import android.content.Context;
import android.util.Log;
+import com.android.internal.util.ConcurrentUtils;
+import com.android.server.location.ContextHubService;
+
import java.util.concurrent.Future;
class ContextHubSystemService extends SystemService {
@@ -32,7 +32,7 @@ class ContextHubSystemService extends SystemService {
public ContextHubSystemService(Context context) {
super(context);
- mInit = SystemServerInitThreadPool.get().submit(() -> {
+ mInit = SystemServerInitThreadPool.submit(() -> {
mContextHubService = new ContextHubService(context);
}, "Init ContextHubSystemService");
}
diff --git a/services/core/java/com/android/server/GnssManagerService.java b/services/core/java/com/android/server/GnssManagerService.java
new file mode 100644
index 000000000000..274e2f1b53f8
--- /dev/null
+++ b/services/core/java/com/android/server/GnssManagerService.java
@@ -0,0 +1,812 @@
+/*
+ * 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;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.location.GnssCapabilities;
+import android.location.GnssMeasurementCorrections;
+import android.location.IBatchedLocationCallback;
+import android.location.IGnssMeasurementsListener;
+import android.location.IGnssNavigationMessageListener;
+import android.location.IGnssStatusListener;
+import android.location.IGpsGeofenceHardware;
+import android.location.INetInitiatedListener;
+import android.location.Location;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.Process;
+import android.os.RemoteException;
+import android.stats.location.LocationStatsEnums;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocationManagerServiceUtils.LinkedListener;
+import com.android.server.LocationManagerServiceUtils.LinkedListenerBase;
+import com.android.server.location.AbstractLocationProvider;
+import com.android.server.location.CallerIdentity;
+import com.android.server.location.GnssBatchingProvider;
+import com.android.server.location.GnssCapabilitiesProvider;
+import com.android.server.location.GnssLocationProvider;
+import com.android.server.location.GnssMeasurementCorrectionsProvider;
+import com.android.server.location.GnssMeasurementsProvider;
+import com.android.server.location.GnssNavigationMessageProvider;
+import com.android.server.location.GnssStatusListenerHelper;
+import com.android.server.location.RemoteListenerHelper;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/** Manages Gnss providers and related Gnss functions for LocationManagerService. */
+public class GnssManagerService {
+ private static final String TAG = "LocationManagerService";
+ private static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
+
+ // Providers
+ private final GnssLocationProvider mGnssLocationProvider;
+ private final GnssStatusListenerHelper mGnssStatusProvider;
+ private final GnssMeasurementsProvider mGnssMeasurementsProvider;
+ private final GnssMeasurementCorrectionsProvider mGnssMeasurementCorrectionsProvider;
+ private final GnssNavigationMessageProvider mGnssNavigationMessageProvider;
+ private final GnssLocationProvider.GnssSystemInfoProvider mGnssSystemInfoProvider;
+ private final GnssLocationProvider.GnssMetricsProvider mGnssMetricsProvider;
+ private final GnssCapabilitiesProvider mGnssCapabilitiesProvider;
+ private final GnssBatchingProvider mGnssBatchingProvider;
+
+ private final INetInitiatedListener mNetInitiatedListener;
+ private final IGpsGeofenceHardware mGpsGeofenceProxy;
+ private final LocationManagerService mLocationManagerService;
+ private final LocationUsageLogger mLocationUsageLogger;
+
+ @GuardedBy("mGnssMeasurementsListeners")
+ private final ArrayMap<IBinder,
+ LinkedListener<IGnssMeasurementsListener>>
+ mGnssMeasurementsListeners = new ArrayMap<>();
+
+ @GuardedBy("mGnssNavigationMessageListeners")
+ private final ArrayMap<
+ IBinder, LinkedListener<IGnssNavigationMessageListener>>
+ mGnssNavigationMessageListeners = new ArrayMap<>();
+
+ @GuardedBy("mGnssStatusListeners")
+ private final ArrayMap<IBinder, LinkedListener<IGnssStatusListener>>
+ mGnssStatusListeners = new ArrayMap<>();
+
+ @GuardedBy("mGnssBatchingLock")
+ private IBatchedLocationCallback mGnssBatchingCallback;
+
+ @GuardedBy("mGnssBatchingLock")
+ private LinkedListener<IBatchedLocationCallback>
+ mGnssBatchingDeathCallback;
+
+ @GuardedBy("mGnssBatchingLock")
+ private boolean mGnssBatchingInProgress = false;
+
+ private final Object mGnssBatchingLock = new Object();
+ private final Context mContext;
+ private final Handler mHandler;
+
+ public GnssManagerService(LocationManagerService locationManagerService,
+ Context context,
+ AbstractLocationProvider.LocationProviderManager gnssProviderManager,
+ LocationUsageLogger locationUsageLogger) {
+ this(locationManagerService, context, new GnssLocationProvider(context, gnssProviderManager,
+ FgThread.getHandler().getLooper()), locationUsageLogger);
+ }
+
+ // Can use this constructor to inject GnssLocationProvider for testing
+ @VisibleForTesting
+ GnssManagerService(LocationManagerService locationManagerService,
+ Context context,
+ GnssLocationProvider gnssLocationProvider,
+ LocationUsageLogger locationUsageLogger) {
+ mContext = context;
+ mHandler = FgThread.getHandler();
+
+ mGnssLocationProvider =
+ gnssLocationProvider;
+
+ mGnssStatusProvider = mGnssLocationProvider.getGnssStatusProvider();
+ mGnssMeasurementsProvider = mGnssLocationProvider.getGnssMeasurementsProvider();
+ mGnssMeasurementCorrectionsProvider =
+ mGnssLocationProvider.getGnssMeasurementCorrectionsProvider();
+ mGnssNavigationMessageProvider = mGnssLocationProvider.getGnssNavigationMessageProvider();
+ mGnssSystemInfoProvider = mGnssLocationProvider.getGnssSystemInfoProvider();
+ mGnssMetricsProvider = mGnssLocationProvider.getGnssMetricsProvider();
+ mGnssCapabilitiesProvider = mGnssLocationProvider.getGnssCapabilitiesProvider();
+ mGnssBatchingProvider = mGnssLocationProvider.getGnssBatchingProvider();
+
+ mNetInitiatedListener = mGnssLocationProvider.getNetInitiatedListener();
+ mGpsGeofenceProxy = mGnssLocationProvider.getGpsGeofenceProxy();
+ mLocationManagerService = locationManagerService;
+ mLocationUsageLogger = locationUsageLogger;
+
+ registerUidListener();
+ }
+
+ public static boolean isGnssSupported() {
+ return GnssLocationProvider.isSupported();
+ }
+
+ private boolean hasGnssPermissions(String packageName) {
+ mContext.enforceCallingPermission(
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ "Fine location permission not granted.");
+
+ int uid = Binder.getCallingUid();
+ long identity = Binder.clearCallingIdentity();
+ try {
+ return mContext.getSystemService(
+ AppOpsManager.class).checkOp(AppOpsManager.OP_FINE_LOCATION, uid, packageName)
+ == AppOpsManager.MODE_ALLOWED;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ public GnssLocationProvider getGnssLocationProvider() {
+ return mGnssLocationProvider;
+ }
+
+ public IGpsGeofenceHardware getGpsGeofenceProxy() {
+ return mGpsGeofenceProxy;
+ }
+
+ /**
+ * Get year of GNSS hardware.
+ *
+ * @return year of GNSS hardware as an int if possible, otherwise zero
+ */
+ public int getGnssYearOfHardware() {
+ if (mGnssSystemInfoProvider != null) {
+ return mGnssSystemInfoProvider.getGnssYearOfHardware();
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Get model name of GNSS hardware.
+ *
+ * @return GNSS hardware model name as a string if possible, otherwise null
+ */
+ public String getGnssHardwareModelName() {
+ if (mGnssSystemInfoProvider != null) {
+ return mGnssSystemInfoProvider.getGnssHardwareModelName();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get GNSS hardware capabilities. The capabilities are described in {@link
+ * android.location.GnssCapabilities} and their integer values correspond to the
+ * bit positions in the returned {@code long} value.
+ *
+ * @param packageName name of requesting package
+ * @return capabilities supported by the GNSS chipset
+ */
+ public long getGnssCapabilities(String packageName) {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to obtain GNSS chipset capabilities.");
+ if (!hasGnssPermissions(packageName) || mGnssCapabilitiesProvider == null) {
+ return GnssCapabilities.INVALID_CAPABILITIES;
+ }
+ return mGnssCapabilitiesProvider.getGnssCapabilities();
+ }
+
+ /**
+ * Get size of GNSS batch (GNSS location results are batched together for power savings).
+ * Requires LOCATION_HARDWARE and GNSS permissions.
+ *
+ * @param packageName name of requesting package
+ * @return size of the GNSS batch collection
+ */
+ public int getGnssBatchSize(String packageName) {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware batching");
+
+ if (!hasGnssPermissions(packageName)) {
+ Log.e(TAG, "getGnssBatchSize called without GNSS permissions");
+ return 0;
+ }
+ if (mGnssBatchingProvider == null) {
+ Log.e(
+ TAG,
+ "Can not get GNSS batch size. GNSS batching provider "
+ + "not available.");
+ return 0;
+ }
+
+ synchronized (mGnssBatchingLock) {
+ return mGnssBatchingProvider.getBatchSize();
+ }
+ }
+
+ /**
+ * Starts GNSS batch collection. GNSS positions are collected in a batch before being delivered
+ * as a collection.
+ *
+ * @param periodNanos duration over which to collect GPS positions before delivering as a
+ * batch
+ * @param wakeOnFifoFull specifying whether to wake on full queue
+ * @param packageName name of requesting package
+ * @return true of batch started successfully, false otherwise
+ */
+ public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName) {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware batching");
+
+ if (!hasGnssPermissions(packageName)) {
+ Log.e(TAG, "startGnssBatch called without GNSS permissions");
+ return false;
+ }
+ if (mGnssBatchingProvider == null) {
+ Log.e(
+ TAG,
+ "Can not start GNSS batching. GNSS batching provider "
+ + "not available.");
+ return false;
+ }
+
+ synchronized (mGnssBatchingLock) {
+ if (mGnssBatchingInProgress) {
+ // Current design does not expect multiple starts to be called repeatedly
+ Log.e(TAG, "startGnssBatch unexpectedly called w/o stopping prior batch");
+ // Try to clean up anyway, and continue
+ stopGnssBatch();
+ }
+
+ mGnssBatchingInProgress = true;
+ return mGnssBatchingProvider.start(periodNanos, wakeOnFifoFull);
+ }
+ }
+
+ /**
+ * Adds a GNSS batching callback for delivering GNSS location batch results.
+ *
+ * @param callback called when batching operation is complete to deliver GPS positions
+ * @param packageName name of requesting package
+ * @return true if callback is successfully added, false otherwise
+ */
+ public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName,
+ @Nullable String featureId, @NonNull String listenerIdentity) {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware batching");
+
+ if (!hasGnssPermissions(packageName)) {
+ Log.e(TAG, "addGnssBatchingCallback called without GNSS permissions");
+ return false;
+ }
+ if (mGnssBatchingProvider == null) {
+ Log.e(
+ TAG,
+ "Can not add GNSS batching callback. GNSS batching provider "
+ + "not available.");
+ return false;
+ }
+
+ CallerIdentity callerIdentity =
+ new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName,
+ featureId, listenerIdentity);
+ synchronized (mGnssBatchingLock) {
+ mGnssBatchingCallback = callback;
+ mGnssBatchingDeathCallback =
+ new LocationManagerServiceUtils.LinkedListener<>(
+ callback,
+ "BatchedLocationCallback",
+ callerIdentity,
+ (IBatchedLocationCallback listener) -> {
+ stopGnssBatch();
+ removeGnssBatchingCallback();
+ });
+ if (!mGnssBatchingDeathCallback.linkToListenerDeathNotificationLocked(
+ callback.asBinder())) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Force flush GNSS location results from batch.
+ *
+ * @param packageName name of requesting package
+ */
+ public void flushGnssBatch(String packageName) {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware batching");
+
+ if (!hasGnssPermissions(packageName)) {
+ Log.e(TAG, "flushGnssBatch called without GNSS permissions");
+ return;
+ }
+
+ if (mGnssBatchingProvider == null) {
+ Log.e(
+ TAG,
+ "Can not flush GNSS batch. GNSS batching provider "
+ + "not available.");
+ return;
+ }
+
+ synchronized (mGnssBatchingLock) {
+ if (!mGnssBatchingInProgress) {
+ Log.w(TAG, "flushGnssBatch called with no batch in progress");
+ }
+ mGnssBatchingProvider.flush();
+ }
+ }
+
+ /**
+ * Removes GNSS batching callback.
+ */
+ public void removeGnssBatchingCallback() {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware batching");
+
+ if (mGnssBatchingProvider == null) {
+ Log.e(
+ TAG,
+ "Can not add GNSS batching callback. GNSS batching provider "
+ + "not available.");
+ return;
+ }
+
+ synchronized (mGnssBatchingLock) {
+ mGnssBatchingDeathCallback.unlinkFromListenerDeathNotificationLocked(
+ mGnssBatchingCallback.asBinder());
+ mGnssBatchingCallback = null;
+ mGnssBatchingDeathCallback = null;
+ }
+ }
+
+ /**
+ * Stop GNSS batch collection.
+ *
+ * @return true if GNSS batch successfully stopped, false otherwise
+ */
+ public boolean stopGnssBatch() {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware batching");
+
+ if (mGnssBatchingProvider == null) {
+ Log.e(
+ TAG,
+ "Can not stop GNSS batch. GNSS batching provider "
+ + "not available.");
+ return false;
+ }
+ synchronized (mGnssBatchingLock) {
+ mGnssBatchingInProgress = false;
+ return mGnssBatchingProvider.stop();
+ }
+ }
+
+ private void registerUidListener() {
+ mContext.getSystemService(
+ ActivityManager.class).addOnUidImportanceListener(
+ (uid, importance) -> {
+ // listener invoked on ui thread, move to our thread to reduce risk
+ // of blocking ui thread
+ mHandler.post(
+ () -> {
+ onForegroundChanged(uid,
+ LocationManagerServiceUtils.isImportanceForeground(
+ importance));
+ });
+ },
+ ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
+ }
+
+ private void onForegroundChanged(int uid, boolean foreground) {
+ synchronized (mGnssMeasurementsListeners) {
+ updateListenersOnForegroundChangedLocked(
+ mGnssMeasurementsListeners,
+ mGnssMeasurementsProvider,
+ IGnssMeasurementsListener.Stub::asInterface,
+ uid,
+ foreground);
+ }
+ synchronized (mGnssNavigationMessageListeners) {
+ updateListenersOnForegroundChangedLocked(
+ mGnssNavigationMessageListeners,
+ mGnssNavigationMessageProvider,
+ IGnssNavigationMessageListener.Stub::asInterface,
+ uid,
+ foreground);
+ }
+ synchronized (mGnssStatusListeners) {
+ updateListenersOnForegroundChangedLocked(
+ mGnssStatusListeners,
+ mGnssStatusProvider,
+ IGnssStatusListener.Stub::asInterface,
+ uid,
+ foreground);
+ }
+ }
+
+ private <TListener extends IInterface> void updateListenersOnForegroundChangedLocked(
+ ArrayMap<IBinder, ? extends LinkedListenerBase>
+ gnssDataListeners,
+ RemoteListenerHelper<TListener> gnssDataProvider,
+ Function<IBinder, TListener> mapBinderToListener,
+ int uid,
+ boolean foreground) {
+ for (Map.Entry<IBinder, ? extends LinkedListenerBase> entry :
+ gnssDataListeners.entrySet()) {
+ LinkedListenerBase linkedListener = entry.getValue();
+ CallerIdentity callerIdentity = linkedListener.getCallerIdentity();
+ if (callerIdentity.mUid != uid) {
+ continue;
+ }
+
+ if (D) {
+ Log.d(
+ TAG,
+ linkedListener.getListenerName()
+ + " from uid "
+ + uid
+ + " is now "
+ + LocationManagerServiceUtils.foregroundAsString(foreground));
+ }
+
+ TListener listener = mapBinderToListener.apply(entry.getKey());
+ if (foreground || mLocationManagerService.isThrottlingExemptLocked(callerIdentity)) {
+ gnssDataProvider.addListener(listener, callerIdentity);
+ } else {
+ gnssDataProvider.removeListener(listener);
+ }
+ }
+ }
+
+ private <TListener extends IInterface> boolean addGnssDataListenerLocked(
+ TListener listener,
+ String packageName,
+ @Nullable String featureId,
+ @NonNull String listenerIdentifier,
+ RemoteListenerHelper<TListener> gnssDataProvider,
+ ArrayMap<IBinder,
+ LinkedListener<TListener>> gnssDataListeners,
+ Consumer<TListener> binderDeathCallback) {
+ if (!hasGnssPermissions(packageName)) {
+ Log.e(TAG, "addGnssDataListenerLocked called without GNSS permissions");
+ return false;
+ }
+
+ if (gnssDataProvider == null) {
+ Log.e(
+ TAG,
+ "Can not add GNSS data listener. GNSS data provider "
+ + "not available.");
+ return false;
+ }
+
+ CallerIdentity callerIdentity =
+ new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName,
+ featureId, listenerIdentifier);
+ LinkedListener<TListener> linkedListener =
+ new LocationManagerServiceUtils.LinkedListener<>(
+ listener, listenerIdentifier, callerIdentity, binderDeathCallback);
+ IBinder binder = listener.asBinder();
+ if (!linkedListener.linkToListenerDeathNotificationLocked(binder)) {
+ return false;
+ }
+
+ gnssDataListeners.put(binder, linkedListener);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ if (gnssDataProvider == mGnssMeasurementsProvider
+ || gnssDataProvider == mGnssStatusProvider) {
+ mLocationUsageLogger.logLocationApiUsage(
+ LocationStatsEnums.USAGE_STARTED,
+ gnssDataProvider == mGnssMeasurementsProvider
+ ? LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER
+ : LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK,
+ packageName,
+ /* LocationRequest= */ null,
+ /* hasListener= */ true,
+ /* hasIntent= */ false,
+ /* geofence= */ null,
+ LocationManagerServiceUtils.getPackageImportance(packageName,
+ mContext));
+ }
+ if (mLocationManagerService.isThrottlingExemptLocked(callerIdentity)
+ || LocationManagerServiceUtils.isImportanceForeground(
+ LocationManagerServiceUtils.getPackageImportance(packageName, mContext))) {
+ gnssDataProvider.addListener(listener, callerIdentity);
+ }
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private <TListener extends IInterface> void removeGnssDataListener(
+ TListener listener,
+ RemoteListenerHelper<TListener> gnssDataProvider,
+ ArrayMap<IBinder,
+ LinkedListener<TListener>> gnssDataListeners) {
+ if (gnssDataProvider == null) {
+ Log.e(
+ TAG,
+ "Can not remove GNSS data listener. GNSS data provider "
+ + "not available.");
+ return;
+ }
+
+ IBinder binder = listener.asBinder();
+ LinkedListener<TListener> linkedListener =
+ gnssDataListeners.remove(binder);
+ if (linkedListener == null) {
+ return;
+ }
+ long identity = Binder.clearCallingIdentity();
+ try {
+ if (gnssDataProvider == mGnssMeasurementsProvider
+ || gnssDataProvider == mGnssStatusProvider) {
+ mLocationUsageLogger.logLocationApiUsage(
+ LocationStatsEnums.USAGE_ENDED,
+ gnssDataProvider == mGnssMeasurementsProvider
+ ? LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER
+ : LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK,
+ linkedListener.getCallerIdentity().mPackageName,
+ /* LocationRequest= */ null,
+ /* hasListener= */ true,
+ /* hasIntent= */ false,
+ /* geofence= */ null,
+ LocationManagerServiceUtils.getPackageImportance(
+ linkedListener.getCallerIdentity().mPackageName, mContext));
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ linkedListener.unlinkFromListenerDeathNotificationLocked(binder);
+ gnssDataProvider.removeListener(listener);
+ }
+
+ /**
+ * Registers listener for GNSS status changes.
+ *
+ * @param listener called when GNSS status changes
+ * @param packageName name of requesting package
+ * @return true if listener is successfully registered, false otherwise
+ */
+ public boolean registerGnssStatusCallback(IGnssStatusListener listener, String packageName,
+ @Nullable String featureId) {
+ synchronized (mGnssStatusListeners) {
+ return addGnssDataListenerLocked(
+ listener,
+ packageName,
+ featureId,
+ "Gnss status",
+ mGnssStatusProvider,
+ mGnssStatusListeners,
+ this::unregisterGnssStatusCallback);
+ }
+ }
+
+ /**
+ * Unregisters listener for GNSS status changes.
+ *
+ * @param listener called when GNSS status changes
+ */
+ public void unregisterGnssStatusCallback(IGnssStatusListener listener) {
+ synchronized (mGnssStatusListeners) {
+ removeGnssDataListener(listener, mGnssStatusProvider, mGnssStatusListeners);
+ }
+ }
+
+ /**
+ * Adds a GNSS measurements listener.
+ *
+ * @param listener called when GNSS measurements are received
+ * @param packageName name of requesting package
+ * @return true if listener is successfully added, false otherwise
+ */
+ public boolean addGnssMeasurementsListener(
+ IGnssMeasurementsListener listener, String packageName, @Nullable String featureId,
+ @NonNull String listenerIdentifier) {
+ synchronized (mGnssMeasurementsListeners) {
+ return addGnssDataListenerLocked(
+ listener,
+ packageName,
+ featureId,
+ listenerIdentifier,
+ mGnssMeasurementsProvider,
+ mGnssMeasurementsListeners,
+ this::removeGnssMeasurementsListener);
+ }
+ }
+
+ /**
+ * Injects GNSS measurement corrections.
+ *
+ * @param measurementCorrections GNSS measurement corrections
+ * @param packageName name of requesting package
+ */
+ public void injectGnssMeasurementCorrections(
+ GnssMeasurementCorrections measurementCorrections, String packageName) {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to inject GNSS measurement corrections.");
+ if (!hasGnssPermissions(packageName)) {
+ Log.e(TAG, "Can not inject GNSS corrections due to no permission.");
+ return;
+ }
+ if (mGnssMeasurementCorrectionsProvider == null) {
+ Log.e(
+ TAG,
+ "Can not inject GNSS corrections. GNSS measurement corrections provider "
+ + "not available.");
+ return;
+ }
+ mGnssMeasurementCorrectionsProvider.injectGnssMeasurementCorrections(
+ measurementCorrections);
+ }
+
+ /**
+ * Removes a GNSS measurements listener.
+ *
+ * @param listener called when GNSS measurements are received
+ */
+ public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
+ synchronized (mGnssMeasurementsListeners) {
+ removeGnssDataListener(listener, mGnssMeasurementsProvider, mGnssMeasurementsListeners);
+ }
+ }
+
+ /**
+ * Adds a GNSS navigation message listener.
+ *
+ * @param listener called when navigation message is received
+ * @param packageName name of requesting package
+ * @return true if listener is successfully added, false otherwise
+ */
+ public boolean addGnssNavigationMessageListener(
+ IGnssNavigationMessageListener listener, String packageName,
+ @Nullable String featureId, @NonNull String listenerIdentifier) {
+ synchronized (mGnssNavigationMessageListeners) {
+ return addGnssDataListenerLocked(
+ listener,
+ packageName,
+ featureId,
+ listenerIdentifier,
+ mGnssNavigationMessageProvider,
+ mGnssNavigationMessageListeners,
+ this::removeGnssNavigationMessageListener);
+ }
+ }
+
+ /**
+ * Removes a GNSS navigation message listener.
+ *
+ * @param listener called when navigation message is received
+ */
+ public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) {
+ removeGnssDataListener(
+ listener, mGnssNavigationMessageProvider, mGnssNavigationMessageListeners);
+ }
+
+ /**
+ * Send Ni Response, indicating a location request initiated by a network carrier.
+ */
+ public boolean sendNiResponse(int notifId, int userResponse) {
+ if (Binder.getCallingUid() != Process.myUid()) {
+ throw new SecurityException(
+ "calling sendNiResponse from outside of the system is not allowed");
+ }
+ try {
+ return mNetInitiatedListener.sendNiResponse(notifId, userResponse);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in LocationManagerService.sendNiResponse");
+ return false;
+ }
+ }
+
+ /**
+ * Report location results to GNSS batching listener.
+ *
+ * @param locations batch of locations to report to GNSS batching callback
+ */
+ public void onReportLocation(List<Location> locations) {
+ if (mGnssBatchingCallback == null) {
+ Log.e(TAG, "reportLocationBatch() called without active Callback");
+ return;
+ }
+
+ try {
+ mGnssBatchingCallback.onLocationBatch(locations);
+ } catch (RemoteException e) {
+ Log.e(TAG, "mGnssBatchingCallback.onLocationBatch failed", e);
+ }
+ }
+
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+
+ if (args.length > 0 && args[0].equals("--gnssmetrics")) {
+ if (mGnssMetricsProvider != null) {
+ pw.append(mGnssMetricsProvider.getGnssMetricsAsProtoString());
+ }
+ return;
+ }
+
+ ipw.println("GnssMeasurement Listeners:");
+ ipw.increaseIndent();
+ synchronized (mGnssMeasurementsListeners) {
+ for (LinkedListenerBase listener :
+ mGnssMeasurementsListeners
+ .values()) {
+ ipw.println(listener + ": " + mLocationManagerService.isThrottlingExemptLocked(
+ listener.mCallerIdentity));
+ }
+ }
+ ipw.decreaseIndent();
+
+ ipw.println("GnssNavigationMessage Listeners:");
+ ipw.increaseIndent();
+ synchronized (mGnssNavigationMessageListeners) {
+ for (LinkedListenerBase listener :
+ mGnssNavigationMessageListeners.values()) {
+ ipw.println(listener + ": " + mLocationManagerService.isThrottlingExemptLocked(
+ listener.mCallerIdentity));
+ }
+ }
+ ipw.decreaseIndent();
+
+ ipw.println("GnssStatus Listeners:");
+ ipw.increaseIndent();
+ synchronized (mGnssStatusListeners) {
+ for (LinkedListenerBase listener :
+ mGnssStatusListeners.values()) {
+ ipw.println(listener + ": " + mLocationManagerService.isThrottlingExemptLocked(
+ listener.mCallerIdentity));
+ }
+ }
+ ipw.decreaseIndent();
+
+ synchronized (mGnssBatchingLock) {
+ if (mGnssBatchingInProgress) {
+ ipw.println("GNSS batching in progress");
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 27c7468bd143..8992e8bb6a7b 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -49,25 +49,23 @@ import android.location.Address;
import android.location.Criteria;
import android.location.GeocoderParams;
import android.location.Geofence;
-import android.location.GnssCapabilities;
import android.location.GnssMeasurementCorrections;
import android.location.IBatchedLocationCallback;
import android.location.IGnssMeasurementsListener;
import android.location.IGnssNavigationMessageListener;
import android.location.IGnssStatusListener;
-import android.location.IGpsGeofenceHardware;
import android.location.ILocationListener;
import android.location.ILocationManager;
-import android.location.INetInitiatedListener;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationRequest;
import android.location.LocationTime;
import android.os.Binder;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
-import android.os.IInterface;
+import android.os.ICancellationSignal;
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerManagerInternal;
@@ -81,7 +79,6 @@ import android.os.WorkSource.WorkChain;
import android.provider.Settings;
import android.stats.location.LocationStatsEnums;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.Log;
@@ -102,13 +99,6 @@ import com.android.server.location.CallerIdentity;
import com.android.server.location.GeocoderProxy;
import com.android.server.location.GeofenceManager;
import com.android.server.location.GeofenceProxy;
-import com.android.server.location.GnssBatchingProvider;
-import com.android.server.location.GnssCapabilitiesProvider;
-import com.android.server.location.GnssLocationProvider;
-import com.android.server.location.GnssMeasurementCorrectionsProvider;
-import com.android.server.location.GnssMeasurementsProvider;
-import com.android.server.location.GnssNavigationMessageProvider;
-import com.android.server.location.GnssStatusListenerHelper;
import com.android.server.location.LocationBlacklist;
import com.android.server.location.LocationFudger;
import com.android.server.location.LocationProviderProxy;
@@ -117,7 +107,6 @@ import com.android.server.location.LocationRequestStatistics.PackageProviderKey;
import com.android.server.location.LocationRequestStatistics.PackageStatistics;
import com.android.server.location.MockProvider;
import com.android.server.location.PassiveProvider;
-import com.android.server.location.RemoteListenerHelper;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import java.io.ByteArrayOutputStream;
@@ -132,9 +121,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.NoSuchElementException;
-import java.util.function.Consumer;
-import java.util.function.Function;
+import java.util.concurrent.TimeUnit;
/**
* The service class that manages LocationProviders and issues location
@@ -161,11 +148,12 @@ public class LocationManagerService extends ILocationManager.Stub {
private static final String FUSED_LOCATION_SERVICE_ACTION =
"com.android.location.service.FusedLocationProvider";
- private static final long NANOS_PER_MILLI = 1000000L;
-
// The maximum interval a location request can have and still be considered "high power".
private static final long HIGH_POWER_INTERVAL_MS = 5 * 60 * 1000;
+ // maximum age of a location before it is no longer considered "current"
+ private static final long MAX_CURRENT_LOCATION_AGE_MS = 10 * 1000;
+
private static final int FOREGROUND_IMPORTANCE_CUTOFF
= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
@@ -200,17 +188,13 @@ public class LocationManagerService extends ILocationManager.Stub {
private GeofenceManager mGeofenceManager;
private LocationFudger mLocationFudger;
private GeocoderProxy mGeocodeProvider;
- private GnssStatusListenerHelper mGnssStatusProvider;
- private INetInitiatedListener mNetInitiatedListener;
- private PassiveProvider mPassiveProvider; // track passive provider for special cases
+ @Nullable
+ private GnssManagerService mGnssManagerService;
+ private PassiveProvider mPassiveProvider; // track passive provider for special cases
private LocationBlacklist mBlacklist;
- private GnssMeasurementsProvider mGnssMeasurementsProvider;
- private GnssMeasurementCorrectionsProvider mGnssMeasurementCorrectionsProvider;
- private GnssNavigationMessageProvider mGnssNavigationMessageProvider;
@GuardedBy("mLock")
private String mExtraLocationControllerPackage;
private boolean mExtraLocationControllerPackageEnabled;
- private IGpsGeofenceHardware mGpsGeofenceProxy;
// list of currently active providers
@GuardedBy("mLock")
@@ -242,32 +226,10 @@ public class LocationManagerService extends ILocationManager.Stub {
private final ArraySet<String> mIgnoreSettingsPackageWhitelist = new ArraySet<>();
- @GuardedBy("mLock")
- private final ArrayMap<IBinder, LinkedListener<IGnssMeasurementsListener>>
- mGnssMeasurementsListeners = new ArrayMap<>();
- @GuardedBy("mLock")
- private final ArrayMap<IBinder, LinkedListener<IGnssNavigationMessageListener>>
- mGnssNavigationMessageListeners = new ArrayMap<>();
- @GuardedBy("mLock")
- private final ArrayMap<IBinder, LinkedListener<IGnssStatusListener>>
- mGnssStatusListeners = new ArrayMap<>();
-
// current active user on the device - other users are denied location data
private int mCurrentUserId = UserHandle.USER_SYSTEM;
private int[] mCurrentUserProfiles = new int[]{UserHandle.USER_SYSTEM};
- private GnssLocationProvider.GnssSystemInfoProvider mGnssSystemInfoProvider;
- private GnssLocationProvider.GnssMetricsProvider mGnssMetricsProvider;
- private GnssCapabilitiesProvider mGnssCapabilitiesProvider;
-
- private GnssBatchingProvider mGnssBatchingProvider;
- @GuardedBy("mLock")
- private IBatchedLocationCallback mGnssBatchingCallback;
- @GuardedBy("mLock")
- private LinkedListener<IBatchedLocationCallback> mGnssBatchingDeathCallback;
- @GuardedBy("mLock")
- private boolean mGnssBatchingInProgress = false;
-
@GuardedBy("mLock")
@PowerManager.LocationPowerSaveMode
private int mBatterySaverMode;
@@ -290,7 +252,7 @@ public class LocationManagerService extends ILocationManager.Stub {
com.android.internal.R.array.config_locationProviderPackageNames));
permissionManagerInternal.setLocationExtraPackagesProvider(
userId -> mContext.getResources().getStringArray(
- com.android.internal.R.array.config_locationExtraPackageNames));
+ com.android.internal.R.array.config_locationExtraPackageNames));
// most startup is deferred until systemRunning()
}
@@ -570,7 +532,7 @@ public class LocationManagerService extends ILocationManager.Stub {
@GuardedBy("mLock")
private void onUidImportanceChangedLocked(int uid, int importance) {
- boolean foreground = isImportanceForeground(importance);
+ boolean foreground = LocationManagerServiceUtils.isImportanceForeground(importance);
HashSet<String> affectedProviders = new HashSet<>(mRecordsByProvider.size());
for (Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) {
String provider = entry.getKey();
@@ -579,7 +541,8 @@ public class LocationManagerService extends ILocationManager.Stub {
&& record.mIsForegroundUid != foreground) {
if (D) {
Log.d(TAG, "request from uid " + uid + " is now "
- + foregroundAsString(foreground));
+ + LocationManagerServiceUtils.foregroundAsString(
+ foreground));
}
record.updateForeground(foreground);
@@ -592,51 +555,6 @@ public class LocationManagerService extends ILocationManager.Stub {
for (String provider : affectedProviders) {
applyRequirementsLocked(provider);
}
-
- updateGnssDataProviderOnUidImportanceChangedLocked(mGnssMeasurementsListeners,
- mGnssMeasurementsProvider, IGnssMeasurementsListener.Stub::asInterface,
- uid, foreground);
-
- updateGnssDataProviderOnUidImportanceChangedLocked(mGnssNavigationMessageListeners,
- mGnssNavigationMessageProvider, IGnssNavigationMessageListener.Stub::asInterface,
- uid, foreground);
-
- updateGnssDataProviderOnUidImportanceChangedLocked(mGnssStatusListeners,
- mGnssStatusProvider, IGnssStatusListener.Stub::asInterface, uid, foreground);
- }
-
- @GuardedBy("mLock")
- private <TListener extends IInterface> void updateGnssDataProviderOnUidImportanceChangedLocked(
- ArrayMap<IBinder, ? extends LinkedListenerBase> gnssDataListeners,
- RemoteListenerHelper<TListener> gnssDataProvider,
- Function<IBinder, TListener> mapBinderToListener, int uid, boolean foreground) {
- for (Entry<IBinder, ? extends LinkedListenerBase> entry : gnssDataListeners.entrySet()) {
- LinkedListenerBase linkedListener = entry.getValue();
- CallerIdentity callerIdentity = linkedListener.mCallerIdentity;
- if (callerIdentity.mUid != uid) {
- continue;
- }
-
- if (D) {
- Log.d(TAG, linkedListener.mListenerName + " from uid "
- + uid + " is now " + foregroundAsString(foreground));
- }
-
- TListener listener = mapBinderToListener.apply(entry.getKey());
- if (foreground || isThrottlingExemptLocked(callerIdentity)) {
- gnssDataProvider.addListener(listener, callerIdentity);
- } else {
- gnssDataProvider.removeListener(listener);
- }
- }
- }
-
- private static String foregroundAsString(boolean foreground) {
- return foreground ? "foreground" : "background";
- }
-
- private static boolean isImportanceForeground(int importance) {
- return importance <= FOREGROUND_IMPORTANCE_CUTOFF;
}
@GuardedBy("mLock")
@@ -771,28 +689,15 @@ public class LocationManagerService extends ILocationManager.Stub {
mPassiveProvider = new PassiveProvider(mContext, passiveProviderManager);
passiveProviderManager.attachLocked(mPassiveProvider);
- if (GnssLocationProvider.isSupported()) {
- // Create a gps location provider
+ if (GnssManagerService.isGnssSupported()) {
+ // Create a gps location provider manager
LocationProvider gnssProviderManager = new LocationProvider(GPS_PROVIDER, true);
mRealProviders.add(gnssProviderManager);
addProviderLocked(gnssProviderManager);
- GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext,
- gnssProviderManager,
- mHandler.getLooper());
- gnssProviderManager.attachLocked(gnssProvider);
-
- mGnssSystemInfoProvider = gnssProvider.getGnssSystemInfoProvider();
- mGnssBatchingProvider = gnssProvider.getGnssBatchingProvider();
- mGnssMetricsProvider = gnssProvider.getGnssMetricsProvider();
- mGnssCapabilitiesProvider = gnssProvider.getGnssCapabilitiesProvider();
- mGnssStatusProvider = gnssProvider.getGnssStatusProvider();
- mNetInitiatedListener = gnssProvider.getNetInitiatedListener();
- mGnssMeasurementsProvider = gnssProvider.getGnssMeasurementsProvider();
- mGnssMeasurementCorrectionsProvider =
- gnssProvider.getGnssMeasurementCorrectionsProvider();
- mGnssNavigationMessageProvider = gnssProvider.getGnssNavigationMessageProvider();
- mGpsGeofenceProxy = gnssProvider.getGpsGeofenceProxy();
+ mGnssManagerService = new GnssManagerService(this, mContext, gnssProviderManager,
+ mLocationUsageLogger);
+ gnssProviderManager.attachLocked(mGnssManagerService.getGnssLocationProvider());
}
/*
@@ -860,15 +765,17 @@ public class LocationManagerService extends ILocationManager.Stub {
Slog.e(TAG, "no geocoder provider found");
}
- // bind to geofence provider
- GeofenceProxy provider = GeofenceProxy.createAndBind(
- mContext, com.android.internal.R.bool.config_enableGeofenceOverlay,
- com.android.internal.R.string.config_geofenceProviderPackageName,
- com.android.internal.R.array.config_locationProviderPackageNames,
- mGpsGeofenceProxy,
- null);
- if (provider == null) {
- Slog.d(TAG, "Unable to bind FLP Geofence proxy.");
+ if (mGnssManagerService != null) {
+ // bind to geofence provider
+ GeofenceProxy provider = GeofenceProxy.createAndBind(
+ mContext, com.android.internal.R.bool.config_enableGeofenceOverlay,
+ com.android.internal.R.string.config_geofenceProviderPackageName,
+ com.android.internal.R.array.config_locationProviderPackageNames,
+ mGnssManagerService.getGpsGeofenceProxy(),
+ null);
+ if (provider == null) {
+ Slog.d(TAG, "Unable to bind FLP Geofence proxy.");
+ }
}
mComboNlpPackageName = resources.getString(
@@ -949,7 +856,10 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
- private class LocationProvider implements AbstractLocationProvider.LocationProviderManager {
+ /**
+ * Location provider manager, manages a LocationProvider.
+ */
+ class LocationProvider implements AbstractLocationProvider.LocationProviderManager {
private final String mName;
@@ -958,7 +868,8 @@ public class LocationManagerService extends ILocationManager.Stub {
// remember to clear binder identity before invoking any provider operation
@GuardedBy("mLock")
- @Nullable protected AbstractLocationProvider mProvider;
+ @Nullable
+ protected AbstractLocationProvider mProvider;
@GuardedBy("mLock")
private boolean mUseable; // combined state
@@ -968,7 +879,8 @@ public class LocationManagerService extends ILocationManager.Stub {
private boolean mEnabled; // state of provider
@GuardedBy("mLock")
- @Nullable private ProviderProperties mProperties;
+ @Nullable
+ private ProviderProperties mProperties;
private LocationProvider(String name) {
this(name, false);
@@ -1098,6 +1010,12 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public void onReportLocation(Location location) {
+ // likelihood of a 0,0 bug is far greater than this being a valid location
+ if (!isMock() && location.getLatitude() == 0 && location.getLongitude() == 0) {
+ Slog.w(TAG, "blocking 0,0 location from " + mName + " provider");
+ return;
+ }
+
synchronized (mLock) {
handleLocationChangedLocked(location, this);
}
@@ -1105,6 +1023,9 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public void onReportLocation(List<Location> locations) {
+ if (mGnssManagerService == null) {
+ return;
+ }
synchronized (mLock) {
LocationProvider gpsProvider = getLocationProviderLocked(GPS_PROVIDER);
if (gpsProvider == null || !gpsProvider.isUseableLocked()) {
@@ -1112,16 +1033,7 @@ public class LocationManagerService extends ILocationManager.Stub {
return;
}
- if (mGnssBatchingCallback == null) {
- Slog.e(TAG, "reportLocationBatch() called without active Callback");
- return;
- }
-
- try {
- mGnssBatchingCallback.onLocationBatch(locations);
- } catch (RemoteException e) {
- Slog.e(TAG, "mGnssBatchingCallback.onLocationBatch failed", e);
- }
+ mGnssManagerService.onReportLocation(locations);
}
}
@@ -1310,7 +1222,8 @@ public class LocationManagerService extends ILocationManager.Stub {
* A wrapper class holding either an ILocationListener or a PendingIntent to receive
* location updates.
*/
- private final class Receiver extends LinkedListenerBase implements PendingIntent.OnFinished {
+ private final class Receiver extends LocationManagerServiceUtils.LinkedListenerBase implements
+ PendingIntent.OnFinished {
private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000;
private final int mAllowedResolutionLevel; // resolution level allowed to receiver
@@ -1330,8 +1243,10 @@ public class LocationManagerService extends ILocationManager.Stub {
PowerManager.WakeLock mWakeLock;
private Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
- String packageName, WorkSource workSource, boolean hideFromAppOps) {
- super(new CallerIdentity(uid, pid, packageName), "LocationListener");
+ String packageName, @Nullable String featureId, WorkSource workSource,
+ boolean hideFromAppOps, @NonNull String listenerIdentifier) {
+ super(new CallerIdentity(uid, pid, packageName, featureId, listenerIdentifier),
+ "LocationListener");
mListener = listener;
mPendingIntent = intent;
if (listener != null) {
@@ -1455,7 +1370,8 @@ public class LocationManagerService extends ILocationManager.Stub {
if (!currentlyMonitoring) {
if (allowMonitoring) {
return mAppOps.startOpNoThrow(op, mCallerIdentity.mUid,
- mCallerIdentity.mPackageName) == AppOpsManager.MODE_ALLOWED;
+ mCallerIdentity.mPackageName, false, mCallerIdentity.mFeatureId, null)
+ == AppOpsManager.MODE_ALLOWED;
}
} else {
if (!allowMonitoring
@@ -1624,146 +1540,48 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public int getGnssYearOfHardware() {
- if (mGnssSystemInfoProvider != null) {
- return mGnssSystemInfoProvider.getGnssYearOfHardware();
- } else {
- return 0;
- }
+ return mGnssManagerService == null ? 0 : mGnssManagerService.getGnssYearOfHardware();
}
@Override
@Nullable
public String getGnssHardwareModelName() {
- if (mGnssSystemInfoProvider != null) {
- return mGnssSystemInfoProvider.getGnssHardwareModelName();
- } else {
- return null;
- }
- }
-
- private boolean hasGnssPermissions(String packageName) {
- synchronized (mLock) {
- int allowedResolutionLevel = getCallerAllowedResolutionLevel();
- checkResolutionLevelIsSufficientForProviderUseLocked(
- allowedResolutionLevel,
- GPS_PROVIDER);
-
- int pid = Binder.getCallingPid();
- int uid = Binder.getCallingUid();
- long identity = Binder.clearCallingIdentity();
- try {
- return checkLocationAccess(pid, uid, packageName, allowedResolutionLevel);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
+ return mGnssManagerService == null ? "" : mGnssManagerService.getGnssHardwareModelName();
}
@Override
public int getGnssBatchSize(String packageName) {
- mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
- "Location Hardware permission not granted to access hardware batching");
-
- if (hasGnssPermissions(packageName) && mGnssBatchingProvider != null) {
- return mGnssBatchingProvider.getBatchSize();
- } else {
- return 0;
- }
+ return mGnssManagerService == null ? 0 : mGnssManagerService.getGnssBatchSize(packageName);
}
@Override
- public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName) {
- mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
- "Location Hardware permission not granted to access hardware batching");
-
- if (!hasGnssPermissions(packageName) || mGnssBatchingProvider == null) {
- return false;
- }
+ public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName,
+ String featureId, String listenerIdentifier) {
+ Preconditions.checkNotNull(listenerIdentifier);
- CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(),
- Binder.getCallingPid(), packageName);
- synchronized (mLock) {
- mGnssBatchingCallback = callback;
- mGnssBatchingDeathCallback = new LinkedListener<>(callback,
- "BatchedLocationCallback", callerIdentity,
- (IBatchedLocationCallback listener) -> {
- stopGnssBatch();
- removeGnssBatchingCallback();
- });
- if (!linkToListenerDeathNotificationLocked(callback.asBinder(),
- mGnssBatchingDeathCallback)) {
- return false;
- }
- return true;
- }
+ return mGnssManagerService == null ? false : mGnssManagerService.addGnssBatchingCallback(
+ callback, packageName, featureId, listenerIdentifier);
}
@Override
public void removeGnssBatchingCallback() {
- synchronized (mLock) {
- unlinkFromListenerDeathNotificationLocked(mGnssBatchingCallback.asBinder(),
- mGnssBatchingDeathCallback);
- mGnssBatchingCallback = null;
- mGnssBatchingDeathCallback = null;
- }
+ if (mGnssManagerService != null) mGnssManagerService.removeGnssBatchingCallback();
}
@Override
public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName) {
- mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
- "Location Hardware permission not granted to access hardware batching");
-
- if (!hasGnssPermissions(packageName) || mGnssBatchingProvider == null) {
- return false;
- }
-
- synchronized (mLock) {
- if (mGnssBatchingInProgress) {
- // Current design does not expect multiple starts to be called repeatedly
- Log.e(TAG, "startGnssBatch unexpectedly called w/o stopping prior batch");
- // Try to clean up anyway, and continue
- stopGnssBatch();
- }
-
- mGnssBatchingInProgress = true;
- return mGnssBatchingProvider.start(periodNanos, wakeOnFifoFull);
- }
+ return mGnssManagerService == null ? false : mGnssManagerService.startGnssBatch(periodNanos,
+ wakeOnFifoFull, packageName);
}
@Override
public void flushGnssBatch(String packageName) {
- mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
- "Location Hardware permission not granted to access hardware batching");
-
- if (!hasGnssPermissions(packageName)) {
- Log.e(TAG, "flushGnssBatch called without GNSS permissions");
- return;
- }
-
- synchronized (mLock) {
- if (!mGnssBatchingInProgress) {
- Log.w(TAG, "flushGnssBatch called with no batch in progress");
- }
-
- if (mGnssBatchingProvider != null) {
- mGnssBatchingProvider.flush();
- }
- }
+ if (mGnssManagerService != null) mGnssManagerService.flushGnssBatch(packageName);
}
@Override
public boolean stopGnssBatch() {
- mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
- "Location Hardware permission not granted to access hardware batching");
-
- synchronized (mLock) {
- if (mGnssBatchingProvider != null) {
- mGnssBatchingInProgress = false;
- return mGnssBatchingProvider.stop();
- } else {
- return false;
- }
- }
+ return mGnssManagerService == null ? false : mGnssManagerService.stopGnssBatch();
}
@GuardedBy("mLock")
@@ -1904,11 +1722,12 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
- private boolean reportLocationAccessNoThrow(
- int pid, int uid, String packageName, int allowedResolutionLevel) {
+ private boolean reportLocationAccessNoThrow(int pid, int uid, String packageName,
+ @Nullable String featureId, int allowedResolutionLevel, @Nullable String message) {
int op = resolutionLevelToOp(allowedResolutionLevel);
if (op >= 0) {
- if (mAppOps.noteOpNoThrow(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
+ if (mAppOps.noteOpNoThrow(op, uid, packageName, featureId, message)
+ != AppOpsManager.MODE_ALLOWED) {
return false;
}
}
@@ -2201,7 +2020,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@GuardedBy("mLock")
- private boolean isThrottlingExemptLocked(CallerIdentity callerIdentity) {
+ public boolean isThrottlingExemptLocked(CallerIdentity callerIdentity) {
if (callerIdentity.mUid == Process.SYSTEM_UID) {
return true;
}
@@ -2246,8 +2065,10 @@ public class LocationManagerService extends ILocationManager.Stub {
mRealRequest = request;
mRequest = request;
mReceiver = receiver;
- mIsForegroundUid = isImportanceForeground(
- mActivityManager.getPackageImportance(mReceiver.mCallerIdentity.mPackageName));
+ mIsForegroundUid =
+ LocationManagerServiceUtils.isImportanceForeground(
+ mActivityManager.getPackageImportance(
+ mReceiver.mCallerIdentity.mPackageName));
if (D && receiver.mCallerIdentity.mPid == Process.myPid()) {
mStackTrace = new Throwable();
@@ -2339,14 +2160,15 @@ public class LocationManagerService extends ILocationManager.Stub {
@GuardedBy("mLock")
private Receiver getReceiverLocked(ILocationListener listener, int pid, int uid,
- String packageName, WorkSource workSource, boolean hideFromAppOps) {
+ String packageName, @Nullable String featureId, WorkSource workSource,
+ boolean hideFromAppOps, @NonNull String listenerIdentifier) {
IBinder binder = listener.asBinder();
Receiver receiver = mReceivers.get(binder);
if (receiver == null) {
- receiver = new Receiver(listener, null, pid, uid, packageName, workSource,
- hideFromAppOps);
- if (!linkToListenerDeathNotificationLocked(receiver.getListener().asBinder(),
- receiver)) {
+ receiver = new Receiver(listener, null, pid, uid, packageName, featureId, workSource,
+ hideFromAppOps, listenerIdentifier);
+ if (!receiver.linkToListenerDeathNotificationLocked(
+ receiver.getListener().asBinder())) {
return null;
}
mReceivers.put(binder, receiver);
@@ -2356,11 +2178,12 @@ public class LocationManagerService extends ILocationManager.Stub {
@GuardedBy("mLock")
private Receiver getReceiverLocked(PendingIntent intent, int pid, int uid, String packageName,
- WorkSource workSource, boolean hideFromAppOps) {
+ @Nullable String featureId, WorkSource workSource, boolean hideFromAppOps,
+ @NonNull String listenerIdentifier) {
Receiver receiver = mReceivers.get(intent);
if (receiver == null) {
- receiver = new Receiver(null, intent, pid, uid, packageName, workSource,
- hideFromAppOps);
+ receiver = new Receiver(null, intent, pid, uid, packageName, featureId, workSource,
+ hideFromAppOps, listenerIdentifier);
mReceivers.put(intent, receiver);
}
return receiver;
@@ -2422,7 +2245,10 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
- PendingIntent intent, String packageName) {
+ PendingIntent intent, String packageName, String featureId,
+ String listenerIdentifier) {
+ Preconditions.checkNotNull(listenerIdentifier);
+
synchronized (mLock) {
if (request == null) request = DEFAULT_LOCATION_REQUEST;
checkPackageName(packageName);
@@ -2476,11 +2302,11 @@ public class LocationManagerService extends ILocationManager.Stub {
Receiver receiver;
if (intent != null) {
- receiver = getReceiverLocked(intent, pid, uid, packageName, workSource,
- hideFromAppOps);
+ receiver = getReceiverLocked(intent, pid, uid, packageName, featureId,
+ workSource, hideFromAppOps, listenerIdentifier);
} else {
- receiver = getReceiverLocked(listener, pid, uid, packageName, workSource,
- hideFromAppOps);
+ receiver = getReceiverLocked(listener, pid, uid, packageName, featureId,
+ workSource, hideFromAppOps, listenerIdentifier);
}
requestLocationUpdatesLocked(sanitizedRequest, receiver, uid, packageName);
} finally {
@@ -2549,9 +2375,10 @@ public class LocationManagerService extends ILocationManager.Stub {
synchronized (mLock) {
Receiver receiver;
if (intent != null) {
- receiver = getReceiverLocked(intent, pid, uid, packageName, null, false);
+ receiver = getReceiverLocked(intent, pid, uid, packageName, null, null, false, "");
} else {
- receiver = getReceiverLocked(listener, pid, uid, packageName, null, false);
+ receiver = getReceiverLocked(listener, pid, uid, packageName, null, null, false,
+ "");
}
long identity = Binder.clearCallingIdentity();
@@ -2568,8 +2395,8 @@ public class LocationManagerService extends ILocationManager.Stub {
if (D) Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver)));
if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
- unlinkFromListenerDeathNotificationLocked(receiver.getListener().asBinder(),
- receiver);
+ receiver.unlinkFromListenerDeathNotificationLocked(
+ receiver.getListener().asBinder());
receiver.clearPendingBroadcastsLocked();
}
@@ -2595,7 +2422,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
- public Location getLastLocation(LocationRequest r, String packageName) {
+ public Location getLastLocation(LocationRequest r, String packageName, String featureId) {
synchronized (mLock) {
LocationRequest request = r != null ? r : DEFAULT_LOCATION_REQUEST;
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
@@ -2616,7 +2443,6 @@ public class LocationManagerService extends ILocationManager.Stub {
return null;
}
-
// Figure out the provider. Either its explicitly request (deprecated API's),
// or use the fused provider
String name = request.getProvider();
@@ -2648,8 +2474,8 @@ public class LocationManagerService extends ILocationManager.Stub {
// Don't return stale location to apps with foreground-only location permission.
String op = resolutionLevelToOpStr(allowedResolutionLevel);
- long locationAgeMs = SystemClock.elapsedRealtime()
- - location.getElapsedRealtimeNanos() / NANOS_PER_MILLI;
+ long locationAgeMs = TimeUnit.NANOSECONDS.toMillis(
+ SystemClock.elapsedRealtime() - location.getElapsedRealtimeNanos());
if ((locationAgeMs > Settings.Global.getLong(
mContext.getContentResolver(),
Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
@@ -2671,12 +2497,12 @@ public class LocationManagerService extends ILocationManager.Stub {
}
// Don't report location access if there is no last location to deliver.
if (lastLocation != null) {
- if (!reportLocationAccessNoThrow(
- pid, uid, packageName, allowedResolutionLevel)) {
+ if (!reportLocationAccessNoThrow(pid, uid, packageName, featureId,
+ allowedResolutionLevel, null)) {
if (D) {
Log.d(TAG, "not returning last loc for no op app: " + packageName);
}
- lastLocation = null;
+ lastLocation = null;
}
}
return lastLocation;
@@ -2687,6 +2513,57 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
+ public boolean getCurrentLocation(LocationRequest locationRequest,
+ ICancellationSignal remoteCancellationSignal, ILocationListener listener,
+ String packageName, String featureId, String listenerIdentifier) {
+ // side effect of validating locationRequest and packageName
+ Location lastLocation = getLastLocation(locationRequest, packageName, featureId);
+ if (lastLocation != null) {
+ long locationAgeMs = TimeUnit.NANOSECONDS.toMillis(
+ SystemClock.elapsedRealtimeNanos() - lastLocation.getElapsedRealtimeNanos());
+
+ long identity = Binder.clearCallingIdentity();
+ try {
+ if (locationAgeMs < MAX_CURRENT_LOCATION_AGE_MS) {
+ try {
+ listener.onLocationChanged(lastLocation);
+ return true;
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ return false;
+ }
+ }
+
+ // packageName already validated by getLastLocation() call above
+ boolean foreground = LocationManagerServiceUtils.isImportanceForeground(
+ mActivityManager.getPackageImportance(packageName));
+ if (!foreground) {
+ long backgroundThrottleIntervalMs = Settings.Global.getLong(
+ mContext.getContentResolver(),
+ Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
+ DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS);
+ if (locationAgeMs < backgroundThrottleIntervalMs) {
+ // not allowed to request new locations, so we can't return anything
+ return false;
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ requestLocationUpdates(locationRequest, listener, null, packageName, featureId,
+ listenerIdentifier);
+ CancellationSignal cancellationSignal = CancellationSignal.fromTransport(
+ remoteCancellationSignal);
+ if (cancellationSignal != null) {
+ cancellationSignal.setOnCancelListener(
+ () -> removeUpdates(listener, null, packageName));
+ }
+ return true;
+ }
+
+ @Override
public LocationTime getGnssTimeMillis() {
synchronized (mLock) {
Location location = mLastLocation.get(LocationManager.GPS_PROVIDER);
@@ -2706,13 +2583,6 @@ public class LocationManagerService extends ILocationManager.Stub {
mContext.enforceCallingPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
"Access Fine Location permission not granted to inject Location");
- if (location == null) {
- if (D) {
- Log.d(TAG, "injectLocation(): called with null location");
- }
- return false;
- }
-
synchronized (mLock) {
LocationProvider provider = getLocationProviderLocked(location.getProvider());
if (provider == null || !provider.isUseableLocked()) {
@@ -2733,7 +2603,9 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent,
- String packageName) {
+ String packageName, String featureId, String listenerIdentifier) {
+ Preconditions.checkNotNull(listenerIdentifier);
+
if (request == null) request = DEFAULT_LOCATION_REQUEST;
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
checkResolutionLevelIsSufficientForGeofenceUse(allowedResolutionLevel);
@@ -2778,9 +2650,8 @@ public class LocationManagerService extends ILocationManager.Stub {
mActivityManager.getPackageImportance(packageName));
}
- mGeofenceManager.addFence(sanitizedRequest, geofence, intent,
- allowedResolutionLevel,
- uid, packageName);
+ mGeofenceManager.addFence(sanitizedRequest, geofence, intent, allowedResolutionLevel,
+ uid, packageName, featureId, listenerIdentifier);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -2816,220 +2687,66 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
- public boolean registerGnssStatusCallback(IGnssStatusListener listener, String packageName) {
- return addGnssDataListener(listener, packageName, "GnssStatusListener",
- mGnssStatusProvider, mGnssStatusListeners,
- this::unregisterGnssStatusCallback);
+ public boolean registerGnssStatusCallback(IGnssStatusListener listener, String packageName,
+ String featureId) {
+ return mGnssManagerService == null ? false : mGnssManagerService.registerGnssStatusCallback(
+ listener, packageName, featureId);
}
@Override
public void unregisterGnssStatusCallback(IGnssStatusListener listener) {
- removeGnssDataListener(listener, mGnssStatusProvider, mGnssStatusListeners);
+ if (mGnssManagerService != null) mGnssManagerService.unregisterGnssStatusCallback(listener);
}
@Override
- public boolean addGnssMeasurementsListener(
- IGnssMeasurementsListener listener, String packageName) {
- return addGnssDataListener(listener, packageName, "GnssMeasurementsListener",
- mGnssMeasurementsProvider, mGnssMeasurementsListeners,
- this::removeGnssMeasurementsListener);
+ public boolean addGnssMeasurementsListener(IGnssMeasurementsListener listener,
+ String packageName, String featureId, String listenerIdentifier) {
+ Preconditions.checkNotNull(listenerIdentifier);
+
+ return mGnssManagerService == null ? false
+ : mGnssManagerService.addGnssMeasurementsListener(listener, packageName, featureId,
+ listenerIdentifier);
}
@Override
public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
- removeGnssDataListener(listener, mGnssMeasurementsProvider, mGnssMeasurementsListeners);
- }
-
- private abstract static class LinkedListenerBase implements IBinder.DeathRecipient {
- protected final CallerIdentity mCallerIdentity;
- protected final String mListenerName;
-
- private LinkedListenerBase(@NonNull CallerIdentity callerIdentity,
- @NonNull String listenerName) {
- mCallerIdentity = callerIdentity;
- mListenerName = listenerName;
- }
-
- @Override
- public String toString() {
- return mListenerName + "[" + mCallerIdentity.mPackageName + "(" + mCallerIdentity.mPid
- + ")]";
- }
- }
-
- private static class LinkedListener<TListener> extends LinkedListenerBase {
- private final TListener mListener;
- private final Consumer<TListener> mBinderDeathCallback;
-
- private LinkedListener(@NonNull TListener listener, String listenerName,
- @NonNull CallerIdentity callerIdentity,
- @NonNull Consumer<TListener> binderDeathCallback) {
- super(callerIdentity, listenerName);
- mListener = listener;
- mBinderDeathCallback = binderDeathCallback;
- }
-
- @Override
- public void binderDied() {
- if (D) Log.d(TAG, "Remote " + mListenerName + " died.");
- mBinderDeathCallback.accept(mListener);
- }
- }
-
- private <TListener extends IInterface> boolean addGnssDataListener(
- TListener listener, String packageName, String listenerName,
- RemoteListenerHelper<TListener> gnssDataProvider,
- ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners,
- Consumer<TListener> binderDeathCallback) {
- if (!hasGnssPermissions(packageName) || gnssDataProvider == null) {
- return false;
- }
-
- CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(),
- Binder.getCallingPid(), packageName);
- LinkedListener<TListener> linkedListener = new LinkedListener<>(listener,
- listenerName, callerIdentity, binderDeathCallback);
- IBinder binder = listener.asBinder();
- synchronized (mLock) {
- if (!linkToListenerDeathNotificationLocked(binder, linkedListener)) {
- return false;
- }
-
- gnssDataListeners.put(binder, linkedListener);
- long identity = Binder.clearCallingIdentity();
- try {
- if (gnssDataProvider == mGnssMeasurementsProvider
- || gnssDataProvider == mGnssStatusProvider) {
- mLocationUsageLogger.logLocationApiUsage(
- LocationStatsEnums.USAGE_STARTED,
- gnssDataProvider == mGnssMeasurementsProvider
- ? LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER
- : LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK,
- packageName,
- /* LocationRequest= */ null,
- /* hasListener= */ true,
- /* hasIntent= */ false,
- /* geofence= */ null,
- mActivityManager.getPackageImportance(packageName));
- }
- if (isThrottlingExemptLocked(callerIdentity)
- || isImportanceForeground(
- mActivityManager.getPackageImportance(packageName))) {
- gnssDataProvider.addListener(listener, callerIdentity);
- }
- return true;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- }
-
- private <TListener extends IInterface> void removeGnssDataListener(
- TListener listener, RemoteListenerHelper<TListener> gnssDataProvider,
- ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners) {
- if (gnssDataProvider == null) {
- return;
- }
-
- IBinder binder = listener.asBinder();
- synchronized (mLock) {
- LinkedListener<TListener> linkedListener = gnssDataListeners.remove(binder);
- if (linkedListener == null) {
- return;
- }
- long identity = Binder.clearCallingIdentity();
- try {
- if (gnssDataProvider == mGnssMeasurementsProvider
- || gnssDataProvider == mGnssStatusProvider) {
- mLocationUsageLogger.logLocationApiUsage(
- LocationStatsEnums.USAGE_ENDED,
- gnssDataProvider == mGnssMeasurementsProvider
- ? LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER
- : LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK,
- linkedListener.mCallerIdentity.mPackageName,
- /* LocationRequest= */ null,
- /* hasListener= */ true,
- /* hasIntent= */ false,
- /* geofence= */ null,
- mActivityManager.getPackageImportance(
- linkedListener.mCallerIdentity.mPackageName));
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- unlinkFromListenerDeathNotificationLocked(binder, linkedListener);
- gnssDataProvider.removeListener(listener);
- }
- }
-
- private boolean linkToListenerDeathNotificationLocked(IBinder binder,
- LinkedListenerBase linkedListener) {
- try {
- binder.linkToDeath(linkedListener, 0 /* flags */);
- return true;
- } catch (RemoteException e) {
- // if the remote process registering the listener is already dead, just swallow the
- // exception and return
- Log.w(TAG, "Could not link " + linkedListener.mListenerName + " death callback.", e);
- return false;
- }
- }
-
- private boolean unlinkFromListenerDeathNotificationLocked(IBinder binder,
- LinkedListenerBase linkedListener) {
- try {
- binder.unlinkToDeath(linkedListener, 0 /* flags */);
- return true;
- } catch (NoSuchElementException e) {
- // if the death callback isn't connected (it should be...), log error,
- // swallow the exception and return
- Log.w(TAG, "Could not unlink " + linkedListener.mListenerName + " death callback.", e);
- return false;
+ if (mGnssManagerService != null) {
+ mGnssManagerService.removeGnssMeasurementsListener(
+ listener);
}
}
@Override
public void injectGnssMeasurementCorrections(
GnssMeasurementCorrections measurementCorrections, String packageName) {
- mContext.enforceCallingPermission(
- android.Manifest.permission.LOCATION_HARDWARE,
- "Location Hardware permission not granted to inject GNSS measurement corrections.");
- if (!hasGnssPermissions(packageName)) {
- Slog.e(TAG, "Can not inject GNSS corrections due to no permission.");
- return;
- }
- if (mGnssMeasurementCorrectionsProvider == null) {
- Slog.e(TAG, "Can not inject GNSS corrections. GNSS measurement corrections provider "
- + "not available.");
- return;
+ if (mGnssManagerService != null) {
+ mGnssManagerService.injectGnssMeasurementCorrections(
+ measurementCorrections, packageName);
}
- mGnssMeasurementCorrectionsProvider.injectGnssMeasurementCorrections(
- measurementCorrections);
}
@Override
public long getGnssCapabilities(String packageName) {
- mContext.enforceCallingPermission(
- android.Manifest.permission.LOCATION_HARDWARE,
- "Location Hardware permission not granted to obtain GNSS chipset capabilities.");
- if (!hasGnssPermissions(packageName) || mGnssCapabilitiesProvider == null) {
- return GnssCapabilities.INVALID_CAPABILITIES;
- }
- return mGnssCapabilitiesProvider.getGnssCapabilities();
+ return mGnssManagerService == null ? 0L : mGnssManagerService.getGnssCapabilities(
+ packageName);
}
@Override
- public boolean addGnssNavigationMessageListener(
- IGnssNavigationMessageListener listener, String packageName) {
- return addGnssDataListener(listener, packageName, "GnssNavigationMessageListener",
- mGnssNavigationMessageProvider, mGnssNavigationMessageListeners,
- this::removeGnssNavigationMessageListener);
+ public boolean addGnssNavigationMessageListener(IGnssNavigationMessageListener listener,
+ String packageName, String featureId, String listenerIdentifier) {
+ Preconditions.checkNotNull(listenerIdentifier);
+
+ return mGnssManagerService == null ? false
+ : mGnssManagerService.addGnssNavigationMessageListener(listener, packageName,
+ featureId, listenerIdentifier);
}
@Override
public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) {
- removeGnssDataListener(listener, mGnssNavigationMessageProvider,
- mGnssNavigationMessageListeners);
+ if (mGnssManagerService != null) {
+ mGnssManagerService.removeGnssNavigationMessageListener(
+ listener);
+ }
}
@Override
@@ -3069,24 +2786,13 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public boolean sendNiResponse(int notifId, int userResponse) {
- if (Binder.getCallingUid() != Process.myUid()) {
- throw new SecurityException(
- "calling sendNiResponse from outside of the system is not allowed");
- }
- try {
- return mNetInitiatedListener.sendNiResponse(notifId, userResponse);
- } catch (RemoteException e) {
- Slog.e(TAG, "RemoteException in LocationManagerService.sendNiResponse");
- return false;
- }
+ return mGnssManagerService == null ? false : mGnssManagerService.sendNiResponse(notifId,
+ userResponse);
}
@Override
public ProviderProperties getProviderProperties(String providerName) {
synchronized (mLock) {
- checkResolutionLevelIsSufficientForProviderUseLocked(getCallerAllowedResolutionLevel(),
- providerName);
-
LocationProvider provider = getLocationProviderLocked(providerName);
if (provider == null) {
return null;
@@ -3105,12 +2811,21 @@ public class LocationManagerService extends ILocationManager.Stub {
return true;
}
}
-
return false;
}
}
@Override
+ public List<String> getProviderPackages(String providerName) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_DEVICE_CONFIG,
+ Manifest.permission.READ_DEVICE_CONFIG + " permission required");
+ synchronized (mLock) {
+ LocationProvider provider = getLocationProviderLocked(providerName);
+ return provider == null ? Collections.emptyList() : provider.getPackagesLocked();
+ }
+ }
+
+ @Override
public void setExtraLocationControllerPackage(String packageName) {
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
Manifest.permission.LOCATION_HARDWARE + " permission required");
@@ -3159,10 +2874,10 @@ public class LocationManagerService extends ILocationManager.Stub {
long identity = Binder.clearCallingIdentity();
try {
return Settings.Secure.getIntForUser(
- mContext.getContentResolver(),
- Settings.Secure.LOCATION_MODE,
- Settings.Secure.LOCATION_MODE_OFF,
- userId) != Settings.Secure.LOCATION_MODE_OFF;
+ mContext.getContentResolver(),
+ Settings.Secure.LOCATION_MODE,
+ Settings.Secure.LOCATION_MODE_OFF,
+ userId) != Settings.Secure.LOCATION_MODE_OFF;
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -3197,9 +2912,9 @@ public class LocationManagerService extends ILocationManager.Stub {
// Check whether sufficient time has passed
long minTime = record.mRealRequest.getFastestInterval();
- long delta = (loc.getElapsedRealtimeNanos() - lastLoc.getElapsedRealtimeNanos())
- / NANOS_PER_MILLI;
- if (delta < minTime - MAX_PROVIDER_SCHEDULING_JITTER_MS) {
+ long deltaMs = TimeUnit.NANOSECONDS.toMillis(
+ loc.getElapsedRealtimeNanos() - lastLoc.getElapsedRealtimeNanos());
+ if (deltaMs < minTime - MAX_PROVIDER_SCHEDULING_JITTER_MS) {
return false;
}
@@ -3254,9 +2969,9 @@ public class LocationManagerService extends ILocationManager.Stub {
mLastLocationCoarseInterval.put(provider.getName(), lastLocationCoarseInterval);
}
}
- long timeDiffNanos = location.getElapsedRealtimeNanos()
- - lastLocationCoarseInterval.getElapsedRealtimeNanos();
- if (timeDiffNanos > LocationFudger.FASTEST_INTERVAL_MS * NANOS_PER_MILLI) {
+ long timeDeltaMs = TimeUnit.NANOSECONDS.toMillis(location.getElapsedRealtimeNanos()
+ - lastLocationCoarseInterval.getElapsedRealtimeNanos());
+ if (timeDeltaMs > LocationFudger.FASTEST_INTERVAL_MS) {
lastLocationCoarseInterval.set(location);
}
// Don't ever return a coarse location that is more recent than the allowed update
@@ -3329,7 +3044,9 @@ public class LocationManagerService extends ILocationManager.Stub {
receiver.mCallerIdentity.mPid,
receiver.mCallerIdentity.mUid,
receiver.mCallerIdentity.mPackageName,
- receiver.mAllowedResolutionLevel)) {
+ receiver.mCallerIdentity.mFeatureId,
+ receiver.mAllowedResolutionLevel,
+ "Location sent to " + receiver.mCallerIdentity.mListenerIdentifier)) {
if (D) {
Log.d(TAG, "skipping loc update for no op app: "
+ receiver.mCallerIdentity.mPackageName);
@@ -3450,11 +3167,6 @@ public class LocationManagerService extends ILocationManager.Stub {
try {
LocationProvider oldProvider = getLocationProviderLocked(name);
if (oldProvider != null) {
- if (oldProvider.isMock()) {
- throw new IllegalArgumentException(
- "Provider \"" + name + "\" already exists");
- }
-
removeProviderLocked(oldProvider);
}
@@ -3479,7 +3191,7 @@ public class LocationManagerService extends ILocationManager.Stub {
try {
LocationProvider testProvider = getLocationProviderLocked(name);
if (testProvider == null || !testProvider.isMock()) {
- throw new IllegalArgumentException("Provider \"" + name + "\" unknown");
+ return;
}
removeProviderLocked(testProvider);
@@ -3576,11 +3288,8 @@ public class LocationManagerService extends ILocationManager.Stub {
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
synchronized (mLock) {
- if (args.length > 0 && args[0].equals("--gnssmetrics")) {
- if (mGnssMetricsProvider != null) {
- pw.append(mGnssMetricsProvider.getGnssMetricsAsProtoString());
- }
- return;
+ if (mGnssManagerService != null && args.length > 0 && args[0].equals("--gnssmetrics")) {
+ mGnssManagerService.dump(fd, pw, args);
}
ipw.println("Location Manager State:");
@@ -3614,27 +3323,6 @@ public class LocationManagerService extends ILocationManager.Stub {
}
ipw.decreaseIndent();
- ipw.println("GnssMeasurement Listeners:");
- ipw.increaseIndent();
- for (LinkedListenerBase listener : mGnssMeasurementsListeners.values()) {
- ipw.println(listener + ": " + isThrottlingExemptLocked(listener.mCallerIdentity));
- }
- ipw.decreaseIndent();
-
- ipw.println("GnssNavigationMessage Listeners:");
- ipw.increaseIndent();
- for (LinkedListenerBase listener : mGnssNavigationMessageListeners.values()) {
- ipw.println(listener + ": " + isThrottlingExemptLocked(listener.mCallerIdentity));
- }
- ipw.decreaseIndent();
-
- ipw.println("GnssStatus Listeners:");
- ipw.increaseIndent();
- for (LinkedListenerBase listener : mGnssStatusListeners.values()) {
- ipw.println(listener + ": " + isThrottlingExemptLocked(listener.mCallerIdentity));
- }
- ipw.decreaseIndent();
-
ipw.println("Historical Records by Provider:");
ipw.increaseIndent();
for (Map.Entry<PackageProviderKey, PackageStatistics> entry
@@ -3664,7 +3352,7 @@ public class LocationManagerService extends ILocationManager.Stub {
mGeofenceManager.dump(ipw);
ipw.decreaseIndent();
}
-
+
if (mBlacklist != null) {
mBlacklist.dump(ipw);
}
@@ -3704,11 +3392,11 @@ public class LocationManagerService extends ILocationManager.Stub {
for (LocationProvider provider : mProviders) {
provider.dumpLocked(fd, ipw, args);
}
- ipw.decreaseIndent();
+ }
- if (mGnssBatchingInProgress) {
- ipw.println("GNSS batching in progress");
- }
+ if (mGnssManagerService != null) {
+ ipw.decreaseIndent();
+ mGnssManagerService.dump(fd, pw, args);
}
}
}
diff --git a/services/core/java/com/android/server/LocationManagerServiceUtils.java b/services/core/java/com/android/server/LocationManagerServiceUtils.java
new file mode 100644
index 000000000000..9c8ac19cc591
--- /dev/null
+++ b/services/core/java/com/android/server/LocationManagerServiceUtils.java
@@ -0,0 +1,163 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.server.location.CallerIdentity;
+
+import java.util.NoSuchElementException;
+import java.util.function.Consumer;
+
+/**
+ * Shared utilities for LocationManagerService and GnssManager.
+ */
+public class LocationManagerServiceUtils {
+
+ private static final String TAG = "LocManagerServiceUtils";
+ private static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
+
+ /**
+ * Listener that can be linked to a binder.
+ * @param <TListener> listener type
+ */
+ public static class LinkedListener<TListener> extends
+ LinkedListenerBase {
+ private final TListener mListener;
+ private final Consumer<TListener> mBinderDeathCallback;
+
+ public LinkedListener(
+ @NonNull TListener listener,
+ String listenerName,
+ @NonNull CallerIdentity callerIdentity,
+ @NonNull Consumer<TListener> binderDeathCallback) {
+ super(callerIdentity, listenerName);
+ mListener = listener;
+ mBinderDeathCallback = binderDeathCallback;
+ }
+
+ @Override
+ public void binderDied() {
+ if (D) Log.d(TAG, "Remote " + mListenerName + " died.");
+ mBinderDeathCallback.accept(mListener);
+ }
+ }
+
+ /**
+ * Skeleton class of listener that can be linked to a binder.
+ */
+ public abstract static class LinkedListenerBase implements IBinder.DeathRecipient {
+ protected final CallerIdentity mCallerIdentity;
+ protected final String mListenerName;
+
+ LinkedListenerBase(
+ @NonNull CallerIdentity callerIdentity, @NonNull String listenerName) {
+ mCallerIdentity = callerIdentity;
+ mListenerName = listenerName;
+ }
+
+ @Override
+ public String toString() {
+ return mListenerName + "[" + mCallerIdentity.mPackageName + "(" + mCallerIdentity.mPid
+ + ")]";
+ }
+
+ public CallerIdentity getCallerIdentity() {
+ return mCallerIdentity;
+ }
+
+ public String getListenerName() {
+ return mListenerName;
+ }
+
+ /**
+ * Link listener (i.e. callback) to a binder, so that it will be called upon binder's death.
+ *
+ * @param binder that calls listener upon death
+ * @return true if listener is successfully linked to binder, false otherwise
+ */
+ public boolean linkToListenerDeathNotificationLocked(
+ IBinder binder) {
+ try {
+ binder.linkToDeath(this, 0 /* flags */);
+ return true;
+ } catch (RemoteException e) {
+ // if the remote process registering the listener is already dead, just swallow the
+ // exception and return
+ Log.w(TAG, "Could not link " + mListenerName + " death callback.", e);
+ return false;
+ }
+ }
+
+ /**
+ * Unlink death listener (i.e. callback) from binder.
+ *
+ * @param binder that calls listener upon death
+ * @return true if binder is successfully unlinked from binder, false otherwise
+ */
+ public boolean unlinkFromListenerDeathNotificationLocked(
+ IBinder binder) {
+ try {
+ binder.unlinkToDeath(this, 0 /* flags */);
+ return true;
+ } catch (NoSuchElementException e) {
+ // if the death callback isn't connected (it should be...), log error,
+ // swallow the exception and return
+ Log.w(TAG, "Could not unlink " + mListenerName + " death callback.", e);
+ return false;
+ }
+ }
+
+ }
+
+ /**
+ * Convert boolean foreground into "foreground" or "background" string.
+ *
+ * @param foreground boolean indicating foreground
+ * @return "foreground" string if true, false otherwise
+ */
+ public static String foregroundAsString(boolean foreground) {
+ return foreground ? "foreground" : "background";
+ }
+
+
+ /**
+ * Classifies importance level as foreground or not.
+ *
+ * @param importance level as int
+ * @return boolean indicating if importance level is foreground or greater
+ */
+ public static boolean isImportanceForeground(int importance) {
+ return importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
+ }
+
+ /**
+ * Get package importance level.
+ *
+ * @param packageName package name
+ * @return package importance level as int
+ */
+ public static int getPackageImportance(String packageName, Context context) {
+ return ((ActivityManager) context.getSystemService(
+ Context.ACTIVITY_SERVICE)).getPackageImportance(packageName);
+ }
+}
diff --git a/services/core/java/com/android/server/MasterClearReceiver.java b/services/core/java/com/android/server/MasterClearReceiver.java
index fa1653db3579..793e34292623 100644
--- a/services/core/java/com/android/server/MasterClearReceiver.java
+++ b/services/core/java/com/android/server/MasterClearReceiver.java
@@ -88,7 +88,7 @@ public class MasterClearReceiver extends BroadcastReceiver {
}
};
- if (mWipeExternalStorage || mWipeEsims) {
+ if (mWipeExternalStorage) {
// thr will be started at the end of this task.
new WipeDataTask(context, thr).execute();
} else {
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index bd5ad960a886..73c852083cfd 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -162,7 +162,7 @@ public class PersistentDataBlockService extends SystemService {
@Override
public void onStart() {
// Do init on a separate thread, will join in PHASE_ACTIVITY_MANAGER_READY
- SystemServerInitThreadPool.get().submit(() -> {
+ SystemServerInitThreadPool.submit(() -> {
mAllowedUid = getAllowedUid(UserHandle.USER_SYSTEM);
enforceChecksumValidity();
formatIfOemUnlockEnabled();
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 598314166e7c..eac0add748e6 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -197,6 +197,9 @@ class StorageManagerService extends IStorageManager.Stub
private static final String ZRAM_ENABLED_PROPERTY =
"persist.sys.zram_enabled";
+ private static final boolean IS_FUSE_ENABLED =
+ SystemProperties.getBoolean(StorageManager.PROP_FUSE, false);
+
private static final boolean ENABLE_ISOLATED_STORAGE = StorageManager.hasIsolatedStorage();
/**
@@ -206,6 +209,12 @@ 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";
+
public static class Lifecycle extends SystemService {
private StorageManagerService mStorageManagerService;
@@ -347,6 +356,9 @@ class StorageManagerService extends IStorageManager.Stub
@GuardedBy("mLock")
private String mMoveTargetUuid;
+ @Nullable
+ private volatile String mMediaStoreAuthorityPackageName = null;
+
private volatile int mCurrentUserId = UserHandle.USER_SYSTEM;
/** Holding lock for AppFuse business */
@@ -809,11 +821,13 @@ class StorageManagerService extends IStorageManager.Stub
}
});
// For now, simply clone property when it changes
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE,
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
mContext.getMainExecutor(), (properties) -> {
refreshIsolatedStorageSettings();
+ refreshFuseSettings();
});
refreshIsolatedStorageSettings();
+ refreshFuseSettings();
}
/**
@@ -849,7 +863,8 @@ class StorageManagerService extends IStorageManager.Stub
// Always copy value from newer DeviceConfig location
Settings.Global.putString(mResolver,
Settings.Global.ISOLATED_STORAGE_REMOTE,
- DeviceConfig.getProperty(DeviceConfig.NAMESPACE_STORAGE, ISOLATED_STORAGE_ENABLED));
+ DeviceConfig.getProperty(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ ISOLATED_STORAGE_ENABLED));
final int local = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ISOLATED_STORAGE_LOCAL, 0);
@@ -876,6 +891,18 @@ class StorageManagerService extends IStorageManager.Stub
SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE, Boolean.toString(res));
}
+ private void refreshFuseSettings() {
+ int isFuseEnabled = DeviceConfig.getInt(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ FUSE_ENABLED, 0);
+ if (isFuseEnabled == 1) {
+ SystemProperties.set(StorageManager.PROP_FUSE, "true");
+ } else if (isFuseEnabled == -1) {
+ SystemProperties.set(StorageManager.PROP_FUSE, "false");
+ }
+ // else, keep the build config.
+ // This can be overridden be direct adjustment of persist.sys.prop
+ }
+
/**
* MediaProvider has a ton of code that makes assumptions about storage
* paths never changing, so we outright kill them to pick up new state.
@@ -1521,6 +1548,9 @@ class StorageManagerService extends IStorageManager.Stub
SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT, Boolean.toString(
SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, true)));
+ SystemProperties.set(StorageManager.PROP_FUSE_SNAPSHOT, Boolean.toString(
+ SystemProperties.getBoolean(StorageManager.PROP_FUSE, false)));
+
mContext = context;
mResolver = mContext.getContentResolver();
@@ -1662,6 +1692,15 @@ class StorageManagerService extends IStorageManager.Stub
ServiceManager.getService("package"));
mIAppOpsService = IAppOpsService.Stub.asInterface(
ServiceManager.getService(Context.APP_OPS_SERVICE));
+
+ ProviderInfo provider = mPmInternal.resolveContentProvider(
+ MediaStore.AUTHORITY, PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ UserHandle.getUserId(UserHandle.USER_SYSTEM));
+ if (provider != null) {
+ mMediaStoreAuthorityPackageName = provider.packageName;
+ }
+
try {
mIAppOpsService.startWatchingMode(OP_REQUEST_INSTALL_PACKAGES, null, mAppOpsCallback);
mIAppOpsService.startWatchingMode(OP_LEGACY_STORAGE, null, mAppOpsCallback);
@@ -1844,7 +1883,7 @@ class StorageManagerService extends IStorageManager.Stub
// This means the mountUserId on such volumes is USER_NULL. This breaks fuse which
// requires a valid user to mount a volume. Create individual volumes per user in vold
// and remove this property check
- int userId = SystemProperties.getBoolean("persist.sys.fuse", false)
+ int userId = SystemProperties.getBoolean(StorageManager.PROP_FUSE_SNAPSHOT, false)
? mCurrentUserId : vol.mountUserId;
return mVold.mount(vol.id, vol.mountFlags, userId);
} catch (Exception e) {
@@ -3705,6 +3744,11 @@ class StorageManagerService extends IStorageManager.Stub
return Zygote.MOUNT_EXTERNAL_NONE;
}
+ if (IS_FUSE_ENABLED && packageName.equals(mMediaStoreAuthorityPackageName)) {
+ // Determine if caller requires pass_through mount
+ return Zygote.MOUNT_EXTERNAL_PASS_THROUGH;
+ }
+
// Determine if caller is holding runtime permission
final boolean hasRead = StorageManager.checkPermissionAndCheckOp(mContext, false, 0,
uid, packageName, READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE);
diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java
index ff6a5375565a..5ed94e380b5b 100644
--- a/services/core/java/com/android/server/SystemServerInitThreadPool.java
+++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java
@@ -16,10 +16,12 @@
package com.android.server;
+import android.annotation.NonNull;
import android.os.Build;
import android.os.Process;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.Preconditions;
import com.android.server.am.ActivityManagerService;
@@ -32,9 +34,11 @@ import java.util.concurrent.TimeUnit;
/**
* Thread pool used during initialization of system server.
+ *
* <p>System services can {@link #submit(Runnable)} tasks for execution during boot.
* The pool will be shut down after {@link SystemService#PHASE_BOOT_COMPLETED}.
- * New tasks <em>should not</em> be submitted afterwards.
+ *
+ * <p>New tasks <em>should not</em> be submitted afterwards.
*
* @hide
*/
@@ -42,26 +46,49 @@ public class SystemServerInitThreadPool {
private static final String TAG = SystemServerInitThreadPool.class.getSimpleName();
private static final int SHUTDOWN_TIMEOUT_MILLIS = 20000;
private static final boolean IS_DEBUGGABLE = Build.IS_DEBUGGABLE;
+ private static final Object LOCK = new Object();
+ @GuardedBy("LOCK")
private static SystemServerInitThreadPool sInstance;
- private ExecutorService mService = ConcurrentUtils.newFixedThreadPool(
- Runtime.getRuntime().availableProcessors(),
- "system-server-init-thread", Process.THREAD_PRIORITY_FOREGROUND);
+ private final ExecutorService mService;
- private List<String> mPendingTasks = new ArrayList<>();
+ @GuardedBy("mPendingTasks")
+ private final List<String> mPendingTasks = new ArrayList<>();
- public static synchronized SystemServerInitThreadPool get() {
- if (sInstance == null) {
- sInstance = new SystemServerInitThreadPool();
+ @GuardedBy("mPendingTasks")
+ private boolean mShutDown;
+
+ private SystemServerInitThreadPool() {
+ final int size = Runtime.getRuntime().availableProcessors();
+ Slog.i(TAG, "Creating instance with " + size + " threads");
+ mService = ConcurrentUtils.newFixedThreadPool(size,
+ "system-server-init-thread", Process.THREAD_PRIORITY_FOREGROUND);
+ }
+
+ /**
+ * Submits a task for execution.
+ *
+ * @throws IllegalStateException if it hasn't been started or has been shut down already.
+ */
+ public static @NonNull Future<?> submit(@NonNull Runnable runnable,
+ @NonNull String description) {
+ Preconditions.checkNotNull(description, "description cannot be null");
+
+ SystemServerInitThreadPool instance;
+ synchronized (LOCK) {
+ Preconditions.checkState(sInstance != null, "Cannot get " + TAG
+ + " - it has been shut down");
+ instance = sInstance;
}
- Preconditions.checkState(sInstance.mService != null, "Cannot get " + TAG
- + " - it has been shut down");
- return sInstance;
+
+ return instance.submitTask(runnable, description);
}
- public Future<?> submit(Runnable runnable, String description) {
+ private @NonNull Future<?> submitTask(@NonNull Runnable runnable,
+ @NonNull String description) {
synchronized (mPendingTasks) {
+ Preconditions.checkState(!mShutDown, TAG + " already shut down");
mPendingTasks.add(description);
}
return mService.submit(() -> {
@@ -83,10 +110,36 @@ public class SystemServerInitThreadPool {
});
}
- static synchronized void shutdown() {
- if (sInstance != null && sInstance.mService != null) {
+ /**
+ * Starts it.
+ *
+ * <p>Note:</p> should only be called by {@link SystemServer}.
+ *
+ * @throws IllegalStateException if it has been started already without being shut down yet.
+ */
+ static void start() {
+ synchronized (LOCK) {
+ Preconditions.checkState(sInstance == null, TAG + " already started");
+ sInstance = new SystemServerInitThreadPool();
+ }
+ }
+
+ /**
+ * Shuts it down.
+ *
+ * <p>Note:</p> should only be called by {@link SystemServer}.
+ */
+ static void shutdown() {
+ synchronized (LOCK) {
+ if (sInstance == null) {
+ Slog.wtf(TAG, "Already shutdown", new Exception());
+ return;
+ }
+ synchronized (sInstance.mPendingTasks) {
+ sInstance.mShutDown = true;
+ }
sInstance.mService.shutdown();
- boolean terminated;
+ final boolean terminated;
try {
terminated = sInstance.mService.awaitTermination(SHUTDOWN_TIMEOUT_MILLIS,
TimeUnit.MILLISECONDS);
@@ -100,7 +153,7 @@ public class SystemServerInitThreadPool {
// in the thread pool.
dumpStackTraces();
}
- List<Runnable> unstartedRunnables = sInstance.mService.shutdownNow();
+ final List<Runnable> unstartedRunnables = sInstance.mService.shutdownNow();
if (!terminated) {
final List<String> copy = new ArrayList<>();
synchronized (sInstance.mPendingTasks) {
@@ -109,8 +162,7 @@ public class SystemServerInitThreadPool {
throw new IllegalStateException("Cannot shutdown. Unstarted tasks "
+ unstartedRunnables + " Unfinished tasks " + copy);
}
- sInstance.mService = null; // Make mService eligible for GC
- sInstance.mPendingTasks = null;
+ sInstance = null; // Make eligible for GC
Slog.d(TAG, "Shutdown successful");
}
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index eb2723a2c7d5..447ed59a775e 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -33,6 +33,9 @@ import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.telephony.Annotation.DataFailureCause;
+import android.telephony.Annotation.RadioPowerState;
+import android.telephony.Annotation.SrvccState;
import android.telephony.CallAttributes;
import android.telephony.CallQuality;
import android.telephony.CellInfo;
@@ -208,6 +211,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private Map<Integer, List<EmergencyNumber>> mEmergencyNumberList;
+ private EmergencyNumber[] mOutgoingSmsEmergencyNumber;
+
+ private EmergencyNumber[] mOutgoingCallEmergencyNumber;
+
private CallQuality[] mCallQuality;
private CallAttributes[] mCallAttributes;
@@ -241,7 +248,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- @TelephonyManager.RadioPowerState
+ @RadioPowerState
private int mRadioPowerState = TelephonyManager.RADIO_POWER_UNAVAILABLE;
private final LocalLog mLocalLog = new LocalLog(100);
@@ -266,6 +273,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
PhoneStateListener.LISTEN_PRECISE_CALL_STATE |
PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE;
+ static final int READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK =
+ PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL
+ | PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS;
+
private static final int MSG_USER_SWITCHED = 1;
private static final int MSG_UPDATE_DEFAULT_SUB = 2;
@@ -284,11 +295,12 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
case MSG_UPDATE_DEFAULT_SUB: {
int newDefaultPhoneId = msg.arg1;
- int newDefaultSubId = (Integer)(msg.obj);
+ int newDefaultSubId = msg.arg2;
if (VDBG) {
log("MSG_UPDATE_DEFAULT_SUB:current mDefaultSubId=" + mDefaultSubId
- + " current mDefaultPhoneId=" + mDefaultPhoneId + " newDefaultSubId= "
- + newDefaultSubId + " newDefaultPhoneId=" + newDefaultPhoneId);
+ + " current mDefaultPhoneId=" + mDefaultPhoneId
+ + " newDefaultSubId=" + newDefaultSubId
+ + " newDefaultPhoneId=" + newDefaultPhoneId);
}
//Due to possible risk condition,(notify call back using the new
@@ -305,7 +317,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mDefaultSubId = newDefaultSubId;
mDefaultPhoneId = newDefaultPhoneId;
mLocalLog.log("Default subscription updated: mDefaultPhoneId="
- + mDefaultPhoneId + ", mDefaultSubId" + mDefaultSubId);
+ + mDefaultPhoneId + ", mDefaultSubId=" + mDefaultSubId);
}
}
}
@@ -335,22 +347,25 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
if (DBG) log("onReceive: userHandle=" + userHandle);
mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHED, userHandle, 0));
- } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED)) {
- Integer newDefaultSubIdObj = new Integer(intent.getIntExtra(
- PhoneConstants.SUBSCRIPTION_KEY,
- SubscriptionManager.getDefaultSubscriptionId()));
- int newDefaultPhoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY,
- SubscriptionManager.getPhoneId(mDefaultSubId));
+ } else if (action.equals(SubscriptionManager.ACTION_DEFAULT_SUBSCRIPTION_CHANGED)) {
+ int newDefaultSubId = intent.getIntExtra(
+ SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
+ SubscriptionManager.getDefaultSubscriptionId());
+ int newDefaultPhoneId = intent.getIntExtra(
+ PhoneConstants.PHONE_KEY,
+ SubscriptionManager.getPhoneId(newDefaultSubId));
if (DBG) {
log("onReceive:current mDefaultSubId=" + mDefaultSubId
- + " current mDefaultPhoneId=" + mDefaultPhoneId + " newDefaultSubId= "
- + newDefaultSubIdObj + " newDefaultPhoneId=" + newDefaultPhoneId);
+ + " current mDefaultPhoneId=" + mDefaultPhoneId
+ + " newDefaultSubId=" + newDefaultSubId
+ + " newDefaultPhoneId=" + newDefaultPhoneId);
}
- if(validatePhoneId(newDefaultPhoneId) && (!newDefaultSubIdObj.equals(mDefaultSubId)
- || (newDefaultPhoneId != mDefaultPhoneId))) {
+ if (validatePhoneId(newDefaultPhoneId)
+ && (newDefaultSubId != mDefaultSubId
+ || newDefaultPhoneId != mDefaultPhoneId)) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_DEFAULT_SUB,
- newDefaultPhoneId, 0, newDefaultSubIdObj));
+ newDefaultPhoneId, newDefaultSubId));
}
}
}
@@ -370,7 +385,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mContext = context;
mBatteryStats = BatteryStatsService.getService();
- int numPhones = TelephonyManager.getDefault().getPhoneCount();
+ int numPhones = TelephonyManager.getDefault().getSupportedModemCount();
if (DBG) log("TelephonyRegistry: ctor numPhones=" + numPhones);
mNumPhones = numPhones;
mCallState = new int[numPhones];
@@ -402,6 +417,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mImsReasonInfo = new ArrayList<>();
mPhysicalChannelConfigs = new ArrayList<>();
mEmergencyNumberList = new HashMap<>();
+ mOutgoingCallEmergencyNumber = new EmergencyNumber[numPhones];
+ mOutgoingSmsEmergencyNumber = new EmergencyNumber[numPhones];
for (int i = 0; i < numPhones; i++) {
mCallState[i] = TelephonyManager.CALL_STATE_IDLE;
mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE;
@@ -449,7 +466,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_REMOVED);
- filter.addAction(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED);
+ filter.addAction(SubscriptionManager.ACTION_DEFAULT_SUBSCRIPTION_CHANGED);
log("systemRunning register for intents");
mContext.registerReceiver(mBroadcastReceiver, filter);
}
@@ -947,13 +964,13 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- public void notifyCallState(int state, String phoneNumber) {
+ public void notifyCallStateForAllSubs(int state, String phoneNumber) {
if (!checkNotifyPermission("notifyCallState()")) {
return;
}
if (VDBG) {
- log("notifyCallState: state=" + state + " phoneNumber=" + phoneNumber);
+ log("notifyCallStateForAllSubs: state=" + state + " phoneNumber=" + phoneNumber);
}
synchronized (mRecords) {
@@ -980,13 +997,12 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
}
- public void notifyCallStateForPhoneId(int phoneId, int subId, int state,
- String incomingNumber) {
+ public void notifyCallState(int phoneId, int subId, int state, String incomingNumber) {
if (!checkNotifyPermission("notifyCallState()")) {
return;
}
if (VDBG) {
- log("notifyCallStateForPhoneId: subId=" + subId
+ log("notifyCallState: subId=" + subId
+ " state=" + state + " incomingNumber=" + incomingNumber);
}
synchronized (mRecords) {
@@ -1080,10 +1096,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
synchronized (mRecords) {
if (validatePhoneId(phoneId)) {
switch (activationType) {
- case PhoneConstants.SIM_ACTIVATION_TYPE_VOICE:
+ case TelephonyManager.SIM_ACTIVATION_TYPE_VOICE:
mVoiceActivationState[phoneId] = activationState;
break;
- case PhoneConstants.SIM_ACTIVATION_TYPE_DATA:
+ case TelephonyManager.SIM_ACTIVATION_TYPE_DATA:
mDataActivationState[phoneId] = activationState;
break;
default:
@@ -1096,7 +1112,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
+ " state=" + activationState);
}
try {
- if ((activationType == PhoneConstants.SIM_ACTIVATION_TYPE_VOICE) &&
+ if ((activationType == TelephonyManager.SIM_ACTIVATION_TYPE_VOICE) &&
r.matchPhoneStateListenerEvent(
PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) &&
idMatch(r.subId, subId, phoneId)) {
@@ -1107,7 +1123,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
r.callback.onVoiceActivationStateChanged(activationState);
}
- if ((activationType == PhoneConstants.SIM_ACTIVATION_TYPE_DATA) &&
+ if ((activationType == TelephonyManager.SIM_ACTIVATION_TYPE_DATA) &&
r.matchPhoneStateListenerEvent(
PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) &&
idMatch(r.subId, subId, phoneId)) {
@@ -1190,7 +1206,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
public void notifyCarrierNetworkChange(boolean active) {
// only CarrierService with carrier privilege rule should have the permission
int[] subIds = Arrays.stream(SubscriptionManager.from(mContext)
- .getActiveSubscriptionIdList())
+ .getActiveSubscriptionIdList(false))
.filter(i -> TelephonyPermissions.checkCarrierPrivilegeForSubId(i)).toArray();
if (ArrayUtils.isEmpty(subIds)) {
loge("notifyCarrierNetworkChange without carrier privilege");
@@ -1227,7 +1243,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
public void notifyCellInfoForSubscriber(int subId, List<CellInfo> cellInfo) {
- if (!checkNotifyPermission("notifyCellInfo()")) {
+ if (!checkNotifyPermission("notifyCellInfoForSubscriber()")) {
return;
}
if (VDBG) {
@@ -1244,7 +1260,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
try {
if (DBG_LOC) {
- log("notifyCellInfo: mCellInfo=" + cellInfo + " r=" + r);
+ log("notifyCellInfoForSubscriber: mCellInfo=" + cellInfo
+ + " r=" + r);
}
r.callback.onCellInfoChanged(cellInfo);
} catch (RemoteException ex) {
@@ -1704,7 +1721,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
public void notifyPreciseDataConnectionFailed(int phoneId, int subId, String apnType,
- String apn, @DataFailCause.FailCause int failCause) {
+ String apn, @DataFailureCause int failCause) {
if (!checkNotifyPermission("notifyPreciseDataConnectionFailed()")) {
return;
}
@@ -1734,7 +1751,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
@Override
- public void notifySrvccStateChanged(int subId, @TelephonyManager.SrvccState int state) {
+ public void notifySrvccStateChanged(int subId, @SrvccState int state) {
if (!checkNotifyPermission("notifySrvccStateChanged()")) {
return;
}
@@ -1841,8 +1858,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- public void notifyRadioPowerStateChanged(int phoneId, int subId,
- @TelephonyManager.RadioPowerState int state) {
+ public void notifyRadioPowerStateChanged(int phoneId, int subId, @RadioPowerState int state) {
if (!checkNotifyPermission("notifyRadioPowerStateChanged()")) {
return;
}
@@ -1906,6 +1922,56 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
@Override
+ public void notifyOutgoingEmergencyCall(int phoneId, int subId,
+ EmergencyNumber emergencyNumber) {
+ if (!checkNotifyPermission("notifyOutgoingEmergencyCall()")) {
+ return;
+ }
+ synchronized (mRecords) {
+ if (validatePhoneId(phoneId)) {
+ mOutgoingCallEmergencyNumber[phoneId] = emergencyNumber;
+ for (Record r : mRecords) {
+ if (r.matchPhoneStateListenerEvent(
+ PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL)
+ && idMatch(r.subId, subId, phoneId)) {
+ try {
+ r.callback.onOutgoingEmergencyCall(emergencyNumber);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
+ @Override
+ public void notifyOutgoingEmergencySms(int phoneId, int subId,
+ EmergencyNumber emergencyNumber) {
+ if (!checkNotifyPermission("notifyOutgoingEmergencySms()")) {
+ return;
+ }
+ synchronized (mRecords) {
+ if (validatePhoneId(phoneId)) {
+ mOutgoingSmsEmergencyNumber[phoneId] = emergencyNumber;
+ for (Record r : mRecords) {
+ if (r.matchPhoneStateListenerEvent(
+ PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS)
+ && idMatch(r.subId, subId, phoneId)) {
+ try {
+ r.callback.onOutgoingEmergencySms(emergencyNumber);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
+ @Override
public void notifyCallQualityChanged(CallQuality callQuality, int phoneId, int subId,
int callNetworkType) {
if (!checkNotifyPermission("notifyCallQualityChanged()")) {
@@ -1977,6 +2043,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
pw.println("mCallAttributes=" + mCallAttributes[i]);
pw.println("mCallNetworkType=" + mCallNetworkType[i]);
pw.println("mPreciseDataConnectionState=" + mPreciseDataConnectionState[i]);
+ pw.println("mOutgoingCallEmergencyNumber=" + mOutgoingCallEmergencyNumber[i]);
+ pw.println("mOutgoingSmsEmergencyNumber=" + mOutgoingSmsEmergencyNumber[i]);
pw.decreaseIndent();
}
pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState);
@@ -2161,7 +2229,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private void broadcastPreciseDataConnectionStateChanged(int state, int networkType,
String apnType, String apn, LinkProperties linkProperties,
- @DataFailCause.FailCause int failCause) {
+ @DataFailureCause int failCause) {
Intent intent = new Intent(TelephonyManager.ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED);
intent.putExtra(PhoneConstants.STATE_KEY, state);
intent.putExtra(PhoneConstants.DATA_NETWORK_TYPE_KEY, networkType);
@@ -2246,6 +2314,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
}
+ if ((events & READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK) != 0) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null);
+ }
+
if ((events & PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT) != 0) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index b9d7c687704c..a51746767f0f 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -288,7 +288,7 @@ final class UiModeManagerService extends SystemService {
updateNightModeFromSettings(context, res, UserHandle.getCallingUserId());
// Update the initial, static configurations.
- SystemServerInitThreadPool.get().submit(() -> {
+ SystemServerInitThreadPool.submit(() -> {
synchronized (mLock) {
updateConfigurationLocked();
sendConfigurationLocked();
diff --git a/services/core/java/com/android/server/UiThread.java b/services/core/java/com/android/server/UiThread.java
index b2fa6846a6d0..34fc9abec51e 100644
--- a/services/core/java/com/android/server/UiThread.java
+++ b/services/core/java/com/android/server/UiThread.java
@@ -21,6 +21,8 @@ import android.os.Looper;
import android.os.Process;
import android.os.Trace;
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* Shared singleton thread for showing UI. This is a foreground thread, and in
* additional should not have operations that can take more than a few ms scheduled
@@ -68,4 +70,20 @@ public final class UiThread extends ServiceThread {
return sHandler;
}
}
+
+ /**
+ * Disposes current ui thread if it's initialized. Should only be used in tests to set up a
+ * new environment.
+ */
+ @VisibleForTesting
+ public static void dispose() {
+ synchronized (UiThread.class) {
+ if (sInstance == null) {
+ return;
+ }
+
+ getHandler().runWithScissors(sInstance::quit, 0 /* timeout */);
+ sInstance = null;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 9936d73fb800..d622fb433ed8 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -29,6 +29,7 @@ import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.input.InputManager;
import android.hardware.vibrator.V1_0.EffectStrength;
+import android.hardware.vibrator.V1_4.Capabilities;
import android.icu.text.DateFormat;
import android.media.AudioAttributes;
import android.media.AudioManager;
@@ -108,6 +109,9 @@ public class VibratorService extends IVibratorService.Stub
// If a vibration is playing for longer than 5s, it's probably not haptic feedback.
private static final long MAX_HAPTIC_FEEDBACK_DURATION = 5000;
+ // If HAL supports callbacks set the timeout to ASYNC_TIMEOUT_MULTIPLIER * duration.
+ private static final long ASYNC_TIMEOUT_MULTIPLIER = 2;
+
// A mapping from the intensity adjustment to the scaling to apply, where the intensity
// adjustment is defined as the delta between the default intensity level and the user selected
@@ -123,6 +127,7 @@ public class VibratorService extends IVibratorService.Stub
private final boolean mAllowPriorityVibrationsInLowPowerMode;
private final boolean mSupportsAmplitudeControl;
private final boolean mSupportsExternalControl;
+ private final long mCapabilities;
private final int mDefaultVibrationAmplitude;
private final SparseArray<VibrationEffect> mFallbackEffects;
private final SparseArray<Integer> mProcStatesCache = new SparseArray();
@@ -163,9 +168,10 @@ public class VibratorService extends IVibratorService.Stub
static native void vibratorOff();
static native boolean vibratorSupportsAmplitudeControl();
static native void vibratorSetAmplitude(int amplitude);
- static native long vibratorPerformEffect(long effect, long strength);
+ static native long vibratorPerformEffect(long effect, long strength, Vibration vibration);
static native boolean vibratorSupportsExternalControl();
static native void vibratorSetExternalControl(boolean enabled);
+ static native long vibratorGetCapabilities();
private final IUidObserver mUidObserver = new IUidObserver.Stub() {
@Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
@@ -226,6 +232,14 @@ public class VibratorService extends IVibratorService.Stub
}
}
+ private void onComplete() {
+ synchronized (mLock) {
+ if (this == mCurrentVibration) {
+ doCancelVibrateLocked();
+ }
+ }
+ }
+
public boolean hasTimeoutLongerThan(long millis) {
final long duration = effect.getDuration();
return duration >= 0 && duration > millis;
@@ -347,6 +361,7 @@ public class VibratorService extends IVibratorService.Stub
mSupportsAmplitudeControl = vibratorSupportsAmplitudeControl();
mSupportsExternalControl = vibratorSupportsExternalControl();
+ mCapabilities = vibratorGetCapabilities();
mContext = context;
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
@@ -1135,10 +1150,14 @@ public class VibratorService extends IVibratorService.Stub
}
// Input devices don't support prebaked effect, so skip trying it with them.
if (!usingInputDeviceVibrators) {
- long timeout = vibratorPerformEffect(prebaked.getId(),
- prebaked.getEffectStrength());
+ long duration = vibratorPerformEffect(prebaked.getId(),
+ prebaked.getEffectStrength(), vib);
+ long timeout = duration;
+ if ((mCapabilities & Capabilities.PERFORM_COMPLETION_CALLBACK) != 0) {
+ timeout *= ASYNC_TIMEOUT_MULTIPLIER;
+ }
if (timeout > 0) {
- noteVibratorOnLocked(vib.uid, timeout);
+ noteVibratorOnLocked(vib.uid, duration);
return timeout;
}
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 7ce1c7cb40a8..b470fbb6de22 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -110,6 +110,7 @@ public class Watchdog extends Thread {
"android.hardware.audio@2.0::IDevicesFactory",
"android.hardware.audio@4.0::IDevicesFactory",
"android.hardware.audio@5.0::IDevicesFactory",
+ "android.hardware.audio@6.0::IDevicesFactory",
"android.hardware.biometrics.face@1.0::IBiometricsFace",
"android.hardware.bluetooth@1.0::IBluetoothHci",
"android.hardware.camera.provider@2.4::ICameraProvider",
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index f487834879d6..a9ad2ba7b545 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -207,34 +207,38 @@ public final class ActiveServices {
@Override
public void stopForegroundServicesForUidPackage(final int uid, final String packageName) {
synchronized (mAm) {
- final ServiceMap smap = getServiceMapLocked(UserHandle.getUserId(uid));
- final int N = smap.mServicesByInstanceName.size();
- final ArrayList<ServiceRecord> toStop = new ArrayList<>(N);
- for (int i = 0; i < N; i++) {
- final ServiceRecord r = smap.mServicesByInstanceName.valueAt(i);
- if (uid == r.serviceInfo.applicationInfo.uid
- || packageName.equals(r.serviceInfo.packageName)) {
- if (r.isForeground) {
- toStop.add(r);
- }
- }
- }
+ stopAllForegroundServicesLocked(uid, packageName);
+ }
+ }
+ }
- // Now stop them all
- final int numToStop = toStop.size();
- if (numToStop > 0 && DEBUG_FOREGROUND_SERVICE) {
- Slog.i(TAG, "Package " + packageName + "/" + uid
- + " entering FAS with foreground services");
- }
- for (int i = 0; i < numToStop; i++) {
- final ServiceRecord r = toStop.get(i);
- if (DEBUG_FOREGROUND_SERVICE) {
- Slog.i(TAG, " Stopping fg for service " + r);
- }
- setServiceForegroundInnerLocked(r, 0, null, 0, 0);
+ void stopAllForegroundServicesLocked(final int uid, final String packageName) {
+ final ServiceMap smap = getServiceMapLocked(UserHandle.getUserId(uid));
+ final int N = smap.mServicesByInstanceName.size();
+ final ArrayList<ServiceRecord> toStop = new ArrayList<>(N);
+ for (int i = 0; i < N; i++) {
+ final ServiceRecord r = smap.mServicesByInstanceName.valueAt(i);
+ if (uid == r.serviceInfo.applicationInfo.uid
+ || packageName.equals(r.serviceInfo.packageName)) {
+ if (r.isForeground) {
+ toStop.add(r);
}
}
}
+
+ // Now stop them all
+ final int numToStop = toStop.size();
+ if (numToStop > 0 && DEBUG_FOREGROUND_SERVICE) {
+ Slog.i(TAG, "Package " + packageName + "/" + uid
+ + " in FAS with foreground services");
+ }
+ for (int i = 0; i < numToStop; i++) {
+ final ServiceRecord r = toStop.get(i);
+ if (DEBUG_FOREGROUND_SERVICE) {
+ Slog.i(TAG, " Stopping fg for service " + r);
+ }
+ setServiceForegroundInnerLocked(r, 0, null, 0, 0);
+ }
}
/**
@@ -601,7 +605,8 @@ public final class ActiveServices {
r.lastActivity);
}
mAm.mAppOpsService.startOperation(AppOpsManager.getToken(mAm.mAppOpsService),
- AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, true);
+ AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null,
+ true);
}
final ServiceMap smap = getServiceMapLocked(r.userId);
@@ -1055,12 +1060,23 @@ public final class ActiveServices {
}
}
if (!aa.mAppOnTop) {
- if (active == null) {
- active = new ArrayList<>();
+ // Transitioning a fg-service host app out of top: if it's bg restricted,
+ // it loses the fg service state now.
+ if (!appRestrictedAnyInBackground(aa.mUid, aa.mPackageName)) {
+ if (active == null) {
+ active = new ArrayList<>();
+ }
+ if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "Adding active: pkg="
+ + aa.mPackageName + ", uid=" + aa.mUid);
+ active.add(aa);
+ } else {
+ if (DEBUG_FOREGROUND_SERVICE) {
+ Slog.d(TAG, "bg-restricted app "
+ + aa.mPackageName + "/" + aa.mUid
+ + " exiting top; demoting fg services ");
+ }
+ stopAllForegroundServicesLocked(aa.mUid, aa.mPackageName);
}
- if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "Adding active: pkg="
- + aa.mPackageName + ", uid=" + aa.mUid);
- active.add(aa);
}
}
smap.removeMessages(ServiceMap.MSG_UPDATE_FOREGROUND_APPS);
@@ -1415,7 +1431,7 @@ public final class ActiveServices {
mAm.mAppOpsService.startOperation(
AppOpsManager.getToken(mAm.mAppOpsService),
AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName,
- true);
+ null, true);
StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
r.appInfo.uid, r.shortInstanceName,
StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER);
@@ -1448,7 +1464,8 @@ public final class ActiveServices {
// we have cleared the flag so can now drop it.
mAm.mAppOpsService.finishOperation(
AppOpsManager.getToken(mAm.mAppOpsService),
- AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
+ AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName,
+ null);
}
}
} else {
@@ -1465,7 +1482,7 @@ public final class ActiveServices {
}
mAm.mAppOpsService.finishOperation(
AppOpsManager.getToken(mAm.mAppOpsService),
- AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
+ AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
r.appInfo.uid, r.shortInstanceName,
StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
@@ -1785,7 +1802,7 @@ public final class ActiveServices {
// Once the apps have become associated, if one of them is caller is ephemeral
// the target app should now be able to see the calling app
mAm.grantImplicitAccess(callerApp.userId, service,
- UserHandle.getAppId(callerApp.uid), UserHandle.getAppId(s.appInfo.uid));
+ callerApp.uid, UserHandle.getAppId(s.appInfo.uid));
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity,
@@ -1864,7 +1881,7 @@ public final class ActiveServices {
|| (callerApp.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP
&& (flags & Context.BIND_TREAT_LIKE_ACTIVITY) != 0),
b.client);
- mAm.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_BIND_SERVICE);
+ mAm.updateOomAdjLocked(s.app, OomAdjuster.OOM_ADJ_REASON_BIND_SERVICE);
}
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bind " + s + " with " + b
@@ -2829,7 +2846,7 @@ public final class ActiveServices {
bumpServiceExecutingLocked(r, execInFg, "create");
mAm.updateLruProcessLocked(app, false, null);
updateServiceForegroundLocked(r.app, /* oomAdj= */ false);
- mAm.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_START_SERVICE);
+ mAm.updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_START_SERVICE);
boolean created = false;
try {
@@ -2966,7 +2983,7 @@ public final class ActiveServices {
mAm.mUgmInternal.grantUriPermissionUncheckedFromIntent(si.neededGrants,
si.getUriPermissionsLocked());
}
- mAm.grantImplicitAccess(r.userId, si.intent, UserHandle.getAppId(si.callingId),
+ mAm.grantImplicitAccess(r.userId, si.intent, si.callingId,
UserHandle.getAppId(r.appInfo.uid)
);
bumpServiceExecutingLocked(r, execInFg, "start");
@@ -3148,7 +3165,7 @@ public final class ActiveServices {
r.lastActivity);
}
mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),
- AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
+ AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
mAm.mHandler.removeMessages(
ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
if (r.app != null) {
@@ -3206,7 +3223,7 @@ public final class ActiveServices {
}
mAm.mAppOpsService.finishOperation(
AppOpsManager.getToken(mAm.mAppOpsService),
- AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
+ AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid,
r.shortInstanceName, StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 701ba9a58791..1bf715e023b0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -294,6 +294,19 @@ final class ActivityManagerConstants extends ContentObserver {
// memory trimming.
public int CUR_TRIM_CACHED_PROCESSES;
+ @SuppressWarnings("unused")
+ private static final int OOMADJ_UPDATE_POLICY_SLOW = 0;
+ private static final int OOMADJ_UPDATE_POLICY_QUICK = 1;
+ private static final int DEFAULT_OOMADJ_UPDATE_POLICY = OOMADJ_UPDATE_POLICY_QUICK;
+
+ private static final String KEY_OOMADJ_UPDATE_POLICY = "oomadj_update_policy";
+
+ // Indicate if the oom adjuster should take the quick path to update the oom adj scores,
+ // in which no futher actions will be performed if there are no significant adj/proc state
+ // changes for the specific process; otherwise, use the traditonal slow path which would
+ // keep updating all processes in the LRU list.
+ public boolean OOMADJ_UPDATE_QUICK = DEFAULT_OOMADJ_UPDATE_POLICY == OOMADJ_UPDATE_POLICY_QUICK;
+
private static final long MIN_AUTOMATIC_HEAP_DUMP_PSS_THRESHOLD_BYTES = 100 * 1024; // 100 KB
private final boolean mSystemServerAutomaticHeapDumpEnabled;
@@ -328,6 +341,9 @@ final class ActivityManagerConstants extends ContentObserver {
case KEY_DEFAULT_BACKGROUND_ACTIVITY_STARTS_ENABLED:
updateBackgroundActivityStarts();
break;
+ case KEY_OOMADJ_UPDATE_POLICY:
+ updateOomAdjUpdatePolicy();
+ break;
default:
break;
}
@@ -379,6 +395,7 @@ final class ActivityManagerConstants extends ContentObserver {
updateMaxCachedProcesses();
updateActivityStartsLoggingEnabled();
updateBackgroundActivityStarts();
+ updateOomAdjUpdatePolicy();
}
public void setOverrideMaxCachedProcesses(int value) {
@@ -517,6 +534,14 @@ final class ActivityManagerConstants extends ContentObserver {
/*defaultValue*/ false);
}
+ private void updateOomAdjUpdatePolicy() {
+ OOMADJ_UPDATE_QUICK = DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_OOMADJ_UPDATE_POLICY,
+ /* defaultValue */ DEFAULT_OOMADJ_UPDATE_POLICY)
+ == OOMADJ_UPDATE_POLICY_QUICK;
+ }
+
private void updateEnableAutomaticSystemServerHeapDumps() {
if (!mSystemServerAutomaticHeapDumpEnabled) {
Slog.wtf(TAG,
@@ -633,5 +658,6 @@ final class ActivityManagerConstants extends ContentObserver {
pw.print(" CUR_MAX_EMPTY_PROCESSES="); pw.println(CUR_MAX_EMPTY_PROCESSES);
pw.print(" CUR_TRIM_EMPTY_PROCESSES="); pw.println(CUR_TRIM_EMPTY_PROCESSES);
pw.print(" CUR_TRIM_CACHED_PROCESSES="); pw.println(CUR_TRIM_CACHED_PROCESSES);
+ pw.print(" OOMADJ_UPDATE_QUICK="); pw.println(OOMADJ_UPDATE_QUICK);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 82a04fd59ca9..e50d0282a451 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -42,8 +42,6 @@ import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode;
-import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground;
import static android.os.FactoryTest.FACTORY_TEST_OFF;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
@@ -235,6 +233,7 @@ import android.os.AppZygote;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.BinderProxy;
+import android.os.BugreportParams;
import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
@@ -284,7 +283,6 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.EventLog;
-import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.Pair;
import android.util.PrintWriterPrinter;
@@ -354,6 +352,7 @@ import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
import com.android.server.appop.AppOpsService;
import com.android.server.compat.CompatConfig;
+import com.android.server.compat.PlatformCompat;
import com.android.server.contentcapture.ContentCaptureManagerInternal;
import com.android.server.firewall.IntentFirewall;
import com.android.server.job.JobSchedulerInternal;
@@ -385,7 +384,6 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -428,7 +426,7 @@ public class ActivityManagerService extends IActivityManager.Stub
private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
static final String TAG_LRU = TAG + POSTFIX_LRU;
private static final String TAG_MU = TAG + POSTFIX_MU;
- private static final String TAG_NETWORK = TAG + POSTFIX_NETWORK;
+ static final String TAG_NETWORK = TAG + POSTFIX_NETWORK;
static final String TAG_OOM_ADJ = TAG + POSTFIX_OOM_ADJ;
private static final String TAG_POWER = TAG + POSTFIX_POWER;
static final String TAG_PROCESS_OBSERVERS = TAG + POSTFIX_PROCESS_OBSERVERS;
@@ -511,10 +509,6 @@ public class ActivityManagerService extends IActivityManager.Stub
static final int PERSISTENT_MASK =
ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT;
- // Intent sent when remote bugreport collection has been completed
- private static final String INTENT_REMOTE_BUGREPORT_FINISHED =
- "com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED";
-
// If set, we will push process association information in to procstats.
static final boolean TRACK_PROCSTATS_ASSOCIATIONS = true;
@@ -538,27 +532,10 @@ public class ActivityManagerService extends IActivityManager.Stub
*/
private static final int BINDER_PROXY_LOW_WATERMARK = 5500;
- /**
- * State indicating that there is no need for any blocking for network.
- */
- @VisibleForTesting
- static final int NETWORK_STATE_NO_CHANGE = 0;
-
- /**
- * State indicating that the main thread needs to be informed about the network wait.
- */
- @VisibleForTesting
- static final int NETWORK_STATE_BLOCK = 1;
-
- /**
- * State indicating that any threads waiting for network state to get updated can be unblocked.
- */
- @VisibleForTesting
- static final int NETWORK_STATE_UNBLOCK = 2;
-
// Max character limit for a notification title. If the notification title is larger than this
// the notification will not be legible to the user.
private static final int MAX_BUGREPORT_TITLE_SIZE = 50;
+ private static final int MAX_BUGREPORT_DESCRIPTION_SIZE = 150;
private static final int NATIVE_DUMP_TIMEOUT_MS = 2000; // 2 seconds;
private static final int JAVA_DUMP_MINIMUM_SIZE = 100; // 100 bytes.
@@ -913,7 +890,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// The other observer methods are unused
@Override
- public void onIntentStarted(Intent intent) {
+ public void onIntentStarted(Intent intent, long timestampNs) {
}
@Override
@@ -925,7 +902,11 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public void onActivityLaunchFinished(byte[] finalActivity) {
+ public void onActivityLaunchFinished(byte[] finalActivity, long timestampNs) {
+ }
+
+ @Override
+ public void onReportFullyDrawn(byte[] finalActivity, long timestampNs) {
}
};
@@ -1373,7 +1354,7 @@ public class ActivityManagerService extends IActivityManager.Stub
String mTrackAllocationApp = null;
String mNativeDebuggingApp = null;
- private final Injector mInjector;
+ final Injector mInjector;
static final class ProcessChangeItem {
static final int CHANGE_ACTIVITIES = 1<<0;
@@ -1591,6 +1572,8 @@ public class ActivityManagerService extends IActivityManager.Stub
// Encapsulates the global setting "hidden_api_blacklist_exemptions"
final HiddenApiSettings mHiddenApiBlacklist;
+ private final PlatformCompat mPlatformCompat;
+
PackageManagerInternal mPackageManagerInt;
PermissionManagerServiceInternal mPermissionManagerInt;
@@ -2421,7 +2404,8 @@ public class ActivityManagerService extends IActivityManager.Stub
final ActiveUids activeUids = new ActiveUids(this, false /* postChangesToAtm */);
mProcessList.init(this, activeUids);
mLowMemDetector = null;
- mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids, handlerThread);
+ mOomAdjuster = hasHandlerThread
+ ? new OomAdjuster(this, mProcessList, activeUids, handlerThread) : null;
mIntentFirewall = hasHandlerThread
? new IntentFirewall(new IntentFirewallInterface(), mHandler) : null;
@@ -2442,6 +2426,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mFactoryTest = FACTORY_TEST_OFF;
mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
mInternal = new LocalService();
+ mPlatformCompat = null;
}
// Note: This method is invoked on the main thread but may need to attach various
@@ -2579,6 +2564,9 @@ public class ActivityManagerService extends IActivityManager.Stub
mHiddenApiBlacklist = new HiddenApiSettings(mHandler, mContext);
+ mPlatformCompat = (PlatformCompat) ServiceManager.getService(
+ Context.PLATFORM_COMPAT_SERVICE);
+
Watchdog.getInstance().addMonitor(this);
Watchdog.getInstance().addThread(mHandler);
@@ -3159,7 +3147,7 @@ public class ActivityManagerService extends IActivityManager.Stub
private boolean hasUsageStatsPermission(String callingPackage) {
final int mode = mAppOpsService.noteOperation(AppOpsManager.OP_GET_USAGE_STATS,
- Binder.getCallingUid(), callingPackage);
+ Binder.getCallingUid(), callingPackage, null);
if (mode == AppOpsManager.MODE_DEFAULT) {
return checkCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS)
== PackageManager.PERMISSION_GRANTED;
@@ -5081,7 +5069,9 @@ public class ActivityManagerService extends IActivityManager.Stub
if (preBindAgent != null) {
thread.attachAgent(preBindAgent);
}
-
+ if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+ thread.attachStartupAgents(app.info.dataDir);
+ }
// Figure out whether the app needs to run in autofill compat mode.
AutofillOptions autofillOptions = null;
@@ -5108,6 +5098,9 @@ public class ActivityManagerService extends IActivityManager.Stub
mAtmInternal.preBindApplication(app.getWindowProcessController());
final ActiveInstrumentation instr2 = app.getActiveInstrumentation();
long[] disabledCompatChanges = CompatConfig.get().getDisabledChanges(app.info);
+ if (mPlatformCompat != null) {
+ mPlatformCompat.resetReporting(app.info);
+ }
if (app.isolatedEntryPoint != null) {
// This is an isolated process which should just call an entry point instead of
// being bound to an application.
@@ -5225,7 +5218,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
if (!didSomething) {
- updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN);
+ updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN);
checkTime(startTime, "attachApplicationLocked: after updateOomAdjLocked");
}
@@ -5391,10 +5384,42 @@ public class ActivityManagerService extends IActivityManager.Stub
});
mUserController.scheduleStartProfiles();
}
+ // UART is on if init's console service is running, send a warning notification.
+ showConsoleNotificationIfActive();
t.traceEnd();
}
+ private void showConsoleNotificationIfActive() {
+ if (!SystemProperties.get("init.svc.console").equals("running")) {
+ return;
+ }
+ String title = mContext
+ .getString(com.android.internal.R.string.console_running_notification_title);
+ String message = mContext
+ .getString(com.android.internal.R.string.console_running_notification_message);
+ Notification notification =
+ new Notification.Builder(mContext, SystemNotificationChannels.DEVELOPER)
+ .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+ .setWhen(0)
+ .setOngoing(true)
+ .setTicker(title)
+ .setDefaults(0) // please be quiet
+ .setColor(mContext.getColor(
+ com.android.internal.R.color
+ .system_notification_accent_color))
+ .setContentTitle(title)
+ .setContentText(message)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .build();
+
+ NotificationManager notificationManager =
+ mContext.getSystemService(NotificationManager.class);
+ notificationManager.notifyAsUser(
+ null, SystemMessage.NOTE_SERIAL_CONSOLE_ENABLED, notification, UserHandle.ALL);
+
+ }
+
@Override
public void bootAnimationComplete() {
final boolean callFinishBooting;
@@ -5711,6 +5736,7 @@ public class ActivityManagerService extends IActivityManager.Stub
void importanceTokenDied(ImportanceToken token) {
synchronized (ActivityManagerService.this) {
+ ProcessRecord pr = null;
synchronized (mPidsSelfLocked) {
ImportanceToken cur
= mImportantProcesses.get(token.pid);
@@ -5718,14 +5744,14 @@ public class ActivityManagerService extends IActivityManager.Stub
return;
}
mImportantProcesses.remove(token.pid);
- ProcessRecord pr = mPidsSelfLocked.get(token.pid);
+ pr = mPidsSelfLocked.get(token.pid);
if (pr == null) {
return;
}
pr.forcingToImportant = null;
updateProcessForegroundLocked(pr, false, 0, false);
}
- updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
+ updateOomAdjLocked(pr, OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
}
}
@@ -5736,8 +5762,9 @@ public class ActivityManagerService extends IActivityManager.Stub
synchronized(this) {
boolean changed = false;
+ ProcessRecord pr = null;
synchronized (mPidsSelfLocked) {
- ProcessRecord pr = mPidsSelfLocked.get(pid);
+ pr = mPidsSelfLocked.get(pid);
if (pr == null && isForeground) {
Slog.w(TAG, "setProcessForeground called on unknown pid: " + pid);
return;
@@ -5771,7 +5798,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
if (changed) {
- updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
+ updateOomAdjLocked(pr, OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
}
}
}
@@ -5877,8 +5904,9 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public int noteOp(String op, int uid, String packageName) {
+ // TODO moltmann: Allow to specify featureId
return mActivityManagerService.mAppOpsService
- .noteOperation(AppOpsManager.strOpToOp(op), uid, packageName);
+ .noteOperation(AppOpsManager.strOpToOp(op), uid, packageName, null);
}
@Override
@@ -6030,7 +6058,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
// ...and legacy apps get an AppOp check
int appop = mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND,
- uid, packageName);
+ uid, packageName, null);
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "Legacy app " + uid + "/" + packageName + " bg appop " + appop);
}
@@ -6189,9 +6217,9 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@VisibleForTesting
- public void grantImplicitAccess(int userId, Intent intent, int callingAppId, int targetAppId) {
+ public void grantImplicitAccess(int userId, Intent intent, int callingUid, int targetAppId) {
getPackageManagerInternalLocked().
- grantImplicitAccess(userId, intent, callingAppId, targetAppId);
+ grantImplicitAccess(userId, intent, callingUid, targetAppId);
}
/**
@@ -7478,6 +7506,7 @@ public class ActivityManagerService extends IActivityManager.Stub
dst.setProcess(r);
dst.notifyAll();
}
+ dst.mRestartCount = 0;
updateOomAdjLocked(r, true, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,
src.info.authority);
@@ -7914,7 +7943,7 @@ public class ActivityManagerService extends IActivityManager.Stub
new HostingRecord("added application",
customProcess != null ? customProcess : info.processName));
mProcessList.updateLruProcessLocked(app, false, null);
- updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN);
+ updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN);
}
// This package really, really can not be stopped.
@@ -8271,45 +8300,39 @@ public class ActivityManagerService extends IActivityManager.Stub
}
/**
- * @deprecated This method is only used by a few internal components and it will soon start
- * using bug report API (which will be restricted to a few, pre-defined apps).
- * No new code should be calling it.
+ * Takes a bugreport using bug report API ({@code BugreportManager}) with no pre-set
+ * title and description
*/
- // TODO(b/137825297): Remove deprecated annotation and rephrase comments for all
- // requestBugreport functions below.
- @Deprecated
@Override
- public void requestBugReport(int bugreportType) {
+ public void requestBugReport(@BugreportParams.BugreportMode int bugreportType) {
requestBugReportWithDescription(null, null, bugreportType);
}
/**
- * @deprecated This method is only used by a few internal components and it will soon start
- * using bug report API (which will be restricted to a few, pre-defined apps).
- * No new code should be calling it.
+ * Takes a bugreport using bug report API ({@code BugreportManager}) which gets
+ * triggered by sending a broadcast to Shell.
*/
- @Deprecated
@Override
public void requestBugReportWithDescription(@Nullable String shareTitle,
@Nullable String shareDescription, int bugreportType) {
String type = null;
switch (bugreportType) {
- case ActivityManager.BUGREPORT_OPTION_FULL:
+ case BugreportParams.BUGREPORT_MODE_FULL:
type = "bugreportfull";
break;
- case ActivityManager.BUGREPORT_OPTION_INTERACTIVE:
+ case BugreportParams.BUGREPORT_MODE_INTERACTIVE:
type = "bugreportplus";
break;
- case ActivityManager.BUGREPORT_OPTION_REMOTE:
+ case BugreportParams.BUGREPORT_MODE_REMOTE:
type = "bugreportremote";
break;
- case ActivityManager.BUGREPORT_OPTION_WEAR:
+ case BugreportParams.BUGREPORT_MODE_WEAR:
type = "bugreportwear";
break;
- case ActivityManager.BUGREPORT_OPTION_TELEPHONY:
+ case BugreportParams.BUGREPORT_MODE_TELEPHONY:
type = "bugreporttelephony";
break;
- case ActivityManager.BUGREPORT_OPTION_WIFI:
+ case BugreportParams.BUGREPORT_MODE_WIFI:
type = "bugreportwifi";
break;
default:
@@ -8323,74 +8346,58 @@ public class ActivityManagerService extends IActivityManager.Stub
if (!TextUtils.isEmpty(shareTitle)) {
if (shareTitle.length() > MAX_BUGREPORT_TITLE_SIZE) {
- String errorStr = "shareTitle should be less than " +
- MAX_BUGREPORT_TITLE_SIZE + " characters";
+ String errorStr = "shareTitle should be less than "
+ + MAX_BUGREPORT_TITLE_SIZE + " characters";
throw new IllegalArgumentException(errorStr);
}
if (!TextUtils.isEmpty(shareDescription)) {
- int length = shareDescription.getBytes(StandardCharsets.UTF_8).length;
- if (length > SystemProperties.PROP_VALUE_MAX) {
- String errorStr = "shareTitle should be less than " +
- SystemProperties.PROP_VALUE_MAX + " bytes";
+ if (shareDescription.length() > MAX_BUGREPORT_DESCRIPTION_SIZE) {
+ String errorStr = "shareDescription should be less than "
+ + MAX_BUGREPORT_DESCRIPTION_SIZE + " characters";
throw new IllegalArgumentException(errorStr);
- } else {
- SystemProperties.set("dumpstate.options.description", shareDescription);
}
}
- SystemProperties.set("dumpstate.options.title", shareTitle);
Slog.d(TAG, "Bugreport notification title " + shareTitle
+ " description " + shareDescription);
}
- final boolean useApi = FeatureFlagUtils.isEnabled(mContext,
- FeatureFlagUtils.USE_BUGREPORT_API);
-
- if (useApi && bugreportType == ActivityManager.BUGREPORT_OPTION_INTERACTIVE) {
- // Create intent to trigger Bugreport API via Shell
- Intent triggerShellBugreport = new Intent();
- triggerShellBugreport.setAction(INTENT_BUGREPORT_REQUESTED);
- triggerShellBugreport.setPackage(SHELL_APP_PACKAGE);
- triggerShellBugreport.putExtra(EXTRA_BUGREPORT_TYPE, bugreportType);
- if (shareTitle != null) {
- triggerShellBugreport.putExtra(EXTRA_TITLE, shareTitle);
- }
- if (shareDescription != null) {
- triggerShellBugreport.putExtra(EXTRA_DESCRIPTION, shareDescription);
- }
- final long identity = Binder.clearCallingIdentity();
- try {
- // Send broadcast to shell to trigger bugreport using Bugreport API
- mContext.sendBroadcast(triggerShellBugreport);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- } else {
- SystemProperties.set("dumpstate.options", type);
- SystemProperties.set("ctl.start", "bugreport");
+ // Create intent to trigger Bugreport API via Shell
+ Intent triggerShellBugreport = new Intent();
+ triggerShellBugreport.setAction(INTENT_BUGREPORT_REQUESTED);
+ triggerShellBugreport.setPackage(SHELL_APP_PACKAGE);
+ triggerShellBugreport.putExtra(EXTRA_BUGREPORT_TYPE, bugreportType);
+ triggerShellBugreport.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ triggerShellBugreport.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ if (shareTitle != null) {
+ triggerShellBugreport.putExtra(EXTRA_TITLE, shareTitle);
+ }
+ if (shareDescription != null) {
+ triggerShellBugreport.putExtra(EXTRA_DESCRIPTION, shareDescription);
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ // Send broadcast to shell to trigger bugreport using Bugreport API
+ mContext.sendBroadcast(triggerShellBugreport);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
/**
- * @deprecated This method is only used by a few internal components and it will soon start
- * using bug report API (which will be restricted to a few, pre-defined apps).
- * No new code should be calling it.
+ * Takes a telephony bugreport with title and description
*/
- @Deprecated
@Override
public void requestTelephonyBugReport(String shareTitle, String shareDescription) {
requestBugReportWithDescription(shareTitle, shareDescription,
- ActivityManager.BUGREPORT_OPTION_TELEPHONY);
+ BugreportParams.BUGREPORT_MODE_TELEPHONY);
}
/**
- * @deprecated This method is only used by a few internal components and it will soon start
- * using bug report API (which will be restricted to a few, pre-defined apps).
- * No new code should be calling it.
+ * Takes a minimal bugreport of Wifi-related state with pre-set title and description
*/
- @Deprecated
@Override
public void requestWifiBugReport(String shareTitle, String shareDescription) {
requestBugReportWithDescription(shareTitle, shareDescription,
- ActivityManager.BUGREPORT_OPTION_WIFI);
+ BugreportParams.BUGREPORT_MODE_WIFI);
}
/**
@@ -8398,7 +8405,7 @@ public class ActivityManagerService extends IActivityManager.Stub
*/
@Override
public void requestInteractiveBugReport() {
- requestBugReportWithDescription(null, null, ActivityManager.BUGREPORT_OPTION_INTERACTIVE);
+ requestBugReportWithDescription(null, null, BugreportParams.BUGREPORT_MODE_INTERACTIVE);
}
/**
@@ -8409,7 +8416,7 @@ public class ActivityManagerService extends IActivityManager.Stub
public void requestInteractiveBugReportWithDescription(String shareTitle,
String shareDescription) {
requestBugReportWithDescription(shareTitle, shareDescription,
- ActivityManager.BUGREPORT_OPTION_INTERACTIVE);
+ BugreportParams.BUGREPORT_MODE_INTERACTIVE);
}
/**
@@ -8417,7 +8424,7 @@ public class ActivityManagerService extends IActivityManager.Stub
*/
@Override
public void requestFullBugReport() {
- requestBugReportWithDescription(null, null, ActivityManager.BUGREPORT_OPTION_FULL);
+ requestBugReportWithDescription(null, null, BugreportParams.BUGREPORT_MODE_FULL);
}
/**
@@ -8425,7 +8432,7 @@ public class ActivityManagerService extends IActivityManager.Stub
*/
@Override
public void requestRemoteBugReport() {
- requestBugReportWithDescription(null, null, ActivityManager.BUGREPORT_OPTION_REMOTE);
+ requestBugReportWithDescription(null, null, BugreportParams.BUGREPORT_MODE_REMOTE);
}
public void registerProcessObserver(IProcessObserver observer) {
@@ -8680,7 +8687,7 @@ public class ActivityManagerService extends IActivityManager.Stub
lp.format = v.getBackground().getOpacity();
lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
((WindowManager)mContext.getSystemService(
Context.WINDOW_SERVICE)).addView(v, lp);
}
@@ -9770,6 +9777,10 @@ public class ActivityManagerService extends IActivityManager.Stub
sb.append("Foreground: ")
.append(process.isInterestingToUserLocked() ? "Yes" : "No")
.append("\n");
+ if (process.startTime > 0) {
+ long runtimeMillis = SystemClock.elapsedRealtime() - process.startTime;
+ sb.append("Process-Runtime: ").append(runtimeMillis).append("\n");
+ }
}
if (activityShortComponentName != null) {
sb.append("Activity: ").append(activityShortComponentName).append("\n");
@@ -13919,9 +13930,20 @@ public class ActivityManagerService extends IActivityManager.Stub
return false;
}
+ /**
+ * Remove the dying provider from known provider map and launching provider map.
+ * @param proc The dying process recoder
+ * @param cpr The provider to be removed.
+ * @param always If true, remove the provider from launching map always, no more restart attempt
+ * @return true if the given provider is in launching
+ */
private final boolean removeDyingProviderLocked(ProcessRecord proc,
ContentProviderRecord cpr, boolean always) {
- final boolean inLaunching = mLaunchingProviders.contains(cpr);
+ boolean inLaunching = mLaunchingProviders.contains(cpr);
+ if (inLaunching && !always && ++cpr.mRestartCount > ContentProviderRecord.MAX_RETRY_COUNT) {
+ // It's being launched but we've reached maximum attempts, force the removal
+ always = true;
+ }
if (!inLaunching || always) {
synchronized (cpr) {
@@ -13973,6 +13995,8 @@ public class ActivityManagerService extends IActivityManager.Stub
if (inLaunching && always) {
mLaunchingProviders.remove(cpr);
+ cpr.mRestartCount = 0;
+ inLaunching = false;
}
return inLaunching;
}
@@ -14188,6 +14212,10 @@ public class ActivityManagerService extends IActivityManager.Stub
for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) {
ContentProviderRecord cpr = mLaunchingProviders.get(i);
if (cpr.launchingApp == app) {
+ if (++cpr.mRestartCount > ContentProviderRecord.MAX_RETRY_COUNT) {
+ // It's being launched but we've reached maximum attempts, mark it as bad
+ alwaysBad = true;
+ }
if (!alwaysBad && !app.bad && cpr.hasConnectionOrHandle()) {
restart = true;
} else {
@@ -14967,12 +14995,10 @@ public class ActivityManagerService extends IActivityManager.Stub
HashSet<ComponentName> singleUserReceivers = null;
boolean scannedFirstReceivers = false;
for (int user : users) {
- // Skip users that have Shell restrictions, with exception of always permitted
- // Shell broadcasts
+ // Skip users that have Shell restrictions
if (callingUid == SHELL_UID
&& mUserController.hasUserRestriction(
- UserManager.DISALLOW_DEBUGGING_FEATURES, user)
- && !isPermittedShellBroadcast(intent)) {
+ UserManager.DISALLOW_DEBUGGING_FEATURES, user)) {
continue;
}
List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
@@ -15039,11 +15065,6 @@ public class ActivityManagerService extends IActivityManager.Stub
return receivers;
}
- private boolean isPermittedShellBroadcast(Intent intent) {
- // remote bugreport should always be allowed to be taken
- return INTENT_REMOTE_BUGREPORT_FINISHED.equals(intent.getAction());
- }
-
private void checkBroadcastFromSystem(Intent intent, ProcessRecord callerApp,
String callerPackage, int callingUid, boolean isProtectedBroadcast, List receivers) {
if ((intent.getFlags() & Intent.FLAG_RECEIVER_FROM_SHELL) != 0) {
@@ -15338,7 +15359,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mBatteryStatsService.removeUid(uid);
if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
mAppOpsService.resetAllModes(UserHandle.getUserId(uid),
- intent.getData().getSchemeSpecificPart());
+ intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME));
} else {
mAppOpsService.uidRemoved(uid);
}
@@ -17032,7 +17053,7 @@ public class ActivityManagerService extends IActivityManager.Stub
item.foregroundServiceTypes = fgServiceTypes;
if (oomAdj) {
- updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
+ updateOomAdjLocked(proc, OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
}
}
}
@@ -17287,6 +17308,16 @@ public class ActivityManagerService extends IActivityManager.Stub
mOomAdjuster.updateOomAdjLocked(oomAdjReason);
}
+ /*
+ * Update OomAdj for a specific process and its reachable processes.
+ * @param app The process to update
+ * @param oomAdjReason
+ */
+ @GuardedBy("this")
+ final void updateOomAdjLocked(ProcessRecord app, String oomAdjReason) {
+ mOomAdjuster.updateOomAdjLocked(app, oomAdjReason);
+ }
+
@Override
public void makePackageIdle(String packageName, int userId) {
if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
@@ -17353,115 +17384,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- /**
- * Checks if any uid is coming from background to foreground or vice versa and if so, increments
- * the {@link UidRecord#curProcStateSeq} corresponding to that uid using global seq counter
- * {@link ProcessList#mProcStateSeqCounter} and notifies the app if it needs to block.
- */
- @VisibleForTesting
- @GuardedBy("this")
- void incrementProcStateSeqAndNotifyAppsLocked() {
- if (mWaitForNetworkTimeoutMs <= 0) {
- return;
- }
- // Used for identifying which uids need to block for network.
- ArrayList<Integer> blockingUids = null;
- for (int i = mProcessList.mActiveUids.size() - 1; i >= 0; --i) {
- final UidRecord uidRec = mProcessList.mActiveUids.valueAt(i);
- // If the network is not restricted for uid, then nothing to do here.
- if (!mInjector.isNetworkRestrictedForUid(uidRec.uid)) {
- continue;
- }
- if (!UserHandle.isApp(uidRec.uid) || !uidRec.hasInternetPermission) {
- continue;
- }
- // If process state is not changed, then there's nothing to do.
- if (uidRec.setProcState == uidRec.getCurProcState()) {
- continue;
- }
- final int blockState = getBlockStateForUid(uidRec);
- // No need to inform the app when the blockState is NETWORK_STATE_NO_CHANGE as
- // there's nothing the app needs to do in this scenario.
- if (blockState == NETWORK_STATE_NO_CHANGE) {
- continue;
- }
- synchronized (uidRec.networkStateLock) {
- uidRec.curProcStateSeq = ++mProcessList.mProcStateSeqCounter; // TODO: use method
- if (blockState == NETWORK_STATE_BLOCK) {
- if (blockingUids == null) {
- blockingUids = new ArrayList<>();
- }
- blockingUids.add(uidRec.uid);
- } else {
- if (DEBUG_NETWORK) {
- Slog.d(TAG_NETWORK, "uid going to background, notifying all blocking"
- + " threads for uid: " + uidRec);
- }
- if (uidRec.waitingForNetwork) {
- uidRec.networkStateLock.notifyAll();
- }
- }
- }
- }
-
- // There are no uids that need to block, so nothing more to do.
- if (blockingUids == null) {
- return;
- }
-
- for (int i = mProcessList.mLruProcesses.size() - 1; i >= 0; --i) {
- final ProcessRecord app = mProcessList.mLruProcesses.get(i);
- if (!blockingUids.contains(app.uid)) {
- continue;
- }
- if (!app.killedByAm && app.thread != null) {
- final UidRecord uidRec = mProcessList.getUidRecordLocked(app.uid);
- try {
- if (DEBUG_NETWORK) {
- Slog.d(TAG_NETWORK, "Informing app thread that it needs to block: "
- + uidRec);
- }
- if (uidRec != null) {
- app.thread.setNetworkBlockSeq(uidRec.curProcStateSeq);
- }
- } catch (RemoteException ignored) {
- }
- }
- }
- }
-
- /**
- * Checks if the uid is coming from background to foreground or vice versa and returns
- * appropriate block state based on this.
- *
- * @return blockState based on whether the uid is coming from background to foreground or
- * vice versa. If bg->fg or fg->bg, then {@link #NETWORK_STATE_BLOCK} or
- * {@link #NETWORK_STATE_UNBLOCK} respectively, otherwise
- * {@link #NETWORK_STATE_NO_CHANGE}.
- */
- @VisibleForTesting
- int getBlockStateForUid(UidRecord uidRec) {
- // Denotes whether uid's process state is currently allowed network access.
- final boolean isAllowed =
- isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getCurProcState())
- || isProcStateAllowedWhileOnRestrictBackground(uidRec.getCurProcState());
- // Denotes whether uid's process state was previously allowed network access.
- final boolean wasAllowed = isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.setProcState)
- || isProcStateAllowedWhileOnRestrictBackground(uidRec.setProcState);
-
- // When the uid is coming to foreground, AMS should inform the app thread that it should
- // block for the network rules to get updated before launching an activity.
- if (!wasAllowed && isAllowed) {
- return NETWORK_STATE_BLOCK;
- }
- // When the uid is going to background, AMS should inform the app thread that if an
- // activity launch is blocked for the network rules to get updated, it should be unblocked.
- if (wasAllowed && !isAllowed) {
- return NETWORK_STATE_UNBLOCK;
- }
- return NETWORK_STATE_NO_CHANGE;
- }
-
final void runInBackgroundDisabled(int uid) {
synchronized (this) {
UidRecord uidRec = mProcessList.getUidRecordLocked(uid);
@@ -18371,7 +18293,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public int getCurrentUserId() {
- return mUserController.getCurrentUserIdLU();
+ return mUserController.getCurrentUserId();
}
@Override
@@ -19316,18 +19238,19 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public int noteOperation(int code, int uid, String packageName,
- TriFunction<Integer, Integer, String, Integer> superImpl) {
+ public int noteOperation(int code, int uid, @Nullable String packageName,
+ @Nullable String featureId,
+ @NonNull QuadFunction<Integer, Integer, String, String, Integer> superImpl) {
if (uid == mTargetUid && isTargetOp(code)) {
final long identity = Binder.clearCallingIdentity();
try {
return mAppOpsService.noteProxyOperation(code, Process.SHELL_UID,
- "com.android.shell", uid, packageName);
+ "com.android.shell", null, uid, packageName, featureId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- return superImpl.apply(code, uid, packageName);
+ return superImpl.apply(code, uid, packageName, featureId);
}
@Override
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index d46c62662faf..8be2438b4f2f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -1016,18 +1016,23 @@ final class ActivityManagerShellCommand extends ShellCommand {
int runBugReport(PrintWriter pw) throws RemoteException {
String opt;
- int bugreportType = ActivityManager.BUGREPORT_OPTION_FULL;
+ boolean fullBugreport = true;
while ((opt=getNextOption()) != null) {
if (opt.equals("--progress")) {
- bugreportType = ActivityManager.BUGREPORT_OPTION_INTERACTIVE;
+ fullBugreport = false;
+ mInterface.requestInteractiveBugReport();
} else if (opt.equals("--telephony")) {
- bugreportType = ActivityManager.BUGREPORT_OPTION_TELEPHONY;
+ fullBugreport = false;
+ // no title and description specified
+ mInterface.requestTelephonyBugReport("" /* no title */, "" /* no descriptions */);
} else {
getErrPrintWriter().println("Error: Unknown option: " + opt);
return -1;
}
}
- mInterface.requestBugReport(bugreportType);
+ if (fullBugreport) {
+ mInterface.requestFullBugReport();
+ }
pw.println("Your lovely bug report is being created; please be patient.");
return 0;
}
@@ -2854,7 +2859,21 @@ final class ActivityManagerShellCommand extends ShellCommand {
return 0;
}
- private int runCompat(PrintWriter pw) {
+ private void killPackage(String packageName, PrintWriter pw) throws RemoteException {
+ int uid = mPm.getPackageUid(packageName, 0, mUserId);
+ if (uid < 0) {
+ // uid is negative if the package wasn't found.
+ pw.println("Didn't find package " + packageName + " on device.");
+ } else {
+ pw.println("Killing package " + packageName + " (UID " + uid + ").");
+ final long origId = Binder.clearCallingIdentity();
+ mInterface.killUid(UserHandle.getAppId(uid),
+ UserHandle.USER_ALL, "killPackage");
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ private int runCompat(PrintWriter pw) throws RemoteException {
final CompatConfig config = CompatConfig.get();
String toggleValue = getNextArgRequired();
long changeId;
@@ -2868,13 +2887,14 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println("Unknown or invalid change: '" + changeIdString + "'.");
}
String packageName = getNextArgRequired();
- switch(toggleValue) {
+ switch (toggleValue) {
case "enable":
if (!config.addOverride(changeId, packageName, true)) {
pw.println("Warning! Change " + changeId + " is not known yet. Enabling it"
+ " could have no effect.");
}
pw.println("Enabled change " + changeId + " for " + packageName + ".");
+ killPackage(packageName, pw);
return 0;
case "disable":
if (!config.addOverride(changeId, packageName, false)) {
@@ -2882,11 +2902,13 @@ final class ActivityManagerShellCommand extends ShellCommand {
+ " could have no effect.");
}
pw.println("Disabled change " + changeId + " for " + packageName + ".");
+ killPackage(packageName, pw);
return 0;
case "reset":
if (config.removeOverride(changeId, packageName)) {
pw.println("Reset change " + changeId + " for " + packageName
+ " to default value.");
+ killPackage(packageName, pw);
} else {
pw.println("No override exists for changeId " + changeId + ".");
}
@@ -3205,6 +3227,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println(" Write all pending state to storage.");
pw.println(" compat enable|disable|reset <CHANGE_ID|CHANGE_NAME> <PACKAGE_NAME>");
pw.println(" Toggles a change either by id or by name for <PACKAGE_NAME>.");
+ pw.println(" It kills <PACKAGE_NAME> (to allow the toggle to take effect).");
pw.println();
Intent.printIntentArgsHelp(pw, "");
}
diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java
index a80a5b5211ea..852c9b65e3c0 100644
--- a/services/core/java/com/android/server/am/AppErrorDialog.java
+++ b/services/core/java/com/android/server/am/AppErrorDialog.java
@@ -92,7 +92,7 @@ final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListen
WindowManager.LayoutParams attrs = getWindow().getAttributes();
attrs.setTitle("Application Error: " + mProc.info.processName);
attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR
- | WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ | WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
getWindow().setAttributes(attrs);
if (mProc.isPersistent()) {
getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index cb76e2fafebd..65d7e01a3e0c 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -94,7 +94,7 @@ final class AppNotRespondingDialog extends BaseErrorDialog implements View.OnCli
WindowManager.LayoutParams attrs = getWindow().getAttributes();
attrs.setTitle("Application Not Responding: " + mProc.info.processName);
attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR |
- WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
getWindow().setAttributes(attrs);
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 56208a9565e1..1d03b36d032b 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -309,7 +309,7 @@ public final class BroadcastQueue {
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
mService.mProcessList.updateLruProcessLocked(app, false, null);
if (!skipOomAdj) {
- mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
+ mService.updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_NONE);
}
// Tell the application to launch this receiver.
@@ -446,7 +446,9 @@ public final class BroadcastQueue {
mHandler.removeCallbacksAndMessages(msgToken);
// ...then schedule the removal of the token after the extended timeout
mHandler.postAtTime(() -> {
- app.removeAllowBackgroundActivityStartsToken(r);
+ synchronized (mService) {
+ app.removeAllowBackgroundActivityStartsToken(r);
+ }
}, msgToken, (r.receiverTime + mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT));
}
@@ -645,9 +647,10 @@ public final class BroadcastQueue {
skip = true;
} else {
final int opCode = AppOpsManager.permissionToOpCode(filter.requiredPermission);
+ // TODO moltmann: Set featureId from caller
if (opCode != AppOpsManager.OP_NONE
&& mService.mAppOpsService.noteOperation(opCode, r.callingUid,
- r.callerPackage) != AppOpsManager.MODE_ALLOWED) {
+ r.callerPackage, null) != AppOpsManager.MODE_ALLOWED) {
Slog.w(TAG, "Appop Denial: broadcasting "
+ r.intent.toString()
+ " from " + r.callerPackage + " (pid="
@@ -677,9 +680,10 @@ public final class BroadcastQueue {
break;
}
int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
+ // TODO moltmann: Set componentId from caller
if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
&& mService.mAppOpsService.noteOperation(appOp,
- filter.receiverList.uid, filter.packageName)
+ filter.receiverList.uid, filter.packageName, null)
!= AppOpsManager.MODE_ALLOWED) {
Slog.w(TAG, "Appop Denial: receiving "
+ r.intent.toString()
@@ -709,9 +713,10 @@ public final class BroadcastQueue {
skip = true;
}
}
+ // TODO moltmann: Set componentId from caller
if (!skip && r.appOp != AppOpsManager.OP_NONE
&& mService.mAppOpsService.noteOperation(r.appOp,
- filter.receiverList.uid, filter.packageName)
+ filter.receiverList.uid, filter.packageName, null)
!= AppOpsManager.MODE_ALLOWED) {
Slog.w(TAG, "Appop Denial: receiving "
+ r.intent.toString()
@@ -1365,9 +1370,10 @@ public final class BroadcastQueue {
skip = true;
} else if (!skip && info.activityInfo.permission != null) {
final int opCode = AppOpsManager.permissionToOpCode(info.activityInfo.permission);
+ // TODO moltmann: Set componentId from caller
if (opCode != AppOpsManager.OP_NONE
&& mService.mAppOpsService.noteOperation(opCode, r.callingUid,
- r.callerPackage) != AppOpsManager.MODE_ALLOWED) {
+ r.callerPackage, null) != AppOpsManager.MODE_ALLOWED) {
Slog.w(TAG, "Appop Denial: broadcasting "
+ r.intent.toString()
+ " from " + r.callerPackage + " (pid="
@@ -1403,9 +1409,10 @@ public final class BroadcastQueue {
break;
}
int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
+ // TODO moltmann: Set componentId from caller
if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
&& mService.mAppOpsService.noteOperation(appOp,
- info.activityInfo.applicationInfo.uid, info.activityInfo.packageName)
+ info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, null)
!= AppOpsManager.MODE_ALLOWED) {
Slog.w(TAG, "Appop Denial: receiving "
+ r.intent + " to "
@@ -1419,9 +1426,10 @@ public final class BroadcastQueue {
}
}
}
+ // TODO moltmann: Set componentId from caller
if (!skip && r.appOp != AppOpsManager.OP_NONE
&& mService.mAppOpsService.noteOperation(r.appOp,
- info.activityInfo.applicationInfo.uid, info.activityInfo.packageName)
+ info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, null)
!= AppOpsManager.MODE_ALLOWED) {
Slog.w(TAG, "Appop Denial: receiving "
+ r.intent + " to "
diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java
index 46dfc7c60fd9..d8d8cccc05f3 100644
--- a/services/core/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/core/java/com/android/server/am/ContentProviderRecord.java
@@ -38,6 +38,9 @@ import java.io.PrintWriter;
import java.util.ArrayList;
final class ContentProviderRecord implements ComponentName.WithComponentName {
+ // Maximum attempts to bring up the content provider before giving up.
+ static final int MAX_RETRY_COUNT = 3;
+
final ActivityManagerService service;
public final ProviderInfo info;
final int uid;
@@ -54,6 +57,7 @@ final class ContentProviderRecord implements ComponentName.WithComponentName {
ArrayMap<IBinder, ExternalProcessHandle> externalProcessTokenToHandle;
// Count for external process for which we have no handles.
int externalProcessNoHandleCount;
+ int mRestartCount; // number of times we tried before bringing up it successfully.
ProcessRecord proc; // if non-null, hosting process.
ProcessRecord launchingApp; // if non-null, waiting for this app to be launched.
String stringName;
diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java
index fd64df9e86e0..2081b177e2b2 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -39,8 +39,7 @@ import java.util.regex.Pattern;
* Static utility methods related to {@link MemoryStat}.
*/
public final class MemoryStatUtil {
- static final int BYTES_IN_KILOBYTE = 1024;
- static final long JIFFY_NANOS = 1_000_000_000 / Os.sysconf(OsConstants._SC_CLK_TCK);
+ static final int PAGE_SIZE = (int) Os.sysconf(OsConstants._SC_PAGESIZE);
private static final String TAG = TAG_WITH_CLASS_NAME ? "MemoryStatUtil" : TAG_AM;
@@ -52,10 +51,6 @@ public final class MemoryStatUtil {
private static final String MEMORY_STAT_FILE_FMT = "/dev/memcg/apps/uid_%d/pid_%d/memory.stat";
/** Path to procfs stat file for logging app start memory state */
private static final String PROC_STAT_FILE_FMT = "/proc/%d/stat";
- /** Path to procfs status file for logging app memory state */
- private static final String PROC_STATUS_FILE_FMT = "/proc/%d/status";
- /** Path to procfs cmdline file. Used with pid: /proc/pid/cmdline. */
- private static final String PROC_CMDLINE_FILE_FMT = "/proc/%d/cmdline";
private static final Pattern PGFAULT = Pattern.compile("total_pgfault (\\d+)");
private static final Pattern PGMAJFAULT = Pattern.compile("total_pgmajfault (\\d+)");
@@ -63,16 +58,9 @@ public final class MemoryStatUtil {
private static final Pattern CACHE_IN_BYTES = Pattern.compile("total_cache (\\d+)");
private static final Pattern SWAP_IN_BYTES = Pattern.compile("total_swap (\\d+)");
- private static final Pattern PROCFS_RSS_IN_KILOBYTES =
- Pattern.compile("VmRSS:\\s*(\\d+)\\s*kB");
- private static final Pattern PROCFS_ANON_RSS_IN_KILOBYTES =
- Pattern.compile("RssAnon:\\s*(\\d+)\\s*kB");
- private static final Pattern PROCFS_SWAP_IN_KILOBYTES =
- Pattern.compile("VmSwap:\\s*(\\d+)\\s*kB");
-
private static final int PGFAULT_INDEX = 9;
private static final int PGMAJFAULT_INDEX = 11;
- private static final int START_TIME_INDEX = 21;
+ private static final int RSS_IN_PAGES_INDEX = 23;
private MemoryStatUtil() {}
@@ -106,19 +94,7 @@ public final class MemoryStatUtil {
@Nullable
public static MemoryStat readMemoryStatFromProcfs(int pid) {
final String statPath = String.format(Locale.US, PROC_STAT_FILE_FMT, pid);
- final String statusPath = String.format(Locale.US, PROC_STATUS_FILE_FMT, pid);
- return parseMemoryStatFromProcfs(readFileContents(statPath), readFileContents(statusPath));
- }
-
- /**
- * Reads cmdline of a process from procfs.
- *
- * Returns content of /proc/pid/cmdline (e.g. /system/bin/statsd) or an empty string
- * if the file is not available.
- */
- public static String readCmdlineFromProcfs(int pid) {
- final String path = String.format(Locale.US, PROC_CMDLINE_FILE_FMT, pid);
- return parseCmdlineFromProcfs(readFileContents(path));
+ return parseMemoryStatFromProcfs(readFileContents(statPath));
}
private static String readFileContents(String path) {
@@ -160,31 +136,19 @@ public final class MemoryStatUtil {
*/
@VisibleForTesting
@Nullable
- static MemoryStat parseMemoryStatFromProcfs(
- String procStatContents, String procStatusContents) {
+ static MemoryStat parseMemoryStatFromProcfs(String procStatContents) {
if (procStatContents == null || procStatContents.isEmpty()) {
return null;
}
- if (procStatusContents == null || procStatusContents.isEmpty()) {
- return null;
- }
-
final String[] splits = procStatContents.split(" ");
if (splits.length < 24) {
return null;
}
-
try {
final MemoryStat memoryStat = new MemoryStat();
memoryStat.pgfault = Long.parseLong(splits[PGFAULT_INDEX]);
memoryStat.pgmajfault = Long.parseLong(splits[PGMAJFAULT_INDEX]);
- memoryStat.rssInBytes =
- tryParseLong(PROCFS_RSS_IN_KILOBYTES, procStatusContents) * BYTES_IN_KILOBYTE;
- memoryStat.anonRssInBytes =
- tryParseLong(PROCFS_ANON_RSS_IN_KILOBYTES, procStatusContents) * BYTES_IN_KILOBYTE;
- memoryStat.swapInBytes =
- tryParseLong(PROCFS_SWAP_IN_KILOBYTES, procStatusContents) * BYTES_IN_KILOBYTE;
- memoryStat.startTimeNanos = Long.parseLong(splits[START_TIME_INDEX]) * JIFFY_NANOS;
+ memoryStat.rssInBytes = Long.parseLong(splits[RSS_IN_PAGES_INDEX]) * PAGE_SIZE;
return memoryStat;
} catch (NumberFormatException e) {
Slog.e(TAG, "Failed to parse value", e);
@@ -193,23 +157,6 @@ public final class MemoryStatUtil {
}
/**
- * Parses cmdline out of the contents of the /proc/pid/cmdline file in procfs.
- *
- * Parsing is required to strip anything after first null byte.
- */
- @VisibleForTesting
- static String parseCmdlineFromProcfs(String cmdline) {
- if (cmdline == null) {
- return "";
- }
- int firstNullByte = cmdline.indexOf("\0");
- if (firstNullByte == -1) {
- return cmdline;
- }
- return cmdline.substring(0, firstNullByte);
- }
-
- /**
* Returns whether per-app memcg is available on device.
*/
static boolean hasMemcg() {
@@ -237,13 +184,9 @@ public final class MemoryStatUtil {
public long pgmajfault;
/** For memcg stats, the anon rss + swap cache size. Otherwise total RSS. */
public long rssInBytes;
- /** Number of bytes of the anonymous RSS. Only present for non-memcg stats. */
- public long anonRssInBytes;
/** Number of bytes of page cache memory. Only present for memcg stats. */
public long cacheInBytes;
/** Number of bytes of swap usage */
public long swapInBytes;
- /** Device time when the processes started. */
- public long startTimeNanos;
}
}
diff --git a/services/core/java/com/android/server/am/OomAdjProfiler.java b/services/core/java/com/android/server/am/OomAdjProfiler.java
index 7e38184040d9..08691145bf6e 100644
--- a/services/core/java/com/android/server/am/OomAdjProfiler.java
+++ b/services/core/java/com/android/server/am/OomAdjProfiler.java
@@ -45,7 +45,7 @@ public class OomAdjProfiler {
private boolean mLastScheduledScreenOff;
@GuardedBy("this")
- private long mOomAdjStartTimeMs;
+ private long mOomAdjStartTimeUs;
@GuardedBy("this")
private boolean mOomAdjStarted;
@@ -65,6 +65,11 @@ public class OomAdjProfiler {
@GuardedBy("this")
final RingBuffer<CpuTimes> mSystemServerCpuTimesHist = new RingBuffer<>(CpuTimes.class, 10);
+ @GuardedBy("this")
+ private long mTotalOomAdjRunTimeUs;
+ @GuardedBy("this")
+ private int mTotalOomAdjCalls;
+
void batteryPowerChanged(boolean onBattery) {
synchronized (this) {
scheduleSystemServerCpuTimeUpdate();
@@ -81,7 +86,7 @@ public class OomAdjProfiler {
void oomAdjStarted() {
synchronized (this) {
- mOomAdjStartTimeMs = SystemClock.currentThreadTimeMillis();
+ mOomAdjStartTimeUs = SystemClock.currentThreadTimeMicro();
mOomAdjStarted = true;
}
}
@@ -91,7 +96,10 @@ public class OomAdjProfiler {
if (!mOomAdjStarted) {
return;
}
- mOomAdjRunTime.addCpuTimeMs(SystemClock.currentThreadTimeMillis() - mOomAdjStartTimeMs);
+ long elapsedUs = SystemClock.currentThreadTimeMicro() - mOomAdjStartTimeUs;
+ mOomAdjRunTime.addCpuTimeUs(elapsedUs);
+ mTotalOomAdjRunTimeUs += elapsedUs;
+ mTotalOomAdjCalls++;
}
}
@@ -169,32 +177,50 @@ public class OomAdjProfiler {
pw.print("oom_adj=");
pw.println(oomAdjRunTimes[i]);
}
+ if (mTotalOomAdjCalls != 0) {
+ pw.println("System server total oomAdj runtimes (us) since boot:");
+ pw.print(" cpu time spent=");
+ pw.print(mTotalOomAdjRunTimeUs);
+ pw.print(" number of calls=");
+ pw.print(mTotalOomAdjCalls);
+ pw.print(" average=");
+ pw.println(mTotalOomAdjRunTimeUs / mTotalOomAdjCalls);
+ }
}
}
private class CpuTimes {
- private long mOnBatteryTimeMs;
- private long mOnBatteryScreenOffTimeMs;
+ private long mOnBatteryTimeUs;
+ private long mOnBatteryScreenOffTimeUs;
public void addCpuTimeMs(long cpuTimeMs) {
- addCpuTimeMs(cpuTimeMs, mOnBattery, mScreenOff);
+ addCpuTimeUs(cpuTimeMs * 1000, mOnBattery, mScreenOff);
}
public void addCpuTimeMs(long cpuTimeMs, boolean onBattery, boolean screenOff) {
+ addCpuTimeUs(cpuTimeMs * 1000, onBattery, screenOff);
+ }
+
+ public void addCpuTimeUs(long cpuTimeUs) {
+ addCpuTimeUs(cpuTimeUs, mOnBattery, mScreenOff);
+ }
+
+ public void addCpuTimeUs(long cpuTimeUs, boolean onBattery, boolean screenOff) {
if (onBattery) {
- mOnBatteryTimeMs += cpuTimeMs;
+ mOnBatteryTimeUs += cpuTimeUs;
if (screenOff) {
- mOnBatteryScreenOffTimeMs += cpuTimeMs;
+ mOnBatteryScreenOffTimeUs += cpuTimeUs;
}
}
}
public boolean isEmpty() {
- return mOnBatteryTimeMs == 0 && mOnBatteryScreenOffTimeMs == 0;
+ return mOnBatteryTimeUs == 0 && mOnBatteryScreenOffTimeUs == 0;
}
public String toString() {
- return "[" + mOnBatteryTimeMs + "," + mOnBatteryScreenOffTimeMs + "]";
+ return "[" + (mOnBatteryTimeUs / 1000) + ","
+ + (mOnBatteryScreenOffTimeUs / 1000) + "]";
}
}
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 21aed4e215cf..9d27468022c0 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -19,7 +19,9 @@ package com.android.server.am;
import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_RECENT;
import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE_LOCATION;
import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
@@ -30,7 +32,7 @@ import static android.app.ActivityManager.PROCESS_STATE_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
import static android.os.Process.SCHED_OTHER;
-import static android.os.Process.THREAD_GROUP_BG_NONINTERACTIVE;
+import static android.os.Process.THREAD_GROUP_BACKGROUND;
import static android.os.Process.THREAD_GROUP_DEFAULT;
import static android.os.Process.THREAD_GROUP_RESTRICTED;
import static android.os.Process.THREAD_GROUP_TOP_APP;
@@ -62,6 +64,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
import android.app.ActivityManager;
import android.app.usage.UsageEvents;
import android.content.Context;
+import android.content.pm.ServiceInfo;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
@@ -87,6 +90,7 @@ import com.android.server.wm.ActivityServiceConnectionsHolder;
import com.android.server.wm.WindowProcessController;
import java.io.PrintWriter;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
@@ -173,6 +177,12 @@ public final class OomAdjuster {
public static BoostFramework mPerf = new BoostFramework();
+ private final int mNumSlots;
+ private ArrayList<ProcessRecord> mTmpProcessList = new ArrayList<ProcessRecord>();
+ private ArrayList<UidRecord> mTmpBecameIdle = new ArrayList<UidRecord>();
+ private ActiveUids mTmpUidRecords;
+ private ArrayDeque<ProcessRecord> mTmpQueue;
+
OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids) {
this(service, processList, activeUids, createAdjusterThread());
}
@@ -221,6 +231,10 @@ public final class OomAdjuster {
}
return true;
});
+ mTmpUidRecords = new ActiveUids(service, false);
+ mTmpQueue = new ArrayDeque<ProcessRecord>(mConstants.CUR_MAX_CACHED_PROCESSES << 1);
+ mNumSlots = ((ProcessList.CACHED_APP_MAX_ADJ - ProcessList.CACHED_APP_MIN_ADJ + 1) >> 1)
+ / ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
}
void initSettings() {
@@ -238,6 +252,9 @@ public final class OomAdjuster {
@GuardedBy("mService")
boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll,
String oomAdjReason) {
+ if (oomAdjAll && mConstants.OOMADJ_UPDATE_QUICK) {
+ return updateOomAdjLocked(app, oomAdjReason);
+ }
final ProcessRecord TOP_APP = mService.getTopAppLocked();
final boolean wasCached = app.cached;
@@ -267,26 +284,189 @@ public final class OomAdjuster {
return false;
}
- computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now, false);
+ app.resetCachedInfo();
+ UidRecord uidRec = app.uidRecord;
+ if (uidRec != null) {
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "Starting update of " + uidRec);
+ }
+ uidRec.reset();
+ }
+
+ computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now, false, true);
+
+ boolean success = applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime());
+
+ if (uidRec != null) {
+ updateAppUidRecLocked(app);
+ // If this proc state is changed, need to update its uid record here
+ if (uidRec.getCurProcState() != PROCESS_STATE_NONEXISTENT
+ && (uidRec.setProcState != uidRec.getCurProcState()
+ || uidRec.setWhitelist != uidRec.curWhitelist)) {
+ ActiveUids uids = mTmpUidRecords;
+ uids.clear();
+ uids.put(uidRec.uid, uidRec);
+ updateUidsLocked(uids, now);
+ mProcessList.incrementProcStateSeqAndNotifyAppsLocked(uids);
+ }
+ }
- return applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime());
+ return success;
}
+ /**
+ * Update OomAdj for all processes in LRU list
+ */
@GuardedBy("mService")
void updateOomAdjLocked(String oomAdjReason) {
+ final ProcessRecord topApp = mService.getTopAppLocked();
+ updateOomAdjLockedInner(oomAdjReason, topApp , null, null, true);
+ }
+
+ /**
+ * Update OomAdj for specific process and its reachable processes (with direction/indirect
+ * bindings from this process); Note its clients' proc state won't be re-evaluated if this proc
+ * is hosting any service/content provider.
+ *
+ * @param app The process to update, or null to update all processes
+ * @param oomAdjReason
+ */
+ @GuardedBy("mService")
+ boolean updateOomAdjLocked(ProcessRecord app, String oomAdjReason) {
+ if (app == null || !mConstants.OOMADJ_UPDATE_QUICK) {
+ updateOomAdjLocked(oomAdjReason);
+ return true;
+ }
+
+ final ProcessRecord topApp = mService.getTopAppLocked();
+
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason);
mService.mOomAdjProfiler.oomAdjStarted();
- final ProcessRecord TOP_APP = mService.getTopAppLocked();
+ mAdjSeq++;
+
+ // Firstly, try to see if the importance of itself gets changed
+ final boolean wasCached = app.cached;
+ final int oldAdj = app.getCurRawAdj();
+ final int cachedAdj = oldAdj >= ProcessList.CACHED_APP_MIN_ADJ
+ ? oldAdj : ProcessList.UNKNOWN_ADJ;
+ final boolean wasBackground = ActivityManager.isProcStateBackground(app.setProcState);
+ app.containsCycle = false;
+ app.procStateChanged = false;
+ app.resetCachedInfo();
+ boolean success = updateOomAdjLocked(app, cachedAdj, topApp, false,
+ SystemClock.uptimeMillis());
+ if (!success || (wasCached == app.cached && oldAdj != ProcessList.INVALID_ADJ
+ && wasBackground == ActivityManager.isProcStateBackground(app.setProcState))) {
+ // Okay, it's unchanged, it won't impact any service it binds to, we're done here.
+ if (DEBUG_OOM_ADJ) {
+ Slog.i(TAG_OOM_ADJ, "No oomadj changes for " + app);
+ }
+ mService.mOomAdjProfiler.oomAdjEnded();
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ return success;
+ }
+
+ // Next to find out all its reachable processes
+ ArrayList<ProcessRecord> processes = mTmpProcessList;
+ ActiveUids uids = mTmpUidRecords;
+ ArrayDeque<ProcessRecord> queue = mTmpQueue;
+
+ processes.clear();
+ uids.clear();
+ queue.clear();
+
+ // borrow the "containsCycle" flag to mark it being scanned
+ app.containsCycle = true;
+ for (ProcessRecord pr = app; pr != null; pr = queue.poll()) {
+ if (pr != app) {
+ processes.add(pr);
+ }
+ if (pr.uidRecord != null) {
+ uids.put(pr.uidRecord.uid, pr.uidRecord);
+ }
+ for (int i = pr.connections.size() - 1; i >= 0; i--) {
+ ConnectionRecord cr = pr.connections.valueAt(i);
+ ProcessRecord service = (cr.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0
+ ? cr.binding.service.isolatedProc : cr.binding.service.app;
+ if (service == null || service.containsCycle) {
+ continue;
+ }
+ if ((cr.flags & (Context.BIND_WAIVE_PRIORITY
+ | Context.BIND_TREAT_LIKE_ACTIVITY
+ | Context.BIND_ADJUST_WITH_ACTIVITY))
+ == Context.BIND_WAIVE_PRIORITY) {
+ continue;
+ }
+ queue.offer(service);
+ service.containsCycle = true;
+ }
+ for (int i = pr.conProviders.size() - 1; i >= 0; i--) {
+ ContentProviderConnection cpc = pr.conProviders.get(i);
+ ProcessRecord provider = cpc.provider.proc;
+ if (provider == null || provider.containsCycle) {
+ continue;
+ }
+ queue.offer(provider);
+ provider.containsCycle = true;
+ }
+ }
+
+ // Reset the flag
+ app.containsCycle = false;
+ int size = processes.size();
+ if (size > 0) {
+ // Reverse the process list, since the updateOomAdjLockedInner scans from the end of it.
+ for (int l = 0, r = size - 1; l < r; l++, r--) {
+ ProcessRecord t = processes.get(l);
+ processes.set(l, processes.get(r));
+ processes.set(r, t);
+ }
+ mAdjSeq--;
+ // Update these reachable processes
+ updateOomAdjLockedInner(oomAdjReason, topApp, processes, uids, false);
+ }
+ mService.mOomAdjProfiler.oomAdjEnded();
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ return true;
+ }
+
+ /**
+ * Update OomAdj for all processes within the given list (could be partial), or the whole LRU
+ * list if the given list is null; when it's partial update, each process's client proc won't
+ * get evaluated recursively here.
+ */
+ @GuardedBy("mService")
+ private void updateOomAdjLockedInner(String oomAdjReason, final ProcessRecord topApp,
+ ArrayList<ProcessRecord> processes, ActiveUids uids, boolean startProfiling) {
+ if (startProfiling) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason);
+ mService.mOomAdjProfiler.oomAdjStarted();
+ }
final long now = SystemClock.uptimeMillis();
final long nowElapsed = SystemClock.elapsedRealtime();
final long oldTime = now - ProcessList.MAX_EMPTY_TIME;
- final int N = mProcessList.getLruSizeLocked();
+ final boolean fullUpdate = processes == null;
+ ActiveUids activeUids = uids;
+ ArrayList<ProcessRecord> activeProcesses = fullUpdate ? mProcessList.mLruProcesses
+ : processes;
+ final int numProc = activeProcesses.size();
+
+ if (activeUids == null) {
+ final int numUids = mActiveUids.size();
+ activeUids = mTmpUidRecords;
+ activeUids.clear();
+ for (int i = 0; i < numUids; i++) {
+ UidRecord r = mActiveUids.valueAt(i);
+ activeUids.put(r.uid, r);
+ }
+ }
// Reset state in all uid records.
- for (int i = mActiveUids.size() - 1; i >= 0; i--) {
- final UidRecord uidRec = mActiveUids.valueAt(i);
- if (false && DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
- "Starting update of " + uidRec);
+ for (int i = activeUids.size() - 1; i >= 0; i--) {
+ final UidRecord uidRec = activeUids.valueAt(i);
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "Starting update of " + uidRec);
+ }
uidRec.reset();
}
@@ -295,21 +475,128 @@ public final class OomAdjuster {
}
mAdjSeq++;
- mNewNumServiceProcs = 0;
- mNewNumAServiceProcs = 0;
+ if (fullUpdate) {
+ mNewNumServiceProcs = 0;
+ mNewNumAServiceProcs = 0;
+ }
+
+ boolean retryCycles = false;
+
+ // need to reset cycle state before calling computeOomAdjLocked because of service conns
+ for (int i = numProc - 1; i >= 0; i--) {
+ ProcessRecord app = activeProcesses.get(i);
+ app.containsCycle = false;
+ app.setCurRawProcState(PROCESS_STATE_CACHED_EMPTY);
+ app.setCurRawAdj(ProcessList.UNKNOWN_ADJ);
+ app.resetCachedInfo();
+ }
+ for (int i = numProc - 1; i >= 0; i--) {
+ ProcessRecord app = activeProcesses.get(i);
+ if (!app.killedByAm && app.thread != null) {
+ app.procStateChanged = false;
+ computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, topApp, fullUpdate, now, false,
+ fullUpdate); // It won't enter cycle if not computing clients.
+ // if any app encountered a cycle, we need to perform an additional loop later
+ retryCycles |= app.containsCycle;
+ // Keep the completedAdjSeq to up to date.
+ app.completedAdjSeq = mAdjSeq;
+ }
+ }
+
+ assignCachedAdjIfNecessary();
+
+ if (fullUpdate) { // There won't be cycles if we didn't compute clients above.
+ // Cycle strategy:
+ // - Retry computing any process that has encountered a cycle.
+ // - Continue retrying until no process was promoted.
+ // - Iterate from least important to most important.
+ int cycleCount = 0;
+ while (retryCycles && cycleCount < 10) {
+ cycleCount++;
+ retryCycles = false;
+
+ for (int i = 0; i < numProc; i++) {
+ ProcessRecord app = activeProcesses.get(i);
+ if (!app.killedByAm && app.thread != null && app.containsCycle) {
+ app.adjSeq--;
+ app.completedAdjSeq--;
+ }
+ }
+
+ for (int i = 0; i < numProc; i++) {
+ ProcessRecord app = activeProcesses.get(i);
+ if (!app.killedByAm && app.thread != null && app.containsCycle) {
+ if (computeOomAdjLocked(app, app.getCurRawAdj(), topApp, true, now,
+ true, true)) {
+ retryCycles = true;
+ }
+ }
+ }
+ }
+ }
+
+ mNumNonCachedProcs = 0;
+ mNumCachedHiddenProcs = 0;
+
+ boolean allChanged = updateAndTrimProcessLocked(now, nowElapsed, oldTime, activeUids);
+ mNumServiceProcs = mNewNumServiceProcs;
+
+ if (mService.mAlwaysFinishActivities) {
+ // Need to do this on its own message because the stack may not
+ // be in a consistent state at this point.
+ mService.mAtmInternal.scheduleDestroyAllActivities("always-finish");
+ }
+
+ if (allChanged) {
+ mService.requestPssAllProcsLocked(now, false,
+ mService.mProcessStats.isMemFactorLowered());
+ }
+
+ updateUidsLocked(activeUids, nowElapsed);
+
+ if (mService.mProcessStats.shouldWriteNowLocked(now)) {
+ mService.mHandler.post(new ActivityManagerService.ProcStatsRunnable(mService,
+ mService.mProcessStats));
+ }
+
+ // Run this after making sure all procstates are updated.
+ mService.mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, now);
+
+ if (DEBUG_OOM_ADJ) {
+ final long duration = SystemClock.uptimeMillis() - now;
+ if (false) {
+ Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms",
+ new RuntimeException("here").fillInStackTrace());
+ } else {
+ Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms");
+ }
+ }
+ if (startProfiling) {
+ mService.mOomAdjProfiler.oomAdjEnded();
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+ }
+
+ private void assignCachedAdjIfNecessary() {
+ ArrayList<ProcessRecord> lruList = mProcessList.mLruProcesses;
+ final int numLru = lruList.size();
+
+ // First update the OOM adjustment for each of the
+ // application processes based on their current state.
+ int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ;
+ int nextCachedAdj = curCachedAdj + (ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2);
+ int curCachedImpAdj = 0;
+ int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
+ int nextEmptyAdj = curEmptyAdj + (ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2);
final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES;
final int cachedProcessLimit = mConstants.CUR_MAX_CACHED_PROCESSES
- emptyProcessLimit;
-
// Let's determine how many processes we have running vs.
// how many slots we have for background processes; we may want
// to put multiple processes in a slot of there are enough of
// them.
- final int numSlots = (ProcessList.CACHED_APP_MAX_ADJ
- - ProcessList.CACHED_APP_MIN_ADJ + 1) / 2
- / ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
- int numEmptyProcs = N - mNumNonCachedProcs - mNumCachedHiddenProcs;
+ int numEmptyProcs = numLru - mNumNonCachedProcs - mNumCachedHiddenProcs;
if (numEmptyProcs > cachedProcessLimit) {
// If there are more empty processes than our limit on cached
// processes, then use the cached process limit for the factor.
@@ -319,46 +606,124 @@ public final class OomAdjuster {
// instead of a gazillion empty processes.
numEmptyProcs = cachedProcessLimit;
}
- int emptyFactor = (numEmptyProcs + numSlots - 1) / numSlots;
- if (emptyFactor < 1) emptyFactor = 1;
- int cachedFactor = (mNumCachedHiddenProcs > 0 ? (mNumCachedHiddenProcs + numSlots - 1) : 1)
- / numSlots;
+ int cachedFactor = (mNumCachedHiddenProcs > 0 ? (mNumCachedHiddenProcs + mNumSlots - 1) : 1)
+ / mNumSlots;
if (cachedFactor < 1) cachedFactor = 1;
+
+ int emptyFactor = (numEmptyProcs + mNumSlots - 1) / mNumSlots;
+ if (emptyFactor < 1) emptyFactor = 1;
+
int stepCached = -1;
int stepEmpty = -1;
- int numCached = 0;
- int numCachedExtraGroup = 0;
- int numEmpty = 0;
- int numTrimming = 0;
int lastCachedGroup = 0;
int lastCachedGroupImportance = 0;
int lastCachedGroupUid = 0;
- mNumNonCachedProcs = 0;
- mNumCachedHiddenProcs = 0;
+ for (int i = numLru - 1; i >= 0; i--) {
+ ProcessRecord app = lruList.get(i);
+ // If we haven't yet assigned the final cached adj
+ // to the process, do that now.
+ if (!app.killedByAm && app.thread != null && app.curAdj
+ >= ProcessList.UNKNOWN_ADJ) {
+ switch (app.getCurProcState()) {
+ case PROCESS_STATE_CACHED_ACTIVITY:
+ case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
+ case ActivityManager.PROCESS_STATE_CACHED_RECENT:
+ // Figure out the next cached level, taking into account groups.
+ boolean inGroup = false;
+ if (app.connectionGroup != 0) {
+ if (lastCachedGroupUid == app.uid
+ && lastCachedGroup == app.connectionGroup) {
+ // This is in the same group as the last process, just tweak
+ // adjustment by importance.
+ if (app.connectionImportance > lastCachedGroupImportance) {
+ lastCachedGroupImportance = app.connectionImportance;
+ if (curCachedAdj < nextCachedAdj
+ && curCachedAdj < ProcessList.CACHED_APP_MAX_ADJ) {
+ curCachedImpAdj++;
+ }
+ }
+ inGroup = true;
+ } else {
+ lastCachedGroupUid = app.uid;
+ lastCachedGroup = app.connectionGroup;
+ lastCachedGroupImportance = app.connectionImportance;
+ }
+ }
+ if (!inGroup && curCachedAdj != nextCachedAdj) {
+ stepCached++;
+ curCachedImpAdj = 0;
+ if (stepCached >= cachedFactor) {
+ stepCached = 0;
+ curCachedAdj = nextCachedAdj;
+ nextCachedAdj += ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
+ if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+ nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
+ }
+ }
+ }
+ // This process is a cached process holding activities...
+ // assign it the next cached value for that type, and then
+ // step that cached level.
+ app.setCurRawAdj(curCachedAdj + curCachedImpAdj);
+ app.curAdj = app.modifyRawOomAdj(curCachedAdj + curCachedImpAdj);
+ if (DEBUG_LRU) {
+ Slog.d(TAG_LRU, "Assigning activity LRU #" + i
+ + " adj: " + app.curAdj + " (curCachedAdj=" + curCachedAdj
+ + " curCachedImpAdj=" + curCachedImpAdj + ")");
+ }
+ break;
+ default:
+ // Figure out the next cached level.
+ if (curEmptyAdj != nextEmptyAdj) {
+ stepEmpty++;
+ if (stepEmpty >= emptyFactor) {
+ stepEmpty = 0;
+ curEmptyAdj = nextEmptyAdj;
+ nextEmptyAdj += ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
+ if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+ nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;
+ }
+ }
+ }
+ // For everything else, assign next empty cached process
+ // level and bump that up. Note that this means that
+ // long-running services that have dropped down to the
+ // cached level will be treated as empty (since their process
+ // state is still as a service), which is what we want.
+ app.setCurRawAdj(curEmptyAdj);
+ app.curAdj = app.modifyRawOomAdj(curEmptyAdj);
+ if (DEBUG_LRU) {
+ Slog.d(TAG_LRU, "Assigning empty LRU #" + i
+ + " adj: " + app.curAdj + " (curEmptyAdj=" + curEmptyAdj
+ + ")");
+ }
+ break;
+ }
+ }
+ }
+ }
- // First update the OOM adjustment for each of the
- // application processes based on their current state.
- int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ;
- int nextCachedAdj = curCachedAdj + (ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2);
- int curCachedImpAdj = 0;
- int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
- int nextEmptyAdj = curEmptyAdj + (ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2);
+ private boolean updateAndTrimProcessLocked(final long now, final long nowElapsed,
+ final long oldTime, final ActiveUids activeUids) {
+ ArrayList<ProcessRecord> lruList = mProcessList.mLruProcesses;
+ final int numLru = lruList.size();
+
+ final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES;
+ final int cachedProcessLimit = mConstants.CUR_MAX_CACHED_PROCESSES
+ - emptyProcessLimit;
+ int lastCachedGroup = 0;
+ int lastCachedGroupUid = 0;
+ int numCached = 0;
+ int numCachedExtraGroup = 0;
+ int numEmpty = 0;
+ int numTrimming = 0;
ProcessRecord selectedAppRecord = null;
long serviceLastActivity = 0;
int numBServices = 0;
- boolean retryCycles = false;
-
- // need to reset cycle state before calling computeOomAdjLocked because of service conns
- for (int i = N - 1; i >= 0; i--) {
- ProcessRecord app = mProcessList.mLruProcesses.get(i);
- app.containsCycle = false;
- app.setCurRawProcState(PROCESS_STATE_CACHED_EMPTY);
- app.setCurRawAdj(ProcessList.UNKNOWN_ADJ);
- }
- for (int i = N - 1; i >= 0; i--) {
- ProcessRecord app = mProcessList.mLruProcesses.get(i);
+ for (int i = numLru - 1; i >= 0; i--) {
+ ProcessRecord app = lruList.get(i);
if (mEnableBServicePropagation && app.serviceb
&& (app.curAdj == ProcessList.SERVICE_B_ADJ)) {
numBServices++;
@@ -387,126 +752,11 @@ public final class OomAdjuster {
if (DEBUG_OOM_ADJ && selectedAppRecord != null) Slog.d(TAG,
"Identified app.processName = " + selectedAppRecord.processName
+ " app.pid = " + selectedAppRecord.pid);
-
if (!app.killedByAm && app.thread != null) {
- app.procStateChanged = false;
- computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now, false);
-
- // if any app encountered a cycle, we need to perform an additional loop later
- retryCycles |= app.containsCycle;
-
- // If we haven't yet assigned the final cached adj
- // to the process, do that now.
- if (app.curAdj >= ProcessList.UNKNOWN_ADJ) {
- switch (app.getCurProcState()) {
- case PROCESS_STATE_CACHED_ACTIVITY:
- case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
- case ActivityManager.PROCESS_STATE_CACHED_RECENT:
- // Figure out the next cached level, taking into account groups.
- boolean inGroup = false;
- if (app.connectionGroup != 0) {
- if (lastCachedGroupUid == app.uid
- && lastCachedGroup == app.connectionGroup) {
- // This is in the same group as the last process, just tweak
- // adjustment by importance.
- if (app.connectionImportance > lastCachedGroupImportance) {
- lastCachedGroupImportance = app.connectionImportance;
- if (curCachedAdj < nextCachedAdj
- && curCachedAdj < ProcessList.CACHED_APP_MAX_ADJ) {
- curCachedImpAdj++;
- }
- }
- inGroup = true;
- } else {
- lastCachedGroupUid = app.uid;
- lastCachedGroup = app.connectionGroup;
- lastCachedGroupImportance = app.connectionImportance;
- }
- }
- if (!inGroup && curCachedAdj != nextCachedAdj) {
- stepCached++;
- curCachedImpAdj = 0;
- if (stepCached >= cachedFactor) {
- stepCached = 0;
- curCachedAdj = nextCachedAdj;
- nextCachedAdj += ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
- if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
- nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
- }
- }
- }
- // This process is a cached process holding activities...
- // assign it the next cached value for that type, and then
- // step that cached level.
- app.setCurRawAdj(curCachedAdj + curCachedImpAdj);
- app.curAdj = app.modifyRawOomAdj(curCachedAdj + curCachedImpAdj);
- if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning activity LRU #" + i
- + " adj: " + app.curAdj + " (curCachedAdj=" + curCachedAdj
- + " curCachedImpAdj=" + curCachedImpAdj + ")");
- break;
- default:
- // Figure out the next cached level.
- if (curEmptyAdj != nextEmptyAdj) {
- stepEmpty++;
- if (stepEmpty >= emptyFactor) {
- stepEmpty = 0;
- curEmptyAdj = nextEmptyAdj;
- nextEmptyAdj += ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
- if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {
- nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;
- }
- }
- }
- // For everything else, assign next empty cached process
- // level and bump that up. Note that this means that
- // long-running services that have dropped down to the
- // cached level will be treated as empty (since their process
- // state is still as a service), which is what we want.
- app.setCurRawAdj(curEmptyAdj);
- app.curAdj = app.modifyRawOomAdj(curEmptyAdj);
- if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning empty LRU #" + i
- + " adj: " + app.curAdj + " (curEmptyAdj=" + curEmptyAdj
- + ")");
- break;
- }
- }
- }
- }
-
- // Cycle strategy:
- // - Retry computing any process that has encountered a cycle.
- // - Continue retrying until no process was promoted.
- // - Iterate from least important to most important.
- int cycleCount = 0;
- while (retryCycles && cycleCount < 10) {
- cycleCount++;
- retryCycles = false;
-
- for (int i = 0; i < N; i++) {
- ProcessRecord app = mProcessList.mLruProcesses.get(i);
- if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
- app.adjSeq--;
- app.completedAdjSeq--;
+ // We don't need to apply the update for the process which didn't get computed
+ if (app.completedAdjSeq == mAdjSeq) {
+ applyOomAdjLocked(app, true, now, nowElapsed);
}
- }
-
- for (int i = 0; i < N; i++) {
- ProcessRecord app = mProcessList.mLruProcesses.get(i);
- if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
- if (computeOomAdjLocked(app, app.getCurRawAdj(), TOP_APP, true, now,
- true)) {
- retryCycles = true;
- }
- }
- }
- }
-
- lastCachedGroup = lastCachedGroupUid = 0;
-
- for (int i = N - 1; i >= 0; i--) {
- ProcessRecord app = mProcessList.mLruProcesses.get(i);
- if (!app.killedByAm && app.thread != null) {
- applyOomAdjLocked(app, true, now, nowElapsed);
// Count the number of process types.
switch (app.getCurProcState()) {
@@ -561,16 +811,7 @@ public final class OomAdjuster {
app.kill("isolated not needed", true);
} else {
// Keeping this process, update its uid.
- final UidRecord uidRec = app.uidRecord;
- if (uidRec != null) {
- uidRec.ephemeral = app.info.isInstantApp();
- if (uidRec.getCurProcState() > app.getCurProcState()) {
- uidRec.setCurProcState(app.getCurProcState());
- }
- if (app.hasForegroundServices()) {
- uidRec.foregroundServices = true;
- }
- }
+ updateAppUidRecLocked(app);
}
if (app.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
@@ -589,31 +830,34 @@ public final class OomAdjuster {
+ " app.pid = " + selectedAppRecord.pid + " is moved to higher adj");
}
- mService.incrementProcStateSeqAndNotifyAppsLocked();
-
- mNumServiceProcs = mNewNumServiceProcs;
-
- boolean allChanged = mService.updateLowMemStateLocked(numCached, numEmpty, numTrimming);
+ mProcessList.incrementProcStateSeqAndNotifyAppsLocked(activeUids);
- if (mService.mAlwaysFinishActivities) {
- // Need to do this on its own message because the stack may not
- // be in a consistent state at this point.
- mService.mAtmInternal.scheduleDestroyAllActivities("always-finish");
- }
+ return mService.updateLowMemStateLocked(numCached, numEmpty, numTrimming);
+ }
- if (allChanged) {
- mService.requestPssAllProcsLocked(now, false,
- mService.mProcessStats.isMemFactorLowered());
+ private void updateAppUidRecLocked(ProcessRecord app) {
+ final UidRecord uidRec = app.uidRecord;
+ if (uidRec != null) {
+ uidRec.ephemeral = app.info.isInstantApp();
+ if (uidRec.getCurProcState() > app.getCurProcState()) {
+ uidRec.setCurProcState(app.getCurProcState());
+ }
+ if (app.hasForegroundServices()) {
+ uidRec.foregroundServices = true;
+ }
}
+ }
- ArrayList<UidRecord> becameIdle = null;
+ private void updateUidsLocked(ActiveUids activeUids, final long nowElapsed) {
+ ArrayList<UidRecord> becameIdle = mTmpBecameIdle;
+ becameIdle.clear();
// Update from any uid changes.
if (mLocalPowerManager != null) {
mLocalPowerManager.startUidChanges();
}
- for (int i = mActiveUids.size() - 1; i >= 0; i--) {
- final UidRecord uidRec = mActiveUids.valueAt(i);
+ for (int i = activeUids.size() - 1; i >= 0; i--) {
+ final UidRecord uidRec = activeUids.valueAt(i);
int uidChange = UidRecord.CHANGE_PROCSTATE;
if (uidRec.getCurProcState() != PROCESS_STATE_NONEXISTENT
&& (uidRec.setProcState != uidRec.getCurProcState()
@@ -640,9 +884,6 @@ public final class OomAdjuster {
}
if (uidRec.idle && !uidRec.setIdle) {
uidChange = UidRecord.CHANGE_IDLE;
- if (becameIdle == null) {
- becameIdle = new ArrayList<>();
- }
becameIdle.add(uidRec);
}
} else {
@@ -675,40 +916,21 @@ public final class OomAdjuster {
mLocalPowerManager.finishUidChanges();
}
- if (becameIdle != null) {
+ int size = becameIdle.size();
+ if (size > 0) {
// If we have any new uids that became idle this time, we need to make sure
// they aren't left with running services.
- for (int i = becameIdle.size() - 1; i >= 0; i--) {
+ for (int i = size - 1; i >= 0; i--) {
mService.mServices.stopInBackgroundLocked(becameIdle.get(i).uid);
}
}
-
- if (mService.mProcessStats.shouldWriteNowLocked(now)) {
- mService.mHandler.post(new ActivityManagerService.ProcStatsRunnable(mService,
- mService.mProcessStats));
- }
-
- // Run this after making sure all procstates are updated.
- mService.mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, now);
-
- if (DEBUG_OOM_ADJ) {
- final long duration = SystemClock.uptimeMillis() - now;
- if (false) {
- Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms",
- new RuntimeException("here").fillInStackTrace());
- } else {
- Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms");
- }
- }
- mService.mOomAdjProfiler.oomAdjEnded();
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
private final ComputeOomAdjWindowCallback mTmpComputeOomAdjWindowCallback =
new ComputeOomAdjWindowCallback();
/** These methods are called inline during computeOomAdjLocked(), on the same thread */
- private final class ComputeOomAdjWindowCallback
+ final class ComputeOomAdjWindowCallback
implements WindowProcessController.ComputeOomAdjCallback {
ProcessRecord app;
@@ -829,7 +1051,8 @@ public final class OomAdjuster {
}
private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj,
- ProcessRecord TOP_APP, boolean doingAll, long now, boolean cycleReEval) {
+ ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval,
+ boolean computeClients) {
if (mAdjSeq == app.adjSeq) {
if (app.adjSeq == app.completedAdjSeq) {
// This adjustment has already been computed successfully.
@@ -859,7 +1082,6 @@ public final class OomAdjuster {
app.empty = false;
app.cached = false;
- final WindowProcessController wpc = app.getWindowProcessController();
final int appUid = app.info.uid;
final int logUid = mService.mCurOomAdjUid;
@@ -883,7 +1105,7 @@ public final class OomAdjuster {
// facilitate this, here we need to determine whether or not it
// is currently showing UI.
app.systemNoUi = true;
- if (app == TOP_APP) {
+ if (app == topApp) {
app.systemNoUi = false;
app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
app.adjType = "pers-top-activity";
@@ -891,7 +1113,7 @@ public final class OomAdjuster {
// sched group/proc state adjustment is below
app.systemNoUi = false;
app.adjType = "pers-top-ui";
- } else if (wpc.hasVisibleActivities()) {
+ } else if (app.getCachedHasVisibleActivities()) {
app.systemNoUi = false;
}
if (!app.systemNoUi) {
@@ -924,8 +1146,7 @@ public final class OomAdjuster {
int cachedAdjSeq;
boolean foregroundActivities = false;
- mTmpBroadcastQueue.clear();
- if (PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP && app == TOP_APP) {
+ if (PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP && app == topApp) {
// The last app on the list is the foreground app.
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
@@ -952,7 +1173,7 @@ public final class OomAdjuster {
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making instrumentation: " + app);
}
- } else if (mService.isReceivingBroadcastLocked(app, mTmpBroadcastQueue)) {
+ } else if (app.getCachedIsReceivingBroadcast(mTmpBroadcastQueue)) {
// An app that is currently receiving a broadcast also
// counts as being in the foreground for OOM killer purposes.
// It's placed in a sched group based on the nature of the
@@ -977,7 +1198,7 @@ public final class OomAdjuster {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making exec-service: " + app);
}
//Slog.i(TAG, "EXEC " + (app.execServicesFg ? "FG" : "BG") + ": " + app);
- } else if (app == TOP_APP) {
+ } else if (app == topApp) {
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
app.adjType = "top-sleeping";
@@ -1002,24 +1223,19 @@ public final class OomAdjuster {
}
// Examine all activities if not already foreground.
- if (!foregroundActivities && wpc.hasActivities()) {
- mTmpComputeOomAdjWindowCallback.initialize(app, adj, foregroundActivities, procState,
- schedGroup, appUid, logUid, PROCESS_STATE_CUR_TOP);
- final int minLayer = wpc.computeOomAdjFromActivities(
- ProcessList.VISIBLE_APP_LAYER_MAX, mTmpComputeOomAdjWindowCallback);
+ if (!foregroundActivities && app.getCachedHasActivities()) {
+ app.computeOomAdjFromActivitiesIfNecessary(mTmpComputeOomAdjWindowCallback,
+ adj, foregroundActivities, procState, schedGroup, appUid, logUid,
+ PROCESS_STATE_CUR_TOP);
- adj = mTmpComputeOomAdjWindowCallback.adj;
- foregroundActivities = mTmpComputeOomAdjWindowCallback.foregroundActivities;
- procState = mTmpComputeOomAdjWindowCallback.procState;
- schedGroup = mTmpComputeOomAdjWindowCallback.schedGroup;
-
- if (adj == ProcessList.VISIBLE_APP_ADJ) {
- adj += minLayer;
- }
+ adj = app.mCachedAdj;
+ foregroundActivities = app.mCachedForegroundActivities;
+ procState = app.mCachedProcState;
+ schedGroup = app.mCachedSchedGroup;
}
- if (procState > ActivityManager.PROCESS_STATE_CACHED_RECENT && app.hasRecentTasks()) {
- procState = ActivityManager.PROCESS_STATE_CACHED_RECENT;
+ if (procState > PROCESS_STATE_CACHED_RECENT && app.getCachedHasRecentTasks()) {
+ procState = PROCESS_STATE_CACHED_RECENT;
app.adjType = "cch-rec";
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to cached recent: " + app);
@@ -1089,7 +1305,7 @@ public final class OomAdjuster {
}
}
- if (mService.mAtmInternal.isHeavyWeightProcess(app.getWindowProcessController())) {
+ if (app.getCachedIsHeavyWeight()) {
if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {
// We don't want to kill the current heavy-weight process.
adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;
@@ -1109,7 +1325,7 @@ public final class OomAdjuster {
}
}
- if (wpc.isHomeProcess()) {
+ if (app.getCachedIsHomeProcess()) {
if (adj > ProcessList.HOME_APP_ADJ) {
// This process is hosting what we currently consider to be the
// home app, so we don't want to let it go into the background.
@@ -1130,7 +1346,7 @@ public final class OomAdjuster {
}
}
- if (wpc.isPreviousProcess() && app.hasActivities()) {
+ if (app.getCachedIsPreviousProcess() && app.getCachedHasActivities()) {
if (adj > ProcessList.PREVIOUS_APP_ADJ) {
// This was the previous process that showed UI to the user.
// We want to try to keep it around more aggressively, to give
@@ -1211,7 +1427,7 @@ public final class OomAdjuster {
"Raise procstate to started service: " + app);
}
}
- if (app.hasShownUi && !wpc.isHomeProcess()) {
+ if (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
@@ -1266,7 +1482,13 @@ public final class OomAdjuster {
boolean trackedProcState = false;
if ((cr.flags& Context.BIND_WAIVE_PRIORITY) == 0) {
ProcessRecord client = cr.binding.client;
- computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now, cycleReEval);
+ if (computeClients) {
+ computeOomAdjLocked(client, cachedAdj, topApp, doingAll, now,
+ cycleReEval, true);
+ } else {
+ client.setCurRawAdj(client.setAdj);
+ client.setCurRawProcState(client.setProcState);
+ }
if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
continue;
@@ -1285,7 +1507,7 @@ public final class OomAdjuster {
if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
// Not doing bind OOM management, so treat
// this guy more like a started service.
- if (app.hasShownUi && !wpc.isHomeProcess()) {
+ if (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
@@ -1319,7 +1541,7 @@ public final class OomAdjuster {
// about letting this process get into the LRU
// list to be killed and restarted if needed for
// memory.
- if (app.hasShownUi && !wpc.isHomeProcess()
+ if (app.hasShownUi && !app.getCachedIsHomeProcess()
&& clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
if (adj >= ProcessList.CACHED_APP_MIN_ADJ) {
adjType = "cch-bound-ui-services";
@@ -1509,7 +1731,13 @@ public final class OomAdjuster {
// Being our own client is not interesting.
continue;
}
- computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now, cycleReEval);
+ if (computeClients) {
+ computeOomAdjLocked(client, cachedAdj, topApp, doingAll, now, cycleReEval,
+ true);
+ } else {
+ client.setCurRawAdj(client.setAdj);
+ client.setCurRawProcState(client.setProcState);
+ }
if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
continue;
@@ -1525,7 +1753,7 @@ public final class OomAdjuster {
}
String adjType = null;
if (adj > clientAdj) {
- if (app.hasShownUi && !wpc.isHomeProcess()
+ if (app.hasShownUi && !app.getCachedIsHomeProcess()
&& clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
adjType = "cch-ui-provider";
} else {
@@ -1623,7 +1851,7 @@ public final class OomAdjuster {
if (procState >= PROCESS_STATE_CACHED_EMPTY) {
if (app.hasClientActivities()) {
// This is a cached process, but with client activities. Mark it so.
- procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
+ procState = PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
app.adjType = "cch-client-act";
} else if (app.treatLikeActivity) {
// This is a cached process, but somebody wants us to treat it like it has
@@ -1817,7 +2045,7 @@ public final class OomAdjuster {
int processGroup;
switch (curSchedGroup) {
case ProcessList.SCHED_GROUP_BACKGROUND:
- processGroup = THREAD_GROUP_BG_NONINTERACTIVE;
+ processGroup = THREAD_GROUP_BACKGROUND;
break;
case ProcessList.SCHED_GROUP_TOP_APP:
case ProcessList.SCHED_GROUP_TOP_APP_BOUND:
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index ff15f9689093..c8f447f693dc 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -20,6 +20,8 @@ import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityThread.PROC_START_SEQ_IDENT;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO;
+import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode;
+import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static android.os.Process.getFreeMemory;
@@ -28,6 +30,7 @@ import static android.os.Process.killProcessQuiet;
import static android.os.Process.startWebView;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
@@ -41,6 +44,7 @@ import static com.android.server.am.ActivityManagerService.PROC_START_TIMEOUT_MS
import static com.android.server.am.ActivityManagerService.PROC_START_TIMEOUT_WITH_WRAPPER;
import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
import static com.android.server.am.ActivityManagerService.TAG_LRU;
+import static com.android.server.am.ActivityManagerService.TAG_NETWORK;
import static com.android.server.am.ActivityManagerService.TAG_PROCESSES;
import static com.android.server.am.ActivityManagerService.TAG_PSS;
import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
@@ -216,7 +220,7 @@ public final class ProcessList {
// Memory pages are 4K.
static final int PAGE_SIZE = 4 * 1024;
- // Activity manager's version of Process.THREAD_GROUP_BG_NONINTERACTIVE
+ // Activity manager's version of Process.THREAD_GROUP_BACKGROUND
static final int SCHED_GROUP_BACKGROUND = 0;
// Activity manager's version of Process.THREAD_GROUP_RESTRICTED
static final int SCHED_GROUP_RESTRICTED = 1;
@@ -241,6 +245,24 @@ public final class ProcessList {
// Threshold of number of cached+empty where we consider memory critical.
static final int TRIM_LOW_THRESHOLD = 5;
+ /**
+ * State indicating that there is no need for any blocking for network.
+ */
+ @VisibleForTesting
+ static final int NETWORK_STATE_NO_CHANGE = 0;
+
+ /**
+ * State indicating that the main thread needs to be informed about the network wait.
+ */
+ @VisibleForTesting
+ static final int NETWORK_STATE_BLOCK = 1;
+
+ /**
+ * State indicating that any threads waiting for network state to get updated can be unblocked.
+ */
+ @VisibleForTesting
+ static final int NETWORK_STATE_UNBLOCK = 2;
+
// If true, then we pass the flag to ART to load the app image startup cache.
private static final String PROPERTY_USE_APP_IMAGE_STARTUP_CACHE =
"persist.device_config.runtime_native.use_app_image_startup_cache";
@@ -3201,4 +3223,113 @@ public final class ProcessList {
mService.doStopUidLocked(uidRec.uid, uidRec);
}
}
+
+ /**
+ * Checks if the uid is coming from background to foreground or vice versa and returns
+ * appropriate block state based on this.
+ *
+ * @return blockState based on whether the uid is coming from background to foreground or
+ * vice versa. If bg->fg or fg->bg, then {@link #NETWORK_STATE_BLOCK} or
+ * {@link #NETWORK_STATE_UNBLOCK} respectively, otherwise
+ * {@link #NETWORK_STATE_NO_CHANGE}.
+ */
+ @VisibleForTesting
+ int getBlockStateForUid(UidRecord uidRec) {
+ // Denotes whether uid's process state is currently allowed network access.
+ final boolean isAllowed =
+ isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getCurProcState())
+ || isProcStateAllowedWhileOnRestrictBackground(uidRec.getCurProcState());
+ // Denotes whether uid's process state was previously allowed network access.
+ final boolean wasAllowed = isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.setProcState)
+ || isProcStateAllowedWhileOnRestrictBackground(uidRec.setProcState);
+
+ // When the uid is coming to foreground, AMS should inform the app thread that it should
+ // block for the network rules to get updated before launching an activity.
+ if (!wasAllowed && isAllowed) {
+ return NETWORK_STATE_BLOCK;
+ }
+ // When the uid is going to background, AMS should inform the app thread that if an
+ // activity launch is blocked for the network rules to get updated, it should be unblocked.
+ if (wasAllowed && !isAllowed) {
+ return NETWORK_STATE_UNBLOCK;
+ }
+ return NETWORK_STATE_NO_CHANGE;
+ }
+
+ /**
+ * Checks if any uid is coming from background to foreground or vice versa and if so, increments
+ * the {@link UidRecord#curProcStateSeq} corresponding to that uid using global seq counter
+ * {@link ProcessList#mProcStateSeqCounter} and notifies the app if it needs to block.
+ */
+ @VisibleForTesting
+ @GuardedBy("mService")
+ void incrementProcStateSeqAndNotifyAppsLocked(ActiveUids activeUids) {
+ if (mService.mWaitForNetworkTimeoutMs <= 0) {
+ return;
+ }
+ // Used for identifying which uids need to block for network.
+ ArrayList<Integer> blockingUids = null;
+ for (int i = activeUids.size() - 1; i >= 0; --i) {
+ final UidRecord uidRec = activeUids.valueAt(i);
+ // If the network is not restricted for uid, then nothing to do here.
+ if (!mService.mInjector.isNetworkRestrictedForUid(uidRec.uid)) {
+ continue;
+ }
+ if (!UserHandle.isApp(uidRec.uid) || !uidRec.hasInternetPermission) {
+ continue;
+ }
+ // If process state is not changed, then there's nothing to do.
+ if (uidRec.setProcState == uidRec.getCurProcState()) {
+ continue;
+ }
+ final int blockState = getBlockStateForUid(uidRec);
+ // No need to inform the app when the blockState is NETWORK_STATE_NO_CHANGE as
+ // there's nothing the app needs to do in this scenario.
+ if (blockState == NETWORK_STATE_NO_CHANGE) {
+ continue;
+ }
+ synchronized (uidRec.networkStateLock) {
+ uidRec.curProcStateSeq = ++mProcStateSeqCounter; // TODO: use method
+ if (blockState == NETWORK_STATE_BLOCK) {
+ if (blockingUids == null) {
+ blockingUids = new ArrayList<>();
+ }
+ blockingUids.add(uidRec.uid);
+ } else {
+ if (DEBUG_NETWORK) {
+ Slog.d(TAG_NETWORK, "uid going to background, notifying all blocking"
+ + " threads for uid: " + uidRec);
+ }
+ if (uidRec.waitingForNetwork) {
+ uidRec.networkStateLock.notifyAll();
+ }
+ }
+ }
+ }
+
+ // There are no uids that need to block, so nothing more to do.
+ if (blockingUids == null) {
+ return;
+ }
+
+ for (int i = mLruProcesses.size() - 1; i >= 0; --i) {
+ final ProcessRecord app = mLruProcesses.get(i);
+ if (!blockingUids.contains(app.uid)) {
+ continue;
+ }
+ if (!app.killedByAm && app.thread != null) {
+ final UidRecord uidRec = getUidRecordLocked(app.uid);
+ try {
+ if (DEBUG_NETWORK) {
+ Slog.d(TAG_NETWORK, "Informing app thread that it needs to block: "
+ + uidRec);
+ }
+ if (uidRec != null) {
+ app.thread.setNetworkBlockSeq(uidRec.curProcStateSeq);
+ }
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index c4a3ff83f0f5..b181a7f00195 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -310,6 +310,22 @@ class ProcessRecord implements WindowProcessListener {
// This will be same as {@link #uid} usually except for some apps used during factory testing.
int startUid;
+ // Cached task info for OomAdjuster
+ private static final int VALUE_INVALID = -1;
+ private static final int VALUE_FALSE = 0;
+ private static final int VALUE_TRUE = 1;
+ private int mCachedHasActivities = VALUE_INVALID;
+ private int mCachedIsHeavyWeight = VALUE_INVALID;
+ private int mCachedHasVisibleActivities = VALUE_INVALID;
+ private int mCachedIsHomeProcess = VALUE_INVALID;
+ private int mCachedIsPreviousProcess = VALUE_INVALID;
+ private int mCachedHasRecentTasks = VALUE_INVALID;
+ private int mCachedIsReceivingBroadcast = VALUE_INVALID;
+ int mCachedAdj = ProcessList.INVALID_ADJ;
+ boolean mCachedForegroundActivities = false;
+ int mCachedProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+ int mCachedSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+
void setStartParams(int startUid, HostingRecord hostingRecord, String seInfo,
long startTime) {
this.startUid = startUid;
@@ -1335,7 +1351,7 @@ class ProcessRecord implements WindowProcessListener {
}
mService.mProcessList.updateLruProcessLocked(this, activityChange, null /* client */);
if (updateOomAdj) {
- mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_ACTIVITY);
+ mService.updateOomAdjLocked(this, OomAdjuster.OOM_ADJ_REASON_ACTIVITY);
}
}
}
@@ -1681,4 +1697,100 @@ class ProcessRecord implements WindowProcessListener {
return Settings.Secure.getInt(mService.mContext.getContentResolver(),
Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
}
+
+ void resetCachedInfo() {
+ mCachedHasActivities = VALUE_INVALID;
+ mCachedIsHeavyWeight = VALUE_INVALID;
+ mCachedHasVisibleActivities = VALUE_INVALID;
+ mCachedIsHomeProcess = VALUE_INVALID;
+ mCachedIsPreviousProcess = VALUE_INVALID;
+ mCachedHasRecentTasks = VALUE_INVALID;
+ mCachedIsReceivingBroadcast = VALUE_INVALID;
+ mCachedAdj = ProcessList.INVALID_ADJ;
+ mCachedForegroundActivities = false;
+ mCachedProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+ mCachedSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+ }
+
+ boolean getCachedHasActivities() {
+ if (mCachedHasActivities == VALUE_INVALID) {
+ mCachedHasActivities = getWindowProcessController().hasActivities() ? VALUE_TRUE
+ : VALUE_FALSE;
+ }
+ return mCachedHasActivities == VALUE_TRUE;
+ }
+
+ boolean getCachedIsHeavyWeight() {
+ if (mCachedIsHeavyWeight == VALUE_INVALID) {
+ mCachedIsHeavyWeight = mService.mAtmInternal.isHeavyWeightProcess(
+ getWindowProcessController()) ? VALUE_TRUE : VALUE_FALSE;
+ }
+ return mCachedIsHeavyWeight == VALUE_TRUE;
+ }
+
+ boolean getCachedHasVisibleActivities() {
+ if (mCachedHasVisibleActivities == VALUE_INVALID) {
+ mCachedHasVisibleActivities = getWindowProcessController().hasVisibleActivities()
+ ? VALUE_TRUE : VALUE_FALSE;
+ }
+ return mCachedHasVisibleActivities == VALUE_TRUE;
+ }
+
+ boolean getCachedIsHomeProcess() {
+ if (mCachedIsHomeProcess == VALUE_INVALID) {
+ mCachedIsHomeProcess = getWindowProcessController().isHomeProcess()
+ ? VALUE_TRUE : VALUE_FALSE;
+ }
+ return mCachedIsHomeProcess == VALUE_TRUE;
+ }
+
+ boolean getCachedIsPreviousProcess() {
+ if (mCachedIsPreviousProcess == VALUE_INVALID) {
+ mCachedIsPreviousProcess = getWindowProcessController().isPreviousProcess()
+ ? VALUE_TRUE : VALUE_FALSE;
+ }
+ return mCachedIsPreviousProcess == VALUE_TRUE;
+ }
+
+ boolean getCachedHasRecentTasks() {
+ if (mCachedHasRecentTasks == VALUE_INVALID) {
+ mCachedHasRecentTasks = getWindowProcessController().hasRecentTasks()
+ ? VALUE_TRUE : VALUE_FALSE;
+ }
+ return mCachedHasRecentTasks == VALUE_TRUE;
+ }
+
+ boolean getCachedIsReceivingBroadcast(ArraySet<BroadcastQueue> tmpQueue) {
+ if (mCachedIsReceivingBroadcast == VALUE_INVALID) {
+ tmpQueue.clear();
+ mCachedIsReceivingBroadcast = mService.isReceivingBroadcastLocked(this, tmpQueue)
+ ? VALUE_TRUE : VALUE_FALSE;
+ if (mCachedIsReceivingBroadcast == VALUE_TRUE) {
+ mCachedSchedGroup = tmpQueue.contains(mService.mFgBroadcastQueue)
+ ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
+ }
+ }
+ return mCachedIsReceivingBroadcast == VALUE_TRUE;
+ }
+
+ void computeOomAdjFromActivitiesIfNecessary(OomAdjuster.ComputeOomAdjWindowCallback callback,
+ int adj, boolean foregroundActivities, int procState, int schedGroup, int appUid,
+ int logUid, int processCurTop) {
+ if (mCachedAdj != ProcessList.INVALID_ADJ) {
+ return;
+ }
+ callback.initialize(this, adj, foregroundActivities, procState, schedGroup, appUid, logUid,
+ processCurTop);
+ final int minLayer = getWindowProcessController().computeOomAdjFromActivities(
+ ProcessList.VISIBLE_APP_LAYER_MAX, callback);
+
+ mCachedAdj = callback.adj;
+ mCachedForegroundActivities = callback.foregroundActivities;
+ mCachedProcState = callback.procState;
+ mCachedSchedGroup = callback.schedGroup;
+
+ if (mCachedAdj == ProcessList.VISIBLE_APP_ADJ) {
+ mCachedAdj += minLayer;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index 6ffd8a9180ba..5c840ada86d4 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -1101,7 +1101,7 @@ public final class ProcessStatsService extends IProcessStats.Stub {
}
boolean sepNeeded = false;
- if (dumpAll || isCheckin) {
+ if ((dumpAll || isCheckin) && !currentOnly) {
mWriteLock.lock();
try {
ArrayList<String> files = getCommittedFiles(0, false, !isCheckin);
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 64f4a35032c0..4a6e63f2d2ab 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -86,6 +86,7 @@ public class SettingsToPropertiesMapper {
DeviceConfig.NAMESPACE_NETD_NATIVE,
DeviceConfig.NAMESPACE_RUNTIME_NATIVE,
DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
+ DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
};
private final String[] mGlobalSettings;
@@ -276,4 +277,4 @@ public class SettingsToPropertiesMapper {
String settingValue = Settings.Global.getString(mContentResolver, settingName);
setProperty(propName, settingValue);
}
-} \ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING
index 21d4925722d0..bc4707f04724 100644
--- a/services/core/java/com/android/server/am/TEST_MAPPING
+++ b/services/core/java/com/android/server/am/TEST_MAPPING
@@ -33,7 +33,6 @@
},
{
"name": "FrameworksMockingServicesTests",
- "file_patterns": ["AppCompactor\\.java"],
"options": [
{
"include-filter": "com.android.server.am."
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 5c8e530faf70..175419117898 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -133,12 +133,12 @@ class UserController implements Handler.Callback {
static final int CONTINUE_USER_SWITCH_MSG = 20;
static final int USER_SWITCH_TIMEOUT_MSG = 30;
static final int START_PROFILES_MSG = 40;
- static final int SYSTEM_USER_START_MSG = 50;
- static final int SYSTEM_USER_CURRENT_MSG = 60;
+ static final int USER_START_MSG = 50;
+ static final int USER_CURRENT_MSG = 60;
static final int FOREGROUND_PROFILE_CHANGED_MSG = 70;
static final int REPORT_USER_SWITCH_COMPLETE_MSG = 80;
static final int USER_SWITCH_CALLBACKS_TIMEOUT_MSG = 90;
- static final int SYSTEM_USER_UNLOCK_MSG = 100;
+ static final int USER_UNLOCK_MSG = 100;
static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 110;
static final int START_USER_SWITCH_FG_MSG = 120;
@@ -368,16 +368,18 @@ class UserController implements Handler.Callback {
}
}
- mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG,
- userId, 0));
- Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
- | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- mInjector.broadcastIntent(intent, null, resultTo, 0, null, null,
- new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
- AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID,
- Binder.getCallingUid(), Binder.getCallingPid(), userId);
+ if (!mInjector.getUserManager().isPreCreated(userId)) {
+ mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG,
+ userId, 0));
+ Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ mInjector.broadcastIntent(intent, null, resultTo, 0, null, null,
+ new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
+ AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID,
+ Binder.getCallingUid(), Binder.getCallingPid(), userId);
+ }
}
// We need to delay unlocking managed profiles until the parent user
@@ -438,8 +440,7 @@ class UserController implements Handler.Callback {
// Dispatch unlocked to system services; when fully dispatched,
// that calls through to the next "unlocked" phase
- mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss)
- .sendToTarget();
+ mHandler.obtainMessage(USER_UNLOCK_MSG, userId, 0, uss).sendToTarget();
});
return true;
}
@@ -555,6 +556,17 @@ class UserController implements Handler.Callback {
}
}
+ if (userInfo.preCreated) {
+ Slog.i(TAG, "Stopping pre-created user " + userInfo.toFullString());
+ // Pre-created user was started right after creation so services could properly
+ // intialize it; it should be stopped right away as it's not really a "real" user.
+ // TODO(b/140750212): in the long-term, we should add a onCreateUser() callback
+ // on SystemService instead.
+ stopUser(userInfo.id, /* force= */ true, /* stopUserCallback= */ null,
+ /* keyEvictedCallback= */ null);
+ return;
+ }
+
// Spin up app widgets prior to boot-complete, so they can be ready promptly
mInjector.startUserWidgets(userId);
@@ -799,7 +811,8 @@ class UserController implements Handler.Callback {
mInjector.systemServiceManagerCleanupUser(userId);
mInjector.stackSupervisorRemoveUser(userId);
// Remove the user if it is ephemeral.
- if (getUserInfo(userId).isEphemeral()) {
+ UserInfo userInfo = getUserInfo(userId);
+ if (userInfo.isEphemeral() && !userInfo.preCreated) {
mInjector.getUserManager().removeUserEvenWhenDisallowed(userId);
}
@@ -1069,6 +1082,11 @@ class UserController implements Handler.Callback {
return false;
}
+ if (foreground && userInfo.preCreated) {
+ Slog.w(TAG, "Cannot start pre-created user #" + userId + " as foreground");
+ return false;
+ }
+
if (foreground && mUserSwitchUiEnabled) {
t.traceBegin("startFreezingScreen");
mInjector.getWindowManager().startFreezingScreen(
@@ -1166,7 +1184,7 @@ class UserController implements Handler.Callback {
updateStartedUserArrayLU();
}
needStart = true;
- t.traceBegin("updateStateStopping");
+ t.traceEnd();
}
if (uss.state == UserState.STATE_BOOTING) {
@@ -1178,15 +1196,13 @@ class UserController implements Handler.Callback {
// Booting up a new user, need to tell system services about it.
// Note that this is on the same handler as scheduling of broadcasts,
// which is important because it needs to go first.
- mHandler.sendMessage(
- mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId, 0));
+ mHandler.sendMessage(mHandler.obtainMessage(USER_START_MSG, userId, 0));
t.traceEnd();
}
t.traceBegin("sendMessages");
if (foreground) {
- mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId,
- oldUserId));
+ mHandler.sendMessage(mHandler.obtainMessage(USER_CURRENT_MSG, userId, oldUserId));
mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
@@ -1195,6 +1211,10 @@ class UserController implements Handler.Callback {
oldUserId, userId, uss), USER_SWITCH_TIMEOUT_MS);
}
+ if (userInfo.preCreated) {
+ needStart = false;
+ }
+
if (needStart) {
// Send USER_STARTED broadcast
Intent intent = new Intent(Intent.ACTION_USER_STARTED);
@@ -2168,14 +2188,14 @@ class UserController implements Handler.Callback {
case START_PROFILES_MSG:
startProfiles();
break;
- case SYSTEM_USER_START_MSG:
+ case USER_START_MSG:
mInjector.batteryStatsServiceNoteEvent(
BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
Integer.toString(msg.arg1), msg.arg1);
mInjector.getSystemServiceManager().startUser(TimingsTraceAndSlog.newAsyncLog(),
msg.arg1);
break;
- case SYSTEM_USER_UNLOCK_MSG:
+ case USER_UNLOCK_MSG:
final int userId = msg.arg1;
mInjector.getSystemServiceManager().unlockUser(userId);
// Loads recents on a worker thread that allows disk I/O
@@ -2184,7 +2204,7 @@ class UserController implements Handler.Callback {
});
finishUserUnlocked((UserState) msg.obj);
break;
- case SYSTEM_USER_CURRENT_MSG:
+ case USER_CURRENT_MSG:
mInjector.batteryStatsServiceNoteEvent(
BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_FINISH,
Integer.toString(msg.arg2), msg.arg2);
diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java
index 98f5557903d6..bbf57728a20a 100644
--- a/services/core/java/com/android/server/am/UserSwitchingDialog.java
+++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java
@@ -78,7 +78,7 @@ class UserSwitchingDialog extends AlertDialog
WindowManager.LayoutParams attrs = getWindow().getAttributes();
attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR |
- WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
getWindow().setAttributes(attrs);
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 159e5b87e5a8..857506889033 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -47,6 +47,7 @@ import android.app.AppOpsManager;
import android.app.AppOpsManager.HistoricalOps;
import android.app.AppOpsManager.HistoricalOpsRequest;
import android.app.AppOpsManager.Mode;
+import android.app.AppOpsManager.OpFeatureEntry;
import android.app.AppOpsManager.OpEntry;
import android.app.AppOpsManager.OpFlags;
import android.app.AppOpsManagerInternal;
@@ -68,6 +69,7 @@ import android.hardware.camera2.CameraDevice.CAMERA_AUDIO_RESTRICTION;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -108,6 +110,7 @@ import com.android.internal.app.IAppOpsNotedCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
@@ -139,6 +142,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
public class AppOpsService extends IAppOpsService.Stub {
static final String TAG = "AppOps";
@@ -256,6 +260,9 @@ public class AppOpsService extends IAppOpsService.Stub {
@GuardedBy("this")
private CheckOpsDelegate mCheckOpsDelegate;
+ @GuardedBy("this")
+ private SparseArray<List<Integer>> mSwitchOpToOps;
+
/**
* All times are in milliseconds. These constants are kept synchronized with the system
* global Settings. Any access to this class or its fields should be done while
@@ -358,9 +365,11 @@ public class AppOpsService extends IAppOpsService.Stub {
public int pendingState = UID_STATE_CACHED;
public long pendingStateCommitTime;
+ // For all features combined
public int startNesting;
+
public ArrayMap<String, Ops> pkgOps;
- private SparseIntArray opModes;
+ public SparseIntArray opModes;
// true indicates there is an interested observer, false there isn't but it has such an op
public SparseBooleanArray foregroundOps;
@@ -382,64 +391,6 @@ public class AppOpsService extends IAppOpsService.Stub {
&& (pendingState == UID_STATE_CACHED));
}
- public int getOpModeCount() {
- return opModes != null ? opModes.size() : 0;
- }
-
- public int getOpCodeAt(int index) {
- return opModes.keyAt(index);
- }
-
- public boolean hasOpMode(int code) {
- return opModes != null && opModes.indexOfKey(code) >= 0;
- }
-
- public int getOpMode(int code) {
- return opModes.get(code);
- }
-
- public boolean putOpMode(int code, int mode) {
- if (mode == AppOpsManager.opToDefaultMode(code)) {
- return removeOpMode(code);
- }
- if (opModes == null) {
- opModes = new SparseIntArray();
- }
- int index = opModes.indexOfKey(code);
- if (index < 0) {
- opModes.put(code, mode);
- return true;
- }
- if (opModes.valueAt(index) == mode) {
- return false;
- }
- opModes.setValueAt(index, mode);
- return true;
- }
-
- public boolean removeOpMode(int code) {
- if (opModes == null) {
- return false;
- }
- int index = opModes.indexOfKey(code);
- if (index < 0) {
- return false;
- }
- opModes.removeAt(index);
- if (opModes.size() == 0) {
- opModes = null;
- }
- return true;
- }
-
- @Nullable
- public SparseIntArray cloneOpModes() {
- if (opModes == null) {
- return null;
- }
- return opModes.clone();
- }
-
int evalMode(int op, int mode) {
if (mode == AppOpsManager.MODE_FOREGROUND) {
return state <= AppOpsManager.resolveFirstUnrestrictedUidState(op)
@@ -467,13 +418,14 @@ public class AppOpsService extends IAppOpsService.Stub {
public void evalForegroundOps(SparseArray<ArraySet<ModeCallback>> watchers) {
SparseBooleanArray which = null;
hasForegroundWatchers = false;
- for (int i = getOpModeCount() - 1; i >= 0; i--) {
- int code = getOpCodeAt(i);
- if (getOpMode(code) == AppOpsManager.MODE_FOREGROUND) {
- if (which == null) {
- which = new SparseBooleanArray();
+ if (opModes != null) {
+ for (int i = opModes.size() - 1; i >= 0; i--) {
+ if (opModes.valueAt(i) == AppOpsManager.MODE_FOREGROUND) {
+ if (which == null) {
+ which = new SparseBooleanArray();
+ }
+ evalForegroundWatchers(opModes.keyAt(i), watchers, which);
}
- evalForegroundWatchers(code, watchers, which);
}
}
if (pkgOps != null) {
@@ -505,87 +457,71 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
- final static class Op {
- int op;
- boolean running;
- final UidState uidState;
- final @NonNull String packageName;
+ private static final class FeatureOp {
+ public final @NonNull Op parent;
+
+ public boolean running;
- private @Mode int mode;
private @Nullable LongSparseLongArray mAccessTimes;
private @Nullable LongSparseLongArray mRejectTimes;
private @Nullable LongSparseLongArray mDurations;
private @Nullable LongSparseLongArray mProxyUids;
+ private @Nullable LongSparseArray<String> mProxyFeatureIds;
private @Nullable LongSparseArray<String> mProxyPackageNames;
- int startNesting;
- long startRealtime;
-
- Op(UidState uidState, String packageName, int op) {
- this.op = op;
- this.uidState = uidState;
- this.packageName = packageName;
- this.mode = AppOpsManager.opToDefaultMode(op);
- }
-
- int getMode() {
- return mode;
- }
+ public int startNesting;
+ public long startRealtime;
- int evalMode() {
- return uidState.evalMode(op, mode);
+ FeatureOp(@NonNull Op parent) {
+ this.parent = parent;
}
- /** @hide */
public void accessed(long time, int proxyUid, @Nullable String proxyPackageName,
- @AppOpsManager.UidState int uidState, @OpFlags int flags) {
+ @Nullable String proxyFeatureId, @AppOpsManager.UidState int uidState,
+ @OpFlags int flags) {
final long key = AppOpsManager.makeKey(uidState, flags);
if (mAccessTimes == null) {
mAccessTimes = new LongSparseLongArray();
}
mAccessTimes.put(key, time);
- updateProxyState(key, proxyUid, proxyPackageName);
+ updateProxyState(key, proxyUid, proxyPackageName, proxyFeatureId);
if (mDurations != null) {
mDurations.delete(key);
}
}
- /** @hide */
public void rejected(long time, int proxyUid, @Nullable String proxyPackageName,
- @AppOpsManager.UidState int uidState, @OpFlags int flags) {
+ @Nullable String proxyFeatureId, @AppOpsManager.UidState int uidState,
+ @OpFlags int flags) {
final long key = AppOpsManager.makeKey(uidState, flags);
if (mRejectTimes == null) {
mRejectTimes = new LongSparseLongArray();
}
mRejectTimes.put(key, time);
- updateProxyState(key, proxyUid, proxyPackageName);
+ updateProxyState(key, proxyUid, proxyPackageName, proxyFeatureId);
if (mDurations != null) {
mDurations.delete(key);
}
}
- /** @hide */
public void started(long time, @AppOpsManager.UidState int uidState, @OpFlags int flags) {
updateAccessTimeAndDuration(time, -1 /*duration*/, uidState, flags);
running = true;
}
- /** @hide */
public void finished(long time, long duration, @AppOpsManager.UidState int uidState,
- @OpFlags int flags) {
+ @OpFlags int flags) {
updateAccessTimeAndDuration(time, duration, uidState, flags);
running = false;
}
- /** @hide */
public void running(long time, long duration, @AppOpsManager.UidState int uidState,
- @OpFlags int flags) {
+ @OpFlags int flags) {
updateAccessTimeAndDuration(time, duration, uidState, flags);
}
- /** @hide */
public void continuing(long duration, @AppOpsManager.UidState int uidState,
- @OpFlags int flags) {
+ @OpFlags int flags) {
final long key = AppOpsManager.makeKey(uidState, flags);
if (mDurations == null) {
mDurations = new LongSparseLongArray();
@@ -594,7 +530,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
private void updateAccessTimeAndDuration(long time, long duration,
- @AppOpsManager.UidState int uidState, @OpFlags int flags) {
+ @AppOpsManager.UidState int uidState, @OpFlags int flags) {
final long key = AppOpsManager.makeKey(uidState, flags);
if (mAccessTimes == null) {
mAccessTimes = new LongSparseLongArray();
@@ -607,7 +543,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
private void updateProxyState(long key, int proxyUid,
- @Nullable String proxyPackageName) {
+ @Nullable String proxyPackageName, @Nullable String featureId) {
if (proxyUid == Process.INVALID_UID) {
return;
}
@@ -616,15 +552,102 @@ public class AppOpsService extends IAppOpsService.Stub {
mProxyUids = new LongSparseLongArray();
}
mProxyUids.put(key, proxyUid);
+
if (mProxyPackageNames == null) {
mProxyPackageNames = new LongSparseArray<>();
}
mProxyPackageNames.put(key, proxyPackageName);
+
+ if (mProxyFeatureIds == null) {
+ mProxyFeatureIds = new LongSparseArray<>();
+ }
+ mProxyFeatureIds.put(key, featureId);
}
boolean hasAnyTime() {
return (mAccessTimes != null && mAccessTimes.size() > 0)
- || (mRejectTimes != null && mRejectTimes.size() > 0);
+ || (mRejectTimes != null && mRejectTimes.size() > 0);
+ }
+
+ @NonNull OpFeatureEntry.Builder createFeatureEntryBuilderLocked() {
+ return new OpFeatureEntry.Builder(running, mAccessTimes, mRejectTimes, mDurations,
+ mProxyUids, mProxyPackageNames, mProxyFeatureIds);
+ }
+ }
+
+ final static class Op {
+ int op;
+ final UidState uidState;
+ final @NonNull String packageName;
+
+ private @Mode int mode;
+
+ /** featureId -> FeatureOp */
+ final ArrayMap<String, FeatureOp> mFeatures = new ArrayMap<>(1);
+
+ Op(UidState uidState, String packageName, int op) {
+ this.op = op;
+ this.uidState = uidState;
+ this.packageName = packageName;
+ this.mode = AppOpsManager.opToDefaultMode(op);
+ }
+
+ int getMode() {
+ return mode;
+ }
+
+ int evalMode() {
+ return uidState.evalMode(op, mode);
+ }
+
+ void removeFeaturesWithNoTime() {
+ for (int i = mFeatures.size() - 1; i >= 0; i--) {
+ if (!mFeatures.valueAt(i).hasAnyTime()) {
+ mFeatures.removeAt(i);
+ }
+ }
+ }
+
+ private @NonNull FeatureOp getOrCreateFeature(@NonNull Op parent,
+ @Nullable String featureId) {
+ FeatureOp featureOp;
+
+ featureOp = mFeatures.get(featureId);
+ if (featureOp == null) {
+ featureOp = new FeatureOp(parent);
+ mFeatures.put(featureId, featureOp);
+ }
+
+ return featureOp;
+ }
+
+ @NonNull OpEntry createEntryLocked() {
+ final int numFeatures = mFeatures.size();
+
+ final Pair<String, OpFeatureEntry.Builder>[] featureEntries =
+ new Pair[numFeatures];
+ for (int i = 0; i < numFeatures; i++) {
+ featureEntries[i] = new Pair<>(mFeatures.keyAt(i),
+ mFeatures.valueAt(i).createFeatureEntryBuilderLocked());
+ }
+
+ return new OpEntry(op, mode, featureEntries);
+ }
+
+ @NonNull OpEntry createSingleFeatureEntryLocked(@Nullable String featureId) {
+ final int numFeatures = mFeatures.size();
+
+ final Pair<String, AppOpsManager.OpFeatureEntry.Builder>[] featureEntries =
+ new Pair[1];
+ for (int i = 0; i < numFeatures; i++) {
+ if (Objects.equals(mFeatures.keyAt(i), featureId)) {
+ featureEntries[0] = new Pair<>(mFeatures.keyAt(i),
+ mFeatures.valueAt(i).createFeatureEntryBuilderLocked());
+ break;
+ }
+ }
+
+ return new OpEntry(op, mode, featureEntries);
}
}
@@ -778,7 +801,7 @@ public class AppOpsService extends IAppOpsService.Stub {
final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>();
final class ClientState extends Binder implements DeathRecipient {
- final ArrayList<Op> mStartedOps = new ArrayList<>();
+ final ArrayList<Pair<Op, String>> mStartedOps = new ArrayList<>();
final IBinder mAppToken;
final int mPid;
@@ -807,7 +830,15 @@ public class AppOpsService extends IAppOpsService.Stub {
public void binderDied() {
synchronized (AppOpsService.this) {
for (int i=mStartedOps.size()-1; i>=0; i--) {
- finishOperationLocked(mStartedOps.get(i), /*finishNested*/ true);
+ final Pair<Op, String> startedOp = mStartedOps.get(i);
+ final Op op = startedOp.first;
+ final String featureId = startedOp.second;
+
+ finishOperationLocked(op, featureId, /*finishNested*/ true);
+ if (op.mFeatures.get(featureId).startNesting <= 0) {
+ scheduleOpActiveChangedIfNeededLocked(op.op, op.uidState.uid,
+ op.packageName, false);
+ }
}
mClients.remove(mAppToken);
}
@@ -936,11 +967,11 @@ public class AppOpsService extends IAppOpsService.Stub {
return Zygote.MOUNT_EXTERNAL_NONE;
}
if (noteOperation(AppOpsManager.OP_READ_EXTERNAL_STORAGE, uid,
- packageName) != AppOpsManager.MODE_ALLOWED) {
+ packageName, null) != AppOpsManager.MODE_ALLOWED) {
return Zygote.MOUNT_EXTERNAL_NONE;
}
if (noteOperation(AppOpsManager.OP_WRITE_EXTERNAL_STORAGE, uid,
- packageName) != AppOpsManager.MODE_ALLOWED) {
+ packageName, null) != AppOpsManager.MODE_ALLOWED) {
return Zygote.MOUNT_EXTERNAL_READ;
}
return Zygote.MOUNT_EXTERNAL_WRITE;
@@ -983,13 +1014,16 @@ public class AppOpsService extends IAppOpsService.Stub {
if (client.mStartedOps == null) {
continue;
}
- final int opCount = client.mStartedOps.size();
- for (int j = opCount - 1; j >= 0; j--) {
- final Op op = client.mStartedOps.get(j);
+ final int startedOpCount = client.mStartedOps.size();
+ for (int j = startedOpCount - 1; j >= 0; j--) {
+ final Pair<Op, String> startedOp = client.mStartedOps.get(j);
+ final Op op = startedOp.first;
+ final String featureId = startedOp.second;
+
if (uid == op.uidState.uid && packageName.equals(op.packageName)) {
- finishOperationLocked(op, /*finishNested*/ true);
+ finishOperationLocked(op, featureId, /*finishNested*/ true);
client.mStartedOps.remove(j);
- if (op.startNesting <= 0) {
+ if (op.mFeatures.get(featureId).startNesting <= 0) {
scheduleOpActiveChangedIfNeededLocked(op.op,
uid, packageName, false);
}
@@ -1001,11 +1035,16 @@ public class AppOpsService extends IAppOpsService.Stub {
scheduleFastWriteLocked();
final int opCount = ops.size();
- for (int i = 0; i < opCount; i++) {
- final Op op = ops.valueAt(i);
- if (op.running) {
- scheduleOpActiveChangedIfNeededLocked(
- op.op, op.uidState.uid, op.packageName, false);
+ for (int opNum = 0; opNum < opCount; opNum++) {
+ final Op op = ops.valueAt(opNum);
+
+ final int numFeatures = op.mFeatures.size();
+ for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
+ if (op.mFeatures.valueAt(featureNum).running) {
+ scheduleOpActiveChangedIfNeededLocked(
+ op.op, op.uidState.uid, op.packageName, false);
+ break;
+ }
}
}
}
@@ -1058,19 +1097,26 @@ public class AppOpsService extends IAppOpsService.Stub {
final Ops ops = uidState.pkgOps.valueAt(i);
for (int j = ops.size() - 1; j >= 0; j--) {
final Op op = ops.valueAt(j);
- if (op.startNesting > 0) {
- final long duration = SystemClock.elapsedRealtime()
- - op.startRealtime;
- // We don't support proxy long running ops (start/stop)
- mHistoricalRegistry.increaseOpAccessDuration(op.op,
- op.uidState.uid, op.packageName, oldPendingState,
- AppOpsManager.OP_FLAG_SELF, duration);
- // Finish the op in the old state
- op.finished(now, duration, oldPendingState,
- AppOpsManager.OP_FLAG_SELF);
- // Start the op in the new state
- op.startRealtime = now;
- op.started(now, newState, AppOpsManager.OP_FLAG_SELF);
+
+ int numFeatures = op.mFeatures.size();
+ for (int featureNum = 0; featureNum < numFeatures;
+ featureNum++) {
+ final FeatureOp featureOp = op.mFeatures.valueAt(
+ featureNum);
+ if (featureOp.startNesting > 0) {
+ final long duration = SystemClock.elapsedRealtime()
+ - featureOp.startRealtime;
+ // We don't support proxy long running ops (start/stop)
+ mHistoricalRegistry.increaseOpAccessDuration(op.op,
+ op.uidState.uid, op.packageName, oldPendingState,
+ AppOpsManager.OP_FLAG_SELF, duration);
+ // Finish the op in the old state
+ featureOp.finished(now, duration, oldPendingState,
+ AppOpsManager.OP_FLAG_SELF);
+ // Start the op in the new state
+ featureOp.startRealtime = now;
+ featureOp.started(now, newState, AppOpsManager.OP_FLAG_SELF);
+ }
}
}
}
@@ -1117,9 +1163,13 @@ public class AppOpsService extends IAppOpsService.Stub {
}
@Nullable
- private ArrayList<AppOpsManager.OpEntry> collectOps(@NonNull UidState uidState,
+ private ArrayList<AppOpsManager.OpEntry> collectUidOps(@NonNull UidState uidState,
@Nullable int[] ops) {
- int opModeCount = uidState.getOpModeCount();
+ if (uidState.opModes == null) {
+ return null;
+ }
+
+ int opModeCount = uidState.opModes.size();
if (opModeCount == 0) {
return null;
}
@@ -1127,17 +1177,17 @@ public class AppOpsService extends IAppOpsService.Stub {
if (ops == null) {
resOps = new ArrayList<>();
for (int i = 0; i < opModeCount; i++) {
- int code = uidState.getOpCodeAt(i);
- resOps.add(new OpEntry(code, uidState.getOpMode(code)));
+ int code = uidState.opModes.keyAt(i);
+ resOps.add(new OpEntry(code, uidState.opModes.get(code), new Pair[0]));
}
} else {
- for (int i = 0; i < ops.length; i++) {
- int code = ops[i];
- if (uidState.hasOpMode(code)) {
+ for (int j=0; j<ops.length; j++) {
+ int code = uidState.opModes.keyAt(j);
+ if (code >= 0) {
if (resOps == null) {
resOps = new ArrayList<>();
}
- resOps.add(new OpEntry(code, uidState.getOpMode(code)));
+ resOps.add(new OpEntry(code, uidState.opModes.get(code), new Pair[0]));
}
}
}
@@ -1145,17 +1195,17 @@ public class AppOpsService extends IAppOpsService.Stub {
}
private static @NonNull OpEntry getOpEntryForResult(@NonNull Op op, long elapsedNow) {
- if (op.running) {
- op.continuing(elapsedNow - op.startRealtime,
- op.uidState.state, AppOpsManager.OP_FLAG_SELF);
- }
- final OpEntry entry = new OpEntry(op.op, op.running, op.mode,
- op.mAccessTimes != null ? op.mAccessTimes.clone() : null,
- op.mRejectTimes != null ? op.mRejectTimes.clone() : null,
- op.mDurations != null ? op.mDurations.clone() : null,
- op.mProxyUids != null ? op.mProxyUids.clone() : null,
- op.mProxyPackageNames != null ? op.mProxyPackageNames.clone() : null);
- return entry;
+ final int numFeatures = op.mFeatures.size();
+
+ for (int i = 0; i < numFeatures; i++) {
+ final FeatureOp featureOp = op.mFeatures.valueAt(i);
+ if (featureOp.running) {
+ featureOp.continuing(elapsedNow - featureOp.startRealtime,
+ op.uidState.state, AppOpsManager.OP_FLAG_SELF);
+ }
+ }
+
+ return op.createEntryLocked();
}
@Override
@@ -1283,11 +1333,11 @@ public class AppOpsService extends IAppOpsService.Stub {
if (uidState == null) {
return null;
}
- ArrayList<AppOpsManager.OpEntry> resOps = collectOps(uidState, ops);
+ ArrayList<AppOpsManager.OpEntry> resOps = collectUidOps(uidState, ops);
if (resOps == null) {
return null;
}
- ArrayList<AppOpsManager.PackageOps> res = new ArrayList<>();
+ ArrayList<AppOpsManager.PackageOps> res = new ArrayList<AppOpsManager.PackageOps>();
AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
null, uidState.uid, resOps);
res.add(resPackage);
@@ -1296,8 +1346,11 @@ public class AppOpsService extends IAppOpsService.Stub {
}
private void pruneOpLocked(Op op, int uid, String packageName) {
- if (!op.hasAnyTime()) {
- Ops ops = getOpsRawLocked(uid, packageName, false /* isPrivileged */, false /* edit */);
+ op.removeFeaturesWithNoTime();
+
+ if (op.mFeatures.size() == 0) {
+ Ops ops = getOpsRawLocked(uid, packageName, false /* isPrivileged */,
+ false /* edit */);
if (ops != null) {
ops.remove(op.op);
if (ops.size() <= 0) {
@@ -1346,6 +1399,8 @@ public class AppOpsService extends IAppOpsService.Stub {
verifyIncomingOp(code);
code = AppOpsManager.opToSwitch(code);
+ updatePermissionRevokedCompat(uid, code, mode);
+
synchronized (this) {
final int defaultMode = AppOpsManager.opToDefaultMode(code);
@@ -1355,14 +1410,29 @@ public class AppOpsService extends IAppOpsService.Stub {
return;
}
uidState = new UidState(uid);
- uidState.putOpMode(code, mode);
+ uidState.opModes = new SparseIntArray();
+ uidState.opModes.put(code, mode);
mUidStates.put(uid, uidState);
scheduleWriteLocked();
- } else {
- boolean changed = uidState.putOpMode(code, mode);
- if (changed) {
+ } else if (uidState.opModes == null) {
+ if (mode != defaultMode) {
+ uidState.opModes = new SparseIntArray();
+ uidState.opModes.put(code, mode);
scheduleWriteLocked();
}
+ } else {
+ if (uidState.opModes.indexOfKey(code) >= 0 && uidState.opModes.get(code) == mode) {
+ return;
+ }
+ if (mode == defaultMode) {
+ uidState.opModes.delete(code);
+ if (uidState.opModes.size() <= 0) {
+ uidState.opModes = null;
+ }
+ } else {
+ uidState.opModes.put(code, mode);
+ }
+ scheduleWriteLocked();
}
uidState.evalForegroundOps(mOpModeWatchers);
}
@@ -1432,6 +1502,92 @@ public class AppOpsService extends IAppOpsService.Stub {
notifyOpChangedSync(code, uid, null, mode);
}
+ private void updatePermissionRevokedCompat(int uid, int switchCode, int mode) {
+ PackageManagerInternal packageManagerInternal = LocalServices.getService(
+ PackageManagerInternal.class);
+ if (packageManagerInternal.getUidTargetSdkVersion(uid) >= Build.VERSION_CODES.M) {
+ return;
+ }
+
+ PackageManager packageManager = mContext.getPackageManager();
+ String[] packageNames = packageManager.getPackagesForUid(uid);
+ if (ArrayUtils.isEmpty(packageNames)) {
+ return;
+ }
+ String packageName = packageNames[0];
+
+ List<Integer> ops = getSwitchOpToOps().get(switchCode);
+ int opsSize = CollectionUtils.size(ops);
+ for (int i = 0; i < opsSize; i++) {
+ int code = ops.get(i);
+
+ String permissionName = AppOpsManager.opToPermission(code);
+ if (permissionName == null) {
+ continue;
+ }
+
+ PermissionInfo permissionInfo;
+ try {
+ permissionInfo = packageManager.getPermissionInfo(permissionName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ continue;
+ }
+
+ if (!permissionInfo.isRuntime()) {
+ continue;
+ }
+
+ UserHandle user = UserHandle.getUserHandleForUid(uid);
+ boolean isRevokedCompat;
+ if (permissionInfo.backgroundPermission != null) {
+ boolean isBackgroundRevokedCompat = mode != AppOpsManager.MODE_ALLOWED;
+ long identity = Binder.clearCallingIdentity();
+ try {
+ packageManager.updatePermissionFlags(permissionInfo.backgroundPermission,
+ packageName, PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
+ isBackgroundRevokedCompat
+ ? PackageManager.FLAG_PERMISSION_REVOKED_COMPAT : 0, user);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+
+ isRevokedCompat = mode != AppOpsManager.MODE_ALLOWED
+ && mode != AppOpsManager.MODE_FOREGROUND;
+ } else {
+ isRevokedCompat = mode != AppOpsManager.MODE_ALLOWED;
+ }
+
+ long identity = Binder.clearCallingIdentity();
+ try {
+ packageManager.updatePermissionFlags(permissionName, packageName,
+ PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, isRevokedCompat
+ ? PackageManager.FLAG_PERMISSION_REVOKED_COMPAT : 0, user);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ @NonNull
+ private SparseArray<List<Integer>> getSwitchOpToOps() {
+ synchronized (this) {
+ if (mSwitchOpToOps == null) {
+ mSwitchOpToOps = new SparseArray<>();
+ for (int op = 0; op < _NUM_OP; op++) {
+ int switchOp = AppOpsManager.opToSwitch(op);
+ List<Integer> ops = mSwitchOpToOps.get(switchOp);
+ if (ops == null) {
+ ops = new ArrayList<>();
+ mSwitchOpToOps.put(switchOp, ops);
+ }
+ ops.add(op);
+ }
+ }
+ return mSwitchOpToOps;
+ }
+ }
+
private void notifyOpChangedSync(int code, int uid, @NonNull String packageName, int mode) {
final StorageManagerInternal storageManagerInternal =
LocalServices.getService(StorageManagerInternal.class);
@@ -1517,9 +1673,9 @@ public class AppOpsService extends IAppOpsService.Stub {
if (uid != UID_ANY && callback.mWatchingUid >= 0 && callback.mWatchingUid != uid) {
return;
}
- // There are components watching for mode changes such as window manager
+ // There are features watching for mode changes such as window manager
// and location manager which are in our process. The callbacks in these
- // components may require permissions our remote caller does not have.
+ // features may require permissions our remote caller does not have.
final long identity = Binder.clearCallingIdentity();
try {
callback.mCallback.opChanged(code, uid, packageName);
@@ -1601,13 +1757,16 @@ public class AppOpsService extends IAppOpsService.Stub {
for (int i = mUidStates.size() - 1; i >= 0; i--) {
UidState uidState = mUidStates.valueAt(i);
- if (uidState.uid == reqUid || reqUid == -1) {
- for (int opModeIndex = uidState.getOpModeCount() - 1; opModeIndex >= 0;
- opModeIndex--) {
- final int code = uidState.getOpCodeAt(opModeIndex);
-
+ SparseIntArray opModes = uidState.opModes;
+ if (opModes != null && (uidState.uid == reqUid || reqUid == -1)) {
+ final int uidOpCount = opModes.size();
+ for (int j = uidOpCount - 1; j >= 0; j--) {
+ final int code = opModes.keyAt(j);
if (AppOpsManager.opAllowsReset(code)) {
- uidState.removeOpMode(code);
+ opModes.removeAt(j);
+ if (opModes.size() <= 0) {
+ uidState.opModes = null;
+ }
for (String packageName : getPackagesForUid(uidState.uid)) {
callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
mOpModeWatchers.get(code));
@@ -1651,7 +1810,9 @@ public class AppOpsService extends IAppOpsService.Stub {
mOpModeWatchers.get(curOp.op));
callbacks = addCallbacks(callbacks, curOp.op, uid, packageName,
mPackageModeWatchers.get(packageName));
- if (!curOp.hasAnyTime()) {
+
+ curOp.removeFeaturesWithNoTime();
+ if (curOp.mFeatures.size() == 0) {
pkgOps.removeAt(j);
}
}
@@ -1857,8 +2018,9 @@ public class AppOpsService extends IAppOpsService.Stub {
}
code = AppOpsManager.opToSwitch(code);
UidState uidState = getUidStateLocked(uid, false);
- if (uidState != null && uidState.hasOpMode(code)) {
- final int rawMode = uidState.getOpMode(code);
+ if (uidState != null && uidState.opModes != null
+ && uidState.opModes.indexOfKey(code) >= 0) {
+ final int rawMode = uidState.opModes.get(code);
return raw ? rawMode : uidState.evalMode(code, rawMode);
}
Op op = getOpLocked(code, uid, packageName, false, false);
@@ -1933,8 +2095,9 @@ public class AppOpsService extends IAppOpsService.Stub {
}
@Override
- public int noteProxyOperation(int code, int proxyUid,
- String proxyPackageName, int proxiedUid, String proxiedPackageName) {
+ public int noteProxyOperation(int code, int proxiedUid, String proxiedPackageName,
+ String proxiedFeatureId, int proxyUid, String proxyPackageName,
+ String proxyFeatureId) {
verifyIncomingUid(proxyUid);
verifyIncomingOp(code);
@@ -1949,8 +2112,8 @@ public class AppOpsService extends IAppOpsService.Stub {
final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
: AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;
- final int proxyMode = noteOperationUnchecked(code, proxyUid,
- resolveProxyPackageName, Process.INVALID_UID, null, proxyFlags);
+ final int proxyMode = noteOperationUnchecked(code, proxyUid, resolveProxyPackageName,
+ proxyFeatureId, Process.INVALID_UID, null, null, proxyFlags);
if (proxyMode != AppOpsManager.MODE_ALLOWED || Binder.getCallingUid() == proxiedUid) {
return proxyMode;
}
@@ -1962,35 +2125,39 @@ public class AppOpsService extends IAppOpsService.Stub {
final int proxiedFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXIED
: AppOpsManager.OP_FLAG_UNTRUSTED_PROXIED;
return noteOperationUnchecked(code, proxiedUid, resolveProxiedPackageName,
- proxyUid, resolveProxyPackageName, proxiedFlags);
+ proxiedFeatureId, proxyUid, resolveProxyPackageName, proxyFeatureId,
+ proxiedFlags);
}
@Override
- public int noteOperation(int code, int uid, String packageName) {
+ public int noteOperation(int code, int uid, String packageName, String featureId) {
final CheckOpsDelegate delegate;
synchronized (this) {
delegate = mCheckOpsDelegate;
}
if (delegate == null) {
- return noteOperationImpl(code, uid, packageName);
+ return noteOperationImpl(code, uid, packageName, featureId);
}
- return delegate.noteOperation(code, uid, packageName,
+ return delegate.noteOperation(code, uid, packageName, featureId,
AppOpsService.this::noteOperationImpl);
}
- private int noteOperationImpl(int code, int uid, String packageName) {
+ private int noteOperationImpl(int code, int uid, String packageName, String featureId) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
return AppOpsManager.MODE_IGNORED;
}
- return noteOperationUnchecked(code, uid, resolvedPackageName, Process.INVALID_UID, null,
- AppOpsManager.OP_FLAG_SELF);
+ return noteOperationUnchecked(code, uid, resolvedPackageName, featureId,
+ Process.INVALID_UID, null, null, AppOpsManager.OP_FLAG_SELF);
}
- private int noteOperationUnchecked(int code, int uid, String packageName,
- int proxyUid, String proxyPackageName, @OpFlags int flags) {
+ private int noteOperationUnchecked(int code, int uid, String packageName, String featureId,
+ int proxyUid, String proxyPackageName, @Nullable String proxyFeatureId,
+ @OpFlags int flags) {
+ // TODO moltmann: Verify that feature is declared in package
+
boolean isPrivileged;
try {
isPrivileged = verifyAndGetIsPrivileged(uid, packageName);
@@ -2009,15 +2176,18 @@ public class AppOpsService extends IAppOpsService.Stub {
return AppOpsManager.MODE_ERRORED;
}
final Op op = getOpLocked(ops, code, true);
+ final FeatureOp featureOp = op.getOrCreateFeature(op, featureId);
if (isOpRestrictedLocked(uid, code, packageName, isPrivileged)) {
scheduleOpNotedIfNeededLocked(code, uid, packageName,
AppOpsManager.MODE_IGNORED);
return AppOpsManager.MODE_IGNORED;
}
final UidState uidState = ops.uidState;
- if (op.running) {
- final OpEntry entry = new OpEntry(op.op, op.running, op.mode, op.mAccessTimes,
- op.mRejectTimes, op.mDurations, op.mProxyUids, op.mProxyPackageNames);
+ if (featureOp.running) {
+ final OpFeatureEntry entry = getOpLocked(ops, code,
+ false).createSingleFeatureEntryLocked(featureId).getFeatures().get(
+ featureId);
+
Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
+ " code " + code + " time=" + entry.getLastAccessTime(uidState.state,
uidState.state, flags) + " duration=" + entry.getLastDuration(
@@ -2027,14 +2197,14 @@ public class AppOpsService extends IAppOpsService.Stub {
final int switchCode = AppOpsManager.opToSwitch(code);
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
- if (uidState.hasOpMode(switchCode)) {
- final int uidMode = uidState.evalMode(code, uidState.getOpMode(switchCode));
+ if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
+ final int uidMode = uidState.evalMode(code, uidState.opModes.get(switchCode));
if (uidMode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
+ packageName);
- op.rejected(System.currentTimeMillis(), proxyUid, proxyPackageName,
- uidState.state, flags);
+ featureOp.rejected(System.currentTimeMillis(), proxyUid, proxyPackageName,
+ proxyFeatureId, uidState.state, flags);
mHistoricalRegistry.incrementOpRejected(code, uid, packageName,
uidState.state, flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName, uidMode);
@@ -2047,8 +2217,8 @@ public class AppOpsService extends IAppOpsService.Stub {
if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + mode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
+ packageName);
- op.rejected(System.currentTimeMillis(), proxyUid, proxyPackageName,
- uidState.state, flags);
+ featureOp.rejected(System.currentTimeMillis(), proxyUid, proxyPackageName,
+ proxyFeatureId, uidState.state, flags);
mHistoricalRegistry.incrementOpRejected(code, uid, packageName,
uidState.state, flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName, mode);
@@ -2056,9 +2226,10 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
if (DEBUG) Slog.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
- + " package " + packageName);
- op.accessed(System.currentTimeMillis(), proxyUid, proxyPackageName,
- uidState.state, flags);
+ + " package " + packageName + (featureId == null ? "" : "." + featureId));
+ featureOp.accessed(System.currentTimeMillis(), proxyUid, proxyPackageName,
+ proxyFeatureId, uidState.state, flags);
+ // TODO moltmann: Add features to historical app-ops
mHistoricalRegistry.incrementOpAccessedCount(op.op, uid, packageName,
uidState.state, flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName,
@@ -2067,6 +2238,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
+ // TODO moltmann: Allow watching for feature ops
@Override
public void startWatchingActive(int[] ops, IAppOpsActiveCallback callback) {
int watchedUid = -1;
@@ -2160,7 +2332,7 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public void noteAsyncOp(String callingPackageName, int uid, String packageName, int opCode,
- String message) {
+ String featureId, String message) {
Preconditions.checkNotNull(message);
Preconditions.checkNotNull(packageName);
verifyAndGetIsPrivileged(uid, packageName);
@@ -2182,7 +2354,7 @@ public class AppOpsService extends IAppOpsService.Stub {
RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key);
AsyncNotedAppOp asyncNotedOp = new AsyncNotedAppOp(opCode, callingUid,
- callingPackageName, message, now);
+ callingPackageName, featureId, message, now);
final boolean[] wasNoteForwarded = {false};
if (callbacks != null) {
@@ -2193,7 +2365,7 @@ public class AppOpsService extends IAppOpsService.Stub {
} catch (RemoteException e) {
Slog.e(TAG,
"Could not forward noteOp of " + opCode + " to " + packageName
- + "/" + uid, e);
+ + "/" + uid + "(" + featureId + ")", e);
}
});
}
@@ -2295,7 +2467,7 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public int startOperation(IBinder token, int code, int uid, String packageName,
- boolean startIfModeDefault) {
+ String featureId, boolean startIfModeDefault) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
String resolvedPackageName = resolvePackageName(uid, packageName);
@@ -2324,21 +2496,23 @@ public class AppOpsService extends IAppOpsService.Stub {
if (isOpRestrictedLocked(uid, code, resolvedPackageName, isPrivileged)) {
return AppOpsManager.MODE_IGNORED;
}
+ final FeatureOp featureOp = op.getOrCreateFeature(op, featureId);
final int switchCode = AppOpsManager.opToSwitch(code);
final UidState uidState = ops.uidState;
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
final int opCode = op.op;
- if (uidState.hasOpMode(switchCode)) {
- final int uidMode = uidState.evalMode(code, uidState.getOpMode(switchCode));
+ if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
+ final int uidMode = uidState.evalMode(code, uidState.opModes.get(switchCode));
if (uidMode != AppOpsManager.MODE_ALLOWED
&& (!startIfModeDefault || uidMode != AppOpsManager.MODE_DEFAULT)) {
if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
+ resolvedPackageName);
// We don't support proxy long running ops (start/stop)
- op.rejected(System.currentTimeMillis(), -1 /*proxyUid*/,
- null /*proxyPackage*/, uidState.state, AppOpsManager.OP_FLAG_SELF);
+ featureOp.rejected(System.currentTimeMillis(), -1 /*proxyUid*/,
+ null /*proxyPackage*/, null, uidState.state,
+ AppOpsManager.OP_FLAG_SELF);
mHistoricalRegistry.incrementOpRejected(opCode, uid, packageName,
uidState.state, AppOpsManager.OP_FLAG_SELF);
return uidMode;
@@ -2352,8 +2526,9 @@ public class AppOpsService extends IAppOpsService.Stub {
+ switchCode + " (" + code + ") uid " + uid + " package "
+ resolvedPackageName);
// We don't support proxy long running ops (start/stop)
- op.rejected(System.currentTimeMillis(), -1 /*proxyUid*/,
- null /*proxyPackage*/, uidState.state, AppOpsManager.OP_FLAG_SELF);
+ featureOp.rejected(System.currentTimeMillis(), -1 /*proxyUid*/,
+ null /*proxyPackage*/, null, uidState.state,
+ AppOpsManager.OP_FLAG_SELF);
mHistoricalRegistry.incrementOpRejected(opCode, uid, packageName,
uidState.state, AppOpsManager.OP_FLAG_SELF);
return mode;
@@ -2361,28 +2536,30 @@ public class AppOpsService extends IAppOpsService.Stub {
}
if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid
+ " package " + resolvedPackageName);
- if (op.startNesting == 0) {
- op.startRealtime = SystemClock.elapsedRealtime();
+ if (featureOp.startNesting == 0) {
+ featureOp.startRealtime = SystemClock.elapsedRealtime();
// We don't support proxy long running ops (start/stop)
- op.started(System.currentTimeMillis(), uidState.state,
+ featureOp.started(System.currentTimeMillis(), uidState.state,
AppOpsManager.OP_FLAG_SELF);
mHistoricalRegistry.incrementOpAccessedCount(opCode, uid, packageName,
uidState.state, AppOpsManager.OP_FLAG_SELF);
- scheduleOpActiveChangedIfNeededLocked(code, uid, packageName, true);
+ // TODO moltmann: call back when a feature became inactive
+ if (uidState.startNesting == 0) {
+ scheduleOpActiveChangedIfNeededLocked(code, uid, packageName, true);
+ }
}
- op.startNesting++;
+ featureOp.startNesting++;
uidState.startNesting++;
- if (client.mStartedOps != null) {
- client.mStartedOps.add(op);
- }
+ client.mStartedOps.add(new Pair<>(op, featureId));
}
return AppOpsManager.MODE_ALLOWED;
}
@Override
- public void finishOperation(IBinder token, int code, int uid, String packageName) {
+ public void finishOperation(IBinder token, int code, int uid, String packageName,
+ String featureId) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
String resolvedPackageName = resolvePackageName(uid, packageName);
@@ -2407,9 +2584,16 @@ public class AppOpsService extends IAppOpsService.Stub {
if (op == null) {
return;
}
- if (client.mStartedOps.remove(op)) {
- finishOperationLocked(op, /*finishNested*/ false);
- if (op.startNesting <= 0) {
+ final FeatureOp featureOp = op.mFeatures.get(featureId);
+ if (featureOp == null) {
+ return;
+ }
+
+ if (client.mStartedOps.remove(new Pair<>(op, featureId))) {
+ finishOperationLocked(op, featureId, /*finishNested*/ false);
+
+ // TODO moltmann: call back when a feature became inactive
+ if (op.uidState.startNesting <= 0) {
scheduleOpActiveChangedIfNeededLocked(code, uid, packageName, false);
}
@@ -2464,9 +2648,9 @@ public class AppOpsService extends IAppOpsService.Stub {
private void notifyOpActiveChanged(ArraySet<ActiveCallback> callbacks,
int code, int uid, String packageName, boolean active) {
- // There are components watching for mode changes such as window manager
+ // There are features watching for mode changes such as window manager
// and location manager which are in our process. The callbacks in these
- // components may require permissions our remote caller does not have.
+ // features may require permissions our remote caller does not have.
final long identity = Binder.clearCallingIdentity();
try {
final int callbackCount = callbacks.size();
@@ -2510,8 +2694,8 @@ public class AppOpsService extends IAppOpsService.Stub {
private void notifyOpChecked(ArraySet<NotedCallback> callbacks,
int code, int uid, String packageName, int result) {
- // There are components watching for checks in our process. The callbacks in
- // these components may require permissions our remote caller does not have.
+ // There are features watching for checks in our process. The callbacks in
+ // these features may require permissions our remote caller does not have.
final long identity = Binder.clearCallingIdentity();
try {
final int callbackCount = callbacks.size();
@@ -2555,32 +2739,34 @@ public class AppOpsService extends IAppOpsService.Stub {
return permInfo.getProtection() == PROTECTION_DANGEROUS;
}
- void finishOperationLocked(Op op, boolean finishNested) {
- final int opCode = op.op;
- final int uid = op.uidState.uid;
- if (op.startNesting <= 1 || finishNested) {
- if (op.startNesting == 1 || finishNested) {
+ void finishOperationLocked(@NonNull Op op, @Nullable String featureId, boolean finishNested) {
+ final FeatureOp featureOp = op.mFeatures.get(featureId);
+ final int opCode = featureOp.parent.op;
+ final int uid = featureOp.parent.uidState.uid;
+ if (featureOp.startNesting <= 1 || finishNested) {
+ if (featureOp.startNesting == 1 || finishNested) {
// We don't support proxy long running ops (start/stop)
- final long duration = SystemClock.elapsedRealtime() - op.startRealtime;
- op.finished(System.currentTimeMillis(), duration, op.uidState.state,
+ final long duration = SystemClock.elapsedRealtime() - featureOp.startRealtime;
+ featureOp.finished(System.currentTimeMillis(), duration, op.uidState.state,
AppOpsManager.OP_FLAG_SELF);
mHistoricalRegistry.increaseOpAccessDuration(opCode, uid, op.packageName,
op.uidState.state, AppOpsManager.OP_FLAG_SELF, duration);
} else {
- final OpEntry entry = new OpEntry(op.op, op.running, op.mode, op.mAccessTimes,
- op.mRejectTimes, op.mDurations, op.mProxyUids, op.mProxyPackageNames);
+ final OpFeatureEntry entry = op.createSingleFeatureEntryLocked(
+ featureId).getFeatures().get(featureId);
Slog.w(TAG, "Finishing op nesting under-run: uid " + uid + " pkg "
+ op.packageName + " code " + opCode + " time="
+ entry.getLastAccessTime(OP_FLAGS_ALL)
+ " duration=" + entry.getLastDuration(MAX_PRIORITY_UID_STATE,
- MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL) + " nesting=" + op.startNesting);
+ MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL) + " nesting="
+ + featureOp.startNesting);
}
- if (op.startNesting >= 1) {
- op.uidState.startNesting -= op.startNesting;
+ if (featureOp.startNesting >= 1) {
+ op.uidState.startNesting -= featureOp.startNesting;
}
- op.startNesting = 0;
+ featureOp.startNesting = 0;
} else {
- op.startNesting--;
+ featureOp.startNesting--;
op.uidState.startNesting--;
}
}
@@ -2649,8 +2835,9 @@ public class AppOpsService extends IAppOpsService.Stub {
|| !callback.isWatchingUid(uidState.uid)) {
continue;
}
- boolean doAllPackages = uidState.hasOpMode(code)
- && uidState.getOpMode(code) == AppOpsManager.MODE_FOREGROUND;
+ boolean doAllPackages = uidState.opModes != null
+ && uidState.opModes.indexOfKey(code) >= 0
+ && uidState.opModes.get(code) == AppOpsManager.MODE_FOREGROUND;
if (uidState.pkgOps != null) {
for (int pkgi = uidState.pkgOps.size() - 1; pkgi >= 0; pkgi--) {
final Op op = uidState.pkgOps.valueAt(pkgi).get(code);
@@ -2973,9 +3160,12 @@ public class AppOpsService extends IAppOpsService.Stub {
if (uidState == null) {
continue;
}
- if (uidState.hasOpMode(AppOpsManager.OP_RUN_IN_BACKGROUND)) {
- uidState.putOpMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uidState.getOpMode(
- AppOpsManager.OP_RUN_IN_BACKGROUND));
+ if (uidState.opModes != null) {
+ final int idx = uidState.opModes.indexOfKey(AppOpsManager.OP_RUN_IN_BACKGROUND);
+ if (idx >= 0) {
+ uidState.opModes.put(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
+ uidState.opModes.valueAt(idx));
+ }
}
if (uidState.pkgOps == null) {
continue;
@@ -3031,7 +3221,10 @@ public class AppOpsService extends IAppOpsService.Stub {
final int code = Integer.parseInt(parser.getAttributeValue(null, "n"));
final int mode = Integer.parseInt(parser.getAttributeValue(null, "m"));
UidState uidState = getUidStateLocked(uid, true);
- uidState.putOpMode(code, mode);
+ if (uidState.opModes == null) {
+ uidState.opModes = new SparseIntArray();
+ }
+ uidState.opModes.put(code, mode);
} else {
Slog.w(TAG, "Unknown element under <uid-ops>: "
+ parser.getName());
@@ -3107,9 +3300,36 @@ public class AppOpsService extends IAppOpsService.Stub {
uidState.evalForegroundOps(mOpModeWatchers);
}
+ private void readFeatureOp(XmlPullParser parser, @NonNull Op parent,
+ @Nullable String feature) throws NumberFormatException, IOException {
+ final FeatureOp featureOp = parent.getOrCreateFeature(parent, feature);
+
+ final long key = XmlUtils.readLongAttribute(parser, "n");
+
+ final int flags = AppOpsManager.extractFlagsFromKey(key);
+ final int state = AppOpsManager.extractUidStateFromKey(key);
+
+ final long accessTime = XmlUtils.readLongAttribute(parser, "t", 0);
+ final long rejectTime = XmlUtils.readLongAttribute(parser, "r", 0);
+ final long accessDuration = XmlUtils.readLongAttribute(parser, "d", 0);
+ final String proxyPkg = XmlUtils.readStringAttribute(parser, "pp");
+ final int proxyUid = XmlUtils.readIntAttribute(parser, "pu", 0);
+ final String proxyFeatureId = XmlUtils.readStringAttribute(parser, "pc");
+
+ if (accessTime > 0) {
+ featureOp.accessed(accessTime, proxyUid, proxyPkg, proxyFeatureId, state, flags);
+ }
+ if (rejectTime > 0) {
+ featureOp.rejected(rejectTime, proxyUid, proxyPkg, proxyFeatureId, state, flags);
+ }
+ if (accessDuration > 0) {
+ featureOp.running(accessTime, accessDuration, state, flags);
+ }
+ }
+
private void readOp(XmlPullParser parser, @NonNull UidState uidState,
- @NonNull String pkgName, boolean isPrivileged) throws NumberFormatException,
- XmlPullParserException, IOException {
+ @NonNull String pkgName, boolean isPrivileged) throws NumberFormatException,
+ XmlPullParserException, IOException {
Op op = new Op(uidState, pkgName,
Integer.parseInt(parser.getAttributeValue(null, "n")));
@@ -3126,26 +3346,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
String tagName = parser.getName();
if (tagName.equals("st")) {
- final long key = XmlUtils.readLongAttribute(parser, "n");
-
- final int flags = AppOpsManager.extractFlagsFromKey(key);
- final int state = AppOpsManager.extractUidStateFromKey(key);
-
- final long accessTime = XmlUtils.readLongAttribute(parser, "t", 0);
- final long rejectTime = XmlUtils.readLongAttribute(parser, "r", 0);
- final long accessDuration = XmlUtils.readLongAttribute(parser, "d", 0);
- final String proxyPkg = XmlUtils.readStringAttribute(parser, "pp");
- final int proxyUid = XmlUtils.readIntAttribute(parser, "pu", 0);
-
- if (accessTime > 0) {
- op.accessed(accessTime, proxyUid, proxyPkg, state, flags);
- }
- if (rejectTime > 0) {
- op.rejected(rejectTime, proxyUid, proxyPkg, state, flags);
- }
- if (accessDuration > 0) {
- op.running(accessTime, accessDuration, state, flags);
- }
+ readFeatureOp(parser, op, XmlUtils.readStringAttribute(parser, "id"));
} else {
Slog.w(TAG, "Unknown element under <op>: "
+ parser.getName());
@@ -3183,38 +3384,47 @@ public class AppOpsService extends IAppOpsService.Stub {
out.startTag(null, "app-ops");
out.attribute(null, "v", String.valueOf(CURRENT_VERSION));
- final SparseArray<SparseIntArray> uidOpModes = new SparseArray<>();
+ SparseArray<SparseIntArray> uidStatesClone;
synchronized (this) {
- final int uidStatesSize = mUidStates.size();
- for (int i = 0; i < uidStatesSize; i++) {
- final SparseIntArray opModes = mUidStates.valueAt(i).cloneOpModes();
- if (opModes != null) {
- final int uid = mUidStates.keyAt(i);
- uidOpModes.put(uid, opModes);
+ uidStatesClone = new SparseArray<>(mUidStates.size());
+
+ final int uidStateCount = mUidStates.size();
+ for (int uidStateNum = 0; uidStateNum < uidStateCount; uidStateNum++) {
+ UidState uidState = mUidStates.valueAt(uidStateNum);
+ int uid = mUidStates.keyAt(uidStateNum);
+
+ SparseIntArray opModes = uidState.opModes;
+ if (opModes != null && opModes.size() > 0) {
+ uidStatesClone.put(uid, new SparseIntArray(opModes.size()));
+
+ final int opCount = opModes.size();
+ for (int opCountNum = 0; opCountNum < opCount; opCountNum++) {
+ uidStatesClone.get(uid).put(
+ opModes.keyAt(opCountNum),
+ opModes.valueAt(opCountNum));
+ }
}
}
}
- final int uidOpModesSize = uidOpModes.size();
- for (int uidOpModesIndex = 0; uidOpModesIndex < uidOpModesSize; uidOpModesIndex++) {
- final int uid = uidOpModes.keyAt(uidOpModesIndex);
- final SparseIntArray opModes = uidOpModes.valueAt(uidOpModesIndex);
-
- out.startTag(null, "uid");
- out.attribute(null, "n", Integer.toString(uid));
-
- final int opModesSize = opModes.size();
- for (int opModesIndex = 0; opModesIndex < opModesSize; opModesIndex++) {
- final int code = opModes.keyAt(opModesIndex);
- final int mode = opModes.valueAt(opModesIndex);
-
- out.startTag(null, "op");
- out.attribute(null, "n", Integer.toString(code));
- out.attribute(null, "m", Integer.toString(mode));
- out.endTag(null, "op");
+ final int uidStateCount = uidStatesClone.size();
+ for (int uidStateNum = 0; uidStateNum < uidStateCount; uidStateNum++) {
+ SparseIntArray opModes = uidStatesClone.valueAt(uidStateNum);
+ if (opModes != null && opModes.size() > 0) {
+ out.startTag(null, "uid");
+ out.attribute(null, "n",
+ Integer.toString(uidStatesClone.keyAt(uidStateNum)));
+ final int opCount = opModes.size();
+ for (int opCountNum = 0; opCountNum < opCount; opCountNum++) {
+ final int op = opModes.keyAt(opCountNum);
+ final int mode = opModes.valueAt(opCountNum);
+ out.startTag(null, "op");
+ out.attribute(null, "n", Integer.toString(op));
+ out.attribute(null, "m", Integer.toString(mode));
+ out.endTag(null, "op");
+ }
+ out.endTag(null, "uid");
}
-
- out.endTag(null, "uid");
}
if (allOps != null) {
@@ -3251,51 +3461,64 @@ public class AppOpsService extends IAppOpsService.Stub {
out.attribute(null, "m", Integer.toString(op.getMode()));
}
- final LongSparseArray keys = op.collectKeys();
- if (keys == null || keys.size() <= 0) {
- out.endTag(null, "op");
- continue;
- }
-
- final int keyCount = keys.size();
- for (int k = 0; k < keyCount; k++) {
- final long key = keys.keyAt(k);
-
- final int uidState = AppOpsManager.extractUidStateFromKey(key);
- final int flags = AppOpsManager.extractFlagsFromKey(key);
+ for (String featureId : op.getFeatures().keySet()) {
+ final OpFeatureEntry feature = op.getFeatures().get(
+ featureId);
- final long accessTime = op.getLastAccessTime(
- uidState, uidState, flags);
- final long rejectTime = op.getLastRejectTime(
- uidState, uidState, flags);
- final long accessDuration = op.getLastDuration(
- uidState, uidState, flags);
- final String proxyPkg = op.getProxyPackageName(uidState, flags);
- final int proxyUid = op.getProxyUid(uidState, flags);
-
- if (accessTime <= 0 && rejectTime <= 0 && accessDuration <= 0
- && proxyPkg == null && proxyUid < 0) {
+ final LongSparseArray keys = feature.collectKeys();
+ if (keys == null || keys.size() <= 0) {
continue;
}
+ final int keyCount = keys.size();
+
+ for (int k = 0; k < keyCount; k++) {
+ final long key = keys.keyAt(k);
+
+ final int uidState = AppOpsManager.extractUidStateFromKey(key);
+ final int flags = AppOpsManager.extractFlagsFromKey(key);
+
+ final long accessTime = feature.getLastAccessTime(
+ uidState, uidState, flags);
+ final long rejectTime = feature.getLastRejectTime(
+ uidState, uidState, flags);
+ final long accessDuration = feature.getLastDuration(
+ uidState, uidState, flags);
+ final String proxyPkg = feature.getProxyPackageName(uidState,
+ flags);
+ final String proxyFeatureId = feature.getProxyFeatureId(
+ uidState, flags);
+ final int proxyUid = feature.getProxyUid(uidState, flags);
+
+ if (accessTime <= 0 && rejectTime <= 0 && accessDuration <= 0
+ && proxyPkg == null && proxyUid < 0) {
+ continue;
+ }
- out.startTag(null, "st");
- out.attribute(null, "n", Long.toString(key));
- if (accessTime > 0) {
- out.attribute(null, "t", Long.toString(accessTime));
- }
- if (rejectTime > 0) {
- out.attribute(null, "r", Long.toString(rejectTime));
- }
- if (accessDuration > 0) {
- out.attribute(null, "d", Long.toString(accessDuration));
- }
- if (proxyPkg != null) {
- out.attribute(null, "pp", proxyPkg);
- }
- if (proxyUid >= 0) {
- out.attribute(null, "pu", Integer.toString(proxyUid));
+ out.startTag(null, "st");
+ if (featureId != null) {
+ out.attribute(null, "id", featureId);
+ }
+ out.attribute(null, "n", Long.toString(key));
+ if (accessTime > 0) {
+ out.attribute(null, "t", Long.toString(accessTime));
+ }
+ if (rejectTime > 0) {
+ out.attribute(null, "r", Long.toString(rejectTime));
+ }
+ if (accessDuration > 0) {
+ out.attribute(null, "d", Long.toString(accessDuration));
+ }
+ if (proxyPkg != null) {
+ out.attribute(null, "pp", proxyPkg);
+ }
+ if (proxyFeatureId != null) {
+ out.attribute(null, "pc", proxyFeatureId);
+ }
+ if (proxyUid >= 0) {
+ out.attribute(null, "pu", Integer.toString(proxyUid));
+ }
+ out.endTag(null, "st");
}
- out.endTag(null, "st");
}
out.endTag(null, "op");
@@ -3323,6 +3546,7 @@ public class AppOpsService extends IAppOpsService.Stub {
int userId = UserHandle.USER_SYSTEM;
String packageName;
+ String featureId;
String opStr;
String modeStr;
int op;
@@ -3427,6 +3651,8 @@ public class AppOpsService extends IAppOpsService.Stub {
userId = UserHandle.parseUserArg(getNextArgRequired());
} else if ("--uid".equals(argument)) {
targetsUid = true;
+ } else if ("--feature".equals(argument)) {
+ featureId = getNextArgRequired();
} else {
if (packageName == null) {
packageName = argument;
@@ -3521,13 +3747,13 @@ public class AppOpsService extends IAppOpsService.Stub {
pw.println("AppOps service (appops) commands:");
pw.println(" help");
pw.println(" Print this help text.");
- pw.println(" start [--user <USER_ID>] <PACKAGE | UID> <OP> ");
+ pw.println(" start [--user <USER_ID>] [--feature <FEATURE_ID>] <PACKAGE | UID> <OP> ");
pw.println(" Starts a given operation for a particular application.");
- pw.println(" stop [--user <USER_ID>] <PACKAGE | UID> <OP> ");
+ pw.println(" stop [--user <USER_ID>] [--feature <FEATURE_ID>] <PACKAGE | UID> <OP> ");
pw.println(" Stops a given operation for a particular application.");
pw.println(" set [--user <USER_ID>] <[--uid] PACKAGE | UID> <OP> <MODE>");
pw.println(" Set the mode for a particular application and operation.");
- pw.println(" get [--user <USER_ID>] <PACKAGE | UID> [<OP>]");
+ pw.println(" get [--user <USER_ID>] [--feature <FEATURE_ID>] <PACKAGE | UID> [<OP>]");
pw.println(" Return the mode for a particular application and optional operation.");
pw.println(" query-op [--user <USER_ID>] <OP> [<MODE>]");
pw.println(" Print all packages that currently have the given op in the given mode.");
@@ -3631,21 +3857,45 @@ public class AppOpsService extends IAppOpsService.Stub {
pw.print(AppOpsManager.opToName(ent.getOp()));
pw.print(": ");
pw.print(AppOpsManager.modeToName(ent.getMode()));
- if (ent.getTime() != 0) {
- pw.print("; time=");
- TimeUtils.formatDuration(now - ent.getTime(), pw);
- pw.print(" ago");
- }
- if (ent.getRejectTime() != 0) {
- pw.print("; rejectTime=");
- TimeUtils.formatDuration(now - ent.getRejectTime(), pw);
- pw.print(" ago");
- }
- if (ent.getDuration() == -1) {
- pw.print(" (running)");
- } else if (ent.getDuration() != 0) {
- pw.print("; duration=");
- TimeUtils.formatDuration(ent.getDuration(), pw);
+ if (shell.featureId == null) {
+ if (ent.getTime() != 0) {
+ pw.print("; time=");
+ TimeUtils.formatDuration(now - ent.getTime(), pw);
+ pw.print(" ago");
+ }
+ if (ent.getRejectTime() != 0) {
+ pw.print("; rejectTime=");
+ TimeUtils.formatDuration(now - ent.getRejectTime(), pw);
+ pw.print(" ago");
+ }
+ if (ent.getDuration() == -1) {
+ pw.print(" (running)");
+ } else if (ent.getDuration() != 0) {
+ pw.print("; duration=");
+ TimeUtils.formatDuration(ent.getDuration(), pw);
+ }
+ } else {
+ final OpFeatureEntry featureEnt = ent.getFeatures().get(
+ shell.featureId);
+ if (featureEnt != null) {
+ if (featureEnt.getTime() != 0) {
+ pw.print("; time=");
+ TimeUtils.formatDuration(now - featureEnt.getTime(), pw);
+ pw.print(" ago");
+ }
+ if (featureEnt.getRejectTime() != 0) {
+ pw.print("; rejectTime=");
+ TimeUtils.formatDuration(now - featureEnt.getRejectTime(),
+ pw);
+ pw.print(" ago");
+ }
+ if (featureEnt.getDuration() == -1) {
+ pw.print(" (running)");
+ } else if (featureEnt.getDuration() != 0) {
+ pw.print("; duration=");
+ TimeUtils.formatDuration(featureEnt.getDuration(), pw);
+ }
+ }
}
pw.println();
}
@@ -3750,8 +4000,8 @@ public class AppOpsService extends IAppOpsService.Stub {
}
if (shell.packageName != null) {
- shell.mInterface.startOperation(shell.mToken,
- shell.op, shell.packageUid, shell.packageName, true);
+ shell.mInterface.startOperation(shell.mToken, shell.op, shell.packageUid,
+ shell.packageName, shell.featureId, true);
} else {
return -1;
}
@@ -3765,7 +4015,7 @@ public class AppOpsService extends IAppOpsService.Stub {
if (shell.packageName != null) {
shell.mInterface.finishOperation(shell.mToken,
- shell.op, shell.packageUid, shell.packageName);
+ shell.op, shell.packageUid, shell.packageName, shell.featureId);
} else {
return -1;
}
@@ -3796,11 +4046,23 @@ public class AppOpsService extends IAppOpsService.Stub {
pw.println(" Output the historical data.");
}
- private void dumpStatesLocked(@NonNull PrintWriter pw, @NonNull Op op,
+ private void dumpStatesLocked(@NonNull PrintWriter pw, long nowElapsed, @NonNull Op op,
long now, @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix) {
+ final int numFeatures = op.mFeatures.size();
+ for (int i = 0; i < numFeatures; i++) {
+ pw.print(prefix + op.mFeatures.keyAt(i) + "=[\n");
+ dumpStatesLocked(pw, nowElapsed, op, op.mFeatures.keyAt(i), now, sdf, date,
+ prefix + " ");
+ pw.print(prefix + "]\n");
+ }
+ }
+
+ private void dumpStatesLocked(@NonNull PrintWriter pw, long nowElapsed, @NonNull Op op,
+ @Nullable String featureId, long now, @NonNull SimpleDateFormat sdf,
+ @NonNull Date date, @NonNull String prefix) {
- final OpEntry entry = new OpEntry(op.op, op.running, op.mode, op.mAccessTimes,
- op.mRejectTimes, op.mDurations, op.mProxyUids, op.mProxyPackageNames);
+ final OpFeatureEntry entry = op.createSingleFeatureEntryLocked(
+ featureId).getFeatures().get(featureId);
final LongSparseArray keys = entry.collectKeys();
if (keys == null || keys.size() <= 0) {
@@ -3821,6 +4083,7 @@ public class AppOpsService extends IAppOpsService.Stub {
final long accessDuration = entry.getLastDuration(
uidState, uidState, flags);
final String proxyPkg = entry.getProxyPackageName(uidState, flags);
+ final String proxyFeatureId = entry.getProxyFeatureId(uidState, flags);
final int proxyUid = entry.getProxyUid(uidState, flags);
if (accessTime > 0) {
@@ -3843,6 +4106,8 @@ public class AppOpsService extends IAppOpsService.Stub {
pw.print(proxyUid);
pw.print(", pkg=");
pw.print(proxyPkg);
+ pw.print(", feature=");
+ pw.print(proxyFeatureId);
pw.print("]");
}
pw.println();
@@ -3863,11 +4128,24 @@ public class AppOpsService extends IAppOpsService.Stub {
pw.print(proxyUid);
pw.print(", pkg=");
pw.print(proxyPkg);
+ pw.print(", feature=");
+ pw.print(proxyFeatureId);
pw.print("]");
}
pw.println();
}
}
+
+ final FeatureOp featureOp = op.mFeatures.get(featureId);
+ if (featureOp.running) {
+ pw.print(prefix + "Running start at: ");
+ TimeUtils.formatDuration(nowElapsed - featureOp.startRealtime, pw);
+ pw.println();
+ }
+ if (featureOp.startNesting != 0) {
+ pw.print(prefix + "startNesting=");
+ pw.println(featureOp.startNesting);
+ }
}
@Override
@@ -4123,7 +4401,8 @@ public class AppOpsService extends IAppOpsService.Stub {
if (cs.mStartedOps.size() > 0) {
boolean printedStarted = false;
for (int j=0; j<cs.mStartedOps.size(); j++) {
- Op op = cs.mStartedOps.get(j);
+ final Pair<Op, String> startedOp = cs.mStartedOps.get(j);
+ final Op op = startedOp.first;
if (dumpOp >= 0 && op.op != dumpOp) {
continue;
}
@@ -4145,6 +4424,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
pw.print(" "); pw.print("uid="); pw.print(op.uidState.uid);
pw.print(" pkg="); pw.print(op.packageName);
+ pw.print(" featureId="); pw.print(startedOp.second);
pw.print(" op="); pw.println(AppOpsManager.opToName(op.op));
}
}
@@ -4159,22 +4439,21 @@ public class AppOpsService extends IAppOpsService.Stub {
}
for (int i=0; i<mUidStates.size(); i++) {
UidState uidState = mUidStates.valueAt(i);
+ final SparseIntArray opModes = uidState.opModes;
final ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
if (dumpWatchers || dumpHistory) {
continue;
}
if (dumpOp >= 0 || dumpPackage != null || dumpMode >= 0) {
- boolean hasOp = dumpOp < 0 || uidState.hasOpMode(dumpOp);
+ boolean hasOp = dumpOp < 0 || (uidState.opModes != null
+ && uidState.opModes.indexOfKey(dumpOp) >= 0);
boolean hasPackage = dumpPackage == null;
boolean hasMode = dumpMode < 0;
- if (!hasMode) {
- int opModeCount = uidState.getOpModeCount();
- for (int opModeIndex = 0; opModeIndex < opModeCount; opModeIndex++) {
- int code = uidState.getOpCodeAt(opModeIndex);
- if (uidState.getOpMode(code) == dumpMode) {
+ if (!hasMode && opModes != null) {
+ for (int opi = 0; !hasMode && opi < opModes.size(); opi++) {
+ if (opModes.valueAt(opi) == dumpMode) {
hasMode = true;
- break;
}
}
}
@@ -4241,18 +4520,20 @@ public class AppOpsService extends IAppOpsService.Stub {
}
needSep = true;
- final int opModeCount = uidState.getOpModeCount();
- for (int opModeIndex = 0; opModeIndex < opModeCount; opModeIndex++) {
- final int code = uidState.getOpCodeAt(opModeIndex);
- final int mode = uidState.getOpMode(code);
- if (dumpOp >= 0 && dumpOp != code) {
- continue;
- }
- if (dumpMode >= 0 && dumpMode != mode) {
- continue;
+ if (opModes != null) {
+ final int opModeCount = opModes.size();
+ for (int j = 0; j < opModeCount; j++) {
+ final int code = opModes.keyAt(j);
+ final int mode = opModes.valueAt(j);
+ if (dumpOp >= 0 && dumpOp != code) {
+ continue;
+ }
+ if (dumpMode >= 0 && dumpMode != mode) {
+ continue;
+ }
+ pw.print(" "); pw.print(AppOpsManager.opToName(code));
+ pw.print(": mode="); pw.println(AppOpsManager.modeToName(mode));
}
- pw.print(" "); pw.print(AppOpsManager.opToName(code));
- pw.print(": mode="); pw.println(AppOpsManager.modeToName(mode));
}
if (pkgOps == null) {
@@ -4290,16 +4571,7 @@ public class AppOpsService extends IAppOpsService.Stub {
pw.print("="); pw.print(AppOpsManager.modeToName(mode));
}
pw.println("): ");
- dumpStatesLocked(pw, op, now, sdf, date, " ");
- if (op.running) {
- pw.print(" Running start at: ");
- TimeUtils.formatDuration(nowElapsed-op.startRealtime, pw);
- pw.println();
- }
- if (op.startNesting != 0) {
- pw.print(" startNesting=");
- pw.println(op.startNesting);
- }
+ dumpStatesLocked(pw, nowElapsed, op, now, sdf, date, " ");
}
}
}
@@ -4502,12 +4774,15 @@ public class AppOpsService extends IAppOpsService.Stub {
if (resolvedPackageName == null) {
return false;
}
+ // TODO moltmann: Allow to check for feature op activeness
synchronized (AppOpsService.this) {
for (int i = mClients.size() - 1; i >= 0; i--) {
final ClientState client = mClients.valueAt(i);
for (int j = client.mStartedOps.size() - 1; j >= 0; j--) {
- final Op op = client.mStartedOps.get(j);
- if (op.op == code && op.uidState.uid == uid) return true;
+ final Pair<Op, String> startedOp = client.mStartedOps.get(j);
+ if (startedOp.first.op == code && startedOp.first.uidState.uid == uid) {
+ return true;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/appop/TEST_MAPPING b/services/core/java/com/android/server/appop/TEST_MAPPING
index 1a5dac503463..e9d2b312e01c 100644
--- a/services/core/java/com/android/server/appop/TEST_MAPPING
+++ b/services/core/java/com/android/server/appop/TEST_MAPPING
@@ -18,6 +18,23 @@
"include-filter": "com.android.server.appop"
}
]
+ },
+ {
+ "name": "CtsPermissionTestCases",
+ "options": [
+ {
+ "include-filter": "android.permission.cts.BackgroundPermissionsTest"
+ },
+ {
+ "include-filter": "android.permission.cts.SplitPermissionTest"
+ },
+ {
+ "include-filter": "android.permission.cts.PermissionFlagsTest"
+ },
+ {
+ "include-filter": "android.permission.cts.SharedUidPermissionsTest"
+ }
+ ]
}
]
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 4bc9394276b8..f3717bb58900 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -348,6 +348,7 @@ import java.io.PrintWriter;
if (AudioService.DEBUG_SCO) {
Log.i(TAG, "setBluetoothScoOn: " + on + " " + eventSource);
}
+ //Log.i(TAG, "setBluetoothScoOnInt: " + on + " " + eventSource);
synchronized (mDeviceStateLock) {
if (on) {
// do not accept SCO ON if SCO audio is not connected
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 54bfcccbc6e8..13b8856e471e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -59,6 +59,7 @@ import android.hardware.hdmi.HdmiAudioSystemClient;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPlaybackClient;
import android.hardware.hdmi.HdmiTvClient;
+import android.hardware.input.InputManager;
import android.hardware.usb.UsbManager;
import android.media.AudioAttributes;
import android.media.AudioFocusInfo;
@@ -549,6 +550,10 @@ public class AudioService extends IAudioService.Stub
private String mEnabledSurroundFormats;
private boolean mSurroundModeChanged;
+ private boolean mMicMuteFromSwitch;
+ private boolean mMicMuteFromApi;
+ private boolean mMicMuteFromRestrictions;
+
@GuardedBy("mSettingsLock")
private int mAssistantUid;
@@ -886,6 +891,8 @@ public class AudioService extends IAudioService.Stub
mRoleObserver.register();
onIndicateSystemReady();
+
+ setMicMuteFromSwitchInput();
}
RoleObserver mRoleObserver;
@@ -1015,7 +1022,14 @@ public class AudioService extends IAudioService.Stub
synchronized (mAudioPolicies) {
for (AudioPolicyProxy policy : mAudioPolicies.values()) {
- policy.connectMixes();
+ final int status = policy.connectMixes();
+ if (status != AudioSystem.SUCCESS) {
+ // note that PERMISSION_DENIED may also indicate trouble getting to APService
+ Log.e(TAG, "onAudioServerDied: error "
+ + AudioSystem.audioSystemErrorToString(status)
+ + " when connecting mixes for policy " + policy.toLogFriendlyString());
+ policy.release();
+ }
}
}
@@ -1025,6 +1039,8 @@ public class AudioService extends IAudioService.Stub
sendMsg(mAudioHandler, MSG_DISPATCH_AUDIO_SERVER_STATE,
SENDMSG_QUEUE, 1, 0, null, 0);
+
+ setMicMuteFromSwitchInput();
}
private void onDispatchAudioServerStateChange(boolean state) {
@@ -1561,12 +1577,13 @@ public class AudioService extends IAudioService.Stub
AudioSystem.setMasterMute(masterMute);
broadcastMasterMuteStatus(masterMute);
- boolean microphoneMute = mUserManagerInternal.getUserRestriction(
+ mMicMuteFromRestrictions = mUserManagerInternal.getUserRestriction(
currentUser, UserManager.DISALLOW_UNMUTE_MICROPHONE);
if (DEBUG_VOL) {
- Log.d(TAG, String.format("Mic mute %s, user=%d", microphoneMute, currentUser));
+ Log.d(TAG, String.format("Mic mute %b, user=%d", mMicMuteFromRestrictions,
+ currentUser));
}
- AudioSystem.muteMicrophone(microphoneMute);
+ setMicrophoneMuteNoCallerCheck(currentUser);
}
private int rescaleIndex(int index, int srcStream, int dstStream) {
@@ -2861,20 +2878,45 @@ public class AudioService extends IAudioService.Stub
!= PackageManager.PERMISSION_GRANTED) {
return;
}
- setMicrophoneMuteNoCallerCheck(on, userId);
+ mMicMuteFromApi = on;
+ setMicrophoneMuteNoCallerCheck(userId);
+ }
+
+ /** @see AudioManager#setMicrophoneMuteFromSwitch(boolean) */
+ public void setMicrophoneMuteFromSwitch(boolean on) {
+ int userId = Binder.getCallingUid();
+ if (userId != android.os.Process.SYSTEM_UID) {
+ Log.e(TAG, "setMicrophoneMuteFromSwitch() called from non system user!");
+ return;
+ }
+ mMicMuteFromSwitch = on;
+ setMicrophoneMuteNoCallerCheck(userId);
+ }
+
+ private void setMicMuteFromSwitchInput() {
+ InputManager im = mContext.getSystemService(InputManager.class);
+ final int isMicMuted = im.isMicMuted();
+ if (isMicMuted != InputManager.SWITCH_STATE_UNKNOWN) {
+ setMicrophoneMuteFromSwitch(im.isMicMuted() != InputManager.SWITCH_STATE_OFF);
+ }
}
- private void setMicrophoneMuteNoCallerCheck(boolean on, int userId) {
+ public boolean isMicrophoneMuted() {
+ return mMicMuteFromSwitch || mMicMuteFromRestrictions || mMicMuteFromApi;
+ }
+
+ private void setMicrophoneMuteNoCallerCheck(int userId) {
+ final boolean muted = isMicrophoneMuted();
if (DEBUG_VOL) {
- Log.d(TAG, String.format("Mic mute %s, user=%d", on, userId));
+ Log.d(TAG, String.format("Mic mute %b, user=%d", muted, userId));
}
// only mute for the current user
- if (getCurrentUserId() == userId) {
+ if (getCurrentUserId() == userId || userId == android.os.Process.SYSTEM_UID) {
final boolean currentMute = AudioSystem.isMicrophoneMuted();
final long identity = Binder.clearCallingIdentity();
- AudioSystem.muteMicrophone(on);
+ AudioSystem.muteMicrophone(muted);
Binder.restoreCallingIdentity(identity);
- if (on != currentMute) {
+ if (muted != currentMute) {
mContext.sendBroadcastAsUser(
new Intent(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED)
.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL);
@@ -3269,7 +3311,6 @@ public class AudioService extends IAudioService.Stub
// when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
// SCO connections not started by the application changing the mode when pid changes
if ((newModeOwnerPid != oldModeOwnerPid) && (newModeOwnerPid != 0)) {
- Log.i(TAG, "In setMode(), calling disconnectBluetoothSco()");
mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid);
}
}
@@ -3898,7 +3939,7 @@ public class AudioService extends IAudioService.Stub
final boolean muteSystem = (zenPolicy.priorityCategories
& NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM) == 0;
final boolean muteNotificationAndRing = ZenModeConfig
- .areAllPriorityOnlyNotificationZenSoundsMuted(
+ .areAllPriorityOnlyRingerSoundsMuted(
mNm.getConsolidatedNotificationPolicy());
return muteAlarms && isAlarm(streamType)
|| muteMedia && isMedia(streamType)
@@ -3911,16 +3952,26 @@ public class AudioService extends IAudioService.Stub
}
/**
- * DND total silence: media and alarms streams are tied to the muted ringer
+ * Notifications, ringer and system sounds are controlled by the ringer:
* {@link ZenModeHelper.RingerModeDelegate#getRingerModeAffectedStreams(int)}
- * DND alarms only: notification, ringer + system muted (by default tied to muted ringer mode)
- * DND priority only: alarms, media, system streams can be muted separate from ringer based on
+ * DND total silence: media and alarms streams can be muted by DND
+ * DND alarms only: no streams additionally controlled by DND
+ * DND priority only: alarms, media, system streams can be muted by DND based on
* zenPolicy (this method determines which streams)
* @return true if changed, else false
*/
private boolean updateZenModeAffectedStreams() {
+ if (!mSystemReady) {
+ return false;
+ }
+
int zenModeAffectedStreams = 0;
- if (mSystemReady && mNm.getZenMode() == Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
+ final int zenMode = mNm.getZenMode();
+
+ if (zenMode == Settings.Global.ZEN_MODE_NO_INTERRUPTIONS) {
+ zenModeAffectedStreams |= 1 << AudioManager.STREAM_ALARM;
+ zenModeAffectedStreams |= 1 << AudioManager.STREAM_MUSIC;
+ } else if (zenMode == Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
NotificationManager.Policy zenPolicy = mNm.getConsolidatedNotificationPolicy();
if ((zenPolicy.priorityCategories
& NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS) == 0) {
@@ -3932,6 +3983,8 @@ public class AudioService extends IAudioService.Stub
zenModeAffectedStreams |= 1 << AudioManager.STREAM_MUSIC;
}
+ // even if zen isn't muting the system stream, the ringer mode can still mute
+ // the system stream
if ((zenPolicy.priorityCategories
& NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM) == 0) {
zenModeAffectedStreams |= 1 << AudioManager.STREAM_SYSTEM;
@@ -5469,7 +5522,8 @@ public class AudioService extends IAudioService.Stub
final boolean isRestricted =
newRestrictions.getBoolean(UserManager.DISALLOW_UNMUTE_MICROPHONE);
if (wasRestricted != isRestricted) {
- setMicrophoneMuteNoCallerCheck(isRestricted, userId);
+ mMicMuteFromRestrictions = isRestricted;
+ setMicrophoneMuteNoCallerCheck(userId);
}
}
@@ -7062,16 +7116,8 @@ public class AudioService extends IAudioService.Stub
}
public void binderDied() {
- synchronized (mAudioPolicies) {
- Log.i(TAG, "audio policy " + mPolicyCallback + " died");
- release();
- mAudioPolicies.remove(mPolicyCallback.asBinder());
- }
- if (mIsVolumeController) {
- synchronized (mExtVolumeControllerLock) {
- mExtVolumeController = null;
- }
- }
+ Log.i(TAG, "audio policy " + mPolicyCallback + " died");
+ release();
}
String getRegistrationId() {
@@ -7095,9 +7141,20 @@ public class AudioService extends IAudioService.Stub
Log.e(TAG, "Fail to unregister Audiopolicy callback from MediaProjection");
}
}
+ if (mIsVolumeController) {
+ synchronized (mExtVolumeControllerLock) {
+ mExtVolumeController = null;
+ }
+ }
final long identity = Binder.clearCallingIdentity();
AudioSystem.registerPolicyMixes(mMixes, false);
Binder.restoreCallingIdentity(identity);
+ synchronized (mAudioPolicies) {
+ mAudioPolicies.remove(mPolicyCallback.asBinder());
+ }
+ try {
+ mPolicyCallback.notifyUnregistration();
+ } catch (RemoteException e) { }
}
boolean hasMixAffectingUsage(int usage, int excludedFlags) {
@@ -7148,7 +7205,7 @@ public class AudioService extends IAudioService.Stub
}
}
- int connectMixes() {
+ @AudioSystem.AudioSystemError int connectMixes() {
final long identity = Binder.clearCallingIdentity();
int status = AudioSystem.registerPolicyMixes(mMixes, true);
Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 09e2925affca..71221a75c90b 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -265,7 +265,7 @@ public class BtHelper {
return mapBluetoothCodecToAudioFormat(btCodecConfig.getCodecType());
}
- //SCO device tracking for TWSPLUS device
+ //SCO device tracking for TWSPLUS device
private HashMap<BluetoothDevice, Integer> mScoClientDevices =
new HashMap<BluetoothDevice, Integer>();
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 7302b985181b..44c81fc09b2a 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -25,28 +25,24 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_IRIS;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
import android.app.ActivityManager;
-import android.app.AppOpsManager;
import android.app.IActivityManager;
-import android.app.KeyguardManager;
import android.app.UserSwitchObserver;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
+import android.hardware.biometrics.Authenticator;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.BiometricsProtoEnums;
-import android.hardware.biometrics.IBiometricConfirmDeviceCredentialCallback;
+import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricServiceReceiverInternal;
-import android.hardware.face.FaceManager;
import android.hardware.face.IFaceService;
-import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintService;
import android.net.Uri;
import android.os.Binder;
@@ -71,6 +67,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.internal.statusbar.IStatusBarService;
import com.android.server.SystemService;
+import com.android.server.biometrics.face.FaceAuthenticator;
+import com.android.server.biometrics.fingerprint.FingerprintAuthenticator;
import java.util.ArrayList;
import java.util.HashMap;
@@ -96,16 +94,8 @@ public class BiometricService extends SystemService {
private static final int MSG_ON_READY_FOR_AUTHENTICATION = 8;
private static final int MSG_AUTHENTICATE = 9;
private static final int MSG_CANCEL_AUTHENTICATION = 10;
- private static final int MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS = 11;
- private static final int MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR = 12;
- private static final int MSG_REGISTER_CANCELLATION_CALLBACK = 13;
- private static final int MSG_ON_AUTHENTICATION_TIMED_OUT = 14;
-
- private static final int[] FEATURE_ID = {
- TYPE_FINGERPRINT,
- TYPE_IRIS,
- TYPE_FACE
- };
+ private static final int MSG_ON_AUTHENTICATION_TIMED_OUT = 11;
+ private static final int MSG_ON_DEVICE_CREDENTIAL_PRESSED = 12;
/**
* Authentication either just called and we have not transitioned to the CALLED state, or
@@ -133,19 +123,19 @@ public class BiometricService extends SystemService {
*/
static final int STATE_AUTH_PENDING_CONFIRM = 5;
/**
- * Biometric authentication was canceled, but the device is now showing ConfirmDeviceCredential
- */
- static final int STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC = 6;
- /**
* Biometric authenticated, waiting for SysUI to finish animation
*/
- static final int STATE_AUTHENTICATED_PENDING_SYSUI = 7;
+ static final int STATE_AUTHENTICATED_PENDING_SYSUI = 6;
/**
* Biometric error, waiting for SysUI to finish animation
*/
- static final int STATE_ERROR_PENDING_SYSUI = 8;
+ static final int STATE_ERROR_PENDING_SYSUI = 7;
+ /**
+ * Device credential in AuthController is showing
+ */
+ static final int STATE_SHOWING_DEVICE_CREDENTIAL = 8;
- final class AuthSession implements IBinder.DeathRecipient {
+ final class AuthSession {
// Map of Authenticator/Cookie pairs. We expect to receive the cookies back from
// <Biometric>Services before we can start authenticating. Pairs that have been returned
// are moved to mModalitiesMatched.
@@ -178,21 +168,17 @@ public class BiometricService extends SystemService {
byte[] mTokenEscrow;
// Waiting for SystemUI to complete animation
int mErrorEscrow;
- String mErrorStringEscrow;
+ int mVendorCodeEscrow;
// Timestamp when authentication started
private long mStartTimeMs;
// Timestamp when hardware authentication occurred
private long mAuthenticatedTimeMs;
- // TODO(b/123378871): Remove when moved.
- private IBiometricConfirmDeviceCredentialCallback mConfirmDeviceCredentialCallback;
-
AuthSession(HashMap<Integer, Integer> modalities, IBinder token, long sessionId,
int userId, IBiometricServiceReceiver receiver, String opPackageName,
Bundle bundle, int callingUid, int callingPid, int callingUserId,
- int modality, boolean requireConfirmation,
- IBiometricConfirmDeviceCredentialCallback callback) {
+ int modality, boolean requireConfirmation) {
mModalitiesWaiting = modalities;
mToken = token;
mSessionId = sessionId;
@@ -205,25 +191,12 @@ public class BiometricService extends SystemService {
mCallingUserId = callingUserId;
mModality = modality;
mRequireConfirmation = requireConfirmation;
- mConfirmDeviceCredentialCallback = callback;
-
- if (isFromConfirmDeviceCredential()) {
- try {
- token.linkToDeath(this, 0 /* flags */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to link to death", e);
- }
- }
}
boolean isCrypto() {
return mSessionId != 0;
}
- boolean isFromConfirmDeviceCredential() {
- return mBundle.getBoolean(BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, false);
- }
-
boolean containsCookie(int cookie) {
if (mModalitiesWaiting != null && mModalitiesWaiting.containsValue(cookie)) {
return true;
@@ -234,50 +207,30 @@ public class BiometricService extends SystemService {
return false;
}
- // TODO(b/123378871): Remove when moved.
- @Override
- public void binderDied() {
- mHandler.post(() -> {
- Slog.e(TAG, "Binder died, killing ConfirmDeviceCredential");
- if (mConfirmDeviceCredentialCallback == null) {
- Slog.e(TAG, "Callback is null");
- return;
- }
-
- try {
- mConfirmDeviceCredentialCallback.cancel();
- mConfirmDeviceCredentialCallback = null;
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to send cancel", e);
- }
- });
+ boolean isAllowDeviceCredential() {
+ return Utils.isDeviceCredentialAllowed(mBundle);
}
}
private final Injector mInjector;
@VisibleForTesting
final IBiometricService.Stub mImpl;
- private final AppOpsManager mAppOps;
+ private final boolean mHasFeatureFace;
private final boolean mHasFeatureFingerprint;
private final boolean mHasFeatureIris;
- private final boolean mHasFeatureFace;
@VisibleForTesting
- SettingObserver mSettingObserver;
+ final SettingObserver mSettingObserver;
private final List<EnabledOnKeyguardCallback> mEnabledOnKeyguardCallbacks;
private final Random mRandom = new Random();
@VisibleForTesting
- IFingerprintService mFingerprintService;
- @VisibleForTesting
- IFaceService mFaceService;
- @VisibleForTesting
IStatusBarService mStatusBarService;
@VisibleForTesting
KeyStore mKeyStore;
// Get and cache the available authenticator (manager) classes. Used since aidl doesn't support
// polymorphism :/
- final ArrayList<Authenticator> mAuthenticators = new ArrayList<>();
+ final ArrayList<AuthenticatorWrapper> mAuthenticators = new ArrayList<>();
// The current authentication session, null if idle/done. We need to track both the current
// and pending sessions since errors may be sent to either.
@@ -286,14 +239,6 @@ public class BiometricService extends SystemService {
@VisibleForTesting
AuthSession mPendingAuthSession;
- // TODO(b/123378871): Remove when moved.
- // When BiometricPrompt#setAllowDeviceCredentials is set to true, we need to store the
- // client (app) receiver. BiometricService internally launches CDCA which invokes
- // BiometricService to start authentication (normal path). When auth is success/rejected,
- // CDCA will use an aidl method to poke BiometricService - the result will then be forwarded
- // to this receiver.
- private IBiometricServiceReceiver mConfirmDeviceCredentialReceiver;
-
@VisibleForTesting
final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
@@ -309,7 +254,7 @@ public class BiometricService extends SystemService {
}
case MSG_ON_AUTHENTICATION_REJECTED: {
- handleAuthenticationRejected((String) msg.obj /* failureReason */);
+ handleAuthenticationRejected();
break;
}
@@ -317,8 +262,9 @@ public class BiometricService extends SystemService {
SomeArgs args = (SomeArgs) msg.obj;
handleOnError(
args.argi1 /* cookie */,
- args.argi2 /* error */,
- (String) args.arg1 /* message */);
+ args.argi2 /* modality */,
+ args.argi3 /* error */,
+ args.argi4 /* vendorCode */);
args.recycle();
break;
}
@@ -363,8 +309,7 @@ public class BiometricService extends SystemService {
(Bundle) args.arg5 /* bundle */,
args.argi2 /* callingUid */,
args.argi3 /* callingPid */,
- args.argi4 /* callingUserId */,
- (IBiometricConfirmDeviceCredentialCallback) args.arg6 /* callback */);
+ args.argi4 /* callingUserId */);
args.recycle();
break;
}
@@ -378,28 +323,18 @@ public class BiometricService extends SystemService {
break;
}
- case MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS: {
- handleOnConfirmDeviceCredentialSuccess();
- break;
- }
-
- case MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR: {
+ case MSG_ON_AUTHENTICATION_TIMED_OUT: {
SomeArgs args = (SomeArgs) msg.obj;
- handleOnConfirmDeviceCredentialError(
- args.argi1 /* error */,
- (String) args.arg1 /* errorMsg */);
+ handleAuthenticationTimedOut(
+ args.argi1 /* modality */,
+ args.argi2 /* error */,
+ args.argi3 /* vendorCode */);
args.recycle();
break;
}
- case MSG_REGISTER_CANCELLATION_CALLBACK: {
- handleRegisterCancellationCallback(
- (IBiometricConfirmDeviceCredentialCallback) msg.obj /* callback */);
- break;
- }
-
- case MSG_ON_AUTHENTICATION_TIMED_OUT: {
- handleAuthenticationTimedOut((String) msg.obj /* errorMessage */);
+ case MSG_ON_DEVICE_CREDENTIAL_PRESSED: {
+ handleOnDeviceCredentialPressed();
break;
}
@@ -410,21 +345,23 @@ public class BiometricService extends SystemService {
}
};
- private final class Authenticator {
- int mType;
- BiometricAuthenticator mAuthenticator;
-
- Authenticator(int type, BiometricAuthenticator authenticator) {
- mType = type;
- mAuthenticator = authenticator;
- }
-
- int getType() {
- return mType;
- }
-
- BiometricAuthenticator getAuthenticator() {
- return mAuthenticator;
+ /**
+ * Wraps IBiometricAuthenticator implementation and stores information about the authenticator.
+ * TODO(b/141025588): Consider refactoring the tests to not rely on this implementation detail.
+ */
+ @VisibleForTesting
+ public static final class AuthenticatorWrapper {
+ public final int id;
+ public final int strength;
+ public final int modality;
+ public final IBiometricAuthenticator impl;
+
+ AuthenticatorWrapper(int id, int strength, int modality,
+ IBiometricAuthenticator impl) {
+ this.id = id;
+ this.strength = strength;
+ this.modality = modality;
+ this.impl = impl;
}
}
@@ -445,9 +382,9 @@ public class BiometricService extends SystemService {
private final ContentResolver mContentResolver;
private final List<BiometricService.EnabledOnKeyguardCallback> mCallbacks;
- private Map<Integer, Boolean> mFaceEnabledOnKeyguard = new HashMap<>();
- private Map<Integer, Boolean> mFaceEnabledForApps = new HashMap<>();
- private Map<Integer, Boolean> mFaceAlwaysRequireConfirmation = new HashMap<>();
+ private final Map<Integer, Boolean> mFaceEnabledOnKeyguard = new HashMap<>();
+ private final Map<Integer, Boolean> mFaceEnabledForApps = new HashMap<>();
+ private final Map<Integer, Boolean> mFaceAlwaysRequireConfirmation = new HashMap<>();
/**
* Creates a content observer.
@@ -584,23 +521,28 @@ public class BiometricService extends SystemService {
@Override
public void onAuthenticationFailed()
throws RemoteException {
- String failureReason = getContext().getString(R.string.biometric_not_recognized);
- Slog.v(TAG, "onAuthenticationFailed: " + failureReason);
- mHandler.obtainMessage(MSG_ON_AUTHENTICATION_REJECTED, failureReason).sendToTarget();
+ Slog.v(TAG, "onAuthenticationFailed");
+ mHandler.obtainMessage(MSG_ON_AUTHENTICATION_REJECTED).sendToTarget();
}
@Override
- public void onError(int cookie, int error, String message) throws RemoteException {
+ public void onError(int cookie, int modality, int error, int vendorCode)
+ throws RemoteException {
// Determine if error is hard or soft error. Certain errors (such as TIMEOUT) are
// soft errors and we should allow the user to try authenticating again instead of
// dismissing BiometricPrompt.
if (error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT) {
- mHandler.obtainMessage(MSG_ON_AUTHENTICATION_TIMED_OUT, message).sendToTarget();
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = modality;
+ args.argi2 = error;
+ args.argi3 = vendorCode;
+ mHandler.obtainMessage(MSG_ON_AUTHENTICATION_TIMED_OUT, args).sendToTarget();
} else {
SomeArgs args = SomeArgs.obtain();
args.argi1 = cookie;
- args.argi2 = error;
- args.arg1 = message;
+ args.argi2 = modality;
+ args.argi3 = error;
+ args.argi4 = vendorCode;
mHandler.obtainMessage(MSG_ON_ERROR, args).sendToTarget();
}
}
@@ -622,6 +564,11 @@ public class BiometricService extends SystemService {
public void onTryAgainPressed() {
mHandler.sendEmptyMessage(MSG_ON_TRY_AGAIN_PRESSED);
}
+
+ @Override
+ public void onDeviceCredentialPressed() {
+ mHandler.sendEmptyMessage(MSG_ON_DEVICE_CREDENTIAL_PRESSED);
+ }
};
@@ -644,18 +591,12 @@ public class BiometricService extends SystemService {
@Override // Binder call
public void authenticate(IBinder token, long sessionId, int userId,
- IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
- IBiometricConfirmDeviceCredentialCallback callback)
+ IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle)
throws RemoteException {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
- // TODO(b/123378871): Remove when moved.
- if (callback != null) {
- checkInternalPermission();
- }
-
// In the BiometricServiceBase, check do the AppOps and foreground check.
if (userId == callingUserId) {
// Check the USE_BIOMETRIC permission here.
@@ -672,12 +613,12 @@ public class BiometricService extends SystemService {
return;
}
- final boolean isFromConfirmDeviceCredential =
- bundle.getBoolean(BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, false);
- if (isFromConfirmDeviceCredential) {
+ if (bundle.get(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED) != null) {
checkInternalPermission();
}
+ Utils.combineAuthenticatorBundles(bundle);
+
// Check the usage of this in system server. Need to remove this check if it becomes
// a public API.
final boolean useDefaultTitle =
@@ -691,39 +632,6 @@ public class BiometricService extends SystemService {
}
}
- // Launch CDC instead if necessary. CDC will return results through an AIDL call, since
- // we can't get activity results. Store the receiver somewhere so we can forward the
- // result back to the client.
- // TODO(b/123378871): Remove when moved.
- if (bundle.getBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL)) {
- mHandler.post(() -> {
- final KeyguardManager kgm = getContext().getSystemService(
- KeyguardManager.class);
- if (!kgm.isDeviceSecure()) {
- try {
- receiver.onError(
- BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL,
- getContext().getString(
- R.string.biometric_error_device_not_secured));
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- }
- return;
- }
- mConfirmDeviceCredentialReceiver = receiver;
- // Use this so we don't need to duplicate logic..
- final Intent intent = kgm.createConfirmDeviceCredentialIntent(null /* title */,
- null /* description */, userId);
- // Then give it the bundle to do magic behavior..
- intent.putExtra(KeyguardManager.EXTRA_BIOMETRIC_PROMPT_BUNDLE, bundle);
- // Create a new task with this activity located at the root.
- intent.setFlags(
- Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
- getContext().startActivityAsUser(intent, UserHandle.CURRENT);
- });
- return;
- }
-
SomeArgs args = SomeArgs.obtain();
args.arg1 = token;
args.arg2 = sessionId;
@@ -734,41 +642,11 @@ public class BiometricService extends SystemService {
args.argi2 = callingUid;
args.argi3 = callingPid;
args.argi4 = callingUserId;
- args.arg6 = callback;
mHandler.obtainMessage(MSG_AUTHENTICATE, args).sendToTarget();
}
@Override // Binder call
- public void onConfirmDeviceCredentialSuccess() {
- checkInternalPermission();
-
- mHandler.sendEmptyMessage(MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS);
- }
-
- @Override // Binder call
- public void onConfirmDeviceCredentialError(int error, String message) {
- checkInternalPermission();
-
- SomeArgs args = SomeArgs.obtain();
- args.argi1 = error;
- args.arg1 = message;
- mHandler.obtainMessage(MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR, args).sendToTarget();
- }
-
- @Override // Binder call
- public void registerCancellationCallback(
- IBiometricConfirmDeviceCredentialCallback callback) {
- // TODO(b/123378871): Remove when moved.
- // This callback replaces the one stored in the current session. If the session is null
- // we can ignore this, since it means ConfirmDeviceCredential was launched by something
- // else (not BiometricPrompt)
- checkInternalPermission();
-
- mHandler.obtainMessage(MSG_REGISTER_CANCELLATION_CALLBACK, callback).sendToTarget();
- }
-
- @Override // Binder call
public void cancelAuthentication(IBinder token, String opPackageName)
throws RemoteException {
checkPermission();
@@ -793,7 +671,8 @@ public class BiometricService extends SystemService {
final long ident = Binder.clearCallingIdentity();
int error;
try {
- final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId);
+ final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId,
+ opPackageName);
error = result.second;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -802,22 +681,30 @@ public class BiometricService extends SystemService {
}
@Override
- public boolean hasEnrolledBiometrics(int userId) {
+ public boolean hasEnrolledBiometrics(int userId, String opPackageName) {
checkInternalPermission();
final long ident = Binder.clearCallingIdentity();
try {
- for (int i = 0; i < mAuthenticators.size(); i++) {
- if (mAuthenticators.get(i).mAuthenticator.hasEnrolledTemplates(userId)) {
+ for (AuthenticatorWrapper authenticator : mAuthenticators) {
+ if (authenticator.impl.hasEnrolledTemplates(userId, opPackageName)) {
return true;
}
}
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
} finally {
Binder.restoreCallingIdentity(ident);
}
return false;
}
+ @Override
+ public void registerAuthenticator(int id, int strength, int modality,
+ IBiometricAuthenticator authenticator) {
+ mAuthenticators.add(new AuthenticatorWrapper(id, strength, modality, authenticator));
+ }
+
@Override // Binder call
public void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback)
throws RemoteException {
@@ -837,9 +724,11 @@ public class BiometricService extends SystemService {
checkInternalPermission();
final long ident = Binder.clearCallingIdentity();
try {
- for (int i = 0; i < mAuthenticators.size(); i++) {
- mAuthenticators.get(i).getAuthenticator().setActiveUser(userId);
+ for (AuthenticatorWrapper authenticator : mAuthenticators) {
+ authenticator.impl.setActiveUser(userId);
}
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -850,11 +739,8 @@ public class BiometricService extends SystemService {
checkInternalPermission();
final long ident = Binder.clearCallingIdentity();
try {
- if (mFingerprintService != null) {
- mFingerprintService.resetTimeout(token);
- }
- if (mFaceService != null) {
- mFaceService.resetLockout(token);
+ for (AuthenticatorWrapper authenticator : mAuthenticators) {
+ authenticator.impl.resetLockout(token);
}
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
@@ -864,14 +750,6 @@ public class BiometricService extends SystemService {
}
}
- private void checkAppOp(String opPackageName, int callingUid) {
- if (mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, callingUid,
- opPackageName) != AppOpsManager.MODE_ALLOWED) {
- Slog.w(TAG, "Rejecting " + opPackageName + "; permission denied");
- throw new SecurityException("Permission denied");
- }
- }
-
private void checkInternalPermission() {
getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL,
"Must have USE_BIOMETRIC_INTERNAL permission");
@@ -885,40 +763,69 @@ public class BiometricService extends SystemService {
}
}
+ /**
+ * Class for injecting dependencies into BiometricService.
+ * TODO(b/141025588): Replace with a dependency injection framework (e.g. Guice, Dagger).
+ */
@VisibleForTesting
- static class Injector {
- IActivityManager getActivityManagerService() {
+ public static class Injector {
+
+ @VisibleForTesting
+ public IActivityManager getActivityManagerService() {
return ActivityManager.getService();
}
- IStatusBarService getStatusBarService() {
+ @VisibleForTesting
+ public IStatusBarService getStatusBarService() {
return IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
}
- IFingerprintService getFingerprintService() {
- return IFingerprintService.Stub.asInterface(
- ServiceManager.getService(Context.FINGERPRINT_SERVICE));
+ /**
+ * Allows to mock FaceAuthenticator for testing.
+ */
+ @VisibleForTesting
+ public IBiometricAuthenticator getFingerprintAuthenticator() {
+ return new FingerprintAuthenticator(IFingerprintService.Stub.asInterface(
+ ServiceManager.getService(Context.FINGERPRINT_SERVICE)));
}
- IFaceService getFaceService() {
- return IFaceService.Stub.asInterface(ServiceManager.getService(Context.FACE_SERVICE));
+ /**
+ * Allows to mock FaceAuthenticator for testing.
+ */
+ @VisibleForTesting
+ public IBiometricAuthenticator getFaceAuthenticator() {
+ return new FaceAuthenticator(
+ IFaceService.Stub.asInterface(ServiceManager.getService(Context.FACE_SERVICE)));
}
- SettingObserver getSettingObserver(Context context, Handler handler,
+ /**
+ * Allows to mock SettingObserver for testing.
+ */
+ @VisibleForTesting
+ public SettingObserver getSettingObserver(Context context, Handler handler,
List<EnabledOnKeyguardCallback> callbacks) {
return new SettingObserver(context, handler, callbacks);
}
- KeyStore getKeyStore() {
+ @VisibleForTesting
+ public KeyStore getKeyStore() {
return KeyStore.getInstance();
}
- boolean isDebugEnabled(Context context, int userId) {
+ /**
+ * Allows to enable/disable debug logs.
+ */
+ @VisibleForTesting
+ public boolean isDebugEnabled(Context context, int userId) {
return Utils.isDebugEnabled(context, userId);
}
- void publishBinderService(BiometricService service, IBiometricService.Stub impl) {
+ /**
+ * Allows to stub publishBinderService(...) for testing.
+ */
+ @VisibleForTesting
+ public void publishBinderService(BiometricService service, IBiometricService.Stub impl) {
service.publishBinderService(Context.BIOMETRIC_SERVICE, impl);
}
}
@@ -942,15 +849,14 @@ public class BiometricService extends SystemService {
mInjector = injector;
mImpl = new BiometricServiceWrapper();
- mAppOps = context.getSystemService(AppOpsManager.class);
mEnabledOnKeyguardCallbacks = new ArrayList<>();
mSettingObserver = mInjector.getSettingObserver(context, mHandler,
mEnabledOnKeyguardCallbacks);
final PackageManager pm = context.getPackageManager();
+ mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
mHasFeatureIris = pm.hasSystemFeature(PackageManager.FEATURE_IRIS);
- mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
try {
injector.getActivityManagerService().registerUserSwitchObserver(
@@ -969,26 +875,30 @@ public class BiometricService extends SystemService {
@Override
public void onStart() {
- // TODO: maybe get these on-demand
- if (mHasFeatureFingerprint) {
- mFingerprintService = mInjector.getFingerprintService();
- }
- if (mHasFeatureFace) {
- mFaceService = mInjector.getFaceService();
+ // TODO(b/141025588): remove this code block once AuthService is integrated.
+ {
+ if (mHasFeatureFace) {
+ try {
+ mImpl.registerAuthenticator(0, 0, TYPE_FACE, mInjector.getFaceAuthenticator());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+ if (mHasFeatureFingerprint) {
+ try {
+ mImpl.registerAuthenticator(0, 0, TYPE_FINGERPRINT,
+ mInjector.getFingerprintAuthenticator());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+ if (mHasFeatureIris) {
+ Slog.e(TAG, "Iris is not supported");
+ }
}
mKeyStore = mInjector.getKeyStore();
mStatusBarService = mInjector.getStatusBarService();
-
- // Cache the authenticators
- for (int featureId : FEATURE_ID) {
- if (hasFeature(featureId)) {
- Authenticator authenticator =
- new Authenticator(featureId, getAuthenticator(featureId));
- mAuthenticators.add(authenticator);
- }
- }
-
mInjector.publishBinderService(this, mImpl);
}
@@ -1004,7 +914,7 @@ public class BiometricService extends SystemService {
* {@link BiometricAuthenticator#TYPE_FACE}
* and the error containing one of the {@link BiometricConstants} errors.
*/
- private Pair<Integer, Integer> checkAndGetBiometricModality(int userId) {
+ private Pair<Integer, Integer> checkAndGetBiometricModality(int userId, String opPackageName) {
// No biometric features, send error
if (mAuthenticators.isEmpty()) {
return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
@@ -1022,23 +932,26 @@ public class BiometricService extends SystemService {
int modality = TYPE_NONE;
int firstHwAvailable = TYPE_NONE;
- for (Authenticator authenticatorWrapper : mAuthenticators) {
- modality = authenticatorWrapper.getType();
- BiometricAuthenticator authenticator = authenticatorWrapper.getAuthenticator();
- if (authenticator.isHardwareDetected()) {
- isHardwareDetected = true;
- if (firstHwAvailable == TYPE_NONE) {
- // Store the first one since we want to return the error in correct priority
- // order.
- firstHwAvailable = modality;
- }
- if (authenticator.hasEnrolledTemplates(userId)) {
- hasTemplatesEnrolled = true;
- if (isEnabledForApp(modality, userId)) {
- enabledForApps = true;
- break;
+ for (AuthenticatorWrapper authenticator : mAuthenticators) {
+ modality = authenticator.modality;
+ try {
+ if (authenticator.impl.isHardwareDetected(opPackageName)) {
+ isHardwareDetected = true;
+ if (firstHwAvailable == TYPE_NONE) {
+ // Store the first one since we want to return the error in correct priority
+ // order.
+ firstHwAvailable = modality;
+ }
+ if (authenticator.impl.hasEnrolledTemplates(userId, opPackageName)) {
+ hasTemplatesEnrolled = true;
+ if (isEnabledForApp(modality, userId)) {
+ enabledForApps = true;
+ break;
+ }
}
}
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
}
}
@@ -1062,7 +975,7 @@ public class BiometricService extends SystemService {
}
private boolean isEnabledForApp(int modality, int userId) {
- switch(modality) {
+ switch (modality) {
case TYPE_FINGERPRINT:
return true;
case TYPE_IRIS:
@@ -1075,51 +988,8 @@ public class BiometricService extends SystemService {
}
}
- private String getErrorString(int type, int error, int vendorCode) {
- switch (type) {
- case TYPE_FINGERPRINT:
- return FingerprintManager.getErrorString(getContext(), error, vendorCode);
- case TYPE_IRIS:
- Slog.w(TAG, "Modality not supported");
- return null; // not supported
- case TYPE_FACE:
- return FaceManager.getErrorString(getContext(), error, vendorCode);
- default:
- Slog.w(TAG, "Unable to get error string for modality: " + type);
- return null;
- }
- }
-
- private BiometricAuthenticator getAuthenticator(int type) {
- switch (type) {
- case TYPE_FINGERPRINT:
- return (FingerprintManager)
- getContext().getSystemService(Context.FINGERPRINT_SERVICE);
- case TYPE_IRIS:
- return null;
- case TYPE_FACE:
- return (FaceManager)
- getContext().getSystemService(Context.FACE_SERVICE);
- default:
- return null;
- }
- }
-
- private boolean hasFeature(int type) {
- switch (type) {
- case TYPE_FINGERPRINT:
- return mHasFeatureFingerprint;
- case TYPE_IRIS:
- return mHasFeatureIris;
- case TYPE_FACE:
- return mHasFeatureFace;
- default:
- return false;
- }
- }
-
private void logDialogDismissed(int reason) {
- if (reason == BiometricPrompt.DISMISSED_REASON_CONFIRMED) {
+ if (reason == BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED) {
// Explicit auth, authentication confirmed.
// Latency in this case is authenticated -> confirmed. <Biometric>Service
// should have the first half (first acquired -> authenticated).
@@ -1217,14 +1087,14 @@ public class BiometricService extends SystemService {
// Notify SysUI that the biometric has been authenticated. SysUI already knows
// the implicit/explicit state and will react accordingly.
- mStatusBarService.onBiometricAuthenticated(true, null /* failureReason */);
+ mStatusBarService.onBiometricAuthenticated();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
}
- private void handleAuthenticationRejected(String failureReason) {
- Slog.v(TAG, "handleAuthenticationRejected: " + failureReason);
+ private void handleAuthenticationRejected() {
+ Slog.v(TAG, "handleAuthenticationRejected()");
try {
// Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
// after user dismissed/canceled dialog).
@@ -1233,7 +1103,8 @@ public class BiometricService extends SystemService {
return;
}
- mStatusBarService.onBiometricAuthenticated(false, failureReason);
+ mStatusBarService.onBiometricError(TYPE_NONE,
+ BiometricConstants.BIOMETRIC_PAUSED_REJECTED, 0 /* vendorCode */);
// TODO: This logic will need to be updated if BP is multi-modal
if ((mCurrentAuthSession.mModality & TYPE_FACE) != 0) {
@@ -1248,8 +1119,9 @@ public class BiometricService extends SystemService {
}
}
- private void handleAuthenticationTimedOut(String message) {
- Slog.v(TAG, "handleAuthenticationTimedOut: " + message);
+ private void handleAuthenticationTimedOut(int modality, int error, int vendorCode) {
+ Slog.v(TAG, String.format("handleAuthenticationTimedOut(%d, %d, %d)", modality, error,
+ vendorCode));
try {
// Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
// after user dismissed/canceled dialog).
@@ -1258,57 +1130,14 @@ public class BiometricService extends SystemService {
return;
}
- mStatusBarService.onBiometricAuthenticated(false, message);
+ mStatusBarService.onBiometricError(modality, error, vendorCode);
mCurrentAuthSession.mState = STATE_AUTH_PAUSED;
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
}
- private void handleOnConfirmDeviceCredentialSuccess() {
- if (mConfirmDeviceCredentialReceiver == null) {
- Slog.w(TAG, "handleOnConfirmDeviceCredentialSuccess null!");
- return;
- }
- try {
- mConfirmDeviceCredentialReceiver.onAuthenticationSucceeded();
- if (mCurrentAuthSession != null) {
- mCurrentAuthSession = null;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "RemoteException", e);
- }
- mConfirmDeviceCredentialReceiver = null;
- }
-
- private void handleOnConfirmDeviceCredentialError(int error, String message) {
- if (mConfirmDeviceCredentialReceiver == null) {
- Slog.w(TAG, "handleOnConfirmDeviceCredentialError null! Error: "
- + error + " " + message);
- return;
- }
- try {
- mConfirmDeviceCredentialReceiver.onError(error, message);
- if (mCurrentAuthSession != null) {
- mCurrentAuthSession = null;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "RemoteException", e);
- }
- mConfirmDeviceCredentialReceiver = null;
- }
-
- private void handleRegisterCancellationCallback(
- IBiometricConfirmDeviceCredentialCallback callback) {
- if (mCurrentAuthSession == null) {
- Slog.d(TAG, "Current auth session null");
- return;
- }
- Slog.d(TAG, "Updating cancel callback");
- mCurrentAuthSession.mConfirmDeviceCredentialCallback = callback;
- }
-
- private void handleOnError(int cookie, int error, String message) {
+ private void handleOnError(int cookie, int modality, int error, int vendorCode) {
Slog.d(TAG, "handleOnError: " + error + " cookie: " + cookie);
// Errors can either be from the current auth session or the pending auth session.
// The pending auth session may receive errors such as ERROR_LOCKOUT before
@@ -1318,34 +1147,34 @@ public class BiometricService extends SystemService {
// of their intended receivers.
try {
if (mCurrentAuthSession != null && mCurrentAuthSession.containsCookie(cookie)) {
-
mCurrentAuthSession.mErrorEscrow = error;
- mCurrentAuthSession.mErrorStringEscrow = message;
-
- if (mCurrentAuthSession.isFromConfirmDeviceCredential()) {
- // If we were invoked by ConfirmDeviceCredential, do not delete the current
- // auth session since we still need to respond to cancel signal while
- if (DEBUG) Slog.d(TAG, "From CDC, transition to CANCELED_SHOWING_CDC state");
-
- // Send the error to ConfirmDeviceCredential so that it goes to Pin/Pattern/Pass
- // screen
- mCurrentAuthSession.mClientReceiver.onError(error, message);
- mCurrentAuthSession.mState = STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC;
- mStatusBarService.hideBiometricDialog();
- } else if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) {
- mCurrentAuthSession.mState = STATE_ERROR_PENDING_SYSUI;
- if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
- mStatusBarService.hideBiometricDialog();
+ mCurrentAuthSession.mVendorCodeEscrow = vendorCode;
+
+ if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) {
+ final boolean errorLockout = error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
+ || error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
+ if (mCurrentAuthSession.isAllowDeviceCredential() && errorLockout) {
+ // SystemUI handles transition from biometric to device credential.
+ mCurrentAuthSession.mState = STATE_SHOWING_DEVICE_CREDENTIAL;
+ mStatusBarService.onBiometricError(modality, error, vendorCode);
} else {
- mStatusBarService.onBiometricError(message);
+ mCurrentAuthSession.mState = STATE_ERROR_PENDING_SYSUI;
+ if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
+ mStatusBarService.hideAuthenticationDialog();
+ } else {
+ mStatusBarService.onBiometricError(modality, error, vendorCode);
+ }
}
} else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED) {
// In the "try again" state, we should forward canceled errors to
// the client and and clean up. The only error we should get here is
// ERROR_CANCELED due to another client kicking us out.
- mCurrentAuthSession.mClientReceiver.onError(error, message);
- mStatusBarService.hideBiometricDialog();
+ mCurrentAuthSession.mClientReceiver.onError(modality, error, vendorCode);
+ mStatusBarService.hideAuthenticationDialog();
mCurrentAuthSession = null;
+ } else if (mCurrentAuthSession.mState == STATE_SHOWING_DEVICE_CREDENTIAL) {
+ Slog.d(TAG, "Biometric canceled, ignoring from state: "
+ + mCurrentAuthSession.mState);
} else {
Slog.e(TAG, "Impossible session error state: "
+ mCurrentAuthSession.mState);
@@ -1353,12 +1182,38 @@ public class BiometricService extends SystemService {
} else if (mPendingAuthSession != null
&& mPendingAuthSession.containsCookie(cookie)) {
if (mPendingAuthSession.mState == STATE_AUTH_CALLED) {
- mPendingAuthSession.mClientReceiver.onError(error, message);
- mPendingAuthSession = null;
+ // If any error is received while preparing the auth session (lockout, etc),
+ // and if device credential is allowed, just show the credential UI.
+ if (mPendingAuthSession.isAllowDeviceCredential()) {
+ int authenticators = mPendingAuthSession.mBundle
+ .getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, 0);
+ // Disallow biometric and notify SystemUI to show the authentication prompt.
+ authenticators &= ~Authenticator.TYPE_BIOMETRIC;
+ mPendingAuthSession.mBundle.putInt(
+ BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED,
+ authenticators);
+
+ mCurrentAuthSession = mPendingAuthSession;
+ mCurrentAuthSession.mState = STATE_SHOWING_DEVICE_CREDENTIAL;
+ mPendingAuthSession = null;
+
+ mStatusBarService.showAuthenticationDialog(
+ mCurrentAuthSession.mBundle,
+ mInternalReceiver,
+ 0 /* biometricModality */,
+ false /* requireConfirmation */,
+ mCurrentAuthSession.mUserId,
+ mCurrentAuthSession.mOpPackageName);
+ } else {
+ mPendingAuthSession.mClientReceiver.onError(modality, error, vendorCode);
+ mPendingAuthSession = null;
+ }
} else {
Slog.e(TAG, "Impossible pending session error state: "
+ mPendingAuthSession.mState);
}
+ } else {
+ Slog.e(TAG, "Unknown cookie: " + cookie);
}
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
@@ -1396,9 +1251,12 @@ public class BiometricService extends SystemService {
try {
switch (reason) {
- case BiometricPrompt.DISMISSED_REASON_CONFIRMED:
- case BiometricPrompt.DISMISSED_REASON_CONFIRM_NOT_REQUIRED:
- mKeyStore.addAuthToken(mCurrentAuthSession.mTokenEscrow);
+ case BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED:
+ case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED:
+ case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED:
+ if (mCurrentAuthSession.mTokenEscrow != null) {
+ mKeyStore.addAuthToken(mCurrentAuthSession.mTokenEscrow);
+ }
mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
break;
@@ -1411,8 +1269,10 @@ public class BiometricService extends SystemService {
case BiometricPrompt.DISMISSED_REASON_USER_CANCEL:
mCurrentAuthSession.mClientReceiver.onError(
+ mCurrentAuthSession.mModality,
BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
- getContext().getString(R.string.biometric_error_user_canceled));
+ 0 /* vendorCode */
+ );
// Cancel authentication. Skip the token/package check since we are cancelling
// from system server. The interface is permission protected so this is fine.
cancelInternal(null /* token */, null /* package */, false /* fromClient */);
@@ -1420,8 +1280,11 @@ public class BiometricService extends SystemService {
case BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED:
case BiometricPrompt.DISMISSED_REASON_ERROR:
- mCurrentAuthSession.mClientReceiver.onError(mCurrentAuthSession.mErrorEscrow,
- mCurrentAuthSession.mErrorStringEscrow);
+ mCurrentAuthSession.mClientReceiver.onError(
+ mCurrentAuthSession.mModality,
+ mCurrentAuthSession.mErrorEscrow,
+ mCurrentAuthSession.mVendorCodeEscrow
+ );
break;
default:
@@ -1450,12 +1313,37 @@ public class BiometricService extends SystemService {
mCurrentAuthSession.mCallingUid,
mCurrentAuthSession.mCallingPid,
mCurrentAuthSession.mCallingUserId,
- mCurrentAuthSession.mModality,
- mCurrentAuthSession.mConfirmDeviceCredentialCallback);
+ mCurrentAuthSession.mModality);
}
+ private void handleOnDeviceCredentialPressed() {
+ Slog.d(TAG, "onDeviceCredentialPressed");
+ if (mCurrentAuthSession == null) {
+ Slog.e(TAG, "Auth session null");
+ return;
+ }
+
+ // Cancel authentication. Skip the token/package check since we are cancelling
+ // from system server. The interface is permission protected so this is fine.
+ cancelInternal(null /* token */, null /* package */, false /* fromClient */);
+
+ mCurrentAuthSession.mState = STATE_SHOWING_DEVICE_CREDENTIAL;
+ }
+
+ /**
+ * Invoked when each service has notified that its client is ready to be started. When
+ * all biometrics are ready, this invokes the SystemUI dialog through StatusBar.
+ */
private void handleOnReadyForAuthentication(int cookie, boolean requireConfirmation,
int userId) {
+ if (mPendingAuthSession == null) {
+ // Only should happen if a biometric was locked out when authenticate() was invoked.
+ // In that case, if device credentials are allowed, the UI is already showing. If not
+ // allowed, the error has already been returned to the caller.
+ Slog.w(TAG, "Pending auth session null");
+ return;
+ }
+
Iterator it = mPendingAuthSession.mModalitiesWaiting.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
@@ -1479,64 +1367,59 @@ public class BiometricService extends SystemService {
mPendingAuthSession = null;
mCurrentAuthSession.mState = STATE_AUTH_STARTED;
- try {
- int modality = TYPE_NONE;
- it = mCurrentAuthSession.mModalitiesMatched.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
- if (pair.getKey() == TYPE_FINGERPRINT) {
- mFingerprintService.startPreparedClient(pair.getValue());
- } else if (pair.getKey() == TYPE_IRIS) {
- Slog.e(TAG, "Iris unsupported");
- } else if (pair.getKey() == TYPE_FACE) {
- mFaceService.startPreparedClient(pair.getValue());
- } else {
- Slog.e(TAG, "Unknown modality: " + pair.getKey());
+ int modality = TYPE_NONE;
+ it = mCurrentAuthSession.mModalitiesMatched.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
+ boolean foundAuthenticator = false;
+ for (AuthenticatorWrapper authenticator : mAuthenticators) {
+ if (authenticator.modality == pair.getKey()) {
+ foundAuthenticator = true;
+ try {
+ authenticator.impl.startPreparedClient(pair.getValue());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
}
- modality |= pair.getKey();
}
+ if (!foundAuthenticator) {
+ Slog.e(TAG, "Unknown modality: " + pair.getKey());
+ }
+ modality |= pair.getKey();
+ }
- if (!continuing) {
- mStatusBarService.showBiometricDialog(mCurrentAuthSession.mBundle,
+ if (!continuing) {
+ try {
+ mStatusBarService.showAuthenticationDialog(mCurrentAuthSession.mBundle,
mInternalReceiver, modality, requireConfirmation, userId,
mCurrentAuthSession.mOpPackageName);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
}
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
}
}
}
private void handleAuthenticate(IBinder token, long sessionId, int userId,
IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
- int callingUid, int callingPid, int callingUserId,
- IBiometricConfirmDeviceCredentialCallback callback) {
+ int callingUid, int callingPid, int callingUserId) {
mHandler.post(() -> {
- final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId);
+ final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId,
+ opPackageName);
final int modality = result.first;
final int error = result.second;
- // Check for errors, notify callback, and return
- if (error != BiometricConstants.BIOMETRIC_SUCCESS) {
+ final boolean credentialAllowed = Utils.isDeviceCredentialAllowed(bundle);
+
+ if (error != BiometricConstants.BIOMETRIC_SUCCESS && credentialAllowed) {
+ // If there's a problem but device credential is allowed, only show credential UI.
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED,
+ Authenticator.TYPE_CREDENTIAL);
+ } else if (error != BiometricConstants.BIOMETRIC_SUCCESS) {
+ // Check for errors, notify callback, and return
try {
- final String hardwareUnavailable =
- getContext().getString(R.string.biometric_error_hw_unavailable);
- switch (error) {
- case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT:
- receiver.onError(error, hardwareUnavailable);
- break;
- case BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE:
- receiver.onError(error, hardwareUnavailable);
- break;
- case BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS:
- receiver.onError(error,
- getErrorString(modality, error, 0 /* vendorCode */));
- break;
- default:
- Slog.e(TAG, "Unhandled error");
- break;
- }
+ receiver.onError(modality, error, 0 /* vendorCode */);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to send error", e);
}
@@ -1546,7 +1429,7 @@ public class BiometricService extends SystemService {
// Start preparing for authentication. Authentication starts when
// all modalities requested have invoked onReadyForAuthentication.
authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle,
- callingUid, callingPid, callingUserId, modality, callback);
+ callingUid, callingPid, callingUserId, modality);
});
}
@@ -1555,48 +1438,60 @@ public class BiometricService extends SystemService {
* modality/modalities to start authenticating with. authenticateInternal() should only be
* used for:
* 1) Preparing <Biometric>Services for authentication when BiometricPrompt#authenticate is,
- * invoked, shortly after which BiometricPrompt is shown and authentication starts
+ * invoked, shortly after which BiometricPrompt is shown and authentication starts
* 2) Preparing <Biometric>Services for authentication when BiometricPrompt is already shown
- * and the user has pressed "try again"
+ * and the user has pressed "try again"
*/
private void authenticateInternal(IBinder token, long sessionId, int userId,
IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
- int callingUid, int callingPid, int callingUserId, int modality,
- IBiometricConfirmDeviceCredentialCallback callback) {
+ int callingUid, int callingPid, int callingUserId, int modality) {
+ boolean requireConfirmation = bundle.getBoolean(
+ BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */);
+ if ((modality & TYPE_FACE) != 0) {
+ // Check if the user has forced confirmation to be required in Settings.
+ requireConfirmation = requireConfirmation
+ || mSettingObserver.getFaceAlwaysRequireConfirmation(userId);
+ }
+ // Generate random cookies to pass to the services that should prepare to start
+ // authenticating. Store the cookie here and wait for all services to "ack"
+ // with the cookie. Once all cookies are received, we can show the prompt
+ // and let the services start authenticating. The cookie should be non-zero.
+ final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
+ final int authenticators = bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED);
+ Slog.d(TAG, "Creating auth session. Modality: " + modality
+ + ", cookie: " + cookie
+ + ", authenticators: " + authenticators);
+ final HashMap<Integer, Integer> modalities = new HashMap<>();
+
+ // If it's only device credential, we don't need to wait - LockSettingsService is
+ // always ready to check credential (SystemUI invokes that path).
+ if ((authenticators & ~Authenticator.TYPE_CREDENTIAL) != 0) {
+ modalities.put(modality, cookie);
+ }
+ mPendingAuthSession = new AuthSession(modalities, token, sessionId, userId,
+ receiver, opPackageName, bundle, callingUid, callingPid, callingUserId,
+ modality, requireConfirmation);
+
try {
- boolean requireConfirmation = bundle.getBoolean(
- BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */);
- if ((modality & TYPE_FACE) != 0) {
- // Check if the user has forced confirmation to be required in Settings.
- requireConfirmation = requireConfirmation
- || mSettingObserver.getFaceAlwaysRequireConfirmation(userId);
- }
- // Generate random cookies to pass to the services that should prepare to start
- // authenticating. Store the cookie here and wait for all services to "ack"
- // with the cookie. Once all cookies are received, we can show the prompt
- // and let the services start authenticating. The cookie should be non-zero.
- final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
- Slog.d(TAG, "Creating auth session. Modality: " + modality
- + ", cookie: " + cookie);
- final HashMap<Integer, Integer> authenticators = new HashMap<>();
- authenticators.put(modality, cookie);
- mPendingAuthSession = new AuthSession(authenticators, token, sessionId, userId,
- receiver, opPackageName, bundle, callingUid, callingPid, callingUserId,
- modality, requireConfirmation, callback);
- mPendingAuthSession.mState = STATE_AUTH_CALLED;
- // No polymorphism :(
- if ((modality & TYPE_FINGERPRINT) != 0) {
- mFingerprintService.prepareForAuthentication(token, sessionId, userId,
- mInternalReceiver, opPackageName, cookie,
- callingUid, callingPid, callingUserId);
- }
- if ((modality & TYPE_IRIS) != 0) {
- Slog.w(TAG, "Iris unsupported");
- }
- if ((modality & TYPE_FACE) != 0) {
- mFaceService.prepareForAuthentication(requireConfirmation,
- token, sessionId, userId, mInternalReceiver, opPackageName,
- cookie, callingUid, callingPid, callingUserId);
+ if (authenticators == Authenticator.TYPE_CREDENTIAL) {
+ mPendingAuthSession.mState = STATE_SHOWING_DEVICE_CREDENTIAL;
+ mCurrentAuthSession = mPendingAuthSession;
+ mPendingAuthSession = null;
+
+ mStatusBarService.showAuthenticationDialog(
+ mCurrentAuthSession.mBundle,
+ mInternalReceiver,
+ 0 /* biometricModality */,
+ false /* requireConfirmation */,
+ mCurrentAuthSession.mUserId,
+ mCurrentAuthSession.mOpPackageName);
+ } else {
+ mPendingAuthSession.mState = STATE_AUTH_CALLED;
+ for (AuthenticatorWrapper authenticator : mAuthenticators) {
+ authenticator.impl.prepareForAuthentication(requireConfirmation, token,
+ sessionId, userId, mInternalReceiver, opPackageName, cookie, callingUid,
+ callingPid, callingUserId);
+ }
}
} catch (RemoteException e) {
Slog.e(TAG, "Unable to start authentication", e);
@@ -1609,50 +1504,24 @@ public class BiometricService extends SystemService {
return;
}
- if (mCurrentAuthSession != null
- && mCurrentAuthSession.mState == STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC) {
- if (DEBUG) Slog.d(TAG, "Cancel received while ConfirmDeviceCredential showing");
- try {
- mCurrentAuthSession.mConfirmDeviceCredentialCallback.cancel();
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to cancel ConfirmDeviceCredential", e);
- }
-
- // TODO(b/123378871): Remove when moved. Piggy back on this for now to clean up.
- handleOnConfirmDeviceCredentialError(BiometricConstants.BIOMETRIC_ERROR_CANCELED,
- getContext().getString(R.string.biometric_error_canceled));
- } else if (mCurrentAuthSession != null
- && mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
+ if (mCurrentAuthSession != null && mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
// We need to check the current authenticators state. If we're pending confirm
// or idle, we need to dismiss the dialog and send an ERROR_CANCELED to the client,
// since we won't be getting an onError from the driver.
try {
// Send error to client
mCurrentAuthSession.mClientReceiver.onError(
+ mCurrentAuthSession.mModality,
BiometricConstants.BIOMETRIC_ERROR_CANCELED,
- getContext().getString(
- com.android.internal.R.string.biometric_error_user_canceled)
+ 0 /* vendorCode */
);
-
mCurrentAuthSession = null;
- mStatusBarService.hideBiometricDialog();
+ mStatusBarService.hideAuthenticationDialog();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
} else {
- boolean fromCDC = false;
- if (mCurrentAuthSession != null) {
- fromCDC = mCurrentAuthSession.mBundle.getBoolean(
- BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, false);
- }
-
- if (fromCDC) {
- if (DEBUG) Slog.d(TAG, "Cancelling from CDC");
- cancelInternal(token, opPackageName, false /* fromClient */);
- } else {
- cancelInternal(token, opPackageName, true /* fromClient */);
- }
-
+ cancelInternal(token, opPackageName, true /* fromClient */);
}
}
@@ -1661,30 +1530,25 @@ public class BiometricService extends SystemService {
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
- try {
- if (mCurrentAuthSession == null) {
- Slog.w(TAG, "Skipping cancelInternal");
- return;
- } else if (mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
- Slog.w(TAG, "Skipping cancelInternal, state: " + mCurrentAuthSession.mState);
- return;
- }
+ if (mCurrentAuthSession == null) {
+ Slog.w(TAG, "Skipping cancelInternal");
+ return;
+ } else if (mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
+ Slog.w(TAG, "Skipping cancelInternal, state: " + mCurrentAuthSession.mState);
+ return;
+ }
- // TODO: For multiple modalities, send a single ERROR_CANCELED only when all
- // drivers have canceled authentication.
- if ((mCurrentAuthSession.mModality & TYPE_FINGERPRINT) != 0) {
- mFingerprintService.cancelAuthenticationFromService(token, opPackageName,
- callingUid, callingPid, callingUserId, fromClient);
- }
- if ((mCurrentAuthSession.mModality & TYPE_IRIS) != 0) {
- Slog.w(TAG, "Iris unsupported");
- }
- if ((mCurrentAuthSession.mModality & TYPE_FACE) != 0) {
- mFaceService.cancelAuthenticationFromService(token, opPackageName,
- callingUid, callingPid, callingUserId, fromClient);
+ // TODO: For multiple modalities, send a single ERROR_CANCELED only when all
+ // drivers have canceled authentication.
+ for (AuthenticatorWrapper authenticator : mAuthenticators) {
+ if ((authenticator.modality & mCurrentAuthSession.mModality) != 0) {
+ try {
+ authenticator.impl.cancelAuthenticationFromService(token, opPackageName,
+ callingUid, callingPid, callingUserId, fromClient);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to cancel authentication");
+ }
}
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to cancel authentication");
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 4fa29ac541f9..ed5f9de01bcf 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -17,10 +17,15 @@
package com.android.server.biometrics;
import android.content.Context;
+import android.hardware.biometrics.Authenticator;
+import android.hardware.biometrics.BiometricPrompt;
import android.os.Build;
+import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
+import com.android.internal.annotations.VisibleForTesting;
+
public class Utils {
public static boolean isDebugEnabled(Context context, int targetUserId) {
if (targetUserId == UserHandle.USER_NULL) {
@@ -38,4 +43,43 @@ public class Utils {
}
return true;
}
+
+ /**
+ * Combine {@link BiometricPrompt#KEY_ALLOW_DEVICE_CREDENTIAL} with
+ * {@link BiometricPrompt#KEY_AUTHENTICATORS_ALLOWED}, as the former is not flexible
+ * enough.
+ */
+ public static void combineAuthenticatorBundles(Bundle bundle) {
+ boolean biometricEnabled = true; // enabled by default
+ boolean credentialEnabled = bundle.getBoolean(
+ BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, false);
+ if (bundle.get(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED) != null) {
+ final int authenticatorFlags =
+ bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED);
+ biometricEnabled = (authenticatorFlags & Authenticator.TYPE_BIOMETRIC) != 0;
+ // Using both KEY_ALLOW_DEVICE_CREDENTIAL and KEY_AUTHENTICATORS_ALLOWED together
+ // is not supported. Default to overwriting.
+ credentialEnabled = (authenticatorFlags & Authenticator.TYPE_CREDENTIAL) != 0;
+ }
+
+ bundle.remove(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL);
+
+ int authenticators = 0;
+ if (biometricEnabled) {
+ authenticators |= Authenticator.TYPE_BIOMETRIC;
+ }
+ if (credentialEnabled) {
+ authenticators |= Authenticator.TYPE_CREDENTIAL;
+ }
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
+ }
+
+ /**
+ * @param bundle should be first processed by {@link #combineAuthenticatorBundles(Bundle)}
+ * @return true if device credential allowed.
+ */
+ public static boolean isDeviceCredentialAllowed(Bundle bundle) {
+ final int authenticators = bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED);
+ return (authenticators & Authenticator.TYPE_CREDENTIAL) != 0;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/face/FaceAuthenticator.java
new file mode 100644
index 000000000000..5df5b29d58a8
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/face/FaceAuthenticator.java
@@ -0,0 +1,75 @@
+/*
+ * 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.biometrics.face;
+
+import android.hardware.biometrics.IBiometricAuthenticator;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
+import android.hardware.face.IFaceService;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+/**
+ * TODO(b/141025588): Add JavaDoc.
+ */
+public final class FaceAuthenticator extends IBiometricAuthenticator.Stub {
+ private final IFaceService mFaceService;
+
+ public FaceAuthenticator(IFaceService faceService) {
+ mFaceService = faceService;
+ }
+
+ @Override
+ public void prepareForAuthentication(boolean requireConfirmation, IBinder token,
+ long sessionId, int userId, IBiometricServiceReceiverInternal wrapperReceiver,
+ String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId)
+ throws RemoteException {
+ mFaceService.prepareForAuthentication(requireConfirmation, token, sessionId, userId,
+ wrapperReceiver, opPackageName, cookie, callingUid, callingPid, callingUserId);
+ }
+
+ @Override
+ public void startPreparedClient(int cookie) throws RemoteException {
+ mFaceService.startPreparedClient(cookie);
+ }
+
+ @Override
+ public void cancelAuthenticationFromService(IBinder token, String opPackageName, int callingUid,
+ int callingPid, int callingUserId, boolean fromClient) throws RemoteException {
+ mFaceService.cancelAuthenticationFromService(token, opPackageName, callingUid, callingPid,
+ callingUserId, fromClient);
+ }
+
+ @Override
+ public boolean isHardwareDetected(String opPackageName) throws RemoteException {
+ return mFaceService.isHardwareDetected(opPackageName);
+ }
+
+ @Override
+ public boolean hasEnrolledTemplates(int userId, String opPackageName) throws RemoteException {
+ return mFaceService.hasEnrolledFaces(userId, opPackageName);
+ }
+
+ @Override
+ public void resetLockout(byte[] token) throws RemoteException {
+ mFaceService.resetLockout(token);
+ }
+
+ @Override
+ public void setActiveUser(int uid) throws RemoteException {
+ mFaceService.setActiveUser(uid);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index b1c7c7685ddf..a0c8e2325f77 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -20,6 +20,7 @@ import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.RESET_FACE_LOCKOUT;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import android.app.ActivityManager;
import android.app.AppOpsManager;
@@ -538,7 +539,7 @@ public class FaceService extends BiometricServiceBase {
// TODO: refactor out common code here
@Override // Binder call
- public boolean isHardwareDetected(long deviceId, String opPackageName) {
+ public boolean isHardwareDetected(String opPackageName) {
checkPermission(USE_BIOMETRIC_INTERNAL);
if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
Binder.getCallingUid(), Binder.getCallingPid(),
@@ -752,8 +753,7 @@ public class FaceService extends BiometricServiceBase {
public void onError(long deviceId, int error, int vendorCode, int cookie)
throws RemoteException {
if (getWrapperReceiver() != null) {
- getWrapperReceiver().onError(cookie, error,
- FaceManager.getErrorString(getContext(), error, vendorCode));
+ getWrapperReceiver().onError(cookie, TYPE_FACE, error, vendorCode);
}
}
}
@@ -930,7 +930,8 @@ public class FaceService extends BiometricServiceBase {
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);
});
}
@@ -1088,7 +1089,7 @@ public class FaceService extends BiometricServiceBase {
publishBinderService(Context.FACE_SERVICE, new FaceServiceWrapper());
// Get the face daemon on FaceService's on thread so SystemServerInitThreadPool isn't
// blocked
- SystemServerInitThreadPool.get().submit(() -> mHandler.post(this::getFaceDaemon),
+ SystemServerInitThreadPool.submit(() -> mHandler.post(this::getFaceDaemon),
TAG + ".onStart");
}
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java
new file mode 100644
index 000000000000..6150de151ccc
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java
@@ -0,0 +1,75 @@
+/*
+ * 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.biometrics.fingerprint;
+
+import android.hardware.biometrics.IBiometricAuthenticator;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
+import android.hardware.fingerprint.IFingerprintService;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+/**
+ * TODO(b/141025588): Add JavaDoc.
+ */
+public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub {
+ private final IFingerprintService mFingerprintService;
+
+ public FingerprintAuthenticator(IFingerprintService fingerprintService) {
+ mFingerprintService = fingerprintService;
+ }
+
+ @Override
+ public void prepareForAuthentication(boolean requireConfirmation, IBinder token,
+ long sessionId, int userId, IBiometricServiceReceiverInternal wrapperReceiver,
+ String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId)
+ throws RemoteException {
+ mFingerprintService.prepareForAuthentication(token, sessionId, userId, wrapperReceiver,
+ opPackageName, cookie, callingUid, callingPid, callingUserId);
+ }
+
+ @Override
+ public void startPreparedClient(int cookie) throws RemoteException {
+ mFingerprintService.startPreparedClient(cookie);
+ }
+
+ @Override
+ public void cancelAuthenticationFromService(IBinder token, String opPackageName, int callingUid,
+ int callingPid, int callingUserId, boolean fromClient) throws RemoteException {
+ mFingerprintService.cancelAuthenticationFromService(token, opPackageName, callingUid,
+ callingPid, callingUserId, fromClient);
+ }
+
+ @Override
+ public boolean isHardwareDetected(String opPackageName) throws RemoteException {
+ return mFingerprintService.isHardwareDetected(opPackageName);
+ }
+
+ @Override
+ public boolean hasEnrolledTemplates(int userId, String opPackageName) throws RemoteException {
+ return mFingerprintService.hasEnrolledFingerprints(userId, opPackageName);
+ }
+
+ @Override
+ public void resetLockout(byte[] token) throws RemoteException {
+ mFingerprintService.resetTimeout(token);
+ }
+
+ @Override
+ public void setActiveUser(int uid) throws RemoteException {
+ mFingerprintService.setActiveUser(uid);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index 320e1022873c..44797ad97b37 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -22,6 +22,7 @@ import static android.Manifest.permission.MANAGE_FINGERPRINT;
import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_FINGERPRINT;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import android.app.ActivityManager;
import android.app.AlarmManager;
@@ -350,7 +351,7 @@ public class FingerprintService extends BiometricServiceBase {
// TODO: refactor out common code here
@Override // Binder call
- public boolean isHardwareDetected(long deviceId, String opPackageName) {
+ public boolean isHardwareDetected(String opPackageName) {
if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
Binder.getCallingUid(), Binder.getCallingPid(),
UserHandle.getCallingUserId())) {
@@ -480,8 +481,7 @@ public class FingerprintService extends BiometricServiceBase {
public void onError(long deviceId, int error, int vendorCode, int cookie)
throws RemoteException {
if (getWrapperReceiver() != null) {
- getWrapperReceiver().onError(cookie, error,
- FingerprintManager.getErrorString(getContext(), error, vendorCode));
+ getWrapperReceiver().onError(cookie, TYPE_FINGERPRINT, error, vendorCode);
}
}
}
@@ -725,7 +725,7 @@ public class FingerprintService extends BiometricServiceBase {
public void onStart() {
super.onStart();
publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
- SystemServerInitThreadPool.get().submit(this::getFingerprintDaemon, TAG + ".onStart");
+ SystemServerInitThreadPool.submit(this::getFingerprintDaemon, TAG + ".onStart");
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/iris/IrisAuthenticator.java b/services/core/java/com/android/server/biometrics/iris/IrisAuthenticator.java
new file mode 100644
index 000000000000..c44b8e7227e3
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/iris/IrisAuthenticator.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.iris;
+
+import android.hardware.biometrics.IBiometricAuthenticator;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
+import android.hardware.iris.IIrisService;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+/**
+ * TODO(b/141025588): Add JavaDoc.
+ */
+public final class IrisAuthenticator extends IBiometricAuthenticator.Stub {
+ private final IIrisService mIrisService;
+
+ public IrisAuthenticator(IIrisService irisService) {
+ mIrisService = irisService;
+ }
+
+ @Override
+ public void prepareForAuthentication(boolean requireConfirmation, IBinder token,
+ long sessionId, int userId, IBiometricServiceReceiverInternal wrapperReceiver,
+ String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId)
+ throws RemoteException {
+ }
+
+ @Override
+ public void startPreparedClient(int cookie) throws RemoteException {
+ }
+
+ @Override
+ public void cancelAuthenticationFromService(IBinder token, String opPackageName, int callingUid,
+ int callingPid, int callingUserId, boolean fromClient) throws RemoteException {
+ }
+
+ @Override
+ public boolean isHardwareDetected(String opPackageName) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean hasEnrolledTemplates(int userId, String opPackageName) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public void resetLockout(byte[] token) throws RemoteException {
+ }
+
+ @Override
+ public void setActiveUser(int uid) throws RemoteException {
+ }
+}
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 027e2fb5ccaa..0fabd9aef373 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -25,6 +25,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.server.compat.config.Change;
import com.android.server.compat.config.XmlParser;
@@ -186,6 +187,43 @@ public final class CompatConfig {
}
return overrideExists;
}
+ /**
+ * Overrides the enabled state for a given change and app. This method is intended to be used
+ * *only* for debugging purposes.
+ *
+ * <p>Note, package overrides are not persistent and will be lost on system or runtime restart.
+ *
+ * @param overrides list of overrides to default changes config.
+ * @param packageName app for which the overrides will be applied.
+ */
+ public void addOverrides(
+ CompatibilityChangeConfig overrides, String packageName) {
+ synchronized (mChanges) {
+ for (Long changeId: overrides.enabledChanges()) {
+ addOverride(changeId, packageName, true);
+ }
+ for (Long changeId: overrides.disabledChanges()) {
+ addOverride(changeId, packageName, false);
+ }
+ }
+ }
+
+ /**
+ * Removes all overrides previously added via {@link #addOverride(long, String, boolean)} or
+ * {@link #addAppOverrides(CompatibilityChangeConfig, String)} for a certain package.
+ *
+ * <p>This restores the default behaviour for the given change and app, once any app
+ * processes have been restarted.
+ *
+ * @param packageName The package for which the overrides should be purged.
+ */
+ public void removePackageOverrides(String packageName) {
+ synchronized (mChanges) {
+ for (int i = 0; i < mChanges.size(); ++i) {
+ mChanges.valueAt(i).removePackageOverride(packageName);
+ }
+ }
+ }
/**
* Dumps the current list of compatibility config information.
diff --git a/services/core/java/com/android/server/compat/OWNERS b/services/core/java/com/android/server/compat/OWNERS
new file mode 100644
index 000000000000..2b7cdb0cbce9
--- /dev/null
+++ b/services/core/java/com/android/server/compat/OWNERS
@@ -0,0 +1,7 @@
+# Use this reviewer by default.
+platform-compat-eng+reviews@google.com
+
+andreionea@google.com
+atrost@google.com
+mathewi@google.com
+satayev@google.com
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 81e507cd24cf..9ac9955493cc 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -18,10 +18,13 @@ package com.android.server.compat;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
import android.util.Slog;
import android.util.StatsLog;
import com.android.internal.compat.ChangeReporter;
+import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.util.DumpUtils;
@@ -40,38 +43,101 @@ public class PlatformCompat extends IPlatformCompat.Stub {
public PlatformCompat(Context context) {
mContext = context;
- mChangeReporter = new ChangeReporter();
+ mChangeReporter = new ChangeReporter(
+ StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER);
}
@Override
public void reportChange(long changeId, ApplicationInfo appInfo) {
- reportChange(changeId, appInfo, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
+ reportChange(changeId, appInfo.uid,
+ StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
+ }
+
+ @Override
+ public void reportChangeByPackageName(long changeId, String packageName, int userId) {
+ ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
+ if (appInfo == null) {
+ return;
+ }
+ reportChange(changeId, appInfo);
+ }
+
+ @Override
+ public void reportChangeByUid(long changeId, int uid) {
+ reportChange(changeId, uid, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
}
@Override
public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
if (CompatConfig.get().isChangeEnabled(changeId, appInfo)) {
- reportChange(changeId, appInfo,
+ reportChange(changeId, appInfo.uid,
StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED);
return true;
}
- reportChange(changeId, appInfo,
+ reportChange(changeId, appInfo.uid,
StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__DISABLED);
return false;
}
@Override
+ public boolean isChangeEnabledByPackageName(long changeId, String packageName, int userId) {
+ ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
+ if (appInfo == null) {
+ return true;
+ }
+ return isChangeEnabled(changeId, appInfo);
+ }
+
+ @Override
+ public boolean isChangeEnabledByUid(long changeId, int uid) {
+ String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
+ if (packages == null || packages.length == 0) {
+ return true;
+ }
+ boolean enabled = true;
+ for (String packageName : packages) {
+ enabled = enabled && isChangeEnabledByPackageName(changeId, packageName,
+ UserHandle.getUserId(uid));
+ }
+ return enabled;
+ }
+
+ @Override
+ public void setOverrides(CompatibilityChangeConfig overrides, String packageName) {
+ CompatConfig.get().addOverrides(overrides, packageName);
+ }
+
+ @Override
+ public void clearOverrides(String packageName) {
+ CompatConfig config = CompatConfig.get();
+ config.removePackageOverrides(packageName);
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return;
CompatConfig.get().dumpConfig(pw);
}
- private void reportChange(long changeId, ApplicationInfo appInfo, int state) {
- int uid = appInfo.uid;
- //TODO(b/138374585): Implement rate limiting for the logs.
- Slog.d(TAG, ChangeReporter.createLogString(uid, changeId, state));
- mChangeReporter.reportChange(uid, changeId,
- state, /* source */
- StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER);
+ /**
+ * Clears information stored about events reported on behalf of an app.
+ * To be called once upon app start or end. A second call would be a no-op.
+ * @param appInfo the app to reset
+ */
+ public void resetReporting(ApplicationInfo appInfo) {
+ mChangeReporter.resetReportedChanges(appInfo.uid);
+ }
+
+ private ApplicationInfo getApplicationInfo(String packageName, int userId) {
+ try {
+ return mContext.getPackageManager().getApplicationInfoAsUser(packageName, 0, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "No installed package " + packageName);
+ }
+ return null;
+ }
+
+ private void reportChange(long changeId, int uid, int state) {
+ mChangeReporter.reportChange(uid, changeId, state);
}
}
diff --git a/services/core/java/com/android/server/compat/PlatformCompatNative.java b/services/core/java/com/android/server/compat/PlatformCompatNative.java
new file mode 100644
index 000000000000..85dfbf411667
--- /dev/null
+++ b/services/core/java/com/android/server/compat/PlatformCompatNative.java
@@ -0,0 +1,50 @@
+/*
+ * 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.compat;
+
+import com.android.internal.compat.IPlatformCompatNative;
+
+/**
+ * @hide
+ */
+public class PlatformCompatNative extends IPlatformCompatNative.Stub {
+ private final PlatformCompat mPlatformCompat;
+
+ public PlatformCompatNative(PlatformCompat platformCompat) {
+ mPlatformCompat = platformCompat;
+ }
+
+ @Override
+ public void reportChangeByPackageName(long changeId, String packageName, int userId) {
+ mPlatformCompat.reportChangeByPackageName(changeId, packageName, userId);
+ }
+
+ @Override
+ public void reportChangeByUid(long changeId, int uid) {
+ mPlatformCompat.reportChangeByUid(changeId, uid);
+ }
+
+ @Override
+ public boolean isChangeEnabledByPackageName(long changeId, String packageName, int userId) {
+ return mPlatformCompat.isChangeEnabledByPackageName(changeId, packageName, userId);
+ }
+
+ @Override
+ public boolean isChangeEnabledByUid(long changeId, int uid) {
+ return mPlatformCompat.isChangeEnabledByUid(changeId, uid);
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index e908acac96b1..20088197141d 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -16,6 +16,7 @@
package com.android.server.connectivity;
+import android.annotation.NonNull;
import android.net.ConnectivityManager;
import android.net.IDnsResolver;
import android.net.INetd;
@@ -344,13 +345,13 @@ public class Nat464Xlat extends BaseNetworkObserver {
* This is necessary because the LinkProperties in mNetwork come from the transport layer, which
* has no idea that 464xlat is running on top of it.
*/
- public void fixupLinkProperties(LinkProperties oldLp, LinkProperties lp) {
+ public void fixupLinkProperties(@NonNull LinkProperties oldLp, @NonNull LinkProperties lp) {
lp.setNat64Prefix(mNat64Prefix);
if (!isRunning()) {
return;
}
- if (lp == null || lp.getAllInterfaceNames().contains(mIface)) {
+ if (lp.getAllInterfaceNames().contains(mIface)) {
return;
}
@@ -453,7 +454,7 @@ public class Nat464Xlat extends BaseNetworkObserver {
@Override
public void interfaceRemoved(String iface) {
- mNetwork.handler().post(() -> { handleInterfaceRemoved(iface); });
+ mNetwork.handler().post(() -> handleInterfaceRemoved(iface));
}
@Override
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index ff07211b287a..2c76c21e516c 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -54,7 +54,8 @@ public class NetworkNotificationManager {
NO_INTERNET(SystemMessage.NOTE_NETWORK_NO_INTERNET),
LOGGED_IN(SystemMessage.NOTE_NETWORK_LOGGED_IN),
PARTIAL_CONNECTIVITY(SystemMessage.NOTE_NETWORK_PARTIAL_CONNECTIVITY),
- SIGN_IN(SystemMessage.NOTE_NETWORK_SIGN_IN);
+ SIGN_IN(SystemMessage.NOTE_NETWORK_SIGN_IN),
+ PRIVATE_DNS_BROKEN(SystemMessage.NOTE_NETWORK_PRIVATE_DNS_BROKEN);
public final int eventId;
@@ -176,13 +177,23 @@ public class NetworkNotificationManager {
}
Resources r = Resources.getSystem();
- CharSequence title;
- CharSequence details;
+ final CharSequence title;
+ final CharSequence details;
int icon = getIcon(transportType, notifyType);
if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) {
title = r.getString(R.string.wifi_no_internet,
WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID()));
details = r.getString(R.string.wifi_no_internet_detailed);
+ } else if (notifyType == NotificationType.PRIVATE_DNS_BROKEN) {
+ if (transportType == TRANSPORT_CELLULAR) {
+ title = r.getString(R.string.mobile_no_internet);
+ } else if (transportType == TRANSPORT_WIFI) {
+ title = r.getString(R.string.wifi_no_internet,
+ WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID()));
+ } else {
+ title = r.getString(R.string.other_networks_no_internet);
+ }
+ details = r.getString(R.string.private_dns_broken_detailed);
} else if (notifyType == NotificationType.PARTIAL_CONNECTIVITY
&& transportType == TRANSPORT_WIFI) {
title = r.getString(R.string.network_partial_connectivity,
@@ -358,8 +369,10 @@ public class NetworkNotificationManager {
}
switch (t) {
case SIGN_IN:
- return 5;
+ return 6;
case PARTIAL_CONNECTIVITY:
+ return 5;
+ case PRIVATE_DNS_BROKEN:
return 4;
case NO_INTERNET:
return 3;
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 6806ff804bc5..a3d966717eb0 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -30,8 +30,8 @@ import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
import static android.net.ConnectivityManager.TETHERING_INVALID;
import static android.net.ConnectivityManager.TETHERING_USB;
import static android.net.ConnectivityManager.TETHERING_WIFI;
+import static android.net.ConnectivityManager.TETHERING_WIFI_P2P;
import static android.net.ConnectivityManager.TETHERING_WIGIG;
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL;
@@ -79,6 +79,9 @@ import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
import android.net.util.VersionedBroadcastListener;
import android.net.wifi.WifiManager;
+import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.WifiP2pInfo;
+import android.net.wifi.p2p.WifiP2pManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -293,6 +296,7 @@ public class Tethering extends BaseNetworkObserver {
filter.addAction(CONNECTIVITY_ACTION);
filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+ filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
mContext.registerReceiver(mStateReceiver, filter, null, handler);
filter = new IntentFilter();
@@ -361,6 +365,8 @@ public class Tethering extends BaseNetworkObserver {
return TETHERING_WIGIG;
}
return TETHERING_WIFI;
+ } else if (cfg.isWifiP2p(iface)) {
+ return TETHERING_WIFI_P2P;
} else if (cfg.isUsb(iface)) {
return TETHERING_USB;
} else if (cfg.isBluetooth(iface)) {
@@ -534,6 +540,7 @@ public class Tethering extends BaseNetworkObserver {
public void untetherAll() {
stopTethering(TETHERING_WIFI);
+ stopTethering(TETHERING_WIFI_P2P);
stopTethering(TETHERING_USB);
stopTethering(TETHERING_BLUETOOTH);
}
@@ -720,6 +727,8 @@ public class Tethering extends BaseNetworkObserver {
handleConnectivityAction(intent);
} else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
handleWifiApAction(intent);
+ } else if (action.equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) {
+ handleWifiP2pAction(intent);
} else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
mLog.log("OBSERVED configuration changed");
updateConfiguration();
@@ -796,6 +805,39 @@ public class Tethering extends BaseNetworkObserver {
}
}
}
+
+ private void handleWifiP2pAction(Intent intent) {
+ if (mConfig.isWifiP2pLegacyTetheringMode()) return;
+
+ final WifiP2pInfo p2pInfo =
+ (WifiP2pInfo) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO);
+ final WifiP2pGroup group =
+ (WifiP2pGroup) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP);
+
+ if (VDBG) {
+ Log.d(TAG, "WifiP2pAction: P2pInfo: " + p2pInfo + " Group: " + group);
+ }
+
+ if (p2pInfo == null) return;
+ // When a p2p group is disconnected, p2pInfo would be cleared.
+ // group is still valid for detecting whether this device is group owner.
+ if (group == null || !group.isGroupOwner()
+ || TextUtils.isEmpty(group.getInterface())) return;
+
+ synchronized (Tethering.this.mPublicSync) {
+ // Enter below only if this device is Group Owner with a valid interface.
+ if (p2pInfo.groupFormed) {
+ TetherState tetherState = mTetherStates.get(group.getInterface());
+ if (tetherState == null
+ || (tetherState.lastState != IpServer.STATE_TETHERED
+ && tetherState.lastState != IpServer.STATE_LOCAL_ONLY)) {
+ enableWifiIpServingLocked(group.getInterface(), IFACE_IP_MODE_LOCAL_ONLY);
+ }
+ } else {
+ disableWifiP2pIpServingLocked(group.getInterface());
+ }
+ }
+ }
}
@VisibleForTesting
@@ -830,14 +872,11 @@ public class Tethering extends BaseNetworkObserver {
}
}
- private void disableWifiIpServingLocked(String ifname, int apState) {
- mLog.log("Canceling WiFi tethering request - AP_STATE=" + apState);
-
- // Regardless of whether we requested this transition, the AP has gone
- // down. Don't try to tether again unless we're requested to do so.
- // TODO: Remove this altogether, once Wi-Fi reliably gives us an
- // interface name with every broadcast.
- mWifiTetherRequested = false;
+ private void disableWifiIpServingLockedCommon(int tetheringType, String ifname, int apState) {
+ mLog.log("Canceling WiFi tethering request -"
+ + " type=" + tetheringType
+ + " interface=" + ifname
+ + " state=" + apState);
if (!TextUtils.isEmpty(ifname)) {
final TetherState ts = mTetherStates.get(ifname);
@@ -849,7 +888,7 @@ public class Tethering extends BaseNetworkObserver {
for (int i = 0; i < mTetherStates.size(); i++) {
final IpServer ipServer = mTetherStates.valueAt(i).ipServer;
- if (ipServer.interfaceType() == TETHERING_WIFI) {
+ if (ipServer.interfaceType() == tetheringType) {
ipServer.unwanted();
return;
}
@@ -860,6 +899,20 @@ public class Tethering extends BaseNetworkObserver {
: "specified interface: " + ifname));
}
+ private void disableWifiIpServingLocked(String ifname, int apState) {
+ // Regardless of whether we requested this transition, the AP has gone
+ // down. Don't try to tether again unless we're requested to do so.
+ // TODO: Remove this altogether, once Wi-Fi reliably gives us an
+ // interface name with every broadcast.
+ mWifiTetherRequested = false;
+
+ disableWifiIpServingLockedCommon(TETHERING_WIFI, ifname, apState);
+ }
+
+ private void disableWifiP2pIpServingLocked(String ifname) {
+ disableWifiIpServingLockedCommon(TETHERING_WIFI_P2P, ifname, /* dummy */ 0);
+ }
+
private void enableWifiIpServingLocked(String ifname, int wifiIpMode) {
// Map wifiIpMode values to IpServer.Callback serving states, inferring
// from mWifiTetherRequested as a final "best guess".
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 7b3eae14c97a..6b70e5f00330 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -199,13 +199,22 @@ public class Vpn {
*/
private @NonNull List<String> mLockdownWhitelist = Collections.emptyList();
- /**
- * List of UIDs for which networking should be blocked until VPN is ready, during brief periods
- * when VPN is not running. For example, during system startup or after a crash.
+ /**
+ * A memory of what UIDs this class told netd to block for the lockdown feature.
+ *
+ * Netd maintains ranges of UIDs for which network should be restricted to using only the VPN
+ * for the lockdown feature. This class manages these UIDs and sends this information to netd.
+ * To avoid sending the same commands multiple times (which would be wasteful) and to be able
+ * to revoke lists (when the rules should change), it's simplest to keep this cache of what
+ * netd knows, so it can be diffed and sent most efficiently.
+ *
+ * The contents of this list must only be changed when updating the UIDs lists with netd,
+ * since it needs to keep in sync with the picture netd has of them.
+ *
* @see mLockdown
*/
@GuardedBy("this")
- private Set<UidRange> mBlockedUsers = new ArraySet<>();
+ private final Set<UidRange> mBlockedUidsAsToldToNetd = new ArraySet<>();
// Handle of the user initiating VPN.
private final int mUserHandle;
@@ -254,7 +263,7 @@ public class Vpn {
}
/**
- * Update current state, dispaching event to listeners.
+ * Update current state, dispatching event to listeners.
*/
@VisibleForTesting
protected void updateState(DetailedState detailedState, String reason) {
@@ -1325,7 +1334,7 @@ public class Vpn {
* {@link Vpn} goes through a VPN connection or is blocked until one is
* available, {@code false} to lift the requirement.
*
- * @see #mBlockedUsers
+ * @see #mBlockedUidsAsToldToNetd
*/
@GuardedBy("this")
private void setVpnForcedLocked(boolean enforce) {
@@ -1336,37 +1345,47 @@ public class Vpn {
exemptedPackages = new ArrayList<>(mLockdownWhitelist);
exemptedPackages.add(mPackage);
}
- final Set<UidRange> removedRanges = new ArraySet<>(mBlockedUsers);
+ final Set<UidRange> rangesToTellNetdToRemove = new ArraySet<>(mBlockedUidsAsToldToNetd);
- Set<UidRange> addedRanges = Collections.emptySet();
+ final Set<UidRange> rangesToTellNetdToAdd;
if (enforce) {
- addedRanges = createUserAndRestrictedProfilesRanges(mUserHandle,
- /* allowedApplications */ null,
- /* disallowedApplications */ exemptedPackages);
+ final Set<UidRange> rangesThatShouldBeBlocked =
+ createUserAndRestrictedProfilesRanges(mUserHandle,
+ /* allowedApplications */ null,
+ /* disallowedApplications */ exemptedPackages);
// The UID range of the first user (0-99999) would block the IPSec traffic, which comes
// directly from the kernel and is marked as uid=0. So we adjust the range to allow
// it through (b/69873852).
- for (UidRange range : addedRanges) {
+ for (UidRange range : rangesThatShouldBeBlocked) {
if (range.start == 0) {
- addedRanges.remove(range);
+ rangesThatShouldBeBlocked.remove(range);
if (range.stop != 0) {
- addedRanges.add(new UidRange(1, range.stop));
+ rangesThatShouldBeBlocked.add(new UidRange(1, range.stop));
}
}
}
- removedRanges.removeAll(addedRanges);
- addedRanges.removeAll(mBlockedUsers);
+ rangesToTellNetdToRemove.removeAll(rangesThatShouldBeBlocked);
+ rangesToTellNetdToAdd = rangesThatShouldBeBlocked;
+ // The ranges to tell netd to add are the ones that should be blocked minus the
+ // ones it already knows to block. Note that this will change the contents of
+ // rangesThatShouldBeBlocked, but the list of ranges that should be blocked is
+ // not used after this so it's fine to destroy it.
+ rangesToTellNetdToAdd.removeAll(mBlockedUidsAsToldToNetd);
+ } else {
+ rangesToTellNetdToAdd = Collections.emptySet();
}
- setAllowOnlyVpnForUids(false, removedRanges);
- setAllowOnlyVpnForUids(true, addedRanges);
+ // If mBlockedUidsAsToldToNetd used to be empty, this will always be a no-op.
+ setAllowOnlyVpnForUids(false, rangesToTellNetdToRemove);
+ // If nothing should be blocked now, this will now be a no-op.
+ setAllowOnlyVpnForUids(true, rangesToTellNetdToAdd);
}
/**
- * Either add or remove a list of {@link UidRange}s to the list of UIDs that are only allowed
- * to make connections through sockets that have had {@code protect()} called on them.
+ * Tell netd to add or remove a list of {@link UidRange}s to the list of UIDs that are only
+ * allowed to make connections through sockets that have had {@code protect()} called on them.
*
* @param enforce {@code true} to add to the blacklist, {@code false} to remove.
* @param ranges {@link Collection} of {@link UidRange}s to add (if {@param enforce} is
@@ -1388,9 +1407,9 @@ public class Vpn {
return false;
}
if (enforce) {
- mBlockedUsers.addAll(ranges);
+ mBlockedUidsAsToldToNetd.addAll(ranges);
} else {
- mBlockedUsers.removeAll(ranges);
+ mBlockedUidsAsToldToNetd.removeAll(ranges);
}
return true;
}
@@ -1557,17 +1576,18 @@ public class Vpn {
/**
* @param uid The target uid.
*
- * @return {@code true} if {@code uid} is included in one of the mBlockedUsers ranges and the
- * VPN is not connected, or if the VPN is connected but does not apply to the {@code uid}.
+ * @return {@code true} if {@code uid} is included in one of the mBlockedUidsAsToldToNetd
+ * ranges and the VPN is not connected, or if the VPN is connected but does not apply to
+ * the {@code uid}.
*
* @apiNote This method don't check VPN lockdown status.
- * @see #mBlockedUsers
+ * @see #mBlockedUidsAsToldToNetd
*/
public synchronized boolean isBlockingUid(int uid) {
if (mNetworkInfo.isConnected()) {
return !appliesToUid(uid);
} else {
- return UidRange.containsUid(mBlockedUsers, uid);
+ return UidRange.containsUid(mBlockedUidsAsToldToNetd, uid);
}
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
index 6ddbf10ab7c7..5ffd766a3503 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -28,6 +28,7 @@ import static com.android.internal.R.array.config_tether_bluetooth_regexs;
import static com.android.internal.R.array.config_tether_dhcp_range;
import static com.android.internal.R.array.config_tether_upstream_types;
import static com.android.internal.R.array.config_tether_usb_regexs;
+import static com.android.internal.R.array.config_tether_wifi_p2p_regexs;
import static com.android.internal.R.array.config_tether_wifi_regexs;
import static com.android.internal.R.bool.config_tether_upstream_automatic;
import static com.android.internal.R.integer.config_mobile_hotspot_provision_check_period;
@@ -86,6 +87,7 @@ public class TetheringConfiguration {
public final String[] tetherableUsbRegexs;
public final String[] tetherableWifiRegexs;
+ public final String[] tetherableWifiP2pRegexs;
public final String[] tetherableBluetoothRegexs;
public final boolean isDunRequired;
public final boolean chooseUpstreamAutomatically;
@@ -116,6 +118,7 @@ public class TetheringConfiguration {
} else {
tetherableWifiRegexs = getResourceStringArray(res, config_tether_wifi_regexs);
}
+ tetherableWifiP2pRegexs = getResourceStringArray(res, config_tether_wifi_p2p_regexs);
tetherableBluetoothRegexs = getResourceStringArray(res, config_tether_bluetooth_regexs);
isDunRequired = checkDunRequired(ctx, subId);
@@ -152,6 +155,15 @@ public class TetheringConfiguration {
return matchesDownstreamRegexs(iface, tetherableWifiRegexs);
}
+ /** Check whether this interface is Wifi P2P interface. */
+ public boolean isWifiP2p(String iface) {
+ return matchesDownstreamRegexs(iface, tetherableWifiP2pRegexs);
+ }
+
+ public boolean isWifiP2pLegacyTetheringMode() {
+ return (tetherableWifiP2pRegexs == null || tetherableWifiP2pRegexs.length == 0);
+ }
+
public boolean isBluetooth(String iface) {
return matchesDownstreamRegexs(iface, tetherableBluetoothRegexs);
}
@@ -166,6 +178,7 @@ public class TetheringConfiguration {
dumpStringArray(pw, "tetherableUsbRegexs", tetherableUsbRegexs);
dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs);
+ dumpStringArray(pw, "tetherableWifiP2pRegexs", tetherableWifiP2pRegexs);
dumpStringArray(pw, "tetherableBluetoothRegexs", tetherableBluetoothRegexs);
pw.print("isDunRequired: ");
@@ -192,6 +205,7 @@ public class TetheringConfiguration {
sj.add(String.format("subId:%d", subId));
sj.add(String.format("tetherableUsbRegexs:%s", makeString(tetherableUsbRegexs)));
sj.add(String.format("tetherableWifiRegexs:%s", makeString(tetherableWifiRegexs)));
+ sj.add(String.format("tetherableWifiP2pRegexs:%s", makeString(tetherableWifiP2pRegexs)));
sj.add(String.format("tetherableBluetoothRegexs:%s",
makeString(tetherableBluetoothRegexs)));
sj.add(String.format("isDunRequired:%s", isDunRequired));
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index c46fc20b3cc8..29026e8affcf 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -114,6 +114,8 @@ final class ColorFade {
private final FloatBuffer mVertexBuffer = createNativeFloatBuffer(8);
private final FloatBuffer mTexCoordBuffer = createNativeFloatBuffer(8);
+ private final Transaction mTransaction = new Transaction();
+
/**
* Animates an color fade warming up.
*/
@@ -659,14 +661,10 @@ final class ColorFade {
private boolean showSurface(float alpha) {
if (!mSurfaceVisible || mSurfaceAlpha != alpha) {
- SurfaceControl.openTransaction();
- try {
- mSurfaceControl.setLayer(COLOR_FADE_LAYER);
- mSurfaceControl.setAlpha(alpha);
- mSurfaceControl.show();
- } finally {
- SurfaceControl.closeTransaction();
- }
+ mTransaction.setLayer(mSurfaceControl, COLOR_FADE_LAYER)
+ .setAlpha(mSurfaceControl, alpha)
+ .show(mSurfaceControl)
+ .apply();
mSurfaceVisible = true;
mSurfaceAlpha = alpha;
}
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 1fc0db3ff7cb..d24bd1aad1b1 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -49,8 +49,9 @@ import android.view.DisplayInfo;
import com.android.internal.os.BackgroundThread;
import com.android.internal.R;
+import com.android.server.display.utils.AmbientFilter;
+import com.android.server.display.utils.AmbientFilterFactory;
import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory;
-import com.android.server.display.whitebalance.AmbientFilter;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -970,7 +971,7 @@ public class DisplayModeDirector {
if (lightSensor != null) {
final Resources res = mContext.getResources();
- mAmbientFilter = DisplayWhiteBalanceFactory.createBrightnessFilter(res);
+ mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, res);
mLightSensor = lightSensor;
onScreenOn(isDefaultDisplayOn());
diff --git a/services/core/java/com/android/server/display/utils/AmbientFilter.java b/services/core/java/com/android/server/display/utils/AmbientFilter.java
new file mode 100644
index 000000000000..1a8412180c27
--- /dev/null
+++ b/services/core/java/com/android/server/display/utils/AmbientFilter.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.utils;
+
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+/**
+ * The DisplayWhiteBalanceController uses the AmbientFilter to average ambient changes over time,
+ * filter out the noise, and arrive at an estimate of the actual value.
+ *
+ * When the DisplayWhiteBalanceController detects a change in ambient brightness or color
+ * temperature, it passes it to the AmbientFilter, and when it needs the actual ambient value, it
+ * asks it for an estimate.
+ *
+ * Implementations:
+ * - {@link WeightedMovingAverageAmbientFilter}
+ * A weighted average prioritising recent changes.
+ */
+abstract public class AmbientFilter {
+
+ protected static final boolean DEBUG = false; // Enable for verbose logs.
+
+ protected final String mTag;
+ protected boolean mLoggingEnabled;
+
+ // How long ambient value changes are kept and taken into consideration.
+ private final int mHorizon; // Milliseconds
+
+ private final RollingBuffer mBuffer;
+
+ /**
+ * @param tag
+ * The tag used for dumping and logging.
+ * @param horizon
+ * How long ambient value changes are kept and taken into consideration.
+ *
+ * @throws IllegalArgumentException
+ * - horizon is not positive.
+ */
+ AmbientFilter(String tag, int horizon) {
+ validateArguments(horizon);
+ mTag = tag;
+ mLoggingEnabled = false;
+ mHorizon = horizon;
+ mBuffer = new RollingBuffer();
+ }
+
+ /**
+ * Add an ambient value change.
+ *
+ * @param time
+ * The time.
+ * @param value
+ * The ambient value.
+ *
+ * @return Whether the method succeeded or not.
+ */
+ public boolean addValue(long time, float value) {
+ if (value < 0.0f) {
+ return false;
+ }
+ truncateOldValues(time);
+ if (mLoggingEnabled) {
+ Slog.d(mTag, "add value: " + value + " @ " + time);
+ }
+ mBuffer.add(time, value);
+ return true;
+ }
+
+ /**
+ * Get an estimate of the actual ambient color temperature.
+ *
+ * @param time
+ * The time.
+ *
+ * @return An estimate of the actual ambient color temperature.
+ */
+ public float getEstimate(long time) {
+ truncateOldValues(time);
+ final float value = filter(time, mBuffer);
+ if (mLoggingEnabled) {
+ Slog.d(mTag, "get estimate: " + value + " @ " + time);
+ }
+ return value;
+ }
+
+ /**
+ * Clears the filter state.
+ */
+ public void clear() {
+ mBuffer.clear();
+ }
+
+ /**
+ * Enable/disable logging.
+ *
+ * @param loggingEnabled
+ * Whether logging is on/off.
+ *
+ * @return Whether the method succeeded or not.
+ */
+ public boolean setLoggingEnabled(boolean loggingEnabled) {
+ if (mLoggingEnabled == loggingEnabled) {
+ return false;
+ }
+ mLoggingEnabled = loggingEnabled;
+ return true;
+ }
+
+ /**
+ * Dump the state.
+ *
+ * @param writer
+ * The PrintWriter used to dump the state.
+ */
+ public void dump(PrintWriter writer) {
+ writer.println(" " + mTag);
+ writer.println(" mLoggingEnabled=" + mLoggingEnabled);
+ writer.println(" mHorizon=" + mHorizon);
+ writer.println(" mBuffer=" + mBuffer);
+ }
+
+ private void validateArguments(int horizon) {
+ if (horizon <= 0) {
+ throw new IllegalArgumentException("horizon must be positive");
+ }
+ }
+
+ private void truncateOldValues(long time) {
+ final long minTime = time - mHorizon;
+ mBuffer.truncate(minTime);
+ }
+
+ protected abstract float filter(long time, RollingBuffer buffer);
+
+ /**
+ * A weighted average prioritising recent changes.
+ */
+ static class WeightedMovingAverageAmbientFilter extends AmbientFilter {
+
+ // How long the latest ambient value change is predicted to last.
+ private static final int PREDICTION_TIME = 100; // Milliseconds
+
+ // Recent changes are prioritised by integrating their duration over y = x + mIntercept
+ // (the higher it is, the less prioritised recent changes are).
+ private final float mIntercept;
+
+ /**
+ * @param tag
+ * The tag used for dumping and logging.
+ * @param horizon
+ * How long ambient value changes are kept and taken into consideration.
+ * @param intercept
+ * Recent changes are prioritised by integrating their duration over y = x + intercept
+ * (the higher it is, the less prioritised recent changes are).
+ *
+ * @throws IllegalArgumentException
+ * - horizon is not positive.
+ * - intercept is NaN or negative.
+ */
+ WeightedMovingAverageAmbientFilter(String tag, int horizon, float intercept) {
+ super(tag, horizon);
+ validateArguments(intercept);
+ mIntercept = intercept;
+ }
+
+ /**
+ * See {@link AmbientFilter#dump base class}.
+ */
+ @Override
+ public void dump(PrintWriter writer) {
+ super.dump(writer);
+ writer.println(" mIntercept=" + mIntercept);
+ }
+
+ // Normalise the times to [t1=0, t2, ..., tN, now + PREDICTION_TIME], so the first change
+ // starts at 0 and the last change is predicted to last a bit, and divide them by 1000 as
+ // milliseconds are high enough to overflow.
+ // The weight of the value from t[i] to t[i+1] is the area under (A.K.A. the integral of)
+ // y = x + mIntercept from t[i] to t[i+1].
+ @Override
+ protected float filter(long time, RollingBuffer buffer) {
+ if (buffer.isEmpty()) {
+ return -1.0f;
+ }
+ float total = 0.0f;
+ float totalWeight = 0.0f;
+ final float[] weights = getWeights(time, buffer);
+ if (DEBUG && mLoggingEnabled) {
+ Slog.v(mTag, "filter: " + buffer + " => " + Arrays.toString(weights));
+ }
+ for (int i = 0; i < weights.length; i++) {
+ final float value = buffer.getValue(i);
+ final float weight = weights[i];
+ total += weight * value;
+ totalWeight += weight;
+ }
+ if (totalWeight == 0.0f) {
+ return buffer.getValue(buffer.size() - 1);
+ }
+ return total / totalWeight;
+ }
+
+ private void validateArguments(float intercept) {
+ if (Float.isNaN(intercept) || intercept < 0.0f) {
+ throw new IllegalArgumentException("intercept must be a non-negative number");
+ }
+ }
+
+ private float[] getWeights(long time, RollingBuffer buffer) {
+ float[] weights = new float[buffer.size()];
+ final long startTime = buffer.getTime(0);
+ float previousTime = 0.0f;
+ for (int i = 1; i < weights.length; i++) {
+ final float currentTime = (buffer.getTime(i) - startTime) / 1000.0f;
+ final float weight = calculateIntegral(previousTime, currentTime);
+ weights[i - 1] = weight;
+ previousTime = currentTime;
+ }
+ final float lastTime = (time + PREDICTION_TIME - startTime) / 1000.0f;
+ final float lastWeight = calculateIntegral(previousTime, lastTime);
+ weights[weights.length - 1] = lastWeight;
+ return weights;
+ }
+
+ private float calculateIntegral(float from, float to) {
+ return antiderivative(to) - antiderivative(from);
+ }
+
+ private float antiderivative(float x) {
+ // f(x) = x + c => F(x) = 1/2 * x^2 + c * x
+ return 0.5f * x * x + mIntercept * x;
+ }
+
+ }
+
+}
diff --git a/services/core/java/com/android/server/display/utils/AmbientFilterFactory.java b/services/core/java/com/android/server/display/utils/AmbientFilterFactory.java
new file mode 100644
index 000000000000..dfa1ddc67528
--- /dev/null
+++ b/services/core/java/com/android/server/display/utils/AmbientFilterFactory.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.utils;
+
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.util.TypedValue;
+
+public class AmbientFilterFactory {
+ /**
+ * Creates a temporal filter which functions as a weighted moving average buffer for recent
+ * sensor values.
+ * @param tag
+ * The tag used for dumping and logging.
+ * @param horizon
+ * How long ambient value changes are kept and taken into consideration.
+ * @param intercept
+ * Recent changes are prioritised by integrating their duration over y = x + intercept
+ * (the higher it is, the less prioritised recent changes are).
+ *
+ * @return
+ * An AmbientFiler.
+ *
+ * @throws IllegalArgumentException
+ * - Horizon is not positive.
+ * - Intercept not configured.
+ */
+ public static AmbientFilter createAmbientFilter(String tag, int horizon, float intercept) {
+ if (!Float.isNaN(intercept)) {
+ return new AmbientFilter.WeightedMovingAverageAmbientFilter(tag, horizon, intercept);
+ }
+ throw new IllegalArgumentException("missing configurations: "
+ + "expected config_displayWhiteBalanceBrightnessFilterIntercept");
+ }
+
+ /**
+ * Helper to create a default BrightnessFilter which has configuration in the resource file.
+ * @param tag
+ * The tag used for dumping and logging.
+ * @param resources
+ * The resources used to configure the various components.
+ *
+ * @return
+ * An AmbientFilter.
+ */
+ public static AmbientFilter createBrightnessFilter(String tag, Resources resources) {
+ final int horizon = resources.getInteger(
+ com.android.internal.R.integer.config_displayWhiteBalanceBrightnessFilterHorizon);
+ final float intercept = getFloat(resources,
+ com.android.internal.R.dimen.config_displayWhiteBalanceBrightnessFilterIntercept);
+
+ return createAmbientFilter(tag, horizon, intercept);
+ }
+
+ /**
+ * Helper to creates a default ColorTemperatureFilter which has configuration in the resource
+ * file.
+ * @param tag
+ * The tag used for dumping and logging.
+ * @param resources
+ * The resources used to configure the various components.
+ *
+ * @return
+ * An AmbientFilter.
+ */
+ public static AmbientFilter createColorTemperatureFilter(String tag, Resources resources) {
+ final int horizon = resources.getInteger(
+ com.android.internal.R.integer
+ .config_displayWhiteBalanceColorTemperatureFilterHorizon);
+ final float intercept = getFloat(resources,
+ com.android.internal.R.dimen
+ .config_displayWhiteBalanceColorTemperatureFilterIntercept);
+
+ return createAmbientFilter(tag, horizon, intercept);
+ }
+
+ // Instantiation is disabled.
+ private AmbientFilterFactory() { }
+
+ private static float getFloat(Resources resources, int id) {
+ TypedValue value = new TypedValue();
+
+ resources.getValue(id, value, true /* resolveRefs */);
+ if (value.type != TypedValue.TYPE_FLOAT) {
+ return Float.NaN;
+ }
+
+ return value.getFloat();
+ }
+}
+
diff --git a/services/core/java/com/android/server/display/whitebalance/AmbientFilter.java b/services/core/java/com/android/server/display/whitebalance/AmbientFilter.java
deleted file mode 100644
index 35808974b9e4..000000000000
--- a/services/core/java/com/android/server/display/whitebalance/AmbientFilter.java
+++ /dev/null
@@ -1,257 +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.display.whitebalance;
-
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.display.utils.RollingBuffer;
-
-import java.io.PrintWriter;
-import java.util.Arrays;
-
-/**
- * The DisplayWhiteBalanceController uses the AmbientFilter to average ambient changes over time,
- * filter out the noise, and arrive at an estimate of the actual value.
- *
- * When the DisplayWhiteBalanceController detects a change in ambient brightness or color
- * temperature, it passes it to the AmbientFilter, and when it needs the actual ambient value, it
- * asks it for an estimate.
- *
- * Implementations:
- * - {@link WeightedMovingAverageAmbientFilter}
- * A weighted average prioritising recent changes.
- */
-abstract public class AmbientFilter {
-
- protected static final boolean DEBUG = false; // Enable for verbose logs.
-
- protected final String mTag;
- protected boolean mLoggingEnabled;
-
- // How long ambient value changes are kept and taken into consideration.
- private final int mHorizon; // Milliseconds
-
- private final RollingBuffer mBuffer;
-
- /**
- * @param tag
- * The tag used for dumping and logging.
- * @param horizon
- * How long ambient value changes are kept and taken into consideration.
- *
- * @throws IllegalArgumentException
- * - horizon is not positive.
- */
- AmbientFilter(String tag, int horizon) {
- validateArguments(horizon);
- mTag = tag;
- mLoggingEnabled = false;
- mHorizon = horizon;
- mBuffer = new RollingBuffer();
- }
-
- /**
- * Add an ambient value change.
- *
- * @param time
- * The time.
- * @param value
- * The ambient value.
- *
- * @return Whether the method succeeded or not.
- */
- public boolean addValue(long time, float value) {
- if (value < 0.0f) {
- return false;
- }
- truncateOldValues(time);
- if (mLoggingEnabled) {
- Slog.d(mTag, "add value: " + value + " @ " + time);
- }
- mBuffer.add(time, value);
- return true;
- }
-
- /**
- * Get an estimate of the actual ambient color temperature.
- *
- * @param time
- * The time.
- *
- * @return An estimate of the actual ambient color temperature.
- */
- public float getEstimate(long time) {
- truncateOldValues(time);
- final float value = filter(time, mBuffer);
- if (mLoggingEnabled) {
- Slog.d(mTag, "get estimate: " + value + " @ " + time);
- }
- return value;
- }
-
- /**
- * Clears the filter state.
- */
- public void clear() {
- mBuffer.clear();
- }
-
- /**
- * Enable/disable logging.
- *
- * @param loggingEnabled
- * Whether logging is on/off.
- *
- * @return Whether the method succeeded or not.
- */
- public boolean setLoggingEnabled(boolean loggingEnabled) {
- if (mLoggingEnabled == loggingEnabled) {
- return false;
- }
- mLoggingEnabled = loggingEnabled;
- return true;
- }
-
- /**
- * Dump the state.
- *
- * @param writer
- * The PrintWriter used to dump the state.
- */
- public void dump(PrintWriter writer) {
- writer.println(" " + mTag);
- writer.println(" mLoggingEnabled=" + mLoggingEnabled);
- writer.println(" mHorizon=" + mHorizon);
- writer.println(" mBuffer=" + mBuffer);
- }
-
- private void validateArguments(int horizon) {
- if (horizon <= 0) {
- throw new IllegalArgumentException("horizon must be positive");
- }
- }
-
- private void truncateOldValues(long time) {
- final long minTime = time - mHorizon;
- mBuffer.truncate(minTime);
- }
-
- protected abstract float filter(long time, RollingBuffer buffer);
-
- /**
- * A weighted average prioritising recent changes.
- */
- static class WeightedMovingAverageAmbientFilter extends AmbientFilter {
-
- // How long the latest ambient value change is predicted to last.
- private static final int PREDICTION_TIME = 100; // Milliseconds
-
- // Recent changes are prioritised by integrating their duration over y = x + mIntercept
- // (the higher it is, the less prioritised recent changes are).
- private final float mIntercept;
-
- /**
- * @param tag
- * The tag used for dumping and logging.
- * @param horizon
- * How long ambient value changes are kept and taken into consideration.
- * @param intercept
- * Recent changes are prioritised by integrating their duration over y = x + intercept
- * (the higher it is, the less prioritised recent changes are).
- *
- * @throws IllegalArgumentException
- * - horizon is not positive.
- * - intercept is NaN or negative.
- */
- WeightedMovingAverageAmbientFilter(String tag, int horizon, float intercept) {
- super(tag, horizon);
- validateArguments(intercept);
- mIntercept = intercept;
- }
-
- /**
- * See {@link AmbientFilter#dump base class}.
- */
- @Override
- public void dump(PrintWriter writer) {
- super.dump(writer);
- writer.println(" mIntercept=" + mIntercept);
- }
-
- // Normalise the times to [t1=0, t2, ..., tN, now + PREDICTION_TIME], so the first change
- // starts at 0 and the last change is predicted to last a bit, and divide them by 1000 as
- // milliseconds are high enough to overflow.
- // The weight of the value from t[i] to t[i+1] is the area under (A.K.A. the integral of)
- // y = x + mIntercept from t[i] to t[i+1].
- @Override
- protected float filter(long time, RollingBuffer buffer) {
- if (buffer.isEmpty()) {
- return -1.0f;
- }
- float total = 0.0f;
- float totalWeight = 0.0f;
- final float[] weights = getWeights(time, buffer);
- if (DEBUG && mLoggingEnabled) {
- Slog.v(mTag, "filter: " + buffer + " => " + Arrays.toString(weights));
- }
- for (int i = 0; i < weights.length; i++) {
- final float value = buffer.getValue(i);
- final float weight = weights[i];
- total += weight * value;
- totalWeight += weight;
- }
- if (totalWeight == 0.0f) {
- return buffer.getValue(buffer.size() - 1);
- }
- return total / totalWeight;
- }
-
- private void validateArguments(float intercept) {
- if (Float.isNaN(intercept) || intercept < 0.0f) {
- throw new IllegalArgumentException("intercept must be a non-negative number");
- }
- }
-
- private float[] getWeights(long time, RollingBuffer buffer) {
- float[] weights = new float[buffer.size()];
- final long startTime = buffer.getTime(0);
- float previousTime = 0.0f;
- for (int i = 1; i < weights.length; i++) {
- final float currentTime = (buffer.getTime(i) - startTime) / 1000.0f;
- final float weight = calculateIntegral(previousTime, currentTime);
- weights[i - 1] = weight;
- previousTime = currentTime;
- }
- final float lastTime = (time + PREDICTION_TIME - startTime) / 1000.0f;
- final float lastWeight = calculateIntegral(previousTime, lastTime);
- weights[weights.length - 1] = lastWeight;
- return weights;
- }
-
- private float calculateIntegral(float from, float to) {
- return antiderivative(to) - antiderivative(from);
- }
-
- private float antiderivative(float x) {
- // f(x) = x + c => F(x) = 1/2 * x^2 + c * x
- return 0.5f * x * x + mIntercept * x;
- }
-
- }
-
-}
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
index 7b1f4c3222f3..88a7077ac37a 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
@@ -24,6 +24,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
+import com.android.server.display.utils.AmbientFilter;
import com.android.server.display.utils.History;
import java.io.PrintWriter;
@@ -63,7 +64,13 @@ public class DisplayWhiteBalanceController implements
AmbientFilter mColorTemperatureFilter;
private DisplayWhiteBalanceThrottler mThrottler;
+ // In low brightness conditions the ALS readings are more noisy and produce
+ // high errors. This default is introduced to provide a fixed display color
+ // temperature when sensor readings become unreliable.
private final float mLowLightAmbientColorTemperature;
+ // In high brightness conditions certain color temperatures can cause peak display
+ // brightness to drop. This fixed color temperature can be used to compensate for
+ // this effect.
private final float mHighLightAmbientColorTemperature;
private float mAmbientColorTemperature;
@@ -84,12 +91,14 @@ public class DisplayWhiteBalanceController implements
// A piecewise linear relationship between ambient and display color temperatures.
private Spline.LinearSpline mAmbientToDisplayColorTemperatureSpline;
- // In very low or very high brightness conditions ambient EQ should to set to a default
- // instead of using mAmbientToDisplayColorTemperatureSpline. However, setting ambient EQ
- // based on thresholds can cause the display to rapidly change color temperature. To solve
- // this, mLowLightAmbientBrightnessToBiasSpline and mHighLightAmbientBrightnessToBiasSpline
- // are used to smoothly interpolate from ambient color temperature to the defaults.
- // A piecewise linear relationship between low light brightness and low light bias.
+ // In very low or very high brightness conditions Display White Balance should
+ // be to set to a default instead of using mAmbientToDisplayColorTemperatureSpline.
+ // However, setting Display White Balance based on thresholds can cause the
+ // display to rapidly change color temperature. To solve this,
+ // mLowLightAmbientBrightnessToBiasSpline and
+ // mHighLightAmbientBrightnessToBiasSpline are used to smoothly interpolate from
+ // ambient color temperature to the defaults. A piecewise linear relationship
+ // between low light brightness and low light bias.
private Spline.LinearSpline mLowLightAmbientBrightnessToBiasSpline;
// A piecewise linear relationship between high light brightness and high light bias.
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
index bf0a1d16219d..a72b1ed8f3ec 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
@@ -23,6 +23,8 @@ import android.os.Handler;
import android.util.TypedValue;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.utils.AmbientFilter;
+import com.android.server.display.utils.AmbientFilterFactory;
/**
* The DisplayWhiteBalanceFactory creates and configures an DisplayWhiteBalanceController.
@@ -58,10 +60,12 @@ public class DisplayWhiteBalanceFactory {
SensorManager sensorManager, Resources resources) {
final AmbientSensor.AmbientBrightnessSensor brightnessSensor =
createBrightnessSensor(handler, sensorManager, resources);
- final AmbientFilter brightnessFilter = createBrightnessFilter(resources);
+ final AmbientFilter brightnessFilter =
+ AmbientFilterFactory.createBrightnessFilter(BRIGHTNESS_FILTER_TAG, resources);
final AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor =
createColorTemperatureSensor(handler, sensorManager, resources);
- final AmbientFilter colorTemperatureFilter = createColorTemperatureFilter(resources);
+ final AmbientFilter colorTemperatureFilter = AmbientFilterFactory
+ .createColorTemperatureFilter(COLOR_TEMPERATURE_FILTER_TAG, resources);
final DisplayWhiteBalanceThrottler throttler = createThrottler(resources);
final float[] displayWhiteBalanceLowLightAmbientBrightnesses = getFloatArray(resources,
com.android.internal.R.array
@@ -112,23 +116,6 @@ public class DisplayWhiteBalanceFactory {
}
/**
- * Creates a BrightnessFilter which functions as a weighted moving average buffer for recent
- * brightness values.
- */
- public static AmbientFilter createBrightnessFilter(Resources resources) {
- final int horizon = resources.getInteger(
- com.android.internal.R.integer.config_displayWhiteBalanceBrightnessFilterHorizon);
- final float intercept = getFloat(resources,
- com.android.internal.R.dimen.config_displayWhiteBalanceBrightnessFilterIntercept);
- if (!Float.isNaN(intercept)) {
- return new AmbientFilter.WeightedMovingAverageAmbientFilter(
- BRIGHTNESS_FILTER_TAG, horizon, intercept);
- }
- throw new IllegalArgumentException("missing configurations: "
- + "expected config_displayWhiteBalanceBrightnessFilterIntercept");
- }
-
- /**
* Creates an ambient color sensor instance to redirect sensor data to callbacks.
*/
@VisibleForTesting
@@ -143,21 +130,6 @@ public class DisplayWhiteBalanceFactory {
return new AmbientSensor.AmbientColorTemperatureSensor(handler, sensorManager, name, rate);
}
- private static AmbientFilter createColorTemperatureFilter(Resources resources) {
- final int horizon = resources.getInteger(
- com.android.internal.R.integer
- .config_displayWhiteBalanceColorTemperatureFilterHorizon);
- final float intercept = getFloat(resources,
- com.android.internal.R.dimen
- .config_displayWhiteBalanceColorTemperatureFilterIntercept);
- if (!Float.isNaN(intercept)) {
- return new AmbientFilter.WeightedMovingAverageAmbientFilter(
- COLOR_TEMPERATURE_FILTER_TAG, horizon, intercept);
- }
- throw new IllegalArgumentException("missing configurations: "
- + "expected config_displayWhiteBalanceColorTemperatureFilterIntercept");
- }
-
private static DisplayWhiteBalanceThrottler createThrottler(Resources resources) {
final int increaseDebounce = resources.getInteger(
com.android.internal.R.integer.config_displayWhiteBalanceDecreaseDebounce);
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index cfbf8bcd6433..648e07a01c1c 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -394,18 +394,10 @@ final class Constants {
static final String PROPERTY_PREFERRED_ADDRESS_PLAYBACK = "persist.sys.hdmi.addr.playback";
static final String PROPERTY_PREFERRED_ADDRESS_TV = "persist.sys.hdmi.addr.tv";
- // Property name for the local device configurations.
- // TODO(OEM): OEM should provide this property, and the value is the comma separated integer
- // values which denotes the device type in HDMI Spec 1.4.
- static final String PROPERTY_DEVICE_TYPE = "ro.hdmi.device_type";
-
// TODO(OEM): Set this to false to keep the playback device in sleep upon hotplug event.
// True by default.
static final String PROPERTY_WAKE_ON_HOTPLUG = "ro.hdmi.wake_on_hotplug";
- // TODO(OEM): Set this to true to enable 'Set Menu Language' feature. False by default.
- static final String PROPERTY_SET_MENU_LANGUAGE = "ro.hdmi.set_menu_language";
-
/**
* Property to save the ARC port id on system audio device.
* <p>When ARC is initiated, this port will be used to turn on ARC.
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
index b4c7dd310dd0..080e6cea8333 100755
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -333,7 +333,8 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction {
current.mPhysicalAddress = HdmiUtils.twoBytesToInt(params);
current.mPortId = getPortId(current.mPhysicalAddress);
current.mDeviceType = params[2] & 0xFF;
- current.mDisplayName = HdmiUtils.getDefaultDeviceName(current.mDeviceType);
+ // Keep display name empty. TIF fallbacks to the service label provided by the package mg.
+ current.mDisplayName = "";
// This is to manager CEC device separately in case they don't have address.
if (mIsTvDevice) {
@@ -359,17 +360,13 @@ final class DeviceDiscoveryAction extends HdmiCecFeatureAction {
return;
}
- String displayName = null;
+ String displayName = "";
try {
- if (cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT) {
- displayName = HdmiUtils.getDefaultDeviceName(current.mLogicalAddress);
- } else {
+ if (cmd.getOpcode() != Constants.MESSAGE_FEATURE_ABORT) {
displayName = new String(cmd.getParams(), "US-ASCII");
}
} catch (UnsupportedEncodingException e) {
Slog.w(TAG, "Failed to decode display name: " + cmd.toString());
- // If failed to get display name, use the default name of device.
- displayName = HdmiUtils.getDefaultDeviceName(current.mLogicalAddress);
}
current.mDisplayName = displayName;
increaseProcessedDeviceCount();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 4f4baab204e6..dde873b5ef97 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -489,7 +489,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
if (oldDevice == null || oldDevice.getPhysicalAddress() != path) {
addCecDevice(new HdmiDeviceInfo(
address, path, mService.pathToPortId(path), type,
- Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(address)));
+ Constants.UNKNOWN_VENDOR_ID, ""));
// if we are adding a new device info, send out a give osd name command
// to update the name of the device in TIF
mService.sendCecCommand(
@@ -526,7 +526,8 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
return true;
}
- if (deviceInfo.getDisplayName().equals(osdName)) {
+ if (deviceInfo.getDisplayName() != null
+ && deviceInfo.getDisplayName().equals(osdName)) {
Slog.d(TAG, "Ignore incoming <Set Osd Name> having same osd name:" + message);
return true;
}
@@ -850,36 +851,25 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
mAddress, Constants.ADDR_BROADCAST, systemAudioStatusOn));
if (systemAudioStatusOn) {
+ // If TV sends out SAM Request with a path of a non-CEC device, which should not show
+ // up in the CEC device list and not under the current AVR device, the AVR would switch
+ // to ARC.
int sourcePhysicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
- if (sourcePhysicalAddress != getActiveSource().physicalAddress) {
- // If the Active Source recorded by the current device is not synced up with TV,
- // update the Active Source internally.
- if (sourcePhysicalAddress == mService.getPhysicalAddress()) {
- // If the active path is the current device itself, update with local info
- if (mService.playback() != null) {
- setActiveSource(mService.playback().mAddress, sourcePhysicalAddress);
- } else {
- setActiveSource(mAddress, sourcePhysicalAddress);
- }
- } else {
- // If it's not the current device, look for the device info from the list
- for (HdmiDeviceInfo info : HdmiUtils.sparseArrayToList(mDeviceInfos)) {
- if (info.getPhysicalAddress() == sourcePhysicalAddress) {
- setActiveSource(info.getLogicalAddress(), info.getPhysicalAddress());
- break;
- }
- }
- }
- // If the Active path from TV's System Audio Mode request does not belong to any
- // device in the local device list, record the new Active physicalAddress with an
- // unregistered logical address first. Then query the Active Source again.
- if (sourcePhysicalAddress != getActiveSource().physicalAddress) {
- setActiveSource(Constants.ADDR_UNREGISTERED, sourcePhysicalAddress);
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildRequestActiveSource(mAddress));
+ if (HdmiUtils.getLocalPortFromPhysicalAddress(
+ sourcePhysicalAddress, getDeviceInfo().getPhysicalAddress())
+ != HdmiUtils.TARGET_NOT_UNDER_LOCAL_DEVICE) {
+ return true;
+ }
+ boolean isDeviceInCecDeviceList = false;
+ for (HdmiDeviceInfo info : HdmiUtils.sparseArrayToList(mDeviceInfos)) {
+ if (info.getPhysicalAddress() == sourcePhysicalAddress) {
+ isDeviceInCecDeviceList = true;
+ break;
}
}
- switchInputOnReceivingNewActivePath(sourcePhysicalAddress);
+ if (!isDeviceInCecDeviceList) {
+ switchInputOnReceivingNewActivePath(sourcePhysicalAddress);
+ }
}
return true;
}
@@ -1249,8 +1239,8 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
}
// Wake up if the current device if ready to route.
mService.wakeUp();
- if (getLocalActivePort() == portId) {
- HdmiLogger.debug("Not switching to the same port " + portId);
+ if ((getLocalActivePort() == portId) && (portId != Constants.CEC_SWITCH_ARC)) {
+ HdmiLogger.debug("Not switching to the same port " + portId + " except for arc");
return;
}
// Switch to HOME if the current active port is not HOME yet
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 413e7a087434..09443242d499 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -24,6 +24,7 @@ import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.SystemProperties;
import android.provider.Settings.Global;
+import android.sysprop.HdmiProperties;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -47,7 +48,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
SystemProperties.getBoolean(Constants.PROPERTY_WAKE_ON_HOTPLUG, true);
private static final boolean SET_MENU_LANGUAGE =
- SystemProperties.getBoolean(Constants.PROPERTY_SET_MENU_LANGUAGE, false);
+ HdmiProperties.set_menu_language().orElse(false);
// Used to keep the device awake while it is the active source. For devices that
// cannot wake up via CEC commands, this address the inconvenience of having to
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 3856de47a522..1794df3b602e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -56,8 +56,8 @@ import android.media.AudioManager;
import android.media.tv.TvInputManager;
import android.media.tv.TvInputManager.TvInputCallback;
import android.net.Uri;
-import android.os.Build;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -68,6 +68,7 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings.Global;
+import android.sysprop.HdmiProperties;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Slog;
@@ -95,6 +96,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
/**
* Provides a service for sending and processing HDMI control messages,
@@ -308,6 +311,11 @@ public class HdmiControlService extends SystemService {
private IHdmiControlCallback mDisplayStatusCallback = null;
@Nullable
+ // Save callback when the device is still under logcial address allocation
+ // Invoke once new local device is ready.
+ private IHdmiControlCallback mOtpCallbackPendingAddressAllocation = null;
+
+ @Nullable
private HdmiCecController mCecController;
// HDMI port information. Stored in the unmodifiable list to keep the static information
@@ -452,7 +460,14 @@ public class HdmiControlService extends SystemService {
public HdmiControlService(Context context) {
super(context);
- mLocalDevices = getIntList(SystemProperties.get(Constants.PROPERTY_DEVICE_TYPE));
+ List<Integer> deviceTypes = HdmiProperties.device_type();
+ if (deviceTypes.contains(null)) {
+ Slog.w(TAG, "Error parsing ro.hdmi.device.type: " + SystemProperties.get(
+ "ro.hdmi.device_type"));
+ deviceTypes = deviceTypes.stream().filter(Objects::nonNull).collect(
+ Collectors.toList());
+ }
+ mLocalDevices = deviceTypes;
mSettingsObserver = new SettingsObserver(mHandler);
}
@@ -775,17 +790,21 @@ public class HdmiControlService extends SystemService {
// Address allocation completed for all devices. Notify each device.
if (allocatingDevices.size() == ++finished[0]) {
mAddressAllocated = true;
- // Reinvoke the saved display status callback once the local device is ready.
- if (mDisplayStatusCallback != null) {
- queryDisplayStatus(mDisplayStatusCallback);
- mDisplayStatusCallback = null;
- }
if (initiatedBy != INITIATED_BY_HOTPLUG) {
// In case of the hotplug we don't call onInitializeCecComplete()
// since we reallocate the logical address only.
onInitializeCecComplete(initiatedBy);
}
notifyAddressAllocated(allocatedDevices, initiatedBy);
+ // Reinvoke the saved display status callback once the local device is ready.
+ if (mDisplayStatusCallback != null) {
+ queryDisplayStatus(mDisplayStatusCallback);
+ mDisplayStatusCallback = null;
+ }
+ if (mOtpCallbackPendingAddressAllocation != null) {
+ oneTouchPlay(mOtpCallbackPendingAddressAllocation);
+ mOtpCallbackPendingAddressAllocation = null;
+ }
mCecMessageBuffer.processMessages();
}
}
@@ -2236,8 +2255,16 @@ public class HdmiControlService extends SystemService {
}
@ServiceThreadOnly
- private void oneTouchPlay(final IHdmiControlCallback callback) {
+ @VisibleForTesting
+ protected void oneTouchPlay(final IHdmiControlCallback callback) {
assertRunOnServiceThread();
+ if (!mAddressAllocated) {
+ mOtpCallbackPendingAddressAllocation = callback;
+ Slog.d(TAG, "Local device is under address allocation. "
+ + "Save OTP callback for later process.");
+ return;
+ }
+
HdmiCecLocalDeviceSource source = playback();
if (source == null) {
source = audioSystem();
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index c8fc5fc96e59..4962af176f18 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -77,7 +77,6 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
sendCommand(HdmiCecMessageBuilder.buildTextViewOn(getSourceAddress(), mTargetAddress));
broadcastActiveSource();
queryDevicePowerStatus();
- mState = STATE_WAITING_FOR_REPORT_POWER_STATUS;
addTimer(mState, HdmiConfig.TIMEOUT_MS);
return true;
}
@@ -99,6 +98,7 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
}
private void queryDevicePowerStatus() {
+ mState = STATE_WAITING_FOR_REPORT_POWER_STATUS;
sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(),
mTargetAddress));
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 75b9705e1045..362955d589af 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -47,6 +47,7 @@ import android.hardware.input.InputManager;
import android.hardware.input.InputManagerInternal;
import android.hardware.input.KeyboardLayout;
import android.hardware.input.TouchCalibration;
+import android.media.AudioManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
@@ -70,7 +71,6 @@ import android.view.Display;
import android.view.IInputFilter;
import android.view.IInputFilterHost;
import android.view.IInputMonitorHost;
-import android.view.IWindow;
import android.view.InputApplicationHandle;
import android.view.InputChannel;
import android.view.InputDevice;
@@ -185,9 +185,6 @@ public class InputManagerService extends IInputManager.Stub
IInputFilter mInputFilter; // guarded by mInputFilterLock
InputFilterHost mInputFilterHost; // guarded by mInputFilterLock
- private IWindow mFocusedWindow;
- private boolean mFocusedWindowHasCapture;
-
private static native long nativeInit(InputManagerService service,
Context context, MessageQueue messageQueue);
private static native void nativeStart(long ptr);
@@ -202,8 +199,7 @@ public class InputManagerService extends IInputManager.Stub
int deviceId, int sourceMask, int sw);
private static native boolean nativeHasKeys(long ptr,
int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists);
- private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel,
- int displayId);
+ private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel);
private static native void nativeRegisterInputMonitor(long ptr, InputChannel inputChannel,
int displayId, boolean isGestureMonitor);
private static native void nativeUnregisterInputChannel(long ptr, InputChannel inputChannel);
@@ -220,6 +216,8 @@ public class InputManagerService extends IInputManager.Stub
private static native void nativeSetFocusedApplication(long ptr,
int displayId, InputApplicationHandle application);
private static native void nativeSetFocusedDisplay(long ptr, int displayId);
+ private static native boolean nativeTransferTouchFocus(long ptr,
+ InputChannel fromChannel, InputChannel toChannel);
private static native void nativeSetPointerSpeed(long ptr, int speed);
private static native void nativeSetShowTouches(long ptr, boolean enabled);
private static native void nativeSetInteractive(long ptr, boolean interactive);
@@ -294,6 +292,9 @@ public class InputManagerService extends IInputManager.Stub
/** Switch code: Camera lens cover. When set the lens is covered. */
public static final int SW_CAMERA_LENS_COVER = 0x09;
+ /** Switch code: Microphone. When set it is off. */
+ public static final int SW_MUTE_DEVICE = 0x0e;
+
public static final int SW_LID_BIT = 1 << SW_LID;
public static final int SW_TABLET_MODE_BIT = 1 << SW_TABLET_MODE;
public static final int SW_KEYPAD_SLIDE_BIT = 1 << SW_KEYPAD_SLIDE;
@@ -304,6 +305,7 @@ public class InputManagerService extends IInputManager.Stub
public static final int SW_JACK_BITS =
SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_JACK_PHYSICAL_INSERT_BIT | SW_LINEOUT_INSERT_BIT;
public static final int SW_CAMERA_LENS_COVER_BIT = 1 << SW_CAMERA_LENS_COVER;
+ public static final int SW_MUTE_DEVICE_BIT = 1 << SW_MUTE_DEVICE;
/** Whether to use the dev/input/event or uevent subsystem for the audio jack. */
final boolean mUseDevInputEventForAudioJack;
@@ -522,7 +524,6 @@ public class InputManagerService extends IInputManager.Stub
throw new IllegalArgumentException("displayId must >= 0.");
}
-
final long ident = Binder.clearCallingIdentity();
try {
InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
@@ -530,29 +531,25 @@ public class InputManagerService extends IInputManager.Stub
inputChannels[0].setToken(host.asBinder());
nativeRegisterInputMonitor(mPtr, inputChannels[0], displayId,
true /*isGestureMonitor*/);
- return new InputMonitor(inputChannelName, inputChannels[1], host);
+ return new InputMonitor(inputChannels[1], host);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
/**
- * Registers an input channel so that it can be used as an input event target.
+ * Registers an input channel so that it can be used as an input event target. The channel is
+ * registered with a generated token.
+ *
* @param inputChannel The input channel to register.
- * @param inputWindowHandle The handle of the input window associated with the
- * input channel, or null if none.
*/
- public void registerInputChannel(InputChannel inputChannel, IBinder token) {
+ public void registerInputChannel(InputChannel inputChannel) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null.");
}
+ inputChannel.setToken(new Binder());
- if (token == null) {
- token = new Binder();
- }
- inputChannel.setToken(token);
-
- nativeRegisterInputChannel(mPtr, inputChannel, Display.INVALID_DISPLAY);
+ nativeRegisterInputChannel(mPtr, inputChannel);
}
/**
@@ -970,6 +967,11 @@ public class InputManagerService extends IInputManager.Stub
}
@Override // Binder call
+ public int isMicMuted() {
+ return getSwitchState(-1, InputDevice.SOURCE_ANY, SW_MUTE_DEVICE);
+ }
+
+ @Override // Binder call
public void registerTabletModeChangedListener(ITabletModeChangedListener listener) {
if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE,
"registerTabletModeChangedListener()")) {
@@ -1501,26 +1503,9 @@ public class InputManagerService extends IInputManager.Stub
@Override
public void requestPointerCapture(IBinder windowToken, boolean enabled) {
- if (mFocusedWindow == null || mFocusedWindow.asBinder() != windowToken) {
- Slog.e(TAG, "requestPointerCapture called for a window that has no focus: "
- + windowToken);
- return;
- }
- if (mFocusedWindowHasCapture == enabled) {
- Slog.i(TAG, "requestPointerCapture: already " + (enabled ? "enabled" : "disabled"));
- return;
- }
- setPointerCapture(enabled);
- }
-
- private void setPointerCapture(boolean enabled) {
- if (mFocusedWindowHasCapture != enabled) {
- mFocusedWindowHasCapture = enabled;
- try {
- mFocusedWindow.dispatchPointerCaptureChanged(enabled);
- } catch (RemoteException ex) {
- /* ignore */
- }
+ boolean requestConfigurationRefresh =
+ mWindowManagerCallbacks.requestPointerCapture(windowToken, enabled);
+ if (requestConfigurationRefresh) {
nativeSetPointerCapture(mPtr, enabled);
}
}
@@ -1533,6 +1518,29 @@ public class InputManagerService extends IInputManager.Stub
nativeSetSystemUiVisibility(mPtr, visibility);
}
+ /**
+ * Atomically transfers touch focus from one window to another as identified by
+ * their input channels. It is possible for multiple windows to have
+ * touch focus if they support split touch dispatch
+ * {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH} but this
+ * method only transfers touch focus of the specified window without affecting
+ * other windows that may also have touch focus at the same time.
+ * @param fromChannel The channel of a window that currently has touch focus.
+ * @param toChannel The channel of the window that should receive touch focus in
+ * place of the first.
+ * @return True if the transfer was successful. False if the window with the
+ * specified channel did not actually have touch focus at the time of the request.
+ */
+ public boolean transferTouchFocus(InputChannel fromChannel, InputChannel toChannel) {
+ if (fromChannel == null) {
+ throw new IllegalArgumentException("fromChannel must not be null.");
+ }
+ if (toChannel == null) {
+ throw new IllegalArgumentException("toChannel must not be null.");
+ }
+ return nativeTransferTouchFocus(mPtr, fromChannel, toChannel);
+ }
+
@Override // Binder call
public void tryPointerSpeed(int speed) {
if (!checkCallingPermission(android.Manifest.permission.SET_POINTER_SPEED,
@@ -1779,6 +1787,12 @@ public class InputManagerService extends IInputManager.Stub
mHandler.obtainMessage(MSG_DELIVER_TABLET_MODE_CHANGED,
args).sendToTarget();
}
+
+ if ((switchMask & SW_MUTE_DEVICE_BIT) != 0) {
+ final boolean micMute = ((switchValues & SW_MUTE_DEVICE_BIT) != 0);
+ AudioManager audioManager = mContext.getSystemService(AudioManager.class);
+ audioManager.setMicrophoneMuteFromSwitch(micMute);
+ }
}
// Native callback.
@@ -1788,16 +1802,11 @@ public class InputManagerService extends IInputManager.Stub
// Native callback
private void notifyFocusChanged(IBinder oldToken, IBinder newToken) {
- if (mFocusedWindow != null) {
- if (mFocusedWindow.asBinder() == newToken) {
- Slog.w(TAG, "notifyFocusChanged called with unchanged mFocusedWindow="
- + mFocusedWindow);
- return;
- }
- setPointerCapture(false);
+ final boolean requestConfigurationRefresh =
+ mWindowManagerCallbacks.notifyFocusChanged(oldToken, newToken);
+ if (requestConfigurationRefresh) {
+ nativeSetPointerCapture(mPtr, false);
}
-
- mFocusedWindow = IWindow.Stub.asInterface(newToken);
}
// Native callback.
@@ -2075,6 +2084,20 @@ public class InputManagerService extends IInputManager.Stub
* @param touchedToken The token for the window that received the input event.
*/
void onPointerDownOutsideFocus(IBinder touchedToken);
+
+ /**
+ * Called when the focused window has changed.
+ *
+ * @return true if we want to request a configuration refresh.
+ */
+ boolean notifyFocusChanged(IBinder oldToken, IBinder newToken);
+
+ /**
+ * Called by the client to request pointer capture.
+ *
+ * @return true if we want to request a configuration refresh.
+ */
+ boolean requestPointerCapture(IBinder windowToken, boolean enabled);
}
/**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index f20003a2ee04..471fa72b1957 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3569,9 +3569,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (!calledWithValidTokenLocked(token)) {
return;
}
- if (mCurClient != null && mCurClient.client != null) {
- executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
- MSG_APPLY_IME_VISIBILITY, setVisible ? 1 : 0, mCurClient));
+ if (!setVisible) {
+ if (mCurClient != null) {
+ // IMMS only knows of focused window, not the actual IME target.
+ // e.g. it isn't aware of any window that has both
+ // NOT_FOCUSABLE, ALT_FOCUSABLE_IM flags set and can the IME target.
+ // 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(mCurClient.selfReportedDisplayId);
+ }
+ } else {
+ // Send to window manager to show IME after IME layout finishes.
+ mWindowManagerInternal.showImePostLayout(mLastImeTargetWindow);
}
}
}
@@ -4202,7 +4212,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// with other IME windows based on type vs. grouping based on whichever token happens
// to get selected by the system later on.
attrs.token = mSwitchingDialogToken;
- attrs.privateFlags |= LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ attrs.privateFlags |= LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
attrs.setTitle("Select input method");
w.setAttributes(attrs);
updateSystemUiLocked(mImeWindowVis, mBackDisposition);
diff --git a/services/core/java/com/android/server/integrity/OWNERS b/services/core/java/com/android/server/integrity/OWNERS
new file mode 100644
index 000000000000..019aa4fb0f2b
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/OWNERS
@@ -0,0 +1,6 @@
+omernebil@google.com
+khelmy@google.com
+mdchurchill@google.com
+sturla@google.com
+songpan@google.com
+bjy@google.com
diff --git a/services/core/java/com/android/server/integrity/TEST_MAPPING b/services/core/java/com/android/server/integrity/TEST_MAPPING
new file mode 100644
index 000000000000..b45b4eaa1ba7
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.integrity."
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
new file mode 100644
index 000000000000..e90612e54105
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
@@ -0,0 +1,79 @@
+/*
+ * 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.integrity.engine;
+
+import android.util.Slog;
+
+import com.android.server.integrity.model.AppInstallMetadata;
+import com.android.server.integrity.model.IntegrityCheckResult;
+import com.android.server.integrity.model.Rule;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The engine used to evaluate rules against app installs.
+ *
+ * <p>Every app install is evaluated against rules (pushed by the verifier) by the evaluation
+ * engine to allow/block that install.
+ */
+public final class RuleEvaluationEngine {
+ private static final String TAG = "RuleEvaluation";
+
+ // The engine for loading rules, retrieving metadata for app installs, and evaluating app
+ // installs against rules.
+ private static RuleEvaluationEngine sRuleEvaluationEngine;
+
+ /**
+ * Provide a singleton instance of the rule evaluation engine.
+ */
+ public static synchronized RuleEvaluationEngine getRuleEvaluationEngine() {
+ if (sRuleEvaluationEngine == null) {
+ return new RuleEvaluationEngine();
+ }
+ return sRuleEvaluationEngine;
+ }
+
+ /**
+ * Load, and match the list of rules against an app install metadata.
+ *
+ * @param appInstallMetadata Metadata of the app to be installed, and to evaluate the rules
+ * against.
+ * @return A rule matching the metadata. If there are multiple matching rules, returns any. If
+ * no rules are matching, returns {@link Rule#EMPTY}.
+ */
+ public IntegrityCheckResult evaluate(AppInstallMetadata appInstallMetadata) {
+ List<Rule> rules = loadRules(appInstallMetadata);
+ Rule matchedRule = RuleEvaluator.evaluateRules(rules, appInstallMetadata);
+ if (matchedRule == Rule.EMPTY) {
+ return IntegrityCheckResult.allow();
+ } else {
+ switch (matchedRule.getEffect()) {
+ case DENY:
+ return IntegrityCheckResult.deny(matchedRule);
+ default:
+ Slog.e(TAG, "Matched a non-DENY rule: " + matchedRule);
+ return IntegrityCheckResult.allow();
+ }
+ }
+ }
+
+ private List<Rule> loadRules(AppInstallMetadata appInstallMetadata) {
+ // TODO: Load rules
+ return new ArrayList<>();
+ }
+}
diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
new file mode 100644
index 000000000000..641650557f4c
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
@@ -0,0 +1,126 @@
+/*
+ * 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.integrity.engine;
+
+import android.util.Slog;
+
+import com.android.server.integrity.model.AppInstallMetadata;
+import com.android.server.integrity.model.AtomicFormula;
+import com.android.server.integrity.model.Formula;
+import com.android.server.integrity.model.OpenFormula;
+import com.android.server.integrity.model.Rule;
+
+import java.util.List;
+
+/**
+ * A helper class for evaluating rules against app install metadata to find if there are matching
+ * rules.
+ */
+final class RuleEvaluator {
+
+ private static final String TAG = "RuleEvaluator";
+
+ /**
+ * Match the list of rules against an app install metadata.
+ *
+ * <p>Rules must be in disjunctive normal form (DNF). A rule should contain AND'ed formulas
+ * only. All rules are OR'ed together by default.
+ *
+ * @param rules The list of rules to evaluate.
+ * @param appInstallMetadata Metadata of the app to be installed, and to evaluate the rules
+ * against.
+ * @return A rule matching the metadata. If there are multiple matching rules, returns any. If
+ * no rules are matching, returns {@link Rule#EMPTY}.
+ */
+ static Rule evaluateRules(List<Rule> rules, AppInstallMetadata appInstallMetadata) {
+ for (Rule rule : rules) {
+ if (isConjunctionOfFormulas(rule.getFormula()) && isMatch(rule, appInstallMetadata)) {
+ return rule;
+ }
+ }
+ return Rule.EMPTY;
+ }
+
+ /**
+ * Match a rule against app install metadata.
+ */
+ private static boolean isMatch(Rule rule, AppInstallMetadata appInstallMetadata) {
+ return isMatch(rule.getFormula(), appInstallMetadata);
+ }
+
+ private static boolean isMatch(Formula formula, AppInstallMetadata appInstallMetadata) {
+ if (formula instanceof AtomicFormula) {
+ AtomicFormula atomicFormula = (AtomicFormula) formula;
+ switch (atomicFormula.getKey()) {
+ case PACKAGE_NAME:
+ return atomicFormula.isMatch(appInstallMetadata.getPackageName());
+ case APP_CERTIFICATE:
+ return atomicFormula.isMatch(appInstallMetadata.getAppCertificate());
+ case INSTALLER_NAME:
+ return atomicFormula.isMatch(appInstallMetadata.getInstallerName());
+ case INSTALLER_CERTIFICATE:
+ return atomicFormula.isMatch(appInstallMetadata.getInstallerCertificate());
+ case VERSION_CODE:
+ return atomicFormula.isMatch(appInstallMetadata.getVersionCode());
+ case PRE_INSTALLED:
+ return atomicFormula.isMatch(appInstallMetadata.isPreInstalled());
+ default:
+ Slog.i(TAG, String.format("Returned no match for unknown key %s",
+ atomicFormula.getKey()));
+ return false;
+ }
+ } else if (formula instanceof OpenFormula) {
+ OpenFormula openFormula = (OpenFormula) formula;
+ // A rule is in disjunctive normal form, so there are no OR connectors.
+ switch (openFormula.getConnector()) {
+ case NOT:
+ // NOT connector has only 1 formula attached.
+ return !isMatch(openFormula.getFormulas().get(0), appInstallMetadata);
+ case AND:
+ return openFormula.getFormulas().stream().allMatch(
+ subFormula -> isMatch(subFormula, appInstallMetadata));
+ default:
+ Slog.i(TAG, String.format("Returned no match for unknown connector %s",
+ openFormula.getConnector()));
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+ private static boolean isConjunctionOfFormulas(Formula formula) {
+ if (formula == null) {
+ return false;
+ }
+ if (isAtomicFormula(formula)) {
+ return true;
+ }
+ OpenFormula openFormula = (OpenFormula) formula;
+ return openFormula.getConnector() == OpenFormula.Connector.AND
+ && openFormula.getFormulas().stream().allMatch(RuleEvaluator::isAtomicFormula);
+ }
+
+ private static boolean isAtomicFormula(Formula formula) {
+ if (formula instanceof AtomicFormula) {
+ return true;
+ }
+ OpenFormula openFormula = (OpenFormula) formula;
+ return openFormula.getConnector() == OpenFormula.Connector.NOT
+ && openFormula.getFormulas().get(0) instanceof AtomicFormula;
+ }
+}
diff --git a/services/core/java/com/android/server/integrity/engine/RuleLoader.java b/services/core/java/com/android/server/integrity/engine/RuleLoader.java
new file mode 100644
index 000000000000..af24d7a21dc8
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/engine/RuleLoader.java
@@ -0,0 +1,61 @@
+/*
+ * 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.integrity.engine;
+
+import com.android.server.integrity.model.Rule;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A helper class for loading rules to the rule evaluation engine.
+ *
+ * <p>Expose fine-grained APIs for loading rules to be passed to the rule evaluation engine.
+ *
+ * <p>It supports:
+ * <ul>
+ * <li>Loading rules based on some keys, such as PACKAGE_NAME and APP_CERT.</li>
+ * </ul>
+ *
+ * <p>It does NOT support:
+ * <ul>
+ * <li>Loading the list of all rules.</li>
+ * <li>Merging rules resulting from different APIs.</li>
+ * </ul>
+ */
+final class RuleLoader {
+
+ List<Rule> loadRulesByPackageName(String packageName) {
+ // TODO: Add logic based on rule storage.
+ return new ArrayList<>();
+ }
+
+ List<Rule> loadRulesByAppCertificate(String appCertificate) {
+ // TODO: Add logic based on rule storage.
+ return new ArrayList<>();
+ }
+
+ List<Rule> loadRulesByInstallerName(String installerName) {
+ // TODO: Add logic based on rule storage.
+ return new ArrayList<>();
+ }
+
+ List<Rule> loadRulesByInstallerCertificate(String installerCertificate) {
+ // TODO: Add logic based on rule storage.
+ return new ArrayList<>();
+ }
+}
diff --git a/services/core/java/com/android/server/integrity/model/AppInstallMetadata.java b/services/core/java/com/android/server/integrity/model/AppInstallMetadata.java
new file mode 100644
index 000000000000..660bd2e0d62e
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/model/AppInstallMetadata.java
@@ -0,0 +1,169 @@
+/*
+ * 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.integrity.model;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.Nullable;
+
+/**
+ * The app install metadata.
+ *
+ * <p>The integrity component retrieves metadata for app installs from package manager, passing it
+ * to the rule evaluation engine to evaluate the metadata against the rules.
+ *
+ * <p>Instances of this class are immutable.
+ */
+public final class AppInstallMetadata {
+ private final String mPackageName;
+ // Raw string encoding for the SHA-256 hash of the certificate of the app.
+ private final String mAppCertificate;
+ private final String mInstallerName;
+ // Raw string encoding for the SHA-256 hash of the certificate of the installer.
+ private final String mInstallerCertificate;
+ private final int mVersionCode;
+ private final boolean mIsPreInstalled;
+
+ private AppInstallMetadata(Builder builder) {
+ this.mPackageName = builder.mPackageName;
+ this.mAppCertificate = builder.mAppCertificate;
+ this.mInstallerName = builder.mInstallerName;
+ this.mInstallerCertificate = builder.mInstallerCertificate;
+ this.mVersionCode = builder.mVersionCode;
+ this.mIsPreInstalled = builder.mIsPreInstalled;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public String getAppCertificate() {
+ return mAppCertificate;
+ }
+
+ @Nullable
+ public String getInstallerName() {
+ return mInstallerName;
+ }
+
+ @Nullable
+ public String getInstallerCertificate() {
+ return mInstallerCertificate;
+ }
+
+ /**
+ * @see AppInstallMetadata.Builder#setVersionCode(int)
+ */
+ public int getVersionCode() {
+ return mVersionCode;
+ }
+
+ /**
+ * @see AppInstallMetadata.Builder#setIsPreInstalled(boolean)
+ */
+ public boolean isPreInstalled() {
+ return mIsPreInstalled;
+ }
+
+ /**
+ * Builder class for constructing {@link AppInstallMetadata} objects.
+ */
+ public static final class Builder {
+ private String mPackageName;
+ private String mAppCertificate;
+ private String mInstallerName;
+ private String mInstallerCertificate;
+ private int mVersionCode;
+ private boolean mIsPreInstalled;
+
+ /**
+ * Set package name of the app to be installed.
+ *
+ * @see AppInstallMetadata#getPackageName()
+ */
+ public Builder setPackageName(String packageName) {
+ this.mPackageName = checkNotNull(packageName);
+ return this;
+ }
+
+ /**
+ * Set certificate of the app to be installed.
+ *
+ * <p>It is represented as the raw string encoding for the SHA-256 hash of the certificate
+ * of the app.
+ *
+ * @see AppInstallMetadata#getAppCertificate()
+ */
+ public Builder setAppCertificate(String appCertificate) {
+ this.mAppCertificate = checkNotNull(appCertificate);
+ return this;
+ }
+
+ /**
+ * Set name of the installer installing the app.
+ *
+ * @see AppInstallMetadata#getInstallerName()
+ */
+ public Builder setInstallerName(String installerName) {
+ this.mInstallerName = checkNotNull(installerName);
+ return this;
+ }
+
+ /**
+ * Set certificate of the installer installing the app.
+ *
+ * <p>It is represented as the raw string encoding for the SHA-256 hash of the certificate
+ * of the installer.
+ *
+ * @see AppInstallMetadata#getInstallerCertificate()
+ */
+ public Builder setInstallerCertificate(String installerCertificate) {
+ this.mInstallerCertificate = checkNotNull(installerCertificate);
+ return this;
+ }
+
+ /**
+ * Set version code of the app to be installed.
+ *
+ * @see AppInstallMetadata#getVersionCode()
+ */
+ public Builder setVersionCode(int versionCode) {
+ this.mVersionCode = versionCode;
+ return this;
+ }
+
+ /**
+ * Set whether the app is pre-installed on the device or not.
+ *
+ * @see AppInstallMetadata#isPreInstalled()
+ */
+ public Builder setIsPreInstalled(boolean isPreInstalled) {
+ this.mIsPreInstalled = isPreInstalled;
+ return this;
+ }
+
+ /**
+ * Build {@link AppInstallMetadata}.
+ */
+ public AppInstallMetadata build() {
+ checkArgument(mPackageName != null);
+ checkArgument(mAppCertificate != null);
+ return new AppInstallMetadata(this);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/integrity/model/AtomicFormula.java b/services/core/java/com/android/server/integrity/model/AtomicFormula.java
new file mode 100644
index 000000000000..b9b46e3e1aae
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/model/AtomicFormula.java
@@ -0,0 +1,236 @@
+/*
+ * 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.integrity.model;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.Nullable;
+import android.util.Slog;
+
+import java.util.Objects;
+
+/**
+ * Represents a simple formula consisting of an app install metadata field and a value.
+ *
+ * <p>Instances of this class are immutable.
+ */
+public final class AtomicFormula extends Formula {
+
+ private static final String TAG = "AtomicFormula";
+
+ public enum Key {
+ PACKAGE_NAME,
+ APP_CERTIFICATE,
+ INSTALLER_NAME,
+ INSTALLER_CERTIFICATE,
+ VERSION_CODE,
+ PRE_INSTALLED
+ }
+
+ public enum Operator {
+ EQ,
+ LT,
+ LE,
+ GT,
+ GE
+ }
+
+ private final Key mKey;
+ private final Operator mOperator;
+
+ // The value of a key can take either 1 of 3 forms: String, Integer, or Boolean.
+ // It cannot have multiple values.
+ @Nullable
+ private final String mStringValue;
+ @Nullable
+ private final Integer mIntValue;
+ @Nullable
+ private final Boolean mBoolValue;
+
+ public AtomicFormula(Key key, Operator operator, String stringValue) {
+ validateOperator(key, operator);
+ checkArgument(
+ key == Key.PACKAGE_NAME || key == Key.APP_CERTIFICATE || key == Key.INSTALLER_NAME
+ || key == Key.INSTALLER_CERTIFICATE,
+ String.format("Key %s cannot have string value", key));
+ this.mKey = checkNotNull(key);
+ this.mOperator = checkNotNull(operator);
+ this.mStringValue = checkNotNull(stringValue);
+ this.mIntValue = null;
+ this.mBoolValue = null;
+ }
+
+ public AtomicFormula(Key key, Operator operator, Integer intValue) {
+ validateOperator(key, operator);
+ checkArgument(key == Key.VERSION_CODE,
+ String.format("Key %s cannot have integer value", key));
+ this.mKey = checkNotNull(key);
+ this.mOperator = checkNotNull(operator);
+ this.mStringValue = null;
+ this.mIntValue = checkNotNull(intValue);
+ this.mBoolValue = null;
+ }
+
+ public AtomicFormula(Key key, Operator operator, Boolean boolValue) {
+ validateOperator(key, operator);
+ checkArgument(key == Key.PRE_INSTALLED,
+ String.format("Key %s cannot have boolean value", key));
+ this.mKey = checkNotNull(key);
+ this.mOperator = checkNotNull(operator);
+ this.mStringValue = null;
+ this.mIntValue = null;
+ this.mBoolValue = checkNotNull(boolValue);
+ }
+
+ public Key getKey() {
+ return mKey;
+ }
+
+ public Operator getOperator() {
+ return mOperator;
+ }
+
+ public String getStringValue() {
+ return mStringValue;
+ }
+
+ public Integer getIntValue() {
+ return mIntValue;
+ }
+
+ public Boolean getBoolValue() {
+ return mBoolValue;
+ }
+
+ /**
+ * Get string representation of the value of the key in the formula.
+ *
+ * @return string representation of the value of the key.
+ */
+ public String getValue() {
+ if (mStringValue != null) {
+ return mStringValue;
+ }
+ if (mIntValue != null) {
+ return mIntValue.toString();
+ }
+ return mBoolValue.toString();
+ }
+
+ /**
+ * Check if the formula is true when substituting its {@link Key} with the string value.
+ *
+ * @param value String value to substitute the key with.
+ * @return {@code true} if the formula is true, and {@code false} otherwise.
+ */
+ public boolean isMatch(String value) {
+ switch (mOperator) {
+ case EQ:
+ return mStringValue.equals(value);
+ }
+ Slog.i(TAG, String.format("Found operator %s for value %s", mOperator, mStringValue));
+ return false;
+ }
+
+ /**
+ * Check if the formula is true when substituting its {@link Key} with the integer value.
+ *
+ * @param value Integer value to substitute the key with.
+ * @return {@code true} if the formula is true, and {@code false} otherwise.
+ */
+ public boolean isMatch(int value) {
+ switch (mOperator) {
+ case EQ:
+ return mIntValue == value;
+ case LE:
+ return mIntValue <= value;
+ case LT:
+ return mIntValue < value;
+ case GE:
+ return mIntValue >= value;
+ case GT:
+ return mIntValue > value;
+ }
+ Slog.i(TAG, String.format("Found operator %s for value %s", mOperator, mIntValue));
+ return false;
+ }
+
+ /**
+ * Check if the formula is true when substituting its {@link Key} with the boolean value.
+ *
+ * @param value Boolean value to substitute the key with.
+ * @return {@code true} if the formula is true, and {@code false} otherwise.
+ */
+ public boolean isMatch(boolean value) {
+ switch (mOperator) {
+ case EQ:
+ return mBoolValue == value;
+ }
+ Slog.i(TAG, String.format("Found operator %s for value %s", mOperator, mBoolValue));
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s %s %s", mKey, mOperator, getValue());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ AtomicFormula that = (AtomicFormula) o;
+ return mKey == that.mKey
+ && mOperator == that.mOperator
+ && Objects.equals(mStringValue, that.mStringValue)
+ && Objects.equals(mIntValue, that.mIntValue)
+ && Objects.equals(mBoolValue, that.mBoolValue);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mKey, mOperator, mStringValue, mIntValue, mBoolValue);
+ }
+
+ private void validateOperator(Key key, Operator operator) {
+ boolean validOperator;
+ switch (key) {
+ case PACKAGE_NAME:
+ case APP_CERTIFICATE:
+ case INSTALLER_NAME:
+ case INSTALLER_CERTIFICATE:
+ case PRE_INSTALLED:
+ validOperator = (operator == Operator.EQ);
+ break;
+ case VERSION_CODE:
+ validOperator = true;
+ break;
+ default:
+ Slog.i(TAG, String.format("Found operator %s for key %s", operator, key));
+ validOperator = false;
+ }
+ if (!validOperator) {
+ throw new IllegalArgumentException(
+ String.format("Invalid operator %s used for key %s", operator, key));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/integrity/model/Formula.java b/services/core/java/com/android/server/integrity/model/Formula.java
new file mode 100644
index 000000000000..9db445378dce
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/model/Formula.java
@@ -0,0 +1,24 @@
+/*
+ * 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.integrity.model;
+
+/**
+ * Represents a rule logic/content.
+ */
+public abstract class Formula {
+
+}
diff --git a/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java b/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java
new file mode 100644
index 000000000000..7aeb0c1b188e
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.integrity.model;
+
+/**
+ * A class encapsulating the result from the evaluation engine after evaluating rules against app
+ * install metadata.
+ *
+ * <p>It contains the outcome effect (whether to allow or block the install), and the rule causing
+ * that effect.
+ */
+public final class IntegrityCheckResult {
+
+ public enum Effect {
+ ALLOW,
+ DENY
+ }
+
+ private final Effect mEffect;
+ private final Rule mRule;
+
+ private IntegrityCheckResult(Effect effect, Rule rule) {
+ this.mEffect = effect;
+ this.mRule = rule;
+ }
+
+ public Effect getEffect() {
+ return mEffect;
+ }
+
+ public Rule getRule() {
+ return mRule;
+ }
+
+ /**
+ * Create an ALLOW evaluation outcome.
+ *
+ * @return An evaluation outcome with ALLOW effect and empty rule.
+ */
+ public static IntegrityCheckResult allow() {
+ return new IntegrityCheckResult(Effect.ALLOW, Rule.EMPTY);
+ }
+
+ /**
+ * Create a DENY evaluation outcome.
+ *
+ * @param rule Rule causing the DENY effect.
+ * @return An evaluation outcome with DENY effect and rule causing that effect.
+ */
+ public static IntegrityCheckResult deny(Rule rule) {
+ return new IntegrityCheckResult(Effect.DENY, rule);
+ }
+}
diff --git a/services/core/java/com/android/server/integrity/model/OpenFormula.java b/services/core/java/com/android/server/integrity/model/OpenFormula.java
new file mode 100644
index 000000000000..21da629fba2e
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/model/OpenFormula.java
@@ -0,0 +1,99 @@
+/*
+ * 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.integrity.model;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents a complex formula consisting of other simple and complex formulas.
+ *
+ * <p>Instances of this class are immutable.
+ */
+public final class OpenFormula extends Formula {
+
+ public enum Connector {
+ AND,
+ OR,
+ NOT
+ }
+
+ private final Connector mConnector;
+ private final List<Formula> mFormulas;
+
+ public OpenFormula(Connector connector, List<Formula> formulas) {
+ validateFormulas(connector, formulas);
+ this.mConnector = checkNotNull(connector);
+ this.mFormulas = Collections.unmodifiableList(checkNotNull(formulas));
+ }
+
+ public Connector getConnector() {
+ return mConnector;
+ }
+
+ public List<Formula> getFormulas() {
+ return mFormulas;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < mFormulas.size(); i++) {
+ if (i > 0) {
+ sb.append(String.format(" %s ", mConnector));
+ }
+ sb.append(mFormulas.get(i).toString());
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ OpenFormula that = (OpenFormula) o;
+ return mConnector == that.mConnector
+ && mFormulas.equals(that.mFormulas);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mConnector, mFormulas);
+ }
+
+ private void validateFormulas(Connector connector, List<Formula> formulas) {
+ switch (connector) {
+ case AND:
+ case OR:
+ checkArgument(formulas.size() >= 2,
+ String.format("Connector %s must have at least 2 formulas", connector));
+ break;
+ case NOT:
+ checkArgument(formulas.size() == 1,
+ String.format("Connector %s must have 1 formula only", connector));
+ break;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/integrity/model/Rule.java b/services/core/java/com/android/server/integrity/model/Rule.java
new file mode 100644
index 000000000000..63b9b911ff4f
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/model/Rule.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.integrity.model;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import java.util.Objects;
+
+/**
+ * Represent rules to be used in the rule evaluation engine to match against app installs.
+ *
+ * <p>Instances of this class are immutable.
+ */
+public final class Rule {
+
+ public enum Effect {
+ DENY
+ }
+
+ // Holds an empty rule instance.
+ public static final Rule EMPTY = new Rule();
+
+ private final Formula mFormula;
+ private final Effect mEffect;
+
+ private Rule() {
+ this.mFormula = null;
+ this.mEffect = null;
+ }
+
+ public Rule(Formula formula, Effect effect) {
+ this.mFormula = checkNotNull(formula);
+ this.mEffect = checkNotNull(effect);
+ }
+
+ /**
+ * Indicates whether the rule is empty or not.
+ *
+ * @return {@code true} if the rule is empty, and {@code false} otherwise.
+ */
+ public boolean isEmpty() {
+ return mFormula == null && mEffect == null;
+ }
+
+ public Formula getFormula() {
+ return mFormula;
+ }
+
+ public Effect getEffect() {
+ return mEffect;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Rule: %s, %s", mFormula, mEffect);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Rule that = (Rule) o;
+ return Objects.equals(mFormula, that.mFormula)
+ && Objects.equals(mEffect, that.mEffect);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mFormula, mEffect);
+ }
+}
diff --git a/services/core/java/com/android/server/location/CallerIdentity.java b/services/core/java/com/android/server/location/CallerIdentity.java
index da31d0b8e9a3..75ba5b8c212e 100644
--- a/services/core/java/com/android/server/location/CallerIdentity.java
+++ b/services/core/java/com/android/server/location/CallerIdentity.java
@@ -16,6 +16,9 @@
package com.android.server.location;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
/**
* Represents the calling process's uid, pid, and package name.
*/
@@ -23,10 +26,15 @@ public class CallerIdentity {
public final int mUid;
public final int mPid;
public final String mPackageName;
+ public final @Nullable String mFeatureId;
+ public final @NonNull String mListenerIdentifier;
- public CallerIdentity(int uid, int pid, String packageName) {
+ public CallerIdentity(int uid, int pid, String packageName, @Nullable String featureId,
+ @NonNull String listenerIdentifier) {
mUid = uid;
mPid = pid;
mPackageName = packageName;
+ mFeatureId = featureId;
+ mListenerIdentifier = listenerIdentifier;
}
}
diff --git a/services/core/java/com/android/server/location/GeofenceManager.java b/services/core/java/com/android/server/location/GeofenceManager.java
index a1922067e7cf..17a21694e725 100644
--- a/services/core/java/com/android/server/location/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/GeofenceManager.java
@@ -16,6 +16,8 @@
package com.android.server.location;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.content.ContentResolver;
@@ -151,14 +153,16 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish
}
public void addFence(LocationRequest request, Geofence geofence, PendingIntent intent,
- int allowedResolutionLevel, int uid, String packageName) {
+ int allowedResolutionLevel, int uid, String packageName, @Nullable String featureId,
+ @NonNull String listenerIdentifier) {
if (D) {
Slog.d(TAG, "addFence: request=" + request + ", geofence=" + geofence
+ ", intent=" + intent + ", uid=" + uid + ", packageName=" + packageName);
}
GeofenceState state = new GeofenceState(geofence,
- request.getExpireAt(), allowedResolutionLevel, uid, packageName, intent);
+ request.getExpireAt(), allowedResolutionLevel, uid, packageName, featureId,
+ listenerIdentifier, intent);
synchronized (mLock) {
// first make sure it doesn't already exist
for (int i = mFences.size() - 1; i >= 0; i--) {
@@ -301,7 +305,8 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish
int op = LocationManagerService.resolutionLevelToOp(state.mAllowedResolutionLevel);
if (op >= 0) {
if (mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, state.mUid,
- state.mPackageName) != AppOpsManager.MODE_ALLOWED) {
+ state.mPackageName, state.mFeatureId, state.mListenerIdentifier)
+ != AppOpsManager.MODE_ALLOWED) {
if (D) {
Slog.d(TAG, "skipping geofence processing for no op app: "
+ state.mPackageName);
diff --git a/services/core/java/com/android/server/location/GeofenceState.java b/services/core/java/com/android/server/location/GeofenceState.java
index 3ebe20a7e8e6..a91a1dcb78e7 100644
--- a/services/core/java/com/android/server/location/GeofenceState.java
+++ b/services/core/java/com/android/server/location/GeofenceState.java
@@ -17,6 +17,8 @@
package com.android.server.location;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.PendingIntent;
import android.location.Geofence;
import android.location.Location;
@@ -38,13 +40,16 @@ public class GeofenceState {
public final int mAllowedResolutionLevel;
public final int mUid;
public final String mPackageName;
+ public final @Nullable String mFeatureId;
+ public final @NonNull String mListenerIdentifier;
public final PendingIntent mIntent;
int mState; // current state
double mDistanceToCenter; // current distance to center of fence
- public GeofenceState(Geofence fence, long expireAt,
- int allowedResolutionLevel, int uid, String packageName, PendingIntent intent) {
+ public GeofenceState(Geofence fence, long expireAt, int allowedResolutionLevel, int uid,
+ String packageName, @Nullable String featureId, @NonNull String listenerIdentifier,
+ PendingIntent intent) {
mState = STATE_UNKNOWN;
mDistanceToCenter = Double.MAX_VALUE;
@@ -53,6 +58,8 @@ public class GeofenceState {
mAllowedResolutionLevel = allowedResolutionLevel;
mUid = uid;
mPackageName = packageName;
+ mFeatureId = featureId;
+ mListenerIdentifier = listenerIdentifier;
mIntent = intent;
mLocation = new Location("");
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 9a063a4a6553..d94a5c5d4b31 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -76,6 +76,8 @@ import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
import com.android.internal.location.gnssmetrics.GnssMetrics;
import com.android.internal.telephony.TelephonyIntents;
+import com.android.server.DeviceIdleInternal;
+import com.android.server.LocalServices;
import com.android.server.location.GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback;
import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback;
@@ -179,6 +181,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
private static final int AGPS_SUPL_MODE_MSA = 0x02;
private static final int AGPS_SUPL_MODE_MSB = 0x01;
+ private static final int UPDATE_LOW_POWER_MODE = 1;
private static final int SET_REQUEST = 3;
private static final int INJECT_NTP_TIME = 5;
// PSDS stands for Predicted Satellite Data Service
@@ -362,6 +365,12 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
private boolean mDisableGpsForPowerManager = false;
/**
+ * True if the device idle controller has determined that the device is stationary. This is only
+ * updated when the device enters idle mode.
+ */
+ private volatile boolean mIsDeviceStationary = false;
+
+ /**
* Properties loaded from PROPERTIES_FILE.
* It must be accessed only inside {@link #mHandler}.
*/
@@ -453,6 +462,15 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
public GnssNavigationMessageProvider getGnssNavigationMessageProvider() {
return mGnssNavigationMessageProvider;
}
+
+ private final DeviceIdleInternal.StationaryListener mDeviceIdleStationaryListener =
+ isStationary -> {
+ mIsDeviceStationary = isStationary;
+ // Call updateLowPowerMode on handler thread so it's always called from the same
+ // thread.
+ mHandler.sendEmptyMessage(UPDATE_LOW_POWER_MODE);
+ };
+
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -469,11 +487,22 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
case ALARM_TIMEOUT:
hibernate();
break;
- case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
+ DeviceIdleInternal deviceIdleService = LocalServices.getService(
+ DeviceIdleInternal.class);
+ if (mPowerManager.isDeviceIdleMode()) {
+ deviceIdleService.registerStationaryListener(mDeviceIdleStationaryListener);
+ } else {
+ deviceIdleService.unregisterStationaryListener(
+ mDeviceIdleStationaryListener);
+ }
+ // Intentional fall-through.
+ case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
case Intent.ACTION_SCREEN_OFF:
case Intent.ACTION_SCREEN_ON:
- updateLowPowerMode();
+ // Call updateLowPowerMode on handler thread so it's always called from the
+ // same thread.
+ mHandler.sendEmptyMessage(UPDATE_LOW_POWER_MODE);
break;
case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
case TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
@@ -531,10 +560,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
}
private void updateLowPowerMode() {
- // Disable GPS if we are in device idle mode.
- boolean disableGpsForPowerManager = mPowerManager.isDeviceIdleMode();
- final PowerSaveState result =
- mPowerManager.getPowerSaveState(ServiceType.LOCATION);
+ // Disable GPS if we are in device idle mode and the device is stationary.
+ boolean disableGpsForPowerManager = mPowerManager.isDeviceIdleMode() && mIsDeviceStationary;
+ final PowerSaveState result = mPowerManager.getPowerSaveState(ServiceType.LOCATION);
switch (result.locationMode) {
case PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF:
case PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF:
@@ -2007,6 +2035,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
case REPORT_SV_STATUS:
handleReportSvStatus((SvStatusInfo) msg.obj);
break;
+ case UPDATE_LOW_POWER_MODE:
+ updateLowPowerMode();
+ break;
}
if (msg.arg2 == 1) {
// wakelock was taken for this message, release it
diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java
index aa8a25a36333..60ce1f4d56f7 100644
--- a/services/core/java/com/android/server/location/RemoteListenerHelper.java
+++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java
@@ -182,7 +182,9 @@ public abstract class RemoteListenerHelper<TListener extends IInterface> {
}
return mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, callerIdentity.mUid,
- callerIdentity.mPackageName) == AppOpsManager.MODE_ALLOWED;
+ callerIdentity.mPackageName, callerIdentity.mFeatureId,
+ "Location sent to " + callerIdentity.mListenerIdentifier)
+ == AppOpsManager.MODE_ALLOWED;
}
protected void logPermissionDisabledEventNotReported(String tag, String packageName,
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index f3b66f6a2200..ec56ec5ef831 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -23,8 +23,11 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
+import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_BY_DEFAULT;
import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_KEY;
import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -44,6 +47,7 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DeviceStateCache;
import android.app.admin.PasswordMetrics;
import android.app.backup.BackupManager;
import android.app.trust.IStrongAuthTracker;
@@ -60,7 +64,10 @@ import android.database.ContentObserver;
import android.database.sqlite.SQLiteDatabase;
import android.hardware.authsecret.V1_0.IAuthSecret;
import android.hardware.biometrics.BiometricManager;
+import android.hardware.face.Face;
import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -76,6 +83,7 @@ import android.os.StrictMode;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.UserManagerInternal;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.provider.Settings;
@@ -96,7 +104,6 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
-import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -111,10 +118,11 @@ import com.android.internal.util.Preconditions;
import com.android.internal.widget.ICheckCredentialProgressCallback;
import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.LockPatternUtils.CredentialType;
import com.android.internal.widget.LockSettingsInternal;
+import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.server.LocalServices;
+import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.locksettings.LockSettingsStorage.CredentialHash;
import com.android.server.locksettings.LockSettingsStorage.PersistentData;
@@ -171,7 +179,6 @@ public class LockSettingsService extends ILockSettings.Stub {
private static final int PROFILE_KEY_IV_SIZE = 12;
private static final String SEPARATE_PROFILE_CHALLENGE_KEY = "lockscreen.profilechallenge";
- private static final int SYNTHETIC_PASSWORD_ENABLED_BY_DEFAULT = 1;
private static final String PREV_SYNTHETIC_PASSWORD_HANDLE_KEY = "prev-sp-handle";
private static final String SYNTHETIC_PASSWORD_UPDATE_TIME_KEY = "sp-handle-ts";
@@ -312,14 +319,34 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
+ private LockscreenCredential generateRandomProfilePassword() {
+ byte[] randomLockSeed = new byte[] {};
+ try {
+ randomLockSeed = SecureRandom.getInstance("SHA1PRNG").generateSeed(40);
+ char[] newPasswordChars = HexEncoding.encode(randomLockSeed);
+ byte[] newPassword = new byte[newPasswordChars.length];
+ for (int i = 0; i < newPasswordChars.length; i++) {
+ newPassword[i] = (byte) newPasswordChars[i];
+ }
+ LockscreenCredential credential =
+ LockscreenCredential.createManagedPassword(newPassword);
+ Arrays.fill(newPasswordChars, '\u0000');
+ Arrays.fill(newPassword, (byte) 0);
+ Arrays.fill(randomLockSeed, (byte) 0);
+ return credential;
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException("Fail to generate profile password", e);
+ }
+ }
+
/**
* Tie managed profile to primary profile if it is in unified mode and not tied before.
*
* @param managedUserId Managed profile user Id
* @param managedUserPassword Managed profile original password (when it has separated lock).
- * NULL when it does not have a separated lock before.
*/
- public void tieManagedProfileLockIfNecessary(int managedUserId, byte[] managedUserPassword) {
+ public void tieManagedProfileLockIfNecessary(int managedUserId,
+ LockscreenCredential managedUserPassword) {
if (DEBUG) Slog.v(TAG, "Check child profile lock for user: " + managedUserId);
// Only for managed profile
if (!mUserManager.getUserInfo(managedUserId).isManagedProfile()) {
@@ -351,27 +378,10 @@ public class LockSettingsService extends ILockSettings.Stub {
return;
}
if (DEBUG) Slog.v(TAG, "Tie managed profile to parent now!");
- byte[] randomLockSeed = new byte[] {};
- try {
- randomLockSeed = SecureRandom.getInstance("SHA1PRNG").generateSeed(40);
- char[] newPasswordChars = HexEncoding.encode(randomLockSeed);
- byte[] newPassword = new byte[newPasswordChars.length];
- for (int i = 0; i < newPasswordChars.length; i++) {
- newPassword[i] = (byte) newPasswordChars[i];
- }
- Arrays.fill(newPasswordChars, '\u0000');
- final int quality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
- setLockCredentialInternal(newPassword, CREDENTIAL_TYPE_PASSWORD, managedUserPassword,
- quality, managedUserId, false, /* isLockTiedToParent= */ true);
- // We store a private credential for the managed user that's unlocked by the primary
- // account holder's credential. As such, the user will never be prompted to enter this
- // password directly, so we always store a password.
- setLong(LockPatternUtils.PASSWORD_TYPE_KEY, quality, managedUserId);
- tieProfileLockToParent(managedUserId, newPassword);
- Arrays.fill(newPassword, (byte) 0);
- } catch (NoSuchAlgorithmException e) {
- Slog.e(TAG, "Fail to tie managed profile", e);
- // Nothing client can do to fix this issue, so we do not throw exception out
+ try (LockscreenCredential unifiedProfilePassword = generateRandomProfilePassword()) {
+ setLockCredentialInternal(unifiedProfilePassword, managedUserPassword, managedUserId,
+ false, /* isLockTiedToParent= */ true);
+ tieProfileLockToParent(managedUserId, unifiedProfilePassword);
}
}
@@ -387,8 +397,15 @@ public class LockSettingsService extends ILockSettings.Stub {
return mContext;
}
- public Handler getHandler() {
- return new Handler();
+ public ServiceThread getServiceThread() {
+ ServiceThread handlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND,
+ true /*allowIo*/);
+ handlerThread.start();
+ return handlerThread;
+ }
+
+ public Handler getHandler(ServiceThread handlerThread) {
+ return new Handler(handlerThread.getLooper());
}
public LockSettingsStorage getStorage() {
@@ -431,6 +448,10 @@ public class LockSettingsService extends ILockSettings.Stub {
return (UserManager) mContext.getSystemService(Context.USER_SERVICE);
}
+ public UserManagerInternal getUserManagerInternal() {
+ return LocalServices.getService(UserManagerInternal.class);
+ }
+
/**
* Return the {@link DevicePolicyManager} object.
*
@@ -442,6 +463,10 @@ public class LockSettingsService extends ILockSettings.Stub {
return (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
}
+ public DeviceStateCache getDeviceStateCache() {
+ return DeviceStateCache.getInstance();
+ }
+
public KeyStore getKeyStore() {
return KeyStore.getInstance();
}
@@ -475,6 +500,27 @@ public class LockSettingsService extends ILockSettings.Stub {
public boolean isGsiRunning() {
return SystemProperties.getInt(GSI_RUNNING_PROP, 0) > 0;
}
+
+ public FingerprintManager getFingerprintManager() {
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+ return (FingerprintManager) mContext.getSystemService(Context.FINGERPRINT_SERVICE);
+ } else {
+ return null;
+ }
+ }
+
+ public FaceManager getFaceManager() {
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
+ return (FaceManager) mContext.getSystemService(Context.FACE_SERVICE);
+ } else {
+ return null;
+ }
+ }
+
+ public int settingsGlobalGetInt(ContentResolver contentResolver, String keyName,
+ int defaultValue) {
+ return Settings.Global.getInt(contentResolver, keyName, defaultValue);
+ }
}
public LockSettingsService(Context context) {
@@ -487,7 +533,7 @@ public class LockSettingsService extends ILockSettings.Stub {
mContext = injector.getContext();
mKeyStore = injector.getKeyStore();
mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager(mKeyStore);
- mHandler = injector.getHandler();
+ mHandler = injector.getHandler(injector.getServiceThread());
mStrongAuth = injector.getStrongAuth();
mActivityManager = injector.getActivityManager();
@@ -645,7 +691,7 @@ public class LockSettingsService extends ILockSettings.Stub {
hideEncryptionNotification(new UserHandle(userId));
if (mUserManager.getUserInfo(userId).isManagedProfile()) {
- tieManagedProfileLockIfNecessary(userId, null);
+ tieManagedProfileLockIfNecessary(userId, LockscreenCredential.createNone());
}
// If the user doesn't have a credential, try and derive their secret for the
@@ -667,10 +713,9 @@ public class LockSettingsService extends ILockSettings.Stub {
}
final long handle = getSyntheticPasswordHandleLocked(userId);
- final byte[] noCredential = null;
AuthenticationResult result =
- mSpManager.unwrapPasswordBasedSyntheticPassword(
- getGateKeeperService(), handle, noCredential, userId, null);
+ mSpManager.unwrapPasswordBasedSyntheticPassword(getGateKeeperService(),
+ handle, LockscreenCredential.createNone(), userId, null);
if (result.authToken != null) {
Slog.i(TAG, "Retrieved auth token for user " + userId);
onAuthTokenKnownForUser(userId, result.authToken);
@@ -833,25 +878,6 @@ public class LockSettingsService extends ILockSettings.Stub {
final List<UserInfo> users = mUserManager.getUsers();
for (int i = 0; i < users.size(); i++) {
final UserInfo userInfo = users.get(i);
- if (userInfo.isManagedProfile() && mStorage.hasChildProfileLock(userInfo.id)) {
- // When managed profile has a unified lock, the password quality stored has 2
- // possibilities only.
- // 1). PASSWORD_QUALITY_UNSPECIFIED, which is upgraded from dp2, and we are
- // going to set it back to PASSWORD_QUALITY_ALPHANUMERIC.
- // 2). PASSWORD_QUALITY_ALPHANUMERIC, which is the actual password quality for
- // unified lock.
- final long quality = getLong(LockPatternUtils.PASSWORD_TYPE_KEY,
- DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userInfo.id);
- if (quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
- // Only possible when it's upgraded from nyc dp3
- Slog.i(TAG, "Migrated tied profile lock type");
- setLong(LockPatternUtils.PASSWORD_TYPE_KEY,
- DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, userInfo.id);
- } else if (quality != DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC) {
- // It should not happen
- Slog.e(TAG, "Invalid tied profile lock type: " + quality);
- }
- }
try {
final String alias = LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userInfo.id;
java.security.KeyStore keyStore =
@@ -994,21 +1020,22 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public void setSeparateProfileChallengeEnabled(int userId, boolean enabled,
- byte[] managedUserPassword) {
+ LockscreenCredential managedUserPassword) {
checkWritePermission(userId);
if (!mLockPatternUtils.hasSecureLockScreen()) {
throw new UnsupportedOperationException(
"This operation requires secure lock screen feature.");
}
synchronized (mSeparateChallengeLock) {
- setSeparateProfileChallengeEnabledLocked(userId, enabled, managedUserPassword);
+ setSeparateProfileChallengeEnabledLocked(userId, enabled, managedUserPassword != null
+ ? managedUserPassword : LockscreenCredential.createNone());
}
notifySeparateProfileChallengeChanged(userId);
}
@GuardedBy("mSeparateChallengeLock")
private void setSeparateProfileChallengeEnabledLocked(@UserIdInt int userId,
- boolean enabled, byte[] managedUserPassword) {
+ boolean enabled, LockscreenCredential managedUserPassword) {
final boolean old = getBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, false, userId);
setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, enabled, userId);
try {
@@ -1025,11 +1052,16 @@ public class LockSettingsService extends ILockSettings.Stub {
}
private void notifySeparateProfileChallengeChanged(int userId) {
- final DevicePolicyManagerInternal dpmi = LocalServices.getService(
- DevicePolicyManagerInternal.class);
- if (dpmi != null) {
- dpmi.reportSeparateProfileChallengeChanged(userId);
- }
+ // LSS cannot call into DPM directly, otherwise it will cause deadlock.
+ // In this case, calling DPM on a handler thread is OK since DPM doesn't
+ // expect reportSeparateProfileChallengeChanged() to happen synchronously.
+ mHandler.post(() -> {
+ final DevicePolicyManagerInternal dpmi = LocalServices.getService(
+ DevicePolicyManagerInternal.class);
+ if (dpmi != null) {
+ dpmi.reportSeparateProfileChallengeChanged(userId);
+ }
+ });
}
@Override
@@ -1041,6 +1073,10 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public void setLong(String key, long value, int userId) {
checkWritePermission(userId);
+ setLongUnchecked(key, value, userId);
+ }
+
+ private void setLongUnchecked(String key, long value, int userId) {
setStringUnchecked(key, userId, Long.toString(value));
}
@@ -1070,6 +1106,10 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public long getLong(String key, long defaultValue, int userId) {
checkReadPermission(key, userId);
+ return getLongUnchecked(key, defaultValue, userId);
+ }
+
+ private long getLongUnchecked(String key, long defaultValue, int userId) {
String value = getStringUnchecked(key, null, userId);
return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value);
}
@@ -1080,7 +1120,7 @@ public class LockSettingsService extends ILockSettings.Stub {
return getStringUnchecked(key, defaultValue, userId);
}
- public String getStringUnchecked(String key, String defaultValue, int userId) {
+ private String getStringUnchecked(String key, String defaultValue, int userId) {
if (Settings.Secure.LOCK_PATTERN_ENABLED.equals(key)) {
long ident = Binder.clearCallingIdentity();
try {
@@ -1089,9 +1129,8 @@ public class LockSettingsService extends ILockSettings.Stub {
Binder.restoreCallingIdentity(ident);
}
}
-
if (userId == USER_FRP) {
- return getFrpStringUnchecked(key);
+ return null;
}
if (LockPatternUtils.LEGACY_LOCK_PATTERN_ENABLED.equals(key)) {
@@ -1101,51 +1140,81 @@ public class LockSettingsService extends ILockSettings.Stub {
return mStorage.readKeyValue(key, defaultValue, userId);
}
- private String getFrpStringUnchecked(String key) {
- if (LockPatternUtils.PASSWORD_TYPE_KEY.equals(key)) {
- return String.valueOf(readFrpPasswordQuality());
- }
- return null;
+ private void setKeyguardStoredQuality(int quality, int userId) {
+ if (DEBUG) Slog.d(TAG, "setKeyguardStoredQuality: user=" + userId + " quality=" + quality);
+ setLongUnchecked(LockPatternUtils.PASSWORD_TYPE_KEY, quality, userId);
}
- private int readFrpPasswordQuality() {
- return mStorage.readPersistentDataBlock().qualityForUi;
+ private int getKeyguardStoredQuality(int userId) {
+ return (int) getLongUnchecked(LockPatternUtils.PASSWORD_TYPE_KEY,
+ DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userId);
}
@Override
- public boolean havePassword(int userId) {
+ public int getCredentialType(int userId) {
checkPasswordHavePermission(userId);
+ return getCredentialTypeInternal(userId);
+ }
+
+ // TODO: this is a hot path, can we optimize it?
+ /**
+ * Returns the credential type of the user, can be one of {@link #CREDENTIAL_TYPE_NONE},
+ * {@link #CREDENTIAL_TYPE_PATTERN}, {@link #CREDENTIAL_TYPE_PIN} and
+ * {@link #CREDENTIAL_TYPE_PASSWORD}
+ */
+ public int getCredentialTypeInternal(int userId) {
+ if (userId == USER_FRP) {
+ return getFrpCredentialType();
+ }
synchronized (mSpManager) {
if (isSyntheticPasswordBasedCredentialLocked(userId)) {
final long handle = getSyntheticPasswordHandleLocked(userId);
- return mSpManager.getCredentialType(handle, userId) == CREDENTIAL_TYPE_PASSWORD;
+ int rawType = mSpManager.getCredentialType(handle, userId);
+ if (rawType != CREDENTIAL_TYPE_PASSWORD_OR_PIN) {
+ return rawType;
+ }
+ return pinOrPasswordQualityToCredentialType(getKeyguardStoredQuality(userId));
}
}
- // Do we need a permissions check here?
- return mStorage.hasPassword(userId);
+ // Intentional duplication of the getKeyguardStoredQuality() call above since this is a
+ // unlikely code path (device with pre-synthetic password credential). We want to skip
+ // calling getKeyguardStoredQuality whenever possible.
+ final int savedQuality = getKeyguardStoredQuality(userId);
+ if (savedQuality == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
+ && mStorage.hasPattern(userId)) {
+ return CREDENTIAL_TYPE_PATTERN;
+ }
+ if (savedQuality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
+ && mStorage.hasPassword(userId)) {
+ return pinOrPasswordQualityToCredentialType(savedQuality);
+ }
+ return CREDENTIAL_TYPE_NONE;
}
- @Override
- public boolean havePattern(int userId) {
- checkPasswordHavePermission(userId);
- synchronized (mSpManager) {
- if (isSyntheticPasswordBasedCredentialLocked(userId)) {
- final long handle = getSyntheticPasswordHandleLocked(userId);
- return mSpManager.getCredentialType(handle, userId) == CREDENTIAL_TYPE_PATTERN;
- }
+ private int getFrpCredentialType() {
+ PersistentData data = mStorage.readPersistentDataBlock();
+ if (data.type != PersistentData.TYPE_SP && data.type != PersistentData.TYPE_SP_WEAVER) {
+ return CREDENTIAL_TYPE_NONE;
+ }
+ int credentialType = SyntheticPasswordManager.getFrpCredentialType(data.payload);
+ if (credentialType != CREDENTIAL_TYPE_PASSWORD_OR_PIN) {
+ return credentialType;
}
- // Do we need a permissions check here?
- return mStorage.hasPattern(userId);
+ return pinOrPasswordQualityToCredentialType(data.qualityForUi);
}
- private boolean isUserSecure(int userId) {
- synchronized (mSpManager) {
- if (isSyntheticPasswordBasedCredentialLocked(userId)) {
- final long handle = getSyntheticPasswordHandleLocked(userId);
- return mSpManager.getCredentialType(handle, userId) != CREDENTIAL_TYPE_NONE;
- }
+ private static int pinOrPasswordQualityToCredentialType(int quality) {
+ if (LockPatternUtils.isQualityAlphabeticPassword(quality)) {
+ return CREDENTIAL_TYPE_PASSWORD;
}
- return mStorage.hasCredential(userId);
+ if (LockPatternUtils.isQualityNumericPin(quality)) {
+ return CREDENTIAL_TYPE_PIN;
+ }
+ throw new IllegalArgumentException("Quality is neither Pin nor password: " + quality);
+ }
+
+ private boolean isUserSecure(int userId) {
+ return getCredentialTypeInternal(userId) != CREDENTIAL_TYPE_NONE;
}
public void retainPassword(String password) {
@@ -1202,8 +1271,8 @@ public class LockSettingsService extends ILockSettings.Stub {
ks.unlock(userHandle, passwordString);
}
- @VisibleForTesting
- protected byte[] getDecryptedPasswordForTiedProfile(int userId)
+ @VisibleForTesting /** Note: this method is overridden in unit tests */
+ protected LockscreenCredential getDecryptedPasswordForTiedProfile(int userId)
throws KeyStoreException, UnrecoverableKeyException,
NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException,
@@ -1227,7 +1296,10 @@ public class LockSettingsService extends ILockSettings.Stub {
cipher.init(Cipher.DECRYPT_MODE, decryptionKey, new GCMParameterSpec(128, iv));
decryptionResult = cipher.doFinal(encryptedPassword);
- return decryptionResult;
+ LockscreenCredential credential = LockscreenCredential.createManagedPassword(
+ decryptionResult);
+ Arrays.fill(decryptionResult, (byte) 0);
+ return credential;
}
private void unlockChildProfile(int profileHandle, boolean ignoreUserNotAuthenticated,
@@ -1235,7 +1307,6 @@ public class LockSettingsService extends ILockSettings.Stub {
@Nullable ArrayList<PendingResetLockout> resetLockouts) {
try {
doVerifyCredential(getDecryptedPasswordForTiedProfile(profileHandle),
- CREDENTIAL_TYPE_PASSWORD,
challengeType, challenge, profileHandle, null /* progressCallback */,
resetLockouts);
} catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
@@ -1273,17 +1344,17 @@ public class LockSettingsService extends ILockSettings.Stub {
final IProgressListener listener = new IProgressListener.Stub() {
@Override
public void onStarted(int id, Bundle extras) throws RemoteException {
- Log.d(TAG, "unlockUser started");
+ Slog.d(TAG, "unlockUser started");
}
@Override
public void onProgress(int id, int progress, Bundle extras) throws RemoteException {
- Log.d(TAG, "unlockUser progress " + progress);
+ Slog.d(TAG, "unlockUser progress " + progress);
}
@Override
public void onFinished(int id, Bundle extras) throws RemoteException {
- Log.d(TAG, "unlockUser finished");
+ Slog.d(TAG, "unlockUser finished");
latch.countDown();
}
};
@@ -1348,11 +1419,11 @@ public class LockSettingsService extends ILockSettings.Stub {
&& mUserManager.isUserRunning(userInfo.id);
}
- private Map<Integer, byte[]> getDecryptedPasswordsForAllTiedProfiles(int userId) {
+ private Map<Integer, LockscreenCredential> getDecryptedPasswordsForAllTiedProfiles(int userId) {
if (mUserManager.getUserInfo(userId).isManagedProfile()) {
return null;
}
- Map<Integer, byte[]> result = new ArrayMap<Integer, byte[]>();
+ Map<Integer, LockscreenCredential> result = new ArrayMap<>();
final List<UserInfo> profiles = mUserManager.getProfiles(userId);
final int size = profiles.size();
for (int i = 0; i < size; i++) {
@@ -1390,7 +1461,7 @@ public class LockSettingsService extends ILockSettings.Stub {
* terminates when the user is a managed profile.
*/
private void synchronizeUnifiedWorkChallengeForProfiles(int userId,
- Map<Integer, byte[]> profilePasswordMap) {
+ Map<Integer, LockscreenCredential> profilePasswordMap) {
if (mUserManager.getUserInfo(userId).isManagedProfile()) {
return;
}
@@ -1405,20 +1476,23 @@ public class LockSettingsService extends ILockSettings.Stub {
continue;
}
if (isSecure) {
- tieManagedProfileLockIfNecessary(managedUserId, null);
+ tieManagedProfileLockIfNecessary(managedUserId,
+ LockscreenCredential.createNone());
} else {
// We use cached work profile password computed before clearing the parent's
// credential, otherwise they get lost
- if (profilePasswordMap != null && profilePasswordMap.containsKey(managedUserId)) {
- setLockCredentialInternal(null, CREDENTIAL_TYPE_NONE,
+ if (profilePasswordMap != null
+ && profilePasswordMap.containsKey(managedUserId)) {
+ setLockCredentialInternal(LockscreenCredential.createNone(),
profilePasswordMap.get(managedUserId),
- DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, managedUserId,
+ managedUserId,
false, /* isLockTiedToParent= */ true);
} else {
Slog.wtf(TAG, "clear tied profile challenges, but no password supplied.");
- // Supplying null here would lead to untrusted credential change
- setLockCredentialInternal(null, CREDENTIAL_TYPE_NONE, null,
- DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, managedUserId,
+ // Attempt an untrusted reset by supplying an empty credential.
+ setLockCredentialInternal(LockscreenCredential.createNone(),
+ LockscreenCredential.createNone(),
+ managedUserId,
true, /* isLockTiedToParent= */ true);
}
mStorage.removeChildProfileLock(managedUserId);
@@ -1442,8 +1516,7 @@ public class LockSettingsService extends ILockSettings.Stub {
* Send credentials for user {@code userId} to {@link RecoverableKeyStoreManager} during an
* unlock operation.
*/
- private void sendCredentialsOnUnlockIfRequired(
- int credentialType, @NonNull byte[] credential, int userId) {
+ private void sendCredentialsOnUnlockIfRequired(LockscreenCredential credential, int userId) {
// Don't send credentials during the factory reset protection flow.
if (userId == USER_FRP) {
return;
@@ -1456,10 +1529,12 @@ public class LockSettingsService extends ILockSettings.Stub {
return;
}
+ // RecoverableKeyStoreManager expects null for empty credential.
+ final byte[] secret = credential.isNone() ? null : credential.getCredential();
// Send credentials for the user and any child profiles that share its lock screen.
for (int profileId : getProfilesWithSameLockScreen(userId)) {
mRecoverableKeyStoreManager.lockScreenSecretAvailable(
- credentialType, credential, profileId);
+ credential.getType(), secret, profileId);
}
}
@@ -1468,7 +1543,7 @@ public class LockSettingsService extends ILockSettings.Stub {
* credentials are set/changed.
*/
private void sendCredentialsOnChangeIfRequired(
- int credentialType, byte[] credential, int userId, boolean isLockTiedToParent) {
+ LockscreenCredential credential, int userId, boolean isLockTiedToParent) {
// A profile whose lock screen is being tied to its parent's will either have a randomly
// generated credential (creation) or null (removal). We rely on the parent to send its
// credentials for the profile in both cases as it stores the unified lock credential.
@@ -1476,10 +1551,12 @@ public class LockSettingsService extends ILockSettings.Stub {
return;
}
+ // RecoverableKeyStoreManager expects null for empty credential.
+ final byte[] secret = credential.isNone() ? null : credential.getCredential();
// Send credentials for the user and any child profiles that share its lock screen.
for (int profileId : getProfilesWithSameLockScreen(userId)) {
mRecoverableKeyStoreManager.lockScreenSecretChanged(
- credentialType, credential, profileId);
+ credential.getType(), secret, profileId);
}
}
@@ -1502,9 +1579,8 @@ public class LockSettingsService extends ILockSettings.Stub {
// This method should be called by LockPatternUtil only, all internal methods in this class
// should call setLockCredentialInternal.
@Override
- public boolean setLockCredential(byte[] credential, int type,
- byte[] savedCredential, int requestedQuality, int userId,
- boolean allowUntrustedChange) {
+ public boolean setLockCredential(LockscreenCredential credential,
+ LockscreenCredential savedCredential, int userId, boolean allowUntrustedChange) {
if (!mLockPatternUtils.hasSecureLockScreen()) {
throw new UnsupportedOperationException(
@@ -1512,11 +1588,11 @@ public class LockSettingsService extends ILockSettings.Stub {
}
checkWritePermission(userId);
synchronized (mSeparateChallengeLock) {
- if (!setLockCredentialInternal(credential, type, savedCredential, requestedQuality,
+ if (!setLockCredentialInternal(credential, savedCredential,
userId, allowUntrustedChange, /* isLockTiedToParent= */ false)) {
return false;
}
- setSeparateProfileChallengeEnabledLocked(userId, true, null);
+ setSeparateProfileChallengeEnabledLocked(userId, true, /* unused */ null);
notifyPasswordChanged(userId);
}
if (mUserManager.getUserInfo(userId).isManagedProfile()) {
@@ -1528,51 +1604,44 @@ public class LockSettingsService extends ILockSettings.Stub {
}
/**
+ * @param savedCredential if the user is a managed profile with unified challenge and
+ * savedCredential is empty, LSS will try to re-derive the profile password internally.
+ * TODO (b/80170828): Fix this so profile password is always passed in.
* @param isLockTiedToParent is {@code true} if {@code userId} is a profile and its new
* credentials are being tied to its parent's credentials.
*/
- private boolean setLockCredentialInternal(byte[] credential, @CredentialType int credentialType,
- byte[] savedCredential, int requestedQuality, int userId, boolean allowUntrustedChange,
- boolean isLockTiedToParent) {
- // Normalize savedCredential and credential such that empty string is always represented
- // as null.
- if (savedCredential == null || savedCredential.length == 0) {
- savedCredential = null;
- }
- if (credential == null || credential.length == 0) {
- credential = null;
- }
+ private boolean setLockCredentialInternal(LockscreenCredential credential,
+ LockscreenCredential savedCredential, int userId,
+ boolean allowUntrustedChange, boolean isLockTiedToParent) {
+ Preconditions.checkNotNull(credential);
+ Preconditions.checkNotNull(savedCredential);
synchronized (mSpManager) {
if (isSyntheticPasswordBasedCredentialLocked(userId)) {
- return spBasedSetLockCredentialInternalLocked(credential, credentialType,
- savedCredential, requestedQuality, userId, allowUntrustedChange,
- isLockTiedToParent);
+ return spBasedSetLockCredentialInternalLocked(credential, savedCredential, userId,
+ allowUntrustedChange, isLockTiedToParent);
}
}
- if (credentialType == CREDENTIAL_TYPE_NONE) {
- if (credential != null) {
- Slog.wtf(TAG, "CredentialType is none, but credential is non-null.");
- }
+ if (credential.isNone()) {
clearUserKeyProtection(userId);
gateKeeperClearSecureUserId(userId);
mStorage.writeCredentialHash(CredentialHash.createEmptyHash(), userId);
+ // Still update PASSWORD_TYPE_KEY if we are running in pre-synthetic password code path,
+ // since it forms part of the state that determines the credential type
+ // @see getCredentialTypeInternal
+ setKeyguardStoredQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userId);
setKeystorePassword(null, userId);
fixateNewestUserKeyAuth(userId);
synchronizeUnifiedWorkChallengeForProfiles(userId, null);
- setUserPasswordMetrics(CREDENTIAL_TYPE_NONE, null, userId);
- sendCredentialsOnChangeIfRequired(
- credentialType, credential, userId, isLockTiedToParent);
+ setUserPasswordMetrics(LockscreenCredential.createNone(), userId);
+ sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent);
return true;
}
- if (credential == null) {
- throw new IllegalArgumentException("Null credential with mismatched credential type");
- }
CredentialHash currentHandle = mStorage.readCredentialHash(userId);
if (isManagedProfileWithUnifiedLock(userId)) {
// get credential from keystore when managed profile has unified lock
- if (savedCredential == null) {
+ if (savedCredential.isNone()) {
try {
savedCredential = getDecryptedPasswordForTiedProfile(userId);
} catch (FileNotFoundException e) {
@@ -1586,47 +1655,50 @@ public class LockSettingsService extends ILockSettings.Stub {
}
} else {
if (currentHandle.hash == null) {
- if (savedCredential != null) {
+ if (!savedCredential.isNone()) {
Slog.w(TAG, "Saved credential provided, but none stored");
}
- savedCredential = null;
+ savedCredential.close();
+ savedCredential = LockscreenCredential.createNone();
}
}
synchronized (mSpManager) {
if (shouldMigrateToSyntheticPasswordLocked(userId)) {
- initializeSyntheticPasswordLocked(currentHandle.hash, savedCredential,
- currentHandle.type, requestedQuality, userId);
- return spBasedSetLockCredentialInternalLocked(credential, credentialType,
- savedCredential, requestedQuality, userId, allowUntrustedChange,
- isLockTiedToParent);
+ initializeSyntheticPasswordLocked(currentHandle.hash, savedCredential, userId);
+ return spBasedSetLockCredentialInternalLocked(credential, savedCredential, userId,
+ allowUntrustedChange, isLockTiedToParent);
}
}
if (DEBUG) Slog.d(TAG, "setLockCredentialInternal: user=" + userId);
- byte[] enrolledHandle = enrollCredential(currentHandle.hash, savedCredential, credential,
- userId);
+ byte[] enrolledHandle = enrollCredential(currentHandle.hash,
+ savedCredential.getCredential(), credential.getCredential(), userId);
if (enrolledHandle == null) {
Slog.w(TAG, String.format("Failed to enroll %s: incorrect credential",
- credentialType == CREDENTIAL_TYPE_PASSWORD ? "password" : "pattern"));
+ credential.isPattern() ? "pattern" : "password"));
return false;
}
- CredentialHash willStore = CredentialHash.create(enrolledHandle, credentialType);
+ CredentialHash willStore = CredentialHash.create(enrolledHandle, credential.getType());
mStorage.writeCredentialHash(willStore, userId);
+ // Still update PASSWORD_TYPE_KEY if we are running in pre-synthetic password code path,
+ // since it forms part of the state that determines the credential type
+ // @see getCredentialTypeInternal
+ setKeyguardStoredQuality(
+ LockPatternUtils.credentialTypeToPasswordQuality(credential.getType()), userId);
// push new secret and auth token to vold
GateKeeperResponse gkResponse;
try {
gkResponse = getGateKeeperService().verifyChallenge(userId, 0, willStore.hash,
- credential);
+ credential.getCredential());
} catch (RemoteException e) {
throw new IllegalStateException("Failed to verify current credential", e);
}
setUserKeyProtection(userId, credential, convertResponse(gkResponse));
fixateNewestUserKeyAuth(userId);
// Refresh the auth token
- doVerifyCredential(credential, credentialType, CHALLENGE_FROM_CALLER, 0, userId,
+ doVerifyCredential(credential, CHALLENGE_FROM_CALLER, 0, userId,
null /* progressCallback */);
synchronizeUnifiedWorkChallengeForProfiles(userId, null);
- sendCredentialsOnChangeIfRequired(
- credentialType, credential, userId, isLockTiedToParent);
+ sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent);
return true;
}
@@ -1634,8 +1706,8 @@ public class LockSettingsService extends ILockSettings.Stub {
return VerifyCredentialResponse.fromGateKeeperResponse(gateKeeperResponse);
}
- @VisibleForTesting
- protected void tieProfileLockToParent(int userId, byte[] password) {
+ @VisibleForTesting /** Note: this method is overridden in unit tests */
+ protected void tieProfileLockToParent(int userId, LockscreenCredential password) {
if (DEBUG) Slog.v(TAG, "tieProfileLockToParent for user: " + userId);
byte[] encryptionResult;
byte[] iv;
@@ -1670,7 +1742,7 @@ public class LockSettingsService extends ILockSettings.Stub {
KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/"
+ KeyProperties.ENCRYPTION_PADDING_NONE);
cipher.init(Cipher.ENCRYPT_MODE, keyStoreEncryptionKey);
- encryptionResult = cipher.doFinal(password);
+ encryptionResult = cipher.doFinal(password.getCredential());
iv = cipher.getIV();
} finally {
// The original key can now be discarded.
@@ -1725,7 +1797,8 @@ public class LockSettingsService extends ILockSettings.Stub {
addUserKeyAuth(userId, null, key);
}
- private void setUserKeyProtection(int userId, byte[] credential, VerifyCredentialResponse vcr) {
+ private void setUserKeyProtection(int userId, LockscreenCredential credential,
+ VerifyCredentialResponse vcr) {
if (DEBUG) Slog.d(TAG, "setUserKeyProtection: user=" + userId);
if (vcr == null) {
throw new IllegalArgumentException("Null response verifying a credential we just set");
@@ -1760,7 +1833,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
- private static byte[] secretFromCredential(byte[] credential) {
+ private static byte[] secretFromCredential(LockscreenCredential credential) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-512");
// Personalize the hash
@@ -1768,7 +1841,7 @@ public class LockSettingsService extends ILockSettings.Stub {
// Pad it to the block size of the hash function
personalization = Arrays.copyOf(personalization, 128);
digest.update(personalization);
- digest.update(credential);
+ digest.update(credential.getCredential());
return digest.digest();
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("NoSuchAlgorithmException for SHA-512");
@@ -1779,7 +1852,7 @@ public class LockSettingsService extends ILockSettings.Stub {
try {
return mStorageManager.isUserKeyUnlocked(userId);
} catch (RemoteException e) {
- Log.e(TAG, "failed to check user key locked state", e);
+ Slog.e(TAG, "failed to check user key locked state", e);
return false;
}
}
@@ -1826,7 +1899,7 @@ public class LockSettingsService extends ILockSettings.Stub {
checkWritePermission(userId);
if (DEBUG) Slog.v(TAG, "Reset keystore for user: " + userId);
int managedUserId = -1;
- byte[] managedUserDecryptedPassword = null;
+ LockscreenCredential managedUserDecryptedPassword = null;
final List<UserInfo> profiles = mUserManager.getProfiles(userId);
for (UserInfo pi : profiles) {
// Unlock managed profile with unified lock
@@ -1863,38 +1936,38 @@ public class LockSettingsService extends ILockSettings.Stub {
tieProfileLockToParent(managedUserId, managedUserDecryptedPassword);
}
}
- if (managedUserDecryptedPassword != null && managedUserDecryptedPassword.length > 0) {
- Arrays.fill(managedUserDecryptedPassword, (byte) 0);
+ if (managedUserDecryptedPassword != null) {
+ managedUserDecryptedPassword.zeroize();
}
}
@Override
- public VerifyCredentialResponse checkCredential(byte[] credential, int type, int userId,
+ public VerifyCredentialResponse checkCredential(LockscreenCredential credential, int userId,
ICheckCredentialProgressCallback progressCallback) {
checkPasswordReadPermission(userId);
- VerifyCredentialResponse response = doVerifyCredential(credential, type,
- CHALLENGE_NONE, 0, userId, progressCallback);
+ VerifyCredentialResponse response = doVerifyCredential(credential, CHALLENGE_NONE,
+ 0, userId, progressCallback);
if ((response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) &&
(userId == UserHandle.USER_OWNER)) {
- //TODO(b/127810705): Update to credentials to use byte[]
- String credentialString = credential == null ? null : new String(credential);
+ //TODO(b/127810705): Update to credentials to use LockscreenCredential
+ String credentialString = credential.isNone() ? null : new String(credential.getCredential());
retainPassword(credentialString);
}
return response;
}
@Override
- public VerifyCredentialResponse verifyCredential(byte[] credential, int type, long challenge,
- int userId) {
+ public VerifyCredentialResponse verifyCredential(LockscreenCredential credential,
+ long challenge, int userId) {
checkPasswordReadPermission(userId);
- return doVerifyCredential(credential, type, CHALLENGE_FROM_CALLER, challenge, userId,
+ return doVerifyCredential(credential, CHALLENGE_FROM_CALLER, challenge, userId,
null /* progressCallback */);
}
- private VerifyCredentialResponse doVerifyCredential(byte[] credential, int credentialType,
+ private VerifyCredentialResponse doVerifyCredential(LockscreenCredential credential,
@ChallengeType int challengeType, long challenge, int userId,
ICheckCredentialProgressCallback progressCallback) {
- return doVerifyCredential(credential, credentialType, challengeType, challenge, userId,
+ return doVerifyCredential(credential, challengeType, challenge, userId,
progressCallback, null /* resetLockouts */);
}
@@ -1902,25 +1975,25 @@ public class LockSettingsService extends ILockSettings.Stub {
* Verify user credential and unlock the user. Fix pattern bug by deprecating the old base zero
* format.
*/
- private VerifyCredentialResponse doVerifyCredential(byte[] credential, int credentialType,
+ private VerifyCredentialResponse doVerifyCredential(LockscreenCredential credential,
@ChallengeType int challengeType, long challenge, int userId,
ICheckCredentialProgressCallback progressCallback,
@Nullable ArrayList<PendingResetLockout> resetLockouts) {
- if (credential == null || credential.length == 0) {
+ if (credential == null || credential.isNone()) {
throw new IllegalArgumentException("Credential can't be null or empty");
}
- if (userId == USER_FRP && Settings.Global.getInt(mContext.getContentResolver(),
+ if (userId == USER_FRP && mInjector.settingsGlobalGetInt(mContext.getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
Slog.e(TAG, "FRP credential can only be verified prior to provisioning.");
return VerifyCredentialResponse.ERROR;
}
VerifyCredentialResponse response = null;
- response = spBasedDoVerifyCredential(credential, credentialType, challengeType, challenge,
+ response = spBasedDoVerifyCredential(credential, challengeType, challenge,
userId, progressCallback, resetLockouts);
// The user employs synthetic password based credential.
if (response != null) {
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
- sendCredentialsOnUnlockIfRequired(credentialType, credential, userId);
+ sendCredentialsOnUnlockIfRequired(credential, userId);
}
return response;
}
@@ -1931,9 +2004,9 @@ public class LockSettingsService extends ILockSettings.Stub {
}
final CredentialHash storedHash = mStorage.readCredentialHash(userId);
- if (storedHash.type != credentialType) {
+ if (!credential.checkAgainstStoredType(storedHash.type)) {
Slog.wtf(TAG, "doVerifyCredential type mismatch with stored credential??"
- + " stored: " + storedHash.type + " passed in: " + credentialType);
+ + " stored: " + storedHash.type + " passed in: " + credential.getType());
return VerifyCredentialResponse.ERROR;
}
@@ -1948,7 +2021,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
@Override
- public VerifyCredentialResponse verifyTiedProfileChallenge(byte[] credential, int type,
+ public VerifyCredentialResponse verifyTiedProfileChallenge(LockscreenCredential credential,
long challenge, int userId) {
checkPasswordReadPermission(userId);
if (!isManagedProfileWithUnifiedLock(userId)) {
@@ -1958,7 +2031,6 @@ public class LockSettingsService extends ILockSettings.Stub {
// Unlock parent by using parent's challenge
final VerifyCredentialResponse parentResponse = doVerifyCredential(
credential,
- type,
CHALLENGE_FROM_CALLER,
challenge,
parentProfileId,
@@ -1971,7 +2043,6 @@ public class LockSettingsService extends ILockSettings.Stub {
try {
// Unlock work profile, and work profile with unified lock must use password only
return doVerifyCredential(getDecryptedPasswordForTiedProfile(userId),
- CREDENTIAL_TYPE_PASSWORD,
CHALLENGE_FROM_CALLER,
challenge,
userId, null /* progressCallback */);
@@ -1990,15 +2061,14 @@ public class LockSettingsService extends ILockSettings.Stub {
* hash to GK.
*/
private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash,
- byte[] credential, @ChallengeType int challengeType, long challenge,
+ LockscreenCredential credential, @ChallengeType int challengeType, long challenge,
ICheckCredentialProgressCallback progressCallback) {
- if ((storedHash == null || storedHash.hash.length == 0)
- && (credential == null || credential.length == 0)) {
+ if ((storedHash == null || storedHash.hash.length == 0) && credential.isNone()) {
// don't need to pass empty credentials to GateKeeper
return VerifyCredentialResponse.OK;
}
- if (storedHash == null || credential == null || credential.length == 0) {
+ if (storedHash == null || storedHash.hash.length == 0 || credential.isNone()) {
return VerifyCredentialResponse.ERROR;
}
@@ -2008,8 +2078,8 @@ public class LockSettingsService extends ILockSettings.Stub {
GateKeeperResponse gateKeeperResponse;
try {
- gateKeeperResponse = getGateKeeperService()
- .verifyChallenge(userId, challenge, storedHash.hash, credential);
+ gateKeeperResponse = getGateKeeperService().verifyChallenge(
+ userId, challenge, storedHash.hash, credential.getCredential());
} catch (RemoteException e) {
Slog.e(TAG, "gatekeeper verify failed", e);
gateKeeperResponse = GateKeeperResponse.ERROR;
@@ -2025,11 +2095,11 @@ public class LockSettingsService extends ILockSettings.Stub {
try {
progressCallback.onCredentialVerified();
} catch (RemoteException e) {
- Log.w(TAG, "progressCallback throws exception", e);
+ Slog.w(TAG, "progressCallback throws exception", e);
}
}
- setUserPasswordMetrics(storedHash.type, credential, userId);
- unlockKeystore(credential, userId);
+ setUserPasswordMetrics(credential, userId);
+ unlockKeystore(credential.getCredential(), userId);
Slog.i(TAG, "Unlocking user " + userId + " with token length "
+ response.getPayload().length);
@@ -2038,27 +2108,22 @@ public class LockSettingsService extends ILockSettings.Stub {
if (isManagedProfileWithSeparatedLock(userId)) {
setDeviceUnlockedForUser(userId);
}
- int reEnrollQuality = storedHash.type == CREDENTIAL_TYPE_PATTERN
- ? DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
- : DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
- /* TODO(roosa): keep the same password quality */;
if (shouldReEnroll) {
- setLockCredentialInternal(credential, storedHash.type, credential,
- reEnrollQuality, userId, false, /* isLockTiedToParent= */ false);
+ setLockCredentialInternal(credential, credential,
+ userId, false, /* isLockTiedToParent= */ false);
} else {
// Now that we've cleared of all required GK migration, let's do the final
// migration to synthetic password.
synchronized (mSpManager) {
if (shouldMigrateToSyntheticPasswordLocked(userId)) {
AuthenticationToken auth = initializeSyntheticPasswordLocked(
- storedHash.hash, credential, storedHash.type, reEnrollQuality,
- userId);
+ storedHash.hash, credential, userId);
activateEscrowTokens(auth, userId);
}
}
}
// Use credentials to create recoverable keystore snapshot.
- sendCredentialsOnUnlockIfRequired(storedHash.type, credential, userId);
+ sendCredentialsOnUnlockIfRequired(credential, userId);
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
if (response.getTimeout() > 0) {
@@ -2074,11 +2139,9 @@ public class LockSettingsService extends ILockSettings.Stub {
* when the user is authenticating or when a new password is being set. In comparison,
* {@link #notifyPasswordChanged} only needs to be called when the user changes the password.
*/
- private void setUserPasswordMetrics(@CredentialType int credentialType, byte[] password,
- @UserIdInt int userHandle) {
+ private void setUserPasswordMetrics(LockscreenCredential password, @UserIdInt int userHandle) {
synchronized (this) {
- mUserPasswordMetrics.put(userHandle,
- PasswordMetrics.computeForCredential(credentialType, password));
+ mUserPasswordMetrics.put(userHandle, PasswordMetrics.computeForCredential(password));
}
}
@@ -2089,7 +2152,7 @@ public class LockSettingsService extends ILockSettings.Stub {
// since the user never unlock the device manually. In this case, always
// return a default metrics object. This is to distinguish this case from
// the case where during boot user password is unknown yet (returning null here)
- return new PasswordMetrics();
+ return new PasswordMetrics(CREDENTIAL_TYPE_NONE);
}
synchronized (this) {
return mUserPasswordMetrics.get(userHandle);
@@ -2101,13 +2164,20 @@ public class LockSettingsService extends ILockSettings.Stub {
* reporting the password changed.
*/
private void notifyPasswordChanged(@UserIdInt int userId) {
- // Same handler as notifyActivePasswordMetricsAvailable to ensure correct ordering
mHandler.post(() -> {
mInjector.getDevicePolicyManager().reportPasswordChanged(userId);
LocalServices.getService(WindowManagerInternal.class).reportPasswordChanged(userId);
});
}
+ private LockscreenCredential createPattern(String patternString) {
+ final byte[] patternBytes = patternString.getBytes();
+ LockscreenCredential pattern = LockscreenCredential.createPattern(
+ LockPatternUtils.byteArrayToPattern(patternBytes));
+ Arrays.fill(patternBytes, (byte) 0);
+ return pattern;
+ }
+
@Override
public boolean checkVoldPassword(int userId) {
if (!mFirstCallToVold) {
@@ -2138,30 +2208,34 @@ public class LockSettingsService extends ILockSettings.Stub {
} finally {
Binder.restoreCallingIdentity(identity);
}
- if (password == null) {
+ if (TextUtils.isEmpty(password)) {
return false;
}
try {
- if (mLockPatternUtils.isLockPatternEnabled(userId)) {
- if (checkCredential(password.getBytes(), CREDENTIAL_TYPE_PATTERN,
- userId, null /* progressCallback */)
+ final LockscreenCredential credential;
+ switch (getCredentialTypeInternal(userId)) {
+ case CREDENTIAL_TYPE_PATTERN:
+ credential = createPattern(password);
+ break;
+ case CREDENTIAL_TYPE_PIN:
+ credential = LockscreenCredential.createPin(password);
+ break;
+ case CREDENTIAL_TYPE_PASSWORD:
+ credential = LockscreenCredential.createPassword(password);
+ break;
+ default:
+ credential = null;
+ Slog.e(TAG, "Unknown credential type");
+ }
+
+ if (credential != null
+ && checkCredential(credential, userId, null /* progressCallback */)
.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
- return true;
- }
- }
- } catch (Exception e) {
- }
-
- try {
- if (mLockPatternUtils.isLockPasswordEnabled(userId)) {
- if (checkCredential(password.getBytes(), CREDENTIAL_TYPE_PASSWORD,
- userId, null /* progressCallback */)
- .getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
- return true;
- }
+ return true;
}
} catch (Exception e) {
+ Slog.e(TAG, "checkVoldPassword failed: ", e);
}
return false;
@@ -2543,7 +2617,7 @@ public class LockSettingsService extends ILockSettings.Stub {
@GuardedBy("mSpManager")
@VisibleForTesting
protected AuthenticationToken initializeSyntheticPasswordLocked(byte[] credentialHash,
- byte[] credential, int credentialType, int requestedQuality, int userId) {
+ LockscreenCredential credential, int userId) {
Slog.i(TAG, "Initialize SyntheticPassword for user: " + userId);
final AuthenticationToken auth = mSpManager.newSyntheticPasswordAndSid(
getGateKeeperService(), credentialHash, credential, userId);
@@ -2553,8 +2627,8 @@ public class LockSettingsService extends ILockSettings.Stub {
return null;
}
long handle = mSpManager.createPasswordBasedSyntheticPassword(getGateKeeperService(),
- credential, credentialType, auth, requestedQuality, userId);
- if (credential != null) {
+ credential, auth, userId);
+ if (!credential.isNone()) {
if (credentialHash == null) {
// Since when initializing SP, we didn't provide an existing password handle
// for it to migrate SID, we need to create a new SID for the user.
@@ -2586,6 +2660,13 @@ public class LockSettingsService extends ILockSettings.Stub {
}
+ @VisibleForTesting
+ boolean isSyntheticPasswordBasedCredential(int userId) {
+ synchronized (mSpManager) {
+ return isSyntheticPasswordBasedCredentialLocked(userId);
+ }
+ }
+
private boolean isSyntheticPasswordBasedCredentialLocked(int userId) {
if (userId == USER_FRP) {
final int type = mStorage.readPersistentDataBlock().type;
@@ -2611,8 +2692,8 @@ public class LockSettingsService extends ILockSettings.Stub {
setLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 1, UserHandle.USER_SYSTEM);
}
- private VerifyCredentialResponse spBasedDoVerifyCredential(byte[] userCredential,
- @CredentialType int credentialType, @ChallengeType int challengeType, long challenge,
+ private VerifyCredentialResponse spBasedDoVerifyCredential(LockscreenCredential userCredential,
+ @ChallengeType int challengeType, long challenge,
int userId, ICheckCredentialProgressCallback progressCallback,
@Nullable ArrayList<PendingResetLockout> resetLockouts) {
@@ -2620,9 +2701,6 @@ public class LockSettingsService extends ILockSettings.Stub {
Slog.d(TAG, "spBasedDoVerifyCredential: user=" + userId + " challengeType=" + challengeType
+ " hasEnrolledBiometrics=" + hasEnrolledBiometrics);
- if (credentialType == CREDENTIAL_TYPE_NONE) {
- userCredential = null;
- }
final PackageManager pm = mContext.getPackageManager();
// TODO: When lockout is handled under the HAL for all biometrics (fingerprint),
@@ -2644,17 +2722,13 @@ public class LockSettingsService extends ILockSettings.Stub {
}
if (userId == USER_FRP) {
return mSpManager.verifyFrpCredential(getGateKeeperService(),
- userCredential, credentialType, progressCallback);
+ userCredential, progressCallback);
}
long handle = getSyntheticPasswordHandleLocked(userId);
authResult = mSpManager.unwrapPasswordBasedSyntheticPassword(
getGateKeeperService(), handle, userCredential, userId, progressCallback);
- if (authResult.credentialType != credentialType) {
- Slog.e(TAG, "Credential type mismatch.");
- return VerifyCredentialResponse.ERROR;
- }
response = authResult.gkResponse;
// credential has matched
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
@@ -2672,7 +2746,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
- setUserPasswordMetrics(credentialType, userCredential, userId);
+ setUserPasswordMetrics(userCredential, userId);
unlockKeystore(authResult.authToken.deriveKeyStorePassword(), userId);
// Do resetLockout / revokeChallenge when all profiles are unlocked
@@ -2719,14 +2793,13 @@ public class LockSettingsService extends ILockSettings.Stub {
* added back when new password is set in future.
*/
@GuardedBy("mSpManager")
- private long setLockCredentialWithAuthTokenLocked(byte[] credential,
- @CredentialType int credentialType, AuthenticationToken auth, int requestedQuality,
- int userId) {
+ private long setLockCredentialWithAuthTokenLocked(LockscreenCredential credential,
+ AuthenticationToken auth, int userId) {
if (DEBUG) Slog.d(TAG, "setLockCredentialWithAuthTokenLocked: user=" + userId);
long newHandle = mSpManager.createPasswordBasedSyntheticPassword(getGateKeeperService(),
- credential, credentialType, auth, requestedQuality, userId);
- final Map<Integer, byte[]> profilePasswords;
- if (credential != null) {
+ credential, auth, userId);
+ final Map<Integer, LockscreenCredential> profilePasswords;
+ if (!credential.isNone()) {
// not needed by synchronizeUnifiedWorkChallengeForProfiles()
profilePasswords = null;
@@ -2762,27 +2835,112 @@ public class LockSettingsService extends ILockSettings.Stub {
fixateNewestUserKeyAuth(userId);
unlockKeystore(auth.deriveKeyStorePassword(), userId);
setKeystorePassword(null, userId);
+ removeBiometricsForUser(userId);
}
setSyntheticPasswordHandleLocked(newHandle, userId);
synchronizeUnifiedWorkChallengeForProfiles(userId, profilePasswords);
- setUserPasswordMetrics(credentialType, credential, userId);
+ setUserPasswordMetrics(credential, userId);
if (profilePasswords != null) {
- for (Map.Entry<Integer, byte[]> entry : profilePasswords.entrySet()) {
- Arrays.fill(entry.getValue(), (byte) 0);
+ for (Map.Entry<Integer, LockscreenCredential> entry : profilePasswords.entrySet()) {
+ entry.getValue().zeroize();
}
}
return newHandle;
}
+ private void removeBiometricsForUser(int userId) {
+ removeAllFingerprintForUser(userId);
+ removeAllFaceForUser(userId);
+ }
+
+ private void removeAllFingerprintForUser(final int userId) {
+ FingerprintManager mFingerprintManager = mInjector.getFingerprintManager();
+ if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected()) {
+ if (mFingerprintManager.hasEnrolledFingerprints(userId)) {
+ mFingerprintManager.setActiveUser(userId);
+ CountDownLatch latch = new CountDownLatch(1);
+ // For the purposes of M and N, groupId is the same as userId.
+ Fingerprint finger = new Fingerprint(null, userId, 0, 0);
+ mFingerprintManager.remove(finger, userId,
+ fingerprintManagerRemovalCallback(latch));
+ try {
+ latch.await(10000, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ Slog.e(TAG, "Latch interrupted when removing fingerprint", e);
+ }
+ }
+ }
+ }
+
+ private void removeAllFaceForUser(final int userId) {
+ FaceManager mFaceManager = mInjector.getFaceManager();
+ if (mFaceManager != null && mFaceManager.isHardwareDetected()) {
+ if (mFaceManager.hasEnrolledTemplates(userId)) {
+ mFaceManager.setActiveUser(userId);
+ CountDownLatch latch = new CountDownLatch(1);
+ Face face = new Face(null, 0, 0);
+ mFaceManager.remove(face, userId, faceManagerRemovalCallback(latch));
+ try {
+ latch.await(10000, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ Slog.e(TAG, "Latch interrupted when removing face", e);
+ }
+ }
+ }
+ }
+
+ private FingerprintManager.RemovalCallback fingerprintManagerRemovalCallback(
+ CountDownLatch latch) {
+ return new FingerprintManager.RemovalCallback() {
+ @Override
+ public void onRemovalError(Fingerprint fp, int errMsgId, CharSequence err) {
+ Slog.e(TAG, String.format(
+ "Can't remove fingerprint %d in group %d. Reason: %s",
+ fp.getBiometricId(), fp.getGroupId(), err));
+ latch.countDown();
+ }
+
+ @Override
+ public void onRemovalSucceeded(Fingerprint fp, int remaining) {
+ if (remaining == 0) {
+ latch.countDown();
+ }
+ }
+ };
+ }
+
+ private FaceManager.RemovalCallback faceManagerRemovalCallback(CountDownLatch latch) {
+ return new FaceManager.RemovalCallback() {
+ @Override
+ public void onRemovalError(Face face, int errMsgId, CharSequence err) {
+ Slog.e(TAG, String.format("Can't remove face %d. Reason: %s",
+ face.getBiometricId(), err));
+ latch.countDown();
+ }
+
+ @Override
+ public void onRemovalSucceeded(Face face, int remaining) {
+ if (remaining == 0) {
+ latch.countDown();
+ }
+ }
+ };
+ }
+
+ /**
+ * @param savedCredential if the user is a managed profile with unified challenge and
+ * savedCredential is empty, LSS will try to re-derive the profile password internally.
+ * TODO (b/80170828): Fix this so profile password is always passed in.
+ */
@GuardedBy("mSpManager")
- private boolean spBasedSetLockCredentialInternalLocked(byte[] credential, int credentialType,
- byte[] savedCredential, int requestedQuality, int userId,
+ private boolean spBasedSetLockCredentialInternalLocked(LockscreenCredential credential,
+ LockscreenCredential savedCredential, int userId,
boolean allowUntrustedChange, boolean isLockTiedToParent) {
if (DEBUG) Slog.d(TAG, "spBasedSetLockCredentialInternalLocked: user=" + userId);
- if (isManagedProfileWithUnifiedLock(userId)) {
+ if (savedCredential.isNone() && isManagedProfileWithUnifiedLock(userId)) {
// get credential from keystore when managed profile has unified lock
try {
savedCredential = getDecryptedPasswordForTiedProfile(userId);
@@ -2802,9 +2960,8 @@ public class LockSettingsService extends ILockSettings.Stub {
AuthenticationToken auth = authResult.authToken;
// If existing credential is provided, the existing credential must match.
- if (savedCredential != null && auth == null) {
- Slog.w(TAG, String.format("Failed to enroll %s: incorrect credential",
- credentialType == CREDENTIAL_TYPE_PASSWORD ? "password" : "pattern"));
+ if (!savedCredential.isNone() && auth == null) {
+ Slog.w(TAG, "Failed to enroll: incorrect credential");
return false;
}
boolean untrustedReset = false;
@@ -2837,8 +2994,7 @@ public class LockSettingsService extends ILockSettings.Stub {
// setLockCredentialWithAuthTokenLocked next
mSpManager.newSidForUser(getGateKeeperService(), auth, userId);
}
- setLockCredentialWithAuthTokenLocked(credential, credentialType, auth, requestedQuality,
- userId);
+ setLockCredentialWithAuthTokenLocked(credential, auth, userId);
mSpManager.destroyPasswordBasedSyntheticPassword(handle, userId);
} else {
throw new IllegalStateException(
@@ -2847,7 +3003,7 @@ public class LockSettingsService extends ILockSettings.Stub {
// requestedQuality, userId) instead if we still allow untrusted reset that changes
// synthetic password. That would invalidate existing escrow tokens though.
}
- sendCredentialsOnChangeIfRequired(credentialType, credential, userId, isLockTiedToParent);
+ sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent);
return true;
}
@@ -2858,11 +3014,8 @@ public class LockSettingsService extends ILockSettings.Stub {
* If user is a managed profile with unified challenge, currentCredential is ignored.
*/
@Override
- public byte[] getHashFactor(byte[] currentCredential, int userId) {
+ public byte[] getHashFactor(LockscreenCredential currentCredential, int userId) {
checkPasswordReadPermission(userId);
- if (currentCredential == null || currentCredential.length == 0) {
- currentCredential = null;
- }
if (isManagedProfileWithUnifiedLock(userId)) {
try {
currentCredential = getDecryptedPasswordForTiedProfile(userId);
@@ -2896,13 +3049,12 @@ public class LockSettingsService extends ILockSettings.Stub {
AuthenticationToken auth = null;
if (!isUserSecure(userId)) {
if (shouldMigrateToSyntheticPasswordLocked(userId)) {
- auth = initializeSyntheticPasswordLocked(null, null,
- CREDENTIAL_TYPE_NONE,
- DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userId);
+ auth = initializeSyntheticPasswordLocked(
+ /* credentialHash */ null, LockscreenCredential.createNone(), userId);
} else /* isSyntheticPasswordBasedCredentialLocked(userId) */ {
long pwdHandle = getSyntheticPasswordHandleLocked(userId);
auth = mSpManager.unwrapPasswordBasedSyntheticPassword(getGateKeeperService(),
- pwdHandle, null, userId, null).authToken;
+ pwdHandle, LockscreenCredential.createNone(), userId, null).authToken;
}
}
if (isSyntheticPasswordBasedCredentialLocked(userId)) {
@@ -2962,21 +3114,21 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
- private boolean setLockCredentialWithToken(byte[] credential, int type, long tokenHandle,
- byte[] token, int requestedQuality, int userId) {
+ private boolean setLockCredentialWithToken(LockscreenCredential credential, long tokenHandle,
+ byte[] token, int userId) {
boolean result;
synchronized (mSpManager) {
if (!mSpManager.hasEscrowData(userId)) {
throw new SecurityException("Escrow token is disabled on the current user");
}
- result = setLockCredentialWithTokenInternalLocked(credential, type, tokenHandle, token,
- requestedQuality, userId);
+ result = setLockCredentialWithTokenInternalLocked(
+ credential, tokenHandle, token, userId);
}
if (result) {
synchronized (mSeparateChallengeLock) {
- setSeparateProfileChallengeEnabledLocked(userId, true, null);
+ setSeparateProfileChallengeEnabledLocked(userId, true, /* unused */ null);
}
- if (credential == null) {
+ if (credential.isNone()) {
// If clearing credential, unlock the user manually in order to progress user start
// Call unlockUser() on a handler thread so no lock is held (either by LSS or by
// the caller like DPMS), otherwise it can lead to deadlock.
@@ -2989,8 +3141,8 @@ public class LockSettingsService extends ILockSettings.Stub {
}
@GuardedBy("mSpManager")
- private boolean setLockCredentialWithTokenInternalLocked(byte[] credential, int type,
- long tokenHandle, byte[] token, int requestedQuality, int userId) {
+ private boolean setLockCredentialWithTokenInternalLocked(LockscreenCredential credential,
+ long tokenHandle, byte[] token, int userId) {
final AuthenticationResult result;
result = mSpManager.unwrapTokenBasedSyntheticPassword(
getGateKeeperService(), tokenHandle, token, userId);
@@ -3005,11 +3157,8 @@ public class LockSettingsService extends ILockSettings.Stub {
+ "verification.");
return false;
}
- // TODO: refactor usage of PASSWORD_TYPE_KEY b/65239740
- setLong(LockPatternUtils.PASSWORD_TYPE_KEY, requestedQuality, userId);
long oldHandle = getSyntheticPasswordHandleLocked(userId);
- setLockCredentialWithAuthTokenLocked(credential, type, result.authToken,
- requestedQuality, userId);
+ setLockCredentialWithAuthTokenLocked(credential, result.authToken, userId);
mSpManager.destroyPasswordBasedSyntheticPassword(oldHandle, userId);
onAuthTokenKnownForUser(userId, result.authToken);
@@ -3071,11 +3220,10 @@ public class LockSettingsService extends ILockSettings.Stub {
}
// It's OK to dump the password type since anyone with physical access can just
// observe it from the keyguard directly.
- pw.println("PasswordType: " + getLong(LockPatternUtils.PASSWORD_TYPE_KEY, 0, userId));
- pw.println("hasPassword: " + havePassword(userId));
- pw.println("hasPattern: " + havePattern(userId)); // print raw credential type instead?
+ pw.println("Quality: " + getKeyguardStoredQuality(userId));
+ pw.println("CredentialType: " + getCredentialTypeInternal(userId));
pw.println("SeparateChallenge: " + getSeparateProfileChallengeEnabled(userId));
- pw.println(String.format("metrics: %s",
+ pw.println(String.format("Metrics: %s",
getUserPasswordMetrics(userId) != null ? "known" : "unknown"));
pw.decreaseIndent();
}
@@ -3089,45 +3237,43 @@ public class LockSettingsService extends ILockSettings.Stub {
pw.decreaseIndent();
}
+ /**
+ * Cryptographically disable escrow token support for the current user, if the user is not
+ * managed (either user has a profile owner, or if device is managed). Do not disable
+ * if we are running an automotive build.
+ */
private void disableEscrowTokenOnNonManagedDevicesIfNeeded(int userId) {
- long ident = Binder.clearCallingIdentity();
- try {
- // Managed profile should have escrow enabled
- if (mUserManager.getUserInfo(userId).isManagedProfile()) {
- Slog.i(TAG, "Managed profile can have escrow token");
- return;
- }
- DevicePolicyManager dpm = mInjector.getDevicePolicyManager();
- // Devices with Device Owner should have escrow enabled on all users.
- if (dpm.getDeviceOwnerComponentOnAnyUser() != null) {
- Slog.i(TAG, "Corp-owned device can have escrow token");
- return;
- }
- // We could also have a profile owner on the given (non-managed) user for unicorn cases
- if (dpm.getProfileOwnerAsUser(userId) != null) {
- Slog.i(TAG, "User with profile owner can have escrow token");
- return;
- }
- // If the device is yet to be provisioned (still in SUW), there is still
- // a chance that Device Owner will be set on the device later, so postpone
- // disabling escrow token for now.
- if (!dpm.isDeviceProvisioned()) {
- Slog.i(TAG, "Postpone disabling escrow tokens until device is provisioned");
- return;
- }
+ final UserManagerInternal userManagerInternal = mInjector.getUserManagerInternal();
- // Escrow tokens are enabled on automotive builds.
- if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
- return;
- }
+ // Managed profile should have escrow enabled
+ if (userManagerInternal.isUserManaged(userId)) {
+ Slog.i(TAG, "Managed profile can have escrow token");
+ return;
+ }
- // Disable escrow token permanently on all other device/user types.
- Slog.i(TAG, "Disabling escrow token on user " + userId);
- if (isSyntheticPasswordBasedCredentialLocked(userId)) {
- mSpManager.destroyEscrowData(userId);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
+ // Devices with Device Owner should have escrow enabled on all users.
+ if (userManagerInternal.isDeviceManaged()) {
+ Slog.i(TAG, "Corp-owned device can have escrow token");
+ return;
+ }
+
+ // If the device is yet to be provisioned (still in SUW), there is still
+ // a chance that Device Owner will be set on the device later, so postpone
+ // disabling escrow token for now.
+ if (!mInjector.getDeviceStateCache().isDeviceProvisioned()) {
+ Slog.i(TAG, "Postpone disabling escrow tokens until device is provisioned");
+ return;
+ }
+
+ // Escrow tokens are enabled on automotive builds.
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ return;
+ }
+
+ // Disable escrow token permanently on all other device/user types.
+ Slog.i(TAG, "Disabling escrow token on user " + userId);
+ if (isSyntheticPasswordBasedCredentialLocked(userId)) {
+ mSpManager.destroyEscrowData(userId);
}
}
@@ -3239,14 +3385,14 @@ public class LockSettingsService extends ILockSettings.Stub {
}
@Override
- public boolean setLockCredentialWithToken(byte[] credential, int type,
- long tokenHandle, byte[] token, int requestedQuality, int userId) {
+ public boolean setLockCredentialWithToken(LockscreenCredential credential, long tokenHandle,
+ byte[] token, int userId) {
if (!mLockPatternUtils.hasSecureLockScreen()) {
throw new UnsupportedOperationException(
"This operation requires secure lock screen feature.");
}
- return LockSettingsService.this.setLockCredentialWithToken(credential, type,
- tokenHandle, token, requestedQuality, userId);
+ return LockSettingsService.this.setLockCredentialWithToken(
+ credential, tokenHandle, token, userId);
}
@Override
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index a5d59e3c8884..0f8561e5afd9 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -16,18 +16,22 @@
package com.android.server.locksettings;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
-import static com.android.internal.widget.LockPatternUtils.stringToPattern;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
import android.app.ActivityManager;
+import android.app.admin.PasswordMetrics;
import android.os.ShellCommand;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
+import com.android.internal.widget.LockscreenCredential;
+import com.android.internal.widget.PasswordValidationError;
import java.io.PrintWriter;
+import java.util.List;
class LockSettingsShellCommand extends ShellCommand {
@@ -74,18 +78,19 @@ class LockSettingsShellCommand extends ShellCommand {
if (!checkCredential()) {
return -1;
}
+ boolean success = true;
switch (cmd) {
case COMMAND_SET_PATTERN:
- runSetPattern();
+ success = runSetPattern();
break;
case COMMAND_SET_PASSWORD:
- runSetPassword();
+ success = runSetPassword();
break;
case COMMAND_SET_PIN:
- runSetPin();
+ success = runSetPin();
break;
case COMMAND_CLEAR:
- runClear();
+ success = runClear();
break;
case COMMAND_SP:
runChangeSp();
@@ -106,7 +111,7 @@ class LockSettingsShellCommand extends ShellCommand {
getErrPrintWriter().println("Unknown command: " + cmd);
break;
}
- return 0;
+ return success ? 0 : -1;
} catch (Exception e) {
getErrPrintWriter().println("Error while executing command: " + cmd);
e.printStackTrace(getErrPrintWriter());
@@ -189,32 +194,82 @@ class LockSettingsShellCommand extends ShellCommand {
mLockPatternUtils.isSyntheticPasswordEnabled()));
}
- private void runSetPattern() {
- byte[] oldBytes = mOld != null ? mOld.getBytes() : null;
- mLockPatternUtils.saveLockPattern(stringToPattern(mNew), oldBytes, mCurrentUserId);
+ private LockscreenCredential getOldCredential() {
+ if (mLockPatternUtils.isLockPasswordEnabled(mCurrentUserId)) {
+ final int quality = mLockPatternUtils.getKeyguardStoredPasswordQuality(mCurrentUserId);
+ if (LockPatternUtils.isQualityAlphabeticPassword(quality)) {
+ return LockscreenCredential.createPassword(mOld);
+ } else {
+ return LockscreenCredential.createPin(mOld);
+ }
+ } else if (mLockPatternUtils.isLockPatternEnabled(mCurrentUserId)) {
+ return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
+ mOld.getBytes()));
+ } else {
+ return LockscreenCredential.createNone();
+ }
+ }
+
+ private boolean runSetPattern() {
+ final LockscreenCredential pattern = LockscreenCredential.createPattern(
+ LockPatternUtils.byteArrayToPattern(mNew.getBytes()));
+ if (!isNewCredentialSufficient(pattern)) {
+ return false;
+ }
+ mLockPatternUtils.setLockCredential(pattern, getOldCredential(), mCurrentUserId);
getOutPrintWriter().println("Pattern set to '" + mNew + "'");
+ return true;
}
- private void runSetPassword() {
- byte[] newBytes = mNew != null ? mNew.getBytes() : null;
- byte[] oldBytes = mOld != null ? mOld.getBytes() : null;
- mLockPatternUtils.saveLockPassword(newBytes, oldBytes, PASSWORD_QUALITY_ALPHABETIC,
- mCurrentUserId);
+ private boolean runSetPassword() {
+ final LockscreenCredential password = LockscreenCredential.createPassword(mNew);
+ if (!isNewCredentialSufficient(password)) {
+ return false;
+ }
+ mLockPatternUtils.setLockCredential(password, getOldCredential(), mCurrentUserId);
getOutPrintWriter().println("Password set to '" + mNew + "'");
+ return true;
}
- private void runSetPin() {
- byte[] newBytes = mNew != null ? mNew.getBytes() : null;
- byte[] oldBytes = mOld != null ? mOld.getBytes() : null;
- mLockPatternUtils.saveLockPassword(newBytes, oldBytes, PASSWORD_QUALITY_NUMERIC,
- mCurrentUserId);
+ private boolean runSetPin() {
+ final LockscreenCredential pin = LockscreenCredential.createPin(mNew);
+ if (!isNewCredentialSufficient(pin)) {
+ return false;
+ }
+ mLockPatternUtils.setLockCredential(pin, getOldCredential(), mCurrentUserId);
getOutPrintWriter().println("Pin set to '" + mNew + "'");
+ return true;
}
- private void runClear() {
- byte[] oldBytes = mOld != null ? mOld.getBytes() : null;
- mLockPatternUtils.clearLock(oldBytes, mCurrentUserId);
+ private boolean runClear() {
+ LockscreenCredential none = LockscreenCredential.createNone();
+ if (!isNewCredentialSufficient(none)) {
+ return false;
+ }
+ mLockPatternUtils.setLockCredential(none, getOldCredential(), mCurrentUserId);
getOutPrintWriter().println("Lock credential cleared");
+ return true;
+ }
+
+ private boolean isNewCredentialSufficient(LockscreenCredential credential) {
+ final PasswordMetrics requiredMetrics =
+ mLockPatternUtils.getRequestedPasswordMetrics(mCurrentUserId);
+ final List<PasswordValidationError> errors;
+ if (credential.isPassword() || credential.isPin()) {
+ errors = PasswordMetrics.validatePassword(requiredMetrics, PASSWORD_COMPLEXITY_NONE,
+ credential.isPin(), credential.getCredential());
+ } else {
+ PasswordMetrics metrics = new PasswordMetrics(
+ credential.isPattern() ? CREDENTIAL_TYPE_PATTERN : CREDENTIAL_TYPE_NONE);
+ errors = PasswordMetrics.validatePasswordMetrics(
+ requiredMetrics, PASSWORD_COMPLEXITY_NONE, false /* isPin */, metrics);
+ }
+ if (!errors.isEmpty()) {
+ getOutPrintWriter().println(
+ "New credential doesn't satisfy admin policies: " + errors.get(0));
+ return false;
+ }
+ return true;
}
private void runSetDisabled() {
@@ -229,22 +284,15 @@ class LockSettingsShellCommand extends ShellCommand {
}
private boolean checkCredential() {
- final boolean havePassword = mLockPatternUtils.isLockPasswordEnabled(mCurrentUserId);
- final boolean havePattern = mLockPatternUtils.isLockPatternEnabled(mCurrentUserId);
- if (havePassword || havePattern) {
+ if (mLockPatternUtils.isSecure(mCurrentUserId)) {
if (mLockPatternUtils.isManagedProfileWithUnifiedChallenge(mCurrentUserId)) {
getOutPrintWriter().println("Profile uses unified challenge");
return false;
}
try {
- final boolean result;
- if (havePassword) {
- byte[] passwordBytes = mOld != null ? mOld.getBytes() : null;
- result = mLockPatternUtils.checkPassword(passwordBytes, mCurrentUserId);
- } else {
- result = mLockPatternUtils.checkPattern(stringToPattern(mOld), mCurrentUserId);
- }
+ final boolean result = mLockPatternUtils.checkCredential(getOldCredential(),
+ mCurrentUserId, null);
if (!result) {
if (!mLockPatternUtils.isManagedProfileWithUnifiedChallenge(mCurrentUserId)) {
mLockPatternUtils.reportFailedPasswordAttempt(mCurrentUserId);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 559461485042..3dab3ce7116c 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -30,7 +30,6 @@ import android.os.Environment;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArrayMap;
-import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -95,8 +94,6 @@ class LockSettingsStorage {
@VisibleForTesting
public static class CredentialHash {
- /** Deprecated private static final int VERSION_LEGACY = 0; */
- private static final int VERSION_GATEKEEPER = 1;
private CredentialHash(byte[] hash, @CredentialType int type) {
if (type != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
@@ -126,42 +123,6 @@ class LockSettingsStorage {
byte[] hash;
@CredentialType int type;
-
- public byte[] toBytes() {
- try {
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- DataOutputStream dos = new DataOutputStream(os);
- dos.write(VERSION_GATEKEEPER);
- dos.write(type);
- if (hash != null && hash.length > 0) {
- dos.writeInt(hash.length);
- dos.write(hash);
- } else {
- dos.writeInt(0);
- }
- dos.close();
- return os.toByteArray();
- } catch (IOException e) {
- throw new IllegalStateException("Fail to serialze credential hash", e);
- }
- }
-
- public static CredentialHash fromBytes(byte[] bytes) {
- try {
- DataInputStream is = new DataInputStream(new ByteArrayInputStream(bytes));
- /* int version = */ is.read();
- int type = is.read();
- int hashSize = is.readInt();
- byte[] hash = null;
- if (hashSize > 0) {
- hash = new byte[hashSize];
- is.readFully(hash);
- }
- return new CredentialHash(hash, type);
- } catch (IOException e) {
- throw new IllegalStateException("Fail to deserialze credential hash", e);
- }
- }
}
public LockSettingsStorage(Context context) {
@@ -252,7 +213,7 @@ class LockSettingsStorage {
private CredentialHash readPasswordHashIfExists(int userId) {
byte[] stored = readFile(getLockPasswordFilename(userId));
if (!ArrayUtils.isEmpty(stored)) {
- return new CredentialHash(stored, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD);
+ return new CredentialHash(stored, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN);
}
return null;
}
@@ -308,10 +269,6 @@ class LockSettingsStorage {
return hasFile(getLockPatternFilename(userId));
}
- public boolean hasCredential(int userId) {
- return hasPassword(userId) || hasPattern(userId);
- }
-
private boolean hasFile(String name) {
byte[] contents = readFile(name);
return contents != null && contents.length > 0;
@@ -398,11 +355,15 @@ class LockSettingsStorage {
public void writeCredentialHash(CredentialHash hash, int userId) {
byte[] patternHash = null;
byte[] passwordHash = null;
-
- if (hash.type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) {
+ if (hash.type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN
+ || hash.type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD
+ || hash.type == LockPatternUtils.CREDENTIAL_TYPE_PIN) {
passwordHash = hash.hash;
} else if (hash.type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN) {
patternHash = hash.hash;
+ } else {
+ Preconditions.checkArgument(hash.type == LockPatternUtils.CREDENTIAL_TYPE_NONE,
+ "Unknown credential type");
}
writeFile(getLockPasswordFilename(userId), passwordHash);
writeFile(getLockPatternFilename(userId), patternHash);
@@ -561,8 +522,8 @@ class LockSettingsStorage {
mCache.clear();
}
- @Nullable
- public PersistentDataBlockManagerInternal getPersistentDataBlock() {
+ @Nullable @VisibleForTesting
+ PersistentDataBlockManagerInternal getPersistentDataBlockManager() {
if (mPersistentDataBlockManagerInternal == null) {
mPersistentDataBlockManagerInternal =
LocalServices.getService(PersistentDataBlockManagerInternal.class);
@@ -572,7 +533,7 @@ class LockSettingsStorage {
public void writePersistentDataBlock(int persistentType, int userId, int qualityForUi,
byte[] payload) {
- PersistentDataBlockManagerInternal persistentDataBlock = getPersistentDataBlock();
+ PersistentDataBlockManagerInternal persistentDataBlock = getPersistentDataBlockManager();
if (persistentDataBlock == null) {
return;
}
@@ -581,7 +542,7 @@ class LockSettingsStorage {
}
public PersistentData readPersistentDataBlock() {
- PersistentDataBlockManagerInternal persistentDataBlock = getPersistentDataBlock();
+ PersistentDataBlockManagerInternal persistentDataBlock = getPersistentDataBlockManager();
if (persistentDataBlock == null) {
return PersistentData.NONE;
}
@@ -737,7 +698,7 @@ class LockSettingsStorage {
}
if (upgradeVersion != DATABASE_VERSION) {
- Log.w(TAG, "Failed to upgrade database!");
+ Slog.w(TAG, "Failed to upgrade database!");
}
}
}
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
index ea0fb47a49b3..9246311fcdc7 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
@@ -129,6 +129,9 @@ public class SyntheticPasswordCrypto {
keyStore.load(null);
SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null);
+ if (decryptionKey == null) {
+ throw new IllegalStateException("SP key is missing: " + keyAlias);
+ }
byte[] intermediate = decrypt(applicationId, APPLICATION_ID_PERSONALIZATION, blob);
return decrypt(decryptionKey, intermediate);
} catch (Exception e) {
@@ -143,6 +146,9 @@ public class SyntheticPasswordCrypto {
keyStore.load(null);
SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null);
+ if (decryptionKey == null) {
+ throw new IllegalStateException("SP key is missing: " + keyAlias);
+ }
byte[] intermediate = decrypt(decryptionKey, blob);
return decrypt(applicationId, APPLICATION_ID_PERSONALIZATION, intermediate);
} catch (CertificateException | IOException | BadPaddingException
@@ -193,6 +199,7 @@ public class SyntheticPasswordCrypto {
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
keyStore.deleteEntry(keyAlias);
+ Slog.i(TAG, "SP key deleted: " + keyAlias);
} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException
| IOException e) {
Slog.e(TAG, "Failed to destroy blob", e);
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 955a9aa8d0de..53d922b6da9f 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -20,7 +20,6 @@ import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChang
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.pm.UserInfo;
import android.hardware.weaver.V1_0.IWeaver;
@@ -35,13 +34,13 @@ import android.security.Scrypt;
import android.service.gatekeeper.GateKeeperResponse;
import android.service.gatekeeper.IGateKeeperService;
import android.util.ArrayMap;
-import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.widget.ICheckCredentialProgressCallback;
import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.server.locksettings.LockSettingsStorage.PersistentData;
@@ -138,7 +137,6 @@ public class SyntheticPasswordManager {
static class AuthenticationResult {
public AuthenticationToken authToken;
public VerifyCredentialResponse gkResponse;
- public int credentialType;
}
static class AuthenticationToken {
@@ -220,7 +218,7 @@ public class SyntheticPasswordManager {
byte scryptN;
byte scryptR;
byte scryptP;
- public int passwordType;
+ public int credentialType;
byte[] salt;
// For GateKeeper-based credential, this is the password handle returned by GK,
// for weaver-based credential, this is empty.
@@ -231,7 +229,7 @@ public class SyntheticPasswordManager {
result.scryptN = PASSWORD_SCRYPT_N;
result.scryptR = PASSWORD_SCRYPT_R;
result.scryptP = PASSWORD_SCRYPT_P;
- result.passwordType = passwordType;
+ result.credentialType = passwordType;
result.salt = secureRandom(PASSWORD_SALT_LENGTH);
return result;
}
@@ -241,7 +239,7 @@ public class SyntheticPasswordManager {
ByteBuffer buffer = ByteBuffer.allocate(data.length);
buffer.put(data, 0, data.length);
buffer.flip();
- result.passwordType = buffer.getInt();
+ result.credentialType = buffer.getInt();
result.scryptN = buffer.get();
result.scryptR = buffer.get();
result.scryptP = buffer.get();
@@ -263,7 +261,7 @@ public class SyntheticPasswordManager {
ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + 3 * Byte.BYTES
+ Integer.BYTES + salt.length + Integer.BYTES +
(passwordHandle != null ? passwordHandle.length : 0));
- buffer.putInt(passwordType);
+ buffer.putInt(credentialType);
buffer.put(scryptN);
buffer.put(scryptR);
buffer.put(scryptP);
@@ -366,11 +364,11 @@ public class SyntheticPasswordManager {
try {
int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value));
if (writeStatus != WeaverStatus.OK) {
- Log.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus);
+ Slog.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus);
return null;
}
} catch (RemoteException e) {
- Log.e(TAG, "weaver write failed", e);
+ Slog.e(TAG, "weaver write failed", e);
return null;
}
return value;
@@ -401,31 +399,31 @@ public class SyntheticPasswordManager {
break;
case WeaverReadStatus.THROTTLE:
response[0] = new VerifyCredentialResponse(readResponse.timeout);
- Log.e(TAG, "weaver read failed (THROTTLE), slot: " + slot);
+ Slog.e(TAG, "weaver read failed (THROTTLE), slot: " + slot);
break;
case WeaverReadStatus.INCORRECT_KEY:
if (readResponse.timeout == 0) {
response[0] = VerifyCredentialResponse.ERROR;
- Log.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot);
+ Slog.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot);
} else {
response[0] = new VerifyCredentialResponse(readResponse.timeout);
- Log.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: "
+ Slog.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: "
+ slot);
}
break;
case WeaverReadStatus.FAILED:
response[0] = VerifyCredentialResponse.ERROR;
- Log.e(TAG, "weaver read failed (FAILED), slot: " + slot);
+ Slog.e(TAG, "weaver read failed (FAILED), slot: " + slot);
break;
default:
response[0] = VerifyCredentialResponse.ERROR;
- Log.e(TAG, "weaver read unknown status " + status + ", slot: " + slot);
+ Slog.e(TAG, "weaver read unknown status " + status + ", slot: " + slot);
break;
}
});
} catch (RemoteException e) {
response[0] = VerifyCredentialResponse.ERROR;
- Log.e(TAG, "weaver read failed, slot: " + slot, e);
+ Slog.e(TAG, "weaver read failed, slot: " + slot, e);
}
return response[0];
}
@@ -437,13 +435,20 @@ public class SyntheticPasswordManager {
}
}
- public int getCredentialType(long handle, int userId) {
+ int getCredentialType(long handle, int userId) {
byte[] passwordData = loadState(PASSWORD_DATA_NAME, handle, userId);
if (passwordData == null) {
- Log.w(TAG, "getCredentialType: encountered empty password data for user " + userId);
+ Slog.w(TAG, "getCredentialType: encountered empty password data for user " + userId);
+ return LockPatternUtils.CREDENTIAL_TYPE_NONE;
+ }
+ return PasswordData.fromBytes(passwordData).credentialType;
+ }
+
+ static int getFrpCredentialType(byte[] payload) {
+ if (payload == null) {
return LockPatternUtils.CREDENTIAL_TYPE_NONE;
}
- return PasswordData.fromBytes(passwordData).passwordType;
+ return PasswordData.fromBytes(payload).credentialType;
}
/**
@@ -469,17 +474,18 @@ public class SyntheticPasswordManager {
*
*/
public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper,
- byte[] hash, byte[] credential, int userId) {
+ byte[] hash, LockscreenCredential credential, int userId) {
AuthenticationToken result = AuthenticationToken.create();
GateKeeperResponse response;
if (hash != null) {
try {
- response = gatekeeper.enroll(userId, hash, credential, result.deriveGkPassword());
+ response = gatekeeper.enroll(userId, hash, credential.getCredential(),
+ result.deriveGkPassword());
} catch (RemoteException e) {
throw new IllegalStateException("Failed to enroll credential duing SP init", e);
}
if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
- Log.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId);
+ Slog.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId);
clearSidForUser(userId);
} else {
saveSyntheticPasswordHandle(response.getPayload(), userId);
@@ -504,7 +510,7 @@ public class SyntheticPasswordManager {
throw new IllegalStateException("Failed to create new SID for user", e);
}
if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
- Log.e(TAG, "Fail to create new SID for user " + userId);
+ Slog.e(TAG, "Fail to create new SID for user " + userId);
return;
}
saveSyntheticPasswordHandle(response.getPayload(), userId);
@@ -561,7 +567,7 @@ public class SyntheticPasswordManager {
buffer.put(data, 0, data.length);
buffer.flip();
if (buffer.get() != WEAVER_VERSION) {
- Log.e(TAG, "Invalid weaver slot version of handle " + handle);
+ Slog.e(TAG, "Invalid weaver slot version of handle " + handle);
return INVALID_WEAVER_SLOT;
}
return buffer.getInt();
@@ -580,11 +586,11 @@ public class SyntheticPasswordManager {
if (slot != INVALID_WEAVER_SLOT) {
Set<Integer> usedSlots = getUsedWeaverSlots();
if (!usedSlots.contains(slot)) {
- Log.i(TAG, "Destroy weaver slot " + slot + " for user " + userId);
+ Slog.i(TAG, "Destroy weaver slot " + slot + " for user " + userId);
weaverEnroll(slot, null, null);
mPasswordSlotManager.markSlotDeleted(slot);
} else {
- Log.w(TAG, "Skip destroying reused weaver slot " + slot + " for user " + userId);
+ Slog.w(TAG, "Skip destroying reused weaver slot " + slot + " for user " + userId);
}
}
}
@@ -638,15 +644,9 @@ public class SyntheticPasswordManager {
* @throw IllegalStateException if creation fails.
*/
public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
- byte[] credential, int credentialType, AuthenticationToken authToken,
- int requestedQuality, int userId) {
- if (credential == null || credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
- credentialType = LockPatternUtils.CREDENTIAL_TYPE_NONE;
- credential = DEFAULT_PASSWORD;
- }
-
+ LockscreenCredential credential, AuthenticationToken authToken, int userId) {
long handle = generateHandle();
- PasswordData pwd = PasswordData.create(credentialType);
+ PasswordData pwd = PasswordData.create(credential.getType());
byte[] pwdToken = computePasswordToken(credential, pwd);
final long sid;
final byte[] applicationId;
@@ -654,7 +654,7 @@ public class SyntheticPasswordManager {
if (isWeaverAvailable()) {
// Weaver based user password
int weaverSlot = getNextAvailableWeaverSlot();
- Log.i(TAG, "Weaver enroll password to slot " + weaverSlot + " for user " + userId);
+ Slog.i(TAG, "Weaver enroll password to slot " + weaverSlot + " for user " + userId);
byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken),
null);
if (weaverSecret == null) {
@@ -663,7 +663,8 @@ public class SyntheticPasswordManager {
}
saveWeaverSlot(weaverSlot, handle, userId);
mPasswordSlotManager.markSlotInUse(weaverSlot);
- synchronizeWeaverFrpPassword(pwd, requestedQuality, userId, weaverSlot);
+ // No need to pass in quality since the credential type already encodes sufficient info
+ synchronizeWeaverFrpPassword(pwd, 0, userId, weaverSlot);
pwd.passwordHandle = null;
sid = GateKeeper.INVALID_SECURE_USER_ID;
@@ -674,7 +675,7 @@ public class SyntheticPasswordManager {
try {
gatekeeper.clearSecureUserId(fakeUid(userId));
} catch (RemoteException ignore) {
- Log.w(TAG, "Failed to clear SID from gatekeeper");
+ Slog.w(TAG, "Failed to clear SID from gatekeeper");
}
// GateKeeper based user password
GateKeeperResponse response;
@@ -692,7 +693,8 @@ public class SyntheticPasswordManager {
sid = sidFromPasswordHandle(pwd.passwordHandle);
applicationId = transformUnderSecdiscardable(pwdToken,
createSecdiscardable(handle, userId));
- synchronizeFrpPassword(pwd, requestedQuality, userId);
+ // No need to pass in quality since the credential type already encodes sufficient info
+ synchronizeFrpPassword(pwd, 0, userId);
}
saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
@@ -702,7 +704,7 @@ public class SyntheticPasswordManager {
}
public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper,
- byte[] userCredential, int credentialType,
+ LockscreenCredential userCredential,
ICheckCredentialProgressCallback progressCallback) {
PersistentData persistentData = mStorage.readPersistentDataBlock();
if (persistentData.type == PersistentData.TYPE_SP) {
@@ -714,7 +716,7 @@ public class SyntheticPasswordManager {
response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId),
0 /* challenge */, pwd.passwordHandle, passwordTokenToGkInput(pwdToken));
} catch (RemoteException e) {
- Log.e(TAG, "FRP verifyChallenge failed", e);
+ Slog.e(TAG, "FRP verifyChallenge failed", e);
return VerifyCredentialResponse.ERROR;
}
return VerifyCredentialResponse.fromGateKeeperResponse(response);
@@ -725,7 +727,7 @@ public class SyntheticPasswordManager {
return weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken)).stripPayload();
} else {
- Log.e(TAG, "persistentData.type must be TYPE_SP or TYPE_SP_WEAVER, but is "
+ Slog.e(TAG, "persistentData.type must be TYPE_SP or TYPE_SP_WEAVER, but is "
+ persistentData.type);
return VerifyCredentialResponse.ERROR;
}
@@ -733,11 +735,11 @@ public class SyntheticPasswordManager {
public void migrateFrpPasswordLocked(long handle, UserInfo userInfo, int requestedQuality) {
- if (mStorage.getPersistentDataBlock() != null
+ if (mStorage.getPersistentDataBlockManager() != null
&& LockPatternUtils.userOwnsFrpCredential(mContext, userInfo)) {
PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle,
userInfo.id));
- if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
+ if (pwd.credentialType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
int weaverSlot = loadWeaverSlot(handle, userInfo.id);
if (weaverSlot != INVALID_WEAVER_SLOT) {
synchronizeWeaverFrpPassword(pwd, requestedQuality, userInfo.id, weaverSlot);
@@ -750,10 +752,10 @@ public class SyntheticPasswordManager {
private void synchronizeFrpPassword(PasswordData pwd,
int requestedQuality, int userId) {
- if (mStorage.getPersistentDataBlock() != null
+ if (mStorage.getPersistentDataBlockManager() != null
&& LockPatternUtils.userOwnsFrpCredential(mContext,
mUserManager.getUserInfo(userId))) {
- if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
+ if (pwd.credentialType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
mStorage.writePersistentDataBlock(PersistentData.TYPE_SP, userId, requestedQuality,
pwd.toBytes());
} else {
@@ -764,10 +766,10 @@ public class SyntheticPasswordManager {
private void synchronizeWeaverFrpPassword(PasswordData pwd, int requestedQuality, int userId,
int weaverSlot) {
- if (mStorage.getPersistentDataBlock() != null
+ if (mStorage.getPersistentDataBlockManager() != null
&& LockPatternUtils.userOwnsFrpCredential(mContext,
mUserManager.getUserInfo(userId))) {
- if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
+ if (pwd.credentialType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
mStorage.writePersistentDataBlock(PersistentData.TYPE_SP_WEAVER, weaverSlot,
requestedQuality, pwd.toBytes());
} else {
@@ -829,14 +831,14 @@ public class SyntheticPasswordManager {
return false;
}
if (!loadEscrowData(authToken, userId)) {
- Log.w(TAG, "User is not escrowable");
+ Slog.w(TAG, "User is not escrowable");
return false;
}
if (isWeaverAvailable()) {
int slot = getNextAvailableWeaverSlot();
- Log.i(TAG, "Weaver enroll token to slot " + slot + " for user " + userId);
+ Slog.i(TAG, "Weaver enroll token to slot " + slot + " for user " + userId);
if (weaverEnroll(slot, null, tokenData.weaverSecret) == null) {
- Log.e(TAG, "Failed to enroll weaver secret when activating token");
+ Slog.e(TAG, "Failed to enroll weaver secret when activating token");
return false;
}
saveWeaverSlot(slot, handle, userId);
@@ -881,18 +883,20 @@ public class SyntheticPasswordManager {
* Decrypt a synthetic password by supplying the user credential and corresponding password
* blob handle generated previously. If the decryption is successful, initiate a GateKeeper
* verification to referesh the SID & Auth token maintained by the system.
- * Note: the credential type is not validated here since there are call sites where the type is
- * unknown. Caller might choose to validate it by examining AuthenticationResult.credentialType
*/
public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
- long handle, byte[] credential, int userId,
+ long handle, @NonNull LockscreenCredential credential, int userId,
ICheckCredentialProgressCallback progressCallback) {
- if (credential == null) {
- credential = DEFAULT_PASSWORD;
- }
AuthenticationResult result = new AuthenticationResult();
PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle, userId));
- result.credentialType = pwd.passwordType;
+
+ if (!credential.checkAgainstStoredType(pwd.credentialType)) {
+ Slog.e(TAG, String.format("Credential type mismatch: expected %d actual %d",
+ pwd.credentialType, credential.getType()));
+ result.gkResponse = VerifyCredentialResponse.ERROR;
+ return result;
+ }
+
byte[] pwdToken = computePasswordToken(credential, pwd);
final byte[] applicationId;
@@ -901,7 +905,7 @@ public class SyntheticPasswordManager {
if (weaverSlot != INVALID_WEAVER_SLOT) {
// Weaver based user password
if (!isWeaverAvailable()) {
- Log.e(TAG, "No weaver service to unwrap password based SP");
+ Slog.e(TAG, "No weaver service to unwrap password based SP");
result.gkResponse = VerifyCredentialResponse.ERROR;
return result;
}
@@ -918,7 +922,7 @@ public class SyntheticPasswordManager {
response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
pwd.passwordHandle, gkPwdToken);
} catch (RemoteException e) {
- Log.e(TAG, "gatekeeper verify failed", e);
+ Slog.e(TAG, "gatekeeper verify failed", e);
result.gkResponse = VerifyCredentialResponse.ERROR;
return result;
}
@@ -931,21 +935,19 @@ public class SyntheticPasswordManager {
reenrollResponse = gatekeeper.enroll(fakeUid(userId),
pwd.passwordHandle, gkPwdToken, gkPwdToken);
} catch (RemoteException e) {
- Log.w(TAG, "Fail to invoke gatekeeper.enroll", e);
+ Slog.w(TAG, "Fail to invoke gatekeeper.enroll", e);
reenrollResponse = GateKeeperResponse.ERROR;
// continue the flow anyway
}
if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
pwd.passwordHandle = reenrollResponse.getPayload();
+ // Use the reenrollment opportunity to update credential type
+ // (getting rid of CREDENTIAL_TYPE_PASSWORD_OR_PIN)
+ pwd.credentialType = credential.getType();
saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
- synchronizeFrpPassword(pwd,
- pwd.passwordType == LockPatternUtils.CREDENTIAL_TYPE_PATTERN
- ? DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
- : DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
- /* TODO(roosa): keep the same password quality */,
- userId);
+ synchronizeFrpPassword(pwd, 0, userId);
} else {
- Log.w(TAG, "Fail to re-enroll user password for user " + userId);
+ Slog.w(TAG, "Fail to re-enroll user password for user " + userId);
// continue the flow anyway
}
}
@@ -966,7 +968,7 @@ public class SyntheticPasswordManager {
try {
progressCallback.onCredentialVerified();
} catch (RemoteException e) {
- Log.w(TAG, "progressCallback throws exception", e);
+ Slog.w(TAG, "progressCallback throws exception", e);
}
}
result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED,
@@ -989,14 +991,14 @@ public class SyntheticPasswordManager {
int slotId = loadWeaverSlot(handle, userId);
if (slotId != INVALID_WEAVER_SLOT) {
if (!isWeaverAvailable()) {
- Log.e(TAG, "No weaver service to unwrap token based SP");
+ Slog.e(TAG, "No weaver service to unwrap token based SP");
result.gkResponse = VerifyCredentialResponse.ERROR;
return result;
}
VerifyCredentialResponse response = weaverVerify(slotId, null);
if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK ||
response.getPayload() == null) {
- Log.e(TAG, "Failed to retrieve weaver secret when unwrapping token");
+ Slog.e(TAG, "Failed to retrieve weaver secret when unwrapping token");
result.gkResponse = VerifyCredentialResponse.ERROR;
return result;
}
@@ -1043,13 +1045,13 @@ public class SyntheticPasswordManager {
Arrays.copyOfRange(blob, 2, blob.length), applicationId);
}
if (secret == null) {
- Log.e(TAG, "Fail to decrypt SP for user " + userId);
+ Slog.e(TAG, "Fail to decrypt SP for user " + userId);
return null;
}
AuthenticationToken result = new AuthenticationToken(version);
if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
if (!loadEscrowData(result, userId)) {
- Log.e(TAG, "User is not escrowable: " + userId);
+ Slog.e(TAG, "User is not escrowable: " + userId);
return null;
}
result.recreate(secret);
@@ -1057,7 +1059,7 @@ public class SyntheticPasswordManager {
result.syntheticPassword = new String(secret);
}
if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
- Log.i(TAG, "Upgrade v1 SP blob for user " + userId + ", type = " + type);
+ Slog.i(TAG, "Upgrade v1 SP blob for user " + userId + ", type = " + type);
createSyntheticPasswordBlob(handle, type, result, applicationId, sid, userId);
}
return result;
@@ -1084,7 +1086,7 @@ public class SyntheticPasswordManager {
response = gatekeeper.verifyChallenge(userId, challenge,
spHandle, auth.deriveGkPassword());
} catch (RemoteException e) {
- Log.e(TAG, "Fail to verify with gatekeeper " + userId, e);
+ Slog.e(TAG, "Fail to verify with gatekeeper " + userId, e);
return VerifyCredentialResponse.ERROR;
}
int responseCode = response.getResponseCode();
@@ -1095,7 +1097,7 @@ public class SyntheticPasswordManager {
response = gatekeeper.enroll(userId, spHandle, spHandle,
auth.deriveGkPassword());
} catch (RemoteException e) {
- Log.e(TAG, "Failed to invoke gatekeeper.enroll", e);
+ Slog.e(TAG, "Failed to invoke gatekeeper.enroll", e);
response = GateKeeperResponse.ERROR;
}
if (response.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
@@ -1105,7 +1107,7 @@ public class SyntheticPasswordManager {
return verifyChallenge(gatekeeper, auth, challenge, userId);
} else {
// Fall through, return result from the previous verification attempt.
- Log.w(TAG, "Fail to re-enroll SP handle for user " + userId);
+ Slog.w(TAG, "Fail to re-enroll SP handle for user " + userId);
}
}
return result;
@@ -1225,7 +1227,8 @@ public class SyntheticPasswordManager {
return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, handle);
}
- private byte[] computePasswordToken(byte[] password, PasswordData data) {
+ private byte[] computePasswordToken(LockscreenCredential credential, PasswordData data) {
+ final byte[] password = credential.isNone() ? DEFAULT_PASSWORD : credential.getCredential();
return scrypt(password, data.salt, 1 << data.scryptN, 1 << data.scryptR, 1 << data.scryptP,
PASSWORD_TOKEN_LENGTH);
}
diff --git a/services/core/java/com/android/server/locksettings/TEST_MAPPING b/services/core/java/com/android/server/locksettings/TEST_MAPPING
index c1cba5f7f22d..56f5cc034f05 100644
--- a/services/core/java/com/android/server/locksettings/TEST_MAPPING
+++ b/services/core/java/com/android/server/locksettings/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "presubmit": [
+ "presubmit-devicepolicy": [
{
"name": "CtsDevicePolicyManagerTestCases",
"options": [
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index 5676da213dd2..29338ba06dc2 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -18,7 +18,6 @@ package com.android.server.locksettings.recoverablekeystore;
import static android.security.keystore.recovery.KeyChainProtectionParams.TYPE_LOCKSCREEN;
-import android.annotation.Nullable;
import android.content.Context;
import android.os.RemoteException;
import android.security.Scrypt;
@@ -193,6 +192,7 @@ public class KeySyncTask implements Runnable {
private boolean isCustomLockScreen() {
return mCredentialType != LockPatternUtils.CREDENTIAL_TYPE_NONE
&& mCredentialType != LockPatternUtils.CREDENTIAL_TYPE_PATTERN
+ && mCredentialType != LockPatternUtils.CREDENTIAL_TYPE_PIN
&& mCredentialType != LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
}
@@ -345,7 +345,7 @@ public class KeySyncTask implements Runnable {
}
KeyChainProtectionParams keyChainProtectionParams = new KeyChainProtectionParams.Builder()
.setUserSecretType(TYPE_LOCKSCREEN)
- .setLockScreenUiFormat(getUiFormat(mCredentialType, mCredential))
+ .setLockScreenUiFormat(getUiFormat(mCredentialType))
.setKeyDerivationParams(keyDerivationParams)
.setSecret(new byte[0])
.build();
@@ -449,11 +449,10 @@ public class KeySyncTask implements Runnable {
* @return The format - either pattern, pin, or password.
*/
@VisibleForTesting
- @KeyChainProtectionParams.LockScreenUiFormat static int getUiFormat(
- int credentialType, byte[] credential) {
+ @KeyChainProtectionParams.LockScreenUiFormat static int getUiFormat(int credentialType) {
if (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PATTERN) {
return KeyChainProtectionParams.UI_FORMAT_PATTERN;
- } else if (isPin(credential)) {
+ } else if (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PIN) {
return KeyChainProtectionParams.UI_FORMAT_PIN;
} else {
return KeyChainProtectionParams.UI_FORMAT_PASSWORD;
@@ -472,23 +471,6 @@ public class KeySyncTask implements Runnable {
}
/**
- * Returns {@code true} if {@code credential} looks like a pin.
- */
- @VisibleForTesting
- static boolean isPin(@Nullable byte[] credential) {
- if (credential == null) {
- return false;
- }
- int length = credential.length;
- for (int i = 0; i < length; i++) {
- if (!Character.isDigit((char) credential[i])) {
- return false;
- }
- }
- return true;
- }
-
- /**
* Hashes {@code credentials} with the given {@code salt}.
*
* @return The SHA-256 hash.
@@ -541,6 +523,7 @@ public class KeySyncTask implements Runnable {
}
private boolean shouldUseScryptToHashCredential() {
- return mCredentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+ return mCredentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD
+ || mCredentialType == LockPatternUtils.CREDENTIAL_TYPE_PIN;
}
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java
index 90a36723de4d..c963f799245f 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java
@@ -97,7 +97,8 @@ public class TestOnlyInsecureCertificateHelper {
if (credential == null) {
return false;
}
- if (credentialType != LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) {
+ if (credentialType != LockPatternUtils.CREDENTIAL_TYPE_PASSWORD
+ && credentialType != LockPatternUtils.CREDENTIAL_TYPE_PIN) {
return false;
}
byte[] insecurePasswordPrefixBytes =
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index e753a7be99f6..4816ceb5d76c 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -43,7 +43,7 @@ import java.util.Objects;
* Maintains a connection to a particular media route provider service.
*/
final class MediaRoute2ProviderProxy implements ServiceConnection {
- private static final String TAG = "MediaRoute2Provider";
+ private static final String TAG = "MR2ProviderProxy";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final Context mContext;
@@ -93,7 +93,7 @@ final class MediaRoute2ProviderProxy implements ServiceConnection {
public void unselectRoute(String packageName, String routeId) {
if (mConnectionReady) {
- mActiveConnection.unselectRotue(packageName, routeId);
+ mActiveConnection.unselectRoute(packageName, routeId);
updateBinding();
}
}
@@ -105,6 +105,20 @@ final class MediaRoute2ProviderProxy implements ServiceConnection {
}
}
+ public void requestSetVolume(MediaRoute2Info route, int volume) {
+ if (mConnectionReady) {
+ mActiveConnection.requestSetVolume(route.getId(), volume);
+ updateBinding();
+ }
+ }
+
+ public void requestUpdateVolume(MediaRoute2Info route, int delta) {
+ if (mConnectionReady) {
+ mActiveConnection.requestUpdateVolume(route.getId(), delta);
+ updateBinding();
+ }
+ }
+
@Nullable
public MediaRoute2ProviderInfo getProviderInfo() {
return mProviderInfo;
@@ -260,7 +274,9 @@ final class MediaRoute2ProviderProxy implements ServiceConnection {
.setUniqueId(mUniqueId)
.build();
}
- mHandler.post(mStateChanged);
+ if (mCallback != null) {
+ mCallback.onProviderStateChanged(MediaRoute2ProviderProxy.this);
+ }
}
private void disconnect() {
@@ -277,15 +293,6 @@ final class MediaRoute2ProviderProxy implements ServiceConnection {
return "Service connection " + mComponentName.flattenToShortString();
}
- private final Runnable mStateChanged = new Runnable() {
- @Override
- public void run() {
- if (mCallback != null) {
- mCallback.onProviderStateChanged(MediaRoute2ProviderProxy.this);
- }
- }
- };
-
public interface Callback {
void onProviderStateChanged(MediaRoute2ProviderProxy provider);
}
@@ -324,7 +331,7 @@ final class MediaRoute2ProviderProxy implements ServiceConnection {
}
}
- public void unselectRotue(String packageName, String routeId) {
+ public void unselectRoute(String packageName, String routeId) {
try {
mProvider.unselectRoute(packageName, routeId);
} catch (RemoteException ex) {
@@ -340,6 +347,22 @@ final class MediaRoute2ProviderProxy implements ServiceConnection {
}
}
+ public void requestSetVolume(String routeId, int volume) {
+ try {
+ mProvider.requestSetVolume(routeId, volume);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Failed to deliver request to request set volume.", ex);
+ }
+ }
+
+ public void requestUpdateVolume(String routeId, int delta) {
+ try {
+ mProvider.requestUpdateVolume(routeId, delta);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Failed to deliver request to request update volume.", ex);
+ }
+ }
+
@Override
public void binderDied() {
mHandler.post(() -> onConnectionDied(Connection.this));
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
index 194015d306de..c95119da6d25 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
@@ -35,9 +35,10 @@ import java.util.ArrayList;
import java.util.Collections;
/**
+ * Watches changes of packages, or scan them for finding media route providers.
*/
final class MediaRoute2ProviderWatcher {
- private static final String TAG = "MediaRouteProvider"; // max. 23 chars
+ private static final String TAG = "MR2ProviderWatcher";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final Context mContext;
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 043c834230d1..361dc3673665 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -26,6 +26,7 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.media.IMediaRouter2Client;
import android.media.IMediaRouter2Manager;
+import android.media.IMediaRouterClient;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
import android.os.Binder;
@@ -54,7 +55,7 @@ import java.util.Objects;
* TODO: Merge this to MediaRouterService once it's finished.
*/
class MediaRouter2ServiceImpl {
- private static final String TAG = "MediaRouter2ServiceImpl";
+ private static final String TAG = "MR2ServiceImpl";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final Context mContext;
@@ -86,7 +87,7 @@ class MediaRouter2ServiceImpl {
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- registerClientLocked(client, uid, pid, packageName, userId, trusted);
+ registerClient2Locked(client, uid, pid, packageName, userId, trusted);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -99,7 +100,7 @@ class MediaRouter2ServiceImpl {
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- unregisterClientLocked(client, false);
+ unregisterClient2Locked(client, false);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -155,14 +156,30 @@ class MediaRouter2ServiceImpl {
}
}
- public void setControlCategories(@NonNull IMediaRouter2Client client,
+ //TODO: What would happen if a media app used MediaRouter and MediaRouter2 simultaneously?
+ public void setControlCategories(@NonNull IMediaRouterClient client,
+ @Nullable List<String> categories) {
+ Objects.requireNonNull(client, "client must not be null");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ ClientRecord clientRecord = mAllClientRecords.get(client.asBinder());
+ setControlCategoriesLocked(clientRecord, categories);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ public void setControlCategories2(@NonNull IMediaRouter2Client client,
@Nullable List<String> categories) {
Objects.requireNonNull(client, "client must not be null");
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- setControlCategoriesLocked(client, categories);
+ ClientRecord clientRecord = mAllClientRecords.get(client.asBinder());
+ setControlCategoriesLocked(clientRecord, categories);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -174,7 +191,35 @@ class MediaRouter2ServiceImpl {
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- selectRoute2Locked(client, route);
+ selectRoute2Locked(mAllClientRecords.get(client.asBinder()), route);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ public void requestSetVolume2(IMediaRouter2Client client, MediaRoute2Info route, int volume) {
+ Objects.requireNonNull(client, "client must not be null");
+ Objects.requireNonNull(route, "route must not be null");
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ requestSetVolumeLocked(client, route, volume);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ public void requestUpdateVolume2(IMediaRouter2Client client, MediaRoute2Info route, int delta) {
+ Objects.requireNonNull(client, "client must not be null");
+ Objects.requireNonNull(route, "route must not be null");
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ requestUpdateVolumeLocked(client, route, delta);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -193,6 +238,67 @@ class MediaRouter2ServiceImpl {
}
}
+ public void requestSetVolume2Manager(IMediaRouter2Manager manager,
+ MediaRoute2Info route, int volume) {
+ Objects.requireNonNull(manager, "manager must not be null");
+ Objects.requireNonNull(route, "route must not be null");
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ requestSetVolumeLocked(manager, route, volume);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ public void requestUpdateVolume2Manager(IMediaRouter2Manager manager,
+ MediaRoute2Info route, int delta) {
+ Objects.requireNonNull(manager, "manager must not be null");
+ Objects.requireNonNull(route, "route must not be null");
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ requestUpdateVolumeLocked(manager, route, delta);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+
+ public void registerClient(@NonNull IMediaRouterClient client, @NonNull String packageName) {
+ Objects.requireNonNull(client, "client must not be null");
+
+ final int uid = Binder.getCallingUid();
+ final int pid = Binder.getCallingPid();
+ final int userId = UserHandle.getUserId(uid);
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ registerClient1Locked(client, packageName, userId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ public void unregisterClient(@NonNull IMediaRouterClient client) {
+ Objects.requireNonNull(client, "client must not be null");
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ unregisterClient1Locked(client);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
//TODO: Review this is handling multi-user properly.
void switchUser() {
synchronized (mLock) {
@@ -217,9 +323,9 @@ class MediaRouter2ServiceImpl {
}
}
- void clientDied(ClientRecord clientRecord) {
+ void clientDied(Client2Record clientRecord) {
synchronized (mLock) {
- unregisterClientLocked(clientRecord.mClient, true);
+ unregisterClient2Locked(clientRecord.mClient, true);
}
}
@@ -229,29 +335,24 @@ class MediaRouter2ServiceImpl {
}
}
- private void registerClientLocked(IMediaRouter2Client client,
+ private void registerClient2Locked(IMediaRouter2Client client,
int uid, int pid, String packageName, int userId, boolean trusted) {
final IBinder binder = client.asBinder();
- ClientRecord clientRecord = mAllClientRecords.get(binder);
- if (clientRecord == null) {
- boolean newUser = false;
+ if (mAllClientRecords.get(binder) == null) {
UserRecord userRecord = mUserRecords.get(userId);
if (userRecord == null) {
userRecord = new UserRecord(userId);
- newUser = true;
+ mUserRecords.put(userId, userRecord);
+ initializeUserLocked(userRecord);
}
- clientRecord = new ClientRecord(userRecord, client, uid, pid, packageName, trusted);
+ Client2Record clientRecord = new Client2Record(userRecord, client, uid, pid,
+ packageName, trusted);
try {
binder.linkToDeath(clientRecord, 0);
} catch (RemoteException ex) {
throw new RuntimeException("Media router client died prematurely.", ex);
}
- if (newUser) {
- mUserRecords.put(userId, userRecord);
- initializeUserLocked(userRecord);
- }
-
userRecord.mClientRecords.add(clientRecord);
mAllClientRecords.put(binder, clientRecord);
@@ -261,8 +362,8 @@ class MediaRouter2ServiceImpl {
}
}
- private void unregisterClientLocked(IMediaRouter2Client client, boolean died) {
- ClientRecord clientRecord = mAllClientRecords.remove(client.asBinder());
+ private void unregisterClient2Locked(IMediaRouter2Client client, boolean died) {
+ Client2Record clientRecord = (Client2Record) mAllClientRecords.remove(client.asBinder());
if (clientRecord != null) {
UserRecord userRecord = clientRecord.mUserRecord;
userRecord.mClientRecords.remove(clientRecord);
@@ -272,8 +373,7 @@ class MediaRouter2ServiceImpl {
}
}
- private void selectRoute2Locked(IMediaRouter2Client client, MediaRoute2Info route) {
- ClientRecord clientRecord = mAllClientRecords.get(client.asBinder());
+ private void selectRoute2Locked(ClientRecord clientRecord, MediaRoute2Info route) {
if (clientRecord != null) {
MediaRoute2Info oldRoute = clientRecord.mSelectedRoute;
clientRecord.mSelectedRoute = route;
@@ -294,10 +394,7 @@ class MediaRouter2ServiceImpl {
}
}
- private void setControlCategoriesLocked(IMediaRouter2Client client, List<String> categories) {
- final IBinder binder = client.asBinder();
- ClientRecord clientRecord = mAllClientRecords.get(binder);
-
+ private void setControlCategoriesLocked(ClientRecord clientRecord, List<String> categories) {
if (clientRecord != null) {
clientRecord.mControlCategories = categories;
@@ -319,6 +416,30 @@ class MediaRouter2ServiceImpl {
}
}
+ private void requestSetVolumeLocked(IMediaRouter2Client client, MediaRoute2Info route,
+ int volume) {
+ final IBinder binder = client.asBinder();
+ ClientRecord clientRecord = mAllClientRecords.get(binder);
+
+ if (clientRecord != null) {
+ clientRecord.mUserRecord.mHandler.sendMessage(
+ obtainMessage(UserHandler::requestSetVolume,
+ clientRecord.mUserRecord.mHandler, route, volume));
+ }
+ }
+
+ private void requestUpdateVolumeLocked(IMediaRouter2Client client, MediaRoute2Info route,
+ int delta) {
+ final IBinder binder = client.asBinder();
+ ClientRecord clientRecord = mAllClientRecords.get(binder);
+
+ if (clientRecord != null) {
+ clientRecord.mUserRecord.mHandler.sendMessage(
+ obtainMessage(UserHandler::requestUpdateVolume,
+ clientRecord.mUserRecord.mHandler, route, delta));
+ }
+ }
+
private void registerManagerLocked(IMediaRouter2Manager manager,
int uid, int pid, String packageName, int userId, boolean trusted) {
final IBinder binder = manager.asBinder();
@@ -349,9 +470,7 @@ class MediaRouter2ServiceImpl {
obtainMessage(UserHandler::notifyProviderInfosUpdatedToManager,
userRecord.mHandler, manager));
- final int count = userRecord.mClientRecords.size();
- for (int i = 0; i < count; i++) {
- ClientRecord clientRecord = userRecord.mClientRecords.get(i);
+ for (ClientRecord clientRecord : userRecord.mClientRecords) {
clientRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::updateClientUsage,
clientRecord.mUserRecord.mHandler, clientRecord));
@@ -378,11 +497,36 @@ class MediaRouter2ServiceImpl {
Slog.w(TAG, "Ignoring route selection for unknown client.");
}
if (clientRecord != null && managerRecord.mTrusted) {
- selectRoute2Locked(clientRecord.mClient, route);
+ selectRoute2Locked(clientRecord, route);
}
}
}
+ private void requestSetVolumeLocked(IMediaRouter2Manager manager, MediaRoute2Info route,
+ int volume) {
+ final IBinder binder = manager.asBinder();
+ ManagerRecord managerRecord = mAllManagerRecords.get(binder);
+
+ if (managerRecord != null) {
+ managerRecord.mUserRecord.mHandler.sendMessage(
+ obtainMessage(UserHandler::requestSetVolume,
+ managerRecord.mUserRecord.mHandler, route, volume));
+ }
+ }
+
+ private void requestUpdateVolumeLocked(IMediaRouter2Manager manager, MediaRoute2Info route,
+ int delta) {
+ final IBinder binder = manager.asBinder();
+ ManagerRecord managerRecord = mAllManagerRecords.get(binder);
+
+ if (managerRecord != null) {
+ managerRecord.mUserRecord.mHandler.sendMessage(
+ obtainMessage(UserHandler::requestUpdateVolume,
+ managerRecord.mUserRecord.mHandler, route, delta));
+ }
+ }
+
+
private void initializeUserLocked(UserRecord userRecord) {
if (DEBUG) {
Slog.d(TAG, userRecord + ": Initialized");
@@ -409,6 +553,37 @@ class MediaRouter2ServiceImpl {
}
}
+ private void registerClient1Locked(IMediaRouterClient client, String packageName,
+ int userId) {
+ final IBinder binder = client.asBinder();
+ if (mAllClientRecords.get(binder) == null) {
+ boolean newUser = false;
+ UserRecord userRecord = mUserRecords.get(userId);
+ if (userRecord == null) {
+ userRecord = new UserRecord(userId);
+ newUser = true;
+ }
+ ClientRecord clientRecord = new Client1Record(userRecord, client, packageName);
+
+ if (newUser) {
+ mUserRecords.put(userId, userRecord);
+ initializeUserLocked(userRecord);
+ }
+
+ userRecord.mClientRecords.add(clientRecord);
+ mAllClientRecords.put(binder, clientRecord);
+ }
+ }
+
+ private void unregisterClient1Locked(IMediaRouterClient client) {
+ ClientRecord clientRecord = mAllClientRecords.remove(client.asBinder());
+ if (clientRecord != null) {
+ UserRecord userRecord = clientRecord.mUserRecord;
+ userRecord.mClientRecords.remove(clientRecord);
+ disposeUserIfNeededLocked(userRecord);
+ }
+ }
+
final class UserRecord {
public final int mUserId;
final ArrayList<ClientRecord> mClientRecords = new ArrayList<>();
@@ -430,25 +605,43 @@ class MediaRouter2ServiceImpl {
}
}
- final class ClientRecord implements IBinder.DeathRecipient {
+ class ClientRecord {
public final UserRecord mUserRecord;
+ public final String mPackageName;
+ public List<String> mControlCategories;
+ public MediaRoute2Info mSelectedRoute;
+
+ ClientRecord(UserRecord userRecord, String packageName) {
+ mUserRecord = userRecord;
+ mPackageName = packageName;
+ mControlCategories = Collections.emptyList();
+ }
+ }
+
+ final class Client1Record extends ClientRecord {
+ public final IMediaRouterClient mClient;
+
+ Client1Record(UserRecord userRecord, IMediaRouterClient client,
+ String packageName) {
+ super(userRecord, packageName);
+ mClient = client;
+ }
+ }
+
+ final class Client2Record extends ClientRecord
+ implements IBinder.DeathRecipient {
public final IMediaRouter2Client mClient;
public final int mUid;
public final int mPid;
- public final String mPackageName;
public final boolean mTrusted;
- public List<String> mControlCategories;
- public MediaRoute2Info mSelectedRoute;
- ClientRecord(UserRecord userRecord, IMediaRouter2Client client,
+ Client2Record(UserRecord userRecord, IMediaRouter2Client client,
int uid, int pid, String packageName, boolean trusted) {
- mUserRecord = userRecord;
+ super(userRecord, packageName);
mClient = client;
mUid = uid;
mPid = pid;
- mPackageName = packageName;
mTrusted = trusted;
- mControlCategories = Collections.emptyList();
}
public void dispose() {
@@ -589,6 +782,20 @@ class MediaRouter2ServiceImpl {
}
}
+ private void requestSetVolume(MediaRoute2Info route, int volume) {
+ final MediaRoute2ProviderProxy provider = findProvider(route.getProviderId());
+ if (provider != null) {
+ provider.requestSetVolume(route, volume);
+ }
+ }
+
+ private void requestUpdateVolume(MediaRoute2Info route, int delta) {
+ final MediaRoute2ProviderProxy provider = findProvider(route.getProviderId());
+ if (provider != null) {
+ provider.requestUpdateVolume(route, delta);
+ }
+ }
+
private void scheduleUpdateProviderInfos() {
if (!mProviderInfosUpdateScheduled) {
mProviderInfosUpdateScheduled = true;
@@ -603,8 +810,6 @@ class MediaRouter2ServiceImpl {
if (service == null) {
return;
}
- final List<IMediaRouter2Manager> managers = new ArrayList<>();
- final List<IMediaRouter2Client> clients = new ArrayList<>();
final List<MediaRoute2ProviderInfo> providers = new ArrayList<>();
for (MediaRoute2ProviderProxy mediaProvider : mMediaProviders) {
final MediaRoute2ProviderInfo providerInfo =
@@ -617,12 +822,16 @@ class MediaRouter2ServiceImpl {
}
mProviderInfos = providers;
+ final List<IMediaRouter2Manager> managers = new ArrayList<>();
+ final List<IMediaRouter2Client> clients = new ArrayList<>();
synchronized (service.mLock) {
for (ManagerRecord managerRecord : mUserRecord.mManagerRecords) {
managers.add(managerRecord.mManager);
}
for (ClientRecord clientRecord : mUserRecord.mClientRecords) {
- clients.add(clientRecord.mClient);
+ if (clientRecord instanceof Client2Record) {
+ clients.add(((Client2Record) clientRecord).mClient);
+ }
}
}
for (IMediaRouter2Manager manager : managers) {
@@ -664,9 +873,8 @@ class MediaRouter2ServiceImpl {
}
List<IMediaRouter2Manager> managers = new ArrayList<>();
synchronized (service.mLock) {
- final int count = mUserRecord.mManagerRecords.size();
- for (int i = 0; i < count; i++) {
- managers.add(mUserRecord.mManagerRecords.get(i).mManager);
+ for (ManagerRecord managerRecord : mUserRecord.mManagerRecords) {
+ managers.add(managerRecord.mManager);
}
}
for (IMediaRouter2Manager manager : managers) {
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 2670cd8247ac..afd92f61b9d3 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -249,6 +249,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub
synchronized (mLock) {
registerClientLocked(client, uid, pid, packageName, resolvedUserId, trusted);
}
+ mService2.registerClient(client, packageName);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -289,6 +290,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub
synchronized (mLock) {
unregisterClientLocked(client, false);
}
+ mService2.unregisterClient(client);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -395,6 +397,12 @@ public final class MediaRouterService extends IMediaRouterService.Stub
// Binder call
@Override
+ public void setControlCategories(IMediaRouterClient client, List<String> controlCategories) {
+ mService2.setControlCategories(client, controlCategories);
+ }
+
+ // Binder call
+ @Override
public void requestUpdateVolume(IMediaRouterClient client, String routeId, int direction) {
if (client == null) {
throw new IllegalArgumentException("client must not be null");
@@ -487,8 +495,34 @@ public final class MediaRouterService extends IMediaRouterService.Stub
// Binder call
@Override
- public void setControlCategories(IMediaRouter2Client client, List<String> categories) {
- mService2.setControlCategories(client, categories);
+ public void setControlCategories2(IMediaRouter2Client client, List<String> categories) {
+ mService2.setControlCategories2(client, categories);
+ }
+
+ // Binder call
+ @Override
+ public void requestSetVolume2(IMediaRouter2Client client, MediaRoute2Info route, int volume) {
+ mService2.requestSetVolume2(client, route, volume);
+ }
+
+ // Binder call
+ @Override
+ public void requestUpdateVolume2(IMediaRouter2Client client, MediaRoute2Info route, int delta) {
+ mService2.requestUpdateVolume2(client, route, delta);
+ }
+
+ // Binder call
+ @Override
+ public void requestSetVolume2Manager(IMediaRouter2Manager manager,
+ MediaRoute2Info route, int volume) {
+ mService2.requestSetVolume2Manager(manager, route, volume);
+ }
+
+ // Binder call
+ @Override
+ public void requestUpdateVolume2Manager(IMediaRouter2Manager manager,
+ MediaRoute2Info route, int delta) {
+ mService2.requestUpdateVolume2Manager(manager, route, delta);
}
void restoreBluetoothA2dp() {
@@ -561,6 +595,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub
synchronized (mLock) {
unregisterClientLocked(clientRecord.mClient, true);
}
+ mService2.unregisterClient(clientRecord.mClient);
}
private void registerClientLocked(IMediaRouterClient client,
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 248351ca3d2f..0aee8507d5af 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -456,6 +456,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
return;
}
mDestroyed = true;
+ mPlaybackState = null;
mHandler.post(MessageHandler.MSG_DESTROYED);
}
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 976a0c663101..09be474a5598 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -217,7 +217,6 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
-import com.android.internal.os.SomeArgs;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ConcurrentUtils;
@@ -385,6 +384,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final int MSG_SUBSCRIPTION_OVERRIDE = 16;
private static final int MSG_METERED_RESTRICTED_PACKAGES_CHANGED = 17;
private static final int MSG_SET_NETWORK_TEMPLATE_ENABLED = 18;
+ private static final int MSG_SUBSCRIPTION_PLANS_CHANGED = 19;
private static final int UID_MSG_STATE_CHANGED = 100;
private static final int UID_MSG_GONE = 101;
@@ -797,6 +797,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
writePolicyAL();
}
+ enableFirewallChainUL(FIREWALL_CHAIN_STANDBY, true);
setRestrictBackgroundUL(mLoadedRestrictBackground);
updateRulesForGlobalChangeAL(false);
updateNotificationsNL();
@@ -1509,6 +1510,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
latch.await(5, TimeUnit.SECONDS);
}
+ @VisibleForTesting
+ Handler getHandlerForTesting() {
+ return mHandler;
+ }
+
/**
* Update mobile policies with data cycle information from {@link CarrierConfigManager}
* if necessary.
@@ -3064,6 +3070,34 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mContext.enforceCallingOrSelfPermission(MANAGE_SUBSCRIPTION_PLANS, TAG);
}
+ private void enforceSubscriptionPlanValidity(SubscriptionPlan[] plans) {
+ // nothing to check if no plans
+ if (plans.length == 0) {
+ return;
+ }
+
+ long applicableNetworkTypes = 0;
+ boolean allNetworks = false;
+ for (SubscriptionPlan plan : plans) {
+ if (plan.getNetworkTypes() == null) {
+ allNetworks = true;
+ } else {
+ if ((applicableNetworkTypes & plan.getNetworkTypesBitMask()) != 0) {
+ throw new IllegalArgumentException(
+ "Multiple subscription plans defined for a single network type.");
+ } else {
+ applicableNetworkTypes |= plan.getNetworkTypesBitMask();
+ }
+ }
+ }
+
+ // ensure at least one plan applies for every network type
+ if (!allNetworks) {
+ throw new IllegalArgumentException(
+ "No generic subscription plan that applies to all network types.");
+ }
+ }
+
@Override
public SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage) {
enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
@@ -3228,6 +3262,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
@Override
public void setSubscriptionPlans(int subId, SubscriptionPlan[] plans, String callingPackage) {
enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
+ enforceSubscriptionPlanValidity(plans);
for (SubscriptionPlan plan : plans) {
Preconditions.checkNotNull(plan);
@@ -3256,6 +3291,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
mContext.sendBroadcast(intent, android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS);
+ mHandler.sendMessage(
+ mHandler.obtainMessage(MSG_SUBSCRIPTION_PLANS_CHANGED, subId, 0, plans));
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -3282,7 +3319,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
@Override
public void setSubscriptionOverride(int subId, int overrideMask, int overrideValue,
- long networkTypeMask, long timeoutMillis, String callingPackage) {
+ long timeoutMillis, String callingPackage) {
enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
// We can only override when carrier told us about plans
@@ -3300,16 +3337,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final boolean overrideEnabled = Settings.Global.getInt(mContext.getContentResolver(),
NETPOLICY_OVERRIDE_ENABLED, 1) != 0;
if (overrideEnabled || overrideValue == 0) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = subId;
- args.arg2 = overrideMask;
- args.arg3 = overrideValue;
- args.arg4 = networkTypeMask;
- mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, args));
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE,
+ overrideMask, overrideValue, subId));
if (timeoutMillis > 0) {
- args.arg3 = 0;
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, args),
- timeoutMillis);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE,
+ overrideMask, 0, subId), timeoutMillis);
}
}
}
@@ -3799,39 +3831,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
/**
- * Toggle the firewall standby chain and inform listeners if the uid rules have effectively
- * changed.
- */
- @GuardedBy("mUidRulesFirstLock")
- void updateRulesForAppIdleParoleUL() {
- boolean paroled = mUsageStats.isAppIdleParoleOn();
- boolean enableChain = !paroled;
- enableFirewallChainUL(FIREWALL_CHAIN_STANDBY, enableChain);
-
- int ruleCount = mUidFirewallStandbyRules.size();
- for (int i = 0; i < ruleCount; i++) {
- int uid = mUidFirewallStandbyRules.keyAt(i);
- int oldRules = mUidRules.get(uid);
- if (enableChain) {
- // Chain wasn't enabled before and the other power-related
- // chains are whitelists, so we can clear the
- // MASK_ALL_NETWORKS part of the rules and re-inform listeners if
- // the effective rules result in blocking network access.
- oldRules &= MASK_METERED_NETWORKS;
- } else {
- // Skip if it had no restrictions to begin with
- if ((oldRules & MASK_ALL_NETWORKS) == 0) continue;
- }
- final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldRules, paroled);
- if (newUidRules == RULE_NONE) {
- mUidRules.delete(uid);
- } else {
- mUidRules.put(uid, newUidRules);
- }
- }
- }
-
- /**
* Update rules that might be changed by {@link #mRestrictBackground},
* {@link #mRestrictPower}, or {@link #mDeviceIdleMode} value.
*/
@@ -4286,7 +4285,7 @@ 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);
if (newUidRules == RULE_NONE) {
mUidRules.delete(uid);
@@ -4300,30 +4299,28 @@ 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.
*
* @return the new computed rules for the uid
*/
- private int updateRulesForPowerRestrictionsUL(int uid, int oldUidRules, boolean paroled) {
+ private int updateRulesForPowerRestrictionsUL(int uid, int oldUidRules) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK,
- "updateRulesForPowerRestrictionsUL: " + uid + "/" + oldUidRules + "/"
- + (paroled ? "P" : "-"));
+ "updateRulesForPowerRestrictionsUL: " + uid + "/" + oldUidRules);
}
try {
- return updateRulesForPowerRestrictionsULInner(uid, oldUidRules, paroled);
+ return updateRulesForPowerRestrictionsULInner(uid, oldUidRules);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
}
- private int updateRulesForPowerRestrictionsULInner(int uid, int oldUidRules, boolean paroled) {
+ private int updateRulesForPowerRestrictionsULInner(int uid, int oldUidRules) {
if (!isUidValidForBlacklistRules(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 isIdle = isUidIdle(uid);
final boolean restrictMode = isIdle || mRestrictPower || mDeviceIdleMode;
final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid);
@@ -4395,14 +4392,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
} catch (NameNotFoundException nnfe) {
}
}
-
- @Override
- public void onParoleStateChanged(boolean isParoleOn) {
- synchronized (mUidRulesFirstLock) {
- mLogger.paroleStateChanged(isParoleOn);
- updateRulesForAppIdleParoleUL();
- }
- }
}
private void dispatchUidRulesChanged(INetworkPolicyListener listener, int uid, int uidRules) {
@@ -4445,11 +4434,20 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
private void dispatchSubscriptionOverride(INetworkPolicyListener listener, int subId,
- int overrideMask, int overrideValue, long networkTypeMask) {
+ int overrideMask, int overrideValue) {
+ if (listener != null) {
+ try {
+ listener.onSubscriptionOverride(subId, overrideMask, overrideValue);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ private void dispatchSubscriptionPlansChanged(INetworkPolicyListener listener, int subId,
+ SubscriptionPlan[] plans) {
if (listener != null) {
try {
- listener.onSubscriptionOverride(subId, overrideMask, overrideValue,
- networkTypeMask);
+ listener.onSubscriptionPlansChanged(subId, plans);
} catch (RemoteException ignored) {
}
}
@@ -4550,16 +4548,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
return true;
}
case MSG_SUBSCRIPTION_OVERRIDE: {
- final SomeArgs args = (SomeArgs) msg.obj;
- final int subId = (int) args.arg1;
- final int overrideMask = (int) args.arg2;
- final int overrideValue = (int) args.arg3;
- final long networkTypeMask = (long) args.arg4;
+ final int overrideMask = msg.arg1;
+ final int overrideValue = msg.arg2;
+ final int subId = (int) msg.obj;
final int length = mListeners.beginBroadcast();
for (int i = 0; i < length; i++) {
final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
- dispatchSubscriptionOverride(listener, subId, overrideMask, overrideValue,
- networkTypeMask);
+ dispatchSubscriptionOverride(listener, subId, overrideMask, overrideValue);
}
mListeners.finishBroadcast();
return true;
@@ -4576,6 +4571,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
setNetworkTemplateEnabledInner(template, enabled);
return true;
}
+ case MSG_SUBSCRIPTION_PLANS_CHANGED: {
+ final SubscriptionPlan[] plans = (SubscriptionPlan[]) msg.obj;
+ final int subId = msg.arg1;
+ final int length = mListeners.beginBroadcast();
+ for (int i = 0; i < length; i++) {
+ final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
+ dispatchSubscriptionPlansChanged(listener, subId, plans);
+ }
+ mListeners.finishBroadcast();
+ return true;
+ }
default: {
return false;
}
@@ -4727,7 +4733,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
/**
- * Calls {@link #setUidFirewallRules(int, SparseIntArray)} and
+ * Calls {@link #setUidFirewallRulesUL(int, SparseIntArray)} and
* {@link #enableFirewallChainUL(int, boolean)} synchronously.
*
* @param chain firewall chain.
diff --git a/services/core/java/com/android/server/net/watchlist/HarmfulCrcs.java b/services/core/java/com/android/server/net/watchlist/HarmfulCrcs.java
new file mode 100644
index 000000000000..65a4b23225c2
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/HarmfulCrcs.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net.watchlist;
+
+import com.android.internal.util.HexDump;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Helper class to store a set of harmful CRC32s in memory.
+ * TODO: Optimize memory usage using int array with binary search.
+ */
+class HarmfulCrcs {
+
+ private final Set<Integer> mCrcSet;
+
+ HarmfulCrcs(List<byte[]> digests) {
+ final HashSet<Integer> crcSet = new HashSet<>();
+ final int size = digests.size();
+ for (int i = 0; i < size; i++) {
+ byte[] bytes = digests.get(i);
+ if (bytes.length <= 4) {
+ int crc = 0;
+ for (byte b : bytes) {
+ // Remember byte is signed
+ crc = (crc << 8) | (b & 0xff);
+ }
+ crcSet.add(crc);
+ }
+ }
+ mCrcSet = Collections.unmodifiableSet(crcSet);
+ }
+
+ public boolean contains(int crc) {
+ return mCrcSet.contains(crc);
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ for (int crc : mCrcSet) {
+ pw.println(HexDump.toHexString(crc));
+ }
+ pw.println("");
+ }
+}
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
index 8352ca60e362..7d06de335e84 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
@@ -18,7 +18,6 @@ package com.android.server.net.watchlist;
import android.annotation.Nullable;
import android.os.FileUtils;
-import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
import android.util.Xml;
@@ -66,11 +65,11 @@ class WatchlistConfig {
}
private static class CrcShaDigests {
- final HarmfulDigests crc32Digests;
- final HarmfulDigests sha256Digests;
+ public final HarmfulCrcs crc32s;
+ public final HarmfulDigests sha256Digests;
- public CrcShaDigests(HarmfulDigests crc32Digests, HarmfulDigests sha256Digests) {
- this.crc32Digests = crc32Digests;
+ CrcShaDigests(HarmfulCrcs crc32s, HarmfulDigests sha256Digests) {
+ this.crc32s = crc32s;
this.sha256Digests = sha256Digests;
}
}
@@ -140,9 +139,9 @@ class WatchlistConfig {
}
}
parser.require(XmlPullParser.END_TAG, null, XmlTags.WATCHLIST_CONFIG);
- mDomainDigests = new CrcShaDigests(new HarmfulDigests(crc32DomainList),
+ mDomainDigests = new CrcShaDigests(new HarmfulCrcs(crc32DomainList),
new HarmfulDigests(sha256DomainList));
- mIpDigests = new CrcShaDigests(new HarmfulDigests(crc32IpList),
+ mIpDigests = new CrcShaDigests(new HarmfulCrcs(crc32IpList),
new HarmfulDigests(sha256IpList));
Log.i(TAG, "Reload watchlist done");
} catch (IllegalStateException | NullPointerException | NumberFormatException |
@@ -171,8 +170,8 @@ class WatchlistConfig {
return false;
}
// First it does a quick CRC32 check.
- final byte[] crc32 = getCrc32(domain);
- if (!domainDigests.crc32Digests.contains(crc32)) {
+ final int crc32 = getCrc32(domain);
+ if (!domainDigests.crc32s.contains(crc32)) {
return false;
}
// Now we do a slow SHA256 check.
@@ -187,8 +186,8 @@ class WatchlistConfig {
return false;
}
// First it does a quick CRC32 check.
- final byte[] crc32 = getCrc32(ip);
- if (!ipDigests.crc32Digests.contains(crc32)) {
+ final int crc32 = getCrc32(ip);
+ if (!ipDigests.crc32s.contains(crc32)) {
return false;
}
// Now we do a slow SHA256 check.
@@ -198,15 +197,11 @@ class WatchlistConfig {
/** Get CRC32 of a string
- *
- * TODO: Review if we should use CRC32 or other algorithms
*/
- private byte[] getCrc32(String str) {
+ private int getCrc32(String str) {
final CRC32 crc = new CRC32();
crc.update(str.getBytes());
- final long tmp = crc.getValue();
- return new byte[]{(byte) (tmp >> 24 & 255), (byte) (tmp >> 16 & 255),
- (byte) (tmp >> 8 & 255), (byte) (tmp & 255)};
+ return (int) crc.getValue();
}
/** Get SHA256 of a string */
@@ -279,7 +274,7 @@ class WatchlistConfig {
pw.println("Domain CRC32 digest list:");
// mDomainDigests won't go from non-null to null so it's safe
if (mDomainDigests != null) {
- mDomainDigests.crc32Digests.dump(fd, pw, args);
+ mDomainDigests.crc32s.dump(fd, pw, args);
}
pw.println("Domain SHA256 digest list:");
if (mDomainDigests != null) {
@@ -288,7 +283,7 @@ class WatchlistConfig {
pw.println("Ip CRC32 digest list:");
// mIpDigests won't go from non-null to null so it's safe
if (mIpDigests != null) {
- mIpDigests.crc32Digests.dump(fd, pw, args);
+ mIpDigests.crc32s.dump(fd, pw, args);
}
pw.println("Ip SHA256 digest list:");
if (mIpDigests != null) {
diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java
index b1cd62788073..9cb8a0105286 100644
--- a/services/core/java/com/android/server/notification/GroupHelper.java
+++ b/services/core/java/com/android/server/notification/GroupHelper.java
@@ -16,9 +16,13 @@
package com.android.server.notification;
import android.service.notification.StatusBarNotification;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
@@ -37,6 +41,11 @@ public class GroupHelper {
private final Callback mCallback;
private final int mAutoGroupAtCount;
+ // count the number of ongoing notifications per group
+ // userId -> (package name -> (group Id -> (set of notification keys)))
+ final ArrayMap<String, ArraySet<String>>
+ mOngoingGroupCount = new ArrayMap<>();
+
// Map of user : <Map of package : notification keys>. Only contains notifications that are not
// grouped by the app (aka no group or sort key).
Map<Integer, Map<String, LinkedHashSet<String>>> mUngroupedNotifications = new HashMap<>();
@@ -46,10 +55,52 @@ public class GroupHelper {
mCallback = callback;
}
+ private String generatePackageGroupKey(int userId, String pkg, String group) {
+ return userId + "|" + pkg + "|" + group;
+ }
+
+ @VisibleForTesting
+ protected int getOngoingGroupCount(int userId, String pkg, String group) {
+ String key = generatePackageGroupKey(userId, pkg, group);
+ return mOngoingGroupCount.getOrDefault(key, new ArraySet<>(0)).size();
+ }
+
+ private void addToOngoingGroupCount(StatusBarNotification sbn, boolean add) {
+ if (sbn.getNotification().isGroupSummary()) return;
+ if (!sbn.isOngoing() && add) return;
+ String group = sbn.getGroup();
+ if (group == null) return;
+ int userId = sbn.getUser().getIdentifier();
+ String key = generatePackageGroupKey(userId, sbn.getPackageName(), group);
+ ArraySet<String> notifications = mOngoingGroupCount.getOrDefault(key, new ArraySet<>(0));
+ if (add) {
+ notifications.add(sbn.getKey());
+ mOngoingGroupCount.put(key, notifications);
+ } else {
+ notifications.remove(sbn.getKey());
+ // we dont need to put it back if it is default
+ }
+ String combinedKey = generatePackageGroupKey(userId, sbn.getPackageName(), group);
+ boolean needsOngoingFlag = notifications.size() > 0;
+ mCallback.updateAutogroupSummary(sbn.getKey(), needsOngoingFlag);
+ }
+
+ public void onNotificationUpdated(StatusBarNotification childSbn,
+ boolean autogroupSummaryExists) {
+ if (childSbn.getGroup() != AUTOGROUP_KEY
+ || childSbn.getNotification().isGroupSummary()) return;
+ if (childSbn.isOngoing()) {
+ addToOngoingGroupCount(childSbn, true);
+ } else {
+ addToOngoingGroupCount(childSbn, false);
+ }
+ }
+
public void onNotificationPosted(StatusBarNotification sbn, boolean autogroupSummaryExists) {
if (DEBUG) Log.i(TAG, "POSTED " + sbn.getKey());
try {
List<String> notificationsToGroup = new ArrayList<>();
+ if (autogroupSummaryExists) addToOngoingGroupCount(sbn, true);
if (!sbn.isAppGroup()) {
// Not grouped by the app, add to the list of notifications for the app;
// send grouping update if app exceeds the autogrouping limit.
@@ -90,6 +141,7 @@ public class GroupHelper {
public void onNotificationRemoved(StatusBarNotification sbn) {
try {
+ addToOngoingGroupCount(sbn, false);
maybeUngroup(sbn, true, sbn.getUserId());
} catch (Exception e) {
Slog.e(TAG, "Error processing canceled notification", e);
@@ -159,5 +211,6 @@ public class GroupHelper {
void removeAutoGroup(String key);
void addAutoGroupSummary(int userId, String pkg, String triggeringKey);
void removeAutoGroupSummary(int user, String pkg);
+ void updateAutogroupSummary(String key, boolean needsOngoingFlag);
}
}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 48b0fd6e86d1..8560ae6d2e3a 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -185,7 +185,7 @@ abstract public class ManagedServices {
}
protected void addDefaultComponentOrPackage(String packageOrComponent) {
- if (packageOrComponent != null) {
+ if (!TextUtils.isEmpty(packageOrComponent)) {
synchronized (mDefaultsLock) {
ComponentName cn = ComponentName.unflattenFromString(packageOrComponent);
if (cn == null) {
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 61be1f5e559b..6f0ad33244e4 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -17,6 +17,7 @@
package com.android.server.notification;
import android.app.Notification;
+import android.net.Uri;
import android.service.notification.NotificationStats;
import com.android.internal.statusbar.NotificationVisibility;
@@ -49,6 +50,12 @@ public interface NotificationDelegate {
void onNotificationBubbleChanged(String key, boolean isBubble);
/**
+ * Grant permission to read the specified URI to the package associated with the
+ * NotificationRecord associated with the given key.
+ */
+ void grantInlineReplyUriPermission(String key, Uri uri, int callingUid);
+
+ /**
* Notifies that smart replies and actions have been added to the UI.
*/
void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount, int smartActionCount,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index d480cb6e9800..cd3343bbec7b 100644..100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -21,6 +21,7 @@ import static android.app.Notification.CATEGORY_CALL;
import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY;
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
+import static android.app.Notification.FLAG_INSISTENT;
import static android.app.Notification.FLAG_NO_CLEAR;
import static android.app.Notification.FLAG_ONGOING_EVENT;
import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
@@ -49,13 +50,13 @@ import static android.content.Context.BIND_ALLOW_WHITELIST_MANAGEMENT;
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
import static android.content.Context.BIND_NOT_PERCEPTIBLE;
-import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_TELEVISION;
import static android.content.pm.PackageManager.MATCH_ALL;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
import static android.os.UserHandle.USER_NULL;
@@ -88,13 +89,11 @@ import static android.service.notification.NotificationListenerService.TRIM_FULL
import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
-import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER;
-import static com.android.server.notification.PreferencesHelper.DEFAULT_ALLOW_BUBBLE;
import static com.android.server.utils.PriorityDump.PRIORITY_ARG;
import static com.android.server.utils.PriorityDump.PRIORITY_ARG_CRITICAL;
import static com.android.server.utils.PriorityDump.PRIORITY_ARG_NORMAL;
@@ -523,7 +522,6 @@ public class NotificationManagerService extends SystemService {
}
-
void loadDefaultApprovedServices(int userId) {
String defaultListenerAccess = getContext().getResources().getString(
com.android.internal.R.string.config_defaultListenerAccessPackages);
@@ -531,6 +529,9 @@ public class NotificationManagerService extends SystemService {
String[] listeners =
defaultListenerAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR);
for (int i = 0; i < listeners.length; i++) {
+ if (TextUtils.isEmpty(listeners[i])) {
+ continue;
+ }
ArraySet<ComponentName> approvedListeners =
mListeners.queryPackageForServices(listeners[i],
MATCH_DIRECT_BOOT_AWARE
@@ -547,6 +548,9 @@ public class NotificationManagerService extends SystemService {
if (defaultDndAccess != null) {
String[] dnds = defaultDndAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR);
for (int i = 0; i < dnds.length; i++) {
+ if (TextUtils.isEmpty(dnds[i])) {
+ continue;
+ }
mConditionProviders.addDefaultComponentOrPackage(dnds[i]);
}
}
@@ -565,12 +569,14 @@ public class NotificationManagerService extends SystemService {
.split(ManagedServices.ENABLED_SERVICES_SEPARATOR)));
for (int i = 0; i < assistants.size(); i++) {
String cnString = assistants.valueAt(i);
+ if (TextUtils.isEmpty(cnString)) {
+ continue;
+ }
mAssistants.addDefaultComponentOrPackage(cnString);
}
}
protected void allowDefaultApprovedServices(int userId) {
-
ArraySet<ComponentName> defaultListeners = mListeners.getDefaultComponents();
for (int i = 0; i < defaultListeners.size(); i++) {
ComponentName cn = defaultListeners.valueAt(i);
@@ -595,6 +601,40 @@ public class NotificationManagerService extends SystemService {
}
}
+ /**
+ * This method will update the flags of the summary.
+ * It will set it to FLAG_ONGOING_EVENT if any of its group members
+ * has the same flag. It will delete the flag otherwise
+ * @param userId user id of the autogroup summary
+ * @param pkg package of the autogroup summary
+ * @param needsOngoingFlag true if the group has at least one ongoing notification
+ */
+ @GuardedBy("mNotificationLock")
+ protected void updateAutobundledSummaryFlags(int userId, String pkg, boolean needsOngoingFlag) {
+ ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
+ if (summaries == null) {
+ return;
+ }
+ String summaryKey = summaries.get(pkg);
+ if (summaryKey == null) {
+ return;
+ }
+ NotificationRecord summary = mNotificationsByKey.get(summaryKey);
+ if (summary == null) {
+ return;
+ }
+ int oldFlags = summary.sbn.getNotification().flags;
+ if (needsOngoingFlag) {
+ summary.sbn.getNotification().flags |= FLAG_ONGOING_EVENT;
+ } else {
+ summary.sbn.getNotification().flags &= ~FLAG_ONGOING_EVENT;
+ }
+
+ if (summary.sbn.getNotification().flags != oldFlags) {
+ mHandler.post(new EnqueueNotificationRunnable(userId, summary));
+ }
+ }
+
private void allowDndPackage(String packageName) {
try {
getBinderService().setNotificationPolicyAccessGranted(packageName, true);
@@ -1116,6 +1156,56 @@ public class NotificationManagerService extends SystemService {
}
}
}
+
+ @Override
+ /**
+ * Grant permission to read the specified URI to the package specified in the
+ * NotificationRecord associated with the given key. The callingUid represents the UID of
+ * SystemUI from which this method is being called.
+ *
+ * For this to work, SystemUI must have permission to read the URI when running under the
+ * user associated with the NotificationRecord, and this grant will fail when trying
+ * to grant URI permissions across users.
+ */
+ public void grantInlineReplyUriPermission(String key, Uri uri, int callingUid) {
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r != null) {
+ IBinder owner = r.permissionOwner;
+ if (owner == null) {
+ r.permissionOwner = mUgmInternal.newUriPermissionOwner("NOTIF:" + key);
+ owner = r.permissionOwner;
+ }
+ int uid = callingUid;
+ int userId = r.sbn.getUserId();
+ if (userId == UserHandle.USER_ALL) {
+ userId = USER_SYSTEM;
+ }
+ if (UserHandle.getUserId(uid) != userId) {
+ try {
+ final String[] pkgs = mPackageManager.getPackagesForUid(callingUid);
+ if (pkgs == null) {
+ Log.e(TAG, "Cannot grant uri permission to unknown UID: "
+ + callingUid);
+ }
+ final String pkg = pkgs[0]; // Get the SystemUI package
+ // Find the UID for SystemUI for the correct user
+ uid = mPackageManager.getPackageUid(pkg, 0, userId);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Cannot talk to package manager", re);
+ }
+ }
+ grantUriPermission(owner, uri, uid, r.sbn.getPackageName(), userId);
+ } else {
+ Log.w(TAG, "No record found for notification key:" + key);
+
+ // TODO: figure out cancel story. I think it's: sysui needs to tell us
+ // whenever noitifications held by a lifetimextender go away
+ // IBinder owner = mUgmInternal.newUriPermissionOwner("InlineReply:" + key);
+ // pass in userId and package as well as key (key for logging purposes)
+ }
+ }
+ }
};
@VisibleForTesting
@@ -1145,7 +1235,7 @@ public class NotificationManagerService extends SystemService {
}
@GuardedBy("mNotificationLock")
- private void clearSoundLocked() {
+ void clearSoundLocked() {
mSoundNotificationKey = null;
long identity = Binder.clearCallingIdentity();
try {
@@ -1160,7 +1250,7 @@ public class NotificationManagerService extends SystemService {
}
@GuardedBy("mNotificationLock")
- private void clearVibrateLocked() {
+ void clearVibrateLocked() {
mVibrateNotificationKey = null;
long identity = Binder.clearCallingIdentity();
try {
@@ -1331,16 +1421,15 @@ public class NotificationManagerService extends SystemService {
uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)};
}
if (pkgList != null && (pkgList.length > 0)) {
- for (String pkgName : pkgList) {
- if (cancelNotifications) {
+ if (cancelNotifications) {
+ for (String pkgName : pkgList) {
cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, null, 0, 0,
!queryRestart, changeUserId, reason, null);
- } else if (hideNotifications) {
- hideNotificationsForPackages(pkgList);
- } else if (unhideNotifications) {
- unhideNotificationsForPackages(pkgList);
}
-
+ } else if (hideNotifications) {
+ hideNotificationsForPackages(pkgList);
+ } else if (unhideNotifications) {
+ unhideNotificationsForPackages(pkgList);
}
}
@@ -1429,10 +1518,8 @@ public class NotificationManagerService extends SystemService {
private final class SettingsObserver extends ContentObserver {
private final Uri NOTIFICATION_BADGING_URI
= Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BADGING);
- private final Uri NOTIFICATION_BUBBLES_URI_GLOBAL
+ private final Uri NOTIFICATION_BUBBLES_URI
= Settings.Global.getUriFor(Settings.Global.NOTIFICATION_BUBBLES);
- private final Uri NOTIFICATION_BUBBLES_URI_SECURE
- = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BUBBLES);
private final Uri NOTIFICATION_LIGHT_PULSE_URI
= Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
private final Uri NOTIFICATION_RATE_LIMIT_URI
@@ -1450,9 +1537,7 @@ public class NotificationManagerService extends SystemService {
false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(NOTIFICATION_RATE_LIMIT_URI,
false, this, UserHandle.USER_ALL);
- resolver.registerContentObserver(NOTIFICATION_BUBBLES_URI_GLOBAL,
- false, this, UserHandle.USER_ALL);
- resolver.registerContentObserver(NOTIFICATION_BUBBLES_URI_SECURE,
+ resolver.registerContentObserver(NOTIFICATION_BUBBLES_URI,
false, this, UserHandle.USER_ALL);
update(null);
}
@@ -1479,41 +1564,9 @@ public class NotificationManagerService extends SystemService {
if (uri == null || NOTIFICATION_BADGING_URI.equals(uri)) {
mPreferencesHelper.updateBadgingEnabled();
}
- // In QPR we moved the setting to Global rather than Secure so that the setting
- // applied to work profiles. Unfortunately we need to maintain both to pass CTS without
- // a change to CTS outside of a normal letter release.
- if (uri == null || NOTIFICATION_BUBBLES_URI_GLOBAL.equals(uri)) {
- syncBubbleSettings(resolver, NOTIFICATION_BUBBLES_URI_GLOBAL);
+ if (uri == null || NOTIFICATION_BUBBLES_URI.equals(uri)) {
mPreferencesHelper.updateBubblesEnabled();
}
- if (NOTIFICATION_BUBBLES_URI_SECURE.equals(uri)) {
- syncBubbleSettings(resolver, NOTIFICATION_BUBBLES_URI_SECURE);
- }
- }
-
- private void syncBubbleSettings(ContentResolver resolver, Uri settingToFollow) {
- boolean followSecureSetting = settingToFollow.equals(NOTIFICATION_BUBBLES_URI_SECURE);
-
- int secureSettingValue = Settings.Secure.getInt(resolver,
- Settings.Secure.NOTIFICATION_BUBBLES, DEFAULT_ALLOW_BUBBLE ? 1 : 0);
- int globalSettingValue = Settings.Global.getInt(resolver,
- Settings.Global.NOTIFICATION_BUBBLES, DEFAULT_ALLOW_BUBBLE ? 1 : 0);
-
- if (globalSettingValue == secureSettingValue) {
- return;
- }
-
- if (followSecureSetting) {
- // Global => secure
- Settings.Global.putInt(resolver,
- Settings.Global.NOTIFICATION_BUBBLES,
- secureSettingValue);
- } else {
- // Secure => Global
- Settings.Secure.putInt(resolver,
- Settings.Secure.NOTIFICATION_BADGING,
- globalSettingValue);
- }
}
}
@@ -1948,7 +2001,6 @@ public class NotificationManagerService extends SystemService {
});
}
-
private GroupHelper getGroupHelper() {
mAutoGroupAtCount =
getContext().getResources().getInteger(R.integer.config_autoGroupAtCount);
@@ -1978,6 +2030,16 @@ public class NotificationManagerService extends SystemService {
clearAutogroupSummaryLocked(userId, pkg);
}
}
+
+ @Override
+ public void updateAutogroupSummary(String key, boolean needsOngoingFlag) {
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r == null) return;
+ updateAutobundledSummaryFlags(r.getUser().getIdentifier(),
+ r.sbn.getPackageName(), needsOngoingFlag);
+ }
+ }
});
}
@@ -4485,13 +4547,13 @@ public class NotificationManagerService extends SystemService {
if (record != null && record.getAudioAttributes() != null) {
if ((mListenerHints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
if (record.getAudioAttributes().getUsage()
- != AudioAttributes.USAGE_VOICE_COMMUNICATION) {
+ != AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
return "listenerNoti";
}
}
if ((mListenerHints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
if (record.getAudioAttributes().getUsage()
- == AudioAttributes.USAGE_VOICE_COMMUNICATION) {
+ == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
return "listenerCall";
}
}
@@ -5045,12 +5107,13 @@ public class NotificationManagerService extends SystemService {
final int contentViewSize = contentView.estimateMemoryUsage();
if (contentViewSize > mWarnRemoteViewsSizeBytes
&& contentViewSize < mStripRemoteViewsSizeBytes) {
- Slog.w(TAG, "RemoteViews too large on tag: " + tag + " id: " + id
+ Slog.w(TAG, "RemoteViews too large on pkg: " + pkg + " tag: " + tag + " id: " + id
+ " this might be stripped in a future release");
}
if (contentViewSize >= mStripRemoteViewsSizeBytes) {
mUsageStats.registerImageRemoved(pkg);
- Slog.w(TAG, "Removed too large RemoteViews on tag: " + tag + " id: " + id);
+ Slog.w(TAG, "Removed too large RemoteViews (" + contentViewSize + " bytes) on pkg: "
+ + pkg + " tag: " + tag + " id: " + id);
return true;
}
return false;
@@ -5238,18 +5301,6 @@ public class NotificationManagerService extends SystemService {
+ intent);
return false;
}
- if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) {
- StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
- BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS);
- Log.w(TAG, "Unable to send as bubble -- activity is not documentLaunchMode=always "
- + "for intent: " + intent);
- return false;
- }
- if ((info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) {
- Log.w(TAG, "Unable to send as bubble -- activity is not embeddable for intent: "
- + intent);
- return false;
- }
return true;
}
@@ -5792,6 +5843,10 @@ public class NotificationManagerService extends SystemService {
n, hasAutoGroupSummaryLocked(n));
}
});
+ } else if (oldSbn != null) {
+ final NotificationRecord finalRecord = r;
+ mHandler.post(() -> mGroupHelper.onNotificationUpdated(
+ finalRecord.sbn, hasAutoGroupSummaryLocked(n)));
}
} else {
Slog.e(TAG, "Not posting notification without small icon: " + notification);
@@ -6057,7 +6112,6 @@ public class NotificationManagerService extends SystemService {
mIsAutomotive
? record.getImportance() > NotificationManager.IMPORTANCE_DEFAULT
: record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
-
// Remember if this notification already owns the notification channels.
boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
@@ -6073,7 +6127,6 @@ public class NotificationManagerService extends SystemService {
}
if (aboveThreshold && isNotificationForCurrentUser(record)) {
-
if (mSystemReady && mAudioManager != null) {
Uri soundUri = record.getSound();
hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
@@ -6088,7 +6141,6 @@ public class NotificationManagerService extends SystemService {
vibration = mFallbackVibrationPattern;
}
hasValidVibrate = vibration != null;
-
boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
if (!sentAccessibilityEvent) {
@@ -6245,11 +6297,29 @@ public class NotificationManagerService extends SystemService {
return true;
}
+ // A looping ringtone, such as an incoming call is playing
+ if (isLoopingRingtoneNotification(mNotificationsByKey.get(mSoundNotificationKey))
+ || isLoopingRingtoneNotification(
+ mNotificationsByKey.get(mVibrateNotificationKey))) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @GuardedBy("mNotificationLock")
+ private boolean isLoopingRingtoneNotification(final NotificationRecord playingRecord) {
+ if (playingRecord != null) {
+ if (playingRecord.getAudioAttributes().getUsage() == USAGE_NOTIFICATION_RINGTONE
+ && (playingRecord.getNotification().flags & FLAG_INSISTENT) != 0) {
+ return true;
+ }
+ }
return false;
}
private boolean playSound(final NotificationRecord record, Uri soundUri) {
- boolean looping = (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
+ boolean looping = (record.getNotification().flags & FLAG_INSISTENT) != 0;
// play notifications if there is no user of exclusive audio focus
// and the stream volume is not 0 (non-zero volume implies not silenced by SILENT or
// VIBRATE ringer mode)
@@ -6301,7 +6371,6 @@ public class NotificationManagerService extends SystemService {
try {
Thread.sleep(waitMs);
} catch (InterruptedException e) { }
-
// Notifications might be canceled before it actually vibrates due to waitMs,
// so need to check the notification still valide for vibrate.
synchronized (mNotificationLock) {
@@ -7009,7 +7078,6 @@ public class NotificationManagerService extends SystemService {
private void grantUriPermission(IBinder owner, Uri uri, int sourceUid, String targetPkg,
int targetUserId) {
if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
-
final long ident = Binder.clearCallingIdentity();
try {
mUgm.grantUriPermissionFromOwner(owner, sourceUid, targetPkg,
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index f63aa5256078..65109230bb9a 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -1179,7 +1179,7 @@ public class ZenModeHelper {
if (mZenMode == Global.ZEN_MODE_OFF
|| (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
- && !ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(mConfig))) {
+ && !ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(mConfig))) {
// in priority only with ringer not muted, save ringer mode changes
// in dnd off, save ringer mode changes
setPreviousRingerModeSetting(ringerModeNew);
@@ -1200,7 +1200,7 @@ public class ZenModeHelper {
&& (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS
|| mZenMode == Global.ZEN_MODE_ALARMS
|| (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
- && ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(
+ && ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(
mConfig)))) {
newZen = Global.ZEN_MODE_OFF;
} else if (mZenMode != Global.ZEN_MODE_OFF) {
@@ -1264,29 +1264,21 @@ public class ZenModeHelper {
@Override
public int getRingerModeAffectedStreams(int streams) {
- // ringtone and notification streams are always affected by ringer mode
- // system stream is affected by ringer mode when not in priority-only
+ // ringtone, notification and system streams are always affected by ringer mode
+ // zen muting is handled in AudioService.java's mZenModeAffectedStreams
streams |= (1 << AudioSystem.STREAM_RING) |
(1 << AudioSystem.STREAM_NOTIFICATION) |
(1 << AudioSystem.STREAM_SYSTEM);
if (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) {
- // alarm and music streams affected by ringer mode when in total silence
+ // alarm and music streams affected by ringer mode (cannot be adjusted) when in
+ // total silence
streams |= (1 << AudioSystem.STREAM_ALARM) |
(1 << AudioSystem.STREAM_MUSIC);
} else {
streams &= ~((1 << AudioSystem.STREAM_ALARM) |
(1 << AudioSystem.STREAM_MUSIC));
}
-
- if (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
- && ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(mConfig)) {
- // system stream is not affected by ringer mode in priority only when the ringer
- // is zen muted (all other notification categories are muted)
- streams &= ~(1 << AudioSystem.STREAM_SYSTEM);
- } else {
- streams |= (1 << AudioSystem.STREAM_SYSTEM);
- }
return streams;
}
}
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 288ef0e618b6..f3c912817d05 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -30,20 +30,17 @@ import android.os.UserHandle;
import android.util.Slog;
import com.android.server.om.OverlayManagerServiceImpl.PackageManagerHelper;
-import com.android.server.pm.Installer;
import java.io.File;
/**
* Handle the creation and deletion of idmap files.
*
- * The actual work is performed by the idmap binary, launched through Installer
- * and installd (or idmap2).
+ * The actual work is performed by the idmap binary, launched through idmap2d.
*
* Note: this class is subclassed in the OMS unit tests, and hence not marked as final.
*/
class IdmapManager {
- private static final boolean FEATURE_FLAG_IDMAP2 = true;
private static final boolean VENDOR_IS_Q_OR_LATER;
static {
final String value = SystemProperties.get("ro.vndk.version", "29");
@@ -58,12 +55,10 @@ class IdmapManager {
VENDOR_IS_Q_OR_LATER = isQOrLater;
}
- private final Installer mInstaller;
private final PackageManagerHelper mPackageManager;
private final IdmapDaemon mIdmapDaemon;
- IdmapManager(final Installer installer, final PackageManagerHelper packageManager) {
- mInstaller = installer;
+ IdmapManager(final PackageManagerHelper packageManager) {
mPackageManager = packageManager;
mIdmapDaemon = IdmapDaemon.getInstance();
}
@@ -78,18 +73,13 @@ class IdmapManager {
final String targetPath = targetPackage.applicationInfo.getBaseCodePath();
final String overlayPath = overlayPackage.applicationInfo.getBaseCodePath();
try {
- if (FEATURE_FLAG_IDMAP2) {
- int policies = calculateFulfilledPolicies(targetPackage, overlayPackage, userId);
- boolean enforce = enforceOverlayable(overlayPackage);
- if (mIdmapDaemon.verifyIdmap(overlayPath, policies, enforce, userId)) {
- return true;
- }
- return mIdmapDaemon.createIdmap(targetPath, overlayPath, policies,
- enforce, userId) != null;
- } else {
- mInstaller.idmap(targetPath, overlayPath, sharedGid);
+ int policies = calculateFulfilledPolicies(targetPackage, overlayPackage, userId);
+ boolean enforce = enforceOverlayable(overlayPackage);
+ if (mIdmapDaemon.verifyIdmap(overlayPath, policies, enforce, userId)) {
return true;
}
+ return mIdmapDaemon.createIdmap(targetPath, overlayPath, policies,
+ enforce, userId) != null;
} catch (Exception e) {
Slog.w(TAG, "failed to generate idmap for " + targetPath + " and "
+ overlayPath + ": " + e.getMessage());
@@ -102,12 +92,7 @@ class IdmapManager {
Slog.d(TAG, "remove idmap for " + oi.baseCodePath);
}
try {
- if (FEATURE_FLAG_IDMAP2) {
- return mIdmapDaemon.removeIdmap(oi.baseCodePath, userId);
- } else {
- mInstaller.removeIdmap(oi.baseCodePath);
- return true;
- }
+ return mIdmapDaemon.removeIdmap(oi.baseCodePath, userId);
} catch (Exception e) {
Slog.w(TAG, "failed to remove idmap for " + oi.baseCodePath + ": " + e.getMessage());
return false;
@@ -125,19 +110,12 @@ class IdmapManager {
private @NonNull String getIdmapPath(@NonNull final String overlayPackagePath,
final int userId) {
- if (FEATURE_FLAG_IDMAP2) {
- try {
- return mIdmapDaemon.getIdmapPath(overlayPackagePath, userId);
- } catch (Exception e) {
- Slog.w(TAG, "failed to get idmap path for " + overlayPackagePath + ": "
- + e.getMessage());
- return "";
- }
- } else {
- final StringBuilder sb = new StringBuilder("/data/resource-cache/");
- sb.append(overlayPackagePath.substring(1).replace('/', '@'));
- sb.append("@idmap");
- return sb.toString();
+ try {
+ return mIdmapDaemon.getIdmapPath(overlayPackagePath, userId);
+ } catch (Exception e) {
+ Slog.w(TAG, "failed to get idmap path for " + overlayPackagePath + ": "
+ + e.getMessage());
+ return "";
}
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index f8b3fb259089..5f3e50320752 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -17,11 +17,13 @@
package com.android.server.om;
import static android.app.AppGlobals.getPackageManager;
+import static android.content.Intent.ACTION_OVERLAY_CHANGED;
import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.ACTION_PACKAGE_CHANGED;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.ACTION_USER_ADDED;
import static android.content.Intent.ACTION_USER_REMOVED;
+import static android.content.Intent.EXTRA_REASON;
import static android.content.pm.PackageManager.SIGNATURE_MATCH;
import static android.os.Trace.TRACE_TAG_RRO;
import static android.os.Trace.traceBegin;
@@ -62,7 +64,6 @@ import com.android.server.FgThread;
import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
-import com.android.server.pm.Installer;
import com.android.server.pm.UserManagerService;
import libcore.util.EmptyArray;
@@ -230,8 +231,7 @@ public final class OverlayManagerService extends SystemService {
private final AtomicBoolean mPersistSettingsScheduled = new AtomicBoolean(false);
- public OverlayManagerService(@NonNull final Context context,
- @NonNull final Installer installer) {
+ public OverlayManagerService(@NonNull final Context context) {
super(context);
try {
traceBegin(TRACE_TAG_RRO, "OMS#OverlayManagerService");
@@ -239,7 +239,7 @@ public final class OverlayManagerService extends SystemService {
new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays");
mPackageManager = new PackageManagerHelper();
mUserManager = UserManagerService.getInstance();
- IdmapManager im = new IdmapManager(installer, mPackageManager);
+ IdmapManager im = new IdmapManager(mPackageManager);
mSettings = new OverlayManagerSettings();
mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
getDefaultOverlayPackages(), new OverlayChangeListener());
@@ -358,7 +358,11 @@ public final class OverlayManagerService extends SystemService {
}
break;
case ACTION_PACKAGE_CHANGED:
- onPackageChanged(packageName, userIds);
+ // ignore the intent if it was sent by the package manager as a result of the
+ // overlay manager having sent ACTION_OVERLAY_CHANGED
+ if (!ACTION_OVERLAY_CHANGED.equals(intent.getStringExtra(EXTRA_REASON))) {
+ onPackageChanged(packageName, userIds);
+ }
break;
case ACTION_PACKAGE_REMOVED:
if (replacing) {
@@ -740,6 +744,25 @@ public final class OverlayManagerService extends SystemService {
}
@Override
+ public void invalidateCachesForOverlay(@Nullable String packageName, int userId)
+ throws RemoteException {
+ if (packageName == null) {
+ return;
+ }
+
+ enforceChangeOverlayPackagesPermission("invalidateCachesForOverlay");
+ userId = handleIncomingUser(userId, "invalidateCachesForOverlay");
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ mImpl.removeIdmapForOverlay(packageName, userId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
public void onShellCommand(@NonNull final FileDescriptor in,
@NonNull final FileDescriptor out, @NonNull final FileDescriptor err,
@NonNull final String[] args, @NonNull final ShellCallback callback,
@@ -868,7 +891,7 @@ public final class OverlayManagerService extends SystemService {
FgThread.getHandler().post(() -> {
updateAssets(userId, targetPackageName);
- final Intent intent = new Intent(Intent.ACTION_OVERLAY_CHANGED,
+ final Intent intent = new Intent(ACTION_OVERLAY_CHANGED,
Uri.fromParts("package", targetPackageName, null));
intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 934511bf88d1..019c9528f8ab 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -651,6 +651,11 @@ final class OverlayManagerServiceImpl {
return mDefaultOverlays;
}
+ void removeIdmapForOverlay(String packageName, int userId) {
+ final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
+ removeIdmapIfPossible(oi);
+ }
+
List<String> getEnabledOverlayPackageNames(@NonNull final String targetPackageName,
final int userId) {
final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName,
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index 714bbb97c90d..9a1b30dc2b0b 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -25,7 +25,6 @@ import android.os.Binder;
import android.os.BugreportParams;
import android.os.IDumpstate;
import android.os.IDumpstateListener;
-import android.os.IDumpstateToken;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -64,13 +63,6 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
@Override
@RequiresPermission(android.Manifest.permission.DUMP)
- public IDumpstateToken setListener(String name, IDumpstateListener listener,
- boolean getSectionDetails) {
- throw new UnsupportedOperationException("setListener is not allowed on this service");
- }
-
- @Override
- @RequiresPermission(android.Manifest.permission.DUMP)
public void startBugreport(int callingUidUnused, String callingPackage,
FileDescriptor bugreportFd, FileDescriptor screenshotFd,
int bugreportMode, IDumpstateListener listener) {
diff --git a/services/core/java/com/android/server/os/SchedulingPolicyService.java b/services/core/java/com/android/server/os/SchedulingPolicyService.java
index 2371b04b583c..c3ed7d7dd6c6 100644
--- a/services/core/java/com/android/server/os/SchedulingPolicyService.java
+++ b/services/core/java/com/android/server/os/SchedulingPolicyService.java
@@ -21,7 +21,6 @@ import android.os.Binder;
import android.os.IBinder;
import android.os.ISchedulingPolicyService;
import android.os.Process;
-import android.os.RemoteException;
import android.util.Log;
import com.android.server.SystemServerInitThreadPool;
@@ -64,7 +63,7 @@ public class SchedulingPolicyService extends ISchedulingPolicyService.Stub {
// (Note that if mediaserver thinks we're in boosted state before the crash,
// the state could go out of sync temporarily until mediaserver enables/disable
// boost next time, but this won't be a big issue.)
- SystemServerInitThreadPool.get().submit(() -> {
+ SystemServerInitThreadPool.submit(() -> {
synchronized (mDeathRecipient) {
// only do this if we haven't already got a request to boost.
if (mBoostedPid == -1) {
diff --git a/services/core/java/com/android/server/os/TEST_MAPPING b/services/core/java/com/android/server/os/TEST_MAPPING
new file mode 100644
index 000000000000..502f1e852e08
--- /dev/null
+++ b/services/core/java/com/android/server/os/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsUsbTests"
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 61ea84f9dc7f..c8179a767d23 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -41,7 +41,7 @@ import android.util.SparseArray;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.FgThread;
-import com.android.server.compat.PlatformCompat;
+import com.android.server.compat.CompatConfig;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -131,24 +131,22 @@ public class AppsFilter {
private static class FeatureConfigImpl implements FeatureConfig {
private static final String FILTERING_ENABLED_NAME = "package_query_filtering_enabled";
- private final PackageManagerService.Injector mInjector;
- private volatile boolean mFeatureEnabled = true;
+ private volatile boolean mFeatureEnabled = false;
+ private CompatConfig mCompatibility;
private FeatureConfigImpl(PackageManagerService.Injector injector) {
- mInjector = injector;
+ mCompatibility = injector.getCompatibility();
}
@Override
public void onSystemReady() {
mFeatureEnabled = DeviceConfig.getBoolean(
- NAMESPACE_PACKAGE_MANAGER_SERVICE, FILTERING_ENABLED_NAME,
- true);
+ NAMESPACE_PACKAGE_MANAGER_SERVICE, FILTERING_ENABLED_NAME, false);
DeviceConfig.addOnPropertiesChangedListener(
NAMESPACE_PACKAGE_MANAGER_SERVICE, FgThread.getExecutor(),
properties -> {
synchronized (FeatureConfigImpl.this) {
- mFeatureEnabled = properties.getBoolean(
- FILTERING_ENABLED_NAME, true);
+ mFeatureEnabled = properties.getBoolean(FILTERING_ENABLED_NAME, false);
}
});
}
@@ -160,12 +158,7 @@ public class AppsFilter {
@Override
public boolean packageIsEnabled(PackageParser.Package pkg) {
- final PlatformCompat compatibility = mInjector.getCompatibility();
- if (compatibility == null) {
- Slog.wtf(TAG, "PlatformCompat is null");
- return mFeatureEnabled;
- }
- return compatibility.isChangeEnabled(
+ return mCompatibility.isChangeEnabled(
PackageManager.FILTER_APPLICATION_QUERY, pkg.applicationInfo);
}
}
@@ -202,19 +195,19 @@ public class AppsFilter {
return false;
}
for (Intent intent : querying.mQueriesIntents) {
- if (matches(intent, potentialTarget.providers, potentialTarget.activities,
- potentialTarget.services, potentialTarget.receivers)) {
+ if (matches(intent, potentialTarget)) {
return true;
}
}
return false;
}
- private static boolean matches(Intent intent,
- ArrayList<PackageParser.Provider> providerList,
- ArrayList<? extends Component<? extends IntentInfo>>... componentLists) {
- for (int p = providerList.size() - 1; p >= 0; p--) {
- PackageParser.Provider provider = providerList.get(p);
+ private static boolean matches(Intent intent, PackageParser.Package potentialTarget) {
+ for (int p = potentialTarget.providers.size() - 1; p >= 0; p--) {
+ PackageParser.Provider provider = potentialTarget.providers.get(p);
+ if (!provider.info.exported) {
+ continue;
+ }
final ProviderInfo providerInfo = provider.info;
final Uri data = intent.getData();
if ("content".equalsIgnoreCase(intent.getScheme())
@@ -223,19 +216,44 @@ public class AppsFilter {
return true;
}
}
+ for (int s = potentialTarget.services.size() - 1; s >= 0; s--) {
+ PackageParser.Service service = potentialTarget.services.get(s);
+ if (!service.info.exported) {
+ continue;
+ }
+ if (matchesAnyFilter(intent, service)) {
+ return true;
+ }
+ }
+ for (int a = potentialTarget.activities.size() - 1; a >= 0; a--) {
+ PackageParser.Activity activity = potentialTarget.activities.get(a);
+ if (!activity.info.exported) {
+ continue;
+ }
+ if (matchesAnyFilter(intent, activity)) {
+ return true;
+ }
+ }
+ for (int r = potentialTarget.receivers.size() - 1; r >= 0; r--) {
+ PackageParser.Activity receiver = potentialTarget.receivers.get(r);
+ if (!receiver.info.exported) {
+ continue;
+ }
+ if (matchesAnyFilter(intent, receiver)) {
+ return true;
+ }
+ }
+ return false;
+ }
- for (int l = componentLists.length - 1; l >= 0; l--) {
- ArrayList<? extends Component<? extends IntentInfo>> components = componentLists[l];
- for (int c = components.size() - 1; c >= 0; c--) {
- Component<? extends IntentInfo> component = components.get(c);
- ArrayList<? extends IntentInfo> intents = component.intents;
- for (int i = intents.size() - 1; i >= 0; i--) {
- IntentFilter intentFilter = intents.get(i);
- if (intentFilter.match(intent.getAction(), intent.getType(), intent.getScheme(),
- intent.getData(), intent.getCategories(), "AppsFilter") > 0) {
- return true;
- }
- }
+ private static boolean matchesAnyFilter(
+ Intent intent, Component<? extends IntentInfo> component) {
+ ArrayList<? extends IntentInfo> intents = component.intents;
+ for (int i = intents.size() - 1; i >= 0; i--) {
+ IntentFilter intentFilter = intents.get(i);
+ if (intentFilter.match(intent.getAction(), intent.getType(), intent.getScheme(),
+ intent.getData(), intent.getCategories(), "AppsFilter") > 0) {
+ return true;
}
}
return false;
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 8facce112b52..b1eb7e79bc1b 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -23,6 +23,7 @@ import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING
import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
import static com.android.server.pm.PackageManagerService.fixProcessName;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
@@ -49,6 +50,7 @@ import android.util.Pair;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
import com.android.server.IntentResolver;
import java.io.PrintWriter;
@@ -219,12 +221,14 @@ public class ComponentResolver {
}
}
+ @Nullable
List<ResolveInfo> queryActivities(Intent intent, String resolvedType, int flags, int userId) {
synchronized (mLock) {
return mActivities.queryIntent(intent, resolvedType, flags, userId);
}
}
+ @Nullable
List<ResolveInfo> queryActivities(Intent intent, String resolvedType, int flags,
List<PackageParser.Activity> activities, int userId) {
synchronized (mLock) {
@@ -233,12 +237,14 @@ public class ComponentResolver {
}
}
+ @Nullable
List<ResolveInfo> queryProviders(Intent intent, String resolvedType, int flags, int userId) {
synchronized (mLock) {
return mProviders.queryIntent(intent, resolvedType, flags, userId);
}
}
+ @Nullable
List<ResolveInfo> queryProviders(Intent intent, String resolvedType, int flags,
List<PackageParser.Provider> providers, int userId) {
synchronized (mLock) {
@@ -246,6 +252,7 @@ public class ComponentResolver {
}
}
+ @Nullable
List<ProviderInfo> queryProviders(String processName, String metaDataKey, int uid, int flags,
int userId) {
if (!sUserManager.exists(userId)) {
@@ -285,6 +292,7 @@ public class ComponentResolver {
return providerList;
}
+ @Nullable
ProviderInfo queryProvider(String authority, int flags, int userId) {
synchronized (mLock) {
final PackageParser.Provider p = mProvidersByAuthority.get(authority);
@@ -326,12 +334,14 @@ public class ComponentResolver {
}
}
+ @Nullable
List<ResolveInfo> queryReceivers(Intent intent, String resolvedType, int flags, int userId) {
synchronized (mLock) {
return mReceivers.queryIntent(intent, resolvedType, flags, userId);
}
}
+ @Nullable
List<ResolveInfo> queryReceivers(Intent intent, String resolvedType, int flags,
List<PackageParser.Activity> receivers, int userId) {
synchronized (mLock) {
@@ -339,12 +349,14 @@ public class ComponentResolver {
}
}
+ @Nullable
List<ResolveInfo> queryServices(Intent intent, String resolvedType, int flags, int userId) {
synchronized (mLock) {
return mServices.queryIntent(intent, resolvedType, flags, userId);
}
}
+ @Nullable
List<ResolveInfo> queryServices(Intent intent, String resolvedType, int flags,
List<PackageParser.Service> services, int userId) {
synchronized (mLock) {
@@ -375,8 +387,11 @@ public class ComponentResolver {
addProvidersLocked(pkg, chatty);
addServicesLocked(pkg, chatty);
}
- final String setupWizardPackage = sPackageManagerInternal.getKnownPackageName(
- PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM);
+ // expect single setupwizard package
+ final String setupWizardPackage = ArrayUtils.firstOrNull(
+ sPackageManagerInternal.getKnownPackageNames(
+ PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM));
+
for (int i = newIntents.size() - 1; i >= 0; --i) {
final PackageParser.ActivityIntentInfo intentInfo = newIntents.get(i);
final PackageParser.Package disabledPkg = sPackageManagerInternal
@@ -410,8 +425,11 @@ public class ComponentResolver {
final List<ActivityIntentInfo> protectedFilters = mProtectedFilters;
mProtectedFilters = null;
- final String setupWizardPackage = sPackageManagerInternal.getKnownPackageName(
- PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM);
+ // expect single setupwizard package
+ final String setupWizardPackage = ArrayUtils.firstOrNull(
+ sPackageManagerInternal.getKnownPackageNames(
+ PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM));
+
if (DEBUG_FILTERS && setupWizardPackage == null) {
Slog.i(TAG, "No setup wizard;"
+ " All protected intents capped to priority 0");
@@ -1355,6 +1373,7 @@ public class ComponentResolver {
return super.queryIntent(intent, resolvedType, defaultOnly, userId);
}
+ @Nullable
List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags,
int userId) {
if (!sUserManager.exists(userId)) {
@@ -1366,6 +1385,7 @@ public class ComponentResolver {
userId);
}
+ @Nullable
List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
int flags, List<PackageParser.Provider> packageProviders, int userId) {
if (!sUserManager.exists(userId)) {
diff --git a/services/core/java/com/android/server/pm/InstallSource.java b/services/core/java/com/android/server/pm/InstallSource.java
new file mode 100644
index 000000000000..990eba15234e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/InstallSource.java
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.Nullable;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.util.Objects;
+
+/**
+ * Immutable class holding information about where the request to install or update an app
+ * came from.
+ */
+final class InstallSource {
+ /**
+ * An instance of InstallSource representing an absence of knowledge of the source of
+ * a package. Used in preference to null.
+ */
+ static final InstallSource EMPTY = new InstallSource(null, null, false);
+
+ /** The package that requested the installation, if known. */
+ @Nullable
+ final String initiatingPackageName;
+
+ /**
+ * Package name of the app that installed this package (the installer of record). Note that
+ * this may be modified.
+ */
+ @Nullable
+ final String installerPackageName;
+
+ /** Indicates if the package that was the installerPackageName has been uninstalled. */
+ final boolean isOrphaned;
+
+ static InstallSource create(@Nullable String initiatingPackageName,
+ @Nullable String installerPackageName) {
+ return create(initiatingPackageName, installerPackageName, false);
+ }
+
+ static InstallSource create(@Nullable String initiatingPackageName,
+ @Nullable String installerPackageName, boolean isOrphaned) {
+ if (initiatingPackageName == null && installerPackageName == null && !isOrphaned) {
+ return EMPTY;
+ }
+ return new InstallSource(
+ initiatingPackageName == null ? null : initiatingPackageName.intern(),
+ installerPackageName == null ? null : installerPackageName.intern(),
+ isOrphaned);
+ }
+
+ private InstallSource(@Nullable String initiatingPackageName,
+ @Nullable String installerPackageName, boolean isOrphaned) {
+ this.initiatingPackageName = initiatingPackageName;
+ this.isOrphaned = isOrphaned;
+ this.installerPackageName = installerPackageName;
+ }
+
+ void dump(IndentingPrintWriter pw) {
+ pw.printPair("installerPackageName", installerPackageName);
+ pw.printPair("installInitiatingPackageName", initiatingPackageName);
+ }
+
+ /**
+ * Return an InstallSource the same as this one except with the specified installerPackageName.
+ */
+ InstallSource setInstallerPackage(String installerPackageName) {
+ return Objects.equals(installerPackageName, this.installerPackageName) ? this
+ : create(initiatingPackageName, installerPackageName, isOrphaned);
+ }
+
+ /**
+ * Return an InstallSource the same as this one except with the specified value for isOrphaned.
+ */
+ InstallSource setIsOrphaned(boolean isOrphaned) {
+ return isOrphaned == this.isOrphaned ? this
+ : create(initiatingPackageName, installerPackageName, isOrphaned);
+ }
+
+ /**
+ * Return an InstallSource the same as this one except it does not refer to the specified
+ * installer package name (which is being uninstalled).
+ */
+ InstallSource removeInstallerPackage(String packageName) {
+ if (packageName == null) {
+ return this;
+ }
+
+ boolean modified = false;
+ String initiatingPackageName = this.initiatingPackageName;
+ String installerPackageName = this.installerPackageName;
+ boolean isOrphaned = this.isOrphaned;
+
+ if (packageName.equals(initiatingPackageName)) {
+ initiatingPackageName = null;
+ modified = true;
+ }
+ if (packageName.equals(installerPackageName)) {
+ installerPackageName = null;
+ isOrphaned = true;
+ modified = true;
+ }
+
+ return modified
+ ? create(initiatingPackageName, installerPackageName, isOrphaned)
+ : this;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 3464cab99d93..2b6c347fe726 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -62,6 +62,7 @@ import android.os.UserManager;
import android.os.UserManagerInternal;
import android.provider.Settings;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -1024,8 +1025,22 @@ public class LauncherAppsService extends SystemService {
}
@Override
- public void onPackagesSuspended(String[] packages, Bundle launcherExtras) {
+ public void onPackagesSuspended(String[] packages) {
UserHandle user = new UserHandle(getChangingUserId());
+ PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+ final ArrayList<Pair<String, Bundle>> packagesWithExtras = new ArrayList<>();
+ final ArrayList<String> packagesWithoutExtras = new ArrayList<>();
+ for (String pkg : packages) {
+ final Bundle launcherExtras = pmi.getSuspendedPackageLauncherExtras(pkg,
+ user.getIdentifier());
+ if (launcherExtras != null) {
+ packagesWithExtras.add(new Pair<>(pkg, launcherExtras));
+ } else {
+ packagesWithoutExtras.add(pkg);
+ }
+ }
+ final String[] packagesNullExtras = packagesWithoutExtras.toArray(
+ new String[packagesWithoutExtras.size()]);
final int n = mListeners.beginBroadcast();
try {
for (int i = 0; i < n; i++) {
@@ -1033,7 +1048,13 @@ public class LauncherAppsService extends SystemService {
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
if (!isEnabledProfileOf(cookie.user, user, "onPackagesSuspended")) continue;
try {
- listener.onPackagesSuspended(user, packages, launcherExtras);
+ listener.onPackagesSuspended(user, packagesNullExtras, null);
+ for (int idx = 0; idx < packagesWithExtras.size(); idx++) {
+ Pair<String, Bundle> packageExtraPair = packagesWithExtras.get(idx);
+ listener.onPackagesSuspended(user,
+ new String[]{packageExtraPair.first},
+ packageExtraPair.second);
+ }
} catch (RemoteException re) {
Slog.d(TAG, "Callback failed ", re);
}
@@ -1041,8 +1062,6 @@ public class LauncherAppsService extends SystemService {
} finally {
mListeners.finishBroadcast();
}
-
- super.onPackagesSuspended(packages, launcherExtras);
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index f777fe9cd95c..e3199e0eef9c 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -259,6 +259,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
// Don't hold mSessions lock when calling restoreSession, since it might trigger an APK
// atomic install which needs to query sessions, which requires lock on mSessions.
for (PackageInstallerSession session : stagedSessionsToRestore) {
+ if (mPm.isDeviceUpgrading() && !session.isStagedAndInTerminalState()) {
+ session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+ "Build fingerprint has changed");
+ }
mStagingManager.restoreSession(session);
}
// Broadcasts are not sent while we restore sessions on boot, since no processes would be
@@ -479,15 +483,24 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
throw new SecurityException("User restriction prevents installing");
}
+ String requestedInstallerPackageName = params.installerPackageName != null
+ ? params.installerPackageName : installerPackageName;
+
if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
params.installFlags |= PackageManager.INSTALL_FROM_ADB;
} else {
+ if (callingUid != Process.SYSTEM_UID) {
+ // The supplied installerPackageName must always belong to the calling app.
+ mAppOps.checkPackage(callingUid, installerPackageName);
+ }
// Only apps with INSTALL_PACKAGES are allowed to set an installer that is not the
// caller.
- if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES) !=
- PackageManager.PERMISSION_GRANTED) {
- mAppOps.checkPackage(callingUid, installerPackageName);
+ if (!requestedInstallerPackageName.equals(installerPackageName)) {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES)
+ != PackageManager.PERMISSION_GRANTED) {
+ mAppOps.checkPackage(callingUid, requestedInstallerPackageName);
+ }
}
params.installFlags &= ~PackageManager.INSTALL_FROM_ADB;
@@ -611,11 +624,13 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
stageCid = buildExternalStageCid(sessionId);
}
}
+ InstallSource installSource = InstallSource.create(installerPackageName,
+ requestedInstallerPackageName);
session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
- mInstallThread.getLooper(), mStagingManager, sessionId, userId,
- installerPackageName, callingUid, params, createdMillis, stageDir, stageCid, false,
- false, false, null, SessionInfo.INVALID_ID, false, false, false,
- SessionInfo.STAGED_SESSION_NO_ERROR, "");
+ mInstallThread.getLooper(), mStagingManager, sessionId, userId, callingUid,
+ installSource, params, createdMillis,
+ stageDir, stageCid, false, false, false, null, SessionInfo.INVALID_ID,
+ false, false, false, SessionInfo.STAGED_SESSION_NO_ERROR, "");
synchronized (mSessions) {
mSessions.put(sessionId, session);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index b72029046067..feb1271d8c79 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -146,6 +146,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private static final String ATTR_USER_ID = "userId";
private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName";
private static final String ATTR_INSTALLER_UID = "installerUid";
+ private static final String ATTR_INITIATING_PACKAGE_NAME =
+ "installInitiatingPackageName";
private static final String ATTR_CREATED_MILLIS = "createdMillis";
private static final String ATTR_UPDATED_MILLIS = "updatedMillis";
private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir";
@@ -210,14 +212,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
/** Uid of the creator of this session. */
private final int mOriginalInstallerUid;
- /** Package of the owner of the installer session */
- @GuardedBy("mLock")
- private @Nullable String mInstallerPackageName;
-
/** Uid of the owner of the installer session */
@GuardedBy("mLock")
private int mInstallerUid;
+ /** Where this install request came from */
+ @GuardedBy("mLock")
+ private InstallSource mInstallSource;
+
@GuardedBy("mLock")
private float mClientProgress = 0;
@GuardedBy("mLock")
@@ -368,7 +370,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
DevicePolicyManagerInternal dpmi =
LocalServices.getService(DevicePolicyManagerInternal.class);
- return dpmi != null && dpmi.canSilentlyInstallPackage(mInstallerPackageName, mInstallerUid);
+ return dpmi != null && dpmi.canSilentlyInstallPackage(
+ mInstallSource.installerPackageName, mInstallerUid);
}
/**
@@ -412,8 +415,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
Context context, PackageManagerService pm,
PackageSessionProvider sessionProvider, Looper looper, StagingManager stagingManager,
- int sessionId, int userId,
- String installerPackageName, int installerUid, SessionParams params, long createdMillis,
+ int sessionId, int userId, int installerUid, @NonNull InstallSource installSource,
+ SessionParams params, long createdMillis,
File stageDir, String stageCid, boolean prepared, boolean committed, boolean sealed,
@Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
boolean isFailed, boolean isApplied, int stagedSessionErrorCode,
@@ -428,8 +431,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
this.sessionId = sessionId;
this.userId = userId;
mOriginalInstallerUid = installerUid;
- mInstallerPackageName = installerPackageName;
mInstallerUid = installerUid;
+ mInstallSource = Preconditions.checkNotNull(installSource);
this.params = params;
this.createdMillis = createdMillis;
this.updatedMillis = createdMillis;
@@ -467,7 +470,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
synchronized (mLock) {
info.sessionId = sessionId;
info.userId = userId;
- info.installerPackageName = mInstallerPackageName;
+ info.installerPackageName = mInstallSource.installerPackageName;
info.resolvedBaseCodePath = (mResolvedBaseFile != null) ?
mResolvedBaseFile.getAbsolutePath() : null;
info.progress = mProgress;
@@ -1218,13 +1221,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
throw new IllegalArgumentException("Package is not valid", e);
}
- if (!mPackageName.equals(mInstallerPackageName)) {
+ if (!mPackageName.equals(mInstallSource.installerPackageName)) {
throw new SecurityException("Can only transfer sessions that update the original "
+ "installer");
}
- mInstallerPackageName = packageName;
mInstallerUid = newOwnerAppInfo.uid;
+ mInstallSource = InstallSource.create(packageName, packageName);
}
// Persist the fact that we've sealed ourselves to prevent
@@ -1237,7 +1240,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked()) {
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.INSTALL_PACKAGE)
- .setAdmin(mInstallerPackageName)
+ .setAdmin(mInstallSource.installerPackageName)
.write();
}
if (params.isStaged) {
@@ -1443,8 +1446,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mRelinquished = true;
return new PackageManagerService.ActiveInstallSession(mPackageName, stageDir,
- localObserver, params, mInstallerPackageName, mInstallerUid, user,
- mSigningDetails);
+ localObserver, params, mInstallerUid, mInstallSource, user, mSigningDetails);
}
private static void maybeRenameFile(File from, File to) throws PackageManagerException {
@@ -1893,7 +1895,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
String getInstallerPackageName() {
synchronized (mLock) {
- return mInstallerPackageName;
+ return mInstallSource.installerPackageName;
}
}
@@ -2334,7 +2336,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
pw.printPair("userId", userId);
pw.printPair("mOriginalInstallerUid", mOriginalInstallerUid);
- pw.printPair("mInstallerPackageName", mInstallerPackageName);
+ mInstallSource.dump(pw);
pw.printPair("mInstallerUid", mInstallerUid);
pw.printPair("createdMillis", createdMillis);
pw.printPair("updatedMillis", updatedMillis);
@@ -2414,8 +2416,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
writeIntAttribute(out, ATTR_SESSION_ID, sessionId);
writeIntAttribute(out, ATTR_USER_ID, userId);
writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME,
- mInstallerPackageName);
+ mInstallSource.installerPackageName);
writeIntAttribute(out, ATTR_INSTALLER_UID, mInstallerUid);
+ writeStringAttribute(out, ATTR_INITIATING_PACKAGE_NAME,
+ mInstallSource.initiatingPackageName);
writeLongAttribute(out, ATTR_CREATED_MILLIS, createdMillis);
writeLongAttribute(out, ATTR_UPDATED_MILLIS, updatedMillis);
if (stageDir != null) {
@@ -2521,6 +2525,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME);
final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID, pm.getPackageUid(
installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId));
+ final String installInitiatingPackageName =
+ readStringAttribute(in, ATTR_INITIATING_PACKAGE_NAME);
final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
long updatedMillis = readLongAttribute(in, ATTR_UPDATED_MILLIS);
final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
@@ -2612,17 +2618,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
childSessionIdsArray = EMPTY_CHILD_SESSION_ARRAY;
}
+ InstallSource installSource = InstallSource.create(installInitiatingPackageName,
+ installerPackageName);
return new PackageInstallerSession(callback, context, pm, sessionProvider,
- installerThread, stagingManager, sessionId, userId, installerPackageName,
- installerUid, params, createdMillis, stageDir, stageCid, prepared, committed,
- sealed, childSessionIdsArray, parentSessionId, isReady, isFailed, isApplied,
- stagedSessionErrorCode, stagedSessionErrorMessage);
- }
-
- /**
- * Reads the session ID from a child session tag stored in the provided {@link XmlPullParser}
- */
- static int readChildSessionIdFromXml(@NonNull XmlPullParser in) {
- return readIntAttribute(in, ATTR_SESSION_ID, SessionInfo.INVALID_ID);
+ installerThread, stagingManager, sessionId, userId, installerUid,
+ installSource, params, createdMillis, stageDir, stageCid,
+ prepared, committed, sealed, childSessionIdsArray, parentSessionId,
+ isReady, isFailed, isApplied, stagedSessionErrorCode, stagedSessionErrorMessage);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageList.java b/services/core/java/com/android/server/pm/PackageList.java
new file mode 100644
index 000000000000..60bc8a87f62d
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageList.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageManagerInternal.PackageListObserver;
+
+import com.android.server.LocalServices;
+
+import java.util.List;
+
+/**
+ * All of the package name installed on the system.
+ * <p>A self observable list that automatically removes the listener when it goes out of scope.
+ *
+ * @hide Only for use within the system server.
+ */
+public class PackageList implements PackageListObserver, AutoCloseable {
+ private final PackageListObserver mWrappedObserver;
+ private final List<String> mPackageNames;
+
+ /**
+ * Create a new object.
+ * <p>Ownership of the given {@link List} transfers to this object and should not
+ * be modified by the caller.
+ */
+ public PackageList(@NonNull List<String> packageNames, @Nullable PackageListObserver observer) {
+ mPackageNames = packageNames;
+ mWrappedObserver = observer;
+ }
+
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ if (mWrappedObserver != null) {
+ mWrappedObserver.onPackageAdded(packageName, uid);
+ }
+ }
+
+ @Override
+ public void onPackageChanged(String packageName, int uid) {
+ if (mWrappedObserver != null) {
+ mWrappedObserver.onPackageChanged(packageName, uid);
+ }
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ if (mWrappedObserver != null) {
+ mWrappedObserver.onPackageRemoved(packageName, uid);
+ }
+ }
+
+ @Override
+ public void close() throws Exception {
+ LocalServices.getService(PackageManagerInternal.class).removePackageListObserver(this);
+ }
+
+ /**
+ * Returns the names of packages installed on the system.
+ * <p>The list is a copy-in-time and the actual set of installed packages may differ. Real
+ * time updates to the package list are sent via the {@link PackageListObserver} callback.
+ */
+ public @NonNull List<String> getPackageNames() {
+ return mPackageNames;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e61e1d40c2c9..e88e64dab096 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -37,7 +37,7 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
@@ -137,7 +137,6 @@ import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
-import android.content.pm.AppsQueryHelper;
import android.content.pm.AuxiliaryResolveInfo;
import android.content.pm.ChangedPackages;
import android.content.pm.ComponentInfo;
@@ -163,7 +162,6 @@ import android.content.pm.PackageBackwardCompatibility;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
-import android.content.pm.PackageList;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
import android.content.pm.PackageManager.ModuleInfoFlags;
@@ -302,7 +300,7 @@ import com.android.server.ServiceThread;
import com.android.server.SystemConfig;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.Watchdog;
-import com.android.server.compat.PlatformCompat;
+import com.android.server.compat.CompatConfig;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.Settings.DatabaseVersion;
@@ -373,7 +371,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
-import java.util.function.Predicate;
/**
* Keep track of all those APKs everywhere.
@@ -582,16 +579,6 @@ public class PackageManagerService extends IPackageManager.Stub
private static final String PACKAGE_SCHEME = "package";
- private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
-
- private static final String PRODUCT_OVERLAY_DIR = "/product/overlay";
-
- private static final String SYSTEM_EXT_OVERLAY_DIR = "/system_ext/overlay";
-
- private static final String ODM_OVERLAY_DIR = "/odm/overlay";
-
- private static final String OEM_OVERLAY_DIR = "/oem/overlay";
-
/** Canonical intent used to identify what counts as a "web browser" app */
private static final Intent sBrowserIntent;
static {
@@ -757,6 +744,26 @@ public class PackageManagerService extends IPackageManager.Stub
private final Injector mInjector;
/**
+ * The list of all system partitions that may contain packages in ascending order of
+ * specificity (the more generic, the earlier in the list a partition appears).
+ */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final List<SystemPartition> SYSTEM_PARTITIONS = Collections.unmodifiableList(
+ Arrays.asList(
+ new SystemPartition(Environment.getRootDirectory(), 0 /* scanFlag */,
+ true /* hasPriv */, false /* hasOverlays */),
+ new SystemPartition(Environment.getVendorDirectory(), SCAN_AS_VENDOR,
+ true /* hasPriv */, true /* hasOverlays */),
+ new SystemPartition(Environment.getOdmDirectory(), SCAN_AS_ODM,
+ true /* hasPriv */, true /* hasOverlays */),
+ new SystemPartition(Environment.getOemDirectory(), SCAN_AS_OEM,
+ false /* hasPriv */, true /* hasOverlays */),
+ new SystemPartition(Environment.getProductDirectory(), SCAN_AS_PRODUCT,
+ true /* hasPriv */, true /* hasOverlays */),
+ new SystemPartition(Environment.getSystemExtDirectory(), SCAN_AS_SYSTEM_EXT,
+ true /* hasPriv */, true /* hasOverlays */)));
+
+ /**
* Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors.
*
* NOTE: All getters should return the same instance for every call.
@@ -828,7 +835,7 @@ public class PackageManagerService extends IPackageManager.Stub
private final Singleton<StorageManager> mStorageManagerProducer;
private final Singleton<AppOpsManager> mAppOpsManagerProducer;
private final Singleton<AppsFilter> mAppsFilterProducer;
- private final Singleton<PlatformCompat> mPlatformCompatProducer;
+ private final Singleton<CompatConfig> mPlatformCompatProducer;
Injector(Context context, Object lock, Installer installer,
Object installLock, PackageAbiHelper abiHelper,
@@ -846,7 +853,7 @@ public class PackageManagerService extends IPackageManager.Stub
Producer<StorageManager> storageManagerProducer,
Producer<AppOpsManager> appOpsManagerProducer,
Producer<AppsFilter> appsFilterProducer,
- Producer<PlatformCompat> platformCompatProducer) {
+ Producer<CompatConfig> platformCompatProducer) {
mContext = context;
mLock = lock;
mInstaller = installer;
@@ -957,7 +964,7 @@ public class PackageManagerService extends IPackageManager.Stub
return mAppsFilterProducer.get(this, mPackageManager);
}
- public PlatformCompat getCompatibility() {
+ public CompatConfig getCompatibility() {
return mPlatformCompatProducer.get(this, mPackageManager);
}
}
@@ -968,124 +975,9 @@ public class PackageManagerService extends IPackageManager.Stub
@Override public final boolean hasFeature(String feature) {
return PackageManagerService.this.hasSystemFeature(feature, 0);
}
-
- final List<PackageParser.Package> getStaticOverlayPackages(
- Collection<PackageParser.Package> allPackages, String targetPackageName) {
- if ("android".equals(targetPackageName)) {
- // Static RROs targeting to "android", ie framework-res.apk, are already applied by
- // native AssetManager.
- return null;
- }
-
- List<PackageParser.Package> overlayPackages = null;
- for (PackageParser.Package p : allPackages) {
- if (targetPackageName.equals(p.mOverlayTarget) && p.mOverlayIsStatic) {
- if (overlayPackages == null) {
- overlayPackages = new ArrayList<>();
- }
- overlayPackages.add(p);
- }
- }
- if (overlayPackages != null) {
- Comparator<PackageParser.Package> cmp =
- Comparator.comparingInt(p -> p.mOverlayPriority);
- overlayPackages.sort(cmp);
- }
- return overlayPackages;
- }
-
- final String[] getStaticOverlayPaths(List<PackageParser.Package> overlayPackages,
- String targetPath) {
- if (overlayPackages == null || overlayPackages.isEmpty()) {
- return null;
- }
- List<String> overlayPathList = null;
- for (PackageParser.Package overlayPackage : overlayPackages) {
- if (targetPath == null) {
- if (overlayPathList == null) {
- overlayPathList = new ArrayList<>();
- }
- overlayPathList.add(overlayPackage.baseCodePath);
- continue;
- }
-
- try {
- // Creates idmaps for system to parse correctly the Android manifest of the
- // target package.
- //
- // OverlayManagerService will update each of them with a correct gid from its
- // target package app id.
- mInstaller.idmap(targetPath, overlayPackage.baseCodePath,
- UserHandle.getSharedAppGid(
- UserHandle.getUserGid(UserHandle.USER_SYSTEM)));
- if (overlayPathList == null) {
- overlayPathList = new ArrayList<>();
- }
- overlayPathList.add(overlayPackage.baseCodePath);
- } catch (InstallerException e) {
- Slog.e(TAG, "Failed to generate idmap for " + targetPath + " and " +
- overlayPackage.baseCodePath);
- }
- }
- return overlayPathList == null ? null : overlayPathList.toArray(new String[0]);
- }
-
- String[] getStaticOverlayPaths(String targetPackageName, String targetPath) {
- List<PackageParser.Package> overlayPackages;
- synchronized (mInstallLock) {
- synchronized (mLock) {
- overlayPackages = getStaticOverlayPackages(
- mPackages.values(), targetPackageName);
- }
- // It is safe to keep overlayPackages without holding mPackages because static overlay
- // packages can't be uninstalled or disabled.
- return getStaticOverlayPaths(overlayPackages, targetPath);
- }
- }
-
- @Override public final String[] getOverlayApks(String targetPackageName) {
- return getStaticOverlayPaths(targetPackageName, null);
- }
-
- @Override public final String[] getOverlayPaths(String targetPackageName,
- String targetPath) {
- return getStaticOverlayPaths(targetPackageName, targetPath);
- }
- }
-
- class ParallelPackageParserCallback extends PackageParserCallback {
- List<PackageParser.Package> mOverlayPackages = null;
-
- void findStaticOverlayPackages() {
- synchronized (mLock) {
- for (PackageParser.Package p : mPackages.values()) {
- if (p.mOverlayIsStatic) {
- if (mOverlayPackages == null) {
- mOverlayPackages = new ArrayList<>();
- }
- mOverlayPackages.add(p);
- }
- }
- }
- }
-
- @Override
- synchronized String[] getStaticOverlayPaths(String targetPackageName, String targetPath) {
- // We can trust mOverlayPackages without holding mPackages because package uninstall
- // can't happen while running parallel parsing.
- // And we can call mInstaller inside getStaticOverlayPaths without holding mInstallLock
- // because mInstallLock is held before running parallel parsing.
- // Moreover holding mPackages or mInstallLock on each parsing thread causes dead-lock.
- return mOverlayPackages == null ? null :
- getStaticOverlayPaths(
- getStaticOverlayPackages(mOverlayPackages, targetPackageName),
- targetPath);
- }
}
final PackageParser.Callback mPackageParserCallback = new PackageParserCallback();
- final ParallelPackageParserCallback mParallelPackageParserCallback =
- new ParallelPackageParserCallback();
// Currently known shared libraries.
final ArrayMap<String, LongSparseArray<SharedLibraryInfo>> mSharedLibraries = new ArrayMap<>();
@@ -1583,7 +1475,7 @@ public class PackageManagerService extends IPackageManager.Stub
private static final int USER_RUNTIME_GRANT_MASK =
FLAG_PERMISSION_USER_SET
| FLAG_PERMISSION_USER_FIXED
- | FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+ | FLAG_PERMISSION_REVOKED_COMPAT;
final @Nullable String mRequiredVerifierPackage;
final @Nullable String mOptionalVerifierPackage;
@@ -1598,6 +1490,8 @@ public class PackageManagerService extends IPackageManager.Stub
final @Nullable String mConfiguratorPackage;
final @Nullable String mAppPredictionServicePackage;
final @Nullable String mIncidentReportApproverPackage;
+ final @Nullable String[] mTelephonyPackages;
+ final @Nullable String mWifiPackage;
final @NonNull String mServicesSystemSharedLibraryPackageName;
final @NonNull String mSharedSystemSharedLibraryPackageName;
@@ -1670,7 +1564,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
// Send broadcasts
for (int i = 0; i < size; i++) {
- sendPackageChangedBroadcast(packages[i], true, components[i], uids[i]);
+ sendPackageChangedBroadcast(packages[i], true /* dontKillApp */,
+ components[i], uids[i], null /* reason */);
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
break;
@@ -1705,7 +1600,7 @@ public class PackageManagerService extends IPackageManager.Stub
handlePackagePostInstall(parentRes, grantPermissions,
killApp, virtualPreload, grantedPermissions,
whitelistedRestrictedPermissions, didRestore,
- args.installerPackageName, args.observer);
+ args.installSource.installerPackageName, args.observer);
// Handle the child packages
final int childCount = (parentRes.addedChildPackages != null)
@@ -1715,7 +1610,7 @@ public class PackageManagerService extends IPackageManager.Stub
handlePackagePostInstall(childRes, grantPermissions,
killApp, virtualPreload, grantedPermissions,
whitelistedRestrictedPermissions, false /*didRestore*/,
- args.installerPackageName, args.observer);
+ args.installSource.installerPackageName, args.observer);
}
// Log tracing if needed
@@ -2156,9 +2051,9 @@ public class PackageManagerService extends IPackageManager.Stub
for (int i = 0; i < res.libraryConsumers.size(); i++) {
PackageParser.Package pkg = res.libraryConsumers.get(i);
// send broadcast that all consumers of the static shared library have changed
- sendPackageChangedBroadcast(pkg.packageName, false /*killFlag*/,
+ sendPackageChangedBroadcast(pkg.packageName, false /* dontKillApp */,
new ArrayList<>(Collections.singletonList(pkg.packageName)),
- pkg.applicationInfo.uid);
+ pkg.applicationInfo.uid, null);
}
}
@@ -2460,59 +2355,26 @@ public class PackageManagerService extends IPackageManager.Stub
new Injector.SystemServiceProducer<>(StorageManager.class),
new Injector.SystemServiceProducer<>(AppOpsManager.class),
(i, pm) -> AppsFilter.create(i),
- (i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"));
+ (i, pm) -> CompatConfig.get());
PackageManagerService m = new PackageManagerService(injector, factoryTest, onlyCore);
t.traceEnd(); // "create package manager"
- m.enableSystemUserPackages();
+ m.installWhitelistedSystemPackages();
ServiceManager.addService("package", m);
final PackageManagerNative pmn = m.new PackageManagerNative();
ServiceManager.addService("package_native", pmn);
return m;
}
- private void enableSystemUserPackages() {
- if (!UserManager.isSplitSystemUser()) {
- return;
- }
- // For system user, enable apps based on the following conditions:
- // - app is whitelisted or belong to one of these groups:
- // -- system app which has no launcher icons
- // -- system app which has INTERACT_ACROSS_USERS permission
- // -- system IME app
- // - app is not in the blacklist
- AppsQueryHelper queryHelper = new AppsQueryHelper(this);
- Set<String> enableApps = new ArraySet<>();
- enableApps.addAll(queryHelper.queryApps(AppsQueryHelper.GET_NON_LAUNCHABLE_APPS
- | AppsQueryHelper.GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM
- | AppsQueryHelper.GET_IMES, /* systemAppsOnly */ true, UserHandle.SYSTEM));
- ArraySet<String> wlApps = SystemConfig.getInstance().getSystemUserWhitelistedApps();
- enableApps.addAll(wlApps);
- enableApps.addAll(queryHelper.queryApps(AppsQueryHelper.GET_REQUIRED_FOR_SYSTEM_USER,
- /* systemAppsOnly */ false, UserHandle.SYSTEM));
- ArraySet<String> blApps = SystemConfig.getInstance().getSystemUserBlacklistedApps();
- enableApps.removeAll(blApps);
- Log.i(TAG, "Applications installed for system user: " + enableApps);
- List<String> allAps = queryHelper.queryApps(0, /* systemAppsOnly */ false,
- UserHandle.SYSTEM);
- final int allAppsSize = allAps.size();
+ /** Install/uninstall system packages for all users based on their user-type, as applicable. */
+ private void installWhitelistedSystemPackages() {
synchronized (mLock) {
- for (int i = 0; i < allAppsSize; i++) {
- String pName = allAps.get(i);
- PackageSetting pkgSetting = mSettings.mPackages.get(pName);
- // Should not happen, but we shouldn't be failing if it does
- if (pkgSetting == null) {
- continue;
- }
- boolean install = enableApps.contains(pName);
- if (pkgSetting.getInstalled(UserHandle.USER_SYSTEM) != install) {
- Log.i(TAG, (install ? "Installing " : "Uninstalling ") + pName
- + " for system user");
- pkgSetting.setInstalled(install, UserHandle.USER_SYSTEM);
- }
+ final boolean scheduleWrite = mUserManager.installWhitelistedSystemPackages(
+ isFirstBoot(), isDeviceUpgrading());
+ if (scheduleWrite) {
+ scheduleWritePackageRestrictionsLocked(UserHandle.USER_ALL);
}
- scheduleWritePackageRestrictionsLocked(UserHandle.USER_SYSTEM);
}
}
@@ -2554,6 +2416,51 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static class SystemPartition {
+ public final File folder;
+ public final int scanFlag;
+ public final File appFolder;
+ @Nullable
+ public final File privAppFolder;
+ @Nullable
+ public final File overlayFolder;
+
+ private SystemPartition(File folder, int scanFlag, boolean hasPrivApps,
+ boolean hasOverlays) {
+ this.folder = folder;
+ this.scanFlag = scanFlag;
+ this.appFolder = toCanonical(new File(folder, "app"));
+ this.privAppFolder = hasPrivApps ? toCanonical(new File(folder, "priv-app")) : null;
+ this.overlayFolder = hasOverlays ? toCanonical(new File(folder, "overlay")) : null;
+ }
+
+ public boolean containsPrivApp(File scanFile) {
+ return FileUtils.contains(privAppFolder, scanFile);
+ }
+
+ public boolean containsApp(File scanFile) {
+ return FileUtils.contains(appFolder, scanFile);
+ }
+
+ public boolean containsPath(String path) {
+ return path.startsWith(folder.getPath() + "/");
+ }
+
+ public boolean containsPrivPath(String path) {
+ return privAppFolder != null && path.startsWith(privAppFolder.getPath() + "/");
+ }
+
+ private static File toCanonical(File dir) {
+ try {
+ return dir.getCanonicalFile();
+ } catch (IOException e) {
+ // failed to look up canonical path, continue with original one
+ return dir;
+ }
+ }
+ }
+
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
Trace.TRACE_TAG_PACKAGE_MANAGER);
@@ -2777,215 +2684,33 @@ public class PackageManagerService extends IPackageManager.Stub
// any apps.)
// For security and version matching reason, only consider overlay packages if they
// reside in the right directory.
- scanDirTracedLI(new File(VENDOR_OVERLAY_DIR),
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_VENDOR,
- 0);
- scanDirTracedLI(new File(PRODUCT_OVERLAY_DIR),
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRODUCT,
- 0);
- scanDirTracedLI(new File(SYSTEM_EXT_OVERLAY_DIR),
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_SYSTEM_EXT,
- 0);
- scanDirTracedLI(new File(ODM_OVERLAY_DIR),
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_ODM,
- 0);
- scanDirTracedLI(new File(OEM_OVERLAY_DIR),
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_OEM,
- 0);
-
- mParallelPackageParserCallback.findStaticOverlayPackages();
-
- // Find base frameworks (resource packages without code).
- scanDirTracedLI(frameworkDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_NO_DEX
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRIVILEGED,
- 0);
+ final int systemParseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR;
+ final int systemScanFlags = scanFlags | SCAN_AS_SYSTEM;
+ for (int i = SYSTEM_PARTITIONS.size() - 1; i >= 0; i--) {
+ final SystemPartition partition = SYSTEM_PARTITIONS.get(i);
+ if (partition.overlayFolder == null) {
+ continue;
+ }
+ scanDirTracedLI(partition.overlayFolder, systemParseFlags,
+ systemScanFlags | partition.scanFlag, 0);
+ }
+
+ scanDirTracedLI(frameworkDir, systemParseFlags,
+ systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0);
if (!mPackages.containsKey("android")) {
throw new IllegalStateException(
"Failed to load frameworks package; check log for warnings");
}
-
- // Collect privileged system packages.
- final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
- scanDirTracedLI(privilegedAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRIVILEGED,
- 0);
-
- // Collect ordinary system packages.
- final File systemAppDir = new File(Environment.getRootDirectory(), "app");
- scanDirTracedLI(systemAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM,
- 0);
-
- // Collect privileged vendor packages.
- File privilegedVendorAppDir = new File(Environment.getVendorDirectory(), "priv-app");
- try {
- privilegedVendorAppDir = privilegedVendorAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
- }
- scanDirTracedLI(privilegedVendorAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_VENDOR
- | SCAN_AS_PRIVILEGED,
- 0);
-
- // Collect ordinary vendor packages.
- File vendorAppDir = new File(Environment.getVendorDirectory(), "app");
- try {
- vendorAppDir = vendorAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
- }
- scanDirTracedLI(vendorAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_VENDOR,
- 0);
-
- // Collect privileged odm packages. /odm is another vendor partition
- // other than /vendor.
- File privilegedOdmAppDir = new File(Environment.getOdmDirectory(),
- "priv-app");
- try {
- privilegedOdmAppDir = privilegedOdmAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
- }
- scanDirTracedLI(privilegedOdmAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_VENDOR
- | SCAN_AS_PRIVILEGED,
- 0);
-
- // Collect ordinary odm packages. /odm is another vendor partition
- // other than /vendor.
- File odmAppDir = new File(Environment.getOdmDirectory(), "app");
- try {
- odmAppDir = odmAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
- }
- scanDirTracedLI(odmAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_VENDOR,
- 0);
-
- // Collect all OEM packages.
- final File oemAppDir = new File(Environment.getOemDirectory(), "app");
- scanDirTracedLI(oemAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_OEM,
- 0);
-
- // Collected privileged /product packages.
- File privilegedProductAppDir = new File(Environment.getProductDirectory(), "priv-app");
- try {
- privilegedProductAppDir = privilegedProductAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
- }
- scanDirTracedLI(privilegedProductAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRODUCT
- | SCAN_AS_PRIVILEGED,
- 0);
-
- // Collect ordinary /product packages.
- File productAppDir = new File(Environment.getProductDirectory(), "app");
- try {
- productAppDir = productAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
- }
- scanDirTracedLI(productAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRODUCT,
- 0);
-
- // Collected privileged /system_ext packages.
- File privilegedSystemExtAppDir =
- new File(Environment.getSystemExtDirectory(), "priv-app");
- try {
- privilegedSystemExtAppDir =
- privilegedSystemExtAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
- }
- scanDirTracedLI(privilegedSystemExtAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_SYSTEM_EXT
- | SCAN_AS_PRIVILEGED,
- 0);
-
- // Collect ordinary /system_ext packages.
- File systemExtAppDir = new File(Environment.getSystemExtDirectory(), "app");
- try {
- systemExtAppDir = systemExtAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
+ for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
+ final SystemPartition partition = SYSTEM_PARTITIONS.get(i);
+ if (partition.privAppFolder != null) {
+ scanDirTracedLI(partition.privAppFolder, systemParseFlags,
+ systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0);
+ }
+ scanDirTracedLI(partition.appFolder, systemParseFlags,
+ systemScanFlags | partition.scanFlag, 0);
}
- scanDirTracedLI(systemExtAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_SYSTEM_EXT,
- 0);
+
// Prune any system packages that no longer exist.
final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
@@ -3153,89 +2878,26 @@ public class PackageManagerService extends IPackageManager.Stub
logCriticalInfo(Log.WARN, "Expected better " + packageName
+ " but never showed up; reverting to system");
- final @ParseFlags int reparseFlags;
- final @ScanFlags int rescanFlags;
- if (FileUtils.contains(privilegedAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRIVILEGED;
- } else if (FileUtils.contains(systemAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM;
- } else if (FileUtils.contains(privilegedVendorAppDir, scanFile)
- || FileUtils.contains(privilegedOdmAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_VENDOR
- | SCAN_AS_PRIVILEGED;
- } else if (FileUtils.contains(vendorAppDir, scanFile)
- || FileUtils.contains(odmAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_VENDOR;
- } else if (FileUtils.contains(oemAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_OEM;
- } else if (FileUtils.contains(privilegedProductAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRODUCT
- | SCAN_AS_PRIVILEGED;
- } else if (FileUtils.contains(productAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRODUCT;
- } else if (FileUtils.contains(privilegedSystemExtAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_SYSTEM_EXT
- | SCAN_AS_PRIVILEGED;
- } else if (FileUtils.contains(systemExtAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_SYSTEM_EXT;
- } else {
+ @ParseFlags int reparseFlags = 0;
+ @ScanFlags int rescanFlags = 0;
+ for (int i1 = 0, size = SYSTEM_PARTITIONS.size(); i1 < size; i1++) {
+ SystemPartition partition = SYSTEM_PARTITIONS.get(i1);
+ if (partition.containsPrivApp(scanFile)) {
+ reparseFlags = systemParseFlags;
+ rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
+ | partition.scanFlag;
+ break;
+ }
+ if (partition.containsApp(scanFile)) {
+ reparseFlags = systemParseFlags;
+ rescanFlags = systemScanFlags | partition.scanFlag;
+ break;
+ }
+ }
+ if (rescanFlags == 0) {
Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
continue;
}
-
mSettings.enableSystemPackageLPw(packageName);
try {
@@ -3273,7 +2935,7 @@ public class PackageManagerService extends IPackageManager.Stub
// Resolve protected action filters. Only the setup wizard is allowed to
// have a high priority filter for these actions.
- mSetupWizardPackage = getSetupWizardPackageName();
+ mSetupWizardPackage = getSetupWizardPackageNameImpl();
mComponentResolver.fixProtectedFilterPriorities();
mSystemTextClassifierPackage = getSystemTextClassifierPackageName();
@@ -3284,6 +2946,8 @@ public class PackageManagerService extends IPackageManager.Stub
mContext.getString(R.string.config_deviceConfiguratorPackageName);
mAppPredictionServicePackage = getAppPredictionServicePackageName();
mIncidentReportApproverPackage = getIncidentReportApproverPackageName();
+ mTelephonyPackages = getTelephonyPackageNames();
+ mWifiPackage = mContext.getString(R.string.config_wifiPackage);
// Now that we know all of the shared libraries, update all clients to have
// the correct library paths.
@@ -3360,7 +3024,7 @@ public class PackageManagerService extends IPackageManager.Stub
List<String> deferPackages = reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL,
UserHandle.USER_SYSTEM, storageFlags, true /* migrateAppData */,
true /* onlyCoreApps */);
- mPrepareAppDataFuture = SystemServerInitThreadPool.get().submit(() -> {
+ mPrepareAppDataFuture = SystemServerInitThreadPool.submit(() -> {
TimingsTraceLog traceLog = new TimingsTraceLog("SystemServerTimingAsync",
Trace.TRACE_TAG_PACKAGE_MANAGER);
traceLog.traceBegin("AppDataFixup");
@@ -4404,7 +4068,7 @@ public class PackageManagerService extends IPackageManager.Stub
private PackageInfo getPackageInfoInternal(String packageName, long versionCode,
int flags, int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
- flags = updateFlagsForPackage(flags, userId, packageName);
+ flags = updateFlagsForPackage(flags, userId);
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */, "get package info");
@@ -4706,7 +4370,7 @@ public class PackageManagerService extends IPackageManager.Stub
public int getPackageUid(String packageName, int flags, int userId) {
if (!mUserManager.exists(userId)) return -1;
final int callingUid = Binder.getCallingUid();
- flags = updateFlagsForPackage(flags, userId, packageName);
+ flags = updateFlagsForPackage(flags, userId);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "getPackageUid");
@@ -4736,7 +4400,7 @@ public class PackageManagerService extends IPackageManager.Stub
public int[] getPackageGids(String packageName, int flags, int userId) {
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
- flags = updateFlagsForPackage(flags, userId, packageName);
+ flags = updateFlagsForPackage(flags, userId);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "getPackageGids");
@@ -4818,7 +4482,7 @@ public class PackageManagerService extends IPackageManager.Stub
private ApplicationInfo getApplicationInfoInternal(String packageName, int flags,
int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
- flags = updateFlagsForApplication(flags, userId, packageName);
+ flags = updateFlagsForApplication(flags, userId);
if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) {
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
@@ -5105,7 +4769,7 @@ public class PackageManagerService extends IPackageManager.Stub
/**
* Update given flags when being used to request {@link PackageInfo}.
*/
- private int updateFlagsForPackage(int flags, int userId, Object cookie) {
+ private int updateFlagsForPackage(int flags, int userId) {
final boolean isCallerSystemUser = UserHandle.getCallingUserId() == UserHandle.USER_SYSTEM;
if ((flags & PackageManager.MATCH_ANY_USER) != 0) {
// require the permission to be held; the calling uid and given user id referring
@@ -5129,14 +4793,14 @@ public class PackageManagerService extends IPackageManager.Stub
/**
* Update given flags when being used to request {@link ApplicationInfo}.
*/
- private int updateFlagsForApplication(int flags, int userId, Object cookie) {
- return updateFlagsForPackage(flags, userId, cookie);
+ private int updateFlagsForApplication(int flags, int userId) {
+ return updateFlagsForPackage(flags, userId);
}
/**
* Update given flags when being used to request {@link ComponentInfo}.
*/
- private int updateFlagsForComponent(int flags, int userId, Object cookie) {
+ private int updateFlagsForComponent(int flags, int userId) {
return updateFlags(flags, userId);
}
@@ -5165,16 +4829,12 @@ public class PackageManagerService extends IPackageManager.Stub
* action and a {@code android.intent.category.BROWSABLE} category</li>
* </ul>
*/
- int updateFlagsForResolve(int flags, int userId, Intent intent, int callingUid) {
- return updateFlagsForResolve(flags, userId, intent, callingUid,
- false /*wantInstantApps*/, false /*onlyExposedExplicitly*/);
- }
- int updateFlagsForResolve(int flags, int userId, Intent intent, int callingUid,
- boolean wantInstantApps) {
- return updateFlagsForResolve(flags, userId, intent, callingUid,
+ int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps) {
+ return updateFlagsForResolve(flags, userId, callingUid,
wantInstantApps, false /*onlyExposedExplicitly*/);
}
- int updateFlagsForResolve(int flags, int userId, Intent intent, int callingUid,
+
+ int updateFlagsForResolve(int flags, int userId, int callingUid,
boolean wantInstantApps, boolean onlyExposedExplicitly) {
// Safe mode means we shouldn't match any third-party components
if (mSafeMode) {
@@ -5197,7 +4857,7 @@ public class PackageManagerService extends IPackageManager.Stub
flags &= ~PackageManager.MATCH_INSTANT;
}
}
- return updateFlagsForComponent(flags, userId, intent /*cookie*/);
+ return updateFlagsForComponent(flags, userId);
}
@Override
@@ -5214,7 +4874,7 @@ public class PackageManagerService extends IPackageManager.Stub
private ActivityInfo getActivityInfoInternal(ComponentName component, int flags,
int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
- flags = updateFlagsForComponent(flags, userId, component);
+ flags = updateFlagsForComponent(flags, userId);
if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) {
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
@@ -5295,7 +4955,7 @@ public class PackageManagerService extends IPackageManager.Stub
public ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) {
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
- flags = updateFlagsForComponent(flags, userId, component);
+ flags = updateFlagsForComponent(flags, userId);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */, "get receiver info");
synchronized (mLock) {
@@ -5325,7 +4985,7 @@ public class PackageManagerService extends IPackageManager.Stub
return null;
}
- flags = updateFlagsForPackage(flags, userId, null);
+ flags = updateFlagsForPackage(flags, userId);
final boolean canSeeStaticLibraries =
mContext.checkCallingOrSelfPermission(INSTALL_PACKAGES)
@@ -5510,7 +5170,7 @@ public class PackageManagerService extends IPackageManager.Stub
public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
- flags = updateFlagsForComponent(flags, userId, component);
+ flags = updateFlagsForComponent(flags, userId);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */, "get service info");
synchronized (mLock) {
@@ -5535,7 +5195,7 @@ public class PackageManagerService extends IPackageManager.Stub
public ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) {
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
- flags = updateFlagsForComponent(flags, userId, component);
+ flags = updateFlagsForComponent(flags, userId);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /* requireFullPermission */, false /* checkShell */, "get provider info");
synchronized (mLock) {
@@ -6235,7 +5895,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
- flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart);
+ flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");
@@ -6265,7 +5925,7 @@ public class PackageManagerService extends IPackageManager.Stub
intent = updateIntentForResolve(intent);
final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
final int flags = updateFlagsForResolve(
- 0, userId, intent, callingUid, false /*includeInstantApps*/);
+ 0, userId, callingUid, false /*includeInstantApps*/);
final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
userId);
synchronized (mLock) {
@@ -6579,7 +6239,7 @@ public class PackageManagerService extends IPackageManager.Stub
android.provider.Settings.Global.getInt(mContext.getContentResolver(),
android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1;
flags = updateFlagsForResolve(
- flags, userId, intent, callingUid, false /*includeInstantApps*/);
+ flags, userId, callingUid, false /*includeInstantApps*/);
intent = updateIntentForResolve(intent);
// writer
synchronized (mLock) {
@@ -6787,7 +6447,7 @@ public class PackageManagerService extends IPackageManager.Stub
final int callingUid = Binder.getCallingUid();
final UserInfo parent = getProfileParent(sourceUserId);
synchronized (mLock) {
- int flags = updateFlagsForResolve(0, parent.id, intent, callingUid,
+ int flags = updateFlagsForResolve(0, parent.id, callingUid,
false /*includeInstantApps*/);
CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr(
intent, resolvedType, flags, sourceUserId, parent.id);
@@ -6873,7 +6533,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart,
+ flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
comp != null || pkgName != null /*onlyExposedExplicitly*/);
if (comp != null) {
final List<ResolveInfo> list = new ArrayList<>(1);
@@ -7230,7 +6890,7 @@ public class PackageManagerService extends IPackageManager.Stub
* @param intent
* @return A filtered list of resolved activities.
*/
- private List<ResolveInfo> applyPostResolutionFilter(List<ResolveInfo> resolveInfos,
+ private List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos,
String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
boolean resolveForStart, int userId, Intent intent) {
final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId);
@@ -7640,8 +7300,7 @@ public class PackageManagerService extends IPackageManager.Stub
String resolvedType, int flags, int userId) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
- flags = updateFlagsForResolve(flags, userId, intent, callingUid,
- false /*includeInstantApps*/);
+ flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/,
"query intent activity options");
@@ -7827,8 +7486,7 @@ public class PackageManagerService extends IPackageManager.Stub
false /*requireFullPermission*/, false /*checkShell*/,
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
- flags = updateFlagsForResolve(flags, userId, intent, callingUid,
- false /*includeInstantApps*/);
+ flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -7887,6 +7545,9 @@ public class PackageManagerService extends IPackageManager.Stub
if (pkgName == null) {
final List<ResolveInfo> result =
mComponentResolver.queryReceivers(intent, resolvedType, flags, userId);
+ if (result == null) {
+ return Collections.emptyList();
+ }
return applyPostResolutionFilter(
result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId,
intent);
@@ -7895,6 +7556,9 @@ public class PackageManagerService extends IPackageManager.Stub
if (pkg != null) {
final List<ResolveInfo> result = mComponentResolver.queryReceivers(
intent, resolvedType, flags, pkg.receivers, userId);
+ if (result == null) {
+ return Collections.emptyList();
+ }
return applyPostResolutionFilter(
result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId,
intent);
@@ -7912,8 +7576,7 @@ public class PackageManagerService extends IPackageManager.Stub
private ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags,
int userId, int callingUid) {
if (!mUserManager.exists(userId)) return null;
- flags = updateFlagsForResolve(
- flags, userId, intent, callingUid, false /*includeInstantApps*/);
+ flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
List<ResolveInfo> query = queryIntentServicesInternal(
intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/);
if (query != null) {
@@ -7942,7 +7605,7 @@ public class PackageManagerService extends IPackageManager.Stub
false /*requireFullPermission*/, false /*checkShell*/,
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
- flags = updateFlagsForResolve(flags, userId, intent, callingUid, includeInstantApps);
+ flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -7989,15 +7652,25 @@ public class PackageManagerService extends IPackageManager.Stub
synchronized (mLock) {
String pkgName = intent.getPackage();
if (pkgName == null) {
+ final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(intent,
+ resolvedType, flags, userId);
+ if (resolveInfos == null) {
+ return Collections.emptyList();
+ }
return applyPostServiceResolutionFilter(
- mComponentResolver.queryServices(intent, resolvedType, flags, userId),
+ resolveInfos,
instantAppPkgName);
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
+ final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(intent,
+ resolvedType, flags, pkg.services,
+ userId);
+ if (resolveInfos == null) {
+ return Collections.emptyList();
+ }
return applyPostServiceResolutionFilter(
- mComponentResolver.queryServices(intent, resolvedType, flags, pkg.services,
- userId),
+ resolveInfos,
instantAppPkgName);
}
return Collections.emptyList();
@@ -8059,8 +7732,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (!mUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
final String instantAppPkgName = getInstantAppPackageName(callingUid);
- flags = updateFlagsForResolve(flags, userId, intent, callingUid,
- false /*includeInstantApps*/);
+ flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -8107,15 +7779,25 @@ public class PackageManagerService extends IPackageManager.Stub
synchronized (mLock) {
String pkgName = intent.getPackage();
if (pkgName == null) {
+ final List<ResolveInfo> resolveInfos = mComponentResolver.queryProviders(intent,
+ resolvedType, flags, userId);
+ if (resolveInfos == null) {
+ return Collections.emptyList();
+ }
return applyPostContentProviderResolutionFilter(
- mComponentResolver.queryProviders(intent, resolvedType, flags, userId),
+ resolveInfos,
instantAppPkgName);
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
+ final List<ResolveInfo> resolveInfos = mComponentResolver.queryProviders(intent,
+ resolvedType, flags,
+ pkg.providers, userId);
+ if (resolveInfos == null) {
+ return Collections.emptyList();
+ }
return applyPostContentProviderResolutionFilter(
- mComponentResolver.queryProviders(intent, resolvedType, flags,
- pkg.providers, userId),
+ resolveInfos,
instantAppPkgName);
}
return Collections.emptyList();
@@ -8172,7 +7854,7 @@ public class PackageManagerService extends IPackageManager.Stub
return ParceledListSlice.emptyList();
}
if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList();
- flags = updateFlagsForPackage(flags, userId, null);
+ flags = updateFlagsForPackage(flags, userId);
final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
final boolean listApex = (flags & MATCH_APEX) != 0;
final boolean listFactory = (flags & MATCH_FACTORY_ONLY) != 0;
@@ -8272,7 +7954,7 @@ public class PackageManagerService extends IPackageManager.Stub
public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
String[] permissions, int flags, int userId) {
if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList();
- flags = updateFlagsForPackage(flags, userId, permissions);
+ flags = updateFlagsForPackage(flags, userId);
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"get packages holding permissions");
@@ -8314,7 +7996,7 @@ public class PackageManagerService extends IPackageManager.Stub
return Collections.emptyList();
}
if (!mUserManager.exists(userId)) return Collections.emptyList();
- flags = updateFlagsForApplication(flags, userId, null);
+ flags = updateFlagsForApplication(flags, userId);
final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
mPermissionManager.enforceCrossUserPermission(
@@ -8406,15 +8088,20 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public boolean isInstantApp(String packageName, int userId) {
- mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ final int callingUid = Binder.getCallingUid();
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, false /* checkShell */,
"isInstantApp");
+
+ return isInstantAppInternal(packageName, userId, callingUid);
+ }
+
+ private boolean isInstantAppInternal(String packageName, @UserIdInt int userId,
+ int callingUid) {
if (HIDE_EPHEMERAL_APIS) {
return false;
}
-
synchronized (mLock) {
- int callingUid = Binder.getCallingUid();
if (Process.isIsolated(callingUid)) {
callingUid = mIsolatedOwners.get(callingUid);
}
@@ -8544,7 +8231,7 @@ public class PackageManagerService extends IPackageManager.Stub
private ProviderInfo resolveContentProviderInternal(String name, int flags, int userId) {
if (!mUserManager.exists(userId)) return null;
- flags = updateFlagsForComponent(flags, userId, name);
+ flags = updateFlagsForComponent(flags, userId);
final int callingUid = Binder.getCallingUid();
final ProviderInfo providerInfo = mComponentResolver.queryProvider(name, flags, userId);
if (providerInfo == null) {
@@ -8583,7 +8270,7 @@ public class PackageManagerService extends IPackageManager.Stub
final int userId = processName != null ? UserHandle.getUserId(uid)
: UserHandle.getCallingUserId();
if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList();
- flags = updateFlagsForComponent(flags, userId, processName);
+ flags = updateFlagsForComponent(flags, userId);
ArrayList<ProviderInfo> finalList = null;
final List<ProviderInfo> matchList =
mComponentResolver.queryProviders(processName, metaDataKey, uid, flags, userId);
@@ -8690,7 +8377,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
- mParallelPackageParserCallback)) {
+ mPackageParserCallback)) {
// Submit files for parsing in parallel
int fileCount = 0;
for (File file : files) {
@@ -10906,6 +10593,50 @@ public class PackageManagerService extends IPackageManager.Stub
return changedAbiCodePath;
}
+ /**
+ * Sets the enabled state of components configured through {@link SystemConfig}.
+ * This modifies the {@link PackageSetting} object.
+ **/
+ static void configurePackageComponents(PackageParser.Package pkg) {
+ final ArrayMap<String, Boolean> componentsEnabledStates = SystemConfig.getInstance()
+ .getComponentsEnabledStates(pkg.packageName);
+ if (componentsEnabledStates == null) {
+ return;
+ }
+
+ for (int i = pkg.activities.size() - 1; i >= 0; i--) {
+ final PackageParser.Activity component = pkg.activities.get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.className);
+ if (enabled != null) {
+ component.info.enabled = enabled;
+ }
+ }
+
+ for (int i = pkg.receivers.size() - 1; i >= 0; i--) {
+ final PackageParser.Activity component = pkg.receivers.get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.className);
+ if (enabled != null) {
+ component.info.enabled = enabled;
+ }
+ }
+
+ for (int i = pkg.providers.size() - 1; i >= 0; i--) {
+ final PackageParser.Provider component = pkg.providers.get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.className);
+ if (enabled != null) {
+ component.info.enabled = enabled;
+ }
+ }
+
+ for (int i = pkg.services.size() - 1; i >= 0; i--) {
+ final PackageParser.Service component = pkg.services.get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.className);
+ if (enabled != null) {
+ component.info.enabled = enabled;
+ }
+ }
+ }
+
/**
* Just scans the package without any side effects.
@@ -11073,6 +10804,10 @@ public class PackageManagerService extends IPackageManager.Stub
pkg.applicationInfo.initForUser(UserHandle.USER_SYSTEM);
}
+ if (pkg.isSystem()) {
+ configurePackageComponents(pkg);
+ }
+
final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
@@ -11195,7 +10930,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
if (isSystemApp(pkg)) {
- pkgSetting.isOrphaned = true;
+ pkgSetting.setIsOrphaned(true);
}
// Take care of first install / last update times.
@@ -12678,7 +12413,7 @@ public class PackageManagerService extends IPackageManager.Stub
int userId) {
final PackageRemovedInfo info = new PackageRemovedInfo(this);
info.removedPackage = packageName;
- info.installerPackageName = pkgSetting.installerPackageName;
+ info.installerPackageName = pkgSetting.installSource.installerPackageName;
info.removedUsers = new int[] {userId};
info.broadcastUsers = new int[] {userId};
info.uid = UserHandle.getUid(userId, pkgSetting.appId);
@@ -12696,14 +12431,10 @@ public class PackageManagerService extends IPackageManager.Stub
}
private void sendPackagesSuspendedForUser(String[] pkgList, int[] uidList, int userId,
- boolean suspended, PersistableBundle launcherExtras) {
+ boolean suspended) {
final Bundle extras = new Bundle(3);
extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
- if (launcherExtras != null) {
- extras.putBundle(Intent.EXTRA_LAUNCHER_EXTRAS,
- new Bundle(launcherExtras.deepCopy()));
- }
sendPackageBroadcast(
suspended ? Intent.ACTION_PACKAGES_SUSPENDED
: Intent.ACTION_PACKAGES_UNSUSPENDED,
@@ -12968,8 +12699,6 @@ public class PackageManagerService extends IPackageManager.Stub
if (ownerUid == callingUid) {
return;
}
- throw new UnsupportedOperationException("Cannot suspend/unsuspend packages. User "
- + userId + " has an active DO or PO");
}
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
@@ -12977,6 +12706,7 @@ public class PackageManagerService extends IPackageManager.Stub
final int packageUid = getPackageUid(callingPackage, 0, userId);
final boolean allowedPackageUid = packageUid == callingUid;
+ // TODO(b/139383163): remove special casing for shell and enforce INTERACT_ACROSS_USERS_FULL
final boolean allowedShell = callingUid == SHELL_UID
&& UserHandle.isSameApp(packageUid, callingUid);
@@ -13027,20 +12757,27 @@ public class PackageManagerService extends IPackageManager.Stub
unactionedPackages.add(packageName);
continue;
}
+ boolean packageUnsuspended;
synchronized (mLock) {
- pkgSetting.setSuspended(suspended, callingPackage, dialogInfo, appExtras,
- launcherExtras, userId);
+ if (suspended) {
+ pkgSetting.addOrUpdateSuspension(callingPackage, dialogInfo, appExtras,
+ launcherExtras, userId);
+ } else {
+ pkgSetting.removeSuspension(callingPackage, userId);
+ }
+ packageUnsuspended = !suspended && !pkgSetting.getSuspended(userId);
+ }
+ if (suspended || packageUnsuspended) {
+ changedPackagesList.add(packageName);
+ changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
}
- changedPackagesList.add(packageName);
- changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
}
if (!changedPackagesList.isEmpty()) {
final String[] changedPackages = changedPackagesList.toArray(
new String[changedPackagesList.size()]);
- sendPackagesSuspendedForUser(
- changedPackages, changedUids.toArray(), userId, suspended, launcherExtras);
- sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, appExtras, userId);
+ sendPackagesSuspendedForUser(changedPackages, changedUids.toArray(), userId, suspended);
+ sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, userId);
synchronized (mLock) {
scheduleWritePackageRestrictionsLocked(userId);
}
@@ -13049,38 +12786,40 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
- public PersistableBundle getSuspendedPackageAppExtras(String packageName, int userId) {
+ public Bundle getSuspendedPackageAppExtras(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
if (getPackageUid(packageName, 0, userId) != callingUid) {
throw new SecurityException("Calling package " + packageName
+ " does not belong to calling uid " + callingUid);
}
+ return getSuspendedPackageAppExtrasInternal(packageName, userId);
+ }
+
+ private Bundle getSuspendedPackageAppExtrasInternal(String packageName, int userId) {
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
- if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
+ if (ps == null) {
throw new IllegalArgumentException("Unknown target package: " + packageName);
}
- final PackageUserState packageUserState = ps.readUserState(userId);
- if (packageUserState.suspended) {
- return packageUserState.suspendedAppExtras;
+ final PackageUserState pus = ps.readUserState(userId);
+ final Bundle allExtras = new Bundle();
+ if (pus.suspended) {
+ for (int i = 0; i < pus.suspendParams.size(); i++) {
+ final PackageUserState.SuspendParams params = pus.suspendParams.valueAt(i);
+ if (params != null && params.appExtras != null) {
+ allExtras.putAll(params.appExtras);
+ }
+ }
}
- return null;
+ return (allExtras.size() > 0) ? allExtras : null;
}
}
private void sendMyPackageSuspendedOrUnsuspended(String[] affectedPackages, boolean suspended,
- PersistableBundle appExtras, int userId) {
- final String action;
- final Bundle intentExtras = new Bundle();
- if (suspended) {
- action = Intent.ACTION_MY_PACKAGE_SUSPENDED;
- if (appExtras != null) {
- final Bundle bundledAppExtras = new Bundle(appExtras.deepCopy());
- intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, bundledAppExtras);
- }
- } else {
- action = Intent.ACTION_MY_PACKAGE_UNSUSPENDED;
- }
+ int userId) {
+ final String action = suspended
+ ? Intent.ACTION_MY_PACKAGE_SUSPENDED
+ : Intent.ACTION_MY_PACKAGE_UNSUSPENDED;
mHandler.post(() -> {
try {
final IActivityManager am = ActivityManager.getService();
@@ -13091,6 +12830,16 @@ public class PackageManagerService extends IPackageManager.Stub
}
final int[] targetUserIds = new int[] {userId};
for (String packageName : affectedPackages) {
+ final Bundle appExtras = suspended
+ ? getSuspendedPackageAppExtrasInternal(packageName, userId)
+ : null;
+ final Bundle intentExtras;
+ if (appExtras != null) {
+ intentExtras = new Bundle(1);
+ intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, appExtras);
+ } else {
+ intentExtras = null;
+ }
doSendBroadcast(am, action, null, intentExtras,
Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
targetUserIds, false);
@@ -13123,57 +12872,32 @@ public class PackageManagerService extends IPackageManager.Stub
* <p><b>Should not be used on a frequent code path</b> as it flushes state to disk
* synchronously
*
- * @param packageName The package holding {@link Manifest.permission#SUSPEND_APPS} permission
- * @param affectedUser The user for which the changes are taking place.
+ * @param suspendingPackage The suspending package
+ * @param userId The user for which the changes are taking place.
*/
- void unsuspendForSuspendingPackage(final String packageName, int affectedUser) {
- final int[] userIds = (affectedUser == UserHandle.USER_ALL) ? mUserManager.getUserIds()
- : new int[] {affectedUser};
- for (int userId : userIds) {
- unsuspendForSuspendingPackages(packageName::equals, userId);
- }
- }
-
- /**
- * Immediately unsuspends any packages in the given users not suspended by the platform or root.
- * To be called when a profile owner or a device owner is added.
- *
- * <p><b>Should not be used on a frequent code path</b> as it flushes state to disk
- * synchronously
- *
- * @param userIds The users for which to unsuspend packages
- */
- void unsuspendForNonSystemSuspendingPackages(ArraySet<Integer> userIds) {
- final int sz = userIds.size();
- for (int i = 0; i < sz; i++) {
- unsuspendForSuspendingPackages(
- (suspendingPackage) -> !PLATFORM_PACKAGE_NAME.equals(suspendingPackage),
- userIds.valueAt(i));
- }
- }
-
- private void unsuspendForSuspendingPackages(Predicate<String> packagePredicate, int userId) {
- final List<String> affectedPackages = new ArrayList<>();
- final IntArray affectedUids = new IntArray();
+ private void unsuspendForSuspendingPackage(String suspendingPackage, int userId) {
+ final List<String> unsuspendedPackages = new ArrayList<>();
+ final IntArray unsuspendedUids = new IntArray();
synchronized (mLock) {
for (PackageSetting ps : mSettings.mPackages.values()) {
final PackageUserState pus = ps.readUserState(userId);
- if (pus.suspended && packagePredicate.test(pus.suspendingPackage)) {
- ps.setSuspended(false, null, null, null, null, userId);
- affectedPackages.add(ps.name);
- affectedUids.add(UserHandle.getUid(userId, ps.getAppId()));
+ if (pus.suspended) {
+ ps.removeSuspension(suspendingPackage, userId);
+ if (!ps.getSuspended(userId)) {
+ unsuspendedPackages.add(ps.name);
+ unsuspendedUids.add(UserHandle.getUid(userId, ps.getAppId()));
+ }
}
}
}
- if (!affectedPackages.isEmpty()) {
- final String[] packageArray = affectedPackages.toArray(
- new String[affectedPackages.size()]);
- sendMyPackageSuspendedOrUnsuspended(packageArray, false, null, userId);
- sendPackagesSuspendedForUser(
- packageArray, affectedUids.toArray(), userId, false, null);
- // Write package restrictions immediately to avoid an inconsistent state.
- mSettings.writePackageRestrictionsLPr(userId);
+ if (!unsuspendedPackages.isEmpty()) {
+ final String[] packageArray = unsuspendedPackages.toArray(
+ new String[unsuspendedPackages.size()]);
+ sendMyPackageSuspendedOrUnsuspended(packageArray, false, userId);
+ sendPackagesSuspendedForUser(packageArray, unsuspendedUids.toArray(), userId, false);
}
+ // Write package restrictions immediately to avoid an inconsistent state.
+ mSettings.writePackageRestrictionsLPr(userId);
}
@Override
@@ -13472,9 +13196,11 @@ public class PackageManagerService extends IPackageManager.Stub
* @return verification timeout in milliseconds
*/
private long getVerificationTimeout() {
- return android.provider.Settings.Global.getLong(mContext.getContentResolver(),
- android.provider.Settings.Global.PACKAGE_VERIFIER_TIMEOUT,
- DEFAULT_VERIFICATION_TIMEOUT);
+ long timeout = Global.getLong(mContext.getContentResolver(),
+ Global.PACKAGE_VERIFIER_TIMEOUT, DEFAULT_VERIFICATION_TIMEOUT);
+ // The setting can be used to increase the timeout but not decrease it, since that is
+ // equivalent to disabling the verifier.
+ return Math.max(timeout, DEFAULT_VERIFICATION_TIMEOUT);
}
/**
@@ -13505,22 +13231,18 @@ public class PackageManagerService extends IPackageManager.Stub
return false;
}
- boolean ensureVerifyAppsEnabled = isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS);
-
// Check if installing from ADB
if ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0) {
// Do not run verification in a test harness environment
if (ActivityManager.isRunningInTestHarness()) {
return false;
}
- if (ensureVerifyAppsEnabled) {
+ if (isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS)) {
return true;
}
// Check if the developer does not want package verification for ADB installs
- if (android.provider.Settings.Global.getInt(mContext.getContentResolver(),
- android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) == 0) {
- return false;
- }
+ return Global.getInt(mContext.getContentResolver(),
+ Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) != 0;
} else {
// only when not installed from ADB, skip verification for instant apps when
// the installer and verifier are the same.
@@ -13538,14 +13260,8 @@ public class PackageManagerService extends IPackageManager.Stub
} catch (SecurityException ignore) { }
}
}
- }
-
- if (ensureVerifyAppsEnabled) {
return true;
}
-
- return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
- android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE, 1) == 1;
}
@Override
@@ -13719,9 +13435,11 @@ public class PackageManagerService extends IPackageManager.Stub
// Verify: if target already has an installer package, it must
// be signed with the same cert as the caller.
- if (targetPackageSetting.installerPackageName != null) {
+ String targetInstallerPackageName =
+ targetPackageSetting.installSource.installerPackageName;
+ if (targetInstallerPackageName != null) {
PackageSetting setting = mSettings.mPackages.get(
- targetPackageSetting.installerPackageName);
+ targetInstallerPackageName);
// If the currently set package isn't valid, then it's always
// okay to change it.
if (setting != null) {
@@ -13730,13 +13448,13 @@ public class PackageManagerService extends IPackageManager.Stub
!= PackageManager.SIGNATURE_MATCH) {
throw new SecurityException(
"Caller does not have same cert as old installer package "
- + targetPackageSetting.installerPackageName);
+ + targetInstallerPackageName);
}
}
}
// Okay!
- targetPackageSetting.installerPackageName = installerPackageName;
+ targetPackageSetting.setInstallerPackageName(installerPackageName);
if (installerPackageName != null) {
mSettings.mInstallerPackages.add(installerPackageName);
}
@@ -13761,7 +13479,7 @@ public class PackageManagerService extends IPackageManager.Stub
ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) {
throw new IllegalArgumentException("Unknown target package " + packageName);
}
- if (!Objects.equals(callerPackageName, ps.installerPackageName)) {
+ if (!Objects.equals(callerPackageName, ps.installSource.installerPackageName)) {
throw new IllegalArgumentException("Calling package " + callerPackageName
+ " is not installer for " + packageName);
}
@@ -14221,7 +13939,7 @@ public class PackageManagerService extends IPackageManager.Stub
final MoveInfo move;
final IPackageInstallObserver2 observer;
int installFlags;
- final String installerPackageName;
+ @NonNull final InstallSource installSource;
final String volumeUuid;
private boolean mVerificationCompleted;
private boolean mEnableRollbackCompleted;
@@ -14238,17 +13956,17 @@ public class PackageManagerService extends IPackageManager.Stub
final long requiredInstalledVersionCode;
InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
- int installFlags, String installerPackageName, String volumeUuid,
+ int installFlags, InstallSource installSource, String volumeUuid,
VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride,
String[] grantedPermissions, List<String> whitelistedRestrictedPermissions,
- PackageParser.SigningDetails signingDetails, int installReason,
+ SigningDetails signingDetails, int installReason,
long requiredInstalledVersionCode) {
super(user);
this.origin = origin;
this.move = move;
this.observer = observer;
this.installFlags = installFlags;
- this.installerPackageName = installerPackageName;
+ this.installSource = Preconditions.checkNotNull(installSource);
this.volumeUuid = volumeUuid;
this.verificationInfo = verificationInfo;
this.packageAbiOverride = packageAbiOverride;
@@ -14274,12 +13992,13 @@ public class PackageManagerService extends IPackageManager.Stub
activeInstallSession.getInstallerUid());
origin = OriginInfo.fromStagedFile(activeInstallSession.getStagedDir());
move = null;
- installReason = fixUpInstallReason(activeInstallSession.getInstallerPackageName(),
+ installReason = fixUpInstallReason(
+ activeInstallSession.getInstallSource().installerPackageName,
activeInstallSession.getInstallerUid(),
activeInstallSession.getSessionParams().installReason);
observer = activeInstallSession.getObserver();
installFlags = activeInstallSession.getSessionParams().installFlags;
- installerPackageName = activeInstallSession.getInstallerPackageName();
+ installSource = activeInstallSession.getInstallSource();
volumeUuid = activeInstallSession.getSessionParams().volumeUuid;
packageAbiOverride = activeInstallSession.getSessionParams().abiOverride;
grantedRuntimePermissions = activeInstallSession.getSessionParams()
@@ -14535,7 +14254,7 @@ public class PackageManagerService extends IPackageManager.Stub
verification.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);
verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE,
- installerPackageName);
+ installSource.installerPackageName);
verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALL_FLAGS,
installFlags);
@@ -14795,7 +14514,7 @@ public class PackageManagerService extends IPackageManager.Stub
final IPackageInstallObserver2 observer;
// Always refers to PackageManager flags only
final int installFlags;
- final String installerPackageName;
+ @NonNull final InstallSource installSource;
final String volumeUuid;
final UserHandle user;
final String abiOverride;
@@ -14814,7 +14533,7 @@ public class PackageManagerService extends IPackageManager.Stub
/* nullable */ String[] instructionSets;
InstallArgs(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
- int installFlags, String installerPackageName, String volumeUuid,
+ int installFlags, InstallSource installSource, String volumeUuid,
UserHandle user, String[] instructionSets,
String abiOverride, String[] installGrantPermissions,
List<String> whitelistedRestrictedPermissions,
@@ -14825,7 +14544,7 @@ public class PackageManagerService extends IPackageManager.Stub
this.move = move;
this.installFlags = installFlags;
this.observer = observer;
- this.installerPackageName = installerPackageName;
+ this.installSource = Preconditions.checkNotNull(installSource);
this.volumeUuid = volumeUuid;
this.user = user;
this.instructionSets = instructionSets;
@@ -14839,6 +14558,16 @@ public class PackageManagerService extends IPackageManager.Stub
this.mMultiPackageInstallParams = multiPackageInstallParams;
}
+ /** New install */
+ InstallArgs(InstallParams params) {
+ this(params.origin, params.move, params.observer, params.installFlags,
+ params.installSource, params.volumeUuid,
+ params.getUser(), null /*instructionSets*/, params.packageAbiOverride,
+ params.grantedRuntimePermissions, params.whitelistedRestrictedPermissions,
+ params.traceMethod, params.traceCookie, params.signingDetails,
+ params.installReason, params.mParentInstallParams);
+ }
+
abstract int copyApk();
abstract int doPreInstall(int status);
@@ -14919,18 +14648,14 @@ public class PackageManagerService extends IPackageManager.Stub
/** New install */
FileInstallArgs(InstallParams params) {
- super(params.origin, params.move, params.observer, params.installFlags,
- params.installerPackageName, params.volumeUuid,
- params.getUser(), null /*instructionSets*/, params.packageAbiOverride,
- params.grantedRuntimePermissions, params.whitelistedRestrictedPermissions,
- params.traceMethod, params.traceCookie, params.signingDetails,
- params.installReason, params.mParentInstallParams);
+ super(params);
}
/** Existing install */
FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) {
- super(OriginInfo.fromNothing(), null, null, 0, null, null, null, instructionSets,
- null, null, null, null, 0, PackageParser.SigningDetails.UNKNOWN,
+ super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY,
+ null, null, instructionSets, null, null, null, null, 0,
+ PackageParser.SigningDetails.UNKNOWN,
PackageManager.INSTALL_REASON_UNKNOWN, null /* parent */);
this.codeFile = (codePath != null) ? new File(codePath) : null;
this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
@@ -15108,12 +14833,7 @@ public class PackageManagerService extends IPackageManager.Stub
/** New install */
MoveInstallArgs(InstallParams params) {
- super(params.origin, params.move, params.observer, params.installFlags,
- params.installerPackageName, params.volumeUuid,
- params.getUser(), null /* instruction sets */, params.packageAbiOverride,
- params.grantedRuntimePermissions, params.whitelistedRestrictedPermissions,
- params.traceMethod, params.traceCookie, params.signingDetails,
- params.installReason, params.mParentInstallParams);
+ super(params);
}
int copyApk() {
@@ -15372,53 +15092,40 @@ public class PackageManagerService extends IPackageManager.Stub
return disabled;
}
- @GuardedBy("mLock")
- private void setInstallerPackageNameLPw(PackageParser.Package pkg,
- String installerPackageName) {
- // Enable the parent package
- mSettings.setInstallerPackageName(pkg.packageName, installerPackageName);
- // Enable the child packages
- final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
- for (int i = 0; i < childCount; i++) {
- PackageParser.Package childPkg = pkg.childPackages.get(i);
- mSettings.setInstallerPackageName(childPkg.packageName, installerPackageName);
- }
- }
-
- private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
- int[] allUsers, PackageInstalledInfo res, UserHandle user, int installReason) {
+ private void updateSettingsLI(PackageParser.Package newPackage,
+ InstallArgs installArgs, int[] allUsers, PackageInstalledInfo res) {
// Update the parent package setting
- updateSettingsInternalLI(newPackage, installerPackageName, allUsers, res.origUsers,
- res, user, installReason);
+ updateSettingsInternalLI(newPackage, installArgs, allUsers, res);
// Update the child packages setting
final int childCount = (newPackage.childPackages != null)
? newPackage.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPackage = newPackage.childPackages.get(i);
PackageInstalledInfo childRes = res.addedChildPackages.get(childPackage.packageName);
- updateSettingsInternalLI(childPackage, installerPackageName, allUsers,
- childRes.origUsers, childRes, user, installReason);
+ updateSettingsInternalLI(childPackage, installArgs, allUsers, childRes);
}
}
private void updateSettingsInternalLI(PackageParser.Package pkg,
- String installerPackageName, int[] allUsers, int[] installedForUsers,
- PackageInstalledInfo res, UserHandle user, int installReason) {
+ InstallArgs installArgs, int[] allUsers, PackageInstalledInfo res) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
final String pkgName = pkg.packageName;
+ final String installerPackageName = installArgs.installSource.installerPackageName;
+ final int[] installedForUsers = res.origUsers;
+ final int installReason = installArgs.installReason;
if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.codePath);
if (pkgName != null)
acquireUxPerfLock(BoostFramework.UXE_EVENT_PKG_INSTALL, pkgName, 0);
synchronized (mLock) {
// NOTE: This changes slightly to include UPDATE_PERMISSIONS_ALL regardless of the size of pkg.permissions
- mPermissionManager.updatePermissions(pkg.packageName, pkg);
+ mPermissionManager.updatePermissions(pkgName, pkg);
// For system-bundled packages, we assume that installing an upgraded version
// of the package implies that the user actually wants to run that new code,
// so we enable the package.
PackageSetting ps = mSettings.mPackages.get(pkgName);
- final int userId = user.getIdentifier();
+ final int userId = installArgs.user.getIdentifier();
if (ps != null) {
if (isSystemApp(pkg)) {
if (DEBUG_INSTALL) {
@@ -15454,6 +15161,9 @@ public class PackageManagerService extends IPackageManager.Stub
ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
}
+ ps.setInstallSource(installArgs.installSource);
+
+
// When replacing an existing package, preserve the original install reason for all
// users that had the package installed before.
final Set<Integer> previousUserIds = new ArraySet<>();
@@ -16097,8 +15807,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
commitReconciledScanResultLocked(reconciledPkg);
- updateSettingsLI(pkg, reconciledPkg.installArgs.installerPackageName, request.mAllUsers,
- res, reconciledPkg.installArgs.user, reconciledPkg.installArgs.installReason);
+ updateSettingsLI(pkg, reconciledPkg.installArgs, request.mAllUsers, res);
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
@@ -16108,8 +15817,7 @@ public class PackageManagerService extends IPackageManager.Stub
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPkg = pkg.childPackages.get(i);
- PackageInstalledInfo childRes = res.addedChildPackages.get(
- childPkg.packageName);
+ PackageInstalledInfo childRes = res.addedChildPackages.get(childPkg.packageName);
PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
if (childPs != null) {
childRes.newUsers = childPs.queryInstalledUsers(
@@ -16173,7 +15881,8 @@ public class PackageManagerService extends IPackageManager.Stub
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
request.installResult.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
- request.installResult.installerPackageName = request.args.installerPackageName;
+ request.installResult.installerPackageName =
+ request.args.installSource.installerPackageName;
final String packageName = prepareResult.packageToScan.packageName;
prepareResults.put(packageName, prepareResult);
@@ -16356,10 +16065,6 @@ public class PackageManagerService extends IPackageManager.Stub
* will be used to scan and reconcile the package.
*/
private static class PrepareResult {
- public final int installReason;
- public final String volumeUuid;
- public final String installerPackageName;
- public final UserHandle user;
public final boolean replace;
public final int scanFlags;
public final int parseFlags;
@@ -16368,24 +16073,16 @@ public class PackageManagerService extends IPackageManager.Stub
public final PackageParser.Package packageToScan;
public final boolean clearCodeCache;
public final boolean system;
- /* The original package name if it was changed during an update, otherwise {@code null}. */
- @Nullable
- public final String renamedPackage;
public final PackageFreezer freezer;
public final PackageSetting originalPs;
public final PackageSetting disabledPs;
public final PackageSetting[] childPackageSettings;
- private PrepareResult(int installReason, String volumeUuid,
- String installerPackageName, UserHandle user, boolean replace, int scanFlags,
+ private PrepareResult(boolean replace, int scanFlags,
int parseFlags, PackageParser.Package existingPackage,
PackageParser.Package packageToScan, boolean clearCodeCache, boolean system,
- String renamedPackage, PackageFreezer freezer, PackageSetting originalPs,
+ PackageFreezer freezer, PackageSetting originalPs,
PackageSetting disabledPs, PackageSetting[] childPackageSettings) {
- this.installReason = installReason;
- this.volumeUuid = volumeUuid;
- this.installerPackageName = installerPackageName;
- this.user = user;
this.replace = replace;
this.scanFlags = scanFlags;
this.parseFlags = parseFlags;
@@ -16393,7 +16090,6 @@ public class PackageManagerService extends IPackageManager.Stub
this.packageToScan = packageToScan;
this.clearCodeCache = clearCodeCache;
this.system = system;
- this.renamedPackage = renamedPackage;
this.freezer = freezer;
this.originalPs = originalPs;
this.disabledPs = disabledPs;
@@ -16433,8 +16129,6 @@ public class PackageManagerService extends IPackageManager.Stub
private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
throws PrepareFailure {
final int installFlags = args.installFlags;
- final String installerPackageName = args.installerPackageName;
- final String volumeUuid = args.volumeUuid;
final File tmpPackageFile = new File(args.getCodePath());
final boolean onExternal = args.volumeUuid != null;
final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
@@ -16534,7 +16228,8 @@ public class PackageManagerService extends IPackageManager.Stub
if ((mPackages.containsKey(childPkg.packageName))) {
childRes.removedInfo = new PackageRemovedInfo(this);
childRes.removedInfo.removedPackage = childPkg.packageName;
- childRes.removedInfo.installerPackageName = childPs.installerPackageName;
+ childRes.removedInfo.installerPackageName =
+ childPs.installSource.installerPackageName;
}
if (res.addedChildPackages == null) {
res.addedChildPackages = new ArrayMap<>();
@@ -16864,14 +16559,12 @@ public class PackageManagerService extends IPackageManager.Stub
final PackageParser.Package existingPackage;
String renamedPackage = null;
boolean sysPkg = false;
- String targetVolumeUuid = volumeUuid;
int targetScanFlags = scanFlags;
int targetParseFlags = parseFlags;
final PackageSetting ps;
final PackageSetting disabledPs;
final PackageSetting[] childPackages;
if (replace) {
- targetVolumeUuid = null;
if (pkg.applicationInfo.isStaticSharedLibrary()) {
// Static libs have a synthetic package name containing the version
// and cannot be updated as an update would get a new package name,
@@ -16989,7 +16682,7 @@ public class PackageManagerService extends IPackageManager.Stub
res.removedInfo = new PackageRemovedInfo(this);
res.removedInfo.uid = oldPackage.applicationInfo.uid;
res.removedInfo.removedPackage = oldPackage.packageName;
- res.removedInfo.installerPackageName = ps.installerPackageName;
+ res.removedInfo.installerPackageName = ps.installSource.installerPackageName;
res.removedInfo.isStaticSharedLib = pkg.staticSharedLibName != null;
res.removedInfo.isUpdate = true;
res.removedInfo.origUsers = installedUsers;
@@ -17012,7 +16705,7 @@ public class PackageManagerService extends IPackageManager.Stub
childRes.removedInfo.removedPackage = childPkg.packageName;
if (childPs != null) {
childRes.removedInfo.installerPackageName =
- childPs.installerPackageName;
+ childPs.installSource.installerPackageName;
}
childRes.removedInfo.isUpdate = true;
childRes.removedInfo.installReasons =
@@ -17024,7 +16717,8 @@ public class PackageManagerService extends IPackageManager.Stub
PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this);
childRemovedRes.removedPackage = childPkg.packageName;
if (childPs != null) {
- childRemovedRes.installerPackageName = childPs.installerPackageName;
+ childRemovedRes.installerPackageName =
+ childPs.installSource.installerPackageName;
}
childRemovedRes.isUpdate = false;
childRemovedRes.dataRemoved = true;
@@ -17123,9 +16817,8 @@ public class PackageManagerService extends IPackageManager.Stub
// we're passing the freezer back to be closed in a later phase of install
shouldCloseFreezerBeforeReturn = false;
- return new PrepareResult(args.installReason, targetVolumeUuid, installerPackageName,
- args.user, replace, targetScanFlags, targetParseFlags, existingPackage, pkg,
- replace /* clearCodeCache */, sysPkg, renamedPackage, freezer,
+ return new PrepareResult(replace, targetScanFlags, targetParseFlags,
+ existingPackage, pkg, replace /* clearCodeCache */, sysPkg, freezer,
ps, disabledPs, childPackages);
} finally {
if (shouldCloseFreezerBeforeReturn) {
@@ -17806,7 +17499,7 @@ public class PackageManagerService extends IPackageManager.Stub
continue;
}
List<VersionedPackage> libClientPackages = getPackagesUsingSharedLibraryLPr(
- libraryInfo, 0, currUserId);
+ libraryInfo, MATCH_KNOWN_PACKAGES, currUserId);
if (!ArrayUtils.isEmpty(libClientPackages)) {
Slog.w(TAG, "Not removing package " + pkg.manifestPackageName
+ " hosting lib " + libraryInfo.getName() + " version "
@@ -18011,8 +17704,14 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
if (removedAppId >= 0) {
+ // If a system app's updates are uninstalled the UID is not actually removed. Some
+ // services need to know the package name affected.
+ if (extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
+ extras.putString(Intent.EXTRA_PACKAGE_NAME, removedPackage);
+ }
+
packageSender.sendPackageBroadcast(Intent.ACTION_UID_REMOVED,
- removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
+ null, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
null, null, broadcastUsers, instantUserIds);
}
}
@@ -18051,7 +17750,7 @@ public class PackageManagerService extends IPackageManager.Stub
final PackageParser.Package deletedPkg = deletedPs.pkg;
if (outInfo != null) {
outInfo.removedPackage = packageName;
- outInfo.installerPackageName = deletedPs.installerPackageName;
+ outInfo.installerPackageName = deletedPs.installSource.installerPackageName;
outInfo.isStaticSharedLib = deletedPkg != null
&& deletedPkg.staticSharedLibName != null;
outInfo.populateUsers(deletedPs == null ? null
@@ -18161,70 +17860,15 @@ public class PackageManagerService extends IPackageManager.Stub
}
static boolean locationIsPrivileged(String path) {
- try {
- final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
- final File privilegedVendorAppDir = new File(Environment.getVendorDirectory(), "priv-app");
- final File privilegedOdmAppDir = new File(Environment.getOdmDirectory(), "priv-app");
- final File privilegedProductAppDir = new File(Environment.getProductDirectory(), "priv-app");
- final File privilegedSystemExtAppDir =
- new File(Environment.getSystemExtDirectory(), "priv-app");
- return path.startsWith(privilegedAppDir.getCanonicalPath() + "/")
- || path.startsWith(privilegedVendorAppDir.getCanonicalPath() + "/")
- || path.startsWith(privilegedOdmAppDir.getCanonicalPath() + "/")
- || path.startsWith(privilegedProductAppDir.getCanonicalPath() + "/")
- || path.startsWith(privilegedSystemExtAppDir.getCanonicalPath() + "/");
- } catch (IOException e) {
- Slog.e(TAG, "Unable to access code path " + path);
- }
- return false;
- }
-
- static boolean locationIsOem(String path) {
- try {
- return path.startsWith(Environment.getOemDirectory().getCanonicalPath() + "/");
- } catch (IOException e) {
- Slog.e(TAG, "Unable to access code path " + path);
- }
- return false;
- }
-
- static boolean locationIsVendor(String path) {
- try {
- return path.startsWith(Environment.getVendorDirectory().getCanonicalPath() + "/")
- || path.startsWith(Environment.getOdmDirectory().getCanonicalPath() + "/");
- } catch (IOException e) {
- Slog.e(TAG, "Unable to access code path " + path);
- }
- return false;
- }
-
- static boolean locationIsProduct(String path) {
- try {
- return path.startsWith(Environment.getProductDirectory().getCanonicalPath() + "/");
- } catch (IOException e) {
- Slog.e(TAG, "Unable to access code path " + path);
- }
- return false;
- }
-
- static boolean locationIsSystemExt(String path) {
- try {
- return path.startsWith(
- Environment.getSystemExtDirectory().getCanonicalPath() + "/");
- } catch (IOException e) {
- Slog.e(TAG, "Unable to access code path " + path);
+ for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
+ SystemPartition partition = SYSTEM_PARTITIONS.get(i);
+ if (partition.containsPrivPath(path)) {
+ return true;
+ }
}
return false;
}
- static boolean locationIsOdm(String path) {
- try {
- return path.startsWith(Environment.getOdmDirectory().getCanonicalPath() + "/");
- } catch (IOException e) {
- Slog.e(TAG, "Unable to access code path " + path);
- }
- return false;
- }
/*
* Tries to delete system package.
@@ -18335,23 +17979,15 @@ public class PackageManagerService extends IPackageManager.Stub
| PackageParser.PARSE_MUST_BE_APK
| PackageParser.PARSE_IS_SYSTEM_DIR;
@ScanFlags int scanFlags = SCAN_AS_SYSTEM;
- if (locationIsPrivileged(codePathString)) {
- scanFlags |= SCAN_AS_PRIVILEGED;
- }
- if (locationIsOem(codePathString)) {
- scanFlags |= SCAN_AS_OEM;
- }
- if (locationIsVendor(codePathString)) {
- scanFlags |= SCAN_AS_VENDOR;
- }
- if (locationIsProduct(codePathString)) {
- scanFlags |= SCAN_AS_PRODUCT;
- }
- if (locationIsSystemExt(codePathString)) {
- scanFlags |= SCAN_AS_SYSTEM_EXT;
- }
- if (locationIsOdm(codePathString)) {
- scanFlags |= SCAN_AS_ODM;
+ for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
+ SystemPartition partition = SYSTEM_PARTITIONS.get(i);
+ if (partition.containsPath(codePathString)) {
+ scanFlags |= partition.scanFlag;
+ if (partition.containsPrivPath(codePathString)) {
+ scanFlags |= SCAN_AS_PRIVILEGED;
+ }
+ break;
+ }
}
final File codePath = new File(codePathString);
@@ -18709,7 +18345,7 @@ public class PackageManagerService extends IPackageManager.Stub
String childPackageName = ps.childPackageNames.get(i);
PackageRemovedInfo childInfo = new PackageRemovedInfo(this);
childInfo.removedPackage = childPackageName;
- childInfo.installerPackageName = ps.installerPackageName;
+ childInfo.installerPackageName = ps.installSource.installerPackageName;
outInfo.removedChildPackages.put(childPackageName, childInfo);
PackageSetting childPs = mSettings.getPackageLPr(childPackageName);
if (childPs != null) {
@@ -18793,10 +18429,7 @@ public class PackageManagerService extends IPackageManager.Stub
false /*hidden*/,
0 /*distractionFlags*/,
false /*suspended*/,
- null /*suspendingPackage*/,
- null /*dialogInfo*/,
- null /*suspendedAppExtras*/,
- null /*suspendedLauncherExtras*/,
+ null /*suspendParams*/,
false /*instantApp*/,
false /*virtualPreload*/,
null /*lastDisableAppCaller*/,
@@ -18852,7 +18485,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (outInfo != null) {
outInfo.removedPackage = ps.name;
- outInfo.installerPackageName = ps.installerPackageName;
+ outInfo.installerPackageName = ps.installSource.installerPackageName;
outInfo.isStaticSharedLib = pkg != null && pkg.staticSharedLibName != null;
outInfo.removedAppId = ps.appId;
outInfo.removedUsers = userIds;
@@ -20044,7 +19677,7 @@ public class PackageManagerService extends IPackageManager.Stub
set, comp, userId);
}
- private @Nullable String getSetupWizardPackageName() {
+ private @Nullable String getSetupWizardPackageNameImpl() {
final Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_SETUP_WIZARD);
@@ -20151,11 +19784,29 @@ public class PackageManagerService extends IPackageManager.Stub
return systemCaptionsServiceComponentName.getPackageName();
}
+ @Override
+ public String getSetupWizardPackageName() {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Non-system caller");
+ }
+ return mPmInternal.getSetupWizardPackageName();
+ }
+
public String getIncidentReportApproverPackageName() {
return mContext.getString(R.string.config_incidentReportApproverPackage);
}
@Override
+ public String[] getTelephonyPackageNames() {
+ String names = mContext.getString(R.string.config_telephonyPackages);
+ String[] telephonyPackageNames = null;
+ if (!TextUtils.isEmpty(names)) {
+ telephonyPackageNames = names.trim().split(",");
+ }
+ return telephonyPackageNames;
+ }
+
+ @Override
public void setApplicationEnabledSetting(String appPackageName,
int newState, int flags, int userId, String callingPackage) {
if (!mUserManager.exists(userId)) return;
@@ -20351,7 +20002,11 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
synchronized (mLock) {
- scheduleWritePackageRestrictionsLocked(userId);
+ if ((flags & PackageManager.SYNCHRONOUS) != 0) {
+ flushPackageRestrictionsAsUserInternalLocked(userId);
+ } else {
+ scheduleWritePackageRestrictionsLocked(userId);
+ }
updateSequenceNumberLP(pkgSetting, new int[] { userId });
final long callingId = Binder.clearCallingIdentity();
try {
@@ -20394,7 +20049,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (sendNow) {
int packageUid = UserHandle.getUid(userId, pkgSetting.appId);
sendPackageChangedBroadcast(packageName,
- (flags&PackageManager.DONT_KILL_APP) != 0, components, packageUid);
+ (flags & PackageManager.DONT_KILL_APP) != 0, components, packageUid, null);
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -20412,16 +20067,22 @@ public class PackageManagerService extends IPackageManager.Stub
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission*/,
false /* checkShell */, "flushPackageRestrictions");
synchronized (mLock) {
- mSettings.writePackageRestrictionsLPr(userId);
- mDirtyUsers.remove(userId);
- if (mDirtyUsers.isEmpty()) {
- mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS);
- }
+ flushPackageRestrictionsAsUserInternalLocked(userId);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void flushPackageRestrictionsAsUserInternalLocked(int userId) {
+ mSettings.writePackageRestrictionsLPr(userId);
+ mDirtyUsers.remove(userId);
+ if (mDirtyUsers.isEmpty()) {
+ mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS);
}
}
private void sendPackageChangedBroadcast(String packageName,
- boolean killFlag, ArrayList<String> componentNames, int packageUid) {
+ boolean dontKillApp, ArrayList<String> componentNames, int packageUid,
+ String reason) {
if (DEBUG_INSTALL)
Log.v(TAG, "Sending package changed: package=" + packageName + " components="
+ componentNames);
@@ -20430,8 +20091,11 @@ public class PackageManagerService extends IPackageManager.Stub
String nameList[] = new String[componentNames.size()];
componentNames.toArray(nameList);
extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList);
- extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag);
+ extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, dontKillApp);
extras.putInt(Intent.EXTRA_UID, packageUid);
+ if (reason != null) {
+ extras.putString(Intent.EXTRA_REASON, reason);
+ }
// If this is not reporting a change of the overall package, then only send it
// to registered receivers. We don't want to launch a swath of apps for every
// little component state change.
@@ -20626,7 +20290,7 @@ public class PackageManagerService extends IPackageManager.Stub
mInjector.getPermissionPolicyInternal();
permissionPolicyInternal.setOnInitializedCallback(userId -> {
// The SDK updated case is already handled when we run during the ctor.
- synchronized (mPackages) {
+ synchronized (mLock) {
mPermissionManager.updateAllPermissions(
StorageManager.UUID_PRIVATE_INTERNAL, false);
}
@@ -20679,6 +20343,34 @@ public class PackageManagerService extends IPackageManager.Stub
}, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
}
+ IntentFilter overlayFilter = new IntentFilter(Intent.ACTION_OVERLAY_CHANGED);
+ overlayFilter.addDataScheme("package");
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent == null) {
+ return;
+ }
+ Uri data = intent.getData();
+ if (data == null) {
+ return;
+ }
+ String packageName = data.getSchemeSpecificPart();
+ if (packageName == null) {
+ return;
+ }
+ PackageParser.Package pkg = mPackages.get(packageName);
+ if (pkg == null) {
+ return;
+ }
+ sendPackageChangedBroadcast(pkg.packageName,
+ true /* dontKillApp */,
+ new ArrayList<>(Collections.singletonList(pkg.packageName)),
+ pkg.applicationInfo.uid,
+ Intent.ACTION_OVERLAY_CHANGED);
+ }
+ }, overlayFilter);
+
mModuleInfoProvider.systemReady();
// Installer service might attempt to install some packages that have been staged for
@@ -22261,7 +21953,7 @@ public class PackageManagerService extends IPackageManager.Stub
final String currentVolumeUuid;
final File codeFile;
- final String installerPackageName;
+ final InstallSource installSource;
final String packageAbiOverride;
final int appId;
final String seinfo;
@@ -22318,7 +22010,7 @@ public class PackageManagerService extends IPackageManager.Stub
isCurrentLocationExternal = isExternal(pkg);
codeFile = new File(pkg.codePath);
- installerPackageName = ps.installerPackageName;
+ installSource = ps.installSource;
packageAbiOverride = ps.cpuAbiOverrideString;
appId = UserHandle.getAppId(pkg.applicationInfo.uid);
seinfo = pkg.applicationInfo.seInfo;
@@ -22464,7 +22156,7 @@ public class PackageManagerService extends IPackageManager.Stub
final Message msg = mHandler.obtainMessage(INIT_COPY);
final OriginInfo origin = OriginInfo.fromExistingFile(codeFile);
final InstallParams params = new InstallParams(origin, move, installObserver, installFlags,
- installerPackageName, volumeUuid, null /*verificationInfo*/, user,
+ installSource, volumeUuid, null /*verificationInfo*/, user,
packageAbiOverride, null /*grantedPermissions*/,
null /*whitelistedRestrictedPermissions*/, PackageParser.SigningDetails.UNKNOWN,
PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.VERSION_CODE_HIGHEST);
@@ -22643,10 +22335,19 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- /** Called by UserManagerService */
- void createNewUser(int userId, String[] disallowedPackages) {
+ /**
+ * Called by UserManagerService.
+ *
+ * @param installablePackages system packages that should be initially installed for this user,
+ * or {@code null} if all system packages should be installed
+ * @param disallowedPackages packages that should not be initially installed. Takes precedence
+ * over installablePackages.
+ */
+ void createNewUser(int userId, @Nullable Set<String> installablePackages,
+ String[] disallowedPackages) {
synchronized (mInstallLock) {
- mSettings.createNewUserLI(this, mInstaller, userId, disallowedPackages);
+ mSettings.createNewUserLI(this, mInstaller, userId,
+ installablePackages, disallowedPackages);
}
synchronized (mLock) {
scheduleWritePackageRestrictionsLocked(userId);
@@ -23213,34 +22914,38 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
- public String getKnownPackageName(int knownPackage, int userId) {
+ public @NonNull String[] getKnownPackageNames(int knownPackage, int userId) {
switch(knownPackage) {
case PackageManagerInternal.PACKAGE_BROWSER:
- return mPermissionManager.getDefaultBrowser(userId);
+ return new String[]{mPermissionManager.getDefaultBrowser(userId)};
case PackageManagerInternal.PACKAGE_INSTALLER:
- return mRequiredInstallerPackage;
+ return new String[]{mRequiredInstallerPackage};
case PackageManagerInternal.PACKAGE_SETUP_WIZARD:
- return mSetupWizardPackage;
+ return new String[]{mSetupWizardPackage};
case PackageManagerInternal.PACKAGE_SYSTEM:
- return "android";
+ return new String[]{"android"};
case PackageManagerInternal.PACKAGE_VERIFIER:
- return mRequiredVerifierPackage;
+ return new String[]{mRequiredVerifierPackage};
case PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER:
- return mSystemTextClassifierPackage;
+ return new String[]{mSystemTextClassifierPackage};
case PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER:
- return mRequiredPermissionControllerPackage;
+ return new String[]{mRequiredPermissionControllerPackage};
case PackageManagerInternal.PACKAGE_WELLBEING:
- return mWellbeingPackage;
+ return new String[]{mWellbeingPackage};
case PackageManagerInternal.PACKAGE_DOCUMENTER:
- return mDocumenterPackage;
+ return new String[]{mDocumenterPackage};
case PackageManagerInternal.PACKAGE_CONFIGURATOR:
- return mConfiguratorPackage;
+ return new String[]{mConfiguratorPackage};
case PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER:
- return mIncidentReportApproverPackage;
+ return new String[]{mIncidentReportApproverPackage};
case PackageManagerInternal.PACKAGE_APP_PREDICTOR:
- return mAppPredictionServicePackage;
+ return new String[]{mAppPredictionServicePackage};
+ case PackageManagerInternal.PACKAGE_TELEPHONY:
+ return mTelephonyPackages;
+ case PackageManagerInternal.PACKAGE_WIFI:
+ return new String[]{mWifiPackage};
}
- return null;
+ return ArrayUtils.emptyArray(String.class);
}
@Override
@@ -23301,11 +23006,21 @@ public class PackageManagerService extends IPackageManager.Stub
public Bundle getSuspendedPackageLauncherExtras(String packageName, int userId) {
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
- PersistableBundle launcherExtras = null;
+ final Bundle allExtras = new Bundle();
if (ps != null) {
- launcherExtras = ps.readUserState(userId).suspendedLauncherExtras;
+ final PackageUserState pus = ps.readUserState(userId);
+ if (pus.suspended) {
+ for (int i = 0; i < pus.suspendParams.size(); i++) {
+ final PackageUserState.SuspendParams params =
+ pus.suspendParams.valueAt(i);
+ if (params != null && params.launcherExtras != null) {
+ allExtras.putAll(params.launcherExtras);
+ }
+ }
+ }
+
}
- return (launcherExtras != null) ? new Bundle(launcherExtras.deepCopy()) : null;
+ return (allExtras.size() > 0) ? allExtras : null;
}
}
@@ -23321,16 +23036,38 @@ public class PackageManagerService extends IPackageManager.Stub
public String getSuspendingPackage(String suspendedPackage, int userId) {
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(suspendedPackage);
- return (ps != null) ? ps.readUserState(userId).suspendingPackage : null;
+ if (ps != null) {
+ final PackageUserState pus = ps.readUserState(userId);
+ if (pus.suspended) {
+ String suspendingPackage = null;
+ for (int i = 0; i < pus.suspendParams.size(); i++) {
+ suspendingPackage = pus.suspendParams.keyAt(i);
+ if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
+ return suspendingPackage;
+ }
+ }
+ return suspendingPackage;
+ }
+ }
+ return null;
}
}
@Override
- public SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, int userId) {
+ public SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage,
+ String suspendingPackage, int userId) {
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(suspendedPackage);
- return (ps != null) ? ps.readUserState(userId).dialogInfo : null;
+ if (ps != null) {
+ final PackageUserState pus = ps.readUserState(userId);
+ if (pus.suspended) {
+ final PackageUserState.SuspendParams suspendParams =
+ pus.suspendParams.get(suspendingPackage);
+ return (suspendParams != null) ? suspendParams.dialogInfo : null;
+ }
+ }
}
+ return null;
}
@Override
@@ -23407,7 +23144,6 @@ public class PackageManagerService extends IPackageManager.Stub
usersWithPoOrDo.add(profileOwnerPackages.keyAt(i));
}
}
- unsuspendForNonSystemSuspendingPackages(usersWithPoOrDo);
}
@Override
@@ -23455,6 +23191,19 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
+ public boolean setInstalled(PackageParser.Package pkg, @UserIdInt int userId,
+ boolean installed) {
+ synchronized (mLock) {
+ final PackageSetting ps = mSettings.mPackages.get(pkg.packageName);
+ if (ps.getInstalled(userId) != installed) {
+ ps.setInstalled(installed, userId);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ @Override
public void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
Intent origIntent, String resolvedType, String callingPackage,
Bundle verificationBundle, int userId) {
@@ -23465,19 +23214,20 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public void grantImplicitAccess(int userId, Intent intent,
- int callingAppId, int targetAppId) {
+ int callingUid, int targetAppId) {
synchronized (mLock) {
- final PackageParser.Package callingPackage = getPackage(
- UserHandle.getUid(userId, callingAppId));
- final PackageParser.Package targetPackage = getPackage(
- UserHandle.getUid(userId, targetAppId));
+ final PackageParser.Package callingPackage = getPackage(callingUid);
+ final PackageParser.Package targetPackage =
+ getPackage(UserHandle.getUid(userId, targetAppId));
if (callingPackage == null || targetPackage == null) {
return;
}
- if (isInstantApp(callingPackage.packageName, userId)) {
+ final boolean instantApp = isInstantAppInternal(callingPackage.packageName, userId,
+ callingUid);
+ if (instantApp) {
mInstantAppRegistry.grantInstantAccessLPw(userId, intent,
- callingAppId, targetAppId);
+ UserHandle.getAppId(callingUid), targetAppId);
} else {
mAppsFilter.grantImplicitAccess(
callingPackage.packageName, targetPackage.packageName, userId);
@@ -23868,7 +23618,7 @@ public class PackageManagerService extends IPackageManager.Stub
return false;
}
final PackageSetting installerPackageSetting =
- mSettings.mPackages.get(packageSetting.installerPackageName);
+ mSettings.mPackages.get(packageSetting.installSource.installerPackageName);
return installerPackageSetting != null
&& UserHandle.isSameApp(installerPackageSetting.appId, callingUid);
}
@@ -24291,20 +24041,20 @@ public class PackageManagerService extends IPackageManager.Stub
private final File mStagedDir;
private final IPackageInstallObserver2 mObserver;
private final PackageInstaller.SessionParams mSessionParams;
- private final String mInstallerPackageName;
private final int mInstallerUid;
+ @NonNull private final InstallSource mInstallSource;
private final UserHandle mUser;
private final SigningDetails mSigningDetails;
ActiveInstallSession(String packageName, File stagedDir, IPackageInstallObserver2 observer,
- PackageInstaller.SessionParams sessionParams, String installerPackageName,
- int installerUid, UserHandle user, SigningDetails signingDetails) {
+ PackageInstaller.SessionParams sessionParams, int installerUid,
+ InstallSource installSource, UserHandle user, SigningDetails signingDetails) {
mPackageName = packageName;
mStagedDir = stagedDir;
mObserver = observer;
mSessionParams = sessionParams;
- mInstallerPackageName = installerPackageName;
mInstallerUid = installerUid;
+ mInstallSource = Preconditions.checkNotNull(installSource);
mUser = user;
mSigningDetails = signingDetails;
}
@@ -24325,14 +24075,15 @@ public class PackageManagerService extends IPackageManager.Stub
return mSessionParams;
}
- public String getInstallerPackageName() {
- return mInstallerPackageName;
- }
-
public int getInstallerUid() {
return mInstallerUid;
}
+ @NonNull
+ public InstallSource getInstallSource() {
+ return mInstallSource;
+ }
+
public UserHandle getUser() {
return mUser;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index fe529a152364..f1c84b8701ba 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -468,7 +468,7 @@ class PackageManagerShellCommand extends ShellCommand {
* @param pckg
*/
private int displayPackageFilePath(String pckg, int userId) throws RemoteException {
- PackageInfo info = mInterface.getPackageInfo(pckg, 0, userId);
+ PackageInfo info = mInterface.getPackageInfo(pckg, PackageManager.MATCH_APEX, userId);
if (info != null && info.applicationInfo != null) {
final PrintWriter pw = getOutPrintWriter();
pw.print("package:");
@@ -496,6 +496,10 @@ class PackageManagerShellCommand extends ShellCommand {
getErrPrintWriter().println("Error: no package specified");
return 1;
}
+ userId = translateUserId(userId, true /*allowAll*/, "runPath");
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ }
return displayPackageFilePath(pkg, userId);
}
@@ -718,6 +722,10 @@ class PackageManagerShellCommand extends ShellCommand {
final String filter = getNextArg();
+ userId = translateUserId(userId, true /*allowAll*/, "runListPackages");
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ }
@SuppressWarnings("unchecked")
final ParceledListSlice<PackageInfo> slice =
mInterface.getInstalledPackages(getFlags, userId);
@@ -743,7 +751,7 @@ class PackageManagerShellCommand extends ShellCommand {
(!listThirdParty || !isSystem) &&
(!listApexOnly || isApex)) {
pw.print("package:");
- if (showSourceDir && !isApex) {
+ if (showSourceDir) {
pw.print(info.applicationInfo.sourceDir);
pw.print("=");
}
@@ -1285,7 +1293,7 @@ class PackageManagerShellCommand extends ShellCommand {
private int runInstallExisting() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
- int userId = UserHandle.USER_SYSTEM;
+ int userId = UserHandle.USER_CURRENT;
int installFlags = PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS;
String opt;
boolean waitTillComplete = false;
@@ -1320,6 +1328,10 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println("Error: package name not specified");
return 1;
}
+ userId = translateUserId(userId, true /*allowAll*/, "runInstallExisting");
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ }
int installReason = PackageManager.INSTALL_REASON_UNKNOWN;
try {
@@ -1945,6 +1957,10 @@ class PackageManagerShellCommand extends ShellCommand {
getErrPrintWriter().println("Error: no package or component specified");
return 1;
}
+ userId = translateUserId(userId, true /*allowAll*/, "runSetEnabledSetting");
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ }
ComponentName cn = ComponentName.unflattenFromString(pkg);
if (cn == null) {
mInterface.setApplicationEnabledSetting(pkg, state, 0, userId,
@@ -1974,6 +1990,10 @@ class PackageManagerShellCommand extends ShellCommand {
getErrPrintWriter().println("Error: no package or component specified");
return 1;
}
+ userId = translateUserId(userId, true /*allowAll*/, "runSetHiddenSetting");
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ }
mInterface.setApplicationHiddenSettingAsUser(pkg, state, userId);
getOutPrintWriter().println("Package " + pkg + " new hidden state: "
+ mInterface.getApplicationHiddenSettingAsUser(pkg, userId));
@@ -2043,8 +2063,14 @@ class PackageManagerShellCommand extends ShellCommand {
info = null;
}
try {
+ userId = translateUserId(userId, true /*allowAll*/, "runSuspend");
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ }
mInterface.setPackagesSuspendedAsUser(new String[]{packageName}, suspendedState,
- appExtras, launcherExtras, info, callingPackage, userId);
+ ((appExtras.size() > 0) ? appExtras : null),
+ ((launcherExtras.size() > 0) ? launcherExtras : null),
+ info, callingPackage, userId);
pw.println("Package " + packageName + " new suspended state: "
+ mInterface.isPackageSuspendedForUser(packageName, userId));
return 0;
@@ -2074,7 +2100,7 @@ class PackageManagerShellCommand extends ShellCommand {
getErrPrintWriter().println("Error: no permission specified");
return 1;
}
-
+ userId = translateUserId(userId, true /*allowAll*/, "runGrantRevokePermission");
if (grant) {
mPermissionManager.grantRuntimePermission(pkg, perm, userId);
} else {
@@ -2262,6 +2288,10 @@ class PackageManagerShellCommand extends ShellCommand {
return 1;
}
+ userId = translateUserId(userId, true /*allowAll*/, "runSetAppLink");
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ }
final PackageInfo info = mInterface.getPackageInfo(pkg, 0, userId);
if (info == null) {
getErrPrintWriter().println("Error: package " + pkg + " not found.");
@@ -2302,6 +2332,10 @@ class PackageManagerShellCommand extends ShellCommand {
return 1;
}
+ userId = translateUserId(userId, true /*allowAll*/, "runGetAppLink");
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ }
final PackageInfo info = mInterface.getPackageInfo(pkg, 0, userId);
if (info == null) {
getErrPrintWriter().println("Error: package " + pkg + " not found.");
@@ -2381,6 +2415,7 @@ class PackageManagerShellCommand extends ShellCommand {
int userId = -1;
int flags = 0;
String opt;
+ boolean preCreateOnly = false;
while ((opt = getNextOption()) != null) {
if ("--profileOf".equals(opt)) {
userId = UserHandle.parseUserArg(getNextArgRequired());
@@ -2394,16 +2429,22 @@ class PackageManagerShellCommand extends ShellCommand {
flags |= UserInfo.FLAG_GUEST;
} else if ("--demo".equals(opt)) {
flags |= UserInfo.FLAG_DEMO;
+ } else if ("--pre-create-only".equals(opt)) {
+ preCreateOnly = true;
} else {
getErrPrintWriter().println("Error: unknown option " + opt);
return 1;
}
}
String arg = getNextArg();
- if (arg == null) {
+ if (arg == null && !preCreateOnly) {
getErrPrintWriter().println("Error: no user name specified.");
return 1;
}
+ if (arg != null && preCreateOnly) {
+ getErrPrintWriter().println("Warning: name is ignored for pre-created users");
+ }
+
name = arg;
UserInfo info;
IUserManager um = IUserManager.Stub.asInterface(
@@ -2417,7 +2458,7 @@ class PackageManagerShellCommand extends ShellCommand {
accm.addSharedAccountsFromParentUser(parentUserId, userId,
(Process.myUid() == Process.ROOT_UID) ? "root" : "com.android.shell");
} else if (userId < 0) {
- info = um.createUser(name, flags);
+ info = preCreateOnly ? um.preCreateUser(flags) : um.createUser(name, flags);
} else {
info = um.createProfileForUser(name, flags, userId, null);
}
@@ -2666,8 +2707,7 @@ class PackageManagerShellCommand extends ShellCommand {
}
pkgName = componentName.getPackageName();
}
-
-
+ userId = translateUserId(userId, true /*allowAll*/, "runInstallCreate");
final CompletableFuture<Boolean> future = new CompletableFuture<>();
final RemoteCallback callback = new RemoteCallback(res -> future.complete(res != null));
try {
@@ -2763,8 +2803,10 @@ class PackageManagerShellCommand extends ShellCommand {
}
}
- userId = translateUserId(userId, false /*allowAll*/, "runSetHarmfulAppWarning");
-
+ userId = translateUserId(userId, true /*allowAll*/, "runSetHarmfulAppWarning");
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ }
final String packageName = getNextArgRequired();
final String warning = getNextArg();
@@ -2786,8 +2828,10 @@ class PackageManagerShellCommand extends ShellCommand {
}
}
- userId = translateUserId(userId, false /*allowAll*/, "runGetHarmfulAppWarning");
-
+ userId = translateUserId(userId, true /*allowAll*/, "runGetHarmfulAppWarning");
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ }
final String packageName = getNextArgRequired();
final CharSequence warning = mInterface.getHarmfulAppWarning(packageName, userId);
if (!TextUtils.isEmpty(warning)) {
@@ -2824,7 +2868,7 @@ class PackageManagerShellCommand extends ShellCommand {
private int doCreateSession(SessionParams params, String installerPackageName, int userId)
throws RemoteException {
- userId = translateUserId(userId, true /*allowAll*/, "runInstallCreate");
+ userId = translateUserId(userId, true /*allowAll*/, "doCreateSession");
if (userId == UserHandle.USER_ALL) {
userId = UserHandle.USER_SYSTEM;
params.installFlags |= PackageManager.INSTALL_ALL_USERS;
@@ -3115,13 +3159,13 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" dump PACKAGE");
pw.println(" Print various system state associated with the given PACKAGE.");
pw.println("");
- pw.println(" list features");
- pw.println(" Prints all features of the system.");
- pw.println("");
pw.println(" has-feature FEATURE_NAME [version]");
pw.println(" Prints true and returns exit status 0 when system has a FEATURE_NAME,");
pw.println(" otherwise prints false and returns exit status 1");
pw.println("");
+ pw.println(" list features");
+ pw.println(" Prints all features of the system.");
+ pw.println("");
pw.println(" list instrumentation [-f] [TARGET-PACKAGE]");
pw.println(" Prints all test packages; optionally only those targeting TARGET-PACKAGE");
pw.println(" Options:");
@@ -3161,11 +3205,14 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" -u: list only the permissions users will see");
pw.println("");
pw.println(" list staged-sessions [--only-ready] [--only-sessionid] [--only-parent]");
- pw.println(" Displays list of all staged sessions on device.");
+ pw.println(" Prints all staged sessions.");
pw.println(" --only-ready: show only staged sessions that are ready");
pw.println(" --only-sessionid: show only sessionId of each session");
pw.println(" --only-parent: hide all children sessions");
pw.println("");
+ pw.println(" list users");
+ pw.println(" Prints all users.");
+ pw.println("");
pw.println(" resolve-activity [--brief] [--components] [--query-flags FLAGS]");
pw.println(" [--user USER_ID] INTENT");
pw.println(" Prints the activity that resolves to the given INTENT.");
@@ -3186,7 +3233,7 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" [-p INHERIT_PACKAGE] [--install-location 0/1/2]");
pw.println(" [--install-reason 0/1/2/3/4] [--originating-uri URI]");
pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
- pw.println(" [--preload] [--instantapp] [--full] [--dont-kill]");
+ 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]");
@@ -3209,7 +3256,7 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" --referrer: set URI that instigated the install of the app");
pw.println(" --pkg: specify expected package name of app being installed");
pw.println(" --abi: override the default ABI of the platform");
- pw.println(" --instantapp: cause the app to be installed as an ephemeral install app");
+ pw.println(" --instant: cause the app to be installed as an ephemeral install app");
pw.println(" --full: cause the app to be installed as a non-ephemeral full app");
pw.println(" --install-location: force the install location:");
pw.println(" 0=auto, 1=internal only, 2=prefer external");
@@ -3222,11 +3269,20 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" 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]");
+ pw.println(" [--instant] [--full] [--wait] [--restrict-permissions] PACKAGE");
+ pw.println(" Installs an existing application for a new user. Options are:");
+ pw.println(" --user: install for the given user.");
+ pw.println(" --instant: install as an instant app");
+ pw.println(" --full: install as a full app");
+ pw.println(" --wait: wait until the package is installed");
+ pw.println(" --restrict-permissions: don't whitelist restricted permissions");
+ pw.println("");
pw.println(" install-create [-lrtsfdg] [-i PACKAGE] [--user USER_ID|all|current]");
pw.println(" [-p INHERIT_PACKAGE] [--install-location 0/1/2]");
pw.println(" [--install-reason 0/1/2/3/4] [--originating-uri URI]");
pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
- pw.println(" [--preload] [--instantapp] [--full] [--dont-kill]");
+ pw.println(" [--preload] [--instant] [--full] [--dont-kill]");
pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [--apex] [-S BYTES]");
pw.println(" [--multi-package] [--staged]");
pw.println(" Like \"install\", but starts an install session. Use \"install-write\"");
@@ -3315,8 +3371,11 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" trim-caches DESIRED_FREE_SPACE [internal|UUID]");
pw.println(" Trim cache files to reach the given free space.");
pw.println("");
+ pw.println(" list users");
+ pw.println(" Lists the current users.");
+ pw.println("");
pw.println(" create-user [--profileOf USER_ID] [--managed] [--restricted] [--ephemeral]");
- pw.println(" [--guest] USER_NAME");
+ pw.println(" [--guest] [--pre-create-only] USER_NAME");
pw.println(" Create a new user with the given USER_NAME, printing the new user identifier");
pw.println(" of the user.");
pw.println("");
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 4ea8a30fa206..4fca91a83724 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -187,7 +187,7 @@ public final class PackageSetting extends PackageSettingBase {
proto.write(PackageProto.VERSION_CODE, versionCode);
proto.write(PackageProto.INSTALL_TIME_MS, firstInstallTime);
proto.write(PackageProto.UPDATE_TIME_MS, lastUpdateTime);
- proto.write(PackageProto.INSTALLER_NAME, installerPackageName);
+ proto.write(PackageProto.INSTALLER_NAME, installSource.installerPackageName);
if (pkg != null) {
proto.write(PackageProto.VERSION_STRING, pkg.mVersionName);
@@ -205,6 +205,11 @@ public final class PackageSetting extends PackageSettingBase {
proto.end(splitToken);
}
}
+
+ long sourceToken = proto.start(PackageProto.INSTALL_SOURCE);
+ proto.write(PackageProto.InstallSourceProto.INITIATING_PACKAGE_NAME,
+ installSource.initiatingPackageName);
+ proto.end(sourceToken);
}
writeUsersInfoToProto(proto, PackageProto.USERS);
proto.end(packageToken);
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 029673ffd87b..45ab3575c805 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import android.annotation.NonNull;
import android.content.pm.ApplicationInfo;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.PackageManager;
@@ -29,11 +30,13 @@ import android.content.pm.Signature;
import android.content.pm.SuspendDialogInfo;
import android.os.PersistableBundle;
import android.service.pm.PackageProto;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
import java.io.File;
import java.util.ArrayList;
@@ -120,10 +123,8 @@ public abstract class PackageSettingBase extends SettingBase {
*/
Set<String> mOldCodePaths;
- /** Package name of the app that installed this package */
- String installerPackageName;
- /** Indicates if the package that installed this app has been uninstalled */
- boolean isOrphaned;
+ /** Information about how this package was installed/updated. */
+ @NonNull InstallSource installSource;
/** UUID of {@link VolumeInfo} hosting this app */
String volumeUuid;
/** The category of this app, as hinted by the installer */
@@ -147,8 +148,17 @@ public abstract class PackageSettingBase extends SettingBase {
? new ArrayList<>(childPackageNames) : null;
this.usesStaticLibraries = usesStaticLibraries;
this.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
- init(codePath, resourcePath, legacyNativeLibraryPathString, primaryCpuAbiString,
- secondaryCpuAbiString, cpuAbiOverrideString, pVersionCode);
+ this.codePath = codePath;
+ this.codePathString = codePath.toString();
+ this.resourcePath = resourcePath;
+ this.resourcePathString = resourcePath.toString();
+ this.legacyNativeLibraryPathString = legacyNativeLibraryPathString;
+ this.primaryCpuAbiString = primaryCpuAbiString;
+ this.secondaryCpuAbiString = secondaryCpuAbiString;
+ this.cpuAbiOverrideString = cpuAbiOverrideString;
+ this.versionCode = pVersionCode;
+ this.signatures = new PackageSignatures();
+ this.installSource = InstallSource.EMPTY;
}
/**
@@ -165,27 +175,20 @@ public abstract class PackageSettingBase extends SettingBase {
doCopy(base);
}
- void init(File codePath, File resourcePath, String legacyNativeLibraryPathString,
- String primaryCpuAbiString, String secondaryCpuAbiString,
- String cpuAbiOverrideString, long pVersionCode) {
- this.codePath = codePath;
- this.codePathString = codePath.toString();
- this.resourcePath = resourcePath;
- this.resourcePathString = resourcePath.toString();
- this.legacyNativeLibraryPathString = legacyNativeLibraryPathString;
- this.primaryCpuAbiString = primaryCpuAbiString;
- this.secondaryCpuAbiString = secondaryCpuAbiString;
- this.cpuAbiOverrideString = cpuAbiOverrideString;
- this.versionCode = pVersionCode;
- this.signatures = new PackageSignatures();
+ public void setInstallerPackageName(String packageName) {
+ installSource = installSource.setInstallerPackage(packageName);
}
- public void setInstallerPackageName(String packageName) {
- installerPackageName = packageName;
+ public void setInstallSource(InstallSource installSource) {
+ this.installSource = Preconditions.checkNotNull(installSource);
}
- public String getInstallerPackageName() {
- return installerPackageName;
+ void removeInstallerPackage(String packageName) {
+ installSource = installSource.removeInstallerPackage(packageName);
+ }
+
+ public void setIsOrphaned(boolean isOrphaned) {
+ installSource = installSource.setIsOrphaned(isOrphaned);
}
public void setVolumeUuid(String volumeUuid) {
@@ -239,8 +242,7 @@ public abstract class PackageSettingBase extends SettingBase {
cpuAbiOverrideString = orig.cpuAbiOverrideString;
firstInstallTime = orig.firstInstallTime;
installPermissionsFixed = orig.installPermissionsFixed;
- installerPackageName = orig.installerPackageName;
- isOrphaned = orig.isOrphaned;
+ installSource = orig.installSource;
keySetData = orig.keySetData;
lastUpdateTime = orig.lastUpdateTime;
legacyNativeLibraryPathString = orig.legacyNativeLibraryPathString;
@@ -405,14 +407,28 @@ public abstract class PackageSettingBase extends SettingBase {
return readUserState(userId).suspended;
}
- void setSuspended(boolean suspended, String suspendingPackage, SuspendDialogInfo dialogInfo,
+ void addOrUpdateSuspension(String suspendingPackage, SuspendDialogInfo dialogInfo,
PersistableBundle appExtras, PersistableBundle launcherExtras, int userId) {
final PackageUserState existingUserState = modifyUserState(userId);
- existingUserState.suspended = suspended;
- existingUserState.suspendingPackage = suspended ? suspendingPackage : null;
- existingUserState.dialogInfo = suspended ? dialogInfo : null;
- existingUserState.suspendedAppExtras = suspended ? appExtras : null;
- existingUserState.suspendedLauncherExtras = suspended ? launcherExtras : null;
+ final PackageUserState.SuspendParams newSuspendParams =
+ PackageUserState.SuspendParams.getInstanceOrNull(dialogInfo, appExtras,
+ launcherExtras);
+ if (existingUserState.suspendParams == null) {
+ existingUserState.suspendParams = new ArrayMap<>();
+ }
+ existingUserState.suspendParams.put(suspendingPackage, newSuspendParams);
+ existingUserState.suspended = true;
+ }
+
+ void removeSuspension(String suspendingPackage, int userId) {
+ final PackageUserState existingUserState = modifyUserState(userId);
+ if (existingUserState.suspendParams != null) {
+ existingUserState.suspendParams.remove(suspendingPackage);
+ if (existingUserState.suspendParams.size() == 0) {
+ existingUserState.suspendParams = null;
+ }
+ }
+ existingUserState.suspended = (existingUserState.suspendParams != null);
}
public boolean getInstantApp(int userId) {
@@ -433,9 +449,7 @@ public abstract class PackageSettingBase extends SettingBase {
void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
boolean notLaunched, boolean hidden, int distractionFlags, boolean suspended,
- String suspendingPackage,
- SuspendDialogInfo dialogInfo, PersistableBundle suspendedAppExtras,
- PersistableBundle suspendedLauncherExtras, boolean instantApp,
+ ArrayMap<String, PackageUserState.SuspendParams> suspendParams, boolean instantApp,
boolean virtualPreload, String lastDisableAppCaller,
ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
int domainVerifState, int linkGeneration, int installReason,
@@ -449,10 +463,7 @@ public abstract class PackageSettingBase extends SettingBase {
state.hidden = hidden;
state.distractionFlags = distractionFlags;
state.suspended = suspended;
- state.suspendingPackage = suspendingPackage;
- state.dialogInfo = dialogInfo;
- state.suspendedAppExtras = suspendedAppExtras;
- state.suspendedLauncherExtras = suspendedLauncherExtras;
+ state.suspendParams = suspendParams;
state.lastDisableAppCaller = lastDisableAppCaller;
state.enabledComponents = enabledComponents;
state.disabledComponents = disabledComponents;
@@ -467,9 +478,8 @@ public abstract class PackageSettingBase extends SettingBase {
void setUserState(int userId, PackageUserState otherState) {
setUserState(userId, otherState.ceDataInode, otherState.enabled, otherState.installed,
otherState.stopped, otherState.notLaunched, otherState.hidden,
- otherState.distractionFlags, otherState.suspended, otherState.suspendingPackage,
- otherState.dialogInfo, otherState.suspendedAppExtras,
- otherState.suspendedLauncherExtras, otherState.instantApp,
+ otherState.distractionFlags, otherState.suspended, otherState.suspendParams,
+ otherState.instantApp,
otherState.virtualPreload, otherState.lastDisableAppCaller,
otherState.enabledComponents, otherState.disabledComponents,
otherState.domainVerificationStatus, otherState.appLinkGeneration,
@@ -633,7 +643,10 @@ public abstract class PackageSettingBase extends SettingBase {
proto.write(PackageProto.UserInfoProto.DISTRACTION_FLAGS, state.distractionFlags);
proto.write(PackageProto.UserInfoProto.IS_SUSPENDED, state.suspended);
if (state.suspended) {
- proto.write(PackageProto.UserInfoProto.SUSPENDING_PACKAGE, state.suspendingPackage);
+ for (int j = 0; j < state.suspendParams.size(); j++) {
+ proto.write(PackageProto.UserInfoProto.SUSPENDING_PACKAGE,
+ state.suspendParams.keyAt(j));
+ }
}
proto.write(PackageProto.UserInfoProto.IS_STOPPED, state.stopped);
proto.write(PackageProto.UserInfoProto.IS_LAUNCHED, !state.notLaunched);
@@ -677,8 +690,7 @@ public abstract class PackageSettingBase extends SettingBase {
this.signatures = other.signatures;
this.installPermissionsFixed = other.installPermissionsFixed;
this.keySetData = other.keySetData;
- this.installerPackageName = other.installerPackageName;
- this.isOrphaned = other.isOrphaned;
+ this.installSource = other.installSource;
this.volumeUuid = other.volumeUuid;
this.categoryHint = other.categoryHint;
this.updateAvailable = other.updateAvailable;
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index b94047e119d5..b464988d5871 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -78,6 +78,13 @@ public final class SELinuxMMAC {
sMacPermissions.add(new File(
Environment.getRootDirectory(), "/etc/selinux/plat_mac_permissions.xml"));
+ // SystemExt mac permissions (optional).
+ final File systemExtMacPermission = new File(
+ Environment.getSystemExtDirectory(), "/etc/selinux/system_ext_mac_permissions.xml");
+ if (systemExtMacPermission.exists()) {
+ sMacPermissions.add(systemExtMacPermission);
+ }
+
// Product mac permissions (optional).
final File productMacPermission = new File(
Environment.getProductDirectory(), "/etc/selinux/product_mac_permissions.xml");
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 1873a4ec98d9..a11ae8ce6f54 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -205,9 +205,22 @@ public final class Settings {
private static final String TAG_DEFAULT_BROWSER = "default-browser";
private static final String TAG_DEFAULT_DIALER = "default-dialer";
private static final String TAG_VERSION = "version";
+ /**
+ * @deprecated Moved to {@link android.content.pm.PackageUserState.SuspendParams}
+ */
+ @Deprecated
private static final String TAG_SUSPENDED_DIALOG_INFO = "suspended-dialog-info";
+ /**
+ * @deprecated Moved to {@link android.content.pm.PackageUserState.SuspendParams}
+ */
+ @Deprecated
private static final String TAG_SUSPENDED_APP_EXTRAS = "suspended-app-extras";
+ /**
+ * @deprecated Moved to {@link android.content.pm.PackageUserState.SuspendParams}
+ */
+ @Deprecated
private static final String TAG_SUSPENDED_LAUNCHER_EXTRAS = "suspended-launcher-extras";
+ private static final String TAG_SUSPEND_PARAMS = "suspend-params";
public static final String ATTR_NAME = "name";
public static final String ATTR_PACKAGE = "package";
@@ -541,10 +554,6 @@ public final class Settings {
return null;
}
- void addAppOpPackage(String permName, String packageName) {
- mPermissions.addAppOpPackage(permName, packageName);
- }
-
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
SharedUserSetting s = mSharedUsers.get(name);
if (s != null) {
@@ -660,10 +669,7 @@ public final class Settings {
false /*hidden*/,
0 /*distractionFlags*/,
false /*suspended*/,
- null /*suspendingPackage*/,
- null /*dialogInfo*/,
- null /*suspendedAppExtras*/,
- null /*suspendedLauncherExtras*/,
+ null /*suspendParams*/,
instantApp,
virtualPreload,
null /*lastDisableAppCaller*/,
@@ -1042,13 +1048,7 @@ public final class Settings {
return;
}
for (int i = 0; i < mPackages.size(); i++) {
- final PackageSetting ps = mPackages.valueAt(i);
- final String installerPackageName = ps.getInstallerPackageName();
- if (installerPackageName != null
- && installerPackageName.equals(packageName)) {
- ps.setInstallerPackageName(null);
- ps.isOrphaned = true;
- }
+ mPackages.valueAt(i).removeInstallerPackage(packageName);
}
mInstallerPackages.remove(packageName);
}
@@ -1550,10 +1550,7 @@ public final class Settings {
false /*hidden*/,
0 /*distractionFlags*/,
false /*suspended*/,
- null /*suspendingPackage*/,
- null /*dialogInfo*/,
- null /*suspendedAppExtras*/,
- null /*suspendedLauncherExtras*/,
+ null /*suspendParams*/,
false /*instantApp*/,
false /*virtualPreload*/,
null /*lastDisableAppCaller*/,
@@ -1628,12 +1625,12 @@ public final class Settings {
ATTR_DISTRACTION_FLAGS, 0);
final boolean suspended = XmlUtils.readBooleanAttribute(parser, ATTR_SUSPENDED,
false);
- String suspendingPackage = parser.getAttributeValue(null,
+ String oldSuspendingPackage = parser.getAttributeValue(null,
ATTR_SUSPENDING_PACKAGE);
final String dialogMessage = parser.getAttributeValue(null,
ATTR_SUSPEND_DIALOG_MESSAGE);
- if (suspended && suspendingPackage == null) {
- suspendingPackage = PLATFORM_PACKAGE_NAME;
+ if (suspended && oldSuspendingPackage == null) {
+ oldSuspendingPackage = PLATFORM_PACKAGE_NAME;
}
final boolean blockUninstall = XmlUtils.readBooleanAttribute(parser,
@@ -1663,9 +1660,10 @@ public final class Settings {
ArraySet<String> disabledComponents = null;
PersistableBundle suspendedAppExtras = null;
PersistableBundle suspendedLauncherExtras = null;
- SuspendDialogInfo suspendDialogInfo = null;
+ SuspendDialogInfo oldSuspendDialogInfo = null;
int packageDepth = parser.getDepth();
+ ArrayMap<String, PackageUserState.SuspendParams> suspendParamsMap = null;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > packageDepth)) {
@@ -1687,26 +1685,48 @@ public final class Settings {
suspendedLauncherExtras = PersistableBundle.restoreFromXml(parser);
break;
case TAG_SUSPENDED_DIALOG_INFO:
- suspendDialogInfo = SuspendDialogInfo.restoreFromXml(parser);
+ oldSuspendDialogInfo = SuspendDialogInfo.restoreFromXml(parser);
+ break;
+ case TAG_SUSPEND_PARAMS:
+ final String suspendingPackage = parser.getAttributeValue(null,
+ ATTR_SUSPENDING_PACKAGE);
+ if (suspendingPackage == null) {
+ Slog.wtf(TAG, "No suspendingPackage found inside tag "
+ + TAG_SUSPEND_PARAMS);
+ continue;
+ }
+ if (suspendParamsMap == null) {
+ suspendParamsMap = new ArrayMap<>();
+ }
+ suspendParamsMap.put(suspendingPackage,
+ PackageUserState.SuspendParams.restoreFromXml(parser));
break;
default:
Slog.wtf(TAG, "Unknown tag " + parser.getName() + " under tag "
+ TAG_PACKAGE);
}
}
- if (suspendDialogInfo == null && !TextUtils.isEmpty(dialogMessage)) {
- suspendDialogInfo = new SuspendDialogInfo.Builder()
+ if (oldSuspendDialogInfo == null && !TextUtils.isEmpty(dialogMessage)) {
+ oldSuspendDialogInfo = new SuspendDialogInfo.Builder()
.setMessage(dialogMessage)
.build();
}
+ if (suspended && suspendParamsMap == null) {
+ final PackageUserState.SuspendParams suspendParams =
+ PackageUserState.SuspendParams.getInstanceOrNull(
+ oldSuspendDialogInfo,
+ suspendedAppExtras,
+ suspendedLauncherExtras);
+ suspendParamsMap = new ArrayMap<>();
+ suspendParamsMap.put(oldSuspendingPackage, suspendParams);
+ }
if (blockUninstall) {
setBlockUninstallLPw(userId, name, true);
}
ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
- hidden, distractionFlags, suspended, suspendingPackage,
- suspendDialogInfo,
- suspendedAppExtras, suspendedLauncherExtras, instantApp, virtualPreload,
+ hidden, distractionFlags, suspended, suspendParamsMap,
+ instantApp, virtualPreload,
enabledCaller, enabledComponents, disabledComponents, verifState,
linkGeneration, installReason, harmfulAppWarning);
} else if (tagName.equals("preferred-activities")) {
@@ -2006,35 +2026,6 @@ public final class Settings {
}
if (ustate.suspended) {
serializer.attribute(null, ATTR_SUSPENDED, "true");
- if (ustate.suspendingPackage != null) {
- serializer.attribute(null, ATTR_SUSPENDING_PACKAGE,
- ustate.suspendingPackage);
- }
- if (ustate.dialogInfo != null) {
- serializer.startTag(null, TAG_SUSPENDED_DIALOG_INFO);
- ustate.dialogInfo.saveToXml(serializer);
- serializer.endTag(null, TAG_SUSPENDED_DIALOG_INFO);
- }
- if (ustate.suspendedAppExtras != null) {
- serializer.startTag(null, TAG_SUSPENDED_APP_EXTRAS);
- try {
- ustate.suspendedAppExtras.saveToXml(serializer);
- } catch (XmlPullParserException xmle) {
- Slog.wtf(TAG, "Exception while trying to write suspendedAppExtras for "
- + pkg + ". Will be lost on reboot", xmle);
- }
- serializer.endTag(null, TAG_SUSPENDED_APP_EXTRAS);
- }
- if (ustate.suspendedLauncherExtras != null) {
- serializer.startTag(null, TAG_SUSPENDED_LAUNCHER_EXTRAS);
- try {
- ustate.suspendedLauncherExtras.saveToXml(serializer);
- } catch (XmlPullParserException xmle) {
- Slog.wtf(TAG, "Exception while trying to write suspendedLauncherExtras"
- + " for " + pkg + ". Will be lost on reboot", xmle);
- }
- serializer.endTag(null, TAG_SUSPENDED_LAUNCHER_EXTRAS);
- }
}
if (ustate.instantApp) {
serializer.attribute(null, ATTR_INSTANT_APP, "true");
@@ -2067,6 +2058,19 @@ public final class Settings {
serializer.attribute(null, ATTR_HARMFUL_APP_WARNING,
ustate.harmfulAppWarning);
}
+ if (ustate.suspended) {
+ for (int i = 0; i < ustate.suspendParams.size(); i++) {
+ final String suspendingPackage = ustate.suspendParams.keyAt(i);
+ serializer.startTag(null, TAG_SUSPEND_PARAMS);
+ serializer.attribute(null, ATTR_SUSPENDING_PACKAGE, suspendingPackage);
+ final PackageUserState.SuspendParams params =
+ ustate.suspendParams.valueAt(i);
+ if (params != null) {
+ params.saveToXml(serializer);
+ }
+ serializer.endTag(null, TAG_SUSPEND_PARAMS);
+ }
+ }
if (!ArrayUtils.isEmpty(ustate.enabledComponents)) {
serializer.startTag(null, TAG_ENABLED_COMPONENTS);
for (final String name : ustate.enabledComponents) {
@@ -2834,18 +2838,21 @@ public final class Settings {
if (pkg.uidError) {
serializer.attribute(null, "uidError", "true");
}
- if (pkg.installerPackageName != null) {
- serializer.attribute(null, "installer", pkg.installerPackageName);
+ InstallSource installSource = pkg.installSource;
+ if (installSource.installerPackageName != null) {
+ serializer.attribute(null, "installer", installSource.installerPackageName);
}
- if (pkg.isOrphaned) {
+ if (installSource.isOrphaned) {
serializer.attribute(null, "isOrphaned", "true");
}
+ if (installSource.initiatingPackageName != null) {
+ serializer.attribute(null, "installInitiator", installSource.initiatingPackageName);
+ }
if (pkg.volumeUuid != null) {
serializer.attribute(null, "volumeUuid", pkg.volumeUuid);
}
if (pkg.categoryHint != ApplicationInfo.CATEGORY_UNDEFINED) {
- serializer.attribute(null, "categoryHint",
- Integer.toString(pkg.categoryHint));
+ serializer.attribute(null, "categoryHint", Integer.toString(pkg.categoryHint));
}
if (pkg.parentPackageName != null) {
serializer.attribute(null, "parentPackageName", pkg.parentPackageName);
@@ -2860,8 +2867,7 @@ public final class Settings {
pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
- writePermissionsLPr(serializer, pkg.getPermissionsState()
- .getInstallPermissionStates());
+ writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissionStates());
writeSigningKeySetLPr(serializer, pkg.keySetData);
writeUpgradeKeySetsLPr(serializer, pkg.keySetData);
@@ -3599,6 +3605,7 @@ public final class Settings {
String systemStr = null;
String installerPackageName = null;
String isOrphaned = null;
+ String installInitiatingPackageName = null;
String volumeUuid = null;
String categoryHintString = null;
String updateAvailable = null;
@@ -3645,6 +3652,7 @@ public final class Settings {
}
installerPackageName = parser.getAttributeValue(null, "installer");
isOrphaned = parser.getAttributeValue(null, "isOrphaned");
+ installInitiatingPackageName = parser.getAttributeValue(null, "installInitiator");
volumeUuid = parser.getAttributeValue(null, "volumeUuid");
categoryHintString = parser.getAttributeValue(null, "categoryHint");
if (categoryHintString != null) {
@@ -3799,8 +3807,8 @@ public final class Settings {
}
if (packageSetting != null) {
packageSetting.uidError = "true".equals(uidError);
- packageSetting.installerPackageName = installerPackageName;
- packageSetting.isOrphaned = "true".equals(isOrphaned);
+ packageSetting.installSource = InstallSource.create(
+ installInitiatingPackageName, installerPackageName, "true".equals(isOrphaned));
packageSetting.volumeUuid = volumeUuid;
packageSetting.categoryHint = categoryHint;
packageSetting.legacyNativeLibraryPathString = legacyNativeLibraryPathStr;
@@ -4014,8 +4022,9 @@ public final class Settings {
}
}
- void createNewUserLI(@NonNull PackageManagerService service,
- @NonNull Installer installer, int userHandle, String[] disallowedPackages) {
+ void createNewUserLI(@NonNull PackageManagerService service, @NonNull Installer installer,
+ @UserIdInt int userHandle, @Nullable Set<String> installablePackages,
+ String[] disallowedPackages) {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
Trace.TRACE_TAG_PACKAGE_MANAGER);
t.traceBegin("createNewUser-" + userHandle);
@@ -4025,6 +4034,7 @@ public final class Settings {
String[] seinfos;
int[] targetSdkVersions;
int packagesCount;
+ final boolean skipPackageWhitelist = installablePackages == null;
synchronized (mLock) {
Collection<PackageSetting> packages = mPackages.values();
packagesCount = packages.size();
@@ -4040,6 +4050,7 @@ public final class Settings {
continue;
}
final boolean shouldInstall = ps.isSystem() &&
+ (skipPackageWhitelist || installablePackages.contains(ps.name)) &&
!ArrayUtils.contains(disallowedPackages, ps.name) &&
!ps.pkg.applicationInfo.hiddenUntilInstalled;
// Only system apps are initially installed.
@@ -4240,7 +4251,7 @@ public final class Settings {
if (pkg == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
- return pkg.installerPackageName;
+ return pkg.installSource.installerPackageName;
}
boolean isOrphaned(String packageName) {
@@ -4248,7 +4259,7 @@ public final class Settings {
if (pkg == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
- return pkg.isOrphaned;
+ return pkg.installSource.isOrphaned;
}
int getApplicationEnabledSettingLPr(String packageName, int userId) {
@@ -4301,8 +4312,9 @@ public final class Settings {
pkgSetting.setStopped(stopped, userId);
// pkgSetting.pkg.mSetStopped = stopped;
if (pkgSetting.getNotLaunched(userId)) {
- if (pkgSetting.installerPackageName != null) {
- pm.notifyFirstLaunch(pkgSetting.name, pkgSetting.installerPackageName, userId);
+ if (pkgSetting.installSource.installerPackageName != null) {
+ pm.notifyFirstLaunch(pkgSetting.name,
+ pkgSetting.installSource.installerPackageName, userId);
}
pkgSetting.setNotLaunched(false, userId);
}
@@ -4465,7 +4477,8 @@ public final class Settings {
pw.print(",");
pw.print(ps.lastUpdateTime);
pw.print(",");
- pw.print(ps.installerPackageName != null ? ps.installerPackageName : "?");
+ pw.print(ps.installSource.installerPackageName != null
+ ? ps.installSource.installerPackageName : "?");
pw.println();
if (ps.pkg != null) {
pw.print(checkinTag); pw.print("-"); pw.print("splt,");
@@ -4678,9 +4691,9 @@ public final class Settings {
pw.print(prefix); pw.print(" lastUpdateTime=");
date.setTime(ps.lastUpdateTime);
pw.println(sdf.format(date));
- if (ps.installerPackageName != null) {
+ if (ps.installSource.installerPackageName != null) {
pw.print(prefix); pw.print(" installerPackageName=");
- pw.println(ps.installerPackageName);
+ pw.println(ps.installSource.installerPackageName);
}
if (ps.volumeUuid != null) {
pw.print(prefix); pw.print(" volumeUuid=");
@@ -4763,13 +4776,6 @@ public final class Settings {
pw.print(ps.getHidden(user.id));
pw.print(" suspended=");
pw.print(ps.getSuspended(user.id));
- if (ps.getSuspended(user.id)) {
- final PackageUserState pus = ps.readUserState(user.id);
- pw.print(" suspendingPackage=");
- pw.print(pus.suspendingPackage);
- pw.print(" dialogInfo=");
- pw.print(pus.dialogInfo);
- }
pw.print(" stopped=");
pw.print(ps.getStopped(user.id));
pw.print(" notLaunched=");
@@ -4781,6 +4787,23 @@ public final class Settings {
pw.print(" virtual=");
pw.println(ps.getVirtulalPreload(user.id));
+ if (ps.getSuspended(user.id)) {
+ pw.print(prefix);
+ pw.println(" Suspend params:");
+ final PackageUserState pus = ps.readUserState(user.id);
+ for (int i = 0; i < pus.suspendParams.size(); i++) {
+ pw.print(prefix);
+ pw.print(" suspendingPackage=");
+ pw.print(pus.suspendParams.keyAt(i));
+ final PackageUserState.SuspendParams params = pus.suspendParams.valueAt(i);
+ if (params != null) {
+ pw.print(" dialogInfo=");
+ pw.print(params.dialogInfo);
+ }
+ pw.println();
+ }
+ }
+
String[] overlayPaths = ps.getOverlayPaths(user.id);
if (overlayPaths != null && overlayPaths.length > 0) {
pw.print(prefix); pw.println(" overlay paths:");
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index ebba128b5f28..8253b392768f 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1200,12 +1200,20 @@ public class ShortcutService extends IShortcutService.Stub {
return mUsers.get(userId) != null;
}
+ private int mLastLockedUser = -1;
+
/** Return the per-user state. */
@GuardedBy("mLock")
@NonNull
ShortcutUser getUserShortcutsLocked(@UserIdInt int userId) {
if (!isUserUnlockedL(userId)) {
- wtf("User still locked");
+ // Only do wtf once for each user. (until the user is unlocked)
+ if (userId != mLastLockedUser) {
+ wtf("User still locked");
+ mLastLockedUser = userId;
+ }
+ } else {
+ mLastLockedUser = -1;
}
ShortcutUser userPackages = mUsers.get(userId);
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 1cea4ca1b945..6b4ef698a8f4 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -318,7 +318,7 @@ public class StagingManager {
// The APEX part of the session is activated, proceed with the installation of APKs.
try {
Slog.d(TAG, "Installing APK packages in session " + session.sessionId);
- installApksInSession(session, /* preReboot */ false);
+ installApksInSession(session);
} catch (PackageManagerException e) {
session.setStagedSessionFailed(e.error, e.getMessage());
@@ -410,72 +410,23 @@ public class StagingManager {
return apkSession;
}
- private void commitApkSession(@NonNull PackageInstallerSession apkSession,
- PackageInstallerSession originalSession, boolean preReboot)
- throws PackageManagerException {
- final int errorCode = preReboot ? SessionInfo.STAGED_SESSION_VERIFICATION_FAILED
- : SessionInfo.STAGED_SESSION_ACTIVATION_FAILED;
- if (preReboot) {
- final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync(
- (Intent result) -> {
- int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status != PackageInstaller.STATUS_SUCCESS) {
- final String errorMessage = result.getStringExtra(
- PackageInstaller.EXTRA_STATUS_MESSAGE);
- Slog.e(TAG, "Failure to install APK staged session "
- + originalSession.sessionId + " [" + errorMessage + "]");
- originalSession.setStagedSessionFailed(errorCode, errorMessage);
- return;
- }
- mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(
- originalSession.sessionId);
- });
- apkSession.commit(receiver.getIntentSender(), false);
- return;
- }
-
- if ((apkSession.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
- // If rollback is available for this session, notify the rollback
- // manager of the apk session so it can properly enable rollback.
- final IRollbackManager rm = IRollbackManager.Stub.asInterface(
- ServiceManager.getService(Context.ROLLBACK_SERVICE));
- try {
- rm.notifyStagedApkSession(originalSession.sessionId, apkSession.sessionId);
- } catch (RemoteException re) {
- Slog.e(TAG, "Failed to notifyStagedApkSession for session: "
- + originalSession.sessionId, re);
- }
- }
-
- final LocalIntentReceiverSync receiver = new LocalIntentReceiverSync();
- apkSession.commit(receiver.getIntentSender(), false);
- final Intent result = receiver.getResult();
- final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status != PackageInstaller.STATUS_SUCCESS) {
- final String errorMessage = result.getStringExtra(
- PackageInstaller.EXTRA_STATUS_MESSAGE);
- Slog.e(TAG, "Failure to install APK staged session "
- + originalSession.sessionId + " [" + errorMessage + "]");
- throw new PackageManagerException(errorCode, errorMessage);
- }
- }
-
- private void installApksInSession(@NonNull PackageInstallerSession session,
- boolean preReboot) throws PackageManagerException {
+ /**
+ * Extract apks in the given session into a new session. Returns {@code null} if there is no
+ * apks in the given session. Only parent session is returned for multi-package session.
+ */
+ @Nullable
+ private PackageInstallerSession extractApksInSession(PackageInstallerSession session,
+ boolean preReboot) throws PackageManagerException {
final int errorCode = preReboot ? SessionInfo.STAGED_SESSION_VERIFICATION_FAILED
: SessionInfo.STAGED_SESSION_ACTIVATION_FAILED;
if (!session.isMultiPackage() && !isApexSession(session)) {
- // APK single-packaged staged session. Do a regular install.
- PackageInstallerSession apkSession = createAndWriteApkSession(session, preReboot);
- commitApkSession(apkSession, session, preReboot);
+ return createAndWriteApkSession(session, preReboot);
} else if (session.isMultiPackage()) {
// For multi-package staged sessions containing APKs, we identify which child sessions
// contain an APK, and with those then create a new multi-package group of sessions,
// carrying over all the session parameters and unmarking them as staged. On commit the
// sessions will be installed atomically.
- List<PackageInstallerSession> childSessions;
+ final List<PackageInstallerSession> childSessions;
synchronized (mStagedSessions) {
childSessions =
Arrays.stream(session.getChildSessionIds())
@@ -487,18 +438,18 @@ public class StagingManager {
}
if (childSessions.isEmpty()) {
// APEX-only multi-package staged session, nothing to do.
- return;
+ return null;
}
- PackageInstaller.SessionParams params = session.params.copy();
+ final PackageInstaller.SessionParams params = session.params.copy();
params.isStaged = false;
if (preReboot) {
params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
}
// TODO(b/129744602): use the userid from the original session.
- int apkParentSessionId = mPi.createSession(
+ final int apkParentSessionId = mPi.createSession(
params, session.getInstallerPackageName(),
0 /* UserHandle.SYSTEM */);
- PackageInstallerSession apkParentSession = mPi.getSession(apkParentSessionId);
+ final PackageInstallerSession apkParentSession = mPi.getSession(apkParentSessionId);
try {
apkParentSession.open();
} catch (IOException e) {
@@ -519,9 +470,75 @@ public class StagingManager {
"Failed to add a child session " + apkChildSession.sessionId);
}
}
- commitApkSession(apkParentSession, session, preReboot);
+ return apkParentSession;
+ }
+ return null;
+ }
+
+ private void verifyApksInSession(PackageInstallerSession session)
+ throws PackageManagerException {
+
+ final PackageInstallerSession apksToVerify = extractApksInSession(
+ session, /* preReboot */ true);
+ if (apksToVerify == null) {
+ return;
+ }
+
+ final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync(
+ (Intent result) -> {
+ int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status != PackageInstaller.STATUS_SUCCESS) {
+ final String errorMessage = result.getStringExtra(
+ PackageInstaller.EXTRA_STATUS_MESSAGE);
+ Slog.e(TAG, "Failure to verify APK staged session "
+ + session.sessionId + " [" + errorMessage + "]");
+ session.setStagedSessionFailed(
+ SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, errorMessage);
+ return;
+ }
+ mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(
+ session.sessionId);
+ });
+
+ apksToVerify.commit(receiver.getIntentSender(), false);
+ }
+
+ private void installApksInSession(@NonNull PackageInstallerSession session)
+ throws PackageManagerException {
+
+ final PackageInstallerSession apksToInstall = extractApksInSession(
+ session, /* preReboot */ false);
+ if (apksToInstall == null) {
+ return;
+ }
+
+ if ((apksToInstall.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
+ // If rollback is available for this session, notify the rollback
+ // manager of the apk session so it can properly enable rollback.
+ final IRollbackManager rm = IRollbackManager.Stub.asInterface(
+ ServiceManager.getService(Context.ROLLBACK_SERVICE));
+ try {
+ rm.notifyStagedApkSession(session.sessionId, apksToInstall.sessionId);
+ } catch (RemoteException re) {
+ Slog.e(TAG, "Failed to notifyStagedApkSession for session: "
+ + session.sessionId, re);
+ }
+ }
+
+ final LocalIntentReceiverSync receiver = new LocalIntentReceiverSync();
+ apksToInstall.commit(receiver.getIntentSender(), false);
+ final Intent result = receiver.getResult();
+ final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status != PackageInstaller.STATUS_SUCCESS) {
+ final String errorMessage = result.getStringExtra(
+ PackageInstaller.EXTRA_STATUS_MESSAGE);
+ Slog.e(TAG, "Failure to install APK staged session "
+ + session.sessionId + " [" + errorMessage + "]");
+ throw new PackageManagerException(
+ SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMessage);
}
- // APEX single-package staged session, nothing to do.
}
void commitSession(@NonNull PackageInstallerSession session) {
@@ -836,8 +853,8 @@ public class StagingManager {
Slog.d(TAG, "Running a pre-reboot verification for APKs in session "
+ session.sessionId + " by performing a dry-run install");
- // installApksInSession will notify the handler when APK verification is complete
- installApksInSession(session, /* preReboot */ true);
+ // verifyApksInSession will notify the handler when APK verification is complete
+ verifyApksInSession(session);
// TODO(b/118865310): abort the session on apexd.
} catch (PackageManagerException e) {
session.setStagedSessionFailed(e.error, e.getMessage());
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 9371c4473bb3..cfc5ca0f11cd 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -41,6 +41,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.UserInfo;
+import android.content.pm.UserInfo.UserInfoFlag;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.os.Binder;
@@ -53,6 +54,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.IProgressListener;
import android.os.IUserManager;
+import android.os.IUserRestrictionsListener;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
@@ -158,6 +160,7 @@ public class UserManagerService extends IUserManager.Stub {
private static final String ATTR_SERIAL_NO = "serialNumber";
private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber";
private static final String ATTR_PARTIAL = "partial";
+ private static final String ATTR_PRE_CREATED = "preCreated";
private static final String ATTR_GUEST_TO_REMOVE = "guestToRemove";
private static final String ATTR_USER_VERSION = "version";
private static final String ATTR_PROFILE_GROUP_ID = "profileGroupId";
@@ -207,7 +210,8 @@ public class UserManagerService extends IUserManager.Stub {
| UserInfo.FLAG_DEMO;
@VisibleForTesting
- static final int MIN_USER_ID = 10;
+ static final int MIN_USER_ID = UserHandle.MIN_SECONDARY_USER_ID;
+
// We need to keep process uid within Integer.MAX_VALUE.
@VisibleForTesting
static final int MAX_USER_ID = Integer.MAX_VALUE / UserHandle.PER_USER_RANGE;
@@ -250,6 +254,9 @@ public class UserManagerService extends IUserManager.Stub {
private static final IBinder mUserRestriconToken = new Binder();
+ /** Installs system packages based on user-type. */
+ private final UserSystemPackageInstaller mSystemPackageInstaller;
+
/**
* Internal non-parcelable wrapper for UserInfo that is not exposed to other system apps.
*/
@@ -550,6 +557,7 @@ public class UserManagerService extends IUserManager.Stub {
readUserListLP();
sInstance = this;
}
+ mSystemPackageInstaller = new UserSystemPackageInstaller(this);
mLocalService = new LocalService();
LocalServices.addService(UserManagerInternal.class, mLocalService);
mLockPatternUtils = new LockPatternUtils(mContext);
@@ -593,7 +601,8 @@ public class UserManagerService extends IUserManager.Stub {
final int userSize = mUsers.size();
for (int i = 0; i < userSize; i++) {
UserInfo ui = mUsers.valueAt(i).info;
- if ((ui.partial || ui.guestToRemove || ui.isEphemeral()) && i != 0) {
+ if ((ui.partial || ui.guestToRemove || (ui.isEphemeral() && !ui.preCreated))
+ && i != 0) {
partials.add(ui);
addRemovingUserIdLocked(ui.id);
ui.partial = true;
@@ -656,20 +665,25 @@ public class UserManagerService extends IUserManager.Stub {
return null;
}
- @Override
public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
+ return getUsers(/*excludePartial= */ true, excludeDying, /* excludePreCreated= */ true);
+ }
+
+ @Override
+ public @NonNull List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying,
+ boolean excludePreCreated) {
checkManageOrCreateUsersPermission("query users");
synchronized (mUsersLock) {
ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
final int userSize = mUsers.size();
for (int i = 0; i < userSize; i++) {
UserInfo ui = mUsers.valueAt(i).info;
- if (ui.partial) {
+ if ((excludePartial && ui.partial)
+ || (excludeDying && mRemovingUserIds.get(ui.id))
+ || (excludePreCreated && ui.preCreated)) {
continue;
}
- if (!excludeDying || !mRemovingUserIds.get(ui.id)) {
- users.add(userWithName(ui));
- }
+ users.add(userWithName(ui));
}
return users;
}
@@ -1192,7 +1206,7 @@ public class UserManagerService extends IUserManager.Stub {
private void checkManageOrInteractPermIfCallerInOtherProfileGroup(@UserIdInt int userId,
String name) {
- int callingUserId = UserHandle.getCallingUserId();
+ final int callingUserId = UserHandle.getCallingUserId();
if (callingUserId == userId || isSameProfileGroupNoChecks(callingUserId, userId) ||
hasManageUsersPermission()) {
return;
@@ -1206,7 +1220,7 @@ public class UserManagerService extends IUserManager.Stub {
@Override
public boolean isDemoUser(@UserIdInt int userId) {
- int callingUserId = UserHandle.getCallingUserId();
+ final int callingUserId = UserHandle.getCallingUserId();
if (callingUserId != userId && !hasManageUsersPermission()) {
throw new SecurityException("You need MANAGE_USERS permission to query if u=" + userId
+ " is a demo user");
@@ -1218,6 +1232,19 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
+ public boolean isPreCreated(@UserIdInt int userId) {
+ final int callingUserId = UserHandle.getCallingUserId();
+ if (callingUserId != userId && !hasManageUsersPermission()) {
+ throw new SecurityException("You need MANAGE_USERS permission to query if u=" + userId
+ + " is pre-created");
+ }
+ synchronized (mUsersLock) {
+ UserInfo userInfo = getUserInfoLU(userId);
+ return userInfo != null && userInfo.preCreated;
+ }
+ }
+
+ @Override
public boolean isRestricted() {
synchronized (mUsersLock) {
return getUserInfoLU(UserHandle.getCallingUserId()).isRestricted();
@@ -1580,6 +1607,36 @@ public class UserManagerService extends IUserManager.Stub {
return false;
}
+ @Override
+ public boolean isSettingRestrictedForUser(String setting, @UserIdInt int userId,
+ String value, int callingUid) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Non-system caller");
+ }
+ return UserRestrictionsUtils.isSettingRestrictedForUser(mContext, setting, userId,
+ value, callingUid);
+ }
+
+ @Override
+ public void addUserRestrictionsListener(final IUserRestrictionsListener listener) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Non-system caller");
+ }
+
+ // NOTE: unregistering not supported; only client is the settings provider,
+ // which installs a single static permanent listener. If that listener goes
+ // bad it implies the whole system process is going to crash.
+ mLocalService.addUserRestrictionsListener(
+ (int userId, Bundle newRestrict, Bundle prevRestrict) -> {
+ try {
+ listener.onUserRestrictionsChanged(userId, newRestrict, prevRestrict);
+ } catch (RemoteException re) {
+ Slog.e("IUserRestrictionsListener",
+ "Unable to invoke listener: " + re.getMessage());
+ }
+ });
+ }
+
/**
* @hide
*
@@ -1867,7 +1924,7 @@ public class UserManagerService extends IUserManager.Stub {
// Skip over users being removed
for (int i = 0; i < totalUserCount; i++) {
UserInfo user = mUsers.valueAt(i).info;
- if (!mRemovingUserIds.get(user.id) && !user.isGuest()) {
+ if (!mRemovingUserIds.get(user.id) && !user.isGuest() && !user.preCreated) {
aliveUserCount++;
}
}
@@ -2358,6 +2415,9 @@ public class UserManagerService extends IUserManager.Stub {
if (userInfo.partial) {
serializer.attribute(null, ATTR_PARTIAL, "true");
}
+ if (userInfo.preCreated) {
+ serializer.attribute(null, ATTR_PRE_CREATED, "true");
+ }
if (userInfo.guestToRemove) {
serializer.attribute(null, ATTR_GUEST_TO_REMOVE, "true");
}
@@ -2514,6 +2574,7 @@ public class UserManagerService extends IUserManager.Stub {
int profileBadge = 0;
int restrictedProfileParentId = UserInfo.NO_PROFILE_GROUP_ID;
boolean partial = false;
+ boolean preCreated = false;
boolean guestToRemove = false;
boolean persistSeedData = false;
String seedAccountName = null;
@@ -2558,6 +2619,10 @@ public class UserManagerService extends IUserManager.Stub {
if ("true".equals(valueString)) {
partial = true;
}
+ valueString = parser.getAttributeValue(null, ATTR_PRE_CREATED);
+ if ("true".equals(valueString)) {
+ preCreated = true;
+ }
valueString = parser.getAttributeValue(null, ATTR_GUEST_TO_REMOVE);
if ("true".equals(valueString)) {
guestToRemove = true;
@@ -2611,6 +2676,7 @@ public class UserManagerService extends IUserManager.Stub {
userInfo.lastLoggedInTime = lastLoggedInTime;
userInfo.lastLoggedInFingerprint = lastLoggedInFingerprint;
userInfo.partial = partial;
+ userInfo.preCreated = preCreated;
userInfo.guestToRemove = guestToRemove;
userInfo.profileGroupId = profileGroupId;
userInfo.profileBadge = profileBadge;
@@ -2682,7 +2748,8 @@ public class UserManagerService extends IUserManager.Stub {
public UserInfo createProfileForUserEvenWhenDisallowed(String name, int flags,
@UserIdInt int userId, String[] disallowedPackages) {
checkManageOrCreateUsersPermission(flags);
- return createUserInternalUnchecked(name, flags, userId, disallowedPackages);
+ return createUserInternalUnchecked(name, flags, userId, /* preCreate= */ false,
+ disallowedPackages);
}
@Override
@@ -2697,12 +2764,27 @@ public class UserManagerService extends IUserManager.Stub {
return createUserInternal(name, flags, UserHandle.USER_NULL);
}
- private UserInfo createUserInternal(String name, int flags, int parentId) {
+ @Override
+ public UserInfo preCreateUser(int flags) {
+ checkManageOrCreateUsersPermission(flags);
+
+ Preconditions.checkArgument(!UserInfo.isManagedProfile(flags),
+ "cannot pre-create managed profiles");
+
+ Slog.i(LOG_TAG, "Pre-creating user with flags " + UserInfo.flagsToString(flags));
+
+ return createUserInternalUnchecked(/* name= */ null, flags,
+ /* parentId= */ UserHandle.USER_NULL, /* preCreate= */ true,
+ /* disallowedPackages= */ null);
+ }
+
+ private UserInfo createUserInternal(@Nullable String name, @UserInfoFlag int flags,
+ @UserIdInt int parentId) {
return createUserInternal(name, flags, parentId, null);
}
- private UserInfo createUserInternal(String name, int flags, int parentId,
- String[] disallowedPackages) {
+ private UserInfo createUserInternal(@Nullable String name, @UserInfoFlag int flags,
+ @UserIdInt int parentId, @Nullable String[] disallowedPackages) {
String restriction = ((flags & UserInfo.FLAG_MANAGED_PROFILE) != 0)
? UserManager.DISALLOW_ADD_MANAGED_PROFILE
: UserManager.DISALLOW_ADD_USER;
@@ -2710,21 +2792,56 @@ public class UserManagerService extends IUserManager.Stub {
Log.w(LOG_TAG, "Cannot add user. " + restriction + " is enabled.");
return null;
}
- return createUserInternalUnchecked(name, flags, parentId, disallowedPackages);
+ return createUserInternalUnchecked(name, flags, parentId, /* preCreate= */ false,
+ disallowedPackages);
}
- private UserInfo createUserInternalUnchecked(@Nullable String name, int flags,
- int parentId, @Nullable String[] disallowedPackages) {
- TimingsTraceAndSlog t = new TimingsTraceAndSlog();
- t.traceBegin("createUser");
- UserInfo userInfo =
- createUserInternalUncheckedNoTracing(name, flags, parentId, disallowedPackages, t);
- t.traceEnd();
- return userInfo;
+ private UserInfo createUserInternalUnchecked(@Nullable String name, @UserInfoFlag int flags,
+ @UserIdInt int parentId, boolean preCreate,
+ @Nullable String[] disallowedPackages) {
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("createUser-" + flags);
+ try {
+ return createUserInternalUncheckedNoTracing(name, flags, parentId, preCreate,
+ disallowedPackages, t);
+ } finally {
+ t.traceEnd();
+ }
}
- private UserInfo createUserInternalUncheckedNoTracing(@Nullable String name, int flags,
- int parentId, @Nullable String[] disallowedPackages, @NonNull TimingsTraceAndSlog t) {
+ private UserInfo createUserInternalUncheckedNoTracing(@Nullable String name,
+ @UserInfoFlag int flags, @UserIdInt int parentId, boolean preCreate,
+ @Nullable String[] disallowedPackages, @NonNull TimingsTraceAndSlog t) {
+
+ // First try to use a pre-created user (if available).
+ // NOTE: currently we don't support pre-created managed profiles
+ if (!preCreate && (parentId < 0 && !UserInfo.isManagedProfile(flags))) {
+ final UserData preCreatedUserData;
+ synchronized (mUsersLock) {
+ preCreatedUserData = getPreCreatedUserLU(flags);
+ }
+ if (preCreatedUserData != null) {
+ final UserInfo preCreatedUser = preCreatedUserData.info;
+ Log.i(LOG_TAG, "Reusing pre-created user " + preCreatedUser.id + " for flags + "
+ + UserInfo.flagsToString(flags));
+ if (DBG) {
+ Log.d(LOG_TAG, "pre-created user flags: "
+ + UserInfo.flagsToString(preCreatedUser.flags)
+ + " new-user flags: " + UserInfo.flagsToString(flags));
+ }
+ preCreatedUser.name = name;
+ preCreatedUser.preCreated = false;
+ preCreatedUser.creationTime = getCreationTime();
+
+ dispatchUserAddedIntent(preCreatedUser);
+
+ writeUserLP(preCreatedUserData);
+ writeUserListLP();
+
+ return preCreatedUser;
+ }
+ }
+
DeviceStorageMonitorInternal dsm = LocalServices
.getService(DeviceStorageMonitorInternal.class);
if (dsm.isMemoryLow()) {
@@ -2732,8 +2849,8 @@ public class UserManagerService extends IUserManager.Stub {
return null;
}
- final boolean isGuest = (flags & UserInfo.FLAG_GUEST) != 0;
- final boolean isManagedProfile = (flags & UserInfo.FLAG_MANAGED_PROFILE) != 0;
+ final boolean isGuest = UserInfo.isGuest(flags);
+ final boolean isManagedProfile = UserInfo.isManagedProfile(flags);
final boolean isRestricted = (flags & UserInfo.FLAG_RESTRICTED) != 0;
final boolean isDemo = (flags & UserInfo.FLAG_DEMO) != 0;
final long ident = Binder.clearCallingIdentity();
@@ -2754,13 +2871,13 @@ public class UserManagerService extends IUserManager.Stub {
return null;
}
if (!isGuest && !isManagedProfile && !isDemo && isUserLimitReached()) {
- // If we're not adding a guest/demo user or a managed profile and the limit has
- // been reached, cannot add a user.
+ // If we're not adding a guest/demo user or a managed profile,
+ // and the limit has been reached, cannot add a user.
Log.e(LOG_TAG, "Cannot add user. Maximum user limit is reached.");
return null;
}
// If we're adding a guest and there already exists one, bail.
- if (isGuest && findCurrentGuestUser() != null) {
+ if (isGuest && !preCreate && findCurrentGuestUser() != null) {
Log.e(LOG_TAG, "Cannot add guest user. Guest user already exists.");
return null;
}
@@ -2790,8 +2907,7 @@ public class UserManagerService extends IUserManager.Stub {
userId = getNextAvailableId();
Environment.getUserSystemDirectory(userId).mkdirs();
- boolean ephemeralGuests = Resources.getSystem()
- .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral);
+ boolean ephemeralGuests = areGuestUsersEphemeral();
synchronized (mUsersLock) {
// Add ephemeral flag to guests/users if required. Also inherit it from parent.
@@ -2802,9 +2918,9 @@ public class UserManagerService extends IUserManager.Stub {
userInfo = new UserInfo(userId, name, null, flags);
userInfo.serialNumber = mNextSerialNumber++;
- long now = System.currentTimeMillis();
- userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0;
+ userInfo.creationTime = getCreationTime();
userInfo.partial = true;
+ userInfo.preCreated = preCreate;
userInfo.lastLoggedInFingerprint = Build.FINGERPRINT;
if (isManagedProfile && parentId != UserHandle.USER_NULL) {
userInfo.profileBadge = getFreeProfileBadgeLU(parentId);
@@ -2842,8 +2958,10 @@ public class UserManagerService extends IUserManager.Stub {
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
t.traceEnd();
+ final Set<String> installablePackages =
+ mSystemPackageInstaller.getInstallablePackagesForUserType(flags);
t.traceBegin("PM.createNewUser");
- mPm.createNewUser(userId, disallowedPackages);
+ mPm.createNewUser(userId, installablePackages, disallowedPackages);
t.traceEnd();
userInfo.partial = false;
@@ -2863,20 +2981,104 @@ public class UserManagerService extends IUserManager.Stub {
t.traceBegin("PM.onNewUserCreated");
mPm.onNewUserCreated(userId);
- t.traceEnd();
- Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
- addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
- android.Manifest.permission.MANAGE_USERS);
- MetricsLogger.count(mContext, isGuest ? TRON_GUEST_CREATED
- : (isDemo ? TRON_DEMO_CREATED : TRON_USER_CREATED), 1);
+ if (preCreate) {
+ // Must start user (which will be stopped right away, through
+ // UserController.finishUserUnlockedCompleted) so services can properly
+ // intialize it.
+ // TODO(b/140750212): in the long-term, we should add a onCreateUser() callback
+ // on SystemService instead.
+ Slog.i(LOG_TAG, "starting pre-created user " + userInfo.toFullString());
+ final IActivityManager am = ActivityManager.getService();
+ try {
+ am.startUserInBackground(userId);
+ } catch (RemoteException e) {
+ Slog.w(LOG_TAG, "could not start pre-created user " + userId, e);
+ }
+ } else {
+ dispatchUserAddedIntent(userInfo);
+ }
+
} finally {
Binder.restoreCallingIdentity(ident);
}
+
+ // TODO(b/140750212): it's possible to reach "max users overflow" when the user is created
+ // "from scratch" (i.e., not from a pre-created user) and reaches the maximum number of
+ // users without counting the pre-created one. Then when the pre-created is converted, the
+ // "effective" number of max users is exceeds. Example:
+ // Max: 3 Current: 2 full (u0 and u10) + 1 pre-created (u11)
+ // Step 1: create(/* flags doesn't match u11 */): u12 is created, "effective max" is now 3
+ // (u0, u10, u12) but "real" max is 4 (u0, u10, u11, u12)
+ // Step 2: create(/* flags match u11 */): u11 is converted, now "effective max" is also 4
+ // (u0, u10, u11, u12)
+ // One way to avoid this issue is by removing a pre-created user from the pool when the
+ // "real" max exceeds the max here.
+
return userInfo;
}
+ /** Install/uninstall system packages for all users based on their user-type, as applicable. */
+ boolean installWhitelistedSystemPackages(boolean isFirstBoot, boolean isUpgrade) {
+ return mSystemPackageInstaller.installWhitelistedSystemPackages(isFirstBoot, isUpgrade);
+ }
+
+ private long getCreationTime() {
+ final long now = System.currentTimeMillis();
+ return (now > EPOCH_PLUS_30_YEARS) ? now : 0;
+ }
+
+ private void dispatchUserAddedIntent(@NonNull UserInfo userInfo) {
+ Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
+ addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id);
+ mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
+ android.Manifest.permission.MANAGE_USERS);
+ MetricsLogger.count(mContext, userInfo.isGuest() ? TRON_GUEST_CREATED
+ : (userInfo.isDemo() ? TRON_DEMO_CREATED : TRON_USER_CREATED), 1);
+ }
+
+ private boolean areGuestUsersEphemeral() {
+ return Resources.getSystem()
+ .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral);
+ }
+
+ /**
+ * Gets a pre-created user for the given flag.
+ *
+ * <p>Should be used only during user creation, so the pre-created user can be used (instead of
+ * creating and initializing a new user from scratch).
+ */
+ // TODO(b/140750212): add unit test
+ @GuardedBy("mUsersLock")
+ private @Nullable UserData getPreCreatedUserLU(@UserInfoFlag int flags) {
+ if (DBG) {
+ Slog.d(LOG_TAG, "getPreCreatedUser(): initialFlags= " + UserInfo.flagsToString(flags));
+ }
+ flags |= UserInfo.FLAG_FULL;
+ if (UserInfo.isGuest(flags) && areGuestUsersEphemeral()) {
+ flags |= UserInfo.FLAG_EPHEMERAL;
+ }
+ if (DBG) {
+ Slog.d(LOG_TAG, "getPreCreatedUser(): targetFlags= " + UserInfo.flagsToString(flags));
+ }
+ final int userSize = mUsers.size();
+ for (int i = 0; i < userSize; i++) {
+ final UserData user = mUsers.valueAt(i);
+ if (DBG) Slog.d(LOG_TAG, i + ":" + user.info.toFullString());
+ if (user.info.preCreated
+ && (user.info.flags & ~UserInfo.FLAG_INITIALIZED) == flags) {
+ if (!user.info.isInitialized()) {
+ Slog.w(LOG_TAG, "found pre-created user for flags "
+ + "" + UserInfo.flagsToString(flags)
+ + ", but it's not initialized yet: " + user.info.toFullString());
+ continue;
+ }
+ return user;
+ }
+ }
+ return null;
+ }
+
@VisibleForTesting
UserData putUserInfo(UserInfo userInfo) {
final UserData userData = new UserData();
@@ -2929,7 +3131,8 @@ public class UserManagerService extends IUserManager.Stub {
final int size = mUsers.size();
for (int i = 0; i < size; i++) {
final UserInfo user = mUsers.valueAt(i).info;
- if (user.isGuest() && !user.guestToRemove && !mRemovingUserIds.get(user.id)) {
+ if (user.isGuest() && !user.guestToRemove && !user.preCreated
+ && !mRemovingUserIds.get(user.id)) {
return user;
}
}
@@ -3717,7 +3920,7 @@ public class UserManagerService extends IUserManager.Stub {
try {
switch(cmd) {
case "list":
- return runList(pw);
+ return runList(pw, shell);
default:
return shell.handleDefaultCommands(cmd);
}
@@ -3727,17 +3930,58 @@ public class UserManagerService extends IUserManager.Stub {
return -1;
}
- private int runList(PrintWriter pw) throws RemoteException {
+ private int runList(PrintWriter pw, Shell shell) throws RemoteException {
+ boolean all = false;
+ boolean verbose = false;
+ String opt;
+ while ((opt = shell.getNextOption()) != null) {
+ switch (opt) {
+ case "-v":
+ verbose = true;
+ break;
+ case "--all":
+ all = true;
+ break;
+ default:
+ pw.println("Invalid option: " + opt);
+ return -1;
+ }
+ }
final IActivityManager am = ActivityManager.getService();
- final List<UserInfo> users = getUsers(false);
+ final List<UserInfo> users = getUsers(/* excludePartial= */ !all,
+ /* excludingDying=*/ false, /* excludePreCreated= */ !all);
if (users == null) {
pw.println("Error: couldn't get users");
return 1;
} else {
- pw.println("Users:");
- for (int i = 0; i < users.size(); i++) {
- String running = am.isUserRunning(users.get(i).id, 0) ? " running" : "";
- pw.println("\t" + users.get(i).toString() + running);
+ final int size = users.size();
+ int currentUser = UserHandle.USER_NULL;
+ if (verbose) {
+ pw.printf("%d users:\n\n", size);
+ currentUser = am.getCurrentUser().id;
+ } else {
+ // NOTE: the standard "list users" command is used by integration tests and
+ // hence should not be changed. If you need to add more info, use the
+ // verbose option.
+ pw.println("Users:");
+ }
+ for (int i = 0; i < size; i++) {
+ final UserInfo user = users.get(i);
+ final boolean running = am.isUserRunning(user.id, 0);
+ final boolean current = user.id == currentUser;
+ if (verbose) {
+ pw.printf("%d: id=%d, name=%s, flags=%s%s%s%s%s\n", i, user.id, user.name,
+ UserInfo.flagsToString(user.flags),
+ running ? " (running)" : "",
+ user.partial ? " (partial)" : "",
+ user.preCreated ? " (pre-created)" : "",
+ current ? " (current)" : "");
+ } else {
+ // NOTE: the standard "list users" command is used by integration tests and
+ // hence should not be changed. If you need to add more info, use the
+ // verbose option.
+ pw.printf("\t%s%s\n", user, running ? " running" : "");
+ }
}
return 0;
}
@@ -3774,6 +4018,9 @@ public class UserManagerService extends IUserManager.Stub {
if (userInfo.partial) {
pw.print(" <partial>");
}
+ if (userInfo.preCreated) {
+ pw.print(" <pre-created>");
+ }
pw.println();
pw.print(" Flags: "); pw.print(userInfo.flags); pw.print(" (");
pw.print(UserInfo.flagsToString(userInfo.flags)); pw.println(")");
@@ -3856,13 +4103,17 @@ public class UserManagerService extends IUserManager.Stub {
// Dump some capabilities
pw.println();
- pw.println(" Max users: " + UserManager.getMaxSupportedUsers());
+ pw.print(" Max users: " + UserManager.getMaxSupportedUsers());
+ pw.println(" (limit reached: " + isUserLimitReached() + ")");
pw.println(" Supports switchable users: " + UserManager.supportsMultipleUsers());
- pw.println(" All guests ephemeral: " + Resources.getSystem().getBoolean(
- com.android.internal.R.bool.config_guestUserEphemeral));
+ pw.println(" All guests ephemeral: " + areGuestUsersEphemeral());
pw.println(" Is split-system user: " + UserManager.isSplitSystemUser());
pw.println(" Is headless-system mode: " + UserManager.isHeadlessSystemUserMode());
pw.println(" User version: " + mUserVersion);
+
+ // Dump package whitelist
+ pw.println();
+ mSystemPackageInstaller.dump(pw);
}
private static void dumpTimeAgo(PrintWriter pw, StringBuilder sb, long nowTime, long time) {
@@ -3964,6 +4215,13 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
+ public boolean isDeviceManaged() {
+ synchronized (mUsersLock) {
+ return mIsDeviceManaged;
+ }
+ }
+
+ @Override
public void setUserManaged(@UserIdInt int userId, boolean isManaged) {
synchronized (mUsersLock) {
mIsUserManaged.put(userId, isManaged);
@@ -3971,6 +4229,13 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
+ public boolean isUserManaged(@UserIdInt int userId) {
+ synchronized (mUsersLock) {
+ return mIsUserManaged.get(userId);
+ }
+ }
+
+ @Override
public void setUserIcon(@UserIdInt int userId, Bitmap bitmap) {
long ident = Binder.clearCallingIdentity();
try {
@@ -4047,7 +4312,7 @@ public class UserManagerService extends IUserManager.Stub {
public UserInfo createUserEvenWhenDisallowed(String name, int flags,
String[] disallowedPackages) {
UserInfo user = createUserInternalUnchecked(name, flags, UserHandle.USER_NULL,
- disallowedPackages);
+ /* preCreated= */ false, disallowedPackages);
// Keep this in sync with UserManager.createUser
if (user != null && !user.isAdmin() && !user.isDemo()) {
setUserRestriction(UserManager.DISALLOW_SMS, true, user.id);
@@ -4177,7 +4442,7 @@ public class UserManagerService extends IUserManager.Stub {
@Override
public boolean isSettingRestrictedForUser(String setting, @UserIdInt int userId,
String value, int callingUid) {
- return UserRestrictionsUtils.isSettingRestrictedForUser(mContext, setting, userId,
+ return UserManagerService.this.isSettingRestrictedForUser(setting, userId,
value, callingUid);
}
@@ -4190,6 +4455,7 @@ public class UserManagerService extends IUserManager.Stub {
return restrictions != null && restrictions.getBoolean(restrictionKey);
}
+ @Override
public @Nullable UserInfo getUserInfo(@UserIdInt int userId) {
UserData userData;
synchronized (mUsersLock) {
@@ -4240,7 +4506,7 @@ public class UserManagerService extends IUserManager.Stub {
pw.println(" help");
pw.println(" Print this help text.");
pw.println("");
- pw.println(" list");
+ pw.println(" list [-v] [-all]");
pw.println(" Prints all users on the system.");
}
}
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
new file mode 100644
index 000000000000..323c957acdd0
--- /dev/null
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -0,0 +1,466 @@
+/*
+ * 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.pm;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageParser;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+import com.android.server.SystemConfig;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Set;
+
+/**
+ * Responsible for un/installing system packages based on user type.
+ *
+ * <p>Uses the SystemConfig's install-in-user-type whitelist;
+ * see {@link SystemConfig#getAndClearPackageToUserTypeWhitelist} and
+ * {@link SystemConfig#getAndClearPackageToUserTypeBlacklist}.
+ *
+ * <p>If {@link #isEnforceMode()} is false, then all system packages are always installed for all
+ * users. The following applies when it is true.
+ *
+ * Any package can be in one of three states in the SystemConfig whitelist
+ * <ol>
+ * <li>Explicitly blacklisted for a particular user type</li>
+ * <li>Explicitly whitelisted for a particular user type</li>
+ * <li>Not mentioned at all, for any user type (neither whitelisted nor blacklisted)</li>
+ * </ol>
+ * Blacklisting always takes precedence - if a package is blacklisted for a particular user,
+ * it won't be installed on that type of user (even if it is also whitelisted for that user).
+ * Next comes whitelisting - if it is whitelisted for a particular user, it will be installed on
+ * that type of user (as long as it isn't blacklisted).
+ * Finally, if the package is not mentioned at all (i.e. neither whitelisted nor blacklisted for
+ * any user types) in the SystemConfig 'install-in-user-type' lists
+ * then:
+ * <ul>
+ * <li>If {@link #isImplicitWhitelistMode()}, the package is implicitly treated as whitelisted
+ * for all users</li>
+ * <li>Otherwise, the package is implicitly treated as blacklisted for all non-SYSTEM users</li>
+ * <li>Either way, for {@link UserHandle#USER_SYSTEM}, the package will be implicitly
+ * whitelisted so that it can be used for local development purposes.</li>
+ * </ul>
+ */
+class UserSystemPackageInstaller {
+ private static final String TAG = "UserManagerService";
+
+ /**
+ * System Property whether to only install system packages on a user if they're whitelisted for
+ * that user type. These are flags and can be freely combined.
+ * <ul>
+ * <li> 0 (0b000) - disable whitelist (install all system packages; no logging)</li>
+ * <li> 1 (0b001) - enforce (only install system packages if they are whitelisted)</li>
+ * <li> 2 (0b010) - log (log when a non-whitelisted package is run)</li>
+ * <li> 4 (0b100) - implicitly whitelist any package not mentioned in the whitelist</li>
+ * <li>-1 - use device default (as defined in res/res/values/config.xml)</li>
+ * </ul>
+ * Note: This list must be kept current with config_userTypePackageWhitelistMode in
+ * frameworks/base/core/res/res/values/config.xml
+ */
+ static final String PACKAGE_WHITELIST_MODE_PROP = "persist.debug.user.package_whitelist_mode";
+ static final int USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE = 0;
+ static final int USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE = 0b001;
+ static final int USER_TYPE_PACKAGE_WHITELIST_MODE_LOG = 0b010;
+ static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST = 0b100;
+ static final int USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT = -1;
+
+ @IntDef(flag = true, prefix = "USER_TYPE_PACKAGE_WHITELIST_MODE_", value = {
+ USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE,
+ USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE,
+ USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE,
+ USER_TYPE_PACKAGE_WHITELIST_MODE_LOG,
+ USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PackageWhitelistMode {}
+
+ /**
+ * Maps system package manifest names to the user flags on which they should be initially
+ * installed.
+ * <p>Packages that are whitelisted, but then blacklisted so that they aren't to be installed on
+ * any user, are purposefully still present in this list.
+ */
+ private final ArrayMap<String, Integer> mWhitelitsedPackagesForUserTypes;
+
+ private final UserManagerService mUm;
+
+ UserSystemPackageInstaller(UserManagerService ums) {
+ mUm = ums;
+ mWhitelitsedPackagesForUserTypes =
+ determineWhitelistedPackagesForUserTypes(SystemConfig.getInstance());
+ }
+
+ /** Constructor for testing purposes. */
+ @VisibleForTesting
+ UserSystemPackageInstaller(UserManagerService ums, ArrayMap<String, Integer> whitelist) {
+ mUm = ums;
+ mWhitelitsedPackagesForUserTypes = whitelist;
+ }
+
+ /**
+ * During OTAs and first boot, install/uninstall all system packages for all users based on the
+ * user's UserInfo flags and the SystemConfig whitelist.
+ * We do NOT uninstall packages during an OTA though.
+ *
+ * This is responsible for enforcing the whitelist for pre-existing users (i.e. USER_SYSTEM);
+ * enforcement for new users is done when they are created in UserManagerService.createUser().
+ */
+ boolean installWhitelistedSystemPackages(boolean isFirstBoot, boolean isUpgrade) {
+ final int mode = getWhitelistMode();
+ checkWhitelistedSystemPackages(mode);
+ if (!isUpgrade && !isFirstBoot) {
+ return false;
+ }
+ Slog.i(TAG, "Reviewing whitelisted packages due to "
+ + (isFirstBoot ? "[firstBoot]" : "") + (isUpgrade ? "[upgrade]" : ""));
+ final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
+ // Install/uninstall system packages per user.
+ for (int userId : mUm.getUserIds()) {
+ final Set<String> userWhitelist = getInstallablePackagesForUserId(userId);
+ pmInt.forEachPackage(pkg -> {
+ if (!pkg.isSystem()) {
+ return;
+ }
+ final boolean install =
+ (userWhitelist == null || userWhitelist.contains(pkg.packageName))
+ && !pkg.applicationInfo.hiddenUntilInstalled;
+ if (isUpgrade && !isFirstBoot && !install) {
+ return; // To be careful, we don’t uninstall apps during OTAs
+ }
+ final boolean changed = pmInt.setInstalled(pkg, userId, install);
+ if (changed) {
+ Slog.i(TAG, (install ? "Installed " : "Uninstalled ")
+ + pkg.packageName + " for user " + userId);
+ }
+ });
+ }
+ return true;
+ }
+
+ /**
+ * Checks whether the system packages and the mWhitelistedPackagesForUserTypes whitelist are
+ * in 1-to-1 correspondence.
+ */
+ private void checkWhitelistedSystemPackages(@PackageWhitelistMode int mode) {
+ if (!isLogMode(mode) && !isEnforceMode(mode)) {
+ return;
+ }
+ Slog.v(TAG, "Checking that all system packages are whitelisted.");
+ final Set<String> allWhitelistedPackages = getWhitelistedSystemPackages();
+ PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
+
+ // Check whether all whitelisted packages are indeed on the system.
+ for (String pkgName : allWhitelistedPackages) {
+ PackageParser.Package pkg = pmInt.getPackage(pkgName);
+ if (pkg == null) {
+ Slog.w(TAG, pkgName + " is whitelisted but not present.");
+ } else if (!pkg.isSystem()) {
+ Slog.w(TAG, pkgName + " is whitelisted and present but not a system package.");
+ }
+ }
+
+ // Check whether all system packages are indeed whitelisted.
+ if (isImplicitWhitelistMode(mode) && !isLogMode(mode)) {
+ return;
+ }
+ final boolean doWtf = isEnforceMode(mode);
+ pmInt.forEachPackage(pkg -> {
+ if (pkg.isSystem() && !allWhitelistedPackages.contains(pkg.manifestPackageName)) {
+ final String msg = "System package " + pkg.manifestPackageName
+ + " is not whitelisted using 'install-in-user-type' in SystemConfig "
+ + "for any user types!";
+ if (doWtf) {
+ Slog.wtf(TAG, msg);
+ } else {
+ Slog.e(TAG, msg);
+ }
+ }
+ });
+ }
+
+ /** Whether to only install system packages in new users for which they are whitelisted. */
+ boolean isEnforceMode() {
+ return isEnforceMode(getWhitelistMode());
+ }
+
+ /**
+ * Whether to log a warning concerning potential problems with the user-type package whitelist.
+ */
+ boolean isLogMode() {
+ return isLogMode(getWhitelistMode());
+ }
+
+ /**
+ * Whether to treat all packages that are not mentioned at all in the whitelist to be implicitly
+ * whitelisted for all users.
+ */
+ boolean isImplicitWhitelistMode() {
+ return isImplicitWhitelistMode(getWhitelistMode());
+ }
+
+ /** See {@link #isEnforceMode()}. */
+ private static boolean isEnforceMode(int whitelistMode) {
+ return (whitelistMode & USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE) != 0;
+ }
+
+ /** See {@link #isLogMode()}. */
+ private static boolean isLogMode(int whitelistMode) {
+ return (whitelistMode & USER_TYPE_PACKAGE_WHITELIST_MODE_LOG) != 0;
+ }
+
+ /** See {@link #isImplicitWhitelistMode()}. */
+ private static boolean isImplicitWhitelistMode(int whitelistMode) {
+ return (whitelistMode & USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST) != 0;
+ }
+
+ /** Gets the PackageWhitelistMode for use of {@link #mWhitelitsedPackagesForUserTypes}. */
+ private @PackageWhitelistMode int getWhitelistMode() {
+ final int runtimeMode = SystemProperties.getInt(
+ PACKAGE_WHITELIST_MODE_PROP, USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT);
+ if (runtimeMode != USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT) {
+ return runtimeMode;
+ }
+ return Resources.getSystem()
+ .getInteger(com.android.internal.R.integer.config_userTypePackageWhitelistMode);
+ }
+
+ /**
+ * Gets the system packages names that should be installed on the given user.
+ * See {@link #getInstallablePackagesForUserType(int)}.
+ */
+ private @Nullable Set<String> getInstallablePackagesForUserId(@UserIdInt int userId) {
+ return getInstallablePackagesForUserType(mUm.getUserInfo(userId).flags);
+ }
+
+ /**
+ * Gets the system package names that should be installed on a user with the given flags, as
+ * determined by SystemConfig, the whitelist mode, and the apps actually on the device.
+ * Names are the {@link PackageParser.Package#packageName}, not necessarily the manifest names.
+ *
+ * Returns null if all system packages should be installed (due enforce-mode being off).
+ */
+ @Nullable Set<String> getInstallablePackagesForUserType(int flags) {
+ final int mode = getWhitelistMode();
+ if (!isEnforceMode(mode)) {
+ return null;
+ }
+ final boolean isSystemUser = (flags & UserInfo.FLAG_SYSTEM) != 0;
+ final boolean isImplicitWhitelistMode = isImplicitWhitelistMode(mode);
+ final Set<String> whitelistedPackages = getWhitelistedPackagesForUserType(flags);
+
+ final Set<String> installPackages = new ArraySet<>();
+ final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
+ pmInt.forEachPackage(pkg -> {
+ if (!pkg.isSystem()) {
+ return;
+ }
+ if (shouldInstallPackage(pkg, mWhitelitsedPackagesForUserTypes,
+ whitelistedPackages, isImplicitWhitelistMode, isSystemUser)) {
+ // Although the whitelist uses manifest names, this function returns packageNames.
+ installPackages.add(pkg.packageName);
+ }
+ });
+ return installPackages;
+ }
+
+ /**
+ * Returns whether the given system package should be installed on the given user, based on the
+ * the given whitelist of system packages.
+ *
+ * @param sysPkg the system package. Must be a system package; no verification for this is done.
+ * @param userTypeWhitelist map of package manifest names to user flags on which they should be
+ * installed
+ * @param userWhitelist set of package manifest names that should be installed on this
+ * particular user. This must be consistent with userTypeWhitelist, but is
+ * passed in separately to avoid repeatedly calculating it from
+ * userTypeWhitelist.
+ * @param isImplicitWhitelistMode whether non-mentioned packages are implicitly whitelisted.
+ * @param isSystemUser whether the user is USER_SYSTEM (which gets special treatment).
+ */
+ @VisibleForTesting
+ static boolean shouldInstallPackage(PackageParser.Package sysPkg,
+ @NonNull ArrayMap<String, Integer> userTypeWhitelist,
+ @NonNull Set<String> userWhitelist, boolean isImplicitWhitelistMode,
+ boolean isSystemUser) {
+
+ final String pkgName = sysPkg.manifestPackageName;
+ boolean install = (isImplicitWhitelistMode && !userTypeWhitelist.containsKey(pkgName))
+ || userWhitelist.contains(pkgName);
+
+ // For the purposes of local development, any package that isn't even mentioned in the
+ // whitelist at all is implicitly treated as whitelisted for the SYSTEM user.
+ if (!install && isSystemUser && !userTypeWhitelist.containsKey(pkgName)) {
+ install = true;
+ Slog.e(TAG, "System package " + pkgName + " is not mentioned "
+ + "in SystemConfig's 'install-in-user-type' but we are "
+ + "implicitly treating it as whitelisted for the SYSTEM user.");
+ }
+ return install;
+ }
+
+ /**
+ * Gets the package manifest names that are whitelisted for a user with the given flags,
+ * as determined by SystemConfig.
+ */
+ @VisibleForTesting
+ @NonNull Set<String> getWhitelistedPackagesForUserType(int flags) {
+ Set<String> installablePkgs = new ArraySet<>(mWhitelitsedPackagesForUserTypes.size());
+ for (int i = 0; i < mWhitelitsedPackagesForUserTypes.size(); i++) {
+ String pkgName = mWhitelitsedPackagesForUserTypes.keyAt(i);
+ int whitelistedUserTypes = mWhitelitsedPackagesForUserTypes.valueAt(i);
+ if ((flags & whitelistedUserTypes) != 0) {
+ installablePkgs.add(pkgName);
+ }
+ }
+ return installablePkgs;
+ }
+
+ /**
+ * Set of package manifest names that are included anywhere in the package-to-user-type
+ * whitelist, as determined by SystemConfig.
+ *
+ * Packages that are whitelisted, but then blacklisted so that they aren't to be installed on
+ * any user, are still present in this list, since that is a valid scenario (e.g. if an OEM
+ * completely blacklists an AOSP app).
+ */
+ private Set<String> getWhitelistedSystemPackages() {
+ return mWhitelitsedPackagesForUserTypes.keySet();
+ }
+
+ /**
+ * Returns a map of package manifest names to the user flags on which it is to be installed.
+ * Also, clears this data from SystemConfig where it was stored inefficiently (and therefore
+ * should be called exactly once, even if the data isn't useful).
+ *
+ * Any system packages not present in this map should not even be on the device at all.
+ * To enforce this:
+ * <ul>
+ * <li>Illegal user types are ignored.</li>
+ * <li>Packages that never whitelisted at all (even if they are explicitly blacklisted) are
+ * ignored.</li>
+ * <li>Packages that are blacklisted whenever they are whitelisted will be stored with the
+ * flag 0 (since this is a valid scenario, e.g. if an OEM completely blacklists an AOSP
+ * app).</li>
+ * </ul>
+ */
+ @VisibleForTesting
+ static ArrayMap<String, Integer> determineWhitelistedPackagesForUserTypes(
+ SystemConfig sysConfig) {
+
+ final ArrayMap<String, Set<String>> whitelist =
+ sysConfig.getAndClearPackageToUserTypeWhitelist();
+ // result maps packageName -> userTypes on which the package should be installed.
+ final ArrayMap<String, Integer> result = new ArrayMap<>(whitelist.size() + 1);
+ // First, do the whitelisted user types.
+ for (int i = 0; i < whitelist.size(); i++) {
+ final String pkgName = whitelist.keyAt(i);
+ final int flags = getFlagsFromUserTypes(whitelist.valueAt(i));
+ if (flags != 0) {
+ result.put(pkgName, flags);
+ }
+ }
+ // Then, un-whitelist any blacklisted user types.
+ // TODO(b/141370854): Right now, the blacklist is actually just an 'unwhitelist'. Which
+ // direction we go depends on how we design user subtypes, which is still
+ // being designed. For now, unwhitelisting works for current use-cases.
+ final ArrayMap<String, Set<String>> blacklist =
+ sysConfig.getAndClearPackageToUserTypeBlacklist();
+ for (int i = 0; i < blacklist.size(); i++) {
+ final String pkgName = blacklist.keyAt(i);
+ final int nonFlags = getFlagsFromUserTypes(blacklist.valueAt(i));
+ final Integer flags = result.get(pkgName);
+ if (flags != null) {
+ result.put(pkgName, flags & ~nonFlags);
+ }
+ }
+ // Regardless of the whitelists/blacklists, ensure mandatory packages.
+ result.put("android",
+ UserInfo.FLAG_SYSTEM | UserInfo.FLAG_FULL | UserInfo.PROFILE_FLAGS_MASK);
+ return result;
+ }
+
+ /** Converts a user types, as used in SystemConfig, to a UserInfo flag. */
+ private static int getFlagsFromUserTypes(Iterable<String> userTypes) {
+ int flags = 0;
+ for (String type : userTypes) {
+ switch (type) {
+ case "GUEST":
+ flags |= UserInfo.FLAG_GUEST;
+ break;
+ case "RESTRICTED":
+ flags |= UserInfo.FLAG_RESTRICTED;
+ break;
+ case "MANAGED_PROFILE":
+ flags |= UserInfo.FLAG_MANAGED_PROFILE;
+ break;
+ case "EPHEMERAL":
+ flags |= UserInfo.FLAG_EPHEMERAL;
+ break;
+ case "DEMO":
+ flags |= UserInfo.FLAG_DEMO;
+ break;
+ case "FULL":
+ flags |= UserInfo.FLAG_FULL;
+ break;
+ case "SYSTEM":
+ flags |= UserInfo.FLAG_SYSTEM;
+ break;
+ case "PROFILE":
+ flags |= UserInfo.PROFILE_FLAGS_MASK;
+ break;
+ default:
+ Slog.w(TAG, "SystemConfig contained an invalid user type: " + type);
+ break;
+ // Other UserInfo flags are forbidden.
+ // In particular, FLAG_INITIALIZED, FLAG_DISABLED, FLAG_QUIET_MODE are inapplicable.
+ // The following are invalid now, but are reconsiderable: FLAG_PRIMARY, FLAG_ADMIN.
+ }
+ }
+ return flags;
+ }
+
+ void dump(PrintWriter pw) {
+ pw.print("Whitelisted packages per user type");
+ final int size = mWhitelitsedPackagesForUserTypes.size();
+ if (size == 0) {
+ pw.println(": N/A");
+ return;
+ }
+ pw.println(" (" + size + " packages)");
+ for (int i = 0; i < size; i++) {
+ final String pkgName = mWhitelitsedPackagesForUserTypes.keyAt(i);
+ final String whitelistedUserTypes =
+ UserInfo.flagsToString(mWhitelitsedPackagesForUserTypes.valueAt(i));
+ pw.println(" " + pkgName + ": " + whitelistedUserTypes);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 6d22faa7032e..037912a66b3a 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -276,6 +276,12 @@ public final class BasePermission {
public boolean isAppPredictor() {
return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0;
}
+ public boolean isTelephony() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_TELEPHONY) != 0;
+ }
+ public boolean isWifi() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_WIFI) != 0;
+ }
public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) {
if (!origPackageName.equals(sourcePackageName)) {
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 3e655edf5db0..f247037edf5c 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -190,6 +190,7 @@ public final class DefaultPermissionGrantPolicy {
static {
STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ STORAGE_PERMISSIONS.add(Manifest.permission.ACCESS_MEDIA_LOCATION);
}
private static final int MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS = 1;
@@ -436,17 +437,20 @@ public final class DefaultPermissionGrantPolicy {
// Installer
grantSystemFixedPermissionsToSystemPackage(
- getKnownPackage(PackageManagerInternal.PACKAGE_INSTALLER, userId),
+ ArrayUtils.firstOrNull(getKnownPackages(
+ PackageManagerInternal.PACKAGE_INSTALLER, userId)),
userId, STORAGE_PERMISSIONS);
// Verifier
- final String verifier = getKnownPackage(PackageManagerInternal.PACKAGE_VERIFIER, userId);
+ final String verifier = ArrayUtils.firstOrNull(getKnownPackages(
+ PackageManagerInternal.PACKAGE_VERIFIER, userId));
grantSystemFixedPermissionsToSystemPackage(verifier, userId, STORAGE_PERMISSIONS);
grantPermissionsToSystemPackage(verifier, userId, PHONE_PERMISSIONS, SMS_PERMISSIONS);
// SetupWizard
grantPermissionsToSystemPackage(
- getKnownPackage(PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId), userId,
+ ArrayUtils.firstOrNull(getKnownPackages(
+ PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId)), userId,
PHONE_PERMISSIONS, CONTACTS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS,
CAMERA_PERMISSIONS);
@@ -595,7 +599,8 @@ public final class DefaultPermissionGrantPolicy {
userId, CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS);
// Browser
- String browserPackage = getKnownPackage(PackageManagerInternal.PACKAGE_BROWSER, userId);
+ String browserPackage = ArrayUtils.firstOrNull(getKnownPackages(
+ PackageManagerInternal.PACKAGE_BROWSER, userId));
if (browserPackage == null) {
browserPackage = getDefaultSystemHandlerActivityPackageForCategory(
Intent.CATEGORY_APP_BROWSER, userId);
@@ -760,8 +765,8 @@ public final class DefaultPermissionGrantPolicy {
}
}
- private String getKnownPackage(int knownPkgId, int userId) {
- return mServiceInternal.getKnownPackageName(knownPkgId, userId);
+ private @NonNull String[] getKnownPackages(int knownPkgId, int userId) {
+ return mServiceInternal.getKnownPackageNames(knownPkgId, userId);
}
private void grantDefaultPermissionsToDefaultSystemDialerApp(
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 a57321e8012d..bb3388cc5b9a 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -24,7 +24,7 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTIO
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
@@ -53,7 +53,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
-import android.app.AppOpsManager;
import android.app.ApplicationPackageManager;
import android.app.IActivityManager;
import android.content.Context;
@@ -298,7 +297,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// Critical; after this call the application should never have the permission
mPackageManagerInt.writeSettings(false);
final int appId = UserHandle.getAppId(uid);
- killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
+ mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED));
}
@Override
public void onInstallPermissionRevoked() {
@@ -795,79 +794,57 @@ public class PermissionManagerService extends IPermissionManager.Stub {
synchronized (mLock) {
checkPermissionDelegate = mCheckPermissionDelegate;
}
- if (checkPermissionDelegate == null) {
+ if (checkPermissionDelegate == null) {
return checkPermissionImpl(permName, pkgName, userId);
}
return checkPermissionDelegate.checkPermission(permName, pkgName, userId,
this::checkPermissionImpl);
}
- private int checkPermissionImpl(@NonNull String permissionName, @NonNull String packageName,
- @UserIdInt int userId) {
- final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
+ private int checkPermissionImpl(String permName, String pkgName, int userId) {
+ final PackageParser.Package pkg = mPackageManagerInt.getPackage(pkgName);
if (pkg == null) {
return PackageManager.PERMISSION_DENIED;
}
- return checkPermissionInternal(pkg, true, permissionName, true, userId)
- ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED;
+ return checkPermissionInternal(pkg, true, permName, userId);
}
- private boolean checkPermissionInternal(@NonNull Package pkg, boolean isPackageExplicit,
- @NonNull String permissionName, boolean useRequestedPermissionsForLegacyApps,
- @UserIdInt int userId) {
+ private int checkPermissionInternal(@NonNull Package pkg, boolean isPackageExplicit,
+ @NonNull String permissionName, @UserIdInt int userId) {
final int callingUid = getCallingUid();
if (isPackageExplicit || pkg.mSharedUserId == null) {
if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
- return false;
+ return PackageManager.PERMISSION_DENIED;
}
} else {
if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
- return false;
+ return PackageManager.PERMISSION_DENIED;
}
}
final int uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);
final PackageSetting ps = (PackageSetting) pkg.mExtras;
if (ps == null) {
- return false;
+ return PackageManager.PERMISSION_DENIED;
}
final PermissionsState permissionsState = ps.getPermissionsState();
- if (checkSinglePermissionInternal(uid, permissionsState, permissionName,
- useRequestedPermissionsForLegacyApps)) {
- return true;
+ if (checkSinglePermissionInternal(uid, permissionsState, permissionName)) {
+ return PackageManager.PERMISSION_GRANTED;
}
final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName);
- if (fullerPermissionName != null && checkSinglePermissionInternal(uid, permissionsState,
- fullerPermissionName, useRequestedPermissionsForLegacyApps)) {
- return true;
+ if (fullerPermissionName != null
+ && checkSinglePermissionInternal(uid, permissionsState, fullerPermissionName)) {
+ return PackageManager.PERMISSION_GRANTED;
}
- return false;
+ return PackageManager.PERMISSION_DENIED;
}
private boolean checkSinglePermissionInternal(int uid,
- @NonNull PermissionsState permissionsState, @NonNull String permissionName,
- boolean useRequestedPermissionsForLegacyApps) {
- boolean hasPermission = permissionsState.hasPermission(permissionName,
- UserHandle.getUserId(uid));
-
- if (!hasPermission && useRequestedPermissionsForLegacyApps
- && mSettings.isPermissionRuntime(permissionName)) {
- final String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
- final int packageNamesSize = packageNames != null ? packageNames.length : 0;
- for (int i = 0; i < packageNamesSize; i++) {
- final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageNames[i]);
- if (pkg != null && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
- && pkg.requestedPermissions.contains(permissionName)) {
- hasPermission = true;
- break;
- }
- }
- }
-
- if (!hasPermission) {
+ @NonNull PermissionsState permissionsState, @NonNull String permissionName) {
+ if (!permissionsState.hasPermission(permissionName, UserHandle.getUserId(uid))) {
return false;
}
@@ -891,19 +868,18 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final CheckPermissionDelegate checkPermissionDelegate;
synchronized (mLock) {
- if (mCheckPermissionDelegate == null) {
- return checkUidPermissionImpl(permName, uid);
- }
checkPermissionDelegate = mCheckPermissionDelegate;
}
+ if (checkPermissionDelegate == null) {
+ return checkUidPermissionImpl(permName, uid);
+ }
return checkPermissionDelegate.checkUidPermission(permName, uid,
this::checkUidPermissionImpl);
}
- private int checkUidPermissionImpl(@NonNull String permissionName, int uid) {
+ private int checkUidPermissionImpl(String permName, int uid) {
final PackageParser.Package pkg = mPackageManagerInt.getPackage(uid);
- return checkUidPermissionInternal(uid, pkg, permissionName, true)
- ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED;
+ return checkUidPermissionInternal(pkg, uid, permName);
}
/**
@@ -913,25 +889,24 @@ public class PermissionManagerService extends IPermissionManager.Stub {
*
* @see SystemConfig#getSystemPermissions()
*/
- private boolean checkUidPermissionInternal(int uid, @Nullable Package pkg,
- @NonNull String permissionName, boolean useRequestedPermissionsForLegacyApps) {
+ private int checkUidPermissionInternal(@Nullable Package pkg, int uid,
+ @NonNull String permissionName) {
if (pkg != null) {
final int userId = UserHandle.getUserId(uid);
- return checkPermissionInternal(pkg, false, permissionName,
- useRequestedPermissionsForLegacyApps, userId);
+ return checkPermissionInternal(pkg, false, permissionName, userId);
}
if (checkSingleUidPermissionInternal(uid, permissionName)) {
- return true;
+ return PackageManager.PERMISSION_GRANTED;
}
final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName);
if (fullerPermissionName != null
&& checkSingleUidPermissionInternal(uid, fullerPermissionName)) {
- return true;
+ return PackageManager.PERMISSION_GRANTED;
}
- return false;
+ return PackageManager.PERMISSION_DENIED;
}
private boolean checkSingleUidPermissionInternal(int uid, @NonNull String permissionName) {
@@ -941,17 +916,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
- private int computeRuntimePermissionAppOpMode(int uid, @NonNull String permissionName) {
- boolean granted = isUidPermissionGranted(uid, permissionName);
- // TODO: Foreground permissions.
- return granted ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED;
- }
-
- private boolean isUidPermissionGranted(int uid, @NonNull String permissionName) {
- final PackageParser.Package pkg = mPackageManagerInt.getPackage(uid);
- return checkUidPermissionInternal(uid, pkg, permissionName, false);
- }
-
@Override
public void addOnPermissionsChangeListener(IOnPermissionsChangeListener listener) {
mContext.enforceCallingOrSelfPermission(
@@ -1295,7 +1259,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
if (bp.isSoftRestricted() && !SoftRestrictedPermissionPolicy.forPermission(mContext,
- pkg.applicationInfo, UserHandle.of(userId), permName).canBeGranted()) {
+ pkg.applicationInfo, UserHandle.of(userId), permName).mayGrantPermission()) {
Log.e(TAG, "Cannot grant soft restricted permission " + permName + " for package "
+ packageName);
return;
@@ -1514,7 +1478,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// These are flags that can change base on user actions.
final int userSettableMask = FLAG_PERMISSION_USER_SET
| FLAG_PERMISSION_USER_FIXED
- | FLAG_PERMISSION_REVOKE_ON_UPGRADE
+ | FLAG_PERMISSION_REVOKED_COMPAT
| FLAG_PERMISSION_REVIEW_REQUIRED;
final int policyOrSystemFlags = FLAG_PERMISSION_SYSTEM_FIXED
@@ -1577,7 +1541,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
};
- final AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
for (int i = 0; i < permissionCount; i++) {
final String permName = pkg.requestedPermissions.get(i);
final BasePermission bp;
@@ -1624,7 +1587,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final int uid = mPackageManagerInt.getPackageUid(packageName, 0, userId);
final int targetSdk = mPackageManagerInt.getUidTargetSdkVersion(uid);
final int flags = (targetSdk < Build.VERSION_CODES.M && bp.isRuntime())
- ? FLAG_PERMISSION_REVIEW_REQUIRED | FLAG_PERMISSION_REVOKE_ON_UPGRADE
+ ? FLAG_PERMISSION_REVIEW_REQUIRED | FLAG_PERMISSION_REVOKED_COMPAT
: 0;
updatePermissionFlagsInternal(
@@ -1643,16 +1606,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// If this permission was granted by default, make sure it is.
if ((oldFlags & FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0) {
+ // PermissionPolicyService will handle the app op for runtime permissions later.
grantRuntimePermissionInternal(permName, packageName, false,
Process.SYSTEM_UID, userId, delayingPermCallback);
- // Allow app op later as we are holding mPackages
- // PermissionPolicyService will handle the app op for foreground/background
- // permissions.
- String appOp = AppOpsManager.permissionToOp(permName);
- if (appOp != null) {
- mHandler.post(() -> appOpsManager.setUidMode(appOp, uid,
- AppOpsManager.MODE_ALLOWED));
- }
// If permission review is enabled the permissions for a legacy apps
// are represented as constantly granted runtime ones, so don't revoke.
} else if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
@@ -1697,10 +1653,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
return null;
}
+ DefaultBrowserProvider provider;
synchronized (mLock) {
- return mDefaultBrowserProvider == null
- ? null : mDefaultBrowserProvider.getDefaultBrowser(userId);
+ provider = mDefaultBrowserProvider;
}
+ return provider != null ? provider.getDefaultBrowser(userId) : null;
}
@Override
@@ -1716,23 +1673,27 @@ public class PermissionManagerService extends IPermissionManager.Stub {
private boolean setDefaultBrowserInternal(String packageName, boolean async,
boolean doGrant, int userId) {
+ if (userId == UserHandle.USER_ALL) {
+ return false;
+ }
+ DefaultBrowserProvider provider;
synchronized (mLock) {
- if (userId == UserHandle.USER_ALL) {
- return false;
- }
- if (mDefaultBrowserProvider == null) {
+ provider = mDefaultBrowserProvider;
+ }
+ if (provider == null) {
+ return false;
+ }
+ if (async) {
+ provider.setDefaultBrowserAsync(packageName, userId);
+ } else {
+ if (!provider.setDefaultBrowser(packageName, userId)) {
return false;
}
- if (async) {
- mDefaultBrowserProvider.setDefaultBrowserAsync(packageName, userId);
- } else {
- if (!mDefaultBrowserProvider.setDefaultBrowser(packageName, userId)) {
- return false;
- }
- }
- if (doGrant && packageName != null) {
- mDefaultPermissionGrantPolicy
- .grantDefaultPermissionsToDefaultBrowser(packageName, userId);
+ }
+ if (doGrant && packageName != null) {
+ synchronized (mLock) {
+ mDefaultPermissionGrantPolicy.grantDefaultPermissionsToDefaultBrowser(packageName,
+ userId);
}
}
return true;
@@ -1941,7 +1902,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
private void restoreRuntimePermissions(@NonNull byte[] backup, @NonNull UserHandle user) {
synchronized (mLock) {
mHasNoDelayedPermBackup.delete(user.getIdentifier());
- mPermissionControllerManager.restoreRuntimePermissionBackup(backup, user);
+ mPermissionControllerManager.stageAndApplyRuntimePermissionsBackup(backup, user);
}
}
@@ -1962,7 +1923,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return;
}
- mPermissionControllerManager.restoreDelayedRuntimePermissionBackup(packageName, user,
+ mPermissionControllerManager.applyStagedRuntimePermissionBackup(packageName, user,
mContext.getMainExecutor(), (hasMoreBackup) -> {
if (hasMoreBackup) {
return;
@@ -2536,8 +2497,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
wasChanged = true;
}
- if ((flags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
- flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+ if ((flags & FLAG_PERMISSION_REVOKED_COMPAT) != 0) {
+ flags &= ~FLAG_PERMISSION_REVOKED_COMPAT;
wasChanged = true;
// Hard restricted permissions cannot be held.
} else if (!permissionPolicyInitialized
@@ -2556,7 +2517,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
bp.getSourcePackageName())) {
if (!bp.isRemoved()) {
flags |= FLAG_PERMISSION_REVIEW_REQUIRED
- | FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+ | FLAG_PERMISSION_REVOKED_COMPAT;
wasChanged = true;
}
}
@@ -2671,8 +2632,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
wasChanged = true;
}
- if ((flags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
- flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+ if ((flags & FLAG_PERMISSION_REVOKED_COMPAT) != 0) {
+ flags &= ~FLAG_PERMISSION_REVOKED_COMPAT;
wasChanged = true;
// Hard restricted permissions cannot be held.
} else if (!permissionPolicyInitialized ||
@@ -3117,8 +3078,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
}
- final String systemPackageName = mPackageManagerInt.getKnownPackageName(
- PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM);
+ // expect single system package
+ String systemPackageName = ArrayUtils.firstOrNull(mPackageManagerInt.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM));
final PackageParser.Package systemPackage =
mPackageManagerInt.getPackage(systemPackageName);
@@ -3234,18 +3196,19 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// need a separate flag anymore. Hence we need to check which
// permissions are needed by the permission controller
if (!allowed && bp.isInstaller()
- && (pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
- PackageManagerInternal.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM))
- || pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+ && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM),
+ pkg.packageName) || ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER,
- UserHandle.USER_SYSTEM)))) {
+ UserHandle.USER_SYSTEM), pkg.packageName)) {
// If this permission is to be granted to the system installer and
// this app is an installer, then it gets the permission.
allowed = true;
}
if (!allowed && bp.isVerifier()
- && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
- PackageManagerInternal.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM))) {
+ && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM),
+ pkg.packageName)) {
// If this permission is to be granted to the system verifier and
// this app is a verifier, then it gets the permission.
allowed = true;
@@ -3261,53 +3224,71 @@ public class PermissionManagerService extends IPermissionManager.Stub {
allowed = origPermissions.hasInstallPermission(perm);
}
if (!allowed && bp.isSetup()
- && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
- PackageManagerInternal.PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM))) {
+ && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM),
+ pkg.packageName)) {
// If this permission is to be granted to the system setup wizard and
// this app is a setup wizard, then it gets the permission.
allowed = true;
}
if (!allowed && bp.isSystemTextClassifier()
- && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+ && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER,
- UserHandle.USER_SYSTEM))) {
+ UserHandle.USER_SYSTEM), pkg.packageName)) {
// Special permissions for the system default text classifier.
allowed = true;
}
if (!allowed && bp.isConfigurator()
- && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
- PackageManagerInternal.PACKAGE_CONFIGURATOR,
- UserHandle.USER_SYSTEM))) {
+ && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_CONFIGURATOR,
+ UserHandle.USER_SYSTEM), pkg.packageName)) {
// Special permissions for the device configurator.
allowed = true;
}
if (!allowed && bp.isWellbeing()
- && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
- PackageManagerInternal.PACKAGE_WELLBEING, UserHandle.USER_SYSTEM))) {
+ && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_WELLBEING, UserHandle.USER_SYSTEM),
+ pkg.packageName)) {
// Special permission granted only to the OEM specified wellbeing app
allowed = true;
}
if (!allowed && bp.isDocumenter()
- && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
- PackageManagerInternal.PACKAGE_DOCUMENTER, UserHandle.USER_SYSTEM))) {
+ && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_DOCUMENTER, UserHandle.USER_SYSTEM),
+ pkg.packageName)) {
// If this permission is to be granted to the documenter and
// this app is the documenter, then it gets the permission.
allowed = true;
}
if (!allowed && bp.isIncidentReportApprover()
- && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+ && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER,
- UserHandle.USER_SYSTEM))) {
+ UserHandle.USER_SYSTEM), pkg.packageName)) {
// If this permission is to be granted to the incident report approver and
// this app is the incident report approver, then it gets the permission.
allowed = true;
}
if (!allowed && bp.isAppPredictor()
- && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
- PackageManagerInternal.PACKAGE_APP_PREDICTOR, UserHandle.USER_SYSTEM))) {
+ && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_APP_PREDICTOR, UserHandle.USER_SYSTEM),
+ pkg.packageName)) {
// Special permissions for the system app predictor.
allowed = true;
}
+ if (!allowed && bp.isTelephony()
+ && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_TELEPHONY, UserHandle.USER_SYSTEM),
+ pkg.packageName)) {
+ // Special permissions for the system telephony apps.
+ allowed = true;
+ }
+ if (!allowed && bp.isWifi()
+ && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_WIFI, UserHandle.USER_SYSTEM),
+ pkg.packageName)) {
+ // Special permissions for the system wifi.
+ allowed = true;
+ }
}
return allowed;
}
@@ -3407,6 +3388,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
| PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+ final int compatFlags = PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
+ | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
+
final boolean supportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
>= Build.VERSION_CODES.M;
@@ -3430,12 +3414,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
callingUid, userId, callback);
}
} else {
- // In permission review mode we clear the review flag when we
- // are asked to install the app with all permissions granted.
- if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
- updatePermissionFlagsInternal(permission, pkg.packageName,
- PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED, 0, callingUid,
- userId, false, callback);
+ // In permission review mode we clear the review flag and the revoked compat
+ // flag when we are asked to install the app with all permissions granted.
+ if ((flags & compatFlags) != 0) {
+ updatePermissionFlagsInternal(permission, pkg.packageName, compatFlags,
+ 0, callingUid, userId, false, callback);
}
}
}
@@ -4241,8 +4224,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@Override
- public @NonNull ArrayList<PermissionInfo> getAllPermissionWithProtectionLevel(
- @PermissionInfo.Protection int protectionLevel) {
+ public @NonNull ArrayList<PermissionInfo> getAllPermissionWithProtection(
+ @PermissionInfo.Protection int protection) {
ArrayList<PermissionInfo> matchingPermissions = new ArrayList<>();
synchronized (PermissionManagerService.this.mLock) {
@@ -4252,7 +4235,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
BasePermission bp = mSettings.mPermissions.valueAt(i);
if (bp.perm != null && bp.perm.info != null
- && bp.protectionLevel == protectionLevel) {
+ && bp.perm.info.getProtection() == protection) {
matchingPermissions.add(bp.perm.info);
}
}
@@ -4334,15 +4317,17 @@ public class PermissionManagerService extends IPermissionManager.Stub {
@Override
public void setDefaultHome(String packageName, int userId, Consumer<Boolean> callback) {
+ if (userId == UserHandle.USER_ALL) {
+ return;
+ }
+ DefaultHomeProvider provider;
synchronized (mLock) {
- if (userId == UserHandle.USER_ALL) {
- return;
- }
- if (mDefaultHomeProvider == null) {
- return;
- }
- mDefaultHomeProvider.setDefaultHomeAsync(packageName, userId, callback);
+ provider = mDefaultHomeProvider;
+ }
+ if (provider == null) {
+ return;
}
+ provider.setDefaultHomeAsync(packageName, userId, callback);
}
@Override
@@ -4403,26 +4388,29 @@ public class PermissionManagerService extends IPermissionManager.Stub {
@Override
public String getDefaultBrowser(int userId) {
+ DefaultBrowserProvider provider;
synchronized (mLock) {
- return mDefaultBrowserProvider == null
- ? null : mDefaultBrowserProvider.getDefaultBrowser(userId);
+ provider = mDefaultBrowserProvider;
}
+ return provider != null ? provider.getDefaultBrowser(userId) : null;
}
@Override
public String getDefaultDialer(int userId) {
+ DefaultDialerProvider provider;
synchronized (mLock) {
- return mDefaultDialerProvider == null
- ? null : mDefaultDialerProvider.getDefaultDialer(userId);
+ provider = mDefaultDialerProvider;
}
+ return provider != null ? provider.getDefaultDialer(userId) : null;
}
@Override
public String getDefaultHome(int userId) {
+ DefaultHomeProvider provider;
synchronized (mLock) {
- return mDefaultHomeProvider == null
- ? null : mDefaultHomeProvider.getDefaultHome(userId);
+ provider = mDefaultHomeProvider;
}
+ return provider != null ? provider.getDefaultHome(userId) : null;
}
@Override
@@ -4465,12 +4453,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
StorageManager.UUID_PRIVATE_INTERNAL, true, mDefaultPermissionCallback);
}
}
-
- @Override
- public int computeRuntimePermissionAppOpMode(int uid, @NonNull String permissionName) {
- return PermissionManagerService.this.computeRuntimePermissionAppOpMode(uid,
- permissionName);
- }
}
private static final class OnPermissionChangeListeners extends Handler {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 8f22f9245a53..a807a7e6313c 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -291,8 +291,8 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
public abstract @Nullable BasePermission getPermissionTEMP(@NonNull String permName);
/** Get all permission that have a certain protection level */
- public abstract @NonNull ArrayList<PermissionInfo> getAllPermissionWithProtectionLevel(
- @PermissionInfo.Protection int protectionLevel);
+ public abstract @NonNull ArrayList<PermissionInfo> getAllPermissionWithProtection(
+ @PermissionInfo.Protection int protection);
/**
* Returns the delegate used to influence permission checking.
@@ -445,13 +445,4 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
/** Called when a new user has been created. */
public abstract void onNewUserCreated(@UserIdInt int userId);
-
- /**
- * Compute an app op mode based on its runtime permission state.
- *
- * @param uid the uid for the app op
- * @param permissionName the permission name for the app op
- * @return the computed mode
- */
- public abstract int computeRuntimePermissionAppOpMode(int uid, @NonNull String permissionName);
}
diff --git a/services/core/java/com/android/server/policy/LegacyGlobalActions.java b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
index 9cb2441d5662..bbee393bb98d 100644
--- a/services/core/java/com/android/server/policy/LegacyGlobalActions.java
+++ b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
@@ -370,8 +370,7 @@ class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogIn
// Take an "interactive" bugreport.
MetricsLogger.action(mContext,
MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_INTERACTIVE);
- ActivityManager.getService().requestBugReport(
- ActivityManager.BUGREPORT_OPTION_INTERACTIVE);
+ ActivityManager.getService().requestInteractiveBugReport();
} catch (RemoteException e) {
}
}
@@ -388,8 +387,7 @@ class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogIn
try {
// Take a "full" bugreport.
MetricsLogger.action(mContext, MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_FULL);
- ActivityManager.getService().requestBugReport(
- ActivityManager.BUGREPORT_OPTION_FULL);
+ ActivityManager.getService().requestFullBugReport();
} catch (RemoteException e) {
}
return false;
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 1bf319dfcb69..9b9f93f7b5c4 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -17,13 +17,12 @@
package com.android.server.policy;
import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.MODE_DEFAULT;
-import static android.app.AppOpsManager.MODE_ERRORED;
import static android.app.AppOpsManager.MODE_FOREGROUND;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.OP_NONE;
import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
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.GET_PERMISSIONS;
import android.annotation.NonNull;
@@ -49,12 +48,12 @@ import android.os.UserManagerInternal;
import android.permission.PermissionControllerManager;
import android.provider.Telephony;
import android.telecom.TelecomManager;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LongSparseLongArray;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsCallback;
@@ -68,6 +67,7 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback;
import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
@@ -145,7 +145,7 @@ public final class PermissionPolicyService extends SystemService {
};
final ArrayList<PermissionInfo> dangerousPerms =
- permManagerInternal.getAllPermissionWithProtectionLevel(
+ permManagerInternal.getAllPermissionWithProtection(
PermissionInfo.PROTECTION_DANGEROUS);
try {
@@ -153,17 +153,16 @@ public final class PermissionPolicyService extends SystemService {
for (int i = 0; i < numDangerousPerms; i++) {
PermissionInfo perm = dangerousPerms.get(i);
- if (perm.isHardRestricted() || perm.backgroundPermission != null) {
+ if (perm.isRuntime()) {
appOpsService.startWatchingMode(getSwitchOp(perm.name), null, appOpsListener);
- } else if (perm.isSoftRestricted()) {
- appOpsService.startWatchingMode(getSwitchOp(perm.name), null, appOpsListener);
-
+ }
+ if (perm.isSoftRestricted()) {
SoftRestrictedPermissionPolicy policy =
SoftRestrictedPermissionPolicy.forPermission(null, null, null,
perm.name);
- if (policy.resolveAppOp() != OP_NONE) {
- appOpsService.startWatchingMode(policy.resolveAppOp(), null,
- appOpsListener);
+ int extraAppOp = policy.getExtraAppOpCode();
+ if (extraAppOp != OP_NONE) {
+ appOpsService.startWatchingMode(extraAppOp, null, appOpsListener);
}
}
}
@@ -391,26 +390,7 @@ public final class PermissionPolicyService extends SystemService {
private final @NonNull PackageManager mPackageManager;
private final @NonNull AppOpsManager mAppOpsManager;
- /** All uid that need to be synchronized */
- private final @NonNull SparseIntArray mAllUids = new SparseIntArray();
-
- /**
- * All ops that need to be set to default
- *
- * Currently, only used by the restricted permissions logic.
- *
- * @see #syncPackages
- */
- private final @NonNull ArrayList<OpToChange> mOpsToDefault = new ArrayList<>();
-
- /**
- * All ops that need to be flipped to allow if default.
- *
- * Currently, only used by the restricted permissions logic.
- *
- * @see #syncPackages
- */
- private final @NonNull ArrayList<OpToChange> mOpsToAllowIfDefault = new ArrayList<>();
+ private final @NonNull ArrayMap<String, PermissionInfo> mRuntimePermissionInfos;
/**
* All ops that need to be flipped to allow.
@@ -420,15 +400,6 @@ public final class PermissionPolicyService extends SystemService {
private final @NonNull ArrayList<OpToChange> mOpsToAllow = new ArrayList<>();
/**
- * All ops that need to be flipped to ignore if default.
- *
- * Currently, only used by the restricted permissions logic.
- *
- * @see #syncPackages
- */
- private final @NonNull ArrayList<OpToChange> mOpsToIgnoreIfDefault = new ArrayList<>();
-
- /**
* All ops that need to be flipped to ignore.
*
* @see #syncPackages
@@ -436,34 +407,45 @@ public final class PermissionPolicyService extends SystemService {
private final @NonNull ArrayList<OpToChange> mOpsToIgnore = new ArrayList<>();
/**
- * All ops that need to be flipped to foreground.
+ * All ops that need to be flipped to ignore if not allowed.
*
- * Currently, only used by the foreground/background permissions logic.
+ * Currently, only used by soft restricted permissions logic.
*
* @see #syncPackages
*/
- private final @NonNull ArrayList<OpToChange> mOpsToForeground = new ArrayList<>();
+ private final @NonNull ArrayList<OpToChange> mOpsToIgnoreIfNotAllowed = new ArrayList<>();
/**
- * All ops that need to be flipped to foreground if allow.
+ * All ops that need to be flipped to foreground.
*
* Currently, only used by the foreground/background permissions logic.
*
* @see #syncPackages
*/
- private final @NonNull ArrayList<OpToChange> mOpsToForegroundIfAllow =
- new ArrayList<>();
+ private final @NonNull ArrayList<OpToChange> mOpsToForeground = new ArrayList<>();
PermissionToOpSynchroniser(@NonNull Context context) {
mContext = context;
mPackageManager = context.getPackageManager();
mAppOpsManager = context.getSystemService(AppOpsManager.class);
+
+ mRuntimePermissionInfos = new ArrayMap<>();
+ PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService(
+ PermissionManagerServiceInternal.class);
+ List<PermissionInfo> permissionInfos =
+ permissionManagerInternal.getAllPermissionWithProtection(
+ PermissionInfo.PROTECTION_DANGEROUS);
+ int permissionInfosSize = permissionInfos.size();
+ for (int i = 0; i < permissionInfosSize; i++) {
+ PermissionInfo permissionInfo = permissionInfos.get(i);
+ mRuntimePermissionInfos.put(permissionInfo.name, permissionInfo);
+ }
}
/**
* Set app ops that were added in {@link #addPackage}.
*
- * <p>This processes ops previously added by {@link #addOpIfRestricted}
+ * <p>This processes ops previously added by {@link #addAppOps(PackageInfo, String)}
*/
private void syncPackages() {
// Remember which ops were already set. This makes sure that we always set the most
@@ -479,32 +461,6 @@ public final class PermissionPolicyService extends SystemService {
alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
}
- final int allowIfDefaultCount = mOpsToAllowIfDefault.size();
- for (int i = 0; i < allowIfDefaultCount; i++) {
- final OpToChange op = mOpsToAllowIfDefault.get(i);
- if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
- continue;
- }
-
- boolean wasSet = setUidModeAllowedIfDefault(op.code, op.uid, op.packageName);
- if (wasSet) {
- alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
- }
- }
-
- final int foregroundIfAllowedCount = mOpsToForegroundIfAllow.size();
- for (int i = 0; i < foregroundIfAllowedCount; i++) {
- final OpToChange op = mOpsToForegroundIfAllow.get(i);
- if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
- continue;
- }
-
- boolean wasSet = setUidModeForegroundIfAllow(op.code, op.uid, op.packageName);
- if (wasSet) {
- alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
- }
- }
-
final int foregroundCount = mOpsToForeground.size();
for (int i = 0; i < foregroundCount; i++) {
final OpToChange op = mOpsToForeground.get(i);
@@ -527,180 +483,145 @@ public final class PermissionPolicyService extends SystemService {
alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
}
- final int ignoreIfDefaultCount = mOpsToIgnoreIfDefault.size();
- for (int i = 0; i < ignoreIfDefaultCount; i++) {
- final OpToChange op = mOpsToIgnoreIfDefault.get(i);
+ final int ignoreIfNotAllowedCount = mOpsToIgnoreIfNotAllowed.size();
+ for (int i = 0; i < ignoreIfNotAllowedCount; i++) {
+ final OpToChange op = mOpsToIgnoreIfNotAllowed.get(i);
if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
continue;
}
- boolean wasSet = setUidModeIgnoredIfDefault(op.code, op.uid, op.packageName);
+ boolean wasSet = setUidModeIgnoredIfNotAllowed(op.code, op.uid, op.packageName);
if (wasSet) {
alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
}
}
-
- final int defaultCount = mOpsToDefault.size();
- for (int i = 0; i < defaultCount; i++) {
- final OpToChange op = mOpsToDefault.get(i);
- if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
- continue;
- }
-
- setUidModeDefault(op.code, op.uid, op.packageName);
- alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
- }
}
/**
- * Add op that belong to a restricted permission for later processing in
- * {@link #syncPackages()}.
- *
- * <p>Note: Called with the package lock held. Do <u>not</u> call into app-op manager.
- *
- * @param permissionInfo The permission that is currently looked at
- * @param pkg The package looked at
+ * Note: Called with the package lock held. Do <u>not</u> call into app-op manager.
*/
- private void addOpIfRestricted(@NonNull PermissionInfo permissionInfo,
- @NonNull PackageInfo pkg) {
- final String permission = permissionInfo.name;
- final int opCode = getSwitchOp(permission);
- final int uid = pkg.applicationInfo.uid;
-
- if (!permissionInfo.isRestricted()) {
+ private void addAppOps(@NonNull PackageInfo packageInfo, @NonNull String permissionName) {
+ PermissionInfo permissionInfo = mRuntimePermissionInfos.get(permissionName);
+ if (permissionInfo == null) {
return;
}
+ addPermissionAppOp(packageInfo, permissionInfo);
+ addExtraAppOp(packageInfo, permissionInfo);
+ }
- final boolean applyRestriction =
- (mPackageManager.getPermissionFlags(permission, pkg.packageName,
- mContext.getUser()) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
-
- if (permissionInfo.isHardRestricted()) {
- if (opCode != OP_NONE) {
- if (applyRestriction) {
- mOpsToDefault.add(new OpToChange(uid, pkg.packageName, opCode));
- } else {
- mOpsToAllowIfDefault.add(new OpToChange(uid, pkg.packageName, opCode));
- }
- }
- } else if (permissionInfo.isSoftRestricted()) {
- final SoftRestrictedPermissionPolicy policy =
- SoftRestrictedPermissionPolicy.forPermission(mContext, pkg.applicationInfo,
- mContext.getUser(), permission);
-
- if (opCode != OP_NONE) {
- if (policy.canBeGranted()) {
- mOpsToAllowIfDefault.add(new OpToChange(uid, pkg.packageName, opCode));
- } else {
- mOpsToDefault.add(new OpToChange(uid, pkg.packageName, opCode));
- }
- }
+ private void addPermissionAppOp(@NonNull PackageInfo packageInfo,
+ @NonNull PermissionInfo permissionInfo) {
+ if (!permissionInfo.isRuntime()) {
+ return;
+ }
- final int op = policy.resolveAppOp();
- if (op != OP_NONE) {
- switch (policy.getDesiredOpMode()) {
- case MODE_DEFAULT:
- mOpsToDefault.add(new OpToChange(uid, pkg.packageName, op));
- break;
- case MODE_ALLOWED:
- if (policy.shouldSetAppOpIfNotDefault()) {
- mOpsToAllow.add(new OpToChange(uid, pkg.packageName, op));
- } else {
- mOpsToAllowIfDefault.add(
- new OpToChange(uid, pkg.packageName, op));
- }
- break;
- case MODE_FOREGROUND:
- Slog.wtf(LOG_TAG,
- "Setting appop to foreground is not implemented");
- break;
- case MODE_IGNORED:
- if (policy.shouldSetAppOpIfNotDefault()) {
- mOpsToIgnore.add(new OpToChange(uid, pkg.packageName, op));
- } else {
- mOpsToIgnoreIfDefault.add(
- new OpToChange(uid, pkg.packageName,
- op));
- }
- break;
- case MODE_ERRORED:
- Slog.wtf(LOG_TAG, "Setting appop to errored is not implemented");
- }
- }
+ String permissionName = permissionInfo.name;
+ String packageName = packageInfo.packageName;
+ int permissionFlags = mPackageManager.getPermissionFlags(permissionName,
+ packageName, mContext.getUser());
+ boolean isReviewRequired = (permissionFlags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0;
+ if (isReviewRequired) {
+ return;
}
- }
- private boolean isBgPermRestricted(@NonNull String pkg, @NonNull String perm, int uid) {
- try {
- final PermissionInfo bgPermInfo = mPackageManager.getPermissionInfo(perm, 0);
+ // TODO: COARSE_LOCATION and FINE_LOCATION shares the same app op. We are solving this
+ // with switch op but once we start syncing single permission this won't work.
+ int appOpCode = getSwitchOp(permissionName);
+ if (appOpCode == OP_NONE) {
+ // Note that background permissions don't have an associated app op.
+ return;
+ }
- if (bgPermInfo.isSoftRestricted()) {
- Slog.wtf(LOG_TAG, "Support for soft restricted background permissions not "
- + "implemented");
+ int appOpMode;
+ boolean shouldGrantAppOp = shouldGrantAppOp(packageInfo, permissionInfo);
+ if (shouldGrantAppOp) {
+ if (permissionInfo.backgroundPermission != null) {
+ PermissionInfo backgroundPermissionInfo = mRuntimePermissionInfos.get(
+ permissionInfo.backgroundPermission);
+ boolean shouldGrantBackgroundAppOp = backgroundPermissionInfo != null
+ && shouldGrantAppOp(packageInfo, backgroundPermissionInfo);
+ appOpMode = shouldGrantBackgroundAppOp ? MODE_ALLOWED : MODE_FOREGROUND;
+ } else {
+ appOpMode = MODE_ALLOWED;
}
+ } else {
+ appOpMode = MODE_IGNORED;
+ }
+
+ int uid = packageInfo.applicationInfo.uid;
+ OpToChange opToChange = new OpToChange(uid, packageName, appOpCode);
+ switch (appOpMode) {
+ case MODE_ALLOWED:
+ mOpsToAllow.add(opToChange);
+ break;
+ case MODE_FOREGROUND:
+ mOpsToForeground.add(opToChange);
+ break;
+ case MODE_IGNORED:
+ mOpsToIgnore.add(opToChange);
+ break;
+ }
+ }
+
+ private boolean shouldGrantAppOp(@NonNull PackageInfo packageInfo,
+ @NonNull PermissionInfo permissionInfo) {
+ String permissionName = permissionInfo.name;
+ String packageName = packageInfo.packageName;
+ boolean isGranted = mPackageManager.checkPermission(permissionName, packageName)
+ == PackageManager.PERMISSION_GRANTED;
+ if (!isGranted) {
+ return false;
+ }
- return bgPermInfo.isHardRestricted() && (mPackageManager.getPermissionFlags(
- perm, pkg, UserHandle.getUserHandleForUid(uid))
- & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
- } catch (NameNotFoundException e) {
- Slog.w(LOG_TAG, "Cannot read permission state of " + perm, e);
+ int permissionFlags = mPackageManager.getPermissionFlags(permissionName, packageName,
+ mContext.getUser());
+ boolean isRevokedCompat = (permissionFlags & FLAG_PERMISSION_REVOKED_COMPAT)
+ == FLAG_PERMISSION_REVOKED_COMPAT;
+ if (isRevokedCompat) {
return false;
}
- }
- /**
- * Add op that belong to a foreground permission for later processing in
- * {@link #syncPackages()}.
- *
- * <p>Note: Called with the package lock held. Do <u>not</u> call into app-op manager.
- *
- * @param permissionInfo The permission that is currently looked at
- * @param pkg The package looked at
- */
- private void addOpIfFgPermissions(@NonNull PermissionInfo permissionInfo,
- @NonNull PackageInfo pkg) {
- final String bgPermissionName = permissionInfo.backgroundPermission;
+ if (permissionInfo.isHardRestricted()) {
+ boolean shouldApplyRestriction =
+ (permissionFlags & FLAG_PERMISSION_APPLY_RESTRICTION)
+ == FLAG_PERMISSION_APPLY_RESTRICTION;
+ return !shouldApplyRestriction;
+ } else if (permissionInfo.isSoftRestricted()) {
+ SoftRestrictedPermissionPolicy policy =
+ SoftRestrictedPermissionPolicy.forPermission(mContext,
+ packageInfo.applicationInfo, mContext.getUser(), permissionName);
+ return policy.mayGrantPermission();
+ } else {
+ return true;
+ }
+ }
- if (bgPermissionName == null) {
+ private void addExtraAppOp(@NonNull PackageInfo packageInfo,
+ @NonNull PermissionInfo permissionInfo) {
+ if (!permissionInfo.isSoftRestricted()) {
return;
}
- final String permission = permissionInfo.name;
- final int opCode = getSwitchOp(permission);
- final String pkgName = pkg.packageName;
- final int uid = pkg.applicationInfo.uid;
-
- // App does not support runtime permissions. Hence the state is encoded in the app-op.
- // To not override unrecoverable state don't change app-op unless bg perm is reviewed.
- if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
- // If the review is required for this permission, the grant state does not
- // really matter. To have a stable state, don't change the app-op if review is still
- // pending.
- int flags = mPackageManager.getPermissionFlags(bgPermissionName,
- pkg.packageName, UserHandle.getUserHandleForUid(uid));
-
- if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0
- && isBgPermRestricted(pkgName, bgPermissionName, uid)) {
- mOpsToForegroundIfAllow.add(new OpToChange(uid, pkgName, opCode));
- }
-
+ String permissionName = permissionInfo.name;
+ SoftRestrictedPermissionPolicy policy =
+ SoftRestrictedPermissionPolicy.forPermission(mContext,
+ packageInfo.applicationInfo, mContext.getUser(), permissionName);
+ int extraOpCode = policy.getExtraAppOpCode();
+ if (extraOpCode == OP_NONE) {
return;
}
- if (mPackageManager.checkPermission(permission, pkgName)
- == PackageManager.PERMISSION_GRANTED) {
- final boolean isBgHardRestricted = isBgPermRestricted(pkgName, bgPermissionName,
- uid);
- final boolean isBgPermGranted = mPackageManager.checkPermission(bgPermissionName,
- pkgName) == PackageManager.PERMISSION_GRANTED;
-
- if (!isBgHardRestricted && isBgPermGranted) {
- mOpsToAllow.add(new OpToChange(uid, pkgName, opCode));
+ int uid = packageInfo.applicationInfo.uid;
+ String packageName = packageInfo.packageName;
+ OpToChange extraOpToChange = new OpToChange(uid, packageName, extraOpCode);
+ if (policy.mayAllowExtraAppOp()) {
+ mOpsToAllow.add(extraOpToChange);
+ } else {
+ if (policy.mayDenyExtraAppOpIfGranted()) {
+ mOpsToIgnore.add(extraOpToChange);
} else {
- mOpsToForeground.add(new OpToChange(uid, pkgName, opCode));
+ mOpsToIgnoreIfNotAllowed.add(extraOpToChange);
}
- } else {
- mOpsToIgnore.add(new OpToChange(uid, pkgName, opCode));
}
}
@@ -719,82 +640,47 @@ public final class PermissionPolicyService extends SystemService {
return;
}
- mAllUids.put(pkg.applicationInfo.uid, pkg.applicationInfo.uid);
-
if (pkg.requestedPermissions == null) {
return;
}
for (String permission : pkg.requestedPermissions) {
- final int opCode = getSwitchOp(permission);
- if (opCode == OP_NONE) {
- continue;
- }
-
- final PermissionInfo permissionInfo;
- try {
- permissionInfo = mPackageManager.getPermissionInfo(permission, 0);
- } catch (PackageManager.NameNotFoundException e) {
- continue;
- }
-
- addOpIfRestricted(permissionInfo, pkg);
- addOpIfFgPermissions(permissionInfo, pkg);
+ addAppOps(pkg, permission);
}
}
- private boolean setUidModeAllowedIfDefault(int opCode, int uid,
- @NonNull String packageName) {
- return setUidModeIfMode(opCode, uid, MODE_DEFAULT, MODE_ALLOWED, packageName);
- }
-
private void setUidModeAllowed(int opCode, int uid, @NonNull String packageName) {
setUidMode(opCode, uid, MODE_ALLOWED, packageName);
}
- private boolean setUidModeForegroundIfAllow(int opCode, int uid,
- @NonNull String packageName) {
- return setUidModeIfMode(opCode, uid, MODE_ALLOWED, MODE_FOREGROUND, packageName);
- }
-
private void setUidModeForeground(int opCode, int uid, @NonNull String packageName) {
setUidMode(opCode, uid, MODE_FOREGROUND, packageName);
}
- private boolean setUidModeIgnoredIfDefault(int opCode, int uid,
- @NonNull String packageName) {
- return setUidModeIfMode(opCode, uid, MODE_DEFAULT, MODE_IGNORED, packageName);
- }
-
private void setUidModeIgnored(int opCode, int uid, @NonNull String packageName) {
setUidMode(opCode, uid, MODE_IGNORED, packageName);
}
- private void setUidMode(int opCode, int uid, int mode,
+ private boolean setUidModeIgnoredIfNotAllowed(int opCode, int uid,
@NonNull String packageName) {
- final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager
- .opToPublicName(opCode), uid, packageName);
-
- if (currentMode != mode) {
- mAppOpsManager.setUidMode(opCode, uid, mode);
+ final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
+ opCode), uid, packageName);
+ if (currentMode != MODE_ALLOWED) {
+ if (currentMode != MODE_IGNORED) {
+ mAppOpsManager.setUidMode(opCode, uid, MODE_IGNORED);
+ }
+ return true;
}
+ return false;
}
- private boolean setUidModeIfMode(int opCode, int uid, int requiredModeBefore, int newMode,
+ private void setUidMode(int opCode, int uid, int mode,
@NonNull String packageName) {
final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager
.opToPublicName(opCode), uid, packageName);
-
- if (currentMode == requiredModeBefore) {
- mAppOpsManager.setUidMode(opCode, uid, newMode);
- return true;
+ if (currentMode != mode) {
+ mAppOpsManager.setUidMode(opCode, uid, mode);
}
-
- return false;
- }
-
- private void setUidModeDefault(int opCode, int uid, String packageName) {
- setUidMode(opCode, uid, MODE_DEFAULT, packageName);
}
private class OpToChange {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index aed98cd7a2a4..a7c91a0baddf 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -23,6 +23,7 @@ import static android.app.AppOpsManager.OP_TOAST_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.Context.CONTEXT_RESTRICTED;
import static android.content.Context.WINDOW_SERVICE;
+import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
import static android.content.pm.PackageManager.FEATURE_HDMI_CEC;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
@@ -46,8 +47,8 @@ import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -204,6 +205,7 @@ import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.os.RoSystemProperties;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
+import com.android.internal.policy.KeyInterceptionInfo;
import com.android.internal.policy.PhoneWindow;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.ArrayUtils;
@@ -384,6 +386,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
BurnInProtectionHelper mBurnInProtectionHelper;
private DisplayFoldController mDisplayFoldController;
AppOpsManager mAppOpsManager;
+ PackageManager mPackageManager;
+ private boolean mHasFeatureAuto;
private boolean mHasFeatureWatch;
private boolean mHasFeatureLeanback;
private boolean mHasFeatureHdmiCec;
@@ -1561,10 +1565,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private void launchAllAppsAction() {
Intent intent = new Intent(Intent.ACTION_ALL_APPS);
if (mHasFeatureLeanback) {
- final PackageManager pm = mContext.getPackageManager();
Intent intentLauncher = new Intent(Intent.ACTION_MAIN);
intentLauncher.addCategory(Intent.CATEGORY_HOME);
- ResolveInfo resolveInfo = pm.resolveActivityAsUser(intentLauncher,
+ ResolveInfo resolveInfo = mPackageManager.resolveActivityAsUser(intentLauncher,
PackageManager.MATCH_SYSTEM_ONLY,
mCurrentUserId);
if (resolveInfo != null) {
@@ -1612,7 +1615,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mDisplayId = displayId;
}
- int handleHomeButton(WindowState win, KeyEvent event) {
+ int handleHomeButton(IBinder focusedToken, KeyEvent event) {
final boolean keyguardOn = keyguardOn();
final int repeatCount = event.getRepeatCount();
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
@@ -1655,18 +1658,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return -1;
}
- // If a system window has focus, then it doesn't make sense
- // right now to interact with applications.
- WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;
- if (attrs != null) {
- final int type = attrs.type;
- if (type == TYPE_KEYGUARD_DIALOG
- || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
+ final KeyInterceptionInfo info =
+ mWindowManagerInternal.getKeyInterceptionInfoFromToken(focusedToken);
+ if (info != null) {
+ // If a system window has focus, then it doesn't make sense
+ // right now to interact with applications.
+ if (info.layoutParamsType == TYPE_KEYGUARD_DIALOG
+ || (info.layoutParamsPrivateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
// the "app" is keyguard, so give it the key
return 0;
}
for (int t : WINDOW_TYPES_WHERE_HOME_DOESNT_WORK) {
- if (type == t) {
+ if (info.layoutParamsType == t) {
// don't do anything, but also don't pass it to the app
return -1;
}
@@ -1759,9 +1762,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mDisplayManager = mContext.getSystemService(DisplayManager.class);
- mHasFeatureWatch = mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH);
- mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK);
- mHasFeatureHdmiCec = mContext.getPackageManager().hasSystemFeature(FEATURE_HDMI_CEC);
+ mPackageManager = mContext.getPackageManager();
+ mHasFeatureWatch = mPackageManager.hasSystemFeature(FEATURE_WATCH);
+ mHasFeatureLeanback = mPackageManager.hasSystemFeature(FEATURE_LEANBACK);
+ mHasFeatureAuto = mPackageManager.hasSystemFeature(FEATURE_AUTOMOTIVE);
+ mHasFeatureHdmiCec = mPackageManager.hasSystemFeature(FEATURE_HDMI_CEC);
mAccessibilityShortcutController =
new AccessibilityShortcutController(mContext, new Handler(), mCurrentUserId);
mLogger = new MetricsLogger();
@@ -1999,7 +2004,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_NOTHING;
- if (mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
+ if (mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE;
}
}
@@ -2143,7 +2148,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
ApplicationInfo appInfo;
try {
- appInfo = mContext.getPackageManager().getApplicationInfoAsUser(
+ appInfo = mPackageManager.getApplicationInfoAsUser(
attrs.packageName,
0 /* flags */,
UserHandle.getUserId(callingUid));
@@ -2194,10 +2199,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
default:
// These are the windows that by default are shown only to the user that created
// them. If this needs to be overridden, set
- // {@link WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS} in
+ // {@link WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS} in
// {@link WindowManager.LayoutParams}. Note that permission
// {@link android.Manifest.permission.INTERNAL_SYSTEM_WINDOW} is required as well.
- if ((attrs.privateFlags & PRIVATE_FLAG_SHOW_FOR_ALL_USERS) == 0) {
+ if ((attrs.privateFlags & SYSTEM_FLAG_SHOW_FOR_ALL_USERS) == 0) {
return true;
}
break;
@@ -2451,7 +2456,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
params.privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED;
- params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
if (!compatInfo.supportsScreen()) {
params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
@@ -2607,8 +2612,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// TODO(b/117479243): handle it in InputPolicy
/** {@inheritDoc} */
@Override
- public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
- final long result = interceptKeyBeforeDispatchingInner(win, event, policyFlags);
+ public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event,
+ int policyFlags) {
+ final long result = interceptKeyBeforeDispatchingInner(focusedToken, event, policyFlags);
final int eventDisplayId = event.getDisplayId();
if (result == 0 && !mPerDisplayFocusEnabled
&& eventDisplayId != INVALID_DISPLAY && eventDisplayId != mTopFocusedDisplayId) {
@@ -2636,7 +2642,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return result;
}
- private long interceptKeyBeforeDispatchingInner(WindowState win, KeyEvent event,
+ private long interceptKeyBeforeDispatchingInner(IBinder focusedToken, KeyEvent event,
int policyFlags) {
final boolean keyguardOn = keyguardOn();
final int keyCode = event.getKeyCode();
@@ -2739,7 +2745,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
handler = new DisplayHomeButtonHandler(displayId);
mDisplayHomeButtonHandlers.put(displayId, handler);
}
- return handler.handleHomeButton(win, event);
+ return handler.handleHomeButton(focusedToken, event);
} else if (keyCode == KeyEvent.KEYCODE_MENU) {
// Hijack modified menu keys for debugging features
final int chordBug = KeyEvent.META_SHIFT_ON;
@@ -3129,8 +3135,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|| Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1) {
try {
- ActivityManager.getService()
- .requestBugReport(ActivityManager.BUGREPORT_OPTION_FULL);
+ ActivityManager.getService().requestFullBugReport();
} catch (RemoteException e) {
Slog.e(TAG, "Error taking bugreport", e);
}
@@ -3140,10 +3145,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// TODO(b/117479243): handle it in InputPolicy
/** {@inheritDoc} */
@Override
- public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
+ public KeyEvent dispatchUnhandledKey(IBinder focusedToken, KeyEvent event, int policyFlags) {
// Note: This method is only called if the initial down was unhandled.
if (DEBUG_INPUT) {
- Slog.d(TAG, "Unhandled key: win=" + win + ", action=" + event.getAction()
+ final KeyInterceptionInfo info =
+ mWindowManagerInternal.getKeyInterceptionInfoFromToken(focusedToken);
+ final String title = info == null ? "<unknown>" : info.windowTitle;
+ Slog.d(TAG, "Unhandled key: inputToken=" + focusedToken
+ + ", title=" + title
+ + ", action=" + event.getAction()
+ ", flags=" + event.getFlags()
+ ", keyCode=" + event.getKeyCode()
+ ", scanCode=" + event.getScanCode()
@@ -3182,7 +3192,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
event.getDeviceId(), event.getScanCode(),
flags, event.getSource(), event.getDisplayId(), null);
- if (!interceptFallback(win, fallbackEvent, policyFlags)) {
+ if (!interceptFallback(focusedToken, fallbackEvent, policyFlags)) {
fallbackEvent.recycle();
fallbackEvent = null;
}
@@ -3206,11 +3216,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return fallbackEvent;
}
- private boolean interceptFallback(WindowState win, KeyEvent fallbackEvent, int policyFlags) {
+ private boolean interceptFallback(IBinder focusedToken, KeyEvent fallbackEvent,
+ int policyFlags) {
int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags);
if ((actions & ACTION_PASS_TO_USER) != 0) {
long delayMillis = interceptKeyBeforeDispatching(
- win, fallbackEvent, policyFlags);
+ focusedToken, fallbackEvent, policyFlags);
if (delayMillis == 0) {
return true;
}
@@ -4543,6 +4554,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private void wakeUpFromPowerKey(long eventTime) {
wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey,
PowerManager.WAKE_REASON_POWER_BUTTON, "android.policy:POWER");
+
+ // Turn on the connected TV and switch HDMI input if we're a HDMI playback device.
+ final HdmiControl hdmiControl = getHdmiControl();
+ if (hdmiControl != null) {
+ hdmiControl.turnOnTv();
+ }
}
private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, @WakeReason int reason,
@@ -4575,7 +4592,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// ... eventually calls finishWindowsDrawn which will finalize our screen turn on
// as well as enabling the orientation change logic/sensor.
mWindowManagerInternal.waitForAllWindowsDrawn(mWindowManagerDrawCallback,
- WAITING_FOR_DRAWN_TIMEOUT);
+ WAITING_FOR_DRAWN_TIMEOUT, INVALID_DISPLAY);
}
// Called on the DisplayManager's DisplayPowerController thread.
@@ -4908,7 +4925,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override public void run() {
if (mBootMsgDialog == null) {
int theme;
- if (mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK)) {
+ if (mPackageManager.hasSystemFeature(FEATURE_LEANBACK)) {
theme = com.android.internal.R.style.Theme_Leanback_Dialog_Alert;
} else {
theme = 0;
@@ -4937,7 +4954,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return true;
}
};
- if (mContext.getPackageManager().isDeviceUpgrading()) {
+ if (mPackageManager.isDeviceUpgrading()) {
mBootMsgDialog.setTitle(R.string.android_upgrading_title);
} else {
mBootMsgDialog.setTitle(R.string.android_start_title);
@@ -5197,7 +5214,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
ActivityInfo ai = null;
- ResolveInfo info = mContext.getPackageManager().resolveActivityAsUser(
+ ResolveInfo info = mPackageManager.resolveActivityAsUser(
intent,
PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,
mCurrentUserId);
@@ -5225,7 +5242,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
awakenDreams();
}
- if (!isUserSetupComplete()) {
+ if (!mHasFeatureAuto && !isUserSetupComplete()) {
Slog.i(TAG, "Not going home because user setup is in progress.");
return;
}
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
index c1a6dbd8ae14..b0f22e4248ba 100644
--- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
+++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
@@ -18,9 +18,6 @@ package com.android.server.policy;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
-import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.MODE_DEFAULT;
-import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
import static android.app.AppOpsManager.OP_NONE;
import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
@@ -55,22 +52,7 @@ public abstract class SoftRestrictedPermissionPolicy {
private static final SoftRestrictedPermissionPolicy DUMMY_POLICY =
new SoftRestrictedPermissionPolicy() {
@Override
- public int resolveAppOp() {
- return OP_NONE;
- }
-
- @Override
- public int getDesiredOpMode() {
- return MODE_DEFAULT;
- }
-
- @Override
- public boolean shouldSetAppOpIfNotDefault() {
- return false;
- }
-
- @Override
- public boolean canBeGranted() {
+ public boolean mayGrantPermission() {
return true;
}
};
@@ -114,10 +96,8 @@ public abstract class SoftRestrictedPermissionPolicy {
* Get the policy for a soft restricted permission.
*
* @param context A context to use
- * @param appInfo The application the permission belongs to. Can be {@code null}, but then
- * only {@link #resolveAppOp} will work.
- * @param user The user the app belongs to. Can be {@code null}, but then only
- * {@link #resolveAppOp} will work.
+ * @param appInfo The application the permission belongs to.
+ * @param user The user the app belongs to.
* @param permission The name of the permission
*
* @return The policy for this permission
@@ -130,82 +110,46 @@ public abstract class SoftRestrictedPermissionPolicy {
// where the restricted state allows the permission but only for accessing the medial
// collections.
case READ_EXTERNAL_STORAGE: {
- final int flags;
- final boolean applyRestriction;
final boolean isWhiteListed;
- final boolean hasRequestedLegacyExternalStorage;
+ boolean shouldApplyRestriction;
final int targetSDK;
+ final boolean hasRequestedLegacyExternalStorage;
if (appInfo != null) {
PackageManager pm = context.getPackageManager();
- flags = pm.getPermissionFlags(permission, appInfo.packageName, user);
- applyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
+ int flags = pm.getPermissionFlags(permission, appInfo.packageName, user);
isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
+ shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
targetSDK = getMinimumTargetSDK(context, appInfo, user);
-
- boolean hasAnyRequestedLegacyExternalStorage =
- appInfo.hasRequestedLegacyExternalStorage();
-
- // hasRequestedLegacyExternalStorage is per package. To make sure two apps in
- // the same shared UID do not fight over what to set, always compute the
- // combined hasRequestedLegacyExternalStorage
- String[] uidPkgs = pm.getPackagesForUid(appInfo.uid);
- if (uidPkgs != null) {
- for (String uidPkg : uidPkgs) {
- if (!uidPkg.equals(appInfo.packageName)) {
- ApplicationInfo uidPkgInfo;
- try {
- uidPkgInfo = pm.getApplicationInfoAsUser(uidPkg, 0, user);
- } catch (PackageManager.NameNotFoundException e) {
- continue;
- }
-
- hasAnyRequestedLegacyExternalStorage |=
- uidPkgInfo.hasRequestedLegacyExternalStorage();
- }
- }
- }
-
- hasRequestedLegacyExternalStorage = hasAnyRequestedLegacyExternalStorage;
+ hasRequestedLegacyExternalStorage = hasUidRequestedLegacyExternalStorage(
+ appInfo.uid, context);
} else {
- flags = 0;
- applyRestriction = false;
isWhiteListed = false;
- hasRequestedLegacyExternalStorage = false;
+ shouldApplyRestriction = false;
targetSDK = 0;
+ hasRequestedLegacyExternalStorage = false;
}
+ // We have a check in PermissionPolicyService.PermissionToOpSynchroniser.setUidMode
+ // to prevent apps losing files in legacy storage, because we are holding the
+ // package manager lock here. If we ever remove this policy that check should be
+ // removed as well.
return new SoftRestrictedPermissionPolicy() {
@Override
- public int resolveAppOp() {
- return OP_LEGACY_STORAGE;
+ public boolean mayGrantPermission() {
+ return isWhiteListed || targetSDK >= Build.VERSION_CODES.Q;
}
-
@Override
- public int getDesiredOpMode() {
- if (applyRestriction) {
- return MODE_DEFAULT;
- } else if (hasRequestedLegacyExternalStorage) {
- return MODE_ALLOWED;
- } else {
- return MODE_IGNORED;
- }
+ public int getExtraAppOpCode() {
+ return OP_LEGACY_STORAGE;
}
-
@Override
- public boolean shouldSetAppOpIfNotDefault() {
- // Do not switch from allowed -> ignored as this would mean to retroactively
- // turn on isolated storage. This will make the app loose all its files.
- return getDesiredOpMode() != MODE_IGNORED;
+ public boolean mayAllowExtraAppOp() {
+ return !shouldApplyRestriction && hasRequestedLegacyExternalStorage;
}
-
@Override
- public boolean canBeGranted() {
- if (isWhiteListed || targetSDK >= Build.VERSION_CODES.Q) {
- return true;
- } else {
- return false;
- }
+ public boolean mayDenyExtraAppOpIfGranted() {
+ return shouldApplyRestriction;
}
};
}
@@ -225,22 +169,7 @@ public abstract class SoftRestrictedPermissionPolicy {
return new SoftRestrictedPermissionPolicy() {
@Override
- public int resolveAppOp() {
- return OP_NONE;
- }
-
- @Override
- public int getDesiredOpMode() {
- return MODE_DEFAULT;
- }
-
- @Override
- public boolean shouldSetAppOpIfNotDefault() {
- return false;
- }
-
- @Override
- public boolean canBeGranted() {
+ public boolean mayGrantPermission() {
return isWhiteListed || targetSDK >= Build.VERSION_CODES.Q;
}
};
@@ -250,25 +179,51 @@ public abstract class SoftRestrictedPermissionPolicy {
}
}
+ private static boolean hasUidRequestedLegacyExternalStorage(int uid, @NonNull Context context) {
+ PackageManager packageManager = context.getPackageManager();
+ String[] packageNames = packageManager.getPackagesForUid(uid);
+ if (packageNames == null) {
+ return false;
+ }
+ UserHandle user = UserHandle.getUserHandleForUid(uid);
+ for (String packageName : packageNames) {
+ ApplicationInfo applicationInfo;
+ try {
+ applicationInfo = packageManager.getApplicationInfoAsUser(packageName, 0, user);
+ } catch (PackageManager.NameNotFoundException e) {
+ continue;
+ }
+ if (applicationInfo.hasRequestedLegacyExternalStorage()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
- * @return An app op to be changed based on the state of the permission or
- * {@link AppOpsManager#OP_NONE} if not app-op should be set.
+ * @return If the permission can be granted
*/
- public abstract int resolveAppOp();
+ public abstract boolean mayGrantPermission();
/**
- * @return The mode the {@link #resolveAppOp() app op} should be in.
+ * @return An app op to be changed based on the state of the permission or
+ * {@link AppOpsManager#OP_NONE} if not app-op should be set.
*/
- public abstract @AppOpsManager.Mode int getDesiredOpMode();
+ public int getExtraAppOpCode() {
+ return OP_NONE;
+ }
/**
- * @return If the {@link #resolveAppOp() app op} should be set even if the app-op is currently
- * not {@link AppOpsManager#MODE_DEFAULT}.
+ * @return Whether the {@link #getExtraAppOpCode() app op} may be granted.
*/
- public abstract boolean shouldSetAppOpIfNotDefault();
+ public boolean mayAllowExtraAppOp() {
+ return false;
+ }
/**
- * @return If the permission can be granted
+ * @return Whether the {@link #getExtraAppOpCode() app op} may be denied if was granted.
*/
- public abstract boolean canBeGranted();
+ public boolean mayDenyExtraAppOpIfGranted() {
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 6d9c71096cb0..01250db9c5c6 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -173,7 +173,7 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
/**
* Interface to the Window Manager state associated with a particular
- * window. You can hold on to an instance of this interface from the call
+ * window. You can hold on to an instance of this interface from the call
* to prepareAddWindow() until removeWindow().
*/
public interface WindowState {
@@ -1025,7 +1025,7 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
* behavior for keys that can not be overridden by applications.
* This method is called from the input thread, with no locks held.
*
- * @param win The window that currently has focus. This is where the key
+ * @param focusedToken Client window token that currently has focus. This is where the key
* event will normally go.
* @param event The key event.
* @param policyFlags The policy flags associated with the key.
@@ -1034,7 +1034,7 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
* milliseconds by which the key dispatch should be delayed before trying
* again.
*/
- public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags);
+ long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event, int policyFlags);
/**
* Called from the input dispatcher thread when an application did not handle
@@ -1043,14 +1043,14 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
* <p>Allows you to define default global behavior for keys that were not handled
* by applications. This method is called from the input thread, with no locks held.
*
- * @param win The window that currently has focus. This is where the key
+ * @param focusedToken Client window token that currently has focus. This is where the key
* event will normally go.
* @param event The key event.
* @param policyFlags The policy flags associated with the key.
* @return Returns an alternate key event to redispatch as a fallback, or null to give up.
* The caller is responsible for recycling the key event.
*/
- public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags);
+ KeyEvent dispatchUnhandledKey(IBinder focusedToken, KeyEvent event, int policyFlags);
/**
* Called when the top focused display is changed.
diff --git a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
index 712012d9e621..017c684e7449 100644
--- a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
+++ b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
@@ -21,6 +21,7 @@ import android.annotation.UserIdInt;
import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
@@ -34,9 +35,9 @@ import com.android.internal.util.CollectionUtils;
import com.android.server.LocalServices;
import com.android.server.role.RoleManagerService;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/**
* Logic to retrieve the various legacy(pre-Q) equivalents of role holders.
@@ -125,9 +126,21 @@ public class LegacyRoleResolutionPolicy implements RoleManagerService.RoleHolder
}
case RoleManager.ROLE_HOME: {
PackageManager packageManager = mContext.getPackageManager();
- List<ResolveInfo> resolveInfos = new ArrayList<>();
- ComponentName componentName = packageManager.getHomeActivities(resolveInfos);
- String packageName = componentName != null ? componentName.getPackageName() : null;
+ String packageName;
+ if (packageManager.isDeviceUpgrading()) {
+ ResolveInfo resolveInfo = packageManager.resolveActivityAsUser(
+ new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME),
+ PackageManager.MATCH_DEFAULT_ONLY
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+ packageName = resolveInfo != null && resolveInfo.activityInfo != null
+ ? resolveInfo.activityInfo.packageName : null;
+ if (packageName != null && isSettingsApplication(packageName, userId)) {
+ packageName = null;
+ }
+ } else {
+ packageName = null;
+ }
return CollectionUtils.singletonOrEmpty(packageName);
}
case RoleManager.ROLE_EMERGENCY: {
@@ -142,4 +155,16 @@ public class LegacyRoleResolutionPolicy implements RoleManagerService.RoleHolder
}
}
}
+
+ private boolean isSettingsApplication(@NonNull String packageName, @UserIdInt int userId) {
+ PackageManager packageManager = mContext.getPackageManager();
+ ResolveInfo resolveInfo = packageManager.resolveActivityAsUser(new Intent(
+ Settings.ACTION_SETTINGS), PackageManager.MATCH_DEFAULT_ONLY
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+ if (resolveInfo == null || resolveInfo.activityInfo == null) {
+ return false;
+ }
+ return Objects.equals(packageName, resolveInfo.activityInfo.packageName);
+ }
}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index edf0cbfe459a..b67d9b285acd 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -94,16 +94,16 @@ public class Notifier {
private static final int MSG_PROFILE_TIMED_OUT = 5;
private static final int MSG_WIRED_CHARGING_STARTED = 6;
- private static final long[] WIRELESS_VIBRATION_TIME = {
+ private static final long[] CHARGING_VIBRATION_TIME = {
40, 40, 40, 40, 40, 40, 40, 40, 40, // ramp-up sampling rate = 40ms
40, 40, 40, 40, 40, 40, 40 // ramp-down sampling rate = 40ms
};
- private static final int[] WIRELESS_VIBRATION_AMPLITUDE = {
+ private static final int[] CHARGING_VIBRATION_AMPLITUDE = {
1, 4, 11, 25, 44, 67, 91, 114, 123, // ramp-up amplitude (from 0 to 50%)
103, 79, 55, 34, 17, 7, 2 // ramp-up amplitude
};
- private static final VibrationEffect WIRELESS_CHARGING_VIBRATION_EFFECT =
- VibrationEffect.createWaveform(WIRELESS_VIBRATION_TIME, WIRELESS_VIBRATION_AMPLITUDE,
+ private static final VibrationEffect CHARGING_VIBRATION_EFFECT =
+ VibrationEffect.createWaveform(CHARGING_VIBRATION_TIME, CHARGING_VIBRATION_AMPLITUDE,
-1);
private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
@@ -130,6 +130,10 @@ public class Notifier {
// True if the device should suspend when the screen is off due to proximity.
private final boolean mSuspendWhenScreenOffDueToProximityConfig;
+ // True if the device should show the wireless charging animation when the device
+ // begins charging wirelessly
+ private final boolean mShowWirelessChargingAnimationConfig;
+
// The current interactive state. This is set as soon as an interactive state
// transition begins so as to capture the reason that it happened. At some point
// this state will propagate to the pending state then eventually to the
@@ -182,6 +186,8 @@ public class Notifier {
mSuspendWhenScreenOffDueToProximityConfig = context.getResources().getBoolean(
com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity);
+ mShowWirelessChargingAnimationConfig = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_showBuiltinWirelessChargingAnim);
// Initialize interactive state for battery stats.
try {
@@ -755,35 +761,45 @@ public class Notifier {
}
};
- /**
- * If enabled, plays a sound and/or vibration when wireless or non-wireless charging has started
- */
- private void playChargingStartedFeedback(@UserIdInt int userId) {
- playChargingStartedVibration(userId);
+ private void playChargingStartedFeedback(@UserIdInt int userId, boolean wireless) {
+ if (!isChargingFeedbackEnabled(userId)) {
+ return;
+ }
+
+ // vibrate
+ final boolean vibrate = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0;
+ if (vibrate) {
+ mVibrator.vibrate(CHARGING_VIBRATION_EFFECT, VIBRATION_ATTRIBUTES);
+ }
+
+ // play sound
final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
- Settings.Global.CHARGING_STARTED_SOUND);
- if (isChargingFeedbackEnabled(userId) && soundPath != null) {
- final Uri soundUri = Uri.parse("file://" + soundPath);
- if (soundUri != null) {
- final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
- if (sfx != null) {
- sfx.setStreamType(AudioManager.STREAM_SYSTEM);
- sfx.play();
- }
+ wireless ? Settings.Global.WIRELESS_CHARGING_STARTED_SOUND
+ : Settings.Global.CHARGING_STARTED_SOUND);
+ final Uri soundUri = Uri.parse("file://" + soundPath);
+ if (soundUri != null) {
+ final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
+ if (sfx != null) {
+ sfx.setStreamType(AudioManager.STREAM_SYSTEM);
+ sfx.play();
}
}
}
private void showWirelessChargingStarted(int batteryLevel, @UserIdInt int userId) {
- playChargingStartedFeedback(userId);
- if (mStatusBarManagerInternal != null) {
+ // play sounds + haptics
+ playChargingStartedFeedback(userId, true /* wireless */);
+
+ // show animation
+ if (mShowWirelessChargingAnimationConfig && mStatusBarManagerInternal != null) {
mStatusBarManagerInternal.showChargingAnimation(batteryLevel);
}
mSuspendBlocker.release();
}
private void showWiredChargingStarted(@UserIdInt int userId) {
- playChargingStartedFeedback(userId);
+ playChargingStartedFeedback(userId, false /* wireless */);
mSuspendBlocker.release();
}
@@ -791,14 +807,6 @@ public class Notifier {
mTrustManager.setDeviceLockedForUser(userId, true /*locked*/);
}
- private void playChargingStartedVibration(@UserIdInt int userId) {
- final boolean vibrateEnabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0;
- if (vibrateEnabled && isChargingFeedbackEnabled(userId)) {
- mVibrator.vibrate(WIRELESS_CHARGING_VIBRATION_EFFECT, VIBRATION_ATTRIBUTES);
- }
- }
-
private boolean isChargingFeedbackEnabled(@UserIdInt int userId) {
final boolean enabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.CHARGING_SOUNDS_ENABLED, 1, userId) != 0;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index eb648b33c4ca..befe4e968b12 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -548,12 +548,16 @@ public final class PowerManagerService extends SystemService
private final class ForegroundProfileObserver extends SynchronousUserSwitchObserver {
@Override
- public void onUserSwitching(int newUserId) throws RemoteException {}
+ public void onUserSwitching(@UserIdInt int newUserId) throws RemoteException {
+ synchronized (mLock) {
+ mUserId = newUserId;
+ }
+ }
@Override
public void onForegroundProfileSwitch(@UserIdInt int newProfileId) throws RemoteException {
final long now = SystemClock.uptimeMillis();
- synchronized(mLock) {
+ synchronized (mLock) {
mForegroundProfile = newProfileId;
maybeUpdateForegroundProfileLastActivityLocked(now);
}
@@ -562,6 +566,8 @@ public final class PowerManagerService extends SystemService
// User id corresponding to activity the user is currently interacting with.
private @UserIdInt int mForegroundProfile;
+ // User id of main profile for the current user (doesn't include managed profiles)
+ private @UserIdInt int mUserId;
// Per-profile state to track when a profile should be locked.
private final SparseArray<ProfilePowerState> mProfilePowerState = new SparseArray<>();
@@ -1792,9 +1798,9 @@ public final class PowerManagerService extends SystemService
if (mBootCompleted) {
if (mIsPowered && !BatteryManager.isPlugWired(oldPlugType)
&& BatteryManager.isPlugWired(mPlugType)) {
- mNotifier.onWiredChargingStarted(mForegroundProfile);
+ mNotifier.onWiredChargingStarted(mUserId);
} else if (dockedOnWirelessCharger) {
- mNotifier.onWirelessChargingStarted(mBatteryLevel, mForegroundProfile);
+ mNotifier.onWirelessChargingStarted(mBatteryLevel, mUserId);
}
}
}
@@ -3493,6 +3499,7 @@ public final class PowerManagerService extends SystemService
pw.println(" mDoubleTapWakeEnabled=" + mDoubleTapWakeEnabled);
pw.println(" mIsVrModeEnabled=" + mIsVrModeEnabled);
pw.println(" mForegroundProfile=" + mForegroundProfile);
+ pw.println(" mUserId=" + mUserId);
final long sleepTimeout = getSleepTimeoutLocked();
final long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout);
diff --git a/services/core/java/com/android/server/protolog/ProtoLogImpl.java b/services/core/java/com/android/server/protolog/ProtoLogImpl.java
new file mode 100644
index 000000000000..1653b3d2ae28
--- /dev/null
+++ b/services/core/java/com/android/server/protolog/ProtoLogImpl.java
@@ -0,0 +1,466 @@
+/*
+ * 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.protolog;
+
+import static com.android.server.protolog.ProtoLogFileProto.LOG;
+import static com.android.server.protolog.ProtoLogFileProto.MAGIC_NUMBER;
+import static com.android.server.protolog.ProtoLogFileProto.MAGIC_NUMBER_H;
+import static com.android.server.protolog.ProtoLogFileProto.MAGIC_NUMBER_L;
+import static com.android.server.protolog.ProtoLogFileProto.REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS;
+import static com.android.server.protolog.ProtoLogFileProto.VERSION;
+import static com.android.server.protolog.ProtoLogMessage.BOOLEAN_PARAMS;
+import static com.android.server.protolog.ProtoLogMessage.DOUBLE_PARAMS;
+import static com.android.server.protolog.ProtoLogMessage.ELAPSED_REALTIME_NANOS;
+import static com.android.server.protolog.ProtoLogMessage.MESSAGE_HASH;
+import static com.android.server.protolog.ProtoLogMessage.SINT64_PARAMS;
+import static com.android.server.protolog.ProtoLogMessage.STR_PARAMS;
+
+import android.annotation.Nullable;
+import android.os.ShellCommand;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.protolog.common.IProtoLogGroup;
+import com.android.server.protolog.common.LogDataType;
+import com.android.server.utils.TraceBuffer;
+import com.android.server.wm.ProtoLogGroup;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.IllegalFormatConversionException;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+
+
+/**
+ * A service for the ProtoLog logging system.
+ */
+public class ProtoLogImpl {
+ private static final TreeMap<String, IProtoLogGroup> LOG_GROUPS = new TreeMap<>();
+
+ /**
+ * A runnable to update the cached output of {@link #isEnabled}.
+ *
+ * Must be invoked after every action that could change the result of {@link #isEnabled}, eg.
+ * starting / stopping proto log, or enabling / disabling log groups.
+ */
+ static Runnable sCacheUpdater = () -> { };
+
+ private static void addLogGroupEnum(IProtoLogGroup[] config) {
+ for (IProtoLogGroup group : config) {
+ LOG_GROUPS.put(group.name(), group);
+ }
+ }
+
+ static {
+ addLogGroupEnum(ProtoLogGroup.values());
+ }
+
+ /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+ public static void d(IProtoLogGroup group, int messageHash, int paramsMask,
+ @Nullable String messageString,
+ Object... args) {
+ getSingleInstance()
+ .log(LogLevel.DEBUG, group, messageHash, paramsMask, messageString, args);
+ }
+
+ /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+ public static void v(IProtoLogGroup group, int messageHash, int paramsMask,
+ @Nullable String messageString,
+ Object... args) {
+ getSingleInstance().log(LogLevel.VERBOSE, group, messageHash, paramsMask, messageString,
+ args);
+ }
+
+ /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+ public static void i(IProtoLogGroup group, int messageHash, int paramsMask,
+ @Nullable String messageString,
+ Object... args) {
+ getSingleInstance().log(LogLevel.INFO, group, messageHash, paramsMask, messageString, args);
+ }
+
+ /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+ public static void w(IProtoLogGroup group, int messageHash, int paramsMask,
+ @Nullable String messageString,
+ Object... args) {
+ getSingleInstance().log(LogLevel.WARN, group, messageHash, paramsMask, messageString, args);
+ }
+
+ /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+ public static void e(IProtoLogGroup group, int messageHash, int paramsMask,
+ @Nullable String messageString,
+ Object... args) {
+ getSingleInstance()
+ .log(LogLevel.ERROR, group, messageHash, paramsMask, messageString, args);
+ }
+
+ /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+ public static void wtf(IProtoLogGroup group, int messageHash, int paramsMask,
+ @Nullable String messageString,
+ Object... args) {
+ getSingleInstance().log(LogLevel.WTF, group, messageHash, paramsMask, messageString, args);
+ }
+
+ /** Returns true iff logging is enabled for the given {@code IProtoLogGroup}. */
+ public static boolean isEnabled(IProtoLogGroup group) {
+ return group.isLogToLogcat()
+ || (group.isLogToProto() && getSingleInstance().isProtoEnabled());
+ }
+
+ private static final int BUFFER_CAPACITY = 1024 * 1024;
+ private static final String LOG_FILENAME = "/data/misc/wmtrace/wm_log.pb";
+ private static final String VIEWER_CONFIG_FILENAME = "/system/etc/protolog.conf.json.gz";
+ private static final String TAG = "ProtoLog";
+ private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
+ static final String PROTOLOG_VERSION = "1.0.0";
+
+ private final File mLogFile;
+ private final TraceBuffer mBuffer;
+ private final ProtoLogViewerConfigReader mViewerConfig;
+
+ private boolean mProtoLogEnabled;
+ private boolean mProtoLogEnabledLockFree;
+ private final Object mProtoLogEnabledLock = new Object();
+
+ private static ProtoLogImpl sServiceInstance = null;
+
+ /**
+ * Returns the single instance of the ProtoLogImpl singleton class.
+ */
+ public static synchronized ProtoLogImpl getSingleInstance() {
+ if (sServiceInstance == null) {
+ sServiceInstance = new ProtoLogImpl(new File(LOG_FILENAME), BUFFER_CAPACITY,
+ new ProtoLogViewerConfigReader());
+ }
+ return sServiceInstance;
+ }
+
+ @VisibleForTesting
+ public static synchronized void setSingleInstance(@Nullable ProtoLogImpl instance) {
+ sServiceInstance = instance;
+ }
+
+ @VisibleForTesting
+ public enum LogLevel {
+ DEBUG, VERBOSE, INFO, WARN, ERROR, WTF
+ }
+
+ /**
+ * Main log method, do not call directly.
+ */
+ @VisibleForTesting
+ public void log(LogLevel level, IProtoLogGroup group, int messageHash, int paramsMask,
+ @Nullable String messageString, Object[] args) {
+ if (group.isLogToProto()) {
+ logToProto(messageHash, paramsMask, args);
+ }
+ if (group.isLogToLogcat()) {
+ logToLogcat(group.getTag(), level, messageHash, messageString, args);
+ }
+ }
+
+ private void logToLogcat(String tag, LogLevel level, int messageHash,
+ @Nullable String messageString, Object[] args) {
+ String message = null;
+ if (messageString == null) {
+ messageString = mViewerConfig.getViewerString(messageHash);
+ }
+ if (messageString != null) {
+ try {
+ message = String.format(messageString, args);
+ } catch (IllegalFormatConversionException ex) {
+ Slog.w(TAG, "Invalid ProtoLog format string.", ex);
+ }
+ }
+ if (message == null) {
+ StringBuilder builder = new StringBuilder("UNKNOWN MESSAGE (" + messageHash + ")");
+ for (Object o : args) {
+ builder.append(" ").append(o);
+ }
+ message = builder.toString();
+ }
+ passToLogcat(tag, level, message);
+ }
+
+ /**
+ * SLog wrapper.
+ */
+ @VisibleForTesting
+ public void passToLogcat(String tag, LogLevel level, String message) {
+ switch (level) {
+ case DEBUG:
+ Slog.d(tag, message);
+ break;
+ case VERBOSE:
+ Slog.v(tag, message);
+ break;
+ case INFO:
+ Slog.i(tag, message);
+ break;
+ case WARN:
+ Slog.w(tag, message);
+ break;
+ case ERROR:
+ Slog.e(tag, message);
+ break;
+ case WTF:
+ Slog.wtf(tag, message);
+ break;
+ }
+ }
+
+ private void logToProto(int messageHash, int paramsMask, Object[] args) {
+ if (!isProtoEnabled()) {
+ return;
+ }
+ try {
+ ProtoOutputStream os = new ProtoOutputStream();
+ long token = os.start(LOG);
+ os.write(MESSAGE_HASH, messageHash);
+ os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
+
+ if (args != null) {
+ int argIndex = 0;
+ ArrayList<Long> longParams = new ArrayList<>();
+ ArrayList<Double> doubleParams = new ArrayList<>();
+ ArrayList<Boolean> booleanParams = new ArrayList<>();
+ for (Object o : args) {
+ int type = LogDataType.bitmaskToLogDataType(paramsMask, argIndex);
+ try {
+ switch (type) {
+ case LogDataType.STRING:
+ os.write(STR_PARAMS, o.toString());
+ break;
+ case LogDataType.LONG:
+ longParams.add(((Number) o).longValue());
+ break;
+ case LogDataType.DOUBLE:
+ doubleParams.add(((Number) o).doubleValue());
+ break;
+ case LogDataType.BOOLEAN:
+ booleanParams.add((boolean) o);
+ break;
+ }
+ } catch (ClassCastException ex) {
+ // Should not happen unless there is an error in the ProtoLogTool.
+ os.write(STR_PARAMS, "(INVALID PARAMS_MASK) " + o.toString());
+ Slog.e(TAG, "Invalid ProtoLog paramsMask", ex);
+ }
+ argIndex++;
+ }
+ if (longParams.size() > 0) {
+ os.writePackedSInt64(SINT64_PARAMS,
+ longParams.stream().mapToLong(i -> i).toArray());
+ }
+ if (doubleParams.size() > 0) {
+ os.writePackedDouble(DOUBLE_PARAMS,
+ doubleParams.stream().mapToDouble(i -> i).toArray());
+ }
+ if (booleanParams.size() > 0) {
+ boolean[] arr = new boolean[booleanParams.size()];
+ for (int i = 0; i < booleanParams.size(); i++) {
+ arr[i] = booleanParams.get(i);
+ }
+ os.writePackedBool(BOOLEAN_PARAMS, arr);
+ }
+ }
+ os.end(token);
+ mBuffer.add(os);
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception while logging to proto", e);
+ }
+ }
+
+
+ @VisibleForTesting
+ ProtoLogImpl(File file, int bufferCapacity, ProtoLogViewerConfigReader viewerConfig) {
+ mLogFile = file;
+ mBuffer = new TraceBuffer(bufferCapacity);
+ mViewerConfig = viewerConfig;
+ }
+
+ /**
+ * Starts the logging a circular proto buffer.
+ *
+ * @param pw Print writer
+ */
+ public void startProtoLog(@Nullable PrintWriter pw) {
+ if (isProtoEnabled()) {
+ return;
+ }
+ synchronized (mProtoLogEnabledLock) {
+ logAndPrintln(pw, "Start logging to " + mLogFile + ".");
+ mBuffer.resetBuffer();
+ mProtoLogEnabled = true;
+ mProtoLogEnabledLockFree = true;
+ }
+ sCacheUpdater.run();
+ }
+
+ /**
+ * Stops logging to proto.
+ *
+ * @param pw Print writer
+ * @param writeToFile If the current buffer should be written to disk or not
+ */
+ public void stopProtoLog(@Nullable PrintWriter pw, boolean writeToFile) {
+ if (!isProtoEnabled()) {
+ return;
+ }
+ synchronized (mProtoLogEnabledLock) {
+ logAndPrintln(pw, "Stop logging to " + mLogFile + ". Waiting for log to flush.");
+ mProtoLogEnabled = mProtoLogEnabledLockFree = false;
+ if (writeToFile) {
+ writeProtoLogToFileLocked();
+ logAndPrintln(pw, "Log written to " + mLogFile + ".");
+ }
+ if (mProtoLogEnabled) {
+ logAndPrintln(pw, "ERROR: logging was re-enabled while waiting for flush.");
+ throw new IllegalStateException("logging enabled while waiting for flush.");
+ }
+ }
+ sCacheUpdater.run();
+ }
+
+ /**
+ * Returns {@code true} iff logging to proto is enabled.
+ */
+ public boolean isProtoEnabled() {
+ return mProtoLogEnabledLockFree;
+ }
+
+ private int setLogging(ShellCommand shell, boolean setTextLogging, boolean value) {
+ String group;
+ while ((group = shell.getNextArg()) != null) {
+ IProtoLogGroup g = LOG_GROUPS.get(group);
+ if (g != null) {
+ if (setTextLogging) {
+ g.setLogToLogcat(value);
+ } else {
+ g.setLogToProto(value);
+ }
+ } else {
+ logAndPrintln(shell.getOutPrintWriter(), "No IProtoLogGroup named " + group);
+ return -1;
+ }
+ }
+ sCacheUpdater.run();
+ return 0;
+ }
+
+ private int unknownCommand(PrintWriter pw) {
+ pw.println("Unknown command");
+ pw.println("Window manager logging options:");
+ pw.println(" start: Start proto logging");
+ pw.println(" stop: Stop proto logging");
+ pw.println(" enable [group...]: Enable proto logging for given groups");
+ pw.println(" disable [group...]: Disable proto logging for given groups");
+ pw.println(" enable-text [group...]: Enable logcat logging for given groups");
+ pw.println(" disable-text [group...]: Disable logcat logging for given groups");
+ return -1;
+ }
+
+ /**
+ * Responds to a shell command.
+ */
+ public int onShellCommand(ShellCommand shell) {
+ PrintWriter pw = shell.getOutPrintWriter();
+ String cmd = shell.getNextArg();
+ if (cmd == null) {
+ return unknownCommand(pw);
+ }
+ switch (cmd) {
+ case "start":
+ startProtoLog(pw);
+ return 0;
+ case "stop":
+ stopProtoLog(pw, true);
+ return 0;
+ case "status":
+ logAndPrintln(pw, getStatus());
+ return 0;
+ case "enable":
+ return setLogging(shell, false, true);
+ case "enable-text":
+ mViewerConfig.loadViewerConfig(pw, VIEWER_CONFIG_FILENAME);
+ return setLogging(shell, true, true);
+ case "disable":
+ return setLogging(shell, false, false);
+ case "disable-text":
+ return setLogging(shell, true, false);
+ default:
+ return unknownCommand(pw);
+ }
+ }
+
+ /**
+ * Returns a human-readable ProtoLog status text.
+ */
+ public String getStatus() {
+ return "ProtoLog status: "
+ + ((isProtoEnabled()) ? "Enabled" : "Disabled")
+ + "\nEnabled log groups: \n Proto: "
+ + LOG_GROUPS.values().stream().filter(
+ it -> it.isEnabled() && it.isLogToProto())
+ .map(IProtoLogGroup::name).collect(Collectors.joining(" "))
+ + "\n Logcat: "
+ + LOG_GROUPS.values().stream().filter(
+ it -> it.isEnabled() && it.isLogToLogcat())
+ .map(IProtoLogGroup::name).collect(Collectors.joining(" "))
+ + "\nLogging definitions loaded: " + mViewerConfig.knownViewerStringsNumber();
+ }
+
+ /**
+ * Writes the log buffer to a new file for the bugreport.
+ *
+ * This method is synchronized with {@code #startProtoLog(PrintWriter)} and
+ * {@link #stopProtoLog(PrintWriter, boolean)}.
+ */
+ public void writeProtoLogToFile() {
+ synchronized (mProtoLogEnabledLock) {
+ writeProtoLogToFileLocked();
+ }
+ }
+
+ private void writeProtoLogToFileLocked() {
+ try {
+ long offset =
+ (System.currentTimeMillis() - (SystemClock.elapsedRealtimeNanos() / 1000000));
+ ProtoOutputStream proto = new ProtoOutputStream();
+ proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+ proto.write(VERSION, PROTOLOG_VERSION);
+ proto.write(REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS, offset);
+ mBuffer.writeTraceToFile(mLogFile, proto);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to write buffer to file", e);
+ }
+ }
+
+
+ static void logAndPrintln(@Nullable PrintWriter pw, String msg) {
+ Slog.i(TAG, msg);
+ if (pw != null) {
+ pw.println(msg);
+ pw.flush();
+ }
+ }
+}
+
diff --git a/services/core/java/com/android/server/protolog/ProtoLogViewerConfigReader.java b/services/core/java/com/android/server/protolog/ProtoLogViewerConfigReader.java
new file mode 100644
index 000000000000..494421717800
--- /dev/null
+++ b/services/core/java/com/android/server/protolog/ProtoLogViewerConfigReader.java
@@ -0,0 +1,107 @@
+/*
+ * 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.protolog;
+
+import static com.android.server.protolog.ProtoLogImpl.logAndPrintln;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * Handles loading and parsing of ProtoLog viewer configuration.
+ */
+public class ProtoLogViewerConfigReader {
+ private Map<Integer, String> mLogMessageMap = null;
+
+ /** Returns message format string for its hash or null if unavailable. */
+ public synchronized String getViewerString(int messageHash) {
+ if (mLogMessageMap != null) {
+ return mLogMessageMap.get(messageHash);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Reads the specified viewer configuration file. Does nothing if the config is already loaded.
+ */
+ public synchronized void loadViewerConfig(PrintWriter pw, String viewerConfigFilename) {
+ if (mLogMessageMap != null) {
+ return;
+ }
+ try {
+ InputStreamReader config = new InputStreamReader(
+ new GZIPInputStream(new FileInputStream(viewerConfigFilename)));
+ BufferedReader reader = new BufferedReader(config);
+ StringBuilder builder = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ builder.append(line).append('\n');
+ }
+ reader.close();
+ JSONObject json = new JSONObject(builder.toString());
+ JSONObject messages = json.getJSONObject("messages");
+
+ mLogMessageMap = new TreeMap<>();
+ Iterator it = messages.keys();
+ while (it.hasNext()) {
+ String key = (String) it.next();
+ try {
+ int hash = Integer.parseInt(key);
+ JSONObject val = messages.getJSONObject(key);
+ String msg = val.getString("message");
+ mLogMessageMap.put(hash, msg);
+ } catch (NumberFormatException expected) {
+ // Not a messageHash - skip it
+ }
+ }
+ logAndPrintln(pw, "Loaded " + mLogMessageMap.size() + " log definitions from "
+ + viewerConfigFilename);
+ } catch (FileNotFoundException e) {
+ logAndPrintln(pw, "Unable to load log definitions: File "
+ + viewerConfigFilename + " not found." + e);
+ } catch (IOException e) {
+ logAndPrintln(pw, "Unable to load log definitions: IOException while reading "
+ + viewerConfigFilename + ". " + e);
+ } catch (JSONException e) {
+ logAndPrintln(pw,
+ "Unable to load log definitions: JSON parsing exception while reading "
+ + viewerConfigFilename + ". " + e);
+ }
+ }
+
+ /**
+ * Returns the number of loaded log definitions kept in memory.
+ */
+ public synchronized int knownViewerStringsNumber() {
+ if (mLogMessageMap != null) {
+ return mLogMessageMap.size();
+ }
+ return 0;
+ }
+}
diff --git a/services/core/java/com/android/server/protolog/common/BitmaskConversionException.java b/services/core/java/com/android/server/protolog/common/BitmaskConversionException.java
new file mode 100644
index 000000000000..7bb27b2d9bcd
--- /dev/null
+++ b/services/core/java/com/android/server/protolog/common/BitmaskConversionException.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.protolog.common;
+
+/**
+ * Error while converting a bitmask representing a list of LogDataTypes.
+ */
+public class BitmaskConversionException extends RuntimeException {
+ BitmaskConversionException(String msg) {
+ super(msg);
+ }
+}
diff --git a/services/core/java/com/android/server/protolog/common/IProtoLogGroup.java b/services/core/java/com/android/server/protolog/common/IProtoLogGroup.java
new file mode 100644
index 000000000000..2c65341453e9
--- /dev/null
+++ b/services/core/java/com/android/server/protolog/common/IProtoLogGroup.java
@@ -0,0 +1,64 @@
+/*
+ * 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.protolog.common;
+
+/**
+ * Defines a log group configuration object for ProtoLog. Should be implemented as en enum.
+ */
+public interface IProtoLogGroup {
+ /**
+ * if false all log statements for this group are excluded from compilation,
+ */
+ boolean isEnabled();
+
+ /**
+ * is binary logging enabled for the group.
+ */
+ boolean isLogToProto();
+
+ /**
+ * is text logging enabled for the group.
+ */
+ boolean isLogToLogcat();
+
+ /**
+ * returns true is any logging is enabled for this group.
+ */
+ default boolean isLogToAny() {
+ return isLogToLogcat() || isLogToProto();
+ }
+
+ /**
+ * returns the name of the source of the logged message
+ */
+ String getTag();
+
+ /**
+ * set binary logging for this group.
+ */
+ void setLogToProto(boolean logToProto);
+
+ /**
+ * set text logging for this group.
+ */
+ void setLogToLogcat(boolean logToLogcat);
+
+ /**
+ * returns name of the logging group.
+ */
+ String name();
+}
diff --git a/services/core/java/com/android/server/protolog/common/InvalidFormatStringException.java b/services/core/java/com/android/server/protolog/common/InvalidFormatStringException.java
new file mode 100644
index 000000000000..947bf98eea3c
--- /dev/null
+++ b/services/core/java/com/android/server/protolog/common/InvalidFormatStringException.java
@@ -0,0 +1,30 @@
+/*
+ * 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.protolog.common;
+
+/**
+ * Unsupported/invalid message format string error.
+ */
+public class InvalidFormatStringException extends RuntimeException {
+ public InvalidFormatStringException(String message) {
+ super(message);
+ }
+
+ public InvalidFormatStringException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/services/core/java/com/android/server/protolog/common/LogDataType.java b/services/core/java/com/android/server/protolog/common/LogDataType.java
new file mode 100644
index 000000000000..e73b41abddc7
--- /dev/null
+++ b/services/core/java/com/android/server/protolog/common/LogDataType.java
@@ -0,0 +1,102 @@
+/*
+ * 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.protolog.common;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a type of logged data encoded in the proto.
+ */
+public class LogDataType {
+ // When updating this list make sure to update bitmask conversion methods accordingly.
+ // STR type should be the first in the enum in order to be the default type.
+ public static final int STRING = 0b00;
+ public static final int LONG = 0b01;
+ public static final int DOUBLE = 0b10;
+ public static final int BOOLEAN = 0b11;
+
+ private static final int TYPE_WIDTH = 2;
+ private static final int TYPE_MASK = 0b11;
+
+ /**
+ * Creates a bitmask representing a list of data types.
+ */
+ public static int logDataTypesToBitMask(List<Integer> types) {
+ if (types.size() > 16) {
+ throw new BitmaskConversionException("Too many log call parameters "
+ + "- max 16 parameters supported");
+ }
+ int mask = 0;
+ for (int i = 0; i < types.size(); i++) {
+ int x = types.get(i);
+ mask = mask | (x << (i * TYPE_WIDTH));
+ }
+ return mask;
+ }
+
+ /**
+ * Decodes a bitmask to a list of LogDataTypes of provided length.
+ */
+ public static int bitmaskToLogDataType(int bitmask, int index) {
+ if (index > 16) {
+ throw new BitmaskConversionException("Max 16 parameters allowed");
+ }
+ return (bitmask >> (index * TYPE_WIDTH)) & TYPE_MASK;
+ }
+
+ /**
+ * Creates a list of LogDataTypes from a message format string.
+ */
+ public static List<Integer> parseFormatString(String messageString) {
+ ArrayList<Integer> types = new ArrayList<>();
+ for (int i = 0; i < messageString.length(); ) {
+ if (messageString.charAt(i) == '%') {
+ if (i + 1 >= messageString.length()) {
+ throw new InvalidFormatStringException("Invalid format string in config");
+ }
+ switch (messageString.charAt(i + 1)) {
+ case 'b':
+ types.add(LogDataType.BOOLEAN);
+ break;
+ case 'd':
+ case 'o':
+ case 'x':
+ types.add(LogDataType.LONG);
+ break;
+ case 'f':
+ case 'e':
+ case 'g':
+ types.add(LogDataType.DOUBLE);
+ break;
+ case 's':
+ types.add(LogDataType.STRING);
+ break;
+ case '%':
+ break;
+ default:
+ throw new InvalidFormatStringException("Invalid format string field"
+ + " %${messageString[i + 1]}");
+ }
+ i += 2;
+ } else {
+ i += 1;
+ }
+ }
+ return types;
+ }
+}
diff --git a/services/core/java/com/android/server/protolog/common/ProtoLog.java b/services/core/java/com/android/server/protolog/common/ProtoLog.java
new file mode 100644
index 000000000000..b631bcb23f5f
--- /dev/null
+++ b/services/core/java/com/android/server/protolog/common/ProtoLog.java
@@ -0,0 +1,115 @@
+/*
+ * 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.protolog.common;
+
+/**
+ * ProtoLog API - exposes static logging methods. Usage of this API is similar
+ * to {@code android.utils.Log} class. Instead of plain text log messages each call consists of
+ * a messageString, which is a format string for the log message (has to be a string literal or
+ * a concatenation of string literals) and a vararg array of parameters for the formatter.
+ *
+ * The syntax for the message string is a subset of {@code java.util.Formatter} syntax.
+ * Supported conversions:
+ * %b - boolean
+ * %d, %o and %x - integral type (Short, Integer or Long)
+ * %f, %e and %g - floating point type (Float or Double)
+ * %s - string
+ * %% - a literal percent character
+ * The width and precision modifiers are supported, argument_index and flags are not.
+ *
+ * Methods in this class are stubs, that are replaced by optimised versions by the ProtoLogTool
+ * during build.
+ */
+public class ProtoLog {
+ /**
+ * DEBUG level log.
+ *
+ * @param group {@code IProtoLogGroup} controlling this log call.
+ * @param messageString constant format string for the logged message.
+ * @param args parameters to be used with the format string.
+ */
+ public static void d(IProtoLogGroup group, String messageString, Object... args) {
+ // Stub, replaced by the ProtoLogTool.
+ throw new UnsupportedOperationException(
+ "ProtoLog calls MUST be processed with ProtoLogTool");
+ }
+
+ /**
+ * VERBOSE level log.
+ *
+ * @param group {@code IProtoLogGroup} controlling this log call.
+ * @param messageString constant format string for the logged message.
+ * @param args parameters to be used with the format string.
+ */
+ public static void v(IProtoLogGroup group, String messageString, Object... args) {
+ // Stub, replaced by the ProtoLogTool.
+ throw new UnsupportedOperationException(
+ "ProtoLog calls MUST be processed with ProtoLogTool");
+ }
+
+ /**
+ * INFO level log.
+ *
+ * @param group {@code IProtoLogGroup} controlling this log call.
+ * @param messageString constant format string for the logged message.
+ * @param args parameters to be used with the format string.
+ */
+ public static void i(IProtoLogGroup group, String messageString, Object... args) {
+ // Stub, replaced by the ProtoLogTool.
+ throw new UnsupportedOperationException(
+ "ProtoLog calls MUST be processed with ProtoLogTool");
+ }
+
+ /**
+ * WARNING level log.
+ *
+ * @param group {@code IProtoLogGroup} controlling this log call.
+ * @param messageString constant format string for the logged message.
+ * @param args parameters to be used with the format string.
+ */
+ public static void w(IProtoLogGroup group, String messageString, Object... args) {
+ // Stub, replaced by the ProtoLogTool.
+ throw new UnsupportedOperationException(
+ "ProtoLog calls MUST be processed with ProtoLogTool");
+ }
+
+ /**
+ * ERROR level log.
+ *
+ * @param group {@code IProtoLogGroup} controlling this log call.
+ * @param messageString constant format string for the logged message.
+ * @param args parameters to be used with the format string.
+ */
+ public static void e(IProtoLogGroup group, String messageString, Object... args) {
+ // Stub, replaced by the ProtoLogTool.
+ throw new UnsupportedOperationException(
+ "ProtoLog calls MUST be processed with ProtoLogTool");
+ }
+
+ /**
+ * WHAT A TERRIBLE FAILURE level log.
+ *
+ * @param group {@code IProtoLogGroup} controlling this log call.
+ * @param messageString constant format string for the logged message.
+ * @param args parameters to be used with the format string.
+ */
+ public static void wtf(IProtoLogGroup group, String messageString, Object... args) {
+ // Stub, replaced by the ProtoLogTool.
+ throw new UnsupportedOperationException(
+ "ProtoLog calls MUST be processed with ProtoLogTool");
+ }
+}
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index bf8c042835dd..aac0f906feaa 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -51,7 +51,6 @@ import android.os.ShellCallback;
import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.service.sms.FinancialSmsService;
-import android.telephony.IFinancialSmsCallback;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -269,6 +268,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
maybeMigrateRole(RoleManager.ROLE_DIALER, userId);
maybeMigrateRole(RoleManager.ROLE_SMS, userId);
maybeMigrateRole(RoleManager.ROLE_EMERGENCY, userId);
+ maybeMigrateRole(RoleManager.ROLE_HOME, userId);
// Some package state has changed, so grant default roles again.
Slog.i(LOG_TAG, "Granting default roles...");
@@ -701,40 +701,6 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
dumpOutputStream.flush();
}
- /**
- * Get filtered SMS messages for financial app.
- */
- @Override
- public void getSmsMessagesForFinancialApp(
- String callingPkg, Bundle params, IFinancialSmsCallback callback) {
- int mode = PermissionChecker.checkCallingOrSelfPermission(
- getContext(),
- AppOpsManager.OPSTR_SMS_FINANCIAL_TRANSACTIONS);
-
- if (mode == PermissionChecker.PERMISSION_GRANTED) {
- FinancialSmsManager financialSmsManager = new FinancialSmsManager(getContext());
- financialSmsManager.getSmsMessages(new RemoteCallback((result) -> {
- CursorWindow messages = null;
- if (result == null) {
- Slog.w(LOG_TAG, "result is null.");
- } else {
- messages = result.getParcelable(FinancialSmsService.EXTRA_SMS_MSGS);
- }
- try {
- callback.onGetSmsMessagesForFinancialApp(messages);
- } catch (RemoteException e) {
- // do nothing
- }
- }), params);
- } else {
- try {
- callback.onGetSmsMessagesForFinancialApp(null);
- } catch (RemoteException e) {
- // do nothing
- }
- }
- }
-
private int getUidForPackage(String packageName) {
long ident = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
index cae09ea37f2a..1123f70b4334 100644
--- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
+++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
@@ -23,17 +23,12 @@ import android.util.IntArray;
import android.util.Slog;
import android.util.SparseLongArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.Installer;
import com.android.server.pm.Installer.InstallerException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
/**
* Encapsulates the logic for initiating userdata snapshots and rollbacks via installd.
@@ -56,6 +51,8 @@ public class AppDataRollbackHelper {
* {@code userIds}. Updates said {@code packageRollbackInfo} with the inodes of the CE user data
* snapshot folders.
*/
+ @GuardedBy("rollback.mLock")
+ // TODO(b/136241838): Move into Rollback and synchronize there.
public void snapshotAppData(
int snapshotId, PackageRollbackInfo packageRollbackInfo, int[] userIds) {
for (int user : userIds) {
@@ -81,7 +78,6 @@ public class AppDataRollbackHelper {
+ packageRollbackInfo.getPackageName() + ", userId: " + user, ie);
}
}
- packageRollbackInfo.getSnapshottedUsers().addAll(IntArray.wrap(userIds));
}
/**
@@ -92,6 +88,8 @@ public class AppDataRollbackHelper {
* to {@code packageRollbackInfo} are restricted to the removal or addition of {@code
* userId} to the list of pending backups or restores.
*/
+ @GuardedBy("rollback.mLock")
+ // TODO(b/136241838): Move into Rollback and synchronize there.
public boolean restoreAppData(int rollbackId, PackageRollbackInfo packageRollbackInfo,
int userId, int appId, String seInfo) {
int storageFlags = Installer.FLAG_STORAGE_DE;
@@ -135,6 +133,8 @@ public class AppDataRollbackHelper {
* Deletes an app data snapshot with a given {@code rollbackId} for a specified package
* {@code packageName} for a given {@code user}.
*/
+ @GuardedBy("rollback.mLock")
+ // TODO(b/136241838): Move into Rollback and synchronize there.
public void destroyAppDataSnapshot(int rollbackId, PackageRollbackInfo packageRollbackInfo,
int user) {
int storageFlags = Installer.FLAG_STORAGE_DE;
@@ -156,141 +156,68 @@ public class AppDataRollbackHelper {
}
/**
- * Computes the list of pending backups for {@code userId} given lists of rollbacks.
- * Packages pending backup for the given user are added to {@code pendingBackupPackages} along
- * with their corresponding {@code PackageRollbackInfo}.
+ * Commits the pending backups and restores for a given {@code userId} and {@code rollback}. If
+ * the rollback has a pending backup, it is updated with a mapping from {@code userId} to inode
+ * of the CE user data snapshot.
*
- * @return the list of rollbacks that have pending backups. Note that some of the
- * backups won't be performed, because they might be counteracted by pending restores.
+ * @return true if any backups or restores were found for the userId
*/
- private static List<Rollback> computePendingBackups(int userId,
- Map<String, PackageRollbackInfo> pendingBackupPackages,
- List<Rollback> rollbacks) {
- List<Rollback> rollbacksWithPendingBackups = new ArrayList<>();
-
- for (Rollback rollback : rollbacks) {
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- final IntArray pendingBackupUsers = info.getPendingBackups();
- if (pendingBackupUsers != null) {
- final int idx = pendingBackupUsers.indexOf(userId);
- if (idx != -1) {
- pendingBackupPackages.put(info.getPackageName(), info);
- if (rollbacksWithPendingBackups.indexOf(rollback) == -1) {
- rollbacksWithPendingBackups.add(rollback);
- }
- }
+ @GuardedBy("rollback.mLock")
+ boolean commitPendingBackupAndRestoreForUser(int userId, Rollback rollback) {
+ boolean foundBackupOrRestore = false;
+ for (PackageRollbackInfo info : rollback.info.getPackages()) {
+ boolean hasPendingBackup = false;
+ boolean hasPendingRestore = false;
+ final IntArray pendingBackupUsers = info.getPendingBackups();
+ if (pendingBackupUsers != null) {
+ if (pendingBackupUsers.indexOf(userId) != -1) {
+ hasPendingBackup = true;
+ foundBackupOrRestore = true;
}
}
- }
- return rollbacksWithPendingBackups;
- }
-
- /**
- * Computes the list of pending restores for {@code userId} given lists of rollbacks.
- * Packages pending restore are added to {@code pendingRestores} along with their corresponding
- * {@code PackageRollbackInfo}.
- *
- * @return the list of rollbacks that have pending restores. Note that some of the
- * restores won't be performed, because they might be counteracted by pending backups.
- */
- private static List<Rollback> computePendingRestores(int userId,
- Map<String, PackageRollbackInfo> pendingRestorePackages,
- List<Rollback> rollbacks) {
- List<Rollback> rollbacksWithPendingRestores = new ArrayList<>();
- for (Rollback rollback : rollbacks) {
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- final RestoreInfo ri = info.getRestoreInfo(userId);
- if (ri != null) {
- pendingRestorePackages.put(info.getPackageName(), info);
- if (rollbacksWithPendingRestores.indexOf(rollback) == -1) {
- rollbacksWithPendingRestores.add(rollback);
- }
- }
+ RestoreInfo ri = info.getRestoreInfo(userId);
+ if (ri != null) {
+ hasPendingRestore = true;
+ foundBackupOrRestore = true;
}
- }
-
- return rollbacksWithPendingRestores;
- }
-
- /**
- * Commits the list of pending backups and restores for a given {@code userId}. For rollbacks
- * with pending backups, updates the {@code Rollback} instance with a mapping from
- * {@code userId} to inode of the CE user data snapshot.
- *
- * @return the set of rollbacks with changes that should be stored on disk.
- */
- public Set<Rollback> commitPendingBackupAndRestoreForUser(int userId,
- List<Rollback> rollbacks) {
- final Map<String, PackageRollbackInfo> pendingBackupPackages = new HashMap<>();
- final List<Rollback> pendingBackups = computePendingBackups(userId,
- pendingBackupPackages, rollbacks);
-
- final Map<String, PackageRollbackInfo> pendingRestorePackages = new HashMap<>();
- final List<Rollback> pendingRestores = computePendingRestores(userId,
- pendingRestorePackages, rollbacks);
-
- // First remove unnecessary backups, i.e. when user did not unlock their phone between the
- // request to backup data and the request to restore it.
- Iterator<Map.Entry<String, PackageRollbackInfo>> iter =
- pendingBackupPackages.entrySet().iterator();
- while (iter.hasNext()) {
- PackageRollbackInfo backupPackage = iter.next().getValue();
- PackageRollbackInfo restorePackage =
- pendingRestorePackages.get(backupPackage.getPackageName());
- if (restorePackage != null) {
- backupPackage.removePendingBackup(userId);
- backupPackage.removePendingRestoreInfo(userId);
- iter.remove();
- pendingRestorePackages.remove(backupPackage.getPackageName());
+ if (hasPendingBackup && hasPendingRestore) {
+ // Remove unnecessary backup, i.e. when user did not unlock their phone between the
+ // request to backup data and the request to restore it.
+ info.removePendingBackup(userId);
+ info.removePendingRestoreInfo(userId);
+ continue;
}
- }
- if (!pendingBackupPackages.isEmpty()) {
- for (Rollback rollback : pendingBackups) {
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- final IntArray pendingBackupUsers = info.getPendingBackups();
- final int idx = pendingBackupUsers.indexOf(userId);
- if (idx != -1) {
- try {
- long ceSnapshotInode = mInstaller.snapshotAppData(info.getPackageName(),
- userId, rollback.info.getRollbackId(),
- Installer.FLAG_STORAGE_CE);
- info.putCeSnapshotInode(userId, ceSnapshotInode);
- pendingBackupUsers.remove(idx);
- } catch (InstallerException ie) {
- Slog.e(TAG,
- "Unable to create app data snapshot for: "
+ if (hasPendingBackup) {
+ int idx = pendingBackupUsers.indexOf(userId);
+ try {
+ long ceSnapshotInode = mInstaller.snapshotAppData(info.getPackageName(),
+ userId, rollback.info.getRollbackId(),
+ Installer.FLAG_STORAGE_CE);
+ info.putCeSnapshotInode(userId, ceSnapshotInode);
+ pendingBackupUsers.remove(idx);
+ } catch (InstallerException ie) {
+ Slog.e(TAG,
+ "Unable to create app data snapshot for: "
+ info.getPackageName() + ", userId: " + userId, ie);
- }
- }
}
}
- }
- if (!pendingRestorePackages.isEmpty()) {
- for (Rollback rollback : pendingRestores) {
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- final RestoreInfo ri = info.getRestoreInfo(userId);
- if (ri != null) {
- try {
- mInstaller.restoreAppDataSnapshot(info.getPackageName(), ri.appId,
- ri.seInfo, userId, rollback.info.getRollbackId(),
- Installer.FLAG_STORAGE_CE);
- info.removeRestoreInfo(ri);
- } catch (InstallerException ie) {
- Slog.e(TAG, "Unable to restore app data snapshot for: "
- + info.getPackageName(), ie);
- }
- }
+ if (hasPendingRestore) {
+ try {
+ mInstaller.restoreAppDataSnapshot(info.getPackageName(), ri.appId,
+ ri.seInfo, userId, rollback.info.getRollbackId(),
+ Installer.FLAG_STORAGE_CE);
+ info.removeRestoreInfo(ri);
+ } catch (InstallerException ie) {
+ Slog.e(TAG, "Unable to restore app data snapshot for: "
+ + info.getPackageName(), ie);
}
}
}
-
- final Set<Rollback> changed = new HashSet<>(pendingBackups);
- changed.addAll(pendingRestores);
- return changed;
+ return foundBackupOrRestore;
}
/**
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 6769fe07bbf8..8b79c3ff66c6 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -16,27 +16,57 @@
package com.android.server.rollback;
+import static com.android.server.rollback.RollbackManagerServiceImpl.sendFailure;
+
+import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.content.pm.VersionedPackage;
+import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.RollbackInfo;
+import android.content.rollback.RollbackManager;
+import android.os.Binder;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.TextUtils;
+import android.util.IntArray;
+import android.util.Slog;
+import android.util.SparseLongArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.IndentingPrintWriter;
import java.io.File;
+import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.text.ParseException;
import java.time.Instant;
import java.util.ArrayList;
+import java.util.List;
/**
- * Information about a rollback available for a set of atomically installed
- * packages.
+ * Information about a rollback available for a set of atomically installed packages.
*/
class Rollback {
+
+ private static final String TAG = "RollbackManager";
+
@IntDef(flag = true, prefix = { "ROLLBACK_STATE_" }, value = {
ROLLBACK_STATE_ENABLING,
ROLLBACK_STATE_AVAILABLE,
ROLLBACK_STATE_COMMITTED,
+ ROLLBACK_STATE_DELETED
})
@Retention(RetentionPolicy.SOURCE)
@interface RollbackState {}
@@ -58,6 +88,17 @@ class Rollback {
static final int ROLLBACK_STATE_COMMITTED = 3;
/**
+ * The rollback has been deleted.
+ */
+ static final int ROLLBACK_STATE_DELETED = 4;
+
+ /**
+ * The session ID for the staged session if this rollback data represents a staged session,
+ * {@code -1} otherwise.
+ */
+ private final int mStagedSessionId;
+
+ /**
* The rollback info for this rollback.
*/
public final RollbackInfo info;
@@ -74,23 +115,20 @@ class Rollback {
* The timestamp is not applicable for all rollback states, but we make
* sure to keep it non-null to avoid potential errors there.
*/
+ @GuardedBy("mLock")
private @NonNull Instant mTimestamp;
/**
- * The session ID for the staged session if this rollback data represents a staged session,
- * {@code -1} otherwise.
- */
- private final int mStagedSessionId;
-
- /**
* The current state of the rollback.
* ENABLING, AVAILABLE, or COMMITTED.
*/
+ @GuardedBy("mLock")
private @RollbackState int mState;
/**
* The id of the post-reboot apk session for a staged install, if any.
*/
+ @GuardedBy("mLock")
private int mApkSessionId = -1;
/**
@@ -98,22 +136,46 @@ class Rollback {
* for this rollback because it has just been committed but the rollback
* has not yet been fully applied.
*/
- // NOTE: All accesses to this field are from the RollbackManager handler thread.
+ @GuardedBy("mLock")
private boolean mRestoreUserDataInProgress = false;
/**
+ * Lock object to guard all access to Rollback state.
+ */
+ private final Object mLock = new Object();
+
+ /**
+ * The user that performed the install with rollback enabled.
+ */
+ public final int mUserId;
+
+ /**
+ * The installer package name from the install session that enabled the rollback. May be null if
+ * that session did not set this value.
+ *
+ * If this is an empty string then the installer package name will be resolved by
+ * PackageManager.
+ */
+ @Nullable public final String mInstallerPackageName;
+
+ /**
* Constructs a new, empty Rollback instance.
*
* @param rollbackId the id of the rollback.
* @param backupDir the directory where the rollback data is stored.
* @param stagedSessionId the session id if this is a staged rollback, -1 otherwise.
+ * @param userId the user that performed the install with rollback enabled.
+ * @param installerPackageName the installer package name from the original install session.
*/
- Rollback(int rollbackId, File backupDir, int stagedSessionId) {
+ Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId,
+ String installerPackageName) {
this.info = new RollbackInfo(rollbackId,
/* packages */ new ArrayList<>(),
/* isStaged */ stagedSessionId != -1,
/* causePackages */ new ArrayList<>(),
/* committedSessionId */ -1);
+ mUserId = userId;
+ mInstallerPackageName = installerPackageName;
mBackupDir = backupDir;
mStagedSessionId = stagedSessionId;
mState = ROLLBACK_STATE_ENABLING;
@@ -124,8 +186,11 @@ class Rollback {
* Constructs a pre-populated Rollback instance.
*/
Rollback(RollbackInfo info, File backupDir, Instant timestamp, int stagedSessionId,
- @RollbackState int state, int apkSessionId, boolean restoreUserDataInProgress) {
+ @RollbackState int state, int apkSessionId, boolean restoreUserDataInProgress,
+ int userId, String installerPackageName) {
this.info = info;
+ mUserId = userId;
+ mInstallerPackageName = installerPackageName;
mBackupDir = backupDir;
mTimestamp = timestamp;
mStagedSessionId = stagedSessionId;
@@ -152,14 +217,19 @@ class Rollback {
* Returns the time when the upgrade occurred, for purposes of expiring rollback data.
*/
Instant getTimestamp() {
- return mTimestamp;
+ synchronized (mLock) {
+ return mTimestamp;
+ }
}
/**
* Sets the time at which upgrade occurred.
*/
void setTimestamp(Instant timestamp) {
- mTimestamp = timestamp;
+ synchronized (mLock) {
+ mTimestamp = timestamp;
+ RollbackStore.saveRollback(this);
+ }
}
/**
@@ -171,52 +241,374 @@ class Rollback {
}
/**
+ * Returns the ID of the user that performed the install with rollback enabled.
+ */
+ int getUserId() {
+ return mUserId;
+ }
+
+ /**
+ * Returns the installer package name from the install session that enabled the rollback. In the
+ * case that this is called on a rollback from an older version, returns the empty string.
+ */
+ @Nullable String getInstallerPackageName() {
+ return mInstallerPackageName;
+ }
+
+ /**
* Returns true if the rollback is in the ENABLING state.
*/
boolean isEnabling() {
- return mState == ROLLBACK_STATE_ENABLING;
+ synchronized (mLock) {
+ return mState == ROLLBACK_STATE_ENABLING;
+ }
}
/**
* Returns true if the rollback is in the AVAILABLE state.
*/
boolean isAvailable() {
- return mState == ROLLBACK_STATE_AVAILABLE;
+ synchronized (mLock) {
+ return mState == ROLLBACK_STATE_AVAILABLE;
+ }
}
/**
* Returns true if the rollback is in the COMMITTED state.
*/
boolean isCommitted() {
- return mState == ROLLBACK_STATE_COMMITTED;
+ synchronized (mLock) {
+ return mState == ROLLBACK_STATE_COMMITTED;
+ }
+ }
+
+ /**
+ * Returns true if the rollback is in the DELETED state.
+ */
+ boolean isDeleted() {
+ synchronized (mLock) {
+ return mState == ROLLBACK_STATE_DELETED;
+ }
+ }
+
+ /**
+ * Saves this rollback to persistent storage.
+ */
+ void saveRollback() {
+ synchronized (mLock) {
+ RollbackStore.saveRollback(this);
+ }
+ }
+
+ /**
+ * Enables this rollback for the provided package.
+ *
+ * @return boolean True if the rollback was enabled successfully for the specified package.
+ */
+ boolean enableForPackage(String packageName, long newVersion, long installedVersion,
+ boolean isApex, String sourceDir, String[] splitSourceDirs) {
+ try {
+ RollbackStore.backupPackageCodePath(this, packageName, sourceDir);
+ if (!ArrayUtils.isEmpty(splitSourceDirs)) {
+ for (String dir : splitSourceDirs) {
+ RollbackStore.backupPackageCodePath(this, packageName, dir);
+ }
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to copy package for rollback for " + packageName, e);
+ return false;
+ }
+
+ PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(
+ new VersionedPackage(packageName, newVersion),
+ new VersionedPackage(packageName, installedVersion),
+ new IntArray() /* pendingBackups */, new ArrayList<>() /* pendingRestores */,
+ isApex, new IntArray(), new SparseLongArray() /* ceSnapshotInodes */);
+
+ synchronized (mLock) {
+ info.getPackages().add(packageRollbackInfo);
+ }
+
+ return true;
+ }
+
+ /**
+ * Snapshots user data for the provided package and user ids. Does nothing if this rollback is
+ * not in the ENABLING state.
+ */
+ void snapshotUserData(String packageName, int[] userIds, AppDataRollbackHelper dataHelper) {
+ synchronized (mLock) {
+ if (!isEnabling()) {
+ return;
+ }
+
+ for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
+ if (pkgRollbackInfo.getPackageName().equals(packageName)) {
+ dataHelper.snapshotAppData(info.getRollbackId(), pkgRollbackInfo, userIds);
+
+ RollbackStore.saveRollback(this);
+ pkgRollbackInfo.getSnapshottedUsers().addAll(IntArray.wrap(userIds));
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Commits the pending backups and restores for a given {@code userId}. If this rollback has a
+ * pending backup, it is updated with a mapping from {@code userId} to inode of the CE user data
+ * snapshot.
+ */
+ void commitPendingBackupAndRestoreForUser(int userId, AppDataRollbackHelper dataHelper) {
+ synchronized (mLock) {
+ if (dataHelper.commitPendingBackupAndRestoreForUser(userId, this)) {
+ RollbackStore.saveRollback(this);
+ }
+ }
+ }
+
+ /**
+ * Changes the state of the rollback to AVAILABLE. This also changes the timestamp to the
+ * current time and saves the rollback. Does nothing if this rollback is already in the
+ * DELETED state.
+ */
+ void makeAvailable() {
+ synchronized (mLock) {
+ if (isDeleted()) {
+ Slog.w(TAG, "Cannot make deleted rollback available.");
+ return;
+ }
+ mState = ROLLBACK_STATE_AVAILABLE;
+ mTimestamp = Instant.now();
+ RollbackStore.saveRollback(this);
+ }
}
/**
- * Sets the state of the rollback to AVAILABLE.
+ * Commits the rollback.
*/
- void setAvailable() {
- mState = ROLLBACK_STATE_AVAILABLE;
+ void commit(final Context context, List<VersionedPackage> causePackages,
+ String callerPackageName, IntentSender statusReceiver) {
+ synchronized (mLock) {
+ if (!isAvailable()) {
+ sendFailure(context, statusReceiver,
+ RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
+ "Rollback unavailable");
+ return;
+ }
+
+ // Get a context to use to install the downgraded version of the package.
+ Context pkgContext;
+ try {
+ pkgContext = context.createPackageContextAsUser(callerPackageName, 0,
+ UserHandle.of(mUserId));
+ } catch (PackageManager.NameNotFoundException e) {
+ sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE,
+ "Invalid callerPackageName");
+ return;
+ }
+
+ PackageManager pm = pkgContext.getPackageManager();
+ try {
+ PackageInstaller packageInstaller = pm.getPackageInstaller();
+ PackageInstaller.SessionParams parentParams = new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ parentParams.setRequestDowngrade(true);
+ parentParams.setMultiPackage();
+ if (isStaged()) {
+ parentParams.setStaged();
+ }
+
+ int parentSessionId = packageInstaller.createSession(parentParams);
+ PackageInstaller.Session parentSession = packageInstaller.openSession(
+ parentSessionId);
+
+ for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
+ PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ String installerPackageName = mInstallerPackageName;
+ if (TextUtils.isEmpty(mInstallerPackageName)) {
+ installerPackageName = pm.getInstallerPackageName(
+ pkgRollbackInfo.getPackageName());
+ }
+ if (installerPackageName != null) {
+ params.setInstallerPackageName(installerPackageName);
+ }
+ params.setRequestDowngrade(true);
+ params.setRequiredInstalledVersionCode(
+ pkgRollbackInfo.getVersionRolledBackFrom().getLongVersionCode());
+ if (isStaged()) {
+ params.setStaged();
+ }
+ if (pkgRollbackInfo.isApex()) {
+ params.setInstallAsApex();
+ }
+ int sessionId = packageInstaller.createSession(params);
+ PackageInstaller.Session session = packageInstaller.openSession(sessionId);
+ File[] packageCodePaths = RollbackStore.getPackageCodePaths(
+ this, pkgRollbackInfo.getPackageName());
+ if (packageCodePaths == null) {
+ sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE,
+ "Backup copy of package inaccessible");
+ return;
+ }
+
+ for (File packageCodePath : packageCodePaths) {
+ try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCodePath,
+ ParcelFileDescriptor.MODE_READ_ONLY)) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ session.write(packageCodePath.getName(), 0,
+ packageCodePath.length(),
+ fd);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+ parentSession.addChildSessionId(sessionId);
+ }
+
+ final LocalIntentReceiver receiver = new LocalIntentReceiver(
+ (Intent result) -> {
+ int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status != PackageInstaller.STATUS_SUCCESS) {
+ // Committing the rollback failed, but we still have all the info we
+ // need to try rolling back again, so restore the rollback state to
+ // how it was before we tried committing.
+ // TODO: Should we just kill this rollback if commit failed?
+ // Why would we expect commit not to fail again?
+ // TODO: Could this cause a rollback to be resurrected
+ // if it should otherwise have expired by now?
+ synchronized (mLock) {
+ mState = ROLLBACK_STATE_AVAILABLE;
+ mRestoreUserDataInProgress = false;
+ }
+ sendFailure(context, statusReceiver,
+ RollbackManager.STATUS_FAILURE_INSTALL,
+ "Rollback downgrade install failed: "
+ + result.getStringExtra(
+ PackageInstaller.EXTRA_STATUS_MESSAGE));
+ return;
+ }
+
+ synchronized (mLock) {
+ if (!isStaged()) {
+ // All calls to restoreUserData should have
+ // completed by now for a non-staged install.
+ mRestoreUserDataInProgress = false;
+ }
+
+ info.setCommittedSessionId(parentSessionId);
+ info.getCausePackages().addAll(causePackages);
+ RollbackStore.deletePackageCodePaths(this);
+ RollbackStore.saveRollback(this);
+ }
+
+ // Send success.
+ try {
+ final Intent fillIn = new Intent();
+ fillIn.putExtra(
+ RollbackManager.EXTRA_STATUS,
+ RollbackManager.STATUS_SUCCESS);
+ statusReceiver.sendIntent(context, 0, fillIn, null, null);
+ } catch (IntentSender.SendIntentException e) {
+ // Nowhere to send the result back to, so don't bother.
+ }
+
+ Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED);
+
+ for (UserInfo userInfo : UserManager.get(context).getUsers(true)) {
+ context.sendBroadcastAsUser(broadcast,
+ userInfo.getUserHandle(),
+ Manifest.permission.MANAGE_ROLLBACKS);
+ }
+ }
+ );
+
+ mState = ROLLBACK_STATE_COMMITTED;
+ mRestoreUserDataInProgress = true;
+ parentSession.commit(receiver.getIntentSender());
+ } catch (IOException e) {
+ Slog.e(TAG, "Rollback failed", e);
+ sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE,
+ "IOException: " + e.toString());
+ }
+ }
}
/**
- * Sets the state of the rollback to COMMITTED.
+ * Restores user data for the specified package if this rollback is currently marked as
+ * having a restore in progress.
+ *
+ * @return boolean True if this rollback has a restore in progress and contains the specified
+ * package.
*/
- void setCommitted() {
- mState = ROLLBACK_STATE_COMMITTED;
+ boolean restoreUserDataForPackageIfInProgress(String packageName, int[] userIds, int appId,
+ String seInfo, AppDataRollbackHelper dataHelper) {
+ synchronized (mLock) {
+ if (!isRestoreUserDataInProgress()) {
+ return false;
+ }
+
+ boolean foundPackage = false;
+ for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
+ if (pkgRollbackInfo.getPackageName().equals(packageName)) {
+ foundPackage = true;
+ boolean changedRollback = false;
+ for (int userId : userIds) {
+ changedRollback |= dataHelper.restoreAppData(
+ info.getRollbackId(), pkgRollbackInfo, userId, appId, seInfo);
+ }
+ // We've updated metadata about this rollback, so save it to flash.
+ if (changedRollback) {
+ RollbackStore.saveRollback(this);
+ }
+ break;
+ }
+ }
+ return foundPackage;
+ }
+ }
+
+ /**
+ * Deletes app data snapshots associated with this rollback, and moves to the DELETED state.
+ */
+ void delete(AppDataRollbackHelper dataHelper) {
+ synchronized (mLock) {
+ for (PackageRollbackInfo pkgInfo : info.getPackages()) {
+ IntArray snapshottedUsers = pkgInfo.getSnapshottedUsers();
+ for (int i = 0; i < snapshottedUsers.size(); i++) {
+ // Destroy app data snapshot.
+ int userId = snapshottedUsers.get(i);
+
+ dataHelper.destroyAppDataSnapshot(info.getRollbackId(), pkgInfo, userId);
+ }
+ }
+
+ RollbackStore.deleteRollback(this);
+ mState = ROLLBACK_STATE_DELETED;
+ }
}
/**
* Returns the id of the post-reboot apk session for a staged install, if any.
*/
int getApkSessionId() {
- return mApkSessionId;
+ synchronized (mLock) {
+ return mApkSessionId;
+ }
}
/**
* Sets the id of the post-reboot apk session for a staged install.
*/
void setApkSessionId(int apkSessionId) {
- mApkSessionId = apkSessionId;
+ synchronized (mLock) {
+ mApkSessionId = apkSessionId;
+ RollbackStore.saveRollback(this);
+ }
}
/**
@@ -224,7 +616,9 @@ class Rollback {
* rollback because it has just been committed but the rollback has not yet been fully applied.
*/
boolean isRestoreUserDataInProgress() {
- return mRestoreUserDataInProgress;
+ synchronized (mLock) {
+ return mRestoreUserDataInProgress;
+ }
}
/**
@@ -232,7 +626,75 @@ class Rollback {
* rollback because it has just been committed but the rollback has not yet been fully applied.
*/
void setRestoreUserDataInProgress(boolean restoreUserDataInProgress) {
- mRestoreUserDataInProgress = restoreUserDataInProgress;
+ synchronized (mLock) {
+ mRestoreUserDataInProgress = restoreUserDataInProgress;
+ RollbackStore.saveRollback(this);
+ }
+ }
+
+ /**
+ * Returns true if this rollback includes the package with the provided {@code packageName}.
+ */
+ boolean includesPackage(String packageName) {
+ synchronized (mLock) {
+ for (PackageRollbackInfo packageRollbackInfo : info.getPackages()) {
+ if (packageRollbackInfo.getPackageName().equals(packageName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Returns true if this rollback includes the package with the provided {@code packageName}
+ * with a <i>version rolled back from</i> that is not {@code versionCode}.
+ */
+ boolean includesPackageWithDifferentVersion(String packageName, long versionCode) {
+ synchronized (mLock) {
+ for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
+ if (pkgRollbackInfo.getPackageName().equals(packageName)
+ && pkgRollbackInfo.getVersionRolledBackFrom().getLongVersionCode()
+ != versionCode) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Returns a list containing the names of all the packages included in this rollback.
+ */
+ List<String> getPackageNames() {
+ synchronized (mLock) {
+ List<String> result = new ArrayList<>();
+ for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
+ result.add(pkgRollbackInfo.getPackageName());
+ }
+ return result;
+ }
+ }
+
+ /**
+ * Returns a list containing the names of all the apex packages included in this rollback.
+ */
+ List<String> getApexPackageNames() {
+ synchronized (mLock) {
+ List<String> result = new ArrayList<>();
+ for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
+ if (pkgRollbackInfo.isApex()) {
+ result.add(pkgRollbackInfo.getPackageName());
+ }
+ }
+ return result;
+ }
+ }
+
+ int getPackageCount() {
+ synchronized (mLock) {
+ return info.getPackages().size();
+ }
}
static String rollbackStateToString(@RollbackState int state) {
@@ -255,6 +717,38 @@ class Rollback {
}
String getStateAsString() {
- return rollbackStateToString(mState);
+ synchronized (mLock) {
+ return rollbackStateToString(mState);
+ }
+ }
+
+ void dump(IndentingPrintWriter ipw) {
+ synchronized (mLock) {
+ ipw.println(info.getRollbackId() + ":");
+ ipw.increaseIndent();
+ ipw.println("-state: " + getStateAsString());
+ ipw.println("-timestamp: " + getTimestamp());
+ if (getStagedSessionId() != -1) {
+ ipw.println("-stagedSessionId: " + getStagedSessionId());
+ }
+ ipw.println("-packages:");
+ ipw.increaseIndent();
+ for (PackageRollbackInfo pkg : info.getPackages()) {
+ ipw.println(pkg.getPackageName()
+ + " " + pkg.getVersionRolledBackFrom().getLongVersionCode()
+ + " -> " + pkg.getVersionRolledBackTo().getLongVersionCode());
+ }
+ ipw.decreaseIndent();
+ if (isCommitted()) {
+ ipw.println("-causePackages:");
+ ipw.increaseIndent();
+ for (VersionedPackage cPkg : info.getCausePackages()) {
+ ipw.println(cPkg.getPackageName() + " " + cPkg.getLongVersionCode());
+ }
+ ipw.decreaseIndent();
+ ipw.println("-committedSessionId: " + info.getCommittedSessionId());
+ }
+ ipw.decreaseIndent();
+ }
}
}
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 96d284bb1c58..ef4c12e8145a 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -36,14 +36,12 @@ import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
import android.content.rollback.IRollbackManager;
-import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
-import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -53,10 +51,8 @@ import android.util.ArraySet;
import android.util.IntArray;
import android.util.Slog;
import android.util.SparseBooleanArray;
-import android.util.SparseLongArray;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.Watchdog;
@@ -64,7 +60,6 @@ import com.android.server.pm.Installer;
import java.io.File;
import java.io.FileDescriptor;
-import java.io.IOException;
import java.io.PrintWriter;
import java.security.SecureRandom;
import java.time.Instant;
@@ -219,7 +214,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
synchronized (mLock) {
for (NewRollback rollback : mNewRollbacks) {
if (rollback.hasToken(token)) {
- rollback.isCancelled = true;
+ rollback.setCancelled();
return;
}
}
@@ -332,8 +327,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
Iterator<Rollback> iter = mRollbacks.iterator();
while (iter.hasNext()) {
Rollback rollback = iter.next();
- rollback.setTimestamp(rollback.getTimestamp().plusMillis(timeDifference));
- saveRollback(rollback);
+ rollback.setTimestamp(
+ rollback.getTimestamp().plusMillis(timeDifference));
}
}
}
@@ -358,148 +353,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
Slog.i(TAG, "Initiating rollback");
Rollback rollback = getRollbackForId(rollbackId);
- if (rollback == null || !rollback.isAvailable()) {
- sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
+ if (rollback == null) {
+ sendFailure(
+ mContext, statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
"Rollback unavailable");
return;
}
-
- // Get a context for the caller to use to install the downgraded
- // version of the package.
- final Context context;
- try {
- context = mContext.createPackageContext(callerPackageName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
- "Invalid callerPackageName");
- return;
- }
-
- PackageManager pm = context.getPackageManager();
- try {
- PackageInstaller packageInstaller = pm.getPackageInstaller();
- PackageInstaller.SessionParams parentParams = new PackageInstaller.SessionParams(
- PackageInstaller.SessionParams.MODE_FULL_INSTALL);
- parentParams.setRequestDowngrade(true);
- parentParams.setMultiPackage();
- if (rollback.isStaged()) {
- parentParams.setStaged();
- }
-
- int parentSessionId = packageInstaller.createSession(parentParams);
- PackageInstaller.Session parentSession = packageInstaller.openSession(parentSessionId);
-
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
- PackageInstaller.SessionParams.MODE_FULL_INSTALL);
- // TODO: We can't get the installerPackageName for apex
- // (b/123920130). Is it okay to ignore the installer package
- // for apex?
- if (!info.isApex()) {
- String installerPackageName = pm.getInstallerPackageName(info.getPackageName());
- if (installerPackageName != null) {
- params.setInstallerPackageName(installerPackageName);
- }
- }
- params.setRequestDowngrade(true);
- params.setRequiredInstalledVersionCode(
- info.getVersionRolledBackFrom().getLongVersionCode());
- if (rollback.isStaged()) {
- params.setStaged();
- }
- if (info.isApex()) {
- params.setInstallAsApex();
- }
- int sessionId = packageInstaller.createSession(params);
- PackageInstaller.Session session = packageInstaller.openSession(sessionId);
- File[] packageCodePaths = RollbackStore.getPackageCodePaths(
- rollback, info.getPackageName());
- if (packageCodePaths == null) {
- sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
- "Backup copy of package inaccessible");
- return;
- }
-
- for (File packageCodePath : packageCodePaths) {
- try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCodePath,
- ParcelFileDescriptor.MODE_READ_ONLY)) {
- final long token = Binder.clearCallingIdentity();
- try {
- session.write(packageCodePath.getName(), 0, packageCodePath.length(),
- fd);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- }
- parentSession.addChildSessionId(sessionId);
- }
-
- final LocalIntentReceiver receiver = new LocalIntentReceiver(
- (Intent result) -> {
- getHandler().post(() -> {
-
- int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status != PackageInstaller.STATUS_SUCCESS) {
- // Committing the rollback failed, but we
- // still have all the info we need to try
- // rolling back again, so restore the rollback
- // state to how it was before we tried
- // committing.
- // TODO: Should we just kill this rollback if
- // commit failed? Why would we expect commit
- // not to fail again?
- synchronized (mLock) {
- // TODO: Could this cause a rollback to be
- // resurrected if it should otherwise have
- // expired by now?
- rollback.setAvailable();
- rollback.setRestoreUserDataInProgress(false);
- }
- sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_INSTALL,
- "Rollback downgrade install failed: "
- + result.getStringExtra(
- PackageInstaller.EXTRA_STATUS_MESSAGE));
- return;
- }
-
- synchronized (mLock) {
- if (!rollback.isStaged()) {
- // All calls to restoreUserData should have
- // completed by now for a non-staged install.
- rollback.setRestoreUserDataInProgress(false);
- }
-
- rollback.info.setCommittedSessionId(parentSessionId);
- rollback.info.getCausePackages().addAll(causePackages);
- }
- mRollbackStore.deletePackageCodePaths(rollback);
- saveRollback(rollback);
-
- sendSuccess(statusReceiver);
-
- Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED);
-
- for (UserInfo userInfo : UserManager.get(mContext).getUsers(true)) {
- mContext.sendBroadcastAsUser(broadcast, userInfo.getUserHandle(),
- Manifest.permission.MANAGE_ROLLBACKS);
- }
- });
- }
- );
-
- synchronized (mLock) {
- rollback.setCommitted();
- rollback.setRestoreUserDataInProgress(true);
- }
- parentSession.commit(receiver.getIntentSender());
- } catch (IOException e) {
- Slog.e(TAG, "Rollback failed", e);
- sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
- "IOException: " + e.toString());
- return;
- }
+ rollback.commit(mContext, causePackages, callerPackageName, statusReceiver);
}
@Override
@@ -534,20 +394,14 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
Iterator<Rollback> iter = mRollbacks.iterator();
while (iter.hasNext()) {
Rollback rollback = iter.next();
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- if (info.getPackageName().equals(packageName)) {
- iter.remove();
- deleteRollback(rollback);
- break;
- }
+ if (rollback.includesPackage(packageName)) {
+ iter.remove();
+ rollback.delete(mAppDataRollbackHelper);
}
}
for (NewRollback newRollback : mNewRollbacks) {
- for (PackageRollbackInfo info : newRollback.rollback.info.getPackages()) {
- if (info.getPackageName().equals(packageName)) {
- newRollback.isCancelled = true;
- break;
- }
+ if (newRollback.rollback.includesPackage(packageName)) {
+ newRollback.setCancelled();
}
}
}
@@ -578,12 +432,11 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
rollbacks = new ArrayList<>(mRollbacks);
}
- final Set<Rollback> changed =
- mAppDataRollbackHelper.commitPendingBackupAndRestoreForUser(userId, rollbacks);
-
- for (Rollback rollback : changed) {
- saveRollback(rollback);
+ for (int i = 0; i < rollbacks.size(); i++) {
+ Rollback rollback = rollbacks.get(i);
+ rollback.commitPendingBackupAndRestoreForUser(userId, mAppDataRollbackHelper);
}
+
latch.countDown();
});
@@ -624,11 +477,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
restoreInProgress.add(rollback);
}
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- if (info.isApex()) {
- apexPackageNames.add(info.getPackageName());
- }
- }
+ apexPackageNames.addAll(rollback.getApexPackageNames());
}
}
}
@@ -642,7 +491,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
// mRollbacks, or is it okay to leave as
// unavailable until the next reboot when it will go
// away on its own?
- deleteRollback(rollback);
+ rollback.delete(mAppDataRollbackHelper);
} else if (session.isStagedSessionApplied()) {
makeRollbackAvailable(rollback);
}
@@ -655,10 +504,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
// TODO: What if session is null?
if (session != null) {
if (session.isStagedSessionApplied() || session.isStagedSessionFailed()) {
- synchronized (mLock) {
- rollback.setRestoreUserDataInProgress(false);
- }
- saveRollback(rollback);
+ rollback.setRestoreUserDataInProgress(false);
}
}
}
@@ -687,24 +533,18 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
private void onPackageReplaced(String packageName) {
// TODO: Could this end up incorrectly deleting a rollback for a
// package that is about to be installed?
- VersionedPackage installedVersion = getInstalledPackageVersion(packageName);
+ long installedVersion = getInstalledPackageVersion(packageName);
synchronized (mLock) {
Iterator<Rollback> iter = mRollbacks.iterator();
while (iter.hasNext()) {
Rollback rollback = iter.next();
// TODO: Should we remove rollbacks in the ENABLING state here?
- if (rollback.isEnabling() || rollback.isAvailable()) {
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- if (info.getPackageName().equals(packageName)
- && !packageVersionsEqual(
- info.getVersionRolledBackFrom(),
- installedVersion)) {
- iter.remove();
- deleteRollback(rollback);
- break;
- }
- }
+ if ((rollback.isEnabling() || rollback.isAvailable())
+ && rollback.includesPackageWithDifferentVersion(packageName,
+ installedVersion)) {
+ iter.remove();
+ rollback.delete(mAppDataRollbackHelper);
}
}
}
@@ -725,27 +565,14 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
* @param status the RollbackManager.STATUS_* code with the failure.
* @param message the failure message.
*/
- private void sendFailure(IntentSender statusReceiver, @RollbackManager.Status int status,
- String message) {
+ static void sendFailure(Context context, IntentSender statusReceiver,
+ @RollbackManager.Status int status, String message) {
Slog.e(TAG, message);
try {
final Intent fillIn = new Intent();
fillIn.putExtra(RollbackManager.EXTRA_STATUS, status);
fillIn.putExtra(RollbackManager.EXTRA_STATUS_MESSAGE, message);
- statusReceiver.sendIntent(mContext, 0, fillIn, null, null);
- } catch (IntentSender.SendIntentException e) {
- // Nowhere to send the result back to, so don't bother.
- }
- }
-
- /**
- * Notifies an IntentSender of success.
- */
- private void sendSuccess(IntentSender statusReceiver) {
- try {
- final Intent fillIn = new Intent();
- fillIn.putExtra(RollbackManager.EXTRA_STATUS, RollbackManager.STATUS_SUCCESS);
- statusReceiver.sendIntent(mContext, 0, fillIn, null, null);
+ statusReceiver.sendIntent(context, 0, fillIn, null, null);
} catch (IntentSender.SendIntentException e) {
// Nowhere to send the result back to, so don't bother.
}
@@ -763,13 +590,14 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
if (!rollback.isAvailable()) {
continue;
}
+ Instant rollbackTimestamp = rollback.getTimestamp();
if (!now.isBefore(
- rollback.getTimestamp()
- .plusMillis(mRollbackLifetimeDurationInMillis))) {
+ rollbackTimestamp
+ .plusMillis(mRollbackLifetimeDurationInMillis))) {
iter.remove();
- deleteRollback(rollback);
- } else if (oldest == null || oldest.isAfter(rollback.getTimestamp())) {
- oldest = rollback.getTimestamp();
+ rollback.delete(mAppDataRollbackHelper);
+ } else if (oldest == null || oldest.isAfter(rollbackTimestamp)) {
+ oldest = rollbackTimestamp;
}
}
}
@@ -878,8 +706,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
for (int i = 0; i < mRollbacks.size(); ++i) {
Rollback rollback = mRollbacks.get(i);
if (rollback.getApkSessionId() == parentSession.getSessionId()) {
- // This is the apk session for a staged session with rollback enabled. We do not
- // need to create a new rollback for this session.
+ // This is the apk session for a staged session with rollback enabled. We do
+ // not need to create a new rollback for this session.
return true;
}
}
@@ -937,7 +765,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
}
// Get information about the package to be installed.
- PackageParser.PackageLite newPackage = null;
+ PackageParser.PackageLite newPackage;
try {
newPackage = PackageParser.parsePackageLite(new File(session.resolvedBaseCodePath), 0);
} catch (PackageParser.PackageParserException e) {
@@ -956,11 +784,9 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
return false;
}
- VersionedPackage newVersion = new VersionedPackage(packageName, newPackage.versionCode);
final boolean isApex = ((installFlags & PackageManager.INSTALL_APEX) != 0);
// Get information about the currently installed package.
- PackageManager pm = mContext.getPackageManager();
final PackageInfo pkgInfo;
try {
pkgInfo = getPackageInfo(packageName);
@@ -971,31 +797,10 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
return false;
}
- VersionedPackage installedVersion = new VersionedPackage(packageName,
- pkgInfo.getLongVersionCode());
-
- PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(
- newVersion, installedVersion,
- new IntArray() /* pendingBackups */, new ArrayList<>() /* pendingRestores */,
- isApex, new IntArray(), new SparseLongArray() /* ceSnapshotInodes */);
-
- try {
- ApplicationInfo appInfo = pkgInfo.applicationInfo;
- RollbackStore.backupPackageCodePath(rollback, packageName, appInfo.sourceDir);
- if (!ArrayUtils.isEmpty(appInfo.splitSourceDirs)) {
- for (String sourceDir : appInfo.splitSourceDirs) {
- RollbackStore.backupPackageCodePath(rollback, packageName, sourceDir);
- }
- }
- } catch (IOException e) {
- Slog.e(TAG, "Unable to copy package for rollback for " + packageName, e);
- return false;
- }
-
- synchronized (mLock) {
- rollback.info.getPackages().add(packageRollbackInfo);
- }
- return true;
+ ApplicationInfo appInfo = pkgInfo.applicationInfo;
+ return rollback.enableForPackage(packageName, newPackage.versionCode,
+ pkgInfo.getLongVersionCode(), isApex, appInfo.sourceDir,
+ appInfo.splitSourceDirs);
}
@Override
@@ -1008,7 +813,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
getHandler().post(() -> {
snapshotUserDataInternal(packageName, userIds);
- restoreUserDataInternal(packageName, userIds, appId, ceDataInode, seInfo, token);
+ restoreUserDataInternal(packageName, userIds, appId, seInfo);
final PackageManagerInternal pmi = LocalServices.getService(
PackageManagerInternal.class);
pmi.finishPackageInstall(token, false);
@@ -1020,62 +825,27 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
// staged installs
for (int i = 0; i < mRollbacks.size(); i++) {
Rollback rollback = mRollbacks.get(i);
- if (!rollback.isEnabling()) {
- continue;
- }
-
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- if (info.getPackageName().equals(packageName)) {
- mAppDataRollbackHelper.snapshotAppData(
- rollback.info.getRollbackId(), info, userIds);
- saveRollback(rollback);
- break;
- }
- }
+ rollback.snapshotUserData(packageName, userIds, mAppDataRollbackHelper);
}
// non-staged installs
- PackageRollbackInfo info;
for (NewRollback rollback : mNewRollbacks) {
- info = getPackageRollbackInfo(rollback.rollback, packageName);
- if (info != null) {
- mAppDataRollbackHelper.snapshotAppData(
- rollback.rollback.info.getRollbackId(), info, userIds);
- saveRollback(rollback.rollback);
- }
+ rollback.rollback.snapshotUserData(
+ packageName, userIds, mAppDataRollbackHelper);
}
}
}
- private void restoreUserDataInternal(String packageName, int[] userIds, int appId,
- long ceDataInode, String seInfo, int token) {
- PackageRollbackInfo info = null;
- Rollback rollback = null;
+ private void restoreUserDataInternal(
+ String packageName, int[] userIds, int appId, String seInfo) {
synchronized (mLock) {
for (int i = 0; i < mRollbacks.size(); ++i) {
- Rollback candidate = mRollbacks.get(i);
- if (candidate.isRestoreUserDataInProgress()) {
- info = getPackageRollbackInfo(candidate, packageName);
- if (info != null) {
- rollback = candidate;
- break;
- }
+ Rollback rollback = mRollbacks.get(i);
+ if (rollback.restoreUserDataForPackageIfInProgress(
+ packageName, userIds, appId, seInfo, mAppDataRollbackHelper)) {
+ return;
}
}
}
-
- if (rollback == null) {
- return;
- }
-
- for (int userId : userIds) {
- final boolean changedRollback = mAppDataRollbackHelper.restoreAppData(
- rollback.info.getRollbackId(), info, userId, appId, seInfo);
-
- // We've updated metadata about this rollback, so save it to flash.
- if (changedRollback) {
- saveRollback(rollback);
- }
- }
}
@Override
@@ -1147,7 +917,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
for (int i = 0; i < mRollbacks.size(); ++i) {
Rollback candidate = mRollbacks.get(i);
if (candidate.getStagedSessionId() == originalSessionId) {
- candidate.setApkSessionId(apkSessionId);
rollback = candidate;
break;
}
@@ -1162,7 +931,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
}
if (rollback != null) {
- saveRollback(rollback);
+ rollback.setApkSessionId(apkSessionId);
}
});
}
@@ -1207,24 +976,23 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
/**
* Gets the version of the package currently installed.
- * Returns null if the package is not currently installed.
+ * Returns -1 if the package is not currently installed.
*/
- private VersionedPackage getInstalledPackageVersion(String packageName) {
- PackageManager pm = mContext.getPackageManager();
- PackageInfo pkgInfo = null;
+ private long getInstalledPackageVersion(String packageName) {
+ PackageInfo pkgInfo;
try {
pkgInfo = getPackageInfo(packageName);
} catch (PackageManager.NameNotFoundException e) {
- return null;
+ return -1;
}
- return new VersionedPackage(packageName, pkgInfo.getLongVersionCode());
+ return pkgInfo.getLongVersionCode();
}
/**
- * Gets PackageInfo for the given package.
- * Matches any user and apex. Returns null if no such package is
- * installed.
+ * 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 {
@@ -1240,13 +1008,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
}
}
-
- private boolean packageVersionsEqual(VersionedPackage a, VersionedPackage b) {
- return a != null && b != null
- && a.getPackageName().equals(b.getPackageName())
- && a.getLongVersionCode() == b.getLongVersionCode();
- }
-
private class SessionCallback extends PackageInstaller.SessionCallback {
@Override
@@ -1281,9 +1042,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
}
/**
- * Add a rollback to the list of rollbacks.
- * This should be called after rollback has been enabled for all packages
- * in the rollback. It does not make the rollback available yet.
+ * Add a rollback to the list of rollbacks. This should be called after rollback has been
+ * enabled for all packages in the rollback. It does not make the rollback available yet.
*
* @return the Rollback instance for a successfully enable-completed rollback,
* or null on error.
@@ -1292,25 +1052,23 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
Rollback rollback = newRollback.rollback;
if (!success) {
// The install session was aborted, clean up the pending install.
- deleteRollback(rollback);
+ rollback.delete(mAppDataRollbackHelper);
return null;
}
- if (newRollback.isCancelled) {
+
+ if (newRollback.isCancelled()) {
Slog.e(TAG, "Rollback has been cancelled by PackageManager");
- deleteRollback(rollback);
+ rollback.delete(mAppDataRollbackHelper);
return null;
}
- // It's safe to access rollback.info outside a synchronized block because
- // this is running on the handler thread and all changes to the
- // rollback.info occur on the handler thread.
- if (rollback.info.getPackages().size() != newRollback.packageSessionIds.length) {
+ if (rollback.getPackageCount() != newRollback.getPackageSessionIdCount()) {
Slog.e(TAG, "Failed to enable rollback for all packages in session.");
- deleteRollback(rollback);
+ rollback.delete(mAppDataRollbackHelper);
return null;
}
- saveRollback(rollback);
+ rollback.saveRollback();
synchronized (mLock) {
// Note: There is a small window of time between when
// the session has been committed by the package
@@ -1328,26 +1086,16 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
return rollback;
}
+ @GuardedBy("rollback.getLock")
private void makeRollbackAvailable(Rollback rollback) {
- // TODO: What if the rollback has since been expired, for example due
- // to a new package being installed. Won't this revive an expired
- // rollback? Consider adding a ROLLBACK_STATE_EXPIRED to address this.
- synchronized (mLock) {
- rollback.setAvailable();
- rollback.setTimestamp(Instant.now());
- }
- saveRollback(rollback);
+ rollback.makeAvailable();
// TODO(zezeozue): Provide API to explicitly start observing instead
// of doing this for all rollbacks. If we do this for all rollbacks,
// should document in PackageInstaller.SessionParams#setEnableRollback
- // After enabling and commiting any rollback, observe packages and
+ // After enabling and committing any rollback, observe packages and
// prepare to rollback if packages crashes too frequently.
- List<String> packages = new ArrayList<>();
- for (int i = 0; i < rollback.info.getPackages().size(); i++) {
- packages.add(rollback.info.getPackages().get(i).getPackageName());
- }
- mPackageHealthObserver.startObservingHealth(packages,
+ mPackageHealthObserver.startObservingHealth(rollback.getPackageNames(),
mRollbackLifetimeDurationInMillis);
scheduleExpiration(mRollbackLifetimeDurationInMillis);
}
@@ -1368,21 +1116,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
return null;
}
- /**
- * Returns the {@code PackageRollbackInfo} associated with {@code packageName} from
- * a specified {@code Rollback}.
- */
- private static PackageRollbackInfo getPackageRollbackInfo(Rollback rollback,
- String packageName) {
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- if (info.getPackageName().equals(packageName)) {
- return info;
- }
- }
-
- return null;
- }
-
@GuardedBy("mLock")
private int allocateRollbackIdLocked() {
int n = 0;
@@ -1398,64 +1131,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
throw new IllegalStateException("Failed to allocate rollback ID");
}
- private void deleteRollback(Rollback rollback) {
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- IntArray snapshottedUsers = info.getSnapshottedUsers();
- for (int i = 0; i < snapshottedUsers.size(); i++) {
- int userId = snapshottedUsers.get(i);
- mAppDataRollbackHelper.destroyAppDataSnapshot(rollback.info.getRollbackId(),
- info, userId);
- }
- }
- mRollbackStore.deleteRollback(rollback);
- }
-
- /**
- * Saves a rollback, swallowing any IOExceptions.
- * For those times when it's not obvious what to do about the IOException.
- * TODO: Double check we can't do a better job handling the IOException in
- * a cases where this method is called.
- */
- private void saveRollback(Rollback rollback) {
- try {
- mRollbackStore.saveRollback(rollback);
- } catch (IOException ioe) {
- Slog.e(TAG, "Unable to save rollback for: "
- + rollback.info.getRollbackId(), ioe);
- }
- }
-
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
synchronized (mLock) {
for (Rollback rollback : mRollbacks) {
- RollbackInfo info = rollback.info;
- ipw.println(info.getRollbackId() + ":");
- ipw.increaseIndent();
- ipw.println("-state: " + rollback.getStateAsString());
- ipw.println("-timestamp: " + rollback.getTimestamp());
- if (rollback.getStagedSessionId() != -1) {
- ipw.println("-stagedSessionId: " + rollback.getStagedSessionId());
- }
- ipw.println("-packages:");
- ipw.increaseIndent();
- for (PackageRollbackInfo pkg : info.getPackages()) {
- ipw.println(pkg.getPackageName()
- + " " + pkg.getVersionRolledBackFrom().getLongVersionCode()
- + " -> " + pkg.getVersionRolledBackTo().getLongVersionCode());
- }
- ipw.decreaseIndent();
- if (rollback.isCommitted()) {
- ipw.println("-causePackages:");
- ipw.increaseIndent();
- for (VersionedPackage cPkg : info.getCausePackages()) {
- ipw.println(cPkg.getPackageName() + " " + cPkg.getLongVersionCode());
- }
- ipw.decreaseIndent();
- ipw.println("-committedSessionId: " + info.getCommittedSessionId());
- }
- ipw.decreaseIndent();
+ rollback.dump(ipw);
}
}
}
@@ -1475,22 +1156,51 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
public final Rollback rollback;
/**
- * This array holds all of the rollback tokens associated with package sessions included
- * in this rollback. This is used to identify which rollback should be cancelled in case
- * {@link PackageManager} sends an {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} intent.
+ * This array holds all of the rollback tokens associated with package sessions included in
+ * this rollback.
*/
+ @GuardedBy("mNewRollbackLock")
private final IntArray mTokens = new IntArray();
/**
- * Session ids for all packages in the install.
- * For multi-package sessions, this is the list of child session ids.
- * For normal sessions, this list is a single element with the normal
+ * Session ids for all packages in the install. For multi-package sessions, this is the list
+ * of child session ids. For normal sessions, this list is a single element with the normal
* session id.
*/
- public final int[] packageSessionIds;
+ private final int[] mPackageSessionIds;
+
+ @GuardedBy("mNewRollbackLock")
+ private boolean mIsCancelled = false;
+
+ private final Object mNewRollbackLock = new Object();
+
+ NewRollback(Rollback rollback, int[] packageSessionIds) {
+ this.rollback = rollback;
+ this.mPackageSessionIds = packageSessionIds;
+ }
+
+ /**
+ * Adds a rollback token to be associated with this NewRollback. This may be used to
+ * identify which rollback should be cancelled in case {@link PackageManager} sends an
+ * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} intent.
+ */
+ void addToken(int token) {
+ synchronized (mNewRollbackLock) {
+ mTokens.add(token);
+ }
+ }
+
+ /**
+ * Returns true if this NewRollback is associated with the provided {@code token}.
+ */
+ boolean hasToken(int token) {
+ synchronized (mNewRollbackLock) {
+ return mTokens.indexOf(token) != -1;
+ }
+ }
/**
- * Flag to determine whether the rollback has been cancelled.
+ * Returns true if this NewRollback has been cancelled.
*
* <p>Rollback could be invalidated and cancelled if RollbackManager receives
* {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} from {@link PackageManager}.
@@ -1500,31 +1210,60 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
* {@link PackageInstaller.SessionCallback#onFinished(int, boolean)} before it broadcasts
* {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK}.
*/
- public boolean isCancelled = false;
+ boolean isCancelled() {
+ synchronized (mNewRollbackLock) {
+ return mIsCancelled;
+ }
+ }
- NewRollback(Rollback rollback, int[] packageSessionIds) {
- this.rollback = rollback;
- this.packageSessionIds = packageSessionIds;
+ /**
+ * Sets this NewRollback to be marked as cancelled.
+ */
+ void setCancelled() {
+ synchronized (mNewRollbackLock) {
+ mIsCancelled = true;
+ }
}
- public void addToken(int token) {
- mTokens.add(token);
+ /**
+ * Returns true if this NewRollback contains the provided {@code packageSessionId}.
+ */
+ boolean containsSessionId(int packageSessionId) {
+ for (int id : mPackageSessionIds) {
+ if (id == packageSessionId) {
+ return true;
+ }
+ }
+ return false;
}
- public boolean hasToken(int token) {
- return mTokens.indexOf(token) != -1;
+ /**
+ * Returns the number of package session ids in this NewRollback.
+ */
+ int getPackageSessionIdCount() {
+ return mPackageSessionIds.length;
}
}
- NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
+ @GuardedBy("mLock")
+ private NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
int rollbackId = allocateRollbackIdLocked();
+ final int userId;
+ if (parentSession.getUser() == UserHandle.ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ } else {
+ userId = parentSession.getUser().getIdentifier();
+ }
+ String installerPackageName = parentSession.getInstallerPackageName();
final Rollback rollback;
int parentSessionId = parentSession.getSessionId();
if (parentSession.isStaged()) {
- rollback = mRollbackStore.createStagedRollback(rollbackId, parentSessionId);
+ rollback = mRollbackStore.createStagedRollback(rollbackId, parentSessionId, userId,
+ installerPackageName);
} else {
- rollback = mRollbackStore.createNonStagedRollback(rollbackId);
+ rollback = mRollbackStore.createNonStagedRollback(rollbackId, userId,
+ installerPackageName);
}
int[] packageSessionIds;
@@ -1542,14 +1281,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
* Returns null if no NewRollback is found for the given package
* session.
*/
+ @GuardedBy("mLock")
NewRollback getNewRollbackForPackageSessionLocked(int packageSessionId) {
// We expect mNewRollbacks to be a very small list; linear search
// should be plenty fast.
for (NewRollback newRollback: mNewRollbacks) {
- for (int id : newRollback.packageSessionIds) {
- if (id == packageSessionId) {
- return newRollback;
- }
+ if (newRollback.containsSessionId(packageSessionId)) {
+ return newRollback;
}
}
return null;
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 9c19aeccd59a..83891f60d4f7 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -370,7 +370,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
private void checkAndMitigateNativeCrashes() {
mNumberOfNativeCrashPollsRemaining--;
// Check if native watchdog reported a crash
- if ("1".equals(SystemProperties.get("ro.init.updatable_crashing"))) {
+ if ("1".equals(SystemProperties.get("sys.init.updatable_crashing"))) {
execute(getModuleMetadataPackage());
// we stop polling after an attempt to execute rollback, regardless of whether the
// attempt succeeds or not
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index 772c53fec4ce..550c75442892 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -16,6 +16,8 @@
package com.android.server.rollback;
+import static android.os.UserHandle.USER_SYSTEM;
+
import static com.android.server.rollback.Rollback.rollbackStateFromString;
import android.annotation.NonNull;
@@ -27,6 +29,9 @@ import android.util.IntArray;
import android.util.Slog;
import android.util.SparseLongArray;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
import libcore.io.IoUtils;
import org.json.JSONArray;
@@ -193,18 +198,19 @@ class RollbackStore {
* Creates a new Rollback instance for a non-staged rollback with
* backupDir assigned.
*/
- Rollback createNonStagedRollback(int rollbackId) {
+ Rollback createNonStagedRollback(int rollbackId, int userId, String installerPackageName) {
File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
- return new Rollback(rollbackId, backupDir, -1);
+ return new Rollback(rollbackId, backupDir, -1, userId, installerPackageName);
}
/**
* Creates a new Rollback instance for a staged rollback with
* backupDir assigned.
*/
- Rollback createStagedRollback(int rollbackId, int stagedSessionId) {
+ Rollback createStagedRollback(int rollbackId, int stagedSessionId, int userId,
+ String installerPackageName) {
File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
- return new Rollback(rollbackId, backupDir, stagedSessionId);
+ return new Rollback(rollbackId, backupDir, stagedSessionId, userId, installerPackageName);
}
/**
@@ -250,7 +256,8 @@ class RollbackStore {
/**
* Saves the given rollback to persistent storage.
*/
- void saveRollback(Rollback rollback) throws IOException {
+ @GuardedBy("rollback.mLock")
+ static void saveRollback(Rollback rollback) {
try {
JSONObject dataJson = new JSONObject();
dataJson.put("info", rollbackInfoToJson(rollback.info));
@@ -259,19 +266,21 @@ class RollbackStore {
dataJson.put("state", rollback.getStateAsString());
dataJson.put("apkSessionId", rollback.getApkSessionId());
dataJson.put("restoreUserDataInProgress", rollback.isRestoreUserDataInProgress());
+ dataJson.put("userId", rollback.getUserId());
+ dataJson.putOpt("installerPackageName", rollback.getInstallerPackageName());
PrintWriter pw = new PrintWriter(new File(rollback.getBackupDir(), "rollback.json"));
pw.println(dataJson.toString());
pw.close();
- } catch (JSONException e) {
- throw new IOException(e);
+ } catch (JSONException | IOException e) {
+ Slog.e(TAG, "Unable to save rollback for: " + rollback.info.getRollbackId(), e);
}
}
/**
* Removes all persistent storage associated with the given rollback.
*/
- void deleteRollback(Rollback rollback) {
+ static void deleteRollback(Rollback rollback) {
removeFile(rollback.getBackupDir());
}
@@ -285,19 +294,27 @@ class RollbackStore {
JSONObject dataJson = new JSONObject(
IoUtils.readFileAsString(rollbackJsonFile.getAbsolutePath()));
- return new Rollback(
- rollbackInfoFromJson(dataJson.getJSONObject("info")),
- backupDir,
- Instant.parse(dataJson.getString("timestamp")),
- dataJson.getInt("stagedSessionId"),
- rollbackStateFromString(dataJson.getString("state")),
- dataJson.getInt("apkSessionId"),
- dataJson.getBoolean("restoreUserDataInProgress"));
+ return rollbackFromJson(dataJson, backupDir);
} catch (JSONException | DateTimeParseException | ParseException e) {
throw new IOException(e);
}
}
+ @VisibleForTesting
+ static Rollback rollbackFromJson(JSONObject dataJson, File backupDir)
+ throws JSONException, ParseException {
+ return new Rollback(
+ rollbackInfoFromJson(dataJson.getJSONObject("info")),
+ backupDir,
+ Instant.parse(dataJson.getString("timestamp")),
+ dataJson.getInt("stagedSessionId"),
+ rollbackStateFromString(dataJson.getString("state")),
+ dataJson.getInt("apkSessionId"),
+ dataJson.getBoolean("restoreUserDataInProgress"),
+ dataJson.optInt("userId", USER_SYSTEM),
+ dataJson.optString("installerPackageName", ""));
+ }
+
private static JSONObject toJson(VersionedPackage pkg) throws JSONException {
JSONObject json = new JSONObject();
json.put("packageName", pkg.getPackageName());
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 5fd2ab87f410..3b2f32479ed2 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -180,8 +180,13 @@ public class SliceManagerService extends ISliceManager.Stub {
verifyCaller(pkg);
enforceAccess(pkg, uri);
uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier());
- if (getPinnedSlice(uri).unpin(pkg, token)) {
- removePinnedSlice(uri);
+ try {
+ PinnedSliceState slice = getPinnedSlice(uri);
+ if (slice != null && slice.unpin(pkg, token)) {
+ removePinnedSlice(uri);
+ }
+ } catch (IllegalStateException exception) {
+ Slog.w(TAG, exception.getMessage());
}
}
diff --git a/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java b/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java
index 3076284a80ed..8431ae4aeb98 100644
--- a/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java
+++ b/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java
@@ -15,6 +15,7 @@
*/
package com.android.server.stats;
+import android.annotation.Nullable;
import android.os.FileUtils;
import android.util.Slog;
@@ -22,46 +23,77 @@ import com.android.internal.annotations.VisibleForTesting;
import java.io.File;
import java.io.IOException;
-import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
final class ProcfsMemoryUtil {
private static final String TAG = "ProcfsMemoryUtil";
- /** Path to procfs status file: /proc/pid/status. */
- private static final String STATUS_FILE_FMT = "/proc/%d/status";
-
- private static final Pattern RSS_HIGH_WATER_MARK_IN_KILOBYTES =
- Pattern.compile("VmHWM:\\s*(\\d+)\\s*kB");
+ private static final Pattern STATUS_MEMORY_STATS =
+ Pattern.compile(String.join(
+ ".*",
+ "Uid:\\s*(\\d+)\\s*",
+ "VmHWM:\\s*(\\d+)\\s*kB",
+ "VmRSS:\\s*(\\d+)\\s*kB",
+ "RssAnon:\\s*(\\d+)\\s*kB",
+ "VmSwap:\\s*(\\d+)\\s*kB"), Pattern.DOTALL);
private ProcfsMemoryUtil() {}
/**
- * Reads RSS high-water mark of a process from procfs. Returns value of the VmHWM field in
- * /proc/PID/status in kilobytes or 0 if not available.
+ * Reads memory stats of a process from procfs. Returns values of the VmHWM, VmRss, AnonRSS,
+ * VmSwap fields in /proc/pid/status in kilobytes or null if not available.
*/
- static int readRssHighWaterMarkFromProcfs(int pid) {
- final String statusPath = String.format(Locale.US, STATUS_FILE_FMT, pid);
- return parseVmHWMFromStatus(readFile(statusPath));
+ @Nullable
+ static MemorySnapshot readMemorySnapshotFromProcfs(int pid) {
+ return parseMemorySnapshotFromStatus(readFile("/proc/" + pid + "/status"));
}
- /**
- * Parses RSS high-water mark out from the contents of the /proc/pid/status file in procfs. The
- * returned value is in kilobytes.
- */
@VisibleForTesting
- static int parseVmHWMFromStatus(String contents) {
+ @Nullable
+ static MemorySnapshot parseMemorySnapshotFromStatus(String contents) {
if (contents.isEmpty()) {
- return 0;
+ return null;
}
- final Matcher matcher = RSS_HIGH_WATER_MARK_IN_KILOBYTES.matcher(contents);
try {
- return matcher.find() ? Integer.parseInt(matcher.group(1)) : 0;
+ final Matcher matcher = STATUS_MEMORY_STATS.matcher(contents);
+ if (matcher.find()) {
+ final MemorySnapshot snapshot = new MemorySnapshot();
+ snapshot.uid = Integer.parseInt(matcher.group(1));
+ snapshot.rssHighWaterMarkInKilobytes = Integer.parseInt(matcher.group(2));
+ snapshot.rssInKilobytes = Integer.parseInt(matcher.group(3));
+ snapshot.anonRssInKilobytes = Integer.parseInt(matcher.group(4));
+ snapshot.swapInKilobytes = Integer.parseInt(matcher.group(5));
+ return snapshot;
+ }
} catch (NumberFormatException e) {
Slog.e(TAG, "Failed to parse value", e);
- return 0;
}
+ return null;
+ }
+
+ /**
+ * Reads cmdline of a process from procfs.
+ *
+ * Returns content of /proc/pid/cmdline (e.g. /system/bin/statsd) or an empty string
+ * if the file is not available.
+ */
+ public static String readCmdlineFromProcfs(int pid) {
+ return parseCmdline(readFile("/proc/" + pid + "/cmdline"));
+ }
+
+ /**
+ * Parses cmdline out of the contents of the /proc/pid/cmdline file in procfs.
+ *
+ * Parsing is required to strip anything after the first null byte.
+ */
+ @VisibleForTesting
+ static String parseCmdline(String contents) {
+ int firstNullByte = contents.indexOf("\0");
+ if (firstNullByte == -1) {
+ return contents;
+ }
+ return contents.substring(0, firstNullByte);
}
private static String readFile(String path) {
@@ -72,4 +104,12 @@ final class ProcfsMemoryUtil {
return "";
}
}
+
+ static final class MemorySnapshot {
+ public int uid;
+ public int rssHighWaterMarkInKilobytes;
+ public int rssInKilobytes;
+ public int anonRssInKilobytes;
+ public int swapInKilobytes;
+ }
}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
deleted file mode 100644
index 19b80556a779..000000000000
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ /dev/null
@@ -1,2737 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.stats;
-
-import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED;
-import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
-import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
-import static android.os.Process.getPidsForCommands;
-import static android.os.Process.getUidForPid;
-import static android.os.storage.VolumeInfo.TYPE_PRIVATE;
-import static android.os.storage.VolumeInfo.TYPE_PUBLIC;
-
-import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.server.am.MemoryStatUtil.readCmdlineFromProcfs;
-import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
-import static com.android.server.am.MemoryStatUtil.readMemoryStatFromProcfs;
-import static com.android.server.stats.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
-import static com.android.server.stats.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
-import static com.android.server.stats.ProcfsMemoryUtil.readRssHighWaterMarkFromProcfs;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityManagerInternal;
-import android.app.AlarmManager;
-import android.app.AlarmManager.OnAlarmListener;
-import android.app.AppOpsManager;
-import android.app.AppOpsManager.HistoricalOps;
-import android.app.AppOpsManager.HistoricalOpsRequest;
-import android.app.AppOpsManager.HistoricalPackageOps;
-import android.app.AppOpsManager.HistoricalUidOps;
-import android.app.ProcessMemoryState;
-import android.app.StatsManager;
-import android.bluetooth.BluetoothActivityEnergyInfo;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.UidTraffic;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.IntentSender;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PermissionInfo;
-import android.content.pm.UserInfo;
-import android.hardware.biometrics.BiometricsProtoEnums;
-import android.hardware.face.FaceManager;
-import android.hardware.fingerprint.FingerprintManager;
-import android.net.ConnectivityManager;
-import android.net.INetworkStatsService;
-import android.net.Network;
-import android.net.NetworkRequest;
-import android.net.NetworkStats;
-import android.net.wifi.IWifiManager;
-import android.net.wifi.WifiActivityEnergyInfo;
-import android.os.BatteryStats;
-import android.os.BatteryStatsInternal;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.CoolingDevice;
-import android.os.Environment;
-import android.os.FileUtils;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.IStatsCompanionService;
-import android.os.IStatsManager;
-import android.os.IStoraged;
-import android.os.IThermalEventListener;
-import android.os.IThermalService;
-import android.os.Looper;
-import android.os.ParcelFileDescriptor;
-import android.os.Parcelable;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.StatFs;
-import android.os.StatsDimensionsValue;
-import android.os.StatsLogEventWrapper;
-import android.os.SynchronousResultReceiver;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.Temperature;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.os.storage.DiskInfo;
-import android.os.storage.StorageManager;
-import android.os.storage.VolumeInfo;
-import android.provider.Settings;
-import android.stats.storage.StorageEnums;
-import android.telephony.ModemActivityInfo;
-import android.telephony.TelephonyManager;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.Slog;
-import android.util.StatsLog;
-import android.util.proto.ProtoOutputStream;
-import android.util.proto.ProtoStream;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.app.procstats.IProcessStats;
-import com.android.internal.app.procstats.ProcessStats;
-import com.android.internal.os.BackgroundThread;
-import com.android.internal.os.BatterySipper;
-import com.android.internal.os.BatteryStatsHelper;
-import com.android.internal.os.BinderCallsStats.ExportedCallStat;
-import com.android.internal.os.KernelCpuSpeedReader;
-import com.android.internal.os.KernelCpuThreadReader;
-import com.android.internal.os.KernelCpuThreadReaderDiff;
-import com.android.internal.os.KernelCpuThreadReaderSettingsObserver;
-import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
-import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
-import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
-import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
-import com.android.internal.os.KernelWakelockReader;
-import com.android.internal.os.KernelWakelockStats;
-import com.android.internal.os.LooperStats;
-import com.android.internal.os.PowerProfile;
-import com.android.internal.os.ProcessCpuTracker;
-import com.android.internal.os.StoragedUidIoStatsReader;
-import com.android.internal.util.DumpUtils;
-import com.android.server.BinderCallsStatsService;
-import com.android.server.LocalServices;
-import com.android.server.SystemService;
-import com.android.server.SystemServiceManager;
-import com.android.server.am.MemoryStatUtil.MemoryStat;
-import com.android.server.role.RoleManagerInternal;
-import com.android.server.stats.IonMemoryUtil.IonAllocations;
-import com.android.server.storage.DiskStatsFileLogger;
-import com.android.server.storage.DiskStatsLoggingService;
-
-import libcore.io.IoUtils;
-
-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.InputStream;
-import java.io.PrintWriter;
-import java.time.Instant;
-import java.time.temporal.ChronoUnit;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.UUID;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-/**
- * Helper service for statsd (the native stats management service in cmds/statsd/).
- * Used for registering and receiving alarms on behalf of statsd.
- *
- * @hide
- */
-public class StatsCompanionService extends IStatsCompanionService.Stub {
- /**
- * How long to wait on an individual subsystem to return its stats.
- */
- private static final long EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS = 2000;
- private static final long MILLIS_IN_A_DAY = TimeUnit.DAYS.toMillis(1);
-
- public static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity";
- public static final String CONFIG_DIR = "/data/misc/stats-service";
-
- static final String TAG = "StatsCompanionService";
- static final boolean DEBUG = false;
- /**
- * Hard coded field ids of frameworks/base/cmds/statsd/src/uid_data.proto
- * to be used in ProtoOutputStream.
- */
- private static final int APPLICATION_INFO_FIELD_ID = 1;
- private static final int UID_FIELD_ID = 1;
- private static final int VERSION_FIELD_ID = 2;
- private static final int VERSION_STRING_FIELD_ID = 3;
- private static final int PACKAGE_NAME_FIELD_ID = 4;
- private static final int INSTALLER_FIELD_ID = 5;
-
- public static final int CODE_DATA_BROADCAST = 1;
- public static final int CODE_SUBSCRIBER_BROADCAST = 1;
- public static final int CODE_ACTIVE_CONFIGS_BROADCAST = 1;
- /**
- * The last report time is provided with each intent registered to
- * StatsManager#setFetchReportsOperation. This allows easy de-duping in the receiver if
- * statsd is requesting the client to retrieve the same statsd data. The last report time
- * corresponds to the last_report_elapsed_nanos that will provided in the current
- * ConfigMetricsReport, and this timestamp also corresponds to the
- * current_report_elapsed_nanos of the most recently obtained ConfigMetricsReport.
- */
- public static final String EXTRA_LAST_REPORT_TIME = "android.app.extra.LAST_REPORT_TIME";
- public static final int DEATH_THRESHOLD = 10;
- /**
- * Which native processes to snapshot memory for.
- *
- * <p>Processes are matched by their cmdline in procfs. Example: cat /proc/pid/cmdline returns
- * /system/bin/statsd for the stats daemon.
- */
- private static final String[] MEMORY_INTERESTING_NATIVE_PROCESSES = new String[]{
- "/system/bin/statsd", // Stats daemon.
- "/system/bin/surfaceflinger",
- "/system/bin/apexd", // APEX daemon.
- "/system/bin/audioserver",
- "/system/bin/cameraserver",
- "/system/bin/drmserver",
- "/system/bin/healthd",
- "/system/bin/incidentd",
- "/system/bin/installd",
- "/system/bin/lmkd", // Low memory killer daemon.
- "/system/bin/logd",
- "media.codec",
- "media.extractor",
- "media.metrics",
- "/system/bin/mediadrmserver",
- "/system/bin/mediaserver",
- "/system/bin/performanced",
- "/system/bin/tombstoned",
- "/system/bin/traced", // Perfetto.
- "/system/bin/traced_probes", // Perfetto.
- "webview_zygote",
- "zygote",
- "zygote64",
- };
-
- private static final int CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES = 8;
-
- static final class CompanionHandler extends Handler {
- CompanionHandler(Looper looper) {
- super(looper);
- }
- }
-
- private final Context mContext;
- private final AlarmManager mAlarmManager;
- private final INetworkStatsService mNetworkStatsService;
- @GuardedBy("sStatsdLock")
- private static IStatsManager sStatsd;
- private static final Object sStatsdLock = new Object();
-
- private final OnAlarmListener mAnomalyAlarmListener = new AnomalyAlarmListener();
- private final OnAlarmListener mPullingAlarmListener = new PullingAlarmListener();
- private final OnAlarmListener mPeriodicAlarmListener = new PeriodicAlarmListener();
- private final BroadcastReceiver mAppUpdateReceiver;
- private final BroadcastReceiver mUserUpdateReceiver;
- private final ShutdownEventReceiver mShutdownEventReceiver;
- private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
- private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
- private IWifiManager mWifiManager = null;
- private TelephonyManager mTelephony = null;
- @GuardedBy("sStatsdLock")
- private final HashSet<Long> mDeathTimeMillis = new HashSet<>();
- @GuardedBy("sStatsdLock")
- private final HashMap<Long, String> mDeletedFiles = new HashMap<>();
- private final CompanionHandler mHandler;
-
- // Disables throttler on CPU time readers.
- private KernelCpuUidUserSysTimeReader mCpuUidUserSysTimeReader =
- new KernelCpuUidUserSysTimeReader(false);
- private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
- private KernelCpuUidFreqTimeReader mCpuUidFreqTimeReader =
- new KernelCpuUidFreqTimeReader(false);
- private KernelCpuUidActiveTimeReader mCpuUidActiveTimeReader =
- new KernelCpuUidActiveTimeReader(false);
- private KernelCpuUidClusterTimeReader mCpuUidClusterTimeReader =
- new KernelCpuUidClusterTimeReader(false);
- private StoragedUidIoStatsReader mStoragedUidIoStatsReader =
- new StoragedUidIoStatsReader();
- @Nullable
- private final KernelCpuThreadReaderDiff mKernelCpuThreadReader;
-
- private long mDebugElapsedClockPreviousValue = 0;
- private long mDebugElapsedClockPullCount = 0;
- private long mDebugFailingElapsedClockPreviousValue = 0;
- private long mDebugFailingElapsedClockPullCount = 0;
- private BatteryStatsHelper mBatteryStatsHelper = null;
- private static final int MAX_BATTERY_STATS_HELPER_FREQUENCY_MS = 1000;
- private long mBatteryStatsHelperTimestampMs = -MAX_BATTERY_STATS_HELPER_FREQUENCY_MS;
-
- private static IThermalService sThermalService;
- private File mBaseDir =
- new File(SystemServiceManager.ensureSystemDir(), "stats_companion");
- @GuardedBy("this")
- ProcessCpuTracker mProcessCpuTracker = null;
-
- public StatsCompanionService(Context context) {
- super();
- mContext = context;
- mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
- mNetworkStatsService = INetworkStatsService.Stub.asInterface(
- ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
- mBaseDir.mkdirs();
- mAppUpdateReceiver = new AppUpdateReceiver();
- mUserUpdateReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- synchronized (sStatsdLock) {
- sStatsd = fetchStatsdService();
- if (sStatsd == null) {
- Slog.w(TAG, "Could not access statsd for UserUpdateReceiver");
- return;
- }
- try {
- // Pull the latest state of UID->app name, version mapping.
- // Needed since the new user basically has a version of every app.
- informAllUidsLocked(context);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to inform statsd latest update of all apps", e);
- forgetEverythingLocked();
- }
- }
- }
- };
- mShutdownEventReceiver = new ShutdownEventReceiver();
- if (DEBUG) Slog.d(TAG, "Registered receiver for ACTION_PACKAGE_REPLACED and ADDED.");
- PowerProfile powerProfile = new PowerProfile(context);
- final int numClusters = powerProfile.getNumCpuClusters();
- mKernelCpuSpeedReaders = new KernelCpuSpeedReader[numClusters];
- int firstCpuOfCluster = 0;
- for (int i = 0; i < numClusters; i++) {
- final int numSpeedSteps = powerProfile.getNumSpeedStepsInCpuCluster(i);
- mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster,
- numSpeedSteps);
- firstCpuOfCluster += powerProfile.getNumCoresInCpuCluster(i);
- }
-
- // Enable push notifications of throttling from vendor thermal
- // management subsystem via thermalservice.
- IBinder b = ServiceManager.getService("thermalservice");
-
- if (b != null) {
- sThermalService = IThermalService.Stub.asInterface(b);
- try {
- sThermalService.registerThermalEventListener(
- new ThermalEventListener());
- Slog.i(TAG, "register thermal listener successfully");
- } catch (RemoteException e) {
- // Should never happen.
- Slog.e(TAG, "register thermal listener error");
- }
- } else {
- Slog.e(TAG, "cannot find thermalservice, no throttling push notifications");
- }
-
- // Default NetworkRequest should cover all transport types.
- final NetworkRequest request = new NetworkRequest.Builder().build();
- final ConnectivityManager connectivityManager =
- (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- connectivityManager.registerNetworkCallback(request, new ConnectivityStatsCallback());
-
- HandlerThread handlerThread = new HandlerThread(TAG);
- handlerThread.start();
- mHandler = new CompanionHandler(handlerThread.getLooper());
-
- mKernelCpuThreadReader =
- KernelCpuThreadReaderSettingsObserver.getSettingsModifiedReader(mContext);
- }
-
- @Override
- public void sendDataBroadcast(IBinder intentSenderBinder, long lastReportTimeNs) {
- enforceCallingPermission();
- IntentSender intentSender = new IntentSender(intentSenderBinder);
- Intent intent = new Intent();
- intent.putExtra(EXTRA_LAST_REPORT_TIME, lastReportTimeNs);
- try {
- intentSender.sendIntent(mContext, CODE_DATA_BROADCAST, intent, null, null);
- } catch (IntentSender.SendIntentException e) {
- Slog.w(TAG, "Unable to send using IntentSender");
- }
- }
-
- @Override
- public void sendActiveConfigsChangedBroadcast(IBinder intentSenderBinder, long[] configIds) {
- enforceCallingPermission();
- IntentSender intentSender = new IntentSender(intentSenderBinder);
- Intent intent = new Intent();
- intent.putExtra(StatsManager.EXTRA_STATS_ACTIVE_CONFIG_KEYS, configIds);
- try {
- intentSender.sendIntent(mContext, CODE_ACTIVE_CONFIGS_BROADCAST, intent, null, null);
- if (DEBUG) {
- Slog.d(TAG, "Sent broadcast with config ids " + Arrays.toString(configIds));
- }
- } catch (IntentSender.SendIntentException e) {
- Slog.w(TAG, "Unable to send active configs changed broadcast using IntentSender");
- }
- }
-
- @Override
- public void sendSubscriberBroadcast(IBinder intentSenderBinder, long configUid, long configKey,
- long subscriptionId, long subscriptionRuleId, String[] cookies,
- StatsDimensionsValue dimensionsValue) {
- enforceCallingPermission();
- IntentSender intentSender = new IntentSender(intentSenderBinder);
- Intent intent =
- new Intent()
- .putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid)
- .putExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, configKey)
- .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId)
- .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID, subscriptionRuleId)
- .putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue);
-
- ArrayList<String> cookieList = new ArrayList<>(cookies.length);
- for (String cookie : cookies) {
- cookieList.add(cookie);
- }
- intent.putStringArrayListExtra(
- StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES, cookieList);
-
- if (DEBUG) {
- Slog.d(TAG,
- String.format("Statsd sendSubscriberBroadcast with params {%d %d %d %d %s %s}",
- configUid, configKey, subscriptionId, subscriptionRuleId,
- Arrays.toString(cookies),
- dimensionsValue));
- }
- try {
- intentSender.sendIntent(mContext, CODE_SUBSCRIBER_BROADCAST, intent, null, null);
- } catch (IntentSender.SendIntentException e) {
- Slog.w(TAG,
- "Unable to send using IntentSender from uid " + configUid
- + "; presumably it had been cancelled.");
- }
- }
-
- private final static int[] toIntArray(List<Integer> list) {
- int[] ret = new int[list.size()];
- for (int i = 0; i < ret.length; i++) {
- ret[i] = list.get(i);
- }
- return ret;
- }
-
- private final static long[] toLongArray(List<Long> list) {
- long[] ret = new long[list.size()];
- for (int i = 0; i < ret.length; i++) {
- ret[i] = list.get(i);
- }
- return ret;
- }
-
- // Assumes that sStatsdLock is held.
- @GuardedBy("sStatsdLock")
- private final void informAllUidsLocked(Context context) throws RemoteException {
- UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
- PackageManager pm = context.getPackageManager();
- final List<UserInfo> users = um.getUsers(true);
- if (DEBUG) {
- Slog.d(TAG, "Iterating over " + users.size() + " profiles.");
- }
-
- ParcelFileDescriptor[] fds;
- try {
- fds = ParcelFileDescriptor.createPipe();
- } catch (IOException e) {
- Slog.e(TAG, "Failed to create a pipe to send uid map data.", e);
- return;
- }
- sStatsd.informAllUidData(fds[0]);
- try {
- fds[0].close();
- } catch (IOException e) {
- Slog.e(TAG, "Failed to close the read side of the pipe.", e);
- }
- final ParcelFileDescriptor writeFd = fds[1];
- BackgroundThread.getHandler().post(() -> {
- FileOutputStream fout = new ParcelFileDescriptor.AutoCloseOutputStream(writeFd);
- try {
- ProtoOutputStream output = new ProtoOutputStream(fout);
- int numRecords = 0;
- // Add in all the apps for every user/profile.
- for (UserInfo profile : users) {
- List<PackageInfo> pi =
- pm.getInstalledPackagesAsUser(PackageManager.MATCH_KNOWN_PACKAGES,
- profile.id);
- for (int j = 0; j < pi.size(); j++) {
- if (pi.get(j).applicationInfo != null) {
- String installer;
- try {
- installer = pm.getInstallerPackageName(pi.get(j).packageName);
- } catch (IllegalArgumentException e) {
- installer = "";
- }
- long applicationInfoToken =
- output.start(ProtoStream.FIELD_TYPE_MESSAGE
- | ProtoStream.FIELD_COUNT_REPEATED
- | APPLICATION_INFO_FIELD_ID);
- output.write(ProtoStream.FIELD_TYPE_INT32
- | ProtoStream.FIELD_COUNT_SINGLE | UID_FIELD_ID,
- pi.get(j).applicationInfo.uid);
- output.write(ProtoStream.FIELD_TYPE_INT64
- | ProtoStream.FIELD_COUNT_SINGLE
- | VERSION_FIELD_ID, pi.get(j).getLongVersionCode());
- output.write(ProtoStream.FIELD_TYPE_STRING
- | ProtoStream.FIELD_COUNT_SINGLE | VERSION_STRING_FIELD_ID,
- pi.get(j).versionName);
- output.write(ProtoStream.FIELD_TYPE_STRING
- | ProtoStream.FIELD_COUNT_SINGLE
- | PACKAGE_NAME_FIELD_ID, pi.get(j).packageName);
- output.write(ProtoStream.FIELD_TYPE_STRING
- | ProtoStream.FIELD_COUNT_SINGLE
- | INSTALLER_FIELD_ID,
- installer == null ? "" : installer);
- numRecords++;
- output.end(applicationInfoToken);
- }
- }
- }
- output.flush();
- if (DEBUG) {
- Slog.d(TAG, "Sent data for " + numRecords + " apps");
- }
- } finally {
- IoUtils.closeQuietly(fout);
- }
- });
- }
-
- private final static class AppUpdateReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- /**
- * App updates actually consist of REMOVE, ADD, and then REPLACE broadcasts. To avoid
- * waste, we ignore the REMOVE and ADD broadcasts that contain the replacing flag.
- * If we can't find the value for EXTRA_REPLACING, we default to false.
- */
- if (!intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)
- && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
- return; // Keep only replacing or normal add and remove.
- }
- if (DEBUG) Slog.d(TAG, "StatsCompanionService noticed an app was updated.");
- synchronized (sStatsdLock) {
- if (sStatsd == null) {
- Slog.w(TAG, "Could not access statsd to inform it of an app update");
- return;
- }
- try {
- if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) {
- Bundle b = intent.getExtras();
- int uid = b.getInt(Intent.EXTRA_UID);
- boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
- if (!replacing) {
- // Don't bother sending an update if we're right about to get another
- // intent for the new version that's added.
- PackageManager pm = context.getPackageManager();
- String app = intent.getData().getSchemeSpecificPart();
- sStatsd.informOnePackageRemoved(app, uid);
- }
- } else {
- PackageManager pm = context.getPackageManager();
- Bundle b = intent.getExtras();
- int uid = b.getInt(Intent.EXTRA_UID);
- String app = intent.getData().getSchemeSpecificPart();
- PackageInfo pi = pm.getPackageInfo(app, PackageManager.MATCH_ANY_USER);
- String installer;
- try {
- installer = pm.getInstallerPackageName(app);
- } catch (IllegalArgumentException e) {
- installer = "";
- }
- sStatsd.informOnePackage(app, uid, pi.getLongVersionCode(), pi.versionName,
- installer == null ? "" : installer);
- }
- } catch (Exception e) {
- Slog.w(TAG, "Failed to inform statsd of an app update", e);
- }
- }
- }
- }
-
- public final static class AnomalyAlarmListener implements OnAlarmListener {
- @Override
- public void onAlarm() {
- Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred at time "
- + System.currentTimeMillis() + "ms.");
- synchronized (sStatsdLock) {
- if (sStatsd == null) {
- Slog.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
- return;
- }
- try {
- // Two-way call to statsd to retain AlarmManager wakelock
- sStatsd.informAnomalyAlarmFired();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to inform statsd of anomaly alarm firing", e);
- }
- }
- // AlarmManager releases its own wakelock here.
- }
- }
-
- public final static class PullingAlarmListener implements OnAlarmListener {
- @Override
- public void onAlarm() {
- if (DEBUG) {
- Slog.d(TAG, "Time to poll something.");
- }
- synchronized (sStatsdLock) {
- if (sStatsd == null) {
- Slog.w(TAG, "Could not access statsd to inform it of pulling alarm firing.");
- return;
- }
- try {
- // Two-way call to statsd to retain AlarmManager wakelock
- sStatsd.informPollAlarmFired();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to inform statsd of pulling alarm firing.", e);
- }
- }
- }
- }
-
- public final static class PeriodicAlarmListener implements OnAlarmListener {
- @Override
- public void onAlarm() {
- if (DEBUG) {
- Slog.d(TAG, "Time to trigger periodic alarm.");
- }
- synchronized (sStatsdLock) {
- if (sStatsd == null) {
- Slog.w(TAG, "Could not access statsd to inform it of periodic alarm firing.");
- return;
- }
- try {
- // Two-way call to statsd to retain AlarmManager wakelock
- sStatsd.informAlarmForSubscriberTriggeringFired();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to inform statsd of periodic alarm firing.", e);
- }
- }
- // AlarmManager releases its own wakelock here.
- }
- }
-
- public final static class ShutdownEventReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- /**
- * Skip immediately if intent is not relevant to device shutdown.
- */
- if (!intent.getAction().equals(Intent.ACTION_REBOOT)
- && !(intent.getAction().equals(Intent.ACTION_SHUTDOWN)
- && (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0)) {
- return;
- }
-
- Slog.i(TAG, "StatsCompanionService noticed a shutdown.");
- synchronized (sStatsdLock) {
- if (sStatsd == null) {
- Slog.w(TAG, "Could not access statsd to inform it of a shutdown event.");
- return;
- }
- try {
- sStatsd.informDeviceShutdown();
- } catch (Exception e) {
- Slog.w(TAG, "Failed to inform statsd of a shutdown event.", e);
- }
- }
- }
- }
-
- @Override // Binder call
- public void setAnomalyAlarm(long timestampMs) {
- enforceCallingPermission();
- if (DEBUG) Slog.d(TAG, "Setting anomaly alarm for " + timestampMs);
- final long callingToken = Binder.clearCallingIdentity();
- try {
- // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
- // only fire when it awakens.
- // AlarmManager will automatically cancel any previous mAnomalyAlarmListener alarm.
- mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, timestampMs, TAG + ".anomaly",
- mAnomalyAlarmListener, mHandler);
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
- }
-
- @Override // Binder call
- public void cancelAnomalyAlarm() {
- enforceCallingPermission();
- if (DEBUG) Slog.d(TAG, "Cancelling anomaly alarm");
- final long callingToken = Binder.clearCallingIdentity();
- try {
- mAlarmManager.cancel(mAnomalyAlarmListener);
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
- }
-
- @Override // Binder call
- public void setAlarmForSubscriberTriggering(long timestampMs) {
- enforceCallingPermission();
- if (DEBUG) {
- Slog.d(TAG,
- "Setting periodic alarm in about " + (timestampMs
- - SystemClock.elapsedRealtime()));
- }
- final long callingToken = Binder.clearCallingIdentity();
- try {
- // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
- // only fire when it awakens.
- mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, timestampMs, TAG + ".periodic",
- mPeriodicAlarmListener, mHandler);
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
- }
-
- @Override // Binder call
- public void cancelAlarmForSubscriberTriggering() {
- enforceCallingPermission();
- if (DEBUG) {
- Slog.d(TAG, "Cancelling periodic alarm");
- }
- final long callingToken = Binder.clearCallingIdentity();
- try {
- mAlarmManager.cancel(mPeriodicAlarmListener);
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
- }
-
- @Override // Binder call
- public void setPullingAlarm(long nextPullTimeMs) {
- enforceCallingPermission();
- if (DEBUG) {
- Slog.d(TAG, "Setting pulling alarm in about "
- + (nextPullTimeMs - SystemClock.elapsedRealtime()));
- }
- final long callingToken = Binder.clearCallingIdentity();
- try {
- // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
- // only fire when it awakens.
- mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, nextPullTimeMs, TAG + ".pull",
- mPullingAlarmListener, mHandler);
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
- }
-
- @Override // Binder call
- public void cancelPullingAlarm() {
- enforceCallingPermission();
- if (DEBUG) {
- Slog.d(TAG, "Cancelling pulling alarm");
- }
- final long callingToken = Binder.clearCallingIdentity();
- try {
- mAlarmManager.cancel(mPullingAlarmListener);
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
- }
-
- private void addNetworkStats(
- int tag, List<StatsLogEventWrapper> ret, NetworkStats stats, boolean withFGBG) {
- int size = stats.size();
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
- long wallClockNanos = SystemClock.currentTimeMicro() * 1000L;
- NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
- for (int j = 0; j < size; j++) {
- stats.getValues(j, entry);
- StatsLogEventWrapper e = new StatsLogEventWrapper(tag, elapsedNanos, wallClockNanos);
- e.writeInt(entry.uid);
- if (withFGBG) {
- e.writeInt(entry.set);
- }
- e.writeLong(entry.rxBytes);
- e.writeLong(entry.rxPackets);
- e.writeLong(entry.txBytes);
- e.writeLong(entry.txPackets);
- ret.add(e);
- }
- }
-
- /**
- * Allows rollups per UID but keeping the set (foreground/background) slicing.
- * Adapted from groupedByUid in frameworks/base/core/java/android/net/NetworkStats.java
- */
- private NetworkStats rollupNetworkStatsByFGBG(NetworkStats stats) {
- final NetworkStats ret = new NetworkStats(stats.getElapsedRealtime(), 1);
-
- final NetworkStats.Entry entry = new NetworkStats.Entry();
- entry.iface = NetworkStats.IFACE_ALL;
- entry.tag = NetworkStats.TAG_NONE;
- entry.metered = NetworkStats.METERED_ALL;
- entry.roaming = NetworkStats.ROAMING_ALL;
-
- int size = stats.size();
- NetworkStats.Entry recycle = new NetworkStats.Entry(); // Used for retrieving values
- for (int i = 0; i < size; i++) {
- stats.getValues(i, recycle);
-
- // Skip specific tags, since already counted in TAG_NONE
- if (recycle.tag != NetworkStats.TAG_NONE) continue;
-
- entry.set = recycle.set; // Allows slicing by background/foreground
- entry.uid = recycle.uid;
- entry.rxBytes = recycle.rxBytes;
- entry.rxPackets = recycle.rxPackets;
- entry.txBytes = recycle.txBytes;
- entry.txPackets = recycle.txPackets;
- // Operations purposefully omitted since we don't use them for statsd.
- ret.combineValues(entry);
- }
- return ret;
- }
-
- /**
- * Helper method to extract the Parcelable controller info from a
- * SynchronousResultReceiver.
- */
- private static <T extends Parcelable> T awaitControllerInfo(
- @Nullable SynchronousResultReceiver receiver) {
- if (receiver == null) {
- return null;
- }
-
- try {
- final SynchronousResultReceiver.Result result =
- receiver.awaitResult(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS);
- if (result.bundle != null) {
- // This is the final destination for the Bundle.
- result.bundle.setDefusable(true);
-
- final T data = result.bundle.getParcelable(
- RESULT_RECEIVER_CONTROLLER_KEY);
- if (data != null) {
- return data;
- }
- }
- Slog.e(TAG, "no controller energy info supplied for " + receiver.getName());
- } catch (TimeoutException e) {
- Slog.w(TAG, "timeout reading " + receiver.getName() + " stats");
- }
- return null;
- }
-
- private void pullKernelWakelock(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- final KernelWakelockStats wakelockStats =
- mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
- for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
- String name = ent.getKey();
- KernelWakelockStats.Entry kws = ent.getValue();
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeString(name);
- e.writeInt(kws.mCount);
- e.writeInt(kws.mVersion);
- e.writeLong(kws.mTotalTime);
- pulledData.add(e);
- }
- }
-
- private void pullWifiBytesTransfer(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- try {
- // TODO: Consider caching the following call to get BatteryStatsInternal.
- BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
- String[] ifaces = bs.getWifiIfaces();
- if (ifaces.length == 0) {
- return;
- }
- if (mNetworkStatsService == null) {
- Slog.e(TAG, "NetworkStats Service is not available!");
- return;
- }
- // Combine all the metrics per Uid into one record.
- NetworkStats stats = mNetworkStatsService.getDetailedUidStats(ifaces).groupedByUid();
- addNetworkStats(tagId, pulledData, stats, false);
- } catch (RemoteException e) {
- Slog.e(TAG, "Pulling netstats for wifi bytes has error", e);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- private void pullWifiBytesTransferByFgBg(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- try {
- BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
- String[] ifaces = bs.getWifiIfaces();
- if (ifaces.length == 0) {
- return;
- }
- if (mNetworkStatsService == null) {
- Slog.e(TAG, "NetworkStats Service is not available!");
- return;
- }
- NetworkStats stats = rollupNetworkStatsByFGBG(
- mNetworkStatsService.getDetailedUidStats(ifaces));
- addNetworkStats(tagId, pulledData, stats, true);
- } catch (RemoteException e) {
- Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- private void pullMobileBytesTransfer(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- try {
- BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
- String[] ifaces = bs.getMobileIfaces();
- if (ifaces.length == 0) {
- return;
- }
- if (mNetworkStatsService == null) {
- Slog.e(TAG, "NetworkStats Service is not available!");
- return;
- }
- // Combine all the metrics per Uid into one record.
- NetworkStats stats = mNetworkStatsService.getDetailedUidStats(ifaces).groupedByUid();
- addNetworkStats(tagId, pulledData, stats, false);
- } catch (RemoteException e) {
- Slog.e(TAG, "Pulling netstats for mobile bytes has error", e);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- private void pullBluetoothBytesTransfer(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- BluetoothActivityEnergyInfo info = fetchBluetoothData();
- if (info.getUidTraffic() != null) {
- for (UidTraffic traffic : info.getUidTraffic()) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
- wallClockNanos);
- e.writeInt(traffic.getUid());
- e.writeLong(traffic.getRxBytes());
- e.writeLong(traffic.getTxBytes());
- pulledData.add(e);
- }
- }
- }
-
- private void pullMobileBytesTransferByFgBg(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- try {
- BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
- String[] ifaces = bs.getMobileIfaces();
- if (ifaces.length == 0) {
- return;
- }
- if (mNetworkStatsService == null) {
- Slog.e(TAG, "NetworkStats Service is not available!");
- return;
- }
- NetworkStats stats = rollupNetworkStatsByFGBG(
- mNetworkStatsService.getDetailedUidStats(ifaces));
- addNetworkStats(tagId, pulledData, stats, true);
- } catch (RemoteException e) {
- Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- private void pullCpuTimePerFreq(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
- long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readAbsolute();
- if (clusterTimeMs != null) {
- for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
- wallClockNanos);
- e.writeInt(cluster);
- e.writeInt(speed);
- e.writeLong(clusterTimeMs[speed]);
- pulledData.add(e);
- }
- }
- }
- }
-
- private void pullKernelUidCpuTime(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- mCpuUidUserSysTimeReader.readAbsolute((uid, timesUs) -> {
- long userTimeUs = timesUs[0], systemTimeUs = timesUs[1];
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(uid);
- e.writeLong(userTimeUs);
- e.writeLong(systemTimeUs);
- pulledData.add(e);
- });
- }
-
- private void pullKernelUidCpuFreqTime(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- mCpuUidFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> {
- for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) {
- if (cpuFreqTimeMs[freqIndex] != 0) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
- wallClockNanos);
- e.writeInt(uid);
- e.writeInt(freqIndex);
- e.writeLong(cpuFreqTimeMs[freqIndex]);
- pulledData.add(e);
- }
- }
- });
- }
-
- private void pullKernelUidCpuClusterTime(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- mCpuUidClusterTimeReader.readAbsolute((uid, cpuClusterTimesMs) -> {
- for (int i = 0; i < cpuClusterTimesMs.length; i++) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
- wallClockNanos);
- e.writeInt(uid);
- e.writeInt(i);
- e.writeLong(cpuClusterTimesMs[i]);
- pulledData.add(e);
- }
- });
- }
-
- private void pullKernelUidCpuActiveTime(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- mCpuUidActiveTimeReader.readAbsolute((uid, cpuActiveTimesMs) -> {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(uid);
- e.writeLong((long) cpuActiveTimesMs);
- pulledData.add(e);
- });
- }
-
- private void pullWifiActivityInfo(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- synchronized (this) {
- if (mWifiManager == null) {
- mWifiManager =
- IWifiManager.Stub.asInterface(
- ServiceManager.getService(Context.WIFI_SERVICE));
- }
- }
- if (mWifiManager != null) {
- try {
- SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
- mWifiManager.requestActivityInfo(wifiReceiver);
- final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
- wallClockNanos);
- e.writeLong(wifiInfo.getTimeStamp());
- e.writeInt(wifiInfo.getStackState());
- e.writeLong(wifiInfo.getControllerTxTimeMillis());
- e.writeLong(wifiInfo.getControllerRxTimeMillis());
- e.writeLong(wifiInfo.getControllerIdleTimeMillis());
- e.writeLong(wifiInfo.getControllerEnergyUsed());
- pulledData.add(e);
- } catch (RemoteException e) {
- Slog.e(TAG,
- "Pulling wifiManager for wifi controller activity energy info has error",
- e);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- }
-
- private void pullModemActivityInfo(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- synchronized (this) {
- if (mTelephony == null) {
- mTelephony = TelephonyManager.from(mContext);
- }
- }
- if (mTelephony != null) {
- SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
- mTelephony.requestModemActivityInfo(modemReceiver);
- final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeLong(modemInfo.getTimestamp());
- e.writeLong(modemInfo.getSleepTimeMillis());
- e.writeLong(modemInfo.getIdleTimeMillis());
- e.writeLong(modemInfo.getTransmitPowerInfo().get(0).getTimeInMillis());
- e.writeLong(modemInfo.getTransmitPowerInfo().get(1).getTimeInMillis());
- e.writeLong(modemInfo.getTransmitPowerInfo().get(2).getTimeInMillis());
- e.writeLong(modemInfo.getTransmitPowerInfo().get(3).getTimeInMillis());
- e.writeLong(modemInfo.getTransmitPowerInfo().get(4).getTimeInMillis());
- e.writeLong(modemInfo.getReceiveTimeMillis());
- pulledData.add(e);
- }
- }
-
- private void pullBluetoothActivityInfo(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- BluetoothActivityEnergyInfo info = fetchBluetoothData();
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeLong(info.getTimeStamp());
- e.writeInt(info.getBluetoothStackState());
- e.writeLong(info.getControllerTxTimeMillis());
- e.writeLong(info.getControllerRxTimeMillis());
- e.writeLong(info.getControllerIdleTimeMillis());
- e.writeLong(info.getControllerEnergyUsed());
- pulledData.add(e);
- }
-
- private synchronized BluetoothActivityEnergyInfo fetchBluetoothData() {
- final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- if (adapter != null) {
- SynchronousResultReceiver bluetoothReceiver = new SynchronousResultReceiver(
- "bluetooth");
- adapter.requestControllerActivityEnergyInfo(bluetoothReceiver);
- return awaitControllerInfo(bluetoothReceiver);
- } else {
- Slog.e(TAG, "Failed to get bluetooth adapter!");
- return null;
- }
- }
-
- private void pullSystemElapsedRealtime(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeLong(SystemClock.elapsedRealtime());
- pulledData.add(e);
- }
-
- private void pullSystemUpTime(int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeLong(SystemClock.uptimeMillis());
- pulledData.add(e);
- }
-
- private void pullProcessMemoryState(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- List<ProcessMemoryState> processMemoryStates =
- LocalServices.getService(
- ActivityManagerInternal.class).getMemoryStateForProcesses();
- for (ProcessMemoryState processMemoryState : processMemoryStates) {
- final MemoryStat memoryStat = readMemoryStatFromFilesystem(processMemoryState.uid,
- processMemoryState.pid);
- if (memoryStat == null) {
- continue;
- }
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(processMemoryState.uid);
- e.writeString(processMemoryState.processName);
- e.writeInt(processMemoryState.oomScore);
- e.writeLong(memoryStat.pgfault);
- e.writeLong(memoryStat.pgmajfault);
- e.writeLong(memoryStat.rssInBytes);
- e.writeLong(memoryStat.cacheInBytes);
- e.writeLong(memoryStat.swapInBytes);
- e.writeLong(0); // unused
- e.writeLong(memoryStat.startTimeNanos);
- e.writeInt(anonAndSwapInKilobytes(memoryStat));
- pulledData.add(e);
- }
- }
-
- private void pullNativeProcessMemoryState(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- final List<String> processNames = Arrays.asList(MEMORY_INTERESTING_NATIVE_PROCESSES);
- int[] pids = getPidsForCommands(MEMORY_INTERESTING_NATIVE_PROCESSES);
- for (int i = 0; i < pids.length; i++) {
- int pid = pids[i];
- MemoryStat memoryStat = readMemoryStatFromProcfs(pid);
- if (memoryStat == null) {
- continue;
- }
- int uid = getUidForPid(pid);
- String processName = readCmdlineFromProcfs(pid);
- // Sometimes we get here processName that is not included in the whitelist. It comes
- // from forking the zygote for an app. We can ignore that sample because this process
- // is collected by ProcessMemoryState.
- if (!processNames.contains(processName)) {
- continue;
- }
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(uid);
- e.writeString(processName);
- e.writeLong(memoryStat.pgfault);
- e.writeLong(memoryStat.pgmajfault);
- e.writeLong(memoryStat.rssInBytes);
- e.writeLong(0); // unused
- e.writeLong(memoryStat.startTimeNanos);
- e.writeLong(memoryStat.swapInBytes);
- e.writeInt(anonAndSwapInKilobytes(memoryStat));
- pulledData.add(e);
- }
- }
-
- private static int anonAndSwapInKilobytes(MemoryStat memoryStat) {
- return (int) ((memoryStat.anonRssInBytes + memoryStat.swapInBytes) / 1024);
- }
-
- private void pullProcessMemoryHighWaterMark(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- List<ProcessMemoryState> managedProcessList =
- LocalServices.getService(
- ActivityManagerInternal.class).getMemoryStateForProcesses();
- for (ProcessMemoryState managedProcess : managedProcessList) {
- final int rssHighWaterMarkInKilobytes =
- readRssHighWaterMarkFromProcfs(managedProcess.pid);
- if (rssHighWaterMarkInKilobytes == 0) {
- continue;
- }
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(managedProcess.uid);
- e.writeString(managedProcess.processName);
- // RSS high-water mark in bytes.
- e.writeLong((long) rssHighWaterMarkInKilobytes * 1024L);
- e.writeInt(rssHighWaterMarkInKilobytes);
- pulledData.add(e);
- }
- int[] pids = getPidsForCommands(MEMORY_INTERESTING_NATIVE_PROCESSES);
- for (int i = 0; i < pids.length; i++) {
- final int pid = pids[i];
- final int uid = getUidForPid(pid);
- final String processName = readCmdlineFromProcfs(pid);
- final int rssHighWaterMarkInKilobytes = readRssHighWaterMarkFromProcfs(pid);
- if (rssHighWaterMarkInKilobytes == 0) {
- continue;
- }
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(uid);
- e.writeString(processName);
- // RSS high-water mark in bytes.
- e.writeLong((long) rssHighWaterMarkInKilobytes * 1024L);
- e.writeInt(rssHighWaterMarkInKilobytes);
- pulledData.add(e);
- }
- // Invoke rss_hwm_reset binary to reset RSS HWM counters for all processes.
- SystemProperties.set("sys.rss_hwm_reset.on", "1");
- }
-
- private void pullSystemIonHeapSize(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- final long systemIonHeapSizeInBytes = readSystemIonHeapSizeFromDebugfs();
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeLong(systemIonHeapSizeInBytes);
- pulledData.add(e);
- }
-
- private void pullProcessSystemIonHeapSize(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- List<IonAllocations> result = readProcessSystemIonHeapSizesFromDebugfs();
- for (IonAllocations allocations : result) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(getUidForPid(allocations.pid));
- e.writeString(readCmdlineFromProcfs(allocations.pid));
- e.writeInt((int) (allocations.totalSizeInBytes / 1024));
- e.writeInt(allocations.count);
- e.writeInt((int) (allocations.maxSizeInBytes / 1024));
- pulledData.add(e);
- }
- }
-
- private void pullBinderCallsStats(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- BinderCallsStatsService.Internal binderStats =
- LocalServices.getService(BinderCallsStatsService.Internal.class);
- if (binderStats == null) {
- throw new IllegalStateException("binderStats is null");
- }
-
- List<ExportedCallStat> callStats = binderStats.getExportedCallStats();
- binderStats.reset();
- for (ExportedCallStat callStat : callStats) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(callStat.workSourceUid);
- e.writeString(callStat.className);
- e.writeString(callStat.methodName);
- e.writeLong(callStat.callCount);
- e.writeLong(callStat.exceptionCount);
- e.writeLong(callStat.latencyMicros);
- e.writeLong(callStat.maxLatencyMicros);
- e.writeLong(callStat.cpuTimeMicros);
- e.writeLong(callStat.maxCpuTimeMicros);
- e.writeLong(callStat.maxReplySizeBytes);
- e.writeLong(callStat.maxRequestSizeBytes);
- e.writeLong(callStat.recordedCallCount);
- e.writeInt(callStat.screenInteractive ? 1 : 0);
- e.writeInt(callStat.callingUid);
- pulledData.add(e);
- }
- }
-
- private void pullBinderCallsStatsExceptions(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- BinderCallsStatsService.Internal binderStats =
- LocalServices.getService(BinderCallsStatsService.Internal.class);
- if (binderStats == null) {
- throw new IllegalStateException("binderStats is null");
- }
-
- ArrayMap<String, Integer> exceptionStats = binderStats.getExportedExceptionStats();
- // TODO: decouple binder calls exceptions with the rest of the binder calls data so that we
- // can reset the exception stats.
- for (Entry<String, Integer> entry : exceptionStats.entrySet()) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeString(entry.getKey());
- e.writeInt(entry.getValue());
- pulledData.add(e);
- }
- }
-
- private void pullLooperStats(int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- LooperStats looperStats = LocalServices.getService(LooperStats.class);
- if (looperStats == null) {
- throw new IllegalStateException("looperStats null");
- }
-
- List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
- looperStats.reset();
- for (LooperStats.ExportedEntry entry : entries) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(entry.workSourceUid);
- e.writeString(entry.handlerClassName);
- e.writeString(entry.threadName);
- e.writeString(entry.messageName);
- e.writeLong(entry.messageCount);
- e.writeLong(entry.exceptionCount);
- e.writeLong(entry.recordedMessageCount);
- e.writeLong(entry.totalLatencyMicros);
- e.writeLong(entry.cpuUsageMicros);
- e.writeBoolean(entry.isInteractive);
- e.writeLong(entry.maxCpuUsageMicros);
- e.writeLong(entry.maxLatencyMicros);
- e.writeLong(entry.recordedDelayMessageCount);
- e.writeLong(entry.delayMillis);
- e.writeLong(entry.maxDelayMillis);
- pulledData.add(e);
- }
- }
-
- private void pullDiskStats(int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- // Run a quick-and-dirty performance test: write 512 bytes
- byte[] junk = new byte[512];
- for (int i = 0; i < junk.length; i++) junk[i] = (byte) i; // Write nonzero bytes
-
- File tmp = new File(Environment.getDataDirectory(), "system/statsdperftest.tmp");
- FileOutputStream fos = null;
- IOException error = null;
-
- long before = SystemClock.elapsedRealtime();
- try {
- fos = new FileOutputStream(tmp);
- fos.write(junk);
- } catch (IOException e) {
- error = e;
- } finally {
- try {
- if (fos != null) fos.close();
- } catch (IOException e) {
- // Do nothing.
- }
- }
-
- long latency = SystemClock.elapsedRealtime() - before;
- if (tmp.exists()) tmp.delete();
-
- if (error != null) {
- Slog.e(TAG, "Error performing diskstats latency test");
- latency = -1;
- }
- // File based encryption.
- boolean fileBased = StorageManager.isFileEncryptedNativeOnly();
-
- //Recent disk write speed. Binder call to storaged.
- int writeSpeed = -1;
- try {
- IBinder binder = ServiceManager.getService("storaged");
- if (binder == null) {
- Slog.e(TAG, "storaged not found");
- }
- IStoraged storaged = IStoraged.Stub.asInterface(binder);
- writeSpeed = storaged.getRecentPerf();
- } catch (RemoteException e) {
- Slog.e(TAG, "storaged not found");
- }
-
- // Add info pulledData.
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeLong(latency);
- e.writeBoolean(fileBased);
- e.writeInt(writeSpeed);
- pulledData.add(e);
- }
-
- private void pullDirectoryUsage(int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- StatFs statFsData = new StatFs(Environment.getDataDirectory().getAbsolutePath());
- StatFs statFsSystem = new StatFs(Environment.getRootDirectory().getAbsolutePath());
- StatFs statFsCache = new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
-
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__DATA);
- e.writeLong(statFsData.getAvailableBytes());
- e.writeLong(statFsData.getTotalBytes());
- pulledData.add(e);
-
- e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__CACHE);
- e.writeLong(statFsCache.getAvailableBytes());
- e.writeLong(statFsCache.getTotalBytes());
- pulledData.add(e);
-
- e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__SYSTEM);
- e.writeLong(statFsSystem.getAvailableBytes());
- e.writeLong(statFsSystem.getTotalBytes());
- pulledData.add(e);
- }
-
- private void pullAppSize(int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- try {
- String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH);
- JSONObject json = new JSONObject(jsonStr);
- long cache_time = json.optLong(DiskStatsFileLogger.LAST_QUERY_TIMESTAMP_KEY, -1L);
- JSONArray pkg_names = json.getJSONArray(DiskStatsFileLogger.PACKAGE_NAMES_KEY);
- JSONArray app_sizes = json.getJSONArray(DiskStatsFileLogger.APP_SIZES_KEY);
- JSONArray app_data_sizes = json.getJSONArray(DiskStatsFileLogger.APP_DATA_KEY);
- JSONArray app_cache_sizes = json.getJSONArray(DiskStatsFileLogger.APP_CACHES_KEY);
- // Sanity check: Ensure all 4 lists have the same length.
- int length = pkg_names.length();
- if (app_sizes.length() != length || app_data_sizes.length() != length
- || app_cache_sizes.length() != length) {
- Slog.e(TAG, "formatting error in diskstats cache file!");
- return;
- }
- for (int i = 0; i < length; i++) {
- StatsLogEventWrapper e =
- new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeString(pkg_names.getString(i));
- e.writeLong(app_sizes.optLong(i, -1L));
- e.writeLong(app_data_sizes.optLong(i, -1L));
- e.writeLong(app_cache_sizes.optLong(i, -1L));
- e.writeLong(cache_time);
- pulledData.add(e);
- }
- } catch (IOException | JSONException e) {
- Slog.e(TAG, "exception reading diskstats cache file", e);
- }
- }
-
- private void pullCategorySize(int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- try {
- String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH);
- JSONObject json = new JSONObject(jsonStr);
- long cacheTime = json.optLong(DiskStatsFileLogger.LAST_QUERY_TIMESTAMP_KEY, -1L);
-
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_SIZE);
- e.writeLong(json.optLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY, -1L));
- e.writeLong(cacheTime);
- pulledData.add(e);
-
- e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_DATA_SIZE);
- e.writeLong(json.optLong(DiskStatsFileLogger.APP_DATA_SIZE_AGG_KEY, -1L));
- e.writeLong(cacheTime);
- pulledData.add(e);
-
- e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_CACHE_SIZE);
- e.writeLong(json.optLong(DiskStatsFileLogger.APP_CACHE_AGG_KEY, -1L));
- e.writeLong(cacheTime);
- pulledData.add(e);
-
- e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__PHOTOS);
- e.writeLong(json.optLong(DiskStatsFileLogger.PHOTOS_KEY, -1L));
- e.writeLong(cacheTime);
- pulledData.add(e);
-
- e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__VIDEOS);
- e.writeLong(json.optLong(DiskStatsFileLogger.VIDEOS_KEY, -1L));
- e.writeLong(cacheTime);
- pulledData.add(e);
-
- e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__AUDIO);
- e.writeLong(json.optLong(DiskStatsFileLogger.AUDIO_KEY, -1L));
- e.writeLong(cacheTime);
- pulledData.add(e);
-
- e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__DOWNLOADS);
- e.writeLong(json.optLong(DiskStatsFileLogger.DOWNLOADS_KEY, -1L));
- e.writeLong(cacheTime);
- pulledData.add(e);
-
- e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__SYSTEM);
- e.writeLong(json.optLong(DiskStatsFileLogger.SYSTEM_KEY, -1L));
- e.writeLong(cacheTime);
- pulledData.add(e);
-
- e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__OTHER);
- e.writeLong(json.optLong(DiskStatsFileLogger.MISC_KEY, -1L));
- e.writeLong(cacheTime);
- pulledData.add(e);
- } catch (IOException | JSONException e) {
- Slog.e(TAG, "exception reading diskstats cache file", e);
- }
- }
-
- private void pullNumBiometricsEnrolled(int modality, int tagId, long elapsedNanos,
- long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
- final PackageManager pm = mContext.getPackageManager();
- FingerprintManager fingerprintManager = null;
- FaceManager faceManager = null;
-
- if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
- fingerprintManager = mContext.getSystemService(
- FingerprintManager.class);
- }
- if (pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
- faceManager = mContext.getSystemService(FaceManager.class);
- }
-
- if (modality == BiometricsProtoEnums.MODALITY_FINGERPRINT && fingerprintManager == null) {
- return;
- }
- if (modality == BiometricsProtoEnums.MODALITY_FACE && faceManager == null) {
- return;
- }
- UserManager userManager = mContext.getSystemService(UserManager.class);
- if (userManager == null) {
- return;
- }
-
- final long token = Binder.clearCallingIdentity();
- for (UserInfo user : userManager.getUsers()) {
- final int userId = user.getUserHandle().getIdentifier();
- int numEnrolled = 0;
- if (modality == BiometricsProtoEnums.MODALITY_FINGERPRINT) {
- numEnrolled = fingerprintManager.getEnrolledFingerprints(userId).size();
- } else if (modality == BiometricsProtoEnums.MODALITY_FACE) {
- numEnrolled = faceManager.getEnrolledFaces(userId).size();
- } else {
- return;
- }
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(userId);
- e.writeInt(numEnrolled);
- pulledData.add(e);
- }
- Binder.restoreCallingIdentity(token);
- }
-
- // read high watermark for section
- private long readProcStatsHighWaterMark(int section) {
- try {
- File[] files = mBaseDir.listFiles((d, name) -> {
- return name.toLowerCase().startsWith(String.valueOf(section) + '_');
- });
- if (files == null || files.length == 0) {
- return 0;
- }
- if (files.length > 1) {
- Log.e(TAG, "Only 1 file expected for high water mark. Found " + files.length);
- }
- return Long.valueOf(files[0].getName().split("_")[1]);
- } catch (SecurityException e) {
- Log.e(TAG, "Failed to get procstats high watermark file.", e);
- } catch (NumberFormatException e) {
- Log.e(TAG, "Failed to parse file name.", e);
- }
- return 0;
- }
-
- private IProcessStats mProcessStats =
- IProcessStats.Stub.asInterface(ServiceManager.getService(ProcessStats.SERVICE_NAME));
-
- private void pullProcessStats(int section, int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- synchronized (this) {
- try {
- long lastHighWaterMark = readProcStatsHighWaterMark(section);
- List<ParcelFileDescriptor> statsFiles = new ArrayList<>();
- long highWaterMark = mProcessStats.getCommittedStats(
- lastHighWaterMark, section, true, statsFiles);
- if (statsFiles.size() != 1) {
- return;
- }
- InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(
- statsFiles.get(0));
- int[] len = new int[1];
- byte[] stats = readFully(stream, len);
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
- wallClockNanos);
- e.writeStorage(Arrays.copyOf(stats, len[0]));
- pulledData.add(e);
- new File(mBaseDir.getAbsolutePath() + "/" + section + "_"
- + lastHighWaterMark).delete();
- new File(
- mBaseDir.getAbsolutePath() + "/" + section + "_"
- + highWaterMark).createNewFile();
- } catch (IOException e) {
- Log.e(TAG, "Getting procstats failed: ", e);
- } catch (RemoteException e) {
- Log.e(TAG, "Getting procstats failed: ", e);
- } catch (SecurityException e) {
- Log.e(TAG, "Getting procstats failed: ", e);
- }
- }
- }
-
- static byte[] readFully(InputStream stream, int[] outLen) throws IOException {
- int pos = 0;
- final int initialAvail = stream.available();
- byte[] data = new byte[initialAvail > 0 ? (initialAvail + 1) : 16384];
- while (true) {
- int amt = stream.read(data, pos, data.length - pos);
- if (DEBUG) {
- Slog.i(TAG, "Read " + amt + " bytes at " + pos + " of avail " + data.length);
- }
- if (amt < 0) {
- if (DEBUG) {
- Slog.i(TAG, "**** FINISHED READING: pos=" + pos + " len=" + data.length);
- }
- outLen[0] = pos;
- return data;
- }
- pos += amt;
- if (pos >= data.length) {
- byte[] newData = new byte[pos + 16384];
- if (DEBUG) {
- Slog.i(TAG, "Copying " + pos + " bytes to new array len " + newData.length);
- }
- System.arraycopy(data, 0, newData, 0, pos);
- data = newData;
- }
- }
- }
-
- private void pullPowerProfile(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- PowerProfile powerProfile = new PowerProfile(mContext);
- checkNotNull(powerProfile);
-
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
- wallClockNanos);
- ProtoOutputStream proto = new ProtoOutputStream();
- powerProfile.writeToProto(proto);
- proto.flush();
- e.writeStorage(proto.getBytes());
- pulledData.add(e);
- }
-
- private void pullBuildInformation(int tagId,
- long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeString(Build.FINGERPRINT);
- e.writeString(Build.BRAND);
- e.writeString(Build.PRODUCT);
- e.writeString(Build.DEVICE);
- e.writeString(Build.VERSION.RELEASE);
- e.writeString(Build.ID);
- e.writeString(Build.VERSION.INCREMENTAL);
- e.writeString(Build.TYPE);
- e.writeString(Build.TAGS);
- pulledData.add(e);
- }
-
- private BatteryStatsHelper getBatteryStatsHelper() {
- if (mBatteryStatsHelper == null) {
- final long callingToken = Binder.clearCallingIdentity();
- try {
- // clearCallingIdentity required for BatteryStatsHelper.checkWifiOnly().
- mBatteryStatsHelper = new BatteryStatsHelper(mContext, false);
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
- mBatteryStatsHelper.create((Bundle) null);
- }
- long currentTime = SystemClock.elapsedRealtime();
- if (currentTime - mBatteryStatsHelperTimestampMs >= MAX_BATTERY_STATS_HELPER_FREQUENCY_MS) {
- // Load BatteryStats and do all the calculations.
- mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.USER_ALL);
- // Calculations are done so we don't need to save the raw BatteryStats data in RAM.
- mBatteryStatsHelper.clearStats();
- mBatteryStatsHelperTimestampMs = currentTime;
- }
- return mBatteryStatsHelper;
- }
-
- private long milliAmpHrsToNanoAmpSecs(double mAh) {
- final long MILLI_AMP_HR_TO_NANO_AMP_SECS = 1_000_000L * 3600L;
- return (long) (mAh * MILLI_AMP_HR_TO_NANO_AMP_SECS + 0.5);
- }
-
- private void pullDeviceCalculatedPowerUse(int tagId,
- long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
- BatteryStatsHelper bsHelper = getBatteryStatsHelper();
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeLong(milliAmpHrsToNanoAmpSecs(bsHelper.getComputedPower()));
- pulledData.add(e);
- }
-
- private void pullDeviceCalculatedPowerBlameUid(int tagId,
- long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
- final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
- if (sippers == null) {
- return;
- }
- for (BatterySipper bs : sippers) {
- if (bs.drainType != bs.drainType.APP) {
- continue;
- }
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(bs.uidObj.getUid());
- e.writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah));
- pulledData.add(e);
- }
- }
-
- private void pullDeviceCalculatedPowerBlameOther(int tagId,
- long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
- final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
- if (sippers == null) {
- return;
- }
- for (BatterySipper bs : sippers) {
- if (bs.drainType == bs.drainType.APP) {
- continue; // This is a separate atom; see pullDeviceCalculatedPowerBlameUid().
- }
- if (bs.drainType == bs.drainType.USER) {
- continue; // This is not supported. We purposefully calculate over USER_ALL.
- }
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(bs.drainType.ordinal());
- e.writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah));
- pulledData.add(e);
- }
- }
-
- private void pullDiskIo(int tagId, long elapsedNanos, final long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- mStoragedUidIoStatsReader.readAbsolute((uid, fgCharsRead, fgCharsWrite, fgBytesRead,
- fgBytesWrite, bgCharsRead, bgCharsWrite, bgBytesRead, bgBytesWrite,
- fgFsync, bgFsync) -> {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
- wallClockNanos);
- e.writeInt(uid);
- e.writeLong(fgCharsRead);
- e.writeLong(fgCharsWrite);
- e.writeLong(fgBytesRead);
- e.writeLong(fgBytesWrite);
- e.writeLong(bgCharsRead);
- e.writeLong(bgCharsWrite);
- e.writeLong(bgBytesRead);
- e.writeLong(bgBytesWrite);
- e.writeLong(fgFsync);
- e.writeLong(bgFsync);
- pulledData.add(e);
- });
- }
-
- private void pullProcessCpuTime(int tagId, long elapsedNanos, final long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- synchronized (this) {
- if (mProcessCpuTracker == null) {
- mProcessCpuTracker = new ProcessCpuTracker(false);
- mProcessCpuTracker.init();
- }
- mProcessCpuTracker.update();
- for (int i = 0; i < mProcessCpuTracker.countStats(); i++) {
- ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
- wallClockNanos);
- e.writeInt(st.uid);
- e.writeString(st.name);
- e.writeLong(st.base_utime);
- e.writeLong(st.base_stime);
- pulledData.add(e);
- }
- }
- }
-
- private void pullCpuTimePerThreadFreq(int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- if (this.mKernelCpuThreadReader == null) {
- throw new IllegalStateException("mKernelCpuThreadReader is null");
- }
- ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsages =
- this.mKernelCpuThreadReader.getProcessCpuUsageDiffed();
- if (processCpuUsages == null) {
- throw new IllegalStateException("processCpuUsages is null");
- }
- int[] cpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz();
- if (cpuFrequencies.length > CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES) {
- String message = "Expected maximum " + CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES
- + " frequencies, but got " + cpuFrequencies.length;
- Slog.w(TAG, message);
- throw new IllegalStateException(message);
- }
- for (int i = 0; i < processCpuUsages.size(); i++) {
- KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = processCpuUsages.get(i);
- ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages =
- processCpuUsage.threadCpuUsages;
- for (int j = 0; j < threadCpuUsages.size(); j++) {
- KernelCpuThreadReader.ThreadCpuUsage threadCpuUsage = threadCpuUsages.get(j);
- if (threadCpuUsage.usageTimesMillis.length != cpuFrequencies.length) {
- String message = "Unexpected number of usage times,"
- + " expected " + cpuFrequencies.length
- + " but got " + threadCpuUsage.usageTimesMillis.length;
- Slog.w(TAG, message);
- throw new IllegalStateException(message);
- }
-
- StatsLogEventWrapper e =
- new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(processCpuUsage.uid);
- e.writeInt(processCpuUsage.processId);
- e.writeInt(threadCpuUsage.threadId);
- e.writeString(processCpuUsage.processName);
- e.writeString(threadCpuUsage.threadName);
- 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]);
- } 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);
- }
- }
- pulledData.add(e);
- }
- }
- }
-
- private void pullTemperature(int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- long callingToken = Binder.clearCallingIdentity();
- try {
- List<Temperature> temperatures = sThermalService.getCurrentTemperatures();
- for (Temperature temp : temperatures) {
- StatsLogEventWrapper e =
- new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(temp.getType());
- e.writeString(temp.getName());
- e.writeInt((int) (temp.getValue() * 10));
- e.writeInt(temp.getStatus());
- pulledData.add(e);
- }
- } catch (RemoteException e) {
- // Should not happen.
- Slog.e(TAG, "Disconnected from thermal service. Cannot pull temperatures.");
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
- }
-
- private void pullCoolingDevices(int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- long callingToken = Binder.clearCallingIdentity();
- try {
- List<CoolingDevice> devices = sThermalService.getCurrentCoolingDevices();
- for (CoolingDevice device : devices) {
- StatsLogEventWrapper e =
- new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(device.getType());
- e.writeString(device.getName());
- e.writeInt((int) (device.getValue()));
- pulledData.add(e);
- }
- } catch (RemoteException e) {
- // Should not happen.
- Slog.e(TAG, "Disconnected from thermal service. Cannot pull temperatures.");
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
- }
-
- private void pullDebugElapsedClock(int tagId,
- long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
- final long elapsedMillis = SystemClock.elapsedRealtime();
- final long clockDiffMillis = mDebugElapsedClockPreviousValue == 0
- ? 0 : elapsedMillis - mDebugElapsedClockPreviousValue;
-
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeLong(mDebugElapsedClockPullCount);
- e.writeLong(elapsedMillis);
- // Log it twice to be able to test multi-value aggregation from ValueMetric.
- e.writeLong(elapsedMillis);
- e.writeLong(clockDiffMillis);
- e.writeInt(1 /* always set */);
- pulledData.add(e);
-
- if (mDebugElapsedClockPullCount % 2 == 1) {
- StatsLogEventWrapper e2 = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e2.writeLong(mDebugElapsedClockPullCount);
- e2.writeLong(elapsedMillis);
- // Log it twice to be able to test multi-value aggregation from ValueMetric.
- e2.writeLong(elapsedMillis);
- e2.writeLong(clockDiffMillis);
- e2.writeInt(2 /* set on odd pulls */);
- pulledData.add(e2);
- }
-
- mDebugElapsedClockPullCount++;
- mDebugElapsedClockPreviousValue = elapsedMillis;
- }
-
- private void pullDebugFailingElapsedClock(int tagId,
- long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- final long elapsedMillis = SystemClock.elapsedRealtime();
- // Fails every 5 buckets.
- if (mDebugFailingElapsedClockPullCount++ % 5 == 0) {
- mDebugFailingElapsedClockPreviousValue = elapsedMillis;
- throw new RuntimeException("Failing debug elapsed clock");
- }
-
- e.writeLong(mDebugFailingElapsedClockPullCount);
- e.writeLong(elapsedMillis);
- // Log it twice to be able to test multi-value aggregation from ValueMetric.
- e.writeLong(elapsedMillis);
- e.writeLong(mDebugFailingElapsedClockPreviousValue == 0
- ? 0 : elapsedMillis - mDebugFailingElapsedClockPreviousValue);
- mDebugFailingElapsedClockPreviousValue = elapsedMillis;
- pulledData.add(e);
- }
-
- private void pullDangerousPermissionState(long elapsedNanos, final long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- try {
- PackageManager pm = mContext.getPackageManager();
-
- List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
-
- int numUsers = users.size();
- for (int userNum = 0; userNum < numUsers; userNum++) {
- UserHandle user = users.get(userNum).getUserHandle();
-
- List<PackageInfo> pkgs = pm.getInstalledPackagesAsUser(
- PackageManager.GET_PERMISSIONS, user.getIdentifier());
-
- int numPkgs = pkgs.size();
- for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) {
- PackageInfo pkg = pkgs.get(pkgNum);
-
- if (pkg.requestedPermissions == null) {
- continue;
- }
-
- int numPerms = pkg.requestedPermissions.length;
- for (int permNum = 0; permNum < numPerms; permNum++) {
- String permName = pkg.requestedPermissions[permNum];
-
- PermissionInfo permissionInfo;
- int permissionFlags = 0;
- try {
- permissionInfo = pm.getPermissionInfo(permName, 0);
- permissionFlags =
- pm.getPermissionFlags(permName, pkg.packageName, user);
-
- } catch (PackageManager.NameNotFoundException ignored) {
- continue;
- }
-
- if (permissionInfo.getProtection() != PROTECTION_DANGEROUS) {
- continue;
- }
-
- StatsLogEventWrapper e = new StatsLogEventWrapper(
- StatsLog.DANGEROUS_PERMISSION_STATE, elapsedNanos, wallClockNanos);
-
- e.writeString(permName);
- e.writeInt(pkg.applicationInfo.uid);
- e.writeString(pkg.packageName);
- e.writeBoolean((pkg.requestedPermissionsFlags[permNum]
- & REQUESTED_PERMISSION_GRANTED) != 0);
- e.writeInt(permissionFlags);
-
- pulledData.add(e);
- }
- }
- }
- } catch (Throwable t) {
- Log.e(TAG, "Could not read permissions", t);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- private void pullAppOps(long elapsedNanos, final long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- try {
- AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
-
- CompletableFuture<HistoricalOps> ops = new CompletableFuture<>();
- HistoricalOpsRequest histOpsRequest =
- new HistoricalOpsRequest.Builder(
- Instant.now().minus(1, ChronoUnit.HOURS).toEpochMilli(),
- Long.MAX_VALUE).build();
- appOps.getHistoricalOps(histOpsRequest, mContext.getMainExecutor(), ops::complete);
-
- HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
- TimeUnit.MILLISECONDS);
-
- StatsLogEventWrapper e = new StatsLogEventWrapper(StatsLog.APP_OPS, elapsedNanos,
- wallClockNanos);
-
- for (int uidIdx = 0; uidIdx < histOps.getUidCount(); uidIdx++) {
- final HistoricalUidOps uidOps = histOps.getUidOpsAt(uidIdx);
- final int uid = uidOps.getUid();
- for (int pkgIdx = 0; pkgIdx < uidOps.getPackageCount(); pkgIdx++) {
- final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(pkgIdx);
- for (int opIdx = 0; opIdx < packageOps.getOpCount(); opIdx++) {
- final AppOpsManager.HistoricalOp op = packageOps.getOpAt(opIdx);
- e.writeInt(uid);
- e.writeString(packageOps.getPackageName());
- e.writeInt(op.getOpCode());
- e.writeLong(op.getForegroundAccessCount(OP_FLAGS_ALL_TRUSTED));
- e.writeLong(op.getBackgroundAccessCount(OP_FLAGS_ALL_TRUSTED));
- e.writeLong(op.getForegroundRejectCount(OP_FLAGS_ALL_TRUSTED));
- e.writeLong(op.getBackgroundRejectCount(OP_FLAGS_ALL_TRUSTED));
- e.writeLong(op.getForegroundAccessDuration(OP_FLAGS_ALL_TRUSTED));
- e.writeLong(op.getBackgroundAccessDuration(OP_FLAGS_ALL_TRUSTED));
- pulledData.add(e);
- }
- }
- }
- } catch (Throwable t) {
- Log.e(TAG, "Could not read appops", t);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
-
- /**
- * Add a RoleHolder atom for each package that holds a role.
- *
- * @param elapsedNanos the time since boot
- * @param wallClockNanos the time on the clock
- * @param pulledData the data sink to write to
- */
- private void pullRoleHolders(long elapsedNanos, final long wallClockNanos,
- @NonNull List<StatsLogEventWrapper> pulledData) {
- long callingToken = Binder.clearCallingIdentity();
- try {
- PackageManager pm = mContext.getPackageManager();
- RoleManagerInternal rmi = LocalServices.getService(RoleManagerInternal.class);
-
- List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
-
- int numUsers = users.size();
- for (int userNum = 0; userNum < numUsers; userNum++) {
- int userId = users.get(userNum).getUserHandle().getIdentifier();
-
- ArrayMap<String, ArraySet<String>> roles = rmi.getRolesAndHolders(
- userId);
-
- int numRoles = roles.size();
- for (int roleNum = 0; roleNum < numRoles; roleNum++) {
- String roleName = roles.keyAt(roleNum);
- ArraySet<String> holders = roles.valueAt(roleNum);
-
- int numHolders = holders.size();
- for (int holderNum = 0; holderNum < numHolders; holderNum++) {
- String holderName = holders.valueAt(holderNum);
-
- PackageInfo pkg;
- try {
- pkg = pm.getPackageInfoAsUser(holderName, 0, userId);
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Role holder " + holderName + " not found");
- return;
- }
-
- StatsLogEventWrapper e = new StatsLogEventWrapper(StatsLog.ROLE_HOLDER,
- elapsedNanos, wallClockNanos);
- e.writeInt(pkg.applicationInfo.uid);
- e.writeString(holderName);
- e.writeString(roleName);
- pulledData.add(e);
- }
- }
- }
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
- }
-
- private void pullTimeZoneDataInfo(int tagId,
- long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
- String tzDbVersion = "Unknown";
- try {
- tzDbVersion = android.icu.util.TimeZone.getTZDataVersion();
- } catch (Exception e) {
- Log.e(TAG, "Getting tzdb version failed: ", e);
- }
-
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeString(tzDbVersion);
- pulledData.add(e);
- }
-
- private void pullExternalStorageInfo(int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- StorageManager storageManager = mContext.getSystemService(StorageManager.class);
- if (storageManager != null) {
- List<VolumeInfo> volumes = storageManager.getVolumes();
- for (VolumeInfo vol : volumes) {
- final String envState = VolumeInfo.getEnvironmentForState(vol.getState());
- final DiskInfo diskInfo = vol.getDisk();
- if (diskInfo != null) {
- if (envState.equals(Environment.MEDIA_MOUNTED)) {
- // Get the type of the volume, if it is adoptable or portable.
- int volumeType = StatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__OTHER;
- if (vol.getType() == TYPE_PUBLIC) {
- volumeType = StatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__PUBLIC;
- } else if (vol.getType() == TYPE_PRIVATE) {
- volumeType = StatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__PRIVATE;
- }
- // Get the type of external storage inserted in the device (sd cards,
- // usb, etc)
- int externalStorageType;
- if (diskInfo.isSd()) {
- externalStorageType = StorageEnums.SD_CARD;
- } else if (diskInfo.isUsb()) {
- externalStorageType = StorageEnums.USB;
- } else {
- externalStorageType = StorageEnums.OTHER;
- }
- StatsLogEventWrapper e =
- new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(externalStorageType);
- e.writeInt(volumeType);
- e.writeLong(diskInfo.size);
- pulledData.add(e);
- }
- }
- }
- }
- }
-
- private void pullAppsOnExternalStorageInfo(int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- PackageManager pm = mContext.getPackageManager();
- StorageManager storage = mContext.getSystemService(StorageManager.class);
- List<ApplicationInfo> apps = pm.getInstalledApplications(/* flags = */ 0);
- for (ApplicationInfo appInfo : apps) {
- UUID storageUuid = appInfo.storageUuid;
- if (storageUuid != null) {
- VolumeInfo volumeInfo = storage.findVolumeByUuid(appInfo.storageUuid.toString());
- if (volumeInfo != null) {
- DiskInfo diskInfo = volumeInfo.getDisk();
- if (diskInfo != null) {
- int externalStorageType = -1;
- if (diskInfo.isSd()) {
- externalStorageType = StorageEnums.SD_CARD;
- } else if (diskInfo.isUsb()) {
- externalStorageType = StorageEnums.USB;
- } else if (appInfo.isExternal()) {
- externalStorageType = StorageEnums.OTHER;
- }
- // App is installed on external storage.
- if (externalStorageType != -1) {
- StatsLogEventWrapper e =
- new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(externalStorageType);
- e.writeString(appInfo.packageName);
- pulledData.add(e);
- }
- }
- }
- }
- }
- }
-
- private void pullFaceSettings(int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- long callingToken = Binder.clearCallingIdentity();
- try {
- List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
- int numUsers = users.size();
- for (int userNum = 0; userNum < numUsers; userNum++) {
- int userId = users.get(userNum).getUserHandle().getIdentifier();
-
- StatsLogEventWrapper e =
- new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeBoolean(Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED, 1,
- userId) != 0);
- e.writeBoolean(Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD,
- 0, userId) != 0);
- e.writeBoolean(Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED, 1,
- userId) != 0);
- e.writeBoolean(Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.FACE_UNLOCK_APP_ENABLED, 1,
- userId) != 0);
- e.writeBoolean(Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, 0,
- userId) != 0);
- e.writeBoolean(Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.FACE_UNLOCK_DIVERSITY_REQUIRED, 1,
- userId) != 0);
-
- pulledData.add(e);
- }
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
- }
-
- /**
- * Pulls various data.
- */
- @Override // Binder call
- public StatsLogEventWrapper[] pullData(int tagId) {
- enforceCallingPermission();
- if (DEBUG) {
- Slog.d(TAG, "Pulling " + tagId);
- }
- List<StatsLogEventWrapper> ret = new ArrayList<>();
- long elapsedNanos = SystemClock.elapsedRealtimeNanos();
- long wallClockNanos = SystemClock.currentTimeMicro() * 1000L;
- switch (tagId) {
- case StatsLog.WIFI_BYTES_TRANSFER: {
- pullWifiBytesTransfer(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.MOBILE_BYTES_TRANSFER: {
- pullMobileBytesTransfer(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: {
- pullWifiBytesTransferByFgBg(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: {
- pullMobileBytesTransferByFgBg(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.BLUETOOTH_BYTES_TRANSFER: {
- pullBluetoothBytesTransfer(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.KERNEL_WAKELOCK: {
- pullKernelWakelock(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.CPU_TIME_PER_FREQ: {
- pullCpuTimePerFreq(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.CPU_TIME_PER_UID: {
- pullKernelUidCpuTime(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.CPU_TIME_PER_UID_FREQ: {
- pullKernelUidCpuFreqTime(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.CPU_CLUSTER_TIME: {
- pullKernelUidCpuClusterTime(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.CPU_ACTIVE_TIME: {
- pullKernelUidCpuActiveTime(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.WIFI_ACTIVITY_INFO: {
- pullWifiActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.MODEM_ACTIVITY_INFO: {
- pullModemActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.BLUETOOTH_ACTIVITY_INFO: {
- pullBluetoothActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.SYSTEM_UPTIME: {
- pullSystemUpTime(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.SYSTEM_ELAPSED_REALTIME: {
- pullSystemElapsedRealtime(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.PROCESS_MEMORY_STATE: {
- pullProcessMemoryState(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.NATIVE_PROCESS_MEMORY_STATE: {
- pullNativeProcessMemoryState(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.PROCESS_MEMORY_HIGH_WATER_MARK: {
- pullProcessMemoryHighWaterMark(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.SYSTEM_ION_HEAP_SIZE: {
- pullSystemIonHeapSize(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.PROCESS_SYSTEM_ION_HEAP_SIZE: {
- pullProcessSystemIonHeapSize(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.BINDER_CALLS: {
- pullBinderCallsStats(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.BINDER_CALLS_EXCEPTIONS: {
- pullBinderCallsStatsExceptions(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.LOOPER_STATS: {
- pullLooperStats(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.DISK_STATS: {
- pullDiskStats(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.DIRECTORY_USAGE: {
- pullDirectoryUsage(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.APP_SIZE: {
- pullAppSize(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.CATEGORY_SIZE: {
- pullCategorySize(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.NUM_FINGERPRINTS_ENROLLED: {
- pullNumBiometricsEnrolled(BiometricsProtoEnums.MODALITY_FINGERPRINT, tagId,
- elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.NUM_FACES_ENROLLED: {
- pullNumBiometricsEnrolled(BiometricsProtoEnums.MODALITY_FACE, tagId, elapsedNanos,
- wallClockNanos, ret);
- break;
- }
- case StatsLog.PROC_STATS: {
- pullProcessStats(ProcessStats.REPORT_ALL, tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.PROC_STATS_PKG_PROC: {
- pullProcessStats(ProcessStats.REPORT_PKG_PROC_STATS, tagId, elapsedNanos,
- wallClockNanos, ret);
- break;
- }
- case StatsLog.DISK_IO: {
- pullDiskIo(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.POWER_PROFILE: {
- pullPowerProfile(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.BUILD_INFORMATION: {
- pullBuildInformation(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.PROCESS_CPU_TIME: {
- pullProcessCpuTime(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.CPU_TIME_PER_THREAD_FREQ: {
- pullCpuTimePerThreadFreq(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.DEVICE_CALCULATED_POWER_USE: {
- pullDeviceCalculatedPowerUse(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.DEVICE_CALCULATED_POWER_BLAME_UID: {
- pullDeviceCalculatedPowerBlameUid(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER: {
- pullDeviceCalculatedPowerBlameOther(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.TEMPERATURE: {
- pullTemperature(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.COOLING_DEVICE: {
- pullCoolingDevices(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.DEBUG_ELAPSED_CLOCK: {
- pullDebugElapsedClock(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.DEBUG_FAILING_ELAPSED_CLOCK: {
- pullDebugFailingElapsedClock(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.ROLE_HOLDER: {
- pullRoleHolders(elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.DANGEROUS_PERMISSION_STATE: {
- pullDangerousPermissionState(elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.TIME_ZONE_DATA_INFO: {
- pullTimeZoneDataInfo(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.EXTERNAL_STORAGE_INFO: {
- pullExternalStorageInfo(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.APPS_ON_EXTERNAL_STORAGE_INFO: {
- pullAppsOnExternalStorageInfo(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.FACE_SETTINGS: {
- pullFaceSettings(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
- case StatsLog.APP_OPS: {
- pullAppOps(elapsedNanos, wallClockNanos, ret);
- break;
- }
- default:
- Slog.w(TAG, "No such tagId data as " + tagId);
- return null;
- }
- return ret.toArray(new StatsLogEventWrapper[ret.size()]);
- }
-
- @Override // Binder call
- public void statsdReady() {
- enforceCallingPermission();
- if (DEBUG) {
- Slog.d(TAG, "learned that statsdReady");
- }
- sayHiToStatsd(); // tell statsd that we're ready too and link to it
- mContext.sendBroadcastAsUser(new Intent(StatsManager.ACTION_STATSD_STARTED)
- .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND),
- UserHandle.SYSTEM, android.Manifest.permission.DUMP);
- }
-
- @Override
- public void triggerUidSnapshot() {
- enforceCallingPermission();
- synchronized (sStatsdLock) {
- final long token = Binder.clearCallingIdentity();
- try {
- informAllUidsLocked(mContext);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to trigger uid snapshot.", e);
- } finally {
- restoreCallingIdentity(token);
- }
- }
- }
-
- private void enforceCallingPermission() {
- if (Binder.getCallingPid() == Process.myPid()) {
- return;
- }
- mContext.enforceCallingPermission(android.Manifest.permission.STATSCOMPANION, null);
- }
-
- // Lifecycle and related code
-
- /**
- * Fetches the statsd IBinder service
- */
- private static IStatsManager fetchStatsdService() {
- return IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
- }
-
- public static final class Lifecycle extends SystemService {
- private StatsCompanionService mStatsCompanionService;
-
- public Lifecycle(Context context) {
- super(context);
- }
-
- @Override
- public void onStart() {
- mStatsCompanionService = new StatsCompanionService(getContext());
- try {
- publishBinderService(Context.STATS_COMPANION_SERVICE,
- mStatsCompanionService);
- if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_COMPANION_SERVICE);
- } catch (Exception e) {
- Slog.e(TAG, "Failed to publishBinderService", e);
- }
- }
-
- @Override
- public void onBootPhase(int phase) {
- super.onBootPhase(phase);
- if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
- mStatsCompanionService.systemReady();
- }
- }
- }
-
- /**
- * Now that the android system is ready, StatsCompanion is ready too, so inform statsd.
- */
- private void systemReady() {
- if (DEBUG) Slog.d(TAG, "Learned that systemReady");
- sayHiToStatsd();
- }
-
- /**
- * Tells statsd that statscompanion is ready. If the binder call returns, link to
- * statsd.
- */
- private void sayHiToStatsd() {
- synchronized (sStatsdLock) {
- if (sStatsd != null) {
- Slog.e(TAG, "Trying to fetch statsd, but it was already fetched",
- new IllegalStateException(
- "sStatsd is not null when being fetched"));
- return;
- }
- sStatsd = fetchStatsdService();
- if (sStatsd == null) {
- Slog.i(TAG,
- "Could not yet find statsd to tell it that StatsCompanion is "
- + "alive.");
- return;
- }
- if (DEBUG) Slog.d(TAG, "Saying hi to statsd");
- try {
- sStatsd.statsCompanionReady();
- // If the statsCompanionReady two-way binder call returns, link to statsd.
- try {
- sStatsd.asBinder().linkToDeath(new StatsdDeathRecipient(), 0);
- } catch (RemoteException e) {
- Slog.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e);
- forgetEverythingLocked();
- }
- // Setup broadcast receiver for updates.
- IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REPLACED);
- filter.addAction(Intent.ACTION_PACKAGE_ADDED);
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addDataScheme("package");
- mContext.registerReceiverAsUser(mAppUpdateReceiver, UserHandle.ALL, filter,
- null,
- null);
-
- // Setup receiver for user initialize (which happens once for a new user)
- // and
- // if a user is removed.
- filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE);
- filter.addAction(Intent.ACTION_USER_REMOVED);
- mContext.registerReceiverAsUser(mUserUpdateReceiver, UserHandle.ALL,
- filter, null, null);
-
- // Setup receiver for device reboots or shutdowns.
- filter = new IntentFilter(Intent.ACTION_REBOOT);
- filter.addAction(Intent.ACTION_SHUTDOWN);
- mContext.registerReceiverAsUser(
- mShutdownEventReceiver, UserHandle.ALL, filter, null, null);
- final long token = Binder.clearCallingIdentity();
- try {
- // Pull the latest state of UID->app name, version mapping when
- // statsd starts.
- informAllUidsLocked(mContext);
- } finally {
- restoreCallingIdentity(token);
- }
- Slog.i(TAG, "Told statsd that StatsCompanionService is alive.");
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
- forgetEverythingLocked();
- }
- }
- }
-
- private class StatsdDeathRecipient implements IBinder.DeathRecipient {
- @Override
- public void binderDied() {
- Slog.i(TAG, "Statsd is dead - erase all my knowledge.");
- synchronized (sStatsdLock) {
- long now = SystemClock.elapsedRealtime();
- for (Long timeMillis : mDeathTimeMillis) {
- long ageMillis = now - timeMillis;
- if (ageMillis > MILLIS_IN_A_DAY) {
- mDeathTimeMillis.remove(timeMillis);
- }
- }
- for (Long timeMillis : mDeletedFiles.keySet()) {
- long ageMillis = now - timeMillis;
- if (ageMillis > MILLIS_IN_A_DAY * 7) {
- mDeletedFiles.remove(timeMillis);
- }
- }
- mDeathTimeMillis.add(now);
- if (mDeathTimeMillis.size() >= DEATH_THRESHOLD) {
- mDeathTimeMillis.clear();
- File[] configs = FileUtils.listFilesOrEmpty(new File(CONFIG_DIR));
- if (configs.length > 0) {
- String fileName = configs[0].getName();
- if (configs[0].delete()) {
- mDeletedFiles.put(now, fileName);
- }
- }
- }
- forgetEverythingLocked();
- }
- }
- }
-
- @GuardedBy("StatsCompanionService.sStatsdLock")
- private void forgetEverythingLocked() {
- sStatsd = null;
- mContext.unregisterReceiver(mAppUpdateReceiver);
- mContext.unregisterReceiver(mUserUpdateReceiver);
- mContext.unregisterReceiver(mShutdownEventReceiver);
- cancelAnomalyAlarm();
- cancelPullingAlarm();
-
- BinderCallsStatsService.Internal binderStats =
- LocalServices.getService(BinderCallsStatsService.Internal.class);
- if (binderStats != null) {
- binderStats.reset();
- }
-
- LooperStats looperStats = LocalServices.getService(LooperStats.class);
- if (looperStats != null) {
- looperStats.reset();
- }
- }
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
-
- synchronized (sStatsdLock) {
- writer.println(
- "Number of configuration files deleted: " + mDeletedFiles.size());
- if (mDeletedFiles.size() > 0) {
- writer.println(" timestamp, deleted file name");
- }
- long lastBootMillis =
- SystemClock.currentThreadTimeMillis() - SystemClock.elapsedRealtime();
- for (Long elapsedMillis : mDeletedFiles.keySet()) {
- long deletionMillis = lastBootMillis + elapsedMillis;
- writer.println(
- " " + deletionMillis + ", " + mDeletedFiles.get(elapsedMillis));
- }
- }
- }
-
- // Thermal event received from vendor thermal management subsystem
- private static final class ThermalEventListener extends IThermalEventListener.Stub {
- @Override
- public void notifyThrottling(Temperature temp) {
- StatsLog.write(StatsLog.THERMAL_THROTTLING_SEVERITY_STATE_CHANGED, temp.getType(),
- temp.getName(), (int) (temp.getValue() * 10), temp.getStatus());
- }
- }
-
- private static final class ConnectivityStatsCallback extends
- ConnectivityManager.NetworkCallback {
- @Override
- public void onAvailable(Network network) {
- StatsLog.write(StatsLog.CONNECTIVITY_STATE_CHANGED, network.netId,
- StatsLog.CONNECTIVITY_STATE_CHANGED__STATE__CONNECTED);
- }
-
- @Override
- public void onLost(Network network) {
- StatsLog.write(StatsLog.CONNECTIVITY_STATE_CHANGED, network.netId,
- StatsLog.CONNECTIVITY_STATE_CHANGED__STATE__DISCONNECTED);
- }
- }
-}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index a93d2b8b6fb9..ec64ee6d52de 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -18,7 +18,10 @@ package com.android.server.statusbar;
import android.graphics.Rect;
import android.os.Bundle;
+import android.view.InsetsState.InternalInsetType;
+import android.view.WindowInsetsController.Appearance;
+import com.android.internal.view.AppearanceRegion;
import com.android.server.notification.NotificationDelegate;
public interface StatusBarManagerInternal {
@@ -75,7 +78,7 @@ public interface StatusBarManagerInternal {
void startAssist(Bundle args);
void onCameraLaunchGestureDetected(int source);
- void topAppWindowChanged(int displayId, boolean menuVisible);
+ void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive);
void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis,
int mask, Rect fullscreenBounds, Rect dockedBounds, boolean isNavbarColorManagedByIme,
String cause);
@@ -113,4 +116,14 @@ public interface StatusBarManagerInternal {
* Notifies System UI whether the recents animation is running.
*/
void onRecentsAnimationStateChanged(boolean running);
+
+ /** @see com.android.internal.statusbar.IStatusBar#onSystemBarAppearanceChanged */
+ void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance,
+ AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme);
+
+ /** @see com.android.internal.statusbar.IStatusBar#showTransient */
+ void showTransient(int displayId, @InternalInsetType int[] types);
+
+ /** @see com.android.internal.statusbar.IStatusBar#abortTransient */
+ void abortTransient(int displayId, @InternalInsetType int[] types);
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 8897eca85d7a..489c34359645 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -29,6 +29,7 @@ import android.graphics.Rect;
import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
+import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -46,6 +47,7 @@ import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.WindowInsetsController.Appearance;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -55,6 +57,7 @@ import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.statusbar.RegisterStatusBarResult;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.DumpUtils;
+import com.android.internal.view.AppearanceRegion;
import com.android.server.LocalServices;
import com.android.server.notification.NotificationDelegate;
import com.android.server.policy.GlobalActionsProvider;
@@ -255,8 +258,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
@Override
- public void topAppWindowChanged(int displayId, boolean menuVisible) {
- StatusBarManagerService.this.topAppWindowChanged(displayId, menuVisible);
+ public void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) {
+ StatusBarManagerService.this.topAppWindowChanged(displayId, isFullscreen, isImmersive);
}
@Override
@@ -466,6 +469,36 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
}
+
+ @Override
+ public void onSystemBarAppearanceChanged(int displayId, @Appearance int appearance,
+ AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme) {
+ // TODO (b/118118435): save the information to UiState
+ if (mBar != null) {
+ try {
+ mBar.onSystemBarAppearanceChanged(displayId, appearance, appearanceRegions,
+ navbarColorManagedByIme);
+ } catch (RemoteException ex) { }
+ }
+ }
+
+ @Override
+ public void showTransient(int displayId, int[] types) {
+ if (mBar != null) {
+ try {
+ mBar.showTransient(displayId, types);
+ } catch (RemoteException ex) { }
+ }
+ }
+
+ @Override
+ public void abortTransient(int displayId, int[] types) {
+ if (mBar != null) {
+ try {
+ mBar.abortTransient(displayId, types);
+ } catch (RemoteException ex) { }
+ }
+ }
};
private final GlobalActionsProvider mGlobalActionsProvider = new GlobalActionsProvider() {
@@ -609,24 +642,24 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
@Override
- public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
- int type, boolean requireConfirmation, int userId, String opPackageName) {
+ public void showAuthenticationDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
+ int biometricModality, boolean requireConfirmation, int userId, String opPackageName) {
enforceBiometricDialog();
if (mBar != null) {
try {
- mBar.showBiometricDialog(bundle, receiver, type, requireConfirmation, userId,
- opPackageName);
+ mBar.showAuthenticationDialog(bundle, receiver, biometricModality,
+ requireConfirmation, userId, opPackageName);
} catch (RemoteException ex) {
}
}
}
@Override
- public void onBiometricAuthenticated(boolean authenticated, String failureReason) {
+ public void onBiometricAuthenticated() {
enforceBiometricDialog();
if (mBar != null) {
try {
- mBar.onBiometricAuthenticated(authenticated, failureReason);
+ mBar.onBiometricAuthenticated();
} catch (RemoteException ex) {
}
}
@@ -644,22 +677,22 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
@Override
- public void onBiometricError(String error) {
+ public void onBiometricError(int modality, int error, int vendorCode) {
enforceBiometricDialog();
if (mBar != null) {
try {
- mBar.onBiometricError(error);
+ mBar.onBiometricError(modality, error, vendorCode);
} catch (RemoteException ex) {
}
}
}
@Override
- public void hideBiometricDialog() {
+ public void hideAuthenticationDialog() {
enforceBiometricDialog();
if (mBar != null) {
try {
- mBar.hideBiometricDialog();
+ mBar.hideAuthenticationDialog();
} catch (RemoteException ex) {
}
}
@@ -816,23 +849,19 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
/**
- * Hide or show the on-screen Menu key. Only call this from the window manager, typically in
- * response to a window with {@link android.view.WindowManager.LayoutParams#needsMenuKey} set
- * to {@link android.view.WindowManager.LayoutParams#NEEDS_MENU_SET_TRUE}.
+ * Enables System UI to know whether the top app is fullscreen or not, and whether this app is
+ * in immersive mode or not.
*/
- private void topAppWindowChanged(int displayId, final boolean menuVisible) {
+ private void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) {
enforceStatusBar();
- if (SPEW) {
- Slog.d(TAG, "display#" + displayId + ": "
- + (menuVisible ? "showing" : "hiding") + " MENU key");
- }
synchronized(mLock) {
- getUiState(displayId).setMenuVisible(menuVisible);
+ getUiState(displayId).setFullscreen(isFullscreen);
+ getUiState(displayId).setImmersive(isImmersive);
mHandler.post(() -> {
if (mBar != null) {
try {
- mBar.topAppWindowChanged(displayId, menuVisible);
+ mBar.topAppWindowChanged(displayId, isFullscreen, isImmersive);
} catch (RemoteException ex) {
}
}
@@ -941,7 +970,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
private int mDockedStackSysUiVisibility = 0;
private final Rect mFullscreenStackBounds = new Rect();
private final Rect mDockedStackBounds = new Rect();
- private boolean mMenuVisible = false;
+ private boolean mFullscreen = false;
+ private boolean mImmersive = false;
private int mDisabled1 = 0;
private int mDisabled2 = 0;
private int mImeWindowVis = 0;
@@ -963,12 +993,12 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
mDisabled2 = disabled2;
}
- private boolean isMenuVisible() {
- return mMenuVisible;
+ private void setFullscreen(boolean isFullscreen) {
+ mFullscreen = isFullscreen;
}
- private void setMenuVisible(boolean menuVisible) {
- mMenuVisible = menuVisible;
+ private void setImmersive(boolean immersive) {
+ mImmersive = immersive;
}
private boolean disableEquals(int disabled1, int disabled2) {
@@ -1055,12 +1085,12 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
// Make it aware of multi-display if needed.
final UiState state = mDisplayUiState.get(DEFAULT_DISPLAY);
return new RegisterStatusBarResult(icons, gatherDisableActionsLocked(mCurrentUserId, 1),
- state.mSystemUiVisibility, state.mMenuVisible, state.mImeWindowVis,
+ state.mSystemUiVisibility, state.mImeWindowVis,
state.mImeBackDisposition, state.mShowImeSwitcher,
gatherDisableActionsLocked(mCurrentUserId, 2),
state.mFullscreenStackSysUiVisibility, state.mDockedStackSysUiVisibility,
state.mImeToken, state.mFullscreenStackBounds, state.mDockedStackBounds,
- state.mNavbarColorManagedByIme);
+ state.mNavbarColorManagedByIme, state.mFullscreen, state.mImmersive);
}
}
@@ -1334,6 +1364,18 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
@Override
+ public void grantInlineReplyUriPermission(String key, Uri uri) {
+ enforceStatusBarService();
+ int callingUid = Binder.getCallingUid();
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mNotificationDelegate.grantInlineReplyUriPermission(key, uri, callingUid);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
(new StatusBarShellCommand(this, mContext)).exec(
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 4c935643da55..5493afd1b123 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -365,10 +365,16 @@ public final class TextClassificationManagerService extends ITextClassifierServi
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, fout)) return;
IndentingPrintWriter pw = new IndentingPrintWriter(fout, " ");
- TextClassificationManager tcm = mContext.getSystemService(TextClassificationManager.class);
- tcm.dump(pw);
- pw.printPair("context", mContext); pw.println();
+ // Create a TCM instance with the system server identity. TCM creates a ContentObserver
+ // to listen for settings changes. It does not pass the checkContentProviderAccess check
+ // if we are using the shell identity, because AMS does not track of processes spawn from
+ // shell.
+ Binder.withCleanCallingIdentity(
+ () -> mContext.getSystemService(TextClassificationManager.class).dump(pw));
+
+ pw.printPair("context", mContext);
+ pw.println();
synchronized (mLock) {
int size = mUserStates.size();
pw.print("Number user states: "); pw.println(size);
diff --git a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
index 7bdc8a32815a..9dbbf16e7734 100644
--- a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
@@ -19,7 +19,7 @@ package com.android.server.timedetector;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AlarmManager;
-import android.app.timedetector.TimeSignal;
+import android.app.timedetector.PhoneTimeSuggestion;
import android.content.Intent;
import android.util.Slog;
import android.util.TimestampedValue;
@@ -48,9 +48,8 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy {
// @NonNull after initialize()
private Callback mCallback;
- // NITZ state.
- @Nullable private TimestampedValue<Long> mLastNitzTime;
-
+ // Last phone suggestion.
+ @Nullable private PhoneTimeSuggestion mLastPhoneSuggestion;
// Information about the last time signal received: Used when toggling auto-time.
@Nullable private TimestampedValue<Long> mLastSystemClockTime;
@@ -65,46 +64,40 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy {
}
@Override
- public void suggestTime(@NonNull TimeSignal timeSignal) {
- if (!TimeSignal.SOURCE_ID_NITZ.equals(timeSignal.getSourceId())) {
- Slog.w(TAG, "Ignoring signal from unsupported source: " + timeSignal);
- return;
- }
-
+ public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
// NITZ logic
- TimestampedValue<Long> newNitzUtcTime = timeSignal.getUtcTime();
- boolean nitzTimeIsValid = validateNewNitzTime(newNitzUtcTime, mLastNitzTime);
- if (!nitzTimeIsValid) {
+ boolean timeSuggestionIsValid =
+ validateNewPhoneSuggestion(timeSuggestion, mLastPhoneSuggestion);
+ if (!timeSuggestionIsValid) {
return;
}
// Always store the last NITZ value received, regardless of whether we go on to use it to
// update the system clock. This is so that we can validate future NITZ signals.
- mLastNitzTime = newNitzUtcTime;
+ mLastPhoneSuggestion = timeSuggestion;
// System clock update logic.
// Historically, Android has sent a telephony broadcast only when setting the time using
// NITZ.
- final boolean sendNetworkBroadcast =
- TimeSignal.SOURCE_ID_NITZ.equals(timeSignal.getSourceId());
+ final boolean sendNetworkBroadcast = true;
- final TimestampedValue<Long> newUtcTime = newNitzUtcTime;
+ final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime();
setSystemClockIfRequired(newUtcTime, sendNetworkBroadcast);
}
- private static boolean validateNewNitzTime(TimestampedValue<Long> newNitzUtcTime,
- TimestampedValue<Long> lastNitzTime) {
+ private static boolean validateNewPhoneSuggestion(@NonNull PhoneTimeSuggestion newSuggestion,
+ @Nullable PhoneTimeSuggestion lastSuggestion) {
- if (lastNitzTime != null) {
- long referenceTimeDifference =
- TimestampedValue.referenceTimeDifference(newNitzUtcTime, lastNitzTime);
+ if (lastSuggestion != null) {
+ long referenceTimeDifference = TimestampedValue.referenceTimeDifference(
+ newSuggestion.getUtcTime(), lastSuggestion.getUtcTime());
if (referenceTimeDifference < 0 || referenceTimeDifference > Integer.MAX_VALUE) {
// Out of order or bogus.
Slog.w(TAG, "validateNewNitzTime: Bad NITZ signal received."
+ " referenceTimeDifference=" + referenceTimeDifference
- + " lastNitzTime=" + lastNitzTime
- + " newNitzUtcTime=" + newNitzUtcTime);
+ + " lastSuggestion=" + lastSuggestion
+ + " newSuggestion=" + newSuggestion);
return false;
}
}
@@ -182,7 +175,7 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy {
@Override
public void dump(@NonNull PrintWriter pw, @Nullable String[] args) {
- pw.println("mLastNitzTime=" + mLastNitzTime);
+ pw.println("mLastPhoneSuggestion=" + mLastPhoneSuggestion);
pw.println("mLastSystemClockTimeSet=" + mLastSystemClockTimeSet);
pw.println("mLastSystemClockTime=" + mLastSystemClockTime);
pw.println("mLastSystemClockTimeSendNetworkBroadcast="
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 9c830003cab4..ee42279f7d50 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -19,7 +19,7 @@ package com.android.server.timedetector;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.timedetector.ITimeDetectorService;
-import android.app.timedetector.TimeSignal;
+import android.app.timedetector.PhoneTimeSuggestion;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
@@ -96,14 +96,14 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub {
}
@Override
- public void suggestTime(@NonNull TimeSignal timeSignal) {
+ public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSignal) {
enforceSetTimePermission();
Objects.requireNonNull(timeSignal);
long idToken = Binder.clearCallingIdentity();
try {
synchronized (mStrategyLock) {
- mTimeDetectorStrategy.suggestTime(timeSignal);
+ mTimeDetectorStrategy.suggestPhoneTime(timeSignal);
}
} finally {
Binder.restoreCallingIdentity(idToken);
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index e050865d96c7..7c2a945854f5 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -18,7 +18,7 @@ package com.android.server.timedetector;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.timedetector.TimeSignal;
+import android.app.timedetector.PhoneTimeSuggestion;
import android.content.Intent;
import android.util.TimestampedValue;
@@ -72,7 +72,7 @@ public interface TimeDetectorStrategy {
void initialize(@NonNull Callback callback);
/** Process the suggested time. */
- void suggestTime(@NonNull TimeSignal timeSignal);
+ void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion);
/** Handle the auto-time setting being toggled on or off. */
void handleAutoTimeDetectionToggle(boolean enabled);
diff --git a/services/core/java/com/android/server/tv/TvRemoteProviderProxy.java b/services/core/java/com/android/server/tv/TvRemoteProviderProxy.java
index f8ffb7c1c0e2..b7bc77dc97ee 100644
--- a/services/core/java/com/android/server/tv/TvRemoteProviderProxy.java
+++ b/services/core/java/com/android/server/tv/TvRemoteProviderProxy.java
@@ -21,8 +21,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.media.tv.ITvRemoteProvider;
-import android.media.tv.ITvRemoteServiceInput;
-import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -30,44 +28,33 @@ import android.util.Log;
import android.util.Slog;
import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
/**
* Maintains a connection to a tv remote provider service.
*/
final class TvRemoteProviderProxy implements ServiceConnection {
- private static final String TAG = "TvRemoteProvProxy"; // max. 23 chars
+ private static final String TAG = "TvRemoteProviderProxy";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.VERBOSE);
- private static final boolean DEBUG_KEY = false;
// This should match TvRemoteProvider.ACTION_TV_REMOTE_PROVIDER
protected static final String SERVICE_INTERFACE =
"com.android.media.tv.remoteprovider.TvRemoteProvider";
private final Context mContext;
+ private final Object mLock;
private final ComponentName mComponentName;
private final int mUserId;
private final int mUid;
- /**
- * State guarded by mLock.
- * This is the first lock in sequence for an incoming call.
- * The second lock is always {@link TvRemoteService#mLock}
- *
- * There are currently no methods that break this sequence.
- */
- private final Object mLock = new Object();
-
- private ProviderMethods mProviderMethods;
- // Connection state
+ // State changes happen only in the main thread, hence no lock is needed
private boolean mRunning;
private boolean mBound;
- private Connection mActiveConnection;
+ private boolean mConnected;
- TvRemoteProviderProxy(Context context, ProviderMethods provider,
+ TvRemoteProviderProxy(Context context, Object lock,
ComponentName componentName, int userId, int uid) {
mContext = context;
- mProviderMethods = provider;
+ mLock = lock;
mComponentName = componentName;
mUserId = userId;
mUid = uid;
@@ -78,7 +65,7 @@ final class TvRemoteProviderProxy implements ServiceConnection {
pw.println(prefix + " mUserId=" + mUserId);
pw.println(prefix + " mRunning=" + mRunning);
pw.println(prefix + " mBound=" + mBound);
- pw.println(prefix + " mActiveConnection=" + mActiveConnection);
+ pw.println(prefix + " mConnected=" + mConnected);
}
public boolean hasComponentName(String packageName, String className) {
@@ -109,11 +96,9 @@ final class TvRemoteProviderProxy implements ServiceConnection {
}
public void rebindIfDisconnected() {
- synchronized (mLock) {
- if (mActiveConnection == null && mRunning) {
- unbind();
- bind();
- }
+ if (mRunning && !mConnected) {
+ unbind();
+ bind();
}
}
@@ -129,7 +114,7 @@ final class TvRemoteProviderProxy implements ServiceConnection {
mBound = mContext.bindServiceAsUser(service, this,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
new UserHandle(mUserId));
- if (!mBound && DEBUG) {
+ if (DEBUG && !mBound) {
Slog.d(TAG, this + ": Bind failed");
}
} catch (SecurityException ex) {
@@ -147,7 +132,6 @@ final class TvRemoteProviderProxy implements ServiceConnection {
}
mBound = false;
- disconnect();
mContext.unbindService(this);
}
}
@@ -158,392 +142,27 @@ final class TvRemoteProviderProxy implements ServiceConnection {
Slog.d(TAG, this + ": onServiceConnected()");
}
- if (mBound) {
- disconnect();
+ mConnected = true;
- ITvRemoteProvider provider = ITvRemoteProvider.Stub.asInterface(service);
- if (provider != null) {
- Connection connection = new Connection(provider);
- if (connection.register()) {
- synchronized (mLock) {
- mActiveConnection = connection;
- }
- if (DEBUG) {
- Slog.d(TAG, this + ": Connected successfully.");
- }
- } else {
- if (DEBUG) {
- Slog.d(TAG, this + ": Registration failed");
- }
- }
- } else {
- Slog.e(TAG, this + ": Service returned invalid remote-control provider binder");
- }
+ final ITvRemoteProvider provider = ITvRemoteProvider.Stub.asInterface(service);
+ if (provider == null) {
+ Slog.e(TAG, this + ": Invalid binder");
+ return;
}
- }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- if (DEBUG) Slog.d(TAG, this + ": Service disconnected");
- disconnect();
- }
-
- private void disconnect() {
- synchronized (mLock) {
- if (mActiveConnection != null) {
- mActiveConnection.dispose();
- mActiveConnection = null;
- }
+ try {
+ provider.setRemoteServiceInputSink(new TvRemoteServiceInput(mLock, provider));
+ } catch (RemoteException e) {
+ Slog.e(TAG, this + ": Failed remote call to setRemoteServiceInputSink");
}
}
- interface ProviderMethods {
- // InputBridge
- boolean openInputBridge(TvRemoteProviderProxy provider, IBinder token, String name,
- int width, int height, int maxPointers);
-
- void closeInputBridge(TvRemoteProviderProxy provider, IBinder token);
-
- void clearInputBridge(TvRemoteProviderProxy provider, IBinder token);
-
- void sendKeyDown(TvRemoteProviderProxy provider, IBinder token, int keyCode);
-
- void sendKeyUp(TvRemoteProviderProxy provider, IBinder token, int keyCode);
-
- void sendPointerDown(TvRemoteProviderProxy provider, IBinder token, int pointerId, int x,
- int y);
-
- void sendPointerUp(TvRemoteProviderProxy provider, IBinder token, int pointerId);
-
- void sendPointerSync(TvRemoteProviderProxy provider, IBinder token);
- }
-
- private final class Connection {
- private final ITvRemoteProvider mTvRemoteProvider;
- private final RemoteServiceInputProvider mServiceInputProvider;
-
- public Connection(ITvRemoteProvider provider) {
- mTvRemoteProvider = provider;
- mServiceInputProvider = new RemoteServiceInputProvider(this);
- }
-
- public boolean register() {
- if (DEBUG) Slog.d(TAG, "Connection::register()");
- try {
- mTvRemoteProvider.setRemoteServiceInputSink(mServiceInputProvider);
- return true;
- } catch (RemoteException ex) {
- dispose();
- return false;
- }
- }
-
- public void dispose() {
- if (DEBUG) Slog.d(TAG, "Connection::dispose()");
- mServiceInputProvider.dispose();
- }
-
-
- public void onInputBridgeConnected(IBinder token) {
- if (DEBUG) Slog.d(TAG, this + ": onInputBridgeConnected");
- try {
- mTvRemoteProvider.onInputBridgeConnected(token);
- } catch (RemoteException ex) {
- Slog.e(TAG, "Failed to deliver onInputBridgeConnected. ", ex);
- }
- }
-
- void openInputBridge(final IBinder token, final String name, final int width,
- final int height, final int maxPointers) {
- synchronized (mLock) {
- if (mActiveConnection == this && Binder.getCallingUid() == mUid) {
- if (DEBUG) {
- Slog.d(TAG, this + ": openInputBridge," +
- " token=" + token + ", name=" + name);
- }
- final long idToken = Binder.clearCallingIdentity();
- try {
- if (mProviderMethods.openInputBridge(TvRemoteProviderProxy.this, token,
- name, width, height, maxPointers)) {
- onInputBridgeConnected(token);
- }
- } finally {
- Binder.restoreCallingIdentity(idToken);
- }
- } else {
- if (DEBUG) {
- Slog.w(TAG,
- "openInputBridge, Invalid connection or incorrect uid: " + Binder
- .getCallingUid());
- }
- }
- }
- }
-
- void closeInputBridge(final IBinder token) {
- synchronized (mLock) {
- if (mActiveConnection == this && Binder.getCallingUid() == mUid) {
- if (DEBUG) {
- Slog.d(TAG, this + ": closeInputBridge," +
- " token=" + token);
- }
- final long idToken = Binder.clearCallingIdentity();
- try {
- mProviderMethods.closeInputBridge(TvRemoteProviderProxy.this, token);
- } finally {
- Binder.restoreCallingIdentity(idToken);
- }
- } else {
- if (DEBUG) {
- Slog.w(TAG,
- "closeInputBridge, Invalid connection or incorrect uid: " +
- Binder.getCallingUid());
- }
- }
- }
- }
-
- void clearInputBridge(final IBinder token) {
- synchronized (mLock) {
- if (mActiveConnection == this && Binder.getCallingUid() == mUid) {
- if (DEBUG) {
- Slog.d(TAG, this + ": clearInputBridge," +
- " token=" + token);
- }
- final long idToken = Binder.clearCallingIdentity();
- try {
- mProviderMethods.clearInputBridge(TvRemoteProviderProxy.this, token);
- } finally {
- Binder.restoreCallingIdentity(idToken);
- }
- } else {
- if (DEBUG) {
- Slog.w(TAG,
- "clearInputBridge, Invalid connection or incorrect uid: " +
- Binder.getCallingUid());
- }
- }
- }
- }
-
- void sendTimestamp(final IBinder token, final long timestamp) {
- if (DEBUG) {
- Slog.e(TAG, "sendTimestamp is deprecated, please remove all usages of this API.");
- }
- }
-
- void sendKeyDown(final IBinder token, final int keyCode) {
- synchronized (mLock) {
- if (mActiveConnection == this && Binder.getCallingUid() == mUid) {
- if (DEBUG_KEY) {
- Slog.d(TAG, this + ": sendKeyDown," +
- " token=" + token + ", keyCode=" + keyCode);
- }
- final long idToken = Binder.clearCallingIdentity();
- try {
- mProviderMethods.sendKeyDown(TvRemoteProviderProxy.this, token, keyCode);
- } finally {
- Binder.restoreCallingIdentity(idToken);
- }
- } else {
- if (DEBUG) {
- Slog.w(TAG,
- "sendKeyDown, Invalid connection or incorrect uid: " + Binder
- .getCallingUid());
- }
- }
- }
- }
-
- void sendKeyUp(final IBinder token, final int keyCode) {
- synchronized (mLock) {
- if (mActiveConnection == this && Binder.getCallingUid() == mUid) {
- if (DEBUG_KEY) {
- Slog.d(TAG, this + ": sendKeyUp," +
- " token=" + token + ", keyCode=" + keyCode);
- }
- final long idToken = Binder.clearCallingIdentity();
- try {
- mProviderMethods.sendKeyUp(TvRemoteProviderProxy.this, token, keyCode);
- } finally {
- Binder.restoreCallingIdentity(idToken);
- }
- } else {
- if (DEBUG) {
- Slog.w(TAG,
- "sendKeyUp, Invalid connection or incorrect uid: " + Binder
- .getCallingUid());
- }
- }
- }
- }
-
- void sendPointerDown(final IBinder token, final int pointerId, final int x, final int y) {
- synchronized (mLock) {
- if (mActiveConnection == this && Binder.getCallingUid() == mUid) {
- if (DEBUG_KEY) {
- Slog.d(TAG, this + ": sendPointerDown," +
- " token=" + token + ", pointerId=" + pointerId);
- }
- final long idToken = Binder.clearCallingIdentity();
- try {
- mProviderMethods.sendPointerDown(TvRemoteProviderProxy.this, token,
- pointerId, x, y);
- } finally {
- Binder.restoreCallingIdentity(idToken);
- }
- } else {
- if (DEBUG) {
- Slog.w(TAG,
- "sendPointerDown, Invalid connection or incorrect uid: " + Binder
- .getCallingUid());
- }
- }
- }
- }
-
- void sendPointerUp(final IBinder token, final int pointerId) {
- synchronized (mLock) {
- if (mActiveConnection == this && Binder.getCallingUid() == mUid) {
- if (DEBUG_KEY) {
- Slog.d(TAG, this + ": sendPointerUp," +
- " token=" + token + ", pointerId=" + pointerId);
- }
- final long idToken = Binder.clearCallingIdentity();
- try {
- mProviderMethods.sendPointerUp(TvRemoteProviderProxy.this, token,
- pointerId);
- } finally {
- Binder.restoreCallingIdentity(idToken);
- }
- } else {
- if (DEBUG) {
- Slog.w(TAG,
- "sendPointerUp, Invalid connection or incorrect uid: " + Binder
- .getCallingUid());
- }
- }
- }
- }
-
- void sendPointerSync(final IBinder token) {
- synchronized (mLock) {
- if (mActiveConnection == this && Binder.getCallingUid() == mUid) {
- if (DEBUG_KEY) {
- Slog.d(TAG, this + ": sendPointerSync," +
- " token=" + token);
- }
- final long idToken = Binder.clearCallingIdentity();
- try {
- if (mProviderMethods != null) {
- mProviderMethods.sendPointerSync(TvRemoteProviderProxy.this, token);
- }
- } finally {
- Binder.restoreCallingIdentity(idToken);
- }
- } else {
- if (DEBUG) {
- Slog.w(TAG,
- "sendPointerSync, Invalid connection or incorrect uid: " + Binder
- .getCallingUid());
- }
- }
- }
- }
- }
-
- /**
- * Receives events from the connected provider.
- * <p>
- * This inner class is static and only retains a weak reference to the connection
- * to prevent the client from being leaked in case the service is holding an
- * active reference to the client's callback.
- * </p>
- */
- private static final class RemoteServiceInputProvider extends ITvRemoteServiceInput.Stub {
- private final WeakReference<Connection> mConnectionRef;
-
- public RemoteServiceInputProvider(Connection connection) {
- mConnectionRef = new WeakReference<Connection>(connection);
- }
-
- public void dispose() {
- // Terminate the connection.
- mConnectionRef.clear();
- }
-
- @Override
- public void openInputBridge(IBinder token, String name, int width,
- int height, int maxPointers) throws RemoteException {
- Connection connection = mConnectionRef.get();
- if (connection != null) {
- connection.openInputBridge(token, name, width, height, maxPointers);
- }
- }
-
- @Override
- public void closeInputBridge(IBinder token) throws RemoteException {
- Connection connection = mConnectionRef.get();
- if (connection != null) {
- connection.closeInputBridge(token);
- }
- }
-
- @Override
- public void clearInputBridge(IBinder token) throws RemoteException {
- Connection connection = mConnectionRef.get();
- if (connection != null) {
- connection.clearInputBridge(token);
- }
- }
-
- @Override
- public void sendTimestamp(IBinder token, long timestamp) throws RemoteException {
- Connection connection = mConnectionRef.get();
- if (connection != null) {
- connection.sendTimestamp(token, timestamp);
- }
- }
-
- @Override
- public void sendKeyDown(IBinder token, int keyCode) throws RemoteException {
- Connection connection = mConnectionRef.get();
- if (connection != null) {
- connection.sendKeyDown(token, keyCode);
- }
- }
-
- @Override
- public void sendKeyUp(IBinder token, int keyCode) throws RemoteException {
- Connection connection = mConnectionRef.get();
- if (connection != null) {
- connection.sendKeyUp(token, keyCode);
- }
- }
-
- @Override
- public void sendPointerDown(IBinder token, int pointerId, int x, int y)
- throws RemoteException {
- Connection connection = mConnectionRef.get();
- if (connection != null) {
- connection.sendPointerDown(token, pointerId, x, y);
- }
- }
-
- @Override
- public void sendPointerUp(IBinder token, int pointerId) throws RemoteException {
- Connection connection = mConnectionRef.get();
- if (connection != null) {
- connection.sendPointerUp(token, pointerId);
- }
- }
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mConnected = false;
- @Override
- public void sendPointerSync(IBinder token) throws RemoteException {
- Connection connection = mConnectionRef.get();
- if (connection != null) {
- connection.sendPointerSync(token);
- }
+ if (DEBUG) {
+ Slog.d(TAG, this + ": onServiceDisconnected()");
}
}
}
diff --git a/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java b/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java
index 0d29edd02663..cddcabe80f33 100644
--- a/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java
+++ b/services/core/java/com/android/server/tv/TvRemoteProviderWatcher.java
@@ -41,27 +41,27 @@ import java.util.Collections;
*/
final class TvRemoteProviderWatcher {
- private static final String TAG = "TvRemoteProvWatcher"; // max. 23 chars
+ private static final String TAG = "TvRemoteProviderWatcher";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.VERBOSE);
private final Context mContext;
- private final TvRemoteProviderProxy.ProviderMethods mProvider;
private final Handler mHandler;
private final PackageManager mPackageManager;
private final ArrayList<TvRemoteProviderProxy> mProviderProxies = new ArrayList<>();
private final int mUserId;
private final String mUnbundledServicePackage;
+ private final Object mLock;
private boolean mRunning;
- TvRemoteProviderWatcher(Context context, TvRemoteProviderProxy.ProviderMethods provider) {
+ TvRemoteProviderWatcher(Context context, Object lock) {
mContext = context;
- mProvider = provider;
mHandler = new Handler(true);
mUserId = UserHandle.myUserId();
mPackageManager = context.getPackageManager();
mUnbundledServicePackage = context.getString(
com.android.internal.R.string.config_tvRemoteServicePackage);
+ mLock = lock;
}
public void start() {
@@ -116,7 +116,7 @@ final class TvRemoteProviderWatcher {
int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name);
if (sourceIndex < 0) {
TvRemoteProviderProxy providerProxy =
- new TvRemoteProviderProxy(mContext, mProvider,
+ new TvRemoteProviderProxy(mContext, mLock,
new ComponentName(serviceInfo.packageName, serviceInfo.name),
mUserId, serviceInfo.applicationInfo.uid);
providerProxy.start();
diff --git a/services/core/java/com/android/server/tv/TvRemoteService.java b/services/core/java/com/android/server/tv/TvRemoteService.java
index bee6fb34a899..58946456b940 100644
--- a/services/core/java/com/android/server/tv/TvRemoteService.java
+++ b/services/core/java/com/android/server/tv/TvRemoteService.java
@@ -17,17 +17,11 @@
package com.android.server.tv;
import android.content.Context;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.ArrayMap;
import android.util.Slog;
import com.android.server.SystemService;
import com.android.server.Watchdog;
-import java.io.IOException;
-import java.util.Map;
-
/**
* TvRemoteService represents a system service that allows a connected
* remote control (emote) service to inject white-listed input events
@@ -38,27 +32,17 @@ import java.util.Map;
public class TvRemoteService extends SystemService implements Watchdog.Monitor {
private static final String TAG = "TvRemoteService";
private static final boolean DEBUG = false;
- private static final boolean DEBUG_KEYS = false;
-
- private final TvRemoteProviderWatcher mWatcher;
- private Map<IBinder, UinputBridge> mBridgeMap = new ArrayMap();
/**
- * State guarded by mLock.
- * This is the second lock in sequence for an incoming call.
- * The first lock is always {@link TvRemoteProviderProxy#mLock}
- *
- * There are currently no methods that break this sequence.
- * Special note:
- * Outgoing call informInputBridgeConnected(), which is called from
- * openInputBridgeInternalLocked() uses a handler thereby relinquishing held locks.
+ * All actions on input bridges are serialized using mLock.
+ * This is necessary because {@link UInputBridge} is not thread-safe.
*/
private final Object mLock = new Object();
+ private final TvRemoteProviderWatcher mWatcher;
public TvRemoteService(Context context) {
super(context);
- mWatcher = new TvRemoteProviderWatcher(context,
- new UserProvider(TvRemoteService.this));
+ mWatcher = new TvRemoteProviderWatcher(context, mLock);
Watchdog.getInstance().addMonitor(this);
}
@@ -81,214 +65,4 @@ public class TvRemoteService extends SystemService implements Watchdog.Monitor {
mWatcher.start(); // Also schedules the start of all providers.
}
}
-
- private boolean openInputBridgeInternalLocked(final IBinder token,
- String name, int width, int height,
- int maxPointers) {
- if (DEBUG) {
- Slog.d(TAG, "openInputBridgeInternalLocked(), token: " + token + ", name: " + name +
- ", width: " + width + ", height: " + height + ", maxPointers: " + maxPointers);
- }
-
- try {
- //Create a new bridge, if one does not exist already
- if (mBridgeMap.containsKey(token)) {
- if (DEBUG) Slog.d(TAG, "RemoteBridge already exists");
- return true;
- }
-
- UinputBridge inputBridge = new UinputBridge(token, name, width, height, maxPointers);
- mBridgeMap.put(token, inputBridge);
-
- try {
- token.linkToDeath(new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- synchronized (mLock) {
- closeInputBridgeInternalLocked(token);
- }
- }
- }, 0);
- } catch (RemoteException e) {
- if (DEBUG) Slog.d(TAG, "Token is already dead");
- closeInputBridgeInternalLocked(token);
- return false;
- }
- } catch (IOException ioe) {
- Slog.e(TAG, "Cannot create device for " + name);
- return false;
- }
- return true;
- }
-
- private void closeInputBridgeInternalLocked(IBinder token) {
- if (DEBUG) {
- Slog.d(TAG, "closeInputBridgeInternalLocked(), token: " + token);
- }
-
- // Close an existing RemoteBridge
- UinputBridge inputBridge = mBridgeMap.get(token);
- if (inputBridge != null) {
- inputBridge.close(token);
- }
-
- mBridgeMap.remove(token);
- }
-
- private void clearInputBridgeInternalLocked(IBinder token) {
- if (DEBUG) {
- Slog.d(TAG, "clearInputBridgeInternalLocked(), token: " + token);
- }
-
- UinputBridge inputBridge = mBridgeMap.get(token);
- if (inputBridge != null) {
- inputBridge.clear(token);
- }
- }
-
- private void sendKeyDownInternalLocked(IBinder token, int keyCode) {
- if (DEBUG_KEYS) {
- Slog.d(TAG, "sendKeyDownInternalLocked(), token: " + token + ", keyCode: " + keyCode);
- }
-
- UinputBridge inputBridge = mBridgeMap.get(token);
- if (inputBridge != null) {
- inputBridge.sendKeyDown(token, keyCode);
- }
- }
-
- private void sendKeyUpInternalLocked(IBinder token, int keyCode) {
- if (DEBUG_KEYS) {
- Slog.d(TAG, "sendKeyUpInternalLocked(), token: " + token + ", keyCode: " + keyCode);
- }
-
- UinputBridge inputBridge = mBridgeMap.get(token);
- if (inputBridge != null) {
- inputBridge.sendKeyUp(token, keyCode);
- }
- }
-
- private void sendPointerDownInternalLocked(IBinder token, int pointerId, int x, int y) {
- if (DEBUG_KEYS) {
- Slog.d(TAG, "sendPointerDownInternalLocked(), token: " + token + ", pointerId: " +
- pointerId + ", x: " + x + ", y: " + y);
- }
-
- UinputBridge inputBridge = mBridgeMap.get(token);
- if (inputBridge != null) {
- inputBridge.sendPointerDown(token, pointerId, x, y);
- }
- }
-
- private void sendPointerUpInternalLocked(IBinder token, int pointerId) {
- if (DEBUG_KEYS) {
- Slog.d(TAG, "sendPointerUpInternalLocked(), token: " + token + ", pointerId: " +
- pointerId);
- }
-
- UinputBridge inputBridge = mBridgeMap.get(token);
- if (inputBridge != null) {
- inputBridge.sendPointerUp(token, pointerId);
- }
- }
-
- private void sendPointerSyncInternalLocked(IBinder token) {
- if (DEBUG_KEYS) {
- Slog.d(TAG, "sendPointerSyncInternalLocked(), token: " + token);
- }
-
- UinputBridge inputBridge = mBridgeMap.get(token);
- if (inputBridge != null) {
- inputBridge.sendPointerSync(token);
- }
- }
-
- private final class UserProvider implements TvRemoteProviderProxy.ProviderMethods {
-
- private final TvRemoteService mService;
-
- public UserProvider(TvRemoteService service) {
- mService = service;
- }
-
- @Override
- public boolean openInputBridge(TvRemoteProviderProxy provider, IBinder token, String name,
- int width, int height, int maxPointers) {
- if (DEBUG) {
- Slog.d(TAG, "openInputBridge(), token: " + token +
- ", name: " + name + ", width: " + width +
- ", height: " + height + ", maxPointers: " + maxPointers);
- }
-
- synchronized (mLock) {
- return mService.openInputBridgeInternalLocked(token, name, width,
- height, maxPointers);
- }
- }
-
- @Override
- public void closeInputBridge(TvRemoteProviderProxy provider, IBinder token) {
- if (DEBUG) Slog.d(TAG, "closeInputBridge(), token: " + token);
- synchronized (mLock) {
- mService.closeInputBridgeInternalLocked(token);
- }
- }
-
- @Override
- public void clearInputBridge(TvRemoteProviderProxy provider, IBinder token) {
- if (DEBUG) Slog.d(TAG, "clearInputBridge(), token: " + token);
- synchronized (mLock) {
- mService.clearInputBridgeInternalLocked(token);
- }
- }
-
- @Override
- public void sendKeyDown(TvRemoteProviderProxy provider, IBinder token, int keyCode) {
- if (DEBUG_KEYS) {
- Slog.d(TAG, "sendKeyDown(), token: " + token + ", keyCode: " + keyCode);
- }
- synchronized (mLock) {
- mService.sendKeyDownInternalLocked(token, keyCode);
- }
- }
-
- @Override
- public void sendKeyUp(TvRemoteProviderProxy provider, IBinder token, int keyCode) {
- if (DEBUG_KEYS) {
- Slog.d(TAG, "sendKeyUp(), token: " + token + ", keyCode: " + keyCode);
- }
- synchronized (mLock) {
- mService.sendKeyUpInternalLocked(token, keyCode);
- }
- }
-
- @Override
- public void sendPointerDown(TvRemoteProviderProxy provider, IBinder token, int pointerId,
- int x, int y) {
- if (DEBUG_KEYS) {
- Slog.d(TAG, "sendPointerDown(), token: " + token + ", pointerId: " + pointerId);
- }
- synchronized (mLock) {
- mService.sendPointerDownInternalLocked(token, pointerId, x, y);
- }
- }
-
- @Override
- public void sendPointerUp(TvRemoteProviderProxy provider, IBinder token, int pointerId) {
- if (DEBUG_KEYS) {
- Slog.d(TAG, "sendPointerUp(), token: " + token + ", pointerId: " + pointerId);
- }
- synchronized (mLock) {
- mService.sendPointerUpInternalLocked(token, pointerId);
- }
- }
-
- @Override
- public void sendPointerSync(TvRemoteProviderProxy provider, IBinder token) {
- if (DEBUG_KEYS) Slog.d(TAG, "sendPointerSync(), token: " + token);
- synchronized (mLock) {
- mService.sendPointerSyncInternalLocked(token);
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/tv/TvRemoteServiceInput.java b/services/core/java/com/android/server/tv/TvRemoteServiceInput.java
new file mode 100644
index 000000000000..8fe6da5e8dbe
--- /dev/null
+++ b/services/core/java/com/android/server/tv/TvRemoteServiceInput.java
@@ -0,0 +1,244 @@
+/*
+ * 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.tv;
+
+import android.media.tv.ITvRemoteProvider;
+import android.media.tv.ITvRemoteServiceInput;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import java.io.IOException;
+import java.util.Map;
+
+final class TvRemoteServiceInput extends ITvRemoteServiceInput.Stub {
+ private static final String TAG = "TvRemoteServiceInput";
+ private static final boolean DEBUG = false;
+ private static final boolean DEBUG_KEYS = false;
+
+ private final Map<IBinder, UinputBridge> mBridgeMap;
+ private final Object mLock;
+ private final ITvRemoteProvider mProvider;
+
+ TvRemoteServiceInput(Object lock, ITvRemoteProvider provider) {
+ mBridgeMap = new ArrayMap();
+ mLock = lock;
+ mProvider = provider;
+ }
+
+ @Override
+ public void openInputBridge(IBinder token, String name, int width,
+ int height, int maxPointers) {
+ if (DEBUG) {
+ Slog.d(TAG, "openInputBridge(), token: " + token
+ + ", name: " + name + ", width: " + width
+ + ", height: " + height + ", maxPointers: " + maxPointers);
+ }
+
+ synchronized (mLock) {
+ if (mBridgeMap.containsKey(token)) {
+ if (DEBUG) {
+ Slog.d(TAG, "InputBridge already exists");
+ }
+ } else {
+ final long idToken = Binder.clearCallingIdentity();
+ try {
+ mBridgeMap.put(token,
+ new UinputBridge(token, name, width, height, maxPointers));
+ token.linkToDeath(new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ closeInputBridge(token);
+ }
+ }, 0);
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot create device for " + name);
+ return;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Token is already dead");
+ closeInputBridge(token);
+ return;
+ } finally {
+ Binder.restoreCallingIdentity(idToken);
+ }
+ }
+ }
+
+ try {
+ mProvider.onInputBridgeConnected(token);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed remote call to onInputBridgeConnected");
+ }
+ }
+
+ @Override
+ public void closeInputBridge(IBinder token) {
+ if (DEBUG) {
+ Slog.d(TAG, "closeInputBridge(), token: " + token);
+ }
+
+ synchronized (mLock) {
+ UinputBridge inputBridge = mBridgeMap.remove(token);
+ if (inputBridge == null) {
+ return;
+ }
+
+ final long idToken = Binder.clearCallingIdentity();
+ try {
+ inputBridge.close(token);
+ } finally {
+ Binder.restoreCallingIdentity(idToken);
+ }
+ }
+ }
+
+ @Override
+ public void clearInputBridge(IBinder token) {
+ if (DEBUG) {
+ Slog.d(TAG, "clearInputBridge, token: " + token);
+ }
+
+ synchronized (mLock) {
+ UinputBridge inputBridge = mBridgeMap.get(token);
+ if (inputBridge == null) {
+ return;
+ }
+
+ final long idToken = Binder.clearCallingIdentity();
+ try {
+ inputBridge.clear(token);
+ } finally {
+ Binder.restoreCallingIdentity(idToken);
+ }
+ }
+ }
+
+ @Override
+ public void sendTimestamp(IBinder token, long timestamp) {
+ if (DEBUG) {
+ Slog.e(TAG, "sendTimestamp is deprecated, please remove all usages of this API.");
+ }
+ }
+
+ @Override
+ public void sendKeyDown(IBinder token, int keyCode) {
+ if (DEBUG_KEYS) {
+ Slog.d(TAG, "sendKeyDown(), token: " + token + ", keyCode: " + keyCode);
+ }
+
+ synchronized (mLock) {
+ UinputBridge inputBridge = mBridgeMap.get(token);
+ if (inputBridge == null) {
+ return;
+ }
+
+ final long idToken = Binder.clearCallingIdentity();
+ try {
+ inputBridge.sendKeyDown(token, keyCode);
+ } finally {
+ Binder.restoreCallingIdentity(idToken);
+ }
+ }
+ }
+
+ @Override
+ public void sendKeyUp(IBinder token, int keyCode) {
+ if (DEBUG_KEYS) {
+ Slog.d(TAG, "sendKeyUp(), token: " + token + ", keyCode: " + keyCode);
+ }
+
+ synchronized (mLock) {
+ UinputBridge inputBridge = mBridgeMap.get(token);
+ if (inputBridge == null) {
+ return;
+ }
+
+ final long idToken = Binder.clearCallingIdentity();
+ try {
+ inputBridge.sendKeyUp(token, keyCode);
+ } finally {
+ Binder.restoreCallingIdentity(idToken);
+ }
+ }
+ }
+
+ @Override
+ public void sendPointerDown(IBinder token, int pointerId, int x, int y) {
+ if (DEBUG_KEYS) {
+ Slog.d(TAG, "sendPointerDown(), token: " + token + ", pointerId: "
+ + pointerId + ", x: " + x + ", y: " + y);
+ }
+
+ synchronized (mLock) {
+ UinputBridge inputBridge = mBridgeMap.get(token);
+ if (inputBridge == null) {
+ return;
+ }
+
+ final long idToken = Binder.clearCallingIdentity();
+ try {
+ inputBridge.sendPointerDown(token, pointerId, x, y);
+ } finally {
+ Binder.restoreCallingIdentity(idToken);
+ }
+ }
+ }
+
+ @Override
+ public void sendPointerUp(IBinder token, int pointerId) {
+ if (DEBUG_KEYS) {
+ Slog.d(TAG, "sendPointerUp(), token: " + token + ", pointerId: " + pointerId);
+ }
+
+ synchronized (mLock) {
+ UinputBridge inputBridge = mBridgeMap.get(token);
+ if (inputBridge == null) {
+ return;
+ }
+
+ final long idToken = Binder.clearCallingIdentity();
+ try {
+ inputBridge.sendPointerUp(token, pointerId);
+ } finally {
+ Binder.restoreCallingIdentity(idToken);
+ }
+ }
+ }
+
+ @Override
+ public void sendPointerSync(IBinder token) {
+ if (DEBUG_KEYS) {
+ Slog.d(TAG, "sendPointerSync(), token: " + token);
+ }
+
+ synchronized (mLock) {
+ UinputBridge inputBridge = mBridgeMap.get(token);
+ if (inputBridge == null) {
+ return;
+ }
+
+ final long idToken = Binder.clearCallingIdentity();
+ try {
+ inputBridge.sendPointerSync(token);
+ } finally {
+ Binder.restoreCallingIdentity(idToken);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/twilight/TwilightService.java b/services/core/java/com/android/server/twilight/TwilightService.java
index e4cb19e3eb21..e72ba8d9f01b 100644
--- a/services/core/java/com/android/server/twilight/TwilightService.java
+++ b/services/core/java/com/android/server/twilight/TwilightService.java
@@ -17,6 +17,7 @@
package com.android.server.twilight;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AlarmManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -160,8 +161,13 @@ public final class TwilightService extends SystemService
// Request the device's location immediately if a previous location isn't available.
if (mLocationManager.getLastLocation() == null) {
if (mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
- mLocationManager.requestSingleUpdate(
- LocationManager.NETWORK_PROVIDER, this, Looper.getMainLooper());
+ mLocationManager.getCurrentLocation(
+ LocationManager.NETWORK_PROVIDER, null, getContext().getMainExecutor(),
+ this::onLocationChanged);
+ } else if (mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
+ mLocationManager.getCurrentLocation(
+ LocationManager.GPS_PROVIDER, null, getContext().getMainExecutor(),
+ this::onLocationChanged);
}
}
@@ -218,12 +224,7 @@ public final class TwilightService extends SystemService
for (int i = mListeners.size() - 1; i >= 0; --i) {
final TwilightListener listener = mListeners.keyAt(i);
final Handler handler = mListeners.valueAt(i);
- handler.post(new Runnable() {
- @Override
- public void run() {
- listener.onTwilightStateChanged(state);
- }
- });
+ handler.post(() -> listener.onTwilightStateChanged(state));
}
}
}
@@ -243,12 +244,8 @@ public final class TwilightService extends SystemService
}
@Override
- public void onLocationChanged(Location location) {
- // Location providers may erroneously return (0.0, 0.0) when they fail to determine the
- // device's location. These location updates can be safely ignored since the chance of a
- // user actually being at these coordinates is quite low.
- if (location != null
- && !(location.getLongitude() == 0.0 && location.getLatitude() == 0.0)) {
+ public void onLocationChanged(@Nullable Location location) {
+ if (location != null) {
Slog.d(TAG, "onLocationChanged:"
+ " provider=" + location.getProvider()
+ " accuracy=" + location.getAccuracy()
diff --git a/services/core/java/com/android/server/updates/EmergencyNumberDbInstallReceiver.java b/services/core/java/com/android/server/updates/EmergencyNumberDbInstallReceiver.java
new file mode 100644
index 000000000000..852f70779f77
--- /dev/null
+++ b/services/core/java/com/android/server/updates/EmergencyNumberDbInstallReceiver.java
@@ -0,0 +1,39 @@
+/*
+ * 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.updates;
+
+import android.content.Context;
+import android.content.Intent;
+import android.util.Slog;
+
+/**
+ * Emergency Number Database Install Receiver.
+ */
+public class EmergencyNumberDbInstallReceiver extends ConfigUpdateInstallReceiver {
+
+ private static final String TAG = "EmergencyNumberDbInstallReceiver";
+
+ public EmergencyNumberDbInstallReceiver() {
+ super("/data/misc/emergencynumberdb", "emergency_number_db", "metadata/", "version");
+ }
+
+ @Override
+ protected void postInstall(Context context, Intent intent) {
+ Slog.i(TAG, "Emergency number database is updated in file partition");
+ // TODO Send a notification to EmergencyNumberTracker for updating of emergency number db.
+ }
+}
diff --git a/services/core/java/com/android/server/utils/TraceBuffer.java b/services/core/java/com/android/server/utils/TraceBuffer.java
new file mode 100644
index 000000000000..0567960e05e4
--- /dev/null
+++ b/services/core/java/com/android/server/utils/TraceBuffer.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.utils;
+
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Queue;
+
+/**
+ * Buffer used for tracing and logging.
+ */
+public class TraceBuffer {
+ private final Object mBufferLock = new Object();
+
+ private final Queue<ProtoOutputStream> mBuffer = new ArrayDeque<>();
+ private int mBufferUsedSize;
+ private int mBufferCapacity;
+
+ public TraceBuffer(int bufferCapacity) {
+ mBufferCapacity = bufferCapacity;
+ resetBuffer();
+ }
+
+ public int getAvailableSpace() {
+ return mBufferCapacity - mBufferUsedSize;
+ }
+
+ /**
+ * Returns buffer size.
+ */
+ public int size() {
+ return mBuffer.size();
+ }
+
+ public void setCapacity(int capacity) {
+ mBufferCapacity = capacity;
+ }
+
+ /**
+ * Inserts the specified element into this buffer.
+ *
+ * @param proto the element to add
+ * @throws IllegalStateException if the element cannot be added because it is larger
+ * than the buffer size.
+ */
+ public void add(ProtoOutputStream proto) {
+ int protoLength = proto.getRawSize();
+ if (protoLength > mBufferCapacity) {
+ throw new IllegalStateException("Trace object too large for the buffer. Buffer size:"
+ + mBufferCapacity + " Object size: " + protoLength);
+ }
+ synchronized (mBufferLock) {
+ discardOldest(protoLength);
+ mBuffer.add(proto);
+ mBufferUsedSize += protoLength;
+ mBufferLock.notify();
+ }
+ }
+
+ boolean contains(byte[] other) {
+ return mBuffer.stream()
+ .anyMatch(p -> Arrays.equals(p.getBytes(), other));
+ }
+
+ /**
+ * Writes the trace buffer to disk inside the encapsulatingProto..
+ */
+ public void writeTraceToFile(File traceFile, ProtoOutputStream encapsulatingProto)
+ throws IOException {
+ synchronized (mBufferLock) {
+ traceFile.delete();
+ try (OutputStream os = new FileOutputStream(traceFile)) {
+ traceFile.setReadable(true /* readable */, false /* ownerOnly */);
+ os.write(encapsulatingProto.getBytes());
+ for (ProtoOutputStream protoOutputStream : mBuffer) {
+ encapsulatingProto = protoOutputStream;
+ byte[] protoBytes = encapsulatingProto.getBytes();
+ os.write(protoBytes);
+ }
+ os.flush();
+ }
+ }
+ }
+
+ /**
+ * Checks if the element can be added to the buffer. The element is already certain to be
+ * smaller than the overall buffer size.
+ *
+ * @param protoLength byte array representation of the Proto object to add
+ */
+ private void discardOldest(int protoLength) {
+ long availableSpace = getAvailableSpace();
+
+ while (availableSpace < protoLength) {
+
+ ProtoOutputStream item = mBuffer.poll();
+ if (item == null) {
+ throw new IllegalStateException("No element to discard from buffer");
+ }
+ mBufferUsedSize -= item.getRawSize();
+ availableSpace = getAvailableSpace();
+ }
+ }
+
+ /**
+ * Removes all elements form the buffer
+ */
+ public void resetBuffer() {
+ synchronized (mBufferLock) {
+ mBuffer.clear();
+ mBufferUsedSize = 0;
+ }
+ }
+
+ @VisibleForTesting
+ int getBufferSize() {
+ return mBufferUsedSize;
+ }
+
+ /**
+ * Returns the buffer status in human-readable form.
+ */
+ public String getStatus() {
+ synchronized (mBufferLock) {
+ return "Buffer size: "
+ + mBufferCapacity
+ + " bytes"
+ + "\n"
+ + "Buffer usage: "
+ + mBufferUsedSize
+ + " bytes"
+ + "\n"
+ + "Elements in the buffer: "
+ + mBuffer.size();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 3cdb59beb23c..3663f4696a27 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1228,6 +1228,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
saveSettingsLocked(mWallpaper.userId);
}
FgThread.getHandler().removeCallbacks(mResetRunnable);
+ mContext.getMainThreadHandler().removeCallbacks(this::tryToRebind);
}
}
}
@@ -1270,6 +1271,34 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
+ private void tryToRebind() {
+ synchronized (mLock) {
+ if (mWallpaper.wallpaperUpdating) {
+ return;
+ }
+ final ComponentName wpService = mWallpaper.wallpaperComponent;
+ // The broadcast of package update could be delayed after service disconnected. Try
+ // to re-bind the service for 10 seconds.
+ if (bindWallpaperComponentLocked(
+ wpService, true, false, mWallpaper, null)) {
+ mWallpaper.connection.scheduleTimeoutLocked();
+ } else if (SystemClock.uptimeMillis() - mWallpaper.lastDiedTime
+ < WALLPAPER_RECONNECT_TIMEOUT_MS) {
+ // Bind fail without timeout, schedule rebind
+ Slog.w(TAG, "Rebind fail! Try again later");
+ mContext.getMainThreadHandler().postDelayed(this::tryToRebind, 1000);
+ } else {
+ // Timeout
+ Slog.w(TAG, "Reverting to built-in wallpaper!");
+ clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
+ final String flattened = wpService.flattenToString();
+ EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED,
+ flattened.substring(0, Math.min(flattened.length(),
+ MAX_WALLPAPER_COMPONENT_LOG_LENGTH)));
+ }
+ }
+ }
+
private void processDisconnect(final ServiceConnection connection) {
synchronized (mLock) {
// The wallpaper disappeared. If this isn't a system-default one, track
@@ -1293,20 +1322,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
} else {
mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
-
- clearWallpaperComponentLocked(mWallpaper);
- if (bindWallpaperComponentLocked(
- wpService, false, false, mWallpaper, null)) {
- mWallpaper.connection.scheduleTimeoutLocked();
- } else {
- Slog.w(TAG, "Reverting to built-in wallpaper!");
- clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
- }
+ tryToRebind();
}
- final String flattened = wpService.flattenToString();
- EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED,
- flattened.substring(0, Math.min(flattened.length(),
- MAX_WALLPAPER_COMPONENT_LOG_LENGTH)));
}
} else {
if (DEBUG_LIVE) {
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 59f051bc76a6..b131ab66da9f 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -271,11 +271,11 @@ final class AccessibilityController {
}
}
- /** NOTE: This has to be called within a surface transaction. */
- public void drawMagnifiedRegionBorderIfNeededLocked(int displayId) {
+ public void drawMagnifiedRegionBorderIfNeededLocked(int displayId,
+ SurfaceControl.Transaction t) {
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
+ displayMagnifier.drawMagnifiedRegionBorderIfNeededLocked(t);
}
// Not relevant for the window observer.
}
@@ -431,7 +431,7 @@ final class AccessibilityController {
Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
+ " displayId: " + displayContent.getDisplayId());
}
- mMagnifedViewport.onRotationChangedLocked();
+ mMagnifedViewport.onRotationChangedLocked(displayContent.getPendingTransaction());
mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
}
@@ -536,9 +536,8 @@ final class AccessibilityController {
.sendToTarget();
}
- /** NOTE: This has to be called within a surface transaction. */
- public void drawMagnifiedRegionBorderIfNeededLocked() {
- mMagnifedViewport.drawWindowIfNeededLocked();
+ public void drawMagnifiedRegionBorderIfNeededLocked(SurfaceControl.Transaction t) {
+ mMagnifedViewport.drawWindowIfNeededLocked(t);
}
private final class MagnifiedViewport {
@@ -726,7 +725,7 @@ final class AccessibilityController {
}
private Region getLetterboxBounds(WindowState windowState) {
- final AppWindowToken appToken = windowState.mAppToken;
+ final ActivityRecord appToken = windowState.mActivityRecord;
if (appToken == null) {
return new Region();
}
@@ -744,7 +743,7 @@ final class AccessibilityController {
return letterboxBounds;
}
- public void onRotationChangedLocked() {
+ public void onRotationChangedLocked(SurfaceControl.Transaction t) {
// If we are showing the magnification border, hide it immediately so
// the user does not see strange artifacts during rotation. The screenshot
// used for rotation already has the border. After the rotation is complete
@@ -758,7 +757,7 @@ final class AccessibilityController {
mHandler.sendMessageDelayed(message, delay);
}
recomputeBoundsLocked();
- mWindow.updateSize();
+ mWindow.updateSize(t);
}
public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
@@ -784,10 +783,9 @@ final class AccessibilityController {
return mMagnificationSpec;
}
- /** NOTE: This has to be called within a surface transaction. */
- public void drawWindowIfNeededLocked() {
+ public void drawWindowIfNeededLocked(SurfaceControl.Transaction t) {
recomputeBoundsLocked();
- mWindow.drawIfNeeded();
+ mWindow.drawIfNeeded(t);
}
public void destroyWindow() {
@@ -837,10 +835,11 @@ final class AccessibilityController {
/* ignore */
}
mSurfaceControl = surfaceControl;
- mSurfaceControl.setLayer(mService.mPolicy.getWindowLayerFromTypeLw(
- TYPE_MAGNIFICATION_OVERLAY)
- * WindowManagerService.TYPE_LAYER_MULTIPLIER);
- mSurfaceControl.setPosition(0, 0);
+ mService.mTransactionFactory.get().setLayer(mSurfaceControl,
+ mService.mPolicy.getWindowLayerFromTypeLw(TYPE_MAGNIFICATION_OVERLAY)
+ * WindowManagerService.TYPE_LAYER_MULTIPLIER)
+ .setPosition(mSurfaceControl, 0, 0)
+ .apply();
mSurface.copyFrom(mSurfaceControl);
mAnimationController = new AnimationController(context,
@@ -905,10 +904,10 @@ final class AccessibilityController {
}
}
- public void updateSize() {
+ public void updateSize(SurfaceControl.Transaction t) {
synchronized (mService.mGlobalLock) {
mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
- mSurfaceControl.setBufferSize(mTempPoint.x, mTempPoint.y);
+ t.setBufferSize(mSurfaceControl, mTempPoint.x, mTempPoint.y);
invalidate(mDirtyRect);
}
}
@@ -923,8 +922,7 @@ final class AccessibilityController {
mService.scheduleAnimationLocked();
}
- /** NOTE: This has to be called within a surface transaction. */
- public void drawIfNeeded() {
+ public void drawIfNeeded(SurfaceControl.Transaction t) {
synchronized (mService.mGlobalLock) {
if (!mInvalidated) {
return;
@@ -959,9 +957,9 @@ final class AccessibilityController {
canvas.drawPath(path, mPaint);
mSurface.unlockCanvasAndPost(canvas);
- mSurfaceControl.show();
+ t.show(mSurfaceControl);
} else {
- mSurfaceControl.hide();
+ t.hide(mSurfaceControl);
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index 03d35e024200..d2b115c7c686 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -52,9 +52,9 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
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.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.RootActivityContainer.FindTaskResult;
import static com.android.server.wm.RootActivityContainer.TAG_STATES;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
@@ -79,6 +79,7 @@ import android.util.BoostFramework;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.am.EventLogTags;
+import com.android.server.protolog.common.ProtoLog;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -87,8 +88,7 @@ import java.util.ArrayList;
* Exactly one of these classes per Display in the system. Capable of holding zero or more
* attached {@link ActivityStack}s.
*/
-class ActivityDisplay extends ConfigurationContainer<ActivityStack>
- implements WindowContainerListener {
+class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityDisplay" : TAG_ATM;
private static final String TAG_STACK = TAG + POSTFIX_STACK;
@@ -209,11 +209,6 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
}
}
- @Override
- public void onInitializeOverrideConfiguration(Configuration config) {
- getRequestedOverrideConfiguration().updateFrom(config);
- }
-
void addChild(ActivityStack stack, int position) {
if (position == POSITION_BOTTOM) {
position = 0;
@@ -299,9 +294,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
}
// Since positionChildAt() is called during the creation process of pinned stacks,
- // ActivityStack#getStack() can be null. In this special case,
- // since DisplayContest#positionStackAt() is called in TaskStack#onConfigurationChanged(),
- // we don't have to call WindowContainerController#positionChildAt() here.
+ // ActivityStack#getStack() can be null.
if (stack.getTaskStack() != null && mDisplayContent != null) {
mDisplayContent.positionStackAt(insertPosition,
stack.getTaskStack(), includingParents);
@@ -1257,8 +1250,8 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
// Stacks could be reparented from the removed display to other display. While
// reparenting the last stack of the removed display, the remove display is ready to be
// released (no more ActivityStack). But, we cannot release it at that moment or the
- // related WindowContainer and WindowContainerController will also be removed. So, we
- // set display as removed after reparenting stack finished.
+ // related WindowContainer will also be removed. So, we set display as removed after
+ // reparenting stack finished.
final ActivityDisplay toDisplay = mRootActivityContainer.getDefaultDisplay();
mRootActivityContainer.mStackSupervisor.beginDeferResume();
try {
@@ -1356,20 +1349,21 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
if (mDisplayContent == null) {
return;
}
- final AppWindowToken newFocus;
+ final ActivityRecord newFocus;
final IBinder token = r.appToken;
if (token == null) {
- if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Clearing focused app, displayId="
- + mDisplayId);
+ ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Clearing focused app, displayId=%d",
+ mDisplayId);
newFocus = null;
} else {
- newFocus = mService.mWindowManager.mRoot.getAppWindowToken(token);
+ newFocus = mService.mWindowManager.mRoot.getActivityRecord(token);
if (newFocus == null) {
Slog.w(TAG_WM, "Attempted to set focus to non-existing app token: " + token
+ ", displayId=" + mDisplayId);
}
- if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Set focused app to: " + newFocus
- + " moveFocusNow=" + moveFocusNow + " displayId=" + mDisplayId);
+ ProtoLog.v(WM_DEBUG_FOCUS_LIGHT,
+ "Set focused app to: %s moveFocusNow=%b displayId=%d", newFocus,
+ moveFocusNow, mDisplayId);
}
final boolean changed = mDisplayContent.setFocusedApp(newFocus);
@@ -1491,9 +1485,8 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
continue;
}
- final ArrayList<ActivityRecord> activities = task.mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if (r.isActivityTypeHome()
&& ((userId == UserHandle.USER_ALL) || (r.mUserId == userId))) {
return r;
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
index eff0f75466d9..c6b17e24b1de 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
@@ -39,8 +39,13 @@ import java.lang.annotation.RetentionPolicy;
* If an activity is successfully started, the launch sequence's state will transition into
* {@code STARTED} via {@link #onActivityLaunched}. This is a transient state.
*
- * It must then transition to either {@code CANCELLED} with {@link #onActivityLaunchCancelled}
- * or into {@code FINISHED} with {@link #onActivityLaunchFinished}. These are terminal states.
+ * It must then transition to either {@code CANCELLED} with {@link #onActivityLaunchCancelled},
+ * which is a terminal state or into {@code FINISHED} with {@link #onActivityLaunchFinished}.
+ *
+ * The {@code FINISHED} with {@link #onActivityLaunchFinished} then may transition to
+ * {@code FULLY_DRAWN} with {@link #onReportFullyDrawn}, which is a terminal state.
+ * Note this transition may not happen if the reportFullyDrawn event is not receivied,
+ * in which case {@code FINISHED} is terminal.
*
* Note that the {@code ActivityRecordProto} provided as a parameter to some state transitions isn't
* necessarily the same within a single launch sequence: it is only the top-most activity at the
@@ -51,15 +56,15 @@ import java.lang.annotation.RetentionPolicy;
* until a subsequent transition into {@code INTENT_STARTED} initiates a new launch sequence.
*
* <pre>
- * ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐ ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐ ╔══════════════════════════╗
- * ╴╴▶ ⋮ INTENT_STARTED ⋮ ──▶ ⋮ ACTIVITY_LAUNCHED ⋮ ──▶ ║ ACTIVITY_LAUNCH_FINISHED ║
- * └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘ └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘ ╚══════════════════════════╝
- * : :
- * : :
- * ▼ ▼
- * ╔════════════════╗ ╔═══════════════════════════╗
- * ║ INTENT_FAILED ║ ║ ACTIVITY_LAUNCH_CANCELLED ║
- * ╚════════════════╝ ╚═══════════════════════════╝
+ * ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐ ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐ ┌--------------------------┐
+ * ╴╴▶ INTENT_STARTED ──▶ ACTIVITY_LAUNCHED ──▶ ACTIVITY_LAUNCH_FINISHED
+ * └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘ └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘ └--------------------------┘
+ * : : :
+ * : : :
+ * ▼ ▼ ▼
+ * ╔════════════════╗ ╔═══════════════════════════╗ ╔═══════════════════════════╗
+ * ║ INTENT_FAILED ║ ║ ACTIVITY_LAUNCH_CANCELLED ║ ║ REPORT_FULLY_DRAWN ║
+ * ╚════════════════╝ ╚═══════════════════════════╝ ╚═══════════════════════════╝
* </pre>
*/
public interface ActivityMetricsLaunchObserver {
@@ -111,7 +116,7 @@ public interface ActivityMetricsLaunchObserver {
* Multiple calls to this method cannot occur without first terminating the current
* launch sequence.
*/
- public void onIntentStarted(@NonNull Intent intent);
+ public void onIntentStarted(@NonNull Intent intent, long timestampNanos);
/**
* Notifies the observer that the current launch sequence has failed to launch an activity.
@@ -177,6 +182,9 @@ public interface ActivityMetricsLaunchObserver {
* drawn for the first time: the top-most activity at the time is what's reported here.
*
* @param finalActivity the top-most activity whose windows were first to fully draw
+ * @param timestampNanos the timestamp of ActivityLaunchFinished event in nanoseconds.
+ * To compute the TotalTime duration, deduct the timestamp {@link #onIntentStarted}
+ * from {@code timestampNanos}.
*
* Multiple calls to this method cannot occur without first terminating the current
* launch sequence.
@@ -186,5 +194,22 @@ public interface ActivityMetricsLaunchObserver {
* and only the latest activity that was top-most during first-frame drawn
* is reported here.
*/
- public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] finalActivity);
+ public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] finalActivity,
+ long timestampNanos);
+
+ /**
+ * Notifies the observer that the application self-reported itself as being fully drawn.
+ *
+ * @param activity the activity that triggers the ReportFullyDrawn event.
+ * @param timestampNanos the timestamp of ReportFullyDrawn event in nanoseconds.
+ * To compute the duration, deduct the deduct the timestamp {@link #onIntentStarted}
+ * from {@code timestampNanos}.
+ *
+ * @apiNote The behavior of ReportFullyDrawn mostly depends on the app.
+ * It is used as an accurate estimate of meanfully app startup time.
+ * This event may be missing for many apps.
+ */
+ public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity,
+ long timestampNanos);
+
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 35c0fe25644c..dbcd7526d05a 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -93,6 +93,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.SomeArgs;
import com.android.server.LocalServices;
+import java.util.concurrent.TimeUnit;
/**
* Listens to activity launches, transitions, visibility changes and window drawn callbacks to
@@ -133,8 +134,8 @@ class ActivityMetricsLogger {
// set to INVALID_START_TIME in reset.
// set to valid value in notifyActivityLaunching
- private long mCurrentTransitionStartTime = INVALID_START_TIME;
- private long mLastTransitionStartTime = INVALID_START_TIME;
+ private long mCurrentTransitionStartTimeNs = INVALID_START_TIME;
+ private long mLastTransitionStartTimeNs = INVALID_START_TIME;
private int mCurrentTransitionDeviceUptime;
private int mCurrentTransitionDelayMs;
@@ -190,9 +191,22 @@ class ActivityMetricsLogger {
private int startingWindowDelayMs = INVALID_DELAY;
private int bindApplicationDelayMs = INVALID_DELAY;
private int reason = APP_TRANSITION_TIMEOUT;
- private boolean loggedWindowsDrawn;
+ // TODO(b/132736359) The number may need to consider the visibility change.
+ private int numUndrawnActivities = 1;
private boolean loggedStartingWindowDrawn;
private boolean launchTraceActive;
+
+ /**
+ * Remembers the latest launched activity to represent the final transition. This also
+ * increments the number of activities that should be drawn, so a consecutive launching
+ * sequence can be coalesced as one event.
+ */
+ void setLatestLaunchedActivity(ActivityRecord r) {
+ if (launchedActivity == r) {
+ return;
+ }
+ launchedActivity = r;
+ }
}
final class WindowingModeTransitionInfoSnapshot {
@@ -330,12 +344,12 @@ class ActivityMetricsLogger {
intent));
}
- if (mCurrentTransitionStartTime == INVALID_START_TIME) {
+ if (mCurrentTransitionStartTimeNs == INVALID_START_TIME) {
- mCurrentTransitionStartTime = SystemClock.uptimeMillis();
- mLastTransitionStartTime = mCurrentTransitionStartTime;
+ mCurrentTransitionStartTimeNs = SystemClock.elapsedRealtimeNanos();
+ mLastTransitionStartTimeNs = mCurrentTransitionStartTimeNs;
- launchObserverNotifyIntentStarted(intent);
+ launchObserverNotifyIntentStarted(intent, mCurrentTransitionStartTimeNs);
}
}
@@ -386,14 +400,15 @@ class ActivityMetricsLogger {
? launchedActivity.getWindowingMode()
: WINDOWING_MODE_UNDEFINED;
final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
- if (mCurrentTransitionStartTime == INVALID_START_TIME) {
+ if (mCurrentTransitionStartTimeNs == INVALID_START_TIME) {
// No transition is active ignore this launch.
return;
}
if (launchedActivity != null && launchedActivity.mDrawn) {
// Launched activity is already visible. We cannot measure windows drawn delay.
- reset(true /* abort */, info, "launched activity already visible");
+ reset(true /* abort */, info, "launched activity already visible",
+ 0L /* timestampNs */);
return;
}
@@ -402,7 +417,7 @@ class ActivityMetricsLogger {
// the other attributes.
// Coalesce multiple (trampoline) activities from a single sequence together.
- info.launchedActivity = launchedActivity;
+ info.setLatestLaunchedActivity(launchedActivity);
return;
}
@@ -411,7 +426,8 @@ class ActivityMetricsLogger {
if ((!isLoggableResultCode(resultCode) || launchedActivity == null
|| windowingMode == WINDOWING_MODE_UNDEFINED) && !otherWindowModesLaunching) {
// Failed to launch or it was not a process switch, so we don't care about the timing.
- reset(true /* abort */, info, "failed to launch or not a process switch");
+ reset(true /* abort */, info, "failed to launch or not a process switch",
+ 0L /* timestampNs */);
return;
} else if (otherWindowModesLaunching) {
// Don't log this windowing mode but continue with the other windowing modes.
@@ -423,7 +439,7 @@ class ActivityMetricsLogger {
// A new launch sequence [with the windowingMode] has begun.
// Start tracking it.
final WindowingModeTransitionInfo newInfo = new WindowingModeTransitionInfo();
- newInfo.launchedActivity = launchedActivity;
+ newInfo.setLatestLaunchedActivity(launchedActivity);
newInfo.currentTransitionProcessRunning = processRunning;
newInfo.startResult = resultCode;
mWindowingModeTransitionInfo.put(windowingMode, newInfo);
@@ -445,19 +461,20 @@ class ActivityMetricsLogger {
* Notifies the tracker that all windows of the app have been drawn.
*/
WindowingModeTransitionInfoSnapshot notifyWindowsDrawn(@WindowingMode int windowingMode,
- long timestamp) {
+ long timestampNs) {
if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn windowingMode=" + windowingMode);
final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
- if (info == null || info.loggedWindowsDrawn) {
+ if (info == null || info.numUndrawnActivities == 0) {
return null;
}
- info.windowsDrawnDelayMs = calculateDelay(timestamp);
- info.loggedWindowsDrawn = true;
+ info.windowsDrawnDelayMs = calculateDelay(timestampNs);
+ info.numUndrawnActivities--;
final WindowingModeTransitionInfoSnapshot infoSnapshot =
new WindowingModeTransitionInfoSnapshot(info);
if (allWindowsDrawn() && mLoggedTransitionStarting) {
- reset(false /* abort */, info, "notifyWindowsDrawn - all windows drawn");
+ reset(false /* abort */, info, "notifyWindowsDrawn - all windows drawn",
+ timestampNs /* timestampNs */);
}
return infoSnapshot;
}
@@ -480,7 +497,7 @@ class ActivityMetricsLogger {
* @param windowingModeToReason A map from windowing mode to a reason integer, which must be on
* of ActivityTaskManagerInternal.APP_TRANSITION_* reasons.
*/
- void notifyTransitionStarting(SparseIntArray windowingModeToReason, long timestamp) {
+ void notifyTransitionStarting(SparseIntArray windowingModeToReason, long timestampNs) {
if (!isAnyTransitionActive() || mLoggedTransitionStarting) {
// Ignore calls to this made after a reset and prior to notifyActivityLaunching.
@@ -488,7 +505,7 @@ class ActivityMetricsLogger {
return;
}
if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting");
- mCurrentTransitionDelayMs = calculateDelay(timestamp);
+ mCurrentTransitionDelayMs = calculateDelay(timestampNs);
mLoggedTransitionStarting = true;
WindowingModeTransitionInfo foundInfo = null;
@@ -505,7 +522,8 @@ class ActivityMetricsLogger {
if (allWindowsDrawn()) {
// abort metrics collection if we cannot find a matching transition.
final boolean abortMetrics = foundInfo == null;
- reset(abortMetrics, foundInfo, "notifyTransitionStarting - all windows drawn");
+ reset(abortMetrics, foundInfo, "notifyTransitionStarting - all windows drawn",
+ timestampNs /* timestampNs */);
}
}
@@ -531,8 +549,8 @@ class ActivityMetricsLogger {
}
private boolean hasVisibleNonFinishingActivity(TaskRecord t) {
- for (int i = t.mActivities.size() - 1; i >= 0; --i) {
- final ActivityRecord r = t.mActivities.get(i);
+ for (int i = t.getChildCount() - 1; i >= 0; --i) {
+ final ActivityRecord r = t.getChildAt(i);
if (r.visible && !r.finishing) {
return true;
}
@@ -571,7 +589,8 @@ class ActivityMetricsLogger {
logAppTransitionCancel(info);
mWindowingModeTransitionInfo.remove(r.getWindowingMode());
if (mWindowingModeTransitionInfo.size() == 0) {
- reset(true /* abort */, info, "notifyVisibilityChanged to invisible");
+ reset(true /* abort */, info, "notifyVisibilityChanged to invisible",
+ 0L /* timestampNs */);
}
}
}
@@ -592,9 +611,10 @@ class ActivityMetricsLogger {
}
}
- private boolean allWindowsDrawn() {
+ @VisibleForTesting
+ boolean allWindowsDrawn() {
for (int index = mWindowingModeTransitionInfo.size() - 1; index >= 0; index--) {
- if (!mWindowingModeTransitionInfo.valueAt(index).loggedWindowsDrawn) {
+ if (mWindowingModeTransitionInfo.valueAt(index).numUndrawnActivities != 0) {
return false;
}
}
@@ -602,12 +622,16 @@ class ActivityMetricsLogger {
}
private boolean isAnyTransitionActive() {
- return mCurrentTransitionStartTime != INVALID_START_TIME
+ return mCurrentTransitionStartTimeNs != INVALID_START_TIME
&& mWindowingModeTransitionInfo.size() > 0;
}
- private void reset(boolean abort, WindowingModeTransitionInfo info, String cause) {
- if (DEBUG_METRICS) Slog.i(TAG, "reset abort=" + abort + ",cause=" + cause);
+ private void reset(boolean abort, WindowingModeTransitionInfo info, String cause,
+ long timestampNs) {
+ if (DEBUG_METRICS) {
+ Slog.i(TAG,
+ "reset abort=" + abort + ",cause=" + cause + ",timestamp=" + timestampNs);
+ }
if (!abort && isAnyTransitionActive()) {
logAppTransitionMultiEvents();
}
@@ -619,13 +643,13 @@ class ActivityMetricsLogger {
if (abort) {
launchObserverNotifyActivityLaunchCancelled(info);
} else {
- launchObserverNotifyActivityLaunchFinished(info);
+ launchObserverNotifyActivityLaunchFinished(info, timestampNs);
}
} else {
launchObserverNotifyIntentFailed();
}
- mCurrentTransitionStartTime = INVALID_START_TIME;
+ mCurrentTransitionStartTimeNs = INVALID_START_TIME;
mCurrentTransitionDelayMs = INVALID_DELAY;
mLoggedTransitionStarting = false;
mWindowingModeTransitionInfo.clear();
@@ -633,12 +657,14 @@ class ActivityMetricsLogger {
private int calculateCurrentDelay() {
// Shouldn't take more than 25 days to launch an app, so int is fine here.
- return (int) (SystemClock.uptimeMillis() - mCurrentTransitionStartTime);
+ return (int) TimeUnit.NANOSECONDS
+ .toMillis(SystemClock.elapsedRealtimeNanos() - mCurrentTransitionStartTimeNs);
}
- private int calculateDelay(long timestamp) {
+ private int calculateDelay(long timestampNs) {
// Shouldn't take more than 25 days to launch an app, so int is fine here.
- return (int) (timestamp - mCurrentTransitionStartTime);
+ return (int) TimeUnit.NANOSECONDS.toMillis(timestampNs -
+ mCurrentTransitionStartTimeNs);
}
private void logAppTransitionCancel(WindowingModeTransitionInfo info) {
@@ -685,7 +711,7 @@ class ActivityMetricsLogger {
// Take a snapshot of the transition info before sending it to the handler for logging.
// This will avoid any races with other operations that modify the ActivityRecord.
final WindowingModeTransitionInfoSnapshot infoSnapshot =
- new WindowingModeTransitionInfoSnapshot(info);
+ new WindowingModeTransitionInfoSnapshot(info);
final int currentTransitionDeviceUptime = mCurrentTransitionDeviceUptime;
final int currentTransitionDelayMs = mCurrentTransitionDelayMs;
BackgroundThread.getHandler().post(() -> logAppTransition(
@@ -832,7 +858,9 @@ class ActivityMetricsLogger {
final LogMaker builder = new LogMaker(APP_TRANSITION_REPORTED_DRAWN);
builder.setPackageName(r.packageName);
builder.addTaggedData(FIELD_CLASS_NAME, r.info.name);
- long startupTimeMs = SystemClock.uptimeMillis() - mLastTransitionStartTime;
+ long currentTimestampNs = SystemClock.elapsedRealtimeNanos();
+ long startupTimeMs =
+ TimeUnit.NANOSECONDS.toMillis(currentTimestampNs - mLastTransitionStartTimeNs);
builder.addTaggedData(APP_TRANSITION_REPORTED_DRAWN_MS, startupTimeMs);
builder.setType(restoredFromBundle
? TYPE_TRANSITION_REPORTED_DRAWN_WITH_BUNDLE
@@ -858,6 +886,10 @@ class ActivityMetricsLogger {
final WindowingModeTransitionInfoSnapshot infoSnapshot =
new WindowingModeTransitionInfoSnapshot(info, r, (int) startupTimeMs);
BackgroundThread.getHandler().post(() -> logAppFullyDrawn(infoSnapshot));
+
+ // Notify reportFullyDrawn event.
+ launchObserverNotifyReportFullyDrawn(r, currentTimestampNs);
+
return infoSnapshot;
}
@@ -1027,12 +1059,12 @@ class ActivityMetricsLogger {
}
/** Notify the {@link ActivityMetricsLaunchObserver} that a new launch sequence has begun. */
- private void launchObserverNotifyIntentStarted(Intent intent) {
+ private void launchObserverNotifyIntentStarted(Intent intent, long timestampNs) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"MetricsLogger:launchObserverNotifyIntentStarted");
// Beginning a launch is timing sensitive and so should be observed as soon as possible.
- mLaunchObserver.onIntentStarted(intent);
+ mLaunchObserver.onIntentStarted(intent, timestampNs);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -1070,6 +1102,16 @@ class ActivityMetricsLogger {
}
/**
+ * Notifies the {@link ActivityMetricsLaunchObserver} the reportFullDrawn event.
+ */
+ private void launchObserverNotifyReportFullyDrawn(ActivityRecord r, long timestampNs) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "MetricsLogger:launchObserverNotifyReportFullyDrawn");
+ mLaunchObserver.onReportFullyDrawn(convertActivityRecordToProto(r), timestampNs);
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ /**
* Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence is
* cancelled.
*/
@@ -1089,12 +1131,14 @@ class ActivityMetricsLogger {
* Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence's activity
* has fully finished (successfully).
*/
- private void launchObserverNotifyActivityLaunchFinished(WindowingModeTransitionInfo info) {
+ private void launchObserverNotifyActivityLaunchFinished(WindowingModeTransitionInfo info,
+ long timestampNs) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"MetricsLogger:launchObserverNotifyActivityLaunchFinished");
- mLaunchObserver.onActivityLaunchFinished(
- convertActivityRecordToProto(info.launchedActivity));
+ mLaunchObserver
+ .onActivityLaunchFinished(convertActivityRecordToProto(info.launchedActivity),
+ timestampNs);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c6d550515a8d..de6137ee6abd 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -41,6 +41,10 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+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.content.Intent.ACTION_MAIN;
import static android.content.Intent.CATEGORY_HOME;
@@ -61,7 +65,6 @@ import static android.content.pm.ActivityInfo.FLAG_INHERIT_SHOW_WHEN_LOCKED;
import static android.content.pm.ActivityInfo.FLAG_MULTIPROCESS;
import static android.content.pm.ActivityInfo.FLAG_NO_HISTORY;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
-import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
import static android.content.pm.ActivityInfo.FLAG_STATE_NOT_NEEDED;
import static android.content.pm.ActivityInfo.FLAG_TURN_SCREEN_ON;
import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
@@ -78,6 +81,8 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
import static android.content.res.Configuration.EMPTY;
@@ -89,14 +94,28 @@ import static android.content.res.Configuration.UI_MODE_TYPE_VR_HEADSET;
import static android.os.Build.VERSION_CODES.HONEYCOMB;
import static android.os.Build.VERSION_CODES.O;
import static android.os.Process.SYSTEM_UID;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.Display.COLOR_MODE_DEFAULT;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
+import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
+import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
+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_BEHIND;
+import static android.view.WindowManager.TRANSIT_UNSET;
+import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
-import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityRecordProto.APP_WINDOW_TOKEN;
import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
import static com.android.server.am.ActivityRecordProto.IDENTIFIER;
import static com.android.server.am.ActivityRecordProto.PROC_ID;
@@ -105,6 +124,8 @@ import static com.android.server.am.ActivityRecordProto.TRANSLUCENT;
import static com.android.server.am.ActivityRecordProto.VISIBLE;
import static com.android.server.am.EventLogTags.AM_RELAUNCH_ACTIVITY;
import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY;
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
import static com.android.server.wm.ActivityStack.ActivityState.FINISHING;
@@ -151,16 +172,52 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutLocked;
+import static com.android.server.wm.AppWindowTokenProto.ALL_DRAWN;
+import static com.android.server.wm.AppWindowTokenProto.APP_STOPPED;
+import static com.android.server.wm.AppWindowTokenProto.CLIENT_HIDDEN;
+import static com.android.server.wm.AppWindowTokenProto.DEFER_HIDING_CLIENT;
+import static com.android.server.wm.AppWindowTokenProto.FILLS_PARENT;
+import static com.android.server.wm.AppWindowTokenProto.FROZEN_BOUNDS;
+import static com.android.server.wm.AppWindowTokenProto.HIDDEN_REQUESTED;
+import static com.android.server.wm.AppWindowTokenProto.HIDDEN_SET_FROM_TRANSFERRED_STARTING_WINDOW;
+import static com.android.server.wm.AppWindowTokenProto.IS_REALLY_ANIMATING;
+import static com.android.server.wm.AppWindowTokenProto.IS_WAITING_FOR_TRANSITION_START;
+import static com.android.server.wm.AppWindowTokenProto.LAST_ALL_DRAWN;
+import static com.android.server.wm.AppWindowTokenProto.LAST_SURFACE_SHOWING;
+import static com.android.server.wm.AppWindowTokenProto.NAME;
+import static com.android.server.wm.AppWindowTokenProto.NUM_DRAWN_WINDOWS;
+import static com.android.server.wm.AppWindowTokenProto.NUM_INTERESTING_WINDOWS;
+import static com.android.server.wm.AppWindowTokenProto.REMOVED;
+import static com.android.server.wm.AppWindowTokenProto.REPORTED_DRAWN;
+import static com.android.server.wm.AppWindowTokenProto.REPORTED_VISIBLE;
+import static com.android.server.wm.AppWindowTokenProto.STARTING_DISPLAYED;
+import static com.android.server.wm.AppWindowTokenProto.STARTING_MOVED;
+import static com.android.server.wm.AppWindowTokenProto.STARTING_WINDOW;
+import static com.android.server.wm.AppWindowTokenProto.THUMBNAIL;
+import static com.android.server.wm.AppWindowTokenProto.WINDOW_TOKEN;
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_APP_TRANSITIONS;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
+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.TaskPersister.DEBUG;
import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
+import static com.android.server.wm.WindowManagerService.logWithStack;
+import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY;
+import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
@@ -169,7 +226,9 @@ import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.Size;
import android.app.Activity;
+import android.app.ActivityManager;
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityOptions;
import android.app.PendingIntent;
@@ -199,8 +258,11 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.GraphicBuffer;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Binder;
@@ -212,6 +274,7 @@ import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.service.voice.IVoiceInteractionSession;
@@ -225,24 +288,36 @@ import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.view.AppTransitionAnimationSpec;
import android.view.DisplayCutout;
+import android.view.DisplayInfo;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IApplicationToken;
+import android.view.InputApplicationHandle;
+import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
+import android.view.animation.Animation;
-import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.ReferrerIntent;
+import com.android.internal.R;
+import com.android.internal.util.ToBooleanFunction;
import com.android.internal.util.XmlUtils;
import com.android.server.AttributeCache;
-import com.android.server.AttributeCache.Entry;
+import com.android.server.LocalServices;
import com.android.server.am.AppTimeTracker;
import com.android.server.am.EventLogTags;
import com.android.server.am.PendingIntentRecord;
+import com.android.server.display.color.ColorDisplayService;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.protolog.common.ProtoLog;
import com.android.server.uri.UriPermissionOwner;
import com.android.server.wm.ActivityMetricsLogger.WindowingModeTransitionInfoSnapshot;
import com.android.server.wm.ActivityStack.ActivityState;
+import com.android.server.wm.WindowManagerService.H;
import com.google.android.collect.Sets;
@@ -254,16 +329,18 @@ import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
+import java.util.function.Consumer;
/**
* An entry in the history stack, representing an activity.
*/
-public final class ActivityRecord extends ConfigurationContainer {
+public final class ActivityRecord extends WindowToken implements WindowManagerService.AppFreezeListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityRecord" : TAG_ATM;
private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
private static final String TAG_APP = TAG + POSTFIX_APP;
@@ -292,29 +369,43 @@ public final class ActivityRecord extends ConfigurationContainer {
// How many activities have to be scheduled to stop to force a stop pass.
private static final int MAX_STOPPING_TO_FORCE = 3;
- final ActivityTaskManagerService mAtmService; // owner
- final IApplicationToken.Stub appToken; // window manager token
- // TODO: Remove after unification
- AppWindowToken mAppWindowToken;
+ private static final int STARTING_WINDOW_TYPE_NONE = 0;
+ private static final int STARTING_WINDOW_TYPE_SNAPSHOT = 1;
+ private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2;
+
+ /**
+ * Value to increment the z-layer when boosting a layer during animations. BOOST in l33tsp34k.
+ */
+ @VisibleForTesting static final int Z_BOOST_BASE = 800570000;
+ final ActivityTaskManagerService mAtmService;
final ActivityInfo info; // activity info provided by developer in AndroidManifest
+ // Non-null only for application tokens.
+ // TODO: rename to mActivityToken
+ final ActivityRecord.Token appToken;
+ // Which user is this running for?
+ final int mUserId;
+ // The package implementing intent's component
+ // TODO: rename to mPackageName
+ public final String packageName;
+ // the intent component, or target of an alias.
+ final ComponentName mActivityComponent;
+ // Has a wallpaper window as a background.
+ // TODO: Rename to mHasWallpaper and also see if it possible to combine this with the
+ // mOccludesParent field.
+ final boolean hasWallpaper;
+ // Input application handle used by the input dispatcher.
+ final InputApplicationHandle mInputApplicationHandle;
+
final int launchedFromPid; // always the pid who started the activity.
final int launchedFromUid; // always the uid who started the activity.
final String launchedFromPackage; // always the package who started the activity.
- final int mUserId; // Which user is this running for?
final Intent intent; // the original intent that generated us
- final ComponentName mActivityComponent; // the intent component, or target of an alias.
final String shortComponentName; // the short component name of the intent
final String resolvedType; // as per original caller;
- public final String packageName; // the package implementing intent's component
final String processName; // process where this component wants to run
final String taskAffinity; // as per ActivityInfo.taskAffinity
final boolean stateNotNeeded; // As per ActivityInfo.flags
- boolean fullscreen; // The activity is opaque and fills the entire space of this task.
- // TODO: See if it possible to combine this with the fullscreen field.
- final boolean hasWallpaper; // Has a wallpaper window as a background.
- @VisibleForTesting
- boolean noDisplay; // activity is not displayed?
@VisibleForTesting
int mHandoverLaunchDisplayId = INVALID_DISPLAY; // Handover launch display id to next activity.
private final boolean componentSpecified; // did caller specify an explicit component?
@@ -325,7 +416,6 @@ public final class ActivityRecord extends ConfigurationContainer {
private int icon; // resource identifier of activity's icon.
private int logo; // resource identifier of activity's logo.
private int theme; // resource identifier of activity's theme.
- private int realTheme; // actual theme resource we will use, never 0.
private int windowFlags; // custom window flags for preview window.
public int perfActivityBoostHandler = -1; //perflock handler when activity is created.
private TaskRecord task; // the task this is in.
@@ -347,6 +437,7 @@ public final class ActivityRecord extends ConfigurationContainer {
ArrayList<ResultInfo> results; // pending ActivityResult objs we have received
HashSet<WeakReference<PendingIntentRecord>> pendingResults; // all pending intents for this act
ArrayList<ReferrerIntent> newIntents; // any pending new intents for single-top mode
+ Intent mLastNewIntent; // the last new intent we delivered to client
ActivityOptions pendingOptions; // most recently given options
ActivityOptions returningOptions; // options that are coming back via convertToTranslucent
AppTimeTracker appTimeTracker; // set if we are tracking the time in this app/task/activity
@@ -376,8 +467,14 @@ public final class ActivityRecord extends ConfigurationContainer {
boolean visible; // does this activity's window need to be shown?
boolean visibleIgnoringKeyguard; // is this activity visible, ignoring the fact that Keyguard
// might hide this activity?
+ // True if the hidden state of this token was forced to false due to a transferred starting
+ // window.
+ private boolean mHiddenSetFromTransferredStartingWindow;
+ // TODO: figureout how to consolidate with the same variable in ActivityRecord.
private boolean mDeferHidingClient; // If true we told WM to defer reporting to the client
// process that it is hidden.
+ private boolean mLastDeferHidingClient; // If true we will defer setting mClientHidden to true
+ // and reporting to the client that it is hidden.
boolean sleeping; // have we told the activity to sleep?
public boolean launching; // is activity launch in progress?
boolean nowVisible; // is this activity's window visible?
@@ -396,8 +493,6 @@ public final class ActivityRecord extends ConfigurationContainer {
long lastLaunchTime; // time of last launch of this activity
ComponentName requestedVrComponent; // the requested component for handling VR mode.
- String stringName; // for caching of toString().
-
private boolean inHistory; // are we in the history stack?
final ActivityStackSupervisor mStackSupervisor;
final RootActivityContainer mRootActivityContainer;
@@ -414,8 +509,6 @@ public final class ActivityRecord extends ConfigurationContainer {
int mRelaunchReason = RELAUNCH_REASON_NONE;
TaskDescription taskDescription; // the recents information for this activity
- boolean mLaunchTaskBehind; // this activity is actively being launched with
- // ActivityOptions.setLaunchTaskBehind, will be cleared once launch is completed.
// These configurations are collected from application's resources based on size-sensitive
// qualifiers. For example, layout-w800dp will be added to mHorizontalSizeConfigurations as 800
@@ -436,17 +529,188 @@ public final class ActivityRecord extends ConfigurationContainer {
public BoostFramework mPerf = null;
public BoostFramework mPerf_iop = null;
- // A hint to override the window specified rotation animation, or -1
- // to use the window specified value. We use this so that
- // we can select the right animation in the cases of starting
- // windows, where the app hasn't had time to set a value
- // on the window.
- int mRotationAnimationHint = -1;
+ boolean mVoiceInteraction;
+
+ private int mPendingRelaunchCount;
+
+ // True if we are current in the process of removing this app token from the display
+ private boolean mRemovingFromDisplay = false;
+
+ // Flag set while reparenting to prevent actions normally triggered by an individual parent
+ // change.
+ private boolean mReparenting;
+
+ private RemoteAnimationDefinition mRemoteAnimationDefinition;
+
+ private AnimatingActivityRegistry mAnimatingActivityRegistry;
+
+ private Task mLastParent;
+
+ // Have we told the window clients to hide themselves?
+ private boolean mClientHidden;
+
+ boolean firstWindowDrawn;
+ // Last drawn state we reported to the app token.
+ private boolean reportedDrawn;
+ private final WindowState.UpdateReportedVisibilityResults mReportedVisibilityResults =
+ new WindowState.UpdateReportedVisibilityResults();
+
+ private boolean mUseTransferredAnimation;
+
+ /**
+ * @see #currentLaunchCanTurnScreenOn()
+ */
+ private boolean mCurrentLaunchCanTurnScreenOn = true;
+
+ /**
+ * This gets used during some open/close transitions as well as during a change transition
+ * where it represents the starting-state snapshot.
+ */
+ private AppWindowThumbnail mThumbnail;
+ private final Rect mTransitStartRect = new Rect();
+
+ /**
+ * If we are running an animation, this determines the transition type. Must be one of
+ * AppTransition.TRANSIT_* constants.
+ */
+ private int mTransit;
+
+ /**
+ * If we are running an animation, this determines the flags during this animation. Must be a
+ * bitwise combination of AppTransition.TRANSIT_FLAG_* constants.
+ */
+ private int mTransitFlags;
+ /**
+ * This leash is used to "freeze" the app surface in place after the state change, but before
+ * the animation is ready to start.
+ */
+ private SurfaceControl mTransitChangeLeash = null;
+
+ /** Whether our surface was set to be showing in the last call to {@link #prepareSurfaces} */
+ private boolean mLastSurfaceShowing = true;
+
+ private Letterbox mLetterbox;
+
+ /**
+ * The activity is opaque and fills the entire space of this task.
+ * @see WindowContainer#fillsParent()
+ */
+ private boolean mOccludesParent;
+
+ // The input dispatching timeout for this application token in nanoseconds.
+ long mInputDispatchingTimeoutNanos;
private boolean mShowWhenLocked;
private boolean mInheritShownWhenLocked;
private boolean mTurnScreenOn;
+ /** Have we been asked to have this token keep the screen frozen? */
+ private boolean mFreezingScreen;
+
+ // These are used for determining when all windows associated with
+ // an activity have been drawn, so they can be made visible together
+ // at the same time.
+ // initialize so that it doesn't match mTransactionSequence which is an int.
+ private long mLastTransactionSequence = Long.MIN_VALUE;
+ private int mNumInterestingWindows;
+ private int mNumDrawnWindows;
+ boolean inPendingTransaction;
+ boolean allDrawn;
+ private boolean mLastAllDrawn;
+
+ private boolean mLastContainsShowWhenLockedWindow;
+ private boolean mLastContainsDismissKeyguardWindow;
+
+ /**
+ * A flag to determine if this AR is in the process of closing or entering PIP. This is needed
+ * to help AR know that the app is in the process of closing but hasn't yet started closing on
+ * the WM side.
+ */
+ private boolean mWillCloseOrEnterPip;
+
+ /**
+ * The scale to fit at least one side of the activity to its parent. If the activity uses
+ * 1920x1080, and the actually size on the screen is 960x540, then the scale is 0.5.
+ */
+ private float mSizeCompatScale = 1f;
+ /**
+ * The bounds in global coordinates for activity in size compatibility mode.
+ * @see ActivityRecord#hasSizeCompatBounds()
+ */
+ private Rect mSizeCompatBounds;
+
+ // activity is not displayed?
+ // TODO: rename to mNoDisplay
+ @VisibleForTesting
+ boolean noDisplay;
+ boolean mShowForAllUsers;
+ // TODO: Make this final
+ int mTargetSdk;
+
+ // Set to true when this app creates a surface while in the middle of an animation. In that
+ // case do not clear allDrawn until the animation completes.
+ boolean deferClearAllDrawn;
+
+ // Is this window's surface needed? This is almost like hidden, except
+ // it will sometimes be true a little earlier: when the token has
+ // been shown, but is still waiting for its app transition to execute
+ // before making its windows shown.
+ boolean hiddenRequested;
+
+ // Last visibility state we reported to the app token.
+ boolean reportedVisible;
+
+ // Set to true when the token has been removed from the window mgr.
+ boolean removed;
+
+ boolean mDisablePreviewScreenshots;
+
+ // Information about an application starting window if displayed.
+ // Note: these are de-referenced before the starting window animates away.
+ StartingData mStartingData;
+ WindowState startingWindow;
+ WindowManagerPolicy.StartingSurface startingSurface;
+ boolean startingDisplayed;
+ boolean startingMoved;
+
+ // TODO: Have a WindowContainer state for tracking exiting/deferred removal.
+ boolean mIsExiting;
+
+ boolean mLaunchTaskBehind;
+ boolean mEnteringAnimation;
+
+ boolean mAppStopped;
+ // A hint to override the window specified rotation animation, or -1 to use the window specified
+ // value. We use this so that we can select the right animation in the cases of starting
+ // windows, where the app hasn't had time to set a value on the window.
+ int mRotationAnimationHint = -1;
+
+ ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>();
+ ArrayDeque<Configuration> mFrozenMergedConfig = new ArrayDeque<>();
+
+ /** Whether this token should be boosted at the top of all app window tokens. */
+ @VisibleForTesting boolean mNeedsZBoost;
+
+ /** Layer used to constrain the animation to a token's stack bounds. */
+ SurfaceControl mAnimationBoundsLayer;
+
+ /** Whether this token needs to create mAnimationBoundsLayer for cropping animations. */
+ boolean mNeedsAnimationBoundsLayer;
+
+ private AppSaturationInfo mLastAppSaturationInfo;
+
+ private final ColorDisplayService.ColorTransformController mColorTransformController =
+ (matrix, translation) -> mWmService.mH.post(() -> {
+ synchronized (mWmService.mGlobalLock) {
+ if (mLastAppSaturationInfo == null) {
+ mLastAppSaturationInfo = new AppSaturationInfo();
+ }
+
+ mLastAppSaturationInfo.setSaturation(matrix, translation);
+ updateColorTransform();
+ }
+ });
+
/**
* Current sequencing integer of the configuration, for skipping old activity configurations.
*/
@@ -458,6 +722,10 @@ public final class ActivityRecord extends ConfigurationContainer {
private final Configuration mTmpConfig = new Configuration();
private final Rect mTmpBounds = new Rect();
+ private final Point mTmpPoint = new Point();
+ private final Rect mTmpRect = new Rect();
+ private final Rect mTmpPrevBounds = new Rect();
+
// Token for targeting this activity for assist purposes.
final Binder assistToken = new Binder();
@@ -475,6 +743,13 @@ public final class ActivityRecord extends ConfigurationContainer {
}
void dump(PrintWriter pw, String prefix) {
+ }
+
+ /**
+ * Copied from old AppWindowToken.
+ */
+ @Override
+ void dump(PrintWriter pw, String prefix, boolean dumpAll) {
final long now = SystemClock.uptimeMillis();
pw.print(prefix); pw.print("packageName="); pw.print(packageName);
pw.print(" processName="); pw.println(processName);
@@ -609,7 +884,7 @@ public final class ActivityRecord extends ConfigurationContainer {
pw.print(" idle="); pw.print(idle);
pw.print(" mStartingWindowState=");
pw.println(startingWindowStateToString(mStartingWindowState));
- pw.print(prefix); pw.print("fullscreen="); pw.print(fullscreen);
+ pw.print(prefix); pw.print("occludesParent="); pw.print(occludesParent());
pw.print(" noDisplay="); pw.print(noDisplay);
pw.print(" immersive="); pw.print(immersive);
pw.print(" launchMode="); pw.println(launchMode);
@@ -622,6 +897,64 @@ public final class ActivityRecord extends ConfigurationContainer {
pw.print("requestedVrComponent=");
pw.println(requestedVrComponent);
}
+ super.dump(pw, prefix, dumpAll);
+ if (appToken != null) {
+ pw.println(prefix + "app=true mVoiceInteraction=" + mVoiceInteraction);
+ }
+ pw.print(prefix); pw.print(" mOccludesParent="); pw.print(mOccludesParent);
+ pw.print(" mOrientation="); pw.println(mOrientation);
+ pw.println(prefix + "hiddenRequested=" + hiddenRequested + " mClientHidden=" + mClientHidden
+ + ((mDeferHidingClient) ? " mDeferHidingClient=" + mDeferHidingClient : "")
+ + " reportedDrawn=" + reportedDrawn + " reportedVisible=" + reportedVisible);
+ if (paused) {
+ pw.print(prefix); pw.print("paused="); pw.println(paused);
+ }
+ if (mAppStopped) {
+ pw.print(prefix); pw.print("mAppStopped="); pw.println(mAppStopped);
+ }
+ if (mNumInterestingWindows != 0 || mNumDrawnWindows != 0
+ || allDrawn || mLastAllDrawn) {
+ pw.print(prefix); pw.print("mNumInterestingWindows=");
+ pw.print(mNumInterestingWindows);
+ pw.print(" mNumDrawnWindows="); pw.print(mNumDrawnWindows);
+ pw.print(" inPendingTransaction="); pw.print(inPendingTransaction);
+ pw.print(" allDrawn="); pw.print(allDrawn);
+ pw.print(" lastAllDrawn="); pw.print(mLastAllDrawn);
+ pw.println(")");
+ }
+ if (inPendingTransaction) {
+ pw.print(prefix); pw.print("inPendingTransaction=");
+ pw.println(inPendingTransaction);
+ }
+ if (mStartingData != null || removed || firstWindowDrawn || mIsExiting) {
+ pw.print(prefix); pw.print("startingData="); pw.print(mStartingData);
+ pw.print(" removed="); pw.print(removed);
+ pw.print(" firstWindowDrawn="); pw.print(firstWindowDrawn);
+ pw.print(" mIsExiting="); pw.println(mIsExiting);
+ }
+ if (startingWindow != null || startingSurface != null
+ || startingDisplayed || startingMoved || mHiddenSetFromTransferredStartingWindow) {
+ pw.print(prefix); pw.print("startingWindow="); pw.print(startingWindow);
+ pw.print(" startingSurface="); pw.print(startingSurface);
+ pw.print(" startingDisplayed="); pw.print(startingDisplayed);
+ pw.print(" startingMoved="); pw.print(startingMoved);
+ pw.println(" mHiddenSetFromTransferredStartingWindow="
+ + mHiddenSetFromTransferredStartingWindow);
+ }
+ if (!mFrozenBounds.isEmpty()) {
+ pw.print(prefix); pw.print("mFrozenBounds="); pw.println(mFrozenBounds);
+ pw.print(prefix); pw.print("mFrozenMergedConfig="); pw.println(mFrozenMergedConfig);
+ }
+ if (mPendingRelaunchCount != 0) {
+ pw.print(prefix); pw.print("mPendingRelaunchCount="); pw.println(mPendingRelaunchCount);
+ }
+ if (mSizeCompatScale != 1f || mSizeCompatBounds != null) {
+ pw.println(prefix + "mSizeCompatScale=" + mSizeCompatScale + " mSizeCompatBounds="
+ + mSizeCompatBounds);
+ }
+ if (mRemovingFromDisplay) {
+ pw.println(prefix + "mRemovingFromDisplay=" + mRemovingFromDisplay);
+ }
if (lastVisibleTime != 0 || nowVisible) {
pw.print(prefix); pw.print(" nowVisible="); pw.print(nowVisible);
pw.print(" lastVisibleTime=");
@@ -633,7 +966,8 @@ public final class ActivityRecord extends ConfigurationContainer {
pw.println(prefix + "mDeferHidingClient=" + mDeferHidingClient);
}
if (deferRelaunchUntilPaused || configChangeFlags != 0) {
- pw.print(prefix); pw.print("deferRelaunchUntilPaused="); pw.print(deferRelaunchUntilPaused);
+ pw.print(prefix); pw.print("deferRelaunchUntilPaused=");
+ pw.print(deferRelaunchUntilPaused);
pw.print(" configChangeFlags=");
pw.println(Integer.toHexString(configChangeFlags));
}
@@ -861,22 +1195,7 @@ public final class ActivityRecord extends ConfigurationContainer {
}
}
- @Override
- protected int getChildCount() {
- // {@link ActivityRecord} is a leaf node and has no children.
- return 0;
- }
-
- @Override
- protected ConfigurationContainer getChildAt(int index) {
- return null;
- }
-
- @Override
- protected ConfigurationContainer getParent() {
- return getTaskRecord();
- }
-
+ // TODO: Remove once TaskRecord and Task are unified.
TaskRecord getTaskRecord() {
return task;
}
@@ -920,31 +1239,224 @@ public final class ActivityRecord extends ConfigurationContainer {
this.task = task;
- if (!reparenting) {
+ // This is attaching the activity to the task which we only want to do once.
+ // TODO: Need to re-work after unifying the task level since it will already have a parent
+ // then. Just need to restructure the re-parent case not to do this. NOTE that the
+ // reparenting flag passed in can't be used directly for this as it isn't set in
+ // ActivityRecord#reparent() case that ends up calling this method.
+ if (task != null && getParent() == null) {
+ inHistory = true;
+ final Task container = task.getTask();
+ if (container != null) {
+ onAttachToTask(task.voiceSession != null, container.getDisplayContent(),
+ getInputDispatchingTimeoutLocked(this) * 1000000L);
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE, "setTask: %s at top.", this);
+ container.addChild(this, Integer.MAX_VALUE /* add on top */);
+ }
+
+ // TODO(b/36505427): Maybe this call should be moved inside
+ // updateOverrideConfiguration()
+ task.updateOverrideConfigurationFromLaunchBounds();
+ // Make sure override configuration is up-to-date before using to create window
+ // controller.
+ updateOverrideConfiguration();
+
+ task.addActivityToTop(this);
+
+ // When an activity is started directly into a split-screen fullscreen stack, we need to
+ // update the initial multi-window modes so that the callbacks are scheduled correctly
+ // when the user leaves that mode.
+ mLastReportedMultiWindowMode = inMultiWindowMode();
+ mLastReportedPictureInPictureMode = inPinnedWindowingMode();
+ } else if (!reparenting) {
onParentChanged();
}
}
/**
- * Notifies AWT that this app is waiting to pause in order to determine if it will enter PIP.
- * This information helps AWT know that the app is in the process of pausing before it gets the
- * signal on the WM side.
+ * Sets the Task on this activity for the purposes of re-use during launch where we will
+ * re-use another activity instead of this one for the launch.
*/
- void setWillCloseOrEnterPip(boolean willCloseOrEnterPip) {
- if (mAppWindowToken == null) {
+ void setTaskForReuse(TaskRecord task) {
+ this.task = task;
+ }
+
+ Task getTask() {
+ return (Task) getParent();
+ }
+
+ TaskStack getStack() {
+ final Task task = getTask();
+ if (task != null) {
+ return task.mStack;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ void onParentChanged() {
+ super.onParentChanged();
+
+ final Task task = getTask();
+
+ // When the associated task is {@code null}, the {@link ActivityRecord} can no longer
+ // access visual elements like the {@link DisplayContent}. We must remove any associations
+ // such as animations.
+ if (!mReparenting) {
+ if (task == null) {
+ // It is possible we have been marked as a closing app earlier. We must remove ourselves
+ // from this list so we do not participate in any future animations.
+ if (getDisplayContent() != null) {
+ getDisplayContent().mClosingApps.remove(this);
+ }
+ } else if (mLastParent != null && mLastParent.mStack != null) {
+ task.mStack.mExitingActivities.remove(this);
+ }
+ }
+ final TaskStack stack = getStack();
+
+ // If we reparent, make sure to remove ourselves from the old animation registry.
+ if (mAnimatingActivityRegistry != null) {
+ mAnimatingActivityRegistry.notifyFinished(this);
+ }
+ mAnimatingActivityRegistry = stack != null
+ ? stack.getAnimatingActivityRegistry()
+ : null;
+
+ mLastParent = task;
+
+ updateColorTransform();
+ }
+
+ private void updateColorTransform() {
+ if (mSurfaceControl != null && mLastAppSaturationInfo != null) {
+ getPendingTransaction().setColorTransform(mSurfaceControl,
+ mLastAppSaturationInfo.mMatrix, mLastAppSaturationInfo.mTranslation);
+ mWmService.scheduleAnimationLocked();
+ }
+ }
+
+ @Override
+ void onDisplayChanged(DisplayContent dc) {
+ DisplayContent prevDc = mDisplayContent;
+ super.onDisplayChanged(dc);
+ if (prevDc == null || prevDc == mDisplayContent) {
+ return;
+ }
+
+ if (prevDc.mOpeningApps.remove(this)) {
+ // Transfer opening transition to new display.
+ mDisplayContent.mOpeningApps.add(this);
+ mDisplayContent.prepareAppTransition(prevDc.mAppTransition.getAppTransition(), true);
+ mDisplayContent.executeAppTransition();
+ }
+
+ if (prevDc.mChangingApps.remove(this)) {
+ // This gets called *after* the ActivityRecord has been reparented to the new display.
+ // That reparenting resulted in this window changing modes (eg. FREEFORM -> FULLSCREEN),
+ // so this token is now "frozen" while waiting for the animation to start on prevDc
+ // (which will be cancelled since the window is no-longer a child). However, since this
+ // is no longer a child of prevDc, this won't be notified of the cancelled animation,
+ // so we need to cancel the change transition here.
+ clearChangeLeash(getPendingTransaction(), true /* cancel */);
+ }
+ prevDc.mClosingApps.remove(this);
+
+ if (prevDc.mFocusedApp == this) {
+ prevDc.setFocusedApp(null);
+ final TaskStack stack = dc.getTopStack();
+ if (stack != null) {
+ final Task task = stack.getTopChild();
+ if (task != null && task.getTopChild() == this) {
+ dc.setFocusedApp(this);
+ }
+ }
+ }
+
+ if (mLetterbox != null) {
+ mLetterbox.onMovedToDisplay(mDisplayContent.getDisplayId());
+ }
+ }
+
+ void layoutLetterbox(WindowState winHint) {
+ final WindowState w = findMainWindow();
+ if (w == null || winHint != null && w != winHint) {
return;
}
+ final boolean surfaceReady = w.isDrawnLw() // Regular case
+ || w.mWinAnimator.mSurfaceDestroyDeferred // The preserved surface is still ready.
+ || w.isDragResizeChanged(); // Waiting for relayoutWindow to call preserveSurface.
+ final boolean needsLetterbox = surfaceReady && w.isLetterboxedAppWindow() && fillsParent();
+ if (needsLetterbox) {
+ if (mLetterbox == null) {
+ mLetterbox = new Letterbox(() -> makeChildSurface(null),
+ mWmService.mTransactionFactory);
+ mLetterbox.attachInput(w);
+ }
+ getPosition(mTmpPoint);
+ // Get the bounds of the "space-to-fill". In multi-window mode, the task-level
+ // represents this. In fullscreen-mode, the stack does (since the orientation letterbox
+ // is also applied to the task).
+ Rect spaceToFill = (inMultiWindowMode() || getStack() == null)
+ ? getTask().getDisplayedBounds() : getStack().getDisplayedBounds();
+ mLetterbox.layout(spaceToFill, w.getFrameLw(), mTmpPoint);
+ } else if (mLetterbox != null) {
+ mLetterbox.hide();
+ }
+ }
+
+ void updateLetterboxSurface(WindowState winHint) {
+ final WindowState w = findMainWindow();
+ if (w != winHint && winHint != null && w != null) {
+ return;
+ }
+ layoutLetterbox(winHint);
+ if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
+ mLetterbox.applySurfaceChanges(getPendingTransaction());
+ }
+ }
- mAppWindowToken.setWillCloseOrEnterPip(willCloseOrEnterPip);
+ Rect getLetterboxInsets() {
+ if (mLetterbox != null) {
+ return mLetterbox.getInsets();
+ } else {
+ return new Rect();
+ }
+ }
+
+ /** Gets the inner bounds of letterbox. The bounds will be empty if there is no letterbox. */
+ void getLetterboxInnerBounds(Rect outBounds) {
+ if (mLetterbox != null) {
+ outBounds.set(mLetterbox.getInnerFrame());
+ } else {
+ outBounds.setEmpty();
+ }
+ }
+
+ /**
+ * @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 final WeakReference<ActivityRecord> weakActivity;
+ private WeakReference<ActivityRecord> weakActivity;
private final String name;
+ private final String tokenString;
- Token(ActivityRecord activity, Intent intent) {
- weakActivity = new WeakReference<>(activity);
+ Token(Intent intent) {
name = intent.getComponent().flattenToShortString();
+ tokenString = "Token{" + Integer.toHexString(System.identityHashCode(this)) + "}";
+ }
+
+ private void attach(ActivityRecord activity) {
+ if (weakActivity != null) {
+ throw new IllegalStateException("Already attached..." + this);
+ }
+ weakActivity = new WeakReference<>(activity);
}
private static @Nullable ActivityRecord tokenToActivityRecordLocked(Token token) {
@@ -964,7 +1476,9 @@ public final class ActivityRecord extends ConfigurationContainer {
sb.append("Token{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(' ');
- sb.append(weakActivity.get());
+ if (weakActivity != null) {
+ sb.append(weakActivity.get());
+ }
sb.append('}');
return sb.toString();
}
@@ -1011,15 +1525,82 @@ public final class ActivityRecord extends ConfigurationContainer {
ActivityRecord _resultTo, String _resultWho, int _reqCode, boolean _componentSpecified,
boolean _rootVoiceInteraction, ActivityStackSupervisor supervisor,
ActivityOptions options, ActivityRecord sourceRecord) {
+ super(_service.mWindowManager, new Token(_intent).asBinder(), TYPE_APPLICATION, true,
+ null /* displayContent */, false /* ownerCanManageAppTokens */);
+
mAtmService = _service;
- mRootActivityContainer = _service.mRootActivityContainer;
- appToken = new Token(this, _intent);
+ appToken = (Token) token;
info = aInfo;
+ mUserId = UserHandle.getUserId(info.applicationInfo.uid);
+ packageName = info.applicationInfo.packageName;
+ mInputApplicationHandle = new InputApplicationHandle(appToken);
+ intent = _intent;
+
+ // If the class name in the intent doesn't match that of the target, this is probably an
+ // alias. We have to create a new ComponentName object to keep track of the real activity
+ // name, so that FLAG_ACTIVITY_CLEAR_TOP is handled properly.
+ if (info.targetActivity == null
+ || (info.targetActivity.equals(intent.getComponent().getClassName())
+ && (info.launchMode == LAUNCH_MULTIPLE
+ || info.launchMode == LAUNCH_SINGLE_TOP))) {
+ mActivityComponent = intent.getComponent();
+ } else {
+ mActivityComponent =
+ new ComponentName(info.packageName, info.targetActivity);
+ }
+
+ mTargetSdk = info.applicationInfo.targetSdkVersion;
+ mShowForAllUsers = (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0;
+ setOrientation(info.screenOrientation);
+ mRotationAnimationHint = info.rotationAnimation;
+
+ mShowWhenLocked = (aInfo.flags & ActivityInfo.FLAG_SHOW_WHEN_LOCKED) != 0;
+ mInheritShownWhenLocked = (aInfo.privateFlags & FLAG_INHERIT_SHOW_WHEN_LOCKED) != 0;
+ mTurnScreenOn = (aInfo.flags & FLAG_TURN_SCREEN_ON) != 0;
+
+ int realTheme = info.getThemeResource();
+ if (realTheme == Resources.ID_NULL) {
+ realTheme = aInfo.applicationInfo.targetSdkVersion < HONEYCOMB
+ ? android.R.style.Theme : android.R.style.Theme_Holo;
+ }
+
+ final AttributeCache.Entry ent = AttributeCache.instance().get(packageName,
+ realTheme, com.android.internal.R.styleable.Window, mUserId);
+
+ if (ent != null) {
+ mOccludesParent = !ActivityInfo.isTranslucentOrFloating(ent.array);
+ hasWallpaper = ent.array.getBoolean(R.styleable.Window_windowShowWallpaper, false);
+ noDisplay = ent.array.getBoolean(R.styleable.Window_windowNoDisplay, false);
+ } else {
+ hasWallpaper = false;
+ noDisplay = false;
+ }
+
+ if (options != null) {
+ mLaunchTaskBehind = options.getLaunchTaskBehind();
+
+ final int rotationAnimation = options.getRotationAnimationHint();
+ // Only override manifest supplied option if set.
+ if (rotationAnimation >= 0) {
+ mRotationAnimationHint = rotationAnimation;
+ }
+ }
+
+ // Application tokens start out hidden.
+ setHidden(true);
+ hiddenRequested = true;
+
+ ColorDisplayService.ColorDisplayServiceInternal cds = LocalServices.getService(
+ ColorDisplayService.ColorDisplayServiceInternal.class);
+ cds.attachColorTransformController(packageName, mUserId,
+ new WeakReference<>(mColorTransformController));
+
+ appToken.attach(this);
+
+ mRootActivityContainer = _service.mRootActivityContainer;
launchedFromPid = _launchedFromPid;
launchedFromUid = _launchedFromUid;
launchedFromPackage = _launchedFromPackage;
- mUserId = UserHandle.getUserId(aInfo.applicationInfo.uid);
- intent = _intent;
shortComponentName = _intent.getComponent().flattenToShortString();
resolvedType = _resolvedType;
componentSpecified = _componentSpecified;
@@ -1043,17 +1624,6 @@ public final class ActivityRecord extends ConfigurationContainer {
hasBeenLaunched = false;
mStackSupervisor = supervisor;
- // If the class name in the intent doesn't match that of the target, this is
- // probably an alias. We have to create a new ComponentName object to keep track
- // of the real activity name, so that FLAG_ACTIVITY_CLEAR_TOP is handled properly.
- if (aInfo.targetActivity == null
- || (aInfo.targetActivity.equals(_intent.getComponent().getClassName())
- && (aInfo.launchMode == LAUNCH_MULTIPLE
- || aInfo.launchMode == LAUNCH_SINGLE_TOP))) {
- mActivityComponent = _intent.getComponent();
- } else {
- mActivityComponent = new ComponentName(aInfo.packageName, aInfo.targetActivity);
- }
taskAffinity = aInfo.taskAffinity;
stateNotNeeded = (aInfo.flags & FLAG_STATE_NOT_NEEDED) != 0;
nonLocalizedLabel = aInfo.nonLocalizedLabel;
@@ -1066,11 +1636,6 @@ public final class ActivityRecord extends ConfigurationContainer {
icon = aInfo.getIconResource();
logo = aInfo.getLogoResource();
theme = aInfo.getThemeResource();
- realTheme = theme;
- if (realTheme == 0) {
- realTheme = aInfo.applicationInfo.targetSdkVersion < HONEYCOMB
- ? android.R.style.Theme : android.R.style.Theme_Holo;
- }
if ((aInfo.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
windowFlags |= LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
@@ -1086,21 +1651,8 @@ public final class ActivityRecord extends ConfigurationContainer {
intent.addFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
}
- packageName = aInfo.applicationInfo.packageName;
launchMode = aInfo.launchMode;
- Entry ent = AttributeCache.instance().get(packageName,
- realTheme, com.android.internal.R.styleable.Window, mUserId);
-
- if (ent != null) {
- fullscreen = !ActivityInfo.isTranslucentOrFloating(ent.array);
- hasWallpaper = ent.array.getBoolean(R.styleable.Window_windowShowWallpaper, false);
- noDisplay = ent.array.getBoolean(R.styleable.Window_windowNoDisplay, false);
- } else {
- hasWallpaper = false;
- noDisplay = false;
- }
-
setActivityType(_componentSpecified, _launchedFromUid, _intent, options, sourceRecord);
immersive = (aInfo.flags & FLAG_IMMERSIVE) != 0;
@@ -1108,11 +1660,6 @@ public final class ActivityRecord extends ConfigurationContainer {
requestedVrComponent = (aInfo.requestedVrComponent == null) ?
null : ComponentName.unflattenFromString(aInfo.requestedVrComponent);
- mShowWhenLocked = (aInfo.flags & FLAG_SHOW_WHEN_LOCKED) != 0;
- mInheritShownWhenLocked = (aInfo.privateFlags & FLAG_INHERIT_SHOW_WHEN_LOCKED) != 0;
- mTurnScreenOn = (aInfo.flags & FLAG_TURN_SCREEN_ON) != 0;
-
- mRotationAnimationHint = aInfo.rotationAnimation;
lockTaskLaunchMode = aInfo.lockTaskLaunchMode;
if (info.applicationInfo.isPrivilegedApp()
&& (lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_ALWAYS
@@ -1122,13 +1669,6 @@ public final class ActivityRecord extends ConfigurationContainer {
if (options != null) {
pendingOptions = options;
- mLaunchTaskBehind = options.getLaunchTaskBehind();
-
- final int rotationAnimation = pendingOptions.getRotationAnimationHint();
- // Only override manifest supplied option if set.
- if (rotationAnimation >= 0) {
- mRotationAnimationHint = rotationAnimation;
- }
final PendingIntent usageReport = pendingOptions.getUsageTimeReport();
if (usageReport != null) {
appTimeTracker = new AppTimeTracker(usageReport);
@@ -1145,6 +1685,12 @@ public final class ActivityRecord extends ConfigurationContainer {
mPerf = new BoostFramework();
}
+ @Override
+ ActivityRecord asActivityRecord() {
+ // I am an activity record!
+ return this;
+ }
+
void setProcess(WindowProcessController proc) {
app = proc;
final ActivityRecord root = task != null ? task.getRootActivity() : null;
@@ -1161,97 +1707,280 @@ public final class ActivityRecord extends ConfigurationContainer {
return hasProcess() && app.hasThread();
}
- void createAppWindowToken() {
- if (mAppWindowToken != null) {
- throw new IllegalArgumentException("App Window Token=" + mAppWindowToken
- + " already created for r=" + this);
+ void onAttachToTask(boolean voiceInteraction, DisplayContent dc,
+ long inputDispatchingTimeoutNanos) {
+ mInputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
+ mVoiceInteraction = voiceInteraction;
+ onDisplayChanged(dc);
+
+ // Application tokens start out hidden.
+ setHidden(true);
+ hiddenRequested = true;
+ }
+
+ boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
+ CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
+ IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
+ boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
+ // If the display is frozen, we won't do anything until the actual window is
+ // displayed so there is no reason to put in the starting window.
+ if (!okToDisplay()) {
+ return false;
}
- inHistory = true;
+ if (mStartingData != null) {
+ return false;
+ }
- // TODO(b/36505427): Maybe this call should be moved inside updateOverrideConfiguration()
- task.updateOverrideConfigurationFromLaunchBounds();
- // Make sure override configuration is up-to-date before using to create window controller.
- updateOverrideConfiguration();
+ final WindowState mainWin = findMainWindow();
+ if (mainWin != null && mainWin.mWinAnimator.getShown()) {
+ // App already has a visible window...why would you want a starting window?
+ return false;
+ }
- // TODO: remove after unification
- mAppWindowToken = mAtmService.mWindowManager.mRoot.getAppWindowToken(appToken.asBinder());
- if (mAppWindowToken != null) {
- // TODO: Should this throw an exception instead?
- Slog.w(TAG, "Attempted to add existing app token: " + appToken);
- } else {
- final Task container = task.getTask();
- if (container == null) {
- throw new IllegalArgumentException("createAppWindowToken: invalid task =" + task);
+ final ActivityManager.TaskSnapshot snapshot =
+ mWmService.mTaskSnapshotController.getSnapshot(
+ getTask().mTaskId, getTask().mUserId,
+ false /* restoreFromDisk */, false /* reducedResolution */);
+ final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
+ allowTaskSnapshot, activityCreated, fromRecents, snapshot);
+
+ if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
+ return createSnapshot(snapshot);
+ }
+
+ // If this is a translucent window, then don't show a starting window -- the current
+ // effect (a full-screen opaque starting window that fades away to the real contents
+ // when it is ready) does not work for this.
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Checking theme of starting window: 0x%x", theme);
+ if (theme != 0) {
+ AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
+ com.android.internal.R.styleable.Window,
+ mWmService.mCurrentUserId);
+ if (ent == null) {
+ // Whoops! App doesn't exist. Um. Okay. We'll just pretend like we didn't
+ // see that.
+ return false;
}
- mAppWindowToken = createAppWindow(mAtmService.mWindowManager, appToken,
- task.voiceSession != null, container.getDisplayContent(),
- ActivityTaskManagerService.getInputDispatchingTimeoutLocked(this)
- * 1000000L, fullscreen,
- (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0,
- info.applicationInfo.targetSdkVersion,
- info.screenOrientation, mRotationAnimationHint,
- mLaunchTaskBehind, isAlwaysFocusable());
- if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) {
- Slog.v(TAG, "addAppToken: "
- + mAppWindowToken + " task=" + container + " at "
- + Integer.MAX_VALUE);
+ final boolean windowIsTranslucent = ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowIsTranslucent, false);
+ final boolean windowIsFloating = ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowIsFloating, false);
+ final boolean windowShowWallpaper = ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowShowWallpaper, false);
+ final boolean windowDisableStarting = ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowDisablePreview, false);
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Translucent=%s Floating=%s ShowWallpaper=%s",
+ windowIsTranslucent, windowIsFloating, windowShowWallpaper);
+ if (windowIsTranslucent) {
+ return false;
+ }
+ if (windowIsFloating || windowDisableStarting) {
+ return false;
+ }
+ if (windowShowWallpaper) {
+ if (getDisplayContent().mWallpaperController
+ .getWallpaperTarget() == null) {
+ // If this theme is requesting a wallpaper, and the wallpaper
+ // is not currently visible, then this effectively serves as
+ // an opaque window and our starting window transition animation
+ // can still work. We just need to make sure the starting window
+ // is also showing the wallpaper.
+ windowFlags |= FLAG_SHOW_WALLPAPER;
+ } else {
+ return false;
+ }
}
- container.addChild(mAppWindowToken, Integer.MAX_VALUE /* add on top */);
}
- task.addActivityToTop(this);
+ if (transferStartingWindow(transferFrom)) {
+ return true;
+ }
+
+ // There is no existing starting window, and we don't want to create a splash screen, so
+ // that's it!
+ if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
+ return false;
+ }
- // When an activity is started directly into a split-screen fullscreen stack, we need to
- // update the initial multi-window modes so that the callbacks are scheduled correctly when
- // the user leaves that mode.
- mLastReportedMultiWindowMode = inMultiWindowMode();
- mLastReportedPictureInPictureMode = inPinnedWindowingMode();
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Creating SplashScreenStartingData");
+ mStartingData = new SplashScreenStartingData(mWmService, pkg,
+ theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
+ getMergedOverrideConfiguration());
+ scheduleAddStartingWindow();
+ return true;
}
- boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
- CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
- IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
- boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
- if (DEBUG_STARTING_WINDOW) {
- Slog.v(TAG, "setAppStartingWindow: token=" + appToken
- + " pkg=" + pkg + " transferFrom=" + transferFrom + " newTask=" + newTask
- + " taskSwitch=" + taskSwitch + " processRunning=" + processRunning
- + " allowTaskSnapshot=" + allowTaskSnapshot);
- }
- if (mAppWindowToken == null) {
- Slog.w(TAG_WM, "Attempted to set icon of non-existing app token: " + appToken);
+ private boolean createSnapshot(ActivityManager.TaskSnapshot snapshot) {
+ if (snapshot == null) {
return false;
}
- if (mAppWindowToken.getTask() == null) {
- // Can be removed after unification of Task and TaskRecord.
- Slog.w(TAG_WM, "Attempted to start a window to an app token not having attached to any"
- + " task: " + appToken);
+
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Creating SnapshotStartingData");
+ mStartingData = new SnapshotStartingData(mWmService, snapshot);
+ scheduleAddStartingWindow();
+ return true;
+ }
+
+ void scheduleAddStartingWindow() {
+ // Note: we really want to do sendMessageAtFrontOfQueue() because we
+ // want to process the message ASAP, before any other queued
+ // messages.
+ if (!mWmService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Enqueueing ADD_STARTING");
+ mWmService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
+ }
+ }
+
+ private class AddStartingWindow implements Runnable {
+
+ @Override
+ public void run() {
+ // Can be accessed without holding the global lock
+ final StartingData startingData;
+ synchronized (mWmService.mGlobalLock) {
+ // There can only be one adding request, silly caller!
+ mWmService.mAnimationHandler.removeCallbacks(this);
+
+ if (mStartingData == null) {
+ // Animation has been canceled... do nothing.
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
+ "startingData was nulled out before handling"
+ + " mAddStartingWindow: %s", ActivityRecord.this);
+ return;
+ }
+ startingData = mStartingData;
+ }
+
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Add starting %s: startingData=%s",
+ this, startingData);
+
+
+ WindowManagerPolicy.StartingSurface surface = null;
+ try {
+ surface = startingData.createStartingSurface(ActivityRecord.this);
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception when adding starting window", e);
+ }
+ if (surface != null) {
+ boolean abort = false;
+ synchronized (mWmService.mGlobalLock) {
+ // If the window was successfully added, then
+ // we need to remove it.
+ if (removed || mStartingData == null) {
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
+ "Aborted starting %s: removed=%b startingData=%s",
+ ActivityRecord.this, removed, mStartingData);
+
+ startingWindow = null;
+ mStartingData = null;
+ abort = true;
+ } else {
+ startingSurface = surface;
+ }
+ if (!abort) {
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
+ "Added starting %s: startingWindow=%s startingView=%s",
+ ActivityRecord.this, startingWindow, startingSurface);
+ }
+ }
+ if (abort) {
+ surface.remove();
+ }
+ } else {
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Surface returned was null: %s",
+ ActivityRecord.this);
+ }
+ }
+ }
+
+ private final AddStartingWindow mAddStartingWindow = new AddStartingWindow();
+
+ private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
+ boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents,
+ ActivityManager.TaskSnapshot snapshot) {
+ if (getDisplayContent().mAppTransition.getAppTransition()
+ == TRANSIT_DOCK_TASK_FROM_RECENTS) {
+ // TODO(b/34099271): Remove this statement to add back the starting window and figure
+ // out why it causes flickering, the starting window appears over the thumbnail while
+ // the docked from recents transition occurs
+ return STARTING_WINDOW_TYPE_NONE;
+ } else if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
+ return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+ } else if (taskSwitch && allowTaskSnapshot) {
+ if (mWmService.mLowRamTaskSnapshotsAndRecents) {
+ // For low RAM devices, we use the splash screen starting window instead of the
+ // task snapshot starting window.
+ return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+ }
+ return snapshot == null ? STARTING_WINDOW_TYPE_NONE
+ : snapshotOrientationSameAsTask(snapshot) || fromRecents
+ ? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+ } else {
+ return STARTING_WINDOW_TYPE_NONE;
+ }
+ }
+
+ private boolean snapshotOrientationSameAsTask(ActivityManager.TaskSnapshot snapshot) {
+ if (snapshot == null) {
return false;
}
- return mAppWindowToken.addStartingWindow(pkg, theme, compatInfo, nonLocalizedLabel,
- labelRes, icon, logo, windowFlags, transferFrom, newTask, taskSwitch,
- processRunning, allowTaskSnapshot, activityCreated, fromRecents);
+ return getTask().getConfiguration().orientation == snapshot.getOrientation();
}
- // TODO: Remove after unification
- @VisibleForTesting
- AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
- boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
- boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
- int rotationAnimationHint, boolean launchTaskBehind,
- boolean alwaysFocusable) {
- return new AppWindowToken(service, token, mActivityComponent, voiceInteraction, dc,
- inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation,
- rotationAnimationHint, launchTaskBehind, alwaysFocusable,
- this);
+ void removeStartingWindow() {
+ if (startingWindow == null) {
+ if (mStartingData != null) {
+ // Starting window has not been added yet, but it is scheduled to be added.
+ // Go ahead and cancel the request.
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Clearing startingData for token=%s", this);
+ mStartingData = null;
+ }
+ return;
+ }
+
+ final WindowManagerPolicy.StartingSurface surface;
+ if (mStartingData != null) {
+ surface = startingSurface;
+ mStartingData = null;
+ startingSurface = null;
+ startingWindow = null;
+ startingDisplayed = false;
+ if (surface == null) {
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
+ "startingWindow was set but startingSurface==null, couldn't "
+ + "remove");
+
+ return;
+ }
+ } else {
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
+ "Tried to remove starting window but startingWindow was null: %s",
+ this);
+ return;
+ }
+
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Schedule remove starting %s startingWindow=%s"
+ + " startingView=%s Callers=%s",
+ this, startingWindow, startingSurface, Debug.getCallers(5));
+
+
+ // Use the same thread to remove the window as we used to add it, as otherwise we end up
+ // with things in the view hierarchy being called from different threads.
+ mWmService.mAnimationHandler.post(() -> {
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Removing startingView=%s", surface);
+ try {
+ surface.remove();
+ } catch (Exception e) {
+ Slog.w(TAG_WM, "Exception when removing starting window", e);
+ }
+ });
}
void removeWindowContainer() {
- if (mAtmService.mWindowManager.mRoot == null) return;
+ if (mWmService.mRoot == null) return;
- final DisplayContent dc = mAtmService.mWindowManager.mRoot.getDisplayContent(
- getDisplayId());
+ final DisplayContent dc = mWmService.mRoot.getDisplayContent(getDisplayId());
if (dc == null) {
Slog.w(TAG, "removeWindowContainer: Attempted to remove token: "
+ appToken + " from non-existing displayId=" + getDisplayId());
@@ -1267,7 +1996,7 @@ public final class ActivityRecord extends ConfigurationContainer {
* should ensure that the {@param newTask} is not already the parent of this activity.
*/
void reparent(TaskRecord newTask, int position, String reason) {
- if (mAppWindowToken == null) {
+ if (getParent() == null) {
Slog.w(TAG, "reparent: Attempted to reparent non-existing app token: " + appToken);
return;
}
@@ -1286,7 +2015,44 @@ public final class ActivityRecord extends ConfigurationContainer {
+ " r=" + this + " (" + prevTask.getStackId() + ")");
}
- mAppWindowToken.reparent(newTask.getTask(), position);
+ final Task task = newTask.getTask();
+ ProtoLog.i(WM_DEBUG_ADD_REMOVE, "reparent: moving app token=%s"
+ + " to task=%d at %d", this, task.mTaskId, position);
+
+ if (task == null) {
+ throw new IllegalArgumentException("reparent: could not find task");
+ }
+ final Task currentTask = getTask();
+ if (task == currentTask) {
+ throw new IllegalArgumentException(
+ "window token=" + this + " already child of task=" + currentTask);
+ }
+
+ if (currentTask.mStack != task.mStack) {
+ throw new IllegalArgumentException(
+ "window token=" + this + " current task=" + currentTask
+ + " belongs to a different stack than " + task);
+ }
+
+ ProtoLog.i(WM_DEBUG_ADD_REMOVE, "reParentWindowToken: removing window token=%s"
+ + " from task=%s" , this, currentTask);
+ final DisplayContent prevDisplayContent = getDisplayContent();
+
+ mReparenting = true;
+
+ getParent().removeChild(this);
+ task.addChild(this, position);
+
+ mReparenting = false;
+
+ // Relayout display(s).
+ final DisplayContent displayContent = task.getDisplayContent();
+ displayContent.setLayoutNeeded();
+ if (prevDisplayContent != displayContent) {
+ onDisplayChanged(displayContent);
+ prevDisplayContent.setLayoutNeeded();
+ }
+ getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
// Reparenting prevents informing the parent stack of activity removal in the case that
// the new stack has the same parent. we must manually signal here if this is not the case.
@@ -1377,6 +2143,7 @@ public final class ActivityRecord extends ConfigurationContainer {
/**
* @return Stack value from current task, null if there is no task.
*/
+ // TODO: Remove once ActivityStack and TaskStack are unified.
<T extends ActivityStack> T getActivityStack() {
return task != null ? (T) task.getStack() : null;
}
@@ -1390,15 +2157,28 @@ public final class ActivityRecord extends ConfigurationContainer {
return stack != null ? stack.getDisplay() : null;
}
+ @Override
+ boolean fillsParent() {
+ return occludesParent();
+ }
+
+ /** Returns true if this activity is opaque and fills the entire space of this task. */
+ boolean occludesParent() {
+ return mOccludesParent;
+ }
+
boolean setOccludesParent(boolean occludesParent) {
- final boolean changed = mAppWindowToken.setOccludesParent(occludesParent);
- if (changed) {
+ final boolean changed = occludesParent != mOccludesParent;
+ mOccludesParent = occludesParent;
+ setMainWindowOpaque(occludesParent);
+ mWmService.mWindowPlacerLocked.requestTraversal();
+
+ if (changed && task != null) {
if (!occludesParent) {
getActivityStack().convertActivityToTranslucent(this);
}
// Keep track of the number of fullscreen activities in this task.
task.numFullscreen += occludesParent ? +1 : -1;
- fullscreen = occludesParent;
}
// Always ensure visibility if this activity doesn't occlude parent, so the
// {@link #returningOptions} of the activity under this one can be applied in
@@ -1409,6 +2189,15 @@ public final class ActivityRecord extends ConfigurationContainer {
return changed;
}
+ void setMainWindowOpaque(boolean isOpaque) {
+ final WindowState win = findMainWindow();
+ if (win == null) {
+ return;
+ }
+ isOpaque = isOpaque & !PixelFormat.formatHasAlpha(win.getAttrs().format);
+ win.mWinAnimator.setOpaqueLocked(isOpaque);
+ }
+
void takeFromHistory() {
if (inHistory) {
inHistory = false;
@@ -1560,6 +2349,22 @@ public final class ActivityRecord extends ConfigurationContainer {
}
/**
+ * Sets if this AWT is in the process of closing or entering PIP.
+ * {@link #mWillCloseOrEnterPip}}
+ */
+ void setWillCloseOrEnterPip(boolean willCloseOrEnterPip) {
+ mWillCloseOrEnterPip = willCloseOrEnterPip;
+ }
+
+ /**
+ * Returns whether this AWT is considered closing. Conditions are either
+ * 1. Is this app animating and was requested to be hidden
+ * 2. App is delayed closing since it might enter PIP.
+ */
+ boolean isClosingOrEnteringPip() {
+ return (isAnimating() && hiddenRequested) || mWillCloseOrEnterPip;
+ }
+ /**
* @return Whether AppOps allows this package to enter picture-in-picture.
*/
private boolean checkEnterPictureInPictureAppOpsState() {
@@ -1571,6 +2376,23 @@ public final class ActivityRecord extends ConfigurationContainer {
return (info.flags & FLAG_ALWAYS_FOCUSABLE) != 0;
}
+ // TODO: Does this really need to be different from isAlwaysFocusable()? For the activity side
+ // focusable means resumeable. I guess with that in mind maybe we should rename the other
+ // method to isResumeable() or something like that.
+ boolean windowsAreFocusable() {
+ if (mTargetSdk < Build.VERSION_CODES.Q) {
+ final int pid = getPid();
+ final ActivityRecord topFocusedAppOfMyProcess =
+ mWmService.mRoot.mTopFocusedAppByProcess.get(pid);
+ if (topFocusedAppOfMyProcess != null && topFocusedAppOfMyProcess != this) {
+ // For the apps below Q, there can be only one app which has the focused window per
+ // process, because legacy apps may not be ready for a multi-focus system.
+ return false;
+ }
+ }
+ return getWindowConfiguration().canReceiveKeys() || isAlwaysFocusable();
+ }
+
/** Move activity with its stack to front and make the stack focused. */
boolean moveFocusableActivityToTop(String reason) {
if (!isFocusable()) {
@@ -1718,15 +2540,15 @@ public final class ActivityRecord extends ConfigurationContainer {
final TaskRecord task = getTaskRecord();
EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
mUserId, System.identityHashCode(this),
- task.taskId, shortComponentName, reason);
+ task.mTaskId, shortComponentName, reason);
final ArrayList<ActivityRecord> activities = task.mActivities;
final int index = activities.indexOf(this);
- if (index < (activities.size() - 1)) {
+ if (index < (task.getChildCount() - 1)) {
if ((intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
// If the caller asked that this activity (and all above it)
// be cleared when the task is reset, don't lose that information,
// but propagate it up to the next activity.
- final ActivityRecord next = activities.get(index + 1);
+ final ActivityRecord next = task.getChildAt(index + 1);
next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
}
}
@@ -1867,25 +2689,27 @@ public final class ActivityRecord extends ConfigurationContainer {
// implied that the current finishing activity should be added into stopping list rather
// than destroy immediately.
final boolean isNextNotYetVisible = next != null && (!next.nowVisible || !next.visible);
- final ActivityStack stack = getActivityStack();
- final boolean notFocusedStack = stack != mRootActivityContainer.getTopDisplayFocusedStack();
+ final boolean notGlobalFocusedStack =
+ getActivityStack() != mRootActivityContainer.getTopDisplayFocusedStack();
if (isVisible && isNextNotYetVisible) {
+ // Add this activity to the list of stopping activities. It will be processed and
+ // destroyed when the next activity reports idle.
addToStopping(false /* scheduleIdle */, false /* idleDelayed */,
"completeFinishing");
- if (DEBUG_STATES) {
- Slog.v(TAG_STATES, "Moving to STOPPING: " + this + " (finish requested)");
- }
setState(STOPPING, "completeFinishing");
- if (notFocusedStack) {
+ if (notGlobalFocusedStack) {
+ // Ensuring visibility and configuration only for non-focused stacks since this
+ // method call is relatively expensive and not necessary for focused stacks.
mRootActivityContainer.ensureVisibilityAndConfig(next, getDisplayId(),
false /* markFrozenIfConfigChanged */, true /* deferResume */);
}
- } else if (isVisible && isState(PAUSED) && getActivityStack().isFocusedStackOnDisplay()
- && !inPinnedWindowingMode()) {
- // TODO(b/137329632): Currently non-focused stack is handled differently.
- addToFinishingAndWaitForIdle();
+ } else if (addToFinishingAndWaitForIdle()) {
+ // We added this activity to the finishing list and something else is becoming resumed.
+ // The activity will complete finishing when the next activity reports idle. No need to
+ // do anything else here.
} else {
- // Not waiting for the next one to become visible - finish right away.
+ // Not waiting for the next one to become visible, and nothing else will be resumed in
+ // place of this activity - requesting destruction right away.
activityRemoved = destroyIfPossible(reason);
}
@@ -1942,13 +2766,20 @@ public final class ActivityRecord extends ConfigurationContainer {
return activityRemoved;
}
+ /**
+ * Add this activity to the list of finishing and trigger resuming of activities in focused
+ * stacks.
+ * @return {@code true} if some other activity is being resumed as a result of this call.
+ */
@VisibleForTesting
- void addToFinishingAndWaitForIdle() {
+ boolean addToFinishingAndWaitForIdle() {
if (DEBUG_STATES) Slog.v(TAG, "Enqueueing pending finish: " + this);
setState(FINISHING, "addToFinishingAndWaitForIdle");
- mStackSupervisor.mFinishingActivities.add(this);
+ if (!mStackSupervisor.mFinishingActivities.contains(this)) {
+ mStackSupervisor.mFinishingActivities.add(this);
+ }
resumeKeyDispatchingLocked();
- mRootActivityContainer.resumeFocusedStacksTopActivities();
+ return mRootActivityContainer.resumeFocusedStacksTopActivities();
}
/**
@@ -1978,7 +2809,7 @@ public final class ActivityRecord extends ConfigurationContainer {
}
EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY, mUserId,
- System.identityHashCode(this), getTaskRecord().taskId, shortComponentName, reason);
+ System.identityHashCode(this), getTaskRecord().mTaskId, shortComponentName, reason);
boolean removedFromHistory = false;
@@ -2116,7 +2947,7 @@ public final class ActivityRecord extends ConfigurationContainer {
// work.
// TODO: If the callers to removeTask() changes such that we have multiple places
// where we are destroying the task, move this back into removeTask()
- mStackSupervisor.removeTaskByIdLocked(task.taskId, false /* killProcess */,
+ mStackSupervisor.removeTaskByIdLocked(task.mTaskId, false /* killProcess */,
!REMOVE_FROM_RECENTS, reason);
}
@@ -2139,10 +2970,6 @@ public final class ActivityRecord extends ConfigurationContainer {
if (stopped) {
clearOptionsLocked();
}
-
- if (mAtmService != null) {
- mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
- }
}
/**
@@ -2211,9 +3038,107 @@ public final class ActivityRecord extends ConfigurationContainer {
stack.removeTimeoutsForActivity(this);
// Clean-up activities are no longer relaunching (e.g. app process died). Notify window
// manager so it can update its bookkeeping.
- if (mAppWindowToken != null) {
- mAppWindowToken.clearRelaunching();
+ clearRelaunching();
+ }
+
+ boolean isRelaunching() {
+ return mPendingRelaunchCount > 0;
+ }
+
+ boolean shouldFreezeBounds() {
+ final Task task = getTask();
+
+ // For freeform windows, we can't freeze the bounds at the moment because this would make
+ // the resizing unresponsive.
+ if (task == null || task.inFreeformWindowingMode()) {
+ return false;
}
+
+ // We freeze the bounds while drag resizing to deal with the time between
+ // the divider/drag handle being released, and the handling it's new
+ // configuration. If we are relaunched outside of the drag resizing state,
+ // we need to be careful not to do this.
+ return getTask().isDragResizing();
+ }
+
+ void startRelaunching() {
+ if (shouldFreezeBounds()) {
+ freezeBounds();
+ }
+
+ // In the process of tearing down before relaunching, the app will
+ // try and clean up it's child surfaces. We need to prevent this from
+ // happening, so we sever the children, transfering their ownership
+ // from the client it-self to the parent surface (owned by us).
+ detachChildren();
+
+ mPendingRelaunchCount++;
+ }
+
+ /**
+ * Freezes the task bounds. The size of this task reported the app will be fixed to the bounds
+ * freezed by {@link Task#prepareFreezingBounds} until {@link #unfreezeBounds} gets called, even
+ * if they change in the meantime. If the bounds are already frozen, the bounds will be frozen
+ * with a queue.
+ */
+ private void freezeBounds() {
+ final Task task = getTask();
+ mFrozenBounds.offer(new Rect(task.mPreparedFrozenBounds));
+
+ if (task.mPreparedFrozenMergedConfig.equals(Configuration.EMPTY)) {
+ // We didn't call prepareFreezingBounds on the task, so use the current value.
+ mFrozenMergedConfig.offer(new Configuration(task.getConfiguration()));
+ } else {
+ mFrozenMergedConfig.offer(new Configuration(task.mPreparedFrozenMergedConfig));
+ }
+ // Calling unset() to make it equal to Configuration.EMPTY.
+ task.mPreparedFrozenMergedConfig.unset();
+ }
+
+ void detachChildren() {
+ SurfaceControl.openTransaction();
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ final WindowState w = mChildren.get(i);
+ w.mWinAnimator.detachChildren();
+ }
+ SurfaceControl.closeTransaction();
+ }
+
+ void finishRelaunching() {
+ unfreezeBounds();
+
+ if (mPendingRelaunchCount > 0) {
+ mPendingRelaunchCount--;
+ } else {
+ // Update keyguard flags upon finishing relaunch.
+ checkKeyguardFlagsChanged();
+ }
+ }
+
+ void clearRelaunching() {
+ if (mPendingRelaunchCount == 0) {
+ return;
+ }
+ unfreezeBounds();
+ mPendingRelaunchCount = 0;
+ }
+
+ /**
+ * Unfreezes the previously frozen bounds. See {@link #freezeBounds}.
+ */
+ private void unfreezeBounds() {
+ if (mFrozenBounds.isEmpty()) {
+ return;
+ }
+ mFrozenBounds.remove();
+ if (!mFrozenMergedConfig.isEmpty()) {
+ mFrozenMergedConfig.remove();
+ }
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ final WindowState win = mChildren.get(i);
+ win.onUnfreezeBounds();
+ }
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
}
/**
@@ -2227,12 +3152,510 @@ public final class ActivityRecord extends ConfigurationContainer {
mServiceConnectionsHolder.disconnectActivityFromServices();
}
+ @Override
+ void removeImmediately() {
+ onRemovedFromDisplay();
+ super.removeImmediately();
+ }
+
+ @Override
+ void removeIfPossible() {
+ mIsExiting = false;
+ removeAllWindowsIfPossible();
+ removeImmediately();
+ }
+
+ @Override
+ boolean checkCompleteDeferredRemoval() {
+ if (mIsExiting) {
+ removeIfPossible();
+ }
+ return super.checkCompleteDeferredRemoval();
+ }
+
+ void onRemovedFromDisplay() {
+ if (mRemovingFromDisplay) {
+ return;
+ }
+ mRemovingFromDisplay = true;
+
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Removing app token: %s", this);
+
+ boolean delayed = commitVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction);
+
+ getDisplayContent().mOpeningApps.remove(this);
+ getDisplayContent().mChangingApps.remove(this);
+ getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
+ mWmService.mTaskSnapshotController.onAppRemoved(this);
+ waitingToShow = false;
+ if (getDisplayContent().mClosingApps.contains(this)) {
+ delayed = true;
+ } else if (getDisplayContent().mAppTransition.isTransitionSet()) {
+ getDisplayContent().mClosingApps.add(this);
+ delayed = true;
+ }
+
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+ "Removing app %s delayed=%b animation=%s animating=%b", this, delayed,
+ getAnimation(), isSelfAnimating());
+
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE, "removeAppToken: %s"
+ + " delayed=%b Callers=%s", this, delayed, Debug.getCallers(4));
+
+ if (mStartingData != null) {
+ removeStartingWindow();
+ }
+
+ // If this window was animating, then we need to ensure that the app transition notifies
+ // that animations have completed in DisplayContent.handleAnimatingStoppedAndTransition(),
+ // so add to that list now
+ if (isSelfAnimating()) {
+ getDisplayContent().mNoAnimationNotifyOnTransitionFinished.add(token);
+ }
+
+ final TaskStack stack = getStack();
+ if (delayed && !isEmpty()) {
+ // set the token aside because it has an active animation to be finished
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE,
+ "removeAppToken make exiting: %s", this);
+ if (stack != null) {
+ stack.mExitingActivities.add(this);
+ }
+ mIsExiting = true;
+ } else {
+ // Make sure there is no animation running on this token, so any windows associated
+ // with it will be removed as soon as their animations are complete
+ cancelAnimation();
+ if (stack != null) {
+ stack.mExitingActivities.remove(this);
+ }
+ removeIfPossible();
+ }
+
+ removed = true;
+ stopFreezingScreen(true, true);
+
+ final DisplayContent dc = getDisplayContent();
+ if (dc.mFocusedApp == this) {
+ ProtoLog.v(WM_DEBUG_FOCUS_LIGHT,
+ "Removing focused app token:%s displayId=%d", this,
+ dc.getDisplayId());
+ dc.setFocusedApp(null);
+ mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
+ }
+ if (mLetterbox != null) {
+ mLetterbox.destroy();
+ mLetterbox = null;
+ }
+
+ if (!delayed) {
+ updateReportedVisibilityLocked();
+ }
+
+ // Reset the last saved PiP snap fraction on removal.
+ mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(mActivityComponent);
+
+ mRemovingFromDisplay = false;
+ }
+
+ /**
+ * Returns true if the new child window we are adding to this token is considered greater than
+ * the existing child window in this token in terms of z-order.
+ */
+ @Override
+ protected boolean isFirstChildWindowGreaterThanSecond(WindowState newWindow,
+ WindowState existingWindow) {
+ final int type1 = newWindow.mAttrs.type;
+ final int type2 = existingWindow.mAttrs.type;
+
+ // Base application windows should be z-ordered BELOW all other windows in the app token.
+ if (type1 == TYPE_BASE_APPLICATION && type2 != TYPE_BASE_APPLICATION) {
+ return false;
+ } else if (type1 != TYPE_BASE_APPLICATION && type2 == TYPE_BASE_APPLICATION) {
+ return true;
+ }
+
+ // Starting windows should be z-ordered ABOVE all other windows in the app token.
+ if (type1 == TYPE_APPLICATION_STARTING && type2 != TYPE_APPLICATION_STARTING) {
+ return true;
+ } else if (type1 != TYPE_APPLICATION_STARTING && type2 == TYPE_APPLICATION_STARTING) {
+ return false;
+ }
+
+ // Otherwise the new window is greater than the existing window.
+ return true;
+ }
+
+ /**
+ * @return {@code true} if starting window is in app's hierarchy.
+ */
+ boolean hasStartingWindow() {
+ if (startingDisplayed || mStartingData != null) {
+ return true;
+ }
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ if (getChildAt(i).mAttrs.type == TYPE_APPLICATION_STARTING) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean isLastWindow(WindowState win) {
+ return mChildren.size() == 1 && mChildren.get(0) == win;
+ }
+
+ @Override
+ void addWindow(WindowState w) {
+ super.addWindow(w);
+
+ boolean gotReplacementWindow = false;
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ final WindowState candidate = mChildren.get(i);
+ gotReplacementWindow |= candidate.setReplacementWindowIfNeeded(w);
+ }
+
+ // if we got a replacement window, reset the timeout to give drawing more time
+ if (gotReplacementWindow) {
+ mWmService.scheduleWindowReplacementTimeouts(this);
+ }
+ checkKeyguardFlagsChanged();
+ }
+
+ @Override
+ void removeChild(WindowState child) {
+ if (!mChildren.contains(child)) {
+ // This can be true when testing.
+ return;
+ }
+ super.removeChild(child);
+ checkKeyguardFlagsChanged();
+ updateLetterboxSurface(child);
+ }
+
+ private boolean waitingForReplacement() {
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ final WindowState candidate = mChildren.get(i);
+ if (candidate.waitingForReplacement()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void onWindowReplacementTimeout() {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ (mChildren.get(i)).onWindowReplacementTimeout();
+ }
+ }
+
+ void setAppLayoutChanges(int changes, String reason) {
+ if (!mChildren.isEmpty()) {
+ final DisplayContent dc = getDisplayContent();
+ dc.pendingLayoutChanges |= changes;
+ if (DEBUG_LAYOUT_REPEATS) {
+ mWmService.mWindowPlacerLocked.debugLayoutRepeats(reason, dc.pendingLayoutChanges);
+ }
+ }
+ }
+
+ void removeReplacedWindowIfNeeded(WindowState replacement) {
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ final WindowState win = mChildren.get(i);
+ if (win.removeReplacedWindowIfNeeded(replacement)) {
+ return;
+ }
+ }
+ }
+
+ boolean transferStartingWindow(IBinder transferFrom) {
+ final ActivityRecord fromActivity = getDisplayContent().getActivityRecord(transferFrom);
+ if (fromActivity == null) {
+ return false;
+ }
+
+ final WindowState tStartingWindow = fromActivity.startingWindow;
+ if (tStartingWindow != null && fromActivity.startingSurface != null) {
+ // In this case, the starting icon has already been displayed, so start
+ // letting windows get shown immediately without any more transitions.
+ getDisplayContent().mSkipAppTransitionAnimation = true;
+
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Moving existing starting %s"
+ + " from %s to %s", tStartingWindow, fromActivity, this);
+
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ // Transfer the starting window over to the new token.
+ mStartingData = fromActivity.mStartingData;
+ startingSurface = fromActivity.startingSurface;
+ startingDisplayed = fromActivity.startingDisplayed;
+ fromActivity.startingDisplayed = false;
+ startingWindow = tStartingWindow;
+ reportedVisible = fromActivity.reportedVisible;
+ fromActivity.mStartingData = null;
+ fromActivity.startingSurface = null;
+ fromActivity.startingWindow = null;
+ fromActivity.startingMoved = true;
+ tStartingWindow.mToken = this;
+ tStartingWindow.mActivityRecord = this;
+
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE,
+ "Removing starting %s from %s", tStartingWindow, fromActivity);
+ fromActivity.removeChild(tStartingWindow);
+ fromActivity.postWindowRemoveStartingWindowCleanup(tStartingWindow);
+ fromActivity.mHiddenSetFromTransferredStartingWindow = false;
+ addWindow(tStartingWindow);
+
+ // Propagate other interesting state between the tokens. If the old token is displayed,
+ // we should immediately force the new one to be displayed. If it is animating, we need
+ // to move that animation to the new one.
+ if (fromActivity.allDrawn) {
+ allDrawn = true;
+ deferClearAllDrawn = fromActivity.deferClearAllDrawn;
+ }
+ if (fromActivity.firstWindowDrawn) {
+ firstWindowDrawn = true;
+ }
+ if (!fromActivity.isHidden()) {
+ setHidden(false);
+ hiddenRequested = false;
+ mHiddenSetFromTransferredStartingWindow = true;
+ }
+ setClientHidden(fromActivity.mClientHidden);
+
+ transferAnimation(fromActivity);
+
+ // When transferring an animation, we no longer need to apply an animation to the
+ // the token we transfer the animation over. Thus, set this flag to indicate we've
+ // transferred the animation.
+ mUseTransferredAnimation = true;
+
+ mWmService.updateFocusedWindowLocked(
+ UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/);
+ getDisplayContent().setLayoutNeeded();
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ return true;
+ } else if (fromActivity.mStartingData != null) {
+ // The previous app was getting ready to show a
+ // starting window, but hasn't yet done so. Steal it!
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
+ "Moving pending starting from %s to %s", fromActivity, this);
+ mStartingData = fromActivity.mStartingData;
+ fromActivity.mStartingData = null;
+ fromActivity.startingMoved = true;
+ scheduleAddStartingWindow();
+ return true;
+ }
+
+ // TODO: Transfer thumbnail
+
+ return false;
+ }
+
+ /**
+ * Tries to transfer the starting window from a token that's above ourselves in the task but
+ * not visible anymore. This is a common scenario apps use: Trampoline activity T start main
+ * activity M in the same task. Now, when reopening the task, T starts on top of M but then
+ * immediately finishes after, so we have to transfer T to M.
+ */
+ void transferStartingWindowFromHiddenAboveTokenIfNeeded() {
+ final Task task = getTask();
+ for (int i = task.mChildren.size() - 1; i >= 0; i--) {
+ final ActivityRecord fromActivity = task.mChildren.get(i);
+ if (fromActivity == this) {
+ return;
+ }
+ if (fromActivity.hiddenRequested && transferStartingWindow(fromActivity.token)) {
+ return;
+ }
+ }
+ }
+
+ void checkKeyguardFlagsChanged() {
+ final boolean containsDismissKeyguard = containsDismissKeyguardWindow();
+ final boolean containsShowWhenLocked = containsShowWhenLockedWindow();
+ if (containsDismissKeyguard != mLastContainsDismissKeyguardWindow
+ || containsShowWhenLocked != mLastContainsShowWhenLockedWindow) {
+ mWmService.notifyKeyguardFlagsChanged(null /* callback */,
+ getDisplayContent().getDisplayId());
+ }
+ mLastContainsDismissKeyguardWindow = containsDismissKeyguard;
+ mLastContainsShowWhenLockedWindow = containsShowWhenLocked;
+ }
+
+ boolean containsDismissKeyguardWindow() {
+ // Window state is transient during relaunch. We are not guaranteed to be frozen during the
+ // entirety of the relaunch.
+ if (isRelaunching()) {
+ return mLastContainsDismissKeyguardWindow;
+ }
+
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ if ((mChildren.get(i).mAttrs.flags & FLAG_DISMISS_KEYGUARD) != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean containsShowWhenLockedWindow() {
+ // When we are relaunching, it is possible for us to be unfrozen before our previous
+ // windows have been added back. Using the cached value ensures that our previous
+ // showWhenLocked preference is honored until relaunching is complete.
+ if (isRelaunching()) {
+ return mLastContainsShowWhenLockedWindow;
+ }
+
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ if ((mChildren.get(i).mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ void setShowWhenLocked(boolean showWhenLocked) {
+ mShowWhenLocked = showWhenLocked;
+ mAtmService.mRootActivityContainer.ensureActivitiesVisible(null /* starting */,
+ 0 /* configChanges */, false /* preserveWindows */);
+ }
+
+ void setInheritShowWhenLocked(boolean inheritShowWhenLocked) {
+ mInheritShownWhenLocked = inheritShowWhenLocked;
+ mAtmService.mRootActivityContainer.ensureActivitiesVisible(null /* starting */,
+ 0 /* configChanges */, false /* preserveWindows */);
+ }
+
+ /**
+ * @return {@code true} if the activity windowing mode is not
+ * {@link android.app.WindowConfiguration#WINDOWING_MODE_PINNED} and a) activity
+ * contains windows that have {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} set or if the
+ * activity has set {@link #mShowWhenLocked}, or b) if the activity has set
+ * {@link #mInheritShownWhenLocked} and the activity behind this satisfies the
+ * conditions a) above.
+ * Multi-windowing mode will be exited if {@code true} is returned.
+ */
+ boolean canShowWhenLocked() {
+ if (!inPinnedWindowingMode() && (mShowWhenLocked || containsShowWhenLockedWindow())) {
+ return true;
+ } else if (mInheritShownWhenLocked) {
+ final ActivityRecord r = getActivityBelow();
+ return r != null && !r.inPinnedWindowingMode() && (r.mShowWhenLocked
+ || r.containsShowWhenLockedWindow());
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * @return Whether we are allowed to show non-starting windows at the moment. We disallow
+ * showing windows during transitions in case we have windows that have wide-color-gamut
+ * color mode set to avoid jank in the middle of the transition.
+ */
+ boolean canShowWindows() {
+ return allDrawn && !(isReallyAnimating() && hasNonDefaultColorWindow());
+ }
+
+ /**
+ * @return true if we have a window that has a non-default color mode set; false otherwise.
+ */
+ private boolean hasNonDefaultColorWindow() {
+ return forAllWindows(ws -> ws.mAttrs.getColorMode() != COLOR_MODE_DEFAULT,
+ true /* topToBottom */);
+ }
+
+ /**
+ * @return an {@link ActivityRecord} of the activity below this activity, or {@code null} if no
+ * such activity exists.
+ */
+ @Nullable
+ private ActivityRecord getActivityBelow() {
+ final Task task = getTask();
+ final int pos = task.mChildren.indexOf(this);
+ if (pos == -1) {
+ throw new IllegalStateException("Activity not found in its task");
+ }
+ return pos == 0 ? null : task.getChildAt(pos - 1);
+ }
+
+ 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 null;
+ }
+
+ WindowState getHighestAnimLayerWindow(WindowState currentTarget) {
+ WindowState candidate = null;
+ for (int i = mChildren.indexOf(currentTarget); i >= 0; i--) {
+ final WindowState w = mChildren.get(i);
+ if (w.mRemoved) {
+ continue;
+ }
+ if (candidate == null) {
+ candidate = w;
+ }
+ }
+ return candidate;
+ }
+
+ @Override
+ boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
+ // For legacy reasons we process the TaskStack.mExitingActivities first in DisplayContent
+ // before the non-exiting app tokens. So, we skip the exiting app tokens here.
+ // TODO: Investigate if we need to continue to do this or if we can just process them
+ // in-order.
+ if (mIsExiting && !waitingForReplacement()) {
+ return false;
+ }
+ return forAllWindowsUnchecked(callback, traverseTopToBottom);
+ }
+
+ @Override
+ void forAllActivities(Consumer<ActivityRecord> callback) {
+ callback.accept(this);
+ }
+
+ boolean forAllWindowsUnchecked(ToBooleanFunction<WindowState> callback,
+ boolean traverseTopToBottom) {
+ return super.forAllWindows(callback, traverseTopToBottom);
+ }
+
+ @Override
+ protected void setLayer(Transaction t, int layer) {
+ if (!mSurfaceAnimator.hasLeash()) {
+ t.setLayer(mSurfaceControl, layer);
+ }
+ }
+
+ @Override
+ protected void setRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) {
+ if (!mSurfaceAnimator.hasLeash()) {
+ t.setRelativeLayer(mSurfaceControl, relativeTo, layer);
+ }
+ }
+
+ @Override
+ protected void reparentSurfaceControl(Transaction t, SurfaceControl newParent) {
+ if (!mSurfaceAnimator.hasLeash()) {
+ t.reparent(mSurfaceControl, newParent);
+ }
+ }
+
void logStartActivity(int tag, TaskRecord task) {
final Uri data = intent.getData();
final String strData = data != null ? data.toSafeString() : null;
EventLog.writeEvent(tag,
- mUserId, System.identityHashCode(this), task.taskId,
+ mUserId, System.identityHashCode(this), task.mTaskId,
shortComponentName, intent.getAction(),
intent.getType(), strData, intent.getFlags());
}
@@ -2379,7 +3802,7 @@ public final class ActivityRecord extends ConfigurationContainer {
*/
void applyOptionsLocked(ActivityOptions pendingOptions, Intent intent) {
final int animationType = pendingOptions.getAnimationType();
- final DisplayContent displayContent = mAppWindowToken.getDisplayContent();
+ final DisplayContent displayContent = getDisplayContent();
switch (animationType) {
case ANIM_CUSTOM:
displayContent.mAppTransition.overridePendingAppTransition(
@@ -2471,6 +3894,65 @@ public final class ActivityRecord extends ConfigurationContainer {
}
}
+ void clearAllDrawn() {
+ allDrawn = false;
+ deferClearAllDrawn = false;
+ }
+
+ /**
+ * Returns whether the drawn window states of this {@link ActivityRecord} has considered every
+ * child {@link WindowState}. A child is considered if it has been passed into
+ * {@link #updateDrawnWindowStates(WindowState)} after being added. This is used to determine
+ * whether states, such as {@code allDrawn}, can be set, which relies on state variables such as
+ * {@code mNumInterestingWindows}, which depend on all {@link WindowState}s being considered.
+ *
+ * @return {@code true} If all children have been considered, {@code false}.
+ */
+ private boolean allDrawnStatesConsidered() {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final WindowState child = mChildren.get(i);
+ if (child.mightAffectAllDrawn() && !child.getDrawnStateEvaluated()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Determines if the token has finished drawing. This should only be called from
+ * {@link DisplayContent#applySurfaceChangesTransaction}
+ */
+ void updateAllDrawn() {
+ if (!allDrawn) {
+ // Number of drawn windows can be less when a window is being relaunched, wait for
+ // all windows to be launched and drawn for this token be considered all drawn.
+ final int numInteresting = mNumInterestingWindows;
+
+ // We must make sure that all present children have been considered (determined by
+ // {@link #allDrawnStatesConsidered}) before evaluating whether everything has been
+ // drawn.
+ if (numInteresting > 0 && allDrawnStatesConsidered()
+ && mNumDrawnWindows >= numInteresting && !isRelaunching()) {
+ if (DEBUG_VISIBILITY) Slog.v(TAG, "allDrawn: " + this
+ + " interesting=" + numInteresting + " drawn=" + mNumDrawnWindows);
+ allDrawn = true;
+ // Force an additional layout pass where
+ // WindowStateAnimator#commitFinishDrawingLocked() will call performShowLocked().
+ if (mDisplayContent != null) {
+ mDisplayContent.setLayoutNeeded();
+ }
+ mWmService.mH.obtainMessage(H.NOTIFY_ACTIVITY_DRAWN, token).sendToTarget();
+
+ // Notify the pinned stack upon all windows drawn. If there was an animation in
+ // progress then this signal will resume that animation.
+ final TaskStack pinnedStack = mDisplayContent.getPinnedStack();
+ if (pinnedStack != null) {
+ pinnedStack.onAllWindowsDrawn();
+ }
+ }
+ }
+ }
+
ActivityOptions getOptionsForTargetActivityLocked() {
return pendingOptions != null ? pendingOptions.forTargetActivity() : null;
}
@@ -2513,11 +3995,8 @@ public final class ActivityRecord extends ConfigurationContainer {
if (!keysPaused) {
keysPaused = true;
- // TODO: remove the check after unification with AppWindowToken. The DC check is not
- // needed after no mock mAppWindowToken in tests.
- if (mAppWindowToken != null && mAppWindowToken.getDisplayContent() != null) {
- mAppWindowToken.getDisplayContent().getInputMonitor().pauseDispatchingLw(
- mAppWindowToken);
+ if (getDisplayContent() != null) {
+ getDisplayContent().getInputMonitor().pauseDispatchingLw(this);
}
}
}
@@ -2526,11 +4005,8 @@ public final class ActivityRecord extends ConfigurationContainer {
if (keysPaused) {
keysPaused = false;
- // TODO: remove the check after unification with AppWindowToken. The DC check is not
- // needed after no mock mAppWindowToken in tests.
- if (mAppWindowToken != null && mAppWindowToken.getDisplayContent() != null) {
- mAppWindowToken.getDisplayContent().getInputMonitor().resumeDispatchingLw(
- mAppWindowToken);
+ if (getDisplayContent() != null) {
+ getDisplayContent().getInputMonitor().resumeDispatchingLw(this);
}
}
}
@@ -2551,13 +4027,20 @@ public final class ActivityRecord extends ConfigurationContainer {
}
}
+ @Override
+ boolean isVisible() {
+ // If the activity isn't hidden then it is considered visible and there is no need to check
+ // its children windows to see if they are visible.
+ return !isHidden();
+ }
+
void setVisibility(boolean visible) {
- if (mAppWindowToken == null) {
+ if (getParent() == null) {
Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: "
+ appToken);
return;
}
- mAppWindowToken.setVisibility(visible, mDeferHidingClient);
+ setVisibility(visible, mDeferHidingClient);
mAtmService.addWindowLayoutReasons(
ActivityTaskManagerService.LAYOUT_REASON_VISIBILITY_CHANGED);
mStackSupervisor.getActivityMetricsLogger().notifyVisibilityChanged(this);
@@ -2571,6 +4054,336 @@ public final class ActivityRecord extends ConfigurationContainer {
mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = true;
}
+ void setVisibility(boolean visible, boolean deferHidingClient) {
+ final AppTransition appTransition = getDisplayContent().mAppTransition;
+
+ // Don't set visibility to false if we were already not visible. This prevents WM from
+ // adding the app to the closing app list which doesn't make sense for something that is
+ // already not visible. However, set visibility to true even if we are already visible.
+ // This makes sure the app is added to the opening apps list so that the right
+ // transition can be selected.
+ // TODO: Probably a good idea to separate the concept of opening/closing apps from the
+ // concept of setting visibility...
+ if (!visible && hiddenRequested) {
+
+ if (!deferHidingClient && mLastDeferHidingClient) {
+ // We previously deferred telling the client to hide itself when visibility was
+ // initially set to false. Now we would like it to hide, so go ahead and set it.
+ mLastDeferHidingClient = deferHidingClient;
+ setClientHidden(true);
+ }
+ return;
+ }
+
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+ "setAppVisibility(%s, visible=%b): %s hidden=%b hiddenRequested=%b Callers=%s",
+ appToken, visible, appTransition, isHidden(), hiddenRequested,
+ Debug.getCallers(6));
+
+ final DisplayContent displayContent = getDisplayContent();
+ displayContent.mOpeningApps.remove(this);
+ displayContent.mClosingApps.remove(this);
+ if (isInChangeTransition()) {
+ clearChangeLeash(getPendingTransaction(), true /* cancel */);
+ }
+ displayContent.mChangingApps.remove(this);
+ waitingToShow = false;
+ hiddenRequested = !visible;
+ mLastDeferHidingClient = deferHidingClient;
+
+ if (!visible) {
+ // If the app is dead while it was visible, we kept its dead window on screen.
+ // Now that the app is going invisible, we can remove it. It will be restarted
+ // if made visible again.
+ removeDeadWindows();
+ } else {
+ if (!appTransition.isTransitionSet()
+ && appTransition.isReady()) {
+ // Add the app mOpeningApps if transition is unset but ready. This means
+ // we're doing a screen freeze, and the unfreeze will wait for all opening
+ // apps to be ready.
+ displayContent.mOpeningApps.add(this);
+ }
+ startingMoved = false;
+ // If the token is currently hidden (should be the common case), or has been
+ // stopped, then we need to set up to wait for its windows to be ready.
+ if (isHidden() || mAppStopped) {
+ clearAllDrawn();
+
+ // If the app was already visible, don't reset the waitingToShow state.
+ if (isHidden()) {
+ waitingToShow = true;
+
+ // If the client isn't hidden, we don't need to reset the drawing state.
+ if (isClientHidden()) {
+ // Let's reset the draw state in order to prevent the starting window to be
+ // immediately dismissed when the app still has the surface.
+ forAllWindows(w -> {
+ if (w.mWinAnimator.mDrawState == HAS_DRAWN) {
+ w.mWinAnimator.resetDrawState();
+
+ // Force add to mResizingWindows, so that we are guaranteed to get
+ // another reportDrawn callback.
+ w.resetLastContentInsets();
+ }
+ }, true /* traverseTopToBottom */);
+ }
+ }
+ }
+
+ // In the case where we are making an app visible but holding off for a transition,
+ // we still need to tell the client to make its windows visible so they get drawn.
+ // Otherwise, we will wait on performing the transition until all windows have been
+ // drawn, they never will be, and we are sad.
+ setClientHidden(false);
+
+ requestUpdateWallpaperIfNeeded();
+
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE, "No longer Stopped: %s", this);
+ mAppStopped = false;
+
+ transferStartingWindowFromHiddenAboveTokenIfNeeded();
+ }
+
+ // If we are preparing an app transition, then delay changing
+ // the visibility of this token until we execute that transition.
+ if (okToAnimate() && appTransition.isTransitionSet()) {
+ inPendingTransaction = true;
+ if (visible) {
+ displayContent.mOpeningApps.add(this);
+ mEnteringAnimation = true;
+ } else {
+ displayContent.mClosingApps.add(this);
+ mEnteringAnimation = false;
+ }
+ if (appTransition.getAppTransition() == TRANSIT_TASK_OPEN_BEHIND) {
+ // We're launchingBehind, add the launching activity to mOpeningApps.
+ final WindowState win = getDisplayContent().findFocusedWindow();
+ if (win != null) {
+ final ActivityRecord focusedActivity = win.mActivityRecord;
+ if (focusedActivity != null) {
+ ProtoLog.d(WM_DEBUG_APP_TRANSITIONS,
+ "TRANSIT_TASK_OPEN_BEHIND, adding %s to mOpeningApps",
+ focusedActivity);
+
+ // Force animation to be loaded.
+ displayContent.mOpeningApps.add(focusedActivity);
+ }
+ }
+ }
+ return;
+ }
+
+ commitVisibility(null, visible, TRANSIT_UNSET, true, mVoiceInteraction);
+ updateReportedVisibilityLocked();
+ }
+
+ boolean commitVisibility(WindowManager.LayoutParams lp,
+ boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) {
+
+ boolean delayed = false;
+ inPendingTransaction = false;
+ // Reset the state of mHiddenSetFromTransferredStartingWindow since visibility is actually
+ // been set by the app now.
+ mHiddenSetFromTransferredStartingWindow = false;
+
+ // Allow for state changes and animation to be applied if:
+ // * token is transitioning visibility state
+ // * or the token was marked as hidden and is exiting before we had a chance to play the
+ // transition animation
+ // * or this is an opening app and windows are being replaced
+ // * or the token is the opening app and visible while opening task behind existing one.
+ final DisplayContent displayContent = getDisplayContent();
+ boolean visibilityChanged = false;
+ if (isHidden() == visible || (isHidden() && mIsExiting)
+ || (visible && waitingForReplacement())
+ || (visible && displayContent.mOpeningApps.contains(this)
+ && displayContent.mAppTransition.getAppTransition() == TRANSIT_TASK_OPEN_BEHIND)) {
+ final AccessibilityController accessibilityController =
+ mWmService.mAccessibilityController;
+ boolean changed = false;
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+ "Changing app %s hidden=%b performLayout=%b", this, isHidden(),
+ performLayout);
+
+ boolean runningAppAnimation = false;
+
+ if (transit != WindowManager.TRANSIT_UNSET) {
+ if (mUseTransferredAnimation) {
+ runningAppAnimation = isReallyAnimating();
+ } else if (applyAnimationLocked(lp, transit, visible, isVoiceInteraction)) {
+ runningAppAnimation = true;
+ }
+ delayed = runningAppAnimation;
+ final WindowState window = findMainWindow();
+ if (window != null && accessibilityController != null) {
+ accessibilityController.onAppWindowTransitionLocked(window, transit);
+ }
+ changed = true;
+ }
+
+ final int windowsCount = mChildren.size();
+ for (int i = 0; i < windowsCount; i++) {
+ final WindowState win = mChildren.get(i);
+ changed |= win.onAppVisibilityChanged(visible, runningAppAnimation);
+ }
+
+ setHidden(!visible);
+ hiddenRequested = !visible;
+ visibilityChanged = true;
+ if (!visible) {
+ stopFreezingScreen(true, true);
+ } else {
+ // If we are being set visible, and the starting window is not yet displayed,
+ // then make sure it doesn't get displayed.
+ if (startingWindow != null && !startingWindow.isDrawnLw()) {
+ startingWindow.clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
+ startingWindow.mLegacyPolicyVisibilityAfterAnim = false;
+ }
+
+ // We are becoming visible, so better freeze the screen with the windows that are
+ // getting visible so we also wait for them.
+ forAllWindows(mWmService::makeWindowFreezingScreenIfNeededLocked, true);
+ }
+
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+ "commitVisibility: %s: hidden=%b hiddenRequested=%b", this,
+ isHidden(), hiddenRequested);
+
+ if (changed) {
+ displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();
+ if (performLayout) {
+ mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+ false /*updateInputWindows*/);
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
+ }
+ displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
+ }
+ }
+ mUseTransferredAnimation = false;
+
+ if (isReallyAnimating()) {
+ delayed = true;
+ } else {
+ // We aren't animating anything, but exiting windows rely on the animation finished
+ // callback being called in case the ActivityRecord was pretending to be animating,
+ // which we might have done because we were in closing/opening apps list.
+ onAnimationFinished();
+ }
+
+ for (int i = mChildren.size() - 1; i >= 0 && !delayed; i--) {
+ if ((mChildren.get(i)).isSelfOrChildAnimating()) {
+ delayed = true;
+ }
+ }
+
+ if (visibilityChanged) {
+ if (visible && !delayed) {
+ // The token was made immediately visible, there will be no entrance animation.
+ // We need to inform the client the enter animation was finished.
+ mEnteringAnimation = true;
+ mWmService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(
+ token);
+ }
+
+ // If we're becoming visible, immediately change client visibility as well. there seem
+ // to be some edge cases where we change our visibility but client visibility never gets
+ // updated.
+ // If we're becoming invisible, update the client visibility if we are not running an
+ // animation. Otherwise, we'll update client visibility in onAnimationFinished.
+ if (visible || !isReallyAnimating()) {
+ setClientHidden(!visible);
+ }
+
+ if (!displayContent.mClosingApps.contains(this)
+ && !displayContent.mOpeningApps.contains(this)) {
+ // The token is not closing nor opening, so even if there is an animation set, that
+ // doesn't mean that it goes through the normal app transition cycle so we have
+ // to inform the docked controller about visibility change.
+ // TODO(multi-display): notify docked divider on all displays where visibility was
+ // affected.
+ displayContent.getDockedDividerController().notifyAppVisibilityChanged();
+
+ // Take the screenshot before possibly hiding the WSA, otherwise the screenshot
+ // will not be taken.
+ mWmService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible);
+ }
+
+ // If we are hidden but there is no delay needed we immediately
+ // apply the Surface transaction so that the ActivityManager
+ // can have some guarantee on the Surface state following
+ // setting the visibility. This captures cases like dismissing
+ // the docked or pinned stack where there is no app transition.
+ //
+ // In the case of a "Null" animation, there will be
+ // no animation but there will still be a transition set.
+ // We still need to delay hiding the surface such that it
+ // can be synchronized with showing the next surface in the transition.
+ if (isHidden() && !delayed && !displayContent.mAppTransition.isTransitionSet()) {
+ SurfaceControl.openTransaction();
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ mChildren.get(i).mWinAnimator.hide("immediately hidden");
+ }
+ SurfaceControl.closeTransaction();
+ }
+ }
+
+ return delayed;
+ }
+
+ @Override
+ void setHidden(boolean hidden) {
+ super.setHidden(hidden);
+ scheduleAnimation();
+ }
+
+ @Override
+ void onAppTransitionDone() {
+ sendingToBottom = false;
+ }
+
+ /**
+ * See {@link Activity#setDisablePreviewScreenshots}.
+ */
+ void setDisablePreviewScreenshots(boolean disable) {
+ mDisablePreviewScreenshots = disable;
+ }
+
+ /**
+ * Retrieves whether we'd like to generate a snapshot that's based solely on the theme. This is
+ * the case when preview screenshots are disabled {@link #setDisablePreviewScreenshots} or when
+ * we can't take a snapshot for other reasons, for example, if we have a secure window.
+ *
+ * @return True if we need to generate an app theme snapshot, false if we'd like to take a real
+ * screenshot.
+ */
+ boolean shouldUseAppThemeSnapshot() {
+ return mDisablePreviewScreenshots || forAllWindows(w -> (w.mAttrs.flags & FLAG_SECURE) != 0,
+ true /* topToBottom */);
+ }
+
+ /**
+ * Sets whether the current launch can turn the screen on.
+ * @see #currentLaunchCanTurnScreenOn()
+ */
+ void setCurrentLaunchCanTurnScreenOn(boolean currentLaunchCanTurnScreenOn) {
+ mCurrentLaunchCanTurnScreenOn = currentLaunchCanTurnScreenOn;
+ }
+
+ /**
+ * Indicates whether the current launch can turn the screen on. This is to prevent multiple
+ * relayouts from turning the screen back on. The screen should only turn on at most
+ * once per activity resume.
+ * <p>
+ * Note this flag is only meaningful when {@link WindowManager.LayoutParams#FLAG_TURN_SCREEN_ON}
+ * or {@link ActivityRecord#canTurnScreenOn} is set.
+ *
+ * @return {@code true} if the activity is ready to turn on the screen.
+ */
+ boolean currentLaunchCanTurnScreenOn() {
+ return mCurrentLaunchCanTurnScreenOn;
+ }
+
void setState(ActivityState state, String reason) {
if (DEBUG_STATES) Slog.v(TAG_STATES, "State movement: " + this + " from:" + getState()
+ " to:" + state + " reason:" + reason);
@@ -2593,12 +4406,12 @@ public final class ActivityRecord extends ConfigurationContainer {
// an indication that the Surface will eventually be destroyed.
// This however isn't necessarily true if we are going to sleep.
if (state == STOPPING && !isSleeping()) {
- if (mAppWindowToken == null) {
+ if (getParent() == null) {
Slog.w(TAG_WM, "Attempted to notify stopping on non-existing app token: "
+ appToken);
return;
}
- mAppWindowToken.detachChildren();
+ detachChildren();
}
if (state == RESUMED) {
@@ -2656,24 +4469,71 @@ public final class ActivityRecord extends ConfigurationContainer {
|| state5 == mState;
}
+ void destroySurfaces() {
+ destroySurfaces(false /*cleanupOnResume*/);
+ }
+
+ /**
+ * Destroy surfaces which have been marked as eligible by the animator, taking care to ensure
+ * the client has finished with them.
+ *
+ * @param cleanupOnResume whether this is done when app is resumed without fully stopped. If
+ * set to true, destroy only surfaces of removed windows, and clear relevant flags of the
+ * others so that they are ready to be reused. If set to false (common case), destroy all
+ * surfaces that's eligible, if the app is already stopped.
+ */
+ private void destroySurfaces(boolean cleanupOnResume) {
+ boolean destroyedSomething = false;
+
+ // Copying to a different list as multiple children can be removed.
+ final ArrayList<WindowState> children = new ArrayList<>(mChildren);
+ for (int i = children.size() - 1; i >= 0; i--) {
+ final WindowState win = children.get(i);
+ destroyedSomething |= win.destroySurface(cleanupOnResume, mAppStopped);
+ }
+ if (destroyedSomething) {
+ final DisplayContent dc = getDisplayContent();
+ dc.assignWindowLayers(true /*setLayoutNeeded*/);
+ updateLetterboxSurface(null);
+ }
+ }
+
void notifyAppResumed(boolean wasStopped) {
- if (mAppWindowToken == null) {
+ if (getParent() == null) {
Slog.w(TAG_WM, "Attempted to notify resumed of non-existing app token: "
+ appToken);
return;
}
- mAppWindowToken.notifyAppResumed(wasStopped);
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE, "notifyAppResumed: wasStopped=%b %s",
+ wasStopped, this);
+ mAppStopped = false;
+ // Allow the window to turn the screen on once the app is resumed again.
+ setCurrentLaunchCanTurnScreenOn(true);
+ if (!wasStopped) {
+ destroySurfaces(true /*cleanupOnResume*/);
+ }
+ }
+
+ /**
+ * Notify that the app has stopped, and it is okay to destroy any surfaces which were
+ * keeping alive in case they were still being used.
+ */
+ void notifyAppStopped() {
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE, "notifyAppStopped: %s", this);
+ mAppStopped = true;
+ // Reset the last saved PiP snap fraction on app stop.
+ mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(mActivityComponent);
+ destroySurfaces();
+ // Remove any starting window that was added for this app if they are still around.
+ removeStartingWindow();
}
void notifyUnknownVisibilityLaunched() {
// No display activities never add a window, so there is no point in waiting them for
// relayout.
- if (!noDisplay) {
- if (mAppWindowToken != null) {
- mAppWindowToken.getDisplayContent().mUnknownAppVisibilityController
- .notifyLaunched(mAppWindowToken);
- }
+ if (!noDisplay && getDisplayContent() != null) {
+ getDisplayContent().mUnknownAppVisibilityController.notifyLaunched(this);
}
}
@@ -2934,12 +4794,12 @@ public final class ActivityRecord extends ConfigurationContainer {
if (positionInTask == -1) {
throw new IllegalStateException("Activity not found in its task");
}
- if (positionInTask == task.mActivities.size() - 1) {
+ if (positionInTask == task.getChildCount() - 1) {
// It's the topmost activity in the task - should become resumed now
return true;
}
// Check if activity above is finishing now and this one becomes the topmost in task.
- final ActivityRecord activityAbove = task.mActivities.get(positionInTask + 1);
+ final ActivityRecord activityAbove = task.getChildAt(positionInTask + 1);
if (activityAbove.finishing && results == null) {
// We will only allow making active if activity above wasn't launched for result.
// Otherwise it will cause this activity to resume before getting result.
@@ -2973,10 +4833,7 @@ public final class ActivityRecord extends ConfigurationContainer {
display.handleActivitySizeCompatModeIfNeeded(r);
}
- if (r.mAppWindowToken != null) {
- r.mAppWindowToken.getDisplayContent().mUnknownAppVisibilityController
- .notifyAppResumedFinished(r.mAppWindowToken);
- }
+ r.getDisplayContent().mUnknownAppVisibilityController.notifyAppResumedFinished(r);
}
/**
@@ -2993,17 +4850,21 @@ public final class ActivityRecord extends ConfigurationContainer {
}
idle = false;
results = null;
+ if (newIntents != null && newIntents.size() > 0) {
+ mLastNewIntent = newIntents.get(newIntents.size() - 1);
+ }
newIntents = null;
stopped = false;
if (isActivityTypeHome()) {
- mStackSupervisor.updateHomeProcess(task.mActivities.get(0).app);
+ mStackSupervisor.updateHomeProcess(task.getChildAt(0).app);
try {
mStackSupervisor.new PreferredAppsTask().execute();
} catch (Exception e) {
Slog.v (TAG, "Exception: " + e);
}
}
+
if (nowVisible) {
mStackSupervisor.stopWaitingForActivityVisible(this);
}
@@ -3129,9 +4990,7 @@ public final class ActivityRecord extends ConfigurationContainer {
setState(STOPPED, "activityStoppedLocked");
}
- if (mAppWindowToken != null) {
- mAppWindowToken.notifyAppStopped();
- }
+ notifyAppStopped();
if (finishing) {
clearOptionsLocked();
@@ -3208,9 +5067,11 @@ public final class ActivityRecord extends ConfigurationContainer {
stack.removeLaunchTickMessages();
}
- // IApplicationToken
+ boolean mayFreezeScreenLocked() {
+ return mayFreezeScreenLocked(app);
+ }
- public boolean mayFreezeScreenLocked(WindowProcessController app) {
+ private boolean mayFreezeScreenLocked(WindowProcessController app) {
// Only freeze the screen if this activity is currently attached to
// an application, and that application is not blocked or unresponding.
// In any other case, we can't count on getting the screen unfrozen,
@@ -3218,9 +5079,13 @@ public final class ActivityRecord extends ConfigurationContainer {
return hasProcess() && !app.isCrashing() && !app.isNotResponding();
}
- public void startFreezingScreenLocked(WindowProcessController app, int configChanges) {
+ void startFreezingScreenLocked(int configChanges) {
+ startFreezingScreenLocked(app, configChanges);
+ }
+
+ void startFreezingScreenLocked(WindowProcessController app, int configChanges) {
if (mayFreezeScreenLocked(app)) {
- if (mAppWindowToken == null) {
+ if (getParent() == null) {
Slog.w(TAG_WM,
"Attempted to freeze screen with non-existing app token: " + appToken);
return;
@@ -3228,117 +5093,339 @@ public final class ActivityRecord extends ConfigurationContainer {
// Window configuration changes only effect windows, so don't require a screen freeze.
int freezableConfigChanges = configChanges & ~(CONFIG_WINDOW_CONFIGURATION);
- if (freezableConfigChanges == 0 && mAppWindowToken.okToDisplay()) {
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + appToken);
+ if (freezableConfigChanges == 0 && okToDisplay()) {
+ ProtoLog.v(WM_DEBUG_ORIENTATION, "Skipping set freeze of %s", appToken);
return;
}
- mAppWindowToken.startFreezingScreen();
+ startFreezingScreen();
+ }
+ }
+
+ void startFreezingScreen() {
+ ProtoLog.i(WM_DEBUG_ORIENTATION,
+ "Set freezing of %s: hidden=%b freezing=%b hiddenRequested=%b. %s",
+ appToken, isHidden(), mFreezingScreen, hiddenRequested,
+ new RuntimeException().fillInStackTrace());
+ if (!hiddenRequested) {
+ if (!mFreezingScreen) {
+ mFreezingScreen = true;
+ mWmService.registerAppFreezeListener(this);
+ mWmService.mAppsFreezingScreen++;
+ if (mWmService.mAppsFreezingScreen == 1) {
+ mWmService.startFreezingDisplayLocked(0, 0, getDisplayContent());
+ mWmService.mH.removeMessages(H.APP_FREEZE_TIMEOUT);
+ mWmService.mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000);
+ }
+ }
+ final int count = mChildren.size();
+ for (int i = 0; i < count; i++) {
+ final WindowState w = mChildren.get(i);
+ w.onStartFreezingScreen();
+ }
}
}
- public void stopFreezingScreenLocked(boolean force) {
+ boolean isFreezingScreen() {
+ return mFreezingScreen;
+ }
+
+ @Override
+ public void onAppFreezeTimeout() {
+ Slog.w(TAG_WM, "Force clearing freeze: " + this);
+ stopFreezingScreen(true, true);
+ }
+
+ void stopFreezingScreenLocked(boolean force) {
if (force || frozenBeforeDestroy) {
frozenBeforeDestroy = false;
- if (mAppWindowToken == null) {
+ if (getParent() == null) {
return;
}
- if (DEBUG_ORIENTATION) {
- Slog.v(TAG_WM, "Clear freezing of " + appToken + ": hidden="
- + mAppWindowToken.isHidden() + " freezing="
- + mAppWindowToken.isFreezingScreen());
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "Clear freezing of %s: hidden=%b freezing=%b", appToken,
+ isHidden(), isFreezingScreen());
+ stopFreezingScreen(true, force);
+ }
+ }
+
+ void stopFreezingScreen(boolean unfreezeSurfaceNow, boolean force) {
+ if (!mFreezingScreen) {
+ return;
+ }
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "Clear freezing of %s force=%b", this, force);
+ final int count = mChildren.size();
+ boolean unfrozeWindows = false;
+ for (int i = 0; i < count; i++) {
+ final WindowState w = mChildren.get(i);
+ unfrozeWindows |= w.onStopFreezingScreen();
+ }
+ if (force || unfrozeWindows) {
+ ProtoLog.v(WM_DEBUG_ORIENTATION, "No longer freezing: %s", this);
+ mFreezingScreen = false;
+ mWmService.unregisterAppFreezeListener(this);
+ mWmService.mAppsFreezingScreen--;
+ mWmService.mLastFinishedFreezeSource = this;
+ }
+ if (unfreezeSurfaceNow) {
+ if (unfrozeWindows) {
+ mWmService.mWindowPlacerLocked.performSurfacePlacement();
}
- mAppWindowToken.stopFreezingScreen(true, force);
+ mWmService.stopFreezingDisplayLocked();
}
}
- public void reportFullyDrawnLocked(boolean restoredFromBundle) {
+ void reportFullyDrawnLocked(boolean restoredFromBundle) {
final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
- .getActivityMetricsLogger().logAppTransitionReportedDrawn(this, restoredFromBundle);
+ .getActivityMetricsLogger().logAppTransitionReportedDrawn(this, restoredFromBundle);
if (info != null) {
mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
info.windowsFullyDrawnDelayMs, info.getLaunchState());
}
}
- public int isAppInfoGame() {
- int isGame = 0;
- if (info.applicationInfo != null) {
- isGame = (info.applicationInfo.category == ApplicationInfo.CATEGORY_GAME ||
- (info.applicationInfo.flags & ApplicationInfo.FLAG_IS_GAME) == ApplicationInfo.FLAG_IS_GAME) ? 1 : 0;
+ void onFirstWindowDrawn(WindowState win, WindowStateAnimator winAnimator) {
+ firstWindowDrawn = true;
+
+ // We now have a good window to show, remove dead placeholders
+ removeDeadWindows();
+
+ if (startingWindow != null) {
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Finish starting %s"
+ + ": first real window is shown, no animation", win.mToken);
+ // If this initial window is animating, stop it -- we will do an animation to reveal
+ // it from behind the starting window, so there is no need for it to also be doing its
+ // own stuff.
+ win.cancelAnimation();
}
- return isGame;
+ removeStartingWindow();
+ updateReportedVisibilityLocked();
}
- /**
- * Called when the starting window for this container is drawn.
- */
- public void onStartingWindowDrawn(long timestamp) {
- synchronized (mAtmService.mGlobalLock) {
- mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(
- getWindowingMode(), timestamp);
+ /** Called when the windows associated app window container are drawn. */
+ void onWindowsDrawn(boolean drawn, long timestampNs) {
+ mDrawn = drawn;
+ if (!drawn) {
+ return;
+ }
+ final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
+ .getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(), timestampNs);
+ final int windowsDrawnDelayMs = info != null ? info.windowsDrawnDelayMs : INVALID_DELAY;
+ final @LaunchState int launchState = info != null ? info.getLaunchState() : -1;
+ mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
+ windowsDrawnDelayMs, launchState);
+ mStackSupervisor.stopWaitingForActivityVisible(this);
+ finishLaunchTickingLocked();
+ if (task != null) {
+ task.hasBeenVisible = true;
}
}
- /** Called when the windows associated app window container are drawn. */
- public void onWindowsDrawn(boolean drawn, long timestamp) {
- synchronized (mAtmService.mGlobalLock) {
- mDrawn = drawn;
- if (!drawn) {
- return;
- }
- final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
- .getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(), timestamp);
- final int windowsDrawnDelayMs = info != null ? info.windowsDrawnDelayMs : INVALID_DELAY;
- final @LaunchState int launchState = info != null ? info.getLaunchState() : -1;
- mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
- windowsDrawnDelayMs, launchState);
- mStackSupervisor.stopWaitingForActivityVisible(this);
- finishLaunchTickingLocked();
- if (task != null) {
- task.hasBeenVisible = true;
+ /** Called when the windows associated app window container are visible. */
+ void onWindowsVisible() {
+ if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting visible in " + appToken);
+ mStackSupervisor.stopWaitingForActivityVisible(this);
+ if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsVisibleLocked(): " + this);
+ if (!nowVisible) {
+ nowVisible = true;
+ launching = false;
+ lastVisibleTime = SystemClock.uptimeMillis();
+ mAtmService.scheduleAppGcsLocked();
+ }
+ }
+
+ /** Called when the windows associated app window container are no longer visible. */
+ void onWindowsGone() {
+ if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting gone in " + appToken);
+ if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsGone(): " + this);
+ nowVisible = false;
+ launching = false;
+ }
+
+ @Override
+ void checkAppWindowsReadyToShow() {
+ if (allDrawn == mLastAllDrawn) {
+ return;
+ }
+
+ mLastAllDrawn = allDrawn;
+ if (!allDrawn) {
+ return;
+ }
+
+ // The token has now changed state to having all windows shown... what to do, what to do?
+ if (mFreezingScreen) {
+ showAllWindowsLocked();
+ stopFreezingScreen(false, true);
+ ProtoLog.i(WM_DEBUG_ORIENTATION,
+ "Setting mOrientationChangeComplete=true because wtoken %s "
+ + "numInteresting=%d numDrawn=%d",
+ this, mNumInterestingWindows, mNumDrawnWindows);
+ // This will set mOrientationChangeComplete and cause a pass through layout.
+ setAppLayoutChanges(FINISH_LAYOUT_REDO_WALLPAPER,
+ "checkAppWindowsReadyToShow: freezingScreen");
+ } else {
+ setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM, "checkAppWindowsReadyToShow");
+
+ // We can now show all of the drawn windows!
+ if (!getDisplayContent().mOpeningApps.contains(this) && canShowWindows()) {
+ showAllWindowsLocked();
}
}
}
- /** Called when the windows associated app window container are visible. */
- public void onWindowsVisible() {
- synchronized (mAtmService.mGlobalLock) {
- mStackSupervisor.stopWaitingForActivityVisible(this);
- if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsVisibleLocked(): " + this);
+ /**
+ * This must be called while inside a transaction.
+ */
+ void showAllWindowsLocked() {
+ forAllWindows(windowState -> {
+ if (DEBUG_VISIBILITY) Slog.v(TAG, "performing show on: " + windowState);
+ windowState.performShowLocked();
+ }, false /* traverseTopToBottom */);
+ }
+
+ void updateReportedVisibilityLocked() {
+ if (appToken == null) {
+ return;
+ }
+
+ if (DEBUG_VISIBILITY) Slog.v(TAG, "Update reported visibility: " + this);
+ final int count = mChildren.size();
+
+ mReportedVisibilityResults.reset();
+
+ for (int i = 0; i < count; i++) {
+ final WindowState win = mChildren.get(i);
+ win.updateReportedVisibility(mReportedVisibilityResults);
+ }
+
+ int numInteresting = mReportedVisibilityResults.numInteresting;
+ int numVisible = mReportedVisibilityResults.numVisible;
+ int numDrawn = mReportedVisibilityResults.numDrawn;
+ boolean nowGone = mReportedVisibilityResults.nowGone;
+
+ boolean nowDrawn = numInteresting > 0 && numDrawn >= numInteresting;
+ boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting && !isHidden();
+ if (!nowGone) {
+ // If the app is not yet gone, then it can only become visible/drawn.
+ if (!nowDrawn) {
+ nowDrawn = reportedDrawn;
+ }
if (!nowVisible) {
- nowVisible = true;
- launching = false;
- lastVisibleTime = SystemClock.uptimeMillis();
- mAtmService.scheduleAppGcsLocked();
+ nowVisible = reportedVisible;
+ }
+ }
+ if (DEBUG_VISIBILITY) Slog.v(TAG, "VIS " + this + ": interesting="
+ + numInteresting + " visible=" + numVisible);
+ if (nowDrawn != reportedDrawn) {
+ onWindowsDrawn(nowDrawn, SystemClock.elapsedRealtimeNanos());
+ reportedDrawn = nowDrawn;
+ }
+ if (nowVisible != reportedVisible) {
+ if (DEBUG_VISIBILITY) Slog.v(TAG,
+ "Visibility changed in " + this + ": vis=" + nowVisible);
+ reportedVisible = nowVisible;
+ if (nowVisible) {
+ onWindowsVisible();
+ } else {
+ onWindowsGone();
}
}
}
- /** Called when the windows associated app window container are no longer visible. */
- public void onWindowsGone() {
- synchronized (mAtmService.mGlobalLock) {
- if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsGone(): " + this);
- nowVisible = false;
- launching = false;
+ boolean isClientHidden() {
+ return mClientHidden;
+ }
+
+ void setClientHidden(boolean hideClient) {
+ if (mClientHidden == hideClient || (hideClient && mDeferHidingClient)) {
+ return;
}
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+ "setClientHidden: %s clientHidden=%b Callers=%s", this, hideClient,
+ Debug.getCallers(5));
+ mClientHidden = hideClient;
+ sendAppVisibilityToClients();
}
- void onAnimationFinished() {
- if (mRootActivityContainer.allResumedActivitiesIdle()
- || mStackSupervisor.isStoppingNoHistoryActivity()) {
- // If all activities are already idle or there is an activity that must be
- // stopped immediately after visible, then we now need to make sure we perform
- // the full stop of this activity. This is because we won't do that while they are still
- // waiting for the animation to finish.
- if (mStackSupervisor.mStoppingActivities.contains(this)) {
- mStackSupervisor.scheduleIdleLocked();
+ /**
+ * Updated this app token tracking states for interesting and drawn windows based on the window.
+ *
+ * @return Returns true if the input window is considered interesting and drawn while all the
+ * windows in this app token where not considered drawn as of the last pass.
+ */
+ boolean updateDrawnWindowStates(WindowState w) {
+ w.setDrawnStateEvaluated(true /*evaluated*/);
+
+ if (DEBUG_STARTING_WINDOW_VERBOSE && w == startingWindow) {
+ Slog.d(TAG, "updateWindows: starting " + w + " isOnScreen=" + w.isOnScreen()
+ + " allDrawn=" + allDrawn + " freezingScreen=" + mFreezingScreen);
+ }
+
+ if (allDrawn && !mFreezingScreen) {
+ return false;
+ }
+
+ if (mLastTransactionSequence != mWmService.mTransactionSequence) {
+ mLastTransactionSequence = mWmService.mTransactionSequence;
+ mNumDrawnWindows = 0;
+ startingDisplayed = false;
+
+ // There is the main base application window, even if it is exiting, wait for it
+ mNumInterestingWindows = findMainWindow(false /* includeStartingApp */) != null ? 1 : 0;
+ }
+
+ final WindowStateAnimator winAnimator = w.mWinAnimator;
+
+ boolean isInterestingAndDrawn = false;
+
+ if (!allDrawn && w.mightAffectAllDrawn()) {
+ if (DEBUG_VISIBILITY || WM_DEBUG_ORIENTATION.isLogToLogcat()) {
+ Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw()
+ + ", isAnimationSet=" + isSelfAnimating());
+ if (!w.isDrawnLw()) {
+ Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceController
+ + " pv=" + w.isVisibleByPolicy()
+ + " mDrawState=" + winAnimator.drawStateToString()
+ + " ph=" + w.isParentWindowHidden() + " th=" + hiddenRequested
+ + " a=" + isSelfAnimating());
+ }
}
- } else {
- // Instead of doing the full stop routine here, let's just hide any activities
- // we now can, and let them stop when the normal idle happens.
- mStackSupervisor.processStoppingActivitiesLocked(null /* idleActivity */,
- false /* remove */, true /* processPausingActivities */);
+
+ if (w != startingWindow) {
+ if (w.isInteresting()) {
+ // Add non-main window as interesting since the main app has already been added
+ if (findMainWindow(false /* includeStartingApp */) != w) {
+ mNumInterestingWindows++;
+ }
+ if (w.isDrawnLw()) {
+ mNumDrawnWindows++;
+
+ if (DEBUG_VISIBILITY || WM_DEBUG_ORIENTATION.isLogToLogcat()) {
+ Slog.v(TAG, "tokenMayBeDrawn: "
+ + this + " w=" + w + " numInteresting=" + mNumInterestingWindows
+ + " freezingScreen=" + mFreezingScreen
+ + " mAppFreezing=" + w.mAppFreezing);
+ }
+
+ isInterestingAndDrawn = true;
+ }
+ }
+ } else if (w.isDrawnLw()) {
+ onStartingWindowDrawn(SystemClock.uptimeMillis());
+ startingDisplayed = true;
+ }
+ }
+
+ return isInterestingAndDrawn;
+ }
+
+ /** Called when the starting window for this container is drawn. */
+ private void onStartingWindowDrawn(long timestamp) {
+ synchronized (mAtmService.mGlobalLock) {
+ mAtmService.mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(
+ getWindowingMode(), timestamp);
}
}
@@ -3444,7 +5531,7 @@ public final class ActivityRecord extends ConfigurationContainer {
|| (onlyRoot && activityNdx > task.findRootIndex(true /* effectiveRoot */))) {
return INVALID_TASK_ID;
}
- return task.taskId;
+ return task.mTaskId;
}
static ActivityRecord isInStackLocked(IBinder token) {
@@ -3499,8 +5586,8 @@ public final class ActivityRecord extends ConfigurationContainer {
Bitmap icon;
if (_taskDescription.getIconFilename() == null &&
(icon = _taskDescription.getIcon()) != null) {
- final String iconFilename = createImageFilename(createTime, task.taskId);
- final File iconFile = new File(TaskPersister.getUserImagesDir(task.userId),
+ final String iconFilename = createImageFilename(createTime, task.mTaskId);
+ final File iconFile = new File(TaskPersister.getUserImagesDir(task.mUserId),
iconFilename);
final String iconFilePath = iconFile.getAbsolutePath();
mAtmService.getRecentTasks().saveImage(icon, iconFilePath);
@@ -3525,9 +5612,6 @@ public final class ActivityRecord extends ConfigurationContainer {
void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch,
boolean fromRecents) {
- if (mAppWindowToken == null) {
- return;
- }
if (mTaskOverlay) {
// We don't show starting window for overlay activities.
return;
@@ -3555,26 +5639,803 @@ public final class ActivityRecord extends ConfigurationContainer {
if (mStartingWindowState == STARTING_WINDOW_SHOWN && behindFullscreenActivity) {
if (DEBUG_VISIBILITY) Slog.w(TAG_VISIBILITY, "Found orphaned starting window " + this);
mStartingWindowState = STARTING_WINDOW_REMOVED;
- mAppWindowToken.removeStartingWindow();
+ removeStartingWindow();
+ }
+ }
+
+ void postWindowRemoveStartingWindowCleanup(WindowState win) {
+ // TODO: Something smells about the code below...Is there a better way?
+ if (startingWindow == win) {
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Notify removed startingWindow %s", win);
+ removeStartingWindow();
+ } else if (mChildren.size() == 0) {
+ // If this is the last window and we had requested a starting transition window,
+ // well there is no point now.
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Nulling last startingData");
+ mStartingData = null;
+ if (mHiddenSetFromTransferredStartingWindow) {
+ // We set the hidden state to false for the token from a transferred starting window.
+ // We now reset it back to true since the starting window was the last window in the
+ // token.
+ setHidden(true);
+ }
+ } else if (mChildren.size() == 1 && startingSurface != null && !isRelaunching()) {
+ // If this is the last window except for a starting transition window,
+ // we need to get rid of the starting transition.
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Last window, removing starting window %s", win);
+ removeStartingWindow();
+ }
+ }
+
+ void removeDeadWindows() {
+ for (int winNdx = mChildren.size() - 1; winNdx >= 0; --winNdx) {
+ WindowState win = mChildren.get(winNdx);
+ if (win.mAppDied) {
+ ProtoLog.w(WM_DEBUG_ADD_REMOVE,
+ "removeDeadWindows: %s", win);
+ // Set mDestroying, we don't want any animation or delayed removal here.
+ win.mDestroying = true;
+ // Also removes child windows.
+ win.removeIfPossible();
+ }
}
}
- void setRequestedOrientation(int requestedOrientation) {
- setOrientation(requestedOrientation, mayFreezeScreenLocked(app));
- mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
- task.taskId, requestedOrientation);
+ boolean hasWindowsAlive() {
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ // No need to loop through child windows as the answer should be the same as that of the
+ // parent window.
+ if (!(mChildren.get(i)).mAppDied) {
+ return true;
+ }
+ }
+ return false;
}
- private void setOrientation(int requestedOrientation, boolean freezeScreenIfNeeded) {
- if (mAppWindowToken == null) {
- Slog.w(TAG_WM,
- "Attempted to set orientation of non-existing app token: " + appToken);
+ void setWillReplaceWindows(boolean animate) {
+ ProtoLog.d(WM_DEBUG_ADD_REMOVE,
+ "Marking app token %s with replacing windows.", this);
+
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ final WindowState w = mChildren.get(i);
+ w.setWillReplaceWindow(animate);
+ }
+ }
+
+ void setWillReplaceChildWindows() {
+ ProtoLog.d(WM_DEBUG_ADD_REMOVE, "Marking app token %s"
+ + " with replacing child windows.", this);
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ final WindowState w = mChildren.get(i);
+ w.setWillReplaceChildWindows();
+ }
+ }
+
+ void clearWillReplaceWindows() {
+ ProtoLog.d(WM_DEBUG_ADD_REMOVE,
+ "Resetting app token %s of replacing window marks.", this);
+
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ final WindowState w = mChildren.get(i);
+ w.clearWillReplaceWindow();
+ }
+ }
+
+ void requestUpdateWallpaperIfNeeded() {
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ final WindowState w = mChildren.get(i);
+ w.requestUpdateWallpaperIfNeeded();
+ }
+ }
+
+ /**
+ * @return The to top most child window for which {@link LayoutParams#isFullscreen()} returns
+ * true.
+ */
+ WindowState getTopFullscreenWindow() {
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ final WindowState win = mChildren.get(i);
+ if (win != null && win.mAttrs.isFullscreen()) {
+ return win;
+ }
+ }
+ return null;
+ }
+
+ WindowState findMainWindow() {
+ return findMainWindow(true);
+ }
+
+ /**
+ * Finds the main window that either has type base application or application starting if
+ * requested.
+ *
+ * @param includeStartingApp Allow to search application-starting windows to also be returned.
+ * @return The main window of type base application or application starting if requested.
+ */
+ WindowState findMainWindow(boolean includeStartingApp) {
+ WindowState candidate = null;
+ for (int j = mChildren.size() - 1; j >= 0; --j) {
+ final WindowState win = mChildren.get(j);
+ final int type = win.mAttrs.type;
+ // No need to loop through child window as base application and starting types can't be
+ // child windows.
+ if (type == TYPE_BASE_APPLICATION
+ || (includeStartingApp && type == TYPE_APPLICATION_STARTING)) {
+ // In cases where there are multiple windows, we prefer the non-exiting window. This
+ // happens for example when replacing windows during an activity relaunch. When
+ // constructing the animation, we want the new window, not the exiting one.
+ if (win.mAnimatingExit) {
+ candidate = win;
+ } else {
+ return win;
+ }
+ }
+ }
+ return candidate;
+ }
+
+ SurfaceControl getAppAnimationLayer() {
+ return getAppAnimationLayer(isActivityTypeHome() ? ANIMATION_LAYER_HOME
+ : needsZBoost() ? ANIMATION_LAYER_BOOSTED
+ : ANIMATION_LAYER_STANDARD);
+ }
+
+ @Override
+ boolean needsZBoost() {
+ return mNeedsZBoost || super.needsZBoost();
+ }
+
+ @Override
+ public SurfaceControl getAnimationLeashParent() {
+ // For transitions in the pinned stack (menu activity) we just let them occur as a child
+ // of the pinned stack.
+ // All normal app transitions take place in an animation layer which is below the pinned
+ // stack but may be above the parent stacks of the given animating apps by default. When
+ // a new hierarchical animation is enabled, we just let them occur as a child of the parent
+ // stack, i.e. the hierarchy of the surfaces is unchanged.
+ if (inPinnedWindowingMode()) {
+ return getStack().getSurfaceControl();
+ } else if (WindowManagerService.sHierarchicalAnimations) {
+ return super.getAnimationLeashParent();
+ } else {
+ return getAppAnimationLayer();
+ }
+ }
+
+
+ @VisibleForTesting
+ boolean shouldAnimate(int transit) {
+ final boolean isSplitScreenPrimary =
+ getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+ final boolean allowSplitScreenPrimaryAnimation = transit != TRANSIT_WALLPAPER_OPEN;
+
+ // Don't animate while the task runs recents animation but only if we are in the mode
+ // where we cancel with deferred screenshot, which means that the controller has
+ // transformed the task.
+ final RecentsAnimationController controller = mWmService.getRecentsAnimationController();
+ if (controller != null && controller.isAnimatingTask(getTask())
+ && controller.shouldDeferCancelUntilNextTransition()) {
+ return false;
+ }
+
+ // We animate always if it's not split screen primary, and only some special cases in split
+ // screen primary because it causes issues with stack clipping when we run an un-minimize
+ // animation at the same time.
+ return !isSplitScreenPrimary || allowSplitScreenPrimaryAnimation;
+ }
+
+ public int isAppInfoGame() {
+ int isGame = 0;
+ if (info.applicationInfo != null) {
+ isGame = (info.applicationInfo.category == ApplicationInfo.CATEGORY_GAME ||
+ (info.applicationInfo.flags & ApplicationInfo.FLAG_IS_GAME) == ApplicationInfo.FLAG_IS_GAME) ? 1 : 0;
+ }
+ return isGame;
+ }
+
+ /**
+ * Creates a layer to apply crop to an animation.
+ */
+ private SurfaceControl createAnimationBoundsLayer(Transaction t) {
+ ProtoLog.i(WM_DEBUG_APP_TRANSITIONS_ANIM, "Creating animation bounds layer");
+ final SurfaceControl.Builder builder = makeAnimationLeash()
+ .setParent(getAnimationLeashParent())
+ .setName(getSurfaceControl() + " - animation-bounds");
+ final SurfaceControl boundsLayer = builder.build();
+ t.show(boundsLayer);
+ return boundsLayer;
+ }
+
+ boolean applyAnimationLocked(WindowManager.LayoutParams lp, int transit, boolean enter,
+ boolean isVoiceInteraction) {
+
+ if (mWmService.mDisableTransitionAnimation || !shouldAnimate(transit)) {
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
+ "applyAnimation: transition animation is disabled or skipped. "
+ + "atoken=%s", this);
+ cancelAnimation();
+ return false;
+ }
+
+ // Only apply an animation if the display isn't frozen. If it is frozen, there is no reason
+ // to animate and it can cause strange artifacts when we unfreeze the display if some
+ // different animation is running.
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AWT#applyAnimationLocked");
+ if (okToAnimate()) {
+ final AnimationAdapter adapter;
+ AnimationAdapter thumbnailAdapter = null;
+
+ final int appStackClipMode =
+ getDisplayContent().mAppTransition.getAppStackClipMode();
+
+ // Separate position and size for use in animators.
+ mTmpRect.set(getAnimationBounds(appStackClipMode));
+ mTmpPoint.set(mTmpRect.left, mTmpRect.top);
+ mTmpRect.offsetTo(0, 0);
+
+ final boolean isChanging = AppTransition.isChangeTransit(transit) && enter
+ && getDisplayContent().mChangingApps.contains(this);
+
+ // Delaying animation start isn't compatible with remote animations at all.
+ if (getDisplayContent().mAppTransition.getRemoteAnimationController() != null
+ && !mSurfaceAnimator.isAnimationStartDelayed()) {
+ RemoteAnimationController.RemoteAnimationRecord adapters =
+ getDisplayContent().mAppTransition.getRemoteAnimationController()
+ .createRemoteAnimationRecord(this, mTmpPoint, mTmpRect,
+ (isChanging ? mTransitStartRect : null));
+ adapter = adapters.mAdapter;
+ thumbnailAdapter = adapters.mThumbnailAdapter;
+ } else if (isChanging) {
+ final float durationScale = mWmService.getTransitionAnimationScaleLocked();
+ mTmpRect.offsetTo(mTmpPoint.x, mTmpPoint.y);
+ adapter = new LocalAnimationAdapter(
+ new WindowChangeAnimationSpec(mTransitStartRect, mTmpRect,
+ getDisplayContent().getDisplayInfo(), durationScale,
+ true /* isAppAnimation */, false /* isThumbnail */),
+ mWmService.mSurfaceAnimationRunner);
+ if (mThumbnail != null) {
+ thumbnailAdapter = new LocalAnimationAdapter(
+ new WindowChangeAnimationSpec(mTransitStartRect, mTmpRect,
+ getDisplayContent().getDisplayInfo(), durationScale,
+ true /* isAppAnimation */, true /* isThumbnail */),
+ mWmService.mSurfaceAnimationRunner);
+ }
+ mTransit = transit;
+ mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();
+ } else {
+ mNeedsAnimationBoundsLayer = (appStackClipMode == STACK_CLIP_AFTER_ANIM);
+
+ final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
+ if (a != null) {
+ // Only apply corner radius to animation if we're not in multi window mode.
+ // We don't want rounded corners when in pip or split screen.
+ final float windowCornerRadius = !inMultiWindowMode()
+ ? getDisplayContent().getWindowCornerRadius()
+ : 0;
+ adapter = new LocalAnimationAdapter(
+ new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
+ getDisplayContent().mAppTransition.canSkipFirstFrame(),
+ appStackClipMode,
+ true /* isAppAnimation */,
+ windowCornerRadius),
+ mWmService.mSurfaceAnimationRunner);
+ if (a.getZAdjustment() == Animation.ZORDER_TOP) {
+ mNeedsZBoost = true;
+ }
+ mTransit = transit;
+ mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();
+ } else {
+ adapter = null;
+ }
+ }
+ if (adapter != null) {
+ startAnimation(getPendingTransaction(), adapter, !isVisible());
+ if (adapter.getShowWallpaper()) {
+ mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ }
+ if (thumbnailAdapter != null) {
+ mThumbnail.startAnimation(
+ getPendingTransaction(), thumbnailAdapter, !isVisible());
+ }
+ }
+ } else {
+ cancelAnimation();
+ }
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+
+ return isReallyAnimating();
+ }
+
+ private Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
+ boolean isVoiceInteraction) {
+ final DisplayContent displayContent = getTask().getDisplayContent();
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ final int width = displayInfo.appWidth;
+ final int height = displayInfo.appHeight;
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
+ "applyAnimation: atoken=%s", this);
+
+ // Determine the visible rect to calculate the thumbnail clip
+ final WindowState win = findMainWindow();
+ final Rect frame = new Rect(0, 0, width, height);
+ final Rect displayFrame = new Rect(0, 0,
+ displayInfo.logicalWidth, displayInfo.logicalHeight);
+ final Rect insets = new Rect();
+ final Rect stableInsets = new Rect();
+ Rect surfaceInsets = null;
+ final boolean freeform = win != null && win.inFreeformWindowingMode();
+ if (win != null) {
+ // Containing frame will usually cover the whole screen, including dialog windows.
+ // For freeform workspace windows it will not cover the whole screen and it also
+ // won't exactly match the final freeform window frame (e.g. when overlapping with
+ // the status bar). In that case we need to use the final frame.
+ if (freeform) {
+ frame.set(win.getFrameLw());
+ } else if (win.isLetterboxedAppWindow()) {
+ frame.set(getTask().getBounds());
+ } else if (win.isDockedResizing()) {
+ // If we are animating while docked resizing, then use the stack bounds as the
+ // animation target (which will be different than the task bounds)
+ frame.set(getTask().getParent().getBounds());
+ } else {
+ frame.set(win.getContainingFrame());
+ }
+ surfaceInsets = win.getAttrs().surfaceInsets;
+ // XXX(b/72757033): These are insets relative to the window frame, but we're really
+ // interested in the insets relative to the frame we chose in the if-blocks above.
+ win.getContentInsets(insets);
+ win.getStableInsets(stableInsets);
+ }
+
+ if (mLaunchTaskBehind) {
+ // Differentiate the two animations. This one which is briefly on the screen
+ // gets the !enter animation, and the other activity which remains on the
+ // screen gets the enter animation. Both appear in the mOpeningApps set.
+ enter = false;
+ }
+ ProtoLog.d(WM_DEBUG_APP_TRANSITIONS,
+ "Loading animation for app transition. transit=%s enter=%b frame=%s insets=%s "
+ + "surfaceInsets=%s",
+ AppTransition.appTransitionToString(transit), enter, frame, insets, surfaceInsets);
+ final Configuration displayConfig = displayContent.getConfiguration();
+ final Animation a = getDisplayContent().mAppTransition.loadAnimation(lp, transit, enter,
+ displayConfig.uiMode, displayConfig.orientation, frame, displayFrame, insets,
+ surfaceInsets, stableInsets, isVoiceInteraction, freeform, getTask().mTaskId);
+ if (a != null) {
+ if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this);
+ final int containingWidth = frame.width();
+ final int containingHeight = frame.height();
+ a.initialize(containingWidth, containingHeight, width, height);
+ a.scaleCurrentDuration(mWmService.getTransitionAnimationScaleLocked());
+ }
+ return a;
+ }
+
+ @Override
+ public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
+ return mAnimatingActivityRegistry != null
+ && mAnimatingActivityRegistry.notifyAboutToFinish(
+ this, endDeferFinishCallback);
+ }
+
+ private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
+ if (mWmService.mDisableTransitionAnimation
+ || !isVisible()
+ || getDisplayContent().mAppTransition.isTransitionSet()
+ || getSurfaceControl() == null) {
+ return false;
+ }
+ // Only do an animation into and out-of freeform mode for now. Other mode
+ // transition animations are currently handled by system-ui.
+ return (prevWinMode == WINDOWING_MODE_FREEFORM) != (newWinMode == WINDOWING_MODE_FREEFORM);
+ }
+
+ boolean isWaitingForTransitionStart() {
+ final DisplayContent dc = getDisplayContent();
+ // TODO: Test for null can be removed once unification is done.
+ if (dc == null) return false;
+ return dc.mAppTransition.isTransitionSet()
+ && (dc.mOpeningApps.contains(this)
+ || dc.mClosingApps.contains(this)
+ || dc.mChangingApps.contains(this));
+ }
+
+ /**
+ * Initializes a change transition. Because the app is visible already, there is a small period
+ * of time where the user can see the app content/window update before the transition starts.
+ * To prevent this, we immediately take a snapshot and place the app/snapshot into a leash which
+ * "freezes" the location/crop until the transition starts.
+ * <p>
+ * Here's a walk-through of the process:
+ * 1. Create a temporary leash ("interim-change-leash") and reparent the app to it.
+ * 2. Set the temporary leash's position/crop to the current state.
+ * 3. Create a snapshot and place that at the top of the leash to cover up content changes.
+ * 4. Once the transition is ready, it will reparent the app to the animation leash.
+ * 5. Detach the interim-change-leash.
+ */
+ private void initializeChangeTransition(Rect startBounds) {
+ mDisplayContent.prepareAppTransition(TRANSIT_TASK_CHANGE_WINDOWING_MODE,
+ false /* alwaysKeepCurrent */, 0, false /* forceOverride */);
+ mDisplayContent.mChangingApps.add(this);
+ mTransitStartRect.set(startBounds);
+
+ final SurfaceControl.Builder builder = makeAnimationLeash()
+ .setParent(getAnimationLeashParent())
+ .setName(getSurfaceControl() + " - interim-change-leash");
+ mTransitChangeLeash = builder.build();
+ Transaction t = getPendingTransaction();
+ t.setWindowCrop(mTransitChangeLeash, startBounds.width(), startBounds.height());
+ t.setPosition(mTransitChangeLeash, startBounds.left, startBounds.top);
+ t.show(mTransitChangeLeash);
+ t.reparent(getSurfaceControl(), mTransitChangeLeash);
+ onAnimationLeashCreated(t, mTransitChangeLeash);
+
+ // Skip creating snapshot if this transition is controlled by a remote animator which
+ // doesn't need it.
+ ArraySet<Integer> activityTypes = new ArraySet<>();
+ activityTypes.add(getActivityType());
+ RemoteAnimationAdapter adapter =
+ mDisplayContent.mAppTransitionController.getRemoteAnimationOverride(
+ this, TRANSIT_TASK_CHANGE_WINDOWING_MODE, activityTypes);
+ if (adapter != null && !adapter.getChangeNeedsSnapshot()) {
+ return;
+ }
+
+ Task task = getTask();
+ if (mThumbnail == null && task != null && !hasCommittedReparentToAnimationLeash()) {
+ SurfaceControl.ScreenshotGraphicBuffer snapshot =
+ mWmService.mTaskSnapshotController.createTaskSnapshot(
+ task, 1 /* scaleFraction */);
+ if (snapshot != null) {
+ mThumbnail = new AppWindowThumbnail(mWmService.mSurfaceFactory, t, this,
+ snapshot.getGraphicBuffer(), true /* relative */);
+ }
+ }
+ }
+
+ @Override
+ public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
+ // The leash is parented to the animation layer. We need to preserve the z-order by using
+ // the prefix order index, but we boost if necessary.
+ int layer = 0;
+ if (!inPinnedWindowingMode()) {
+ layer = getPrefixOrderIndex();
+ } else {
+ // Pinned stacks have animations take place within themselves rather than an animation
+ // layer so we need to preserve the order relative to the stack (e.g. the order of our
+ // task/parent).
+ layer = getParent().getPrefixOrderIndex();
+ }
+
+ if (mNeedsZBoost) {
+ layer += Z_BOOST_BASE;
+ }
+ if (!mNeedsAnimationBoundsLayer) {
+ t.setLayer(leash, layer);
+ }
+
+ final DisplayContent dc = getDisplayContent();
+ dc.assignStackOrdering();
+
+ if (leash == mTransitChangeLeash) {
+ // This is a temporary state so skip any animation notifications
return;
+ } else if (mTransitChangeLeash != null) {
+ // unparent mTransitChangeLeash for clean-up
+ clearChangeLeash(t, false /* cancel */);
+ }
+
+ if (mAnimatingActivityRegistry != null) {
+ mAnimatingActivityRegistry.notifyStarting(this);
}
+ // If the animation needs to be cropped then an animation bounds layer is created as a child
+ // of the pinned stack or animation layer. The leash is then reparented to this new layer.
+ if (mNeedsAnimationBoundsLayer) {
+ mTmpRect.setEmpty();
+ final Task task = getTask();
+ if (getDisplayContent().mAppTransitionController.isTransitWithinTask(
+ getTransit(), task)) {
+ task.getBounds(mTmpRect);
+ } else {
+ final TaskStack stack = getStack();
+ if (stack == null) {
+ return;
+ }
+ // Set clip rect to stack bounds.
+ stack.getBounds(mTmpRect);
+ }
+ mAnimationBoundsLayer = createAnimationBoundsLayer(t);
+
+ // Crop to stack bounds.
+ t.setWindowCrop(mAnimationBoundsLayer, mTmpRect);
+ t.setLayer(mAnimationBoundsLayer, layer);
+
+ // Reparent leash to animation bounds layer.
+ t.reparent(leash, mAnimationBoundsLayer);
+ }
+ }
+
+ @Override
+ void prepareSurfaces() {
+ // isSelfAnimating also returns true when we are about to start a transition, so we need
+ // to check super here.
+ final boolean reallyAnimating = super.isSelfAnimating();
+ final boolean show = !isHidden() || reallyAnimating;
+
+ if (mSurfaceControl != null) {
+ if (show && !mLastSurfaceShowing) {
+ getPendingTransaction().show(mSurfaceControl);
+ } else if (!show && mLastSurfaceShowing) {
+ getPendingTransaction().hide(mSurfaceControl);
+ }
+ }
+ if (mThumbnail != null) {
+ mThumbnail.setShowing(getPendingTransaction(), show);
+ }
+ mLastSurfaceShowing = show;
+ super.prepareSurfaces();
+ }
+
+ /**
+ * @return Whether our {@link #getSurfaceControl} is currently showing.
+ */
+ boolean isSurfaceShowing() {
+ return mLastSurfaceShowing;
+ }
+
+ boolean isInChangeTransition() {
+ return mTransitChangeLeash != null || AppTransition.isChangeTransit(mTransit);
+ }
+
+ void attachThumbnailAnimation() {
+ if (!isReallyAnimating()) {
+ return;
+ }
+ final int taskId = getTask().mTaskId;
+ final GraphicBuffer thumbnailHeader =
+ getDisplayContent().mAppTransition.getAppTransitionThumbnailHeader(taskId);
+ if (thumbnailHeader == null) {
+ ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "No thumbnail header bitmap for: %d", taskId);
+ return;
+ }
+ clearThumbnail();
+ mThumbnail = new AppWindowThumbnail(mWmService.mSurfaceFactory, getPendingTransaction(),
+ this, thumbnailHeader);
+ mThumbnail.startAnimation(getPendingTransaction(), loadThumbnailAnimation(thumbnailHeader));
+ }
+
+ /**
+ * Attaches a surface with a thumbnail for the
+ * {@link android.app.ActivityOptions#ANIM_OPEN_CROSS_PROFILE_APPS} animation.
+ */
+ void attachCrossProfileAppsThumbnailAnimation() {
+ if (!isReallyAnimating()) {
+ return;
+ }
+ clearThumbnail();
+
+ final WindowState win = findMainWindow();
+ if (win == null) {
+ return;
+ }
+ final Rect frame = win.getFrameLw();
+ final int thumbnailDrawableRes = getTask().mUserId == mWmService.mCurrentUserId
+ ? R.drawable.ic_account_circle
+ : R.drawable.ic_corp_badge;
+ final GraphicBuffer thumbnail =
+ getDisplayContent().mAppTransition
+ .createCrossProfileAppsThumbnail(thumbnailDrawableRes, frame);
+ if (thumbnail == null) {
+ return;
+ }
+ mThumbnail = new AppWindowThumbnail(mWmService.mSurfaceFactory,
+ getPendingTransaction(), this, thumbnail);
+ final Animation animation =
+ getDisplayContent().mAppTransition.createCrossProfileAppsThumbnailAnimationLocked(
+ win.getFrameLw());
+ mThumbnail.startAnimation(getPendingTransaction(), animation, new Point(frame.left,
+ frame.top));
+ }
+
+ private Animation loadThumbnailAnimation(GraphicBuffer thumbnailHeader) {
+ final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
+
+ // If this is a multi-window scenario, we use the windows frame as
+ // destination of the thumbnail header animation. If this is a full screen
+ // window scenario, we use the whole display as the target.
+ WindowState win = findMainWindow();
+ Rect appRect = win != null ? win.getContentFrameLw() :
+ new Rect(0, 0, displayInfo.appWidth, displayInfo.appHeight);
+ final Rect insets = win != null ? win.getContentInsets() : null;
+ final Configuration displayConfig = mDisplayContent.getConfiguration();
+ return getDisplayContent().mAppTransition.createThumbnailAspectScaleAnimationLocked(
+ appRect, insets, thumbnailHeader, getTask().mTaskId, displayConfig.uiMode,
+ displayConfig.orientation);
+ }
+
+ @Override
+ boolean isAppAnimating() {
+ return isSelfAnimating();
+ }
+
+ @Override
+ boolean isSelfAnimating() {
+ // If we are about to start a transition, we also need to be considered animating.
+ return isWaitingForTransitionStart() || isReallyAnimating();
+ }
+
+ @Override
+ public void onAnimationLeashLost(Transaction t) {
+ super.onAnimationLeashLost(t);
+ if (mAnimationBoundsLayer != null) {
+ t.remove(mAnimationBoundsLayer);
+ mAnimationBoundsLayer = null;
+ }
+
+ if (mAnimatingActivityRegistry != null) {
+ mAnimatingActivityRegistry.notifyFinished(this);
+ }
+ }
+
+ @Override
+ protected void onAnimationFinished() {
+ super.onAnimationFinished();
+
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AWT#onAnimationFinished");
+ mTransit = TRANSIT_UNSET;
+ mTransitFlags = 0;
+ mNeedsZBoost = false;
+ mNeedsAnimationBoundsLayer = false;
+
+ setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM | FINISH_LAYOUT_REDO_WALLPAPER,
+ "AppWindowToken");
+
+ clearThumbnail();
+ setClientHidden(isHidden() && hiddenRequested);
+
+ getDisplayContent().computeImeTargetIfNeeded(this);
+
+ if (DEBUG_ANIM) Slog.v(TAG, "Animation done in " + this
+ + ": reportedVisible=" + reportedVisible
+ + " okToDisplay=" + okToDisplay()
+ + " okToAnimate=" + okToAnimate()
+ + " startingDisplayed=" + startingDisplayed);
+
+ // clean up thumbnail window
+ if (mThumbnail != null) {
+ mThumbnail.destroy();
+ mThumbnail = null;
+ }
+
+ // WindowState.onExitAnimationDone might modify the children list, so make a copy and then
+ // traverse the copy.
+ final ArrayList<WindowState> children = new ArrayList<>(mChildren);
+ children.forEach(WindowState::onExitAnimationDone);
+
+ getDisplayContent().mAppTransition.notifyAppTransitionFinishedLocked(token);
+ scheduleAnimation();
+
+ if (mAtmService.mRootActivityContainer.allResumedActivitiesIdle()
+ || mAtmService.mStackSupervisor.isStoppingNoHistoryActivity()) {
+ // If all activities are already idle or there is an activity that must be
+ // stopped immediately after visible, then we now need to make sure we perform
+ // the full stop of this activity. This is because we won't do that while they are still
+ // waiting for the animation to finish.
+ if (mAtmService.mStackSupervisor.mStoppingActivities.contains(this)) {
+ mAtmService.mStackSupervisor.scheduleIdleLocked();
+ }
+ } else {
+ // Instead of doing the full stop routine here, let's just hide any activities
+ // we now can, and let them stop when the normal idle happens.
+ mAtmService.mStackSupervisor.processStoppingActivitiesLocked(null /* idleActivity */,
+ false /* remove */, true /* processPausingActivities */);
+ }
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
+
+ /**
+ * @param cancel {@code true} if clearing the leash due to cancelling instead of transferring
+ * to another leash.
+ */
+ private void clearChangeLeash(Transaction t, boolean cancel) {
+ if (mTransitChangeLeash == null) {
+ return;
+ }
+ if (cancel) {
+ clearThumbnail();
+ SurfaceControl sc = getSurfaceControl();
+ SurfaceControl parentSc = getParentSurfaceControl();
+ // Don't reparent if surface is getting destroyed
+ if (parentSc != null && sc != null) {
+ t.reparent(sc, getParentSurfaceControl());
+ }
+ }
+ t.hide(mTransitChangeLeash);
+ t.remove(mTransitChangeLeash);
+ mTransitChangeLeash = null;
+ if (cancel) {
+ onAnimationLeashLost(t);
+ }
+ }
+
+ void clearAnimatingFlags() {
+ boolean wallpaperMightChange = false;
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ final WindowState win = mChildren.get(i);
+ wallpaperMightChange |= win.clearAnimatingFlags();
+ }
+ if (wallpaperMightChange) {
+ requestUpdateWallpaperIfNeeded();
+ }
+ }
+
+ /**
+ * @return True if and only if we are actually running an animation. Note that
+ * {@link #isSelfAnimating} also returns true if we are waiting for an animation to
+ * start.
+ */
+ private boolean isReallyAnimating() {
+ return super.isSelfAnimating();
+ }
+
+ @Override
+ void cancelAnimation() {
+ cancelAnimationOnly();
+ clearThumbnail();
+ clearChangeLeash(getPendingTransaction(), true /* cancel */);
+ }
+
+ /**
+ * This only cancels the animation. It doesn't do other teardown like cleaning-up thumbnail
+ * or interim leashes.
+ * <p>
+ * Used when canceling in preparation for starting a new animation.
+ */
+ void cancelAnimationOnly() {
+ super.cancelAnimation();
+ }
+
+ @VisibleForTesting
+ AppWindowThumbnail getThumbnail() {
+ return mThumbnail;
+ }
+
+ private void clearThumbnail() {
+ if (mThumbnail == null) {
+ return;
+ }
+ mThumbnail.destroy();
+ mThumbnail = null;
+ }
+
+ public int getTransit() {
+ return mTransit;
+ }
+
+ int getTransitFlags() {
+ return mTransitFlags;
+ }
+
+ void registerRemoteAnimations(RemoteAnimationDefinition definition) {
+ mRemoteAnimationDefinition = definition;
+ }
+
+ RemoteAnimationDefinition getRemoteAnimationDefinition() {
+ return mRemoteAnimationDefinition;
+ }
+
+ void setRequestedOrientation(int requestedOrientation) {
+ setOrientation(requestedOrientation, mayFreezeScreenLocked());
+ mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
+ task.mTaskId, requestedOrientation);
+ }
+
+ private void setOrientation(int requestedOrientation, boolean freezeScreenIfNeeded) {
final IBinder binder =
(freezeScreenIfNeeded && appToken != null) ? appToken.asBinder() : null;
- mAppWindowToken.setOrientation(requestedOrientation, binder, this);
+ setOrientation(requestedOrientation, binder, this);
// Push the new configuration to the requested app in case where it's not pushed, e.g. when
// the request is handled at task level with letterbox.
@@ -3584,21 +6445,47 @@ public final class ActivityRecord extends ConfigurationContainer {
}
}
- int getOrientation() {
- if (mAppWindowToken == null) {
- return info.screenOrientation;
+ void reportDescendantOrientationChangeIfNeeded() {
+ // Orientation request is exposed only when we're visible. Therefore visibility change
+ // will change requested orientation. Notify upward the hierarchy ladder to adjust
+ // configuration. This is important to cases where activities with incompatible
+ // orientations launch, or user goes back from an activity of bi-orientation to an
+ // activity with specified orientation.
+ if (getRequestedOrientation() == SCREEN_ORIENTATION_UNSET) {
+ return;
}
- return mAppWindowToken.getOrientationIgnoreVisibility();
+ final IBinder freezeToken = mayFreezeScreenLocked() ? appToken : null;
+ onDescendantOrientationChanged(freezeToken, this);
}
- void setDisablePreviewScreenshots(boolean disable) {
- if (mAppWindowToken == null) {
- Slog.w(TAG_WM, "Attempted to set disable screenshots of non-existing app"
- + " token: " + appToken);
- return;
+ /**
+ * We override because this class doesn't want its children affecting its reported orientation
+ * in anyway.
+ */
+ @Override
+ int getOrientation(int candidate) {
+ if (candidate == SCREEN_ORIENTATION_BEHIND) {
+ // Allow app to specify orientation regardless of its visibility state if the current
+ // candidate want us to use orientation behind. I.e. the visible app on-top of this one
+ // wants us to use the orientation of the app behind it.
+ return mOrientation;
}
- mAppWindowToken.setDisablePreviewScreenshots(disable);
+
+ // The {@link ActivityRecord} should only specify an orientation when it is not closing or
+ // going to the bottom. Allowing closing {@link ActivityRecord} to participate can lead to
+ // an Activity in another task being started in the wrong orientation during the transition.
+ if (!(sendingToBottom || getDisplayContent().mClosingApps.contains(this))
+ && (isVisible() || getDisplayContent().mOpeningApps.contains(this))) {
+ return mOrientation;
+ }
+
+ return SCREEN_ORIENTATION_UNSET;
+ }
+
+ /** Returns the app's preferred orientation regardless of its currently visibility state. */
+ int getRequestedOrientation() {
+ return mOrientation;
}
/**
@@ -3623,33 +6510,6 @@ public final class ActivityRecord extends ConfigurationContainer {
}
/**
- * Get the configuration orientation by the requested screen orientation
- * ({@link ActivityInfo.ScreenOrientation}) of this activity.
- *
- * @return orientation in ({@link Configuration#ORIENTATION_LANDSCAPE},
- * {@link Configuration#ORIENTATION_PORTRAIT},
- * {@link Configuration#ORIENTATION_UNDEFINED}).
- */
- int getRequestedConfigurationOrientation() {
- final int screenOrientation = getOrientation();
- if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
- // NOSENSOR means the display's "natural" orientation, so return that.
- final ActivityDisplay display = getDisplay();
- if (display != null && display.mDisplayContent != null) {
- return display.mDisplayContent.getNaturalOrientation();
- }
- } else if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
- // LOCKED means the activity's orientation remains unchanged, so return existing value.
- return getConfiguration().orientation;
- } else if (isFixedOrientationLandscape(screenOrientation)) {
- return ORIENTATION_LANDSCAPE;
- } else if (isFixedOrientationPortrait(screenOrientation)) {
- return ORIENTATION_PORTRAIT;
- }
- return ORIENTATION_UNDEFINED;
- }
-
- /**
* @return {@code true} if this activity is in size compatibility mode that uses the different
* density or bounds from its parent.
*/
@@ -3727,9 +6587,15 @@ public final class ActivityRecord extends ConfigurationContainer {
&& !mAtmService.mForceResizableActivities;
}
+ boolean hasSizeCompatBounds() {
+ return mSizeCompatBounds != null;
+ }
+
// TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
private void updateOverrideConfiguration() {
final Configuration overrideConfig = mTmpConfig;
+ overrideConfig.setTo(getRequestedOverrideConfiguration());
+
if (shouldUseSizeCompatMode()) {
if (mCompatDisplayInsets != null) {
// The override configuration is set only once in size compatibility mode.
@@ -3748,7 +6614,6 @@ public final class ActivityRecord extends ConfigurationContainer {
// are relative to bounds and density, they will be calculated in
// {@link TaskRecord#computeConfigResourceOverrides} and the result will also be
// relatively fixed.
- overrideConfig.unset();
overrideConfig.colorMode = parentConfig.colorMode;
overrideConfig.densityDpi = parentConfig.densityDpi;
overrideConfig.screenLayout = parentConfig.screenLayout
@@ -3775,7 +6640,6 @@ public final class ActivityRecord extends ConfigurationContainer {
return;
}
- overrideConfig.unset();
overrideConfig.windowConfiguration.setBounds(mTmpBounds);
}
@@ -3783,6 +6647,22 @@ public final class ActivityRecord extends ConfigurationContainer {
}
@Override
+ public boolean matchParentBounds() {
+ if (super.matchParentBounds()) {
+ return true;
+ }
+ // An activity in size compatibility mode may have override bounds which equals to its
+ // parent bounds, so the exact bounds should also be checked.
+ final WindowContainer parent = getParent();
+ return parent == null || parent.getBounds().equals(getResolvedOverrideBounds());
+ }
+
+ @Override
+ float getSizeCompatScale() {
+ return hasSizeCompatBounds() ? mSizeCompatScale : super.getSizeCompatScale();
+ }
+
+ @Override
void resolveOverrideConfiguration(Configuration newParentConfiguration) {
if (mCompatDisplayInsets != null) {
resolveSizeCompatModeConfiguration(newParentConfiguration);
@@ -3790,7 +6670,7 @@ public final class ActivityRecord extends ConfigurationContainer {
super.resolveOverrideConfiguration(newParentConfiguration);
// If the activity has override bounds, the relative configuration (e.g. screen size,
// layout) needs to be resolved according to the bounds.
- if (!matchParentBounds()) {
+ if (task != null && !matchParentBounds()) {
task.computeConfigResourceOverrides(getResolvedOverrideConfiguration(),
newParentConfiguration);
}
@@ -3878,7 +6758,7 @@ public final class ActivityRecord extends ConfigurationContainer {
mCompatDisplayInsets);
// The horizontal inset included in width is not needed if the activity cannot fill the
- // parent, because the offset will be applied by {@link AppWindowToken#mSizeCompatBounds}.
+ // parent, because the offset will be applied by {@link ActivityRecord#mSizeCompatBounds}.
final Rect resolvedAppBounds = resolvedConfig.windowConfiguration.getAppBounds();
final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
if (resolvedBounds.width() < parentAppBounds.width()) {
@@ -3892,9 +6772,100 @@ public final class ActivityRecord extends ConfigurationContainer {
}
@Override
+ public Rect getBounds() {
+ if (mSizeCompatBounds != null) {
+ return mSizeCompatBounds;
+ }
+ return super.getBounds();
+ }
+
+ @Override
+ Rect getDisplayedBounds() {
+ final Task task = getTask();
+ if (task != null) {
+ final Rect overrideDisplayedBounds = task.getOverrideDisplayedBounds();
+ if (!overrideDisplayedBounds.isEmpty()) {
+ return overrideDisplayedBounds;
+ }
+ }
+ return getBounds();
+ }
+
+ @VisibleForTesting
+ Rect getAnimationBounds(int appStackClipMode) {
+ if (appStackClipMode == STACK_CLIP_BEFORE_ANIM && getStack() != null) {
+ // Using the stack bounds here effectively applies the clipping before animation.
+ return getStack().getBounds();
+ }
+ // Use task-bounds if available so that activity-level letterbox (maxAspectRatio) is
+ // included in the animation.
+ return getTask() != null ? getTask().getBounds() : getBounds();
+ }
+
+ /**
+ * Calculates the scale and offset to horizontal center the size compatibility bounds into the
+ * region which is available to application.
+ */
+ private void calculateCompatBoundsTransformation(Configuration newParentConfig) {
+ final Rect parentAppBounds = newParentConfig.windowConfiguration.getAppBounds();
+ final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
+ final Rect viewportBounds = parentAppBounds != null ? parentAppBounds : parentBounds;
+ final Rect appBounds = getWindowConfiguration().getAppBounds();
+ final Rect contentBounds = appBounds != null ? appBounds : getResolvedOverrideBounds();
+ final float contentW = contentBounds.width();
+ final float contentH = contentBounds.height();
+ final float viewportW = viewportBounds.width();
+ final float viewportH = viewportBounds.height();
+ // Only allow to scale down.
+ mSizeCompatScale = (contentW <= viewportW && contentH <= viewportH)
+ ? 1 : Math.min(viewportW / contentW, viewportH / contentH);
+ final int offsetX = (int) ((viewportW - contentW * mSizeCompatScale + 1) * 0.5f)
+ + viewportBounds.left;
+
+ if (mSizeCompatBounds == null) {
+ mSizeCompatBounds = new Rect();
+ }
+ mSizeCompatBounds.set(contentBounds);
+ mSizeCompatBounds.offsetTo(0, 0);
+ mSizeCompatBounds.scale(mSizeCompatScale);
+ // Ensure to align the top with the parent.
+ mSizeCompatBounds.top = parentBounds.top;
+ // The decor inset is included in height.
+ mSizeCompatBounds.bottom += viewportBounds.top;
+ mSizeCompatBounds.left += offsetX;
+ mSizeCompatBounds.right += offsetX;
+ }
+
+ @Override
public void onConfigurationChanged(Configuration newParentConfig) {
+ final int prevWinMode = getWindowingMode();
+ mTmpPrevBounds.set(getBounds());
super.onConfigurationChanged(newParentConfig);
+ final Task task = getTask();
+ final Rect overrideBounds = getResolvedOverrideBounds();
+ if (task != null && !overrideBounds.isEmpty()
+ // If the changes come from change-listener, the incoming parent configuration is
+ // still the old one. Make sure their orientations are the same to reduce computing
+ // the compatibility bounds for the intermediate state.
+ && (task.mTaskRecord == null || task.mTaskRecord
+ .getConfiguration().orientation == newParentConfig.orientation)) {
+ final Rect taskBounds = task.getBounds();
+ // Since we only center the activity horizontally, if only the fixed height is smaller
+ // than its container, the override bounds don't need to take effect.
+ if ((overrideBounds.width() != taskBounds.width()
+ || overrideBounds.height() > taskBounds.height())) {
+ calculateCompatBoundsTransformation(newParentConfig);
+ updateSurfacePosition();
+ } else if (mSizeCompatBounds != null) {
+ mSizeCompatBounds = null;
+ mSizeCompatScale = 1f;
+ updateSurfacePosition();
+ }
+ }
+
+ adjustPinnedStackAndInitChangeTransitionIfNeeded(prevWinMode, getWindowingMode());
+
// Configuration's equality doesn't consider seq so if only seq number changes in resolved
// override configuration. Therefore ConfigurationContainer doesn't change merged override
// configuration, but it's used to push configuration changes so explicitly update that.
@@ -3902,22 +6873,6 @@ public final class ActivityRecord extends ConfigurationContainer {
onMergedOverrideConfigurationChanged();
}
- // TODO(b/80414790): Remove code below after unification.
- // Same as above it doesn't notify configuration listeners, and consequently AppWindowToken
- // can't get updated seq number. However WindowState's merged override configuration needs
- // to have this seq number because that's also used for activity config pushes during layout
- // traversal. Therefore explicitly update them here.
- if (mAppWindowToken == null) {
- return;
- }
- final Configuration appWindowTokenRequestedOverrideConfig =
- mAppWindowToken.getRequestedOverrideConfiguration();
- if (appWindowTokenRequestedOverrideConfig.seq != getResolvedOverrideConfiguration().seq) {
- appWindowTokenRequestedOverrideConfig.seq =
- getResolvedOverrideConfiguration().seq;
- mAppWindowToken.onMergedOverrideConfigurationChanged();
- }
-
final ActivityDisplay display = getDisplay();
if (display == null) {
return;
@@ -3943,9 +6898,40 @@ public final class ActivityRecord extends ConfigurationContainer {
}
}
+ private void adjustPinnedStackAndInitChangeTransitionIfNeeded(int prevWinMode, int winMode) {
+ if (prevWinMode == winMode || mDisplayContent == null) {
+ return;
+ }
+
+ if (prevWinMode == WINDOWING_MODE_PINNED && winMode != WINDOWING_MODE_UNDEFINED
+ && !isHidden()) {
+ // Leaving PiP to fullscreen, save the snap fraction based on the pre-animation bounds
+ // for the next re-entry into PiP (assuming the activity is not hidden or destroyed)
+ final TaskStack pinnedStack = mDisplayContent.getPinnedStack();
+ if (pinnedStack != null) {
+ final Rect stackBounds;
+ if (pinnedStack.lastAnimatingBoundsWasToFullscreen()) {
+ // We are animating the bounds, use the pre-animation bounds to save the snap
+ // fraction
+ stackBounds = pinnedStack.mPreAnimationBounds;
+ } else {
+ // We skip the animation if the fullscreen configuration is not compatible, so
+ // use the current bounds to calculate the saved snap fraction instead
+ // (see PinnedActivityStack.skipResizeAnimation())
+ stackBounds = mTmpRect;
+ pinnedStack.getBounds(stackBounds);
+ }
+ mDisplayContent.mPinnedStackControllerLocked.saveReentrySnapFraction(
+ mActivityComponent, stackBounds);
+ }
+ } else if (shouldStartChangeTransition(prevWinMode, winMode)) {
+ initializeChangeTransition(mTmpPrevBounds);
+ }
+ }
+
/** Returns true if the configuration is compatible with this activity. */
boolean isConfigurationCompatible(Configuration config) {
- final int orientation = getOrientation();
+ final int orientation = getRequestedOrientation();
if (isFixedOrientationPortrait(orientation)
&& config.orientation != ORIENTATION_PORTRAIT) {
return false;
@@ -4178,7 +7164,7 @@ public final class ActivityRecord extends ConfigurationContainer {
if (shouldRelaunchLocked(changes, mTmpConfig) || forceNewConfig) {
// Aha, the activity isn't handling the change, so DIE DIE DIE.
configChangeFlags |= changes;
- startFreezingScreenLocked(app, globalChanges);
+ startFreezingScreenLocked(globalChanges);
forceNewConfig = false;
preserveWindow &= isResizeOnlyChange(changes);
final boolean hasResizeChange = hasResizeChange(changes & ~info.getRealConfigChanged());
@@ -4334,18 +7320,16 @@ public final class ActivityRecord extends ConfigurationContainer {
+ " preserveWindow=" + preserveWindow);
EventLog.writeEvent(andResume ? AM_RELAUNCH_RESUME_ACTIVITY
: AM_RELAUNCH_ACTIVITY, mUserId, System.identityHashCode(this),
- task.taskId, shortComponentName);
+ task.mTaskId, shortComponentName);
- startFreezingScreenLocked(app, 0);
+ startFreezingScreenLocked(0);
try {
if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_SWITCH,
"Moving to " + (andResume ? "RESUMED" : "PAUSED") + " Relaunching " + this
+ " callers=" + Debug.getCallers(6));
forceNewConfig = false;
- if (mAppWindowToken != null) {
- mAppWindowToken.startRelaunching();
- }
+ startRelaunching();
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
pendingNewIntents, configChangeFlags,
new MergedConfiguration(mAtmService.getGlobalConfiguration(),
@@ -4434,8 +7418,8 @@ public final class ActivityRecord extends ConfigurationContainer {
return;
}
- if (mAppWindowToken != null) {
- mAppWindowToken.startFreezingScreen();
+ if (getParent() != null) {
+ startFreezingScreen();
}
// The process will be killed until the activity reports stopped with saved state (see
// {@link ActivityTaskManagerService.activityStopped}).
@@ -4464,12 +7448,19 @@ public final class ActivityRecord extends ConfigurationContainer {
return true;
}
- // Restrict task snapshot starting window to launcher start, or there is no intent at all
- // (eg. task being brought to front). If the intent is something else, likely the app is
- // going to show some specific page or view, instead of what's left last time.
+ // Restrict task snapshot starting window to launcher start, or is same as the last
+ // delivered intent, or there is no intent at all (eg. task being brought to front). If
+ // the intent is something else, likely the app is going to show some specific page or
+ // view, instead of what's left last time.
for (int i = newIntents.size() - 1; i >= 0; i--) {
final Intent intent = newIntents.get(i);
- if (intent != null && !ActivityRecord.isMainIntent(intent)) {
+ if (intent == null || ActivityRecord.isMainIntent(intent)) {
+ continue;
+ }
+
+ final boolean sameIntent = mLastNewIntent != null ? mLastNewIntent.filterEquals(intent)
+ : this.intent.filterEquals(intent);
+ if (!sameIntent || intent.getExtras() != null) {
return false;
}
}
@@ -4603,68 +7594,32 @@ public final class ActivityRecord extends ConfigurationContainer {
return info.applicationInfo.uid;
}
- void setShowWhenLocked(boolean showWhenLocked) {
- mShowWhenLocked = showWhenLocked;
- mRootActivityContainer.ensureActivitiesVisible(null, 0 /* configChanges */,
- false /* preserveWindows */);
- }
-
- void setInheritShowWhenLocked(boolean inheritShowWhenLocked) {
- mInheritShownWhenLocked = inheritShowWhenLocked;
- mRootActivityContainer.ensureActivitiesVisible(null, 0, false);
+ int getPid() {
+ return app != null ? app.getPid() : 0;
}
/**
- * @return true if the activity windowing mode is not
- * {@link android.app.WindowConfiguration#WINDOWING_MODE_PINNED} and a) activity
- * contains windows that have {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} set or if the
- * activity has set {@link #mShowWhenLocked}, or b) if the activity has set
- * {@link #mInheritShownWhenLocked} and the activity behind this satisfies the
- * conditions a) above.
- * Multi-windowing mode will be exited if true is returned.
+ * 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
+ *
+ * @return true if the screen can be turned on, false otherwise.
*/
- boolean canShowWhenLocked() {
- if (!inPinnedWindowingMode() && (mShowWhenLocked
- || (mAppWindowToken != null && mAppWindowToken.containsShowWhenLockedWindow()))) {
- return true;
- } else if (mInheritShownWhenLocked) {
- ActivityRecord r = getActivityBelow();
- return r != null && !r.inPinnedWindowingMode() && (r.mShowWhenLocked
- || (r.mAppWindowToken != null
- && r.mAppWindowToken.containsShowWhenLockedWindow()));
- } else {
+ boolean canTurnScreenOn() {
+ if (!getTurnScreenOnFlag()) {
return false;
}
- }
-
- /**
- * @return an {@link ActivityRecord} of the activity below this activity, or {@code null} if no
- * such activity exists.
- */
- @Nullable
- private ActivityRecord getActivityBelow() {
- final int pos = task.mActivities.indexOf(this);
- if (pos == -1) {
- throw new IllegalStateException("Activity not found in its task");
- }
- return pos == 0 ? null : task.getChildAt(pos - 1);
+ final ActivityStack stack = getActivityStack();
+ return stack != null &&
+ stack.checkKeyguardVisibility(this, true /* shouldBeVisible */, true /* isTop */);
}
void setTurnScreenOn(boolean turnScreenOn) {
mTurnScreenOn = turnScreenOn;
}
- /**
- * Determines whether this ActivityRecord can turn the screen on. It checks whether the flag
- * {@link #mTurnScreenOn} is set and checks whether the ActivityRecord should be visible
- * depending on Keyguard state
- *
- * @return true if the screen can be turned on, false otherwise.
- */
- boolean canTurnScreenOn() {
- final ActivityStack stack = getActivityStack();
- return mTurnScreenOn && stack != null &&
- stack.checkKeyguardVisibility(this, true /* shouldBeVisible */, true /* isTop */);
+ boolean getTurnScreenOnFlag() {
+ return mTurnScreenOn;
}
/**
@@ -4677,10 +7632,6 @@ public final class ActivityRecord extends ConfigurationContainer {
return app == null || app.updateTopResumingActivityInProcessIfNeeded(this);
}
- boolean getTurnScreenOnFlag() {
- return mTurnScreenOn;
- }
-
boolean isTopRunningActivity() {
return mRootActivityContainer.topRunningActivity() == this;
}
@@ -4715,20 +7666,11 @@ public final class ActivityRecord extends ConfigurationContainer {
return task.getChildAt(0) == this;
}
- void registerRemoteAnimations(RemoteAnimationDefinition definition) {
- if (mAppWindowToken == null) {
- Slog.w(TAG_WM, "Attempted to register remote animations with non-existing app"
- + " token: " + appToken);
- return;
- }
- mAppWindowToken.registerRemoteAnimations(definition);
- }
-
@Override
public String toString() {
if (stringName != null) {
- return stringName + " t" + (task == null ? INVALID_TASK_ID : task.taskId) +
- (finishing ? " f}" : "}");
+ return stringName + " t" + (task == null ? INVALID_TASK_ID : task.mTaskId) +
+ (finishing ? " f}" : "") + (mIsExiting ? " mIsExiting=" : "") + "}";
}
StringBuilder sb = new StringBuilder(128);
sb.append("ActivityRecord{");
@@ -4738,15 +7680,7 @@ public final class ActivityRecord extends ConfigurationContainer {
sb.append(' ');
sb.append(intent.getComponent().flattenToShortString());
stringName = sb.toString();
- return toString();
- }
-
- void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
- final long token = proto.start(fieldId);
- proto.write(HASH_CODE, System.identityHashCode(this));
- proto.write(USER_ID, mUserId);
- proto.write(TITLE, intent.getComponent().flattenToShortString());
- proto.end(token);
+ return stringName;
}
/**
@@ -4754,7 +7688,7 @@ public final class ActivityRecord extends ConfigurationContainer {
* {@code ActivityRecordProto} is the outer-most proto data.
*/
void writeToProto(ProtoOutputStream proto) {
- super.writeToProto(proto, CONFIGURATION_CONTAINER, WindowTraceLogLevel.ALL);
+ writeToProto(proto, APP_WINDOW_TOKEN, WindowTraceLogLevel.ALL);
writeIdentifierToProto(proto, IDENTIFIER);
proto.write(STATE, mState.toString());
proto.write(VISIBLE, visible);
@@ -4762,7 +7696,7 @@ public final class ActivityRecord extends ConfigurationContainer {
if (hasProcess()) {
proto.write(PROC_ID, app.getPid());
}
- proto.write(TRANSLUCENT, !fullscreen);
+ proto.write(TRANSLUCENT, !occludesParent());
}
public void writeToProto(ProtoOutputStream proto, long fieldId) {
@@ -4772,6 +7706,65 @@ public final class ActivityRecord extends ConfigurationContainer {
}
/**
+ * Copied from old AppWindowToken.
+ */
+ @Override
+ public void writeToProto(ProtoOutputStream proto, long fieldId,
+ @WindowTraceLogLevel int logLevel) {
+ // Critical log level logs only visible elements to mitigate performance overheard
+ if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
+ return;
+ }
+
+ final long token = proto.start(fieldId);
+ writeNameToProto(proto, NAME);
+ super.writeToProto(proto, WINDOW_TOKEN, logLevel);
+ proto.write(LAST_SURFACE_SHOWING, mLastSurfaceShowing);
+ proto.write(IS_WAITING_FOR_TRANSITION_START, isWaitingForTransitionStart());
+ proto.write(IS_REALLY_ANIMATING, isReallyAnimating());
+ if (mThumbnail != null){
+ mThumbnail.writeToProto(proto, THUMBNAIL);
+ }
+ proto.write(FILLS_PARENT, mOccludesParent);
+ proto.write(APP_STOPPED, mAppStopped);
+ proto.write(HIDDEN_REQUESTED, hiddenRequested);
+ proto.write(CLIENT_HIDDEN, mClientHidden);
+ proto.write(DEFER_HIDING_CLIENT, mDeferHidingClient);
+ proto.write(REPORTED_DRAWN, reportedDrawn);
+ proto.write(REPORTED_VISIBLE, reportedVisible);
+ proto.write(NUM_INTERESTING_WINDOWS, mNumInterestingWindows);
+ proto.write(NUM_DRAWN_WINDOWS, mNumDrawnWindows);
+ proto.write(ALL_DRAWN, allDrawn);
+ proto.write(LAST_ALL_DRAWN, mLastAllDrawn);
+ proto.write(REMOVED, removed);
+ if (startingWindow != null) {
+ startingWindow.writeIdentifierToProto(proto, STARTING_WINDOW);
+ }
+ proto.write(STARTING_DISPLAYED, startingDisplayed);
+ proto.write(STARTING_MOVED, startingMoved);
+ proto.write(HIDDEN_SET_FROM_TRANSFERRED_STARTING_WINDOW,
+ mHiddenSetFromTransferredStartingWindow);
+ for (Rect bounds : mFrozenBounds) {
+ bounds.writeToProto(proto, FROZEN_BOUNDS);
+ }
+ proto.end(token);
+ }
+
+ void writeNameToProto(ProtoOutputStream proto, long fieldId) {
+ if (appToken != null) {
+ proto.write(fieldId, appToken.getName());
+ }
+ }
+
+ void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(HASH_CODE, System.identityHashCode(this));
+ proto.write(USER_ID, mUserId);
+ proto.write(TITLE, intent.getComponent().flattenToShortString());
+ proto.end(token);
+ }
+
+ /**
* The precomputed insets of the display in each rotation. This is used to make the size
* compatibility mode activity compute the configuration without relying on its current display.
*/
@@ -4824,4 +7817,14 @@ public final class ActivityRecord extends ConfigurationContainer {
isLandscape ? shortSide : longSide);
}
}
+
+ private static class AppSaturationInfo {
+ float[] mMatrix = new float[9];
+ float[] mTranslation = new float[3];
+
+ void setSaturation(@Size(9) float[] matrix, @Size(3) float[] translation) {
+ System.arraycopy(matrix, 0, mMatrix, 0, mMatrix.length);
+ System.arraycopy(translation, 0, mTranslation, 0, mTranslation.length);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 44a4903903ea..8589669e2b74 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -33,7 +33,7 @@ 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_ACTIVITY_MANAGER;
+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;
@@ -496,7 +496,7 @@ public class ActivityStack extends ConfigurationContainer {
int numActivities() {
int count = 0;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- count += mTaskHistory.get(taskNdx).mActivities.size();
+ count += mTaskHistory.get(taskNdx).getChildCount();
}
return count;
}
@@ -783,7 +783,7 @@ public class ActivityStack extends ConfigurationContainer {
// multi-window mode.
final String packageName = topActivity.info.applicationInfo.packageName;
mService.getTaskChangeNotificationController().notifyActivityForcedResizable(
- topTask.taskId, FORCED_RESIZEABLE_REASON_SPLIT_SCREEN, packageName);
+ topTask.mTaskId, FORCED_RESIZEABLE_REASON_SPLIT_SCREEN, packageName);
}
mService.deferWindowLayout();
@@ -1085,9 +1085,8 @@ public class ActivityStack extends ConfigurationContainer {
ActivityRecord topRunningNonOverlayTaskActivity() {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
- final ArrayList<ActivityRecord> activities = task.mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if (!r.finishing && !r.mTaskOverlay) {
return r;
}
@@ -1099,9 +1098,8 @@ public class ActivityStack extends ConfigurationContainer {
ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
- final ArrayList<ActivityRecord> activities = task.mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = activities.get(activityNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if (!r.finishing && !r.delayedResume && r != notTop && r.okToShowLocked()) {
return r;
}
@@ -1122,12 +1120,11 @@ public class ActivityStack extends ConfigurationContainer {
final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
TaskRecord task = mTaskHistory.get(taskNdx);
- if (task.taskId == taskId) {
+ if (task.mTaskId == taskId) {
continue;
}
- ArrayList<ActivityRecord> activities = task.mActivities;
- for (int i = activities.size() - 1; i >= 0; --i) {
- final ActivityRecord r = activities.get(i);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
// Note: the taskId check depends on real taskId fields being non-zero
if (!r.finishing && (token != r.appToken) && r.okToShowLocked()) {
return r;
@@ -1158,7 +1155,7 @@ public class ActivityStack extends ConfigurationContainer {
TaskRecord taskForIdLocked(int id) {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
- if (task.taskId == id) {
+ if (task.mTaskId == id) {
return task;
}
}
@@ -1353,7 +1350,7 @@ public class ActivityStack extends ConfigurationContainer {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": voice session");
continue;
}
- if (task.userId != userId) {
+ if (task.mUserId != userId) {
// Looking for a different task.
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": different user");
continue;
@@ -1439,10 +1436,8 @@ public class ActivityStack extends ConfigurationContainer {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
- final ArrayList<ActivityRecord> activities = task.mActivities;
-
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = activities.get(activityNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if (!r.okToShowLocked()) {
continue;
}
@@ -1508,9 +1503,10 @@ public class ActivityStack extends ConfigurationContainer {
void awakeFromSleepingLocked() {
// Ensure activities are no longer sleeping.
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- activities.get(activityNdx).setSleeping(false);
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
+ r.setSleeping(false);
}
}
if (mPausingActivity != null) {
@@ -1524,9 +1520,9 @@ public class ActivityStack extends ConfigurationContainer {
final int userId = UserHandle.getUserId(aInfo.uid);
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- final List<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord ar = activities.get(activityNdx);
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord ar = task.getChildAt(activityNdx);
if ((userId == ar.mUserId) && packageName.equals(ar.packageName)) {
ar.updateApplicationInfo(aInfo);
@@ -1602,9 +1598,9 @@ public class ActivityStack extends ConfigurationContainer {
// Make sure any paused or stopped but visible activities are now sleeping.
// This ensures that the activity's onStop() is called.
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if (r.isState(STARTED, STOPPING, STOPPED, PAUSED, PAUSING)) {
r.setSleeping(true);
}
@@ -1897,9 +1893,8 @@ public class ActivityStack extends ConfigurationContainer {
}
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
- final ArrayList<ActivityRecord> activities = task.mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if (r.finishing) {
// We don't factor in finishing activities when determining translucency since
@@ -1913,7 +1908,7 @@ public class ActivityStack extends ConfigurationContainer {
continue;
}
- if (r.fullscreen || r.hasWallpaper) {
+ if (r.occludesParent() || r.hasWallpaper) {
// Stack isn't translucent if it has at least one fullscreen activity
// that is visible.
return false;
@@ -2123,12 +2118,15 @@ public class ActivityStack extends ConfigurationContainer {
boolean aboveTop = top != null;
final boolean stackShouldBeVisible = shouldBeVisible(starting);
boolean behindFullscreenActivity = !stackShouldBeVisible;
- final boolean resumeTopActivity = isFocusable() && isInStackLocked(starting) == null;
+ // We should not resume activities that being launched behind because these
+ // activities are actually behind other fullscreen activities, but still required
+ // to be visible (such as performing Recents animation).
+ final boolean resumeTopActivity = isFocusable() && isInStackLocked(starting) == null
+ && top != null && !top.mLaunchTaskBehind;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
- final ArrayList<ActivityRecord> activities = task.mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
final boolean isTop = r == top;
if (aboveTop && !isTop) {
continue;
@@ -2272,8 +2270,7 @@ public class ActivityStack extends ConfigurationContainer {
.isKeyguardOrAodShowing(displayId);
final boolean keyguardLocked = mStackSupervisor.getKeyguardController().isKeyguardLocked();
final boolean showWhenLocked = r.canShowWhenLocked();
- final boolean dismissKeyguard = r.mAppWindowToken != null
- && r.mAppWindowToken.containsDismissKeyguardWindow();
+ final boolean dismissKeyguard = r.containsDismissKeyguardWindow();
if (shouldBeVisible) {
if (dismissKeyguard && mTopDismissingKeyguardActivity == null) {
mTopDismissingKeyguardActivity = r;
@@ -2342,18 +2339,14 @@ public class ActivityStack extends ConfigurationContainer {
// get it started and resume if no other stack in this stack is resumed.
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Start and freeze screen for " + r);
if (r != starting) {
- r.startFreezingScreenLocked(r.app, configChanges);
+ r.startFreezingScreenLocked(configChanges);
}
if (!r.visible || r.mLaunchTaskBehind) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Starting and making visible: " + r);
r.setVisible(true);
}
if (r != starting) {
- // We should not resume activities that being launched behind because these
- // activities are actually behind other fullscreen activities, but still required
- // to be visible (such as performing Recents animation).
- mStackSupervisor.startSpecificActivityLocked(r, andResume && !r.mLaunchTaskBehind,
- true /* checkConfig */);
+ mStackSupervisor.startSpecificActivityLocked(r, andResume, true /* checkConfig */);
return true;
}
}
@@ -2362,7 +2355,7 @@ public class ActivityStack extends ConfigurationContainer {
private boolean updateBehindFullscreen(boolean stackInvisible, boolean behindFullscreenActivity,
ActivityRecord r) {
- if (r.fullscreen) {
+ if (r.occludesParent()) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r
+ " stackInvisible=" + stackInvisible
+ " behindFullscreenActivity=" + behindFullscreenActivity);
@@ -2381,9 +2374,8 @@ public class ActivityStack extends ConfigurationContainer {
void clearOtherAppTimeTrackers(AppTimeTracker except) {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
- final ArrayList<ActivityRecord> activities = task.mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if ( r.appTimeTracker != except) {
r.appTimeTracker = null;
}
@@ -2441,19 +2433,19 @@ public class ActivityStack extends ConfigurationContainer {
}
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if (aboveTop) {
if (r == topActivity) {
aboveTop = false;
}
- behindFullscreenActivity |= r.fullscreen;
+ behindFullscreenActivity |= r.occludesParent();
continue;
}
r.removeOrphanedStartingWindow(behindFullscreenActivity);
- behindFullscreenActivity |= r.fullscreen;
+ behindFullscreenActivity |= r.occludesParent();
}
}
}
@@ -2583,8 +2575,7 @@ public class ActivityStack extends ConfigurationContainer {
final boolean canShowWhenLocked = !mTopActivityOccludesKeyguard
&& next.canShowWhenLocked();
final boolean mayDismissKeyguard = mTopDismissingKeyguardActivity != next
- && next.mAppWindowToken != null
- && next.mAppWindowToken.containsDismissKeyguardWindow();
+ && next.containsDismissKeyguardWindow();
if (canShowWhenLocked || mayDismissKeyguard) {
ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
@@ -2622,7 +2613,7 @@ public class ActivityStack extends ConfigurationContainer {
if (mActivityTrigger != null) {
mActivityTrigger.activityResumeTrigger(next.intent, next.info, next.info.applicationInfo,
- next.fullscreen);
+ next.occludesParent());
}
if (mActivityPluginDelegate != null && getWindowingMode() != WINDOWING_MODE_UNDEFINED) {
@@ -2821,7 +2812,7 @@ public class ActivityStack extends ConfigurationContainer {
final boolean lastActivityTranslucent = lastFocusedStack != null
&& (lastFocusedStack.inMultiWindowMode()
|| (lastFocusedStack.mLastPausedActivity != null
- && !lastFocusedStack.mLastPausedActivity.fullscreen));
+ && !lastFocusedStack.mLastPausedActivity.occludesParent()));
// This activity is now becoming visible.
if (!next.visible || next.stopped || lastActivityTranslucent) {
@@ -2910,7 +2901,7 @@ public class ActivityStack extends ConfigurationContainer {
next.notifyAppResumed(next.stopped);
EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.mUserId,
- System.identityHashCode(next), next.getTaskRecord().taskId,
+ System.identityHashCode(next), next.getTaskRecord().mTaskId,
next.shortComponentName);
next.sleeping = false;
@@ -3020,7 +3011,7 @@ public class ActivityStack extends ConfigurationContainer {
// The task can't be shown, put non-current user tasks below current user tasks.
while (maxPosition > 0) {
final TaskRecord tmpTask = mTaskHistory.get(maxPosition - 1);
- if (!mStackSupervisor.isCurrentProfileLocked(tmpTask.userId)
+ if (!mStackSupervisor.isCurrentProfileLocked(tmpTask.mUserId)
|| tmpTask.topRunningActivityLocked() == null) {
break;
}
@@ -3073,7 +3064,7 @@ public class ActivityStack extends ConfigurationContainer {
void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
boolean newTask, boolean keepCurTransition, ActivityOptions options) {
TaskRecord rTask = r.getTaskRecord();
- final int taskId = rTask.taskId;
+ final int taskId = rTask.mTaskId;
final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront();
// mLaunchTaskBehind tasks get placed at the back of the task stack.
if (!r.mLaunchTaskBehind && allowMoveToFront
@@ -3100,7 +3091,7 @@ public class ActivityStack extends ConfigurationContainer {
if (!startIt) {
if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task "
+ task, new RuntimeException("here").fillInStackTrace());
- r.createAppWindowToken();
+ r.setTask(rTask);
ActivityOptions.abort(options);
return;
}
@@ -3127,16 +3118,11 @@ public class ActivityStack extends ConfigurationContainer {
// 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());
- // TODO: Need to investigate if it is okay for the controller to already be created by the
- // time we get to this point. I think it is, but need to double check.
- // Use test in b/34179495 to trace the call path.
- if (r.mAppWindowToken == null) {
- r.createAppWindowToken();
- }
+ r.setTask(task);
if (mActivityPluginDelegate != null) {
mActivityPluginDelegate.activityInvokeNotification
- (r.info.applicationInfo.packageName, r.fullscreen);
+ (r.info.applicationInfo.packageName, r.occludesParent());
}
// The transition animation and starting window are not needed if {@code allowMoveToFront}
@@ -3267,14 +3253,13 @@ public class ActivityStack extends ConfigurationContainer {
// We only do this for activities that are not the root of the task (since if we finish
// the root, we may no longer have the task!).
- final ArrayList<ActivityRecord> activities = task.mActivities;
- final int numActivities = activities.size();
+ final int numActivities = task.getChildCount();
int lastActivityNdx = task.findRootIndex(true /* effectiveRoot */);
if (lastActivityNdx == -1) {
lastActivityNdx = 0;
}
for (int i = numActivities - 1; i > lastActivityNdx; --i) {
- ActivityRecord target = activities.get(i);
+ ActivityRecord target = task.getChildAt(i);
// TODO: Why is this needed? Looks like we're breaking the loop before we reach the root
if (target.isRootOfTask()) break;
@@ -3314,7 +3299,7 @@ public class ActivityStack extends ConfigurationContainer {
final TaskRecord targetTask;
final ActivityRecord bottom =
!mTaskHistory.isEmpty() && !mTaskHistory.get(0).mActivities.isEmpty() ?
- mTaskHistory.get(0).mActivities.get(0) : null;
+ mTaskHistory.get(0).getChildAt(0) : null;
if (bottom != null && target.taskAffinity.equals(bottom.getTaskRecord().affinity)) {
// If the activity currently at the bottom has the
// same task affinity as the one we are moving,
@@ -3336,7 +3321,7 @@ public class ActivityStack extends ConfigurationContainer {
boolean noOptions = canMoveOptions;
final int start = replyChainEnd < 0 ? i : replyChainEnd;
for (int srcPos = start; srcPos >= i; --srcPos) {
- final ActivityRecord p = activities.get(srcPos);
+ final ActivityRecord p = task.getChildAt(srcPos);
if (p.finishing) {
continue;
}
@@ -3369,7 +3354,7 @@ public class ActivityStack extends ConfigurationContainer {
// In this case, we want to finish this activity
// and everything above it, so be sneaky and pretend
// like these are all in the reply chain.
- end = activities.size() - 1;
+ end = task.getChildCount() - 1;
} else if (replyChainEnd < 0) {
end = i;
} else {
@@ -3377,7 +3362,7 @@ public class ActivityStack extends ConfigurationContainer {
}
boolean noOptions = canMoveOptions;
for (int srcPos = i; srcPos <= end; srcPos++) {
- ActivityRecord p = activities.get(srcPos);
+ ActivityRecord p = task.getChildAt(srcPos);
if (p.finishing) {
continue;
}
@@ -3446,8 +3431,7 @@ public class ActivityStack extends ConfigurationContainer {
int replyChainEnd = -1;
final String taskAffinity = task.affinity;
- final ArrayList<ActivityRecord> activities = affinityTask.mActivities;
- final int numActivities = activities.size();
+ final int numActivities = task.getChildCount();
// Do not operate on or below the effective root Activity.
int lastActivityNdx = affinityTask.findRootIndex(true /* effectiveRoot */);
@@ -3455,7 +3439,7 @@ public class ActivityStack extends ConfigurationContainer {
lastActivityNdx = 0;
}
for (int i = numActivities - 1; i > lastActivityNdx; --i) {
- ActivityRecord target = activities.get(i);
+ ActivityRecord target = task.getChildAt(i);
// TODO: Why is this needed? Looks like we're breaking the loop before we reach the root
if (target.isRootOfTask()) break;
@@ -3492,7 +3476,7 @@ public class ActivityStack extends ConfigurationContainer {
if (DEBUG_TASKS) Slog.v(TAG_TASKS,
"Finishing task at index " + start + " to " + i);
for (int srcPos = start; srcPos >= i; --srcPos) {
- final ActivityRecord p = activities.get(srcPos);
+ final ActivityRecord p = task.getChildAt(srcPos);
if (p.finishing) {
continue;
}
@@ -3500,7 +3484,7 @@ public class ActivityStack extends ConfigurationContainer {
}
} else {
if (taskInsertionPoint < 0) {
- taskInsertionPoint = task.mActivities.size();
+ taskInsertionPoint = task.getChildCount();
}
@@ -3509,7 +3493,7 @@ public class ActivityStack extends ConfigurationContainer {
"Reparenting from task=" + affinityTask + ":" + start + "-" + i
+ " to task=" + task + ":" + taskInsertionPoint);
for (int srcPos = start; srcPos >= i; --srcPos) {
- final ActivityRecord p = activities.get(srcPos);
+ final ActivityRecord p = task.getChildAt(srcPos);
p.reparent(task, taskInsertionPoint, "resetAffinityTaskIfNeededLocked");
if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE,
@@ -3646,9 +3630,9 @@ public class ActivityStack extends ConfigurationContainer {
/** Finish all activities that were started for result from the specified activity. */
final void finishSubActivityLocked(ActivityRecord self, String resultWho, int requestCode) {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = activities.get(activityNdx);
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if (r.resultTo == self && r.requestCode == requestCode) {
if ((r.resultWho == null && resultWho == null) ||
(r.resultWho != null && r.resultWho.equals(resultWho))) {
@@ -3694,11 +3678,11 @@ public class ActivityStack extends ConfigurationContainer {
if (taskNdx < 0) {
break;
}
- activityNdx = mTaskHistory.get(taskNdx).mActivities.size() - 1;
+ activityNdx = mTaskHistory.get(taskNdx).getChildCount() - 1;
} while (activityNdx < 0);
}
if (activityNdx >= 0) {
- r = mTaskHistory.get(taskNdx).mActivities.get(activityNdx);
+ r = mTaskHistory.get(taskNdx).getChildAt(activityNdx);
if (r.isState(STARTED, RESUMED, PAUSING, PAUSED)) {
if (!r.isActivityTypeHome() || mService.mHomeProcess != r.app) {
Slog.w(TAG, " Force finishing activity "
@@ -3716,8 +3700,8 @@ public class ActivityStack extends ConfigurationContainer {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
TaskRecord tr = mTaskHistory.get(taskNdx);
if (tr.voiceSession != null && tr.voiceSession.asBinder() == sessionBinder) {
- for (int activityNdx = tr.mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = tr.mActivities.get(activityNdx);
+ for (int activityNdx = tr.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ ActivityRecord r = tr.getChildAt(activityNdx);
if (!r.finishing) {
r.finishIfPossible("finish-voice", false /* oomAdj */);
didOne = true;
@@ -3725,8 +3709,8 @@ public class ActivityStack extends ConfigurationContainer {
}
} else {
// Check if any of the activities are using voice
- for (int activityNdx = tr.mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = tr.mActivities.get(activityNdx);
+ for (int activityNdx = tr.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ ActivityRecord r = tr.getChildAt(activityNdx);
if (r.voiceSession != null && r.voiceSession.asBinder() == sessionBinder) {
// Inform of cancellation
r.clearVoiceSessionLocked();
@@ -3752,9 +3736,9 @@ public class ActivityStack extends ConfigurationContainer {
void finishAllActivitiesImmediately() {
boolean noActivitiesInStack = true;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
noActivitiesInStack = false;
Slog.d(TAG, "finishAllActivitiesImmediatelyLocked: finishing " + r);
r.destroyIfPossible("finishAllActivitiesImmediatelyLocked");
@@ -3822,12 +3806,12 @@ public class ActivityStack extends ConfigurationContainer {
return false;
}
int finishTo = start - 1;
- ActivityRecord parent = finishTo < 0 ? null : activities.get(finishTo);
+ ActivityRecord parent = finishTo < 0 ? null : task.getChildAt(finishTo);
boolean foundParentInTask = false;
final ComponentName dest = destIntent.getComponent();
if (start > 0 && dest != null) {
for (int i = finishTo; i >= 0; i--) {
- ActivityRecord r = activities.get(i);
+ ActivityRecord r = task.getChildAt(i);
if (r.info.packageName.equals(dest.getPackageName()) &&
r.info.name.equals(dest.getClassName())) {
finishTo = i;
@@ -3988,13 +3972,13 @@ public class ActivityStack extends ConfigurationContainer {
boolean lastIsOpaque = false;
boolean activityRemoved = false;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if (r.finishing) {
continue;
}
- if (r.fullscreen) {
+ if (r.occludesParent()) {
lastIsOpaque = true;
}
if (owner != null && r.app != owner) {
@@ -4035,15 +4019,14 @@ public class ActivityStack extends ConfigurationContainer {
}
if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Looking for activities to release in " + task);
int curNum = 0;
- final ArrayList<ActivityRecord> activities = task.mActivities;
- for (int actNdx = 0; actNdx < activities.size(); actNdx++) {
- final ActivityRecord activity = activities.get(actNdx);
+ for (int actNdx = 0; actNdx < task.getChildCount(); actNdx++) {
+ final ActivityRecord activity = task.getChildAt(actNdx);
if (activity.app == app && activity.isDestroyable()) {
if (DEBUG_RELEASE) Slog.v(TAG_RELEASE, "Destroying " + activity
+ " in state " + activity.getState() + " resumed=" + mResumedActivity
+ " pausing=" + mPausingActivity + " for reason " + reason);
activity.destroyImmediately(true /* removeFromApp */, reason);
- if (activities.get(actNdx) != activity) {
+ if (task.getChildAt(actNdx) != activity) {
// Was removed from list, back up so we don't miss the next one.
actNdx--;
}
@@ -4155,7 +4138,7 @@ public class ActivityStack extends ConfigurationContainer {
Slog.w(TAG, "Force removing " + r + ": app died, no saved state");
EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
r.mUserId, System.identityHashCode(r),
- r.getTaskRecord().taskId, r.shortComponentName,
+ r.getTaskRecord().mTaskId, r.shortComponentName,
"proc died without state saved");
}
} else {
@@ -4227,8 +4210,8 @@ public class ActivityStack extends ConfigurationContainer {
if (timeTracker != null) {
// The caller wants a time tracker associated with this task.
- for (int i = tr.mActivities.size() - 1; i >= 0; i--) {
- tr.mActivities.get(i).appTimeTracker = timeTracker;
+ for (int i = tr.getChildCount() - 1; i >= 0; i--) {
+ tr.getChildAt(i).appTimeTracker = timeTracker;
}
}
@@ -4280,7 +4263,7 @@ public class ActivityStack extends ConfigurationContainer {
}
mRootActivityContainer.resumeFocusedStacksTopActivities();
- EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId);
+ EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.mUserId, tr.mTaskId);
mService.getTaskChangeNotificationController().notifyTaskMovedToFront(tr.getTaskInfo());
} finally {
getDisplay().continueUpdateImeTarget();
@@ -4385,7 +4368,7 @@ public class ActivityStack extends ConfigurationContainer {
final ActivityRecord r = activities.get(activityIndex);
updatedConfig |= r.ensureActivityConfiguration(0 /* globalChanges */,
preserveWindow);
- if (r.fullscreen) {
+ if (r.occludesParent()) {
behindFullscreen = true;
break;
}
@@ -4409,7 +4392,7 @@ public class ActivityStack extends ConfigurationContainer {
return;
}
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "stack.resize_" + mStackId);
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "stack.resize_" + mStackId);
mService.deferWindowLayout();
try {
// Update override configurations of all tasks in the stack.
@@ -4435,7 +4418,7 @@ public class ActivityStack extends ConfigurationContainer {
}
} finally {
mService.continueWindowLayout();
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
@@ -4482,13 +4465,13 @@ public class ActivityStack extends ConfigurationContainer {
boolean willActivityBeVisibleLocked(IBinder token) {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if (r.appToken == token) {
return true;
}
- if (r.fullscreen && !r.finishing) {
+ if (r.occludesParent() && !r.finishing) {
return false;
}
}
@@ -4504,9 +4487,9 @@ public class ActivityStack extends ConfigurationContainer {
void closeSystemDialogsLocked() {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
r.finishIfPossible("close-sys", true /* oomAdj */);
}
@@ -4577,7 +4560,7 @@ public class ActivityStack extends ConfigurationContainer {
continue;
}
if (task.effectiveUid != callingUid) {
- if (task.userId != userId && !crossUser && !profileIds.contains(task.userId)) {
+ if (task.mUserId != userId && !crossUser && !profileIds.contains(task.mUserId)) {
// Skip if the caller does not have cross user permission or cannot access
// the task's profile
continue;
@@ -4602,7 +4585,7 @@ public class ActivityStack extends ConfigurationContainer {
// 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)
- task.lastActiveTime = SystemClock.elapsedRealtime();
+ task.touchActiveTime();
topTask = false;
}
tasksOut.add(task);
@@ -4613,10 +4596,10 @@ public class ActivityStack extends ConfigurationContainer {
final int top = mTaskHistory.size() - 1;
if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Performing unhandledBack(): top activity at " + top);
if (top >= 0) {
- final ArrayList<ActivityRecord> activities = mTaskHistory.get(top).mActivities;
- int activityTop = activities.size() - 1;
+ final TaskRecord task = mTaskHistory.get(top);
+ int activityTop = task.getChildCount() - 1;
if (activityTop >= 0) {
- activities.get(activityTop).finishIfPossible("unhandled-back", true /* oomAdj */);
+ task.getChildAt(activityTop).finishIfPossible("unhandled-back", true /* oomAdj */);
}
}
}
@@ -4642,9 +4625,9 @@ public class ActivityStack extends ConfigurationContainer {
void handleAppCrash(WindowProcessController app) {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if (r.app == app) {
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
@@ -4711,7 +4694,7 @@ public class ActivityStack extends ConfigurationContainer {
if (needSep) {
pw.println("");
}
- pw.println(prefix + "Task id #" + task.taskId);
+ pw.println(prefix + "Task id #" + task.mTaskId);
pw.println(prefix + "mBounds=" + task.getRequestedOverrideBounds());
pw.println(prefix + "mMinWidth=" + task.mMinWidth);
pw.println(prefix + "mMinHeight=" + task.mMinHeight);
@@ -4762,14 +4745,13 @@ public class ActivityStack extends ConfigurationContainer {
// All activities that came from the package must be
// restarted as if there was a config change.
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord a = activities.get(activityNdx);
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord a = task.getChildAt(activityNdx);
if (a.info.packageName.equals(packageName)) {
a.forceNewConfig = true;
if (starting != null && a == starting && a.visible) {
- a.startFreezingScreenLocked(starting.app,
- CONFIG_SCREEN_LAYOUT);
+ a.startFreezingScreenLocked(CONFIG_SCREEN_LAYOUT);
}
}
}
@@ -4790,7 +4772,7 @@ public class ActivityStack extends ConfigurationContainer {
final boolean removed = mTaskHistory.remove(task);
if (removed) {
- EventLog.writeEvent(EventLogTags.AM_REMOVE_TASK, task.taskId, getStackId());
+ EventLog.writeEvent(EventLogTags.AM_REMOVE_TASK, task.mTaskId, getStackId());
}
removeActivitiesFromLRUList(task);
@@ -4942,7 +4924,7 @@ public class ActivityStack extends ConfigurationContainer {
mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, prevStack);
} else if (task.voiceSession != null) {
try {
- task.voiceSession.taskStarted(task.intent, task.taskId);
+ task.voiceSession.taskStarted(task.intent, task.mTaskId);
} catch (RemoteException e) {
}
}
@@ -5000,7 +4982,7 @@ public class ActivityStack extends ConfigurationContainer {
if (top != null && !top.isConfigurationCompatible(parentConfig)) {
// The final orientation of this activity will change after moving to full screen.
// Start freezing screen here to prevent showing a temporary full screen window.
- top.startFreezingScreenLocked(top.app, CONFIG_SCREEN_LAYOUT);
+ top.startFreezingScreenLocked(CONFIG_SCREEN_LAYOUT);
mService.moveTasksToFullscreenStack(mStackId, true /* onTop */);
return;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index ea3210be1209..06c17d3a8be4 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -44,7 +44,7 @@ import static android.graphics.Rect.copyOrNull;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.TYPE_VIRTUAL;
@@ -710,7 +710,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags,
int filterCallingUid) {
try {
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent");
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resolveIntent");
int modifiedFlags = flags
| PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS;
if (intent.isWebIntent()
@@ -731,7 +731,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
Binder.restoreCallingIdentity(token);
}
} finally {
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
@@ -841,10 +841,10 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
+ " with results=" + results + " newIntents=" + newIntents
+ " andResume=" + andResume);
EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY, r.mUserId,
- System.identityHashCode(r), task.taskId, r.shortComponentName);
+ System.identityHashCode(r), task.mTaskId, r.shortComponentName);
if (r.isActivityTypeHome()) {
// Home process is the root process of the task.
- updateHomeProcess(task.mActivities.get(0).app);
+ updateHomeProcess(task.getChildAt(0).app);
}
mService.getPackageManagerInternalLocked().notifyPackageUse(
r.intent.getComponent().getPackageName(), NOTIFY_PACKAGE_USE_ACTIVITY);
@@ -1193,7 +1193,8 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
return ACTIVITY_RESTRICTION_NONE;
}
- if (mService.getAppOpsService().noteOperation(opCode, callingUid, callingPackage)
+ // TODO moltmann b/136595429: Set featureId from caller
+ if (mService.getAppOpsService().noteOperation(opCode, callingUid, callingPackage, /* featureId */ null)
!= AppOpsManager.MODE_ALLOWED) {
if (!ignoreTargetSecurity) {
return ACTIVITY_RESTRICTION_APPOP;
@@ -1236,7 +1237,8 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
return ACTIVITY_RESTRICTION_NONE;
}
- if (mService.getAppOpsService().noteOperation(opCode, callingUid, callingPackage)
+ // TODO moltmann b/136595429: Set componentId from caller
+ if (mService.getAppOpsService().noteOperation(opCode, callingUid, callingPackage, /* featureId */ null)
!= AppOpsManager.MODE_ALLOWED) {
return ACTIVITY_RESTRICTION_APPOP;
}
@@ -1663,7 +1665,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
mPendingTempOtherTaskInsetBounds = copyOrNull(tempOtherTaskInsetBounds);
}
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeDockedStack");
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizeDockedStack");
mService.deferWindowLayout();
try {
// Don't allow re-entry while resizing. E.g. due to docked stack detaching.
@@ -1729,7 +1731,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
} finally {
mAllowDockedStackResize = true;
mService.continueWindowLayout();
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
@@ -1751,7 +1753,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
return;
}
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizePinnedStack");
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizePinnedStack");
mService.deferWindowLayout();
try {
Rect insetBounds = null;
@@ -1773,7 +1775,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
!DEFER_RESUME);
} finally {
mService.continueWindowLayout();
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
@@ -1799,7 +1801,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
moveTasksToFullscreenStackLocked(stack, !ON_TOP);
} else {
for (int i = tasks.size() - 1; i >= 0; i--) {
- removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */,
+ removeTaskByIdLocked(tasks.get(i).mTaskId, true /* killProcess */,
REMOVE_FROM_RECENTS, "remove-stack");
}
}
@@ -1830,6 +1832,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
tr.removeTaskActivitiesLocked(reason);
cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
mService.getLockTaskController().clearLockedTask(tr);
+ mService.getTaskChangeNotificationController().notifyTaskStackChanged();
if (tr.isPersistable) {
mService.notifyTaskPersisterLocked(null, true);
}
@@ -1851,7 +1854,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
// Find any running services associated with this app and stop if needed.
final Message msg = PooledLambda.obtainMessage(ActivityManagerInternal::cleanUpServices,
- mService.mAmInternal, tr.userId, component, new Intent(tr.getBaseIntent()));
+ mService.mAmInternal, tr.mUserId, component, new Intent(tr.getBaseIntent()));
mService.mH.sendMessage(msg);
if (!killProcess) {
@@ -1868,7 +1871,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
SparseArray<WindowProcessController> uids = pmap.valueAt(i);
for (int j = 0; j < uids.size(); j++) {
WindowProcessController proc = uids.valueAt(j);
- if (proc.mUserId != tr.userId) {
+ if (proc.mUserId != tr.mUserId) {
// Don't kill process for a different user.
continue;
}
@@ -1942,9 +1945,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
task.createTask(onTop, true /* showForAllUsers */);
if (DEBUG_RECENTS) Slog.v(TAG_RECENTS,
"Added restored task=" + task + " to stack=" + stack);
- final ArrayList<ActivityRecord> activities = task.mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- activities.get(activityNdx).createAppWindowToken();
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
+ r.setTask(task);
}
return true;
}
@@ -1959,7 +1962,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
if (wasTrimmed) {
// Task was trimmed from the recent tasks list -- remove the active task record as well
// since the user won't really be able to go back to it
- removeTaskByIdLocked(task.taskId, killProcess, false /* removeFromRecents */,
+ removeTaskByIdLocked(task.mTaskId, killProcess, false /* removeFromRecents */,
"recent-task-trimmed");
}
task.removedFromRecents();
@@ -2149,7 +2152,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
final TaskRecord task = r.getTaskRecord();
final ActivityStack stack = task.getStack();
- r.mLaunchTaskBehind = false;
mRecentTasks.add(task);
mService.getTaskChangeNotificationController().notifyTaskStackChanged();
stack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
@@ -2189,6 +2191,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
return false;
}
+ // TODO: Change method name to reflect what it actually does.
final ArrayList<ActivityRecord> processStoppingActivitiesLocked(ActivityRecord idleActivity,
boolean remove, boolean processPausingActivities) {
ArrayList<ActivityRecord> stops = null;
@@ -2197,7 +2200,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord s = mStoppingActivities.get(activityNdx);
- final boolean animating = s.mAppWindowToken.isSelfAnimating();
+ final boolean animating = s.isSelfAnimating();
if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible
+ " animating=" + animating + " finishing=" + s.finishing);
@@ -2318,7 +2321,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
pw.print(" #"); pw.print(i); pw.print(": ");
pw.println(r);
if (full) {
- r.dump(pw, innerPrefix);
+ r.dump(pw, innerPrefix, true /* dumpAll */);
} else if (complete) {
// Complete + brief == give a summary. Isn't that obvious?!?
pw.print(innerPrefix); pw.println(r.intent.toInsecureString());
@@ -2542,15 +2545,13 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
return;
}
mService.getTaskChangeNotificationController().notifyActivityForcedResizable(
- task.taskId, reason, topActivity.info.applicationInfo.packageName);
+ task.mTaskId, reason, topActivity.info.applicationInfo.packageName);
}
void activityRelaunchedLocked(IBinder token) {
final ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r != null) {
- if (r.mAppWindowToken != null) {
- r.mAppWindowToken.finishRelaunching();
- }
+ r.finishRelaunching();
if (r.getActivityStack().shouldSleepOrShutDownActivities()) {
r.setSleeping(true, true);
}
@@ -2568,8 +2569,8 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
return;
}
- for (int i = task.mActivities.size() - 1; i >= 0; i--) {
- final ActivityRecord r = task.mActivities.get(i);
+ for (int i = task.getChildCount() - 1; i >= 0; i--) {
+ final ActivityRecord r = task.getChildAt(i);
if (r.attachedToProcess()) {
mMultiWindowModeChangedActivities.add(r);
}
@@ -2591,8 +2592,8 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
}
void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds) {
- for (int i = task.mActivities.size() - 1; i >= 0; i--) {
- final ActivityRecord r = task.mActivities.get(i);
+ for (int i = task.getChildCount() - 1; i >= 0; i--) {
+ final ActivityRecord r = task.getChildAt(i);
if (r.attachedToProcess()) {
mPipModeChangedActivities.add(r);
// If we are scheduling pip change, then remove this activity from multi-window
@@ -2610,8 +2611,8 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
void updatePictureInPictureMode(TaskRecord task, Rect targetStackBounds, boolean forceUpdate) {
mHandler.removeMessages(REPORT_PIP_MODE_CHANGED_MSG);
- for (int i = task.mActivities.size() - 1; i >= 0; i--) {
- final ActivityRecord r = task.mActivities.get(i);
+ for (int i = task.getChildCount() - 1; i >= 0; i--) {
+ final ActivityRecord r = task.getChildAt(i);
if (r.attachedToProcess()) {
r.updatePictureInPictureMode(targetStackBounds, forceUpdate);
}
@@ -2766,7 +2767,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
* @param task The task to put into resizing mode
*/
void setResizingDuringAnimation(TaskRecord task) {
- mResizingTasksDuringAnimation.add(task.taskId);
+ mResizingTasksDuringAnimation.add(task.mTaskId);
task.setTaskDockedResizing(true);
}
@@ -2828,7 +2829,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
// If the user must confirm credentials (e.g. when first launching a work app and the
// Work Challenge is present) let startActivityInPackage handle the intercepting.
- if (!mService.mAmInternal.shouldConfirmCredentials(task.userId)
+ if (!mService.mAmInternal.shouldConfirmCredentials(task.mUserId)
&& task.getRootActivity() != null) {
final ActivityRecord targetActivity = task.getTopActivity();
@@ -2837,7 +2838,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
mActivityMetricsLogger.notifyActivityLaunching(task.intent);
try {
mService.moveTaskToFrontLocked(null /* appThread */, null /* callingPackage */,
- task.taskId, 0, options, true /* fromRecents */);
+ task.mTaskId, 0, options, true /* fromRecents */);
// Apply options to prevent pendingOptions be taken by client to make sure
// the override pending app transition will be applied immediately.
targetActivity.applyOptionsLocked();
@@ -2854,7 +2855,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
callingPackage = task.mCallingPackage;
intent = task.intent;
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
- userId = task.userId;
+ userId = task.mUserId;
return mService.getActivityStartController().startActivityInPackage(
task.mCallingUid, callingPid, callingUid, callingPackage, intent, null, null,
null, 0, 0, options, userId, task, "startActivityFromRecents",
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 919141c13622..63cec1ab4267 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -386,6 +386,8 @@ public class ActivityStartController {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
+ // Get the flag earlier because the intent may be modified in resolveActivity below.
+ final boolean componentSpecified = intent.getComponent() != null;
// Don't modify the client's object!
intent = new Intent(intent);
@@ -409,7 +411,6 @@ public class ActivityStartController {
.setCaller(caller)
.setResolvedType(resolvedTypes[i])
.setActivityInfo(aInfo)
- .setResultTo(resultTo)
.setRequestCode(-1)
.setCallingPid(callingPid)
.setCallingUid(callingUid)
@@ -417,7 +418,7 @@ public class ActivityStartController {
.setRealCallingPid(realCallingPid)
.setRealCallingUid(realCallingUid)
.setActivityOptions(checkedOptions)
- .setComponentSpecified(intent.getComponent() != null)
+ .setComponentSpecified(componentSpecified)
// Top activity decides on animation being run, so we allow only for the
// top one as otherwise an activity below might consume it.
@@ -430,7 +431,8 @@ public class ActivityStartController {
// Lock the loop to ensure the activities launched in a sequence.
synchronized (mService.mGlobalLock) {
for (int i = 0; i < starters.length; i++) {
- final int startResult = starters[i].setOutActivity(outActivity).execute();
+ final int startResult = starters[i].setResultTo(resultTo)
+ .setOutActivity(outActivity).execute();
if (startResult < START_SUCCESS) {
// Abort by error result and recycle unused starters.
for (int j = i + 1; j < starters.length; j++) {
@@ -504,7 +506,7 @@ public class ActivityStartController {
if (mLastHomeActivityStartRecord != null) {
pw.print(prefix);
pw.println("mLastHomeActivityStartRecord:");
- mLastHomeActivityStartRecord.dump(pw, prefix + " ");
+ mLastHomeActivityStartRecord.dump(pw, prefix + " ", true /* dumpAll */);
}
final boolean dumpPackagePresent = dumpPackage != null;
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 9d08e10c6dea..effd154a6aa0 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -249,7 +249,8 @@ class ActivityStartInterceptor {
if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
return interceptSuspendedByAdminPackage();
}
- final SuspendDialogInfo dialogInfo = pmi.getSuspendedDialogInfo(suspendedPackage, mUserId);
+ final SuspendDialogInfo dialogInfo = pmi.getSuspendedDialogInfo(suspendedPackage,
+ suspendingPackage, mUserId);
mIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(suspendedPackage,
suspendingPackage, dialogInfo, mUserId);
mCallingPid = mRealCallingPid;
@@ -273,7 +274,7 @@ class ActivityStartInterceptor {
// ConfirmCredentials intent and unassign it, as otherwise the task will move to
// front even if ConfirmCredentials is cancelled.
if (mInTask != null) {
- mIntent.putExtra(EXTRA_TASK_ID, mInTask.taskId);
+ mIntent.putExtra(EXTRA_TASK_ID, mInTask.mTaskId);
mInTask = null;
}
if (mActivityOptions == null) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 606162b4fccf..174d8b3251d2 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -774,13 +774,13 @@ class ActivityStarter {
boolean restrictedBgActivity = false;
if (!abort) {
try {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
"shouldAbortBackgroundActivityStart");
restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,
originatingPendingIntent, allowBackgroundActivityStart, intent);
} finally {
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
}
@@ -1405,41 +1405,12 @@ class ActivityStarter {
final ActivityStack startedActivityStack;
try {
mService.deferWindowLayout();
- result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
+ result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, doResume, options, inTask, outActivity, restrictedBgActivity);
} finally {
- final ActivityStack currentStack = r.getActivityStack();
- startedActivityStack = currentStack != null ? currentStack : mTargetStack;
-
- if (ActivityManager.isStartResultSuccessful(result)) {
- if (startedActivityStack != null) {
- // If there is no state change (e.g. a resumed activity is reparented to
- // top of another display) to trigger a visibility/configuration checking,
- // we have to update the configuration for changing to different display.
- final ActivityRecord currentTop =
- startedActivityStack.topRunningActivityLocked();
- if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) {
- mRootActivityContainer.ensureVisibilityAndConfig(
- currentTop, currentTop.getDisplayId(),
- true /* markFrozenIfConfigChanged */, false /* deferResume */);
- }
- }
- } else {
- // 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.getActivityStack();
- if (stack != null) {
- mStartActivity.finishIfPossible("startActivity", true /* oomAdj */);
- }
-
- // Stack should also be detached from display and be removed if it's empty.
- if (startedActivityStack != null && startedActivityStack.isAttached()
- && startedActivityStack.numActivities() == 0
- && !startedActivityStack.isActivityTypeHome()) {
- startedActivityStack.remove();
- }
- }
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ startedActivityStack = handleStartResult(r, result);
mService.continueWindowLayout();
}
@@ -1449,6 +1420,49 @@ class ActivityStarter {
}
/**
+ * If the start result is success, ensure that the configuration of the started activity matches
+ * the current display. Otherwise clean up unassociated containers to avoid leakage.
+ *
+ * @return the stack where the successful started activity resides.
+ */
+ private @Nullable ActivityStack handleStartResult(@NonNull ActivityRecord started, int result) {
+ final ActivityStack currentStack = started.getActivityStack();
+ ActivityStack startedActivityStack = currentStack != null ? currentStack : mTargetStack;
+
+ if (ActivityManager.isStartResultSuccessful(result)) {
+ if (startedActivityStack != null) {
+ // If there is no state change (e.g. a resumed activity is reparented to top of
+ // another display) to trigger a visibility/configuration checking, we have to
+ // update the configuration for changing to different display.
+ final ActivityRecord currentTop = startedActivityStack.topRunningActivityLocked();
+ if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) {
+ mRootActivityContainer.ensureVisibilityAndConfig(
+ currentTop, currentTop.getDisplayId(),
+ true /* markFrozenIfConfigChanged */, false /* deferResume */);
+ }
+ }
+ return startedActivityStack;
+ }
+
+ // 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.getActivityStack();
+ if (stack != null) {
+ mStartActivity.finishIfPossible("startActivity", true /* oomAdj */);
+ }
+
+ // Stack should also be detached from display and be removed if it's empty.
+ if (startedActivityStack != null && startedActivityStack.isAttached()
+ && startedActivityStack.numActivities() == 0
+ && !startedActivityStack.isActivityTypeHome()) {
+ startedActivityStack.remove();
+ startedActivityStack = null;
+ }
+ return startedActivityStack;
+ }
+
+ /**
* Return true if background activity is really aborted.
*
* TODO(b/131748165): Refactor the logic so we don't need to call this method everywhere.
@@ -1473,7 +1487,7 @@ class ActivityStarter {
}
// Note: This method should only be called from {@link startActivity}.
- private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
+ private int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
ActivityRecord[] outActivity, boolean restrictedBgActivity) {
@@ -1562,12 +1576,12 @@ class ActivityStarter {
mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.mUserId);
mService.getPackageManagerInternalLocked().grantImplicitAccess(
mStartActivity.mUserId, mIntent,
- UserHandle.getAppId(mCallingUid),
+ mCallingUid,
UserHandle.getAppId(mStartActivity.info.applicationInfo.uid)
);
if (newTask) {
EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.mUserId,
- mStartActivity.getTaskRecord().taskId);
+ mStartActivity.getTaskRecord().mTaskId);
}
mStartActivity.logStartActivity(
EventLogTags.AM_CREATE_ACTIVITY, mStartActivity.getTaskRecord());
@@ -1602,7 +1616,7 @@ class ActivityStarter {
// accordingly.
if (mTargetStack.isFocusable()
&& !mRootActivityContainer.isTopDisplayFocusedStack(mTargetStack)) {
- mTargetStack.moveToFront("startActivityUnchecked");
+ mTargetStack.moveToFront("startActivityInner");
}
mRootActivityContainer.resumeFocusedStacksTopActivities(
mTargetStack, mStartActivity, mOptions);
@@ -1694,14 +1708,16 @@ class ActivityStarter {
== (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
&& mLaunchMode == LAUNCH_MULTIPLE;
- // If mStartActivity does not have a task associated with it, associate it with the
- // reused activity's task. Do not do so if we're clearing top and resetting for a
- // standard launchMode activity.
- if (mStartActivity.getTaskRecord() == null && !clearTopAndResetStandardLaunchMode) {
- mStartActivity.setTask(targetTask);
- }
-
+ boolean clearTaskForReuse = false;
if (reusedActivity != null) {
+ // If mStartActivity does not have a task associated with it, associate it with the
+ // reused activity's task. Do not do so if we're clearing top and resetting for a
+ // standard launchMode activity.
+ if (mStartActivity.getTaskRecord() == null && !clearTopAndResetStandardLaunchMode) {
+ mStartActivity.setTaskForReuse(reusedActivity.getTaskRecord());
+ clearTaskForReuse = true;
+ }
+
if (targetTask.intent == null) {
// This task was started because of movement of the activity based on
// affinity...
@@ -1749,11 +1765,23 @@ class ActivityStarter {
complyActivityFlags(targetTask, reusedActivity);
+ if (clearTaskForReuse) {
+ // Clear task for re-use so later code to methods
+ // {@link #setTaskFromReuseOrCreateNewTask}, {@link #setTaskFromSourceRecord}, or
+ // {@link #setTaskFromInTask} can parent it to the task.
+ mStartActivity.setTaskForReuse(null);
+ }
+
if (mAddingToTask) {
return START_SUCCESS;
}
- if (!mMovedToFront && mDoResume) {
+ if (mMovedToFront) {
+ // We moved the task to front, use starting window to hide initial drawn delay.
+ targetTaskTop.showStartingWindow(null /* prev */, false /* newTask */,
+ true /* taskSwitch */);
+ } else if (mDoResume) {
+ // Make sure the stack and its belonging display are moved to topmost.
mTargetStack.moveToFront("intentActivityFound");
}
// We didn't do anything... but it was needed (a.k.a., client don't use that intent!)
@@ -1816,7 +1844,9 @@ class ActivityStarter {
*/
private void complyActivityFlags(TaskRecord targetTask, ActivityRecord reusedActivity) {
ActivityRecord targetTaskTop = targetTask.getTopActivity();
- if (reusedActivity != null && (mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
+ final boolean resetTask =
+ reusedActivity != null && (mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0;
+ if (resetTask) {
targetTaskTop = mTargetStack.resetTaskIfNeededLocked(targetTaskTop, mStartActivity);
}
@@ -1869,7 +1899,7 @@ class ActivityStarter {
mTargetStack = computeStackFocus(mSourceRecord, false /* newTask */,
mLaunchFlags, mOptions);
mTargetStack.addTask(targetTask,
- !mLaunchTaskBehind /* toTop */, "startActivityUnchecked");
+ !mLaunchTaskBehind /* toTop */, "complyActivityFlags");
}
}
} else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) == 0 && !mAddingToTask
@@ -1889,14 +1919,17 @@ class ActivityStarter {
mAddingToTask = true;
}
} else if (mStartActivity.mActivityComponent.equals(targetTask.realActivity)) {
- // In this case the top activity on the task is the same as the one being launched,
- // so we take that as a request to bring the task to the foreground. If the top
- // activity in the task is the root activity, deliver this new intent to it if it
- // desires.
- if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
- || LAUNCH_SINGLE_TOP == mLaunchMode)
- && targetTaskTop.mActivityComponent.equals(
- mStartActivity.mActivityComponent) && mStartActivity.resultTo == null) {
+ if (targetTask == mInTask) {
+ // In this case we are bringing up an existing activity from a recent task. We
+ // don't need to add a new activity instance on top.
+ } else if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
+ || LAUNCH_SINGLE_TOP == mLaunchMode)
+ && targetTaskTop.mActivityComponent.equals(mStartActivity.mActivityComponent)
+ && mStartActivity.resultTo == null) {
+ // In this case the top activity on the task is the same as the one being launched,
+ // so we take that as a request to bring the task to the foreground. If the top
+ // activity in the task is the root activity, deliver this new intent to it if it
+ // desires.
if (targetTaskTop.isRootOfTask()) {
targetTaskTop.getTaskRecord().setIntent(mStartActivity);
}
@@ -1908,7 +1941,7 @@ class ActivityStarter {
} else if (reusedActivity == null) {
mAddingToTask = true;
}
- } else if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
+ } else if (!resetTask) {
// In this case an activity is being launched in to an existing task, without
// resetting that task. This is typically the situation of launching an activity
// from a notification or shortcut. We want to place the new activity on top of the
@@ -2306,9 +2339,9 @@ class ActivityStarter {
intentActivity.setTaskToAffiliateWith(mSourceRecord.getTaskRecord());
}
- final ActivityStack launchStack = getLaunchStack(
- mStartActivity, mLaunchFlags, mStartActivity.getTaskRecord(), mOptions);
final TaskRecord intentTask = intentActivity.getTaskRecord();
+ final ActivityStack launchStack =
+ getLaunchStack(mStartActivity, mLaunchFlags, intentTask, mOptions);
if (launchStack == null || launchStack == mTargetStack) {
// We only want to move to the front, if we aren't going to launch on a
// different stack. If we launch on a different stack, we will put the
@@ -2352,18 +2385,16 @@ class ActivityStarter {
REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
"reparentingHome");
mMovedToFront = true;
- } else if (launchStack.topTask() == null) {
+ }
+
+ if (launchStack.topTask() == null) {
// The task does not need to be reparented to the launch stack. Remove the
// launch stack if there is no activity in it.
+ Slog.w(TAG, "Removing an empty stack: " + launchStack);
launchStack.remove();
}
mOptions = null;
-
- // We are moving a task to the front, use starting window to hide initial drawn
- // delay.
- intentActivity.showStartingWindow(null /* prev */, false /* newTask */,
- true /* taskSwitch */);
}
}
// Need to update mTargetStack because if task was moved out of it, the original stack may
@@ -2437,7 +2468,7 @@ class ActivityStarter {
if (mStartActivity.getTaskRecord() == null || mStartActivity.getTaskRecord() == parent) {
parent.addActivityToTop(mStartActivity);
} else {
- mStartActivity.reparent(parent, parent.mActivities.size() /* top */, reason);
+ mStartActivity.reparent(parent, parent.getChildCount() /* top */, reason);
}
}
@@ -2834,12 +2865,12 @@ class ActivityStarter {
if (r != null) {
pw.print(prefix);
pw.println("mLastStartActivityRecord:");
- r.dump(pw, prefix + " ");
+ r.dump(pw, prefix + " ", true /* dumpAll */);
}
if (mStartActivity != null) {
pw.print(prefix);
pw.println("mStartActivity:");
- mStartActivity.dump(pw, prefix + " ");
+ mStartActivity.dump(pw, prefix + " ", true /* dumpAll */);
}
if (mIntent != null) {
pw.print(prefix);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index ab35652eb525..0488a3b7065b 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -159,10 +159,10 @@ public abstract class ActivityTaskManagerInternal {
*
* @param reasons A map from windowing mode to a reason integer why the transition was started,
* which must be one of the APP_TRANSITION_* values.
- * @param timestamp The time at which the app transition started in
- * {@link SystemClock#uptimeMillis()} timebase.
+ * @param timestampNs The time at which the app transition started in
+ * {@link SystemClock#elapsedRealtimeNs()} ()} timebase.
*/
- public abstract void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp);
+ public abstract void notifyAppTransitionStarting(SparseIntArray reasons, long timestampNs);
/**
* Callback for window manager to let activity manager know that the app transition was
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index c4b7de674fd2..946e42cbf332 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -52,7 +52,7 @@ import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL;
import static android.os.FactoryTest.FACTORY_TEST_OFF;
import static android.os.Process.FIRST_APPLICATION_UID;
import static android.os.Process.SYSTEM_UID;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
@@ -63,7 +63,6 @@ import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
import static com.android.server.am.ActivityManagerService.ANR_TRACE_DIR;
import static com.android.server.am.ActivityManagerService.MY_PID;
@@ -807,7 +806,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
mUiHandler = new UiHandler();
mIntentFirewall = intentFirewall;
final File systemDir = SystemServiceManager.ensureSystemDir();
- mAppWarnings = new AppWarnings(this, mUiContext, mH, mUiHandler, systemDir);
+ mAppWarnings = createAppWarnings(mUiContext, mH, mUiHandler, systemDir);
mCompatModePackages = new CompatModePackages(this, systemDir, mH);
mPendingIntentController = intentController;
@@ -845,6 +844,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return supervisor;
}
+ protected AppWarnings createAppWarnings(
+ Context uiContext, Handler handler, Handler uiHandler, File systemDir) {
+ return new AppWarnings(this, uiContext, handler, uiHandler, systemDir);
+ }
+
public void setWindowManager(WindowManagerService wm) {
synchronized (mGlobalLock) {
mWindowManager = wm;
@@ -882,7 +886,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
boolean hasSystemAlertWindowPermission(int callingUid, int callingPid, String callingPackage) {
final int mode = getAppOpsService().noteOperation(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
- callingUid, callingPackage);
+ callingUid, callingPackage, /* featureId */ null);
if (mode == AppOpsManager.MODE_DEFAULT) {
return checkPermission(Manifest.permission.SYSTEM_ALERT_WINDOW, callingPid, callingUid)
== PERMISSION_GRANTED;
@@ -1602,6 +1606,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
final long origId = Binder.clearCallingIdentity();
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "finishActivity");
try {
boolean res;
final boolean finishWithRootActivity =
@@ -1613,7 +1618,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
// because we don't support returning them across task boundaries. Also, to
// keep backwards compatibility we remove the task from recents when finishing
// task with root activity.
- res = mStackSupervisor.removeTaskByIdLocked(tr.taskId, false /* killProcess */,
+ res = mStackSupervisor.removeTaskByIdLocked(tr.mTaskId, false /* killProcess */,
finishWithRootActivity, "finish-activity");
if (!res) {
Slog.i(TAG, "Removing task failed to finish activity");
@@ -1629,6 +1634,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
return res;
} finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
Binder.restoreCallingIdentity(origId);
}
}
@@ -1663,6 +1669,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
try {
WindowProcessController proc = null;
synchronized (mGlobalLock) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityIdle");
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack == null) {
return;
@@ -1677,6 +1684,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
} finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
Binder.restoreCallingIdentity(origId);
}
}
@@ -1703,10 +1711,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
public final void activityPaused(IBinder token) {
final long origId = Binder.clearCallingIdentity();
synchronized (mGlobalLock) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityPaused");
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
stack.activityPausedLocked(token, false);
}
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
Binder.restoreCallingIdentity(origId);
}
@@ -1727,6 +1737,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
int restartingUid = 0;
final ActivityRecord r;
synchronized (mGlobalLock) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityStopped");
r = ActivityRecord.isInStackLocked(token);
if (r != null) {
if (r.attachedToProcess()
@@ -1738,6 +1749,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
r.activityStoppedLocked(icicle, persistentState, description);
}
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
if (restartingName != null) {
@@ -1759,12 +1771,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "ACTIVITY DESTROYED: " + token);
synchronized (mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityDestroyed");
try {
final ActivityRecord activity = ActivityRecord.forTokenLocked(token);
if (activity != null) {
activity.destroyed("activityDestroyed");
}
} finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
Binder.restoreCallingIdentity(origId);
}
}
@@ -1813,11 +1827,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public int getRequestedOrientation(IBinder token) {
synchronized (mGlobalLock) {
- ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
- }
- return r.getOrientation();
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ return (r != null)
+ ? r.getRequestedOrientation() : ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
}
}
@@ -1979,7 +1991,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final TaskRecord task = r.getTaskRecord();
int index = task.mActivities.lastIndexOf(r);
if (index > 0) {
- ActivityRecord under = task.mActivities.get(index - 1);
+ ActivityRecord under = task.getChildAt(index - 1);
under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null;
}
return r.setOccludesParent(false);
@@ -2230,7 +2242,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final TaskRecord tr = mRootActivityContainer.anyTaskForId(id,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (tr != null) {
- return tr.lastTaskDescription;
+ return tr.mTaskDescription;
}
}
return null;
@@ -3029,7 +3041,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
if (structure != null) {
// Pre-fill the task/activity component for all assist data receivers
- structure.setTaskId(pae.activity.getTaskRecord().taskId);
+ structure.setTaskId(pae.activity.getTaskRecord().mTaskId);
structure.setActivityComponent(pae.activity.mActivityComponent);
structure.setHomeActivity(pae.isHome);
}
@@ -3056,7 +3068,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
// Caller wants result sent back to them.
sendBundle = new Bundle();
sendBundle.putInt(ActivityTaskManagerInternal.ASSIST_TASK_ID,
- pae.activity.getTaskRecord().taskId);
+ pae.activity.getTaskRecord().mTaskId);
sendBundle.putBinder(ActivityTaskManagerInternal.ASSIST_ACTIVITY_ID,
pae.activity.assistToken);
sendBundle.putBundle(ASSIST_KEY_DATA, pae.extras);
@@ -3152,11 +3164,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
stack.removeTask(task, "addAppTask", REMOVE_TASK_MODE_DESTROYING);
return INVALID_TASK_ID;
}
- task.lastTaskDescription.copyFrom(description);
+ task.mTaskDescription.copyFrom(description);
// TODO: Send the thumbnail to WM to store it.
- return task.taskId;
+ return task.mTaskId;
}
} finally {
Binder.restoreCallingIdentity(callingIdent);
@@ -3318,29 +3330,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public void startInPlaceAnimationOnFrontMostApplication(Bundle opts) {
- final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(opts);
- final ActivityOptions activityOptions = safeOptions != null
- ? safeOptions.getOptions(mStackSupervisor)
- : null;
- if (activityOptions == null
- || activityOptions.getAnimationType() != ActivityOptions.ANIM_CUSTOM_IN_PLACE
- || activityOptions.getCustomInPlaceResId() == 0) {
- throw new IllegalArgumentException("Expected in-place ActivityOption " +
- "with valid animation");
- }
- // Get top display of front most application.
- final ActivityStack focusedStack = getTopDisplayFocusedStack();
- if (focusedStack != null) {
- final DisplayContent dc = focusedStack.getDisplay().mDisplayContent;
- dc.prepareAppTransition(TRANSIT_TASK_IN_PLACE, false);
- dc.mAppTransition.overrideInPlaceAppTransition(activityOptions.getPackageName(),
- activityOptions.getCustomInPlaceResId());
- dc.executeAppTransition();
- }
- }
-
- @Override
public void removeStack(int stackId) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "removeStack()");
synchronized (mGlobalLock) {
@@ -4964,8 +4953,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
if (lastTask != task) {
lastTask = task;
pw.print("TASK "); pw.print(lastTask.affinity);
- pw.print(" id="); pw.print(lastTask.taskId);
- pw.print(" userId="); pw.println(lastTask.userId);
+ pw.print(" id="); pw.print(lastTask.mTaskId);
+ pw.print(" userId="); pw.println(lastTask.mUserId);
if (dumpAll) {
lastTask.dump(pw, " ");
}
@@ -4995,7 +4984,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
pw.println("(not running)");
}
if (dumpAll) {
- r.dump(pw, innerPrefix);
+ r.dump(pw, innerPrefix, true /* dumpAll */);
}
}
if (appThread != null) {
@@ -5527,8 +5516,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
void startProcessAsync(ActivityRecord activity, boolean knownToBeDead, boolean isTop,
String hostingType) {
try {
- if (Trace.isTagEnabled(TRACE_TAG_ACTIVITY_MANAGER)) {
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dispatchingStartProcess:"
+ if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "dispatchingStartProcess:"
+ activity.processName);
}
// Post message to start process to avoid possible deadlock of calling into AMS with the
@@ -5538,7 +5527,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
isTop, hostingType, activity.intent.getComponent());
mH.sendMessage(m);
} finally {
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
@@ -5691,11 +5680,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
private void updateResumedAppTrace(@Nullable ActivityRecord resumed) {
if (mTracedResumedActivity != null) {
- Trace.asyncTraceEnd(TRACE_TAG_ACTIVITY_MANAGER,
+ Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER,
constructResumedTraceName(mTracedResumedActivity.packageName), 0);
}
if (resumed != null) {
- Trace.asyncTraceBegin(TRACE_TAG_ACTIVITY_MANAGER,
+ Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER,
constructResumedTraceName(resumed.packageName), 0);
}
mTracedResumedActivity = resumed;
@@ -6023,10 +6012,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public void notifyAppTransitionStarting(SparseIntArray reasons,
- long timestamp) {
+ long timestampNs) {
synchronized (mGlobalLock) {
mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
- reasons, timestamp);
+ reasons, timestampNs);
}
}
@@ -6794,7 +6783,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public boolean attachApplication(WindowProcessController wpc) throws RemoteException {
synchronized (mGlobalLockWithoutBoost) {
- return mRootActivityContainer.attachApplication(wpc);
+ if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "attachApplication:" + wpc.mName);
+ }
+ try {
+ return mRootActivityContainer.attachApplication(wpc);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/AnimatingActivityRegistry.java b/services/core/java/com/android/server/wm/AnimatingActivityRegistry.java
new file mode 100644
index 000000000000..18ec96c38264
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AnimatingActivityRegistry.java
@@ -0,0 +1,120 @@
+/*
+ * 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;
+
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Keeps track of all {@link ActivityRecord} that are animating and makes sure all animations are
+ * finished at the same time such that we don't run into issues with z-ordering: An activity A
+ * that has a shorter animation that is above another activity B with a longer animation in the same
+ * task, the animation layer would put the B on top of A, but from the hierarchy, A needs to be on
+ * top of B. Thus, we defer reparenting A to the original hierarchy such that it stays on top of B
+ * until B finishes animating.
+ */
+class AnimatingActivityRegistry {
+
+ private ArraySet<ActivityRecord> mAnimatingActivities = new ArraySet<>();
+ private ArrayMap<ActivityRecord, Runnable> mFinishedTokens = new ArrayMap<>();
+
+ private ArrayList<Runnable> mTmpRunnableList = new ArrayList<>();
+
+ private boolean mEndingDeferredFinish;
+
+ /**
+ * Notifies that an {@link ActivityRecord} has started animating.
+ */
+ void notifyStarting(ActivityRecord token) {
+ mAnimatingActivities.add(token);
+ }
+
+ /**
+ * Notifies that an {@link ActivityRecord} has finished animating.
+ */
+ void notifyFinished(ActivityRecord activity) {
+ mAnimatingActivities.remove(activity);
+ mFinishedTokens.remove(activity);
+
+ // If we were the last activity, make sure the end all deferred finishes.
+ if (mAnimatingActivities.isEmpty()) {
+ endDeferringFinished();
+ }
+ }
+
+ /**
+ * Called when an {@link ActivityRecord} is about to finish animating.
+ *
+ * @param endDeferFinishCallback Callback to run when defer finish should be ended.
+ * @return {@code true} if finishing the animation should be deferred, {@code false} otherwise.
+ */
+ boolean notifyAboutToFinish(ActivityRecord activity, Runnable endDeferFinishCallback) {
+ final boolean removed = mAnimatingActivities.remove(activity);
+ if (!removed) {
+ return false;
+ }
+
+ if (mAnimatingActivities.isEmpty()) {
+
+ // If no animations are animating anymore, finish all others.
+ endDeferringFinished();
+ return false;
+ } else {
+
+ // Otherwise let's put it into the pending list of to be finished animations.
+ mFinishedTokens.put(activity, endDeferFinishCallback);
+ return true;
+ }
+ }
+
+ private void endDeferringFinished() {
+
+ // Don't start recursing. Running the finished listener invokes notifyFinished, which may
+ // invoked us again.
+ if (mEndingDeferredFinish) {
+ return;
+ }
+ try {
+ mEndingDeferredFinish = true;
+
+ // Copy it into a separate temp list to avoid modifying the collection while iterating
+ // as calling the callback may call back into notifyFinished.
+ for (int i = mFinishedTokens.size() - 1; i >= 0; i--) {
+ mTmpRunnableList.add(mFinishedTokens.valueAt(i));
+ }
+ mFinishedTokens.clear();
+ for (int i = mTmpRunnableList.size() - 1; i >= 0; i--) {
+ mTmpRunnableList.get(i).run();
+ }
+ mTmpRunnableList.clear();
+ } finally {
+ mEndingDeferredFinish = false;
+ }
+ }
+
+ void dump(PrintWriter pw, String header, String prefix) {
+ if (!mAnimatingActivities.isEmpty() || !mFinishedTokens.isEmpty()) {
+ pw.print(prefix); pw.println(header);
+ prefix = prefix + " ";
+ pw.print(prefix); pw.print("mAnimatingActivities="); pw.println(mAnimatingActivities);
+ pw.print(prefix); pw.print("mFinishedTokens="); pw.println(mFinishedTokens);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/AnimatingAppWindowTokenRegistry.java b/services/core/java/com/android/server/wm/AnimatingAppWindowTokenRegistry.java
deleted file mode 100644
index 9c00d1abafea..000000000000
--- a/services/core/java/com/android/server/wm/AnimatingAppWindowTokenRegistry.java
+++ /dev/null
@@ -1,120 +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;
-
-import android.util.ArrayMap;
-import android.util.ArraySet;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
-/**
- * Keeps track of all {@link AppWindowToken} that are animating and makes sure all animations are
- * finished at the same time such that we don't run into issues with z-ordering: An activity A
- * that has a shorter animation that is above another activity B with a longer animation in the same
- * task, the animation layer would put the B on top of A, but from the hierarchy, A needs to be on
- * top of B. Thus, we defer reparenting A to the original hierarchy such that it stays on top of B
- * until B finishes animating.
- */
-class AnimatingAppWindowTokenRegistry {
-
- private ArraySet<AppWindowToken> mAnimatingTokens = new ArraySet<>();
- private ArrayMap<AppWindowToken, Runnable> mFinishedTokens = new ArrayMap<>();
-
- private ArrayList<Runnable> mTmpRunnableList = new ArrayList<>();
-
- private boolean mEndingDeferredFinish;
-
- /**
- * Notifies that an {@link AppWindowToken} has started animating.
- */
- void notifyStarting(AppWindowToken token) {
- mAnimatingTokens.add(token);
- }
-
- /**
- * Notifies that an {@link AppWindowToken} has finished animating.
- */
- void notifyFinished(AppWindowToken token) {
- mAnimatingTokens.remove(token);
- mFinishedTokens.remove(token);
-
- // If we were the last token, make sure the end all deferred finishes.
- if (mAnimatingTokens.isEmpty()) {
- endDeferringFinished();
- }
- }
-
- /**
- * Called when an {@link AppWindowToken} is about to finish animating.
- *
- * @param endDeferFinishCallback Callback to run when defer finish should be ended.
- * @return {@code true} if finishing the animation should be deferred, {@code false} otherwise.
- */
- boolean notifyAboutToFinish(AppWindowToken token, Runnable endDeferFinishCallback) {
- final boolean removed = mAnimatingTokens.remove(token);
- if (!removed) {
- return false;
- }
-
- if (mAnimatingTokens.isEmpty()) {
-
- // If no animations are animating anymore, finish all others.
- endDeferringFinished();
- return false;
- } else {
-
- // Otherwise let's put it into the pending list of to be finished animations.
- mFinishedTokens.put(token, endDeferFinishCallback);
- return true;
- }
- }
-
- private void endDeferringFinished() {
-
- // Don't start recursing. Running the finished listener invokes notifyFinished, which may
- // invoked us again.
- if (mEndingDeferredFinish) {
- return;
- }
- try {
- mEndingDeferredFinish = true;
-
- // Copy it into a separate temp list to avoid modifying the collection while iterating
- // as calling the callback may call back into notifyFinished.
- for (int i = mFinishedTokens.size() - 1; i >= 0; i--) {
- mTmpRunnableList.add(mFinishedTokens.valueAt(i));
- }
- mFinishedTokens.clear();
- for (int i = mTmpRunnableList.size() - 1; i >= 0; i--) {
- mTmpRunnableList.get(i).run();
- }
- mTmpRunnableList.clear();
- } finally {
- mEndingDeferredFinish = false;
- }
- }
-
- void dump(PrintWriter pw, String header, String prefix) {
- if (!mAnimatingTokens.isEmpty() || !mFinishedTokens.isEmpty()) {
- pw.print(prefix); pw.println(header);
- prefix = prefix + " ";
- pw.print(prefix); pw.print("mAnimatingTokens="); pw.println(mAnimatingTokens);
- pw.print(prefix); pw.print("mFinishedTokens="); pw.println(mFinishedTokens);
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 66d52cc9bf5a..c1143c856bb4 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -33,7 +33,6 @@ 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_IN_PLACE;
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;
@@ -70,8 +69,9 @@ import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnte
import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
import static com.android.server.wm.AppTransitionProto.APP_TRANSITION_STATE;
import static com.android.server.wm.AppTransitionProto.LAST_USED_APP_TRANSITION;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
@@ -131,6 +131,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils.Dump;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.AttributeCache;
+import com.android.server.protolog.common.ProtoLog;
import com.android.server.wm.animation.ClipRectLRAnimation;
import com.android.server.wm.animation.ClipRectTBAnimation;
import com.android.server.wm.animation.CurvedTranslateAnimation;
@@ -337,8 +338,8 @@ public class AppTransition implements Dump {
updateBooster();
}
- void setLastAppTransition(int transit, AppWindowToken openingApp, AppWindowToken closingApp,
- AppWindowToken changingApp) {
+ void setLastAppTransition(int transit, ActivityRecord openingApp, ActivityRecord closingApp,
+ ActivityRecord changingApp) {
mLastUsedAppTransition = transit;
mLastOpeningApp = "" + openingApp;
mLastClosingApp = "" + closingApp;
@@ -429,7 +430,7 @@ public class AppTransition implements Dump {
* @return bit-map of WindowManagerPolicy#FINISH_LAYOUT_REDO_* to indicate whether another
* layout pass needs to be done
*/
- int goodToGo(int transit, AppWindowToken topOpeningApp, ArraySet<AppWindowToken> openingApps) {
+ int goodToGo(int transit, ActivityRecord topOpeningApp, ArraySet<ActivityRecord> openingApps) {
mNextAppTransition = TRANSIT_UNSET;
mNextAppTransitionFlags = 0;
setAppTransitionState(APP_STATE_RUNNING);
@@ -1630,70 +1631,61 @@ public class AppTransition implements Dump {
a = loadAnimationRes(lp, enter
? com.android.internal.R.anim.voice_activity_open_enter
: com.android.internal.R.anim.voice_activity_open_exit);
- if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
- "applyAnimation voice:"
- + " anim=" + a + " transit=" + appTransitionToString(transit)
- + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
+ "applyAnimation voice: anim=%s transit=%s isEntrance=%b Callers=%s", a,
+ appTransitionToString(transit), enter, Debug.getCallers(3));
} else if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_CLOSE
|| transit == TRANSIT_TASK_CLOSE
|| transit == TRANSIT_TASK_TO_BACK)) {
a = loadAnimationRes(lp, enter
? com.android.internal.R.anim.voice_activity_close_enter
: com.android.internal.R.anim.voice_activity_close_exit);
- if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
- "applyAnimation voice:"
- + " anim=" + a + " transit=" + appTransitionToString(transit)
- + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
+ "applyAnimation voice: anim=%s transit=%s isEntrance=%b Callers=%s", a,
+ appTransitionToString(transit), enter, Debug.getCallers(3));
} else if (transit == TRANSIT_ACTIVITY_RELAUNCH) {
a = createRelaunchAnimation(frame, insets);
- if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
- "applyAnimation:"
- + " anim=" + a + " nextAppTransition=" + mNextAppTransition
- + " transit=" + appTransitionToString(transit)
- + " Callers=" + Debug.getCallers(3));
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
+ "applyAnimation: anim=%s nextAppTransition=%d transit=%s Callers=%s", a,
+ mNextAppTransition, appTransitionToString(transit),
+ Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
a = loadAnimationRes(mNextAppTransitionPackage, enter ?
mNextAppTransitionEnter : mNextAppTransitionExit);
- if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
- "applyAnimation:"
- + " anim=" + a + " nextAppTransition=ANIM_CUSTOM"
- + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
- + " Callers=" + Debug.getCallers(3));
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
+ "applyAnimation: anim=%s nextAppTransition=ANIM_CUSTOM transit=%s "
+ + "isEntrance=%b Callers=%s",
+ a, appTransitionToString(transit), enter, Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE) {
a = loadAnimationRes(mNextAppTransitionPackage, mNextAppTransitionInPlace);
- if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
- "applyAnimation:"
- + " anim=" + a + " nextAppTransition=ANIM_CUSTOM_IN_PLACE"
- + " transit=" + appTransitionToString(transit)
- + " Callers=" + Debug.getCallers(3));
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
+ "applyAnimation: anim=%s nextAppTransition=ANIM_CUSTOM_IN_PLACE "
+ + "transit=%s Callers=%s",
+ a, appTransitionToString(transit), Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) {
a = createClipRevealAnimationLocked(transit, enter, frame, displayFrame);
- if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
- "applyAnimation:"
- + " anim=" + a + " nextAppTransition=ANIM_CLIP_REVEAL"
- + " transit=" + appTransitionToString(transit)
- + " Callers=" + Debug.getCallers(3));
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
+ "applyAnimation: anim=%s nextAppTransition=ANIM_CLIP_REVEAL "
+ + "transit=%s Callers=%s",
+ a, appTransitionToString(transit), Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) {
a = createScaleUpAnimationLocked(transit, enter, frame);
- if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
- "applyAnimation:"
- + " anim=" + a + " nextAppTransition=ANIM_SCALE_UP"
- + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
- + " Callers=" + Debug.getCallers(3));
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
+ "applyAnimation: anim=%s nextAppTransition=ANIM_SCALE_UP transit=%s "
+ + "isEntrance=%s Callers=%s",
+ a, appTransitionToString(transit), enter, Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP ||
mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) {
mNextAppTransitionScaleUp =
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter),
frame, transit, taskId);
- if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
- String animName = mNextAppTransitionScaleUp ?
- "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
- Slog.v(TAG, "applyAnimation:"
- + " anim=" + a + " nextAppTransition=" + animName
- + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
- + " Callers=" + Debug.getCallers(3));
- }
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
+ "applyAnimation: anim=%s nextAppTransition=%s transit=%s isEntrance=%b "
+ + "Callers=%s",
+ a, mNextAppTransitionScaleUp
+ ? "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN",
+ appTransitionToString(transit), enter, Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP ||
mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN) {
mNextAppTransitionScaleUp =
@@ -1701,30 +1693,27 @@ public class AppTransition implements Dump {
a = createAspectScaledThumbnailEnterExitAnimationLocked(
getThumbnailTransitionState(enter), uiMode, orientation, transit, frame,
insets, surfaceInsets, stableInsets, freeform, taskId);
- if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
- String animName = mNextAppTransitionScaleUp ?
- "ANIM_THUMBNAIL_ASPECT_SCALE_UP" : "ANIM_THUMBNAIL_ASPECT_SCALE_DOWN";
- Slog.v(TAG, "applyAnimation:"
- + " anim=" + a + " nextAppTransition=" + animName
- + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
- + " Callers=" + Debug.getCallers(3));
- }
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
+ "applyAnimation: anim=%s nextAppTransition=%s transit=%s isEntrance=%b "
+ + "Callers=%s",
+ a, mNextAppTransitionScaleUp
+ ? "ANIM_THUMBNAIL_ASPECT_SCALE_UP"
+ : "ANIM_THUMBNAIL_ASPECT_SCALE_DOWN",
+ appTransitionToString(transit), enter, Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS && enter) {
a = loadAnimationRes("android",
com.android.internal.R.anim.task_open_enter_cross_profile_apps);
- Slog.v(TAG,
- "applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS:"
- + " anim=" + a + " transit=" + appTransitionToString(transit)
- + " isEntrance=true" + " Callers=" + Debug.getCallers(3));
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
+ "applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS: "
+ + "anim=%s transit=%s isEntrance=true Callers=%s",
+ a, appTransitionToString(transit), Debug.getCallers(3));
} else if (transit == TRANSIT_TASK_CHANGE_WINDOWING_MODE) {
// In the absence of a specific adapter, we just want to keep everything stationary.
a = new AlphaAnimation(1.f, 1.f);
a.setDuration(WindowChangeAnimationSpec.ANIMATION_DURATION);
- if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
- Slog.v(TAG, "applyAnimation:"
- + " anim=" + a + " transit=" + appTransitionToString(transit)
- + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
- }
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
+ "applyAnimation: anim=%s transit=%s isEntrance=%b Callers=%s",
+ a, appTransitionToString(transit), enter, Debug.getCallers(3));
} else {
int animAttr = 0;
switch (transit) {
@@ -1787,12 +1776,11 @@ public class AppTransition implements Dump {
: WindowAnimation_launchTaskBehindTargetAnimation;
}
a = animAttr != 0 ? loadAnimationAttr(lp, animAttr, transit) : null;
- if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
- "applyAnimation:"
- + " anim=" + a
- + " animAttr=0x" + Integer.toHexString(animAttr)
- + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
- + " Callers=" + Debug.getCallers(3));
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
+ "applyAnimation: anim=%s animAttr=0x%x transit=%s isEntrance=%b "
+ + "Callers=%s",
+ a, animAttr, appTransitionToString(transit), enter,
+ Debug.getCallers(3));
}
return a;
}
@@ -1941,8 +1929,8 @@ public class AppTransition implements Dump {
}
void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter) {
- if (DEBUG_APP_TRANSITIONS) Slog.i(TAG, "Override pending remote transitionSet="
- + isTransitionSet() + " adapter=" + remoteAnimationAdapter);
+ ProtoLog.i(WM_DEBUG_APP_TRANSITIONS, "Override pending remote transitionSet=%b adapter=%s",
+ isTransitionSet(), remoteAnimationAdapter);
if (isTransitionSet()) {
clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE;
@@ -2214,12 +2202,11 @@ public class AppTransition implements Dump {
*/
boolean prepareAppTransitionLocked(@TransitionType int transit, boolean alwaysKeepCurrent,
@TransitionFlags int flags, boolean forceOverride) {
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Prepare app transition:"
- + " transit=" + appTransitionToString(transit)
- + " " + this
- + " alwaysKeepCurrent=" + alwaysKeepCurrent
- + " displayId=" + mDisplayContent.getDisplayId()
- + " Callers=" + Debug.getCallers(5));
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+ "Prepare app transition: transit=%s %s alwaysKeepCurrent=%b displayId=%d "
+ + "Callers=%s",
+ appTransitionToString(transit), this, alwaysKeepCurrent,
+ mDisplayContent.getDisplayId(), Debug.getCallers(5));
final boolean allowSetCrashing = !isKeyguardTransit(mNextAppTransition)
&& transit == TRANSIT_CRASHING_ACTIVITY_CLOSE;
if (forceOverride || isKeyguardTransit(transit) || !isTransitionSet()
@@ -2269,8 +2256,7 @@ public class AppTransition implements Dump {
static boolean isTaskTransit(int transit) {
return isTaskOpenTransit(transit)
|| transit == TRANSIT_TASK_CLOSE
- || transit == TRANSIT_TASK_TO_BACK
- || transit == TRANSIT_TASK_IN_PLACE;
+ || transit == TRANSIT_TASK_TO_BACK;
}
private static boolean isTaskOpenTransit(int transit) {
@@ -2305,15 +2291,14 @@ public class AppTransition implements Dump {
}
if (isTransitionSet() || !dc.mOpeningApps.isEmpty() || !dc.mClosingApps.isEmpty()
|| !dc.mChangingApps.isEmpty()) {
- if (DEBUG_APP_TRANSITIONS) {
- Slog.v(TAG_WM, "*** APP TRANSITION TIMEOUT."
- + " displayId=" + dc.getDisplayId()
- + " isTransitionSet()="
- + dc.mAppTransition.isTransitionSet()
- + " mOpeningApps.size()=" + dc.mOpeningApps.size()
- + " mClosingApps.size()=" + dc.mClosingApps.size()
- + " mChangingApps.size()=" + dc.mChangingApps.size());
- }
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+ "*** APP TRANSITION TIMEOUT. displayId=%d isTransitionSet()=%b "
+ + "mOpeningApps.size()=%d mClosingApps.size()=%d "
+ + "mChangingApps.size()=%d",
+ dc.getDisplayId(), dc.mAppTransition.isTransitionSet(),
+ dc.mOpeningApps.size(), dc.mClosingApps.size(),
+ dc.mChangingApps.size());
+
setTimeout();
mService.mWindowPlacerLocked.performSurfacePlacement();
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 6b2f9da77f62..3bda0c25a6b0 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -31,7 +31,6 @@ import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPE
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_IN_PLACE;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
@@ -48,7 +47,7 @@ import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_S
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -66,6 +65,7 @@ import android.view.WindowManager.LayoutParams;
import android.view.animation.Animation;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.protolog.common.ProtoLog;
import java.util.function.Predicate;
@@ -104,7 +104,7 @@ public class AppTransitionController {
}
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO");
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "**** GOOD TO GO");
final AppTransition appTransition = mDisplayContent.mAppTransition;
int transit = appTransition.getAppTransition();
if (mDisplayContent.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) {
@@ -156,20 +156,20 @@ public class AppTransitionController {
final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps,
mDisplayContent.mClosingApps, mDisplayContent.mChangingApps);
final boolean allowAnimations = mDisplayContent.getDisplayPolicy().allowAppAnimationsLw();
- final AppWindowToken animLpToken = allowAnimations
+ final ActivityRecord animLpActivity = allowAnimations
? findAnimLayoutParamsToken(transit, activityTypes)
: null;
- final AppWindowToken topOpeningApp = allowAnimations
+ final ActivityRecord topOpeningApp = allowAnimations
? getTopApp(mDisplayContent.mOpeningApps, false /* ignoreHidden */)
: null;
- final AppWindowToken topClosingApp = allowAnimations
+ final ActivityRecord topClosingApp = allowAnimations
? getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */)
: null;
- final AppWindowToken topChangingApp = allowAnimations
+ final ActivityRecord topChangingApp = allowAnimations
? getTopApp(mDisplayContent.mChangingApps, false /* ignoreHidden */)
: null;
- final WindowManager.LayoutParams animLp = getAnimLp(animLpToken);
- overrideWithRemoteAnimationIfSet(animLpToken, transit, activityTypes);
+ final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);
+ overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mOpeningApps)
|| containsVoiceInteraction(mDisplayContent.mOpeningApps)
@@ -178,8 +178,6 @@ public class AppTransitionController {
final int layoutRedo;
mService.mSurfaceAnimationRunner.deferStartingAnimations();
try {
- processApplicationsAnimatingInPlace(transit);
-
handleClosingApps(transit, animLp, voiceInteraction);
handleOpeningApps(transit, animLp, voiceInteraction);
handleChangingApps(transit, animLp, voiceInteraction);
@@ -211,7 +209,7 @@ public class AppTransitionController {
mDisplayContent.computeImeTarget(true /* updateImeTarget */);
mService.mAtmInternal.notifyAppTransitionStarting(mTempTransitionReasons.clone(),
- SystemClock.uptimeMillis());
+ SystemClock.elapsedRealtimeNanos());
if (transit == TRANSIT_SHOW_SINGLE_TASK_DISPLAY) {
mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
@@ -225,14 +223,14 @@ public class AppTransitionController {
layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG;
}
- private static WindowManager.LayoutParams getAnimLp(AppWindowToken wtoken) {
- final WindowState mainWindow = wtoken != null ? wtoken.findMainWindow() : null;
+ private static WindowManager.LayoutParams getAnimLp(ActivityRecord activity) {
+ final WindowState mainWindow = activity != null ? activity.findMainWindow() : null;
return mainWindow != null ? mainWindow.mAttrs : null;
}
- RemoteAnimationAdapter getRemoteAnimationOverride(AppWindowToken animLpToken, int transit,
+ RemoteAnimationAdapter getRemoteAnimationOverride(ActivityRecord animLpActivity, int transit,
ArraySet<Integer> activityTypes) {
- final RemoteAnimationDefinition definition = animLpToken.getRemoteAnimationDefinition();
+ final RemoteAnimationDefinition definition = animLpActivity.getRemoteAnimationDefinition();
if (definition != null) {
final RemoteAnimationAdapter adapter = definition.getAdapter(transit, activityTypes);
if (adapter != null) {
@@ -249,19 +247,19 @@ public class AppTransitionController {
* Overrides the pending transition with the remote animation defined for the transition in the
* set of defined remote animations in the app window token.
*/
- private void overrideWithRemoteAnimationIfSet(AppWindowToken animLpToken, int transit,
+ private void overrideWithRemoteAnimationIfSet(ActivityRecord animLpActivity, int transit,
ArraySet<Integer> activityTypes) {
if (transit == TRANSIT_CRASHING_ACTIVITY_CLOSE) {
// The crash transition has higher priority than any involved remote animations.
return;
}
- if (animLpToken == null) {
+ if (animLpActivity == null) {
return;
}
final RemoteAnimationAdapter adapter =
- getRemoteAnimationOverride(animLpToken, transit, activityTypes);
+ getRemoteAnimationOverride(animLpActivity, transit, activityTypes);
if (adapter != null) {
- animLpToken.getDisplayContent().mAppTransition.overridePendingAppTransitionRemote(
+ animLpActivity.getDisplayContent().mAppTransition.overridePendingAppTransitionRemote(
adapter);
}
}
@@ -269,12 +267,12 @@ public class AppTransitionController {
/**
* @return The window token that determines the animation theme.
*/
- private AppWindowToken findAnimLayoutParamsToken(@WindowManager.TransitionType int transit,
+ private ActivityRecord findAnimLayoutParamsToken(@WindowManager.TransitionType int transit,
ArraySet<Integer> activityTypes) {
- AppWindowToken result;
- final ArraySet<AppWindowToken> closingApps = mDisplayContent.mClosingApps;
- final ArraySet<AppWindowToken> openingApps = mDisplayContent.mOpeningApps;
- final ArraySet<AppWindowToken> changingApps = mDisplayContent.mChangingApps;
+ ActivityRecord result;
+ final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps;
+ final ArraySet<ActivityRecord> openingApps = mDisplayContent.mOpeningApps;
+ final ArraySet<ActivityRecord> changingApps = mDisplayContent.mChangingApps;
// Remote animations always win, but fullscreen tokens override non-fullscreen tokens.
result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
@@ -296,8 +294,8 @@ public class AppTransitionController {
* @return The set of {@link android.app.WindowConfiguration.ActivityType}s contained in the set
* of apps in {@code array1}, {@code array2}, and {@code array3}.
*/
- private static ArraySet<Integer> collectActivityTypes(ArraySet<AppWindowToken> array1,
- ArraySet<AppWindowToken> array2, ArraySet<AppWindowToken> array3) {
+ private static ArraySet<Integer> collectActivityTypes(ArraySet<ActivityRecord> array1,
+ ArraySet<ActivityRecord> array2, ArraySet<ActivityRecord> array3) {
final ArraySet<Integer> result = new ArraySet<>();
for (int i = array1.size() - 1; i >= 0; i--) {
result.add(array1.valueAt(i).getActivityType());
@@ -311,16 +309,16 @@ public class AppTransitionController {
return result;
}
- private static AppWindowToken lookForHighestTokenWithFilter(ArraySet<AppWindowToken> array1,
- ArraySet<AppWindowToken> array2, ArraySet<AppWindowToken> array3,
- Predicate<AppWindowToken> filter) {
+ private static ActivityRecord lookForHighestTokenWithFilter(ArraySet<ActivityRecord> array1,
+ ArraySet<ActivityRecord> array2, ArraySet<ActivityRecord> array3,
+ Predicate<ActivityRecord> filter) {
final int array2base = array1.size();
final int array3base = array2.size() + array2base;
final int count = array3base + array3.size();
int bestPrefixOrderIndex = Integer.MIN_VALUE;
- AppWindowToken bestToken = null;
+ ActivityRecord bestToken = null;
for (int i = 0; i < count; i++) {
- final AppWindowToken wtoken = i < array2base
+ final ActivityRecord wtoken = i < array2base
? array1.valueAt(i)
: (i < array3base
? array2.valueAt(i - array2base)
@@ -334,7 +332,7 @@ public class AppTransitionController {
return bestToken;
}
- private boolean containsVoiceInteraction(ArraySet<AppWindowToken> apps) {
+ private boolean containsVoiceInteraction(ArraySet<ActivityRecord> apps) {
for (int i = apps.size() - 1; i >= 0; i--) {
if (apps.valueAt(i).mVoiceInteraction) {
return true;
@@ -344,11 +342,11 @@ public class AppTransitionController {
}
private void handleOpeningApps(int transit, LayoutParams animLp, boolean voiceInteraction) {
- final ArraySet<AppWindowToken> openingApps = mDisplayContent.mOpeningApps;
+ final ArraySet<ActivityRecord> openingApps = mDisplayContent.mOpeningApps;
final int appsCount = openingApps.size();
for (int i = 0; i < appsCount; i++) {
- AppWindowToken wtoken = openingApps.valueAt(i);
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken);
+ ActivityRecord wtoken = openingApps.valueAt(i);
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now opening app %s", wtoken);
if (!wtoken.commitVisibility(animLp, true, transit, false, voiceInteraction)) {
// This token isn't going to be animating. Add it to the list of tokens to
@@ -378,12 +376,12 @@ public class AppTransitionController {
}
private void handleClosingApps(int transit, LayoutParams animLp, boolean voiceInteraction) {
- final ArraySet<AppWindowToken> closingApps = mDisplayContent.mClosingApps;
+ final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps;
final int appsCount = closingApps.size();
for (int i = 0; i < appsCount; i++) {
- AppWindowToken wtoken = closingApps.valueAt(i);
+ ActivityRecord wtoken = closingApps.valueAt(i);
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken);
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now closing app %s", wtoken);
// TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not
// animating?
wtoken.commitVisibility(animLp, false, transit, false, voiceInteraction);
@@ -406,17 +404,17 @@ public class AppTransitionController {
}
private void handleChangingApps(int transit, LayoutParams animLp, boolean voiceInteraction) {
- final ArraySet<AppWindowToken> apps = mDisplayContent.mChangingApps;
+ final ArraySet<ActivityRecord> apps = mDisplayContent.mChangingApps;
final int appsCount = apps.size();
for (int i = 0; i < appsCount; i++) {
- AppWindowToken wtoken = apps.valueAt(i);
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now changing app" + wtoken);
- wtoken.cancelAnimationOnly();
- wtoken.applyAnimationLocked(null, transit, true, false);
- wtoken.updateReportedVisibilityLocked();
+ ActivityRecord activity = apps.valueAt(i);
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now changing app %s", activity);
+ activity.cancelAnimationOnly();
+ activity.applyAnimationLocked(null, transit, true, false);
+ activity.updateReportedVisibilityLocked();
mService.openSurfaceTransaction();
try {
- wtoken.showAllWindowsLocked();
+ activity.showAllWindowsLocked();
} finally {
mService.closeSurfaceTransaction("handleChangingApps");
}
@@ -444,14 +442,13 @@ public class AppTransitionController {
}
}
- private boolean transitionGoodToGo(ArraySet<AppWindowToken> apps, SparseIntArray outReasons) {
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
- "Checking " + apps.size() + " opening apps (frozen="
- + mService.mDisplayFrozen + " timeout="
- + mDisplayContent.mAppTransition.isTimeout() + ")...");
- final ScreenRotationAnimation screenRotationAnimation =
- mService.mAnimator.getScreenRotationAnimationLocked(
- Display.DEFAULT_DISPLAY);
+ private boolean transitionGoodToGo(ArraySet<ActivityRecord> apps, SparseIntArray outReasons) {
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+ "Checking %d opening apps (frozen=%b timeout=%b)...", apps.size(),
+ mService.mDisplayFrozen, mDisplayContent.mAppTransition.isTimeout());
+
+ final ScreenRotationAnimation screenRotationAnimation = mService.mRoot.getDisplayContent(
+ Display.DEFAULT_DISPLAY).getRotationAnimation();
if (!mDisplayContent.mAppTransition.isTimeout()) {
// Imagine the case where we are changing orientation due to an app transition, but a
@@ -463,32 +460,30 @@ public class AppTransitionController {
// app transition.
if (screenRotationAnimation != null && screenRotationAnimation.isAnimating() &&
mDisplayContent.getDisplayRotation().needsUpdate()) {
- if (DEBUG_APP_TRANSITIONS) {
- Slog.v(TAG, "Delaying app transition for screen rotation animation to finish");
- }
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+ "Delaying app transition for screen rotation animation to finish");
return false;
}
for (int i = 0; i < apps.size(); i++) {
- AppWindowToken wtoken = apps.valueAt(i);
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
- "Check opening app=" + wtoken + ": allDrawn="
- + wtoken.allDrawn + " startingDisplayed="
- + wtoken.startingDisplayed + " startingMoved="
- + wtoken.startingMoved + " isRelaunching()="
- + wtoken.isRelaunching() + " startingWindow="
- + wtoken.startingWindow);
-
-
- final boolean allDrawn = wtoken.allDrawn && !wtoken.isRelaunching();
- if (!allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) {
+ ActivityRecord activity = apps.valueAt(i);
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+ "Check opening app=%s: allDrawn=%b startingDisplayed=%b "
+ + "startingMoved=%b isRelaunching()=%b startingWindow=%s",
+ activity, activity.allDrawn, activity.startingDisplayed,
+ activity.startingMoved, activity.isRelaunching(),
+ activity.startingWindow);
+
+
+ final boolean allDrawn = activity.allDrawn && !activity.isRelaunching();
+ if (!allDrawn && !activity.startingDisplayed && !activity.startingMoved) {
return false;
}
- final int windowingMode = wtoken.getWindowingMode();
+ final int windowingMode = activity.getWindowingMode();
if (allDrawn) {
outReasons.put(windowingMode, APP_TRANSITION_WINDOWS_DRAWN);
} else {
outReasons.put(windowingMode,
- wtoken.mStartingData instanceof SplashScreenStartingData
+ activity.mStartingData instanceof SplashScreenStartingData
? APP_TRANSITION_SPLASH_SCREEN
: APP_TRANSITION_SNAPSHOT);
}
@@ -496,15 +491,13 @@ public class AppTransitionController {
// We also need to wait for the specs to be fetched, if needed.
if (mDisplayContent.mAppTransition.isFetchingAppTransitionsSpecs()) {
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "isFetchingAppTransitionSpecs=true");
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "isFetchingAppTransitionSpecs=true");
return false;
}
if (!mDisplayContent.mUnknownAppVisibilityController.allResolved()) {
- if (DEBUG_APP_TRANSITIONS) {
- Slog.v(TAG, "unknownApps is not empty: "
- + mDisplayContent.mUnknownAppVisibilityController.getDebugMessage());
- }
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "unknownApps is not empty: %s",
+ mDisplayContent.mUnknownAppVisibilityController.getDebugMessage());
return false;
}
@@ -541,30 +534,28 @@ public class AppTransitionController {
(mWallpaperControllerLocked.isWallpaperTargetAnimating() || !showWallpaper)
? null
: wallpaperTarget;
- final ArraySet<AppWindowToken> openingApps = mDisplayContent.mOpeningApps;
- final ArraySet<AppWindowToken> closingApps = mDisplayContent.mClosingApps;
- final AppWindowToken topOpeningApp = getTopApp(mDisplayContent.mOpeningApps,
+ final ArraySet<ActivityRecord> openingApps = mDisplayContent.mOpeningApps;
+ final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps;
+ final ActivityRecord topOpeningApp = getTopApp(mDisplayContent.mOpeningApps,
false /* ignoreHidden */);
- final AppWindowToken topClosingApp = getTopApp(mDisplayContent.mClosingApps,
+ final ActivityRecord topClosingApp = getTopApp(mDisplayContent.mClosingApps,
true /* ignoreHidden */);
boolean openingCanBeWallpaperTarget = canBeWallpaperTarget(openingApps);
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
- "New wallpaper target=" + wallpaperTarget
- + ", oldWallpaper=" + oldWallpaper
- + ", openingApps=" + openingApps
- + ", closingApps=" + closingApps);
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+ "New wallpaper target=%s, oldWallpaper=%s, openingApps=%s, closingApps=%s",
+ wallpaperTarget, oldWallpaper, openingApps, closingApps);
if (openingCanBeWallpaperTarget && transit == TRANSIT_KEYGUARD_GOING_AWAY) {
transit = TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
- "New transit: " + AppTransition.appTransitionToString(transit));
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+ "New transit: %s", AppTransition.appTransitionToString(transit));
}
// We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic
// relies on the fact that we always execute a Keyguard transition after preparing one.
else if (!isKeyguardGoingAwayTransit(transit)) {
if (closingAppHasWallpaper && openingAppHasWallpaper) {
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!");
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Wallpaper animation!");
switch (transit) {
case TRANSIT_ACTIVITY_OPEN:
case TRANSIT_TASK_OPEN:
@@ -577,25 +568,26 @@ public class AppTransitionController {
transit = TRANSIT_WALLPAPER_INTRA_CLOSE;
break;
}
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
- "New transit: " + AppTransition.appTransitionToString(transit));
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+ "New transit: %s", AppTransition.appTransitionToString(transit));
} else if (oldWallpaper != null && !mDisplayContent.mOpeningApps.isEmpty()
- && !openingApps.contains(oldWallpaper.mAppToken)
- && closingApps.contains(oldWallpaper.mAppToken)
- && topClosingApp == oldWallpaper.mAppToken) {
+ && !openingApps.contains(oldWallpaper.mActivityRecord)
+ && closingApps.contains(oldWallpaper.mActivityRecord)
+ && topClosingApp == oldWallpaper.mActivityRecord) {
// We are transitioning from an activity with a wallpaper to one without.
transit = TRANSIT_WALLPAPER_CLOSE;
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit away from wallpaper: "
- + AppTransition.appTransitionToString(transit));
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+ "New transit away from wallpaper: %s",
+ AppTransition.appTransitionToString(transit));
} else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw()
- && openingApps.contains(wallpaperTarget.mAppToken)
- && topOpeningApp == wallpaperTarget.mAppToken
+ && openingApps.contains(wallpaperTarget.mActivityRecord)
+ && topOpeningApp == wallpaperTarget.mActivityRecord
&& transit != TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE) {
// We are transitioning from an activity without
// a wallpaper to now showing the wallpaper
transit = TRANSIT_WALLPAPER_OPEN;
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit into wallpaper: "
- + AppTransition.appTransitionToString(transit));
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "New transit into wallpaper: %s",
+ AppTransition.appTransitionToString(transit));
}
}
return transit;
@@ -624,10 +616,10 @@ public class AppTransitionController {
boolean allOpeningVisible = true;
boolean allTranslucentOpeningApps = !mDisplayContent.mOpeningApps.isEmpty();
for (int i = mDisplayContent.mOpeningApps.size() - 1; i >= 0; i--) {
- final AppWindowToken token = mDisplayContent.mOpeningApps.valueAt(i);
- if (!token.isVisible()) {
+ final ActivityRecord activity = mDisplayContent.mOpeningApps.valueAt(i);
+ if (!activity.isVisible()) {
allOpeningVisible = false;
- if (token.fillsParent()) {
+ if (activity.fillsParent()) {
allTranslucentOpeningApps = false;
}
}
@@ -668,13 +660,13 @@ public class AppTransitionController {
return false;
}
// check that all components are in the task.
- for (AppWindowToken activity : mDisplayContent.mOpeningApps) {
+ for (ActivityRecord activity : mDisplayContent.mOpeningApps) {
Task activityTask = activity.getTask();
if (activityTask != task) {
return false;
}
}
- for (AppWindowToken activity : mDisplayContent.mClosingApps) {
+ for (ActivityRecord activity : mDisplayContent.mClosingApps) {
if (activity.getTask() != task) {
return false;
}
@@ -682,7 +674,7 @@ public class AppTransitionController {
return true;
}
- private boolean canBeWallpaperTarget(ArraySet<AppWindowToken> apps) {
+ private boolean canBeWallpaperTarget(ArraySet<ActivityRecord> apps) {
for (int i = apps.size() - 1; i >= 0; i--) {
if (apps.valueAt(i).windowsCanBeWallpaperTarget()) {
return true;
@@ -692,18 +684,18 @@ public class AppTransitionController {
}
/**
- * Finds the top app in a list of apps, using its {@link AppWindowToken#getPrefixOrderIndex} to
+ * Finds the top app in a list of apps, using its {@link ActivityRecord#getPrefixOrderIndex} to
* compare z-order.
*
* @param apps The list of apps to search.
- * @param ignoreHidden If set to true, ignores apps that are {@link AppWindowToken#isHidden}.
- * @return The top {@link AppWindowToken}.
+ * @param ignoreHidden If set to true, ignores apps that are {@link ActivityRecord#isHidden}.
+ * @return The top {@link ActivityRecord}.
*/
- private AppWindowToken getTopApp(ArraySet<AppWindowToken> apps, boolean ignoreHidden) {
+ private ActivityRecord getTopApp(ArraySet<ActivityRecord> apps, boolean ignoreHidden) {
int topPrefixOrderIndex = Integer.MIN_VALUE;
- AppWindowToken topApp = null;
+ ActivityRecord topApp = null;
for (int i = apps.size() - 1; i >= 0; i--) {
- final AppWindowToken app = apps.valueAt(i);
+ final ActivityRecord app = apps.valueAt(i);
if (ignoreHidden && app.isHidden()) {
continue;
}
@@ -715,20 +707,4 @@ public class AppTransitionController {
}
return topApp;
}
-
- private void processApplicationsAnimatingInPlace(int transit) {
- if (transit == TRANSIT_TASK_IN_PLACE) {
- // Find the focused window
- final WindowState win = mDisplayContent.findFocusedWindow();
- if (win != null) {
- final AppWindowToken wtoken = win.mAppToken;
- if (DEBUG_APP_TRANSITIONS)
- Slog.v(TAG, "Now animating app in place " + wtoken);
- wtoken.cancelAnimation();
- wtoken.applyAnimationLocked(null, transit, false, false);
- wtoken.updateReportedVisibilityLocked();
- wtoken.showAllWindowsLocked();
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
index b52ade415bd4..acd96e916fa8 100644
--- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java
+++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
@@ -22,7 +22,7 @@ import static android.view.SurfaceControl.METADATA_WINDOW_TYPE;
import static com.android.server.wm.AppWindowThumbnailProto.HEIGHT;
import static com.android.server.wm.AppWindowThumbnailProto.SURFACE_ANIMATOR;
import static com.android.server.wm.AppWindowThumbnailProto.WIDTH;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
+import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
@@ -31,7 +31,6 @@ import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.os.Binder;
-import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -39,44 +38,45 @@ import android.view.SurfaceControl.Builder;
import android.view.SurfaceControl.Transaction;
import android.view.animation.Animation;
+import com.android.server.protolog.common.ProtoLog;
import com.android.server.wm.SurfaceAnimator.Animatable;
import java.util.function.Supplier;
/**
- * Represents a surface that is displayed over an {@link AppWindowToken}
+ * Represents a surface that is displayed over an {@link ActivityRecord}
*/
class AppWindowThumbnail implements Animatable {
private static final String TAG = TAG_WITH_CLASS_NAME ? "AppWindowThumbnail" : TAG_WM;
- private final AppWindowToken mAppToken;
+ private final ActivityRecord mActivityRecord;
private SurfaceControl mSurfaceControl;
private final SurfaceAnimator mSurfaceAnimator;
private final int mWidth;
private final int mHeight;
private final boolean mRelative;
- AppWindowThumbnail(Supplier<Surface> surfaceFactory, Transaction t, AppWindowToken appToken,
+ AppWindowThumbnail(Supplier<Surface> surfaceFactory, Transaction t, ActivityRecord activity,
GraphicBuffer thumbnailHeader) {
- this(surfaceFactory, t, appToken, thumbnailHeader, false /* relative */);
+ this(surfaceFactory, t, activity, thumbnailHeader, false /* relative */);
}
/**
* @param t Transaction to create the thumbnail in.
- * @param appToken {@link AppWindowToken} to associate this thumbnail with.
+ * @param activity {@link ActivityRecord} to associate this thumbnail with.
* @param thumbnailHeader A thumbnail or placeholder for thumbnail to initialize with.
- * @param relative Whether this thumbnail will be a child of appToken (and thus positioned
+ * @param relative Whether this thumbnail will be a child of activity (and thus positioned
* relative to it) or not.
*/
- AppWindowThumbnail(Supplier<Surface> surfaceFactory, Transaction t, AppWindowToken appToken,
+ AppWindowThumbnail(Supplier<Surface> surfaceFactory, Transaction t, ActivityRecord activity,
GraphicBuffer thumbnailHeader, boolean relative) {
- this(t, appToken, thumbnailHeader, relative, surfaceFactory.get(), null);
+ this(t, activity, thumbnailHeader, relative, surfaceFactory.get(), null);
}
- AppWindowThumbnail(Transaction t, AppWindowToken appToken, GraphicBuffer thumbnailHeader,
+ AppWindowThumbnail(Transaction t, ActivityRecord activity, GraphicBuffer thumbnailHeader,
boolean relative, Surface drawSurface, SurfaceAnimator animator) {
- mAppToken = appToken;
+ mActivityRecord = activity;
mRelative = relative;
if (animator != null) {
mSurfaceAnimator = animator;
@@ -84,29 +84,27 @@ class AppWindowThumbnail implements Animatable {
// We can't use a delegating constructor since we need to
// reference this::onAnimationFinished
mSurfaceAnimator =
- new SurfaceAnimator(this, this::onAnimationFinished, appToken.mWmService);
+ new SurfaceAnimator(this, this::onAnimationFinished, activity.mWmService);
}
mWidth = thumbnailHeader.getWidth();
mHeight = thumbnailHeader.getHeight();
// Create a new surface for the thumbnail
- WindowState window = appToken.findMainWindow();
+ WindowState window = mActivityRecord.findMainWindow();
// TODO: This should be attached as a child to the app token, once the thumbnail animations
// use relative coordinates. Once we start animating task we can also consider attaching
// this to the task.
- mSurfaceControl = appToken.makeSurface()
- .setName("thumbnail anim: " + appToken.toString())
+ mSurfaceControl = mActivityRecord.makeSurface()
+ .setName("thumbnail anim: " + mActivityRecord.toString())
.setBufferSize(mWidth, mHeight)
.setFormat(PixelFormat.TRANSLUCENT)
- .setMetadata(METADATA_WINDOW_TYPE, appToken.windowType)
+ .setMetadata(METADATA_WINDOW_TYPE, mActivityRecord.windowType)
.setMetadata(METADATA_OWNER_UID,
window != null ? window.mOwnerUid : Binder.getCallingUid())
.build();
- if (SHOW_TRANSACTIONS) {
- Slog.i(TAG, " THUMBNAIL " + mSurfaceControl + ": CREATE");
- }
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, " THUMBNAIL %s: CREATE", mSurfaceControl);
// Transfer the thumbnail to the surface
drawSurface.copyFrom(mSurfaceControl);
@@ -118,7 +116,7 @@ class AppWindowThumbnail implements Animatable {
// task.
t.setLayer(mSurfaceControl, Integer.MAX_VALUE);
if (relative) {
- t.reparent(mSurfaceControl, appToken.getSurfaceControl());
+ t.reparent(mSurfaceControl, mActivityRecord.getSurfaceControl());
}
}
@@ -128,12 +126,12 @@ class AppWindowThumbnail implements Animatable {
void startAnimation(Transaction t, Animation anim, Point position) {
anim.restrictDuration(MAX_ANIMATION_DURATION);
- anim.scaleCurrentDuration(mAppToken.mWmService.getTransitionAnimationScaleLocked());
+ anim.scaleCurrentDuration(mActivityRecord.mWmService.getTransitionAnimationScaleLocked());
mSurfaceAnimator.startAnimation(t, new LocalAnimationAdapter(
new WindowAnimationSpec(anim, position,
- mAppToken.getDisplayContent().mAppTransition.canSkipFirstFrame(),
- mAppToken.getDisplayContent().getWindowCornerRadius()),
- mAppToken.mWmService.mSurfaceAnimationRunner), false /* hidden */);
+ mActivityRecord.getDisplayContent().mAppTransition.canSkipFirstFrame(),
+ mActivityRecord.getDisplayContent().getWindowCornerRadius()),
+ mActivityRecord.mWmService.mSurfaceAnimationRunner), false /* hidden */);
}
/**
@@ -181,19 +179,19 @@ class AppWindowThumbnail implements Animatable {
@Override
public Transaction getPendingTransaction() {
- return mAppToken.getPendingTransaction();
+ return mActivityRecord.getPendingTransaction();
}
@Override
public void commitPendingTransaction() {
- mAppToken.commitPendingTransaction();
+ mActivityRecord.commitPendingTransaction();
}
@Override
public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
t.setLayer(leash, Integer.MAX_VALUE);
if (mRelative) {
- t.reparent(leash, mAppToken.getSurfaceControl());
+ t.reparent(leash, mActivityRecord.getSurfaceControl());
}
}
@@ -207,7 +205,7 @@ class AppWindowThumbnail implements Animatable {
@Override
public Builder makeAnimationLeash() {
- return mAppToken.makeSurface();
+ return mActivityRecord.makeSurface();
}
@Override
@@ -217,12 +215,12 @@ class AppWindowThumbnail implements Animatable {
@Override
public SurfaceControl getAnimationLeashParent() {
- return mAppToken.getAppAnimationLayer();
+ return mActivityRecord.getAppAnimationLayer();
}
@Override
public SurfaceControl getParentSurfaceControl() {
- return mAppToken.getParentSurfaceControl();
+ return mActivityRecord.getParentSurfaceControl();
}
@Override
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
deleted file mode 100644
index f647fe46f067..000000000000
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ /dev/null
@@ -1,3309 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.WINDOWING_MODE_FREEFORM;
-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.content.pm.ActivityInfo.COLOR_MODE_DEFAULT;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
-import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
-import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
-import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
-import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
-import static android.view.WindowManager.TRANSIT_UNSET;
-import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
-
-import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
-import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static com.android.server.wm.AppWindowTokenProto.ALL_DRAWN;
-import static com.android.server.wm.AppWindowTokenProto.APP_STOPPED;
-import static com.android.server.wm.AppWindowTokenProto.CLIENT_HIDDEN;
-import static com.android.server.wm.AppWindowTokenProto.DEFER_HIDING_CLIENT;
-import static com.android.server.wm.AppWindowTokenProto.FILLS_PARENT;
-import static com.android.server.wm.AppWindowTokenProto.FROZEN_BOUNDS;
-import static com.android.server.wm.AppWindowTokenProto.HIDDEN_REQUESTED;
-import static com.android.server.wm.AppWindowTokenProto.HIDDEN_SET_FROM_TRANSFERRED_STARTING_WINDOW;
-import static com.android.server.wm.AppWindowTokenProto.IS_REALLY_ANIMATING;
-import static com.android.server.wm.AppWindowTokenProto.IS_WAITING_FOR_TRANSITION_START;
-import static com.android.server.wm.AppWindowTokenProto.LAST_ALL_DRAWN;
-import static com.android.server.wm.AppWindowTokenProto.LAST_SURFACE_SHOWING;
-import static com.android.server.wm.AppWindowTokenProto.NAME;
-import static com.android.server.wm.AppWindowTokenProto.NUM_DRAWN_WINDOWS;
-import static com.android.server.wm.AppWindowTokenProto.NUM_INTERESTING_WINDOWS;
-import static com.android.server.wm.AppWindowTokenProto.REMOVED;
-import static com.android.server.wm.AppWindowTokenProto.REPORTED_DRAWN;
-import static com.android.server.wm.AppWindowTokenProto.REPORTED_VISIBLE;
-import static com.android.server.wm.AppWindowTokenProto.STARTING_DISPLAYED;
-import static com.android.server.wm.AppWindowTokenProto.STARTING_MOVED;
-import static com.android.server.wm.AppWindowTokenProto.STARTING_WINDOW;
-import static com.android.server.wm.AppWindowTokenProto.THUMBNAIL;
-import static com.android.server.wm.AppWindowTokenProto.WINDOW_TOKEN;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN;
-import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
-import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
-import static com.android.server.wm.WindowManagerService.logWithStack;
-import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY;
-import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
-import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
-import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
-
-import android.annotation.CallSuper;
-import android.annotation.Size;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.content.res.CompatibilityInfo;
-import android.content.res.Configuration;
-import android.graphics.GraphicBuffer;
-import android.graphics.PixelFormat;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Debug;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.Trace;
-import android.util.ArraySet;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-import android.view.DisplayInfo;
-import android.view.IApplicationToken;
-import android.view.InputApplicationHandle;
-import android.view.RemoteAnimationAdapter;
-import android.view.RemoteAnimationDefinition;
-import android.view.SurfaceControl;
-import android.view.SurfaceControl.Transaction;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-import android.view.animation.Animation;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ToBooleanFunction;
-import com.android.server.AttributeCache;
-import com.android.server.LocalServices;
-import com.android.server.display.color.ColorDisplayService;
-import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.policy.WindowManagerPolicy.StartingSurface;
-import com.android.server.wm.RemoteAnimationController.RemoteAnimationRecord;
-import com.android.server.wm.WindowManagerService.H;
-
-import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.function.Consumer;
-
-class AppTokenList extends ArrayList<AppWindowToken> {
-}
-
-/**
- * Version of WindowToken that is specifically for a particular application (or
- * really activity) that is displaying windows.
- */
-class AppWindowToken extends WindowToken implements WindowManagerService.AppFreezeListener,
- ConfigurationContainerListener {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "AppWindowToken" : TAG_WM;
-
- /**
- * Value to increment the z-layer when boosting a layer during animations. BOOST in l33tsp34k.
- */
- @VisibleForTesting static final int Z_BOOST_BASE = 800570000;
-
- // Non-null only for application tokens.
- final IApplicationToken appToken;
- final ComponentName mActivityComponent;
- final boolean mVoiceInteraction;
-
- /**
- * The activity is opaque and fills the entire space of this task.
- * @see WindowContainer#fillsParent()
- */
- private boolean mOccludesParent;
- boolean mShowForAllUsers;
- int mTargetSdk;
-
- // Flag set while reparenting to prevent actions normally triggered by an individual parent
- // change.
- private boolean mReparenting;
-
- // True if we are current in the process of removing this app token from the display
- private boolean mRemovingFromDisplay = false;
-
- // The input dispatching timeout for this application token in nanoseconds.
- long mInputDispatchingTimeoutNanos;
-
- // These are used for determining when all windows associated with
- // an activity have been drawn, so they can be made visible together
- // at the same time.
- // initialize so that it doesn't match mTransactionSequence which is an int.
- private long mLastTransactionSequence = Long.MIN_VALUE;
- private int mNumInterestingWindows;
- private int mNumDrawnWindows;
- boolean inPendingTransaction;
- boolean allDrawn;
- private boolean mLastAllDrawn;
- private boolean mUseTransferredAnimation;
-
- // Set to true when this app creates a surface while in the middle of an animation. In that
- // case do not clear allDrawn until the animation completes.
- boolean deferClearAllDrawn;
-
- // Is this window's surface needed? This is almost like hidden, except
- // it will sometimes be true a little earlier: when the token has
- // been shown, but is still waiting for its app transition to execute
- // before making its windows shown.
- boolean hiddenRequested;
-
- // Have we told the window clients to hide themselves?
- private boolean mClientHidden;
-
- // If true we will defer setting mClientHidden to true and reporting to the client that it is
- // hidden.
- boolean mDeferHidingClient;
-
- // Last visibility state we reported to the app token.
- boolean reportedVisible;
-
- // Last drawn state we reported to the app token.
- private boolean reportedDrawn;
-
- // Set to true when the token has been removed from the window mgr.
- boolean removed;
-
- // Information about an application starting window if displayed.
- // Note: these are de-referenced before the starting window animates away.
- StartingData mStartingData;
- WindowState startingWindow;
- StartingSurface startingSurface;
- boolean startingDisplayed;
- boolean startingMoved;
-
- // True if the hidden state of this token was forced to false due to a transferred starting
- // window.
- private boolean mHiddenSetFromTransferredStartingWindow;
- boolean firstWindowDrawn;
- private final WindowState.UpdateReportedVisibilityResults mReportedVisibilityResults =
- new WindowState.UpdateReportedVisibilityResults();
-
- // Input application handle used by the input dispatcher.
- final InputApplicationHandle mInputApplicationHandle;
-
- // TODO: Have a WindowContainer state for tracking exiting/deferred removal.
- boolean mIsExiting;
-
- boolean mLaunchTaskBehind;
- boolean mEnteringAnimation;
-
- private boolean mAlwaysFocusable;
-
- boolean mAppStopped;
- int mRotationAnimationHint;
- private int mPendingRelaunchCount;
-
- private boolean mLastContainsShowWhenLockedWindow;
- private boolean mLastContainsDismissKeyguardWindow;
-
- ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>();
- ArrayDeque<Configuration> mFrozenMergedConfig = new ArrayDeque<>();
-
- /**
- * The scale to fit at least one side of the activity to its parent. If the activity uses
- * 1920x1080, and the actually size on the screen is 960x540, then the scale is 0.5.
- */
- private float mSizeCompatScale = 1f;
- /**
- * The bounds in global coordinates for activity in size compatibility mode.
- * @see ActivityRecord#inSizeCompatMode
- */
- private Rect mSizeCompatBounds;
-
- private boolean mDisablePreviewScreenshots;
-
- private Task mLastParent;
-
- // TODO: Remove after unification
- ActivityRecord mActivityRecord;
-
- /**
- * @see #currentLaunchCanTurnScreenOn()
- */
- private boolean mCurrentLaunchCanTurnScreenOn = true;
-
- /**
- * If we are running an animation, this determines the transition type. Must be one of
- * AppTransition.TRANSIT_* constants.
- */
- private int mTransit;
-
- /**
- * If we are running an animation, this determines the flags during this animation. Must be a
- * bitwise combination of AppTransition.TRANSIT_FLAG_* constants.
- */
- private int mTransitFlags;
-
- /** Whether our surface was set to be showing in the last call to {@link #prepareSurfaces} */
- private boolean mLastSurfaceShowing = true;
-
- /**
- * This gets used during some open/close transitions as well as during a change transition
- * where it represents the starting-state snapshot.
- */
- private AppWindowThumbnail mThumbnail;
- private final Rect mTransitStartRect = new Rect();
-
- /**
- * This leash is used to "freeze" the app surface in place after the state change, but before
- * the animation is ready to start.
- */
- private SurfaceControl mTransitChangeLeash = null;
-
- /** Have we been asked to have this token keep the screen frozen? */
- private boolean mFreezingScreen;
-
- /** Whether this token should be boosted at the top of all app window tokens. */
- @VisibleForTesting boolean mNeedsZBoost;
- private Letterbox mLetterbox;
-
- private final Point mTmpPoint = new Point();
- private final Rect mTmpRect = new Rect();
- private final Rect mTmpPrevBounds = new Rect();
- private RemoteAnimationDefinition mRemoteAnimationDefinition;
- private AnimatingAppWindowTokenRegistry mAnimatingAppWindowTokenRegistry;
-
- /**
- * A flag to determine if this AWT is in the process of closing or entering PIP. This is needed
- * to help AWT know that the app is in the process of closing but hasn't yet started closing on
- * the WM side.
- */
- private boolean mWillCloseOrEnterPip;
-
- /** Layer used to constrain the animation to a token's stack bounds. */
- SurfaceControl mAnimationBoundsLayer;
-
- /** Whether this token needs to create mAnimationBoundsLayer for cropping animations. */
- boolean mNeedsAnimationBoundsLayer;
-
- private static final int STARTING_WINDOW_TYPE_NONE = 0;
- private static final int STARTING_WINDOW_TYPE_SNAPSHOT = 1;
- private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2;
-
- private AppSaturationInfo mLastAppSaturationInfo;
-
- private final ColorDisplayService.ColorTransformController mColorTransformController =
- (matrix, translation) -> mWmService.mH.post(() -> {
- synchronized (mWmService.mGlobalLock) {
- if (mLastAppSaturationInfo == null) {
- mLastAppSaturationInfo = new AppSaturationInfo();
- }
-
- mLastAppSaturationInfo.setSaturation(matrix, translation);
- updateColorTransform();
- }
- });
-
- AppWindowToken(WindowManagerService service, IApplicationToken token,
- ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc,
- long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers,
- int targetSdk, int orientation, int rotationAnimationHint,
- boolean launchTaskBehind, boolean alwaysFocusable,
- ActivityRecord activityRecord) {
- this(service, token, activityComponent, voiceInteraction, dc, fullscreen);
- // TODO: remove after unification
- mActivityRecord = activityRecord;
- mActivityRecord.registerConfigurationChangeListener(this);
- mInputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
- mShowForAllUsers = showForAllUsers;
- mTargetSdk = targetSdk;
- mOrientation = orientation;
- mLaunchTaskBehind = launchTaskBehind;
- mAlwaysFocusable = alwaysFocusable;
- mRotationAnimationHint = rotationAnimationHint;
-
- // Application tokens start out hidden.
- setHidden(true);
- hiddenRequested = true;
-
- ColorDisplayService.ColorDisplayServiceInternal cds = LocalServices.getService(
- ColorDisplayService.ColorDisplayServiceInternal.class);
- cds.attachColorTransformController(activityRecord.packageName, activityRecord.mUserId,
- new WeakReference<>(mColorTransformController));
- }
-
- AppWindowToken(WindowManagerService service, IApplicationToken token,
- ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc,
- boolean fillsParent) {
- super(service, token != null ? token.asBinder() : null, TYPE_APPLICATION, true, dc,
- false /* ownerCanManageAppTokens */);
- appToken = token;
- mActivityComponent = activityComponent;
- mVoiceInteraction = voiceInteraction;
- mOccludesParent = fillsParent;
- mInputApplicationHandle = new InputApplicationHandle(appToken.asBinder());
- }
-
- void onFirstWindowDrawn(WindowState win, WindowStateAnimator winAnimator) {
- firstWindowDrawn = true;
-
- // We now have a good window to show, remove dead placeholders
- removeDeadWindows();
-
- if (startingWindow != null) {
- if (DEBUG_STARTING_WINDOW || DEBUG_ANIM) Slog.v(TAG, "Finish starting "
- + win.mToken + ": first real window is shown, no animation");
- // If this initial window is animating, stop it -- we will do an animation to reveal
- // it from behind the starting window, so there is no need for it to also be doing its
- // own stuff.
- win.cancelAnimation();
- }
- removeStartingWindow();
- updateReportedVisibilityLocked();
- }
-
- void updateReportedVisibilityLocked() {
- if (appToken == null) {
- return;
- }
-
- if (DEBUG_VISIBILITY) Slog.v(TAG, "Update reported visibility: " + this);
- final int count = mChildren.size();
-
- mReportedVisibilityResults.reset();
-
- for (int i = 0; i < count; i++) {
- final WindowState win = mChildren.get(i);
- win.updateReportedVisibility(mReportedVisibilityResults);
- }
-
- int numInteresting = mReportedVisibilityResults.numInteresting;
- int numVisible = mReportedVisibilityResults.numVisible;
- int numDrawn = mReportedVisibilityResults.numDrawn;
- boolean nowGone = mReportedVisibilityResults.nowGone;
-
- boolean nowDrawn = numInteresting > 0 && numDrawn >= numInteresting;
- boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting && !isHidden();
- if (!nowGone) {
- // If the app is not yet gone, then it can only become visible/drawn.
- if (!nowDrawn) {
- nowDrawn = reportedDrawn;
- }
- if (!nowVisible) {
- nowVisible = reportedVisible;
- }
- }
- if (DEBUG_VISIBILITY) Slog.v(TAG, "VIS " + this + ": interesting="
- + numInteresting + " visible=" + numVisible);
- if (nowDrawn != reportedDrawn) {
- if (mActivityRecord != null) {
- mActivityRecord.onWindowsDrawn(nowDrawn, SystemClock.uptimeMillis());
- }
- reportedDrawn = nowDrawn;
- }
- if (nowVisible != reportedVisible) {
- if (DEBUG_VISIBILITY) Slog.v(TAG,
- "Visibility changed in " + this + ": vis=" + nowVisible);
- reportedVisible = nowVisible;
- if (mActivityRecord != null) {
- if (nowVisible) {
- onWindowsVisible();
- } else {
- onWindowsGone();
- }
- }
- }
- }
-
- private void onWindowsGone() {
- if (mActivityRecord == null) {
- return;
- }
- if (DEBUG_VISIBILITY) {
- Slog.v(TAG_WM, "Reporting gone in " + mActivityRecord.appToken);
- }
- mActivityRecord.onWindowsGone();
- }
-
- private void onWindowsVisible() {
- if (mActivityRecord == null) {
- return;
- }
- if (DEBUG_VISIBILITY) {
- Slog.v(TAG_WM, "Reporting visible in " + mActivityRecord.appToken);
- }
- mActivityRecord.onWindowsVisible();
- }
-
- boolean isClientHidden() {
- return mClientHidden;
- }
-
- void setClientHidden(boolean hideClient) {
- if (mClientHidden == hideClient || (hideClient && mDeferHidingClient)) {
- return;
- }
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "setClientHidden: " + this
- + " clientHidden=" + hideClient + " Callers=" + Debug.getCallers(5));
- mClientHidden = hideClient;
- sendAppVisibilityToClients();
- }
-
- void setVisibility(boolean visible, boolean deferHidingClient) {
- final AppTransition appTransition = getDisplayContent().mAppTransition;
-
- // Don't set visibility to false if we were already not visible. This prevents WM from
- // adding the app to the closing app list which doesn't make sense for something that is
- // already not visible. However, set visibility to true even if we are already visible.
- // This makes sure the app is added to the opening apps list so that the right
- // transition can be selected.
- // TODO: Probably a good idea to separate the concept of opening/closing apps from the
- // concept of setting visibility...
- if (!visible && hiddenRequested) {
-
- if (!deferHidingClient && mDeferHidingClient) {
- // We previously deferred telling the client to hide itself when visibility was
- // initially set to false. Now we would like it to hide, so go ahead and set it.
- mDeferHidingClient = deferHidingClient;
- setClientHidden(true);
- }
- return;
- }
-
- if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) {
- Slog.v(TAG_WM, "setAppVisibility("
- + appToken + ", visible=" + visible + "): " + appTransition
- + " hidden=" + isHidden() + " hiddenRequested="
- + hiddenRequested + " Callers=" + Debug.getCallers(6));
- }
-
- final DisplayContent displayContent = getDisplayContent();
- displayContent.mOpeningApps.remove(this);
- displayContent.mClosingApps.remove(this);
- if (isInChangeTransition()) {
- clearChangeLeash(getPendingTransaction(), true /* cancel */);
- }
- displayContent.mChangingApps.remove(this);
- waitingToShow = false;
- hiddenRequested = !visible;
- mDeferHidingClient = deferHidingClient;
-
- if (!visible) {
- // If the app is dead while it was visible, we kept its dead window on screen.
- // Now that the app is going invisible, we can remove it. It will be restarted
- // if made visible again.
- removeDeadWindows();
- } else {
- if (!appTransition.isTransitionSet()
- && appTransition.isReady()) {
- // Add the app mOpeningApps if transition is unset but ready. This means
- // we're doing a screen freeze, and the unfreeze will wait for all opening
- // apps to be ready.
- displayContent.mOpeningApps.add(this);
- }
- startingMoved = false;
- // If the token is currently hidden (should be the common case), or has been
- // stopped, then we need to set up to wait for its windows to be ready.
- if (isHidden() || mAppStopped) {
- clearAllDrawn();
-
- // If the app was already visible, don't reset the waitingToShow state.
- if (isHidden()) {
- waitingToShow = true;
-
- // Let's reset the draw state in order to prevent the starting window to be
- // immediately dismissed when the app still has the surface.
- forAllWindows(w -> {
- if (w.mWinAnimator.mDrawState == HAS_DRAWN) {
- w.mWinAnimator.resetDrawState();
-
- // Force add to mResizingWindows, so that we are guaranteed to get
- // another reportDrawn callback.
- w.resetLastContentInsets();
- }
- }, true /* traverseTopToBottom */);
- }
- }
-
- // In the case where we are making an app visible but holding off for a transition,
- // we still need to tell the client to make its windows visible so they get drawn.
- // Otherwise, we will wait on performing the transition until all windows have been
- // drawn, they never will be, and we are sad.
- setClientHidden(false);
-
- requestUpdateWallpaperIfNeeded();
-
- if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "No longer Stopped: " + this);
- mAppStopped = false;
-
- transferStartingWindowFromHiddenAboveTokenIfNeeded();
- }
-
- // If we are preparing an app transition, then delay changing
- // the visibility of this token until we execute that transition.
- if (okToAnimate() && appTransition.isTransitionSet()) {
- inPendingTransaction = true;
- if (visible) {
- displayContent.mOpeningApps.add(this);
- mEnteringAnimation = true;
- } else {
- displayContent.mClosingApps.add(this);
- mEnteringAnimation = false;
- }
- if (appTransition.getAppTransition() == TRANSIT_TASK_OPEN_BEHIND) {
- // We're launchingBehind, add the launching activity to mOpeningApps.
- final WindowState win = getDisplayContent().findFocusedWindow();
- if (win != null) {
- final AppWindowToken focusedToken = win.mAppToken;
- if (focusedToken != null) {
- if (DEBUG_APP_TRANSITIONS) {
- Slog.d(TAG_WM, "TRANSIT_TASK_OPEN_BEHIND, "
- + " adding " + focusedToken + " to mOpeningApps");
- }
- // Force animation to be loaded.
- displayContent.mOpeningApps.add(focusedToken);
- }
- }
- }
- // Changes in opening apps and closing apps may cause orientation change.
- reportDescendantOrientationChangeIfNeeded();
- return;
- }
-
- commitVisibility(null, visible, TRANSIT_UNSET, true, mVoiceInteraction);
- updateReportedVisibilityLocked();
- }
-
- boolean commitVisibility(WindowManager.LayoutParams lp,
- boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) {
-
- boolean delayed = false;
- inPendingTransaction = false;
- // Reset the state of mHiddenSetFromTransferredStartingWindow since visibility is actually
- // been set by the app now.
- mHiddenSetFromTransferredStartingWindow = false;
-
- // Allow for state changes and animation to be applied if:
- // * token is transitioning visibility state
- // * or the token was marked as hidden and is exiting before we had a chance to play the
- // transition animation
- // * or this is an opening app and windows are being replaced
- // * or the token is the opening app and visible while opening task behind existing one.
- final DisplayContent displayContent = getDisplayContent();
- boolean visibilityChanged = false;
- if (isHidden() == visible || (isHidden() && mIsExiting)
- || (visible && waitingForReplacement())
- || (visible && displayContent.mOpeningApps.contains(this)
- && displayContent.mAppTransition.getAppTransition() == TRANSIT_TASK_OPEN_BEHIND)) {
- final AccessibilityController accessibilityController =
- mWmService.mAccessibilityController;
- boolean changed = false;
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM,
- "Changing app " + this + " hidden=" + isHidden() + " performLayout=" + performLayout);
-
- boolean runningAppAnimation = false;
-
- if (transit != WindowManager.TRANSIT_UNSET) {
- if (mUseTransferredAnimation) {
- runningAppAnimation = isReallyAnimating();
- } else if (applyAnimationLocked(lp, transit, visible, isVoiceInteraction)) {
- runningAppAnimation = true;
- }
- delayed = runningAppAnimation;
- final WindowState window = findMainWindow();
- if (window != null && accessibilityController != null) {
- accessibilityController.onAppWindowTransitionLocked(window, transit);
- }
- changed = true;
- }
-
- final int windowsCount = mChildren.size();
- for (int i = 0; i < windowsCount; i++) {
- final WindowState win = mChildren.get(i);
- changed |= win.onAppVisibilityChanged(visible, runningAppAnimation);
- }
-
- setHidden(!visible);
- hiddenRequested = !visible;
- visibilityChanged = true;
- if (!visible) {
- stopFreezingScreen(true, true);
- } else {
- // If we are being set visible, and the starting window is not yet displayed,
- // then make sure it doesn't get displayed.
- if (startingWindow != null && !startingWindow.isDrawnLw()) {
- startingWindow.clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
- startingWindow.mLegacyPolicyVisibilityAfterAnim = false;
- }
-
- // We are becoming visible, so better freeze the screen with the windows that are
- // getting visible so we also wait for them.
- forAllWindows(mWmService::makeWindowFreezingScreenIfNeededLocked, true);
- }
-
- if (DEBUG_APP_TRANSITIONS) {
- Slog.v(TAG_WM, "commitVisibility: " + this
- + ": hidden=" + isHidden() + " hiddenRequested=" + hiddenRequested);
- }
-
- if (changed) {
- displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();
- if (performLayout) {
- mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
- false /*updateInputWindows*/);
- mWmService.mWindowPlacerLocked.performSurfacePlacement();
- }
- displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
- }
- }
- mUseTransferredAnimation = false;
-
- if (isReallyAnimating()) {
- delayed = true;
- } else {
-
- // We aren't animating anything, but exiting windows rely on the animation finished
- // callback being called in case the AppWindowToken was pretending to be animating,
- // which we might have done because we were in closing/opening apps list.
- onAnimationFinished();
- }
-
- for (int i = mChildren.size() - 1; i >= 0 && !delayed; i--) {
- if ((mChildren.get(i)).isSelfOrChildAnimating()) {
- delayed = true;
- }
- }
-
- if (visibilityChanged) {
- if (visible && !delayed) {
- // The token was made immediately visible, there will be no entrance animation.
- // We need to inform the client the enter animation was finished.
- mEnteringAnimation = true;
- mWmService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(
- token);
- }
-
- // If we're becoming visible, immediately change client visibility as well. there seem
- // to be some edge cases where we change our visibility but client visibility never gets
- // updated.
- // If we're becoming invisible, update the client visibility if we are not running an
- // animation. Otherwise, we'll update client visibility in onAnimationFinished.
- if (visible || !isReallyAnimating()) {
- setClientHidden(!visible);
- }
-
- if (!displayContent.mClosingApps.contains(this)
- && !displayContent.mOpeningApps.contains(this)) {
- // The token is not closing nor opening, so even if there is an animation set, that
- // doesn't mean that it goes through the normal app transition cycle so we have
- // to inform the docked controller about visibility change.
- // TODO(multi-display): notify docked divider on all displays where visibility was
- // affected.
- displayContent.getDockedDividerController().notifyAppVisibilityChanged();
-
- // Take the screenshot before possibly hiding the WSA, otherwise the screenshot
- // will not be taken.
- mWmService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible);
- }
-
- // If we are hidden but there is no delay needed we immediately
- // apply the Surface transaction so that the ActivityManager
- // can have some guarantee on the Surface state following
- // setting the visibility. This captures cases like dismissing
- // the docked or pinned stack where there is no app transition.
- //
- // In the case of a "Null" animation, there will be
- // no animation but there will still be a transition set.
- // We still need to delay hiding the surface such that it
- // can be synchronized with showing the next surface in the transition.
- if (isHidden() && !delayed && !displayContent.mAppTransition.isTransitionSet()) {
- SurfaceControl.openTransaction();
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- mChildren.get(i).mWinAnimator.hide("immediately hidden");
- }
- SurfaceControl.closeTransaction();
- }
-
- // Visibility changes may cause orientation request change.
- reportDescendantOrientationChangeIfNeeded();
- }
-
- return delayed;
- }
-
- private void reportDescendantOrientationChangeIfNeeded() {
- // Orientation request is exposed only when we're visible. Therefore visibility change
- // will change requested orientation. Notify upward the hierarchy ladder to adjust
- // configuration. This is important to cases where activities with incompatible
- // orientations launch, or user goes back from an activity of bi-orientation to an
- // activity with specified orientation.
- if (mActivityRecord.getRequestedConfigurationOrientation() == getConfiguration().orientation
- || getOrientationIgnoreVisibility() == SCREEN_ORIENTATION_UNSET) {
- return;
- }
-
- final IBinder freezeToken =
- mActivityRecord.mayFreezeScreenLocked(mActivityRecord.app)
- ? mActivityRecord.appToken : null;
- onDescendantOrientationChanged(freezeToken, mActivityRecord);
- }
-
- /**
- * @return The to top most child window for which {@link LayoutParams#isFullscreen()} returns
- * true.
- */
- WindowState getTopFullscreenWindow() {
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState win = mChildren.get(i);
- if (win != null && win.mAttrs.isFullscreen()) {
- return win;
- }
- }
- return null;
- }
-
- WindowState findMainWindow() {
- return findMainWindow(true);
- }
-
- /**
- * Finds the main window that either has type base application or application starting if
- * requested.
- *
- * @param includeStartingApp Allow to search application-starting windows to also be returned.
- * @return The main window of type base application or application starting if requested.
- */
- WindowState findMainWindow(boolean includeStartingApp) {
- WindowState candidate = null;
- for (int j = mChildren.size() - 1; j >= 0; --j) {
- final WindowState win = mChildren.get(j);
- final int type = win.mAttrs.type;
- // No need to loop through child window as base application and starting types can't be
- // child windows.
- if (type == TYPE_BASE_APPLICATION
- || (includeStartingApp && type == TYPE_APPLICATION_STARTING)) {
- // In cases where there are multiple windows, we prefer the non-exiting window. This
- // happens for example when replacing windows during an activity relaunch. When
- // constructing the animation, we want the new window, not the exiting one.
- if (win.mAnimatingExit) {
- candidate = win;
- } else {
- return win;
- }
- }
- }
- return candidate;
- }
-
- boolean windowsAreFocusable() {
- if (mTargetSdk < Build.VERSION_CODES.Q) {
- final int pid = mActivityRecord != null
- ? (mActivityRecord.app != null ? mActivityRecord.app.getPid() : 0) : 0;
- final AppWindowToken topFocusedAppOfMyProcess =
- mWmService.mRoot.mTopFocusedAppByProcess.get(pid);
- if (topFocusedAppOfMyProcess != null && topFocusedAppOfMyProcess != this) {
- // For the apps below Q, there can be only one app which has the focused window per
- // process, because legacy apps may not be ready for a multi-focus system.
- return false;
- }
- }
- return getWindowConfiguration().canReceiveKeys() || mAlwaysFocusable;
- }
-
- @Override
- boolean isVisible() {
- // If the app token isn't hidden then it is considered visible and there is no need to check
- // its children windows to see if they are visible.
- return !isHidden();
- }
-
- @Override
- void removeImmediately() {
- onRemovedFromDisplay();
- if (mActivityRecord != null) {
- mActivityRecord.unregisterConfigurationChangeListener(this);
- }
- super.removeImmediately();
- }
-
- @Override
- void removeIfPossible() {
- mIsExiting = false;
- removeAllWindowsIfPossible();
- removeImmediately();
- }
-
- @Override
- boolean checkCompleteDeferredRemoval() {
- if (mIsExiting) {
- removeIfPossible();
- }
- return super.checkCompleteDeferredRemoval();
- }
-
- void onRemovedFromDisplay() {
- if (mRemovingFromDisplay) {
- return;
- }
- mRemovingFromDisplay = true;
-
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Removing app token: " + this);
-
- boolean delayed = commitVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction);
-
- getDisplayContent().mOpeningApps.remove(this);
- getDisplayContent().mChangingApps.remove(this);
- getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
- mWmService.mTaskSnapshotController.onAppRemoved(this);
- waitingToShow = false;
- if (getDisplayContent().mClosingApps.contains(this)) {
- delayed = true;
- } else if (getDisplayContent().mAppTransition.isTransitionSet()) {
- getDisplayContent().mClosingApps.add(this);
- delayed = true;
- }
-
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Removing app " + this + " delayed=" + delayed
- + " animation=" + getAnimation() + " animating=" + isSelfAnimating());
-
- if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM, "removeAppToken: "
- + this + " delayed=" + delayed + " Callers=" + Debug.getCallers(4));
-
- if (mStartingData != null) {
- removeStartingWindow();
- }
-
- // If this window was animating, then we need to ensure that the app transition notifies
- // that animations have completed in DisplayContent.handleAnimatingStoppedAndTransition(),
- // so add to that list now
- if (isSelfAnimating()) {
- getDisplayContent().mNoAnimationNotifyOnTransitionFinished.add(token);
- }
-
- final TaskStack stack = getStack();
- if (delayed && !isEmpty()) {
- // set the token aside because it has an active animation to be finished
- if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM,
- "removeAppToken make exiting: " + this);
- if (stack != null) {
- stack.mExitingAppTokens.add(this);
- }
- mIsExiting = true;
- } else {
- // Make sure there is no animation running on this token, so any windows associated
- // with it will be removed as soon as their animations are complete
- cancelAnimation();
- if (stack != null) {
- stack.mExitingAppTokens.remove(this);
- }
- removeIfPossible();
- }
-
- removed = true;
- stopFreezingScreen(true, true);
-
- final DisplayContent dc = getDisplayContent();
- if (dc.mFocusedApp == this) {
- if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Removing focused app token:" + this
- + " displayId=" + dc.getDisplayId());
- dc.setFocusedApp(null);
- mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
- }
- if (mLetterbox != null) {
- mLetterbox.destroy();
- mLetterbox = null;
- }
-
- if (!delayed) {
- updateReportedVisibilityLocked();
- }
-
- // Reset the last saved PiP snap fraction on removal.
- mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(mActivityComponent);
-
- mRemovingFromDisplay = false;
- }
-
- void clearAnimatingFlags() {
- boolean wallpaperMightChange = false;
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState win = mChildren.get(i);
- wallpaperMightChange |= win.clearAnimatingFlags();
- }
- if (wallpaperMightChange) {
- requestUpdateWallpaperIfNeeded();
- }
- }
-
- void destroySurfaces() {
- destroySurfaces(false /*cleanupOnResume*/);
- }
-
- /**
- * Destroy surfaces which have been marked as eligible by the animator, taking care to ensure
- * the client has finished with them.
- *
- * @param cleanupOnResume whether this is done when app is resumed without fully stopped. If
- * set to true, destroy only surfaces of removed windows, and clear relevant flags of the
- * others so that they are ready to be reused. If set to false (common case), destroy all
- * surfaces that's eligible, if the app is already stopped.
- */
- private void destroySurfaces(boolean cleanupOnResume) {
- boolean destroyedSomething = false;
-
- // Copying to a different list as multiple children can be removed.
- final ArrayList<WindowState> children = new ArrayList<>(mChildren);
- for (int i = children.size() - 1; i >= 0; i--) {
- final WindowState win = children.get(i);
- destroyedSomething |= win.destroySurface(cleanupOnResume, mAppStopped);
- }
- if (destroyedSomething) {
- final DisplayContent dc = getDisplayContent();
- dc.assignWindowLayers(true /*setLayoutNeeded*/);
- updateLetterboxSurface(null);
- }
- }
-
- /**
- * Notify that the app is now resumed, and it was not stopped before, perform a clean
- * up of the surfaces
- */
- void notifyAppResumed(boolean wasStopped) {
- if (DEBUG_ADD_REMOVE) Slog.v(TAG, "notifyAppResumed: wasStopped=" + wasStopped
- + " " + this);
- mAppStopped = false;
- // Allow the window to turn the screen on once the app is resumed again.
- setCurrentLaunchCanTurnScreenOn(true);
- if (!wasStopped) {
- destroySurfaces(true /*cleanupOnResume*/);
- }
- }
-
- /**
- * Notify that the app has stopped, and it is okay to destroy any surfaces which were
- * keeping alive in case they were still being used.
- */
- void notifyAppStopped() {
- if (DEBUG_ADD_REMOVE) Slog.v(TAG, "notifyAppStopped: " + this);
- mAppStopped = true;
- // Reset the last saved PiP snap fraction on app stop.
- mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(mActivityComponent);
- destroySurfaces();
- // Remove any starting window that was added for this app if they are still around.
- removeStartingWindow();
- }
-
- void clearAllDrawn() {
- allDrawn = false;
- deferClearAllDrawn = false;
- }
-
- Task getTask() {
- return (Task) getParent();
- }
-
- TaskStack getStack() {
- final Task task = getTask();
- if (task != null) {
- return task.mStack;
- } else {
- return null;
- }
- }
-
- @Override
- void onParentChanged() {
- super.onParentChanged();
-
- final Task task = getTask();
-
- // When the associated task is {@code null}, the {@link AppWindowToken} can no longer
- // access visual elements like the {@link DisplayContent}. We must remove any associations
- // such as animations.
- if (!mReparenting) {
- if (task == null) {
- // It is possible we have been marked as a closing app earlier. We must remove ourselves
- // from this list so we do not participate in any future animations.
- getDisplayContent().mClosingApps.remove(this);
- } else if (mLastParent != null && mLastParent.mStack != null) {
- task.mStack.mExitingAppTokens.remove(this);
- }
- }
- final TaskStack stack = getStack();
-
- // If we reparent, make sure to remove ourselves from the old animation registry.
- if (mAnimatingAppWindowTokenRegistry != null) {
- mAnimatingAppWindowTokenRegistry.notifyFinished(this);
- }
- mAnimatingAppWindowTokenRegistry = stack != null
- ? stack.getAnimatingAppWindowTokenRegistry()
- : null;
-
- mLastParent = task;
-
- updateColorTransform();
- }
-
- void postWindowRemoveStartingWindowCleanup(WindowState win) {
- // TODO: Something smells about the code below...Is there a better way?
- if (startingWindow == win) {
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Notify removed startingWindow " + win);
- removeStartingWindow();
- } else if (mChildren.size() == 0) {
- // If this is the last window and we had requested a starting transition window,
- // well there is no point now.
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Nulling last startingData");
- mStartingData = null;
- if (mHiddenSetFromTransferredStartingWindow) {
- // We set the hidden state to false for the token from a transferred starting window.
- // We now reset it back to true since the starting window was the last window in the
- // token.
- setHidden(true);
- }
- } else if (mChildren.size() == 1 && startingSurface != null && !isRelaunching()) {
- // If this is the last window except for a starting transition window,
- // we need to get rid of the starting transition.
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Last window, removing starting window "
- + win);
- removeStartingWindow();
- }
- }
-
- void removeDeadWindows() {
- for (int winNdx = mChildren.size() - 1; winNdx >= 0; --winNdx) {
- WindowState win = mChildren.get(winNdx);
- if (win.mAppDied) {
- if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.w(TAG,
- "removeDeadWindows: " + win);
- // Set mDestroying, we don't want any animation or delayed removal here.
- win.mDestroying = true;
- // Also removes child windows.
- win.removeIfPossible();
- }
- }
- }
-
- boolean hasWindowsAlive() {
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- // No need to loop through child windows as the answer should be the same as that of the
- // parent window.
- if (!(mChildren.get(i)).mAppDied) {
- return true;
- }
- }
- return false;
- }
-
- void setWillReplaceWindows(boolean animate) {
- if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM,
- "Marking app token " + this + " with replacing windows.");
-
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState w = mChildren.get(i);
- w.setWillReplaceWindow(animate);
- }
- }
-
- void setWillReplaceChildWindows() {
- if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "Marking app token " + this
- + " with replacing child windows.");
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState w = mChildren.get(i);
- w.setWillReplaceChildWindows();
- }
- }
-
- void clearWillReplaceWindows() {
- if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM,
- "Resetting app token " + this + " of replacing window marks.");
-
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState w = mChildren.get(i);
- w.clearWillReplaceWindow();
- }
- }
-
- void requestUpdateWallpaperIfNeeded() {
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState w = mChildren.get(i);
- w.requestUpdateWallpaperIfNeeded();
- }
- }
-
- boolean isRelaunching() {
- return mPendingRelaunchCount > 0;
- }
-
- boolean shouldFreezeBounds() {
- final Task task = getTask();
-
- // For freeform windows, we can't freeze the bounds at the moment because this would make
- // the resizing unresponsive.
- if (task == null || task.inFreeformWindowingMode()) {
- return false;
- }
-
- // We freeze the bounds while drag resizing to deal with the time between
- // the divider/drag handle being released, and the handling it's new
- // configuration. If we are relaunched outside of the drag resizing state,
- // we need to be careful not to do this.
- return getTask().isDragResizing();
- }
-
- void startRelaunching() {
- if (shouldFreezeBounds()) {
- freezeBounds();
- }
-
- // In the process of tearing down before relaunching, the app will
- // try and clean up it's child surfaces. We need to prevent this from
- // happening, so we sever the children, transfering their ownership
- // from the client it-self to the parent surface (owned by us).
- detachChildren();
-
- mPendingRelaunchCount++;
- }
-
- void detachChildren() {
- SurfaceControl.openTransaction();
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState w = mChildren.get(i);
- w.mWinAnimator.detachChildren();
- }
- SurfaceControl.closeTransaction();
- }
-
- void finishRelaunching() {
- unfreezeBounds();
-
- if (mPendingRelaunchCount > 0) {
- mPendingRelaunchCount--;
- } else {
- // Update keyguard flags upon finishing relaunch.
- checkKeyguardFlagsChanged();
- }
- }
-
- void clearRelaunching() {
- if (mPendingRelaunchCount == 0) {
- return;
- }
- unfreezeBounds();
- mPendingRelaunchCount = 0;
- }
-
- /**
- * Returns true if the new child window we are adding to this token is considered greater than
- * the existing child window in this token in terms of z-order.
- */
- @Override
- protected boolean isFirstChildWindowGreaterThanSecond(WindowState newWindow,
- WindowState existingWindow) {
- final int type1 = newWindow.mAttrs.type;
- final int type2 = existingWindow.mAttrs.type;
-
- // Base application windows should be z-ordered BELOW all other windows in the app token.
- if (type1 == TYPE_BASE_APPLICATION && type2 != TYPE_BASE_APPLICATION) {
- return false;
- } else if (type1 != TYPE_BASE_APPLICATION && type2 == TYPE_BASE_APPLICATION) {
- return true;
- }
-
- // Starting windows should be z-ordered ABOVE all other windows in the app token.
- if (type1 == TYPE_APPLICATION_STARTING && type2 != TYPE_APPLICATION_STARTING) {
- return true;
- } else if (type1 != TYPE_APPLICATION_STARTING && type2 == TYPE_APPLICATION_STARTING) {
- return false;
- }
-
- // Otherwise the new window is greater than the existing window.
- return true;
- }
-
- /**
- * @return {@code true} if starting window is in app's hierarchy.
- */
- boolean hasStartingWindow() {
- if (startingDisplayed || mStartingData != null) {
- return true;
- }
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- if (getChildAt(i).mAttrs.type == TYPE_APPLICATION_STARTING) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- void addWindow(WindowState w) {
- super.addWindow(w);
-
- boolean gotReplacementWindow = false;
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState candidate = mChildren.get(i);
- gotReplacementWindow |= candidate.setReplacementWindowIfNeeded(w);
- }
-
- // if we got a replacement window, reset the timeout to give drawing more time
- if (gotReplacementWindow) {
- mWmService.scheduleWindowReplacementTimeouts(this);
- }
- checkKeyguardFlagsChanged();
- }
-
- @Override
- void removeChild(WindowState child) {
- if (!mChildren.contains(child)) {
- // This can be true when testing.
- return;
- }
- super.removeChild(child);
- checkKeyguardFlagsChanged();
- updateLetterboxSurface(child);
- }
-
- private boolean waitingForReplacement() {
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState candidate = mChildren.get(i);
- if (candidate.waitingForReplacement()) {
- return true;
- }
- }
- return false;
- }
-
- void onWindowReplacementTimeout() {
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- (mChildren.get(i)).onWindowReplacementTimeout();
- }
- }
-
- void reparent(Task task, int position) {
- if (DEBUG_ADD_REMOVE) {
- Slog.i(TAG_WM, "reparent: moving app token=" + this
- + " to task=" + task.mTaskId + " at " + position);
- }
- if (task == null) {
- throw new IllegalArgumentException("reparent: could not find task");
- }
- final Task currentTask = getTask();
- if (task == currentTask) {
- throw new IllegalArgumentException(
- "window token=" + this + " already child of task=" + currentTask);
- }
-
- if (currentTask.mStack != task.mStack) {
- throw new IllegalArgumentException(
- "window token=" + this + " current task=" + currentTask
- + " belongs to a different stack than " + task);
- }
-
- if (DEBUG_ADD_REMOVE) Slog.i(TAG, "reParentWindowToken: removing window token=" + this
- + " from task=" + currentTask);
- final DisplayContent prevDisplayContent = getDisplayContent();
-
- mReparenting = true;
-
- getParent().removeChild(this);
- task.addChild(this, position);
-
- mReparenting = false;
-
- // Relayout display(s).
- final DisplayContent displayContent = task.getDisplayContent();
- displayContent.setLayoutNeeded();
- if (prevDisplayContent != displayContent) {
- onDisplayChanged(displayContent);
- prevDisplayContent.setLayoutNeeded();
- }
- getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
- }
-
- @Override
- void onDisplayChanged(DisplayContent dc) {
- DisplayContent prevDc = mDisplayContent;
- super.onDisplayChanged(dc);
- if (prevDc == null || prevDc == mDisplayContent) {
- return;
- }
-
- if (prevDc.mOpeningApps.remove(this)) {
- // Transfer opening transition to new display.
- mDisplayContent.mOpeningApps.add(this);
- mDisplayContent.prepareAppTransition(prevDc.mAppTransition.getAppTransition(), true);
- mDisplayContent.executeAppTransition();
- }
-
- if (prevDc.mChangingApps.remove(this)) {
- // This gets called *after* the AppWindowToken has been reparented to the new display.
- // That reparenting resulted in this window changing modes (eg. FREEFORM -> FULLSCREEN),
- // so this token is now "frozen" while waiting for the animation to start on prevDc
- // (which will be cancelled since the window is no-longer a child). However, since this
- // is no longer a child of prevDc, this won't be notified of the cancelled animation,
- // so we need to cancel the change transition here.
- clearChangeLeash(getPendingTransaction(), true /* cancel */);
- }
- prevDc.mClosingApps.remove(this);
-
- if (prevDc.mFocusedApp == this) {
- prevDc.setFocusedApp(null);
- final TaskStack stack = dc.getTopStack();
- if (stack != null) {
- final Task task = stack.getTopChild();
- if (task != null && task.getTopChild() == this) {
- dc.setFocusedApp(this);
- }
- }
- }
-
- if (mLetterbox != null) {
- mLetterbox.onMovedToDisplay(mDisplayContent.getDisplayId());
- }
- }
-
- /**
- * Freezes the task bounds. The size of this task reported the app will be fixed to the bounds
- * freezed by {@link Task#prepareFreezingBounds} until {@link #unfreezeBounds} gets called, even
- * if they change in the meantime. If the bounds are already frozen, the bounds will be frozen
- * with a queue.
- */
- private void freezeBounds() {
- final Task task = getTask();
- mFrozenBounds.offer(new Rect(task.mPreparedFrozenBounds));
-
- if (task.mPreparedFrozenMergedConfig.equals(Configuration.EMPTY)) {
- // We didn't call prepareFreezingBounds on the task, so use the current value.
- mFrozenMergedConfig.offer(new Configuration(task.getConfiguration()));
- } else {
- mFrozenMergedConfig.offer(new Configuration(task.mPreparedFrozenMergedConfig));
- }
- // Calling unset() to make it equal to Configuration.EMPTY.
- task.mPreparedFrozenMergedConfig.unset();
- }
-
- /**
- * Unfreezes the previously frozen bounds. See {@link #freezeBounds}.
- */
- private void unfreezeBounds() {
- if (mFrozenBounds.isEmpty()) {
- return;
- }
- mFrozenBounds.remove();
- if (!mFrozenMergedConfig.isEmpty()) {
- mFrozenMergedConfig.remove();
- }
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState win = mChildren.get(i);
- win.onUnfreezeBounds();
- }
- mWmService.mWindowPlacerLocked.performSurfacePlacement();
- }
-
- void setAppLayoutChanges(int changes, String reason) {
- if (!mChildren.isEmpty()) {
- final DisplayContent dc = getDisplayContent();
- dc.pendingLayoutChanges |= changes;
- if (DEBUG_LAYOUT_REPEATS) {
- mWmService.mWindowPlacerLocked.debugLayoutRepeats(reason, dc.pendingLayoutChanges);
- }
- }
- }
-
- void removeReplacedWindowIfNeeded(WindowState replacement) {
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState win = mChildren.get(i);
- if (win.removeReplacedWindowIfNeeded(replacement)) {
- return;
- }
- }
- }
-
- void startFreezingScreen() {
- if (DEBUG_ORIENTATION) logWithStack(TAG, "Set freezing of " + appToken + ": hidden="
- + isHidden() + " freezing=" + mFreezingScreen + " hiddenRequested="
- + hiddenRequested);
- if (!hiddenRequested) {
- if (!mFreezingScreen) {
- mFreezingScreen = true;
- mWmService.registerAppFreezeListener(this);
- mWmService.mAppsFreezingScreen++;
- if (mWmService.mAppsFreezingScreen == 1) {
- mWmService.startFreezingDisplayLocked(0, 0, getDisplayContent());
- mWmService.mH.removeMessages(H.APP_FREEZE_TIMEOUT);
- mWmService.mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000);
- }
- }
- final int count = mChildren.size();
- for (int i = 0; i < count; i++) {
- final WindowState w = mChildren.get(i);
- w.onStartFreezingScreen();
- }
- }
- }
-
- void stopFreezingScreen(boolean unfreezeSurfaceNow, boolean force) {
- if (!mFreezingScreen) {
- return;
- }
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Clear freezing of " + this + " force=" + force);
- final int count = mChildren.size();
- boolean unfrozeWindows = false;
- for (int i = 0; i < count; i++) {
- final WindowState w = mChildren.get(i);
- unfrozeWindows |= w.onStopFreezingScreen();
- }
- if (force || unfrozeWindows) {
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "No longer freezing: " + this);
- mFreezingScreen = false;
- mWmService.unregisterAppFreezeListener(this);
- mWmService.mAppsFreezingScreen--;
- mWmService.mLastFinishedFreezeSource = this;
- }
- if (unfreezeSurfaceNow) {
- if (unfrozeWindows) {
- mWmService.mWindowPlacerLocked.performSurfacePlacement();
- }
- mWmService.stopFreezingDisplayLocked();
- }
- }
-
- @Override
- public void onAppFreezeTimeout() {
- Slog.w(TAG_WM, "Force clearing freeze: " + this);
- stopFreezingScreen(true, true);
- }
-
- /**
- * Tries to transfer the starting window from a token that's above ourselves in the task but
- * not visible anymore. This is a common scenario apps use: Trampoline activity T start main
- * activity M in the same task. Now, when reopening the task, T starts on top of M but then
- * immediately finishes after, so we have to transfer T to M.
- */
- void transferStartingWindowFromHiddenAboveTokenIfNeeded() {
- final Task task = getTask();
- for (int i = task.mChildren.size() - 1; i >= 0; i--) {
- final AppWindowToken fromToken = task.mChildren.get(i);
- if (fromToken == this) {
- return;
- }
- if (fromToken.hiddenRequested && transferStartingWindow(fromToken.token)) {
- return;
- }
- }
- }
-
- boolean transferStartingWindow(IBinder transferFrom) {
- final AppWindowToken fromToken = getDisplayContent().getAppWindowToken(transferFrom);
- if (fromToken == null) {
- return false;
- }
-
- final WindowState tStartingWindow = fromToken.startingWindow;
- if (tStartingWindow != null && fromToken.startingSurface != null) {
- // In this case, the starting icon has already been displayed, so start
- // letting windows get shown immediately without any more transitions.
- getDisplayContent().mSkipAppTransitionAnimation = true;
-
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Moving existing starting " + tStartingWindow
- + " from " + fromToken + " to " + this);
-
- final long origId = Binder.clearCallingIdentity();
- try {
- // Transfer the starting window over to the new token.
- mStartingData = fromToken.mStartingData;
- startingSurface = fromToken.startingSurface;
- startingDisplayed = fromToken.startingDisplayed;
- fromToken.startingDisplayed = false;
- startingWindow = tStartingWindow;
- reportedVisible = fromToken.reportedVisible;
- fromToken.mStartingData = null;
- fromToken.startingSurface = null;
- fromToken.startingWindow = null;
- fromToken.startingMoved = true;
- tStartingWindow.mToken = this;
- tStartingWindow.mAppToken = this;
-
- if (DEBUG_ADD_REMOVE || DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
- "Removing starting " + tStartingWindow + " from " + fromToken);
- fromToken.removeChild(tStartingWindow);
- fromToken.postWindowRemoveStartingWindowCleanup(tStartingWindow);
- fromToken.mHiddenSetFromTransferredStartingWindow = false;
- addWindow(tStartingWindow);
-
- // Propagate other interesting state between the tokens. If the old token is displayed,
- // we should immediately force the new one to be displayed. If it is animating, we need
- // to move that animation to the new one.
- if (fromToken.allDrawn) {
- allDrawn = true;
- deferClearAllDrawn = fromToken.deferClearAllDrawn;
- }
- if (fromToken.firstWindowDrawn) {
- firstWindowDrawn = true;
- }
- if (!fromToken.isHidden()) {
- setHidden(false);
- hiddenRequested = false;
- mHiddenSetFromTransferredStartingWindow = true;
- }
- setClientHidden(fromToken.mClientHidden);
-
- transferAnimation(fromToken);
-
- // When transferring an animation, we no longer need to apply an animation to the
- // the token we transfer the animation over. Thus, set this flag to indicate we've
- // transferred the animation.
- mUseTransferredAnimation = true;
-
- mWmService.updateFocusedWindowLocked(
- UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/);
- getDisplayContent().setLayoutNeeded();
- mWmService.mWindowPlacerLocked.performSurfacePlacement();
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- return true;
- } else if (fromToken.mStartingData != null) {
- // The previous app was getting ready to show a
- // starting window, but hasn't yet done so. Steal it!
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
- "Moving pending starting from " + fromToken + " to " + this);
- mStartingData = fromToken.mStartingData;
- fromToken.mStartingData = null;
- fromToken.startingMoved = true;
- scheduleAddStartingWindow();
- return true;
- }
-
- // TODO: Transfer thumbnail
-
- return false;
- }
-
- boolean isLastWindow(WindowState win) {
- return mChildren.size() == 1 && mChildren.get(0) == win;
- }
-
- @Override
- void onAppTransitionDone() {
- sendingToBottom = false;
- }
-
- /**
- * We override because this class doesn't want its children affecting its reported orientation
- * in anyway.
- */
- @Override
- int getOrientation(int candidate) {
- if (candidate == SCREEN_ORIENTATION_BEHIND) {
- // Allow app to specify orientation regardless of its visibility state if the current
- // candidate want us to use orientation behind. I.e. the visible app on-top of this one
- // wants us to use the orientation of the app behind it.
- return mOrientation;
- }
-
- // The {@link AppWindowToken} should only specify an orientation when it is not closing or
- // going to the bottom. Allowing closing {@link AppWindowToken} to participate can lead to
- // an Activity in another task being started in the wrong orientation during the transition.
- if (!(sendingToBottom || getDisplayContent().mClosingApps.contains(this))
- && (isVisible() || getDisplayContent().mOpeningApps.contains(this))) {
- return mOrientation;
- }
-
- return SCREEN_ORIENTATION_UNSET;
- }
-
- /** Returns the app's preferred orientation regardless of its currently visibility state. */
- int getOrientationIgnoreVisibility() {
- return mOrientation;
- }
-
- /** @return {@code true} if the compatibility bounds is taking effect. */
- boolean inSizeCompatMode() {
- return mSizeCompatBounds != null;
- }
-
- @Override
- float getSizeCompatScale() {
- return inSizeCompatMode() ? mSizeCompatScale : super.getSizeCompatScale();
- }
-
- /**
- * @return Non-empty bounds if the activity has override bounds.
- * @see ActivityRecord#resolveOverrideConfiguration(Configuration)
- */
- Rect getResolvedOverrideBounds() {
- // Get bounds from resolved override configuration because it is computed with orientation.
- return getResolvedOverrideConfiguration().windowConfiguration.getBounds();
- }
-
- @Override
- public void onConfigurationChanged(Configuration newParentConfig) {
- final int prevWinMode = getWindowingMode();
- mTmpPrevBounds.set(getBounds());
- super.onConfigurationChanged(newParentConfig);
-
- final Task task = getTask();
- final Rect overrideBounds = getResolvedOverrideBounds();
- if (task != null && !overrideBounds.isEmpty()
- // If the changes come from change-listener, the incoming parent configuration is
- // still the old one. Make sure their orientations are the same to reduce computing
- // the compatibility bounds for the intermediate state.
- && (task.mTaskRecord == null || task.mTaskRecord
- .getConfiguration().orientation == newParentConfig.orientation)) {
- final Rect taskBounds = task.getBounds();
- // Since we only center the activity horizontally, if only the fixed height is smaller
- // than its container, the override bounds don't need to take effect.
- if ((overrideBounds.width() != taskBounds.width()
- || overrideBounds.height() > taskBounds.height())) {
- calculateCompatBoundsTransformation(newParentConfig);
- updateSurfacePosition();
- } else if (mSizeCompatBounds != null) {
- mSizeCompatBounds = null;
- mSizeCompatScale = 1f;
- updateSurfacePosition();
- }
- }
-
- final int winMode = getWindowingMode();
-
- if (prevWinMode == winMode) {
- return;
- }
-
- if (prevWinMode == WINDOWING_MODE_PINNED && winMode != WINDOWING_MODE_UNDEFINED
- && !isHidden()) {
- // Leaving PiP to fullscreen, save the snap fraction based on the pre-animation bounds
- // for the next re-entry into PiP (assuming the activity is not hidden or destroyed)
- final TaskStack pinnedStack = mDisplayContent.getPinnedStack();
- if (pinnedStack != null) {
- final Rect stackBounds;
- if (pinnedStack.lastAnimatingBoundsWasToFullscreen()) {
- // We are animating the bounds, use the pre-animation bounds to save the snap
- // fraction
- stackBounds = pinnedStack.mPreAnimationBounds;
- } else {
- // We skip the animation if the fullscreen configuration is not compatible, so
- // use the current bounds to calculate the saved snap fraction instead
- // (see PinnedActivityStack.skipResizeAnimation())
- stackBounds = mTmpRect;
- pinnedStack.getBounds(stackBounds);
- }
- mDisplayContent.mPinnedStackControllerLocked.saveReentrySnapFraction(
- mActivityComponent, stackBounds);
- }
- } else if (shouldStartChangeTransition(prevWinMode, winMode)) {
- initializeChangeTransition(mTmpPrevBounds);
- }
- }
-
- private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
- if (mWmService.mDisableTransitionAnimation
- || !isVisible()
- || getDisplayContent().mAppTransition.isTransitionSet()
- || getSurfaceControl() == null) {
- return false;
- }
- // Only do an animation into and out-of freeform mode for now. Other mode
- // transition animations are currently handled by system-ui.
- return (prevWinMode == WINDOWING_MODE_FREEFORM) != (newWinMode == WINDOWING_MODE_FREEFORM);
- }
-
- /**
- * Initializes a change transition. Because the app is visible already, there is a small period
- * of time where the user can see the app content/window update before the transition starts.
- * To prevent this, we immediately take a snapshot and place the app/snapshot into a leash which
- * "freezes" the location/crop until the transition starts.
- * <p>
- * Here's a walk-through of the process:
- * 1. Create a temporary leash ("interim-change-leash") and reparent the app to it.
- * 2. Set the temporary leash's position/crop to the current state.
- * 3. Create a snapshot and place that at the top of the leash to cover up content changes.
- * 4. Once the transition is ready, it will reparent the app to the animation leash.
- * 5. Detach the interim-change-leash.
- */
- private void initializeChangeTransition(Rect startBounds) {
- mDisplayContent.prepareAppTransition(TRANSIT_TASK_CHANGE_WINDOWING_MODE,
- false /* alwaysKeepCurrent */, 0, false /* forceOverride */);
- mDisplayContent.mChangingApps.add(this);
- mTransitStartRect.set(startBounds);
-
- final SurfaceControl.Builder builder = makeAnimationLeash()
- .setParent(getAnimationLeashParent())
- .setName(getSurfaceControl() + " - interim-change-leash");
- mTransitChangeLeash = builder.build();
- Transaction t = getPendingTransaction();
- t.setWindowCrop(mTransitChangeLeash, startBounds.width(), startBounds.height());
- t.setPosition(mTransitChangeLeash, startBounds.left, startBounds.top);
- t.show(mTransitChangeLeash);
- t.reparent(getSurfaceControl(), mTransitChangeLeash);
- onAnimationLeashCreated(t, mTransitChangeLeash);
-
- // Skip creating snapshot if this transition is controlled by a remote animator which
- // doesn't need it.
- ArraySet<Integer> activityTypes = new ArraySet<>();
- activityTypes.add(getActivityType());
- RemoteAnimationAdapter adapter =
- mDisplayContent.mAppTransitionController.getRemoteAnimationOverride(
- this, TRANSIT_TASK_CHANGE_WINDOWING_MODE, activityTypes);
- if (adapter != null && !adapter.getChangeNeedsSnapshot()) {
- return;
- }
-
- Task task = getTask();
- if (mThumbnail == null && task != null && !hasCommittedReparentToAnimationLeash()) {
- SurfaceControl.ScreenshotGraphicBuffer snapshot =
- mWmService.mTaskSnapshotController.createTaskSnapshot(
- task, 1 /* scaleFraction */);
- if (snapshot != null) {
- mThumbnail = new AppWindowThumbnail(mWmService.mSurfaceFactory, t, this,
- snapshot.getGraphicBuffer(), true /* relative */);
- }
- }
- }
-
- boolean isInChangeTransition() {
- return mTransitChangeLeash != null || AppTransition.isChangeTransit(mTransit);
- }
-
- @VisibleForTesting
- AppWindowThumbnail getThumbnail() {
- return mThumbnail;
- }
-
- /**
- * Calculates the scale and offset to horizontal center the size compatibility bounds into the
- * region which is available to application.
- */
- private void calculateCompatBoundsTransformation(Configuration newParentConfig) {
- final Rect parentAppBounds = newParentConfig.windowConfiguration.getAppBounds();
- final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
- final Rect viewportBounds = parentAppBounds != null ? parentAppBounds : parentBounds;
- final Rect appBounds = getWindowConfiguration().getAppBounds();
- final Rect contentBounds = appBounds != null ? appBounds : getResolvedOverrideBounds();
- final float contentW = contentBounds.width();
- final float contentH = contentBounds.height();
- final float viewportW = viewportBounds.width();
- final float viewportH = viewportBounds.height();
- // Only allow to scale down.
- mSizeCompatScale = (contentW <= viewportW && contentH <= viewportH)
- ? 1 : Math.min(viewportW / contentW, viewportH / contentH);
- final int offsetX = (int) ((viewportW - contentW * mSizeCompatScale + 1) * 0.5f)
- + viewportBounds.left;
-
- if (mSizeCompatBounds == null) {
- mSizeCompatBounds = new Rect();
- }
- mSizeCompatBounds.set(contentBounds);
- mSizeCompatBounds.offsetTo(0, 0);
- mSizeCompatBounds.scale(mSizeCompatScale);
- // Ensure to align the top with the parent.
- mSizeCompatBounds.top = parentBounds.top;
- // The decor inset is included in height.
- mSizeCompatBounds.bottom += viewportBounds.top;
- mSizeCompatBounds.left += offsetX;
- mSizeCompatBounds.right += offsetX;
- }
-
- @Override
- public Rect getBounds() {
- if (mSizeCompatBounds != null) {
- return mSizeCompatBounds;
- }
- return super.getBounds();
- }
-
- @Override
- public boolean matchParentBounds() {
- if (super.matchParentBounds()) {
- return true;
- }
- // An activity in size compatibility mode may have override bounds which equals to its
- // parent bounds, so the exact bounds should also be checked.
- final WindowContainer parent = getParent();
- return parent == null || parent.getBounds().equals(getResolvedOverrideBounds());
- }
-
- @Override
- void checkAppWindowsReadyToShow() {
- if (allDrawn == mLastAllDrawn) {
- return;
- }
-
- mLastAllDrawn = allDrawn;
- if (!allDrawn) {
- return;
- }
-
- // The token has now changed state to having all windows shown... what to do, what to do?
- if (mFreezingScreen) {
- showAllWindowsLocked();
- stopFreezingScreen(false, true);
- if (DEBUG_ORIENTATION) Slog.i(TAG,
- "Setting mOrientationChangeComplete=true because wtoken " + this
- + " numInteresting=" + mNumInterestingWindows + " numDrawn=" + mNumDrawnWindows);
- // This will set mOrientationChangeComplete and cause a pass through layout.
- setAppLayoutChanges(FINISH_LAYOUT_REDO_WALLPAPER,
- "checkAppWindowsReadyToShow: freezingScreen");
- } else {
- setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM, "checkAppWindowsReadyToShow");
-
- // We can now show all of the drawn windows!
- if (!getDisplayContent().mOpeningApps.contains(this) && canShowWindows()) {
- showAllWindowsLocked();
- }
- }
- }
-
- /**
- * Returns whether the drawn window states of this {@link AppWindowToken} has considered every
- * child {@link WindowState}. A child is considered if it has been passed into
- * {@link #updateDrawnWindowStates(WindowState)} after being added. This is used to determine
- * whether states, such as {@code allDrawn}, can be set, which relies on state variables such as
- * {@code mNumInterestingWindows}, which depend on all {@link WindowState}s being considered.
- *
- * @return {@code true} If all children have been considered, {@code false}.
- */
- private boolean allDrawnStatesConsidered() {
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final WindowState child = mChildren.get(i);
- if (child.mightAffectAllDrawn() && !child.getDrawnStateEvaluated()) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Determines if the token has finished drawing. This should only be called from
- * {@link DisplayContent#applySurfaceChangesTransaction}
- */
- void updateAllDrawn() {
- if (!allDrawn) {
- // Number of drawn windows can be less when a window is being relaunched, wait for
- // all windows to be launched and drawn for this token be considered all drawn.
- final int numInteresting = mNumInterestingWindows;
-
- // We must make sure that all present children have been considered (determined by
- // {@link #allDrawnStatesConsidered}) before evaluating whether everything has been
- // drawn.
- if (numInteresting > 0 && allDrawnStatesConsidered()
- && mNumDrawnWindows >= numInteresting && !isRelaunching()) {
- if (DEBUG_VISIBILITY) Slog.v(TAG, "allDrawn: " + this
- + " interesting=" + numInteresting + " drawn=" + mNumDrawnWindows);
- allDrawn = true;
- // Force an additional layout pass where
- // WindowStateAnimator#commitFinishDrawingLocked() will call performShowLocked().
- if (mDisplayContent != null) {
- mDisplayContent.setLayoutNeeded();
- }
- mWmService.mH.obtainMessage(NOTIFY_ACTIVITY_DRAWN, token).sendToTarget();
-
- // Notify the pinned stack upon all windows drawn. If there was an animation in
- // progress then this signal will resume that animation.
- final TaskStack pinnedStack = mDisplayContent.getPinnedStack();
- if (pinnedStack != null) {
- pinnedStack.onAllWindowsDrawn();
- }
- }
- }
- }
-
- boolean keyDispatchingTimedOut(String reason, int windowPid) {
- return mActivityRecord != null && mActivityRecord.keyDispatchingTimedOut(reason, windowPid);
- }
-
- /**
- * Updated this app token tracking states for interesting and drawn windows based on the window.
- *
- * @return Returns true if the input window is considered interesting and drawn while all the
- * windows in this app token where not considered drawn as of the last pass.
- */
- boolean updateDrawnWindowStates(WindowState w) {
- w.setDrawnStateEvaluated(true /*evaluated*/);
-
- if (DEBUG_STARTING_WINDOW_VERBOSE && w == startingWindow) {
- Slog.d(TAG, "updateWindows: starting " + w + " isOnScreen=" + w.isOnScreen()
- + " allDrawn=" + allDrawn + " freezingScreen=" + mFreezingScreen);
- }
-
- if (allDrawn && !mFreezingScreen) {
- return false;
- }
-
- if (mLastTransactionSequence != mWmService.mTransactionSequence) {
- mLastTransactionSequence = mWmService.mTransactionSequence;
- mNumDrawnWindows = 0;
- startingDisplayed = false;
-
- // There is the main base application window, even if it is exiting, wait for it
- mNumInterestingWindows = findMainWindow(false /* includeStartingApp */) != null ? 1 : 0;
- }
-
- final WindowStateAnimator winAnimator = w.mWinAnimator;
-
- boolean isInterestingAndDrawn = false;
-
- if (!allDrawn && w.mightAffectAllDrawn()) {
- if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) {
- Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw()
- + ", isAnimationSet=" + isSelfAnimating());
- if (!w.isDrawnLw()) {
- Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceController
- + " pv=" + w.isVisibleByPolicy()
- + " mDrawState=" + winAnimator.drawStateToString()
- + " ph=" + w.isParentWindowHidden() + " th=" + hiddenRequested
- + " a=" + isSelfAnimating());
- }
- }
-
- if (w != startingWindow) {
- if (w.isInteresting()) {
- // Add non-main window as interesting since the main app has already been added
- if (findMainWindow(false /* includeStartingApp */) != w) {
- mNumInterestingWindows++;
- }
- if (w.isDrawnLw()) {
- mNumDrawnWindows++;
-
- if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) Slog.v(TAG, "tokenMayBeDrawn: "
- + this + " w=" + w + " numInteresting=" + mNumInterestingWindows
- + " freezingScreen=" + mFreezingScreen
- + " mAppFreezing=" + w.mAppFreezing);
-
- isInterestingAndDrawn = true;
- }
- }
- } else if (w.isDrawnLw()) {
- if (mActivityRecord != null) {
- mActivityRecord.onStartingWindowDrawn(SystemClock.uptimeMillis());
- }
- startingDisplayed = true;
- }
- }
-
- return isInterestingAndDrawn;
- }
-
- void layoutLetterbox(WindowState winHint) {
- final WindowState w = findMainWindow();
- if (w == null || winHint != null && w != winHint) {
- return;
- }
- final boolean surfaceReady = w.isDrawnLw() // Regular case
- || w.mWinAnimator.mSurfaceDestroyDeferred // The preserved surface is still ready.
- || w.isDragResizeChanged(); // Waiting for relayoutWindow to call preserveSurface.
- final boolean needsLetterbox = surfaceReady && w.isLetterboxedAppWindow() && fillsParent();
- if (needsLetterbox) {
- if (mLetterbox == null) {
- mLetterbox = new Letterbox(() -> makeChildSurface(null),
- mWmService.mTransactionFactory);
- mLetterbox.attachInput(w);
- }
- getPosition(mTmpPoint);
- // Get the bounds of the "space-to-fill". In multi-window mode, the task-level
- // represents this. In fullscreen-mode, the stack does (since the orientation letterbox
- // is also applied to the task).
- Rect spaceToFill = (inMultiWindowMode() || getStack() == null)
- ? getTask().getDisplayedBounds() : getStack().getDisplayedBounds();
- mLetterbox.layout(spaceToFill, w.getFrameLw(), mTmpPoint);
- } else if (mLetterbox != null) {
- mLetterbox.hide();
- }
- }
-
- void updateLetterboxSurface(WindowState winHint) {
- final WindowState w = findMainWindow();
- if (w != winHint && winHint != null && w != null) {
- return;
- }
- layoutLetterbox(winHint);
- if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
- mLetterbox.applySurfaceChanges(getPendingTransaction());
- }
- }
-
- @Override
- boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
- // For legacy reasons we process the TaskStack.mExitingAppTokens first in DisplayContent
- // before the non-exiting app tokens. So, we skip the exiting app tokens here.
- // TODO: Investigate if we need to continue to do this or if we can just process them
- // in-order.
- if (mIsExiting && !waitingForReplacement()) {
- return false;
- }
- return forAllWindowsUnchecked(callback, traverseTopToBottom);
- }
-
- @Override
- void forAllAppWindows(Consumer<AppWindowToken> callback) {
- callback.accept(this);
- }
-
- boolean forAllWindowsUnchecked(ToBooleanFunction<WindowState> callback,
- boolean traverseTopToBottom) {
- return super.forAllWindows(callback, traverseTopToBottom);
- }
-
- @Override
- AppWindowToken asAppWindowToken() {
- // I am an app window token!
- return this;
- }
-
- boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
- CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
- IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
- boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
- // If the display is frozen, we won't do anything until the actual window is
- // displayed so there is no reason to put in the starting window.
- if (!okToDisplay()) {
- return false;
- }
-
- if (mStartingData != null) {
- return false;
- }
-
- final WindowState mainWin = findMainWindow();
- if (mainWin != null && mainWin.mWinAnimator.getShown()) {
- // App already has a visible window...why would you want a starting window?
- return false;
- }
-
- final ActivityManager.TaskSnapshot snapshot =
- mWmService.mTaskSnapshotController.getSnapshot(
- getTask().mTaskId, getTask().mUserId,
- false /* restoreFromDisk */, false /* reducedResolution */);
- final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
- allowTaskSnapshot, activityCreated, fromRecents, snapshot);
-
- if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
- return createSnapshot(snapshot);
- }
-
- // If this is a translucent window, then don't show a starting window -- the current
- // effect (a full-screen opaque starting window that fades away to the real contents
- // when it is ready) does not work for this.
- if (DEBUG_STARTING_WINDOW) {
- Slog.v(TAG, "Checking theme of starting window: 0x" + Integer.toHexString(theme));
- }
- if (theme != 0) {
- AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
- com.android.internal.R.styleable.Window,
- mWmService.mCurrentUserId);
- if (ent == null) {
- // Whoops! App doesn't exist. Um. Okay. We'll just pretend like we didn't
- // see that.
- return false;
- }
- final boolean windowIsTranslucent = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowIsTranslucent, false);
- final boolean windowIsFloating = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowIsFloating, false);
- final boolean windowShowWallpaper = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowShowWallpaper, false);
- final boolean windowDisableStarting = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowDisablePreview, false);
- if (DEBUG_STARTING_WINDOW) {
- Slog.v(TAG, "Translucent=" + windowIsTranslucent
- + " Floating=" + windowIsFloating
- + " ShowWallpaper=" + windowShowWallpaper);
- }
- if (windowIsTranslucent) {
- return false;
- }
- if (windowIsFloating || windowDisableStarting) {
- return false;
- }
- if (windowShowWallpaper) {
- if (getDisplayContent().mWallpaperController
- .getWallpaperTarget() == null) {
- // If this theme is requesting a wallpaper, and the wallpaper
- // is not currently visible, then this effectively serves as
- // an opaque window and our starting window transition animation
- // can still work. We just need to make sure the starting window
- // is also showing the wallpaper.
- windowFlags |= FLAG_SHOW_WALLPAPER;
- } else {
- return false;
- }
- }
- }
-
- if (transferStartingWindow(transferFrom)) {
- return true;
- }
-
- // There is no existing starting window, and we don't want to create a splash screen, so
- // that's it!
- if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
- return false;
- }
-
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData");
- mStartingData = new SplashScreenStartingData(mWmService, pkg,
- theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
- getMergedOverrideConfiguration());
- scheduleAddStartingWindow();
- return true;
- }
-
-
- private boolean createSnapshot(ActivityManager.TaskSnapshot snapshot) {
- if (snapshot == null) {
- return false;
- }
-
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SnapshotStartingData");
- mStartingData = new SnapshotStartingData(mWmService, snapshot);
- scheduleAddStartingWindow();
- return true;
- }
-
- void scheduleAddStartingWindow() {
- // Note: we really want to do sendMessageAtFrontOfQueue() because we
- // want to process the message ASAP, before any other queued
- // messages.
- if (!mWmService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Enqueueing ADD_STARTING");
- mWmService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
- }
- }
-
- private final Runnable mAddStartingWindow = new Runnable() {
-
- @Override
- public void run() {
- // Can be accessed without holding the global lock
- final StartingData startingData;
- synchronized (mWmService.mGlobalLock) {
- // There can only be one adding request, silly caller!
- mWmService.mAnimationHandler.removeCallbacks(this);
-
- if (mStartingData == null) {
- // Animation has been canceled... do nothing.
- if (DEBUG_STARTING_WINDOW) {
- Slog.v(TAG, "startingData was nulled out before handling"
- + " mAddStartingWindow: " + AppWindowToken.this);
- }
- return;
- }
- startingData = mStartingData;
- }
-
- if (DEBUG_STARTING_WINDOW) {
- Slog.v(TAG, "Add starting " + this + ": startingData=" + startingData);
- }
-
- WindowManagerPolicy.StartingSurface surface = null;
- try {
- surface = startingData.createStartingSurface(AppWindowToken.this);
- } catch (Exception e) {
- Slog.w(TAG, "Exception when adding starting window", e);
- }
- if (surface != null) {
- boolean abort = false;
- synchronized (mWmService.mGlobalLock) {
- // If the window was successfully added, then
- // we need to remove it.
- if (removed || mStartingData == null) {
- if (DEBUG_STARTING_WINDOW) {
- Slog.v(TAG, "Aborted starting " + AppWindowToken.this
- + ": removed=" + removed + " startingData=" + mStartingData);
- }
- startingWindow = null;
- mStartingData = null;
- abort = true;
- } else {
- startingSurface = surface;
- }
- if (DEBUG_STARTING_WINDOW && !abort) {
- Slog.v(TAG,
- "Added starting " + AppWindowToken.this + ": startingWindow="
- + startingWindow + " startingView=" + startingSurface);
- }
- }
- if (abort) {
- surface.remove();
- }
- } else if (DEBUG_STARTING_WINDOW) {
- Slog.v(TAG, "Surface returned was null: " + AppWindowToken.this);
- }
- }
- };
-
- private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
- boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents,
- ActivityManager.TaskSnapshot snapshot) {
- if (getDisplayContent().mAppTransition.getAppTransition()
- == TRANSIT_DOCK_TASK_FROM_RECENTS) {
- // TODO(b/34099271): Remove this statement to add back the starting window and figure
- // out why it causes flickering, the starting window appears over the thumbnail while
- // the docked from recents transition occurs
- return STARTING_WINDOW_TYPE_NONE;
- } else if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
- return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
- } else if (taskSwitch && allowTaskSnapshot) {
- if (mWmService.mLowRamTaskSnapshotsAndRecents) {
- // For low RAM devices, we use the splash screen starting window instead of the
- // task snapshot starting window.
- return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
- }
- return snapshot == null ? STARTING_WINDOW_TYPE_NONE
- : snapshotOrientationSameAsTask(snapshot) || fromRecents
- ? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
- } else {
- return STARTING_WINDOW_TYPE_NONE;
- }
- }
-
-
- private boolean snapshotOrientationSameAsTask(ActivityManager.TaskSnapshot snapshot) {
- if (snapshot == null) {
- return false;
- }
- return getTask().getConfiguration().orientation == snapshot.getOrientation();
- }
-
- void removeStartingWindow() {
- if (startingWindow == null) {
- if (mStartingData != null) {
- // Starting window has not been added yet, but it is scheduled to be added.
- // Go ahead and cancel the request.
- if (DEBUG_STARTING_WINDOW) {
- Slog.v(TAG_WM, "Clearing startingData for token=" + this);
- }
- mStartingData = null;
- }
- return;
- }
-
- final WindowManagerPolicy.StartingSurface surface;
- if (mStartingData != null) {
- surface = startingSurface;
- mStartingData = null;
- startingSurface = null;
- startingWindow = null;
- startingDisplayed = false;
- if (surface == null) {
- if (DEBUG_STARTING_WINDOW) {
- Slog.v(TAG_WM, "startingWindow was set but startingSurface==null, couldn't "
- + "remove");
- }
- return;
- }
- } else {
- if (DEBUG_STARTING_WINDOW) {
- Slog.v(TAG_WM, "Tried to remove starting window but startingWindow was null:"
- + this);
- }
- return;
- }
-
- if (DEBUG_STARTING_WINDOW) {
- Slog.v(TAG_WM, "Schedule remove starting " + this
- + " startingWindow=" + startingWindow
- + " startingView=" + startingSurface
- + " Callers=" + Debug.getCallers(5));
- }
-
- // Use the same thread to remove the window as we used to add it, as otherwise we end up
- // with things in the view hierarchy being called from different threads.
- mWmService.mAnimationHandler.post(() -> {
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Removing startingView=" + surface);
- try {
- surface.remove();
- } catch (Exception e) {
- Slog.w(TAG_WM, "Exception when removing starting window", e);
- }
- });
- }
-
- @Override
- boolean fillsParent() {
- return occludesParent();
- }
-
- /** Returns true if this activity is opaque and fills the entire space of this task. */
- boolean occludesParent() {
- return mOccludesParent;
- }
-
- boolean setOccludesParent(boolean occludesParent) {
- final boolean changed = occludesParent != mOccludesParent;
- mOccludesParent = occludesParent;
- setMainWindowOpaque(occludesParent);
- mWmService.mWindowPlacerLocked.requestTraversal();
- return changed;
- }
-
- void setMainWindowOpaque(boolean isOpaque) {
- final WindowState win = findMainWindow();
- if (win == null) {
- return;
- }
- isOpaque = isOpaque & !PixelFormat.formatHasAlpha(win.getAttrs().format);
- win.mWinAnimator.setOpaqueLocked(isOpaque);
- }
-
- boolean containsDismissKeyguardWindow() {
- // Window state is transient during relaunch. We are not guaranteed to be frozen during the
- // entirety of the relaunch.
- if (isRelaunching()) {
- return mLastContainsDismissKeyguardWindow;
- }
-
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- if ((mChildren.get(i).mAttrs.flags & FLAG_DISMISS_KEYGUARD) != 0) {
- return true;
- }
- }
- return false;
- }
-
- boolean containsShowWhenLockedWindow() {
- // When we are relaunching, it is possible for us to be unfrozen before our previous
- // windows have been added back. Using the cached value ensures that our previous
- // showWhenLocked preference is honored until relaunching is complete.
- if (isRelaunching()) {
- return mLastContainsShowWhenLockedWindow;
- }
-
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- if ((mChildren.get(i).mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0) {
- return true;
- }
- }
-
- return false;
- }
-
- void checkKeyguardFlagsChanged() {
- final boolean containsDismissKeyguard = containsDismissKeyguardWindow();
- final boolean containsShowWhenLocked = containsShowWhenLockedWindow();
- if (containsDismissKeyguard != mLastContainsDismissKeyguardWindow
- || containsShowWhenLocked != mLastContainsShowWhenLockedWindow) {
- mWmService.notifyKeyguardFlagsChanged(null /* callback */,
- getDisplayContent().getDisplayId());
- }
- mLastContainsDismissKeyguardWindow = containsDismissKeyguard;
- mLastContainsShowWhenLockedWindow = containsShowWhenLocked;
- }
-
- 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 null;
- }
-
- WindowState getHighestAnimLayerWindow(WindowState currentTarget) {
- WindowState candidate = null;
- for (int i = mChildren.indexOf(currentTarget); i >= 0; i--) {
- final WindowState w = mChildren.get(i);
- if (w.mRemoved) {
- continue;
- }
- if (candidate == null) {
- candidate = w;
- }
- }
- return candidate;
- }
-
- /**
- * See {@link Activity#setDisablePreviewScreenshots}.
- */
- void setDisablePreviewScreenshots(boolean disable) {
- mDisablePreviewScreenshots = disable;
- }
-
- /**
- * Sets whether the current launch can turn the screen on.
- * @see #currentLaunchCanTurnScreenOn()
- */
- void setCurrentLaunchCanTurnScreenOn(boolean currentLaunchCanTurnScreenOn) {
- mCurrentLaunchCanTurnScreenOn = currentLaunchCanTurnScreenOn;
- }
-
- /**
- * Indicates whether the current launch can turn the screen on. This is to prevent multiple
- * relayouts from turning the screen back on. The screen should only turn on at most
- * once per activity resume.
- * <p>
- * Note this flag is only meaningful when {@link WindowManager.LayoutParams#FLAG_TURN_SCREEN_ON}
- * or {@link ActivityRecord#canTurnScreenOn} is set.
- *
- * @return {@code true} if the activity is ready to turn on the screen.
- */
- boolean currentLaunchCanTurnScreenOn() {
- return mCurrentLaunchCanTurnScreenOn;
- }
-
- /**
- * Retrieves whether we'd like to generate a snapshot that's based solely on the theme. This is
- * the case when preview screenshots are disabled {@link #setDisablePreviewScreenshots} or when
- * we can't take a snapshot for other reasons, for example, if we have a secure window.
- *
- * @return True if we need to generate an app theme snapshot, false if we'd like to take a real
- * screenshot.
- */
- boolean shouldUseAppThemeSnapshot() {
- return mDisablePreviewScreenshots || forAllWindows(w -> (w.mAttrs.flags & FLAG_SECURE) != 0,
- true /* topToBottom */);
- }
-
- SurfaceControl getAppAnimationLayer() {
- return getAppAnimationLayer(isActivityTypeHome() ? ANIMATION_LAYER_HOME
- : needsZBoost() ? ANIMATION_LAYER_BOOSTED
- : ANIMATION_LAYER_STANDARD);
- }
-
- @Override
- public SurfaceControl getAnimationLeashParent() {
- // For transitions in the pinned stack (menu activity) we just let them occur as a child
- // of the pinned stack.
- // All normal app transitions take place in an animation layer which is below the pinned
- // stack but may be above the parent stacks of the given animating apps by default. When
- // a new hierarchical animation is enabled, we just let them occur as a child of the parent
- // stack, i.e. the hierarchy of the surfaces is unchanged.
- if (inPinnedWindowingMode()) {
- return getStack().getSurfaceControl();
- } else if (WindowManagerService.sHierarchicalAnimations) {
- return super.getAnimationLeashParent();
- } else {
- return getAppAnimationLayer();
- }
- }
-
-
- @VisibleForTesting
- boolean shouldAnimate(int transit) {
- final boolean isSplitScreenPrimary =
- getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
- final boolean allowSplitScreenPrimaryAnimation = transit != TRANSIT_WALLPAPER_OPEN;
-
- // Don't animate while the task runs recents animation but only if we are in the mode
- // where we cancel with deferred screenshot, which means that the controller has
- // transformed the task.
- final RecentsAnimationController controller = mWmService.getRecentsAnimationController();
- if (controller != null && controller.isAnimatingTask(getTask())
- && controller.shouldDeferCancelUntilNextTransition()) {
- return false;
- }
-
- // We animate always if it's not split screen primary, and only some special cases in split
- // screen primary because it causes issues with stack clipping when we run an un-minimize
- // animation at the same time.
- return !isSplitScreenPrimary || allowSplitScreenPrimaryAnimation;
- }
-
- /**
- * Creates a layer to apply crop to an animation.
- */
- private SurfaceControl createAnimationBoundsLayer(Transaction t) {
- if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.i(TAG, "Creating animation bounds layer");
- final SurfaceControl.Builder builder = makeAnimationLeash()
- .setParent(getAnimationLeashParent())
- .setName(getSurfaceControl() + " - animation-bounds");
- final SurfaceControl boundsLayer = builder.build();
- t.show(boundsLayer);
- return boundsLayer;
- }
-
- @Override
- Rect getDisplayedBounds() {
- final Task task = getTask();
- if (task != null) {
- final Rect overrideDisplayedBounds = task.getOverrideDisplayedBounds();
- if (!overrideDisplayedBounds.isEmpty()) {
- return overrideDisplayedBounds;
- }
- }
- return getBounds();
- }
-
- @VisibleForTesting
- Rect getAnimationBounds(int appStackClipMode) {
- if (appStackClipMode == STACK_CLIP_BEFORE_ANIM && getStack() != null) {
- // Using the stack bounds here effectively applies the clipping before animation.
- return getStack().getBounds();
- }
- // Use task-bounds if available so that activity-level letterbox (maxAspectRatio) is
- // included in the animation.
- return getTask() != null ? getTask().getBounds() : getBounds();
- }
-
- boolean applyAnimationLocked(WindowManager.LayoutParams lp, int transit, boolean enter,
- boolean isVoiceInteraction) {
-
- if (mWmService.mDisableTransitionAnimation || !shouldAnimate(transit)) {
- if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
- Slog.v(TAG_WM, "applyAnimation: transition animation is disabled or skipped."
- + " atoken=" + this);
- }
- cancelAnimation();
- return false;
- }
-
- // Only apply an animation if the display isn't frozen. If it is frozen, there is no reason
- // to animate and it can cause strange artifacts when we unfreeze the display if some
- // different animation is running.
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AWT#applyAnimationLocked");
- if (okToAnimate()) {
- final AnimationAdapter adapter;
- AnimationAdapter thumbnailAdapter = null;
-
- final int appStackClipMode =
- getDisplayContent().mAppTransition.getAppStackClipMode();
-
- // Separate position and size for use in animators.
- mTmpRect.set(getAnimationBounds(appStackClipMode));
- mTmpPoint.set(mTmpRect.left, mTmpRect.top);
- mTmpRect.offsetTo(0, 0);
-
- final boolean isChanging = AppTransition.isChangeTransit(transit) && enter
- && getDisplayContent().mChangingApps.contains(this);
-
- // Delaying animation start isn't compatible with remote animations at all.
- if (getDisplayContent().mAppTransition.getRemoteAnimationController() != null
- && !mSurfaceAnimator.isAnimationStartDelayed()) {
- RemoteAnimationRecord adapters =
- getDisplayContent().mAppTransition.getRemoteAnimationController()
- .createRemoteAnimationRecord(this, mTmpPoint, mTmpRect,
- (isChanging ? mTransitStartRect : null));
- adapter = adapters.mAdapter;
- thumbnailAdapter = adapters.mThumbnailAdapter;
- } else if (isChanging) {
- final float durationScale = mWmService.getTransitionAnimationScaleLocked();
- mTmpRect.offsetTo(mTmpPoint.x, mTmpPoint.y);
- adapter = new LocalAnimationAdapter(
- new WindowChangeAnimationSpec(mTransitStartRect, mTmpRect,
- getDisplayContent().getDisplayInfo(), durationScale,
- true /* isAppAnimation */, false /* isThumbnail */),
- mWmService.mSurfaceAnimationRunner);
- if (mThumbnail != null) {
- thumbnailAdapter = new LocalAnimationAdapter(
- new WindowChangeAnimationSpec(mTransitStartRect, mTmpRect,
- getDisplayContent().getDisplayInfo(), durationScale,
- true /* isAppAnimation */, true /* isThumbnail */),
- mWmService.mSurfaceAnimationRunner);
- }
- mTransit = transit;
- mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();
- } else {
- mNeedsAnimationBoundsLayer = (appStackClipMode == STACK_CLIP_AFTER_ANIM);
-
- final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
- if (a != null) {
- // Only apply corner radius to animation if we're not in multi window mode.
- // We don't want rounded corners when in pip or split screen.
- final float windowCornerRadius = !inMultiWindowMode()
- ? getDisplayContent().getWindowCornerRadius()
- : 0;
- adapter = new LocalAnimationAdapter(
- new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
- getDisplayContent().mAppTransition.canSkipFirstFrame(),
- appStackClipMode,
- true /* isAppAnimation */,
- windowCornerRadius),
- mWmService.mSurfaceAnimationRunner);
- if (a.getZAdjustment() == Animation.ZORDER_TOP) {
- mNeedsZBoost = true;
- }
- mTransit = transit;
- mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();
- } else {
- adapter = null;
- }
- }
- if (adapter != null) {
- startAnimation(getPendingTransaction(), adapter, !isVisible());
- if (adapter.getShowWallpaper()) {
- mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
- }
- if (thumbnailAdapter != null) {
- mThumbnail.startAnimation(
- getPendingTransaction(), thumbnailAdapter, !isVisible());
- }
- }
- } else {
- cancelAnimation();
- }
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-
- return isReallyAnimating();
- }
-
- private Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
- boolean isVoiceInteraction) {
- final DisplayContent displayContent = getTask().getDisplayContent();
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- final int width = displayInfo.appWidth;
- final int height = displayInfo.appHeight;
- if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG_WM,
- "applyAnimation: atoken=" + this);
-
- // Determine the visible rect to calculate the thumbnail clip
- final WindowState win = findMainWindow();
- final Rect frame = new Rect(0, 0, width, height);
- final Rect displayFrame = new Rect(0, 0,
- displayInfo.logicalWidth, displayInfo.logicalHeight);
- final Rect insets = new Rect();
- final Rect stableInsets = new Rect();
- Rect surfaceInsets = null;
- final boolean freeform = win != null && win.inFreeformWindowingMode();
- if (win != null) {
- // Containing frame will usually cover the whole screen, including dialog windows.
- // For freeform workspace windows it will not cover the whole screen and it also
- // won't exactly match the final freeform window frame (e.g. when overlapping with
- // the status bar). In that case we need to use the final frame.
- if (freeform) {
- frame.set(win.getFrameLw());
- } else if (win.isLetterboxedAppWindow()) {
- frame.set(getTask().getBounds());
- } else if (win.isDockedResizing()) {
- // If we are animating while docked resizing, then use the stack bounds as the
- // animation target (which will be different than the task bounds)
- frame.set(getTask().getParent().getBounds());
- } else {
- frame.set(win.getContainingFrame());
- }
- surfaceInsets = win.getAttrs().surfaceInsets;
- // XXX(b/72757033): These are insets relative to the window frame, but we're really
- // interested in the insets relative to the frame we chose in the if-blocks above.
- win.getContentInsets(insets);
- win.getStableInsets(stableInsets);
- }
-
- if (mLaunchTaskBehind) {
- // Differentiate the two animations. This one which is briefly on the screen
- // gets the !enter animation, and the other activity which remains on the
- // screen gets the enter animation. Both appear in the mOpeningApps set.
- enter = false;
- }
- if (DEBUG_APP_TRANSITIONS) Slog.d(TAG_WM, "Loading animation for app transition."
- + " transit=" + AppTransition.appTransitionToString(transit) + " enter=" + enter
- + " frame=" + frame + " insets=" + insets + " surfaceInsets=" + surfaceInsets);
- final Configuration displayConfig = displayContent.getConfiguration();
- final Animation a = getDisplayContent().mAppTransition.loadAnimation(lp, transit, enter,
- displayConfig.uiMode, displayConfig.orientation, frame, displayFrame, insets,
- surfaceInsets, stableInsets, isVoiceInteraction, freeform, getTask().mTaskId);
- if (a != null) {
- if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this);
- final int containingWidth = frame.width();
- final int containingHeight = frame.height();
- a.initialize(containingWidth, containingHeight, width, height);
- a.scaleCurrentDuration(mWmService.getTransitionAnimationScaleLocked());
- }
- return a;
- }
-
- @Override
- public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
- return mAnimatingAppWindowTokenRegistry != null
- && mAnimatingAppWindowTokenRegistry.notifyAboutToFinish(
- this, endDeferFinishCallback);
- }
-
- @Override
- public void onAnimationLeashLost(Transaction t) {
- super.onAnimationLeashLost(t);
- if (mAnimationBoundsLayer != null) {
- t.remove(mAnimationBoundsLayer);
- mAnimationBoundsLayer = null;
- }
-
- if (mAnimatingAppWindowTokenRegistry != null) {
- mAnimatingAppWindowTokenRegistry.notifyFinished(this);
- }
- }
-
- @Override
- protected void setLayer(Transaction t, int layer) {
- if (!mSurfaceAnimator.hasLeash()) {
- t.setLayer(mSurfaceControl, layer);
- }
- }
-
- @Override
- protected void setRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) {
- if (!mSurfaceAnimator.hasLeash()) {
- t.setRelativeLayer(mSurfaceControl, relativeTo, layer);
- }
- }
-
- @Override
- protected void reparentSurfaceControl(Transaction t, SurfaceControl newParent) {
- if (!mSurfaceAnimator.hasLeash()) {
- t.reparent(mSurfaceControl, newParent);
- }
- }
-
- @Override
- public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
- // The leash is parented to the animation layer. We need to preserve the z-order by using
- // the prefix order index, but we boost if necessary.
- int layer = 0;
- if (!inPinnedWindowingMode()) {
- layer = getPrefixOrderIndex();
- } else {
- // Pinned stacks have animations take place within themselves rather than an animation
- // layer so we need to preserve the order relative to the stack (e.g. the order of our
- // task/parent).
- layer = getParent().getPrefixOrderIndex();
- }
-
- if (mNeedsZBoost) {
- layer += Z_BOOST_BASE;
- }
- if (!mNeedsAnimationBoundsLayer) {
- leash.setLayer(layer);
- }
-
- final DisplayContent dc = getDisplayContent();
- dc.assignStackOrdering();
-
- if (leash == mTransitChangeLeash) {
- // This is a temporary state so skip any animation notifications
- return;
- } else if (mTransitChangeLeash != null) {
- // unparent mTransitChangeLeash for clean-up
- clearChangeLeash(t, false /* cancel */);
- }
-
- if (mAnimatingAppWindowTokenRegistry != null) {
- mAnimatingAppWindowTokenRegistry.notifyStarting(this);
- }
-
- // If the animation needs to be cropped then an animation bounds layer is created as a child
- // of the pinned stack or animation layer. The leash is then reparented to this new layer.
- if (mNeedsAnimationBoundsLayer) {
- mTmpRect.setEmpty();
- final Task task = getTask();
- if (getDisplayContent().mAppTransitionController.isTransitWithinTask(
- getTransit(), task)) {
- task.getBounds(mTmpRect);
- } else {
- final TaskStack stack = getStack();
- if (stack == null) {
- return;
- }
- // Set clip rect to stack bounds.
- stack.getBounds(mTmpRect);
- }
- mAnimationBoundsLayer = createAnimationBoundsLayer(t);
-
- // Crop to stack bounds.
- t.setWindowCrop(mAnimationBoundsLayer, mTmpRect);
- t.setLayer(mAnimationBoundsLayer, layer);
-
- // Reparent leash to animation bounds layer.
- t.reparent(leash, mAnimationBoundsLayer);
- }
- }
-
- /**
- * This must be called while inside a transaction.
- */
- void showAllWindowsLocked() {
- forAllWindows(windowState -> {
- if (DEBUG_VISIBILITY) Slog.v(TAG, "performing show on: " + windowState);
- windowState.performShowLocked();
- }, false /* traverseTopToBottom */);
- }
-
- @Override
- protected void onAnimationFinished() {
- super.onAnimationFinished();
-
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AWT#onAnimationFinished");
- mTransit = TRANSIT_UNSET;
- mTransitFlags = 0;
- mNeedsZBoost = false;
- mNeedsAnimationBoundsLayer = false;
-
- setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM | FINISH_LAYOUT_REDO_WALLPAPER,
- "AppWindowToken");
-
- clearThumbnail();
- setClientHidden(isHidden() && hiddenRequested);
-
- getDisplayContent().computeImeTargetIfNeeded(this);
-
- if (DEBUG_ANIM) Slog.v(TAG, "Animation done in " + this
- + ": reportedVisible=" + reportedVisible
- + " okToDisplay=" + okToDisplay()
- + " okToAnimate=" + okToAnimate()
- + " startingDisplayed=" + startingDisplayed);
-
- // clean up thumbnail window
- if (mThumbnail != null) {
- mThumbnail.destroy();
- mThumbnail = null;
- }
-
- // WindowState.onExitAnimationDone might modify the children list, so make a copy and then
- // traverse the copy.
- final ArrayList<WindowState> children = new ArrayList<>(mChildren);
- children.forEach(WindowState::onExitAnimationDone);
-
- getDisplayContent().mAppTransition.notifyAppTransitionFinishedLocked(token);
- scheduleAnimation();
-
- mActivityRecord.onAnimationFinished();
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- }
-
- @Override
- boolean isAppAnimating() {
- return isSelfAnimating();
- }
-
- @Override
- boolean isSelfAnimating() {
- // If we are about to start a transition, we also need to be considered animating.
- return isWaitingForTransitionStart() || isReallyAnimating();
- }
-
- /**
- * @return True if and only if we are actually running an animation. Note that
- * {@link #isSelfAnimating} also returns true if we are waiting for an animation to
- * start.
- */
- private boolean isReallyAnimating() {
- return super.isSelfAnimating();
- }
-
- /**
- * @param cancel {@code true} if clearing the leash due to cancelling instead of transferring
- * to another leash.
- */
- private void clearChangeLeash(Transaction t, boolean cancel) {
- if (mTransitChangeLeash == null) {
- return;
- }
- if (cancel) {
- clearThumbnail();
- SurfaceControl sc = getSurfaceControl();
- SurfaceControl parentSc = getParentSurfaceControl();
- // Don't reparent if surface is getting destroyed
- if (parentSc != null && sc != null) {
- t.reparent(sc, getParentSurfaceControl());
- }
- }
- t.hide(mTransitChangeLeash);
- t.remove(mTransitChangeLeash);
- mTransitChangeLeash = null;
- if (cancel) {
- onAnimationLeashLost(t);
- }
- }
-
- @Override
- void cancelAnimation() {
- cancelAnimationOnly();
- clearThumbnail();
- clearChangeLeash(getPendingTransaction(), true /* cancel */);
- }
-
- /**
- * This only cancels the animation. It doesn't do other teardown like cleaning-up thumbnail
- * or interim leashes.
- * <p>
- * Used when canceling in preparation for starting a new animation.
- */
- void cancelAnimationOnly() {
- super.cancelAnimation();
- }
-
- boolean isWaitingForTransitionStart() {
- return getDisplayContent().mAppTransition.isTransitionSet()
- && (getDisplayContent().mOpeningApps.contains(this)
- || getDisplayContent().mClosingApps.contains(this)
- || getDisplayContent().mChangingApps.contains(this));
- }
-
- public int getTransit() {
- return mTransit;
- }
-
- int getTransitFlags() {
- return mTransitFlags;
- }
-
- void attachThumbnailAnimation() {
- if (!isReallyAnimating()) {
- return;
- }
- final int taskId = getTask().mTaskId;
- final GraphicBuffer thumbnailHeader =
- getDisplayContent().mAppTransition.getAppTransitionThumbnailHeader(taskId);
- if (thumbnailHeader == null) {
- if (DEBUG_APP_TRANSITIONS) Slog.d(TAG, "No thumbnail header bitmap for: " + taskId);
- return;
- }
- clearThumbnail();
- mThumbnail = new AppWindowThumbnail(mWmService.mSurfaceFactory, getPendingTransaction(),
- this, thumbnailHeader);
- mThumbnail.startAnimation(getPendingTransaction(), loadThumbnailAnimation(thumbnailHeader));
- }
-
- /**
- * Attaches a surface with a thumbnail for the
- * {@link android.app.ActivityOptions#ANIM_OPEN_CROSS_PROFILE_APPS} animation.
- */
- void attachCrossProfileAppsThumbnailAnimation() {
- if (!isReallyAnimating()) {
- return;
- }
- clearThumbnail();
-
- final WindowState win = findMainWindow();
- if (win == null) {
- return;
- }
- final Rect frame = win.getFrameLw();
- final int thumbnailDrawableRes = getTask().mUserId == mWmService.mCurrentUserId
- ? R.drawable.ic_account_circle
- : R.drawable.ic_corp_badge;
- final GraphicBuffer thumbnail =
- getDisplayContent().mAppTransition
- .createCrossProfileAppsThumbnail(thumbnailDrawableRes, frame);
- if (thumbnail == null) {
- return;
- }
- mThumbnail = new AppWindowThumbnail(mWmService.mSurfaceFactory,
- getPendingTransaction(), this, thumbnail);
- final Animation animation =
- getDisplayContent().mAppTransition.createCrossProfileAppsThumbnailAnimationLocked(
- win.getFrameLw());
- mThumbnail.startAnimation(getPendingTransaction(), animation, new Point(frame.left,
- frame.top));
- }
-
- private Animation loadThumbnailAnimation(GraphicBuffer thumbnailHeader) {
- final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
-
- // If this is a multi-window scenario, we use the windows frame as
- // destination of the thumbnail header animation. If this is a full screen
- // window scenario, we use the whole display as the target.
- WindowState win = findMainWindow();
- Rect appRect = win != null ? win.getContentFrameLw() :
- new Rect(0, 0, displayInfo.appWidth, displayInfo.appHeight);
- final Rect insets = win != null ? win.getContentInsets() : null;
- final Configuration displayConfig = mDisplayContent.getConfiguration();
- return getDisplayContent().mAppTransition.createThumbnailAspectScaleAnimationLocked(
- appRect, insets, thumbnailHeader, getTask().mTaskId, displayConfig.uiMode,
- displayConfig.orientation);
- }
-
- private void clearThumbnail() {
- if (mThumbnail == null) {
- return;
- }
- mThumbnail.destroy();
- mThumbnail = null;
- }
-
- void registerRemoteAnimations(RemoteAnimationDefinition definition) {
- mRemoteAnimationDefinition = definition;
- }
-
- RemoteAnimationDefinition getRemoteAnimationDefinition() {
- return mRemoteAnimationDefinition;
- }
-
- @Override
- void dump(PrintWriter pw, String prefix, boolean dumpAll) {
- super.dump(pw, prefix, dumpAll);
- if (appToken != null) {
- pw.println(prefix + "app=true mVoiceInteraction=" + mVoiceInteraction);
- }
- pw.println(prefix + "component=" + mActivityComponent.flattenToShortString());
- pw.print(prefix); pw.print("task="); pw.println(getTask());
- pw.print(prefix); pw.print(" mOccludesParent="); pw.print(mOccludesParent);
- pw.print(" mOrientation="); pw.println(mOrientation);
- pw.println(prefix + "hiddenRequested=" + hiddenRequested + " mClientHidden=" + mClientHidden
- + ((mDeferHidingClient) ? " mDeferHidingClient=" + mDeferHidingClient : "")
- + " reportedDrawn=" + reportedDrawn + " reportedVisible=" + reportedVisible);
- if (paused) {
- pw.print(prefix); pw.print("paused="); pw.println(paused);
- }
- if (mAppStopped) {
- pw.print(prefix); pw.print("mAppStopped="); pw.println(mAppStopped);
- }
- if (mNumInterestingWindows != 0 || mNumDrawnWindows != 0
- || allDrawn || mLastAllDrawn) {
- pw.print(prefix); pw.print("mNumInterestingWindows=");
- pw.print(mNumInterestingWindows);
- pw.print(" mNumDrawnWindows="); pw.print(mNumDrawnWindows);
- pw.print(" inPendingTransaction="); pw.print(inPendingTransaction);
- pw.print(" allDrawn="); pw.print(allDrawn);
- pw.print(" lastAllDrawn="); pw.print(mLastAllDrawn);
- pw.println(")");
- }
- if (inPendingTransaction) {
- pw.print(prefix); pw.print("inPendingTransaction=");
- pw.println(inPendingTransaction);
- }
- if (mStartingData != null || removed || firstWindowDrawn || mIsExiting) {
- pw.print(prefix); pw.print("startingData="); pw.print(mStartingData);
- pw.print(" removed="); pw.print(removed);
- pw.print(" firstWindowDrawn="); pw.print(firstWindowDrawn);
- pw.print(" mIsExiting="); pw.println(mIsExiting);
- }
- if (startingWindow != null || startingSurface != null
- || startingDisplayed || startingMoved || mHiddenSetFromTransferredStartingWindow) {
- pw.print(prefix); pw.print("startingWindow="); pw.print(startingWindow);
- pw.print(" startingSurface="); pw.print(startingSurface);
- pw.print(" startingDisplayed="); pw.print(startingDisplayed);
- pw.print(" startingMoved="); pw.print(startingMoved);
- pw.println(" mHiddenSetFromTransferredStartingWindow="
- + mHiddenSetFromTransferredStartingWindow);
- }
- if (!mFrozenBounds.isEmpty()) {
- pw.print(prefix); pw.print("mFrozenBounds="); pw.println(mFrozenBounds);
- pw.print(prefix); pw.print("mFrozenMergedConfig="); pw.println(mFrozenMergedConfig);
- }
- if (mPendingRelaunchCount != 0) {
- pw.print(prefix); pw.print("mPendingRelaunchCount="); pw.println(mPendingRelaunchCount);
- }
- if (mSizeCompatScale != 1f || mSizeCompatBounds != null) {
- pw.println(prefix + "mSizeCompatScale=" + mSizeCompatScale + " mSizeCompatBounds="
- + mSizeCompatBounds);
- }
- if (mRemovingFromDisplay) {
- pw.println(prefix + "mRemovingFromDisplay=" + mRemovingFromDisplay);
- }
- }
-
- @Override
- void setHidden(boolean hidden) {
- super.setHidden(hidden);
- scheduleAnimation();
- }
-
- @Override
- void prepareSurfaces() {
- // isSelfAnimating also returns true when we are about to start a transition, so we need
- // to check super here.
- final boolean reallyAnimating = super.isSelfAnimating();
- final boolean show = !isHidden() || reallyAnimating;
-
- if (mSurfaceControl != null) {
- if (show && !mLastSurfaceShowing) {
- getPendingTransaction().show(mSurfaceControl);
- } else if (!show && mLastSurfaceShowing) {
- getPendingTransaction().hide(mSurfaceControl);
- }
- }
- if (mThumbnail != null) {
- mThumbnail.setShowing(getPendingTransaction(), show);
- }
- mLastSurfaceShowing = show;
- super.prepareSurfaces();
- }
-
- /**
- * @return Whether our {@link #getSurfaceControl} is currently showing.
- */
- boolean isSurfaceShowing() {
- return mLastSurfaceShowing;
- }
-
- boolean isFreezingScreen() {
- return mFreezingScreen;
- }
-
- @Override
- boolean needsZBoost() {
- return mNeedsZBoost || super.needsZBoost();
- }
-
- @CallSuper
- @Override
- public void writeToProto(ProtoOutputStream proto, long fieldId,
- @WindowTraceLogLevel int logLevel) {
- // Critical log level logs only visible elements to mitigate performance overheard
- if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
- return;
- }
-
- final long token = proto.start(fieldId);
- writeNameToProto(proto, NAME);
- super.writeToProto(proto, WINDOW_TOKEN, logLevel);
- proto.write(LAST_SURFACE_SHOWING, mLastSurfaceShowing);
- proto.write(IS_WAITING_FOR_TRANSITION_START, isWaitingForTransitionStart());
- proto.write(IS_REALLY_ANIMATING, isReallyAnimating());
- if (mThumbnail != null){
- mThumbnail.writeToProto(proto, THUMBNAIL);
- }
- proto.write(FILLS_PARENT, mOccludesParent);
- proto.write(APP_STOPPED, mAppStopped);
- proto.write(HIDDEN_REQUESTED, hiddenRequested);
- proto.write(CLIENT_HIDDEN, mClientHidden);
- proto.write(DEFER_HIDING_CLIENT, mDeferHidingClient);
- proto.write(REPORTED_DRAWN, reportedDrawn);
- proto.write(REPORTED_VISIBLE, reportedVisible);
- proto.write(NUM_INTERESTING_WINDOWS, mNumInterestingWindows);
- proto.write(NUM_DRAWN_WINDOWS, mNumDrawnWindows);
- proto.write(ALL_DRAWN, allDrawn);
- proto.write(LAST_ALL_DRAWN, mLastAllDrawn);
- proto.write(REMOVED, removed);
- if (startingWindow != null) {
- startingWindow.writeIdentifierToProto(proto, STARTING_WINDOW);
- }
- proto.write(STARTING_DISPLAYED, startingDisplayed);
- proto.write(STARTING_MOVED, startingMoved);
- proto.write(HIDDEN_SET_FROM_TRANSFERRED_STARTING_WINDOW,
- mHiddenSetFromTransferredStartingWindow);
- for (Rect bounds : mFrozenBounds) {
- bounds.writeToProto(proto, FROZEN_BOUNDS);
- }
- proto.end(token);
- }
-
- void writeNameToProto(ProtoOutputStream proto, long fieldId) {
- if (appToken == null) {
- return;
- }
- try {
- proto.write(fieldId, appToken.getName());
- } catch (RemoteException e) {
- // This shouldn't happen, but in this case fall back to outputting nothing
- Slog.e(TAG, e.toString());
- }
- }
-
- @Override
- public String toString() {
- if (stringName == null) {
- StringBuilder sb = new StringBuilder();
- sb.append("AppWindowToken{");
- sb.append(Integer.toHexString(System.identityHashCode(this)));
- sb.append(" token="); sb.append(token); sb.append('}');
- stringName = sb.toString();
- }
- return stringName + ((mIsExiting) ? " mIsExiting=" : "");
- }
-
- Rect getLetterboxInsets() {
- if (mLetterbox != null) {
- return mLetterbox.getInsets();
- } else {
- return new Rect();
- }
- }
-
- /** Gets the inner bounds of letterbox. The bounds will be empty if there is no letterbox. */
- void getLetterboxInnerBounds(Rect outBounds) {
- if (mLetterbox != null) {
- outBounds.set(mLetterbox.getInnerFrame());
- } else {
- outBounds.setEmpty();
- }
- }
-
- /**
- * @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);
- }
-
- /**
- * Sets if this AWT is in the process of closing or entering PIP.
- * {@link #mWillCloseOrEnterPip}}
- */
- void setWillCloseOrEnterPip(boolean willCloseOrEnterPip) {
- mWillCloseOrEnterPip = willCloseOrEnterPip;
- }
-
- /**
- * Returns whether this AWT is considered closing. Conditions are either
- * 1. Is this app animating and was requested to be hidden
- * 2. App is delayed closing since it might enter PIP.
- */
- boolean isClosingOrEnteringPip() {
- return (isAnimating() && hiddenRequested) || mWillCloseOrEnterPip;
- }
-
- /**
- * @return Whether we are allowed to show non-starting windows at the moment. We disallow
- * showing windows during transitions in case we have windows that have wide-color-gamut
- * color mode set to avoid jank in the middle of the transition.
- */
- boolean canShowWindows() {
- return allDrawn && !(isReallyAnimating() && hasNonDefaultColorWindow());
- }
-
- /**
- * @return true if we have a window that has a non-default color mode set; false otherwise.
- */
- private boolean hasNonDefaultColorWindow() {
- return forAllWindows(ws -> ws.mAttrs.getColorMode() != COLOR_MODE_DEFAULT,
- true /* topToBottom */);
- }
-
- private void updateColorTransform() {
- if (mSurfaceControl != null && mLastAppSaturationInfo != null) {
- getPendingTransaction().setColorTransform(mSurfaceControl,
- mLastAppSaturationInfo.mMatrix, mLastAppSaturationInfo.mTranslation);
- mWmService.scheduleAnimationLocked();
- }
- }
-
- private static class AppSaturationInfo {
- float[] mMatrix = new float[9];
- float[] mTranslation = new float[3];
-
- void setSaturation(@Size(9) float[] matrix, @Size(3) float[] translation) {
- System.arraycopy(matrix, 0, mMatrix, 0, mMatrix.length);
- System.arraycopy(translation, 0, mTranslation, 0, mTranslation.length);
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index 7557271a2416..b4cecff728fd 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -16,16 +16,14 @@
package com.android.server.wm;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
-import android.graphics.Matrix;
import android.graphics.Rect;
-import android.util.Slog;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
+import com.android.server.protolog.common.ProtoLog;
+
import java.io.PrintWriter;
import java.util.function.Supplier;
@@ -33,14 +31,15 @@ import java.util.function.Supplier;
* Four black surfaces put together to make a black frame.
*/
public class BlackFrame {
- class BlackSurface {
+ static class BlackSurface {
final int left;
final int top;
final int layer;
final SurfaceControl surface;
BlackSurface(SurfaceControl.Transaction transaction, int layer,
- int l, int t, int r, int b, DisplayContent dc) throws OutOfResourcesException {
+ int l, int t, int r, int b, DisplayContent dc,
+ SurfaceControl surfaceControl) throws OutOfResourcesException {
left = l;
top = t;
this.layer = layer;
@@ -50,7 +49,7 @@ public class BlackFrame {
surface = dc.makeOverlay()
.setName("BlackSurface")
.setColorLayer()
- .setParent(null) // TODO: Work-around for b/69259549
+ .setParent(surfaceControl)
.build();
transaction.setWindowCrop(surface, w, h);
transaction.setLayerStack(surface, dc.getDisplayId());
@@ -58,46 +57,15 @@ public class BlackFrame {
transaction.setLayer(surface, layer);
transaction.setPosition(surface, left, top);
transaction.show(surface);
- if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG_WM,
- " BLACK " + surface + ": CREATE layer=" + layer);
- }
-
- void setAlpha(SurfaceControl.Transaction t, float alpha) {
- t.setAlpha(surface, alpha);
- }
-
- void setMatrix(SurfaceControl.Transaction t, Matrix matrix) {
- mTmpMatrix.setTranslate(left, top);
- mTmpMatrix.postConcat(matrix);
- mTmpMatrix.getValues(mTmpFloats);
- t.setPosition(surface, mTmpFloats[Matrix.MTRANS_X],
- mTmpFloats[Matrix.MTRANS_Y]);
- t.setMatrix(surface,
- mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
- mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
- if (false) {
- Slog.i(TAG_WM, "Black Surface @ (" + left + "," + top + "): ("
- + mTmpFloats[Matrix.MTRANS_X] + ","
- + mTmpFloats[Matrix.MTRANS_Y] + ") matrix=["
- + mTmpFloats[Matrix.MSCALE_X] + ","
- + mTmpFloats[Matrix.MSCALE_Y] + "]["
- + mTmpFloats[Matrix.MSKEW_X] + ","
- + mTmpFloats[Matrix.MSKEW_Y] + "]");
- }
- }
-
- void clearMatrix(SurfaceControl.Transaction t) {
- t.setMatrix(surface, 1, 0, 0, 1);
+ ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
+ " BLACK %s: CREATE layer=%d", surface, layer);
}
}
- final Rect mOuterRect;
- final Rect mInnerRect;
- final Matrix mTmpMatrix = new Matrix();
- final float[] mTmpFloats = new float[9];
- final BlackSurface[] mBlackSurfaces = new BlackSurface[4];
+ private final Rect mOuterRect;
+ private final Rect mInnerRect;
+ private final BlackSurface[] mBlackSurfaces = new BlackSurface[4];
- final boolean mForceDefaultOrientation;
private final Supplier<SurfaceControl.Transaction> mTransactionFactory;
public void printTo(String prefix, PrintWriter pw) {
@@ -114,12 +82,12 @@ public class BlackFrame {
}
public BlackFrame(Supplier<SurfaceControl.Transaction> factory, SurfaceControl.Transaction t,
- Rect outer, Rect inner, int layer, DisplayContent dc, boolean forceDefaultOrientation)
+ Rect outer, Rect inner, int layer, DisplayContent dc, boolean forceDefaultOrientation,
+ SurfaceControl surfaceControl)
throws OutOfResourcesException {
boolean success = false;
mTransactionFactory = factory;
- mForceDefaultOrientation = forceDefaultOrientation;
// TODO: Why do we use 4 surfaces instead of just one big one behind the screenshot?
// b/68253229
@@ -128,19 +96,20 @@ public class BlackFrame {
try {
if (outer.top < inner.top) {
mBlackSurfaces[0] = new BlackSurface(t, layer,
- outer.left, outer.top, inner.right, inner.top, dc);
+ outer.left, outer.top, inner.right, inner.top, dc, surfaceControl);
}
if (outer.left < inner.left) {
mBlackSurfaces[1] = new BlackSurface(t, layer,
- outer.left, inner.top, inner.left, outer.bottom, dc);
+ outer.left, inner.top, inner.left, outer.bottom, dc, surfaceControl);
}
if (outer.bottom > inner.bottom) {
mBlackSurfaces[2] = new BlackSurface(t, layer,
- inner.left, inner.bottom, outer.right, outer.bottom, dc);
+ inner.left, inner.bottom, outer.right, outer.bottom, dc,
+ surfaceControl);
}
if (outer.right > inner.right) {
mBlackSurfaces[3] = new BlackSurface(t, layer,
- inner.right, outer.top, outer.right, inner.bottom, dc);
+ inner.right, outer.top, outer.right, inner.bottom, dc, surfaceControl);
}
success = true;
} finally {
@@ -151,51 +120,14 @@ public class BlackFrame {
}
public void kill() {
- if (mBlackSurfaces != null) {
- SurfaceControl.Transaction t = mTransactionFactory.get();
- for (int i=0; i<mBlackSurfaces.length; i++) {
- if (mBlackSurfaces[i] != null) {
- if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG_WM,
- " BLACK " + mBlackSurfaces[i].surface + ": DESTROY");
- t.remove(mBlackSurfaces[i].surface);
- mBlackSurfaces[i] = null;
- }
- }
- t.apply();
- }
- }
-
- public void hide(SurfaceControl.Transaction t) {
- if (mBlackSurfaces != null) {
- for (int i=0; i<mBlackSurfaces.length; i++) {
- if (mBlackSurfaces[i] != null) {
- t.hide(mBlackSurfaces[i].surface);
- }
- }
- }
- }
-
- public void setAlpha(SurfaceControl.Transaction t, float alpha) {
- for (int i=0; i<mBlackSurfaces.length; i++) {
- if (mBlackSurfaces[i] != null) {
- mBlackSurfaces[i].setAlpha(t, alpha);
- }
- }
- }
-
- public void setMatrix(SurfaceControl.Transaction t, Matrix matrix) {
- for (int i=0; i<mBlackSurfaces.length; i++) {
- if (mBlackSurfaces[i] != null) {
- mBlackSurfaces[i].setMatrix(t, matrix);
- }
- }
- }
-
- public void clearMatrix(SurfaceControl.Transaction t) {
- for (int i=0; i<mBlackSurfaces.length; i++) {
+ SurfaceControl.Transaction t = mTransactionFactory.get();
+ for (int i = 0; i < mBlackSurfaces.length; i++) {
if (mBlackSurfaces[i] != null) {
- mBlackSurfaces[i].clearMatrix(t);
+ ProtoLog.i(WM_SHOW_SURFACE_ALLOC, " BLACK %s: DESTROY", mBlackSurfaces[i].surface);
+ t.remove(mBlackSurfaces[i].surface);
+ mBlackSurfaces[i] = null;
}
}
+ t.apply();
}
}
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
index c1ca816d788a..b73b481075ae 100644
--- a/services/core/java/com/android/server/wm/CircularDisplayMask.java
+++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java
@@ -56,7 +56,7 @@ class CircularDisplayMask {
private int mMaskThickness;
CircularDisplayMask(Supplier<Surface> surfaceFactory, DisplayContent dc, int zOrder,
- int screenOffset, int maskThickness) {
+ int screenOffset, int maskThickness, SurfaceControl.Transaction t) {
final Display display = dc.getDisplay();
mSurface = surfaceFactory.get();
mScreenSize = new Point();
@@ -75,10 +75,10 @@ class CircularDisplayMask {
.setFormat(PixelFormat.TRANSLUCENT)
.build();
- ctrl.setLayerStack(display.getLayerStack());
- ctrl.setLayer(zOrder);
- ctrl.setPosition(0, 0);
- ctrl.show();
+ t.setLayerStack(ctrl, display.getLayerStack());
+ t.setLayer(ctrl, zOrder);
+ t.setPosition(ctrl, 0, 0);
+ t.show(ctrl);
mSurface.copyFrom(ctrl);
} catch (OutOfResourcesException e) {
}
@@ -91,7 +91,7 @@ class CircularDisplayMask {
mMaskThickness = maskThickness;
}
- private void drawIfNeeded() {
+ private void drawIfNeeded(SurfaceControl.Transaction t) {
if (!mDrawNeeded || !mVisible || mDimensionsUnequal) {
return;
}
@@ -108,45 +108,46 @@ class CircularDisplayMask {
return;
}
switch (mRotation) {
- case Surface.ROTATION_0:
- case Surface.ROTATION_90:
- // chin bottom or right
- mSurfaceControl.setPosition(0, 0);
- break;
- case Surface.ROTATION_180:
- // chin top
- mSurfaceControl.setPosition(0, -mScreenOffset);
- break;
- case Surface.ROTATION_270:
- // chin left
- mSurfaceControl.setPosition(-mScreenOffset, 0);
- break;
+ case Surface.ROTATION_0:
+ case Surface.ROTATION_90:
+ // chin bottom or right
+ t.setPosition(mSurfaceControl, 0, 0);
+ break;
+ case Surface.ROTATION_180:
+ // chin top
+ t.setPosition(mSurfaceControl, 0, -mScreenOffset);
+ break;
+ case Surface.ROTATION_270:
+ // chin left
+ t.setPosition(mSurfaceControl, -mScreenOffset, 0);
+ break;
}
int circleRadius = mScreenSize.x / 2;
c.drawColor(Color.BLACK);
- // The radius is reduced by mMaskThickness to provide an anti aliasing effect on the display edges.
+ // The radius is reduced by mMaskThickness to provide an anti aliasing effect on the
+ // display edges.
c.drawCircle(circleRadius, circleRadius, circleRadius - mMaskThickness, mPaint);
mSurface.unlockCanvasAndPost(c);
}
// Note: caller responsible for being inside
// Surface.openTransaction() / closeTransaction()
- public void setVisibility(boolean on) {
+ public void setVisibility(boolean on, SurfaceControl.Transaction t) {
if (mSurfaceControl == null) {
return;
}
mVisible = on;
- drawIfNeeded();
+ drawIfNeeded(t);
if (on) {
- mSurfaceControl.show();
+ t.show(mSurfaceControl);
} else {
- mSurfaceControl.hide();
+ t.hide(mSurfaceControl);
}
}
- void positionSurface(int dw, int dh, int rotation) {
+ void positionSurface(int dw, int dh, int rotation, SurfaceControl.Transaction t) {
if (mLastDW == dw && mLastDH == dh && mRotation == rotation) {
return;
}
@@ -154,7 +155,7 @@ class CircularDisplayMask {
mLastDH = dh;
mDrawNeeded = true;
mRotation = rotation;
- drawIfNeeded();
+ drawIfNeeded(t);
}
}
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 3886ae1635cc..300ee1ddc77d 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -90,7 +90,8 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
private ArrayList<ConfigurationContainerListener> mChangeListeners = new ArrayList<>();
// TODO: Can't have ag/2592611 soon enough!
- private final Configuration mTmpConfig = new Configuration();
+ private final Configuration mRequestsTmpConfig = new Configuration();
+ private final Configuration mResolvedTmpConfig = new Configuration();
// Used for setting bounds
private final Rect mTmpRect = new Rect();
@@ -121,12 +122,19 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
* @see #mFullConfiguration
*/
public void onConfigurationChanged(Configuration newParentConfig) {
- mTmpConfig.setTo(mResolvedOverrideConfiguration);
+ onConfigurationChanged(newParentConfig, true /*forwardToChildren*/);
+ }
+
+ // TODO: Consolidate with onConfigurationChanged() method above once unification is done. This
+ // is only currently need during the process of unification where we don't want configuration
+ // forwarded to a child from both parents.
+ public void onConfigurationChanged(Configuration newParentConfig, boolean forwardToChildren) {
+ mResolvedTmpConfig.setTo(mResolvedOverrideConfiguration);
resolveOverrideConfiguration(newParentConfig);
mFullConfiguration.setTo(newParentConfig);
mLastOverrideConfigurationChanges =
mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
- if (!mTmpConfig.equals(mResolvedOverrideConfiguration)) {
+ if (!mResolvedTmpConfig.equals(mResolvedOverrideConfiguration)) {
onMergedOverrideConfigurationChanged();
// This depends on the assumption that change-listeners don't do
// their own override resolution. This way, dependent hierarchies
@@ -139,9 +147,11 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
mResolvedOverrideConfiguration);
}
}
- for (int i = getChildCount() - 1; i >= 0; --i) {
- final ConfigurationContainer child = getChildAt(i);
- child.onConfigurationChanged(mFullConfiguration);
+ if (forwardToChildren) {
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final ConfigurationContainer child = getChildAt(i);
+ child.onConfigurationChanged(mFullConfiguration);
+ }
}
}
@@ -262,6 +272,11 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
out.set(bounds.left, bounds.top);
}
+ Rect getResolvedOverrideBounds() {
+ mReturnBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
+ return mReturnBounds;
+ }
+
/**
* Returns the bounds requested on this container. These may not be the actual bounds the
* container ends up with due to policy constraints. The {@link Rect} handed back is
@@ -306,9 +321,9 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
}
- mTmpConfig.setTo(getRequestedOverrideConfiguration());
- mTmpConfig.windowConfiguration.setBounds(bounds);
- onRequestedOverrideConfigurationChanged(mTmpConfig);
+ mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
+ mRequestsTmpConfig.windowConfiguration.setBounds(bounds);
+ onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
return boundsChange;
}
@@ -360,9 +375,9 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
/** Sets the requested windowing mode override for the configuration container. */
public void setWindowingMode(/*@WindowConfiguration.WindowingMode*/ int windowingMode) {
- mTmpConfig.setTo(getRequestedOverrideConfiguration());
- mTmpConfig.windowConfiguration.setWindowingMode(windowingMode);
- onRequestedOverrideConfigurationChanged(mTmpConfig);
+ mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
+ mRequestsTmpConfig.windowConfiguration.setWindowingMode(windowingMode);
+ onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
}
/** Sets the always on top flag for this configuration container.
@@ -372,16 +387,16 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
* - {@Link ActivityDisplay#positionChildAtTop(ActivityStack)};
* */
public void setAlwaysOnTop(boolean alwaysOnTop) {
- mTmpConfig.setTo(getRequestedOverrideConfiguration());
- mTmpConfig.windowConfiguration.setAlwaysOnTop(alwaysOnTop);
- onRequestedOverrideConfigurationChanged(mTmpConfig);
+ mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
+ mRequestsTmpConfig.windowConfiguration.setAlwaysOnTop(alwaysOnTop);
+ onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
}
/** Sets the windowing mode for the configuration container. */
void setDisplayWindowingMode(int windowingMode) {
- mTmpConfig.setTo(getRequestedOverrideConfiguration());
- mTmpConfig.windowConfiguration.setDisplayWindowingMode(windowingMode);
- onRequestedOverrideConfigurationChanged(mTmpConfig);
+ mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
+ mRequestsTmpConfig.windowConfiguration.setDisplayWindowingMode(windowingMode);
+ onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
}
/**
@@ -451,9 +466,9 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
throw new IllegalStateException("Can't change activity type once set: " + this
+ " activityType=" + activityTypeToString(activityType));
}
- mTmpConfig.setTo(getRequestedOverrideConfiguration());
- mTmpConfig.windowConfiguration.setActivityType(activityType);
- onRequestedOverrideConfigurationChanged(mTmpConfig);
+ mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
+ mRequestsTmpConfig.windowConfiguration.setActivityType(activityType);
+ onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
}
public boolean isActivityTypeHome() {
@@ -611,6 +626,10 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
return mFullConfiguration.windowConfiguration.isAlwaysOnTop();
}
+ boolean hasChild() {
+ return getChildCount() > 0;
+ }
+
abstract protected int getChildCount();
abstract protected E getChildAt(int index);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 63ff2ea8069c..aa0b68b9bfc5 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -59,11 +59,10 @@ import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE;
import static android.view.WindowManager.LayoutParams.NEEDS_MENU_UNSET;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
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_BOOT_PROGRESS;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
@@ -100,24 +99,21 @@ import static com.android.server.wm.DisplayContentProto.ROTATION;
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
import static com.android.server.wm.DisplayContentProto.STACKS;
import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
+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_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.WindowManagerDebugConfig.DEBUG_DISPLAY;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_STACK_CRAWLS;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.REPORT_FOCUS_CHANGE;
@@ -133,11 +129,9 @@ import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_ASSIG
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
import static com.android.server.wm.WindowManagerService.dipToPixel;
-import static com.android.server.wm.WindowManagerService.logSurface;
import static com.android.server.wm.WindowState.EXCLUSION_LEFT;
import static com.android.server.wm.WindowState.EXCLUSION_RIGHT;
import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP;
-import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
import static com.android.server.wm.utils.RegionUtils.forEachRectReverse;
import static com.android.server.wm.utils.RegionUtils.rectListToRegion;
@@ -173,6 +167,7 @@ import android.os.UserHandle;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.Slog;
+import android.util.SparseBooleanArray;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.DisplayCutout;
@@ -202,6 +197,7 @@ import com.android.internal.util.function.pooled.PooledConsumer;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.AnimationThread;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.protolog.common.ProtoLog;
import com.android.server.wm.utils.DisplayRotationUtil;
import com.android.server.wm.utils.RotationCache;
import com.android.server.wm.utils.WmDisplayCutout;
@@ -224,7 +220,7 @@ import java.util.function.Predicate;
* particular Display.
*/
class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer>
- implements WindowManagerPolicy.DisplayContentInfo {
+ implements WindowManagerPolicy.DisplayContentInfo, ConfigurationContainerListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayContent" : TAG_WM;
/** The default scaling mode that scales content automatically. */
@@ -243,7 +239,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
private final int mDisplayId;
// TODO: Remove once unification is complete.
- ActivityDisplay mAcitvityDisplay;
+ ActivityDisplay mActivityDisplay;
/** The containers below are the only child containers the display can have. */
// Contains all window containers that are related to apps (Activities)
@@ -274,9 +270,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
final AppTransitionController mAppTransitionController;
boolean mSkipAppTransitionAnimation = false;
- final ArraySet<AppWindowToken> mOpeningApps = new ArraySet<>();
- final ArraySet<AppWindowToken> mClosingApps = new ArraySet<>();
- final ArraySet<AppWindowToken> mChangingApps = new ArraySet<>();
+ final ArraySet<ActivityRecord> mOpeningApps = new ArraySet<>();
+ final ArraySet<ActivityRecord> mClosingApps = new ArraySet<>();
+ final ArraySet<ActivityRecord> mChangingApps = new ArraySet<>();
final UnknownAppVisibilityController mUnknownAppVisibilityController;
BoundsAnimationController mBoundsAnimationController;
@@ -335,7 +331,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
/**
* For default display it contains real metrics, empty for others.
- * @see WindowManagerService#createWatermarkInTransaction()
+ * @see WindowManagerService#createWatermark()
*/
final DisplayMetrics mRealDisplayMetrics = new DisplayMetrics();
@@ -442,12 +438,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
/** A collection of windows that provide tap exclude regions inside of them. */
final ArraySet<WindowState> mTapExcludeProvidingWindows = new ArraySet<>();
- private boolean mHaveBootMsg = false;
- private boolean mHaveApp = false;
- private boolean mHaveWallpaper = false;
- private boolean mHaveKeyguard = true;
-
- private final LinkedList<AppWindowToken> mTmpUpdateAllDrawn = new LinkedList();
+ private final LinkedList<ActivityRecord> mTmpUpdateAllDrawn = new LinkedList();
private final TaskForResizePointSearchResult mTmpTaskForResizePointSearchResult =
new TaskForResizePointSearchResult();
@@ -489,7 +480,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* the user taps on the area outside of the task of the focused app, we will notify AM about the
* new task the user wants to interact with.
*/
- AppWindowToken mFocusedApp = null;
+ ActivityRecord mFocusedApp = null;
/** Windows added since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */
final ArrayList<WindowState> mWinAddedSinceNullFocus = new ArrayList<>();
@@ -497,6 +488,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
/** Windows removed since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */
final ArrayList<WindowState> mWinRemovedSinceNullFocus = new ArrayList<>();
+ private ScreenRotationAnimation mScreenRotationAnimation;
+
/**
* We organize all top-level Surfaces in to the following layers.
* mOverlayLayer contains a few Surfaces which are always on top of others
@@ -521,9 +514,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
*/
private int mDeferUpdateImeTargetCount;
- /** Temporary float array to retrieve 3x3 matrix values. */
- private final float[] mTmpFloats = new float[9];
-
private MagnificationSpec mMagnificationSpec;
private InputMonitor mInputMonitor;
@@ -564,14 +554,18 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// Last systemUiVisibility we dispatched to windows.
private int mLastDispatchedSystemUiVisibility = 0;
+ private final ArrayList<TaskStack> mTmpAlwaysOnTopStacks = new ArrayList<>();
+ private final ArrayList<TaskStack> mTmpNormalStacks = new ArrayList<>();
+ private final ArrayList<TaskStack> mTmpHomeStacks = new ArrayList<>();
+
/** Corner radius that windows should have in order to match the display. */
private final float mWindowCornerRadius;
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
- final AppWindowToken atoken = w.mAppToken;
+ final ActivityRecord activity = w.mActivityRecord;
if (winAnimator.mDrawState == READY_TO_SHOW) {
- if (atoken == null || atoken.canShowWindows()) {
+ if (activity == null || activity.canShowWindows()) {
if (w.performShowLocked()) {
pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
if (DEBUG_LAYOUT_REPEATS) {
@@ -595,51 +589,51 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
};
private final ToBooleanFunction<WindowState> mFindFocusedWindow = w -> {
- final AppWindowToken focusedApp = mFocusedApp;
- if (DEBUG_FOCUS) Slog.v(TAG_WM, "Looking for focus: " + w
- + ", flags=" + w.mAttrs.flags + ", canReceive=" + w.canReceiveKeys());
+ final ActivityRecord focusedApp = mFocusedApp;
+ ProtoLog.v(WM_DEBUG_FOCUS, "Looking for focus: %s, flags=%d, canReceive=%b",
+ w, w.mAttrs.flags, w.canReceiveKeys());
if (!w.canReceiveKeys()) {
return false;
}
- final AppWindowToken wtoken = w.mAppToken;
+ final ActivityRecord activity = w.mActivityRecord;
// If this window's application has been removed, just skip it.
- if (wtoken != null && (wtoken.removed || wtoken.sendingToBottom)) {
- if (DEBUG_FOCUS) Slog.v(TAG_WM, "Skipping " + wtoken + " because "
- + (wtoken.removed ? "removed" : "sendingToBottom"));
+ if (activity!= null && (activity.removed || activity.sendingToBottom)) {
+ ProtoLog.v(WM_DEBUG_FOCUS, "Skipping %s because %s", activity,
+ (activity.removed ? "removed" : "sendingToBottom"));
return false;
}
if (focusedApp == null) {
- if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: focusedApp=null"
- + " using new focus @ " + w);
+ ProtoLog.v(WM_DEBUG_FOCUS_LIGHT,
+ "findFocusedWindow: focusedApp=null using new focus @ %s", w);
mTmpWindow = w;
return true;
}
if (!focusedApp.windowsAreFocusable()) {
// Current focused app windows aren't focusable...
- if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: focusedApp windows not"
- + " focusable using new focus @ " + w);
+ ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "findFocusedWindow: focusedApp windows not"
+ + " focusable using new focus @ %s", w);
mTmpWindow = w;
return true;
}
// Descend through all of the app tokens and find the first that either matches
- // win.mAppToken (return win) or mFocusedApp (return null).
- if (wtoken != null && w.mAttrs.type != TYPE_APPLICATION_STARTING) {
- if (focusedApp.compareTo(wtoken) > 0) {
+ // win.mActivityRecord (return win) or mFocusedApp (return null).
+ if (activity != null && w.mAttrs.type != TYPE_APPLICATION_STARTING) {
+ if (focusedApp.compareTo(activity) > 0) {
// App stack below focused app stack. No focus for you!!!
- if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM,
- "findFocusedWindow: Reached focused app=" + focusedApp);
+ ProtoLog.v(WM_DEBUG_FOCUS_LIGHT,
+ "findFocusedWindow: Reached focused app=%s", focusedApp);
mTmpWindow = null;
return true;
}
}
- if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: Found new focus @ " + w);
+ ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "findFocusedWindow: Found new focus @ %s", w);
mTmpWindow = w;
return true;
};
@@ -654,14 +648,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
Slog.v(TAG, "1ST PASS " + w + ": gone=" + gone + " mHaveFrame=" + w.mHaveFrame
+ " mLayoutAttached=" + w.mLayoutAttached
+ " config reported=" + w.isLastConfigReportedToClient());
- final AppWindowToken atoken = w.mAppToken;
+ final ActivityRecord activity = w.mActivityRecord;
if (gone) Slog.v(TAG, " GONE: mViewVisibility=" + w.mViewVisibility
+ " mRelayoutCalled=" + w.mRelayoutCalled + " hidden=" + w.mToken.isHidden()
- + " hiddenRequested=" + (atoken != null && atoken.hiddenRequested)
+ + " hiddenRequested=" + (activity != null && activity.hiddenRequested)
+ " parentHidden=" + w.isParentWindowHidden());
else Slog.v(TAG, " VIS: mViewVisibility=" + w.mViewVisibility
+ " mRelayoutCalled=" + w.mRelayoutCalled + " hidden=" + w.mToken.isHidden()
- + " hiddenRequested=" + (atoken != null && atoken.hiddenRequested)
+ + " hiddenRequested=" + (activity != null && activity.hiddenRequested)
+ " parentHidden=" + w.isParentWindowHidden());
}
@@ -689,8 +683,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
w.updateLastInsetValues();
}
- if (w.mAppToken != null) {
- w.mAppToken.layoutLetterbox(w);
+ if (w.mActivityRecord != null) {
+ w.mActivityRecord.layoutLetterbox(w);
}
if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrameLw()
@@ -831,12 +825,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
- final AppWindowToken atoken = w.mAppToken;
- if (atoken != null) {
- atoken.updateLetterboxSurface(w);
- final boolean updateAllDrawn = atoken.updateDrawnWindowStates(w);
- if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(atoken)) {
- mTmpUpdateAllDrawn.add(atoken);
+ final ActivityRecord activity = w.mActivityRecord;
+ if (activity != null) {
+ activity.updateLetterboxSurface(w);
+ final boolean updateAllDrawn = activity.updateDrawnWindowStates(w);
+ if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(activity)) {
+ mTmpUpdateAllDrawn.add(activity);
}
}
@@ -857,7 +851,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
DisplayContent(Display display, WindowManagerService service,
ActivityDisplay activityDisplay) {
super(service);
- mAcitvityDisplay = activityDisplay;
+ mActivityDisplay = activityDisplay;
if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) {
throw new IllegalArgumentException("Display with ID=" + display.getDisplayId()
+ " already exists=" + service.mRoot.getDisplayContent(display.getDisplayId())
@@ -972,12 +966,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return mTokenMap.get(binder);
}
- AppWindowToken getAppWindowToken(IBinder binder) {
+ ActivityRecord getActivityRecord(IBinder binder) {
final WindowToken token = getWindowToken(binder);
if (token == null) {
return null;
}
- return token.asAppWindowToken();
+ return token.asActivityRecord();
}
private void addWindowToken(IBinder binder, WindowToken token) {
@@ -1001,7 +995,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mTokenMap.put(binder, token);
- if (token.asAppWindowToken() == null) {
+ if (token.asActivityRecord() == null) {
// Add non-app token to container hierarchy on the display. App tokens are added through
// the parent container managing them (e.g. Tasks).
switch (token.windowType) {
@@ -1021,7 +1015,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
WindowToken removeWindowToken(IBinder binder) {
final WindowToken token = mTokenMap.remove(binder);
- if (token != null && token.asAppWindowToken() == null) {
+ if (token != null && token.asActivityRecord() == null) {
token.setExiting();
}
return token;
@@ -1034,7 +1028,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return;
}
if (prevDc != null) {
- if (prevDc.mTokenMap.remove(token.token) != null && token.asAppWindowToken() == null) {
+ if (prevDc.mTokenMap.remove(token.token) != null && token.asActivityRecord() == null) {
// Removed the token from the map, but made sure it's not an app token before
// removing from parent.
token.getParent().removeChild(token);
@@ -1063,14 +1057,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return;
}
- final AppWindowToken appToken = token.asAppWindowToken();
+ final ActivityRecord activity = token.asActivityRecord();
- if (appToken == null) {
+ if (activity == null) {
Slog.w(TAG_WM, "Attempted to remove non-App token: " + binder + " token=" + token);
return;
}
- appToken.onRemovedFromDisplay();
+ activity.onRemovedFromDisplay();
}
@Override
@@ -1141,9 +1135,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* values from being replaced by the initializing {@link #ActivityDisplay}.
*/
void initializeDisplayOverrideConfiguration() {
- if (mAcitvityDisplay != null) {
- mAcitvityDisplay.onInitializeOverrideConfiguration(getRequestedOverrideConfiguration());
+ if (mActivityDisplay == null) {
+ return;
}
+ mActivityDisplay.onRequestedOverrideConfigurationChanged(
+ getResolvedOverrideConfiguration());
+ mActivityDisplay.registerConfigurationChangeListener(this);
}
void reconfigureDisplayLocked() {
@@ -1169,10 +1166,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
void sendNewConfiguration() {
- if (!isReady() || mAcitvityDisplay == null) {
+ if (!isReady() || mActivityDisplay == null) {
return;
}
- final boolean configUpdated = mAcitvityDisplay.updateDisplayOverrideConfigurationLocked();
+ final boolean configUpdated = mActivityDisplay.updateDisplayOverrideConfigurationLocked();
if (configUpdated) {
return;
}
@@ -1203,7 +1200,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
if (handled && requestingContainer instanceof ActivityRecord) {
final ActivityRecord activityRecord = (ActivityRecord) requestingContainer;
- final boolean kept = mAcitvityDisplay.updateDisplayOverrideConfigurationLocked(
+ final boolean kept = mActivityDisplay.updateDisplayOverrideConfigurationLocked(
config, activityRecord, false /* deferResume */, null /* result */);
activityRecord.frozenBeforeDestroy = true;
if (!kept) {
@@ -1212,7 +1209,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
} else {
// We have a new configuration to push so we need to update ATMS for now.
// TODO: Clean up display configuration push between ATMS and WMS after unification.
- mAcitvityDisplay.updateDisplayOverrideConfigurationLocked(
+ mActivityDisplay.updateDisplayOverrideConfigurationLocked(
config, null /* starting */, false /* deferResume */, null);
}
return handled;
@@ -1258,9 +1255,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// If we changed the orientation but mOrientationChangeComplete is already true,
// we used seamless rotation, and we don't need to freeze the screen.
if (freezeDisplayToken != null && !mWmService.mRoot.mOrientationChangeComplete) {
- final AppWindowToken atoken = getAppWindowToken(freezeDisplayToken);
- if (atoken != null) {
- atoken.startFreezingScreen();
+ final ActivityRecord activity = getActivityRecord(freezeDisplayToken);
+ if (activity != null) {
+ activity.startFreezingScreen();
}
}
config = new Configuration();
@@ -1305,9 +1302,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
void applyRotationLocked(final int oldRotation, final int rotation) {
mDisplayRotation.applyCurrentRotation(rotation);
final boolean rotateSeamlessly = mDisplayRotation.isRotatingSeamlessly();
- final ScreenRotationAnimation screenRotationAnimation = rotateSeamlessly
- ? null : mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
final Transaction transaction = getPendingTransaction();
+ ScreenRotationAnimation screenRotationAnimation = rotateSeamlessly
+ ? null : getRotationAnimation();
// We need to update our screen size information to match the new rotation. If the rotation
// has actually changed then this method will return true and, according to the comment at
// the top of the method, the caller is obligated to call computeNewConfigurationLocked().
@@ -1330,7 +1327,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
forAllWindows(w -> {
if (w.mHasSurface && !rotateSeamlessly) {
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Set mOrientationChanging of " + w);
+ ProtoLog.v(WM_DEBUG_ORIENTATION, "Set mOrientationChanging of %s", w);
w.setOrientationChanging(true);
mWmService.mRoot.mOrientationChangeComplete = false;
w.mLastFreezeDuration = 0;
@@ -1994,8 +1991,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
if (mWmService.mDisplayFrozen) {
if (mLastWindowForcedOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId
- + " is frozen, return " + mLastWindowForcedOrientation);
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "Display id=%d is frozen, return %d", mDisplayId,
+ mLastWindowForcedOrientation);
// If the display is frozen, some activities may be in the middle of restarting, and
// thus have removed their old window. If the window has the flag to hide the lock
// screen, then the lock screen can re-appear and inflict its own orientation on us.
@@ -2007,8 +2005,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// window. We don't want to check the show when locked window directly though as
// things aren't stable while the display is frozen, for example the window could be
// momentarily unavailable due to activity relaunch.
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId
- + " is frozen while keyguard locked, return " + getLastOrientation());
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "Display id=%d is frozen while keyguard locked, return %d",
+ mDisplayId, getLastOrientation());
return getLastOrientation();
}
} else {
@@ -2375,6 +2374,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
void removeImmediately() {
mRemovingDisplay = true;
try {
+ if (mActivityDisplay != null) {
+ mActivityDisplay.unregisterConfigurationChangeListener(this);
+ }
if (mParentWindow != null) {
mParentWindow.removeEmbeddedDisplayContent(this);
}
@@ -2389,6 +2391,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
super.removeImmediately();
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
mPointerEventDispatcher.dispose();
+ setRotationAnimation(null);
mWmService.mAnimator.removeDisplayLocked(mDisplayId);
mWindowingLayer.release();
mOverlayLayer.release();
@@ -2553,6 +2556,17 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return delta;
}
+ public void setRotationAnimation(ScreenRotationAnimation screenRotationAnimation) {
+ if (mScreenRotationAnimation != null) {
+ mScreenRotationAnimation.kill();
+ }
+ mScreenRotationAnimation = screenRotationAnimation;
+ }
+
+ public ScreenRotationAnimation getRotationAnimation() {
+ return mScreenRotationAnimation;
+ }
+
private static void createRotationMatrix(int rotation, float displayWidth, float displayHeight,
Matrix outMatrix) {
// For rotations without Z-ordering we don't need the target rectangle's position.
@@ -2615,8 +2629,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
proto.write(DPI, mBaseDisplayDensity);
mDisplayInfo.writeToProto(proto, DISPLAY_INFO);
proto.write(ROTATION, getRotation());
- final ScreenRotationAnimation screenRotationAnimation =
- mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
+ final ScreenRotationAnimation screenRotationAnimation = getRotationAnimation();
if (screenRotationAnimation != null) {
screenRotationAnimation.writeToProto(proto, SCREEN_ROTATION_ANIMATION);
}
@@ -2626,13 +2639,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
}
for (int i = mOpeningApps.size() - 1; i >= 0; i--) {
- mOpeningApps.valueAt(i).mActivityRecord.writeIdentifierToProto(proto, OPENING_APPS);
+ mOpeningApps.valueAt(i).writeIdentifierToProto(proto, OPENING_APPS);
}
for (int i = mClosingApps.size() - 1; i >= 0; i--) {
- mClosingApps.valueAt(i).mActivityRecord.writeIdentifierToProto(proto, CLOSING_APPS);
+ mClosingApps.valueAt(i).writeIdentifierToProto(proto, CLOSING_APPS);
}
for (int i = mChangingApps.size() - 1; i >= 0; i--) {
- mChangingApps.valueAt(i).mActivityRecord.writeIdentifierToProto(proto, CHANGING_APPS);
+ mChangingApps.valueAt(i).writeIdentifierToProto(proto, CHANGING_APPS);
}
proto.end(token);
}
@@ -2728,6 +2741,16 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
+ final ScreenRotationAnimation rotationAnimation = getRotationAnimation();
+ if (rotationAnimation != null) {
+ pw.print(subPrefix);
+ pw.println(" mScreenRotationAnimation:");
+ rotationAnimation.printTo(" ", pw);
+ } else if (dumpAll) {
+ pw.print(subPrefix);
+ pw.println(" no ScreenRotationAnimation ");
+ }
+
pw.println();
// Dump stack references
@@ -2847,7 +2870,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
forAllWindows(mFindFocusedWindow, true /* traverseTopToBottom */);
if (mTmpWindow == null) {
- if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: No focusable windows.");
+ ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "findFocusedWindow: No focusable windows.");
return null;
}
return mTmpWindow;
@@ -2893,11 +2916,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mWmService.mH.obtainMessage(REPORT_FOCUS_CHANGE, this).sendToTarget();
}
- if (DEBUG_FOCUS_LIGHT || DEBUG) {
- Slog.v(TAG_WM, "Changing focus from "
- + mCurrentFocus + " to " + newFocus + " displayId=" + getDisplayId()
- + " Callers=" + Debug.getCallers(4));
- }
+ ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Changing focus from %s to %s displayId=%d Callers=%s",
+ mCurrentFocus, newFocus, getDisplayId(), Debug.getCallers(4));
final WindowState oldFocus = mCurrentFocus;
mCurrentFocus = newFocus;
mLosingFocus.remove(newFocus);
@@ -2959,10 +2979,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
/**
* Set the new focused app to this display.
*
- * @param newFocus the new focused AppWindowToken.
+ * @param newFocus the new focused {@link ActivityRecord}.
* @return true if the focused app is changed.
*/
- boolean setFocusedApp(AppWindowToken newFocus) {
+ boolean setFocusedApp(ActivityRecord newFocus) {
if (newFocus != null) {
final DisplayContent appDisplay = newFocus.getDisplayContent();
if (appDisplay != this) {
@@ -3030,11 +3050,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
wsa.destroySurface();
mWmService.mForceRemoves.add(w);
mTmpWindow = w;
- } else if (w.mAppToken != null && w.mAppToken.isClientHidden()) {
+ } else if (w.mActivityRecord != null && w.mActivityRecord.isClientHidden()) {
Slog.w(TAG_WM, "LEAKED SURFACE (app token hidden): "
+ w + " surface=" + wsa.mSurfaceController
- + " token=" + w.mAppToken);
- if (SHOW_TRANSACTIONS) logSurface(w, "LEAK DESTROY", false);
+ + " token=" + w.mActivityRecord);
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE LEAK DESTROY: %s", w);
wsa.destroySurface();
mTmpWindow = w;
}
@@ -3102,9 +3122,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// to be on top of it, but it is not -really- where input will go. So look down below
// for a real window to target...
if (target != null && target.mAttrs.type == TYPE_APPLICATION_STARTING) {
- final AppWindowToken token = target.mAppToken;
- if (token != null) {
- final WindowState betterTarget = token.getImeTargetBelowWindow(target);
+ final ActivityRecord activity = target.mActivityRecord;
+ if (activity != null) {
+ final WindowState betterTarget = activity.getImeTargetBelowWindow(target);
if (betterTarget != null) {
target = betterTarget;
}
@@ -3138,15 +3158,15 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
if (updateImeTarget) {
- AppWindowToken token = curTarget == null ? null : curTarget.mAppToken;
- if (token != null) {
+ ActivityRecord activity = curTarget == null ? null : curTarget.mActivityRecord;
+ if (activity != null) {
// Now some fun for dealing with window animations that modify the Z order. We need
// to look at all windows below the current target that are in this app, finding the
// highest visible one in layering.
WindowState highestTarget = null;
- if (token.isSelfAnimating()) {
- highestTarget = token.getHighestAnimLayerWindow(curTarget);
+ if (activity.isSelfAnimating()) {
+ highestTarget = activity.getHighestAnimLayerWindow(curTarget);
}
if (highestTarget != null) {
@@ -3174,8 +3194,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* Calling {@link #computeImeTarget(boolean)} to update the input method target window in
* the candidate app window token if needed.
*/
- void computeImeTargetIfNeeded(AppWindowToken candidate) {
- if (mInputMethodTarget != null && mInputMethodTarget.mAppToken == candidate) {
+ void computeImeTargetIfNeeded(ActivityRecord candidate) {
+ if (mInputMethodTarget != null && mInputMethodTarget.mActivityRecord == candidate) {
computeImeTarget(true /* updateImeTarget */);
}
}
@@ -3213,12 +3233,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// Attach it to app if the target is part of an app and such app is covering the entire
// screen. If it's not covering the entire screen the IME might extend beyond the apps
// bounds.
- if (mInputMethodTarget != null && mInputMethodTarget.mAppToken != null
+ if (mInputMethodTarget != null && mInputMethodTarget.mActivityRecord != null
&& mInputMethodTarget.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
// An activity with override bounds should be letterboxed inside its parent bounds,
// so it doesn't fill the screen.
- && mInputMethodTarget.mAppToken.matchParentBounds()) {
- return mInputMethodTarget.mAppToken.getSurfaceControl();
+ && mInputMethodTarget.mActivityRecord.matchParentBounds()) {
+ return mInputMethodTarget.mActivityRecord.getSurfaceControl();
}
// Otherwise, we just attach it to the display.
@@ -3321,7 +3341,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
boolean subtle) {
final WindowManagerPolicy policy = mWmService.mPolicy;
forAllWindows(w -> {
- if (w.mAppToken == null && policy.canBeHiddenByKeyguardLw(w)
+ if (w.mActivityRecord == null && policy.canBeHiddenByKeyguardLw(w)
&& w.wouldBeVisibleIfPolicyIgnored() && !w.isVisible()) {
w.startAnimation(policy.createHiddenByKeyguardExit(
onWallpaper, goingToShade, subtle));
@@ -3329,34 +3349,38 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}, true /* traverseTopToBottom */);
}
- boolean checkWaitingForWindows() {
+ /** @return {@code true} if there is window to wait before enabling the screen. */
+ boolean shouldWaitForSystemDecorWindowsOnBoot() {
+ if (!isDefaultDisplay && !supportsSystemDecorations()) {
+ // Nothing to wait because the secondary display doesn't support system decorations,
+ // there is no wallpaper, keyguard (status bar) or application (home) window to show
+ // during booting.
+ return false;
+ }
- mHaveBootMsg = false;
- mHaveApp = false;
- mHaveWallpaper = false;
- mHaveKeyguard = true;
+ final SparseBooleanArray drawnWindowTypes = new SparseBooleanArray();
+ // Presuppose keyguard is drawn because if its window isn't attached, we don't know if it
+ // wants to be shown or hidden, then it should not delay enabling the screen.
+ drawnWindowTypes.put(TYPE_STATUS_BAR, true);
- final WindowState visibleWindow = getWindow(w -> {
- if (w.isVisibleLw() && !w.mObscured && !w.isDrawnLw()) {
+ final WindowState visibleNotDrawnWindow = getWindow(w -> {
+ if (w.mViewVisibility == View.VISIBLE && !w.mObscured && !w.isDrawnLw()) {
return true;
}
if (w.isDrawnLw()) {
- if (w.mAttrs.type == TYPE_BOOT_PROGRESS) {
- mHaveBootMsg = true;
- } else if (w.mAttrs.type == TYPE_APPLICATION
- || w.mAttrs.type == TYPE_DRAWN_APPLICATION) {
- mHaveApp = true;
- } else if (w.mAttrs.type == TYPE_WALLPAPER) {
- mHaveWallpaper = true;
- } else if (w.mAttrs.type == TYPE_STATUS_BAR) {
- mHaveKeyguard = mWmService.mPolicy.isKeyguardDrawnLw();
+ final int type = w.mAttrs.type;
+ if (type == TYPE_BOOT_PROGRESS || type == TYPE_BASE_APPLICATION
+ || type == TYPE_WALLPAPER) {
+ drawnWindowTypes.put(type, true);
+ } else if (type == TYPE_STATUS_BAR) {
+ drawnWindowTypes.put(TYPE_STATUS_BAR, mWmService.mPolicy.isKeyguardDrawnLw());
}
}
return false;
});
- if (visibleWindow != null) {
- // We have a visible window.
+ if (visibleNotDrawnWindow != null) {
+ // Wait for the visible window to be drawn.
return true;
}
@@ -3368,23 +3392,27 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
com.android.internal.R.bool.config_checkWallpaperAtBoot)
&& !mWmService.mOnlyCore;
- if (DEBUG_SCREEN_ON || DEBUG_BOOT) Slog.i(TAG_WM,
- "******** booted=" + mWmService.mSystemBooted
- + " msg=" + mWmService.mShowingBootMessages
- + " haveBoot=" + mHaveBootMsg + " haveApp=" + mHaveApp
- + " haveWall=" + mHaveWallpaper + " wallEnabled=" + wallpaperEnabled
- + " haveKeyguard=" + mHaveKeyguard);
+ final boolean haveBootMsg = drawnWindowTypes.get(TYPE_BOOT_PROGRESS);
+ final boolean haveApp = drawnWindowTypes.get(TYPE_BASE_APPLICATION);
+ final boolean haveWallpaper = drawnWindowTypes.get(TYPE_WALLPAPER);
+ final boolean haveKeyguard = drawnWindowTypes.get(TYPE_STATUS_BAR);
+
+ ProtoLog.i(WM_DEBUG_SCREEN_ON,
+ "******** booted=%b msg=%b haveBoot=%b haveApp=%b haveWall=%b "
+ + "wallEnabled=%b haveKeyguard=%b",
+ mWmService.mSystemBooted, mWmService.mShowingBootMessages, haveBootMsg,
+ haveApp, haveWallpaper, wallpaperEnabled, haveKeyguard);
// If we are turning on the screen to show the boot message, don't do it until the boot
// message is actually displayed.
- if (!mWmService.mSystemBooted && !mHaveBootMsg) {
+ if (!mWmService.mSystemBooted && !haveBootMsg) {
return true;
}
// If we are turning on the screen after the boot is completed normally, don't do so until
// we have the application and wallpaper.
if (mWmService.mSystemBooted
- && ((!mHaveApp && !mHaveKeyguard) || (wallpaperEnabled && !mHaveWallpaper))) {
+ && ((!haveApp && !haveKeyguard) || (wallpaperEnabled && !haveWallpaper))) {
return true;
}
@@ -3422,6 +3450,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return win != null;
}
+ void hideTransientBars() {
+ // TODO(b/118118435): Remove this after migration
+ final int transientFlags = View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
+ statusBarVisibilityChanged(mLastStatusBarVisibility & ~transientFlags);
+
+ getInsetsPolicy().hideTransient();
+ }
+
void statusBarVisibilityChanged(int visibility) {
mLastStatusBarVisibility = visibility;
visibility = getDisplayPolicy().adjustSystemUiVisibilityLw(visibility);
@@ -3490,19 +3526,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mWmService.mWindowPlacerLocked.performSurfacePlacement();
}
- void waitForAllWindowsDrawn() {
- final WindowManagerPolicy policy = mWmService.mPolicy;
- forAllWindows(w -> {
- final boolean keyguard = policy.isKeyguardHostWindow(w.mAttrs);
- if (w.isVisibleLw() && (w.mAppToken != null || keyguard)) {
- w.mWinAnimator.mDrawState = DRAW_PENDING;
- // Force add to mResizingWindows.
- w.resetLastContentInsets();
- mWmService.mWaitingForDrawn.add(w);
- }
- }, true /* traverseTopToBottom */);
- }
-
// TODO: Super crazy long method that should be broken down...
void applySurfaceChangesTransaction(boolean recoveringMemory) {
final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
@@ -3586,10 +3609,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
while (!mTmpUpdateAllDrawn.isEmpty()) {
- final AppWindowToken atoken = mTmpUpdateAllDrawn.removeLast();
+ final ActivityRecord activity = mTmpUpdateAllDrawn.removeLast();
// See if any windows have been drawn, so they (and others associated with them)
// can now be shown.
- atoken.updateAllDrawn();
+ activity.updateAllDrawn();
}
}
@@ -3737,7 +3760,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
convertCropForSurfaceFlinger(frame, rot, dw, dh);
final ScreenRotationAnimation screenRotationAnimation =
- mWmService.mAnimator.getScreenRotationAnimationLocked(DEFAULT_DISPLAY);
+ 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");
@@ -4160,15 +4183,15 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
private boolean forAllExitingAppTokenWindows(ToBooleanFunction<WindowState> callback,
boolean traverseTopToBottom) {
- // For legacy reasons we process the TaskStack.mExitingAppTokens first here before the
+ // For legacy reasons we process the TaskStack.mExitingActivities first here before the
// app tokens.
// TODO: Investigate if we need to continue to do this or if we can just process them
// in-order.
if (traverseTopToBottom) {
for (int i = mChildren.size() - 1; i >= 0; --i) {
- final AppTokenList appTokens = mChildren.get(i).mExitingAppTokens;
- for (int j = appTokens.size() - 1; j >= 0; --j) {
- if (appTokens.get(j).forAllWindowsUnchecked(callback,
+ final List<ActivityRecord> activities = mChildren.get(i).mExitingActivities;
+ for (int j = activities.size() - 1; j >= 0; --j) {
+ if (activities.get(j).forAllWindowsUnchecked(callback,
traverseTopToBottom)) {
return true;
}
@@ -4177,10 +4200,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
} else {
final int count = mChildren.size();
for (int i = 0; i < count; ++i) {
- final AppTokenList appTokens = mChildren.get(i).mExitingAppTokens;
- final int appTokensCount = appTokens.size();
+ final List<ActivityRecord> activities = mChildren.get(i).mExitingActivities;
+ final int appTokensCount = activities.size();
for (int j = 0; j < appTokensCount; j++) {
- if (appTokens.get(j).forAllWindowsUnchecked(callback,
+ if (activities.get(j).forAllWindowsUnchecked(callback,
traverseTopToBottom)) {
return true;
}
@@ -4192,27 +4215,27 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
void setExitingTokensHasVisible(boolean hasVisible) {
for (int i = mChildren.size() - 1; i >= 0; --i) {
- final AppTokenList appTokens = mChildren.get(i).mExitingAppTokens;
- for (int j = appTokens.size() - 1; j >= 0; --j) {
- appTokens.get(j).hasVisible = hasVisible;
+ final ArrayList<ActivityRecord> activities = mChildren.get(i).mExitingActivities;
+ for (int j = activities.size() - 1; j >= 0; --j) {
+ activities.get(j).hasVisible = hasVisible;
}
}
}
void removeExistingAppTokensIfPossible() {
for (int i = mChildren.size() - 1; i >= 0; --i) {
- final AppTokenList appTokens = mChildren.get(i).mExitingAppTokens;
- for (int j = appTokens.size() - 1; j >= 0; --j) {
- final AppWindowToken token = appTokens.get(j);
- if (!token.hasVisible && !mClosingApps.contains(token)
- && (!token.mIsExiting || token.isEmpty())) {
- // Make sure there is no animation running on this token, so any windows
+ final ArrayList<ActivityRecord> activities = mChildren.get(i).mExitingActivities;
+ for (int j = activities.size() - 1; j >= 0; --j) {
+ final ActivityRecord activity = activities.get(j);
+ if (!activity.hasVisible && !mClosingApps.contains(activity)
+ && (!activity.mIsExiting || activity.isEmpty())) {
+ // Make sure there is no animation running on this activity, so any windows
// associated with it will be removed as soon as their animations are
// complete.
cancelAnimation();
- if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
- "performLayout: App token exiting now removed" + token);
- token.removeIfPossible();
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE,
+ "performLayout: Activity exiting now removed %s", activity);
+ activity.removeIfPossible();
}
}
}
@@ -4241,15 +4264,15 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
final int orientation = super.getOrientation();
if (orientation != SCREEN_ORIENTATION_UNSET
&& orientation != SCREEN_ORIENTATION_BEHIND) {
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
- "App is requesting an orientation, return " + orientation
- + " for display id=" + mDisplayId);
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "App is requesting an orientation, return %d for display id=%d",
+ orientation, mDisplayId);
return orientation;
}
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
- "No app is requesting an orientation, return " + getLastOrientation()
- + " for display id=" + mDisplayId);
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "No app is requesting an orientation, return %d for display id=%d",
+ getLastOrientation(), mDisplayId);
// The next app has not been requested to be visible, so we keep the current orientation
// to prevent freezing/unfreezing the display too early.
return getLastOrientation();
@@ -4266,54 +4289,56 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
void assignStackOrdering(SurfaceControl.Transaction t) {
-
- final int HOME_STACK_STATE = 0;
- final int NORMAL_STACK_STATE = 1;
- final int ALWAYS_ON_TOP_STATE = 2;
+ if (getParent() == null) {
+ return;
+ }
+ mTmpAlwaysOnTopStacks.clear();
+ mTmpHomeStacks.clear();
+ mTmpNormalStacks.clear();
+ for (int i = 0; i < mChildren.size(); ++i) {
+ final TaskStack s = mChildren.get(i);
+ if (s.isAlwaysOnTop()) {
+ mTmpAlwaysOnTopStacks.add(s);
+ } else if (s.isActivityTypeHome()) {
+ mTmpHomeStacks.add(s);
+ } else {
+ mTmpNormalStacks.add(s);
+ }
+ }
int layer = 0;
- int layerForAnimationLayer = 0;
- int layerForBoostedAnimationLayer = 0;
- int layerForHomeAnimationLayer = 0;
-
- for (int state = 0; state <= ALWAYS_ON_TOP_STATE; state++) {
- for (int i = 0; i < mChildren.size(); i++) {
- final TaskStack s = mChildren.get(i);
- if (state == HOME_STACK_STATE && !s.isActivityTypeHome()) {
- continue;
- } else if (state == NORMAL_STACK_STATE && (s.isActivityTypeHome()
- || s.isAlwaysOnTop())) {
- continue;
- } else if (state == ALWAYS_ON_TOP_STATE && !s.isAlwaysOnTop()) {
- continue;
- }
- s.assignLayer(t, layer++);
- if (s.inSplitScreenWindowingMode() && mSplitScreenDividerAnchor != null) {
- t.setLayer(mSplitScreenDividerAnchor, layer++);
- }
- if ((s.isTaskAnimating() || s.isAppAnimating())
- && state != ALWAYS_ON_TOP_STATE) {
- // Ensure the animation layer ends up above the
- // highest animating stack and no higher.
- layerForAnimationLayer = layer++;
- }
- if (state != ALWAYS_ON_TOP_STATE) {
- layerForBoostedAnimationLayer = layer++;
- }
+ // Place home stacks to the bottom.
+ for (int i = 0; i < mTmpHomeStacks.size(); i++) {
+ mTmpHomeStacks.get(i).assignLayer(t, layer++);
+ }
+ // 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 TaskStack 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 (state == HOME_STACK_STATE) {
- layerForHomeAnimationLayer = layer++;
+ if (s.isTaskAnimating() || s.isAppAnimating()) {
+ // The animation layer is located above the highest animating stack and no
+ // higher.
+ layerForAnimationLayer = layer++;
}
}
- if (mAppAnimationLayer != null) {
- t.setLayer(mAppAnimationLayer, layerForAnimationLayer);
- }
- if (mBoostedAppAnimationLayer != null) {
- t.setLayer(mBoostedAppAnimationLayer, layerForBoostedAnimationLayer);
- }
- if (mHomeAppAnimationLayer != null) {
- t.setLayer(mHomeAppAnimationLayer, layerForHomeAnimationLayer);
+ // 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++);
}
+
+ t.setLayer(mHomeAppAnimationLayer, layerForHomeAnimationLayer);
+ t.setLayer(mAppAnimationLayer, layerForAnimationLayer);
+ t.setLayer(mSplitScreenDividerAnchor, layerForSplitScreenDividerAnchor);
+ t.setLayer(mBoostedAppAnimationLayer, layerForBoostedAnimationLayer);
}
@Override
@@ -4335,27 +4360,28 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
@Override
void onParentChanged() {
- super.onParentChanged();
if (getParent() != null) {
- mAppAnimationLayer = makeChildSurface(null)
- .setName("animationLayer")
- .build();
- mBoostedAppAnimationLayer = makeChildSurface(null)
- .setName("boostedAnimationLayer")
- .build();
- mHomeAppAnimationLayer = makeChildSurface(null)
- .setName("homeAnimationLayer")
- .build();
- mSplitScreenDividerAnchor = makeChildSurface(null)
- .setName("splitScreenDividerAnchor")
- .build();
- getPendingTransaction()
- .show(mAppAnimationLayer)
- .show(mBoostedAppAnimationLayer)
- .show(mHomeAppAnimationLayer)
- .show(mSplitScreenDividerAnchor);
- scheduleAnimation();
+ super.onParentChanged(() -> {
+ mAppAnimationLayer = makeChildSurface(null)
+ .setName("animationLayer")
+ .build();
+ mBoostedAppAnimationLayer = makeChildSurface(null)
+ .setName("boostedAnimationLayer")
+ .build();
+ mHomeAppAnimationLayer = makeChildSurface(null)
+ .setName("homeAnimationLayer")
+ .build();
+ mSplitScreenDividerAnchor = makeChildSurface(null)
+ .setName("splitScreenDividerAnchor")
+ .build();
+ getPendingTransaction()
+ .show(mAppAnimationLayer)
+ .show(mBoostedAppAnimationLayer)
+ .show(mHomeAppAnimationLayer)
+ .show(mSplitScreenDividerAnchor);
+ });
} else {
+ super.onParentChanged();
mWmService.mTransactionFactory.get()
.remove(mAppAnimationLayer)
.remove(mBoostedAppAnimationLayer)
@@ -4483,8 +4509,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return SCREEN_ORIENTATION_UNSET;
}
}
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, win + " forcing orientation to " + req
- + " for display id=" + mDisplayId);
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "%s forcing orientation to %d for display id=%d", win, req,
+ mDisplayId);
return (mLastWindowForcedOrientation = req);
}
@@ -4623,7 +4650,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// Keep IME window in mAboveAppWindowsContainers as long as app's starting window exists
// so it get's layered above the starting window.
if (imeTarget != null
- && !(imeTarget.mAppToken != null && imeTarget.mAppToken.hasStartingWindow())
+ && !(imeTarget.mActivityRecord != null && imeTarget.mActivityRecord.hasStartingWindow())
&& (!(imeTarget.inSplitScreenWindowingMode() || imeTarget.mToken.isAppAnimating())
&& (imeTarget.getSurfaceControl() != null))) {
mImeWindowsContainers.assignRelativeLayer(t, imeTarget.getSurfaceControl(),
@@ -4659,20 +4686,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
void prepareSurfaces() {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "prepareSurfaces");
try {
- final ScreenRotationAnimation screenRotationAnimation =
- mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
final Transaction transaction = getPendingTransaction();
- if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
- screenRotationAnimation.getEnterTransformation().getMatrix().getValues(mTmpFloats);
- transaction.setMatrix(mWindowingLayer,
- mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
- mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
- transaction.setPosition(mWindowingLayer,
- mTmpFloats[Matrix.MTRANS_X], mTmpFloats[Matrix.MTRANS_Y]);
- transaction.setAlpha(mWindowingLayer,
- screenRotationAnimation.getEnterTransformation().getAlpha());
- }
-
super.prepareSurfaces();
// TODO: Once we totally eliminate global transaction we will pass transaction in here
@@ -4752,10 +4766,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
void executeAppTransition() {
if (mAppTransition.isTransitionSet()) {
- if (DEBUG_APP_TRANSITIONS) {
- Slog.w(TAG_WM, "Execute app transition: " + mAppTransition + ", displayId: "
- + mDisplayId + " Callers=" + Debug.getCallers(5));
- }
+ ProtoLog.w(WM_DEBUG_APP_TRANSITIONS,
+ "Execute app transition: %s, displayId: %d Callers=%s",
+ mAppTransition, mDisplayId, Debug.getCallers(5));
mAppTransition.setReady();
mWmService.mWindowPlacerLocked.requestTraversal();
}
@@ -4902,6 +4915,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return mWindowingLayer;
}
+ SurfaceControl getOverlayLayer() {
+ return mOverlayLayer;
+ }
+
/**
* Updates the display's system gesture exclusion.
*
@@ -5143,7 +5160,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// Let surface flinger to set the display ID of this input window handle because we don't
// know which display the parent surface control is on.
final InputWindowHandle portalWindowHandle = new InputWindowHandle(
- null /* inputApplicationHandle */, null /* clientWindow */, INVALID_DISPLAY);
+ null /* inputApplicationHandle */, INVALID_DISPLAY);
portalWindowHandle.name = name;
portalWindowHandle.token = new Binder();
portalWindowHandle.layoutParamsFlags =
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 53092edd2a1e..2400a334d28d 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -16,9 +16,9 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
@@ -32,7 +32,11 @@ import static android.view.InsetsState.TYPE_TOP_GESTURES;
import static android.view.InsetsState.TYPE_TOP_TAPPABLE_ELEMENT;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_TOP_BAR;
+import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE;
+import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
@@ -99,9 +103,9 @@ import static com.android.server.policy.WindowManagerPolicy.TRANSIT_PREVIEW_DONE
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_SHOW;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
import static com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -133,6 +137,7 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.BoostFramework;
import android.util.ArraySet;
+import android.util.IntArray;
import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Slog;
@@ -149,6 +154,7 @@ import android.view.PointerIcon;
import android.view.Surface;
import android.view.View;
import android.view.ViewRootImpl;
+import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManagerGlobal;
@@ -162,6 +168,7 @@ import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.util.ScreenShapeHelper;
import com.android.internal.util.ScreenshotHelper;
import com.android.internal.util.function.TriConsumer;
+import com.android.internal.view.AppearanceRegion;
import com.android.internal.widget.PointerLocationView;
import com.android.server.LocalServices;
import com.android.server.UiThread;
@@ -171,6 +178,7 @@ import com.android.server.policy.WindowManagerPolicy.NavigationBarPosition;
import com.android.server.policy.WindowManagerPolicy.ScreenOnListener;
import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs;
import com.android.server.policy.WindowOrientationListener;
+import com.android.server.protolog.common.ProtoLog;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wallpaper.WallpaperManagerInternal;
import com.android.server.wm.utils.InsetUtils;
@@ -199,6 +207,11 @@ public class DisplayPolicy {
// Nav bar is never forced opaque.
private static final int NAV_BAR_FORCE_TRANSPARENT = 2;
+ /** Don't apply window animation (see {@link #selectAnimation}). */
+ static final int ANIMATION_NONE = -1;
+ /** Use the transit animation in style resource (see {@link #selectAnimation}). */
+ static final int ANIMATION_STYLEABLE = 0;
+
/**
* These are the system UI flags that, when changing, can cause the layout
* of the screen to change.
@@ -245,7 +258,7 @@ public class DisplayPolicy {
@Px
private int mSideGestureInset;
- private StatusBarManagerInternal getStatusBarManagerInternal() {
+ StatusBarManagerInternal getStatusBarManagerInternal() {
synchronized (mServiceAcquireLock) {
if (mStatusBarManagerInternal == null) {
mStatusBarManagerInternal =
@@ -337,14 +350,18 @@ public class DisplayPolicy {
private int mForceClearedSystemUiFlags = 0;
private int mLastFullscreenStackSysUiFlags;
private int mLastDockedStackSysUiFlags;
+ private int mLastAppearance;
+ private int mLastFullscreenAppearance;
+ private int mLastDockedAppearance;
private final Rect mNonDockedStackBounds = new Rect();
private final Rect mDockedStackBounds = new Rect();
private final Rect mLastNonDockedStackBounds = new Rect();
private final Rect mLastDockedStackBounds = new Rect();
- // What we last reported to system UI about whether the compatibility
- // menu needs to be displayed.
- private boolean mLastFocusNeedsMenu = false;
+ // What we last reported to system UI about whether the focused window is fullscreen/immersive.
+ private boolean mLastFocusIsFullscreen = false;
+ private boolean mLastFocusIsImmersive = false;
+
// If nonzero, a panic gesture was performed at that time in uptime millis and is still pending.
private long mPendingPanicGestureUptime;
@@ -909,19 +926,19 @@ public class DisplayPolicy {
/** Return false if it is not ready to turn on. */
public boolean finishScreenTurningOn() {
synchronized (mLock) {
- if (DEBUG_SCREEN_ON) Slog.d(TAG,
- "finishScreenTurningOn: mAwake=" + mAwake
- + ", mScreenOnEarly=" + mScreenOnEarly
- + ", mScreenOnFully=" + mScreenOnFully
- + ", mKeyguardDrawComplete=" + mKeyguardDrawComplete
- + ", mWindowManagerDrawComplete=" + mWindowManagerDrawComplete);
+ ProtoLog.d(WM_DEBUG_SCREEN_ON,
+ "finishScreenTurningOn: mAwake=%b, mScreenOnEarly=%b, "
+ + "mScreenOnFully=%b, mKeyguardDrawComplete=%b, "
+ + "mWindowManagerDrawComplete=%b",
+ mAwake, mScreenOnEarly, mScreenOnFully, mKeyguardDrawComplete,
+ mWindowManagerDrawComplete);
if (mScreenOnFully || !mScreenOnEarly || !mWindowManagerDrawComplete
|| (mAwake && !mKeyguardDrawComplete)) {
return false;
}
- if (DEBUG_SCREEN_ON) Slog.i(TAG, "Finished screen turning on...");
+ ProtoLog.i(WM_DEBUG_SCREEN_ON, "Finished screen turning on...");
mScreenOnListener = null;
mScreenOnFully = true;
}
@@ -1004,6 +1021,8 @@ public class DisplayPolicy {
if (canToastShowWhenLocked(callingPid)) {
attrs.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
}
+ // Toasts can't be clickable
+ attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
break;
}
@@ -1180,9 +1199,9 @@ public class DisplayPolicy {
}
/**
- * Control the animation to run when a window's state changes. Return a
- * non-0 number to force the animation to a specific resource ID, or 0
- * to use the default animation.
+ * Control the animation to run when a window's state changes. Return a positive number to
+ * force the animation to a specific resource ID, {@link #ANIMATION_STYLEABLE} to use the
+ * style resource defining the animation, or {@link #ANIMATION_NONE} for no animation.
*
* @param win The window that is changing.
* @param transit What is happening to the window:
@@ -1191,9 +1210,9 @@ public class DisplayPolicy {
* {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_SHOW}, or
* {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_HIDE}.
*
- * @return Resource ID of the actual animation to use, or 0 for none.
+ * @return Resource ID of the actual animation to use, or {@link #ANIMATION_NONE} for none.
*/
- public int selectAnimationLw(WindowState win, int transit) {
+ int selectAnimation(WindowState win, int transit) {
if (DEBUG_ANIM) Slog.i(TAG, "selectAnimation in " + win
+ ": transit=" + transit);
if (win == mStatusBar) {
@@ -1201,7 +1220,7 @@ public class DisplayPolicy {
final boolean expanded = win.getAttrs().height == MATCH_PARENT
&& win.getAttrs().width == MATCH_PARENT;
if (isKeyguard || expanded) {
- return -1;
+ return ANIMATION_NONE;
}
if (transit == TRANSIT_EXIT
|| transit == TRANSIT_HIDE) {
@@ -1212,7 +1231,7 @@ public class DisplayPolicy {
}
} else if (win == mNavigationBar) {
if (win.getAttrs().windowAnimations != 0) {
- return 0;
+ return ANIMATION_STYLEABLE;
}
// This can be on either the bottom or the right or the left.
if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
@@ -1245,7 +1264,7 @@ public class DisplayPolicy {
}
}
} else if (win.getAttrs().type == TYPE_DOCK_DIVIDER) {
- return selectDockedDividerAnimationLw(win, transit);
+ return selectDockedDividerAnimation(win, transit);
}
if (transit == TRANSIT_PREVIEW_DONE) {
@@ -1259,13 +1278,13 @@ public class DisplayPolicy {
// is shown. We don't want an animation on the dream, because
// we need it shown immediately with the keyguard animating away
// to reveal it.
- return -1;
+ return ANIMATION_NONE;
}
- return 0;
+ return ANIMATION_STYLEABLE;
}
- private int selectDockedDividerAnimationLw(WindowState win, int transit) {
+ private int selectDockedDividerAnimation(WindowState win, int transit) {
int insets = mDisplayContent.getDockedDividerController().getContentInsets();
// If the divider is behind the navigation bar, don't animate.
@@ -1284,14 +1303,14 @@ public class DisplayPolicy {
|| frame.bottom + insets >= win.getDisplayFrameLw().bottom);
final boolean offscreen = offscreenLandscape || offscreenPortrait;
if (behindNavBar || offscreen) {
- return 0;
+ return ANIMATION_STYLEABLE;
}
if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) {
return R.anim.fade_in;
} else if (transit == TRANSIT_EXIT) {
return R.anim.fade_out;
} else {
- return 0;
+ return ANIMATION_STYLEABLE;
}
}
@@ -2674,7 +2693,6 @@ public class DisplayPolicy {
changes |= FINISH_LAYOUT_REDO_LAYOUT;
}
} else if (topIsFullscreen
- && !mDisplayContent.isStackVisible(WINDOWING_MODE_FREEFORM)
&& !mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
if (DEBUG_LAYOUT) Slog.v(TAG, "** HIDING status bar");
if (mStatusBarController.setBarShowingLw(false)) {
@@ -2734,7 +2752,7 @@ public class DisplayPolicy {
* window.
*/
private boolean topAppHidesStatusBar() {
- if (mTopFullscreenOpaqueWindowState == null) {
+ if (mTopFullscreenOpaqueWindowState == null || mForceShowSystemBars) {
return false;
}
final int fl = PolicyControl.getWindowFlags(null,
@@ -3184,19 +3202,38 @@ public class DisplayPolicy {
// Swipe-up for navigation bar is disabled during setup
return;
}
- boolean sb = mStatusBarController.checkShowTransientBarLw();
- boolean nb = mNavigationBarController.checkShowTransientBarLw()
- && !isNavBarEmpty(mLastSystemUiFlags);
- if (sb || nb) {
- // Don't show status bar when swiping on already visible navigation bar
- if (!nb && swipeTarget == mNavigationBar) {
- if (DEBUG) Slog.d(TAG, "Not showing transient bar, wrong swipe target");
+ if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
+ if (swipeTarget == mNavigationBar
+ && !getInsetsPolicy().isHidden(InsetsState.TYPE_NAVIGATION_BAR)) {
+ // Don't show status bar when swiping on already visible navigation bar
+ return;
+ }
+ final InsetsControlTarget controlTarget =
+ swipeTarget.getControllableInsetProvider().getControlTarget();
+ if (controlTarget == null) {
return;
}
- if (sb) mStatusBarController.showTransient();
- if (nb) mNavigationBarController.showTransient();
+ if (controlTarget.canShowTransient()) {
+ mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap(
+ new int[]{TYPE_TOP_BAR, InsetsState.TYPE_NAVIGATION_BAR}));
+ } else {
+ controlTarget.showInsets(WindowInsets.Type.systemBars(), false);
+ }
+ } else {
+ boolean sb = mStatusBarController.checkShowTransientBarLw();
+ boolean nb = mNavigationBarController.checkShowTransientBarLw()
+ && !isNavBarEmpty(mLastSystemUiFlags);
+ if (sb || nb) {
+ // Don't show status bar when swiping on already visible navigation bar
+ if (!nb && swipeTarget == mNavigationBar) {
+ if (DEBUG) Slog.d(TAG, "Not showing transient bar, wrong swipe target");
+ return;
+ }
+ if (sb) mStatusBarController.showTransient();
+ if (nb) mNavigationBarController.showTransient();
+ updateSystemUiVisibilityLw();
+ }
mImmersiveModeConfirmation.confirmCurrentPrompt();
- updateSystemUiVisibilityLw();
}
}
}
@@ -3217,6 +3254,10 @@ public class DisplayPolicy {
return mService.mPolicy.isKeyguardOccluded();
}
+ InsetsPolicy getInsetsPolicy() {
+ return mDisplayContent.getInsetsPolicy();
+ }
+
void resetSystemUiVisibilityLw() {
mLastSystemUiFlags = 0;
updateSystemUiVisibilityLw();
@@ -3268,22 +3309,45 @@ public class DisplayPolicy {
&= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS);
}
+ final int appearance = win.mAttrs.insetsFlags.appearance;
final int fullscreenVisibility = updateLightStatusBarLw(0 /* vis */,
mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
final int dockedVisibility = updateLightStatusBarLw(0 /* vis */,
mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
- mService.getStackBounds(
- WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mNonDockedStackBounds);
+ final int fullscreenAppearance = updateLightStatusBarAppearanceLw(0 /* vis */,
+ mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
+ final int dockedAppearance = updateLightStatusBarAppearanceLw(0 /* vis */,
+ mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
mService.getStackBounds(
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds);
+ final boolean inSplitScreen = !mDockedStackBounds.isEmpty();
+ mService.getStackBounds(inSplitScreen ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ : WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_UNDEFINED, mNonDockedStackBounds);
final Pair<Integer, Boolean> result =
updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
final int visibility = result.first;
final int diff = visibility ^ mLastSystemUiFlags;
final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags;
final int dockedDiff = dockedVisibility ^ mLastDockedStackSysUiFlags;
- final boolean needsMenu = win.getNeedsMenuLw(mTopFullscreenOpaqueWindowState);
- if (diff == 0 && fullscreenDiff == 0 && dockedDiff == 0 && mLastFocusNeedsMenu == needsMenu
+ final InsetsPolicy insetsPolicy = getInsetsPolicy();
+ 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(TYPE_TOP_BAR))
+ || (mNavigationBar != null && insetsPolicy.isHidden(
+ InsetsState.TYPE_NAVIGATION_BAR));
+ final int behavior = win.mAttrs.insetsFlags.behavior;
+ final boolean isImmersive = (visibility & (View.SYSTEM_UI_FLAG_IMMERSIVE
+ | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)) != 0
+ || behavior == BEHAVIOR_SHOW_BARS_BY_SWIPE
+ || behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+ if (diff == 0 && fullscreenDiff == 0 && dockedDiff == 0
+ && mLastAppearance == appearance
+ && mLastFullscreenAppearance == fullscreenAppearance
+ && mLastDockedAppearance == dockedAppearance
+ && mLastFocusIsFullscreen == isFullscreen
+ && mLastFocusIsImmersive == isImmersive
&& mFocusedApp == win.getAppToken()
&& mLastNonDockedStackBounds.equals(mNonDockedStackBounds)
&& mLastDockedStackBounds.equals(mDockedStackBounds)) {
@@ -3292,21 +3356,40 @@ public class DisplayPolicy {
mLastSystemUiFlags = visibility;
mLastFullscreenStackSysUiFlags = fullscreenVisibility;
mLastDockedStackSysUiFlags = dockedVisibility;
- mLastFocusNeedsMenu = needsMenu;
+ mLastAppearance = appearance;
+ mLastFullscreenAppearance = fullscreenAppearance;
+ mLastDockedAppearance = dockedAppearance;
+ mLastFocusIsFullscreen = isFullscreen;
+ mLastFocusIsImmersive = isImmersive;
mFocusedApp = win.getAppToken();
mLastNonDockedStackBounds.set(mNonDockedStackBounds);
mLastDockedStackBounds.set(mDockedStackBounds);
final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds);
final Rect dockedStackBounds = new Rect(mDockedStackBounds);
+ final AppearanceRegion[] appearanceRegions = inSplitScreen
+ ? new AppearanceRegion[]{
+ new AppearanceRegion(fullscreenAppearance, fullscreenStackBounds),
+ new AppearanceRegion(dockedAppearance, dockedStackBounds)}
+ : new AppearanceRegion[]{
+ new AppearanceRegion(fullscreenAppearance, fullscreenStackBounds)};
final boolean isNavbarColorManagedByIme = result.second;
mHandler.post(() -> {
StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
if (statusBar != null) {
final int displayId = getDisplayId();
+ // TODO(b/118118435): disabled flags only
statusBar.setSystemUiVisibility(displayId, visibility, fullscreenVisibility,
dockedVisibility, 0xffffffff, fullscreenStackBounds,
dockedStackBounds, isNavbarColorManagedByIme, win.toString());
- statusBar.topAppWindowChanged(displayId, needsMenu);
+ if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
+ statusBar.onSystemBarAppearanceChanged(displayId, appearance,
+ appearanceRegions, isNavbarColorManagedByIme);
+ }
+ statusBar.topAppWindowChanged(displayId, isFullscreen, isImmersive);
+
+ // TODO(b/118118435): Remove this after removing system UI visibilities.
+ mDisplayContent.statusBarVisibilityChanged(
+ visibility & ~(View.STATUS_BAR_UNHIDE | View.NAVIGATION_BAR_UNHIDE));
}
});
return diff;
@@ -3328,6 +3411,22 @@ public class DisplayPolicy {
return vis;
}
+ private int updateLightStatusBarAppearanceLw(int appearance, WindowState opaque,
+ WindowState opaqueOrDimming) {
+ final boolean onKeyguard = isStatusBarKeyguard() && !isKeyguardOccluded();
+ final WindowState statusColorWin = onKeyguard ? mStatusBar : 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_TOP_BAR;
+ appearance |= statusColorWin.mAttrs.insetsFlags.appearance & APPEARANCE_LIGHT_TOP_BAR;
+ } else if (statusColorWin != null && statusColorWin.isDimming()) {
+ // Otherwise if it's dimming, clear the light flag.
+ appearance &= ~APPEARANCE_LIGHT_TOP_BAR;
+ }
+ return appearance;
+ }
+
@VisibleForTesting
@Nullable
static WindowState chooseNavigationColorWindowLw(WindowState opaque,
@@ -3396,9 +3495,9 @@ public class DisplayPolicy {
final boolean resizing = mDisplayContent.getDockedDividerController().isResizing();
// 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
+ // is focused but also when we are resizing for the transitions when docked stack
// visibility changes.
- mForceShowSystemBars = dockedStackVisible || freeformStackVisible || resizing
+ mForceShowSystemBars = dockedStackVisible || win.inFreeformWindowingMode() || resizing
|| mForceShowSystemBarsFromExternal;
final boolean forceOpaqueStatusBar = mForceShowSystemBars && !mForceStatusBarFromKeyguard;
@@ -3649,6 +3748,8 @@ public class DisplayPolicy {
mPendingPanicGestureUptime = SystemClock.uptimeMillis();
if (!isNavBarEmpty(mLastSystemUiFlags)) {
mNavigationBarController.showTransient();
+ mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap(
+ new int[] {InsetsState.TYPE_NAVIGATION_BAR}));
}
}
}
@@ -3726,9 +3827,6 @@ public class DisplayPolicy {
pw.print(" mForceClearedSystemUiFlags=0x");
pw.println(Integer.toHexString(mForceClearedSystemUiFlags));
}
- if (mLastFocusNeedsMenu) {
- pw.print(prefix); pw.print("mLastFocusNeedsMenu="); pw.println(mLastFocusNeedsMenu);
- }
pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream);
pw.print(" mDreamingLockscreen="); pw.print(mDreamingLockscreen);
pw.print(" mDreamingSleepToken="); pw.println(mDreamingSleepToken);
@@ -3845,7 +3943,7 @@ public class DisplayPolicy {
@VisibleForTesting
static boolean isOverlappingWithNavBar(WindowState targetWindow, WindowState navBarWindow) {
if (navBarWindow == null || !navBarWindow.isVisibleLw()
- || targetWindow.mAppToken == null || !targetWindow.isVisibleLw()) {
+ || targetWindow.mActivityRecord == null || !targetWindow.isVisibleLw()) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index a628b6a58b5a..10a6d0ca0a54 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -23,8 +23,8 @@ import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_ACTIVE;
@@ -60,6 +60,7 @@ import com.android.server.LocalServices;
import com.android.server.UiThread;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.policy.WindowOrientationListener;
+import com.android.server.protolog.common.ProtoLog;
import com.android.server.statusbar.StatusBarManagerInternal;
import java.io.PrintWriter;
@@ -459,24 +460,24 @@ public class DisplayRotation {
if (mDeferredRotationPauseCount > 0) {
// Rotation updates have been paused temporarily. Defer the update until updates
// have been resumed.
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, rotation is paused.");
+ ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, rotation is paused.");
return false;
}
final ScreenRotationAnimation screenRotationAnimation =
- mService.mAnimator.getScreenRotationAnimationLocked(displayId);
+ mDisplayContent.getRotationAnimation();
if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
// Rotation updates cannot be performed while the previous rotation change animation
// is still in progress. Skip this update. We will try updating again after the
// animation is finished and the display is unfrozen.
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, animation in progress.");
+ ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, animation in progress.");
return false;
}
if (mService.mDisplayFrozen) {
// Even if the screen rotation animation has finished (e.g. isAnimating returns
// false), there is still some time where we haven't yet unfrozen the display. We
// also need to abort rotation here.
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
"Deferring rotation, still finishing previous rotation");
return false;
}
@@ -484,30 +485,30 @@ public class DisplayRotation {
if (!mService.mDisplayEnabled) {
// No point choosing a rotation if the display is not enabled.
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, display is not enabled.");
+ ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, display is not enabled.");
return false;
}
final int oldRotation = mRotation;
final int lastOrientation = mLastOrientation;
final int rotation = rotationForOrientation(lastOrientation, oldRotation);
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Computed rotation=" + rotation + " for display id="
- + displayId + " based on lastOrientation=" + lastOrientation
- + " and oldRotation=" + oldRotation);
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "Computed rotation=%d for display id=%d based on lastOrientation=%d and "
+ + "oldRotation=%d",
+ rotation, displayId, lastOrientation, oldRotation);
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + displayId
- + " selected orientation " + lastOrientation
- + ", got rotation " + rotation);
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "Display id=%d selected orientation %d, got rotation %d", displayId,
+ lastOrientation, rotation);
if (oldRotation == rotation) {
// No change.
return false;
}
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + displayId
- + " rotation changed to " + rotation
- + " from " + oldRotation
- + ", lastOrientation=" + lastOrientation);
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "Display id=%d rotation changed to %d from %d, lastOrientation=%d",
+ displayId, rotation, oldRotation, lastOrientation);
if (DisplayContent.deltaRotation(rotation, oldRotation) != 2) {
mDisplayContent.mWaitingForConfig = true;
@@ -579,7 +580,7 @@ public class DisplayRotation {
// If the bounds of activity window is different from its parent, then reject to be seamless
// because the window position may change after rotation that will look like a sudden jump.
- if (w.mAppToken != null && !w.mAppToken.matchParentBounds()) {
+ if (w.mActivityRecord != null && !w.mActivityRecord.matchParentBounds()) {
return false;
}
@@ -611,9 +612,8 @@ public class DisplayRotation {
mSeamlessRotationCount--;
}
if (mSeamlessRotationCount == 0) {
- if (DEBUG_ORIENTATION) {
- Slog.i(TAG, "Performing post-rotate rotation after seamless rotation");
- }
+ ProtoLog.i(WM_DEBUG_ORIENTATION,
+ "Performing post-rotate rotation after seamless rotation");
// Finish seamless rotation.
mRotatingSeamlessly = false;
@@ -904,11 +904,12 @@ public class DisplayRotation {
// Could have been invoked due to screen turning on or off or
// change of the currently visible window's orientation.
- if (DEBUG_ORIENTATION) Slog.v(TAG, "screenOnEarly=" + screenOnEarly
- + ", awake=" + awake + ", currentAppOrientation=" + mCurrentAppOrientation
- + ", orientationSensorEnabled=" + mOrientationListener.mEnabled
- + ", keyguardDrawComplete=" + keyguardDrawComplete
- + ", windowManagerDrawComplete=" + windowManagerDrawComplete);
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "screenOnEarly=%b, awake=%b, currentAppOrientation=%d, "
+ + "orientationSensorEnabled=%b, keyguardDrawComplete=%b, "
+ + "windowManagerDrawComplete=%b",
+ screenOnEarly, awake, mCurrentAppOrientation, mOrientationListener.mEnabled,
+ keyguardDrawComplete, windowManagerDrawComplete);
boolean disable = true;
// Note: We postpone the rotating of the screen until the keyguard as well as the
@@ -1012,14 +1013,11 @@ public class DisplayRotation {
*/
@VisibleForTesting
int rotationForOrientation(int orientation, int lastRotation) {
- if (DEBUG_ORIENTATION) {
- Slog.v(TAG, "rotationForOrientation(orient="
- + orientation + ", last=" + lastRotation
- + "); user=" + mUserRotation + " "
- + (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
- ? "USER_ROTATION_LOCKED" : "")
+ ProtoLog.v(WM_DEBUG_ORIENTATION, "rotationForOrientation(orient=%d, last=%d); user=%d %s",
+ orientation, lastRotation, mUserRotation,
+ mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
+ ? "USER_ROTATION_LOCKED" : ""
);
- }
if (isFixedToUserRotation()) {
return mUserRotation;
@@ -1122,11 +1120,19 @@ public class DisplayRotation {
preferredRotation = lastRotation;
}
} else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
- && orientation != ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
- // Apply rotation lock. Does not apply to NOSENSOR.
+ && orientation != ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
+ && orientation != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+ && orientation != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
+ && orientation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
+ && orientation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT) {
+ // Apply rotation lock. Does not apply to NOSENSOR or specific rotations.
// The idea is that the user rotation expresses a weak preference for the direction
// of gravity and as NOSENSOR is never affected by gravity, then neither should
// NOSENSOR be affected by rotation lock (although it will be affected by docks).
+ // Also avoid setting user rotation when app has preference over one particular rotation
+ // to avoid leaving the rotation to the reverse of it which has the compatible
+ // orientation, but isn't what app wants, when the user rotation is the reverse of the
+ // preferred rotation.
preferredRotation = mUserRotation;
} else {
// No overriding preference.
@@ -1435,7 +1441,7 @@ public class DisplayRotation {
@Override
public void onProposedRotationChanged(int rotation) {
- if (DEBUG_ORIENTATION) Slog.v(TAG, "onProposedRotationChanged, rotation=" + rotation);
+ ProtoLog.v(WM_DEBUG_ORIENTATION, "onProposedRotationChanged, rotation=%d", rotation);
Runnable r = mRunnableCache.get(rotation, null);
if (r == null) {
r = new UpdateRunnable(rotation);
@@ -1448,14 +1454,14 @@ public class DisplayRotation {
public void enable(boolean clearCurrentRotation) {
super.enable(clearCurrentRotation);
mEnabled = true;
- if (DEBUG_ORIENTATION) Slog.v(TAG, "Enabling listeners");
+ ProtoLog.v(WM_DEBUG_ORIENTATION, "Enabling listeners");
}
@Override
public void disable() {
super.disable();
mEnabled = false;
- if (DEBUG_ORIENTATION) Slog.v(TAG, "Disabling listeners");
+ ProtoLog.v(WM_DEBUG_ORIENTATION, "Disabling listeners");
}
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index ae3b5f2f70d3..b454922ae7cd 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -660,7 +660,7 @@ public class DockedStackDividerController {
checkMinimizeChanged(false /* animate */);
}
- void notifyAppTransitionStarting(ArraySet<AppWindowToken> openingApps, int appTransition) {
+ void notifyAppTransitionStarting(ArraySet<ActivityRecord> openingApps, int appTransition) {
final boolean wasMinimized = mMinimizedDock;
checkMinimizeChanged(true /* animate */);
@@ -685,10 +685,10 @@ public class DockedStackDividerController {
/**
* @return true if {@param apps} contains an activity in the docked stack, false otherwise.
*/
- private boolean containsAppInDockedStack(ArraySet<AppWindowToken> apps) {
+ private boolean containsAppInDockedStack(ArraySet<ActivityRecord> apps) {
for (int i = apps.size() - 1; i >= 0; i--) {
- final AppWindowToken token = apps.valueAt(i);
- if (token.getTask() != null && token.inSplitScreenPrimaryWindowingMode()) {
+ final ActivityRecord activity = apps.valueAt(i);
+ if (activity.getTask() != null && activity.inSplitScreenPrimaryWindowingMode()) {
return true;
}
}
@@ -722,7 +722,7 @@ public class DockedStackDividerController {
final RecentsAnimationController recentsAnim = mService.getRecentsAnimationController();
final boolean minimizedForRecentsAnimation = recentsAnim != null &&
recentsAnim.isSplitScreenMinimized();
- boolean homeVisible = homeTask.getTopVisibleAppToken() != null;
+ boolean homeVisible = homeTask.getTopVisibleActivity() != null;
if (homeVisible && topSecondaryStack != null) {
// Home should only be considered visible if it is greater or equal to the top secondary
// stack in terms of z-order.
@@ -819,9 +819,12 @@ public class DockedStackDividerController {
// We put all tasks into drag resizing mode - wait until all of them have completed the
// drag resizing switch.
- if (!mService.mWaitingForDrawn.isEmpty()) {
- mService.mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
- mService.mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT,
+ final Runnable existingWaitingForDrwanCallback =
+ mService.mWaitingForDrawnCallbacks.get(mService.mRoot);
+ if (existingWaitingForDrwanCallback != null) {
+ mService.mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, mService.mRoot);
+ mService.mH.sendMessageDelayed(mService.mH.obtainMessage(H.WAITING_FOR_DRAWN_TIMEOUT,
+ mService.mRoot),
IME_ADJUST_DRAWN_TIMEOUT);
mAnimationStartDelayed = true;
if (imeWin != null) {
@@ -838,10 +841,8 @@ public class DockedStackDividerController {
// still gets executed.
// TODO: Have a real system where we can wait on different windows to be drawn with
// different callbacks.
- if (mService.mWaitingForDrawnCallback != null) {
- mService.mWaitingForDrawnCallback.run();
- }
- mService.mWaitingForDrawnCallback = () -> {
+ existingWaitingForDrwanCallback.run();
+ mService.mWaitingForDrawnCallbacks.put(mService.mRoot, () -> {
synchronized (mService.mGlobalLock) {
mAnimationStartDelayed = false;
if (mDelayedImeWin != null) {
@@ -863,7 +864,7 @@ public class DockedStackDividerController {
notifyAdjustedForImeChanged(
mAdjustedForIme || mAdjustedForDivider, duration);
}
- };
+ });
} else {
notifyAdjustedForImeChanged(
adjustedForIme || adjustedForDivider, IME_ADJUST_ANIM_DURATION);
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index f8f6334b04dc..d5f403f85621 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -113,7 +113,7 @@ class DragDropController {
final WindowState callingWin = mService.windowForClientLocked(
null, window, false);
- if (callingWin == null) {
+ if (callingWin == null || callingWin.cantReceiveTouchInput()) {
Slog.w(TAG_WM, "Bad requesting window " + window);
return null; // !!! TODO: throw here?
}
@@ -167,8 +167,7 @@ class DragDropController {
final SurfaceControl surfaceControl = mDragState.mSurfaceControl;
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> OPEN TRANSACTION performDrag");
- final SurfaceControl.Transaction transaction =
- callingWin.getPendingTransaction();
+ final SurfaceControl.Transaction transaction = mDragState.mTransaction;
transaction.setAlpha(surfaceControl, mDragState.mOriginalAlpha);
transaction.setPosition(
surfaceControl, touchX - thumbCenterX, touchY - thumbCenterY);
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 17daabfe3e79..255ef6ec3e55 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -19,10 +19,10 @@ package com.android.server.wm;
import static com.android.server.wm.DragDropController.MSG_ANIMATION_END;
import static com.android.server.wm.DragDropController.MSG_DRAG_END_TIMEOUT;
import static com.android.server.wm.DragDropController.MSG_TEAR_DOWN_DRAG_AND_DROP_INPUT;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
+import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.animation.Animator;
@@ -58,6 +58,7 @@ import android.view.animation.Interpolator;
import com.android.internal.view.IDragAndDropPermissions;
import com.android.server.LocalServices;
+import com.android.server.protolog.common.ProtoLog;
import java.util.ArrayList;
@@ -120,7 +121,7 @@ class DragState {
// A surface used to catch input events for the drag-and-drop operation.
SurfaceControl mInputSurface;
- private final SurfaceControl.Transaction mTransaction;
+ final SurfaceControl.Transaction mTransaction;
private final Rect mTmpClipRect = new Rect();
@@ -129,7 +130,6 @@ class DragState {
* {@code true} when {@link #closeLocked()} is called.
*/
private boolean mIsClosing;
- IBinder mTransferTouchFromToken;
DragState(WindowManagerService service, DragDropController controller, IBinder token,
SurfaceControl surface, int flags, IBinder localWin) {
@@ -167,12 +167,11 @@ class DragState {
mTmpClipRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
mTransaction.setWindowCrop(mInputSurface, mTmpClipRect);
- mTransaction.transferTouchFocus(mTransferTouchFromToken, h.token);
- mTransferTouchFromToken = null;
- // syncInputWindows here to ensure the input channel isn't removed before the transfer.
+ // syncInputWindows here to ensure the input window info is sent before the
+ // transferTouchFocus is called.
mTransaction.syncInputWindows();
- mTransaction.apply();
+ mTransaction.apply(true);
}
/**
@@ -264,7 +263,7 @@ class DragState {
InputChannel[] channels = InputChannel.openInputChannelPair("drag");
mServerChannel = channels[0];
mClientChannel = channels[1];
- mService.mInputManager.registerInputChannel(mServerChannel, null);
+ mService.mInputManager.registerInputChannel(mServerChannel);
mInputEventReceiver = new DragInputEventReceiver(mClientChannel,
mService.mH.getLooper(), mDragDropController);
@@ -273,7 +272,7 @@ class DragState {
mDragApplicationHandle.dispatchingTimeoutNanos =
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
- mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
+ mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle,
display.getDisplayId());
mDragWindowHandle.name = "drag";
mDragWindowHandle.token = mServerChannel.getToken();
@@ -302,9 +301,7 @@ class DragState {
mDragWindowHandle.frameBottom = mDisplaySize.y;
// Pause rotations before a drag.
- if (DEBUG_ORIENTATION) {
- Slog.d(TAG_WM, "Pausing rotation during drag");
- }
+ ProtoLog.d(WM_DEBUG_ORIENTATION, "Pausing rotation during drag");
mDisplayContent.getDisplayRotation().pause();
}
@@ -321,9 +318,7 @@ class DragState {
mDragApplicationHandle = null;
// Resume rotations after a drag.
- if (DEBUG_ORIENTATION) {
- Slog.d(TAG_WM, "Resuming rotation after drag");
- }
+ ProtoLog.d(WM_DEBUG_ORIENTATION, "Resuming rotation after drag");
mDisplayContent.getDisplayRotation().resume();
}
}
@@ -432,8 +427,8 @@ class DragState {
private boolean targetWindowSupportsGlobalDrag(WindowState targetWin) {
// Global drags are limited to system windows, and windows for apps that are targeting N and
// above.
- return targetWin.mAppToken == null
- || targetWin.mAppToken.mTargetSdk >= Build.VERSION_CODES.N;
+ return targetWin.mActivityRecord == null
+ || targetWin.mActivityRecord.mTargetSdk >= Build.VERSION_CODES.N;
}
/* helper - send a ACTION_DRAG_STARTED event only if the window has not
@@ -501,10 +496,9 @@ class DragState {
Slog.i(TAG_WM, ">>> OPEN TRANSACTION notifyMoveLocked");
}
mTransaction.setPosition(mSurfaceControl, x - mThumbOffsetX, y - mThumbOffsetY).apply();
- if (SHOW_TRANSACTIONS) {
- Slog.i(TAG_WM, " DRAG " + mSurfaceControl + ": pos=(" + (int) (x - mThumbOffsetX) + ","
- + (int) (y - mThumbOffsetY) + ")");
- }
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "DRAG %s: pos=(%d,%d)", mSurfaceControl,
+ (int) (x - mThumbOffsetX), (int) (y - mThumbOffsetY));
+
notifyLocationLocked(x, y);
}
diff --git a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
index f64592fd46bd..2165b0e8b593 100644
--- a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
+++ b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
@@ -50,7 +50,7 @@ class EmulatorDisplayOverlay {
private boolean mVisible;
EmulatorDisplayOverlay(Supplier<Surface> surfaceFactory, Context context, DisplayContent dc,
- int zOrder) {
+ int zOrder, SurfaceControl.Transaction t) {
mSurface = surfaceFactory.get();
final Display display = dc.getDisplay();
mScreenSize = new Point();
@@ -63,9 +63,9 @@ class EmulatorDisplayOverlay {
.setBufferSize(mScreenSize.x, mScreenSize.y)
.setFormat(PixelFormat.TRANSLUCENT)
.build();
- ctrl.setLayer(zOrder);
- ctrl.setPosition(0, 0);
- ctrl.show();
+ t.setLayer(ctrl, zOrder);
+ t.setPosition(ctrl, 0, 0);
+ t.show(ctrl);
mSurface.copyFrom(ctrl);
} catch (OutOfResourcesException e) {
}
@@ -75,7 +75,7 @@ class EmulatorDisplayOverlay {
com.android.internal.R.drawable.emulator_circular_window_overlay);
}
- private void drawIfNeeded() {
+ private void drawIfNeeded(SurfaceControl.Transaction t) {
if (!mDrawNeeded || !mVisible) {
return;
}
@@ -92,7 +92,7 @@ class EmulatorDisplayOverlay {
return;
}
c.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SRC);
- mSurfaceControl.setPosition(0, 0);
+ t.setPosition(mSurfaceControl, 0, 0);
// Always draw the overlay with square dimensions
int size = Math.max(mScreenSize.x, mScreenSize.y);
mOverlay.setBounds(0, 0, size, size);
@@ -102,20 +102,20 @@ class EmulatorDisplayOverlay {
// Note: caller responsible for being inside
// Surface.openTransaction() / closeTransaction()
- public void setVisibility(boolean on) {
+ public void setVisibility(boolean on, SurfaceControl.Transaction t) {
if (mSurfaceControl == null) {
return;
}
mVisible = on;
- drawIfNeeded();
+ drawIfNeeded(t);
if (on) {
- mSurfaceControl.show();
+ t.show(mSurfaceControl);
} else {
- mSurfaceControl.hide();
+ t.hide(mSurfaceControl);
}
}
- void positionSurface(int dw, int dh, int rotation) {
+ void positionSurface(int dw, int dh, int rotation, SurfaceControl.Transaction t) {
if (mLastDW == dw && mLastDH == dh && mRotation == rotation) {
return;
}
@@ -123,7 +123,7 @@ class EmulatorDisplayOverlay {
mLastDH = dh;
mDrawNeeded = true;
mRotation = rotation;
- drawIfNeeded();
+ drawIfNeeded(t);
}
}
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
new file mode 100644
index 000000000000..bc95481dcee9
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.view.InsetsSource;
+import android.view.WindowInsets;
+
+/**
+ * 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 Runnable mShowImeRunner;
+ private boolean mIsImeLayoutDrawn;
+
+ ImeInsetsSourceProvider(InsetsSource source,
+ InsetsStateController stateController, DisplayContent displayContent) {
+ super(source, stateController, displayContent);
+ }
+
+ /**
+ * Called when a layout pass has occurred.
+ */
+ void onPostLayout() {
+ super.onPostLayout();
+
+ if (mImeTargetFromIme != null
+ && isImeTargetFromDisplayContentAndImeSame()
+ && mWin != null
+ && mWin.isDrawnLw()
+ && !mWin.mGivenInsetsPending) {
+ mIsImeLayoutDrawn = true;
+ }
+ }
+
+ /**
+ * Called when Insets have been dispatched to client.
+ */
+ void onPostInsetsDispatched() {
+ if (mIsImeLayoutDrawn && mShowImeRunner != null) {
+ // Show IME if InputMethodService requested to be shown and it's layout has finished.
+ mShowImeRunner.run();
+ mIsImeLayoutDrawn = false;
+ mShowImeRunner = null;
+ }
+ }
+
+ /**
+ * Called from {@link WindowManagerInternal#showImePostLayout} when {@link InputMethodService}
+ * requests to show IME on {@param imeTarget}.
+ * @param imeTarget imeTarget on which IME request is coming from.
+ */
+ void scheduleShowImePostLayout(WindowState imeTarget) {
+ mImeTargetFromIme = imeTarget;
+ mShowImeRunner = () -> {
+ // Target should still be the same.
+ if (isImeTargetFromDisplayContentAndImeSame()) {
+ mDisplayContent.mInputMethodTarget.showInsets(
+ WindowInsets.Type.ime(), true /* fromIme */);
+ }
+ mImeTargetFromIme = null;
+ };
+ }
+
+ private boolean isImeTargetFromDisplayContentAndImeSame() {
+ // IMMS#mLastImeTargetWindow always considers focused window as
+ // IME target, however DisplayContent#computeImeTarget() can compute
+ // a different IME target.
+ // Refer to WindowManagerService#applyImeVisibility(token, false).
+ // If IMMS's imeTarget is child of DisplayContent's imeTarget and child window
+ // is above the parent, we will consider it as the same target for now.
+ // TODO(b/139861270): Remove the child & sublayer check once IMMS is aware of
+ // actual IME target.
+ return mImeTargetFromIme == mDisplayContent.mInputMethodTarget
+ || (mDisplayContent.mInputMethodTarget.getParentWindow() == mImeTargetFromIme
+ && mDisplayContent.mInputMethodTarget.mSubLayer
+ > mImeTargetFromIme.mSubLayer);
+ }
+
+}
diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index d774dc3fd2f1..82ac6b897a8f 100644
--- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -189,7 +189,7 @@ public class ImmersiveModeConfirmation {
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT);
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("ImmersiveModeConfirmation");
lp.windowAnimations = com.android.internal.R.style.Animation_ImmersiveModeConfirmation;
lp.token = getWindowToken();
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 8dae0163b612..ebe9f08f83b7 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -65,14 +65,14 @@ class InputConsumerImpl implements IBinder.DeathRecipient {
} else {
mClientChannel = channels[1];
}
- mService.mInputManager.registerInputChannel(mServerChannel, null);
+ mService.mInputManager.registerInputChannel(mServerChannel);
mApplicationHandle = new InputApplicationHandle(new Binder());
mApplicationHandle.name = name;
mApplicationHandle.dispatchingTimeoutNanos =
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
- mWindowHandle = new InputWindowHandle(mApplicationHandle, null, displayId);
+ mWindowHandle = new InputWindowHandle(mApplicationHandle, displayId);
mWindowHandle.name = name;
mWindowHandle.token = mServerChannel.getToken();
mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 6830adebad22..7f9e76b0c7e0 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -5,20 +5,25 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.ON_POINTER_DOWN_OUTSIDE_FOCUS;
import android.os.Debug;
import android.os.IBinder;
+import android.os.RemoteException;
import android.util.Slog;
+import android.view.IWindow;
import android.view.KeyEvent;
import android.view.WindowManager;
import com.android.server.input.InputManagerService;
import java.io.PrintWriter;
+import java.util.concurrent.atomic.AtomicReference;
final class InputManagerCallback implements InputManagerService.WindowManagerCallbacks {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "InputManagerCallback" : TAG_WM;
private final WindowManagerService mService;
// Set to true when the first input device configuration change notification
@@ -37,6 +42,13 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
// which point the ActivityManager will enable dispatching.
private boolean mInputDispatchEnabled;
+ // TODO(b/141749603)) investigate if this can be part of client focus change dispatch
+ // Tracks the currently focused window used to update pointer capture state in clients
+ private AtomicReference<IWindow> mFocusedWindow = new AtomicReference<>();
+
+ // Tracks focused window pointer capture state
+ private boolean mFocusedWindowHasCapture;
+
public InputManagerCallback(WindowManagerService service) {
mService = service;
}
@@ -53,7 +65,7 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
}
synchronized (mService.mGlobalLock) {
- WindowState windowState = mService.windowForClientLocked(null, token, false);
+ WindowState windowState = mService.mInputToWindowMap.get(token);
if (windowState != null) {
Slog.i(TAG_WM, "WINDOW DIED " + windowState);
windowState.removeIfPossible();
@@ -69,14 +81,15 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
*/
@Override
public long notifyANR(IBinder token, String reason) {
- AppWindowToken appWindowToken = null;
+ ActivityRecord activity = null;
WindowState windowState = null;
boolean aboveSystem = false;
+ //TODO(b/141764879) Limit scope of wm lock when input calls notifyANR
synchronized (mService.mGlobalLock) {
if (token != null) {
- windowState = mService.windowForClientLocked(null, token, false);
+ windowState = mService.mInputToWindowMap.get(token);
if (windowState != null) {
- appWindowToken = windowState.mAppToken;
+ activity = windowState.mActivityRecord;
}
}
@@ -90,30 +103,30 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
int systemAlertLayer = mService.mPolicy.getWindowLayerFromTypeLw(
TYPE_APPLICATION_OVERLAY, windowState.mOwnerCanAddInternalSystemWindow);
aboveSystem = windowState.mBaseLayer > systemAlertLayer;
- } else if (appWindowToken != null) {
+ } else if (activity != null) {
Slog.i(TAG_WM, "Input event dispatching timed out "
- + "sending to application " + appWindowToken.stringName
+ + "sending to application " + activity.stringName
+ ". Reason: " + reason);
} else {
Slog.i(TAG_WM, "Input event dispatching timed out "
+ ". Reason: " + reason);
}
- mService.saveANRStateLocked(appWindowToken, windowState, reason);
+ mService.saveANRStateLocked(activity, windowState, reason);
}
// All the calls below need to happen without the WM lock held since they call into AM.
mService.mAtmInternal.saveANRState(reason);
- if (appWindowToken != null && appWindowToken.appToken != null) {
+ if (activity != null && activity.appToken != null) {
// Notify the activity manager about the timeout and let it decide whether
// to abort dispatching or keep waiting.
- final boolean abort = appWindowToken.keyDispatchingTimedOut(reason,
- (windowState != null) ? windowState.mSession.mPid : -1);
+ final boolean abort = activity.keyDispatchingTimedOut(reason,
+ windowState.mSession.mPid);
if (!abort) {
// The activity manager declined to abort dispatching.
// Wait a bit longer and timeout again later.
- return appWindowToken.mInputDispatchingTimeoutNanos;
+ return activity.mInputDispatchingTimeoutNanos;
}
} else if (windowState != null) {
// Notify the activity manager about the timeout and let it decide whether
@@ -181,9 +194,8 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
*/
@Override
public long interceptKeyBeforeDispatching(
- IBinder focus, KeyEvent event, int policyFlags) {
- WindowState windowState = mService.windowForClientLocked(null, focus, false);
- return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
+ IBinder focusedToken, KeyEvent event, int policyFlags) {
+ return mService.mPolicy.interceptKeyBeforeDispatching(focusedToken, event, policyFlags);
}
/**
@@ -192,9 +204,8 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
*/
@Override
public KeyEvent dispatchUnhandledKey(
- IBinder focus, KeyEvent event, int policyFlags) {
- WindowState windowState = mService.windowForClientLocked(null, focus, false);
- return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags);
+ IBinder focusedToken, KeyEvent event, int policyFlags) {
+ return mService.mPolicy.dispatchUnhandledKey(focusedToken, event, policyFlags);
}
/** Callback to get pointer layer. */
@@ -241,6 +252,60 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
mService.mH.obtainMessage(ON_POINTER_DOWN_OUTSIDE_FOCUS, touchedToken).sendToTarget();
}
+ @Override
+ public boolean notifyFocusChanged(IBinder oldToken, IBinder newToken) {
+ boolean requestRefreshConfiguration = false;
+ final IWindow newFocusedWindow;
+ final WindowState win;
+
+ // TODO(b/141749603) investigate if this can be part of client focus change dispatch
+ synchronized (mService.mGlobalLock) {
+ win = mService.mInputToWindowMap.get(newToken);
+ }
+ newFocusedWindow = (win != null) ? win.mClient : null;
+
+ final IWindow focusedWindow = mFocusedWindow.get();
+ if (focusedWindow != null) {
+ if (newFocusedWindow != null
+ && newFocusedWindow.asBinder() == focusedWindow.asBinder()) {
+ Slog.w(TAG, "notifyFocusChanged called with unchanged mFocusedWindow="
+ + focusedWindow);
+ return false;
+ }
+ requestRefreshConfiguration = dispatchPointerCaptureChanged(focusedWindow, false);
+ }
+ mFocusedWindow.set(newFocusedWindow);
+ return requestRefreshConfiguration;
+ }
+
+ @Override
+ public boolean requestPointerCapture(IBinder windowToken, boolean enabled) {
+ final IWindow focusedWindow = mFocusedWindow.get();
+ if (focusedWindow == null || focusedWindow.asBinder() != windowToken) {
+ Slog.e(TAG, "requestPointerCapture called for a window that has no focus: "
+ + windowToken);
+ return false;
+ }
+ if (mFocusedWindowHasCapture == enabled) {
+ Slog.i(TAG, "requestPointerCapture: already " + (enabled ? "enabled" : "disabled"));
+ return false;
+ }
+ return dispatchPointerCaptureChanged(focusedWindow, enabled);
+ }
+
+ private boolean dispatchPointerCaptureChanged(IWindow focusedWindow, boolean enabled) {
+ if (mFocusedWindowHasCapture != enabled) {
+ mFocusedWindowHasCapture = enabled;
+ try {
+ focusedWindow.dispatchPointerCaptureChanged(enabled);
+ } catch (RemoteException ex) {
+ /* ignore */
+ }
+ return true;
+ }
+ return false;
+ }
+
/** Waits until the built-in input devices have been configured. */
public boolean waitForInputDevicesReady(long timeoutMillis) {
synchronized (mInputDevicesReadyMonitor) {
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index dd9000e06356..f9ff2e395793 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -26,7 +26,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLP
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -49,6 +49,7 @@ import android.view.SurfaceControl;
import com.android.server.AnimationThread;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.protolog.common.ProtoLog;
import java.io.PrintWriter;
import java.util.Set;
@@ -111,7 +112,7 @@ final class InputMonitor {
}
}
- private final Runnable mUpdateInputWindows = new Runnable() {
+ private class UpdateInputWindows implements Runnable {
@Override
public void run() {
synchronized (mService.mGlobalLock) {
@@ -147,7 +148,9 @@ final class InputMonitor {
mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag);
}
}
- };
+ }
+
+ private final UpdateInputWindows mUpdateInputWindows = new UpdateInputWindows();
public InputMonitor(WindowManagerService service, int displayId) {
mService = service;
@@ -260,7 +263,7 @@ final class InputMonitor {
inputWindowHandle.canReceiveKeys = child.canReceiveKeys();
inputWindowHandle.hasFocus = hasFocus;
inputWindowHandle.hasWallpaper = hasWallpaper;
- inputWindowHandle.paused = child.mAppToken != null ? child.mAppToken.paused : false;
+ inputWindowHandle.paused = child.mActivityRecord != null ? child.mActivityRecord.paused : false;
inputWindowHandle.layer = child.mLayer;
inputWindowHandle.ownerPid = child.mSession.mPid;
inputWindowHandle.ownerUid = child.mSession.mUid;
@@ -331,9 +334,7 @@ final class InputMonitor {
* Layer assignment is assumed to be complete by the time this is called.
*/
public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
- if (DEBUG_FOCUS_LIGHT || DEBUG_INPUT) {
- Slog.d(TAG_WM, "Input focus has changed to " + newWindow);
- }
+ ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "Input focus has changed to %s", newWindow);
if (newWindow != mInputFocus) {
if (newWindow != null && newWindow.canReceiveKeys()) {
@@ -352,7 +353,7 @@ final class InputMonitor {
}
}
- public void setFocusedAppLw(AppWindowToken newApp) {
+ public void setFocusedAppLw(ActivityRecord newApp) {
// Focused app has changed.
if (newApp == null) {
mService.mInputManager.setFocusedApplication(mDisplayId, null);
@@ -412,7 +413,7 @@ final class InputMonitor {
WallpaperController wallpaperController;
// An invalid window handle that tells SurfaceFlinger not update the input info.
- final InputWindowHandle mInvalidInputWindow = new InputWindowHandle(null, null, mDisplayId);
+ final InputWindowHandle mInvalidInputWindow = new InputWindowHandle(null, mDisplayId);
private void updateInputWindows(boolean inDrag) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");
@@ -475,7 +476,7 @@ final class InputMonitor {
final RecentsAnimationController recentsAnimationController =
mService.getRecentsAnimationController();
if (recentsAnimationController != null
- && recentsAnimationController.shouldApplyInputConsumer(w.mAppToken)) {
+ && recentsAnimationController.shouldApplyInputConsumer(w.mActivityRecord)) {
if (recentsAnimationController.updateInputConsumerForApp(
recentsAnimationInputConsumer.mWindowHandle, hasFocus)) {
recentsAnimationInputConsumer.show(mInputTransaction, w);
@@ -528,6 +529,10 @@ final class InputMonitor {
populateInputWindowHandle(
inputWindowHandle, w, flags, type, isVisible, hasFocus, hasWallpaper);
+ // register key interception info
+ mService.mKeyInterceptionInfoForToken.put(inputWindowHandle.token,
+ w.getKeyInterceptionInfo());
+
if (w.mWinAnimator.hasSurface()) {
mInputTransaction.setInputWindowInfo(
w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle);
diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java
index 3db6dcf07abf..c8ce53d444d5 100644
--- a/services/core/java/com/android/server/wm/InsetsControlTarget.java
+++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java
@@ -16,9 +16,37 @@
package com.android.server.wm;
+import android.inputmethodservice.InputMethodService;
+import android.view.WindowInsets.Type.InsetType;
+
/**
* Generalization of an object that can control insets state.
*/
interface InsetsControlTarget {
void notifyInsetsControlChanged();
+
+ /**
+ * Instructs the control target to show inset sources.
+ *
+ * @param types to specify which types of insets source window should be shown.
+ * @param fromIme {@code true} if IME show request originated from {@link InputMethodService}.
+ */
+ default void showInsets(@InsetType int types, boolean fromIme) {
+ }
+
+ /**
+ * Instructs the control target to hide inset sources.
+ *
+ * @param types to specify which types of insets source window should be hidden.
+ * @param fromIme {@code true} if IME hide request originated from {@link InputMethodService}.
+ */
+ default void hideInsets(@InsetType int types, boolean fromIme) {
+ }
+
+ /**
+ * Returns {@code true} if the control target allows the system to show transient windows.
+ */
+ default boolean canShowTransient() {
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 2dc50d86eaea..fc51b467c8d4 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -16,13 +16,22 @@
package com.android.server.wm;
+import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
+import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
import android.annotation.Nullable;
+import android.app.StatusBarManager;
+import android.util.IntArray;
+import android.view.InsetsState;
+import android.view.InsetsState.InternalInsetType;
+import android.view.ViewRootImpl;
/**
* Policy that implements who gets control over the windows generating insets.
@@ -32,6 +41,12 @@ class InsetsPolicy {
private final InsetsStateController mStateController;
private final DisplayContent mDisplayContent;
private final DisplayPolicy mPolicy;
+ private final TransientControlTarget mTransientControlTarget = new TransientControlTarget();
+ private final IntArray mShowingTransientTypes = new IntArray();
+
+ private WindowState mFocusedWin;
+ private BarWindow mTopBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR);
+ private BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR);
InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) {
mStateController = stateController;
@@ -41,11 +56,132 @@ class InsetsPolicy {
/** Updates the target which can control system bars. */
void updateBarControlTarget(@Nullable WindowState focusedWin) {
+ mFocusedWin = focusedWin;
mStateController.onBarControlTargetChanged(getTopControlTarget(focusedWin),
- getNavControlTarget(focusedWin));
+ getFakeTopControlTarget(focusedWin),
+ getNavControlTarget(focusedWin),
+ getFakeNavControlTarget(focusedWin));
+ if (ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL) {
+ return;
+ }
+ mTopBar.setVisible(focusedWin == null
+ || focusedWin != getTopControlTarget(focusedWin)
+ || focusedWin.getClientInsetsState().getSource(TYPE_TOP_BAR).isVisible());
+ mNavBar.setVisible(focusedWin == null
+ || focusedWin != getNavControlTarget(focusedWin)
+ || focusedWin.getClientInsetsState().getSource(TYPE_NAVIGATION_BAR).isVisible());
+ }
+
+ boolean isHidden(@InternalInsetType int type) {
+ final InsetsSourceProvider provider = mStateController.peekSourceProvider(type);
+ return provider != null && provider.hasWindow() && !provider.getSource().isVisible();
+ }
+
+ void showTransient(IntArray types) {
+ boolean changed = false;
+ for (int i = types.size() - 1; i >= 0; i--) {
+ final int type = types.get(i);
+ if (mShowingTransientTypes.indexOf(type) != -1) {
+ continue;
+ }
+ if (!isHidden(type)) {
+ continue;
+ }
+ mShowingTransientTypes.add(type);
+ changed = true;
+ }
+ if (changed) {
+ updateBarControlTarget(mFocusedWin);
+ mPolicy.getStatusBarManagerInternal().showTransient(mDisplayContent.getDisplayId(),
+ mShowingTransientTypes.toArray());
+ mStateController.notifyInsetsChanged();
+ // TODO(b/118118435): Animation
+ }
+ }
+
+ void hideTransient() {
+ if (mShowingTransientTypes.size() == 0) {
+ return;
+ }
+
+ // TODO(b/118118435): Animation
+ mShowingTransientTypes.clear();
+ updateBarControlTarget(mFocusedWin);
+ mStateController.notifyInsetsChanged();
+ }
+
+ /**
+ * @see InsetsStateController#getInsetsForDispatch
+ */
+ InsetsState getInsetsForDispatch(WindowState target) {
+ InsetsState state = mStateController.getInsetsForDispatch(target);
+ if (mShowingTransientTypes.size() == 0) {
+ return state;
+ }
+ for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
+ state.setSourceVisible(mShowingTransientTypes.get(i), false);
+ }
+ return state;
+ }
+
+ void onInsetsModified(WindowState windowState, InsetsState state) {
+ mStateController.onInsetsModified(windowState, state);
+ checkAbortTransient(windowState, state);
+ if (ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL) {
+ return;
+ }
+ if (windowState == getTopControlTarget(mFocusedWin)) {
+ mTopBar.setVisible(state.getSource(TYPE_TOP_BAR).isVisible());
+ }
+ if (windowState == getNavControlTarget(mFocusedWin)) {
+ mNavBar.setVisible(state.getSource(TYPE_NAVIGATION_BAR).isVisible());
+ }
+ }
+
+ /**
+ * Called when a window modified the insets state. If the window set a insets source to visible
+ * while it is shown transiently, we need to abort the transient state.
+ *
+ * @param windowState who changed the insets state.
+ * @param state the modified insets state.
+ */
+ private void checkAbortTransient(WindowState windowState, InsetsState state) {
+ if (mShowingTransientTypes.size() != 0) {
+ IntArray abortTypes = new IntArray();
+ for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
+ final int type = mShowingTransientTypes.get(i);
+ if (mStateController.isFakeTarget(type, windowState)
+ && state.getSource(type).isVisible()) {
+ mShowingTransientTypes.remove(i);
+ abortTypes.add(type);
+ }
+ }
+ if (abortTypes.size() > 0) {
+ mPolicy.getStatusBarManagerInternal().abortTransient(mDisplayContent.getDisplayId(),
+ abortTypes.toArray());
+ updateBarControlTarget(mFocusedWin);
+ }
+ }
+ }
+
+ private @Nullable InsetsControlTarget getFakeTopControlTarget(@Nullable WindowState focused) {
+ if (mShowingTransientTypes.indexOf(TYPE_TOP_BAR) != -1) {
+ return focused;
+ }
+ return null;
+ }
+
+ private @Nullable InsetsControlTarget getFakeNavControlTarget(@Nullable WindowState focused) {
+ if (mShowingTransientTypes.indexOf(TYPE_NAVIGATION_BAR) != -1) {
+ return focused;
+ }
+ return null;
}
private @Nullable InsetsControlTarget getTopControlTarget(@Nullable WindowState focusedWin) {
+ if (mShowingTransientTypes.indexOf(TYPE_TOP_BAR) != -1) {
+ return mTransientControlTarget;
+ }
if (areSystemBarsForciblyVisible() || isStatusBarForciblyVisible()) {
return null;
}
@@ -53,6 +189,9 @@ class InsetsPolicy {
}
private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin) {
+ if (mShowingTransientTypes.indexOf(TYPE_NAVIGATION_BAR) != -1) {
+ return mTransientControlTarget;
+ }
if (areSystemBarsForciblyVisible() || isNavBarForciblyVisible()) {
return null;
}
@@ -66,7 +205,7 @@ class InsetsPolicy {
}
final int privateFlags = statusBar.mAttrs.privateFlags;
- // TODO: Pretend to the app that it's still able to control it?
+ // TODO(b/118118435): Pretend to the app that it's still able to control it?
if ((privateFlags & PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT) != 0) {
return true;
}
@@ -100,4 +239,31 @@ class InsetsPolicy {
return isDockedStackVisible || isFreeformStackVisible || isResizing;
}
+ private class BarWindow {
+
+ private final int mId;
+ private @StatusBarManager.WindowVisibleState int mState =
+ StatusBarManager.WINDOW_STATE_SHOWING;
+
+ BarWindow(int id) {
+ mId = id;
+ }
+
+ private void setVisible(boolean visible) {
+ final int state = visible ? WINDOW_STATE_SHOWING : WINDOW_STATE_HIDDEN;
+ if (mState != state) {
+ mState = state;
+ mPolicy.getStatusBarManagerInternal().setWindowState(
+ mDisplayContent.getDisplayId(), mId, state);
+ }
+ }
+ }
+
+ // TODO(b/118118435): Implement animations for it (with SurfaceAnimator)
+ private class TransientControlTarget implements InsetsControlTarget {
+
+ @Override
+ public void notifyInsetsControlChanged() {
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 842686441465..a7724a11eec2 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -47,9 +47,11 @@ import java.io.PrintWriter;
*/
class InsetsSourceProvider {
+ protected final DisplayContent mDisplayContent;
+ protected final @NonNull InsetsSource mSource;
+ protected WindowState mWin;
+
private final Rect mTmpRect = new Rect();
- private final @NonNull InsetsSource mSource;
- private final DisplayContent mDisplayContent;
private final InsetsStateController mStateController;
private final InsetsSourceControl mFakeControl;
private @Nullable InsetsSourceControl mControl;
@@ -57,7 +59,6 @@ class InsetsSourceProvider {
private @Nullable InsetsControlTarget mFakeControlTarget;
private @Nullable ControlAdapter mAdapter;
- private WindowState mWin;
private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider;
/** The visibility override from the current controlling window. */
@@ -110,7 +111,9 @@ class InsetsSourceProvider {
void setWindow(@Nullable WindowState win,
@Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider) {
if (mWin != null) {
- mWin.setInsetProvider(null);
+ if (mControllable) {
+ mWin.setControllableInsetProvider(null);
+ }
// The window may be animating such that we can hand out the leash to the control
// target. Revoke the leash by cancelling the animation to correct the state.
// TODO: Ideally, we should wait for the animation to finish so previous window can
@@ -122,8 +125,8 @@ class InsetsSourceProvider {
if (win == null) {
setServerVisible(false);
mSource.setFrame(new Rect());
- } else {
- mWin.setInsetProvider(this);
+ } else if (mControllable) {
+ mWin.setControllableInsetProvider(this);
if (mControlTarget != null) {
updateControlForTarget(mControlTarget, true /* force */);
}
@@ -131,6 +134,13 @@ class InsetsSourceProvider {
}
/**
+ * @return Whether there is a window which backs this source.
+ */
+ boolean hasWindow() {
+ return mWin != null;
+ }
+
+ /**
* Called when a layout pass has occurred.
*/
void onPostLayout() {
@@ -224,6 +234,10 @@ class InsetsSourceProvider {
return null;
}
+ InsetsControlTarget getControlTarget() {
+ return mControlTarget;
+ }
+
boolean isClientVisible() {
return sNewInsetsMode == NEW_INSETS_MODE_NONE || mClientVisible;
}
@@ -240,6 +254,14 @@ class InsetsSourceProvider {
@Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
OnAnimationFinishedCallback finishCallback) {
+ // TODO(b/118118435): We can remove the type check when implementing the transient bar
+ // animation.
+ if (mSource.getType() == TYPE_IME) {
+ // TODO: use 0 alpha and remove t.hide() once b/138459974 is fixed.
+ t.setAlpha(animationLeash, 1 /* alpha */);
+ t.hide(animationLeash);
+ }
+
mCapturedLeash = animationLeash;
final Rect frame = mWin.getWindowFrames().mFrame;
t.setPosition(mCapturedLeash, frame.left, frame.top);
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 4ebb553318e8..e0554242fb29 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.view.InsetsState.InternalInsetType;
import static android.view.InsetsState.TYPE_IME;
import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
import static android.view.InsetsState.TYPE_TOP_BAR;
@@ -31,6 +30,7 @@ import android.util.SparseArray;
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
+import android.view.InsetsState.InternalInsetType;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -73,7 +73,7 @@ class InsetsStateController {
* @return The state stripped of the necessary information.
*/
InsetsState getInsetsForDispatch(WindowState target) {
- final InsetsSourceProvider provider = target.getInsetProvider();
+ final InsetsSourceProvider provider = target.getControllableInsetProvider();
if (provider == null) {
return mState;
}
@@ -108,8 +108,25 @@ class InsetsStateController {
* @return The provider of a specific type.
*/
InsetsSourceProvider getSourceProvider(@InternalInsetType int type) {
- return mProviders.computeIfAbsent(type,
- key -> new InsetsSourceProvider(mState.getSource(key), this, mDisplayContent));
+ if (type == TYPE_IME) {
+ return mProviders.computeIfAbsent(type,
+ key -> new ImeInsetsSourceProvider(
+ mState.getSource(key), this, mDisplayContent));
+ } else {
+ return mProviders.computeIfAbsent(type,
+ key -> new InsetsSourceProvider(mState.getSource(key), this, mDisplayContent));
+ }
+ }
+
+ ImeInsetsSourceProvider getImeSourceProvider() {
+ return (ImeInsetsSourceProvider) getSourceProvider(TYPE_IME);
+ }
+
+ /**
+ * @return The provider of a specific type or null if we don't have it.
+ */
+ @Nullable InsetsSourceProvider peekSourceProvider(@InternalInsetType int type) {
+ return mProviders.get(type);
}
/**
@@ -124,6 +141,7 @@ class InsetsStateController {
mLastState.set(mState, true /* copySources */);
notifyInsetsChanged();
}
+ getImeSourceProvider().onPostInsetsDispatched();
}
void onInsetsModified(WindowState windowState, InsetsState state) {
@@ -141,6 +159,10 @@ class InsetsStateController {
}
}
+ boolean isFakeTarget(@InternalInsetType int type, InsetsControlTarget target) {
+ return mTypeFakeControlTargetMap.get(type) == target;
+ }
+
void onImeTargetChanged(@Nullable InsetsControlTarget imeTarget) {
onControlChanged(TYPE_IME, imeTarget);
notifyPendingInsetsControlChanged();
@@ -155,9 +177,13 @@ class InsetsStateController {
* and visibility.
*/
void onBarControlTargetChanged(@Nullable InsetsControlTarget topControlling,
- @Nullable InsetsControlTarget navControlling) {
+ @Nullable InsetsControlTarget fakeTopControlling,
+ @Nullable InsetsControlTarget navControlling,
+ @Nullable InsetsControlTarget fakeNavControlling) {
onControlChanged(TYPE_TOP_BAR, topControlling);
onControlChanged(TYPE_NAVIGATION_BAR, navControlling);
+ onControlFakeTargetChanged(TYPE_TOP_BAR, fakeTopControlling);
+ onControlFakeTargetChanged(TYPE_NAVIGATION_BAR, fakeNavControlling);
notifyPendingInsetsControlChanged();
}
@@ -268,7 +294,7 @@ class InsetsStateController {
});
}
- private void notifyInsetsChanged() {
+ void notifyInsetsChanged() {
mDisplayContent.forAllWindows(mDispatchInsetsChanged, true /* traverseTopToBottom */);
}
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 2b5eb3ac29fb..52cc422f8c51 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -16,7 +16,7 @@
package com.android.server.wm;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
@@ -180,7 +180,7 @@ class KeyguardController {
if (!mKeyguardShowing) {
return;
}
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway");
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "keyguardGoingAway");
mService.deferWindowLayout();
try {
setKeyguardGoingAway(true);
@@ -202,11 +202,8 @@ class KeyguardController {
true /* taskSwitch */);
mWindowManager.executeAppTransition();
} finally {
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway: surfaceLayout");
mService.continueWindowLayout();
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
-
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
diff --git a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
index 93e2d8d6fba4..362ed3c380c5 100644
--- a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
+++ b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
@@ -70,9 +70,12 @@ class LaunchObserverRegistryImpl implements
}
@Override
- public void onIntentStarted(Intent intent) {
+ public void onIntentStarted(Intent intent, long timestampNs) {
mHandler.sendMessage(PooledLambda.obtainMessage(
- LaunchObserverRegistryImpl::handleOnIntentStarted, this, intent));
+ LaunchObserverRegistryImpl::handleOnIntentStarted,
+ this,
+ intent,
+ timestampNs));
}
@Override
@@ -99,9 +102,22 @@ class LaunchObserverRegistryImpl implements
@Override
public void onActivityLaunchFinished(
- @ActivityRecordProto byte[] activity) {
+ @ActivityRecordProto byte[] activity,
+ long timestampNs) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LaunchObserverRegistryImpl::handleOnActivityLaunchFinished,
+ this,
+ activity,
+ timestampNs));
+ }
+
+ @Override
+ public void onReportFullyDrawn(@ActivityRecordProto byte[] activity, long timestampNs) {
mHandler.sendMessage(PooledLambda.obtainMessage(
- LaunchObserverRegistryImpl::handleOnActivityLaunchFinished, this, activity));
+ LaunchObserverRegistryImpl::handleOnReportFullyDrawn,
+ this,
+ activity,
+ timestampNs));
}
// Use PooledLambda.obtainMessage to invoke below methods. Every method reference must be
@@ -116,11 +132,11 @@ class LaunchObserverRegistryImpl implements
mList.remove(observer);
}
- private void handleOnIntentStarted(Intent intent) {
+ private void handleOnIntentStarted(Intent intent, long timestampNs) {
// Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
for (int i = 0; i < mList.size(); i++) {
ActivityMetricsLaunchObserver o = mList.get(i);
- o.onIntentStarted(intent);
+ o.onIntentStarted(intent, timestampNs);
}
}
@@ -152,11 +168,20 @@ class LaunchObserverRegistryImpl implements
}
private void handleOnActivityLaunchFinished(
- @ActivityRecordProto byte[] activity) {
+ @ActivityRecordProto byte[] activity, long timestampNs) {
+ // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+ for (int i = 0; i < mList.size(); i++) {
+ ActivityMetricsLaunchObserver o = mList.get(i);
+ o.onActivityLaunchFinished(activity, timestampNs);
+ }
+ }
+
+ private void handleOnReportFullyDrawn(
+ @ActivityRecordProto byte[] activity, long timestampNs) {
// Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
for (int i = 0; i < mList.size(); i++) {
ActivityMetricsLaunchObserver o = mList.get(i);
- o.onActivityLaunchFinished(activity);
+ o.onReportFullyDrawn(activity, timestampNs);
}
}
}
diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
index d364a3765c22..5d27390da588 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import android.content.ComponentName;
-import android.content.pm.PackageList;
import android.content.pm.PackageManagerInternal;
import android.graphics.Rect;
import android.os.Environment;
@@ -32,6 +31,7 @@ import android.view.DisplayInfo;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastXmlSerializer;
import com.android.server.LocalServices;
+import com.android.server.pm.PackageList;
import com.android.server.wm.LaunchParamsController.LaunchParams;
import libcore.io.IoUtils;
@@ -198,7 +198,7 @@ class LaunchParamsPersister {
void saveTask(TaskRecord task) {
final ComponentName name = task.realActivity;
- final int userId = task.userId;
+ final int userId = task.mUserId;
PersistableLaunchParams params;
ArrayMap<ComponentName, PersistableLaunchParams> map = mMap.get(userId);
if (map == null) {
@@ -247,7 +247,7 @@ class LaunchParamsPersister {
void getLaunchParams(TaskRecord task, ActivityRecord activity, LaunchParams outParams) {
final ComponentName name = task != null ? task.realActivity : activity.mActivityComponent;
- final int userId = task != null ? task.userId : activity.mUserId;
+ final int userId = task != null ? task.mUserId : activity.mUserId;
outParams.reset();
Map<ComponentName, PersistableLaunchParams> map = mMap.get(userId);
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 1bd2493e9a07..6721ef836e4e 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -20,7 +20,7 @@ import static android.view.SurfaceControl.HIDDEN;
import android.graphics.Point;
import android.graphics.Rect;
-import android.os.Binder;
+import android.os.IBinder;
import android.os.Process;
import android.view.InputChannel;
import android.view.InputEventReceiver;
@@ -170,20 +170,21 @@ public class Letterbox {
final InputWindowHandle mWindowHandle;
final InputEventReceiver mInputEventReceiver;
final WindowManagerService mWmService;
- final Binder mToken = new Binder();
+ final IBinder mToken;
InputInterceptor(String namePrefix, WindowState win) {
mWmService = win.mWmService;
- final String name = namePrefix + (win.mAppToken != null ? win.mAppToken : win);
+ final String name = namePrefix + (win.mActivityRecord != null ? win.mActivityRecord : win);
final InputChannel[] channels = InputChannel.openInputChannelPair(name);
mServerChannel = channels[0];
mClientChannel = channels[1];
mInputEventReceiver = new SimpleInputReceiver(mClientChannel);
- mWmService.mInputManager.registerInputChannel(mServerChannel, mToken);
+ mWmService.mInputManager.registerInputChannel(mServerChannel);
+ mToken = mServerChannel.getToken();
mWindowHandle = new InputWindowHandle(null /* inputApplicationHandle */,
- null /* clientWindow */, win.getDisplayId());
+ win.getDisplayId());
mWindowHandle.name = name;
mWindowHandle.token = mToken;
mWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
@@ -247,12 +248,12 @@ public class Letterbox {
mLayoutFrameRelative.offset(-surfaceOrigin.x, -surfaceOrigin.y);
}
- private void createSurface() {
+ private void createSurface(SurfaceControl.Transaction t) {
mSurface = mSurfaceControlFactory.get().setName("Letterbox - " + mType)
.setFlags(HIDDEN).setColorLayer().build();
- mSurface.setLayer(-1);
- mSurface.setColor(new float[]{0, 0, 0});
- mSurface.setColorSpaceAgnostic(true);
+ t.setLayer(mSurface, -1)
+ .setColor(mSurface, new float[]{0, 0, 0})
+ .setColorSpaceAgnostic(mSurface, true);
}
void attachInput(WindowState win) {
@@ -300,7 +301,7 @@ public class Letterbox {
mSurfaceFrameRelative.set(mLayoutFrameRelative);
if (!mSurfaceFrameRelative.isEmpty()) {
if (mSurface == null) {
- createSurface();
+ createSurface(t);
}
t.setPosition(mSurface, mSurfaceFrameRelative.left, mSurfaceFrameRelative.top);
t.setWindowCrop(mSurface, mSurfaceFrameRelative.width(),
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index b30da5e156e2..dc45686a1359 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -317,12 +317,12 @@ public class LockTaskController {
}
// Allow recents activity if enabled by policy
- if (task.isActivityTypeRecents() && isRecentsAllowed(task.userId)) {
+ if (task.isActivityTypeRecents() && isRecentsAllowed(task.mUserId)) {
return false;
}
// Allow emergency calling when the device is protected by a locked keyguard
- if (isKeyguardAllowed(task.userId) && isEmergencyCallTask(task)) {
+ if (isKeyguardAllowed(task.mUserId) && isEmergencyCallTask(task)) {
return false;
}
@@ -474,7 +474,7 @@ public class LockTaskController {
if (mLockTaskModeTasks.isEmpty()) {
if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "removeLockedTask: task=" + task +
" last task, reverting locktask mode. Callers=" + Debug.getCallers(3));
- mHandler.post(() -> performStopLockTask(task.userId));
+ mHandler.post(() -> performStopLockTask(task.mUserId));
}
}
@@ -537,7 +537,7 @@ public class LockTaskController {
StatusBarManagerInternal statusBarManager = LocalServices.getService(
StatusBarManagerInternal.class);
if (statusBarManager != null) {
- statusBarManager.showScreenPinningRequest(task.taskId);
+ statusBarManager.showScreenPinningRequest(task.mTaskId);
}
return;
}
@@ -570,11 +570,11 @@ public class LockTaskController {
final Intent taskIntent = task.intent;
if (mLockTaskModeTasks.isEmpty() && taskIntent != null) {
- mSupervisor.mRecentTasks.onLockTaskModeStateChanged(lockTaskModeState, task.userId);
+ mSupervisor.mRecentTasks.onLockTaskModeStateChanged(lockTaskModeState, task.mUserId);
// Start lock task on the handler thread
mHandler.post(() -> performStartLockTask(
taskIntent.getComponent().getPackageName(),
- task.userId,
+ task.mUserId,
lockTaskModeState));
}
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "setLockTaskMode: Locking to " + task +
@@ -640,7 +640,7 @@ public class LockTaskController {
|| lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED;
if (mLockTaskModeState != LOCK_TASK_MODE_LOCKED
- || lockedTask.userId != userId
+ || lockedTask.mUserId != userId
|| !wasWhitelisted || isWhitelisted) {
continue;
}
@@ -704,7 +704,7 @@ public class LockTaskController {
}
mLockTaskFeatures.put(userId, flags);
- if (!mLockTaskModeTasks.isEmpty() && userId == mLockTaskModeTasks.get(0).userId) {
+ if (!mLockTaskModeTasks.isEmpty() && userId == mLockTaskModeTasks.get(0).mUserId) {
mHandler.post(() -> {
if (mLockTaskModeState == LOCK_TASK_MODE_LOCKED) {
setStatusBarState(mLockTaskModeState, userId);
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 8e57fec6ba46..2e6df601cf0b 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -90,8 +90,6 @@ class PinnedStackController {
private boolean mIsMinimized;
private boolean mIsImeShowing;
private int mImeHeight;
- private boolean mIsShelfShowing;
- private int mShelfHeight;
// The set of actions and aspect-ratio for the that are currently allowed on the PiP activity
private ArrayList<RemoteAction> mActions = new ArrayList<>();
@@ -216,7 +214,6 @@ class PinnedStackController {
mPinnedStackListener = listener;
notifyDisplayInfoChanged(mDisplayInfo);
notifyImeVisibilityChanged(mIsImeShowing, mImeHeight);
- notifyShelfVisibilityChanged(mIsShelfShowing, mShelfHeight);
// The movement bounds notification needs to be sent before the minimized state, since
// SystemUI may use the bounds to retore the minimized position
notifyMovementBoundsChanged(false /* fromImeAdjustment */,
@@ -278,9 +275,7 @@ class PinnedStackController {
mSnapAlgorithm.applySnapFraction(defaultBounds, movementBounds, snapFraction);
} else {
Gravity.apply(mDefaultStackGravity, size.getWidth(), size.getHeight(), insetBounds,
- 0, Math.max(mIsImeShowing ? mImeHeight : 0,
- mIsShelfShowing ? mShelfHeight : 0),
- defaultBounds);
+ 0, mIsImeShowing ? mImeHeight : 0, defaultBounds);
}
return defaultBounds;
}
@@ -367,21 +362,6 @@ class PinnedStackController {
}
/**
- * Sets the shelf state and height.
- */
- void setAdjustedForShelf(boolean adjustedForShelf, int shelfHeight) {
- final boolean shelfShowing = adjustedForShelf && shelfHeight > 0;
- if (shelfShowing == mIsShelfShowing && shelfHeight == mShelfHeight) {
- return;
- }
-
- mIsShelfShowing = shelfShowing;
- mShelfHeight = shelfHeight;
- notifyShelfVisibilityChanged(shelfShowing, shelfHeight);
- notifyMovementBoundsChanged(false /* fromImeAdjustment */, true /* fromShelfAdjustment */);
- }
-
- /**
* Sets the current aspect ratio.
*/
void setAspectRatio(float aspectRatio) {
@@ -439,16 +419,6 @@ class PinnedStackController {
}
}
- private void notifyShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {
- if (mPinnedStackListener != null) {
- try {
- mPinnedStackListener.onShelfVisibilityChanged(shelfVisible, shelfHeight);
- } catch (RemoteException e) {
- Slog.e(TAG_WM, "Error delivering bounds changed event.", e);
- }
- }
- }
-
private void notifyAspectRatioChanged(float aspectRatio) {
if (mPinnedStackListener == null) return;
try {
@@ -613,8 +583,6 @@ class PinnedStackController {
pw.println();
pw.println(prefix + " mIsImeShowing=" + mIsImeShowing);
pw.println(prefix + " mImeHeight=" + mImeHeight);
- pw.println(prefix + " mIsShelfShowing=" + mIsShelfShowing);
- pw.println(prefix + " mShelfHeight=" + mShelfHeight);
pw.println(prefix + " mIsMinimized=" + mIsMinimized);
pw.println(prefix + " mAspectRatio=" + mAspectRatio);
pw.println(prefix + " mMinAspectRatio=" + mMinAspectRatio);
diff --git a/services/core/java/com/android/server/wm/ProtoLogGroup.java b/services/core/java/com/android/server/wm/ProtoLogGroup.java
new file mode 100644
index 000000000000..5c2830e13b75
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ProtoLogGroup.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.protolog.common.IProtoLogGroup;
+import com.android.server.protolog.common.ProtoLog;
+
+/**
+ * Defines logging groups for ProtoLog.
+ *
+ * This file is used by the ProtoLogTool to generate optimized logging code. All of its dependencies
+ * must be included in services.core.wm.protologgroups build target.
+ */
+public enum ProtoLogGroup implements IProtoLogGroup {
+ WM_ERROR(true, true, true, Consts.TAG_WM),
+ WM_DEBUG_ORIENTATION(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM),
+ WM_DEBUG_FOCUS_LIGHT(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM),
+ WM_DEBUG_BOOT(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM),
+ WM_DEBUG_RESIZE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM),
+ WM_DEBUG_ADD_REMOVE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM),
+ WM_DEBUG_FOCUS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM),
+ WM_DEBUG_STARTING_WINDOW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM),
+ WM_SHOW_TRANSACTIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM),
+ WM_SHOW_SURFACE_ALLOC(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM),
+ WM_DEBUG_APP_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM),
+ WM_DEBUG_APP_TRANSITIONS_ANIM(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM),
+ WM_DEBUG_RECENTS_ANIMATIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM),
+ WM_DEBUG_DRAW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM),
+ WM_DEBUG_REMOTE_ANIMATIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM),
+ WM_DEBUG_SCREEN_ON(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM),
+ WM_DEBUG_KEEP_SCREEN_ON(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM),
+ WM_DEBUG_WINDOW_MOVEMENT(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM),
+ TEST_GROUP(true, true, false, "WindowManagetProtoLogTest");
+
+ private final boolean mEnabled;
+ private volatile boolean mLogToProto;
+ private volatile boolean mLogToLogcat;
+ private final String mTag;
+
+ /**
+ * @param enabled set to false to exclude all log statements for this group from
+ * compilation,
+ * they will not be available in runtime.
+ * @param logToProto enable binary logging for the group
+ * @param logToLogcat enable text logging for the group
+ * @param tag name of the source of the logged message
+ */
+ ProtoLogGroup(boolean enabled, boolean logToProto, boolean logToLogcat, String tag) {
+ this.mEnabled = enabled;
+ this.mLogToProto = logToProto;
+ this.mLogToLogcat = logToLogcat;
+ this.mTag = tag;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ @Override
+ public boolean isLogToProto() {
+ return mLogToProto;
+ }
+
+ @Override
+ public boolean isLogToLogcat() {
+ return mLogToLogcat;
+ }
+
+ @Override
+ public boolean isLogToAny() {
+ return mLogToLogcat || mLogToProto;
+ }
+
+ @Override
+ public String getTag() {
+ return mTag;
+ }
+
+ @Override
+ public void setLogToProto(boolean logToProto) {
+ this.mLogToProto = logToProto;
+ }
+
+ @Override
+ public void setLogToLogcat(boolean logToLogcat) {
+ this.mLogToLogcat = logToLogcat;
+ }
+
+ /**
+ * Test function for automated integration tests. Can be also called manually from adb shell.
+ */
+ @VisibleForTesting
+ public static void testProtoLog() {
+ ProtoLog.e(ProtoLogGroup.TEST_GROUP,
+ "Test completed successfully: %b %d %o %x %e %g %f %% %s.",
+ true, 1, 2, 3, 0.4, 0.5, 0.6, "ok");
+ }
+
+ private static class Consts {
+ private static final String TAG_WM = "WindowManager";
+
+ private static final boolean ENABLE_DEBUG = true;
+ private static final boolean ENABLE_LOG_TO_PROTO_DEBUG = true;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index d522deb390c7..4e95d117bcf9 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -121,7 +121,7 @@ class RecentTasks {
// Comparator to sort by taskId
private static final Comparator<TaskRecord> TASK_ID_COMPARATOR =
- (lhs, rhs) -> rhs.taskId - lhs.taskId;
+ (lhs, rhs) -> rhs.mTaskId - lhs.mTaskId;
// Placeholder variables to keep track of activities/apps that are no longer avialble while
// iterating through the recents list
@@ -274,9 +274,14 @@ class RecentTasks {
* app, or a timeout occurs.
*/
void setFreezeTaskListReordering() {
+ // Only fire the callback once per quickswitch session, not on every individual switch
+ if (!mFreezeTaskListReordering) {
+ mTaskNotificationController.notifyTaskListFrozen(true);
+ mFreezeTaskListReordering = true;
+ }
+
// Always update the reordering time when this is called to ensure that the timeout
// is reset
- mFreezeTaskListReordering = true;
mService.mH.removeCallbacks(mResetFreezeTaskListOnTimeoutRunnable);
mService.mH.postDelayed(mResetFreezeTaskListOnTimeoutRunnable, mFreezeTaskListTimeoutMs);
}
@@ -304,6 +309,7 @@ class RecentTasks {
trimInactiveRecentTasks();
mTaskNotificationController.notifyTaskStackChanged();
+ mTaskNotificationController.notifyTaskListFrozen(false);
}
/**
@@ -460,8 +466,8 @@ class RecentTasks {
// Check if any tasks are added before recents is loaded
final SparseBooleanArray preaddedTasks = new SparseBooleanArray();
for (final TaskRecord task : mTasks) {
- if (task.userId == userId && shouldPersistTaskLocked(task)) {
- preaddedTasks.put(task.taskId, true);
+ if (task.mUserId == userId && shouldPersistTaskLocked(task)) {
+ preaddedTasks.put(task.mTaskId, true);
}
}
@@ -529,12 +535,12 @@ class RecentTasks {
if (shouldPersistTaskLocked(task)) {
// Set of persisted taskIds for task.userId should not be null here
// TODO Investigate why it can happen. For now initialize with an empty set
- if (mPersistedTaskIds.get(task.userId) == null) {
- Slog.wtf(TAG, "No task ids found for userId " + task.userId + ". task=" + task
+ if (mPersistedTaskIds.get(task.mUserId) == null) {
+ Slog.wtf(TAG, "No task ids found for userId " + task.mUserId + ". task=" + task
+ " mPersistedTaskIds=" + mPersistedTaskIds);
- mPersistedTaskIds.put(task.userId, new SparseBooleanArray());
+ mPersistedTaskIds.put(task.mUserId, new SparseBooleanArray());
}
- mPersistedTaskIds.get(task.userId).put(task.taskId, true);
+ mPersistedTaskIds.get(task.mUserId).put(task.mTaskId, true);
}
}
}
@@ -610,7 +616,7 @@ class RecentTasks {
for (int i = mTasks.size() - 1; i >= 0; --i) {
TaskRecord tr = mTasks.get(i);
- if (tr.userId == userId) {
+ if (tr.mUserId == userId) {
if(DEBUG_TASKS) Slog.i(TAG_TASKS,
"remove RecentTask " + tr + " when finishing user" + userId);
remove(tr);
@@ -624,11 +630,11 @@ class RecentTasks {
final TaskRecord tr = mTasks.get(i);
if (tr.realActivity != null
&& packageNames.contains(tr.realActivity.getPackageName())
- && tr.userId == userId
+ && tr.mUserId == userId
&& tr.realActivitySuspended != suspended) {
tr.realActivitySuspended = suspended;
if (suspended) {
- mSupervisor.removeTaskByIdLocked(tr.taskId, false,
+ mSupervisor.removeTaskByIdLocked(tr.mTaskId, false,
REMOVE_FROM_RECENTS, "suspended-package");
}
notifyTaskPersisterLocked(tr, false);
@@ -642,7 +648,7 @@ class RecentTasks {
}
for (int i = mTasks.size() - 1; i >= 0; --i) {
final TaskRecord tr = mTasks.get(i);
- if (tr.userId == userId && !mService.getLockTaskController().isTaskWhitelisted(tr)) {
+ if (tr.mUserId == userId && !mService.getLockTaskController().isTaskWhitelisted(tr)) {
remove(tr);
}
}
@@ -653,10 +659,10 @@ class RecentTasks {
final TaskRecord tr = mTasks.get(i);
final String taskPackageName =
tr.getBaseIntent().getComponent().getPackageName();
- if (tr.userId != userId) continue;
+ if (tr.mUserId != userId) continue;
if (!taskPackageName.equals(packageName)) continue;
- mSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS,
+ mSupervisor.removeTaskByIdLocked(tr.mTaskId, true, REMOVE_FROM_RECENTS,
"remove-package-task");
}
}
@@ -665,7 +671,7 @@ class RecentTasks {
Set<Integer> profileIds = getProfileIds(userId);
for (int i = mTasks.size() - 1; i >= 0; --i) {
final TaskRecord tr = mTasks.get(i);
- if (!profileIds.contains(tr.userId)) continue;
+ if (!profileIds.contains(tr.mUserId)) continue;
if (isVisibleRecentTask(tr)) {
mTasks.remove(i);
notifyTaskRemoved(tr, true /* wasTrimmed */, true /* killProcess */);
@@ -677,7 +683,7 @@ class RecentTasks {
int userId) {
for (int i = mTasks.size() - 1; i >= 0; --i) {
final TaskRecord tr = mTasks.get(i);
- if (userId != UserHandle.USER_ALL && tr.userId != userId) {
+ if (userId != UserHandle.USER_ALL && tr.mUserId != userId) {
continue;
}
@@ -685,7 +691,7 @@ class RecentTasks {
final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName)
&& (filterByClasses == null || filterByClasses.contains(cn.getClassName()));
if (sameComponent) {
- mSupervisor.removeTaskByIdLocked(tr.taskId, false,
+ mSupervisor.removeTaskByIdLocked(tr.mTaskId, false,
REMOVE_FROM_RECENTS, "disabled-package");
}
}
@@ -711,7 +717,7 @@ class RecentTasks {
final IPackageManager pm = AppGlobals.getPackageManager();
for (int i = recentsCount - 1; i >= 0; i--) {
final TaskRecord task = mTasks.get(i);
- if (userId != UserHandle.USER_ALL && task.userId != userId) {
+ if (userId != UserHandle.USER_ALL && task.mUserId != userId) {
// Only look at tasks for the user ID of interest.
continue;
}
@@ -828,7 +834,7 @@ class RecentTasks {
if (intent == null || !callingPackage.equals(intent.getComponent().getPackageName())) {
continue;
}
- AppTaskImpl taskImpl = new AppTaskImpl(mService, tr.taskId, callingUid);
+ AppTaskImpl taskImpl = new AppTaskImpl(mService, tr.mTaskId, callingUid);
list.add(taskImpl.asBinder());
}
return list;
@@ -910,7 +916,7 @@ class RecentTasks {
}
// Only add calling user or related users recent tasks
- if (!includedUsers.contains(Integer.valueOf(tr.userId))) {
+ if (!includedUsers.contains(Integer.valueOf(tr.mUserId))) {
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not user: " + tr);
continue;
}
@@ -974,7 +980,7 @@ class RecentTasks {
if ((task.isPersistable || task.inRecents)
&& (stack == null || !stack.isHomeOrRecentsStack())) {
if (TaskPersister.DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
- persistentTaskIds.add(task.taskId);
+ persistentTaskIds.add(task.mTaskId);
} else {
if (TaskPersister.DEBUG) Slog.d(TAG, "omitting from persistentTaskIds task="
+ task);
@@ -999,7 +1005,7 @@ class RecentTasks {
if (isVisibleRecentTask(tr)) {
numVisibleTasks++;
if (isInVisibleRange(tr, i, numVisibleTasks, false /* skipExcludedCheck */)) {
- res.put(tr.taskId, true);
+ res.put(tr.mTaskId, true);
}
}
}
@@ -1013,7 +1019,7 @@ class RecentTasks {
final int recentsCount = mTasks.size();
for (int i = 0; i < recentsCount; i++) {
TaskRecord tr = mTasks.get(i);
- if (tr.taskId == id) {
+ if (tr.mTaskId == id) {
return tr;
}
}
@@ -1026,7 +1032,7 @@ class RecentTasks {
void add(TaskRecord task) {
if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "add: task=" + task);
- final boolean isAffiliated = task.mAffiliatedTaskId != task.taskId
+ final boolean isAffiliated = task.mAffiliatedTaskId != task.mTaskId
|| task.mNextAffiliateTaskId != INVALID_TASK_ID
|| task.mPrevAffiliateTaskId != INVALID_TASK_ID;
@@ -1140,7 +1146,7 @@ class RecentTasks {
if (needAffiliationFix) {
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: regrouping affiliations");
- cleanupLocked(task.userId);
+ cleanupLocked(task.mUserId);
}
// Trim the set of tasks to the active set
@@ -1257,13 +1263,13 @@ class RecentTasks {
if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "isActiveRecentTask: task=" + task
+ " globalMax=" + mGlobalMaxNumTasks);
- if (quietProfileUserIds.get(task.userId)) {
+ if (quietProfileUserIds.get(task.mUserId)) {
// Quiet profile user's tasks are never active
if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\tisQuietProfileTask=true");
return false;
}
- if (task.mAffiliatedTaskId != INVALID_TASK_ID && task.mAffiliatedTaskId != task.taskId) {
+ if (task.mAffiliatedTaskId != INVALID_TASK_ID && task.mAffiliatedTaskId != task.mTaskId) {
// Keep the task active if its affiliated task is also active
final TaskRecord affiliatedTask = getTask(task.mAffiliatedTaskId);
if (affiliatedTask != null) {
@@ -1439,7 +1445,7 @@ class RecentTasks {
final TaskRecord tr = mTasks.get(i);
if (task != tr) {
if (!hasCompatibleActivityTypeAndWindowingMode(task, tr)
- || task.userId != tr.userId) {
+ || task.mUserId != tr.mUserId) {
continue;
}
final Intent trIntent = tr.intent;
@@ -1494,7 +1500,7 @@ class RecentTasks {
final int affiliateId = startTask.mAffiliatedTaskId;
// Quick identification of isolated tasks. I.e. those not launched behind.
- if (startTask.taskId == affiliateId && startTask.mPrevAffiliate == null &&
+ if (startTask.mTaskId == affiliateId && startTask.mPrevAffiliate == null &&
startTask.mNextAffiliate == null) {
// There is still a slim chance that there are other tasks that point to this task
// and that the chain is so messed up that this task no longer points to them but
@@ -1590,7 +1596,7 @@ class RecentTasks {
} else {
// Verify middle of the chain's next points back to the one before.
if (cur.mNextAffiliate != prev
- || cur.mNextAffiliateTaskId != prev.taskId) {
+ || cur.mNextAffiliateTaskId != prev.mTaskId) {
Slog.wtf(TAG, "Bad chain @" + endIndex
+ ": middle task " + cur + " @" + endIndex
+ " has bad next affiliate "
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 5cabbd97b1f7..b7d25c374a15 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -22,16 +22,16 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowManager.TRANSIT_NONE;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.BoundsAnimationController.BOUNDS;
import static com.android.server.wm.BoundsAnimationController.FADE_IN;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_TOP;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS;
import android.annotation.Nullable;
import android.app.ActivityOptions;
@@ -42,6 +42,7 @@ import android.os.Trace;
import android.util.Slog;
import android.view.IRecentsAnimationRunner;
+import com.android.server.protolog.common.ProtoLog;
import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
/**
@@ -51,7 +52,6 @@ import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallback
class RecentsAnimation implements RecentsAnimationCallbacks,
ActivityDisplay.OnStackOrderChangedListener {
private static final String TAG = RecentsAnimation.class.getSimpleName();
- private static final boolean DEBUG = DEBUG_RECENTS_ANIMATIONS;
private final ActivityTaskManagerService mService;
private final ActivityStackSupervisor mStackSupervisor;
@@ -101,7 +101,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
* is updated to the current one.
*/
void preloadRecentsActivity() {
- if (DEBUG) Slog.d(TAG, "Preload recents with " + mTargetIntent);
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Preload recents with %s",
+ mTargetIntent);
ActivityStack targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
mTargetActivityType);
ActivityRecord targetActivity = getTargetActivity(targetStack);
@@ -116,7 +117,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
// keeps the original stopped state.
targetActivity.ensureActivityConfiguration(0 /* globalChanges */,
false /* preserveWindow */, true /* ignoreVisibility */);
- if (DEBUG) Slog.d(TAG, "Updated config=" + targetActivity.getConfiguration());
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Updated config=%s",
+ targetActivity.getConfiguration());
}
} else {
// Create the activity record. Because the activity is invisible, this doesn't really
@@ -131,13 +133,13 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
}
if (!targetActivity.attachedToProcess()) {
- if (DEBUG) Slog.d(TAG, "Real start recents");
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Real start recents");
mStackSupervisor.startSpecificActivityLocked(targetActivity, false /* andResume */,
false /* checkConfig */);
// Make sure the activity won't be involved in transition.
- if (targetActivity.mAppWindowToken != null) {
- targetActivity.mAppWindowToken.getDisplayContent().mUnknownAppVisibilityController
- .appRemovedOrHidden(targetActivity.mAppWindowToken);
+ if (targetActivity.getDisplayContent() != null) {
+ targetActivity.getDisplayContent().mUnknownAppVisibilityController
+ .appRemovedOrHidden(targetActivity);
}
}
@@ -155,16 +157,17 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
}
void startRecentsActivity(IRecentsAnimationRunner recentsAnimationRunner) {
- if (DEBUG) Slog.d(TAG, "startRecentsActivity(): intent=" + mTargetIntent);
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity");
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "startRecentsActivity(): intent=%s", mTargetIntent);
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RecentsAnimation#startRecentsActivity");
// TODO(multi-display) currently only support recents animation in default display.
final DisplayContent dc =
mService.mRootActivityContainer.getDefaultDisplay().mDisplayContent;
if (!mWindowManager.canStartRecentsAnimation()) {
notifyAnimationCancelBeforeStart(recentsAnimationRunner);
- if (DEBUG) Slog.d(TAG, "Can't start recents animation, nextAppTransition="
- + dc.mAppTransition.getAppTransition());
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
+ "Can't start recents animation, nextAppTransition=%s",
+ dc.mAppTransition.getAppTransition());
return;
}
@@ -178,7 +181,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
mRestoreTargetBehindStack = display.getStackAbove(targetStack);
if (mRestoreTargetBehindStack == null) {
notifyAnimationCancelBeforeStart(recentsAnimationRunner);
- if (DEBUG) Slog.d(TAG, "No stack above target stack=" + targetStack);
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
+ "No stack above target stack=%s", targetStack);
return;
}
}
@@ -201,8 +205,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
if (hasExistingActivity) {
// Move the recents activity into place for the animation if it is not top most
mDefaultDisplay.moveStackBehindBottomMostVisibleStack(targetStack);
- if (DEBUG) Slog.d(TAG, "Moved stack=" + targetStack + " behind stack="
- + mDefaultDisplay.getStackAbove(targetStack));
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Moved stack=%s behind stack=%s",
+ targetStack, mDefaultDisplay.getStackAbove(targetStack));
// If there are multiple tasks in the target stack (ie. the home stack, with 3p
// and default launchers coexisting), then move the task to the top as a part of
@@ -220,17 +224,15 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
mTargetActivityType);
targetActivity = getTargetActivity(targetStack);
mDefaultDisplay.moveStackBehindBottomMostVisibleStack(targetStack);
- if (DEBUG) {
- Slog.d(TAG, "Moved stack=" + targetStack + " behind stack="
- + mDefaultDisplay.getStackAbove(targetStack));
- }
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Moved stack=%s behind stack=%s",
+ targetStack, mDefaultDisplay.getStackAbove(targetStack));
mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
mWindowManager.executeAppTransition();
// TODO: Maybe wait for app to draw in this particular case?
- if (DEBUG) Slog.d(TAG, "Started intent=" + mTargetIntent);
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Started intent=%s", mTargetIntent);
}
// Mark the target activity as launch-behind to bump its visibility for the
@@ -245,7 +247,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
"startRecentsActivity");
mWindowManager.initializeRecentsAnimation(mTargetActivityType, recentsAnimationRunner,
this, mDefaultDisplay.mDisplayId,
- mStackSupervisor.mRecentTasks.getRecentTaskIds());
+ mStackSupervisor.mRecentTasks.getRecentTaskIds(), targetActivity);
// If we updated the launch-behind state, update the visibility of the activities after
// we fetch the visible tasks to be controlled by the animation
@@ -261,16 +263,16 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
throw e;
} finally {
mService.continueWindowLayout();
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
private void finishAnimation(@RecentsAnimationController.ReorderMode int reorderMode,
boolean sendUserLeaveHint) {
synchronized (mService.mGlobalLock) {
- if (DEBUG) Slog.d(TAG, "onAnimationFinished(): controller="
- + mWindowManager.getRecentsAnimationController()
- + " reorderMode=" + reorderMode);
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
+ "onAnimationFinished(): controller=%s reorderMode=%d",
+ mWindowManager.getRecentsAnimationController(), reorderMode);
// Unregister for stack order changes
mDefaultDisplay.unregisterStackOrderChangedListener(this);
@@ -295,7 +297,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
}
mWindowManager.inSurfaceTransaction(() -> {
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER,
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
"RecentsAnimation#onAnimationFinished_inSurfaceTransaction");
mService.deferWindowLayout();
try {
@@ -308,9 +310,10 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
final ActivityRecord targetActivity = targetStack != null
? targetStack.isInStackLocked(mLaunchedTargetActivity)
: null;
- if (DEBUG) Slog.d(TAG, "onAnimationFinished(): targetStack=" + targetStack
- + " targetActivity=" + targetActivity
- + " mRestoreTargetBehindStack=" + mRestoreTargetBehindStack);
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
+ "onAnimationFinished(): targetStack=%s targetActivity=%s "
+ + "mRestoreTargetBehindStack=%s",
+ targetStack, targetActivity, mRestoreTargetBehindStack);
if (targetActivity == null) {
return;
}
@@ -333,25 +336,28 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
targetStack.moveToFront("RecentsAnimation.onAnimationFinished()");
}
- if (DEBUG) {
+ if (WM_DEBUG_RECENTS_ANIMATIONS.isLogToAny()) {
final ActivityStack topStack = getTopNonAlwaysOnTopStack();
if (topStack != targetStack) {
- Slog.w(TAG, "Expected target stack=" + targetStack
- + " to be top most but found stack=" + topStack);
+ ProtoLog.w(WM_DEBUG_RECENTS_ANIMATIONS,
+ "Expected target stack=%s"
+ + " to be top most but found stack=%s",
+ targetStack, topStack);
}
}
} else if (reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION){
// Restore the target stack to its previous position
final ActivityDisplay display = targetActivity.getDisplay();
display.moveStackBehindStack(targetStack, mRestoreTargetBehindStack);
- if (DEBUG) {
+ if (WM_DEBUG_RECENTS_ANIMATIONS.isLogToAny()) {
final ActivityStack aboveTargetStack =
mDefaultDisplay.getStackAbove(targetStack);
if (mRestoreTargetBehindStack != null
&& aboveTargetStack != mRestoreTargetBehindStack) {
- Slog.w(TAG, "Expected target stack=" + targetStack
- + " to restored behind stack=" + mRestoreTargetBehindStack
- + " but it is behind stack=" + aboveTargetStack);
+ ProtoLog.w(WM_DEBUG_RECENTS_ANIMATIONS,
+ "Expected target stack=%s to restored behind stack=%s but"
+ + " it is behind stack=%s",
+ targetStack, mRestoreTargetBehindStack, aboveTargetStack);
}
}
} else {
@@ -388,7 +394,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
throw e;
} finally {
mService.continueWindowLayout();
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
});
}
@@ -402,7 +408,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
@Override
public void onStackOrderChanged(ActivityStack stack) {
- if (DEBUG) Slog.d(TAG, "onStackOrderChanged(): stack=" + stack);
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "onStackOrderChanged(): stack=%s", stack);
if (mDefaultDisplay.getIndexOf(stack) == -1 || !stack.shouldBeVisible(null)) {
// The stack is not visible, so ignore this change
return;
@@ -423,7 +429,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
// 1) The next launching task is not being animated by the recents animation
// 2) The next task is home activity. (i.e. pressing home key to back home in recents).
if ((!controller.isAnimatingTask(stack.getTaskStack().getTopChild())
- || controller.isTargetApp(stack.getTopActivity().mAppWindowToken))
+ || controller.isTargetApp(stack.getTopActivity()))
&& controller.shouldDeferCancelUntilNextTransition()) {
// Always prepare an app transition since we rely on the transition callbacks to cleanup
mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
@@ -486,7 +492,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
for (int i = targetStack.getChildCount() - 1; i >= 0; i--) {
final TaskRecord task = targetStack.getChildAt(i);
- if (task.userId == mUserId
+ if (task.mUserId == mUserId
&& task.getBaseIntent().getComponent().equals(mTargetIntent.getComponent())) {
return task.getTopActivity();
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 795a2ca67ac3..7a3e43b127f4 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -29,8 +29,8 @@ import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_W
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
import static com.android.server.wm.AnimationAdapterProto.REMOTE;
import static com.android.server.wm.BoundsAnimationController.FADE_IN;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS;
import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
import android.annotation.IntDef;
@@ -57,6 +57,7 @@ import android.view.SurfaceControl.Transaction;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.inputmethod.InputMethodManagerInternal;
+import com.android.server.protolog.common.ProtoLog;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import com.android.server.wm.utils.InsetUtils;
@@ -65,6 +66,7 @@ import com.google.android.collect.Sets;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.stream.Collectors;
/**
* Controls a single instance of the remote driven recents animation. In particular, this allows
@@ -96,11 +98,13 @@ public class RecentsAnimationController implements DeathRecipient {
private final ArrayList<WallpaperAnimationAdapter> mPendingWallpaperAnimations =
new ArrayList<>();
private final int mDisplayId;
- private final Runnable mFailsafeRunnable = () ->
- cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "failSafeRunnable");
+ private boolean mWillFinishToHome = false;
+ private final Runnable mFailsafeRunnable = () -> cancelAnimation(
+ mWillFinishToHome ? REORDER_MOVE_TO_TOP : REORDER_MOVE_TO_ORIGINAL_POSITION,
+ "failSafeRunnable");
// The recents component app token that is shown behind the visibile tasks
- private AppWindowToken mTargetAppToken;
+ private ActivityRecord mTargetActivityRecord;
private DisplayContent mDisplayContent;
private int mTargetActivityType;
private Rect mMinimizedHomeBounds = new Rect();
@@ -178,8 +182,8 @@ public class RecentsAnimationController implements DeathRecipient {
@Override
public TaskSnapshot screenshotTask(int taskId) {
- if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "screenshotTask(" + taskId + "):"
- + " mCanceled=" + mCanceled);
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
+ "screenshotTask(%d): mCanceled=%b", taskId, mCanceled);
final long token = Binder.clearCallingIdentity();
try {
synchronized (mService.getWindowManagerLock()) {
@@ -208,8 +212,8 @@ public class RecentsAnimationController implements DeathRecipient {
@Override
public void finish(boolean moveHomeToTop, boolean sendUserLeaveHint) {
- if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "finish(" + moveHomeToTop + "):"
- + " mCanceled=" + mCanceled);
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
+ "finish(%b): mCanceled=%b", moveHomeToTop, mCanceled);
final long token = Binder.clearCallingIdentity();
try {
synchronized (mService.getWindowManagerLock()) {
@@ -250,8 +254,8 @@ public class RecentsAnimationController implements DeathRecipient {
@Override
public void setInputConsumerEnabled(boolean enabled) {
- if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "setInputConsumerEnabled(" + enabled + "):"
- + " mCanceled=" + mCanceled);
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
+ "setInputConsumerEnabled(%s): mCanceled=%b", enabled, mCanceled);
final long token = Binder.clearCallingIdentity();
try {
synchronized (mService.getWindowManagerLock()) {
@@ -324,6 +328,13 @@ public class RecentsAnimationController implements DeathRecipient {
}
}
}
+
+ @Override
+ public void setWillFinishToHome(boolean willFinishToHome) {
+ synchronized (mService.getWindowManagerLock()) {
+ mWillFinishToHome = willFinishToHome;
+ }
+ }
};
/**
@@ -347,7 +358,8 @@ public class RecentsAnimationController implements DeathRecipient {
* because it may call cancelAnimation() which needs to properly clean up the controller
* in the window manager.
*/
- public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds) {
+ public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds,
+ ActivityRecord targetActivity) {
mTargetActivityType = targetActivityType;
mDisplayContent.mAppTransition.registerListenerLocked(mAppTransitionListener);
@@ -389,16 +401,12 @@ public class RecentsAnimationController implements DeathRecipient {
}
// Adjust the wallpaper visibility for the showing target activity
- final AppWindowToken recentsComponentAppToken =
- targetStack.getTopChild().getTopFullscreenAppToken();
- if (recentsComponentAppToken != null) {
- if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "setHomeApp("
- + recentsComponentAppToken.getName() + ")");
- mTargetAppToken = recentsComponentAppToken;
- if (recentsComponentAppToken.windowsCanBeWallpaperTarget()) {
- mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
- mDisplayContent.setLayoutNeeded();
- }
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
+ "setHomeApp(%s)", targetActivity.getName());
+ mTargetActivityRecord = targetActivity;
+ if (targetActivity.windowsCanBeWallpaperTarget()) {
+ mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ mDisplayContent.setLayoutNeeded();
}
// Save the minimized home height
@@ -418,7 +426,7 @@ public class RecentsAnimationController implements DeathRecipient {
@VisibleForTesting
AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) {
- if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "addAnimation(" + task.getName() + ")");
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "addAnimation(%s)", task.getName());
final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task,
isRecentTaskInvisible);
task.startAnimation(task.getPendingTransaction(), taskAdapter, false /* hidden */);
@@ -429,8 +437,8 @@ public class RecentsAnimationController implements DeathRecipient {
@VisibleForTesting
void removeAnimation(TaskAnimationAdapter taskAdapter) {
- if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "removeAnimation("
- + taskAdapter.mTask.mTaskId + ")");
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
+ "removeAnimation(%d)", taskAdapter.mTask.mTaskId);
taskAdapter.mTask.setCanAffectSystemUiFlags(true);
taskAdapter.mCapturedFinishCallback.onAnimationFinished(taskAdapter);
mPendingAnimations.remove(taskAdapter);
@@ -438,14 +446,14 @@ public class RecentsAnimationController implements DeathRecipient {
@VisibleForTesting
void removeWallpaperAnimation(WallpaperAnimationAdapter wallpaperAdapter) {
- if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "removeWallpaperAnimation()");
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "removeWallpaperAnimation()");
wallpaperAdapter.getLeashFinishedCallback().onAnimationFinished(wallpaperAdapter);
mPendingWallpaperAnimations.remove(wallpaperAdapter);
}
void startAnimation() {
- if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart
- + " mCanceled=" + mCanceled);
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
+ "startAnimation(): mPendingStart=%b mCanceled=%b", mPendingStart, mCanceled);
if (!mPendingStart || mCanceled) {
// Skip starting if we've already started or canceled the animation
return;
@@ -469,13 +477,13 @@ public class RecentsAnimationController implements DeathRecipient {
// insets for the target app window after a rotation
mDisplayContent.performLayout(false /* initial */, false /* updateInputWindows */);
- final Rect minimizedHomeBounds = mTargetAppToken != null
- && mTargetAppToken.inSplitScreenSecondaryWindowingMode()
+ final Rect minimizedHomeBounds = mTargetActivityRecord != null
+ && mTargetActivityRecord.inSplitScreenSecondaryWindowingMode()
? mMinimizedHomeBounds
: null;
final Rect contentInsets;
- if (mTargetAppToken != null && mTargetAppToken.findMainWindow() != null) {
- contentInsets = mTargetAppToken.findMainWindow().getContentInsets();
+ if (mTargetActivityRecord != null && mTargetActivityRecord.findMainWindow() != null) {
+ contentInsets = mTargetActivityRecord.findMainWindow().getContentInsets();
} else {
// If the window for the activity had not yet been created, use the display insets.
mService.getStableInsets(mDisplayId, mTmpRect);
@@ -483,19 +491,17 @@ public class RecentsAnimationController implements DeathRecipient {
}
mRunner.onAnimationStart(mController, appTargets, wallpaperTargets, contentInsets,
minimizedHomeBounds);
- if (DEBUG_RECENTS_ANIMATIONS) {
- Slog.d(TAG, "startAnimation(): Notify animation start:");
- for (int i = 0; i < mPendingAnimations.size(); i++) {
- final Task task = mPendingAnimations.get(i).mTask;
- Slog.d(TAG, "\t" + task.mTaskId);
- }
- }
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
+ "startAnimation(): Notify animation start: %s",
+ mPendingAnimations.stream()
+ .map(anim->anim.mTask.mTaskId).collect(Collectors.toList()));
} catch (RemoteException e) {
Slog.e(TAG, "Failed to start recents animation", e);
}
final SparseIntArray reasons = new SparseIntArray();
reasons.put(WINDOWING_MODE_FULLSCREEN, APP_TRANSITION_RECENTS_ANIM);
- mService.mAtmInternal.notifyAppTransitionStarting(reasons, SystemClock.uptimeMillis());
+ mService.mAtmInternal
+ .notifyAppTransitionStarting(reasons, SystemClock.elapsedRealtimeNanos());
}
private RemoteAnimationTarget[] createAppAnimations() {
@@ -513,7 +519,7 @@ public class RecentsAnimationController implements DeathRecipient {
}
private RemoteAnimationTarget[] createWallpaperAnimations() {
- if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "createWallpaperAnimations()");
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "createWallpaperAnimations()");
return WallpaperAnimationAdapter.startWallpaperAnimations(mService, 0L, 0L,
adapter -> {
synchronized (mService.mGlobalLock) {
@@ -533,7 +539,7 @@ public class RecentsAnimationController implements DeathRecipient {
}
private void cancelAnimation(@ReorderMode int reorderMode, boolean screenshot, String reason) {
- if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "cancelAnimation(): reason=" + reason);
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "cancelAnimation(): reason=%s", reason);
synchronized (mService.getWindowManagerLock()) {
if (mCanceled) {
// We've already canceled the animation
@@ -627,9 +633,7 @@ public class RecentsAnimationController implements DeathRecipient {
mRecentScreenshotAnimator = new SurfaceAnimator(
animatable,
() -> {
- if (DEBUG_RECENTS_ANIMATIONS) {
- Slog.d(TAG, "mRecentScreenshotAnimator finish");
- }
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "mRecentScreenshotAnimator finish");
mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
}, mService);
mRecentScreenshotAnimator.transferAnimation(task.mSurfaceAnimator);
@@ -637,9 +641,10 @@ public class RecentsAnimationController implements DeathRecipient {
}
void cleanupAnimation(@ReorderMode int reorderMode) {
- if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG,
- "cleanupAnimation(): Notify animation finished mPendingAnimations="
- + mPendingAnimations.size() + " reorderMode=" + reorderMode);
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
+ "cleanupAnimation(): Notify animation finished mPendingAnimations=%d "
+ + "reorderMode=%d",
+ mPendingAnimations.size(), reorderMode);
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) {
@@ -674,10 +679,10 @@ public class RecentsAnimationController implements DeathRecipient {
// We have deferred all notifications to the target app as a part of the recents animation,
// so if we are actually transitioning there, notify again here
- if (mTargetAppToken != null) {
+ if (mTargetActivityRecord != null) {
if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) {
mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(
- mTargetAppToken.token);
+ mTargetActivityRecord.token);
}
}
@@ -732,25 +737,26 @@ public class RecentsAnimationController implements DeathRecipient {
}
boolean isWallpaperVisible(WindowState w) {
- return w != null && w.mAttrs.type == TYPE_BASE_APPLICATION && w.mAppToken != null
- && mTargetAppToken == w.mAppToken && isTargetOverWallpaper();
+ return w != null && w.mAttrs.type == TYPE_BASE_APPLICATION && w.mActivityRecord != null
+ && mTargetActivityRecord == w.mActivityRecord && isTargetOverWallpaper();
}
/**
* @return Whether to use the input consumer to override app input to route home/recents.
*/
- boolean shouldApplyInputConsumer(AppWindowToken appToken) {
+ boolean shouldApplyInputConsumer(ActivityRecord activity) {
// Only apply the input consumer if it is enabled, it is not the target (home/recents)
// being revealed with the transition, and we are actively animating the app as a part of
// the animation
- return mInputConsumerEnabled && mTargetAppToken != appToken && isAnimatingApp(appToken);
+ return mInputConsumerEnabled && mTargetActivityRecord != activity
+ && isAnimatingApp(activity);
}
boolean updateInputConsumerForApp(InputWindowHandle inputWindowHandle,
boolean hasFocus) {
// Update the input consumer touchable region to match the target app main window
- final WindowState targetAppMainWindow = mTargetAppToken != null
- ? mTargetAppToken.findMainWindow()
+ final WindowState targetAppMainWindow = mTargetActivityRecord != null
+ ? mTargetActivityRecord.findMainWindow()
: null;
if (targetAppMainWindow != null) {
targetAppMainWindow.getBounds(mTmpRect);
@@ -761,15 +767,15 @@ public class RecentsAnimationController implements DeathRecipient {
return false;
}
- boolean isTargetApp(AppWindowToken token) {
- return mTargetAppToken != null && token == mTargetAppToken;
+ boolean isTargetApp(ActivityRecord activity) {
+ return mTargetActivityRecord != null && activity == mTargetActivityRecord;
}
private boolean isTargetOverWallpaper() {
- if (mTargetAppToken == null) {
+ if (mTargetActivityRecord == null) {
return false;
}
- return mTargetAppToken.windowsCanBeWallpaperTarget();
+ return mTargetActivityRecord.windowsCanBeWallpaperTarget();
}
boolean isAnimatingTask(Task task) {
@@ -790,12 +796,12 @@ public class RecentsAnimationController implements DeathRecipient {
return false;
}
- private boolean isAnimatingApp(AppWindowToken appToken) {
+ private boolean isAnimatingApp(ActivityRecord activity) {
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
final Task task = mPendingAnimations.get(i).mTask;
for (int j = task.getChildCount() - 1; j >= 0; j--) {
- final AppWindowToken app = task.getChildAt(j);
- if (app == appToken) {
+ final ActivityRecord app = task.getChildAt(j);
+ if (app == activity) {
return true;
}
}
@@ -805,7 +811,7 @@ public class RecentsAnimationController implements DeathRecipient {
boolean shouldIgnoreForAccessibility(WindowState windowState) {
final Task task = windowState.getTask();
- return task != null && isAnimatingTask(task) && !isTargetApp(windowState.mAppToken);
+ return task != null && isAnimatingTask(task) && !isTargetApp(windowState.mActivityRecord);
}
@VisibleForTesting
@@ -828,7 +834,7 @@ public class RecentsAnimationController implements DeathRecipient {
}
RemoteAnimationTarget createRemoteAnimationTarget() {
- final AppWindowToken topApp = mTask.getTopVisibleAppToken();
+ final ActivityRecord topApp = mTask.getTopVisibleActivity();
final WindowState mainWindow = topApp != null
? topApp.findMainWindow()
: null;
@@ -837,7 +843,7 @@ public class RecentsAnimationController implements DeathRecipient {
}
final Rect insets = new Rect();
mainWindow.getContentInsets(insets);
- InsetUtils.addInsets(insets, mainWindow.mAppToken.getLetterboxInsets());
+ InsetUtils.addInsets(insets, mainWindow.mActivityRecord.getLetterboxInsets());
final int mode = topApp.getActivityType() == mTargetActivityType
? MODE_OPENING
: MODE_CLOSING;
@@ -915,7 +921,7 @@ public class RecentsAnimationController implements DeathRecipient {
pw.print(innerPrefix); pw.println("mCanceled=" + mCanceled);
pw.print(innerPrefix); pw.println("mInputConsumerEnabled=" + mInputConsumerEnabled);
pw.print(innerPrefix); pw.println("mSplitScreenMinimized=" + mSplitScreenMinimized);
- pw.print(innerPrefix); pw.println("mTargetAppToken=" + mTargetAppToken);
+ pw.print(innerPrefix); pw.println("mTargetActivityRecord=" + mTargetActivityRecord);
pw.print(innerPrefix); pw.println("isTargetOverWallpaper=" + isTargetOverWallpaper());
pw.print(innerPrefix); pw.println("mRequestDeferCancelUntilNextTransition="
+ mRequestDeferCancelUntilNextTransition);
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 87bda4a545e1..c23ffd964e06 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -17,9 +17,8 @@
package com.android.server.wm;
import static com.android.server.wm.AnimationAdapterProto.REMOTE;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS;
import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_REMOTE_ANIMATIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -39,6 +38,8 @@ import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import com.android.internal.util.FastPrintWriter;
+import com.android.server.protolog.ProtoLogImpl;
+import com.android.server.protolog.common.ProtoLog;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import com.android.server.wm.utils.InsetUtils;
@@ -51,7 +52,6 @@ import java.util.ArrayList;
*/
class RemoteAnimationController implements DeathRecipient {
private static final String TAG = TAG_WITH_CLASS_NAME
- || (DEBUG_REMOTE_ANIMATIONS && !DEBUG_APP_TRANSITIONS)
? "RemoteAnimationController" : TAG_WM;
private static final long TIMEOUT_MS = 2000;
@@ -76,20 +76,20 @@ class RemoteAnimationController implements DeathRecipient {
}
/**
- * Creates an animation record for each individual {@link AppWindowToken}.
+ * Creates an animation record for each individual {@link ActivityRecord}.
*
- * @param appWindowToken The app to animate.
+ * @param activity The app to animate.
* @param position The position app bounds, in screen coordinates.
* @param stackBounds The stack bounds of the app relative to position.
* @param startBounds The stack bounds before the transition, in screen coordinates
* @return The record representing animation(s) to run on the app.
*/
- RemoteAnimationRecord createRemoteAnimationRecord(AppWindowToken appWindowToken,
- Point position, Rect stackBounds, Rect startBounds) {
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAnimationAdapter(): token="
- + appWindowToken);
+ RemoteAnimationRecord createRemoteAnimationRecord(ActivityRecord activity, Point position,
+ Rect stackBounds, Rect startBounds) {
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAnimationAdapter(): token=%s",
+ activity);
final RemoteAnimationRecord adapters =
- new RemoteAnimationRecord(appWindowToken, position, stackBounds, startBounds);
+ new RemoteAnimationRecord(activity, position, stackBounds, startBounds);
mPendingAnimations.add(adapters);
return adapters;
}
@@ -98,11 +98,11 @@ class RemoteAnimationController implements DeathRecipient {
* Called when the transition is ready to be started, and all leashes have been set up.
*/
void goodToGo() {
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo()");
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo()");
if (mPendingAnimations.isEmpty() || mCanceled) {
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo(): Animation finished already,"
- + " canceled=" + mCanceled
- + " mPendingAnimations=" + mPendingAnimations.size());
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
+ "goodToGo(): Animation finished already, canceled=%s mPendingAnimations=%d",
+ mCanceled, mPendingAnimations.size());
onAnimationFinished();
return;
}
@@ -115,7 +115,7 @@ class RemoteAnimationController implements DeathRecipient {
// Create the app targets
final RemoteAnimationTarget[] appTargets = createAppAnimations();
if (appTargets.length == 0) {
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo(): No apps to animate");
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo(): No apps to animate");
onAnimationFinished();
return;
}
@@ -131,8 +131,8 @@ class RemoteAnimationController implements DeathRecipient {
Slog.e(TAG, "Failed to start remote animation", e);
onAnimationFinished();
}
- if (DEBUG_REMOTE_ANIMATIONS) {
- Slog.d(TAG, "startAnimation(): Notify animation start:");
+ if (ProtoLogImpl.isEnabled(WM_DEBUG_REMOTE_ANIMATIONS)) {
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation(): Notify animation start:");
writeStartDebugStatement();
}
});
@@ -140,7 +140,7 @@ class RemoteAnimationController implements DeathRecipient {
}
void cancelAnimation(String reason) {
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "cancelAnimation(): reason=" + reason);
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "cancelAnimation(): reason=%s", reason);
synchronized (mService.getWindowManagerLock()) {
if (mCanceled) {
return;
@@ -152,28 +152,28 @@ class RemoteAnimationController implements DeathRecipient {
}
private void writeStartDebugStatement() {
- Slog.i(TAG, "Starting remote animation");
+ ProtoLog.i(WM_DEBUG_REMOTE_ANIMATIONS, "Starting remote animation");
final StringWriter sw = new StringWriter();
final FastPrintWriter pw = new FastPrintWriter(sw);
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
mPendingAnimations.get(i).mAdapter.dump(pw, "");
}
pw.close();
- Slog.i(TAG, sw.toString());
+ ProtoLog.i(WM_DEBUG_REMOTE_ANIMATIONS, "%s", sw.toString());
}
private RemoteAnimationTarget[] createAppAnimations() {
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAppAnimations()");
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAppAnimations()");
final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
final RemoteAnimationRecord wrappers = mPendingAnimations.get(i);
final RemoteAnimationTarget target = wrappers.createRemoteAnimationTarget();
if (target != null) {
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tAdd token=" + wrappers.mAppWindowToken);
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tAdd token=%s", wrappers.mActivityRecord);
targets.add(target);
} else {
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tRemove token="
- + wrappers.mAppWindowToken);
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tRemove token=%s",
+ wrappers.mActivityRecord);
// We can't really start an animation but we still need to make sure to finish the
// pending animation that was started by SurfaceAnimator
@@ -194,7 +194,7 @@ class RemoteAnimationController implements DeathRecipient {
}
private RemoteAnimationTarget[] createWallpaperAnimations() {
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createWallpaperAnimations()");
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createWallpaperAnimations()");
return WallpaperAnimationAdapter.startWallpaperAnimations(mService,
mRemoteAnimationAdapter.getDuration(),
mRemoteAnimationAdapter.getStatusBarTransitionDelay(),
@@ -207,15 +207,15 @@ class RemoteAnimationController implements DeathRecipient {
}
private void onAnimationFinished() {
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "onAnimationFinished(): mPendingAnimations="
- + mPendingAnimations.size());
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "onAnimationFinished(): mPendingAnimations=%d",
+ mPendingAnimations.size());
mHandler.removeCallbacks(mTimeoutRunnable);
synchronized (mService.mGlobalLock) {
unlinkToDeathOfRunner();
releaseFinishedCallback();
mService.openSurfaceTransaction();
try {
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG,
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
"onAnimationFinished(): Notify animation finished:");
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
final RemoteAnimationRecord adapters = mPendingAnimations.get(i);
@@ -228,14 +228,14 @@ class RemoteAnimationController implements DeathRecipient {
.onAnimationFinished(adapters.mThumbnailAdapter);
}
mPendingAnimations.remove(i);
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tapp=" + adapters.mAppWindowToken);
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tapp=%s", adapters.mActivityRecord);
}
for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
final WallpaperAnimationAdapter adapter = mPendingWallpaperAnimations.get(i);
adapter.getLeashFinishedCallback().onAnimationFinished(adapter);
mPendingWallpaperAnimations.remove(i);
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\twallpaper=" + adapter.getToken());
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\twallpaper=%s", adapter.getToken());
}
} catch (Exception e) {
Slog.e(TAG, "Failed to finish remote animation", e);
@@ -245,7 +245,7 @@ class RemoteAnimationController implements DeathRecipient {
}
}
setRunningRemoteAnimation(false);
- if (DEBUG_REMOTE_ANIMATIONS) Slog.i(TAG, "Finishing remote animation");
+ ProtoLog.i(WM_DEBUG_REMOTE_ANIMATIONS, "Finishing remote animation");
}
private void invokeAnimationCancelled() {
@@ -306,7 +306,7 @@ class RemoteAnimationController implements DeathRecipient {
@Override
public void onAnimationFinished() throws RemoteException {
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "app-onAnimationFinished(): mOuter=" + mOuter);
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "app-onAnimationFinished(): mOuter=%s", mOuter);
final long token = Binder.clearCallingIdentity();
try {
if (mOuter != null) {
@@ -326,7 +326,7 @@ class RemoteAnimationController implements DeathRecipient {
* to prevent memory leak.
*/
void release() {
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "app-release(): mOuter=" + mOuter);
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "app-release(): mOuter=%s", mOuter);
mOuter = null;
}
};
@@ -345,12 +345,12 @@ class RemoteAnimationController implements DeathRecipient {
RemoteAnimationAdapterWrapper mAdapter;
RemoteAnimationAdapterWrapper mThumbnailAdapter = null;
RemoteAnimationTarget mTarget;
- final AppWindowToken mAppWindowToken;
+ final ActivityRecord mActivityRecord;
final Rect mStartBounds;
- RemoteAnimationRecord(AppWindowToken appWindowToken, Point endPos, Rect endBounds,
+ RemoteAnimationRecord(ActivityRecord activityRecord, Point endPos, Rect endBounds,
Rect startBounds) {
- mAppWindowToken = appWindowToken;
+ mActivityRecord = activityRecord;
mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, endBounds);
if (startBounds != null) {
mStartBounds = new Rect(startBounds);
@@ -366,8 +366,8 @@ class RemoteAnimationController implements DeathRecipient {
}
RemoteAnimationTarget createRemoteAnimationTarget() {
- final Task task = mAppWindowToken.getTask();
- final WindowState mainWindow = mAppWindowToken.findMainWindow();
+ final Task task = mActivityRecord.getTask();
+ final WindowState mainWindow = mActivityRecord.findMainWindow();
if (task == null || mainWindow == null || mAdapter == null
|| mAdapter.mCapturedFinishCallback == null
|| mAdapter.mCapturedLeash == null) {
@@ -375,11 +375,11 @@ class RemoteAnimationController implements DeathRecipient {
}
final Rect insets = new Rect();
mainWindow.getContentInsets(insets);
- InsetUtils.addInsets(insets, mAppWindowToken.getLetterboxInsets());
+ InsetUtils.addInsets(insets, mActivityRecord.getLetterboxInsets());
mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(),
- mAdapter.mCapturedLeash, !mAppWindowToken.fillsParent(),
+ mAdapter.mCapturedLeash, !mActivityRecord.fillsParent(),
mainWindow.mWinAnimator.mLastClipRect, insets,
- mAppWindowToken.getPrefixOrderIndex(), mAdapter.mPosition,
+ mActivityRecord.getPrefixOrderIndex(), mAdapter.mPosition,
mAdapter.mStackBounds, task.getWindowConfiguration(), false /*isNotInRecents*/,
mThumbnailAdapter != null ? mThumbnailAdapter.mCapturedLeash : null,
mStartBounds);
@@ -387,10 +387,10 @@ class RemoteAnimationController implements DeathRecipient {
}
private int getMode() {
- final DisplayContent dc = mAppWindowToken.getDisplayContent();
- if (dc.mOpeningApps.contains(mAppWindowToken)) {
+ final DisplayContent dc = mActivityRecord.getDisplayContent();
+ if (dc.mOpeningApps.contains(mActivityRecord)) {
return RemoteAnimationTarget.MODE_OPENING;
- } else if (dc.mChangingApps.contains(mAppWindowToken)) {
+ } else if (dc.mChangingApps.contains(mActivityRecord)) {
return RemoteAnimationTarget.MODE_CHANGING;
} else {
return RemoteAnimationTarget.MODE_CLOSING;
@@ -420,10 +420,10 @@ class RemoteAnimationController implements DeathRecipient {
@Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
OnAnimationFinishedCallback finishCallback) {
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "startAnimation");
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation");
// Restore z-layering, position and stack crop until client has a chance to modify it.
- t.setLayer(animationLeash, mRecord.mAppWindowToken.getPrefixOrderIndex());
+ t.setLayer(animationLeash, mRecord.mActivityRecord.getPrefixOrderIndex());
if (mRecord.mStartBounds != null) {
t.setPosition(animationLeash, mRecord.mStartBounds.left, mRecord.mStartBounds.top);
t.setWindowCrop(animationLeash, mRecord.mStartBounds.width(),
@@ -464,7 +464,7 @@ class RemoteAnimationController implements DeathRecipient {
@Override
public void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("token="); pw.println(mRecord.mAppWindowToken);
+ pw.print(prefix); pw.print("token="); pw.println(mRecord.mActivityRecord);
if (mRecord.mTarget != null) {
pw.print(prefix); pw.println("Target:");
mRecord.mTarget.dump(pw, prefix + " ");
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index 525b017cb3ba..999d5761b7d3 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -638,17 +638,23 @@ public class RootActivityContainer extends ConfigurationContainer
if (displayContent != null) {
config = displayContent.updateOrientation(
getDisplayOverrideConfiguration(displayId),
- starting != null && starting.mayFreezeScreenLocked(starting.app)
+ starting != null && starting.mayFreezeScreenLocked()
? starting.appToken : null,
true /* forceUpdate */);
}
+ // Visibilities may change so let the starting activity have a chance to report. Can't do it
+ // when visibility is changed in each AppWindowToken because it may trigger wrong
+ // configuration push because the visibility of some activities may not be updated yet.
+ if (starting != null) {
+ starting.reportDescendantOrientationChangeIfNeeded();
+ }
if (starting != null && markFrozenIfConfigChanged && config != null) {
starting.frozenBeforeDestroy = true;
}
- if (displayContent != null && displayContent.mAcitvityDisplay != null) {
+ if (displayContent != null && displayContent.mActivityDisplay != null) {
// Update the configuration of the activities on the display.
- return displayContent.mAcitvityDisplay.updateDisplayOverrideConfigurationLocked(config,
+ return displayContent.mActivityDisplay.updateDisplayOverrideConfigurationLocked(config,
starting, deferResume, null /* result */);
} else {
return true;
@@ -977,7 +983,7 @@ public class RootActivityContainer extends ConfigurationContainer
stack.resize(task.getRequestedOverrideBounds(), null /* tempTaskBounds */,
null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS, !DEFER_RESUME);
- if (task.mActivities.size() == 1) {
+ if (task.getChildCount() == 1) {
// Defer resume until below, and do not schedule PiP changes until we animate below
task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME,
false /* schedulePictureInPictureModeChange */, reason);
@@ -1097,7 +1103,7 @@ public class RootActivityContainer extends ConfigurationContainer
}
}
}
- return finishedTask != null ? finishedTask.taskId : INVALID_TASK_ID;
+ return finishedTask != null ? finishedTask.mTaskId : INVALID_TASK_ID;
}
boolean resumeFocusedStacksTopActivities() {
@@ -1254,14 +1260,14 @@ public class RootActivityContainer extends ConfigurationContainer
int[] taskUserIds = new int[numTasks];
for (int i = 0; i < numTasks; ++i) {
final TaskRecord task = tasks.get(i);
- taskIds[i] = task.taskId;
+ taskIds[i] = task.mTaskId;
taskNames[i] = task.origActivity != null ? task.origActivity.flattenToString()
: task.realActivity != null ? task.realActivity.flattenToString()
: task.getTopActivity() != null ? task.getTopActivity().packageName
: "unknown";
taskBounds[i] = new Rect();
task.getWindowContainerBounds(taskBounds[i]);
- taskUserIds[i] = task.userId;
+ taskUserIds[i] = task.mUserId;
}
info.taskIds = taskIds;
info.taskNames = taskNames;
@@ -1774,8 +1780,12 @@ public class RootActivityContainer extends ConfigurationContainer
// If {@code r} is already in target display and its task is the same as the candidate task,
// the intention should be getting a launch stack for the reusable activity, so we can use
// the existing stack.
- if (r.getDisplayId() == displayId && r.getTaskRecord() == candidateTask) {
- return candidateTask.getStack();
+ if (candidateTask != null
+ && (r.getTaskRecord() == null || r.getTaskRecord() == candidateTask)) {
+ final int attachedDisplayId = r.getDisplayId();
+ if (attachedDisplayId == INVALID_DISPLAY || attachedDisplayId == displayId) {
+ return candidateTask.getStack();
+ }
}
int windowingMode;
@@ -2097,7 +2107,7 @@ public class RootActivityContainer extends ConfigurationContainer
// picker for personal files, opened by a work app, should still get locked.
if (taskTopActivityIsUser(task, userId)) {
mService.getTaskChangeNotificationController().notifyTaskProfileLocked(
- task.taskId, userId);
+ task.mTaskId, userId);
}
}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 4365d0325545..17f5abdda2c7 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -29,20 +29,19 @@ import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
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.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
+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_SHOW_SURFACE_ALLOC;
+import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.wm.RootWindowContainerProto.DISPLAYS;
import static com.android.server.wm.RootWindowContainerProto.WINDOWS;
import static com.android.server.wm.RootWindowContainerProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEEP_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_KEEP_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.WINDOW_FREEZE_TIMEOUT;
@@ -50,7 +49,6 @@ import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_NONE;
-import static com.android.server.wm.WindowManagerService.logSurface;
import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
@@ -80,6 +78,7 @@ import android.view.SurfaceControl;
import android.view.WindowManager;
import com.android.server.EventLogTags;
+import com.android.server.protolog.common.ProtoLog;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -130,7 +129,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
private int mTopFocusedDisplayId = INVALID_DISPLAY;
// Map from the PID to the top most app which has a focused window of the process.
- final HashMap<Integer, AppWindowToken> mTopFocusedAppByProcess = new HashMap<>();
+ final HashMap<Integer, ActivityRecord> mTopFocusedAppByProcess = new HashMap<>();
// Only a separate transaction until we separate the apply surface changes
// transaction from the global transaction.
@@ -146,9 +145,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
};
private static final Consumer<WindowState> sRemoveReplacedWindowsConsumer = w -> {
- final AppWindowToken aToken = w.mAppToken;
- if (aToken != null) {
- aToken.removeReplacedWindowIfNeeded(w);
+ final ActivityRecord activity = w.mActivityRecord;
+ if (activity != null) {
+ activity.removeReplacedWindowIfNeeded(w);
}
};
@@ -176,7 +175,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (newFocus != null) {
final int pidOfNewFocus = newFocus.mSession.mPid;
if (mTopFocusedAppByProcess.get(pidOfNewFocus) == null) {
- mTopFocusedAppByProcess.put(pidOfNewFocus, newFocus.mAppToken);
+ mTopFocusedAppByProcess.put(pidOfNewFocus, newFocus.mActivityRecord);
}
if (topFocusedDisplayId == INVALID_DISPLAY) {
topFocusedDisplayId = dc.getDisplayId();
@@ -195,8 +194,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
mTopFocusedDisplayId = topFocusedDisplayId;
mWmService.mInputManager.setFocusedDisplay(topFocusedDisplayId);
mWmService.mPolicy.setTopFocusedDisplay(topFocusedDisplayId);
- if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "New topFocusedDisplayId="
- + topFocusedDisplayId);
+ ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "New topFocusedDisplayId=%d",
+ topFocusedDisplayId);
}
return changed;
}
@@ -231,7 +230,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
final DisplayContent existing = getDisplayContent(displayId);
if (existing != null) {
- existing.mAcitvityDisplay = activityDisplay;
+ existing.mActivityDisplay = activityDisplay;
existing.initializeDisplayOverrideConfiguration();
return existing;
}
@@ -331,12 +330,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
* NOTE: Only one AppWindowToken is allowed to exist in the system for a binder token, since
* AppWindowToken represents an activity which can only exist on one display.
*/
- AppWindowToken getAppWindowToken(IBinder binder) {
+ ActivityRecord getActivityRecord(IBinder binder) {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final DisplayContent dc = mChildren.get(i);
- final AppWindowToken atoken = dc.getAppWindowToken(binder);
- if (atoken != null) {
- return atoken;
+ final ActivityRecord activity = dc.getActivityRecord(binder);
+ if (activity != null) {
+ return activity;
}
}
return null;
@@ -457,13 +456,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
void removeReplacedWindows() {
- if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION removeReplacedWindows");
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, ">>> OPEN TRANSACTION removeReplacedWindows");
mWmService.openSurfaceTransaction();
try {
forAllWindows(sRemoveReplacedWindowsConsumer, true /* traverseTopToBottom */);
} finally {
mWmService.closeSurfaceTransaction("removeReplacedWindows");
- if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION removeReplacedWindows");
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION removeReplacedWindows");
}
}
@@ -539,11 +538,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
Slog.w(TAG_WM,
"Looks like we have reclaimed some memory, clearing surface for retry.");
if (surfaceController != null) {
- if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) logSurface(winAnimator.mWin,
- "RECOVER DESTROY", false);
+ ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
+ "SURFACE RECOVER DESTROY: %s", winAnimator.mWin);
winAnimator.destroySurface();
- if (winAnimator.mWin.mAppToken != null) {
- winAnimator.mWin.mAppToken.removeStartingWindow();
+ if (winAnimator.mWin.mActivityRecord != null) {
+ winAnimator.mWin.mActivityRecord.removeStartingWindow();
}
}
@@ -651,8 +650,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
handleResizingWindows();
- if (DEBUG_ORIENTATION && mWmService.mDisplayFrozen) Slog.v(TAG,
- "With display frozen, orientationChangeComplete=" + mOrientationChangeComplete);
+ if (mWmService.mDisplayFrozen) {
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "With display frozen, orientationChangeComplete=%b",
+ mOrientationChangeComplete);
+ }
if (mOrientationChangeComplete) {
if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
@@ -714,11 +716,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
if (mUpdateRotation) {
- if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-rotate rotation");
+ ProtoLog.d(WM_DEBUG_ORIENTATION, "Performing post-rotate rotation");
mUpdateRotation = updateRotationUnchecked();
}
- if (mWmService.mWaitingForDrawnCallback != null
+ if (!mWmService.mWaitingForDrawnCallbacks.isEmpty()
|| (mOrientationChangeComplete && !isLayoutNeeded()
&& !mUpdateRotation)) {
mWmService.checkDrawnWindowsLocked();
@@ -811,18 +813,18 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
final int defaultDw = defaultInfo.logicalWidth;
final int defaultDh = defaultInfo.logicalHeight;
if (mWmService.mWatermark != null) {
- mWmService.mWatermark.positionSurface(defaultDw, defaultDh);
+ mWmService.mWatermark.positionSurface(defaultDw, defaultDh, mDisplayTransaction);
}
if (mWmService.mStrictModeFlash != null) {
- mWmService.mStrictModeFlash.positionSurface(defaultDw, defaultDh);
+ mWmService.mStrictModeFlash.positionSurface(defaultDw, defaultDh, mDisplayTransaction);
}
if (mWmService.mCircularDisplayMask != null) {
mWmService.mCircularDisplayMask.positionSurface(defaultDw, defaultDh,
- mWmService.getDefaultDisplayRotation());
+ mWmService.getDefaultDisplayRotation(), mDisplayTransaction);
}
if (mWmService.mEmulatorDisplayOverlay != null) {
mWmService.mEmulatorDisplayOverlay.positionSurface(defaultDw, defaultDh,
- mWmService.getDefaultDisplayRotation());
+ mWmService.getDefaultDisplayRotation(), mDisplayTransaction);
}
final int count = mChildren.size();
@@ -868,29 +870,26 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
final int privateflags = attrs.privateFlags;
boolean displayHasContent = false;
- if (DEBUG_KEEP_SCREEN_ON) {
- Slog.d(TAG_KEEP_SCREEN_ON, "handleNotObscuredLocked w: " + w
- + ", w.mHasSurface: " + w.mHasSurface
- + ", w.isOnScreen(): " + onScreen
- + ", w.isDisplayedLw(): " + w.isDisplayedLw()
- + ", w.mAttrs.userActivityTimeout: " + w.mAttrs.userActivityTimeout);
- }
+ 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);
if (w.mHasSurface && onScreen) {
if (!syswin && w.mAttrs.userActivityTimeout >= 0 && mUserActivityTimeout < 0) {
mUserActivityTimeout = w.mAttrs.userActivityTimeout;
- if (DEBUG_KEEP_SCREEN_ON) {
- Slog.d(TAG, "mUserActivityTimeout set to " + mUserActivityTimeout);
- }
+ ProtoLog.d(WM_DEBUG_KEEP_SCREEN_ON, "mUserActivityTimeout set to %d",
+ mUserActivityTimeout);
}
}
if (w.mHasSurface && canBeSeen) {
if ((attrFlags & FLAG_KEEP_SCREEN_ON) != 0) {
mHoldScreen = w.mSession;
mHoldScreenWindow = w;
- } else if (DEBUG_KEEP_SCREEN_ON && w == mWmService.mLastWakeLockHoldingWindow) {
- Slog.d(TAG_KEEP_SCREEN_ON, "handleNotObscuredLocked: " + w + " was holding "
- + "screen wakelock but no longer has FLAG_KEEP_SCREEN_ON!!! called by"
- + Debug.getCallers(10));
+ } else if (w == mWmService.mLastWakeLockHoldingWindow) {
+ ProtoLog.d(WM_DEBUG_KEEP_SCREEN_ON,
+ "handleNotObscuredLocked: %s was holding screen wakelock but no longer "
+ + "has FLAG_KEEP_SCREEN_ON!!! called by%s",
+ w, Debug.getCallers(10));
}
if (!syswin && w.mAttrs.screenBrightness >= 0 && mScreenBrightness < 0) {
mScreenBrightness = w.mAttrs.screenBrightness;
@@ -1068,7 +1067,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
void positionChildAt(int position, DisplayContent child, boolean includingParents) {
super.positionChildAt(position, child, includingParents);
if (mRootActivityContainer != null) {
- mRootActivityContainer.onChildPositionChanged(child.mAcitvityDisplay, position);
+ mRootActivityContainer.onChildPositionChanged(child.mActivityDisplay, position);
}
}
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 25aceb935019..b4467f9683c0 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -16,10 +16,9 @@
package com.android.server.wm;
+import static com.android.server.wm.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
import static com.android.server.wm.ScreenRotationAnimationProto.ANIMATION_RUNNING;
import static com.android.server.wm.ScreenRotationAnimationProto.STARTED;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
@@ -27,8 +26,8 @@ import static com.android.server.wm.WindowStateAnimator.WINDOW_FREEZE_LAYER;
import android.content.Context;
import android.graphics.Matrix;
+import android.graphics.Point;
import android.graphics.Rect;
-import android.util.BoostFramework;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
@@ -40,8 +39,38 @@ import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Transformation;
+import com.android.server.protolog.common.ProtoLog;
+
import java.io.PrintWriter;
+/**
+ * This class handles the rotation animation when the device is rotated.
+ *
+ * <p>
+ * The screen rotation animation is composed of 4 different part:
+ * <ul>
+ * <li> The screenshot: <p>
+ * A screenshot of the whole screen prior the change of orientation is taken to hide the
+ * element resizing below. The screenshot is then animated to rotate and cross-fade to
+ * the new orientation with the content in the new orientation.
+ *
+ * <li> The windows on the display: <p>y
+ * Once the device is rotated, the screen and its content are in the new orientation. The
+ * animation first rotate the new content into the old orientation to then be able to
+ * animate to the new orientation
+ *
+ * <li> The exiting Blackframe: <p>
+ * Because the change of orientation might change the width and height of the content (i.e
+ * when rotating from portrait to landscape) we "crop" the new content using black frames
+ * around the screenshot so the new content does not go beyond the screenshot's bounds
+ *
+ * <li> The entering Blackframe: <p>
+ * The enter Blackframe is similar to the exit Blackframe but is only used when a custom
+ * rotation animation is used and matches the new content size instead of the screenshot.
+ * </ul>
+ *
+ * Each part has its own Surface which are then animated by {@link SurfaceAnimator}s.
+ */
class ScreenRotationAnimation {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ScreenRotationAnimation" : TAG_WM;
@@ -54,7 +83,6 @@ class ScreenRotationAnimation {
private static final int SCREEN_FREEZE_LAYER_SCREENSHOT = SCREEN_FREEZE_LAYER_BASE + 1;
private static final int SCREEN_FREEZE_LAYER_EXIT = SCREEN_FREEZE_LAYER_BASE + 2;
- private BoostFramework mPerf = null;
private boolean mIsPerfLockAcquired = false;
private final Context mContext;
@@ -70,6 +98,8 @@ class ScreenRotationAnimation {
private final Matrix mSnapshotFinalMatrix = new Matrix();
private final Matrix mExitFrameFinalMatrix = new Matrix();
private final WindowManagerService mService;
+ private SurfaceControl mEnterBlackFrameLayer;
+ private SurfaceControl mRotationLayer;
private SurfaceControl mSurfaceControl;
private BlackFrame mEnteringBlackFrame;
private int mWidth, mHeight;
@@ -85,15 +115,14 @@ class ScreenRotationAnimation {
// rotations.
private Animation mRotateExitAnimation;
private Animation mRotateEnterAnimation;
+ private Animation mRotateAlphaAnimation;
private boolean mStarted;
private boolean mAnimRunning;
private boolean mFinishAnimReady;
private long mFinishAnimStartTime;
private boolean mForceDefaultOrientation;
private BlackFrame mExitingBlackFrame;
- private boolean mMoreRotateEnter;
- private boolean mMoreRotateExit;
- private long mHalfwayPoint;
+ private SurfaceRotationAnimationController mSurfaceRotationAnimationController;
public ScreenRotationAnimation(Context context, DisplayContent displayContent,
boolean fixedToUserRotation, boolean isSecure, WindowManagerService service) {
@@ -102,8 +131,6 @@ class ScreenRotationAnimation {
mDisplayContent = displayContent;
displayContent.getBounds(mOriginalDisplayRect);
- mPerf = new BoostFramework();
-
// Screenshot does NOT include rotation!
final Display display = displayContent.getDisplay();
int originalRotation = display.getRotation();
@@ -132,11 +159,23 @@ class ScreenRotationAnimation {
mOriginalRotation = originalRotation;
mOriginalWidth = originalWidth;
mOriginalHeight = originalHeight;
+ mSurfaceRotationAnimationController = new SurfaceRotationAnimationController();
final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
try {
- mSurfaceControl = displayContent.makeOverlay()
+ mRotationLayer = displayContent.makeOverlay()
+ .setName("RotationLayer")
+ .setContainerLayer()
+ .build();
+
+ mEnterBlackFrameLayer = displayContent.makeOverlay()
+ .setName("EnterBlackFrameLayer")
+ .setContainerLayer()
+ .build();
+
+ mSurfaceControl = displayContent.makeSurface(null)
.setName("ScreenshotSurface")
+ .setParent(mRotationLayer)
.setBufferSize(mWidth, mHeight)
.setSecure(isSecure)
.build();
@@ -166,8 +205,10 @@ class ScreenRotationAnimation {
if (gb.containsSecureLayers()) {
t.setSecure(mSurfaceControl, true);
}
+ t.setLayer(mRotationLayer, SCREEN_FREEZE_LAYER_BASE);
t.setLayer(mSurfaceControl, SCREEN_FREEZE_LAYER_SCREENSHOT);
t.setAlpha(mSurfaceControl, 0);
+ t.show(mRotationLayer);
t.show(mSurfaceControl);
} else {
Slog.w(TAG, "Unable to take screenshot of display " + displayId);
@@ -177,10 +218,8 @@ class ScreenRotationAnimation {
Slog.w(TAG, "Unable to allocate freeze surface", e);
}
- if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
- Slog.i(TAG_WM,
- " FREEZE " + mSurfaceControl + ": CREATE");
- }
+ ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
+ " FREEZE %s: CREATE", mSurfaceControl);
setRotation(t, originalRotation);
t.apply();
}
@@ -217,22 +256,26 @@ class ScreenRotationAnimation {
return mSurfaceControl != null;
}
- private void setSnapshotTransform(SurfaceControl.Transaction t, Matrix matrix, float alpha) {
- if (mSurfaceControl != null) {
- matrix.getValues(mTmpFloats);
- float x = mTmpFloats[Matrix.MTRANS_X];
- float y = mTmpFloats[Matrix.MTRANS_Y];
- if (mForceDefaultOrientation) {
- mDisplayContent.getBounds(mCurrentDisplayRect);
- x -= mCurrentDisplayRect.left;
- y -= mCurrentDisplayRect.top;
- }
- t.setPosition(mSurfaceControl, x, y);
- t.setMatrix(mSurfaceControl,
- mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
- mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
- t.setAlpha(mSurfaceControl, alpha);
+ private void setRotationTransform(SurfaceControl.Transaction t, Matrix matrix) {
+ if (mRotationLayer == null) {
+ return;
}
+ matrix.getValues(mTmpFloats);
+ float x = mTmpFloats[Matrix.MTRANS_X];
+ float y = mTmpFloats[Matrix.MTRANS_Y];
+ if (mForceDefaultOrientation) {
+ mDisplayContent.getBounds(mCurrentDisplayRect);
+ x -= mCurrentDisplayRect.left;
+ y -= mCurrentDisplayRect.top;
+ }
+ t.setPosition(mRotationLayer, x, y);
+ t.setMatrix(mRotationLayer,
+ mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
+ mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
+
+ t.setAlpha(mSurfaceControl, (float) 1.0);
+ t.setAlpha(mRotationLayer, (float) 1.0);
+ t.show(mRotationLayer);
}
public void printTo(String prefix, PrintWriter pw) {
@@ -243,7 +286,9 @@ class ScreenRotationAnimation {
if (mExitingBlackFrame != null) {
mExitingBlackFrame.printTo(prefix + " ", pw);
}
- pw.print(prefix); pw.print("mEnteringBlackFrame="); pw.println(mEnteringBlackFrame);
+ pw.print(prefix);
+ pw.print("mEnteringBlackFrame=");
+ pw.println(mEnteringBlackFrame);
if (mEnteringBlackFrame != null) {
mEnteringBlackFrame.printTo(prefix + " ", pw);
}
@@ -289,7 +334,7 @@ class ScreenRotationAnimation {
int delta = DisplayContent.deltaRotation(rotation, Surface.ROTATION_0);
createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
- setSnapshotTransform(t, mSnapshotInitialMatrix, 1.0f);
+ setRotationTransform(t, mSnapshotInitialMatrix);
}
/**
@@ -310,6 +355,9 @@ class ScreenRotationAnimation {
// Figure out how the screen has moved from the original rotation.
int delta = DisplayContent.deltaRotation(mCurRotation, mOriginalRotation);
+ mRotateAlphaAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_alpha);
+
final boolean customAnim;
if (exitAnim != 0 && enterAnim != 0) {
customAnim = true;
@@ -359,6 +407,8 @@ class ScreenRotationAnimation {
mRotateExitAnimation.scaleCurrentDuration(animationScale);
mRotateEnterAnimation.restrictDuration(maxAnimationDuration);
mRotateEnterAnimation.scaleCurrentDuration(animationScale);
+ mRotateAlphaAnimation.restrictDuration(maxAnimationDuration);
+ mRotateAlphaAnimation.scaleCurrentDuration(animationScale);
if (!customAnim && mExitingBlackFrame == null) {
try {
@@ -379,13 +429,12 @@ class ScreenRotationAnimation {
outer = mCurrentDisplayRect;
inner = mOriginalDisplayRect;
} else {
- outer = new Rect(-mOriginalWidth * 1, -mOriginalHeight * 1,
- mOriginalWidth * 2, mOriginalHeight * 2);
- inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
+ outer = new Rect(-mWidth, -mHeight, mWidth * 2, mHeight * 2);
+ inner = new Rect(0, 0, mWidth, mHeight);
}
mExitingBlackFrame = new BlackFrame(mService.mTransactionFactory, t, outer, inner,
- SCREEN_FREEZE_LAYER_EXIT, mDisplayContent, mForceDefaultOrientation);
- mExitingBlackFrame.setMatrix(t, mFrameInitialMatrix);
+ SCREEN_FREEZE_LAYER_EXIT, mDisplayContent, mForceDefaultOrientation,
+ mRotationLayer);
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
}
@@ -393,16 +442,17 @@ class ScreenRotationAnimation {
if (customAnim && mEnteringBlackFrame == null) {
try {
- Rect outer = new Rect(-finalWidth * 1, -finalHeight * 1,
+ Rect outer = new Rect(-finalWidth, -finalHeight,
finalWidth * 2, finalHeight * 2);
Rect inner = new Rect(0, 0, finalWidth, finalHeight);
mEnteringBlackFrame = new BlackFrame(mService.mTransactionFactory, t, outer, inner,
- SCREEN_FREEZE_LAYER_ENTER, mDisplayContent, false);
+ SCREEN_FREEZE_LAYER_ENTER, mDisplayContent, false, mEnterBlackFrameLayer);
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
}
}
+ mSurfaceRotationAnimationController.startAnimation();
return true;
}
@@ -427,14 +477,27 @@ class ScreenRotationAnimation {
}
public void kill() {
+ if (mSurfaceRotationAnimationController != null) {
+ mSurfaceRotationAnimationController.cancel();
+ mSurfaceRotationAnimationController = null;
+ }
if (mSurfaceControl != null) {
- if (SHOW_TRANSACTIONS ||
- SHOW_SURFACE_ALLOC) {
- Slog.i(TAG_WM,
- " FREEZE " + mSurfaceControl + ": DESTROY");
- }
- mService.mTransactionFactory.get().remove(mSurfaceControl).apply();
+ ProtoLog.i(WM_SHOW_SURFACE_ALLOC, " FREEZE %s: DESTROY", mSurfaceControl);
mSurfaceControl = null;
+ SurfaceControl.Transaction t = mService.mTransactionFactory.get();
+ if (mRotationLayer != null) {
+ if (mRotationLayer.isValid()) {
+ t.remove(mRotationLayer);
+ }
+ mRotationLayer = null;
+ }
+ if (mEnterBlackFrameLayer != null) {
+ if (mEnterBlackFrameLayer.isValid()) {
+ t.remove(mEnterBlackFrameLayer);
+ }
+ mEnterBlackFrameLayer = null;
+ }
+ t.apply();
}
if (mExitingBlackFrame != null) {
mExitingBlackFrame.kill();
@@ -452,140 +515,186 @@ class ScreenRotationAnimation {
mRotateEnterAnimation.cancel();
mRotateEnterAnimation = null;
}
- if (mPerf != null && mIsPerfLockAcquired) {
- mPerf.perfLockRelease();
- mIsPerfLockAcquired = false;
+ if (mRotateAlphaAnimation != null) {
+ mRotateAlphaAnimation.cancel();
+ mRotateAlphaAnimation = null;
}
}
public boolean isAnimating() {
- return hasAnimations();
+ return mSurfaceRotationAnimationController != null
+ && mSurfaceRotationAnimationController.isAnimating();
}
public boolean isRotating() {
return mCurRotation != mOriginalRotation;
}
- private boolean hasAnimations() {
- return mRotateEnterAnimation != null || mRotateExitAnimation != null;
+ public Transformation getEnterTransformation() {
+ return mEnterTransformation;
}
- private boolean stepAnimation(long now) {
- if (now > mHalfwayPoint) {
- mHalfwayPoint = Long.MAX_VALUE;
- }
- if (mFinishAnimReady && mFinishAnimStartTime < 0) {
- mFinishAnimStartTime = now;
- }
-
- mMoreRotateExit = false;
- if (mRotateExitAnimation != null) {
- mMoreRotateExit = mRotateExitAnimation.getTransformation(now,
- mRotateExitTransformation);
- }
-
- mMoreRotateEnter = false;
- if (mRotateEnterAnimation != null) {
- mMoreRotateEnter = mRotateEnterAnimation.getTransformation(now,
- mRotateEnterTransformation);
- }
-
- if (!mMoreRotateExit) {
- if (mRotateExitAnimation != null) {
- mRotateExitAnimation.cancel();
- mRotateExitAnimation = null;
- mRotateExitTransformation.clear();
+ /**
+ * Utility class that runs a {@link ScreenRotationAnimation} on the {@link
+ * SurfaceAnimationRunner}.
+ * <p>
+ * The rotation animation is divided into the following hierarchy:
+ * <ul>
+ * <li> A first rotation layer, containing the blackframes. This layer is animated by the
+ * "screen_rotate_X_exit" that applies a scale and rotate and where X is value of the rotation.
+ * <ul>
+ * <li> A child layer containing the screenshot on which is added an animation of it's
+ * alpha channel ("screen_rotate_alpha") and that will rotate with his parent layer.</li>
+ * </ul>
+ * <li> A second rotation layer used when custom animations are passed in
+ * {@link ScreenRotationAnimation#startAnimation(
+ * SurfaceControl.Transaction, long, float, int, int, int, int)}.
+ * </ul>
+ * <p>
+ * Thus an {@link LocalAnimationAdapter.AnimationSpec} is created for each of
+ * this three {@link SurfaceControl}s which then delegates the animation to the
+ * {@link ScreenRotationAnimation}.
+ */
+ class SurfaceRotationAnimationController {
+ private SurfaceAnimator mDisplayAnimator;
+ private SurfaceAnimator mEnterBlackFrameAnimator;
+ private SurfaceAnimator mScreenshotRotationAnimator;
+ private SurfaceAnimator mRotateScreenAnimator;
+ private final Runnable mNoopCallback = () -> { // b/141177184
+ };
+
+ /**
+ * Start the rotation animation of the display and the screenshot on the
+ * {@link SurfaceAnimationRunner}.
+ */
+ void startAnimation() {
+ mRotateScreenAnimator = startScreenshotAlphaAnimation();
+ mDisplayAnimator = startDisplayRotation();
+ if (mExitingBlackFrame != null) {
+ mScreenshotRotationAnimator = startScreenshotRotationAnimation();
}
- if (mPerf != null && mIsPerfLockAcquired) {
- mPerf.perfLockRelease();
- mIsPerfLockAcquired = false;
+ if (mEnteringBlackFrame != null) {
+ mEnterBlackFrameAnimator = startEnterBlackFrameAnimation();
}
}
- if (!mMoreRotateEnter) {
- if (mRotateEnterAnimation != null) {
- mRotateEnterAnimation.cancel();
- mRotateEnterAnimation = null;
- mRotateEnterTransformation.clear();
- }
- if (mPerf != null && mIsPerfLockAcquired) {
- mPerf.perfLockRelease();
- mIsPerfLockAcquired = false;
+ private SimpleSurfaceAnimatable.Builder initializeBuilder() {
+ return new SimpleSurfaceAnimatable.Builder()
+ .setPendingTransactionSupplier(mDisplayContent::getPendingTransaction)
+ .setCommitTransactionRunnable(mDisplayContent::commitPendingTransaction)
+ .setAnimationLeashSupplier(mDisplayContent::makeOverlay);
+ }
+
+ private SurfaceAnimator startDisplayRotation() {
+ return startAnimation(initializeBuilder()
+ .setAnimationLeashParent(mDisplayContent.getSurfaceControl())
+ .setSurfaceControl(mDisplayContent.getWindowingLayer())
+ .setParentSurfaceControl(mDisplayContent.getSurfaceControl())
+ .setWidth(mDisplayContent.getSurfaceWidth())
+ .setHeight(mDisplayContent.getSurfaceHeight())
+ .build(),
+ createWindowAnimationSpec(mRotateEnterAnimation),
+ this::cancel);
+ }
+
+ private SurfaceAnimator startScreenshotAlphaAnimation() {
+ return startAnimation(initializeBuilder()
+ .setSurfaceControl(mSurfaceControl)
+ .setAnimationLeashParent(mRotationLayer)
+ .setWidth(mWidth)
+ .setHeight(mHeight)
+ .build(),
+ createWindowAnimationSpec(mRotateAlphaAnimation),
+ mNoopCallback);
+ }
+
+ private SurfaceAnimator startEnterBlackFrameAnimation() {
+ return startAnimation(initializeBuilder()
+ .setSurfaceControl(mEnterBlackFrameLayer)
+ .setAnimationLeashParent(mDisplayContent.getOverlayLayer())
+ .build(),
+ createWindowAnimationSpec(mRotateEnterAnimation),
+ mNoopCallback);
+ }
+
+ private SurfaceAnimator startScreenshotRotationAnimation() {
+ return startAnimation(initializeBuilder()
+ .setSurfaceControl(mRotationLayer)
+ .setAnimationLeashParent(mDisplayContent.getOverlayLayer())
+ .build(),
+ createWindowAnimationSpec(mRotateExitAnimation),
+ this::onAnimationEnd);
+ }
+
+ private WindowAnimationSpec createWindowAnimationSpec(Animation mAnimation) {
+ return new WindowAnimationSpec(mAnimation, new Point(0, 0) /* position */,
+ false /* canSkipFirstFrame */, 0 /* WindowCornerRadius */);
+ }
+
+ /**
+ * Start an animation defined by animationSpec on a new {@link SurfaceAnimator}.
+ *
+ * @param animatable The animatable used for the animation.
+ * @param animationSpec The spec of the animation.
+ * @param animationFinishedCallback Callback passed to the {@link SurfaceAnimator} and
+ * called when the animation finishes.
+ * @return The newly created {@link SurfaceAnimator} that as been started.
+ */
+ private SurfaceAnimator startAnimation(
+ SurfaceAnimator.Animatable animatable,
+ LocalAnimationAdapter.AnimationSpec animationSpec,
+ Runnable animationFinishedCallback) {
+ SurfaceAnimator animator = new SurfaceAnimator(
+ animatable, animationFinishedCallback, mService);
+
+ LocalAnimationAdapter localAnimationAdapter = new LocalAnimationAdapter(
+ animationSpec, mService.mSurfaceAnimationRunner);
+
+ animator.startAnimation(mDisplayContent.getPendingTransaction(),
+ localAnimationAdapter, false);
+ return animator;
+ }
+
+ private void onAnimationEnd() {
+ mEnterBlackFrameAnimator = null;
+ mScreenshotRotationAnimator = null;
+ mRotateScreenAnimator = null;
+ mService.mAnimator.mBulkUpdateParams |= WindowSurfacePlacer.SET_UPDATE_ROTATION;
+ kill();
+ mService.updateRotation(false, false);
+ AccessibilityController accessibilityController = mService.mAccessibilityController;
+
+ if (accessibilityController != null) {
+ // We just finished rotation animation which means we did not
+ // announce the rotation and waited for it to end, announce now.
+ accessibilityController.onRotationChangedLocked(mDisplayContent);
}
}
- mExitTransformation.set(mRotateExitTransformation);
- mEnterTransformation.set(mRotateEnterTransformation);
-
- final boolean more = mMoreRotateEnter || mMoreRotateExit
- || !mFinishAnimReady;
-
- mSnapshotFinalMatrix.setConcat(mExitTransformation.getMatrix(), mSnapshotInitialMatrix);
-
- return more;
- }
-
- void updateSurfaces(SurfaceControl.Transaction t) {
- if (!mStarted) {
- return;
- }
-
- if (mSurfaceControl != null) {
- if (!mMoreRotateExit) {
- t.hide(mSurfaceControl);
+ public void cancel() {
+ if (mEnterBlackFrameAnimator != null) {
+ mEnterBlackFrameAnimator.cancelAnimation();
}
- }
- if (mExitingBlackFrame != null) {
- if (!mMoreRotateExit) {
- mExitingBlackFrame.hide(t);
- } else {
- mExitFrameFinalMatrix.setConcat(mExitTransformation.getMatrix(),
- mFrameInitialMatrix);
- mExitingBlackFrame.setMatrix(t, mExitFrameFinalMatrix);
- if (mForceDefaultOrientation) {
- mExitingBlackFrame.setAlpha(t, mExitTransformation.getAlpha());
- }
+ if (mScreenshotRotationAnimator != null) {
+ mScreenshotRotationAnimator.cancelAnimation();
}
- }
- if (mEnteringBlackFrame != null) {
- if (!mMoreRotateEnter) {
- mEnteringBlackFrame.hide(t);
- } else {
- mEnteringBlackFrame.setMatrix(t, mEnterTransformation.getMatrix());
+ if (mRotateScreenAnimator != null) {
+ mRotateScreenAnimator.cancelAnimation();
}
- }
- t.setEarlyWakeup();
- setSnapshotTransform(t, mSnapshotFinalMatrix, mExitTransformation.getAlpha());
- }
-
- public boolean stepAnimationLocked(long now) {
- if (!hasAnimations()) {
- mFinishAnimReady = false;
- return false;
- }
-
- if (!mAnimRunning) {
- if (mRotateEnterAnimation != null) {
- mRotateEnterAnimation.setStartTime(now);
- }
- if (mRotateExitAnimation != null) {
- mRotateExitAnimation.setStartTime(now);
- }
- mAnimRunning = true;
- mHalfwayPoint = now + mRotateEnterAnimation.getDuration() / 2;
- if (mPerf != null && !mIsPerfLockAcquired) {
- mPerf.perfHint(BoostFramework.VENDOR_HINT_ROTATION_ANIM_BOOST, null);
- mIsPerfLockAcquired = true;
+ if (mDisplayAnimator != null) {
+ mDisplayAnimator.cancelAnimation();
}
}
- return stepAnimation(now);
- }
-
- public Transformation getEnterTransformation() {
- return mEnterTransformation;
+ public boolean isAnimating() {
+ return mDisplayAnimator != null && mDisplayAnimator.isAnimating()
+ || mEnterBlackFrameAnimator != null && mEnterBlackFrameAnimator.isAnimating()
+ || mRotateScreenAnimator != null && mRotateScreenAnimator.isAnimating()
+ || mScreenshotRotationAnimator != null
+ && mScreenshotRotationAnimator.isAnimating();
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java
index bcd90a14ac96..ba31818d6331 100644
--- a/services/core/java/com/android/server/wm/SeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/SeamlessRotator.java
@@ -20,9 +20,10 @@ import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import android.graphics.Matrix;
+import android.os.IBinder;
import android.view.DisplayInfo;
-import android.view.Surface;
import android.view.Surface.Rotation;
+import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import com.android.server.wm.utils.CoordinateTransforms;
@@ -35,7 +36,7 @@ import java.io.StringWriter;
*
* Works by transforming the {@link WindowState} back into the old display rotation.
*
- * Uses {@link android.view.SurfaceControl#deferTransactionUntil(Surface, long)} instead of
+ * Uses {@link Transaction#deferTransactionUntil(SurfaceControl, IBinder, long)} instead of
* latching on the buffer size to allow for seamless 180 degree rotations.
*/
public class SeamlessRotator {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 72bb355146d1..06e7d665bb42 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -24,9 +24,9 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
+import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.Nullable;
@@ -56,6 +56,7 @@ import android.view.SurfaceSession;
import android.view.WindowManager;
import com.android.internal.os.logging.MetricsLoggerWrapper;
+import com.android.server.protolog.common.ProtoLog;
import com.android.server.wm.WindowManagerService.H;
import java.io.PrintWriter;
@@ -464,7 +465,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final WindowState windowState = mService.windowForClientLocked(this, window,
false /* throwOnError */);
if (windowState != null) {
- windowState.getDisplayContent().getInsetsStateController().onInsetsModified(
+ windowState.setClientInsetsState(state);
+ windowState.getDisplayContent().getInsetsPolicy().onInsetsModified(
windowState, state);
}
}
@@ -478,8 +480,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
Slog.v(TAG_WM, "First window added to " + this + ", creating SurfaceSession");
}
mSurfaceSession = new SurfaceSession();
- if (SHOW_TRANSACTIONS) Slog.i(
- TAG_WM, " NEW SURFACE SESSION " + mSurfaceSession);
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, " NEW SURFACE SESSION %s", mSurfaceSession);
mService.mSessions.add(this);
if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
mService.dispatchNewAnimatorScaleLocked(this);
@@ -570,7 +571,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
Slog.v(TAG_WM, "Last window removed from " + this
+ ", destroying " + mSurfaceSession);
}
- if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " KILL SURFACE SESSION " + mSurfaceSession);
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, " KILL SURFACE SESSION %s", mSurfaceSession);
try {
mSurfaceSession.kill();
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/wm/SimpleSurfaceAnimatable.java b/services/core/java/com/android/server/wm/SimpleSurfaceAnimatable.java
new file mode 100644
index 000000000000..bf5d5e2653a8
--- /dev/null
+++ b/services/core/java/com/android/server/wm/SimpleSurfaceAnimatable.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.SurfaceControl;
+
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * An implementation of {@link SurfaceAnimator.Animatable} that is instantiated
+ * using a builder pattern for more convenience over reimplementing the whole interface.
+ * <p>
+ * Use {@link SimpleSurfaceAnimatable.Builder} to create a new instance of this class.
+ *
+ * @see com.android.server.wm.SurfaceAnimator.Animatable
+ */
+public class SimpleSurfaceAnimatable implements SurfaceAnimator.Animatable {
+ private final int mWidth;
+ private final int mHeight;
+ private final boolean mShouldDeferAnimationFinish;
+ private final SurfaceControl mAnimationLeashParent;
+ private final SurfaceControl mSurfaceControl;
+ private final SurfaceControl mParentSurfaceControl;
+ private final Runnable mCommitTransactionRunnable;
+ private final Supplier<SurfaceControl.Builder> mAnimationLeashFactory;
+ private final Supplier<SurfaceControl.Transaction> mPendingTransaction;
+ private final BiConsumer<SurfaceControl.Transaction, SurfaceControl> mOnAnimationLeashCreated;
+ private final Consumer<SurfaceControl.Transaction> mOnAnimationLeashLost;
+ private final Consumer<Runnable> mOnAnimationFinished;
+
+ /**
+ * Use {@link SimpleSurfaceAnimatable.Builder} to create a new instance.
+ */
+ private SimpleSurfaceAnimatable(Builder builder) {
+ mWidth = builder.mWidth;
+ mHeight = builder.mHeight;
+ mShouldDeferAnimationFinish = builder.mShouldDeferAnimationFinish;
+ mAnimationLeashParent = builder.mAnimationLeashParent;
+ mSurfaceControl = builder.mSurfaceControl;
+ mParentSurfaceControl = builder.mParentSurfaceControl;
+ mCommitTransactionRunnable = builder.mCommitTransactionRunnable;
+ mAnimationLeashFactory = builder.mAnimationLeashFactory;
+ mOnAnimationLeashCreated = builder.mOnAnimationLeashCreated;
+ mOnAnimationLeashLost = builder.mOnAnimationLeashLost;
+ mPendingTransaction = builder.mPendingTransactionSupplier;
+ mOnAnimationFinished = builder.mOnAnimationFinished;
+ }
+
+ @NonNull
+ @Override
+ public SurfaceControl.Transaction getPendingTransaction() {
+ return mPendingTransaction.get();
+ }
+
+ @Override
+ public void commitPendingTransaction() {
+ mCommitTransactionRunnable.run();
+ }
+
+ @Override
+ public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) {
+ if (mOnAnimationLeashCreated != null) {
+ mOnAnimationLeashCreated.accept(t, leash);
+ }
+
+ }
+
+ @Override
+ public void onAnimationLeashLost(SurfaceControl.Transaction t) {
+ if (mOnAnimationLeashLost != null) {
+ mOnAnimationLeashLost.accept(t);
+ }
+ }
+
+ @Override
+ @NonNull
+ public SurfaceControl.Builder makeAnimationLeash() {
+ return mAnimationLeashFactory.get();
+ }
+
+ @Override
+ public SurfaceControl getAnimationLeashParent() {
+ return mAnimationLeashParent;
+ }
+
+ @Override
+ @Nullable
+ public SurfaceControl getSurfaceControl() {
+ return mSurfaceControl;
+ }
+
+ @Override
+ public SurfaceControl getParentSurfaceControl() {
+ return mParentSurfaceControl;
+ }
+
+ @Override
+ public int getSurfaceWidth() {
+ return mWidth;
+ }
+
+ @Override
+ public int getSurfaceHeight() {
+ return mHeight;
+ }
+
+ @Override
+ public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
+ if (mOnAnimationFinished != null) {
+ mOnAnimationFinished.accept(endDeferFinishCallback);
+ }
+ return mShouldDeferAnimationFinish;
+ }
+
+ /**
+ * Builder class to create a {@link SurfaceAnimator.Animatable} without having to
+ * create a new class that implements the interface.
+ */
+ static class Builder {
+ private int mWidth = -1;
+ private int mHeight = -1;
+ private boolean mShouldDeferAnimationFinish = false;
+
+ @Nullable
+ private SurfaceControl mAnimationLeashParent = null;
+
+ @Nullable
+ private SurfaceControl mSurfaceControl = null;
+
+ @Nullable
+ private SurfaceControl mParentSurfaceControl = null;
+ private Runnable mCommitTransactionRunnable;
+
+ @Nullable
+ private BiConsumer<SurfaceControl.Transaction, SurfaceControl> mOnAnimationLeashCreated =
+ null;
+
+ @Nullable
+ private Consumer<SurfaceControl.Transaction> mOnAnimationLeashLost = null;
+
+ @Nullable
+ private Consumer<Runnable> mOnAnimationFinished = null;
+
+ @NonNull
+ private Supplier<SurfaceControl.Transaction> mPendingTransactionSupplier;
+
+ @NonNull
+ private Supplier<SurfaceControl.Builder> mAnimationLeashFactory;
+
+ /**
+ * Set the runnable to be called when
+ * {@link SurfaceAnimator.Animatable#commitPendingTransaction()}
+ * is called.
+ *
+ * @see SurfaceAnimator.Animatable#commitPendingTransaction()
+ */
+ public SimpleSurfaceAnimatable.Builder setCommitTransactionRunnable(
+ @NonNull Runnable commitTransactionRunnable) {
+ mCommitTransactionRunnable = commitTransactionRunnable;
+ return this;
+ }
+
+ /**
+ * Set the callback called when
+ * {@link SurfaceAnimator.Animatable#onAnimationLeashCreated(SurfaceControl.Transaction,
+ * SurfaceControl)} is called
+ *
+ * @see SurfaceAnimator.Animatable#onAnimationLeashCreated(SurfaceControl.Transaction,
+ * SurfaceControl)
+ */
+ public SimpleSurfaceAnimatable.Builder setOnAnimationLeashCreated(
+ @Nullable BiConsumer<SurfaceControl.Transaction, SurfaceControl>
+ onAnimationLeashCreated) {
+ mOnAnimationLeashCreated = onAnimationLeashCreated;
+ return this;
+ }
+
+ /**
+ * Set the callback called when
+ * {@link SurfaceAnimator.Animatable#onAnimationLeashLost(SurfaceControl.Transaction)}
+ * (SurfaceControl.Transaction, SurfaceControl)} is called
+ *
+ * @see SurfaceAnimator.Animatable#onAnimationLeashLost(SurfaceControl.Transaction)
+ */
+ public SimpleSurfaceAnimatable.Builder setOnAnimationLeashLost(
+ @Nullable Consumer<SurfaceControl.Transaction> onAnimationLeashLost) {
+ mOnAnimationLeashLost = onAnimationLeashLost;
+ return this;
+ }
+
+ /**
+ * @see SurfaceAnimator.Animatable#getPendingTransaction()
+ */
+ public Builder setPendingTransactionSupplier(
+ @NonNull Supplier<SurfaceControl.Transaction> pendingTransactionSupplier) {
+ mPendingTransactionSupplier = pendingTransactionSupplier;
+ return this;
+ }
+
+ /**
+ * Set the {@link Supplier} responsible for creating a new animation leash.
+ *
+ * @see SurfaceAnimator.Animatable#makeAnimationLeash()
+ */
+ public SimpleSurfaceAnimatable.Builder setAnimationLeashSupplier(
+ @NonNull Supplier<SurfaceControl.Builder> animationLeashFactory) {
+ mAnimationLeashFactory = animationLeashFactory;
+ return this;
+ }
+
+ /**
+ * @see SurfaceAnimator.Animatable#getAnimationLeashParent()
+ */
+ public SimpleSurfaceAnimatable.Builder setAnimationLeashParent(
+ SurfaceControl animationLeashParent) {
+ mAnimationLeashParent = animationLeashParent;
+ return this;
+ }
+
+ /**
+ * @see SurfaceAnimator.Animatable#getSurfaceControl()
+ */
+ public SimpleSurfaceAnimatable.Builder setSurfaceControl(
+ @NonNull SurfaceControl surfaceControl) {
+ mSurfaceControl = surfaceControl;
+ return this;
+ }
+
+ /**
+ * @see SurfaceAnimator.Animatable#getParentSurfaceControl()
+ */
+ public SimpleSurfaceAnimatable.Builder setParentSurfaceControl(
+ SurfaceControl parentSurfaceControl) {
+ mParentSurfaceControl = parentSurfaceControl;
+ return this;
+ }
+
+ /**
+ * Default to -1.
+ *
+ * @see SurfaceAnimator.Animatable#getSurfaceWidth()
+ */
+ public SimpleSurfaceAnimatable.Builder setWidth(int width) {
+ mWidth = width;
+ return this;
+ }
+
+ /**
+ * Default to -1.
+ *
+ * @see SurfaceAnimator.Animatable#getSurfaceHeight()
+ */
+ public SimpleSurfaceAnimatable.Builder setHeight(int height) {
+ mHeight = height;
+ return this;
+ }
+
+ /**
+ * Set the value returned by
+ * {@link SurfaceAnimator.Animatable#shouldDeferAnimationFinish(Runnable)}.
+ *
+ * @param onAnimationFinish will be called with the runnable to execute when the animation
+ * needs to be finished.
+ * @see SurfaceAnimator.Animatable#shouldDeferAnimationFinish(Runnable)
+ */
+ public SimpleSurfaceAnimatable.Builder setShouldDeferAnimationFinish(
+ boolean shouldDeferAnimationFinish,
+ @Nullable Consumer<Runnable> onAnimationFinish) {
+ mShouldDeferAnimationFinish = shouldDeferAnimationFinish;
+ mOnAnimationFinished = onAnimationFinish;
+ return this;
+ }
+
+ public SurfaceAnimator.Animatable build() {
+ if (mPendingTransactionSupplier == null) {
+ throw new IllegalArgumentException("mPendingTransactionSupplier cannot be null");
+ }
+ if (mAnimationLeashFactory == null) {
+ throw new IllegalArgumentException("mAnimationLeashFactory cannot be null");
+ }
+ if (mCommitTransactionRunnable == null) {
+ throw new IllegalArgumentException("mCommitTransactionRunnable cannot be null");
+ }
+ if (mSurfaceControl == null) {
+ throw new IllegalArgumentException("mSurfaceControl cannot be null");
+ }
+ return new SimpleSurfaceAnimatable(this);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/SnapshotStartingData.java b/services/core/java/com/android/server/wm/SnapshotStartingData.java
index c9e43c52b911..3764122487b6 100644
--- a/services/core/java/com/android/server/wm/SnapshotStartingData.java
+++ b/services/core/java/com/android/server/wm/SnapshotStartingData.java
@@ -35,7 +35,7 @@ class SnapshotStartingData extends StartingData {
}
@Override
- StartingSurface createStartingSurface(AppWindowToken atoken) {
- return mService.mTaskSnapshotController.createStartingSurface(atoken, mSnapshot);
+ StartingSurface createStartingSurface(ActivityRecord activity) {
+ return mService.mTaskSnapshotController.createStartingSurface(activity, mSnapshot);
}
}
diff --git a/services/core/java/com/android/server/wm/SplashScreenStartingData.java b/services/core/java/com/android/server/wm/SplashScreenStartingData.java
index f52ce3894e19..726b7dac6938 100644
--- a/services/core/java/com/android/server/wm/SplashScreenStartingData.java
+++ b/services/core/java/com/android/server/wm/SplashScreenStartingData.java
@@ -52,9 +52,9 @@ class SplashScreenStartingData extends StartingData {
}
@Override
- StartingSurface createStartingSurface(AppWindowToken atoken) {
- return mService.mPolicy.addSplashScreen(atoken.token, mPkg, mTheme, mCompatInfo,
+ StartingSurface createStartingSurface(ActivityRecord activity) {
+ return mService.mPolicy.addSplashScreen(activity.token, mPkg, mTheme, mCompatInfo,
mNonLocalizedLabel, mLabelRes, mIcon, mLogo, mWindowFlags,
- mMergedOverrideConfiguration, atoken.getDisplayContent().getDisplayId());
+ mMergedOverrideConfiguration, activity.getDisplayContent().getDisplayId());
}
}
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index eb5011f7778a..2e6e777dd0d0 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -33,9 +33,9 @@ public abstract class StartingData {
* Creates the actual starting window surface. DO NOT HOLD THE WINDOW MANAGER LOCK WHEN CALLING
* THIS METHOD.
*
- * @param atoken the app to add the starting window to
+ * @param activity the app to add the starting window to
* @return a class implementing {@link StartingSurface} for easy removal with
* {@link StartingSurface#remove}
*/
- abstract StartingSurface createStartingSurface(AppWindowToken atoken);
-} \ No newline at end of file
+ abstract StartingSurface createStartingSurface(ActivityRecord activity);
+}
diff --git a/services/core/java/com/android/server/wm/StrictModeFlash.java b/services/core/java/com/android/server/wm/StrictModeFlash.java
index 9e5d9cab7669..f537005c955c 100644
--- a/services/core/java/com/android/server/wm/StrictModeFlash.java
+++ b/services/core/java/com/android/server/wm/StrictModeFlash.java
@@ -39,7 +39,8 @@ class StrictModeFlash {
private boolean mDrawNeeded;
private final int mThickness = 20;
- StrictModeFlash(Supplier<Surface> surfaceFactory, DisplayContent dc) {
+ StrictModeFlash(Supplier<Surface> surfaceFactory, DisplayContent dc,
+ SurfaceControl.Transaction t) {
mSurface = surfaceFactory.get();
SurfaceControl ctrl = null;
try {
@@ -48,9 +49,11 @@ class StrictModeFlash {
.setBufferSize(1, 1)
.setFormat(PixelFormat.TRANSLUCENT)
.build();
- ctrl.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 101); // one more than Watermark? arbitrary.
- ctrl.setPosition(0, 0);
- ctrl.show();
+
+ // one more than Watermark? arbitrary.
+ t.setLayer(ctrl, WindowManagerService.TYPE_LAYER_MULTIPLIER * 101);
+ t.setPosition(ctrl, 0, 0);
+ t.show(ctrl);
mSurface.copyFrom(ctrl);
} catch (OutOfResourcesException e) {
}
@@ -103,25 +106,25 @@ class StrictModeFlash {
// Note: caller responsible for being inside
// Surface.openTransaction() / closeTransaction()
- public void setVisibility(boolean on) {
+ public void setVisibility(boolean on, SurfaceControl.Transaction t) {
if (mSurfaceControl == null) {
return;
}
drawIfNeeded();
if (on) {
- mSurfaceControl.show();
+ t.show(mSurfaceControl);
} else {
- mSurfaceControl.hide();
+ t.hide(mSurfaceControl);
}
}
- void positionSurface(int dw, int dh) {
+ void positionSurface(int dw, int dh, SurfaceControl.Transaction t) {
if (mLastDW == dw && mLastDH == dh) {
return;
}
mLastDW = dw;
mLastDH = dh;
- mSurfaceControl.setBufferSize(dw, dh);
+ t.setBufferSize(mSurfaceControl, dw, dh);
mDrawNeeded = true;
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 85ba80602bdb..1db3149fb75f 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -58,12 +58,14 @@ import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
import java.util.function.Consumer;
-class Task extends WindowContainer<AppWindowToken> implements ConfigurationContainerListener{
+class Task extends WindowContainer<ActivityRecord> implements ConfigurationContainerListener{
static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_WM;
// TODO: Track parent marks like this in WindowContainer.
TaskStack mStack;
+ /* Unique identifier for this task. */
final int mTaskId;
+ /* User for which this task was created. */
final int mUserId;
private boolean mDeferRemoval = false;
@@ -97,6 +99,8 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta
private boolean mDragResizing;
private int mDragResizeMode;
+ // This represents the last resolved activity values for this task
+ // NOTE: This value needs to be persisted with each task
private TaskDescription mTaskDescription;
// If set to true, the task will report that it is not in the floating
@@ -114,6 +118,16 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta
// TODO: remove after unification
TaskRecord mTaskRecord;
+ // TODO: Remove after unification.
+ @Override
+ public void onConfigurationChanged(Configuration newParentConfig, boolean forwardToChildren) {
+ // Forward configuration changes in cases
+ // - children won't get it from TaskRecord
+ // - it's a pinned task
+ forwardToChildren &= (mTaskRecord == null) || inPinnedWindowingMode();
+ super.onConfigurationChanged(newParentConfig, forwardToChildren);
+ }
+
Task(int taskId, TaskStack stack, int userId, WindowManagerService service, int resizeMode,
boolean supportsPictureInPicture, TaskDescription taskDescription,
TaskRecord taskRecord) {
@@ -124,16 +138,17 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta
mResizeMode = resizeMode;
mSupportsPictureInPicture = supportsPictureInPicture;
mTaskRecord = taskRecord;
+ mTaskDescription = taskDescription;
+
+ // Tasks have no set orientation value (including SCREEN_ORIENTATION_UNSPECIFIED).
+ setOrientation(SCREEN_ORIENTATION_UNSET);
if (mTaskRecord != null) {
// This can be null when we call createTaskInStack in WindowTestUtils. Remove this after
// unification.
mTaskRecord.registerConfigurationChangeListener(this);
+ } else {
+ setBounds(getResolvedOverrideBounds());
}
- setBounds(getRequestedOverrideBounds());
- mTaskDescription = taskDescription;
-
- // Tasks have no set orientation value (including SCREEN_ORIENTATION_UNSPECIFIED).
- setOrientation(SCREEN_ORIENTATION_UNSET);
}
@Override
@@ -158,14 +173,14 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta
}
@Override
- void addChild(AppWindowToken wtoken, int position) {
+ void addChild(ActivityRecord child, int position) {
position = getAdjustedAddPosition(position);
- super.addChild(wtoken, position);
+ super.addChild(child, position);
mDeferRemoval = false;
}
@Override
- void positionChildAt(int position, AppWindowToken child, boolean includingParents) {
+ void positionChildAt(int position, ActivityRecord child, boolean includingParents) {
position = getAdjustedAddPosition(position);
super.positionChildAt(position, child, includingParents);
mDeferRemoval = false;
@@ -267,16 +282,16 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta
}
@Override
- void removeChild(AppWindowToken token) {
- if (!mChildren.contains(token)) {
+ void removeChild(ActivityRecord child) {
+ if (!mChildren.contains(child)) {
Slog.e(TAG, "removeChild: token=" + this + " not found.");
return;
}
- super.removeChild(token);
+ super.removeChild(child);
if (mChildren.isEmpty()) {
- EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "removeAppToken: last token");
+ EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "removeActivity: last activity");
if (mDeferRemoval) {
removeIfPossible();
}
@@ -398,7 +413,7 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta
/**
* Prepares the task bounds to be frozen with the current size. See
- * {@link AppWindowToken#freezeBounds}.
+ * {@link ActivityRecord#freezeBounds}.
*/
void prepareFreezingBounds() {
mPreparedFrozenBounds.set(getBounds());
@@ -459,7 +474,7 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta
private boolean getMaxVisibleBounds(Rect out) {
boolean foundTop = false;
for (int i = mChildren.size() - 1; i >= 0; i--) {
- final AppWindowToken token = mChildren.get(i);
+ final ActivityRecord token = mChildren.get(i);
// skip hidden (or about to hide) apps
if (token.mIsExiting || token.isClientHidden() || token.hiddenRequested) {
continue;
@@ -611,7 +626,7 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta
@Override
public SurfaceControl getAnimationLeashParent() {
- if (!WindowManagerService.sHierarchicalAnimations) {
+ if (WindowManagerService.sHierarchicalAnimations) {
return super.getAnimationLeashParent();
}
// Currently, only the recents animation will create animation leashes for tasks. In this
@@ -636,24 +651,24 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta
}
WindowState getTopVisibleAppMainWindow() {
- final AppWindowToken token = getTopVisibleAppToken();
- return token != null ? token.findMainWindow() : null;
+ final ActivityRecord activity = getTopVisibleActivity();
+ return activity != null ? activity.findMainWindow() : null;
}
- AppWindowToken getTopFullscreenAppToken() {
+ ActivityRecord getTopFullscreenActivity() {
for (int i = mChildren.size() - 1; i >= 0; i--) {
- final AppWindowToken token = mChildren.get(i);
- final WindowState win = token.findMainWindow();
+ final ActivityRecord activity = mChildren.get(i);
+ final WindowState win = activity.findMainWindow();
if (win != null && win.mAttrs.isFullscreen()) {
- return token;
+ return activity;
}
}
return null;
}
- AppWindowToken getTopVisibleAppToken() {
+ ActivityRecord getTopVisibleActivity() {
for (int i = mChildren.size() - 1; i >= 0; i--) {
- final AppWindowToken token = mChildren.get(i);
+ final ActivityRecord token = mChildren.get(i);
// skip hidden (or about to hide) apps
if (!token.mIsExiting && !token.isClientHidden() && !token.hiddenRequested) {
return token;
@@ -662,18 +677,18 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta
return null;
}
- void positionChildAtTop(AppWindowToken aToken) {
- positionChildAt(aToken, POSITION_TOP);
+ void positionChildAtTop(ActivityRecord child) {
+ positionChildAt(child, POSITION_TOP);
}
- void positionChildAt(AppWindowToken aToken, int position) {
- if (aToken == null) {
+ void positionChildAt(ActivityRecord child, int position) {
+ if (child == null) {
Slog.w(TAG_WM,
"Attempted to position of non-existing app");
return;
}
- positionChildAt(position, aToken, false /* includeParents */);
+ positionChildAt(position, child, false /* includeParents */);
}
void forceWindowsScaleable(boolean force) {
@@ -771,8 +786,8 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta
super.writeToProto(proto, WINDOW_CONTAINER, logLevel);
proto.write(ID, mTaskId);
for (int i = mChildren.size() - 1; i >= 0; i--) {
- final AppWindowToken appWindowToken = mChildren.get(i);
- appWindowToken.writeToProto(proto, APP_WINDOW_TOKENS, logLevel);
+ final ActivityRecord activity = mChildren.get(i);
+ activity.writeToProto(proto, APP_WINDOW_TOKENS, logLevel);
}
proto.write(FILLS_PARENT, matchParentBounds());
getBounds().writeToProto(proto, BOUNDS);
@@ -798,9 +813,9 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta
final String quadruplePrefix = triplePrefix + " ";
for (int i = mChildren.size() - 1; i >= 0; i--) {
- final AppWindowToken wtoken = mChildren.get(i);
- pw.println(triplePrefix + "Activity #" + i + " " + wtoken);
- wtoken.dump(pw, quadruplePrefix, dumpAll);
+ final ActivityRecord activity = mChildren.get(i);
+ pw.println(triplePrefix + "Activity #" + i + " " + activity);
+ activity.dump(pw, quadruplePrefix, dumpAll);
}
}
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 5e8831d47c12..a61c908e0f6f 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -58,6 +58,7 @@ class TaskChangeNotificationController {
private static final int NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG = 23;
private static final int NOTIFY_TASK_LIST_UPDATED_LISTENERS_MSG = 24;
private static final int NOTIFY_SINGLE_TASK_DISPLAY_EMPTY = 25;
+ private static final int NOTIFY_TASK_LIST_FROZEN_UNFROZEN_MSG = 26;
// Delay in notifying task stack change listeners (in millis)
private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -174,6 +175,10 @@ class TaskChangeNotificationController {
l.onRecentTaskListUpdated();
};
+ private final TaskStackConsumer mNotifyTaskListFrozen = (l, m) -> {
+ l.onRecentTaskListFrozenChanged(m.arg1 != 0);
+ };
+
@FunctionalInterface
public interface TaskStackConsumer {
void accept(ITaskStackListener t, Message m) throws RemoteException;
@@ -265,6 +270,9 @@ class TaskChangeNotificationController {
case NOTIFY_TASK_LIST_UPDATED_LISTENERS_MSG:
forAllRemoteListeners(mNotifyTaskListUpdated, msg);
break;
+ case NOTIFY_TASK_LIST_FROZEN_UNFROZEN_MSG:
+ forAllRemoteListeners(mNotifyTaskListFrozen, msg);
+ break;
}
}
}
@@ -342,7 +350,7 @@ class TaskChangeNotificationController {
void notifyActivityPinned(ActivityRecord r) {
mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG);
final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG,
- r.getTaskRecord().taskId, r.getStackId(), r.packageName);
+ r.getTaskRecord().mTaskId, r.getStackId(), r.packageName);
msg.sendingUid = r.mUserId;
forAllLocalListeners(mNotifyActivityPinned, msg);
msg.sendToTarget();
@@ -549,4 +557,12 @@ class TaskChangeNotificationController {
forAllLocalListeners(mNotifyTaskListUpdated, msg);
msg.sendToTarget();
}
+
+ /** @see ITaskStackListener#onRecentTaskListFrozenChanged(boolean) */
+ void notifyTaskListFrozen(boolean frozen) {
+ final Message msg = mHandler.obtainMessage(NOTIFY_TASK_LIST_FROZEN_UNFROZEN_MSG,
+ frozen ? 1 : 0, 0 /* unused */);
+ forAllLocalListeners(mNotifyTaskListFrozen, msg);
+ msg.sendToTarget();
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 1c015d0787dd..9712277495df 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -398,9 +398,11 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
return;
}
- final Rect bounds = display.getBounds();
- final int defaultWidth = bounds.width();
- final int defaultHeight = bounds.height();
+ // Use stable frame instead of raw frame to avoid launching freeform windows on top of
+ // stable insets, which usually are system widgets such as sysbar & navbar.
+ final Rect displayStableBounds = display.mDisplayContent.mDisplayFrames.mStable;
+ final int defaultWidth = displayStableBounds.width();
+ final int defaultHeight = displayStableBounds.height();
int width;
int height;
@@ -451,6 +453,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
}
outBounds.set(0, 0, width, height);
+ outBounds.offset(displayStableBounds.left, displayStableBounds.top);
final int xOffset = (int) (fractionOfHorizontalOffset * (defaultWidth - width));
final int yOffset = (int) (fractionOfVerticalOffset * (defaultHeight - height));
outBounds.offset(xOffset, yOffset);
@@ -627,10 +630,14 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
@NonNull ActivityInfo.WindowLayout layout, int orientation, @NonNull Rect bounds) {
// Default size, which is letterboxing/pillarboxing in display. That's to say the large
// dimension of default size is the small dimension of display size, and the small dimension
- // of default size is calculated to keep the same aspect ratio as the display's.
- Rect displayBounds = display.getBounds();
- final int portraitHeight = Math.min(displayBounds.width(), displayBounds.height());
- final int otherDimension = Math.max(displayBounds.width(), displayBounds.height());
+ // of default size is calculated to keep the same aspect ratio as the display's. Here we use
+ // stable bounds of displays because that indicates the area that isn't occupied by system
+ // widgets (e.g. sysbar and navbar).
+ Rect displayStableBounds = display.mDisplayContent.mDisplayFrames.mStable;
+ final int portraitHeight =
+ Math.min(displayStableBounds.width(), displayStableBounds.height());
+ final int otherDimension =
+ Math.max(displayStableBounds.width(), displayStableBounds.height());
final int portraitWidth = (portraitHeight * portraitHeight) / otherDimension;
final int defaultWidth = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? portraitHeight
: portraitWidth;
@@ -656,16 +663,17 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
final int height = Math.min(defaultHeight, Math.max(phoneHeight, layoutMinHeight));
bounds.set(0, 0, width, height);
+ bounds.offset(displayStableBounds.left, displayStableBounds.top);
}
/**
* Gets centered bounds of width x height. If inOutBounds is not empty, the result bounds
- * centers at its center or display's center if inOutBounds is empty.
+ * centers at its center or display's app bounds center if inOutBounds is empty.
*/
private void centerBounds(@NonNull ActivityDisplay display, int width, int height,
@NonNull Rect inOutBounds) {
if (inOutBounds.isEmpty()) {
- display.getBounds(inOutBounds);
+ inOutBounds.set(display.mDisplayContent.mDisplayFrames.mStable);
}
final int left = inOutBounds.centerX() - width / 2;
final int top = inOutBounds.centerY() - height / 2;
@@ -674,40 +682,40 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
private void adjustBoundsToFitInDisplay(@NonNull ActivityDisplay display,
@NonNull Rect inOutBounds) {
- final Rect displayBounds = display.getBounds();
+ final Rect displayStableBounds = display.mDisplayContent.mDisplayFrames.mStable;
- if (displayBounds.width() < inOutBounds.width()
- || displayBounds.height() < inOutBounds.height()) {
+ if (displayStableBounds.width() < inOutBounds.width()
+ || displayStableBounds.height() < inOutBounds.height()) {
// There is no way for us to fit the bounds in the display without changing width
// or height. Just move the start to align with the display.
final int layoutDirection =
mSupervisor.mRootActivityContainer.getConfiguration().getLayoutDirection();
final int left = layoutDirection == View.LAYOUT_DIRECTION_RTL
- ? displayBounds.width() - inOutBounds.width()
- : 0;
- inOutBounds.offsetTo(left, 0 /* newTop */);
+ ? displayStableBounds.right - inOutBounds.right + inOutBounds.left
+ : displayStableBounds.left;
+ inOutBounds.offsetTo(left, displayStableBounds.top);
return;
}
final int dx;
- if (inOutBounds.right > displayBounds.right) {
+ if (inOutBounds.right > displayStableBounds.right) {
// Right edge is out of display.
- dx = displayBounds.right - inOutBounds.right;
- } else if (inOutBounds.left < displayBounds.left) {
+ dx = displayStableBounds.right - inOutBounds.right;
+ } else if (inOutBounds.left < displayStableBounds.left) {
// Left edge is out of display.
- dx = displayBounds.left - inOutBounds.left;
+ dx = displayStableBounds.left - inOutBounds.left;
} else {
// Vertical edges are all in display.
dx = 0;
}
final int dy;
- if (inOutBounds.top < displayBounds.top) {
+ if (inOutBounds.top < displayStableBounds.top) {
// Top edge is out of display.
- dy = displayBounds.top - inOutBounds.top;
- } else if (inOutBounds.bottom > displayBounds.bottom) {
+ dy = displayStableBounds.top - inOutBounds.top;
+ } else if (inOutBounds.bottom > displayStableBounds.bottom) {
// Bottom edge is out of display.
- dy = displayBounds.bottom - inOutBounds.bottom;
+ dy = displayStableBounds.bottom - inOutBounds.bottom;
} else {
// Horizontal edges are all in display.
dy = 0;
diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java
index 06bdcc04e9c8..f9a75d3d4943 100644
--- a/services/core/java/com/android/server/wm/TaskPersister.java
+++ b/services/core/java/com/android/server/wm/TaskPersister.java
@@ -122,7 +122,7 @@ public class TaskPersister implements PersisterQueue.Listener {
mPersisterQueue.removeItems(
item -> {
File file = new File(item.mFilePath);
- return file.getName().startsWith(Integer.toString(task.taskId));
+ return file.getName().startsWith(Integer.toString(task.mTaskId));
},
ImageWriteQueueItem.class);
}
@@ -262,7 +262,7 @@ public class TaskPersister implements PersisterQueue.Listener {
}
for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = tasks.get(taskNdx);
- if (task.taskId == taskId) {
+ if (task.mTaskId == taskId) {
return task;
}
}
@@ -329,14 +329,14 @@ public class TaskPersister implements PersisterQueue.Listener {
// read the same thing again.
// mWriteQueue.add(new TaskWriteQueueItem(task));
- final int taskId = task.taskId;
+ final int taskId = task.mTaskId;
if (mService.mRootActivityContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) {
// Should not happen.
Slog.wtf(TAG, "Existing task with taskId " + taskId + "found");
- } else if (userId != task.userId) {
+ } else if (userId != task.mUserId) {
// Should not happen.
- Slog.wtf(TAG, "Task with userId " + task.userId + " found in "
+ Slog.wtf(TAG, "Task with userId " + task.mUserId + " found in "
+ userTasksDir.getAbsolutePath());
} else {
// Looks fine.
@@ -560,14 +560,14 @@ public class TaskPersister implements PersisterQueue.Listener {
FileOutputStream file = null;
AtomicFile atomicFile = null;
try {
- File userTasksDir = getUserTasksDir(task.userId);
+ File userTasksDir = getUserTasksDir(task.mUserId);
if (!userTasksDir.isDirectory() && !userTasksDir.mkdirs()) {
- Slog.e(TAG, "Failure creating tasks directory for user " + task.userId
+ Slog.e(TAG, "Failure creating tasks directory for user " + task.mUserId
+ ": " + userTasksDir + " Dropping persistence for task " + task);
return;
}
atomicFile = new AtomicFile(new File(userTasksDir,
- String.valueOf(task.taskId) + TASK_FILENAME_SUFFIX));
+ String.valueOf(task.mTaskId) + TASK_FILENAME_SUFFIX));
file = atomicFile.startWrite();
file.write(stringWriter.toString().getBytes());
file.write('\n');
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 42866f97b4b8..478b1b533f4c 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -21,7 +21,7 @@ import static android.app.ActivityTaskManager.RESIZE_MODE_USER_FORCED;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -50,9 +50,11 @@ import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputWindowHandle;
import android.view.MotionEvent;
+import android.view.SurfaceControl;
import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.protolog.common.ProtoLog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -254,7 +256,7 @@ class TaskPositioner implements IBinder.DeathRecipient {
final InputChannel[] channels = InputChannel.openInputChannelPair(TAG);
mServerChannel = channels[0];
mClientChannel = channels[1];
- mService.mInputManager.registerInputChannel(mServerChannel, null);
+ mService.mInputManager.registerInputChannel(mServerChannel);
mInputEventReceiver = new WindowPositionerEventReceiver(
mClientChannel, mService.mAnimationHandler.getLooper(),
@@ -265,8 +267,7 @@ class TaskPositioner implements IBinder.DeathRecipient {
mDragApplicationHandle.dispatchingTimeoutNanos =
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
- mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
- display.getDisplayId());
+ mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, display.getDisplayId());
mDragWindowHandle.name = TAG;
mDragWindowHandle.token = mServerChannel.getToken();
mDragWindowHandle.layer = mService.getDragLayerLocked();
@@ -296,13 +297,12 @@ class TaskPositioner implements IBinder.DeathRecipient {
mDragWindowHandle.frameBottom = p.y;
// Pause rotations before a drag.
- if (DEBUG_ORIENTATION) {
- Slog.d(TAG, "Pausing rotation during re-position");
- }
+ ProtoLog.d(WM_DEBUG_ORIENTATION, "Pausing rotation during re-position");
mDisplayContent.getDisplayRotation().pause();
// Notify InputMonitor to take mDragWindowHandle.
- mDisplayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
+ mDisplayContent.getInputMonitor().updateInputWindowsImmediately();
+ new SurfaceControl.Transaction().syncInputWindows().apply(true);
mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
@@ -338,9 +338,7 @@ class TaskPositioner implements IBinder.DeathRecipient {
mDisplayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
// Resume rotations after a drag.
- if (DEBUG_ORIENTATION) {
- Slog.d(TAG, "Resuming rotation after re-position");
- }
+ ProtoLog.d(WM_DEBUG_ORIENTATION, "Resuming rotation after re-position");
mDisplayContent.getDisplayRotation().resume();
mDisplayContent = null;
mClientCallback.unlinkToDeath(this, 0 /* flags */);
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index 2441954012e5..ed07f30fc9dc 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -24,7 +24,6 @@ import android.app.IActivityTaskManager;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
-import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Slog;
@@ -51,7 +50,6 @@ class TaskPositioningController {
private @Nullable TaskPositioner mTaskPositioner;
private final Rect mTmpClipRect = new Rect();
- private IBinder mTransferTouchFromToken;
boolean isPositioningLocked() {
return mTaskPositioner != null;
@@ -104,8 +102,6 @@ class TaskPositioningController {
mTmpClipRect.set(0, 0, p.x, p.y);
t.setWindowCrop(mInputSurface, mTmpClipRect);
- t.transferTouchFocus(mTransferTouchFromToken, h.token);
- mTransferTouchFromToken = null;
}
boolean startMovingTask(IWindow window, float startX, float startY) {
@@ -168,6 +164,7 @@ class TaskPositioningController {
mPositioningDisplay = displayContent;
mTaskPositioner = TaskPositioner.create(mService);
+ mTaskPositioner.register(displayContent);
// We need to grab the touch focus so that the touch events during the
// resizing/scrolling are not sent to the app. 'win' is the main window
@@ -175,11 +172,15 @@ class TaskPositioningController {
// on top (eg. a dialog window).
WindowState transferFocusFromWin = win;
if (displayContent.mCurrentFocus != null && displayContent.mCurrentFocus != win
- && displayContent.mCurrentFocus.mAppToken == win.mAppToken) {
+ && displayContent.mCurrentFocus.mActivityRecord == win.mActivityRecord) {
transferFocusFromWin = displayContent.mCurrentFocus;
}
- mTransferTouchFromToken = transferFocusFromWin.mInputChannel.getToken();
- mTaskPositioner.register(displayContent);
+ if (!mInputManager.transferTouchFocus(
+ transferFocusFromWin.mInputChannel, mTaskPositioner.mServerChannel)) {
+ Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
+ cleanUpTaskPositioner();
+ return false;
+ }
mTaskPositioner.startDrag(win, resize, preserveOrientation, startX, startY);
return true;
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 0ea108e30280..299b32cce039 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -48,7 +48,7 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VER
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+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;
@@ -208,7 +208,7 @@ class TaskRecord extends ConfigurationContainer {
*/
private static TaskRecordFactory sTaskRecordFactory;
- final int taskId; // Unique identifier for this task.
+ final int mTaskId; // Unique identifier for this task.
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.
final IVoiceInteractionSession voiceSession; // Voice interaction session driving task
@@ -234,7 +234,7 @@ class TaskRecord extends ConfigurationContainer {
boolean hasBeenVisible; // Set if any activities in the task have been visible to the user.
String stringName; // caching of toString() result.
- int userId; // user for which this task was created
+ int mUserId; // user for which this task was created
boolean mUserSetupComplete; // The user set-up is complete as of the last time the task activity
// was changed.
@@ -262,7 +262,7 @@ class TaskRecord extends ConfigurationContainer {
// This represents the last resolved activity values for this task
// NOTE: This value needs to be persisted with each task
- TaskDescription lastTaskDescription = new TaskDescription();
+ TaskDescription mTaskDescription;
/** List of all activities in the task arranged in history order */
final ArrayList<ActivityRecord> mActivities;
@@ -282,7 +282,7 @@ class TaskRecord extends ConfigurationContainer {
/** Only used for persistable tasks, otherwise 0. The last time this task was moved. Used for
* determining the order when restoring. Sign indicates whether last task movement was to front
* (positive) or back (negative). Absolute value indicates time. */
- long mLastTimeMoved = System.currentTimeMillis();
+ long mLastTimeMoved;
/** If original intent did not allow relinquishing task identity, save that information */
private boolean mNeverRelinquishIdentity = true;
@@ -304,7 +304,7 @@ class TaskRecord extends ConfigurationContainer {
int mCallingUid;
String mCallingPackage;
- final ActivityTaskManagerService mService;
+ final ActivityTaskManagerService mAtmService;
private final Rect mTmpStableBounds = new Rect();
private final Rect mTmpNonDecorBounds = new Rect();
@@ -342,60 +342,25 @@ class TaskRecord extends ConfigurationContainer {
* Don't use constructor directly. Use {@link #create(ActivityTaskManagerService, int,
* ActivityInfo, Intent, TaskDescription)} instead.
*/
- TaskRecord(ActivityTaskManagerService service, int _taskId, ActivityInfo info, Intent _intent,
- IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) {
- mService = service;
- userId = UserHandle.getUserId(info.applicationInfo.uid);
- taskId = _taskId;
- lastActiveTime = SystemClock.elapsedRealtime();
- mAffiliatedTaskId = _taskId;
- voiceSession = _voiceSession;
- voiceInteractor = _voiceInteractor;
- isAvailable = true;
- mActivities = new ArrayList<>();
- mCallingUid = info.applicationInfo.uid;
- mCallingPackage = info.packageName;
- setIntent(_intent, info);
- setMinDimensions(info);
- touchActiveTime();
- mService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
- }
-
- /**
- * Don't use constructor directly.
- * Use {@link #create(ActivityTaskManagerService, int, ActivityInfo,
- * Intent, IVoiceInteractionSession, IVoiceInteractor)} instead.
- */
- TaskRecord(ActivityTaskManagerService service, int _taskId, ActivityInfo info, Intent _intent,
- TaskDescription _taskDescription) {
- mService = service;
- userId = UserHandle.getUserId(info.applicationInfo.uid);
- taskId = _taskId;
- lastActiveTime = SystemClock.elapsedRealtime();
- mAffiliatedTaskId = _taskId;
- voiceSession = null;
- voiceInteractor = null;
- isAvailable = true;
- mActivities = new ArrayList<>();
- mCallingUid = info.applicationInfo.uid;
- mCallingPackage = info.packageName;
- setIntent(_intent, info);
- setMinDimensions(info);
-
- isPersistable = true;
- // Clamp to [1, max].
- maxRecents = Math.min(Math.max(info.maxRecents, 1),
- ActivityTaskManager.getMaxAppRecentsLimitStatic());
-
- lastTaskDescription = _taskDescription;
- touchActiveTime();
- mService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
- }
-
- /**
- * Don't use constructor directly. This is only used by XML parser.
- */
- TaskRecord(ActivityTaskManagerService service, int _taskId, Intent _intent,
+ TaskRecord(ActivityTaskManagerService atmService, int _taskId, ActivityInfo info,
+ Intent _intent, IVoiceInteractionSession _voiceSession,
+ IVoiceInteractor _voiceInteractor, TaskDescription _taskDescription) {
+ this(atmService, _taskId, _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*/, new ArrayList<>(), System.currentTimeMillis(),
+ true /*neverRelinquishIdentity*/,
+ _taskDescription != null ? _taskDescription : new TaskDescription(),
+ _taskId, INVALID_TASK_ID, INVALID_TASK_ID, 0 /*taskAffiliationColor*/,
+ info.applicationInfo.uid, info.packageName, info.resizeMode,
+ info.supportsPictureInPicture(), false /*_realActivitySuspended*/,
+ false /*userSetupComplete*/, INVALID_MIN_SIZE, INVALID_MIN_SIZE, info,
+ _voiceSession, _voiceInteractor);
+ }
+
+ /** Don't use constructor directly. This is only used by XML parser. */
+ TaskRecord(ActivityTaskManagerService atmService, int _taskId, Intent _intent,
Intent _affinityIntent, String _affinity, String _rootAffinity,
ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
boolean _autoRemoveRecents, boolean _askedCompatMode, int _userId,
@@ -404,15 +369,15 @@ class TaskRecord extends ConfigurationContainer {
TaskDescription _lastTaskDescription, int taskAffiliation, int prevTaskId,
int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
int resizeMode, boolean supportsPictureInPicture, boolean _realActivitySuspended,
- boolean userSetupComplete, int minWidth, int minHeight) {
- mService = service;
- taskId = _taskId;
- intent = _intent;
+ boolean userSetupComplete, int minWidth, int minHeight, ActivityInfo info,
+ IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) {
+ mAtmService = atmService;
+ mTaskId = _taskId;
affinityIntent = _affinityIntent;
affinity = _affinity;
rootAffinity = _rootAffinity;
- voiceSession = null;
- voiceInteractor = null;
+ voiceSession = _voiceSession;
+ voiceInteractor = _voiceInteractor;
realActivity = _realActivity;
realActivitySuspended = _realActivitySuspended;
origActivity = _origActivity;
@@ -420,15 +385,15 @@ class TaskRecord extends ConfigurationContainer {
isAvailable = true;
autoRemoveRecents = _autoRemoveRecents;
askedCompatMode = _askedCompatMode;
- userId = _userId;
+ mUserId = _userId;
mUserSetupComplete = userSetupComplete;
effectiveUid = _effectiveUid;
- lastActiveTime = SystemClock.elapsedRealtime();
+ touchActiveTime();
lastDescription = _lastDescription;
mActivities = activities;
mLastTimeMoved = lastTimeMoved;
mNeverRelinquishIdentity = neverRelinquishIdentity;
- lastTaskDescription = _lastTaskDescription;
+ mTaskDescription = _lastTaskDescription;
mAffiliatedTaskId = taskAffiliation;
mAffiliatedTaskColor = taskAffiliationColor;
mPrevAffiliateTaskId = prevTaskId;
@@ -437,9 +402,15 @@ class TaskRecord extends ConfigurationContainer {
mCallingPackage = callingPackage;
mResizeMode = resizeMode;
mSupportsPictureInPicture = supportsPictureInPicture;
- mMinWidth = minWidth;
- mMinHeight = minHeight;
- mService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
+ if (info != null) {
+ setIntent(_intent, info);
+ setMinDimensions(info);
+ } else {
+ intent = _intent;
+ mMinWidth = minWidth;
+ mMinHeight = minHeight;
+ }
+ mAtmService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
}
Task getTask() {
@@ -458,9 +429,9 @@ class TaskRecord extends ConfigurationContainer {
if (stack == null) {
throw new IllegalArgumentException("TaskRecord: invalid stack=" + mStack);
}
- EventLog.writeEvent(WM_TASK_CREATED, taskId, stack.mStackId);
- mTask = new Task(taskId, stack, userId, mService.mWindowManager, mResizeMode,
- mSupportsPictureInPicture, lastTaskDescription, this);
+ EventLog.writeEvent(WM_TASK_CREATED, mTaskId, stack.mStackId);
+ mTask = new Task(mTaskId, stack, mUserId, mAtmService.mWindowManager, mResizeMode,
+ mSupportsPictureInPicture, mTaskDescription, this);
final int position = onTop ? POSITION_TOP : POSITION_BOTTOM;
if (!mDisplayedBounds.isEmpty()) {
@@ -476,7 +447,7 @@ class TaskRecord extends ConfigurationContainer {
}
void cleanUpResourcesForDestroy() {
- if (!mActivities.isEmpty()) {
+ if (hasChild()) {
return;
}
@@ -487,14 +458,14 @@ class TaskRecord extends ConfigurationContainer {
final boolean isVoiceSession = voiceSession != null;
if (isVoiceSession) {
try {
- voiceSession.taskFinished(intent, taskId);
+ voiceSession.taskFinished(intent, mTaskId);
} catch (RemoteException e) {
}
}
if (autoRemoveFromRecents() || isVoiceSession) {
// Task creator asked to remove this when done, or this task was a voice
// interaction, so it should not remain on the recent tasks list.
- mService.mStackSupervisor.mRecentTasks.remove(this);
+ mAtmService.mStackSupervisor.mRecentTasks.remove(this);
}
removeWindowContainer();
@@ -502,9 +473,9 @@ class TaskRecord extends ConfigurationContainer {
@VisibleForTesting
void removeWindowContainer() {
- mService.getLockTaskController().clearLockedTask(this);
+ mAtmService.getLockTaskController().clearLockedTask(this);
if (mTask == null) {
- if (DEBUG_STACK) Slog.i(TAG_WM, "removeTask: could not find taskId=" + taskId);
+ if (DEBUG_STACK) Slog.i(TAG_WM, "removeTask: could not find taskId=" + mTaskId);
return;
}
mTask.removeIfPossible();
@@ -514,11 +485,11 @@ class TaskRecord extends ConfigurationContainer {
// default configuration the next time it launches.
setBounds(null);
}
- mService.getTaskChangeNotificationController().notifyTaskRemoved(taskId);
+ mAtmService.getTaskChangeNotificationController().notifyTaskRemoved(mTaskId);
}
- public void onSnapshotChanged(TaskSnapshot snapshot) {
- mService.getTaskChangeNotificationController().notifyTaskSnapshotChanged(taskId, snapshot);
+ void onSnapshotChanged(TaskSnapshot snapshot) {
+ mAtmService.getTaskChangeNotificationController().notifyTaskSnapshotChanged(mTaskId, snapshot);
}
void setResizeMode(int resizeMode) {
@@ -527,13 +498,13 @@ class TaskRecord extends ConfigurationContainer {
}
mResizeMode = resizeMode;
mTask.setResizeable(resizeMode);
- mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
- mService.mRootActivityContainer.resumeFocusedStacksTopActivities();
+ mAtmService.mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+ mAtmService.mRootActivityContainer.resumeFocusedStacksTopActivities();
}
void setTaskDockedResizing(boolean resizing) {
if (mTask == null) {
- Slog.w(TAG_WM, "setTaskDockedResizing: taskId " + taskId + " not found.");
+ Slog.w(TAG_WM, "setTaskDockedResizing: taskId " + mTaskId + " not found.");
return;
}
mTask.setTaskDockedResizing(resizing);
@@ -541,11 +512,11 @@ class TaskRecord extends ConfigurationContainer {
// TODO: Consolidate this with the resize() method below.
public void requestResize(Rect bounds, int resizeMode) {
- mService.resizeTask(taskId, bounds, resizeMode);
+ mAtmService.resizeTask(mTaskId, bounds, resizeMode);
}
boolean resize(Rect bounds, int resizeMode, boolean preserveWindow, boolean deferResume) {
- mService.deferWindowLayout();
+ mAtmService.deferWindowLayout();
try {
if (!isResizeable()) {
@@ -568,7 +539,7 @@ class TaskRecord extends ConfigurationContainer {
setBounds(bounds);
if (!inFreeformWindowingMode()) {
// re-restore the task so it can have the proper stack association.
- mService.mStackSupervisor.restoreRecentTaskLocked(this, null, !ON_TOP);
+ mAtmService.mStackSupervisor.restoreRecentTaskLocked(this, null, !ON_TOP);
}
return true;
}
@@ -582,7 +553,7 @@ class TaskRecord extends ConfigurationContainer {
// This method assumes that the task is already placed in the right stack.
// we do not mess with that decision and we only do the resize!
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeTask_" + taskId);
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizeTask_" + mTaskId);
boolean updatedConfig = false;
mTmpConfig.setTo(getResolvedOverrideConfiguration());
@@ -606,9 +577,9 @@ class TaskRecord extends ConfigurationContainer {
// this won't cause tons of irrelevant windows being preserved because only
// activities in this task may experience a bounds change. Configs for other
// activities stay the same.
- mService.mRootActivityContainer.ensureActivitiesVisible(r, 0, preserveWindow);
+ mAtmService.mRootActivityContainer.ensureActivitiesVisible(r, 0, preserveWindow);
if (!kept) {
- mService.mRootActivityContainer.resumeFocusedStacksTopActivities();
+ mAtmService.mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
}
@@ -616,10 +587,10 @@ class TaskRecord extends ConfigurationContainer {
saveLaunchingStateIfNeeded();
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
return kept;
} finally {
- mService.continueWindowLayout();
+ mAtmService.continueWindowLayout();
}
}
@@ -687,9 +658,9 @@ class TaskRecord extends ConfigurationContainer {
boolean reparent(ActivityStack preferredStack, int position,
@ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
boolean schedulePictureInPictureModeChange, String reason) {
- final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
- final RootActivityContainer root = mService.mRootActivityContainer;
- final WindowManagerService windowManager = mService.mWindowManager;
+ final ActivityStackSupervisor supervisor = mAtmService.mStackSupervisor;
+ final RootActivityContainer root = mAtmService.mRootActivityContainer;
+ final WindowManagerService windowManager = mAtmService.mWindowManager;
final ActivityStack sourceStack = getStack();
final ActivityStack toStack = supervisor.getReparentTargetStack(this, preferredStack,
position == MAX_VALUE);
@@ -725,7 +696,7 @@ class TaskRecord extends ConfigurationContainer {
windowManager.setWillReplaceWindow(topActivity.appToken, animate);
}
- mService.deferWindowLayout();
+ mAtmService.deferWindowLayout();
boolean kept = true;
try {
final ActivityRecord r = topRunningActivityLocked();
@@ -764,7 +735,7 @@ class TaskRecord extends ConfigurationContainer {
// Notify the voice session if required
if (voiceSession != null) {
try {
- voiceSession.taskStarted(intent, taskId);
+ voiceSession.taskStarted(intent, mTaskId);
} catch (RemoteException e) {
}
}
@@ -776,7 +747,7 @@ class TaskRecord extends ConfigurationContainer {
wasPaused, reason);
}
if (!animate) {
- mService.mStackSupervisor.mNoAnimActivities.add(topActivity);
+ mAtmService.mStackSupervisor.mNoAnimActivities.add(topActivity);
}
// We might trigger a configuration change. Save the current task bounds for freezing.
@@ -795,7 +766,7 @@ class TaskRecord extends ConfigurationContainer {
} else if (toStackWindowingMode == WINDOWING_MODE_FREEFORM) {
Rect bounds = getLaunchBounds();
if (bounds == null) {
- mService.mStackSupervisor.getLaunchParamsController().layoutTask(this, null);
+ mAtmService.mStackSupervisor.getLaunchParamsController().layoutTask(this, null);
bounds = configBounds;
}
kept = resize(bounds, RESIZE_MODE_FORCED, !mightReplaceWindow, deferResume);
@@ -803,13 +774,13 @@ class TaskRecord extends ConfigurationContainer {
if (toStackSplitScreenPrimary && moveStackMode == REPARENT_KEEP_STACK_AT_FRONT) {
// Move recents to front so it is not behind home stack when going into docked
// mode
- mService.mStackSupervisor.moveRecentsStackToFront(reason);
+ mAtmService.mStackSupervisor.moveRecentsStackToFront(reason);
}
kept = resize(toStack.getRequestedOverrideBounds(), RESIZE_MODE_SYSTEM,
!mightReplaceWindow, deferResume);
}
} finally {
- mService.continueWindowLayout();
+ mAtmService.continueWindowLayout();
}
if (mightReplaceWindow) {
@@ -846,7 +817,7 @@ class TaskRecord extends ConfigurationContainer {
void cancelWindowTransition() {
if (mTask == null) {
- Slog.w(TAG_WM, "cancelWindowTransition: taskId " + taskId + " not found.");
+ Slog.w(TAG_WM, "cancelWindowTransition: taskId " + mTaskId + " not found.");
return;
}
mTask.cancelTaskWindowTransition();
@@ -859,7 +830,7 @@ class TaskRecord extends ConfigurationContainer {
// TODO: Move this to {@link TaskWindowContainerController} once recent tasks are more
// synchronized between AM and WM.
- return mService.mWindowManager.getTaskSnapshot(taskId, userId, reducedResolution,
+ return mAtmService.mWindowManager.getTaskSnapshot(mTaskId, mUserId, reducedResolution,
restoreFromDisk);
}
@@ -938,9 +909,9 @@ class TaskRecord extends ConfigurationContainer {
// task as having a true root activity.
rootWasReset = true;
}
- userId = UserHandle.getUserId(info.applicationInfo.uid);
- mUserSetupComplete = Settings.Secure.getIntForUser(mService.mContext.getContentResolver(),
- USER_SETUP_COMPLETE, 0, userId) != 0;
+ mUserId = UserHandle.getUserId(info.applicationInfo.uid);
+ mUserSetupComplete = Settings.Secure.getIntForUser(
+ mAtmService.mContext.getContentResolver(), USER_SETUP_COMPLETE, 0, mUserId) != 0;
if ((info.flags & ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS) != 0) {
// If the activity itself has requested auto-remove, then just always do it.
autoRemoveRecents = true;
@@ -993,12 +964,12 @@ class TaskRecord extends ConfigurationContainer {
void setPrevAffiliate(TaskRecord prevAffiliate) {
mPrevAffiliate = prevAffiliate;
- mPrevAffiliateTaskId = prevAffiliate == null ? INVALID_TASK_ID : prevAffiliate.taskId;
+ mPrevAffiliateTaskId = prevAffiliate == null ? INVALID_TASK_ID : prevAffiliate.mTaskId;
}
void setNextAffiliate(TaskRecord nextAffiliate) {
mNextAffiliate = nextAffiliate;
- mNextAffiliateTaskId = nextAffiliate == null ? INVALID_TASK_ID : nextAffiliate.taskId;
+ mNextAffiliateTaskId = nextAffiliate == null ? INVALID_TASK_ID : nextAffiliate.mTaskId;
}
<T extends ActivityStack> T getStack() {
@@ -1061,7 +1032,7 @@ class TaskRecord extends ConfigurationContainer {
@Override
protected void onParentChanged() {
super.onParentChanged();
- mService.mRootActivityContainer.updateUIDsPresentOnDisplay();
+ mAtmService.mRootActivityContainer.updateUIDsPresentOnDisplay();
}
// Close up recents linked list.
@@ -1080,13 +1051,13 @@ class TaskRecord extends ConfigurationContainer {
closeRecentsChain();
if (inRecents) {
inRecents = false;
- mService.notifyTaskPersisterLocked(this, false);
+ mAtmService.notifyTaskPersisterLocked(this, false);
}
clearRootProcess();
- mService.mWindowManager.mTaskSnapshotController.notifyTaskRemovedFromRecents(
- taskId, userId);
+ mAtmService.mWindowManager.mTaskSnapshotController.notifyTaskRemovedFromRecents(
+ mTaskId, mUserId);
}
void setTaskToAffiliateWith(TaskRecord taskToAffiliateWith) {
@@ -1124,7 +1095,7 @@ class TaskRecord extends ConfigurationContainer {
// There are no non-finishing activities in the task.
return null;
}
- return mActivities.get(rootActivityIndex);
+ return getChildAt(rootActivityIndex);
}
ActivityRecord getTopActivity() {
@@ -1132,8 +1103,8 @@ class TaskRecord extends ConfigurationContainer {
}
ActivityRecord getTopActivity(boolean includeOverlays) {
- for (int i = mActivities.size() - 1; i >= 0; --i) {
- final ActivityRecord r = mActivities.get(i);
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final ActivityRecord r = getChildAt(i);
if (r.finishing || (!includeOverlays && r.mTaskOverlay)) {
continue;
}
@@ -1144,8 +1115,8 @@ class TaskRecord extends ConfigurationContainer {
ActivityRecord topRunningActivityLocked() {
if (mStack != null) {
- for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = mActivities.get(activityNdx);
+ for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ ActivityRecord r = getChildAt(activityNdx);
if (!r.finishing && r.okToShowLocked()) {
return r;
}
@@ -1155,8 +1126,8 @@ class TaskRecord extends ConfigurationContainer {
}
boolean isVisible() {
- for (int i = mActivities.size() - 1; i >= 0; --i) {
- final ActivityRecord r = mActivities.get(i);
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final ActivityRecord r = getChildAt(i);
if (r.visible) {
return true;
}
@@ -1168,8 +1139,8 @@ class TaskRecord extends ConfigurationContainer {
* Return true if any activities in this task belongs to input uid.
*/
boolean containsAppUid(int uid) {
- for (int i = mActivities.size() - 1; i >= 0; --i) {
- final ActivityRecord r = mActivities.get(i);
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final ActivityRecord r = getChildAt(i);
if (r.getUid() == uid) {
return true;
}
@@ -1179,8 +1150,8 @@ class TaskRecord extends ConfigurationContainer {
void getAllRunningVisibleActivitiesLocked(ArrayList<ActivityRecord> outActivities) {
if (mStack != null) {
- for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = mActivities.get(activityNdx);
+ for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ ActivityRecord r = getChildAt(activityNdx);
if (!r.finishing && r.okToShowLocked() && r.visibleIgnoringKeyguard) {
outActivities.add(r);
}
@@ -1190,8 +1161,8 @@ class TaskRecord extends ConfigurationContainer {
ActivityRecord topRunningActivityWithStartingWindowLocked() {
if (mStack != null) {
- for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = mActivities.get(activityNdx);
+ for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ ActivityRecord r = getChildAt(activityNdx);
if (r.mStartingWindowState != STARTING_WINDOW_SHOWN
|| r.finishing || !r.okToShowLocked()) {
continue;
@@ -1208,8 +1179,8 @@ class TaskRecord extends ConfigurationContainer {
*/
void getNumRunningActivities(TaskActivitiesReport reportOut) {
reportOut.reset();
- for (int i = mActivities.size() - 1; i >= 0; --i) {
- final ActivityRecord r = mActivities.get(i);
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final ActivityRecord r = getChildAt(i);
if (r.finishing) {
continue;
}
@@ -1235,7 +1206,7 @@ class TaskRecord extends ConfigurationContainer {
boolean okToShowLocked() {
// NOTE: If {@link TaskRecord#topRunningActivity} return is not null then it is
// okay to show the activity when locked.
- return mService.mStackSupervisor.isCurrentProfileLocked(userId)
+ return mAtmService.mStackSupervisor.isCurrentProfileLocked(mUserId)
|| topRunningActivityLocked() != null;
}
@@ -1251,22 +1222,22 @@ class TaskRecord extends ConfigurationContainer {
mActivities.add(newTop);
// Make sure window manager is aware of the position change.
- mTask.positionChildAtTop(newTop.mAppWindowToken);
+ mTask.positionChildAtTop(newTop);
updateEffectiveIntent();
}
void addActivityToTop(ActivityRecord r) {
- addActivityAtIndex(mActivities.size(), r);
+ addActivityAtIndex(getChildCount(), r);
}
@Override
/*@WindowConfiguration.ActivityType*/
public int getActivityType() {
final int applicationType = super.getActivityType();
- if (applicationType != ACTIVITY_TYPE_UNDEFINED || mActivities.isEmpty()) {
+ if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) {
return applicationType;
}
- return mActivities.get(0).getActivityType();
+ return getChildAt(0).getActivityType();
}
/**
@@ -1283,12 +1254,12 @@ class TaskRecord extends ConfigurationContainer {
r.setTask(this);
// Remove r first, and if it wasn't already in the list and it's fullscreen, count it.
- if (!mActivities.remove(r) && r.fullscreen) {
+ if (!mActivities.remove(r) && r.occludesParent()) {
// Was not previously in list.
numFullscreen++;
}
// Only set this based on the first activity
- if (mActivities.isEmpty()) {
+ if (!hasChild()) {
if (r.getActivityType() == ACTIVITY_TYPE_UNDEFINED) {
// Normally non-standard activity type for the activity record will be set when the
// object is created, however we delay setting the standard application type until
@@ -1308,10 +1279,10 @@ class TaskRecord extends ConfigurationContainer {
r.setActivityType(getActivityType());
}
- final int size = mActivities.size();
+ final int size = getChildCount();
if (index == size && size > 0) {
- final ActivityRecord top = mActivities.get(size - 1);
+ final ActivityRecord top = getChildAt(size - 1);
if (top.mTaskOverlay) {
// Place below the task overlay activity since the overlay activity should always
// be on top.
@@ -1324,18 +1295,18 @@ class TaskRecord extends ConfigurationContainer {
updateEffectiveIntent();
if (r.isPersistable()) {
- mService.notifyTaskPersisterLocked(this, false);
+ mAtmService.notifyTaskPersisterLocked(this, false);
}
- if (r.mAppWindowToken != null) {
+ if (r.getParent() != null) {
// Only attempt to move in WM if the child has a controller. It is possible we haven't
// created controller for the activity we are starting yet.
- mTask.positionChildAt(r.mAppWindowToken, index);
+ mTask.positionChildAt(r, index);
}
// Make sure the list of display UID whitelists is updated
// now that this record is in a new task.
- mService.mRootActivityContainer.updateUIDsPresentOnDisplay();
+ mAtmService.mRootActivityContainer.updateUIDsPresentOnDisplay();
}
/**
@@ -1355,22 +1326,22 @@ class TaskRecord extends ConfigurationContainer {
r.setTask(null /* task */, reparenting /* reparenting */);
- if (mActivities.remove(r) && r.fullscreen) {
+ if (mActivities.remove(r) && r.occludesParent()) {
// Was previously in list.
numFullscreen--;
}
if (r.isPersistable()) {
- mService.notifyTaskPersisterLocked(this, false);
+ mAtmService.notifyTaskPersisterLocked(this, false);
}
if (inPinnedWindowingMode()) {
// We normally notify listeners of task stack changes on pause, however pinned stack
// activities are normally in the paused state so no notification will be sent there
// before the activity is removed. We send it here so instead.
- mService.getTaskChangeNotificationController().notifyTaskStackChanged();
+ mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
}
- if (mActivities.isEmpty()) {
+ if (!hasChild()) {
return !mReuseTask;
}
updateEffectiveIntent();
@@ -1384,8 +1355,8 @@ class TaskRecord extends ConfigurationContainer {
*/
boolean onlyHasTaskOverlayActivities(boolean excludeFinishing) {
int count = 0;
- for (int i = mActivities.size() - 1; i >= 0; i--) {
- final ActivityRecord r = mActivities.get(i);
+ for (int i = getChildCount() - 1; i >= 0; i--) {
+ final ActivityRecord r = getChildAt(i);
if (excludeFinishing && r.finishing) {
continue;
}
@@ -1401,7 +1372,7 @@ class TaskRecord extends ConfigurationContainer {
// We will automatically remove the task either if it has explicitly asked for
// this, or it is empty and has never contained an activity that got shown to
// the user.
- return autoRemoveRecents || (mActivities.isEmpty() && !hasBeenVisible);
+ return autoRemoveRecents || (!hasChild() && !hasBeenVisible);
}
/**
@@ -1409,9 +1380,9 @@ class TaskRecord extends ConfigurationContainer {
* task starting at a specified index.
*/
final void performClearTaskAtIndexLocked(int activityNdx, String reason) {
- int numActivities = mActivities.size();
+ int numActivities = getChildCount();
for ( ; activityNdx < numActivities; ++activityNdx) {
- final ActivityRecord r = mActivities.get(activityNdx);
+ final ActivityRecord r = getChildAt(activityNdx);
if (r.finishing) {
continue;
}
@@ -1458,9 +1429,9 @@ class TaskRecord extends ConfigurationContainer {
* or null if none was found.
*/
final ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) {
- int numActivities = mActivities.size();
+ int numActivities = getChildCount();
for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = mActivities.get(activityNdx);
+ ActivityRecord r = getChildAt(activityNdx);
if (r.finishing) {
continue;
}
@@ -1469,7 +1440,7 @@ class TaskRecord extends ConfigurationContainer {
final ActivityRecord ret = r;
for (++activityNdx; activityNdx < numActivities; ++activityNdx) {
- r = mActivities.get(activityNdx);
+ r = getChildAt(activityNdx);
if (r.finishing) {
continue;
}
@@ -1530,10 +1501,10 @@ class TaskRecord extends ConfigurationContainer {
}
final String pkg = (realActivity != null) ? realActivity.getPackageName() : null;
- final LockTaskController lockTaskController = mService.getLockTaskController();
+ final LockTaskController lockTaskController = mAtmService.getLockTaskController();
switch (r.lockTaskLaunchMode) {
case LOCK_TASK_LAUNCH_MODE_DEFAULT:
- mLockTaskAuth = lockTaskController.isPackageWhitelisted(userId, pkg)
+ mLockTaskAuth = lockTaskController.isPackageWhitelisted(mUserId, pkg)
? LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE;
break;
@@ -1546,7 +1517,7 @@ class TaskRecord extends ConfigurationContainer {
break;
case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED:
- mLockTaskAuth = lockTaskController.isPackageWhitelisted(userId, pkg)
+ mLockTaskAuth = lockTaskController.isPackageWhitelisted(mUserId, pkg)
? LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE;
break;
}
@@ -1555,7 +1526,7 @@ class TaskRecord extends ConfigurationContainer {
}
private boolean isResizeable(boolean checkSupportsPip) {
- return (mService.mForceResizableActivities || ActivityInfo.isResizeableMode(mResizeMode)
+ return (mAtmService.mForceResizableActivities || ActivityInfo.isResizeableMode(mResizeMode)
|| (checkSupportsPip && mSupportsPictureInPicture));
}
@@ -1568,8 +1539,8 @@ class TaskRecord extends ConfigurationContainer {
// A task can not be docked even if it is considered resizeable because it only supports
// picture-in-picture mode but has a non-resizeable resizeMode
return super.supportsSplitScreenWindowingMode()
- && mService.mSupportsSplitScreenMultiWindow
- && (mService.mForceResizableActivities
+ && mAtmService.mSupportsSplitScreenMultiWindow
+ && (mAtmService.mForceResizableActivities
|| (isResizeable(false /* checkSupportsPip */)
&& !ActivityInfo.isPreserveOrientationMode(mResizeMode)));
}
@@ -1582,7 +1553,7 @@ class TaskRecord extends ConfigurationContainer {
* secondary display.
*/
boolean canBeLaunchedOnDisplay(int displayId) {
- return mService.mStackSupervisor.canPlaceEntityOnDisplay(displayId,
+ return mAtmService.mStackSupervisor.canPlaceEntityOnDisplay(displayId,
-1 /* don't check PID */, -1 /* don't check UID */, null /* activityInfo */);
}
@@ -1620,8 +1591,8 @@ class TaskRecord extends ConfigurationContainer {
*/
final ActivityRecord findActivityInHistoryLocked(ActivityRecord r) {
final ComponentName realActivity = r.mActivityComponent;
- for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord candidate = mActivities.get(activityNdx);
+ for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ ActivityRecord candidate = getChildAt(activityNdx);
if (candidate.finishing) {
continue;
}
@@ -1638,12 +1609,12 @@ class TaskRecord extends ConfigurationContainer {
// Traverse upwards looking for any break between main task activities and
// utility activities.
int activityNdx;
- final int numActivities = mActivities.size();
+ final int numActivities = getChildCount();
final boolean relinquish = numActivities != 0 &&
- (mActivities.get(0).info.flags & FLAG_RELINQUISH_TASK_IDENTITY) != 0;
+ (getChildAt(0).info.flags & FLAG_RELINQUISH_TASK_IDENTITY) != 0;
for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities;
++activityNdx) {
- final ActivityRecord r = mActivities.get(activityNdx);
+ final ActivityRecord r = getChildAt(activityNdx);
if (relinquish && (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0) {
// This will be the top activity for determining taskDescription. Pre-inc to
// overcome initial decrement below.
@@ -1671,7 +1642,7 @@ class TaskRecord extends ConfigurationContainer {
boolean navigationBarContrastWhenTransparent = false;
boolean topActivity = true;
for (--activityNdx; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = mActivities.get(activityNdx);
+ final ActivityRecord r = getChildAt(activityNdx);
if (r.mTaskOverlay) {
continue;
}
@@ -1700,15 +1671,15 @@ class TaskRecord extends ConfigurationContainer {
}
topActivity = false;
}
- lastTaskDescription = new TaskDescription(label, null, iconResource, iconFilename,
+ mTaskDescription = new TaskDescription(label, null, iconResource, iconFilename,
colorPrimary, colorBackground, statusBarColor, navigationBarColor,
statusBarContrastWhenTransparent, navigationBarContrastWhenTransparent);
if (mTask != null) {
- mTask.setTaskDescription(lastTaskDescription);
+ mTask.setTaskDescription(mTaskDescription);
}
// Update the task affiliation color if we are the parent of the group
- if (taskId == mAffiliatedTaskId) {
- mAffiliatedTaskColor = lastTaskDescription.getPrimaryColor();
+ if (mTaskId == mAffiliatedTaskId) {
+ mAffiliatedTaskColor = mTaskDescription.getPrimaryColor();
}
}
}
@@ -1726,9 +1697,9 @@ class TaskRecord extends ConfigurationContainer {
*/
int findRootIndex(boolean effectiveRoot) {
int effectiveNdx = -1;
- final int topActivityNdx = mActivities.size() - 1;
+ final int topActivityNdx = getChildCount() - 1;
for (int activityNdx = 0; activityNdx <= topActivityNdx; ++activityNdx) {
- final ActivityRecord r = mActivities.get(activityNdx);
+ final ActivityRecord r = getChildAt(activityNdx);
if (r.finishing) {
continue;
}
@@ -1749,7 +1720,7 @@ class TaskRecord extends ConfigurationContainer {
// But we still want to update the intent, so let's use the bottom activity.
effectiveRootIndex = 0;
}
- final ActivityRecord r = mActivities.get(effectiveRootIndex);
+ final ActivityRecord r = getChildAt(effectiveRootIndex);
setIntent(r);
// Update the task description when the activities change
@@ -1767,9 +1738,9 @@ class TaskRecord extends ConfigurationContainer {
// to do this for the pinned stack as the bounds are controlled by the system.
if (!inPinnedWindowingMode() && mStack != null) {
final int defaultMinSizeDp =
- mService.mRootActivityContainer.mDefaultMinSizeOfResizeableTaskDp;
+ mAtmService.mRootActivityContainer.mDefaultMinSizeOfResizeableTaskDp;
final ActivityDisplay display =
- mService.mRootActivityContainer.getActivityDisplay(mStack.mDisplayId);
+ mAtmService.mRootActivityContainer.getActivityDisplay(mStack.mDisplayId);
final float density =
(float) display.getConfiguration().densityDpi / DisplayMetrics.DENSITY_DEFAULT;
final int defaultMinSize = (int) (defaultMinSizeDp * density);
@@ -1849,7 +1820,7 @@ class TaskRecord extends ConfigurationContainer {
final boolean wasInMultiWindowMode = inMultiWindowMode();
super.onConfigurationChanged(newParentConfig);
if (wasInMultiWindowMode != inMultiWindowMode()) {
- mService.mStackSupervisor.scheduleUpdateMultiWindowMode(this);
+ mAtmService.mStackSupervisor.scheduleUpdateMultiWindowMode(this);
}
// If the configuration supports persistent bounds (eg. Freeform), keep track of the
@@ -1890,7 +1861,7 @@ class TaskRecord extends ConfigurationContainer {
}
// Saves the new state so that we can launch the activity at the same location.
- mService.mStackSupervisor.mLaunchParamsPersister.saveTask(this);
+ mAtmService.mStackSupervisor.mLaunchParamsPersister.saveTask(this);
}
/**
@@ -2293,7 +2264,7 @@ class TaskRecord extends ConfigurationContainer {
if (mLastNonFullscreenBounds != null) {
setBounds(mLastNonFullscreenBounds);
} else {
- mService.mStackSupervisor.getLaunchParamsController().layoutTask(this, null);
+ mAtmService.mStackSupervisor.getLaunchParamsController().layoutTask(this, null);
}
} else {
setBounds(inStack.getRequestedOverrideBounds());
@@ -2318,8 +2289,8 @@ class TaskRecord extends ConfigurationContainer {
}
void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
- for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = mActivities.get(activityNdx);
+ for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = getChildAt(activityNdx);
if (r.visible) {
r.showStartingWindow(null /* prev */, false /* newTask */, taskSwitch);
}
@@ -2354,9 +2325,9 @@ class TaskRecord extends ConfigurationContainer {
*/
void fillTaskInfo(TaskInfo info) {
getNumRunningActivities(mReuseActivitiesReport);
- info.userId = userId;
+ info.userId = mUserId;
info.stackId = getStackId();
- info.taskId = taskId;
+ info.taskId = mTaskId;
info.displayId = mStack == null ? Display.INVALID_DISPLAY : mStack.mDisplayId;
info.isRunning = getTopActivity() != null;
info.baseIntent = new Intent(getBaseIntent());
@@ -2370,7 +2341,7 @@ class TaskRecord extends ConfigurationContainer {
info.realActivity = realActivity;
info.numActivities = mReuseActivitiesReport.numActivities;
info.lastActiveTime = lastActiveTime;
- info.taskDescription = new ActivityManager.TaskDescription(lastTaskDescription);
+ info.taskDescription = new ActivityManager.TaskDescription(mTaskDescription);
info.supportsSplitScreenMultiWindow = supportsSplitScreenWindowingMode();
info.resizeMode = mResizeMode;
info.configuration.setTo(getConfiguration());
@@ -2386,7 +2357,7 @@ class TaskRecord extends ConfigurationContainer {
}
void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("userId="); pw.print(userId);
+ pw.print(prefix); pw.print("userId="); pw.print(mUserId);
pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid);
pw.print(" mCallingUid="); UserHandle.formatUid(pw, mCallingUid);
pw.print(" mUserSetupComplete="); pw.print(mUserSetupComplete);
@@ -2440,7 +2411,7 @@ class TaskRecord extends ConfigurationContainer {
pw.print(" mReuseTask="); pw.print(mReuseTask);
pw.print(" mLockTaskAuth="); pw.println(lockTaskAuthToString());
}
- if (mAffiliatedTaskId != taskId || mPrevAffiliateTaskId != INVALID_TASK_ID
+ if (mAffiliatedTaskId != mTaskId || mPrevAffiliateTaskId != INVALID_TASK_ID
|| mPrevAffiliate != null || mNextAffiliateTaskId != INVALID_TASK_ID
|| mNextAffiliate != null) {
pw.print(prefix); pw.print("affiliation="); pw.print(mAffiliatedTaskId);
@@ -2487,18 +2458,18 @@ class TaskRecord extends ConfigurationContainer {
if (stringName != null) {
sb.append(stringName);
sb.append(" U=");
- sb.append(userId);
+ sb.append(mUserId);
sb.append(" StackId=");
sb.append(getStackId());
sb.append(" sz=");
- sb.append(mActivities.size());
+ sb.append(getChildCount());
sb.append('}');
return sb.toString();
}
sb.append("TaskRecord{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(" #");
- sb.append(taskId);
+ sb.append(mTaskId);
if (affinity != null) {
sb.append(" A=");
sb.append(affinity);
@@ -2523,9 +2494,9 @@ class TaskRecord extends ConfigurationContainer {
final long token = proto.start(fieldId);
super.writeToProto(proto, CONFIGURATION_CONTAINER, logLevel);
- proto.write(ID, taskId);
- for (int i = mActivities.size() - 1; i >= 0; i--) {
- ActivityRecord activity = mActivities.get(i);
+ proto.write(ID, mTaskId);
+ for (int i = getChildCount() - 1; i >= 0; i--) {
+ ActivityRecord activity = getChildAt(i);
activity.writeToProto(proto, ACTIVITIES);
}
proto.write(STACK_ID, mStack.mStackId);
@@ -2573,7 +2544,7 @@ class TaskRecord extends ConfigurationContainer {
void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
if (DEBUG_RECENTS) Slog.i(TAG_RECENTS, "Saving task=" + this);
- out.attribute(null, ATTR_TASKID, String.valueOf(taskId));
+ out.attribute(null, ATTR_TASKID, String.valueOf(mTaskId));
if (realActivity != null) {
out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString());
}
@@ -2596,7 +2567,7 @@ class TaskRecord extends ConfigurationContainer {
out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset));
out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents));
out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
- out.attribute(null, ATTR_USERID, String.valueOf(userId));
+ out.attribute(null, ATTR_USERID, String.valueOf(mUserId));
out.attribute(null, ATTR_USER_SETUP_COMPLETE, String.valueOf(mUserSetupComplete));
out.attribute(null, ATTR_EFFECTIVE_UID, String.valueOf(effectiveUid));
out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
@@ -2604,8 +2575,8 @@ class TaskRecord extends ConfigurationContainer {
if (lastDescription != null) {
out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString());
}
- if (lastTaskDescription != null) {
- lastTaskDescription.saveToXml(out);
+ if (mTaskDescription != null) {
+ mTaskDescription.saveToXml(out);
}
out.attribute(null, ATTR_TASK_AFFILIATION_COLOR, String.valueOf(mAffiliatedTaskColor));
out.attribute(null, ATTR_TASK_AFFILIATION, String.valueOf(mAffiliatedTaskId));
@@ -2636,10 +2607,9 @@ class TaskRecord extends ConfigurationContainer {
out.endTag(null, TAG_INTENT);
}
- final ArrayList<ActivityRecord> activities = mActivities;
- final int numActivities = activities.size();
+ final int numActivities = getChildCount();
for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ final ActivityRecord r = getChildAt(activityNdx);
if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() ||
((r.intent.getFlags() & FLAG_ACTIVITY_NEW_DOCUMENT
| FLAG_ACTIVITY_RETAIN_IN_RECENTS) == FLAG_ACTIVITY_NEW_DOCUMENT) &&
@@ -2692,13 +2662,14 @@ class TaskRecord extends ConfigurationContainer {
TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
Intent intent, IVoiceInteractionSession voiceSession,
IVoiceInteractor voiceInteractor) {
- return new TaskRecord(
- service, taskId, info, intent, voiceSession, voiceInteractor);
+ return new TaskRecord(service, taskId, info, intent, voiceSession, voiceInteractor,
+ null /*taskDescription*/);
}
TaskRecord create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
Intent intent, TaskDescription taskDescription) {
- return new TaskRecord(service, taskId, info, intent, taskDescription);
+ return new TaskRecord(service, taskId, info, intent, null /*voiceSession*/,
+ null /*voiceInteractor*/, taskDescription);
}
/**
@@ -2720,7 +2691,8 @@ class TaskRecord extends ConfigurationContainer {
lastTimeMoved, neverRelinquishIdentity, lastTaskDescription, taskAffiliation,
prevTaskId, nextTaskId, taskAffiliationColor, callingUid, callingPackage,
resizeMode, supportsPictureInPicture, realActivitySuspended, userSetupComplete,
- minWidth, minHeight);
+ minWidth, minHeight, null /*ActivityInfo*/, null /*_voiceSession*/,
+ null /*_voiceInteractor*/);
}
TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
diff --git a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java b/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
index d36ebf01bcc1..adecc36671c9 100644
--- a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
+++ b/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
@@ -15,14 +15,15 @@
*/
package com.android.server.wm;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
import android.graphics.GraphicBuffer;
-import android.util.Slog;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
+import com.android.server.protolog.common.ProtoLog;
+
import java.util.function.Function;
/**
@@ -38,17 +39,16 @@ class TaskScreenshotAnimatable implements SurfaceAnimator.Animatable {
private int mWidth;
private int mHeight;
- TaskScreenshotAnimatable(Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory, Task task,
- SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer) {
+ TaskScreenshotAnimatable(Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory,
+ Task task, SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer) {
GraphicBuffer buffer = screenshotBuffer == null
? null : screenshotBuffer.getGraphicBuffer();
mTask = task;
mWidth = (buffer != null) ? buffer.getWidth() : 1;
mHeight = (buffer != null) ? buffer.getHeight() : 1;
- if (DEBUG_RECENTS_ANIMATIONS) {
- Slog.d(TAG, "Creating TaskScreenshotAnimatable: task: " + task
- + "width: " + mWidth + "height: " + mHeight);
- }
+ ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
+ "Creating TaskScreenshotAnimatable: task: %s width: %d height: %d",
+ task, mWidth, mHeight);
mSurfaceControl = surfaceControlFactory.apply(new SurfaceSession())
.setName("RecentTaskScreenshotSurface")
.setBufferSize(mWidth, mHeight)
@@ -58,6 +58,8 @@ class TaskScreenshotAnimatable implements SurfaceAnimator.Animatable {
surface.copyFrom(mSurfaceControl);
surface.attachAndQueueBufferWithColorSpace(buffer, screenshotBuffer.getColorSpace());
surface.release();
+ final float scale = 1.0f * mTask.getBounds().width() / mWidth;
+ mSurfaceControl.setMatrix(scale, 0, 0, scale);
}
getPendingTransaction().show(mSurfaceControl);
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotCache.java b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
index 8175c4a1b528..9e55113c7705 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotCache.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
@@ -34,7 +34,7 @@ class TaskSnapshotCache {
private final WindowManagerService mService;
private final TaskSnapshotLoader mLoader;
- private final ArrayMap<AppWindowToken, Integer> mAppTaskMap = new ArrayMap<>();
+ private final ArrayMap<ActivityRecord, Integer> mAppTaskMap = new ArrayMap<>();
private final ArrayMap<Integer, CacheEntry> mRunningCache = new ArrayMap<>();
TaskSnapshotCache(WindowManagerService service, TaskSnapshotLoader loader) {
@@ -47,7 +47,7 @@ class TaskSnapshotCache {
if (entry != null) {
mAppTaskMap.remove(entry.topApp);
}
- final AppWindowToken top = task.getTopChild();
+ final ActivityRecord top = task.getTopChild();
mAppTaskMap.put(top, task.mTaskId);
mRunningCache.put(task.mTaskId, new CacheEntry(snapshot, task.getTopChild()));
}
@@ -87,8 +87,8 @@ class TaskSnapshotCache {
/**
* Called when an app token has been removed
*/
- void onAppRemoved(AppWindowToken wtoken) {
- final Integer taskId = mAppTaskMap.get(wtoken);
+ void onAppRemoved(ActivityRecord activity) {
+ final Integer taskId = mAppTaskMap.get(activity);
if (taskId != null) {
removeRunningEntry(taskId);
}
@@ -97,8 +97,8 @@ class TaskSnapshotCache {
/**
* Callend when an app window token's process died.
*/
- void onAppDied(AppWindowToken wtoken) {
- final Integer taskId = mAppTaskMap.get(wtoken);
+ void onAppDied(ActivityRecord activity) {
+ final Integer taskId = mAppTaskMap.get(activity);
if (taskId != null) {
removeRunningEntry(taskId);
}
@@ -134,9 +134,9 @@ class TaskSnapshotCache {
final TaskSnapshot snapshot;
/** The app token that was on top of the task when the snapshot was taken */
- final AppWindowToken topApp;
+ final ActivityRecord topApp;
- CacheEntry(TaskSnapshot snapshot, AppWindowToken topApp) {
+ CacheEntry(TaskSnapshot snapshot, ActivityRecord topApp) {
this.snapshot = snapshot;
this.topApp = topApp;
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index f9a6fe725fa0..0d4ec652f3bb 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -140,13 +140,13 @@ class TaskSnapshotController {
/**
* Called when the visibility of an app changes outside of the regular app transition flow.
*/
- void notifyAppVisibilityChanged(AppWindowToken appWindowToken, boolean visible) {
+ void notifyAppVisibilityChanged(ActivityRecord appWindowToken, boolean visible) {
if (!visible) {
handleClosingApps(Sets.newArraySet(appWindowToken));
}
}
- private void handleClosingApps(ArraySet<AppWindowToken> closingApps) {
+ private void handleClosingApps(ArraySet<ActivityRecord> closingApps) {
if (shouldDisableSnapshots()) {
return;
}
@@ -215,9 +215,9 @@ class TaskSnapshotController {
* Creates a starting surface for {@param token} with {@param snapshot}. DO NOT HOLD THE WINDOW
* MANAGER LOCK WHEN CALLING THIS METHOD!
*/
- StartingSurface createStartingSurface(AppWindowToken token,
+ StartingSurface createStartingSurface(ActivityRecord activity,
TaskSnapshot snapshot) {
- return TaskSnapshotSurface.create(mService, token, snapshot);
+ return TaskSnapshotSurface.create(mService, activity, snapshot);
}
/**
@@ -225,21 +225,21 @@ class TaskSnapshotController {
* we're looking for, but during app transitions, trampoline activities can appear in the
* children, which should be ignored.
*/
- @Nullable private AppWindowToken findAppTokenForSnapshot(Task task) {
+ @Nullable private ActivityRecord findAppTokenForSnapshot(Task task) {
for (int i = task.getChildCount() - 1; i >= 0; --i) {
- final AppWindowToken appWindowToken = task.getChildAt(i);
- if (appWindowToken == null || !appWindowToken.isSurfaceShowing()
- || appWindowToken.findMainWindow() == null) {
+ final ActivityRecord activity = task.getChildAt(i);
+ if (activity == null || !activity.isSurfaceShowing()
+ || activity.findMainWindow() == null) {
continue;
}
- final boolean hasVisibleChild = appWindowToken.forAllWindows(
+ final boolean hasVisibleChild = activity.forAllWindows(
// Ensure at least one window for the top app is visible before attempting to
// take a screenshot. Visible here means that the WSA surface is shown and has
// an alpha greater than 0.
ws -> ws.mWinAnimator != null && ws.mWinAnimator.getShown()
&& ws.mWinAnimator.mLastAlpha > 0f, true /* traverseTopToBottom */);
if (hasVisibleChild) {
- return appWindowToken;
+ return activity;
}
}
return null;
@@ -275,16 +275,16 @@ class TaskSnapshotController {
return null;
}
- final AppWindowToken appWindowToken = findAppTokenForSnapshot(task);
- if (appWindowToken == null) {
+ final ActivityRecord activity = findAppTokenForSnapshot(task);
+ if (activity == null) {
if (DEBUG_SCREENSHOT) {
Slog.w(TAG_WM, "Failed to take screenshot. No visible windows for " + task);
}
return null;
}
- if (appWindowToken.hasCommittedReparentToAnimationLeash()) {
+ if (activity.hasCommittedReparentToAnimationLeash()) {
if (DEBUG_SCREENSHOT) {
- Slog.w(TAG_WM, "Failed to take screenshot. App is animating " + appWindowToken);
+ Slog.w(TAG_WM, "Failed to take screenshot. App is animating " + activity);
}
return null;
}
@@ -294,7 +294,7 @@ class TaskSnapshotController {
? mPersister.getReducedScale()
: mFullSnapshotScale;
- final WindowState mainWindow = appWindowToken.findMainWindow();
+ final WindowState mainWindow = activity.findMainWindow();
if (mainWindow == null) {
Slog.w(TAG_WM, "Failed to take screenshot. No main window for " + task);
return null;
@@ -311,12 +311,12 @@ class TaskSnapshotController {
final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE;
return new TaskSnapshot(
System.currentTimeMillis() /* id */,
- appWindowToken.mActivityComponent, screenshotBuffer.getGraphicBuffer(),
+ activity.mActivityComponent, screenshotBuffer.getGraphicBuffer(),
screenshotBuffer.getColorSpace(),
- appWindowToken.getTask().getConfiguration().orientation,
+ activity.getTask().getConfiguration().orientation,
getInsets(mainWindow), isLowRamDevice /* reduced */, scaleFraction /* scale */,
true /* isRealSnapshot */, task.getWindowingMode(), getSystemUiVisibility(task),
- !appWindowToken.fillsParent() || isWindowTranslucent);
+ !activity.fillsParent() || isWindowTranslucent);
}
private boolean shouldDisableSnapshots() {
@@ -327,7 +327,7 @@ class TaskSnapshotController {
// XXX(b/72757033): These are insets relative to the window frame, but we're really
// interested in the insets relative to the task bounds.
final Rect insets = minRect(state.getContentInsets(), state.getStableInsets());
- InsetUtils.addInsets(insets, state.mAppToken.getLetterboxInsets());
+ InsetUtils.addInsets(insets, state.mActivityRecord.getLetterboxInsets());
return insets;
}
@@ -342,11 +342,11 @@ class TaskSnapshotController {
* Retrieves all closing tasks based on the list of closing apps during an app transition.
*/
@VisibleForTesting
- void getClosingTasks(ArraySet<AppWindowToken> closingApps, ArraySet<Task> outClosingTasks) {
+ void getClosingTasks(ArraySet<ActivityRecord> closingApps, ArraySet<Task> outClosingTasks) {
outClosingTasks.clear();
for (int i = closingApps.size() - 1; i >= 0; i--) {
- final AppWindowToken atoken = closingApps.valueAt(i);
- final Task task = atoken.getTask();
+ final ActivityRecord activity = closingApps.valueAt(i);
+ final Task task = activity.getTask();
// If the task of the app is not visible anymore, it means no other app in that task
// is opening. Thus, the task is closing.
@@ -358,7 +358,7 @@ class TaskSnapshotController {
@VisibleForTesting
int getSnapshotMode(Task task) {
- final AppWindowToken topChild = task.getTopChild();
+ final ActivityRecord topChild = task.getTopChild();
if (!task.isActivityTypeStandardOrUndefined() && !task.isActivityTypeAssistant()) {
return SNAPSHOT_MODE_NONE;
} else if (topChild != null && topChild.shouldUseAppThemeSnapshot()) {
@@ -373,7 +373,7 @@ class TaskSnapshotController {
* as possible by using the theme's window background.
*/
private TaskSnapshot drawAppThemeSnapshot(Task task) {
- final AppWindowToken topChild = task.getTopChild();
+ final ActivityRecord topChild = task.getTopChild();
if (topChild == null) {
return null;
}
@@ -415,17 +415,17 @@ class TaskSnapshotController {
}
/**
- * Called when an {@link AppWindowToken} has been removed.
+ * Called when an {@link ActivityRecord} has been removed.
*/
- void onAppRemoved(AppWindowToken wtoken) {
- mCache.onAppRemoved(wtoken);
+ void onAppRemoved(ActivityRecord activity) {
+ mCache.onAppRemoved(activity);
}
/**
- * Called when the process of an {@link AppWindowToken} has died.
+ * Called when the process of an {@link ActivityRecord} has died.
*/
- void onAppDied(AppWindowToken wtoken) {
- mCache.onAppDied(wtoken);
+ void onAppDied(ActivityRecord activity) {
+ mCache.onAppDied(activity);
}
void notifyTaskRemovedFromRecents(int taskId, int userId) {
@@ -481,9 +481,9 @@ class TaskSnapshotController {
* {@param task}.
*/
private int getSystemUiVisibility(Task task) {
- final AppWindowToken topFullscreenToken = task.getTopFullscreenAppToken();
- final WindowState topFullscreenWindow = topFullscreenToken != null
- ? topFullscreenToken.getTopFullscreenWindow()
+ final ActivityRecord topFullscreenActivity = task.getTopFullscreenActivity();
+ final WindowState topFullscreenWindow = topFullscreenActivity != null
+ ? topFullscreenActivity.getTopFullscreenWindow()
: null;
if (topFullscreenWindow != null) {
return topFullscreenWindow.getSystemUiVisibility();
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 7456f0d78137..b1e5c8f7f5ee 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -42,7 +42,7 @@ import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIB
import static com.android.internal.policy.DecorView.getColorViewLeftInset;
import static com.android.internal.policy.DecorView.getColorViewTopInset;
import static com.android.internal.policy.DecorView.getNavigationBarRect;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -79,6 +79,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.DecorView;
import com.android.internal.view.BaseIWindow;
import com.android.server.policy.WindowManagerPolicy.StartingSurface;
+import com.android.server.protolog.common.ProtoLog;
/**
* This class represents a starting window that shows a snapshot.
@@ -133,8 +134,9 @@ class TaskSnapshotSurface implements StartingSurface {
private final int mStatusBarColor;
@VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter;
private final int mOrientationOnCreation;
+ private final SurfaceControl.Transaction mTransaction;
- static TaskSnapshotSurface create(WindowManagerService service, AppWindowToken token,
+ static TaskSnapshotSurface create(WindowManagerService service, ActivityRecord activity,
TaskSnapshot snapshot) {
final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
@@ -157,23 +159,24 @@ class TaskSnapshotSurface implements StartingSurface {
final int windowPrivateFlags;
final int currentOrientation;
synchronized (service.mGlobalLock) {
- final WindowState mainWindow = token.findMainWindow();
- final Task task = token.getTask();
+ final WindowState mainWindow = activity.findMainWindow();
+ final Task task = activity.getTask();
if (task == null) {
- Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find task for token="
- + token);
+ Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find task for activity="
+ + activity);
return null;
}
- final AppWindowToken topFullscreenToken = token.getTask().getTopFullscreenAppToken();
- if (topFullscreenToken == null) {
+ final ActivityRecord topFullscreenActivity =
+ activity.getTask().getTopFullscreenActivity();
+ if (topFullscreenActivity == null) {
Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find top fullscreen for task="
+ task);
return null;
}
- final WindowState topFullscreenWindow = topFullscreenToken.getTopFullscreenWindow();
+ final WindowState topFullscreenWindow = topFullscreenActivity.getTopFullscreenWindow();
if (mainWindow == null || topFullscreenWindow == null) {
- Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find main window for token="
- + token);
+ Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find main window for activity="
+ + activity);
return null;
}
sysUiVis = topFullscreenWindow.getSystemUiVisibility();
@@ -189,7 +192,7 @@ class TaskSnapshotSurface implements StartingSurface {
| FLAG_NOT_FOCUSABLE
| FLAG_NOT_TOUCHABLE;
layoutParams.privateFlags = windowPrivateFlags & PRIVATE_FLAG_INHERITS;
- layoutParams.token = token.token;
+ layoutParams.token = activity.token;
layoutParams.width = LayoutParams.MATCH_PARENT;
layoutParams.height = LayoutParams.MATCH_PARENT;
layoutParams.systemUiVisibility = sysUiVis;
@@ -205,7 +208,7 @@ class TaskSnapshotSurface implements StartingSurface {
}
try {
final int res = session.addToDisplay(window, window.mSeq, layoutParams,
- View.GONE, token.getDisplayContent().getDisplayId(), tmpFrame, tmpRect, tmpRect,
+ View.GONE, activity.getDisplayContent().getDisplayId(), tmpFrame, tmpRect, tmpRect,
tmpRect, tmpCutout, null, mTmpInsetsState);
if (res < 0) {
Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
@@ -251,6 +254,7 @@ class TaskSnapshotSurface implements StartingSurface {
windowPrivateFlags, sysUiVis, taskDescription, 1f);
mStatusBarColor = taskDescription.getStatusBarColor();
mOrientationOnCreation = currentOrientation;
+ mTransaction = mService.mTransactionFactory.get();
}
@Override
@@ -259,14 +263,14 @@ class TaskSnapshotSurface implements StartingSurface {
final long now = SystemClock.uptimeMillis();
if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS) {
mHandler.postAtTime(this::remove, mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS);
- if (DEBUG_STARTING_WINDOW) {
- Slog.v(TAG, "Defer removing snapshot surface in " + (now - mShownTime) + "ms");
- }
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
+ "Defer removing snapshot surface in %dms", (now - mShownTime));
+
return;
}
}
try {
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Removing snapshot surface");
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Removing snapshot surface");
mSession.remove(mWindow);
} catch (RemoteException e) {
// Local call.
@@ -286,8 +290,8 @@ class TaskSnapshotSurface implements StartingSurface {
private void drawSnapshot() {
mSurface.copyFrom(mSurfaceControl);
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Drawing snapshot surface sizeMismatch="
- + mSizeMismatch);
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Drawing snapshot surface sizeMismatch=%b",
+ mSizeMismatch);
if (mSizeMismatch) {
// The dimensions of the buffer and the window don't match, so attaching the buffer
// will fail. Better create a child window with the exact dimensions and fill the parent
@@ -335,27 +339,23 @@ class TaskSnapshotSurface implements StartingSurface {
surface.copyFrom(mChildSurfaceControl);
final Rect frame;
- SurfaceControl.openTransaction();
- try {
- // We can just show the surface here as it will still be hidden as the parent is
- // still hidden.
- mChildSurfaceControl.show();
- if (aspectRatioMismatch) {
- // Clip off ugly navigation bar.
- final Rect crop = calculateSnapshotCrop();
- frame = calculateSnapshotFrame(crop);
- mChildSurfaceControl.setWindowCrop(crop);
- mChildSurfaceControl.setPosition(frame.left, frame.top);
- } else {
- frame = null;
- }
-
- // Scale the mismatch dimensions to fill the task bounds
- final float scale = 1 / mSnapshot.getScale();
- mChildSurfaceControl.setMatrix(scale, 0, 0, scale);
- } finally {
- SurfaceControl.closeTransaction();
+ // We can just show the surface here as it will still be hidden as the parent is
+ // still hidden.
+ mTransaction.show(mChildSurfaceControl);
+ if (aspectRatioMismatch) {
+ // Clip off ugly navigation bar.
+ final Rect crop = calculateSnapshotCrop();
+ frame = calculateSnapshotFrame(crop);
+ mTransaction.setWindowCrop(mChildSurfaceControl, crop);
+ mTransaction.setPosition(mChildSurfaceControl, frame.left, frame.top);
+ } else {
+ frame = null;
}
+
+ // Scale the mismatch dimensions to fill the task bounds
+ final float scale = 1 / mSnapshot.getScale();
+ mTransaction.setMatrix(mChildSurfaceControl, scale, 0, 0, scale);
+ mTransaction.apply();
surface.attachAndQueueBufferWithColorSpace(buffer, mSnapshot.getColorSpace());
surface.release();
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 10d8328a7535..fc9a110bb4b1 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -78,6 +78,7 @@ import com.android.internal.policy.DockedDividerUtils;
import com.android.server.EventLogTags;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
public class TaskStack extends WindowContainer<Task> implements
@@ -110,9 +111,9 @@ public class TaskStack extends WindowContainer<Task> implements
*/
private final Rect mFullyAdjustedImeBounds = new Rect();
- /** Application tokens that are exiting, but still on screen for animations. */
- final AppTokenList mExitingAppTokens = new AppTokenList();
- final AppTokenList mTmpAppTokens = new AppTokenList();
+ /** ActivityRecords that are exiting, but still on screen for animations. */
+ final ArrayList<ActivityRecord> mExitingActivities = new ArrayList<>();
+ final ArrayList<ActivityRecord> mTmpActivities = new ArrayList<>();
/** Detach this stack from its display when animation completes. */
// TODO: maybe tie this to WindowContainer#removeChild some how...
@@ -153,8 +154,8 @@ public class TaskStack extends WindowContainer<Task> implements
final Rect mTmpDimBoundsRect = new Rect();
private final Point mLastSurfaceSize = new Point();
- private final AnimatingAppWindowTokenRegistry mAnimatingAppWindowTokenRegistry =
- new AnimatingAppWindowTokenRegistry();
+ private final AnimatingActivityRegistry mAnimatingActivityRegistry =
+ new AnimatingActivityRegistry();
TaskStack(WindowManagerService service, int stackId, ActivityStack activityStack) {
super(service);
@@ -676,11 +677,11 @@ public class TaskStack extends WindowContainer<Task> implements
}
mDisplayContent.setLayoutNeeded();
}
- for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) {
- final AppWindowToken wtoken = mExitingAppTokens.get(appNdx);
- if (wtoken.getTask() == task) {
- wtoken.mIsExiting = false;
- mExitingAppTokens.remove(appNdx);
+ for (int appNdx = mExitingActivities.size() - 1; appNdx >= 0; --appNdx) {
+ final ActivityRecord activity = mExitingActivities.get(appNdx);
+ if (activity.getTask() == task) {
+ activity.mIsExiting = false;
+ mExitingActivities.remove(appNdx);
}
}
}
@@ -1327,18 +1328,18 @@ public class TaskStack extends WindowContainer<Task> implements
for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
mChildren.get(taskNdx).dump(pw, prefix + " ", dumpAll);
}
- if (!mExitingAppTokens.isEmpty()) {
+ if (!mExitingActivities.isEmpty()) {
pw.println();
pw.println(" Exiting application tokens:");
- for (int i = mExitingAppTokens.size() - 1; i >= 0; i--) {
- WindowToken token = mExitingAppTokens.get(i);
+ for (int i = mExitingActivities.size() - 1; i >= 0; i--) {
+ WindowToken token = mExitingActivities.get(i);
pw.print(" Exiting App #"); pw.print(i);
pw.print(' '); pw.print(token);
pw.println(':');
token.dump(pw, " ", dumpAll);
}
}
- mAnimatingAppWindowTokenRegistry.dump(pw, "AnimatingApps:", prefix);
+ mAnimatingActivityRegistry.dump(pw, "AnimatingApps:", prefix);
}
@Override
@@ -1435,8 +1436,8 @@ public class TaskStack extends WindowContainer<Task> implements
Rect contentRect, Rect postExclude) {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final Task task = mChildren.get(i);
- AppWindowToken token = task.getTopVisibleAppToken();
- if (token == null || !token.hasContentToDisplay()) {
+ ActivityRecord topVisibleActivity = task.getTopVisibleActivity();
+ if (topVisibleActivity == null || !topVisibleActivity.hasContentToDisplay()) {
continue;
}
@@ -1747,7 +1748,7 @@ public class TaskStack extends WindowContainer<Task> implements
if (homeTask == null) {
return true;
}
- final AppWindowToken homeApp = homeTask.getTopVisibleAppToken();
+ final ActivityRecord homeApp = homeTask.getTopVisibleActivity();
if (!homeTask.isVisible() || homeApp == null) {
return true;
}
@@ -1862,7 +1863,7 @@ public class TaskStack extends WindowContainer<Task> implements
scheduleAnimation();
}
- AnimatingAppWindowTokenRegistry getAnimatingAppWindowTokenRegistry() {
- return mAnimatingAppWindowTokenRegistry;
+ AnimatingActivityRegistry getAnimatingActivityRegistry() {
+ return mAnimatingActivityRegistry;
}
}
diff --git a/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java b/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java
index 01abcab45760..61e9e5082d17 100644
--- a/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java
+++ b/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java
@@ -29,7 +29,7 @@ import com.android.server.wm.WindowManagerService.H;
import java.io.PrintWriter;
/**
- * Manages the set of {@link AppWindowToken}s for which we don't know yet whether it's visible or
+ * Manages the set of {@link ActivityRecord}s for which we don't know yet whether it's visible or
* not. This happens when starting an activity while the lockscreen is showing. In that case, the
* keyguard flags an app might set influence it's visibility, so we wait until this is resolved to
* start the transition to avoid flickers.
@@ -56,7 +56,7 @@ class UnknownAppVisibilityController {
// Set of apps for which we don't know yet whether it's visible or not, depending on what kind
// of lockscreen flags the app might set during its first relayout.
- private final ArrayMap<AppWindowToken, Integer> mUnknownApps = new ArrayMap<>();
+ private final ArrayMap<ActivityRecord, Integer> mUnknownApps = new ArrayMap<>();
private final WindowManagerService mService;
@@ -87,52 +87,52 @@ class UnknownAppVisibilityController {
return builder.toString();
}
- void appRemovedOrHidden(@NonNull AppWindowToken appWindow) {
+ void appRemovedOrHidden(@NonNull ActivityRecord activity) {
if (DEBUG_UNKNOWN_APP_VISIBILITY) {
- Slog.d(TAG, "App removed or hidden appWindow=" + appWindow);
+ Slog.d(TAG, "App removed or hidden activity=" + activity);
}
- mUnknownApps.remove(appWindow);
+ mUnknownApps.remove(activity);
}
/**
- * Notifies that {@param appWindow} has been launched behind Keyguard, and we need to wait until
+ * Notifies that {@param activity} has been launched behind Keyguard, and we need to wait until
* it is resumed and relaid out to resolve the visibility.
*/
- void notifyLaunched(@NonNull AppWindowToken appWindow) {
+ void notifyLaunched(@NonNull ActivityRecord activity) {
if (DEBUG_UNKNOWN_APP_VISIBILITY) {
- Slog.d(TAG, "App launched appWindow=" + appWindow);
+ Slog.d(TAG, "App launched activity=" + activity);
}
- mUnknownApps.put(appWindow, UNKNOWN_STATE_WAITING_RESUME);
+ mUnknownApps.put(activity, UNKNOWN_STATE_WAITING_RESUME);
}
/**
- * Notifies that {@param appWindow} has finished resuming.
+ * Notifies that {@param activity} has finished resuming.
*/
- void notifyAppResumedFinished(@NonNull AppWindowToken appWindow) {
- if (mUnknownApps.containsKey(appWindow)
- && mUnknownApps.get(appWindow) == UNKNOWN_STATE_WAITING_RESUME) {
+ void notifyAppResumedFinished(@NonNull ActivityRecord activity) {
+ if (mUnknownApps.containsKey(activity)
+ && mUnknownApps.get(activity) == UNKNOWN_STATE_WAITING_RESUME) {
if (DEBUG_UNKNOWN_APP_VISIBILITY) {
- Slog.d(TAG, "App resume finished appWindow=" + appWindow);
+ Slog.d(TAG, "App resume finished activity=" + activity);
}
- mUnknownApps.put(appWindow, UNKNOWN_STATE_WAITING_RELAYOUT);
+ mUnknownApps.put(activity, UNKNOWN_STATE_WAITING_RELAYOUT);
}
}
/**
- * Notifies that {@param appWindow} has relaid out.
+ * Notifies that {@param activity} has relaid out.
*/
- void notifyRelayouted(@NonNull AppWindowToken appWindow) {
- if (!mUnknownApps.containsKey(appWindow)) {
+ void notifyRelayouted(@NonNull ActivityRecord activity) {
+ if (!mUnknownApps.containsKey(activity)) {
return;
}
if (DEBUG_UNKNOWN_APP_VISIBILITY) {
- Slog.d(TAG, "App relayouted appWindow=" + appWindow);
+ Slog.d(TAG, "App relayouted appWindow=" + activity);
}
- int state = mUnknownApps.get(appWindow);
+ int state = mUnknownApps.get(activity);
if (state == UNKNOWN_STATE_WAITING_RELAYOUT) {
- mUnknownApps.put(appWindow, UNKNOWN_STATE_WAITING_VISIBILITY_UPDATE);
+ mUnknownApps.put(activity, UNKNOWN_STATE_WAITING_VISIBILITY_UPDATE);
mService.notifyKeyguardFlagsChanged(this::notifyVisibilitiesUpdated,
- appWindow.getDisplayContent().getDisplayId());
+ activity.getDisplayContent().getDisplayId());
}
}
diff --git a/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
index 895350b43eeb..513008d3c3c3 100644
--- a/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
@@ -16,17 +16,17 @@
package com.android.server.wm;
import static com.android.server.wm.AnimationAdapterProto.REMOTE;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS;
import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_REMOTE_ANIMATIONS;
import android.graphics.Point;
import android.os.SystemClock;
-import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
+import com.android.server.protolog.common.ProtoLog;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.function.Consumer;
@@ -68,15 +68,11 @@ class WallpaperAnimationAdapter implements AnimationAdapter {
final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
service.mRoot.forAllWallpaperWindows(wallpaperWindow -> {
if (!wallpaperWindow.getDisplayContent().mWallpaperController.isWallpaperVisible()) {
- if (DEBUG_REMOTE_ANIMATIONS || DEBUG_RECENTS_ANIMATIONS) {
- Slog.d(TAG, "\tNot visible=" + wallpaperWindow);
- }
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tNot visible=%s", wallpaperWindow);
return;
}
- if (DEBUG_REMOTE_ANIMATIONS || DEBUG_RECENTS_ANIMATIONS) {
- Slog.d(TAG, "\tvisible=" + wallpaperWindow);
- }
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tvisible=%s", wallpaperWindow);
final WallpaperAnimationAdapter wallpaperAdapter = new WallpaperAnimationAdapter(
wallpaperWindow, durationHint, statusBarTransitionDelay,
animationCanceledRunnable);
@@ -129,7 +125,7 @@ class WallpaperAnimationAdapter implements AnimationAdapter {
@Override
public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t,
SurfaceAnimator.OnAnimationFinishedCallback finishCallback) {
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "startAnimation");
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation");
// Restore z-layering until client has a chance to modify it.
t.setLayer(animationLeash, mWallpaperToken.getPrefixOrderIndex());
@@ -139,7 +135,7 @@ class WallpaperAnimationAdapter implements AnimationAdapter {
@Override
public void onAnimationCancelled(SurfaceControl animationLeash) {
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "onAnimationCancelled");
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "onAnimationCancelled");
mAnimationCanceledRunnable.accept(this);
}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 13902eedbfba..1e13aef2f8eb 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -24,7 +24,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
@@ -119,7 +118,7 @@ class WallpaperController {
}
mFindResults.resetTopWallpaper = true;
- if (w.mAppToken != null && w.mAppToken.isHidden() && !w.mAppToken.isSelfAnimating()) {
+ if (w.mActivityRecord != null && w.mActivityRecord.isHidden() && !w.mActivityRecord.isSelfAnimating()) {
// If this window's app token is hidden and not animating, it is of no interest to us.
if (DEBUG_WALLPAPER) Slog.v(TAG, "Skipping hidden and not animating token: " + w);
@@ -136,10 +135,10 @@ class WallpaperController {
mFindResults.setUseTopWallpaperAsTarget(true);
}
- final boolean keyguardGoingAwayWithWallpaper = (w.mAppToken != null
- && w.mAppToken.isSelfAnimating()
- && AppTransition.isKeyguardGoingAwayTransit(w.mAppToken.getTransit())
- && (w.mAppToken.getTransitFlags()
+ final boolean keyguardGoingAwayWithWallpaper = (w.mActivityRecord != null
+ && w.mActivityRecord.isSelfAnimating()
+ && AppTransition.isKeyguardGoingAwayTransit(w.mActivityRecord.getTransit())
+ && (w.mActivityRecord.getTransitFlags()
& TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0);
boolean needsShowWhenLockedWallpaper = false;
@@ -149,7 +148,7 @@ class WallpaperController {
// The lowest show when locked window decides whether we need to put the wallpaper
// behind.
needsShowWhenLockedWallpaper = !isFullscreen(w.mAttrs)
- || (w.mAppToken != null && !w.mAppToken.fillsParent());
+ || (w.mActivityRecord != null && !w.mActivityRecord.fillsParent());
}
if (keyguardGoingAwayWithWallpaper || needsShowWhenLockedWallpaper) {
@@ -160,8 +159,8 @@ class WallpaperController {
final RecentsAnimationController recentsAnimationController =
mService.getRecentsAnimationController();
- final boolean animationWallpaper = w.mAppToken != null && w.mAppToken.getAnimation() != null
- && w.mAppToken.getAnimation().getShowWallpaper();
+ final boolean animationWallpaper = w.mActivityRecord != null && w.mActivityRecord.getAnimation() != null
+ && w.mActivityRecord.getAnimation().getShowWallpaper();
final boolean hasWallpaper = (w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0
|| animationWallpaper;
final boolean isRecentsTransitionTarget = (recentsAnimationController != null
@@ -224,22 +223,22 @@ class WallpaperController {
&& recentsAnimationController.isWallpaperVisible(wallpaperTarget);
if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + ", obscured="
+ (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??")
- + " animating=" + ((wallpaperTarget != null && wallpaperTarget.mAppToken != null)
- ? wallpaperTarget.mAppToken.isSelfAnimating() : null)
+ + " animating=" + ((wallpaperTarget != null && wallpaperTarget.mActivityRecord != null)
+ ? wallpaperTarget.mActivityRecord.isSelfAnimating() : null)
+ " prev=" + mPrevWallpaperTarget
+ " recentsAnimationWallpaperVisible=" + isAnimatingWithRecentsComponent);
return (wallpaperTarget != null
&& (!wallpaperTarget.mObscured
|| isAnimatingWithRecentsComponent
- || (wallpaperTarget.mAppToken != null
- && wallpaperTarget.mAppToken.isSelfAnimating())))
+ || (wallpaperTarget.mActivityRecord != null
+ && wallpaperTarget.mActivityRecord.isSelfAnimating())))
|| mPrevWallpaperTarget != null;
}
boolean isWallpaperTargetAnimating() {
return mWallpaperTarget != null && mWallpaperTarget.isAnimating()
- && (mWallpaperTarget.mAppToken == null
- || !mWallpaperTarget.mAppToken.isWaitingForTransitionStart());
+ && (mWallpaperTarget.mActivityRecord == null
+ || !mWallpaperTarget.mActivityRecord.isWaitingForTransitionStart());
}
void updateWallpaperVisibility() {
@@ -528,10 +527,10 @@ class WallpaperController {
return;
}
- final boolean newTargetHidden = wallpaperTarget.mAppToken != null
- && wallpaperTarget.mAppToken.hiddenRequested;
- final boolean oldTargetHidden = prevWallpaperTarget.mAppToken != null
- && prevWallpaperTarget.mAppToken.hiddenRequested;
+ final boolean newTargetHidden = wallpaperTarget.mActivityRecord != null
+ && wallpaperTarget.mActivityRecord.hiddenRequested;
+ final boolean oldTargetHidden = prevWallpaperTarget.mActivityRecord != null
+ && prevWallpaperTarget.mActivityRecord.hiddenRequested;
if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Animating wallpapers:" + " old: "
+ prevWallpaperTarget + " hidden=" + oldTargetHidden + " new: " + wallpaperTarget
@@ -545,9 +544,9 @@ class WallpaperController {
// is not. If they're both hidden, still use the new target.
mWallpaperTarget = prevWallpaperTarget;
} else if (newTargetHidden == oldTargetHidden
- && !mDisplayContent.mOpeningApps.contains(wallpaperTarget.mAppToken)
- && (mDisplayContent.mOpeningApps.contains(prevWallpaperTarget.mAppToken)
- || mDisplayContent.mClosingApps.contains(prevWallpaperTarget.mAppToken))) {
+ && !mDisplayContent.mOpeningApps.contains(wallpaperTarget.mActivityRecord)
+ && (mDisplayContent.mOpeningApps.contains(prevWallpaperTarget.mActivityRecord)
+ || mDisplayContent.mClosingApps.contains(prevWallpaperTarget.mActivityRecord))) {
// If they're both hidden (or both not hidden), prefer the one that's currently in
// opening or closing app list, this allows transition selection logic to better
// determine the wallpaper status of opening/closing apps.
@@ -607,8 +606,9 @@ class WallpaperController {
boolean processWallpaperDrawPendingTimeout() {
if (mWallpaperDrawState == WALLPAPER_DRAW_PENDING) {
mWallpaperDrawState = WALLPAPER_DRAW_TIMEOUT;
- if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG,
- "*** WALLPAPER DRAW TIMEOUT");
+ if (DEBUG_WALLPAPER) {
+ Slog.v(TAG, "*** WALLPAPER DRAW TIMEOUT");
+ }
// If there was a pending recents animation, start the animation anyways (it's better
// to not see the wallpaper than for the animation to not start)
@@ -641,9 +641,11 @@ class WallpaperController {
WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION);
}
- if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG,
- "Wallpaper should be visible but has not been drawn yet. " +
- "mWallpaperDrawState=" + mWallpaperDrawState);
+ if (DEBUG_WALLPAPER) {
+ Slog.v(TAG,
+ "Wallpaper should be visible but has not been drawn yet. "
+ + "mWallpaperDrawState=" + mWallpaperDrawState);
+ }
break;
}
}
@@ -659,23 +661,23 @@ class WallpaperController {
* Adjusts the wallpaper windows if the input display has a pending wallpaper layout or one of
* the opening apps should be a wallpaper target.
*/
- void adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<AppWindowToken> openingApps,
- ArraySet<AppWindowToken> changingApps) {
+ void adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<ActivityRecord> openingApps,
+ ArraySet<ActivityRecord> changingApps) {
boolean adjust = false;
if ((mDisplayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
adjust = true;
} else {
for (int i = openingApps.size() - 1; i >= 0; --i) {
- final AppWindowToken token = openingApps.valueAt(i);
- if (token.windowsCanBeWallpaperTarget()) {
+ final ActivityRecord activity = openingApps.valueAt(i);
+ if (activity.windowsCanBeWallpaperTarget()) {
adjust = true;
break;
}
}
if (!adjust) {
for (int i = changingApps.size() - 1; i >= 0; --i) {
- final AppWindowToken token = changingApps.valueAt(i);
- if (token.windowsCanBeWallpaperTarget()) {
+ final ActivityRecord activity = changingApps.valueAt(i);
+ if (activity.windowsCanBeWallpaperTarget()) {
adjust = true;
break;
}
diff --git a/services/core/java/com/android/server/wm/Watermark.java b/services/core/java/com/android/server/wm/Watermark.java
index 729cfc04e36c..725aaa48c645 100644
--- a/services/core/java/com/android/server/wm/Watermark.java
+++ b/services/core/java/com/android/server/wm/Watermark.java
@@ -55,7 +55,7 @@ class Watermark {
private boolean mDrawNeeded;
Watermark(Supplier<Surface> surfaceFactory, DisplayContent dc, DisplayMetrics dm,
- String[] tokens) {
+ String[] tokens, SurfaceControl.Transaction t) {
if (false) {
Log.i(TAG_WM, "*********************** WATERMARK");
for (int i=0; i<tokens.length; i++) {
@@ -121,21 +121,21 @@ class Watermark {
.setBufferSize(1, 1)
.setFormat(PixelFormat.TRANSLUCENT)
.build();
- ctrl.setLayerStack(mDisplay.getLayerStack());
- ctrl.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER*100);
- ctrl.setPosition(0, 0);
- ctrl.show();
+ t.setLayerStack(ctrl, mDisplay.getLayerStack())
+ .setLayer(ctrl, WindowManagerService.TYPE_LAYER_MULTIPLIER * 100)
+ .setPosition(ctrl, 0, 0)
+ .show(ctrl);
mSurface.copyFrom(ctrl);
} catch (OutOfResourcesException e) {
}
mSurfaceControl = ctrl;
}
- void positionSurface(int dw, int dh) {
+ void positionSurface(int dw, int dh, SurfaceControl.Transaction t) {
if (mLastDW != dw || mLastDH != dh) {
mLastDW = dw;
mLastDH = dh;
- mSurfaceControl.setBufferSize(dw, dh);
+ t.setBufferSize(mSurfaceControl, dw, dh);
mDrawNeeded = true;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index c7916e829349..3a1d6e047fa8 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -16,12 +16,11 @@
package com.android.server.wm;
+import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
-import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
import android.content.Context;
import android.os.Trace;
@@ -33,6 +32,7 @@ import android.view.SurfaceControl;
import com.android.server.AnimationThread;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.protolog.common.ProtoLog;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -108,14 +108,6 @@ public class WindowAnimator {
}
void removeDisplayLocked(final int displayId) {
- final DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.get(displayId);
- if (displayAnimator != null) {
- if (displayAnimator.mScreenRotationAnimation != null) {
- displayAnimator.mScreenRotationAnimation.kill();
- displayAnimator.mScreenRotationAnimation = null;
- }
- }
-
mDisplayContentsAnimators.delete(displayId);
}
@@ -147,7 +139,7 @@ public class WindowAnimator {
Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
}
- if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION animate");
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, ">>> OPEN TRANSACTION animate");
mService.openSurfaceTransaction();
try {
final AccessibilityController accessibilityController =
@@ -156,27 +148,6 @@ public class WindowAnimator {
for (int i = 0; i < numDisplays; i++) {
final int displayId = mDisplayContentsAnimators.keyAt(i);
final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
- DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
-
- final ScreenRotationAnimation screenRotationAnimation =
- displayAnimator.mScreenRotationAnimation;
- if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
- if (screenRotationAnimation.stepAnimationLocked(mCurrentTime)) {
- setAnimating(true);
- } else {
- mBulkUpdateParams |= SET_UPDATE_ROTATION;
- screenRotationAnimation.kill();
- displayAnimator.mScreenRotationAnimation = null;
-
- // display.
- if (accessibilityController != null) {
- // We just finished rotation animation which means we did not
- // announce the rotation and waited for it to end, announce now.
- accessibilityController.onRotationChangedLocked(dc);
- }
- }
- }
-
// Update animations of all applications, including those
// associated with exiting/removed apps
dc.updateWindowsForAnimator();
@@ -188,15 +159,10 @@ public class WindowAnimator {
final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
dc.checkAppWindowsReadyToShow();
-
- final ScreenRotationAnimation screenRotationAnimation =
- mDisplayContentsAnimators.valueAt(i).mScreenRotationAnimation;
- if (screenRotationAnimation != null) {
- screenRotationAnimation.updateSurfaces(mTransaction);
- }
orAnimating(dc.getDockedDividerController().animate(mCurrentTime));
if (accessibilityController != null) {
- accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(displayId);
+ accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(displayId,
+ mTransaction);
}
}
@@ -213,7 +179,7 @@ public class WindowAnimator {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
mService.closeSurfaceTransaction("WindowAnimator");
- if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animate");
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate");
}
boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
@@ -273,22 +239,14 @@ public class WindowAnimator {
public void dumpLocked(PrintWriter pw, String prefix, boolean dumpAll) {
final String subPrefix = " " + prefix;
- final String subSubPrefix = " " + subPrefix;
for (int i = 0; i < mDisplayContentsAnimators.size(); i++) {
pw.print(prefix); pw.print("DisplayContentsAnimator #");
pw.print(mDisplayContentsAnimators.keyAt(i));
pw.println(":");
- final DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
final DisplayContent dc =
mService.mRoot.getDisplayContent(mDisplayContentsAnimators.keyAt(i));
dc.dumpWindowAnimators(pw, subPrefix);
- if (displayAnimator.mScreenRotationAnimation != null) {
- pw.print(subPrefix); pw.println("mScreenRotationAnimation:");
- displayAnimator.mScreenRotationAnimation.printTo(subSubPrefix, pw);
- } else if (dumpAll) {
- pw.print(subPrefix); pw.println("no ScreenRotationAnimation ");
- }
pw.println();
}
@@ -322,23 +280,6 @@ public class WindowAnimator {
return displayAnimator;
}
- void setScreenRotationAnimationLocked(int displayId, ScreenRotationAnimation animation) {
- final DisplayContentsAnimator animator = getDisplayContentsAnimatorLocked(displayId);
-
- if (animator != null) {
- animator.mScreenRotationAnimation = animation;
- }
- }
-
- ScreenRotationAnimation getScreenRotationAnimationLocked(int displayId) {
- if (displayId < 0) {
- return null;
- }
-
- DisplayContentsAnimator animator = getDisplayContentsAnimatorLocked(displayId);
- return animator != null? animator.mScreenRotationAnimation : null;
- }
-
void requestRemovalOfReplacedWindows(WindowState win) {
mRemoveReplacedWindows = true;
}
@@ -358,7 +299,6 @@ public class WindowAnimator {
}
private class DisplayContentsAnimator {
- ScreenRotationAnimation mScreenRotationAnimation = null;
}
boolean isAnimating() {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 586375f9d714..037edf1b5ab1 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -19,6 +19,11 @@ package com.android.server.wm;
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 android.content.pm.ActivityInfo.isFixedOrientationLandscape;
+import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.view.SurfaceControl.Transaction;
import static com.android.server.wm.WindowContainerProto.CONFIGURATION_CONTAINER;
@@ -28,14 +33,17 @@ import static com.android.server.wm.WindowContainerProto.VISIBLE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
import android.annotation.CallSuper;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.WindowConfiguration;
+import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.Debug;
import android.os.IBinder;
import android.util.Pools;
import android.util.Slog;
@@ -47,9 +55,11 @@ import android.view.SurfaceSession;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ToBooleanFunction;
+import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.SurfaceAnimator.Animatable;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.function.Consumer;
@@ -106,9 +116,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
private final Pools.SynchronizedPool<ForAllWindowsConsumerWrapper> mConsumerWrapperPool =
new Pools.SynchronizedPool<>(3);
- // The owner/creator for this container. No controller if null.
- WindowContainerController mController;
-
// The display this window container is on.
protected DisplayContent mDisplayContent;
@@ -120,6 +127,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
private final Transaction mPendingTransaction;
/**
+ * Windows that clients are waiting to have drawn.
+ */
+ final ArrayList<WindowState> mWaitingForDrawn = new ArrayList<>();
+
+ /**
* Applied as part of the animation pass in "prepareSurfaces".
*/
protected final SurfaceAnimator mSurfaceAnimator;
@@ -137,6 +149,16 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
*/
private boolean mCommittedReparentToAnimationLeash;
+ private final Configuration mTmpConfig = new Configuration();
+
+ /**
+ * Callback which is triggered while changing the parent, after setting up the surface but
+ * before asking the parent to assign child layers.
+ */
+ interface PreAssignChildLayersCallback {
+ void onPreAssignChildLayers();
+ }
+
WindowContainer(WindowManagerService wms) {
mWmService = wms;
mPendingTransaction = wms.mTransactionFactory.get();
@@ -176,6 +198,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
*/
@Override
void onParentChanged() {
+ onParentChanged(null);
+ }
+
+ void onParentChanged(PreAssignChildLayersCallback callback) {
super.onParentChanged();
if (mParent == null) {
return;
@@ -195,6 +221,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
reparentSurfaceControl(getPendingTransaction(), mParent.mSurfaceControl);
}
+ if (callback != null) {
+ callback.onPreAssignChildLayers();
+ }
+
// Either way we need to ask the parent to assign us a Z-order.
mParent.assignChildLayers();
scheduleAnimation();
@@ -247,7 +277,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
if (child.getParent() != null) {
throw new IllegalArgumentException("addChild: container=" + child.getName()
+ " is already a child of container=" + child.getParent().getName()
- + " can't add to container=" + getName());
+ + " can't add to container=" + getName()
+ + "\n callers=" + Debug.getCallers(15, "\n"));
}
if ((index < 0 && index != POSITION_BOTTOM)
@@ -340,11 +371,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
if (mParent != null) {
mParent.removeChild(this);
}
-
- if (mController != null) {
- setController(null);
- }
-
}
/**
@@ -743,6 +769,31 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
/**
+ * Get the configuration orientation by the requested screen orientation
+ * ({@link ActivityInfo.ScreenOrientation}) of this activity.
+ *
+ * @return orientation in ({@link Configuration#ORIENTATION_LANDSCAPE},
+ * {@link Configuration#ORIENTATION_PORTRAIT},
+ * {@link Configuration#ORIENTATION_UNDEFINED}).
+ */
+ int getRequestedConfigurationOrientation() {
+ if (mOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
+ // NOSENSOR means the display's "natural" orientation, so return that.
+ if (mDisplayContent != null) {
+ return mDisplayContent.getNaturalOrientation();
+ }
+ } else if (mOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
+ // LOCKED means the activity's orientation remains unchanged, so return existing value.
+ return getConfiguration().orientation;
+ } else if (isFixedOrientationLandscape(mOrientation)) {
+ return ORIENTATION_LANDSCAPE;
+ } else if (isFixedOrientationPortrait(mOrientation)) {
+ return ORIENTATION_PORTRAIT;
+ }
+ return ORIENTATION_UNDEFINED;
+ }
+
+ /**
* Calls {@link #setOrientation(int, IBinder, ActivityRecord)} with {@code null} to the last 2
* parameters.
*
@@ -772,6 +823,13 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
mOrientation = orientation;
+ final int configOrientation = getRequestedConfigurationOrientation();
+ if (getRequestedOverrideConfiguration().orientation != configOrientation) {
+ mTmpConfig.setTo(getRequestedOverrideConfiguration());
+ mTmpConfig.orientation = configOrientation;
+ onRequestedOverrideConfigurationChanged(mTmpConfig);
+ }
+
final WindowContainer parent = getParent();
if (parent != null) {
onDescendantOrientationChanged(freezeDisplayToken, requestingContainer);
@@ -889,9 +947,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
wrapper.release();
}
- void forAllAppWindows(Consumer<AppWindowToken> callback) {
+ void forAllActivities(Consumer<ActivityRecord> callback) {
for (int i = mChildren.size() - 1; i >= 0; --i) {
- mChildren.get(i).forAllAppWindows(callback);
+ mChildren.get(i).forAllActivities(callback);
}
}
@@ -989,23 +1047,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
} while (current != null);
}
- WindowContainerController getController() {
- return mController;
- }
-
- void setController(WindowContainerController controller) {
- if (mController != null && controller != null) {
- throw new IllegalArgumentException("Can't set controller=" + mController
- + " for container=" + this + " Already set to=" + mController);
- }
- if (controller != null) {
- controller.setContainer(this);
- } else if (mController != null) {
- mController.setContainer(null);
- }
- mController = controller;
- }
-
SurfaceControl.Builder makeSurface() {
final WindowContainer p = getParent();
return p.makeChildSurface(this);
@@ -1401,6 +1442,19 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
}
+ void waitForAllWindowsDrawn() {
+ final WindowManagerPolicy policy = mWmService.mPolicy;
+ forAllWindows(w -> {
+ final boolean keyguard = policy.isKeyguardHostWindow(w.mAttrs);
+ if (w.isVisibleLw() && (w.mActivityRecord != null || keyguard)) {
+ w.mWinAnimator.mDrawState = DRAW_PENDING;
+ // Force add to mResizingWindows.
+ w.resetLastContentInsets();
+ mWaitingForDrawn.add(w);
+ }
+ }, true /* traverseTopToBottom */);
+ }
+
Dimmer getDimmer() {
if (mParent == null) {
return null;
diff --git a/services/core/java/com/android/server/wm/WindowContainerController.java b/services/core/java/com/android/server/wm/WindowContainerController.java
deleted file mode 100644
index 17bc0e2de1f8..000000000000
--- a/services/core/java/com/android/server/wm/WindowContainerController.java
+++ /dev/null
@@ -1,83 +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.server.wm;
-
-import android.content.res.Configuration;
-
-/**
- * Class that allows the owner/creator of a {@link WindowContainer} to communicate directly with the
- * container and make changes.
- * Note that public calls (mostly in sub-classes) into this class are assumed to be originating from
- * outside the window manager so the window manager lock is held and appropriate permissions are
- * checked before calls are allowed to proceed.
- *
- * Test class: {@link WindowContainerControllerTests}
- */
-class WindowContainerController<E extends WindowContainer, I extends WindowContainerListener>
- implements ConfigurationContainerListener {
-
- final WindowManagerService mService;
- final RootWindowContainer mRoot;
- final WindowManagerGlobalLock mGlobalLock;
-
- // The window container this controller owns.
- E mContainer;
- // Interface for communicating changes back to the owner.
- final I mListener;
-
- WindowContainerController(I listener, WindowManagerService service) {
- mListener = listener;
- mService = service;
- mRoot = mService != null ? mService.mRoot : null;
- mGlobalLock = mService != null ? mService.mGlobalLock : null;
- }
-
- void setContainer(E container) {
- if (mContainer != null && container != null) {
- throw new IllegalArgumentException("Can't set container=" + container
- + " for controller=" + this + " Already set to=" + mContainer);
- }
- mContainer = container;
- if (mContainer != null && mListener != null) {
- mListener.registerConfigurationChangeListener(this);
- }
- }
-
- void removeContainer() {
- // TODO: See if most uses cases should support removeIfPossible here.
- //mContainer.removeIfPossible();
- if (mContainer == null) {
- return;
- }
-
- mContainer.setController(null);
- mContainer = null;
- if (mListener != null) {
- mListener.unregisterConfigurationChangeListener(this);
- }
- }
-
- @Override
- public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
- synchronized (mGlobalLock) {
- if (mContainer == null) {
- return;
- }
- mContainer.onRequestedOverrideConfigurationChanged(overrideConfiguration);
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/WindowContainerListener.java b/services/core/java/com/android/server/wm/WindowContainerListener.java
deleted file mode 100644
index 3d3d2e02693c..000000000000
--- a/services/core/java/com/android/server/wm/WindowContainerListener.java
+++ /dev/null
@@ -1,29 +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.server.wm;
-
-import android.content.res.Configuration;
-
-/**
- * Interface used by the owner/creator of the container to listen to changes with the container.
- * @see WindowContainerController
- */
-public interface WindowContainerListener {
- void registerConfigurationChangeListener(ConfigurationContainerListener listener);
- void unregisterConfigurationChangeListener(ConfigurationContainerListener listener);
- default void onInitializeOverrideConfiguration(Configuration config) {}
-}
diff --git a/services/core/java/com/android/server/wm/WindowHashMap.java b/services/core/java/com/android/server/wm/WindowHashMap.java
deleted file mode 100644
index 49bba4145996..000000000000
--- a/services/core/java/com/android/server/wm/WindowHashMap.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import android.os.IBinder;
-
-import java.util.HashMap;
-
-/**
- * Subclass of HashMap such that we can instruct the compiler to boost our thread priority when
- * locking this class. See makefile.
- */
-class WindowHashMap extends HashMap<IBinder, WindowState> {
-}
diff --git a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
index c366e4d6108b..93b0fd9b1fe3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
@@ -33,32 +33,19 @@ public class WindowManagerDebugConfig {
// Default log tag for the window manager package.
static final String TAG_WM = "WindowManager";
- static final boolean DEBUG_RESIZE = false;
static final boolean DEBUG = false;
- static final boolean DEBUG_ADD_REMOVE = false;
- static final boolean DEBUG_FOCUS = false;
- static final boolean DEBUG_FOCUS_LIGHT = DEBUG_FOCUS || false;
static final boolean DEBUG_ANIM = false;
- static final boolean DEBUG_KEYGUARD = false;
static final boolean DEBUG_LAYOUT = false;
static final boolean DEBUG_LAYERS = false;
static final boolean DEBUG_INPUT = false;
static final boolean DEBUG_INPUT_METHOD = false;
static final boolean DEBUG_VISIBILITY = false;
- static final boolean DEBUG_WINDOW_MOVEMENT = false;
- static final boolean DEBUG_TOKEN_MOVEMENT = false;
- static final boolean DEBUG_ORIENTATION = false;
- static final boolean DEBUG_APP_ORIENTATION = false;
static final boolean DEBUG_CONFIGURATION = false;
- static final boolean DEBUG_APP_TRANSITIONS = false;
static final boolean DEBUG_STARTING_WINDOW_VERBOSE = false;
- static final boolean DEBUG_STARTING_WINDOW = DEBUG_STARTING_WINDOW_VERBOSE || false;
static final boolean DEBUG_WALLPAPER = false;
static final boolean DEBUG_WALLPAPER_LIGHT = false || DEBUG_WALLPAPER;
static final boolean DEBUG_DRAG = false;
- static final boolean DEBUG_SCREEN_ON = false;
static final boolean DEBUG_SCREENSHOT = false;
- static final boolean DEBUG_BOOT = false;
static final boolean DEBUG_LAYOUT_REPEATS = false;
static final boolean DEBUG_WINDOW_TRACE = false;
static final boolean DEBUG_TASK_MOVEMENT = false;
@@ -66,17 +53,9 @@ public class WindowManagerDebugConfig {
static final boolean DEBUG_STACK = false;
static final boolean DEBUG_DISPLAY = false;
static final boolean DEBUG_POWER = false;
- static final boolean DEBUG_DIM_LAYER = false;
- static final boolean SHOW_SURFACE_ALLOC = false;
- static final boolean SHOW_TRANSACTIONS = false;
- static final boolean SHOW_VERBOSE_TRANSACTIONS = false && SHOW_TRANSACTIONS;
- static final boolean SHOW_LIGHT_TRANSACTIONS = false || SHOW_TRANSACTIONS;
+ static final boolean SHOW_VERBOSE_TRANSACTIONS = false;
+ static final boolean SHOW_LIGHT_TRANSACTIONS = false;
static final boolean SHOW_STACK_CRAWLS = false;
static final boolean DEBUG_WINDOW_CROP = false;
static final boolean DEBUG_UNKNOWN_APP_VISIBILITY = false;
- static final boolean DEBUG_RECENTS_ANIMATIONS = false;
- static final boolean DEBUG_REMOTE_ANIMATIONS = DEBUG_APP_TRANSITIONS || false;
-
- static final String TAG_KEEP_SCREEN_ON = "DebugKeepScreenOn";
- static final boolean DEBUG_KEEP_SCREEN_ON = false;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 750926f11180..0cb4826fdfc3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -30,6 +30,7 @@ import android.view.InputChannel;
import android.view.MagnificationSpec;
import android.view.WindowInfo;
+import com.android.internal.policy.KeyInterceptionInfo;
import com.android.server.input.InputManagerService;
import com.android.server.policy.WindowManagerPolicy;
@@ -160,9 +161,8 @@ public abstract class WindowManagerInternal {
default boolean registerInputChannel(
DragState state, Display display, InputManagerService service,
InputChannel source) {
- state.mTransferTouchFromToken = source.getToken();
state.register(display);
- return true;
+ return service.transferTouchFocus(source, state.getInputChannel());
}
/**
@@ -313,10 +313,15 @@ public abstract class WindowManagerInternal {
public abstract void showGlobalActions();
/**
- * Invalidate all visible windows. Then report back on the callback once all windows have
- * redrawn.
+ * Invalidate all visible windows on a given display, and report back on the callback when all
+ * windows have redrawn.
+ *
+ * @param callback reporting callback to be called when all windows have redrawn.
+ * @param timeout calls the callback anyway after the timeout.
+ * @param displayId waits for the windows on the given display, INVALID_DISPLAY to wait for all
+ * windows on all displays.
*/
- public abstract void waitForAllWindowsDrawn(Runnable callback, long timeout);
+ public abstract void waitForAllWindowsDrawn(Runnable callback, long timeout, int displayId);
/**
* Overrides the display size.
@@ -502,6 +507,20 @@ public abstract class WindowManagerInternal {
public abstract boolean shouldShowIme(int displayId);
/**
+ * Show IME on imeTargetWindow once IME has finished layout.
+ *
+ * @param imeTargetWindowToken token of the (IME target) window on which IME should be shown.
+ */
+ public abstract void showImePostLayout(IBinder imeTargetWindowToken);
+
+ /**
+ * Hide IME using imeTargetWindow when requested.
+ *
+ * @param displayId on which IME is shown
+ */
+ public abstract void hideIme(int displayId);
+
+ /**
* Tell window manager about a package that should not be running with high refresh rate
* setting until removeNonHighRefreshRatePackage is called for the same package.
*
@@ -519,4 +538,10 @@ public abstract class WindowManagerInternal {
*/
public abstract boolean isTouchableDisplay(int displayId);
+ /**
+ * Returns the info associated with the input token used to determine if a key should be
+ * intercepted. This info can be accessed without holding the global wm lock.
+ */
+ public abstract @Nullable KeyInterceptionInfo
+ getKeyInterceptionInfoFromToken(IBinder inputToken);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 350ec754a6bc..5fbad4a7a808 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.Manifest.permission.ACCESS_SURFACE_FLINGER;
import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.MANAGE_APP_TOKENS;
@@ -81,27 +82,27 @@ import static com.android.server.LockGuard.INDEX_WINDOW;
import static com.android.server.LockGuard.installLock;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+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_KEEP_SCREEN_ON;
+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_DEBUG_STARTING_WINDOW;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT;
+import static com.android.server.wm.ProtoLogGroup.WM_ERROR;
+import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEEP_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_STACK_CRAWLS;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_VERBOSE_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_KEEP_SCREEN_ON;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerServiceDumpProto.DISPLAY_FROZEN;
@@ -236,6 +237,7 @@ import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;
import android.view.WindowContentFrameStats;
+import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.RemoveContentMode;
@@ -249,6 +251,7 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.os.IResultReceiver;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
+import com.android.internal.policy.KeyInterceptionInfo;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.LatencyTracker;
@@ -266,6 +269,8 @@ import com.android.server.input.InputManagerService;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
import com.android.server.power.ShutdownThread;
+import com.android.server.protolog.ProtoLogImpl;
+import com.android.server.protolog.common.ProtoLog;
import com.android.server.utils.PriorityDump;
import java.io.BufferedWriter;
@@ -285,8 +290,11 @@ import java.net.Socket;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Date;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -411,6 +419,14 @@ public class WindowManagerService extends IWindowManager.Stub
int mVr2dDisplayId = INVALID_DISPLAY;
boolean mVrModeEnabled = false;
+ /**
+ * Tracks a map of input tokens to info that is used to decide whether to intercept
+ * a key event.
+ */
+ final Map<IBinder, KeyInterceptionInfo> mKeyInterceptionInfoForToken =
+ Collections.synchronizedMap(new ArrayMap<>());
+
+
private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
@Override
public void onVrStateChanged(boolean enabled) {
@@ -507,7 +523,10 @@ public class WindowManagerService extends IWindowManager.Stub
final ArraySet<Session> mSessions = new ArraySet<>();
/** Mapping from an IWindow IBinder to the server's Window object. */
- final WindowHashMap mWindowMap = new WindowHashMap();
+ final HashMap<IBinder, WindowState> mWindowMap = new HashMap<>();
+
+ /** Mapping from an InputWindowHandle token to the server's Window object. */
+ final HashMap<IBinder, WindowState> mInputToWindowMap = new HashMap<>();
/** Global service lock used by the package the owns this service. */
final WindowManagerGlobalLock mGlobalLock;
@@ -516,7 +535,7 @@ public class WindowManagerService extends IWindowManager.Stub
* List of app window tokens that are waiting for replacing windows. If the
* replacement doesn't come in time the stale windows needs to be disposed of.
*/
- final ArrayList<AppWindowToken> mWindowReplacementTimeouts = new ArrayList<>();
+ final ArrayList<ActivityRecord> mWindowReplacementTimeouts = new ArrayList<>();
/**
* Windows that are being resized. Used so we can tell the client about
@@ -558,13 +577,10 @@ public class WindowManagerService extends IWindowManager.Stub
final ArrayList<WindowState> mForceRemoves = new ArrayList<>();
/**
- * Windows that clients are waiting to have drawn.
+ * The callbacks to make when the windows all have been drawn for a given
+ * {@link WindowContainer}.
*/
- ArrayList<WindowState> mWaitingForDrawn = new ArrayList<>();
- /**
- * And the callback to make when they've all been drawn.
- */
- Runnable mWaitingForDrawnCallback;
+ final HashMap<WindowContainer, Runnable> mWaitingForDrawnCallbacks = new HashMap<>();
/** List of window currently causing non-system overlay windows to be hidden. */
private ArrayList<WindowState> mHidingNonSystemOverlayWindows = new ArrayList<>();
@@ -868,7 +884,7 @@ public class WindowManagerService extends IWindowManager.Stub
/** The display that the rotation animation is applying to. */
private int mFrozenDisplayId;
- /** Skip repeated AppWindowTokens initialization. Note that AppWindowsToken's version of this
+ /** Skip repeated ActivityRecords initialization. Note that AppWindowsToken's version of this
* is a long initialized to Long.MIN_VALUE so that it doesn't match this value on startup. */
int mTransactionSequence;
@@ -965,6 +981,7 @@ public class WindowManagerService extends IWindowManager.Stub
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
+
/** Listener to notify activity manager about app transitions. */
final WindowManagerInternal.AppTransitionListener mActivityManagerAppTransitionNotifier
= new WindowManagerInternal.AppTransitionListener() {
@@ -977,11 +994,17 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void onAppTransitionFinishedLocked(IBinder token) {
mAtmInternal.notifyAppTransitionFinished();
- final AppWindowToken atoken = mRoot.getAppWindowToken(token);
+ final ActivityRecord atoken = mRoot.getActivityRecord(token);
if (atoken == null) {
return;
}
- if (atoken.mLaunchTaskBehind) {
+
+ // While running a recents animation, this will get called early because we show the
+ // recents animation target activity immediately when the animation starts. Defer the
+ // mLaunchTaskBehind updates until recents animation finishes.
+ final boolean isRecentsAnimationTarget = getRecentsAnimationController() != null
+ && getRecentsAnimationController().isTargetApp(atoken);
+ if (atoken.mLaunchTaskBehind && !isRecentsAnimationTarget) {
try {
mActivityTaskManager.notifyLaunchTaskBehindComplete(atoken.token);
} catch (RemoteException e) {
@@ -989,20 +1012,13 @@ public class WindowManagerService extends IWindowManager.Stub
atoken.mLaunchTaskBehind = false;
} else {
atoken.updateReportedVisibilityLocked();
- if (atoken.mEnteringAnimation) {
- if (getRecentsAnimationController() != null
- && getRecentsAnimationController().isTargetApp(atoken)) {
- // Currently running a recents animation, this will get called early because
- // we show the recents animation target activity immediately when the
- // animation starts. In this case, we should defer sending the finished
- // callback until the animation successfully finishes
- return;
- } else {
- atoken.mEnteringAnimation = false;
- try {
- mActivityTaskManager.notifyEnterAnimationComplete(atoken.token);
- } catch (RemoteException e) {
- }
+ // We should also defer sending the finished callback until the recents animation
+ // successfully finishes.
+ if (atoken.mEnteringAnimation && !isRecentsAnimationTarget) {
+ atoken.mEnteringAnimation = false;
+ try {
+ mActivityTaskManager.notifyEnterAnimationComplete(atoken.token);
+ } catch (RemoteException e) {
}
}
}
@@ -1261,14 +1277,7 @@ public class WindowManagerService extends IWindowManager.Stub
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
-
- openSurfaceTransaction();
- try {
- createWatermarkInTransaction();
- } finally {
- closeSurfaceTransaction("createWatermarkInTransaction");
- }
-
+ createWatermark();
showEmulatorDisplayOverlayIfNeeded();
}
@@ -1285,7 +1294,7 @@ public class WindowManagerService extends IWindowManager.Stub
// The window manager only throws security exceptions, so let's
// log all others.
if (!(e instanceof SecurityException)) {
- Slog.wtf(TAG_WM, "Window Manager Crash", e);
+ ProtoLog.wtf(WM_ERROR, "Window Manager Crash %s", e);
}
throw e;
}
@@ -1325,42 +1334,45 @@ public class WindowManagerService extends IWindowManager.Stub
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
if (displayContent == null) {
- Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
- + displayId + ". Aborting.");
+ ProtoLog.w(WM_ERROR, "Attempted to add window to a display that does "
+ + "not exist: %d. Aborting.", displayId);
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
if (!displayContent.hasAccess(session.mUid)) {
- Slog.w(TAG_WM, "Attempted to add window to a display for which the application "
- + "does not have access: " + displayId + ". Aborting.");
+ ProtoLog.w(WM_ERROR,
+ "Attempted to add window to a display for which the application "
+ + "does not have access: %d. Aborting.", displayId);
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
if (mWindowMap.containsKey(client.asBinder())) {
- Slog.w(TAG_WM, "Window " + client + " is already added");
+ ProtoLog.w(WM_ERROR, "Window %s is already added", client);
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
parentWindow = windowForClientLocked(null, attrs.token, false);
if (parentWindow == null) {
- Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
- + attrs.token + ". Aborting.");
+ ProtoLog.w(WM_ERROR, "Attempted to add window with token that is not a window: "
+ + "%s. Aborting.", attrs.token);
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
- Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
- + attrs.token + ". Aborting.");
+ ProtoLog.w(WM_ERROR, "Attempted to add window with token that is a sub-window: "
+ + "%s. Aborting.", attrs.token);
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
}
if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {
- Slog.w(TAG_WM, "Attempted to add private presentation window to a non-private display. Aborting.");
+ ProtoLog.w(WM_ERROR,
+ "Attempted to add private presentation window to a non-private display. "
+ + "Aborting.");
return WindowManagerGlobal.ADD_PERMISSION_DENIED;
}
- AppWindowToken atoken = null;
+ ActivityRecord activity = null;
final boolean hasParent = parentWindow != null;
// Use existing parent window token for child windows since they go in the same token
// as there parent window so we can apply the same policy on them.
@@ -1374,46 +1386,48 @@ public class WindowManagerService extends IWindowManager.Stub
if (token == null) {
if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
- Slog.w(TAG_WM, "Attempted to add application window with unknown token "
- + attrs.token + ". Aborting.");
+ ProtoLog.w(WM_ERROR, "Attempted to add application window with unknown token "
+ + "%s. Aborting.", attrs.token);
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_INPUT_METHOD) {
- Slog.w(TAG_WM, "Attempted to add input method window with unknown token "
- + attrs.token + ". Aborting.");
+ ProtoLog.w(WM_ERROR, "Attempted to add input method window with unknown token "
+ + "%s. Aborting.", attrs.token);
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_VOICE_INTERACTION) {
- Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token "
- + attrs.token + ". Aborting.");
+ ProtoLog.w(WM_ERROR,
+ "Attempted to add voice interaction window with unknown token "
+ + "%s. Aborting.", attrs.token);
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_WALLPAPER) {
- Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token "
- + attrs.token + ". Aborting.");
+ ProtoLog.w(WM_ERROR, "Attempted to add wallpaper window with unknown token "
+ + "%s. Aborting.", attrs.token);
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_DREAM) {
- Slog.w(TAG_WM, "Attempted to add Dream window with unknown token "
- + attrs.token + ". Aborting.");
+ ProtoLog.w(WM_ERROR, "Attempted to add Dream window with unknown token "
+ + "%s. Aborting.", attrs.token);
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_QS_DIALOG) {
- Slog.w(TAG_WM, "Attempted to add QS dialog window with unknown token "
- + attrs.token + ". Aborting.");
+ ProtoLog.w(WM_ERROR, "Attempted to add QS dialog window with unknown token "
+ + "%s. Aborting.", attrs.token);
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
- Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with unknown token "
- + attrs.token + ". Aborting.");
+ ProtoLog.w(WM_ERROR,
+ "Attempted to add Accessibility overlay window with unknown token "
+ + "%s. Aborting.", attrs.token);
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
if (type == TYPE_TOAST) {
// Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,
parentWindow)) {
- Slog.w(TAG_WM, "Attempted to add a toast window with unknown token "
- + attrs.token + ". Aborting.");
+ ProtoLog.w(WM_ERROR, "Attempted to add a toast window with unknown token "
+ + "%s. Aborting.", attrs.token);
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
}
@@ -1422,49 +1436,52 @@ public class WindowManagerService extends IWindowManager.Stub
(attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
- } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
- atoken = token.asAppWindowToken();
- if (atoken == null) {
- Slog.w(TAG_WM, "Attempted to add window with non-application token "
- + token + ". Aborting.");
+ } else if (rootType >= FIRST_APPLICATION_WINDOW
+ && rootType <= LAST_APPLICATION_WINDOW) {
+ activity = token.asActivityRecord();
+ if (activity == null) {
+ ProtoLog.w(WM_ERROR, "Attempted to add window with non-application token "
+ + ".%s Aborting.", token);
return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
- } else if (atoken.removed) {
- Slog.w(TAG_WM, "Attempted to add window with exiting application token "
- + token + ". Aborting.");
+ } else if (activity.removed) {
+ ProtoLog.w(WM_ERROR, "Attempted to add window with exiting application token "
+ + ".%s Aborting.", token);
return WindowManagerGlobal.ADD_APP_EXITING;
- } else if (type == TYPE_APPLICATION_STARTING && atoken.startingWindow != null) {
- Slog.w(TAG_WM, "Attempted to add starting window to token with already existing"
- + " starting window");
+ } else if (type == TYPE_APPLICATION_STARTING && activity.startingWindow != null) {
+ ProtoLog.w(WM_ERROR,
+ "Attempted to add starting window to token with already existing"
+ + " starting window");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
} else if (rootType == TYPE_INPUT_METHOD) {
if (token.windowType != TYPE_INPUT_METHOD) {
- Slog.w(TAG_WM, "Attempted to add input method window with bad token "
- + attrs.token + ". Aborting.");
- return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
+ ProtoLog.w(WM_ERROR, "Attempted to add input method window with bad token "
+ + "%s. Aborting.", attrs.token);
+ return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (rootType == TYPE_VOICE_INTERACTION) {
if (token.windowType != TYPE_VOICE_INTERACTION) {
- Slog.w(TAG_WM, "Attempted to add voice interaction window with bad token "
- + attrs.token + ". Aborting.");
- return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
+ ProtoLog.w(WM_ERROR, "Attempted to add voice interaction window with bad token "
+ + "%s. Aborting.", attrs.token);
+ return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (rootType == TYPE_WALLPAPER) {
if (token.windowType != TYPE_WALLPAPER) {
- Slog.w(TAG_WM, "Attempted to add wallpaper window with bad token "
- + attrs.token + ". Aborting.");
- return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
+ ProtoLog.w(WM_ERROR, "Attempted to add wallpaper window with bad token "
+ + "%s. Aborting.", attrs.token);
+ return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (rootType == TYPE_DREAM) {
if (token.windowType != TYPE_DREAM) {
- Slog.w(TAG_WM, "Attempted to add Dream window with bad token "
- + attrs.token + ". Aborting.");
- return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
+ ProtoLog.w(WM_ERROR, "Attempted to add Dream window with bad token "
+ + "%s. Aborting.", attrs.token);
+ return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) {
- Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with bad token "
- + attrs.token + ". Aborting.");
+ ProtoLog.w(WM_ERROR,
+ "Attempted to add Accessibility overlay window with bad token "
+ + "%s. Aborting.", attrs.token);
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (type == TYPE_TOAST) {
@@ -1472,18 +1489,19 @@ public class WindowManagerService extends IWindowManager.Stub
addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName,
callingUid, parentWindow);
if (addToastWindowRequiresToken && token.windowType != TYPE_TOAST) {
- Slog.w(TAG_WM, "Attempted to add a toast window with bad token "
- + attrs.token + ". Aborting.");
+ ProtoLog.w(WM_ERROR, "Attempted to add a toast window with bad token "
+ + "%s. Aborting.", attrs.token);
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (type == TYPE_QS_DIALOG) {
if (token.windowType != TYPE_QS_DIALOG) {
- Slog.w(TAG_WM, "Attempted to add QS dialog window with bad token "
- + attrs.token + ". Aborting.");
+ ProtoLog.w(WM_ERROR, "Attempted to add QS dialog window with bad token "
+ + "%s. Aborting.", attrs.token);
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
- } else if (token.asAppWindowToken() != null) {
- Slog.w(TAG_WM, "Non-null appWindowToken for system window of rootType=" + rootType);
+ } else if (token.asActivityRecord() != null) {
+ ProtoLog.w(WM_ERROR, "Non-null activity for system window of rootType=%d",
+ rootType);
// It is not valid to use an app token with other system types; we will
// instead make a new token for it (as if null had been passed in for the token).
attrs.token = null;
@@ -1497,13 +1515,13 @@ public class WindowManagerService extends IWindowManager.Stub
if (win.mDeathRecipient == null) {
// Client has apparently died, so there is no reason to
// continue.
- Slog.w(TAG_WM, "Adding window client " + client.asBinder()
- + " that is dead, aborting.");
+ ProtoLog.w(WM_ERROR, "Adding window client %s"
+ + " that is dead, aborting.", client.asBinder());
return WindowManagerGlobal.ADD_APP_EXITING;
}
if (win.getDisplayContent() == null) {
- Slog.w(TAG_WM, "Adding window to Display that has been removed.");
+ ProtoLog.w(WM_ERROR, "Adding window to Display that has been removed.");
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
@@ -1535,7 +1553,7 @@ public class WindowManagerService extends IWindowManager.Stub
// schedule hiding all of its toast windows.
if (type == TYPE_TOAST) {
if (!displayContent.canAddToastWindowForUid(callingUid)) {
- Slog.w(TAG_WM, "Adding more than one toast window for UID at a time.");
+ ProtoLog.w(WM_ERROR, "Adding more than one toast window for UID at a time.");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
// Make sure this happens before we moved focus as one can make the
@@ -1568,7 +1586,6 @@ public class WindowManagerService extends IWindowManager.Stub
win.attach();
mWindowMap.put(client.asBinder(), win);
-
win.initAppOpsState();
final boolean suspended = mPmInternal.isPackageSuspended(win.getOwningPackage(),
@@ -1578,11 +1595,11 @@ public class WindowManagerService extends IWindowManager.Stub
final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty();
win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);
- final AppWindowToken aToken = token.asAppWindowToken();
- if (type == TYPE_APPLICATION_STARTING && aToken != null) {
- aToken.startingWindow = win;
- if (DEBUG_STARTING_WINDOW) Slog.v (TAG_WM, "addWindow: " + aToken
- + " startingWindow=" + win);
+ final ActivityRecord tokenActivity = token.asActivityRecord();
+ if (type == TYPE_APPLICATION_STARTING && tokenActivity != null) {
+ tokenActivity.startingWindow = win;
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s",
+ activity, win);
}
boolean imMayMove = true;
@@ -1599,7 +1616,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (type == TYPE_WALLPAPER) {
displayContent.mWallpaperController.clearLastWallpaperTimeoutTime();
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
- } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
+ } else if ((attrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
} else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) {
// If there is currently a wallpaper being shown, and
@@ -1623,12 +1640,12 @@ public class WindowManagerService extends IWindowManager.Stub
winAnimator.mEnterAnimationPending = true;
winAnimator.mEnteringAnimation = true;
// Check if we need to prepare a transition for replacing window first.
- if (atoken != null && atoken.isVisible()
- && !prepareWindowReplacementTransition(atoken)) {
+ if (activity != null && activity.isVisible()
+ && !prepareWindowReplacementTransition(activity)) {
// If not, check if need to set up a dummy transition during display freeze
// so that the unfreeze wait for the apps to draw. This might be needed if
// the app is relaunching.
- prepareNoneTransitionForRelaunching(atoken);
+ prepareNoneTransitionForRelaunching(activity);
}
final DisplayFrames displayFrames = displayContent.mDisplayFrames;
@@ -1638,10 +1655,10 @@ public class WindowManagerService extends IWindowManager.Stub
displayContent.calculateDisplayCutoutForRotation(displayInfo.rotation));
final Rect taskBounds;
final boolean floatingStack;
- if (atoken != null && atoken.getTask() != null) {
+ if (activity != null && activity.getTask() != null) {
taskBounds = mTmpRect;
- atoken.getTask().getBounds(mTmpRect);
- floatingStack = atoken.getTask().isFloating();
+ tokenActivity.getTask().getBounds(mTmpRect);
+ floatingStack = activity.getTask().isFloating();
} else {
taskBounds = null;
floatingStack = false;
@@ -1650,12 +1667,12 @@ public class WindowManagerService extends IWindowManager.Stub
outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout)) {
res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
}
- outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win));
+ outInsetsState.set(displayContent.getInsetsPolicy().getInsetsForDispatch(win));
if (mInTouchMode) {
res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
}
- if (win.mAppToken == null || !win.mAppToken.isClientHidden()) {
+ if (win.mActivityRecord == null || !win.mActivityRecord.isClientHidden()) {
res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
}
@@ -1684,10 +1701,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
- if (DEBUG || DEBUG_ADD_REMOVE) {
- Slog.v(TAG_WM, "addWindow: New client " + client.asBinder()
- + ": window=" + win + " Callers=" + Debug.getCallers(5));
- }
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s"
+ + ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5));
+
if (win.isVisibleOrAdding() && displayContent.updateOrientation()) {
displayContent.sendNewConfiguration();
@@ -1741,8 +1757,8 @@ public class WindowManagerService extends IWindowManager.Stub
WindowState attachedWindow) {
// Try using the target SDK of the root window
if (attachedWindow != null) {
- return attachedWindow.mAppToken != null
- && attachedWindow.mAppToken.mTargetSdk >= Build.VERSION_CODES.O;
+ return attachedWindow.mActivityRecord != null
+ && attachedWindow.mActivityRecord.mTargetSdk >= Build.VERSION_CODES.O;
} else {
// Otherwise, look at the package
try {
@@ -1766,9 +1782,9 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* Returns true if we're done setting up any transitions.
*/
- private boolean prepareWindowReplacementTransition(AppWindowToken atoken) {
- atoken.clearAllDrawn();
- final WindowState replacedWindow = atoken.getReplacingWindow();
+ private boolean prepareWindowReplacementTransition(ActivityRecord activity) {
+ activity.clearAllDrawn();
+ final WindowState replacedWindow = activity.getReplacingWindow();
if (replacedWindow == null) {
// We expect to already receive a request to remove the old window. If it did not
// happen, let's just simply add a window.
@@ -1779,8 +1795,8 @@ public class WindowManagerService extends IWindowManager.Stub
Rect frame = replacedWindow.getVisibleFrameLw();
// We treat this as if this activity was opening, so we can trigger the app transition
// animation and piggy-back on existing transition animation infrastructure.
- final DisplayContent dc = atoken.getDisplayContent();
- dc.mOpeningApps.add(atoken);
+ final DisplayContent dc = activity.getDisplayContent();
+ dc.mOpeningApps.add(activity);
dc.prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_RELAUNCH, ALWAYS_KEEP_CURRENT,
0 /* flags */, false /* forceOverride */);
dc.mAppTransition.overridePendingAppTransitionClipReveal(frame.left, frame.top,
@@ -1789,14 +1805,14 @@ public class WindowManagerService extends IWindowManager.Stub
return true;
}
- private void prepareNoneTransitionForRelaunching(AppWindowToken atoken) {
+ private void prepareNoneTransitionForRelaunching(ActivityRecord activity) {
// Set up a none-transition and add the app to opening apps, so that the display
// unfreeze wait for the apps to be drawn.
// Note that if the display unfroze already because app unfreeze timed out,
// we don't set up the transition anymore and just let it go.
- final DisplayContent dc = atoken.getDisplayContent();
- if (mDisplayFrozen && !dc.mOpeningApps.contains(atoken) && atoken.isRelaunching()) {
- dc.mOpeningApps.add(atoken);
+ final DisplayContent dc = activity.getDisplayContent();
+ if (mDisplayFrozen && !dc.mOpeningApps.contains(activity) && activity.isRelaunching()) {
+ dc.mOpeningApps.add(activity);
dc.prepareAppTransition(WindowManager.TRANSIT_NONE, !ALWAYS_KEEP_CURRENT, 0 /* flags */,
false /* forceOverride */);
dc.executeAppTransition();
@@ -1850,7 +1866,7 @@ public class WindowManagerService extends IWindowManager.Stub
* forgetting to add the wiring when a new parent of WindowState is added.
*/
void postWindowRemoveCleanupLocked(WindowState win) {
- if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "postWindowRemoveCleanupLocked: " + win);
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE, "postWindowRemoveCleanupLocked: %s", win);
mWindowMap.remove(win.mClient.asBinder());
final DisplayContent dc = win.getDisplayContent();
@@ -1865,7 +1881,7 @@ public class WindowManagerService extends IWindowManager.Stub
mResizingWindows.remove(win);
updateNonSystemOverlayWindowsVisibilityIfNeeded(win, false /* surfaceShown */);
mWindowsChanged = true;
- if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Final remove of window: " + win);
+ ProtoLog.v(WM_DEBUG_WINDOW_MOVEMENT, "Final remove of window: %s", win);
final DisplayContent displayContent = win.getDisplayContent();
if (displayContent.mInputMethodWindow == win) {
@@ -1873,26 +1889,26 @@ public class WindowManagerService extends IWindowManager.Stub
}
final WindowToken token = win.mToken;
- final AppWindowToken atoken = win.mAppToken;
- if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Removing " + win + " from " + token);
+ final ActivityRecord activity = win.mActivityRecord;
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Removing %s from %s", win, token);
// Window will already be removed from token before this post clean-up method is called.
if (token.isEmpty()) {
if (!token.mPersistOnEmpty) {
token.removeImmediately();
- } else if (atoken != null) {
- // TODO: Should this be moved into AppWindowToken.removeWindow? Might go away after
+ } else if (activity != null) {
+ // TODO: Should this be moved into ActivityRecord.removeWindow? Might go away after
// re-factor.
- atoken.firstWindowDrawn = false;
- atoken.clearAllDrawn();
- final TaskStack stack = atoken.getStack();
+ activity.firstWindowDrawn = false;
+ activity.clearAllDrawn();
+ final TaskStack stack = activity.getStack();
if (stack != null) {
- stack.mExitingAppTokens.remove(atoken);
+ stack.mExitingActivities.remove(activity);
}
}
}
- if (atoken != null) {
- atoken.postWindowRemoveStartingWindowCleanup(win);
+ if (activity != null) {
+ activity.postWindowRemoveStartingWindowCleanup(win);
}
if (win.mAttrs.type == TYPE_WALLPAPER) {
@@ -1905,8 +1921,8 @@ public class WindowManagerService extends IWindowManager.Stub
if (dc != null && !mWindowPlacerLocked.isInLayout()) {
dc.assignWindowLayers(true /* setLayoutNeeded */);
mWindowPlacerLocked.performSurfacePlacement();
- if (win.mAppToken != null) {
- win.mAppToken.updateReportedVisibilityLocked();
+ if (win.mActivityRecord != null) {
+ win.mActivityRecord.updateReportedVisibilityLocked();
}
}
@@ -1934,11 +1950,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- static void logSurface(SurfaceControl s, String title, String msg) {
- String str = " SURFACE " + s + ": " + msg + " / " + title;
- Slog.i(TAG_WM, str);
- }
-
static void logWithStack(String tag, String s) {
RuntimeException e = null;
if (SHOW_STACK_CRAWLS) {
@@ -1953,8 +1964,8 @@ public class WindowManagerService extends IWindowManager.Stub
try {
synchronized (mGlobalLock) {
WindowState w = windowForClientLocked(session, client, false);
- if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
- "transparentRegionHint=" + region, false);
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE transparentRegionHint=%s: %s",
+ region, w);
if ((w != null) && w.mHasSurface) {
w.mWinAnimator.setTransparentRegionHintLocked(region);
@@ -2118,9 +2129,9 @@ public class WindowManagerService extends IWindowManager.Stub
| WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED)) != 0) {
win.mLayoutNeeded = true;
}
- if (win.mAppToken != null && ((flagChanges & FLAG_SHOW_WHEN_LOCKED) != 0
+ if (win.mActivityRecord != null && ((flagChanges & FLAG_SHOW_WHEN_LOCKED) != 0
|| (flagChanges & FLAG_DISMISS_KEYGUARD) != 0)) {
- win.mAppToken.checkKeyguardFlagsChanged();
+ win.mActivityRecord.checkKeyguardFlagsChanged();
}
if (((attrChanges & LayoutParams.ACCESSIBILITY_TITLE_CHANGED) != 0)
&& (mAccessibilityController != null)) {
@@ -2178,12 +2189,10 @@ public class WindowManagerService extends IWindowManager.Stub
win.mInRelayout = true;
win.mViewVisibility = viewVisibility;
- if (DEBUG_SCREEN_ON) {
- RuntimeException stack = new RuntimeException();
- stack.fillInStackTrace();
- Slog.i(TAG_WM, "Relayout " + win + ": oldVis=" + oldVisibility
- + " newVis=" + viewVisibility, stack);
- }
+ ProtoLog.i(WM_DEBUG_SCREEN_ON,
+ "Relayout %s: oldVis=%d newVis=%d. %s", win, oldVisibility,
+ viewVisibility, new RuntimeException().fillInStackTrace());
+
win.setDisplayLayoutNeeded();
win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
@@ -2191,8 +2200,8 @@ public class WindowManagerService extends IWindowManager.Stub
// We should only relayout if the view is visible, it is a starting window, or the
// associated appToken is not hidden.
final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
- (win.mAppToken == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
- || !win.mAppToken.isClientHidden());
+ (win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
+ || !win.mActivityRecord.isClientHidden());
// If we are not currently running the exit animation, we need to see about starting
// one.
@@ -2228,9 +2237,9 @@ public class WindowManagerService extends IWindowManager.Stub
} catch (Exception e) {
displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
- Slog.w(TAG_WM, "Exception thrown when creating surface for client "
- + client + " (" + win.mAttrs.getTitle() + ")",
- e);
+ ProtoLog.w(WM_ERROR,
+ "Exception thrown when creating surface for client %s (%s). %s",
+ client, win.mAttrs.getTitle(), e);
Binder.restoreCallingIdentity(origId);
return 0;
}
@@ -2297,8 +2306,8 @@ public class WindowManagerService extends IWindowManager.Stub
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
- if (win.mAppToken != null) {
- displayContent.mUnknownAppVisibilityController.notifyRelayouted(win.mAppToken);
+ if (win.mActivityRecord != null) {
+ displayContent.mUnknownAppVisibilityController.notifyRelayouted(win.mActivityRecord);
}
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: updateOrientation");
@@ -2310,8 +2319,8 @@ public class WindowManagerService extends IWindowManager.Stub
displayContent.mWallpaperController.updateWallpaperOffset(
win, displayInfo.logicalWidth, displayInfo.logicalHeight, false);
}
- if (win.mAppToken != null) {
- win.mAppToken.updateReportedVisibilityLocked();
+ if (win.mActivityRecord != null) {
+ win.mActivityRecord.updateReportedVisibilityLocked();
}
if (winAnimator.mReportSurfaceResized) {
winAnimator.mReportSurfaceResized = false;
@@ -2351,24 +2360,24 @@ public class WindowManagerService extends IWindowManager.Stub
outStableInsets, outOutsets);
outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
- outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win));
+ outInsetsState.set(displayContent.getInsetsPolicy().getInsetsForDispatch(win));
if (DEBUG) {
Slog.v(TAG_WM, "Relayout given client " + client.asBinder()
- + ", requestedWidth=" + requestedWidth
- + ", requestedHeight=" + requestedHeight
- + ", viewVisibility=" + viewVisibility
- + "\nRelayout returning frame=" + outFrame
- + ", surface=" + outSurfaceControl);
+ + ", requestedWidth=" + requestedWidth
+ + ", requestedHeight=" + requestedHeight
+ + ", viewVisibility=" + viewVisibility
+ + "\nRelayout returning frame=" + outFrame
+ + ", surface=" + outSurfaceControl);
}
- if (DEBUG || DEBUG_FOCUS) {
- Slog.v(TAG_WM, "Relayout of " + win + ": focusMayChange=" + focusMayChange);
- }
+ ProtoLog.v(WM_DEBUG_FOCUS, "Relayout of %s: focusMayChange=%b",
+ win, focusMayChange);
result |= mInTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;
if (DEBUG_LAYOUT) {
- Slog.v(TAG_WM, "Relayout complete " + win + ": outFrame=" + outFrame.toShortString());
+ Slog.v(TAG_WM,
+ "Relayout complete " + win + ": outFrame=" + outFrame.toShortString());
}
win.mInRelayout = false;
@@ -2409,8 +2418,8 @@ public class WindowManagerService extends IWindowManager.Stub
if (displayContent.mInputMethodWindow == win) {
displayContent.setInputMethodWindowLocked(null);
}
- boolean stopped = win.mAppToken != null ? win.mAppToken.mAppStopped : true;
- // We set mDestroying=true so AppWindowToken#notifyAppStopped in-to destroy surfaces
+ boolean stopped = win.mActivityRecord != null ? win.mActivityRecord.mAppStopped : true;
+ // We set mDestroying=true so ActivityRecord#notifyAppStopped in-to destroy surfaces
// will later actually destroy the surface if we do not do so here. Normally we leave
// this to the exit animation.
win.mDestroying = true;
@@ -2446,11 +2455,12 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (surfaceController != null) {
surfaceController.getSurfaceControl(outSurfaceControl);
- if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " OUT SURFACE " + outSurfaceControl + ": copied");
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "OUT SURFACE %s: copied", outSurfaceControl);
+
} else {
// For some reason there isn't a surface. Clear the
// caller's object so they see the same state.
- Slog.w(TAG_WM, "Failed to create surface control for " + win);
+ ProtoLog.w(WM_ERROR, "Failed to create surface control for %s", win);
outSurfaceControl.release();
}
@@ -2479,8 +2489,8 @@ public class WindowManagerService extends IWindowManager.Stub
try {
synchronized (mGlobalLock) {
WindowState win = windowForClientLocked(session, client, false);
- if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "finishDrawingWindow: " + win + " mDrawState="
- + (win != null ? win.mWinAnimator.drawStateToString() : "null"));
+ ProtoLog.d(WM_DEBUG_ADD_REMOVE, "finishDrawingWindow: %s mDrawState=%s",
+ win, (win != null ? win.mWinAnimator.drawStateToString() : "null"));
if (win != null && win.mWinAnimator.finishDrawingLocked(postDrawTransaction)) {
if ((win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
win.getDisplayContent().pendingLayoutChanges |=
@@ -2505,9 +2515,8 @@ public class WindowManagerService extends IWindowManager.Stub
== PackageManager.PERMISSION_GRANTED) {
return true;
}
- final String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid() + " requires " + permission;
- Slog.w(TAG_WM, msg);
+ ProtoLog.w(WM_ERROR, "Permission Denial: %s from pid=%d, uid=%d requires %s",
+ func, Binder.getCallingPid(), Binder.getCallingUid(), permission);
return false;
}
@@ -2520,16 +2529,16 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mGlobalLock) {
final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */);
if (dc == null) {
- Slog.w(TAG_WM, "addWindowToken: Attempted to add token: " + binder
- + " for non-exiting displayId=" + displayId);
+ ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add token: %s"
+ + " for non-exiting displayId=%d", binder, displayId);
return;
}
WindowToken token = dc.getWindowToken(binder);
if (token != null) {
- Slog.w(TAG_WM, "addWindowToken: Attempted to add binder token: " + binder
- + " for already created window token: " + token
- + " displayId=" + displayId);
+ ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add binder token: %s"
+ + " for already created window token: %s"
+ + " displayId=%d", binder, token, displayId);
return;
}
if (type == TYPE_WALLPAPER) {
@@ -2552,15 +2561,16 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mGlobalLock) {
final DisplayContent dc = mRoot.getDisplayContent(displayId);
if (dc == null) {
- Slog.w(TAG_WM, "removeWindowToken: Attempted to remove token: " + binder
- + " for non-exiting displayId=" + displayId);
+ ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s"
+ + " for non-exiting displayId=%d", binder, displayId);
return;
}
final WindowToken token = dc.removeWindowToken(binder);
if (token == null) {
- Slog.w(TAG_WM,
- "removeWindowToken: Attempted to remove non-existing token: " + binder);
+ ProtoLog.w(WM_ERROR,
+ "removeWindowToken: Attempted to remove non-existing token: %s",
+ binder);
return;
}
@@ -2646,11 +2656,11 @@ public class WindowManagerService extends IWindowManager.Stub
void initializeRecentsAnimation(int targetActivityType,
IRecentsAnimationRunner recentsAnimationRunner,
RecentsAnimationController.RecentsAnimationCallbacks callbacks, int displayId,
- SparseBooleanArray recentTaskIds) {
+ SparseBooleanArray recentTaskIds, ActivityRecord targetActivity) {
mRecentsAnimationController = new RecentsAnimationController(this, recentsAnimationRunner,
callbacks, displayId);
mRoot.getDisplayContent(displayId).mAppTransition.updateBooster();
- mRecentsAnimationController.initialize(targetActivityType, recentTaskIds);
+ mRecentsAnimationController.initialize(targetActivityType, recentTaskIds, targetActivity);
}
@VisibleForTesting
@@ -2694,7 +2704,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
void setWindowOpaqueLocked(IBinder token, boolean isOpaque) {
- final AppWindowToken wtoken = mRoot.getAppWindowToken(token);
+ final ActivityRecord wtoken = mRoot.getActivityRecord(token);
if (wtoken != null) {
wtoken.setMainWindowOpaque(isOpaque);
}
@@ -3216,14 +3226,11 @@ public class WindowManagerService extends IWindowManager.Stub
public void enableScreenAfterBoot() {
synchronized (mGlobalLock) {
- if (DEBUG_BOOT) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.i(TAG_WM, "enableScreenAfterBoot: mDisplayEnabled=" + mDisplayEnabled
- + " mForceDisplayEnabled=" + mForceDisplayEnabled
- + " mShowingBootMessages=" + mShowingBootMessages
- + " mSystemBooted=" + mSystemBooted, here);
- }
+ ProtoLog.i(WM_DEBUG_BOOT, "enableScreenAfterBoot: mDisplayEnabled=%b "
+ + "mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. "
+ + "%s",
+ mDisplayEnabled, mForceDisplayEnabled, mShowingBootMessages, mSystemBooted,
+ new RuntimeException("here").fillInStackTrace());
if (mSystemBooted) {
return;
}
@@ -3247,14 +3254,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
void enableScreenIfNeededLocked() {
- if (DEBUG_BOOT) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.i(TAG_WM, "enableScreenIfNeededLocked: mDisplayEnabled=" + mDisplayEnabled
- + " mForceDisplayEnabled=" + mForceDisplayEnabled
- + " mShowingBootMessages=" + mShowingBootMessages
- + " mSystemBooted=" + mSystemBooted, here);
- }
+ ProtoLog.i(WM_DEBUG_BOOT, "enableScreenIfNeededLocked: mDisplayEnabled=%b "
+ + "mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. "
+ + "%s",
+ mDisplayEnabled, mForceDisplayEnabled, mShowingBootMessages, mSystemBooted,
+ new RuntimeException("here").fillInStackTrace());
if (mDisplayEnabled) {
return;
}
@@ -3269,7 +3273,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (mDisplayEnabled) {
return;
}
- Slog.w(TAG_WM, "***** BOOT TIMEOUT: forcing display enabled");
+ ProtoLog.w(WM_ERROR, "***** BOOT TIMEOUT: forcing display enabled");
mForceDisplayEnabled = true;
}
performEnableScreen();
@@ -3284,11 +3288,10 @@ public class WindowManagerService extends IWindowManager.Stub
private void performEnableScreen() {
synchronized (mGlobalLock) {
- if (DEBUG_BOOT) Slog.i(TAG_WM, "performEnableScreen: mDisplayEnabled=" + mDisplayEnabled
- + " mForceDisplayEnabled=" + mForceDisplayEnabled
- + " mShowingBootMessages=" + mShowingBootMessages
- + " mSystemBooted=" + mSystemBooted
- + " mOnlyCore=" + mOnlyCore,
+ ProtoLog.i(WM_DEBUG_BOOT, "performEnableScreen: mDisplayEnabled=%b"
+ + " mForceDisplayEnabled=%b" + " mShowingBootMessages=%b"
+ + " mSystemBooted=%b mOnlyCore=%b. %s", mDisplayEnabled,
+ mForceDisplayEnabled, mShowingBootMessages, mSystemBooted, mOnlyCore,
new RuntimeException("here").fillInStackTrace());
if (mDisplayEnabled) {
return;
@@ -3302,10 +3305,12 @@ public class WindowManagerService extends IWindowManager.Stub
}
// Don't enable the screen until all existing windows have been drawn.
- if (!mForceDisplayEnabled
- // TODO(multidisplay): Expand to all displays?
- && getDefaultDisplayContentLocked().checkWaitingForWindows()) {
- return;
+ if (!mForceDisplayEnabled) {
+ for (int i = mRoot.getChildCount() - 1; i >= 0; i--) {
+ if (mRoot.getChildAt(i).shouldWaitForSystemDecorWindowsOnBoot()) {
+ return;
+ }
+ }
}
if (!mBootAnimationStopped) {
@@ -3318,14 +3323,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (!mForceDisplayEnabled && !checkBootAnimationCompleteLocked()) {
- if (DEBUG_BOOT) Slog.i(TAG_WM, "performEnableScreen: Waiting for anim complete");
+ ProtoLog.i(WM_DEBUG_BOOT, "performEnableScreen: Waiting for anim complete");
return;
}
try {
IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
if (surfaceFlinger != null) {
- Slog.i(TAG_WM, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");
+ ProtoLog.i(WM_ERROR, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");
Parcel data = Parcel.obtain();
data.writeInterfaceToken("android.ui.ISurfaceComposer");
surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED
@@ -3333,13 +3338,13 @@ public class WindowManagerService extends IWindowManager.Stub
data.recycle();
}
} catch (RemoteException ex) {
- Slog.e(TAG_WM, "Boot completed: SurfaceFlinger is dead!");
+ ProtoLog.e(WM_ERROR, "Boot completed: SurfaceFlinger is dead!");
}
EventLog.writeEvent(EventLogTags.WM_BOOT_ANIMATION_DONE, SystemClock.uptimeMillis());
Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
mDisplayEnabled = true;
- if (DEBUG_SCREEN_ON || DEBUG_BOOT) Slog.i(TAG_WM, "******************** ENABLING SCREEN!");
+ ProtoLog.i(WM_DEBUG_SCREEN_ON, "******************** ENABLING SCREEN!");
// Enable input dispatch.
mInputManagerCallback.setEventDispatchingLw(mEventDispatchingEnabled);
@@ -3361,24 +3366,21 @@ public class WindowManagerService extends IWindowManager.Stub
mH.removeMessages(H.CHECK_IF_BOOT_ANIMATION_FINISHED);
mH.sendEmptyMessageDelayed(H.CHECK_IF_BOOT_ANIMATION_FINISHED,
BOOT_ANIMATION_POLL_INTERVAL);
- if (DEBUG_BOOT) Slog.i(TAG_WM, "checkBootAnimationComplete: Waiting for anim complete");
+ ProtoLog.i(WM_DEBUG_BOOT, "checkBootAnimationComplete: Waiting for anim complete");
return false;
}
- if (DEBUG_BOOT) Slog.i(TAG_WM, "checkBootAnimationComplete: Animation complete!");
+ ProtoLog.i(WM_DEBUG_BOOT, "checkBootAnimationComplete: Animation complete!");
return true;
}
public void showBootMessage(final CharSequence msg, final boolean always) {
boolean first = false;
synchronized (mGlobalLock) {
- if (DEBUG_BOOT) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.i(TAG_WM, "showBootMessage: msg=" + msg + " always=" + always
- + " mAllowBootMessages=" + mAllowBootMessages
- + " mShowingBootMessages=" + mShowingBootMessages
- + " mSystemBooted=" + mSystemBooted, here);
- }
+ ProtoLog.i(WM_DEBUG_BOOT, "showBootMessage: msg=%s always=%b"
+ + " mAllowBootMessages=%b mShowingBootMessages=%b"
+ + " mSystemBooted=%b. %s", msg, always, mAllowBootMessages,
+ mShowingBootMessages, mSystemBooted,
+ new RuntimeException("here").fillInStackTrace());
if (!mAllowBootMessages) {
return;
}
@@ -3400,14 +3402,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
public void hideBootMessagesLocked() {
- if (DEBUG_BOOT) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.i(TAG_WM, "hideBootMessagesLocked: mDisplayEnabled=" + mDisplayEnabled
- + " mForceDisplayEnabled=" + mForceDisplayEnabled
- + " mShowingBootMessages=" + mShowingBootMessages
- + " mSystemBooted=" + mSystemBooted, here);
- }
+ ProtoLog.i(WM_DEBUG_BOOT, "hideBootMessagesLocked: mDisplayEnabled=%b"
+ + " mForceDisplayEnabled=%b mShowingBootMessages=%b"
+ + " mSystemBooted=%b. %s", mDisplayEnabled, mForceDisplayEnabled,
+ mShowingBootMessages, mSystemBooted,
+ new RuntimeException("here").fillInStackTrace());
if (mShowingBootMessages) {
mShowingBootMessages = false;
mPolicy.hideBootMessages();
@@ -3452,60 +3451,45 @@ public class WindowManagerService extends IWindowManager.Stub
public void showCircularMask(boolean visible) {
synchronized (mGlobalLock) {
+ if (visible) {
+ // TODO(multi-display): support multiple displays
+ if (mCircularDisplayMask == null) {
+ int screenOffset = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_windowOutsetBottom);
+ int maskThickness = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.circular_display_mask_thickness);
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
- ">>> OPEN TRANSACTION showCircularMask(visible=" + visible + ")");
- openSurfaceTransaction();
- try {
- if (visible) {
- // TODO(multi-display): support multiple displays
- if (mCircularDisplayMask == null) {
- int screenOffset = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_windowOutsetBottom);
- int maskThickness = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.circular_display_mask_thickness);
-
- mCircularDisplayMask = new CircularDisplayMask(mSurfaceFactory,
- getDefaultDisplayContentLocked(),
- mPolicy.getWindowLayerFromTypeLw(
- WindowManager.LayoutParams.TYPE_POINTER)
- * TYPE_LAYER_MULTIPLIER + 10, screenOffset, maskThickness);
+
+ if (SHOW_LIGHT_TRANSACTIONS) {
+ Slog.i(TAG_WM,
+ ">>> showCircularMask(visible=" + visible + ")");
}
- mCircularDisplayMask.setVisibility(true);
- } else if (mCircularDisplayMask != null) {
- mCircularDisplayMask.setVisibility(false);
- mCircularDisplayMask = null;
+ mCircularDisplayMask = new CircularDisplayMask(mSurfaceFactory,
+ getDefaultDisplayContentLocked(), mPolicy.getWindowLayerFromTypeLw(
+ WindowManager.LayoutParams.TYPE_POINTER) * TYPE_LAYER_MULTIPLIER
+ + 10, screenOffset, maskThickness, mTransaction);
}
- } finally {
- closeSurfaceTransaction("showCircularMask");
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
- "<<< CLOSE TRANSACTION showCircularMask(visible=" + visible + ")");
+ mCircularDisplayMask.setVisibility(true, mTransaction);
+ } else if (mCircularDisplayMask != null) {
+ mCircularDisplayMask.setVisibility(false, mTransaction);
+ mCircularDisplayMask = null;
}
+ mTransaction.apply();
}
}
public void showEmulatorDisplayOverlay() {
synchronized (mGlobalLock) {
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
- ">>> OPEN TRANSACTION showEmulatorDisplayOverlay");
- openSurfaceTransaction();
- try {
- if (mEmulatorDisplayOverlay == null) {
- mEmulatorDisplayOverlay = new EmulatorDisplayOverlay(
- mSurfaceFactory,
- mContext,
- getDefaultDisplayContentLocked(),
- mPolicy.getWindowLayerFromTypeLw(
- WindowManager.LayoutParams.TYPE_POINTER)
- * TYPE_LAYER_MULTIPLIER + 10);
- }
- mEmulatorDisplayOverlay.setVisibility(true);
- } finally {
- closeSurfaceTransaction("showEmulatorDisplayOverlay");
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM,
- "<<< CLOSE TRANSACTION showEmulatorDisplayOverlay");
+ if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> showEmulatorDisplayOverlay");
+ if (mEmulatorDisplayOverlay == null) {
+ mEmulatorDisplayOverlay = new EmulatorDisplayOverlay(mSurfaceFactory, mContext,
+ getDefaultDisplayContentLocked(),
+ mPolicy.getWindowLayerFromTypeLw(WindowManager.LayoutParams.TYPE_POINTER)
+ * TYPE_LAYER_MULTIPLIER + 10, mTransaction);
}
+ mEmulatorDisplayOverlay.setVisibility(true, mTransaction);
+ mTransaction.apply();
}
}
@@ -3534,23 +3518,16 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
- if (SHOW_VERBOSE_TRANSACTIONS) Slog.i(TAG_WM,
- ">>> OPEN TRANSACTION showStrictModeViolation");
+ if (SHOW_VERBOSE_TRANSACTIONS) Slog.i(TAG_WM, ">>> showStrictModeViolation");
// TODO: Modify this to use the surface trace once it is not going crazy.
// b/31532461
- SurfaceControl.openTransaction();
- try {
- // TODO(multi-display): support multiple displays
- if (mStrictModeFlash == null) {
- mStrictModeFlash = new StrictModeFlash(mSurfaceFactory,
- getDefaultDisplayContentLocked());
- }
- mStrictModeFlash.setVisibility(on);
- } finally {
- SurfaceControl.closeTransaction();
- if (SHOW_VERBOSE_TRANSACTIONS) Slog.i(TAG_WM,
- "<<< CLOSE TRANSACTION showStrictModeViolation");
+ // TODO(multi-display): support multiple displays
+ if (mStrictModeFlash == null) {
+ mStrictModeFlash = new StrictModeFlash(mSurfaceFactory,
+ getDefaultDisplayContentLocked(), mTransaction);
}
+ mStrictModeFlash.setVisibility(on, mTransaction);
+ mTransaction.apply();
}
}
@@ -3699,8 +3676,7 @@ public class WindowManagerService extends IWindowManager.Stub
throw new SecurityException("Requires SET_ORIENTATION permission");
}
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "thawRotation: mRotation="
- + getDefaultDisplayRotation());
+ ProtoLog.v(WM_DEBUG_ORIENTATION, "thawRotation: mRotation=%d", getDefaultDisplayRotation());
long origId = Binder.clearCallingIdentity();
try {
@@ -3749,9 +3725,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
- if(DEBUG_ORIENTATION) Slog.v(TAG_WM, "updateRotationUnchecked:"
- + " alwaysSendConfiguration=" + alwaysSendConfiguration
- + " forceRelayout=" + forceRelayout);
+ ProtoLog.v(WM_DEBUG_ORIENTATION, "updateRotationUnchecked:"
+ + " alwaysSendConfiguration=%b forceRelayout=%b",
+ alwaysSendConfiguration, forceRelayout);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation");
@@ -4000,7 +3976,7 @@ public class WindowManagerService extends IWindowManager.Stub
try {
return mViewServer.start();
} catch (IOException e) {
- Slog.w(TAG_WM, "View server did not start");
+ ProtoLog.w(WM_ERROR, "View server did not start");
}
}
return false;
@@ -4010,7 +3986,7 @@ public class WindowManagerService extends IWindowManager.Stub
mViewServer = new ViewServer(this, port);
return mViewServer.start();
} catch (IOException e) {
- Slog.w(TAG_WM, "View server did not start");
+ ProtoLog.w(WM_ERROR, "View server did not start");
}
return false;
}
@@ -4240,7 +4216,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
} catch (Exception e) {
- Slog.w(TAG_WM, "Could not send command " + command + " with parameters " + parameters, e);
+ ProtoLog.w(WM_ERROR, "Could not send command %s with parameters %s. %s", command,
+ parameters, e);
success = false;
} finally {
if (data != null) {
@@ -4381,7 +4358,7 @@ public class WindowManagerService extends IWindowManager.Stub
// to make room for IME, but the window is not the focused window that's taking input.
// TODO (b/111080190): Consider the case of multiple IMEs on multi-display.
final DisplayContent topFocusedDisplay = mRoot.getTopFocusedDisplayContent();
- final AppWindowToken focusedApp = topFocusedDisplay.mFocusedApp;
+ final ActivityRecord focusedApp = topFocusedDisplay.mFocusedApp;
return (focusedApp != null && focusedApp.getTask() != null)
? focusedApp.getTask().mStack : null;
}
@@ -4389,9 +4366,9 @@ public class WindowManagerService extends IWindowManager.Stub
public boolean detectSafeMode() {
if (!mInputManagerCallback.waitForInputDevicesReady(
INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS)) {
- Slog.w(TAG_WM, "Devices still not ready after waiting "
- + INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS
- + " milliseconds before attempting to detect safe mode.");
+ ProtoLog.w(WM_ERROR, "Devices still not ready after waiting %d"
+ + " milliseconds before attempting to detect safe mode.",
+ INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS);
}
if (Settings.Global.getInt(
@@ -4419,14 +4396,14 @@ public class WindowManagerService extends IWindowManager.Stub
} catch (IllegalArgumentException e) {
}
if (mSafeMode) {
- Log.i(TAG_WM, "SAFE MODE ENABLED (menu=" + menuState + " s=" + sState
- + " dpad=" + dpadState + " trackball=" + trackballState + ")");
+ ProtoLog.i(WM_ERROR, "SAFE MODE ENABLED (menu=%d s=%d dpad=%d"
+ + " trackball=%d)", menuState, sState, dpadState, trackballState);
// May already be set if (for instance) this process has crashed
if (SystemProperties.getInt(ShutdownThread.RO_SAFEMODE_PROPERTY, 0) == 0) {
SystemProperties.set(ShutdownThread.RO_SAFEMODE_PROPERTY, "1");
}
} else {
- Log.i(TAG_WM, "SAFE MODE not enabled");
+ ProtoLog.i(WM_ERROR, "SAFE MODE not enabled");
}
mPolicy.setSafeMode(mSafeMode);
return mSafeMode;
@@ -4585,10 +4562,11 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
displayContent.mLastFocus = newFocus;
- if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Focus moving from " + lastFocus +
- " to " + newFocus + " displayId=" + displayContent.getDisplayId());
+ ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Focus moving from %s"
+ + " to %s displayId=%d", lastFocus, newFocus,
+ displayContent.getDisplayId());
if (newFocus != null && lastFocus != null && !newFocus.isDisplayedLw()) {
- if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Delaying loss of focus...");
+ ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Delaying loss of focus...");
displayContent.mLosingFocus.add(lastFocus);
lastFocus = null;
}
@@ -4602,13 +4580,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (newFocus != null) {
- if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Gaining focus: " + newFocus);
+ ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Gaining focus: %s", newFocus);
newFocus.reportFocusChangedSerialized(true, mInTouchMode);
notifyFocusChanged();
}
if (lastFocus != null) {
- if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Losing focus: " + lastFocus);
+ ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Losing focus: %s", lastFocus);
lastFocus.reportFocusChangedSerialized(false, mInTouchMode);
}
break;
@@ -4625,7 +4603,7 @@ public class WindowManagerService extends IWindowManager.Stub
final int N = losers.size();
for (int i = 0; i < N; i++) {
- if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Losing delayed focus: " +
+ ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Losing delayed focus: %s",
losers.get(i));
losers.get(i).reportFocusChangedSerialized(false, mInTouchMode);
}
@@ -4708,9 +4686,9 @@ public class WindowManagerService extends IWindowManager.Stub
case APP_FREEZE_TIMEOUT: {
synchronized (mGlobalLock) {
- Slog.w(TAG_WM, "App freeze timeout expired.");
+ ProtoLog.w(WM_ERROR, "App freeze timeout expired.");
mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
- for (int i = mAppFreezeListeners.size() - 1; i >=0 ; --i) {
+ for (int i = mAppFreezeListeners.size() - 1; i >= 0; --i) {
mAppFreezeListeners.get(i).onAppFreezeTimeout();
}
}
@@ -4750,11 +4728,12 @@ public class WindowManagerService extends IWindowManager.Stub
case WAITING_FOR_DRAWN_TIMEOUT: {
Runnable callback = null;
+ final WindowContainer container = (WindowContainer) msg.obj;
synchronized (mGlobalLock) {
- Slog.w(TAG_WM, "Timeout waiting for drawn: undrawn=" + mWaitingForDrawn);
- mWaitingForDrawn.clear();
- callback = mWaitingForDrawnCallback;
- mWaitingForDrawnCallback = null;
+ ProtoLog.w(WM_ERROR, "Timeout waiting for drawn: undrawn=%s",
+ container.mWaitingForDrawn);
+ container.mWaitingForDrawn.clear();
+ callback = mWaitingForDrawnCallbacks.remove(container);
}
if (callback != null) {
callback.run();
@@ -4786,9 +4765,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
case ALL_WINDOWS_DRAWN: {
Runnable callback;
+ final WindowContainer container = (WindowContainer) msg.obj;
synchronized (mGlobalLock) {
- callback = mWaitingForDrawnCallback;
- mWaitingForDrawnCallback = null;
+ callback = mWaitingForDrawnCallbacks.remove(container);
}
if (callback != null) {
callback.run();
@@ -4825,7 +4804,7 @@ public class WindowManagerService extends IWindowManager.Stub
case CHECK_IF_BOOT_ANIMATION_FINISHED: {
final boolean bootAnimationComplete;
synchronized (mGlobalLock) {
- if (DEBUG_BOOT) Slog.i(TAG_WM, "CHECK_IF_BOOT_ANIMATION_FINISHED:");
+ ProtoLog.i(WM_DEBUG_BOOT, "CHECK_IF_BOOT_ANIMATION_FINISHED:");
bootAnimationComplete = checkBootAnimationCompleteLocked();
}
if (bootAnimationComplete) {
@@ -4864,8 +4843,8 @@ public class WindowManagerService extends IWindowManager.Stub
case WINDOW_REPLACEMENT_TIMEOUT: {
synchronized (mGlobalLock) {
for (int i = mWindowReplacementTimeouts.size() - 1; i >= 0; i--) {
- final AppWindowToken token = mWindowReplacementTimeouts.get(i);
- token.onWindowReplacementTimeout();
+ final ActivityRecord activity = mWindowReplacementTimeouts.get(i);
+ activity.onWindowReplacementTimeout();
}
mWindowReplacementTimeouts.clear();
}
@@ -5039,10 +5018,10 @@ public class WindowManagerService extends IWindowManager.Stub
int width, height;
try {
width = Integer.parseInt(sizeStr.substring(0, pos));
- height = Integer.parseInt(sizeStr.substring(pos+1));
+ height = Integer.parseInt(sizeStr.substring(pos + 1));
if (displayContent.mBaseDisplayWidth != width
|| displayContent.mBaseDisplayHeight != height) {
- Slog.i(TAG_WM, "FORCED DISPLAY SIZE: " + width + "x" + height);
+ ProtoLog.i(WM_ERROR, "FORCED DISPLAY SIZE: %dx%d", width, height);
displayContent.updateBaseDisplayMetrics(width, height,
displayContent.mBaseDisplayDensity);
changed = true;
@@ -5063,7 +5042,7 @@ public class WindowManagerService extends IWindowManager.Stub
int mode = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DISPLAY_SCALING_FORCE, 0);
if (displayContent.mDisplayScalingDisabled != (mode != 0)) {
- Slog.i(TAG_WM, "FORCED DISPLAY SCALING DISABLED");
+ ProtoLog.i(WM_ERROR, "FORCED DISPLAY SCALING DISABLED");
displayContent.mDisplayScalingDisabled = true;
changed = true;
}
@@ -5243,7 +5222,7 @@ public class WindowManagerService extends IWindowManager.Stub
throw new IllegalArgumentException(
"Requested window " + client + " does not exist");
}
- Slog.w(TAG_WM, "Failed looking up window callers=" + Debug.getCallers(3));
+ ProtoLog.w(WM_ERROR, "Failed looking up window callers=%s", Debug.getCallers(3));
return null;
}
if (session != null && win.mSession != session) {
@@ -5251,7 +5230,7 @@ public class WindowManagerService extends IWindowManager.Stub
throw new IllegalArgumentException("Requested window " + client + " is in session "
+ win.mSession + ", not " + session);
}
- Slog.w(TAG_WM, "Failed looking up window callers=" + Debug.getCallers(3));
+ ProtoLog.w(WM_ERROR, "Failed looking up window callers=%s", Debug.getCallers(3));
return null;
}
@@ -5263,7 +5242,7 @@ public class WindowManagerService extends IWindowManager.Stub
// it frozen/off until this window draws at its new
// orientation.
if (!w.mToken.okToDisplay() && mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Changing surface while display frozen: " + w);
+ ProtoLog.v(WM_DEBUG_ORIENTATION, "Changing surface while display frozen: %s", w);
w.setOrientationChanging(true);
w.mLastFreezeDuration = 0;
mRoot.mOrientationChangeComplete = false;
@@ -5278,30 +5257,32 @@ public class WindowManagerService extends IWindowManager.Stub
}
void checkDrawnWindowsLocked() {
- if (mWaitingForDrawn.isEmpty() || mWaitingForDrawnCallback == null) {
+ if (mWaitingForDrawnCallbacks.isEmpty()) {
return;
}
- for (int j = mWaitingForDrawn.size() - 1; j >= 0; j--) {
- WindowState win = mWaitingForDrawn.get(j);
- if (DEBUG_SCREEN_ON) Slog.i(TAG_WM, "Waiting for drawn " + win +
- ": removed=" + win.mRemoved + " visible=" + win.isVisibleLw() +
- " mHasSurface=" + win.mHasSurface +
- " drawState=" + win.mWinAnimator.mDrawState);
- if (win.mRemoved || !win.mHasSurface || !win.isVisibleByPolicy()) {
- // Window has been removed or hidden; no draw will now happen, so stop waiting.
- if (DEBUG_SCREEN_ON) Slog.w(TAG_WM, "Aborted waiting for drawn: " + win);
- mWaitingForDrawn.remove(win);
- } else if (win.hasDrawnLw()) {
- // Window is now drawn (and shown).
- if (DEBUG_SCREEN_ON) Slog.d(TAG_WM, "Window drawn win=" + win);
- mWaitingForDrawn.remove(win);
+ mWaitingForDrawnCallbacks.forEach((container, callback) -> {
+ for (int j = container.mWaitingForDrawn.size() - 1; j >= 0; j--) {
+ final WindowState win = (WindowState) container.mWaitingForDrawn.get(j);
+ ProtoLog.i(WM_DEBUG_SCREEN_ON,
+ "Waiting for drawn %s: removed=%b visible=%b mHasSurface=%b drawState=%d",
+ win, win.mRemoved, win.isVisibleLw(), win.mHasSurface,
+ win.mWinAnimator.mDrawState);
+ if (win.mRemoved || !win.mHasSurface || !win.isVisibleByPolicy()) {
+ // Window has been removed or hidden; no draw will now happen, so stop waiting.
+ ProtoLog.w(WM_DEBUG_SCREEN_ON, "Aborted waiting for drawn: %s", win);
+ container.mWaitingForDrawn.remove(win);
+ } else if (win.hasDrawnLw()) {
+ // Window is now drawn (and shown).
+ ProtoLog.d(WM_DEBUG_SCREEN_ON, "Window drawn win=%s", win);
+ container.mWaitingForDrawn.remove(win);
+ }
+ }
+ if (container.mWaitingForDrawn.isEmpty()) {
+ ProtoLog.d(WM_DEBUG_SCREEN_ON, "All windows drawn!");
+ mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, container);
+ mH.sendMessage(mH.obtainMessage(H.ALL_WINDOWS_DRAWN, container));
}
- }
- if (mWaitingForDrawn.isEmpty()) {
- if (DEBUG_SCREEN_ON) Slog.d(TAG_WM, "All windows drawn!");
- mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
- mH.sendEmptyMessage(H.ALL_WINDOWS_DRAWN);
- }
+ });
}
void setHoldScreenLocked(final Session newHoldScreen) {
@@ -5315,19 +5296,15 @@ public class WindowManagerService extends IWindowManager.Stub
final boolean state = mHoldingScreenWakeLock.isHeld();
if (hold != state) {
if (hold) {
- if (DEBUG_KEEP_SCREEN_ON) {
- Slog.d(TAG_KEEP_SCREEN_ON, "Acquiring screen wakelock due to "
- + mRoot.mHoldScreenWindow);
- }
+ ProtoLog.d(WM_DEBUG_KEEP_SCREEN_ON, "Acquiring screen wakelock due to %s",
+ mRoot.mHoldScreenWindow);
mLastWakeLockHoldingWindow = mRoot.mHoldScreenWindow;
mLastWakeLockObscuringWindow = null;
mHoldingScreenWakeLock.acquire();
mPolicy.keepScreenOnStartedLw();
} else {
- if (DEBUG_KEEP_SCREEN_ON) {
- Slog.d(TAG_KEEP_SCREEN_ON, "Releasing screen wakelock, obscured by "
- + mRoot.mObscuringWindow);
- }
+ ProtoLog.d(WM_DEBUG_KEEP_SCREEN_ON, "Releasing screen wakelock, obscured by %s",
+ mRoot.mObscuringWindow);
mLastWakeLockHoldingWindow = null;
mLastWakeLockObscuringWindow = mRoot.mObscuringWindow;
mPolicy.keepScreenOnStoppedLw();
@@ -5371,10 +5348,9 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
- if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
- "startFreezingDisplayLocked: exitAnim="
- + exitAnim + " enterAnim=" + enterAnim
- + " called by " + Debug.getCallers(8));
+ ProtoLog.d(WM_DEBUG_ORIENTATION,
+ "startFreezingDisplayLocked: exitAnim=%d enterAnim=%d called by %s",
+ exitAnim, enterAnim, Debug.getCallers(8));
mScreenFrozenLock.acquire();
mDisplayFrozen = true;
@@ -5406,7 +5382,7 @@ public class WindowManagerService extends IWindowManager.Stub
mExitAnimId = exitAnim;
mEnterAnimId = enterAnim;
ScreenRotationAnimation screenRotationAnimation =
- mAnimator.getScreenRotationAnimationLocked(mFrozenDisplayId);
+ displayContent.getRotationAnimation();
if (screenRotationAnimation != null) {
screenRotationAnimation.kill();
}
@@ -5418,8 +5394,7 @@ public class WindowManagerService extends IWindowManager.Stub
screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
displayContent.getDisplayRotation().isFixedToUserRotation(), isSecure,
this);
- mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId,
- screenRotationAnimation);
+ displayContent.setRotationAnimation(screenRotationAnimation);
}
void stopFreezingDisplayLocked() {
@@ -5433,17 +5408,17 @@ public class WindowManagerService extends IWindowManager.Stub
if (waitingForConfig || mAppsFreezingScreen > 0
|| mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_ACTIVE
|| mClientFreezingScreen || numOpeningApps > 0) {
- if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
- "stopFreezingDisplayLocked: Returning mWaitingForConfig=" + waitingForConfig
- + ", mAppsFreezingScreen=" + mAppsFreezingScreen
- + ", mWindowsFreezingScreen=" + mWindowsFreezingScreen
- + ", mClientFreezingScreen=" + mClientFreezingScreen
- + ", mOpeningApps.size()=" + numOpeningApps);
+ ProtoLog.d(WM_DEBUG_ORIENTATION,
+ "stopFreezingDisplayLocked: Returning mWaitingForConfig=%b, "
+ + "mAppsFreezingScreen=%d, mWindowsFreezingScreen=%d, "
+ + "mClientFreezingScreen=%b, mOpeningApps.size()=%d",
+ waitingForConfig, mAppsFreezingScreen, mWindowsFreezingScreen,
+ mClientFreezingScreen, numOpeningApps);
return;
}
- if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
- "stopFreezingDisplayLocked: Unfreezing now");
+ ProtoLog.d(WM_DEBUG_ORIENTATION,
+ "stopFreezingDisplayLocked: Unfreezing now");
// We must make a local copy of the displayId as it can be potentially overwritten later on
@@ -5461,7 +5436,7 @@ public class WindowManagerService extends IWindowManager.Stub
sb.append(" due to ");
sb.append(mLastFinishedFreezeSource);
}
- Slog.i(TAG_WM, sb.toString());
+ ProtoLog.i(WM_ERROR, "%s", sb.toString());
mH.removeMessages(H.APP_FREEZE_TIMEOUT);
mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT);
if (PROFILE_ORIENTATION) {
@@ -5470,10 +5445,10 @@ public class WindowManagerService extends IWindowManager.Stub
boolean updateRotation = false;
- ScreenRotationAnimation screenRotationAnimation =
- mAnimator.getScreenRotationAnimationLocked(displayId);
+ ScreenRotationAnimation screenRotationAnimation = displayContent == null ? null
+ : displayContent.getRotationAnimation();
if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()) {
- if (DEBUG_ORIENTATION) Slog.i(TAG_WM, "**** Dismissing screen rotation animation");
+ ProtoLog.i(WM_DEBUG_ORIENTATION, "**** Dismissing screen rotation animation");
DisplayInfo displayInfo = displayContent.getDisplayInfo();
// Get rotation animation again, with new top window
if (!displayContent.getDisplayRotation().validateRotationAnimation(
@@ -5484,16 +5459,15 @@ public class WindowManagerService extends IWindowManager.Stub
getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) {
mTransaction.apply();
- scheduleAnimationLocked();
} else {
screenRotationAnimation.kill();
- mAnimator.setScreenRotationAnimationLocked(displayId, null);
+ displayContent.setRotationAnimation(null);
updateRotation = true;
}
} else {
if (screenRotationAnimation != null) {
screenRotationAnimation.kill();
- mAnimator.setScreenRotationAnimationLocked(displayId, null);
+ displayContent.setRotationAnimation(null);
}
updateRotation = true;
}
@@ -5517,7 +5491,7 @@ public class WindowManagerService extends IWindowManager.Stub
mScreenFrozenLock.release();
if (updateRotation && displayContent != null) {
- if (DEBUG_ORIENTATION) Slog.d(TAG_WM, "Performing post-rotate rotation");
+ ProtoLog.d(WM_DEBUG_ORIENTATION, "Performing post-rotate rotation");
configChanged |= displayContent.updateRotationUnchecked();
}
@@ -5549,7 +5523,7 @@ public class WindowManagerService extends IWindowManager.Stub
return val;
}
- void createWatermarkInTransaction() {
+ void createWatermark() {
if (mWatermark != null) {
return;
}
@@ -5567,8 +5541,8 @@ public class WindowManagerService extends IWindowManager.Stub
// TODO(multi-display): Show watermarks on secondary displays.
final DisplayContent displayContent = getDefaultDisplayContentLocked();
mWatermark = new Watermark(mSurfaceFactory, displayContent,
- displayContent.mRealDisplayMetrics,
- toks);
+ displayContent.mRealDisplayMetrics, toks, mTransaction);
+ mTransaction.apply();
}
}
} catch (FileNotFoundException e) {
@@ -5611,16 +5585,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void setShelfHeight(boolean visible, int shelfHeight) {
- mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.STATUS_BAR,
- "setShelfHeight()");
- synchronized (mGlobalLock) {
- getDefaultDisplayContentLocked().getPinnedStackController().setAdjustedForShelf(visible,
- shelfHeight);
- }
- }
-
- @Override
public void statusBarVisibilityChanged(int displayId, int visibility) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
!= PackageManager.PERMISSION_GRANTED) {
@@ -5639,6 +5603,20 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
+ public void hideTransientBars(int displayId) {
+ mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.STATUS_BAR,
+ "hideTransientBars()");
+ synchronized (mGlobalLock) {
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent != null) {
+ displayContent.hideTransientBars();
+ } else {
+ Slog.w(TAG, "hideTransientBars with invalid displayId=" + displayId);
+ }
+ }
+ }
+
+ @Override
public void setForceShowSystemBars(boolean show) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
!= PackageManager.PERMISSION_GRANTED) {
@@ -5840,6 +5818,11 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print(mWindowTracing.getStatus() + "\n");
}
+ private void dumpLogStatus(PrintWriter pw) {
+ pw.println("WINDOW MANAGER LOGGING (dumpsys window logging)");
+ pw.println(ProtoLogImpl.getSingleInstance().getStatus());
+ }
+
private void dumpSessionsLocked(PrintWriter pw, boolean dumpAll) {
pw.println("WINDOW MANAGER SESSIONS (dumpsys window sessions)");
for (int i=0; i<mSessions.size(); i++) {
@@ -5968,13 +5951,18 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
}
- if (mWaitingForDrawn.size() > 0) {
+ if (!mWaitingForDrawnCallbacks.isEmpty()) {
pw.println();
pw.println(" Clients waiting for these windows to be drawn:");
- for (int i=mWaitingForDrawn.size()-1; i>=0; i--) {
- WindowState win = mWaitingForDrawn.get(i);
- pw.print(" Waiting #"); pw.print(i); pw.print(' '); pw.print(win);
- }
+ mWaitingForDrawnCallbacks.forEach((wc, callback) -> {
+ pw.print(" WindowContainer ");
+ pw.println(wc.getName());
+ for (int i = wc.mWaitingForDrawn.size() - 1; i >= 0; i--) {
+ final WindowState win = (WindowState) wc.mWaitingForDrawn.get(i);
+ pw.print(" Waiting #"); pw.print(i); pw.print(' '); pw.print(win);
+ }
+ });
+
}
pw.println();
pw.print(" mGlobalConfiguration="); pw.println(mRoot.getConfiguration());
@@ -6052,7 +6040,7 @@ public class WindowManagerService extends IWindowManager.Stub
mRoot.forAllWindows((w) -> {
if ((!visibleOnly || w.mWinAnimator.getShown())
- && (!appsOnly || w.mAppToken != null)) {
+ && (!appsOnly || w.mActivityRecord != null)) {
windows.add(w);
}
}, true /* traverseTopToBottom */);
@@ -6087,16 +6075,16 @@ public class WindowManagerService extends IWindowManager.Stub
* the time an ANR occurred before anything else in the system changes
* in response.
*
- * @param appWindowToken The application that ANR'd, may be null.
+ * @param activity The application that ANR'd, may be null.
* @param windowState The window that ANR'd, may be null.
* @param reason The reason for the ANR, may be null.
*/
- void saveANRStateLocked(AppWindowToken appWindowToken, WindowState windowState, String reason) {
+ void saveANRStateLocked(ActivityRecord activity, WindowState windowState, String reason) {
StringWriter sw = new StringWriter();
PrintWriter pw = new FastPrintWriter(sw, false, 1024);
pw.println(" ANR time: " + DateFormat.getDateTimeInstance().format(new Date()));
- if (appWindowToken != null) {
- pw.println(" Application at fault: " + appWindowToken.stringName);
+ if (activity != null) {
+ pw.println(" Application at fault: " + activity.stringName);
}
if (windowState != null) {
pw.println(" Window at fault: " + windowState.mAttrs.getTitle());
@@ -6235,6 +6223,9 @@ public class WindowManagerService extends IWindowManager.Stub
} else if ("trace".equals(cmd)) {
dumpTraceStatus(pw);
return;
+ } else if ("logging".equals(cmd)) {
+ dumpLogStatus(pw);
+ return;
} else if ("refresh".equals(cmd)) {
dumpHighRefreshRateBlacklist(pw);
return;
@@ -6296,6 +6287,10 @@ public class WindowManagerService extends IWindowManager.Stub
if (dumpAll) {
pw.println(separator);
}
+ dumpLogStatus(pw);
+ if (dumpAll) {
+ pw.println(separator);
+ }
dumpHighRefreshRateBlacklist(pw);
}
}
@@ -6331,20 +6326,23 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* Hint to a token that its activity will relaunch, which will trigger removal and addition of
* a window.
+ *
* @param token Application token for which the activity will be relaunched.
*/
void setWillReplaceWindow(IBinder token, boolean animate) {
- final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token);
- if (appWindowToken == null) {
- Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token " + token);
+ final ActivityRecord activity = mRoot.getActivityRecord(token);
+ if (activity == null) {
+ ProtoLog.w(WM_ERROR, "Attempted to set replacing window on non-existing app token %s",
+ token);
return;
}
- if (!appWindowToken.hasContentToDisplay()) {
- Slog.w(TAG_WM, "Attempted to set replacing window on app token with no content"
- + token);
+ if (!activity.hasContentToDisplay()) {
+ ProtoLog.w(WM_ERROR,
+ "Attempted to set replacing window on app token with no content %s",
+ token);
return;
}
- appWindowToken.setWillReplaceWindows(animate);
+ activity.setWillReplaceWindows(animate);
}
/**
@@ -6361,22 +6359,24 @@ public class WindowManagerService extends IWindowManager.Stub
// above. We should combine them or find better names.
void setWillReplaceWindows(IBinder token, boolean childrenOnly) {
synchronized (mGlobalLock) {
- final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token);
- if (appWindowToken == null) {
- Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token "
- + token);
+ final ActivityRecord activity = mRoot.getActivityRecord(token);
+ if (activity == null) {
+ ProtoLog.w(WM_ERROR,
+ "Attempted to set replacing window on non-existing app token %s",
+ token);
return;
}
- if (!appWindowToken.hasContentToDisplay()) {
- Slog.w(TAG_WM, "Attempted to set replacing window on app token with no content"
- + token);
+ if (!activity.hasContentToDisplay()) {
+ ProtoLog.w(WM_ERROR,
+ "Attempted to set replacing window on app token with no content %s",
+ token);
return;
}
if (childrenOnly) {
- appWindowToken.setWillReplaceChildWindows();
+ activity.setWillReplaceChildWindows();
} else {
- appWindowToken.setWillReplaceWindows(false /* animate */);
+ activity.setWillReplaceWindows(false /* animate */);
}
scheduleClearWillReplaceWindows(token, true /* replacing */);
@@ -6389,26 +6389,26 @@ public class WindowManagerService extends IWindowManager.Stub
*
* If we're not replacing the window, clear the replace window settings of the app.
*
- * @param token Application token for the activity whose window might be replaced.
+ * @param token Application token for the activity whose window might be replaced.
* @param replacing Whether the window is being replaced or not.
*/
void scheduleClearWillReplaceWindows(IBinder token, boolean replacing) {
- final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token);
- if (appWindowToken == null) {
- Slog.w(TAG_WM, "Attempted to reset replacing window on non-existing app token "
- + token);
+ final ActivityRecord activity = mRoot.getActivityRecord(token);
+ if (activity == null) {
+ ProtoLog.w(WM_ERROR, "Attempted to reset replacing window on non-existing app token %s",
+ token);
return;
}
if (replacing) {
- scheduleWindowReplacementTimeouts(appWindowToken);
+ scheduleWindowReplacementTimeouts(activity);
} else {
- appWindowToken.clearWillReplaceWindows();
+ activity.clearWillReplaceWindows();
}
}
- void scheduleWindowReplacementTimeouts(AppWindowToken appWindowToken) {
- if (!mWindowReplacementTimeouts.contains(appWindowToken)) {
- mWindowReplacementTimeouts.add(appWindowToken);
+ void scheduleWindowReplacementTimeouts(ActivityRecord activity) {
+ if (!mWindowReplacementTimeouts.contains(activity)) {
+ mWindowReplacementTimeouts.add(activity);
}
mH.removeMessages(H.WINDOW_REPLACEMENT_TIMEOUT);
mH.sendEmptyMessageDelayed(
@@ -6585,7 +6585,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
WindowState callingWin = windowForClientLocked(null, client, false);
if (callingWin == null) {
- Slog.w(TAG_WM, "Bad requesting window " + client);
+ ProtoLog.w(WM_ERROR, "Bad requesting window %s", client);
return;
}
final DisplayContent displayContent = callingWin.getDisplayContent();
@@ -6602,7 +6602,7 @@ public class WindowManagerService extends IWindowManager.Stub
windowUnderPointer.translateToWindowX(mouseX),
windowUnderPointer.translateToWindowY(mouseY));
} catch (RemoteException e) {
- Slog.w(TAG_WM, "unable to update pointer icon");
+ ProtoLog.w(WM_ERROR, "unable to update pointer icon");
}
}
}
@@ -6619,7 +6619,7 @@ public class WindowManagerService extends IWindowManager.Stub
windowUnderPointer.translateToWindowX(latestX),
windowUnderPointer.translateToWindowY(latestY));
} catch (RemoteException e) {
- Slog.w(TAG_WM, "unable to restore pointer icon");
+ ProtoLog.w(WM_ERROR, "unable to restore pointer icon");
}
} else {
InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_DEFAULT);
@@ -6649,7 +6649,7 @@ public class WindowManagerService extends IWindowManager.Stub
try {
final WindowState win = windowForClientLocked(null, client, false);
if (win == null) {
- Slog.w(TAG_WM, "Bad requesting window " + client);
+ ProtoLog.w(WM_ERROR, "Bad requesting window %s", client);
return;
}
getDisplayContentOrCreate(displayId, null).reparentDisplayContent(win, sc);
@@ -6674,7 +6674,7 @@ public class WindowManagerService extends IWindowManager.Stub
try {
final WindowState win = windowForClientLocked(null, client, false);
if (win == null) {
- Slog.w(TAG_WM, "Bad requesting window " + client);
+ ProtoLog.w(WM_ERROR, "Bad requesting window %s", client);
return;
}
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
@@ -6701,7 +6701,7 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mGlobalLock) {
final WindowState callingWin = windowForClientLocked(null, client, false);
if (callingWin == null) {
- Slog.w(TAG_WM, "Bad requesting window " + client);
+ ProtoLog.w(WM_ERROR, "Bad requesting window %s", client);
return;
}
callingWin.updateTapExcludeRegion(regionId, region);
@@ -6740,8 +6740,9 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mGlobalLock) {
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent == null) {
- Slog.w(TAG_WM, "Attempted to get windowing mode of a display that does not exist: "
- + displayId);
+ ProtoLog.w(WM_ERROR,
+ "Attempted to get windowing mode of a display that does not exist: %d",
+ displayId);
return WindowConfiguration.WINDOWING_MODE_UNDEFINED;
}
return mDisplayWindowSettings.getWindowingModeLocked(displayContent);
@@ -6757,8 +6758,9 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mGlobalLock) {
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
if (displayContent == null) {
- Slog.w(TAG_WM, "Attempted to set windowing mode to a display that does not exist: "
- + displayId);
+ ProtoLog.w(WM_ERROR,
+ "Attempted to set windowing mode to a display that does not exist: %d",
+ displayId);
return;
}
@@ -6793,8 +6795,9 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mGlobalLock) {
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent == null) {
- Slog.w(TAG_WM, "Attempted to get remove mode of a display that does not exist: "
- + displayId);
+ ProtoLog.w(WM_ERROR,
+ "Attempted to get remove mode of a display that does not exist: %d",
+ displayId);
return REMOVE_CONTENT_MODE_UNDEFINED;
}
return mDisplayWindowSettings.getRemoveContentModeLocked(displayContent);
@@ -6810,8 +6813,9 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mGlobalLock) {
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
if (displayContent == null) {
- Slog.w(TAG_WM, "Attempted to set remove mode to a display that does not exist: "
- + displayId);
+ ProtoLog.w(WM_ERROR,
+ "Attempted to set remove mode to a display that does not exist: %d",
+ displayId);
return;
}
@@ -6830,8 +6834,8 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mGlobalLock) {
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent == null) {
- Slog.w(TAG_WM, "Attempted to get flag of a display that does not exist: "
- + displayId);
+ ProtoLog.w(WM_ERROR, "Attempted to get flag of a display that does not exist: %d",
+ displayId);
return false;
}
return mDisplayWindowSettings.shouldShowWithInsecureKeyguardLocked(displayContent);
@@ -6848,8 +6852,8 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mGlobalLock) {
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
if (displayContent == null) {
- Slog.w(TAG_WM, "Attempted to set flag to a display that does not exist: "
- + displayId);
+ ProtoLog.w(WM_ERROR, "Attempted to set flag to a display that does not exist: %d",
+ displayId);
return;
}
@@ -6869,8 +6873,8 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mGlobalLock) {
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent == null) {
- Slog.w(TAG_WM, "Attempted to get system decors flag of a display that does "
- + "not exist: " + displayId);
+ ProtoLog.w(WM_ERROR, "Attempted to get system decors flag of a display that does "
+ + "not exist: %d", displayId);
return false;
}
if (displayContent.isUntrustedVirtualDisplay()) {
@@ -6889,8 +6893,8 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mGlobalLock) {
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
if (displayContent == null) {
- Slog.w(TAG_WM, "Attempted to set system decors flag to a display that does "
- + "not exist: " + displayId);
+ ProtoLog.w(WM_ERROR, "Attempted to set system decors flag to a display that does "
+ + "not exist: %d", displayId);
return;
}
if (displayContent.isUntrustedVirtualDisplay()) {
@@ -6913,8 +6917,9 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mGlobalLock) {
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent == null) {
- Slog.w(TAG_WM, "Attempted to get IME flag of a display that does not exist: "
- + displayId);
+ ProtoLog.w(WM_ERROR,
+ "Attempted to get IME flag of a display that does not exist: %d",
+ displayId);
return false;
}
if (displayContent.isUntrustedVirtualDisplay()) {
@@ -6934,8 +6939,9 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mGlobalLock) {
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
if (displayContent == null) {
- Slog.w(TAG_WM, "Attempted to set IME flag to a display that does not exist: "
- + displayId);
+ ProtoLog.w(WM_ERROR,
+ "Attempted to set IME flag to a display that does not exist: %d",
+ displayId);
return;
}
if (displayContent.isUntrustedVirtualDisplay()) {
@@ -7112,17 +7118,25 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void waitForAllWindowsDrawn(Runnable callback, long timeout) {
+ public void waitForAllWindowsDrawn(Runnable callback, long timeout, int displayId) {
+ final WindowContainer container = displayId == INVALID_DISPLAY
+ ? mRoot : mRoot.getDisplayContent(displayId);
+ if (container == null) {
+ // The waiting container doesn't exist, no need to wait to run the callback. Run and
+ // return;
+ callback.run();
+ return;
+ }
boolean allWindowsDrawn = false;
synchronized (mGlobalLock) {
- mWaitingForDrawnCallback = callback;
- getDefaultDisplayContentLocked().waitForAllWindowsDrawn();
+ container.waitForAllWindowsDrawn();
mWindowPlacerLocked.requestTraversal();
- mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
- if (mWaitingForDrawn.isEmpty()) {
+ mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, container);
+ if (container.mWaitingForDrawn.isEmpty()) {
allWindowsDrawn = true;
} else {
- mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, timeout);
+ mWaitingForDrawnCallbacks.put(container, callback);
+ mH.sendNewMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, container, timeout);
checkDrawnWindowsLocked();
}
}
@@ -7152,15 +7166,16 @@ public class WindowManagerService extends IWindowManager.Stub
if (removeWindows) {
final DisplayContent dc = mRoot.getDisplayContent(displayId);
if (dc == null) {
- Slog.w(TAG_WM, "removeWindowToken: Attempted to remove token: " + binder
- + " for non-exiting displayId=" + displayId);
+ ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s"
+ + " for non-exiting displayId=%d", binder, displayId);
return;
}
final WindowToken token = dc.removeWindowToken(binder);
if (token == null) {
- Slog.w(TAG_WM, "removeWindowToken: Attempted to remove non-existing token: "
- + binder);
+ ProtoLog.w(WM_ERROR,
+ "removeWindowToken: Attempted to remove non-existing token: %s",
+ binder);
return;
}
@@ -7318,6 +7333,39 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
+ public void showImePostLayout(IBinder imeTargetWindowToken) {
+ synchronized (mGlobalLock) {
+ final WindowState imeTarget = mWindowMap.get(imeTargetWindowToken);
+ if (imeTarget == null) {
+ return;
+ }
+ final DisplayContent displayContent = imeTarget.getDisplayContent();
+ if (displayContent == null) {
+ Slog.w(TAG_WM, "Attempted to show IME on an IME target that does not exist: "
+ + imeTarget.getName());
+ return;
+ }
+ if (displayContent.isUntrustedVirtualDisplay()) {
+ throw new SecurityException("Attempted to show IME on an untrusted "
+ + "virtual display: " + displayContent.getDisplayId());
+ }
+
+ displayContent.getInsetsStateController().getImeSourceProvider()
+ .scheduleShowImePostLayout(imeTarget);
+ }
+ }
+
+ @Override
+ public void hideIme(int displayId) {
+ synchronized (mGlobalLock) {
+ final DisplayContent dc = mRoot.getDisplayContent(displayId);
+ if (dc != null && dc.mInputMethodTarget != null) {
+ dc.mInputMethodTarget.hideInsets(WindowInsets.Type.ime(), true /* fromIme */);
+ }
+ }
+ }
+
+ @Override
public boolean isUidAllowedOnDisplay(int displayId, int uid) {
if (displayId == Display.DEFAULT_DISPLAY) {
return true;
@@ -7389,6 +7437,11 @@ public class WindowManagerService extends IWindowManager.Stub
&& configuration.touchscreen == Configuration.TOUCHSCREEN_FINGER;
}
}
+
+ @Override
+ public @Nullable KeyInterceptionInfo getKeyInterceptionInfoFromToken(IBinder inputToken) {
+ return mKeyInterceptionInfoForToken.get(inputToken);
+ }
}
void registerAppFreezeListener(AppFreezeListener listener) {
@@ -7595,7 +7648,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
private void onPointerDownOutsideFocusLocked(IBinder touchedToken) {
- final WindowState touchedWindow = windowForClientLocked(null, touchedToken, false);
+ final WindowState touchedWindow = mInputToWindowMap.get(touchedToken);
if (touchedWindow == null || !touchedWindow.canReceiveKeys()) {
return;
}
@@ -7646,7 +7699,7 @@ public class WindowManagerService extends IWindowManager.Stub
// to do so because it seems possible to resume activities as part of a larger
// transaction and it's too early to resume based on current order when performing
// updateTopResumedActivityIfNeeded().
- displayContent.mAcitvityDisplay.ensureActivitiesVisible(null /* starting */,
+ displayContent.mActivityDisplay.ensureActivitiesVisible(null /* starting */,
0 /* configChanges */, !PRESERVE_WINDOWS, true /* notifyClients */);
}
}
@@ -7668,9 +7721,9 @@ public class WindowManagerService extends IWindowManager.Stub
clientChannel.transferTo(outInputChannel);
clientChannel.dispose();
- mInputManager.registerInputChannel(inputChannel, null /* generate new token */);
+ mInputManager.registerInputChannel(inputChannel);
- InputWindowHandle h = new InputWindowHandle(null, null, displayId);
+ InputWindowHandle h = new InputWindowHandle(null, displayId);
h.token = inputChannel.getToken();
h.name = name;
h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
@@ -7758,4 +7811,27 @@ public class WindowManagerService extends IWindowManager.Stub
Binder.restoreCallingIdentity(token);
}
}
+
+ @Override
+ public boolean mirrorDisplay(int displayId, SurfaceControl outSurfaceControl) {
+ if (!checkCallingPermission(ACCESS_SURFACE_FLINGER, "mirrorDisplay()")) {
+ throw new SecurityException("Requires ACCESS_SURFACE_FLINGER permission");
+ }
+
+ final SurfaceControl displaySc;
+ synchronized (mGlobalLock) {
+ DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent == null) {
+ Slog.e(TAG, "Invalid displayId " + displayId + " for mirrorDisplay");
+ return false;
+ }
+
+ displaySc = displayContent.getSurfaceControl();
+ }
+
+ final SurfaceControl mirror = SurfaceControl.mirrorSurface(displaySc);
+ outSurfaceControl.copyFrom(mirror);
+
+ return true;
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 7384bb7e1587..e01cbf26dadc 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -28,6 +28,8 @@ import android.view.Display;
import android.view.IWindowManager;
import android.view.Surface;
+import com.android.server.protolog.ProtoLogImpl;
+
import java.io.PrintWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -75,6 +77,8 @@ public class WindowManagerShellCommand extends ShellCommand {
// the output trace file, so the shell gets the correct semantics for where
// trace files can be written.
return mInternal.mWindowTracing.onShellCommand(this);
+ case "logging":
+ return ProtoLogImpl.getSingleInstance().onShellCommand(this);
case "set-user-rotation":
return runSetDisplayUserRotation(pw);
case "set-fix-to-user-rotation":
@@ -389,6 +393,8 @@ public class WindowManagerShellCommand extends ShellCommand {
if (!IS_USER) {
pw.println(" tracing (start | stop)");
pw.println(" Start or stop window tracing.");
+ pw.println(" logging (start | stop | enable | disable | enable-text | disable-text)");
+ pw.println(" Logging settings.");
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index d7116d8bbd87..eb75684036c4 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -702,7 +702,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
}
ActivityRecord hist = mActivities.get(0);
intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP, hist.packageName);
- intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK, hist.getTaskRecord().taskId);
+ intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK, hist.getTaskRecord().mTaskId);
}
boolean shouldKillProcessForRemovedTask(TaskRecord tr) {
@@ -713,7 +713,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
return false;
}
final TaskRecord otherTask = activity.getTaskRecord();
- if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
+ if (tr.mTaskId != otherTask.mTaskId && otherTask.inRecents) {
// Don't kill process(es) that has an activity in a different task that is
// also in recents.
return false;
@@ -773,6 +773,12 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
public int computeOomAdjFromActivities(int minTaskLayer, ComputeOomAdjCallback callback) {
+ // 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;
+ boolean finishing = true;
+ boolean visible = false;
synchronized (mAtm.mGlobalLockWithoutBoost) {
final int activitiesSize = mActivities.size();
for (int j = 0; j < activitiesSize; j++) {
@@ -788,7 +794,6 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
}
}
if (r.visible) {
- callback.onVisibleActivity();
final TaskRecord task = r.getTaskRecord();
if (task != null && minTaskLayer > 0) {
final int layer = task.mLayerRank;
@@ -796,16 +801,32 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
minTaskLayer = layer;
}
}
- break;
- } else if (r.isState(PAUSING, PAUSED)) {
- callback.onPausedActivity();
- } else if (r.isState(STOPPING)) {
- callback.onStoppingActivity(r.finishing);
+ visible = true;
+ // continue the loop, in case there are multiple visible activities in
+ // this process, we'd find out the one with the minimal layer, thus it'll
+ // get a higher adj score.
} else {
- callback.onOtherActivity();
+ if (best != PAUSING && best != PAUSED) {
+ if (r.isState(PAUSING, PAUSED)) {
+ best = PAUSING;
+ } else if (r.isState(STOPPING)) {
+ best = STOPPING;
+ // Not "finishing" if any of activity isn't finishing.
+ finishing &= r.finishing;
+ }
+ }
}
}
}
+ if (visible) {
+ callback.onVisibleActivity();
+ } else if (best == PAUSING) {
+ callback.onPausedActivity();
+ } else if (best == STOPPING) {
+ callback.onStoppingActivity(finishing);
+ } else {
+ callback.onOtherActivity();
+ }
return minTaskLayer;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 501a93ef6645..b9cf29ad7790 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -31,6 +31,7 @@ import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
+import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
@@ -90,20 +91,20 @@ import static com.android.server.wm.IdentifierProto.USER_ID;
import static com.android.server.wm.MoveAnimationSpecProto.DURATION_MS;
import static com.android.server.wm.MoveAnimationSpecProto.FROM;
import static com.android.server.wm.MoveAnimationSpecProto.TO;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
+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_ORIENTATION;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RESIZE;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_POWER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RESIZE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
@@ -191,20 +192,24 @@ import android.view.InputChannel;
import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.InputWindowHandle;
+import android.view.InsetsState;
import android.view.Surface.Rotation;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowInfo;
+import android.view.WindowInsets.Type.InsetType;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.KeyInterceptionInfo;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.protolog.common.ProtoLog;
import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
import com.android.server.wm.utils.InsetUtils;
import com.android.server.wm.utils.WmDisplayCutout;
@@ -246,7 +251,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final WindowId mWindowId;
WindowToken mToken;
// The same object as mToken if this is an app window and null for non-app windows.
- AppWindowToken mAppToken;
+ ActivityRecord mActivityRecord;
// mAttrs.flags is tested in animation without being locked. If the bits tested are ever
// modified they will need to be locked.
@@ -630,9 +635,19 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
*/
private boolean mIsDimming = false;
- private @Nullable InsetsSourceProvider mInsetProvider;
+ private @Nullable InsetsSourceProvider mControllableInsetProvider;
+ private InsetsState mClientInsetsState = new InsetsState();
private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
+ private KeyInterceptionInfo mKeyInterceptionInfo;
+
+ InsetsState getClientInsetsState() {
+ return mClientInsetsState;
+ }
+
+ void setClientInsetsState(InsetsState state) {
+ mClientInsetsState = state;
+ }
void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
@Rotation int rotation, boolean requested) {
@@ -687,7 +702,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final boolean immersiveSticky =
(mSystemUiVisibility & immersiveStickyFlags) == immersiveStickyFlags;
return immersiveSticky && mWmService.mSystemGestureExcludedByPreQStickyImmersive
- && mAppToken != null && mAppToken.mTargetSdk < Build.VERSION_CODES.Q;
+ && mActivityRecord != null && mActivityRecord.mTargetSdk < Build.VERSION_CODES.Q;
}
void setLastExclusionHeights(int side, int requested, int granted) {
@@ -737,7 +752,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mClient = c;
mAppOp = appOp;
mToken = token;
- mAppToken = mToken.asAppWindowToken();
+ mActivityRecord = mToken.asActivityRecord();
mOwnerUid = ownerId;
mOwnerCanAddInternalSystemWindow = ownerCanAddInternalSystemWindow;
mWindowId = new WindowId(this);
@@ -779,7 +794,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type);
mIsChildWindow = true;
- if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + this + " to " + parentWindow);
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", this, parentWindow);
parentWindow.addChild(this, sWindowSubLayerComparator);
mLayoutAttached = mAttrs.type !=
@@ -801,7 +816,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
mIsFloatingLayer = mIsImWindow || mIsWallpaper;
- if (mAppToken != null && mAppToken.mShowForAllUsers) {
+ if (mActivityRecord != null && mActivityRecord.mShowForAllUsers) {
// Windows for apps that can show for all users should also show when the device is
// locked.
mAttrs.flags |= FLAG_SHOW_WHEN_LOCKED;
@@ -816,7 +831,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mLastRequestedHeight = 0;
mLayer = 0;
mInputWindowHandle = new InputWindowHandle(
- mAppToken != null ? mAppToken.mInputApplicationHandle : null, c,
+ mActivityRecord != null ? mActivityRecord.mInputApplicationHandle : null,
getDisplayId());
}
@@ -832,7 +847,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
*/
boolean inSizeCompatMode() {
return (mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0
- || (mAppToken != null && mAppToken.inSizeCompatMode()
+ || (mActivityRecord != null && mActivityRecord.hasSizeCompatBounds()
// Exclude starting window because it is not displayed by the application.
&& mAttrs.type != TYPE_APPLICATION_STARTING);
}
@@ -947,11 +962,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
layoutYDiff = 0;
} else {
mWindowFrames.mContainingFrame.set(getDisplayedBounds());
- if (mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) {
+ if (mActivityRecord != null && !mActivityRecord.mFrozenBounds.isEmpty()) {
// If the bounds are frozen, we still want to translate the window freely and only
// freeze the size.
- Rect frozen = mAppToken.mFrozenBounds.peek();
+ Rect frozen = mActivityRecord.mFrozenBounds.peek();
mWindowFrames.mContainingFrame.right =
mWindowFrames.mContainingFrame.left + frozen.width();
mWindowFrames.mContainingFrame.bottom =
@@ -1134,8 +1149,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// TODO: Look into whether this override is still necessary.
@Override
public Rect getBounds() {
- if (mAppToken != null) {
- return mAppToken.getBounds();
+ if (mActivityRecord != null) {
+ return mActivityRecord.getBounds();
} else {
return super.getBounds();
}
@@ -1236,12 +1251,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
public IApplicationToken getAppToken() {
- return mAppToken != null ? mAppToken.appToken : null;
+ return mActivityRecord != null ? mActivityRecord.appToken : null;
}
@Override
public boolean isVoiceInteraction() {
- return mAppToken != null && mAppToken.mVoiceInteraction;
+ return mActivityRecord != null && mActivityRecord.mVoiceInteraction;
}
boolean setReportResizeHints() {
@@ -1292,19 +1307,16 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|| configChanged
|| dragResizingChanged
|| mReportOrientationChanged) {
- if (DEBUG_RESIZE || DEBUG_ORIENTATION) {
- Slog.v(TAG_WM, "Resize reasons for w=" + this + ": "
- + " " + mWindowFrames.getInsetsChangedInfo()
- + " surfaceResized=" + winAnimator.mSurfaceResized
- + " configChanged=" + configChanged
- + " dragResizingChanged=" + dragResizingChanged
- + " reportOrientationChanged=" + mReportOrientationChanged);
- }
+ ProtoLog.v(WM_DEBUG_RESIZE,
+ "Resize reasons for w=%s: %s surfaceResized=%b configChanged=%b "
+ + "dragResizingChanged=%b reportOrientationChanged=%b",
+ this, mWindowFrames.getInsetsChangedInfo(), winAnimator.mSurfaceResized,
+ configChanged, dragResizingChanged, mReportOrientationChanged);
// If it's a dead window left on screen, and the configuration changed, there is nothing
// we can do about it. Remove the window now.
- if (mAppToken != null && mAppDied) {
- mAppToken.removeDeadWindows();
+ if (mActivityRecord != null && mAppDied) {
+ mActivityRecord.removeDeadWindows();
return;
}
@@ -1316,24 +1328,31 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// redrawn; to do that, we need to go through the process of getting informed by the
// application when it has finished drawing.
if (getOrientationChanging() || dragResizingChanged) {
- if (DEBUG_ANIM || DEBUG_ORIENTATION || DEBUG_RESIZE) {
- Slog.v(TAG_WM, "Orientation or resize start waiting for draw"
+ if (getOrientationChanging()) {
+ Slog.v(TAG_WM, "Orientation start waiting for draw"
+ ", mDrawState=DRAW_PENDING in " + this
+ ", surfaceController " + winAnimator.mSurfaceController);
}
+ if (dragResizingChanged) {
+ ProtoLog.v(WM_DEBUG_RESIZE,
+ "Resize start waiting for draw, "
+ + "mDrawState=DRAW_PENDING in %s, surfaceController %s",
+ this, winAnimator.mSurfaceController);
+ }
winAnimator.mDrawState = DRAW_PENDING;
- if (mAppToken != null) {
- mAppToken.clearAllDrawn();
+ if (mActivityRecord != null) {
+ mActivityRecord.clearAllDrawn();
}
}
if (!mWmService.mResizingWindows.contains(this)) {
- if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG_WM, "Resizing window " + this);
+ ProtoLog.v(WM_DEBUG_RESIZE, "Resizing window %s", this);
mWmService.mResizingWindows.add(this);
}
} else if (getOrientationChanging()) {
if (isDrawnLw()) {
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Orientation not waiting for draw in "
- + this + ", surfaceController " + winAnimator.mSurfaceController);
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "Orientation not waiting for draw in %s, surfaceController %s", this,
+ winAnimator.mSurfaceController);
setOrientationChanging(false);
mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
- mWmService.mDisplayFreezeTime);
@@ -1394,7 +1413,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
Task getTask() {
- return mAppToken != null ? mAppToken.getTask() : null;
+ return mActivityRecord != null ? mActivityRecord.getTask() : null;
}
TaskStack getStack() {
@@ -1443,14 +1462,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
public long getInputDispatchingTimeoutNanos() {
- return mAppToken != null
- ? mAppToken.mInputDispatchingTimeoutNanos
+ return mActivityRecord != null
+ ? mActivityRecord.mInputDispatchingTimeoutNanos
: WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
}
@Override
public boolean hasAppShownWindows() {
- return mAppToken != null && (mAppToken.firstWindowDrawn || mAppToken.startingDisplayed);
+ return mActivityRecord != null && (mActivityRecord.firstWindowDrawn || mActivityRecord.startingDisplayed);
}
boolean isIdentityMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
@@ -1485,7 +1504,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return wouldBeVisibleIfPolicyIgnored() && isVisibleByPolicy()
// If we don't have a provider, this window isn't used as a window generating
// insets, so nobody can hide it over the inset APIs.
- && (mInsetProvider == null || mInsetProvider.isClientVisible());
+ && (mControllableInsetProvider == null
+ || mControllableInsetProvider.isClientVisible());
}
/**
@@ -1530,7 +1550,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
*/
// TODO: Can we consolidate this with #isVisible() or have a more appropriate name for this?
boolean isWinVisibleLw() {
- return (mAppToken == null || !mAppToken.hiddenRequested || mAppToken.isSelfAnimating())
+ return (mActivityRecord == null || !mActivityRecord.hiddenRequested || mActivityRecord.isSelfAnimating())
&& isVisible();
}
@@ -1558,7 +1578,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* call to IWindowSession.add() and the first relayout().
*/
boolean isVisibleOrAdding() {
- final AppWindowToken atoken = mAppToken;
+ final ActivityRecord atoken = mActivityRecord;
return (mHasSurface || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
&& isVisibleByPolicy() && !isParentWindowHidden()
&& (atoken == null || !atoken.hiddenRequested)
@@ -1574,7 +1594,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (!mHasSurface || mDestroying || !isVisibleByPolicy()) {
return false;
}
- final AppWindowToken atoken = mAppToken;
+ final ActivityRecord atoken = mActivityRecord;
if (atoken != null) {
return ((!isParentWindowHidden() && !atoken.hiddenRequested)
|| isAnimating());
@@ -1598,8 +1618,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* it must be drawn before allDrawn can become true.
*/
boolean isInteresting() {
- return mAppToken != null && !mAppDied
- && (!mAppToken.isFreezingScreen() || !mAppFreezing)
+ return mActivityRecord != null && !mAppDied
+ && (!mActivityRecord.isFreezingScreen() || !mAppFreezing)
&& mViewVisibility == View.VISIBLE;
}
@@ -1624,14 +1644,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (translucent) {
return false;
}
- if (mAppToken == null) {
+ if (mActivityRecord == null) {
final boolean shown = mWinAnimator.getShown();
final boolean exiting = mAnimatingExit || mDestroying;
return shown && !exiting;
} else {
final Task task = getTask();
final boolean canFromTask = task != null && task.canAffectSystemUiFlags();
- return canFromTask && !mAppToken.isHidden();
+ return canFromTask && !mActivityRecord.isHidden();
}
}
@@ -1641,7 +1661,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
*/
@Override
public boolean isDisplayedLw() {
- final AppWindowToken atoken = mAppToken;
+ final ActivityRecord atoken = mActivityRecord;
return isDrawnLw() && isVisibleByPolicy()
&& ((!isParentWindowHidden() && (atoken == null || !atoken.hiddenRequested))
|| isAnimating());
@@ -1657,7 +1677,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
public boolean isGoneForLayoutLw() {
- final AppWindowToken atoken = mAppToken;
+ final ActivityRecord atoken = mActivityRecord;
return mViewVisibility == View.GONE
|| !mRelayoutCalled
|| (atoken == null && mToken.isHidden())
@@ -1703,7 +1723,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
void onMovedByResize() {
- if (DEBUG_RESIZE) Slog.d(TAG, "onMovedByResize: Moving " + this);
+ ProtoLog.d(WM_DEBUG_RESIZE, "onMovedByResize: Moving %s", this);
mMovedByResize = true;
super.onMovedByResize();
}
@@ -1720,7 +1740,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Starting window that's exiting will be removed when the animation finishes.
// Mark all relevant flags for that onExitAnimationDone will proceed all the way
// to actually remove it.
- if (!visible && isVisibleNow() && mAppToken.isSelfAnimating()) {
+ if (!visible && isVisibleNow() && mActivityRecord.isSelfAnimating()) {
mAnimatingExit = true;
mRemoveOnExit = true;
mWindowRemovalAllowed = true;
@@ -1777,7 +1797,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
void onResize() {
final ArrayList<WindowState> resizingWindows = mWmService.mResizingWindows;
if (mHasSurface && !isGoneForLayoutLw() && !resizingWindows.contains(this)) {
- if (DEBUG_RESIZE) Slog.d(TAG, "onResize: Resizing " + this);
+ ProtoLog.d(WM_DEBUG_RESIZE, "onResize: Resizing %s", this);
resizingWindows.add(this);
}
if (isGoneForLayoutLw()) {
@@ -1920,8 +1940,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (mRemoved) {
// Nothing to do.
- if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
- "WS.removeImmediately: " + this + " Already removed...");
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE,
+ "WS.removeImmediately: %s Already removed...", this);
return;
}
@@ -1971,39 +1991,35 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
private void removeIfPossible(boolean keepVisibleDeadWindow) {
mWindowRemovalAllowed = true;
- if (DEBUG_ADD_REMOVE) Slog.v(TAG,
- "removeIfPossible: " + this + " callers=" + Debug.getCallers(5));
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE,
+ "removeIfPossible: %s callers=%s", this, Debug.getCallers(5));
final boolean startingWindow = mAttrs.type == TYPE_APPLICATION_STARTING;
- if (startingWindow && DEBUG_STARTING_WINDOW) Slog.d(TAG_WM,
- "Starting window removed " + this);
-
- if (DEBUG || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && isFocused()) {
- Slog.v(TAG_WM, "Remove " + this + " client="
- + Integer.toHexString(System.identityHashCode(mClient.asBinder()))
- + ", surfaceController=" + mWinAnimator.mSurfaceController + " Callers="
- + Debug.getCallers(5));
+ if (startingWindow) {
+ ProtoLog.d(WM_DEBUG_STARTING_WINDOW, "Starting window removed %s", this);
}
+ ProtoLog.v(WM_DEBUG_FOCUS, "Remove client=%x, surfaceController=%s Callers=%s",
+ System.identityHashCode(mClient.asBinder()),
+ mWinAnimator.mSurfaceController,
+ Debug.getCallers(5));
+
+
final long origId = Binder.clearCallingIdentity();
try {
disposeInputChannel();
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Remove " + this
- + ": mSurfaceController=" + mWinAnimator.mSurfaceController
- + " mAnimatingExit=" + mAnimatingExit
- + " mRemoveOnExit=" + mRemoveOnExit
- + " mHasSurface=" + mHasSurface
- + " surfaceShowing=" + mWinAnimator.getShown()
- + " animating=" + isAnimating()
- + " app-animation="
- + (mAppToken != null ? mAppToken.isSelfAnimating() : "false")
- + " mWillReplaceWindow=" + mWillReplaceWindow
- + " inPendingTransaction="
- + (mAppToken != null ? mAppToken.inPendingTransaction : false)
- + " mDisplayFrozen=" + mWmService.mDisplayFrozen
- + " callers=" + Debug.getCallers(6));
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+ "Remove %s: mSurfaceController=%s mAnimatingExit=%b mRemoveOnExit=%b "
+ + "mHasSurface=%b surfaceShowing=%b animating=%b app-animation=%b "
+ + "mWillReplaceWindow=%b inPendingTransaction=%b mDisplayFrozen=%b "
+ + "callers=%s",
+ this, mWinAnimator.mSurfaceController, mAnimatingExit, mRemoveOnExit,
+ mHasSurface, mWinAnimator.getShown(), isAnimating(),
+ mActivityRecord != null && mActivityRecord.isSelfAnimating(), mWillReplaceWindow,
+ mActivityRecord != null && mActivityRecord.inPendingTransaction,
+ mWmService.mDisplayFrozen, Debug.getCallers(6));
// Visibility of the removed window. Will be used later to update orientation later on.
boolean wasVisible = false;
@@ -2015,8 +2031,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (mWillReplaceWindow) {
// This window is going to be replaced. We need to keep it around until the new one
// gets added, then we will get rid of this one.
- if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
- "Preserving " + this + " until the new one is " + "added");
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE,
+ "Preserving %s until the new one is added", this);
// TODO: We are overloading mAnimatingExit flag to prevent the window state from
// been removed. We probably need another flag to indicate that window removal
// should be deffered vs. overloading the flag that says we are playing an exit
@@ -2030,8 +2046,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
wasVisible = isWinVisibleLw();
if (keepVisibleDeadWindow) {
- if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
- "Not removing " + this + " because app died while it's visible");
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE,
+ "Not removing %s because app died while it's visible", this);
mAppDied = true;
setDisplayLayoutNeeded();
@@ -2061,9 +2077,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
}
final boolean isAnimating = isAnimating()
- && (mAppToken == null || !mAppToken.isWaitingForTransitionStart());
- final boolean lastWindowIsStartingWindow = startingWindow && mAppToken != null
- && mAppToken.isLastWindow(this);
+ && (mActivityRecord == null || !mActivityRecord.isWaitingForTransitionStart());
+ final boolean lastWindowIsStartingWindow = startingWindow && mActivityRecord != null
+ && mActivityRecord.isLastWindow(this);
// We delay the removal of a window if it has a showing surface that can be used to run
// exit animation and it is marked as exiting.
// Also, If isn't the an animating starting window that is the last window in the app.
@@ -2072,11 +2088,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (mWinAnimator.getShown() && mAnimatingExit
&& (!lastWindowIsStartingWindow || isAnimating)) {
// The exit animation is running or should run... wait for it!
- if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
- "Not removing " + this + " due to exit animation ");
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE,
+ "Not removing %s due to exit animation", this);
setupWindowForRemoveOnExit();
- if (mAppToken != null) {
- mAppToken.updateReportedVisibilityLocked();
+ if (mActivityRecord != null) {
+ mActivityRecord.updateReportedVisibilityLocked();
}
return;
}
@@ -2124,7 +2140,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return false;
}
- final boolean windowsAreFocusable = mAppToken == null || mAppToken.windowsAreFocusable();
+ 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;
@@ -2157,8 +2173,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
+ " policyVisAfterAnim=" + mLegacyPolicyVisibilityAfterAnim
+ " parentHidden=" + isParentWindowHidden()
+ " exiting=" + mAnimatingExit + " destroying=" + mDestroying);
- if (mAppToken != null) {
- Slog.i(TAG_WM, " mAppToken.hiddenRequested=" + mAppToken.hiddenRequested);
+ if (mActivityRecord != null) {
+ Slog.i(TAG_WM, " mActivityRecord.hiddenRequested=" + mActivityRecord.hiddenRequested);
}
}
}
@@ -2187,7 +2203,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
mInputChannel = inputChannels[0];
mClientChannel = inputChannels[1];
- mInputWindowHandle.token = mClient.asBinder();
+ mWmService.mInputManager.registerInputChannel(mInputChannel);
+ mClientChannel.setToken(mInputChannel.getToken());
+ mInputWindowHandle.token = mInputChannel.getToken();
if (outInputChannel != null) {
mClientChannel.transferTo(outInputChannel);
mClientChannel.dispose();
@@ -2198,7 +2216,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Create dummy event receiver that simply reports all events as handled.
mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
}
- mWmService.mInputManager.registerInputChannel(mInputChannel, mClient.asBinder());
+ mWmService.mInputToWindowMap.put(mInputWindowHandle.token, this);
}
void disposeInputChannel() {
@@ -2218,6 +2236,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mClientChannel.dispose();
mClientChannel = null;
}
+ mWmService.mKeyInterceptionInfoForToken.remove(mInputWindowHandle.token);
+ mWmService.mInputToWindowMap.remove(mInputWindowHandle.token);
mInputWindowHandle.token = null;
}
@@ -2239,7 +2259,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
private void removeReplacedWindow() {
- if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replaced window: " + this);
+ ProtoLog.d(WM_DEBUG_ADD_REMOVE, "Removing replaced window: %s", this);
mWillReplaceWindow = false;
mAnimateReplacingWindow = false;
mReplacingRemoveRequested = false;
@@ -2299,8 +2319,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final Region region = inputWindowHandle.touchableRegion;
setTouchableRegionCropIfNeeded(inputWindowHandle);
- final Rect appOverrideBounds = mAppToken != null
- ? mAppToken.getResolvedOverrideBounds() : null;
+ final Rect appOverrideBounds = mActivityRecord != null
+ ? mActivityRecord.getResolvedOverrideBounds() : null;
if (appOverrideBounds != null && !appOverrideBounds.isEmpty()) {
// There may have touchable letterboxes around the activity, so in order to let the
// letterboxes are able to receive touch event and slip to activity, the activity with
@@ -2313,22 +2333,22 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Non-modal uses the application based frame.
mTmpRect.set(mWindowFrames.mCompatFrame);
}
- // The offset of compatibility bounds is applied to surface of {@link #AppWindowToken}
+ // The offset of compatibility bounds is applied to surface of {@link #ActivityRecord}
// and frame, so it is unnecessary to translate twice in surface based coordinates.
- final int surfaceOffsetX = mAppToken.inSizeCompatMode()
- ? mAppToken.getBounds().left : 0;
+ final int surfaceOffsetX = mActivityRecord.hasSizeCompatBounds()
+ ? mActivityRecord.getBounds().left : 0;
mTmpRect.offset(surfaceOffsetX - mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top);
region.set(mTmpRect);
return flags;
}
- if (modal && mAppToken != null) {
+ if (modal && mActivityRecord != null) {
// Limit the outer touch to the activity stack region.
flags |= FLAG_NOT_TOUCH_MODAL;
// If the inner bounds of letterbox is available, then it will be used as the touchable
// region so it won't cover the touchable letterbox and the touch events can slip to
// activity from letterbox.
- mAppToken.getLetterboxInnerBounds(mTmpRect);
+ mActivityRecord.getLetterboxInnerBounds(mTmpRect);
if (mTmpRect.isEmpty()) {
// If this is a modal window we need to dismiss it if it's not full screen and the
// touch happens outside of the frame that displays the content. This means we need
@@ -2395,7 +2415,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (!isVisibleByPolicy()) {
mWinAnimator.hide("checkPolicyVisibilityChange");
if (isFocused()) {
- if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
+ ProtoLog.i(WM_DEBUG_FOCUS_LIGHT,
"setAnimationLocked: setting mFocusMayChange true");
mWmService.mFocusMayChange = true;
}
@@ -2419,15 +2439,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
void prepareWindowToDisplayDuringRelayout(boolean wasVisible) {
// We need to turn on screen regardless of visibility.
final boolean hasTurnScreenOnFlag = (mAttrs.flags & FLAG_TURN_SCREEN_ON) != 0
- || (mAppToken != null && mAppToken.mActivityRecord.canTurnScreenOn());
+ || (mActivityRecord != null && mActivityRecord.canTurnScreenOn());
// The screen will turn on if the following conditions are met
// 1. The window has the flag FLAG_TURN_SCREEN_ON or ActivityRecord#canTurnScreenOn.
// 2. The WMS allows theater mode.
// 3. No AWT or the AWT allows the screen to be turned on. This should only be true once
// per resume to prevent the screen getting getting turned on for each relayout. Set
- // canTurnScreenOn will be set to false so the window doesn't turn the screen on again
- // during this resume.
+ // currentLaunchCanTurnScreenOn will be set to false so the window doesn't turn the screen
+ // on again during this resume.
// 4. When the screen is not interactive. This is because when the screen is already
// interactive, the value may persist until the next animation, which could potentially
// be occurring while turning off the screen. This would lead to the screen incorrectly
@@ -2436,7 +2456,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
boolean allowTheaterMode = mWmService.mAllowTheaterModeWakeFromLayout
|| Settings.Global.getInt(mWmService.mContext.getContentResolver(),
Settings.Global.THEATER_MODE_ON, 0) == 0;
- boolean canTurnScreenOn = mAppToken == null || mAppToken.currentLaunchCanTurnScreenOn();
+ boolean canTurnScreenOn = mActivityRecord == null || mActivityRecord.currentLaunchCanTurnScreenOn();
if (allowTheaterMode && canTurnScreenOn && !mPowerManagerWrapper.isInteractive()) {
if (DEBUG_VISIBILITY || DEBUG_POWER) {
@@ -2446,8 +2466,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
PowerManager.WAKE_REASON_APPLICATION, "android.server.wm:SCREEN_ON_FLAG");
}
- if (mAppToken != null) {
- mAppToken.setCurrentLaunchCanTurnScreenOn(false);
+ if (mActivityRecord != null) {
+ mActivityRecord.setCurrentLaunchCanTurnScreenOn(false);
}
}
@@ -2498,14 +2518,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
void adjustStartingWindowFlags() {
- if (mAttrs.type == TYPE_BASE_APPLICATION && mAppToken != null
- && mAppToken.startingWindow != null) {
+ if (mAttrs.type == TYPE_BASE_APPLICATION && mActivityRecord != null
+ && mActivityRecord.startingWindow != null) {
// Special handling of starting window over the base
// window of the app: propagate lock screen flags to it,
// to provide the correct semantics while starting.
final int mask = FLAG_SHOW_WHEN_LOCKED | FLAG_DISMISS_KEYGUARD
| FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
- WindowManager.LayoutParams sa = mAppToken.startingWindow.mAttrs;
+ WindowManager.LayoutParams sa = mActivityRecord.startingWindow.mAttrs;
sa.flags = (sa.flags & ~mask) | (mAttrs.flags & mask);
}
}
@@ -2537,8 +2557,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
Slog.i(TAG, "WIN DEATH: " + win);
if (win != null) {
final DisplayContent dc = getDisplayContent();
- if (win.mAppToken != null && win.mAppToken.findMainWindow() == win) {
- mWmService.mTaskSnapshotController.onAppDied(win.mAppToken);
+ if (win.mActivityRecord != null && win.mActivityRecord.findMainWindow() == win) {
+ mWmService.mTaskSnapshotController.onAppDied(win.mActivityRecord);
}
win.removeIfPossible(shouldKeepVisibleDeadAppWindow());
if (win.mAttrs.type == TYPE_DOCK_DIVIDER) {
@@ -2580,7 +2600,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* interacts with it.
*/
private boolean shouldKeepVisibleDeadAppWindow() {
- if (!isWinVisibleLw() || mAppToken == null || mAppToken.isClientHidden()) {
+ if (!isWinVisibleLw() || mActivityRecord == null || mActivityRecord.isClientHidden()) {
// Not a visible app window or the app isn't dead.
return false;
}
@@ -2605,22 +2625,38 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return isVisibleOrAdding()
&& (mViewVisibility == View.VISIBLE) && !mRemoveOnExit
&& ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
- && (mAppToken == null || mAppToken.windowsAreFocusable())
+ && (mActivityRecord == null || mActivityRecord.windowsAreFocusable())
&& !cantReceiveTouchInput();
}
@Override
public boolean canShowWhenLocked() {
final boolean showBecauseOfActivity =
- mAppToken != null && mAppToken.mActivityRecord.canShowWhenLocked();
+ mActivityRecord != null && mActivityRecord.canShowWhenLocked();
final boolean showBecauseOfWindow = (getAttrs().flags & FLAG_SHOW_WHEN_LOCKED) != 0;
return showBecauseOfActivity || showBecauseOfWindow;
}
/** @return false if this window desires touch events. */
boolean cantReceiveTouchInput() {
- return mAppToken != null && mAppToken.getTask() != null
- && (mAppToken.getTask().mStack.shouldIgnoreInput() || mAppToken.hiddenRequested);
+ if (mActivityRecord == null || mActivityRecord.getTask() == null) {
+ return false;
+ }
+
+ return mActivityRecord.getTask().mStack.shouldIgnoreInput()
+ || mActivityRecord.hiddenRequested
+ || isAnimatingToRecents();
+ }
+
+ /**
+ * Returns {@code true} if the window is animating to home as part of the recents animation.
+ */
+ private boolean isAnimatingToRecents() {
+ final RecentsAnimationController recentsAnimationController =
+ mWmService.getRecentsAnimationController();
+ return recentsAnimationController != null
+ && recentsAnimationController.isAnimatingTask(getTask())
+ && !recentsAnimationController.isTargetApp(mActivityRecord);
}
@Override
@@ -2718,7 +2754,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// we allow the display to be enabled now.
mWmService.enableScreenIfNeededLocked();
if (isFocused) {
- if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
+ ProtoLog.i(WM_DEBUG_FOCUS_LIGHT,
"WindowState.hideLw: setting mFocusMayChange true");
mWmService.mFocusMayChange = true;
}
@@ -2860,7 +2896,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
boolean isClosing() {
- return mAnimatingExit || (mAppToken != null && mAppToken.isClosingOrEnteringPip());
+ return mAnimatingExit || (mActivityRecord != null && mActivityRecord.isClosingOrEnteringPip());
}
void addWinAnimatorToList(ArrayList<WindowStateAnimator> animators) {
@@ -2875,7 +2911,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
void sendAppVisibilityToClients() {
super.sendAppVisibilityToClients();
- final boolean clientHidden = mAppToken.isClientHidden();
+ final boolean clientHidden = mActivityRecord.isClientHidden();
if (mAttrs.type == TYPE_APPLICATION_STARTING && clientHidden) {
// Don't hide the starting window.
return;
@@ -2924,7 +2960,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (mHasSurface && !getOrientationChanging()
&& mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
- if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "set mOrientationChanging of " + this);
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "set mOrientationChanging of %s", this);
setOrientationChanging(true);
mWmService.mRoot.mOrientationChangeComplete = false;
}
@@ -2952,10 +2989,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
if (mDestroying) {
- if (DEBUG_ADD_REMOVE) Slog.e(TAG_WM, "win=" + this
- + " destroySurfaces: appStopped=" + appStopped
- + " win.mWindowRemovalAllowed=" + mWindowRemovalAllowed
- + " win.mRemoveOnExit=" + mRemoveOnExit);
+ ProtoLog.e(WM_DEBUG_ADD_REMOVE, "win=%s"
+ + " destroySurfaces: appStopped=%b"
+ + " win.mWindowRemovalAllowed=%b"
+ + " win.mRemoveOnExit=%b", this, appStopped,
+ mWindowRemovalAllowed, mRemoveOnExit);
if (!cleanupOnResume || mRemoveOnExit) {
destroySurfaceUnchecked();
}
@@ -2968,10 +3006,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mDestroying = false;
destroyedSomething = true;
- // Since mDestroying will affect AppWindowToken#allDrawn, we need to perform another
+ // Since mDestroying will affect ActivityRecord#allDrawn, we need to perform another
// traversal in case we are waiting on this window to start the transition.
if (getDisplayContent().mAppTransition.isTransitionSet()
- && getDisplayContent().mOpeningApps.contains(mAppToken)) {
+ && getDisplayContent().mOpeningApps.contains(mActivityRecord)) {
mWmService.mWindowPlacerLocked.requestTraversal();
}
}
@@ -3051,7 +3089,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Child windows are evaluated based on their parent window.
final WindowState win = getTopParentWindow();
if (win.mAttrs.type < FIRST_SYSTEM_WINDOW
- && win.mAppToken != null && win.mAppToken.mShowForAllUsers) {
+ && win.mActivityRecord != null && win.mActivityRecord.mShowForAllUsers) {
// All window frames that are fullscreen extend above status bar, but some don't extend
// below navigation bar. Thus, check for display frame for top/left and stable frame for
@@ -3191,8 +3229,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
public Configuration getConfiguration() {
- if (mAppToken != null && mAppToken.mFrozenMergedConfig.size() > 0) {
- return mAppToken.mFrozenMergedConfig.peek();
+ if (mActivityRecord != null && mActivityRecord.mFrozenMergedConfig.size() > 0) {
+ return mActivityRecord.mFrozenMergedConfig.peek();
}
// If the process has not registered to any display to listen to the configuration change,
@@ -3223,16 +3261,19 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
void reportResized() {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.reportResized_" + getWindowTag());
try {
- if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this
- + ": " + mWindowFrames.mCompatFrame);
+ ProtoLog.v(WM_DEBUG_RESIZE,
+ "Reporting new frame to %s: %s", this,
+ mWindowFrames.mCompatFrame);
final MergedConfiguration mergedConfiguration =
new MergedConfiguration(mWmService.mRoot.getConfiguration(),
getMergedOverrideConfiguration());
setLastReportedMergedConfiguration(mergedConfiguration);
- if (DEBUG_ORIENTATION && mWinAnimator.mDrawState == DRAW_PENDING)
- Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING");
+ if (mWinAnimator.mDrawState == DRAW_PENDING) {
+ ProtoLog.i(WM_DEBUG_ORIENTATION,
+ "Resizing %s WITH DRAW PENDING", this);
+ }
final Rect frame = mWindowFrames.mCompatFrame;
final Rect overscanInsets = mWindowFrames.mLastOverscanInsets;
@@ -3322,7 +3363,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
void notifyInsetsChanged() {
try {
mClient.insetsChanged(
- getDisplayContent().getInsetsStateController().getInsetsForDispatch(this));
+ getDisplayContent().getInsetsPolicy().getInsetsForDispatch(this));
} catch (RemoteException e) {
Slog.w(TAG, "Failed to deliver inset state change", e);
}
@@ -3332,14 +3373,38 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
public void notifyInsetsControlChanged() {
final InsetsStateController stateController =
getDisplayContent().getInsetsStateController();
+ final InsetsPolicy policy = getDisplayContent().getInsetsPolicy();
try {
- mClient.insetsControlChanged(stateController.getInsetsForDispatch(this),
+ mClient.insetsControlChanged(policy.getInsetsForDispatch(this),
stateController.getControlsForDispatch(this));
} catch (RemoteException e) {
Slog.w(TAG, "Failed to deliver inset state change", e);
}
}
+ @Override
+ public void showInsets(@InsetType int types, boolean fromIme) {
+ try {
+ mClient.showInsets(types, fromIme);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to deliver showInsets", e);
+ }
+ }
+
+ @Override
+ public void hideInsets(@InsetType int types, boolean fromIme) {
+ try {
+ mClient.hideInsets(types, fromIme);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to deliver showInsets", e);
+ }
+ }
+
+ @Override
+ public boolean canShowTransient() {
+ return (mAttrs.insetsFlags.behavior & BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) != 0;
+ }
+
Rect getBackdropFrame(Rect frame) {
// When the task is docked, we send fullscreen sized backDropFrame as soon as resizing
// start even if we haven't received the relayout window, so that the client requests
@@ -3407,7 +3472,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
/** Is this window in a container that takes up the entire screen space? */
private boolean inAppWindowThatMatchesParentBounds() {
- return mAppToken == null || (mAppToken.matchParentBounds() && !inMultiWindowMode());
+ return mActivityRecord == null || (mActivityRecord.matchParentBounds() && !inMultiWindowMode());
}
/** @return true when the window is in fullscreen mode, but has non-fullscreen bounds set, or
@@ -3419,8 +3484,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
public boolean isLetterboxedForDisplayCutoutLw() {
- if (mAppToken == null) {
- // Only windows with an AppWindowToken are letterboxed.
+ if (mActivityRecord == null) {
+ // Only windows with an ActivityRecord are letterboxed.
return false;
}
if (!mWindowFrames.parentFrameWasClippedByDisplayCutout()) {
@@ -3445,14 +3510,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* @throws NullPointerException if there is no app window token for this window
*/
private boolean frameCoversEntireAppTokenBounds() {
- mTmpRect.set(mAppToken.getBounds());
+ mTmpRect.set(mActivityRecord.getBounds());
mTmpRect.intersectUnchecked(mWindowFrames.mFrame);
- return mAppToken.getBounds().equals(mTmpRect);
+ return mActivityRecord.getBounds().equals(mTmpRect);
}
@Override
public boolean isLetterboxedOverlappingWith(Rect rect) {
- return mAppToken != null && mAppToken.isLetterboxOverlappingWith(rect);
+ return mActivityRecord != null && mActivityRecord.isLetterboxOverlappingWith(rect);
}
boolean isDragResizeChanged() {
@@ -3462,7 +3527,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
void setWaitingForDrawnIfResizingChanged() {
if (isDragResizeChanged()) {
- mWmService.mWaitingForDrawn.add(this);
+ mWmService.mRoot.mWaitingForDrawn.add(this);
}
super.setWaitingForDrawnIfResizingChanged();
}
@@ -3508,7 +3573,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// simulate that we are still resizing so the app fills the hole with the resizing
// background.
return (getDisplayContent().mDividerControllerLocked.isResizing()
- || mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) &&
+ || mActivityRecord != null && !mActivityRecord.mFrozenBounds.isEmpty()) &&
!task.inFreeformWindowingMode() && !isGoneForLayoutLw();
}
@@ -3628,8 +3693,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
if (dumpAll) {
pw.println(prefix + "mToken=" + mToken);
- if (mAppToken != null) {
- pw.println(prefix + "mAppToken=" + mAppToken);
+ if (mActivityRecord != null) {
+ pw.println(prefix + "mActivityRecord=" + mActivityRecord);
pw.print(prefix + "mAppDied=" + mAppDied);
pw.print(prefix + "drawnStateEvaluated=" + getDrawnStateEvaluated());
pw.println(prefix + "mightAffectAllDrawn=" + mightAffectAllDrawn());
@@ -4031,8 +4096,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
public int getRotationAnimationHint() {
- if (mAppToken != null) {
- return mAppToken.mRotationAnimationHint;
+ if (mActivityRecord != null) {
+ return mActivityRecord.mRotationAnimationHint;
} else {
return -1;
}
@@ -4055,8 +4120,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final int drawState = mWinAnimator.mDrawState;
if ((drawState == HAS_DRAWN || drawState == READY_TO_SHOW)
- && mAttrs.type != TYPE_APPLICATION_STARTING && mAppToken != null) {
- mAppToken.onFirstWindowDrawn(this, mWinAnimator);
+ && mAttrs.type != TYPE_APPLICATION_STARTING && mActivityRecord != null) {
+ mActivityRecord.onFirstWindowDrawn(this, mWinAnimator);
}
if (mWinAnimator.mDrawState != READY_TO_SHOW || !isReadyForDisplay()) {
@@ -4110,11 +4175,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
+ " during animation: policyVis=" + isVisibleByPolicy()
+ " parentHidden=" + isParentWindowHidden()
+ " tok.hiddenRequested="
- + (mAppToken != null && mAppToken.hiddenRequested)
- + " tok.hidden=" + (mAppToken != null && mAppToken.isHidden())
+ + (mActivityRecord != null && mActivityRecord.hiddenRequested)
+ + " tok.hidden=" + (mActivityRecord != null && mActivityRecord.isHidden())
+ " animating=" + isAnimating()
+ " tok animating="
- + (mAppToken != null && mAppToken.isSelfAnimating())
+ + (mActivityRecord != null && mActivityRecord.isSelfAnimating())
+ " Callers=" + Debug.getCallers(4));
}
}
@@ -4125,8 +4190,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
windowInfo.type = mAttrs.type;
windowInfo.layer = mLayer;
windowInfo.token = mClient.asBinder();
- if (mAppToken != null) {
- windowInfo.activityToken = mAppToken.appToken.asBinder();
+ if (mActivityRecord != null) {
+ windowInfo.activityToken = mActivityRecord.appToken.asBinder();
}
windowInfo.title = mAttrs.accessibilityTitle;
// Panel windows have no public way to set the a11y title directly. Use the
@@ -4356,7 +4421,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mWmService.requestTraversal();
// System windows don't have an activity and an app token as a result, but need a way
// to be informed about their entrance animation end.
- if (mAppToken == null) {
+ if (mActivityRecord == null) {
try {
mClient.dispatchWindowShown();
} catch (RemoteException e) {
@@ -4375,9 +4440,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return;
}
- if (DEBUG || DEBUG_ADD_REMOVE) {
- Slog.v(TAG, "Exit animation finished in " + this + ": remove=" + mRemoveOnExit);
- }
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Exit animation finished in %s: remove=%b",
+ this, mRemoveOnExit);
mDestroying = true;
@@ -4391,8 +4455,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// care to ensure the activity has actually stopped and the surface is not still in use.
// Otherwise we add the service to mDestroySurface and allow it to be processed in our next
// transaction.
- if (mAppToken != null) {
- mAppToken.destroySurfaces();
+ if (mActivityRecord != null) {
+ mActivityRecord.destroySurfaces();
} else {
if (hasSurface) {
mWmService.mDestroySurface.add(this);
@@ -4519,7 +4583,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
+ " pv=" + isVisibleByPolicy()
+ " mDrawState=" + mWinAnimator.mDrawState
+ " ph=" + isParentWindowHidden()
- + " th=" + (mAppToken != null ? mAppToken.hiddenRequested : false)
+ + " th=" + (mActivityRecord != null ? mActivityRecord.hiddenRequested : false)
+ " a=" + isAnimating());
}
}
@@ -4545,7 +4609,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
// But if we have a frame, and are an application window, then we must be cropped.
- if (mAppToken != null) {
+ if (mActivityRecord != null) {
return false;
}
@@ -4769,7 +4833,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
void startAnimation(Animation anim) {
// If we are an inset provider, all our animations are driven by the inset client.
- if (mInsetProvider != null && mInsetProvider.isControllable()) {
+ if (mControllableInsetProvider != null) {
return;
}
@@ -4789,7 +4853,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
private void startMoveAnimation(int left, int top) {
// If we are an inset provider, all our animations are driven by the inset client.
- if (mInsetProvider != null && mInsetProvider.isControllable()) {
+ if (mControllableInsetProvider != null) {
return;
}
@@ -4860,9 +4924,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
outMatrix.setValues(float9);
}
- // TODO: Hack to work around the number of states AppWindowToken needs to access without having
+ // TODO: Hack to work around the number of states ActivityRecord needs to access without having
// access to its windows children. Need to investigate re-writing
- // {@link AppWindowToken#updateReportedVisibilityLocked} so this can be removed.
+ // {@link ActivityRecord#updateReportedVisibilityLocked} so this can be removed.
static final class UpdateReportedVisibilityResults {
int numInteresting;
int numVisible;
@@ -4944,9 +5008,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
boolean needsZBoost() {
final WindowState inputMethodTarget = getDisplayContent().mInputMethodTarget;
if (mIsImWindow && inputMethodTarget != null) {
- final AppWindowToken appToken = inputMethodTarget.mAppToken;
- if (appToken != null) {
- return appToken.needsZBoost();
+ final ActivityRecord activity = inputMethodTarget.mActivityRecord;
+ if (activity != null) {
+ return activity.needsZBoost();
}
}
return mWillReplaceWindow;
@@ -5086,7 +5150,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (getParentWindow().isInputMethodTarget()) {
return true;
}
- } else if (mAppToken != null) {
+ } else if (mActivityRecord != null) {
// Likewise if we share a token with the Input method target and are ordered
// above it but not necessarily a child (e.g. a Dialog) then we also need
// this promotion.
@@ -5273,12 +5337,22 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mWindowFrames.setContentChanged(false);
}
- void setInsetProvider(InsetsSourceProvider insetProvider) {
- mInsetProvider = insetProvider;
+ /**
+ * Set's an {@link InsetsSourceProvider} to be associated with this window, but only if the
+ * provider itself is controllable, as one window can be the provider of more than one inset
+ * type (i.e. gesture insets). If this window is controllable, all its animations must be
+ * controlled by its control target, and the visibility of this window should be taken account
+ * into the state of the control target.
+ *
+ * @param insetProvider the provider which should not be visible to the client.
+ * @see InsetsStateController#getInsetsForDispatch(WindowState)
+ */
+ void setControllableInsetProvider(InsetsSourceProvider insetProvider) {
+ mControllableInsetProvider = insetProvider;
}
- InsetsSourceProvider getInsetProvider() {
- return mInsetProvider;
+ InsetsSourceProvider getControllableInsetProvider() {
+ return mControllableInsetProvider;
}
private final class MoveAnimationSpec implements AnimationSpec {
@@ -5327,4 +5401,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
proto.end(token);
}
}
+
+ KeyInterceptionInfo getKeyInterceptionInfo() {
+ if (mKeyInterceptionInfo == null
+ || mKeyInterceptionInfo.layoutParamsPrivateFlags != getAttrs().privateFlags
+ || mKeyInterceptionInfo.layoutParamsType != getAttrs().type
+ || mKeyInterceptionInfo.windowTitle != getWindowTag()) {
+ mKeyInterceptionInfo = new KeyInterceptionInfo(getAttrs().type, getAttrs().privateFlags,
+ getWindowTag().toString());
+ }
+ return mKeyInterceptionInfo;
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index c676e723de71..3f25f89c1a3f 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -27,17 +27,18 @@ import static android.view.WindowManager.TRANSIT_NONE;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_DRAW;
+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.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
+import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_CROP;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
@@ -67,6 +68,7 @@ import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.protolog.common.ProtoLog;
import java.io.PrintWriter;
@@ -264,7 +266,7 @@ class WindowStateAnimator {
if (DEBUG_ANIM) Slog.v(
TAG, "Animation done in " + this + ": exiting=" + mWin.mAnimatingExit
+ ", reportedVisible="
- + (mWin.mAppToken != null ? mWin.mAppToken.reportedVisible : false));
+ + (mWin.mActivityRecord != null ? mWin.mActivityRecord.reportedVisible : false));
mWin.checkPolicyVisibilityChange();
final DisplayContent displayContent = mWin.getDisplayContent();
@@ -283,8 +285,8 @@ class WindowStateAnimator {
"WindowStateAnimator", displayContent.pendingLayoutChanges);
}
- if (mWin.mAppToken != null) {
- mWin.mAppToken.updateReportedVisibilityLocked();
+ if (mWin.mActivityRecord != null) {
+ mWin.mActivityRecord.updateReportedVisibilityLocked();
}
}
@@ -312,19 +314,19 @@ class WindowStateAnimator {
boolean finishDrawingLocked(SurfaceControl.Transaction postDrawTransaction) {
final boolean startingWindow =
mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
- if (DEBUG_STARTING_WINDOW && startingWindow) {
- Slog.v(TAG, "Finishing drawing window " + mWin + ": mDrawState="
- + drawStateToString());
+ if (startingWindow) {
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Finishing drawing window %s: mDrawState=%s",
+ mWin, drawStateToString());
}
boolean layoutNeeded = false;
if (mDrawState == DRAW_PENDING) {
- if (DEBUG_ANIM || SHOW_TRANSACTIONS || DEBUG_ORIENTATION)
- Slog.v(TAG, "finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING " + mWin + " in "
- + mSurfaceController);
- if (DEBUG_STARTING_WINDOW && startingWindow) {
- Slog.v(TAG, "Draw state now committed in " + mWin);
+ ProtoLog.v(WM_DEBUG_DRAW,
+ "finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING %s in %s", mWin,
+ mSurfaceController);
+ if (startingWindow) {
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Draw state now committed in %s", mWin);
}
mDrawState = COMMIT_DRAW_PENDING;
layoutNeeded = true;
@@ -356,8 +358,8 @@ class WindowStateAnimator {
}
mDrawState = READY_TO_SHOW;
boolean result = false;
- final AppWindowToken atoken = mWin.mAppToken;
- if (atoken == null || atoken.canShowWindows()
+ final ActivityRecord activity = mWin.mActivityRecord;
+ if (activity == null || activity.canShowWindows()
|| mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
result = mWin.performShowLocked();
}
@@ -385,7 +387,7 @@ class WindowStateAnimator {
mSurfaceDestroyDeferred = true;
return;
}
- if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin, "SET FREEZE LAYER", false);
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE SET FREEZE LAYER: %s", mWin);
if (mSurfaceController != null) {
// Our SurfaceControl is always at layer 0 within the parent Surface managed by
// window-state. We want this old Surface to stay on top of the new one
@@ -407,7 +409,7 @@ class WindowStateAnimator {
// we are just doing an in-place switch. In that case any SurfaceFlinger side
// child layers need to be reparented to the new surface to make this
// transparent to the app.
- if (mWin.mAppToken == null || mWin.mAppToken.isRelaunching() == false) {
+ if (mWin.mActivityRecord == null || mWin.mActivityRecord.isRelaunching() == false) {
mPostDrawTransaction.reparentChildren(mPendingDestroySurface.mSurfaceControl,
mSurfaceController.mSurfaceControl)
.apply();
@@ -429,16 +431,16 @@ class WindowStateAnimator {
void resetDrawState() {
mDrawState = DRAW_PENDING;
- if (mWin.mAppToken == null) {
+ if (mWin.mActivityRecord == null) {
return;
}
- if (!mWin.mAppToken.isSelfAnimating()) {
- mWin.mAppToken.clearAllDrawn();
+ if (!mWin.mActivityRecord.isSelfAnimating()) {
+ mWin.mActivityRecord.clearAllDrawn();
} else {
// Currently animating, persist current state of allDrawn until animation
// is complete.
- mWin.mAppToken.deferClearAllDrawn = true;
+ mWin.mActivityRecord.deferClearAllDrawn = true;
}
}
@@ -456,8 +458,9 @@ class WindowStateAnimator {
w.setHasSurface(false);
- if (DEBUG_ANIM || DEBUG_ORIENTATION) Slog.i(TAG,
- "createSurface " + this + ": mDrawState=DRAW_PENDING");
+ if (DEBUG_ANIM) {
+ Slog.i(TAG, "createSurface " + this + ": mDrawState=DRAW_PENDING");
+ }
resetDrawState();
@@ -503,9 +506,8 @@ class WindowStateAnimator {
flags |= SurfaceControl.OPAQUE;
}
- mSurfaceController = new WindowSurfaceController(mSession.mSurfaceSession,
- attrs.getTitle().toString(), width, height, format, flags, this,
- windowType, ownerUid);
+ mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), width,
+ height, format, flags, this, windowType, ownerUid);
mSurfaceController.setColorSpaceAgnostic((attrs.privateFlags
& WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0);
@@ -514,15 +516,10 @@ class WindowStateAnimator {
w.setHasSurface(true);
- if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
- Slog.i(TAG, " CREATE SURFACE "
- + mSurfaceController + " IN SESSION "
- + mSession.mSurfaceSession
- + ": pid=" + mSession.mPid + " format="
- + attrs.format + " flags=0x"
- + Integer.toHexString(flags)
- + " / " + this);
- }
+ ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
+ " CREATE SURFACE %s IN SESSION %s: pid=%d format=%d flags=0x%x / %s",
+ mSurfaceController, mSession.mSurfaceSession, mSession.mPid, attrs.format,
+ flags, this);
} catch (OutOfResourcesException e) {
Slog.w(TAG, "OutOfResourcesException creating surface");
mService.mRoot.reclaimSomeSurfaceMemory(this, "create", true);
@@ -591,10 +588,10 @@ class WindowStateAnimator {
}
void destroySurfaceLocked() {
- final AppWindowToken wtoken = mWin.mAppToken;
- if (wtoken != null) {
- if (mWin == wtoken.startingWindow) {
- wtoken.startingDisplayed = false;
+ final ActivityRecord activity = mWin.mActivityRecord;
+ if (activity != null) {
+ if (mWin == activity.startingWindow) {
+ activity.startingDisplayed = false;
}
}
@@ -616,17 +613,15 @@ class WindowStateAnimator {
if (mSurfaceDestroyDeferred) {
if (mSurfaceController != null && mPendingDestroySurface != mSurfaceController) {
if (mPendingDestroySurface != null) {
- if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
- WindowManagerService.logSurface(mWin, "DESTROY PENDING", true);
- }
+ ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY PENDING: %s. %s",
+ mWin, new RuntimeException().fillInStackTrace());
mPendingDestroySurface.destroyNotInTransaction();
}
mPendingDestroySurface = mSurfaceController;
}
} else {
- if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
- WindowManagerService.logSurface(mWin, "DESTROY", true);
- }
+ ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY: %s. %s",
+ mWin, new RuntimeException().fillInStackTrace());
destroySurface();
}
// Don't hide wallpaper if we're deferring the surface destroy
@@ -653,9 +648,8 @@ class WindowStateAnimator {
void destroyDeferredSurfaceLocked() {
try {
if (mPendingDestroySurface != null) {
- if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
- WindowManagerService.logSurface(mWin, "DESTROY PENDING", true);
- }
+ ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY PENDING: %s. %s",
+ mWin, new RuntimeException().fillInStackTrace());
mPendingDestroySurface.destroyNotInTransaction();
// Don't hide wallpaper if we're destroying a deferred surface
// after a surface mode change.
@@ -673,9 +667,8 @@ class WindowStateAnimator {
}
void computeShownFrameLocked() {
- final int displayId = mWin.getDisplayId();
final ScreenRotationAnimation screenRotationAnimation =
- mAnimator.getScreenRotationAnimationLocked(displayId);
+ mWin.getDisplayContent().getRotationAnimation();
final boolean windowParticipatesInScreenRotationAnimation =
!mWin.mForceSeamlesslyRotate;
final boolean screenAnimation = screenRotationAnimation != null
@@ -1089,9 +1082,7 @@ class WindowStateAnimator {
// There is no need to wait for an animation change if our window is gone for layout
// already as we'll never be visible.
if (w.getOrientationChanging() && w.isGoneForLayoutLw()) {
- if (DEBUG_ORIENTATION) {
- Slog.v(TAG, "Orientation change skips hidden " + w);
- }
+ ProtoLog.v(WM_DEBUG_ORIENTATION, "Orientation change skips hidden %s", w);
w.setOrientationChanging(false);
}
return;
@@ -1116,8 +1107,8 @@ class WindowStateAnimator {
// before it has drawn for the new orientation.
if (w.getOrientationChanging() && w.isGoneForLayoutLw()) {
w.setOrientationChanging(false);
- if (DEBUG_ORIENTATION) Slog.v(TAG,
- "Orientation change skips hidden " + w);
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "Orientation change skips hidden %s", w);
}
} else if (mLastAlpha != mShownAlpha
|| mLastDsDx != mDsDx
@@ -1135,13 +1126,10 @@ class WindowStateAnimator {
mLastDtDy = mDtDy;
w.mLastHScale = w.mHScale;
w.mLastVScale = w.mVScale;
- if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
- "controller=" + mSurfaceController +
- "alpha=" + mShownAlpha
- + " matrix=[" + mDsDx + "*" + w.mHScale
- + "," + mDtDx + "*" + w.mVScale
- + "][" + mDtDy + "*" + w.mHScale
- + "," + mDsDy + "*" + w.mVScale + "]", false);
+ ProtoLog.i(WM_SHOW_TRANSACTIONS,
+ "SURFACE controller=%s alpha=%f matrix=[%f*%f,%f*%f][%f*%f,%f*%f]: %s",
+ mSurfaceController, mShownAlpha, mDsDx, w.mHScale, mDtDx, w.mVScale,
+ mDtDy, w.mHScale, mDsDy, w.mVScale, w);
boolean prepared =
mSurfaceController.prepareToShowInTransaction(mShownAlpha,
@@ -1191,11 +1179,11 @@ class WindowStateAnimator {
if (!w.isDrawnLw()) {
mAnimator.mBulkUpdateParams &= ~SET_ORIENTATION_CHANGE_COMPLETE;
mAnimator.mLastWindowFreezeSource = w;
- if (DEBUG_ORIENTATION) Slog.v(TAG,
- "Orientation continue waiting for draw in " + w);
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "Orientation continue waiting for draw in %s", w);
} else {
w.setOrientationChanging(false);
- if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation change complete in " + w);
+ ProtoLog.v(WM_DEBUG_ORIENTATION, "Orientation change complete in %s", w);
}
}
@@ -1360,13 +1348,16 @@ class WindowStateAnimator {
// frozen, there is no reason to animate and it can cause strange
// artifacts when we unfreeze the display if some different animation
// is running.
- Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#applyAnimationLocked");
if (mWin.mToken.okToAnimate()) {
- int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimationLw(mWin, transit);
+ int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimation(mWin, transit);
int attr = -1;
Animation a = null;
- if (anim != 0) {
- a = anim != -1 ? AnimationUtils.loadAnimation(mContext, anim) : null;
+ if (anim != DisplayPolicy.ANIMATION_STYLEABLE) {
+ if (anim != DisplayPolicy.ANIMATION_NONE) {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#loadAnimation");
+ a = AnimationUtils.loadAnimation(mContext, anim);
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
} else {
switch (transit) {
case WindowManagerPolicy.TRANSIT_ENTER:
@@ -1396,7 +1387,9 @@ class WindowStateAnimator {
+ " isEntrance=" + isEntrance + " Callers " + Debug.getCallers(3));
if (a != null) {
if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this);
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#startAnimation");
mWin.startAnimation(a);
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
mAnimationIsEntrance = isEntrance;
}
} else {
@@ -1407,7 +1400,6 @@ class WindowStateAnimator {
mWin.getDisplayContent().adjustForImeIfNeeded();
}
- Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
return mWin.isAnimating();
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 6d813d106345..0b4ea9927139 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -21,10 +21,10 @@ import static android.view.Surface.SCALING_MODE_SCALE_TO_WINDOW;
import static android.view.SurfaceControl.METADATA_OWNER_UID;
import static android.view.SurfaceControl.METADATA_WINDOW_TYPE;
+import static com.android.server.wm.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
+import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowSurfaceControllerProto.LAYER;
@@ -37,8 +37,10 @@ import android.os.Trace;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl;
-import android.view.SurfaceSession;
import android.view.WindowContentFrameStats;
+import android.view.WindowManager;
+
+import com.android.server.protolog.common.ProtoLog;
import java.io.PrintWriter;
@@ -83,7 +85,7 @@ class WindowSurfaceController {
private final SurfaceControl.Transaction mTmpTransaction;
- public WindowSurfaceController(SurfaceSession s, String name, int w, int h, int format,
+ WindowSurfaceController(String name, int w, int h, int format,
int flags, WindowStateAnimator animator, int windowType, int ownerUid) {
mAnimator = animator;
@@ -107,35 +109,33 @@ class WindowSurfaceController {
.setFlags(flags)
.setMetadata(METADATA_WINDOW_TYPE, windowType)
.setMetadata(METADATA_OWNER_UID, ownerUid);
- mSurfaceControl = b.build();
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- }
- private void logSurface(String msg, RuntimeException where) {
- String str = " SURFACE " + msg + ": " + title;
- if (where != null) {
- Slog.i(TAG, str, where);
- } else {
- Slog.i(TAG, str);
+ if ((win.getAttrs().privateFlags &
+ WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0) {
+ b.setContainerLayer();
}
+
+
+ mSurfaceControl = b.build();
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
void reparentChildrenInTransaction(WindowSurfaceController other) {
- if (SHOW_TRANSACTIONS) Slog.i(TAG, "REPARENT from: " + this + " to: " + other);
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "REPARENT from: %s to: %s", this, other);
if ((mSurfaceControl != null) && (other.mSurfaceControl != null)) {
mSurfaceControl.reparentChildren(other.mSurfaceControl);
}
}
void detachChildren() {
- if (SHOW_TRANSACTIONS) Slog.i(TAG, "SEVER CHILDREN");
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "SEVER CHILDREN");
if (mSurfaceControl != null) {
mSurfaceControl.detachChildren();
}
}
void hide(SurfaceControl.Transaction transaction, String reason) {
- if (SHOW_TRANSACTIONS) logSurface("HIDE ( " + reason + " )", null);
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE HIDE ( %s ): %s", reason, title);
mHiddenForOtherReasons = true;
mAnimator.destroyPreservedSurfaceLocked();
@@ -157,9 +157,8 @@ class WindowSurfaceController {
}
void destroyNotInTransaction() {
- if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
- Slog.i(TAG, "Destroying surface " + this + " called by " + Debug.getCallers(8));
- }
+ ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
+ "Destroying surface %s called by %s", this, Debug.getCallers(8));
try {
if (mSurfaceControl != null) {
mTmpTransaction.remove(mSurfaceControl).apply();
@@ -173,8 +172,7 @@ class WindowSurfaceController {
}
void setCropInTransaction(Rect clipRect, boolean recoveringMemory) {
- if (SHOW_TRANSACTIONS) logSurface(
- "CROP " + clipRect.toShortString(), null);
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE CROP %s: %s", clipRect.toShortString(), title);
try {
if (clipRect.width() > 0 && clipRect.height() > 0) {
if (!clipRect.equals(mSurfaceCrop)) {
@@ -198,8 +196,7 @@ class WindowSurfaceController {
}
void clearCropInTransaction(boolean recoveringMemory) {
- if (SHOW_TRANSACTIONS) logSurface(
- "CLEAR CROP", null);
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE CLEAR CROP: %s", title);
try {
Rect clipRect = new Rect(0, 0, -1, -1);
if (mSurfaceCrop.equals(clipRect)) {
@@ -227,8 +224,8 @@ class WindowSurfaceController {
mSurfaceY = top;
try {
- if (SHOW_TRANSACTIONS) logSurface(
- "POS (setPositionInTransaction) @ (" + left + "," + top + ")", null);
+ ProtoLog.i(WM_SHOW_TRANSACTIONS,
+ "SURFACE POS (setPositionInTransaction) @ (%f,%f): %s", left, top, title);
if (t == null) {
mSurfaceControl.setPosition(left, top);
@@ -264,8 +261,8 @@ class WindowSurfaceController {
mLastDsdy = dsdy;
try {
- if (SHOW_TRANSACTIONS) logSurface(
- "MATRIX [" + dsdx + "," + dtdx + "," + dtdy + "," + dsdy + "]", null);
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE MATRIX [%f,%f,%f,%f]: %s",
+ dsdx, dtdx, dtdy, dsdy, title);
if (t == null) {
mSurfaceControl.setMatrix(dsdx, dtdx, dtdy, dsdy);
} else {
@@ -290,8 +287,7 @@ class WindowSurfaceController {
mSurfaceH = height;
try {
- if (SHOW_TRANSACTIONS) logSurface(
- "SIZE " + width + "x" + height, null);
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE SIZE %dx%d: %s", width, height, title);
mSurfaceControl.setBufferSize(width, height);
} catch (RuntimeException e) {
// If something goes wrong with the surface (such
@@ -350,8 +346,7 @@ class WindowSurfaceController {
}
void setOpaque(boolean isOpaque) {
- if (SHOW_TRANSACTIONS) logSurface("isOpaque=" + isOpaque,
- null);
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isOpaque=%b: %s", isOpaque, title);
if (mSurfaceControl == null) {
return;
@@ -367,8 +362,7 @@ class WindowSurfaceController {
}
void setSecure(boolean isSecure) {
- if (SHOW_TRANSACTIONS) logSurface("isSecure=" + isSecure,
- null);
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isSecure=%b: %s", isSecure, title);
if (mSurfaceControl == null) {
return;
@@ -384,9 +378,7 @@ class WindowSurfaceController {
}
void setColorSpaceAgnostic(boolean agnostic) {
- if (SHOW_TRANSACTIONS) {
- logSurface("isColorSpaceAgnostic=" + agnostic, null);
- }
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isColorSpaceAgnostic=%b: %s", agnostic, title);
if (mSurfaceControl == null) {
return;
@@ -410,8 +402,7 @@ class WindowSurfaceController {
}
boolean showRobustlyInTransaction() {
- if (SHOW_TRANSACTIONS) logSurface(
- "SHOW (performLayout)", null);
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE SHOW (performLayout): %s", title);
if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + this
+ " during relayout");
mHiddenForOtherReasons = false;
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 56f6d4b02e32..0cfdebc6792d 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -23,7 +23,6 @@ import static com.android.server.wm.WindowManagerService.H.REPORT_WINDOWS_CHANGE
import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
import android.os.Debug;
-import android.os.Trace;
import android.util.Slog;
import java.io.PrintWriter;
@@ -52,15 +51,19 @@ class WindowSurfacePlacer {
/** The number of layout requests when deferring. */
private int mDeferredRequests;
- private final Runnable mPerformSurfacePlacement;
-
- public WindowSurfacePlacer(WindowManagerService service) {
- mService = service;
- mPerformSurfacePlacement = () -> {
+ private class Traverser implements Runnable {
+ @Override
+ public void run() {
synchronized (mService.mGlobalLock) {
performSurfacePlacement();
}
- };
+ }
+ }
+
+ private final Traverser mPerformSurfacePlacement = new Traverser();
+
+ WindowSurfacePlacer(WindowManagerService service) {
+ mService = service;
}
/**
@@ -152,7 +155,6 @@ class WindowSurfacePlacer {
return;
}
- Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmLayout");
mInLayout = true;
boolean recoveringMemory = false;
@@ -198,8 +200,6 @@ class WindowSurfacePlacer {
mInLayout = false;
Slog.wtf(TAG, "Unhandled exception while laying out windows", e);
}
-
- Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
void debugLayoutRepeats(final String msg, int pendingLayoutChanges) {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 8aee0f2a8308..ad71237bdf4e 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -19,9 +19,9 @@ package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
@@ -35,9 +35,10 @@ import static com.android.server.wm.WindowTokenProto.WINDOW_CONTAINER;
import android.annotation.CallSuper;
import android.os.Debug;
import android.os.IBinder;
-import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import com.android.server.protolog.common.ProtoLog;
+
import java.io.PrintWriter;
import java.util.Comparator;
@@ -119,7 +120,9 @@ class WindowToken extends WindowContainer<WindowState> {
mPersistOnEmpty = persistOnEmpty;
mOwnerCanManageAppTokens = ownerCanManageAppTokens;
mRoundedCornerOverlay = roundedCornerOverlay;
- onDisplayChanged(dc);
+ if (dc != null) {
+ onDisplayChanged(dc);
+ }
}
void setHidden(boolean hidden) {
@@ -135,8 +138,8 @@ class WindowToken extends WindowContainer<WindowState> {
void removeAllWindowsIfPossible() {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowState win = mChildren.get(i);
- if (DEBUG_WINDOW_MOVEMENT) Slog.w(TAG_WM,
- "removeAllWindowsIfPossible: removing win=" + win);
+ ProtoLog.w(WM_DEBUG_WINDOW_MOVEMENT,
+ "removeAllWindowsIfPossible: removing win=%s", win);
win.removeIfPossible();
}
}
@@ -197,15 +200,15 @@ class WindowToken extends WindowContainer<WindowState> {
}
void addWindow(final WindowState win) {
- if (DEBUG_FOCUS) Slog.d(TAG_WM,
- "addWindow: win=" + win + " Callers=" + Debug.getCallers(5));
+ ProtoLog.d(WM_DEBUG_FOCUS,
+ "addWindow: win=%s Callers=%s", win, Debug.getCallers(5));
if (win.isChildWindow()) {
// Child windows are added to their parent windows.
return;
}
if (!mChildren.contains(win)) {
- if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Adding " + win + " to " + this);
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", win, this);
addChild(win, mWindowComparator);
mWmService.mWindowsChanged = true;
// TODO: Should we also be setting layout needed here and other places?
@@ -240,7 +243,7 @@ class WindowToken extends WindowContainer<WindowState> {
return false;
}
- AppWindowToken asAppWindowToken() {
+ ActivityRecord asActivityRecord() {
// TODO: Not sure if this is the best way to handle this vs. using instanceof and casting.
// I am not an app window token!
return null;
diff --git a/services/core/java/com/android/server/wm/WindowTraceBuffer.java b/services/core/java/com/android/server/wm/WindowTraceBuffer.java
deleted file mode 100644
index 8c65884a4d89..000000000000
--- a/services/core/java/com/android/server/wm/WindowTraceBuffer.java
+++ /dev/null
@@ -1,160 +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;
-
-import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER;
-import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
-import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
-
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.ArrayDeque;
-import java.util.Arrays;
-import java.util.Queue;
-
-/**
- * Buffer used for window tracing.
- */
-class WindowTraceBuffer {
- private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
-
- private final Object mBufferLock = new Object();
-
- private final Queue<ProtoOutputStream> mBuffer = new ArrayDeque<>();
- private int mBufferUsedSize;
- private int mBufferCapacity;
-
- WindowTraceBuffer(int bufferCapacity) {
- mBufferCapacity = bufferCapacity;
- resetBuffer();
- }
-
- int getAvailableSpace() {
- return mBufferCapacity - mBufferUsedSize;
- }
-
- int size() {
- return mBuffer.size();
- }
-
- void setCapacity(int capacity) {
- mBufferCapacity = capacity;
- }
-
- /**
- * Inserts the specified element into this buffer.
- *
- * @param proto the element to add
- * @throws IllegalStateException if the element cannot be added because it is larger
- * than the buffer size.
- */
- void add(ProtoOutputStream proto) {
- int protoLength = proto.getRawSize();
- if (protoLength > mBufferCapacity) {
- throw new IllegalStateException("Trace object too large for the buffer. Buffer size:"
- + mBufferCapacity + " Object size: " + protoLength);
- }
- synchronized (mBufferLock) {
- discardOldest(protoLength);
- mBuffer.add(proto);
- mBufferUsedSize += protoLength;
- mBufferLock.notify();
- }
- }
-
- boolean contains(byte[] other) {
- return mBuffer.stream()
- .anyMatch(p -> Arrays.equals(p.getBytes(), other));
- }
-
- /**
- * Writes the trace buffer to disk.
- */
- void writeTraceToFile(File traceFile) throws IOException {
- synchronized (mBufferLock) {
- traceFile.delete();
- try (OutputStream os = new FileOutputStream(traceFile)) {
- traceFile.setReadable(true /* readable */, false /* ownerOnly */);
- ProtoOutputStream proto = new ProtoOutputStream();
- proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
- os.write(proto.getBytes());
- for (ProtoOutputStream protoOutputStream : mBuffer) {
- proto = protoOutputStream;
- byte[] protoBytes = proto.getBytes();
- os.write(protoBytes);
- }
- os.flush();
- }
- }
- }
-
- /**
- * Checks if the element can be added to the buffer. The element is already certain to be
- * smaller than the overall buffer size.
- *
- * @param protoLength byte array representation of the Proto object to add
- */
- private void discardOldest(int protoLength) {
- long availableSpace = getAvailableSpace();
-
- while (availableSpace < protoLength) {
-
- ProtoOutputStream item = mBuffer.poll();
- if (item == null) {
- throw new IllegalStateException("No element to discard from buffer");
- }
- mBufferUsedSize -= item.getRawSize();
- availableSpace = getAvailableSpace();
- }
- }
-
- /**
- * Removes all elements form the buffer
- */
- void resetBuffer() {
- synchronized (mBufferLock) {
- mBuffer.clear();
- mBufferUsedSize = 0;
- }
- }
-
- @VisibleForTesting
- int getBufferSize() {
- return mBufferUsedSize;
- }
-
- String getStatus() {
- synchronized (mBufferLock) {
- return "Buffer size: "
- + mBufferCapacity
- + " bytes"
- + "\n"
- + "Buffer usage: "
- + mBufferUsedSize
- + " bytes"
- + "\n"
- + "Elements in the buffer: "
- + mBuffer.size();
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index 765e34788ac8..bb66530d7c15 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -19,6 +19,9 @@ package com.android.server.wm;
import static android.os.Build.IS_USER;
import static com.android.server.wm.WindowManagerTraceFileProto.ENTRY;
+import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER;
+import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
+import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
import static com.android.server.wm.WindowManagerTraceProto.ELAPSED_REALTIME_NANOS;
import static com.android.server.wm.WindowManagerTraceProto.WHERE;
import static com.android.server.wm.WindowManagerTraceProto.WINDOW_MANAGER_SERVICE;
@@ -31,6 +34,9 @@ import android.util.Log;
import android.util.proto.ProtoOutputStream;
import android.view.Choreographer;
+import com.android.server.protolog.ProtoLogImpl;
+import com.android.server.utils.TraceBuffer;
+
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
@@ -50,6 +56,7 @@ class WindowTracing {
private static final int BUFFER_CAPACITY_ALL = 4096 * 1024;
private static final String TRACE_FILENAME = "/data/misc/wmtrace/wm_trace.pb";
private static final String TAG = "WindowTracing";
+ private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
private final WindowManagerService mService;
private final Choreographer mChoreographer;
@@ -57,7 +64,7 @@ class WindowTracing {
private final Object mEnabledLock = new Object();
private final File mTraceFile;
- private final WindowTraceBuffer mBuffer;
+ private final com.android.server.utils.TraceBuffer mBuffer;
private final Choreographer.FrameCallback mFrameCallback = (frameTimeNanos) ->
log("onFrame" /* where */);
@@ -84,7 +91,7 @@ class WindowTracing {
mService = service;
mGlobalLock = globalLock;
mTraceFile = file;
- mBuffer = new WindowTraceBuffer(bufferCapacity);
+ mBuffer = new TraceBuffer(bufferCapacity);
setLogLevel(WindowTraceLogLevel.TRIM, null /* pw */);
}
@@ -94,6 +101,7 @@ class WindowTracing {
return;
}
synchronized (mEnabledLock) {
+ ProtoLogImpl.getSingleInstance().startProtoLog(pw);
logAndPrintln(pw, "Start tracing to " + mTraceFile + ".");
mBuffer.resetBuffer();
mEnabled = mEnabledLockFree = true;
@@ -132,6 +140,7 @@ class WindowTracing {
logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
}
}
+ ProtoLogImpl.getSingleInstance().stopProtoLog(pw, writeToFile);
}
private void setLogLevel(@WindowTraceLogLevel int logLevel, PrintWriter pw) {
@@ -317,6 +326,7 @@ class WindowTracing {
synchronized (mEnabledLock) {
writeTraceToFileLocked();
}
+ ProtoLogImpl.getSingleInstance().writeProtoLogToFile();
}
private void logAndPrintln(@Nullable PrintWriter pw, String msg) {
@@ -334,11 +344,13 @@ class WindowTracing {
private void writeTraceToFileLocked() {
try {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeTraceToFileLocked");
- mBuffer.writeTraceToFile(mTraceFile);
+ ProtoOutputStream proto = new ProtoOutputStream();
+ proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+ mBuffer.writeTraceToFile(mTraceFile, proto);
} catch (IOException e) {
Log.e(TAG, "Unable to write buffer to file", e);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
}
-} \ No newline at end of file
+}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index d9c7fed0ff00..bcd1713222cb 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -141,6 +141,7 @@ cc_defaults {
"android.hardware.vibrator@1.1",
"android.hardware.vibrator@1.2",
"android.hardware.vibrator@1.3",
+ "android.hardware.vibrator@1.4",
"android.hardware.vr@1.0",
"android.frameworks.schedulerservice@1.0",
"android.frameworks.sensorservice@1.0",
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index d5fbd2b316e7..a8c76827c43c 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -16,17 +16,12 @@
#define LOG_TAG "VibratorService"
-#include <android/hardware/vibrator/1.0/IVibrator.h>
-#include <android/hardware/vibrator/1.0/types.h>
-#include <android/hardware/vibrator/1.0/IVibrator.h>
-#include <android/hardware/vibrator/1.1/types.h>
-#include <android/hardware/vibrator/1.2/IVibrator.h>
-#include <android/hardware/vibrator/1.2/types.h>
-#include <android/hardware/vibrator/1.3/IVibrator.h>
+#include <android/hardware/vibrator/1.4/IVibrator.h>
#include "jni.h"
#include <nativehelper/JNIHelp.h>
#include "android_runtime/AndroidRuntime.h"
+#include "core_jni_helpers.h"
#include <utils/misc.h>
#include <utils/Log.h>
@@ -36,6 +31,7 @@
#include <stdio.h>
using android::hardware::Return;
+using android::hardware::Void;
using android::hardware::vibrator::V1_0::EffectStrength;
using android::hardware::vibrator::V1_0::Status;
using android::hardware::vibrator::V1_1::Effect_1_1;
@@ -44,9 +40,32 @@ namespace V1_0 = android::hardware::vibrator::V1_0;
namespace V1_1 = android::hardware::vibrator::V1_1;
namespace V1_2 = android::hardware::vibrator::V1_2;
namespace V1_3 = android::hardware::vibrator::V1_3;
+namespace V1_4 = android::hardware::vibrator::V1_4;
namespace android {
+static jmethodID sMethodIdOnComplete;
+
+class VibratorCallback : public V1_4::IVibratorCallback {
+ public:
+ VibratorCallback(JNIEnv *env, jobject vibration) :
+ mVibration(MakeGlobalRefOrDie(env, vibration)) {}
+
+ ~VibratorCallback() {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ env->DeleteGlobalRef(mVibration);
+ }
+
+ Return<void> onComplete() override {
+ auto env = AndroidRuntime::getJNIEnv();
+ env->CallVoidMethod(mVibration, sMethodIdOnComplete);
+ return Void();
+ }
+
+ private:
+ jobject mVibration;
+};
+
static constexpr int NUM_TRIES = 2;
// Creates a Return<R> with STATUS::EX_NULL_POINTER.
@@ -56,37 +75,57 @@ inline Return<R> NullptrStatus() {
return Return<R>{Status::fromExceptionCode(Status::EX_NULL_POINTER)};
}
-// Helper used to transparently deal with the vibrator HAL becoming unavailable.
-template<class R, class I, class... Args0, class... Args1>
-Return<R> halCall(Return<R> (I::* fn)(Args0...), Args1&&... args1) {
- // Assume that if getService returns a nullptr, HAL is not available on the
- // device.
- static sp<I> sHal = I::getService();
- static bool sAvailable = sHal != nullptr;
+template <typename I>
+class HalWrapper {
+ public:
+ static std::unique_ptr<HalWrapper> Create() {
+ // Assume that if getService returns a nullptr, HAL is not available on the
+ // device.
+ auto hal = I::getService();
+ return hal ? std::unique_ptr<HalWrapper>(new HalWrapper(std::move(hal))) : nullptr;
+ }
- if (!sAvailable) {
- return NullptrStatus<R>();
+ // Helper used to transparently deal with the vibrator HAL becoming unavailable.
+ template<class R, class... Args0, class... Args1>
+ Return<R> call(Return<R> (I::* fn)(Args0...), Args1&&... args1) {
+ // Return<R> doesn't have a default constructor, so make a Return<R> with
+ // STATUS::EX_NONE.
+ using ::android::hardware::Status;
+ Return<R> ret{Status::fromExceptionCode(Status::EX_NONE)};
+
+ // Note that ret is guaranteed to be changed after this loop.
+ for (int i = 0; i < NUM_TRIES; ++i) {
+ ret = (mHal == nullptr) ? NullptrStatus<R>()
+ : (*mHal.*fn)(std::forward<Args1>(args1)...);
+
+ if (ret.isOk()) {
+ break;
+ }
+
+ ALOGE("Failed to issue command to vibrator HAL. Retrying.");
+ // Restoring connection to the HAL.
+ mHal = I::tryGetService();
+ }
+ return ret;
}
- // Return<R> doesn't have a default constructor, so make a Return<R> with
- // STATUS::EX_NONE.
- using ::android::hardware::Status;
- Return<R> ret{Status::fromExceptionCode(Status::EX_NONE)};
+ private:
+ HalWrapper(sp<I> &&hal) : mHal(std::move(hal)) {}
- // Note that ret is guaranteed to be changed after this loop.
- for (int i = 0; i < NUM_TRIES; ++i) {
- ret = (sHal == nullptr) ? NullptrStatus<R>()
- : (*sHal.*fn)(std::forward<Args1>(args1)...);
+ private:
+ sp<I> mHal;
+};
- if (ret.isOk()) {
- break;
- }
+template <typename I>
+static auto getHal() {
+ static auto sHalWrapper = HalWrapper<I>::Create();
+ return sHalWrapper.get();
+}
- ALOGE("Failed to issue command to vibrator HAL. Retrying.");
- // Restoring connection to the HAL.
- sHal = I::tryGetService();
- }
- return ret;
+template<class R, class I, class... Args0, class... Args1>
+Return<R> halCall(Return<R> (I::* fn)(Args0...), Args1&&... args1) {
+ auto hal = getHal<I>();
+ return hal ? hal->call(fn, std::forward<Args1>(args1)...) : NullptrStatus<R>();
}
template<class R>
@@ -99,17 +138,17 @@ bool isValidEffect(jlong effect) {
return val >= *iter.begin() && val <= *std::prev(iter.end());
}
-static void vibratorInit(JNIEnv /* env */, jobject /* clazz */)
+static void vibratorInit(JNIEnv *env, jclass clazz)
{
halCall(&V1_0::IVibrator::ping).isOk();
}
-static jboolean vibratorExists(JNIEnv* /* env */, jobject /* clazz */)
+static jboolean vibratorExists(JNIEnv* /* env */, jclass /* clazz */)
{
return halCall(&V1_0::IVibrator::ping).isOk() ? JNI_TRUE : JNI_FALSE;
}
-static void vibratorOn(JNIEnv* /* env */, jobject /* clazz */, jlong timeout_ms)
+static void vibratorOn(JNIEnv* /* env */, jclass /* clazz */, jlong timeout_ms)
{
Status retStatus = halCall(&V1_0::IVibrator::on, timeout_ms).withDefault(Status::UNKNOWN_ERROR);
if (retStatus != Status::OK) {
@@ -117,7 +156,7 @@ static void vibratorOn(JNIEnv* /* env */, jobject /* clazz */, jlong timeout_ms)
}
}
-static void vibratorOff(JNIEnv* /* env */, jobject /* clazz */)
+static void vibratorOff(JNIEnv* /* env */, jclass /* clazz */)
{
Status retStatus = halCall(&V1_0::IVibrator::off).withDefault(Status::UNKNOWN_ERROR);
if (retStatus != Status::OK) {
@@ -125,11 +164,11 @@ static void vibratorOff(JNIEnv* /* env */, jobject /* clazz */)
}
}
-static jlong vibratorSupportsAmplitudeControl(JNIEnv*, jobject) {
+static jlong vibratorSupportsAmplitudeControl(JNIEnv*, jclass) {
return halCall(&V1_0::IVibrator::supportsAmplitudeControl).withDefault(false);
}
-static void vibratorSetAmplitude(JNIEnv*, jobject, jint amplitude) {
+static void vibratorSetAmplitude(JNIEnv*, jclass, jint amplitude) {
Status status = halCall(&V1_0::IVibrator::setAmplitude, static_cast<uint32_t>(amplitude))
.withDefault(Status::UNKNOWN_ERROR);
if (status != Status::OK) {
@@ -138,11 +177,11 @@ static void vibratorSetAmplitude(JNIEnv*, jobject, jint amplitude) {
}
}
-static jboolean vibratorSupportsExternalControl(JNIEnv*, jobject) {
+static jboolean vibratorSupportsExternalControl(JNIEnv*, jclass) {
return halCall(&V1_3::IVibrator::supportsExternalControl).withDefault(false);
}
-static void vibratorSetExternalControl(JNIEnv*, jobject, jboolean enabled) {
+static void vibratorSetExternalControl(JNIEnv*, jclass, jboolean enabled) {
Status status = halCall(&V1_3::IVibrator::setExternalControl, static_cast<uint32_t>(enabled))
.withDefault(Status::UNKNOWN_ERROR);
if (status != Status::OK) {
@@ -151,7 +190,8 @@ static void vibratorSetExternalControl(JNIEnv*, jobject, jboolean enabled) {
}
}
-static jlong vibratorPerformEffect(JNIEnv*, jobject, jlong effect, jint strength) {
+static jlong vibratorPerformEffect(JNIEnv* env, jclass, jlong effect, jint strength,
+ jobject vibration) {
Status status;
uint32_t lengthMs;
auto callback = [&status, &lengthMs](Status retStatus, uint32_t retLengthMs) {
@@ -161,7 +201,11 @@ static jlong vibratorPerformEffect(JNIEnv*, jobject, jlong effect, jint strength
EffectStrength effectStrength(static_cast<EffectStrength>(strength));
Return<void> ret;
- if (isValidEffect<V1_0::Effect>(effect)) {
+ if (auto hal = getHal<V1_4::IVibrator>(); hal && isValidEffect<V1_3::Effect>(effect)) {
+ sp<VibratorCallback> effectCallback = new VibratorCallback(env, vibration);
+ ret = hal->call(&V1_4::IVibrator::perform_1_4, static_cast<V1_3::Effect>(effect),
+ effectStrength, effectCallback, callback);
+ } else if (isValidEffect<V1_0::Effect>(effect)) {
ret = halCall(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(effect),
effectStrength, callback);
} else if (isValidEffect<Effect_1_1>(effect)) {
@@ -198,6 +242,10 @@ static jlong vibratorPerformEffect(JNIEnv*, jobject, jlong effect, jint strength
return -1;
}
+static jlong vibratorGetCapabilities(JNIEnv*, jclass) {
+ return halCall(&V1_4::IVibrator::getCapabilities).withDefault(0);
+}
+
static const JNINativeMethod method_table[] = {
{ "vibratorExists", "()Z", (void*)vibratorExists },
{ "vibratorInit", "()V", (void*)vibratorInit },
@@ -205,13 +253,18 @@ static const JNINativeMethod method_table[] = {
{ "vibratorOff", "()V", (void*)vibratorOff },
{ "vibratorSupportsAmplitudeControl", "()Z", (void*)vibratorSupportsAmplitudeControl},
{ "vibratorSetAmplitude", "(I)V", (void*)vibratorSetAmplitude},
- { "vibratorPerformEffect", "(JJ)J", (void*)vibratorPerformEffect},
+ { "vibratorPerformEffect", "(JJLcom/android/server/VibratorService$Vibration;)J",
+ (void*)vibratorPerformEffect},
{ "vibratorSupportsExternalControl", "()Z", (void*)vibratorSupportsExternalControl},
{ "vibratorSetExternalControl", "(Z)V", (void*)vibratorSetExternalControl},
+ { "vibratorGetCapabilities", "()J", (void*)vibratorGetCapabilities},
};
int register_android_server_VibratorService(JNIEnv *env)
{
+ sMethodIdOnComplete = GetMethodIDOrDie(env,
+ FindClassOrDie(env, "com/android/server/VibratorService$Vibration"),
+ "onComplete", "()V");
return jniRegisterNativeMethods(env, "com/android/server/VibratorService",
method_table, NELEM(method_table));
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 03f475582a5a..dd2629d31768 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -159,12 +159,7 @@ static void loadSystemIconAsSpriteWithPointerIcon(JNIEnv* env, jobject contextOb
status_t status = android_view_PointerIcon_loadSystemIcon(env,
contextObj, style, outPointerIcon);
if (!status) {
- SkBitmap* bitmapCopy = &outSpriteIcon->bitmap;
- SkImageInfo bitmapCopyInfo = outPointerIcon->bitmap.info().makeColorType(kN32_SkColorType);
- if (bitmapCopy->tryAllocPixels(bitmapCopyInfo)) {
- outPointerIcon->bitmap.readPixels(bitmapCopy->info(), bitmapCopy->getPixels(),
- bitmapCopy->rowBytes(), 0, 0);
- }
+ outSpriteIcon->bitmap = outPointerIcon->bitmap.copy(ANDROID_BITMAP_FORMAT_RGBA_8888);
outSpriteIcon->style = outPointerIcon->style;
outSpriteIcon->hotSpotX = outPointerIcon->hotSpotX;
outSpriteIcon->hotSpotY = outPointerIcon->hotSpotY;
@@ -206,8 +201,7 @@ public:
void setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
- status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel,
- int32_t displayId);
+ 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);
@@ -440,10 +434,9 @@ void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportO
}
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
- const sp<InputChannel>& inputChannel, int32_t displayId) {
+ const sp<InputChannel>& inputChannel) {
ATRACE_CALL();
- return mInputManager->getDispatcher()->registerInputChannel(
- inputChannel, displayId);
+ return mInputManager->getDispatcher()->registerInputChannel(inputChannel);
}
status_t NativeInputManager::registerInputMonitor(JNIEnv* /* env */,
@@ -1410,7 +1403,7 @@ static void handleInputChannelDisposed(JNIEnv* env,
}
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
- jlong ptr, jobject inputChannelObj, jint displayId) {
+ jlong ptr, jobject inputChannelObj) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
@@ -1420,7 +1413,7 @@ static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
return;
}
- status_t status = im->registerInputChannel(env, inputChannel, displayId);
+ status_t status = im->registerInputChannel(env, inputChannel);
if (status) {
std::string message;
@@ -1575,6 +1568,27 @@ static void nativeSetSystemUiVisibility(JNIEnv* /* env */,
im->setSystemUiVisibility(visibility);
}
+static jboolean nativeTransferTouchFocus(JNIEnv* env,
+ jclass /* clazz */, jlong ptr, jobject fromChannelObj, jobject toChannelObj) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ sp<InputChannel> fromChannel =
+ android_view_InputChannel_getInputChannel(env, fromChannelObj);
+ sp<InputChannel> toChannel =
+ android_view_InputChannel_getInputChannel(env, toChannelObj);
+
+ if (fromChannel == nullptr || toChannel == nullptr) {
+ return JNI_FALSE;
+ }
+
+ if (im->getInputManager()->getDispatcher()->
+ transferTouchFocus(fromChannel->getToken(), toChannel->getToken())) {
+ return JNI_TRUE;
+ } else {
+ return JNI_FALSE;
+ }
+}
+
static void nativeSetPointerSpeed(JNIEnv* /* env */,
jclass /* clazz */, jlong ptr, jint speed) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
@@ -1709,15 +1723,8 @@ static void nativeSetCustomPointerIcon(JNIEnv* env, jclass /* clazz */,
return;
}
- SpriteIcon spriteIcon;
- SkImageInfo spriteInfo = pointerIcon.bitmap.info().makeColorType(kN32_SkColorType);
- if (spriteIcon.bitmap.tryAllocPixels(spriteInfo)) {
- pointerIcon.bitmap.readPixels(spriteInfo, spriteIcon.bitmap.getPixels(),
- spriteIcon.bitmap.rowBytes(), 0, 0);
- }
- spriteIcon.style = pointerIcon.style;
- spriteIcon.hotSpotX = pointerIcon.hotSpotX;
- spriteIcon.hotSpotY = pointerIcon.hotSpotY;
+ SpriteIcon spriteIcon(pointerIcon.bitmap.copy(ANDROID_BITMAP_FORMAT_RGBA_8888),
+ pointerIcon.style, pointerIcon.hotSpotX, pointerIcon.hotSpotY);
im->setCustomPointerIcon(spriteIcon);
}
@@ -1748,7 +1755,7 @@ static const JNINativeMethod gInputManagerMethods[] = {
{ "nativeHasKeys", "(JII[I[Z)Z",
(void*) nativeHasKeys },
{ "nativeRegisterInputChannel",
- "(JLandroid/view/InputChannel;I)V",
+ "(JLandroid/view/InputChannel;)V",
(void*) nativeRegisterInputChannel },
{ "nativeRegisterInputMonitor",
"(JLandroid/view/InputChannel;IZ)V",
@@ -1775,6 +1782,8 @@ static const JNINativeMethod gInputManagerMethods[] = {
(void*) nativeSetInputDispatchMode },
{ "nativeSetSystemUiVisibility", "(JI)V",
(void*) nativeSetSystemUiVisibility },
+ { "nativeTransferTouchFocus", "(JLandroid/view/InputChannel;Landroid/view/InputChannel;)Z",
+ (void*) nativeTransferTouchFocus },
{ "nativeSetPointerSpeed", "(JI)V",
(void*) nativeSetPointerSpeed },
{ "nativeSetShowTouches", "(JZ)V",
diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp
index 9ceb7706628a..906b5688d51f 100644
--- a/services/core/jni/com_android_server_security_VerityUtils.cpp
+++ b/services/core/jni/com_android_server_security_VerityUtils.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "VerityUtils"
#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
#include "jni.h"
#include <utils/Log.h>
@@ -72,11 +74,17 @@ namespace android {
namespace {
int enableFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath, jbyteArray signature) {
- const char* path = env->GetStringUTFChars(filePath, nullptr);
- ::android::base::unique_fd rfd(open(path, O_RDONLY | O_CLOEXEC));
- env->ReleaseStringUTFChars(filePath, path);
+ ScopedUtfChars path(env, filePath);
+ if (path.c_str() == nullptr) {
+ return EINVAL;
+ }
+ ::android::base::unique_fd rfd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
if (rfd.get() < 0) {
- return errno;
+ return errno;
+ }
+ ScopedByteArrayRO signature_bytes(env, signature);
+ if (signature_bytes.get() == nullptr) {
+ return EINVAL;
}
fsverity_enable_arg arg = {};
@@ -85,11 +93,11 @@ int enableFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath, jbyteArra
arg.block_size = 4096;
arg.salt_size = 0;
arg.salt_ptr = reinterpret_cast<uintptr_t>(nullptr);
- arg.sig_size = env->GetArrayLength(signature);
- arg.sig_ptr = reinterpret_cast<uintptr_t>(signature);
+ arg.sig_size = signature_bytes.size();
+ arg.sig_ptr = reinterpret_cast<uintptr_t>(signature_bytes.get());
if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, &arg) < 0) {
- return errno;
+ return errno;
}
return 0;
}
@@ -101,14 +109,16 @@ int measureFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) {
fsverity_digest *data = reinterpret_cast<fsverity_digest *>(&bytes);
data->digest_size = kSha256Bytes; // the only input/output parameter
- const char* path = env->GetStringUTFChars(filePath, nullptr);
- ::android::base::unique_fd rfd(open(path, O_RDONLY | O_CLOEXEC));
- env->ReleaseStringUTFChars(filePath, path);
+ ScopedUtfChars path(env, filePath);
+ if (path.c_str() == nullptr) {
+ return EINVAL;
+ }
+ ::android::base::unique_fd rfd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
if (rfd.get() < 0) {
- return errno;
+ return errno;
}
if (ioctl(rfd.get(), FS_IOC_MEASURE_VERITY, data) < 0) {
- return errno;
+ return errno;
}
return 0;
}
diff --git a/services/devicepolicy/Android.bp b/services/devicepolicy/Android.bp
index 47790ce68dc1..91c05a858ce4 100644
--- a/services/devicepolicy/Android.bp
+++ b/services/devicepolicy/Android.bp
@@ -5,4 +5,13 @@ java_library_static {
libs: [
"services.core",
],
+
+ plugins: [
+ "compat-changeid-annotation-processor",
+ ],
}
+
+platform_compat_config {
+ name: "services-devicepolicy-platform-compat-config",
+ src: ":services.devicepolicy",
+} \ No newline at end of file
diff --git a/services/devicepolicy/TEST_MAPPING b/services/devicepolicy/TEST_MAPPING
index a5ee3e24e14a..3d86cf30f38e 100644
--- a/services/devicepolicy/TEST_MAPPING
+++ b/services/devicepolicy/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "presubmit": [
+ "presubmit-devicepolicy": [
{
"name": "CtsDevicePolicyManagerTestCases",
"options": [
@@ -12,7 +12,7 @@
]
}
],
- "postsubmit": [
+ "postsubmit-devicepolicy": [
{
"name": "CtsDevicePolicyManagerTestCases"
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 704c80870fe5..5e49c7a9e5d1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -50,6 +50,7 @@ import static android.app.admin.DevicePolicyManager.DELEGATION_PACKAGE_ACCESS;
import static android.app.admin.DevicePolicyManager.DELEGATION_PERMISSION_GRANT;
import static android.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO;
import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI;
+import static android.app.admin.DevicePolicyManager.ID_TYPE_INDIVIDUAL_ATTESTATION;
import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID;
import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL;
import static android.app.admin.DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED;
@@ -85,6 +86,7 @@ import static android.provider.Telephony.Carriers.ENFORCE_KEY;
import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER;
@@ -125,8 +127,10 @@ import android.app.admin.DevicePolicyEventLogger;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DeviceStateCache;
import android.app.admin.NetworkEvent;
import android.app.admin.PasswordMetrics;
+import android.app.admin.PasswordPolicy;
import android.app.admin.SecurityLog;
import android.app.admin.SecurityLog.SecurityEvent;
import android.app.admin.StartInstallingUpdateCallback;
@@ -135,6 +139,8 @@ import android.app.admin.SystemUpdatePolicy;
import android.app.backup.IBackupManager;
import android.app.trust.TrustManager;
import android.app.usage.UsageStatsManagerInternal;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -162,6 +168,7 @@ import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Color;
+import android.location.LocationManager;
import android.media.AudioManager;
import android.media.IAudioService;
import android.net.ConnectivityManager;
@@ -233,6 +240,7 @@ import android.view.inputmethod.InputMethodInfo;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.compat.IPlatformCompat;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
@@ -249,6 +257,8 @@ import com.android.internal.util.StatLogger;
import com.android.internal.util.XmlUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockSettingsInternal;
+import com.android.internal.widget.LockscreenCredential;
+import com.android.internal.widget.PasswordValidationError;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
import com.android.server.SystemServerInitThreadPool;
@@ -492,6 +502,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private static final String LOG_TAG_PROFILE_OWNER = "profile-owner";
private static final String LOG_TAG_DEVICE_OWNER = "device-owner";
+ /**
+ * For admin apps targeting R+, throw when the app sets password requirement
+ * that is not taken into account at given quality. For example when quality is set
+ * to {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}, it doesn't make sense to
+ * require certain password length. If the intent is to require a password of certain length
+ * having at least NUMERIC quality, the admin should first call
+ * {@link #setPasswordQuality(ComponentName, int, boolean)} and only then call
+ * {@link #setPasswordMinimumLength(ComponentName, int, boolean)}.
+ *
+ * <p>Conversely when an admin app targeting R+ lowers password quality, those
+ * requirements that stop making sense are reset to default values.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+ private static final long ADMIN_APP_PASSWORD_COMPLEXITY = 123562444L;
+
final Context mContext;
final Injector mInjector;
final IPackageManager mIPackageManager;
@@ -504,8 +530,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private final LockSettingsInternal mLockSettingsInternal;
private final DeviceAdminServiceController mDeviceAdminServiceController;
private final OverlayPackagesProvider mOverlayPackagesProvider;
+ private final IPlatformCompat mIPlatformCompat;
private final DevicePolicyCacheImpl mPolicyCache = new DevicePolicyCacheImpl();
+ private final DeviceStateCacheImpl mStateCache = new DeviceStateCacheImpl();
/**
* Contains (package-user) pairs to remove. An entry (p, u) implies that removal of package p
@@ -965,19 +993,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
static final int DEF_PASSWORD_HISTORY_LENGTH = 0;
int passwordHistoryLength = DEF_PASSWORD_HISTORY_LENGTH;
- static final int DEF_MINIMUM_PASSWORD_LENGTH = 0;
- static final int DEF_MINIMUM_PASSWORD_LETTERS = 1;
- static final int DEF_MINIMUM_PASSWORD_UPPER_CASE = 0;
- static final int DEF_MINIMUM_PASSWORD_LOWER_CASE = 0;
- static final int DEF_MINIMUM_PASSWORD_NUMERIC = 1;
- static final int DEF_MINIMUM_PASSWORD_SYMBOLS = 1;
- static final int DEF_MINIMUM_PASSWORD_NON_LETTER = 0;
@NonNull
- PasswordMetrics minimumPasswordMetrics = new PasswordMetrics(
- PASSWORD_QUALITY_UNSPECIFIED, DEF_MINIMUM_PASSWORD_LENGTH,
- DEF_MINIMUM_PASSWORD_LETTERS, DEF_MINIMUM_PASSWORD_UPPER_CASE,
- DEF_MINIMUM_PASSWORD_LOWER_CASE, DEF_MINIMUM_PASSWORD_NUMERIC,
- DEF_MINIMUM_PASSWORD_SYMBOLS, DEF_MINIMUM_PASSWORD_NON_LETTER);
+ PasswordPolicy mPasswordPolicy = new PasswordPolicy();
static final long DEF_MAXIMUM_TIME_TO_UNLOCK = 0;
long maximumTimeToUnlock = DEF_MAXIMUM_TIME_TO_UNLOCK;
@@ -1112,36 +1129,36 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
out.startTag(null, TAG_POLICIES);
info.writePoliciesToXml(out);
out.endTag(null, TAG_POLICIES);
- if (minimumPasswordMetrics.quality != PASSWORD_QUALITY_UNSPECIFIED) {
+ if (mPasswordPolicy.quality != PASSWORD_QUALITY_UNSPECIFIED) {
writeAttributeValueToXml(
- out, TAG_PASSWORD_QUALITY, minimumPasswordMetrics.quality);
- if (minimumPasswordMetrics.length != DEF_MINIMUM_PASSWORD_LENGTH) {
+ out, TAG_PASSWORD_QUALITY, mPasswordPolicy.quality);
+ if (mPasswordPolicy.length != PasswordPolicy.DEF_MINIMUM_LENGTH) {
writeAttributeValueToXml(
- out, TAG_MIN_PASSWORD_LENGTH, minimumPasswordMetrics.length);
+ out, TAG_MIN_PASSWORD_LENGTH, mPasswordPolicy.length);
}
- if (minimumPasswordMetrics.upperCase != DEF_MINIMUM_PASSWORD_UPPER_CASE) {
+ if (mPasswordPolicy.upperCase != PasswordPolicy.DEF_MINIMUM_UPPER_CASE) {
writeAttributeValueToXml(
- out, TAG_MIN_PASSWORD_UPPERCASE, minimumPasswordMetrics.upperCase);
+ out, TAG_MIN_PASSWORD_UPPERCASE, mPasswordPolicy.upperCase);
}
- if (minimumPasswordMetrics.lowerCase != DEF_MINIMUM_PASSWORD_LOWER_CASE) {
+ if (mPasswordPolicy.lowerCase != PasswordPolicy.DEF_MINIMUM_LOWER_CASE) {
writeAttributeValueToXml(
- out, TAG_MIN_PASSWORD_LOWERCASE, minimumPasswordMetrics.lowerCase);
+ out, TAG_MIN_PASSWORD_LOWERCASE, mPasswordPolicy.lowerCase);
}
- if (minimumPasswordMetrics.letters != DEF_MINIMUM_PASSWORD_LETTERS) {
+ if (mPasswordPolicy.letters != PasswordPolicy.DEF_MINIMUM_LETTERS) {
writeAttributeValueToXml(
- out, TAG_MIN_PASSWORD_LETTERS, minimumPasswordMetrics.letters);
+ out, TAG_MIN_PASSWORD_LETTERS, mPasswordPolicy.letters);
}
- if (minimumPasswordMetrics.numeric != DEF_MINIMUM_PASSWORD_NUMERIC) {
+ if (mPasswordPolicy.numeric != PasswordPolicy.DEF_MINIMUM_NUMERIC) {
writeAttributeValueToXml(
- out, TAG_MIN_PASSWORD_NUMERIC, minimumPasswordMetrics.numeric);
+ out, TAG_MIN_PASSWORD_NUMERIC, mPasswordPolicy.numeric);
}
- if (minimumPasswordMetrics.symbols != DEF_MINIMUM_PASSWORD_SYMBOLS) {
+ if (mPasswordPolicy.symbols != PasswordPolicy.DEF_MINIMUM_SYMBOLS) {
writeAttributeValueToXml(
- out, TAG_MIN_PASSWORD_SYMBOLS, minimumPasswordMetrics.symbols);
+ out, TAG_MIN_PASSWORD_SYMBOLS, mPasswordPolicy.symbols);
}
- if (minimumPasswordMetrics.nonLetter > DEF_MINIMUM_PASSWORD_NON_LETTER) {
+ if (mPasswordPolicy.nonLetter > PasswordPolicy.DEF_MINIMUM_NON_LETTER) {
writeAttributeValueToXml(
- out, TAG_MIN_PASSWORD_NONLETTER, minimumPasswordMetrics.nonLetter);
+ out, TAG_MIN_PASSWORD_NONLETTER, mPasswordPolicy.nonLetter);
}
}
if (passwordHistoryLength != DEF_PASSWORD_HISTORY_LENGTH) {
@@ -1380,31 +1397,31 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
info.readPoliciesFromXml(parser);
}
} else if (TAG_PASSWORD_QUALITY.equals(tag)) {
- minimumPasswordMetrics.quality = Integer.parseInt(
+ mPasswordPolicy.quality = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MIN_PASSWORD_LENGTH.equals(tag)) {
- minimumPasswordMetrics.length = Integer.parseInt(
+ mPasswordPolicy.length = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_PASSWORD_HISTORY_LENGTH.equals(tag)) {
passwordHistoryLength = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MIN_PASSWORD_UPPERCASE.equals(tag)) {
- minimumPasswordMetrics.upperCase = Integer.parseInt(
+ mPasswordPolicy.upperCase = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MIN_PASSWORD_LOWERCASE.equals(tag)) {
- minimumPasswordMetrics.lowerCase = Integer.parseInt(
+ mPasswordPolicy.lowerCase = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MIN_PASSWORD_LETTERS.equals(tag)) {
- minimumPasswordMetrics.letters = Integer.parseInt(
+ mPasswordPolicy.letters = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MIN_PASSWORD_NUMERIC.equals(tag)) {
- minimumPasswordMetrics.numeric = Integer.parseInt(
+ mPasswordPolicy.numeric = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MIN_PASSWORD_SYMBOLS.equals(tag)) {
- minimumPasswordMetrics.symbols = Integer.parseInt(
+ mPasswordPolicy.symbols = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_MIN_PASSWORD_NONLETTER.equals(tag)) {
- minimumPasswordMetrics.nonLetter = Integer.parseInt(
+ mPasswordPolicy.nonLetter = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
}else if (TAG_MAX_TIME_TO_UNLOCK.equals(tag)) {
maximumTimeToUnlock = Long.parseLong(
@@ -1665,23 +1682,23 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
pw.decreaseIndent();
}
pw.print("passwordQuality=0x");
- pw.println(Integer.toHexString(minimumPasswordMetrics.quality));
+ pw.println(Integer.toHexString(mPasswordPolicy.quality));
pw.print("minimumPasswordLength=");
- pw.println(minimumPasswordMetrics.length);
+ pw.println(mPasswordPolicy.length);
pw.print("passwordHistoryLength=");
pw.println(passwordHistoryLength);
pw.print("minimumPasswordUpperCase=");
- pw.println(minimumPasswordMetrics.upperCase);
+ pw.println(mPasswordPolicy.upperCase);
pw.print("minimumPasswordLowerCase=");
- pw.println(minimumPasswordMetrics.lowerCase);
+ pw.println(mPasswordPolicy.lowerCase);
pw.print("minimumPasswordLetters=");
- pw.println(minimumPasswordMetrics.letters);
+ pw.println(mPasswordPolicy.letters);
pw.print("minimumPasswordNumeric=");
- pw.println(minimumPasswordMetrics.numeric);
+ pw.println(mPasswordPolicy.numeric);
pw.print("minimumPasswordSymbols=");
- pw.println(minimumPasswordMetrics.symbols);
+ pw.println(mPasswordPolicy.symbols);
pw.print("minimumPasswordNonLetter=");
- pw.println(minimumPasswordMetrics.nonLetter);
+ pw.println(mPasswordPolicy.nonLetter);
pw.print("maximumTimeToUnlock=");
pw.println(maximumTimeToUnlock);
pw.print("strongAuthUnlockTimeout=");
@@ -1943,6 +1960,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return mContext.getSystemService(ConnectivityManager.class);
}
+ LocationManager getLocationManager() {
+ return mContext.getSystemService(LocationManager.class);
+ }
+
IWindowManager getIWindowManager() {
return IWindowManager.Stub
.asInterface(ServiceManager.getService(Context.WINDOW_SERVICE));
@@ -1981,6 +2002,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return LocalServices.getService(LockSettingsInternal.class);
}
+ IPlatformCompat getIPlatformCompat() {
+ return IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+ }
+
boolean hasUserSetupCompleted(DevicePolicyData userData) {
return userData.mUserSetupComplete;
}
@@ -2183,7 +2209,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
void postOnSystemServerInitThreadPool(Runnable runnable) {
- SystemServerInitThreadPool.get().submit(runnable, LOG_TAG);
+ SystemServerInitThreadPool.submit(runnable, LOG_TAG);
}
public TransferOwnershipMetadataManager newTransferOwnershipMetadataManager() {
@@ -2219,6 +2245,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mUsageStatsManagerInternal = Preconditions.checkNotNull(
injector.getUsageStatsManagerInternal());
mIPackageManager = Preconditions.checkNotNull(injector.getIPackageManager());
+ mIPlatformCompat = Preconditions.checkNotNull(injector.getIPlatformCompat());
mIPermissionManager = Preconditions.checkNotNull(injector.getIPermissionManager());
mTelephonyManager = Preconditions.checkNotNull(injector.getTelephonyManager());
@@ -2294,6 +2321,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
policy = new DevicePolicyData(userHandle);
mUserData.append(userHandle, policy);
loadSettingsLocked(policy, userHandle);
+ if (userHandle == UserHandle.USER_SYSTEM) {
+ mStateCache.setDeviceProvisioned(policy.mUserSetupComplete);
+ }
}
return policy;
}
@@ -4129,14 +4159,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
final long ident = mInjector.binderClearCallingIdentity();
try {
- final PasswordMetrics metrics = ap.minimumPasswordMetrics;
- if (metrics.quality != quality) {
- metrics.quality = quality;
+ final PasswordPolicy passwordPolicy = ap.mPasswordPolicy;
+ if (passwordPolicy.quality != quality) {
+ passwordPolicy.quality = quality;
+ resetInactivePasswordRequirementsIfRPlus(userId, ap);
updatePasswordValidityCheckpointLocked(userId, parent);
updatePasswordQualityCacheForUserGroup(userId);
saveSettingsLocked(userId);
}
- maybeLogPasswordComplexitySet(who, userId, parent, metrics);
+ maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
@@ -4149,6 +4180,37 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
.write();
}
+ private boolean passwordQualityInvocationOrderCheckEnabled(String packageName, int userId) {
+ try {
+ return mIPlatformCompat.isChangeEnabledByPackageName(ADMIN_APP_PASSWORD_COMPLEXITY,
+ packageName, userId);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Failed to get a response from PLATFORM_COMPAT_SERVICE", e);
+ }
+ return getTargetSdk(packageName, userId) > Build.VERSION_CODES.Q;
+ }
+
+ /**
+ * For admins targeting R+ reset various password constraints to default values when quality is
+ * set to a value that makes those constraints that have no effect.
+ */
+ private void resetInactivePasswordRequirementsIfRPlus(int userId, ActiveAdmin admin) {
+ if (passwordQualityInvocationOrderCheckEnabled(admin.info.getPackageName(), userId)) {
+ final PasswordPolicy policy = admin.mPasswordPolicy;
+ if (policy.quality < PASSWORD_QUALITY_NUMERIC) {
+ policy.length = PasswordPolicy.DEF_MINIMUM_LENGTH;
+ }
+ if (policy.quality < PASSWORD_QUALITY_COMPLEX) {
+ policy.letters = PasswordPolicy.DEF_MINIMUM_LETTERS;
+ policy.upperCase = PasswordPolicy.DEF_MINIMUM_UPPER_CASE;
+ policy.lowerCase = PasswordPolicy.DEF_MINIMUM_LOWER_CASE;
+ policy.numeric = PasswordPolicy.DEF_MINIMUM_NUMERIC;
+ policy.symbols = PasswordPolicy.DEF_MINIMUM_SYMBOLS;
+ policy.nonLetter = PasswordPolicy.DEF_MINIMUM_NON_LETTER;
+ }
+ }
+ }
+
/**
* 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
@@ -4209,7 +4271,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
- return admin != null ? admin.minimumPasswordMetrics.quality : mode;
+ return admin != null ? admin.mPasswordPolicy.quality : mode;
}
// Return the strictest policy across all participating admins.
@@ -4218,8 +4280,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final int N = admins.size();
for (int i = 0; i < N; i++) {
ActiveAdmin admin = admins.get(i);
- if (mode < admin.minimumPasswordMetrics.quality) {
- mode = admin.minimumPasswordMetrics.quality;
+ if (mode < admin.mPasswordPolicy.quality) {
+ mode = admin.mPasswordPolicy.quality;
}
}
return mode;
@@ -4279,13 +4341,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
synchronized (getLockObject()) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
- final PasswordMetrics metrics = ap.minimumPasswordMetrics;
- if (metrics.length != length) {
- metrics.length = length;
+ ensureMinimumQuality(userId, ap, PASSWORD_QUALITY_NUMERIC, "setPasswordMinimumLength");
+ final PasswordPolicy passwordPolicy = ap.mPasswordPolicy;
+ if (passwordPolicy.length != length) {
+ passwordPolicy.length = length;
updatePasswordValidityCheckpointLocked(userId, parent);
saveSettingsLocked(userId);
}
- maybeLogPasswordComplexitySet(who, userId, parent, metrics);
+ maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_LENGTH)
@@ -4294,10 +4357,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
.write();
}
+ private void ensureMinimumQuality(
+ int userId, ActiveAdmin admin, int minimumQuality, String operation) {
+ if (admin.mPasswordPolicy.quality < minimumQuality
+ && passwordQualityInvocationOrderCheckEnabled(admin.info.getPackageName(),
+ userId)) {
+ throw new IllegalStateException(String.format(
+ "password quality should be at least %d for %s", minimumQuality, operation));
+ }
+ }
+
@Override
public int getPasswordMinimumLength(ComponentName who, int userHandle, boolean parent) {
return getStrictestPasswordRequirement(who, userHandle, parent,
- admin -> admin.minimumPasswordMetrics.length, PASSWORD_QUALITY_UNSPECIFIED);
+ admin -> admin.mPasswordPolicy.length, PASSWORD_QUALITY_NUMERIC);
}
@Override
@@ -4524,13 +4597,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
synchronized (getLockObject()) {
final ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
- final PasswordMetrics metrics = ap.minimumPasswordMetrics;
- if (metrics.upperCase != length) {
- metrics.upperCase = length;
+ ensureMinimumQuality(
+ userId, ap, PASSWORD_QUALITY_COMPLEX, "setPasswordMinimumUpperCase");
+ final PasswordPolicy passwordPolicy = ap.mPasswordPolicy;
+ if (passwordPolicy.upperCase != length) {
+ passwordPolicy.upperCase = length;
updatePasswordValidityCheckpointLocked(userId, parent);
saveSettingsLocked(userId);
}
- maybeLogPasswordComplexitySet(who, userId, parent, metrics);
+ maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_UPPER_CASE)
@@ -4542,7 +4617,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public int getPasswordMinimumUpperCase(ComponentName who, int userHandle, boolean parent) {
return getStrictestPasswordRequirement(who, userHandle, parent,
- admin -> admin.minimumPasswordMetrics.upperCase, PASSWORD_QUALITY_COMPLEX);
+ admin -> admin.mPasswordPolicy.upperCase, PASSWORD_QUALITY_COMPLEX);
}
@Override
@@ -4552,13 +4627,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
synchronized (getLockObject()) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
- final PasswordMetrics metrics = ap.minimumPasswordMetrics;
- if (metrics.lowerCase != length) {
- metrics.lowerCase = length;
+ ensureMinimumQuality(
+ userId, ap, PASSWORD_QUALITY_COMPLEX, "setPasswordMinimumLowerCase");
+ final PasswordPolicy passwordPolicy = ap.mPasswordPolicy;
+ if (passwordPolicy.lowerCase != length) {
+ passwordPolicy.lowerCase = length;
updatePasswordValidityCheckpointLocked(userId, parent);
saveSettingsLocked(userId);
}
- maybeLogPasswordComplexitySet(who, userId, parent, metrics);
+ maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_LOWER_CASE)
@@ -4570,7 +4647,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public int getPasswordMinimumLowerCase(ComponentName who, int userHandle, boolean parent) {
return getStrictestPasswordRequirement(who, userHandle, parent,
- admin -> admin.minimumPasswordMetrics.lowerCase, PASSWORD_QUALITY_COMPLEX);
+ admin -> admin.mPasswordPolicy.lowerCase, PASSWORD_QUALITY_COMPLEX);
}
@Override
@@ -4583,13 +4660,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
synchronized (getLockObject()) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
- final PasswordMetrics metrics = ap.minimumPasswordMetrics;
- if (metrics.letters != length) {
- metrics.letters = length;
+ ensureMinimumQuality(userId, ap, PASSWORD_QUALITY_COMPLEX, "setPasswordMinimumLetters");
+ final PasswordPolicy passwordPolicy = ap.mPasswordPolicy;
+ if (passwordPolicy.letters != length) {
+ passwordPolicy.letters = length;
updatePasswordValidityCheckpointLocked(userId, parent);
saveSettingsLocked(userId);
}
- maybeLogPasswordComplexitySet(who, userId, parent, metrics);
+ maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_LETTERS)
@@ -4601,7 +4679,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public int getPasswordMinimumLetters(ComponentName who, int userHandle, boolean parent) {
return getStrictestPasswordRequirement(who, userHandle, parent,
- admin -> admin.minimumPasswordMetrics.letters, PASSWORD_QUALITY_COMPLEX);
+ admin -> admin.mPasswordPolicy.letters, PASSWORD_QUALITY_COMPLEX);
}
@Override
@@ -4614,13 +4692,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
synchronized (getLockObject()) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
- final PasswordMetrics metrics = ap.minimumPasswordMetrics;
- if (metrics.numeric != length) {
- metrics.numeric = length;
+ ensureMinimumQuality(userId, ap, PASSWORD_QUALITY_COMPLEX, "setPasswordMinimumNumeric");
+ final PasswordPolicy passwordPolicy = ap.mPasswordPolicy;
+ if (passwordPolicy.numeric != length) {
+ passwordPolicy.numeric = length;
updatePasswordValidityCheckpointLocked(userId, parent);
saveSettingsLocked(userId);
}
- maybeLogPasswordComplexitySet(who, userId, parent, metrics);
+ maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_NUMERIC)
@@ -4632,7 +4711,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public int getPasswordMinimumNumeric(ComponentName who, int userHandle, boolean parent) {
return getStrictestPasswordRequirement(who, userHandle, parent,
- admin -> admin.minimumPasswordMetrics.numeric, PASSWORD_QUALITY_COMPLEX);
+ admin -> admin.mPasswordPolicy.numeric, PASSWORD_QUALITY_COMPLEX);
}
@Override
@@ -4645,13 +4724,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
synchronized (getLockObject()) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
- final PasswordMetrics metrics = ap.minimumPasswordMetrics;
- if (metrics.symbols != length) {
- ap.minimumPasswordMetrics.symbols = length;
+ ensureMinimumQuality(userId, ap, PASSWORD_QUALITY_COMPLEX, "setPasswordMinimumSymbols");
+ final PasswordPolicy passwordPolicy = ap.mPasswordPolicy;
+ if (passwordPolicy.symbols != length) {
+ ap.mPasswordPolicy.symbols = length;
updatePasswordValidityCheckpointLocked(userId, parent);
saveSettingsLocked(userId);
}
- maybeLogPasswordComplexitySet(who, userId, parent, metrics);
+ maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_SYMBOLS)
@@ -4663,7 +4743,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public int getPasswordMinimumSymbols(ComponentName who, int userHandle, boolean parent) {
return getStrictestPasswordRequirement(who, userHandle, parent,
- admin -> admin.minimumPasswordMetrics.symbols, PASSWORD_QUALITY_COMPLEX);
+ admin -> admin.mPasswordPolicy.symbols, PASSWORD_QUALITY_COMPLEX);
}
@Override
@@ -4676,13 +4756,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
synchronized (getLockObject()) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
- final PasswordMetrics metrics = ap.minimumPasswordMetrics;
- if (metrics.nonLetter != length) {
- ap.minimumPasswordMetrics.nonLetter = length;
+ ensureMinimumQuality(
+ userId, ap, PASSWORD_QUALITY_COMPLEX, "setPasswordMinimumNonLetter");
+ final PasswordPolicy passwordPolicy = ap.mPasswordPolicy;
+ if (passwordPolicy.nonLetter != length) {
+ ap.mPasswordPolicy.nonLetter = length;
updatePasswordValidityCheckpointLocked(userId, parent);
saveSettingsLocked(userId);
}
- maybeLogPasswordComplexitySet(who, userId, parent, metrics);
+ maybeLogPasswordComplexitySet(who, userId, parent, passwordPolicy);
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_PASSWORD_MINIMUM_NON_LETTER)
@@ -4694,7 +4776,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public int getPasswordMinimumNonLetter(ComponentName who, int userHandle, boolean parent) {
return getStrictestPasswordRequirement(who, userHandle, parent,
- admin -> admin.minimumPasswordMetrics.nonLetter, PASSWORD_QUALITY_COMPLEX);
+ admin -> admin.mPasswordPolicy.nonLetter, PASSWORD_QUALITY_COMPLEX);
}
/**
@@ -4730,6 +4812,33 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+ /**
+ * Calculates strictest (maximum) value for a given password property enforced by admin[s].
+ */
+ @Override
+ public PasswordMetrics getPasswordMinimumMetrics(@UserIdInt int userHandle) {
+ return getPasswordMinimumMetrics(userHandle, false /* parent */);
+ }
+
+ /**
+ * Calculates strictest (maximum) value for a given password property enforced by admin[s].
+ */
+ private PasswordMetrics getPasswordMinimumMetrics(@UserIdInt int userHandle, boolean parent) {
+ if (!mHasFeature) {
+ new PasswordMetrics(CREDENTIAL_TYPE_NONE);
+ }
+ enforceFullCrossUsersPermission(userHandle);
+ ArrayList<PasswordMetrics> adminMetrics = new ArrayList<>();
+ synchronized (getLockObject()) {
+ List<ActiveAdmin> admins =
+ getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent);
+ for (ActiveAdmin admin : admins) {
+ adminMetrics.add(admin.mPasswordPolicy.getMinMetrics());
+ }
+ }
+ return PasswordMetrics.merge(adminMetrics);
+ }
+
@Override
public boolean isActivePasswordSufficient(int userHandle, boolean parent) {
if (!mHasFeature) {
@@ -4745,8 +4854,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
int credentialOwner = getCredentialOwner(userHandle, parent);
DevicePolicyData policy = getUserDataUnchecked(credentialOwner);
PasswordMetrics metrics = mLockSettingsInternal.getUserPasswordMetrics(credentialOwner);
- return isActivePasswordSufficientForUserLocked(
+ boolean activePasswordSufficientForUserLocked = isActivePasswordSufficientForUserLocked(
policy.mPasswordValidAtLastCheckpoint, metrics, userHandle, parent);
+ return activePasswordSufficientForUserLocked;
}
}
@@ -4809,25 +4919,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
*/
private boolean isPasswordSufficientForUserWithoutCheckpointLocked(
@NonNull PasswordMetrics metrics, @UserIdInt int userId, boolean parent) {
- final int requiredQuality = getPasswordQuality(null, userId, parent);
-
- if (requiredQuality >= PASSWORD_QUALITY_NUMERIC
- && metrics.length < getPasswordMinimumLength(null, userId, parent)) {
- return false;
- }
-
- // PASSWORD_QUALITY_COMPLEX doesn't represent actual password quality, it means that number
- // of characters of each class should be checked instead of quality itself.
- if (requiredQuality == PASSWORD_QUALITY_COMPLEX) {
- return metrics.upperCase >= getPasswordMinimumUpperCase(null, userId, parent)
- && metrics.lowerCase >= getPasswordMinimumLowerCase(null, userId, parent)
- && metrics.letters >= getPasswordMinimumLetters(null, userId, parent)
- && metrics.numeric >= getPasswordMinimumNumeric(null, userId, parent)
- && metrics.symbols >= getPasswordMinimumSymbols(null, userId, parent)
- && metrics.nonLetter >= getPasswordMinimumNonLetter(null, userId, parent);
- } else {
- return metrics.quality >= requiredQuality;
- }
+ PasswordMetrics minMetrics = getPasswordMinimumMetrics(userId, parent);
+ final List<PasswordValidationError> passwordValidationErrors =
+ PasswordMetrics.validatePasswordMetrics(
+ minMetrics, PASSWORD_COMPLEXITY_NONE, false, metrics);
+ return passwordValidationErrors.isEmpty();
}
@Override
@@ -5085,77 +5181,25 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private boolean resetPasswordInternal(String password, long tokenHandle, byte[] token,
int flags, int callingUid, int userHandle) {
- int quality;
- synchronized (getLockObject()) {
- quality = getPasswordQuality(null, userHandle, /* parent */ false);
- if (quality == PASSWORD_QUALITY_MANAGED) {
- quality = PASSWORD_QUALITY_UNSPECIFIED;
- }
- // TODO(b/120484642): remove getBytes() below
- final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password.getBytes());
- final int realQuality = metrics.quality;
- if (realQuality < quality && quality != PASSWORD_QUALITY_COMPLEX) {
- Slog.w(LOG_TAG, "resetPassword: password quality 0x"
- + Integer.toHexString(realQuality)
- + " does not meet required quality 0x"
- + Integer.toHexString(quality));
- return false;
+ synchronized (getLockObject()) {
+ final PasswordMetrics minMetrics = getPasswordMinimumMetrics(userHandle);
+ final List<PasswordValidationError> validationErrors;
+ // TODO: Consider changing validation API to take LockscreenCredential.
+ if (password.isEmpty()) {
+ validationErrors = PasswordMetrics.validatePasswordMetrics(
+ minMetrics, PASSWORD_COMPLEXITY_NONE, false /* isPin */,
+ new PasswordMetrics(CREDENTIAL_TYPE_NONE));
+ } else {
+ // TODO(b/120484642): remove getBytes() below
+ validationErrors = PasswordMetrics.validatePassword(
+ minMetrics, PASSWORD_COMPLEXITY_NONE, false, password.getBytes());
}
- quality = Math.max(realQuality, quality);
- int length = getPasswordMinimumLength(null, userHandle, /* parent */ false);
- if (password.length() < length) {
- Slog.w(LOG_TAG, "resetPassword: password length " + password.length()
- + " does not meet required length " + length);
+
+ if (!validationErrors.isEmpty()) {
+ Log.w(LOG_TAG, "Failed to reset password due to constraint violation: "
+ + validationErrors.get(0));
return false;
}
- if (quality == PASSWORD_QUALITY_COMPLEX) {
- int neededLetters = getPasswordMinimumLetters(null, userHandle, /* parent */ false);
- if(metrics.letters < neededLetters) {
- Slog.w(LOG_TAG, "resetPassword: number of letters " + metrics.letters
- + " does not meet required number of letters " + neededLetters);
- return false;
- }
- int neededNumeric = getPasswordMinimumNumeric(null, userHandle, /* parent */ false);
- if (metrics.numeric < neededNumeric) {
- Slog.w(LOG_TAG, "resetPassword: number of numerical digits " + metrics.numeric
- + " does not meet required number of numerical digits "
- + neededNumeric);
- return false;
- }
- int neededLowerCase = getPasswordMinimumLowerCase(
- null, userHandle, /* parent */ false);
- if (metrics.lowerCase < neededLowerCase) {
- Slog.w(LOG_TAG, "resetPassword: number of lowercase letters "
- + metrics.lowerCase
- + " does not meet required number of lowercase letters "
- + neededLowerCase);
- return false;
- }
- int neededUpperCase = getPasswordMinimumUpperCase(
- null, userHandle, /* parent */ false);
- if (metrics.upperCase < neededUpperCase) {
- Slog.w(LOG_TAG, "resetPassword: number of uppercase letters "
- + metrics.upperCase
- + " does not meet required number of uppercase letters "
- + neededUpperCase);
- return false;
- }
- int neededSymbols = getPasswordMinimumSymbols(null, userHandle, /* parent */ false);
- if (metrics.symbols < neededSymbols) {
- Slog.w(LOG_TAG, "resetPassword: number of special symbols " + metrics.symbols
- + " does not meet required number of special symbols " + neededSymbols);
- return false;
- }
- int neededNonLetter = getPasswordMinimumNonLetter(
- null, userHandle, /* parent */ false);
- if (metrics.nonLetter < neededNonLetter) {
- Slog.w(LOG_TAG, "resetPassword: number of non-letter characters "
- + metrics.nonLetter
- + " does not meet required number of non-letter characters "
- + neededNonLetter);
- return false;
- }
- }
}
DevicePolicyData policy = getUserData(userHandle);
@@ -5175,28 +5219,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// back in to the service.
final long ident = mInjector.binderClearCallingIdentity();
final boolean result;
+ final LockscreenCredential newCredential =
+ LockscreenCredential.createPasswordOrNone(password);
try {
if (token == null) {
// This is the legacy reset password for DPM. Here we want to be able to override
// the old device password without necessarily knowing it.
- if (!TextUtils.isEmpty(password)) {
- mLockPatternUtils.saveLockPassword(password.getBytes(), null, quality,
- userHandle, /*allowUntrustedChange */true);
- } else {
- mLockPatternUtils.clearLock(null, userHandle,
- /*allowUntrustedChange */ true);
- }
+ mLockPatternUtils.setLockCredential(
+ newCredential,
+ LockscreenCredential.createNone(),
+ userHandle, /*allowUntrustedChange */true);
result = true;
} else {
- if (!TextUtils.isEmpty(password)) {
- result = mLockPatternUtils.setLockCredentialWithToken(password.getBytes(),
- LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
- quality, tokenHandle, token, userHandle);
- } else {
- result = mLockPatternUtils.setLockCredentialWithToken(null,
- LockPatternUtils.CREDENTIAL_TYPE_NONE,
- quality, tokenHandle, token, userHandle);
- }
+ result = mLockPatternUtils.setLockCredentialWithToken(newCredential, tokenHandle,
+ token, userHandle);
}
boolean requireEntry = (flags & DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0;
if (requireEntry) {
@@ -5816,6 +5852,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
idTypeToAttestationFlag.put(ID_TYPE_SERIAL, AttestationUtils.ID_TYPE_SERIAL);
idTypeToAttestationFlag.put(ID_TYPE_IMEI, AttestationUtils.ID_TYPE_IMEI);
idTypeToAttestationFlag.put(ID_TYPE_MEID, AttestationUtils.ID_TYPE_MEID);
+ idTypeToAttestationFlag.put(
+ ID_TYPE_INDIVIDUAL_ATTESTATION, AttestationUtils.USE_INDIVIDUAL_ATTESTATION);
int numFlagsSet = Integer.bitCount(idAttestationFlags);
// No flags are set - return null to indicate no device ID attestation information should
@@ -7389,8 +7427,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final long callingIdentity = mInjector.binderClearCallingIdentity();
try {
- mInjector.getIActivityManager().requestBugReport(
- ActivityManager.BUGREPORT_OPTION_REMOTE);
+ mInjector.getIActivityManager().requestRemoteBugReport();
mRemoteBugreportServiceIsActive.set(true);
mRemoteBugreportSharingAccepted.set(false);
@@ -8009,6 +8046,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
"clearDeviceOwner can only be called by the device owner");
}
enforceUserUnlocked(deviceOwnerUserId);
+ DevicePolicyData policy = getUserData(deviceOwnerUserId);
+ if (policy.mPasswordTokenHandle != 0) {
+ mLockPatternUtils.removeEscrowToken(policy.mPasswordTokenHandle, deviceOwnerUserId);
+ }
final ActiveAdmin admin = getDeviceOwnerAdminLocked();
long ident = mInjector.binderClearCallingIdentity();
@@ -8911,6 +8952,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
pw.println("Encryption Status: " + getEncryptionStatusName(getEncryptionStatus()));
pw.println();
mPolicyCache.dump(pw);
+ pw.println();
+ mStateCache.dump(pw);
}
}
@@ -10861,6 +10904,36 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
+ public void setLocationEnabled(ComponentName who, boolean locationEnabled) {
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ int userId = mInjector.userHandleGetCallingUserId();
+
+ synchronized (getLockObject()) {
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+
+ if (!isDeviceOwner(who, userId) && !isCurrentUserDemo()) {
+ throw new SecurityException(
+ "Permission denial: Profile owners cannot update location settings");
+ }
+ }
+
+ long ident = mInjector.binderClearCallingIdentity();
+ try {
+ mInjector.getLocationManager().setLocationEnabledForUser(
+ locationEnabled, UserHandle.of(userId));
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_SECURE_SETTING)
+ .setAdmin(who)
+ .setStrings(Settings.Secure.LOCATION_MODE, Integer.toString(
+ locationEnabled ? Settings.Secure.LOCATION_MODE_ON
+ : Settings.Secure.LOCATION_MODE_OFF))
+ .write();
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
public boolean setTime(ComponentName who, long millis) {
Preconditions.checkNotNull(who, "ComponentName is null in setTime");
enforceDeviceOwner(who);
@@ -11122,6 +11195,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
DevicePolicyData policy = getUserData(userHandle);
if (!policy.mUserSetupComplete) {
policy.mUserSetupComplete = true;
+ if (userHandle == UserHandle.USER_SYSTEM) {
+ mStateCache.setDeviceProvisioned(true);
+ }
synchronized (getLockObject()) {
saveSettingsLocked(userHandle);
}
@@ -11263,6 +11339,28 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+ @Override
+ public boolean isActiveSupervisionApp(int uid) {
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getActiveAdminWithPolicyForUidLocked(
+ null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, uid);
+ if (admin == null) {
+ return false;
+ }
+
+ final String supervisionString = mContext.getResources().getString(
+ com.android.internal.R.string
+ .config_defaultSupervisionProfileOwnerComponent);
+ if (supervisionString == null) {
+ return false;
+ }
+
+ final ComponentName supervisorComponent = ComponentName.unflattenFromString(
+ supervisionString);
+ return admin.info.getComponent().equals(supervisorComponent);
+ }
+ }
+
private void notifyCrossProfileProvidersChanged(int userId, List<String> packages) {
final List<OnCrossProfileWidgetProvidersChangeListener> listeners;
synchronized (getLockObject()) {
@@ -11434,6 +11532,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
protected DevicePolicyCache getDevicePolicyCache() {
return mPolicyCache;
}
+
+ @Override
+ protected DeviceStateCache getDeviceStateCache() {
+ return mStateCache;
+ }
+
}
private Intent createShowAdminSupportIntent(ComponentName admin, int userId) {
@@ -11489,10 +11593,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
/**
* Returns true if specified admin is allowed to limit passwords and has a
- * {@code minimumPasswordMetrics.quality} of at least {@code minPasswordQuality}
+ * {@code mPasswordPolicy.quality} of at least {@code minPasswordQuality}
*/
private static boolean isLimitPasswordAllowed(ActiveAdmin admin, int minPasswordQuality) {
- if (admin.minimumPasswordMetrics.quality < minPasswordQuality) {
+ if (admin.mPasswordPolicy.quality < minPasswordQuality) {
return false;
}
return admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
@@ -11839,10 +11943,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
try {
int uid = packageManager.getPackageUidAsUser(packageName,
user.getIdentifier());
-
- // TODO: Prevent noting the app-op
- granted = PermissionChecker.checkPermission(mContext, permission, -1,
- uid, packageName);
+ if (PermissionChecker.checkPermissionForPreflight(mContext, permission,
+ PermissionChecker.PID_UNKNOWN, uid, packageName)
+ != PermissionChecker.PERMISSION_GRANTED) {
+ granted = PackageManager.PERMISSION_DENIED;
+ } else {
+ granted = PackageManager.PERMISSION_GRANTED;
+ }
} catch (NameNotFoundException e) {
throw new RemoteException(
"Cannot check if " + permission + "is a runtime permission", e,
@@ -12974,6 +13081,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Settings.Secure.USER_SETUP_COMPLETE, 0, userId) != 0;
DevicePolicyData policy = getUserData(userId);
policy.mUserSetupComplete = isUserCompleted;
+ mStateCache.setDeviceProvisioned(isUserCompleted);
synchronized (getLockObject()) {
saveSettingsLocked(userId);
}
@@ -14107,13 +14215,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private void maybeLogPasswordComplexitySet(ComponentName who, int userId, boolean parent,
- PasswordMetrics metrics) {
+ PasswordPolicy passwordPolicy) {
if (SecurityLog.isLoggingEnabled()) {
final int affectedUserId = parent ? getProfileParentId(userId) : userId;
SecurityLog.writeEvent(SecurityLog.TAG_PASSWORD_COMPLEXITY_SET, who.getPackageName(),
- userId, affectedUserId, metrics.length, metrics.quality, metrics.letters,
- metrics.nonLetter, metrics.numeric, metrics.upperCase, metrics.lowerCase,
- metrics.symbols);
+ userId, affectedUserId, passwordPolicy.length, passwordPolicy.quality,
+ passwordPolicy.letters, passwordPolicy.nonLetter, passwordPolicy.numeric,
+ passwordPolicy.upperCase, passwordPolicy.lowerCase, passwordPolicy.symbols);
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceStateCacheImpl.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceStateCacheImpl.java
new file mode 100644
index 000000000000..c3cb9b035ed2
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceStateCacheImpl.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.devicepolicy;
+
+import android.app.admin.DeviceStateCache;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
+
+/**
+ * Implementation of {@link DeviceStateCache}, to which {@link DevicePolicyManagerService} pushes
+ * device state.
+ *
+ */
+public class DeviceStateCacheImpl extends DeviceStateCache {
+ /**
+ * Lock object. For simplicity we just always use this as the lock. We could use each object
+ * as a lock object to make it more fine-grained, but that'd make copy-paste error-prone.
+ */
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private boolean mIsDeviceProvisioned = false;
+
+ @Override
+ public boolean isDeviceProvisioned() {
+ return mIsDeviceProvisioned;
+ }
+
+ /** Update the device provisioned flag for USER_SYSTEM */
+ public void setDeviceProvisioned(boolean provisioned) {
+ synchronized (mLock) {
+ mIsDeviceProvisioned = provisioned;
+ }
+ }
+
+ /** Dump content */
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("Device state cache:");
+ pw.increaseIndent();
+ pw.println("Device provisioned: " + mIsDeviceProvisioned);
+ pw.decreaseIndent();
+ }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java
index 0838fbc536c1..7cfbcc87edc3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java
@@ -51,7 +51,7 @@ class RemoteBugreportUtils {
static final long REMOTE_BUGREPORT_TIMEOUT_MILLIS = 10 * DateUtils.MINUTE_IN_MILLIS;
static final String CTL_STOP = "ctl.stop";
- static final String REMOTE_BUGREPORT_SERVICE = "bugreportremote";
+ static final String REMOTE_BUGREPORT_SERVICE = "bugreportd";
static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport";
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index ae98bdf0831c..9f9ff4f1091d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -91,6 +91,7 @@ import com.android.server.broadcastradio.BroadcastRadioService;
import com.android.server.camera.CameraServiceProxy;
import com.android.server.clipboard.ClipboardService;
import com.android.server.compat.PlatformCompat;
+import com.android.server.compat.PlatformCompatNative;
import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.contentcapture.ContentCaptureManagerInternal;
import com.android.server.coverage.CoverageService;
@@ -142,7 +143,6 @@ import com.android.server.security.KeyAttestationApplicationIdProviderService;
import com.android.server.security.KeyChainSystemService;
import com.android.server.signedconfig.SignedConfigService;
import com.android.server.soundtrigger.SoundTriggerService;
-import com.android.server.stats.StatsCompanionService;
import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.storage.DeviceStorageMonitorService;
import com.android.server.telecom.TelecomLoaderService;
@@ -206,6 +206,8 @@ public final class SystemServer {
"com.android.server.print.PrintManagerService";
private static final String COMPANION_DEVICE_MANAGER_SERVICE_CLASS =
"com.android.server.companion.CompanionDeviceManagerService";
+ private static final String STATS_COMPANION_SERVICE_LIFECYCLE_CLASS =
+ "com.android.server.stats.StatsCompanionService$Lifecycle";
private static final String USB_SERVICE_CLASS =
"com.android.server.usb.UsbService$Lifecycle";
private static final String MIDI_SERVICE_CLASS =
@@ -504,7 +506,7 @@ public final class SystemServer {
mRuntimeStartElapsedTime, mRuntimeStartUptime);
LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
// Prepare the thread pool for init tasks that can be parallelized
- SystemServerInitThreadPool.get();
+ SystemServerInitThreadPool.start();
} finally {
t.traceEnd(); // InitBeforeStartServices
}
@@ -638,14 +640,16 @@ public final class SystemServer {
Slog.i(TAG, "Reading configuration...");
final String TAG_SYSTEM_CONFIG = "ReadingSystemConfig";
t.traceBegin(TAG_SYSTEM_CONFIG);
- SystemServerInitThreadPool.get().submit(SystemConfig::getInstance, TAG_SYSTEM_CONFIG);
+ SystemServerInitThreadPool.submit(SystemConfig::getInstance, TAG_SYSTEM_CONFIG);
t.traceEnd();
// Platform compat service is used by ActivityManagerService, PackageManagerService, and
// possibly others in the future. b/135010838.
t.traceBegin("PlatformCompat");
- ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE,
- new PlatformCompat(mSystemContext));
+ PlatformCompat platformCompat = new PlatformCompat(mSystemContext);
+ ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE, platformCompat);
+ ServiceManager.addService(Context.PLATFORM_COMPAT_NATIVE_SERVICE,
+ new PlatformCompatNative(platformCompat));
t.traceEnd();
// Wait for installd to finish starting up so that it has a chance to
@@ -807,7 +811,7 @@ public final class SystemServer {
// Manages Overlay packages
t.traceBegin("StartOverlayManagerService");
- mSystemServiceManager.startService(new OverlayManagerService(mSystemContext, installer));
+ mSystemServiceManager.startService(new OverlayManagerService(mSystemContext));
t.traceEnd();
t.traceBegin("StartSensorPrivacyService");
@@ -824,7 +828,7 @@ public final class SystemServer {
// service, and permissions service, therefore we start it after them.
// Start sensor service in a separate thread. Completion should be checked
// before using it.
- mSensorServiceStart = SystemServerInitThreadPool.get().submit(() -> {
+ mSensorServiceStart = SystemServerInitThreadPool.submit(() -> {
TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
traceLog.traceBegin(START_SENSOR_SERVICE);
startSensorService();
@@ -926,7 +930,6 @@ public final class SystemServer {
false);
boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
false);
- boolean disableSlices = SystemProperties.getBoolean("config.disable_slices", false);
boolean enableLeftyService = SystemProperties.getBoolean("config.enable_lefty", false);
boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
@@ -952,7 +955,7 @@ public final class SystemServer {
// ensure that it completes before the 32 bit relro process is forked
// from the zygote. In the event that it takes too long, the webview
// RELRO process will block, but it will do so without holding any locks.
- mZygotePreload = SystemServerInitThreadPool.get().submit(() -> {
+ mZygotePreload = SystemServerInitThreadPool.submit(() -> {
try {
Slog.i(TAG, SECONDARY_ZYGOTE_PRELOAD);
TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
@@ -1064,7 +1067,7 @@ public final class SystemServer {
// Start receiving calls from HIDL services. Start in in a separate thread
// because it need to connect to SensorManager. This have to start
// after START_SENSOR_SERVICE is done.
- SystemServerInitThreadPool.get().submit(() -> {
+ SystemServerInitThreadPool.submit(() -> {
TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
traceLog.traceBegin(START_HIDL_SERVICES);
startHidlServices();
@@ -1890,7 +1893,7 @@ public final class SystemServer {
t.traceEnd();
}
- if (!disableSlices) {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_SLICES_DISABLED)) {
t.traceBegin("StartSliceManagerService");
mSystemServiceManager.startService(SLICE_MANAGER_SERVICE_CLASS);
t.traceEnd();
@@ -1910,7 +1913,7 @@ public final class SystemServer {
// Statsd helper
t.traceBegin("StartStatsCompanionService");
- mSystemServiceManager.startService(StatsCompanionService.Lifecycle.class);
+ mSystemServiceManager.startService(STATS_COMPANION_SERVICE_LIFECYCLE_CLASS);
t.traceEnd();
// Incidentd and dumpstated helper
@@ -2103,7 +2106,7 @@ public final class SystemServer {
final String WEBVIEW_PREPARATION = "WebViewFactoryPreparation";
Future<?> webviewPrep = null;
if (!mOnlyCore && mWebViewUpdateService != null) {
- webviewPrep = SystemServerInitThreadPool.get().submit(() -> {
+ webviewPrep = SystemServerInitThreadPool.submit(() -> {
Slog.i(TAG, WEBVIEW_PREPARATION);
TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
traceLog.traceBegin(WEBVIEW_PREPARATION);
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 8f8f9f9bbf55..1ca96ed80e5e 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -1,87 +1,10 @@
-// AIDL interfaces between the core system and the networking mainline module.
-aidl_interface {
- name: "ipmemorystore-aidl-interfaces",
- local_include_dir: "java",
- srcs: [
- "java/android/net/IIpMemoryStore.aidl",
- "java/android/net/IIpMemoryStoreCallbacks.aidl",
- "java/android/net/ipmemorystore/**/*.aidl",
- ],
- backend: {
- ndk: {
- enabled: false,
- },
- cpp: {
- enabled: false,
- },
- },
- api_dir: "aidl/ipmemorystore",
- versions: [
- "1",
- "2",
- "3",
- ],
-}
-
-aidl_interface {
- name: "networkstack-aidl-interfaces",
- local_include_dir: "java",
- include_dirs: ["frameworks/base/core/java"], // For framework parcelables.
- srcs: [
- "java/android/net/DhcpResultsParcelable.aidl",
- "java/android/net/INetworkMonitor.aidl",
- "java/android/net/INetworkMonitorCallbacks.aidl",
- "java/android/net/INetworkStackConnector.aidl",
- "java/android/net/INetworkStackStatusCallback.aidl",
- "java/android/net/InitialConfigurationParcelable.aidl",
- "java/android/net/NattKeepalivePacketDataParcelable.aidl",
- "java/android/net/PrivateDnsConfigParcel.aidl",
- "java/android/net/ProvisioningConfigurationParcelable.aidl",
- "java/android/net/TcpKeepalivePacketDataParcelable.aidl",
- "java/android/net/dhcp/DhcpServingParamsParcel.aidl",
- "java/android/net/dhcp/IDhcpServer.aidl",
- "java/android/net/dhcp/IDhcpServerCallbacks.aidl",
- "java/android/net/ip/IIpClient.aidl",
- "java/android/net/ip/IIpClientCallbacks.aidl",
- ],
- backend: {
- ndk: {
- enabled: false,
- },
- cpp: {
- enabled: false,
- },
- },
- api_dir: "aidl/networkstack",
- imports: ["ipmemorystore-aidl-interfaces"],
- versions: [
- "1",
- "2",
- "3",
- ],
-}
-
java_library_static {
name: "services.net",
srcs: ["java/**/*.java"],
static_libs: [
"dnsresolver_aidl_interface-V2-java",
- "ipmemorystore-client",
"netd_aidl_interface-java",
- "networkstack-aidl-interfaces-V3-java",
- ],
-}
-
-java_library_static {
- name: "ipmemorystore-client",
- sdk_version: "system_current",
- srcs: [
- ":framework-annotations",
- "java/android/net/IpMemoryStoreClient.java",
- "java/android/net/ipmemorystore/**/*.java",
- ],
- static_libs: [
- "ipmemorystore-aidl-interfaces-V3-java",
+ "networkstack-client",
],
}
diff --git a/services/net/aidl/ipmemorystore/1/android/net/IIpMemoryStore.aidl b/services/net/aidl/ipmemorystore/1/android/net/IIpMemoryStore.aidl
deleted file mode 100644
index a8cbab26190f..000000000000
--- a/services/net/aidl/ipmemorystore/1/android/net/IIpMemoryStore.aidl
+++ /dev/null
@@ -1,9 +0,0 @@
-package android.net;
-interface IIpMemoryStore {
- oneway void storeNetworkAttributes(String l2Key, in android.net.ipmemorystore.NetworkAttributesParcelable attributes, android.net.ipmemorystore.IOnStatusListener listener);
- oneway void storeBlob(String l2Key, String clientId, String name, in android.net.ipmemorystore.Blob data, android.net.ipmemorystore.IOnStatusListener listener);
- oneway void findL2Key(in android.net.ipmemorystore.NetworkAttributesParcelable attributes, android.net.ipmemorystore.IOnL2KeyResponseListener listener);
- oneway void isSameNetwork(String l2Key1, String l2Key2, android.net.ipmemorystore.IOnSameL3NetworkResponseListener listener);
- oneway void retrieveNetworkAttributes(String l2Key, android.net.ipmemorystore.IOnNetworkAttributesRetrievedListener listener);
- oneway void retrieveBlob(String l2Key, String clientId, String name, android.net.ipmemorystore.IOnBlobRetrievedListener listener);
-}
diff --git a/services/net/aidl/ipmemorystore/1/android/net/IIpMemoryStoreCallbacks.aidl b/services/net/aidl/ipmemorystore/1/android/net/IIpMemoryStoreCallbacks.aidl
deleted file mode 100644
index cf02c26c2fe3..000000000000
--- a/services/net/aidl/ipmemorystore/1/android/net/IIpMemoryStoreCallbacks.aidl
+++ /dev/null
@@ -1,4 +0,0 @@
-package android.net;
-interface IIpMemoryStoreCallbacks {
- oneway void onIpMemoryStoreFetched(in android.net.IIpMemoryStore ipMemoryStore);
-}
diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/Blob.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/Blob.aidl
deleted file mode 100644
index 291dbef817e6..000000000000
--- a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/Blob.aidl
+++ /dev/null
@@ -1,4 +0,0 @@
-package android.net.ipmemorystore;
-parcelable Blob {
- byte[] data;
-}
diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl
deleted file mode 100644
index 52f40d49abd5..000000000000
--- a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl
+++ /dev/null
@@ -1,4 +0,0 @@
-package android.net.ipmemorystore;
-interface IOnBlobRetrievedListener {
- oneway void onBlobRetrieved(in android.net.ipmemorystore.StatusParcelable status, in String l2Key, in String name, in android.net.ipmemorystore.Blob data);
-}
diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl
deleted file mode 100644
index 785351435d73..000000000000
--- a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl
+++ /dev/null
@@ -1,4 +0,0 @@
-package android.net.ipmemorystore;
-interface IOnL2KeyResponseListener {
- oneway void onL2KeyResponse(in android.net.ipmemorystore.StatusParcelable status, in String l2Key);
-}
diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl
deleted file mode 100644
index 3dd2ae6e9bab..000000000000
--- a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl
+++ /dev/null
@@ -1,4 +0,0 @@
-package android.net.ipmemorystore;
-interface IOnNetworkAttributesRetrievedListener {
- oneway void onNetworkAttributesRetrieved(in android.net.ipmemorystore.StatusParcelable status, in String l2Key, in android.net.ipmemorystore.NetworkAttributesParcelable attributes);
-}
diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl
deleted file mode 100644
index 46d4ecb9ed7c..000000000000
--- a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl
+++ /dev/null
@@ -1,4 +0,0 @@
-package android.net.ipmemorystore;
-interface IOnSameL3NetworkResponseListener {
- oneway void onSameL3NetworkResponse(in android.net.ipmemorystore.StatusParcelable status, in android.net.ipmemorystore.SameL3NetworkResponseParcelable response);
-}
diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnStatusListener.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnStatusListener.aidl
deleted file mode 100644
index 54e654b80c9e..000000000000
--- a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/IOnStatusListener.aidl
+++ /dev/null
@@ -1,4 +0,0 @@
-package android.net.ipmemorystore;
-interface IOnStatusListener {
- oneway void onComplete(in android.net.ipmemorystore.StatusParcelable status);
-}
diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/NetworkAttributesParcelable.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
deleted file mode 100644
index 9531ea3963fb..000000000000
--- a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
+++ /dev/null
@@ -1,8 +0,0 @@
-package android.net.ipmemorystore;
-parcelable NetworkAttributesParcelable {
- byte[] assignedV4Address;
- long assignedV4AddressExpiry;
- String groupHint;
- android.net.ipmemorystore.Blob[] dnsAddresses;
- int mtu;
-}
diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl
deleted file mode 100644
index 414272b49f1d..000000000000
--- a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl
+++ /dev/null
@@ -1,6 +0,0 @@
-package android.net.ipmemorystore;
-parcelable SameL3NetworkResponseParcelable {
- String l2Key1;
- String l2Key2;
- float confidence;
-}
diff --git a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/StatusParcelable.aidl b/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/StatusParcelable.aidl
deleted file mode 100644
index 92c6779b5dc0..000000000000
--- a/services/net/aidl/ipmemorystore/1/android/net/ipmemorystore/StatusParcelable.aidl
+++ /dev/null
@@ -1,4 +0,0 @@
-package android.net.ipmemorystore;
-parcelable StatusParcelable {
- int resultCode;
-}
diff --git a/services/net/aidl/ipmemorystore/2/android/net/IIpMemoryStore.aidl b/services/net/aidl/ipmemorystore/2/android/net/IIpMemoryStore.aidl
deleted file mode 100644
index a8cbab26190f..000000000000
--- a/services/net/aidl/ipmemorystore/2/android/net/IIpMemoryStore.aidl
+++ /dev/null
@@ -1,9 +0,0 @@
-package android.net;
-interface IIpMemoryStore {
- oneway void storeNetworkAttributes(String l2Key, in android.net.ipmemorystore.NetworkAttributesParcelable attributes, android.net.ipmemorystore.IOnStatusListener listener);
- oneway void storeBlob(String l2Key, String clientId, String name, in android.net.ipmemorystore.Blob data, android.net.ipmemorystore.IOnStatusListener listener);
- oneway void findL2Key(in android.net.ipmemorystore.NetworkAttributesParcelable attributes, android.net.ipmemorystore.IOnL2KeyResponseListener listener);
- oneway void isSameNetwork(String l2Key1, String l2Key2, android.net.ipmemorystore.IOnSameL3NetworkResponseListener listener);
- oneway void retrieveNetworkAttributes(String l2Key, android.net.ipmemorystore.IOnNetworkAttributesRetrievedListener listener);
- oneway void retrieveBlob(String l2Key, String clientId, String name, android.net.ipmemorystore.IOnBlobRetrievedListener listener);
-}
diff --git a/services/net/aidl/ipmemorystore/2/android/net/IIpMemoryStoreCallbacks.aidl b/services/net/aidl/ipmemorystore/2/android/net/IIpMemoryStoreCallbacks.aidl
deleted file mode 100644
index cf02c26c2fe3..000000000000
--- a/services/net/aidl/ipmemorystore/2/android/net/IIpMemoryStoreCallbacks.aidl
+++ /dev/null
@@ -1,4 +0,0 @@
-package android.net;
-interface IIpMemoryStoreCallbacks {
- oneway void onIpMemoryStoreFetched(in android.net.IIpMemoryStore ipMemoryStore);
-}
diff --git a/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/Blob.aidl b/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/Blob.aidl
deleted file mode 100644
index 291dbef817e6..000000000000
--- a/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/Blob.aidl
+++ /dev/null
@@ -1,4 +0,0 @@
-package android.net.ipmemorystore;
-parcelable Blob {
- byte[] data;
-}
diff --git a/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl b/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl
deleted file mode 100644
index 52f40d49abd5..000000000000
--- a/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl
+++ /dev/null
@@ -1,4 +0,0 @@
-package android.net.ipmemorystore;
-interface IOnBlobRetrievedListener {
- oneway void onBlobRetrieved(in android.net.ipmemorystore.StatusParcelable status, in String l2Key, in String name, in android.net.ipmemorystore.Blob data);
-}
diff --git a/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl b/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl
deleted file mode 100644
index 785351435d73..000000000000
--- a/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl
+++ /dev/null
@@ -1,4 +0,0 @@
-package android.net.ipmemorystore;
-interface IOnL2KeyResponseListener {
- oneway void onL2KeyResponse(in android.net.ipmemorystore.StatusParcelable status, in String l2Key);
-}
diff --git a/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl b/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl
deleted file mode 100644
index 3dd2ae6e9bab..000000000000
--- a/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl
+++ /dev/null
@@ -1,4 +0,0 @@
-package android.net.ipmemorystore;
-interface IOnNetworkAttributesRetrievedListener {
- oneway void onNetworkAttributesRetrieved(in android.net.ipmemorystore.StatusParcelable status, in String l2Key, in android.net.ipmemorystore.NetworkAttributesParcelable attributes);
-}
diff --git a/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl b/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl
deleted file mode 100644
index 46d4ecb9ed7c..000000000000
--- a/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl
+++ /dev/null
@@ -1,4 +0,0 @@
-package android.net.ipmemorystore;
-interface IOnSameL3NetworkResponseListener {
- oneway void onSameL3NetworkResponse(in android.net.ipmemorystore.StatusParcelable status, in android.net.ipmemorystore.SameL3NetworkResponseParcelable response);
-}
diff --git a/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/IOnStatusListener.aidl b/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/IOnStatusListener.aidl
deleted file mode 100644
index 54e654b80c9e..000000000000
--- a/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/IOnStatusListener.aidl
+++ /dev/null
@@ -1,4 +0,0 @@
-package android.net.ipmemorystore;
-interface IOnStatusListener {
- oneway void onComplete(in android.net.ipmemorystore.StatusParcelable status);
-}
diff --git a/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/NetworkAttributesParcelable.aidl b/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
deleted file mode 100644
index 9531ea3963fb..000000000000
--- a/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
+++ /dev/null
@@ -1,8 +0,0 @@
-package android.net.ipmemorystore;
-parcelable NetworkAttributesParcelable {
- byte[] assignedV4Address;
- long assignedV4AddressExpiry;
- String groupHint;
- android.net.ipmemorystore.Blob[] dnsAddresses;
- int mtu;
-}
diff --git a/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl b/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl
deleted file mode 100644
index 414272b49f1d..000000000000
--- a/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl
+++ /dev/null
@@ -1,6 +0,0 @@
-package android.net.ipmemorystore;
-parcelable SameL3NetworkResponseParcelable {
- String l2Key1;
- String l2Key2;
- float confidence;
-}
diff --git a/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/StatusParcelable.aidl b/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/StatusParcelable.aidl
deleted file mode 100644
index 92c6779b5dc0..000000000000
--- a/services/net/aidl/ipmemorystore/2/android/net/ipmemorystore/StatusParcelable.aidl
+++ /dev/null
@@ -1,4 +0,0 @@
-package android.net.ipmemorystore;
-parcelable StatusParcelable {
- int resultCode;
-}
diff --git a/services/net/aidl/ipmemorystore/3/android/net/IIpMemoryStore.aidl b/services/net/aidl/ipmemorystore/3/android/net/IIpMemoryStore.aidl
deleted file mode 100644
index 30893b215001..000000000000
--- a/services/net/aidl/ipmemorystore/3/android/net/IIpMemoryStore.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net;
-interface IIpMemoryStore {
- oneway void storeNetworkAttributes(String l2Key, in android.net.ipmemorystore.NetworkAttributesParcelable attributes, android.net.ipmemorystore.IOnStatusListener listener);
- oneway void storeBlob(String l2Key, String clientId, String name, in android.net.ipmemorystore.Blob data, android.net.ipmemorystore.IOnStatusListener listener);
- oneway void findL2Key(in android.net.ipmemorystore.NetworkAttributesParcelable attributes, android.net.ipmemorystore.IOnL2KeyResponseListener listener);
- oneway void isSameNetwork(String l2Key1, String l2Key2, android.net.ipmemorystore.IOnSameL3NetworkResponseListener listener);
- oneway void retrieveNetworkAttributes(String l2Key, android.net.ipmemorystore.IOnNetworkAttributesRetrievedListener listener);
- oneway void retrieveBlob(String l2Key, String clientId, String name, android.net.ipmemorystore.IOnBlobRetrievedListener listener);
- oneway void factoryReset();
-}
diff --git a/services/net/aidl/ipmemorystore/3/android/net/IIpMemoryStoreCallbacks.aidl b/services/net/aidl/ipmemorystore/3/android/net/IIpMemoryStoreCallbacks.aidl
deleted file mode 100644
index 535ae2cf25e4..000000000000
--- a/services/net/aidl/ipmemorystore/3/android/net/IIpMemoryStoreCallbacks.aidl
+++ /dev/null
@@ -1,21 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net;
-interface IIpMemoryStoreCallbacks {
- oneway void onIpMemoryStoreFetched(in android.net.IIpMemoryStore ipMemoryStore);
-}
diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/Blob.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/Blob.aidl
deleted file mode 100644
index 6d2dc0ccaaac..000000000000
--- a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/Blob.aidl
+++ /dev/null
@@ -1,21 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net.ipmemorystore;
-parcelable Blob {
- byte[] data;
-}
diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl
deleted file mode 100644
index 48c1fb8c180a..000000000000
--- a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl
+++ /dev/null
@@ -1,21 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net.ipmemorystore;
-interface IOnBlobRetrievedListener {
- oneway void onBlobRetrieved(in android.net.ipmemorystore.StatusParcelable status, in String l2Key, in String name, in android.net.ipmemorystore.Blob data);
-}
diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl
deleted file mode 100644
index aebc7240bc9e..000000000000
--- a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl
+++ /dev/null
@@ -1,21 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net.ipmemorystore;
-interface IOnL2KeyResponseListener {
- oneway void onL2KeyResponse(in android.net.ipmemorystore.StatusParcelable status, in String l2Key);
-}
diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl
deleted file mode 100644
index b66db5ab21cb..000000000000
--- a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl
+++ /dev/null
@@ -1,21 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net.ipmemorystore;
-interface IOnNetworkAttributesRetrievedListener {
- oneway void onNetworkAttributesRetrieved(in android.net.ipmemorystore.StatusParcelable status, in String l2Key, in android.net.ipmemorystore.NetworkAttributesParcelable attributes);
-}
diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl
deleted file mode 100644
index e9f2db445d38..000000000000
--- a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl
+++ /dev/null
@@ -1,21 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net.ipmemorystore;
-interface IOnSameL3NetworkResponseListener {
- oneway void onSameL3NetworkResponse(in android.net.ipmemorystore.StatusParcelable status, in android.net.ipmemorystore.SameL3NetworkResponseParcelable response);
-}
diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnStatusListener.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnStatusListener.aidl
deleted file mode 100644
index 49172cea9587..000000000000
--- a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnStatusListener.aidl
+++ /dev/null
@@ -1,21 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net.ipmemorystore;
-interface IOnStatusListener {
- oneway void onComplete(in android.net.ipmemorystore.StatusParcelable status);
-}
diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/NetworkAttributesParcelable.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
deleted file mode 100644
index 188db20b531a..000000000000
--- a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
+++ /dev/null
@@ -1,25 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net.ipmemorystore;
-parcelable NetworkAttributesParcelable {
- byte[] assignedV4Address;
- long assignedV4AddressExpiry;
- String groupHint;
- android.net.ipmemorystore.Blob[] dnsAddresses;
- int mtu;
-}
diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl
deleted file mode 100644
index 7a2ed48241e7..000000000000
--- a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl
+++ /dev/null
@@ -1,23 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net.ipmemorystore;
-parcelable SameL3NetworkResponseParcelable {
- String l2Key1;
- String l2Key2;
- float confidence;
-}
diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/StatusParcelable.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/StatusParcelable.aidl
deleted file mode 100644
index d9b067875e84..000000000000
--- a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/StatusParcelable.aidl
+++ /dev/null
@@ -1,21 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net.ipmemorystore;
-parcelable StatusParcelable {
- int resultCode;
-}
diff --git a/services/net/aidl/networkstack/1/android/net/DhcpResultsParcelable.aidl b/services/net/aidl/networkstack/1/android/net/DhcpResultsParcelable.aidl
deleted file mode 100644
index 92b5345ee221..000000000000
--- a/services/net/aidl/networkstack/1/android/net/DhcpResultsParcelable.aidl
+++ /dev/null
@@ -1,8 +0,0 @@
-package android.net;
-parcelable DhcpResultsParcelable {
- android.net.StaticIpConfiguration baseConfiguration;
- int leaseDuration;
- int mtu;
- String serverAddress;
- String vendorInfo;
-}
diff --git a/services/net/aidl/networkstack/1/android/net/INetworkMonitor.aidl b/services/net/aidl/networkstack/1/android/net/INetworkMonitor.aidl
deleted file mode 100644
index b19f522880ec..000000000000
--- a/services/net/aidl/networkstack/1/android/net/INetworkMonitor.aidl
+++ /dev/null
@@ -1,17 +0,0 @@
-package android.net;
-interface INetworkMonitor {
- oneway void start();
- oneway void launchCaptivePortalApp();
- oneway void notifyCaptivePortalAppFinished(int response);
- oneway void setAcceptPartialConnectivity();
- oneway void forceReevaluation(int uid);
- oneway void notifyPrivateDnsChanged(in android.net.PrivateDnsConfigParcel config);
- oneway void notifyDnsResponse(int returnCode);
- oneway void notifyNetworkConnected(in android.net.LinkProperties lp, in android.net.NetworkCapabilities nc);
- oneway void notifyNetworkDisconnected();
- oneway void notifyLinkPropertiesChanged(in android.net.LinkProperties lp);
- oneway void notifyNetworkCapabilitiesChanged(in android.net.NetworkCapabilities nc);
- const int NETWORK_TEST_RESULT_VALID = 0;
- const int NETWORK_TEST_RESULT_INVALID = 1;
- const int NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY = 2;
-}
diff --git a/services/net/aidl/networkstack/1/android/net/INetworkMonitorCallbacks.aidl b/services/net/aidl/networkstack/1/android/net/INetworkMonitorCallbacks.aidl
deleted file mode 100644
index ee9871ddcd15..000000000000
--- a/services/net/aidl/networkstack/1/android/net/INetworkMonitorCallbacks.aidl
+++ /dev/null
@@ -1,8 +0,0 @@
-package android.net;
-interface INetworkMonitorCallbacks {
- oneway void onNetworkMonitorCreated(in android.net.INetworkMonitor networkMonitor);
- oneway void notifyNetworkTested(int testResult, @nullable String redirectUrl);
- oneway void notifyPrivateDnsConfigResolved(in android.net.PrivateDnsConfigParcel config);
- oneway void showProvisioningNotification(String action, String packageName);
- oneway void hideProvisioningNotification();
-}
diff --git a/services/net/aidl/networkstack/1/android/net/INetworkStackConnector.aidl b/services/net/aidl/networkstack/1/android/net/INetworkStackConnector.aidl
deleted file mode 100644
index 7da11e476c0e..000000000000
--- a/services/net/aidl/networkstack/1/android/net/INetworkStackConnector.aidl
+++ /dev/null
@@ -1,7 +0,0 @@
-package android.net;
-interface INetworkStackConnector {
- oneway void makeDhcpServer(in String ifName, in android.net.dhcp.DhcpServingParamsParcel params, in android.net.dhcp.IDhcpServerCallbacks cb);
- oneway void makeNetworkMonitor(in android.net.Network network, String name, in android.net.INetworkMonitorCallbacks cb);
- oneway void makeIpClient(in String ifName, in android.net.ip.IIpClientCallbacks callbacks);
- oneway void fetchIpMemoryStore(in android.net.IIpMemoryStoreCallbacks cb);
-}
diff --git a/services/net/aidl/networkstack/1/android/net/INetworkStackStatusCallback.aidl b/services/net/aidl/networkstack/1/android/net/INetworkStackStatusCallback.aidl
deleted file mode 100644
index f6ca6f7a78e2..000000000000
--- a/services/net/aidl/networkstack/1/android/net/INetworkStackStatusCallback.aidl
+++ /dev/null
@@ -1,4 +0,0 @@
-package android.net;
-interface INetworkStackStatusCallback {
- oneway void onStatusAvailable(int statusCode);
-}
diff --git a/services/net/aidl/networkstack/1/android/net/InitialConfigurationParcelable.aidl b/services/net/aidl/networkstack/1/android/net/InitialConfigurationParcelable.aidl
deleted file mode 100644
index c80a78785b3b..000000000000
--- a/services/net/aidl/networkstack/1/android/net/InitialConfigurationParcelable.aidl
+++ /dev/null
@@ -1,7 +0,0 @@
-package android.net;
-parcelable InitialConfigurationParcelable {
- android.net.LinkAddress[] ipAddresses;
- android.net.IpPrefix[] directlyConnectedRoutes;
- String[] dnsServers;
- String gateway;
-}
diff --git a/services/net/aidl/networkstack/1/android/net/PrivateDnsConfigParcel.aidl b/services/net/aidl/networkstack/1/android/net/PrivateDnsConfigParcel.aidl
deleted file mode 100644
index 2de790bb7754..000000000000
--- a/services/net/aidl/networkstack/1/android/net/PrivateDnsConfigParcel.aidl
+++ /dev/null
@@ -1,5 +0,0 @@
-package android.net;
-parcelable PrivateDnsConfigParcel {
- String hostname;
- String[] ips;
-}
diff --git a/services/net/aidl/networkstack/1/android/net/ProvisioningConfigurationParcelable.aidl b/services/net/aidl/networkstack/1/android/net/ProvisioningConfigurationParcelable.aidl
deleted file mode 100644
index 3a6c30496fd8..000000000000
--- a/services/net/aidl/networkstack/1/android/net/ProvisioningConfigurationParcelable.aidl
+++ /dev/null
@@ -1,15 +0,0 @@
-package android.net;
-parcelable ProvisioningConfigurationParcelable {
- boolean enableIPv4;
- boolean enableIPv6;
- boolean usingMultinetworkPolicyTracker;
- boolean usingIpReachabilityMonitor;
- int requestedPreDhcpActionMs;
- android.net.InitialConfigurationParcelable initialConfig;
- android.net.StaticIpConfiguration staticIpConfig;
- android.net.apf.ApfCapabilities apfCapabilities;
- int provisioningTimeoutMs;
- int ipv6AddrGenMode;
- android.net.Network network;
- String displayName;
-}
diff --git a/services/net/aidl/networkstack/1/android/net/TcpKeepalivePacketDataParcelable.aidl b/services/net/aidl/networkstack/1/android/net/TcpKeepalivePacketDataParcelable.aidl
deleted file mode 100644
index e121c064f7ac..000000000000
--- a/services/net/aidl/networkstack/1/android/net/TcpKeepalivePacketDataParcelable.aidl
+++ /dev/null
@@ -1,13 +0,0 @@
-package android.net;
-parcelable TcpKeepalivePacketDataParcelable {
- byte[] srcAddress;
- int srcPort;
- byte[] dstAddress;
- int dstPort;
- int seq;
- int ack;
- int rcvWnd;
- int rcvWndScale;
- int tos;
- int ttl;
-}
diff --git a/services/net/aidl/networkstack/1/android/net/dhcp/DhcpServingParamsParcel.aidl b/services/net/aidl/networkstack/1/android/net/dhcp/DhcpServingParamsParcel.aidl
deleted file mode 100644
index 67193ae904bc..000000000000
--- a/services/net/aidl/networkstack/1/android/net/dhcp/DhcpServingParamsParcel.aidl
+++ /dev/null
@@ -1,11 +0,0 @@
-package android.net.dhcp;
-parcelable DhcpServingParamsParcel {
- int serverAddr;
- int serverAddrPrefixLength;
- int[] defaultRouters;
- int[] dnsServers;
- int[] excludedAddrs;
- long dhcpLeaseTimeSecs;
- int linkMtu;
- boolean metered;
-}
diff --git a/services/net/aidl/networkstack/1/android/net/dhcp/IDhcpServer.aidl b/services/net/aidl/networkstack/1/android/net/dhcp/IDhcpServer.aidl
deleted file mode 100644
index 914315855496..000000000000
--- a/services/net/aidl/networkstack/1/android/net/dhcp/IDhcpServer.aidl
+++ /dev/null
@@ -1,10 +0,0 @@
-package android.net.dhcp;
-interface IDhcpServer {
- oneway void start(in android.net.INetworkStackStatusCallback cb);
- oneway void updateParams(in android.net.dhcp.DhcpServingParamsParcel params, in android.net.INetworkStackStatusCallback cb);
- oneway void stop(in android.net.INetworkStackStatusCallback cb);
- const int STATUS_UNKNOWN = 0;
- const int STATUS_SUCCESS = 1;
- const int STATUS_INVALID_ARGUMENT = 2;
- const int STATUS_UNKNOWN_ERROR = 3;
-}
diff --git a/services/net/aidl/networkstack/1/android/net/dhcp/IDhcpServerCallbacks.aidl b/services/net/aidl/networkstack/1/android/net/dhcp/IDhcpServerCallbacks.aidl
deleted file mode 100644
index dcc4489d52a6..000000000000
--- a/services/net/aidl/networkstack/1/android/net/dhcp/IDhcpServerCallbacks.aidl
+++ /dev/null
@@ -1,4 +0,0 @@
-package android.net.dhcp;
-interface IDhcpServerCallbacks {
- oneway void onDhcpServerCreated(int statusCode, in android.net.dhcp.IDhcpServer server);
-}
diff --git a/services/net/aidl/networkstack/1/android/net/ip/IIpClient.aidl b/services/net/aidl/networkstack/1/android/net/ip/IIpClient.aidl
deleted file mode 100644
index 95a15742a684..000000000000
--- a/services/net/aidl/networkstack/1/android/net/ip/IIpClient.aidl
+++ /dev/null
@@ -1,14 +0,0 @@
-package android.net.ip;
-interface IIpClient {
- oneway void completedPreDhcpAction();
- oneway void confirmConfiguration();
- oneway void readPacketFilterComplete(in byte[] data);
- oneway void shutdown();
- oneway void startProvisioning(in android.net.ProvisioningConfigurationParcelable req);
- oneway void stop();
- oneway void setTcpBufferSizes(in String tcpBufferSizes);
- oneway void setHttpProxy(in android.net.ProxyInfo proxyInfo);
- oneway void setMulticastFilter(boolean enabled);
- oneway void addKeepalivePacketFilter(int slot, in android.net.TcpKeepalivePacketDataParcelable pkt);
- oneway void removeKeepalivePacketFilter(int slot);
-}
diff --git a/services/net/aidl/networkstack/1/android/net/ip/IIpClientCallbacks.aidl b/services/net/aidl/networkstack/1/android/net/ip/IIpClientCallbacks.aidl
deleted file mode 100644
index d6bc8089a0be..000000000000
--- a/services/net/aidl/networkstack/1/android/net/ip/IIpClientCallbacks.aidl
+++ /dev/null
@@ -1,16 +0,0 @@
-package android.net.ip;
-interface IIpClientCallbacks {
- oneway void onIpClientCreated(in android.net.ip.IIpClient ipClient);
- oneway void onPreDhcpAction();
- oneway void onPostDhcpAction();
- oneway void onNewDhcpResults(in android.net.DhcpResultsParcelable dhcpResults);
- oneway void onProvisioningSuccess(in android.net.LinkProperties newLp);
- oneway void onProvisioningFailure(in android.net.LinkProperties newLp);
- oneway void onLinkPropertiesChange(in android.net.LinkProperties newLp);
- oneway void onReachabilityLost(in String logMsg);
- oneway void onQuit();
- oneway void installPacketFilter(in byte[] filter);
- oneway void startReadPacketFilter();
- oneway void setFallbackMulticastFilter(boolean enabled);
- oneway void setNeighborDiscoveryOffload(boolean enable);
-}
diff --git a/services/net/aidl/networkstack/2/android/net/DhcpResultsParcelable.aidl b/services/net/aidl/networkstack/2/android/net/DhcpResultsParcelable.aidl
deleted file mode 100644
index 31891de7230a..000000000000
--- a/services/net/aidl/networkstack/2/android/net/DhcpResultsParcelable.aidl
+++ /dev/null
@@ -1,9 +0,0 @@
-package android.net;
-parcelable DhcpResultsParcelable {
- android.net.StaticIpConfiguration baseConfiguration;
- int leaseDuration;
- int mtu;
- String serverAddress;
- String vendorInfo;
- String serverHostName;
-}
diff --git a/services/net/aidl/networkstack/2/android/net/INetworkMonitor.aidl b/services/net/aidl/networkstack/2/android/net/INetworkMonitor.aidl
deleted file mode 100644
index 029968b6f324..000000000000
--- a/services/net/aidl/networkstack/2/android/net/INetworkMonitor.aidl
+++ /dev/null
@@ -1,24 +0,0 @@
-package android.net;
-interface INetworkMonitor {
- oneway void start();
- oneway void launchCaptivePortalApp();
- oneway void notifyCaptivePortalAppFinished(int response);
- oneway void setAcceptPartialConnectivity();
- oneway void forceReevaluation(int uid);
- oneway void notifyPrivateDnsChanged(in android.net.PrivateDnsConfigParcel config);
- oneway void notifyDnsResponse(int returnCode);
- oneway void notifyNetworkConnected(in android.net.LinkProperties lp, in android.net.NetworkCapabilities nc);
- oneway void notifyNetworkDisconnected();
- oneway void notifyLinkPropertiesChanged(in android.net.LinkProperties lp);
- oneway void notifyNetworkCapabilitiesChanged(in android.net.NetworkCapabilities nc);
- const int NETWORK_TEST_RESULT_VALID = 0;
- const int NETWORK_TEST_RESULT_INVALID = 1;
- const int NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY = 2;
- const int NETWORK_VALIDATION_RESULT_VALID = 1;
- const int NETWORK_VALIDATION_RESULT_PARTIAL = 2;
- const int NETWORK_VALIDATION_PROBE_DNS = 4;
- const int NETWORK_VALIDATION_PROBE_HTTP = 8;
- const int NETWORK_VALIDATION_PROBE_HTTPS = 16;
- const int NETWORK_VALIDATION_PROBE_FALLBACK = 32;
- const int NETWORK_VALIDATION_PROBE_PRIVDNS = 64;
-}
diff --git a/services/net/aidl/networkstack/2/android/net/INetworkMonitorCallbacks.aidl b/services/net/aidl/networkstack/2/android/net/INetworkMonitorCallbacks.aidl
deleted file mode 100644
index ee9871ddcd15..000000000000
--- a/services/net/aidl/networkstack/2/android/net/INetworkMonitorCallbacks.aidl
+++ /dev/null
@@ -1,8 +0,0 @@
-package android.net;
-interface INetworkMonitorCallbacks {
- oneway void onNetworkMonitorCreated(in android.net.INetworkMonitor networkMonitor);
- oneway void notifyNetworkTested(int testResult, @nullable String redirectUrl);
- oneway void notifyPrivateDnsConfigResolved(in android.net.PrivateDnsConfigParcel config);
- oneway void showProvisioningNotification(String action, String packageName);
- oneway void hideProvisioningNotification();
-}
diff --git a/services/net/aidl/networkstack/2/android/net/INetworkStackConnector.aidl b/services/net/aidl/networkstack/2/android/net/INetworkStackConnector.aidl
deleted file mode 100644
index 7da11e476c0e..000000000000
--- a/services/net/aidl/networkstack/2/android/net/INetworkStackConnector.aidl
+++ /dev/null
@@ -1,7 +0,0 @@
-package android.net;
-interface INetworkStackConnector {
- oneway void makeDhcpServer(in String ifName, in android.net.dhcp.DhcpServingParamsParcel params, in android.net.dhcp.IDhcpServerCallbacks cb);
- oneway void makeNetworkMonitor(in android.net.Network network, String name, in android.net.INetworkMonitorCallbacks cb);
- oneway void makeIpClient(in String ifName, in android.net.ip.IIpClientCallbacks callbacks);
- oneway void fetchIpMemoryStore(in android.net.IIpMemoryStoreCallbacks cb);
-}
diff --git a/services/net/aidl/networkstack/2/android/net/INetworkStackStatusCallback.aidl b/services/net/aidl/networkstack/2/android/net/INetworkStackStatusCallback.aidl
deleted file mode 100644
index f6ca6f7a78e2..000000000000
--- a/services/net/aidl/networkstack/2/android/net/INetworkStackStatusCallback.aidl
+++ /dev/null
@@ -1,4 +0,0 @@
-package android.net;
-interface INetworkStackStatusCallback {
- oneway void onStatusAvailable(int statusCode);
-}
diff --git a/services/net/aidl/networkstack/2/android/net/InitialConfigurationParcelable.aidl b/services/net/aidl/networkstack/2/android/net/InitialConfigurationParcelable.aidl
deleted file mode 100644
index c80a78785b3b..000000000000
--- a/services/net/aidl/networkstack/2/android/net/InitialConfigurationParcelable.aidl
+++ /dev/null
@@ -1,7 +0,0 @@
-package android.net;
-parcelable InitialConfigurationParcelable {
- android.net.LinkAddress[] ipAddresses;
- android.net.IpPrefix[] directlyConnectedRoutes;
- String[] dnsServers;
- String gateway;
-}
diff --git a/services/net/aidl/networkstack/2/android/net/NattKeepalivePacketDataParcelable.aidl b/services/net/aidl/networkstack/2/android/net/NattKeepalivePacketDataParcelable.aidl
deleted file mode 100644
index 65de8833e6c5..000000000000
--- a/services/net/aidl/networkstack/2/android/net/NattKeepalivePacketDataParcelable.aidl
+++ /dev/null
@@ -1,7 +0,0 @@
-package android.net;
-parcelable NattKeepalivePacketDataParcelable {
- byte[] srcAddress;
- int srcPort;
- byte[] dstAddress;
- int dstPort;
-}
diff --git a/services/net/aidl/networkstack/2/android/net/PrivateDnsConfigParcel.aidl b/services/net/aidl/networkstack/2/android/net/PrivateDnsConfigParcel.aidl
deleted file mode 100644
index 2de790bb7754..000000000000
--- a/services/net/aidl/networkstack/2/android/net/PrivateDnsConfigParcel.aidl
+++ /dev/null
@@ -1,5 +0,0 @@
-package android.net;
-parcelable PrivateDnsConfigParcel {
- String hostname;
- String[] ips;
-}
diff --git a/services/net/aidl/networkstack/2/android/net/ProvisioningConfigurationParcelable.aidl b/services/net/aidl/networkstack/2/android/net/ProvisioningConfigurationParcelable.aidl
deleted file mode 100644
index 3a6c30496fd8..000000000000
--- a/services/net/aidl/networkstack/2/android/net/ProvisioningConfigurationParcelable.aidl
+++ /dev/null
@@ -1,15 +0,0 @@
-package android.net;
-parcelable ProvisioningConfigurationParcelable {
- boolean enableIPv4;
- boolean enableIPv6;
- boolean usingMultinetworkPolicyTracker;
- boolean usingIpReachabilityMonitor;
- int requestedPreDhcpActionMs;
- android.net.InitialConfigurationParcelable initialConfig;
- android.net.StaticIpConfiguration staticIpConfig;
- android.net.apf.ApfCapabilities apfCapabilities;
- int provisioningTimeoutMs;
- int ipv6AddrGenMode;
- android.net.Network network;
- String displayName;
-}
diff --git a/services/net/aidl/networkstack/2/android/net/TcpKeepalivePacketDataParcelable.aidl b/services/net/aidl/networkstack/2/android/net/TcpKeepalivePacketDataParcelable.aidl
deleted file mode 100644
index e121c064f7ac..000000000000
--- a/services/net/aidl/networkstack/2/android/net/TcpKeepalivePacketDataParcelable.aidl
+++ /dev/null
@@ -1,13 +0,0 @@
-package android.net;
-parcelable TcpKeepalivePacketDataParcelable {
- byte[] srcAddress;
- int srcPort;
- byte[] dstAddress;
- int dstPort;
- int seq;
- int ack;
- int rcvWnd;
- int rcvWndScale;
- int tos;
- int ttl;
-}
diff --git a/services/net/aidl/networkstack/2/android/net/dhcp/DhcpServingParamsParcel.aidl b/services/net/aidl/networkstack/2/android/net/dhcp/DhcpServingParamsParcel.aidl
deleted file mode 100644
index 67193ae904bc..000000000000
--- a/services/net/aidl/networkstack/2/android/net/dhcp/DhcpServingParamsParcel.aidl
+++ /dev/null
@@ -1,11 +0,0 @@
-package android.net.dhcp;
-parcelable DhcpServingParamsParcel {
- int serverAddr;
- int serverAddrPrefixLength;
- int[] defaultRouters;
- int[] dnsServers;
- int[] excludedAddrs;
- long dhcpLeaseTimeSecs;
- int linkMtu;
- boolean metered;
-}
diff --git a/services/net/aidl/networkstack/2/android/net/dhcp/IDhcpServer.aidl b/services/net/aidl/networkstack/2/android/net/dhcp/IDhcpServer.aidl
deleted file mode 100644
index 914315855496..000000000000
--- a/services/net/aidl/networkstack/2/android/net/dhcp/IDhcpServer.aidl
+++ /dev/null
@@ -1,10 +0,0 @@
-package android.net.dhcp;
-interface IDhcpServer {
- oneway void start(in android.net.INetworkStackStatusCallback cb);
- oneway void updateParams(in android.net.dhcp.DhcpServingParamsParcel params, in android.net.INetworkStackStatusCallback cb);
- oneway void stop(in android.net.INetworkStackStatusCallback cb);
- const int STATUS_UNKNOWN = 0;
- const int STATUS_SUCCESS = 1;
- const int STATUS_INVALID_ARGUMENT = 2;
- const int STATUS_UNKNOWN_ERROR = 3;
-}
diff --git a/services/net/aidl/networkstack/2/android/net/dhcp/IDhcpServerCallbacks.aidl b/services/net/aidl/networkstack/2/android/net/dhcp/IDhcpServerCallbacks.aidl
deleted file mode 100644
index dcc4489d52a6..000000000000
--- a/services/net/aidl/networkstack/2/android/net/dhcp/IDhcpServerCallbacks.aidl
+++ /dev/null
@@ -1,4 +0,0 @@
-package android.net.dhcp;
-interface IDhcpServerCallbacks {
- oneway void onDhcpServerCreated(int statusCode, in android.net.dhcp.IDhcpServer server);
-}
diff --git a/services/net/aidl/networkstack/2/android/net/ip/IIpClient.aidl b/services/net/aidl/networkstack/2/android/net/ip/IIpClient.aidl
deleted file mode 100644
index 77d5917de913..000000000000
--- a/services/net/aidl/networkstack/2/android/net/ip/IIpClient.aidl
+++ /dev/null
@@ -1,15 +0,0 @@
-package android.net.ip;
-interface IIpClient {
- oneway void completedPreDhcpAction();
- oneway void confirmConfiguration();
- oneway void readPacketFilterComplete(in byte[] data);
- oneway void shutdown();
- oneway void startProvisioning(in android.net.ProvisioningConfigurationParcelable req);
- oneway void stop();
- oneway void setTcpBufferSizes(in String tcpBufferSizes);
- oneway void setHttpProxy(in android.net.ProxyInfo proxyInfo);
- oneway void setMulticastFilter(boolean enabled);
- oneway void addKeepalivePacketFilter(int slot, in android.net.TcpKeepalivePacketDataParcelable pkt);
- oneway void removeKeepalivePacketFilter(int slot);
- oneway void setL2KeyAndGroupHint(in String l2Key, in String groupHint);
-}
diff --git a/services/net/aidl/networkstack/2/android/net/ip/IIpClientCallbacks.aidl b/services/net/aidl/networkstack/2/android/net/ip/IIpClientCallbacks.aidl
deleted file mode 100644
index d6bc8089a0be..000000000000
--- a/services/net/aidl/networkstack/2/android/net/ip/IIpClientCallbacks.aidl
+++ /dev/null
@@ -1,16 +0,0 @@
-package android.net.ip;
-interface IIpClientCallbacks {
- oneway void onIpClientCreated(in android.net.ip.IIpClient ipClient);
- oneway void onPreDhcpAction();
- oneway void onPostDhcpAction();
- oneway void onNewDhcpResults(in android.net.DhcpResultsParcelable dhcpResults);
- oneway void onProvisioningSuccess(in android.net.LinkProperties newLp);
- oneway void onProvisioningFailure(in android.net.LinkProperties newLp);
- oneway void onLinkPropertiesChange(in android.net.LinkProperties newLp);
- oneway void onReachabilityLost(in String logMsg);
- oneway void onQuit();
- oneway void installPacketFilter(in byte[] filter);
- oneway void startReadPacketFilter();
- oneway void setFallbackMulticastFilter(boolean enabled);
- oneway void setNeighborDiscoveryOffload(boolean enable);
-}
diff --git a/services/net/aidl/networkstack/3/android/net/DhcpResultsParcelable.aidl b/services/net/aidl/networkstack/3/android/net/DhcpResultsParcelable.aidl
deleted file mode 100644
index 07ff32111bb1..000000000000
--- a/services/net/aidl/networkstack/3/android/net/DhcpResultsParcelable.aidl
+++ /dev/null
@@ -1,26 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net;
-parcelable DhcpResultsParcelable {
- android.net.StaticIpConfiguration baseConfiguration;
- int leaseDuration;
- int mtu;
- String serverAddress;
- String vendorInfo;
- String serverHostName;
-}
diff --git a/services/net/aidl/networkstack/3/android/net/INetworkMonitor.aidl b/services/net/aidl/networkstack/3/android/net/INetworkMonitor.aidl
deleted file mode 100644
index 8aa68bd1c7bf..000000000000
--- a/services/net/aidl/networkstack/3/android/net/INetworkMonitor.aidl
+++ /dev/null
@@ -1,41 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net;
-interface INetworkMonitor {
- oneway void start();
- oneway void launchCaptivePortalApp();
- oneway void notifyCaptivePortalAppFinished(int response);
- oneway void setAcceptPartialConnectivity();
- oneway void forceReevaluation(int uid);
- oneway void notifyPrivateDnsChanged(in android.net.PrivateDnsConfigParcel config);
- oneway void notifyDnsResponse(int returnCode);
- oneway void notifyNetworkConnected(in android.net.LinkProperties lp, in android.net.NetworkCapabilities nc);
- oneway void notifyNetworkDisconnected();
- oneway void notifyLinkPropertiesChanged(in android.net.LinkProperties lp);
- oneway void notifyNetworkCapabilitiesChanged(in android.net.NetworkCapabilities nc);
- const int NETWORK_TEST_RESULT_VALID = 0;
- const int NETWORK_TEST_RESULT_INVALID = 1;
- const int NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY = 2;
- const int NETWORK_VALIDATION_RESULT_VALID = 1;
- const int NETWORK_VALIDATION_RESULT_PARTIAL = 2;
- const int NETWORK_VALIDATION_PROBE_DNS = 4;
- const int NETWORK_VALIDATION_PROBE_HTTP = 8;
- const int NETWORK_VALIDATION_PROBE_HTTPS = 16;
- const int NETWORK_VALIDATION_PROBE_FALLBACK = 32;
- const int NETWORK_VALIDATION_PROBE_PRIVDNS = 64;
-}
diff --git a/services/net/aidl/networkstack/3/android/net/INetworkMonitorCallbacks.aidl b/services/net/aidl/networkstack/3/android/net/INetworkMonitorCallbacks.aidl
deleted file mode 100644
index ea93729da5e7..000000000000
--- a/services/net/aidl/networkstack/3/android/net/INetworkMonitorCallbacks.aidl
+++ /dev/null
@@ -1,25 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net;
-interface INetworkMonitorCallbacks {
- oneway void onNetworkMonitorCreated(in android.net.INetworkMonitor networkMonitor);
- oneway void notifyNetworkTested(int testResult, @nullable String redirectUrl);
- oneway void notifyPrivateDnsConfigResolved(in android.net.PrivateDnsConfigParcel config);
- oneway void showProvisioningNotification(String action, String packageName);
- oneway void hideProvisioningNotification();
-}
diff --git a/services/net/aidl/networkstack/3/android/net/INetworkStackConnector.aidl b/services/net/aidl/networkstack/3/android/net/INetworkStackConnector.aidl
deleted file mode 100644
index e3a83d17eb0b..000000000000
--- a/services/net/aidl/networkstack/3/android/net/INetworkStackConnector.aidl
+++ /dev/null
@@ -1,24 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net;
-interface INetworkStackConnector {
- oneway void makeDhcpServer(in String ifName, in android.net.dhcp.DhcpServingParamsParcel params, in android.net.dhcp.IDhcpServerCallbacks cb);
- oneway void makeNetworkMonitor(in android.net.Network network, String name, in android.net.INetworkMonitorCallbacks cb);
- oneway void makeIpClient(in String ifName, in android.net.ip.IIpClientCallbacks callbacks);
- oneway void fetchIpMemoryStore(in android.net.IIpMemoryStoreCallbacks cb);
-}
diff --git a/services/net/aidl/networkstack/3/android/net/INetworkStackStatusCallback.aidl b/services/net/aidl/networkstack/3/android/net/INetworkStackStatusCallback.aidl
deleted file mode 100644
index 3112a081735a..000000000000
--- a/services/net/aidl/networkstack/3/android/net/INetworkStackStatusCallback.aidl
+++ /dev/null
@@ -1,21 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net;
-interface INetworkStackStatusCallback {
- oneway void onStatusAvailable(int statusCode);
-}
diff --git a/services/net/aidl/networkstack/3/android/net/InitialConfigurationParcelable.aidl b/services/net/aidl/networkstack/3/android/net/InitialConfigurationParcelable.aidl
deleted file mode 100644
index f846b26af808..000000000000
--- a/services/net/aidl/networkstack/3/android/net/InitialConfigurationParcelable.aidl
+++ /dev/null
@@ -1,24 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net;
-parcelable InitialConfigurationParcelable {
- android.net.LinkAddress[] ipAddresses;
- android.net.IpPrefix[] directlyConnectedRoutes;
- String[] dnsServers;
- String gateway;
-}
diff --git a/services/net/aidl/networkstack/3/android/net/NattKeepalivePacketDataParcelable.aidl b/services/net/aidl/networkstack/3/android/net/NattKeepalivePacketDataParcelable.aidl
deleted file mode 100644
index de75940f5a50..000000000000
--- a/services/net/aidl/networkstack/3/android/net/NattKeepalivePacketDataParcelable.aidl
+++ /dev/null
@@ -1,24 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net;
-parcelable NattKeepalivePacketDataParcelable {
- byte[] srcAddress;
- int srcPort;
- byte[] dstAddress;
- int dstPort;
-}
diff --git a/services/net/aidl/networkstack/3/android/net/PrivateDnsConfigParcel.aidl b/services/net/aidl/networkstack/3/android/net/PrivateDnsConfigParcel.aidl
deleted file mode 100644
index cf0fbce94c91..000000000000
--- a/services/net/aidl/networkstack/3/android/net/PrivateDnsConfigParcel.aidl
+++ /dev/null
@@ -1,22 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net;
-parcelable PrivateDnsConfigParcel {
- String hostname;
- String[] ips;
-}
diff --git a/services/net/aidl/networkstack/3/android/net/ProvisioningConfigurationParcelable.aidl b/services/net/aidl/networkstack/3/android/net/ProvisioningConfigurationParcelable.aidl
deleted file mode 100644
index c0f2d4d1747e..000000000000
--- a/services/net/aidl/networkstack/3/android/net/ProvisioningConfigurationParcelable.aidl
+++ /dev/null
@@ -1,32 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net;
-parcelable ProvisioningConfigurationParcelable {
- boolean enableIPv4;
- boolean enableIPv6;
- boolean usingMultinetworkPolicyTracker;
- boolean usingIpReachabilityMonitor;
- int requestedPreDhcpActionMs;
- android.net.InitialConfigurationParcelable initialConfig;
- android.net.StaticIpConfiguration staticIpConfig;
- android.net.apf.ApfCapabilities apfCapabilities;
- int provisioningTimeoutMs;
- int ipv6AddrGenMode;
- android.net.Network network;
- String displayName;
-}
diff --git a/services/net/aidl/networkstack/3/android/net/TcpKeepalivePacketDataParcelable.aidl b/services/net/aidl/networkstack/3/android/net/TcpKeepalivePacketDataParcelable.aidl
deleted file mode 100644
index 5926794c2e8a..000000000000
--- a/services/net/aidl/networkstack/3/android/net/TcpKeepalivePacketDataParcelable.aidl
+++ /dev/null
@@ -1,30 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net;
-parcelable TcpKeepalivePacketDataParcelable {
- byte[] srcAddress;
- int srcPort;
- byte[] dstAddress;
- int dstPort;
- int seq;
- int ack;
- int rcvWnd;
- int rcvWndScale;
- int tos;
- int ttl;
-}
diff --git a/services/net/aidl/networkstack/3/android/net/dhcp/DhcpServingParamsParcel.aidl b/services/net/aidl/networkstack/3/android/net/dhcp/DhcpServingParamsParcel.aidl
deleted file mode 100644
index 7ab156f10553..000000000000
--- a/services/net/aidl/networkstack/3/android/net/dhcp/DhcpServingParamsParcel.aidl
+++ /dev/null
@@ -1,28 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net.dhcp;
-parcelable DhcpServingParamsParcel {
- int serverAddr;
- int serverAddrPrefixLength;
- int[] defaultRouters;
- int[] dnsServers;
- int[] excludedAddrs;
- long dhcpLeaseTimeSecs;
- int linkMtu;
- boolean metered;
-}
diff --git a/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServer.aidl b/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServer.aidl
deleted file mode 100644
index d281ecfee61d..000000000000
--- a/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServer.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net.dhcp;
-interface IDhcpServer {
- oneway void start(in android.net.INetworkStackStatusCallback cb);
- oneway void updateParams(in android.net.dhcp.DhcpServingParamsParcel params, in android.net.INetworkStackStatusCallback cb);
- oneway void stop(in android.net.INetworkStackStatusCallback cb);
- const int STATUS_UNKNOWN = 0;
- const int STATUS_SUCCESS = 1;
- const int STATUS_INVALID_ARGUMENT = 2;
- const int STATUS_UNKNOWN_ERROR = 3;
-}
diff --git a/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServerCallbacks.aidl b/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServerCallbacks.aidl
deleted file mode 100644
index 98be0ab1d540..000000000000
--- a/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServerCallbacks.aidl
+++ /dev/null
@@ -1,21 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net.dhcp;
-interface IDhcpServerCallbacks {
- oneway void onDhcpServerCreated(int statusCode, in android.net.dhcp.IDhcpServer server);
-}
diff --git a/services/net/aidl/networkstack/3/android/net/ip/IIpClient.aidl b/services/net/aidl/networkstack/3/android/net/ip/IIpClient.aidl
deleted file mode 100644
index 85c8676ab8d0..000000000000
--- a/services/net/aidl/networkstack/3/android/net/ip/IIpClient.aidl
+++ /dev/null
@@ -1,33 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net.ip;
-interface IIpClient {
- oneway void completedPreDhcpAction();
- oneway void confirmConfiguration();
- oneway void readPacketFilterComplete(in byte[] data);
- oneway void shutdown();
- oneway void startProvisioning(in android.net.ProvisioningConfigurationParcelable req);
- oneway void stop();
- oneway void setTcpBufferSizes(in String tcpBufferSizes);
- oneway void setHttpProxy(in android.net.ProxyInfo proxyInfo);
- oneway void setMulticastFilter(boolean enabled);
- oneway void addKeepalivePacketFilter(int slot, in android.net.TcpKeepalivePacketDataParcelable pkt);
- oneway void removeKeepalivePacketFilter(int slot);
- oneway void setL2KeyAndGroupHint(in String l2Key, in String groupHint);
- oneway void addNattKeepalivePacketFilter(int slot, in android.net.NattKeepalivePacketDataParcelable pkt);
-}
diff --git a/services/net/aidl/networkstack/3/android/net/ip/IIpClientCallbacks.aidl b/services/net/aidl/networkstack/3/android/net/ip/IIpClientCallbacks.aidl
deleted file mode 100644
index 7fe39ed1ed7a..000000000000
--- a/services/net/aidl/networkstack/3/android/net/ip/IIpClientCallbacks.aidl
+++ /dev/null
@@ -1,33 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
-///////////////////////////////////////////////////////////////////////////////
-
-// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
-// try to edit this file. It looks like you are doing that because you have
-// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
-// function from an interface or a field from a parcelable and it broke the
-// build. That breakage is intended.
-//
-// You must not make a backward incompatible changes to the AIDL files built
-// with the aidl_interface module type with versions property set. The module
-// type is used to build AIDL files in a way that they can be used across
-// independently updatable components of the system. If a device is shipped
-// with such a backward incompatible change, it has a high risk of breaking
-// later when a module using the interface is updated, e.g., Mainline modules.
-
-package android.net.ip;
-interface IIpClientCallbacks {
- oneway void onIpClientCreated(in android.net.ip.IIpClient ipClient);
- oneway void onPreDhcpAction();
- oneway void onPostDhcpAction();
- oneway void onNewDhcpResults(in android.net.DhcpResultsParcelable dhcpResults);
- oneway void onProvisioningSuccess(in android.net.LinkProperties newLp);
- oneway void onProvisioningFailure(in android.net.LinkProperties newLp);
- oneway void onLinkPropertiesChange(in android.net.LinkProperties newLp);
- oneway void onReachabilityLost(in String logMsg);
- oneway void onQuit();
- oneway void installPacketFilter(in byte[] filter);
- oneway void startReadPacketFilter();
- oneway void setFallbackMulticastFilter(boolean enabled);
- oneway void setNeighborDiscoveryOffload(boolean enable);
-}
diff --git a/services/net/java/android/net/DhcpResultsParcelable.aidl b/services/net/java/android/net/DhcpResultsParcelable.aidl
deleted file mode 100644
index c98d9c201342..000000000000
--- a/services/net/java/android/net/DhcpResultsParcelable.aidl
+++ /dev/null
@@ -1,28 +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 perNmissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.net.StaticIpConfiguration;
-
-parcelable DhcpResultsParcelable {
- StaticIpConfiguration baseConfiguration;
- int leaseDuration;
- int mtu;
- String serverAddress;
- String vendorInfo;
- String serverHostName;
-}
diff --git a/services/net/java/android/net/IIpMemoryStore.aidl b/services/net/java/android/net/IIpMemoryStore.aidl
deleted file mode 100644
index add221ae2e01..000000000000
--- a/services/net/java/android/net/IIpMemoryStore.aidl
+++ /dev/null
@@ -1,118 +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 android.net;
-
-import android.net.ipmemorystore.Blob;
-import android.net.ipmemorystore.NetworkAttributesParcelable;
-import android.net.ipmemorystore.IOnBlobRetrievedListener;
-import android.net.ipmemorystore.IOnL2KeyResponseListener;
-import android.net.ipmemorystore.IOnNetworkAttributesRetrievedListener;
-import android.net.ipmemorystore.IOnSameL3NetworkResponseListener;
-import android.net.ipmemorystore.IOnStatusListener;
-
-/** {@hide} */
-oneway interface IIpMemoryStore {
- /**
- * Store network attributes for a given L2 key.
- * If L2Key is null, choose automatically from the attributes ; passing null is equivalent to
- * calling findL2Key with the attributes and storing in the returned value.
- *
- * @param l2Key The L2 key for the L2 network. Clients that don't know or care about the L2
- * key and only care about grouping can pass a unique ID here like the ones
- * generated by {@code java.util.UUID.randomUUID()}, but keep in mind the low
- * relevance of such a network will lead to it being evicted soon if it's not
- * refreshed. Use findL2Key to try and find a similar L2Key to these attributes.
- * @param attributes The attributes for this network.
- * @param listener A listener that will be invoked to inform of the completion of this call,
- * or null if the client is not interested in learning about success/failure.
- * @return (through the listener) The L2 key. This is useful if the L2 key was not specified.
- * If the call failed, the L2 key will be null.
- */
- void storeNetworkAttributes(String l2Key, in NetworkAttributesParcelable attributes,
- IOnStatusListener listener);
-
- /**
- * Store a binary blob associated with an L2 key and a name.
- *
- * @param l2Key The L2 key for this network.
- * @param clientId The ID of the client.
- * @param name The name of this data.
- * @param data The data to store.
- * @param listener A listener to inform of the completion of this call, or null if the client
- * is not interested in learning about success/failure.
- * @return (through the listener) A status to indicate success or failure.
- */
- void storeBlob(String l2Key, String clientId, String name, in Blob data,
- IOnStatusListener listener);
-
- /**
- * Returns the best L2 key associated with the attributes.
- *
- * This will find a record that would be in the same group as the passed attributes. This is
- * useful to choose the key for storing a sample or private data when the L2 key is not known.
- * If multiple records are group-close to these attributes, the closest match is returned.
- * If multiple records have the same closeness, the one with the smaller (unicode codepoint
- * order) L2 key is returned.
- * If no record matches these attributes, null is returned.
- *
- * @param attributes The attributes of the network to find.
- * @param listener The listener that will be invoked to return the answer.
- * @return (through the listener) The L2 key if one matched, or null.
- */
- void findL2Key(in NetworkAttributesParcelable attributes, IOnL2KeyResponseListener listener);
-
- /**
- * Returns whether, to the best of the store's ability to tell, the two specified L2 keys point
- * to the same L3 network. Group-closeness is used to determine this.
- *
- * @param l2Key1 The key for the first network.
- * @param l2Key2 The key for the second network.
- * @param listener The listener that will be invoked to return the answer.
- * @return (through the listener) A SameL3NetworkResponse containing the answer and confidence.
- */
- void isSameNetwork(String l2Key1, String l2Key2, IOnSameL3NetworkResponseListener listener);
-
- /**
- * Retrieve the network attributes for a key.
- * If no record is present for this key, this will return null attributes.
- *
- * @param l2Key The key of the network to query.
- * @param listener The listener that will be invoked to return the answer.
- * @return (through the listener) The network attributes and the L2 key associated with
- * the query.
- */
- void retrieveNetworkAttributes(String l2Key, IOnNetworkAttributesRetrievedListener listener);
-
- /**
- * Retrieve previously stored private data.
- * If no data was stored for this L2 key and name this will return null.
- *
- * @param l2Key The L2 key.
- * @param clientId The id of the client that stored this data.
- * @param name The name of the data.
- * @param listener The listener that will be invoked to return the answer.
- * @return (through the listener) The private data (or null), with the L2 key
- * and the name of the data associated with the query.
- */
- void retrieveBlob(String l2Key, String clientId, String name,
- IOnBlobRetrievedListener listener);
-
- /**
- * Delete all data because a factory reset operation is in progress.
- */
- void factoryReset();
-}
diff --git a/services/net/java/android/net/IIpMemoryStoreCallbacks.aidl b/services/net/java/android/net/IIpMemoryStoreCallbacks.aidl
deleted file mode 100644
index 53108dbca097..000000000000
--- a/services/net/java/android/net/IIpMemoryStoreCallbacks.aidl
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.net.IIpMemoryStore;
-
-/** {@hide} */
-oneway interface IIpMemoryStoreCallbacks {
- void onIpMemoryStoreFetched(in IIpMemoryStore ipMemoryStore);
-}
diff --git a/services/net/java/android/net/INetworkMonitor.aidl b/services/net/java/android/net/INetworkMonitor.aidl
deleted file mode 100644
index 3fc81a3dadc5..000000000000
--- a/services/net/java/android/net/INetworkMonitor.aidl
+++ /dev/null
@@ -1,68 +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 perNmissions and
- * limitations under the License.
- */
-package android.net;
-
-import android.net.LinkProperties;
-import android.net.NetworkCapabilities;
-import android.net.PrivateDnsConfigParcel;
-
-/** @hide */
-oneway interface INetworkMonitor {
- // After a network has been tested this result can be sent with EVENT_NETWORK_TESTED.
- // The network should be used as a default internet connection. It was found to be:
- // 1. a functioning network providing internet access, or
- // 2. a captive portal and the user decided to use it as is.
- const int NETWORK_TEST_RESULT_VALID = 0;
-
- // After a network has been tested this result can be sent with EVENT_NETWORK_TESTED.
- // The network should not be used as a default internet connection. It was found to be:
- // 1. a captive portal and the user is prompted to sign-in, or
- // 2. a captive portal and the user did not want to use it, or
- // 3. a broken network (e.g. DNS failed, connect failed, HTTP request failed).
- const int NETWORK_TEST_RESULT_INVALID = 1;
-
- // After a network has been tested, this result can be sent with EVENT_NETWORK_TESTED.
- // The network may be used as a default internet connection, but it was found to be a partial
- // connectivity network which can get the pass result for http probe but get the failed result
- // for https probe.
- const int NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY = 2;
-
- // Network validation flags indicate probe result and types. If no NETWORK_VALIDATION_RESULT_*
- // are set, then it's equal to NETWORK_TEST_RESULT_INVALID. If NETWORK_VALIDATION_RESULT_VALID
- // is set, then the network validates and equal to NETWORK_TEST_RESULT_VALID. If
- // NETWORK_VALIDATION_RESULT_PARTIAL is set, then the network has partial connectivity which
- // is equal to NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY. NETWORK_VALIDATION_PROBE_* is set
- // when the specific probe result of the network is resolved.
- const int NETWORK_VALIDATION_RESULT_VALID = 0x01;
- const int NETWORK_VALIDATION_RESULT_PARTIAL = 0x02;
- const int NETWORK_VALIDATION_PROBE_DNS = 0x04;
- const int NETWORK_VALIDATION_PROBE_HTTP = 0x08;
- const int NETWORK_VALIDATION_PROBE_HTTPS = 0x10;
- const int NETWORK_VALIDATION_PROBE_FALLBACK = 0x20;
- const int NETWORK_VALIDATION_PROBE_PRIVDNS = 0x40;
-
- void start();
- void launchCaptivePortalApp();
- void notifyCaptivePortalAppFinished(int response);
- void setAcceptPartialConnectivity();
- void forceReevaluation(int uid);
- void notifyPrivateDnsChanged(in PrivateDnsConfigParcel config);
- void notifyDnsResponse(int returnCode);
- void notifyNetworkConnected(in LinkProperties lp, in NetworkCapabilities nc);
- void notifyNetworkDisconnected();
- void notifyLinkPropertiesChanged(in LinkProperties lp);
- void notifyNetworkCapabilitiesChanged(in NetworkCapabilities nc);
-}
diff --git a/services/net/java/android/net/INetworkMonitorCallbacks.aidl b/services/net/java/android/net/INetworkMonitorCallbacks.aidl
deleted file mode 100644
index 2c61511feb72..000000000000
--- a/services/net/java/android/net/INetworkMonitorCallbacks.aidl
+++ /dev/null
@@ -1,29 +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 android.net;
-
-import android.net.INetworkMonitor;
-import android.net.PrivateDnsConfigParcel;
-
-/** @hide */
-oneway interface INetworkMonitorCallbacks {
- void onNetworkMonitorCreated(in INetworkMonitor networkMonitor);
- void notifyNetworkTested(int testResult, @nullable String redirectUrl);
- void notifyPrivateDnsConfigResolved(in PrivateDnsConfigParcel config);
- void showProvisioningNotification(String action, String packageName);
- void hideProvisioningNotification();
-} \ No newline at end of file
diff --git a/services/net/java/android/net/INetworkStackConnector.aidl b/services/net/java/android/net/INetworkStackConnector.aidl
deleted file mode 100644
index 3751c36d6ee9..000000000000
--- a/services/net/java/android/net/INetworkStackConnector.aidl
+++ /dev/null
@@ -1,32 +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 perNmissions and
- * limitations under the License.
- */
-package android.net;
-
-import android.net.IIpMemoryStoreCallbacks;
-import android.net.INetworkMonitorCallbacks;
-import android.net.Network;
-import android.net.dhcp.DhcpServingParamsParcel;
-import android.net.dhcp.IDhcpServerCallbacks;
-import android.net.ip.IIpClientCallbacks;
-
-/** @hide */
-oneway interface INetworkStackConnector {
- void makeDhcpServer(in String ifName, in DhcpServingParamsParcel params,
- in IDhcpServerCallbacks cb);
- void makeNetworkMonitor(in Network network, String name, in INetworkMonitorCallbacks cb);
- void makeIpClient(in String ifName, in IIpClientCallbacks callbacks);
- void fetchIpMemoryStore(in IIpMemoryStoreCallbacks cb);
-}
diff --git a/services/net/java/android/net/INetworkStackStatusCallback.aidl b/services/net/java/android/net/INetworkStackStatusCallback.aidl
deleted file mode 100644
index 51032d80a172..000000000000
--- a/services/net/java/android/net/INetworkStackStatusCallback.aidl
+++ /dev/null
@@ -1,22 +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 android.net;
-
-/** @hide */
-oneway interface INetworkStackStatusCallback {
- void onStatusAvailable(int statusCode);
-} \ No newline at end of file
diff --git a/services/net/java/android/net/InitialConfigurationParcelable.aidl b/services/net/java/android/net/InitialConfigurationParcelable.aidl
deleted file mode 100644
index 3fa88c377a64..000000000000
--- a/services/net/java/android/net/InitialConfigurationParcelable.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-
-parcelable InitialConfigurationParcelable {
- LinkAddress[] ipAddresses;
- IpPrefix[] directlyConnectedRoutes;
- String[] dnsServers;
- String gateway;
-} \ No newline at end of file
diff --git a/services/net/java/android/net/IpMemoryStoreClient.java b/services/net/java/android/net/IpMemoryStoreClient.java
deleted file mode 100644
index 014b5289bace..000000000000
--- a/services/net/java/android/net/IpMemoryStoreClient.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.net.ipmemorystore.Blob;
-import android.net.ipmemorystore.NetworkAttributes;
-import android.net.ipmemorystore.OnBlobRetrievedListener;
-import android.net.ipmemorystore.OnL2KeyResponseListener;
-import android.net.ipmemorystore.OnNetworkAttributesRetrievedListener;
-import android.net.ipmemorystore.OnSameL3NetworkResponseListener;
-import android.net.ipmemorystore.OnStatusListener;
-import android.net.ipmemorystore.Status;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.concurrent.ExecutionException;
-import java.util.function.Consumer;
-
-/**
- * service used to communicate with the ip memory store service in network stack,
- * which is running in a separate module.
- * @hide
- */
-public abstract class IpMemoryStoreClient {
- private static final String TAG = IpMemoryStoreClient.class.getSimpleName();
- private final Context mContext;
-
- public IpMemoryStoreClient(@NonNull final Context context) {
- if (context == null) throw new IllegalArgumentException("missing context");
- mContext = context;
- }
-
- protected abstract void runWhenServiceReady(Consumer<IIpMemoryStore> cb)
- throws ExecutionException;
-
- @FunctionalInterface
- private interface ThrowingRunnable {
- void run() throws RemoteException;
- }
-
- private void ignoringRemoteException(ThrowingRunnable r) {
- ignoringRemoteException("Failed to execute remote procedure call", r);
- }
-
- private void ignoringRemoteException(String message, ThrowingRunnable r) {
- try {
- r.run();
- } catch (RemoteException e) {
- Log.e(TAG, message, e);
- }
- }
-
- /**
- * Store network attributes for a given L2 key.
- * If L2Key is null, choose automatically from the attributes ; passing null is equivalent to
- * calling findL2Key with the attributes and storing in the returned value.
- *
- * @param l2Key The L2 key for the L2 network. Clients that don't know or care about the L2
- * key and only care about grouping can pass a unique ID here like the ones
- * generated by {@code java.util.UUID.randomUUID()}, but keep in mind the low
- * relevance of such a network will lead to it being evicted soon if it's not
- * refreshed. Use findL2Key to try and find a similar L2Key to these attributes.
- * @param attributes The attributes for this network.
- * @param listener A listener that will be invoked to inform of the completion of this call,
- * or null if the client is not interested in learning about success/failure.
- * Through the listener, returns the L2 key. This is useful if the L2 key was not specified.
- * If the call failed, the L2 key will be null.
- */
- public void storeNetworkAttributes(@NonNull final String l2Key,
- @NonNull final NetworkAttributes attributes,
- @Nullable final OnStatusListener listener) {
- try {
- runWhenServiceReady(service -> ignoringRemoteException(
- () -> service.storeNetworkAttributes(l2Key, attributes.toParcelable(),
- OnStatusListener.toAIDL(listener))));
- } catch (ExecutionException m) {
- ignoringRemoteException("Error storing network attributes",
- () -> listener.onComplete(new Status(Status.ERROR_UNKNOWN)));
- }
- }
-
- /**
- * Store a binary blob associated with an L2 key and a name.
- *
- * @param l2Key The L2 key for this network.
- * @param clientId The ID of the client.
- * @param name The name of this data.
- * @param data The data to store.
- * @param listener A listener to inform of the completion of this call, or null if the client
- * is not interested in learning about success/failure.
- * Through the listener, returns a status to indicate success or failure.
- */
- public void storeBlob(@NonNull final String l2Key, @NonNull final String clientId,
- @NonNull final String name, @NonNull final Blob data,
- @Nullable final OnStatusListener listener) {
- try {
- runWhenServiceReady(service -> ignoringRemoteException(
- () -> service.storeBlob(l2Key, clientId, name, data,
- OnStatusListener.toAIDL(listener))));
- } catch (ExecutionException m) {
- ignoringRemoteException("Error storing blob",
- () -> listener.onComplete(new Status(Status.ERROR_UNKNOWN)));
- }
- }
-
- /**
- * Returns the best L2 key associated with the attributes.
- *
- * This will find a record that would be in the same group as the passed attributes. This is
- * useful to choose the key for storing a sample or private data when the L2 key is not known.
- * If multiple records are group-close to these attributes, the closest match is returned.
- * If multiple records have the same closeness, the one with the smaller (unicode codepoint
- * order) L2 key is returned.
- * If no record matches these attributes, null is returned.
- *
- * @param attributes The attributes of the network to find.
- * @param listener The listener that will be invoked to return the answer.
- * Through the listener, returns the L2 key if one matched, or null.
- */
- public void findL2Key(@NonNull final NetworkAttributes attributes,
- @NonNull final OnL2KeyResponseListener listener) {
- try {
- runWhenServiceReady(service -> ignoringRemoteException(
- () -> service.findL2Key(attributes.toParcelable(),
- OnL2KeyResponseListener.toAIDL(listener))));
- } catch (ExecutionException m) {
- ignoringRemoteException("Error finding L2 Key",
- () -> listener.onL2KeyResponse(new Status(Status.ERROR_UNKNOWN), null));
- }
- }
-
- /**
- * Returns whether, to the best of the store's ability to tell, the two specified L2 keys point
- * to the same L3 network. Group-closeness is used to determine this.
- *
- * @param l2Key1 The key for the first network.
- * @param l2Key2 The key for the second network.
- * @param listener The listener that will be invoked to return the answer.
- * Through the listener, a SameL3NetworkResponse containing the answer and confidence.
- */
- public void isSameNetwork(@NonNull final String l2Key1, @NonNull final String l2Key2,
- @NonNull final OnSameL3NetworkResponseListener listener) {
- try {
- runWhenServiceReady(service -> ignoringRemoteException(
- () -> service.isSameNetwork(l2Key1, l2Key2,
- OnSameL3NetworkResponseListener.toAIDL(listener))));
- } catch (ExecutionException m) {
- ignoringRemoteException("Error checking for network sameness",
- () -> listener.onSameL3NetworkResponse(new Status(Status.ERROR_UNKNOWN), null));
- }
- }
-
- /**
- * Retrieve the network attributes for a key.
- * If no record is present for this key, this will return null attributes.
- *
- * @param l2Key The key of the network to query.
- * @param listener The listener that will be invoked to return the answer.
- * Through the listener, returns the network attributes and the L2 key associated with
- * the query.
- */
- public void retrieveNetworkAttributes(@NonNull final String l2Key,
- @NonNull final OnNetworkAttributesRetrievedListener listener) {
- try {
- runWhenServiceReady(service -> ignoringRemoteException(
- () -> service.retrieveNetworkAttributes(l2Key,
- OnNetworkAttributesRetrievedListener.toAIDL(listener))));
- } catch (ExecutionException m) {
- ignoringRemoteException("Error retrieving network attributes",
- () -> listener.onNetworkAttributesRetrieved(new Status(Status.ERROR_UNKNOWN),
- null, null));
- }
- }
-
- /**
- * Retrieve previously stored private data.
- * If no data was stored for this L2 key and name this will return null.
- *
- * @param l2Key The L2 key.
- * @param clientId The id of the client that stored this data.
- * @param name The name of the data.
- * @param listener The listener that will be invoked to return the answer.
- * Through the listener, returns the private data (or null), with the L2 key
- * and the name of the data associated with the query.
- */
- public void retrieveBlob(@NonNull final String l2Key, @NonNull final String clientId,
- @NonNull final String name, @NonNull final OnBlobRetrievedListener listener) {
- try {
- runWhenServiceReady(service -> ignoringRemoteException(
- () -> service.retrieveBlob(l2Key, clientId, name,
- OnBlobRetrievedListener.toAIDL(listener))));
- } catch (ExecutionException m) {
- ignoringRemoteException("Error retrieving blob",
- () -> listener.onBlobRetrieved(new Status(Status.ERROR_UNKNOWN),
- null, null, null));
- }
- }
-
- /**
- * Wipe the data in the database upon network factory reset.
- */
- public void factoryReset() {
- try {
- runWhenServiceReady(service -> ignoringRemoteException(
- () -> service.factoryReset()));
- } catch (ExecutionException m) {
- Log.e(TAG, "Error executing factory reset", m);
- }
- }
-}
diff --git a/services/net/java/android/net/NattKeepalivePacketDataParcelable.aidl b/services/net/java/android/net/NattKeepalivePacketDataParcelable.aidl
deleted file mode 100644
index 6f006d4971fb..000000000000
--- a/services/net/java/android/net/NattKeepalivePacketDataParcelable.aidl
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-parcelable NattKeepalivePacketDataParcelable {
- byte[] srcAddress;
- int srcPort;
- byte[] dstAddress;
- int dstPort;
-}
-
diff --git a/services/net/java/android/net/PrivateDnsConfigParcel.aidl b/services/net/java/android/net/PrivateDnsConfigParcel.aidl
deleted file mode 100644
index b52fce643302..000000000000
--- a/services/net/java/android/net/PrivateDnsConfigParcel.aidl
+++ /dev/null
@@ -1,22 +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 android.net;
-
-parcelable PrivateDnsConfigParcel {
- String hostname;
- String[] ips;
-}
diff --git a/services/net/java/android/net/ProvisioningConfigurationParcelable.aidl b/services/net/java/android/net/ProvisioningConfigurationParcelable.aidl
deleted file mode 100644
index 99606fb4b7a2..000000000000
--- a/services/net/java/android/net/ProvisioningConfigurationParcelable.aidl
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-**
-** Copyright (C) 2019 The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.net;
-
-import android.net.InitialConfigurationParcelable;
-import android.net.Network;
-import android.net.StaticIpConfiguration;
-import android.net.apf.ApfCapabilities;
-
-parcelable ProvisioningConfigurationParcelable {
- boolean enableIPv4;
- boolean enableIPv6;
- boolean usingMultinetworkPolicyTracker;
- boolean usingIpReachabilityMonitor;
- int requestedPreDhcpActionMs;
- InitialConfigurationParcelable initialConfig;
- StaticIpConfiguration staticIpConfig;
- ApfCapabilities apfCapabilities;
- int provisioningTimeoutMs;
- int ipv6AddrGenMode;
- Network network;
- String displayName;
-}
diff --git a/services/net/java/android/net/TcpKeepalivePacketDataParcelable.aidl b/services/net/java/android/net/TcpKeepalivePacketDataParcelable.aidl
deleted file mode 100644
index e25168d588e7..000000000000
--- a/services/net/java/android/net/TcpKeepalivePacketDataParcelable.aidl
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-parcelable TcpKeepalivePacketDataParcelable {
- byte[] srcAddress;
- int srcPort;
- byte[] dstAddress;
- int dstPort;
- int seq;
- int ack;
- int rcvWnd;
- int rcvWndScale;
- int tos;
- int ttl;
-}
diff --git a/services/net/java/android/net/dhcp/DhcpServingParamsParcel.aidl b/services/net/java/android/net/dhcp/DhcpServingParamsParcel.aidl
deleted file mode 100644
index 7b8b9ee324bc..000000000000
--- a/services/net/java/android/net/dhcp/DhcpServingParamsParcel.aidl
+++ /dev/null
@@ -1,30 +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 android.net.dhcp;
-
-parcelable DhcpServingParamsParcel {
- int serverAddr;
- int serverAddrPrefixLength;
- int[] defaultRouters;
- int[] dnsServers;
- int[] excludedAddrs;
- long dhcpLeaseTimeSecs;
- int linkMtu;
- boolean metered;
-}
-
diff --git a/services/net/java/android/net/dhcp/IDhcpServer.aidl b/services/net/java/android/net/dhcp/IDhcpServer.aidl
deleted file mode 100644
index 559433b13962..000000000000
--- a/services/net/java/android/net/dhcp/IDhcpServer.aidl
+++ /dev/null
@@ -1,32 +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 perNmissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-import android.net.INetworkStackStatusCallback;
-import android.net.dhcp.DhcpServingParamsParcel;
-
-/** @hide */
-oneway interface IDhcpServer {
- const int STATUS_UNKNOWN = 0;
- const int STATUS_SUCCESS = 1;
- const int STATUS_INVALID_ARGUMENT = 2;
- const int STATUS_UNKNOWN_ERROR = 3;
-
- void start(in INetworkStackStatusCallback cb);
- void updateParams(in DhcpServingParamsParcel params, in INetworkStackStatusCallback cb);
- void stop(in INetworkStackStatusCallback cb);
-}
diff --git a/services/net/java/android/net/dhcp/IDhcpServerCallbacks.aidl b/services/net/java/android/net/dhcp/IDhcpServerCallbacks.aidl
deleted file mode 100644
index 7ab4dcdbe584..000000000000
--- a/services/net/java/android/net/dhcp/IDhcpServerCallbacks.aidl
+++ /dev/null
@@ -1,24 +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 perNmissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-import android.net.dhcp.IDhcpServer;
-
-/** @hide */
-oneway interface IDhcpServerCallbacks {
- void onDhcpServerCreated(int statusCode, in IDhcpServer server);
-}
diff --git a/services/net/java/android/net/ip/IIpClient.aidl b/services/net/java/android/net/ip/IIpClient.aidl
deleted file mode 100644
index 9989c52fc403..000000000000
--- a/services/net/java/android/net/ip/IIpClient.aidl
+++ /dev/null
@@ -1,38 +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 perNmissions and
- * limitations under the License.
- */
-package android.net.ip;
-
-import android.net.ProxyInfo;
-import android.net.ProvisioningConfigurationParcelable;
-import android.net.NattKeepalivePacketDataParcelable;
-import android.net.TcpKeepalivePacketDataParcelable;
-
-/** @hide */
-oneway interface IIpClient {
- void completedPreDhcpAction();
- void confirmConfiguration();
- void readPacketFilterComplete(in byte[] data);
- void shutdown();
- void startProvisioning(in ProvisioningConfigurationParcelable req);
- void stop();
- void setTcpBufferSizes(in String tcpBufferSizes);
- void setHttpProxy(in ProxyInfo proxyInfo);
- void setMulticastFilter(boolean enabled);
- void addKeepalivePacketFilter(int slot, in TcpKeepalivePacketDataParcelable pkt);
- void removeKeepalivePacketFilter(int slot);
- void setL2KeyAndGroupHint(in String l2Key, in String groupHint);
- void addNattKeepalivePacketFilter(int slot, in NattKeepalivePacketDataParcelable pkt);
-}
diff --git a/services/net/java/android/net/ip/IIpClientCallbacks.aidl b/services/net/java/android/net/ip/IIpClientCallbacks.aidl
deleted file mode 100644
index 3681416611a9..000000000000
--- a/services/net/java/android/net/ip/IIpClientCallbacks.aidl
+++ /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 perNmissions and
- * limitations under the License.
- */
-package android.net.ip;
-
-import android.net.LinkProperties;
-import android.net.ip.IIpClient;
-import android.net.DhcpResultsParcelable;
-
-/** @hide */
-oneway interface IIpClientCallbacks {
- void onIpClientCreated(in IIpClient ipClient);
-
- void onPreDhcpAction();
- void onPostDhcpAction();
-
- // This is purely advisory and not an indication of provisioning
- // success or failure. This is only here for callers that want to
- // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress).
- // DHCPv4 or static IPv4 configuration failure or success can be
- // determined by whether or not the passed-in DhcpResults object is
- // null or not.
- void onNewDhcpResults(in DhcpResultsParcelable dhcpResults);
-
- void onProvisioningSuccess(in LinkProperties newLp);
- void onProvisioningFailure(in LinkProperties newLp);
-
- // Invoked on LinkProperties changes.
- void onLinkPropertiesChange(in LinkProperties newLp);
-
- // Called when the internal IpReachabilityMonitor (if enabled) has
- // detected the loss of a critical number of required neighbors.
- void onReachabilityLost(in String logMsg);
-
- // Called when the IpClient state machine terminates.
- void onQuit();
-
- // Install an APF program to filter incoming packets.
- void installPacketFilter(in byte[] filter);
-
- // Asynchronously read back the APF program & data buffer from the wifi driver.
- // Due to Wifi HAL limitations, the current implementation only supports dumping the entire
- // buffer. In response to this request, the driver returns the data buffer asynchronously
- // by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message.
- void startReadPacketFilter();
-
- // If multicast filtering cannot be accomplished with APF, this function will be called to
- // actuate multicast filtering using another means.
- void setFallbackMulticastFilter(boolean enabled);
-
- // Enabled/disable Neighbor Discover offload functionality. This is
- // called, for example, whenever 464xlat is being started or stopped.
- void setNeighborDiscoveryOffload(boolean enable);
-} \ No newline at end of file
diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java
index c61134962fa8..33984bf1c475 100644
--- a/services/net/java/android/net/ip/IpServer.java
+++ b/services/net/java/android/net/ip/IpServer.java
@@ -93,6 +93,8 @@ public class IpServer extends StateMachine {
private static final int USB_PREFIX_LENGTH = 24;
private static final String WIFI_HOST_IFACE_ADDR = "192.168.43.1";
private static final int WIFI_HOST_IFACE_PREFIX_LENGTH = 24;
+ private static final String WIFI_P2P_IFACE_ADDR = "192.168.49.1";
+ private static final int WIFI_P2P_IFACE_PREFIX_LENGTH = 24;
private static final String WIGIG_HOST_IFACE_ADDR = "192.168.50.1";
private static final int WIGIG_HOST_IFACE_PREFIX_LENGTH = 24;
@@ -406,6 +408,9 @@ public class IpServer extends StateMachine {
} else if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
ipAsString = getRandomWifiIPv4Address();
prefixLen = WIFI_HOST_IFACE_PREFIX_LENGTH;
+ } else if (mInterfaceType == ConnectivityManager.TETHERING_WIFI_P2P) {
+ ipAsString = WIFI_P2P_IFACE_ADDR;
+ prefixLen = WIFI_P2P_IFACE_PREFIX_LENGTH;
} else if (mInterfaceType == ConnectivityManager.TETHERING_WIGIG) {
ipAsString = WIGIG_HOST_IFACE_ADDR;
prefixLen = WIGIG_HOST_IFACE_PREFIX_LENGTH;
diff --git a/services/net/java/android/net/ipmemorystore/Blob.aidl b/services/net/java/android/net/ipmemorystore/Blob.aidl
deleted file mode 100644
index 9dbef117f8a4..000000000000
--- a/services/net/java/android/net/ipmemorystore/Blob.aidl
+++ /dev/null
@@ -1,26 +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 android.net.ipmemorystore;
-
-/**
- * A blob of data opaque to the memory store. The client mutates this at its own risk,
- * and it is strongly suggested to never do it at all and treat this as immutable.
- * {@hide}
- */
-parcelable Blob {
- byte[] data;
-}
diff --git a/services/net/java/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl b/services/net/java/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl
deleted file mode 100644
index 4926feb06e55..000000000000
--- a/services/net/java/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl
+++ /dev/null
@@ -1,30 +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 android.net.ipmemorystore;
-
-import android.net.ipmemorystore.Blob;
-import android.net.ipmemorystore.StatusParcelable;
-
-/** {@hide} */
-oneway interface IOnBlobRetrievedListener {
- /**
- * Private data was retrieved for the L2 key and name specified.
- * Note this does not return the client ID, as clients are expected to only ever use one ID.
- */
- void onBlobRetrieved(in StatusParcelable status, in String l2Key, in String name,
- in Blob data);
-}
diff --git a/services/net/java/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl b/services/net/java/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl
deleted file mode 100644
index dea0cc4e2586..000000000000
--- a/services/net/java/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl
+++ /dev/null
@@ -1,27 +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 android.net.ipmemorystore;
-
-import android.net.ipmemorystore.StatusParcelable;
-
-/** {@hide} */
-oneway interface IOnL2KeyResponseListener {
- /**
- * The operation completed with the specified L2 key.
- */
- void onL2KeyResponse(in StatusParcelable status, in String l2Key);
-}
diff --git a/services/net/java/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl b/services/net/java/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl
deleted file mode 100644
index 870e217eb5b7..000000000000
--- a/services/net/java/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl
+++ /dev/null
@@ -1,30 +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 android.net.ipmemorystore;
-
-import android.net.ipmemorystore.NetworkAttributesParcelable;
-import android.net.ipmemorystore.StatusParcelable;
-
-/** {@hide} */
-oneway interface IOnNetworkAttributesRetrievedListener {
- /**
- * Network attributes were fetched for the specified L2 key. While the L2 key will never
- * be null, the attributes may be if no data is stored about this L2 key.
- */
- void onNetworkAttributesRetrieved(in StatusParcelable status, in String l2Key,
- in NetworkAttributesParcelable attributes);
-}
diff --git a/services/net/java/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl b/services/net/java/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl
deleted file mode 100644
index b8ccfb99fddd..000000000000
--- a/services/net/java/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl
+++ /dev/null
@@ -1,29 +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 android.net.ipmemorystore;
-
-import android.net.ipmemorystore.SameL3NetworkResponseParcelable;
-import android.net.ipmemorystore.StatusParcelable;
-
-/** {@hide} */
-oneway interface IOnSameL3NetworkResponseListener {
- /**
- * The memory store has come up with the answer to a query that was sent.
- */
- void onSameL3NetworkResponse(in StatusParcelable status,
- in SameL3NetworkResponseParcelable response);
-}
diff --git a/services/net/java/android/net/ipmemorystore/IOnStatusListener.aidl b/services/net/java/android/net/ipmemorystore/IOnStatusListener.aidl
deleted file mode 100644
index 5d0750449ec5..000000000000
--- a/services/net/java/android/net/ipmemorystore/IOnStatusListener.aidl
+++ /dev/null
@@ -1,27 +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 android.net.ipmemorystore;
-
-import android.net.ipmemorystore.StatusParcelable;
-
-/** {@hide} */
-oneway interface IOnStatusListener {
- /**
- * The operation has completed with the specified status.
- */
- void onComplete(in StatusParcelable status);
-}
diff --git a/services/net/java/android/net/ipmemorystore/NetworkAttributes.java b/services/net/java/android/net/ipmemorystore/NetworkAttributes.java
deleted file mode 100644
index 818515ac9af1..000000000000
--- a/services/net/java/android/net/ipmemorystore/NetworkAttributes.java
+++ /dev/null
@@ -1,371 +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 android.net.ipmemorystore;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-import java.util.StringJoiner;
-
-/**
- * A POD object to represent attributes of a single L2 network entry.
- * @hide
- */
-public class NetworkAttributes {
- private static final boolean DBG = true;
-
- // Weight cutoff for grouping. To group, a similarity score is computed with the following
- // algorithm : if both fields are non-null and equals() then add their assigned weight, else if
- // both are null then add a portion of their assigned weight (see NULL_MATCH_WEIGHT),
- // otherwise add nothing.
- // As a guideline, this should be something like 60~75% of the total weights in this class. The
- // design states "in essence a reader should imagine that if two important columns don't match,
- // or one important and several unimportant columns don't match then the two records are
- // considered a different group".
- private static final float TOTAL_WEIGHT_CUTOFF = 520.0f;
- // The portion of the weight that is earned when scoring group-sameness by having both columns
- // being null. This is because some networks rightfully don't have some attributes (e.g. a
- // V6-only network won't have an assigned V4 address) and both being null should count for
- // something, but attributes may also be null just because data is unavailable.
- private static final float NULL_MATCH_WEIGHT = 0.25f;
-
- // The v4 address that was assigned to this device the last time it joined this network.
- // This typically comes from DHCP but could be something else like static configuration.
- // This does not apply to IPv6.
- // TODO : add a list of v6 prefixes for the v6 case.
- @Nullable
- public final Inet4Address assignedV4Address;
- private static final float WEIGHT_ASSIGNEDV4ADDR = 300.0f;
-
- // The lease expiry timestamp of v4 address allocated from DHCP server, in milliseconds.
- @Nullable
- public final Long assignedV4AddressExpiry;
- // lease expiry doesn't imply any correlation between "the same lease expiry value" and "the
- // same L3 network".
- private static final float WEIGHT_ASSIGNEDV4ADDREXPIRY = 0.0f;
-
- // Optionally supplied by the client if it has an opinion on L3 network. For example, this
- // could be a hash of the SSID + security type on WiFi.
- @Nullable
- public final String groupHint;
- private static final float WEIGHT_GROUPHINT = 300.0f;
-
- // The list of DNS server addresses.
- @Nullable
- public final List<InetAddress> dnsAddresses;
- private static final float WEIGHT_DNSADDRESSES = 200.0f;
-
- // The mtu on this network.
- @Nullable
- public final Integer mtu;
- private static final float WEIGHT_MTU = 50.0f;
-
- // The sum of all weights in this class. Tests ensure that this stays equal to the total of
- // all weights.
- /** @hide */
- @VisibleForTesting
- public static final float TOTAL_WEIGHT = WEIGHT_ASSIGNEDV4ADDR
- + WEIGHT_ASSIGNEDV4ADDREXPIRY
- + WEIGHT_GROUPHINT
- + WEIGHT_DNSADDRESSES
- + WEIGHT_MTU;
-
- /** @hide */
- @VisibleForTesting
- public NetworkAttributes(
- @Nullable final Inet4Address assignedV4Address,
- @Nullable final Long assignedV4AddressExpiry,
- @Nullable final String groupHint,
- @Nullable final List<InetAddress> dnsAddresses,
- @Nullable final Integer mtu) {
- if (mtu != null && mtu < 0) throw new IllegalArgumentException("MTU can't be negative");
- if (assignedV4AddressExpiry != null && assignedV4AddressExpiry <= 0) {
- throw new IllegalArgumentException("lease expiry can't be negative or zero");
- }
- this.assignedV4Address = assignedV4Address;
- this.assignedV4AddressExpiry = assignedV4AddressExpiry;
- this.groupHint = groupHint;
- this.dnsAddresses = null == dnsAddresses ? null :
- Collections.unmodifiableList(new ArrayList<>(dnsAddresses));
- this.mtu = mtu;
- }
-
- @VisibleForTesting
- public NetworkAttributes(@NonNull final NetworkAttributesParcelable parcelable) {
- // The call to the other constructor must be the first statement of this constructor,
- // so everything has to be inline
- this((Inet4Address) getByAddressOrNull(parcelable.assignedV4Address),
- parcelable.assignedV4AddressExpiry > 0
- ? parcelable.assignedV4AddressExpiry : null,
- parcelable.groupHint,
- blobArrayToInetAddressList(parcelable.dnsAddresses),
- parcelable.mtu >= 0 ? parcelable.mtu : null);
- }
-
- @Nullable
- private static InetAddress getByAddressOrNull(@Nullable final byte[] address) {
- if (null == address) return null;
- try {
- return InetAddress.getByAddress(address);
- } catch (UnknownHostException e) {
- return null;
- }
- }
-
- @Nullable
- private static List<InetAddress> blobArrayToInetAddressList(@Nullable final Blob[] blobs) {
- if (null == blobs) return null;
- final ArrayList<InetAddress> list = new ArrayList<>(blobs.length);
- for (final Blob b : blobs) {
- final InetAddress addr = getByAddressOrNull(b.data);
- if (null != addr) list.add(addr);
- }
- return list;
- }
-
- @Nullable
- private static Blob[] inetAddressListToBlobArray(@Nullable final List<InetAddress> addresses) {
- if (null == addresses) return null;
- final ArrayList<Blob> blobs = new ArrayList<>();
- for (int i = 0; i < addresses.size(); ++i) {
- final InetAddress addr = addresses.get(i);
- if (null == addr) continue;
- final Blob b = new Blob();
- b.data = addr.getAddress();
- blobs.add(b);
- }
- return blobs.toArray(new Blob[0]);
- }
-
- /** Converts this NetworkAttributes to a parcelable object */
- @NonNull
- public NetworkAttributesParcelable toParcelable() {
- final NetworkAttributesParcelable parcelable = new NetworkAttributesParcelable();
- parcelable.assignedV4Address =
- (null == assignedV4Address) ? null : assignedV4Address.getAddress();
- parcelable.assignedV4AddressExpiry =
- (null == assignedV4AddressExpiry) ? 0 : assignedV4AddressExpiry;
- parcelable.groupHint = groupHint;
- parcelable.dnsAddresses = inetAddressListToBlobArray(dnsAddresses);
- parcelable.mtu = (null == mtu) ? -1 : mtu;
- return parcelable;
- }
-
- private float samenessContribution(final float weight,
- @Nullable final Object o1, @Nullable final Object o2) {
- if (null == o1) {
- return (null == o2) ? weight * NULL_MATCH_WEIGHT : 0f;
- }
- return Objects.equals(o1, o2) ? weight : 0f;
- }
-
- /** @hide */
- public float getNetworkGroupSamenessConfidence(@NonNull final NetworkAttributes o) {
- final float samenessScore =
- samenessContribution(WEIGHT_ASSIGNEDV4ADDR, assignedV4Address, o.assignedV4Address)
- + samenessContribution(WEIGHT_ASSIGNEDV4ADDREXPIRY, assignedV4AddressExpiry,
- o.assignedV4AddressExpiry)
- + samenessContribution(WEIGHT_GROUPHINT, groupHint, o.groupHint)
- + samenessContribution(WEIGHT_DNSADDRESSES, dnsAddresses, o.dnsAddresses)
- + samenessContribution(WEIGHT_MTU, mtu, o.mtu);
- // The minimum is 0, the max is TOTAL_WEIGHT and should be represented by 1.0, and
- // TOTAL_WEIGHT_CUTOFF should represent 0.5, but there is no requirement that
- // TOTAL_WEIGHT_CUTOFF would be half of TOTAL_WEIGHT (indeed, it should not be).
- // So scale scores under the cutoff between 0 and 0.5, and the scores over the cutoff
- // between 0.5 and 1.0.
- if (samenessScore < TOTAL_WEIGHT_CUTOFF) {
- return samenessScore / (TOTAL_WEIGHT_CUTOFF * 2);
- } else {
- return (samenessScore - TOTAL_WEIGHT_CUTOFF) / (TOTAL_WEIGHT - TOTAL_WEIGHT_CUTOFF) / 2
- + 0.5f;
- }
- }
-
- /** @hide */
- public static class Builder {
- @Nullable
- private Inet4Address mAssignedAddress;
- @Nullable
- private Long mAssignedAddressExpiry;
- @Nullable
- private String mGroupHint;
- @Nullable
- private List<InetAddress> mDnsAddresses;
- @Nullable
- private Integer mMtu;
-
- /**
- * Set the assigned address.
- * @param assignedV4Address The assigned address.
- * @return This builder.
- */
- public Builder setAssignedV4Address(@Nullable final Inet4Address assignedV4Address) {
- mAssignedAddress = assignedV4Address;
- return this;
- }
-
- /**
- * Set the lease expiry timestamp of assigned v4 address. Long.MAX_VALUE is used
- * to represent "infinite lease".
- *
- * @param assignedV4AddressExpiry The lease expiry timestamp of assigned v4 address.
- * @return This builder.
- */
- public Builder setAssignedV4AddressExpiry(
- @Nullable final Long assignedV4AddressExpiry) {
- if (null != assignedV4AddressExpiry && assignedV4AddressExpiry <= 0) {
- throw new IllegalArgumentException("lease expiry can't be negative or zero");
- }
- mAssignedAddressExpiry = assignedV4AddressExpiry;
- return this;
- }
-
- /**
- * Set the group hint.
- * @param groupHint The group hint.
- * @return This builder.
- */
- public Builder setGroupHint(@Nullable final String groupHint) {
- mGroupHint = groupHint;
- return this;
- }
-
- /**
- * Set the DNS addresses.
- * @param dnsAddresses The DNS addresses.
- * @return This builder.
- */
- public Builder setDnsAddresses(@Nullable final List<InetAddress> dnsAddresses) {
- if (DBG && null != dnsAddresses) {
- // Parceling code crashes if one of the addresses is null, therefore validate
- // them when running in debug.
- for (final InetAddress address : dnsAddresses) {
- if (null == address) throw new IllegalArgumentException("Null DNS address");
- }
- }
- this.mDnsAddresses = dnsAddresses;
- return this;
- }
-
- /**
- * Set the MTU.
- * @param mtu The MTU.
- * @return This builder.
- */
- public Builder setMtu(@Nullable final Integer mtu) {
- if (null != mtu && mtu < 0) throw new IllegalArgumentException("MTU can't be negative");
- mMtu = mtu;
- return this;
- }
-
- /**
- * Return the built NetworkAttributes object.
- * @return The built NetworkAttributes object.
- */
- public NetworkAttributes build() {
- return new NetworkAttributes(mAssignedAddress, mAssignedAddressExpiry,
- mGroupHint, mDnsAddresses, mMtu);
- }
- }
-
- /** @hide */
- public boolean isEmpty() {
- return (null == assignedV4Address) && (null == assignedV4AddressExpiry)
- && (null == groupHint) && (null == dnsAddresses) && (null == mtu);
- }
-
- @Override
- public boolean equals(@Nullable final Object o) {
- if (!(o instanceof NetworkAttributes)) return false;
- final NetworkAttributes other = (NetworkAttributes) o;
- return Objects.equals(assignedV4Address, other.assignedV4Address)
- && Objects.equals(assignedV4AddressExpiry, other.assignedV4AddressExpiry)
- && Objects.equals(groupHint, other.groupHint)
- && Objects.equals(dnsAddresses, other.dnsAddresses)
- && Objects.equals(mtu, other.mtu);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(assignedV4Address, assignedV4AddressExpiry,
- groupHint, dnsAddresses, mtu);
- }
-
- /** Pretty print */
- @Override
- public String toString() {
- final StringJoiner resultJoiner = new StringJoiner(" ", "{", "}");
- final ArrayList<String> nullFields = new ArrayList<>();
-
- if (null != assignedV4Address) {
- resultJoiner.add("assignedV4Addr :");
- resultJoiner.add(assignedV4Address.toString());
- } else {
- nullFields.add("assignedV4Addr");
- }
-
- if (null != assignedV4AddressExpiry) {
- resultJoiner.add("assignedV4AddressExpiry :");
- resultJoiner.add(assignedV4AddressExpiry.toString());
- } else {
- nullFields.add("assignedV4AddressExpiry");
- }
-
- if (null != groupHint) {
- resultJoiner.add("groupHint :");
- resultJoiner.add(groupHint);
- } else {
- nullFields.add("groupHint");
- }
-
- if (null != dnsAddresses) {
- resultJoiner.add("dnsAddr : [");
- for (final InetAddress addr : dnsAddresses) {
- resultJoiner.add(addr.getHostAddress());
- }
- resultJoiner.add("]");
- } else {
- nullFields.add("dnsAddr");
- }
-
- if (null != mtu) {
- resultJoiner.add("mtu :");
- resultJoiner.add(mtu.toString());
- } else {
- nullFields.add("mtu");
- }
-
- if (!nullFields.isEmpty()) {
- resultJoiner.add("; Null fields : [");
- for (final String field : nullFields) {
- resultJoiner.add(field);
- }
- resultJoiner.add("]");
- }
-
- return resultJoiner.toString();
- }
-}
diff --git a/services/net/java/android/net/ipmemorystore/NetworkAttributesParcelable.aidl b/services/net/java/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
deleted file mode 100644
index 997eb2b5128b..000000000000
--- a/services/net/java/android/net/ipmemorystore/NetworkAttributesParcelable.aidl
+++ /dev/null
@@ -1,37 +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 android.net.ipmemorystore;
-
-// Blob[] is used to represent an array of byte[], as structured AIDL does not support arrays
-// of arrays.
-import android.net.ipmemorystore.Blob;
-
-/**
- * An object to represent attributes of a single L2 network entry.
- * See NetworkAttributes.java for a description of each field. The types used in this class
- * are structured parcelable types instead of the richer types of the NetworkAttributes object,
- * but they have the same purpose. The NetworkAttributes.java file also contains the code
- * to convert the richer types to the parcelable types and back.
- * @hide
- */
-parcelable NetworkAttributesParcelable {
- byte[] assignedV4Address;
- long assignedV4AddressExpiry;
- String groupHint;
- Blob[] dnsAddresses;
- int mtu;
-}
diff --git a/services/net/java/android/net/ipmemorystore/OnBlobRetrievedListener.java b/services/net/java/android/net/ipmemorystore/OnBlobRetrievedListener.java
deleted file mode 100644
index a17483a84e78..000000000000
--- a/services/net/java/android/net/ipmemorystore/OnBlobRetrievedListener.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ipmemorystore;
-
-import android.annotation.NonNull;
-
-/**
- * A listener for the IpMemoryStore to return a blob.
- * @hide
- */
-public interface OnBlobRetrievedListener {
- /**
- * The memory store has come up with the answer to a query that was sent.
- */
- void onBlobRetrieved(Status status, String l2Key, String name, Blob blob);
-
- /** Converts this OnBlobRetrievedListener to a parcelable object */
- @NonNull
- static IOnBlobRetrievedListener toAIDL(@NonNull final OnBlobRetrievedListener listener) {
- return new IOnBlobRetrievedListener.Stub() {
- @Override
- public void onBlobRetrieved(final StatusParcelable statusParcelable, final String l2Key,
- final String name, final Blob blob) {
- // NonNull, but still don't crash the system server if null
- if (null != listener) {
- listener.onBlobRetrieved(new Status(statusParcelable), l2Key, name, blob);
- }
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
- };
- }
-}
diff --git a/services/net/java/android/net/ipmemorystore/OnL2KeyResponseListener.java b/services/net/java/android/net/ipmemorystore/OnL2KeyResponseListener.java
deleted file mode 100644
index e608aecbf498..000000000000
--- a/services/net/java/android/net/ipmemorystore/OnL2KeyResponseListener.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ipmemorystore;
-
-import android.annotation.NonNull;
-
-/**
- * A listener for the IpMemoryStore to return a L2 key.
- * @hide
- */
-public interface OnL2KeyResponseListener {
- /**
- * The operation has completed with the specified status.
- */
- void onL2KeyResponse(Status status, String l2Key);
-
- /** Converts this OnL2KeyResponseListener to a parcelable object */
- @NonNull
- static IOnL2KeyResponseListener toAIDL(@NonNull final OnL2KeyResponseListener listener) {
- return new IOnL2KeyResponseListener.Stub() {
- @Override
- public void onL2KeyResponse(final StatusParcelable statusParcelable,
- final String l2Key) {
- // NonNull, but still don't crash the system server if null
- if (null != listener) {
- listener.onL2KeyResponse(new Status(statusParcelable), l2Key);
- }
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
- };
- }
-}
diff --git a/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java b/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java
deleted file mode 100644
index 395ad98f38e0..000000000000
--- a/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ipmemorystore;
-
-import android.annotation.NonNull;
-
-/**
- * A listener for the IpMemoryStore to return network attributes.
- * @hide
- */
-public interface OnNetworkAttributesRetrievedListener {
- /**
- * The memory store has come up with the answer to a query that was sent.
- */
- void onNetworkAttributesRetrieved(Status status, String l2Key, NetworkAttributes attributes);
-
- /** Converts this OnNetworkAttributesRetrievedListener to a parcelable object */
- @NonNull
- static IOnNetworkAttributesRetrievedListener toAIDL(
- @NonNull final OnNetworkAttributesRetrievedListener listener) {
- return new IOnNetworkAttributesRetrievedListener.Stub() {
- @Override
- public void onNetworkAttributesRetrieved(final StatusParcelable statusParcelable,
- final String l2Key,
- final NetworkAttributesParcelable networkAttributesParcelable) {
- // NonNull, but still don't crash the system server if null
- if (null != listener) {
- listener.onNetworkAttributesRetrieved(
- new Status(statusParcelable), l2Key, null == networkAttributesParcelable
- ? null : new NetworkAttributes(networkAttributesParcelable));
- }
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
- };
- }
-}
diff --git a/services/net/java/android/net/ipmemorystore/OnSameL3NetworkResponseListener.java b/services/net/java/android/net/ipmemorystore/OnSameL3NetworkResponseListener.java
deleted file mode 100644
index 67f8da81c3f2..000000000000
--- a/services/net/java/android/net/ipmemorystore/OnSameL3NetworkResponseListener.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ipmemorystore;
-
-import android.annotation.NonNull;
-
-/**
- * A listener for the IpMemoryStore to return a response about network sameness.
- * @hide
- */
-public interface OnSameL3NetworkResponseListener {
- /**
- * The memory store has come up with the answer to a query that was sent.
- */
- void onSameL3NetworkResponse(Status status, SameL3NetworkResponse response);
-
- /** Converts this OnSameL3NetworkResponseListener to a parcelable object */
- @NonNull
- static IOnSameL3NetworkResponseListener toAIDL(
- @NonNull final OnSameL3NetworkResponseListener listener) {
- return new IOnSameL3NetworkResponseListener.Stub() {
- @Override
- public void onSameL3NetworkResponse(final StatusParcelable statusParcelable,
- final SameL3NetworkResponseParcelable sameL3NetworkResponseParcelable) {
- // NonNull, but still don't crash the system server if null
- if (null != listener) {
- listener.onSameL3NetworkResponse(
- new Status(statusParcelable),
- new SameL3NetworkResponse(sameL3NetworkResponseParcelable));
- }
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
- };
- }
-}
diff --git a/services/net/java/android/net/ipmemorystore/OnStatusListener.java b/services/net/java/android/net/ipmemorystore/OnStatusListener.java
deleted file mode 100644
index 4262efde8843..000000000000
--- a/services/net/java/android/net/ipmemorystore/OnStatusListener.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ipmemorystore;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-/**
- * A listener for the IpMemoryStore to return a status to a client.
- * @hide
- */
-public interface OnStatusListener {
- /**
- * The operation has completed with the specified status.
- */
- void onComplete(Status status);
-
- /** Converts this OnStatusListener to a parcelable object */
- @NonNull
- static IOnStatusListener toAIDL(@Nullable final OnStatusListener listener) {
- return new IOnStatusListener.Stub() {
- @Override
- public void onComplete(final StatusParcelable statusParcelable) {
- if (null != listener) {
- listener.onComplete(new Status(statusParcelable));
- }
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
- };
- }
-}
diff --git a/services/net/java/android/net/ipmemorystore/SameL3NetworkResponse.java b/services/net/java/android/net/ipmemorystore/SameL3NetworkResponse.java
deleted file mode 100644
index 291aca8fc611..000000000000
--- a/services/net/java/android/net/ipmemorystore/SameL3NetworkResponse.java
+++ /dev/null
@@ -1,147 +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 android.net.ipmemorystore;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-
-/**
- * An object representing the answer to a query whether two given L2 networks represent the
- * same L3 network. Parcels as a SameL3NetworkResponseParceled object.
- * @hide
- */
-public class SameL3NetworkResponse {
- @IntDef(prefix = "NETWORK_",
- value = {NETWORK_SAME, NETWORK_DIFFERENT, NETWORK_NEVER_CONNECTED})
- @Retention(RetentionPolicy.SOURCE)
- public @interface NetworkSameness {}
-
- /**
- * Both L2 networks represent the same L3 network.
- */
- public static final int NETWORK_SAME = 1;
-
- /**
- * The two L2 networks represent a different L3 network.
- */
- public static final int NETWORK_DIFFERENT = 2;
-
- /**
- * The device has never connected to at least one of these two L2 networks, or data
- * has been wiped. Therefore the device has never seen the L3 network behind at least
- * one of these two L2 networks, and can't evaluate whether it's the same as the other.
- */
- public static final int NETWORK_NEVER_CONNECTED = 3;
-
- /**
- * The first L2 key specified in the query.
- */
- @NonNull
- public final String l2Key1;
-
- /**
- * The second L2 key specified in the query.
- */
- @NonNull
- public final String l2Key2;
-
- /**
- * A confidence value indicating whether the two L2 networks represent the same L3 network.
- *
- * If both L2 networks were known, this value will be between 0.0 and 1.0, with 0.0
- * representing complete confidence that the given L2 networks represent a different
- * L3 network, and 1.0 representing complete confidence that the given L2 networks
- * represent the same L3 network.
- * If at least one of the L2 networks was not known, this value will be outside of the
- * 0.0~1.0 range.
- *
- * Most apps should not be interested in this, and are encouraged to use the collapsing
- * {@link #getNetworkSameness()} function below.
- */
- public final float confidence;
-
- /**
- * @return whether the two L2 networks represent the same L3 network. Either
- * {@code NETWORK_SAME}, {@code NETWORK_DIFFERENT} or {@code NETWORK_NEVER_CONNECTED}.
- */
- @NetworkSameness
- public final int getNetworkSameness() {
- if (confidence > 1.0 || confidence < 0.0) return NETWORK_NEVER_CONNECTED;
- return confidence > 0.5 ? NETWORK_SAME : NETWORK_DIFFERENT;
- }
-
- /** @hide */
- public SameL3NetworkResponse(@NonNull final String l2Key1, @NonNull final String l2Key2,
- final float confidence) {
- this.l2Key1 = l2Key1;
- this.l2Key2 = l2Key2;
- this.confidence = confidence;
- }
-
- /** Builds a SameL3NetworkResponse from a parcelable object */
- @VisibleForTesting
- public SameL3NetworkResponse(@NonNull final SameL3NetworkResponseParcelable parceled) {
- this(parceled.l2Key1, parceled.l2Key2, parceled.confidence);
- }
-
- /** Converts this SameL3NetworkResponse to a parcelable object */
- @NonNull
- public SameL3NetworkResponseParcelable toParcelable() {
- final SameL3NetworkResponseParcelable parcelable = new SameL3NetworkResponseParcelable();
- parcelable.l2Key1 = l2Key1;
- parcelable.l2Key2 = l2Key2;
- parcelable.confidence = confidence;
- return parcelable;
- }
-
- // Note key1 and key2 have to match each other for this to return true. If
- // key1 matches o.key2 and the other way around this returns false.
- @Override
- public boolean equals(@Nullable final Object o) {
- if (!(o instanceof SameL3NetworkResponse)) return false;
- final SameL3NetworkResponse other = (SameL3NetworkResponse) o;
- return l2Key1.equals(other.l2Key1) && l2Key2.equals(other.l2Key2)
- && confidence == other.confidence;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(l2Key1, l2Key2, confidence);
- }
-
- @Override
- /** Pretty print */
- public String toString() {
- switch (getNetworkSameness()) {
- case NETWORK_SAME:
- return "\"" + l2Key1 + "\" same L3 network as \"" + l2Key2 + "\"";
- case NETWORK_DIFFERENT:
- return "\"" + l2Key1 + "\" different L3 network from \"" + l2Key2 + "\"";
- case NETWORK_NEVER_CONNECTED:
- return "\"" + l2Key1 + "\" can't be tested against \"" + l2Key2 + "\"";
- default:
- return "Buggy sameness value ? \"" + l2Key1 + "\", \"" + l2Key2 + "\"";
- }
- }
-}
diff --git a/services/net/java/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl b/services/net/java/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl
deleted file mode 100644
index 71966998a68a..000000000000
--- a/services/net/java/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl
+++ /dev/null
@@ -1,24 +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 android.net.ipmemorystore;
-
-/** {@hide} */
-parcelable SameL3NetworkResponseParcelable {
- String l2Key1;
- String l2Key2;
- float confidence;
-}
diff --git a/services/net/java/android/net/ipmemorystore/Status.java b/services/net/java/android/net/ipmemorystore/Status.java
deleted file mode 100644
index 13242c03ce01..000000000000
--- a/services/net/java/android/net/ipmemorystore/Status.java
+++ /dev/null
@@ -1,74 +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 android.net.ipmemorystore;
-
-import android.annotation.NonNull;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * A parcelable status representing the result of an operation.
- * Parcels as StatusParceled.
- * @hide
- */
-public class Status {
- public static final int SUCCESS = 0;
-
- public static final int ERROR_GENERIC = -1;
- public static final int ERROR_ILLEGAL_ARGUMENT = -2;
- public static final int ERROR_DATABASE_CANNOT_BE_OPENED = -3;
- public static final int ERROR_STORAGE = -4;
- public static final int ERROR_UNKNOWN = -5;
-
- public final int resultCode;
-
- public Status(final int resultCode) {
- this.resultCode = resultCode;
- }
-
- @VisibleForTesting
- public Status(@NonNull final StatusParcelable parcelable) {
- this(parcelable.resultCode);
- }
-
- /** Converts this Status to a parcelable object */
- @NonNull
- public StatusParcelable toParcelable() {
- final StatusParcelable parcelable = new StatusParcelable();
- parcelable.resultCode = resultCode;
- return parcelable;
- }
-
- public boolean isSuccess() {
- return SUCCESS == resultCode;
- }
-
- /** Pretty print */
- @Override
- public String toString() {
- switch (resultCode) {
- case SUCCESS: return "SUCCESS";
- case ERROR_GENERIC: return "GENERIC ERROR";
- case ERROR_ILLEGAL_ARGUMENT: return "ILLEGAL ARGUMENT";
- case ERROR_DATABASE_CANNOT_BE_OPENED: return "DATABASE CANNOT BE OPENED";
- // "DB storage error" is not very helpful but SQLite does not provide specific error
- // codes upon store failure. Thus this indicates SQLite returned some error upon store
- case ERROR_STORAGE: return "DATABASE STORAGE ERROR";
- default: return "Unknown value ?!";
- }
- }
-}
diff --git a/services/net/java/android/net/ipmemorystore/StatusParcelable.aidl b/services/net/java/android/net/ipmemorystore/StatusParcelable.aidl
deleted file mode 100644
index fb36ef4a56ff..000000000000
--- a/services/net/java/android/net/ipmemorystore/StatusParcelable.aidl
+++ /dev/null
@@ -1,22 +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 android.net.ipmemorystore;
-
-/** {@hide} */
-parcelable StatusParcelable {
- int resultCode;
-}
diff --git a/services/net/java/android/net/netlink/InetDiagMessage.java b/services/net/java/android/net/netlink/InetDiagMessage.java
index af9e601da9ec..31a2556f2041 100644
--- a/services/net/java/android/net/netlink/InetDiagMessage.java
+++ b/services/net/java/android/net/netlink/InetDiagMessage.java
@@ -16,26 +16,23 @@
package android.net.netlink;
-import static android.os.Process.INVALID_UID;
import static android.net.netlink.NetlinkConstants.SOCK_DIAG_BY_FAMILY;
import static android.net.netlink.NetlinkSocket.DEFAULT_RECV_BUFSIZE;
import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP;
import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
+import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.AF_INET6;
import static android.system.OsConstants.IPPROTO_UDP;
import static android.system.OsConstants.NETLINK_INET_DIAG;
-import android.os.Build;
-import android.os.Process;
+import android.net.util.SocketUtils;
import android.system.ErrnoException;
import android.util.Log;
import java.io.FileDescriptor;
+import java.io.IOException;
import java.io.InterruptedIOException;
-import java.net.DatagramSocket;
-import java.net.DatagramSocket;
-import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetSocketAddress;
@@ -163,17 +160,25 @@ public class InetDiagMessage extends NetlinkMessage {
*/
public static int getConnectionOwnerUid(int protocol, InetSocketAddress local,
InetSocketAddress remote) {
+ int uid = INVALID_UID;
+ FileDescriptor fd = null;
try {
- final FileDescriptor fd = NetlinkSocket.forProto(NETLINK_INET_DIAG);
+ fd = NetlinkSocket.forProto(NETLINK_INET_DIAG);
NetlinkSocket.connectToKernel(fd);
-
- return lookupUid(protocol, local, remote, fd);
-
+ uid = lookupUid(protocol, local, remote, fd);
} catch (ErrnoException | SocketException | IllegalArgumentException
| InterruptedIOException e) {
Log.e(TAG, e.toString());
+ } finally {
+ if (fd != null) {
+ try {
+ SocketUtils.closeSocket(fd);
+ } catch (IOException e) {
+ Log.e(TAG, e.toString());
+ }
+ }
}
- return INVALID_UID;
+ return uid;
}
@Override
diff --git a/services/print/java/com/android/server/print/TEST_MAPPING b/services/print/java/com/android/server/print/TEST_MAPPING
new file mode 100644
index 000000000000..4fa882265e53
--- /dev/null
+++ b/services/print/java/com/android/server/print/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsPrintTestCases",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index 6e8b86add2c4..7b7b8e6c628a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -34,7 +34,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.AlarmManagerService.ACTIVE_INDEX;
import static com.android.server.AlarmManagerService.AlarmHandler.APP_STANDBY_BUCKET_CHANGED;
-import static com.android.server.AlarmManagerService.AlarmHandler.APP_STANDBY_PAROLE_CHANGED;
+import static com.android.server.AlarmManagerService.AlarmHandler.CHARGING_STATUS_CHANGED;
import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_LONG_TIME;
import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_SHORT_TIME;
import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION;
@@ -53,6 +53,7 @@ import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.atLeastOnce;
@@ -68,6 +69,7 @@ import android.app.usage.UsageStatsManagerInternal;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.os.BatteryManager;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -107,6 +109,7 @@ public class AlarmManagerServiceTest {
private long mAppStandbyWindow;
private AlarmManagerService mService;
private UsageStatsManagerInternal.AppIdleStateChangeListener mAppStandbyListener;
+ private AlarmManagerService.ChargingReceiver mChargingReceiver;
@Mock
private ContentResolver mMockResolver;
@Mock
@@ -290,6 +293,13 @@ public class AlarmManagerServiceTest {
ArgumentCaptor.forClass(UsageStatsManagerInternal.AppIdleStateChangeListener.class);
verify(mUsageStatsManagerInternal).addAppIdleStateChangeListener(captor.capture());
mAppStandbyListener = captor.getValue();
+
+ ArgumentCaptor<AlarmManagerService.ChargingReceiver> chargingReceiverCaptor =
+ ArgumentCaptor.forClass(AlarmManagerService.ChargingReceiver.class);
+ verify(mMockContext).registerReceiver(chargingReceiverCaptor.capture(),
+ argThat((filter) -> filter.hasAction(BatteryManager.ACTION_CHARGING)
+ && filter.hasAction(BatteryManager.ACTION_DISCHARGING)));
+ mChargingReceiver = chargingReceiverCaptor.getValue();
}
private void setTestAlarm(int type, long triggerTime, PendingIntent operation) {
@@ -724,17 +734,19 @@ public class AlarmManagerServiceTest {
}
private void assertAndHandleParoleChanged(boolean parole) {
- mAppStandbyListener.onParoleStateChanged(parole);
+ mChargingReceiver.onReceive(mMockContext,
+ new Intent(parole ? BatteryManager.ACTION_CHARGING
+ : BatteryManager.ACTION_DISCHARGING));
final ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
verify(mService.mHandler, atLeastOnce()).sendMessage(messageCaptor.capture());
final Message lastMessage = messageCaptor.getValue();
assertEquals("Unexpected message send to handler", lastMessage.what,
- APP_STANDBY_PAROLE_CHANGED);
+ CHARGING_STATUS_CHANGED);
mService.mHandler.handleMessage(lastMessage);
}
@Test
- public void testParole() throws Exception {
+ public void testCharging() throws Exception {
setQuotasEnabled(true);
final int workingQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_WORKING_SET);
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
diff --git a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
index d0158e0c819f..80d11290fdd4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
@@ -64,6 +64,9 @@ import android.test.mock.MockContentResolver;
import android.util.ArraySet;
import android.util.Pair;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
import com.android.server.AppStateTracker.Listener;
@@ -85,14 +88,10 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
/**
* Tests for {@link AppStateTracker}
*
- * Run with:
- atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
+ * Run with: atest com.android.server.AppStateTrackerTest
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -685,10 +684,12 @@ public class AppStateTrackerTest {
List<OpEntry> entries = new ArrayList<>();
entries.add(new OpEntry(
AppOpsManager.OP_ACCESS_NOTIFICATIONS,
- AppOpsManager.MODE_IGNORED));
+ AppOpsManager.MODE_IGNORED,
+ new Pair[0]));
entries.add(new OpEntry(
AppStateTracker.TARGET_OP,
- AppOpsManager.MODE_IGNORED));
+ AppOpsManager.MODE_IGNORED,
+ new Pair[0]));
ops.add(new PackageOps(PACKAGE_1, UID_1, entries));
@@ -696,7 +697,8 @@ public class AppStateTrackerTest {
entries = new ArrayList<>();
entries.add(new OpEntry(
AppStateTracker.TARGET_OP,
- AppOpsManager.MODE_IGNORED));
+ AppOpsManager.MODE_IGNORED,
+ new Pair[0]));
ops.add(new PackageOps(PACKAGE_2, UID_2, entries));
@@ -704,7 +706,8 @@ public class AppStateTrackerTest {
entries = new ArrayList<>();
entries.add(new OpEntry(
AppStateTracker.TARGET_OP,
- AppOpsManager.MODE_ALLOWED));
+ AppOpsManager.MODE_ALLOWED,
+ new Pair[0]));
ops.add(new PackageOps(PACKAGE_1, UID_10_1, entries));
@@ -712,10 +715,12 @@ public class AppStateTrackerTest {
entries = new ArrayList<>();
entries.add(new OpEntry(
AppStateTracker.TARGET_OP,
- AppOpsManager.MODE_IGNORED));
+ AppOpsManager.MODE_IGNORED,
+ new Pair[0]));
entries.add(new OpEntry(
AppOpsManager.OP_ACCESS_NOTIFICATIONS,
- AppOpsManager.MODE_IGNORED));
+ AppOpsManager.MODE_IGNORED,
+ new Pair[0]));
ops.add(new PackageOps(PACKAGE_3, UID_10_3, entries));
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 108b017fc76c..9c9730501a78 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -17,6 +17,7 @@ package com.android.server;
import static androidx.test.InstrumentationRegistry.getContext;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
@@ -31,6 +32,7 @@ import static com.android.server.DeviceIdleController.LIGHT_STATE_INACTIVE;
import static com.android.server.DeviceIdleController.LIGHT_STATE_OVERRIDE;
import static com.android.server.DeviceIdleController.LIGHT_STATE_PRE_IDLE;
import static com.android.server.DeviceIdleController.LIGHT_STATE_WAITING_FOR_NETWORK;
+import static com.android.server.DeviceIdleController.MSG_REPORT_STATIONARY_STATUS;
import static com.android.server.DeviceIdleController.STATE_ACTIVE;
import static com.android.server.DeviceIdleController.STATE_IDLE;
import static com.android.server.DeviceIdleController.STATE_IDLE_MAINTENANCE;
@@ -51,6 +53,7 @@ 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.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.longThat;
import static org.mockito.Mockito.atLeastOnce;
@@ -72,6 +75,7 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Handler;
import android.os.Looper;
+import android.os.Message;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
@@ -87,11 +91,13 @@ 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.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoSession;
+import org.mockito.invocation.InvocationOnMock;
import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
/**
* Tests for {@link com.android.server.DeviceIdleController}.
@@ -99,6 +105,7 @@ import org.mockito.quality.Strictness;
@RunWith(AndroidJUnit4.class)
public class DeviceIdleControllerTest {
private DeviceIdleController mDeviceIdleController;
+ private DeviceIdleController.MyHandler mHandler;
private AnyMotionDetectorForTest mAnyMotionDetector;
private AppStateTrackerForTest mAppStateTracker;
private DeviceIdleController.Constants mConstants;
@@ -108,12 +115,10 @@ public class DeviceIdleControllerTest {
@Mock
private AlarmManager mAlarmManager;
@Mock
- private ConnectivityService mConnectivityService;
+ private ConnectivityManager mConnectivityManager;
@Mock
private ContentResolver mContentResolver;
@Mock
- private DeviceIdleController.MyHandler mHandler;
- @Mock
private IActivityManager mIActivityManager;
@Mock
private LocationManager mLocationManager;
@@ -127,7 +132,7 @@ public class DeviceIdleControllerTest {
private SensorManager mSensorManager;
class InjectorForTest extends DeviceIdleController.Injector {
- ConnectivityService connectivityService;
+ ConnectivityManager connectivityManager;
LocationManager locationManager;
ConstraintController constraintController;
// Freeze time for testing.
@@ -155,8 +160,8 @@ public class DeviceIdleControllerTest {
}
@Override
- ConnectivityService getConnectivityService() {
- return connectivityService;
+ ConnectivityManager getConnectivityManager() {
+ return connectivityManager;
}
@Override
@@ -171,6 +176,23 @@ public class DeviceIdleControllerTest {
@Override
DeviceIdleController.MyHandler getHandler(DeviceIdleController controller) {
+ if (mHandler == null) {
+ mHandler = controller.new MyHandler(getContext().getMainLooper());
+ spyOn(mHandler);
+ doNothing().when(mHandler).handleMessage(argThat((message) ->
+ message.what != MSG_REPORT_STATIONARY_STATUS));
+ doAnswer(new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ Message msg = invocation.getArgument(0);
+ mHandler.handleMessage(msg);
+ return true;
+ }
+ }).when(mHandler).sendMessageDelayed(
+ argThat((message) -> message.what == MSG_REPORT_STATIONARY_STATUS),
+ anyLong());
+ }
+
return mHandler;
}
@@ -236,6 +258,19 @@ public class DeviceIdleControllerTest {
}
}
+ private class StationaryListenerForTest implements DeviceIdleInternal.StationaryListener {
+ boolean motionExpected = false;
+ boolean isStationary = false;
+
+ @Override
+ public void onDeviceStationaryChanged(boolean isStationary) {
+ if (isStationary == motionExpected) {
+ fail("Unexpected device stationary status: " + isStationary);
+ }
+ this.isStationary = isStationary;
+ }
+ }
+
@Before
public void setUp() {
mMockingSession = mockitoSession()
@@ -265,8 +300,6 @@ public class DeviceIdleControllerTest {
doReturn(true).when(mSensorManager).registerListener(any(), any(), anyInt());
mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper());
mAnyMotionDetector = new AnyMotionDetectorForTest();
- mHandler = mock(DeviceIdleController.MyHandler.class, Answers.RETURNS_DEEP_STUBS);
- doNothing().when(mHandler).handleMessage(any());
mInjector = new InjectorForTest(getContext());
doNothing().when(mContentResolver).registerContentObserver(any(), anyBoolean(), any());
@@ -347,19 +380,19 @@ public class DeviceIdleControllerTest {
public void testUpdateConnectivityState() {
// No connectivity service
final boolean isConnected = mDeviceIdleController.isNetworkConnected();
- mInjector.connectivityService = null;
+ mInjector.connectivityManager = null;
mDeviceIdleController.updateConnectivityState(null);
assertEquals(isConnected, mDeviceIdleController.isNetworkConnected());
// No active network info
- mInjector.connectivityService = mConnectivityService;
- doReturn(null).when(mConnectivityService).getActiveNetworkInfo();
+ mInjector.connectivityManager = mConnectivityManager;
+ doReturn(null).when(mConnectivityManager).getActiveNetworkInfo();
mDeviceIdleController.updateConnectivityState(null);
assertFalse(mDeviceIdleController.isNetworkConnected());
// Active network info says connected.
final NetworkInfo ani = mock(NetworkInfo.class);
- doReturn(ani).when(mConnectivityService).getActiveNetworkInfo();
+ doReturn(ani).when(mConnectivityManager).getActiveNetworkInfo();
doReturn(true).when(ani).isConnected();
mDeviceIdleController.updateConnectivityState(null);
assertTrue(mDeviceIdleController.isNetworkConnected());
@@ -1724,6 +1757,86 @@ public class DeviceIdleControllerTest {
1.0f, curfactor, delta);
}
+ @Test
+ public void testStationaryDetection_QuickDozeOff() {
+ setQuickDozeEnabled(false);
+ enterDeepState(STATE_IDLE);
+ // Regular progression through states, so time should have increased appropriately.
+ mInjector.nowElapsed += mConstants.IDLE_AFTER_INACTIVE_TIMEOUT + mConstants.SENSING_TIMEOUT
+ + mConstants.LOCATING_TIMEOUT;
+
+ StationaryListenerForTest stationaryListener = new StationaryListenerForTest();
+
+ mDeviceIdleController.registerStationaryListener(stationaryListener);
+
+ // Go to IDLE_MAINTENANCE
+ mDeviceIdleController.stepIdleStateLocked("testing");
+
+ // Back to IDLE
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ assertTrue(stationaryListener.isStationary);
+
+ // Test motion
+ stationaryListener.motionExpected = true;
+ mDeviceIdleController.mMotionListener.onTrigger(null);
+ assertFalse(stationaryListener.isStationary);
+ }
+
+ @Test
+ public void testStationaryDetection_QuickDozeOn() {
+ setAlarmSoon(false);
+ enterDeepState(STATE_QUICK_DOZE_DELAY);
+ mDeviceIdleController.stepIdleStateLocked("testing");
+ verifyStateConditions(STATE_IDLE);
+ // Quick doze progression through states, so time should have increased appropriately.
+ mInjector.nowElapsed += mConstants.QUICK_DOZE_DELAY_TIMEOUT;
+ final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListener = ArgumentCaptor
+ .forClass(AlarmManager.OnAlarmListener.class);
+ doNothing().when(mAlarmManager).set(anyInt(), anyLong(), eq("DeviceIdleController.motion"),
+ alarmListener.capture(), any());
+
+ StationaryListenerForTest stationaryListener = new StationaryListenerForTest();
+
+ stationaryListener.motionExpected = true;
+ mDeviceIdleController.registerStationaryListener(stationaryListener);
+ assertFalse(stationaryListener.isStationary);
+
+ // Go to IDLE_MAINTENANCE
+ mDeviceIdleController.stepIdleStateLocked("testing");
+
+ mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
+
+ // Back to IDLE
+ mDeviceIdleController.stepIdleStateLocked("testing");
+
+ // Now enough time has passed.
+ mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
+ stationaryListener.motionExpected = false;
+ alarmListener.getValue().onAlarm();
+ assertTrue(stationaryListener.isStationary);
+
+ stationaryListener.motionExpected = true;
+ mDeviceIdleController.mMotionListener.onSensorChanged(null);
+ assertFalse(stationaryListener.isStationary);
+
+ // Since we're in quick doze, the device shouldn't stop idling.
+ verifyStateConditions(STATE_IDLE);
+
+ // Go to IDLE_MAINTENANCE
+ mDeviceIdleController.stepIdleStateLocked("testing");
+
+ mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
+
+ // Back to IDLE
+ mDeviceIdleController.stepIdleStateLocked("testing");
+
+ // Now enough time has passed.
+ mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2;
+ stationaryListener.motionExpected = false;
+ alarmListener.getValue().onAlarm();
+ assertTrue(stationaryListener.isStationary);
+ }
+
private void enterDeepState(int state) {
switch (state) {
case STATE_ACTIVE:
@@ -1827,10 +1940,10 @@ public class DeviceIdleControllerTest {
}
private void setNetworkConnected(boolean connected) {
- mInjector.connectivityService = mConnectivityService;
+ mInjector.connectivityManager = mConnectivityManager;
final NetworkInfo ani = mock(NetworkInfo.class);
doReturn(connected).when(ani).isConnected();
- doReturn(ani).when(mConnectivityService).getActiveNetworkInfo();
+ doReturn(ani).when(mConnectivityManager).getActiveNetworkInfo();
mDeviceIdleController.updateConnectivityState(null);
}
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 1685b04cf2ca..6dd1bd87019f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -82,6 +82,7 @@ import android.os.Build;
import android.os.IBinder;
import android.os.PowerManagerInternal;
import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -103,6 +104,7 @@ import java.util.ArrayList;
* Build/Install/Run:
* atest MockingOomAdjusterTests
*/
+@Presubmit
public class MockingOomAdjusterTests {
private static final int MOCKAPP_PID = 12345;
private static final int MOCKAPP_UID = 12345;
@@ -139,8 +141,10 @@ public class MockingOomAdjusterTests {
sService.mConstants = new ActivityManagerConstants(sContext, sService,
sContext.getMainThreadHandler());
+ ProcessList pr = new ProcessList();
+ pr.init(sService, new ActiveUids(sService, false));
setFieldValue(ActivityManagerService.class, sService, "mProcessList",
- new ProcessList());
+ pr);
setFieldValue(ActivityManagerService.class, sService, "mHandler",
mock(ActivityManagerService.MainHandler.class));
setFieldValue(ActivityManagerService.class, sService, "mProcessStats",
@@ -344,11 +348,13 @@ public class MockingOomAdjusterTests {
public void testUpdateOomAdj_DoOne_RecentTasks() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- doReturn(true).when(app).hasRecentTasks();
+ doReturn(mock(WindowProcessController.class)).when(app).getWindowProcessController();
+ WindowProcessController wpc = app.getWindowProcessController();
+ doReturn(true).when(wpc).hasRecentTasks();
app.lastTopTime = SystemClock.uptimeMillis();
sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
- doCallRealMethod().when(app).hasRecentTasks();
+ doCallRealMethod().when(wpc).hasRecentTasks();
assertEquals(PROCESS_STATE_CACHED_RECENT, app.setProcState);
}
@@ -457,7 +463,7 @@ public class MockingOomAdjusterTests {
doReturn(mock(WindowProcessController.class)).when(app).getWindowProcessController();
WindowProcessController wpc = app.getWindowProcessController();
doReturn(true).when(wpc).isPreviousProcess();
- doReturn(true).when(app).hasActivities();
+ doReturn(true).when(wpc).hasActivities();
sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
@@ -642,7 +648,7 @@ public class MockingOomAdjusterTests {
WindowProcessController wpc = app.getWindowProcessController();
doReturn(false).when(wpc).isHomeProcess();
doReturn(true).when(wpc).isPreviousProcess();
- doReturn(true).when(app).hasActivities();
+ doReturn(true).when(wpc).hasActivities();
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, Context.BIND_ALLOW_OOM_MANAGEMENT, mock(IBinder.class));
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
index 06366cf7d143..b6a7b09b3b7f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
@@ -162,12 +162,12 @@ public class AppOpsServiceTest {
mAppOpsService.setMode(OP_WRITE_SMS, mMyUid, sMyPackageName, MODE_ERRORED);
// Note an op that's allowed.
- mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName);
+ mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null);
List<PackageOps> loggedOps = getLoggedOps();
assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED);
// Note another op that's not allowed.
- mAppOpsService.noteOperation(OP_WRITE_SMS, mMyUid, sMyPackageName);
+ mAppOpsService.noteOperation(OP_WRITE_SMS, mMyUid, sMyPackageName, null);
loggedOps = getLoggedOps();
assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED);
assertContainsOp(loggedOps, OP_WRITE_SMS, -1, mTestStartMillis, MODE_ERRORED);
@@ -183,7 +183,7 @@ public class AppOpsServiceTest {
// This op controls WIFI_SCAN
mAppOpsService.setMode(OP_COARSE_LOCATION, mMyUid, sMyPackageName, MODE_ALLOWED);
- assertThat(mAppOpsService.noteOperation(OP_WIFI_SCAN, mMyUid, sMyPackageName))
+ assertThat(mAppOpsService.noteOperation(OP_WIFI_SCAN, mMyUid, sMyPackageName, null))
.isEqualTo(MODE_ALLOWED);
assertContainsOp(getLoggedOps(), OP_WIFI_SCAN, mTestStartMillis, -1,
@@ -191,7 +191,7 @@ public class AppOpsServiceTest {
// Now set COARSE_LOCATION to ERRORED -> this will make WIFI_SCAN disabled as well.
mAppOpsService.setMode(OP_COARSE_LOCATION, mMyUid, sMyPackageName, MODE_ERRORED);
- assertThat(mAppOpsService.noteOperation(OP_WIFI_SCAN, mMyUid, sMyPackageName))
+ assertThat(mAppOpsService.noteOperation(OP_WIFI_SCAN, mMyUid, sMyPackageName, null))
.isEqualTo(MODE_ERRORED);
assertContainsOp(getLoggedOps(), OP_WIFI_SCAN, mTestStartMillis, mTestStartMillis,
@@ -203,8 +203,8 @@ public class AppOpsServiceTest {
public void testStatePersistence() {
mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
mAppOpsService.setMode(OP_WRITE_SMS, mMyUid, sMyPackageName, MODE_ERRORED);
- mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName);
- mAppOpsService.noteOperation(OP_WRITE_SMS, mMyUid, sMyPackageName);
+ mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null);
+ mAppOpsService.noteOperation(OP_WRITE_SMS, mMyUid, sMyPackageName, null);
mAppOpsService.writeState();
// Create a new app ops service, and initialize its state from XML.
@@ -221,7 +221,7 @@ public class AppOpsServiceTest {
@Test
public void testShutdown() {
mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
- mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName);
+ mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null);
mAppOpsService.shutdown();
// Create a new app ops service, and initialize its state from XML.
@@ -236,7 +236,7 @@ public class AppOpsServiceTest {
@Test
public void testGetOpsForPackage() {
mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
- mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName);
+ mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null);
// Query all ops
List<PackageOps> loggedOps = mAppOpsService.getOpsForPackage(
@@ -265,7 +265,7 @@ public class AppOpsServiceTest {
@Test
public void testPackageRemoved() {
mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
- mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName);
+ mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null);
List<PackageOps> loggedOps = getLoggedOps();
assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED);
@@ -278,7 +278,7 @@ public class AppOpsServiceTest {
@Test
public void testPackageRemovedHistoricalOps() throws InterruptedException {
mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
- mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName);
+ mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null);
AppOpsManager.HistoricalOps historicalOps = new AppOpsManager.HistoricalOps(0, 15000);
historicalOps.increaseAccessCount(OP_READ_SMS, mMyUid, sMyPackageName,
@@ -317,7 +317,7 @@ public class AppOpsServiceTest {
@Test
public void testUidRemoved() {
mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
- mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName);
+ mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null);
List<PackageOps> loggedOps = getLoggedOps();
assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED);
@@ -339,18 +339,18 @@ public class AppOpsServiceTest {
setupProcStateTests();
mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName))
+ assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
.isNotEqualTo(MODE_ALLOWED);
mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_TOP);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName))
+ assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
.isEqualTo(MODE_ALLOWED);
mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
// Second time to make sure that settle time is overcome
Thread.sleep(50);
mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName))
+ assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
.isNotEqualTo(MODE_ALLOWED);
}
@@ -359,11 +359,11 @@ public class AppOpsServiceTest {
setupProcStateTests();
mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName))
+ assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
.isNotEqualTo(MODE_ALLOWED);
mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName))
+ assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
.isNotEqualTo(MODE_ALLOWED);
}
@@ -372,12 +372,12 @@ public class AppOpsServiceTest {
setupProcStateTests();
mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName))
+ assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
.isNotEqualTo(MODE_ALLOWED);
mAppOpsService.updateUidProcState(mMyUid,
PROCESS_STATE_FOREGROUND_SERVICE_LOCATION);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName))
+ assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
.isEqualTo(MODE_ALLOWED);
}
@@ -386,18 +386,18 @@ public class AppOpsServiceTest {
setupProcStateTests();
mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName))
+ assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
.isNotEqualTo(MODE_ALLOWED);
mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_TOP);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName))
+ assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
.isEqualTo(MODE_ALLOWED);
mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE);
// Second time to make sure that settle time is overcome
Thread.sleep(50);
mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName))
+ assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
.isNotEqualTo(MODE_ALLOWED);
}
@@ -406,25 +406,25 @@ public class AppOpsServiceTest {
setupProcStateTests();
mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName))
+ assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
.isNotEqualTo(MODE_ALLOWED);
mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_TOP);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName))
+ assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
.isEqualTo(MODE_ALLOWED);
mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE_LOCATION);
// Second time to make sure that settle time is overcome
Thread.sleep(50);
mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE_LOCATION);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName))
+ assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
.isEqualTo(MODE_ALLOWED);
mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE);
// Second time to make sure that settle time is overcome
Thread.sleep(50);
mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName))
+ assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
.isNotEqualTo(MODE_ALLOWED);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java
index 4538cacbf9c4..5c2b8ce4b432 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java
@@ -20,6 +20,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.junit.Assert.assertArrayEquals;
+
import android.content.Context;
import android.content.res.Resources;
import android.os.Binder;
@@ -184,8 +186,56 @@ public class DisplayWhiteBalanceTintControllerTest {
.isFalse();
}
+ /**
+ * Matrix should match the precalculated one for given cct and display primaries.
+ */
+ @Test
+ public void displayWhiteBalance_validateTransformMatrix() {
+ DisplayPrimaries displayPrimaries = new DisplayPrimaries();
+ displayPrimaries.red = new CieXyz();
+ displayPrimaries.red.X = 0.412315f;
+ displayPrimaries.red.Y = 0.212600f;
+ displayPrimaries.red.Z = 0.019327f;
+ displayPrimaries.green = new CieXyz();
+ displayPrimaries.green.X = 0.357600f;
+ displayPrimaries.green.Y = 0.715200f;
+ displayPrimaries.green.Z = 0.119200f;
+ displayPrimaries.blue = new CieXyz();
+ displayPrimaries.blue.X = 0.180500f;
+ displayPrimaries.blue.Y = 0.072200f;
+ displayPrimaries.blue.Z = 0.950633f;
+ displayPrimaries.white = new CieXyz();
+ displayPrimaries.white.X = 0.950456f;
+ displayPrimaries.white.Y = 1.000000f;
+ displayPrimaries.white.Z = 1.089058f;
+ doReturn(displayPrimaries)
+ .when(() -> SurfaceControl.getDisplayNativePrimaries(mDisplayToken));
+
+ setUpTintController();
+ assertWithMessage("Setup with valid SurfaceControl failed")
+ .that(mDisplayWhiteBalanceTintController.mSetUp)
+ .isTrue();
+
+ final int cct = 6500;
+ mDisplayWhiteBalanceTintController.setMatrix(cct);
+ assertWithMessage("Failed to set temperature")
+ .that(mDisplayWhiteBalanceTintController.mCurrentColorTemperature)
+ .isEqualTo(cct);
+
+ float[] matrixDwb = mDisplayWhiteBalanceTintController.getMatrix();
+ final float[] expectedMatrixDwb = {
+ 0.962880f, -0.001780f, -0.000158f, 0.0f,
+ 0.035765f, 0.929988f, 0.000858f, 0.0f,
+ 0.001354f, -0.000470f, 0.948327f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f
+ };
+ assertArrayEquals("Unexpected DWB matrix", matrixDwb, expectedMatrixDwb,
+ 1e-6f /* tolerance */);
+ }
+
private void setUpTintController() {
mDisplayWhiteBalanceTintController = new DisplayWhiteBalanceTintController();
mDisplayWhiteBalanceTintController.setUp(mMockedContext, true);
+ mDisplayWhiteBalanceTintController.setActivated(true);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/SystemConfigTest.java
new file mode 100644
index 000000000000..ff03391ea031
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/SystemConfigTest.java
@@ -0,0 +1,180 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.Set;
+
+/**
+ * Tests for {@link SystemConfig}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:SystemConfigTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SystemConfigTest {
+ private static final String LOG_TAG = "SystemConfigTest";
+
+ private SystemConfig mSysConfig;
+
+ @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+ @Before
+ public void setUp() throws Exception {
+ mSysConfig = new SystemConfigTestClass();
+ }
+
+ /**
+ * Subclass of SystemConfig without running the constructor.
+ */
+ private class SystemConfigTestClass extends SystemConfig {
+ SystemConfigTestClass() {
+ super(false);
+ }
+ }
+
+ /**
+ * Tests that readPermissions works correctly for the tag: install-in-user-type
+ */
+ @Test
+ public void testInstallInUserType() throws Exception {
+ final String contents1 =
+ "<permissions>\n"
+ + " <install-in-user-type package=\"com.android.package1\">\n"
+ + " <install-in user-type=\"FULL\" />\n"
+ + " <install-in user-type=\"PROFILE\" />\n"
+ + " </install-in-user-type>\n"
+ + " <install-in-user-type package=\"com.android.package2\">\n"
+ + " <install-in user-type=\"FULL\" />\n"
+ + " <install-in user-type=\"PROFILE\" />\n"
+ + " <do-not-install-in user-type=\"GUEST\" />\n"
+ + " </install-in-user-type>\n"
+ + "</permissions>";
+
+ final String contents2 =
+ "<permissions>\n"
+ + " <install-in-user-type package=\"com.android.package2\">\n"
+ + " <install-in user-type=\"SYSTEM\" />\n"
+ + " <do-not-install-in user-type=\"PROFILE\" />\n"
+ + " </install-in-user-type>\n"
+ + "</permissions>";
+
+ final String contents3 =
+ "<permissions>\n"
+ + " <install-in-user-type package=\"com.android.package2\">\n"
+ + " <install-in invalid-attribute=\"ADMIN\" />\n" // Ignore invalid attribute
+ + " </install-in-user-type>\n"
+ + " <install-in-user-type package=\"com.android.package2\">\n"
+ + " <install-in user-type=\"RESTRICTED\" />\n" // Valid
+ + " </install-in-user-type>\n"
+ + " <install-in-user-type>\n" // Ignored since missing package name
+ + " <install-in user-type=\"ADMIN\" />\n"
+ + " </install-in-user-type>\n"
+ + "</permissions>";
+
+ Map<String, Set<String>> expectedWhite = new ArrayMap<>();
+ expectedWhite.put("com.android.package1",
+ new ArraySet<>(Arrays.asList("FULL", "PROFILE")));
+ expectedWhite.put("com.android.package2",
+ new ArraySet<>(Arrays.asList("FULL", "PROFILE", "RESTRICTED", "SYSTEM")));
+
+ Map<String, Set<String>> expectedBlack = new ArrayMap<>();
+ expectedBlack.put("com.android.package2",
+ new ArraySet<>(Arrays.asList("GUEST", "PROFILE")));
+
+ final File folder1 = createTempSubfolder("folder1");
+ createTempFile(folder1, "permFile1.xml", contents1);
+
+ final File folder2 = createTempSubfolder("folder2");
+ createTempFile(folder2, "permFile2.xml", contents2);
+
+ // Also, make a third file, but with the name folder1/permFile2.xml, to prove no conflicts.
+ createTempFile(folder1, "permFile2.xml", contents3);
+
+ mSysConfig.readPermissions(folder1, /* No permission needed anyway */ 0);
+ mSysConfig.readPermissions(folder2, /* No permission needed anyway */ 0);
+
+ Map<String, Set<String>> actualWhite = mSysConfig.getAndClearPackageToUserTypeWhitelist();
+ Map<String, Set<String>> actualBlack = mSysConfig.getAndClearPackageToUserTypeBlacklist();
+
+ assertEquals("Whitelist was not cleared", 0,
+ mSysConfig.getAndClearPackageToUserTypeWhitelist().size());
+ assertEquals("Blacklist was not cleared", 0,
+ mSysConfig.getAndClearPackageToUserTypeBlacklist().size());
+
+ assertEquals("Incorrect whitelist.", expectedWhite, actualWhite);
+ assertEquals("Incorrect blacklist", expectedBlack, actualBlack);
+ }
+
+ /**
+ * Creates folderName/fileName in the mTemporaryFolder and fills it with the contents.
+ * @param folderName subdirectory of mTemporaryFolder to put the file, creating if needed
+ * @return the folder
+ */
+ private File createTempSubfolder(String folderName)
+ throws IOException {
+ File folder = new File(mTemporaryFolder.getRoot(), folderName);
+ folder.mkdir();
+ return folder;
+ }
+
+ /**
+ * Creates folderName/fileName in the mTemporaryFolder and fills it with the contents.
+ * @param folder pre-existing subdirectory of mTemporaryFolder to put the file
+ * @param fileName name of the file (e.g. filename.xml) to create
+ * @param contents contents to write to the file
+ * @return the folder containing the newly created file (not the file itself!)
+ */
+ private File createTempFile(File folder, String fileName, String contents)
+ throws IOException {
+ File file = new File(folder, fileName);
+ BufferedWriter bw = new BufferedWriter(new FileWriter(file));
+ bw.write(contents);
+ bw.close();
+
+ // Print to logcat for test debugging.
+ Log.d(LOG_TAG, "Contents of file " + file.getAbsolutePath());
+ Scanner input = new Scanner(file);
+ while (input.hasNextLine()) {
+ Log.d(LOG_TAG, input.nextLine());
+ }
+
+ return folder;
+ }
+}
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 73dcb98abc8c..d11d98766b01 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -46,6 +46,7 @@ import static android.view.accessibility.AccessibilityNodeInfo.ROOT_NODE_ID;
import static org.hamcrest.Matchers.hasItems;
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.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -140,6 +141,8 @@ public class AbstractAccessibilityServiceConnectionTest {
private final List<AccessibilityWindowInfo> mA11yWindowInfosOnSecondDisplay = new ArrayList<>();
private Callable[] mFindA11yNodesFunctions;
private Callable<Boolean> mPerformA11yAction;
+ private ArrayList<Integer> mDisplayList = new ArrayList<>(Arrays.asList(
+ Display.DEFAULT_DISPLAY, SECONDARY_DISPLAY_ID));
// To mock package-private class.
@Rule public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
@@ -153,7 +156,7 @@ public class AbstractAccessibilityServiceConnectionTest {
@Mock private AccessibilityWindowManager mMockA11yWindowManager;
@Mock private AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
@Mock private WindowManagerInternal mMockWindowManagerInternal;
- @Mock private GlobalActionPerformer mMockGlobalActionPerformer;
+ @Mock private SystemActionPerformer mMockSystemActionPerformer;
@Mock private IBinder mMockService;
@Mock private IAccessibilityServiceClient mMockServiceInterface;
@Mock private KeyEventDispatcher mMockKeyEventDispatcher;
@@ -184,6 +187,7 @@ public class AbstractAccessibilityServiceConnectionTest {
addA11yWindowInfo(mA11yWindowInfos, PIP_WINDOWID, true, Display.DEFAULT_DISPLAY);
addA11yWindowInfo(mA11yWindowInfosOnSecondDisplay, WINDOWID_ONSECONDDISPLAY, false,
SECONDARY_DISPLAY_ID);
+ when(mMockA11yWindowManager.getDisplayListLocked()).thenReturn(mDisplayList);
when(mMockA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY))
.thenReturn(mA11yWindowInfos);
when(mMockA11yWindowManager.findA11yWindowInfoByIdLocked(WINDOWID))
@@ -217,7 +221,7 @@ public class AbstractAccessibilityServiceConnectionTest {
mServiceConnection = new TestAccessibilityServiceConnection(mMockContext, COMPONENT_NAME,
mSpyServiceInfo, SERVICE_ID, mHandler, new Object(), mMockSecurityPolicy,
- mMockSystemSupport, mMockWindowManagerInternal, mMockGlobalActionPerformer,
+ mMockSystemSupport, mMockWindowManagerInternal, mMockSystemActionPerformer,
mMockA11yWindowManager);
// Assume that the service is connected
mServiceConnection.mService = mMockService;
@@ -285,7 +289,14 @@ public class AbstractAccessibilityServiceConnectionTest {
@Test
public void getWindows() {
- assertThat(mServiceConnection.getWindows(), is(mA11yWindowInfos));
+ final AccessibilityWindowInfo.WindowListSparseArray allWindows =
+ mServiceConnection.getWindows();
+
+ assertEquals(2, allWindows.size());
+ assertThat(allWindows.get(Display.DEFAULT_DISPLAY), is(mA11yWindowInfos));
+ assertEquals(2, allWindows.get(Display.DEFAULT_DISPLAY).size());
+ assertThat(allWindows.get(SECONDARY_DISPLAY_ID), is(mA11yWindowInfosOnSecondDisplay));
+ assertEquals(1, allWindows.get(SECONDARY_DISPLAY_ID).size());
}
@Test
@@ -478,7 +489,7 @@ public class AbstractAccessibilityServiceConnectionTest {
@Test
public void performGlobalAction() {
mServiceConnection.performGlobalAction(GLOBAL_ACTION_HOME);
- verify(mMockGlobalActionPerformer).performGlobalAction(GLOBAL_ACTION_HOME);
+ verify(mMockSystemActionPerformer).performSystemAction(GLOBAL_ACTION_HOME);
}
@Test
@@ -765,10 +776,10 @@ public class AbstractAccessibilityServiceConnectionTest {
AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
Object lock, AccessibilitySecurityPolicy securityPolicy,
SystemSupport systemSupport, WindowManagerInternal windowManagerInternal,
- GlobalActionPerformer globalActionPerfomer,
+ SystemActionPerformer systemActionPerfomer,
AccessibilityWindowManager a11yWindowManager) {
super(context, componentName, accessibilityServiceInfo, id, mainHandler, lock,
- securityPolicy, systemSupport, windowManagerInternal, globalActionPerfomer,
+ securityPolicy, systemSupport, windowManagerInternal, systemActionPerfomer,
a11yWindowManager);
mResolvedUserId = USER_ID;
}
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 6be5a3785865..597d337c2450 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -41,12 +41,14 @@ import android.content.pm.ServiceInfo;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.testing.DexmakerShareClassLoaderRule;
import android.view.Display;
import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -65,9 +67,14 @@ public class AccessibilityServiceConnectionTest {
"com.android.server.accessibility", "AccessibilityServiceConnectionTest");
static final int SERVICE_ID = 42;
+ // Mock package-private AccessibilityUserState class
+ @Rule
+ public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
+ new DexmakerShareClassLoaderRule();
+
AccessibilityServiceConnection mConnection;
- @Mock AccessibilityManagerService.UserState mMockUserState;
+ @Mock AccessibilityUserState mMockUserState;
@Mock Context mMockContext;
@Mock AccessibilityServiceInfo mMockServiceInfo;
@Mock ResolveInfo mMockResolveInfo;
@@ -75,7 +82,7 @@ public class AccessibilityServiceConnectionTest {
@Mock AccessibilityWindowManager mMockA11yWindowManager;
@Mock AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
@Mock WindowManagerInternal mMockWindowManagerInternal;
- @Mock GlobalActionPerformer mMockGlobalActionPerformer;
+ @Mock SystemActionPerformer mMockSystemActionPerformer;
@Mock KeyEventDispatcher mMockKeyEventDispatcher;
@Mock MagnificationController mMockMagnificationController;
@Mock IBinder mMockIBinder;
@@ -104,7 +111,7 @@ public class AccessibilityServiceConnectionTest {
mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext,
COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(),
mMockSecurityPolicy, mMockSystemSupport, mMockWindowManagerInternal,
- mMockGlobalActionPerformer, mMockA11yWindowManager);
+ mMockSystemActionPerformer, mMockA11yWindowManager);
when(mMockSecurityPolicy.canPerformGestures(mConnection)).thenReturn(true);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
new file mode 100644
index 000000000000..d70e1648f719
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility;
+
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+import android.testing.DexmakerShareClassLoaderRule;
+
+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.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Tests for AccessibilityUserState */
+public class AccessibilityUserStateTest {
+
+ private static final ComponentName COMPONENT_NAME =
+ new ComponentName("com.android.server.accessibility", "AccessibilityUserStateTest");
+
+ // Values of setting key SHOW_IME_WITH_HARD_KEYBOARD
+ private static final int STATE_HIDE_IME = 0;
+ private static final int STATE_SHOW_IME = 1;
+
+ private static final int USER_ID = 42;
+
+ // Mock package-private class AccessibilityServiceConnection
+ @Rule public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
+ new DexmakerShareClassLoaderRule();
+
+ @Mock private AccessibilityServiceInfo mMockServiceInfo;
+
+ @Mock private AccessibilityServiceConnection mMockConnection;
+
+ @Mock private AccessibilityUserState.ServiceInfoChangeListener mMockListener;
+
+ @Mock private Context mContext;
+
+ private MockContentResolver mMockResolver;
+
+ private AccessibilityUserState mUserState;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ FakeSettingsProvider.clearSettingsProvider();
+ mMockResolver = new MockContentResolver();
+ mMockResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ when(mContext.getContentResolver()).thenReturn(mMockResolver);
+ when(mMockServiceInfo.getComponentName()).thenReturn(COMPONENT_NAME);
+ when(mMockConnection.getServiceInfo()).thenReturn(mMockServiceInfo);
+
+ mUserState = new AccessibilityUserState(USER_ID, mContext, mMockListener);
+ }
+
+ @After
+ public void tearDown() {
+ FakeSettingsProvider.clearSettingsProvider();
+ }
+
+ @Test
+ public void onSwitchToAnotherUser_userStateClearedNonDefaultValues() {
+ mUserState.getBoundServicesLocked().add(mMockConnection);
+ mUserState.getBindingServicesLocked().add(COMPONENT_NAME);
+ mUserState.setLastSentClientStateLocked(
+ STATE_FLAG_ACCESSIBILITY_ENABLED
+ | STATE_FLAG_TOUCH_EXPLORATION_ENABLED
+ | STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED);
+ mUserState.setNonInteractiveUiTimeoutLocked(30);
+ mUserState.setInteractiveUiTimeoutLocked(30);
+ mUserState.mEnabledServices.add(COMPONENT_NAME);
+ mUserState.mTouchExplorationGrantedServices.add(COMPONENT_NAME);
+ mUserState.setTouchExplorationEnabledLocked(true);
+ mUserState.setDisplayMagnificationEnabledLocked(true);
+ mUserState.setNavBarMagnificationEnabledLocked(true);
+ mUserState.setServiceAssignedToAccessibilityButtonLocked(COMPONENT_NAME);
+ mUserState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(true);
+ mUserState.setAutoclickEnabledLocked(true);
+ mUserState.setUserNonInteractiveUiTimeoutLocked(30);
+ mUserState.setUserInteractiveUiTimeoutLocked(30);
+
+ mUserState.onSwitchToAnotherUserLocked();
+
+ verify(mMockConnection).unbindLocked();
+ assertTrue(mUserState.getBoundServicesLocked().isEmpty());
+ assertTrue(mUserState.getBindingServicesLocked().isEmpty());
+ assertEquals(-1, mUserState.getLastSentClientStateLocked());
+ assertEquals(0, mUserState.getNonInteractiveUiTimeoutLocked());
+ assertEquals(0, mUserState.getInteractiveUiTimeoutLocked());
+ assertTrue(mUserState.mEnabledServices.isEmpty());
+ assertTrue(mUserState.mTouchExplorationGrantedServices.isEmpty());
+ assertFalse(mUserState.isTouchExplorationEnabledLocked());
+ assertFalse(mUserState.isDisplayMagnificationEnabledLocked());
+ assertFalse(mUserState.isNavBarMagnificationEnabledLocked());
+ assertNull(mUserState.getServiceAssignedToAccessibilityButtonLocked());
+ assertFalse(mUserState.isNavBarMagnificationAssignedToAccessibilityButtonLocked());
+ assertFalse(mUserState.isAutoclickEnabledLocked());
+ assertEquals(0, mUserState.getUserNonInteractiveUiTimeoutLocked());
+ assertEquals(0, mUserState.getUserInteractiveUiTimeoutLocked());
+ }
+
+ @Test
+ public void addService_connectionAlreadyAdded_notAddAgain() {
+ mUserState.getBoundServicesLocked().add(mMockConnection);
+
+ mUserState.addServiceLocked(mMockConnection);
+
+ verify(mMockConnection, never()).onAdded();
+ }
+
+ @Test
+ public void addService_connectionNotYetAddedToBoundService_addAndNotifyServices() {
+ when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME);
+
+ mUserState.addServiceLocked(mMockConnection);
+
+ verify(mMockConnection).onAdded();
+ assertTrue(mUserState.getBoundServicesLocked().contains(mMockConnection));
+ assertEquals(mMockConnection, mUserState.mComponentNameToServiceMap.get(COMPONENT_NAME));
+ verify(mMockListener).onServiceInfoChangedLocked(eq(mUserState));
+ }
+
+ @Test
+ public void reconcileSoftKeyboardMode_whenStateNotMatchSettings_setBothDefault() {
+ // When soft kb show mode is hidden in settings and is auto in state.
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ SHOW_MODE_HIDDEN, USER_ID);
+
+ mUserState.reconcileSoftKeyboardModeWithSettingsLocked();
+
+ assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked());
+ assertEquals(SHOW_MODE_AUTO, getSecureIntForUser(
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, USER_ID));
+ assertNull(mUserState.getServiceChangingSoftKeyboardModeLocked());
+ }
+
+ @Test
+ public void
+ reconcileSoftKeyboardMode_stateIgnoreHardKb_settingsShowImeHardKb_setAutoOverride() {
+ // When show mode is ignore hard kb without original hard kb value
+ // and show ime with hard kb is hide
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ SHOW_MODE_IGNORE_HARD_KEYBOARD, USER_ID);
+ mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, COMPONENT_NAME);
+ putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
+ STATE_HIDE_IME, USER_ID);
+
+ mUserState.reconcileSoftKeyboardModeWithSettingsLocked();
+
+ assertEquals(SHOW_MODE_AUTO | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN,
+ getSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, USER_ID));
+ assertNull(mUserState.getServiceChangingSoftKeyboardModeLocked());
+ }
+
+ @Test
+ public void removeService_serviceChangingSoftKeyboardMode_removeAndSetSoftKbModeAuto() {
+ mUserState.setServiceChangingSoftKeyboardModeLocked(COMPONENT_NAME);
+ mUserState.mComponentNameToServiceMap.put(COMPONENT_NAME, mMockConnection);
+ mUserState.setSoftKeyboardModeLocked(SHOW_MODE_HIDDEN, COMPONENT_NAME);
+
+ mUserState.removeServiceLocked(mMockConnection);
+
+ assertFalse(mUserState.getBoundServicesLocked().contains(mMockConnection));
+ verify(mMockConnection).onRemoved();
+ assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked());
+ assertNull(mUserState.mComponentNameToServiceMap.get(COMPONENT_NAME));
+ verify(mMockListener).onServiceInfoChangedLocked(eq(mUserState));
+ }
+
+ @Test
+ public void serviceDisconnected_removeServiceAndAddToCrashed() {
+ when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME);
+ mUserState.addServiceLocked(mMockConnection);
+
+ mUserState.serviceDisconnectedLocked(mMockConnection);
+
+ assertFalse(mUserState.getBoundServicesLocked().contains(mMockConnection));
+ assertTrue(mUserState.getCrashedServicesLocked().contains(COMPONENT_NAME));
+ }
+
+ @Test
+ public void setSoftKeyboardMode_withInvalidShowMode_shouldKeepDefaultAuto() {
+ final int invalidShowMode = SHOW_MODE_HIDDEN | SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
+
+ assertFalse(mUserState.setSoftKeyboardModeLocked(invalidShowMode, null));
+
+ assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked());
+ }
+
+ @Test
+ public void setSoftKeyboardMode_newModeSameWithCurrentState_returnTrue() {
+ when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME);
+ mUserState.addServiceLocked(mMockConnection);
+
+ assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null));
+ }
+
+ @Test
+ public void setSoftKeyboardMode_withIgnoreHardKb_whenHardKbOverridden_returnFalseAdNoChange() {
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ SHOW_MODE_AUTO | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN, USER_ID);
+
+ assertFalse(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, null));
+
+ assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked());
+ }
+
+ @Test
+ public void
+ setSoftKeyboardMode_withIgnoreHardKb_whenShowImeWithHardKb_setOriginalHardKbValue() {
+ putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, STATE_SHOW_IME, USER_ID);
+
+ assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, null));
+
+ assertEquals(SHOW_MODE_IGNORE_HARD_KEYBOARD | SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE,
+ getSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, USER_ID));
+ }
+
+ @Test
+ public void setSoftKeyboardMode_whenCurrentIgnoreHardKb_shouldSetShowImeWithHardKbValue() {
+ mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, COMPONENT_NAME);
+ putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, STATE_HIDE_IME, USER_ID);
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ SHOW_MODE_IGNORE_HARD_KEYBOARD | SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE, USER_ID);
+
+ assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null));
+
+ assertEquals(STATE_SHOW_IME, getSecureIntForUser(
+ Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, USER_ID));
+ }
+
+ @Test
+ public void setSoftKeyboardMode_withRequester_shouldUpdateInternalStateAndSettingsAsIs() {
+ assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_HIDDEN, COMPONENT_NAME));
+
+ assertEquals(SHOW_MODE_HIDDEN, mUserState.getSoftKeyboardShowModeLocked());
+ assertEquals(SHOW_MODE_HIDDEN, getSecureIntForUser(
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, USER_ID));
+ assertEquals(COMPONENT_NAME, mUserState.getServiceChangingSoftKeyboardModeLocked());
+ }
+
+ @Test
+ public void setSoftKeyboardMode_shouldNotifyBoundService() {
+ mUserState.addServiceLocked(mMockConnection);
+
+ assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_HIDDEN, COMPONENT_NAME));
+
+ verify(mMockConnection).notifySoftKeyboardShowModeChangedLocked(eq(SHOW_MODE_HIDDEN));
+ }
+
+ private int getSecureIntForUser(String key, int userId) {
+ return Settings.Secure.getIntForUser(mMockResolver, key, -1, userId);
+ }
+
+ private void putSecureIntForUser(String key, int value, int userId) {
+ Settings.Secure.putIntForUser(mMockResolver, key, value, userId);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index b7b5a4eaacfa..b5e5deb9ff59 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -67,6 +67,7 @@ import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -104,6 +105,9 @@ public class AccessibilityWindowManagerTest {
// List of callback, mapping from displayId -> callback.
private final SparseArray<WindowsForAccessibilityCallback> mCallbackOfWindows =
new SparseArray<>();
+ // List of display ID.
+ private final ArrayList<Integer> mExpectedDisplayList = new ArrayList<>(Arrays.asList(
+ Display.DEFAULT_DISPLAY, SECONDARY_DISPLAY_ID));
private final MessageCapturingHandler mHandler = new MessageCapturingHandler(null);
@@ -692,6 +696,15 @@ public class AccessibilityWindowManagerTest {
assertTrue(windowId >= 0);
}
+ @Test
+ public void getDisplayList() throws RemoteException {
+ // Starts tracking window of second display.
+ startTrackingPerDisplay(SECONDARY_DISPLAY_ID);
+
+ final ArrayList<Integer> displayList = mA11yWindowManager.getDisplayListLocked();
+ assertTrue(displayList.equals(mExpectedDisplayList));
+ }
+
private void startTrackingPerDisplay(int displayId) throws RemoteException {
ArrayList<WindowInfo> windowInfosForDisplay = new ArrayList<>();
// Adds RemoteAccessibilityConnection into AccessibilityWindowManager, and copy
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java
deleted file mode 100644
index c73be6f100cd..000000000000
--- a/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- ** Copyright 2017, The Android Open Source Project
- **
- ** Licensed under the Apache License, Version 2.0 (the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
- **
- ** http://www.apache.org/licenses/LICENSE-2.0
- **
- ** Unless required by applicable law or agreed to in writing, software
- ** distributed under the License is distributed on an "AS IS" BASIS,
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ** See the License for the specific language governing permissions and
- ** limitations under the License.
- */
-
-package com.android.server.accessibility;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.accessibilityservice.AccessibilityService;
-import android.app.StatusBarManager;
-import android.content.Context;
-import android.os.Handler;
-
-import com.android.internal.util.ScreenshotHelper;
-import com.android.server.wm.WindowManagerInternal;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.function.Consumer;
-
-/**
- * Tests for GlobalActionPerformer
- */
-public class GlobalActionPerformerTest {
- GlobalActionPerformer mGlobalActionPerformer;
-
- @Mock Context mMockContext;
- @Mock WindowManagerInternal mMockWindowManagerInternal;
- @Mock StatusBarManager mMockStatusBarManager;
- @Mock ScreenshotHelper mMockScreenshotHelper;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
-
- when(mMockContext.getSystemService(android.app.Service.STATUS_BAR_SERVICE))
- .thenReturn(mMockStatusBarManager);
-
- mGlobalActionPerformer =
- new GlobalActionPerformer(mMockContext, mMockWindowManagerInternal,
- () -> mMockScreenshotHelper);
- }
-
- @Test
- public void testNotifications_expandsNotificationPanel() {
- mGlobalActionPerformer
- .performGlobalAction(AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS);
- verify(mMockStatusBarManager).expandNotificationsPanel();
- }
-
- @Test
- public void testQuickSettings_requestsQuickSettingsPanel() {
- mGlobalActionPerformer
- .performGlobalAction(AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS);
- verify(mMockStatusBarManager).expandSettingsPanel();
- }
-
- @Test
- public void testPowerDialog_requestsFromWindowManager() {
- mGlobalActionPerformer.performGlobalAction(AccessibilityService.GLOBAL_ACTION_POWER_DIALOG);
- verify(mMockWindowManagerInternal).showGlobalActions();
- }
-
- @Test
- public void testScreenshot_requestsFromScreenshotHelper() {
- mGlobalActionPerformer.performGlobalAction(
- AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT);
- verify(mMockScreenshotHelper).takeScreenshot(
- eq(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN), anyBoolean(),
- anyBoolean(), any(Handler.class), any());
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
index 9926a09dd105..322653b4115c 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
@@ -28,12 +28,12 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
+import android.os.IBinder;
import android.view.KeyEvent;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.policy.WindowManagerPolicy.WindowState;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
@@ -79,7 +79,7 @@ public class KeyboardInterceptorTest {
@Test
public void whenVolumeKeyArrives_andPolicySaysUseIt_eventGoesToAms() {
KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_DOWN);
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(event)), eq(0))).thenReturn(0L);
mInterceptor.onKeyEvent(event, 0);
verify(mMockAms).notifyKeyEvent(argThat(matchesKeyEvent(event)), eq(0));
@@ -88,7 +88,7 @@ public class KeyboardInterceptorTest {
@Test
public void whenVolumeKeyArrives_andPolicySaysDropIt_eventDropped() {
KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_UP);
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(event)), eq(0))).thenReturn(-1L);
mInterceptor.onKeyEvent(event, 0);
verify(mMockAms, times(0)).notifyKeyEvent(anyObject(), anyInt());
@@ -98,14 +98,14 @@ public class KeyboardInterceptorTest {
@Test
public void whenVolumeKeyArrives_andPolicySaysDelayThenUse_eventQueuedThenSentToAms() {
KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_UP);
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(event)), eq(0))).thenReturn(150L);
mInterceptor.onKeyEvent(event, 0);
assertTrue(mHandler.hasMessages());
verify(mMockAms, times(0)).notifyKeyEvent(anyObject(), anyInt());
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(event)), eq(0))).thenReturn(0L);
mHandler.sendAllMessages();
@@ -115,14 +115,14 @@ public class KeyboardInterceptorTest {
@Test
public void whenVolumeKeyArrives_andPolicySaysDelayThenDrop_eventQueuedThenDropped() {
KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_DOWN);
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(event)), eq(0))).thenReturn(150L);
mInterceptor.onKeyEvent(event, 0);
assertTrue(mHandler.hasMessages());
verify(mMockAms, times(0)).notifyKeyEvent(anyObject(), anyInt());
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(event)), eq(0))).thenReturn(-1L);
mHandler.sendAllMessages();
@@ -137,18 +137,18 @@ public class KeyboardInterceptorTest {
new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_VOLUME_UP),
new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_0)};
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(events[1])), eq(0))).thenReturn(150L);
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(events[3])), eq(0))).thenReturn(75L);
for (KeyEvent event : events) {
mInterceptor.onKeyEvent(event, 0);
}
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(events[1])), eq(0))).thenReturn(0L);
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(events[3])), eq(0))).thenReturn(0L);
mHandler.sendAllMessages();
@@ -167,18 +167,18 @@ public class KeyboardInterceptorTest {
new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_VOLUME_UP),
new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_0)};
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(events[1])), eq(0))).thenReturn(150L);
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(events[3])), eq(0))).thenReturn(75L);
for (KeyEvent event : events) {
mInterceptor.onKeyEvent(event, 0);
}
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(events[1])), eq(0))).thenReturn(-1L);
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(events[3])), eq(0))).thenReturn(-1L);
mHandler.sendAllMessages();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
index f1142fd2f8f9..36e854ca77cd 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
@@ -46,7 +46,6 @@ import android.graphics.Point;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
-import android.util.Log;
import android.view.Display;
import android.view.InputDevice;
import android.view.KeyEvent;
@@ -55,6 +54,8 @@ import android.view.accessibility.AccessibilityEvent;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.accessibility.utils.MotionEventMatcher;
+
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
@@ -761,56 +762,6 @@ public class MotionEventInjectorTest {
return next;
}
- static class MotionEventMatcher extends TypeSafeMatcher<MotionEvent> {
- long mDownTime;
- long mEventTime;
- long mActionMasked;
- int mX;
- int mY;
-
- MotionEventMatcher(long downTime, long eventTime, int actionMasked, int x, int y) {
- mDownTime = downTime;
- mEventTime = eventTime;
- mActionMasked = actionMasked;
- mX = x;
- mY = y;
- }
-
- MotionEventMatcher(MotionEvent event) {
- this(event.getDownTime(), event.getEventTime(), event.getActionMasked(),
- (int) event.getX(), (int) event.getY());
- }
-
- void offsetTimesBy(long timeOffset) {
- mDownTime += timeOffset;
- mEventTime += timeOffset;
- }
-
- @Override
- public boolean matchesSafely(MotionEvent event) {
- if ((event.getDownTime() == mDownTime) && (event.getEventTime() == mEventTime)
- && (event.getActionMasked() == mActionMasked) && ((int) event.getX() == mX)
- && ((int) event.getY() == mY)) {
- return true;
- }
- Log.e(LOG_TAG, "MotionEvent match failed");
- Log.e(LOG_TAG, "event.getDownTime() = " + event.getDownTime()
- + ", expected " + mDownTime);
- Log.e(LOG_TAG, "event.getEventTime() = " + event.getEventTime()
- + ", expected " + mEventTime);
- Log.e(LOG_TAG, "event.getActionMasked() = " + event.getActionMasked()
- + ", expected " + mActionMasked);
- Log.e(LOG_TAG, "event.getX() = " + event.getX() + ", expected " + mX);
- Log.e(LOG_TAG, "event.getY() = " + event.getY() + ", expected " + mY);
- return false;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("Motion event matcher");
- }
- }
-
private static class MotionEventActionMatcher extends TypeSafeMatcher<MotionEvent> {
int mAction;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
new file mode 100644
index 000000000000..37f5b87ac115
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.accessibilityservice.AccessibilityService;
+import android.app.StatusBarManager;
+import android.content.Context;
+import android.os.Handler;
+
+import com.android.internal.util.ScreenshotHelper;
+import com.android.server.wm.WindowManagerInternal;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for SystemActionPerformer
+ */
+public class SystemActionPerformerTest {
+ SystemActionPerformer mSystemActionPerformer;
+
+ @Mock Context mMockContext;
+ @Mock WindowManagerInternal mMockWindowManagerInternal;
+ @Mock StatusBarManager mMockStatusBarManager;
+ @Mock ScreenshotHelper mMockScreenshotHelper;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mMockContext.getSystemService(android.app.Service.STATUS_BAR_SERVICE))
+ .thenReturn(mMockStatusBarManager);
+
+ mSystemActionPerformer =
+ new SystemActionPerformer(mMockContext, mMockWindowManagerInternal,
+ () -> mMockScreenshotHelper);
+ }
+
+ @Test
+ public void testNotifications_expandsNotificationPanel() {
+ mSystemActionPerformer
+ .performSystemAction(AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS);
+ verify(mMockStatusBarManager).expandNotificationsPanel();
+ }
+
+ @Test
+ public void testQuickSettings_requestsQuickSettingsPanel() {
+ mSystemActionPerformer
+ .performSystemAction(AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS);
+ verify(mMockStatusBarManager).expandSettingsPanel();
+ }
+
+ @Test
+ public void testPowerDialog_requestsFromWindowManager() {
+ mSystemActionPerformer.performSystemAction(AccessibilityService.GLOBAL_ACTION_POWER_DIALOG);
+ verify(mMockWindowManagerInternal).showGlobalActions();
+ }
+
+ @Test
+ public void testScreenshot_requestsFromScreenshotHelper() {
+ mSystemActionPerformer.performSystemAction(
+ AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT);
+ verify(mMockScreenshotHelper).takeScreenshot(
+ eq(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN), anyBoolean(),
+ anyBoolean(), any(Handler.class), any());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index 210de538d0bd..8da927dcb4ab 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -56,7 +56,6 @@ public class UiAutomationManagerTest {
MessageCapturingHandler mMessageCapturingHandler;
- @Mock AccessibilityManagerService.UserState mMockUserState;
@Mock Context mMockContext;
@Mock AccessibilityServiceInfo mMockServiceInfo;
@Mock ResolveInfo mMockResolveInfo;
@@ -64,7 +63,7 @@ public class UiAutomationManagerTest {
@Mock AccessibilityWindowManager mMockA11yWindowManager;
@Mock AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport;
@Mock WindowManagerInternal mMockWindowManagerInternal;
- @Mock GlobalActionPerformer mMockGlobalActionPerformer;
+ @Mock SystemActionPerformer mMockSystemActionPerformer;
@Mock IBinder mMockOwner;
@Mock IAccessibilityServiceClient mMockAccessibilityServiceClient;
@Mock IBinder mMockServiceAsBinder;
@@ -174,7 +173,7 @@ public class UiAutomationManagerTest {
mUiAutomationManager.registerUiTestAutomationServiceLocked(mMockOwner,
mMockAccessibilityServiceClient, mMockContext, mMockServiceInfo, SERVICE_ID,
mMessageCapturingHandler, mMockSecurityPolicy, mMockSystemSupport,
- mMockWindowManagerInternal, mMockGlobalActionPerformer,
+ mMockWindowManagerInternal, mMockSystemActionPerformer,
mMockA11yWindowManager, flags);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
index 104aacb5ef79..4b1ec6fe032b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
@@ -21,6 +21,7 @@ import static com.android.server.accessibility.gestures.TouchState.STATE_DELEGAT
import static com.android.server.accessibility.gestures.TouchState.STATE_DRAGGING;
import static com.android.server.accessibility.gestures.TouchState.STATE_TOUCH_EXPLORING;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
@@ -36,6 +37,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accessibility.EventStreamTransformation;
+import com.android.server.accessibility.utils.MotionEventMatcher;
import org.junit.Before;
import org.junit.Rule;
@@ -49,6 +51,7 @@ import java.util.List;
@RunWith(AndroidJUnit4.class)
public class TouchExplorerTest {
+ private static final String LOG_TAG = "TouchExplorerTest";
private static final int FLAG_1FINGER = 0x8000;
private static final int FLAG_2FINGERS = 0x0100;
private static final int FLAG_3FINGERS = 0x0200;
@@ -86,7 +89,9 @@ public class TouchExplorerTest {
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ MotionEventMatcher lastEventMatcher = new MotionEventMatcher(mLastEvent);
mEvents.add(0, event.copy());
+ assertThat(rawEvent, lastEventMatcher);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/utils/MotionEventMatcher.java b/services/tests/servicestests/src/com/android/server/accessibility/utils/MotionEventMatcher.java
new file mode 100644
index 000000000000..2b6d385fed48
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/utils/MotionEventMatcher.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility.utils;
+
+import android.util.Log;
+import android.view.MotionEvent;
+
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+
+/**
+ * This class compares two motion events using a subset of their attributes: actionMasked, downTime,
+ * eventTime, and location. If two events match they are considered to be effectively equal.
+ */
+public class MotionEventMatcher extends TypeSafeMatcher<MotionEvent> {
+ private static final String LOG_TAG = "MotionEventMatcher";
+ long mDownTime;
+ long mEventTime;
+ long mActionMasked;
+ int mX;
+ int mY;
+
+ MotionEventMatcher(long downTime, long eventTime, int actionMasked, int x, int y) {
+ mDownTime = downTime;
+ mEventTime = eventTime;
+ mActionMasked = actionMasked;
+ mX = x;
+ mY = y;
+ }
+
+ public MotionEventMatcher(MotionEvent event) {
+ this(
+ event.getDownTime(),
+ event.getEventTime(),
+ event.getActionMasked(),
+ (int) event.getX(),
+ (int) event.getY());
+ }
+
+ void offsetTimesBy(long timeOffset) {
+ mDownTime += timeOffset;
+ mEventTime += timeOffset;
+ }
+
+ @Override
+ public boolean matchesSafely(MotionEvent event) {
+ if ((event.getDownTime() == mDownTime)
+ && (event.getEventTime() == mEventTime)
+ && (event.getActionMasked() == mActionMasked)
+ && ((int) event.getX() == mX)
+ && ((int) event.getY() == mY)) {
+ return true;
+ }
+ Log.e(LOG_TAG, "MotionEvent match failed");
+ Log.e(LOG_TAG, "event.getDownTime() = " + event.getDownTime() + ", expected " + mDownTime);
+ Log.e(
+ LOG_TAG,
+ "event.getEventTime() = " + event.getEventTime() + ", expected " + mEventTime);
+ Log.e(
+ LOG_TAG,
+ "event.getActionMasked() = "
+ + event.getActionMasked()
+ + ", expected "
+ + mActionMasked);
+ Log.e(LOG_TAG, "event.getX() = " + event.getX() + ", expected " + mX);
+ Log.e(LOG_TAG, "event.getY() = " + event.getY() + ", expected " + mY);
+ return false;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Motion event matcher");
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 29244f036f95..1edc9534b011 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -34,9 +34,9 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
import static com.android.server.am.ActivityManagerInternalTest.CustomThread;
import static com.android.server.am.ActivityManagerService.DISPATCH_UIDS_CHANGED_UI_MSG;
import static com.android.server.am.ActivityManagerService.Injector;
-import static com.android.server.am.ActivityManagerService.NETWORK_STATE_BLOCK;
-import static com.android.server.am.ActivityManagerService.NETWORK_STATE_NO_CHANGE;
-import static com.android.server.am.ActivityManagerService.NETWORK_STATE_UNBLOCK;
+import static com.android.server.am.ProcessList.NETWORK_STATE_BLOCK;
+import static com.android.server.am.ProcessList.NETWORK_STATE_NO_CHANGE;
+import static com.android.server.am.ProcessList.NETWORK_STATE_UNBLOCK;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -274,7 +274,7 @@ public class ActivityManagerServiceTest {
uidRec.setProcState = prevState;
uidRec.setCurProcState(curState);
- mAms.incrementProcStateSeqAndNotifyAppsLocked();
+ mAms.mProcessList.incrementProcStateSeqAndNotifyAppsLocked(mAms.mProcessList.mActiveUids);
// @SuppressWarnings("GuardedBy")
assertEquals(expectedGlobalCounter, mAms.mProcessList.mProcStateSeqCounter);
@@ -429,42 +429,42 @@ public class ActivityManagerServiceTest {
uidRec.setCurProcState(PROCESS_STATE_RECEIVER);
expectedBlockState = NETWORK_STATE_NO_CHANGE;
assertEquals(errorMsg.apply(expectedBlockState),
- expectedBlockState, mAms.getBlockStateForUid(uidRec));
+ expectedBlockState, mAms.mProcessList.getBlockStateForUid(uidRec));
// Foreground to foreground
uidRec.setProcState = PROCESS_STATE_FOREGROUND_SERVICE;
uidRec.setCurProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
expectedBlockState = NETWORK_STATE_NO_CHANGE;
assertEquals(errorMsg.apply(expectedBlockState),
- expectedBlockState, mAms.getBlockStateForUid(uidRec));
+ expectedBlockState, mAms.mProcessList.getBlockStateForUid(uidRec));
// Background to background
uidRec.setProcState = PROCESS_STATE_CACHED_ACTIVITY;
uidRec.setCurProcState(PROCESS_STATE_CACHED_EMPTY);
expectedBlockState = NETWORK_STATE_NO_CHANGE;
assertEquals(errorMsg.apply(expectedBlockState),
- expectedBlockState, mAms.getBlockStateForUid(uidRec));
+ expectedBlockState, mAms.mProcessList.getBlockStateForUid(uidRec));
// Background to background
uidRec.setProcState = PROCESS_STATE_NONEXISTENT;
uidRec.setCurProcState(PROCESS_STATE_CACHED_ACTIVITY);
expectedBlockState = NETWORK_STATE_NO_CHANGE;
assertEquals(errorMsg.apply(expectedBlockState),
- expectedBlockState, mAms.getBlockStateForUid(uidRec));
+ expectedBlockState, mAms.mProcessList.getBlockStateForUid(uidRec));
// Background to foreground
uidRec.setProcState = PROCESS_STATE_SERVICE;
uidRec.setCurProcState(PROCESS_STATE_FOREGROUND_SERVICE);
expectedBlockState = NETWORK_STATE_BLOCK;
assertEquals(errorMsg.apply(expectedBlockState),
- expectedBlockState, mAms.getBlockStateForUid(uidRec));
+ expectedBlockState, mAms.mProcessList.getBlockStateForUid(uidRec));
// Foreground to background
uidRec.setProcState = PROCESS_STATE_TOP;
uidRec.setCurProcState(PROCESS_STATE_LAST_ACTIVITY);
expectedBlockState = NETWORK_STATE_UNBLOCK;
assertEquals(errorMsg.apply(expectedBlockState),
- expectedBlockState, mAms.getBlockStateForUid(uidRec));
+ expectedBlockState, mAms.mProcessList.getBlockStateForUid(uidRec));
}
/**
@@ -473,8 +473,8 @@ public class ActivityManagerServiceTest {
*/
@Test
public void testDispatchUids_dispatchNeededChanges() throws RemoteException {
- when(mAppOpsService.noteOperation(AppOpsManager.OP_GET_USAGE_STATS, Process.myUid(), null))
- .thenReturn(AppOpsManager.MODE_ALLOWED);
+ when(mAppOpsService.noteOperation(AppOpsManager.OP_GET_USAGE_STATS, Process.myUid(), null,
+ null)).thenReturn(AppOpsManager.MODE_ALLOWED);
final int[] changesToObserve = {
ActivityManager.UID_OBSERVER_PROCSTATE,
diff --git a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
index 9e3b54d1ca96..c3a1243f75af 100644
--- a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
@@ -16,10 +16,8 @@
package com.android.server.am;
-import static com.android.server.am.MemoryStatUtil.BYTES_IN_KILOBYTE;
-import static com.android.server.am.MemoryStatUtil.JIFFY_NANOS;
import static com.android.server.am.MemoryStatUtil.MemoryStat;
-import static com.android.server.am.MemoryStatUtil.parseCmdlineFromProcfs;
+import static com.android.server.am.MemoryStatUtil.PAGE_SIZE;
import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromMemcg;
import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromProcfs;
@@ -30,7 +28,6 @@ import androidx.test.filters.SmallTest;
import org.junit.Test;
-import java.io.ByteArrayOutputStream;
import java.util.Collections;
/**
@@ -99,7 +96,7 @@ public class MemoryStatUtilTest {
"0",
"2222", // this in start time (in ticks per second)
"1257177088",
- "3",
+ "3", // this is RSS in pages
"4294967295",
"2936971264",
"2936991289",
@@ -129,53 +126,6 @@ public class MemoryStatUtilTest {
"3198889956",
"0");
- private static final String PROC_STATUS_CONTENTS = "Name:\tandroid.youtube\n"
- + "State:\tS (sleeping)\n"
- + "Tgid:\t12088\n"
- + "Pid:\t12088\n"
- + "PPid:\t723\n"
- + "TracerPid:\t0\n"
- + "Uid:\t10083\t10083\t10083\t10083\n"
- + "Gid:\t10083\t10083\t10083\t10083\n"
- + "Ngid:\t0\n"
- + "FDSize:\t128\n"
- + "Groups:\t3003 9997 20083 50083 \n"
- + "VmPeak:\t 4546844 kB\n"
- + "VmSize:\t 4542636 kB\n"
- + "VmLck:\t 0 kB\n"
- + "VmPin:\t 0 kB\n"
- + "VmHWM:\t 137668 kB\n" // RSS high-water mark
- + "VmRSS:\t 126776 kB\n" // RSS
- + "RssAnon:\t 37860 kB\n"
- + "RssFile:\t 88764 kB\n"
- + "RssShmem:\t 152 kB\n"
- + "VmData:\t 4125112 kB\n"
- + "VmStk:\t 8192 kB\n"
- + "VmExe:\t 24 kB\n"
- + "VmLib:\t 102432 kB\n"
- + "VmPTE:\t 1300 kB\n"
- + "VmPMD:\t 36 kB\n"
- + "VmSwap:\t 22 kB\n" // Swap
- + "Threads:\t95\n"
- + "SigQ:\t0/13641\n"
- + "SigPnd:\t0000000000000000\n"
- + "ShdPnd:\t0000000000000000\n"
- + "SigBlk:\t0000000000001204\n"
- + "SigIgn:\t0000000000000001\n"
- + "SigCgt:\t00000006400084f8\n"
- + "CapInh:\t0000000000000000\n"
- + "CapPrm:\t0000000000000000\n"
- + "CapEff:\t0000000000000000\n"
- + "CapBnd:\t0000000000000000\n"
- + "CapAmb:\t0000000000000000\n"
- + "Seccomp:\t2\n"
- + "Cpus_allowed:\tff\n"
- + "Cpus_allowed_list:\t0-7\n"
- + "Mems_allowed:\t1\n"
- + "Mems_allowed_list:\t0\n"
- + "voluntary_ctxt_switches:\t903\n"
- + "nonvoluntary_ctxt_switches:\t104\n";
-
@Test
public void testParseMemoryStatFromMemcg_parsesCorrectValues() {
MemoryStat stat = parseMemoryStatFromMemcg(MEMORY_STAT_CONTENTS);
@@ -197,71 +147,26 @@ public class MemoryStatUtilTest {
@Test
public void testParseMemoryStatFromProcfs_parsesCorrectValues() {
- MemoryStat stat = parseMemoryStatFromProcfs(PROC_STAT_CONTENTS, PROC_STATUS_CONTENTS);
+ MemoryStat stat = parseMemoryStatFromProcfs(PROC_STAT_CONTENTS);
assertEquals(1, stat.pgfault);
assertEquals(2, stat.pgmajfault);
- assertEquals(126776 * BYTES_IN_KILOBYTE, stat.rssInBytes);
+ assertEquals(3 * PAGE_SIZE, stat.rssInBytes);
assertEquals(0, stat.cacheInBytes);
- assertEquals(22 * BYTES_IN_KILOBYTE, stat.swapInBytes);
- assertEquals(2222 * JIFFY_NANOS, stat.startTimeNanos);
- assertEquals(37860 * BYTES_IN_KILOBYTE, stat.anonRssInBytes);
+ assertEquals(0, stat.swapInBytes);
}
@Test
public void testParseMemoryStatFromProcfs_emptyContents() {
- MemoryStat stat = parseMemoryStatFromProcfs("", PROC_STATUS_CONTENTS);
- assertNull(stat);
-
- stat = parseMemoryStatFromProcfs(null, PROC_STATUS_CONTENTS);
+ MemoryStat stat = parseMemoryStatFromProcfs("");
assertNull(stat);
- stat = parseMemoryStatFromProcfs(PROC_STAT_CONTENTS, "");
- assertNull(stat);
-
- stat = parseMemoryStatFromProcfs(PROC_STAT_CONTENTS, null);
+ stat = parseMemoryStatFromProcfs(null);
assertNull(stat);
}
@Test
public void testParseMemoryStatFromProcfs_invalidValue() {
String contents = String.join(" ", Collections.nCopies(24, "memory"));
- assertNull(parseMemoryStatFromProcfs(contents, PROC_STATUS_CONTENTS));
- }
-
- @Test
- public void testParseCmdlineFromProcfs_invalidValue() {
- byte[] nothing = new byte[] {0x00, 0x74, 0x65, 0x73, 0x74}; // \0test
-
- assertEquals("", parseCmdlineFromProcfs(bytesToString(nothing)));
- }
-
- @Test
- public void testParseCmdlineFromProcfs_correctValue_noNullBytes() {
- assertEquals("com.google.app", parseCmdlineFromProcfs("com.google.app"));
- }
-
- @Test
- public void testParseCmdlineFromProcfs_correctValue_withNullBytes() {
- byte[] trailing = new byte[] {0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00}; // test\0\0\0
-
- assertEquals("test", parseCmdlineFromProcfs(bytesToString(trailing)));
-
- // test\0\0test
- byte[] inside = new byte[] {0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74};
-
- assertEquals("test", parseCmdlineFromProcfs(bytesToString(trailing)));
- }
-
- @Test
- public void testParseCmdlineFromProcfs_emptyContents() {
- assertEquals("", parseCmdlineFromProcfs(""));
-
- assertEquals("", parseCmdlineFromProcfs(null));
- }
-
- private static String bytesToString(byte[] bytes) {
- ByteArrayOutputStream output = new ByteArrayOutputStream();
- output.write(bytes, 0, bytes.length);
- return output.toString();
+ assertNull(parseMemoryStatFromProcfs(contents));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index d4b7e7e913f6..79cc3db90fff 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -25,8 +25,8 @@ import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG;
import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG;
import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG;
import static com.android.server.am.UserController.REPORT_USER_SWITCH_MSG;
-import static com.android.server.am.UserController.SYSTEM_USER_CURRENT_MSG;
-import static com.android.server.am.UserController.SYSTEM_USER_START_MSG;
+import static com.android.server.am.UserController.USER_CURRENT_MSG;
+import static com.android.server.am.UserController.USER_START_MSG;
import static com.android.server.am.UserController.USER_SWITCH_TIMEOUT_MSG;
import static com.google.android.collect.Lists.newArrayList;
@@ -53,11 +53,13 @@ import static org.mockito.Mockito.validateMockitoUsage;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.UserIdInt;
import android.app.IUserSwitchObserver;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.pm.UserInfo;
+import android.content.pm.UserInfo.UserInfoFlag;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -106,6 +108,10 @@ public class UserControllerTest {
private static final int TEST_USER_ID1 = 101;
private static final int TEST_USER_ID2 = 102;
private static final int NONEXIST_USER_ID = 2;
+ private static final int TEST_PRE_CREATED_USER_ID = 103;
+
+ private static final int NO_USERINFO_FLAGS = 0;
+
private static final String TAG = UserControllerTest.class.getSimpleName();
private static final long HANDLER_WAIT_TIME_MS = 100;
@@ -127,11 +133,11 @@ public class UserControllerTest {
private static final Set<Integer> START_FOREGROUND_USER_MESSAGE_CODES = newHashSet(
REPORT_USER_SWITCH_MSG,
USER_SWITCH_TIMEOUT_MSG,
- SYSTEM_USER_START_MSG,
- SYSTEM_USER_CURRENT_MSG);
+ USER_START_MSG,
+ USER_CURRENT_MSG);
private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES = newHashSet(
- SYSTEM_USER_START_MSG,
+ USER_START_MSG,
REPORT_LOCKED_BOOT_COMPLETE_MSG);
@Before
@@ -148,7 +154,8 @@ public class UserControllerTest {
doNothing().when(mInjector).clearBroadcastQueueForUser(anyInt());
doNothing().when(mInjector).stackSupervisorRemoveUser(anyInt());
mUserController = new UserController(mInjector);
- setUpUser(TEST_USER_ID, 0);
+ setUpUser(TEST_USER_ID, NO_USERINFO_FLAGS);
+ setUpUser(TEST_PRE_CREATED_USER_ID, NO_USERINFO_FLAGS, /* preCreated=*/ true);
});
}
@@ -188,6 +195,31 @@ public class UserControllerTest {
startForegroundUserAssertions();
}
+ @Test
+ public void testStartPreCreatedUser_foreground() {
+ assertFalse(mUserController.startUser(TEST_PRE_CREATED_USER_ID, /* foreground= */ true));
+ }
+
+ @Test
+ public void testStartPreCreatedUser_background() throws Exception {
+ assertTrue(mUserController.startUser(TEST_PRE_CREATED_USER_ID, /* foreground= */ false));
+
+ verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
+ verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
+ verify(mInjector, never()).clearAllLockedTasks(anyString());
+
+ assertWithMessage("should not have received intents")
+ .that(getActions(mInjector.mSentIntents)).isEmpty();
+ // TODO(b/140868593): should have received a USER_UNLOCK_MSG message as well, but it doesn't
+ // because StorageManager.isUserKeyUnlocked(TEST_PRE_CREATED_USER_ID) returns false - to
+ // properly fix it, we'd need to move this class to FrameworksMockingServicesTests so we can
+ // mock static methods (but moving this class would involve changing the presubmit tests,
+ // and the cascade effect goes on...). In fact, a better approach would to not assert the
+ // binder calls, but their side effects (in this case, that the user is stopped right away)
+ assertWithMessage("wrong binder message calls").that(mInjector.mHandler.getMessageCodes())
+ .containsExactly(USER_START_MSG);
+ }
+
private void startUserAssertions(
List<String> expectedActions, Set<Integer> expectedMessageCodes) {
assertEquals(expectedActions, getActions(mInjector.mSentIntents));
@@ -467,9 +499,15 @@ public class UserControllerTest {
continueUserSwitchAssertions(newUserId, expectOldUserStopping);
}
- private void setUpUser(int userId, int flags) {
+ private void setUpUser(@UserIdInt int userId, @UserInfoFlag int flags) {
+ setUpUser(userId, flags, /* preCreated= */ false);
+ }
+
+ private void setUpUser(@UserIdInt int userId, @UserInfoFlag int flags, boolean preCreated) {
UserInfo userInfo = new UserInfo(userId, "User" + userId, flags);
+ userInfo.preCreated = preCreated;
when(mInjector.mUserManagerMock.getUserInfo(eq(userId))).thenReturn(userInfo);
+ when(mInjector.mUserManagerMock.isPreCreated(userId)).thenReturn(preCreated);
}
private static List<String> getActions(List<Intent> intents) {
diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
index 70650de2a4b7..66d2baba2909 100644
--- a/services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
@@ -89,11 +89,13 @@ public class AppOpsUpgradeTest {
final int defaultModeOp2 = AppOpsManager.opToDefaultMode(op2);
for(int i = 0; i < uidStates.size(); i++) {
final AppOpsService.UidState uidState = uidStates.valueAt(i);
- final int uidMode1 = uidState.hasOpMode(op1) ? uidState.getOpMode(op1) : defaultModeOp1;
- final int uidMode2 = uidState.hasOpMode(op2) ? uidState.getOpMode(op2) : defaultModeOp2;
- assertEquals(uidMode1, uidMode2);
- if (uidMode1 != defaultModeOp1) {
- numberOfNonDefaultOps++;
+ if (uidState.opModes != null) {
+ final int uidMode1 = uidState.opModes.get(op1, defaultModeOp1);
+ final int uidMode2 = uidState.opModes.get(op2, defaultModeOp2);
+ assertEquals(uidMode1, uidMode2);
+ if (uidMode1 != defaultModeOp1) {
+ numberOfNonDefaultOps++;
+ }
}
if (uidState.pkgOps == null) {
continue;
diff --git a/services/tests/servicestests/src/com/android/server/backup/transport/DelegatingTransportTest.java b/services/tests/servicestests/src/com/android/server/backup/transport/DelegatingTransportTest.java
new file mode 100644
index 000000000000..bae11eb86b32
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/transport/DelegatingTransportTest.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.transport;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.backup.RestoreDescription;
+import android.app.backup.RestoreSet;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.backup.IBackupTransport;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class DelegatingTransportTest {
+ @Mock private IBackupTransport mBackupTransport;
+ @Mock private PackageInfo mPackageInfo;
+ @Mock private ParcelFileDescriptor mFd;
+
+ private final String mPackageName = "testpackage";
+ private final RestoreSet mRestoreSet = new RestoreSet();
+ private final int mFlags = 1;
+ private final long mRestoreToken = 10;
+ private final long mSize = 100;
+ private final int mNumBytes = 1000;
+ private DelegatingTransport mDelegatingTransport;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mDelegatingTransport = new DelegatingTransport() {
+ @Override
+ protected IBackupTransport getDelegate() {
+ return mBackupTransport;
+ }
+ };
+ }
+
+ @Test
+ public void testName() throws RemoteException {
+ String exp = "dummy";
+ when(mBackupTransport.name()).thenReturn(exp);
+
+ String ret = mDelegatingTransport.name();
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).name();
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testConfigurationIntent() throws RemoteException {
+ Intent exp = new Intent("dummy");
+ when(mBackupTransport.configurationIntent()).thenReturn(exp);
+
+ Intent ret = mDelegatingTransport.configurationIntent();
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).configurationIntent();
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testCurrentDestinationString() throws RemoteException {
+ String exp = "dummy";
+ when(mBackupTransport.currentDestinationString()).thenReturn(exp);
+
+ String ret = mDelegatingTransport.currentDestinationString();
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).currentDestinationString();
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testDataManagementIntent() throws RemoteException {
+ Intent exp = new Intent("dummy");
+ when(mBackupTransport.dataManagementIntent()).thenReturn(exp);
+
+ Intent ret = mDelegatingTransport.dataManagementIntent();
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).dataManagementIntent();
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testDataManagementIntentLabel() throws RemoteException {
+ String exp = "dummy";
+ when(mBackupTransport.dataManagementIntentLabel()).thenReturn(exp);
+
+ CharSequence ret = mDelegatingTransport.dataManagementIntentLabel();
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).dataManagementIntentLabel();
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testTransportDirName() throws RemoteException {
+ String exp = "dummy";
+ when(mBackupTransport.transportDirName()).thenReturn(exp);
+
+ String ret = mDelegatingTransport.transportDirName();
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).transportDirName();
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testRequestBackupTime() throws RemoteException {
+ long exp = 1000L;
+ when(mBackupTransport.requestBackupTime()).thenReturn(exp);
+
+ long ret = mDelegatingTransport.requestBackupTime();
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).requestBackupTime();
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testInitializeDevice() throws RemoteException {
+ int exp = 1000;
+ when(mBackupTransport.initializeDevice()).thenReturn(exp);
+
+ long ret = mDelegatingTransport.initializeDevice();
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).initializeDevice();
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testPerformBackup() throws RemoteException {
+ int exp = 1000;
+ when(mBackupTransport.performBackup(mPackageInfo, mFd, mFlags)).thenReturn(exp);
+
+ int ret = mDelegatingTransport.performBackup(mPackageInfo, mFd, mFlags);
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).performBackup(mPackageInfo, mFd, mFlags);
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testClearBackupData() throws RemoteException {
+ int exp = 1000;
+ when(mBackupTransport.clearBackupData(mPackageInfo)).thenReturn(exp);
+
+ int ret = mDelegatingTransport.clearBackupData(mPackageInfo);
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).clearBackupData(mPackageInfo);
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testFinishBackup() throws RemoteException {
+ int exp = 1000;
+ when(mBackupTransport.finishBackup()).thenReturn(exp);
+
+ int ret = mDelegatingTransport.finishBackup();
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).finishBackup();
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testGetAvailableRestoreSets() throws RemoteException {
+ RestoreSet[] exp = new RestoreSet[] {mRestoreSet};
+ when(mBackupTransport.getAvailableRestoreSets()).thenReturn(exp);
+
+ RestoreSet[] ret = mDelegatingTransport.getAvailableRestoreSets();
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).getAvailableRestoreSets();
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testGetCurrentRestoreSet() throws RemoteException {
+ long exp = 1000;
+ when(mBackupTransport.getCurrentRestoreSet()).thenReturn(exp);
+
+ long ret = mDelegatingTransport.getCurrentRestoreSet();
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).getCurrentRestoreSet();
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testStartRestore() throws RemoteException {
+ int exp = 1000;
+ PackageInfo[] packageInfos = {mPackageInfo};
+ when(mBackupTransport.startRestore(mRestoreToken, packageInfos)).thenReturn(exp);
+
+ int ret = mDelegatingTransport.startRestore(mRestoreToken, packageInfos);
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).startRestore(mRestoreToken, packageInfos);
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testNextRestorePackage() throws RemoteException {
+ RestoreDescription exp = new RestoreDescription(mPackageName, 1);
+ when(mBackupTransport.nextRestorePackage()).thenReturn(exp);
+
+ RestoreDescription ret = mDelegatingTransport.nextRestorePackage();
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).nextRestorePackage();
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testGetRestoreData() throws RemoteException {
+ int exp = 1000;
+ when(mBackupTransport.getRestoreData(mFd)).thenReturn(exp);
+
+ int ret = mDelegatingTransport.getRestoreData(mFd);
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).getRestoreData(mFd);
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void tesFinishRestore() throws RemoteException {
+ mDelegatingTransport.finishRestore();
+
+ verify(mBackupTransport, times(1)).finishRestore();
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testRequestFullBackupTime() throws RemoteException {
+ long exp = 1000L;
+ when(mBackupTransport.requestFullBackupTime()).thenReturn(exp);
+
+ long ret = mDelegatingTransport.requestFullBackupTime();
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).requestFullBackupTime();
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testPerformFullBackup() throws RemoteException {
+ int exp = 1000;
+ when(mBackupTransport.performFullBackup(mPackageInfo, mFd, mFlags)).thenReturn(exp);
+
+ int ret = mDelegatingTransport.performFullBackup(mPackageInfo, mFd, mFlags);
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).performFullBackup(mPackageInfo, mFd, mFlags);
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testCheckFullBackupSize() throws RemoteException {
+ int exp = 1000;
+ when(mBackupTransport.checkFullBackupSize(mSize)).thenReturn(exp);
+
+ int ret = mDelegatingTransport.checkFullBackupSize(mSize);
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).checkFullBackupSize(mSize);
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testSendBackupData() throws RemoteException {
+ int exp = 1000;
+ when(mBackupTransport.sendBackupData(mNumBytes)).thenReturn(exp);
+
+ int ret = mDelegatingTransport.sendBackupData(mNumBytes);
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).sendBackupData(mNumBytes);
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testCancelFullBackup() throws RemoteException {
+ mDelegatingTransport.cancelFullBackup();
+
+ verify(mBackupTransport, times(1)).cancelFullBackup();
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testIsAppEligibleForBackup() throws RemoteException {
+ boolean exp = true;
+ when(mBackupTransport.isAppEligibleForBackup(mPackageInfo, true)).thenReturn(exp);
+
+ boolean ret = mDelegatingTransport.isAppEligibleForBackup(mPackageInfo, true);
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).isAppEligibleForBackup(mPackageInfo, true);
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testGetBackupQuota() throws RemoteException {
+ long exp = 1000;
+ when(mBackupTransport.getBackupQuota(mPackageName, true)).thenReturn(exp);
+
+ long ret = mDelegatingTransport.getBackupQuota(mPackageName, true);
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).getBackupQuota(mPackageName, true);
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testGetNextFullRestoreDataChunk() throws RemoteException {
+ int exp = 1000;
+ when(mBackupTransport.getNextFullRestoreDataChunk(mFd)).thenReturn(exp);
+
+ int ret = mDelegatingTransport.getNextFullRestoreDataChunk(mFd);
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).getNextFullRestoreDataChunk(mFd);
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testAbortFullRestore() throws RemoteException {
+ int exp = 1000;
+ when(mBackupTransport.abortFullRestore()).thenReturn(exp);
+
+ int ret = mDelegatingTransport.abortFullRestore();
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).abortFullRestore();
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+
+ @Test
+ public void testGetTransportFlags() throws RemoteException {
+ int exp = 1000;
+ when(mBackupTransport.getTransportFlags()).thenReturn(exp);
+
+ int ret = mDelegatingTransport.getTransportFlags();
+
+ assertEquals(exp, ret);
+ verify(mBackupTransport, times(1)).getTransportFlags();
+ verifyNoMoreInteractions(mBackupTransport);
+ }
+}
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 ccf3a908364a..ec47a959de30 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -33,28 +33,29 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.hardware.biometrics.Authenticator;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricServiceReceiverInternal;
-import android.hardware.face.FaceManager;
-import android.hardware.face.IFaceService;
import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IFingerprintService;
import android.os.Binder;
import android.os.Bundle;
-import android.os.Handler;
import android.os.IBinder;
+import android.os.RemoteException;
import android.security.KeyStore;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
import com.android.internal.R;
import com.android.internal.statusbar.IStatusBarService;
@@ -64,11 +65,6 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.List;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-
@SmallTest
public class BiometricServiceTest {
@@ -82,6 +78,7 @@ public class BiometricServiceTest {
private static final String ERROR_CANCELED = "error_canceled";
private static final String ERROR_UNABLE_TO_PROCESS = "error_unable_to_process";
private static final String ERROR_USER_CANCELED = "error_user_canceled";
+ private static final String ERROR_LOCKOUT = "error_lockout";
private static final String FINGERPRINT_ACQUIRED_SENSOR_DIRTY = "sensor_dirty";
@@ -96,71 +93,33 @@ public class BiometricServiceTest {
@Mock
private PackageManager mPackageManager;
@Mock
- private AppOpsManager mAppOpsManager;
- @Mock
IBiometricServiceReceiver mReceiver1;
@Mock
IBiometricServiceReceiver mReceiver2;
@Mock
- FingerprintManager mFingerprintManager;
+ BiometricService.Injector mInjector;
@Mock
- FaceManager mFaceManager;
-
- private static class MockInjector extends BiometricService.Injector {
- @Override
- IActivityManager getActivityManagerService() {
- return mock(IActivityManager.class);
- }
-
- @Override
- IStatusBarService getStatusBarService() {
- return mock(IStatusBarService.class);
- }
-
- @Override
- IFingerprintService getFingerprintService() {
- return mock(IFingerprintService.class);
- }
-
- @Override
- IFaceService getFaceService() {
- return mock(IFaceService.class);
- }
-
- @Override
- BiometricService.SettingObserver getSettingObserver(Context context, Handler handler,
- List<BiometricService.EnabledOnKeyguardCallback> callbacks) {
- return mock(BiometricService.SettingObserver.class);
- }
-
- @Override
- KeyStore getKeyStore() {
- return mock(KeyStore.class);
- }
-
- @Override
- boolean isDebugEnabled(Context context, int userId) {
- return false;
- }
-
- @Override
- void publishBinderService(BiometricService service, IBiometricService.Stub impl) {
- // no-op for test
- }
- }
+ IBiometricAuthenticator mFingerprintAuthenticator;
+ @Mock
+ IBiometricAuthenticator mFaceAuthenticator;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
- when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
- when(mContext.getSystemService(Context.FINGERPRINT_SERVICE))
- .thenReturn(mFingerprintManager);
- when(mContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager);
when(mContext.getContentResolver()).thenReturn(mContentResolver);
when(mContext.getResources()).thenReturn(mResources);
+ when(mInjector.getActivityManagerService()).thenReturn(mock(IActivityManager.class));
+ when(mInjector.getStatusBarService()).thenReturn(mock(IStatusBarService.class));
+ when(mInjector.getFingerprintAuthenticator()).thenReturn(mFingerprintAuthenticator);
+ when(mInjector.getFaceAuthenticator()).thenReturn(mFaceAuthenticator);
+ when(mInjector.getSettingObserver(any(), any(), any())).thenReturn(
+ mock(BiometricService.SettingObserver.class));
+ when(mInjector.getKeyStore()).thenReturn(mock(KeyStore.class));
+ when(mInjector.isDebugEnabled(any(), anyInt())).thenReturn(false);
+
when(mResources.getString(R.string.biometric_error_hw_unavailable))
.thenReturn(ERROR_HW_UNAVAILABLE);
when(mResources.getString(R.string.biometric_not_recognized))
@@ -170,76 +129,91 @@ public class BiometricServiceTest {
}
@Test
- public void testAuthenticate_withoutHardware_returnsErrorHardwareNotPresent() throws Exception {
+ public void testAuthenticate_withoutHardware_returnsErrorHardwareNotPresent() throws
+ Exception {
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
.thenReturn(false);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IRIS)).thenReturn(false);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(false);
- mBiometricService = new BiometricService(mContext, new MockInjector());
+ mBiometricService = new BiometricService(mContext, mInjector);
mBiometricService.onStart();
- invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
+ false /* allowDeviceCredential */);
waitForIdle();
verify(mReceiver1).onError(
- eq(BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT), eq(ERROR_HW_UNAVAILABLE));
+ eq(BiometricAuthenticator.TYPE_NONE),
+ eq(BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT),
+ eq(0 /* vendorCode */));
}
@Test
public void testAuthenticate_withoutEnrolled_returnsErrorNoBiometrics() throws Exception {
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
- when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true);
- mBiometricService = new BiometricService(mContext, new MockInjector());
+ mBiometricService = new BiometricService(mContext, mInjector);
mBiometricService.onStart();
- invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
+ false /* allowDeviceCredential */);
waitForIdle();
verify(mReceiver1).onError(
- eq(BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS), any());
+ eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+ eq(BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS),
+ eq(0 /* vendorCode */));
}
@Test
- public void testAuthenticate_whenHalIsDead_returnsErrorHardwareUnavailable() throws Exception {
+ public void testAuthenticate_whenHalIsDead_returnsErrorHardwareUnavailable() throws
+ Exception {
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
- when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
- when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+ when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
+ when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(false);
- mBiometricService = new BiometricService(mContext, new MockInjector());
+ mBiometricService = new BiometricService(mContext, mInjector);
mBiometricService.onStart();
- invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
+ false /* allowDeviceCredential */);
waitForIdle();
verify(mReceiver1).onError(
- eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE), eq(ERROR_HW_UNAVAILABLE));
+ eq(BiometricAuthenticator.TYPE_NONE),
+ eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE),
+ eq(0 /* vendorCode */));
}
@Test
public void testAuthenticateFace_respectsUserSetting()
throws Exception {
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
- when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
- when(mFaceManager.isHardwareDetected()).thenReturn(true);
+ when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
+ when(mFaceAuthenticator.isHardwareDetected(any())).thenReturn(true);
- mBiometricService = new BiometricService(mContext, new MockInjector());
+ mBiometricService = new BiometricService(mContext, mInjector);
mBiometricService.onStart();
// Disabled in user settings receives onError
when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(false);
- invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
+ false /* allowDeviceCredential */);
waitForIdle();
verify(mReceiver1).onError(
- eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE), eq(ERROR_HW_UNAVAILABLE));
+ eq(BiometricAuthenticator.TYPE_NONE),
+ eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE),
+ eq(0 /* vendorCode */));
// Enrolled, not disabled in settings, user requires confirmation in settings
resetReceiver();
when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true);
when(mBiometricService.mSettingObserver.getFaceAlwaysRequireConfirmation(anyInt()))
.thenReturn(true);
- invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
+ false /* allowDeviceCredential */);
waitForIdle();
- verify(mReceiver1, never()).onError(anyInt(), any(String.class));
- verify(mBiometricService.mFaceService).prepareForAuthentication(
+ verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
+ verify(mBiometricService.mAuthenticators.get(0).impl).prepareForAuthentication(
eq(true) /* requireConfirmation */,
any(IBinder.class),
anyLong() /* sessionId */,
@@ -255,9 +229,10 @@ public class BiometricServiceTest {
resetReceiver();
when(mBiometricService.mSettingObserver.getFaceAlwaysRequireConfirmation(anyInt()))
.thenReturn(false);
- invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
+ false /* allowDeviceCredential */);
waitForIdle();
- verify(mBiometricService.mFaceService).prepareForAuthentication(
+ verify(mBiometricService.mAuthenticators.get(0).impl).prepareForAuthentication(
eq(false) /* requireConfirmation */,
any(IBinder.class),
anyLong() /* sessionId */,
@@ -273,11 +248,12 @@ public class BiometricServiceTest {
@Test
public void testAuthenticate_happyPathWithoutConfirmation() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
- mBiometricService = new BiometricService(mContext, new MockInjector());
+ mBiometricService = new BiometricService(mContext, mInjector);
mBiometricService.onStart();
// Start testing the happy path
- invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
+ false /* allowDeviceCredential */);
waitForIdle();
// Creates a pending auth session with the correct initial states
@@ -286,8 +262,9 @@ public class BiometricServiceTest {
// Invokes <Modality>Service#prepareForAuthentication
ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
- verify(mReceiver1, never()).onError(anyInt(), any(String.class));
- verify(mBiometricService.mFingerprintService).prepareForAuthentication(
+ verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
+ verify(mBiometricService.mAuthenticators.get(0).impl).prepareForAuthentication(
+ anyBoolean() /* requireConfirmation */,
any(IBinder.class),
anyLong() /* sessionId */,
anyInt() /* userId */,
@@ -307,11 +284,11 @@ public class BiometricServiceTest {
BiometricService.STATE_AUTH_STARTED);
// startPreparedClient invoked
- verify(mBiometricService.mFingerprintService)
+ verify(mBiometricService.mAuthenticators.get(0).impl)
.startPreparedClient(cookieCaptor.getValue());
// StatusBar showBiometricDialog invoked
- verify(mBiometricService.mStatusBarService).showBiometricDialog(
+ verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
eq(mBiometricService.mCurrentAuthSession.mBundle),
any(IBiometricServiceReceiverInternal.class),
eq(BiometricAuthenticator.TYPE_FINGERPRINT),
@@ -328,12 +305,11 @@ public class BiometricServiceTest {
assertEquals(mBiometricService.mCurrentAuthSession.mState,
BiometricService.STATE_AUTHENTICATED_PENDING_SYSUI);
// Notify SystemUI hardware authenticated
- verify(mBiometricService.mStatusBarService).onBiometricAuthenticated(
- eq(true) /* authenticated */, eq(null) /* failureReason */);
+ verify(mBiometricService.mStatusBarService).onBiometricAuthenticated();
// SystemUI sends callback with dismissed reason
mBiometricService.mInternalReceiver.onDialogDismissed(
- BiometricPrompt.DISMISSED_REASON_CONFIRM_NOT_REQUIRED);
+ BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED);
waitForIdle();
// HAT sent to keystore
verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
@@ -344,10 +320,32 @@ public class BiometricServiceTest {
}
@Test
+ public void testAuthenticate_noBiometrics_credentialAllowed() throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+ when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(false);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
+ true /* requireConfirmation */, true /* allowDeviceCredential */);
+ waitForIdle();
+
+ assertEquals(BiometricService.STATE_SHOWING_DEVICE_CREDENTIAL,
+ mBiometricService.mCurrentAuthSession.mState);
+ assertEquals(Authenticator.TYPE_CREDENTIAL,
+ mBiometricService.mCurrentAuthSession.mBundle
+ .getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
+ verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
+ eq(mBiometricService.mCurrentAuthSession.mBundle),
+ any(IBiometricServiceReceiverInternal.class),
+ eq(0 /* biometricModality */),
+ anyBoolean() /* requireConfirmation */,
+ anyInt() /* userId */,
+ eq(TEST_PACKAGE_NAME));
+ }
+
+ @Test
public void testAuthenticate_happyPathWithConfirmation() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- true /* requireConfirmation */);
+ true /* requireConfirmation */, false /* allowDeviceCredential */);
// Test authentication succeeded goes to PENDING_CONFIRMATION and that the HAT is not
// sent to KeyStore yet
@@ -362,7 +360,7 @@ public class BiometricServiceTest {
// SystemUI sends confirm, HAT is sent to keystore and client is notified.
mBiometricService.mInternalReceiver.onDialogDismissed(
- BiometricPrompt.DISMISSED_REASON_CONFIRMED);
+ BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED);
waitForIdle();
verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
verify(mReceiver1).onAuthenticationSucceeded();
@@ -373,13 +371,15 @@ public class BiometricServiceTest {
throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */);
+ false /* requireConfirmation */, false /* allowDeviceCredential */);
mBiometricService.mInternalReceiver.onAuthenticationFailed();
waitForIdle();
- verify(mBiometricService.mStatusBarService)
- .onBiometricAuthenticated(eq(false), eq(ERROR_NOT_RECOGNIZED));
+ verify(mBiometricService.mStatusBarService).onBiometricError(
+ eq(BiometricAuthenticator.TYPE_NONE),
+ eq(BiometricConstants.BIOMETRIC_PAUSED_REJECTED),
+ eq(0 /* vendorCode */));
verify(mReceiver1).onAuthenticationFailed();
assertEquals(mBiometricService.mCurrentAuthSession.mState,
BiometricService.STATE_AUTH_PAUSED);
@@ -390,55 +390,60 @@ public class BiometricServiceTest {
throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */);
+ false /* requireConfirmation */, false /* allowDeviceCredential */);
mBiometricService.mInternalReceiver.onAuthenticationFailed();
waitForIdle();
- verify(mBiometricService.mStatusBarService)
- .onBiometricAuthenticated(eq(false), eq(ERROR_NOT_RECOGNIZED));
+ verify(mBiometricService.mStatusBarService).onBiometricError(
+ eq(BiometricAuthenticator.TYPE_NONE),
+ eq(BiometricConstants.BIOMETRIC_PAUSED_REJECTED),
+ eq(0 /* vendorCode */));
verify(mReceiver1).onAuthenticationFailed();
assertEquals(mBiometricService.mCurrentAuthSession.mState,
BiometricService.STATE_AUTH_STARTED);
}
@Test
- public void testErrorCanceled_whenAuthenticating_notifiesSystemUIAndClient() throws Exception {
+ public void testErrorCanceled_whenAuthenticating_notifiesSystemUIAndClient() throws
+ Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */);
+ false /* requireConfirmation */, false /* allowDeviceCredential */);
// Create a new pending auth session but don't start it yet. HAL contract is that previous
// one must get ERROR_CANCELED. Simulate that here by creating the pending auth session,
// sending ERROR_CANCELED to the current auth session, and then having the second one
// onReadyForAuthentication.
- invokeAuthenticate(mBiometricService.mImpl, mReceiver2, false /* requireConfirmation */);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver2, false /* requireConfirmation */,
+ false /* allowDeviceCredential */);
waitForIdle();
assertEquals(mBiometricService.mCurrentAuthSession.mState,
BiometricService.STATE_AUTH_STARTED);
mBiometricService.mInternalReceiver.onError(
getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
- BiometricConstants.BIOMETRIC_ERROR_CANCELED, ERROR_CANCELED);
+ BiometricAuthenticator.TYPE_FINGERPRINT,
+ BiometricConstants.BIOMETRIC_ERROR_CANCELED, 0 /* vendorCode */);
waitForIdle();
// Auth session doesn't become null until SystemUI responds that the animation is completed
assertNotNull(mBiometricService.mCurrentAuthSession);
// ERROR_CANCELED is not sent until SystemUI responded that animation is completed
- verify(mReceiver1, never()).onError(
- anyInt(), anyString());
- verify(mReceiver2, never()).onError(anyInt(), any(String.class));
+ verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
+ verify(mReceiver2, never()).onError(anyInt(), anyInt(), anyInt());
// SystemUI dialog closed
- verify(mBiometricService.mStatusBarService).hideBiometricDialog();
+ verify(mBiometricService.mStatusBarService).hideAuthenticationDialog();
// After SystemUI notifies that the animation has completed
mBiometricService.mInternalReceiver
.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED);
waitForIdle();
verify(mReceiver1).onError(
+ eq(BiometricAuthenticator.TYPE_FINGERPRINT),
eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED),
- eq(ERROR_CANCELED));
+ eq(0 /* vendorCode */));
assertNull(mBiometricService.mCurrentAuthSession);
}
@@ -446,18 +451,21 @@ public class BiometricServiceTest {
public void testErrorHalTimeout_whenAuthenticating_entersPausedState() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */);
+ false /* requireConfirmation */, false /* allowDeviceCredential */);
mBiometricService.mInternalReceiver.onError(
getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ BiometricAuthenticator.TYPE_FACE,
BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
- ERROR_TIMEOUT);
+ 0 /* vendorCode */);
waitForIdle();
assertEquals(mBiometricService.mCurrentAuthSession.mState,
BiometricService.STATE_AUTH_PAUSED);
- verify(mBiometricService.mStatusBarService)
- .onBiometricAuthenticated(eq(false), eq(ERROR_TIMEOUT));
+ verify(mBiometricService.mStatusBarService).onBiometricError(
+ eq(BiometricAuthenticator.TYPE_FACE),
+ eq(BiometricConstants.BIOMETRIC_ERROR_TIMEOUT),
+ eq(0 /* vendorCode */));
// Timeout does not count as fail as per BiometricPrompt documentation.
verify(mReceiver1, never()).onAuthenticationFailed();
@@ -479,7 +487,7 @@ public class BiometricServiceTest {
resetStatusBar();
startPendingAuthSession(mBiometricService);
waitForIdle();
- verify(mBiometricService.mStatusBarService, never()).showBiometricDialog(
+ verify(mBiometricService.mStatusBarService, never()).showAuthenticationDialog(
any(Bundle.class),
any(IBiometricServiceReceiverInternal.class),
anyInt(),
@@ -492,24 +500,27 @@ public class BiometricServiceTest {
public void testErrorFromHal_whenPaused_notifiesSystemUIAndClient() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireCOnfirmation */);
+ false /* requireConfirmation */, false /* allowDeviceCredential */);
mBiometricService.mInternalReceiver.onError(
getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ BiometricAuthenticator.TYPE_FACE,
BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
- ERROR_TIMEOUT);
+ 0 /* vendorCode */);
mBiometricService.mInternalReceiver.onError(
getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ BiometricAuthenticator.TYPE_FACE,
BiometricConstants.BIOMETRIC_ERROR_CANCELED,
- ERROR_CANCELED);
+ 0 /* vendorCode */);
waitForIdle();
// Client receives error immediately
verify(mReceiver1).onError(
+ eq(BiometricAuthenticator.TYPE_FACE),
eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED),
- eq(ERROR_CANCELED));
+ eq(0 /* vendorCode */));
// Dialog is hidden immediately
- verify(mBiometricService.mStatusBarService).hideBiometricDialog();
+ verify(mBiometricService.mStatusBarService).hideAuthenticationDialog();
// Auth session is over
assertNull(mBiometricService.mCurrentAuthSession);
}
@@ -522,46 +533,224 @@ public class BiometricServiceTest {
// session is done.
setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */);
+ false /* requireConfirmation */, false /* allowDeviceCredential */);
mBiometricService.mInternalReceiver.onError(
getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ BiometricAuthenticator.TYPE_FINGERPRINT,
BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS,
- ERROR_UNABLE_TO_PROCESS);
+ 0 /* vendorCode */);
waitForIdle();
// Sends error to SystemUI and does not notify client yet
assertEquals(mBiometricService.mCurrentAuthSession.mState,
BiometricService.STATE_ERROR_PENDING_SYSUI);
- verify(mBiometricService.mStatusBarService)
- .onBiometricError(eq(ERROR_UNABLE_TO_PROCESS));
- verify(mBiometricService.mStatusBarService, never()).hideBiometricDialog();
- verify(mReceiver1, never()).onError(anyInt(), anyString());
+ verify(mBiometricService.mStatusBarService).onBiometricError(
+ eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+ eq(BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS),
+ eq(0 /* vendorCode */));
+ verify(mBiometricService.mStatusBarService, never()).hideAuthenticationDialog();
+ verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
// SystemUI animation completed, client is notified, auth session is over
mBiometricService.mInternalReceiver
.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_ERROR);
waitForIdle();
verify(mReceiver1).onError(
+ eq(BiometricAuthenticator.TYPE_FINGERPRINT),
eq(BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS),
- eq(ERROR_UNABLE_TO_PROCESS));
+ eq(0 /* vendorCode */));
assertNull(mBiometricService.mCurrentAuthSession);
}
@Test
+ public void testErrorFromHal_whilePreparingAuthentication_credentialAllowed() throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */, true /* allowDeviceCredential */);
+ waitForIdle();
+
+ mBiometricService.mInternalReceiver.onError(
+ getCookieForPendingSession(mBiometricService.mPendingAuthSession),
+ BiometricAuthenticator.TYPE_FACE,
+ BiometricConstants.BIOMETRIC_ERROR_LOCKOUT,
+ 0 /* vendorCode */);
+ waitForIdle();
+
+ // Pending auth session becomes current auth session, since device credential should
+ // be shown now.
+ assertNull(mBiometricService.mPendingAuthSession);
+ assertNotNull(mBiometricService.mCurrentAuthSession);
+ assertEquals(BiometricService.STATE_SHOWING_DEVICE_CREDENTIAL,
+ mBiometricService.mCurrentAuthSession.mState);
+ assertEquals(Authenticator.TYPE_CREDENTIAL,
+ mBiometricService.mCurrentAuthSession.mBundle.getInt(
+ BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
+ verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
+ eq(mBiometricService.mCurrentAuthSession.mBundle),
+ any(IBiometricServiceReceiverInternal.class),
+ eq(0 /* biometricModality */),
+ anyBoolean() /* requireConfirmation */,
+ anyInt() /* userId */,
+ eq(TEST_PACKAGE_NAME));
+ }
+
+ @Test
+ public void testErrorFromHal_whilePreparingAuthentication_credentialNotAllowed()
+ throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */, false /* allowDeviceCredential */);
+ waitForIdle();
+
+ mBiometricService.mInternalReceiver.onError(
+ getCookieForPendingSession(mBiometricService.mPendingAuthSession),
+ BiometricAuthenticator.TYPE_FINGERPRINT,
+ BiometricConstants.BIOMETRIC_ERROR_LOCKOUT,
+ 0 /* vendorCode */);
+ waitForIdle();
+
+ // Error is sent to client
+ assertNull(mBiometricService.mPendingAuthSession);
+ assertNull(mBiometricService.mCurrentAuthSession);
+ }
+
+ @Test
+ public void testCombineAuthenticatorBundle_keyAllowDeviceCredentialAlwaysRemoved() {
+ Bundle bundle;
+ int authenticators;
+
+ // In:
+ // KEY_ALLOW_DEVICE_CREDENTIAL = true
+ // KEY_AUTHENTICATORS_ALLOWED = TYPE_BIOMETRIC | TYPE_CREDENTIAL
+ // Out:
+ // KEY_ALLOW_DEVICE_CREDENTIAL = null
+ // KEY_AUTHENTICATORS_ALLOWED = TYPE_BIOMETRIC | TYPE_CREDENTIAL
+ bundle = new Bundle();
+ bundle.putBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, true);
+ authenticators = Authenticator.TYPE_CREDENTIAL | Authenticator.TYPE_BIOMETRIC;
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
+ Utils.combineAuthenticatorBundles(bundle);
+ assertNull(bundle.get(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL));
+ assertEquals(authenticators, bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
+
+ // In:
+ // KEY_ALLOW_DEVICE_CREDENTIAL = true
+ // KEY_AUTHENTICATORS_ALLOWED = TYPE_BIOMETRIC
+ // Out:
+ // KEY_ALLOW_DEVICE_CREDENTIAL = null
+ // KEY_AUTHENTICATORS_ALLOWED = TYPE_BIOMETRIC | TYPE_CREDENTIAL
+ bundle = new Bundle();
+ bundle.putBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, true);
+ authenticators = Authenticator.TYPE_BIOMETRIC;
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
+ Utils.combineAuthenticatorBundles(bundle);
+ assertNull(bundle.get(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL));
+ assertEquals(authenticators, bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
+
+ // In:
+ // KEY_ALLOW_DEVICE_CREDENTIAL = null
+ // KEY_AUTHENTICATORS_ALLOWED = TYPE_BIOMETRIC | TYPE_CREDENTIAL
+ // Out:
+ // KEY_ALLOW_DEVICE_CREDENTIAL = null
+ // KEY_AUTHENTICATORS_ALLOWED = TYPE_BIOMETRIC | TYPE_CREDENTIAL
+ bundle = new Bundle();
+ authenticators = Authenticator.TYPE_BIOMETRIC | Authenticator.TYPE_CREDENTIAL;
+ bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
+ Utils.combineAuthenticatorBundles(bundle);
+ assertNull(bundle.get(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL));
+ assertEquals(authenticators, bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
+ }
+
+ @Test
+ public void testErrorFromHal_whileShowingDeviceCredential_doesntNotifySystemUI()
+ throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */, true /* allowDeviceCredential */);
+
+ mBiometricService.mInternalReceiver.onDeviceCredentialPressed();
+ waitForIdle();
+
+ assertEquals(BiometricService.STATE_SHOWING_DEVICE_CREDENTIAL,
+ mBiometricService.mCurrentAuthSession.mState);
+ verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
+
+ mBiometricService.mInternalReceiver.onError(
+ getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ BiometricAuthenticator.TYPE_FINGERPRINT,
+ BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+ 0 /* vendorCode */);
+ waitForIdle();
+
+ assertEquals(BiometricService.STATE_SHOWING_DEVICE_CREDENTIAL,
+ mBiometricService.mCurrentAuthSession.mState);
+ verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
+ }
+
+ @Test
+ public void testLockout_whileAuthenticating_credentialAllowed() throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */, true /* allowDeviceCredential */);
+
+ assertEquals(BiometricService.STATE_AUTH_STARTED,
+ mBiometricService.mCurrentAuthSession.mState);
+
+ mBiometricService.mInternalReceiver.onError(
+ getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ BiometricAuthenticator.TYPE_FINGERPRINT,
+ BiometricConstants.BIOMETRIC_ERROR_LOCKOUT,
+ 0 /* vendorCode */);
+ waitForIdle();
+
+ assertEquals(BiometricService.STATE_SHOWING_DEVICE_CREDENTIAL,
+ mBiometricService.mCurrentAuthSession.mState);
+ verify(mBiometricService.mStatusBarService).onBiometricError(
+ eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+ eq(BiometricConstants.BIOMETRIC_ERROR_LOCKOUT),
+ eq(0 /* vendorCode */));
+ }
+
+ @Test
+ public void testLockout_whenAuthenticating_credentialNotAllowed() throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */, false /* allowDeviceCredential */);
+
+ assertEquals(BiometricService.STATE_AUTH_STARTED,
+ mBiometricService.mCurrentAuthSession.mState);
+
+ mBiometricService.mInternalReceiver.onError(
+ getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ BiometricAuthenticator.TYPE_FINGERPRINT,
+ BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS,
+ 0 /* vendorCode */);
+ waitForIdle();
+
+ assertEquals(BiometricService.STATE_ERROR_PENDING_SYSUI,
+ mBiometricService.mCurrentAuthSession.mState);
+ verify(mBiometricService.mStatusBarService).onBiometricError(
+ eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+ eq(BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS),
+ eq(0 /* vendorCode */));
+ }
+
+ @Test
public void testDismissedReasonUserCancel_whileAuthenticating_cancelsHalAuthentication()
throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */);
+ false /* requireConfirmation */, false /* allowDeviceCredential */);
mBiometricService.mInternalReceiver
.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
waitForIdle();
verify(mReceiver1).onError(
+ eq(BiometricAuthenticator.TYPE_FINGERPRINT),
eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED),
- eq(ERROR_USER_CANCELED));
- verify(mBiometricService.mFingerprintService).cancelAuthenticationFromService(
+ eq(0 /* vendorCode */));
+ verify(mBiometricService.mAuthenticators.get(0).impl).cancelAuthenticationFromService(
any(),
any(),
anyInt(),
@@ -575,17 +764,19 @@ public class BiometricServiceTest {
public void testDismissedReasonNegative_whilePaused_doesntInvokeHalCancel() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */);
+ false /* requireConfirmation */, false /* allowDeviceCredential */);
mBiometricService.mInternalReceiver.onError(
getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ BiometricAuthenticator.TYPE_FACE,
BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
- ERROR_TIMEOUT);
+ 0 /* vendorCode */);
mBiometricService.mInternalReceiver.onDialogDismissed(
BiometricPrompt.DISMISSED_REASON_NEGATIVE);
waitForIdle();
- verify(mBiometricService.mFaceService, never()).cancelAuthenticationFromService(
+ verify(mBiometricService.mAuthenticators.get(0).impl,
+ never()).cancelAuthenticationFromService(
any(),
any(),
anyInt(),
@@ -595,20 +786,23 @@ public class BiometricServiceTest {
}
@Test
- public void testDismissedReasonUserCancel_whilePaused_doesntInvokeHalCancel() throws Exception {
+ public void testDismissedReasonUserCancel_whilePaused_doesntInvokeHalCancel() throws
+ Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */);
+ false /* requireConfirmation */, false /* allowDeviceCredential */);
mBiometricService.mInternalReceiver.onError(
getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ BiometricAuthenticator.TYPE_FACE,
BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
- ERROR_TIMEOUT);
+ 0 /* vendorCode */);
mBiometricService.mInternalReceiver.onDialogDismissed(
BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
waitForIdle();
- verify(mBiometricService.mFaceService, never()).cancelAuthenticationFromService(
+ verify(mBiometricService.mAuthenticators.get(0).impl,
+ never()).cancelAuthenticationFromService(
any(),
any(),
anyInt(),
@@ -621,7 +815,7 @@ public class BiometricServiceTest {
public void testDismissedReasonUserCancel_whenPendingConfirmation() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- true /* requireConfirmation */);
+ true /* requireConfirmation */, false /* allowDeviceCredential */);
mBiometricService.mInternalReceiver.onAuthenticationSucceeded(
true /* requireConfirmation */,
@@ -631,7 +825,8 @@ public class BiometricServiceTest {
waitForIdle();
// doesn't send cancel to HAL
- verify(mBiometricService.mFaceService, never()).cancelAuthenticationFromService(
+ verify(mBiometricService.mAuthenticators.get(0).impl,
+ never()).cancelAuthenticationFromService(
any(),
any(),
anyInt(),
@@ -639,8 +834,9 @@ public class BiometricServiceTest {
anyInt(),
anyBoolean());
verify(mReceiver1).onError(
+ eq(BiometricAuthenticator.TYPE_FACE),
eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED),
- eq(ERROR_USER_CANCELED));
+ eq(0 /* vendorCode */));
assertNull(mBiometricService.mCurrentAuthSession);
}
@@ -648,7 +844,7 @@ public class BiometricServiceTest {
public void testAcquire_whenAuthenticating_sentToSystemUI() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
- false /* requireConfirmation */);
+ false /* requireConfirmation */, false /* allowDeviceCredential */);
mBiometricService.mInternalReceiver.onAcquired(
FingerprintManager.FINGERPRINT_ACQUIRED_IMAGER_DIRTY,
@@ -664,7 +860,7 @@ public class BiometricServiceTest {
// Helper methods
- private void setupAuthForOnly(int modality) {
+ private void setupAuthForOnly(int modality) throws RemoteException {
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
.thenReturn(false);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(false);
@@ -672,17 +868,17 @@ public class BiometricServiceTest {
if (modality == BiometricAuthenticator.TYPE_FINGERPRINT) {
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
.thenReturn(true);
- when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
- when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
+ when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true);
} else if (modality == BiometricAuthenticator.TYPE_FACE) {
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
- when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
- when(mFaceManager.isHardwareDetected()).thenReturn(true);
+ when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
+ when(mFaceAuthenticator.isHardwareDetected(any())).thenReturn(true);
} else {
fail("Unknown modality: " + modality);
}
- mBiometricService = new BiometricService(mContext, new MockInjector());
+ mBiometricService = new BiometricService(mContext, mInjector);
mBiometricService.onStart();
when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true);
@@ -698,9 +894,10 @@ public class BiometricServiceTest {
}
private void invokeAuthenticateAndStart(IBiometricService.Stub service,
- IBiometricServiceReceiver receiver, boolean requireConfirmation) throws Exception {
+ IBiometricServiceReceiver receiver, boolean requireConfirmation,
+ boolean allowDeviceCredential) throws Exception {
// Request auth, creates a pending session
- invokeAuthenticate(service, receiver, requireConfirmation);
+ invokeAuthenticate(service, receiver, requireConfirmation, allowDeviceCredential);
waitForIdle();
startPendingAuthSession(mBiometricService);
@@ -720,20 +917,25 @@ public class BiometricServiceTest {
}
private static void invokeAuthenticate(IBiometricService.Stub service,
- IBiometricServiceReceiver receiver, boolean requireConfirmation) throws Exception {
+ IBiometricServiceReceiver receiver, boolean requireConfirmation,
+ boolean allowDeviceCredential) throws Exception {
service.authenticate(
new Binder() /* token */,
0 /* sessionId */,
0 /* userId */,
receiver,
TEST_PACKAGE_NAME /* packageName */,
- createTestBiometricPromptBundle(requireConfirmation),
- null /* IBiometricConfirmDeviceCredentialCallback */);
+ createTestBiometricPromptBundle(requireConfirmation, allowDeviceCredential));
}
- private static Bundle createTestBiometricPromptBundle(boolean requireConfirmation) {
+ private static Bundle createTestBiometricPromptBundle(boolean requireConfirmation,
+ boolean allowDeviceCredential) {
final Bundle bundle = new Bundle();
bundle.putBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, requireConfirmation);
+
+ if (allowDeviceCredential) {
+ bundle.putBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, true);
+ }
return bundle;
}
@@ -742,6 +944,11 @@ public class BiometricServiceTest {
return session.mModalitiesMatched.values().iterator().next();
}
+ private static int getCookieForPendingSession(BiometricService.AuthSession session) {
+ assertEquals(session.mModalitiesWaiting.values().size(), 1);
+ return session.mModalitiesWaiting.values().iterator().next();
+ }
+
private static void waitForIdle() {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
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 d90091017116..aeccfc5310e9 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -25,10 +25,12 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
+import static android.app.admin.PasswordMetrics.computeForPassword;
import static android.os.UserManagerInternal.CAMERA_DISABLED_GLOBALLY;
import static android.os.UserManagerInternal.CAMERA_DISABLED_LOCALLY;
import static android.os.UserManagerInternal.CAMERA_NOT_DISABLED;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
import static com.android.server.testutils.TestUtils.assertExpectException;
@@ -93,7 +95,7 @@ import android.util.Pair;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
-import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.devicepolicy.DevicePolicyManagerService.RestrictionsListener;
@@ -1212,6 +1214,45 @@ public class DevicePolicyManagerTest extends DpmTestBase {
assertTrue(dpm.isDeviceManaged());
}
+ /**
+ * Test for: {@link DevicePolicyManager#clearDeviceOwnerApp(String)}
+ *
+ * Validates that when the device owner is removed, the reset password token is cleared
+ */
+ public void testClearDeviceOwner_clearResetPasswordToken() throws Exception {
+ mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ // Install admin1 on system user
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+
+ // Set admin1 to active admin and device owner
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+ dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM);
+
+ // Add reset password token
+ final long handle = 12000;
+ final byte[] token = new byte[32];
+ when(getServices().lockPatternUtils.addEscrowToken(eq(token), eq(UserHandle.USER_SYSTEM),
+ nullable(EscrowTokenStateChangeCallback.class)))
+ .thenReturn(handle);
+ assertTrue(dpm.setResetPasswordToken(admin1, token));
+
+ // Assert reset password token is active
+ when(getServices().lockPatternUtils.isEscrowTokenActive(eq(handle),
+ eq(UserHandle.USER_SYSTEM)))
+ .thenReturn(true);
+ assertTrue(dpm.isResetPasswordTokenActive(admin1));
+
+ // Remove the device owner
+ dpm.clearDeviceOwnerApp(admin1.getPackageName());
+
+ // Verify password reset password token was removed
+ verify(getServices().lockPatternUtils).removeEscrowToken(eq(handle),
+ eq(UserHandle.USER_SYSTEM));
+ }
+
public void testSetProfileOwner() throws Exception {
setAsProfileOwner(admin1);
@@ -2611,6 +2652,21 @@ public class DevicePolicyManagerTest extends DpmTestBase {
verifyStayOnWhilePluggedCleared(false);
}
+ public void testIsActiveSupervisionApp() throws Exception {
+ when(mServiceContext.resources
+ .getString(R.string.config_defaultSupervisionProfileOwnerComponent))
+ .thenReturn(admin1.flattenToString());
+
+ final int PROFILE_USER = 15;
+ final int PROFILE_ADMIN = UserHandle.getUid(PROFILE_USER, 19436);
+ addManagedProfile(admin1, PROFILE_ADMIN, admin1);
+ mContext.binder.callingUid = PROFILE_ADMIN;
+
+ final DevicePolicyManagerInternal dpmi =
+ LocalServices.getService(DevicePolicyManagerInternal.class);
+ assertTrue(dpmi.isActiveSupervisionApp(PROFILE_ADMIN));
+ }
+
// Test if lock timeout on managed profile is handled correctly depending on whether profile
// uses separate challenge.
public void testSetMaximumTimeToLockProfile() throws Exception {
@@ -4212,9 +4268,9 @@ public class DevicePolicyManagerTest extends DpmTestBase {
assertTrue(dpm.isResetPasswordTokenActive(admin1));
// test reset password with token
- when(getServices().lockPatternUtils.setLockCredentialWithToken(eq(password.getBytes()),
- eq(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD),
- eq(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC), eq(handle), eq(token),
+ when(getServices().lockPatternUtils.setLockCredentialWithToken(
+ eq(LockscreenCredential.createPassword(password)),
+ eq(handle), eq(token),
eq(UserHandle.USER_SYSTEM)))
.thenReturn(true);
assertTrue(dpm.resetPasswordWithToken(admin1, password, token, 0));
@@ -4241,11 +4297,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
reset(mContext.spiedContext);
- PasswordMetrics passwordMetricsNoSymbols = new PasswordMetrics(
- DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, 9,
- 8, 2,
- 6, 1,
- 0, 1);
+ PasswordMetrics passwordMetricsNoSymbols = computeForPassword("abcdXYZ5".getBytes());
setActivePasswordState(passwordMetricsNoSymbols);
assertTrue(dpm.isActivePasswordSufficient());
@@ -4272,11 +4324,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
reset(mContext.spiedContext);
assertFalse(dpm.isActivePasswordSufficient());
- PasswordMetrics passwordMetricsWithSymbols = new PasswordMetrics(
- DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, 9,
- 7, 2,
- 5, 1,
- 1, 2);
+ PasswordMetrics passwordMetricsWithSymbols = computeForPassword("abcd.XY5".getBytes());
setActivePasswordState(passwordMetricsWithSymbols);
assertTrue(dpm.isActivePasswordSufficient());
@@ -4293,7 +4341,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
final int userHandle = UserHandle.getUserId(mContext.binder.callingUid);
// When there is no lockscreen, user password metrics is always empty.
when(getServices().lockSettingsInternal.getUserPasswordMetrics(userHandle))
- .thenReturn(new PasswordMetrics());
+ .thenReturn(new PasswordMetrics(CREDENTIAL_TYPE_NONE));
// If no password requirements are set, isActivePasswordSufficient should succeed.
assertTrue(dpm.isActivePasswordSufficient());
@@ -5260,7 +5308,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
.thenReturn(DpmMockContext.CALLER_USER_HANDLE);
when(getServices().lockSettingsInternal
.getUserPasswordMetrics(DpmMockContext.CALLER_USER_HANDLE))
- .thenReturn(PasswordMetrics.computeForPassword("asdf".getBytes()));
+ .thenReturn(computeForPassword("asdf".getBytes()));
assertEquals(PASSWORD_COMPLEXITY_MEDIUM, dpm.getPasswordComplexity());
}
@@ -5277,10 +5325,10 @@ public class DevicePolicyManagerTest extends DpmTestBase {
when(getServices().lockSettingsInternal
.getUserPasswordMetrics(DpmMockContext.CALLER_USER_HANDLE))
- .thenReturn(PasswordMetrics.computeForPassword("asdf".getBytes()));
+ .thenReturn(computeForPassword("asdf".getBytes()));
when(getServices().lockSettingsInternal
.getUserPasswordMetrics(parentUser.id))
- .thenReturn(PasswordMetrics.computeForPassword("parentUser".getBytes()));
+ .thenReturn(computeForPassword("parentUser".getBytes()));
assertEquals(PASSWORD_COMPLEXITY_HIGH, dpm.getPasswordComplexity());
}
diff --git a/services/tests/servicestests/src/com/android/server/display/utils/AmbientFilterTest.java b/services/tests/servicestests/src/com/android/server/display/utils/AmbientFilterTest.java
new file mode 100644
index 000000000000..9b76b13d2ede
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/utils/AmbientFilterTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.utils;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.ContextWrapper;
+import android.content.res.Resources;
+import android.util.TypedValue;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class AmbientFilterTest {
+ private ContextWrapper mContextSpy;
+ private Resources mResourcesSpy;
+ private static String TAG = "AmbientFilterTest";
+
+ @Before
+ public void setUp() throws Exception {
+ mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
+ mResourcesSpy = spy(mContextSpy.getResources());
+ when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
+ }
+
+ @Test
+ public void testBrightnessFilter_ZeroIntercept() throws Exception {
+ final int horizon = 5 * 1000;
+ final int time_start = 30 * 1000;
+ final float intercept = 0.0f;
+ final int prediction_time = 100; // Hardcoded in AmbientFilter: prediction of how long the
+ // latest prediction will last before a new prediction.
+ setMockValues(mResourcesSpy, horizon, intercept);
+ AmbientFilter filter = AmbientFilterFactory.createBrightnessFilter(TAG, mResourcesSpy);
+
+ // Add first value and verify
+ filter.addValue(time_start, 30);
+ assertEquals(30, filter.getEstimate(time_start + prediction_time), 0.001);
+
+ // Add second value and verify that they are being averaged:
+ filter.addValue(time_start + prediction_time, 40);
+ // We check immediately after the value is added to verify that the weight of the
+ // prediction time is being correctly applied to the recent value correctly.
+ // In this case (time is in seconds so 100ms = 0.1s):
+ // weight 1 (w1) = (0.5*0.1^2 - 0.5*0^2) = 0.005
+ // weight 2 (w2) = (0.5*0.2^2 - 0.5*0.1^2) = 0.015
+ // w_t = w1 + w2 = 0.02
+ // total = w1 * 30 + w2 * 40 = 0.75
+ // estimate = total / w_t = 0.75 / 0.02 = 37.5
+ assertEquals(37.5, filter.getEstimate(time_start + prediction_time), 0.001);
+
+ // Add a third value to push the first value off of the buffer.
+ filter.addValue(time_start + horizon + prediction_time, 50);
+ assertEquals(40.38846f, filter.getEstimate(time_start + horizon + prediction_time), 0.001);
+ }
+
+ @Test
+ public void testBrightnessFilter_WithIntercept() throws Exception {
+ final int horizon = 5 * 1000;
+ final int time_start = 30 * 1000;
+ final float intercept = 10f;
+ final int prediction_time = 100;
+
+ setMockValues(mResourcesSpy, horizon, intercept);
+ AmbientFilter filter = AmbientFilterFactory.createBrightnessFilter(TAG, mResourcesSpy);
+
+ // Add first value and verify
+ filter.addValue(time_start, 30);
+ assertEquals(30, filter.getEstimate(time_start + prediction_time), 0.001);
+
+ // Add second value and verify that they are being averaged:
+ filter.addValue(time_start + prediction_time, 40);
+ // We check immediately after the value is added to verify that the weight of the
+ // prediction time is being correctly applied to the recent value correctly.
+ // In this case (time is in seconds so 100ms = 0.1s):
+ // weight 1 (w1) = (0.5*0.1^2 + 0.1*100) - (0.5*0^2 + 0*100) = 1.005
+ // weight 2 (w2) = (0.5*0.2^2 + 0.2*100) - (0.5*0.1^2 + 0.1*100) = 1.015
+ // w_t = w1 + w2 = 2.02
+ // total = w1 * 30 + w2 * 40 = 70.75
+ // estimate = total / w_t = 70.75 / 2.02 = 35.024752475
+ assertEquals(35.02475f, filter.getEstimate(time_start + prediction_time), 0.001);
+
+ // Add a third value to push the first value off of the buffer.
+ filter.addValue(time_start + horizon + prediction_time, 50);
+ assertEquals(40.23513f, filter.getEstimate(time_start + horizon + prediction_time), 0.001);
+ }
+
+ private void setMockValues(Resources resources, int horizon, float intercept) {
+ doAnswer(invocation -> {
+ TypedValue value = (TypedValue) invocation.getArguments()[1];
+ value.type = TypedValue.TYPE_FLOAT;
+ value.data = Float.floatToRawIntBits(intercept);
+ return null;
+ }).when(mResourcesSpy).getValue(
+ eq(com.android.internal.R.dimen
+ .config_displayWhiteBalanceBrightnessFilterIntercept),
+ any(TypedValue.class), eq(true));
+ when(mResourcesSpy.getInteger(
+ com.android.internal.R.integer
+ .config_displayWhiteBalanceBrightnessFilterHorizon)).thenReturn(horizon);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientFilterStubber.java b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientFilterStubber.java
new file mode 100644
index 000000000000..4d2551087c59
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientFilterStubber.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.utils;
+
+public class AmbientFilterStubber extends AmbientFilter {
+ public AmbientFilterStubber() {
+ super(null, 1);
+ }
+
+ protected float filter(long time, RollingBuffer buffer) {
+ return 0f;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientFilterTest.java b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientFilterTest.java
deleted file mode 100644
index 78164939aa49..000000000000
--- a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientFilterTest.java
+++ /dev/null
@@ -1,125 +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.display.whitebalance;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-
-import android.content.ContextWrapper;
-import android.content.res.Resources;
-import android.util.TypedValue;
-
-import androidx.test.InstrumentationRegistry;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public final class AmbientFilterTest {
- private ContextWrapper mContextSpy;
- private Resources mResourcesSpy;
-
- @Before
- public void setUp() throws Exception {
- mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
- mResourcesSpy = spy(mContextSpy.getResources());
- when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
- }
-
- @Test
- public void testBrightnessFilter_ZeroIntercept() throws Exception {
- final int horizon = 5 * 1000;
- final int time_start = 30 * 1000;
- final float intercept = 0.0f;
- final int prediction_time = 100; // Hardcoded in AmbientFilter: prediction of how long the
- // latest prediction will last before a new prediction.
- setMockValues(mResourcesSpy, horizon, intercept);
- AmbientFilter filter = DisplayWhiteBalanceFactory.createBrightnessFilter(mResourcesSpy);
-
- // Add first value and verify
- filter.addValue(time_start, 30);
- assertEquals(30, filter.getEstimate(time_start + prediction_time), 0.001);
-
- // Add second value and verify that they are being averaged:
- filter.addValue(time_start + prediction_time, 40);
- // We check immediately after the value is added to verify that the weight of the
- // prediction time is being correctly applied to the recent value correctly.
- // In this case (time is in seconds so 100ms = 0.1s):
- // weight 1 (w1) = (0.5*0.1^2 - 0.5*0^2) = 0.005
- // weight 2 (w2) = (0.5*0.2^2 - 0.5*0.1^2) = 0.015
- // w_t = w1 + w2 = 0.02
- // total = w1 * 30 + w2 * 40 = 0.75
- // estimate = total / w_t = 0.75 / 0.02 = 37.5
- assertEquals(37.5, filter.getEstimate(time_start + prediction_time), 0.001);
-
- // Add a third value to push the first value off of the buffer.
- filter.addValue(time_start + horizon + prediction_time, 50);
- assertEquals(40.38846f, filter.getEstimate(time_start + horizon + prediction_time), 0.001);
- }
-
- @Test
- public void testBrightnessFilter_WithIntercept() throws Exception {
- final int horizon = 5 * 1000;
- final int time_start = 30 * 1000;
- final float intercept = 10f;
- final int prediction_time = 100;
-
- setMockValues(mResourcesSpy, horizon, intercept);
- AmbientFilter filter = DisplayWhiteBalanceFactory.createBrightnessFilter(mResourcesSpy);
-
- // Add first value and verify
- filter.addValue(time_start, 30);
- assertEquals(30, filter.getEstimate(time_start + prediction_time), 0.001);
-
- // Add second value and verify that they are being averaged:
- filter.addValue(time_start + prediction_time, 40);
- // We check immediately after the value is added to verify that the weight of the
- // prediction time is being correctly applied to the recent value correctly.
- // In this case (time is in seconds so 100ms = 0.1s):
- // weight 1 (w1) = (0.5*0.1^2 + 0.1*100) - (0.5*0^2 + 0*100) = 1.005
- // weight 2 (w2) = (0.5*0.2^2 + 0.2*100) - (0.5*0.1^2 + 0.1*100) = 1.015
- // w_t = w1 + w2 = 2.02
- // total = w1 * 30 + w2 * 40 = 70.75
- // estimate = total / w_t = 70.75 / 2.02 = 35.024752475
- assertEquals(35.02475f, filter.getEstimate(time_start + prediction_time), 0.001);
-
- // Add a third value to push the first value off of the buffer.
- filter.addValue(time_start + horizon + prediction_time, 50);
- assertEquals(40.23513f, filter.getEstimate(time_start + horizon + prediction_time), 0.001);
- }
-
- private void setMockValues(Resources resources, int horizon, float intercept) {
- doAnswer(invocation -> {
- TypedValue value = (TypedValue) invocation.getArguments()[1];
- value.type = TypedValue.TYPE_FLOAT;
- value.data = Float.floatToRawIntBits(intercept);
- return null;
- }).when(mResourcesSpy).getValue(
- eq(com.android.internal.R.dimen
- .config_displayWhiteBalanceBrightnessFilterIntercept),
- any(TypedValue.class), eq(true));
- when(mResourcesSpy.getInteger(
- com.android.internal.R.integer
- .config_displayWhiteBalanceBrightnessFilterHorizon)).thenReturn(horizon);
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
index 6b0798bdce22..acf2d0e700d4 100644
--- a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
@@ -17,6 +17,8 @@
package com.android.server.display.whitebalance;
import com.android.internal.R;
+import com.android.server.display.utils.AmbientFilter;
+import com.android.server.display.utils.AmbientFilterStubber;
import com.google.common.collect.ImmutableList;
import static org.junit.Assert.assertEquals;
@@ -76,6 +78,8 @@ public final class AmbientLuxTest {
@Mock private TypedArray mBiases;
@Mock private TypedArray mHighLightBrightnesses;
@Mock private TypedArray mHighLightBiases;
+ @Mock private TypedArray mAmbientColorTemperatures;
+ @Mock private TypedArray mDisplayColorTemperatures;
@Before
public void setUp() throws Exception {
@@ -103,10 +107,10 @@ public final class AmbientLuxTest {
HIGH_LIGHT_AMBIENT_COLOR_TEMPERATURE);
when(mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceAmbientColorTemperatures))
- .thenReturn(createTypedArray());
+ .thenReturn(mAmbientColorTemperatures);
when(mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceDisplayColorTemperatures))
- .thenReturn(createTypedArray());
+ .thenReturn(mDisplayColorTemperatures);
when(mResourcesSpy.obtainTypedArray(
R.array.config_displayWhiteBalanceLowLightAmbientBrightnesses))
@@ -132,7 +136,7 @@ public final class AmbientLuxTest {
DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
final float ambientColorTemperature = 8000.0f;
setEstimatedColorTemperature(controller, ambientColorTemperature);
- controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
+ controller.mBrightnessFilter = spy(new AmbientFilterStubber());
for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) {
setEstimatedBrightnessAndUpdate(controller, luxOverride);
@@ -152,7 +156,7 @@ public final class AmbientLuxTest {
DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
final float ambientColorTemperature = 8000.0f;
setEstimatedColorTemperature(controller, ambientColorTemperature);
- controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
+ controller.mBrightnessFilter = spy(new AmbientFilterStubber());
for (float t = 0.0f; t <= 1.0f; t += 0.1f) {
setEstimatedBrightnessAndUpdate(controller,
@@ -184,7 +188,7 @@ public final class AmbientLuxTest {
DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
final float ambientColorTemperature = 8000.0f;
setEstimatedColorTemperature(controller, ambientColorTemperature);
- controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
+ controller.mBrightnessFilter = spy(new AmbientFilterStubber());
for (float t = 0.0f; t <= 1.0f; t += 0.1f) {
float luxOverride = mix(brightness0, brightness1, t);
@@ -221,7 +225,7 @@ public final class AmbientLuxTest {
DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
final float ambientColorTemperature = 8000.0f;
setEstimatedColorTemperature(controller, ambientColorTemperature);
- controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
+ controller.mBrightnessFilter = spy(new AmbientFilterStubber());
setEstimatedBrightnessAndUpdate(controller, 0.0f);
assertEquals(controller.mPendingAmbientColorTemperature,
@@ -240,7 +244,7 @@ public final class AmbientLuxTest {
DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
final float ambientColorTemperature = 8000.0f;
setEstimatedColorTemperature(controller, ambientColorTemperature);
- controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
+ controller.mBrightnessFilter = spy(new AmbientFilterStubber());
for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) {
setEstimatedBrightnessAndUpdate(controller, luxOverride);
@@ -258,7 +262,7 @@ public final class AmbientLuxTest {
DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
final float ambientColorTemperature = 8000.0f;
setEstimatedColorTemperature(controller, ambientColorTemperature);
- controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
+ controller.mBrightnessFilter = spy(new AmbientFilterStubber());
for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) {
setEstimatedBrightnessAndUpdate(controller, luxOverride);
@@ -278,7 +282,7 @@ public final class AmbientLuxTest {
DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
final float ambientColorTemperature = 8000.0f;
setEstimatedColorTemperature(controller, ambientColorTemperature);
- controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
+ controller.mBrightnessFilter = spy(new AmbientFilterStubber());
for (float t = 0.0f; t <= 1.0f; t += 0.1f) {
setEstimatedBrightnessAndUpdate(controller,
@@ -311,7 +315,7 @@ public final class AmbientLuxTest {
DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
final float ambientColorTemperature = 6000.0f;
setEstimatedColorTemperature(controller, ambientColorTemperature);
- controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
+ controller.mBrightnessFilter = spy(new AmbientFilterStubber());
for (float t = 0.0f; t <= 1.0f; t += 0.1f) {
float luxOverride = mix(brightness0, brightness1, t);
@@ -350,7 +354,7 @@ public final class AmbientLuxTest {
DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
final float ambientColorTemperature = 8000.0f;
setEstimatedColorTemperature(controller, ambientColorTemperature);
- controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
+ controller.mBrightnessFilter = spy(new AmbientFilterStubber());
for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) {
setEstimatedBrightnessAndUpdate(controller, luxOverride);
@@ -370,7 +374,7 @@ public final class AmbientLuxTest {
DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
final float ambientColorTemperature = -1.0f;
setEstimatedColorTemperature(controller, ambientColorTemperature);
- controller.mBrightnessFilter = spy(controller.mBrightnessFilter);
+ controller.mBrightnessFilter = spy(new AmbientFilterStubber());
for (float t = 0.0f; t <= 1.0f; t += 0.1f) {
setEstimatedBrightnessAndUpdate(controller,
@@ -386,6 +390,16 @@ public final class AmbientLuxTest {
assertEquals(controller.mPendingAmbientColorTemperature, ambientColorTemperature, 0.001);
}
+ @Test
+ public void testWhiteBalance_updateWithEmptyFilter() throws Exception {
+ setAmbientColorTemperatures(5300.0f, 6000.0f, 7000.0f, 8000.0f);
+ setDisplayColorTemperatures(6300.0f, 6400.0f, 6850.0f, 7450.0f);
+ DisplayWhiteBalanceController controller =
+ DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
+ controller.updateAmbientColorTemperature();
+ assertEquals(-1.0f, controller.mPendingAmbientColorTemperature, 0);
+ }
+
void mockThrottler() {
when(mResourcesSpy.getInteger(
R.integer.config_displayWhiteBalanceDecreaseDebounce)).thenReturn(0);
@@ -426,7 +440,7 @@ public final class AmbientLuxTest {
private void setEstimatedColorTemperature(DisplayWhiteBalanceController controller,
float ambientColorTemperature) {
- AmbientFilter colorTemperatureFilter = spy(controller.mColorTemperatureFilter);
+ AmbientFilter colorTemperatureFilter = spy(new AmbientFilterStubber());
controller.mColorTemperatureFilter = colorTemperatureFilter;
when(colorTemperatureFilter.getEstimate(anyLong())).thenReturn(ambientColorTemperature);
}
@@ -453,6 +467,14 @@ public final class AmbientLuxTest {
setFloatArrayResource(mHighLightBiases, vals);
}
+ private void setAmbientColorTemperatures(float... vals) {
+ setFloatArrayResource(mAmbientColorTemperatures, vals);
+ }
+
+ private void setDisplayColorTemperatures(float... vals) {
+ setFloatArrayResource(mDisplayColorTemperatures, vals);
+ }
+
private void setFloatArrayResource(TypedArray array, float[] vals) {
when(array.length()).thenReturn(vals.length);
for (int i = 0; i < vals.length; i++) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index 11bd29d8a163..ad3e040aca72 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -28,6 +28,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -114,6 +115,7 @@ public class ArcTerminationActionFromAvrTest {
}
@Test
+ @Ignore("b/120845532")
public void testSendMessage_notSuccess() {
mSendCecCommandSuccess = false;
mShouldDispatchReportArcTerminated = false;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index a89198ae3708..0a1899be8c1f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -232,6 +232,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
}
@Test
+ @Ignore("b/120845532")
public void handleGiveSystemAudioModeStatus_originalOff() throws Exception {
HdmiCecMessage expectedMessage =
HdmiCecMessageBuilder.buildReportSystemAudioMode(
@@ -301,6 +302,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
}
@Test
+ @Ignore("b/120845532")
public void handleSetSystemAudioMode_setOn_orignalOff() throws Exception {
mMusicMute = true;
HdmiCecMessage messageSet =
@@ -328,6 +330,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
}
@Test
+ @Ignore("b/120845532")
public void handleSystemAudioModeRequest_turnOffByTv() throws Exception {
assertThat(mMusicMute).isFalse();
// Check if feature correctly turned off
@@ -497,6 +500,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
}
@Test
+ @Ignore("b/120845532")
public void handleRequestArcTerminate_arcIsNotOn() throws Exception {
assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
HdmiCecMessage message =
@@ -671,6 +675,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
}
@Test
+ @Ignore("b/120845532")
public void handleReportPhysicalAddress_differentPath_addDevice() {
assertThat(mInvokeDeviceEventState).isNotEqualTo(DEVICE_EVENT_ADD_DEVICE);
HdmiDeviceInfo oldDevice = new HdmiDeviceInfo(
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index b8799c3f16f7..0062a1751802 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -99,6 +99,7 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Test
+ @Ignore("b/120845532")
public void handleSetSystemAudioModeOn_audioSystemBroadcast() {
mHdmiControlService.setSystemAudioActivated(false);
assertThat(mHdmiCecLocalDevicePlayback.mService.isSystemAudioActivated()).isFalse();
@@ -110,6 +111,7 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Test
+ @Ignore("b/120845532")
public void handleSetSystemAudioModeOff_audioSystemToPlayback() {
mHdmiCecLocalDevicePlayback.mService.setSystemAudioActivated(true);
assertThat(mHdmiCecLocalDevicePlayback.mService.isSystemAudioActivated()).isTrue();
@@ -123,6 +125,7 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Test
+ @Ignore("b/120845532")
public void handleSystemAudioModeStatusOn_DirectltToLocalDeviceFromAudioSystem() {
mHdmiControlService.setSystemAudioActivated(false);
assertThat(mHdmiCecLocalDevicePlayback.mService.isSystemAudioActivated()).isFalse();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java
new file mode 100644
index 000000000000..c6cf9b116a1d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.hdmi;
+
+import static android.os.SystemClock.sleep;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+import static com.google.common.truth.Truth.assertThat;
+import static junit.framework.Assert.assertEquals;
+
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.os.Looper;
+import android.os.SystemProperties;
+import android.os.test.TestLooper;
+import android.util.Slog;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import java.util.ArrayList;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Tests for {@link HdmiControlServiceBinderAPITest} class.
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class HdmiControlServiceBinderAPITest {
+
+ private class HdmiCecLocalDeviceMyDevice extends HdmiCecLocalDevice {
+
+ private boolean mCanGoToStandby;
+ private boolean mIsStandby;
+ private boolean mIsDisabled;
+
+ protected HdmiCecLocalDeviceMyDevice(HdmiControlService service, int deviceType) {
+ super(service, deviceType);
+ }
+
+ @Override
+ protected void onAddressAllocated(int logicalAddress, int reason) {
+ }
+
+ @Override
+ protected int getPreferredAddress() {
+ return 0;
+ }
+
+ @Override
+ protected void setPreferredAddress(int addr) {
+ }
+
+ @Override
+ protected boolean canGoToStandby() {
+ return mCanGoToStandby;
+ }
+
+ @Override
+ protected void disableDevice(
+ boolean initiatedByCec, final PendingActionClearedCallback originalCallback) {
+ mIsDisabled = true;
+ originalCallback.onCleared(this);
+ }
+
+ @Override
+ protected void onStandby(boolean initiatedByCec, int standbyAction) {
+ mIsStandby = true;
+ }
+
+ protected boolean isStandby() {
+ return mIsStandby;
+ }
+
+ protected boolean isDisabled() {
+ return mIsDisabled;
+ }
+
+ protected void setCanGoToStandby(boolean canGoToStandby) {
+ mCanGoToStandby = canGoToStandby;
+ }
+ }
+
+ private static final String TAG = "HdmiControlServiceBinderAPITest";
+ private HdmiControlService mHdmiControlService;
+ private HdmiCecController mHdmiCecController;
+ private HdmiCecLocalDevicePlayback mPlaybackDevice;
+ private FakeNativeWrapper mNativeWrapper;
+ private Looper mMyLooper;
+ private TestLooper mTestLooper = new TestLooper();
+ private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+ private HdmiPortInfo[] mHdmiPortInfo;
+ private int mResult;
+ private int mPowerStatus;
+
+ @Before
+ public void SetUp() {
+ mHdmiControlService =
+ new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+ @Override
+ void sendCecCommand(HdmiCecMessage command) {
+ switch (command.getOpcode()) {
+ case Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS:
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildReportPowerStatus(
+ Constants.ADDR_TV,
+ Constants.ADDR_PLAYBACK_1,
+ HdmiControlManager.POWER_STATUS_ON);
+ handleCecCommand(message);
+ break;
+ default:
+ return;
+ }
+ }
+
+ @Override
+ boolean isPowerStandby() {
+ return mPowerStatus == HdmiControlManager.POWER_STATUS_STANDBY;
+ }
+ };
+ mMyLooper = mTestLooper.getLooper();
+
+ mPlaybackDevice = new HdmiCecLocalDevicePlayback(mHdmiControlService) {
+ @Override
+ void setIsActiveSource(boolean on) {
+ mIsActiveSource = on;
+ }
+
+ @Override
+ protected void wakeUpIfActiveSource() {}
+
+ @Override
+ protected void setPreferredAddress(int addr) {}
+
+ @Override
+ protected int getPreferredAddress() {
+ return Constants.ADDR_PLAYBACK_1;
+ }
+ };
+ mPlaybackDevice.init();
+
+ mHdmiControlService.setIoLooper(mMyLooper);
+
+ mNativeWrapper = new FakeNativeWrapper();
+ mHdmiCecController =
+ HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
+ mHdmiControlService.setCecController(mHdmiCecController);
+ mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+ mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+
+ mLocalDevices.add(mPlaybackDevice);
+ mHdmiPortInfo = new HdmiPortInfo[1];
+ mHdmiPortInfo[0] =
+ new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x2100, true, false, false);
+ mNativeWrapper.setPortInfo(mHdmiPortInfo);
+ mHdmiControlService.initPortInfo();
+ mResult = -1;
+ mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
+
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void oneTouchPlay_addressNotAllocated() {
+ assertThat(mHdmiControlService.isAddressAllocated()).isFalse();
+ mHdmiControlService.oneTouchPlay(new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ mResult = result;
+ }
+ });
+ assertEquals(mResult, -1);
+ assertThat(mPlaybackDevice.mIsActiveSource).isFalse();
+
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+ assertThat(mHdmiControlService.isAddressAllocated()).isTrue();
+ assertEquals(mResult, HdmiControlManager.RESULT_SUCCESS);
+ assertThat(mPlaybackDevice.mIsActiveSource).isTrue();
+ }
+
+ @Test
+ public void oneTouchPlay_addressAllocated() {
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+ assertThat(mHdmiControlService.isAddressAllocated()).isTrue();
+ mHdmiControlService.oneTouchPlay(new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ mResult = result;
+ }
+ });
+ assertEquals(mResult, HdmiControlManager.RESULT_SUCCESS);
+ assertThat(mPlaybackDevice.mIsActiveSource).isTrue();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
index 440a49ab81fc..6dcff3574faf 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -221,6 +221,7 @@ public class SystemAudioInitiationActionFromAvrTest {
}
@Test
+ @Ignore("b/120845532")
public void testTvSupport() {
resetTestVariables();
mShouldDispatchActiveSource = true;
diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
new file mode 100644
index 000000000000..baf1ed00c7b5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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.integrity.engine;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import com.android.server.integrity.model.AppInstallMetadata;
+import com.android.server.integrity.model.AtomicFormula;
+import com.android.server.integrity.model.OpenFormula;
+import com.android.server.integrity.model.Rule;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class RuleEvaluatorTest {
+
+ private static final String PACKAGE_NAME_1 = "com.test.app";
+ private static final String PACKAGE_NAME_2 = "com.test.app2";
+ private static final String APP_CERTIFICATE = "test_cert";
+ private static final AppInstallMetadata APP_INSTALL_METADATA =
+ new AppInstallMetadata.Builder()
+ .setPackageName(PACKAGE_NAME_1)
+ .setAppCertificate(APP_CERTIFICATE)
+ .build();
+
+ @Test
+ public void testMatchRules_emptyRules() {
+ List<Rule> rules = new ArrayList<>();
+
+ Rule matchedRule = RuleEvaluator.evaluateRules(rules, APP_INSTALL_METADATA);
+
+ assertEquals(Rule.EMPTY, matchedRule);
+ }
+
+ @Test
+ public void testMatchRules_emptyMatch() {
+ Rule rule1 = new Rule(
+ new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ PACKAGE_NAME_2), Rule.Effect.DENY);
+
+ Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule1),
+ APP_INSTALL_METADATA);
+
+ assertEquals(Rule.EMPTY, matchedRule);
+ }
+
+
+ @Test
+ public void testMatchRules_oneMatch() {
+ Rule rule1 = new Rule(
+ new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ PACKAGE_NAME_1), Rule.Effect.DENY);
+ Rule rule2 = new Rule(
+ new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ PACKAGE_NAME_2), Rule.Effect.DENY);
+
+ Rule matchedRule = RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2),
+ APP_INSTALL_METADATA);
+
+ assertEquals(rule1, matchedRule);
+ }
+
+ @Test
+ public void testMatchRules_multipleMatches() {
+ Rule rule1 = new Rule(
+ new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ PACKAGE_NAME_1), Rule.Effect.DENY);
+ OpenFormula openFormula2 = new OpenFormula(OpenFormula.Connector.AND, Arrays.asList(
+ new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ PACKAGE_NAME_1),
+ new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE,
+ AtomicFormula.Operator.EQ,
+ APP_CERTIFICATE)));
+ Rule rule2 = new Rule(
+ openFormula2, Rule.Effect.DENY);
+
+ Rule matchedRule = RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2),
+ APP_INSTALL_METADATA);
+
+ assertNotEquals(Rule.EMPTY, matchedRule);
+ }
+
+ @Test
+ public void testMatchRules_ruleWithNot() {
+ OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.NOT,
+ Collections.singletonList(
+ new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ PACKAGE_NAME_2)));
+ Rule rule = new Rule(openFormula, Rule.Effect.DENY);
+
+ Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule),
+ APP_INSTALL_METADATA);
+
+ assertEquals(rule, matchedRule);
+ }
+
+ @Test
+ public void testMatchRules_ruleWithIntegerOperators() {
+ Rule rule1 = new Rule(
+ new AtomicFormula(AtomicFormula.Key.VERSION_CODE, AtomicFormula.Operator.GT,
+ 1), Rule.Effect.DENY);
+
+ Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule1),
+ APP_INSTALL_METADATA);
+
+ assertEquals(rule1, matchedRule);
+ }
+
+ @Test
+ public void testMatchRules_validForm() {
+ OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.AND, Arrays.asList(
+ new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ PACKAGE_NAME_1),
+ new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE,
+ AtomicFormula.Operator.EQ,
+ APP_CERTIFICATE)));
+ Rule rule = new Rule(
+ openFormula, Rule.Effect.DENY);
+
+ Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule),
+ APP_INSTALL_METADATA);
+
+ assertEquals(rule, matchedRule);
+ }
+
+ @Test
+ public void testMatchRules_ruleNotInDNF() {
+ OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.OR, Arrays.asList(
+ new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ PACKAGE_NAME_1),
+ new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE,
+ AtomicFormula.Operator.EQ,
+ APP_CERTIFICATE)));
+ Rule rule = new Rule(
+ openFormula, Rule.Effect.DENY);
+
+ Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule),
+ APP_INSTALL_METADATA);
+
+ assertEquals(Rule.EMPTY, matchedRule);
+ }
+
+ @Test
+ public void testMatchRules_openFormulaWithNot() {
+ OpenFormula openSubFormula = new OpenFormula(OpenFormula.Connector.AND, Arrays.asList(
+ new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ PACKAGE_NAME_2),
+ new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE,
+ AtomicFormula.Operator.EQ,
+ APP_CERTIFICATE)));
+ OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.NOT,
+ Collections.singletonList(openSubFormula));
+ Rule rule = new Rule(
+ openFormula, Rule.Effect.DENY);
+
+ Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule),
+ APP_INSTALL_METADATA);
+
+ assertEquals(Rule.EMPTY, matchedRule);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/AtomicFormulaTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/AtomicFormulaTest.java
new file mode 100644
index 000000000000..1cb2fb3517d3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/model/AtomicFormulaTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.integrity.model;
+
+import static com.android.server.testutils.TestUtils.assertExpectException;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class AtomicFormulaTest {
+
+ @Test
+ public void testValidAtomicFormula_stringValue() {
+ AtomicFormula atomicFormula = new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME,
+ AtomicFormula.Operator.EQ, "com.test.app");
+
+ assertEquals(AtomicFormula.Key.PACKAGE_NAME, atomicFormula.getKey());
+ assertEquals(AtomicFormula.Operator.EQ, atomicFormula.getOperator());
+ assertEquals("com.test.app", atomicFormula.getStringValue());
+ }
+
+ @Test
+ public void testValidAtomicFormula_intValue() {
+ AtomicFormula atomicFormula = new AtomicFormula(AtomicFormula.Key.VERSION_CODE,
+ AtomicFormula.Operator.LE, 1);
+
+ assertEquals(AtomicFormula.Key.VERSION_CODE, atomicFormula.getKey());
+ assertEquals(AtomicFormula.Operator.LE, atomicFormula.getOperator());
+ assertEquals(1, atomicFormula.getIntValue().intValue());
+ }
+
+ @Test
+ public void testValidAtomicFormula_boolValue() {
+ AtomicFormula atomicFormula = new AtomicFormula(AtomicFormula.Key.PRE_INSTALLED,
+ AtomicFormula.Operator.EQ, true);
+
+ assertEquals(AtomicFormula.Key.PRE_INSTALLED, atomicFormula.getKey());
+ assertEquals(AtomicFormula.Operator.EQ, atomicFormula.getOperator());
+ assertEquals(true, atomicFormula.getBoolValue());
+ }
+
+ @Test
+ public void testInvalidAtomicFormula_stringValue() {
+ assertExpectException(
+ IllegalArgumentException.class,
+ /* expectedExceptionMessageRegex */
+ String.format("Key %s cannot have string value", AtomicFormula.Key.VERSION_CODE),
+ () -> new AtomicFormula(AtomicFormula.Key.VERSION_CODE, AtomicFormula.Operator.EQ,
+ "test-value"));
+ }
+
+ @Test
+ public void testInvalidAtomicFormula_intValue() {
+ assertExpectException(
+ IllegalArgumentException.class,
+ /* expectedExceptionMessageRegex */
+ String.format("Key %s cannot have integer value", AtomicFormula.Key.PACKAGE_NAME),
+ () -> new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ 1));
+ }
+
+ @Test
+ public void testInvalidAtomicFormula_boolValue() {
+ assertExpectException(
+ IllegalArgumentException.class,
+ /* expectedExceptionMessageRegex */
+ String.format("Key %s cannot have boolean value", AtomicFormula.Key.PACKAGE_NAME),
+ () -> new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ true));
+ }
+
+ @Test
+ public void testValidateOperator_invalidKeyOperatorPair() {
+ assertExpectException(
+ IllegalArgumentException.class,
+ /* expectedExceptionMessageRegex */
+ String.format("Invalid operator %s used for key %s",
+ AtomicFormula.Operator.LE, AtomicFormula.Key.PACKAGE_NAME),
+ () -> new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.LE,
+ "test-value"));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/OpenFormulaTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/OpenFormulaTest.java
new file mode 100644
index 000000000000..2133a7d3550b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/model/OpenFormulaTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.integrity.model;
+
+import static com.android.server.testutils.TestUtils.assertExpectException;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+@RunWith(JUnit4.class)
+public class OpenFormulaTest {
+
+ private static final AtomicFormula ATOMIC_FORMULA_1 = new AtomicFormula(
+ AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ, "test1");
+ private static final AtomicFormula ATOMIC_FORMULA_2 = new AtomicFormula(
+ AtomicFormula.Key.VERSION_CODE, AtomicFormula.Operator.EQ, 1);
+
+ @Test
+ public void testValidOpenFormula() {
+ OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.AND,
+ Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
+
+ assertEquals(OpenFormula.Connector.AND, openFormula.getConnector());
+ assertEquals(Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2), openFormula.getFormulas());
+ }
+
+ @Test
+ public void testValidateAuxiliaryFormula_binaryConnectors() {
+ assertExpectException(
+ IllegalArgumentException.class,
+ /* expectedExceptionMessageRegex */
+ String.format("Connector %s must have at least 2 formulas",
+ OpenFormula.Connector.AND),
+ () -> new OpenFormula(OpenFormula.Connector.AND,
+ Collections.singletonList(ATOMIC_FORMULA_1)));
+ }
+
+ @Test
+ public void testValidateAuxiliaryFormula_unaryConnectors() {
+ assertExpectException(
+ IllegalArgumentException.class,
+ /* expectedExceptionMessageRegex */
+ String.format("Connector %s must have 1 formula only", OpenFormula.Connector.NOT),
+ () -> new OpenFormula(OpenFormula.Connector.NOT,
+ Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/RuleTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/RuleTest.java
new file mode 100644
index 000000000000..048ee707d8fe
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/model/RuleTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.integrity.model;
+
+import static com.android.server.testutils.TestUtils.assertExpectException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+
+@RunWith(JUnit4.class)
+public class RuleTest {
+
+ private static final Rule.Effect DENY_EFFECT = Rule.Effect.DENY;
+ private static final String PACKAGE_NAME = "com.test.app";
+ private static final String APP_CERTIFICATE = "test_cert";
+ private static final Formula PACKAGE_NAME_ATOMIC_FORMULA =
+ new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ PACKAGE_NAME);
+ private static final Formula APP_CERTIFICATE_ATOMIC_FORMULA =
+ new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE, AtomicFormula.Operator.EQ,
+ APP_CERTIFICATE);
+
+ @Test
+ public void testEmptyRule() {
+ Rule emptyRule = Rule.EMPTY;
+
+ assertNull(emptyRule.getFormula());
+ assertNull(emptyRule.getEffect());
+ }
+
+ @Test
+ public void testValidRule() {
+ Rule validRule = new Rule(PACKAGE_NAME_ATOMIC_FORMULA, DENY_EFFECT);
+
+ assertEquals(PACKAGE_NAME_ATOMIC_FORMULA, validRule.getFormula());
+ assertEquals(DENY_EFFECT, validRule.getEffect());
+ }
+
+ @Test
+ public void testInvalidRule_invalidEffect() {
+ assertExpectException(
+ NullPointerException.class,
+ /* expectedExceptionMessageRegex */ null,
+ () -> new Rule(PACKAGE_NAME_ATOMIC_FORMULA, null));
+ }
+
+ @Test
+ public void testInvalidRule_invalidFormula() {
+ assertExpectException(
+ NullPointerException.class,
+ /* expectedExceptionMessageRegex */ null,
+ () -> new Rule(null, DENY_EFFECT));
+ }
+
+ @Test
+ public void testToString() {
+ OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.AND,
+ Arrays.asList(PACKAGE_NAME_ATOMIC_FORMULA, APP_CERTIFICATE_ATOMIC_FORMULA));
+ Rule rule = new Rule(openFormula, Rule.Effect.DENY);
+
+ String toString = rule.toString();
+
+ assertEquals(String.format("Rule: PACKAGE_NAME EQ %s AND APP_CERTIFICATE EQ %s, DENY",
+ PACKAGE_NAME, APP_CERTIFICATE), toString);
+ }
+
+ @Test
+ public void testEquals_trueCase() {
+ Rule rule1 = new Rule(PACKAGE_NAME_ATOMIC_FORMULA, DENY_EFFECT);
+ Rule rule2 = new Rule(PACKAGE_NAME_ATOMIC_FORMULA, DENY_EFFECT);
+
+ assertEquals(rule1, rule2);
+ }
+
+ @Test
+ public void testEquals_falseCase() {
+ Rule rule1 = new Rule(PACKAGE_NAME_ATOMIC_FORMULA, DENY_EFFECT);
+ Rule rule2 = new Rule(APP_CERTIFICATE_ATOMIC_FORMULA, DENY_EFFECT);
+
+ assertNotEquals(rule1, rule2);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index 1f5ebe4536d8..98630576ed66 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -29,14 +29,21 @@ import android.app.KeyguardManager;
import android.app.NotificationManager;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DeviceStateCache;
import android.app.trust.TrustManager;
import android.content.ComponentName;
+import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.hardware.authsecret.V1_0.IAuthSecret;
+import android.hardware.face.Face;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
import android.os.FileUtils;
import android.os.IProgressListener;
import android.os.RemoteException;
import android.os.UserManager;
+import android.os.UserManagerInternal;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.security.KeyStore;
@@ -45,6 +52,7 @@ import android.test.AndroidTestCase;
import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockSettingsInternal;
+import com.android.internal.widget.LockscreenCredential;
import com.android.server.LocalServices;
import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
import com.android.server.wm.WindowManagerInternal;
@@ -91,7 +99,13 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase {
FakeGsiService mGsiService;
PasswordSlotManagerTestable mPasswordSlotManager;
RecoverableKeyStoreManager mRecoverableKeyStoreManager;
+ UserManagerInternal mUserManagerInternal;
+ DeviceStateCache mDeviceStateCache;
+ FingerprintManager mFingerprintManager;
+ FaceManager mFaceManager;
+ PackageManager mPackageManager;
protected boolean mHasSecureLockScreen;
+ FakeSettings mSettings;
@Override
protected void setUp() throws Exception {
@@ -108,6 +122,12 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase {
mGsiService = new FakeGsiService();
mPasswordSlotManager = new PasswordSlotManagerTestable();
mRecoverableKeyStoreManager = mock(RecoverableKeyStoreManager.class);
+ mUserManagerInternal = mock(UserManagerInternal.class);
+ mDeviceStateCache = mock(DeviceStateCache.class);
+ mFingerprintManager = mock(FingerprintManager.class);
+ mFaceManager = mock(FaceManager.class);
+ mPackageManager = mock(PackageManager.class);
+ mSettings = new FakeSettings();
LocalServices.removeServiceForTest(LockSettingsInternal.class);
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
@@ -117,7 +137,7 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase {
mContext = new MockLockSettingsContext(getContext(), mUserManager, mNotificationManager,
mDevicePolicyManager, mock(StorageManager.class), mock(TrustManager.class),
- mock(KeyguardManager.class));
+ mock(KeyguardManager.class), mFingerprintManager, mFaceManager, mPackageManager);
mStorage = new LockSettingsStorageTestable(mContext,
new File(getContext().getFilesDir(), "locksettings"));
File storageDir = mStorage.mStorageDir;
@@ -144,7 +164,8 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase {
mAuthSecretService = mock(IAuthSecret.class);
mService = new LockSettingsServiceTestable(mContext, mLockPatternUtils, mStorage,
mGateKeeperService, mKeyStore, setUpStorageManagerMock(), mActivityManager,
- mSpManager, mAuthSecretService, mGsiService, mRecoverableKeyStoreManager);
+ mSpManager, mAuthSecretService, mGsiService, mRecoverableKeyStoreManager,
+ mUserManagerInternal, mDeviceStateCache, mSettings);
when(mUserManager.getUserInfo(eq(PRIMARY_USER_ID))).thenReturn(PRIMARY_USER_INFO);
mPrimaryUserProfiles.add(PRIMARY_USER_INFO);
installChildProfile(MANAGED_PROFILE_USER_ID);
@@ -172,6 +193,12 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase {
// Adding a fake Device Owner app which will enable escrow token support in LSS.
when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()).thenReturn(
new ComponentName("com.dummy.package", ".FakeDeviceOwner"));
+ when(mUserManagerInternal.isDeviceManaged()).thenReturn(true);
+ when(mDeviceStateCache.isDeviceProvisioned()).thenReturn(true);
+ mockBiometricsHardwareFingerprintsAndTemplates(PRIMARY_USER_ID);
+ mockBiometricsHardwareFingerprintsAndTemplates(MANAGED_PROFILE_USER_ID);
+
+ mSettings.setDeviceProvisioned(true);
mLocalService = LocalServices.getService(LockSettingsInternal.class);
}
@@ -184,6 +211,7 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase {
when(mUserManager.getProfileParent(eq(profileId))).thenReturn(PRIMARY_USER_INFO);
when(mUserManager.isUserRunning(eq(profileId))).thenReturn(true);
when(mUserManager.isUserUnlocked(eq(profileId))).thenReturn(true);
+ when(mUserManagerInternal.isUserManaged(eq(profileId))).thenReturn(true);
return userInfo;
}
@@ -222,6 +250,39 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase {
return sm;
}
+ private void mockBiometricsHardwareFingerprintsAndTemplates(int userId) {
+ // Hardware must be detected and fingerprints must be enrolled
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ when(mFingerprintManager.hasEnrolledFingerprints(userId)).thenReturn(true);
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Fingerprint fp = (Fingerprint) invocation.getArguments()[0];
+ FingerprintManager.RemovalCallback callback =
+ (FingerprintManager.RemovalCallback) invocation.getArguments()[2];
+ callback.onRemovalSucceeded(fp, 0);
+ return null;
+ }
+ }).when(mFingerprintManager).remove(any(), eq(userId), any());
+
+
+ // Hardware must be detected and templates must be enrolled
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+ when(mFaceManager.hasEnrolledTemplates(userId)).thenReturn(true);
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Face face = (Face) invocation.getArguments()[0];
+ FaceManager.RemovalCallback callback =
+ (FaceManager.RemovalCallback) invocation.getArguments()[2];
+ callback.onRemovalSucceeded(face, 0);
+ return null;
+ }
+ }).when(mFaceManager).remove(any(), eq(userId), any());
+ }
+
@Override
protected void tearDown() throws Exception {
super.tearDown();
@@ -250,4 +311,22 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase {
protected static void assertArrayNotEquals(byte[] expected, byte[] actual) {
assertFalse(Arrays.equals(expected, actual));
}
+
+ protected LockscreenCredential newPassword(String password) {
+ return LockscreenCredential.createPasswordOrNone(password);
+ }
+
+ protected LockscreenCredential newPin(String pin) {
+ return LockscreenCredential.createPinOrNone(pin);
+ }
+
+ protected LockscreenCredential newPattern(String pattern) {
+ return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
+ pattern.getBytes()));
+ }
+
+ protected LockscreenCredential nonePassword() {
+ return LockscreenCredential.createNone();
+ }
+
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java
index d2a914527880..5c54883c338f 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java
@@ -15,9 +15,6 @@
*/
package com.android.server.locksettings;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
-
import static com.android.server.testutils.TestUtils.assertExpectException;
import static org.mockito.Mockito.anyInt;
@@ -30,7 +27,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
-import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.VerifyCredentialResponse;
import org.mockito.ArgumentCaptor;
@@ -59,56 +56,53 @@ public class CachedSyntheticPasswordTests extends SyntheticPasswordTests {
}
public void testSyntheticPasswordClearCredentialUntrusted() throws RemoteException {
- final byte[] password = "testSyntheticPasswordClearCredential-password".getBytes();
- final byte[] newPassword = "testSyntheticPasswordClearCredential-newpassword".getBytes();
+ final LockscreenCredential password = newPassword("password");
+ final LockscreenCredential newPassword = newPassword("newpassword");
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
// clear password
- mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, null,
- PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID, true);
+ mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID, true);
assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
// set a new password
- mService.setLockCredential(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(newPassword,
- LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ mService.setLockCredential(newPassword, nonePassword(), PRIMARY_USER_ID,
+ false);
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+ newPassword, 0, PRIMARY_USER_ID)
.getResponseCode());
assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
}
public void testSyntheticPasswordChangeCredentialUntrusted() throws RemoteException {
- final byte[] password = "testSyntheticPasswordClearCredential-password".getBytes();
- final byte[] newPassword = "testSyntheticPasswordClearCredential-newpassword".getBytes();
+ final LockscreenCredential password = newPassword("password");
+ final LockscreenCredential newPassword = newPassword("newpassword");
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
// Untrusted change password
- mService.setLockCredential(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, true);
+ mService.setLockCredential(newPassword, nonePassword(), PRIMARY_USER_ID,
+ true);
assertNotEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
// Verify the password
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(newPassword,
- LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode());
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+ newPassword, 0, PRIMARY_USER_ID).getResponseCode());
}
public void testUntrustedCredentialChangeMaintainsAuthSecret() throws RemoteException {
- final byte[] password =
- "testUntrustedCredentialChangeMaintainsAuthSecret-password".getBytes();
- final byte[] newPassword =
- "testUntrustedCredentialChangeMaintainsAuthSecret-newpassword".getBytes();
+ final LockscreenCredential password = newPassword("password");
+ final LockscreenCredential newPassword = newPassword("newpassword");
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
// Untrusted change password
- mService.setLockCredential(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, true);
+ mService.setLockCredential(newPassword, nonePassword(), PRIMARY_USER_ID,
+ true);
// Verify the password
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(newPassword,
- LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+ newPassword, 0, PRIMARY_USER_ID)
.getResponseCode());
// Ensure the same secret was passed each time
@@ -118,31 +112,30 @@ public class CachedSyntheticPasswordTests extends SyntheticPasswordTests {
}
public void testUntrustedCredentialChangeBlockedIfSpNotCached() throws RemoteException {
- final byte[] password =
- "testUntrustedCredentialChangeBlockedIfSpNotCached-password".getBytes();
- final byte[] newPassword =
- "testUntrustedCredentialChangeBlockedIfSpNotCached-newpassword".getBytes();
+ final LockscreenCredential password = newPassword("password");
+ final LockscreenCredential newPassword = newPassword("newpassword");
// Disable caching for this test
enableSpCaching(false);
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
+ flushHandlerTasks();
+
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
// Untrusted change password
assertExpectException(
IllegalStateException.class,
/* messageRegex= */ "Untrusted credential reset not possible without cached SP",
- () -> mService.setLockCredential(newPassword,
- LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, true));
+ () -> mService.setLockCredential(newPassword, nonePassword(),
+ PRIMARY_USER_ID, true));
assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
// Verify the new password doesn't work but the old one still does
- assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, mService.verifyCredential(newPassword,
- LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, mService.verifyCredential(
+ newPassword, 0, PRIMARY_USER_ID)
.getResponseCode());
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(password,
- LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+ password, 0, PRIMARY_USER_ID)
.getResponseCode());
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java b/services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java
new file mode 100644
index 000000000000..70a927c216ec
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java
@@ -0,0 +1,36 @@
+/*
+ * 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.locksettings;
+
+import android.provider.Settings;
+
+public class FakeSettings {
+
+ private int mDeviceProvisioned;
+
+ public void setDeviceProvisioned(boolean provisioned) {
+ mDeviceProvisioned = provisioned ? 1 : 0;
+ }
+
+ public int globalGetInt(String keyName) {
+ switch (keyName) {
+ case Settings.Global.DEVICE_PROVISIONED:
+ return mDeviceProvisioned;
+ default:
+ throw new IllegalArgumentException("Unhandled global settings: " + keyName);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index 5c67d0422aca..7e7e170fee06 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -19,17 +19,22 @@ package com.android.server.locksettings;
import static org.mockito.Mockito.mock;
import android.app.IActivityManager;
+import android.app.admin.DeviceStateCache;
+import android.content.ContentResolver;
import android.content.Context;
import android.hardware.authsecret.V1_0.IAuthSecret;
import android.os.Handler;
-import android.os.Looper;
+import android.os.Parcel;
import android.os.Process;
import android.os.RemoteException;
+import android.os.UserManagerInternal;
import android.os.storage.IStorageManager;
import android.security.KeyStore;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
+import com.android.server.ServiceThread;
import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
import java.io.FileNotFoundException;
@@ -44,15 +49,18 @@ public class LockSettingsServiceTestable extends LockSettingsService {
private LockPatternUtils mLockPatternUtils;
private IStorageManager mStorageManager;
private SyntheticPasswordManager mSpManager;
- private IAuthSecret mAuthSecretService;
private FakeGsiService mGsiService;
private RecoverableKeyStoreManager mRecoverableKeyStoreManager;
+ private UserManagerInternal mUserManagerInternal;
+ private DeviceStateCache mDeviceStateCache;
+ private FakeSettings mSettings;
public MockInjector(Context context, LockSettingsStorage storage, KeyStore keyStore,
IActivityManager activityManager, LockPatternUtils lockPatternUtils,
IStorageManager storageManager, SyntheticPasswordManager spManager,
- IAuthSecret authSecretService, FakeGsiService gsiService,
- RecoverableKeyStoreManager recoverableKeyStoreManager) {
+ FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager,
+ UserManagerInternal userManagerInternal, DeviceStateCache deviceStateCache,
+ FakeSettings settings) {
super(context);
mLockSettingsStorage = storage;
mKeyStore = keyStore;
@@ -62,11 +70,14 @@ public class LockSettingsServiceTestable extends LockSettingsService {
mSpManager = spManager;
mGsiService = gsiService;
mRecoverableKeyStoreManager = recoverableKeyStoreManager;
+ mUserManagerInternal = userManagerInternal;
+ mDeviceStateCache = deviceStateCache;
+ mSettings = settings;
}
@Override
- public Handler getHandler() {
- return new Handler(Looper.getMainLooper());
+ public Handler getHandler(ServiceThread handlerThread) {
+ return new Handler(handlerThread.getLooper());
}
@Override
@@ -93,6 +104,10 @@ public class LockSettingsServiceTestable extends LockSettingsService {
public LockPatternUtils getLockPatternUtils() {
return mLockPatternUtils;
}
+ @Override
+ public DeviceStateCache getDeviceStateCache() {
+ return mDeviceStateCache;
+ }
@Override
public KeyStore getKeyStore() {
@@ -110,6 +125,17 @@ public class LockSettingsServiceTestable extends LockSettingsService {
}
@Override
+ public int settingsGlobalGetInt(ContentResolver contentResolver, String keyName,
+ int defaultValue) {
+ return mSettings.globalGetInt(keyName);
+ }
+
+ @Override
+ public UserManagerInternal getUserManagerInternal() {
+ return mUserManagerInternal;
+ }
+
+ @Override
public boolean hasEnrolledBiometrics(int userId) {
return false;
}
@@ -130,26 +156,33 @@ public class LockSettingsServiceTestable extends LockSettingsService {
}
}
+ public MockInjector mInjector;
+
protected LockSettingsServiceTestable(Context context, LockPatternUtils lockPatternUtils,
LockSettingsStorage storage, FakeGateKeeperService gatekeeper, KeyStore keystore,
IStorageManager storageManager, IActivityManager mActivityManager,
SyntheticPasswordManager spManager, IAuthSecret authSecretService,
- FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager) {
+ FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager,
+ UserManagerInternal userManagerInternal, DeviceStateCache deviceStateCache,
+ FakeSettings settings) {
super(new MockInjector(context, storage, keystore, mActivityManager, lockPatternUtils,
- storageManager, spManager, authSecretService, gsiService,
- recoverableKeyStoreManager));
+ storageManager, spManager, gsiService,
+ recoverableKeyStoreManager, userManagerInternal, deviceStateCache, settings));
mGateKeeperService = gatekeeper;
mAuthSecretService = authSecretService;
}
@Override
- protected void tieProfileLockToParent(int userId, byte[] password) {
- mStorage.writeChildProfileLock(userId, password);
+ protected void tieProfileLockToParent(int userId, LockscreenCredential password) {
+ Parcel parcel = Parcel.obtain();
+ parcel.writeParcelable(password, 0);
+ mStorage.writeChildProfileLock(userId, parcel.marshall());
+ parcel.recycle();
}
@Override
- protected byte[] getDecryptedPasswordForTiedProfile(int userId) throws FileNotFoundException,
- KeyPermanentlyInvalidatedException {
+ protected LockscreenCredential getDecryptedPasswordForTiedProfile(int userId)
+ throws FileNotFoundException, KeyPermanentlyInvalidatedException {
byte[] storedData = mStorage.readChildProfileLock(userId);
if (storedData == null) {
throw new FileNotFoundException("Child profile lock file not found");
@@ -161,6 +194,13 @@ public class LockSettingsServiceTestable extends LockSettingsService {
} catch (RemoteException e) {
// shouldn't happen.
}
- return storedData;
+ Parcel parcel = Parcel.obtain();
+ try {
+ parcel.unmarshall(storedData, 0, storedData.length);
+ parcel.setDataPosition(0);
+ return (LockscreenCredential) parcel.readParcelable(null);
+ } finally {
+ parcel.recycle();
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index 7354ad4b9ac3..86ef31a392b5 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -16,14 +16,10 @@
package com.android.server.locksettings;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
-
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -39,6 +35,7 @@ import android.service.gatekeeper.GateKeeperResponse;
import androidx.test.filters.SmallTest;
import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.server.locksettings.FakeGateKeeperService.VerifyHandle;
import com.android.server.locksettings.LockSettingsStorage.CredentialHash;
@@ -61,60 +58,51 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
}
public void testCreatePasswordPrimaryUser() throws RemoteException {
- testCreateCredential(PRIMARY_USER_ID, "password", CREDENTIAL_TYPE_PASSWORD,
- PASSWORD_QUALITY_ALPHABETIC);
+ testCreateCredential(PRIMARY_USER_ID, newPassword("password"));
}
public void testCreatePasswordFailsWithoutLockScreen() throws RemoteException {
- testCreateCredentialFailsWithoutLockScreen(PRIMARY_USER_ID, "password",
- CREDENTIAL_TYPE_PASSWORD, PASSWORD_QUALITY_ALPHABETIC);
+ testCreateCredentialFailsWithoutLockScreen(PRIMARY_USER_ID, newPassword("password"));
}
public void testCreatePatternPrimaryUser() throws RemoteException {
- testCreateCredential(PRIMARY_USER_ID, "123456789", CREDENTIAL_TYPE_PATTERN,
- PASSWORD_QUALITY_SOMETHING);
+ testCreateCredential(PRIMARY_USER_ID, newPattern("123456789"));
}
public void testCreatePatternFailsWithoutLockScreen() throws RemoteException {
- testCreateCredentialFailsWithoutLockScreen(PRIMARY_USER_ID, "123456789",
- CREDENTIAL_TYPE_PATTERN, PASSWORD_QUALITY_SOMETHING);
+ testCreateCredentialFailsWithoutLockScreen(PRIMARY_USER_ID, newPattern("123456789"));
}
public void testChangePasswordPrimaryUser() throws RemoteException {
- testChangeCredentials(PRIMARY_USER_ID, "78963214", CREDENTIAL_TYPE_PATTERN,
- "asdfghjk", CREDENTIAL_TYPE_PASSWORD, PASSWORD_QUALITY_ALPHABETIC);
+ testChangeCredentials(PRIMARY_USER_ID, newPattern("78963214"), newPassword("asdfghjk"));
}
public void testChangePatternPrimaryUser() throws RemoteException {
- testChangeCredentials(PRIMARY_USER_ID, "!£$%^&*(())", CREDENTIAL_TYPE_PASSWORD,
- "1596321", CREDENTIAL_TYPE_PATTERN, PASSWORD_QUALITY_SOMETHING);
+ testChangeCredentials(PRIMARY_USER_ID, newPassword("!£$%^&*(())"), newPattern("1596321"));
}
public void testChangePasswordFailPrimaryUser() throws RemoteException {
final long sid = 1234;
- initializeStorageWithCredential(PRIMARY_USER_ID, "password", CREDENTIAL_TYPE_PASSWORD, sid);
+ initializeStorageWithCredential(PRIMARY_USER_ID, newPassword("password"), sid);
- assertFalse(mService.setLockCredential("newpwd".getBytes(), CREDENTIAL_TYPE_PASSWORD,
- "badpwd".getBytes(), PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false));
- assertVerifyCredentials(PRIMARY_USER_ID, "password", CREDENTIAL_TYPE_PASSWORD, sid);
+ assertFalse(mService.setLockCredential(newPassword("newpwd"), newPassword("badpwd"),
+ PRIMARY_USER_ID, false));
+ assertVerifyCredentials(PRIMARY_USER_ID, newPassword("password"), sid);
}
public void testClearPasswordPrimaryUser() throws RemoteException {
- final String PASSWORD = "password";
- initializeStorageWithCredential(PRIMARY_USER_ID, PASSWORD, CREDENTIAL_TYPE_PASSWORD, 1234);
- assertTrue(mService.setLockCredential(null, CREDENTIAL_TYPE_NONE, PASSWORD.getBytes(),
- PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID, false));
- assertFalse(mService.havePassword(PRIMARY_USER_ID));
- assertFalse(mService.havePattern(PRIMARY_USER_ID));
+ initializeStorageWithCredential(PRIMARY_USER_ID, newPassword("password"), 1234);
+ assertTrue(mService.setLockCredential(nonePassword(), newPassword("password"),
+ PRIMARY_USER_ID, false));
+ assertEquals(CREDENTIAL_TYPE_NONE, mService.getCredentialType(PRIMARY_USER_ID));
assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
}
public void testManagedProfileUnifiedChallenge() throws RemoteException {
- final String firstUnifiedPassword = "testManagedProfileUnifiedChallenge-pwd-1";
- final String secondUnifiedPassword = "testManagedProfileUnifiedChallenge-pwd-2";
- assertTrue(mService.setLockCredential(firstUnifiedPassword.getBytes(),
- LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
- null, PASSWORD_QUALITY_COMPLEX, PRIMARY_USER_ID, false));
+ final LockscreenCredential firstUnifiedPassword = newPassword("pwd-1");
+ final LockscreenCredential secondUnifiedPassword = newPassword("pwd-2");
+ assertTrue(mService.setLockCredential(firstUnifiedPassword,
+ nonePassword(), PRIMARY_USER_ID, false));
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID);
@@ -132,8 +120,8 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
mGateKeeperService.clearAuthToken(TURNED_OFF_PROFILE_USER_ID);
// verify credential
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- firstUnifiedPassword.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
- PRIMARY_USER_ID).getResponseCode());
+ firstUnifiedPassword, 0, PRIMARY_USER_ID)
+ .getResponseCode());
// Verify that we have a new auth token for the profile
assertNotNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID));
@@ -148,16 +136,15 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
*/
mStorageManager.setIgnoreBadUnlock(true);
// Change primary password and verify that profile SID remains
- assertTrue(mService.setLockCredential(secondUnifiedPassword.getBytes(),
- LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, firstUnifiedPassword.getBytes(),
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false));
+ assertTrue(mService.setLockCredential(
+ secondUnifiedPassword, firstUnifiedPassword, PRIMARY_USER_ID, false));
mStorageManager.setIgnoreBadUnlock(false);
assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
assertNull(mGateKeeperService.getAuthToken(TURNED_OFF_PROFILE_USER_ID));
// Clear unified challenge
- assertTrue(mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE,
- secondUnifiedPassword.getBytes(), PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID,
+ assertTrue(mService.setLockCredential(nonePassword(),
+ secondUnifiedPassword, PRIMARY_USER_ID,
false));
assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
assertEquals(0, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
@@ -165,19 +152,19 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
}
public void testManagedProfileSeparateChallenge() throws RemoteException {
- final String primaryPassword = "testManagedProfileSeparateChallenge-primary";
- final String profilePassword = "testManagedProfileSeparateChallenge-profile";
- assertTrue(mService.setLockCredential(primaryPassword.getBytes(),
- LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
- PASSWORD_QUALITY_COMPLEX, PRIMARY_USER_ID, false));
+ final LockscreenCredential primaryPassword = newPassword("primary");
+ final LockscreenCredential profilePassword = newPassword("profile");
+ assertTrue(mService.setLockCredential(primaryPassword,
+ nonePassword(),
+ PRIMARY_USER_ID, false));
/* Currently in LockSettingsService.setLockCredential, unlockUser() is called with the new
* credential as part of verifyCredential() before the new credential is committed in
* StorageManager. So we relax the check in our mock StorageManager to allow that.
*/
mStorageManager.setIgnoreBadUnlock(true);
- assertTrue(mService.setLockCredential(profilePassword.getBytes(),
- LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
- PASSWORD_QUALITY_COMPLEX, MANAGED_PROFILE_USER_ID, false));
+ assertTrue(mService.setLockCredential(profilePassword,
+ nonePassword(),
+ MANAGED_PROFILE_USER_ID, false));
mStorageManager.setIgnoreBadUnlock(false);
final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
@@ -190,81 +177,69 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
mGateKeeperService.clearAuthToken(MANAGED_PROFILE_USER_ID);
// verify primary credential
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- primaryPassword.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
- PRIMARY_USER_ID).getResponseCode());
+ primaryPassword, 0, PRIMARY_USER_ID)
+ .getResponseCode());
assertNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID));
// verify profile credential
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- profilePassword.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
- MANAGED_PROFILE_USER_ID).getResponseCode());
+ profilePassword, 0, MANAGED_PROFILE_USER_ID)
+ .getResponseCode());
assertNotNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID));
assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
// Change primary credential and make sure we don't affect profile
mStorageManager.setIgnoreBadUnlock(true);
- assertTrue(mService.setLockCredential("pwd".getBytes(),
- LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
- primaryPassword.getBytes(), PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false));
+ assertTrue(mService.setLockCredential(
+ newPassword("pwd"), primaryPassword, PRIMARY_USER_ID, false));
mStorageManager.setIgnoreBadUnlock(false);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- profilePassword.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
- MANAGED_PROFILE_USER_ID).getResponseCode());
+ profilePassword, 0, MANAGED_PROFILE_USER_ID)
+ .getResponseCode());
assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
}
public void testSetLockCredential_forPrimaryUser_sendsCredentials() throws Exception {
- final byte[] password = "password".getBytes();
-
assertTrue(mService.setLockCredential(
- password,
- CREDENTIAL_TYPE_PASSWORD,
- null,
- PASSWORD_QUALITY_ALPHABETIC,
+ newPassword("password"),
+ nonePassword(),
PRIMARY_USER_ID,
false));
verify(mRecoverableKeyStoreManager)
- .lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, password, PRIMARY_USER_ID);
+ .lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, "password".getBytes(),
+ PRIMARY_USER_ID);
}
public void testSetLockCredential_forProfileWithSeparateChallenge_sendsCredentials()
throws Exception {
- final byte[] pattern = "12345".getBytes();
-
assertTrue(mService.setLockCredential(
- pattern,
- CREDENTIAL_TYPE_PATTERN,
- null,
- PASSWORD_QUALITY_SOMETHING,
+ newPattern("12345"),
+ nonePassword(),
MANAGED_PROFILE_USER_ID,
false));
verify(mRecoverableKeyStoreManager)
- .lockScreenSecretChanged(CREDENTIAL_TYPE_PATTERN, pattern, MANAGED_PROFILE_USER_ID);
+ .lockScreenSecretChanged(CREDENTIAL_TYPE_PATTERN, "12345".getBytes(),
+ MANAGED_PROFILE_USER_ID);
}
public void testSetLockCredential_forProfileWithSeparateChallenge_updatesCredentials()
throws Exception {
- final String oldCredential = "12345";
- final byte[] newCredential = "newPassword".getBytes();
initializeStorageWithCredential(
MANAGED_PROFILE_USER_ID,
- oldCredential,
- CREDENTIAL_TYPE_PATTERN,
- PASSWORD_QUALITY_SOMETHING);
+ newPattern("12345"),
+ 1234);
assertTrue(mService.setLockCredential(
- newCredential,
- CREDENTIAL_TYPE_PASSWORD,
- oldCredential.getBytes(),
- PASSWORD_QUALITY_ALPHABETIC,
+ newPassword("newPassword"),
+ newPattern("12345"),
MANAGED_PROFILE_USER_ID,
false));
verify(mRecoverableKeyStoreManager)
- .lockScreenSecretChanged(
- CREDENTIAL_TYPE_PASSWORD, newCredential, MANAGED_PROFILE_USER_ID);
+ .lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, "newPassword".getBytes(),
+ MANAGED_PROFILE_USER_ID);
}
public void testSetLockCredential_forProfileWithUnifiedChallenge_doesNotSendRandomCredential()
@@ -272,10 +247,8 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
assertTrue(mService.setLockCredential(
- "12345".getBytes(),
- CREDENTIAL_TYPE_PATTERN,
- null,
- PASSWORD_QUALITY_SOMETHING,
+ newPattern("12345"),
+ nonePassword(),
PRIMARY_USER_ID,
false));
@@ -287,40 +260,35 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
public void
testSetLockCredential_forPrimaryUserWithUnifiedChallengeProfile_updatesBothCredentials()
throws Exception {
- final String oldCredential = "oldPassword";
- final byte[] newCredential = "newPassword".getBytes();
+ final LockscreenCredential oldCredential = newPassword("oldPassword");
+ final LockscreenCredential newCredential = newPassword("newPassword");
initializeStorageWithCredential(
- PRIMARY_USER_ID, oldCredential, CREDENTIAL_TYPE_PASSWORD, 1234);
+ PRIMARY_USER_ID, oldCredential, 1234);
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
assertTrue(mService.setLockCredential(
newCredential,
- CREDENTIAL_TYPE_PASSWORD,
- oldCredential.getBytes(),
- PASSWORD_QUALITY_ALPHABETIC,
+ oldCredential,
PRIMARY_USER_ID,
false));
verify(mRecoverableKeyStoreManager)
- .lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, newCredential, PRIMARY_USER_ID);
+ .lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, newCredential.getCredential(),
+ PRIMARY_USER_ID);
verify(mRecoverableKeyStoreManager)
- .lockScreenSecretChanged(
- CREDENTIAL_TYPE_PASSWORD, newCredential, MANAGED_PROFILE_USER_ID);
+ .lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, newCredential.getCredential(),
+ MANAGED_PROFILE_USER_ID);
}
public void
testSetLockCredential_forPrimaryUserWithUnifiedChallengeProfile_removesBothCredentials()
throws Exception {
- final String oldCredential = "oldPassword";
- initializeStorageWithCredential(
- PRIMARY_USER_ID, oldCredential, CREDENTIAL_TYPE_PASSWORD, 1234);
+ initializeStorageWithCredential(PRIMARY_USER_ID, newPassword("oldPassword"), 1234);
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
assertTrue(mService.setLockCredential(
- null,
- CREDENTIAL_TYPE_NONE,
- oldCredential.getBytes(),
- PASSWORD_QUALITY_UNSPECIFIED,
+ nonePassword(),
+ newPassword("oldPassword"),
PRIMARY_USER_ID,
false));
@@ -330,43 +298,52 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
.lockScreenSecretChanged(CREDENTIAL_TYPE_NONE, null, MANAGED_PROFILE_USER_ID);
}
+ public void testSetLockCredential_nullCredential_removeBiometrics() throws RemoteException {
+ initializeStorageWithCredential(
+ PRIMARY_USER_ID,
+ newPattern("123654"),
+ 1234);
+ mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
+
+ mService.setLockCredential(nonePassword(), newPattern("123654"), PRIMARY_USER_ID, false);
+
+ // Verify fingerprint is removed
+ verify(mFingerprintManager).remove(any(), eq(PRIMARY_USER_ID), any());
+ verify(mFaceManager).remove(any(), eq(PRIMARY_USER_ID), any());
+
+ verify(mFingerprintManager).remove(any(), eq(MANAGED_PROFILE_USER_ID), any());
+ verify(mFaceManager).remove(any(), eq(MANAGED_PROFILE_USER_ID), any());
+ }
+
public void testSetLockCredential_forUnifiedToSeparateChallengeProfile_sendsNewCredentials()
throws Exception {
- final String parentPassword = "parentPassword";
- final byte[] profilePassword = "profilePassword".getBytes();
- initializeStorageWithCredential(
- PRIMARY_USER_ID, parentPassword, CREDENTIAL_TYPE_PASSWORD, 1234);
+ final LockscreenCredential parentPassword = newPassword("parentPassword");
+ final LockscreenCredential profilePassword = newPassword("profilePassword");
+ initializeStorageWithCredential(PRIMARY_USER_ID, parentPassword, 1234);
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
assertTrue(mService.setLockCredential(
profilePassword,
- CREDENTIAL_TYPE_PASSWORD,
- null,
- PASSWORD_QUALITY_ALPHABETIC,
+ nonePassword(),
MANAGED_PROFILE_USER_ID,
false));
verify(mRecoverableKeyStoreManager)
- .lockScreenSecretChanged(
- CREDENTIAL_TYPE_PASSWORD, profilePassword, MANAGED_PROFILE_USER_ID);
+ .lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, profilePassword.getCredential(),
+ MANAGED_PROFILE_USER_ID);
}
public void
testSetLockCredential_forSeparateToUnifiedChallengeProfile_doesNotSendRandomCredential()
throws Exception {
- final String parentPassword = "parentPassword";
- final String profilePassword = "12345";
- initializeStorageWithCredential(
- PRIMARY_USER_ID, parentPassword, CREDENTIAL_TYPE_PASSWORD, 1234);
+ final LockscreenCredential parentPassword = newPassword("parentPassword");
+ final LockscreenCredential profilePassword = newPattern("12345");
+ initializeStorageWithCredential(PRIMARY_USER_ID, parentPassword, 1234);
// Create and verify separate profile credentials.
- testCreateCredential(
- MANAGED_PROFILE_USER_ID,
- profilePassword,
- CREDENTIAL_TYPE_PATTERN,
- PASSWORD_QUALITY_SOMETHING);
+ testCreateCredential(MANAGED_PROFILE_USER_ID, profilePassword);
mService.setSeparateProfileChallengeEnabled(
- MANAGED_PROFILE_USER_ID, false, profilePassword.getBytes());
+ MANAGED_PROFILE_USER_ID, false, profilePassword);
// Called once for setting the initial separate profile credentials and not again during
// unification.
@@ -375,132 +352,121 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
}
public void testVerifyCredential_forPrimaryUser_sendsCredentials() throws Exception {
- final String password = "password";
- initializeStorageWithCredential(PRIMARY_USER_ID, password, CREDENTIAL_TYPE_PASSWORD, 1234);
+ final LockscreenCredential password = newPassword("password");
+ initializeStorageWithCredential(PRIMARY_USER_ID, password, 1234);
reset(mRecoverableKeyStoreManager);
- mService.verifyCredential(
- password.getBytes(), CREDENTIAL_TYPE_PASSWORD, 1, PRIMARY_USER_ID);
+ mService.verifyCredential(password, 1, PRIMARY_USER_ID);
verify(mRecoverableKeyStoreManager)
.lockScreenSecretAvailable(
- CREDENTIAL_TYPE_PASSWORD, password.getBytes(), PRIMARY_USER_ID);
+ CREDENTIAL_TYPE_PASSWORD, password.getCredential(), PRIMARY_USER_ID);
}
public void testVerifyCredential_forProfileWithSeparateChallenge_sendsCredentials()
throws Exception {
- final byte[] pattern = "12345".getBytes();
+ final LockscreenCredential pattern = newPattern("12345");
assertTrue(mService.setLockCredential(
pattern,
- CREDENTIAL_TYPE_PATTERN,
- null,
- PASSWORD_QUALITY_SOMETHING,
+ nonePassword(),
MANAGED_PROFILE_USER_ID,
false));
reset(mRecoverableKeyStoreManager);
- mService.verifyCredential(pattern, CREDENTIAL_TYPE_PATTERN, 1, MANAGED_PROFILE_USER_ID);
+ mService.verifyCredential(pattern, 1, MANAGED_PROFILE_USER_ID);
verify(mRecoverableKeyStoreManager)
.lockScreenSecretAvailable(
- CREDENTIAL_TYPE_PATTERN, pattern, MANAGED_PROFILE_USER_ID);
+ CREDENTIAL_TYPE_PATTERN, pattern.getCredential(), MANAGED_PROFILE_USER_ID);
}
public void
testVerifyCredential_forPrimaryUserWithUnifiedChallengeProfile_sendsCredentialsForBoth()
throws Exception {
- final String pattern = "12345";
- initializeStorageWithCredential(PRIMARY_USER_ID, pattern, CREDENTIAL_TYPE_PATTERN, 1234);
+ final LockscreenCredential pattern = newPattern("12345");
+ initializeStorageWithCredential(PRIMARY_USER_ID, pattern, 1234);
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
reset(mRecoverableKeyStoreManager);
- mService.verifyCredential(pattern.getBytes(), CREDENTIAL_TYPE_PATTERN, 1, PRIMARY_USER_ID);
+ mService.verifyCredential(pattern, 1, PRIMARY_USER_ID);
// Parent sends its credentials for both the parent and profile.
verify(mRecoverableKeyStoreManager)
.lockScreenSecretAvailable(
- CREDENTIAL_TYPE_PATTERN, pattern.getBytes(), PRIMARY_USER_ID);
+ CREDENTIAL_TYPE_PATTERN, pattern.getCredential(), PRIMARY_USER_ID);
verify(mRecoverableKeyStoreManager)
.lockScreenSecretAvailable(
- CREDENTIAL_TYPE_PATTERN, pattern.getBytes(), MANAGED_PROFILE_USER_ID);
+ CREDENTIAL_TYPE_PATTERN, pattern.getCredential(), MANAGED_PROFILE_USER_ID);
// Profile doesn't send its own random credentials.
verify(mRecoverableKeyStoreManager, never())
.lockScreenSecretAvailable(
eq(CREDENTIAL_TYPE_PASSWORD), any(), eq(MANAGED_PROFILE_USER_ID));
}
- private void testCreateCredential(int userId, String credential, int type, int quality)
+ private void testCreateCredential(int userId, LockscreenCredential credential)
throws RemoteException {
- assertTrue(mService.setLockCredential(credential.getBytes(), type, null, quality,
- userId, false));
- assertVerifyCredentials(userId, credential, type, -1);
+ assertTrue(mService.setLockCredential(credential, nonePassword(), userId, false));
+ assertVerifyCredentials(userId, credential, -1);
}
private void testCreateCredentialFailsWithoutLockScreen(
- int userId, String credential, int type, int quality) throws RemoteException {
+ int userId, LockscreenCredential credential) throws RemoteException {
mHasSecureLockScreen = false;
try {
- mService.setLockCredential(credential.getBytes(), type, null, quality,
- userId, false);
+ mService.setLockCredential(credential, null, userId, false);
fail("An exception should have been thrown.");
} catch (UnsupportedOperationException e) {
// Success - the exception was expected.
}
- assertFalse(mService.havePassword(userId));
- assertFalse(mService.havePattern(userId));
+ assertEquals(CREDENTIAL_TYPE_NONE, mService.getCredentialType(userId));
}
- private void testChangeCredentials(int userId, String newCredential, int newType,
- String oldCredential, int oldType, int quality) throws RemoteException {
+ private void testChangeCredentials(int userId, LockscreenCredential newCredential,
+ LockscreenCredential oldCredential) throws RemoteException {
final long sid = 1234;
- initializeStorageWithCredential(userId, oldCredential, oldType, sid);
- assertTrue(mService.setLockCredential(newCredential.getBytes(), newType,
- oldCredential.getBytes(), quality, userId, false));
- assertVerifyCredentials(userId, newCredential, newType, sid);
+ initializeStorageWithCredential(userId, oldCredential, sid);
+ assertTrue(mService.setLockCredential(newCredential, oldCredential, userId, false));
+ assertVerifyCredentials(userId, newCredential, sid);
}
- private void assertVerifyCredentials(int userId, String credential, int type, long sid)
+ private void assertVerifyCredentials(int userId, LockscreenCredential credential, long sid)
throws RemoteException{
final long challenge = 54321;
- VerifyCredentialResponse response = mService.verifyCredential(credential.getBytes(),
- type, challenge, userId);
+ VerifyCredentialResponse response = mService.verifyCredential(credential,
+ challenge, userId);
assertEquals(GateKeeperResponse.RESPONSE_OK, response.getResponseCode());
if (sid != -1) assertEquals(sid, mGateKeeperService.getSecureUserId(userId));
- final int incorrectType;
- if (type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) {
- assertTrue(mService.havePassword(userId));
- assertFalse(mService.havePattern(userId));
- incorrectType = LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
- } else if (type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN){
- assertFalse(mService.havePassword(userId));
- assertTrue(mService.havePattern(userId));
- incorrectType = LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+ if (credential.isPassword()) {
+ assertEquals(CREDENTIAL_TYPE_PASSWORD, mService.getCredentialType(userId));
+ } else if (credential.isPin()) {
+ assertEquals(CREDENTIAL_TYPE_PIN, mService.getCredentialType(userId));
+ } else if (credential.isPattern()) {
+ assertEquals(CREDENTIAL_TYPE_PATTERN, mService.getCredentialType(userId));
} else {
- assertFalse(mService.havePassword(userId));
- assertFalse(mService.havePassword(userId));
- incorrectType = LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+ assertEquals(CREDENTIAL_TYPE_NONE, mService.getCredentialType(userId));
}
- // check for bad type
- assertEquals(GateKeeperResponse.RESPONSE_ERROR, mService.verifyCredential(
- credential.getBytes(), incorrectType, challenge, userId).getResponseCode());
// check for bad credential
+ final LockscreenCredential badCredential;
+ if (!credential.isNone()) {
+ badCredential = credential.duplicate();
+ badCredential.getCredential()[0] ^= 1;
+ } else {
+ badCredential = LockscreenCredential.createPin("0");
+ }
assertEquals(GateKeeperResponse.RESPONSE_ERROR, mService.verifyCredential(
- ("0" + credential).getBytes(), type, challenge, userId).getResponseCode());
+ badCredential, challenge, userId).getResponseCode());
}
- private void initializeStorageWithCredential(int userId, String credential, int type, long sid)
- throws RemoteException {
- byte[] credentialBytes = credential == null ? null : credential.getBytes();
- byte[] oldHash = new VerifyHandle(credential.getBytes(), sid).toBytes();
+ private void initializeStorageWithCredential(int userId, LockscreenCredential credential,
+ long sid) throws RemoteException {
+ byte[] oldHash = new VerifyHandle(credential.getCredential(), sid).toBytes();
if (mService.shouldMigrateToSyntheticPasswordLocked(userId)) {
- mService.initializeSyntheticPasswordLocked(oldHash, credentialBytes, type,
- type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD ? PASSWORD_QUALITY_ALPHABETIC
- : PASSWORD_QUALITY_SOMETHING, userId);
+ mService.initializeSyntheticPasswordLocked(oldHash, credential, userId);
} else {
- if (type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) {
+ if (credential.isPassword() || credential.isPin()) {
mStorage.writeCredentialHash(CredentialHash.create(oldHash,
LockPatternUtils.CREDENTIAL_TYPE_PASSWORD), userId);
} else {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
index c00d33b431c3..8c2d172cd2da 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
@@ -17,9 +17,10 @@
package com.android.server.locksettings;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
-
-import static com.android.internal.widget.LockPatternUtils.stringToPattern;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
import static junit.framework.Assert.assertEquals;
@@ -35,6 +36,8 @@ import static java.io.FileDescriptor.in;
import static java.io.FileDescriptor.out;
import android.app.ActivityManager;
+import android.app.admin.PasswordMetrics;
+import android.app.admin.PasswordPolicy;
import android.content.Context;
import android.os.Binder;
import android.os.Handler;
@@ -48,6 +51,8 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternView;
+import com.android.internal.widget.LockscreenCredential;
import org.junit.Before;
import org.junit.Test;
@@ -55,6 +60,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.List;
+
/**
* Test class for {@link LockSettingsShellCommand}.
*
@@ -85,26 +92,52 @@ public class LockSettingsShellCommandTest {
@Test
public void testWrongPassword() throws Exception {
+ when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
- when(mLockPatternUtils.checkPassword("1234".getBytes(), mUserId)).thenReturn(false);
+ when(mLockPatternUtils.checkCredential(
+ LockscreenCredential.createPassword("1234"), mUserId, null)).thenReturn(false);
assertEquals(-1, mCommand.exec(mBinder, in, out, err,
new String[] { "set-pin", "--old", "1234" },
mShellCallback, mResultReceiver));
- verify(mLockPatternUtils, never()).saveLockPassword(any(byte[].class), any(byte[].class),
- anyInt(), anyInt());
+ verify(mLockPatternUtils, never()).setLockCredential(any(), any(), anyInt());
}
@Test
public void testChangePin() throws Exception {
+ when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
- when(mLockPatternUtils.checkPassword("1234".getBytes(), mUserId)).thenReturn(true);
+ when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn(
+ PASSWORD_QUALITY_NUMERIC);
+ when(mLockPatternUtils.checkCredential(
+ LockscreenCredential.createPin("1234"), mUserId, null)).thenReturn(true);
+ when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
+ .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_NUMERIC));
assertEquals(0, mCommand.exec(new Binder(), in, out, err,
new String[] { "set-pin", "--old", "1234", "4321" },
mShellCallback, mResultReceiver));
- verify(mLockPatternUtils).saveLockPassword("4321".getBytes(), "1234".getBytes(),
- PASSWORD_QUALITY_NUMERIC, mUserId);
+ verify(mLockPatternUtils).setLockCredential(
+ LockscreenCredential.createPin("4321"),
+ LockscreenCredential.createPin("1234"),
+ mUserId);
+ }
+
+ @Test
+ public void testChangePin_nonCompliant() throws Exception {
+ when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
+ when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
+ when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
+ when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn(
+ PASSWORD_QUALITY_NUMERIC);
+ when(mLockPatternUtils.checkCredential(
+ LockscreenCredential.createPin("1234"), mUserId, null)).thenReturn(true);
+ when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
+ .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_ALPHABETIC));
+ assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
+ new String[] { "set-pin", "--old", "1234", "4321" },
+ mShellCallback, mResultReceiver));
+ verify(mLockPatternUtils, never()).setLockCredential(any(), any(), anyInt());
}
@Test
@@ -119,14 +152,39 @@ public class LockSettingsShellCommandTest {
@Test
public void testChangePassword() throws Exception {
+ when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
- when(mLockPatternUtils.checkPassword("1234".getBytes(), mUserId)).thenReturn(true);
+ when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn(
+ PASSWORD_QUALITY_ALPHABETIC);
+ when(mLockPatternUtils.checkCredential(
+ LockscreenCredential.createPassword("1234"), mUserId, null)).thenReturn(true);
+ when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
+ .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_ALPHABETIC));
assertEquals(0, mCommand.exec(new Binder(), in, out, err,
- new String[] { "set-password", "--old", "1234", "4321" },
+ new String[] { "set-password", "--old", "1234", "abcd" },
+ mShellCallback, mResultReceiver));
+ verify(mLockPatternUtils).setLockCredential(
+ LockscreenCredential.createPassword("abcd"),
+ LockscreenCredential.createPassword("1234"),
+ mUserId);
+ }
+
+ @Test
+ public void testChangePassword_nonCompliant() throws Exception {
+ when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
+ when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
+ when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
+ when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn(
+ PASSWORD_QUALITY_ALPHABETIC);
+ when(mLockPatternUtils.checkCredential(
+ LockscreenCredential.createPassword("1234"), mUserId, null)).thenReturn(true);
+ when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
+ .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_COMPLEX));
+ assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
+ new String[] { "set-password", "--old", "1234", "weakpassword" },
mShellCallback, mResultReceiver));
- verify(mLockPatternUtils).saveLockPassword("4321".getBytes(), "1234".getBytes(),
- PASSWORD_QUALITY_ALPHABETIC, mUserId);
+ verify(mLockPatternUtils, never()).setLockCredential(any(), any(), anyInt());
}
@Test
@@ -141,17 +199,40 @@ public class LockSettingsShellCommandTest {
@Test
public void testChangePattern() throws Exception {
+ when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
- when(mLockPatternUtils.checkPattern(stringToPattern("1234"), mUserId)).thenReturn(true);
+ when(mLockPatternUtils.checkCredential(
+ LockscreenCredential.createPattern(stringToPattern("1234")),
+ mUserId, null)).thenReturn(true);
+ when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
+ .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_SOMETHING));
assertEquals(0, mCommand.exec(new Binder(), in, out, err,
new String[] { "set-pattern", "--old", "1234", "4321" },
mShellCallback, mResultReceiver));
- verify(mLockPatternUtils).saveLockPattern(stringToPattern("4321"), "1234".getBytes(),
+ verify(mLockPatternUtils).setLockCredential(
+ LockscreenCredential.createPattern(stringToPattern("4321")),
+ LockscreenCredential.createPattern(stringToPattern("1234")),
mUserId);
}
@Test
+ public void testChangePattern_nonCompliant() throws Exception {
+ when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
+ when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
+ when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
+ when(mLockPatternUtils.checkCredential(
+ LockscreenCredential.createPattern(stringToPattern("1234")),
+ mUserId, null)).thenReturn(true);
+ when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
+ .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_NUMERIC));
+ assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
+ new String[] { "set-pattern", "--old", "1234", "4321" },
+ mShellCallback, mResultReceiver));
+ verify(mLockPatternUtils, never()).setLockCredential(any(), any(), anyInt());
+ }
+
+ @Test
public void testChangePattern_noLockScreen() throws Exception {
when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(false);
assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
@@ -163,12 +244,46 @@ public class LockSettingsShellCommandTest {
@Test
public void testClear() throws Exception {
+ when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
- when(mLockPatternUtils.checkPattern(stringToPattern("1234"), mUserId)).thenReturn(true);
+ when(mLockPatternUtils.checkCredential(
+ LockscreenCredential.createPattern(stringToPattern("1234")),
+ mUserId, null)).thenReturn(true);
+ when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
+ .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_UNSPECIFIED));
assertEquals(0, mCommand.exec(new Binder(), in, out, err,
new String[] { "clear", "--old", "1234" },
mShellCallback, mResultReceiver));
- verify(mLockPatternUtils).clearLock("1234".getBytes(), mUserId);
+ verify(mLockPatternUtils).setLockCredential(
+ LockscreenCredential.createNone(),
+ LockscreenCredential.createPattern(stringToPattern("1234")),
+ mUserId);
+ }
+
+ @Test
+ public void testClear_nonCompliant() throws Exception {
+ when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
+ when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
+ when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
+ when(mLockPatternUtils.checkCredential(
+ LockscreenCredential.createPattern(stringToPattern("1234")),
+ mUserId, null)).thenReturn(true);
+ when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
+ .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_SOMETHING));
+ assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
+ new String[] { "clear", "--old", "1234" },
+ mShellCallback, mResultReceiver));
+ verify(mLockPatternUtils, never()).setLockCredential(any(), any(), anyInt());
+ }
+
+ private List<LockPatternView.Cell> stringToPattern(String str) {
+ return LockPatternUtils.byteArrayToPattern(str.getBytes());
+ }
+
+ private PasswordMetrics metricsForAdminQuality(int quality) {
+ PasswordPolicy policy = new PasswordPolicy();
+ policy.quality = quality;
+ return policy.getMinMetrics();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
index bc61c582303a..1581d9ac1811 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
@@ -16,20 +16,51 @@
package com.android.server.locksettings;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+
import android.content.Context;
import com.android.server.PersistentDataBlockManagerInternal;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
import java.io.File;
+import java.util.Arrays;
public class LockSettingsStorageTestable extends LockSettingsStorage {
public File mStorageDir;
- public PersistentDataBlockManagerInternal mPersistentDataBlock;
+ public PersistentDataBlockManagerInternal mPersistentDataBlockManager;
+ private byte[] mPersistentData;
public LockSettingsStorageTestable(Context context, File storageDir) {
super(context);
mStorageDir = storageDir;
+ mPersistentDataBlockManager = mock(PersistentDataBlockManagerInternal.class);
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ byte[] handle = (byte[]) invocation.getArguments()[0];
+ if (handle != null) {
+ mPersistentData = Arrays.copyOf(handle, handle.length);
+ } else {
+ mPersistentData = null;
+ }
+ return null;
+ }
+ }).when(mPersistentDataBlockManager).setFrpCredentialHandle(any());
+ // For some reasons, simply mocking getFrpCredentialHandle() with
+ // when(mPersistentDataBlockManager.getFrpCredentialHandle()).thenReturn(mPersistentData)
+ // does not work, I had to use the long-winded way below.
+ doAnswer(new Answer<byte[]>() {
+ @Override
+ public byte[] answer(InvocationOnMock invocation) throws Throwable {
+ return mPersistentData;
+ }
+ }).when(mPersistentDataBlockManager).getFrpCredentialHandle();
}
@Override
@@ -57,8 +88,8 @@ public class LockSettingsStorageTestable extends LockSettingsStorage {
}
@Override
- public PersistentDataBlockManagerInternal getPersistentDataBlock() {
- return mPersistentDataBlock;
+ PersistentDataBlockManagerInternal getPersistentDataBlockManager() {
+ return mPersistentDataBlockManager;
}
private File makeDirs(File baseDir, String filePath) {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
index 8e0d7be5f44f..7a18431cb8e1 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
@@ -24,8 +24,11 @@ import android.app.KeyguardManager;
import android.app.NotificationManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
+import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.database.sqlite.SQLiteDatabase;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
import android.os.FileUtils;
import android.os.SystemClock;
import android.os.UserManager;
@@ -86,7 +89,9 @@ public class LockSettingsStorageTests extends AndroidTestCase {
MockLockSettingsContext context = new MockLockSettingsContext(getContext(), mockUserManager,
mock(NotificationManager.class), mock(DevicePolicyManager.class),
- mock(StorageManager.class), mock(TrustManager.class), mock(KeyguardManager.class));
+ mock(StorageManager.class), mock(TrustManager.class), mock(KeyguardManager.class),
+ mock(FingerprintManager.class), mock(FaceManager.class),
+ mock(PackageManager.class));
mStorage = new LockSettingsStorageTestable(context,
new File(getContext().getFilesDir(), "locksettings"));
mStorage.setDatabaseOnCreateCallback(new LockSettingsStorage.Callback() {
@@ -235,12 +240,12 @@ public class LockSettingsStorageTests extends AndroidTestCase {
writePasswordBytes(PASSWORD_0, 10);
writePatternBytes(PATTERN_0, 20);
- assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN,
mStorage.readCredentialHash(10).type);
assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
mStorage.readCredentialHash(20).type);
mStorage.clearCache();
- assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN,
mStorage.readCredentialHash(10).type);
assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
mStorage.readCredentialHash(20).type);
@@ -347,20 +352,20 @@ public class LockSettingsStorageTests extends AndroidTestCase {
}
public void testPersistentDataBlock_unavailable() {
- mStorage.mPersistentDataBlock = null;
+ mStorage.mPersistentDataBlockManager = null;
assertSame(PersistentData.NONE, mStorage.readPersistentDataBlock());
}
public void testPersistentDataBlock_empty() {
- mStorage.mPersistentDataBlock = mock(PersistentDataBlockManagerInternal.class);
+ mStorage.mPersistentDataBlockManager = mock(PersistentDataBlockManagerInternal.class);
assertSame(PersistentData.NONE, mStorage.readPersistentDataBlock());
}
public void testPersistentDataBlock_withData() {
- mStorage.mPersistentDataBlock = mock(PersistentDataBlockManagerInternal.class);
- when(mStorage.mPersistentDataBlock.getFrpCredentialHandle())
+ mStorage.mPersistentDataBlockManager = mock(PersistentDataBlockManagerInternal.class);
+ when(mStorage.mPersistentDataBlockManager.getFrpCredentialHandle())
.thenReturn(PersistentData.toBytes(PersistentData.TYPE_SP_WEAVER, SOME_USER_ID,
DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, PAYLOAD));
@@ -373,8 +378,8 @@ public class LockSettingsStorageTests extends AndroidTestCase {
}
public void testPersistentDataBlock_exception() {
- mStorage.mPersistentDataBlock = mock(PersistentDataBlockManagerInternal.class);
- when(mStorage.mPersistentDataBlock.getFrpCredentialHandle())
+ mStorage.mPersistentDataBlockManager = mock(PersistentDataBlockManagerInternal.class);
+ when(mStorage.mPersistentDataBlockManager.getFrpCredentialHandle())
.thenThrow(new IllegalStateException("oops"));
assertSame(PersistentData.NONE, mStorage.readPersistentDataBlock());
}
@@ -429,35 +434,6 @@ public class LockSettingsStorageTests extends AndroidTestCase {
assertEquals(2, PersistentData.TYPE_SP_WEAVER);
}
- public void testCredentialHash_serializeUnserialize() {
- byte[] serialized = CredentialHash.create(
- PAYLOAD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD).toBytes();
- CredentialHash deserialized = CredentialHash.fromBytes(serialized);
-
- assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, deserialized.type);
- assertArrayEquals(PAYLOAD, deserialized.hash);
- }
-
- public void testCredentialHash_unserialize_versionGatekeeper() {
- // This test ensures that we can read serialized VERSION_GATEKEEPER CredentialHashes
- // even if we change the wire format in the future.
- byte[] serialized = new byte[] {
- 1, /* VERSION_GATEKEEPER */
- 2, /* CREDENTIAL_TYPE_PASSWORD */
- 0, 0, 0, 5, /* hash length */
- 1, 2, -1, -2, 33, /* hash */
- };
- CredentialHash deserialized = CredentialHash.fromBytes(serialized);
-
- assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, deserialized.type);
- assertArrayEquals(PAYLOAD, deserialized.hash);
-
- // Make sure the constants we use on the wire do not change.
- assertEquals(-1, LockPatternUtils.CREDENTIAL_TYPE_NONE);
- assertEquals(1, LockPatternUtils.CREDENTIAL_TYPE_PATTERN);
- assertEquals(2, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD);
- }
-
private static void assertArrayEquals(byte[] expected, byte[] actual) {
if (!Arrays.equals(expected, actual)) {
fail("expected:<" + Arrays.toString(expected) +
@@ -477,7 +453,7 @@ public class LockSettingsStorageTests extends AndroidTestCase {
private void assertPasswordBytes(byte[] password, int userId) {
CredentialHash cred = mStorage.readCredentialHash(userId);
- assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, cred.type);
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN, cred.type);
assertArrayEquals(password, cred.hash);
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockscreenFrpTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenFrpTest.java
new file mode 100644
index 000000000000..df719b6d3d21
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenFrpTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.locksettings;
+
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
+import static com.android.internal.widget.LockPatternUtils.USER_FRP;
+
+import android.app.admin.DevicePolicyManager;
+
+import com.android.internal.widget.VerifyCredentialResponse;
+import com.android.server.locksettings.LockSettingsStorage.PersistentData;
+
+
+/** Test setting a lockscreen credential and then verify it under USER_FRP */
+public class LockscreenFrpTest extends BaseLockSettingsServiceTests {
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ // FRP credential can only be verified prior to provisioning
+ mSettings.setDeviceProvisioned(false);
+ }
+
+ public void testFrpCredential_setPin() {
+ mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID, false);
+
+ assertEquals(CREDENTIAL_TYPE_PIN, mService.getCredentialType(USER_FRP));
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(newPin("1234"), 0, USER_FRP).getResponseCode());
+ }
+
+ public void testFrpCredential_setPattern() {
+ mService.setLockCredential(newPattern("4321"), nonePassword(), PRIMARY_USER_ID, false);
+
+ assertEquals(CREDENTIAL_TYPE_PATTERN, mService.getCredentialType(USER_FRP));
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(newPattern("4321"), 0, USER_FRP).getResponseCode());
+ }
+
+ public void testFrpCredential_setPassword() {
+ mService.setLockCredential(newPassword("4321"), nonePassword(), PRIMARY_USER_ID, false);
+
+ assertEquals(CREDENTIAL_TYPE_PASSWORD, mService.getCredentialType(USER_FRP));
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(newPassword("4321"), 0, USER_FRP).getResponseCode());
+ }
+
+ public void testFrpCredential_changeCredential() {
+ mService.setLockCredential(newPassword("1234"), nonePassword(), PRIMARY_USER_ID, false);
+ mService.setLockCredential(newPattern("5678"), newPassword("1234"), PRIMARY_USER_ID, false);
+
+ assertEquals(CREDENTIAL_TYPE_PATTERN, mService.getCredentialType(USER_FRP));
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(newPattern("5678"), 0, USER_FRP).getResponseCode());
+ }
+
+ public void testFrpCredential_removeCredential() {
+ mService.setLockCredential(newPassword("1234"), nonePassword(), PRIMARY_USER_ID, false);
+ assertEquals(CREDENTIAL_TYPE_PASSWORD, mService.getCredentialType(USER_FRP));
+
+ mService.setLockCredential(nonePassword(), newPassword("1234"), PRIMARY_USER_ID, false);
+ assertEquals(CREDENTIAL_TYPE_NONE, mService.getCredentialType(USER_FRP));
+ }
+
+ public void testFrpCredential_cannotVerifyAfterProvsioning() {
+ mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID, false);
+
+ mSettings.setDeviceProvisioned(true);
+ assertEquals(VerifyCredentialResponse.RESPONSE_ERROR,
+ mService.verifyCredential(newPin("1234"), 0, USER_FRP).getResponseCode());
+ }
+
+ public void testFrpCredential_legacyPinTypePersistentData() {
+ mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID, false);
+ PersistentData data = mStorage.readPersistentDataBlock();
+ // Tweak the existing persistent data to make it look like one with legacy credential type
+ assertEquals(CREDENTIAL_TYPE_PIN, data.payload[3]);
+ data.payload[3] = CREDENTIAL_TYPE_PASSWORD_OR_PIN;
+ mStorage.writePersistentDataBlock(data.type, data.userId,
+ DevicePolicyManager.PASSWORD_QUALITY_NUMERIC, data.payload);
+
+ assertEquals(CREDENTIAL_TYPE_PIN, mService.getCredentialType(USER_FRP));
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(newPin("1234"), 0, USER_FRP).getResponseCode());
+
+ }
+
+ public void testFrpCredential_legacyPasswordTypePersistentData() {
+ mService.setLockCredential(newPassword("1234"), nonePassword(), PRIMARY_USER_ID, false);
+ PersistentData data = mStorage.readPersistentDataBlock();
+ // Tweak the existing persistent data to make it look like one with legacy credential type
+ assertEquals(CREDENTIAL_TYPE_PASSWORD, data.payload[3]);
+ data.payload[3] = CREDENTIAL_TYPE_PASSWORD_OR_PIN;
+ mStorage.writePersistentDataBlock(data.type, data.userId,
+ DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, data.payload);
+
+ assertEquals(CREDENTIAL_TYPE_PASSWORD, mService.getCredentialType(USER_FRP));
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(newPin("1234"), 0, USER_FRP).getResponseCode());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java b/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java
index b33253264317..2b9a05c3ef63 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java
@@ -23,6 +23,8 @@ import android.app.trust.TrustManager;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManager;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
import android.os.UserManager;
import android.os.storage.StorageManager;
@@ -34,11 +36,15 @@ public class MockLockSettingsContext extends ContextWrapper {
private StorageManager mStorageManager;
private TrustManager mTrustManager;
private KeyguardManager mKeyguardManager;
+ private FingerprintManager mFingerprintManager;
+ private FaceManager mFaceManager;
+ private PackageManager mPackageManager;
public MockLockSettingsContext(Context base, UserManager userManager,
NotificationManager notificationManager, DevicePolicyManager devicePolicyManager,
StorageManager storageManager, TrustManager trustManager,
- KeyguardManager keyguardManager) {
+ KeyguardManager keyguardManager, FingerprintManager fingerprintManager,
+ FaceManager faceManager, PackageManager packageManager) {
super(base);
mUserManager = userManager;
mNotificationManager = notificationManager;
@@ -46,6 +52,9 @@ public class MockLockSettingsContext extends ContextWrapper {
mStorageManager = storageManager;
mTrustManager = trustManager;
mKeyguardManager = keyguardManager;
+ mFingerprintManager = fingerprintManager;
+ mFaceManager = faceManager;
+ mPackageManager = packageManager;
}
@Override
@@ -62,12 +71,21 @@ public class MockLockSettingsContext extends ContextWrapper {
return mTrustManager;
} else if (KEYGUARD_SERVICE.equals(name)) {
return mKeyguardManager;
+ } else if (FINGERPRINT_SERVICE.equals(name)) {
+ return mFingerprintManager;
+ } else if (FACE_SERVICE.equals(name)) {
+ return mFaceManager;
} else {
throw new RuntimeException("System service not mocked: " + name);
}
}
@Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+
+ @Override
public void enforceCallingOrSelfPermission(String permission, String message) {
// Skip permission checks for unit tests.
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 127cf4970725..89a279c566e0 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -16,11 +16,9 @@
package com.android.server.locksettings;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
-
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN;
import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_KEY;
import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY;
@@ -29,6 +27,7 @@ import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.admin.PasswordMetrics;
import android.os.RemoteException;
@@ -37,7 +36,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
-import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
@@ -70,15 +69,14 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
public void testPasswordBasedSyntheticPassword() throws RemoteException {
final int USER_ID = 10;
- final byte[] password = "user-password".getBytes();
- final byte[] badPassword = "bad-password".getBytes();
+ final LockscreenCredential password = newPassword("user-password");
+ final LockscreenCredential badPassword = newPassword("bad-password");
MockSyntheticPasswordManager manager = new MockSyntheticPasswordManager(mContext, mStorage,
mGateKeeperService, mUserManager, mPasswordSlotManager);
AuthenticationToken authToken = manager.newSyntheticPasswordAndSid(mGateKeeperService, null,
null, USER_ID);
long handle = manager.createPasswordBasedSyntheticPassword(mGateKeeperService,
- password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, authToken,
- PASSWORD_QUALITY_ALPHABETIC, USER_ID);
+ password, authToken, USER_ID);
AuthenticationResult result = manager.unwrapPasswordBasedSyntheticPassword(
mGateKeeperService, handle, password, USER_ID, null);
@@ -103,97 +101,90 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
}
public void testPasswordMigration() throws RemoteException {
- final byte[] password = "testPasswordMigration-password".getBytes();
+ final LockscreenCredential password = newPassword("testPasswordMigration-password");
disableSyntheticPassword();
- mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
+ assertTrue(mService.setLockCredential(password, nonePassword(), PRIMARY_USER_ID, false));
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
enableSyntheticPassword();
// Performs migration
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ password, 0, PRIMARY_USER_ID)
.getResponseCode());
assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
// SP-based verification
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(password,
- LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+ password, 0, PRIMARY_USER_ID)
.getResponseCode());
assertArrayNotEquals(primaryStorageKey,
mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
}
- protected void initializeCredentialUnderSP(byte[] password, int userId) throws RemoteException {
+ protected void initializeCredentialUnderSP(LockscreenCredential password, int userId)
+ throws RemoteException {
enableSyntheticPassword();
- int quality = password != null ? PASSWORD_QUALITY_ALPHABETIC
- : PASSWORD_QUALITY_UNSPECIFIED;
- int type = password != null ? LockPatternUtils.CREDENTIAL_TYPE_PASSWORD
- : LockPatternUtils.CREDENTIAL_TYPE_NONE;
- mService.setLockCredential(password, type, null, quality, userId, false);
+ mService.setLockCredential(password, nonePassword(), userId, false);
+ assertEquals(CREDENTIAL_TYPE_PASSWORD, mService.getCredentialType(userId));
+ assertTrue(mService.isSyntheticPasswordBasedCredential(userId));
}
public void testSyntheticPasswordChangeCredential() throws RemoteException {
- final byte[] password = "testSyntheticPasswordChangeCredential-password".getBytes();
- final byte[] newPassword = "testSyntheticPasswordChangeCredential-newpassword".getBytes();
+ final LockscreenCredential password = newPassword("password");
+ final LockscreenCredential newPassword = newPassword("newpassword");
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
- mService.setLockCredential(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, password,
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
+ mService.setLockCredential(newPassword, password, PRIMARY_USER_ID, false);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ newPassword, 0, PRIMARY_USER_ID)
.getResponseCode());
assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
}
public void testSyntheticPasswordVerifyCredential() throws RemoteException {
- final byte[] password = "testSyntheticPasswordVerifyCredential-password".getBytes();
- final byte[] badPassword = "testSyntheticPasswordVerifyCredential-badpassword".getBytes();
+ LockscreenCredential password = newPassword("password");
+ LockscreenCredential badPassword = newPassword("badpassword");
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ password, 0, PRIMARY_USER_ID)
.getResponseCode());
assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, mService.verifyCredential(
- badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ badPassword, 0, PRIMARY_USER_ID)
.getResponseCode());
}
public void testSyntheticPasswordClearCredential() throws RemoteException {
- final byte[] password = "testSyntheticPasswordClearCredential-password".getBytes();
- final byte[] badPassword = "testSyntheticPasswordClearCredential-newpassword".getBytes();
+ LockscreenCredential password = newPassword("password");
+ LockscreenCredential badPassword = newPassword("newpassword");
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
// clear password
- mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, password,
- PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID, false);
+ mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID, false);
assertEquals(0 ,mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
// set a new password
- mService.setLockCredential(badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
+ mService.setLockCredential(badPassword, nonePassword(),
+ PRIMARY_USER_ID, false);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ badPassword, 0, PRIMARY_USER_ID)
.getResponseCode());
assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
}
public void testSyntheticPasswordChangeCredentialKeepsAuthSecret() throws RemoteException {
- final byte[] password =
- "testSyntheticPasswordChangeCredentialKeepsAuthSecret-password".getBytes();
- final byte[] badPassword =
- "testSyntheticPasswordChangeCredentialKeepsAuthSecret-new".getBytes();
+ LockscreenCredential password = newPassword("password");
+ LockscreenCredential badPassword = newPassword("new");
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
- mService.setLockCredential(badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, password,
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
+ mService.setLockCredential(badPassword, password, PRIMARY_USER_ID, false);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ badPassword, 0, PRIMARY_USER_ID)
.getResponseCode());
// Check the same secret was passed each time
@@ -203,41 +194,35 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
}
public void testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret() throws RemoteException {
- final byte[] password =
- "testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret-password".getBytes();
- final byte[] newPassword =
- "testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret-new".getBytes();
+ LockscreenCredential password = newPassword("password");
+ LockscreenCredential newPassword = newPassword("new");
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
reset(mAuthSecretService);
- assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(password,
- LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+ password, 0, PRIMARY_USER_ID)
.getResponseCode());
verify(mAuthSecretService).primaryUserCredential(any(ArrayList.class));
}
public void testSecondaryUserDoesNotPassAuthSecret() throws RemoteException {
- final byte[] password = "testSecondaryUserDoesNotPassAuthSecret-password".getBytes();
+ LockscreenCredential password = newPassword("password");
initializeCredentialUnderSP(password, SECONDARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, SECONDARY_USER_ID)
+ password, 0, SECONDARY_USER_ID)
.getResponseCode());
verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class));
}
public void testNoSyntheticPasswordOrCredentialDoesNotPassAuthSecret() throws RemoteException {
- // Setting null doesn't create a synthetic password
- initializeCredentialUnderSP(null, PRIMARY_USER_ID);
-
- reset(mAuthSecretService);
mService.onUnlockUser(PRIMARY_USER_ID);
flushHandlerTasks();
verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class));
}
public void testSyntheticPasswordAndCredentialDoesNotPassAuthSecret() throws RemoteException {
- final byte[] password = "passwordForASyntheticPassword".getBytes();
+ LockscreenCredential password = newPassword("passwordForASyntheticPassword");
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
reset(mAuthSecretService);
@@ -247,10 +232,9 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
}
public void testSyntheticPasswordButNoCredentialPassesAuthSecret() throws RemoteException {
- final byte[] password = "getASyntheticPassword".getBytes();
+ LockscreenCredential password = newPassword("getASyntheticPassword");
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
- mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, password,
- PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID, false);
+ mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID, false);
reset(mAuthSecretService);
mService.onUnlockUser(PRIMARY_USER_ID);
@@ -259,15 +243,14 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
}
public void testManagedProfileUnifiedChallengeMigration() throws RemoteException {
- final byte[] UnifiedPassword = "testManagedProfileUnifiedChallengeMigration-pwd".getBytes();
+ LockscreenCredential UnifiedPassword = newPassword("unified-pwd");
disableSyntheticPassword();
- mService.setLockCredential(UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
+ mService.setLockCredential(UnifiedPassword, nonePassword(), PRIMARY_USER_ID, false);
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID);
- final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
- final byte[] profileStorageKey = mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID);
+ byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
+ byte[] profileStorageKey = mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID);
assertTrue(primarySid != 0);
assertTrue(profileSid != 0);
assertTrue(profileSid != primarySid);
@@ -275,12 +258,12 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
// do migration
enableSyntheticPassword();
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ UnifiedPassword, 0, PRIMARY_USER_ID)
.getResponseCode());
// verify
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ UnifiedPassword, 0, PRIMARY_USER_ID)
.getResponseCode());
assertEquals(primarySid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
@@ -293,19 +276,16 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
}
public void testManagedProfileSeparateChallengeMigration() throws RemoteException {
- final byte[] primaryPassword =
- "testManagedProfileSeparateChallengeMigration-primary".getBytes();
- final byte[] profilePassword =
- "testManagedProfileSeparateChallengeMigration-profile".getBytes();
+ LockscreenCredential primaryPassword = newPassword("primary");
+ LockscreenCredential profilePassword = newPassword("profile");
disableSyntheticPassword();
- mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
- mService.setLockCredential(profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
- PASSWORD_QUALITY_ALPHABETIC, MANAGED_PROFILE_USER_ID, false);
+ mService.setLockCredential(primaryPassword, nonePassword(), PRIMARY_USER_ID, false);
+ mService.setLockCredential(profilePassword, nonePassword(),
+ MANAGED_PROFILE_USER_ID, false);
final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID);
- final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
- final byte[] profileStorageKey = mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID);
+ byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
+ byte[] profileStorageKey = mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID);
assertTrue(primarySid != 0);
assertTrue(profileSid != 0);
assertTrue(profileSid != primarySid);
@@ -313,19 +293,19 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
// do migration
enableSyntheticPassword();
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ primaryPassword, 0, PRIMARY_USER_ID)
.getResponseCode());
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
- 0, MANAGED_PROFILE_USER_ID).getResponseCode());
+ profilePassword, 0, MANAGED_PROFILE_USER_ID)
+ .getResponseCode());
// verify
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
- .getResponseCode());
+ primaryPassword, 0, PRIMARY_USER_ID)
+ .getResponseCode());
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
- 0, MANAGED_PROFILE_USER_ID).getResponseCode());
+ profilePassword, 0, MANAGED_PROFILE_USER_ID)
+ .getResponseCode());
assertEquals(primarySid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
assertArrayNotEquals(primaryStorageKey,
@@ -337,101 +317,92 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
}
public void testTokenBasedResetPassword() throws RemoteException {
- final byte[] password = "password".getBytes();
- final byte[] pattern = "123654".getBytes();
- final byte[] token = "some-high-entropy-secure-token".getBytes();
+ LockscreenCredential password = newPassword("password");
+ LockscreenCredential pattern = newPattern("123654");
+ byte[] token = "some-high-entropy-secure-token".getBytes();
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
// Disregard any reportPasswordChanged() invocations as part of credential setup.
flushHandlerTasks();
reset(mDevicePolicyManager);
- final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
+ byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
assertFalse(mService.hasPendingEscrowToken(PRIMARY_USER_ID));
long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
assertTrue(mService.hasPendingEscrowToken(PRIMARY_USER_ID));
- mService.verifyCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
- PRIMARY_USER_ID).getResponseCode();
+ mService.verifyCredential(password, 0, PRIMARY_USER_ID).getResponseCode();
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
assertFalse(mService.hasPendingEscrowToken(PRIMARY_USER_ID));
- mLocalService.setLockCredentialWithToken(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
- handle, token, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID);
+ mLocalService.setLockCredentialWithToken(pattern, handle, token, PRIMARY_USER_ID);
// Verify DPM gets notified about new device lock
flushHandlerTasks();
- final PasswordMetrics metric = PasswordMetrics.computeForCredential(
- LockPatternUtils.CREDENTIAL_TYPE_PATTERN, pattern);
+ final PasswordMetrics metric = PasswordMetrics.computeForCredential(pattern);
assertEquals(metric, mService.getUserPasswordMetrics(PRIMARY_USER_ID));
verify(mDevicePolicyManager).reportPasswordChanged(PRIMARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, 0, PRIMARY_USER_ID)
+ pattern, 0, PRIMARY_USER_ID)
.getResponseCode());
assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
}
public void testTokenBasedClearPassword() throws RemoteException {
- final byte[] password = "password".getBytes();
- final byte[] pattern = "123654".getBytes();
- final byte[] token = "some-high-entropy-secure-token".getBytes();
+ LockscreenCredential password = newPassword("password");
+ LockscreenCredential pattern = newPattern("123654");
+ byte[] token = "some-high-entropy-secure-token".getBytes();
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
- final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
+ byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
- mService.verifyCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
- 0, PRIMARY_USER_ID).getResponseCode();
+ mService.verifyCredential(password, 0, PRIMARY_USER_ID).getResponseCode();
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
- mLocalService.setLockCredentialWithToken(null, LockPatternUtils.CREDENTIAL_TYPE_NONE,
- handle, token, PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
+ mLocalService.setLockCredentialWithToken(nonePassword(), handle, token, PRIMARY_USER_ID);
flushHandlerTasks(); // flush the unlockUser() call before changing password again
- mLocalService.setLockCredentialWithToken(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
- handle, token, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID);
+ mLocalService.setLockCredentialWithToken(pattern, handle, token,
+ PRIMARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, 0, PRIMARY_USER_ID)
+ pattern, 0, PRIMARY_USER_ID)
.getResponseCode());
assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
}
public void testTokenBasedResetPasswordAfterCredentialChanges() throws RemoteException {
- final byte[] password = "password".getBytes();
- final byte[] pattern = "123654".getBytes();
- final byte[] newPassword = "password".getBytes();
- final byte[] token = "some-high-entropy-secure-token".getBytes();
+ LockscreenCredential password = newPassword("password");
+ LockscreenCredential pattern = newPattern("123654");
+ LockscreenCredential newPassword = newPassword("password");
+ byte[] token = "some-high-entropy-secure-token".getBytes();
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
- final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
+ byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
- mService.verifyCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
- 0, PRIMARY_USER_ID).getResponseCode();
+ mService.verifyCredential(password, 0, PRIMARY_USER_ID).getResponseCode();
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
- mService.setLockCredential(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, password,
- PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID, false);
+ mService.setLockCredential(pattern, password, PRIMARY_USER_ID, false);
- mLocalService.setLockCredentialWithToken(newPassword,
- LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, token,
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
+ mLocalService.setLockCredentialWithToken(newPassword, handle, token, PRIMARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ newPassword, 0, PRIMARY_USER_ID)
.getResponseCode());
assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID));
}
public void testEscrowTokenActivatedImmediatelyIfNoUserPasswordNeedsMigration()
throws RemoteException {
- final String token = "some-high-entropy-secure-token";
+ final byte[] token = "some-high-entropy-secure-token".getBytes();
enableSyntheticPassword();
- long handle = mLocalService.addEscrowToken(token.getBytes(), PRIMARY_USER_ID, null);
+ long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
@@ -439,9 +410,15 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
public void testEscrowTokenActivatedImmediatelyIfNoUserPasswordNoMigration()
throws RemoteException {
- final String token = "some-high-entropy-secure-token";
- initializeCredentialUnderSP(null, PRIMARY_USER_ID);
- long handle = mLocalService.addEscrowToken(token.getBytes(), PRIMARY_USER_ID, null);
+ final byte[] token = "some-high-entropy-secure-token".getBytes();
+ // By first setting a password and then clearing it, we enter the state where SP is
+ // initialized but the user currently has no password
+ initializeCredentialUnderSP(newPassword("password"), PRIMARY_USER_ID);
+ assertTrue(mService.setLockCredential(nonePassword(), newPassword("password"),
+ PRIMARY_USER_ID, false));
+ assertTrue(mService.isSyntheticPasswordBasedCredential(PRIMARY_USER_ID));
+
+ long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
assertTrue(hasSyntheticPassword(PRIMARY_USER_ID));
@@ -449,12 +426,11 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
public void testEscrowTokenActivatedLaterWithUserPasswordNeedsMigration()
throws RemoteException {
- final byte[] token = "some-high-entropy-secure-token".getBytes();
- final byte[] password = "password".getBytes();
+ byte[] token = "some-high-entropy-secure-token".getBytes();
+ LockscreenCredential password = newPassword("password");
// Set up pre-SP user password
disableSyntheticPassword();
- mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
+ mService.setLockCredential(password, nonePassword(), PRIMARY_USER_ID, false);
enableSyntheticPassword();
long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
@@ -462,16 +438,28 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
// Activate token (password gets migrated to SP at the same time)
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ password, 0, PRIMARY_USER_ID)
.getResponseCode());
// Verify token is activated
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
}
+ public void testEscrowTokenCannotBeActivatedOnUnmanagedUser() {
+ byte[] token = "some-high-entropy-secure-token".getBytes();
+ when(mUserManagerInternal.isDeviceManaged()).thenReturn(false);
+ when(mUserManagerInternal.isUserManaged(PRIMARY_USER_ID)).thenReturn(false);
+ when(mDeviceStateCache.isDeviceProvisioned()).thenReturn(true);
+
+ try {
+ mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
+ fail("Escrow token should not be possible on unmanaged device");
+ } catch (SecurityException expected) { }
+ }
+
public void testSetLockCredentialWithTokenFailsWithoutLockScreen() throws Exception {
- final byte[] password = "password".getBytes();
- final byte[] pattern = "123654".getBytes();
- final byte[] token = "some-high-entropy-secure-token".getBytes();
+ LockscreenCredential password = newPassword("password");
+ LockscreenCredential pattern = newPattern("123654");
+ byte[] token = "some-high-entropy-secure-token".getBytes();
mHasSecureLockScreen = false;
enableSyntheticPassword();
@@ -479,57 +467,50 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
try {
- mLocalService.setLockCredentialWithToken(password,
- LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, token,
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
+ mLocalService.setLockCredentialWithToken(password, handle, token, PRIMARY_USER_ID);
fail("An exception should have been thrown.");
} catch (UnsupportedOperationException e) {
// Success - the exception was expected.
}
- assertFalse(mService.havePassword(PRIMARY_USER_ID));
+ assertEquals(CREDENTIAL_TYPE_NONE, mService.getCredentialType(PRIMARY_USER_ID));
try {
- mLocalService.setLockCredentialWithToken(pattern,
- LockPatternUtils.CREDENTIAL_TYPE_PATTERN, handle, token,
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
+ mLocalService.setLockCredentialWithToken(pattern, handle, token, PRIMARY_USER_ID);
fail("An exception should have been thrown.");
} catch (UnsupportedOperationException e) {
// Success - the exception was expected.
}
- assertFalse(mService.havePattern(PRIMARY_USER_ID));
+ assertEquals(CREDENTIAL_TYPE_NONE, mService.getCredentialType(PRIMARY_USER_ID));
}
- public void testgetHashFactorPrimaryUser() throws RemoteException {
- final byte[] password = "password".getBytes();
- mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
- final byte[] hashFactor = mService.getHashFactor(password, PRIMARY_USER_ID);
+ public void testGetHashFactorPrimaryUser() throws RemoteException {
+ LockscreenCredential password = newPassword("password");
+ mService.setLockCredential(password, nonePassword(), PRIMARY_USER_ID, false);
+ byte[] hashFactor = mService.getHashFactor(password, PRIMARY_USER_ID);
assertNotNull(hashFactor);
- mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE,
- password, PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID, false);
- final byte[] newHashFactor = mService.getHashFactor(null, PRIMARY_USER_ID);
+ mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID, false);
+ byte[] newHashFactor = mService.getHashFactor(nonePassword(), PRIMARY_USER_ID);
assertNotNull(newHashFactor);
// Hash factor should never change after password change/removal
assertArrayEquals(hashFactor, newHashFactor);
}
- public void testgetHashFactorManagedProfileUnifiedChallenge() throws RemoteException {
- final byte[] pattern = "1236".getBytes();
- mService.setLockCredential(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
- null, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID, false);
+ public void testGetHashFactorManagedProfileUnifiedChallenge() throws RemoteException {
+ LockscreenCredential pattern = newPattern("1236");
+ mService.setLockCredential(pattern, nonePassword(), PRIMARY_USER_ID, false);
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
assertNotNull(mService.getHashFactor(null, MANAGED_PROFILE_USER_ID));
}
- public void testgetHashFactorManagedProfileSeparateChallenge() throws RemoteException {
- final byte[] primaryPassword = "primary".getBytes();
- final byte[] profilePassword = "profile".getBytes();
- mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
- PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
- mService.setLockCredential(profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
- PASSWORD_QUALITY_ALPHABETIC, MANAGED_PROFILE_USER_ID, false);
- assertNotNull(mService.getHashFactor(profilePassword, MANAGED_PROFILE_USER_ID));
+ public void testGetHashFactorManagedProfileSeparateChallenge() throws RemoteException {
+ LockscreenCredential primaryPassword = newPassword("primary");
+ LockscreenCredential profilePassword = newPassword("profile");
+ mService.setLockCredential(primaryPassword, nonePassword(), PRIMARY_USER_ID, false);
+ mService.setLockCredential(profilePassword, nonePassword(),
+ MANAGED_PROFILE_USER_ID, false);
+ assertNotNull(
+ mService.getHashFactor(profilePassword, MANAGED_PROFILE_USER_ID));
}
public void testPasswordData_serializeDeserialize() {
@@ -537,7 +518,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
data.scryptN = 11;
data.scryptR = 22;
data.scryptP = 33;
- data.passwordType = CREDENTIAL_TYPE_PASSWORD;
+ data.credentialType = CREDENTIAL_TYPE_PASSWORD;
data.salt = PAYLOAD;
data.passwordHandle = PAYLOAD2;
@@ -546,7 +527,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
assertEquals(11, deserialized.scryptN);
assertEquals(22, deserialized.scryptR);
assertEquals(33, deserialized.scryptP);
- assertEquals(CREDENTIAL_TYPE_PASSWORD, deserialized.passwordType);
+ assertEquals(CREDENTIAL_TYPE_PASSWORD, deserialized.credentialType);
assertArrayEquals(PAYLOAD, deserialized.salt);
assertArrayEquals(PAYLOAD2, deserialized.passwordHandle);
}
@@ -555,7 +536,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
// Test that we can deserialize existing PasswordData and don't inadvertently change the
// wire format.
byte[] serialized = new byte[] {
- 0, 0, 0, 2, /* CREDENTIAL_TYPE_PASSWORD */
+ 0, 0, 0, 2, /* CREDENTIAL_TYPE_PASSWORD_OR_PIN */
11, /* scryptN */
22, /* scryptR */
33, /* scryptP */
@@ -569,7 +550,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
assertEquals(11, deserialized.scryptN);
assertEquals(22, deserialized.scryptR);
assertEquals(33, deserialized.scryptP);
- assertEquals(CREDENTIAL_TYPE_PASSWORD, deserialized.passwordType);
+ assertEquals(CREDENTIAL_TYPE_PASSWORD_OR_PIN, deserialized.credentialType);
assertArrayEquals(PAYLOAD, deserialized.salt);
assertArrayEquals(PAYLOAD2, deserialized.passwordHandle);
}
@@ -577,11 +558,11 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
public void testGsiDisablesAuthSecret() throws RemoteException {
mGsiService.setIsGsiRunning(true);
- final byte[] password = "testGsiDisablesAuthSecret-password".getBytes();
+ LockscreenCredential password = newPassword("testGsiDisablesAuthSecret-password");
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
- password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+ password, 0, PRIMARY_USER_ID)
.getResponseCode());
verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class));
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index a992dd126f5d..7d3ec030e7a6 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -23,6 +23,7 @@ import static android.security.keystore.recovery.KeyChainProtectionParams.UI_FOR
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import static com.google.common.truth.Truth.assertThat;
@@ -31,7 +32,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -162,21 +162,6 @@ public class KeySyncTaskTest {
}
@Test
- public void isPin_isTrueForNumericString() {
- assertTrue(KeySyncTask.isPin("3298432574398654376547".getBytes()));
- }
-
- @Test
- public void isPin_isFalseForStringContainingLetters() {
- assertFalse(KeySyncTask.isPin("398i54369548654".getBytes()));
- }
-
- @Test
- public void isPin_isFalseForStringContainingSymbols() {
- assertFalse(KeySyncTask.isPin("-3987543643".getBytes()));
- }
-
- @Test
public void hashCredentialsBySaltedSha256_returnsSameHashForSameCredentialsAndSalt() {
String credentials = "password1234";
byte[] salt = randomBytes(16);
@@ -221,19 +206,19 @@ public class KeySyncTaskTest {
@Test
public void getUiFormat_returnsPinIfPin() {
assertEquals(UI_FORMAT_PIN,
- KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PASSWORD, "1234".getBytes()));
+ KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PIN));
}
@Test
public void getUiFormat_returnsPasswordIfPassword() {
assertEquals(UI_FORMAT_PASSWORD,
- KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PASSWORD, "1234a".getBytes()));
+ KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PASSWORD));
}
@Test
public void getUiFormat_returnsPatternIfPattern() {
assertEquals(UI_FORMAT_PATTERN,
- KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PATTERN, "1234".getBytes()));
+ KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PATTERN));
}
@@ -683,7 +668,7 @@ public class KeySyncTaskTest {
mRecoverySnapshotStorage,
mSnapshotListenersStorage,
TEST_USER_ID,
- CREDENTIAL_TYPE_PASSWORD,
+ CREDENTIAL_TYPE_PIN,
/*credential=*/ pin.getBytes(),
/*credentialUpdated=*/ false,
mPlatformKeyManager,
@@ -799,7 +784,7 @@ public class KeySyncTaskTest {
mRecoverySnapshotStorage,
mSnapshotListenersStorage,
TEST_USER_ID,
- /*credentialType=*/ 3,
+ /*credentialType=*/ 5, // Some invalid credential type value
"12345".getBytes(),
/*credentialUpdated=*/ false,
mPlatformKeyManager,
diff --git a/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java b/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java
index fe7a376d9e8d..25b41db1aea3 100644
--- a/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java
@@ -29,7 +29,6 @@ import android.os.BatteryManager;
import android.os.Bundle;
import android.os.IBinder;
import android.os.SystemClock;
-import android.provider.Settings;
import android.support.test.uiautomator.UiDevice;
import android.text.TextUtils;
import android.util.Log;
@@ -88,17 +87,11 @@ public class ConnOnActivityStartTest {
private static final int REPEAT_TEST_COUNT = 5;
- private static final String KEY_PAROLE_DURATION = "parole_duration";
- private static final String DESIRED_PAROLE_DURATION = "0";
-
private static Context mContext;
private static UiDevice mUiDevice;
private static int mTestPkgUid;
private static BatteryManager mBatteryManager;
- private static boolean mAppIdleConstsUpdated;
- private static String mOriginalAppIdleConsts;
-
private static ServiceConnection mServiceConnection;
private static ICmdReceiverService mCmdReceiverService;
@@ -107,7 +100,6 @@ public class ConnOnActivityStartTest {
mContext = InstrumentationRegistry.getContext();
mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- setDesiredParoleDuration();
mContext.getPackageManager().setApplicationEnabledSetting(TEST_PKG,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
mTestPkgUid = mContext.getPackageManager().getPackageUid(TEST_PKG, 0);
@@ -119,10 +111,6 @@ public class ConnOnActivityStartTest {
@AfterClass
public static void tearDownOnce() throws Exception {
batteryReset();
- if (mAppIdleConstsUpdated) {
- Settings.Global.putString(mContext.getContentResolver(),
- Settings.Global.APP_IDLE_CONSTANTS, mOriginalAppIdleConsts);
- }
unbindService();
}
@@ -160,27 +148,6 @@ public class ConnOnActivityStartTest {
}
}
- private static void setDesiredParoleDuration() {
- mOriginalAppIdleConsts = Settings.Global.getString(mContext.getContentResolver(),
- Settings.Global.APP_IDLE_CONSTANTS);
- String newAppIdleConstants;
- final String newConstant = KEY_PAROLE_DURATION + "=" + DESIRED_PAROLE_DURATION;
- if (mOriginalAppIdleConsts == null || "null".equals(mOriginalAppIdleConsts)) {
- // app_idle_constants is initially empty, so just assign the desired value.
- newAppIdleConstants = newConstant;
- } else if (mOriginalAppIdleConsts.contains(KEY_PAROLE_DURATION)) {
- // app_idle_constants contains parole_duration, so replace it with the desired value.
- newAppIdleConstants = mOriginalAppIdleConsts.replaceAll(
- KEY_PAROLE_DURATION + "=\\d+", newConstant);
- } else {
- // app_idle_constants didn't have parole_duration, so append the desired value.
- newAppIdleConstants = mOriginalAppIdleConsts + "," + newConstant;
- }
- Settings.Global.putString(mContext.getContentResolver(),
- Settings.Global.APP_IDLE_CONSTANTS, newAppIdleConstants);
- mAppIdleConstsUpdated = true;
- }
-
@Test
public void testStartActivity_batterySaver() throws Exception {
setBatterySaverMode(true);
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index ba12b7393048..8a489047f179 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -113,6 +113,7 @@ import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.StringNetworkSpecifier;
import android.os.Binder;
+import android.os.Handler;
import android.os.INetworkManagementService;
import android.os.PersistableBundle;
import android.os.PowerManagerInternal;
@@ -1117,7 +1118,7 @@ public class NetworkPolicyManagerServiceTest {
// Define simple data plan
final SubscriptionPlan plan = buildMonthlyDataPlan(
ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), DataUnit.MEGABYTES.toBytes(1800));
- mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
+ setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
mServiceContext.getOpPackageName());
// We're 20% through the month (6 days)
@@ -1241,7 +1242,7 @@ public class NetworkPolicyManagerServiceTest {
// Define simple data plan which gives us effectively 60MB/day
final SubscriptionPlan plan = buildMonthlyDataPlan(
ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), DataUnit.MEGABYTES.toBytes(1800));
- mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
+ setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
mServiceContext.getOpPackageName());
// We're 20% through the month (6 days)
@@ -1457,6 +1458,8 @@ public class NetworkPolicyManagerServiceTest {
when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[0]);
when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{FAKE_SUB_ID});
when(mTelephonyManager.getSubscriberId(FAKE_SUB_ID)).thenReturn(FAKE_SUBSCRIBER_ID);
+ when(mTelephonyManager.createForSubscriptionId(FAKE_SUB_ID))
+ .thenReturn(mock(TelephonyManager.class));
PersistableBundle bundle = CarrierConfigManager.getDefaultConfig();
when(mCarrierConfigManager.getConfigForSubId(FAKE_SUB_ID)).thenReturn(bundle);
setNetworkPolicies(buildDefaultFakeMobilePolicy());
@@ -1468,6 +1471,8 @@ public class NetworkPolicyManagerServiceTest {
when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[0]);
when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{FAKE_SUB_ID});
when(mTelephonyManager.getSubscriberId(FAKE_SUB_ID)).thenReturn(FAKE_SUBSCRIBER_ID);
+ when(mTelephonyManager.createForSubscriptionId(FAKE_SUB_ID))
+ .thenReturn(mock(TelephonyManager.class));
when(mCarrierConfigManager.getConfigForSubId(FAKE_SUB_ID)).thenReturn(null);
setNetworkPolicies(buildDefaultFakeMobilePolicy());
// smoke test to make sure no errors are raised
@@ -1653,7 +1658,7 @@ public class NetworkPolicyManagerServiceTest {
final SubscriptionPlan plan = buildMonthlyDataPlan(
ZonedDateTime.parse("2015-11-01T00:00:00.00Z"),
DataUnit.MEGABYTES.toBytes(1800));
- mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
+ setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
mServiceContext.getOpPackageName());
reset(mTelephonyManager, mNetworkManager, mNotifManager);
@@ -1674,7 +1679,7 @@ public class NetworkPolicyManagerServiceTest {
final SubscriptionPlan plan = buildMonthlyDataPlan(
ZonedDateTime.parse("2015-11-01T00:00:00.00Z"),
DataUnit.MEGABYTES.toBytes(100));
- mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
+ setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
mServiceContext.getOpPackageName());
reset(mTelephonyManager, mNetworkManager, mNotifManager);
@@ -1690,7 +1695,7 @@ public class NetworkPolicyManagerServiceTest {
{
final SubscriptionPlan plan = buildMonthlyDataPlan(
ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), BYTES_UNLIMITED);
- mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
+ setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
mServiceContext.getOpPackageName());
reset(mTelephonyManager, mNetworkManager, mNotifManager);
@@ -1707,7 +1712,7 @@ public class NetworkPolicyManagerServiceTest {
{
final SubscriptionPlan plan = buildMonthlyDataPlan(
ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), BYTES_UNLIMITED);
- mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
+ setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
mServiceContext.getOpPackageName());
reset(mTelephonyManager, mNetworkManager, mNotifManager);
@@ -1923,6 +1928,8 @@ public class NetworkPolicyManagerServiceTest {
when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(
new int[] { TEST_SUB_ID });
when(mTelephonyManager.getSubscriberId(TEST_SUB_ID)).thenReturn(TEST_IMSI);
+ when(mTelephonyManager.createForSubscriptionId(TEST_SUB_ID))
+ .thenReturn(mock(TelephonyManager.class));
doNothing().when(mTelephonyManager).setPolicyDataEnabled(anyBoolean(), anyInt());
expectNetworkState(false /* roaming */);
}
@@ -2049,6 +2056,23 @@ public class NetworkPolicyManagerServiceTest {
private FutureIntent mRestrictBackgroundChanged;
+ private void postMsgAndWaitForCompletion() throws InterruptedException {
+ final Handler handler = mService.getHandlerForTesting();
+ final CountDownLatch latch = new CountDownLatch(1);
+ mService.getHandlerForTesting().post(latch::countDown);
+ if (!latch.await(5, TimeUnit.SECONDS)) {
+ fail("Timed out waiting for the test msg to be handled");
+ }
+ }
+
+ private void setSubscriptionPlans(int subId, SubscriptionPlan[] plans, String callingPackage)
+ throws InterruptedException {
+ mService.setSubscriptionPlans(subId, plans, callingPackage);
+ // setSubscriptionPlans() triggers async events, wait for those to be completed before
+ // moving forward as they could interfere with the tests later.
+ postMsgAndWaitForCompletion();
+ }
+
private void setRestrictBackground(boolean flag) throws Exception {
mService.setRestrictBackground(flag);
// Sanity check.
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/HarmfulCrcsTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/HarmfulCrcsTests.java
new file mode 100644
index 000000000000..23f128115ea0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/HarmfulCrcsTests.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net.watchlist;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.HexDump;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+/**
+ * runtest frameworks-services -c com.android.server.net.watchlist.HarmfulCrcTests
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class HarmfulCrcsTests {
+
+ private static final byte[] TEST_DIGEST = HexDump.hexStringToByteArray("AABBCCDD");
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ @Test
+ public void testHarmfulCrcs_setAndContains() throws Exception {
+ HarmfulCrcs harmfulCrcs = new HarmfulCrcs(
+ Arrays.asList(new byte[][] {TEST_DIGEST}));
+ assertTrue(harmfulCrcs.contains(0xaabbccdd));
+ assertFalse(harmfulCrcs.contains(0xbbbbbbbb));
+ assertFalse(harmfulCrcs.contains(0x01020304));
+ assertFalse(harmfulCrcs.contains(0xddccbbaa));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
index 3cdadd58486f..c566dfc4a23e 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
@@ -444,7 +444,7 @@ public class OverlayManagerServiceImplTests {
private Set<String> mIdmapFiles = new ArraySet<>();
DummyIdmapManager(DummyDeviceState state, DummyPackageManagerHelper packageManagerHelper) {
- super(null, packageManagerHelper);
+ super(packageManagerHelper);
mState = state;
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
index 43bcd4fc8436..3c1044728fcc 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
@@ -165,8 +165,8 @@ public class PackageInstallerSessionTest {
/* stagingManager */ null,
/* sessionId */ sessionId,
/* userId */ 456,
- /* installerPackageName */ "testInstaller",
/* installerUid */ -1,
+ /* installSource */ InstallSource.create("testInstaller", "testInstaller"),
/* sessionParams */ params,
/* createdMillis */ 0L,
/* stageDir */ mTmpDir,
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index fc7cfec9dc87..0a310d193675 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -118,17 +118,20 @@ public class PackageManagerServiceTest {
String[] partitions = { "system", "vendor", "odm", "oem", "product", "system_ext" };
String[] appdir = { "app", "priv-app" };
for (int i = 0; i < partitions.length; i++) {
+ final PackageManagerService.SystemPartition systemPartition =
+ PackageManagerService.SYSTEM_PARTITIONS.get(i);
for (int j = 0; j < appdir.length; j++) {
String canonical = new File("/" + partitions[i]).getCanonicalPath();
String path = String.format("%s/%s/A.apk", canonical, appdir[j]);
- Assert.assertEquals(j == 1 && i != 3,
- PackageManagerService.locationIsPrivileged(path));
+ Assert.assertEquals(j == 1 && i != 3, systemPartition.containsPrivPath(path));
- Assert.assertEquals(i == 1 || i == 2, PackageManagerService.locationIsVendor(path));
- Assert.assertEquals(i == 3, PackageManagerService.locationIsOem(path));
- Assert.assertEquals(i == 4, PackageManagerService.locationIsProduct(path));
- Assert.assertEquals(i == 5, PackageManagerService.locationIsSystemExt(path));
+ final int scanFlag = systemPartition.scanFlag;
+ Assert.assertEquals(i == 1, scanFlag == PackageManagerService.SCAN_AS_VENDOR);
+ Assert.assertEquals(i == 2, scanFlag == PackageManagerService.SCAN_AS_ODM);
+ Assert.assertEquals(i == 3, scanFlag == PackageManagerService.SCAN_AS_OEM);
+ Assert.assertEquals(i == 4, scanFlag == PackageManagerService.SCAN_AS_PRODUCT);
+ Assert.assertEquals(i == 5, scanFlag == PackageManagerService.SCAN_AS_SYSTEM_EXT);
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 3fe9b52b8415..1106be2d5f6b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -20,8 +20,8 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.res.Resources.ID_NULL;
-import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
@@ -75,10 +75,11 @@ import java.util.List;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PackageManagerSettingsTests {
+ private static final String TAG = "PackageManagerSettingsTests";
+ private static final String PACKAGE_NAME_1 = "com.android.app1";
private static final String PACKAGE_NAME_2 = "com.android.app2";
private static final String PACKAGE_NAME_3 = "com.android.app3";
- private static final String PACKAGE_NAME_1 = "com.android.app1";
- public static final String TAG = "PackageManagerSettingsTests";
+ private static final int TEST_RESOURCE_ID = 2131231283;
@Mock
PermissionSettings mPermissionSettings;
@@ -158,7 +159,7 @@ public class PackageManagerSettingsTests {
assertThat(ps.getEnabled(1), is(COMPONENT_ENABLED_STATE_DEFAULT));
}
- private PersistableBundle getPersistableBundle(String packageName, long longVal,
+ private static PersistableBundle createPersistableBundle(String packageName, long longVal,
double doubleVal, boolean boolVal, String textVal) {
final PersistableBundle bundle = new PersistableBundle();
bundle.putString(packageName + ".TEXT_VALUE", textVal);
@@ -169,8 +170,8 @@ public class PackageManagerSettingsTests {
}
@Test
- public void testReadPackageRestrictions_oldSuspendInfo() {
- writePackageRestrictions_oldSuspendInfoXml(0);
+ public void testReadPackageRestrictions_noSuspendingPackage() {
+ writePackageRestrictions_noSuspendingPackageXml(0);
final Object lock = new Object();
final Context context = InstrumentationRegistry.getTargetContext();
final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, lock);
@@ -181,26 +182,61 @@ public class PackageManagerSettingsTests {
final PackageSetting ps1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1);
final PackageUserState packageUserState1 = ps1.readUserState(0);
assertThat(packageUserState1.suspended, is(true));
- assertThat("android".equals(packageUserState1.suspendingPackage), is(true));
+ assertThat(packageUserState1.suspendParams.size(), is(1));
+ assertThat(packageUserState1.suspendParams.keyAt(0), is("android"));
+ assertThat(packageUserState1.suspendParams.valueAt(0), is(nullValue()));
final PackageSetting ps2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2);
final PackageUserState packageUserState2 = ps2.readUserState(0);
assertThat(packageUserState2.suspended, is(false));
- assertThat(packageUserState2.suspendingPackage, is(nullValue()));
+ assertThat(packageUserState2.suspendParams, is(nullValue()));
}
@Test
- public void testReadWritePackageRestrictions_newSuspendInfo() {
+ public void testReadPackageRestrictions_noSuspendParamsMap() {
+ writePackageRestrictions_noSuspendParamsMapXml(0);
+ final Object lock = new Object();
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, lock);
+ settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1));
+ settingsUnderTest.readPackageRestrictionsLPr(0);
+
+ final PackageSetting ps1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1);
+ final PackageUserState packageUserState1 = ps1.readUserState(0);
+ assertThat(packageUserState1.suspended, is(true));
+ assertThat(packageUserState1.suspendParams.size(), is(1));
+ assertThat(packageUserState1.suspendParams.keyAt(0), is(PACKAGE_NAME_3));
+ final PackageUserState.SuspendParams params = packageUserState1.suspendParams.valueAt(0);
+ assertThat(params, is(notNullValue()));
+ assertThat(params.appExtras.size(), is(1));
+ assertThat(params.appExtras.getString("app_extra_string"), is("value"));
+ assertThat(params.launcherExtras.size(), is(1));
+ assertThat(params.launcherExtras.getLong("launcher_extra_long"), is(4L));
+ assertThat(params.dialogInfo, is(notNullValue()));
+ assertThat(params.dialogInfo.getDialogMessage(), is("Dialog Message"));
+ assertThat(params.dialogInfo.getTitleResId(), is(ID_NULL));
+ assertThat(params.dialogInfo.getIconResId(), is(TEST_RESOURCE_ID));
+ assertThat(params.dialogInfo.getNeutralButtonTextResId(), is(ID_NULL));
+ assertThat(params.dialogInfo.getDialogMessageResId(), is(ID_NULL));
+ }
+
+ @Test
+ public void testReadWritePackageRestrictions_suspendInfo() {
final Context context = InstrumentationRegistry.getTargetContext();
final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, new Object());
final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2);
final PackageSetting ps3 = createPackageSetting(PACKAGE_NAME_3);
- final PersistableBundle appExtras1 = getPersistableBundle(
+ final PersistableBundle appExtras1 = createPersistableBundle(
PACKAGE_NAME_1, 1L, 0.01, true, "appString1");
- final PersistableBundle launcherExtras1 = getPersistableBundle(
+ final PersistableBundle appExtras2 = createPersistableBundle(
+ PACKAGE_NAME_2, 2L, 0.02, true, "appString2");
+
+ final PersistableBundle launcherExtras1 = createPersistableBundle(
PACKAGE_NAME_1, 10L, 0.1, false, "launcherString1");
+ final PersistableBundle launcherExtras2 = createPersistableBundle(
+ PACKAGE_NAME_2, 20L, 0.2, false, "launcherString2");
final SuspendDialogInfo dialogInfo1 = new SuspendDialogInfo.Builder()
.setIcon(0x11220001)
@@ -208,14 +244,23 @@ public class PackageManagerSettingsTests {
.setMessage("1st message")
.setNeutralButtonText(0x11220003)
.build();
+ final SuspendDialogInfo dialogInfo2 = new SuspendDialogInfo.Builder()
+ .setIcon(0x22220001)
+ .setTitle(0x22220002)
+ .setMessage("2nd message")
+ .setNeutralButtonText(0x22220003)
+ .build();
- ps1.setSuspended(true, "suspendingPackage1", dialogInfo1, appExtras1, launcherExtras1, 0);
+ ps1.addOrUpdateSuspension("suspendingPackage1", dialogInfo1, appExtras1, launcherExtras1,
+ 0);
+ ps1.addOrUpdateSuspension("suspendingPackage2", dialogInfo2, appExtras2, launcherExtras2,
+ 0);
settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
- ps2.setSuspended(true, "suspendingPackage2", null, null, null, 0);
+ ps2.addOrUpdateSuspension("suspendingPackage3", null, appExtras1, null, 0);
settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
- ps3.setSuspended(false, "irrelevant", dialogInfo1, null, null, 0);
+ ps3.removeSuspension("irrelevant", 0);
settingsUnderTest.mPackages.put(PACKAGE_NAME_3, ps3);
settingsUnderTest.writePackageRestrictionsLPr(0);
@@ -229,27 +274,39 @@ public class PackageManagerSettingsTests {
final PackageUserState readPus1 = settingsUnderTest.mPackages.get(PACKAGE_NAME_1)
.readUserState(0);
assertThat(readPus1.suspended, is(true));
- assertThat(readPus1.suspendingPackage, equalTo("suspendingPackage1"));
- assertThat(readPus1.dialogInfo, equalTo(dialogInfo1));
- assertThat(BaseBundle.kindofEquals(readPus1.suspendedAppExtras, appExtras1), is(true));
- assertThat(BaseBundle.kindofEquals(readPus1.suspendedLauncherExtras, launcherExtras1),
+ assertThat(readPus1.suspendParams.size(), is(2));
+
+ assertThat(readPus1.suspendParams.keyAt(0), is("suspendingPackage1"));
+ final PackageUserState.SuspendParams params11 = readPus1.suspendParams.valueAt(0);
+ assertThat(params11, is(notNullValue()));
+ assertThat(params11.dialogInfo, is(dialogInfo1));
+ assertThat(BaseBundle.kindofEquals(params11.appExtras, appExtras1), is(true));
+ assertThat(BaseBundle.kindofEquals(params11.launcherExtras, launcherExtras1),
+ is(true));
+
+ assertThat(readPus1.suspendParams.keyAt(1), is("suspendingPackage2"));
+ final PackageUserState.SuspendParams params12 = readPus1.suspendParams.valueAt(1);
+ assertThat(params12, is(notNullValue()));
+ assertThat(params12.dialogInfo, is(dialogInfo2));
+ assertThat(BaseBundle.kindofEquals(params12.appExtras, appExtras2), is(true));
+ assertThat(BaseBundle.kindofEquals(params12.launcherExtras, launcherExtras2),
is(true));
final PackageUserState readPus2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2)
.readUserState(0);
assertThat(readPus2.suspended, is(true));
- assertThat(readPus2.suspendingPackage, equalTo("suspendingPackage2"));
- assertThat(readPus2.dialogInfo, is(nullValue()));
- assertThat(readPus2.suspendedAppExtras, is(nullValue()));
- assertThat(readPus2.suspendedLauncherExtras, is(nullValue()));
+ assertThat(readPus2.suspendParams.size(), is(1));
+ assertThat(readPus2.suspendParams.keyAt(0), is("suspendingPackage3"));
+ final PackageUserState.SuspendParams params21 = readPus2.suspendParams.valueAt(0);
+ assertThat(params21, is(notNullValue()));
+ assertThat(params21.dialogInfo, is(nullValue()));
+ assertThat(BaseBundle.kindofEquals(params21.appExtras, appExtras1), is(true));
+ assertThat(params21.launcherExtras, is(nullValue()));
final PackageUserState readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3)
.readUserState(0);
assertThat(readPus3.suspended, is(false));
- assertThat(readPus3.suspendingPackage, is(nullValue()));
- assertThat(readPus3.dialogInfo, is(nullValue()));
- assertThat(readPus3.suspendedAppExtras, is(nullValue()));
- assertThat(readPus3.suspendedLauncherExtras, is(nullValue()));
+ assertThat(readPus3.suspendParams, is(nullValue()));
}
@Test
@@ -755,11 +812,9 @@ public class PackageManagerSettingsTests {
assertSame(origPkgSetting.cpuAbiOverrideString, testPkgSetting.cpuAbiOverrideString);
assertThat(origPkgSetting.cpuAbiOverrideString, is(testPkgSetting.cpuAbiOverrideString));
assertThat(origPkgSetting.firstInstallTime, is(testPkgSetting.firstInstallTime));
- assertSame(origPkgSetting.installerPackageName, testPkgSetting.installerPackageName);
- assertThat(origPkgSetting.installerPackageName, is(testPkgSetting.installerPackageName));
+ assertSame(origPkgSetting.installSource, testPkgSetting.installSource);
assertThat(origPkgSetting.installPermissionsFixed,
is(testPkgSetting.installPermissionsFixed));
- assertThat(origPkgSetting.isOrphaned, is(testPkgSetting.isOrphaned));
assertSame(origPkgSetting.keySetData, testPkgSetting.keySetData);
assertThat(origPkgSetting.keySetData, is(testPkgSetting.keySetData));
assertThat(origPkgSetting.lastUpdateTime, is(testPkgSetting.lastUpdateTime));
@@ -940,10 +995,10 @@ public class PackageManagerSettingsTests {
+ "</packages>").getBytes());
}
- private void writePackageRestrictions_oldSuspendInfoXml(final int userId) {
+ private void writePackageRestrictions_noSuspendingPackageXml(final int userId) {
writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), "system/users/"
+ userId + "/package-restrictions.xml"),
- ( "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<package-restrictions>\n"
+ " <pkg name=\"" + PACKAGE_NAME_1 + "\" suspended=\"true\" />"
+ " <pkg name=\"" + PACKAGE_NAME_2 + "\" suspended=\"false\" />"
@@ -955,6 +1010,30 @@ public class PackageManagerSettingsTests {
.getBytes());
}
+ private void writePackageRestrictions_noSuspendParamsMapXml(final int userId) {
+ writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), "system/users/"
+ + userId + "/package-restrictions.xml"),
+ ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<package-restrictions>\n"
+ + " <pkg name=\"" + PACKAGE_NAME_1 + "\" "
+ + " suspended=\"true\" suspending-package=\"" + PACKAGE_NAME_3 + "\">\n"
+ + " <suspended-dialog-info dialogMessage=\"Dialog Message\""
+ + " iconResId=\"" + TEST_RESOURCE_ID + "\"/>\n"
+ + " <suspended-app-extras>\n"
+ + " <string name=\"app_extra_string\">value</string>\n"
+ + " </suspended-app-extras>\n"
+ + " <suspended-launcher-extras>\n"
+ + " <long name=\"launcher_extra_long\" value=\"4\" />\n"
+ + " </suspended-launcher-extras>\n"
+ + " </pkg>\n"
+ + " <preferred-activities />\n"
+ + " <persistent-preferred-activities />\n"
+ + " <crossProfile-intent-filters />\n"
+ + " <default-apps />\n"
+ + "</package-restrictions>\n")
+ .getBytes());
+ }
+
private void writeStoppedPackagesXml() {
writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), "system/packages-stopped.xml"),
( "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index 8eaf35f6432f..fc5a0ba1af7b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -26,6 +26,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageUserState;
import android.content.pm.SuspendDialogInfo;
import android.os.PersistableBundle;
+import android.util.ArrayMap;
import android.util.ArraySet;
import androidx.test.filters.SmallTest;
@@ -175,18 +176,43 @@ public class PackageUserStateTest {
assertThat(testUserState03.equals(oldUserState), is(false));
}
+ private static PackageUserState.SuspendParams createSuspendParams(SuspendDialogInfo dialogInfo,
+ PersistableBundle appExtras, PersistableBundle launcherExtras) {
+ PackageUserState.SuspendParams obj = PackageUserState.SuspendParams.getInstanceOrNull(
+ dialogInfo, appExtras, launcherExtras);
+ return obj;
+ }
+
+ private static PersistableBundle createPersistableBundle(String lKey, long lValue, String sKey,
+ String sValue, String dKey, double dValue) {
+ final PersistableBundle result = new PersistableBundle(3);
+ if (lKey != null) {
+ result.putLong("com.unit_test." + lKey, lValue);
+ }
+ if (sKey != null) {
+ result.putString("com.unit_test." + sKey, sValue);
+ }
+ if (dKey != null) {
+ result.putDouble("com.unit_test." + dKey, dValue);
+ }
+ return result;
+ }
+
@Test
public void testPackageUserState05() {
- PersistableBundle appExtras1 = new PersistableBundle();
- PersistableBundle appExtras2 = new PersistableBundle();
- appExtras1.putInt("appExtraId", 1);
- appExtras2.putInt("appExtraId", 2);
- PersistableBundle launcherExtras1 = new PersistableBundle();
- PersistableBundle launcherExtras2 = new PersistableBundle();
- launcherExtras1.putString("name", "launcherExtras1");
- launcherExtras2.putString("name", "launcherExtras2");
+ final PersistableBundle appExtras1 = createPersistableBundle("appExtraId", 1, null, null,
+ null, 0);
+ final PersistableBundle appExtras2 = createPersistableBundle("appExtraId", 2, null, null,
+ null, 0);
+
+ final PersistableBundle launcherExtras1 = createPersistableBundle(null, 0, "name",
+ "launcherExtras1", null, 0);
+ final PersistableBundle launcherExtras2 = createPersistableBundle(null, 0, "name",
+ "launcherExtras2", null, 0);
+
final String suspendingPackage1 = "package1";
final String suspendingPackage2 = "package2";
+
final SuspendDialogInfo dialogInfo1 = new SuspendDialogInfo.Builder()
.setMessage("dialogMessage1")
.build();
@@ -194,38 +220,23 @@ public class PackageUserStateTest {
.setMessage("dialogMessage2")
.build();
+ final ArrayMap<String, PackageUserState.SuspendParams> paramsMap1 = new ArrayMap<>();
+ paramsMap1.put(suspendingPackage1, createSuspendParams(dialogInfo1, appExtras1,
+ launcherExtras1));
+ final ArrayMap<String, PackageUserState.SuspendParams> paramsMap2 = new ArrayMap<>();
+ paramsMap2.put(suspendingPackage2, createSuspendParams(dialogInfo2,
+ appExtras2, launcherExtras2));
+
+
final PackageUserState testUserState1 = new PackageUserState();
testUserState1.suspended = true;
- testUserState1.suspendedAppExtras = appExtras1;
- testUserState1.suspendedLauncherExtras = launcherExtras1;
- testUserState1.suspendingPackage = suspendingPackage1;
- testUserState1.dialogInfo = dialogInfo1;
+ testUserState1.suspendParams = paramsMap1;
PackageUserState testUserState2 = new PackageUserState(testUserState1);
assertThat(testUserState1.equals(testUserState2), is(true));
- testUserState2.suspendingPackage = suspendingPackage2;
- assertThat(testUserState1.equals(testUserState2), is(false));
-
- testUserState2 = new PackageUserState(testUserState1);
- testUserState2.suspendedAppExtras = appExtras2;
- assertThat(testUserState1.equals(testUserState2), is(false));
-
- testUserState2 = new PackageUserState(testUserState1);
- testUserState2.suspendedLauncherExtras = launcherExtras2;
- assertThat(testUserState1.equals(testUserState2), is(false));
-
- testUserState2 = new PackageUserState(testUserState1);
- testUserState2.dialogInfo = dialogInfo2;
+ testUserState2.suspendParams = paramsMap2;
+ // Should not be equal since suspendParams maps are different
assertThat(testUserState1.equals(testUserState2), is(false));
-
- testUserState2 = new PackageUserState(testUserState1);
- testUserState2.suspended = testUserState1.suspended = false;
- // Everything is different but irrelevant if suspended is false
- testUserState2.suspendingPackage = suspendingPackage2;
- testUserState2.dialogInfo = dialogInfo2;
- testUserState2.suspendedAppExtras = appExtras2;
- testUserState2.suspendedLauncherExtras = launcherExtras2;
- assertThat(testUserState1.equals(testUserState2), is(true));
}
@Test
@@ -243,4 +254,46 @@ public class PackageUserStateTest {
assertThat(userState1.equals(userState2), is(false));
}
+ @Test
+ public void testPackageUserState07() {
+ final PersistableBundle appExtras1 = createPersistableBundle("appExtraId", 1, null, null,
+ null, 0);
+ final PersistableBundle appExtras2 = createPersistableBundle("appExtraId", 2, null, null,
+ null, 0);
+
+ final PersistableBundle launcherExtras1 = createPersistableBundle(null, 0, "name",
+ "launcherExtras1", null, 0);
+ final PersistableBundle launcherExtras2 = createPersistableBundle(null, 0, "name",
+ "launcherExtras2", null, 0);
+
+ final SuspendDialogInfo dialogInfo1 = new SuspendDialogInfo.Builder()
+ .setMessage("dialogMessage1")
+ .build();
+ final SuspendDialogInfo dialogInfo2 = new SuspendDialogInfo.Builder()
+ .setMessage("dialogMessage2")
+ .build();
+
+ final PackageUserState.SuspendParams params1;
+ PackageUserState.SuspendParams params2;
+ params1 = createSuspendParams(dialogInfo1, appExtras1, launcherExtras1);
+ params2 = createSuspendParams(dialogInfo1, appExtras1, launcherExtras1);
+ // Everything is same
+ assertThat(params1.equals(params2), is(true));
+
+ params2 = createSuspendParams(dialogInfo2, appExtras1, launcherExtras1);
+ // DialogInfo is different
+ assertThat(params1.equals(params2), is(false));
+
+ params2 = createSuspendParams(dialogInfo1, appExtras2, launcherExtras1);
+ // app extras are different
+ assertThat(params1.equals(params2), is(false));
+
+ params2 = createSuspendParams(dialogInfo1, appExtras1, launcherExtras2);
+ // Launcher extras are different
+ assertThat(params1.equals(params2), is(false));
+
+ params2 = createSuspendParams(dialogInfo2, appExtras2, launcherExtras2);
+ // Everything is different
+ assertThat(params1.equals(params2), is(false));
+ }
}
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 05905d94dda7..3ea3b3cff7e8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -429,7 +429,7 @@ public class ScanTests {
final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
- assertThat(scanResult.pkgSetting.isOrphaned, is(true));
+ assertThat(scanResult.pkgSetting.installSource.isOrphaned, is(true));
}
private static Matcher<Integer> hasFlag(final int flag) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
index 806c71a7a9b8..6d5b994a63bb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
@@ -132,6 +132,7 @@ public class UserManagerServiceUserInfoTest {
user.profileBadge = 2;
user.partial = true;
user.guestToRemove = true;
+ user.preCreated = true;
return user;
}
@@ -147,5 +148,6 @@ public class UserManagerServiceUserInfoTest {
assertEquals("profile badge not preseved", one.profileBadge, two.profileBadge);
assertEquals("partial not preseved", one.partial, two.partial);
assertEquals("guestToRemove not preseved", one.guestToRemove, two.guestToRemove);
+ assertEquals("preCreated not preseved", one.preCreated, two.preCreated);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
new file mode 100644
index 000000000000..f0b0328ff7d4
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
@@ -0,0 +1,396 @@
+/*
+ * 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.pm;
+
+import static android.content.pm.UserInfo.FLAG_FULL;
+import static android.content.pm.UserInfo.FLAG_GUEST;
+import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
+import static android.content.pm.UserInfo.FLAG_SYSTEM;
+
+import static com.android.server.pm.UserSystemPackageInstaller.PACKAGE_WHITELIST_MODE_PROP;
+import static com.android.server.pm.UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT;
+import static com.android.server.pm.UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE;
+import static com.android.server.pm.UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE;
+import static com.android.server.pm.UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST;
+import static com.android.server.pm.UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_LOG;
+
+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.junit.Assert.fail;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.UserInfo;
+import android.os.Looper;
+import android.os.SystemProperties;
+import android.os.UserManager;
+import android.os.UserManagerInternal;
+import android.support.test.uiautomator.UiDevice;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.SystemConfig;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Tests for UserSystemPackageInstaller.
+ *
+ * <p>Run with:<pre>
+ * atest com.android.server.pm.UserSystemPackageInstallerTest
+ * </pre>
+ */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class UserSystemPackageInstallerTest {
+ private static final String TAG = "UserSystemPackageInstallerTest";
+
+ private UserSystemPackageInstaller mUserSystemPackageInstaller;
+
+ private Context mContext;
+
+ /** Any users created during this test, for them to be removed when it's done. */
+ private final List<Integer> mRemoveUsers = new ArrayList<>();
+ /** Original value of PACKAGE_WHITELIST_MODE_PROP before the test, to reset at end. */
+ private final int mOriginalWhitelistMode = SystemProperties.getInt(
+ PACKAGE_WHITELIST_MODE_PROP, USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT);
+
+ @Before
+ public void setup() {
+ // Currently UserManagerService cannot be instantiated twice inside a VM without a cleanup
+ // TODO: Remove once UMS supports proper dependency injection
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
+ UserManagerService ums = new UserManagerService(InstrumentationRegistry.getContext());
+
+ mUserSystemPackageInstaller = new UserSystemPackageInstaller(ums);
+ mContext = InstrumentationRegistry.getTargetContext();
+ }
+
+ @After
+ public void tearDown() {
+ UserManager um = UserManager.get(mContext);
+ for (int userId : mRemoveUsers) {
+ um.removeUser(userId);
+ }
+ setUserTypePackageWhitelistMode(mOriginalWhitelistMode);
+ }
+
+ /**
+ * Subclass of SystemConfig without running the constructor.
+ */
+ private class SystemConfigTestClass extends SystemConfig {
+ SystemConfigTestClass(boolean readPermissions) {
+ super(readPermissions);
+ }
+ }
+
+ /**
+ * Test that determineWhitelistedPackagesForUserTypes reads SystemConfig information properly.
+ */
+ @Test
+ public void testDetermineWhitelistedPackagesForUserTypes() {
+ SystemConfig sysConfig = new SystemConfigTestClass(false) {
+ @Override
+ public ArrayMap<String, Set<String>> getAndClearPackageToUserTypeWhitelist() {
+ ArrayMap<String, Set<String>> r = new ArrayMap<>();
+ r.put("com.android.package1", new ArraySet<>(Arrays.asList(
+ "PROFILE", "SYSTEM", "GUEST", "FULL", "invalid-garbage1")));
+ r.put("com.android.package2", new ArraySet<>(Arrays.asList(
+ "MANAGED_PROFILE")));
+ return r;
+ }
+
+ @Override
+ public ArrayMap<String, Set<String>> getAndClearPackageToUserTypeBlacklist() {
+ ArrayMap<String, Set<String>> r = new ArrayMap<>();
+ r.put("com.android.package1", new ArraySet<>(Arrays.asList(
+ "FULL", "RESTRICTED", "invalid-garbage2")));
+ return r;
+ }
+ };
+
+ final ArrayMap<String, Integer> expectedOutput = getNewPackageToWhitelistedFlagsMap();
+ expectedOutput.put("com.android.package1",
+ UserInfo.PROFILE_FLAGS_MASK | FLAG_SYSTEM | FLAG_GUEST);
+ expectedOutput.put("com.android.package2",
+ UserInfo.FLAG_MANAGED_PROFILE);
+
+ final ArrayMap<String, Integer> actualOutput =
+ mUserSystemPackageInstaller.determineWhitelistedPackagesForUserTypes(sysConfig);
+
+ assertEquals("Incorrect package-to-user mapping.", expectedOutput, actualOutput);
+ }
+
+ /**
+ * Test that determineWhitelistedPackagesForUserTypes does not include packages that were never
+ * whitelisted properly, but does include packages that were whitelisted but then blacklisted.
+ */
+ @Test
+ public void testDetermineWhitelistedPackagesForUserTypes_noNetWhitelisting() {
+ SystemConfig sysConfig = new SystemConfigTestClass(false) {
+ @Override
+ public ArrayMap<String, Set<String>> getAndClearPackageToUserTypeWhitelist() {
+ ArrayMap<String, Set<String>> r = new ArrayMap<>();
+ r.put("com.android.package1", new ArraySet<>(Arrays.asList("invalid1")));
+ // com.android.package2 has no whitelisting
+ r.put("com.android.package3", new ArraySet<>(Arrays.asList("PROFILE", "FULL")));
+ r.put("com.android.package4", new ArraySet<>(Arrays.asList("PROFILE")));
+ r.put("com.android.package5", new ArraySet<>());
+ // com.android.package6 has no whitelisting
+ return r;
+ }
+
+ @Override
+ public ArrayMap<String, Set<String>> getAndClearPackageToUserTypeBlacklist() {
+ ArrayMap<String, Set<String>> r = new ArrayMap<>();
+ // com.android.package1 has no blacklisting
+ r.put("com.android.package2", new ArraySet<>(Arrays.asList("FULL")));
+ r.put("com.android.package3", new ArraySet<>(Arrays.asList("PROFILE", "FULL")));
+ r.put("com.android.package4", new ArraySet<>(Arrays.asList("PROFILE", "invalid4")));
+ // com.android.package5 has no blacklisting
+ r.put("com.android.package6", new ArraySet<>(Arrays.asList("invalid6")));
+ return r;
+ }
+ };
+
+ final ArrayMap<String, Integer> expectedOutput = getNewPackageToWhitelistedFlagsMap();
+ expectedOutput.put("com.android.package3", 0);
+ expectedOutput.put("com.android.package4", 0);
+
+ final ArrayMap<String, Integer> actualOutput =
+ mUserSystemPackageInstaller.determineWhitelistedPackagesForUserTypes(sysConfig);
+
+ assertEquals("Incorrect package-to-user mapping.", expectedOutput, actualOutput);
+ }
+
+ /**
+ * Tests that shouldInstallPackage correctly determines which packages should be installed.
+ */
+ @Test
+ public void testShouldInstallPackage() {
+ final String packageName1 = "pkg1"; // whitelisted
+ final String packageName2 = "pkg2"; // whitelisted and blacklisted
+ final String packageName3 = "pkg3"; // whitelisted for a different user type
+ final String packageName4 = "pkg4"; // not whitelisted nor blacklisted at all
+
+ final ArrayMap<String, Integer> pkgFlgMap = new ArrayMap<>(); // Whitelist: pkgs per flags
+ pkgFlgMap.put(packageName1, FLAG_FULL);
+ pkgFlgMap.put(packageName2, 0);
+ pkgFlgMap.put(packageName3, FLAG_MANAGED_PROFILE);
+
+ // Whitelist of pkgs for this specific user, i.e. subset of pkgFlagMap for this user.
+ final Set<String> userWhitelist = new ArraySet<>();
+ userWhitelist.add(packageName1);
+
+ final UserSystemPackageInstaller uspi = new UserSystemPackageInstaller(null, pkgFlgMap);
+
+ final PackageParser.Package pkg1 = new PackageParser.Package(packageName1);
+ final PackageParser.Package pkg2 = new PackageParser.Package(packageName2);
+ final PackageParser.Package pkg3 = new PackageParser.Package(packageName3);
+ final PackageParser.Package pkg4 = new PackageParser.Package(packageName4);
+
+ // No implicit whitelist, so only install pkg1.
+ boolean implicit = false;
+ boolean isSysUser = false;
+ assertTrue(uspi.shouldInstallPackage(pkg1, pkgFlgMap, userWhitelist, implicit, isSysUser));
+ assertFalse(uspi.shouldInstallPackage(pkg2, pkgFlgMap, userWhitelist, implicit, isSysUser));
+ assertFalse(uspi.shouldInstallPackage(pkg3, pkgFlgMap, userWhitelist, implicit, isSysUser));
+ assertFalse(uspi.shouldInstallPackage(pkg4, pkgFlgMap, userWhitelist, implicit, isSysUser));
+
+ // Use implicit whitelist, so install pkg1 and pkg4
+ implicit = true;
+ isSysUser = false;
+ assertTrue(uspi.shouldInstallPackage(pkg1, pkgFlgMap, userWhitelist, implicit, isSysUser));
+ assertFalse(uspi.shouldInstallPackage(pkg2, pkgFlgMap, userWhitelist, implicit, isSysUser));
+ assertFalse(uspi.shouldInstallPackage(pkg3, pkgFlgMap, userWhitelist, implicit, isSysUser));
+ assertTrue(uspi.shouldInstallPackage(pkg4, pkgFlgMap, userWhitelist, implicit, isSysUser));
+
+ // For user 0 specifically, we always implicitly whitelist.
+ implicit = false;
+ isSysUser = true;
+ assertTrue(uspi.shouldInstallPackage(pkg1, pkgFlgMap, userWhitelist, implicit, isSysUser));
+ assertFalse(uspi.shouldInstallPackage(pkg2, pkgFlgMap, userWhitelist, implicit, isSysUser));
+ assertFalse(uspi.shouldInstallPackage(pkg3, pkgFlgMap, userWhitelist, implicit, isSysUser));
+ assertTrue(uspi.shouldInstallPackage(pkg4, pkgFlgMap, userWhitelist, implicit, isSysUser));
+ }
+
+ /**
+ * Tests that getWhitelistedPackagesForUserType works properly, assuming that
+ * mWhitelistedPackagesForUserTypes (i.e. determineWhitelistedPackagesForUserTypes) is correct.
+ */
+ @Test
+ public void testGetWhitelistedPackagesForUserType() {
+ final String packageName1 = "pkg1"; // whitelisted for FULL
+ final String packageName2 = "pkg2"; // blacklisted whenever whitelisted
+ final String packageName3 = "pkg3"; // whitelisted for SYSTEM
+ final String packageName4 = "pkg4"; // whitelisted for FULL
+
+ final ArrayMap<String, Integer> pkgFlagMap = new ArrayMap<>(); // Whitelist: pkgs per flags
+ pkgFlagMap.put(packageName1, FLAG_FULL);
+ pkgFlagMap.put(packageName2, 0);
+ pkgFlagMap.put(packageName3, FLAG_SYSTEM);
+ pkgFlagMap.put(packageName4, FLAG_FULL);
+
+ // Whitelist of pkgs for this specific user, i.e. subset of pkgFlagMap for this user.
+ final Set<String> expectedUserWhitelist = new ArraySet<>();
+ expectedUserWhitelist.add(packageName1);
+
+ UserSystemPackageInstaller uspi = new UserSystemPackageInstaller(null, pkgFlagMap);
+
+ Set<String> output = uspi.getWhitelistedPackagesForUserType(FLAG_FULL);
+ assertEquals("Whitelist for FULL is the wrong size", 2, output.size());
+ assertTrue("Whitelist for FULL doesn't contain pkg1", output.contains(packageName1));
+ assertTrue("Whitelist for FULL doesn't contain pkg4", output.contains(packageName4));
+
+ output = uspi.getWhitelistedPackagesForUserType(FLAG_SYSTEM);
+ assertEquals("Whitelist for SYSTEM is the wrong size", 1, output.size());
+ assertTrue("Whitelist for SYSTEM doesn't contain pkg1", output.contains(packageName3));
+ }
+
+ /**
+ * Test that a newly created FULL user has the expected system packages.
+ *
+ * Assumes that SystemConfig and UserManagerService.determineWhitelistedPackagesForUserTypes
+ * work correctly (they are tested separately).
+ */
+ @Test
+ public void testPackagesForCreateUser_full() {
+ final int userFlags = UserInfo.FLAG_FULL;
+ setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE);
+ PackageManager pm = mContext.getPackageManager();
+
+ final SystemConfig sysConfig = new SystemConfigTestClass(true);
+ final ArrayMap<String, Integer> packageMap =
+ mUserSystemPackageInstaller.determineWhitelistedPackagesForUserTypes(sysConfig);
+ final Set<String> expectedPackages = new ArraySet<>(packageMap.size());
+ for (int i = 0; i < packageMap.size(); i++) {
+ if ((userFlags & packageMap.valueAt(i)) != 0) {
+ expectedPackages.add(packageMap.keyAt(i));
+ }
+ }
+
+ final UserManager um = UserManager.get(mContext);
+ final UserInfo user = um.createUser("Test User", userFlags);
+ assertNotNull(user);
+ mRemoveUsers.add(user.id);
+
+ final List<PackageInfo> packageInfos = pm.getInstalledPackagesAsUser(
+ PackageManager.MATCH_SYSTEM_ONLY
+ | PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS,
+ user.id);
+ final Set<String> actualPackages = new ArraySet<>(packageInfos.size());
+ for (PackageInfo p : packageInfos) {
+ actualPackages.add(p.packageName);
+ }
+ checkPackageDifferences(expectedPackages, actualPackages);
+ }
+
+ /** Asserts that actual is a subset of expected. */
+ private void checkPackageDifferences(Set<String> expected, Set<String> actual) {
+ final Set<String> uniqueToExpected = new ArraySet<>(expected);
+ uniqueToExpected.removeAll(actual);
+ final Set<String> uniqueToActual = new ArraySet<>(actual);
+ uniqueToActual.removeAll(expected);
+
+ Log.v(TAG, "Expected list uniquely has " + uniqueToExpected);
+ Log.v(TAG, "Actual list uniquely has " + uniqueToActual);
+
+ assertTrue("User's system packages includes non-whitelisted packages: " + uniqueToActual,
+ uniqueToActual.isEmpty());
+ }
+
+ /**
+ * Test that setEnableUserTypePackageWhitelist() has the correct effect.
+ */
+ @Test
+ public void testSetWhitelistEnabledMode() {
+ setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE);
+ assertFalse(mUserSystemPackageInstaller.isLogMode());
+ assertFalse(mUserSystemPackageInstaller.isEnforceMode());
+ assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode());
+
+ setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_LOG);
+ assertTrue(mUserSystemPackageInstaller.isLogMode());
+ assertFalse(mUserSystemPackageInstaller.isEnforceMode());
+ assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode());
+
+ setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE);
+ assertFalse(mUserSystemPackageInstaller.isLogMode());
+ assertTrue(mUserSystemPackageInstaller.isEnforceMode());
+ assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode());
+
+ setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST);
+ assertFalse(mUserSystemPackageInstaller.isLogMode());
+ assertFalse(mUserSystemPackageInstaller.isEnforceMode());
+ assertTrue(mUserSystemPackageInstaller.isImplicitWhitelistMode());
+
+ setUserTypePackageWhitelistMode(
+ USER_TYPE_PACKAGE_WHITELIST_MODE_LOG | USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE);
+ assertTrue(mUserSystemPackageInstaller.isLogMode());
+ assertTrue(mUserSystemPackageInstaller.isEnforceMode());
+ assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode());
+
+ setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST
+ | USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE);
+ assertFalse(mUserSystemPackageInstaller.isLogMode());
+ assertTrue(mUserSystemPackageInstaller.isEnforceMode());
+ assertTrue(mUserSystemPackageInstaller.isImplicitWhitelistMode());
+ }
+
+ /** Sets the whitelist mode to the desired value via adb's setprop. */
+ private void setUserTypePackageWhitelistMode(int mode) {
+ UiDevice mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ try {
+ String result = mUiDevice.executeShellCommand(String.format("setprop %s %d",
+ PACKAGE_WHITELIST_MODE_PROP, mode));
+ assertFalse("Failed to set sysprop " + PACKAGE_WHITELIST_MODE_PROP + ": " + result,
+ result != null && result.contains("Failed"));
+ } catch (IOException e) {
+ fail("Failed to set sysprop " + PACKAGE_WHITELIST_MODE_PROP + ":\n" + e);
+ }
+ }
+
+ private ArrayMap<String, Integer> getNewPackageToWhitelistedFlagsMap() {
+ final ArrayMap<String, Integer> pkgFlagMap = new ArrayMap<>();
+ // "android" is always treated as whitelisted, regardless of the xml file.
+ pkgFlagMap.put("android", FLAG_SYSTEM | UserInfo.FLAG_FULL | UserInfo.PROFILE_FLAGS_MASK);
+ return pkgFlagMap;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/protolog/ProtoLogImplTest.java b/services/tests/servicestests/src/com/android/server/protolog/ProtoLogImplTest.java
new file mode 100644
index 000000000000..3e9f625ecdd9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/protolog/ProtoLogImplTest.java
@@ -0,0 +1,462 @@
+/*
+ * 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.protolog;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.server.protolog.ProtoLogImpl.PROTOLOG_VERSION;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+import android.util.proto.ProtoInputStream;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.protolog.common.IProtoLogGroup;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.util.LinkedList;
+
+/**
+ * Test class for {@link ProtoLogImpl}.
+ */
+@SuppressWarnings("ConstantConditions")
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class ProtoLogImplTest {
+
+ private static final byte[] MAGIC_HEADER = new byte[]{
+ 0x9, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x4c, 0x4f, 0x47
+ };
+
+ private ProtoLogImpl mProtoLog;
+ private File mFile;
+
+ @Mock
+ private ProtoLogViewerConfigReader mReader;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ final Context testContext = getInstrumentation().getContext();
+ mFile = testContext.getFileStreamPath("tracing_test.dat");
+ //noinspection ResultOfMethodCallIgnored
+ mFile.delete();
+ mProtoLog = new ProtoLogImpl(mFile, 1024 * 1024, mReader);
+ }
+
+ @After
+ public void tearDown() {
+ if (mFile != null) {
+ //noinspection ResultOfMethodCallIgnored
+ mFile.delete();
+ }
+ ProtoLogImpl.setSingleInstance(null);
+ }
+
+ @Test
+ public void isEnabled_returnsFalseByDefault() {
+ assertFalse(mProtoLog.isProtoEnabled());
+ }
+
+ @Test
+ public void isEnabled_returnsTrueAfterStart() {
+ mProtoLog.startProtoLog(mock(PrintWriter.class));
+ assertTrue(mProtoLog.isProtoEnabled());
+ }
+
+ @Test
+ public void isEnabled_returnsFalseAfterStop() {
+ mProtoLog.startProtoLog(mock(PrintWriter.class));
+ mProtoLog.stopProtoLog(mock(PrintWriter.class), true);
+ assertFalse(mProtoLog.isProtoEnabled());
+ }
+
+ @Test
+ public void logFile_startsWithMagicHeader() throws Exception {
+ mProtoLog.startProtoLog(mock(PrintWriter.class));
+ mProtoLog.stopProtoLog(mock(PrintWriter.class), true);
+
+ assertTrue("Log file should exist", mFile.exists());
+
+ byte[] header = new byte[MAGIC_HEADER.length];
+ try (InputStream is = new FileInputStream(mFile)) {
+ assertEquals(MAGIC_HEADER.length, is.read(header));
+ assertArrayEquals(MAGIC_HEADER, header);
+ }
+ }
+
+ @Test
+ public void getSingleInstance() {
+ ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ ProtoLogImpl.setSingleInstance(mockedProtoLog);
+ assertSame(mockedProtoLog, ProtoLogImpl.getSingleInstance());
+ }
+
+ @Test
+ public void d_logCalled() {
+ ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ ProtoLogImpl.setSingleInstance(mockedProtoLog);
+ ProtoLogImpl.d(TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d");
+ verify(mockedProtoLog).log(eq(ProtoLogImpl.LogLevel.DEBUG), eq(
+ TestProtoLogGroup.TEST_GROUP),
+ eq(1234), eq(4321), eq("test %d"), eq(new Object[]{}));
+ }
+
+ @Test
+ public void v_logCalled() {
+ ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ ProtoLogImpl.setSingleInstance(mockedProtoLog);
+ ProtoLogImpl.v(TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d");
+ verify(mockedProtoLog).log(eq(ProtoLogImpl.LogLevel.VERBOSE), eq(
+ TestProtoLogGroup.TEST_GROUP),
+ eq(1234), eq(4321), eq("test %d"), eq(new Object[]{}));
+ }
+
+ @Test
+ public void i_logCalled() {
+ ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ ProtoLogImpl.setSingleInstance(mockedProtoLog);
+ ProtoLogImpl.i(TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d");
+ verify(mockedProtoLog).log(eq(ProtoLogImpl.LogLevel.INFO), eq(
+ TestProtoLogGroup.TEST_GROUP),
+ eq(1234), eq(4321), eq("test %d"), eq(new Object[]{}));
+ }
+
+ @Test
+ public void w_logCalled() {
+ ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ ProtoLogImpl.setSingleInstance(mockedProtoLog);
+ ProtoLogImpl.w(TestProtoLogGroup.TEST_GROUP, 1234,
+ 4321, "test %d");
+ verify(mockedProtoLog).log(eq(ProtoLogImpl.LogLevel.WARN), eq(
+ TestProtoLogGroup.TEST_GROUP),
+ eq(1234), eq(4321), eq("test %d"), eq(new Object[]{}));
+ }
+
+ @Test
+ public void e_logCalled() {
+ ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ ProtoLogImpl.setSingleInstance(mockedProtoLog);
+ ProtoLogImpl.e(TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d");
+ verify(mockedProtoLog).log(eq(ProtoLogImpl.LogLevel.ERROR), eq(
+ TestProtoLogGroup.TEST_GROUP),
+ eq(1234), eq(4321), eq("test %d"), eq(new Object[]{}));
+ }
+
+ @Test
+ public void wtf_logCalled() {
+ ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ ProtoLogImpl.setSingleInstance(mockedProtoLog);
+ ProtoLogImpl.wtf(TestProtoLogGroup.TEST_GROUP,
+ 1234, 4321, "test %d");
+ verify(mockedProtoLog).log(eq(ProtoLogImpl.LogLevel.WTF), eq(
+ TestProtoLogGroup.TEST_GROUP),
+ eq(1234), eq(4321), eq("test %d"), eq(new Object[]{}));
+ }
+
+ @Test
+ public void log_logcatEnabledExternalMessage() {
+ when(mReader.getViewerString(anyInt())).thenReturn("test %b %d %% %o %x %e %g %s %f");
+ ProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
+
+ implSpy.log(
+ ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, null,
+ new Object[]{true, 10000, 20000, 30000, 0.0001, 0.00002, "test", 0.000003});
+
+ verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
+ ProtoLogImpl.LogLevel.INFO),
+ eq("test true 10000 % 47040 7530 1.000000e-04 2.00000e-05 test 0.000003"));
+ verify(mReader).getViewerString(eq(1234));
+ }
+
+ @Test
+ public void log_logcatEnabledInvalidMessage() {
+ when(mReader.getViewerString(anyInt())).thenReturn("test %b %d %% %o %x %e %g %s %f");
+ ProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
+
+ implSpy.log(
+ ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, null,
+ new Object[]{true, 10000, 0.0001, 0.00002, "test"});
+
+ verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
+ ProtoLogImpl.LogLevel.INFO),
+ eq("UNKNOWN MESSAGE (1234) true 10000 1.0E-4 2.0E-5 test"));
+ verify(mReader).getViewerString(eq(1234));
+ }
+
+ @Test
+ public void log_logcatEnabledInlineMessage() {
+ when(mReader.getViewerString(anyInt())).thenReturn("test %d");
+ ProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
+
+ implSpy.log(
+ ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d",
+ new Object[]{5});
+
+ verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
+ ProtoLogImpl.LogLevel.INFO), eq("test 5"));
+ verify(mReader, never()).getViewerString(anyInt());
+ }
+
+ @Test
+ public void log_logcatEnabledNoMessage() {
+ when(mReader.getViewerString(anyInt())).thenReturn(null);
+ ProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
+
+ implSpy.log(
+ ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, null,
+ new Object[]{5});
+
+ verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
+ ProtoLogImpl.LogLevel.INFO), eq("UNKNOWN MESSAGE (1234) 5"));
+ verify(mReader).getViewerString(eq(1234));
+ }
+
+ @Test
+ public void log_logcatDisabled() {
+ when(mReader.getViewerString(anyInt())).thenReturn("test %d");
+ ProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
+
+ implSpy.log(
+ ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d",
+ new Object[]{5});
+
+ verify(implSpy, never()).passToLogcat(any(), any(), any());
+ verify(mReader, never()).getViewerString(anyInt());
+ }
+
+ private static class ProtoLogData {
+ Integer mMessageHash = null;
+ Long mElapsedTime = null;
+ LinkedList<String> mStrParams = new LinkedList<>();
+ LinkedList<Long> mSint64Params = new LinkedList<>();
+ LinkedList<Double> mDoubleParams = new LinkedList<>();
+ LinkedList<Boolean> mBooleanParams = new LinkedList<>();
+ }
+
+ private ProtoLogData readProtoLogSingle(ProtoInputStream ip) throws IOException {
+ while (ip.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (ip.getFieldNumber() == (int) ProtoLogFileProto.VERSION) {
+ assertEquals(PROTOLOG_VERSION, ip.readString(ProtoLogFileProto.VERSION));
+ continue;
+ }
+ if (ip.getFieldNumber() != (int) ProtoLogFileProto.LOG) {
+ continue;
+ }
+ long token = ip.start(ProtoLogFileProto.LOG);
+ ProtoLogData data = new ProtoLogData();
+ while (ip.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (ip.getFieldNumber()) {
+ case (int) ProtoLogMessage.MESSAGE_HASH: {
+ data.mMessageHash = ip.readInt(ProtoLogMessage.MESSAGE_HASH);
+ break;
+ }
+ case (int) ProtoLogMessage.ELAPSED_REALTIME_NANOS: {
+ data.mElapsedTime = ip.readLong(ProtoLogMessage.ELAPSED_REALTIME_NANOS);
+ break;
+ }
+ case (int) ProtoLogMessage.STR_PARAMS: {
+ data.mStrParams.add(ip.readString(ProtoLogMessage.STR_PARAMS));
+ break;
+ }
+ case (int) ProtoLogMessage.SINT64_PARAMS: {
+ data.mSint64Params.add(ip.readLong(ProtoLogMessage.SINT64_PARAMS));
+ break;
+ }
+ case (int) ProtoLogMessage.DOUBLE_PARAMS: {
+ data.mDoubleParams.add(ip.readDouble(ProtoLogMessage.DOUBLE_PARAMS));
+ break;
+ }
+ case (int) ProtoLogMessage.BOOLEAN_PARAMS: {
+ data.mBooleanParams.add(ip.readBoolean(ProtoLogMessage.BOOLEAN_PARAMS));
+ break;
+ }
+ }
+ }
+ ip.end(token);
+ return data;
+ }
+ return null;
+ }
+
+ @Test
+ public void log_protoEnabled() throws Exception {
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(true);
+ mProtoLog.startProtoLog(mock(PrintWriter.class));
+ long before = SystemClock.elapsedRealtimeNanos();
+ mProtoLog.log(
+ ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234,
+ 0b1110101001010100, null,
+ new Object[]{"test", 1, 2, 3, 0.4, 0.5, 0.6, true});
+ long after = SystemClock.elapsedRealtimeNanos();
+ mProtoLog.stopProtoLog(mock(PrintWriter.class), true);
+ try (InputStream is = new FileInputStream(mFile)) {
+ ProtoInputStream ip = new ProtoInputStream(is);
+ ProtoLogData data = readProtoLogSingle(ip);
+ assertNotNull(data);
+ assertEquals(1234, data.mMessageHash.longValue());
+ assertTrue(before <= data.mElapsedTime && data.mElapsedTime <= after);
+ assertArrayEquals(new String[]{"test"}, data.mStrParams.toArray());
+ assertArrayEquals(new Long[]{1L, 2L, 3L}, data.mSint64Params.toArray());
+ assertArrayEquals(new Double[]{0.4, 0.5, 0.6}, data.mDoubleParams.toArray());
+ assertArrayEquals(new Boolean[]{true}, data.mBooleanParams.toArray());
+ }
+ }
+
+ @Test
+ public void log_invalidParamsMask() throws Exception {
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(true);
+ mProtoLog.startProtoLog(mock(PrintWriter.class));
+ long before = SystemClock.elapsedRealtimeNanos();
+ mProtoLog.log(
+ ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234,
+ 0b01100100, null,
+ new Object[]{"test", 1, 0.1, true});
+ long after = SystemClock.elapsedRealtimeNanos();
+ mProtoLog.stopProtoLog(mock(PrintWriter.class), true);
+ try (InputStream is = new FileInputStream(mFile)) {
+ ProtoInputStream ip = new ProtoInputStream(is);
+ ProtoLogData data = readProtoLogSingle(ip);
+ assertNotNull(data);
+ assertEquals(1234, data.mMessageHash.longValue());
+ assertTrue(before <= data.mElapsedTime && data.mElapsedTime <= after);
+ assertArrayEquals(new String[]{"test", "(INVALID PARAMS_MASK) true"},
+ data.mStrParams.toArray());
+ assertArrayEquals(new Long[]{1L}, data.mSint64Params.toArray());
+ assertArrayEquals(new Double[]{0.1}, data.mDoubleParams.toArray());
+ assertArrayEquals(new Boolean[]{}, data.mBooleanParams.toArray());
+ }
+ }
+
+ @Test
+ public void log_protoDisabled() throws Exception {
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
+ mProtoLog.startProtoLog(mock(PrintWriter.class));
+ mProtoLog.log(ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234,
+ 0b11, null, new Object[]{true});
+ mProtoLog.stopProtoLog(mock(PrintWriter.class), true);
+ try (InputStream is = new FileInputStream(mFile)) {
+ ProtoInputStream ip = new ProtoInputStream(is);
+ ProtoLogData data = readProtoLogSingle(ip);
+ assertNull(data);
+ }
+ }
+
+ private enum TestProtoLogGroup implements IProtoLogGroup {
+ TEST_GROUP(true, true, false, "WindowManagetProtoLogTest");
+
+ private final boolean mEnabled;
+ private volatile boolean mLogToProto;
+ private volatile boolean mLogToLogcat;
+ private final String mTag;
+
+ /**
+ * @param enabled set to false to exclude all log statements for this group from
+ * compilation,
+ * they will not be available in runtime.
+ * @param logToProto enable binary logging for the group
+ * @param logToLogcat enable text logging for the group
+ * @param tag name of the source of the logged message
+ */
+ TestProtoLogGroup(boolean enabled, boolean logToProto, boolean logToLogcat, String tag) {
+ this.mEnabled = enabled;
+ this.mLogToProto = logToProto;
+ this.mLogToLogcat = logToLogcat;
+ this.mTag = tag;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ @Override
+ public boolean isLogToProto() {
+ return mLogToProto;
+ }
+
+ @Override
+ public boolean isLogToLogcat() {
+ return mLogToLogcat;
+ }
+
+ @Override
+ public boolean isLogToAny() {
+ return mLogToLogcat || mLogToProto;
+ }
+
+ @Override
+ public String getTag() {
+ return mTag;
+ }
+
+ @Override
+ public void setLogToProto(boolean logToProto) {
+ this.mLogToProto = logToProto;
+ }
+
+ @Override
+ public void setLogToLogcat(boolean logToLogcat) {
+ this.mLogToLogcat = logToLogcat;
+ }
+
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/protolog/ProtoLogViewerConfigReaderTest.java b/services/tests/servicestests/src/com/android/server/protolog/ProtoLogViewerConfigReaderTest.java
new file mode 100644
index 000000000000..02540559fbd0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/protolog/ProtoLogViewerConfigReaderTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.protolog;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.zip.GZIPOutputStream;
+
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class ProtoLogViewerConfigReaderTest {
+ private static final String TEST_VIEWER_CONFIG = "{\n"
+ + " \"version\": \"1.0.0\",\n"
+ + " \"messages\": {\n"
+ + " \"70933285\": {\n"
+ + " \"message\": \"Test completed successfully: %b\",\n"
+ + " \"level\": \"ERROR\",\n"
+ + " \"group\": \"GENERIC_WM\"\n"
+ + " },\n"
+ + " \"1792430067\": {\n"
+ + " \"message\": \"Attempted to add window to a display that does not exist: %d."
+ + " Aborting.\",\n"
+ + " \"level\": \"WARN\",\n"
+ + " \"group\": \"GENERIC_WM\"\n"
+ + " },\n"
+ + " \"1352021864\": {\n"
+ + " \"message\": \"Test 2\",\n"
+ + " \"level\": \"WARN\",\n"
+ + " \"group\": \"GENERIC_WM\"\n"
+ + " },\n"
+ + " \"409412266\": {\n"
+ + " \"message\": \"Window %s is already added\",\n"
+ + " \"level\": \"WARN\",\n"
+ + " \"group\": \"GENERIC_WM\"\n"
+ + " }\n"
+ + " },\n"
+ + " \"groups\": {\n"
+ + " \"GENERIC_WM\": {\n"
+ + " \"tag\": \"WindowManager\"\n"
+ + " }\n"
+ + " }\n"
+ + "}\n";
+
+
+ private ProtoLogViewerConfigReader
+ mConfig = new ProtoLogViewerConfigReader();
+ private File mTestViewerConfig;
+
+ @Before
+ public void setUp() throws IOException {
+ mTestViewerConfig = File.createTempFile("testConfig", ".json.gz");
+ OutputStreamWriter writer = new OutputStreamWriter(
+ new GZIPOutputStream(new FileOutputStream(mTestViewerConfig)));
+ writer.write(TEST_VIEWER_CONFIG);
+ writer.close();
+ }
+
+ @After
+ public void tearDown() {
+ //noinspection ResultOfMethodCallIgnored
+ mTestViewerConfig.delete();
+ }
+
+ @Test
+ public void getViewerString_notLoaded() {
+ assertNull(mConfig.getViewerString(1));
+ }
+
+ @Test
+ public void loadViewerConfig() {
+ mConfig.loadViewerConfig(null, mTestViewerConfig.getAbsolutePath());
+ assertEquals("Test completed successfully: %b", mConfig.getViewerString(70933285));
+ assertEquals("Test 2", mConfig.getViewerString(1352021864));
+ assertEquals("Window %s is already added", mConfig.getViewerString(409412266));
+ assertNull(mConfig.getViewerString(1));
+ }
+
+ @Test
+ public void loadViewerConfig_invalidFile() {
+ mConfig.loadViewerConfig(null, "/tmp/unknown/file/does/not/exist");
+ // No exception is thrown.
+ assertNull(mConfig.getViewerString(1));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/protolog/common/LogDataTypeTest.java b/services/tests/servicestests/src/com/android/server/protolog/common/LogDataTypeTest.java
new file mode 100644
index 000000000000..4c7f5fdc821c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/protolog/common/LogDataTypeTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.protolog.common;
+
+import static org.junit.Assert.assertEquals;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class LogDataTypeTest {
+ @Test
+ public void parseFormatString() {
+ String str = "%b %d %o %x %f %e %g %s %%";
+ List<Integer> out = LogDataType.parseFormatString(str);
+ assertEquals(Arrays.asList(
+ LogDataType.BOOLEAN,
+ LogDataType.LONG,
+ LogDataType.LONG,
+ LogDataType.LONG,
+ LogDataType.DOUBLE,
+ LogDataType.DOUBLE,
+ LogDataType.DOUBLE,
+ LogDataType.STRING
+ ), out);
+ }
+
+ @Test(expected = InvalidFormatStringException.class)
+ public void parseFormatString_invalid() {
+ String str = "%q";
+ LogDataType.parseFormatString(str);
+ }
+
+ @Test
+ public void logDataTypesToBitMask() {
+ List<Integer> types = Arrays.asList(LogDataType.STRING, LogDataType.DOUBLE,
+ LogDataType.LONG, LogDataType.BOOLEAN);
+ int mask = LogDataType.logDataTypesToBitMask(types);
+ assertEquals(0b11011000, mask);
+ }
+
+ @Test(expected = BitmaskConversionException.class)
+ public void logDataTypesToBitMask_toManyParams() {
+ ArrayList<Integer> types = new ArrayList<>();
+ for (int i = 0; i <= 16; i++) {
+ types.add(LogDataType.STRING);
+ }
+ LogDataType.logDataTypesToBitMask(types);
+ }
+
+ @Test
+ public void bitmaskToLogDataTypes() {
+ int bitmask = 0b11011000;
+ List<Integer> types = Arrays.asList(LogDataType.STRING, LogDataType.DOUBLE,
+ LogDataType.LONG, LogDataType.BOOLEAN);
+ for (int i = 0; i < types.size(); i++) {
+ assertEquals(types.get(i).intValue(), LogDataType.bitmaskToLogDataType(bitmask, i));
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
index 8cb5197f2601..a83d94001cf8 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
@@ -16,7 +16,6 @@
package com.android.server.rollback;
-import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
@@ -45,8 +44,6 @@ import org.mockito.Mockito;
import java.io.File;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Set;
@RunWith(JUnit4.class)
public class AppDataRollbackHelperTest {
@@ -109,6 +106,11 @@ public class AppDataRollbackHelperTest {
return createPackageRollbackInfo(packageName, new int[] {});
}
+ private static Rollback createRollbackForId(int rollbackId) {
+ return new Rollback(rollbackId, new File("/does/not/exist"), -1,
+ 0, "com.xyz");
+ }
+
@Test
public void testRestoreAppDataSnapshot_pendingBackupForUser() throws Exception {
Installer installer = mock(Installer.class);
@@ -235,43 +237,35 @@ public class AppDataRollbackHelperTest {
wasRecentlyRestored.getPendingRestores().add(
new RestoreInfo(73 /* userId */, 239 /* appId*/, "seInfo"));
- Rollback dataWithPendingBackup = new Rollback(101, new File("/does/not/exist"), -1);
+ Rollback dataWithPendingBackup = createRollbackForId(101);
dataWithPendingBackup.info.getPackages().add(pendingBackup);
- Rollback dataWithRecentRestore = new Rollback(17239, new File("/does/not/exist"),
- -1);
+ Rollback dataWithRecentRestore = createRollbackForId(17239);
dataWithRecentRestore.info.getPackages().add(wasRecentlyRestored);
- Rollback dataForDifferentUser = new Rollback(17239, new File("/does/not/exist"),
- -1);
+ Rollback dataForDifferentUser = createRollbackForId(17239);
dataForDifferentUser.info.getPackages().add(ignoredInfo);
- Rollback dataForRestore = new Rollback(17239, new File("/does/not/exist"), -1);
+ Rollback dataForRestore = createRollbackForId(17239);
dataForRestore.info.getPackages().add(pendingRestore);
dataForRestore.info.getPackages().add(wasRecentlyRestored);
- Set<Rollback> changed = helper.commitPendingBackupAndRestoreForUser(37,
- Arrays.asList(dataWithPendingBackup, dataWithRecentRestore, dataForDifferentUser,
- dataForRestore));
InOrder inOrder = Mockito.inOrder(installer);
// Check that pending backup and restore for the same package mutually destroyed each other.
+ assertTrue(helper.commitPendingBackupAndRestoreForUser(37, dataWithRecentRestore));
assertEquals(-1, wasRecentlyRestored.getPendingBackups().indexOf(37));
assertNull(wasRecentlyRestored.getRestoreInfo(37));
// Check that backup was performed.
+ assertTrue(helper.commitPendingBackupAndRestoreForUser(37, dataWithPendingBackup));
inOrder.verify(installer).snapshotAppData(eq("com.foo"), eq(37), eq(101),
eq(Installer.FLAG_STORAGE_CE));
assertEquals(-1, pendingBackup.getPendingBackups().indexOf(37));
assertEquals(53, pendingBackup.getCeSnapshotInodes().get(37));
- // Check that changed returns correct Rollback.
- assertEquals(3, changed.size());
- assertTrue(changed.contains(dataWithPendingBackup));
- assertTrue(changed.contains(dataWithRecentRestore));
- assertTrue(changed.contains(dataForRestore));
-
// Check that restore was performed.
+ assertTrue(helper.commitPendingBackupAndRestoreForUser(37, dataForRestore));
inOrder.verify(installer).restoreAppDataSnapshot(
eq("com.abc"), eq(57) /* appId */, eq("seInfo"), eq(37) /* userId */,
eq(17239) /* rollbackId */, eq(Installer.FLAG_STORAGE_CE));
@@ -279,15 +273,4 @@ public class AppDataRollbackHelperTest {
inOrder.verifyNoMoreInteractions();
}
-
- @Test
- public void snapshotAddDataSavesSnapshottedUsersToInfo() {
- Installer installer = mock(Installer.class);
- AppDataRollbackHelper helper = new AppDataRollbackHelper(installer);
-
- PackageRollbackInfo info = createPackageRollbackInfo("com.foo.bar");
- helper.snapshotAppData(5, info, new int[]{10, 11});
-
- assertArrayEquals(info.getSnapshottedUsers().toArray(), new int[]{10, 11});
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
new file mode 100644
index 000000000000..aec489cd1ab4
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.rollback;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.pm.VersionedPackage;
+import android.content.rollback.PackageRollbackInfo;
+import android.util.IntArray;
+import android.util.SparseLongArray;
+
+import com.google.common.truth.Correspondence;
+
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+@RunWith(JUnit4.class)
+public class RollbackStoreTest {
+
+ private static final int ID = 123;
+ private static final int USER = 0;
+ private static final String INSTALLER = "some.installer";
+
+ private static final Correspondence<VersionedPackage, VersionedPackage> VER_PKG_CORR =
+ new Correspondence<VersionedPackage, VersionedPackage>() {
+ @Override
+ public boolean compare(VersionedPackage a, VersionedPackage b) {
+ if (a == null || b == null) {
+ return a == b;
+ }
+ return a.getLongVersionCode() == b.getLongVersionCode()
+ && Objects.equals(a.getPackageName(), b.getPackageName());
+ }
+
+ @Override
+ public String toString() {
+ return "is the same as";
+ }
+ };
+
+ private static final Correspondence<PackageRollbackInfo.RestoreInfo,
+ PackageRollbackInfo.RestoreInfo>
+ RESTORE_INFO_CORR =
+ new Correspondence<PackageRollbackInfo.RestoreInfo, PackageRollbackInfo.RestoreInfo>() {
+ @Override
+ public boolean compare(PackageRollbackInfo.RestoreInfo a,
+ PackageRollbackInfo.RestoreInfo b) {
+ if (a == null || b == null) {
+ return a == b;
+ }
+ return a.userId == b.userId
+ && a.appId == b.appId
+ && Objects.equals(a.seInfo, b.seInfo);
+ }
+
+ @Override
+ public String toString() {
+ return "is the same as";
+ }
+ };
+
+ private static final String JSON_ROLLBACK = "{'info':{'rollbackId':123,'packages':"
+ + "[{'versionRolledBackFrom':{'packageName':'blah','longVersionCode':55},"
+ + "'versionRolledBackTo':{'packageName':'blah1','longVersionCode':50},'pendingBackups':"
+ + "[59,1245,124544],'pendingRestores':[{'userId':498,'appId':32322,'seInfo':'wombles'},"
+ + "{'userId':-895,'appId':1,'seInfo':'pingu'}],'isApex':false,'installedUsers':"
+ + "[498468432,1111,98464],'ceSnapshotInodes':[{'userId':1,'ceSnapshotInode':-6},"
+ + "{'userId':2222,'ceSnapshotInode':81641654445},{'userId':546546,"
+ + "'ceSnapshotInode':345689375}]},{'versionRolledBackFrom':{'packageName':'chips',"
+ + "'longVersionCode':28},'versionRolledBackTo':{'packageName':'com.chips.test',"
+ + "'longVersionCode':48},'pendingBackups':[5],'pendingRestores':[{'userId':18,"
+ + "'appId':-12,'seInfo':''}],'isApex':false,'installedUsers':[55,79],"
+ + "'ceSnapshotInodes':[]}],'isStaged':false,'causePackages':[{'packageName':'hello',"
+ + "'longVersionCode':23},{'packageName':'something','longVersionCode':999}],"
+ + "'committedSessionId':45654465},'timestamp':'2019-10-01T12:29:08.855Z',"
+ + "'stagedSessionId':-1,'state':'enabling','apkSessionId':-1,"
+ + "'restoreUserDataInProgress':true, 'userId':0,"
+ + "'installerPackageName':'some.installer'}";
+
+ @Rule
+ public TemporaryFolder mFolder = new TemporaryFolder();
+
+ private File mRollbackDir;
+
+ private RollbackStore mRollbackStore;
+
+ @Before
+ public void setUp() throws Exception {
+ mRollbackStore = new RollbackStore(mFolder.getRoot());
+ mRollbackDir = mFolder.newFolder(ID + "");
+ mFolder.newFile("rollback.json");
+ }
+
+ @Test
+ public void createNonStaged() {
+ Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER);
+
+ assertThat(rollback.getBackupDir().getAbsolutePath())
+ .isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
+
+ assertThat(rollback.isStaged()).isFalse();
+ assertThat(rollback.info.getRollbackId()).isEqualTo(ID);
+ assertThat(rollback.info.getPackages()).isEmpty();
+ assertThat(rollback.isEnabling()).isTrue();
+ }
+
+ @Test
+ public void createStaged() {
+ Rollback rollback = mRollbackStore.createStagedRollback(ID, 897, USER, INSTALLER);
+
+ assertThat(rollback.getBackupDir().getAbsolutePath())
+ .isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
+
+ assertThat(rollback.isStaged()).isTrue();
+ assertThat(rollback.getStagedSessionId()).isEqualTo(897);
+
+ assertThat(rollback.info.getRollbackId()).isEqualTo(ID);
+ assertThat(rollback.info.getPackages()).isEmpty();
+ assertThat(rollback.isEnabling()).isTrue();
+ }
+
+ @Test
+ public void saveAndLoadRollback() {
+ Rollback origRb = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER);
+
+ origRb.setRestoreUserDataInProgress(true);
+ origRb.info.getCausePackages().add(new VersionedPackage("com.made.up", 2));
+ origRb.info.getCausePackages().add(new VersionedPackage("com.pack.age", 99));
+ origRb.info.setCommittedSessionId(123456);
+
+ PackageRollbackInfo pkgInfo1 =
+ new PackageRollbackInfo(new VersionedPackage("com.made.up", 18),
+ new VersionedPackage("com.something.else", 5), new IntArray(),
+ new ArrayList<>(), false, new IntArray(), new SparseLongArray());
+ pkgInfo1.getPendingBackups().add(8);
+ pkgInfo1.getPendingBackups().add(888);
+ pkgInfo1.getPendingBackups().add(88885);
+ pkgInfo1.getCeSnapshotInodes().put(12, 424);
+ pkgInfo1.getCeSnapshotInodes().put(222772, 10000000000L);
+ pkgInfo1.getCeSnapshotInodes().put(10, -67);
+
+ pkgInfo1.getPendingRestores().add(
+ new PackageRollbackInfo.RestoreInfo(4980, 3442322, "seInfo"));
+ pkgInfo1.getPendingRestores().add(
+ new PackageRollbackInfo.RestoreInfo(-89, 15, "otherSeInfo"));
+
+ pkgInfo1.getSnapshottedUsers().add(11);
+ pkgInfo1.getSnapshottedUsers().add(1);
+ pkgInfo1.getSnapshottedUsers().add(0);
+
+ PackageRollbackInfo pkgInfo2 = new PackageRollbackInfo(
+ new VersionedPackage("another.package", 2),
+ new VersionedPackage("com.test.ing", 48888), new IntArray(), new ArrayList<>(),
+ false, new IntArray(), new SparseLongArray());
+ pkgInfo2.getPendingBackups().add(57);
+
+ pkgInfo2.getPendingRestores().add(
+ new PackageRollbackInfo.RestoreInfo(180, -120, ""));
+
+ origRb.info.getPackages().add(pkgInfo1);
+ origRb.info.getPackages().add(pkgInfo2);
+
+ RollbackStore.saveRollback(origRb);
+
+ List<Rollback> loadedRollbacks = mRollbackStore.loadRollbacks();
+ assertThat(loadedRollbacks).hasSize(1);
+ Rollback loadedRb = loadedRollbacks.get(0);
+
+ assertRollbacksAreEquivalent(loadedRb, origRb);
+ }
+
+ @Test
+ public void loadFromJson() throws Exception {
+ Rollback expectedRb = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER);
+
+ expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z"));
+ expectedRb.setRestoreUserDataInProgress(true);
+ expectedRb.info.getCausePackages().add(new VersionedPackage("hello", 23));
+ expectedRb.info.getCausePackages().add(new VersionedPackage("something", 999));
+ expectedRb.info.setCommittedSessionId(45654465);
+
+ PackageRollbackInfo pkgInfo1 = new PackageRollbackInfo(new VersionedPackage("blah", 55),
+ new VersionedPackage("blah1", 50), new IntArray(), new ArrayList<>(),
+ false, new IntArray(), new SparseLongArray());
+ pkgInfo1.getPendingBackups().add(59);
+ pkgInfo1.getPendingBackups().add(1245);
+ pkgInfo1.getPendingBackups().add(124544);
+ pkgInfo1.getCeSnapshotInodes().put(546546, 345689375);
+ pkgInfo1.getCeSnapshotInodes().put(2222, 81641654445L);
+ pkgInfo1.getCeSnapshotInodes().put(1, -6);
+
+ pkgInfo1.getPendingRestores().add(
+ new PackageRollbackInfo.RestoreInfo(498, 32322, "wombles"));
+ pkgInfo1.getPendingRestores().add(
+ new PackageRollbackInfo.RestoreInfo(-895, 1, "pingu"));
+
+ pkgInfo1.getSnapshottedUsers().add(498468432);
+ pkgInfo1.getSnapshottedUsers().add(1111);
+ pkgInfo1.getSnapshottedUsers().add(98464);
+
+ PackageRollbackInfo pkgInfo2 = new PackageRollbackInfo(new VersionedPackage("chips", 28),
+ new VersionedPackage("com.chips.test", 48), new IntArray(), new ArrayList<>(),
+ false, new IntArray(), new SparseLongArray());
+ pkgInfo2.getPendingBackups().add(5);
+
+ pkgInfo2.getPendingRestores().add(
+ new PackageRollbackInfo.RestoreInfo(18, -12, ""));
+
+ pkgInfo2.getSnapshottedUsers().add(55);
+ pkgInfo2.getSnapshottedUsers().add(79);
+
+ expectedRb.info.getPackages().add(pkgInfo1);
+ expectedRb.info.getPackages().add(pkgInfo2);
+
+ Rollback parsedRb = RollbackStore.rollbackFromJson(
+ new JSONObject(JSON_ROLLBACK), expectedRb.getBackupDir());
+
+ assertRollbacksAreEquivalent(parsedRb, expectedRb);
+ }
+
+ @Test
+ public void saveAndDelete() {
+ Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER);
+
+ RollbackStore.saveRollback(rollback);
+
+ File expectedFile = new File(mRollbackDir.getAbsolutePath() + "/rollback.json");
+
+ assertThat(expectedFile.exists()).isTrue();
+
+ RollbackStore.deleteRollback(rollback);
+
+ assertThat(expectedFile.exists()).isFalse();
+ }
+
+ private void assertRollbacksAreEquivalent(Rollback b, Rollback a) {
+ assertThat(b.info.getRollbackId()).isEqualTo(ID);
+
+ assertThat(b.getBackupDir()).isEqualTo(a.getBackupDir());
+
+ assertThat(b.isRestoreUserDataInProgress())
+ .isEqualTo(a.isRestoreUserDataInProgress());
+
+ assertThat(b.getTimestamp()).isEqualTo(a.getTimestamp());
+
+ assertThat(b.isEnabling()).isEqualTo(a.isEnabling());
+ assertThat(b.isAvailable()).isEqualTo(a.isAvailable());
+ assertThat(b.isCommitted()).isEqualTo(a.isCommitted());
+
+ assertThat(b.isStaged()).isEqualTo(a.isStaged());
+
+ assertThat(b.getApexPackageNames())
+ .containsExactlyElementsIn(a.getApexPackageNames());
+
+ assertThat(b.getStagedSessionId()).isEqualTo(a.getStagedSessionId());
+
+ assertThat(b.info.getCommittedSessionId()).isEqualTo(a.info.getCommittedSessionId());
+
+ assertThat(b.info.getCausePackages()).comparingElementsUsing(VER_PKG_CORR)
+ .containsExactlyElementsIn(a.info.getCausePackages());
+
+ assertThat(b.info.getPackages()).hasSize(a.info.getPackages().size());
+
+ for (int i = 0; i < b.info.getPackages().size(); i++) {
+ assertPackageRollbacksAreEquivalent(
+ b.info.getPackages().get(i), a.info.getPackages().get(i));
+ }
+
+ assertThat(a.getUserId()).isEqualTo(b.getUserId());
+ assertThat(a.getInstallerPackageName()).isEqualTo(b.getInstallerPackageName());
+ }
+
+ private void assertPackageRollbacksAreEquivalent(PackageRollbackInfo b, PackageRollbackInfo a) {
+ assertThat(b.getPackageName()).isEqualTo(a.getPackageName());
+
+ assertThat(b.getVersionRolledBackFrom().getLongVersionCode())
+ .isEqualTo(a.getVersionRolledBackFrom().getLongVersionCode());
+ assertThat(b.getVersionRolledBackFrom().getPackageName())
+ .isEqualTo(a.getVersionRolledBackFrom().getPackageName());
+
+ assertThat(b.getVersionRolledBackTo().getLongVersionCode())
+ .isEqualTo(a.getVersionRolledBackTo().getLongVersionCode());
+ assertThat(b.getVersionRolledBackTo().getPackageName())
+ .isEqualTo(a.getVersionRolledBackTo().getPackageName());
+
+ assertThat(b.getPendingBackups().toArray()).isEqualTo(a.getPendingBackups().toArray());
+
+ assertThat(b.getPendingRestores()).comparingElementsUsing(RESTORE_INFO_CORR)
+ .containsExactlyElementsIn(a.getPendingRestores());
+
+ assertThat(b.isApex()).isEqualTo(a.isApex());
+
+ assertThat(b.getSnapshottedUsers().toArray()).isEqualTo(a.getSnapshottedUsers().toArray());
+
+ assertThat(b.getCeSnapshotInodes().toString())
+ .isEqualTo(a.getCeSnapshotInodes().toString());
+ }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
index d27f1c7e0ce7..e368d634b968 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
@@ -18,22 +18,57 @@ package com.android.server.rollback;
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.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.pm.VersionedPackage;
+import android.content.rollback.PackageRollbackInfo;
+import android.util.IntArray;
+import android.util.SparseLongArray;
+
+import com.google.common.collect.Range;
+
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.io.File;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Arrays;
@RunWith(JUnit4.class)
public class RollbackUnitTest {
+ private static final String PKG_1 = "test.testpackage.pkg1";
+ private static final String PKG_2 = "test.testpackage.pkg2";
+ private static final String PKG_3 = "com.blah.hello.three";
+ private static final String PKG_4 = "com.something.4pack";
+ private static final int USER = 0;
+ private static final String INSTALLER = "some.installer";
+
+ @Mock private AppDataRollbackHelper mMockDataHelper;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
@Test
public void newEmptyStagedRollbackDefaults() {
int rollbackId = 123;
int sessionId = 567;
File file = new File("/test/testing");
- Rollback rollback = new Rollback(rollbackId, file, sessionId);
+ Rollback rollback = new Rollback(rollbackId, file, sessionId, USER, INSTALLER);
assertThat(rollback.isEnabling()).isTrue();
assertThat(rollback.getBackupDir().getAbsolutePath()).isEqualTo("/test/testing");
@@ -46,7 +81,7 @@ public class RollbackUnitTest {
int rollbackId = 123;
File file = new File("/test/testing");
- Rollback rollback = new Rollback(rollbackId, file, -1);
+ Rollback rollback = new Rollback(rollbackId, file, -1, USER, INSTALLER);
assertThat(rollback.isEnabling()).isTrue();
assertThat(rollback.getBackupDir().getAbsolutePath()).isEqualTo("/test/testing");
@@ -54,24 +89,230 @@ public class RollbackUnitTest {
}
@Test
- public void rollbackStateChanges() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1);
+ public void rollbackMadeAvailable() {
+ Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER,
+ INSTALLER);
assertThat(rollback.isEnabling()).isTrue();
assertThat(rollback.isAvailable()).isFalse();
assertThat(rollback.isCommitted()).isFalse();
- rollback.setAvailable();
+ Instant availableTime = Instant.now();
+ rollback.makeAvailable();
assertThat(rollback.isEnabling()).isFalse();
assertThat(rollback.isAvailable()).isTrue();
assertThat(rollback.isCommitted()).isFalse();
- rollback.setCommitted();
+ assertThat(rollback.getTimestamp()).isIn(Range.closed(availableTime, Instant.now()));
+ }
+
+ @Test
+ public void deletedRollbackCannotBeMadeAvailable() {
+ Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+
+ rollback.delete(mMockDataHelper);
+
+ assertThat(rollback.isDeleted()).isTrue();
+
+ rollback.makeAvailable();
- assertThat(rollback.isEnabling()).isFalse();
assertThat(rollback.isAvailable()).isFalse();
- assertThat(rollback.isCommitted()).isTrue();
+ assertThat(rollback.isDeleted()).isTrue();
+ }
+
+ @Test
+ public void getPackageNamesAllAndJustApex() {
+ Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
+ PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 11, true);
+ PackageRollbackInfo pkgInfo3 = newPkgInfoFor(PKG_3, 19, 1, false);
+ PackageRollbackInfo pkgInfo4 = newPkgInfoFor(PKG_4, 12, 1, true);
+
+ rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2, pkgInfo3, pkgInfo4));
+
+ assertThat(rollback.getPackageNames()).containsExactly(PKG_1, PKG_2, PKG_3, PKG_4);
+ assertThat(rollback.getApexPackageNames()).containsExactly(PKG_2, PKG_4);
+ }
+
+ @Test
+ public void includesPackagesAfterEnable() {
+ Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
+ PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
+ PackageRollbackInfo pkgInfo3 = newPkgInfoFor(PKG_3, 157, 156, false);
+ PackageRollbackInfo pkgInfo4 = newPkgInfoFor(PKG_4, 99, 1, true);
+
+ rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2, pkgInfo3, pkgInfo4));
+
+ assertThat(rollback.includesPackage(PKG_2)).isTrue();
+ assertThat(rollback.includesPackage(PKG_3)).isTrue();
+ assertThat(rollback.includesPackage("com.something.else")).isFalse();
+
+ assertThat(rollback.includesPackageWithDifferentVersion(PKG_1, 12)).isFalse();
+ assertThat(rollback.includesPackageWithDifferentVersion(PKG_1, 1)).isTrue();
+
+ assertThat(rollback.includesPackageWithDifferentVersion(PKG_2, 18)).isFalse();
+ assertThat(rollback.includesPackageWithDifferentVersion(PKG_2, 12)).isTrue();
+
+ assertThat(rollback.includesPackageWithDifferentVersion(PKG_3, 157)).isFalse();
+ assertThat(rollback.includesPackageWithDifferentVersion(PKG_3, 156)).isTrue();
+ assertThat(rollback.includesPackageWithDifferentVersion(PKG_3, 15)).isTrue();
+
+ assertThat(rollback.includesPackageWithDifferentVersion(PKG_4, 99)).isFalse();
+ assertThat(rollback.includesPackageWithDifferentVersion(PKG_4, 100)).isTrue();
}
+ @Test
+ public void snapshotWhenEnabling() {
+ Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
+ PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
+ rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
+
+ assertThat(rollback.isEnabling()).isTrue();
+
+ int[] userIds = {4, 77};
+ rollback.snapshotUserData(PKG_2, userIds, mMockDataHelper);
+
+ // Data is snapshotted for the specified package.
+ verify(mMockDataHelper).snapshotAppData(eq(123), pkgRollbackInfoFor(PKG_2), eq(userIds));
+ verify(mMockDataHelper, never())
+ .snapshotAppData(anyInt(), pkgRollbackInfoFor(PKG_1), any());
+ }
+
+ @Test
+ public void snapshotWhenAvailable() {
+ Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
+ PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
+ rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
+
+ rollback.makeAvailable();
+
+ assertThat(rollback.isAvailable()).isTrue();
+
+ int[] userIds = {4, 77};
+ rollback.snapshotUserData(PKG_2, userIds, mMockDataHelper);
+
+ // No data is snapshotted as rollback was not in the enabling state.
+ verify(mMockDataHelper, never())
+ .snapshotAppData(anyInt(), pkgRollbackInfoFor(PKG_1), any());
+ verify(mMockDataHelper, never())
+ .snapshotAppData(anyInt(), pkgRollbackInfoFor(PKG_2), any());
+ }
+
+ @Test
+ public void snapshotWhenDeleted() {
+ Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
+ PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
+ rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
+
+ rollback.delete(mMockDataHelper);
+
+ assertThat(rollback.isDeleted()).isTrue();
+
+ int[] userIds = {4, 77};
+ rollback.snapshotUserData(PKG_2, userIds, mMockDataHelper);
+
+ // No data is snapshotted as rollback was not in the enabling state.
+ verify(mMockDataHelper, never())
+ .snapshotAppData(anyInt(), pkgRollbackInfoFor(PKG_1), any());
+ verify(mMockDataHelper, never())
+ .snapshotAppData(anyInt(), pkgRollbackInfoFor(PKG_2), any());
+ }
+
+ @Test
+ public void snapshotThenDelete() {
+ Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
+ PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
+ rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
+
+ int[] userIds = {12, 18};
+ rollback.snapshotUserData(PKG_2, userIds, mMockDataHelper);
+
+ verify(mMockDataHelper).snapshotAppData(eq(123), pkgRollbackInfoFor(PKG_2), eq(userIds));
+
+ rollback.delete(mMockDataHelper);
+
+ verify(mMockDataHelper).destroyAppDataSnapshot(eq(123), pkgRollbackInfoFor(PKG_2), eq(12));
+ verify(mMockDataHelper).destroyAppDataSnapshot(eq(123), pkgRollbackInfoFor(PKG_2), eq(18));
+
+ assertThat(rollback.isDeleted()).isTrue();
+ }
+
+ @Test
+ public void restoreUserDataDoesNothingIfNotInProgress() {
+ Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
+ PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
+ rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
+
+ assertThat(rollback.isRestoreUserDataInProgress()).isFalse();
+
+ assertThat(rollback.restoreUserDataForPackageIfInProgress(
+ PKG_1, new int[] { 5 }, 333, "", mMockDataHelper)).isFalse();
+
+ verify(mMockDataHelper, never()).restoreAppData(anyInt(), any(), anyInt(), anyInt(), any());
+ }
+
+ @Test
+ public void restoreUserDataDoesNothingIfPackageNotFound() {
+ Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
+ PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
+ rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
+
+ rollback.setRestoreUserDataInProgress(true);
+ assertThat(rollback.isRestoreUserDataInProgress()).isTrue();
+
+ assertThat(rollback.restoreUserDataForPackageIfInProgress(
+ PKG_3, new int[] { 5 }, 333, "", mMockDataHelper)).isFalse();
+
+ verify(mMockDataHelper, never()).restoreAppData(anyInt(), any(), anyInt(), anyInt(), any());
+ }
+
+ @Test
+ public void restoreUserDataRestoresIfInProgressAndPackageFound() {
+ Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
+ PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
+ rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
+
+ rollback.setRestoreUserDataInProgress(true);
+ assertThat(rollback.isRestoreUserDataInProgress()).isTrue();
+
+ assertThat(rollback.restoreUserDataForPackageIfInProgress(
+ PKG_1, new int[] { 5, 7 }, 333, "blah", mMockDataHelper)).isTrue();
+
+ verify(mMockDataHelper).restoreAppData(123, pkgInfo1, 5, 333, "blah");
+ verify(mMockDataHelper).restoreAppData(123, pkgInfo1, 7, 333, "blah");
+ }
+
+ private static PackageRollbackInfo newPkgInfoFor(
+ String packageName, long fromVersion, long toVersion, boolean isApex) {
+ return new PackageRollbackInfo(new VersionedPackage(packageName, fromVersion),
+ new VersionedPackage(packageName, toVersion),
+ new IntArray(), new ArrayList<>(), isApex, new IntArray(), new SparseLongArray());
+ }
+
+ private static class PackageRollbackInfoForPackage implements
+ ArgumentMatcher<PackageRollbackInfo> {
+ private final String mPkg;
+
+ PackageRollbackInfoForPackage(String pkg) {
+ mPkg = pkg;
+ }
+
+ @Override
+ public boolean matches(PackageRollbackInfo pkgRollbackInfo) {
+ return pkgRollbackInfo.getPackageName().equals(mPkg);
+ }
+ }
+
+ private static PackageRollbackInfo pkgRollbackInfoFor(String pkg) {
+ return argThat(new PackageRollbackInfoForPackage(pkg));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java b/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java
index 4fb533f726b2..d1ac19c540a4 100644
--- a/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java
+++ b/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java
@@ -15,14 +15,19 @@
*/
package com.android.server.stats;
-import static com.android.server.stats.ProcfsMemoryUtil.parseVmHWMFromStatus;
+import static com.android.server.stats.ProcfsMemoryUtil.parseCmdline;
+import static com.android.server.stats.ProcfsMemoryUtil.parseMemorySnapshotFromStatus;
import static com.google.common.truth.Truth.assertThat;
import androidx.test.filters.SmallTest;
+import com.android.server.stats.ProcfsMemoryUtil.MemorySnapshot;
+
import org.junit.Test;
+import java.io.ByteArrayOutputStream;
+
/**
* Build/Install/Run:
* atest FrameworksServicesTests:ProcfsMemoryUtilTest
@@ -77,17 +82,60 @@ public class ProcfsMemoryUtilTest {
+ "nonvoluntary_ctxt_switches:\t104\n";
@Test
- public void testParseVmHWMFromStatus_parsesCorrectValue() {
- assertThat(parseVmHWMFromStatus(STATUS_CONTENTS)).isEqualTo(137668);
+ public void testParseMemorySnapshotFromStatus_parsesCorrectValue() {
+ MemorySnapshot snapshot = parseMemorySnapshotFromStatus(STATUS_CONTENTS);
+ assertThat(snapshot.uid).isEqualTo(10083);
+ assertThat(snapshot.rssHighWaterMarkInKilobytes).isEqualTo(137668);
+ assertThat(snapshot.rssInKilobytes).isEqualTo(126776);
+ assertThat(snapshot.anonRssInKilobytes).isEqualTo(37860);
+ assertThat(snapshot.swapInKilobytes).isEqualTo(22);
+ }
+
+ @Test
+ public void testParseMemorySnapshotFromStatus_invalidValue() {
+ MemorySnapshot snapshot =
+ parseMemorySnapshotFromStatus("test\nVmRSS:\tx0x0x\nVmSwap:\t1 kB\ntest");
+ assertThat(snapshot).isNull();
+ }
+
+ @Test
+ public void testParseMemorySnapshotFromStatus_emptyContents() {
+ MemorySnapshot snapshot = parseMemorySnapshotFromStatus("");
+ assertThat(snapshot).isNull();
+ }
+
+ @Test
+ public void testParseCmdline_invalidValue() {
+ byte[] nothing = new byte[] {0x00, 0x74, 0x65, 0x73, 0x74}; // \0test
+
+ assertThat(parseCmdline(bytesToString(nothing))).isEmpty();
+ }
+
+ @Test
+ public void testParseCmdline_correctValue_noNullBytes() {
+ assertThat(parseCmdline("com.google.app")).isEqualTo("com.google.app");
}
@Test
- public void testParseVmHWMFromStatus_invalidValue() {
- assertThat(parseVmHWMFromStatus("test\nVmHWM: x0x0x\ntest")).isEqualTo(0);
+ public void testParseCmdline_correctValue_withNullBytes() {
+ byte[] trailing = new byte[] {0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00}; // test\0\0\0
+
+ assertThat(parseCmdline(bytesToString(trailing))).isEqualTo("test");
+
+ // test\0\0test
+ byte[] inside = new byte[] {0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x74, 0x65, 0x73, 0x74};
+
+ assertThat(parseCmdline(bytesToString(trailing))).isEqualTo("test");
}
@Test
- public void testParseVmHWMFromStatus_emptyContents() {
- assertThat(parseVmHWMFromStatus("")).isEqualTo(0);
+ public void testParseCmdline_emptyContents() {
+ assertThat(parseCmdline("")).isEmpty();
+ }
+
+ private static String bytesToString(byte[] bytes) {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ output.write(bytes, 0, bytes.length);
+ return output.toString();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java
index 9e000770fe42..d79795593456 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java
@@ -23,7 +23,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.app.timedetector.TimeSignal;
+import android.app.timedetector.PhoneTimeSuggestion;
import android.content.Intent;
import android.icu.util.Calendar;
import android.icu.util.GregorianCalendar;
@@ -45,6 +45,8 @@ public class SimpleTimeZoneDetectorStrategyTest {
.setActualTimeUtc(2018, 1, 1, 12, 0, 0)
.build();
+ private static final int ARBITRARY_PHONE_ID = 123456;
+
private Script mScript;
@Before
@@ -53,30 +55,32 @@ public class SimpleTimeZoneDetectorStrategyTest {
}
@Test
- public void testSuggestTime_nitz_timeDetectionEnabled() {
+ public void testSuggestPhoneTime_nitz_timeDetectionEnabled() {
Scenario scenario = SCENARIO_1;
mScript.pokeFakeClocks(scenario)
.pokeTimeDetectionEnabled(true);
- TimeSignal timeSignal = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
+ PhoneTimeSuggestion timeSuggestion =
+ scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
final int clockIncrement = 1000;
long expectSystemClockMillis = scenario.getActualTimeMillis() + clockIncrement;
mScript.simulateTimePassing(clockIncrement)
- .simulateTimeSignalReceived(timeSignal)
+ .simulatePhoneTimeSuggestion(timeSuggestion)
.verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis);
}
@Test
- public void testSuggestTime_systemClockThreshold() {
+ public void testSuggestPhoneTime_systemClockThreshold() {
Scenario scenario = SCENARIO_1;
final int systemClockUpdateThresholdMillis = 1000;
mScript.pokeFakeClocks(scenario)
.pokeThresholds(systemClockUpdateThresholdMillis)
.pokeTimeDetectionEnabled(true);
- TimeSignal timeSignal1 = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
- TimestampedValue<Long> utcTime1 = timeSignal1.getUtcTime();
+ PhoneTimeSuggestion timeSuggestion1 =
+ scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
+ TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
final int clockIncrement = 100;
// Increment the the device clocks to simulate the passage of time.
@@ -86,7 +90,7 @@ public class SimpleTimeZoneDetectorStrategyTest {
TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
// Send the first time signal. It should be used.
- mScript.simulateTimeSignalReceived(timeSignal1)
+ mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
.verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis1);
// Now send another time signal, but one that is too similar to the last one and should be
@@ -95,9 +99,9 @@ public class SimpleTimeZoneDetectorStrategyTest {
TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
mScript.peekElapsedRealtimeMillis(),
mScript.peekSystemClockMillis() + underThresholdMillis);
- TimeSignal timeSignal2 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime2);
+ PhoneTimeSuggestion timeSuggestion2 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime2);
mScript.simulateTimePassing(clockIncrement)
- .simulateTimeSignalReceived(timeSignal2)
+ .simulatePhoneTimeSuggestion(timeSuggestion2)
.verifySystemClockWasNotSetAndResetCallTracking();
// Now send another time signal, but one that is on the threshold and so should be used.
@@ -105,42 +109,44 @@ public class SimpleTimeZoneDetectorStrategyTest {
mScript.peekElapsedRealtimeMillis(),
mScript.peekSystemClockMillis() + systemClockUpdateThresholdMillis);
- TimeSignal timeSignal3 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime3);
+ PhoneTimeSuggestion timeSuggestion3 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime3);
mScript.simulateTimePassing(clockIncrement);
long expectSystemClockMillis3 =
TimeDetectorStrategy.getTimeAt(utcTime3, mScript.peekElapsedRealtimeMillis());
- mScript.simulateTimeSignalReceived(timeSignal3)
+ mScript.simulatePhoneTimeSuggestion(timeSuggestion3)
.verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis3);
}
@Test
- public void testSuggestTime_nitz_timeDetectionDisabled() {
+ public void testSuggestPhoneTime_nitz_timeDetectionDisabled() {
Scenario scenario = SCENARIO_1;
mScript.pokeFakeClocks(scenario)
.pokeTimeDetectionEnabled(false);
- TimeSignal timeSignal = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
- mScript.simulateTimeSignalReceived(timeSignal)
+ PhoneTimeSuggestion timeSuggestion =
+ scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
+ mScript.simulatePhoneTimeSuggestion(timeSuggestion)
.verifySystemClockWasNotSetAndResetCallTracking();
}
@Test
- public void testSuggestTime_nitz_invalidNitzReferenceTimesIgnored() {
+ public void testSuggestPhoneTime_nitz_invalidNitzReferenceTimesIgnored() {
Scenario scenario = SCENARIO_1;
final int systemClockUpdateThreshold = 2000;
mScript.pokeFakeClocks(scenario)
.pokeThresholds(systemClockUpdateThreshold)
.pokeTimeDetectionEnabled(true);
- TimeSignal timeSignal1 = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
- TimestampedValue<Long> utcTime1 = timeSignal1.getUtcTime();
+ PhoneTimeSuggestion timeSuggestion1 =
+ scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
+ TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
// Initialize the strategy / device with a time set from NITZ.
mScript.simulateTimePassing(100);
long expectedSystemClockMillis1 =
TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
- mScript.simulateTimeSignalReceived(timeSignal1)
+ mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
.verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1);
// The UTC time increment should be larger than the system clock update threshold so we
@@ -152,8 +158,8 @@ public class SimpleTimeZoneDetectorStrategyTest {
long referenceTimeBeforeLastSignalMillis = utcTime1.getReferenceTimeMillis() - 1;
TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
referenceTimeBeforeLastSignalMillis, validUtcTimeMillis);
- TimeSignal timeSignal2 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime2);
- mScript.simulateTimeSignalReceived(timeSignal2)
+ PhoneTimeSuggestion timeSuggestion2 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime2);
+ mScript.simulatePhoneTimeSuggestion(timeSuggestion2)
.verifySystemClockWasNotSetAndResetCallTracking();
// Now supply a new signal that has an obviously bogus reference time : substantially in the
@@ -162,8 +168,8 @@ public class SimpleTimeZoneDetectorStrategyTest {
utcTime1.getReferenceTimeMillis() + Integer.MAX_VALUE + 1;
TimestampedValue<Long> utcTime3 = new TimestampedValue<>(
referenceTimeInFutureMillis, validUtcTimeMillis);
- TimeSignal timeSignal3 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime3);
- mScript.simulateTimeSignalReceived(timeSignal3)
+ PhoneTimeSuggestion timeSuggestion3 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime3);
+ mScript.simulatePhoneTimeSuggestion(timeSuggestion3)
.verifySystemClockWasNotSetAndResetCallTracking();
// Just to prove validUtcTimeMillis is valid.
@@ -172,13 +178,13 @@ public class SimpleTimeZoneDetectorStrategyTest {
validReferenceTimeMillis, validUtcTimeMillis);
long expectedSystemClockMillis4 =
TimeDetectorStrategy.getTimeAt(utcTime4, mScript.peekElapsedRealtimeMillis());
- TimeSignal timeSignal4 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime4);
- mScript.simulateTimeSignalReceived(timeSignal4)
+ PhoneTimeSuggestion timeSuggestion4 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime4);
+ mScript.simulatePhoneTimeSuggestion(timeSuggestion4)
.verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis4);
}
@Test
- public void testSuggestTime_timeDetectionToggled() {
+ public void testSuggestPhoneTime_timeDetectionToggled() {
Scenario scenario = SCENARIO_1;
final int clockIncrementMillis = 100;
final int systemClockUpdateThreshold = 2000;
@@ -186,15 +192,16 @@ public class SimpleTimeZoneDetectorStrategyTest {
.pokeThresholds(systemClockUpdateThreshold)
.pokeTimeDetectionEnabled(false);
- TimeSignal timeSignal1 = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
- TimestampedValue<Long> utcTime1 = timeSignal1.getUtcTime();
+ PhoneTimeSuggestion timeSuggestion1 =
+ scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
+ TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
// Simulate time passing.
mScript.simulateTimePassing(clockIncrementMillis);
// Simulate the time signal being received. It should not be used because auto time
// detection is off but it should be recorded.
- mScript.simulateTimeSignalReceived(timeSignal1)
+ mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
.verifySystemClockWasNotSetAndResetCallTracking();
// Simulate more time passing.
@@ -216,7 +223,7 @@ public class SimpleTimeZoneDetectorStrategyTest {
TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
mScript.peekElapsedRealtimeMillis(),
mScript.peekSystemClockMillis() + systemClockUpdateThreshold);
- TimeSignal timeSignal2 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime2);
+ PhoneTimeSuggestion timeSuggestion2 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime2);
// Simulate more time passing.
mScript.simulateTimePassing(clockIncrementMillis);
@@ -226,7 +233,7 @@ public class SimpleTimeZoneDetectorStrategyTest {
// The new time, though valid, should not be set in the system clock because auto time is
// disabled.
- mScript.simulateTimeSignalReceived(timeSignal2)
+ mScript.simulatePhoneTimeSuggestion(timeSuggestion2)
.verifySystemClockWasNotSetAndResetCallTracking();
// Turn on auto time detection.
@@ -234,17 +241,6 @@ public class SimpleTimeZoneDetectorStrategyTest {
.verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis2);
}
- @Test
- public void testSuggestTime_unknownSource() {
- Scenario scenario = SCENARIO_1;
- mScript.pokeFakeClocks(scenario)
- .pokeTimeDetectionEnabled(true);
-
- TimeSignal timeSignal = scenario.createTimeSignalForActual("unknown");
- mScript.simulateTimeSignalReceived(timeSignal)
- .verifySystemClockWasNotSetAndResetCallTracking();
- }
-
/**
* A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving
* like the real thing should, it also asserts preconditions.
@@ -407,8 +403,8 @@ public class SimpleTimeZoneDetectorStrategyTest {
return mFakeCallback.peekSystemClockMillis();
}
- Script simulateTimeSignalReceived(TimeSignal timeSignal) {
- mSimpleTimeDetectorStrategy.suggestTime(timeSignal);
+ Script simulatePhoneTimeSuggestion(PhoneTimeSuggestion timeSuggestion) {
+ mSimpleTimeDetectorStrategy.suggestPhoneTime(timeSuggestion);
return this;
}
@@ -466,10 +462,10 @@ public class SimpleTimeZoneDetectorStrategyTest {
return mActualTimeMillis;
}
- TimeSignal createTimeSignalForActual(String sourceId) {
+ PhoneTimeSuggestion createPhoneTimeSuggestionForActual(int phoneId) {
TimestampedValue<Long> time = new TimestampedValue<>(
mInitialDeviceRealtimeMillis, mActualTimeMillis);
- return new TimeSignal(sourceId, time);
+ return new PhoneTimeSuggestion(phoneId, time);
}
static class Builder {
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 45fef764482f..37da01824e88 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -28,7 +28,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.timedetector.TimeSignal;
+import android.app.timedetector.PhoneTimeSuggestion;
import android.content.Context;
import android.content.pm.PackageManager;
import android.util.TimestampedValue;
@@ -67,10 +67,10 @@ public class TimeDetectorServiceTest {
public void testStubbedCall_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingPermission(anyString(), any());
- TimeSignal timeSignal = createNitzTimeSignal();
+ PhoneTimeSuggestion phoneTimeSuggestion = createPhoneTimeSuggestion();
try {
- mTimeDetectorService.suggestTime(timeSignal);
+ mTimeDetectorService.suggestPhoneTime(phoneTimeSuggestion);
} finally {
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.SET_TIME), anyString());
@@ -78,15 +78,15 @@ public class TimeDetectorServiceTest {
}
@Test
- public void testSuggestTime() {
+ public void testSuggestPhoneTime() {
doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
- TimeSignal timeSignal = createNitzTimeSignal();
- mTimeDetectorService.suggestTime(timeSignal);
+ PhoneTimeSuggestion phoneTimeSuggestion = createPhoneTimeSuggestion();
+ mTimeDetectorService.suggestPhoneTime(phoneTimeSuggestion);
verify(mMockContext)
.enforceCallingPermission(eq(android.Manifest.permission.SET_TIME), anyString());
- mStubbedTimeDetectorStrategy.verifySuggestTimeCalled(timeSignal);
+ mStubbedTimeDetectorStrategy.verifySuggestPhoneTimeCalled(phoneTimeSuggestion);
}
@Test
@@ -115,15 +115,16 @@ public class TimeDetectorServiceTest {
mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled(false);
}
- private static TimeSignal createNitzTimeSignal() {
+ private static PhoneTimeSuggestion createPhoneTimeSuggestion() {
+ int phoneId = 1234;
TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L);
- return new TimeSignal(TimeSignal.SOURCE_ID_NITZ, timeValue);
+ return new PhoneTimeSuggestion(phoneId, timeValue);
}
private static class StubbedTimeDetectorStrategy implements TimeDetectorStrategy {
// Call tracking.
- private TimeSignal mLastSuggestedTime;
+ private PhoneTimeSuggestion mLastPhoneSuggestion;
private Boolean mLastAutoTimeDetectionToggle;
private boolean mDumpCalled;
@@ -132,9 +133,9 @@ public class TimeDetectorServiceTest {
}
@Override
- public void suggestTime(TimeSignal timeSignal) {
+ public void suggestPhoneTime(PhoneTimeSuggestion timeSuggestion) {
resetCallTracking();
- mLastSuggestedTime = timeSignal;
+ mLastPhoneSuggestion = timeSuggestion;
}
@Override
@@ -150,13 +151,13 @@ public class TimeDetectorServiceTest {
}
void resetCallTracking() {
- mLastSuggestedTime = null;
+ mLastPhoneSuggestion = null;
mLastAutoTimeDetectionToggle = null;
mDumpCalled = false;
}
- void verifySuggestTimeCalled(TimeSignal expectedSignal) {
- assertEquals(expectedSignal, mLastSuggestedTime);
+ void verifySuggestPhoneTimeCalled(PhoneTimeSuggestion expectedSignal) {
+ assertEquals(expectedSignal, mLastPhoneSuggestion);
}
void verifyHandleAutoTimeDetectionToggleCalled(boolean expectedEnable) {
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 4ffcf8fa18b7..12ba219d0365 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -47,7 +47,6 @@ import static org.mockito.Mockito.mock;
import android.app.usage.AppStandbyInfo;
import android.app.usage.UsageEvents;
-import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.ContextWrapper;
@@ -77,7 +76,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
@@ -108,8 +106,6 @@ public class AppStandbyControllerTests {
private static final long WORKING_SET_THRESHOLD = 12 * HOUR_MS;
private static final long FREQUENT_THRESHOLD = 24 * HOUR_MS;
private static final long RARE_THRESHOLD = 48 * HOUR_MS;
- // Short STABLE_CHARGING_THRESHOLD for testing purposes
- private static final long STABLE_CHARGING_THRESHOLD = 2000;
/** Mock variable used in {@link MyInjector#isPackageInstalled(String, int, int)} */
private static boolean isPackageInstalled = true;
@@ -132,7 +128,6 @@ public class AppStandbyControllerTests {
static class MyInjector extends AppStandbyController.Injector {
long mElapsedRealtime;
boolean mIsAppIdleEnabled = true;
- boolean mIsCharging;
List<String> mPowerSaveWhitelistExceptIdle = new ArrayList<>();
boolean mDisplayOn;
DisplayManager.DisplayListener mDisplayListener;
@@ -167,11 +162,6 @@ public class AppStandbyControllerTests {
}
@Override
- boolean isCharging() {
- return mIsCharging;
- }
-
- @Override
boolean isPowerSaveWhitelistExceptIdleApp(String packageName) throws RemoteException {
return mPowerSaveWhitelistExceptIdle.contains(packageName);
}
@@ -228,8 +218,7 @@ public class AppStandbyControllerTests {
return "screen_thresholds=0/0/0/" + HOUR_MS + ",elapsed_thresholds=0/"
+ WORKING_SET_THRESHOLD + "/"
+ FREQUENT_THRESHOLD + "/"
- + RARE_THRESHOLD + ","
- + "stable_charging_threshold=" + STABLE_CHARGING_THRESHOLD;
+ + RARE_THRESHOLD;
}
@Override
@@ -273,13 +262,6 @@ public class AppStandbyControllerTests {
} catch (PackageManager.NameNotFoundException nnfe) {}
}
- private void setChargingState(AppStandbyController controller, boolean charging) {
- mInjector.mIsCharging = charging;
- if (controller != null) {
- controller.setChargingState(charging);
- }
- }
-
private void setAppIdleEnabled(AppStandbyController controller, boolean enabled) {
mInjector.mIsAppIdleEnabled = enabled;
if (controller != null) {
@@ -296,7 +278,6 @@ public class AppStandbyControllerTests {
controller.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
mInjector.setDisplayOn(false);
mInjector.setDisplayOn(true);
- setChargingState(controller, false);
controller.checkIdleStates(USER_ID);
assertNotEquals(STANDBY_BUCKET_EXEMPTED,
controller.getAppStandbyBucket(PACKAGE_1, USER_ID,
@@ -314,65 +295,6 @@ public class AppStandbyControllerTests {
MyContextWrapper myContext = new MyContextWrapper(InstrumentationRegistry.getContext());
mInjector = new MyInjector(myContext, Looper.getMainLooper());
mController = setupController();
- setChargingState(mController, false);
- }
-
- private class TestParoleListener extends UsageStatsManagerInternal.AppIdleStateChangeListener {
- private boolean mOnParole = false;
- private CountDownLatch mLatch;
- private long mLastParoleChangeTime;
- private boolean mIsExpecting = false;
- private boolean mExpectedParoleState;
-
- public boolean getParoleState() {
- synchronized (this) {
- return mOnParole;
- }
- }
-
- public void rearmLatch() {
- synchronized (this) {
- mLatch = new CountDownLatch(1);
- mIsExpecting = false;
- }
- }
-
- public void rearmLatch(boolean expectedParoleState) {
- synchronized (this) {
- mLatch = new CountDownLatch(1);
- mIsExpecting = true;
- mExpectedParoleState = expectedParoleState;
- }
- }
-
- public void awaitOnLatch(long time) throws Exception {
- mLatch.await(time, TimeUnit.MILLISECONDS);
- }
-
- public long getLastParoleChangeTime() {
- synchronized (this) {
- return mLastParoleChangeTime;
- }
- }
-
- @Override
- public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
- int bucket, int reason) {
- }
-
- @Override
- public void onParoleStateChanged(boolean isParoleOn) {
- synchronized (this) {
- // Only record information if it is being looked for
- if (mLatch != null && mLatch.getCount() > 0) {
- mOnParole = isParoleOn;
- mLastParoleChangeTime = getCurrentTime();
- if (!mIsExpecting || isParoleOn == mExpectedParoleState) {
- mLatch.countDown();
- }
- }
- }
- }
}
@Test
@@ -383,133 +305,6 @@ public class AppStandbyControllerTests {
mInjector.mElapsedRealtime, false));
}
- @Test
- public void testCharging() throws Exception {
- long startTime;
- TestParoleListener paroleListener = new TestParoleListener();
- long marginOfError = 200;
-
- // Charging
- paroleListener.rearmLatch();
- mController.addListener(paroleListener);
- startTime = getCurrentTime();
- setChargingState(mController, true);
- paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
- assertTrue(paroleListener.mOnParole);
- // Parole will only be granted after device has been charging for a sufficient amount of
- // time.
- assertEquals(STABLE_CHARGING_THRESHOLD,
- paroleListener.getLastParoleChangeTime() - startTime,
- marginOfError);
-
- // Discharging
- paroleListener.rearmLatch();
- startTime = getCurrentTime();
- setChargingState(mController, false);
- mController.checkIdleStates(USER_ID);
- paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
- assertFalse(paroleListener.getParoleState());
- // Parole should be revoked immediately
- assertEquals(0,
- paroleListener.getLastParoleChangeTime() - startTime,
- marginOfError);
-
- // Brief Charging
- paroleListener.rearmLatch();
- setChargingState(mController, true);
- setChargingState(mController, false);
- // Device stopped charging before the stable charging threshold.
- // Parole should not be granted at the end of the threshold
- paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
- assertFalse(paroleListener.getParoleState());
-
- // Charging Again
- paroleListener.rearmLatch();
- startTime = getCurrentTime();
- setChargingState(mController, true);
- paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
- assertTrue(paroleListener.getParoleState());
- assertTrue(paroleListener.mOnParole);
- assertEquals(STABLE_CHARGING_THRESHOLD,
- paroleListener.getLastParoleChangeTime() - startTime,
- marginOfError);
- }
-
- @Test
- public void testEnabledState() throws Exception {
- TestParoleListener paroleListener = new TestParoleListener();
- paroleListener.rearmLatch(true);
- mController.addListener(paroleListener);
- long lastUpdateTime;
-
- // Test that listeners are notified if enabled changes when the device is not in parole.
- setChargingState(mController, false);
-
- // Start off not enabled. Device is effectively in permanent parole.
- setAppIdleEnabled(mController, false);
- // Since AppStandbyController uses a handler to notify listeners of a state change, there is
- // some inherent latency between changing the state and getting the notification. We need to
- // wait until the paroleListener has been notified that parole is on before continuing with
- // the test.
- paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
- assertTrue(paroleListener.mOnParole);
-
- // Enable controller
- paroleListener.rearmLatch();
- setAppIdleEnabled(mController, true);
- paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
- assertFalse(paroleListener.mOnParole);
- lastUpdateTime = paroleListener.getLastParoleChangeTime();
-
- paroleListener.rearmLatch();
- setAppIdleEnabled(mController, true);
- paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
- assertFalse(paroleListener.mOnParole);
- // Make sure AppStandbyController doesn't notify listeners when there's no change.
- assertEquals(lastUpdateTime, paroleListener.getLastParoleChangeTime());
-
- // Disable controller
- paroleListener.rearmLatch();
- setAppIdleEnabled(mController, false);
- paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
- assertTrue(paroleListener.mOnParole);
- lastUpdateTime = paroleListener.getLastParoleChangeTime();
-
- paroleListener.rearmLatch();
- setAppIdleEnabled(mController, false);
- paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
- assertTrue(paroleListener.mOnParole);
- // Make sure AppStandbyController doesn't notify listeners when there's no change.
- assertEquals(lastUpdateTime, paroleListener.getLastParoleChangeTime());
-
-
- // Test that listeners aren't notified if enabled status changes when the device is already
- // in parole.
-
- // A device is in parole whenever it's charging.
- setChargingState(mController, true);
-
- // Start off not enabled.
- paroleListener.rearmLatch();
- setAppIdleEnabled(mController, false);
- paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
- assertTrue(paroleListener.mOnParole);
- lastUpdateTime = paroleListener.getLastParoleChangeTime();
-
- // Test that toggling doesn't notify the listener.
- paroleListener.rearmLatch();
- setAppIdleEnabled(mController, true);
- paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
- assertTrue(paroleListener.mOnParole);
- assertEquals(lastUpdateTime, paroleListener.getLastParoleChangeTime());
-
- paroleListener.rearmLatch();
- setAppIdleEnabled(mController, false);
- paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
- assertTrue(paroleListener.mOnParole);
- assertEquals(lastUpdateTime, paroleListener.getLastParoleChangeTime());
- }
-
private void assertTimeout(AppStandbyController controller, long elapsedTime, int bucket) {
mInjector.mElapsedRealtime = elapsedTime;
controller.checkIdleStates(USER_ID);
@@ -804,8 +599,6 @@ public class AppStandbyControllerTests {
@Test
public void testSystemInteractionTimeout() throws Exception {
- setChargingState(mController, false);
-
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
// Fast forward to RARE
mInjector.mElapsedRealtime = RARE_THRESHOLD + 100;
@@ -829,8 +622,6 @@ public class AppStandbyControllerTests {
@Test
public void testInitialForegroundServiceTimeout() throws Exception {
- setChargingState(mController, false);
-
mInjector.mElapsedRealtime = 1 * RARE_THRESHOLD + 100;
// Make sure app is in NEVER bucket
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
diff --git a/services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java b/services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java
new file mode 100644
index 000000000000..f1b2ef811885
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/usage/IntervalStatsTests.java
@@ -0,0 +1,191 @@
+/*
+ * 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.usage;
+
+import static android.app.usage.UsageEvents.Event.MAX_EVENT_TYPE;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import android.app.usage.UsageEvents;
+import android.content.res.Configuration;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IntervalStatsTests {
+ private static final int NUMBER_OF_PACKAGES = 7;
+ private static final int NUMBER_OF_EVENTS_PER_PACKAGE = 200;
+ private static final int NUMBER_OF_EVENTS = NUMBER_OF_PACKAGES * NUMBER_OF_EVENTS_PER_PACKAGE;
+
+ private long mEndTime = 0;
+
+ private void populateIntervalStats(IntervalStats intervalStats) {
+ final int timeProgression = 23;
+ long time = System.currentTimeMillis() - (NUMBER_OF_EVENTS * timeProgression);
+
+ intervalStats.majorVersion = 7;
+ intervalStats.minorVersion = 8;
+ intervalStats.beginTime = time;
+ intervalStats.interactiveTracker.count = 2;
+ intervalStats.interactiveTracker.duration = 111111;
+ intervalStats.nonInteractiveTracker.count = 3;
+ intervalStats.nonInteractiveTracker.duration = 222222;
+ intervalStats.keyguardShownTracker.count = 4;
+ intervalStats.keyguardShownTracker.duration = 333333;
+ intervalStats.keyguardHiddenTracker.count = 5;
+ intervalStats.keyguardHiddenTracker.duration = 4444444;
+
+ for (int i = 0; i < NUMBER_OF_EVENTS; i++) {
+ UsageEvents.Event event = new UsageEvents.Event();
+ final int packageInt = ((i / 3) % NUMBER_OF_PACKAGES); // clusters of 3 events
+ event.mPackage = "fake.package.name" + packageInt;
+ if (packageInt == 3) {
+ // Third app is an instant app
+ event.mFlags |= UsageEvents.Event.FLAG_IS_PACKAGE_INSTANT_APP;
+ }
+
+ final int instanceId = i % 11;
+ event.mClass = ".fake.class.name" + instanceId;
+ event.mTimeStamp = time;
+ event.mEventType = i % (MAX_EVENT_TYPE + 1); //"random" event type
+ event.mInstanceId = instanceId;
+
+
+ final int rootPackageInt = (i % 5); // 5 "apps" start each task
+ event.mTaskRootPackage = "fake.package.name" + rootPackageInt;
+
+ final int rootClassInt = i % 6;
+ event.mTaskRootClass = ".fake.class.name" + rootClassInt;
+
+ switch (event.mEventType) {
+ case UsageEvents.Event.CONFIGURATION_CHANGE:
+ event.mConfiguration = new Configuration(); //empty config
+ break;
+ case UsageEvents.Event.SHORTCUT_INVOCATION:
+ event.mShortcutId = "shortcut" + (i % 8); //"random" shortcut
+ break;
+ case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+ //"random" bucket and reason
+ event.mBucketAndReason = (((i % 5 + 1) * 10) << 16) & (i % 5 + 1) << 8;
+ break;
+ case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+ event.mNotificationChannelId = "channel" + (i % 5); //"random" channel
+ break;
+ }
+
+ intervalStats.addEvent(event);
+ intervalStats.update(event.mPackage, event.mClass, event.mTimeStamp, event.mEventType,
+ event.mInstanceId);
+
+ time += timeProgression; // Arbitrary progression of time
+ }
+ mEndTime = time;
+
+ final Configuration config1 = new Configuration();
+ config1.fontScale = 3.3f;
+ config1.mcc = 4;
+ intervalStats.getOrCreateConfigurationStats(config1);
+
+ final Configuration config2 = new Configuration();
+ config2.mnc = 5;
+ config2.setLocale(new Locale("en", "US"));
+ intervalStats.getOrCreateConfigurationStats(config2);
+
+ intervalStats.activeConfiguration = config2;
+ }
+
+ @Test
+ public void testObfuscation() {
+ final IntervalStats intervalStats = new IntervalStats();
+ populateIntervalStats(intervalStats);
+
+ final PackagesTokenData packagesTokenData = new PackagesTokenData();
+ intervalStats.obfuscateData(packagesTokenData);
+
+ // data is populated with 7 different "apps"
+ assertEquals(packagesTokenData.tokensToPackagesMap.size(), NUMBER_OF_PACKAGES);
+ assertEquals(packagesTokenData.packagesToTokensMap.size(), NUMBER_OF_PACKAGES);
+ assertEquals(packagesTokenData.counter, NUMBER_OF_PACKAGES + 1);
+
+ assertEquals(intervalStats.events.size(), NUMBER_OF_EVENTS);
+ assertEquals(intervalStats.packageStats.size(), NUMBER_OF_PACKAGES);
+ }
+
+ @Test
+ public void testDeobfuscation() {
+ final IntervalStats intervalStats = new IntervalStats();
+ populateIntervalStats(intervalStats);
+
+ final PackagesTokenData packagesTokenData = new PackagesTokenData();
+ intervalStats.obfuscateData(packagesTokenData);
+ intervalStats.deobfuscateData(packagesTokenData);
+
+ // ensure deobfuscation doesn't update any of the mappings data
+ assertEquals(packagesTokenData.tokensToPackagesMap.size(), NUMBER_OF_PACKAGES);
+ assertEquals(packagesTokenData.packagesToTokensMap.size(), NUMBER_OF_PACKAGES);
+ assertEquals(packagesTokenData.counter, NUMBER_OF_PACKAGES + 1);
+
+ // ensure deobfuscation didn't remove any events or usage stats
+ assertEquals(intervalStats.events.size(), NUMBER_OF_EVENTS);
+ assertEquals(intervalStats.packageStats.size(), NUMBER_OF_PACKAGES);
+ }
+
+ @Test
+ public void testBadDataOnDeobfuscation() {
+ final IntervalStats intervalStats = new IntervalStats();
+ populateIntervalStats(intervalStats);
+
+ final PackagesTokenData packagesTokenData = new PackagesTokenData();
+ intervalStats.obfuscateData(packagesTokenData);
+ intervalStats.packageStats.clear();
+
+ // remove the mapping for token 2
+ packagesTokenData.tokensToPackagesMap.remove(2);
+
+ intervalStats.deobfuscateData(packagesTokenData);
+ // deobfuscation should have removed all events mapped to package token 2
+ assertEquals(intervalStats.events.size(),
+ NUMBER_OF_EVENTS - NUMBER_OF_EVENTS_PER_PACKAGE - 1);
+ assertEquals(intervalStats.packageStats.size(), NUMBER_OF_PACKAGES - 1);
+ }
+
+ @Test
+ public void testBadPackageDataOnDeobfuscation() {
+ final IntervalStats intervalStats = new IntervalStats();
+ populateIntervalStats(intervalStats);
+
+ final PackagesTokenData packagesTokenData = new PackagesTokenData();
+ intervalStats.obfuscateData(packagesTokenData);
+ intervalStats.packageStats.clear();
+
+ // remove mapping number 2 within package 3 (random)
+ packagesTokenData.tokensToPackagesMap.valueAt(3).remove(2);
+
+ intervalStats.deobfuscateData(packagesTokenData);
+ // deobfuscation should not have removed all events for a package - however, it's possible
+ // that some events were removed because of how shortcut and notification events are handled
+ assertTrue(intervalStats.events.size() > NUMBER_OF_EVENTS - NUMBER_OF_EVENTS_PER_PACKAGE);
+ assertEquals(intervalStats.packageStats.size(), NUMBER_OF_PACKAGES);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index c55f459c9fe7..df0c37a9856c 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -48,7 +48,7 @@ import java.util.Locale;
@SmallTest
public class UsageStatsDatabaseTest {
- private static final int MAX_TESTED_VERSION = 4;
+ private static final int MAX_TESTED_VERSION = 5;
protected Context mContext;
private UsageStatsDatabase mUsageStatsDatabase;
private File mTestDir;
@@ -74,6 +74,7 @@ public class UsageStatsDatabaseTest {
mContext = InstrumentationRegistry.getTargetContext();
mTestDir = new File(mContext.getFilesDir(), "UsageStatsDatabaseTest");
mUsageStatsDatabase = new UsageStatsDatabase(mTestDir);
+ mUsageStatsDatabase.readMappingsLocked();
mUsageStatsDatabase.init(1);
populateIntervalStats();
clearUsageStatsFiles();
@@ -259,6 +260,24 @@ public class UsageStatsDatabaseTest {
void compareUsageEvent(Event e1, Event e2, int debugId, int minVersion) {
switch (minVersion) {
+ case 5: // test fields added in version 5
+ assertEquals(e1.mPackageToken, e2.mPackageToken, "Usage event " + debugId);
+ assertEquals(e1.mClassToken, e2.mClassToken, "Usage event " + debugId);
+ assertEquals(e1.mTaskRootPackageToken, e2.mTaskRootPackageToken,
+ "Usage event " + debugId);
+ assertEquals(e1.mTaskRootClassToken, e2.mTaskRootClassToken,
+ "Usage event " + debugId);
+ switch (e1.mEventType) {
+ case Event.SHORTCUT_INVOCATION:
+ assertEquals(e1.mShortcutIdToken, e2.mShortcutIdToken,
+ "Usage event " + debugId);
+ break;
+ case Event.NOTIFICATION_INTERRUPTION:
+ assertEquals(e1.mNotificationChannelIdToken, e2.mNotificationChannelIdToken,
+ "Usage event " + debugId);
+ break;
+ }
+ // fallthrough
case 4: // test fields added in version 4
assertEquals(e1.mInstanceId, e2.mInstanceId, "Usage event " + debugId);
assertEquals(e1.mTaskRootPackage, e2.mTaskRootPackage, "Usage event " + debugId);
@@ -370,11 +389,16 @@ public class UsageStatsDatabaseTest {
void runVersionChangeTest(int oldVersion, int newVersion, int interval) throws IOException {
// Write IntervalStats to disk in old version format
UsageStatsDatabase prevDB = new UsageStatsDatabase(mTestDir, oldVersion);
+ prevDB.readMappingsLocked();
prevDB.init(1);
prevDB.putUsageStats(interval, mIntervalStats);
+ if (oldVersion >= 5) {
+ prevDB.writeMappingsLocked();
+ }
// Simulate an upgrade to a new version and read from the disk
UsageStatsDatabase newDB = new UsageStatsDatabase(mTestDir, newVersion);
+ newDB.readMappingsLocked();
newDB.init(mEndTime);
List<IntervalStats> stats = newDB.queryUsageStats(interval, 0, mEndTime,
mIntervalStatsVerifier);
@@ -394,6 +418,7 @@ public class UsageStatsDatabaseTest {
*/
void runBackupRestoreTest(int version) throws IOException {
UsageStatsDatabase prevDB = new UsageStatsDatabase(mTestDir);
+ prevDB.readMappingsLocked();
prevDB.init(1);
prevDB.putUsageStats(UsageStatsManager.INTERVAL_DAILY, mIntervalStats);
// Create a backup with a specific version
@@ -402,6 +427,7 @@ public class UsageStatsDatabaseTest {
clearUsageStatsFiles();
UsageStatsDatabase newDB = new UsageStatsDatabase(mTestDir);
+ newDB.readMappingsLocked();
newDB.init(1);
// Attempt to restore the usage stats from the backup
newDB.applyRestoredPayload(KEY_USAGE_STATS, blob);
@@ -438,6 +464,28 @@ public class UsageStatsDatabaseTest {
runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_YEARLY);
}
+ /**
+ * Test the version upgrade from 4 to 5
+ */
+ @Test
+ public void testVersionUpgradeFrom4to5() throws IOException {
+ runVersionChangeTest(4, 5, UsageStatsManager.INTERVAL_DAILY);
+ runVersionChangeTest(4, 5, UsageStatsManager.INTERVAL_WEEKLY);
+ runVersionChangeTest(4, 5, UsageStatsManager.INTERVAL_MONTHLY);
+ runVersionChangeTest(4, 5, UsageStatsManager.INTERVAL_YEARLY);
+ }
+
+ /**
+ * Test the version upgrade from 3 to 5
+ */
+ @Test
+ public void testVersionUpgradeFrom3to5() throws IOException {
+ runVersionChangeTest(3, 5, UsageStatsManager.INTERVAL_DAILY);
+ runVersionChangeTest(3, 5, UsageStatsManager.INTERVAL_WEEKLY);
+ runVersionChangeTest(3, 5, UsageStatsManager.INTERVAL_MONTHLY);
+ runVersionChangeTest(3, 5, UsageStatsManager.INTERVAL_YEARLY);
+ }
+
/**
* Test the version upgrade from 3 to 4
@@ -492,4 +540,68 @@ public class UsageStatsDatabaseTest {
assertEquals(extra, files.keyAt(0));
}
}
+
+ private void compareObfuscatedData(int interval) throws IOException {
+ // Write IntervalStats to disk
+ UsageStatsDatabase prevDB = new UsageStatsDatabase(mTestDir, 5);
+ prevDB.readMappingsLocked();
+ prevDB.init(1);
+ prevDB.putUsageStats(interval, mIntervalStats);
+ prevDB.writeMappingsLocked();
+
+ // Read IntervalStats from disk into a new db
+ UsageStatsDatabase newDB = new UsageStatsDatabase(mTestDir, 5);
+ newDB.readMappingsLocked();
+ newDB.init(mEndTime);
+ List<IntervalStats> stats = newDB.queryUsageStats(interval, 0, mEndTime,
+ mIntervalStatsVerifier);
+
+ assertEquals(1, stats.size());
+ // The written and read IntervalStats should match
+ compareIntervalStats(mIntervalStats, stats.get(0), 5);
+ }
+
+ @Test
+ public void testObfuscation() throws IOException {
+ compareObfuscatedData(UsageStatsManager.INTERVAL_DAILY);
+ compareObfuscatedData(UsageStatsManager.INTERVAL_WEEKLY);
+ compareObfuscatedData(UsageStatsManager.INTERVAL_MONTHLY);
+ compareObfuscatedData(UsageStatsManager.INTERVAL_YEARLY);
+ }
+
+ private void verifyPackageNotRetained(int interval) throws IOException {
+ UsageStatsDatabase db = new UsageStatsDatabase(mTestDir, 5);
+ db.readMappingsLocked();
+ db.init(1);
+ db.putUsageStats(interval, mIntervalStats);
+
+ final String removedPackage = "fake.package.name0";
+ // invoke handler call directly from test to remove package
+ db.onPackageRemoved(removedPackage, System.currentTimeMillis());
+
+ List<IntervalStats> stats = db.queryUsageStats(interval, 0, mEndTime,
+ mIntervalStatsVerifier);
+ for (int i = 0; i < stats.size(); i++) {
+ final IntervalStats stat = stats.get(i);
+ if (stat.packageStats.containsKey(removedPackage)) {
+ fail("Found removed package " + removedPackage + " in package stats.");
+ return;
+ }
+ for (int j = 0; j < stat.events.size(); j++) {
+ final Event event = stat.events.get(j);
+ if (removedPackage.equals(event.mPackage)) {
+ fail("Found an event from removed package " + removedPackage);
+ return;
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testPackageRetention() throws IOException {
+ verifyPackageNotRetained(UsageStatsManager.INTERVAL_DAILY);
+ verifyPackageNotRetained(UsageStatsManager.INTERVAL_WEEKLY);
+ verifyPackageNotRetained(UsageStatsManager.INTERVAL_MONTHLY);
+ verifyPackageNotRetained(UsageStatsManager.INTERVAL_YEARLY);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/utils/TraceBufferTest.java b/services/tests/servicestests/src/com/android/server/utils/TraceBufferTest.java
new file mode 100644
index 000000000000..09b75e71d946
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/utils/TraceBufferTest.java
@@ -0,0 +1,184 @@
+/*
+ * 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.utils;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+import android.util.proto.ProtoOutputStream;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.Preconditions;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+
+
+/**
+ * Test class for {@link TraceBuffer}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:TraceBufferTest
+ */
+@SmallTest
+@Presubmit
+public class TraceBufferTest {
+ private File mFile;
+ private TraceBuffer mBuffer;
+
+ @Before
+ public void setUp() throws Exception {
+ final Context testContext = getInstrumentation().getContext();
+ mFile = testContext.getFileStreamPath("tracing_test.dat");
+ mFile.delete();
+
+ mBuffer = new TraceBuffer(10);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mFile.delete();
+ }
+
+ @Test
+ public void test_addItem() {
+ ProtoOutputStream toWrite = getDummy(1);
+ final int objectSize = toWrite.getRawSize();
+ mBuffer.setCapacity(objectSize);
+ mBuffer.resetBuffer();
+
+ Preconditions.checkArgument(mBuffer.size() == 0);
+
+ mBuffer.add(toWrite);
+
+ assertEquals("Item was not added to the buffer", 1, mBuffer.size());
+ assertEquals("Total buffer getSize differs from inserted object",
+ mBuffer.getBufferSize(), objectSize);
+ assertEquals("Available buffer space does not match used one", 0,
+ mBuffer.getAvailableSpace());
+ }
+
+ @Test
+ public void test_addItemMustOverwriteOne() {
+ ProtoOutputStream toWrite1 = getDummy(1);
+ ProtoOutputStream toWrite2 = getDummy(2);
+ ProtoOutputStream toWrite3 = getDummy(3);
+ final int objectSize = toWrite1.getRawSize();
+ final int bufferCapacity = objectSize * 2 + 1;
+ mBuffer.setCapacity(bufferCapacity);
+ mBuffer.resetBuffer();
+
+ mBuffer.add(toWrite1);
+ byte[] toWrite1Bytes = toWrite1.getBytes();
+ assertTrue("First element should be in the list",
+ mBuffer.contains(toWrite1Bytes));
+
+ mBuffer.add(toWrite2);
+ byte[] toWrite2Bytes = toWrite2.getBytes();
+ assertTrue("First element should be in the list",
+ mBuffer.contains(toWrite1Bytes));
+ assertTrue("Second element should be in the list",
+ mBuffer.contains(toWrite2Bytes));
+
+ mBuffer.add(toWrite3);
+ byte[] toWrite3Bytes = toWrite3.getBytes();
+ assertTrue("First element should not be in the list",
+ !mBuffer.contains(toWrite1Bytes));
+ assertTrue("Second element should be in the list",
+ mBuffer.contains(toWrite2Bytes));
+ assertTrue("Third element should be in the list",
+ mBuffer.contains(toWrite3Bytes));
+ assertEquals("Buffer should have 2 elements", 2, mBuffer.size());
+ assertEquals(String.format("Buffer is full, used space should be %d", bufferCapacity),
+ mBuffer.getBufferSize(), bufferCapacity - 1);
+ assertEquals(" Buffer is full, available space should be 0", 1,
+ mBuffer.getAvailableSpace());
+ }
+
+ @Test
+ public void test_addItemMustOverwriteMultiple() {
+ ProtoOutputStream toWriteSmall1 = getDummy(1);
+ ProtoOutputStream toWriteSmall2 = getDummy(2);
+ final int objectSize = toWriteSmall1.getRawSize();
+ final int bufferCapacity = objectSize * 2;
+ mBuffer.setCapacity(bufferCapacity);
+ mBuffer.resetBuffer();
+
+ ProtoOutputStream toWriteBig = new ProtoOutputStream();
+ toWriteBig.write(MAGIC_NUMBER, 1);
+ toWriteBig.write(MAGIC_NUMBER, 2);
+
+ mBuffer.add(toWriteSmall1);
+ byte[] toWriteSmall1Bytes = toWriteSmall1.getBytes();
+ assertTrue("First element should be in the list",
+ mBuffer.contains(toWriteSmall1Bytes));
+
+ mBuffer.add(toWriteSmall2);
+ byte[] toWriteSmall2Bytes = toWriteSmall2.getBytes();
+ assertTrue("First element should be in the list",
+ mBuffer.contains(toWriteSmall1Bytes));
+ assertTrue("Second element should be in the list",
+ mBuffer.contains(toWriteSmall2Bytes));
+
+ mBuffer.add(toWriteBig);
+ byte[] toWriteBigBytes = toWriteBig.getBytes();
+ assertTrue("Third element should overwrite all others",
+ !mBuffer.contains(toWriteSmall1Bytes));
+ assertTrue("Third element should overwrite all others",
+ !mBuffer.contains(toWriteSmall2Bytes));
+ assertTrue("Third element should overwrite all others",
+ mBuffer.contains(toWriteBigBytes));
+
+ assertEquals(" Buffer should have only 1 big element", 1, mBuffer.size());
+ assertEquals(String.format(" Buffer is full, used space should be %d", bufferCapacity),
+ mBuffer.getBufferSize(), bufferCapacity);
+ assertEquals(" Buffer is full, available space should be 0", 0,
+ mBuffer.getAvailableSpace());
+ }
+
+ @Test
+ public void test_startResetsBuffer() {
+ ProtoOutputStream toWrite = getDummy(1);
+ mBuffer.resetBuffer();
+ Preconditions.checkArgument(mBuffer.size() == 0);
+
+ mBuffer.add(toWrite);
+ assertEquals("Item was not added to the buffer", 1, mBuffer.size());
+ mBuffer.resetBuffer();
+ assertEquals("Buffer should be empty after reset", 0, mBuffer.size());
+ assertEquals("Buffer size should be 0 after reset", 0, mBuffer.getBufferSize());
+ }
+
+ private ProtoOutputStream getDummy(int value) {
+ ProtoOutputStream toWrite = new ProtoOutputStream();
+ toWrite.write(MAGIC_NUMBER, value);
+ toWrite.flush();
+
+ return toWrite;
+ }
+
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 8c3373faa0d4..1e55b1521956 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -22,6 +22,8 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
+import static android.media.AudioAttributes.USAGE_NOTIFICATION;
+import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
@@ -105,6 +107,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
private int mUid = 1000;
private int mPid = 2000;
private android.os.UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
+ private NotificationChannel mChannel;
private VibrateRepeatMatcher mVibrateOnceMatcher = new VibrateRepeatMatcher(-1);
private VibrateRepeatMatcher mVibrateLoopMatcher = new VibrateRepeatMatcher(0);
@@ -158,6 +161,8 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
mService.mScreenOn = false;
mService.mInCallStateOffHook = false;
mService.mNotificationPulseEnabled = true;
+
+ mChannel = new NotificationChannel("test", "test", IMPORTANCE_HIGH);
}
//
@@ -174,13 +179,18 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
true /* noisy */, false /* buzzy*/, false /* lights */);
}
+ private NotificationRecord getBeepyOtherNotification() {
+ return getNotificationRecord(mOtherId, false /* insistent */, false /* once */,
+ true /* noisy */, false /* buzzy*/, false /* lights */);
+ }
+
private NotificationRecord getBeepyOnceNotification() {
return getNotificationRecord(mId, false /* insistent */, true /* once */,
true /* noisy */, false /* buzzy*/, false /* lights */);
}
private NotificationRecord getQuietNotification() {
- return getNotificationRecord(mId, false /* insistent */, false /* once */,
+ return getNotificationRecord(mId, false /* insistent */, true /* once */,
false /* noisy */, false /* buzzy*/, false /* lights */);
}
@@ -214,6 +224,11 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
false /* noisy */, true /* buzzy*/, false /* lights */);
}
+ private NotificationRecord getBuzzyOtherNotification() {
+ return getNotificationRecord(mOtherId, false /* insistent */, false /* once */,
+ false /* noisy */, true /* buzzy*/, false /* lights */);
+ }
+
private NotificationRecord getBuzzyOnceNotification() {
return getNotificationRecord(mId, false /* insistent */, true /* once */,
false /* noisy */, true /* buzzy*/, false /* lights */);
@@ -239,22 +254,34 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
false /* noisy */, false /* buzzy*/, true /* lights */);
}
- private NotificationRecord getCallRecord(int id, boolean insistent) {
- return getNotificationRecord(id, false, false /* once */, true /* noisy */,
- false /* buzzy */, false /* lights */, false /* default vib */,
- false /* default sound */, false /* default lights */, "",
- Notification.GROUP_ALERT_ALL, false);
+ private NotificationRecord getCallRecord(int id, NotificationChannel channel, boolean looping) {
+ final Builder builder = new Builder(getContext())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setPriority(Notification.PRIORITY_HIGH);
+ Notification n = builder.build();
+ if (looping) {
+ n.flags |= Notification.FLAG_INSISTENT;
+ }
+ StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid,
+ mPid, n, mUser, null, System.currentTimeMillis());
+ NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+ mService.addNotification(r);
+
+ return r;
}
private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
boolean noisy, boolean buzzy, boolean lights) {
- return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true, true,
- null, Notification.GROUP_ALERT_ALL, false);
+ return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, buzzy, noisy,
+ lights, null, Notification.GROUP_ALERT_ALL, false);
}
- private NotificationRecord getLeanbackNotificationRecord(int id, boolean insistent, boolean once,
+ private NotificationRecord getLeanbackNotificationRecord(int id, boolean insistent,
+ boolean once,
boolean noisy, boolean buzzy, boolean lights) {
- return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true, true,
+ return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true,
+ true,
null, Notification.GROUP_ALERT_ALL, true);
}
@@ -265,16 +292,16 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
private NotificationRecord getLightsNotificationRecord(String groupKey,
int groupAlertBehavior) {
- return getNotificationRecord(mId, false, false, false, false, true /*lights*/, true, true,
- true, groupKey, groupAlertBehavior, false);
+ return getNotificationRecord(mId, false, false, false, false, true /*lights*/, true,
+ true, true, groupKey, groupAlertBehavior, false);
}
- private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
+ private NotificationRecord getNotificationRecord(int id,
+ boolean insistent, boolean once,
boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration,
boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior,
boolean isLeanback) {
- NotificationChannel channel =
- new NotificationChannel("test", "test", IMPORTANCE_HIGH);
+
final Builder builder = new Builder(getContext())
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -285,31 +312,37 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
if (noisy) {
if (defaultSound) {
defaults |= Notification.DEFAULT_SOUND;
- channel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
+ mChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
Notification.AUDIO_ATTRIBUTES_DEFAULT);
} else {
builder.setSound(CUSTOM_SOUND);
- channel.setSound(CUSTOM_SOUND, CUSTOM_ATTRIBUTES);
+ mChannel.setSound(CUSTOM_SOUND, CUSTOM_ATTRIBUTES);
}
} else {
- channel.setSound(null, null);
+ mChannel.setSound(null, null);
}
if (buzzy) {
if (defaultVibration) {
defaults |= Notification.DEFAULT_VIBRATE;
} else {
builder.setVibrate(CUSTOM_VIBRATION);
- channel.setVibrationPattern(CUSTOM_VIBRATION);
+ mChannel.setVibrationPattern(CUSTOM_VIBRATION);
}
- channel.enableVibration(true);
+ mChannel.enableVibration(true);
+ } else {
+ mChannel.setVibrationPattern(null);
+ mChannel.enableVibration(false);
}
+
if (lights) {
if (defaultLights) {
defaults |= Notification.DEFAULT_LIGHTS;
} else {
builder.setLights(CUSTOM_LIGHT_COLOR, CUSTOM_LIGHT_ON, CUSTOM_LIGHT_OFF);
}
- channel.enableLights(true);
+ mChannel.enableLights(true);
+ } else {
+ mChannel.enableLights(false);
}
builder.setDefaults(defaults);
@@ -329,7 +362,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid,
mPid, n, mUser, null, System.currentTimeMillis());
- NotificationRecord r = new NotificationRecord(context, sbn, channel);
+ NotificationRecord r = new NotificationRecord(context, sbn, mChannel);
mService.addNotification(r);
return r;
}
@@ -339,18 +372,19 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
//
private void verifyNeverBeep() throws RemoteException {
- verify(mRingtonePlayer, never()).playAsync((Uri) anyObject(), (UserHandle) anyObject(),
- anyBoolean(), (AudioAttributes) anyObject());
+ verify(mRingtonePlayer, never()).playAsync(any(), any(), anyBoolean(), any());
}
- private void verifyBeep() throws RemoteException {
- verify(mRingtonePlayer, times(1)).playAsync((Uri) anyObject(), (UserHandle) anyObject(),
- eq(true), (AudioAttributes) anyObject());
+ private void verifyBeepUnlooped() throws RemoteException {
+ verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(false), any());
}
- private void verifyBeepLooped() throws RemoteException {
- verify(mRingtonePlayer, times(1)).playAsync((Uri) anyObject(), (UserHandle) anyObject(),
- eq(false), (AudioAttributes) anyObject());
+ private void verifyBeepLooped() throws RemoteException {
+ verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(true), any());
+ }
+
+ private void verifyBeep(int times) throws RemoteException {
+ verify(mRingtonePlayer, times(times)).playAsync(any(), any(), anyBoolean(), any());
}
private void verifyNeverStopAudio() throws RemoteException {
@@ -362,24 +396,31 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
}
private void verifyNeverVibrate() {
- verify(mVibrator, never()).vibrate(anyInt(), anyString(), (VibrationEffect) anyObject(),
- anyString(), (AudioAttributes) anyObject());
+ verify(mVibrator, never()).vibrate(anyInt(), anyString(), any(), anyString(), any());
}
private void verifyVibrate() {
verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateOnceMatcher),
- anyString(), (AudioAttributes) anyObject());
+ anyString(), any());
+ }
+
+ private void verifyVibrate(int times) {
+ verify(mVibrator, times(times)).vibrate(anyInt(), anyString(), any(), anyString(), any());
}
private void verifyVibrateLooped() {
verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateLoopMatcher),
- anyString(), (AudioAttributes) anyObject());
+ anyString(), any());
}
private void verifyDelayedVibrateLooped() {
verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(),
- argThat(mVibrateLoopMatcher), anyString(),
- (AudioAttributes) anyObject());
+ argThat(mVibrateLoopMatcher), anyString(), any());
+ }
+
+ private void verifyDelayedVibrate() {
+ verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(),
+ argThat(mVibrateOnceMatcher), anyString(), any());
}
private void verifyStopVibrate() {
@@ -398,11 +439,6 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
verify(mLight, times(1)).setFlashing(anyInt(), anyInt(), anyInt(), anyInt());
}
- private void verifyCustomLights() {
- verify(mLight, times(1)).setFlashing(
- eq(CUSTOM_LIGHT_COLOR), anyInt(), eq(CUSTOM_LIGHT_ON), eq(CUSTOM_LIGHT_OFF));
- }
-
//
// Tests
//
@@ -425,7 +461,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
mService.buzzBeepBlinkLocked(r);
- verifyBeepLooped();
+ verifyBeepUnlooped();
verifyNeverVibrate();
verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
assertTrue(r.isInterruptive());
@@ -438,7 +474,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
mService.buzzBeepBlinkLocked(r);
- verifyBeep();
+ verifyBeepLooped();
assertTrue(r.isInterruptive());
assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
@@ -492,7 +528,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
mService.buzzBeepBlinkLocked(r);
- verifyBeepLooped();
+ verifyBeepUnlooped();
assertTrue(r.isInterruptive());
}
@@ -533,7 +569,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
// update should beep
r.isUpdate = true;
mService.buzzBeepBlinkLocked(r);
- verifyBeepLooped();
+ verifyBeepUnlooped();
verify(mAccessibilityService, times(2)).sendAccessibilityEvent(any(), anyInt());
assertTrue(r.isInterruptive());
assertNotEquals(-1, r.getLastAudiblyAlertedMs());
@@ -723,7 +759,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
mService.buzzBeepBlinkLocked(r);
verifyNeverVibrate();
- verifyBeepLooped();
+ verifyBeepUnlooped();
assertTrue(r.isInterruptive());
assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
@@ -821,7 +857,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
mService.buzzBeepBlinkLocked(summary);
- verifyBeepLooped();
+ verifyBeepUnlooped();
// summaries are never interruptive for notification counts
assertFalse(summary.isInterruptive());
assertNotEquals(-1, summary.getLastAudiblyAlertedMs());
@@ -833,7 +869,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
mService.buzzBeepBlinkLocked(nonGroup);
- verifyBeepLooped();
+ verifyBeepUnlooped();
assertTrue(nonGroup.isInterruptive());
assertNotEquals(-1, nonGroup.getLastAudiblyAlertedMs());
}
@@ -856,7 +892,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
mService.buzzBeepBlinkLocked(child);
- verifyBeepLooped();
+ verifyBeepUnlooped();
assertTrue(child.isInterruptive());
assertNotEquals(-1, child.getLastAudiblyAlertedMs());
}
@@ -867,7 +903,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
mService.buzzBeepBlinkLocked(nonGroup);
- verifyBeepLooped();
+ verifyBeepUnlooped();
assertTrue(nonGroup.isInterruptive());
assertNotEquals(-1, nonGroup.getLastAudiblyAlertedMs());
}
@@ -878,7 +914,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
mService.buzzBeepBlinkLocked(group);
- verifyBeepLooped();
+ verifyBeepUnlooped();
assertTrue(group.isInterruptive());
assertNotEquals(-1, group.getLastAudiblyAlertedMs());
}
@@ -1293,7 +1329,11 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
@Test
public void testListenerHintCall() throws Exception {
- NotificationRecord r = getCallRecord(1, true);
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ NotificationRecord r = getCallRecord(1, ringtoneChannel, true);
mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS);
@@ -1310,7 +1350,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
mService.buzzBeepBlinkLocked(r);
- verifyBeepLooped();
+ verifyBeepUnlooped();
}
@Test
@@ -1326,7 +1366,11 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
@Test
public void testListenerHintBoth() throws Exception {
- NotificationRecord r = getCallRecord(1, true);
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ NotificationRecord r = getCallRecord(1, ringtoneChannel, true);
NotificationRecord s = getBeepyNotification();
mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
@@ -1340,7 +1384,11 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
@Test
public void testListenerHintNotification_callSound() throws Exception {
- NotificationRecord r = getCallRecord(1, true);
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ NotificationRecord r = getCallRecord(1, ringtoneChannel, true);
mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
@@ -1349,6 +1397,169 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase {
verifyBeepLooped();
}
+ @Test
+ public void testCannotInterruptRingtoneInsistentBeep() throws Exception {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+ mService.addNotification(ringtoneNotification);
+
+ mService.buzzBeepBlinkLocked(ringtoneNotification);
+ verifyBeepLooped();
+
+ NotificationRecord interrupter = getBeepyOtherNotification();
+ assertTrue(mService.shouldMuteNotificationLocked(interrupter));
+ mService.buzzBeepBlinkLocked(interrupter);
+
+ verifyBeep(1);
+
+ assertFalse(interrupter.isInterruptive());
+ assertEquals(-1, interrupter.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testCannotInterruptRingtoneInsistentBuzz() {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Uri.EMPTY,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ ringtoneChannel.enableVibration(true);
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+ assertFalse(mService.shouldMuteNotificationLocked(ringtoneNotification));
+
+ mService.buzzBeepBlinkLocked(ringtoneNotification);
+ verifyVibrateLooped();
+
+ NotificationRecord interrupter = getBuzzyOtherNotification();
+ assertTrue(mService.shouldMuteNotificationLocked(interrupter));
+ mService.buzzBeepBlinkLocked(interrupter);
+
+ verifyVibrate(1);
+
+ assertFalse(interrupter.isInterruptive());
+ assertEquals(-1, interrupter.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testCanInterruptRingtoneNonInsistentBeep() throws Exception {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, false);
+
+ mService.buzzBeepBlinkLocked(ringtoneNotification);
+ verifyBeepUnlooped();
+
+ NotificationRecord interrupter = getBeepyOtherNotification();
+ mService.buzzBeepBlinkLocked(interrupter);
+
+ verifyBeep(2);
+
+ assertTrue(interrupter.isInterruptive());
+ }
+
+ @Test
+ public void testCanInterruptRingtoneNonInsistentBuzz() {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(null,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ ringtoneChannel.enableVibration(true);
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, false);
+
+ mService.buzzBeepBlinkLocked(ringtoneNotification);
+ verifyVibrate();
+
+ NotificationRecord interrupter = getBuzzyOtherNotification();
+ mService.buzzBeepBlinkLocked(interrupter);
+
+ verifyVibrate(2);
+
+ assertTrue(interrupter.isInterruptive());
+ }
+
+ @Test
+ public void testRingtoneInsistentBeep_doesNotBlockFutureSoundsOnceStopped() throws Exception {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+
+ mService.buzzBeepBlinkLocked(ringtoneNotification);
+ verifyBeepLooped();
+
+ mService.clearSoundLocked();
+
+ NotificationRecord interrupter = getBeepyOtherNotification();
+ mService.buzzBeepBlinkLocked(interrupter);
+
+ verifyBeep(2);
+
+ assertTrue(interrupter.isInterruptive());
+ }
+
+ @Test
+ public void testRingtoneInsistentBuzz_doesNotBlockFutureSoundsOnceStopped() {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(null,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ ringtoneChannel.enableVibration(true);
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+
+ mService.buzzBeepBlinkLocked(ringtoneNotification);
+ verifyVibrateLooped();
+
+ mService.clearVibrateLocked();
+
+ NotificationRecord interrupter = getBuzzyOtherNotification();
+ mService.buzzBeepBlinkLocked(interrupter);
+
+ verifyVibrate(2);
+
+ assertTrue(interrupter.isInterruptive());
+ }
+
+ @Test
+ public void testCanInterruptNonRingtoneInsistentBeep() throws Exception {
+ NotificationChannel fakeRingtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ NotificationRecord ringtoneNotification = getCallRecord(1, fakeRingtoneChannel, true);
+
+ mService.buzzBeepBlinkLocked(ringtoneNotification);
+ verifyBeepLooped();
+
+ NotificationRecord interrupter = getBeepyOtherNotification();
+ mService.buzzBeepBlinkLocked(interrupter);
+
+ verifyBeep(2);
+
+ assertTrue(interrupter.isInterruptive());
+ }
+
+ @Test
+ public void testCanInterruptNonRingtoneInsistentBuzz() {
+ NotificationChannel fakeRingtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ fakeRingtoneChannel.enableVibration(true);
+ fakeRingtoneChannel.setSound(null,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION).build());
+ NotificationRecord ringtoneNotification = getCallRecord(1, fakeRingtoneChannel, true);
+
+ mService.buzzBeepBlinkLocked(ringtoneNotification);
+
+ NotificationRecord interrupter = getBuzzyOtherNotification();
+ mService.buzzBeepBlinkLocked(interrupter);
+
+ verifyVibrate(2);
+
+ assertTrue(interrupter.isInterruptive());
+ }
+
static class VibrateRepeatMatcher implements ArgumentMatcher<VibrationEffect> {
private final int mRepeatIndex;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index f652c5afd203..721641a7a8c8 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -15,6 +15,8 @@
*/
package com.android.server.notification;
+import static com.android.server.notification.GroupHelper.AUTOGROUP_KEY;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
@@ -155,6 +157,196 @@ public class GroupHelperTest extends UiServiceTestCase {
}
@Test
+ public void testAutoGroupCount_addingNoGroupSBN() {
+ final String pkg = "package";
+ ArrayList<StatusBarNotification> notifications = new ArrayList<>();
+ for (int i = 0; i < AUTOGROUP_AT_COUNT + 1; i++) {
+ notifications.add(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM));
+ }
+
+ for (StatusBarNotification sbn: notifications) {
+ sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ sbn.setOverrideGroupKey(AUTOGROUP_KEY);
+ }
+
+ for (StatusBarNotification sbn: notifications) {
+ mGroupHelper.onNotificationPosted(sbn, true);
+ }
+
+ verify(mCallback, times(AUTOGROUP_AT_COUNT + 1))
+ .updateAutogroupSummary(anyString(), eq(true));
+
+ int userId = UserHandle.SYSTEM.getIdentifier();
+ assertEquals(mGroupHelper.getOngoingGroupCount(
+ userId, pkg, AUTOGROUP_KEY), AUTOGROUP_AT_COUNT + 1);
+ }
+
+ @Test
+ public void testAutoGroupCount_UpdateNotification() {
+ final String pkg = "package";
+ ArrayList<StatusBarNotification> notifications = new ArrayList<>();
+ for (int i = 0; i < AUTOGROUP_AT_COUNT + 1; i++) {
+ notifications.add(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM));
+ }
+
+ for (StatusBarNotification sbn: notifications) {
+ sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ sbn.setOverrideGroupKey(AUTOGROUP_KEY);
+ }
+
+ for (StatusBarNotification sbn: notifications) {
+ mGroupHelper.onNotificationPosted(sbn, true);
+ }
+
+ notifications.get(0).getNotification().flags &= ~Notification.FLAG_ONGOING_EVENT;
+
+ mGroupHelper.onNotificationUpdated(notifications.get(0), true);
+
+ verify(mCallback, times(AUTOGROUP_AT_COUNT + 2))
+ .updateAutogroupSummary(anyString(), eq(true));
+
+ int userId = UserHandle.SYSTEM.getIdentifier();
+ assertEquals(mGroupHelper.getOngoingGroupCount(
+ userId, pkg, AUTOGROUP_KEY), AUTOGROUP_AT_COUNT);
+ }
+
+ @Test
+ public void testAutoGroupCount_UpdateNotificationAfterChanges() {
+ final String pkg = "package";
+ ArrayList<StatusBarNotification> notifications = new ArrayList<>();
+ for (int i = 0; i < AUTOGROUP_AT_COUNT + 1; i++) {
+ notifications.add(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM));
+ }
+
+ for (StatusBarNotification sbn: notifications) {
+ sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ sbn.setOverrideGroupKey(AUTOGROUP_KEY);
+ }
+
+ for (StatusBarNotification sbn: notifications) {
+ mGroupHelper.onNotificationPosted(sbn, true);
+ }
+
+ notifications.get(0).getNotification().flags &= ~Notification.FLAG_ONGOING_EVENT;
+
+ mGroupHelper.onNotificationUpdated(notifications.get(0), true);
+
+ notifications.get(0).getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+
+ mGroupHelper.onNotificationUpdated(notifications.get(0), true);
+
+ verify(mCallback, times(AUTOGROUP_AT_COUNT + 3))
+ .updateAutogroupSummary(anyString(), eq(true));
+
+ int userId = UserHandle.SYSTEM.getIdentifier();
+ assertEquals(mGroupHelper.getOngoingGroupCount(
+ userId, pkg, AUTOGROUP_KEY), AUTOGROUP_AT_COUNT + 1);
+ }
+
+ @Test
+ public void testAutoGroupCount_RemoveNotification() {
+ final String pkg = "package";
+ ArrayList<StatusBarNotification> notifications = new ArrayList<>();
+ for (int i = 0; i < AUTOGROUP_AT_COUNT + 1; i++) {
+ notifications.add(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM));
+ }
+
+ for (StatusBarNotification sbn: notifications) {
+ sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ sbn.setOverrideGroupKey(AUTOGROUP_KEY);
+ }
+
+ for (StatusBarNotification sbn: notifications) {
+ mGroupHelper.onNotificationPosted(sbn, true);
+ }
+
+ mGroupHelper.onNotificationRemoved(notifications.get(0));
+
+ verify(mCallback, times(AUTOGROUP_AT_COUNT + 2))
+ .updateAutogroupSummary(anyString(), eq(true));
+
+ int userId = UserHandle.SYSTEM.getIdentifier();
+ assertEquals(mGroupHelper.getOngoingGroupCount(
+ userId, pkg, AUTOGROUP_KEY), AUTOGROUP_AT_COUNT);
+ }
+
+
+ @Test
+ public void testAutoGroupCount_UpdateToNoneOngoingNotification() {
+ final String pkg = "package";
+ ArrayList<StatusBarNotification> notifications = new ArrayList<>();
+ for (int i = 0; i < AUTOGROUP_AT_COUNT + 1; i++) {
+ notifications.add(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM));
+ }
+
+ for (StatusBarNotification sbn: notifications) {
+ sbn.setOverrideGroupKey(AUTOGROUP_KEY);
+ }
+
+ for (StatusBarNotification sbn: notifications) {
+ mGroupHelper.onNotificationPosted(sbn, true);
+ }
+
+ notifications.get(0).getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ mGroupHelper.onNotificationUpdated(notifications.get(0), true);
+
+ verify(mCallback, times(1))
+ .updateAutogroupSummary(anyString(), eq(true));
+
+ int userId = UserHandle.SYSTEM.getIdentifier();
+ assertEquals(mGroupHelper.getOngoingGroupCount(
+ userId, pkg, AUTOGROUP_KEY), 1);
+ }
+
+ @Test
+ public void testAutoGroupCount_AddOneOngoingNotification() {
+ final String pkg = "package";
+ ArrayList<StatusBarNotification> notifications = new ArrayList<>();
+ for (int i = 0; i < AUTOGROUP_AT_COUNT + 1; i++) {
+ notifications.add(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM));
+ }
+ StatusBarNotification sbn = notifications.get(0);
+ sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ sbn.setOverrideGroupKey(AUTOGROUP_KEY);
+
+
+ for (StatusBarNotification current: notifications) {
+ mGroupHelper.onNotificationPosted(current, true);
+ }
+
+ verify(mCallback, times(1))
+ .updateAutogroupSummary(anyString(), eq(true));
+
+ int userId = UserHandle.SYSTEM.getIdentifier();
+ assertEquals(mGroupHelper.getOngoingGroupCount(
+ userId, pkg, AUTOGROUP_KEY), 1);
+ }
+
+ @Test
+ public void testAutoGroupCount_UpdateNoneOngoing() {
+ final String pkg = "package";
+ ArrayList<StatusBarNotification> notifications = new ArrayList<>();
+ for (int i = 0; i < AUTOGROUP_AT_COUNT + 1; i++) {
+ notifications.add(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM));
+ }
+
+ for (StatusBarNotification sbn: notifications) {
+ sbn.setOverrideGroupKey(AUTOGROUP_KEY);
+ }
+
+ for (StatusBarNotification sbn: notifications) {
+ mGroupHelper.onNotificationPosted(sbn, true);
+ }
+
+ verify(mCallback, times(0))
+ .updateAutogroupSummary(anyString(), eq(true));
+
+ int userId = UserHandle.SYSTEM.getIdentifier();
+ assertEquals(mGroupHelper.getOngoingGroupCount(userId, pkg, AUTOGROUP_KEY), 0);
+ }
+
+
+ @Test
public void testDropToZeroRemoveGroup() throws Exception {
final String pkg = "package";
List<StatusBarNotification> posted = new ArrayList<>();
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 2de8d0579ec4..cd0f4f185927 100644..100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -44,6 +44,7 @@ import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Build.VERSION_CODES.O_MR1;
import static android.os.Build.VERSION_CODES.P;
+import static android.os.UserHandle.USER_SYSTEM;
import static android.service.notification.Adjustment.KEY_IMPORTANCE;
import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
@@ -129,6 +130,7 @@ import android.testing.TestableContext;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.testing.TestablePermissions;
+import android.testing.TestableResources;
import android.text.Html;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -535,6 +537,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
return new NotificationRecord(mContext, sbn, channel);
}
+ private NotificationRecord generateNotificationRecord(NotificationChannel channel, int userId) {
+ if (channel == null) {
+ channel = mTestNotificationChannel;
+ }
+ Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, "tag", mUid, 0,
+ nb.build(), new UserHandle(userId), null, 0);
+ return new NotificationRecord(mContext, sbn, channel);
+ }
+
private Map<String, Answer> getSignalExtractorSideEffects() {
Map<String, Answer> answers = new ArrayMap<>();
@@ -1180,6 +1194,36 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testAutobundledSummary_notificationAdded() {
+ NotificationRecord summary =
+ generateNotificationRecord(mTestNotificationChannel, 0, "pkg", true);
+ summary.getNotification().flags |= Notification.FLAG_AUTOGROUP_SUMMARY;
+ mService.addNotification(summary);
+ mService.mSummaryByGroupKey.put("pkg", summary);
+ mService.mAutobundledSummaries.put(0, new ArrayMap<>());
+ mService.mAutobundledSummaries.get(0).put("pkg", summary.getKey());
+ mService.updateAutobundledSummaryFlags(0, "pkg", true);
+
+ assertTrue(summary.sbn.isOngoing());
+ }
+
+ @Test
+ public void testAutobundledSummary_notificationRemoved() {
+ NotificationRecord summary =
+ generateNotificationRecord(mTestNotificationChannel, 0, "pkg", true);
+ summary.getNotification().flags |= Notification.FLAG_AUTOGROUP_SUMMARY;
+ summary.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ mService.addNotification(summary);
+ mService.mAutobundledSummaries.put(0, new ArrayMap<>());
+ mService.mAutobundledSummaries.get(0).put("pkg", summary.getKey());
+ mService.mSummaryByGroupKey.put("pkg", summary);
+
+ mService.updateAutobundledSummaryFlags(0, "pkg", false);
+
+ assertFalse(summary.sbn.isOngoing());
+ }
+
+ @Test
public void testCancelAllNotifications_IgnoreForegroundService() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
@@ -3278,7 +3322,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testRestore() throws Exception {
int systemChecks = mService.countSystemChecks;
- mBinderService.applyRestore(null, UserHandle.USER_SYSTEM);
+ mBinderService.applyRestore(null, USER_SYSTEM);
assertEquals(1, mService.countSystemChecks - systemChecks);
}
@@ -3347,7 +3391,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
reset(mUgmInternal);
when(mUgmInternal.newUriPermissionOwner(any())).thenReturn(new Binder());
mService.updateUriPermissions(recordA, null, mContext.getPackageName(),
- UserHandle.USER_SYSTEM);
+ USER_SYSTEM);
verify(mUgm, times(1)).grantUriPermissionFromOwner(any(), anyInt(), any(),
eq(message1.getDataUri()), anyInt(), anyInt(), anyInt());
verify(mUgm, times(1)).grantUriPermissionFromOwner(any(), anyInt(), any(),
@@ -3363,21 +3407,21 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// Update means we drop access to first
reset(mUgmInternal);
mService.updateUriPermissions(recordB, recordA, mContext.getPackageName(),
- UserHandle.USER_SYSTEM);
+ USER_SYSTEM);
verify(mUgmInternal, times(1)).revokeUriPermissionFromOwner(any(),
eq(message1.getDataUri()), anyInt(), anyInt());
// Update back means we grant access to first again
reset(mUgm);
mService.updateUriPermissions(recordA, recordB, mContext.getPackageName(),
- UserHandle.USER_SYSTEM);
+ USER_SYSTEM);
verify(mUgm, times(1)).grantUriPermissionFromOwner(any(), anyInt(), any(),
eq(message1.getDataUri()), anyInt(), anyInt(), anyInt());
// And update to empty means we drop everything
reset(mUgmInternal);
mService.updateUriPermissions(null, recordB, mContext.getPackageName(),
- UserHandle.USER_SYSTEM);
+ USER_SYSTEM);
verify(mUgmInternal, times(1)).revokeUriPermissionFromOwner(any(), eq(null),
anyInt(), anyInt());
}
@@ -3864,7 +3908,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.simulatePackageDistractionBroadcast(
PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"a", "b"});
ArgumentCaptor<List<NotificationRecord>> captorHide = ArgumentCaptor.forClass(List.class);
- verify(mListeners, times(2)).notifyHiddenLocked(captorHide.capture());
+
+ // should be called only once.
+ verify(mListeners, times(1)).notifyHiddenLocked(captorHide.capture());
assertEquals(2, captorHide.getValue().size());
assertEquals("a", captorHide.getValue().get(0).sbn.getPackageName());
assertEquals("b", captorHide.getValue().get(1).sbn.getPackageName());
@@ -3873,7 +3919,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.simulatePackageDistractionBroadcast(
PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, new String[] {"a", "b"});
ArgumentCaptor<List<NotificationRecord>> captorUnhide = ArgumentCaptor.forClass(List.class);
- verify(mListeners, times(2)).notifyUnhiddenLocked(captorUnhide.capture());
+
+ // should be called only once.
+ verify(mListeners, times(1)).notifyUnhiddenLocked(captorUnhide.capture());
assertEquals(2, captorUnhide.getValue().size());
assertEquals("a", captorUnhide.getValue().get(0).sbn.getPackageName());
assertEquals("b", captorUnhide.getValue().get(1).sbn.getPackageName());
@@ -4073,7 +4121,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void testIsCallerInstantApp_userAllNotification() throws Exception {
ApplicationInfo info = new ApplicationInfo();
info.privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT;
- when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(UserHandle.USER_SYSTEM)))
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(USER_SYSTEM)))
.thenReturn(info);
when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{"any"});
@@ -5415,6 +5463,112 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testGrantInlineReplyUriPermission_recordExists() throws Exception {
+ NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, 0);
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+ nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ waitForIdle();
+
+ // A notification exists for the given record
+ StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
+ assertEquals(1, notifsBefore.length);
+
+ reset(mPackageManager);
+
+ Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1);
+
+ mService.mNotificationDelegate.grantInlineReplyUriPermission(
+ nr.getKey(), uri, nr.sbn.getUid());
+
+ // Grant permission called for the UID of SystemUI under the target user ID
+ verify(mUgm, times(1)).grantUriPermissionFromOwner(any(),
+ eq(nr.sbn.getUid()), eq(nr.sbn.getPackageName()), eq(uri), anyInt(), anyInt(),
+ eq(nr.sbn.getUserId()));
+ }
+
+ @Test
+ public void testGrantInlineReplyUriPermission_userAll() throws Exception {
+ // generate a NotificationRecord for USER_ALL to make sure it's converted into USER_SYSTEM
+ NotificationRecord nr =
+ generateNotificationRecord(mTestNotificationChannel, UserHandle.USER_ALL);
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+ nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ waitForIdle();
+
+ // A notification exists for the given record
+ StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
+ assertEquals(1, notifsBefore.length);
+
+ reset(mPackageManager);
+
+ Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1);
+
+ mService.mNotificationDelegate.grantInlineReplyUriPermission(
+ nr.getKey(), uri, nr.sbn.getUid());
+
+ // Target user for the grant is USER_ALL instead of USER_SYSTEM
+ verify(mUgm, times(1)).grantUriPermissionFromOwner(any(),
+ eq(nr.sbn.getUid()), eq(nr.sbn.getPackageName()), eq(uri), anyInt(), anyInt(),
+ eq(UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void testGrantInlineReplyUriPermission_acrossUsers() throws Exception {
+ // generate a NotificationRecord for USER_ALL to make sure it's converted into USER_SYSTEM
+ int otherUserId = 11;
+ NotificationRecord nr =
+ generateNotificationRecord(mTestNotificationChannel, otherUserId);
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+ nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ waitForIdle();
+
+ // A notification exists for the given record
+ StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
+ assertEquals(1, notifsBefore.length);
+
+ reset(mPackageManager);
+
+ Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1);
+
+ int uid = 0; // sysui on primary user
+ int otherUserUid = (otherUserId * 100000) + 1; // SystemUI as a different user
+ String sysuiPackage = "sysui";
+ final String[] sysuiPackages = new String[] { sysuiPackage };
+ when(mPackageManager.getPackagesForUid(uid)).thenReturn(sysuiPackages);
+
+ // Make sure to mock call for USER_SYSTEM and not USER_ALL, since it's been replaced by the
+ // time this is called
+ when(mPackageManager.getPackageUid(sysuiPackage, 0, otherUserId))
+ .thenReturn(otherUserUid);
+
+ mService.mNotificationDelegate.grantInlineReplyUriPermission(nr.getKey(), uri, uid);
+
+ // Target user for the grant is USER_ALL instead of USER_SYSTEM
+ verify(mUgm, times(1)).grantUriPermissionFromOwner(any(),
+ eq(otherUserUid), eq(nr.sbn.getPackageName()), eq(uri), anyInt(), anyInt(),
+ eq(otherUserId));
+ }
+
+ @Test
+ public void testGrantInlineReplyUriPermission_noRecordExists() throws Exception {
+ NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
+ waitForIdle();
+
+ // No notifications exist for the given record
+ StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
+ assertEquals(0, notifsBefore.length);
+
+ Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1);
+ int uid = 0; // sysui on primary user
+
+ mService.mNotificationDelegate.grantInlineReplyUriPermission(nr.getKey(), uri, uid);
+
+ // Grant permission not called if no record exists for the given key
+ verify(mUgm, times(0)).grantUriPermissionFromOwner(any(), anyInt(), any(),
+ eq(uri), anyInt(), anyInt(), anyInt());
+ }
+
+ @Test
public void testNotificationBubbles_disabled_lowRamDevice() throws Exception {
// Bubbles are allowed!
setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
@@ -5662,4 +5816,33 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
assertEquals(1, notifsAfter.length);
}
+
+ @Test
+ public void testLoadDefaultApprovedServices_emptyResources() {
+ TestableResources tr = mContext.getOrCreateTestableResources();
+ tr.addOverride(com.android.internal.R.string.config_defaultListenerAccessPackages, "");
+ tr.addOverride(com.android.internal.R.string.config_defaultDndAccessPackages, "");
+ tr.addOverride(com.android.internal.R.string.config_defaultAssistantAccessComponent, "");
+ setDefaultAssistantInDeviceConfig("");
+
+ mService.loadDefaultApprovedServices(USER_SYSTEM);
+
+ verify(mListeners, never()).addDefaultComponentOrPackage(anyString());
+ verify(mConditionProviders, never()).addDefaultComponentOrPackage(anyString());
+ verify(mAssistants, never()).addDefaultComponentOrPackage(anyString());
+ }
+
+ @Test
+ public void testLoadDefaultApprovedServices_dnd() {
+ TestableResources tr = mContext.getOrCreateTestableResources();
+ tr.addOverride(com.android.internal.R.string.config_defaultDndAccessPackages, "test");
+ when(mListeners.queryPackageForServices(anyString(), anyInt(), anyInt()))
+ .thenReturn(new ArraySet<>());
+
+ mService.loadDefaultApprovedServices(USER_SYSTEM);
+
+ verify(mConditionProviders, times(1)).addDefaultComponentOrPackage("test");
+ }
+
+ // TODO: add tests for the rest of the non-empty cases
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index dcab78ede287..3d872237d8d4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -16,9 +16,10 @@
package com.android.server.notification;
-import static junit.framework.Assert.assertEquals;
+import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertNull;
+import static junit.framework.TestCase.assertTrue;
import android.app.NotificationManager.Policy;
import android.content.ComponentName;
@@ -52,18 +53,18 @@ public class ZenModeConfigTest extends UiServiceTestCase {
@Test
public void testPriorityOnlyMutingAllNotifications() {
- ZenModeConfig config = getMutedNotificationsConfig();
- assertEquals(true, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
+ ZenModeConfig config = getMutedRingerConfig();
+ assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
config.allowReminders = true;
- assertEquals(false, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
+ assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
config.allowReminders = false;
config.areChannelsBypassingDnd = true;
- assertEquals(false, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
+ assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
config.areChannelsBypassingDnd = false;
- assertEquals(true, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
+ assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
}
@Test
@@ -106,26 +107,26 @@ public class ZenModeConfigTest extends UiServiceTestCase {
@Test
public void testPriorityOnlyMutingAll() {
ZenModeConfig config = getMutedAllConfig();
- assertEquals(true, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
- assertEquals(true, ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
+ assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
+ assertTrue(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
config.allowReminders = true;
- assertEquals(false, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
- assertEquals(false, ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
+ assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
+ assertFalse(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
config.allowReminders = false;
config.areChannelsBypassingDnd = true;
- assertEquals(false, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
- assertEquals(false, ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
+ assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
+ assertFalse(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
config.areChannelsBypassingDnd = false;
config.allowAlarms = true;
- assertEquals(true, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
- assertEquals(false, ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
+ assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
+ assertFalse(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
config.allowAlarms = false;
- assertEquals(true, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config));
- assertEquals(true, ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
+ assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
+ assertTrue(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
}
@Test
@@ -200,14 +201,14 @@ public class ZenModeConfigTest extends UiServiceTestCase {
assertEquals(rule.zenMode, fromXml.zenMode);
}
- private ZenModeConfig getMutedNotificationsConfig() {
+ private ZenModeConfig getMutedRingerConfig() {
ZenModeConfig config = new ZenModeConfig();
- // Allow alarms, media, and system
+ // Allow alarms, media
config.allowAlarms = true;
config.allowMedia = true;
- config.allowSystem = true;
- // All notification sounds are not allowed
+ // All sounds that respect the ringer are not allowed
+ config.allowSystem = false;
config.allowCalls = false;
config.allowRepeatCallers = false;
config.allowMessages = false;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 89364500fd80..99771b91ee2a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -487,7 +487,6 @@ public class ZenModeHelperTest extends UiServiceTestCase {
public void testRingerAffectedStreamsPriorityOnly() {
// in priority only mode:
// ringtone, notification and system streams are affected by ringer mode
- // UNLESS ringer is muted due to all the other priority only dnd sounds being muted
mZenModeHelperSpy.mConfig.allowAlarms = true;
mZenModeHelperSpy.mConfig.allowReminders = true;
mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
@@ -503,8 +502,9 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertTrue((ringerModeAffectedStreams & (1 << AudioSystem.STREAM_ALARM)) == 0);
assertTrue((ringerModeAffectedStreams & (1 << AudioSystem.STREAM_MUSIC)) == 0);
- // special case: if ringer is muted (since all notification sounds cannot bypass)
- // then system stream is not affected by ringer mode
+ // even when ringer is muted (since all ringer sounds cannot bypass DND),
+ // system stream is still affected by ringer mode
+ mZenModeHelperSpy.mConfig.allowSystem = false;
mZenModeHelperSpy.mConfig.allowReminders = false;
mZenModeHelperSpy.mConfig.allowCalls = false;
mZenModeHelperSpy.mConfig.allowMessages = false;
@@ -519,7 +519,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertTrue((ringerMutedRingerModeAffectedStreams & (1 << AudioSystem.STREAM_NOTIFICATION))
!= 0);
assertTrue((ringerMutedRingerModeAffectedStreams & (1 << AudioSystem.STREAM_SYSTEM))
- == 0);
+ != 0);
assertTrue((ringerMutedRingerModeAffectedStreams & (1 << AudioSystem.STREAM_ALARM)) == 0);
assertTrue((ringerMutedRingerModeAffectedStreams & (1 << AudioSystem.STREAM_MUSIC)) == 0);
}
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 6c78f6f0443f..fff32215493b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -24,11 +24,9 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations;
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.reset;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
@@ -49,6 +47,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;
@@ -62,6 +61,7 @@ import java.util.concurrent.TimeUnit;
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class ActivityDisplayTests extends ActivityTestsBase {
@Test
@@ -142,30 +142,24 @@ public class ActivityDisplayTests extends ActivityTestsBase {
// Create a display which supports system decoration and allows reparenting stacks to
// another display when the display is removed.
final ActivityDisplay display = createNewActivityDisplay();
- spyOn(display);
doReturn(false).when(display).shouldDestroyContentOnRemove();
doReturn(true).when(display).supportsSystemDecorations();
mRootActivityContainer.addChild(display, ActivityDisplay.POSITION_TOP);
// Put home stack on the display.
- final ActivityStack homeStack = display.createStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
- final TaskRecord task = new TaskBuilder(mSupervisor).setStack(homeStack).build();
- new ActivityBuilder(mService).setTask(task).build();
- display.removeChild(homeStack);
- final ActivityStack spiedHomeStack = spy(homeStack);
- display.addChild(spiedHomeStack, ActivityDisplay.POSITION_TOP);
- reset(spiedHomeStack);
+ final ActivityStack homeStack = new StackBuilder(mRootActivityContainer)
+ .setDisplay(display).setActivityType(ACTIVITY_TYPE_HOME).build();
// Put a finishing standard activity which will be reparented.
final ActivityStack stack = createFullscreenStackWithSimpleActivityAt(display);
stack.topRunningActivityLocked().makeFinishingLocked();
+ clearInvocations(homeStack);
display.remove();
// The removed display should have no focused stack and its home stack should never resume.
assertNull(display.getFocusedStack());
- verify(spiedHomeStack, never()).resumeTopActivityUncheckedLocked(any(), any());
+ verify(homeStack, never()).resumeTopActivityUncheckedLocked(any(), any());
}
private ActivityStack createFullscreenStackWithSimpleActivityAt(ActivityDisplay display) {
@@ -323,24 +317,24 @@ public class ActivityDisplayTests extends ActivityTestsBase {
doAnswer(invocation -> {
display.positionChildAtTop(stack3, false);
return true;
- }).when(mSupervisor).removeTaskByIdLocked(eq(task4.taskId), anyBoolean(), anyBoolean(),
+ }).when(mSupervisor).removeTaskByIdLocked(eq(task4.mTaskId), anyBoolean(), anyBoolean(),
any());
// Removing stacks from the display while removing stacks.
doAnswer(invocation -> {
display.removeChild(stack2);
return true;
- }).when(mSupervisor).removeTaskByIdLocked(eq(task2.taskId), anyBoolean(), anyBoolean(),
+ }).when(mSupervisor).removeTaskByIdLocked(eq(task2.mTaskId), anyBoolean(), anyBoolean(),
any());
runnable.run();
- verify(mSupervisor).removeTaskByIdLocked(eq(task4.taskId), anyBoolean(), anyBoolean(),
+ verify(mSupervisor).removeTaskByIdLocked(eq(task4.mTaskId), anyBoolean(), anyBoolean(),
any());
- verify(mSupervisor).removeTaskByIdLocked(eq(task3.taskId), anyBoolean(), anyBoolean(),
+ verify(mSupervisor).removeTaskByIdLocked(eq(task3.mTaskId), anyBoolean(), anyBoolean(),
any());
- verify(mSupervisor).removeTaskByIdLocked(eq(task2.taskId), anyBoolean(), anyBoolean(),
+ verify(mSupervisor).removeTaskByIdLocked(eq(task2.mTaskId), anyBoolean(), anyBoolean(),
any());
- verify(mSupervisor).removeTaskByIdLocked(eq(task1.taskId), anyBoolean(), anyBoolean(),
+ verify(mSupervisor).removeTaskByIdLocked(eq(task1.mTaskId), 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 aaaa7a5ab596..03367db0c3e6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -18,8 +18,6 @@ package com.android.server.wm;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -28,6 +26,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMor
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.timeout;
@@ -62,10 +61,8 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase {
private ActivityMetricsLaunchObserver mLaunchObserver;
private ActivityMetricsLaunchObserverRegistry mLaunchObserverRegistry;
- private ActivityStack mStack;
- private TaskRecord mTask;
- private ActivityRecord mActivityRecord;
- private ActivityRecord mActivityRecordTrampoline;
+ private ActivityRecord mTrampolineActivity;
+ private ActivityRecord mTopActivity;
@Before
public void setUpAMLO() {
@@ -79,15 +76,10 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase {
// Sometimes we need an ActivityRecord for ActivityMetricsLogger to do anything useful.
// This seems to be the easiest way to create an ActivityRecord.
- mStack = new StackBuilder(mRootActivityContainer)
- .setActivityType(ACTIVITY_TYPE_STANDARD)
- .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
- .setOnTop(true)
- .setCreateActivity(true)
+ mTrampolineActivity = new ActivityBuilder(mService).setCreateTask(true).build();
+ mTopActivity = new ActivityBuilder(mService)
+ .setTask(mTrampolineActivity.getTaskRecord())
.build();
- mTask = mStack.topTask();
- mActivityRecord = mTask.getTopActivity();
- mActivityRecordTrampoline = new ActivityBuilder(mService).setTask(mTask).build();
}
@After
@@ -122,123 +114,130 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase {
return verify(mock, timeout(TimeUnit.SECONDS.toMillis(5)));
}
- @Test
- public void testOnIntentStarted() throws Exception {
+ private void onIntentStarted() {
Intent intent = new Intent("action 1");
mActivityMetricsLogger.notifyActivityLaunching(intent);
- verifyAsync(mLaunchObserver).onIntentStarted(eq(intent));
+ verifyAsync(mLaunchObserver).onIntentStarted(eq(intent), anyLong());
verifyNoMoreInteractions(mLaunchObserver);
}
@Test
- public void testOnIntentFailed() throws Exception {
- testOnIntentStarted();
-
- ActivityRecord activityRecord = null;
+ public void testOnIntentFailed() {
+ onIntentStarted();
// Bringing an intent that's already running 'to front' is not considered
// as an ACTIVITY_LAUNCHED state transition.
mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
- activityRecord);
+ null /* launchedActivity */);
verifyAsync(mLaunchObserver).onIntentFailed();
verifyNoMoreInteractions(mLaunchObserver);
}
- @Test
- public void testOnActivityLaunched() throws Exception {
- testOnIntentStarted();
+ private void onActivityLaunched() {
+ onIntentStarted();
- mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
- mActivityRecord);
+ mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mTopActivity);
- verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mActivityRecord), anyInt());
+ verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mTopActivity), anyInt());
verifyNoMoreInteractions(mLaunchObserver);
}
@Test
- public void testOnActivityLaunchFinished() throws Exception {
- testOnActivityLaunched();
+ public void testOnActivityLaunchFinished() {
+ onActivityLaunched();
- mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(),
- SystemClock.uptimeMillis());
+ mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(),
+ SystemClock.elapsedRealtimeNanos());
- mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecord.getWindowingMode(),
- SystemClock.uptimeMillis());
+ notifyWindowsDrawn(mTopActivity);
- verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecord));
- verifyNoMoreInteractions(mLaunchObserver);
+ verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mTopActivity), anyLong());
+ verifyNoMoreInteractions(mLaunchObserver);
}
@Test
- public void testOnActivityLaunchCancelled() throws Exception {
- testOnActivityLaunched();
+ public void testOnActivityLaunchCancelled() {
+ onActivityLaunched();
- mActivityRecord.mDrawn = true;
+ mTopActivity.mDrawn = true;
- // Cannot time already-visible activities.
- mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mActivityRecord);
+ // Cannot time already-visible activities.
+ mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity);
- verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mActivityRecord));
- verifyNoMoreInteractions(mLaunchObserver);
+ verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mTopActivity));
+ verifyNoMoreInteractions(mLaunchObserver);
}
@Test
- public void testOnActivityLaunchedTrampoline() throws Exception {
- testOnIntentStarted();
+ public void testOnReportFullyDrawn() {
+ onActivityLaunched();
+
+ mActivityMetricsLogger.logAppTransitionReportedDrawn(mTopActivity, false);
- mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
- mActivityRecord);
+ verifyAsync(mLaunchObserver).onReportFullyDrawn(eqProto(mTopActivity), anyLong());
+ verifyNoMoreInteractions(mLaunchObserver);
+ }
- verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mActivityRecord), anyInt());
+ private void onActivityLaunchedTrampoline() {
+ onIntentStarted();
+
+ mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mTopActivity);
+
+ verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mTopActivity), anyInt());
// A second, distinct, activity launch is coalesced into the the current app launch sequence
- mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
- mActivityRecordTrampoline);
+ mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mTrampolineActivity);
verifyNoMoreInteractions(mLaunchObserver);
}
+ private void notifyWindowsDrawn(ActivityRecord r) {
+ mActivityMetricsLogger.notifyWindowsDrawn(r.getWindowingMode(),
+ SystemClock.elapsedRealtimeNanos());
+ }
+
@Test
- public void testOnActivityLaunchFinishedTrampoline() throws Exception {
- testOnActivityLaunchedTrampoline();
+ public void testOnActivityLaunchFinishedTrampoline() {
+ onActivityLaunchedTrampoline();
- mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(),
- SystemClock.uptimeMillis());
+ mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(),
+ SystemClock.elapsedRealtimeNanos());
- mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecordTrampoline.getWindowingMode(),
- SystemClock.uptimeMillis());
+ notifyWindowsDrawn(mTrampolineActivity);
- verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecordTrampoline));
- verifyNoMoreInteractions(mLaunchObserver);
+ notifyWindowsDrawn(mTopActivity);
+
+ verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mTrampolineActivity),
+ anyLong());
+ verifyNoMoreInteractions(mLaunchObserver);
}
@Test
- public void testOnActivityLaunchCancelledTrampoline() throws Exception {
- testOnActivityLaunchedTrampoline();
+ public void testOnActivityLaunchCancelledTrampoline() {
+ onActivityLaunchedTrampoline();
- mActivityRecordTrampoline.mDrawn = true;
+ mTrampolineActivity.mDrawn = true;
- // Cannot time already-visible activities.
- mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
- mActivityRecordTrampoline);
+ // Cannot time already-visible activities.
+ mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mTrampolineActivity);
- verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mActivityRecordTrampoline));
- verifyNoMoreInteractions(mLaunchObserver);
+ verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mTrampolineActivity));
+ verifyNoMoreInteractions(mLaunchObserver);
}
@Test
- public void testActivityRecordProtoIsNotTooBig() throws Exception {
+ public void testActivityRecordProtoIsNotTooBig() {
// The ActivityRecordProto must not be too big, otherwise converting it at runtime
// will become prohibitively expensive.
- assertWithMessage("mActivityRecord: %s", mActivityRecord).
- that(activityRecordToProto(mActivityRecord).length).
- isAtMost(ActivityMetricsLogger.LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE);
+ assertWithMessage("mTopActivity: %s", mTopActivity)
+ .that(activityRecordToProto(mTopActivity).length)
+ .isAtMost(ActivityMetricsLogger.LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE);
- assertWithMessage("mActivityRecordTrampoline: %s", mActivityRecordTrampoline).
- that(activityRecordToProto(mActivityRecordTrampoline).length).
- isAtMost(ActivityMetricsLogger.LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE);
+ assertWithMessage("mTrampolineActivity: %s", mTrampolineActivity)
+ .that(activityRecordToProto(mTrampolineActivity).length)
+ .isAtMost(ActivityMetricsLogger.LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 30c8eb36f34a..bf1508a34872 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -103,8 +103,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
-import java.util.concurrent.TimeUnit;
-
/**
* Tests for the {@link ActivityRecord} class.
*
@@ -289,22 +287,6 @@ public class ActivityRecordTests extends ActivityTestsBase {
}
@Test
- public void testNotifiesSeqIncrementToAppToken() {
- final Configuration appWindowTokenRequestedOrientation = mock(Configuration.class);
- mActivity.mAppWindowToken = mock(AppWindowToken.class);
- doReturn(appWindowTokenRequestedOrientation).when(mActivity.mAppWindowToken)
- .getRequestedOverrideConfiguration();
-
- final Configuration newConfig = new Configuration();
- newConfig.orientation = ORIENTATION_PORTRAIT;
-
- final int prevSeq = mActivity.getMergedOverrideConfiguration().seq;
- mActivity.onRequestedOverrideConfigurationChanged(newConfig);
- assertEquals(prevSeq + 1, appWindowTokenRequestedOrientation.seq);
- verify(mActivity.mAppWindowToken).onMergedOverrideConfigurationChanged();
- }
-
- @Test
public void testSetsRelaunchReason_NotDragResizing() {
mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing");
@@ -521,7 +503,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
anyInt() /* height */, any() /* displayCutout */, any() /* outInsets */);
doReturn(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
- .when(mActivity.mAppWindowToken).getOrientationIgnoreVisibility();
+ .when(mActivity).getRequestedOrientation();
mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
mActivity.info.minAspectRatio = mActivity.info.maxAspectRatio = 1;
ensureActivityConfiguration();
@@ -573,7 +555,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
// Move the non-resizable activity to the new display.
mStack.reparent(newDisplay, true /* onTop */, false /* displayRemoved */);
- assertEquals(originalBounds, mActivity.getBounds());
+ assertEquals(originalBounds, mActivity.getWindowConfiguration().getBounds());
assertEquals(originalDpi, mActivity.getConfiguration().densityDpi);
assertTrue(mActivity.inSizeCompatMode());
}
@@ -581,7 +563,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void testSizeCompatMode_FixedScreenBoundsWhenDisplaySizeChanged() {
setupDisplayContentForCompatDisplayInsets();
- when(mActivity.mAppWindowToken.getOrientationIgnoreVisibility()).thenReturn(
+ when(mActivity.getRequestedOrientation()).thenReturn(
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
mTask.getWindowConfiguration().setAppBounds(mStack.getDisplay().getBounds());
mTask.getConfiguration().orientation = ORIENTATION_PORTRAIT;
@@ -594,7 +576,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
setupDisplayAndParentSize(1000, 2000);
ensureActivityConfiguration();
- assertEquals(originalBounds, mActivity.getBounds());
+ assertEquals(originalBounds, mActivity.getWindowConfiguration().getBounds());
assertTrue(mActivity.inSizeCompatMode());
}
@@ -629,7 +611,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
mActivity.visible = false;
mActivity.app.setReportedProcState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
// Make the parent bounds to be different so the activity is in size compatibility mode.
- mTask.getWindowConfiguration().setAppBounds(new Rect(0, 0, 600, 1200));
+ setupDisplayAndParentSize(600, 1200);
// Simulate the display changes orientation.
doReturn(ActivityInfo.CONFIG_SCREEN_SIZE | CONFIG_ORIENTATION
@@ -646,7 +628,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
// The override configuration should be reset and the activity's process will be killed.
assertFalse(mActivity.inSizeCompatMode());
verify(mActivity).restartProcessIfVisible();
- mLockRule.runWithScissors(mService.mH, () -> { }, TimeUnit.SECONDS.toMillis(3));
+ waitHandlerIdle(mService.mH);
verify(mService.mAmInternal).killProcess(
eq(mActivity.app.mName), eq(mActivity.app.mUid), anyString());
}
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 9583b8acb14a..1eeca91e29d0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
@@ -44,6 +44,7 @@ import androidx.test.filters.MediumTest;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Tests for the {@link ActivityStackSupervisor} class.
@@ -53,6 +54,7 @@ import org.junit.Test;
*/
@MediumTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class ActivityStackSupervisorTests extends ActivityTestsBase {
private ActivityStack mFullscreenStack;
@@ -79,32 +81,28 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase {
/**
* Ensures that waiting results are notified of launches.
*/
- @SuppressWarnings("SynchronizeOnNonFinalField")
@Test
public void testReportWaitingActivityLaunchedIfNeeded() {
final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
.setStack(mFullscreenStack).build();
- // #notifyAll will be called on the ActivityManagerService. we must hold the object lock
- // when this happens.
- synchronized (mService.mGlobalLock) {
- final WaitResult taskToFrontWait = new WaitResult();
- mSupervisor.mWaitingActivityLaunched.add(taskToFrontWait);
- mSupervisor.reportWaitingActivityLaunchedIfNeeded(firstActivity, START_TASK_TO_FRONT);
-
- assertThat(mSupervisor.mWaitingActivityLaunched).isEmpty();
- assertEquals(taskToFrontWait.result, START_TASK_TO_FRONT);
- assertNull(taskToFrontWait.who);
-
- final WaitResult deliverToTopWait = new WaitResult();
- mSupervisor.mWaitingActivityLaunched.add(deliverToTopWait);
- mSupervisor.reportWaitingActivityLaunchedIfNeeded(firstActivity,
- START_DELIVERED_TO_TOP);
-
- assertThat(mSupervisor.mWaitingActivityLaunched).isEmpty();
- assertEquals(deliverToTopWait.result, START_DELIVERED_TO_TOP);
- assertEquals(deliverToTopWait.who, firstActivity.mActivityComponent);
- }
+ final WaitResult taskToFrontWait = new WaitResult();
+ mSupervisor.mWaitingActivityLaunched.add(taskToFrontWait);
+ // #notifyAll will be called on the ActivityTaskManagerService#mGlobalLock. The lock is hold
+ // implicitly by WindowManagerGlobalLockRule.
+ mSupervisor.reportWaitingActivityLaunchedIfNeeded(firstActivity, START_TASK_TO_FRONT);
+
+ assertThat(mSupervisor.mWaitingActivityLaunched).isEmpty();
+ assertEquals(taskToFrontWait.result, START_TASK_TO_FRONT);
+ assertNull(taskToFrontWait.who);
+
+ final WaitResult deliverToTopWait = new WaitResult();
+ mSupervisor.mWaitingActivityLaunched.add(deliverToTopWait);
+ mSupervisor.reportWaitingActivityLaunchedIfNeeded(firstActivity, START_DELIVERED_TO_TOP);
+
+ assertThat(mSupervisor.mWaitingActivityLaunched).isEmpty();
+ assertEquals(deliverToTopWait.result, START_DELIVERED_TO_TOP);
+ assertEquals(deliverToTopWait.who, firstActivity.mActivityComponent);
}
/**
@@ -129,7 +127,7 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase {
mSupervisor.handleNonResizableTaskIfNeeded(task, newDisplay.getWindowingMode(),
newDisplay.mDisplayId, stack);
// The top activity is unresizable, so it should notify the activity is forced resizing.
- verify(taskChangeNotifier).notifyActivityForcedResizable(eq(task.taskId),
+ verify(taskChangeNotifier).notifyActivityForcedResizable(eq(task.mTaskId),
eq(FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY),
eq(unresizableActivity.packageName));
reset(taskChangeNotifier);
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 2a8b4c88c000..2835c1fb59ae 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -67,6 +67,7 @@ import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Tests for the {@link ActivityStack} class.
@@ -76,6 +77,7 @@ import org.junit.Test;
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class ActivityStackTests extends ActivityTestsBase {
private ActivityDisplay mDefaultDisplay;
private ActivityStack mStack;
@@ -889,7 +891,7 @@ public class ActivityStackTests extends ActivityTestsBase {
activity.app = null;
overlayActivity.app = null;
- assertEquals(2, mTask.mActivities.size());
+ assertEquals(2, mTask.getChildCount());
mStack.finishDisabledPackageActivitiesLocked(activity.packageName,
null /* filterByClasses */, true /* doit */, true /* evenPersistent */,
@@ -898,7 +900,7 @@ public class ActivityStackTests extends ActivityTestsBase {
// 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
// should be empty.
- assertThat(mTask.mActivities).isEmpty();
+ assertFalse(mTask.hasChild());
assertThat(mStack.getAllTasks()).isEmpty();
}
@@ -916,11 +918,11 @@ public class ActivityStackTests extends ActivityTestsBase {
// second activity will be immediately removed as it has no state.
secondActivity.setSavedState(null /* savedState */);
- assertEquals(2, mTask.mActivities.size());
+ assertEquals(2, mTask.getChildCount());
mStack.handleAppDiedLocked(secondActivity.app);
- assertThat(mTask.mActivities).isEmpty();
+ assertFalse(mTask.hasChild());
assertThat(mStack.getAllTasks()).isEmpty();
}
@@ -934,7 +936,7 @@ public class ActivityStackTests extends ActivityTestsBase {
mStack.handleAppDiedLocked(activity.app);
- assertEquals(1, mTask.mActivities.size());
+ assertEquals(1, mTask.getChildCount());
assertEquals(1, mStack.getAllTasks().size());
}
@@ -948,7 +950,7 @@ public class ActivityStackTests extends ActivityTestsBase {
mStack.handleAppDiedLocked(activity.app);
- assertThat(mTask.mActivities).isEmpty();
+ assertFalse(mTask.hasChild());
assertThat(mStack.getAllTasks()).isEmpty();
}
@@ -962,7 +964,7 @@ public class ActivityStackTests extends ActivityTestsBase {
mStack.handleAppDiedLocked(activity.app);
- assertEquals(1, mTask.mActivities.size());
+ assertEquals(1, mTask.getChildCount());
assertEquals(1, mStack.getAllTasks().size());
}
@@ -976,7 +978,7 @@ public class ActivityStackTests extends ActivityTestsBase {
mStack.handleAppDiedLocked(activity.app);
- assertThat(mTask.mActivities).isEmpty();
+ assertFalse(mTask.hasChild());
assertThat(mStack.getAllTasks()).isEmpty();
}
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 a7bbe6e4cf02..11aac3cf046b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
@@ -38,6 +38,7 @@ import com.android.server.wm.ActivityStarter.Factory;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.Random;
@@ -49,6 +50,7 @@ import java.util.Random;
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class ActivityStartControllerTests extends ActivityTestsBase {
private ActivityStartController mController;
private Factory mFactory;
@@ -71,7 +73,9 @@ public class ActivityStartControllerTests extends ActivityTestsBase {
final Random random = new Random();
final ActivityRecord activity = new ActivityBuilder(mService).build();
- final ActivityRecord source = new ActivityBuilder(mService).build();
+ final ActivityRecord source = new ActivityBuilder(mService)
+ .setCreateTask(true)
+ .build();
final int startFlags = random.nextInt();
final ActivityStack stack = mService.mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 350114c792bf..4165052fa675 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -175,8 +175,8 @@ public class ActivityStartInterceptorTest {
.build();
when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID))
.thenReturn(suspendingPackage);
- when(mPackageManagerInternal.getSuspendedDialogInfo(TEST_PACKAGE_NAME, TEST_USER_ID))
- .thenReturn(dialogInfo);
+ when(mPackageManagerInternal.getSuspendedDialogInfo(TEST_PACKAGE_NAME, suspendingPackage,
+ TEST_USER_ID)).thenReturn(dialogInfo);
// THEN calling intercept returns true
assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
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 0de564f656af..b9d4c31846dd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -57,6 +57,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import android.app.ActivityOptions;
@@ -369,6 +370,7 @@ public class ActivityStarterTests extends ActivityTestsBase {
doReturn(false).when(mockPackageManager).isPermissionsReviewRequired(any(), anyInt());
doNothing().when(mockPackageManager).grantImplicitAccess(
anyInt(), any(), anyInt(), anyInt());
+ doNothing().when(mockPackageManager).notifyPackageUse(anyString(), anyInt());
final Intent intent = new Intent();
intent.addFlags(launchFlags);
@@ -758,7 +760,7 @@ public class ActivityStarterTests extends ActivityTestsBase {
false /* mockGetLaunchStack */);
// Create a secondary display at bottom.
- final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay());
+ final TestActivityDisplay secondaryDisplay = createNewActivityDisplay();
mRootActivityContainer.addChild(secondaryDisplay, POSITION_BOTTOM);
final ActivityStack stack = secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, true /* onTop */);
@@ -796,7 +798,7 @@ public class ActivityStarterTests extends ActivityTestsBase {
false /* mockGetLaunchStack */);
// Create a secondary display with an activity.
- final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay());
+ final TestActivityDisplay secondaryDisplay = createNewActivityDisplay();
mRootActivityContainer.addChild(secondaryDisplay, POSITION_TOP);
final ActivityRecord singleTaskActivity = createSingleTaskActivityOn(
secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
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 297aa7eab169..7b7e6e70cd80 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -26,6 +26,7 @@ import androidx.test.filters.MediumTest;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Tests for the {@link ActivityTaskManagerService} class.
@@ -34,6 +35,7 @@ import org.junit.Test;
* atest WmTests:ActivityTaskManagerServiceTests
*/
@MediumTest
+@RunWith(WindowTestRunner.class)
public class ActivityTaskManagerServiceTests extends ActivityTestsBase {
@Before
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 d311dfc60675..d43fe63c04d1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -27,6 +27,7 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -38,33 +39,21 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
+import android.os.Build;
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
-import android.testing.DexmakerShareClassLoaderRule;
+import android.util.Pair;
import android.view.DisplayInfo;
import com.android.server.AttributeCache;
import org.junit.Before;
import org.junit.BeforeClass;
-import org.junit.Rule;
/**
* A base class to handle common operations in activity related unit tests.
*/
-class ActivityTestsBase {
-
- @Rule
- public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
- new DexmakerShareClassLoaderRule();
-
- @Rule
- public final SystemServicesTestRule mSystemServicesTestRule = new SystemServicesTestRule();
-
- @WindowTestRunner.MethodWrapperRule
- public final WindowManagerGlobalLockRule mLockRule =
- new WindowManagerGlobalLockRule(mSystemServicesTestRule);
-
+class ActivityTestsBase extends SystemServiceTestsBase {
final Context mContext = getInstrumentation().getTargetContext();
ActivityTaskManagerService mService;
@@ -243,6 +232,7 @@ class ActivityTestsBase {
intent.setComponent(mComponent);
final ActivityInfo aInfo = new ActivityInfo();
aInfo.applicationInfo = new ApplicationInfo();
+ aInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
aInfo.applicationInfo.packageName = mComponent.getPackageName();
aInfo.applicationInfo.uid = mUid;
aInfo.packageName = mComponent.getPackageName();
@@ -271,12 +261,10 @@ class ActivityTestsBase {
if (mTaskRecord != null) {
// fullscreen value is normally read from resources in ctor, so for testing we need
// to set it somewhere else since we can't mock resources.
- activity.fullscreen = true;
+ doReturn(true).when(activity).occludesParent();
activity.setTask(mTaskRecord);
- activity.createAppWindowToken();
- spyOn(activity.mAppWindowToken);
// Make visible by default...
- activity.mAppWindowToken.setHidden(false);
+ activity.setHidden(false);
}
final WindowProcessController wpc = new WindowProcessController(mService,
@@ -285,6 +273,8 @@ class ActivityTestsBase {
mock(WindowProcessListener.class));
wpc.setThread(mock(IApplicationThread.class));
activity.setProcess(wpc);
+ doReturn(wpc).when(mService).getProcessController(
+ activity.processName, activity.info.applicationInfo.uid);
// Resume top activities to make sure all other signals in the system are connected.
mService.mRootActivityContainer.resumeFocusedStacksTopActivities();
@@ -378,9 +368,10 @@ class ActivityTestsBase {
intent.setFlags(mFlags);
final TaskRecord task = new TaskRecord(mSupervisor.mService, mTaskId, aInfo,
- intent /*intent*/, mVoiceSession, null /*_voiceInteractor*/);
+ intent /*intent*/, mVoiceSession, null /*_voiceInteractor*/,
+ null /*taskDescription*/);
spyOn(task);
- task.userId = mUserId;
+ task.mUserId = mUserId;
if (mStack != null) {
mStack.moveToFront("test");
@@ -389,8 +380,6 @@ class ActivityTestsBase {
spyOn(task.mTask);
}
- task.touchActiveTime();
-
return task;
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java
new file mode 100644
index 000000000000..77f9f0428e70
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for the {@link TaskStack} class.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:AnimatingActivityRegistryTest
+ */
+@SmallTest
+@Presubmit
+public class AnimatingActivityRegistryTest extends WindowTestsBase {
+
+ @Mock
+ AnimationAdapter mAdapter;
+
+ @Mock
+ Runnable mMockEndDeferFinishCallback1;
+ @Mock
+ Runnable mMockEndDeferFinishCallback2;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testDeferring() {
+ final ActivityRecord activity1 = createActivityRecord(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final ActivityRecord activity2 = createAppWindow(activity1.getTask(), ACTIVITY_TYPE_STANDARD,
+ "activity2").mActivityRecord;
+ final AnimatingActivityRegistry registry =
+ activity1.getStack().getAnimatingActivityRegistry();
+
+ activity1.startAnimation(activity1.getPendingTransaction(), mAdapter, false /* hidden */);
+ activity2.startAnimation(activity1.getPendingTransaction(), mAdapter, false /* hidden */);
+ assertTrue(activity1.isSelfAnimating());
+ assertTrue(activity2.isSelfAnimating());
+
+ // Make sure that first animation finish is deferred, second one is not deferred, and first
+ // one gets cancelled.
+ assertTrue(registry.notifyAboutToFinish(activity1, mMockEndDeferFinishCallback1));
+ assertFalse(registry.notifyAboutToFinish(activity2, mMockEndDeferFinishCallback2));
+ verify(mMockEndDeferFinishCallback1).run();
+ verifyZeroInteractions(mMockEndDeferFinishCallback2);
+ }
+
+ @Test
+ @FlakyTest(bugId = 131005232)
+ public void testContainerRemoved() {
+ final ActivityRecord window1 = createActivityRecord(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final ActivityRecord window2 = createAppWindow(window1.getTask(), ACTIVITY_TYPE_STANDARD,
+ "window2").mActivityRecord;
+ final AnimatingActivityRegistry registry =
+ window1.getStack().getAnimatingActivityRegistry();
+
+ window1.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */);
+ window2.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */);
+ assertTrue(window1.isSelfAnimating());
+ assertTrue(window2.isSelfAnimating());
+
+ // Make sure that first animation finish is deferred, and removing the second window stops
+ // finishes all pending deferred finishings.
+ registry.notifyAboutToFinish(window1, mMockEndDeferFinishCallback1);
+ window2.setParent(null);
+ verify(mMockEndDeferFinishCallback1).run();
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
deleted file mode 100644
index 5cd649fc968f..000000000000
--- a/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
+++ /dev/null
@@ -1,104 +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;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for the {@link TaskStack} class.
- *
- * Build/Install/Run:
- * atest FrameworksServicesTests:AnimatingAppWindowTokenRegistryTest
- */
-@SmallTest
-@Presubmit
-public class AnimatingAppWindowTokenRegistryTest extends WindowTestsBase {
-
- @Mock
- AnimationAdapter mAdapter;
-
- @Mock
- Runnable mMockEndDeferFinishCallback1;
- @Mock
- Runnable mMockEndDeferFinishCallback2;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- }
-
- @Test
- public void testDeferring() {
- final AppWindowToken window1 = createAppWindowToken(mDisplayContent,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- final AppWindowToken window2 = createAppWindow(window1.getTask(), ACTIVITY_TYPE_STANDARD,
- "window2").mAppToken;
- final AnimatingAppWindowTokenRegistry registry =
- window1.getStack().getAnimatingAppWindowTokenRegistry();
-
- window1.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */);
- window2.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */);
- assertTrue(window1.isSelfAnimating());
- assertTrue(window2.isSelfAnimating());
-
- // Make sure that first animation finish is deferred, second one is not deferred, and first
- // one gets cancelled.
- assertTrue(registry.notifyAboutToFinish(window1, mMockEndDeferFinishCallback1));
- assertFalse(registry.notifyAboutToFinish(window2, mMockEndDeferFinishCallback2));
- verify(mMockEndDeferFinishCallback1).run();
- verifyZeroInteractions(mMockEndDeferFinishCallback2);
- }
-
- @Test
- @FlakyTest(bugId = 131005232)
- public void testContainerRemoved() {
- final AppWindowToken window1 = createAppWindowToken(mDisplayContent,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- final AppWindowToken window2 = createAppWindow(window1.getTask(), ACTIVITY_TYPE_STANDARD,
- "window2").mAppToken;
- final AnimatingAppWindowTokenRegistry registry =
- window1.getStack().getAnimatingAppWindowTokenRegistry();
-
- window1.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */);
- window2.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */);
- assertTrue(window1.isSelfAnimating());
- assertTrue(window2.isSelfAnimating());
-
- // Make sure that first animation finish is deferred, and removing the second window stops
- // finishes all pending deferred finishings.
- registry.notifyAboutToFinish(window1, mMockEndDeferFinishCallback1);
- window2.setParent(null);
- verify(mMockEndDeferFinishCallback1).run();
- }
-}
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 629a95453054..1fb6a563aa40 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -42,6 +42,7 @@ import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Tests for change transitions
@@ -51,18 +52,17 @@ import org.junit.Test;
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class AppChangeTransitionTests extends WindowTestsBase {
private TaskStack mStack;
private Task mTask;
- private WindowTestUtils.TestAppWindowToken mToken;
+ private ActivityRecord mActivity;
public void setUpOnDisplay(DisplayContent dc) {
- mStack = createTaskStackOnDisplay(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, dc);
- mTask = createTaskInStack(mStack, 0 /* userId */);
- mToken = WindowTestUtils.createTestAppWindowToken(dc);
-
- mTask.addChild(mToken, 0);
+ mActivity = createTestActivityRecord(dc, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD);
+ mTask = mActivity.getTask();
+ mStack = mTask.mStack;
// Set a remote animator with snapshot disabled. Snapshots don't work in wmtests.
RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
@@ -108,11 +108,11 @@ public class AppChangeTransitionTests extends WindowTestsBase {
// Verify we are in a change transition, but without a snapshot.
// Though, the test will actually have crashed by now if a snapshot is attempted.
- assertNull(mToken.getThumbnail());
- assertTrue(mToken.isInChangeTransition());
+ assertNull(mActivity.getThumbnail());
+ assertTrue(mActivity.isInChangeTransition());
waitUntilHandlersIdle();
- mToken.removeImmediately();
+ mActivity.removeImmediately();
}
@Test
@@ -123,16 +123,16 @@ public class AppChangeTransitionTests extends WindowTestsBase {
mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
assertEquals(1, mDisplayContent.mChangingApps.size());
- assertTrue(mToken.isInChangeTransition());
+ assertTrue(mActivity.isInChangeTransition());
// Removing the app-token from the display should clean-up the
// the change leash.
- mDisplayContent.removeAppToken(mToken.token);
+ mDisplayContent.removeAppToken(mActivity.token);
assertEquals(0, mDisplayContent.mChangingApps.size());
- assertFalse(mToken.isInChangeTransition());
+ assertFalse(mActivity.isInChangeTransition());
waitUntilHandlersIdle();
- mToken.removeImmediately();
+ mActivity.removeImmediately();
}
@Test
@@ -153,11 +153,11 @@ public class AppChangeTransitionTests extends WindowTestsBase {
assertEquals(WINDOWING_MODE_FULLSCREEN, mTask.getWindowingMode());
// Make sure we're not waiting for a change animation (no leash)
- assertFalse(mToken.isInChangeTransition());
- assertNull(mToken.getThumbnail());
+ assertFalse(mActivity.isInChangeTransition());
+ assertNull(mActivity.getThumbnail());
waitUntilHandlersIdle();
- mToken.removeImmediately();
+ mActivity.removeImmediately();
}
@Test
@@ -165,16 +165,16 @@ public class AppChangeTransitionTests extends WindowTestsBase {
// setup currently defaults to no snapshot.
setUpOnDisplay(mDisplayContent);
- mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ mTask.mTaskRecord.setWindowingMode(WINDOWING_MODE_FREEFORM);
assertEquals(1, mDisplayContent.mChangingApps.size());
- assertTrue(mToken.isInChangeTransition());
+ assertTrue(mActivity.isInChangeTransition());
// Changing visibility should cancel the change transition and become closing
- mToken.setVisibility(false, false);
+ mActivity.setVisibility(false, false);
assertEquals(0, mDisplayContent.mChangingApps.size());
- assertFalse(mToken.isInChangeTransition());
+ assertFalse(mActivity.isInChangeTransition());
waitUntilHandlersIdle();
- mToken.removeImmediately();
+ mActivity.removeImmediately();
}
}
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 e71c8f47a69f..60204539d178 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -36,6 +36,7 @@ import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Build/Install/Run:
@@ -43,6 +44,7 @@ import org.junit.Test;
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class AppTransitionControllerTest extends WindowTestsBase {
private AppTransitionController mAppTransitionController;
@@ -55,73 +57,63 @@ public class AppTransitionControllerTest extends WindowTestsBase {
@Test
@FlakyTest(bugId = 131005232)
public void testTranslucentOpen() {
- synchronized (mWm.mGlobalLock) {
- final AppWindowToken behind = createAppWindowToken(mDisplayContent,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- final AppWindowToken translucentOpening = createAppWindowToken(mDisplayContent,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- translucentOpening.setOccludesParent(false);
- translucentOpening.setHidden(true);
- mDisplayContent.mOpeningApps.add(behind);
- mDisplayContent.mOpeningApps.add(translucentOpening);
- assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN,
- mAppTransitionController.maybeUpdateTransitToTranslucentAnim(
- TRANSIT_TASK_OPEN));
- }
+ final ActivityRecord behind = createActivityRecord(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final ActivityRecord translucentOpening = createActivityRecord(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ translucentOpening.setOccludesParent(false);
+ translucentOpening.setHidden(true);
+ mDisplayContent.mOpeningApps.add(behind);
+ mDisplayContent.mOpeningApps.add(translucentOpening);
+ assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN,
+ mAppTransitionController.maybeUpdateTransitToTranslucentAnim(TRANSIT_TASK_OPEN));
}
@Test
@FlakyTest(bugId = 131005232)
public void testTranslucentClose() {
- synchronized (mWm.mGlobalLock) {
- final AppWindowToken behind = createAppWindowToken(mDisplayContent,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- final AppWindowToken translucentClosing = createAppWindowToken(mDisplayContent,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- translucentClosing.setOccludesParent(false);
- mDisplayContent.mClosingApps.add(translucentClosing);
- assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE,
- mAppTransitionController.maybeUpdateTransitToTranslucentAnim(
- TRANSIT_TASK_CLOSE));
- }
+ final ActivityRecord behind = createActivityRecord(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final ActivityRecord translucentClosing = createActivityRecord(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ translucentClosing.setOccludesParent(false);
+ mDisplayContent.mClosingApps.add(translucentClosing);
+ assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE,
+ mAppTransitionController.maybeUpdateTransitToTranslucentAnim(TRANSIT_TASK_CLOSE));
}
@Test
@FlakyTest(bugId = 131005232)
public void testChangeIsNotOverwritten() {
- synchronized (mWm.mGlobalLock) {
- final AppWindowToken behind = createAppWindowToken(mDisplayContent,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- final AppWindowToken translucentOpening = createAppWindowToken(mDisplayContent,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- translucentOpening.setOccludesParent(false);
- translucentOpening.setHidden(true);
- mDisplayContent.mOpeningApps.add(behind);
- mDisplayContent.mOpeningApps.add(translucentOpening);
- assertEquals(TRANSIT_TASK_CHANGE_WINDOWING_MODE,
- mAppTransitionController.maybeUpdateTransitToTranslucentAnim(
- TRANSIT_TASK_CHANGE_WINDOWING_MODE));
- }
+ final ActivityRecord behind = createActivityRecord(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final ActivityRecord translucentOpening = createActivityRecord(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ translucentOpening.setOccludesParent(false);
+ translucentOpening.setHidden(true);
+ mDisplayContent.mOpeningApps.add(behind);
+ mDisplayContent.mOpeningApps.add(translucentOpening);
+ assertEquals(TRANSIT_TASK_CHANGE_WINDOWING_MODE,
+ mAppTransitionController.maybeUpdateTransitToTranslucentAnim(
+ TRANSIT_TASK_CHANGE_WINDOWING_MODE));
}
@Test
@FlakyTest(bugId = 131005232)
public void testTransitWithinTask() {
- synchronized (mWm.mGlobalLock) {
- final AppWindowToken opening = createAppWindowToken(mDisplayContent,
- WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
- opening.setOccludesParent(false);
- final AppWindowToken closing = createAppWindowToken(mDisplayContent,
- WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
- closing.setOccludesParent(false);
- Task task = opening.getTask();
- mDisplayContent.mOpeningApps.add(opening);
- mDisplayContent.mClosingApps.add(closing);
- assertFalse(mAppTransitionController.isTransitWithinTask(TRANSIT_ACTIVITY_OPEN, task));
- closing.getTask().removeChild(closing);
- task.addChild(closing, 0);
- assertTrue(mAppTransitionController.isTransitWithinTask(TRANSIT_ACTIVITY_OPEN, task));
- assertFalse(mAppTransitionController.isTransitWithinTask(TRANSIT_TASK_OPEN, task));
- }
+ final ActivityRecord opening = createActivityRecord(mDisplayContent,
+ WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
+ opening.setOccludesParent(false);
+ final ActivityRecord closing = createActivityRecord(mDisplayContent,
+ WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
+ closing.setOccludesParent(false);
+ final Task task = opening.getTask();
+ mDisplayContent.mOpeningApps.add(opening);
+ mDisplayContent.mClosingApps.add(closing);
+ assertFalse(mAppTransitionController.isTransitWithinTask(TRANSIT_ACTIVITY_OPEN, task));
+ closing.getTask().removeChild(closing);
+ task.addChild(closing, 0);
+ assertTrue(mAppTransitionController.isTransitWithinTask(TRANSIT_ACTIVITY_OPEN, task));
+ assertFalse(mAppTransitionController.isTransitWithinTask(TRANSIT_TASK_OPEN, task));
}
}
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 45e68902e123..06afce2368b7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -31,7 +31,6 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -55,6 +54,7 @@ import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Test class for {@link AppTransition}.
@@ -64,16 +64,13 @@ import org.junit.Test;
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class AppTransitionTests extends WindowTestsBase {
private DisplayContent mDc;
@Before
public void setUp() throws Exception {
- synchronized (mWm.mGlobalLock) {
- // Hold the lock to protect the stubbing from being accessed by other threads.
- spyOn(mWm.mRoot);
- doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean());
- }
+ doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean());
mDc = mWm.getDefaultDisplayContentLocked();
}
@@ -125,14 +122,14 @@ public class AppTransitionTests extends WindowTestsBase {
final DisplayContent dc2 = createNewDisplay(Display.STATE_ON);
// Create 2 app window tokens to represent 2 activity window.
- final WindowTestUtils.TestAppWindowToken token1 = createTestAppWindowToken(dc1,
+ final ActivityRecord activity1 = createTestActivityRecord(dc1,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- final WindowTestUtils.TestAppWindowToken token2 = createTestAppWindowToken(dc2,
+ final ActivityRecord activity2 = createTestActivityRecord(dc2,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- token1.allDrawn = true;
- token1.startingDisplayed = true;
- token1.startingMoved = true;
+ activity1.allDrawn = true;
+ activity1.startingDisplayed = true;
+ activity1.startingMoved = true;
// Simulate activity resume / finish flows to prepare app transition & set visibility,
// make sure transition is set as expected for each display.
@@ -144,8 +141,8 @@ public class AppTransitionTests extends WindowTestsBase {
assertEquals(TRANSIT_ACTIVITY_CLOSE, dc2.mAppTransition.getAppTransition());
// One activity window is visible for resuming & the other activity window is invisible
// for finishing in different display.
- token1.setVisibility(true, false);
- token2.setVisibility(false, false);
+ activity1.setVisibility(true, false);
+ activity2.setVisibility(false, false);
// Make sure each display is in animating stage.
assertTrue(dc1.mOpeningApps.size() > 0);
@@ -162,12 +159,12 @@ public class AppTransitionTests extends WindowTestsBase {
final TaskStack stack1 = createTaskStackOnDisplay(dc1);
final Task task1 = createTaskInStack(stack1, 0 /* userId */);
- final WindowTestUtils.TestAppWindowToken token1 =
- WindowTestUtils.createTestAppWindowToken(dc1);
- task1.addChild(token1, 0);
+ final ActivityRecord activity1 =
+ WindowTestUtils.createTestActivityRecord(dc1);
+ task1.addChild(activity1, 0);
// Simulate same app is during opening / closing transition set stage.
- dc1.mClosingApps.add(token1);
+ dc1.mClosingApps.add(activity1);
assertTrue(dc1.mClosingApps.size() > 0);
dc1.prepareAppTransition(TRANSIT_ACTIVITY_OPEN,
@@ -175,15 +172,15 @@ public class AppTransitionTests extends WindowTestsBase {
assertEquals(TRANSIT_ACTIVITY_OPEN, dc1.mAppTransition.getAppTransition());
assertTrue(dc1.mAppTransition.isTransitionSet());
- dc1.mOpeningApps.add(token1);
+ dc1.mOpeningApps.add(activity1);
assertTrue(dc1.mOpeningApps.size() > 0);
// Move stack to another display.
stack1.reparent(dc2.getDisplayId(), new Rect(), true);
// Verify if token are cleared from both pending transition list in former display.
- assertFalse(dc1.mOpeningApps.contains(token1));
- assertFalse(dc1.mOpeningApps.contains(token1));
+ assertFalse(dc1.mOpeningApps.contains(activity1));
+ assertFalse(dc1.mOpeningApps.contains(activity1));
}
@Test
@@ -200,7 +197,7 @@ public class AppTransitionTests extends WindowTestsBase {
doReturn(false).when(dc).onDescendantOrientationChanged(any(), any());
final WindowState exitingAppWindow = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
dc, "exiting app");
- final AppWindowToken exitingAppToken = exitingAppWindow.mAppToken;
+ final ActivityRecord exitingActivity= exitingAppWindow.mActivityRecord;
// Wait until everything in animation handler get executed to prevent the exiting window
// from being removed during WindowSurfacePlacer Traversal.
waitUntilHandlersIdle();
@@ -218,7 +215,7 @@ public class AppTransitionTests extends WindowTestsBase {
false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */);
assertEquals(TRANSIT_ACTIVITY_CLOSE, dc.mAppTransition.getAppTransition());
dc.mAppTransition.overridePendingAppTransitionRemote(adapter);
- exitingAppToken.setVisibility(false, false);
+ exitingActivity.setVisibility(false, false);
assertTrue(dc.mClosingApps.size() > 0);
// Make sure window is in animating stage before freeze, and cancel after freeze.
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowThumbnailTest.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowThumbnailTest.java
index b8f8e21d64ac..8520d212c4ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowThumbnailTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowThumbnailTest.java
@@ -17,8 +17,8 @@
package com.android.server.wm;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -26,12 +26,12 @@ import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
import android.platform.test.annotations.Presubmit;
import android.view.Surface;
-import android.view.SurfaceControl;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Test class for {@link TaskSnapshotSurface}.
@@ -42,14 +42,15 @@ import org.junit.Test;
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class AppWindowThumbnailTest extends WindowTestsBase {
private AppWindowThumbnail buildThumbnail() {
final GraphicBuffer buffer = GraphicBuffer.create(1, 1, PixelFormat.RGBA_8888,
GraphicBuffer.USAGE_SW_READ_RARELY | GraphicBuffer.USAGE_SW_WRITE_NEVER);
- final AppWindowToken mockAwt = mock(AppWindowToken.class);
- when(mockAwt.getPendingTransaction()).thenReturn(new StubTransaction());
- when(mockAwt.makeSurface()).thenReturn(new MockSurfaceControlBuilder());
- return new AppWindowThumbnail(new StubTransaction(), mockAwt,
+ final ActivityRecord mockAr = mock(ActivityRecord.class);
+ when(mockAr.getPendingTransaction()).thenReturn(new StubTransaction());
+ when(mockAr.makeSurface()).thenReturn(new MockSurfaceControlBuilder());
+ return new AppWindowThumbnail(new StubTransaction(), mockAr,
buffer, false, mock(Surface.class), mock(SurfaceAnimator.class));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
index 70d9b5fedbcb..b0f3b42271de 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
@@ -33,26 +33,26 @@ import android.view.SurfaceControl;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
-import com.android.server.wm.WindowTestUtils.TestAppWindowToken;
-
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;
/**
- * Animation related tests for the {@link AppWindowToken} class.
+ * Animation related tests for the {@link ActivityRecord} class.
*
* Build/Install/Run:
* atest AppWindowTokenAnimationTests
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class AppWindowTokenAnimationTests extends WindowTestsBase {
- private TestAppWindowToken mToken;
+ private ActivityRecord mActivity;
@Mock
private AnimationAdapter mSpec;
@@ -61,39 +61,39 @@ public class AppWindowTokenAnimationTests extends WindowTestsBase {
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mToken = createTestAppWindowToken(mDisplayContent, WINDOWING_MODE_FULLSCREEN,
+ mActivity = createTestActivityRecord(mDisplayContent, WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD);
}
@Test
@FlakyTest(bugId = 131005232)
public void clipAfterAnim_boundsLayerIsCreated() {
- mToken.mNeedsAnimationBoundsLayer = true;
+ mActivity.mNeedsAnimationBoundsLayer = true;
- mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
- verify(mTransaction).reparent(eq(mToken.getSurfaceControl()),
- eq(mToken.mSurfaceAnimator.mLeash));
- verify(mTransaction).reparent(eq(mToken.mSurfaceAnimator.mLeash),
- eq(mToken.mAnimationBoundsLayer));
+ mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ verify(mTransaction).reparent(eq(mActivity.getSurfaceControl()),
+ eq(mActivity.mSurfaceAnimator.mLeash));
+ verify(mTransaction).reparent(eq(mActivity.mSurfaceAnimator.mLeash),
+ eq(mActivity.mAnimationBoundsLayer));
}
@Test
public void clipAfterAnim_boundsLayerZBoosted() {
- mToken.mNeedsAnimationBoundsLayer = true;
- mToken.mNeedsZBoost = true;
+ mActivity.mNeedsAnimationBoundsLayer = true;
+ mActivity.mNeedsZBoost = true;
- mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
- verify(mTransaction).setLayer(eq(mToken.mAnimationBoundsLayer),
- intThat(layer -> layer >= AppWindowToken.Z_BOOST_BASE));
+ mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ verify(mTransaction).setLayer(eq(mActivity.mAnimationBoundsLayer),
+ intThat(layer -> layer >= ActivityRecord.Z_BOOST_BASE));
}
@Test
@FlakyTest(bugId = 131005232)
public void clipAfterAnim_boundsLayerIsDestroyed() {
- mToken.mNeedsAnimationBoundsLayer = true;
- mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
- final SurfaceControl leash = mToken.mSurfaceAnimator.mLeash;
- final SurfaceControl animationBoundsLayer = mToken.mAnimationBoundsLayer;
+ mActivity.mNeedsAnimationBoundsLayer = true;
+ mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ final SurfaceControl leash = mActivity.mSurfaceAnimator.mLeash;
+ final SurfaceControl animationBoundsLayer = mActivity.mAnimationBoundsLayer;
final ArgumentCaptor<SurfaceAnimator.OnAnimationFinishedCallback> callbackCaptor =
ArgumentCaptor.forClass(
SurfaceAnimator.OnAnimationFinishedCallback.class);
@@ -102,30 +102,30 @@ public class AppWindowTokenAnimationTests extends WindowTestsBase {
callbackCaptor.getValue().onAnimationFinished(mSpec);
verify(mTransaction).remove(eq(leash));
verify(mTransaction).remove(eq(animationBoundsLayer));
- assertThat(mToken.mNeedsAnimationBoundsLayer).isFalse();
+ assertThat(mActivity.mNeedsAnimationBoundsLayer).isFalse();
}
@Test
public void clipAfterAnimCancelled_boundsLayerIsDestroyed() {
- mToken.mNeedsAnimationBoundsLayer = true;
- mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
- final SurfaceControl leash = mToken.mSurfaceAnimator.mLeash;
- final SurfaceControl animationBoundsLayer = mToken.mAnimationBoundsLayer;
+ mActivity.mNeedsAnimationBoundsLayer = true;
+ mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ final SurfaceControl leash = mActivity.mSurfaceAnimator.mLeash;
+ final SurfaceControl animationBoundsLayer = mActivity.mAnimationBoundsLayer;
- mToken.mSurfaceAnimator.cancelAnimation();
+ mActivity.mSurfaceAnimator.cancelAnimation();
verify(mTransaction).remove(eq(leash));
verify(mTransaction).remove(eq(animationBoundsLayer));
- assertThat(mToken.mNeedsAnimationBoundsLayer).isFalse();
+ assertThat(mActivity.mNeedsAnimationBoundsLayer).isFalse();
}
@Test
@FlakyTest(bugId = 131005232)
public void clipNoneAnim_boundsLayerIsNotCreated() {
- mToken.mNeedsAnimationBoundsLayer = false;
+ mActivity.mNeedsAnimationBoundsLayer = false;
- mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
- verify(mTransaction).reparent(eq(mToken.getSurfaceControl()),
- eq(mToken.mSurfaceAnimator.mLeash));
- assertThat(mToken.mAnimationBoundsLayer).isNull();
+ mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ verify(mTransaction).reparent(eq(mActivity.getSurfaceControl()),
+ eq(mActivity.mSurfaceAnimator.mLeash));
+ assertThat(mActivity.mAnimationBoundsLayer).isNull();
}
}
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 1f634b11d173..d68aef0415ac 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -53,7 +53,6 @@ import static org.mockito.Mockito.verify;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
-import android.view.Display;
import android.view.Surface;
import android.view.WindowManager;
@@ -62,21 +61,23 @@ import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mockito;
/**
- * Tests for the {@link AppWindowToken} class.
+ * Tests for the {@link ActivityRecord} class.
*
* Build/Install/Run:
* atest WmTests:AppWindowTokenTests
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class AppWindowTokenTests extends WindowTestsBase {
TaskStack mStack;
Task mTask;
- WindowTestUtils.TestAppWindowToken mToken;
+ ActivityRecord mActivity;
private final String mPackageName = getInstrumentation().getTargetContext().getPackageName();
@@ -84,68 +85,68 @@ public class AppWindowTokenTests extends WindowTestsBase {
public void setUp() throws Exception {
mStack = createTaskStackOnDisplay(mDisplayContent);
mTask = createTaskInStack(mStack, 0 /* userId */);
- mToken = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
+ mActivity = WindowTestUtils.createTestActivityRecord(mDisplayContent);
- mTask.addChild(mToken, 0);
+ mTask.addChild(mActivity, 0);
}
@Test
@Presubmit
public void testAddWindow_Order() {
- assertEquals(0, mToken.getWindowsCount());
+ assertEquals(0, mActivity.getChildCount());
- final WindowState win1 = createWindow(null, TYPE_APPLICATION, mToken, "win1");
- final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, mToken,
+ final WindowState win1 = createWindow(null, TYPE_APPLICATION, mActivity, "win1");
+ final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, mActivity,
"startingWin");
- final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, mToken, "baseWin");
- final WindowState win4 = createWindow(null, TYPE_APPLICATION, mToken, "win4");
+ final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, mActivity, "baseWin");
+ final WindowState win4 = createWindow(null, TYPE_APPLICATION, mActivity, "win4");
// Should not contain the windows that were added above.
- assertEquals(4, mToken.getWindowsCount());
- assertTrue(mToken.hasWindow(win1));
- assertTrue(mToken.hasWindow(startingWin));
- assertTrue(mToken.hasWindow(baseWin));
- assertTrue(mToken.hasWindow(win4));
+ assertEquals(4, mActivity.getChildCount());
+ assertTrue(mActivity.mChildren.contains(win1));
+ assertTrue(mActivity.mChildren.contains(startingWin));
+ assertTrue(mActivity.mChildren.contains(baseWin));
+ assertTrue(mActivity.mChildren.contains(win4));
// The starting window should be on-top of all other windows.
- assertEquals(startingWin, mToken.getLastChild());
+ assertEquals(startingWin, mActivity.mChildren.peekLast());
// The base application window should be below all other windows.
- assertEquals(baseWin, mToken.getFirstChild());
- mToken.removeImmediately();
+ assertEquals(baseWin, mActivity.mChildren.peekFirst());
+ mActivity.removeImmediately();
}
@Test
@Presubmit
public void testFindMainWindow() {
- assertNull(mToken.findMainWindow());
+ assertNull(mActivity.findMainWindow());
- final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1");
- final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, mToken, "window11");
- final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, mToken, "window12");
- assertEquals(window1, mToken.findMainWindow());
+ final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mActivity, "window1");
+ final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, mActivity, "window11");
+ final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, mActivity, "window12");
+ assertEquals(window1, mActivity.findMainWindow());
window1.mAnimatingExit = true;
- assertEquals(window1, mToken.findMainWindow());
- final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, mToken,
+ assertEquals(window1, mActivity.findMainWindow());
+ final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, mActivity,
"window2");
- assertEquals(window2, mToken.findMainWindow());
- mToken.removeImmediately();
+ assertEquals(window2, mActivity.findMainWindow());
+ mActivity.removeImmediately();
}
@Test
@Presubmit
public void testGetTopFullscreenWindow() {
- assertNull(mToken.getTopFullscreenWindow());
+ assertNull(mActivity.getTopFullscreenWindow());
- final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1");
- final WindowState window11 = createWindow(null, TYPE_APPLICATION, mToken, "window11");
- final WindowState window12 = createWindow(null, TYPE_APPLICATION, mToken, "window12");
- assertEquals(window12, mToken.getTopFullscreenWindow());
+ final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mActivity, "window1");
+ final WindowState window11 = createWindow(null, TYPE_APPLICATION, mActivity, "window11");
+ final WindowState window12 = createWindow(null, TYPE_APPLICATION, mActivity, "window12");
+ assertEquals(window12, mActivity.getTopFullscreenWindow());
window12.mAttrs.width = 500;
- assertEquals(window11, mToken.getTopFullscreenWindow());
+ assertEquals(window11, mActivity.getTopFullscreenWindow());
window11.mAttrs.width = 500;
- assertEquals(window1, mToken.getTopFullscreenWindow());
- mToken.removeImmediately();
+ assertEquals(window1, mActivity.getTopFullscreenWindow());
+ mActivity.removeImmediately();
}
@Test
@@ -154,11 +155,11 @@ public class AppWindowTokenTests extends WindowTestsBase {
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
TYPE_BASE_APPLICATION);
attrs.setTitle("AppWindow");
- final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
- mToken.addWindow(appWindow);
+ final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mActivity);
+ mActivity.addWindow(appWindow);
// Set initial orientation and update.
- mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
mDisplayContent.updateOrientation(
mDisplayContent.getRequestedOverrideConfiguration(),
null /* freezeThisOneIfNeeded */, false /* forceUpdate */);
@@ -166,7 +167,7 @@ public class AppWindowTokenTests extends WindowTestsBase {
appWindow.mResizeReported = false;
// Update the orientation to perform 180 degree rotation and check that resize was reported.
- mToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
+ mActivity.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
mDisplayContent.updateOrientation(
mDisplayContent.getRequestedOverrideConfiguration(),
null /* freezeThisOneIfNeeded */, false /* forceUpdate */);
@@ -186,8 +187,8 @@ public class AppWindowTokenTests extends WindowTestsBase {
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
TYPE_BASE_APPLICATION);
attrs.setTitle("RotationByPolicy");
- final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
- mToken.addWindow(appWindow);
+ final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mActivity);
+ mActivity.addWindow(appWindow);
// Set initial orientation and update.
performRotation(displayRotation, Surface.ROTATION_90);
@@ -207,10 +208,10 @@ public class AppWindowTokenTests extends WindowTestsBase {
@Test
public void testSizeCompatBounds() {
- final Rect fixedBounds = mToken.getRequestedOverrideConfiguration().windowConfiguration
+ final Rect fixedBounds = mActivity.getRequestedOverrideConfiguration().windowConfiguration
.getBounds();
fixedBounds.set(0, 0, 1200, 1600);
- mToken.getRequestedOverrideConfiguration().windowConfiguration.setAppBounds(fixedBounds);
+ mActivity.getRequestedOverrideConfiguration().windowConfiguration.setAppBounds(fixedBounds);
final Configuration newParentConfig = mTask.getConfiguration();
// Change the size of the container to two times smaller with insets.
@@ -218,50 +219,50 @@ public class AppWindowTokenTests extends WindowTestsBase {
final Rect containerAppBounds = newParentConfig.windowConfiguration.getAppBounds();
final Rect containerBounds = newParentConfig.windowConfiguration.getBounds();
containerBounds.set(0, 0, 600, 800);
- mToken.onConfigurationChanged(newParentConfig);
+ mActivity.onConfigurationChanged(newParentConfig);
- assertTrue(mToken.inSizeCompatMode());
- assertEquals(containerAppBounds, mToken.getBounds());
+ assertTrue(mActivity.hasSizeCompatBounds());
+ assertEquals(containerAppBounds, mActivity.getBounds());
assertEquals((float) containerAppBounds.width() / fixedBounds.width(),
- mToken.getSizeCompatScale(), 0.0001f /* delta */);
+ mActivity.getSizeCompatScale(), 0.0001f /* delta */);
// Change the width of the container to two times bigger.
containerAppBounds.set(0, 0, 2400, 1600);
containerBounds.set(containerAppBounds);
- mToken.onConfigurationChanged(newParentConfig);
+ mActivity.onConfigurationChanged(newParentConfig);
- assertTrue(mToken.inSizeCompatMode());
+ assertTrue(mActivity.hasSizeCompatBounds());
// Don't scale up, so the bounds keep the same as the fixed width.
- assertEquals(fixedBounds.width(), mToken.getBounds().width());
+ assertEquals(fixedBounds.width(), mActivity.getBounds().width());
// Assert the position is horizontal center.
assertEquals((containerAppBounds.width() - fixedBounds.width()) / 2,
- mToken.getBounds().left);
- assertEquals(1f, mToken.getSizeCompatScale(), 0.0001f /* delta */);
+ mActivity.getBounds().left);
+ assertEquals(1f, mActivity.getSizeCompatScale(), 0.0001f /* delta */);
// Change the width of the container to fit the fixed bounds.
containerBounds.set(0, 0, 1200, 2000);
- mToken.onConfigurationChanged(newParentConfig);
+ mActivity.onConfigurationChanged(newParentConfig);
// Assert don't use fixed bounds because the region is enough.
- assertFalse(mToken.inSizeCompatMode());
+ assertFalse(mActivity.hasSizeCompatBounds());
}
@Test
@Presubmit
public void testGetOrientation() {
- mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- mToken.setOccludesParent(false);
+ mActivity.setOccludesParent(false);
// Can specify orientation if app doesn't occludes parent.
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mActivity.getOrientation());
- mToken.setOccludesParent(true);
- mToken.setHidden(true);
- mToken.sendingToBottom = true;
+ mActivity.setOccludesParent(true);
+ mActivity.setHidden(true);
+ mActivity.sendingToBottom = true;
// Can not specify orientation if app isn't visible even though it occludes parent.
- assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_UNSET, mActivity.getOrientation());
// Can specify orientation if the current orientation candidate is orientation behind.
assertEquals(SCREEN_ORIENTATION_LANDSCAPE,
- mToken.getOrientation(SCREEN_ORIENTATION_BEHIND));
+ mActivity.getOrientation(SCREEN_ORIENTATION_BEHIND));
}
@Test
@@ -271,24 +272,24 @@ public class AppWindowTokenTests extends WindowTestsBase {
TYPE_BASE_APPLICATION);
attrs.flags |= FLAG_SHOW_WHEN_LOCKED | FLAG_DISMISS_KEYGUARD;
attrs.setTitle("AppWindow");
- final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
+ final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mActivity);
// Add window with show when locked flag
- mToken.addWindow(appWindow);
- assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
+ mActivity.addWindow(appWindow);
+ assertTrue(mActivity.containsShowWhenLockedWindow() && mActivity.containsDismissKeyguardWindow());
// Start relaunching
- mToken.startRelaunching();
- assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
+ mActivity.startRelaunching();
+ assertTrue(mActivity.containsShowWhenLockedWindow() && mActivity.containsDismissKeyguardWindow());
// Remove window and make sure that we still report back flag
- mToken.removeChild(appWindow);
- assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow());
+ mActivity.removeChild(appWindow);
+ assertTrue(mActivity.containsShowWhenLockedWindow() && mActivity.containsDismissKeyguardWindow());
// Finish relaunching and ensure flag is now not reported
- mToken.finishRelaunching();
+ mActivity.finishRelaunching();
assertFalse(
- mToken.containsShowWhenLockedWindow() || mToken.containsDismissKeyguardWindow());
+ mActivity.containsShowWhenLockedWindow() || mActivity.containsDismissKeyguardWindow());
}
@Test
@@ -297,11 +298,11 @@ public class AppWindowTokenTests extends WindowTestsBase {
"closingWindow");
closingWindow.mAnimatingExit = true;
closingWindow.mRemoveOnExit = true;
- closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
+ closingWindow.mActivityRecord.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
true /* performLayout */, false /* isVoiceInteraction */);
// We pretended that we were running an exit animation, but that should have been cleared up
- // by changing visibility of AppWindowToken
+ // by changing visibility of ActivityRecord
closingWindow.removeIfPossible();
assertTrue(closingWindow.mRemoved);
}
@@ -309,55 +310,28 @@ public class AppWindowTokenTests extends WindowTestsBase {
@Test
public void testSetOrientation() {
// Assert orientation is unspecified to start.
- assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mToken.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mActivity.getOrientation());
- mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
+ mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mActivity.getOrientation());
- mDisplayContent.removeAppToken(mToken.token);
+ mDisplayContent.removeAppToken(mActivity.token);
// Assert orientation is unset to after container is removed.
- assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_UNSET, mActivity.getOrientation());
// Reset display frozen state
mWm.mDisplayFrozen = false;
}
@Test
- public void testReportOrientationChangeOnVisibilityChange() {
- synchronized (mWm.mGlobalLock) {
- mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ public void testReportOrientationChange() {
+ mActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- mDisplayContent.getDisplayRotation().setFixedToUserRotation(
- DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED);
+ mDisplayContent.getDisplayRotation().setFixedToUserRotation(
+ DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED);
- doReturn(Configuration.ORIENTATION_LANDSCAPE).when(mToken.mActivityRecord)
- .getRequestedConfigurationOrientation();
-
- mTask.mTaskRecord = Mockito.mock(TaskRecord.class, RETURNS_DEEP_STUBS);
- mToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
- true /* performLayout */, false /* isVoiceInteraction */);
- }
-
- verify(mTask.mTaskRecord).onConfigurationChanged(any(Configuration.class));
- }
-
- @Test
- public void testReportOrientationChangeOnOpeningClosingAppChange() {
- synchronized (mWm.mGlobalLock) {
- mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
-
- mDisplayContent.getDisplayRotation().setFixedToUserRotation(
- DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED);
- mDisplayContent.getDisplayInfo().state = Display.STATE_ON;
- mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_CLOSE,
- false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */);
-
- doReturn(Configuration.ORIENTATION_LANDSCAPE).when(mToken.mActivityRecord)
- .getRequestedConfigurationOrientation();
-
- mTask.mTaskRecord = Mockito.mock(TaskRecord.class, RETURNS_DEEP_STUBS);
- mToken.setVisibility(false, false);
- }
+ mTask.mTaskRecord = Mockito.mock(TaskRecord.class, RETURNS_DEEP_STUBS);
+ mActivity.reportDescendantOrientationChangeIfNeeded();
verify(mTask.mTaskRecord).onConfigurationChanged(any(Configuration.class));
}
@@ -365,14 +339,14 @@ public class AppWindowTokenTests extends WindowTestsBase {
@Test
@FlakyTest(bugId = 131176283)
public void testCreateRemoveStartingWindow() {
- mToken.addStartingWindow(mPackageName,
+ mActivity.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
false, false);
waitUntilHandlersIdle();
- assertHasStartingWindow(mToken);
- mToken.removeStartingWindow();
+ assertHasStartingWindow(mActivity);
+ mActivity.removeStartingWindow();
waitUntilHandlersIdle();
- assertNoStartingWindow(mToken);
+ assertNoStartingWindow(mActivity);
}
@Test
@@ -380,7 +354,7 @@ public class AppWindowTokenTests extends WindowTestsBase {
public void testAddRemoveRace() {
// There was once a race condition between adding and removing starting windows
for (int i = 0; i < 1000; i++) {
- final WindowTestUtils.TestAppWindowToken appToken = createIsolatedTestAppWindowToken();
+ final ActivityRecord appToken = createIsolatedTestActivityRecord();
appToken.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
@@ -395,76 +369,76 @@ public class AppWindowTokenTests extends WindowTestsBase {
@Test
public void testTransferStartingWindow() {
- final WindowTestUtils.TestAppWindowToken token1 = createIsolatedTestAppWindowToken();
- final WindowTestUtils.TestAppWindowToken token2 = createIsolatedTestAppWindowToken();
- token1.addStartingWindow(mPackageName,
+ final ActivityRecord activity1 = createIsolatedTestActivityRecord();
+ final ActivityRecord activity2 = createIsolatedTestActivityRecord();
+ activity1.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
false, false);
waitUntilHandlersIdle();
- token2.addStartingWindow(mPackageName,
- android.R.style.Theme, null, "Test", 0, 0, 0, 0, token1.appToken.asBinder(),
+ activity2.addStartingWindow(mPackageName,
+ android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1.appToken.asBinder(),
true, true, false, true, false, false);
waitUntilHandlersIdle();
- assertNoStartingWindow(token1);
- assertHasStartingWindow(token2);
+ assertNoStartingWindow(activity1);
+ assertHasStartingWindow(activity2);
}
@Test
public void testTransferStartingWindowWhileCreating() {
- final WindowTestUtils.TestAppWindowToken token1 = createIsolatedTestAppWindowToken();
- final WindowTestUtils.TestAppWindowToken token2 = createIsolatedTestAppWindowToken();
- ((TestWindowManagerPolicy) token1.mWmService.mPolicy).setRunnableWhenAddingSplashScreen(
+ final ActivityRecord activity1 = createIsolatedTestActivityRecord();
+ final ActivityRecord activity2 = createIsolatedTestActivityRecord();
+ ((TestWindowManagerPolicy) activity1.mWmService.mPolicy).setRunnableWhenAddingSplashScreen(
() -> {
// Surprise, ...! Transfer window in the middle of the creation flow.
- token2.addStartingWindow(mPackageName,
+ activity2.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0,
- token1.appToken.asBinder(), true, true, false,
+ activity1.appToken.asBinder(), true, true, false,
true, false, false);
});
- token1.addStartingWindow(mPackageName,
+ activity1.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
false, false);
waitUntilHandlersIdle();
- assertNoStartingWindow(token1);
- assertHasStartingWindow(token2);
+ assertNoStartingWindow(activity1);
+ assertHasStartingWindow(activity2);
}
- private WindowTestUtils.TestAppWindowToken createIsolatedTestAppWindowToken() {
+ private ActivityRecord createIsolatedTestActivityRecord() {
final TaskStack taskStack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(taskStack, 0 /* userId */);
- return createTestAppWindowTokenForGivenTask(task);
+ return createTestActivityRecordForGivenTask(task);
}
- private WindowTestUtils.TestAppWindowToken createTestAppWindowTokenForGivenTask(Task task) {
- final WindowTestUtils.TestAppWindowToken appToken =
- WindowTestUtils.createTestAppWindowToken(mDisplayContent);
- task.addChild(appToken, 0);
+ private ActivityRecord createTestActivityRecordForGivenTask(Task task) {
+ final ActivityRecord activity =
+ WindowTestUtils.createTestActivityRecord(mDisplayContent);
+ task.addChild(activity, 0);
waitUntilHandlersIdle();
- return appToken;
+ return activity;
}
@Test
public void testTryTransferStartingWindowFromHiddenAboveToken() {
// Add two tasks on top of each other.
- final WindowTestUtils.TestAppWindowToken tokenTop = createIsolatedTestAppWindowToken();
- final WindowTestUtils.TestAppWindowToken tokenBottom =
- createTestAppWindowTokenForGivenTask(tokenTop.getTask());
+ final ActivityRecord activityTop = createIsolatedTestActivityRecord();
+ final ActivityRecord activityBottom =
+ createTestActivityRecordForGivenTask(activityTop.getTask());
// Add a starting window.
- tokenTop.addStartingWindow(mPackageName,
+ activityTop.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
false, false);
waitUntilHandlersIdle();
// Make the top one invisible, and try transferring the starting window from the top to the
// bottom one.
- tokenTop.setVisibility(false, false);
- tokenBottom.transferStartingWindowFromHiddenAboveTokenIfNeeded();
+ activityTop.setVisibility(false, false);
+ activityBottom.transferStartingWindowFromHiddenAboveTokenIfNeeded();
waitUntilHandlersIdle();
// Assert that the bottom window now has the starting window.
- assertNoStartingWindow(tokenTop);
- assertHasStartingWindow(tokenBottom);
+ assertNoStartingWindow(activityTop);
+ assertHasStartingWindow(activityBottom);
}
@Test
@@ -476,40 +450,40 @@ public class AppWindowTokenTests extends WindowTestsBase {
// Check that anim bounds for freeform window match task bounds
mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
- assertEquals(taskBounds, mToken.getAnimationBounds(STACK_CLIP_NONE));
+ assertEquals(taskBounds, mActivity.getAnimationBounds(STACK_CLIP_NONE));
// STACK_CLIP_AFTER_ANIM should use task bounds since they will be clipped by
// bounds animation layer.
mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- assertEquals(taskBounds, mToken.getAnimationBounds(STACK_CLIP_AFTER_ANIM));
+ assertEquals(taskBounds, mActivity.getAnimationBounds(STACK_CLIP_AFTER_ANIM));
// STACK_CLIP_BEFORE_ANIM should use stack bounds since it won't be clipped later.
mTask.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- assertEquals(stackBounds, mToken.getAnimationBounds(STACK_CLIP_BEFORE_ANIM));
+ assertEquals(stackBounds, mActivity.getAnimationBounds(STACK_CLIP_BEFORE_ANIM));
}
@Test
public void testHasStartingWindow() {
final WindowManager.LayoutParams attrs =
new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING);
- final WindowTestUtils.TestWindowState startingWindow = createWindowState(attrs, mToken);
- mToken.startingDisplayed = true;
- mToken.addWindow(startingWindow);
- assertTrue("Starting window should be present", mToken.hasStartingWindow());
- mToken.startingDisplayed = false;
- assertTrue("Starting window should be present", mToken.hasStartingWindow());
-
- mToken.removeChild(startingWindow);
- assertFalse("Starting window should not be present", mToken.hasStartingWindow());
+ final WindowTestUtils.TestWindowState startingWindow = createWindowState(attrs, mActivity);
+ mActivity.startingDisplayed = true;
+ mActivity.addWindow(startingWindow);
+ assertTrue("Starting window should be present", mActivity.hasStartingWindow());
+ mActivity.startingDisplayed = false;
+ assertTrue("Starting window should be present", mActivity.hasStartingWindow());
+
+ mActivity.removeChild(startingWindow);
+ assertFalse("Starting window should not be present", mActivity.hasStartingWindow());
}
- private void assertHasStartingWindow(AppWindowToken atoken) {
+ private void assertHasStartingWindow(ActivityRecord atoken) {
assertNotNull(atoken.startingSurface);
assertNotNull(atoken.mStartingData);
assertNotNull(atoken.startingWindow);
}
- private void assertNoStartingWindow(AppWindowToken atoken) {
+ private void assertNoStartingWindow(ActivityRecord atoken) {
assertNull(atoken.startingSurface);
assertNull(atoken.startingWindow);
assertNull(atoken.mStartingData);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
index bb574ceed4e0..2e85897b6fee 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
@@ -22,6 +22,8 @@ import static android.app.AppOpsManager.OP_ASSIST_SCREENSHOT;
import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
import static android.graphics.Bitmap.Config.ARGB_8888;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -71,7 +73,7 @@ import java.util.concurrent.TimeUnit;
*/
@MediumTest
@Presubmit
-public class AssistDataRequesterTest extends ActivityTestsBase {
+public class AssistDataRequesterTest {
private static final String TAG = AssistDataRequesterTest.class.getSimpleName();
@@ -111,8 +113,9 @@ public class AssistDataRequesterTest extends ActivityTestsBase {
mHandler = new Handler(Looper.getMainLooper());
mCallbacksLock = new Object();
mCallbacks = new Callbacks();
- mDataRequester = new AssistDataRequester(mContext, mWm, mAppOpsManager, mCallbacks,
- mCallbacksLock, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT);
+ mDataRequester = new AssistDataRequester(getInstrumentation().getTargetContext(), mWm,
+ mAppOpsManager, mCallbacks, mCallbacksLock, OP_ASSIST_STRUCTURE,
+ OP_ASSIST_SCREENSHOT);
mDataRequester.mActivityTaskManager = mAtm;
// Gate the continuation of the assist data callbacks until we are ready within the tests
mGate = new CountDownLatch(1);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index a98f79cb5369..73420a095cad 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -46,7 +46,7 @@ public class DimmerTests extends WindowTestsBase {
private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
final SurfaceControl mControl = mock(SurfaceControl.class);
- final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction mTransaction = spy(StubTransaction.class);
TestWindowContainer(WindowManagerService wm) {
super(wm);
@@ -66,7 +66,7 @@ public class DimmerTests extends WindowTestsBase {
private static class MockSurfaceBuildingContainer extends WindowContainer<TestWindowContainer> {
final SurfaceSession mSession = new SurfaceSession();
final SurfaceControl mHostControl = mock(SurfaceControl.class);
- final SurfaceControl.Transaction mHostTransaction = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction mHostTransaction = spy(StubTransaction.class);
MockSurfaceBuildingContainer(WindowManagerService wm) {
super(wm);
@@ -118,7 +118,7 @@ public class DimmerTests extends WindowTestsBase {
public void setUp() throws Exception {
mHost = new MockSurfaceBuildingContainer(mWm);
mSurfaceAnimatorStarter = spy(new SurfaceAnimatorStarterImpl());
- mTransaction = mock(SurfaceControl.Transaction.class);
+ mTransaction = spy(StubTransaction.class);
mDimmer = new Dimmer(mHost, mSurfaceAnimatorStarter);
}
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 44f3ee4130e0..01489c6c8c1b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -39,6 +39,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
@@ -46,8 +47,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.same;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -72,7 +73,6 @@ import android.metrics.LogMaker;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.util.DisplayMetrics;
-import android.util.MutableBoolean;
import android.view.DisplayCutout;
import android.view.Gravity;
import android.view.ISystemGestureExclusionListener;
@@ -88,6 +88,7 @@ import com.android.internal.logging.nano.MetricsProto;
import com.android.server.wm.utils.WmDisplayCutout;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
@@ -105,19 +106,20 @@ import java.util.List;
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class DisplayContentTests extends WindowTestsBase {
@Test
public void testForAllWindows() {
final WindowState exitingAppWindow = createWindow(null, TYPE_BASE_APPLICATION,
mDisplayContent, "exiting app");
- final AppWindowToken exitingAppToken = exitingAppWindow.mAppToken;
+ final ActivityRecord exitingApp = exitingAppWindow.mActivityRecord;
// Wait until everything in animation handler get executed to prevent the exiting window
// from being removed during WindowSurfacePlacer Traversal.
waitUntilHandlersIdle();
- exitingAppToken.mIsExiting = true;
- exitingAppToken.getTask().mStack.mExitingAppTokens.add(exitingAppToken);
+ exitingApp.mIsExiting = true;
+ exitingApp.getTask().mStack.mExitingActivities.add(exitingApp);
assertForAllWindowsOrder(Arrays.asList(
mWallpaperWindow,
@@ -239,11 +241,10 @@ public class DisplayContentTests extends WindowTestsBase {
assertEquals(dc, stack.getDisplayContent());
final Task task = createTaskInStack(stack, 0 /* userId */);
- final WindowTestUtils.TestAppWindowToken token = WindowTestUtils.createTestAppWindowToken(
- dc);
- task.addChild(token, 0);
+ final ActivityRecord activity = WindowTestUtils.createTestActivityRecord(dc);
+ task.addChild(activity, 0);
assertEquals(dc, task.getDisplayContent());
- assertEquals(dc, token.getDisplayContent());
+ assertEquals(dc, activity.getDisplayContent());
// Move stack to first display.
mDisplayContent.moveStackToDisplay(stack, true /* onTop */);
@@ -251,7 +252,7 @@ public class DisplayContentTests extends WindowTestsBase {
assertEquals(mDisplayContent, stack.getParent().getParent());
assertEquals(mDisplayContent, stack.getDisplayContent());
assertEquals(mDisplayContent, task.getDisplayContent());
- assertEquals(mDisplayContent, token.getDisplayContent());
+ assertEquals(mDisplayContent, activity.getDisplayContent());
}
/**
@@ -312,17 +313,17 @@ public class DisplayContentTests extends WindowTestsBase {
// Add stack with activity.
final TaskStack stack0 = createTaskStackOnDisplay(dc0);
final Task task0 = createTaskInStack(stack0, 0 /* userId */);
- final WindowTestUtils.TestAppWindowToken token =
- WindowTestUtils.createTestAppWindowToken(dc0);
- task0.addChild(token, 0);
+ final ActivityRecord activity =
+ WindowTestUtils.createTestActivityRecord(dc0);
+ task0.addChild(activity, 0);
dc0.configureDisplayPolicy();
assertNotNull(dc0.mTapDetector);
final TaskStack stack1 = createTaskStackOnDisplay(dc1);
final Task task1 = createTaskInStack(stack1, 0 /* userId */);
- final WindowTestUtils.TestAppWindowToken token1 =
- WindowTestUtils.createTestAppWindowToken(dc0);
- task1.addChild(token1, 0);
+ final ActivityRecord activity1 =
+ WindowTestUtils.createTestActivityRecord(dc0);
+ task1.addChild(activity1, 0);
dc1.configureDisplayPolicy();
assertNotNull(dc1.mTapDetector);
@@ -361,7 +362,7 @@ public class DisplayContentTests extends WindowTestsBase {
// Create a focusable window and check that focus is calculated correctly
final WindowState window1 =
createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "window1");
- window1.mAppToken.mTargetSdk = targetSdk;
+ window1.mActivityRecord.mTargetSdk = targetSdk;
updateFocusedWindow();
assertTrue(window1.isFocused());
assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
@@ -374,7 +375,7 @@ public class DisplayContentTests extends WindowTestsBase {
// Add a window to the second display, and it should be focused
final WindowState window2 = createWindow(null, TYPE_BASE_APPLICATION, dc, "window2");
- window2.mAppToken.mTargetSdk = targetSdk;
+ window2.mActivityRecord.mTargetSdk = targetSdk;
updateFocusedWindow();
assertTrue(window2.isFocused());
assertEquals(perDisplayFocusEnabled && targetSdk >= Q, window1.isFocused());
@@ -388,14 +389,67 @@ public class DisplayContentTests extends WindowTestsBase {
assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
// Make sure top focused display not changed if there is a focused app.
- window1.mAppToken.hiddenRequested = true;
- window1.getDisplayContent().setFocusedApp(window1.mAppToken);
+ window1.mActivityRecord.hiddenRequested = true;
+ window1.getDisplayContent().setFocusedApp(window1.mActivityRecord);
updateFocusedWindow();
assertTrue(!window1.isFocused());
assertEquals(window1.getDisplayId(),
mWm.mRoot.getTopFocusedDisplayContent().getDisplayId());
}
+ @Test
+ public void testShouldWaitForSystemDecorWindowsOnBoot_OnDefaultDisplay() {
+ mWm.mSystemBooted = true;
+ final DisplayContent defaultDisplay = mWm.getDefaultDisplayContentLocked();
+ final WindowState[] windows = createNotDrawnWindowsOn(defaultDisplay,
+ TYPE_WALLPAPER, TYPE_APPLICATION);
+
+ // Verify waiting for windows to be drawn.
+ assertTrue(defaultDisplay.shouldWaitForSystemDecorWindowsOnBoot());
+
+ // Verify not waiting for drawn windows.
+ makeWindowsDrawn(windows);
+ assertFalse(defaultDisplay.shouldWaitForSystemDecorWindowsOnBoot());
+ }
+
+ @Test
+ public void testShouldWaitForSystemDecorWindowsOnBoot_OnSecondaryDisplay() {
+ mWm.mSystemBooted = true;
+ final DisplayContent secondaryDisplay = createNewDisplay();
+ final WindowState[] windows = createNotDrawnWindowsOn(secondaryDisplay,
+ TYPE_WALLPAPER, TYPE_APPLICATION);
+
+ // Verify not waiting for display without system decorations.
+ doReturn(false).when(secondaryDisplay).supportsSystemDecorations();
+ assertFalse(secondaryDisplay.shouldWaitForSystemDecorWindowsOnBoot());
+
+ // Verify waiting for non-drawn windows on display with system decorations.
+ reset(secondaryDisplay);
+ doReturn(true).when(secondaryDisplay).supportsSystemDecorations();
+ assertTrue(secondaryDisplay.shouldWaitForSystemDecorWindowsOnBoot());
+
+ // Verify not waiting for drawn windows on display with system decorations.
+ makeWindowsDrawn(windows);
+ assertFalse(secondaryDisplay.shouldWaitForSystemDecorWindowsOnBoot());
+ }
+
+ private WindowState[] createNotDrawnWindowsOn(DisplayContent displayContent, int... types) {
+ final WindowState[] windows = new WindowState[types.length];
+ for (int i = 0; i < types.length; i++) {
+ final int type = types[i];
+ windows[i] = createWindow(null /* parent */, type, displayContent, "window-" + type);
+ windows[i].mHasSurface = false;
+ }
+ return windows;
+ }
+
+ private static void makeWindowsDrawn(WindowState[] windows) {
+ for (WindowState window : windows) {
+ window.mHasSurface = true;
+ window.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
+ }
+ }
+
/**
* This tests setting the maximum ui width on a display.
*/
@@ -435,78 +489,70 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testDisplayCutout_rot0() {
- synchronized (mWm.getWindowManagerLock()) {
- final DisplayContent dc = createNewDisplay();
- dc.mInitialDisplayWidth = 200;
- dc.mInitialDisplayHeight = 400;
- Rect r = new Rect(80, 0, 120, 10);
- final DisplayCutout cutout = new WmDisplayCutout(
- fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_TOP), null)
- .computeSafeInsets(200, 400).getDisplayCutout();
-
- dc.mInitialDisplayCutout = cutout;
- dc.getDisplayRotation().setRotation(Surface.ROTATION_0);
- dc.computeScreenConfiguration(new Configuration()); // recomputes dc.mDisplayInfo.
-
- assertEquals(cutout, dc.getDisplayInfo().displayCutout);
- }
+ final DisplayContent dc = createNewDisplay();
+ dc.mInitialDisplayWidth = 200;
+ dc.mInitialDisplayHeight = 400;
+ final Rect r = new Rect(80, 0, 120, 10);
+ final DisplayCutout cutout = new WmDisplayCutout(
+ fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_TOP), null)
+ .computeSafeInsets(200, 400).getDisplayCutout();
+
+ dc.mInitialDisplayCutout = cutout;
+ dc.getDisplayRotation().setRotation(Surface.ROTATION_0);
+ dc.computeScreenConfiguration(new Configuration()); // recomputes dc.mDisplayInfo.
+
+ assertEquals(cutout, dc.getDisplayInfo().displayCutout);
}
@Test
public void testDisplayCutout_rot90() {
- synchronized (mWm.getWindowManagerLock()) {
- // Prevent mInitialDisplayCutout from being updated from real display (e.g. null
- // if the device has no cutout).
- final DisplayContent dc = createDisplayNoUpdateDisplayInfo();
- // Rotation may use real display info to compute bound, so here also uses the
- // same width and height.
- final int displayWidth = dc.mInitialDisplayWidth;
- final int displayHeight = dc.mInitialDisplayHeight;
- final int cutoutWidth = 40;
- final int cutoutHeight = 10;
- final int left = (displayWidth - cutoutWidth) / 2;
- final int top = 0;
- final int right = (displayWidth + cutoutWidth) / 2;
- final int bottom = cutoutHeight;
-
- final Rect r1 = new Rect(left, top, right, bottom);
- final DisplayCutout cutout = new WmDisplayCutout(
- fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom, BOUNDS_POSITION_TOP),
- null)
- .computeSafeInsets(displayWidth, displayHeight).getDisplayCutout();
-
- dc.mInitialDisplayCutout = cutout;
- dc.getDisplayRotation().setRotation(Surface.ROTATION_90);
- dc.computeScreenConfiguration(new Configuration()); // recomputes dc.mDisplayInfo.
-
- // ----o---------- -------------
- // | | | | |
- // | ------o | o---
- // | | | |
- // | | -> | |
- // | | ---o
- // | | |
- // | | -------------
- final Rect r = new Rect(top, left, bottom, right);
- assertEquals(new WmDisplayCutout(
- fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_LEFT), null)
- .computeSafeInsets(displayHeight, displayWidth)
- .getDisplayCutout(), dc.getDisplayInfo().displayCutout);
- }
+ // Prevent mInitialDisplayCutout from being updated from real display (e.g. null
+ // if the device has no cutout).
+ final DisplayContent dc = createDisplayNoUpdateDisplayInfo();
+ // Rotation may use real display info to compute bound, so here also uses the
+ // same width and height.
+ final int displayWidth = dc.mInitialDisplayWidth;
+ final int displayHeight = dc.mInitialDisplayHeight;
+ final int cutoutWidth = 40;
+ final int cutoutHeight = 10;
+ final int left = (displayWidth - cutoutWidth) / 2;
+ final int top = 0;
+ final int right = (displayWidth + cutoutWidth) / 2;
+ final int bottom = cutoutHeight;
+
+ final Rect r1 = new Rect(left, top, right, bottom);
+ final DisplayCutout cutout = new WmDisplayCutout(
+ fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom, BOUNDS_POSITION_TOP), null)
+ .computeSafeInsets(displayWidth, displayHeight).getDisplayCutout();
+
+ dc.mInitialDisplayCutout = cutout;
+ dc.getDisplayRotation().setRotation(Surface.ROTATION_90);
+ dc.computeScreenConfiguration(new Configuration()); // recomputes dc.mDisplayInfo.
+
+ // ----o---------- -------------
+ // | | | | |
+ // | ------o | o---
+ // | | | |
+ // | | -> | |
+ // | | ---o
+ // | | |
+ // | | -------------
+ final Rect r = new Rect(top, left, bottom, right);
+ assertEquals(new WmDisplayCutout(
+ fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_LEFT), null)
+ .computeSafeInsets(displayHeight, displayWidth).getDisplayCutout(),
+ dc.getDisplayInfo().displayCutout);
}
@Test
public void testLayoutSeq_assignedDuringLayout() {
- synchronized (mWm.getWindowManagerLock()) {
-
- final DisplayContent dc = createNewDisplay();
- final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
+ final DisplayContent dc = createNewDisplay();
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
- dc.setLayoutNeeded();
- dc.performLayout(true /* initial */, false /* updateImeWindows */);
+ dc.setLayoutNeeded();
+ dc.performLayout(true /* initial */, false /* updateImeWindows */);
- assertThat(win.mLayoutSeq, is(dc.mLayoutSeq));
- }
+ assertThat(win.mLayoutSeq, is(dc.mLayoutSeq));
}
@Test
@@ -523,7 +569,7 @@ public class DisplayContentTests extends WindowTestsBase {
// Create a window that requests landscape orientation. It will define device orientation
// by default.
final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
- window.mAppToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ window.mActivityRecord.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
final WindowState keyguard = createWindow(null, TYPE_STATUS_BAR, dc, "keyguard");
keyguard.mHasSurface = true;
@@ -636,16 +682,15 @@ public class DisplayContentTests extends WindowTestsBase {
// is appWin & null on the other display.
mDisplayContent.setInputMethodWindowLocked(mImeWindow);
newDisplay.setInputMethodWindowLocked(null);
- assertTrue("appWin should be IME target window",
- appWin.equals(mDisplayContent.mInputMethodTarget));
+ assertEquals("appWin should be IME target window",
+ appWin, mDisplayContent.mInputMethodTarget);
assertNull("newDisplay Ime target: ", newDisplay.mInputMethodTarget);
// Switch input method window on new display & make sure the input method target also
// switched as expected.
newDisplay.setInputMethodWindowLocked(mImeWindow);
mDisplayContent.setInputMethodWindowLocked(null);
- assertTrue("appWin1 should be IME target window",
- appWin1.equals(newDisplay.mInputMethodTarget));
+ assertEquals("appWin1 should be IME target window", appWin1, newDisplay.mInputMethodTarget);
assertNull("default display Ime target: ", mDisplayContent.mInputMethodTarget);
}
@@ -660,13 +705,13 @@ public class DisplayContentTests extends WindowTestsBase {
final ActivityStack stack =
new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootActivityContainer)
- .setDisplay(dc.mAcitvityDisplay).build();
+ .setDisplay(dc.mActivityDisplay).build();
final ActivityRecord activity = stack.topTask().getTopActivity();
activity.setRequestedOrientation(newOrientation);
final ArgumentCaptor<Configuration> captor = ArgumentCaptor.forClass(Configuration.class);
- verify(dc.mAcitvityDisplay).updateDisplayOverrideConfigurationLocked(captor.capture(),
+ verify(dc.mActivityDisplay).updateDisplayOverrideConfigurationLocked(captor.capture(),
same(activity), anyBoolean(), same(null));
final Configuration newDisplayConfig = captor.getValue();
final int expectedOrientation = newOrientation == SCREEN_ORIENTATION_PORTRAIT
@@ -686,12 +731,12 @@ public class DisplayContentTests extends WindowTestsBase {
final ActivityStack stack =
new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootActivityContainer)
- .setDisplay(dc.mAcitvityDisplay).build();
+ .setDisplay(dc.mActivityDisplay).build();
final ActivityRecord activity = stack.topTask().getTopActivity();
activity.setRequestedOrientation(newOrientation);
- verify(dc.mAcitvityDisplay, never()).updateDisplayOverrideConfigurationLocked(any(),
+ verify(dc.mActivityDisplay, never()).updateDisplayOverrideConfigurationLocked(any(),
eq(activity), anyBoolean(), same(null));
assertEquals(dc.getDisplayRotation().getUserRotation(), dc.getRotation());
}
@@ -702,7 +747,7 @@ public class DisplayContentTests extends WindowTestsBase {
new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) {
final DisplayContent dc = createNewDisplay();
dc.mInputMethodTarget = createWindow(null, TYPE_BASE_APPLICATION, "app");
- assertEquals(dc.mInputMethodTarget.mAppToken.getSurfaceControl(),
+ assertEquals(dc.mInputMethodTarget.mActivityRecord.getSurfaceControl(),
dc.computeImeParent());
}
}
@@ -721,8 +766,8 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testComputeImeParent_app_notMatchParentBounds() {
- spyOn(mAppWindow.mAppToken);
- doReturn(false).when(mAppWindow.mAppToken).matchParentBounds();
+ spyOn(mAppWindow.mActivityRecord);
+ doReturn(false).when(mAppWindow.mActivityRecord).matchParentBounds();
mDisplayContent.mInputMethodTarget = mAppWindow;
// The surface parent of IME should be the display instead of app window.
assertEquals(mDisplayContent.getWindowingLayer(), mDisplayContent.computeImeParent());
@@ -751,7 +796,7 @@ public class DisplayContentTests extends WindowTestsBase {
win.setHasSurface(true);
dc.updateSystemGestureExclusion();
- final MutableBoolean invoked = new MutableBoolean(false);
+ final boolean[] invoked = { false };
final ISystemGestureExclusionListener.Stub verifier =
new ISystemGestureExclusionListener.Stub() {
@Override
@@ -760,7 +805,7 @@ public class DisplayContentTests extends WindowTestsBase {
Region expected = Region.obtain();
expected.set(10, 20, 30, 40);
assertEquals(expected, actual);
- invoked.value = true;
+ invoked[0] = true;
}
};
try {
@@ -768,7 +813,7 @@ public class DisplayContentTests extends WindowTestsBase {
} finally {
dc.unregisterSystemGestureExclusionListener(verifier);
}
- assertTrue("SystemGestureExclusionListener was not invoked", invoked.value);
+ assertTrue("SystemGestureExclusionListener was not invoked", invoked[0]);
}
@Test
@@ -826,30 +871,28 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testCalculateSystemGestureExclusion_immersiveStickyLegacyWindow() throws Exception {
- synchronized (mWm.mGlobalLock) {
- mWm.mSystemGestureExcludedByPreQStickyImmersive = true;
+ mWm.mSystemGestureExcludedByPreQStickyImmersive = true;
- final DisplayContent dc = createNewDisplay();
- final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
- win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
- win.getAttrs().layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
- win.getAttrs().privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
- win.getAttrs().subtreeSystemUiVisibility = win.mSystemUiVisibility =
- SYSTEM_UI_FLAG_FULLSCREEN | SYSTEM_UI_FLAG_HIDE_NAVIGATION
- | SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
- win.mAppToken.mTargetSdk = P;
+ final DisplayContent dc = createNewDisplay();
+ final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
+ win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
+ win.getAttrs().layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+ win.getAttrs().privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
+ win.getAttrs().subtreeSystemUiVisibility = win.mSystemUiVisibility =
+ SYSTEM_UI_FLAG_FULLSCREEN | SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+ win.mActivityRecord.mTargetSdk = P;
- dc.setLayoutNeeded();
- dc.performLayout(true /* initial */, false /* updateImeWindows */);
+ dc.setLayoutNeeded();
+ dc.performLayout(true /* initial */, false /* updateImeWindows */);
- win.setHasSurface(true);
+ win.setHasSurface(true);
- final Region expected = Region.obtain();
- expected.set(dc.getBounds());
- assertEquals(expected, calculateSystemGestureExclusion(dc));
+ final Region expected = Region.obtain();
+ expected.set(dc.getBounds());
+ assertEquals(expected, calculateSystemGestureExclusion(dc));
- win.setHasSurface(false);
- }
+ win.setHasSurface(false);
}
@Test
@@ -860,7 +903,7 @@ public class DisplayContentTests extends WindowTestsBase {
Configuration newConfig = new Configuration();
newConfig.orientation = Configuration.ORIENTATION_PORTRAIT;
- final DisplayContent displayContent = spy(createNewDisplay());
+ final DisplayContent displayContent = createNewDisplay();
Mockito.doReturn(mockLogger).when(displayContent).getMetricsLogger();
Mockito.doReturn(oldConfig).doReturn(newConfig).when(displayContent).getConfiguration();
@@ -886,9 +929,7 @@ public class DisplayContentTests extends WindowTestsBase {
}
private void updateFocusedWindow() {
- synchronized (mWm.mGlobalLock) {
- mWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false);
- }
+ mWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateInputWindows */);
}
/**
@@ -896,7 +937,7 @@ public class DisplayContentTests extends WindowTestsBase {
* the values set by test.
*/
private DisplayContent createDisplayNoUpdateDisplayInfo() {
- final DisplayContent displayContent = spy(createNewDisplay());
+ final DisplayContent displayContent = createNewDisplay();
doNothing().when(displayContent).updateDisplayInfo();
return displayContent;
}
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 de184b84857b..2a3731a2e483 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -63,9 +63,11 @@ import com.android.server.wm.utils.WmDisplayCutout;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
private DisplayFrames mFrames;
@@ -118,458 +120,415 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
@Test
public void layoutWindowLw_appDrawsBars() {
- synchronized (mWm.mGlobalLock) {
- mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
- assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
- }
+ mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+ assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
}
@Test
public void layoutWindowLw_appWontDrawBars() {
- synchronized (mWm.mGlobalLock) {
- mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
- }
+ mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
}
@Test
public void layoutWindowLw_appWontDrawBars_forceStatusAndNav() throws Exception {
- synchronized (mWm.mGlobalLock) {
- mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
- }
+ mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
}
@Test
public void layoutWindowLw_keyguardDialog_hideNav() {
- synchronized (mWm.mGlobalLock) {
- mWindow.mAttrs.type = TYPE_KEYGUARD_DIALOG;
- mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- mWindow.mAttrs.systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* uiMode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null /* attached */, mFrames);
-
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
- }
+ mWindow.mAttrs.type = TYPE_KEYGUARD_DIALOG;
+ mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ mWindow.mAttrs.systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* uiMode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null /* attached */, mFrames);
+
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
}
@Test
public void layoutWindowLw_withDisplayCutout() {
- synchronized (mWm.mGlobalLock) {
- addDisplayCutout();
+ addDisplayCutout();
- addWindow(mWindow);
+ addWindow(mWindow);
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
- }
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
}
@Test
public void layoutWindowLw_withDisplayCutout_never() {
- synchronized (mWm.mGlobalLock) {
- addDisplayCutout();
+ addDisplayCutout();
- mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
- addWindow(mWindow);
+ mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+ addWindow(mWindow);
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
- assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
- }
+ assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
}
@Test
public void layoutWindowLw_withDisplayCutout_layoutFullscreen() {
- synchronized (mWm.mGlobalLock) {
- addDisplayCutout();
+ addDisplayCutout();
- mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- addWindow(mWindow);
+ mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+ addWindow(mWindow);
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
- assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
- }
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+ assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
}
@Test
public void layoutWindowLw_withDisplayCutout_fullscreen() {
- synchronized (mWm.mGlobalLock) {
- addDisplayCutout();
+ addDisplayCutout();
- mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
- addWindow(mWindow);
+ mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
+ addWindow(mWindow);
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
- assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
- }
+ assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
}
@Test
public void layoutWindowLw_withDisplayCutout_fullscreenInCutout() {
- synchronized (mWm.mGlobalLock) {
- addDisplayCutout();
-
- mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
- mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
- }
+ addDisplayCutout();
+
+ mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
+ mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+ assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
}
@Test
public void layoutWindowLw_withDisplayCutout_landscape() {
- synchronized (mWm.mGlobalLock) {
- addDisplayCutout();
- setRotation(ROTATION_90);
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
- assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mWindow.getContentFrameLw(),
- DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
- assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
- }
+ addDisplayCutout();
+ setRotation(ROTATION_90);
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+ assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mWindow.getContentFrameLw(),
+ DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+ assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
}
@Test
public void layoutWindowLw_withDisplayCutout_seascape() {
- synchronized (mWm.mGlobalLock) {
- addDisplayCutout();
- setRotation(ROTATION_270);
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetBy(mWindow.getParentFrame(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
- assertInsetBy(mWindow.getStableFrameLw(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
- assertInsetBy(mWindow.getContentFrameLw(),
- NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
- assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
- assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
- }
+ addDisplayCutout();
+ setRotation(ROTATION_270);
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetBy(mWindow.getParentFrame(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
+ assertInsetBy(mWindow.getStableFrameLw(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
+ assertInsetBy(mWindow.getContentFrameLw(),
+ NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
+ assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+ assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
}
@Test
public void layoutWindowLw_withDisplayCutout_fullscreen_landscape() {
- synchronized (mWm.mGlobalLock) {
- addDisplayCutout();
- setRotation(ROTATION_90);
-
- mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
- assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mWindow.getContentFrameLw(),
- DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
- }
+ addDisplayCutout();
+ setRotation(ROTATION_90);
+
+ mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+ assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mWindow.getContentFrameLw(),
+ DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
}
@Test
public void layoutWindowLw_withDisplayCutout_floatingInScreen() {
- synchronized (mWm.mGlobalLock) {
- addDisplayCutout();
+ addDisplayCutout();
- mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN;
- mWindow.mAttrs.type = TYPE_APPLICATION_OVERLAY;
- mWindow.mAttrs.width = DISPLAY_WIDTH;
- mWindow.mAttrs.height = DISPLAY_HEIGHT;
- addWindow(mWindow);
+ mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN;
+ mWindow.mAttrs.type = TYPE_APPLICATION_OVERLAY;
+ mWindow.mAttrs.width = DISPLAY_WIDTH;
+ mWindow.mAttrs.height = DISPLAY_HEIGHT;
+ addWindow(mWindow);
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- }
+ assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
}
@Test
public void layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape() {
- synchronized (mWm.mGlobalLock) {
- addDisplayCutout();
- setRotation(ROTATION_90);
-
- mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
- assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mWindow.getContentFrameLw(),
- DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
- assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
- }
+ addDisplayCutout();
+ setRotation(ROTATION_90);
+
+ mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+ mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ addWindow(mWindow);
+
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
+ assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mWindow.getContentFrameLw(),
+ DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+ assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
}
@Test
public void layoutWindowLw_withForwardInset_SoftInputAdjustResize() {
- synchronized (mWm.mGlobalLock) {
- mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_RESIZE;
- addWindow(mWindow);
-
- final int forwardedInsetBottom = 50;
- mDisplayPolicy.setForwardedInsets(Insets.of(0, 0, 0, forwardedInsetBottom));
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(),
- STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT + forwardedInsetBottom);
- assertInsetByTopBottom(mWindow.getVisibleFrameLw(),
- STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT + forwardedInsetBottom);
- assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
- assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
- }
+ mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_RESIZE;
+ addWindow(mWindow);
+
+ final int forwardedInsetBottom = 50;
+ mDisplayPolicy.setForwardedInsets(Insets.of(0, 0, 0, forwardedInsetBottom));
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(),
+ STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT + forwardedInsetBottom);
+ assertInsetByTopBottom(mWindow.getVisibleFrameLw(),
+ STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT + forwardedInsetBottom);
+ assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+ assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
}
@Test
public void layoutWindowLw_withForwardInset_SoftInputAdjustNothing() {
- synchronized (mWm.mGlobalLock) {
- mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_NOTHING;
- addWindow(mWindow);
-
- final int forwardedInsetBottom = 50;
- mDisplayPolicy.setForwardedInsets(Insets.of(0, 0, 0, forwardedInsetBottom));
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
- assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
- }
+ mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_NOTHING;
+ addWindow(mWindow);
+
+ final int forwardedInsetBottom = 50;
+ mDisplayPolicy.setForwardedInsets(Insets.of(0, 0, 0, forwardedInsetBottom));
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+ assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
+ assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+ assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+ assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
}
@Test
public void layoutHint_appWindow() {
- synchronized (mWm.mGlobalLock) {
- // Initialize DisplayFrames
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-
- final Rect outFrame = new Rect();
- final Rect outContentInsets = new Rect();
- final Rect outStableInsets = new Rect();
- final Rect outOutsets = new Rect();
- final DisplayCutout.ParcelableWrapper outDisplayCutout =
- new DisplayCutout.ParcelableWrapper();
-
- mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, null, mFrames,
- false /* floatingStack */, outFrame, outContentInsets, outStableInsets,
- outOutsets, outDisplayCutout);
-
- assertThat(outFrame, is(mFrames.mUnrestricted));
- assertThat(outContentInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
- assertThat(outStableInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
- assertThat(outOutsets, is(new Rect()));
- assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
- }
+ // Initialize DisplayFrames
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+
+ final Rect outFrame = new Rect();
+ final Rect outContentInsets = new Rect();
+ final Rect outStableInsets = new Rect();
+ final Rect outOutsets = new Rect();
+ final DisplayCutout.ParcelableWrapper outDisplayCutout =
+ new DisplayCutout.ParcelableWrapper();
+
+ mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, null, mFrames,
+ false /* floatingStack */, outFrame, outContentInsets, outStableInsets,
+ outOutsets, outDisplayCutout);
+
+ assertThat(outFrame, is(mFrames.mUnrestricted));
+ assertThat(outContentInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
+ assertThat(outStableInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
+ assertThat(outOutsets, is(new Rect()));
+ assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
}
@Test
public void layoutHint_appWindowInTask() {
- synchronized (mWm.mGlobalLock) {
- // Initialize DisplayFrames
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-
- final Rect taskBounds = new Rect(100, 100, 200, 200);
-
- final Rect outFrame = new Rect();
- final Rect outContentInsets = new Rect();
- final Rect outStableInsets = new Rect();
- final Rect outOutsets = new Rect();
- final DisplayCutout.ParcelableWrapper outDisplayCutout =
- new DisplayCutout.ParcelableWrapper();
-
- mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames,
- false /* floatingStack */, outFrame, outContentInsets, outStableInsets,
- outOutsets, outDisplayCutout);
-
- assertThat(outFrame, is(taskBounds));
- assertThat(outContentInsets, is(new Rect()));
- assertThat(outStableInsets, is(new Rect()));
- assertThat(outOutsets, is(new Rect()));
- assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
- }
+ // Initialize DisplayFrames
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+
+ final Rect taskBounds = new Rect(100, 100, 200, 200);
+
+ final Rect outFrame = new Rect();
+ final Rect outContentInsets = new Rect();
+ final Rect outStableInsets = new Rect();
+ final Rect outOutsets = new Rect();
+ final DisplayCutout.ParcelableWrapper outDisplayCutout =
+ new DisplayCutout.ParcelableWrapper();
+
+ mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames,
+ false /* floatingStack */, outFrame, outContentInsets, outStableInsets,
+ outOutsets, outDisplayCutout);
+
+ assertThat(outFrame, is(taskBounds));
+ assertThat(outContentInsets, is(new Rect()));
+ assertThat(outStableInsets, is(new Rect()));
+ assertThat(outOutsets, is(new Rect()));
+ assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
}
@Test
public void layoutHint_appWindowInTask_outsideContentFrame() {
- synchronized (mWm.mGlobalLock) {
- // Initialize DisplayFrames
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-
- // Task is in the nav bar area (usually does not happen, but this is similar enough to
- // the possible overlap with the IME)
- final Rect taskBounds = new Rect(100, mFrames.mContent.bottom + 1,
- 200, mFrames.mContent.bottom + 10);
-
- final Rect outFrame = new Rect();
- final Rect outContentInsets = new Rect();
- final Rect outStableInsets = new Rect();
- final Rect outOutsets = new Rect();
- final DisplayCutout.ParcelableWrapper outDisplayCutout =
- new DisplayCutout.ParcelableWrapper();
-
- mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames,
- true /* floatingStack */, outFrame, outContentInsets, outStableInsets,
- outOutsets, outDisplayCutout);
-
- assertThat(outFrame, is(taskBounds));
- assertThat(outContentInsets, is(new Rect()));
- assertThat(outStableInsets, is(new Rect()));
- assertThat(outOutsets, is(new Rect()));
- assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
- }
+ // Initialize DisplayFrames
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+
+ // Task is in the nav bar area (usually does not happen, but this is similar enough to
+ // the possible overlap with the IME)
+ final Rect taskBounds = new Rect(100, mFrames.mContent.bottom + 1,
+ 200, mFrames.mContent.bottom + 10);
+
+ final Rect outFrame = new Rect();
+ final Rect outContentInsets = new Rect();
+ final Rect outStableInsets = new Rect();
+ final Rect outOutsets = new Rect();
+ final DisplayCutout.ParcelableWrapper outDisplayCutout =
+ new DisplayCutout.ParcelableWrapper();
+
+ mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames,
+ true /* floatingStack */, outFrame, outContentInsets, outStableInsets,
+ outOutsets, outDisplayCutout);
+
+ assertThat(outFrame, is(taskBounds));
+ assertThat(outContentInsets, is(new Rect()));
+ assertThat(outStableInsets, is(new Rect()));
+ assertThat(outOutsets, is(new Rect()));
+ assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
}
@Test
public void forceShowSystemBars_clearsSystemUIFlags() {
- synchronized (mWm.mGlobalLock) {
- mDisplayPolicy.mLastSystemUiFlags |= SYSTEM_UI_FLAG_FULLSCREEN;
- mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- mWindow.mAttrs.flags |= 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);
- }
+ mDisplayPolicy.mLastSystemUiFlags |= SYSTEM_UI_FLAG_FULLSCREEN;
+ mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+ mWindow.mAttrs.flags |= 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() {
- synchronized (mWm.mGlobalLock) {
- final WindowState decorWindow = createWindow(null, TYPE_APPLICATION_OVERLAY,
- "decorWindow");
- decorWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
- decorWindow.mAttrs.privateFlags |= PRIVATE_FLAG_IS_SCREEN_DECOR;
- addWindow(decorWindow);
- addWindow(mWindow);
-
- // Decor on top
- updateDecorWindow(decorWindow, MATCH_PARENT, DECOR_WINDOW_INSET, TOP);
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), DECOR_WINDOW_INSET, NAV_BAR_HEIGHT);
-
- // Decor on bottom
- updateDecorWindow(decorWindow, MATCH_PARENT, DECOR_WINDOW_INSET, BOTTOM);
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT,
- DECOR_WINDOW_INSET);
-
- // Decor on the left
- updateDecorWindow(decorWindow, DECOR_WINDOW_INSET, MATCH_PARENT, LEFT);
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
- assertInsetBy(mWindow.getContentFrameLw(), DECOR_WINDOW_INSET, STATUS_BAR_HEIGHT, 0,
- NAV_BAR_HEIGHT);
-
- // Decor on the right
- updateDecorWindow(decorWindow, DECOR_WINDOW_INSET, MATCH_PARENT, RIGHT);
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
- assertInsetBy(mWindow.getContentFrameLw(), 0, STATUS_BAR_HEIGHT, DECOR_WINDOW_INSET,
- NAV_BAR_HEIGHT);
-
- // Decor not allowed as inset
- updateDecorWindow(decorWindow, DECOR_WINDOW_INSET, DECOR_WINDOW_INSET, TOP);
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- }
+ final WindowState decorWindow = createWindow(null, TYPE_APPLICATION_OVERLAY, "decorWindow");
+ decorWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
+ decorWindow.mAttrs.privateFlags |= PRIVATE_FLAG_IS_SCREEN_DECOR;
+ addWindow(decorWindow);
+ addWindow(mWindow);
+
+ // Decor on top
+ updateDecorWindow(decorWindow, MATCH_PARENT, DECOR_WINDOW_INSET, TOP);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), DECOR_WINDOW_INSET, NAV_BAR_HEIGHT);
+
+ // Decor on bottom
+ updateDecorWindow(decorWindow, MATCH_PARENT, DECOR_WINDOW_INSET, BOTTOM);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT,
+ DECOR_WINDOW_INSET);
+
+ // Decor on the left
+ updateDecorWindow(decorWindow, DECOR_WINDOW_INSET, MATCH_PARENT, LEFT);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ assertInsetBy(mWindow.getContentFrameLw(), DECOR_WINDOW_INSET, STATUS_BAR_HEIGHT, 0,
+ NAV_BAR_HEIGHT);
+
+ // Decor on the right
+ updateDecorWindow(decorWindow, DECOR_WINDOW_INSET, MATCH_PARENT, RIGHT);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ assertInsetBy(mWindow.getContentFrameLw(), 0, STATUS_BAR_HEIGHT, DECOR_WINDOW_INSET,
+ NAV_BAR_HEIGHT);
+
+ // Decor not allowed as inset
+ updateDecorWindow(decorWindow, DECOR_WINDOW_INSET, DECOR_WINDOW_INSET, TOP);
+ mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+ mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+ assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
}
private void updateDecorWindow(WindowState decorWindow, int width, int height, int gravity) {
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 3ead97795954..e699b526e848 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -54,9 +54,11 @@ import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import org.junit.Test;
+import org.junit.runner.RunWith;
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class DisplayPolicyTests extends WindowTestsBase {
private WindowState createOpaqueFullscreen(boolean hasLightNavBar) {
@@ -225,13 +227,10 @@ public class DisplayPolicyTests extends WindowTestsBase {
final WindowState activity = createApplicationWindow();
final WindowState toast = createToastWindow();
- synchronized (mWm.mGlobalLock) {
- policy.adjustWindowParamsLw(
- toast, toast.mAttrs, 0 /* callingPid */, 0 /* callingUid */);
+ policy.adjustWindowParamsLw(toast, toast.mAttrs, 0 /* callingPid */, 0 /* callingUid */);
- assertTrue(policy.canToastShowWhenLocked(0 /* callingUid */));
- assertNotEquals(0, toast.getAttrs().flags & FLAG_SHOW_WHEN_LOCKED);
- }
+ assertTrue(policy.canToastShowWhenLocked(0 /* callingUid */));
+ assertNotEquals(0, toast.getAttrs().flags & FLAG_SHOW_WHEN_LOCKED);
}
private WindowState createToastWindow() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index 059ff3d70660..bb4d35f177c0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -68,6 +68,7 @@ import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wm.utils.WmDisplayCutout;
import org.junit.After;
+import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -122,6 +123,12 @@ public class DisplayRotationTests {
sMockWm.mPolicy = mock(WindowManagerPolicy.class);
}
+ @AfterClass
+ public static void tearDownOnce() {
+ // Make sure the fake settings are cleared after the last test method.
+ FakeSettingsProvider.clearSettingsProvider();
+ }
+
@Before
public void setUp() {
FakeSettingsProvider.clearSettingsProvider();
@@ -238,18 +245,58 @@ public class DisplayRotationTests {
}
@Test
- public void testReturnsUserRotation_UserRotationLocked_CompatibleAppRequest()
+ public void testReturnsLandscape_UserRotationLockedSeascape_AppRequestsLandscape()
throws Exception {
mBuilder.build();
- configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false /* isCar */,
+ false /* isTv */);
freezeRotation(Surface.ROTATION_180);
- assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
+ assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, Surface.ROTATION_90));
}
@Test
+ public void testReturnsSeascape_UserRotationLockedSeascape_AppRequestsSeascape()
+ throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false /* isCar */,
+ false /* isTv */);
+
+ freezeRotation(Surface.ROTATION_180);
+
+ assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
+ ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE, Surface.ROTATION_90));
+ }
+
+ @Test
+ public void testReturnsPortrait_UserRotationLockedPortrait_AppRequestsPortrait()
+ throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false /* isCar */,
+ false /* isTv */);
+
+ freezeRotation(Surface.ROTATION_270);
+
+ assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
+ ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, Surface.ROTATION_0));
+ }
+
+ @Test
+ public void testReturnsUpsideDown_UserRotationLockedUpsideDown_AppRequestsUpsideDown()
+ throws Exception {
+ mBuilder.build();
+ configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false /* isCar */,
+ false /* isTv */);
+
+ freezeRotation(Surface.ROTATION_90);
+
+ assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
+ ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT, Surface.ROTATION_0));
+ }
+
+ @Test
public void testReturnsSideways_UserRotationLocked_IncompatibleAppRequest()
throws Exception {
mBuilder.build();
@@ -591,7 +638,7 @@ public class DisplayRotationTests {
mBuilder.build();
final WindowState win = mock(WindowState.class);
- win.mAppToken = mock(AppWindowToken.class);
+ win.mActivityRecord = mock(ActivityRecord.class);
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams();
attrs.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
@@ -601,13 +648,13 @@ public class DisplayRotationTests {
mMockDisplayContent.mCurrentFocus = win;
mTarget.mUpsideDownRotation = Surface.ROTATION_180;
- doReturn(true).when(win.mAppToken).matchParentBounds();
+ doReturn(true).when(win.mActivityRecord).matchParentBounds();
// The focused fullscreen opaque window without override bounds should be able to be
// rotated seamlessly.
assertTrue(mTarget.shouldRotateSeamlessly(
Surface.ROTATION_0, Surface.ROTATION_90, false /* forceUpdate */));
- doReturn(false).when(win.mAppToken).matchParentBounds();
+ doReturn(false).when(win.mActivityRecord).matchParentBounds();
// No seamless rotation if the window may be positioned with offset after rotation.
assertFalse(mTarget.shouldRotateSeamlessly(
Surface.ROTATION_0, Surface.ROTATION_90, false /* forceUpdate */));
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 0110e94eb1cd..bfb460740573 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -20,9 +20,10 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
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.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -49,6 +50,7 @@ import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -57,10 +59,11 @@ import java.util.concurrent.TimeUnit;
* Tests for the {@link DragDropController} class.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:DragDropControllerTests
+ * atest WmTests:DragDropControllerTests
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class DragDropControllerTests extends WindowTestsBase {
private static final int TIMEOUT_MS = 3000;
private TestDragDropController mTarget;
@@ -93,15 +96,15 @@ public class DragDropControllerTests extends WindowTestsBase {
* Creates a window state which can be used as a drop target.
*/
private WindowState createDropTargetWindow(String name, int ownerId) {
- final WindowTestUtils.TestAppWindowToken token = WindowTestUtils.createTestAppWindowToken(
+ final ActivityRecord activity = WindowTestUtils.createTestActivityRecord(
mDisplayContent);
final TaskStack stack = createTaskStackOnDisplay(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
final Task task = createTaskInStack(stack, ownerId);
- task.addChild(token, 0);
+ task.addChild(activity, 0);
final WindowState window = createWindow(
- null, TYPE_BASE_APPLICATION, token, name, ownerId, false);
+ null, TYPE_BASE_APPLICATION, activity, name, ownerId, false);
window.mInputChannel = new InputChannel();
window.mHasSurface = true;
return window;
@@ -121,29 +124,25 @@ public class DragDropControllerTests extends WindowTestsBase {
@Before
public void setUp() throws Exception {
mTarget = new TestDragDropController(mWm, mWm.mH.getLooper());
- mDisplayContent = spy(mDisplayContent);
mWindow = createDropTargetWindow("Drag test window", 0);
doReturn(mWindow).when(mDisplayContent).getTouchableWinAtPointLocked(0, 0);
+ when(mWm.mInputManager.transferTouchFocus(any(), any())).thenReturn(true);
- synchronized (mWm.mGlobalLock) {
- mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
- }
+ mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
}
@After
public void tearDown() throws Exception {
final CountDownLatch latch;
- synchronized (mWm.mGlobalLock) {
- if (!mTarget.dragDropActiveLocked()) {
- return;
- }
- if (mToken != null) {
- mTarget.cancelDragAndDrop(mToken, false);
- }
- latch = new CountDownLatch(1);
- mTarget.setOnClosedCallbackLocked(latch::countDown);
+ if (!mTarget.dragDropActiveLocked()) {
+ return;
+ }
+ if (mToken != null) {
+ mTarget.cancelDragAndDrop(mToken, false);
}
- assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ latch = new CountDownLatch(1);
+ mTarget.setOnClosedCallbackLocked(latch::countDown);
+ assertTrue(awaitInWmLock(() -> latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)));
}
@Test
@@ -177,6 +176,7 @@ public class DragDropControllerTests extends WindowTestsBase {
.setFormat(PixelFormat.TRANSLUCENT)
.build();
+ assertTrue(mWm.mInputManager.transferTouchFocus(null, null));
mToken = mTarget.performDrag(
new SurfaceSession(), 0, 0, mWindow.mClient, flag, surface, 0, 0, 0, 0, 0,
data);
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 f3a8e1a0bd5e..8c2ae5a7659a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -19,6 +19,7 @@ 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_SPLIT_SCREEN_PRIMARY;
+import static android.view.InsetsState.TYPE_TOP_BAR;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
@@ -32,7 +33,9 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import android.platform.test.annotations.Presubmit;
+import android.util.IntArray;
import android.view.InsetsSourceControl;
+import android.view.InsetsState;
import android.view.test.InsetsModeSession;
import androidx.test.filters.FlakyTest;
@@ -151,6 +154,91 @@ public class InsetsPolicyTest extends WindowTestsBase {
assertEquals(1, controls.length);
}
+ @Test
+ public void testShowTransientBars_bothCanBeTransient_appGetsBothFakeControls() {
+ addWindow(TYPE_STATUS_BAR, "topBar")
+ .getControllableInsetProvider().getSource().setVisible(false);
+ addWindow(TYPE_NAVIGATION_BAR, "navBar")
+ .getControllableInsetProvider().getSource().setVisible(false);
+ final WindowState app = addWindow(TYPE_APPLICATION, "app");
+
+ final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
+ policy.updateBarControlTarget(app);
+ policy.showTransient(
+ IntArray.wrap(new int[]{TYPE_TOP_BAR, InsetsState.TYPE_NAVIGATION_BAR}));
+ final InsetsSourceControl[] controls =
+ mDisplayContent.getInsetsStateController().getControlsForDispatch(app);
+
+ // The app must get both fake controls.
+ assertEquals(2, controls.length);
+ for (int i = controls.length - 1; i >= 0; i--) {
+ assertNull(controls[i].getLeash());
+ }
+ }
+
+ @Test
+ public void testShowTransientBars_topCanBeTransient_appGetsTopFakeControl() {
+ addWindow(TYPE_STATUS_BAR, "topBar")
+ .getControllableInsetProvider().getSource().setVisible(false);
+ addWindow(TYPE_NAVIGATION_BAR, "navBar")
+ .getControllableInsetProvider().getSource().setVisible(true);
+ final WindowState app = addWindow(TYPE_APPLICATION, "app");
+
+ final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
+ policy.updateBarControlTarget(app);
+ policy.showTransient(
+ IntArray.wrap(new int[]{TYPE_TOP_BAR, InsetsState.TYPE_NAVIGATION_BAR}));
+ final InsetsSourceControl[] controls =
+ mDisplayContent.getInsetsStateController().getControlsForDispatch(app);
+
+ // The app must get the fake control of the top bar, and must get the real control of the
+ // navigation bar.
+ assertEquals(2, controls.length);
+ for (int i = controls.length - 1; i >= 0; i--) {
+ final InsetsSourceControl control = controls[i];
+ if (control.getType() == TYPE_TOP_BAR) {
+ assertNull(controls[i].getLeash());
+ } else {
+ assertNotNull(controls[i].getLeash());
+ }
+ }
+ }
+
+ @Test
+ public void testAbortTransientBars_bothCanBeAborted_appGetsBothRealControls() {
+ addWindow(TYPE_STATUS_BAR, "topBar")
+ .getControllableInsetProvider().getSource().setVisible(false);
+ addWindow(TYPE_NAVIGATION_BAR, "navBar")
+ .getControllableInsetProvider().getSource().setVisible(false);
+ final WindowState app = addWindow(TYPE_APPLICATION, "app");
+
+ final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
+ policy.updateBarControlTarget(app);
+ policy.showTransient(
+ IntArray.wrap(new int[]{TYPE_TOP_BAR, InsetsState.TYPE_NAVIGATION_BAR}));
+ InsetsSourceControl[] controls =
+ mDisplayContent.getInsetsStateController().getControlsForDispatch(app);
+
+ // The app must get both fake controls.
+ assertEquals(2, controls.length);
+ for (int i = controls.length - 1; i >= 0; i--) {
+ assertNull(controls[i].getLeash());
+ }
+
+ final InsetsState state = policy.getInsetsForDispatch(app);
+ state.setSourceVisible(TYPE_TOP_BAR, true);
+ state.setSourceVisible(InsetsState.TYPE_NAVIGATION_BAR, true);
+ policy.onInsetsModified(app, state);
+
+ controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(app);
+
+ // The app must get both real controls.
+ assertEquals(2, controls.length);
+ for (int i = controls.length - 1; i >= 0; i--) {
+ assertNotNull(controls[i].getLeash());
+ }
+ }
+
private WindowState addWindow(int type, String name) {
final WindowState win = createWindow(null, type, name);
mDisplayContent.getDisplayPolicy().addWindowLw(win, win.mAttrs);
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 81ea32bb4f73..011161b4f063 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -66,7 +66,7 @@ public class InsetsStateControllerTest extends WindowTestsBase {
final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
- topBar.setInsetProvider(getController().getSourceProvider(TYPE_TOP_BAR));
+ topBar.setControllableInsetProvider(getController().getSourceProvider(TYPE_TOP_BAR));
assertNotNull(getController().getInsetsForDispatch(app).getSource(TYPE_TOP_BAR));
}
@@ -75,7 +75,7 @@ public class InsetsStateControllerTest extends WindowTestsBase {
final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
.setWindow(topBar, null);
- topBar.setInsetProvider(getController().getSourceProvider(TYPE_TOP_BAR));
+ topBar.setControllableInsetProvider(getController().getSourceProvider(TYPE_TOP_BAR));
final InsetsState state = getController().getInsetsForDispatch(topBar);
for (int i = state.getSourcesCount() - 1; i >= 0; i--) {
final InsetsSource source = state.sourceAt(i);
@@ -101,7 +101,7 @@ public class InsetsStateControllerTest extends WindowTestsBase {
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
getController().getSourceProvider(TYPE_NAVIGATION_BAR).setWindow(navBar, null);
- getController().onBarControlTargetChanged(app, app);
+ getController().onBarControlTargetChanged(app, null, app, null);
InsetsSourceControl[] controls = getController().getControlsForDispatch(app);
assertEquals(2, controls.length);
}
@@ -111,9 +111,9 @@ public class InsetsStateControllerTest extends WindowTestsBase {
final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
- getController().onBarControlTargetChanged(app, null);
+ getController().onBarControlTargetChanged(app, null, null, null);
assertNotNull(getController().getControlsForDispatch(app));
- getController().onBarControlTargetChanged(null, null);
+ getController().onBarControlTargetChanged(null, null, null, null);
assertNull(getController().getControlsForDispatch(app));
}
@@ -123,7 +123,7 @@ public class InsetsStateControllerTest extends WindowTestsBase {
final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
- getController().onBarControlTargetChanged(app, null);
+ getController().onBarControlTargetChanged(app, null, null, null);
assertNotNull(getController().getControlsForDispatch(app));
topBar.cancelAnimation();
assertNull(getController().getControlsForDispatch(app));
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 be2ee2909a22..46435ebd8e15 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
@@ -422,7 +422,7 @@ public class LaunchParamsControllerTests extends ActivityTestsBase {
@Override
void saveTask(TaskRecord task) {
- final int userId = task.userId;
+ final int userId = task.mUserId;
final ComponentName realActivity = task.realActivity;
mTmpParams.mPreferredDisplayId = task.getStack().mDisplayId;
mTmpParams.mWindowingMode = task.getWindowingMode();
@@ -436,7 +436,7 @@ public class LaunchParamsControllerTests extends ActivityTestsBase {
@Override
void getLaunchParams(TaskRecord task, ActivityRecord activity, LaunchParams params) {
- final int userId = task != null ? task.userId : activity.mUserId;
+ final int userId = task != null ? task.mUserId : activity.mUserId;
final ComponentName name = task != null
? task.realActivity : activity.mActivityComponent;
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 49d778f023e5..b9fef4b008c3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -32,7 +32,6 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.any;
import android.content.ComponentName;
-import android.content.pm.PackageList;
import android.content.pm.PackageManagerInternal;
import android.graphics.Rect;
import android.os.UserHandle;
@@ -43,6 +42,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import com.android.server.LocalServices;
+import com.android.server.pm.PackageList;
import com.android.server.wm.LaunchParamsController.LaunchParams;
import org.junit.Before;
@@ -115,16 +115,16 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase {
ACTIVITY_TYPE_STANDARD, /* onTop */ true);
mTestTask = new TaskBuilder(mSupervisor).setComponent(TEST_COMPONENT).setStack(stack)
.build();
- mTestTask.userId = TEST_USER_ID;
+ mTestTask.mUserId = TEST_USER_ID;
mTestTask.mLastNonFullscreenBounds = TEST_BOUNDS;
mTestTask.hasBeenVisible = true;
mTaskWithDifferentComponent = new TaskBuilder(mSupervisor)
.setComponent(ALTERNATIVE_COMPONENT).build();
- mTaskWithDifferentComponent.userId = TEST_USER_ID;
+ mTaskWithDifferentComponent.mUserId = TEST_USER_ID;
mTaskWithDifferentUser = new TaskBuilder(mSupervisor).setComponent(TEST_COMPONENT).build();
- mTaskWithDifferentUser.userId = ALTERNATIVE_USER_ID;
+ mTaskWithDifferentUser.mUserId = ALTERNATIVE_USER_ID;
mTarget = new LaunchParamsPersister(mPersisterQueue, mSupervisor, mUserFolderGetter);
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 2d0416d6f5bc..15417d73bd02 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -21,6 +21,7 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -48,8 +49,8 @@ public class LetterboxTest {
@Before
public void setUp() throws Exception {
mSurfaces = new SurfaceControlMocker();
- mLetterbox = new Letterbox(mSurfaces, () -> mock(SurfaceControl.Transaction.class));
- mTransaction = mock(SurfaceControl.Transaction.class);
+ mLetterbox = new Letterbox(mSurfaces, StubTransaction::new);
+ mTransaction = spy(StubTransaction.class);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index 47c76fc28d15..05e173c957d3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -707,7 +707,7 @@ public class LockTaskControllerTest {
TaskRecord tr = mock(TaskRecord.class);
tr.mLockTaskAuth = lockTaskAuth;
tr.intent = intent;
- tr.userId = TEST_USER_ID;
+ tr.mUserId = TEST_USER_ID;
return tr;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
deleted file mode 100644
index e9c226340164..000000000000
--- a/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
+++ /dev/null
@@ -1,82 +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;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
-
-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 android.os.RemoteException;
-import android.platform.test.annotations.Presubmit;
-import android.view.IPinnedStackListener;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Build/Install/Run:
- * atest FrameworksServicesTests:PinnedStackControllerTest
- */
-@SmallTest
-@Presubmit
-public class PinnedStackControllerTest extends WindowTestsBase {
-
- private static final int SHELF_HEIGHT = 300;
-
- @Mock private IPinnedStackListener mIPinnedStackListener;
- @Mock private IPinnedStackListener.Stub mIPinnedStackListenerStub;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- when(mIPinnedStackListener.asBinder()).thenReturn(mIPinnedStackListenerStub);
- }
-
- @Test
- public void setShelfHeight_shelfVisibilityChangedTriggered() throws RemoteException {
- mWm.mAtmService.mSupportsPictureInPicture = true;
- mWm.registerPinnedStackListener(DEFAULT_DISPLAY, mIPinnedStackListener);
-
- verify(mIPinnedStackListener).onImeVisibilityChanged(false, 0);
- verify(mIPinnedStackListener).onShelfVisibilityChanged(false, 0);
- verify(mIPinnedStackListener).onMovementBoundsChanged(any(), eq(false),
- eq(false));
- verify(mIPinnedStackListener).onActionsChanged(any());
- verify(mIPinnedStackListener).onMinimizedStateChanged(anyBoolean());
-
- reset(mIPinnedStackListener);
-
- mWm.setShelfHeight(true, SHELF_HEIGHT);
- verify(mIPinnedStackListener).onShelfVisibilityChanged(true, SHELF_HEIGHT);
- verify(mIPinnedStackListener).onMovementBoundsChanged(any(), eq(false),
- eq(true));
- verify(mIPinnedStackListener, never()).onImeVisibilityChanged(anyBoolean(), anyInt());
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java b/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java
new file mode 100644
index 000000000000..d1510cf811fb
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.protolog.ProtoLogImpl;
+
+import org.junit.After;
+import org.junit.Test;
+
+/**
+ * Check if the ProtoLogTools is used to process the WindowManager source code.
+ */
+@SmallTest
+@Presubmit
+public class ProtoLogIntegrationTest {
+ @After
+ public void tearDown() {
+ ProtoLogImpl.setSingleInstance(null);
+ }
+
+ @Test
+ public void testProtoLogToolIntegration() {
+ ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ runWith(mockedProtoLog, () -> {
+ ProtoLogGroup.testProtoLog();
+ });
+ verify(mockedProtoLog).log(eq(ProtoLogImpl.LogLevel.ERROR), eq(ProtoLogGroup.TEST_GROUP),
+ anyInt(), eq(0b0010101001010111),
+ eq(ProtoLogGroup.TEST_GROUP.isLogToLogcat()
+ ? "Test completed successfully: %b %d %o %x %e %g %f %% %s"
+ : null),
+ eq(new Object[]{true, 1L, 2L, 3L, 0.4, 0.5, 0.6, "ok"}));
+ }
+
+ /**
+ * Starts protolog for the duration of {@code runnable}, with a ProtoLogImpl instance installed.
+ */
+ private void runWith(ProtoLogImpl mockInstance, Runnable runnable) {
+ ProtoLogImpl original = ProtoLogImpl.getSingleInstance();
+ original.startProtoLog(null);
+ try {
+ ProtoLogImpl.setSingleInstance(mockInstance);
+ try {
+ runnable.run();
+ } finally {
+ ProtoLogImpl.setSingleInstance(original);
+ }
+ } finally {
+ original.stopProtoLog(null, false);
+ }
+ }
+}
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 94abd346a814..cc598ffa63bd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -63,7 +63,6 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
-import android.util.MutableLong;
import android.util.SparseBooleanArray;
import androidx.test.filters.MediumTest;
@@ -72,6 +71,7 @@ import com.android.server.wm.RecentTasks.Callbacks;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.io.File;
import java.util.ArrayList;
@@ -86,6 +86,7 @@ import java.util.Set;
*/
@MediumTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class RecentTasksTest extends ActivityTestsBase {
private static final int TEST_USER_0_ID = 0;
private static final int TEST_USER_1_ID = 10;
@@ -95,10 +96,7 @@ public class RecentTasksTest extends ActivityTestsBase {
private static final int INVALID_STACK_ID = 999;
private ActivityDisplay mDisplay;
- private ActivityDisplay mOtherDisplay;
- private ActivityDisplay mSingleTaskDisplay;
private ActivityStack mStack;
- private ActivityStack mHomeStack;
private TestTaskPersister mTaskPersister;
private TestRecentTasks mRecentTasks;
private TestRunningTasks mRunningTasks;
@@ -111,15 +109,7 @@ public class RecentTasksTest extends ActivityTestsBase {
@Before
public void setUp() throws Exception {
mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
-
- // Set testing displays
mDisplay = mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY);
- mOtherDisplay = createNewActivityDisplay();
- mSingleTaskDisplay = createNewActivityDisplay();
- mSingleTaskDisplay.setDisplayToSingleTaskInstance();
- mRootActivityContainer.addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
- mRootActivityContainer.addChild(mDisplay, ActivityDisplay.POSITION_TOP);
- mRootActivityContainer.addChild(mSingleTaskDisplay, ActivityDisplay.POSITION_TOP);
// Set the recent tasks we should use for testing in this class.
mRecentTasks = new TestRecentTasks(mService, mTaskPersister);
@@ -131,8 +121,6 @@ public class RecentTasksTest extends ActivityTestsBase {
mRunningTasks = new TestRunningTasks();
mService.mStackSupervisor.setRunningTasks(mRunningTasks);
- mHomeStack = mDisplay.getOrCreateStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
mStack = mDisplay.createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
mCallbacksRecorder = new CallbacksRecorder();
@@ -269,7 +257,7 @@ public class RecentTasksTest extends ActivityTestsBase {
// other task
TaskRecord task1 = createTaskBuilder(".Task1")
.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
- .setStack(mHomeStack).build();
+ .setStack(mDisplay.getHomeStack()).build();
TaskRecord task2 = createTaskBuilder(".Task1")
.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
.setStack(mStack).build();
@@ -446,12 +434,12 @@ public class RecentTasksTest extends ActivityTestsBase {
mRecentTasks.add(task3);
mRecentTasks.add(task4);
- MutableLong prevLastActiveTime = new MutableLong(0);
+ long prevLastActiveTime = 0;
final ArrayList<TaskRecord> tasks = mRecentTasks.getRawTasks();
for (int i = 0; i < tasks.size(); i++) {
final TaskRecord task = tasks.get(i);
- assertThat(prevLastActiveTime.value).isLessThan(task.lastActiveTime);
- prevLastActiveTime.value = task.lastActiveTime;
+ assertThat(prevLastActiveTime).isLessThan(task.lastActiveTime);
+ prevLastActiveTime = task.lastActiveTime;
}
}
@@ -618,7 +606,10 @@ public class RecentTasksTest extends ActivityTestsBase {
mRecentTasks.setOnlyTestVisibleRange();
mRecentTasks.setParameters(-1 /* min */, 3 /* max */, -1 /* ms */);
- ActivityStack singleTaskStack = mSingleTaskDisplay.createStack(
+ final ActivityDisplay singleTaskDisplay =
+ addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
+ singleTaskDisplay.setDisplayToSingleTaskInstance();
+ ActivityStack singleTaskStack = singleTaskDisplay.createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
TaskRecord excludedTask1 = createTaskBuilder(".ExcludedTask1")
@@ -795,7 +786,8 @@ public class RecentTasksTest extends ActivityTestsBase {
mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
final ActivityStack homeStack = mDisplay.getHomeStack();
- final ActivityStack otherDisplayStack = mOtherDisplay.createStack(
+ final ActivityDisplay otherDisplay = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
+ final ActivityStack otherDisplayStack = otherDisplay.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
@@ -890,7 +882,7 @@ public class RecentTasksTest extends ActivityTestsBase {
@Test
public void testNotRestoreRecentTaskApis() {
final TaskRecord task = createTaskBuilder(".Task").build();
- final int taskId = task.taskId;
+ final int taskId = task.mTaskId;
mRecentTasks.add(task);
// Only keep the task in RecentTasks.
task.removeWindowContainer();
@@ -976,7 +968,7 @@ public class RecentTasksTest extends ActivityTestsBase {
TEST_USER_0_ID, 0).getList();
assertTrue(expectedTasks.length == infos.size());
for (int i = 0; i < infos.size(); i++) {
- assertTrue(expectedTasks[i].taskId == infos.get(i).taskId);
+ assertTrue(expectedTasks[i].mTaskId == infos.get(i).taskId);
}
}
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 f792b0db6cb5..702600402d8e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -66,15 +66,17 @@ import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
* Build/Install/Run:
- * atest FrameworksServicesTests:RecentsAnimationControllerTest
+ * atest WmTests:RecentsAnimationControllerTest
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class RecentsAnimationControllerTest extends WindowTestsBase {
@Mock SurfaceControl mMockLeash;
@@ -88,12 +90,8 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- synchronized (mWm.mGlobalLock) {
- // Hold the lock to protect the stubbing from being accessed by other threads.
- spyOn(mWm.mRoot);
- doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean());
- doReturn(mDisplayContent).when(mWm.mRoot).getDisplayContent(anyInt());
- }
+ doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean());
+ doReturn(mDisplayContent).when(mWm.mRoot).getDisplayContent(anyInt());
when(mMockRunner.asBinder()).thenReturn(new Binder());
mController = spy(new RecentsAnimationController(mWm, mMockRunner, mAnimationCallbacks,
DEFAULT_DISPLAY));
@@ -101,14 +99,14 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
@Test
public void testRemovedBeforeStarted_expectCanceled() throws Exception {
- final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
+ final ActivityRecord activity = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- AnimationAdapter adapter = mController.addAnimation(appWindow.getTask(),
+ AnimationAdapter adapter = mController.addAnimation(activity.getTask(),
false /* isRecentTaskInvisible */);
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
// Remove the app window so that the animation target can not be created
- appWindow.removeImmediately();
+ activity.removeImmediately();
mController.startAnimation();
// Verify that the finish callback to reparent the leash is called
@@ -120,14 +118,14 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
@Test
public void testCancelAfterRemove_expectIgnored() {
- final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
+ final ActivityRecord activity = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- AnimationAdapter adapter = mController.addAnimation(appWindow.getTask(),
+ AnimationAdapter adapter = mController.addAnimation(activity.getTask(),
false /* isRecentTaskInvisible */);
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
// Remove the app window so that the animation target can not be created
- appWindow.removeImmediately();
+ activity.removeImmediately();
mController.startAnimation();
mController.cleanupAnimation(REORDER_KEEP_IN_PLACE);
try {
@@ -140,41 +138,39 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
@Test
public void testIncludedApps_expectTargetAndVisible() {
mWm.setRecentsAnimationController(mController);
- final ActivityStack homeStack = mDisplayContent.mAcitvityDisplay.getOrCreateStack(
+ final ActivityStack homeStack = mDisplayContent.mActivityDisplay.getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
- final AppWindowToken homeAppWindow =
+ final ActivityRecord homeActivity =
new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
.setStack(homeStack)
.setCreateTask(true)
- .build()
- .mAppWindowToken;
- final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
+ .build();
+ final ActivityRecord activity = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- final AppWindowToken hiddenAppWindow = createAppWindowToken(mDisplayContent,
+ final ActivityRecord hiddenActivity = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- hiddenAppWindow.setHidden(true);
+ hiddenActivity.setHidden(true);
mDisplayContent.getConfiguration().windowConfiguration.setRotation(
mDisplayContent.getRotation());
- mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray());
+ mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray(), homeActivity);
// Ensure that we are animating the target activity as well
- assertTrue(mController.isAnimatingTask(homeAppWindow.getTask()));
- assertTrue(mController.isAnimatingTask(appWindow.getTask()));
- assertFalse(mController.isAnimatingTask(hiddenAppWindow.getTask()));
+ assertTrue(mController.isAnimatingTask(homeActivity.getTask()));
+ assertTrue(mController.isAnimatingTask(activity.getTask()));
+ assertFalse(mController.isAnimatingTask(hiddenActivity.getTask()));
}
@Test
public void testWallpaperIncluded_expectTarget() throws Exception {
mWm.setRecentsAnimationController(mController);
- final ActivityStack homeStack = mDisplayContent.mAcitvityDisplay.getOrCreateStack(
+ final ActivityStack homeStack = mDisplayContent.mActivityDisplay.getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
- final AppWindowToken homeAppWindow =
+ final ActivityRecord homeAppWindow =
new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
.setStack(homeStack)
.setCreateTask(true)
- .build()
- .mAppWindowToken;
- final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
+ .build();
+ final ActivityRecord appWindow = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, appWindow, "win1");
appWindow.addWindow(win1);
@@ -185,7 +181,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
mDisplayContent.getConfiguration().windowConfiguration.setRotation(
mDisplayContent.getRotation());
- mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray());
+ mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray(), homeAppWindow);
mController.startAnimation();
// Ensure that we are animating the app and wallpaper target
@@ -196,18 +192,17 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
@Test
public void testWallpaperAnimatorCanceled_expectAnimationKeepsRunning() throws Exception {
mWm.setRecentsAnimationController(mController);
- final ActivityStack homeStack = mDisplayContent.mAcitvityDisplay.getOrCreateStack(
+ final ActivityStack homeStack = mDisplayContent.mActivityDisplay.getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
- final AppWindowToken homeAppWindow =
+ final ActivityRecord homeActivity =
new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
.setStack(homeStack)
.setCreateTask(true)
- .build()
- .mAppWindowToken;
- final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
+ .build();
+ final ActivityRecord activity = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, appWindow, "win1");
- appWindow.addWindow(win1);
+ final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1");
+ activity.addWindow(win1);
final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
spyOn(mDisplayContent.mWallpaperController);
@@ -215,12 +210,12 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
mDisplayContent.getConfiguration().windowConfiguration.setRotation(
mDisplayContent.getRotation());
- mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray());
+ mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray(), homeActivity);
mController.startAnimation();
// Cancel the animation and ensure the controller is still running
wallpaperWindowToken.cancelAnimation();
- assertTrue(mController.isAnimatingTask(appWindow.getTask()));
+ assertTrue(mController.isAnimatingTask(activity.getTask()));
assertFalse(mController.isAnimatingWallpaper(wallpaperWindowToken));
verify(mMockRunner, never()).onAnimationCanceled(null /* taskSnapshot */);
}
@@ -228,27 +223,26 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
@Test
public void testFinish_expectTargetAndWallpaperAdaptersRemoved() {
mWm.setRecentsAnimationController(mController);
- final ActivityStack homeStack = mDisplayContent.mAcitvityDisplay.getOrCreateStack(
+ final ActivityStack homeStack = mDisplayContent.mActivityDisplay.getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
- final AppWindowToken homeAppWindow =
+ final ActivityRecord homeActivity =
new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
.setStack(homeStack)
.setCreateTask(true)
- .build()
- .mAppWindowToken;
- final WindowState hwin1 = createWindow(null, TYPE_BASE_APPLICATION, homeAppWindow, "hwin1");
- homeAppWindow.addWindow(hwin1);
- final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
+ .build();
+ final WindowState hwin1 = createWindow(null, TYPE_BASE_APPLICATION, homeActivity, "hwin1");
+ homeActivity.addWindow(hwin1);
+ final ActivityRecord activity = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, appWindow, "win1");
- appWindow.addWindow(win1);
+ final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1");
+ activity.addWindow(win1);
final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
spyOn(mDisplayContent.mWallpaperController);
doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
// Start and finish the animation
- mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray());
+ mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray(), homeActivity);
mController.startAnimation();
// Reset at this point since we may remove adapters that couldn't be created
reset(mController);
@@ -262,15 +256,15 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
@Test
public void testDeferCancelAnimation() throws Exception {
mWm.setRecentsAnimationController(mController);
- final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
+ final ActivityRecord activity = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, appWindow, "win1");
- appWindow.addWindow(win1);
- assertEquals(appWindow.getTask().getTopVisibleAppToken(), appWindow);
- assertEquals(appWindow.findMainWindow(), win1);
+ final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1");
+ activity.addWindow(win1);
+ assertEquals(activity.getTask().getTopVisibleActivity(), activity);
+ assertEquals(activity.findMainWindow(), win1);
- mController.addAnimation(appWindow.getTask(), false /* isRecentTaskInvisible */);
- assertTrue(mController.isAnimatingTask(appWindow.getTask()));
+ mController.addAnimation(activity.getTask(), false /* isRecentTaskInvisible */);
+ assertTrue(mController.isAnimatingTask(activity.getTask()));
mController.setDeferredCancel(true /* deferred */, false /* screenshot */);
mController.cancelAnimationWithScreenshot(false /* screenshot */);
@@ -285,15 +279,15 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
@Test
public void testDeferCancelAnimationWithScreenShot() throws Exception {
mWm.setRecentsAnimationController(mController);
- final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
+ final ActivityRecord activity = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, appWindow, "win1");
- appWindow.addWindow(win1);
- assertEquals(appWindow.getTask().getTopVisibleAppToken(), appWindow);
- assertEquals(appWindow.findMainWindow(), win1);
+ final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1");
+ activity.addWindow(win1);
+ assertEquals(activity.getTask().getTopVisibleActivity(), activity);
+ assertEquals(activity.findMainWindow(), win1);
- mController.addAnimation(appWindow.getTask(), false /* isRecentTaskInvisible */);
- assertTrue(mController.isAnimatingTask(appWindow.getTask()));
+ mController.addAnimation(activity.getTask(), false /* isRecentTaskInvisible */);
+ assertTrue(mController.isAnimatingTask(activity.getTask()));
spyOn(mWm.mTaskSnapshotController);
doNothing().when(mWm.mTaskSnapshotController).notifyAppVisibilityChanged(any(),
@@ -317,20 +311,20 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
@Test
public void testShouldAnimateWhenNoCancelWithDeferredScreenshot() {
mWm.setRecentsAnimationController(mController);
- final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
+ final ActivityRecord activity = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, appWindow, "win1");
- appWindow.addWindow(win1);
- assertEquals(appWindow.getTask().getTopVisibleAppToken(), appWindow);
- assertEquals(appWindow.findMainWindow(), win1);
+ final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1");
+ activity.addWindow(win1);
+ assertEquals(activity.getTask().getTopVisibleActivity(), activity);
+ assertEquals(activity.findMainWindow(), win1);
- mController.addAnimation(appWindow.getTask(), false /* isRecentTaskInvisible */);
- assertTrue(mController.isAnimatingTask(appWindow.getTask()));
+ mController.addAnimation(activity.getTask(), false /* isRecentTaskInvisible */);
+ assertTrue(mController.isAnimatingTask(activity.getTask()));
- // Assume appWindow transition should animate when no
+ // Assume activity transition should animate when no
// IRecentsAnimationController#setCancelWithDeferredScreenshot called.
assertFalse(mController.shouldDeferCancelWithScreenshot());
- assertTrue(appWindow.shouldAnimate(TRANSIT_ACTIVITY_CLOSE));
+ assertTrue(activity.shouldAnimate(TRANSIT_ACTIVITY_CLOSE));
}
private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) {
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 b4ccd500c971..839ddb2038ff 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -78,7 +78,7 @@ public class RecentsAnimationTest extends ActivityTestsBase {
mRecentsAnimationController = mock(RecentsAnimationController.class);
mService.mWindowManager.setRecentsAnimationController(mRecentsAnimationController);
doNothing().when(mService.mWindowManager).initializeRecentsAnimation(
- anyInt(), any(), any(), anyInt(), any());
+ anyInt(), any(), any(), anyInt(), any(), any());
doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation();
final RecentTasks recentTasks = mService.getRecentTasks();
@@ -97,7 +97,6 @@ public class RecentsAnimationTest extends ActivityTestsBase {
.setStack(recentsStack)
.build();
ActivityRecord topActivity = new ActivityBuilder(mService).setCreateTask(true).build();
- topActivity.fullscreen = true;
topActivity.getActivityStack().moveToFront("testRecentsActivityVisiblility");
doCallRealMethod().when(mRootActivityContainer).ensureActivitiesVisible(
@@ -346,7 +345,7 @@ public class RecentsAnimationTest extends ActivityTestsBase {
.setCreateTask(true)
.setComponent(new ComponentName(mContext.getPackageName(), "Home2"))
.build();
- otherUserHomeActivity.getTaskRecord().userId = TEST_USER_ID;
+ otherUserHomeActivity.getTaskRecord().mUserId = TEST_USER_ID;
ActivityStack fullscreenStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, true /* onTop */);
@@ -386,7 +385,8 @@ public class RecentsAnimationTest extends ActivityTestsBase {
return null;
}).when(mService.mWindowManager).initializeRecentsAnimation(
anyInt() /* targetActivityType */, any() /* recentsAnimationRunner */,
- any() /* callbacks */, anyInt() /* displayId */, any() /* recentTaskIds */);
+ any() /* callbacks */, anyInt() /* displayId */, any() /* recentTaskIds */,
+ any() /* targetActivity */);
}
Intent recentsIntent = new Intent();
diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
index efc2fd60ba37..a23425f2510c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
@@ -106,7 +106,7 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
"overrideWindow");
overrideWindow.mAttrs.packageName = "com.android.test";
overrideWindow.mAttrs.preferredDisplayModeId = LOW_MODE_ID;
- overrideWindow.mAppToken.mSurfaceAnimator.startAnimation(
+ overrideWindow.mActivityRecord.mSurfaceAnimator.startAnimation(
overrideWindow.getPendingTransaction(), mock(AnimationAdapter.class),
false /* hidden */);
mPolicy.addNonHighRefreshRatePackage("com.android.test");
@@ -122,7 +122,7 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
mPolicy.addNonHighRefreshRatePackage("com.android.test");
assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(cameraUsingWindow));
- cameraUsingWindow.mAppToken.mSurfaceAnimator.startAnimation(
+ cameraUsingWindow.mActivityRecord.mSurfaceAnimator.startAnimation(
cameraUsingWindow.getPendingTransaction(), mock(AnimationAdapter.class),
false /* hidden */);
assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
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 3b9c3bbdc4ba..e353903835a6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -91,9 +91,9 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
@Test
public void testRun() throws Exception {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
- mDisplayContent.mOpeningApps.add(win.mAppToken);
+ mDisplayContent.mOpeningApps.add(win.mActivityRecord);
try {
- final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mAppToken,
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
mController.goodToGo();
@@ -110,8 +110,8 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
final RemoteAnimationTarget app = appsCaptor.getValue()[0];
assertEquals(new Point(50, 100), app.position);
assertEquals(new Rect(50, 100, 150, 150), app.sourceContainerBounds);
- assertEquals(win.mAppToken.getPrefixOrderIndex(), app.prefixOrderIndex);
- assertEquals(win.mAppToken.getTask().mTaskId, app.taskId);
+ assertEquals(win.mActivityRecord.getPrefixOrderIndex(), app.prefixOrderIndex);
+ assertEquals(win.mActivityRecord.getTask().mTaskId, app.taskId);
assertEquals(mMockLeash, app.leash);
assertEquals(win.mWinAnimator.mLastClipRect, app.clipRect);
assertEquals(false, app.isTranslucent);
@@ -129,7 +129,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
@Test
public void testCancel() throws Exception {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
- final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mAppToken,
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
mController.goodToGo();
@@ -142,7 +142,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
@FlakyTest(bugId = 133372977)
public void testTimeout() throws Exception {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
- final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mAppToken,
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
mController.goodToGo();
@@ -161,7 +161,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
"testWin");
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
- win.mAppToken, new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
+ win.mActivityRecord, new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
mController.goodToGo();
@@ -189,7 +189,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
@Test
public void testNotReallyStarted() {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
- mController.createRemoteAnimationRecord(win.mAppToken,
+ mController.createRemoteAnimationRecord(win.mActivityRecord,
new Point(50, 100), new Rect(50, 100, 150, 150), null);
mController.goodToGo();
verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
@@ -199,9 +199,9 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
public void testOneNotStarted() throws Exception {
final WindowState win1 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin1");
final WindowState win2 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin2");
- mController.createRemoteAnimationRecord(win1.mAppToken,
+ mController.createRemoteAnimationRecord(win1.mActivityRecord,
new Point(50, 100), new Rect(50, 100, 150, 150), null);
- final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win2.mAppToken,
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win2.mActivityRecord,
new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
mController.goodToGo();
@@ -221,10 +221,10 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
@Test
public void testRemovedBeforeStarted() {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
- final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mAppToken,
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
- win.mAppToken.removeImmediately();
+ win.mActivityRecord.removeImmediately();
mController.goodToGo();
verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
verify(mFinishedCallback).onAnimationFinished(eq(adapter));
@@ -233,10 +233,10 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
@Test
public void testChange() throws Exception {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
- mDisplayContent.mChangingApps.add(win.mAppToken);
+ mDisplayContent.mChangingApps.add(win.mActivityRecord);
try {
final RemoteAnimationRecord record = mController.createRemoteAnimationRecord(
- win.mAppToken, new Point(50, 100), new Rect(50, 100, 150, 150),
+ win.mActivityRecord, new Point(50, 100), new Rect(50, 100, 150, 150),
new Rect(0, 0, 200, 200));
assertNotNull(record.mThumbnailAdapter);
((AnimationAdapter) record.mAdapter)
@@ -284,9 +284,9 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
spyOn(mDisplayContent.mWallpaperController);
doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
- mDisplayContent.mOpeningApps.add(win.mAppToken);
+ mDisplayContent.mOpeningApps.add(win.mActivityRecord);
try {
- final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mAppToken,
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
mController.goodToGo();
@@ -312,9 +312,9 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
spyOn(mDisplayContent.mWallpaperController);
doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
- mDisplayContent.mOpeningApps.add(win.mAppToken);
+ mDisplayContent.mOpeningApps.add(win.mActivityRecord);
try {
- final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mAppToken,
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
mController.goodToGo();
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 db105ddc956d..afe18c332b9d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -31,6 +31,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Tests for RootWindowContainer.
@@ -40,6 +41,7 @@ import org.junit.Test;
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class RootWindowContainerTests extends WindowTestsBase {
private static final int FAKE_CALLING_UID = 667;
@@ -91,18 +93,16 @@ public class RootWindowContainerTests extends WindowTestsBase {
@Test
public void testUpdateDefaultDisplayWindowingModeOnSettingsRetrieved() {
- synchronized (mWm.mGlobalLock) {
- assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN,
- mWm.getDefaultDisplayContentLocked().getWindowingMode());
+ assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN,
+ mWm.getDefaultDisplayContentLocked().getWindowingMode());
- mWm.mIsPc = true;
- mWm.mAtmService.mSupportsFreeformWindowManagement = true;
+ mWm.mIsPc = true;
+ mWm.mAtmService.mSupportsFreeformWindowManagement = true;
- mWm.mRoot.onSettingsRetrieved();
+ mWm.mRoot.onSettingsRetrieved();
- assertEquals(WindowConfiguration.WINDOWING_MODE_FREEFORM,
- mWm.getDefaultDisplayContentLocked().getWindowingMode());
- }
+ assertEquals(WindowConfiguration.WINDOWING_MODE_FREEFORM,
+ mWm.getDefaultDisplayContentLocked().getWindowingMode());
}
}
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 3e316f674dbf..8326ad8bdefe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -35,6 +35,7 @@ import androidx.test.filters.MediumTest;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.ArrayList;
@@ -44,6 +45,7 @@ import java.util.ArrayList;
*/
@MediumTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class RunningTasksTest extends ActivityTestsBase {
private static final ArraySet<Integer> PROFILE_IDS = new ArraySet<>();
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index 8d2a79b6b5db..f5d08dcfcb77 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -97,11 +97,6 @@ public class StubTransaction extends SurfaceControl.Transaction {
}
@Override
- public SurfaceControl.Transaction transferTouchFocus(IBinder fromToken, IBinder toToken) {
- return this;
- }
-
- @Override
public SurfaceControl.Transaction setGeometry(SurfaceControl sc, Rect sourceCrop,
Rect destFrame, @Surface.Rotation int orientation) {
return this;
@@ -244,4 +239,15 @@ public class StubTransaction extends SurfaceControl.Transaction {
public SurfaceControl.Transaction remove(SurfaceControl sc) {
return this;
}
+
+ @Override
+ public SurfaceControl.Transaction syncInputWindows() {
+ return this;
+ }
+
+ @Override
+ public SurfaceControl.Transaction setColorSpaceAgnostic(SurfaceControl sc, boolean agnostic) {
+ return this;
+ }
+
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 2b1c4fff5861..979aab66be63 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -237,7 +237,7 @@ public class SurfaceAnimatorTest extends WindowTestsBase {
mDeferFinishAnimatable.mFinishedCallbackCalled = false;
// Simulate the first deferred callback is executed from
- // {@link AnimatingAppWindowTokenRegistry#endDeferringFinished}.
+ // {@link AnimatingActivityRegistry#endDeferringFinished}.
firstDeferFinishCallback.run();
// The second animation should not be finished.
assertFalse(mDeferFinishAnimatable.mFinishedCallbackCalled);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
new file mode 100644
index 000000000000..d1b6f6f3f4cf
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.os.Handler;
+import android.testing.DexmakerShareClassLoaderRule;
+
+import org.junit.Rule;
+
+import java.util.concurrent.Callable;
+
+/** The base class which provides the common rule for test classes under wm package. */
+class SystemServiceTestsBase {
+ @Rule
+ public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
+ new DexmakerShareClassLoaderRule();
+ @Rule
+ public final SystemServicesTestRule mSystemServicesTestRule = new SystemServicesTestRule();
+
+ @WindowTestRunner.MethodWrapperRule
+ public final WindowManagerGlobalLockRule mLockRule =
+ new WindowManagerGlobalLockRule(mSystemServicesTestRule);
+
+ /** Waits until the main handler for WM has processed all messages. */
+ void waitUntilHandlersIdle() {
+ mLockRule.waitForLocked(mSystemServicesTestRule::waitUntilWindowManagerHandlersIdle);
+ }
+
+ boolean waitHandlerIdle(Handler handler) {
+ return waitHandlerIdle(handler, 0 /* timeout */);
+ }
+
+ boolean waitHandlerIdle(Handler handler, long timeout) {
+ return runWithScissors(handler, () -> { }, timeout);
+ }
+
+ boolean runWithScissors(Handler handler, Runnable r, long timeout) {
+ return mLockRule.runWithScissors(handler, r, timeout);
+ }
+
+ /** It is used when we want to wait for a result inside {@link WindowManagerGlobalLock}. */
+ <T> T awaitInWmLock(Callable<T> callable) {
+ return mLockRule.waitForLocked(callable);
+ }
+}
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 5a4d3991bedd..fa1f435a37c0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -18,7 +18,6 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.os.Process.THREAD_PRIORITY_DEFAULT;
import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -36,7 +35,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSess
import static com.android.dx.mockito.inline.extended.ExtendedMockito.nullable;
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.WindowManagerDebugConfig.TAG_WM;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
@@ -68,11 +66,12 @@ import com.android.server.AnimationThread;
import com.android.server.DisplayThread;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
-import com.android.server.ServiceThread;
+import com.android.server.UiThread;
import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerService;
import com.android.server.appop.AppOpsService;
import com.android.server.display.color.ColorDisplayService;
+import com.android.server.firewall.IntentFirewall;
import com.android.server.input.InputManagerService;
import com.android.server.pm.UserManagerService;
import com.android.server.policy.PermissionPolicyInternal;
@@ -105,13 +104,13 @@ public class SystemServicesTestRule implements TestRule {
private Context mContext;
private StaticMockitoSession mMockitoSession;
- ServiceThread mHandlerThread;
private ActivityManagerService mAmService;
private ActivityTaskManagerService mAtmService;
private WindowManagerService mWmService;
private TestWindowManagerPolicy mWMPolicy;
private WindowState.PowerManagerWrapper mPowerManagerWrapper;
private InputManagerService mImService;
+ private InputChannel mInputChannel;
/**
* Spied {@link SurfaceControl.Transaction} class than can be used to verify calls.
*/
@@ -147,10 +146,6 @@ public class SystemServicesTestRule implements TestRule {
}
private void setUpSystemCore() {
- mHandlerThread = new ServiceThread(
- "WmTestsThread", THREAD_PRIORITY_DEFAULT, true /* allowIo */);
- mHandlerThread.start();
-
doReturn(mock(Watchdog.class)).when(Watchdog::getInstance);
mContext = getInstrumentation().getTargetContext();
@@ -181,6 +176,11 @@ public class SystemServicesTestRule implements TestRule {
final AppOpsManager aom = mock(AppOpsManager.class);
doReturn(aom).when(mContext).getSystemService(eq(Context.APP_OPS_SERVICE));
+ // Prevent "WakeLock finalized while still held: SCREEN_FROZEN".
+ final PowerManager pm = mock(PowerManager.class);
+ doReturn(pm).when(mContext).getSystemService(eq(Context.POWER_SERVICE));
+ doReturn(mock(PowerManager.WakeLock.class)).when(pm).newWakeLock(anyInt(), anyString());
+
// DisplayManagerInternal
final DisplayManagerInternal dmi = mock(DisplayManagerInternal.class);
doReturn(dmi).when(() -> LocalServices.getService(eq(DisplayManagerInternal.class)));
@@ -214,11 +214,11 @@ public class SystemServicesTestRule implements TestRule {
// InputManagerService
mImService = mock(InputManagerService.class);
- // InputChannel is final and can't be mocked.
- final InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM);
- if (input != null && input.length > 1) {
- doReturn(input[1]).when(mImService).monitorInput(anyString(), anyInt());
- }
+ // InputChannel cannot be mocked because it may pass to InputEventReceiver.
+ final InputChannel[] inputChannels = InputChannel.openInputChannelPair(TAG);
+ inputChannels[0].dispose();
+ mInputChannel = inputChannels[1];
+ doReturn(mInputChannel).when(mImService).monitorInput(anyString(), anyInt());
// StatusBarManagerInternal
final StatusBarManagerInternal sbmi = mock(StatusBarManagerInternal.class);
@@ -227,8 +227,7 @@ public class SystemServicesTestRule implements TestRule {
private void setUpActivityTaskManagerService() {
// ActivityManagerService
- mAmService = new ActivityManagerService(
- new AMTestInjector(mContext, mHandlerThread), mHandlerThread);
+ mAmService = new ActivityManagerService(new AMTestInjector(mContext), null /* thread */);
spyOn(mAmService);
doReturn(mock(IPackageManager.class)).when(mAmService).getPackageManager();
doNothing().when(mAmService).grantImplicitAccess(
@@ -246,6 +245,15 @@ public class SystemServicesTestRule implements TestRule {
doNothing().when(amInternal).startProcess(
any(), any(), anyBoolean(), anyBoolean(), any(), any());
doNothing().when(amInternal).updateOomLevelsForDisplay(anyInt());
+ doNothing().when(amInternal).broadcastGlobalConfigurationChanged(anyInt(), anyBoolean());
+ doNothing().when(amInternal).cleanUpServices(anyInt(), any(), any());
+ doReturn(UserHandle.USER_SYSTEM).when(amInternal).getCurrentUserId();
+ doReturn(TEST_USER_PROFILE_IDS).when(amInternal).getCurrentProfileIds();
+ doReturn(true).when(amInternal).isCurrentProfile(anyInt());
+ doReturn(true).when(amInternal).isUserRunning(anyInt(), anyInt());
+ doReturn(true).when(amInternal).hasStartedUserState(anyInt());
+ doReturn(false).when(amInternal).shouldConfirmCredentials(anyInt());
+ doReturn(false).when(amInternal).isActivityStartsLoggingEnabled();
LocalServices.addService(ActivityManagerInternal.class, amInternal);
mAtmService = new TestActivityTaskManagerService(mContext, mAmService);
@@ -289,7 +297,6 @@ public class SystemServicesTestRule implements TestRule {
}
private void tearDown() {
- waitUntilWindowManagerHandlersIdle();
// Unregister display listener from root to avoid issues with subsequent tests.
mContext.getSystemService(DisplayManager.class)
.unregisterDisplayListener(mAtmService.mRootActivityContainer);
@@ -297,37 +304,23 @@ public class SystemServicesTestRule implements TestRule {
// a static object, so we need to clean it up in tearDown(), even though we didn't set up
// in tests.
DeviceConfig.removeOnPropertiesChangedListener(mWmService.mPropertiesChangedListener);
- mWmService = null;
- mWMPolicy = null;
- mPowerManagerWrapper = null;
-
- tearDownLocalServices();
- tearDownSystemCore();
+ waitUntilWindowManagerHandlersIdle();
// Needs to explicitly dispose current static threads because there could be messages
// scheduled at a later time, and all mocks are invalid when it's executed.
DisplayThread.dispose();
AnimationThread.dispose();
+ UiThread.dispose();
+ mInputChannel.dispose();
+
+ tearDownLocalServices();
// Reset priority booster because animation thread has been changed.
WindowManagerService.sThreadPriorityBooster = new WindowManagerThreadPriorityBooster();
+ mMockitoSession.finishMocking();
Mockito.framework().clearInlineMocks();
}
- private void tearDownSystemCore() {
- if (mMockitoSession != null) {
- mMockitoSession.finishMocking();
- mMockitoSession = null;
- }
-
- if (mHandlerThread != null) {
- // Make sure there are no running messages and then quit the thread so the next test
- // won't be affected.
- mHandlerThread.getThreadHandler().runWithScissors(mHandlerThread::quit,
- 0 /* timeout */);
- }
- }
-
private static void tearDownLocalServices() {
LocalServices.removeServiceForTest(DisplayManagerInternal.class);
LocalServices.removeServiceForTest(PowerManagerInternal.class);
@@ -375,7 +368,6 @@ public class SystemServicesTestRule implements TestRule {
waitHandlerIdle(wm.mH);
waitHandlerIdle(wm.mAnimationHandler);
waitHandlerIdle(SurfaceAnimationThread.getHandler());
- waitHandlerIdle(mHandlerThread.getThreadHandler());
}
private void waitHandlerIdle(Handler handler) {
@@ -423,8 +415,8 @@ public class SystemServicesTestRule implements TestRule {
final AppOpsService aos = mock(AppOpsService.class);
doReturn(aos).when(this).getAppOpsService();
// Make sure permission checks aren't overridden.
- doReturn(AppOpsManager.MODE_DEFAULT)
- .when(aos).noteOperation(anyInt(), anyInt(), anyString());
+ doReturn(AppOpsManager.MODE_DEFAULT).when(aos).noteOperation(anyInt(), anyInt(),
+ anyString(), nullable(String.class));
// UserManagerService
final UserManagerService ums = mock(UserManagerService.class);
@@ -435,8 +427,12 @@ public class SystemServicesTestRule implements TestRule {
ams.mActivityTaskManager = this;
ams.mAtmInternal = mInternal;
onActivityManagerInternalAdded();
- initialize(
- ams.mIntentFirewall, ams.mPendingIntentController, mHandlerThread.getLooper());
+
+ final IntentFirewall intentFirewall = mock(IntentFirewall.class);
+ doReturn(true).when(intentFirewall).checkStartActivity(
+ any(), anyInt(), anyInt(), nullable(String.class), any());
+ initialize(intentFirewall, null /* intentController */,
+ DisplayThread.getHandler().getLooper());
spyOn(getLifecycleManager());
spyOn(getLockTaskController());
spyOn(getTaskChangeNotificationController());
@@ -493,11 +489,9 @@ public class SystemServicesTestRule implements TestRule {
// TODO: Can we just mock this?
private static class AMTestInjector extends ActivityManagerService.Injector {
- private ServiceThread mHandlerThread;
- AMTestInjector(Context context, ServiceThread handlerThread) {
+ AMTestInjector(Context context) {
super(context);
- mHandlerThread = handlerThread;
}
@Override
@@ -512,7 +506,7 @@ public class SystemServicesTestRule implements TestRule {
@Override
public Handler getUiHandler(ActivityManagerService service) {
- return mHandlerThread.getThreadHandler();
+ return UiThread.getHandler();
}
@Override
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 e6c9b9f7a160..dd85f69b7160 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -29,6 +29,8 @@ import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
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.spyOn;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
@@ -65,6 +67,10 @@ import java.util.Locale;
@SmallTest
@Presubmit
public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
+ private static final Rect DISPLAY_BOUNDS = new Rect(/* left */ 0, /* top */ 0,
+ /* right */ 1920, /* bottom */ 1080);
+ private static final Rect DISPLAY_STABLE_BOUNDS = new Rect(/* left */ 100,
+ /* top */ 200, /* right */ 1620, /* bottom */ 680);
private ActivityRecord mActivity;
@@ -614,7 +620,7 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
/* source */ null, /* options */ null, mCurrent, mResult));
- assertEquals(0, mResult.mBounds.left);
+ assertEquals(DISPLAY_STABLE_BOUNDS.left, mResult.mBounds.left);
}
@Test
@@ -630,7 +636,7 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
/* source */ null, /* options */ null, mCurrent, mResult));
- assertEquals(0, mResult.mBounds.top);
+ assertEquals(DISPLAY_STABLE_BOUNDS.top, mResult.mBounds.top);
}
@Test
@@ -646,8 +652,8 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
/* source */ null, /* options */ null, mCurrent, mResult));
- assertEquals(0, mResult.mBounds.left);
- assertEquals(0, mResult.mBounds.top);
+ assertEquals(DISPLAY_STABLE_BOUNDS.left, mResult.mBounds.left);
+ assertEquals(DISPLAY_STABLE_BOUNDS.top, mResult.mBounds.top);
}
@Test
@@ -663,7 +669,7 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
/* source */ null, /* options */ null, mCurrent, mResult));
- assertEquals(1920, mResult.mBounds.right);
+ assertEquals(DISPLAY_STABLE_BOUNDS.right, mResult.mBounds.right);
}
@Test
@@ -679,7 +685,7 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
/* source */ null, /* options */ null, mCurrent, mResult));
- assertEquals(1080, mResult.mBounds.bottom);
+ assertEquals(DISPLAY_STABLE_BOUNDS.bottom, mResult.mBounds.bottom);
}
@Test
@@ -695,8 +701,8 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
/* source */ null, /* options */ null, mCurrent, mResult));
- assertEquals(1920, mResult.mBounds.right);
- assertEquals(1080, mResult.mBounds.bottom);
+ assertEquals(DISPLAY_STABLE_BOUNDS.right, mResult.mBounds.right);
+ assertEquals(DISPLAY_STABLE_BOUNDS.bottom, mResult.mBounds.bottom);
}
@Test
@@ -712,7 +718,7 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
/* source */ null, /* options */ null, mCurrent, mResult));
- assertEquals(new Rect(900, 500, 1020, 580), mResult.mBounds);
+ assertEquals(new Rect(800, 400, 920, 480), mResult.mBounds);
}
@Test
@@ -728,7 +734,7 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
/* source */ null, /* options */ null, mCurrent, mResult));
- assertEquals(new Rect(0, 500, 120, 580), mResult.mBounds);
+ assertEquals(new Rect(100, 400, 220, 480), mResult.mBounds);
}
@Test
@@ -744,7 +750,7 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
/* source */ null, /* options */ null, mCurrent, mResult));
- assertEquals(new Rect(900, 0, 1020, 80), mResult.mBounds);
+ assertEquals(new Rect(800, 200, 920, 280), mResult.mBounds);
}
@Test
@@ -760,7 +766,7 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
/* source */ null, /* options */ null, mCurrent, mResult));
- assertEquals(new Rect(0, 0, 120, 80), mResult.mBounds);
+ assertEquals(new Rect(100, 200, 220, 280), mResult.mBounds);
}
@Test
@@ -776,7 +782,7 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
/* source */ null, /* options */ null, mCurrent, mResult));
- assertEquals(new Rect(1800, 500, 1920, 580), mResult.mBounds);
+ assertEquals(new Rect(1500, 400, 1620, 480), mResult.mBounds);
}
@Test
@@ -792,7 +798,7 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
/* source */ null, /* options */ null, mCurrent, mResult));
- assertEquals(new Rect(900, 1000, 1020, 1080), mResult.mBounds);
+ assertEquals(new Rect(800, 600, 920, 680), mResult.mBounds);
}
@Test
@@ -808,7 +814,7 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
/* source */ null, /* options */ null, mCurrent, mResult));
- assertEquals(new Rect(1800, 1000, 1920, 1080), mResult.mBounds);
+ assertEquals(new Rect(1500, 600, 1620, 680), mResult.mBounds);
}
@Test
@@ -819,12 +825,12 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
- .setWidthFraction(0.0625f).setHeightFraction(0.1f).build();
+ .setWidthFraction(0.125f).setHeightFraction(0.1f).build();
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
/* source */ null, /* options */ null, mCurrent, mResult));
- assertEquals(new Rect(900, 486, 1020, 594), mResult.mBounds);
+ assertEquals(new Rect(765, 416, 955, 464), mResult.mBounds);
}
@Test
@@ -952,13 +958,12 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
mActivity, /* source */ null, options, mCurrent, mResult));
- final Rect displayBounds = freeformDisplay.getBounds();
assertEquals("Distance to left and right should be equal.",
- mResult.mBounds.left - displayBounds.left,
- displayBounds.right - mResult.mBounds.right);
+ mResult.mBounds.left - DISPLAY_STABLE_BOUNDS.left,
+ DISPLAY_STABLE_BOUNDS.right - mResult.mBounds.right, /* delta */ 1);
assertEquals("Distance to top and bottom should be equal.",
- mResult.mBounds.top - displayBounds.top,
- displayBounds.bottom - mResult.mBounds.bottom);
+ mResult.mBounds.top - DISPLAY_STABLE_BOUNDS.top,
+ DISPLAY_STABLE_BOUNDS.bottom - mResult.mBounds.bottom, /* delta */ 1);
}
@Test
@@ -1041,15 +1046,16 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
options.setLaunchDisplayId(freeformDisplay.mDisplayId);
mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
- mCurrent.mBounds.set(100, 200, 2120, 1380);
+ mCurrent.mBounds.set(100, 300, 1820, 1380);
mActivity.info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP;
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
mActivity, /* source */ null, options, mCurrent, mResult));
- assertTrue("Result bounds should start from origin, but it's " + mResult.mBounds,
- mResult.mBounds.left == 0 && mResult.mBounds.top == 0);
+ assertTrue("Result bounds should start from app bounds's origin, but it's "
+ + mResult.mBounds,
+ mResult.mBounds.left == 100 && mResult.mBounds.top == 200);
}
@Test
@@ -1067,15 +1073,16 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
options.setLaunchDisplayId(freeformDisplay.mDisplayId);
mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
- mCurrent.mBounds.set(100, 200, 2120, 1380);
+ mCurrent.mBounds.set(100, 300, 1820, 1380);
mActivity.info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP;
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
mActivity, /* source */ null, options, mCurrent, mResult));
- assertTrue("Result bounds should start from origin, but it's " + mResult.mBounds,
- mResult.mBounds.left == -100 && mResult.mBounds.top == 0);
+ assertTrue("Result bounds should start from top-right corner of app bounds, but "
+ + "it's " + mResult.mBounds,
+ mResult.mBounds.left == -100 && mResult.mBounds.top == 200);
}
@Test
@@ -1083,6 +1090,11 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
WINDOWING_MODE_FREEFORM);
+ // This test case requires a relatively big app bounds to ensure the default size calculated
+ // by letterbox won't be too small to hold the minimum width/height.
+ freeformDisplay.mDisplayContent.mDisplayFrames.mStable.set(/* left */ 10, /* top */ 10,
+ /* right */ 1910, /* top */ 1070);
+
final ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchDisplayId(freeformDisplay.mDisplayId);
@@ -1245,7 +1257,7 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
mActivity, /* source */ null, options, mCurrent, mResult));
- assertEquals(new Rect(0, 0, 300, 300), mResult.mBounds);
+ assertEquals(new Rect(100, 200, 400, 500), mResult.mBounds);
}
@Test
@@ -1301,9 +1313,15 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
private TestActivityDisplay createNewActivityDisplay(int windowingMode) {
final TestActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
display.setWindowingMode(windowingMode);
- display.setBounds(/* left */ 0, /* top */ 0, /* right */ 1920, /* bottom */ 1080);
+ display.setBounds(DISPLAY_BOUNDS);
display.getConfiguration().densityDpi = DENSITY_DEFAULT;
display.getConfiguration().orientation = ORIENTATION_LANDSCAPE;
+ display.mDisplayContent.mDisplayFrames.mStable.set(DISPLAY_STABLE_BOUNDS);
+ spyOn(display.mDisplayContent.mDisplayFrames);
+
+ // We didn't set up the overall environment for this test, so we need to mute the side
+ // effect of layout passes that loosen the stable frame.
+ doNothing().when(display.mDisplayContent.mDisplayFrames).onBeginLayout();
return display;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
index 714d2f2f94a1..dc89f5080c4b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -18,10 +18,10 @@ package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
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.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static org.junit.Assert.assertFalse;
@@ -37,6 +37,7 @@ import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Tests for the {@link TaskPositioningController} class.
@@ -46,6 +47,7 @@ import org.junit.Test;
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class TaskPositioningControllerTests extends WindowTestsBase {
private static final int TIMEOUT_MS = 1000;
@@ -57,32 +59,29 @@ public class TaskPositioningControllerTests extends WindowTestsBase {
assertNotNull(mWm.mTaskPositioningController);
mTarget = mWm.mTaskPositioningController;
+ when(mWm.mInputManager.transferTouchFocus(
+ any(InputChannel.class),
+ any(InputChannel.class))).thenReturn(true);
+
mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window");
mWindow.mInputChannel = new InputChannel();
- synchronized (mWm.mGlobalLock) {
- mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
- spyOn(mDisplayContent);
- doReturn(mock(InputMonitor.class)).when(mDisplayContent).getInputMonitor();
- }
+ mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
+ doReturn(mock(InputMonitor.class)).when(mDisplayContent).getInputMonitor();
}
@Test
public void testStartAndFinishPositioning() {
- synchronized (mWm.mGlobalLock) {
- assertFalse(mTarget.isPositioningLocked());
- assertNull(mTarget.getDragWindowHandleLocked());
- }
+ assertFalse(mTarget.isPositioningLocked());
+ assertNull(mTarget.getDragWindowHandleLocked());
assertTrue(mTarget.startMovingTask(mWindow.mClient, 0, 0));
- synchronized (mWm.mGlobalLock) {
- assertTrue(mTarget.isPositioningLocked());
- assertNotNull(mTarget.getDragWindowHandleLocked());
- }
+ assertTrue(mTarget.isPositioningLocked());
+ assertNotNull(mTarget.getDragWindowHandleLocked());
mTarget.finishTaskPositioning();
// Wait until the looper processes finishTaskPositioning.
- assertTrue(mWm.mH.runWithScissors(() -> { }, TIMEOUT_MS));
+ assertTrue(waitHandlerIdle(mWm.mH, TIMEOUT_MS));
assertFalse(mTarget.isPositioningLocked());
assertNull(mTarget.getDragWindowHandleLocked());
@@ -91,21 +90,17 @@ public class TaskPositioningControllerTests extends WindowTestsBase {
@FlakyTest(bugId = 129507487)
@Test
public void testFinishPositioningWhenAppRequested() {
- synchronized (mWm.mGlobalLock) {
- assertFalse(mTarget.isPositioningLocked());
- assertNull(mTarget.getDragWindowHandleLocked());
- }
+ assertFalse(mTarget.isPositioningLocked());
+ assertNull(mTarget.getDragWindowHandleLocked());
assertTrue(mTarget.startMovingTask(mWindow.mClient, 0, 0));
- synchronized (mWm.mGlobalLock) {
- assertTrue(mTarget.isPositioningLocked());
- assertNotNull(mTarget.getDragWindowHandleLocked());
- }
+ assertTrue(mTarget.isPositioningLocked());
+ assertNotNull(mTarget.getDragWindowHandleLocked());
mTarget.finishTaskPositioning(mWindow.mClient);
// Wait until the looper processes finishTaskPositioning.
- assertTrue(mWm.mH.runWithScissors(() -> { }, TIMEOUT_MS));
+ assertTrue(waitHandlerIdle(mWm.mH, TIMEOUT_MS));
assertFalse(mTarget.isPositioningLocked());
assertNull(mTarget.getDragWindowHandleLocked());
@@ -113,27 +108,23 @@ public class TaskPositioningControllerTests extends WindowTestsBase {
@Test
public void testHandleTapOutsideTask() {
- synchronized (mWm.mGlobalLock) {
- assertFalse(mTarget.isPositioningLocked());
- assertNull(mTarget.getDragWindowHandleLocked());
- }
+ assertFalse(mTarget.isPositioningLocked());
+ assertNull(mTarget.getDragWindowHandleLocked());
final DisplayContent content = mock(DisplayContent.class);
- when(content.findTaskForResizePoint(anyInt(), anyInt())).thenReturn(mWindow.getTask());
+ doReturn(mWindow.getTask()).when(content).findTaskForResizePoint(anyInt(), anyInt());
assertNotNull(mWindow.getTask().getTopVisibleAppMainWindow());
mTarget.handleTapOutsideTask(content, 0, 0);
// Wait until the looper processes finishTaskPositioning.
- assertTrue(mWm.mH.runWithScissors(() -> { }, TIMEOUT_MS));
+ assertTrue(waitHandlerIdle(mWm.mH, TIMEOUT_MS));
- synchronized (mWm.mGlobalLock) {
- assertTrue(mTarget.isPositioningLocked());
- assertNotNull(mTarget.getDragWindowHandleLocked());
- }
+ assertTrue(mTarget.isPositioningLocked());
+ assertNotNull(mTarget.getDragWindowHandleLocked());
mTarget.finishTaskPositioning();
// Wait until the looper processes finishTaskPositioning.
- assertTrue(mWm.mH.runWithScissors(() -> { }, TIMEOUT_MS));
+ assertTrue(waitHandlerIdle(mWm.mH, TIMEOUT_MS));
assertFalse(mTarget.isPositioningLocked());
assertNull(mTarget.getDragWindowHandleLocked());
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 c83401b2eb65..0f8fb0417d01 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -51,7 +51,6 @@ 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.eq;
import static org.mockito.Mockito.mock;
import android.app.ActivityManager;
@@ -78,6 +77,7 @@ import com.android.server.wm.utils.WmDisplayCutout;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -98,6 +98,7 @@ import java.util.ArrayList;
*/
@MediumTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class TaskRecordTests extends ActivityTestsBase {
private static final String TASK_TAG = "task";
@@ -118,7 +119,7 @@ public class TaskRecordTests extends ActivityTestsBase {
final byte[] serializedBytes = serializeToBytes(expected);
final TaskRecord actual = restoreFromBytes(serializedBytes);
- assertEquals(expected.taskId, actual.taskId);
+ assertEquals(expected.mTaskId, actual.mTaskId);
assertEquals(expected.mLastNonFullscreenBounds, actual.mLastNonFullscreenBounds);
}
@@ -131,7 +132,7 @@ public class TaskRecordTests extends ActivityTestsBase {
@Test
public void testCopyBaseIntentForTaskInfo() {
final TaskRecord task = createTaskRecord(1);
- task.lastTaskDescription = new ActivityManager.TaskDescription();
+ task.mTaskDescription = new ActivityManager.TaskDescription();
final TaskInfo info = task.getTaskInfo();
// The intent of info should be a copy so assert that they are different instances.
@@ -237,7 +238,7 @@ public class TaskRecordTests extends ActivityTestsBase {
ActivityStack stack = new StackBuilder(mRootActivityContainer).setDisplay(display)
.setWindowingMode(WINDOWING_MODE_FREEFORM).build();
TaskRecord task = stack.getChildAt(0);
- task.getRootActivity().mAppWindowToken.setOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
+ task.getRootActivity().setOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
DisplayInfo info = new DisplayInfo();
display.mDisplay.getDisplayInfo(info);
final Rect fullScreenBounds = new Rect(0, 0, info.logicalWidth, info.logicalHeight);
@@ -647,9 +648,9 @@ public class TaskRecordTests extends ActivityTestsBase {
final TaskRecord task1 = getTestTask();
final ActivityRecord activity1 = task1.getChildAt(0);
- assertEquals(task0.taskId,
+ assertEquals(task0.mTaskId,
ActivityRecord.getTaskForActivityLocked(activity0.appToken, false /* onlyRoot */));
- assertEquals(task1.taskId,
+ assertEquals(task1.mTaskId,
ActivityRecord.getTaskForActivityLocked(activity1.appToken, false /* onlyRoot */));
}
@@ -668,9 +669,9 @@ public class TaskRecordTests extends ActivityTestsBase {
// Add one more on top
final ActivityRecord activity2 = new ActivityBuilder(mService).setTask(task).build();
- assertEquals(task.taskId,
+ assertEquals(task.mTaskId,
ActivityRecord.getTaskForActivityLocked(activity0.appToken, true /* onlyRoot */));
- assertEquals(task.taskId,
+ assertEquals(task.mTaskId,
ActivityRecord.getTaskForActivityLocked(activity1.appToken, true /* onlyRoot */));
assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID,
ActivityRecord.getTaskForActivityLocked(activity2.appToken, true /* onlyRoot */));
@@ -691,9 +692,9 @@ public class TaskRecordTests extends ActivityTestsBase {
// Add one more on top
final ActivityRecord activity2 = new ActivityBuilder(mService).setTask(task).build();
- assertEquals(task.taskId,
+ assertEquals(task.mTaskId,
ActivityRecord.getTaskForActivityLocked(activity0.appToken, true /* onlyRoot */));
- assertEquals(task.taskId,
+ assertEquals(task.mTaskId,
ActivityRecord.getTaskForActivityLocked(activity1.appToken, true /* onlyRoot */));
assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID,
ActivityRecord.getTaskForActivityLocked(activity2.appToken, true /* onlyRoot */));
@@ -717,11 +718,11 @@ public class TaskRecordTests extends ActivityTestsBase {
// Add one more activity on top
final ActivityRecord activity2 = new ActivityBuilder(mService).setTask(task).build();
- assertEquals(task.taskId,
+ assertEquals(task.mTaskId,
ActivityRecord.getTaskForActivityLocked(activity0.appToken, false /* onlyRoot */));
- assertEquals(task.taskId,
+ assertEquals(task.mTaskId,
ActivityRecord.getTaskForActivityLocked(activity1.appToken, false /* onlyRoot */));
- assertEquals(task.taskId,
+ assertEquals(task.mTaskId,
ActivityRecord.getTaskForActivityLocked(activity2.appToken, false /* onlyRoot */));
}
@@ -831,8 +832,8 @@ public class TaskRecordTests extends ActivityTestsBase {
private TaskRecord createTaskRecord(int taskId) {
return new TaskRecord(mService, taskId, new Intent(), null, null, null,
ActivityBuilder.getDefaultComponent(), null, false, false, false, 0, 10050, null,
- new ArrayList<>(), 0, false, null, 0, 0, 0, 0, 0, null, 0, false, false, false, 0, 0
- );
+ new ArrayList<>(), 0, false, null, 0, 0, 0, 0, 0, null, 0, false, false, false, 0,
+ 0, null /*ActivityInfo*/, null /*_voiceSession*/, null /*_voiceInteractor*/);
}
private static class TestTaskRecordFactory extends TaskRecordFactory {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
index 3bedabc45c00..bebb3ba52bff 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
@@ -54,7 +54,7 @@ public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase {
mCache.putSnapshot(window.getTask(), createSnapshot());
assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
false /* restoreFromDisk */, false /* reducedResolution */));
- mCache.onAppRemoved(window.mAppToken);
+ mCache.onAppRemoved(window.mActivityRecord);
assertNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
false /* restoreFromDisk */, false /* reducedResolution */));
}
@@ -65,7 +65,7 @@ public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase {
mCache.putSnapshot(window.getTask(), createSnapshot());
assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
false /* restoreFromDisk */, false /* reducedResolution */));
- mCache.onAppDied(window.mAppToken);
+ mCache.onAppDied(window.mActivityRecord);
assertNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
false /* restoreFromDisk */, false /* reducedResolution */));
}
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 113f3c8e237c..3b11003f52e6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -48,14 +48,14 @@ public class TaskSnapshotControllerTest extends WindowTestsBase {
public void testGetClosingApps_closing() {
final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
"closingWindow");
- closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
+ closingWindow.mActivityRecord.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
true /* performLayout */, false /* isVoiceInteraction */);
- final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
- closingApps.add(closingWindow.mAppToken);
+ final ArraySet<ActivityRecord> closingApps = new ArraySet<>();
+ closingApps.add(closingWindow.mActivityRecord);
final ArraySet<Task> closingTasks = new ArraySet<>();
mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
assertEquals(1, closingTasks.size());
- assertEquals(closingWindow.mAppToken.getTask(), closingTasks.valueAt(0));
+ assertEquals(closingWindow.mActivityRecord.getTask(), closingTasks.valueAt(0));
}
@Test
@@ -64,12 +64,12 @@ public class TaskSnapshotControllerTest extends WindowTestsBase {
"closingWindow");
final WindowState openingWindow = createAppWindow(closingWindow.getTask(),
FIRST_APPLICATION_WINDOW, "openingWindow");
- closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
+ closingWindow.mActivityRecord.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
true /* performLayout */, false /* isVoiceInteraction */);
- openingWindow.mAppToken.commitVisibility(null, true /* visible */, TRANSIT_UNSET,
+ openingWindow.mActivityRecord.commitVisibility(null, true /* visible */, TRANSIT_UNSET,
true /* performLayout */, false /* isVoiceInteraction */);
- final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
- closingApps.add(closingWindow.mAppToken);
+ final ArraySet<ActivityRecord> closingApps = new ArraySet<>();
+ closingApps.add(closingWindow.mActivityRecord);
final ArraySet<Task> closingTasks = new ArraySet<>();
mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
assertEquals(0, closingTasks.size());
@@ -79,13 +79,13 @@ public class TaskSnapshotControllerTest extends WindowTestsBase {
public void testGetClosingApps_skipClosingAppsSnapshotTasks() {
final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
"closingWindow");
- closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
+ closingWindow.mActivityRecord.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
true /* performLayout */, false /* isVoiceInteraction */);
- final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
- closingApps.add(closingWindow.mAppToken);
+ final ArraySet<ActivityRecord> closingApps = new ArraySet<>();
+ closingApps.add(closingWindow.mActivityRecord);
final ArraySet<Task> closingTasks = new ArraySet<>();
mWm.mTaskSnapshotController.addSkipClosingAppSnapshotTasks(
- Sets.newArraySet(closingWindow.mAppToken.getTask()));
+ Sets.newArraySet(closingWindow.mActivityRecord.getTask()));
mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
assertEquals(0, closingTasks.size());
}
@@ -94,7 +94,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase {
public void testGetSnapshotMode() {
final WindowState disabledWindow = createWindow(null,
FIRST_APPLICATION_WINDOW, mDisplayContent, "disabledWindow");
- disabledWindow.mAppToken.setDisablePreviewScreenshots(true);
+ disabledWindow.mActivityRecord.setDisablePreviewScreenshots(true);
assertEquals(SNAPSHOT_MODE_APP_THEME,
mWm.mTaskSnapshotController.getSnapshotMode(disabledWindow.getTask()));
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
index 74ccccca43bb..a66c79ced83c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
@@ -32,15 +32,17 @@ import androidx.test.filters.SmallTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Tests for the {@link DisplayContent.TaskStackContainers} container in {@link DisplayContent}.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:TaskStackContainersTests
+ * atest WmTests:TaskStackContainersTests
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class TaskStackContainersTests extends WindowTestsBase {
private TaskStack mPinnedStack;
@@ -52,8 +54,8 @@ public class TaskStackContainersTests extends WindowTestsBase {
// Stack should contain visible app window to be considered visible.
final Task pinnedTask = createTaskInStack(mPinnedStack, 0 /* userId */);
assertFalse(mPinnedStack.isVisible());
- final WindowTestUtils.TestAppWindowToken pinnedApp =
- WindowTestUtils.createTestAppWindowToken(mDisplayContent);
+ final ActivityRecord pinnedApp =
+ WindowTestUtils.createTestActivityRecord(mDisplayContent);
pinnedTask.addChild(pinnedApp, 0 /* addPos */);
assertTrue(mPinnedStack.isVisible());
}
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 70ed62a4a11e..2fc03c7f13d3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
@@ -21,12 +21,15 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
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 org.junit.Assert.assertEquals;
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.any;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
@@ -34,15 +37,17 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Tests for the {@link TaskStack} class.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:TaskStackTests
+ * atest WmTests:TaskStackTests
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class TaskStackTests extends WindowTestsBase {
@Test
@@ -66,19 +71,19 @@ public class TaskStackTests extends WindowTestsBase {
public void testClosingAppDifferentStackOrientation() {
final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
final Task task1 = createTaskInStack(stack, 0 /* userId */);
- WindowTestUtils.TestAppWindowToken appWindowToken1 =
- WindowTestUtils.createTestAppWindowToken(mDisplayContent);
- task1.addChild(appWindowToken1, 0);
- appWindowToken1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ ActivityRecord activity1 =
+ WindowTestUtils.createTestActivityRecord(mDisplayContent);
+ task1.addChild(activity1, 0);
+ activity1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
final Task task2 = createTaskInStack(stack, 1 /* userId */);
- WindowTestUtils.TestAppWindowToken appWindowToken2 =
- WindowTestUtils.createTestAppWindowToken(mDisplayContent);
- task2.addChild(appWindowToken2, 0);
- appWindowToken2.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
+ ActivityRecord activity2=
+ WindowTestUtils.createTestActivityRecord(mDisplayContent);
+ task2.addChild(activity2, 0);
+ activity2.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
assertEquals(SCREEN_ORIENTATION_PORTRAIT, stack.getOrientation());
- mDisplayContent.mClosingApps.add(appWindowToken2);
+ mDisplayContent.mClosingApps.add(activity2);
assertEquals(SCREEN_ORIENTATION_LANDSCAPE, stack.getOrientation());
}
@@ -86,16 +91,16 @@ public class TaskStackTests extends WindowTestsBase {
public void testMoveTaskToBackDifferentStackOrientation() {
final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
final Task task1 = createTaskInStack(stack, 0 /* userId */);
- WindowTestUtils.TestAppWindowToken appWindowToken1 =
- WindowTestUtils.createTestAppWindowToken(mDisplayContent);
- task1.addChild(appWindowToken1, 0);
- appWindowToken1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ ActivityRecord activity1 =
+ WindowTestUtils.createTestActivityRecord(mDisplayContent);
+ task1.addChild(activity1, 0);
+ activity1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
final Task task2 = createTaskInStack(stack, 1 /* userId */);
- WindowTestUtils.TestAppWindowToken appWindowToken2 =
- WindowTestUtils.createTestAppWindowToken(mDisplayContent);
- task2.addChild(appWindowToken2, 0);
- appWindowToken2.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
+ ActivityRecord activity2 =
+ WindowTestUtils.createTestActivityRecord(mDisplayContent);
+ task2.addChild(activity2, 0);
+ activity2.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
assertEquals(SCREEN_ORIENTATION_PORTRAIT, stack.getOrientation());
task2.setSendingToBottom(true);
@@ -117,7 +122,7 @@ public class TaskStackTests extends WindowTestsBase {
@Test
public void testRemoveContainer() {
final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack);
+ final Task task = createTaskInStack(stack, 0 /* userId */);
assertNotNull(stack);
assertNotNull(task);
@@ -133,10 +138,10 @@ public class TaskStackTests extends WindowTestsBase {
@Test
public void testRemoveContainer_deferRemoval() {
final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack);
+ final Task task = createTaskInStack(stack, 0 /* userId */);
// Stack removal is deferred if one of its child is animating.
- task.setLocalIsAnimating(true);
+ doReturn(true).when(task).isSelfAnimating();
stack.removeIfPossible();
// For the case of deferred removal the task controller will still be connected to the its
@@ -149,15 +154,13 @@ public class TaskStackTests extends WindowTestsBase {
// After removing, the task will be isolated.
assertNull(task.getParent());
assertEquals(0, task.getChildCount());
- assertNull(task.getController());
}
@Test
public void testReparent() {
// Create first stack on primary display.
final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTask task1 = WindowTestUtils.createTestTask(stack1);
- task1.mOnDisplayChangedCalled = false;
+ final Task task1 = createTaskInStack(stack1, 0 /* userId */);
// Create second display and put second stack on it.
final DisplayContent dc = createNewDisplay();
@@ -169,20 +172,15 @@ public class TaskStackTests extends WindowTestsBase {
final int stack1PositionInParent = stack1.getParent().mChildren.indexOf(stack1);
final int stack2PositionInParent = stack1.getParent().mChildren.indexOf(stack2);
assertEquals(stack1PositionInParent, stack2PositionInParent + 1);
- assertTrue(task1.mOnDisplayChangedCalled);
+ verify(task1, times(1)).onDisplayChanged(any());
}
@Test
public void testStackOutset() {
final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
final int stackOutset = 10;
- // Clear the handler and hold the lock for mock, to prevent multi-thread issue.
- waitUntilHandlersIdle();
- synchronized (mWm.mGlobalLock) {
- spyOn(stack);
-
- doReturn(stackOutset).when(stack).getStackOutset();
- }
+ spyOn(stack);
+ doReturn(stackOutset).when(stack).getStackOutset();
final Rect stackBounds = new Rect(200, 200, 800, 1000);
// Update surface position and size by the given bounds.
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 41842010d3a3..4dfa26644fa9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -16,11 +16,16 @@
package com.android.server.wm;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
import static org.junit.Assert.assertEquals;
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.any;
import android.graphics.Point;
import android.graphics.Rect;
@@ -29,59 +34,61 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Test class for {@link Task}.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:TaskTests
+ * atest WmTests:TaskTests
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class TaskTests extends WindowTestsBase {
@Test
public void testRemoveContainer() {
final TaskStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1);
- final WindowTestUtils.TestAppWindowToken appToken =
- WindowTestUtils.createAppWindowTokenInTask(mDisplayContent, task);
+ final Task task = createTaskInStack(stackController1, 0 /* userId */);
+ final ActivityRecord activity =
+ WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
task.removeIfPossible();
// Assert that the container was removed.
assertNull(task.getParent());
assertEquals(0, task.getChildCount());
- assertNull(appToken.getParent());
+ assertNull(activity.getParent());
}
@Test
public void testRemoveContainer_deferRemoval() {
final TaskStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1);
- final WindowTestUtils.TestAppWindowToken appToken =
- WindowTestUtils.createAppWindowTokenInTask(mDisplayContent, task);
+ final Task task = createTaskInStack(stackController1, 0 /* userId */);
+ final ActivityRecord activity =
+ WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
- task.mShouldDeferRemoval = true;
+ doReturn(true).when(task).shouldDeferRemoval();
task.removeIfPossible();
// For the case of deferred removal the task will still be connected to the its app token
// until the task window container is removed.
assertNotNull(task.getParent());
assertNotEquals(0, task.getChildCount());
- assertNotNull(appToken.getParent());
+ assertNotNull(activity.getParent());
task.removeImmediately();
assertNull(task.getParent());
assertEquals(0, task.getChildCount());
- assertNull(appToken.getParent());
+ assertNull(activity.getParent());
}
@Test
public void testReparent() {
final TaskStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1);
+ final Task task = createTaskInStack(stackController1, 0 /* userId */);
final TaskStack stackController2 = createTaskStackOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTask task2 = WindowTestUtils.createTestTask(stackController2);
+ final Task task2 = createTaskInStack(stackController2, 0 /* userId */);
boolean gotException = false;
try {
@@ -102,34 +109,33 @@ public class TaskTests extends WindowTestsBase {
task.reparent(stackController2, 0, false/* moveParents */);
assertEquals(stackController2, task.getParent());
- assertEquals(0, task.positionInParent());
- assertEquals(1, task2.positionInParent());
+ assertEquals(0, task.getParent().mChildren.indexOf(task));
+ assertEquals(1, task2.getParent().mChildren.indexOf(task2));
}
@Test
public void testReparent_BetweenDisplays() {
// Create first stack on primary display.
final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack1);
- task.mOnDisplayChangedCalled = false;
+ 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 TaskStack stack2 = createTaskStackOnDisplay(dc);
- final WindowTestUtils.TestTask task2 = WindowTestUtils.createTestTask(stack2);
+ final Task task2 = createTaskInStack(stack2, 0 /* userId */);
// Reparent and check state
task.reparent(stack2, 0, false /* moveParents */);
assertEquals(stack2, task.getParent());
- assertEquals(0, task.positionInParent());
- assertEquals(1, task2.positionInParent());
- assertTrue(task.mOnDisplayChangedCalled);
+ assertEquals(0, task.getParent().mChildren.indexOf(task));
+ assertEquals(1, task2.getParent().mChildren.indexOf(task2));
+ verify(task, times(1)).onDisplayChanged(any());
}
@Test
public void testBounds() {
final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack1);
+ final Task task = createTaskInStack(stack1, 0 /* userId */);
// Check that setting bounds also updates surface position
Rect bounds = new Rect(10, 10, 100, 200);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java b/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java
index 778f0ca3c782..9c3ff654ddf0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java
@@ -22,8 +22,11 @@ import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static org.mockito.ArgumentMatchers.any;
+
import android.hardware.display.DisplayManagerGlobal;
import android.view.Display;
import android.view.DisplayInfo;
@@ -85,6 +88,10 @@ class TestActivityDisplay extends ActivityDisplay {
displayRotation.setRotation(rotation);
return true;
}).when(displayRotation).updateRotationUnchecked(anyBoolean());
+
+ final InputMonitor inputMonitor = mDisplayContent.getInputMonitor();
+ spyOn(inputMonitor);
+ doNothing().when(inputMonitor).resumeDispatchingLw(any());
}
@SuppressWarnings("TypeParameterUnusedInFormals")
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index a7586810a824..6a9413716a89 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -111,4 +111,12 @@ public class TestIWindow extends IWindow.Stub {
@Override
public void dispatchPointerCaptureChanged(boolean hasCapture) {
}
+
+ @Override
+ public void showInsets(int types, boolean fromIme) throws RemoteException {
+ }
+
+ @Override
+ public void hideInsets(int types, boolean fromIme) throws RemoteException {
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index bb89446dd6e6..aa6c14e81b43 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -117,16 +117,16 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
int logo, int windowFlags, Configuration overrideConfig, int displayId) {
final com.android.server.wm.WindowState window;
- final AppWindowToken atoken;
+ final ActivityRecord activity;
final WindowManagerService wm = mWmSupplier.get();
synchronized (wm.mGlobalLock) {
- atoken = wm.mRoot.getAppWindowToken(appToken);
+ activity = wm.mRoot.getActivityRecord(appToken);
IWindow iWindow = mock(IWindow.class);
doReturn(mock(IBinder.class)).when(iWindow).asBinder();
- window = WindowTestsBase.createWindow(null, TYPE_APPLICATION_STARTING, atoken,
+ window = WindowTestsBase.createWindow(null, TYPE_APPLICATION_STARTING, activity,
"Starting window", 0 /* ownerId */, false /* internalWindows */, wm,
mock(Session.class), iWindow, mPowerManagerWrapper);
- atoken.startingWindow = window;
+ activity.startingWindow = window;
}
if (mRunnableWhenAddingSplashScreen != null) {
mRunnableWhenAddingSplashScreen.run();
@@ -134,8 +134,8 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
}
return () -> {
synchronized (wm.mGlobalLock) {
- atoken.removeChild(window);
- atoken.startingWindow = null;
+ activity.removeChild(window);
+ activity.startingWindow = null;
}
};
}
@@ -167,12 +167,13 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
}
@Override
- public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
+ public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event,
+ int policyFlags) {
return 0;
}
@Override
- public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
+ public KeyEvent dispatchUnhandledKey(IBinder focusedToken, KeyEvent event, int policyFlags) {
return null;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
index a91daf0c7647..e8a4e90b1aa1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
@@ -24,15 +24,17 @@ import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Test class for {@link AppTransition}.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:UnknownAppVisibilityControllerTest
+ * atest WmTests:UnknownAppVisibilityControllerTest
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class UnknownAppVisibilityControllerTest extends WindowTestsBase {
@Before
@@ -42,45 +44,45 @@ public class UnknownAppVisibilityControllerTest extends WindowTestsBase {
@Test
public void testFlow() {
- final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
- mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token);
- mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(token);
- mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(token);
+ final ActivityRecord activity = WindowTestUtils.createTestActivityRecord(mDisplayContent);
+ mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(activity);
+ mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(activity);
+ mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(activity);
// Make sure our handler processed the message.
- mWm.mH.runWithScissors(() -> { }, 0);
+ waitHandlerIdle(mWm.mH);
assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved());
}
@Test
public void testMultiple() {
- final AppWindowToken token1 = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
- final AppWindowToken token2 = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
- mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token1);
- mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(token1);
- mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token2);
- mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(token1);
- mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(token2);
- mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(token2);
+ final ActivityRecord activity1 = WindowTestUtils.createTestActivityRecord(mDisplayContent);
+ final ActivityRecord activity2 = WindowTestUtils.createTestActivityRecord(mDisplayContent);
+ mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(activity1);
+ mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(activity1);
+ mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(activity2);
+ mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(activity1);
+ mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(activity2);
+ mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(activity2);
// Make sure our handler processed the message.
- mWm.mH.runWithScissors(() -> { }, 0);
+ waitHandlerIdle(mWm.mH);
assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved());
}
@Test
public void testClear() {
- final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
- mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token);
+ final ActivityRecord activity = WindowTestUtils.createTestActivityRecord(mDisplayContent);
+ mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(activity);
mDisplayContent.mUnknownAppVisibilityController.clear();
assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved());
}
@Test
public void testAppRemoved() {
- final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
- mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token);
- mDisplayContent.mUnknownAppVisibilityController.appRemovedOrHidden(token);
+ final ActivityRecord activity = WindowTestUtils.createTestActivityRecord(mDisplayContent);
+ mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(activity);
+ mDisplayContent.mUnknownAppVisibilityController.appRemovedOrHidden(activity);
assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved());
}
}
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 6249bde3cea6..14d8a9ddab67 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -30,6 +30,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Tests for the {@link WallpaperController} class.
@@ -39,37 +40,36 @@ import org.junit.Test;
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class WallpaperControllerTests extends WindowTestsBase {
@Test
public void testWallpaperScreenshot() {
WindowSurfaceController windowSurfaceController = mock(WindowSurfaceController.class);
- synchronized (mWm.mGlobalLock) {
- // No wallpaper
- final DisplayContent dc = createNewDisplay();
- assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
+ // No wallpaper
+ final DisplayContent dc = createNewDisplay();
+ assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
- // No wallpaper WSA Surface
- WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class),
- true, dc, true /* ownerCanManageAppTokens */);
- WindowState wallpaperWindow = createWindow(null /* parent */, TYPE_WALLPAPER,
- wallpaperWindowToken, "wallpaperWindow");
- assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
+ // No wallpaper WSA Surface
+ WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class),
+ true, dc, true /* ownerCanManageAppTokens */);
+ WindowState wallpaperWindow = createWindow(null /* parent */, TYPE_WALLPAPER,
+ wallpaperWindowToken, "wallpaperWindow");
+ assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
- // Wallpaper with not visible WSA surface.
- wallpaperWindow.mWinAnimator.mSurfaceController = windowSurfaceController;
- wallpaperWindow.mWinAnimator.mLastAlpha = 1;
- assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
+ // Wallpaper with not visible WSA surface.
+ wallpaperWindow.mWinAnimator.mSurfaceController = windowSurfaceController;
+ wallpaperWindow.mWinAnimator.mLastAlpha = 1;
+ assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
- when(windowSurfaceController.getShown()).thenReturn(true);
+ when(windowSurfaceController.getShown()).thenReturn(true);
- // Wallpaper with WSA alpha set to 0.
- wallpaperWindow.mWinAnimator.mLastAlpha = 0;
- assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
+ // Wallpaper with WSA alpha set to 0.
+ wallpaperWindow.mWinAnimator.mLastAlpha = 0;
+ assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
- // Wallpaper window with WSA Surface
- wallpaperWindow.mWinAnimator.mLastAlpha = 1;
- assertTrue(dc.mWallpaperController.canScreenshotWallpaper());
- }
+ // Wallpaper window with WSA Surface
+ wallpaperWindow.mWinAnimator.mLastAlpha = 1;
+ assertTrue(dc.mWallpaperController.canScreenshotWallpaper());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java
index 0330de8d2c63..bfc07419fd46 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java
@@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.graphics.Point;
@@ -50,7 +51,7 @@ import org.junit.Test;
@Presubmit
public class WindowAnimationSpecTest {
private final SurfaceControl mSurfaceControl = mock(SurfaceControl.class);
- private final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class);
+ private final SurfaceControl.Transaction mTransaction = spy(StubTransaction.class);
private final Animation mAnimation = mock(Animation.class);
private final Rect mStackBounds = new Rect(0, 0, 10, 10);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java
deleted file mode 100644
index fc7863524240..000000000000
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java
+++ /dev/null
@@ -1,109 +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.server.wm;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.content.res.Configuration.EMPTY;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import android.content.res.Configuration;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-
-/**
- * Test class for {@link WindowContainerController}.
- *
- * Build/Install/Run:
- * atest WmTests:WindowContainerControllerTests
- */
-@SmallTest
-@Presubmit
-public class WindowContainerControllerTests extends WindowTestsBase {
-
- @Test
- public void testCreation() {
- final WindowContainerController controller = new WindowContainerController<>(null, mWm);
- final WindowContainer container = new WindowContainer(mWm);
-
- container.setController(controller);
- assertEquals(controller, container.getController());
- assertEquals(controller.mContainer, container);
- }
-
- @Test
- public void testSetContainer() {
- final WindowContainerController controller = new WindowContainerController<>(null, mWm);
- final WindowContainer container = new WindowContainer(mWm);
-
- controller.setContainer(container);
- assertEquals(controller.mContainer, container);
-
- // Assert we can't change the container to another one once set
- boolean gotException = false;
- try {
- controller.setContainer(new WindowContainer(mWm));
- } catch (IllegalArgumentException e) {
- gotException = true;
- }
- assertTrue(gotException);
-
- // Assert that we can set the container to null.
- controller.setContainer(null);
- assertNull(controller.mContainer);
- }
-
- @Test
- public void testRemoveContainer() {
- final WindowContainerController controller = new WindowContainerController<>(null, mWm);
- final WindowContainer container = new WindowContainer(mWm);
-
- controller.setContainer(container);
- assertEquals(controller.mContainer, container);
-
- controller.removeContainer();
- assertNull(controller.mContainer);
- }
-
- @Test
- public void testOnOverrideConfigurationChanged() {
- final WindowContainerController controller = new WindowContainerController<>(null, mWm);
- final WindowContainer container = new WindowContainer(mWm);
-
- controller.setContainer(container);
- assertEquals(controller.mContainer, container);
- assertEquals(EMPTY, container.getRequestedOverrideConfiguration());
-
- final Configuration config = new Configuration();
- config.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
- config.windowConfiguration.setAppBounds(10, 10, 10, 10);
-
- // Assert that the config change through the controller is propagated to the container.
- controller.onRequestedOverrideConfigurationChanged(config);
- assertEquals(config, container.getRequestedOverrideConfiguration());
-
- // Assert the container configuration isn't changed after removal from the controller.
- controller.removeContainer();
- controller.onRequestedOverrideConfigurationChanged(EMPTY);
- assertEquals(config, container.getRequestedOverrideConfiguration());
- }
-}
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 921f105d1d25..8117ff601a8f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -216,44 +216,6 @@ public class WindowContainerTests extends WindowTestsBase {
}
@Test
- public void testRemoveImmediately_WithController() {
- final WindowContainer container = new WindowContainer(mWm);
- final WindowContainerController controller = new WindowContainerController<>(null, mWm);
-
- container.setController(controller);
- assertEquals(controller, container.getController());
- assertEquals(container, controller.mContainer);
-
- container.removeImmediately();
- assertNull(container.getController());
- assertNull(controller.mContainer);
- }
-
- @Test
- public void testSetController() {
- final WindowContainerController controller = new WindowContainerController<>(null, mWm);
- final WindowContainer container = new WindowContainer(mWm);
-
- container.setController(controller);
- assertEquals(controller, container.getController());
- assertEquals(container, controller.mContainer);
-
- // Assert we can't change the controller to another one once set
- boolean gotException = false;
- try {
- container.setController(new WindowContainerController<>(null, mWm));
- } catch (IllegalArgumentException e) {
- gotException = true;
- }
- assertTrue(gotException);
-
- // Assert that we can set the controller to null.
- container.setController(null);
- assertNull(container.getController());
- assertNull(controller.mContainer);
- }
-
- @Test
public void testAddChildByIndex() {
final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
final TestWindowContainer root = builder.setLayer(0).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index 60cefe858c54..8cd97cb8a344 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -16,24 +16,24 @@
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_SPLIT_SCREEN_SECONDARY;
import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.DisplayCutout.fromBoundingRect;
-import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+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 org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.DisplayInfo;
import android.view.Gravity;
-import android.view.IWindow;
import android.view.WindowManager;
import androidx.test.filters.FlakyTest;
@@ -43,41 +43,25 @@ import com.android.server.wm.utils.WmDisplayCutout;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mockito;
/**
* Tests for the {@link WindowState#computeFrameLw} method and other window frame machinery.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:WindowFrameTests
+ * atest WmTests:WindowFrameTests
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class WindowFrameTests extends WindowTestsBase {
- private final IWindow mIWindow = new TestIWindow();
private final Rect mEmptyRect = new Rect();
private DisplayContent mTestDisplayContent;
- static class FrameTestWindowState extends WindowState {
- boolean mDockedResizingForTest = false;
- FrameTestWindowState(WindowManagerService wm, IWindow iWindow, WindowToken windowToken,
- WindowManager.LayoutParams attrs) {
- super(wm, mock(Session.class), iWindow, windowToken, null, 0, 0, attrs, 0, 0,
- false /* ownerCanAddInternalSystemWindow */);
- }
-
- @Override
- boolean isDockedResizing() {
- return mDockedResizingForTest;
- }
- }
-
- TaskStack mStubStack;
-
@Before
public void setUp() throws Exception {
- mStubStack = mock(TaskStack.class);
DisplayInfo testDisplayInfo = new DisplayInfo(mDisplayInfo);
testDisplayInfo.displayCutout = null;
mTestDisplayContent = createNewDisplay(testDisplayInfo);
@@ -127,8 +111,7 @@ public class WindowFrameTests extends WindowTestsBase {
expectedRect.bottom);
}
- private void assertPolicyCrop(
- FrameTestWindowState w, int left, int top, int right, int bottom) {
+ private void assertPolicyCrop(WindowState w, int left, int top, int right, int bottom) {
Rect policyCrop = new Rect();
w.calculatePolicyCrop(policyCrop);
assertRect(policyCrop, left, top, right, bottom);
@@ -137,7 +120,7 @@ public class WindowFrameTests extends WindowTestsBase {
@Test
public void testLayoutInFullscreenTaskInsets() {
// fullscreen task doesn't use bounds for computeFrame
- WindowState w = createWindow(MATCH_PARENT, MATCH_PARENT);
+ WindowState w = createWindow();
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
final int bottomContentInset = 100;
@@ -194,7 +177,7 @@ public class WindowFrameTests extends WindowTestsBase {
@Test
public void testLayoutInFullscreenTaskNoInsets() {
// fullscreen task doesn't use bounds for computeFrame
- WindowState w = createWindow(MATCH_PARENT, MATCH_PARENT);
+ WindowState w = createWindow();
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
// With no insets or system decor all the frames incoming from PhoneWindowManager
@@ -276,16 +259,16 @@ public class WindowFrameTests extends WindowTestsBase {
final int logicalWidth = displayInfo.logicalWidth;
final int logicalHeight = displayInfo.logicalHeight;
- final int taskLeft = logicalWidth / 4;
- final int taskTop = logicalHeight / 4;
- final int taskRight = logicalWidth / 4 * 3;
- final int taskBottom = logicalHeight / 4 * 3;
- final Rect taskBounds = new Rect(taskLeft, taskTop, taskRight, taskBottom);
- WindowState w = createWindow(MATCH_PARENT, MATCH_PARENT);
+ final Rect taskBounds = new Rect(
+ logicalWidth / 4, logicalHeight / 4, logicalWidth / 4 * 3, logicalHeight / 4 * 3);
+ WindowState w = createWindow();
final Task task = w.getTask();
// Use split-screen because it is non-fullscreen, but also not floating
- task.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- task.setBounds(taskBounds);
+ task.mTaskRecord.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ task.mTaskRecord.setBounds(taskBounds);
+ // The bounds we are requesting might be different from what the system resolved based on
+ // other factors.
+ final Rect resolvedTaskBounds = task.getBounds();
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
@@ -294,8 +277,8 @@ public class WindowFrameTests extends WindowTestsBase {
w.computeFrameLw();
// For non fullscreen tasks the containing frame is based off the
// task bounds not the parent frame.
- assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
- assertContentFrame(w, taskBounds);
+ assertEquals(resolvedTaskBounds, w.getFrameLw());
+ assertContentFrame(w, resolvedTaskBounds);
assertContentInset(w, 0, 0, 0, 0);
pf.set(0, 0, logicalWidth, logicalHeight);
@@ -305,36 +288,38 @@ public class WindowFrameTests extends WindowTestsBase {
final Rect cf = new Rect(0, 0, cfRight, cfBottom);
windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
w.computeFrameLw();
- assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
- int contentInsetRight = taskRight - cfRight;
- int contentInsetBottom = taskBottom - cfBottom;
+ assertEquals(resolvedTaskBounds, w.getFrameLw());
+ int contentInsetRight = resolvedTaskBounds.right - cfRight;
+ int contentInsetBottom = resolvedTaskBounds.bottom - cfBottom;
assertContentInset(w, 0, 0, contentInsetRight, contentInsetBottom);
- assertContentFrame(w, new Rect(taskLeft, taskTop, taskRight - contentInsetRight,
- taskBottom - contentInsetBottom));
+ assertContentFrame(w, new Rect(resolvedTaskBounds.left, resolvedTaskBounds.top,
+ resolvedTaskBounds.right - contentInsetRight,
+ resolvedTaskBounds.bottom - contentInsetBottom));
pf.set(0, 0, logicalWidth, logicalHeight);
// If we set displayed bounds, the insets will be computed with the main task bounds
// but the frame will be positioned according to the displayed bounds.
final int insetLeft = logicalWidth / 5;
final int insetTop = logicalHeight / 5;
- final int insetRight = insetLeft + (taskRight - taskLeft);
- final int insetBottom = insetTop + (taskBottom - taskTop);
- task.setOverrideDisplayedBounds(taskBounds);
- task.setBounds(insetLeft, insetTop, insetRight, insetBottom);
+ final int insetRight = insetLeft + (resolvedTaskBounds.right - resolvedTaskBounds.left);
+ final int insetBottom = insetTop + (resolvedTaskBounds.bottom - resolvedTaskBounds.top);
+ task.mTaskRecord.setDisplayedBounds(resolvedTaskBounds);
+ task.mTaskRecord.setBounds(insetLeft, insetTop, insetRight, insetBottom);
windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
w.computeFrameLw();
- assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
+ assertEquals(resolvedTaskBounds, w.getFrameLw());
contentInsetRight = insetRight - cfRight;
contentInsetBottom = insetBottom - cfBottom;
assertContentInset(w, 0, 0, contentInsetRight, contentInsetBottom);
- assertContentFrame(w, new Rect(taskLeft, taskTop, taskRight - contentInsetRight,
- taskBottom - contentInsetBottom));
+ assertContentFrame(w, new Rect(resolvedTaskBounds.left, resolvedTaskBounds.top,
+ resolvedTaskBounds.right - contentInsetRight,
+ resolvedTaskBounds.bottom - contentInsetBottom));
}
@Test
@FlakyTest(bugId = 130388666)
public void testCalculatePolicyCrop() {
- final FrameTestWindowState w = createWindow(MATCH_PARENT, MATCH_PARENT);
+ final WindowState w = createWindow();
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
final DisplayInfo displayInfo = w.getDisplayContent().getDisplayInfo();
@@ -379,7 +364,7 @@ public class WindowFrameTests extends WindowTestsBase {
// to the computed window frame.
assertPolicyCrop(w, 0, 0, logicalWidth / 2, logicalHeight / 2);
- w.mDockedResizingForTest = true;
+ doReturn(true).when(w).isDockedResizing();
// But if we are docked resizing it won't be, however we will still be
// shrunk to the decor frame and the display.
assertPolicyCrop(w, 0, 0,
@@ -400,7 +385,7 @@ public class WindowFrameTests extends WindowTestsBase {
final int taskRight = logicalWidth / 4 * 3;
final int taskBottom = logicalHeight / 4 * 3;
final Rect taskBounds = new Rect(taskLeft, taskTop, taskRight, taskBottom);
- WindowState w = createWindow(MATCH_PARENT, MATCH_PARENT);
+ WindowState w = createWindow();
final Task task = w.getTask();
// Use split-screen because it is non-fullscreen, but also not floating
task.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
@@ -420,10 +405,10 @@ public class WindowFrameTests extends WindowTestsBase {
// Now simulate switch to fullscreen for letterboxed app.
final int xInset = logicalWidth / 10;
final Rect cf = new Rect(xInset, 0, logicalWidth - xInset, logicalHeight);
- Configuration config = new Configuration(w.mAppToken.getRequestedOverrideConfiguration());
+ Configuration config = new Configuration(w.mActivityRecord.getRequestedOverrideConfiguration());
config.windowConfiguration.setBounds(cf);
config.windowConfiguration.setAppBounds(cf);
- w.mAppToken.onRequestedOverrideConfigurationChanged(config);
+ w.mActivityRecord.onRequestedOverrideConfigurationChanged(config);
pf.set(0, 0, logicalWidth, logicalHeight);
task.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
task.setBounds(null);
@@ -438,7 +423,7 @@ public class WindowFrameTests extends WindowTestsBase {
@FlakyTest(bugId = 130388666)
public void testDisplayCutout() {
// Regular fullscreen task and window
- WindowState w = createWindow(MATCH_PARENT, MATCH_PARENT);
+ WindowState w = createWindow();
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
final Rect pf = new Rect(0, 0, 1000, 2000);
@@ -462,7 +447,7 @@ public class WindowFrameTests extends WindowTestsBase {
@FlakyTest(bugId = 130388666)
public void testDisplayCutout_tempDisplayedBounds() {
// Regular fullscreen task and window
- WindowState w = createWindow(MATCH_PARENT, MATCH_PARENT);
+ WindowState w = createWindow();
final Task task = w.getTask();
task.setBounds(new Rect(0, 0, 1000, 2000));
task.setOverrideDisplayedBounds(new Rect(0, -500, 1000, 1500));
@@ -487,11 +472,12 @@ public class WindowFrameTests extends WindowTestsBase {
@Test
public void testFreeformContentInsets() {
+ removeGlobalMinSizeRestriction();
// fullscreen task doesn't use bounds for computeFrame
- WindowState w = createWindow(MATCH_PARENT, MATCH_PARENT);
+ WindowState w = createWindow();
final Task task = w.getTask();
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
- task.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ task.mTaskRecord.setWindowingMode(WINDOWING_MODE_FREEFORM);
DisplayContent dc = mTestDisplayContent;
dc.mInputMethodTarget = w;
@@ -513,7 +499,7 @@ public class WindowFrameTests extends WindowTestsBase {
// First check that it only gets moved up enough to show window.
final Rect winRect = new Rect(200, 200, 300, 500);
- task.setBounds(winRect);
+ task.mTaskRecord.setBounds(winRect);
w.getWindowFrames().setFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
w.computeFrameLw();
@@ -525,7 +511,7 @@ public class WindowFrameTests extends WindowTestsBase {
// Now check that it won't get moved beyond the top and then has appropriate insets
winRect.bottom = 600;
- task.setBounds(winRect);
+ task.mTaskRecord.setBounds(winRect);
w.setBounds(winRect);
w.getWindowFrames().setFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
w.computeFrameLw();
@@ -542,16 +528,9 @@ public class WindowFrameTests extends WindowTestsBase {
assertEquals(winRect, w.getFrameLw());
}
- private FrameTestWindowState createWindow(int width, int height) {
- final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION);
- attrs.width = width;
- attrs.height = height;
-
- AppWindowToken token = createAppWindowToken(mTestDisplayContent,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
-
- FrameTestWindowState ws = new FrameTestWindowState(mWm, mIWindow, token, attrs);
- token.addWindow(ws);
+ private WindowState createWindow() {
+ final WindowState ws = createWindow(null, TYPE_APPLICATION, mTestDisplayContent, "WindowFrameTests");
+ spyOn(ws);
return ws;
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerGlobalLockRule.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerGlobalLockRule.java
index e0c314fd572e..c5f13e05843b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerGlobalLockRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerGlobalLockRule.java
@@ -27,6 +27,9 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -59,34 +62,41 @@ class WindowManagerGlobalLockRule implements WindowTestRunner.MethodWrapper {
return base;
}
- void waitUntilHandlersIdle() {
- if (!mIsLocked) {
- mSystemServicesTestRule.waitUntilWindowManagerHandlersIdle();
- return;
- }
-
- waitForLocked(mSystemServicesTestRule::waitUntilWindowManagerHandlersIdle);
+ boolean runWithScissors(Handler handler, Runnable r, long timeout) {
+ return waitForLocked(() -> handler.runWithScissors(r, timeout));
}
- void runWithScissors(Handler handler, Runnable r, long timeout) {
- if (!mIsLocked) {
- handler.runWithScissors(r, timeout);
- return;
- }
-
- waitForLocked(() -> handler.runWithScissors(r, timeout));
+ void waitForLocked(Runnable r) {
+ waitForLocked(() -> {
+ r.run();
+ return null;
+ });
}
/**
* If the test holds the lock, we need to invoke {@link Object#wait} to release it so other
* threads won't be blocked when we are waiting.
*/
- private void waitForLocked(Runnable r) {
+ <T> T waitForLocked(Callable<T> callable) {
+ if (!mIsLocked) {
+ try {
+ return callable.call();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
final Object lock = mSystemServicesTestRule.getWindowManagerService().mGlobalLock;
final AtomicBoolean done = new AtomicBoolean(false);
+ final List<T> result = Arrays.asList((T) null);
+ final Exception[] exception = { null };
AsyncTask.SERIAL_EXECUTOR.execute(() -> {
- r.run();
+ try {
+ result.set(0, callable.call());
+ } catch (Exception e) {
+ exception[0] = e;
+ }
synchronized (lock) {
lock.notifyAll();
done.set(true);
@@ -101,6 +111,11 @@ class WindowManagerGlobalLockRule implements WindowTestRunner.MethodWrapper {
}
}
}
+ if (exception[0] != null) {
+ throw new RuntimeException(exception[0]);
+ }
+
+ return result.get(0);
}
/** Wraps methods annotated with {@link org.junit.Test}. */
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index 8c56ffaa6314..cc90ca1e2556 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -26,13 +26,13 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
import android.content.pm.ApplicationInfo;
import android.platform.test.annotations.Presubmit;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mockito;
@@ -43,6 +43,7 @@ import org.mockito.Mockito;
* atest WmTests:WindowProcessControllerTests
*/
@Presubmit
+@RunWith(WindowTestRunner.class)
public class WindowProcessControllerTests extends ActivityTestsBase {
WindowProcessController mWpc;
@@ -97,7 +98,7 @@ public class WindowProcessControllerTests extends ActivityTestsBase {
public void testSetRunningRecentsAnimation() {
mWpc.setRunningRecentsAnimation(true);
mWpc.setRunningRecentsAnimation(false);
- mService.mH.runWithScissors(() -> {}, 0);
+ waitHandlerIdle(mService.mH);
InOrder orderVerifier = Mockito.inOrder(mMockListener);
orderVerifier.verify(mMockListener).setRunningRemoteAnimation(eq(true));
@@ -108,7 +109,7 @@ public class WindowProcessControllerTests extends ActivityTestsBase {
public void testSetRunningRemoteAnimation() {
mWpc.setRunningRemoteAnimation(true);
mWpc.setRunningRemoteAnimation(false);
- mService.mH.runWithScissors(() -> {}, 0);
+ waitHandlerIdle(mService.mH);
InOrder orderVerifier = Mockito.inOrder(mMockListener);
orderVerifier.verify(mMockListener).setRunningRemoteAnimation(eq(true));
@@ -122,7 +123,7 @@ public class WindowProcessControllerTests extends ActivityTestsBase {
mWpc.setRunningRecentsAnimation(false);
mWpc.setRunningRemoteAnimation(false);
- mService.mH.runWithScissors(() -> {}, 0);
+ waitHandlerIdle(mService.mH);
InOrder orderVerifier = Mockito.inOrder(mMockListener);
orderVerifier.verify(mMockListener, times(3)).setRunningRemoteAnimation(eq(true));
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 f41f126593de..98d73bacffb3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -57,6 +57,7 @@ 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.when;
import android.graphics.Insets;
import android.graphics.Matrix;
@@ -302,11 +303,11 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testPrepareWindowToDisplayDuringRelayout() {
// Call prepareWindowToDisplayDuringRelayout for a window without FLAG_TURN_SCREEN_ON before
- // calling setCurrentLaunchCanTurnScreenOn for windows with flag in the same appWindowToken.
- final AppWindowToken appWindowToken = createAppWindowToken(mDisplayContent,
+ // calling setCurrentLaunchCanTurnScreenOn for windows with flag in the same activity.
+ final ActivityRecord activity = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- final WindowState first = createWindow(null, TYPE_APPLICATION, appWindowToken, "first");
- final WindowState second = createWindow(null, TYPE_APPLICATION, appWindowToken, "second");
+ final WindowState first = createWindow(null, TYPE_APPLICATION, activity, "first");
+ final WindowState second = createWindow(null, TYPE_APPLICATION, activity, "second");
second.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
testPrepareWindowToDisplayDuringRelayout(first, false /* expectedWakeupCalled */,
@@ -315,8 +316,8 @@ public class WindowStateTests extends WindowTestsBase {
false /* expectedCurrentLaunchCanTurnScreenOn */);
// Call prepareWindowToDisplayDuringRelayout for two window that have FLAG_TURN_SCREEN_ON
- // from the same appWindowToken. Only one should trigger the wakeup.
- appWindowToken.setCurrentLaunchCanTurnScreenOn(true);
+ // from the same activity. Only one should trigger the wakeup.
+ activity.setCurrentLaunchCanTurnScreenOn(true);
first.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
second.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
@@ -327,15 +328,15 @@ public class WindowStateTests extends WindowTestsBase {
// Without window flags, the state of ActivityRecord.canTurnScreenOn should still be able to
// turn on the screen.
- appWindowToken.setCurrentLaunchCanTurnScreenOn(true);
+ activity.setCurrentLaunchCanTurnScreenOn(true);
first.mAttrs.flags &= ~WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
- doReturn(true).when(appWindowToken.mActivityRecord).canTurnScreenOn();
+ doReturn(true).when(activity).canTurnScreenOn();
testPrepareWindowToDisplayDuringRelayout(first, true /* expectedWakeupCalled */,
false /* expectedCurrentLaunchCanTurnScreenOn */);
// Call prepareWindowToDisplayDuringRelayout for a windows that are not children of an
- // appWindowToken. Both windows have the FLAG_TURNS_SCREEN_ON so both should call wakeup
+ // activity. Both windows have the FLAG_TURNS_SCREEN_ON so both should call wakeup
final WindowToken windowToken = WindowTestUtils.createTestWindowToken(FIRST_SUB_WINDOW,
mDisplayContent);
final WindowState firstWindow = createWindow(null, TYPE_APPLICATION, windowToken,
@@ -370,7 +371,7 @@ public class WindowStateTests extends WindowTestsBase {
}
// If wakeup is expected to be called, the currentLaunchCanTurnScreenOn should be false
// because the state will be consumed.
- assertThat(appWindow.mAppToken.currentLaunchCanTurnScreenOn(),
+ assertThat(appWindow.mActivityRecord.currentLaunchCanTurnScreenOn(),
is(expectedCurrentLaunchCanTurnScreenOn));
}
@@ -396,14 +397,15 @@ public class WindowStateTests extends WindowTestsBase {
}
@Test
- public void testVisibleWithInsetsProvider() throws Exception {
+ public void testVisibleWithInsetsProvider() {
final WindowState topBar = createWindow(null, TYPE_STATUS_BAR, "topBar");
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
topBar.mHasSurface = true;
assertTrue(topBar.isVisible());
mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
- .setWindow(topBar, null);
- mDisplayContent.getInsetsStateController().onBarControlTargetChanged(app, app);
+ .setWindow(topBar, null /* frameProvider */);
+ mDisplayContent.getInsetsStateController().onBarControlTargetChanged(
+ app, null /* fakeTopControlling */, app, null /* fakeNavControlling */);
mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
.onInsetsModified(app, new InsetsSource(TYPE_TOP_BAR));
waitUntilHandlersIdle();
@@ -451,7 +453,7 @@ public class WindowStateTests extends WindowTestsBase {
@Test
public void testSeamlesslyRotateWindow() {
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
- final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction t = spy(StubTransaction.class);
app.mHasSurface = true;
app.mSurfaceControl = mock(SurfaceControl.class);
@@ -535,7 +537,7 @@ public class WindowStateTests extends WindowTestsBase {
final float[] values = new float[9];
final Matrix matrix = new Matrix();
- final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction t = spy(StubTransaction.class);
final WindowState win1 = createWindow(null, TYPE_APPLICATION, dc, "win1");
win1.mHasSurface = true;
win1.mSurfaceControl = mock(SurfaceControl.class);
@@ -547,4 +549,29 @@ public class WindowStateTests extends WindowTestsBase {
assertEquals(OFFSET_SUM, values[Matrix.MTRANS_X], 0f);
assertEquals(0f, values[Matrix.MTRANS_Y], 0f);
}
+
+ @Test
+ public void testCantReceiveTouchDuringRecentsAnimation() {
+ final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
+
+ // Mock active recents animation
+ RecentsAnimationController recentsController = mock(RecentsAnimationController.class);
+ when(recentsController.isAnimatingTask(win0.mActivityRecord.getTask())).thenReturn(true);
+ mWm.setRecentsAnimationController(recentsController);
+ assertTrue(win0.cantReceiveTouchInput());
+ }
+
+ @Test
+ public void testCantReceiveTouchWhenAppTokenHiddenRequested() {
+ final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
+ win0.mActivityRecord.hiddenRequested = true;
+ assertTrue(win0.cantReceiveTouchInput());
+ }
+
+ @Test
+ public void testCantReceiveTouchWhenShouldIgnoreInput() {
+ final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
+ win0.mActivityRecord.getStack().setAdjustedForMinimizedDock(1 /* Any non 0 value works */);
+ assertTrue(win0.cantReceiveTouchInput());
+ }
}
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 a7a785d14d6a..51daf6567a47 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -23,80 +23,61 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.os.Build;
import android.os.IBinder;
-import android.view.IApplicationToken;
import android.view.IWindow;
import android.view.WindowManager;
+import com.android.server.wm.ActivityTestsBase.ActivityBuilder;
+
/**
* A collection of static functions that provide access to WindowManager related test functionality.
*/
class WindowTestUtils {
- private static int sNextTaskId = 0;
/** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
- static Task createTaskInStack(WindowManagerService service, TaskStack stack,
- int userId) {
+ static Task createTaskInStack(WindowManagerService service, TaskStack stack, int userId) {
synchronized (service.mGlobalLock) {
- final Task newTask = new Task(sNextTaskId++, stack, userId, service, 0, false,
- new ActivityManager.TaskDescription(), null);
- stack.addTask(newTask, POSITION_TOP);
- return newTask;
+ final TaskRecord task = new ActivityTestsBase.TaskBuilder(
+ stack.mActivityStack.mStackSupervisor)
+ .setUserId(userId)
+ .setStack(stack.mActivityStack)
+ .build();
+ return task.mTask;
}
}
- /** Creates an {@link AppWindowToken} and adds it to the specified {@link Task}. */
- static TestAppWindowToken createAppWindowTokenInTask(DisplayContent dc, Task task) {
- final TestAppWindowToken newToken = createTestAppWindowToken(dc);
- task.addChild(newToken, POSITION_TOP);
- return newToken;
+ /** Creates an {@link ActivityRecord} and adds it to the specified {@link Task}. */
+ static ActivityRecord createActivityRecordInTask(DisplayContent dc, Task task) {
+ final ActivityRecord activity = createTestActivityRecord(dc);
+ task.addChild(activity, POSITION_TOP);
+ return activity;
}
- static TestAppWindowToken createTestAppWindowToken(DisplayContent dc) {
- synchronized (dc.mWmService.mGlobalLock) {
- return new TestAppWindowToken(dc);
+ static ActivityRecord createTestActivityRecord(ActivityStack stack) {
+ synchronized (stack.mService.mGlobalLock) {
+ final ActivityRecord activity = new ActivityTestsBase.ActivityBuilder(
+ stack.mService)
+ .setStack(stack)
+ .setCreateTask(true)
+ .build();
+ postCreateActivitySetup(activity, stack.mTaskStack.getDisplayContent());
+ return activity;
}
}
- /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */
- static class TestAppWindowToken extends AppWindowToken {
- boolean mOnTop = false;
-
- private TestAppWindowToken(DisplayContent dc) {
- super(dc.mWmService, new IApplicationToken.Stub() {
- @Override
- public String getName() {
- return null;
- }
- }, new ComponentName("", ""), false, dc, true /* fillsParent */);
- mTargetSdk = Build.VERSION_CODES.CUR_DEVELOPMENT;
- mActivityRecord = mock(ActivityRecord.class);
- mActivityRecord.app = mock(WindowProcessController.class);
- }
-
- int getWindowsCount() {
- return mChildren.size();
- }
-
- boolean hasWindow(WindowState w) {
- return mChildren.contains(w);
- }
-
- WindowState getFirstChild() {
- return mChildren.peekFirst();
- }
-
- WindowState getLastChild() {
- return mChildren.peekLast();
- }
-
- @Override
- boolean isOnTop() {
- return mOnTop;
+ static ActivityRecord createTestActivityRecord(DisplayContent dc) {
+ synchronized (dc.mWmService.mGlobalLock) {
+ final ActivityRecord activity = new ActivityBuilder(dc.mWmService.mAtmService).build();
+ postCreateActivitySetup(activity, dc);
+ return activity;
}
+ }
+ private static void postCreateActivitySetup(ActivityRecord activity, DisplayContent dc) {
+ activity.onDisplayChanged(dc);
+ activity.setOccludesParent(true);
+ activity.setHidden(false);
+ activity.hiddenRequested = false;
}
static TestWindowToken createTestWindowToken(int type, DisplayContent dc) {
@@ -127,49 +108,6 @@ class WindowTestUtils {
}
}
- /* Used so we can gain access to some protected members of the {@link Task} class */
- static class TestTask extends Task {
- boolean mShouldDeferRemoval = false;
- boolean mOnDisplayChangedCalled = false;
- private boolean mIsAnimating = false;
-
- TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service,
- int resizeMode, boolean supportsPictureInPicture,
- TaskRecord taskRecord) {
- super(taskId, stack, userId, service, resizeMode, supportsPictureInPicture,
- new ActivityManager.TaskDescription(), taskRecord);
- stack.addTask(this, POSITION_TOP);
- }
-
- boolean shouldDeferRemoval() {
- return mShouldDeferRemoval;
- }
-
- int positionInParent() {
- return getParent().mChildren.indexOf(this);
- }
-
- @Override
- void onDisplayChanged(DisplayContent dc) {
- super.onDisplayChanged(dc);
- mOnDisplayChangedCalled = true;
- }
-
- @Override
- boolean isSelfAnimating() {
- return mIsAnimating;
- }
-
- void setLocalIsAnimating(boolean isAnimating) {
- mIsAnimating = isAnimating;
- }
- }
-
- static TestTask createTestTask(TaskStack stack) {
- return new TestTask(sNextTaskId++, stack, 0, stack.mWmService, RESIZE_MODE_UNRESIZEABLE,
- false, mock(TaskRecord.class));
- }
-
/** Used to track resize reports. */
static class TestWindowState extends WindowState {
boolean mResizeReported;
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 8930e5ac42f8..780fed9805cb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -42,7 +42,6 @@ import static org.mockito.Mockito.mock;
import android.content.Context;
import android.content.res.Configuration;
-import android.testing.DexmakerShareClassLoaderRule;
import android.util.Log;
import android.view.Display;
import android.view.DisplayInfo;
@@ -55,7 +54,6 @@ import com.android.server.AttributeCache;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
-import org.junit.Rule;
import java.util.HashSet;
import java.util.LinkedList;
@@ -65,7 +63,7 @@ import java.util.LinkedList;
*
* Make sure any requests to WM hold the WM lock if needed b/73966377
*/
-class WindowTestsBase {
+class WindowTestsBase extends SystemServiceTestsBase {
private static final String TAG = WindowTestsBase.class.getSimpleName();
WindowManagerService mWm;
@@ -92,16 +90,6 @@ class WindowTestsBase {
*/
Transaction mTransaction;
- @Rule
- public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
- new DexmakerShareClassLoaderRule();
- @Rule
- public final SystemServicesTestRule mSystemServicesTestRule = new SystemServicesTestRule();
-
- @WindowTestRunner.MethodWrapperRule
- public final WindowManagerGlobalLockRule mLockRule =
- new WindowManagerGlobalLockRule(mSystemServicesTestRule);
-
@BeforeClass
public static void setUpOnceBase() {
AttributeCache.init(getInstrumentation().getTargetContext());
@@ -205,13 +193,6 @@ class WindowTestsBase {
}
}
- /**
- * Waits until the main handler for WM has processed all messages.
- */
- void waitUntilHandlersIdle() {
- mLockRule.waitUntilHandlersIdle();
- }
-
private WindowToken createWindowToken(
DisplayContent dc, int windowingMode, int activityType, int type) {
synchronized (mWm.mGlobalLock) {
@@ -219,22 +200,18 @@ class WindowTestsBase {
return WindowTestUtils.createTestWindowToken(type, dc);
}
- return createAppWindowToken(dc, windowingMode, activityType);
+ return createActivityRecord(dc, windowingMode, activityType);
}
}
- AppWindowToken createAppWindowToken(DisplayContent dc, int windowingMode, int activityType) {
- return createTestAppWindowToken(dc, windowingMode, activityType);
+ ActivityRecord createActivityRecord(DisplayContent dc, int windowingMode, int activityType) {
+ return createTestActivityRecord(dc, windowingMode, activityType);
}
- WindowTestUtils.TestAppWindowToken createTestAppWindowToken(DisplayContent dc, int
+ ActivityRecord createTestActivityRecord(DisplayContent dc, int
windowingMode, int activityType) {
final TaskStack stack = createTaskStackOnDisplay(windowingMode, activityType, dc);
- final Task task = createTaskInStack(stack, 0 /* userId */);
- final WindowTestUtils.TestAppWindowToken appWindowToken =
- WindowTestUtils.createTestAppWindowToken(dc);
- task.addChild(appWindowToken, 0);
- return appWindowToken;
+ return WindowTestUtils.createTestActivityRecord(stack.mActivityStack);
}
WindowState createWindow(WindowState parent, int type, String name) {
@@ -263,9 +240,10 @@ class WindowTestsBase {
WindowState createAppWindow(Task task, int type, String name) {
synchronized (mWm.mGlobalLock) {
- final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
- task.addChild(token, 0);
- return createWindow(null, type, token, name);
+ final ActivityRecord activity =
+ WindowTestUtils.createTestActivityRecord(mDisplayContent);
+ task.addChild(activity, 0);
+ return createWindow(null, type, activity, name);
}
}
@@ -345,14 +323,14 @@ class WindowTestsBase {
TaskStack createTaskStackOnDisplay(int windowingMode, int activityType, DisplayContent dc) {
synchronized (mWm.mGlobalLock) {
- final Configuration overrideConfig = new Configuration();
- overrideConfig.windowConfiguration.setWindowingMode(windowingMode);
- overrideConfig.windowConfiguration.setActivityType(activityType);
- final int stackId = ++sNextStackId;
- final TaskStack stack = new TaskStack(mWm, stackId, mock(ActivityStack.class));
- dc.setStackOnDisplay(stackId, true, stack);
- stack.onRequestedOverrideConfigurationChanged(overrideConfig);
- return stack;
+ final ActivityStack stack = new ActivityTestsBase.StackBuilder(
+ dc.mWmService.mAtmService.mRootActivityContainer)
+ .setDisplay(dc.mActivityDisplay)
+ .setWindowingMode(windowingMode)
+ .setActivityType(activityType)
+ .setCreateActivity(false)
+ .build();
+ return stack.mTaskStack;
}
}
@@ -404,4 +382,9 @@ class WindowTestsBase {
displayInfo.ownerUid = SYSTEM_UID;
return createNewDisplay(displayInfo);
}
+
+ /** Sets the default minimum task size to 1 so that tests can use small task sizes */
+ public void removeGlobalMinSizeRestriction() {
+ mWm.mAtmService.mRootActivityContainer.mDefaultMinSizeOfResizeableTaskDp = 1;
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java
deleted file mode 100644
index b299f0dd7253..000000000000
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java
+++ /dev/null
@@ -1,184 +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;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import android.content.Context;
-import android.platform.test.annotations.Presubmit;
-import android.util.proto.ProtoOutputStream;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.util.Preconditions;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.File;
-
-
-/**
- * Test class for {@link WindowTraceBuffer}.
- *
- * Build/Install/Run:
- * atest WmTests:WindowTraceBufferTest
- */
-@SmallTest
-@Presubmit
-public class WindowTraceBufferTest {
- private File mFile;
- private WindowTraceBuffer mBuffer;
-
- @Before
- public void setUp() throws Exception {
- final Context testContext = getInstrumentation().getContext();
- mFile = testContext.getFileStreamPath("tracing_test.dat");
- mFile.delete();
-
- mBuffer = new WindowTraceBuffer(10);
- }
-
- @After
- public void tearDown() throws Exception {
- mFile.delete();
- }
-
- @Test
- public void test_addItem() {
- ProtoOutputStream toWrite = getDummy(1);
- final int objectSize = toWrite.getRawSize();
- mBuffer.setCapacity(objectSize);
- mBuffer.resetBuffer();
-
- Preconditions.checkArgument(mBuffer.size() == 0);
-
- mBuffer.add(toWrite);
-
- assertEquals("Item was not added to the buffer", 1, mBuffer.size());
- assertEquals("Total buffer getSize differs from inserted object",
- mBuffer.getBufferSize(), objectSize);
- assertEquals("Available buffer space does not match used one", 0,
- mBuffer.getAvailableSpace());
- }
-
- @Test
- public void test_addItemMustOverwriteOne() {
- ProtoOutputStream toWrite1 = getDummy(1);
- ProtoOutputStream toWrite2 = getDummy(2);
- ProtoOutputStream toWrite3 = getDummy(3);
- final int objectSize = toWrite1.getRawSize();
- final int bufferCapacity = objectSize * 2 + 1;
- mBuffer.setCapacity(bufferCapacity);
- mBuffer.resetBuffer();
-
- mBuffer.add(toWrite1);
- byte[] toWrite1Bytes = toWrite1.getBytes();
- assertTrue("First element should be in the list",
- mBuffer.contains(toWrite1Bytes));
-
- mBuffer.add(toWrite2);
- byte[] toWrite2Bytes = toWrite2.getBytes();
- assertTrue("First element should be in the list",
- mBuffer.contains(toWrite1Bytes));
- assertTrue("Second element should be in the list",
- mBuffer.contains(toWrite2Bytes));
-
- mBuffer.add(toWrite3);
- byte[] toWrite3Bytes = toWrite3.getBytes();
- assertTrue("First element should not be in the list",
- !mBuffer.contains(toWrite1Bytes));
- assertTrue("Second element should be in the list",
- mBuffer.contains(toWrite2Bytes));
- assertTrue("Third element should be in the list",
- mBuffer.contains(toWrite3Bytes));
- assertEquals("Buffer should have 2 elements", 2, mBuffer.size());
- assertEquals(String.format("Buffer is full, used space should be %d", bufferCapacity),
- mBuffer.getBufferSize(), bufferCapacity - 1);
- assertEquals(" Buffer is full, available space should be 0", 1,
- mBuffer.getAvailableSpace());
- }
-
- @Test
- public void test_addItemMustOverwriteMultiple() {
- ProtoOutputStream toWriteSmall1 = getDummy(1);
- ProtoOutputStream toWriteSmall2 = getDummy(2);
- final int objectSize = toWriteSmall1.getRawSize();
- final int bufferCapacity = objectSize * 2;
- mBuffer.setCapacity(bufferCapacity);
- mBuffer.resetBuffer();
-
- ProtoOutputStream toWriteBig = new ProtoOutputStream();
- toWriteBig.write(MAGIC_NUMBER, 1);
- toWriteBig.write(MAGIC_NUMBER, 2);
-
- mBuffer.add(toWriteSmall1);
- byte[] toWriteSmall1Bytes = toWriteSmall1.getBytes();
- assertTrue("First element should be in the list",
- mBuffer.contains(toWriteSmall1Bytes));
-
- mBuffer.add(toWriteSmall2);
- byte[] toWriteSmall2Bytes = toWriteSmall2.getBytes();
- assertTrue("First element should be in the list",
- mBuffer.contains(toWriteSmall1Bytes));
- assertTrue("Second element should be in the list",
- mBuffer.contains(toWriteSmall2Bytes));
-
- mBuffer.add(toWriteBig);
- byte[] toWriteBigBytes = toWriteBig.getBytes();
- assertTrue("Third element should overwrite all others",
- !mBuffer.contains(toWriteSmall1Bytes));
- assertTrue("Third element should overwrite all others",
- !mBuffer.contains(toWriteSmall2Bytes));
- assertTrue("Third element should overwrite all others",
- mBuffer.contains(toWriteBigBytes));
-
- assertEquals(" Buffer should have only 1 big element", 1, mBuffer.size());
- assertEquals(String.format(" Buffer is full, used space should be %d", bufferCapacity),
- mBuffer.getBufferSize(), bufferCapacity);
- assertEquals(" Buffer is full, available space should be 0", 0,
- mBuffer.getAvailableSpace());
- }
-
- @Test
- public void test_startResetsBuffer() {
- ProtoOutputStream toWrite = getDummy(1);
- mBuffer.resetBuffer();
- Preconditions.checkArgument(mBuffer.size() == 0);
-
- mBuffer.add(toWrite);
- assertEquals("Item was not added to the buffer", 1, mBuffer.size());
- mBuffer.resetBuffer();
- assertEquals("Buffer should be empty after reset", 0, mBuffer.size());
- assertEquals("Buffer size should be 0 after reset", 0, mBuffer.getBufferSize());
- }
-
- private ProtoOutputStream getDummy(int value) {
- ProtoOutputStream toWrite = new ProtoOutputStream();
- toWrite.write(MAGIC_NUMBER, value);
- toWrite.flush();
-
- return toWrite;
- }
-
-}
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 2105ab003e0e..63b50b5a9b2b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -47,6 +47,7 @@ import androidx.test.filters.SmallTest;
import org.junit.After;
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.HashMap;
@@ -61,6 +62,7 @@ import java.util.function.Function;
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class ZOrderingTests extends WindowTestsBase {
private static class LayerRecordingTransaction extends SurfaceControl.Transaction {
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
deleted file mode 100644
index 75e8fb5a34fe..000000000000
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ /dev/null
@@ -1,2032 +0,0 @@
-/**
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy
- * of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- */
-
-package com.android.server.usage;
-
-import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
-import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
-import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
-import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
-import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
-import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
-import static android.app.usage.UsageStatsManager.REASON_SUB_PREDICTED_RESTORED;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOUT;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_START;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED_PRIV;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYNC_ADAPTER;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_INTERACTION;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_UPDATE;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
-import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
-import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
-import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
-import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
-import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
-import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
-
-import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
-import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
-
-import android.annotation.UserIdInt;
-import android.app.ActivityManager;
-import android.app.AppGlobals;
-import android.app.usage.AppStandbyInfo;
-import android.app.usage.UsageEvents;
-import android.app.usage.UsageStatsManager.StandbyBuckets;
-import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
-import android.appwidget.AppWidgetManager;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
-import android.content.pm.ParceledListSlice;
-import android.database.ContentObserver;
-import android.hardware.display.DisplayManager;
-import android.net.ConnectivityManager;
-import android.net.Network;
-import android.net.NetworkInfo;
-import android.net.NetworkRequest;
-import android.net.NetworkScoreManager;
-import android.os.BatteryManager;
-import android.os.BatteryStats;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.IDeviceIdleController;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.provider.Settings.Global;
-import android.telephony.TelephonyManager;
-import android.util.ArraySet;
-import android.util.KeyValueListParser;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
-import android.util.TimeUtils;
-import android.view.Display;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.os.SomeArgs;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.ConcurrentUtils;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.LocalServices;
-import com.android.server.usage.AppIdleHistory.AppUsageHistory;
-
-import java.io.File;
-import java.io.PrintWriter;
-import java.time.Duration;
-import java.time.format.DateTimeParseException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-
-/**
- * Manages the standby state of an app, listening to various events.
- *
- * Unit test:
- atest ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
- */
-public class AppStandbyController {
-
- private static final String TAG = "AppStandbyController";
- static final boolean DEBUG = false;
-
- static final boolean COMPRESS_TIME = false;
- private static final long ONE_MINUTE = 60 * 1000;
- private static final long ONE_HOUR = ONE_MINUTE * 60;
- private static final long ONE_DAY = ONE_HOUR * 24;
-
- static final long[] SCREEN_TIME_THRESHOLDS = {
- 0,
- 0,
- COMPRESS_TIME ? 120 * 1000 : 1 * ONE_HOUR,
- COMPRESS_TIME ? 240 * 1000 : 2 * ONE_HOUR
- };
-
- static final long[] ELAPSED_TIME_THRESHOLDS = {
- 0,
- COMPRESS_TIME ? 1 * ONE_MINUTE : 12 * ONE_HOUR,
- COMPRESS_TIME ? 4 * ONE_MINUTE : 24 * ONE_HOUR,
- COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR
- };
-
- static final int[] THRESHOLD_BUCKETS = {
- STANDBY_BUCKET_ACTIVE,
- STANDBY_BUCKET_WORKING_SET,
- STANDBY_BUCKET_FREQUENT,
- STANDBY_BUCKET_RARE
- };
-
- /** Default expiration time for bucket prediction. After this, use thresholds to downgrade. */
- private static final long DEFAULT_PREDICTION_TIMEOUT = 12 * ONE_HOUR;
-
- /**
- * Indicates the maximum wait time for admin data to be available;
- */
- private static final long WAIT_FOR_ADMIN_DATA_TIMEOUT_MS = 10_000;
-
- // To name the lock for stack traces
- static class Lock {}
-
- /** Lock to protect the app's standby state. Required for calls into AppIdleHistory */
- private final Object mAppIdleLock = new Lock();
-
- /** Keeps the history and state for each app. */
- @GuardedBy("mAppIdleLock")
- private AppIdleHistory mAppIdleHistory;
-
- @GuardedBy("mPackageAccessListeners")
- private ArrayList<AppIdleStateChangeListener>
- mPackageAccessListeners = new ArrayList<>();
-
- /** Whether we've queried the list of carrier privileged apps. */
- @GuardedBy("mAppIdleLock")
- private boolean mHaveCarrierPrivilegedApps;
-
- /** List of carrier-privileged apps that should be excluded from standby */
- @GuardedBy("mAppIdleLock")
- private List<String> mCarrierPrivilegedApps;
-
- @GuardedBy("mActiveAdminApps")
- private final SparseArray<Set<String>> mActiveAdminApps = new SparseArray<>();
-
- private final CountDownLatch mAdminDataAvailableLatch = new CountDownLatch(1);
-
- // Messages for the handler
- static final int MSG_INFORM_LISTENERS = 3;
- static final int MSG_FORCE_IDLE_STATE = 4;
- static final int MSG_CHECK_IDLE_STATES = 5;
- static final int MSG_CHECK_PAROLE_TIMEOUT = 6;
- static final int MSG_PAROLE_END_TIMEOUT = 7;
- static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8;
- static final int MSG_PAROLE_STATE_CHANGED = 9;
- static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
- /** Check the state of one app: arg1 = userId, arg2 = uid, obj = (String) packageName */
- static final int MSG_CHECK_PACKAGE_IDLE_STATE = 11;
- static final int MSG_REPORT_SYNC_SCHEDULED = 12;
- static final int MSG_REPORT_EXEMPTED_SYNC_START = 13;
- static final int MSG_UPDATE_STABLE_CHARGING= 14;
-
- long mCheckIdleIntervalMillis;
- long mAppIdleParoleIntervalMillis;
- long mAppIdleParoleWindowMillis;
- long mAppIdleParoleDurationMillis;
- long[] mAppStandbyScreenThresholds = SCREEN_TIME_THRESHOLDS;
- long[] mAppStandbyElapsedThresholds = ELAPSED_TIME_THRESHOLDS;
- /** Minimum time a strong usage event should keep the bucket elevated. */
- long mStrongUsageTimeoutMillis;
- /** Minimum time a notification seen event should keep the bucket elevated. */
- long mNotificationSeenTimeoutMillis;
- /** Minimum time a system update event should keep the buckets elevated. */
- long mSystemUpdateUsageTimeoutMillis;
- /** Maximum time to wait for a prediction before using simple timeouts to downgrade buckets. */
- long mPredictionTimeoutMillis;
- /** Maximum time a sync adapter associated with a CP should keep the buckets elevated. */
- long mSyncAdapterTimeoutMillis;
- /**
- * Maximum time an exempted sync should keep the buckets elevated, when sync is scheduled in
- * non-doze
- */
- long mExemptedSyncScheduledNonDozeTimeoutMillis;
- /**
- * Maximum time an exempted sync should keep the buckets elevated, when sync is scheduled in
- * doze
- */
- long mExemptedSyncScheduledDozeTimeoutMillis;
- /**
- * Maximum time an exempted sync should keep the buckets elevated, when sync is started.
- */
- long mExemptedSyncStartTimeoutMillis;
- /**
- * Maximum time an unexempted sync should keep the buckets elevated, when sync is scheduled
- */
- long mUnexemptedSyncScheduledTimeoutMillis;
- /** Maximum time a system interaction should keep the buckets elevated. */
- long mSystemInteractionTimeoutMillis;
- /**
- * Maximum time a foreground service start should keep the buckets elevated if the service
- * start is the first usage of the app
- */
- long mInitialForegroundServiceStartTimeoutMillis;
- /** The length of time phone must be charging before considered stable enough to run jobs */
- long mStableChargingThresholdMillis;
-
- volatile boolean mAppIdleEnabled;
- boolean mAppIdleTempParoled;
- boolean mCharging;
- boolean mChargingStable;
- private long mLastAppIdleParoledTime;
- private boolean mSystemServicesReady = false;
- // There was a system update, defaults need to be initialized after services are ready
- private boolean mPendingInitializeDefaults;
-
- private final DeviceStateReceiver mDeviceStateReceiver;
-
- private volatile boolean mPendingOneTimeCheckIdleStates;
-
- private final AppStandbyHandler mHandler;
- private final Context mContext;
-
- // TODO: Provide a mechanism to set an external bucketing service
-
- private AppWidgetManager mAppWidgetManager;
- private ConnectivityManager mConnectivityManager;
- private PowerManager mPowerManager;
- private PackageManager mPackageManager;
- Injector mInjector;
-
- static final ArrayList<StandbyUpdateRecord> sStandbyUpdatePool = new ArrayList<>(4);
-
- public static class StandbyUpdateRecord {
- // Identity of the app whose standby state has changed
- String packageName;
- int userId;
-
- // What the standby bucket the app is now in
- int bucket;
-
- // Whether the bucket change is because the user has started interacting with the app
- boolean isUserInteraction;
-
- // Reason for bucket change
- int reason;
-
- StandbyUpdateRecord(String pkgName, int userId, int bucket, int reason,
- boolean isInteraction) {
- this.packageName = pkgName;
- this.userId = userId;
- this.bucket = bucket;
- this.reason = reason;
- this.isUserInteraction = isInteraction;
- }
-
- public static StandbyUpdateRecord obtain(String pkgName, int userId,
- int bucket, int reason, boolean isInteraction) {
- synchronized (sStandbyUpdatePool) {
- final int size = sStandbyUpdatePool.size();
- if (size < 1) {
- return new StandbyUpdateRecord(pkgName, userId, bucket, reason, isInteraction);
- }
- StandbyUpdateRecord r = sStandbyUpdatePool.remove(size - 1);
- r.packageName = pkgName;
- r.userId = userId;
- r.bucket = bucket;
- r.reason = reason;
- r.isUserInteraction = isInteraction;
- return r;
- }
- }
-
- public void recycle() {
- synchronized (sStandbyUpdatePool) {
- sStandbyUpdatePool.add(this);
- }
- }
- }
-
- AppStandbyController(Context context, Looper looper) {
- this(new Injector(context, looper));
- }
-
- AppStandbyController(Injector injector) {
- mInjector = injector;
- mContext = mInjector.getContext();
- mHandler = new AppStandbyHandler(mInjector.getLooper());
- mPackageManager = mContext.getPackageManager();
- mDeviceStateReceiver = new DeviceStateReceiver();
-
- IntentFilter deviceStates = new IntentFilter(BatteryManager.ACTION_CHARGING);
- deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
- deviceStates.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
- mContext.registerReceiver(mDeviceStateReceiver, deviceStates);
-
- synchronized (mAppIdleLock) {
- mAppIdleHistory = new AppIdleHistory(mInjector.getDataSystemDirectory(),
- mInjector.elapsedRealtime());
- }
-
- IntentFilter packageFilter = new IntentFilter();
- packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
- packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- packageFilter.addDataScheme("package");
-
- mContext.registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, packageFilter,
- null, mHandler);
- }
-
- void setAppIdleEnabled(boolean enabled) {
- synchronized (mAppIdleLock) {
- if (mAppIdleEnabled != enabled) {
- final boolean oldParoleState = isParoledOrCharging();
- mAppIdleEnabled = enabled;
- if (isParoledOrCharging() != oldParoleState) {
- postParoleStateChanged();
- }
- }
- }
- }
-
- public void onBootPhase(int phase) {
- mInjector.onBootPhase(phase);
- if (phase == PHASE_SYSTEM_SERVICES_READY) {
- Slog.d(TAG, "Setting app idle enabled state");
- // Observe changes to the threshold
- SettingsObserver settingsObserver = new SettingsObserver(mHandler);
- settingsObserver.registerObserver();
- settingsObserver.updateSettings();
-
- mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class);
- mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
- mPowerManager = mContext.getSystemService(PowerManager.class);
-
- mInjector.registerDisplayListener(mDisplayListener, mHandler);
- synchronized (mAppIdleLock) {
- mAppIdleHistory.updateDisplay(isDisplayOn(), mInjector.elapsedRealtime());
- }
-
- mSystemServicesReady = true;
-
- boolean userFileExists;
- synchronized (mAppIdleLock) {
- userFileExists = mAppIdleHistory.userFileExists(UserHandle.USER_SYSTEM);
- }
-
- if (mPendingInitializeDefaults || !userFileExists) {
- initializeDefaultsForSystemApps(UserHandle.USER_SYSTEM);
- }
-
- if (mPendingOneTimeCheckIdleStates) {
- postOneTimeCheckIdleStates();
- }
- } else if (phase == PHASE_BOOT_COMPLETED) {
- setChargingState(mInjector.isCharging());
- }
- }
-
- void reportContentProviderUsage(String authority, String providerPkgName, int userId) {
- if (!mAppIdleEnabled) return;
-
- // Get sync adapters for the authority
- String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser(
- authority, userId);
- final long elapsedRealtime = mInjector.elapsedRealtime();
- for (String packageName: packages) {
- // Only force the sync adapters to active if the provider is not in the same package and
- // the sync adapter is a system package.
- try {
- PackageInfo pi = mPackageManager.getPackageInfoAsUser(
- packageName, PackageManager.MATCH_SYSTEM_ONLY, userId);
- if (pi == null || pi.applicationInfo == null) {
- continue;
- }
- if (!packageName.equals(providerPkgName)) {
- synchronized (mAppIdleLock) {
- AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId,
- STANDBY_BUCKET_ACTIVE, REASON_SUB_USAGE_SYNC_ADAPTER,
- 0,
- elapsedRealtime + mSyncAdapterTimeoutMillis);
- maybeInformListeners(packageName, userId, elapsedRealtime,
- appUsage.currentBucket, appUsage.bucketingReason, false);
- }
- }
- } catch (PackageManager.NameNotFoundException e) {
- // Shouldn't happen
- }
- }
- }
-
- void reportExemptedSyncScheduled(String packageName, int userId) {
- if (!mAppIdleEnabled) return;
-
- final int bucketToPromote;
- final int usageReason;
- final long durationMillis;
-
- if (!mInjector.isDeviceIdleMode()) {
- // Not dozing.
- bucketToPromote = STANDBY_BUCKET_ACTIVE;
- usageReason = REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
- durationMillis = mExemptedSyncScheduledNonDozeTimeoutMillis;
- } else {
- // Dozing.
- bucketToPromote = STANDBY_BUCKET_WORKING_SET;
- usageReason = REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE;
- durationMillis = mExemptedSyncScheduledDozeTimeoutMillis;
- }
-
- final long elapsedRealtime = mInjector.elapsedRealtime();
-
- synchronized (mAppIdleLock) {
- AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId,
- bucketToPromote, usageReason,
- 0,
- elapsedRealtime + durationMillis);
- maybeInformListeners(packageName, userId, elapsedRealtime,
- appUsage.currentBucket, appUsage.bucketingReason, false);
- }
- }
-
- void reportUnexemptedSyncScheduled(String packageName, int userId) {
- if (!mAppIdleEnabled) return;
-
- final long elapsedRealtime = mInjector.elapsedRealtime();
- synchronized (mAppIdleLock) {
- final int currentBucket =
- mAppIdleHistory.getAppStandbyBucket(packageName, userId, elapsedRealtime);
- if (currentBucket == STANDBY_BUCKET_NEVER) {
- // Bring the app out of the never bucket
- AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId,
- STANDBY_BUCKET_WORKING_SET, REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED,
- 0,
- elapsedRealtime + mUnexemptedSyncScheduledTimeoutMillis);
- maybeInformListeners(packageName, userId, elapsedRealtime,
- appUsage.currentBucket, appUsage.bucketingReason, false);
- }
- }
- }
-
- void reportExemptedSyncStart(String packageName, int userId) {
- if (!mAppIdleEnabled) return;
-
- final long elapsedRealtime = mInjector.elapsedRealtime();
-
- synchronized (mAppIdleLock) {
- AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId,
- STANDBY_BUCKET_ACTIVE, REASON_SUB_USAGE_EXEMPTED_SYNC_START,
- 0,
- elapsedRealtime + mExemptedSyncStartTimeoutMillis);
- maybeInformListeners(packageName, userId, elapsedRealtime,
- appUsage.currentBucket, appUsage.bucketingReason, false);
- }
- }
-
- void setChargingState(boolean charging) {
- synchronized (mAppIdleLock) {
- if (mCharging != charging) {
- mCharging = charging;
- if (DEBUG) Slog.d(TAG, "Setting mCharging to " + charging);
- if (charging) {
- if (DEBUG) {
- Slog.d(TAG, "Scheduling MSG_UPDATE_STABLE_CHARGING delay = "
- + mStableChargingThresholdMillis);
- }
- mHandler.sendEmptyMessageDelayed(MSG_UPDATE_STABLE_CHARGING,
- mStableChargingThresholdMillis);
- } else {
- mHandler.removeMessages(MSG_UPDATE_STABLE_CHARGING);
- updateChargingStableState();
- }
- }
- }
- }
-
- void updateChargingStableState() {
- synchronized (mAppIdleLock) {
- if (mChargingStable != mCharging) {
- if (DEBUG) Slog.d(TAG, "Setting mChargingStable to " + mCharging);
- mChargingStable = mCharging;
- postParoleStateChanged();
- }
- }
- }
-
- /** Paroled here means temporary pardon from being inactive */
- void setAppIdleParoled(boolean paroled) {
- synchronized (mAppIdleLock) {
- final long now = mInjector.currentTimeMillis();
- if (mAppIdleTempParoled != paroled) {
- mAppIdleTempParoled = paroled;
- if (DEBUG) Slog.d(TAG, "Changing paroled to " + mAppIdleTempParoled);
- if (paroled) {
- postParoleEndTimeout();
- } else {
- mLastAppIdleParoledTime = now;
- postNextParoleTimeout(now, false);
- }
- postParoleStateChanged();
- }
- }
- }
-
- boolean isParoledOrCharging() {
- if (!mAppIdleEnabled) return true;
- synchronized (mAppIdleLock) {
- // Only consider stable charging when determining charge state.
- return mAppIdleTempParoled || mChargingStable;
- }
- }
-
- private void postNextParoleTimeout(long now, boolean forced) {
- if (DEBUG) Slog.d(TAG, "Posting MSG_CHECK_PAROLE_TIMEOUT");
- mHandler.removeMessages(MSG_CHECK_PAROLE_TIMEOUT);
- // Compute when the next parole needs to happen. We check more frequently than necessary
- // since the message handler delays are based on elapsedRealTime and not wallclock time.
- // The comparison is done in wallclock time.
- long timeLeft = (mLastAppIdleParoledTime + mAppIdleParoleIntervalMillis) - now;
- if (forced) {
- // Set next timeout for the end of the parole window
- // If parole is not set by the end of the window it will be forced
- timeLeft += mAppIdleParoleWindowMillis;
- }
- if (timeLeft < 0) {
- timeLeft = 0;
- }
- mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft);
- }
-
- private void postParoleEndTimeout() {
- if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_END_TIMEOUT");
- mHandler.removeMessages(MSG_PAROLE_END_TIMEOUT);
- mHandler.sendEmptyMessageDelayed(MSG_PAROLE_END_TIMEOUT, mAppIdleParoleDurationMillis);
- }
-
- private void postParoleStateChanged() {
- if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_STATE_CHANGED");
- mHandler.removeMessages(MSG_PAROLE_STATE_CHANGED);
- mHandler.sendEmptyMessage(MSG_PAROLE_STATE_CHANGED);
- }
-
- void postCheckIdleStates(int userId) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
- }
-
- /**
- * We send a different message to check idle states once, otherwise we would end up
- * scheduling a series of repeating checkIdleStates each time we fired off one.
- */
- void postOneTimeCheckIdleStates() {
- if (mInjector.getBootPhase() < PHASE_SYSTEM_SERVICES_READY) {
- // Not booted yet; wait for it!
- mPendingOneTimeCheckIdleStates = true;
- } else {
- mHandler.sendEmptyMessage(MSG_ONE_TIME_CHECK_IDLE_STATES);
- mPendingOneTimeCheckIdleStates = false;
- }
- }
-
- /**
- * Check all running users' or specified user's apps to see if they enter an idle state.
- * @return Returns whether checking should continue periodically.
- */
- boolean checkIdleStates(int checkUserId) {
- if (!mAppIdleEnabled) {
- return false;
- }
-
- final int[] runningUserIds;
- try {
- runningUserIds = mInjector.getRunningUserIds();
- if (checkUserId != UserHandle.USER_ALL
- && !ArrayUtils.contains(runningUserIds, checkUserId)) {
- return false;
- }
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
-
- final long elapsedRealtime = mInjector.elapsedRealtime();
- for (int i = 0; i < runningUserIds.length; i++) {
- final int userId = runningUserIds[i];
- if (checkUserId != UserHandle.USER_ALL && checkUserId != userId) {
- continue;
- }
- if (DEBUG) {
- Slog.d(TAG, "Checking idle state for user " + userId);
- }
- List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
- PackageManager.MATCH_DISABLED_COMPONENTS,
- userId);
- final int packageCount = packages.size();
- for (int p = 0; p < packageCount; p++) {
- final PackageInfo pi = packages.get(p);
- final String packageName = pi.packageName;
- checkAndUpdateStandbyState(packageName, userId, pi.applicationInfo.uid,
- elapsedRealtime);
- }
- }
- if (DEBUG) {
- Slog.d(TAG, "checkIdleStates took "
- + (mInjector.elapsedRealtime() - elapsedRealtime));
- }
- return true;
- }
-
- /** Check if we need to update the standby state of a specific app. */
- private void checkAndUpdateStandbyState(String packageName, @UserIdInt int userId,
- int uid, long elapsedRealtime) {
- if (uid <= 0) {
- try {
- uid = mPackageManager.getPackageUidAsUser(packageName, userId);
- } catch (PackageManager.NameNotFoundException e) {
- // Not a valid package for this user, nothing to do
- // TODO: Remove any history of removed packages
- return;
- }
- }
- final boolean isSpecial = isAppSpecial(packageName,
- UserHandle.getAppId(uid),
- userId);
- if (DEBUG) {
- Slog.d(TAG, " Checking idle state for " + packageName + " special=" +
- isSpecial);
- }
- if (isSpecial) {
- synchronized (mAppIdleLock) {
- mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
- STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
- }
- maybeInformListeners(packageName, userId, elapsedRealtime,
- STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT, false);
- } else {
- synchronized (mAppIdleLock) {
- final AppIdleHistory.AppUsageHistory app =
- mAppIdleHistory.getAppUsageHistory(packageName,
- userId, elapsedRealtime);
- int reason = app.bucketingReason;
- final int oldMainReason = reason & REASON_MAIN_MASK;
-
- // If the bucket was forced by the user/developer, leave it alone.
- // A usage event will be the only way to bring it out of this forced state
- if (oldMainReason == REASON_MAIN_FORCED) {
- return;
- }
- final int oldBucket = app.currentBucket;
- int newBucket = Math.max(oldBucket, STANDBY_BUCKET_ACTIVE); // Undo EXEMPTED
- boolean predictionLate = predictionTimedOut(app, elapsedRealtime);
- // Compute age-based bucket
- if (oldMainReason == REASON_MAIN_DEFAULT
- || oldMainReason == REASON_MAIN_USAGE
- || oldMainReason == REASON_MAIN_TIMEOUT
- || predictionLate) {
-
- if (!predictionLate && app.lastPredictedBucket >= STANDBY_BUCKET_ACTIVE
- && app.lastPredictedBucket <= STANDBY_BUCKET_RARE) {
- newBucket = app.lastPredictedBucket;
- reason = REASON_MAIN_PREDICTED | REASON_SUB_PREDICTED_RESTORED;
- if (DEBUG) {
- Slog.d(TAG, "Restored predicted newBucket = " + newBucket);
- }
- } else {
- newBucket = getBucketForLocked(packageName, userId,
- elapsedRealtime);
- if (DEBUG) {
- Slog.d(TAG, "Evaluated AOSP newBucket = " + newBucket);
- }
- reason = REASON_MAIN_TIMEOUT;
- }
- }
-
- // Check if the app is within one of the timeouts for forced bucket elevation
- final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
- if (newBucket >= STANDBY_BUCKET_ACTIVE
- && app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
- newBucket = STANDBY_BUCKET_ACTIVE;
- reason = app.bucketingReason;
- if (DEBUG) {
- Slog.d(TAG, " Keeping at ACTIVE due to min timeout");
- }
- } else if (newBucket >= STANDBY_BUCKET_WORKING_SET
- && app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
- newBucket = STANDBY_BUCKET_WORKING_SET;
- // If it was already there, keep the reason, else assume timeout to WS
- reason = (newBucket == oldBucket)
- ? app.bucketingReason
- : REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
- if (DEBUG) {
- Slog.d(TAG, " Keeping at WORKING_SET due to min timeout");
- }
- }
- if (DEBUG) {
- Slog.d(TAG, " Old bucket=" + oldBucket
- + ", newBucket=" + newBucket);
- }
- if (oldBucket < newBucket || predictionLate) {
- mAppIdleHistory.setAppStandbyBucket(packageName, userId,
- elapsedRealtime, newBucket, reason);
- maybeInformListeners(packageName, userId, elapsedRealtime,
- newBucket, reason, false);
- }
- }
- }
- }
-
- /** Returns true if there hasn't been a prediction for the app in a while. */
- private boolean predictionTimedOut(AppIdleHistory.AppUsageHistory app, long elapsedRealtime) {
- return app.lastPredictedTime > 0
- && mAppIdleHistory.getElapsedTime(elapsedRealtime)
- - app.lastPredictedTime > mPredictionTimeoutMillis;
- }
-
- /** Inform listeners if the bucket has changed since it was last reported to listeners */
- private void maybeInformListeners(String packageName, int userId,
- long elapsedRealtime, int bucket, int reason, boolean userStartedInteracting) {
- synchronized (mAppIdleLock) {
- if (mAppIdleHistory.shouldInformListeners(packageName, userId,
- elapsedRealtime, bucket)) {
- final StandbyUpdateRecord r = StandbyUpdateRecord.obtain(packageName, userId,
- bucket, reason, userStartedInteracting);
- if (DEBUG) Slog.d(TAG, "Standby bucket for " + packageName + "=" + bucket);
- mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, r));
- }
- }
- }
-
- /**
- * Evaluates next bucket based on time since last used and the bucketing thresholds.
- * @param packageName the app
- * @param userId the user
- * @param elapsedRealtime as the name suggests, current elapsed time
- * @return the bucket for the app, based on time since last used
- */
- @GuardedBy("mAppIdleLock")
- @StandbyBuckets int getBucketForLocked(String packageName, int userId,
- long elapsedRealtime) {
- int bucketIndex = mAppIdleHistory.getThresholdIndex(packageName, userId,
- elapsedRealtime, mAppStandbyScreenThresholds, mAppStandbyElapsedThresholds);
- return THRESHOLD_BUCKETS[bucketIndex];
- }
-
- /**
- * Check if it's been a while since last parole and let idle apps do some work.
- * If network is not available, delay parole until it is available up until the end of the
- * parole window. Force the parole to be set if end of the parole window is reached.
- */
- void checkParoleTimeout() {
- boolean setParoled = false;
- boolean waitForNetwork = false;
- NetworkInfo activeNetwork = mConnectivityManager.getActiveNetworkInfo();
- boolean networkActive = activeNetwork != null &&
- activeNetwork.isConnected();
-
- synchronized (mAppIdleLock) {
- final long now = mInjector.currentTimeMillis();
- if (!mAppIdleTempParoled) {
- final long timeSinceLastParole = now - mLastAppIdleParoledTime;
- if (timeSinceLastParole > mAppIdleParoleIntervalMillis) {
- if (DEBUG) Slog.d(TAG, "Crossed default parole interval");
- if (networkActive) {
- // If network is active set parole
- setParoled = true;
- } else {
- if (timeSinceLastParole
- > mAppIdleParoleIntervalMillis + mAppIdleParoleWindowMillis) {
- if (DEBUG) Slog.d(TAG, "Crossed end of parole window, force parole");
- setParoled = true;
- } else {
- if (DEBUG) Slog.d(TAG, "Network unavailable, delaying parole");
- waitForNetwork = true;
- postNextParoleTimeout(now, true);
- }
- }
- } else {
- if (DEBUG) Slog.d(TAG, "Not long enough to go to parole");
- postNextParoleTimeout(now, false);
- }
- }
- }
- if (waitForNetwork) {
- mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback);
- }
- if (setParoled) {
- // Set parole if network is available
- setAppIdleParoled(true);
- }
- }
-
- private void notifyBatteryStats(String packageName, int userId, boolean idle) {
- try {
- final int uid = mPackageManager.getPackageUidAsUser(packageName,
- PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
- if (idle) {
- mInjector.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE,
- packageName, uid);
- } else {
- mInjector.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE,
- packageName, uid);
- }
- } catch (PackageManager.NameNotFoundException | RemoteException e) {
- }
- }
-
- void onDeviceIdleModeChanged() {
- final boolean deviceIdle = mPowerManager.isDeviceIdleMode();
- if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle);
- boolean paroled = false;
- synchronized (mAppIdleLock) {
- final long timeSinceLastParole =
- mInjector.currentTimeMillis() - mLastAppIdleParoledTime;
- if (!deviceIdle
- && timeSinceLastParole >= mAppIdleParoleIntervalMillis) {
- if (DEBUG) {
- Slog.i(TAG,
- "Bringing idle apps out of inactive state due to deviceIdleMode=false");
- }
- paroled = true;
- } else if (deviceIdle) {
- if (DEBUG) Slog.i(TAG, "Device idle, back to prison");
- paroled = false;
- } else {
- return;
- }
- }
- setAppIdleParoled(paroled);
- }
-
- void reportEvent(UsageEvents.Event event, long elapsedRealtime, int userId) {
- if (!mAppIdleEnabled) return;
- synchronized (mAppIdleLock) {
- // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
- // about apps that are on some kind of whitelist anyway.
- final boolean previouslyIdle = mAppIdleHistory.isIdle(
- event.mPackage, userId, elapsedRealtime);
- // Inform listeners if necessary
- if ((event.mEventType == UsageEvents.Event.ACTIVITY_RESUMED
- || event.mEventType == UsageEvents.Event.ACTIVITY_PAUSED
- || event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION
- || event.mEventType == UsageEvents.Event.USER_INTERACTION
- || event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN
- || event.mEventType == UsageEvents.Event.SLICE_PINNED
- || event.mEventType == UsageEvents.Event.SLICE_PINNED_PRIV
- || event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_START)) {
-
- final AppUsageHistory appHistory = mAppIdleHistory.getAppUsageHistory(
- event.mPackage, userId, elapsedRealtime);
- final int prevBucket = appHistory.currentBucket;
- final int prevBucketReason = appHistory.bucketingReason;
- final long nextCheckTime;
- final int subReason = usageEventToSubReason(event.mEventType);
- final int reason = REASON_MAIN_USAGE | subReason;
- if (event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN
- || event.mEventType == UsageEvents.Event.SLICE_PINNED) {
- // Mild usage elevates to WORKING_SET but doesn't change usage time.
- mAppIdleHistory.reportUsage(appHistory, event.mPackage,
- STANDBY_BUCKET_WORKING_SET, subReason,
- 0, elapsedRealtime + mNotificationSeenTimeoutMillis);
- nextCheckTime = mNotificationSeenTimeoutMillis;
- } else if (event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION) {
- mAppIdleHistory.reportUsage(appHistory, event.mPackage,
- STANDBY_BUCKET_ACTIVE, subReason,
- 0, elapsedRealtime + mSystemInteractionTimeoutMillis);
- nextCheckTime = mSystemInteractionTimeoutMillis;
- } else if (event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_START) {
- // Only elevate bucket if this is the first usage of the app
- if (prevBucket != STANDBY_BUCKET_NEVER) return;
- mAppIdleHistory.reportUsage(appHistory, event.mPackage,
- STANDBY_BUCKET_ACTIVE, subReason,
- 0, elapsedRealtime + mInitialForegroundServiceStartTimeoutMillis);
- nextCheckTime = mInitialForegroundServiceStartTimeoutMillis;
- } else {
- mAppIdleHistory.reportUsage(appHistory, event.mPackage,
- STANDBY_BUCKET_ACTIVE, subReason,
- elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis);
- nextCheckTime = mStrongUsageTimeoutMillis;
- }
- mHandler.sendMessageDelayed(mHandler.obtainMessage
- (MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, event.mPackage),
- nextCheckTime);
- final boolean userStartedInteracting =
- appHistory.currentBucket == STANDBY_BUCKET_ACTIVE &&
- prevBucket != appHistory.currentBucket &&
- (prevBucketReason & REASON_MAIN_MASK) != REASON_MAIN_USAGE;
- maybeInformListeners(event.mPackage, userId, elapsedRealtime,
- appHistory.currentBucket, reason, userStartedInteracting);
-
- if (previouslyIdle) {
- notifyBatteryStats(event.mPackage, userId, false);
- }
- }
- }
- }
-
- private int usageEventToSubReason(int eventType) {
- switch (eventType) {
- case UsageEvents.Event.ACTIVITY_RESUMED: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
- case UsageEvents.Event.ACTIVITY_PAUSED: return REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
- case UsageEvents.Event.SYSTEM_INTERACTION: return REASON_SUB_USAGE_SYSTEM_INTERACTION;
- case UsageEvents.Event.USER_INTERACTION: return REASON_SUB_USAGE_USER_INTERACTION;
- case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN;
- case UsageEvents.Event.SLICE_PINNED: return REASON_SUB_USAGE_SLICE_PINNED;
- case UsageEvents.Event.SLICE_PINNED_PRIV: return REASON_SUB_USAGE_SLICE_PINNED_PRIV;
- case UsageEvents.Event.FOREGROUND_SERVICE_START:
- return REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
- default: return 0;
- }
- }
-
- /**
- * Forces the app's beginIdleTime and lastUsedTime to reflect idle or active. If idle,
- * then it rolls back the beginIdleTime and lastUsedTime to a point in time that's behind
- * the threshold for idle.
- *
- * This method is always called from the handler thread, so not much synchronization is
- * required.
- */
- void forceIdleState(String packageName, int userId, boolean idle) {
- if (!mAppIdleEnabled) return;
-
- final int appId = getAppId(packageName);
- if (appId < 0) return;
- final long elapsedRealtime = mInjector.elapsedRealtime();
-
- final boolean previouslyIdle = isAppIdleFiltered(packageName, appId,
- userId, elapsedRealtime);
- final int standbyBucket;
- synchronized (mAppIdleLock) {
- standbyBucket = mAppIdleHistory.setIdle(packageName, userId, idle, elapsedRealtime);
- }
- final boolean stillIdle = isAppIdleFiltered(packageName, appId,
- userId, elapsedRealtime);
- // Inform listeners if necessary
- if (previouslyIdle != stillIdle) {
- maybeInformListeners(packageName, userId, elapsedRealtime, standbyBucket,
- REASON_MAIN_FORCED, false);
- if (!stillIdle) {
- notifyBatteryStats(packageName, userId, idle);
- }
- }
- }
-
- public void setLastJobRunTime(String packageName, int userId, long elapsedRealtime) {
- synchronized (mAppIdleLock) {
- mAppIdleHistory.setLastJobRunTime(packageName, userId, elapsedRealtime);
- }
- }
-
- public long getTimeSinceLastJobRun(String packageName, int userId) {
- final long elapsedRealtime = mInjector.elapsedRealtime();
- synchronized (mAppIdleLock) {
- return mAppIdleHistory.getTimeSinceLastJobRun(packageName, userId, elapsedRealtime);
- }
- }
-
- public void onUserRemoved(int userId) {
- synchronized (mAppIdleLock) {
- mAppIdleHistory.onUserRemoved(userId);
- synchronized (mActiveAdminApps) {
- mActiveAdminApps.remove(userId);
- }
- }
- }
-
- private boolean isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime) {
- synchronized (mAppIdleLock) {
- return mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
- }
- }
-
- void addListener(AppIdleStateChangeListener listener) {
- synchronized (mPackageAccessListeners) {
- if (!mPackageAccessListeners.contains(listener)) {
- mPackageAccessListeners.add(listener);
- }
- }
- }
-
- void removeListener(AppIdleStateChangeListener listener) {
- synchronized (mPackageAccessListeners) {
- mPackageAccessListeners.remove(listener);
- }
- }
-
- int getAppId(String packageName) {
- try {
- ApplicationInfo ai = mPackageManager.getApplicationInfo(packageName,
- PackageManager.MATCH_ANY_USER
- | PackageManager.MATCH_DISABLED_COMPONENTS);
- return ai.uid;
- } catch (PackageManager.NameNotFoundException re) {
- return -1;
- }
- }
-
- boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime,
- boolean shouldObfuscateInstantApps) {
- if (isParoledOrCharging()) {
- return false;
- }
- if (shouldObfuscateInstantApps &&
- mInjector.isPackageEphemeral(userId, packageName)) {
- return false;
- }
- return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
- }
-
- /** Returns true if this app should be whitelisted for some reason, to never go into standby */
- boolean isAppSpecial(String packageName, int appId, int userId) {
- if (packageName == null) return false;
- // If not enabled at all, of course nobody is ever idle.
- if (!mAppIdleEnabled) {
- return true;
- }
- if (appId < Process.FIRST_APPLICATION_UID) {
- // System uids never go idle.
- return true;
- }
- if (packageName.equals("android")) {
- // Nor does the framework (which should be redundant with the above, but for MR1 we will
- // retain this for safety).
- return true;
- }
- if (mSystemServicesReady) {
- try {
- // We allow all whitelisted apps, including those that don't want to be whitelisted
- // for idle mode, because app idle (aka app standby) is really not as big an issue
- // for controlling who participates vs. doze mode.
- if (mInjector.isPowerSaveWhitelistExceptIdleApp(packageName)) {
- return true;
- }
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
-
- if (isActiveDeviceAdmin(packageName, userId)) {
- return true;
- }
-
- if (isActiveNetworkScorer(packageName)) {
- return true;
- }
-
- if (mAppWidgetManager != null
- && mInjector.isBoundWidgetPackage(mAppWidgetManager, packageName, userId)) {
- return true;
- }
-
- if (isDeviceProvisioningPackage(packageName)) {
- return true;
- }
- }
-
- // Check this last, as it can be the most expensive check
- if (isCarrierApp(packageName)) {
- return true;
- }
-
- return false;
- }
-
- /**
- * Checks if an app has been idle for a while and filters out apps that are excluded.
- * It returns false if the current system state allows all apps to be considered active.
- * This happens if the device is plugged in or temporarily allowed to make exceptions.
- * Called by interface impls.
- */
- boolean isAppIdleFiltered(String packageName, int appId, int userId,
- long elapsedRealtime) {
- if (isAppSpecial(packageName, appId, userId)) {
- return false;
- } else {
- return isAppIdleUnfiltered(packageName, userId, elapsedRealtime);
- }
- }
-
- int[] getIdleUidsForUser(int userId) {
- if (!mAppIdleEnabled) {
- return new int[0];
- }
-
- final long elapsedRealtime = mInjector.elapsedRealtime();
-
- List<ApplicationInfo> apps;
- try {
- ParceledListSlice<ApplicationInfo> slice = AppGlobals.getPackageManager()
- .getInstalledApplications(/* flags= */ 0, userId);
- if (slice == null) {
- return new int[0];
- }
- apps = slice.getList();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
- // State of each uid. Key is the uid. Value lower 16 bits is the number of apps
- // associated with that uid, upper 16 bits is the number of those apps that is idle.
- SparseIntArray uidStates = new SparseIntArray();
-
- // Now resolve all app state. Iterating over all apps, keeping track of how many
- // we find for each uid and how many of those are idle.
- for (int i = apps.size() - 1; i >= 0; i--) {
- ApplicationInfo ai = apps.get(i);
-
- // Check whether this app is idle.
- boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid),
- userId, elapsedRealtime);
-
- int index = uidStates.indexOfKey(ai.uid);
- if (index < 0) {
- uidStates.put(ai.uid, 1 + (idle ? 1<<16 : 0));
- } else {
- int value = uidStates.valueAt(index);
- uidStates.setValueAt(index, value + 1 + (idle ? 1<<16 : 0));
- }
- }
- if (DEBUG) {
- Slog.d(TAG, "getIdleUids took " + (mInjector.elapsedRealtime() - elapsedRealtime));
- }
- int numIdle = 0;
- for (int i = uidStates.size() - 1; i >= 0; i--) {
- int value = uidStates.valueAt(i);
- if ((value&0x7fff) == (value>>16)) {
- numIdle++;
- }
- }
-
- int[] res = new int[numIdle];
- numIdle = 0;
- for (int i = uidStates.size() - 1; i >= 0; i--) {
- int value = uidStates.valueAt(i);
- if ((value&0x7fff) == (value>>16)) {
- res[numIdle] = uidStates.keyAt(i);
- numIdle++;
- }
- }
-
- return res;
- }
-
- void setAppIdleAsync(String packageName, boolean idle, int userId) {
- if (packageName == null || !mAppIdleEnabled) return;
-
- mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName)
- .sendToTarget();
- }
-
- @StandbyBuckets public int getAppStandbyBucket(String packageName, int userId,
- long elapsedRealtime, boolean shouldObfuscateInstantApps) {
- if (!mAppIdleEnabled || (shouldObfuscateInstantApps
- && mInjector.isPackageEphemeral(userId, packageName))) {
- return STANDBY_BUCKET_ACTIVE;
- }
-
- synchronized (mAppIdleLock) {
- return mAppIdleHistory.getAppStandbyBucket(packageName, userId, elapsedRealtime);
- }
- }
-
- public List<AppStandbyInfo> getAppStandbyBuckets(int userId) {
- synchronized (mAppIdleLock) {
- return mAppIdleHistory.getAppStandbyBuckets(userId, mAppIdleEnabled);
- }
- }
-
- void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
- int reason, long elapsedRealtime) {
- setAppStandbyBucket(packageName, userId, newBucket, reason, elapsedRealtime, false);
- }
-
- void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
- int reason, long elapsedRealtime, boolean resetTimeout) {
- synchronized (mAppIdleLock) {
- // If the package is not installed, don't allow the bucket to be set.
- if (!mInjector.isPackageInstalled(packageName, 0, userId)) {
- return;
- }
- AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName,
- userId, elapsedRealtime);
- boolean predicted = (reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED;
-
- // Don't allow changing bucket if higher than ACTIVE
- if (app.currentBucket < STANDBY_BUCKET_ACTIVE) return;
-
- // Don't allow prediction to change from/to NEVER
- if ((app.currentBucket == STANDBY_BUCKET_NEVER
- || newBucket == STANDBY_BUCKET_NEVER)
- && predicted) {
- return;
- }
-
- // If the bucket was forced, don't allow prediction to override
- if ((app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED && predicted) return;
-
- // If the bucket is required to stay in a higher state for a specified duration, don't
- // override unless the duration has passed
- if (predicted) {
- // Check if the app is within one of the timeouts for forced bucket elevation
- final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
- // In case of not using the prediction, just keep track of it for applying after
- // ACTIVE or WORKING_SET timeout.
- mAppIdleHistory.updateLastPrediction(app, elapsedTimeAdjusted, newBucket);
-
- if (newBucket > STANDBY_BUCKET_ACTIVE
- && app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
- newBucket = STANDBY_BUCKET_ACTIVE;
- reason = app.bucketingReason;
- if (DEBUG) {
- Slog.d(TAG, " Keeping at ACTIVE due to min timeout");
- }
- } else if (newBucket > STANDBY_BUCKET_WORKING_SET
- && app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
- newBucket = STANDBY_BUCKET_WORKING_SET;
- if (app.currentBucket != newBucket) {
- reason = REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
- } else {
- reason = app.bucketingReason;
- }
- if (DEBUG) {
- Slog.d(TAG, " Keeping at WORKING_SET due to min timeout");
- }
- }
- }
-
- mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket,
- reason, resetTimeout);
- }
- maybeInformListeners(packageName, userId, elapsedRealtime, newBucket, reason, false);
- }
-
- @VisibleForTesting
- boolean isActiveDeviceAdmin(String packageName, int userId) {
- synchronized (mActiveAdminApps) {
- final Set<String> adminPkgs = mActiveAdminApps.get(userId);
- return adminPkgs != null && adminPkgs.contains(packageName);
- }
- }
-
- public void addActiveDeviceAdmin(String adminPkg, int userId) {
- synchronized (mActiveAdminApps) {
- Set<String> adminPkgs = mActiveAdminApps.get(userId);
- if (adminPkgs == null) {
- adminPkgs = new ArraySet<>();
- mActiveAdminApps.put(userId, adminPkgs);
- }
- adminPkgs.add(adminPkg);
- }
- }
-
- public void setActiveAdminApps(Set<String> adminPkgs, int userId) {
- synchronized (mActiveAdminApps) {
- if (adminPkgs == null) {
- mActiveAdminApps.remove(userId);
- } else {
- mActiveAdminApps.put(userId, adminPkgs);
- }
- }
- }
-
- public void onAdminDataAvailable() {
- mAdminDataAvailableLatch.countDown();
- }
-
- /**
- * This will only ever be called once - during device boot.
- */
- private void waitForAdminData() {
- if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) {
- ConcurrentUtils.waitForCountDownNoInterrupt(mAdminDataAvailableLatch,
- WAIT_FOR_ADMIN_DATA_TIMEOUT_MS, "Wait for admin data");
- }
- }
-
- Set<String> getActiveAdminAppsForTest(int userId) {
- synchronized (mActiveAdminApps) {
- return mActiveAdminApps.get(userId);
- }
- }
-
- /**
- * Returns {@code true} if the supplied package is the device provisioning app. Otherwise,
- * returns {@code false}.
- */
- private boolean isDeviceProvisioningPackage(String packageName) {
- String deviceProvisioningPackage = mContext.getResources().getString(
- com.android.internal.R.string.config_deviceProvisioningPackage);
- return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName);
- }
-
- private boolean isCarrierApp(String packageName) {
- synchronized (mAppIdleLock) {
- if (!mHaveCarrierPrivilegedApps) {
- fetchCarrierPrivilegedAppsLocked();
- }
- if (mCarrierPrivilegedApps != null) {
- return mCarrierPrivilegedApps.contains(packageName);
- }
- return false;
- }
- }
-
- void clearCarrierPrivilegedApps() {
- if (DEBUG) {
- Slog.i(TAG, "Clearing carrier privileged apps list");
- }
- synchronized (mAppIdleLock) {
- mHaveCarrierPrivilegedApps = false;
- mCarrierPrivilegedApps = null; // Need to be refetched.
- }
- }
-
- @GuardedBy("mAppIdleLock")
- private void fetchCarrierPrivilegedAppsLocked() {
- TelephonyManager telephonyManager =
- mContext.getSystemService(TelephonyManager.class);
- mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivilegesForAllPhones();
- mHaveCarrierPrivilegedApps = true;
- if (DEBUG) {
- Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps);
- }
- }
-
- private boolean isActiveNetworkScorer(String packageName) {
- String activeScorer = mInjector.getActiveNetworkScorer();
- return packageName != null && packageName.equals(activeScorer);
- }
-
- void informListeners(String packageName, int userId, int bucket, int reason,
- boolean userInteraction) {
- final boolean idle = bucket >= STANDBY_BUCKET_RARE;
- synchronized (mPackageAccessListeners) {
- for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
- listener.onAppIdleStateChanged(packageName, userId, idle, bucket, reason);
- if (userInteraction) {
- listener.onUserInteractionStarted(packageName, userId);
- }
- }
- }
- }
-
- void informParoleStateChanged() {
- final boolean paroled = isParoledOrCharging();
- synchronized (mPackageAccessListeners) {
- for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
- listener.onParoleStateChanged(paroled);
- }
- }
- }
-
- void flushToDisk(int userId) {
- synchronized (mAppIdleLock) {
- mAppIdleHistory.writeAppIdleTimes(userId);
- }
- }
-
- void flushDurationsToDisk() {
- // Persist elapsed and screen on time. If this fails for whatever reason, the apps will be
- // considered not-idle, which is the safest outcome in such an event.
- synchronized (mAppIdleLock) {
- mAppIdleHistory.writeAppIdleDurations();
- }
- }
-
- boolean isDisplayOn() {
- return mInjector.isDefaultDisplayOn();
- }
-
- void clearAppIdleForPackage(String packageName, int userId) {
- synchronized (mAppIdleLock) {
- mAppIdleHistory.clearUsage(packageName, userId);
- }
- }
-
- private class PackageReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_PACKAGE_ADDED.equals(action)
- || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
- clearCarrierPrivilegedApps();
- }
- if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
- Intent.ACTION_PACKAGE_ADDED.equals(action))
- && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
- clearAppIdleForPackage(intent.getData().getSchemeSpecificPart(),
- getSendingUserId());
- }
- }
- }
-
- void initializeDefaultsForSystemApps(int userId) {
- if (!mSystemServicesReady) {
- // Do it later, since SettingsProvider wasn't queried yet for app_standby_enabled
- mPendingInitializeDefaults = true;
- return;
- }
- Slog.d(TAG, "Initializing defaults for system apps on user " + userId + ", "
- + "appIdleEnabled=" + mAppIdleEnabled);
- final long elapsedRealtime = mInjector.elapsedRealtime();
- List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
- PackageManager.MATCH_DISABLED_COMPONENTS,
- userId);
- final int packageCount = packages.size();
- synchronized (mAppIdleLock) {
- for (int i = 0; i < packageCount; i++) {
- final PackageInfo pi = packages.get(i);
- String packageName = pi.packageName;
- if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
- // Mark app as used for 2 hours. After that it can timeout to whatever the
- // past usage pattern was.
- mAppIdleHistory.reportUsage(packageName, userId, STANDBY_BUCKET_ACTIVE,
- REASON_SUB_USAGE_SYSTEM_UPDATE, 0,
- elapsedRealtime + mSystemUpdateUsageTimeoutMillis);
- }
- }
- // Immediately persist defaults to disk
- mAppIdleHistory.writeAppIdleTimes(userId);
- }
- }
-
- void postReportContentProviderUsage(String name, String packageName, int userId) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = name;
- args.arg2 = packageName;
- args.arg3 = userId;
- mHandler.obtainMessage(MSG_REPORT_CONTENT_PROVIDER_USAGE, args)
- .sendToTarget();
- }
-
- void postReportSyncScheduled(String packageName, int userId, boolean exempted) {
- mHandler.obtainMessage(MSG_REPORT_SYNC_SCHEDULED, userId, exempted ? 1 : 0, packageName)
- .sendToTarget();
- }
-
- void postReportExemptedSyncStart(String packageName, int userId) {
- mHandler.obtainMessage(MSG_REPORT_EXEMPTED_SYNC_START, userId, 0, packageName)
- .sendToTarget();
- }
-
- void dumpUser(IndentingPrintWriter idpw, int userId, String pkg) {
- synchronized (mAppIdleLock) {
- mAppIdleHistory.dump(idpw, userId, pkg);
- }
- }
-
- void dumpState(String[] args, PrintWriter pw) {
- synchronized (mAppIdleLock) {
- pw.println("Carrier privileged apps (have=" + mHaveCarrierPrivilegedApps
- + "): " + mCarrierPrivilegedApps);
- }
-
- final long now = System.currentTimeMillis();
-
- pw.println();
- pw.println("Settings:");
-
- pw.print(" mCheckIdleIntervalMillis=");
- TimeUtils.formatDuration(mCheckIdleIntervalMillis, pw);
- pw.println();
-
- pw.print(" mAppIdleParoleIntervalMillis=");
- TimeUtils.formatDuration(mAppIdleParoleIntervalMillis, pw);
- pw.println();
-
- pw.print(" mAppIdleParoleWindowMillis=");
- TimeUtils.formatDuration(mAppIdleParoleWindowMillis, pw);
- pw.println();
-
- pw.print(" mAppIdleParoleDurationMillis=");
- TimeUtils.formatDuration(mAppIdleParoleDurationMillis, pw);
- pw.println();
-
- pw.print(" mStrongUsageTimeoutMillis=");
- TimeUtils.formatDuration(mStrongUsageTimeoutMillis, pw);
- pw.println();
- pw.print(" mNotificationSeenTimeoutMillis=");
- TimeUtils.formatDuration(mNotificationSeenTimeoutMillis, pw);
- pw.println();
- pw.print(" mSyncAdapterTimeoutMillis=");
- TimeUtils.formatDuration(mSyncAdapterTimeoutMillis, pw);
- pw.println();
- pw.print(" mSystemInteractionTimeoutMillis=");
- TimeUtils.formatDuration(mSystemInteractionTimeoutMillis, pw);
- pw.println();
- pw.print(" mInitialForegroundServiceStartTimeoutMillis=");
- TimeUtils.formatDuration(mInitialForegroundServiceStartTimeoutMillis, pw);
- pw.println();
-
- pw.print(" mPredictionTimeoutMillis=");
- TimeUtils.formatDuration(mPredictionTimeoutMillis, pw);
- pw.println();
-
- pw.print(" mExemptedSyncScheduledNonDozeTimeoutMillis=");
- TimeUtils.formatDuration(mExemptedSyncScheduledNonDozeTimeoutMillis, pw);
- pw.println();
- pw.print(" mExemptedSyncScheduledDozeTimeoutMillis=");
- TimeUtils.formatDuration(mExemptedSyncScheduledDozeTimeoutMillis, pw);
- pw.println();
- pw.print(" mExemptedSyncStartTimeoutMillis=");
- TimeUtils.formatDuration(mExemptedSyncStartTimeoutMillis, pw);
- pw.println();
- pw.print(" mUnexemptedSyncScheduledTimeoutMillis=");
- TimeUtils.formatDuration(mUnexemptedSyncScheduledTimeoutMillis, pw);
- pw.println();
-
- pw.print(" mSystemUpdateUsageTimeoutMillis=");
- TimeUtils.formatDuration(mSystemUpdateUsageTimeoutMillis, pw);
- pw.println();
-
- pw.print(" mStableChargingThresholdMillis=");
- TimeUtils.formatDuration(mStableChargingThresholdMillis, pw);
- pw.println();
-
- pw.println();
- pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
- pw.print(" mAppIdleTempParoled="); pw.print(mAppIdleTempParoled);
- pw.print(" mCharging="); pw.print(mCharging);
- pw.print(" mChargingStable="); pw.print(mChargingStable);
- pw.print(" mLastAppIdleParoledTime=");
- TimeUtils.formatDuration(now - mLastAppIdleParoledTime, pw);
- pw.println();
- pw.print("mScreenThresholds="); pw.println(Arrays.toString(mAppStandbyScreenThresholds));
- pw.print("mElapsedThresholds="); pw.println(Arrays.toString(mAppStandbyElapsedThresholds));
- pw.print("mStableChargingThresholdMillis=");
- TimeUtils.formatDuration(mStableChargingThresholdMillis, pw);
- pw.println();
- }
-
- /**
- * Injector for interaction with external code. Override methods to provide a mock
- * implementation for tests.
- * onBootPhase() must be called with at least the PHASE_SYSTEM_SERVICES_READY
- */
- static class Injector {
-
- private final Context mContext;
- private final Looper mLooper;
- private IDeviceIdleController mDeviceIdleController;
- private IBatteryStats mBatteryStats;
- private PackageManagerInternal mPackageManagerInternal;
- private DisplayManager mDisplayManager;
- private PowerManager mPowerManager;
- int mBootPhase;
-
- Injector(Context context, Looper looper) {
- mContext = context;
- mLooper = looper;
- }
-
- Context getContext() {
- return mContext;
- }
-
- Looper getLooper() {
- return mLooper;
- }
-
- void onBootPhase(int phase) {
- if (phase == PHASE_SYSTEM_SERVICES_READY) {
- mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
- ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
- mBatteryStats = IBatteryStats.Stub.asInterface(
- ServiceManager.getService(BatteryStats.SERVICE_NAME));
- mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
- mDisplayManager = (DisplayManager) mContext.getSystemService(
- Context.DISPLAY_SERVICE);
- mPowerManager = mContext.getSystemService(PowerManager.class);
- }
- mBootPhase = phase;
- }
-
- int getBootPhase() {
- return mBootPhase;
- }
-
- /**
- * Returns the elapsed realtime since the device started. Override this
- * to control the clock.
- * @return elapsed realtime
- */
- long elapsedRealtime() {
- return SystemClock.elapsedRealtime();
- }
-
- long currentTimeMillis() {
- return System.currentTimeMillis();
- }
-
- boolean isAppIdleEnabled() {
- final boolean buildFlag = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_enableAutoPowerModes);
- final boolean runtimeFlag = Global.getInt(mContext.getContentResolver(),
- Global.APP_STANDBY_ENABLED, 1) == 1
- && Global.getInt(mContext.getContentResolver(),
- Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, 1) == 1;
- return buildFlag && runtimeFlag;
- }
-
- boolean isCharging() {
- return mContext.getSystemService(BatteryManager.class).isCharging();
- }
-
- boolean isPowerSaveWhitelistExceptIdleApp(String packageName) throws RemoteException {
- return mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName);
- }
-
- File getDataSystemDirectory() {
- return Environment.getDataSystemDirectory();
- }
-
- void noteEvent(int event, String packageName, int uid) throws RemoteException {
- mBatteryStats.noteEvent(event, packageName, uid);
- }
-
- boolean isPackageEphemeral(int userId, String packageName) {
- return mPackageManagerInternal.isPackageEphemeral(userId, packageName);
- }
-
- boolean isPackageInstalled(String packageName, int flags, int userId) {
- return mPackageManagerInternal.getPackageUid(packageName, flags, userId) >= 0;
- }
-
- int[] getRunningUserIds() throws RemoteException {
- return ActivityManager.getService().getRunningUserIds();
- }
-
- boolean isDefaultDisplayOn() {
- return mDisplayManager
- .getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON;
- }
-
- void registerDisplayListener(DisplayManager.DisplayListener listener, Handler handler) {
- mDisplayManager.registerDisplayListener(listener, handler);
- }
-
- String getActiveNetworkScorer() {
- NetworkScoreManager nsm = (NetworkScoreManager) mContext.getSystemService(
- Context.NETWORK_SCORE_SERVICE);
- return nsm.getActiveScorerPackage();
- }
-
- public boolean isBoundWidgetPackage(AppWidgetManager appWidgetManager, String packageName,
- int userId) {
- return appWidgetManager.isBoundWidgetPackage(packageName, userId);
- }
-
- String getAppIdleSettings() {
- return Global.getString(mContext.getContentResolver(),
- Global.APP_IDLE_CONSTANTS);
- }
-
- /** Whether the device is in doze or not. */
- public boolean isDeviceIdleMode() {
- return mPowerManager.isDeviceIdleMode();
- }
- }
-
- class AppStandbyHandler extends Handler {
-
- AppStandbyHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_INFORM_LISTENERS:
- StandbyUpdateRecord r = (StandbyUpdateRecord) msg.obj;
- informListeners(r.packageName, r.userId, r.bucket, r.reason,
- r.isUserInteraction);
- r.recycle();
- break;
-
- case MSG_FORCE_IDLE_STATE:
- forceIdleState((String) msg.obj, msg.arg1, msg.arg2 == 1);
- break;
-
- case MSG_CHECK_IDLE_STATES:
- if (checkIdleStates(msg.arg1) && mAppIdleEnabled) {
- mHandler.sendMessageDelayed(mHandler.obtainMessage(
- MSG_CHECK_IDLE_STATES, msg.arg1, 0),
- mCheckIdleIntervalMillis);
- }
- break;
-
- case MSG_ONE_TIME_CHECK_IDLE_STATES:
- mHandler.removeMessages(MSG_ONE_TIME_CHECK_IDLE_STATES);
- waitForAdminData();
- checkIdleStates(UserHandle.USER_ALL);
- break;
-
- case MSG_CHECK_PAROLE_TIMEOUT:
- checkParoleTimeout();
- break;
-
- case MSG_PAROLE_END_TIMEOUT:
- if (DEBUG) Slog.d(TAG, "Ending parole");
- setAppIdleParoled(false);
- break;
-
- case MSG_REPORT_CONTENT_PROVIDER_USAGE:
- SomeArgs args = (SomeArgs) msg.obj;
- reportContentProviderUsage((String) args.arg1, // authority name
- (String) args.arg2, // package name
- (int) args.arg3); // userId
- args.recycle();
- break;
-
- case MSG_PAROLE_STATE_CHANGED:
- if (DEBUG) Slog.d(TAG, "Parole state: " + mAppIdleTempParoled
- + ", Charging state:" + mChargingStable);
- informParoleStateChanged();
- break;
- case MSG_CHECK_PACKAGE_IDLE_STATE:
- checkAndUpdateStandbyState((String) msg.obj, msg.arg1, msg.arg2,
- mInjector.elapsedRealtime());
- break;
-
- case MSG_REPORT_SYNC_SCHEDULED:
- final boolean exempted = msg.arg1 > 0 ? true : false;
- if (exempted) {
- reportExemptedSyncScheduled((String) msg.obj, msg.arg1);
- } else {
- reportUnexemptedSyncScheduled((String) msg.obj, msg.arg1);
- }
- break;
-
- case MSG_REPORT_EXEMPTED_SYNC_START:
- reportExemptedSyncStart((String) msg.obj, msg.arg1);
- break;
-
- case MSG_UPDATE_STABLE_CHARGING:
- updateChargingStableState();
- break;
-
- default:
- super.handleMessage(msg);
- break;
-
- }
- }
- };
-
- private class DeviceStateReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- switch (intent.getAction()) {
- case BatteryManager.ACTION_CHARGING:
- setChargingState(true);
- break;
- case BatteryManager.ACTION_DISCHARGING:
- setChargingState(false);
- break;
- case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
- onDeviceIdleModeChanged();
- break;
- }
- }
- }
-
- private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder().build();
-
- private final ConnectivityManager.NetworkCallback mNetworkCallback
- = new ConnectivityManager.NetworkCallback() {
- @Override
- public void onAvailable(Network network) {
- mConnectivityManager.unregisterNetworkCallback(this);
- checkParoleTimeout();
- }
- };
-
- private final DisplayManager.DisplayListener mDisplayListener
- = new DisplayManager.DisplayListener() {
-
- @Override public void onDisplayAdded(int displayId) {
- }
-
- @Override public void onDisplayRemoved(int displayId) {
- }
-
- @Override public void onDisplayChanged(int displayId) {
- if (displayId == Display.DEFAULT_DISPLAY) {
- final boolean displayOn = isDisplayOn();
- synchronized (mAppIdleLock) {
- mAppIdleHistory.updateDisplay(displayOn, mInjector.elapsedRealtime());
- }
- }
- }
- };
-
- /**
- * Observe settings changes for {@link Global#APP_IDLE_CONSTANTS}.
- */
- private class SettingsObserver extends ContentObserver {
- /**
- * This flag has been used to disable app idle on older builds with bug b/26355386.
- */
- @Deprecated
- private static final String KEY_IDLE_DURATION_OLD = "idle_duration";
- @Deprecated
- private static final String KEY_IDLE_DURATION = "idle_duration2";
- @Deprecated
- private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold";
-
- private static final String KEY_PAROLE_INTERVAL = "parole_interval";
- private static final String KEY_PAROLE_WINDOW = "parole_window";
- private static final String KEY_PAROLE_DURATION = "parole_duration";
- private static final String KEY_SCREEN_TIME_THRESHOLDS = "screen_thresholds";
- private static final String KEY_ELAPSED_TIME_THRESHOLDS = "elapsed_thresholds";
- private static final String KEY_STRONG_USAGE_HOLD_DURATION = "strong_usage_duration";
- private static final String KEY_NOTIFICATION_SEEN_HOLD_DURATION =
- "notification_seen_duration";
- private static final String KEY_SYSTEM_UPDATE_HOLD_DURATION =
- "system_update_usage_duration";
- private static final String KEY_PREDICTION_TIMEOUT = "prediction_timeout";
- private static final String KEY_SYNC_ADAPTER_HOLD_DURATION = "sync_adapter_duration";
- private static final String KEY_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_HOLD_DURATION =
- "exempted_sync_scheduled_nd_duration";
- private static final String KEY_EXEMPTED_SYNC_SCHEDULED_DOZE_HOLD_DURATION =
- "exempted_sync_scheduled_d_duration";
- private static final String KEY_EXEMPTED_SYNC_START_HOLD_DURATION =
- "exempted_sync_start_duration";
- private static final String KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION =
- "unexempted_sync_scheduled_duration";
- private static final String KEY_SYSTEM_INTERACTION_HOLD_DURATION =
- "system_interaction_duration";
- private static final String KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION =
- "initial_foreground_service_start_duration";
- private static final String KEY_STABLE_CHARGING_THRESHOLD = "stable_charging_threshold";
- public static final long DEFAULT_STRONG_USAGE_TIMEOUT = 1 * ONE_HOUR;
- public static final long DEFAULT_NOTIFICATION_TIMEOUT = 12 * ONE_HOUR;
- public static final long DEFAULT_SYSTEM_UPDATE_TIMEOUT = 2 * ONE_HOUR;
- public static final long DEFAULT_SYSTEM_INTERACTION_TIMEOUT = 10 * ONE_MINUTE;
- public static final long DEFAULT_SYNC_ADAPTER_TIMEOUT = 10 * ONE_MINUTE;
- public static final long DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT = 10 * ONE_MINUTE;
- public static final long DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT = 4 * ONE_HOUR;
- public static final long DEFAULT_EXEMPTED_SYNC_START_TIMEOUT = 10 * ONE_MINUTE;
- public static final long DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT = 10 * ONE_MINUTE;
- public static final long DEFAULT_STABLE_CHARGING_THRESHOLD = 10 * ONE_MINUTE;
- public static final long DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT = 30 * ONE_MINUTE;
-
- private final KeyValueListParser mParser = new KeyValueListParser(',');
-
- SettingsObserver(Handler handler) {
- super(handler);
- }
-
- void registerObserver() {
- final ContentResolver cr = mContext.getContentResolver();
- cr.registerContentObserver(Global.getUriFor(Global.APP_IDLE_CONSTANTS), false, this);
- cr.registerContentObserver(Global.getUriFor(Global.APP_STANDBY_ENABLED), false, this);
- cr.registerContentObserver(Global.getUriFor(Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED),
- false, this);
- }
-
- @Override
- public void onChange(boolean selfChange) {
- updateSettings();
- postOneTimeCheckIdleStates();
- }
-
- void updateSettings() {
- if (DEBUG) {
- Slog.d(TAG,
- "appidle=" + Global.getString(mContext.getContentResolver(),
- Global.APP_STANDBY_ENABLED));
- Slog.d(TAG,
- "adaptivebat=" + Global.getString(mContext.getContentResolver(),
- Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED));
- Slog.d(TAG, "appidleconstants=" + Global.getString(
- mContext.getContentResolver(),
- Global.APP_IDLE_CONSTANTS));
- }
-
- // Look at global settings for this.
- // TODO: Maybe apply different thresholds for different users.
- try {
- mParser.setString(mInjector.getAppIdleSettings());
- } catch (IllegalArgumentException e) {
- Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage());
- // fallthrough, mParser is empty and all defaults will be returned.
- }
-
- synchronized (mAppIdleLock) {
-
- // Default: 24 hours between paroles
- mAppIdleParoleIntervalMillis = mParser.getDurationMillis(KEY_PAROLE_INTERVAL,
- COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE);
-
- // Default: 2 hours to wait on network
- mAppIdleParoleWindowMillis = mParser.getDurationMillis(KEY_PAROLE_WINDOW,
- COMPRESS_TIME ? ONE_MINUTE * 2 : 2 * 60 * ONE_MINUTE);
-
- mAppIdleParoleDurationMillis = mParser.getDurationMillis(KEY_PAROLE_DURATION,
- COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
-
- String screenThresholdsValue = mParser.getString(KEY_SCREEN_TIME_THRESHOLDS, null);
- mAppStandbyScreenThresholds = parseLongArray(screenThresholdsValue,
- SCREEN_TIME_THRESHOLDS);
-
- String elapsedThresholdsValue = mParser.getString(KEY_ELAPSED_TIME_THRESHOLDS,
- null);
- mAppStandbyElapsedThresholds = parseLongArray(elapsedThresholdsValue,
- ELAPSED_TIME_THRESHOLDS);
- mCheckIdleIntervalMillis = Math.min(mAppStandbyElapsedThresholds[1] / 4,
- COMPRESS_TIME ? ONE_MINUTE : 4 * 60 * ONE_MINUTE); // 4 hours
- mStrongUsageTimeoutMillis = mParser.getDurationMillis(
- KEY_STRONG_USAGE_HOLD_DURATION,
- COMPRESS_TIME ? ONE_MINUTE : DEFAULT_STRONG_USAGE_TIMEOUT);
- mNotificationSeenTimeoutMillis = mParser.getDurationMillis(
- KEY_NOTIFICATION_SEEN_HOLD_DURATION,
- COMPRESS_TIME ? 12 * ONE_MINUTE : DEFAULT_NOTIFICATION_TIMEOUT);
- mSystemUpdateUsageTimeoutMillis = mParser.getDurationMillis(
- KEY_SYSTEM_UPDATE_HOLD_DURATION,
- COMPRESS_TIME ? 2 * ONE_MINUTE : DEFAULT_SYSTEM_UPDATE_TIMEOUT);
- mPredictionTimeoutMillis = mParser.getDurationMillis(
- KEY_PREDICTION_TIMEOUT,
- COMPRESS_TIME ? 10 * ONE_MINUTE : DEFAULT_PREDICTION_TIMEOUT);
- mSyncAdapterTimeoutMillis = mParser.getDurationMillis(
- KEY_SYNC_ADAPTER_HOLD_DURATION,
- COMPRESS_TIME ? ONE_MINUTE : DEFAULT_SYNC_ADAPTER_TIMEOUT);
-
- mExemptedSyncScheduledNonDozeTimeoutMillis = mParser.getDurationMillis(
- KEY_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_HOLD_DURATION,
- COMPRESS_TIME ? (ONE_MINUTE / 2)
- : DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT);
-
- mExemptedSyncScheduledDozeTimeoutMillis = mParser.getDurationMillis(
- KEY_EXEMPTED_SYNC_SCHEDULED_DOZE_HOLD_DURATION,
- COMPRESS_TIME ? ONE_MINUTE
- : DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT);
-
- mExemptedSyncStartTimeoutMillis = mParser.getDurationMillis(
- KEY_EXEMPTED_SYNC_START_HOLD_DURATION,
- COMPRESS_TIME ? ONE_MINUTE
- : DEFAULT_EXEMPTED_SYNC_START_TIMEOUT);
-
- mUnexemptedSyncScheduledTimeoutMillis = mParser.getDurationMillis(
- KEY_EXEMPTED_SYNC_SCHEDULED_DOZE_HOLD_DURATION,
- COMPRESS_TIME ? ONE_MINUTE
- : DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT); // TODO
-
- mSystemInteractionTimeoutMillis = mParser.getDurationMillis(
- KEY_SYSTEM_INTERACTION_HOLD_DURATION,
- COMPRESS_TIME ? ONE_MINUTE : DEFAULT_SYSTEM_INTERACTION_TIMEOUT);
-
- mInitialForegroundServiceStartTimeoutMillis = mParser.getDurationMillis(
- KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION,
- COMPRESS_TIME ? ONE_MINUTE :
- DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT);
-
- mStableChargingThresholdMillis = mParser.getDurationMillis(
- KEY_STABLE_CHARGING_THRESHOLD,
- COMPRESS_TIME ? ONE_MINUTE : DEFAULT_STABLE_CHARGING_THRESHOLD);
- }
-
- // Check if app_idle_enabled has changed. Do this after getting the rest of the settings
- // in case we need to change something based on the new values.
- setAppIdleEnabled(mInjector.isAppIdleEnabled());
- }
-
- long[] parseLongArray(String values, long[] defaults) {
- if (values == null) return defaults;
- if (values.isEmpty()) {
- // Reset to defaults
- return defaults;
- } else {
- String[] thresholds = values.split("/");
- if (thresholds.length == THRESHOLD_BUCKETS.length) {
- long[] array = new long[THRESHOLD_BUCKETS.length];
- for (int i = 0; i < THRESHOLD_BUCKETS.length; i++) {
- try {
- if (thresholds[i].startsWith("P") || thresholds[i].startsWith("p")) {
- array[i] = Duration.parse(thresholds[i]).toMillis();
- } else {
- array[i] = Long.parseLong(thresholds[i]);
- }
- } catch (NumberFormatException|DateTimeParseException e) {
- return defaults;
- }
- }
- return array;
- } else {
- return defaults;
- }
- }
- }
- }
-}
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index a783a4007da1..46b261b64192 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -42,8 +42,12 @@ import android.app.usage.EventStats;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
import android.content.res.Configuration;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.util.proto.ProtoInputStream;
import com.android.internal.annotations.VisibleForTesting;
@@ -52,6 +56,8 @@ import java.io.IOException;
import java.util.List;
public class IntervalStats {
+ private static final String TAG = "IntervalStats";
+
public static final int CURRENT_MAJOR_VERSION = 1;
public static final int CURRENT_MINOR_VERSION = 1;
public int majorVersion = CURRENT_MAJOR_VERSION;
@@ -64,6 +70,8 @@ public class IntervalStats {
public final EventTracker keyguardShownTracker = new EventTracker();
public final EventTracker keyguardHiddenTracker = new EventTracker();
public final ArrayMap<String, UsageStats> packageStats = new ArrayMap<>();
+ /** @hide */
+ public final SparseArray<UsageStats> packageStatsObfuscated = new SparseArray<>();
public final ArrayMap<Configuration, ConfigurationStats> configurations = new ArrayMap<>();
public Configuration activeConfiguration;
public final EventList events = new EventList();
@@ -436,4 +444,234 @@ public class IntervalStats {
*/
majorVersion = CURRENT_MAJOR_VERSION;
}
+
+ /**
+ * Parses all of the tokens to strings in the obfuscated usage stats data. This includes
+ * deobfuscating each of the package tokens and chooser actions and categories.
+ */
+ private void deobfuscateUsageStats(PackagesTokenData packagesTokenData) {
+ final int usageStatsSize = packageStatsObfuscated.size();
+ for (int statsIndex = 0; statsIndex < usageStatsSize; statsIndex++) {
+ final int packageToken = packageStatsObfuscated.keyAt(statsIndex);
+ final UsageStats usageStats = packageStatsObfuscated.valueAt(statsIndex);
+ usageStats.mPackageName = packagesTokenData.getPackageString(packageToken);
+ if (usageStats.mPackageName == null) {
+ Slog.e(TAG, "Unable to parse usage stats package " + packageToken);
+ continue;
+ }
+
+ // Update chooser counts
+ final int chooserActionsSize = usageStats.mChooserCountsObfuscated.size();
+ for (int actionIndex = 0; actionIndex < chooserActionsSize; actionIndex++) {
+ final ArrayMap<String, Integer> categoryCountsMap = new ArrayMap<>();
+ final int actionToken = usageStats.mChooserCountsObfuscated.keyAt(actionIndex);
+ final String action = packagesTokenData.getString(packageToken, actionToken);
+ if (action == null) {
+ Slog.i(TAG, "Unable to parse chooser action " + actionToken
+ + " for package " + packageToken);
+ continue;
+ }
+ final SparseIntArray categoryCounts =
+ usageStats.mChooserCountsObfuscated.valueAt(actionIndex);
+ final int categoriesSize = categoryCounts.size();
+ for (int categoryIndex = 0; categoryIndex < categoriesSize; categoryIndex++) {
+ final int categoryToken = categoryCounts.keyAt(categoryIndex);
+ final String category = packagesTokenData.getString(packageToken,
+ categoryToken);
+ if (category == null) {
+ Slog.i(TAG, "Unable to parse chooser category " + categoryToken
+ + " for package " + packageToken);
+ continue;
+ }
+ categoryCountsMap.put(category, categoryCounts.valueAt(categoryIndex));
+ }
+ usageStats.mChooserCounts.put(action, categoryCountsMap);
+ }
+ packageStats.put(usageStats.mPackageName, usageStats);
+ }
+ }
+
+ /**
+ * Parses all of the tokens to strings in the obfuscated events data. This includes
+ * deobfuscating the package token, along with any class, task root package/class tokens, and
+ * shortcut or notification channel tokens.
+ */
+ private void deobfuscateEvents(PackagesTokenData packagesTokenData) {
+ for (int i = this.events.size() - 1; i >= 0; i--) {
+ final Event event = this.events.get(i);
+ final int packageToken = event.mPackageToken;
+ event.mPackage = packagesTokenData.getPackageString(packageToken);
+ if (event.mPackage == null) {
+ Slog.e(TAG, "Unable to parse event package " + packageToken);
+ this.events.remove(i);
+ continue;
+ }
+
+ if (event.mClassToken != PackagesTokenData.UNASSIGNED_TOKEN) {
+ event.mClass = packagesTokenData.getString(packageToken, event.mClassToken);
+ if (event.mClass == null) {
+ Slog.i(TAG, "Unable to parse class " + event.mClassToken
+ + " for package " + packageToken);
+ }
+ }
+ if (event.mTaskRootPackageToken != PackagesTokenData.UNASSIGNED_TOKEN) {
+ event.mTaskRootPackage = packagesTokenData.getString(packageToken,
+ event.mTaskRootPackageToken);
+ if (event.mTaskRootPackage == null) {
+ Slog.i(TAG, "Unable to parse task root package " + event.mTaskRootPackageToken
+ + " for package " + packageToken);
+ }
+ }
+ if (event.mTaskRootClassToken != PackagesTokenData.UNASSIGNED_TOKEN) {
+ event.mTaskRootClass = packagesTokenData.getString(packageToken,
+ event.mTaskRootClassToken);
+ if (event.mTaskRootClass == null) {
+ Slog.i(TAG, "Unable to parse task root class " + event.mTaskRootClassToken
+ + " for package " + packageToken);
+ }
+ }
+ switch (event.mEventType) {
+ case CONFIGURATION_CHANGE:
+ if (event.mConfiguration == null) {
+ event.mConfiguration = new Configuration();
+ }
+ break;
+ case SHORTCUT_INVOCATION:
+ event.mShortcutId = packagesTokenData.getString(packageToken,
+ event.mShortcutIdToken);
+ if (event.mShortcutId == null) {
+ Slog.e(TAG, "Unable to parse shortcut " + event.mShortcutIdToken
+ + " for package " + packageToken);
+ this.events.remove(i);
+ continue;
+ }
+ break;
+ case NOTIFICATION_INTERRUPTION:
+ event.mNotificationChannelId = packagesTokenData.getString(packageToken,
+ event.mNotificationChannelIdToken);
+ if (event.mNotificationChannelId == null) {
+ Slog.e(TAG, "Unable to parse notification channel "
+ + event.mNotificationChannelIdToken + " for package "
+ + packageToken);
+ this.events.remove(i);
+ continue;
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * Parses the obfuscated tokenized data held in this interval stats object.
+ *
+ * @hide
+ */
+ public void deobfuscateData(PackagesTokenData packagesTokenData) {
+ deobfuscateUsageStats(packagesTokenData);
+ deobfuscateEvents(packagesTokenData);
+ }
+
+ /**
+ * Obfuscates certain strings within each package stats such as the package name, and the
+ * chooser actions and categories.
+ */
+ private void obfuscateUsageStatsData(PackagesTokenData packagesTokenData) {
+ final int usageStatsSize = packageStats.size();
+ for (int statsIndex = 0; statsIndex < usageStatsSize; statsIndex++) {
+ final String packageName = packageStats.keyAt(statsIndex);
+ final UsageStats usageStats = packageStats.valueAt(statsIndex);
+ if (usageStats == null) {
+ continue;
+ }
+
+ final int packageToken = packagesTokenData.getPackageTokenOrAdd(
+ packageName, usageStats.mEndTimeStamp);
+ // don't obfuscate stats whose packages have been removed
+ if (packageToken == PackagesTokenData.UNASSIGNED_TOKEN) {
+ continue;
+ }
+ usageStats.mPackageToken = packageToken;
+ // Update chooser counts.
+ final int chooserActionsSize = usageStats.mChooserCounts.size();
+ for (int actionIndex = 0; actionIndex < chooserActionsSize; actionIndex++) {
+ final String action = usageStats.mChooserCounts.keyAt(actionIndex);
+ final ArrayMap<String, Integer> categoriesMap =
+ usageStats.mChooserCounts.valueAt(actionIndex);
+ if (categoriesMap == null) {
+ continue;
+ }
+
+ final SparseIntArray categoryCounts = new SparseIntArray();
+ final int categoriesSize = categoriesMap.size();
+ for (int categoryIndex = 0; categoryIndex < categoriesSize; categoryIndex++) {
+ String category = categoriesMap.keyAt(categoryIndex);
+ int categoryToken = packagesTokenData.getTokenOrAdd(packageToken, packageName,
+ category);
+ categoryCounts.put(categoryToken, categoriesMap.valueAt(categoryIndex));
+ }
+ int actionToken = packagesTokenData.getTokenOrAdd(packageToken, packageName,
+ action);
+ usageStats.mChooserCountsObfuscated.put(actionToken, categoryCounts);
+ }
+ packageStatsObfuscated.put(packageToken, usageStats);
+ }
+ }
+
+ /**
+ * Obfuscates certain strings within an event such as the package name, the class name,
+ * task root package and class names, and shortcut and notification channel ids.
+ */
+ private void obfuscateEventsData(PackagesTokenData packagesTokenData) {
+ for (int i = events.size() - 1; i >= 0; i--) {
+ final Event event = events.get(i);
+ if (event == null) {
+ continue;
+ }
+
+ final int packageToken = packagesTokenData.getPackageTokenOrAdd(
+ event.mPackage, event.mTimeStamp);
+ // don't obfuscate events from packages that have been removed
+ if (packageToken == PackagesTokenData.UNASSIGNED_TOKEN) {
+ events.remove(i);
+ continue;
+ }
+ event.mPackageToken = packageToken;
+ if (!TextUtils.isEmpty(event.mClass)) {
+ event.mClassToken = packagesTokenData.getTokenOrAdd(packageToken,
+ event.mPackage, event.mClass);
+ }
+ if (!TextUtils.isEmpty(event.mTaskRootPackage)) {
+ event.mTaskRootPackageToken = packagesTokenData.getTokenOrAdd(packageToken,
+ event.mPackage, event.mTaskRootPackage);
+ }
+ if (!TextUtils.isEmpty(event.mTaskRootClass)) {
+ event.mTaskRootClassToken = packagesTokenData.getTokenOrAdd(packageToken,
+ event.mPackage, event.mTaskRootClass);
+ }
+ switch (event.mEventType) {
+ case SHORTCUT_INVOCATION:
+ if (!TextUtils.isEmpty(event.mShortcutId)) {
+ event.mShortcutIdToken = packagesTokenData.getTokenOrAdd(packageToken,
+ event.mPackage, event.mShortcutId);
+ }
+ break;
+ case NOTIFICATION_INTERRUPTION:
+ if (!TextUtils.isEmpty(event.mNotificationChannelId)) {
+ event.mNotificationChannelIdToken = packagesTokenData.getTokenOrAdd(
+ packageToken, event.mPackage, event.mNotificationChannelId);
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * Obfuscates the data in this instance of interval stats.
+ *
+ * @hide
+ */
+ public void obfuscateData(PackagesTokenData packagesTokenData) {
+ obfuscateUsageStatsData(packagesTokenData);
+ obfuscateEventsData(packagesTokenData);
+ }
}
diff --git a/services/usage/java/com/android/server/usage/PackagesTokenData.java b/services/usage/java/com/android/server/usage/PackagesTokenData.java
new file mode 100644
index 000000000000..4bf08a49af0f
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/PackagesTokenData.java
@@ -0,0 +1,176 @@
+/*
+ * 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.
+ */
+package com.android.server.usage;
+
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+
+/**
+ * An object holding data defining the obfuscated packages and their token mappings.
+ * Used by {@link UsageStatsDatabase}.
+ *
+ * @hide
+ */
+public final class PackagesTokenData {
+ /**
+ * The package name is always stored at index 0 in {@code tokensToPackagesMap}.
+ */
+ private static final int PACKAGE_NAME_INDEX = 0;
+
+ /**
+ * The default token for any string that hasn't been tokenized yet.
+ */
+ public static final int UNASSIGNED_TOKEN = -1;
+
+ /**
+ * The main token counter for each package.
+ */
+ public int counter = 1;
+ /**
+ * Stores a hierarchy of token to string mappings for each package, indexed by the main
+ * package token. The 0th index within the array list will always hold the package name.
+ */
+ public final SparseArray<ArrayList<String>> tokensToPackagesMap = new SparseArray<>();
+ /**
+ * Stores a hierarchy of strings to token mappings for each package. This is simply an inverse
+ * map of the {@code tokenToPackagesMap} in this class, mainly for an O(1) access to the tokens.
+ */
+ public final ArrayMap<String, ArrayMap<String, Integer>> packagesToTokensMap = new ArrayMap<>();
+ /**
+ * Stores a map of packages that were removed and when they were removed.
+ */
+ public final ArrayMap<String, Long> removedPackagesMap = new ArrayMap<>();
+
+ public PackagesTokenData() {
+ }
+
+ /**
+ * Fetches the token mapped to the given package name. If there is no mapping, a new token is
+ * created and the relevant mappings are updated.
+ *
+ * @param packageName the package name whose token is being fetched
+ * @param timeStamp the time stamp of the event or end time of the usage stats; used to verify
+ * the package hasn't been removed
+ * @return the mapped token
+ */
+ public int getPackageTokenOrAdd(String packageName, long timeStamp) {
+ final Long timeRemoved = removedPackagesMap.get(packageName);
+ if (timeRemoved != null && timeRemoved > timeStamp) {
+ return UNASSIGNED_TOKEN; // package was removed
+ /*
+ Note: instead of querying Package Manager each time for a list of packages to verify
+ if this package is still installed, it's more efficient to check the internal list of
+ removed packages and verify with the incoming time stamp. Although rare, it is possible
+ that some asynchronous function is triggered after a package is removed and the
+ time stamp passed into this function is not accurate. We'll have to keep the respective
+ event/usage stat until the next time the device reboots and the mappings are cleaned.
+ Additionally, this is a data class with some helper methods - it doesn't make sense to
+ overload it with references to other services.
+ */
+ }
+
+ ArrayMap<String, Integer> packageTokensMap = packagesToTokensMap.get(packageName);
+ if (packageTokensMap == null) {
+ packageTokensMap = new ArrayMap<>();
+ packagesToTokensMap.put(packageName, packageTokensMap);
+ }
+ int token = packageTokensMap.getOrDefault(packageName, UNASSIGNED_TOKEN);
+ if (token == UNASSIGNED_TOKEN) {
+ token = counter++;
+ // package name should always be at index 0 in the sub-mapping
+ ArrayList<String> tokenPackages = new ArrayList<>();
+ tokenPackages.add(packageName);
+ packageTokensMap.put(packageName, token);
+ tokensToPackagesMap.put(token, tokenPackages);
+ }
+ return token;
+ }
+
+ /**
+ * Fetches the token mapped to the given key within the package's context. If there is no
+ * mapping, a new token is created and the relevant mappings are updated.
+ *
+ * @param packageToken the package token for which the given key belongs to
+ * @param packageName the package name for which the given key belongs to
+ * @param key the key whose token is being fetched
+ * @return the mapped token
+ */
+ public int getTokenOrAdd(int packageToken, String packageName, String key) {
+ if (packageName.equals(key)) {
+ return PACKAGE_NAME_INDEX;
+ }
+ int token = packagesToTokensMap.get(packageName).getOrDefault(key, UNASSIGNED_TOKEN);
+ if (token == UNASSIGNED_TOKEN) {
+ token = tokensToPackagesMap.get(packageToken).size();
+ packagesToTokensMap.get(packageName).put(key, token);
+ tokensToPackagesMap.get(packageToken).add(key);
+ }
+ return token;
+ }
+
+ /**
+ * Fetches the package name for the given token.
+ *
+ * @param packageToken the package token representing the package name
+ * @return the string representing the given token or {@code null} if not found
+ */
+ public String getPackageString(int packageToken) {
+ final ArrayList<String> packageStrings = tokensToPackagesMap.get(packageToken);
+ if (packageStrings == null) {
+ return null;
+ }
+ return packageStrings.get(PACKAGE_NAME_INDEX);
+ }
+
+ /**
+ * Fetches the string represented by the given token.
+ *
+ * @param packageToken the package token for which this token belongs to
+ * @param token the token whose string needs to be fetched
+ * @return the string representing the given token or {@code null} if not found
+ */
+ public String getString(int packageToken, int token) {
+ try {
+ return tokensToPackagesMap.get(packageToken).get(token);
+ } catch (NullPointerException npe) {
+ Slog.e("PackagesTokenData",
+ "Unable to find tokenized strings for package " + packageToken, npe);
+ return null;
+ } catch (IndexOutOfBoundsException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Removes the package from all known mappings.
+ *
+ * @param packageName the package to be removed
+ * @param timeRemoved the time stamp of when the package was removed
+ */
+ public void removePackage(String packageName, long timeRemoved) {
+ removedPackagesMap.put(packageName, timeRemoved);
+
+ if (!packagesToTokensMap.containsKey(packageName)) {
+ return;
+ }
+ final int packageToken = packagesToTokensMap.get(packageName).get(packageName);
+ packagesToTokensMap.remove(packageName);
+ tokensToPackagesMap.delete(packageToken);
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index 5197b3bad14b..db7ed1f58c1a 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -17,12 +17,15 @@
package com.android.server.usage;
import android.app.usage.TimeSparseArray;
+import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.os.Build;
import android.os.SystemProperties;
+import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.TimeUtils;
import com.android.internal.annotations.VisibleForTesting;
@@ -75,7 +78,7 @@ import java.util.List;
* directory should be deserialized.
*/
public class UsageStatsDatabase {
- private static final int DEFAULT_CURRENT_VERSION = 4;
+ private static final int DEFAULT_CURRENT_VERSION = 5;
/**
* Current version of the backup schema
*
@@ -93,7 +96,8 @@ public class UsageStatsDatabase {
// Persist versioned backup files.
// Should be false, except when testing new versions
- static final boolean KEEP_BACKUP_DIR = false;
+ // STOPSHIP: b/139937606 this should be false on launch
+ static final boolean KEEP_BACKUP_DIR = true;
private static final String TAG = "UsageStatsDatabase";
private static final boolean DEBUG = UsageStatsService.DEBUG;
@@ -119,6 +123,11 @@ public class UsageStatsDatabase {
private boolean mFirstUpdate;
private boolean mNewUpdate;
+ // The obfuscated packages to tokens mappings file
+ private final File mPackageMappingsFile;
+ // Holds all of the data related to the obfuscated packages and their token mappings.
+ final PackagesTokenData mPackagesTokenData = new PackagesTokenData();
+
/**
* UsageStatsDatabase constructor that allows setting the version number.
* This should only be used for testing.
@@ -138,6 +147,7 @@ public class UsageStatsDatabase {
mBackupsDir = new File(dir, "backups");
mUpdateBreadcrumb = new File(dir, "breadcrumb");
mSortedStatFiles = new TimeSparseArray[mIntervalDirs.length];
+ mPackageMappingsFile = new File(dir, "mappings");
mCal = new UnixCalendar(0);
}
@@ -479,6 +489,11 @@ public class UsageStatsDatabase {
private void continueUpgradeLocked(int version, long token) {
final File backupDir = new File(mBackupsDir, Long.toString(token));
+ // Upgrade step logic for the entire usage stats directory, not individual interval dirs.
+ if (version >= 5) {
+ readMappingsLocked();
+ }
+
// Read each file in the backup according to the version and write to the interval
// directories in the current versions format
for (int i = 0; i < mIntervalDirs.length; i++) {
@@ -494,9 +509,16 @@ public class UsageStatsDatabase {
}
try {
IntervalStats stats = new IntervalStats();
- readLocked(new AtomicFile(files[j]), stats, version);
+ readLocked(new AtomicFile(files[j]), stats, version, mPackagesTokenData);
+ // Upgrade to version 5+.
+ // Future version upgrades should add additional logic here to upgrade.
+ if (mCurrentVersion >= 5) {
+ // Create the initial obfuscated packages map.
+ stats.obfuscateData(mPackagesTokenData);
+ }
writeLocked(new AtomicFile(new File(mIntervalDirs[i],
- Long.toString(stats.beginTime))), stats, mCurrentVersion);
+ Long.toString(stats.beginTime))), stats, mCurrentVersion,
+ mPackagesTokenData);
} catch (Exception e) {
// This method is called on boot, log the exception and move on
Slog.e(TAG, "Failed to upgrade backup file : " + files[j].toString());
@@ -504,6 +526,21 @@ public class UsageStatsDatabase {
}
}
}
+
+ // Upgrade step logic for the entire usage stats directory, not individual interval dirs.
+ if (mCurrentVersion >= 5) {
+ try {
+ writeMappingsLocked();
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write the tokens mappings file.");
+ }
+ }
+ }
+
+ void onPackageRemoved(String packageName, long timeRemoved) {
+ synchronized (mLock) {
+ mPackagesTokenData.removePackage(packageName, timeRemoved);
+ }
}
public void onTimeChanged(long timeDiffMillis) {
@@ -580,6 +617,37 @@ public class UsageStatsDatabase {
}
/**
+ * Filter out those stats from the given stats that belong to removed packages. Filtering out
+ * all of the stats at once has an amortized cost for future calls.
+ */
+ void filterStats(IntervalStats stats) {
+ if (mPackagesTokenData.removedPackagesMap.isEmpty()) {
+ return;
+ }
+ final ArrayMap<String, Long> removedPackagesMap = mPackagesTokenData.removedPackagesMap;
+
+ // filter out package usage stats
+ final int removedPackagesSize = removedPackagesMap.size();
+ for (int i = 0; i < removedPackagesSize; i++) {
+ final String removedPackage = removedPackagesMap.keyAt(i);
+ final UsageStats usageStats = stats.packageStats.get(removedPackage);
+ if (usageStats != null && usageStats.mEndTimeStamp < removedPackagesMap.valueAt(i)) {
+ stats.packageStats.remove(removedPackage);
+ }
+ }
+
+ // filter out events
+ final int eventsSize = stats.events.size();
+ for (int i = stats.events.size() - 1; i >= 0; i--) {
+ final UsageEvents.Event event = stats.events.get(i);
+ final Long timeRemoved = removedPackagesMap.get(event.mPackage);
+ if (timeRemoved != null && timeRemoved > event.mTimeStamp) {
+ stats.events.remove(i);
+ }
+ }
+ }
+
+ /**
* Figures out what to extract from the given IntervalStats object.
*/
public interface StatCombiner<T> {
@@ -808,14 +876,14 @@ public class UsageStatsDatabase {
}
private void writeLocked(AtomicFile file, IntervalStats stats) throws IOException {
- writeLocked(file, stats, mCurrentVersion);
+ writeLocked(file, stats, mCurrentVersion, mPackagesTokenData);
}
- private static void writeLocked(AtomicFile file, IntervalStats stats, int version)
- throws IOException {
+ private static void writeLocked(AtomicFile file, IntervalStats stats, int version,
+ PackagesTokenData packagesTokenData) throws IOException {
FileOutputStream fos = file.startWrite();
try {
- writeLocked(fos, stats, version);
+ writeLocked(fos, stats, version, packagesTokenData);
file.finishWrite(fos);
fos = null;
} finally {
@@ -825,11 +893,11 @@ public class UsageStatsDatabase {
}
private void writeLocked(OutputStream out, IntervalStats stats) throws IOException {
- writeLocked(out, stats, mCurrentVersion);
+ writeLocked(out, stats, mCurrentVersion, mPackagesTokenData);
}
- private static void writeLocked(OutputStream out, IntervalStats stats, int version)
- throws IOException {
+ private static void writeLocked(OutputStream out, IntervalStats stats, int version,
+ PackagesTokenData packagesTokenData) throws IOException {
switch (version) {
case 1:
case 2:
@@ -837,7 +905,19 @@ public class UsageStatsDatabase {
UsageStatsXml.write(out, stats);
break;
case 4:
- UsageStatsProto.write(out, stats);
+ try {
+ UsageStatsProto.write(out, stats);
+ } catch (IOException | IllegalArgumentException e) {
+ Slog.e(TAG, "Unable to write interval stats to proto.", e);
+ }
+ break;
+ case 5:
+ stats.obfuscateData(packagesTokenData);
+ try {
+ UsageStatsProtoV2.write(out, stats);
+ } catch (IOException | IllegalArgumentException e) {
+ Slog.e(TAG, "Unable to write interval stats to proto.", e);
+ }
break;
default:
throw new RuntimeException(
@@ -847,16 +927,16 @@ public class UsageStatsDatabase {
}
private void readLocked(AtomicFile file, IntervalStats statsOut) throws IOException {
- readLocked(file, statsOut, mCurrentVersion);
+ readLocked(file, statsOut, mCurrentVersion, mPackagesTokenData);
}
- private static void readLocked(AtomicFile file, IntervalStats statsOut, int version)
- throws IOException {
+ private static void readLocked(AtomicFile file, IntervalStats statsOut, int version,
+ PackagesTokenData packagesTokenData) throws IOException {
try {
FileInputStream in = file.openRead();
try {
statsOut.beginTime = parseBeginTime(file);
- readLocked(in, statsOut, version);
+ readLocked(in, statsOut, version, packagesTokenData);
statsOut.lastTimeSaved = file.getLastModifiedTime();
} finally {
try {
@@ -872,11 +952,11 @@ public class UsageStatsDatabase {
}
private void readLocked(InputStream in, IntervalStats statsOut) throws IOException {
- readLocked(in, statsOut, mCurrentVersion);
+ readLocked(in, statsOut, mCurrentVersion, mPackagesTokenData);
}
- private static void readLocked(InputStream in, IntervalStats statsOut, int version)
- throws IOException {
+ private static void readLocked(InputStream in, IntervalStats statsOut, int version,
+ PackagesTokenData packagesTokenData) throws IOException {
switch (version) {
case 1:
case 2:
@@ -884,7 +964,19 @@ public class UsageStatsDatabase {
UsageStatsXml.read(in, statsOut);
break;
case 4:
- UsageStatsProto.read(in, statsOut);
+ try {
+ UsageStatsProto.read(in, statsOut);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to read interval stats from proto.", e);
+ }
+ break;
+ case 5:
+ try {
+ UsageStatsProtoV2.read(in, statsOut);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to read interval stats from proto.", e);
+ }
+ statsOut.deobfuscateData(packagesTokenData);
break;
default:
throw new RuntimeException(
@@ -895,6 +987,63 @@ public class UsageStatsDatabase {
}
/**
+ * Reads the obfuscated data file from disk containing the tokens to packages mappings and
+ * rebuilds the packages to tokens mappings based on that data.
+ */
+ public void readMappingsLocked() {
+ if (!mPackageMappingsFile.exists()) {
+ return; // package mappings file is missing - recreate mappings on next write.
+ }
+
+ try (FileInputStream in = new AtomicFile(mPackageMappingsFile).openRead()) {
+ UsageStatsProtoV2.readObfuscatedData(in, mPackagesTokenData);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read the obfuscated packages mapping file.", e);
+ return;
+ }
+
+ final SparseArray<ArrayList<String>> tokensToPackagesMap =
+ mPackagesTokenData.tokensToPackagesMap;
+ final int tokensToPackagesMapSize = tokensToPackagesMap.size();
+ for (int i = 0; i < tokensToPackagesMapSize; i++) {
+ final int packageToken = tokensToPackagesMap.keyAt(i);
+ final ArrayList<String> tokensMap = tokensToPackagesMap.valueAt(i);
+ final ArrayMap<String, Integer> packageStringsMap = new ArrayMap<>();
+ final int tokensMapSize = tokensMap.size();
+ // package name will always be at index 0 but its token should not be 0
+ packageStringsMap.put(tokensMap.get(0), packageToken);
+ for (int j = 1; j < tokensMapSize; j++) {
+ packageStringsMap.put(tokensMap.get(j), j);
+ }
+ mPackagesTokenData.packagesToTokensMap.put(tokensMap.get(0), packageStringsMap);
+ }
+ }
+
+ void writeMappingsLocked() throws IOException {
+ final AtomicFile file = new AtomicFile(mPackageMappingsFile);
+ FileOutputStream fos = file.startWrite();
+ try {
+ UsageStatsProtoV2.writeObfuscatedData(fos, mPackagesTokenData);
+ file.finishWrite(fos);
+ fos = null;
+ } catch (IOException | IllegalArgumentException e) {
+ Slog.e(TAG, "Unable to write obfuscated data to proto.", e);
+ } finally {
+ file.failWrite(fos);
+ }
+ }
+
+ void obfuscateCurrentStats(IntervalStats[] currentStats) {
+ if (mCurrentVersion < 5) {
+ return;
+ }
+ for (int i = 0; i < currentStats.length; i++) {
+ final IntervalStats stats = currentStats[i];
+ stats.obfuscateData(mPackagesTokenData);
+ }
+ }
+
+ /**
* Update the stats in the database. They may not be written to disk immediately.
*/
public void putUsageStats(int intervalType, IntervalStats stats) throws IOException {
@@ -1098,7 +1247,7 @@ public class UsageStatsDatabase {
DataOutputStream out = new DataOutputStream(baos);
try {
out.writeLong(stats.beginTime);
- writeLocked(out, stats, version);
+ writeLocked(out, stats, version, mPackagesTokenData);
} catch (Exception ioe) {
Slog.d(TAG, "Serializing IntervalStats Failed", ioe);
baos.reset();
@@ -1112,7 +1261,7 @@ public class UsageStatsDatabase {
IntervalStats stats = new IntervalStats();
try {
stats.beginTime = in.readLong();
- readLocked(in, stats, version);
+ readLocked(in, stats, version, mPackagesTokenData);
} catch (IOException ioe) {
Slog.d(TAG, "DeSerializing IntervalStats Failed", ioe);
stats = null;
@@ -1142,13 +1291,18 @@ public class UsageStatsDatabase {
}
/**
- * print total number and list of stats files for each interval type.
- * @param pw
+ * Prints the obfuscated package mappings and a summary of the database files.
+ * @param pw the print writer to print to
*/
public void dump(IndentingPrintWriter pw, boolean compact) {
synchronized (mLock) {
+ pw.println();
pw.println("UsageStatsDatabase:");
pw.increaseIndent();
+ dumpMappings(pw);
+ pw.decreaseIndent();
+ pw.println("Database Summary:");
+ pw.increaseIndent();
for (int i = 0; i < mSortedStatFiles.length; i++) {
final TimeSparseArray<AtomicFile> files = mSortedStatFiles[i];
final int size = files.size();
@@ -1173,6 +1327,23 @@ public class UsageStatsDatabase {
}
}
+ void dumpMappings(IndentingPrintWriter pw) {
+ synchronized (mLock) {
+ pw.println("Obfuscated Packages Mappings:");
+ pw.increaseIndent();
+ pw.println("Counter: " + mPackagesTokenData.counter);
+ pw.println("Tokens Map Size: " + mPackagesTokenData.tokensToPackagesMap.size());
+ for (int i = 0; i < mPackagesTokenData.tokensToPackagesMap.size(); i++) {
+ final int packageToken = mPackagesTokenData.tokensToPackagesMap.keyAt(i);
+ final String packageStrings = String.join(", ",
+ mPackagesTokenData.tokensToPackagesMap.valueAt(i));
+ pw.println("Token " + packageToken + ": [" + packageStrings + "]");
+ }
+ pw.println();
+ pw.decreaseIndent();
+ }
+ }
+
IntervalStats readIntervalStatsForFile(int interval, long fileName) {
synchronized (mLock) {
final IntervalStats stats = new IntervalStats();
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProto.java b/services/usage/java/com/android/server/usage/UsageStatsProto.java
index 6d3f416065a3..87e077e3df1f 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsProto.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsProto.java
@@ -120,10 +120,14 @@ final class UsageStatsProto {
IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT);
break;
case (int) IntervalStatsProto.UsageStats.CHOOSER_ACTIONS:
- final long chooserToken = proto.start(
- IntervalStatsProto.UsageStats.CHOOSER_ACTIONS);
- loadChooserCounts(proto, stats);
- proto.end(chooserToken);
+ try {
+ final long chooserToken = proto.start(
+ IntervalStatsProto.UsageStats.CHOOSER_ACTIONS);
+ loadChooserCounts(proto, stats);
+ proto.end(chooserToken);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to read chooser counts for " + stats.mPackageName, e);
+ }
break;
case (int) IntervalStatsProto.UsageStats.LAST_TIME_SERVICE_USED_MS:
// Time attributes stored is an offset of the beginTime.
@@ -153,20 +157,24 @@ final class UsageStatsProto {
}
private static void loadCountAndTime(ProtoInputStream proto, long fieldId,
- IntervalStats.EventTracker tracker) throws IOException {
- final long token = proto.start(fieldId);
- while (true) {
- switch (proto.nextField()) {
- case (int) IntervalStatsProto.CountAndTime.COUNT:
- tracker.count = proto.readInt(IntervalStatsProto.CountAndTime.COUNT);
- break;
- case (int) IntervalStatsProto.CountAndTime.TIME_MS:
- tracker.duration = proto.readLong(IntervalStatsProto.CountAndTime.TIME_MS);
- break;
- case ProtoInputStream.NO_MORE_FIELDS:
- proto.end(token);
- return;
+ IntervalStats.EventTracker tracker) {
+ try {
+ final long token = proto.start(fieldId);
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) IntervalStatsProto.CountAndTime.COUNT:
+ tracker.count = proto.readInt(IntervalStatsProto.CountAndTime.COUNT);
+ break;
+ case (int) IntervalStatsProto.CountAndTime.TIME_MS:
+ tracker.duration = proto.readLong(IntervalStatsProto.CountAndTime.TIME_MS);
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ proto.end(token);
+ return;
+ }
}
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to read event tracker " + fieldId, e);
}
}
@@ -306,7 +314,7 @@ final class UsageStatsProto {
}
private static void writeStringPool(ProtoOutputStream proto, final IntervalStats stats)
- throws IOException {
+ throws IllegalArgumentException {
final long token = proto.start(IntervalStatsProto.STRINGPOOL);
final int size = stats.mStringCache.size();
proto.write(IntervalStatsProto.StringPool.SIZE, size);
@@ -317,7 +325,8 @@ final class UsageStatsProto {
}
private static void writeUsageStats(ProtoOutputStream proto, long fieldId,
- final IntervalStats stats, final UsageStats usageStats) throws IOException {
+ final IntervalStats stats, final UsageStats usageStats)
+ throws IllegalArgumentException {
final long token = proto.start(fieldId);
// Write the package name first, so loadUsageStats can avoid creating an extra object
final int packageIndex = stats.mStringCache.indexOf(usageStats.mPackageName);
@@ -325,8 +334,6 @@ final class UsageStatsProto {
proto.write(IntervalStatsProto.UsageStats.PACKAGE_INDEX, packageIndex + 1);
} else {
// Package not in Stringpool for some reason, write full string instead
- Slog.w(TAG, "UsageStats package name (" + usageStats.mPackageName
- + ") not found in IntervalStats string cache");
proto.write(IntervalStatsProto.UsageStats.PACKAGE, usageStats.mPackageName);
}
// Time attributes stored as an offset of the beginTime.
@@ -347,12 +354,16 @@ final class UsageStatsProto {
proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS,
usageStats.mTotalTimeVisible);
proto.write(IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT, usageStats.mAppLaunchCount);
- writeChooserCounts(proto, usageStats);
+ try {
+ writeChooserCounts(proto, usageStats);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Unable to write chooser counts for " + usageStats.mPackageName, e);
+ }
proto.end(token);
}
private static void writeCountAndTime(ProtoOutputStream proto, long fieldId, int count,
- long time) throws IOException {
+ long time) throws IllegalArgumentException {
final long token = proto.start(fieldId);
proto.write(IntervalStatsProto.CountAndTime.COUNT, count);
proto.write(IntervalStatsProto.CountAndTime.TIME_MS, time);
@@ -361,7 +372,7 @@ final class UsageStatsProto {
private static void writeChooserCounts(ProtoOutputStream proto, final UsageStats usageStats)
- throws IOException {
+ throws IllegalArgumentException {
if (usageStats == null || usageStats.mChooserCounts == null
|| usageStats.mChooserCounts.keySet().isEmpty()) {
return;
@@ -381,7 +392,7 @@ final class UsageStatsProto {
}
private static void writeCountsForAction(ProtoOutputStream proto,
- ArrayMap<String, Integer> counts) throws IOException {
+ ArrayMap<String, Integer> counts) throws IllegalArgumentException {
final int countsSize = counts.size();
for (int i = 0; i < countsSize; i++) {
String key = counts.keyAt(i);
@@ -397,7 +408,7 @@ final class UsageStatsProto {
private static void writeConfigStats(ProtoOutputStream proto, long fieldId,
final IntervalStats stats, final ConfigurationStats configStats, boolean isActive)
- throws IOException {
+ throws IllegalArgumentException {
final long token = proto.start(fieldId);
configStats.mConfiguration.writeToProto(proto, IntervalStatsProto.Configuration.CONFIG);
proto.write(IntervalStatsProto.Configuration.LAST_TIME_ACTIVE_MS,
@@ -407,19 +418,16 @@ final class UsageStatsProto {
proto.write(IntervalStatsProto.Configuration.COUNT, configStats.mActivationCount);
proto.write(IntervalStatsProto.Configuration.ACTIVE, isActive);
proto.end(token);
-
}
private static void writeEvent(ProtoOutputStream proto, long fieldId, final IntervalStats stats,
- final UsageEvents.Event event) throws IOException {
+ final UsageEvents.Event event) throws IllegalArgumentException {
final long token = proto.start(fieldId);
final int packageIndex = stats.mStringCache.indexOf(event.mPackage);
if (packageIndex >= 0) {
proto.write(IntervalStatsProto.Event.PACKAGE_INDEX, packageIndex + 1);
} else {
// Package not in Stringpool for some reason, write full string instead
- Slog.w(TAG, "Usage event package name (" + event.mPackage
- + ") not found in IntervalStats string cache");
proto.write(IntervalStatsProto.Event.PACKAGE, event.mPackage);
}
if (event.mClass != null) {
@@ -428,8 +436,6 @@ final class UsageStatsProto {
proto.write(IntervalStatsProto.Event.CLASS_INDEX, classIndex + 1);
} else {
// Class not in Stringpool for some reason, write full string instead
- Slog.w(TAG, "Usage event class name (" + event.mClass
- + ") not found in IntervalStats string cache");
proto.write(IntervalStatsProto.Event.CLASS, event.mClass);
}
}
@@ -442,10 +448,6 @@ final class UsageStatsProto {
if (taskRootPackageIndex >= 0) {
proto.write(IntervalStatsProto.Event.TASK_ROOT_PACKAGE_INDEX,
taskRootPackageIndex + 1);
- } else {
- // Task root package not in Stringpool for some reason.
- Slog.w(TAG, "Usage event task root package name (" + event.mTaskRootPackage
- + ") not found in IntervalStats string cache");
}
}
if (event.mTaskRootClass != null) {
@@ -453,10 +455,6 @@ final class UsageStatsProto {
if (taskRootClassIndex >= 0) {
proto.write(IntervalStatsProto.Event.TASK_ROOT_CLASS_INDEX,
taskRootClassIndex + 1);
- } else {
- // Task root class not in Stringpool for some reason.
- Slog.w(TAG, "Usage event task root class name (" + event.mTaskRootClass
- + ") not found in IntervalStats string cache");
}
}
switch (event.mEventType) {
@@ -484,9 +482,6 @@ final class UsageStatsProto {
channelIndex + 1);
} else {
// Channel not in Stringpool for some reason, write full string instead
- Slog.w(TAG, "Usage event notification channel name ("
- + event.mNotificationChannelId
- + ") not found in IntervalStats string cache");
proto.write(IntervalStatsProto.Event.NOTIFICATION_CHANNEL,
event.mNotificationChannelId);
}
@@ -542,17 +537,33 @@ final class UsageStatsProto {
statsOut.keyguardHiddenTracker);
break;
case (int) IntervalStatsProto.STRINGPOOL:
- stringPool = readStringPool(proto);
- statsOut.mStringCache.addAll(stringPool);
+ try {
+ stringPool = readStringPool(proto);
+ statsOut.mStringCache.addAll(stringPool);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to read string pool from proto.", e);
+ }
break;
case (int) IntervalStatsProto.PACKAGES:
- loadUsageStats(proto, IntervalStatsProto.PACKAGES, statsOut, stringPool);
+ try {
+ loadUsageStats(proto, IntervalStatsProto.PACKAGES, statsOut, stringPool);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to read some usage stats from proto.", e);
+ }
break;
case (int) IntervalStatsProto.CONFIGURATIONS:
- loadConfigStats(proto, IntervalStatsProto.CONFIGURATIONS, statsOut);
+ try {
+ loadConfigStats(proto, IntervalStatsProto.CONFIGURATIONS, statsOut);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to read some configuration stats from proto.", e);
+ }
break;
case (int) IntervalStatsProto.EVENT_LOG:
- loadEvent(proto, IntervalStatsProto.EVENT_LOG, statsOut, stringPool);
+ try {
+ loadEvent(proto, IntervalStatsProto.EVENT_LOG, statsOut, stringPool);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to read some events from proto.", e);
+ }
break;
case ProtoInputStream.NO_MORE_FIELDS:
if (statsOut.endTime == 0) {
@@ -571,72 +582,60 @@ final class UsageStatsProto {
* @param proto The serializer to which to write the packageStats data.
* @param stats The stats object to write to the XML file.
*/
- public static void write(OutputStream out, IntervalStats stats) throws IOException {
+ public static void write(OutputStream out, IntervalStats stats)
+ throws IOException, IllegalArgumentException {
final ProtoOutputStream proto = new ProtoOutputStream(out);
proto.write(IntervalStatsProto.END_TIME_MS, stats.endTime - stats.beginTime);
proto.write(IntervalStatsProto.MAJOR_VERSION, stats.majorVersion);
proto.write(IntervalStatsProto.MINOR_VERSION, stats.minorVersion);
// String pool should be written before the rest of the usage stats
- writeStringPool(proto, stats);
+ try {
+ writeStringPool(proto, stats);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Unable to write string pool to proto.", e);
+ }
- writeCountAndTime(proto, IntervalStatsProto.INTERACTIVE, stats.interactiveTracker.count,
- stats.interactiveTracker.duration);
- writeCountAndTime(proto, IntervalStatsProto.NON_INTERACTIVE,
- stats.nonInteractiveTracker.count, stats.nonInteractiveTracker.duration);
- writeCountAndTime(proto, IntervalStatsProto.KEYGUARD_SHOWN,
- stats.keyguardShownTracker.count, stats.keyguardShownTracker.duration);
- writeCountAndTime(proto, IntervalStatsProto.KEYGUARD_HIDDEN,
- stats.keyguardHiddenTracker.count, stats.keyguardHiddenTracker.duration);
+ try {
+ writeCountAndTime(proto, IntervalStatsProto.INTERACTIVE, stats.interactiveTracker.count,
+ stats.interactiveTracker.duration);
+ writeCountAndTime(proto, IntervalStatsProto.NON_INTERACTIVE,
+ stats.nonInteractiveTracker.count, stats.nonInteractiveTracker.duration);
+ writeCountAndTime(proto, IntervalStatsProto.KEYGUARD_SHOWN,
+ stats.keyguardShownTracker.count, stats.keyguardShownTracker.duration);
+ writeCountAndTime(proto, IntervalStatsProto.KEYGUARD_HIDDEN,
+ stats.keyguardHiddenTracker.count, stats.keyguardHiddenTracker.duration);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Unable to write some interval stats trackers to proto.", e);
+ }
final int statsCount = stats.packageStats.size();
for (int i = 0; i < statsCount; i++) {
- writeUsageStats(proto, IntervalStatsProto.PACKAGES, stats,
- stats.packageStats.valueAt(i));
+ try {
+ writeUsageStats(proto, IntervalStatsProto.PACKAGES, stats,
+ stats.packageStats.valueAt(i));
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Unable to write some usage stats to proto.", e);
+ }
}
final int configCount = stats.configurations.size();
for (int i = 0; i < configCount; i++) {
boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i));
- writeConfigStats(proto, IntervalStatsProto.CONFIGURATIONS, stats,
- stats.configurations.valueAt(i), active);
+ try {
+ writeConfigStats(proto, IntervalStatsProto.CONFIGURATIONS, stats,
+ stats.configurations.valueAt(i), active);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Unable to write some configuration stats to proto.", e);
+ }
}
final int eventCount = stats.events.size();
for (int i = 0; i < eventCount; i++) {
- writeEvent(proto, IntervalStatsProto.EVENT_LOG, stats, stats.events.get(i));
- }
-
- proto.flush();
- }
-
- // TODO: move to UsageStatsProtoV2
- static void readPendingEvents(InputStream in, List<UsageEvents.Event> events)
- throws IOException {
- final ProtoInputStream proto = new ProtoInputStream(in);
- final List<String> stringPool = new ArrayList<>();
- final IntervalStats tmpStatsObj = new IntervalStats();
- while (true) {
- switch (proto.nextField()) {
- case (int) IntervalStatsProto.PENDING_EVENTS:
- loadEvent(proto, IntervalStatsProto.PENDING_EVENTS, tmpStatsObj, stringPool);
- break;
- case ProtoInputStream.NO_MORE_FIELDS:
- final int eventCount = tmpStatsObj.events.size();
- for (int i = 0; i < eventCount; i++) {
- events.add(tmpStatsObj.events.get(i));
- }
- return;
+ try {
+ writeEvent(proto, IntervalStatsProto.EVENT_LOG, stats, stats.events.get(i));
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Unable to write some events to proto.", e);
}
}
- }
- // TODO: move to UsageStatsProtoV2
- static void writePendingEvents(OutputStream out, List<UsageEvents.Event> events)
- throws IOException {
- final ProtoOutputStream proto = new ProtoOutputStream(out);
- final IntervalStats tmpStatsObj = new IntervalStats();
- final int eventCount = events.size();
- for (int i = 0; i < eventCount; i++) {
- writeEvent(proto, IntervalStatsProto.PENDING_EVENTS, tmpStatsObj, events.get(i));
- }
proto.flush();
}
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
new file mode 100644
index 000000000000..7d8e430e5416
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
@@ -0,0 +1,788 @@
+/*
+ * 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.usage;
+
+import android.app.usage.ConfigurationStats;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStats;
+import android.content.res.Configuration;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+/**
+ * UsageStats reader/writer V2 for Protocol Buffer format.
+ */
+final class UsageStatsProtoV2 {
+ private static final String TAG = "UsageStatsProtoV2";
+
+ // Static-only utility class.
+ private UsageStatsProtoV2() {}
+
+ private static UsageStats parseUsageStats(ProtoInputStream proto, final long beginTime)
+ throws IOException {
+ UsageStats stats = new UsageStats();
+ // Time attributes stored is an offset of the beginTime.
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) UsageStatsObfuscatedProto.PACKAGE_TOKEN:
+ stats.mPackageToken = proto.readInt(
+ UsageStatsObfuscatedProto.PACKAGE_TOKEN) - 1;
+ break;
+ case (int) UsageStatsObfuscatedProto.LAST_TIME_ACTIVE_MS:
+ stats.mLastTimeUsed = beginTime + proto.readLong(
+ UsageStatsObfuscatedProto.LAST_TIME_ACTIVE_MS);
+ break;
+ case (int) UsageStatsObfuscatedProto.TOTAL_TIME_ACTIVE_MS:
+ stats.mTotalTimeInForeground = proto.readLong(
+ UsageStatsObfuscatedProto.TOTAL_TIME_ACTIVE_MS);
+ break;
+ case (int) UsageStatsObfuscatedProto.APP_LAUNCH_COUNT:
+ stats.mAppLaunchCount = proto.readInt(
+ UsageStatsObfuscatedProto.APP_LAUNCH_COUNT);
+ break;
+ case (int) UsageStatsObfuscatedProto.CHOOSER_ACTIONS:
+ try {
+ final long token = proto.start(UsageStatsObfuscatedProto.CHOOSER_ACTIONS);
+ loadChooserCounts(proto, stats);
+ proto.end(token);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to read chooser counts for " + stats.mPackageToken);
+ }
+ break;
+ case (int) UsageStatsObfuscatedProto.LAST_TIME_SERVICE_USED_MS:
+ stats.mLastTimeForegroundServiceUsed = beginTime + proto.readLong(
+ UsageStatsObfuscatedProto.LAST_TIME_SERVICE_USED_MS);
+ break;
+ case (int) UsageStatsObfuscatedProto.TOTAL_TIME_SERVICE_USED_MS:
+ stats.mTotalTimeForegroundServiceUsed = proto.readLong(
+ UsageStatsObfuscatedProto.TOTAL_TIME_SERVICE_USED_MS);
+ break;
+ case (int) UsageStatsObfuscatedProto.LAST_TIME_VISIBLE_MS:
+ stats.mLastTimeVisible = beginTime + proto.readLong(
+ UsageStatsObfuscatedProto.LAST_TIME_VISIBLE_MS);
+ break;
+ case (int) UsageStatsObfuscatedProto.TOTAL_TIME_VISIBLE_MS:
+ stats.mTotalTimeVisible = proto.readLong(
+ UsageStatsObfuscatedProto.TOTAL_TIME_VISIBLE_MS);
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ // mLastTimeUsed was not read, assume default value of 0 plus beginTime
+ if (stats.mLastTimeUsed == 0) {
+ stats.mLastTimeUsed = beginTime;
+ }
+ return stats;
+ }
+ }
+ }
+
+ private static void loadCountAndTime(ProtoInputStream proto, long fieldId,
+ IntervalStats.EventTracker tracker) {
+ try {
+ final long token = proto.start(fieldId);
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) IntervalStatsObfuscatedProto.CountAndTime.COUNT:
+ tracker.count = proto.readInt(
+ IntervalStatsObfuscatedProto.CountAndTime.COUNT);
+ break;
+ case (int) IntervalStatsObfuscatedProto.CountAndTime.TIME_MS:
+ tracker.duration = proto.readLong(
+ IntervalStatsObfuscatedProto.CountAndTime.TIME_MS);
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ proto.end(token);
+ return;
+ }
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to read event tracker " + fieldId, e);
+ }
+ }
+
+ private static void loadChooserCounts(ProtoInputStream proto, UsageStats usageStats)
+ throws IOException {
+ int actionToken;
+ SparseIntArray counts;
+ if (proto.nextField(UsageStatsObfuscatedProto.ChooserAction.ACTION_TOKEN)) {
+ // Fast path; this should work for most cases since the action token is written first
+ actionToken = proto.readInt(UsageStatsObfuscatedProto.ChooserAction.ACTION_TOKEN) - 1;
+ counts = usageStats.mChooserCountsObfuscated.get(actionToken);
+ if (counts == null) {
+ counts = new SparseIntArray();
+ usageStats.mChooserCountsObfuscated.put(actionToken, counts);
+ }
+ } else {
+ counts = new SparseIntArray();
+ }
+
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) UsageStatsObfuscatedProto.ChooserAction.ACTION_TOKEN:
+ // Fast path failed for some reason, add the SparseIntArray object to usageStats
+ actionToken = proto.readInt(
+ UsageStatsObfuscatedProto.ChooserAction.ACTION_TOKEN) - 1;
+ usageStats.mChooserCountsObfuscated.put(actionToken, counts);
+ break;
+ case (int) UsageStatsObfuscatedProto.ChooserAction.COUNTS:
+ final long token = proto.start(UsageStatsObfuscatedProto.ChooserAction.COUNTS);
+ loadCountsForAction(proto, counts);
+ proto.end(token);
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ return; // if the action was never read, the loaded counts will be ignored.
+ }
+ }
+ }
+
+ private static void loadCountsForAction(ProtoInputStream proto, SparseIntArray counts)
+ throws IOException {
+ int categoryToken = PackagesTokenData.UNASSIGNED_TOKEN;
+ int count = 0;
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) UsageStatsObfuscatedProto.ChooserAction.CategoryCount.CATEGORY_TOKEN:
+ categoryToken = proto.readInt(
+ UsageStatsObfuscatedProto.ChooserAction.CategoryCount.CATEGORY_TOKEN)
+ - 1;
+ break;
+ case (int) UsageStatsObfuscatedProto.ChooserAction.CategoryCount.COUNT:
+ count = proto.readInt(
+ UsageStatsObfuscatedProto.ChooserAction.CategoryCount.COUNT);
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ if (categoryToken != PackagesTokenData.UNASSIGNED_TOKEN) {
+ counts.put(categoryToken, count);
+ }
+ return;
+ }
+ }
+ }
+
+ private static void loadConfigStats(ProtoInputStream proto, IntervalStats stats)
+ throws IOException {
+ boolean configActive = false;
+ final Configuration config = new Configuration();
+ ConfigurationStats configStats = new ConfigurationStats();
+ if (proto.nextField(IntervalStatsObfuscatedProto.Configuration.CONFIG)) {
+ // Fast path; this should work since the configuration is written first
+ config.readFromProto(proto, IntervalStatsObfuscatedProto.Configuration.CONFIG);
+ configStats = stats.getOrCreateConfigurationStats(config);
+ }
+
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) IntervalStatsObfuscatedProto.Configuration.CONFIG:
+ // Fast path failed from some reason, add ConfigStats object to statsOut now
+ config.readFromProto(proto, IntervalStatsObfuscatedProto.Configuration.CONFIG);
+ final ConfigurationStats temp = stats.getOrCreateConfigurationStats(config);
+ temp.mLastTimeActive = configStats.mLastTimeActive;
+ temp.mTotalTimeActive = configStats.mTotalTimeActive;
+ temp.mActivationCount = configStats.mActivationCount;
+ configStats = temp;
+ break;
+ case (int) IntervalStatsObfuscatedProto.Configuration.LAST_TIME_ACTIVE_MS:
+ configStats.mLastTimeActive = stats.beginTime + proto.readLong(
+ IntervalStatsObfuscatedProto.Configuration.LAST_TIME_ACTIVE_MS);
+ break;
+ case (int) IntervalStatsObfuscatedProto.Configuration.TOTAL_TIME_ACTIVE_MS:
+ configStats.mTotalTimeActive = proto.readLong(
+ IntervalStatsObfuscatedProto.Configuration.TOTAL_TIME_ACTIVE_MS);
+ break;
+ case (int) IntervalStatsObfuscatedProto.Configuration.COUNT:
+ configStats.mActivationCount = proto.readInt(
+ IntervalStatsObfuscatedProto.Configuration.COUNT);
+ break;
+ case (int) IntervalStatsObfuscatedProto.Configuration.ACTIVE:
+ configActive = proto.readBoolean(
+ IntervalStatsObfuscatedProto.Configuration.ACTIVE);
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ // mLastTimeActive was not assigned, assume default value of 0 plus beginTime
+ if (configStats.mLastTimeActive == 0) {
+ configStats.mLastTimeActive = stats.beginTime;
+ }
+ if (configActive) {
+ stats.activeConfiguration = configStats.mConfiguration;
+ }
+ return;
+ }
+ }
+ }
+
+ private static UsageEvents.Event parseEvent(ProtoInputStream proto, long beginTime)
+ throws IOException {
+ final UsageEvents.Event event = new UsageEvents.Event();
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) EventObfuscatedProto.PACKAGE_TOKEN:
+ event.mPackageToken = proto.readInt(EventObfuscatedProto.PACKAGE_TOKEN) - 1;
+ break;
+ case (int) EventObfuscatedProto.CLASS_TOKEN:
+ event.mClassToken = proto.readInt(EventObfuscatedProto.CLASS_TOKEN) - 1;
+ break;
+ case (int) EventObfuscatedProto.TIME_MS:
+ event.mTimeStamp = beginTime + proto.readLong(EventObfuscatedProto.TIME_MS);
+ break;
+ case (int) EventObfuscatedProto.FLAGS:
+ event.mFlags = proto.readInt(EventObfuscatedProto.FLAGS);
+ break;
+ case (int) EventObfuscatedProto.TYPE:
+ event.mEventType = proto.readInt(EventObfuscatedProto.TYPE);
+ break;
+ case (int) EventObfuscatedProto.CONFIG:
+ event.mConfiguration = new Configuration();
+ event.mConfiguration.readFromProto(proto, EventObfuscatedProto.CONFIG);
+ break;
+ case (int) EventObfuscatedProto.SHORTCUT_ID_TOKEN:
+ event.mShortcutIdToken = proto.readInt(
+ EventObfuscatedProto.SHORTCUT_ID_TOKEN) - 1;
+ break;
+ case (int) EventObfuscatedProto.STANDBY_BUCKET:
+ event.mBucketAndReason = proto.readInt(EventObfuscatedProto.STANDBY_BUCKET);
+ break;
+ case (int) EventObfuscatedProto.NOTIFICATION_CHANNEL_ID_TOKEN:
+ event.mNotificationChannelIdToken = proto.readInt(
+ EventObfuscatedProto.NOTIFICATION_CHANNEL_ID_TOKEN) - 1;
+ break;
+ case (int) EventObfuscatedProto.INSTANCE_ID:
+ event.mInstanceId = proto.readInt(EventObfuscatedProto.INSTANCE_ID);
+ break;
+ case (int) EventObfuscatedProto.TASK_ROOT_PACKAGE_TOKEN:
+ event.mTaskRootPackageToken = proto.readInt(
+ EventObfuscatedProto.TASK_ROOT_PACKAGE_TOKEN) - 1;
+ break;
+ case (int) EventObfuscatedProto.TASK_ROOT_CLASS_TOKEN:
+ event.mTaskRootClassToken = proto.readInt(
+ EventObfuscatedProto.TASK_ROOT_CLASS_TOKEN) - 1;
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ // timeStamp was not read, assume default value 0 plus beginTime
+ if (event.mTimeStamp == 0) {
+ event.mTimeStamp = beginTime;
+ }
+ return event.mPackageToken == PackagesTokenData.UNASSIGNED_TOKEN ? null : event;
+ }
+ }
+ }
+
+ private static void writeUsageStats(ProtoOutputStream proto, final long beginTime,
+ final UsageStats stats) throws IllegalArgumentException {
+ // Time attributes stored as an offset of the beginTime.
+ proto.write(UsageStatsObfuscatedProto.PACKAGE_TOKEN, stats.mPackageToken + 1);
+ proto.write(UsageStatsObfuscatedProto.LAST_TIME_ACTIVE_MS, stats.mLastTimeUsed - beginTime);
+ proto.write(UsageStatsObfuscatedProto.TOTAL_TIME_ACTIVE_MS, stats.mTotalTimeInForeground);
+ proto.write(UsageStatsObfuscatedProto.LAST_TIME_SERVICE_USED_MS,
+ stats.mLastTimeForegroundServiceUsed - beginTime);
+ proto.write(UsageStatsObfuscatedProto.TOTAL_TIME_SERVICE_USED_MS,
+ stats.mTotalTimeForegroundServiceUsed);
+ proto.write(UsageStatsObfuscatedProto.LAST_TIME_VISIBLE_MS,
+ stats.mLastTimeVisible - beginTime);
+ proto.write(UsageStatsObfuscatedProto.TOTAL_TIME_VISIBLE_MS, stats.mTotalTimeVisible);
+ proto.write(UsageStatsObfuscatedProto.APP_LAUNCH_COUNT, stats.mAppLaunchCount);
+ try {
+ writeChooserCounts(proto, stats);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Unable to write chooser counts for " + stats.mPackageName, e);
+ }
+ }
+
+ private static void writeCountAndTime(ProtoOutputStream proto, long fieldId, int count,
+ long time) throws IllegalArgumentException {
+ final long token = proto.start(fieldId);
+ proto.write(IntervalStatsObfuscatedProto.CountAndTime.COUNT, count);
+ proto.write(IntervalStatsObfuscatedProto.CountAndTime.TIME_MS, time);
+ proto.end(token);
+ }
+
+ private static void writeChooserCounts(ProtoOutputStream proto, final UsageStats stats)
+ throws IllegalArgumentException {
+ if (stats == null || stats.mChooserCountsObfuscated.size() == 0) {
+ return;
+ }
+ final int chooserCountSize = stats.mChooserCountsObfuscated.size();
+ for (int i = 0; i < chooserCountSize; i++) {
+ final int action = stats.mChooserCountsObfuscated.keyAt(i);
+ final SparseIntArray counts = stats.mChooserCountsObfuscated.valueAt(i);
+ if (counts == null || counts.size() == 0) {
+ continue;
+ }
+ final long token = proto.start(UsageStatsObfuscatedProto.CHOOSER_ACTIONS);
+ proto.write(UsageStatsObfuscatedProto.ChooserAction.ACTION_TOKEN, action + 1);
+ writeCountsForAction(proto, counts);
+ proto.end(token);
+ }
+ }
+
+ private static void writeCountsForAction(ProtoOutputStream proto, SparseIntArray counts)
+ throws IllegalArgumentException {
+ final int countsSize = counts.size();
+ for (int i = 0; i < countsSize; i++) {
+ final int category = counts.keyAt(i);
+ final int count = counts.valueAt(i);
+ if (count <= 0) {
+ continue;
+ }
+ final long token = proto.start(UsageStatsObfuscatedProto.ChooserAction.COUNTS);
+ proto.write(UsageStatsObfuscatedProto.ChooserAction.CategoryCount.CATEGORY_TOKEN,
+ category + 1);
+ proto.write(UsageStatsObfuscatedProto.ChooserAction.CategoryCount.COUNT, count);
+ proto.end(token);
+ }
+ }
+
+ private static void writeConfigStats(ProtoOutputStream proto, final long statsBeginTime,
+ final ConfigurationStats configStats, boolean isActive)
+ throws IllegalArgumentException {
+ configStats.mConfiguration.writeToProto(proto,
+ IntervalStatsObfuscatedProto.Configuration.CONFIG);
+ proto.write(IntervalStatsObfuscatedProto.Configuration.LAST_TIME_ACTIVE_MS,
+ configStats.mLastTimeActive - statsBeginTime);
+ proto.write(IntervalStatsObfuscatedProto.Configuration.TOTAL_TIME_ACTIVE_MS,
+ configStats.mTotalTimeActive);
+ proto.write(IntervalStatsObfuscatedProto.Configuration.COUNT, configStats.mActivationCount);
+ proto.write(IntervalStatsObfuscatedProto.Configuration.ACTIVE, isActive);
+ }
+
+ private static void writeEvent(ProtoOutputStream proto, final long statsBeginTime,
+ final UsageEvents.Event event) throws IllegalArgumentException {
+ proto.write(EventObfuscatedProto.PACKAGE_TOKEN, event.mPackageToken + 1);
+ if (event.mClassToken != PackagesTokenData.UNASSIGNED_TOKEN) {
+ proto.write(EventObfuscatedProto.CLASS_TOKEN, event.mClassToken + 1);
+ }
+ proto.write(EventObfuscatedProto.TIME_MS, event.mTimeStamp - statsBeginTime);
+ proto.write(EventObfuscatedProto.FLAGS, event.mFlags);
+ proto.write(EventObfuscatedProto.TYPE, event.mEventType);
+ proto.write(EventObfuscatedProto.INSTANCE_ID, event.mInstanceId);
+ if (event.mTaskRootPackageToken != PackagesTokenData.UNASSIGNED_TOKEN) {
+ proto.write(EventObfuscatedProto.TASK_ROOT_PACKAGE_TOKEN,
+ event.mTaskRootPackageToken + 1);
+ }
+ if (event.mTaskRootClassToken != PackagesTokenData.UNASSIGNED_TOKEN) {
+ proto.write(EventObfuscatedProto.TASK_ROOT_CLASS_TOKEN, event.mTaskRootClassToken + 1);
+ }
+ switch (event.mEventType) {
+ case UsageEvents.Event.CONFIGURATION_CHANGE:
+ if (event.mConfiguration != null) {
+ event.mConfiguration.writeToProto(proto, EventObfuscatedProto.CONFIG);
+ }
+ break;
+ case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+ if (event.mBucketAndReason != 0) {
+ proto.write(EventObfuscatedProto.STANDBY_BUCKET, event.mBucketAndReason);
+ }
+ break;
+ case UsageEvents.Event.SHORTCUT_INVOCATION:
+ if (event.mShortcutIdToken != PackagesTokenData.UNASSIGNED_TOKEN) {
+ proto.write(EventObfuscatedProto.SHORTCUT_ID_TOKEN, event.mShortcutIdToken + 1);
+ }
+ break;
+ case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+ if (event.mNotificationChannelIdToken != PackagesTokenData.UNASSIGNED_TOKEN) {
+ proto.write(EventObfuscatedProto.NOTIFICATION_CHANNEL_ID_TOKEN,
+ event.mNotificationChannelIdToken + 1);
+ }
+ break;
+ }
+ }
+
+ /**
+ * Populates a tokenized version of interval stats from the input stream given.
+ *
+ * @param in the input stream from which to read events.
+ * @param stats the interval stats object which will be populated.
+ */
+ public static void read(InputStream in, IntervalStats stats) throws IOException {
+ final ProtoInputStream proto = new ProtoInputStream(in);
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) IntervalStatsObfuscatedProto.END_TIME_MS:
+ stats.endTime = stats.beginTime + proto.readLong(
+ IntervalStatsObfuscatedProto.END_TIME_MS);
+ break;
+ case (int) IntervalStatsObfuscatedProto.MAJOR_VERSION:
+ stats.majorVersion = proto.readInt(IntervalStatsObfuscatedProto.MAJOR_VERSION);
+ break;
+ case (int) IntervalStatsObfuscatedProto.MINOR_VERSION:
+ stats.minorVersion = proto.readInt(IntervalStatsObfuscatedProto.MINOR_VERSION);
+ break;
+ case (int) IntervalStatsObfuscatedProto.INTERACTIVE:
+ loadCountAndTime(proto, IntervalStatsObfuscatedProto.INTERACTIVE,
+ stats.interactiveTracker);
+ break;
+ case (int) IntervalStatsObfuscatedProto.NON_INTERACTIVE:
+ loadCountAndTime(proto, IntervalStatsObfuscatedProto.NON_INTERACTIVE,
+ stats.nonInteractiveTracker);
+ break;
+ case (int) IntervalStatsObfuscatedProto.KEYGUARD_SHOWN:
+ loadCountAndTime(proto, IntervalStatsObfuscatedProto.KEYGUARD_SHOWN,
+ stats.keyguardShownTracker);
+ break;
+ case (int) IntervalStatsObfuscatedProto.KEYGUARD_HIDDEN:
+ loadCountAndTime(proto, IntervalStatsObfuscatedProto.KEYGUARD_HIDDEN,
+ stats.keyguardHiddenTracker);
+ break;
+ case (int) IntervalStatsObfuscatedProto.PACKAGES:
+ try {
+ final long packagesToken = proto.start(
+ IntervalStatsObfuscatedProto.PACKAGES);
+ UsageStats usageStats = parseUsageStats(proto, stats.beginTime);
+ if (usageStats.mPackageToken != PackagesTokenData.UNASSIGNED_TOKEN) {
+ stats.packageStatsObfuscated.put(usageStats.mPackageToken, usageStats);
+ }
+ proto.end(packagesToken);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to read some usage stats from proto.", e);
+ }
+ break;
+ case (int) IntervalStatsObfuscatedProto.CONFIGURATIONS:
+ try {
+ final long configsToken = proto.start(
+ IntervalStatsObfuscatedProto.CONFIGURATIONS);
+ loadConfigStats(proto, stats);
+ proto.end(configsToken);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to read some configuration stats from proto.", e);
+ }
+ break;
+ case (int) IntervalStatsObfuscatedProto.EVENT_LOG:
+ try {
+ final long eventsToken = proto.start(
+ IntervalStatsObfuscatedProto.EVENT_LOG);
+ UsageEvents.Event event = parseEvent(proto, stats.beginTime);
+ proto.end(eventsToken);
+ if (event != null) {
+ stats.events.insert(event);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to read some events from proto.", e);
+ }
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ // endTime not assigned, assume default value of 0 plus beginTime
+ if (stats.endTime == 0) {
+ stats.endTime = stats.beginTime;
+ }
+ return;
+ }
+ }
+ }
+
+ /**
+ * Writes the tokenized interval stats object to a ProtoBuf file.
+ *
+ * @param out the output stream to which to write the interval stats data.
+ * @param stats the interval stats object to write to the proto file.
+ */
+ public static void write(OutputStream out, IntervalStats stats)
+ throws IOException, IllegalArgumentException {
+ final ProtoOutputStream proto = new ProtoOutputStream(out);
+ proto.write(IntervalStatsObfuscatedProto.END_TIME_MS, stats.endTime - stats.beginTime);
+ proto.write(IntervalStatsObfuscatedProto.MAJOR_VERSION, stats.majorVersion);
+ proto.write(IntervalStatsObfuscatedProto.MINOR_VERSION, stats.minorVersion);
+
+ try {
+ writeCountAndTime(proto, IntervalStatsObfuscatedProto.INTERACTIVE,
+ stats.interactiveTracker.count, stats.interactiveTracker.duration);
+ writeCountAndTime(proto, IntervalStatsObfuscatedProto.NON_INTERACTIVE,
+ stats.nonInteractiveTracker.count, stats.nonInteractiveTracker.duration);
+ writeCountAndTime(proto, IntervalStatsObfuscatedProto.KEYGUARD_SHOWN,
+ stats.keyguardShownTracker.count, stats.keyguardShownTracker.duration);
+ writeCountAndTime(proto, IntervalStatsObfuscatedProto.KEYGUARD_HIDDEN,
+ stats.keyguardHiddenTracker.count, stats.keyguardHiddenTracker.duration);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Unable to write some interval stats trackers to proto.", e);
+ }
+
+ final int statsCount = stats.packageStatsObfuscated.size();
+ for (int i = 0; i < statsCount; i++) {
+ try {
+ final long token = proto.start(IntervalStatsObfuscatedProto.PACKAGES);
+ writeUsageStats(proto, stats.beginTime, stats.packageStatsObfuscated.valueAt(i));
+ proto.end(token);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Unable to write some usage stats to proto.", e);
+ }
+ }
+ final int configCount = stats.configurations.size();
+ for (int i = 0; i < configCount; i++) {
+ boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i));
+ try {
+ final long token = proto.start(IntervalStatsObfuscatedProto.CONFIGURATIONS);
+ writeConfigStats(proto, stats.beginTime, stats.configurations.valueAt(i), active);
+ proto.end(token);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Unable to write some configuration stats to proto.", e);
+ }
+ }
+ final int eventCount = stats.events.size();
+ for (int i = 0; i < eventCount; i++) {
+ try {
+ final long token = proto.start(IntervalStatsObfuscatedProto.EVENT_LOG);
+ writeEvent(proto, stats.beginTime, stats.events.get(i));
+ proto.end(token);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Unable to write some events to proto.", e);
+ }
+ }
+
+ proto.flush();
+ }
+
+ /***** Read/Write obfuscated packages data logic. *****/
+
+ private static void loadPackagesMap(ProtoInputStream proto,
+ SparseArray<ArrayList<String>> tokensToPackagesMap) throws IOException {
+ int key = PackagesTokenData.UNASSIGNED_TOKEN;
+ final ArrayList<String> strings = new ArrayList<>();
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) ObfuscatedPackagesProto.PackagesMap.PACKAGE_TOKEN:
+ key = proto.readInt(ObfuscatedPackagesProto.PackagesMap.PACKAGE_TOKEN) - 1;
+ break;
+ case (int) ObfuscatedPackagesProto.PackagesMap.STRINGS:
+ strings.add(proto.readString(ObfuscatedPackagesProto.PackagesMap.STRINGS));
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ if (key != PackagesTokenData.UNASSIGNED_TOKEN) {
+ tokensToPackagesMap.put(key, strings);
+ }
+ return;
+ }
+ }
+ }
+
+ /**
+ * Populates the package mappings from the input stream given.
+ *
+ * @param in the input stream from which to read the mappings.
+ * @param packagesTokenData the packages data object to which the data will be read to.
+ */
+ static void readObfuscatedData(InputStream in, PackagesTokenData packagesTokenData)
+ throws IOException {
+ final ProtoInputStream proto = new ProtoInputStream(in);
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) ObfuscatedPackagesProto.COUNTER:
+ packagesTokenData.counter = proto.readInt(ObfuscatedPackagesProto.COUNTER);
+ break;
+ case (int) ObfuscatedPackagesProto.PACKAGES_MAP:
+ final long token = proto.start(ObfuscatedPackagesProto.PACKAGES_MAP);
+ loadPackagesMap(proto, packagesTokenData.tokensToPackagesMap);
+ proto.end(token);
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ return;
+ }
+ }
+ }
+
+ /**
+ * Writes the packages mapping data to a ProtoBuf file.
+ *
+ * @param out the output stream to which to write the mappings.
+ * @param packagesTokenData the packages data object holding the data to write.
+ */
+ static void writeObfuscatedData(OutputStream out, PackagesTokenData packagesTokenData)
+ throws IOException, IllegalArgumentException {
+ final ProtoOutputStream proto = new ProtoOutputStream(out);
+ proto.write(ObfuscatedPackagesProto.COUNTER, packagesTokenData.counter);
+
+ final int mapSize = packagesTokenData.tokensToPackagesMap.size();
+ for (int i = 0; i < mapSize; i++) {
+ final long token = proto.start(ObfuscatedPackagesProto.PACKAGES_MAP);
+ int packageToken = packagesTokenData.tokensToPackagesMap.keyAt(i);
+ proto.write(ObfuscatedPackagesProto.PackagesMap.PACKAGE_TOKEN, packageToken + 1);
+
+ final ArrayList<String> strings = packagesTokenData.tokensToPackagesMap.valueAt(i);
+ final int listSize = strings.size();
+ for (int j = 0; j < listSize; j++) {
+ proto.write(ObfuscatedPackagesProto.PackagesMap.STRINGS, strings.get(j));
+ }
+ proto.end(token);
+ }
+
+ proto.flush();
+ }
+
+ /***** Read/Write pending events logic. *****/
+
+ private static UsageEvents.Event parsePendingEvent(ProtoInputStream proto) throws IOException {
+ final UsageEvents.Event event = new UsageEvents.Event();
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) PendingEventProto.PACKAGE_NAME:
+ event.mPackage = proto.readString(PendingEventProto.PACKAGE_NAME);
+ break;
+ case (int) PendingEventProto.CLASS_NAME:
+ event.mClass = proto.readString(PendingEventProto.CLASS_NAME);
+ break;
+ case (int) PendingEventProto.TIME_MS:
+ event.mTimeStamp = proto.readLong(PendingEventProto.TIME_MS);
+ break;
+ case (int) PendingEventProto.FLAGS:
+ event.mFlags = proto.readInt(PendingEventProto.FLAGS);
+ break;
+ case (int) PendingEventProto.TYPE:
+ event.mEventType = proto.readInt(PendingEventProto.TYPE);
+ break;
+ case (int) PendingEventProto.CONFIG:
+ event.mConfiguration = new Configuration();
+ event.mConfiguration.readFromProto(proto, PendingEventProto.CONFIG);
+ break;
+ case (int) PendingEventProto.SHORTCUT_ID:
+ event.mShortcutId = proto.readString(PendingEventProto.SHORTCUT_ID);
+ break;
+ case (int) PendingEventProto.STANDBY_BUCKET:
+ event.mBucketAndReason = proto.readInt(PendingEventProto.STANDBY_BUCKET);
+ break;
+ case (int) PendingEventProto.NOTIFICATION_CHANNEL_ID:
+ event.mNotificationChannelId = proto.readString(
+ PendingEventProto.NOTIFICATION_CHANNEL_ID);
+ break;
+ case (int) PendingEventProto.INSTANCE_ID:
+ event.mInstanceId = proto.readInt(PendingEventProto.INSTANCE_ID);
+ break;
+ case (int) PendingEventProto.TASK_ROOT_PACKAGE:
+ event.mTaskRootPackage = proto.readString(PendingEventProto.TASK_ROOT_PACKAGE);
+ break;
+ case (int) PendingEventProto.TASK_ROOT_CLASS:
+ event.mTaskRootClass = proto.readString(PendingEventProto.TASK_ROOT_CLASS);
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ // Handle default values for certain events types
+ switch (event.mEventType) {
+ case UsageEvents.Event.CONFIGURATION_CHANGE:
+ if (event.mConfiguration == null) {
+ event.mConfiguration = new Configuration();
+ }
+ break;
+ case UsageEvents.Event.SHORTCUT_INVOCATION:
+ if (event.mShortcutId == null) {
+ event.mShortcutId = "";
+ }
+ break;
+ case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+ if (event.mNotificationChannelId == null) {
+ event.mNotificationChannelId = "";
+ }
+ break;
+ }
+ return event.mPackage == null ? null : event;
+ }
+ }
+ }
+
+ /**
+ * Populates the list of pending events from the input stream given.
+ *
+ * @param in the input stream from which to read the pending events.
+ * @param events the list of pending events to populate.
+ */
+ static void readPendingEvents(InputStream in, LinkedList<UsageEvents.Event> events)
+ throws IOException {
+ final ProtoInputStream proto = new ProtoInputStream(in);
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) IntervalStatsObfuscatedProto.PENDING_EVENTS:
+ try {
+ final long token = proto.start(IntervalStatsObfuscatedProto.PENDING_EVENTS);
+ UsageEvents.Event event = parsePendingEvent(proto);
+ proto.end(token);
+ if (event != null) {
+ events.add(event);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to parse some pending events from proto.", e);
+ }
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ return;
+ }
+ }
+ }
+
+ private static void writePendingEvent(ProtoOutputStream proto, UsageEvents.Event event)
+ throws IllegalArgumentException {
+ proto.write(PendingEventProto.PACKAGE_NAME, event.mPackage);
+ if (event.mClass != null) {
+ proto.write(PendingEventProto.CLASS_NAME, event.mClass);
+ }
+ proto.write(PendingEventProto.TIME_MS, event.mTimeStamp);
+ proto.write(PendingEventProto.FLAGS, event.mFlags);
+ proto.write(PendingEventProto.TYPE, event.mEventType);
+ proto.write(PendingEventProto.INSTANCE_ID, event.mInstanceId);
+ if (event.mTaskRootPackage != null) {
+ proto.write(PendingEventProto.TASK_ROOT_PACKAGE, event.mTaskRootPackage);
+ }
+ if (event.mTaskRootClass != null) {
+ proto.write(PendingEventProto.TASK_ROOT_CLASS, event.mTaskRootClass);
+ }
+ switch (event.mEventType) {
+ case UsageEvents.Event.CONFIGURATION_CHANGE:
+ if (event.mConfiguration != null) {
+ event.mConfiguration.writeToProto(proto, PendingEventProto.CONFIG);
+ }
+ break;
+ case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+ if (event.mBucketAndReason != 0) {
+ proto.write(PendingEventProto.STANDBY_BUCKET, event.mBucketAndReason);
+ }
+ break;
+ case UsageEvents.Event.SHORTCUT_INVOCATION:
+ if (event.mShortcutId != null) {
+ proto.write(PendingEventProto.SHORTCUT_ID, event.mShortcutId);
+ }
+ break;
+ case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+ if (event.mNotificationChannelId != null) {
+ proto.write(PendingEventProto.NOTIFICATION_CHANNEL_ID,
+ event.mNotificationChannelId);
+ }
+ break;
+ }
+ }
+
+ /**
+ * Writes the pending events to a ProtoBuf file.
+ *
+ * @param out the output stream to which to write the pending events.
+ * @param events the list of pending events.
+ */
+ static void writePendingEvents(OutputStream out, LinkedList<UsageEvents.Event> events)
+ throws IOException, IllegalArgumentException {
+ final ProtoOutputStream proto = new ProtoOutputStream(out);
+ final int eventCount = events.size();
+ for (int i = 0; i < eventCount; i++) {
+ try {
+ final long token = proto.start(IntervalStatsObfuscatedProto.PENDING_EVENTS);
+ writePendingEvent(proto, events.get(i));
+ proto.end(token);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Unable to write some pending events to proto.", e);
+ }
+ }
+ proto.flush();
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 090623e2f767..5d03e151e993 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -141,6 +141,7 @@ public class UsageStatsService extends SystemService implements
static final int MSG_UID_STATE_CHANGED = 3;
static final int MSG_REPORT_EVENT_TO_ALL_USERID = 4;
static final int MSG_UNLOCKED_USER = 5;
+ static final int MSG_PACKAGE_REMOVED = 6;
private final Object mLock = new Object();
Handler mHandler;
@@ -148,7 +149,6 @@ public class UsageStatsService extends SystemService implements
UserManager mUserManager;
PackageManager mPackageManager;
PackageManagerInternal mPackageManagerInternal;
- PackageMonitor mPackageMonitor;
IDeviceIdleController mDeviceIdleController;
// Do not use directly. Call getDpmInternal() instead
DevicePolicyManagerInternal mDpmInternal;
@@ -159,11 +159,13 @@ public class UsageStatsService extends SystemService implements
int mUsageSource;
/** Manages the standby state of apps. */
- AppStandbyController mAppStandby;
+ AppStandbyInternal mAppStandby;
/** Manages app time limit observers */
AppTimeLimitController mAppTimeLimit;
+ private final PackageMonitor mPackageMonitor = new MyPackageMonitor();
+
// A map maintaining a queue of events to be reported per user.
private final SparseArray<LinkedList<Event>> mReportedEvents = new SparseArray<>();
final SparseArray<ArraySet<String>> mUsageReporters = new SparseArray();
@@ -189,11 +191,6 @@ public class UsageStatsService extends SystemService implements
event.mPackage = packageName;
reportEventOrAddToQueue(userId, event);
}
-
- @Override
- public void onParoleStateChanged(boolean isParoleOn) {
-
- }
};
public UsageStatsService(Context context) {
@@ -208,7 +205,9 @@ public class UsageStatsService extends SystemService implements
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mHandler = new H(BackgroundThread.get().getLooper());
- mAppStandby = new AppStandbyController(getContext(), BackgroundThread.get().getLooper());
+ mAppStandby = AppStandbyInternal.newAppStandbyController(
+ UsageStatsService.class.getClassLoader(), getContext(),
+ BackgroundThread.get().getLooper());
mAppTimeLimit = new AppTimeLimitController(
new AppTimeLimitController.TimeLimitCallbackListener() {
@@ -246,6 +245,8 @@ public class UsageStatsService extends SystemService implements
mAppStandby.addListener(mStandbyChangeListener);
+ mPackageMonitor.register(getContext(), null, UserHandle.ALL, true);
+
IntentFilter filter = new IntentFilter(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_USER_STARTED);
getContext().registerReceiverAsUser(new UserActionsReceiver(), UserHandle.ALL, filter,
@@ -340,6 +341,10 @@ public class UsageStatsService extends SystemService implements
mUserUnlockedStates.put(userId, true);
final UserUsageStatsService userService = getUserDataAndInitializeIfNeededLocked(
userId, System.currentTimeMillis());
+ if (userService == null) {
+ Slog.i(TAG, "Attempted to unlock stopped or removed user " + userId);
+ return;
+ }
userService.userUnlocked(System.currentTimeMillis());
// Process all the pending reported events
while (pendingEvents.peek() != null) {
@@ -456,7 +461,17 @@ public class UsageStatsService extends SystemService implements
"usagestats");
service = new UserUsageStatsService(getContext(), userId, usageStatsDir, this);
if (mUserUnlockedStates.get(userId)) {
- service.init(currentTimeMillis);
+ try {
+ service.init(currentTimeMillis);
+ } catch (Exception e) {
+ if (mUserManager.isUserUnlocked(userId)) {
+ throw e; // rethrow exception - user is unlocked
+ } else {
+ Slog.w(TAG, "Attempted to initialize service for "
+ + "stopped or removed user " + userId);
+ return null;
+ }
+ }
}
mUserState.put(userId, service);
}
@@ -606,7 +621,7 @@ public class UsageStatsService extends SystemService implements
final AtomicFile af = new AtomicFile(pendingEventsFiles[i]);
try {
try (FileInputStream in = af.openRead()) {
- UsageStatsProto.readPendingEvents(in, pendingEvents);
+ UsageStatsProtoV2.readPendingEvents(in, pendingEvents);
}
} catch (IOException e) {
// Even if one file read fails, exit here to keep all events in order on disk -
@@ -636,11 +651,11 @@ public class UsageStatsService extends SystemService implements
FileOutputStream fos = null;
try {
fos = af.startWrite();
- UsageStatsProto.writePendingEvents(fos, pendingEvents);
+ UsageStatsProtoV2.writePendingEvents(fos, pendingEvents);
af.finishWrite(fos);
fos = null;
pendingEvents.clear();
- } catch (IOException e) {
+ } catch (IOException | IllegalArgumentException e) {
Slog.e(TAG, "Failed to write " + pendingEventsFile.getAbsolutePath()
+ " for user " + userId);
} finally {
@@ -779,6 +794,9 @@ public class UsageStatsService extends SystemService implements
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, timeNow);
+ if (service == null) {
+ return; // user was stopped or removed
+ }
service.reportEvent(event);
mAppStandby.reportEvent(event, elapsedRealtime, userId);
@@ -829,6 +847,26 @@ public class UsageStatsService extends SystemService implements
}
/**
+ * Called by the Handler for message MSG_PACKAGE_REMOVED.
+ */
+ private void onPackageRemoved(int userId, String packageName) {
+ synchronized (mLock) {
+ final long timeRemoved = System.currentTimeMillis();
+ if (!mUserUnlockedStates.get(userId, false)) {
+ // If user is not unlocked and a package is removed for them, we will handle it
+ // when the user service is initialized and package manager is queried.
+ return;
+ }
+ final UserUsageStatsService userService = mUserState.get(userId);
+ if (userService == null) {
+ return;
+ }
+
+ userService.onPackageRemoved(packageName, timeRemoved);
+ }
+ }
+
+ /**
* Called by the Binder stub.
*/
List<UsageStats> queryUsageStats(int userId, int bucketType, long beginTime, long endTime,
@@ -841,6 +879,9 @@ public class UsageStatsService extends SystemService implements
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
+ if (service == null) {
+ return null; // user was stopped or removed
+ }
List<UsageStats> list = service.queryUsageStats(bucketType, beginTime, endTime);
if (list == null) {
return null;
@@ -873,6 +914,9 @@ public class UsageStatsService extends SystemService implements
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
+ if (service == null) {
+ return null; // user was stopped or removed
+ }
return service.queryConfigurationStats(bucketType, beginTime, endTime);
}
}
@@ -890,6 +934,9 @@ public class UsageStatsService extends SystemService implements
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
+ if (service == null) {
+ return null; // user was stopped or removed
+ }
return service.queryEventStats(bucketType, beginTime, endTime);
}
}
@@ -907,6 +954,9 @@ public class UsageStatsService extends SystemService implements
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
+ if (service == null) {
+ return null; // user was stopped or removed
+ }
return service.queryEvents(beginTime, endTime, shouldObfuscateInstantApps);
}
}
@@ -924,6 +974,9 @@ public class UsageStatsService extends SystemService implements
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
+ if (service == null) {
+ return null; // user was stopped or removed
+ }
return service.queryEventsForPackage(beginTime, endTime, packageName, includeTaskRoot);
}
}
@@ -945,7 +998,9 @@ public class UsageStatsService extends SystemService implements
continue;
}
UserUsageStatsService service = mUserState.get(userId);
- service.persistActiveStats();
+ if (service != null) {
+ service.persistActiveStats();
+ }
mAppStandby.flushToDisk(userId);
}
mAppStandby.flushDurationsToDisk();
@@ -976,7 +1031,7 @@ public class UsageStatsService extends SystemService implements
pw.println("Flushed stats to disk");
return;
} else if ("is-app-standby-enabled".equals(arg)) {
- pw.println(mAppStandby.mAppIdleEnabled);
+ pw.println(mAppStandby.isAppIdleEnabled());
return;
} else if ("apptimelimit".equals(arg)) {
if (i + 1 >= args.length) {
@@ -999,21 +1054,13 @@ public class UsageStatsService extends SystemService implements
ipw.decreaseIndent();
}
} else {
- final int user;
- try {
- user = Integer.valueOf(args[i + 1]);
- } catch (NumberFormatException nfe) {
- ipw.println("invalid user specified.");
- return;
+ final int user = parseUserIdFromArgs(args, i, ipw);
+ if (user != UserHandle.USER_NULL) {
+ final String[] remainingArgs = Arrays.copyOfRange(
+ args, i + 2, args.length);
+ // dump everything for the specified user
+ mUserState.get(user).dumpFile(ipw, remainingArgs);
}
- if (mUserState.indexOfKey(user) < 0) {
- ipw.println("the specified user does not exist.");
- return;
- }
- final String[] remainingArgs = Arrays.copyOfRange(
- args, i + 2, args.length);
- // dump everything for the specified user
- mUserState.get(user).dumpFile(ipw, remainingArgs);
}
return;
} else if ("database-info".equals(arg)) {
@@ -1028,19 +1075,11 @@ public class UsageStatsService extends SystemService implements
ipw.decreaseIndent();
}
} else {
- final int user;
- try {
- user = Integer.valueOf(args[i + 1]);
- } catch (NumberFormatException nfe) {
- ipw.println("invalid user specified.");
- return;
+ final int user = parseUserIdFromArgs(args, i, ipw);
+ if (user != UserHandle.USER_NULL) {
+ // dump info only for the specified user
+ mUserState.get(user).dumpDatabaseInfo(ipw);
}
- if (mUserState.indexOfKey(user) < 0) {
- ipw.println("the specified user does not exist.");
- return;
- }
- // dump info only for the specified user
- mUserState.get(user).dumpDatabaseInfo(ipw);
}
return;
} else if ("appstandby".equals(arg)) {
@@ -1048,15 +1087,18 @@ public class UsageStatsService extends SystemService implements
return;
} else if ("stats-directory".equals(arg)) {
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
- final int userId;
- try {
- userId = Integer.valueOf(args[i + 1]);
- } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
- ipw.println("invalid user specified.");
- return;
+ final int userId = parseUserIdFromArgs(args, i, ipw);
+ if (userId != UserHandle.USER_NULL) {
+ ipw.println(new File(Environment.getDataSystemCeDirectory(userId),
+ "usagestats").getAbsolutePath());
+ }
+ return;
+ } else if ("mappings".equals(arg)) {
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ final int userId = parseUserIdFromArgs(args, i, ipw);
+ if (userId != UserHandle.USER_NULL) {
+ mUserState.get(userId).dumpMappings(ipw);
}
- ipw.println(new File(Environment.getDataSystemCeDirectory(userId),
- "usagestats").getAbsolutePath());
return;
} else if (arg != null && !arg.startsWith("-")) {
// Anything else that doesn't start with '-' is a pkg to filter
@@ -1095,6 +1137,21 @@ public class UsageStatsService extends SystemService implements
}
}
+ private int parseUserIdFromArgs(String[] args, int index, IndentingPrintWriter ipw) {
+ final int userId;
+ try {
+ userId = Integer.valueOf(args[index + 1]);
+ } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
+ ipw.println("invalid user specified.");
+ return UserHandle.USER_NULL;
+ }
+ if (mUserState.indexOfKey(userId) < 0) {
+ ipw.println("the specified user does not exist.");
+ return UserHandle.USER_NULL;
+ }
+ return userId;
+ }
+
class H extends Handler {
public H(Looper looper) {
super(looper);
@@ -1113,12 +1170,22 @@ public class UsageStatsService extends SystemService implements
flushToDisk();
break;
case MSG_UNLOCKED_USER:
- onUserUnlocked(msg.arg1);
+ try {
+ onUserUnlocked(msg.arg1);
+ } catch (Exception e) {
+ if (mUserManager.isUserUnlocked(msg.arg1)) {
+ throw e; // rethrow exception - user is unlocked
+ } else {
+ Slog.w(TAG, "Attempted to unlock stopped or removed user " + msg.arg1);
+ }
+ }
break;
case MSG_REMOVE_USER:
onUserRemoved(msg.arg1);
break;
-
+ case MSG_PACKAGE_REMOVED:
+ onPackageRemoved(msg.arg1, (String) msg.obj);
+ break;
case MSG_UID_STATE_CHANGED: {
final int uid = msg.arg1;
final int procState = msg.arg2;
@@ -1380,7 +1447,7 @@ public class UsageStatsService extends SystemService implements
Binder.getCallingUid(), userId);
final long token = Binder.clearCallingIdentity();
try {
- return mAppStandby.isAppIdleFilteredOrParoled(
+ return mAppStandby.isAppIdleFiltered(
packageName, userId,
SystemClock.elapsedRealtime(), obfuscateInstantApps);
} finally {
@@ -1687,10 +1754,13 @@ public class UsageStatsService extends SystemService implements
public void registerAppUsageLimitObserver(int observerId, String[] packages,
long timeLimitMs, long timeUsedMs, PendingIntent callbackIntent,
String callingPackage) {
+ final int callingUid = Binder.getCallingUid();
+ final DevicePolicyManagerInternal dpmInternal = getDpmInternal();
if (!hasPermissions(callingPackage,
- Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE)) {
- throw new SecurityException("Caller doesn't have both SUSPEND_APPS and "
- + "OBSERVE_APP_USAGE permissions");
+ Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE)
+ && (dpmInternal != null && !dpmInternal.isActiveSupervisionApp(callingUid))) {
+ throw new SecurityException("Caller must be the active supervision app or "
+ + "it must have both SUSPEND_APPS and OBSERVE_APP_USAGE permissions");
}
if (packages == null || packages.length == 0) {
@@ -1699,7 +1769,6 @@ public class UsageStatsService extends SystemService implements
if (callbackIntent == null && timeUsedMs < timeLimitMs) {
throw new NullPointerException("callbackIntent can't be null");
}
- final int callingUid = Binder.getCallingUid();
final int userId = UserHandle.getUserId(callingUid);
final long token = Binder.clearCallingIdentity();
try {
@@ -1712,13 +1781,15 @@ public class UsageStatsService extends SystemService implements
@Override
public void unregisterAppUsageLimitObserver(int observerId, String callingPackage) {
+ final int callingUid = Binder.getCallingUid();
+ final DevicePolicyManagerInternal dpmInternal = getDpmInternal();
if (!hasPermissions(callingPackage,
- Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE)) {
- throw new SecurityException("Caller doesn't have both SUSPEND_APPS and "
- + "OBSERVE_APP_USAGE permissions");
+ Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE)
+ && (dpmInternal != null && !dpmInternal.isActiveSupervisionApp(callingUid))) {
+ throw new SecurityException("Caller must be the active supervision app or "
+ + "it must have both SUSPEND_APPS and OBSERVE_APP_USAGE permissions");
}
- final int callingUid = Binder.getCallingUid();
final int userId = UserHandle.getUserId(callingUid);
final long token = Binder.clearCallingIdentity();
try {
@@ -1945,11 +2016,6 @@ public class UsageStatsService extends SystemService implements
}
@Override
- public boolean isAppIdleParoleOn() {
- return mAppStandby.isParoledOrCharging();
- }
-
- @Override
public void prepareShutdown() {
// This method *WILL* do IO work, but we must block until it is finished or else
// we might not shutdown cleanly. This is ok to do with the 'am' lock held, because
@@ -1965,7 +2031,6 @@ public class UsageStatsService extends SystemService implements
@Override
public void addAppIdleStateChangeListener(AppIdleStateChangeListener listener) {
mAppStandby.addListener(listener);
- listener.onParoleStateChanged(isAppIdleParoleOn());
}
@Override
@@ -1986,6 +2051,9 @@ public class UsageStatsService extends SystemService implements
if (user == UserHandle.USER_SYSTEM) {
final UserUsageStatsService userStats = getUserDataAndInitializeIfNeededLocked(
user, System.currentTimeMillis());
+ if (userStats == null) {
+ return null; // user was stopped or removed
+ }
return userStats.getBackupPayload(key);
} else {
return null;
@@ -2004,6 +2072,9 @@ public class UsageStatsService extends SystemService implements
if (user == UserHandle.USER_SYSTEM) {
final UserUsageStatsService userStats = getUserDataAndInitializeIfNeededLocked(
user, System.currentTimeMillis());
+ if (userStats == null) {
+ return; // user was stopped or removed
+ }
userStats.applyRestoredPayload(key, payload);
}
}
@@ -2062,4 +2133,13 @@ public class UsageStatsService extends SystemService implements
return mAppTimeLimit.getAppUsageLimit(packageName, user);
}
}
+
+ private class MyPackageMonitor extends PackageMonitor {
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ mHandler.obtainMessage(MSG_PACKAGE_REMOVED, getChangingUserId(), 0, packageName)
+ .sendToTarget();
+ super.onPackageRemoved(packageName, uid);
+ }
+ }
}
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 1560b9e708e3..5783932db488 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -34,7 +34,10 @@ import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
import android.content.res.Configuration;
+import android.os.Process;
import android.os.SystemClock;
import android.text.format.DateUtils;
import android.util.ArrayMap;
@@ -44,6 +47,7 @@ import android.util.Slog;
import android.util.SparseIntArray;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocalServices;
import com.android.server.usage.UsageStatsDatabase.StatCombiner;
import java.io.File;
@@ -51,6 +55,7 @@ import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
/**
@@ -108,6 +113,7 @@ class UserUsageStatsService {
}
void init(final long currentTimeMillis) {
+ readPackageMappingsLocked();
mDatabase.init(currentTimeMillis);
int nullCount = 0;
@@ -140,17 +146,16 @@ class UserUsageStatsService {
}
// During system reboot, add a DEVICE_SHUTDOWN event to the end of event list, the timestamp
- // is last time UsageStatsDatabase is persisted to disk.
+ // is last time UsageStatsDatabase is persisted to disk or the last event's time whichever
+ // is higher (because the file system timestamp is round down to integral seconds).
// Also add a DEVICE_STARTUP event with current system timestamp.
final IntervalStats currentDailyStats = mCurrentStats[INTERVAL_DAILY];
if (currentDailyStats != null) {
- // File system timestamp only has precision of 1 second, add 1000ms to make up
- // for the loss of round up.
- final Event shutdownEvent =
- new Event(DEVICE_SHUTDOWN, currentDailyStats.lastTimeSaved + 1000);
+ final Event shutdownEvent = new Event(DEVICE_SHUTDOWN,
+ Math.max(currentDailyStats.lastTimeSaved, currentDailyStats.endTime));
shutdownEvent.mPackage = Event.DEVICE_EVENT_PACKAGE_NAME;
currentDailyStats.addEvent(shutdownEvent);
- final Event startupEvent = new Event(DEVICE_STARTUP, currentTimeMillis);
+ final Event startupEvent = new Event(DEVICE_STARTUP, System.currentTimeMillis());
startupEvent.mPackage = Event.DEVICE_EVENT_PACKAGE_NAME;
currentDailyStats.addEvent(startupEvent);
}
@@ -169,6 +174,54 @@ class UserUsageStatsService {
persistActiveStats();
}
+ void onPackageRemoved(String packageName, long timeRemoved) {
+ mDatabase.onPackageRemoved(packageName, timeRemoved);
+ }
+
+ private void readPackageMappingsLocked() {
+ mDatabase.readMappingsLocked();
+ cleanUpPackageMappingsLocked();
+ }
+
+ /**
+ * Queries Package Manager for a list of installed packages and removes those packages from
+ * mPackagesTokenData which are not installed any more.
+ * This will only happen once per device boot, when the user is unlocked for the first time.
+ */
+ private void cleanUpPackageMappingsLocked() {
+ final long timeNow = System.currentTimeMillis();
+ /*
+ Note (b/142501248): PackageManagerInternal#getInstalledApplications is not lightweight.
+ Once its implementation is updated, or it's replaced with a better alternative, update
+ the call here to use it. For now, using the heavy #getInstalledApplications is okay since
+ this clean-up is only performed once every boot.
+ */
+ final PackageManagerInternal packageManagerInternal =
+ LocalServices.getService(PackageManagerInternal.class);
+ if (packageManagerInternal == null) {
+ return;
+ }
+ final List<ApplicationInfo> installedPackages =
+ packageManagerInternal.getInstalledApplications(0, mUserId, Process.SYSTEM_UID);
+ // convert the package list to a set for easy look-ups
+ final HashSet<String> packagesSet = new HashSet<>(installedPackages.size());
+ for (int i = installedPackages.size() - 1; i >= 0; i--) {
+ packagesSet.add(installedPackages.get(i).packageName);
+ }
+ final List<String> removedPackages = new ArrayList<>();
+ // populate list of packages that are found in the mappings but not in the installed list
+ for (int i = mDatabase.mPackagesTokenData.packagesToTokensMap.size() - 1; i >= 0; i--) {
+ if (!packagesSet.contains(mDatabase.mPackagesTokenData.packagesToTokensMap.keyAt(i))) {
+ removedPackages.add(mDatabase.mPackagesTokenData.packagesToTokensMap.keyAt(i));
+ }
+ }
+
+ // remove packages in the mappings that are no longer installed
+ for (int i = removedPackages.size() - 1; i >= 0; i--) {
+ mDatabase.mPackagesTokenData.removePackage(removedPackages.get(i), timeNow);
+ }
+ }
+
private void onTimeChanged(long oldTime, long newTime) {
persistActiveStats();
mDatabase.onTimeChanged(newTime - oldTime);
@@ -400,6 +453,7 @@ class UserUsageStatsService {
if (results == null) {
results = new ArrayList<>();
}
+ mDatabase.filterStats(currentStats);
combiner.combine(currentStats, true, results);
}
@@ -524,6 +578,8 @@ class UserUsageStatsService {
if (mStatsChanged) {
Slog.i(TAG, mLogPrefix + "Flushing usage stats to disk");
try {
+ mDatabase.obfuscateCurrentStats(mCurrentStats);
+ mDatabase.writeMappingsLocked();
for (int i = 0; i < mCurrentStats.length; i++) {
mDatabase.putUsageStats(i, mCurrentStats[i]);
}
@@ -700,6 +756,10 @@ class UserUsageStatsService {
mDatabase.dump(ipw, false);
}
+ void dumpMappings(IndentingPrintWriter ipw) {
+ mDatabase.dumpMappings(ipw);
+ }
+
void dumpFile(IndentingPrintWriter ipw, String[] args) {
if (args == null || args.length == 0) {
// dump all files for every interval for specified user
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index e899dff5e5a2..047fcecd5a5b 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -386,7 +386,7 @@ public class UsbHostManager {
return false;
}
- UsbDevice.Builder newDeviceBuilder = parser.toAndroidUsbDevice();
+ UsbDevice.Builder newDeviceBuilder = parser.toAndroidUsbDeviceBuilder();
if (newDeviceBuilder == null) {
Slog.e(TAG, "Couldn't create UsbDevice object.");
// Tracking
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
index 0535d717222e..ff7f3934086c 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
@@ -52,6 +52,7 @@ abstract class UsbACEndpoint extends UsbDescriptor {
int length, byte type) {
UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
int subClass = interfaceDesc.getUsbSubclass();
+ // TODO shouldn't this switch on subtype?
switch (subClass) {
case AUDIO_AUDIOCONTROL:
if (UsbDescriptorParser.DEBUG) {
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index 6ffbd43a7b1a..b230e4bbf8a2 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -26,7 +26,7 @@ import java.util.ArrayList;
*/
public final class UsbDescriptorParser {
private static final String TAG = "UsbDescriptorParser";
- public static final boolean DEBUG = true;
+ public static final boolean DEBUG = false;
private final String mDeviceAddr;
@@ -43,6 +43,11 @@ public final class UsbDescriptorParser {
// Obtained from the first AudioClass Header descriptor.
private int mACInterfacesSpec = UsbDeviceDescriptor.USBSPEC_1_0;
+ // The VideoClass spec implemented by the VideoClass Interfaces
+ // This may well be different than the overall USB Spec.
+ // Obtained from the first VidieoClass Header descriptor.
+ private int mVCInterfacesSpec = UsbDeviceDescriptor.USBSPEC_1_0;
+
/**
* Connect this parser to an existing set of already parsed descriptors.
* This is useful for reporting.
@@ -90,6 +95,14 @@ public final class UsbDescriptorParser {
return mACInterfacesSpec;
}
+ public void setVCInterfaceSpec(int spec) {
+ mVCInterfacesSpec = spec;
+ }
+
+ public int getVCInterfaceSpec() {
+ return mVCInterfacesSpec;
+ }
+
private class UsbDescriptorsStreamFormatException extends Exception {
String mMessage;
UsbDescriptorsStreamFormatException(String message) {
@@ -186,19 +199,20 @@ public final class UsbDescriptorParser {
break;
case UsbDescriptor.CLASSID_VIDEO:
- Log.d(TAG, " UsbDescriptor.CLASSID_VIDEO subType:0x"
- + Integer.toHexString(stream.getByte()));
+ if (DEBUG) {
+ Log.d(TAG, " UsbDescriptor.CLASSID_VIDEO");
+ }
descriptor = UsbVCInterface.allocDescriptor(this, stream, length, type);
break;
case UsbDescriptor.CLASSID_AUDIOVIDEO:
- Log.d(TAG, " UsbDescriptor.CLASSID_AUDIOVIDEO subType:0x"
- + Integer.toHexString(stream.getByte()));
+ if (DEBUG) {
+ Log.d(TAG, " UsbDescriptor.CLASSID_AUDIOVIDEO");
+ }
break;
default:
- Log.d(TAG, " Unparsed Class-specific Interface:0x"
- + Integer.toHexString(mCurInterfaceDescriptor.getUsbClass()));
+ Log.w(TAG, " Unparsed Class-specific");
break;
}
}
@@ -206,23 +220,32 @@ public final class UsbDescriptorParser {
case UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_ENDPOINT:
if (mCurInterfaceDescriptor != null) {
- switch (mCurInterfaceDescriptor.getUsbClass()) {
+ int subClass = mCurInterfaceDescriptor.getUsbClass();
+ switch (subClass) {
case UsbDescriptor.CLASSID_AUDIO:
descriptor = UsbACEndpoint.allocDescriptor(this, length, type);
break;
- case UsbDescriptor.CLASSID_VIDEO:
- Log.d(TAG, "UsbDescriptor.CLASSID_VIDEO subType:0x"
- + Integer.toHexString(stream.getByte()));
- descriptor = UsbVCEndpoint.allocDescriptor(this, length, type);
+
+ case UsbDescriptor.CLASSID_VIDEO: {
+ Byte subtype = stream.getByte();
+ if (DEBUG) {
+ Log.d(TAG, "UsbDescriptor.CLASSID_VIDEO type:0x"
+ + Integer.toHexString(type));
+ }
+ descriptor = UsbVCEndpoint.allocDescriptor(this, length, type, subtype);
+ }
break;
case UsbDescriptor.CLASSID_AUDIOVIDEO:
- Log.d(TAG, "UsbDescriptor.CLASSID_AUDIOVIDEO subType:0x"
- + Integer.toHexString(stream.getByte()));
+ if (DEBUG) {
+ Log.d(TAG, "UsbDescriptor.CLASSID_AUDIOVIDEO type:0x"
+ + Integer.toHexString(type));
+ }
break;
+
default:
- Log.d(TAG, " Unparsed Class-specific Endpoint:0x"
- + Integer.toHexString(mCurInterfaceDescriptor.getUsbClass()));
+ Log.w(TAG, " Unparsed Class-specific Endpoint:0x"
+ + Integer.toHexString(subClass));
break;
}
}
@@ -309,17 +332,17 @@ public final class UsbDescriptorParser {
/**
* @hide
*/
- public UsbDevice.Builder toAndroidUsbDevice() {
+ public UsbDevice.Builder toAndroidUsbDeviceBuilder() {
if (mDeviceDescriptor == null) {
Log.e(TAG, "toAndroidUsbDevice() ERROR - No Device Descriptor");
return null;
}
- UsbDevice.Builder device = mDeviceDescriptor.toAndroid(this);
- if (device == null) {
+ UsbDevice.Builder builder = mDeviceDescriptor.toAndroid(this);
+ if (builder == null) {
Log.e(TAG, "toAndroidUsbDevice() ERROR Creating Device");
}
- return device;
+ return builder;
}
/**
@@ -524,6 +547,61 @@ public final class UsbDescriptorParser {
/**
* @hide
*/
+ public boolean hasAudioTerminal(int subType) {
+ for (UsbDescriptor descriptor : mDescriptors) {
+ if (descriptor instanceof UsbACInterface) {
+ if (((UsbACInterface) descriptor).getSubclass()
+ == UsbDescriptor.AUDIO_AUDIOCONTROL
+ && ((UsbACInterface) descriptor).getSubtype()
+ == subType) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean hasAudioPlayback() {
+ return hasAudioTerminal(UsbACInterface.ACI_OUTPUT_TERMINAL);
+ }
+
+ /**
+ * @hide
+ */
+ public boolean hasAudioCapture() {
+ return hasAudioTerminal(UsbACInterface.ACI_INPUT_TERMINAL);
+ }
+
+ /**
+ * @hide
+ */
+ public boolean hasVideoCapture() {
+ for (UsbDescriptor descriptor : mDescriptors) {
+ if (descriptor instanceof UsbVCInputTerminal) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean hasVideoPlayback() {
+ for (UsbDescriptor descriptor : mDescriptors) {
+ if (descriptor instanceof UsbVCOutputTerminal) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ */
public boolean hasHIDInterface() {
ArrayList<UsbDescriptor> descriptors =
getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_HID);
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
index e6e10fef9f9a..b1cbbafb9ccd 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
@@ -156,11 +156,12 @@ public final class UsbDeviceDescriptor extends UsbDescriptor {
for (int index = 0; index < mConfigDescriptors.size(); index++) {
configs[index] = mConfigDescriptors.get(index).toAndroid(parser);
}
- UsbDevice.Builder device = new UsbDevice.Builder(parser.getDeviceAddr(), mVendorID,
- mProductID, mDevClass, mDevSubClass, mProtocol, mfgName, prodName, versionString,
- configs, serialStr);
- return device;
+ return new UsbDevice.Builder(parser.getDeviceAddr(), mVendorID,
+ mProductID, mDevClass, mDevSubClass, mProtocol, mfgName, prodName, versionString,
+ configs, serialStr, parser.hasAudioPlayback(), parser.hasAudioCapture(),
+ parser.hasMIDIInterface(),
+ parser.hasVideoPlayback(), parser.hasVideoCapture());
}
@Override
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCEndpoint.java
index 39fbc0d50b87..f9acecee6bcf 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbVCEndpoint.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCEndpoint.java
@@ -20,41 +20,54 @@ import android.util.Log;
/**
* @hide
* A video class-specific Endpoint
- * see
+ * see USB_Video_Class_1.1.pdf - 3.10 VideoStreaming Endpoint Descriptors
*/
abstract class UsbVCEndpoint extends UsbDescriptor {
private static final String TAG = "UsbVCEndpoint";
- UsbVCEndpoint(int length, byte type, int subclass) {
+
+ public static final byte VCEP_UNDEFINED = 0x00;
+ public static final byte VCEP_GENERAL = 0x01;
+ public static final byte VCEP_ENDPOINT = 0x02;
+ public static final byte VCEP_INTERRUPT = 0x03;
+
+ UsbVCEndpoint(int length, byte type) {
super(length, type);
- // mSubclass = subclass;
}
public static UsbDescriptor allocDescriptor(UsbDescriptorParser parser,
- int length, byte type) {
+ int length, byte type, byte subtype) {
UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
- int subClass = interfaceDesc.getUsbSubclass();
- switch (subClass) {
-// case AUDIO_AUDIOCONTROL:
-// if (UsbDescriptorParser.DEBUG) {
-// Log.i(TAG, "---> AUDIO_AUDIOCONTROL");
-// }
-// return new UsbACAudioControlEndpoint(length, type, subClass);
-//
-// case AUDIO_AUDIOSTREAMING:
-// if (UsbDescriptorParser.DEBUG) {
-// Log.i(TAG, "---> AUDIO_AUDIOSTREAMING");
-// }
-// return new UsbACAudioStreamEndpoint(length, type, subClass);
-//
-// case AUDIO_MIDISTREAMING:
-// if (UsbDescriptorParser.DEBUG) {
-// Log.i(TAG, "---> AUDIO_MIDISTREAMING");
-// }
-// return new UsbACMidiEndpoint(length, type, subClass);
+
+ // TODO - create classes for each specific subtype
+ // (don't need it to answer if this device supports video
+ switch (subtype) {
+ case VCEP_UNDEFINED:
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, "---> VCEP_UNDEFINED");
+ }
+ return null;
+
+ case VCEP_GENERAL:
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, "---> VCEP_GENERAL");
+ }
+ return null;
+
+ case VCEP_ENDPOINT:
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, "---> VCEP_ENDPOINT");
+ }
+ return null;
+
+ case VCEP_INTERRUPT:
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, "---> VCEP_INTERRUPT");
+ }
+ return null;
default:
- Log.w(TAG, "Unknown Video Class Endpoint id:0x" + Integer.toHexString(subClass));
+ Log.w(TAG, "Unknown Video Class Endpoint id:0x" + Integer.toHexString(subtype));
return null;
}
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCHeader.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCHeader.java
new file mode 100644
index 000000000000..3fc42241b550
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCHeader.java
@@ -0,0 +1,50 @@
+/*
+ * 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.usb.descriptors;
+
+import android.util.Log;
+
+import com.android.server.usb.descriptors.report.ReportCanvas;
+
+/**
+ * @hide
+ * A video class-specific Interface Header.
+ * see USB_Video_Class_1.1.pdf section 3.9.2 - Class-Specific VS Interface Descriptors
+ */
+public final class UsbVCHeader extends UsbVCHeaderInterface {
+ private static final String TAG = "UsbVCHeader";
+
+ // TODO Add data members for this descriptor's data
+
+ public UsbVCHeader(int length, byte type, byte subtype, int spec) {
+ super(length, type, subtype, spec);
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " ---> parseRawDescriptors()");
+ }
+ // TODO parse data members for this descriptor's data
+ return super.parseRawDescriptors(stream);
+ }
+
+ @Override
+ public void report(ReportCanvas canvas) {
+ super.report(canvas);
+ // TODO add reporting specific to this descriptor
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCHeaderInterface.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCHeaderInterface.java
new file mode 100644
index 000000000000..372509105ac3
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCHeaderInterface.java
@@ -0,0 +1,56 @@
+/*
+ * 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.usb.descriptors;
+
+import com.android.server.usb.descriptors.report.ReportCanvas;
+
+/**
+ * @hide
+ * A video class-specific Interface Header super class.
+ * see USB_Video_Class_1.1.pdf section 3.9.2 - Class-Specific VS Interface Descriptors
+ */
+public abstract class UsbVCHeaderInterface extends UsbVCInterface {
+ private static final String TAG = "UsbVCHeaderInterface";
+
+ protected int mVDCRelease; // Video Device Class Specification Release (BCD).
+ protected int mTotalLength; // Total number of bytes returned for the class-specific
+ // VideoControl interface descriptor. Includes the combined length
+ // of this descriptor header and all Unit and Terminal descriptors.
+
+ public UsbVCHeaderInterface(
+ int length, byte type, byte subtype, int vdcRelease) {
+ super(length, type, subtype);
+ mVDCRelease = vdcRelease;
+ }
+
+ public int getVDCRelease() {
+ return mVDCRelease;
+ }
+
+ public int getTotalLength() {
+ return mTotalLength;
+ }
+
+ @Override
+ public void report(ReportCanvas canvas) {
+ super.report(canvas);
+
+ canvas.openList();
+ canvas.writeListItem("Release: " + ReportCanvas.getBCDString(getVDCRelease()));
+ canvas.writeListItem("Total Length: " + getTotalLength());
+ canvas.closeList();
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCInputTerminal.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCInputTerminal.java
new file mode 100644
index 000000000000..df637950899b
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCInputTerminal.java
@@ -0,0 +1,49 @@
+/*
+ * 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.usb.descriptors;
+
+import android.util.Log;
+
+import com.android.server.usb.descriptors.report.ReportCanvas;
+
+/**
+ * @hide
+ * A video class-specific Input terminal interface.
+ * see USB_Video_Class_1.1.pdf section 3.7.2.1 Input Terminal Descriptor
+ */
+public final class UsbVCInputTerminal extends UsbVCInterface {
+ private static final String TAG = "UsbVCInputTerminal";
+
+ // TODO Define members to hold the data from this descriptor
+ public UsbVCInputTerminal(int length, byte type, byte subtype) {
+ super(length, type, subtype);
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ // TODO Parse the data from this descriptor
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " ---> parseRawDescriptors()");
+ }
+ return super.parseRawDescriptors(stream);
+ }
+
+ @Override
+ public void report(ReportCanvas canvas) {
+ // TODO Add reporting specific to this descriptor
+ super.report(canvas);
+ }
+};
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCInterface.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCInterface.java
index c9eb1ec36eb1..46263dde5564 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbVCInterface.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCInterface.java
@@ -29,20 +29,17 @@ public abstract class UsbVCInterface extends UsbDescriptor {
public static final byte VCI_UNDEFINED = 0x00;
public static final byte VCI_VEADER = 0x01;
public static final byte VCI_INPUT_TERMINAL = 0x02;
- public static final byte VCI_VOUTPUT_TERMINAL = 0x03;
+ public static final byte VCI_OUTPUT_TERMINAL = 0x03;
public static final byte VCI_SELECTOR_UNIT = 0x04;
- public static final byte VCI_VROCESSING_UNIT = 0x05;
- public static final byte VCI_VEXTENSION_UNIT = 0x06;
+ public static final byte VCI_PROCESSING_UNIT = 0x05;
+ public static final byte VCI_EXTENSION_UNIT = 0x06;
- // See “Universal Serial Bus Device Class Definition for Video
+ // See “Universal Serial Bus Device Class Definition for Video
protected final byte mSubtype; // 2:1 HEADER descriptor subtype
- protected final int mSubclass; // from the mSubclass member of the
- // "enclosing" Interface Descriptor
- public UsbVCInterface(int length, byte type, byte subtype, int subclass) {
+ public UsbVCInterface(int length, byte type, byte subtype) {
super(length, type);
mSubtype = subtype;
- mSubclass = subclass;
}
/**
@@ -52,12 +49,10 @@ public abstract class UsbVCInterface extends UsbDescriptor {
int length, byte type) {
byte subtype = stream.getByte();
UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
- int subClass = interfaceDesc.getUsbSubclass();
if (UsbDescriptorParser.DEBUG) {
- Log.d(TAG, " Video Class-specific Interface subClass:0x"
- + Integer.toHexString(subClass));
+ Log.d(TAG, " Video Class-specific Interface subtype: " + subtype);
}
- switch (subClass) {
+ switch (subtype) {
// TODO - Create descriptor classes and parse these...
case VCI_UNDEFINED:
if (UsbDescriptorParser.DEBUG) {
@@ -66,44 +61,51 @@ public abstract class UsbVCInterface extends UsbDescriptor {
break;
case VCI_VEADER:
+ {
if (UsbDescriptorParser.DEBUG) {
Log.d(TAG, " ---> VCI_VEADER");
}
- break;
+ int vcInterfaceSpec = stream.unpackUsbShort();
+ parser.setVCInterfaceSpec(vcInterfaceSpec);
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " vcInterfaceSpec:0x" + Integer.toHexString(vcInterfaceSpec));
+ }
+ return new UsbVCHeader(length, type, subtype, vcInterfaceSpec);
+ }
case VCI_INPUT_TERMINAL:
if (UsbDescriptorParser.DEBUG) {
Log.d(TAG, " ---> VCI_INPUT_TERMINAL");
}
- break;
+ return new UsbVCInputTerminal(length, type, subtype);
- case VCI_VOUTPUT_TERMINAL:
+ case VCI_OUTPUT_TERMINAL:
if (UsbDescriptorParser.DEBUG) {
- Log.d(TAG, " ---> VCI_VOUTPUT_TERMINAL");
+ Log.d(TAG, " ---> VCI_OUTPUT_TERMINAL");
}
- break;
+ return new UsbVCOutputTerminal(length, type, subtype);
case VCI_SELECTOR_UNIT:
if (UsbDescriptorParser.DEBUG) {
Log.d(TAG, " ---> VCI_SELECTOR_UNIT");
}
- break;
+ return new UsbVCSelectorUnit(length, type, subtype);
- case VCI_VROCESSING_UNIT:
+ case VCI_PROCESSING_UNIT:
if (UsbDescriptorParser.DEBUG) {
- Log.d(TAG, " ---> VCI_VROCESSING_UNIT");
+ Log.d(TAG, " ---> VCI_PROCESSING_UNIT");
}
- break;
+ return new UsbVCProcessingUnit(length, type, subtype);
- case VCI_VEXTENSION_UNIT:
+ case VCI_EXTENSION_UNIT:
if (UsbDescriptorParser.DEBUG) {
- Log.d(TAG, " ---> VCI_VEXTENSION_UNIT");
+ Log.d(TAG, " ---> VCI_EXTENSION_UNIT");
}
break;
default:
- Log.w(TAG, "Unknown Video Class Interface Subclass: 0x"
- + Integer.toHexString(subClass));
+ Log.w(TAG, "Unknown Video Class Interface subtype: 0x"
+ + Integer.toHexString(subtype));
return null;
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCOutputTerminal.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCOutputTerminal.java
new file mode 100644
index 000000000000..4aa8ca22cc4e
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCOutputTerminal.java
@@ -0,0 +1,49 @@
+/*
+ * 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.usb.descriptors;
+
+import android.util.Log;
+
+import com.android.server.usb.descriptors.report.ReportCanvas;
+
+/**
+ * @hide
+ * A video class-specific Output Terminal Descriptor.
+ * see USB_Video_Class_1.1.pdf section 3.7.2.2 Output Terminal Descriptor
+ */
+public final class UsbVCOutputTerminal extends UsbVCInterface {
+ private static final String TAG = "UsbVCOutputTerminal";
+
+ // TODO Add members for the data in this descriptor
+ public UsbVCOutputTerminal(int length, byte type, byte subtype) {
+ super(length, type, subtype);
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ // TODO Parse the data in this descriptor
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " ---> parseRawDescriptors()");
+ }
+ return super.parseRawDescriptors(stream);
+ }
+
+ @Override
+ public void report(ReportCanvas canvas) {
+ super.report(canvas);
+ // TODO Add reporting specific to this descriptor
+ }
+};
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCProcessingUnit.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCProcessingUnit.java
new file mode 100644
index 000000000000..5ce842e82598
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCProcessingUnit.java
@@ -0,0 +1,50 @@
+/*
+ * 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.usb.descriptors;
+
+import android.util.Log;
+
+import com.android.server.usb.descriptors.report.ReportCanvas;
+
+/**
+ * @hide
+ * An video class-specific Processing Unit Interface.
+ * see USB_Video_Class_1.1.pdf section Table 3-8 Processing Unit Descriptor
+ */
+public final class UsbVCProcessingUnit extends UsbVCInterface {
+ private static final String TAG = "UsbVCProcessingUnit";
+
+ // TODO Add data members for this descriptor
+
+ public UsbVCProcessingUnit(int length, byte type, byte subtype) {
+ super(length, type, subtype);
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ // TODO Parse this descriptor
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " ---> parseRawDescriptors()");
+ }
+ return super.parseRawDescriptors(stream);
+ }
+
+ @Override
+ public void report(ReportCanvas canvas) {
+ super.report(canvas);
+ // TODO Add reporting specific to this descriptor
+ }
+};
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCSelectorUnit.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCSelectorUnit.java
new file mode 100644
index 000000000000..8e9b0d886389
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCSelectorUnit.java
@@ -0,0 +1,50 @@
+/*
+ * 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.usb.descriptors;
+
+import android.util.Log;
+
+import com.android.server.usb.descriptors.report.ReportCanvas;
+
+/**
+ * @hide
+ * An video class-specific Selector Unit Descriptor
+ * see USB_Video_Class_1.1.pdf section 3.7.2.4
+ */
+public final class UsbVCSelectorUnit extends UsbVCInterface {
+ private static final String TAG = "UsbVCSelectorUnit";
+
+ // TODO Add data members for this descriptor
+
+ public UsbVCSelectorUnit(int length, byte type, byte subtype) {
+ super(length, type, subtype);
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ // TODO Parse this descriptor
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " ---> parseRawDescriptors()");
+ }
+ return super.parseRawDescriptors(stream);
+ }
+
+ @Override
+ public void report(ReportCanvas canvas) {
+ super.report(canvas);
+ // TODO Add reporting specific to this descriptor
+ }
+};
diff --git a/services/wifi/Android.bp b/services/wifi/Android.bp
index 3c916a6d00cd..608fc2c7a55e 100644
--- a/services/wifi/Android.bp
+++ b/services/wifi/Android.bp
@@ -5,6 +5,9 @@ java_library_static {
"java/**/*.java",
"java/**/*.aidl",
],
+ aidl: {
+ local_include_dirs: ["java"]
+ },
libs: [
"services.net",
],
diff --git a/services/wifi/java/android/net/wifi/IWifiStackConnector.aidl b/services/wifi/java/android/net/wifi/IWifiStackConnector.aidl
index eadc7260e81b..3af4666b8d9c 100644
--- a/services/wifi/java/android/net/wifi/IWifiStackConnector.aidl
+++ b/services/wifi/java/android/net/wifi/IWifiStackConnector.aidl
@@ -15,8 +15,9 @@
*/
package android.net.wifi;
+import android.net.wifi.WifiApiServiceInfo;
+
/** @hide */
interface IWifiStackConnector {
- IBinder retrieveApiServiceImpl(String serviceName);
- boolean startApiService(String serviceName);
+ List<WifiApiServiceInfo> getWifiApiServiceInfos();
}
diff --git a/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl b/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl
new file mode 100644
index 000000000000..45e4c69102f0
--- /dev/null
+++ b/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+/** @hide */
+parcelable WifiApiServiceInfo {
+ String name;
+ IBinder binder;
+}
diff --git a/services/wifi/java/android/net/wifi/WifiStackClient.java b/services/wifi/java/android/net/wifi/WifiStackClient.java
index 64af7a8845a7..dcdfbc54687c 100644
--- a/services/wifi/java/android/net/wifi/WifiStackClient.java
+++ b/services/wifi/java/android/net/wifi/WifiStackClient.java
@@ -21,12 +21,13 @@ import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
import android.annotation.NonNull;
import android.content.Context;
import android.net.ConnectivityModuleConnector;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
+import java.util.List;
+
/**
* Service used to communicate with the wifi stack, which could be running in a separate
* module.
@@ -56,21 +57,23 @@ public class WifiStackClient {
@Override
public void onModuleServiceConnected(IBinder service) {
Log.i(TAG, "Wifi stack connected");
+ registerWifiStackService(service);
+
+ IWifiStackConnector connector = IWifiStackConnector.Stub.asInterface(service);
- // spin up a new thread to not block system_server main thread
- HandlerThread thread = new HandlerThread("InitWifiServicesThread");
- thread.start();
- thread.getThreadHandler().post(() -> {
- registerWifiStackService(service);
- IWifiStackConnector connector = IWifiStackConnector.Stub.asInterface(service);
- registerApiServiceAndStart(connector, Context.WIFI_SCANNING_SERVICE);
- registerApiServiceAndStart(connector, Context.WIFI_SERVICE);
- registerApiServiceAndStart(connector, Context.WIFI_P2P_SERVICE);
- registerApiServiceAndStart(connector, Context.WIFI_AWARE_SERVICE);
- registerApiServiceAndStart(connector, Context.WIFI_RTT_RANGING_SERVICE);
+ List<WifiApiServiceInfo> wifiApiServiceInfos;
+ try {
+ wifiApiServiceInfos = connector.getWifiApiServiceInfos();
+ } catch (RemoteException e) {
+ throw new RuntimeException("Failed to getWifiApiServiceInfos()", e);
+ }
- thread.quitSafely();
- });
+ for (WifiApiServiceInfo wifiApiServiceInfo : wifiApiServiceInfos) {
+ String serviceName = wifiApiServiceInfo.name;
+ IBinder binder = wifiApiServiceInfo.binder;
+ Log.i(TAG, "Registering " + serviceName);
+ ServiceManager.addService(serviceName, binder);
+ }
}
}
@@ -81,32 +84,6 @@ public class WifiStackClient {
Log.i(TAG, "Wifi stack service registered");
}
- private void registerApiServiceAndStart(
- IWifiStackConnector stackConnector, String serviceName) {
- IBinder service = null;
- try {
- service = stackConnector.retrieveApiServiceImpl(serviceName);
- } catch (RemoteException e) {
- throw new RuntimeException("Failed to retrieve service impl " + serviceName, e);
- }
- if (service == null) {
- Log.i(TAG, "Service " + serviceName + " not available");
- return;
- }
- Log.i(TAG, "Registering " + serviceName);
- ServiceManager.addService(serviceName, service);
-
- boolean success = false;
- try {
- success = stackConnector.startApiService(serviceName);
- } catch (RemoteException e) {
- throw new RuntimeException("Failed to start service " + serviceName, e);
- }
- if (!success) {
- throw new RuntimeException("Service " + serviceName + " start failed");
- }
- }
-
/**
* Start the wifi stack. Should be called only once on device startup.
*
diff --git a/startop/apps/test/Android.bp b/startop/apps/test/Android.bp
index a8063209b56f..5de7fd2fb0a0 100644
--- a/startop/apps/test/Android.bp
+++ b/startop/apps/test/Android.bp
@@ -17,12 +17,15 @@
android_app {
name: "startop_test_app",
srcs: [
- "src/CPUIntensive.java",
- "src/EmptyActivity.java",
- "src/LayoutInflationActivity.java",
"src/ComplexLayoutInflationActivity.java",
+ "src/CPUIntensiveBenchmarkActivity.java",
+ "src/CPUIntensiveBenchmarks.java",
+ "src/EmptyActivity.java",
"src/FrameLayoutInflationActivity.java",
+ "src/LayoutInflationActivity.java",
+ "src/NonInteractiveSystemServerBenchmarkActivity.java",
"src/SystemServerBenchmarkActivity.java",
+ "src/SystemServerBenchmarks.java",
"src/TextViewInflationActivity.java",
],
sdk_version: "26", // Android O (8.0) and higher
diff --git a/startop/apps/test/AndroidManifest.xml b/startop/apps/test/AndroidManifest.xml
index 15785d4d44b9..b08072e00584 100644
--- a/startop/apps/test/AndroidManifest.xml
+++ b/startop/apps/test/AndroidManifest.xml
@@ -37,6 +37,18 @@
</activity>
<activity
+ android:label="CPU Intensive Benchmark Test"
+ android:name=".CPUIntensiveBenchmarkActivity"
+ android:exported="true" >
+
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
android:label="Empty Activity Layout Test"
android:name=".EmptyActivity"
android:exported="true" >
@@ -84,6 +96,14 @@
</intent-filter>
</activity>
+ <activity
+ android:label="Non-interactive SystemServer Benchmark"
+ android:name=".NonInteractiveSystemServerBenchmarkActivity"
+ android:exported="true" />
+
</application>
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+
</manifest>
diff --git a/startop/apps/test/README.md b/startop/apps/test/README.md
index dadc66af3306..949dff75b723 100644
--- a/startop/apps/test/README.md
+++ b/startop/apps/test/README.md
@@ -24,3 +24,14 @@ spent in view inflation to make it easier to focus on the time spent in view
inflation.
adb shell am start -n com.android.startop.test/.ComplexLayoutInflationActivity
+
+## NonInteractiveSystemServerBenchmark
+
+This activity is for running microbenchmarks from the command line. Run as follows:
+
+ adb shell am start -W -n com.android.startop.test .NonInteractiveSystemServerBenchmarkActivity
+
+It takes awhile (and there's currently no automated way to make sure it's done),
+but when it finishes, you can get the results like this:
+
+ adb shell cat /sdcard/Android/data/com.android.startop.test/files/benchmark.csv
diff --git a/startop/apps/test/src/CPUIntensive.java b/startop/apps/test/src/CPUIntensive.java
deleted file mode 100644
index a411e8ce8ce0..000000000000
--- a/startop/apps/test/src/CPUIntensive.java
+++ /dev/null
@@ -1,67 +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.
- */
-
-/**
- * A threaded CPU intensive class for use in benchmarks.
- */
-
-package com.android.startop.test;
-
-final class CPUIntensive {
- public static final int THREAD_COUNT = 8;
- public static final int ARRAY_SIZE = 30000;
- public static int[][] array = new int[THREAD_COUNT][ARRAY_SIZE];
-
- static class WorkerThread extends Thread {
- int mThreadNumber;
- WorkerThread(int number) {
- mThreadNumber = number;
- }
- public void run() {
- final int arrayLength = array[mThreadNumber].length;
- for (int i = 0; i < arrayLength; ++i) {
- array[mThreadNumber][i] = i * i;
- }
- for (int i = 0; i < arrayLength; ++i) {
- for (int j = 0; j < arrayLength; ++j) {
- int swap = array[mThreadNumber][j];
- array[mThreadNumber][j] = array[mThreadNumber][(j + i) % arrayLength];
- array[mThreadNumber][(j + i) % arrayLength] = swap;
- }
- }
- }
- };
-
- public static void doSomeWork(int threadCount) {
- WorkerThread[] threads = new WorkerThread[threadCount];
- // Create the threads.
- for (int i = 0; i < threadCount; ++i) {
- threads[i] = new WorkerThread(i);
- }
- // Start the threads.
- for (int i = 0; i < threadCount; ++i) {
- threads[i].start();
- }
- // Join the threads.
- for (int i = 0; i < threadCount; ++i) {
- try {
- threads[i].join();
- } catch (Exception ex) {
- }
- }
- }
-}
-
diff --git a/startop/apps/test/src/CPUIntensiveBenchmarkActivity.java b/startop/apps/test/src/CPUIntensiveBenchmarkActivity.java
new file mode 100644
index 000000000000..2ec5308afe14
--- /dev/null
+++ b/startop/apps/test/src/CPUIntensiveBenchmarkActivity.java
@@ -0,0 +1,30 @@
+/*
+ * 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.startop.test;
+
+import android.os.Bundle;
+
+public class CPUIntensiveBenchmarkActivity extends SystemServerBenchmarkActivity {
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.system_server_benchmark_page);
+
+ mBenchmarkList = findViewById(R.id.benchmark_list);
+
+ CPUIntensiveBenchmarks.initializeBenchmarks(this, this);
+ }
+}
diff --git a/startop/apps/test/src/CPUIntensiveBenchmarks.java b/startop/apps/test/src/CPUIntensiveBenchmarks.java
new file mode 100644
index 000000000000..19d0b6372c1b
--- /dev/null
+++ b/startop/apps/test/src/CPUIntensiveBenchmarks.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * A threaded CPU intensive class for use in benchmarks.
+ */
+
+package com.android.startop.test;
+
+import android.app.Activity;
+
+public class CPUIntensiveBenchmarks {
+ public static final int ARRAY_SIZE = 30000;
+ public static int[][] mArray;
+
+ static class WorkerThread extends Thread {
+ int mThreadNumber;
+ WorkerThread(int number) {
+ mThreadNumber = number;
+ }
+ public void run() {
+ final int arrayLength = mArray[mThreadNumber].length;
+ for (int i = 0; i < arrayLength; ++i) {
+ mArray[mThreadNumber][i] = i * i;
+ }
+ for (int i = 0; i < arrayLength; ++i) {
+ for (int j = 0; j < arrayLength; ++j) {
+ int swap = mArray[mThreadNumber][j];
+ mArray[mThreadNumber][j] = mArray[mThreadNumber][(j + i) % arrayLength];
+ mArray[mThreadNumber][(j + i) % arrayLength] = swap;
+ }
+ }
+ }
+ };
+
+ static void doSomeWork(int threadCount) {
+ mArray = new int[threadCount][ARRAY_SIZE];
+ WorkerThread[] threads = new WorkerThread[threadCount];
+ // Create the threads.
+ for (int i = 0; i < threadCount; ++i) {
+ threads[i] = new WorkerThread(i);
+ }
+ // Start the threads.
+ for (int i = 0; i < threadCount; ++i) {
+ threads[i].start();
+ }
+ // Join the threads.
+ for (int i = 0; i < threadCount; ++i) {
+ try {
+ threads[i].join();
+ } catch (Exception ex) {
+ }
+ }
+ }
+
+ // Time limit to run benchmarks in seconds
+ public static final int TIME_LIMIT = 5;
+
+ static void initializeBenchmarks(Activity parent, BenchmarkRunner benchmarks) {
+ benchmarks.addBenchmark("Use 1 thread", () -> {
+ doSomeWork(1);
+ });
+ benchmarks.addBenchmark("Use 2 threads", () -> {
+ doSomeWork(2);
+ });
+ benchmarks.addBenchmark("Use 4 threads", () -> {
+ doSomeWork(4);
+ });
+ benchmarks.addBenchmark("Use 8 threads", () -> {
+ doSomeWork(8);
+ });
+ benchmarks.addBenchmark("Use 16 threads", () -> {
+ doSomeWork(16);
+ });
+ }
+}
diff --git a/startop/apps/test/src/NonInteractiveSystemServerBenchmarkActivity.java b/startop/apps/test/src/NonInteractiveSystemServerBenchmarkActivity.java
new file mode 100644
index 000000000000..a2dc2cf03d69
--- /dev/null
+++ b/startop/apps/test/src/NonInteractiveSystemServerBenchmarkActivity.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.startop.test;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.GridLayout;
+import android.widget.TextView;
+
+public class NonInteractiveSystemServerBenchmarkActivity extends Activity {
+ ArrayList<CharSequence> benchmarkNames = new ArrayList();
+ ArrayList<Runnable> benchmarkThunks = new ArrayList();
+
+ PrintStream out;
+
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ SystemServerBenchmarks.initializeBenchmarks(this, (name, thunk) -> {
+ benchmarkNames.add(name);
+ benchmarkThunks.add(thunk);
+ });
+
+ try {
+ out = new PrintStream(new File(getExternalFilesDir(null), "benchmark.csv"));
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ out.println("Name,Mean,Stdev");
+ runBenchmarks(0);
+ }
+
+ void runBenchmarks(int i) {
+ if (i < benchmarkNames.size()) {
+ SystemServerBenchmarks.runBenchmarkInBackground(benchmarkThunks.get(i),
+ (mean, stdev) -> {
+ out.printf("%s,%.0f,%.0f\n", benchmarkNames.get(i), mean, stdev);
+ runBenchmarks(i + 1);
+ });
+ }
+ }
+}
diff --git a/startop/apps/test/src/SystemServerBenchmarkActivity.java b/startop/apps/test/src/SystemServerBenchmarkActivity.java
index c8d9fde0bdaf..6be8df3728af 100644
--- a/startop/apps/test/src/SystemServerBenchmarkActivity.java
+++ b/startop/apps/test/src/SystemServerBenchmarkActivity.java
@@ -17,27 +17,31 @@
package com.android.startop.test;
import android.app.Activity;
-import android.app.ActivityManager;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.AsyncTask;
import android.os.Bundle;
-import android.view.ViewGroup;
import android.widget.Button;
import android.widget.GridLayout;
import android.widget.TextView;
+public class SystemServerBenchmarkActivity extends Activity implements BenchmarkRunner {
+ protected GridLayout mBenchmarkList;
-class Benchmark {
- // Time limit to run benchmarks in seconds
- public static final int TIME_LIMIT = 5;
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.system_server_benchmark_page);
+
+ mBenchmarkList = findViewById(R.id.benchmark_list);
- public Benchmark(ViewGroup parent, CharSequence name, Runnable thunk) {
- Context context = parent.getContext();
+ SystemServerBenchmarks.initializeBenchmarks(this, this);
+ }
+
+ /**
+ * Adds a benchmark to the set to run.
+ *
+ * @param name A short name that shows up in the UI or benchmark results
+ */
+ public void addBenchmark(CharSequence name, Runnable thunk) {
+ Context context = mBenchmarkList.getContext();
Button button = new Button(context);
TextView mean = new TextView(context);
TextView stdev = new TextView(context);
@@ -50,165 +54,14 @@ class Benchmark {
mean.setText("Running...");
stdev.setText("");
- new AsyncTask() {
- double resultMean = 0;
- double resultStdev = 0;
-
- @Override
- protected Object doInBackground(Object... _args) {
- long startTime = System.nanoTime();
- int count = 0;
-
- // Run benchmark
- while (true) {
- long elapsed = -System.nanoTime();
- thunk.run();
- elapsed += System.nanoTime();
-
- count++;
- double elapsedVariance = (double) elapsed - resultMean;
- resultMean += elapsedVariance / count;
- resultStdev += elapsedVariance * ((double) elapsed - resultMean);
-
- if (System.nanoTime() - startTime > TIME_LIMIT * 1e9) {
- break;
- }
- }
- resultStdev = Math.sqrt(resultStdev / (count - 1));
-
- return null;
- }
-
- @Override
- protected void onPostExecute(Object _result) {
- mean.setText(String.format("%.3f", resultMean / 1e6));
- stdev.setText(String.format("%.3f", resultStdev / 1e6));
- }
- }.execute(new Object());
- });
-
- parent.addView(button);
- parent.addView(mean);
- parent.addView(stdev);
- }
-}
-
-public class SystemServerBenchmarkActivity extends Activity {
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.system_server_benchmark_page);
-
- GridLayout benchmarkList = findViewById(R.id.benchmark_list);
-
- new Benchmark(benchmarkList, "Empty", () -> {
- });
-
- new Benchmark(benchmarkList, "CPU Intensive (1 thread)", () -> {
- CPUIntensive.doSomeWork(1);
- });
-
- new Benchmark(benchmarkList, "CPU Intensive (2 thread)", () -> {
- CPUIntensive.doSomeWork(2);
- });
-
- new Benchmark(benchmarkList, "CPU Intensive (4 thread)", () -> {
- CPUIntensive.doSomeWork(4);
- });
-
- new Benchmark(benchmarkList, "CPU Intensive (8 thread)", () -> {
- CPUIntensive.doSomeWork(8);
- });
-
- PackageManager pm = getPackageManager();
- new Benchmark(benchmarkList, "getInstalledApplications", () -> {
- pm.getInstalledApplications(PackageManager.MATCH_SYSTEM_ONLY);
- });
-
- new Benchmark(benchmarkList, "getInstalledPackages", () -> {
- pm.getInstalledPackages(PackageManager.GET_ACTIVITIES);
- });
-
- new Benchmark(benchmarkList, "getPackageInfo", () -> {
- try {
- pm.getPackageInfo("com.android.startop.test", 0);
- } catch (NameNotFoundException e) {
- throw new RuntimeException(e);
- }
- });
-
- new Benchmark(benchmarkList, "getApplicationInfo", () -> {
- try {
- pm.getApplicationInfo("com.android.startop.test", 0);
- } catch (NameNotFoundException e) {
- throw new RuntimeException(e);
- }
- });
-
- try {
- ApplicationInfo app = pm.getApplicationInfo("com.android.startop.test", 0);
- new Benchmark(benchmarkList, "getResourcesForApplication", () -> {
- try {
- pm.getResourcesForApplication(app);
- } catch (NameNotFoundException e) {
- throw new RuntimeException(e);
- }
- });
-
- new Benchmark(benchmarkList, "getPackagesForUid", () -> {
- pm.getPackagesForUid(app.uid);
+ SystemServerBenchmarks.runBenchmarkInBackground(thunk, (resultMean, resultStdev) -> {
+ mean.setText(String.format("%.3f", resultMean / 1e6));
+ stdev.setText(String.format("%.3f", resultStdev / 1e6));
});
- } catch (NameNotFoundException e) {
- throw new RuntimeException(e);
- }
-
- ComponentName component = new ComponentName(this, this.getClass());
- new Benchmark(benchmarkList, "getActivityInfo", () -> {
- try {
- pm.getActivityInfo(component, PackageManager.GET_META_DATA);
- } catch (NameNotFoundException e) {
- throw new RuntimeException(e);
- }
- });
-
- new Benchmark(benchmarkList, "getLaunchIntentForPackage", () -> {
- pm.getLaunchIntentForPackage("com.android.startop.test");
- });
-
- new Benchmark(benchmarkList, "getPackageUid", () -> {
- try {
- pm.getPackageUid("com.android.startop.test", 0);
- } catch (NameNotFoundException e) {
- throw new RuntimeException(e);
- }
- });
-
- new Benchmark(benchmarkList, "checkPermission", () -> {
- // Check for the first permission I could find.
- pm.checkPermission("android.permission.SEND_SMS", "com.android.startop.test");
- });
-
- new Benchmark(benchmarkList, "checkSignatures", () -> {
- // Compare with settings, since settings is on both AOSP and Master builds
- pm.checkSignatures("com.android.settings", "com.android.startop.test");
- });
-
- Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED);
- new Benchmark(benchmarkList, "queryBroadcastReceivers", () -> {
- pm.queryBroadcastReceivers(intent, 0);
- });
-
- new Benchmark(benchmarkList, "hasSystemFeature", () -> {
- pm.hasSystemFeature(PackageManager.FEATURE_CAMERA);
- });
-
- new Benchmark(benchmarkList, "resolveService", () -> {
- pm.resolveService(intent, 0);
- });
-
- ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
- new Benchmark(benchmarkList, "getRunningAppProcesses", () -> {
- am.getRunningAppProcesses();
});
+ mBenchmarkList.addView(button);
+ mBenchmarkList.addView(mean);
+ mBenchmarkList.addView(stdev);
}
}
diff --git a/startop/apps/test/src/SystemServerBenchmarks.java b/startop/apps/test/src/SystemServerBenchmarks.java
new file mode 100644
index 000000000000..25b43f4d53f6
--- /dev/null
+++ b/startop/apps/test/src/SystemServerBenchmarks.java
@@ -0,0 +1,245 @@
+/*
+ * 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.startop.test;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.ConnectivityManager;
+import android.os.AsyncTask;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.UserManager;
+
+/**
+ * An interface for running benchmarks and collecting results. Used so we can have both an
+ * interactive runner and a non-interactive runner.
+ */
+interface BenchmarkRunner {
+ void addBenchmark(CharSequence name, Runnable thunk);
+}
+
+interface ResultListener {
+ /**
+ * Called when a benchmark result is ready
+ *
+ * @param mean The average iteration time in nanoseconds
+ * @param stdev The standard deviation of iteration times in nanoseconds
+ */
+ void onResult(double mean, double stdev);
+}
+
+class SystemServerBenchmarks {
+ // Time limit to run benchmarks in seconds
+ public static final int TIME_LIMIT = 5;
+
+ static void initializeBenchmarks(Activity parent, BenchmarkRunner benchmarks) {
+ final String packageName = parent.getPackageName();
+
+ benchmarks.addBenchmark("Empty", () -> {
+ });
+
+ PackageManager pm = parent.getPackageManager();
+ benchmarks.addBenchmark("getInstalledApplications", () -> {
+ pm.getInstalledApplications(PackageManager.MATCH_SYSTEM_ONLY);
+ });
+
+ benchmarks.addBenchmark("getInstalledPackages", () -> {
+ pm.getInstalledPackages(PackageManager.GET_ACTIVITIES);
+ });
+
+ benchmarks.addBenchmark("getPackageInfo", () -> {
+ try {
+ pm.getPackageInfo(packageName, 0);
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ });
+
+ benchmarks.addBenchmark("getApplicationInfo", () -> {
+ try {
+ pm.getApplicationInfo(packageName, 0);
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ });
+
+ try {
+ ApplicationInfo app = pm.getApplicationInfo(packageName, 0);
+ benchmarks.addBenchmark("getResourcesForApplication", () -> {
+ try {
+ pm.getResourcesForApplication(app);
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ });
+
+ benchmarks.addBenchmark("getPackagesForUid", () -> {
+ pm.getPackagesForUid(app.uid);
+ });
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ ComponentName component = new ComponentName(parent, parent.getClass());
+ benchmarks.addBenchmark("getActivityInfo", () -> {
+ try {
+ pm.getActivityInfo(component, PackageManager.GET_META_DATA);
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ });
+
+ benchmarks.addBenchmark("getLaunchIntentForPackage", () -> {
+ pm.getLaunchIntentForPackage(packageName);
+ });
+
+ benchmarks.addBenchmark("getPackageUid", () -> {
+ try {
+ pm.getPackageUid(packageName, 0);
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ });
+
+ benchmarks.addBenchmark("checkPermission", () -> {
+ // Check for the first permission I could find.
+ pm.checkPermission("android.permission.SEND_SMS", packageName);
+ });
+
+ benchmarks.addBenchmark("checkSignatures", () -> {
+ // Compare with settings, since settings is on both AOSP and Master builds
+ pm.checkSignatures("com.android.settings", packageName);
+ });
+
+ Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED);
+ benchmarks.addBenchmark("queryBroadcastReceivers", () -> {
+ pm.queryBroadcastReceivers(intent, 0);
+ });
+
+ benchmarks.addBenchmark("hasSystemFeature", () -> {
+ pm.hasSystemFeature(PackageManager.FEATURE_CAMERA);
+ });
+
+ benchmarks.addBenchmark("resolveService", () -> {
+ pm.resolveService(intent, 0);
+ });
+
+ ActivityManager am = (ActivityManager) parent.getSystemService(Context.ACTIVITY_SERVICE);
+ benchmarks.addBenchmark("getRunningAppProcesses", () -> {
+ am.getRunningAppProcesses();
+ });
+
+ // We use PendingIntent.getCreatorPackage, since
+ // getPackageIntentForSender is not public to us, but getCreatorPackage
+ // is just a thin wrapper around it.
+ PendingIntent pi = PendingIntent.getActivity(parent, 0, new Intent(), 0);
+ benchmarks.addBenchmark("getPackageIntentForSender", () -> {
+ pi.getCreatorPackage();
+ });
+
+ PowerManager pwr = (PowerManager) parent.getSystemService(Context.POWER_SERVICE);
+ PowerManager.WakeLock wl = pwr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "benchmark tag");
+ benchmarks.addBenchmark("WakeLock Acquire/Release", () -> {
+ wl.acquire();
+ wl.release();
+ });
+
+ AppOpsManager appOps = (AppOpsManager) parent.getSystemService(Context.APP_OPS_SERVICE);
+ int uid = Process.myUid();
+ benchmarks.addBenchmark("AppOpsService.checkOperation", () -> {
+ appOps.checkOp(AppOpsManager.OPSTR_READ_EXTERNAL_STORAGE, uid, packageName);
+ });
+
+ benchmarks.addBenchmark("AppOpsService.checkPackage", () -> {
+ appOps.checkPackage(uid, packageName);
+ });
+
+ benchmarks.addBenchmark("AppOpsService.noteOperation", () -> {
+ appOps.noteOp(AppOpsManager.OPSTR_READ_EXTERNAL_STORAGE, uid, packageName);
+ });
+
+ benchmarks.addBenchmark("AppOpsService.noteProxyOperation", () -> {
+ appOps.noteProxyOp(AppOpsManager.OPSTR_READ_EXTERNAL_STORAGE, packageName);
+ });
+
+ UserManager userManager = (UserManager) parent.getSystemService(Context.USER_SERVICE);
+ benchmarks.addBenchmark("isUserUnlocked", () -> {
+ userManager.isUserUnlocked();
+ });
+
+ benchmarks.addBenchmark("getIntentSender", () -> {
+ pi.getIntentSender();
+ });
+
+ ConnectivityManager cm = (ConnectivityManager) parent
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ benchmarks.addBenchmark("getActiveNetworkInfo", () -> {
+ cm.getActiveNetworkInfo();
+ });
+ }
+
+ /**
+ * A helper method for benchark runners to actually run the benchmark and gather stats
+ *
+ * @param thunk The code whose performance we want to measure
+ * @param reporter What to do with the results
+ */
+ static void runBenchmarkInBackground(Runnable thunk, ResultListener reporter) {
+ new AsyncTask() {
+ double resultMean = 0;
+ double resultStdev = 0;
+
+ @Override
+ protected Object doInBackground(Object... _args) {
+ long startTime = System.nanoTime();
+ int count = 0;
+
+ // Run benchmark
+ while (true) {
+ long elapsed = -System.nanoTime();
+ thunk.run();
+ elapsed += System.nanoTime();
+
+ count++;
+ double elapsedVariance = (double) elapsed - resultMean;
+ resultMean += elapsedVariance / count;
+ resultStdev += elapsedVariance * ((double) elapsed - resultMean);
+
+ if (System.nanoTime() - startTime > TIME_LIMIT * 1e9) {
+ break;
+ }
+ }
+ resultStdev = Math.sqrt(resultStdev / (count - 1));
+
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Object _result) {
+ reporter.onResult(resultMean, resultStdev);
+ }
+ }.execute(new Object());
+ }
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java b/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java
index acf994610182..59f4d56d3f46 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java
@@ -33,6 +33,7 @@ import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
import java.util.Objects;
/**
@@ -86,10 +87,14 @@ public abstract class AppLaunchEvent implements Parcelable {
public static final class IntentStarted extends AppLaunchEvent {
@NonNull
public final Intent intent;
+ public final long timestampNs;
- public IntentStarted(@SequenceId long sequenceId, Intent intent) {
+ public IntentStarted(@SequenceId long sequenceId,
+ Intent intent,
+ long timestampNs) {
super(sequenceId);
this.intent = intent;
+ this.timestampNs = timestampNs;
Objects.requireNonNull(intent, "intent");
}
@@ -98,14 +103,16 @@ public abstract class AppLaunchEvent implements Parcelable {
public boolean equals(Object other) {
if (other instanceof IntentStarted) {
return intent.equals(((IntentStarted)other).intent) &&
- super.equals(other);
+ timestampNs == ((IntentStarted)other).timestampNs &&
+ super.equals(other);
}
return false;
}
@Override
protected String toStringBody() {
- return ", intent=" + intent.toString();
+ return ", intent=" + intent.toString() +
+ " , timestampNs=" + Long.toString(timestampNs);
}
@@ -113,11 +120,13 @@ public abstract class AppLaunchEvent implements Parcelable {
protected void writeToParcelImpl(Parcel p, int flags) {
super.writeToParcelImpl(p, flags);
IntentProtoParcelable.write(p, intent, flags);
+ p.writeLong(timestampNs);
}
IntentStarted(Parcel p) {
super(p);
intent = IntentProtoParcelable.create(p);
+ timestampNs = p.readLong();
}
}
@@ -154,8 +163,8 @@ public abstract class AppLaunchEvent implements Parcelable {
@Override
public boolean equals(Object other) {
if (other instanceof BaseWithActivityRecordData) {
- return activityRecordSnapshot.equals(
- ((BaseWithActivityRecordData)other).activityRecordSnapshot) &&
+ return (Arrays.equals(activityRecordSnapshot,
+ ((BaseWithActivityRecordData)other).activityRecordSnapshot)) &&
super.equals(other);
}
return false;
@@ -163,7 +172,7 @@ public abstract class AppLaunchEvent implements Parcelable {
@Override
protected String toStringBody() {
- return ", " + activityRecordSnapshot.toString();
+ return ", " + new String(activityRecordSnapshot);
}
@Override
@@ -200,7 +209,7 @@ public abstract class AppLaunchEvent implements Parcelable {
@Override
protected String toStringBody() {
- return ", temperature=" + Integer.toString(temperature);
+ return super.toStringBody() + ", temperature=" + Integer.toString(temperature);
}
@Override
@@ -216,18 +225,39 @@ public abstract class AppLaunchEvent implements Parcelable {
}
public static final class ActivityLaunchFinished extends BaseWithActivityRecordData {
+ public final long timestampNs;
+
public ActivityLaunchFinished(@SequenceId long sequenceId,
- @NonNull @ActivityRecordProto byte[] snapshot) {
+ @NonNull @ActivityRecordProto byte[] snapshot,
+ long timestampNs) {
super(sequenceId, snapshot);
+ this.timestampNs = timestampNs;
}
@Override
public boolean equals(Object other) {
- if (other instanceof ActivityLaunched) {
- return super.equals(other);
+ if (other instanceof ActivityLaunchFinished) {
+ return timestampNs == ((ActivityLaunchFinished)other).timestampNs &&
+ super.equals(other);
}
return false;
}
+
+ @Override
+ protected String toStringBody() {
+ return super.toStringBody() + ", timestampNs=" + Long.toString(timestampNs);
+ }
+
+ @Override
+ protected void writeToParcelImpl(Parcel p, int flags) {
+ super.writeToParcelImpl(p, flags);
+ p.writeLong(timestampNs);
+ }
+
+ ActivityLaunchFinished(Parcel p) {
+ super(p);
+ timestampNs = p.readLong();
+ }
}
public static class ActivityLaunchCancelled extends AppLaunchEvent {
@@ -242,8 +272,8 @@ public abstract class AppLaunchEvent implements Parcelable {
@Override
public boolean equals(Object other) {
if (other instanceof ActivityLaunchCancelled) {
- return Objects.equals(activityRecordSnapshot,
- ((ActivityLaunchCancelled)other).activityRecordSnapshot) &&
+ return Arrays.equals(activityRecordSnapshot,
+ ((ActivityLaunchCancelled)other).activityRecordSnapshot) &&
super.equals(other);
}
return false;
@@ -251,7 +281,7 @@ public abstract class AppLaunchEvent implements Parcelable {
@Override
protected String toStringBody() {
- return ", " + activityRecordSnapshot.toString();
+ return super.toStringBody() + ", " + new String(activityRecordSnapshot);
}
@Override
@@ -275,6 +305,42 @@ public abstract class AppLaunchEvent implements Parcelable {
}
}
+ public static final class ReportFullyDrawn extends BaseWithActivityRecordData {
+ public final long timestampNs;
+
+ public ReportFullyDrawn(@SequenceId long sequenceId,
+ @NonNull @ActivityRecordProto byte[] snapshot,
+ long timestampNs) {
+ super(sequenceId, snapshot);
+ this.timestampNs = timestampNs;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof ReportFullyDrawn) {
+ return timestampNs == ((ReportFullyDrawn)other).timestampNs &&
+ super.equals(other);
+ }
+ return false;
+ }
+
+ @Override
+ protected String toStringBody() {
+ return super.toStringBody() + ", timestampNs=" + Long.toString(timestampNs);
+ }
+
+ @Override
+ protected void writeToParcelImpl(Parcel p, int flags) {
+ super.writeToParcelImpl(p, flags);
+ p.writeLong(timestampNs);
+ }
+
+ ReportFullyDrawn(Parcel p) {
+ super(p);
+ timestampNs = p.readLong();
+ }
+ }
+
@Override
public @ContentsFlags int describeContents() { return 0; }
@@ -348,6 +414,7 @@ public abstract class AppLaunchEvent implements Parcelable {
ActivityLaunched.class,
ActivityLaunchFinished.class,
ActivityLaunchCancelled.class,
+ ReportFullyDrawn.class,
};
public static class ActivityRecordProtoParcelable {
diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
index 902da4c0f72e..f753548516ef 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
@@ -315,19 +315,19 @@ public class IorapForwardingService extends SystemService {
// All callbacks occur on the same background thread. Don't synchronize explicitly.
@Override
- public void onIntentStarted(@NonNull Intent intent) {
+ public void onIntentStarted(@NonNull Intent intent, long timestampNs) {
// #onIntentStarted [is the only transition that] initiates a new launch sequence.
++mSequenceId;
if (DEBUG) {
- Log.v(TAG, String.format("AppLaunchObserver#onIntentStarted(%d, %s)",
- mSequenceId, intent));
+ Log.v(TAG, String.format("AppLaunchObserver#onIntentStarted(%d, %s, %d)",
+ mSequenceId, intent, timestampNs));
}
invokeRemote(mIorapRemote,
(IIorap remote) ->
remote.onAppLaunchEvent(RequestId.nextValueForSequence(),
- new AppLaunchEvent.IntentStarted(mSequenceId, intent))
+ new AppLaunchEvent.IntentStarted(mSequenceId, intent, timestampNs))
);
}
@@ -374,16 +374,34 @@ public class IorapForwardingService extends SystemService {
}
@Override
- public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity) {
+ public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity,
+ long timestampNs) {
if (DEBUG) {
- Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchFinished(%d, %s)",
- mSequenceId, activity));
+ Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchFinished(%d, %s, %d)",
+ mSequenceId, activity, timestampNs));
+ }
+
+ invokeRemote(mIorapRemote,
+ (IIorap remote) ->
+ remote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+ new AppLaunchEvent.ActivityLaunchFinished(mSequenceId,
+ activity,
+ timestampNs))
+ );
+ }
+
+ @Override
+ public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity,
+ long timestampNs) {
+ if (DEBUG) {
+ Log.v(TAG, String.format("AppLaunchObserver#onReportFullyDrawn(%d, %s, %d)",
+ mSequenceId, activity, timestampNs));
}
invokeRemote(mIorapRemote,
(IIorap remote) ->
remote.onAppLaunchEvent(RequestId.nextValueForSequence(),
- new AppLaunchEvent.ActivityLaunchFinished(mSequenceId, activity))
+ new AppLaunchEvent.ReportFullyDrawn(mSequenceId, activity, timestampNs))
);
}
}
diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/AppLaunchEventTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/AppLaunchEventTest.kt
new file mode 100644
index 000000000000..51e407d4cbff
--- /dev/null
+++ b/startop/iorap/tests/src/com/google/android/startop/iorap/AppLaunchEventTest.kt
@@ -0,0 +1,181 @@
+/*
+ * 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.google.android.startop.iorap
+
+import android.content.Intent;
+import android.net.Uri
+import android.os.Parcel
+import android.os.Parcelable
+import androidx.test.filters.SmallTest
+import com.google.android.startop.iorap.AppLaunchEvent;
+import com.google.android.startop.iorap.AppLaunchEvent.ActivityLaunched
+import com.google.android.startop.iorap.AppLaunchEvent.ActivityLaunchCancelled
+import com.google.android.startop.iorap.AppLaunchEvent.ActivityLaunchFinished
+import com.google.android.startop.iorap.AppLaunchEvent.IntentStarted;
+import com.google.android.startop.iorap.AppLaunchEvent.IntentFailed;
+import com.google.android.startop.iorap.AppLaunchEvent.ReportFullyDrawn
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+
+/**
+ * Basic unit tests to test all of the [AppLaunchEvent]s in [com.google.android.startop.iorap].
+ */
+@SmallTest
+class AppLaunchEventTest {
+ /**
+ * Test for IntentStarted.
+ */
+ @Test
+ fun testIntentStarted() {
+ var intent = Intent()
+ val valid = IntentStarted(/* sequenceId= */2L, intent, /* timestampNs= */ 1L)
+ val copy = IntentStarted(/* sequenceId= */2L, intent, /* timestampNs= */ 1L)
+ val noneCopy1 = IntentStarted(/* sequenceId= */1L, intent, /* timestampNs= */ 1L)
+ val noneCopy2 = IntentStarted(/* sequenceId= */2L, intent, /* timestampNs= */ 2L)
+ val noneCopy3 = IntentStarted(/* sequenceId= */2L, Intent(), /* timestampNs= */ 1L)
+
+ // equals(Object other)
+ assertThat(valid).isEqualTo(copy)
+ assertThat(valid).isNotEqualTo(noneCopy1)
+ assertThat(valid).isNotEqualTo(noneCopy2)
+ assertThat(valid).isNotEqualTo(noneCopy3)
+
+ // test toString()
+ val result = valid.toString()
+ assertThat(result).isEqualTo("IntentStarted{sequenceId=2, intent=Intent { } , timestampNs=1}")
+ }
+
+ /**
+ * Test for IntentFailed.
+ */
+ @Test
+ fun testIntentFailed() {
+ val valid = IntentFailed(/* sequenceId= */2L)
+ val copy = IntentFailed(/* sequenceId= */2L)
+ val noneCopy = IntentFailed(/* sequenceId= */1L)
+
+ // equals(Object other)
+ assertThat(valid).isEqualTo(copy)
+ assertThat(valid).isNotEqualTo(noneCopy)
+
+ // test toString()
+ val result = valid.toString()
+ assertThat(result).isEqualTo("IntentFailed{sequenceId=2}")
+ }
+
+ /**
+ * Test for ActivityLaunched.
+ */
+ @Test
+ fun testActivityLaunched() {
+ //var activityRecord =
+ val valid = ActivityLaunched(/* sequenceId= */2L, "test".toByteArray(),
+ /* temperature= */ 0)
+ val copy = ActivityLaunched(/* sequenceId= */2L, "test".toByteArray(),
+ /* temperature= */ 0)
+ val noneCopy1 = ActivityLaunched(/* sequenceId= */1L, "test".toByteArray(),
+ /* temperature= */ 0)
+ val noneCopy2 = ActivityLaunched(/* sequenceId= */1L, "test".toByteArray(),
+ /* temperature= */ 1)
+ val noneCopy3 = ActivityLaunched(/* sequenceId= */1L, "test1".toByteArray(),
+ /* temperature= */ 0)
+
+ // equals(Object other)
+ assertThat(valid).isEqualTo(copy)
+ assertThat(valid).isNotEqualTo(noneCopy1)
+ assertThat(valid).isNotEqualTo(noneCopy2)
+ assertThat(valid).isNotEqualTo(noneCopy3)
+
+ // test toString()
+ val result = valid.toString()
+ assertThat(result).isEqualTo("ActivityLaunched{sequenceId=2, test, temperature=0}")
+ }
+
+
+ /**
+ * Test for ActivityLaunchFinished.
+ */
+ @Test
+ fun testActivityLaunchFinished() {
+ val valid = ActivityLaunchFinished(/* sequenceId= */2L, "test".toByteArray(),
+ /* timestampNs= */ 1L)
+ val copy = ActivityLaunchFinished(/* sequenceId= */2L, "test".toByteArray(),
+ /* timestampNs= */ 1L)
+ val noneCopy1 = ActivityLaunchFinished(/* sequenceId= */1L, "test".toByteArray(),
+ /* timestampNs= */ 1L)
+ val noneCopy2 = ActivityLaunchFinished(/* sequenceId= */1L, "test".toByteArray(),
+ /* timestampNs= */ 2L)
+ val noneCopy3 = ActivityLaunchFinished(/* sequenceId= */2L, "test1".toByteArray(),
+ /* timestampNs= */ 1L)
+
+ // equals(Object other)
+ assertThat(valid).isEqualTo(copy)
+ assertThat(valid).isNotEqualTo(noneCopy1)
+ assertThat(valid).isNotEqualTo(noneCopy2)
+ assertThat(valid).isNotEqualTo(noneCopy3)
+
+ // test toString()
+ val result = valid.toString()
+ assertThat(result).isEqualTo("ActivityLaunchFinished{sequenceId=2, test, timestampNs=1}")
+ }
+
+ /**
+ * Test for ActivityLaunchCancelled.
+ */
+ @Test
+ fun testActivityLaunchCancelled() {
+ val valid = ActivityLaunchCancelled(/* sequenceId= */2L, "test".toByteArray())
+ val copy = ActivityLaunchCancelled(/* sequenceId= */2L, "test".toByteArray())
+ val noneCopy1 = ActivityLaunchCancelled(/* sequenceId= */1L, "test".toByteArray())
+ val noneCopy2 = ActivityLaunchCancelled(/* sequenceId= */2L, "test1".toByteArray())
+
+ // equals(Object other)
+ assertThat(valid).isEqualTo(copy)
+ assertThat(valid).isNotEqualTo(noneCopy1)
+ assertThat(valid).isNotEqualTo(noneCopy2)
+
+ // test toString()
+ val result = valid.toString()
+ assertThat(result).isEqualTo("ActivityLaunchCancelled{sequenceId=2, test}")
+ }
+
+ /**
+ * Test for ReportFullyDrawn.
+ */
+ @Test
+ fun testReportFullyDrawn() {
+ val valid = ReportFullyDrawn(/* sequenceId= */2L, "test".toByteArray(), /* timestampNs= */ 1L)
+ val copy = ReportFullyDrawn(/* sequenceId= */2L, "test".toByteArray(), /* timestampNs= */ 1L)
+ val noneCopy1 = ReportFullyDrawn(/* sequenceId= */1L, "test".toByteArray(),
+ /* timestampNs= */ 1L)
+ val noneCopy2 = ReportFullyDrawn(/* sequenceId= */1L, "test".toByteArray(),
+ /* timestampNs= */ 1L)
+ val noneCopy3 = ReportFullyDrawn(/* sequenceId= */2L, "test1".toByteArray(),
+ /* timestampNs= */ 1L)
+
+ // equals(Object other)
+ assertThat(valid).isEqualTo(copy)
+ assertThat(valid).isNotEqualTo(noneCopy1)
+ assertThat(valid).isNotEqualTo(noneCopy2)
+ assertThat(valid).isNotEqualTo(noneCopy3)
+
+ // test toString()
+ val result = valid.toString()
+ assertThat(result).isEqualTo("ReportFullyDrawn{sequenceId=2, test, timestampNs=1}")
+ }
+}
diff --git a/startop/scripts/app_startup/app_startup_runner.py b/startop/scripts/app_startup/app_startup_runner.py
index fa1c4e601f83..25ee6f7368c8 100755
--- a/startop/scripts/app_startup/app_startup_runner.py
+++ b/startop/scripts/app_startup/app_startup_runner.py
@@ -233,13 +233,17 @@ def execute_run_using_perfetto_trace(collector_info,
simulate: bool,
inodes_path: str,
timeout: int,
- compiler_type: CompilerType) -> DataFrame:
+ compiler_type: CompilerType,
+ requires_trace_collection: bool) -> DataFrame:
""" Executes run based on perfetto trace. """
- passed, perfetto_trace_file = run_perfetto_collector(collector_info,
- timeout,
- simulate)
- if not passed:
- raise RuntimeError('Cannot run perfetto collector!')
+ if requires_trace_collection:
+ passed, perfetto_trace_file = run_perfetto_collector(collector_info,
+ timeout,
+ simulate)
+ if not passed:
+ raise RuntimeError('Cannot run perfetto collector!')
+ else:
+ perfetto_trace_file = tempfile.NamedTemporaryFile()
with perfetto_trace_file:
for combos in run_combos:
@@ -271,7 +275,8 @@ def execute_run_combos(
simulate: bool,
inodes_path: str,
timeout: int,
- compiler_type: CompilerType):
+ compiler_type: CompilerType,
+ requires_trace_collection: bool):
# nothing will work if the screen isn't unlocked first.
cmd_utils.execute_arbitrary_command([_UNLOCK_SCREEN_SCRIPT],
timeout,
@@ -284,7 +289,8 @@ def execute_run_combos(
simulate,
inodes_path,
timeout,
- compiler_type)
+ compiler_type,
+ requires_trace_collection)
def gather_results(commands: Iterable[Tuple[DataFrame]],
key_list: List[str], value_list: List[Tuple[str, ...]]):
@@ -369,11 +375,13 @@ def main():
CollectorPackageInfo)
print_utils.debug_print_gen("grouped run combinations: ", grouped_combos())
+ requires_trace_collection = any(i in _TRACING_READAHEADS for i in opts.readaheads)
exec = execute_run_combos(grouped_combos(),
opts.simulate,
opts.inodes,
opts.timeout,
- opts.compiler_type)
+ opts.compiler_type,
+ requires_trace_collection)
results = gather_results(exec, _COMBINATORIAL_OPTIONS, combos())
diff --git a/startop/scripts/app_startup/app_startup_runner_test.py b/startop/scripts/app_startup/app_startup_runner_test.py
index 382f6f3c70ff..0c2bbea04f6a 100755
--- a/startop/scripts/app_startup/app_startup_runner_test.py
+++ b/startop/scripts/app_startup/app_startup_runner_test.py
@@ -92,7 +92,7 @@ def default_dict_for_parsed_args(**kwargs):
"""
d = {'compiler_filters': None, 'simulate': False, 'debug': False,
'output': None, 'timeout': 10, 'loop_count': 1, 'inodes': None,
- 'trace_duration': None, 'compiler_type': asr.CompilerType.HOST}
+ 'trace_duration': None, 'compiler_type': asr.CompilerType.DEVICE}
d.update(kwargs)
return d
diff --git a/startop/scripts/iorap/common b/startop/scripts/iorap/common
index 031dabfadeab..387e45d431bd 100755
--- a/startop/scripts/iorap/common
+++ b/startop/scripts/iorap/common
@@ -248,6 +248,6 @@ iorapd_readahead_wait_until_finished() {
local remote_path="$(_iorapd_path_to_data_file "$package" "$activity" "compiled_trace.pb")"
# See 'read_ahead.cc' LOG(INFO).
- local pattern="ReadAhead completed ($remote_path)"
+ local pattern="Description = $remote_path"
logcat_wait_for_pattern "$timeout" "$timestamp" "$pattern"
}
diff --git a/startop/scripts/iorap/compiler_test.py b/startop/scripts/iorap/compiler_test.py
index d1f11c5da2a5..b8de70147565 100644
--- a/startop/scripts/iorap/compiler_test.py
+++ b/startop/scripts/iorap/compiler_test.py
@@ -30,7 +30,7 @@ See also https://docs.pytest.org/en/latest/usage.html
"""
import os
-import compiler_host as compiler
+import compiler_ri as compiler
DIR = os.path.abspath(os.path.dirname(__file__))
TEXTCACHE = os.path.join(DIR, 'test_fixtures/compiler/common_textcache')
diff --git a/startop/scripts/iorap/test_fixtures/compiler/test_result_systrace b/startop/scripts/iorap/test_fixtures/compiler/test_result_systrace
index f731e73ba9b2..59ff7537180b 100644
--- a/startop/scripts/iorap/test_fixtures/compiler/test_result_systrace
+++ b/startop/scripts/iorap/test_fixtures/compiler/test_result_systrace
@@ -10,94 +10,94 @@ TRACE:
# ||| / delay
# TASK-PID TGID CPU# |||| TIMESTAMP FUNCTION
# | | | | |||| | |
- <unknown>-27388 (-----) [004] .... 1920260.530929: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1461937 ofs=9535488
- <unknown>-27388 (-----) [005] .... 1920260.532161: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1344589 ofs=9474048
- <unknown>-27388 (-----) [005] .... 1920260.532183: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1153671 ofs=9478144
- <unknown>-27388 (-----) [005] .... 1920260.532184: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1219563 ofs=9482240
- <unknown>-27388 (-----) [005] .... 1920260.532185: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1083162 ofs=9486336
- <unknown>-27388 (-----) [005] .... 1920260.532185: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1147318 ofs=9490432
- <unknown>-27388 (-----) [005] .... 1920260.532186: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1333594 ofs=9494528
- <unknown>-27388 (-----) [005] .... 1920260.532186: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1375715 ofs=9498624
- <unknown>-27388 (-----) [005] .... 1920260.532186: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1184831 ofs=9502720
- <unknown>-27388 (-----) [005] .... 1920260.532187: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1241653 ofs=9506816
- <unknown>-27388 (-----) [005] .... 1920260.532187: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1134975 ofs=9510912
- <unknown>-27388 (-----) [005] .... 1920260.532190: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1145772 ofs=9515008
- <unknown>-27388 (-----) [005] .... 1920260.532190: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1090457 ofs=9519104
- <unknown>-27388 (-----) [005] .... 1920260.532190: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1137942 ofs=9523200
- <unknown>-27388 (-----) [005] .... 1920260.532191: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1130123 ofs=9527296
- <unknown>-27388 (-----) [005] .... 1920260.532191: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1208783 ofs=9531392
- <unknown>-27388 (-----) [005] .... 1920260.532192: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1294989 ofs=9539584
- <unknown>-27388 (-----) [005] .... 1920260.532206: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1163979 ofs=9543680
- <unknown>-27388 (-----) [005] .... 1920260.532206: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1350628 ofs=9547776
- <unknown>-27388 (-----) [005] .... 1920260.532206: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1386717 ofs=9551872
- <unknown>-27388 (-----) [005] .... 1920260.532207: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1316148 ofs=9555968
- <unknown>-27388 (-----) [005] .... 1920260.532208: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1316419 ofs=9560064
- <unknown>-27388 (-----) [005] .... 1920260.532208: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1149076 ofs=9564160
- <unknown>-27388 (-----) [005] .... 1920260.532209: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1372772 ofs=9568256
- <unknown>-27388 (-----) [005] .... 1920260.532209: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1116389 ofs=9572352
- <unknown>-27388 (-----) [005] .... 1920260.532211: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1325458 ofs=9576448
- <unknown>-27388 (-----) [005] .... 1920260.532211: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1195423 ofs=9580544
- <unknown>-27388 (-----) [005] .... 1920260.532211: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1250964 ofs=9584640
- <unknown>-27388 (-----) [005] .... 1920260.532212: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1196027 ofs=9588736
- <unknown>-27388 (-----) [005] .... 1920260.532212: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1354059 ofs=9592832
- <unknown>-27388 (-----) [005] .... 1920260.532213: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1264649 ofs=9596928
- <unknown>-27388 (-----) [005] .... 1920260.532213: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1245285 ofs=9601024
- <unknown>-27388 (-----) [005] .... 1920260.535119: mm_filemap_add_to_page_cache: dev 0:64768 ino 1416 page=0000000000000000 pfn=1411552 ofs=44244992
+ <unknown>-27388 (-----) [004] .... 1920260.530929: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1461937 ofs=9535488
+ <unknown>-27388 (-----) [005] .... 1920260.532161: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1344589 ofs=9474048
+ <unknown>-27388 (-----) [005] .... 1920260.532183: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1153671 ofs=9478144
+ <unknown>-27388 (-----) [005] .... 1920260.532184: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1219563 ofs=9482240
+ <unknown>-27388 (-----) [005] .... 1920260.532185: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1083162 ofs=9486336
+ <unknown>-27388 (-----) [005] .... 1920260.532185: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1147318 ofs=9490432
+ <unknown>-27388 (-----) [005] .... 1920260.532186: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1333594 ofs=9494528
+ <unknown>-27388 (-----) [005] .... 1920260.532186: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1375715 ofs=9498624
+ <unknown>-27388 (-----) [005] .... 1920260.532186: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1184831 ofs=9502720
+ <unknown>-27388 (-----) [005] .... 1920260.532187: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1241653 ofs=9506816
+ <unknown>-27388 (-----) [005] .... 1920260.532187: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1134975 ofs=9510912
+ <unknown>-27388 (-----) [005] .... 1920260.532190: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1145772 ofs=9515008
+ <unknown>-27388 (-----) [005] .... 1920260.532190: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1090457 ofs=9519104
+ <unknown>-27388 (-----) [005] .... 1920260.532190: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1137942 ofs=9523200
+ <unknown>-27388 (-----) [005] .... 1920260.532191: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1130123 ofs=9527296
+ <unknown>-27388 (-----) [005] .... 1920260.532191: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1208783 ofs=9531392
+ <unknown>-27388 (-----) [005] .... 1920260.532192: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1294989 ofs=9539584
+ <unknown>-27388 (-----) [005] .... 1920260.532206: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1163979 ofs=9543680
+ <unknown>-27388 (-----) [005] .... 1920260.532206: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1350628 ofs=9547776
+ <unknown>-27388 (-----) [005] .... 1920260.532206: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1386717 ofs=9551872
+ <unknown>-27388 (-----) [005] .... 1920260.532207: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1316148 ofs=9555968
+ <unknown>-27388 (-----) [005] .... 1920260.532208: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1316419 ofs=9560064
+ <unknown>-27388 (-----) [005] .... 1920260.532208: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1149076 ofs=9564160
+ <unknown>-27388 (-----) [005] .... 1920260.532209: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1372772 ofs=9568256
+ <unknown>-27388 (-----) [005] .... 1920260.532209: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1116389 ofs=9572352
+ <unknown>-27388 (-----) [005] .... 1920260.532211: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1325458 ofs=9576448
+ <unknown>-27388 (-----) [005] .... 1920260.532211: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1195423 ofs=9580544
+ <unknown>-27388 (-----) [005] .... 1920260.532211: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1250964 ofs=9584640
+ <unknown>-27388 (-----) [005] .... 1920260.532212: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1196027 ofs=9588736
+ <unknown>-27388 (-----) [005] .... 1920260.532212: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1354059 ofs=9592832
+ <unknown>-27388 (-----) [005] .... 1920260.532213: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1264649 ofs=9596928
+ <unknown>-27388 (-----) [005] .... 1920260.532213: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1245285 ofs=9601024
+ <unknown>-27388 (-----) [005] .... 1920260.535119: mm_filemap_add_to_page_cache: dev 0:64768 ino 588 page=0000000000000000 pfn=1411552 ofs=44244992
<unknown>-27388 (-----) [005] .... 1920260.535129: mm_filemap_add_to_page_cache: dev 0:3 ino 0 page=0000000000000000 pfn=1483081 ofs=433524736
<unknown>-27388 (-----) [004] .... 1920260.536144: mm_filemap_add_to_page_cache: dev 0:3 ino 0 page=0000000000000000 pfn=1276173 ofs=438185984
- <unknown>-27388 (-----) [004] .... 1920260.536462: mm_filemap_add_to_page_cache: dev 0:64768 ino 1416 page=0000000000000000 pfn=1174575 ofs=44249088
- <unknown>-27388 (-----) [004] .... 1920260.536464: mm_filemap_add_to_page_cache: dev 0:64768 ino 1416 page=0000000000000000 pfn=1126294 ofs=44253184
- <unknown>-27388 (-----) [004] .... 1920260.536464: mm_filemap_add_to_page_cache: dev 0:64768 ino 1416 page=0000000000000000 pfn=1248232 ofs=44257280
- <unknown>-27388 (-----) [004] .... 1920260.537065: mm_filemap_add_to_page_cache: dev 0:64768 ino 1416 page=0000000000000000 pfn=1332993 ofs=44240896
- <unknown>-27388 (-----) [006] .... 1920260.537646: mm_filemap_add_to_page_cache: dev 0:64768 ino 1416 page=0000000000000000 pfn=1153343 ofs=44400640
- <unknown>-27388 (-----) [005] .... 1920260.538777: mm_filemap_add_to_page_cache: dev 0:64768 ino 1416 page=0000000000000000 pfn=1358397 ofs=44474368
- <unknown>-12683 (-----) [006] .... 1920260.560094: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1426577 ofs=0
+ <unknown>-27388 (-----) [004] .... 1920260.536462: mm_filemap_add_to_page_cache: dev 0:64768 ino 588 page=0000000000000000 pfn=1174575 ofs=44249088
+ <unknown>-27388 (-----) [004] .... 1920260.536464: mm_filemap_add_to_page_cache: dev 0:64768 ino 588 page=0000000000000000 pfn=1126294 ofs=44253184
+ <unknown>-27388 (-----) [004] .... 1920260.536464: mm_filemap_add_to_page_cache: dev 0:64768 ino 588 page=0000000000000000 pfn=1248232 ofs=44257280
+ <unknown>-27388 (-----) [004] .... 1920260.537065: mm_filemap_add_to_page_cache: dev 0:64768 ino 588 page=0000000000000000 pfn=1332993 ofs=44240896
+ <unknown>-27388 (-----) [006] .... 1920260.537646: mm_filemap_add_to_page_cache: dev 0:64768 ino 588 page=0000000000000000 pfn=1153343 ofs=44400640
+ <unknown>-27388 (-----) [005] .... 1920260.538777: mm_filemap_add_to_page_cache: dev 0:64768 ino 588 page=0000000000000000 pfn=1358397 ofs=44474368
+ <unknown>-12683 (-----) [006] .... 1920260.560094: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1426577 ofs=0
<unknown>-12683 (-----) [006] .... 1920260.560105: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1117587 ofs=1171456
- <unknown>-12683 (-----) [006] .... 1920260.561199: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1099987 ofs=4096
- <unknown>-12683 (-----) [006] .... 1920260.561411: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1099910 ofs=16384
- <unknown>-12683 (-----) [006] .... 1920260.561598: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1099905 ofs=20480
- <unknown>-12683 (-----) [006] .... 1920260.561758: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1099883 ofs=32768
- <unknown>-12683 (-----) [006] .... 1920260.562088: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1099809 ofs=36864
- <unknown>-12683 (-----) [006] .... 1920260.562325: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1099803 ofs=98304
- <unknown>-12683 (-----) [006] .... 1920260.562516: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1099795 ofs=102400
- <unknown>-12683 (-----) [006] .... 1920260.563094: mm_filemap_add_to_page_cache: dev 0:64768 ino 1523 page=0000000000000000 pfn=1107649 ofs=12288
- <unknown>-12683 (-----) [006] .... 1920260.563105: mm_filemap_add_to_page_cache: dev 0:64768 ino 1523 page=0000000000000000 pfn=1269029 ofs=16384
- <unknown>-12683 (-----) [006] .... 1920260.563785: mm_filemap_add_to_page_cache: dev 0:64768 ino 1242 page=0000000000000000 pfn=1451096 ofs=8192
- <unknown>-12683 (-----) [006] .... 1920260.563790: mm_filemap_add_to_page_cache: dev 0:64768 ino 1242 page=0000000000000000 pfn=1301480 ofs=12288
- <unknown>-12683 (-----) [006] .... 1920260.563790: mm_filemap_add_to_page_cache: dev 0:64768 ino 1242 page=0000000000000000 pfn=1314353 ofs=16384
- <unknown>-12683 (-----) [006] .... 1920260.563791: mm_filemap_add_to_page_cache: dev 0:64768 ino 1242 page=0000000000000000 pfn=1216744 ofs=24576
- <unknown>-12683 (-----) [006] .... 1920260.564309: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1099787 ofs=49152
- <unknown>-12683 (-----) [006] .... 1920260.564514: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1099778 ofs=53248
- <unknown>-12683 (-----) [005] .... 1920260.564756: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1148849 ofs=114688
- <unknown>-12683 (-----) [005] .... 1920260.564973: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1164731 ofs=118784
- <unknown>-12683 (-----) [005] .... 1920260.565000: mm_filemap_add_to_page_cache: dev 0:2053 ino 26 page=0000000000000000 pfn=1170255 ofs=0
- <unknown>-12683 (-----) [005] .... 1920260.565003: mm_filemap_add_to_page_cache: dev 0:2053 ino 26 page=0000000000000000 pfn=1181043 ofs=4096
- <unknown>-12683 (-----) [005] .... 1920260.565004: mm_filemap_add_to_page_cache: dev 0:2053 ino 26 page=0000000000000000 pfn=1296004 ofs=8192
- <unknown>-12683 (-----) [005] .... 1920260.565004: mm_filemap_add_to_page_cache: dev 0:2053 ino 26 page=0000000000000000 pfn=1102004 ofs=12288
+ <unknown>-12683 (-----) [006] .... 1920260.561199: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1099987 ofs=4096
+ <unknown>-12683 (-----) [006] .... 1920260.561411: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1099910 ofs=16384
+ <unknown>-12683 (-----) [006] .... 1920260.561598: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1099905 ofs=20480
+ <unknown>-12683 (-----) [006] .... 1920260.561758: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1099883 ofs=32768
+ <unknown>-12683 (-----) [006] .... 1920260.562088: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1099809 ofs=36864
+ <unknown>-12683 (-----) [006] .... 1920260.562325: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1099803 ofs=98304
+ <unknown>-12683 (-----) [006] .... 1920260.562516: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1099795 ofs=102400
+ <unknown>-12683 (-----) [006] .... 1920260.563094: mm_filemap_add_to_page_cache: dev 0:64768 ino 5f3 page=0000000000000000 pfn=1107649 ofs=12288
+ <unknown>-12683 (-----) [006] .... 1920260.563105: mm_filemap_add_to_page_cache: dev 0:64768 ino 5f3 page=0000000000000000 pfn=1269029 ofs=16384
+ <unknown>-12683 (-----) [006] .... 1920260.563785: mm_filemap_add_to_page_cache: dev 0:64768 ino 4da page=0000000000000000 pfn=1451096 ofs=8192
+ <unknown>-12683 (-----) [006] .... 1920260.563790: mm_filemap_add_to_page_cache: dev 0:64768 ino 4da page=0000000000000000 pfn=1301480 ofs=12288
+ <unknown>-12683 (-----) [006] .... 1920260.563790: mm_filemap_add_to_page_cache: dev 0:64768 ino 4da page=0000000000000000 pfn=1314353 ofs=16384
+ <unknown>-12683 (-----) [006] .... 1920260.563791: mm_filemap_add_to_page_cache: dev 0:64768 ino 4da page=0000000000000000 pfn=1216744 ofs=24576
+ <unknown>-12683 (-----) [006] .... 1920260.564309: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1099787 ofs=49152
+ <unknown>-12683 (-----) [006] .... 1920260.564514: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1099778 ofs=53248
+ <unknown>-12683 (-----) [005] .... 1920260.564756: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1148849 ofs=114688
+ <unknown>-12683 (-----) [005] .... 1920260.564973: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1164731 ofs=118784
+ <unknown>-12683 (-----) [005] .... 1920260.565000: mm_filemap_add_to_page_cache: dev 0:2053 ino 1a page=0000000000000000 pfn=1170255 ofs=0
+ <unknown>-12683 (-----) [005] .... 1920260.565003: mm_filemap_add_to_page_cache: dev 0:2053 ino 1a page=0000000000000000 pfn=1181043 ofs=4096
+ <unknown>-12683 (-----) [005] .... 1920260.565004: mm_filemap_add_to_page_cache: dev 0:2053 ino 1a page=0000000000000000 pfn=1296004 ofs=8192
+ <unknown>-12683 (-----) [005] .... 1920260.565004: mm_filemap_add_to_page_cache: dev 0:2053 ino 1a page=0000000000000000 pfn=1102004 ofs=12288
<unknown>-12683 (-----) [005] .... 1920260.565626: mm_filemap_add_to_page_cache: dev 0:3 ino 0 page=0000000000000000 pfn=1351232 ofs=470597632
<unknown>-12683 (-----) [005] .... 1920260.565982: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1391336 ofs=40210432
<unknown>-12683 (-----) [005] .... 1920260.565985: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1267536 ofs=12668928
- <unknown>-27388 (-----) [007] .... 1920260.566082: mm_filemap_add_to_page_cache: dev 0:64768 ino 1416 page=0000000000000000 pfn=1256752 ofs=43921408
+ <unknown>-27388 (-----) [007] .... 1920260.566082: mm_filemap_add_to_page_cache: dev 0:64768 ino 588 page=0000000000000000 pfn=1256752 ofs=43921408
<unknown>-12683 (-----) [005] .... 1920260.566516: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1110966 ofs=176226304
<unknown>-12683 (-----) [005] .... 1920260.566519: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1060586 ofs=12967936
<unknown>-12683 (-----) [004] .... 1920260.567773: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1117234 ofs=421888
<unknown>-12683 (-----) [005] .... 1920260.568604: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1210571 ofs=430080
- <unknown>-12683 (-----) [005] .... 1920260.568887: mm_filemap_add_to_page_cache: dev 0:64771 ino 105 page=0000000000000000 pfn=1055640 ofs=0
- <unknown>-12683 (-----) [005] .... 1920260.568908: mm_filemap_add_to_page_cache: dev 0:64771 ino 73 page=0000000000000000 pfn=1142694 ofs=0
+ <unknown>-12683 (-----) [005] .... 1920260.568887: mm_filemap_add_to_page_cache: dev 0:64771 ino 69 page=0000000000000000 pfn=1055640 ofs=0
+ <unknown>-12683 (-----) [005] .... 1920260.568908: mm_filemap_add_to_page_cache: dev 0:64771 ino 49 page=0000000000000000 pfn=1142694 ofs=0
<unknown>-12683 (-----) [005] .... 1920260.568910: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1060788 ofs=299008
- <unknown>-12683 (-----) [005] .... 1920260.569418: mm_filemap_add_to_page_cache: dev 0:64771 ino 73 page=0000000000000000 pfn=1085046 ofs=4096
- <unknown>-12683 (-----) [005] .... 1920260.569640: mm_filemap_add_to_page_cache: dev 0:64771 ino 73 page=0000000000000000 pfn=1057135 ofs=8192
+ <unknown>-12683 (-----) [005] .... 1920260.569418: mm_filemap_add_to_page_cache: dev 0:64771 ino 49 page=0000000000000000 pfn=1085046 ofs=4096
+ <unknown>-12683 (-----) [005] .... 1920260.569640: mm_filemap_add_to_page_cache: dev 0:64771 ino 49 page=0000000000000000 pfn=1057135 ofs=8192
<unknown>-12683 (-----) [005] .... 1920260.569833: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1058976 ofs=19406848
<unknown>-12683 (-----) [005] .... 1920260.569835: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1477947 ofs=10526720
- <unknown>-12683 (-----) [005] .... 1920260.572285: mm_filemap_add_to_page_cache: dev 0:64768 ino 1565 page=0000000000000000 pfn=1237492 ofs=299008
- <unknown>-12683 (-----) [005] .... 1920260.572297: mm_filemap_add_to_page_cache: dev 0:64768 ino 1565 page=0000000000000000 pfn=1264914 ofs=339968
- <unknown>-12683 (-----) [005] .... 1920260.572314: mm_filemap_add_to_page_cache: dev 0:64768 ino 1565 page=0000000000000000 pfn=1434748 ofs=348160
- <unknown>-12683 (-----) [005] .... 1920260.572316: mm_filemap_add_to_page_cache: dev 0:64768 ino 1565 page=0000000000000000 pfn=1372959 ofs=352256
- <unknown>-12683 (-----) [005] .... 1920260.572317: mm_filemap_add_to_page_cache: dev 0:64768 ino 1565 page=0000000000000000 pfn=1258955 ofs=356352
- <unknown>-12683 (-----) [005] .... 1920260.572317: mm_filemap_add_to_page_cache: dev 0:64768 ino 1565 page=0000000000000000 pfn=1113420 ofs=360448
- <unknown>-12683 (-----) [005] .... 1920260.572318: mm_filemap_add_to_page_cache: dev 0:64768 ino 1565 page=0000000000000000 pfn=1137083 ofs=364544
- <unknown>-12683 (-----) [004] .... 1920260.575490: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1379679 ofs=65536
- <unknown>-12683 (-----) [006] .... 1920260.576194: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1323898 ofs=69632
+ <unknown>-12683 (-----) [005] .... 1920260.572285: mm_filemap_add_to_page_cache: dev 0:64768 ino 61d page=0000000000000000 pfn=1237492 ofs=299008
+ <unknown>-12683 (-----) [005] .... 1920260.572297: mm_filemap_add_to_page_cache: dev 0:64768 ino 61d page=0000000000000000 pfn=1264914 ofs=339968
+ <unknown>-12683 (-----) [005] .... 1920260.572314: mm_filemap_add_to_page_cache: dev 0:64768 ino 61d page=0000000000000000 pfn=1434748 ofs=348160
+ <unknown>-12683 (-----) [005] .... 1920260.572316: mm_filemap_add_to_page_cache: dev 0:64768 ino 61d page=0000000000000000 pfn=1372959 ofs=352256
+ <unknown>-12683 (-----) [005] .... 1920260.572317: mm_filemap_add_to_page_cache: dev 0:64768 ino 61d page=0000000000000000 pfn=1258955 ofs=356352
+ <unknown>-12683 (-----) [005] .... 1920260.572317: mm_filemap_add_to_page_cache: dev 0:64768 ino 61d page=0000000000000000 pfn=1113420 ofs=360448
+ <unknown>-12683 (-----) [005] .... 1920260.572318: mm_filemap_add_to_page_cache: dev 0:64768 ino 61d page=0000000000000000 pfn=1137083 ofs=364544
+ <unknown>-12683 (-----) [004] .... 1920260.575490: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1379679 ofs=65536
+ <unknown>-12683 (-----) [006] .... 1920260.576194: mm_filemap_add_to_page_cache: dev 0:64771 ino 11e page=0000000000000000 pfn=1323898 ofs=69632
<unknown>-12683 (-----) [006] .... 1920260.576248: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1323895 ofs=262623232
<unknown>-12683 (-----) [006] .... 1920260.576251: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1323861 ofs=13156352
<unknown>-12683 (-----) [005] .... 1920260.576810: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1477585 ofs=262590464
@@ -105,644 +105,644 @@ TRACE:
<unknown>-12683 (-----) [004] .... 1920260.577200: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1267618 ofs=12636160
<unknown>-12683 (-----) [005] .... 1920260.577725: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1056225 ofs=228618240
<unknown>-12683 (-----) [005] .... 1920260.577727: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1164942 ofs=13082624
- <unknown>-12683 (-----) [007] .... 1920260.578411: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1372616 ofs=0
- <unknown>-12683 (-----) [007] .... 1920260.578422: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1307468 ofs=4096
- <unknown>-12683 (-----) [007] .... 1920260.578428: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1120117 ofs=8192
- <unknown>-12683 (-----) [007] .... 1920260.578428: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1217989 ofs=12288
- <unknown>-12683 (-----) [007] .... 1920260.578650: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1475011 ofs=5419008
+ <unknown>-12683 (-----) [007] .... 1920260.578411: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1372616 ofs=0
+ <unknown>-12683 (-----) [007] .... 1920260.578422: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1307468 ofs=4096
+ <unknown>-12683 (-----) [007] .... 1920260.578428: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1120117 ofs=8192
+ <unknown>-12683 (-----) [007] .... 1920260.578428: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1217989 ofs=12288
+ <unknown>-12683 (-----) [007] .... 1920260.578650: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1475011 ofs=5419008
<unknown>-12683 (-----) [007] .... 1920260.578653: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1066084 ofs=236453888
<unknown>-12683 (-----) [007] .... 1920260.578654: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1100271 ofs=13099008
- <unknown>-12683 (-----) [004] .... 1920260.579004: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1485156 ofs=5423104
- <unknown>-12683 (-----) [004] .... 1920260.579005: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1124212 ofs=5427200
- <unknown>-12683 (-----) [004] .... 1920260.579006: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1195377 ofs=5431296
- <unknown>-12683 (-----) [004] .... 1920260.579006: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1265888 ofs=5435392
- <unknown>-12683 (-----) [004] .... 1920260.579007: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1170194 ofs=5439488
- <unknown>-12683 (-----) [004] .... 1920260.579007: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1403742 ofs=5443584
- <unknown>-12683 (-----) [004] .... 1920260.579008: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1123826 ofs=5447680
- <unknown>-12683 (-----) [004] .... 1920260.579008: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1255034 ofs=5451776
- <unknown>-12683 (-----) [004] .... 1920260.579011: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1190447 ofs=5455872
- <unknown>-12683 (-----) [004] .... 1920260.579011: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1286864 ofs=5459968
- <unknown>-12683 (-----) [004] .... 1920260.579012: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1428535 ofs=5464064
- <unknown>-12683 (-----) [004] .... 1920260.579012: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1184092 ofs=5468160
- <unknown>-12683 (-----) [004] .... 1920260.579013: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1411906 ofs=5472256
- <unknown>-12683 (-----) [004] .... 1920260.579013: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1342349 ofs=5476352
- <unknown>-12683 (-----) [004] .... 1920260.579013: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1188185 ofs=5480448
- <unknown>-12683 (-----) [004] .... 1920260.579014: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1158702 ofs=5484544
- <unknown>-12683 (-----) [005] .... 1920260.579430: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1299421 ofs=5230592
- <unknown>-12683 (-----) [005] .... 1920260.579435: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1317097 ofs=5234688
- <unknown>-12683 (-----) [005] .... 1920260.579435: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1441714 ofs=5238784
- <unknown>-12683 (-----) [005] .... 1920260.579438: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1081974 ofs=5242880
- <unknown>-12683 (-----) [005] .... 1920260.579439: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1128684 ofs=5246976
- <unknown>-12683 (-----) [005] .... 1920260.579439: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1447381 ofs=5251072
- <unknown>-12683 (-----) [005] .... 1920260.579440: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1466410 ofs=5255168
- <unknown>-12683 (-----) [005] .... 1920260.579440: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1259909 ofs=5259264
- <unknown>-12683 (-----) [005] .... 1920260.579441: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1125784 ofs=5263360
- <unknown>-12683 (-----) [005] .... 1920260.579441: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1270592 ofs=5267456
- <unknown>-12683 (-----) [005] .... 1920260.579442: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1246070 ofs=5271552
- <unknown>-12683 (-----) [005] .... 1920260.579442: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1472544 ofs=5275648
- <unknown>-12683 (-----) [005] .... 1920260.579442: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1113357 ofs=5279744
- <unknown>-12683 (-----) [005] .... 1920260.579443: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1202021 ofs=5283840
- <unknown>-12683 (-----) [005] .... 1920260.579443: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1078639 ofs=5287936
- <unknown>-12683 (-----) [005] .... 1920260.579449: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1176171 ofs=5292032
- <unknown>-12683 (-----) [005] .... 1920260.579450: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1089516 ofs=5296128
- <unknown>-12683 (-----) [005] .... 1920260.579451: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1400065 ofs=5300224
- <unknown>-12683 (-----) [005] .... 1920260.579452: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1300489 ofs=5304320
- <unknown>-12683 (-----) [005] .... 1920260.579452: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1452081 ofs=5308416
- <unknown>-12683 (-----) [005] .... 1920260.579452: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1161862 ofs=5312512
- <unknown>-12683 (-----) [005] .... 1920260.579453: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1161871 ofs=5316608
- <unknown>-12683 (-----) [005] .... 1920260.579453: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1263798 ofs=5320704
- <unknown>-12683 (-----) [005] .... 1920260.579454: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1126887 ofs=5324800
- <unknown>-12683 (-----) [005] .... 1920260.579454: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1375498 ofs=5328896
- <unknown>-12683 (-----) [005] .... 1920260.579455: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1328067 ofs=5332992
- <unknown>-12683 (-----) [005] .... 1920260.579455: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1420691 ofs=5337088
- <unknown>-12683 (-----) [005] .... 1920260.579456: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1298707 ofs=5341184
- <unknown>-12683 (-----) [005] .... 1920260.579456: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1078670 ofs=5345280
- <unknown>-12683 (-----) [005] .... 1920260.579457: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1430498 ofs=5349376
- <unknown>-12683 (-----) [005] .... 1920260.579458: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1338720 ofs=5353472
- <unknown>-12683 (-----) [005] .... 1920260.579476: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1452611 ofs=5357568
- <unknown>-12683 (-----) [006] .... 1920260.580451: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1241967 ofs=0
- <unknown>-12683 (-----) [006] .... 1920260.580454: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1116541 ofs=4096
- <unknown>-12683 (-----) [006] .... 1920260.580461: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1145049 ofs=8192
- <unknown>-12683 (-----) [006] .... 1920260.580462: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1277255 ofs=12288
- <unknown>-12683 (-----) [006] .... 1920260.580462: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1098037 ofs=16384
- <unknown>-12683 (-----) [006] .... 1920260.580463: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1135986 ofs=20480
- <unknown>-12683 (-----) [006] .... 1920260.580464: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1154455 ofs=24576
- <unknown>-12683 (-----) [006] .... 1920260.580464: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1221822 ofs=28672
- <unknown>-12683 (-----) [006] .... 1920260.580465: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1078684 ofs=32768
- <unknown>-12683 (-----) [006] .... 1920260.580465: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1158876 ofs=36864
- <unknown>-12683 (-----) [006] .... 1920260.580465: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1289644 ofs=40960
- <unknown>-12683 (-----) [006] .... 1920260.580466: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1289386 ofs=45056
- <unknown>-12683 (-----) [006] .... 1920260.580466: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1131002 ofs=49152
- <unknown>-12683 (-----) [006] .... 1920260.580467: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1464335 ofs=53248
- <unknown>-12683 (-----) [006] .... 1920260.580468: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1135789 ofs=57344
- <unknown>-12683 (-----) [006] .... 1920260.580469: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1240897 ofs=61440
- <unknown>-12683 (-----) [006] .... 1920260.580469: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1241770 ofs=65536
- <unknown>-12683 (-----) [006] .... 1920260.580470: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1421959 ofs=69632
- <unknown>-12683 (-----) [006] .... 1920260.580470: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1230007 ofs=73728
- <unknown>-12683 (-----) [006] .... 1920260.580471: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1109271 ofs=77824
- <unknown>-12683 (-----) [006] .... 1920260.580471: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1159974 ofs=81920
- <unknown>-12683 (-----) [006] .... 1920260.580471: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1154528 ofs=86016
- <unknown>-12683 (-----) [006] .... 1920260.580472: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1315790 ofs=90112
- <unknown>-12683 (-----) [006] .... 1920260.580473: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1185583 ofs=94208
- <unknown>-12683 (-----) [006] .... 1920260.580473: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1253153 ofs=98304
- <unknown>-12683 (-----) [006] .... 1920260.580473: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1103982 ofs=102400
- <unknown>-12683 (-----) [006] .... 1920260.580474: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1284589 ofs=106496
- <unknown>-12683 (-----) [006] .... 1920260.580474: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1169601 ofs=110592
- <unknown>-12683 (-----) [006] .... 1920260.580476: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1206248 ofs=114688
- <unknown>-12683 (-----) [006] .... 1920260.580476: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1261161 ofs=118784
- <unknown>-12683 (-----) [006] .... 1920260.580477: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1305841 ofs=122880
- <unknown>-12683 (-----) [006] .... 1920260.580477: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1468293 ofs=126976
- <unknown>-12683 (-----) [004] .... 1920260.580646: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1318816 ofs=16384
- <unknown>-12683 (-----) [004] .... 1920260.580649: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1472922 ofs=20480
- <unknown>-12683 (-----) [004] .... 1920260.580650: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1473229 ofs=24576
- <unknown>-12683 (-----) [004] .... 1920260.580650: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1524262 ofs=28672
- <unknown>-12683 (-----) [004] .... 1920260.580656: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1205714 ofs=32768
- <unknown>-12683 (-----) [004] .... 1920260.580657: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1310560 ofs=36864
- <unknown>-12683 (-----) [004] .... 1920260.580658: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1295070 ofs=40960
- <unknown>-12683 (-----) [004] .... 1920260.580659: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1404093 ofs=45056
- <unknown>-12683 (-----) [004] .... 1920260.580659: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1435814 ofs=49152
- <unknown>-12683 (-----) [004] .... 1920260.580660: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1435442 ofs=53248
- <unknown>-12683 (-----) [004] .... 1920260.580660: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1096077 ofs=57344
- <unknown>-12683 (-----) [004] .... 1920260.580661: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1483793 ofs=61440
- <unknown>-12683 (-----) [004] .... 1920260.580661: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1231298 ofs=65536
- <unknown>-12683 (-----) [004] .... 1920260.580661: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1215648 ofs=69632
- <unknown>-12683 (-----) [004] .... 1920260.580662: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1327326 ofs=73728
- <unknown>-12683 (-----) [004] .... 1920260.580662: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1108894 ofs=77824
- <unknown>-12683 (-----) [004] .... 1920260.580663: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1327545 ofs=81920
- <unknown>-12683 (-----) [004] .... 1920260.580663: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1328804 ofs=86016
- <unknown>-12683 (-----) [004] .... 1920260.580664: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1300171 ofs=90112
- <unknown>-12683 (-----) [004] .... 1920260.580664: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1353250 ofs=94208
- <unknown>-12683 (-----) [004] .... 1920260.580668: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1333681 ofs=98304
- <unknown>-12683 (-----) [004] .... 1920260.580668: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1144969 ofs=102400
- <unknown>-12683 (-----) [004] .... 1920260.580669: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1450962 ofs=106496
- <unknown>-12683 (-----) [004] .... 1920260.580669: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1255701 ofs=110592
- <unknown>-12683 (-----) [004] .... 1920260.580670: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1294782 ofs=114688
- <unknown>-12683 (-----) [004] .... 1920260.580670: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1226912 ofs=118784
- <unknown>-12683 (-----) [004] .... 1920260.580671: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1294579 ofs=122880
- <unknown>-12683 (-----) [004] .... 1920260.580671: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1246960 ofs=126976
- <unknown>-12683 (-----) [004] .... 1920260.580671: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1199086 ofs=131072
- <unknown>-12683 (-----) [004] .... 1920260.580672: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1449590 ofs=135168
- <unknown>-12683 (-----) [004] .... 1920260.580672: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1276363 ofs=139264
- <unknown>-12683 (-----) [004] .... 1920260.580675: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1389998 ofs=143360
- <unknown>-12683 (-----) [004] .... 1920260.580739: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1423031 ofs=1249280
- <unknown>-12683 (-----) [004] .... 1920260.580741: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1171032 ofs=1253376
- <unknown>-12683 (-----) [004] .... 1920260.580742: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1320946 ofs=1257472
- <unknown>-12683 (-----) [004] .... 1920260.580743: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1314696 ofs=1261568
- <unknown>-12683 (-----) [004] .... 1920260.580743: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1414864 ofs=1265664
- <unknown>-12683 (-----) [004] .... 1920260.580744: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1334933 ofs=1269760
- <unknown>-12683 (-----) [004] .... 1920260.580744: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1242845 ofs=1273856
- <unknown>-12683 (-----) [004] .... 1920260.580747: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1289488 ofs=1277952
- <unknown>-12683 (-----) [004] .... 1920260.580748: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1335445 ofs=1282048
- <unknown>-12683 (-----) [004] .... 1920260.580748: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1289663 ofs=1286144
- <unknown>-12683 (-----) [004] .... 1920260.580749: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1080462 ofs=1290240
- <unknown>-12683 (-----) [004] .... 1920260.580749: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1286303 ofs=1294336
- <unknown>-12683 (-----) [004] .... 1920260.580750: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1353531 ofs=1298432
- <unknown>-12683 (-----) [004] .... 1920260.580750: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1280701 ofs=1302528
- <unknown>-12683 (-----) [004] .... 1920260.580751: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1107730 ofs=1306624
- <unknown>-12683 (-----) [004] .... 1920260.580752: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1242729 ofs=1310720
- <unknown>-12683 (-----) [004] .... 1920260.580753: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1078336 ofs=1314816
- <unknown>-12683 (-----) [004] .... 1920260.580753: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1372425 ofs=1318912
- <unknown>-12683 (-----) [004] .... 1920260.580754: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1248813 ofs=1323008
- <unknown>-12683 (-----) [004] .... 1920260.580754: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1201155 ofs=1327104
- <unknown>-12683 (-----) [004] .... 1920260.580755: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1250103 ofs=1331200
- <unknown>-12683 (-----) [004] .... 1920260.580755: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1359710 ofs=1335296
- <unknown>-12683 (-----) [004] .... 1920260.580756: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1272462 ofs=1339392
- <unknown>-12683 (-----) [004] .... 1920260.580758: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1097035 ofs=1343488
- <unknown>-12683 (-----) [004] .... 1920260.580759: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1233124 ofs=1347584
- <unknown>-12683 (-----) [004] .... 1920260.580759: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1455812 ofs=1351680
- <unknown>-12683 (-----) [004] .... 1920260.580759: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1355689 ofs=1355776
- <unknown>-12683 (-----) [004] .... 1920260.580760: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1263593 ofs=1359872
- <unknown>-12683 (-----) [004] .... 1920260.580760: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1230789 ofs=1363968
- <unknown>-12683 (-----) [004] .... 1920260.580761: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1143766 ofs=1368064
- <unknown>-12683 (-----) [004] .... 1920260.580762: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1269666 ofs=1372160
- <unknown>-12683 (-----) [004] .... 1920260.580762: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1353022 ofs=1376256
- <unknown>-12683 (-----) [004] .... 1920260.581613: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1355509 ofs=258048
- <unknown>-12683 (-----) [004] .... 1920260.581615: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1178902 ofs=262144
- <unknown>-12683 (-----) [004] .... 1920260.581616: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1193649 ofs=266240
- <unknown>-12683 (-----) [004] .... 1920260.581618: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1225497 ofs=270336
- <unknown>-12683 (-----) [004] .... 1920260.581618: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1228259 ofs=274432
- <unknown>-12683 (-----) [004] .... 1920260.581635: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1309674 ofs=278528
- <unknown>-12683 (-----) [004] .... 1920260.581635: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1239390 ofs=282624
- <unknown>-12683 (-----) [004] .... 1920260.581636: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1468083 ofs=286720
- <unknown>-12683 (-----) [004] .... 1920260.581636: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1292751 ofs=290816
- <unknown>-12683 (-----) [004] .... 1920260.581637: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1318066 ofs=294912
- <unknown>-12683 (-----) [004] .... 1920260.581637: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1489314 ofs=299008
- <unknown>-12683 (-----) [004] .... 1920260.581637: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1169867 ofs=303104
- <unknown>-12683 (-----) [004] .... 1920260.581639: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1314256 ofs=307200
- <unknown>-12683 (-----) [004] .... 1920260.581639: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1310230 ofs=311296
- <unknown>-12683 (-----) [004] .... 1920260.581640: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1356180 ofs=315392
- <unknown>-12683 (-----) [004] .... 1920260.581640: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1419179 ofs=319488
- <unknown>-12683 (-----) [004] .... 1920260.581641: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1307265 ofs=323584
- <unknown>-12683 (-----) [004] .... 1920260.581641: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1218590 ofs=327680
- <unknown>-12683 (-----) [004] .... 1920260.581642: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1447586 ofs=331776
- <unknown>-12683 (-----) [004] .... 1920260.581642: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1209382 ofs=335872
- <unknown>-12683 (-----) [004] .... 1920260.581642: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1072148 ofs=339968
- <unknown>-12683 (-----) [004] .... 1920260.581645: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1227195 ofs=344064
- <unknown>-12683 (-----) [004] .... 1920260.581646: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1246369 ofs=348160
- <unknown>-12683 (-----) [004] .... 1920260.581646: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1193845 ofs=352256
- <unknown>-12683 (-----) [004] .... 1920260.581647: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1137553 ofs=356352
- <unknown>-12683 (-----) [004] .... 1920260.581647: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1475215 ofs=360448
- <unknown>-12683 (-----) [004] .... 1920260.581648: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1258935 ofs=364544
- <unknown>-12683 (-----) [004] .... 1920260.581649: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1448788 ofs=368640
- <unknown>-12683 (-----) [004] .... 1920260.581649: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1447611 ofs=372736
- <unknown>-12683 (-----) [004] .... 1920260.581650: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1290842 ofs=376832
- <unknown>-12683 (-----) [004] .... 1920260.581650: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1447826 ofs=380928
- <unknown>-12683 (-----) [004] .... 1920260.581650: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1181016 ofs=385024
- <unknown>-12683 (-----) [005] .... 1920260.582230: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1216810 ofs=1662976
- <unknown>-12683 (-----) [005] .... 1920260.582234: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1175966 ofs=1667072
- <unknown>-12683 (-----) [005] .... 1920260.582235: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1449798 ofs=1671168
- <unknown>-12683 (-----) [005] .... 1920260.582236: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1273480 ofs=1675264
- <unknown>-12683 (-----) [005] .... 1920260.582236: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1152779 ofs=1679360
- <unknown>-12683 (-----) [005] .... 1920260.582237: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1272810 ofs=1683456
- <unknown>-12683 (-----) [005] .... 1920260.582237: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1248634 ofs=1687552
- <unknown>-12683 (-----) [005] .... 1920260.582237: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1203376 ofs=1691648
- <unknown>-12683 (-----) [005] .... 1920260.582238: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1138880 ofs=1695744
- <unknown>-12683 (-----) [005] .... 1920260.582238: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1344591 ofs=1699840
- <unknown>-12683 (-----) [005] .... 1920260.582239: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1416060 ofs=1703936
- <unknown>-12683 (-----) [005] .... 1920260.582246: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1128676 ofs=1708032
- <unknown>-12683 (-----) [005] .... 1920260.582247: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1301921 ofs=1712128
- <unknown>-12683 (-----) [005] .... 1920260.582248: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1384569 ofs=1716224
- <unknown>-12683 (-----) [005] .... 1920260.582248: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1249106 ofs=1720320
- <unknown>-12683 (-----) [005] .... 1920260.582249: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1206596 ofs=1724416
- <unknown>-12683 (-----) [005] .... 1920260.582249: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1429831 ofs=1728512
- <unknown>-12683 (-----) [005] .... 1920260.582252: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1107796 ofs=1732608
- <unknown>-12683 (-----) [005] .... 1920260.582255: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1098336 ofs=1736704
- <unknown>-12683 (-----) [005] .... 1920260.582255: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1230286 ofs=1740800
- <unknown>-12683 (-----) [005] .... 1920260.582256: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1100370 ofs=1744896
- <unknown>-12683 (-----) [005] .... 1920260.582256: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1241930 ofs=1748992
- <unknown>-12683 (-----) [005] .... 1920260.582257: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1366807 ofs=1753088
- <unknown>-12683 (-----) [005] .... 1920260.582257: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1136252 ofs=1757184
- <unknown>-12683 (-----) [005] .... 1920260.582258: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1274291 ofs=1761280
- <unknown>-12683 (-----) [005] .... 1920260.582258: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1254775 ofs=1765376
- <unknown>-12683 (-----) [005] .... 1920260.582259: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1194679 ofs=1769472
- <unknown>-12683 (-----) [005] .... 1920260.582262: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1177090 ofs=1773568
- <unknown>-12683 (-----) [005] .... 1920260.582263: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1343925 ofs=1777664
- <unknown>-12683 (-----) [005] .... 1920260.582263: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1159217 ofs=1781760
- <unknown>-12683 (-----) [005] .... 1920260.582263: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1435471 ofs=1785856
- <unknown>-12683 (-----) [005] .... 1920260.582264: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1435529 ofs=1789952
- <unknown>-12683 (-----) [004] .... 1920260.582524: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1181910 ofs=0
- <unknown>-12683 (-----) [004] .... 1920260.582528: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1212021 ofs=4096
- <unknown>-12683 (-----) [004] .... 1920260.582529: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1162778 ofs=8192
- <unknown>-12683 (-----) [004] .... 1920260.582529: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1107700 ofs=12288
- <unknown>-12683 (-----) [004] .... 1920260.583553: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1093394 ofs=3399680
+ <unknown>-12683 (-----) [004] .... 1920260.579004: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1485156 ofs=5423104
+ <unknown>-12683 (-----) [004] .... 1920260.579005: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1124212 ofs=5427200
+ <unknown>-12683 (-----) [004] .... 1920260.579006: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1195377 ofs=5431296
+ <unknown>-12683 (-----) [004] .... 1920260.579006: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1265888 ofs=5435392
+ <unknown>-12683 (-----) [004] .... 1920260.579007: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1170194 ofs=5439488
+ <unknown>-12683 (-----) [004] .... 1920260.579007: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1403742 ofs=5443584
+ <unknown>-12683 (-----) [004] .... 1920260.579008: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1123826 ofs=5447680
+ <unknown>-12683 (-----) [004] .... 1920260.579008: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1255034 ofs=5451776
+ <unknown>-12683 (-----) [004] .... 1920260.579011: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1190447 ofs=5455872
+ <unknown>-12683 (-----) [004] .... 1920260.579011: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1286864 ofs=5459968
+ <unknown>-12683 (-----) [004] .... 1920260.579012: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1428535 ofs=5464064
+ <unknown>-12683 (-----) [004] .... 1920260.579012: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1184092 ofs=5468160
+ <unknown>-12683 (-----) [004] .... 1920260.579013: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1411906 ofs=5472256
+ <unknown>-12683 (-----) [004] .... 1920260.579013: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1342349 ofs=5476352
+ <unknown>-12683 (-----) [004] .... 1920260.579013: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1188185 ofs=5480448
+ <unknown>-12683 (-----) [004] .... 1920260.579014: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1158702 ofs=5484544
+ <unknown>-12683 (-----) [005] .... 1920260.579430: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1299421 ofs=5230592
+ <unknown>-12683 (-----) [005] .... 1920260.579435: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1317097 ofs=5234688
+ <unknown>-12683 (-----) [005] .... 1920260.579435: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1441714 ofs=5238784
+ <unknown>-12683 (-----) [005] .... 1920260.579438: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1081974 ofs=5242880
+ <unknown>-12683 (-----) [005] .... 1920260.579439: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1128684 ofs=5246976
+ <unknown>-12683 (-----) [005] .... 1920260.579439: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1447381 ofs=5251072
+ <unknown>-12683 (-----) [005] .... 1920260.579440: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1466410 ofs=5255168
+ <unknown>-12683 (-----) [005] .... 1920260.579440: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1259909 ofs=5259264
+ <unknown>-12683 (-----) [005] .... 1920260.579441: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1125784 ofs=5263360
+ <unknown>-12683 (-----) [005] .... 1920260.579441: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1270592 ofs=5267456
+ <unknown>-12683 (-----) [005] .... 1920260.579442: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1246070 ofs=5271552
+ <unknown>-12683 (-----) [005] .... 1920260.579442: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1472544 ofs=5275648
+ <unknown>-12683 (-----) [005] .... 1920260.579442: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1113357 ofs=5279744
+ <unknown>-12683 (-----) [005] .... 1920260.579443: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1202021 ofs=5283840
+ <unknown>-12683 (-----) [005] .... 1920260.579443: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1078639 ofs=5287936
+ <unknown>-12683 (-----) [005] .... 1920260.579449: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1176171 ofs=5292032
+ <unknown>-12683 (-----) [005] .... 1920260.579450: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1089516 ofs=5296128
+ <unknown>-12683 (-----) [005] .... 1920260.579451: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1400065 ofs=5300224
+ <unknown>-12683 (-----) [005] .... 1920260.579452: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1300489 ofs=5304320
+ <unknown>-12683 (-----) [005] .... 1920260.579452: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1452081 ofs=5308416
+ <unknown>-12683 (-----) [005] .... 1920260.579452: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1161862 ofs=5312512
+ <unknown>-12683 (-----) [005] .... 1920260.579453: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1161871 ofs=5316608
+ <unknown>-12683 (-----) [005] .... 1920260.579453: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1263798 ofs=5320704
+ <unknown>-12683 (-----) [005] .... 1920260.579454: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1126887 ofs=5324800
+ <unknown>-12683 (-----) [005] .... 1920260.579454: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1375498 ofs=5328896
+ <unknown>-12683 (-----) [005] .... 1920260.579455: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1328067 ofs=5332992
+ <unknown>-12683 (-----) [005] .... 1920260.579455: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1420691 ofs=5337088
+ <unknown>-12683 (-----) [005] .... 1920260.579456: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1298707 ofs=5341184
+ <unknown>-12683 (-----) [005] .... 1920260.579456: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1078670 ofs=5345280
+ <unknown>-12683 (-----) [005] .... 1920260.579457: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1430498 ofs=5349376
+ <unknown>-12683 (-----) [005] .... 1920260.579458: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1338720 ofs=5353472
+ <unknown>-12683 (-----) [005] .... 1920260.579476: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1452611 ofs=5357568
+ <unknown>-12683 (-----) [006] .... 1920260.580451: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1241967 ofs=0
+ <unknown>-12683 (-----) [006] .... 1920260.580454: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1116541 ofs=4096
+ <unknown>-12683 (-----) [006] .... 1920260.580461: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1145049 ofs=8192
+ <unknown>-12683 (-----) [006] .... 1920260.580462: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1277255 ofs=12288
+ <unknown>-12683 (-----) [006] .... 1920260.580462: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1098037 ofs=16384
+ <unknown>-12683 (-----) [006] .... 1920260.580463: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1135986 ofs=20480
+ <unknown>-12683 (-----) [006] .... 1920260.580464: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1154455 ofs=24576
+ <unknown>-12683 (-----) [006] .... 1920260.580464: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1221822 ofs=28672
+ <unknown>-12683 (-----) [006] .... 1920260.580465: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1078684 ofs=32768
+ <unknown>-12683 (-----) [006] .... 1920260.580465: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1158876 ofs=36864
+ <unknown>-12683 (-----) [006] .... 1920260.580465: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1289644 ofs=40960
+ <unknown>-12683 (-----) [006] .... 1920260.580466: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1289386 ofs=45056
+ <unknown>-12683 (-----) [006] .... 1920260.580466: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1131002 ofs=49152
+ <unknown>-12683 (-----) [006] .... 1920260.580467: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1464335 ofs=53248
+ <unknown>-12683 (-----) [006] .... 1920260.580468: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1135789 ofs=57344
+ <unknown>-12683 (-----) [006] .... 1920260.580469: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1240897 ofs=61440
+ <unknown>-12683 (-----) [006] .... 1920260.580469: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1241770 ofs=65536
+ <unknown>-12683 (-----) [006] .... 1920260.580470: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1421959 ofs=69632
+ <unknown>-12683 (-----) [006] .... 1920260.580470: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1230007 ofs=73728
+ <unknown>-12683 (-----) [006] .... 1920260.580471: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1109271 ofs=77824
+ <unknown>-12683 (-----) [006] .... 1920260.580471: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1159974 ofs=81920
+ <unknown>-12683 (-----) [006] .... 1920260.580471: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1154528 ofs=86016
+ <unknown>-12683 (-----) [006] .... 1920260.580472: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1315790 ofs=90112
+ <unknown>-12683 (-----) [006] .... 1920260.580473: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1185583 ofs=94208
+ <unknown>-12683 (-----) [006] .... 1920260.580473: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1253153 ofs=98304
+ <unknown>-12683 (-----) [006] .... 1920260.580473: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1103982 ofs=102400
+ <unknown>-12683 (-----) [006] .... 1920260.580474: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1284589 ofs=106496
+ <unknown>-12683 (-----) [006] .... 1920260.580474: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1169601 ofs=110592
+ <unknown>-12683 (-----) [006] .... 1920260.580476: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1206248 ofs=114688
+ <unknown>-12683 (-----) [006] .... 1920260.580476: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1261161 ofs=118784
+ <unknown>-12683 (-----) [006] .... 1920260.580477: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1305841 ofs=122880
+ <unknown>-12683 (-----) [006] .... 1920260.580477: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1468293 ofs=126976
+ <unknown>-12683 (-----) [004] .... 1920260.580646: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1318816 ofs=16384
+ <unknown>-12683 (-----) [004] .... 1920260.580649: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1472922 ofs=20480
+ <unknown>-12683 (-----) [004] .... 1920260.580650: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1473229 ofs=24576
+ <unknown>-12683 (-----) [004] .... 1920260.580650: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1524262 ofs=28672
+ <unknown>-12683 (-----) [004] .... 1920260.580656: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1205714 ofs=32768
+ <unknown>-12683 (-----) [004] .... 1920260.580657: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1310560 ofs=36864
+ <unknown>-12683 (-----) [004] .... 1920260.580658: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1295070 ofs=40960
+ <unknown>-12683 (-----) [004] .... 1920260.580659: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1404093 ofs=45056
+ <unknown>-12683 (-----) [004] .... 1920260.580659: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1435814 ofs=49152
+ <unknown>-12683 (-----) [004] .... 1920260.580660: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1435442 ofs=53248
+ <unknown>-12683 (-----) [004] .... 1920260.580660: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1096077 ofs=57344
+ <unknown>-12683 (-----) [004] .... 1920260.580661: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1483793 ofs=61440
+ <unknown>-12683 (-----) [004] .... 1920260.580661: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1231298 ofs=65536
+ <unknown>-12683 (-----) [004] .... 1920260.580661: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1215648 ofs=69632
+ <unknown>-12683 (-----) [004] .... 1920260.580662: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1327326 ofs=73728
+ <unknown>-12683 (-----) [004] .... 1920260.580662: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1108894 ofs=77824
+ <unknown>-12683 (-----) [004] .... 1920260.580663: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1327545 ofs=81920
+ <unknown>-12683 (-----) [004] .... 1920260.580663: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1328804 ofs=86016
+ <unknown>-12683 (-----) [004] .... 1920260.580664: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1300171 ofs=90112
+ <unknown>-12683 (-----) [004] .... 1920260.580664: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1353250 ofs=94208
+ <unknown>-12683 (-----) [004] .... 1920260.580668: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1333681 ofs=98304
+ <unknown>-12683 (-----) [004] .... 1920260.580668: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1144969 ofs=102400
+ <unknown>-12683 (-----) [004] .... 1920260.580669: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1450962 ofs=106496
+ <unknown>-12683 (-----) [004] .... 1920260.580669: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1255701 ofs=110592
+ <unknown>-12683 (-----) [004] .... 1920260.580670: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1294782 ofs=114688
+ <unknown>-12683 (-----) [004] .... 1920260.580670: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1226912 ofs=118784
+ <unknown>-12683 (-----) [004] .... 1920260.580671: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1294579 ofs=122880
+ <unknown>-12683 (-----) [004] .... 1920260.580671: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1246960 ofs=126976
+ <unknown>-12683 (-----) [004] .... 1920260.580671: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1199086 ofs=131072
+ <unknown>-12683 (-----) [004] .... 1920260.580672: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1449590 ofs=135168
+ <unknown>-12683 (-----) [004] .... 1920260.580672: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1276363 ofs=139264
+ <unknown>-12683 (-----) [004] .... 1920260.580675: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1389998 ofs=143360
+ <unknown>-12683 (-----) [004] .... 1920260.580739: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1423031 ofs=1249280
+ <unknown>-12683 (-----) [004] .... 1920260.580741: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1171032 ofs=1253376
+ <unknown>-12683 (-----) [004] .... 1920260.580742: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1320946 ofs=1257472
+ <unknown>-12683 (-----) [004] .... 1920260.580743: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1314696 ofs=1261568
+ <unknown>-12683 (-----) [004] .... 1920260.580743: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1414864 ofs=1265664
+ <unknown>-12683 (-----) [004] .... 1920260.580744: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1334933 ofs=1269760
+ <unknown>-12683 (-----) [004] .... 1920260.580744: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1242845 ofs=1273856
+ <unknown>-12683 (-----) [004] .... 1920260.580747: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1289488 ofs=1277952
+ <unknown>-12683 (-----) [004] .... 1920260.580748: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1335445 ofs=1282048
+ <unknown>-12683 (-----) [004] .... 1920260.580748: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1289663 ofs=1286144
+ <unknown>-12683 (-----) [004] .... 1920260.580749: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1080462 ofs=1290240
+ <unknown>-12683 (-----) [004] .... 1920260.580749: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1286303 ofs=1294336
+ <unknown>-12683 (-----) [004] .... 1920260.580750: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1353531 ofs=1298432
+ <unknown>-12683 (-----) [004] .... 1920260.580750: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1280701 ofs=1302528
+ <unknown>-12683 (-----) [004] .... 1920260.580751: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1107730 ofs=1306624
+ <unknown>-12683 (-----) [004] .... 1920260.580752: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1242729 ofs=1310720
+ <unknown>-12683 (-----) [004] .... 1920260.580753: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1078336 ofs=1314816
+ <unknown>-12683 (-----) [004] .... 1920260.580753: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1372425 ofs=1318912
+ <unknown>-12683 (-----) [004] .... 1920260.580754: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1248813 ofs=1323008
+ <unknown>-12683 (-----) [004] .... 1920260.580754: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1201155 ofs=1327104
+ <unknown>-12683 (-----) [004] .... 1920260.580755: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1250103 ofs=1331200
+ <unknown>-12683 (-----) [004] .... 1920260.580755: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1359710 ofs=1335296
+ <unknown>-12683 (-----) [004] .... 1920260.580756: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1272462 ofs=1339392
+ <unknown>-12683 (-----) [004] .... 1920260.580758: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1097035 ofs=1343488
+ <unknown>-12683 (-----) [004] .... 1920260.580759: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1233124 ofs=1347584
+ <unknown>-12683 (-----) [004] .... 1920260.580759: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1455812 ofs=1351680
+ <unknown>-12683 (-----) [004] .... 1920260.580759: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1355689 ofs=1355776
+ <unknown>-12683 (-----) [004] .... 1920260.580760: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1263593 ofs=1359872
+ <unknown>-12683 (-----) [004] .... 1920260.580760: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1230789 ofs=1363968
+ <unknown>-12683 (-----) [004] .... 1920260.580761: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1143766 ofs=1368064
+ <unknown>-12683 (-----) [004] .... 1920260.580762: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1269666 ofs=1372160
+ <unknown>-12683 (-----) [004] .... 1920260.580762: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1353022 ofs=1376256
+ <unknown>-12683 (-----) [004] .... 1920260.581613: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1355509 ofs=258048
+ <unknown>-12683 (-----) [004] .... 1920260.581615: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1178902 ofs=262144
+ <unknown>-12683 (-----) [004] .... 1920260.581616: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1193649 ofs=266240
+ <unknown>-12683 (-----) [004] .... 1920260.581618: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1225497 ofs=270336
+ <unknown>-12683 (-----) [004] .... 1920260.581618: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1228259 ofs=274432
+ <unknown>-12683 (-----) [004] .... 1920260.581635: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1309674 ofs=278528
+ <unknown>-12683 (-----) [004] .... 1920260.581635: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1239390 ofs=282624
+ <unknown>-12683 (-----) [004] .... 1920260.581636: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1468083 ofs=286720
+ <unknown>-12683 (-----) [004] .... 1920260.581636: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1292751 ofs=290816
+ <unknown>-12683 (-----) [004] .... 1920260.581637: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1318066 ofs=294912
+ <unknown>-12683 (-----) [004] .... 1920260.581637: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1489314 ofs=299008
+ <unknown>-12683 (-----) [004] .... 1920260.581637: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1169867 ofs=303104
+ <unknown>-12683 (-----) [004] .... 1920260.581639: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1314256 ofs=307200
+ <unknown>-12683 (-----) [004] .... 1920260.581639: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1310230 ofs=311296
+ <unknown>-12683 (-----) [004] .... 1920260.581640: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1356180 ofs=315392
+ <unknown>-12683 (-----) [004] .... 1920260.581640: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1419179 ofs=319488
+ <unknown>-12683 (-----) [004] .... 1920260.581641: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1307265 ofs=323584
+ <unknown>-12683 (-----) [004] .... 1920260.581641: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1218590 ofs=327680
+ <unknown>-12683 (-----) [004] .... 1920260.581642: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1447586 ofs=331776
+ <unknown>-12683 (-----) [004] .... 1920260.581642: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1209382 ofs=335872
+ <unknown>-12683 (-----) [004] .... 1920260.581642: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1072148 ofs=339968
+ <unknown>-12683 (-----) [004] .... 1920260.581645: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1227195 ofs=344064
+ <unknown>-12683 (-----) [004] .... 1920260.581646: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1246369 ofs=348160
+ <unknown>-12683 (-----) [004] .... 1920260.581646: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1193845 ofs=352256
+ <unknown>-12683 (-----) [004] .... 1920260.581647: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1137553 ofs=356352
+ <unknown>-12683 (-----) [004] .... 1920260.581647: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1475215 ofs=360448
+ <unknown>-12683 (-----) [004] .... 1920260.581648: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1258935 ofs=364544
+ <unknown>-12683 (-----) [004] .... 1920260.581649: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1448788 ofs=368640
+ <unknown>-12683 (-----) [004] .... 1920260.581649: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1447611 ofs=372736
+ <unknown>-12683 (-----) [004] .... 1920260.581650: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1290842 ofs=376832
+ <unknown>-12683 (-----) [004] .... 1920260.581650: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1447826 ofs=380928
+ <unknown>-12683 (-----) [004] .... 1920260.581650: mm_filemap_add_to_page_cache: dev 0:64771 ino da07 page=0000000000000000 pfn=1181016 ofs=385024
+ <unknown>-12683 (-----) [005] .... 1920260.582230: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1216810 ofs=1662976
+ <unknown>-12683 (-----) [005] .... 1920260.582234: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1175966 ofs=1667072
+ <unknown>-12683 (-----) [005] .... 1920260.582235: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1449798 ofs=1671168
+ <unknown>-12683 (-----) [005] .... 1920260.582236: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1273480 ofs=1675264
+ <unknown>-12683 (-----) [005] .... 1920260.582236: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1152779 ofs=1679360
+ <unknown>-12683 (-----) [005] .... 1920260.582237: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1272810 ofs=1683456
+ <unknown>-12683 (-----) [005] .... 1920260.582237: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1248634 ofs=1687552
+ <unknown>-12683 (-----) [005] .... 1920260.582237: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1203376 ofs=1691648
+ <unknown>-12683 (-----) [005] .... 1920260.582238: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1138880 ofs=1695744
+ <unknown>-12683 (-----) [005] .... 1920260.582238: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1344591 ofs=1699840
+ <unknown>-12683 (-----) [005] .... 1920260.582239: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1416060 ofs=1703936
+ <unknown>-12683 (-----) [005] .... 1920260.582246: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1128676 ofs=1708032
+ <unknown>-12683 (-----) [005] .... 1920260.582247: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1301921 ofs=1712128
+ <unknown>-12683 (-----) [005] .... 1920260.582248: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1384569 ofs=1716224
+ <unknown>-12683 (-----) [005] .... 1920260.582248: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1249106 ofs=1720320
+ <unknown>-12683 (-----) [005] .... 1920260.582249: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1206596 ofs=1724416
+ <unknown>-12683 (-----) [005] .... 1920260.582249: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1429831 ofs=1728512
+ <unknown>-12683 (-----) [005] .... 1920260.582252: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1107796 ofs=1732608
+ <unknown>-12683 (-----) [005] .... 1920260.582255: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1098336 ofs=1736704
+ <unknown>-12683 (-----) [005] .... 1920260.582255: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1230286 ofs=1740800
+ <unknown>-12683 (-----) [005] .... 1920260.582256: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1100370 ofs=1744896
+ <unknown>-12683 (-----) [005] .... 1920260.582256: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1241930 ofs=1748992
+ <unknown>-12683 (-----) [005] .... 1920260.582257: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1366807 ofs=1753088
+ <unknown>-12683 (-----) [005] .... 1920260.582257: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1136252 ofs=1757184
+ <unknown>-12683 (-----) [005] .... 1920260.582258: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1274291 ofs=1761280
+ <unknown>-12683 (-----) [005] .... 1920260.582258: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1254775 ofs=1765376
+ <unknown>-12683 (-----) [005] .... 1920260.582259: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1194679 ofs=1769472
+ <unknown>-12683 (-----) [005] .... 1920260.582262: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1177090 ofs=1773568
+ <unknown>-12683 (-----) [005] .... 1920260.582263: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1343925 ofs=1777664
+ <unknown>-12683 (-----) [005] .... 1920260.582263: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1159217 ofs=1781760
+ <unknown>-12683 (-----) [005] .... 1920260.582263: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1435471 ofs=1785856
+ <unknown>-12683 (-----) [005] .... 1920260.582264: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1435529 ofs=1789952
+ <unknown>-12683 (-----) [004] .... 1920260.582524: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1181910 ofs=0
+ <unknown>-12683 (-----) [004] .... 1920260.582528: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1212021 ofs=4096
+ <unknown>-12683 (-----) [004] .... 1920260.582529: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1162778 ofs=8192
+ <unknown>-12683 (-----) [004] .... 1920260.582529: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1107700 ofs=12288
+ <unknown>-12683 (-----) [004] .... 1920260.583553: mm_filemap_add_to_page_cache: dev 0:64771 ino df31 page=0000000000000000 pfn=1093394 ofs=3399680
<unknown>-12683 (-----) [004] .... 1920260.583984: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1121431 ofs=242503680
<unknown>-12683 (-----) [004] .... 1920260.583986: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1168551 ofs=13115392
- <unknown>-12683 (-----) [004] .... 1920260.584304: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1347409 ofs=0
- <unknown>-12683 (-----) [004] .... 1920260.584307: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1428681 ofs=4096
- <unknown>-12683 (-----) [004] .... 1920260.584307: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1259106 ofs=8192
- <unknown>-12683 (-----) [004] .... 1920260.584308: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1343229 ofs=12288
- <unknown>-12694 (-----) [005] .... 1920260.584622: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1098733 ofs=1531904
- <unknown>-12696 (-----) [006] .... 1920260.584626: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1331319 ofs=1536000
- <unknown>-12694 (-----) [005] .... 1920260.584626: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1278537 ofs=1540096
- <unknown>-12696 (-----) [006] .... 1920260.584631: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1492534 ofs=1544192
- <unknown>-12694 (-----) [005] .... 1920260.584636: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1460878 ofs=1548288
- <unknown>-12694 (-----) [005] .... 1920260.584640: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1092973 ofs=1552384
- <unknown>-12694 (-----) [005] .... 1920260.584641: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1103200 ofs=1556480
- <unknown>-12694 (-----) [005] .... 1920260.584642: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1257426 ofs=1560576
- <unknown>-12694 (-----) [005] .... 1920260.584642: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1219424 ofs=1564672
- <unknown>-12683 (-----) [004] .... 1920260.584660: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1279352 ofs=1568768
- <unknown>-12696 (-----) [006] .... 1920260.584662: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1260572 ofs=1572864
- <unknown>-12683 (-----) [004] .... 1920260.584663: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1225809 ofs=1576960
- <unknown>-12696 (-----) [006] .... 1920260.584665: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1350766 ofs=1585152
- <unknown>-12697 (-----) [007] .... 1920260.584666: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1107173 ofs=1581056
- <unknown>-12683 (-----) [004] .... 1920260.584668: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1305885 ofs=1589248
- <unknown>-12694 (-----) [005] .... 1920260.584669: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1293385 ofs=1593344
- <unknown>-12696 (-----) [006] .... 1920260.584670: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1173841 ofs=1597440
- <unknown>-12697 (-----) [007] .... 1920260.584670: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1080021 ofs=1601536
- <unknown>-12683 (-----) [004] .... 1920260.584673: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1147419 ofs=1605632
- <unknown>-12696 (-----) [006] .... 1920260.584673: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1252762 ofs=1609728
- <unknown>-12694 (-----) [005] .... 1920260.584674: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1323916 ofs=1613824
- <unknown>-12683 (-----) [004] .... 1920260.584675: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1155631 ofs=1617920
- <unknown>-12696 (-----) [006] .... 1920260.584676: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1449815 ofs=1622016
- <unknown>-12694 (-----) [005] .... 1920260.584678: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1227069 ofs=1626112
- <unknown>-12696 (-----) [006] .... 1920260.584680: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1317692 ofs=1630208
- <unknown>-12694 (-----) [005] .... 1920260.584681: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1492244 ofs=1634304
- <unknown>-12683 (-----) [004] .... 1920260.584682: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1241876 ofs=1638400
- <unknown>-12697 (-----) [007] .... 1920260.585446: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1402958 ofs=167936
- <unknown>-12697 (-----) [007] .... 1920260.585449: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1133263 ofs=172032
- <unknown>-12697 (-----) [007] .... 1920260.585450: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1295502 ofs=176128
- <unknown>-12697 (-----) [007] .... 1920260.585450: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1249495 ofs=180224
- <unknown>-12697 (-----) [007] .... 1920260.585451: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1237999 ofs=184320
- <unknown>-12697 (-----) [007] .... 1920260.585451: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1280965 ofs=188416
- <unknown>-12697 (-----) [007] .... 1920260.585454: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1208361 ofs=192512
- <unknown>-12697 (-----) [007] .... 1920260.585454: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1308840 ofs=196608
- <unknown>-12695 (-----) [004] .... 1920260.585455: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1138875 ofs=569344
- <unknown>-12695 (-----) [004] .... 1920260.585458: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1314886 ofs=573440
- <unknown>-12697 (-----) [007] .... 1920260.585458: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1242734 ofs=200704
- <unknown>-12695 (-----) [004] .... 1920260.585458: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1447386 ofs=577536
- <unknown>-12697 (-----) [007] .... 1920260.585459: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1241302 ofs=204800
- <unknown>-12695 (-----) [004] .... 1920260.585459: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1328663 ofs=581632
- <unknown>-12697 (-----) [007] .... 1920260.585459: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1476101 ofs=208896
- <unknown>-12695 (-----) [004] .... 1920260.585460: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1209461 ofs=585728
- <unknown>-12697 (-----) [007] .... 1920260.585460: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1080147 ofs=212992
- <unknown>-12697 (-----) [007] .... 1920260.585461: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1128509 ofs=217088
- <unknown>-12697 (-----) [007] .... 1920260.585461: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1371915 ofs=221184
- <unknown>-12697 (-----) [007] .... 1920260.585461: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1264015 ofs=225280
- <unknown>-12697 (-----) [007] .... 1920260.585462: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1211695 ofs=229376
- <unknown>-12697 (-----) [007] .... 1920260.585462: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1150386 ofs=233472
- <unknown>-12697 (-----) [007] .... 1920260.585463: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1135747 ofs=237568
- <unknown>-12697 (-----) [007] .... 1920260.585463: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1128230 ofs=241664
- <unknown>-12697 (-----) [007] .... 1920260.585464: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1155451 ofs=245760
- <unknown>-12697 (-----) [007] .... 1920260.585465: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1246841 ofs=249856
- <unknown>-12697 (-----) [007] .... 1920260.585465: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1462971 ofs=253952
- <unknown>-12697 (-----) [007] .... 1920260.585466: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1131333 ofs=258048
- <unknown>-12697 (-----) [007] .... 1920260.585466: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1289407 ofs=262144
- <unknown>-12695 (-----) [004] .... 1920260.585467: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1134730 ofs=589824
- <unknown>-12697 (-----) [007] .... 1920260.585467: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1289873 ofs=266240
- <unknown>-12697 (-----) [007] .... 1920260.585468: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1448734 ofs=270336
- <unknown>-12695 (-----) [004] .... 1920260.585468: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1129776 ofs=593920
- <unknown>-12697 (-----) [007] .... 1920260.585468: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1524090 ofs=274432
- <unknown>-12695 (-----) [004] .... 1920260.585468: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1399725 ofs=598016
- <unknown>-12697 (-----) [007] .... 1920260.585469: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1524081 ofs=278528
- <unknown>-12695 (-----) [004] .... 1920260.585469: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1276535 ofs=602112
- <unknown>-12697 (-----) [007] .... 1920260.585469: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1524060 ofs=282624
- <unknown>-12695 (-----) [004] .... 1920260.585470: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1449847 ofs=606208
- <unknown>-12697 (-----) [007] .... 1920260.585470: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1158944 ofs=286720
- <unknown>-12695 (-----) [004] .... 1920260.585470: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1384536 ofs=610304
- <unknown>-12697 (-----) [007] .... 1920260.585470: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1116785 ofs=290816
- <unknown>-12695 (-----) [004] .... 1920260.585471: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1308118 ofs=614400
- <unknown>-12697 (-----) [007] .... 1920260.585471: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1448669 ofs=294912
- <unknown>-12695 (-----) [004] .... 1920260.585471: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1227050 ofs=618496
- <unknown>-12695 (-----) [004] .... 1920260.585473: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1289324 ofs=622592
- <unknown>-12695 (-----) [004] .... 1920260.585473: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1187869 ofs=626688
- <unknown>-12695 (-----) [004] .... 1920260.585474: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1400523 ofs=630784
- <unknown>-12695 (-----) [004] .... 1920260.585474: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1344176 ofs=634880
- <unknown>-12695 (-----) [004] .... 1920260.585475: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1092871 ofs=638976
- <unknown>-12695 (-----) [004] .... 1920260.585475: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1092021 ofs=643072
- <unknown>-12695 (-----) [004] .... 1920260.585476: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1198169 ofs=647168
- <unknown>-12695 (-----) [004] .... 1920260.585476: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1371540 ofs=651264
- <unknown>-12683 (-----) [005] .... 1920260.585476: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1195003 ofs=348160
- <unknown>-12695 (-----) [004] .... 1920260.585477: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1228787 ofs=655360
- <unknown>-12695 (-----) [004] .... 1920260.585477: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1236123 ofs=659456
- <unknown>-12695 (-----) [004] .... 1920260.585477: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1137213 ofs=663552
- <unknown>-12695 (-----) [004] .... 1920260.585478: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1294618 ofs=667648
- <unknown>-12695 (-----) [004] .... 1920260.585478: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1241048 ofs=671744
- <unknown>-12695 (-----) [004] .... 1920260.585479: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1228779 ofs=675840
- <unknown>-12683 (-----) [005] .... 1920260.585479: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1199292 ofs=352256
- <unknown>-12683 (-----) [005] .... 1920260.585480: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1200861 ofs=356352
- <unknown>-12695 (-----) [004] .... 1920260.585480: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1309572 ofs=679936
- <unknown>-12683 (-----) [005] .... 1920260.585480: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1215770 ofs=360448
- <unknown>-12695 (-----) [004] .... 1920260.585481: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1409002 ofs=684032
- <unknown>-12683 (-----) [005] .... 1920260.585481: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1151883 ofs=364544
- <unknown>-12695 (-----) [004] .... 1920260.585481: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1103729 ofs=688128
- <unknown>-12683 (-----) [005] .... 1920260.585482: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1468126 ofs=368640
- <unknown>-12695 (-----) [004] .... 1920260.585482: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1162720 ofs=692224
- <unknown>-12683 (-----) [005] .... 1920260.585482: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1251672 ofs=372736
- <unknown>-12695 (-----) [004] .... 1920260.585482: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1199221 ofs=696320
- <unknown>-12683 (-----) [005] .... 1920260.585483: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1283325 ofs=376832
- <unknown>-12683 (-----) [005] .... 1920260.585483: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1190489 ofs=380928
- <unknown>-12683 (-----) [005] .... 1920260.585484: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1489117 ofs=385024
- <unknown>-12683 (-----) [005] .... 1920260.585484: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1273899 ofs=389120
- <unknown>-12683 (-----) [005] .... 1920260.585485: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1274459 ofs=393216
- <unknown>-12683 (-----) [005] .... 1920260.585486: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1316649 ofs=397312
- <unknown>-12683 (-----) [005] .... 1920260.585491: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1375678 ofs=401408
- <unknown>-12683 (-----) [005] .... 1920260.585491: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1483317 ofs=405504
- <unknown>-12683 (-----) [005] .... 1920260.585492: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1240286 ofs=409600
- <unknown>-12683 (-----) [005] .... 1920260.585492: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1131345 ofs=413696
- <unknown>-12683 (-----) [005] .... 1920260.585493: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1200483 ofs=417792
- <unknown>-12683 (-----) [005] .... 1920260.585493: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1384693 ofs=421888
- <unknown>-12683 (-----) [005] .... 1920260.585493: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1161385 ofs=425984
- <unknown>-12683 (-----) [005] .... 1920260.585494: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1452025 ofs=430080
- <unknown>-12683 (-----) [005] .... 1920260.585495: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1253654 ofs=434176
- <unknown>-12683 (-----) [005] .... 1920260.585495: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1116697 ofs=438272
- <unknown>-12683 (-----) [005] .... 1920260.585495: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1432645 ofs=442368
- <unknown>-12694 (-----) [006] .... 1920260.585495: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1337397 ofs=16384
- <unknown>-12683 (-----) [005] .... 1920260.585496: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1304229 ofs=446464
- <unknown>-12683 (-----) [005] .... 1920260.585496: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1419147 ofs=450560
- <unknown>-12683 (-----) [005] .... 1920260.585498: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1349246 ofs=454656
- <unknown>-12683 (-----) [005] .... 1920260.585499: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1128519 ofs=458752
- <unknown>-12683 (-----) [005] .... 1920260.585499: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1125168 ofs=462848
- <unknown>-12694 (-----) [006] .... 1920260.585509: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1081031 ofs=20480
- <unknown>-12694 (-----) [006] .... 1920260.585509: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1293022 ofs=24576
- <unknown>-12694 (-----) [006] .... 1920260.585510: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1113007 ofs=28672
- <unknown>-12694 (-----) [006] .... 1920260.585510: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1339312 ofs=32768
- <unknown>-12694 (-----) [006] .... 1920260.585511: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1412311 ofs=36864
- <unknown>-12694 (-----) [006] .... 1920260.585511: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1260960 ofs=40960
- <unknown>-12694 (-----) [006] .... 1920260.585512: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1189529 ofs=45056
- <unknown>-12694 (-----) [006] .... 1920260.585512: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1412184 ofs=49152
- <unknown>-12694 (-----) [006] .... 1920260.585513: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1481227 ofs=53248
- <unknown>-12694 (-----) [006] .... 1920260.585513: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1455940 ofs=57344
- <unknown>-12694 (-----) [006] .... 1920260.585514: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1299132 ofs=61440
- <unknown>-12694 (-----) [006] .... 1920260.585514: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1337375 ofs=65536
- <unknown>-12694 (-----) [006] .... 1920260.585529: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1328742 ofs=69632
- <unknown>-12694 (-----) [006] .... 1920260.585529: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1315646 ofs=73728
- <unknown>-12694 (-----) [006] .... 1920260.585531: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1225475 ofs=77824
- <unknown>-12694 (-----) [006] .... 1920260.585531: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1146097 ofs=81920
- <unknown>-12694 (-----) [006] .... 1920260.585532: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1318775 ofs=86016
- <unknown>-12694 (-----) [006] .... 1920260.585532: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1448391 ofs=90112
- <unknown>-12694 (-----) [006] .... 1920260.585532: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1441412 ofs=94208
- <unknown>-12694 (-----) [006] .... 1920260.585533: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1138111 ofs=98304
- <unknown>-12694 (-----) [006] .... 1920260.585533: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1143223 ofs=102400
- <unknown>-12683 (-----) [005] .... 1920260.585534: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1079876 ofs=466944
- <unknown>-12694 (-----) [006] .... 1920260.585534: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1447637 ofs=106496
- <unknown>-12694 (-----) [006] .... 1920260.585534: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1220585 ofs=110592
- <unknown>-12694 (-----) [006] .... 1920260.585535: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1449051 ofs=114688
- <unknown>-12694 (-----) [006] .... 1920260.585535: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1313180 ofs=118784
- <unknown>-12694 (-----) [006] .... 1920260.585535: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1313166 ofs=122880
- <unknown>-12694 (-----) [006] .... 1920260.585536: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1313154 ofs=126976
- <unknown>-12683 (-----) [005] .... 1920260.585536: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1218394 ofs=471040
- <unknown>-12694 (-----) [006] .... 1920260.585536: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1144047 ofs=131072
- <unknown>-12683 (-----) [005] .... 1920260.585537: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1218579 ofs=475136
- <unknown>-12694 (-----) [006] .... 1920260.585543: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1241332 ofs=135168
- <unknown>-12694 (-----) [006] .... 1920260.585543: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1097199 ofs=139264
- <unknown>-12694 (-----) [006] .... 1920260.585545: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1214197 ofs=143360
- <unknown>-12694 (-----) [006] .... 1920260.585645: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1197633 ofs=147456
- <unknown>-12694 (-----) [006] .... 1920260.585647: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1311536 ofs=151552
- <unknown>-12694 (-----) [006] .... 1920260.585647: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1322952 ofs=155648
- <unknown>-12694 (-----) [006] .... 1920260.585647: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1346974 ofs=159744
- <unknown>-12694 (-----) [006] .... 1920260.585648: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1257232 ofs=163840
- <unknown>-12695 (-----) [004] .... 1920260.586355: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1204484 ofs=700416
- <unknown>-12695 (-----) [004] .... 1920260.586357: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1326426 ofs=704512
- <unknown>-12695 (-----) [004] .... 1920260.586358: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1151808 ofs=708608
- <unknown>-12695 (-----) [004] .... 1920260.586358: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1209422 ofs=712704
- <unknown>-12695 (-----) [004] .... 1920260.586359: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1408387 ofs=716800
- <unknown>-12695 (-----) [004] .... 1920260.586359: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1197336 ofs=720896
- <unknown>-12695 (-----) [004] .... 1920260.586363: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1205652 ofs=724992
- <unknown>-12695 (-----) [004] .... 1920260.586363: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1133421 ofs=729088
- <unknown>-12695 (-----) [004] .... 1920260.586364: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1092173 ofs=733184
- <unknown>-12695 (-----) [004] .... 1920260.586365: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1124430 ofs=737280
- <unknown>-12695 (-----) [004] .... 1920260.586365: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1143926 ofs=741376
- <unknown>-12695 (-----) [004] .... 1920260.586366: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1090109 ofs=745472
- <unknown>-12695 (-----) [004] .... 1920260.586366: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1102012 ofs=749568
- <unknown>-12695 (-----) [004] .... 1920260.586367: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1154930 ofs=753664
- <unknown>-12695 (-----) [004] .... 1920260.586368: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1132993 ofs=757760
- <unknown>-12695 (-----) [004] .... 1920260.586369: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1430780 ofs=761856
- <unknown>-12695 (-----) [004] .... 1920260.586369: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1197452 ofs=765952
- <unknown>-12695 (-----) [004] .... 1920260.586369: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1075111 ofs=770048
- <unknown>-12695 (-----) [004] .... 1920260.586370: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1275616 ofs=774144
- <unknown>-12695 (-----) [004] .... 1920260.586370: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1444981 ofs=778240
- <unknown>-12695 (-----) [004] .... 1920260.586371: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1452592 ofs=782336
- <unknown>-12695 (-----) [004] .... 1920260.586374: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1102857 ofs=786432
- <unknown>-12695 (-----) [004] .... 1920260.586376: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1406969 ofs=790528
- <unknown>-12695 (-----) [004] .... 1920260.586378: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1522553 ofs=794624
- <unknown>-12695 (-----) [004] .... 1920260.586378: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1260771 ofs=798720
- <unknown>-12695 (-----) [004] .... 1920260.586379: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1474649 ofs=802816
- <unknown>-12695 (-----) [004] .... 1920260.586379: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1268708 ofs=806912
- <unknown>-12695 (-----) [004] .... 1920260.586379: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1346144 ofs=811008
- <unknown>-12695 (-----) [004] .... 1920260.586380: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1081167 ofs=815104
- <unknown>-12695 (-----) [004] .... 1920260.586380: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1137677 ofs=819200
- <unknown>-12695 (-----) [004] .... 1920260.586381: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1161175 ofs=823296
- <unknown>-12695 (-----) [004] .... 1920260.586381: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1461331 ofs=827392
- <unknown>-12695 (-----) [004] .... 1920260.586492: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1347219 ofs=831488
- <unknown>-12695 (-----) [004] .... 1920260.586494: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1290004 ofs=835584
- <unknown>-12695 (-----) [004] .... 1920260.586494: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1299174 ofs=839680
- <unknown>-12695 (-----) [004] .... 1920260.586496: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1317595 ofs=843776
- <unknown>-12695 (-----) [004] .... 1920260.586496: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1484924 ofs=847872
- <unknown>-12695 (-----) [004] .... 1920260.586497: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1169920 ofs=851968
- <unknown>-12695 (-----) [004] .... 1920260.586501: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1359189 ofs=856064
- <unknown>-12695 (-----) [004] .... 1920260.586501: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1307842 ofs=860160
- <unknown>-12695 (-----) [004] .... 1920260.586502: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1237858 ofs=864256
- <unknown>-12695 (-----) [004] .... 1920260.586502: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1189461 ofs=868352
- <unknown>-12695 (-----) [004] .... 1920260.586503: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1223232 ofs=872448
- <unknown>-12695 (-----) [004] .... 1920260.586503: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1104076 ofs=876544
- <unknown>-12695 (-----) [004] .... 1920260.586504: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1079223 ofs=880640
- <unknown>-12695 (-----) [004] .... 1920260.586504: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1092537 ofs=884736
- <unknown>-12695 (-----) [004] .... 1920260.586505: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1353960 ofs=888832
- <unknown>-12695 (-----) [004] .... 1920260.586505: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1346330 ofs=892928
- <unknown>-12695 (-----) [004] .... 1920260.586506: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1345764 ofs=897024
- <unknown>-12695 (-----) [004] .... 1920260.586507: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1363913 ofs=901120
- <unknown>-12695 (-----) [004] .... 1920260.586508: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1319570 ofs=905216
- <unknown>-12695 (-----) [004] .... 1920260.586508: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1367024 ofs=909312
- <unknown>-12695 (-----) [004] .... 1920260.586508: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1333808 ofs=913408
- <unknown>-12695 (-----) [004] .... 1920260.586509: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1158627 ofs=917504
- <unknown>-12695 (-----) [004] .... 1920260.586509: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1300368 ofs=921600
- <unknown>-12695 (-----) [004] .... 1920260.586510: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1245363 ofs=925696
- <unknown>-12695 (-----) [004] .... 1920260.586510: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1345609 ofs=929792
- <unknown>-12695 (-----) [004] .... 1920260.586510: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1393826 ofs=933888
- <unknown>-12695 (-----) [004] .... 1920260.586511: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1200552 ofs=937984
- <unknown>-12695 (-----) [004] .... 1920260.586511: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1170885 ofs=942080
- <unknown>-12695 (-----) [004] .... 1920260.586512: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1536209 ofs=946176
- <unknown>-12695 (-----) [004] .... 1920260.586512: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1189630 ofs=950272
- <unknown>-12695 (-----) [004] .... 1920260.586513: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1121010 ofs=954368
- <unknown>-12695 (-----) [004] .... 1920260.586514: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1324474 ofs=958464
- <unknown>-12697 (-----) [007] .... 1920260.586578: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1129628 ofs=299008
- <unknown>-12697 (-----) [007] .... 1920260.586579: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1307120 ofs=303104
- <unknown>-12697 (-----) [007] .... 1920260.586580: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1347284 ofs=307200
- <unknown>-12697 (-----) [007] .... 1920260.586580: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1312996 ofs=311296
- <unknown>-12697 (-----) [007] .... 1920260.586581: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1170623 ofs=315392
- <unknown>-12697 (-----) [007] .... 1920260.586581: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1359281 ofs=319488
- <unknown>-12697 (-----) [007] .... 1920260.586582: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1180021 ofs=323584
- <unknown>-12697 (-----) [007] .... 1920260.586582: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1195728 ofs=327680
- <unknown>-12697 (-----) [007] .... 1920260.586582: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1163642 ofs=331776
- <unknown>-12697 (-----) [007] .... 1920260.586587: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1152538 ofs=335872
- <unknown>-12697 (-----) [007] .... 1920260.586589: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1345922 ofs=339968
- <unknown>-12697 (-----) [007] .... 1920260.586589: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1343604 ofs=344064
- <unknown>-12697 (-----) [007] .... 1920260.586721: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1399371 ofs=479232
- <unknown>-12697 (-----) [007] .... 1920260.586723: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1106549 ofs=483328
- <unknown>-12697 (-----) [007] .... 1920260.586724: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1331546 ofs=487424
- <unknown>-12697 (-----) [007] .... 1920260.586724: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1299299 ofs=491520
- <unknown>-12697 (-----) [007] .... 1920260.586725: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1288883 ofs=495616
- <unknown>-12697 (-----) [007] .... 1920260.586725: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1399049 ofs=499712
- <unknown>-12697 (-----) [007] .... 1920260.586726: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1146931 ofs=503808
- <unknown>-12697 (-----) [007] .... 1920260.586726: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1296592 ofs=507904
- <unknown>-12697 (-----) [007] .... 1920260.586727: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1468397 ofs=512000
- <unknown>-12697 (-----) [007] .... 1920260.586727: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1215698 ofs=516096
- <unknown>-12697 (-----) [007] .... 1920260.586727: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1177341 ofs=520192
- <unknown>-12697 (-----) [007] .... 1920260.586731: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1189162 ofs=524288
- <unknown>-12697 (-----) [007] .... 1920260.586732: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1435997 ofs=528384
- <unknown>-12697 (-----) [007] .... 1920260.586732: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1209896 ofs=532480
- <unknown>-12697 (-----) [007] .... 1920260.586733: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1255888 ofs=536576
- <unknown>-12697 (-----) [007] .... 1920260.586734: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1234200 ofs=540672
- <unknown>-12697 (-----) [007] .... 1920260.586734: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1422854 ofs=544768
- <unknown>-12697 (-----) [007] .... 1920260.586735: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1435794 ofs=548864
- <unknown>-12697 (-----) [007] .... 1920260.586735: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1236279 ofs=552960
- <unknown>-12697 (-----) [007] .... 1920260.586736: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1485732 ofs=557056
- <unknown>-12683 (-----) [005] .... 1920260.586743: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1417198 ofs=561152
- <unknown>-12683 (-----) [005] .... 1920260.586746: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1469450 ofs=565248
- <unknown>-12696 (-----) [004] .... 1920260.587465: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1489023 ofs=1040384
- <unknown>-12696 (-----) [004] .... 1920260.587469: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1449498 ofs=1044480
- <unknown>-12696 (-----) [004] .... 1920260.587469: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1447737 ofs=1048576
- <unknown>-12696 (-----) [004] .... 1920260.587470: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1124530 ofs=1052672
- <unknown>-12696 (-----) [004] .... 1920260.587476: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1246743 ofs=1056768
- <unknown>-12696 (-----) [004] .... 1920260.587476: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1441927 ofs=1060864
- <unknown>-12696 (-----) [004] .... 1920260.587477: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1280581 ofs=1064960
- <unknown>-12696 (-----) [004] .... 1920260.587477: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1289438 ofs=1069056
- <unknown>-12696 (-----) [004] .... 1920260.587477: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1464236 ofs=1073152
- <unknown>-12696 (-----) [004] .... 1920260.587478: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1125808 ofs=1077248
- <unknown>-12696 (-----) [004] .... 1920260.587478: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1329385 ofs=1081344
- <unknown>-12696 (-----) [004] .... 1920260.587480: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1314093 ofs=1085440
- <unknown>-12696 (-----) [004] .... 1920260.587480: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1201837 ofs=1089536
- <unknown>-12696 (-----) [004] .... 1920260.587481: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1327734 ofs=1093632
- <unknown>-12696 (-----) [004] .... 1920260.587481: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1406568 ofs=1097728
- <unknown>-12696 (-----) [004] .... 1920260.587481: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1331873 ofs=1101824
- <unknown>-12696 (-----) [004] .... 1920260.587482: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1331898 ofs=1105920
- <unknown>-12696 (-----) [004] .... 1920260.587482: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1331917 ofs=1110016
- <unknown>-12696 (-----) [004] .... 1920260.587483: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1332091 ofs=1114112
- <unknown>-12696 (-----) [004] .... 1920260.587483: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1108186 ofs=1118208
- <unknown>-12696 (-----) [004] .... 1920260.587486: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1182631 ofs=1122304
- <unknown>-12696 (-----) [004] .... 1920260.587486: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1085941 ofs=1126400
- <unknown>-12696 (-----) [004] .... 1920260.587487: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1433982 ofs=1130496
- <unknown>-12696 (-----) [004] .... 1920260.587487: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1387028 ofs=1134592
- <unknown>-12696 (-----) [004] .... 1920260.587488: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1353117 ofs=1138688
- <unknown>-12696 (-----) [004] .... 1920260.587489: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1352364 ofs=1142784
- <unknown>-12696 (-----) [004] .... 1920260.587489: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1144513 ofs=1146880
- <unknown>-12696 (-----) [004] .... 1920260.587490: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1403984 ofs=1150976
- <unknown>-12696 (-----) [004] .... 1920260.587490: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1278970 ofs=1155072
- <unknown>-12696 (-----) [004] .... 1920260.587491: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1326743 ofs=1159168
- <unknown>-12696 (-----) [004] .... 1920260.587491: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1221809 ofs=1163264
- <unknown>-12696 (-----) [004] .... 1920260.587492: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1268668 ofs=1167360
- <unknown>-12695 (-----) [005] .... 1920260.587502: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1074544 ofs=962560
- <unknown>-12695 (-----) [005] .... 1920260.587506: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1074294 ofs=966656
- <unknown>-12695 (-----) [005] .... 1920260.587506: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1075097 ofs=970752
- <unknown>-12695 (-----) [005] .... 1920260.587507: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1162407 ofs=974848
- <unknown>-12695 (-----) [005] .... 1920260.587507: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1141370 ofs=978944
- <unknown>-12695 (-----) [005] .... 1920260.587508: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1306487 ofs=983040
- <unknown>-12695 (-----) [005] .... 1920260.587508: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1306434 ofs=987136
- <unknown>-12695 (-----) [005] .... 1920260.587514: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1306347 ofs=991232
- <unknown>-12695 (-----) [005] .... 1920260.587514: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1306247 ofs=995328
- <unknown>-12695 (-----) [005] .... 1920260.587515: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1306195 ofs=999424
- <unknown>-12695 (-----) [005] .... 1920260.587516: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1306039 ofs=1003520
- <unknown>-12695 (-----) [005] .... 1920260.587516: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1305983 ofs=1007616
- <unknown>-12694 (-----) [006] .... 1920260.587701: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1216391 ofs=1171456
- <unknown>-12694 (-----) [006] .... 1920260.587705: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1262462 ofs=1175552
- <unknown>-12694 (-----) [006] .... 1920260.587706: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1358114 ofs=1179648
- <unknown>-12694 (-----) [006] .... 1920260.587706: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1357898 ofs=1183744
- <unknown>-12694 (-----) [006] .... 1920260.587707: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1237003 ofs=1187840
- <unknown>-12694 (-----) [006] .... 1920260.587707: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1126319 ofs=1191936
- <unknown>-12694 (-----) [006] .... 1920260.587708: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1415489 ofs=1196032
- <unknown>-12694 (-----) [006] .... 1920260.587708: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1279558 ofs=1200128
- <unknown>-12694 (-----) [006] .... 1920260.587708: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1434022 ofs=1204224
- <unknown>-12694 (-----) [006] .... 1920260.587709: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1220130 ofs=1208320
- <unknown>-12694 (-----) [006] .... 1920260.587710: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1163037 ofs=1212416
- <unknown>-12694 (-----) [006] .... 1920260.587711: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1404501 ofs=1216512
- <unknown>-12694 (-----) [006] .... 1920260.587711: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1406287 ofs=1220608
- <unknown>-12697 (-----) [007] .... 1920260.588132: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1355143 ofs=1376256
- <unknown>-12697 (-----) [007] .... 1920260.588136: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1213923 ofs=1380352
- <unknown>-12697 (-----) [007] .... 1920260.588136: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1243190 ofs=1384448
- <unknown>-12697 (-----) [007] .... 1920260.588143: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1300698 ofs=1388544
- <unknown>-12697 (-----) [007] .... 1920260.588144: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1482568 ofs=1392640
- <unknown>-12697 (-----) [007] .... 1920260.588144: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1461789 ofs=1396736
- <unknown>-12697 (-----) [007] .... 1920260.588145: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1242314 ofs=1400832
- <unknown>-12697 (-----) [007] .... 1920260.588145: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1471996 ofs=1404928
- <unknown>-12697 (-----) [007] .... 1920260.588146: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1242742 ofs=1409024
- <unknown>-12697 (-----) [007] .... 1920260.588146: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1242579 ofs=1413120
- <unknown>-12697 (-----) [007] .... 1920260.588148: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1242553 ofs=1417216
- <unknown>-12697 (-----) [007] .... 1920260.588148: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1457332 ofs=1421312
- <unknown>-12697 (-----) [007] .... 1920260.588149: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1315431 ofs=1425408
- <unknown>-12697 (-----) [007] .... 1920260.588149: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1080653 ofs=1429504
- <unknown>-12697 (-----) [007] .... 1920260.588149: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1324174 ofs=1433600
- <unknown>-12697 (-----) [007] .... 1920260.588150: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1324142 ofs=1437696
- <unknown>-12697 (-----) [007] .... 1920260.588150: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1157760 ofs=1441792
- <unknown>-12697 (-----) [007] .... 1920260.588151: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1075059 ofs=1445888
- <unknown>-12683 (-----) [006] .... 1920260.589785: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1279192 ofs=1486848
- <unknown>-12683 (-----) [006] .... 1920260.589790: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1278527 ofs=1490944
- <unknown>-12683 (-----) [006] .... 1920260.589791: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1091778 ofs=1495040
- <unknown>-12683 (-----) [006] .... 1920260.589791: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1339447 ofs=1499136
- <unknown>-12683 (-----) [006] .... 1920260.589792: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1254007 ofs=1503232
- <unknown>-12683 (-----) [006] .... 1920260.589793: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1115173 ofs=1507328
- <unknown>-12683 (-----) [006] .... 1920260.589793: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1393985 ofs=1511424
- <unknown>-12683 (-----) [006] .... 1920260.589794: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1369123 ofs=1515520
- <unknown>-12683 (-----) [006] .... 1920260.589794: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1314257 ofs=1519616
- <unknown>-12683 (-----) [006] .... 1920260.589802: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1404487 ofs=1523712
- <unknown>-12683 (-----) [006] .... 1920260.589803: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1354554 ofs=1527808
- <unknown>-12683 (-----) [006] .... 1920260.594312: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1141445 ofs=9801728
+ <unknown>-12683 (-----) [004] .... 1920260.584304: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1347409 ofs=0
+ <unknown>-12683 (-----) [004] .... 1920260.584307: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1428681 ofs=4096
+ <unknown>-12683 (-----) [004] .... 1920260.584307: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1259106 ofs=8192
+ <unknown>-12683 (-----) [004] .... 1920260.584308: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1343229 ofs=12288
+ <unknown>-12694 (-----) [005] .... 1920260.584622: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1098733 ofs=1531904
+ <unknown>-12696 (-----) [006] .... 1920260.584626: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1331319 ofs=1536000
+ <unknown>-12694 (-----) [005] .... 1920260.584626: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1278537 ofs=1540096
+ <unknown>-12696 (-----) [006] .... 1920260.584631: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1492534 ofs=1544192
+ <unknown>-12694 (-----) [005] .... 1920260.584636: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1460878 ofs=1548288
+ <unknown>-12694 (-----) [005] .... 1920260.584640: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1092973 ofs=1552384
+ <unknown>-12694 (-----) [005] .... 1920260.584641: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1103200 ofs=1556480
+ <unknown>-12694 (-----) [005] .... 1920260.584642: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1257426 ofs=1560576
+ <unknown>-12694 (-----) [005] .... 1920260.584642: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1219424 ofs=1564672
+ <unknown>-12683 (-----) [004] .... 1920260.584660: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1279352 ofs=1568768
+ <unknown>-12696 (-----) [006] .... 1920260.584662: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1260572 ofs=1572864
+ <unknown>-12683 (-----) [004] .... 1920260.584663: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1225809 ofs=1576960
+ <unknown>-12696 (-----) [006] .... 1920260.584665: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1350766 ofs=1585152
+ <unknown>-12697 (-----) [007] .... 1920260.584666: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1107173 ofs=1581056
+ <unknown>-12683 (-----) [004] .... 1920260.584668: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1305885 ofs=1589248
+ <unknown>-12694 (-----) [005] .... 1920260.584669: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1293385 ofs=1593344
+ <unknown>-12696 (-----) [006] .... 1920260.584670: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1173841 ofs=1597440
+ <unknown>-12697 (-----) [007] .... 1920260.584670: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1080021 ofs=1601536
+ <unknown>-12683 (-----) [004] .... 1920260.584673: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1147419 ofs=1605632
+ <unknown>-12696 (-----) [006] .... 1920260.584673: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1252762 ofs=1609728
+ <unknown>-12694 (-----) [005] .... 1920260.584674: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1323916 ofs=1613824
+ <unknown>-12683 (-----) [004] .... 1920260.584675: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1155631 ofs=1617920
+ <unknown>-12696 (-----) [006] .... 1920260.584676: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1449815 ofs=1622016
+ <unknown>-12694 (-----) [005] .... 1920260.584678: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1227069 ofs=1626112
+ <unknown>-12696 (-----) [006] .... 1920260.584680: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1317692 ofs=1630208
+ <unknown>-12694 (-----) [005] .... 1920260.584681: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1492244 ofs=1634304
+ <unknown>-12683 (-----) [004] .... 1920260.584682: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1241876 ofs=1638400
+ <unknown>-12697 (-----) [007] .... 1920260.585446: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1402958 ofs=167936
+ <unknown>-12697 (-----) [007] .... 1920260.585449: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1133263 ofs=172032
+ <unknown>-12697 (-----) [007] .... 1920260.585450: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1295502 ofs=176128
+ <unknown>-12697 (-----) [007] .... 1920260.585450: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1249495 ofs=180224
+ <unknown>-12697 (-----) [007] .... 1920260.585451: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1237999 ofs=184320
+ <unknown>-12697 (-----) [007] .... 1920260.585451: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1280965 ofs=188416
+ <unknown>-12697 (-----) [007] .... 1920260.585454: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1208361 ofs=192512
+ <unknown>-12697 (-----) [007] .... 1920260.585454: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1308840 ofs=196608
+ <unknown>-12695 (-----) [004] .... 1920260.585455: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1138875 ofs=569344
+ <unknown>-12695 (-----) [004] .... 1920260.585458: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1314886 ofs=573440
+ <unknown>-12697 (-----) [007] .... 1920260.585458: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1242734 ofs=200704
+ <unknown>-12695 (-----) [004] .... 1920260.585458: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1447386 ofs=577536
+ <unknown>-12697 (-----) [007] .... 1920260.585459: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1241302 ofs=204800
+ <unknown>-12695 (-----) [004] .... 1920260.585459: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1328663 ofs=581632
+ <unknown>-12697 (-----) [007] .... 1920260.585459: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1476101 ofs=208896
+ <unknown>-12695 (-----) [004] .... 1920260.585460: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1209461 ofs=585728
+ <unknown>-12697 (-----) [007] .... 1920260.585460: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1080147 ofs=212992
+ <unknown>-12697 (-----) [007] .... 1920260.585461: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1128509 ofs=217088
+ <unknown>-12697 (-----) [007] .... 1920260.585461: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1371915 ofs=221184
+ <unknown>-12697 (-----) [007] .... 1920260.585461: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1264015 ofs=225280
+ <unknown>-12697 (-----) [007] .... 1920260.585462: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1211695 ofs=229376
+ <unknown>-12697 (-----) [007] .... 1920260.585462: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1150386 ofs=233472
+ <unknown>-12697 (-----) [007] .... 1920260.585463: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1135747 ofs=237568
+ <unknown>-12697 (-----) [007] .... 1920260.585463: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1128230 ofs=241664
+ <unknown>-12697 (-----) [007] .... 1920260.585464: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1155451 ofs=245760
+ <unknown>-12697 (-----) [007] .... 1920260.585465: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1246841 ofs=249856
+ <unknown>-12697 (-----) [007] .... 1920260.585465: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1462971 ofs=253952
+ <unknown>-12697 (-----) [007] .... 1920260.585466: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1131333 ofs=258048
+ <unknown>-12697 (-----) [007] .... 1920260.585466: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1289407 ofs=262144
+ <unknown>-12695 (-----) [004] .... 1920260.585467: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1134730 ofs=589824
+ <unknown>-12697 (-----) [007] .... 1920260.585467: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1289873 ofs=266240
+ <unknown>-12697 (-----) [007] .... 1920260.585468: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1448734 ofs=270336
+ <unknown>-12695 (-----) [004] .... 1920260.585468: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1129776 ofs=593920
+ <unknown>-12697 (-----) [007] .... 1920260.585468: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1524090 ofs=274432
+ <unknown>-12695 (-----) [004] .... 1920260.585468: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1399725 ofs=598016
+ <unknown>-12697 (-----) [007] .... 1920260.585469: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1524081 ofs=278528
+ <unknown>-12695 (-----) [004] .... 1920260.585469: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1276535 ofs=602112
+ <unknown>-12697 (-----) [007] .... 1920260.585469: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1524060 ofs=282624
+ <unknown>-12695 (-----) [004] .... 1920260.585470: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1449847 ofs=606208
+ <unknown>-12697 (-----) [007] .... 1920260.585470: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1158944 ofs=286720
+ <unknown>-12695 (-----) [004] .... 1920260.585470: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1384536 ofs=610304
+ <unknown>-12697 (-----) [007] .... 1920260.585470: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1116785 ofs=290816
+ <unknown>-12695 (-----) [004] .... 1920260.585471: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1308118 ofs=614400
+ <unknown>-12697 (-----) [007] .... 1920260.585471: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1448669 ofs=294912
+ <unknown>-12695 (-----) [004] .... 1920260.585471: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1227050 ofs=618496
+ <unknown>-12695 (-----) [004] .... 1920260.585473: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1289324 ofs=622592
+ <unknown>-12695 (-----) [004] .... 1920260.585473: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1187869 ofs=626688
+ <unknown>-12695 (-----) [004] .... 1920260.585474: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1400523 ofs=630784
+ <unknown>-12695 (-----) [004] .... 1920260.585474: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1344176 ofs=634880
+ <unknown>-12695 (-----) [004] .... 1920260.585475: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1092871 ofs=638976
+ <unknown>-12695 (-----) [004] .... 1920260.585475: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1092021 ofs=643072
+ <unknown>-12695 (-----) [004] .... 1920260.585476: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1198169 ofs=647168
+ <unknown>-12695 (-----) [004] .... 1920260.585476: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1371540 ofs=651264
+ <unknown>-12683 (-----) [005] .... 1920260.585476: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1195003 ofs=348160
+ <unknown>-12695 (-----) [004] .... 1920260.585477: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1228787 ofs=655360
+ <unknown>-12695 (-----) [004] .... 1920260.585477: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1236123 ofs=659456
+ <unknown>-12695 (-----) [004] .... 1920260.585477: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1137213 ofs=663552
+ <unknown>-12695 (-----) [004] .... 1920260.585478: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1294618 ofs=667648
+ <unknown>-12695 (-----) [004] .... 1920260.585478: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1241048 ofs=671744
+ <unknown>-12695 (-----) [004] .... 1920260.585479: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1228779 ofs=675840
+ <unknown>-12683 (-----) [005] .... 1920260.585479: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1199292 ofs=352256
+ <unknown>-12683 (-----) [005] .... 1920260.585480: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1200861 ofs=356352
+ <unknown>-12695 (-----) [004] .... 1920260.585480: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1309572 ofs=679936
+ <unknown>-12683 (-----) [005] .... 1920260.585480: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1215770 ofs=360448
+ <unknown>-12695 (-----) [004] .... 1920260.585481: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1409002 ofs=684032
+ <unknown>-12683 (-----) [005] .... 1920260.585481: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1151883 ofs=364544
+ <unknown>-12695 (-----) [004] .... 1920260.585481: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1103729 ofs=688128
+ <unknown>-12683 (-----) [005] .... 1920260.585482: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1468126 ofs=368640
+ <unknown>-12695 (-----) [004] .... 1920260.585482: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1162720 ofs=692224
+ <unknown>-12683 (-----) [005] .... 1920260.585482: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1251672 ofs=372736
+ <unknown>-12695 (-----) [004] .... 1920260.585482: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1199221 ofs=696320
+ <unknown>-12683 (-----) [005] .... 1920260.585483: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1283325 ofs=376832
+ <unknown>-12683 (-----) [005] .... 1920260.585483: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1190489 ofs=380928
+ <unknown>-12683 (-----) [005] .... 1920260.585484: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1489117 ofs=385024
+ <unknown>-12683 (-----) [005] .... 1920260.585484: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1273899 ofs=389120
+ <unknown>-12683 (-----) [005] .... 1920260.585485: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1274459 ofs=393216
+ <unknown>-12683 (-----) [005] .... 1920260.585486: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1316649 ofs=397312
+ <unknown>-12683 (-----) [005] .... 1920260.585491: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1375678 ofs=401408
+ <unknown>-12683 (-----) [005] .... 1920260.585491: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1483317 ofs=405504
+ <unknown>-12683 (-----) [005] .... 1920260.585492: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1240286 ofs=409600
+ <unknown>-12683 (-----) [005] .... 1920260.585492: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1131345 ofs=413696
+ <unknown>-12683 (-----) [005] .... 1920260.585493: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1200483 ofs=417792
+ <unknown>-12683 (-----) [005] .... 1920260.585493: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1384693 ofs=421888
+ <unknown>-12683 (-----) [005] .... 1920260.585493: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1161385 ofs=425984
+ <unknown>-12683 (-----) [005] .... 1920260.585494: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1452025 ofs=430080
+ <unknown>-12683 (-----) [005] .... 1920260.585495: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1253654 ofs=434176
+ <unknown>-12683 (-----) [005] .... 1920260.585495: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1116697 ofs=438272
+ <unknown>-12683 (-----) [005] .... 1920260.585495: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1432645 ofs=442368
+ <unknown>-12694 (-----) [006] .... 1920260.585495: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1337397 ofs=16384
+ <unknown>-12683 (-----) [005] .... 1920260.585496: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1304229 ofs=446464
+ <unknown>-12683 (-----) [005] .... 1920260.585496: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1419147 ofs=450560
+ <unknown>-12683 (-----) [005] .... 1920260.585498: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1349246 ofs=454656
+ <unknown>-12683 (-----) [005] .... 1920260.585499: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1128519 ofs=458752
+ <unknown>-12683 (-----) [005] .... 1920260.585499: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1125168 ofs=462848
+ <unknown>-12694 (-----) [006] .... 1920260.585509: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1081031 ofs=20480
+ <unknown>-12694 (-----) [006] .... 1920260.585509: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1293022 ofs=24576
+ <unknown>-12694 (-----) [006] .... 1920260.585510: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1113007 ofs=28672
+ <unknown>-12694 (-----) [006] .... 1920260.585510: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1339312 ofs=32768
+ <unknown>-12694 (-----) [006] .... 1920260.585511: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1412311 ofs=36864
+ <unknown>-12694 (-----) [006] .... 1920260.585511: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1260960 ofs=40960
+ <unknown>-12694 (-----) [006] .... 1920260.585512: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1189529 ofs=45056
+ <unknown>-12694 (-----) [006] .... 1920260.585512: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1412184 ofs=49152
+ <unknown>-12694 (-----) [006] .... 1920260.585513: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1481227 ofs=53248
+ <unknown>-12694 (-----) [006] .... 1920260.585513: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1455940 ofs=57344
+ <unknown>-12694 (-----) [006] .... 1920260.585514: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1299132 ofs=61440
+ <unknown>-12694 (-----) [006] .... 1920260.585514: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1337375 ofs=65536
+ <unknown>-12694 (-----) [006] .... 1920260.585529: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1328742 ofs=69632
+ <unknown>-12694 (-----) [006] .... 1920260.585529: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1315646 ofs=73728
+ <unknown>-12694 (-----) [006] .... 1920260.585531: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1225475 ofs=77824
+ <unknown>-12694 (-----) [006] .... 1920260.585531: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1146097 ofs=81920
+ <unknown>-12694 (-----) [006] .... 1920260.585532: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1318775 ofs=86016
+ <unknown>-12694 (-----) [006] .... 1920260.585532: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1448391 ofs=90112
+ <unknown>-12694 (-----) [006] .... 1920260.585532: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1441412 ofs=94208
+ <unknown>-12694 (-----) [006] .... 1920260.585533: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1138111 ofs=98304
+ <unknown>-12694 (-----) [006] .... 1920260.585533: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1143223 ofs=102400
+ <unknown>-12683 (-----) [005] .... 1920260.585534: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1079876 ofs=466944
+ <unknown>-12694 (-----) [006] .... 1920260.585534: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1447637 ofs=106496
+ <unknown>-12694 (-----) [006] .... 1920260.585534: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1220585 ofs=110592
+ <unknown>-12694 (-----) [006] .... 1920260.585535: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1449051 ofs=114688
+ <unknown>-12694 (-----) [006] .... 1920260.585535: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1313180 ofs=118784
+ <unknown>-12694 (-----) [006] .... 1920260.585535: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1313166 ofs=122880
+ <unknown>-12694 (-----) [006] .... 1920260.585536: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1313154 ofs=126976
+ <unknown>-12683 (-----) [005] .... 1920260.585536: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1218394 ofs=471040
+ <unknown>-12694 (-----) [006] .... 1920260.585536: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1144047 ofs=131072
+ <unknown>-12683 (-----) [005] .... 1920260.585537: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1218579 ofs=475136
+ <unknown>-12694 (-----) [006] .... 1920260.585543: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1241332 ofs=135168
+ <unknown>-12694 (-----) [006] .... 1920260.585543: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1097199 ofs=139264
+ <unknown>-12694 (-----) [006] .... 1920260.585545: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1214197 ofs=143360
+ <unknown>-12694 (-----) [006] .... 1920260.585645: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1197633 ofs=147456
+ <unknown>-12694 (-----) [006] .... 1920260.585647: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1311536 ofs=151552
+ <unknown>-12694 (-----) [006] .... 1920260.585647: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1322952 ofs=155648
+ <unknown>-12694 (-----) [006] .... 1920260.585647: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1346974 ofs=159744
+ <unknown>-12694 (-----) [006] .... 1920260.585648: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1257232 ofs=163840
+ <unknown>-12695 (-----) [004] .... 1920260.586355: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1204484 ofs=700416
+ <unknown>-12695 (-----) [004] .... 1920260.586357: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1326426 ofs=704512
+ <unknown>-12695 (-----) [004] .... 1920260.586358: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1151808 ofs=708608
+ <unknown>-12695 (-----) [004] .... 1920260.586358: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1209422 ofs=712704
+ <unknown>-12695 (-----) [004] .... 1920260.586359: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1408387 ofs=716800
+ <unknown>-12695 (-----) [004] .... 1920260.586359: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1197336 ofs=720896
+ <unknown>-12695 (-----) [004] .... 1920260.586363: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1205652 ofs=724992
+ <unknown>-12695 (-----) [004] .... 1920260.586363: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1133421 ofs=729088
+ <unknown>-12695 (-----) [004] .... 1920260.586364: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1092173 ofs=733184
+ <unknown>-12695 (-----) [004] .... 1920260.586365: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1124430 ofs=737280
+ <unknown>-12695 (-----) [004] .... 1920260.586365: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1143926 ofs=741376
+ <unknown>-12695 (-----) [004] .... 1920260.586366: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1090109 ofs=745472
+ <unknown>-12695 (-----) [004] .... 1920260.586366: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1102012 ofs=749568
+ <unknown>-12695 (-----) [004] .... 1920260.586367: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1154930 ofs=753664
+ <unknown>-12695 (-----) [004] .... 1920260.586368: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1132993 ofs=757760
+ <unknown>-12695 (-----) [004] .... 1920260.586369: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1430780 ofs=761856
+ <unknown>-12695 (-----) [004] .... 1920260.586369: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1197452 ofs=765952
+ <unknown>-12695 (-----) [004] .... 1920260.586369: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1075111 ofs=770048
+ <unknown>-12695 (-----) [004] .... 1920260.586370: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1275616 ofs=774144
+ <unknown>-12695 (-----) [004] .... 1920260.586370: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1444981 ofs=778240
+ <unknown>-12695 (-----) [004] .... 1920260.586371: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1452592 ofs=782336
+ <unknown>-12695 (-----) [004] .... 1920260.586374: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1102857 ofs=786432
+ <unknown>-12695 (-----) [004] .... 1920260.586376: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1406969 ofs=790528
+ <unknown>-12695 (-----) [004] .... 1920260.586378: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1522553 ofs=794624
+ <unknown>-12695 (-----) [004] .... 1920260.586378: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1260771 ofs=798720
+ <unknown>-12695 (-----) [004] .... 1920260.586379: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1474649 ofs=802816
+ <unknown>-12695 (-----) [004] .... 1920260.586379: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1268708 ofs=806912
+ <unknown>-12695 (-----) [004] .... 1920260.586379: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1346144 ofs=811008
+ <unknown>-12695 (-----) [004] .... 1920260.586380: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1081167 ofs=815104
+ <unknown>-12695 (-----) [004] .... 1920260.586380: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1137677 ofs=819200
+ <unknown>-12695 (-----) [004] .... 1920260.586381: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1161175 ofs=823296
+ <unknown>-12695 (-----) [004] .... 1920260.586381: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1461331 ofs=827392
+ <unknown>-12695 (-----) [004] .... 1920260.586492: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1347219 ofs=831488
+ <unknown>-12695 (-----) [004] .... 1920260.586494: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1290004 ofs=835584
+ <unknown>-12695 (-----) [004] .... 1920260.586494: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1299174 ofs=839680
+ <unknown>-12695 (-----) [004] .... 1920260.586496: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1317595 ofs=843776
+ <unknown>-12695 (-----) [004] .... 1920260.586496: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1484924 ofs=847872
+ <unknown>-12695 (-----) [004] .... 1920260.586497: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1169920 ofs=851968
+ <unknown>-12695 (-----) [004] .... 1920260.586501: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1359189 ofs=856064
+ <unknown>-12695 (-----) [004] .... 1920260.586501: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1307842 ofs=860160
+ <unknown>-12695 (-----) [004] .... 1920260.586502: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1237858 ofs=864256
+ <unknown>-12695 (-----) [004] .... 1920260.586502: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1189461 ofs=868352
+ <unknown>-12695 (-----) [004] .... 1920260.586503: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1223232 ofs=872448
+ <unknown>-12695 (-----) [004] .... 1920260.586503: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1104076 ofs=876544
+ <unknown>-12695 (-----) [004] .... 1920260.586504: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1079223 ofs=880640
+ <unknown>-12695 (-----) [004] .... 1920260.586504: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1092537 ofs=884736
+ <unknown>-12695 (-----) [004] .... 1920260.586505: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1353960 ofs=888832
+ <unknown>-12695 (-----) [004] .... 1920260.586505: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1346330 ofs=892928
+ <unknown>-12695 (-----) [004] .... 1920260.586506: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1345764 ofs=897024
+ <unknown>-12695 (-----) [004] .... 1920260.586507: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1363913 ofs=901120
+ <unknown>-12695 (-----) [004] .... 1920260.586508: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1319570 ofs=905216
+ <unknown>-12695 (-----) [004] .... 1920260.586508: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1367024 ofs=909312
+ <unknown>-12695 (-----) [004] .... 1920260.586508: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1333808 ofs=913408
+ <unknown>-12695 (-----) [004] .... 1920260.586509: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1158627 ofs=917504
+ <unknown>-12695 (-----) [004] .... 1920260.586509: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1300368 ofs=921600
+ <unknown>-12695 (-----) [004] .... 1920260.586510: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1245363 ofs=925696
+ <unknown>-12695 (-----) [004] .... 1920260.586510: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1345609 ofs=929792
+ <unknown>-12695 (-----) [004] .... 1920260.586510: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1393826 ofs=933888
+ <unknown>-12695 (-----) [004] .... 1920260.586511: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1200552 ofs=937984
+ <unknown>-12695 (-----) [004] .... 1920260.586511: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1170885 ofs=942080
+ <unknown>-12695 (-----) [004] .... 1920260.586512: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1536209 ofs=946176
+ <unknown>-12695 (-----) [004] .... 1920260.586512: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1189630 ofs=950272
+ <unknown>-12695 (-----) [004] .... 1920260.586513: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1121010 ofs=954368
+ <unknown>-12695 (-----) [004] .... 1920260.586514: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1324474 ofs=958464
+ <unknown>-12697 (-----) [007] .... 1920260.586578: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1129628 ofs=299008
+ <unknown>-12697 (-----) [007] .... 1920260.586579: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1307120 ofs=303104
+ <unknown>-12697 (-----) [007] .... 1920260.586580: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1347284 ofs=307200
+ <unknown>-12697 (-----) [007] .... 1920260.586580: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1312996 ofs=311296
+ <unknown>-12697 (-----) [007] .... 1920260.586581: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1170623 ofs=315392
+ <unknown>-12697 (-----) [007] .... 1920260.586581: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1359281 ofs=319488
+ <unknown>-12697 (-----) [007] .... 1920260.586582: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1180021 ofs=323584
+ <unknown>-12697 (-----) [007] .... 1920260.586582: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1195728 ofs=327680
+ <unknown>-12697 (-----) [007] .... 1920260.586582: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1163642 ofs=331776
+ <unknown>-12697 (-----) [007] .... 1920260.586587: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1152538 ofs=335872
+ <unknown>-12697 (-----) [007] .... 1920260.586589: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1345922 ofs=339968
+ <unknown>-12697 (-----) [007] .... 1920260.586589: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1343604 ofs=344064
+ <unknown>-12697 (-----) [007] .... 1920260.586721: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1399371 ofs=479232
+ <unknown>-12697 (-----) [007] .... 1920260.586723: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1106549 ofs=483328
+ <unknown>-12697 (-----) [007] .... 1920260.586724: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1331546 ofs=487424
+ <unknown>-12697 (-----) [007] .... 1920260.586724: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1299299 ofs=491520
+ <unknown>-12697 (-----) [007] .... 1920260.586725: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1288883 ofs=495616
+ <unknown>-12697 (-----) [007] .... 1920260.586725: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1399049 ofs=499712
+ <unknown>-12697 (-----) [007] .... 1920260.586726: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1146931 ofs=503808
+ <unknown>-12697 (-----) [007] .... 1920260.586726: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1296592 ofs=507904
+ <unknown>-12697 (-----) [007] .... 1920260.586727: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1468397 ofs=512000
+ <unknown>-12697 (-----) [007] .... 1920260.586727: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1215698 ofs=516096
+ <unknown>-12697 (-----) [007] .... 1920260.586727: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1177341 ofs=520192
+ <unknown>-12697 (-----) [007] .... 1920260.586731: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1189162 ofs=524288
+ <unknown>-12697 (-----) [007] .... 1920260.586732: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1435997 ofs=528384
+ <unknown>-12697 (-----) [007] .... 1920260.586732: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1209896 ofs=532480
+ <unknown>-12697 (-----) [007] .... 1920260.586733: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1255888 ofs=536576
+ <unknown>-12697 (-----) [007] .... 1920260.586734: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1234200 ofs=540672
+ <unknown>-12697 (-----) [007] .... 1920260.586734: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1422854 ofs=544768
+ <unknown>-12697 (-----) [007] .... 1920260.586735: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1435794 ofs=548864
+ <unknown>-12697 (-----) [007] .... 1920260.586735: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1236279 ofs=552960
+ <unknown>-12697 (-----) [007] .... 1920260.586736: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1485732 ofs=557056
+ <unknown>-12683 (-----) [005] .... 1920260.586743: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1417198 ofs=561152
+ <unknown>-12683 (-----) [005] .... 1920260.586746: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1469450 ofs=565248
+ <unknown>-12696 (-----) [004] .... 1920260.587465: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1489023 ofs=1040384
+ <unknown>-12696 (-----) [004] .... 1920260.587469: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1449498 ofs=1044480
+ <unknown>-12696 (-----) [004] .... 1920260.587469: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1447737 ofs=1048576
+ <unknown>-12696 (-----) [004] .... 1920260.587470: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1124530 ofs=1052672
+ <unknown>-12696 (-----) [004] .... 1920260.587476: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1246743 ofs=1056768
+ <unknown>-12696 (-----) [004] .... 1920260.587476: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1441927 ofs=1060864
+ <unknown>-12696 (-----) [004] .... 1920260.587477: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1280581 ofs=1064960
+ <unknown>-12696 (-----) [004] .... 1920260.587477: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1289438 ofs=1069056
+ <unknown>-12696 (-----) [004] .... 1920260.587477: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1464236 ofs=1073152
+ <unknown>-12696 (-----) [004] .... 1920260.587478: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1125808 ofs=1077248
+ <unknown>-12696 (-----) [004] .... 1920260.587478: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1329385 ofs=1081344
+ <unknown>-12696 (-----) [004] .... 1920260.587480: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1314093 ofs=1085440
+ <unknown>-12696 (-----) [004] .... 1920260.587480: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1201837 ofs=1089536
+ <unknown>-12696 (-----) [004] .... 1920260.587481: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1327734 ofs=1093632
+ <unknown>-12696 (-----) [004] .... 1920260.587481: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1406568 ofs=1097728
+ <unknown>-12696 (-----) [004] .... 1920260.587481: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1331873 ofs=1101824
+ <unknown>-12696 (-----) [004] .... 1920260.587482: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1331898 ofs=1105920
+ <unknown>-12696 (-----) [004] .... 1920260.587482: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1331917 ofs=1110016
+ <unknown>-12696 (-----) [004] .... 1920260.587483: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1332091 ofs=1114112
+ <unknown>-12696 (-----) [004] .... 1920260.587483: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1108186 ofs=1118208
+ <unknown>-12696 (-----) [004] .... 1920260.587486: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1182631 ofs=1122304
+ <unknown>-12696 (-----) [004] .... 1920260.587486: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1085941 ofs=1126400
+ <unknown>-12696 (-----) [004] .... 1920260.587487: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1433982 ofs=1130496
+ <unknown>-12696 (-----) [004] .... 1920260.587487: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1387028 ofs=1134592
+ <unknown>-12696 (-----) [004] .... 1920260.587488: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1353117 ofs=1138688
+ <unknown>-12696 (-----) [004] .... 1920260.587489: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1352364 ofs=1142784
+ <unknown>-12696 (-----) [004] .... 1920260.587489: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1144513 ofs=1146880
+ <unknown>-12696 (-----) [004] .... 1920260.587490: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1403984 ofs=1150976
+ <unknown>-12696 (-----) [004] .... 1920260.587490: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1278970 ofs=1155072
+ <unknown>-12696 (-----) [004] .... 1920260.587491: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1326743 ofs=1159168
+ <unknown>-12696 (-----) [004] .... 1920260.587491: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1221809 ofs=1163264
+ <unknown>-12696 (-----) [004] .... 1920260.587492: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1268668 ofs=1167360
+ <unknown>-12695 (-----) [005] .... 1920260.587502: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1074544 ofs=962560
+ <unknown>-12695 (-----) [005] .... 1920260.587506: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1074294 ofs=966656
+ <unknown>-12695 (-----) [005] .... 1920260.587506: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1075097 ofs=970752
+ <unknown>-12695 (-----) [005] .... 1920260.587507: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1162407 ofs=974848
+ <unknown>-12695 (-----) [005] .... 1920260.587507: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1141370 ofs=978944
+ <unknown>-12695 (-----) [005] .... 1920260.587508: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1306487 ofs=983040
+ <unknown>-12695 (-----) [005] .... 1920260.587508: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1306434 ofs=987136
+ <unknown>-12695 (-----) [005] .... 1920260.587514: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1306347 ofs=991232
+ <unknown>-12695 (-----) [005] .... 1920260.587514: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1306247 ofs=995328
+ <unknown>-12695 (-----) [005] .... 1920260.587515: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1306195 ofs=999424
+ <unknown>-12695 (-----) [005] .... 1920260.587516: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1306039 ofs=1003520
+ <unknown>-12695 (-----) [005] .... 1920260.587516: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1305983 ofs=1007616
+ <unknown>-12694 (-----) [006] .... 1920260.587701: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1216391 ofs=1171456
+ <unknown>-12694 (-----) [006] .... 1920260.587705: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1262462 ofs=1175552
+ <unknown>-12694 (-----) [006] .... 1920260.587706: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1358114 ofs=1179648
+ <unknown>-12694 (-----) [006] .... 1920260.587706: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1357898 ofs=1183744
+ <unknown>-12694 (-----) [006] .... 1920260.587707: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1237003 ofs=1187840
+ <unknown>-12694 (-----) [006] .... 1920260.587707: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1126319 ofs=1191936
+ <unknown>-12694 (-----) [006] .... 1920260.587708: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1415489 ofs=1196032
+ <unknown>-12694 (-----) [006] .... 1920260.587708: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1279558 ofs=1200128
+ <unknown>-12694 (-----) [006] .... 1920260.587708: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1434022 ofs=1204224
+ <unknown>-12694 (-----) [006] .... 1920260.587709: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1220130 ofs=1208320
+ <unknown>-12694 (-----) [006] .... 1920260.587710: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1163037 ofs=1212416
+ <unknown>-12694 (-----) [006] .... 1920260.587711: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1404501 ofs=1216512
+ <unknown>-12694 (-----) [006] .... 1920260.587711: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1406287 ofs=1220608
+ <unknown>-12697 (-----) [007] .... 1920260.588132: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1355143 ofs=1376256
+ <unknown>-12697 (-----) [007] .... 1920260.588136: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1213923 ofs=1380352
+ <unknown>-12697 (-----) [007] .... 1920260.588136: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1243190 ofs=1384448
+ <unknown>-12697 (-----) [007] .... 1920260.588143: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1300698 ofs=1388544
+ <unknown>-12697 (-----) [007] .... 1920260.588144: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1482568 ofs=1392640
+ <unknown>-12697 (-----) [007] .... 1920260.588144: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1461789 ofs=1396736
+ <unknown>-12697 (-----) [007] .... 1920260.588145: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1242314 ofs=1400832
+ <unknown>-12697 (-----) [007] .... 1920260.588145: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1471996 ofs=1404928
+ <unknown>-12697 (-----) [007] .... 1920260.588146: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1242742 ofs=1409024
+ <unknown>-12697 (-----) [007] .... 1920260.588146: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1242579 ofs=1413120
+ <unknown>-12697 (-----) [007] .... 1920260.588148: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1242553 ofs=1417216
+ <unknown>-12697 (-----) [007] .... 1920260.588148: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1457332 ofs=1421312
+ <unknown>-12697 (-----) [007] .... 1920260.588149: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1315431 ofs=1425408
+ <unknown>-12697 (-----) [007] .... 1920260.588149: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1080653 ofs=1429504
+ <unknown>-12697 (-----) [007] .... 1920260.588149: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1324174 ofs=1433600
+ <unknown>-12697 (-----) [007] .... 1920260.588150: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1324142 ofs=1437696
+ <unknown>-12697 (-----) [007] .... 1920260.588150: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1157760 ofs=1441792
+ <unknown>-12697 (-----) [007] .... 1920260.588151: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1075059 ofs=1445888
+ <unknown>-12683 (-----) [006] .... 1920260.589785: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1279192 ofs=1486848
+ <unknown>-12683 (-----) [006] .... 1920260.589790: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1278527 ofs=1490944
+ <unknown>-12683 (-----) [006] .... 1920260.589791: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1091778 ofs=1495040
+ <unknown>-12683 (-----) [006] .... 1920260.589791: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1339447 ofs=1499136
+ <unknown>-12683 (-----) [006] .... 1920260.589792: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1254007 ofs=1503232
+ <unknown>-12683 (-----) [006] .... 1920260.589793: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1115173 ofs=1507328
+ <unknown>-12683 (-----) [006] .... 1920260.589793: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1393985 ofs=1511424
+ <unknown>-12683 (-----) [006] .... 1920260.589794: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1369123 ofs=1515520
+ <unknown>-12683 (-----) [006] .... 1920260.589794: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1314257 ofs=1519616
+ <unknown>-12683 (-----) [006] .... 1920260.589802: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1404487 ofs=1523712
+ <unknown>-12683 (-----) [006] .... 1920260.589803: mm_filemap_add_to_page_cache: dev 0:64771 ino e745 page=0000000000000000 pfn=1354554 ofs=1527808
+ <unknown>-12683 (-----) [006] .... 1920260.594312: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1141445 ofs=9801728
<unknown>-12683 (-----) [006] .... 1920260.594322: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1323774 ofs=231460864
<unknown>-12683 (-----) [006] .... 1920260.594326: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1323772 ofs=10993664
- <unknown>-12683 (-----) [006] .... 1920260.595212: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1481305 ofs=9805824
- <unknown>-12683 (-----) [006] .... 1920260.595214: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1481306 ofs=9809920
- <unknown>-12683 (-----) [006] .... 1920260.595214: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1481316 ofs=9814016
- <unknown>-12683 (-----) [006] .... 1920260.595215: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1481340 ofs=9818112
- <unknown>-12683 (-----) [006] .... 1920260.595216: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1394587 ofs=9822208
- <unknown>-12683 (-----) [006] .... 1920260.595216: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1103455 ofs=9826304
- <unknown>-12683 (-----) [006] .... 1920260.595217: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1103271 ofs=9830400
- <unknown>-12683 (-----) [006] .... 1920260.595218: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1103168 ofs=9834496
- <unknown>-12683 (-----) [006] .... 1920260.595218: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1103145 ofs=9838592
- <unknown>-12683 (-----) [006] .... 1920260.595219: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1103115 ofs=9842688
- <unknown>-12683 (-----) [006] .... 1920260.595222: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1103057 ofs=9846784
- <unknown>-12683 (-----) [006] .... 1920260.595222: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1331958 ofs=9850880
- <unknown>-12683 (-----) [006] .... 1920260.595227: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1356305 ofs=9854976
- <unknown>-12683 (-----) [006] .... 1920260.595228: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1103708 ofs=9859072
- <unknown>-12683 (-----) [006] .... 1920260.595228: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1099286 ofs=9863168
- <unknown>-12683 (-----) [006] .... 1920260.595229: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1435190 ofs=9867264
- <unknown>-12683 (-----) [006] .... 1920260.595229: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1395504 ofs=9871360
- <unknown>-12683 (-----) [006] .... 1920260.595230: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1352916 ofs=9875456
- <unknown>-12683 (-----) [006] .... 1920260.595231: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1255529 ofs=9879552
- <unknown>-12683 (-----) [006] .... 1920260.595231: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1336145 ofs=9883648
- <unknown>-12683 (-----) [006] .... 1920260.595232: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1334143 ofs=9887744
- <unknown>-12683 (-----) [006] .... 1920260.595232: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1328548 ofs=9891840
- <unknown>-12683 (-----) [006] .... 1920260.595232: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1222215 ofs=9895936
- <unknown>-12683 (-----) [006] .... 1920260.595233: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1461056 ofs=9900032
- <unknown>-12683 (-----) [006] .... 1920260.595234: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1228276 ofs=9904128
- <unknown>-12683 (-----) [006] .... 1920260.595235: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1151188 ofs=9908224
- <unknown>-12683 (-----) [006] .... 1920260.595236: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1443605 ofs=9912320
- <unknown>-12683 (-----) [006] .... 1920260.595236: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1146821 ofs=9916416
- <unknown>-12683 (-----) [006] .... 1920260.595237: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1103669 ofs=9920512
- <unknown>-12683 (-----) [006] .... 1920260.595238: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1103744 ofs=9924608
- <unknown>-12683 (-----) [006] .... 1920260.595238: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1103868 ofs=9928704
- <unknown>-12683 (-----) [006] .... 1920260.595789: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1465942 ofs=15855616
+ <unknown>-12683 (-----) [006] .... 1920260.595212: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1481305 ofs=9805824
+ <unknown>-12683 (-----) [006] .... 1920260.595214: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1481306 ofs=9809920
+ <unknown>-12683 (-----) [006] .... 1920260.595214: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1481316 ofs=9814016
+ <unknown>-12683 (-----) [006] .... 1920260.595215: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1481340 ofs=9818112
+ <unknown>-12683 (-----) [006] .... 1920260.595216: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1394587 ofs=9822208
+ <unknown>-12683 (-----) [006] .... 1920260.595216: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1103455 ofs=9826304
+ <unknown>-12683 (-----) [006] .... 1920260.595217: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1103271 ofs=9830400
+ <unknown>-12683 (-----) [006] .... 1920260.595218: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1103168 ofs=9834496
+ <unknown>-12683 (-----) [006] .... 1920260.595218: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1103145 ofs=9838592
+ <unknown>-12683 (-----) [006] .... 1920260.595219: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1103115 ofs=9842688
+ <unknown>-12683 (-----) [006] .... 1920260.595222: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1103057 ofs=9846784
+ <unknown>-12683 (-----) [006] .... 1920260.595222: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1331958 ofs=9850880
+ <unknown>-12683 (-----) [006] .... 1920260.595227: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1356305 ofs=9854976
+ <unknown>-12683 (-----) [006] .... 1920260.595228: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1103708 ofs=9859072
+ <unknown>-12683 (-----) [006] .... 1920260.595228: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1099286 ofs=9863168
+ <unknown>-12683 (-----) [006] .... 1920260.595229: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1435190 ofs=9867264
+ <unknown>-12683 (-----) [006] .... 1920260.595229: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1395504 ofs=9871360
+ <unknown>-12683 (-----) [006] .... 1920260.595230: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1352916 ofs=9875456
+ <unknown>-12683 (-----) [006] .... 1920260.595231: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1255529 ofs=9879552
+ <unknown>-12683 (-----) [006] .... 1920260.595231: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1336145 ofs=9883648
+ <unknown>-12683 (-----) [006] .... 1920260.595232: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1334143 ofs=9887744
+ <unknown>-12683 (-----) [006] .... 1920260.595232: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1328548 ofs=9891840
+ <unknown>-12683 (-----) [006] .... 1920260.595232: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1222215 ofs=9895936
+ <unknown>-12683 (-----) [006] .... 1920260.595233: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1461056 ofs=9900032
+ <unknown>-12683 (-----) [006] .... 1920260.595234: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1228276 ofs=9904128
+ <unknown>-12683 (-----) [006] .... 1920260.595235: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1151188 ofs=9908224
+ <unknown>-12683 (-----) [006] .... 1920260.595236: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1443605 ofs=9912320
+ <unknown>-12683 (-----) [006] .... 1920260.595236: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1146821 ofs=9916416
+ <unknown>-12683 (-----) [006] .... 1920260.595237: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1103669 ofs=9920512
+ <unknown>-12683 (-----) [006] .... 1920260.595238: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1103744 ofs=9924608
+ <unknown>-12683 (-----) [006] .... 1920260.595238: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1103868 ofs=9928704
+ <unknown>-12683 (-----) [006] .... 1920260.595789: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1465942 ofs=15855616
<unknown>-12683 (-----) [006] .... 1920260.595792: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1323712 ofs=261189632
<unknown>-12683 (-----) [006] .... 1920260.595998: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1323701 ofs=262094848
- <unknown>-12683 (-----) [006] .... 1920260.596191: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1222287 ofs=15859712
- <unknown>-12683 (-----) [006] .... 1920260.596192: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1213146 ofs=15863808
- <unknown>-12683 (-----) [006] .... 1920260.596192: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1310396 ofs=15867904
- <unknown>-12683 (-----) [006] .... 1920260.596193: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1310177 ofs=15872000
- <unknown>-12683 (-----) [006] .... 1920260.596194: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1187914 ofs=15876096
- <unknown>-12683 (-----) [006] .... 1920260.596195: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1322409 ofs=15880192
- <unknown>-12683 (-----) [006] .... 1920260.596195: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1282484 ofs=15884288
- <unknown>-12683 (-----) [006] .... 1920260.596200: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1097245 ofs=15888384
- <unknown>-12683 (-----) [006] .... 1920260.596200: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1416816 ofs=15892480
- <unknown>-12683 (-----) [006] .... 1920260.596201: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1257125 ofs=15896576
- <unknown>-12683 (-----) [006] .... 1920260.596201: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1403527 ofs=15900672
- <unknown>-12683 (-----) [006] .... 1920260.596202: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1218006 ofs=15904768
- <unknown>-12683 (-----) [006] .... 1920260.596202: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1153893 ofs=15908864
- <unknown>-12683 (-----) [006] .... 1920260.596202: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1328023 ofs=15912960
- <unknown>-12683 (-----) [006] .... 1920260.596203: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1465412 ofs=15917056
- <unknown>-12683 (-----) [006] .... 1920260.596203: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1092448 ofs=15921152
- <unknown>-12683 (-----) [006] .... 1920260.596204: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1239220 ofs=15925248
- <unknown>-12683 (-----) [006] .... 1920260.596204: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1276491 ofs=15929344
- <unknown>-12683 (-----) [006] .... 1920260.596205: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1262240 ofs=15933440
- <unknown>-12683 (-----) [006] .... 1920260.596206: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1323793 ofs=15937536
- <unknown>-12683 (-----) [006] .... 1920260.596206: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1074937 ofs=15941632
- <unknown>-12683 (-----) [006] .... 1920260.596207: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1311157 ofs=15945728
- <unknown>-12683 (-----) [006] .... 1920260.596207: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1308442 ofs=15949824
- <unknown>-12683 (-----) [006] .... 1920260.596210: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1467709 ofs=15953920
- <unknown>-12683 (-----) [006] .... 1920260.596211: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1394299 ofs=15958016
- <unknown>-12683 (-----) [004] .... 1920260.612586: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1316156 ofs=344064
- <unknown>-12683 (-----) [004] .... 1920260.612591: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1406323 ofs=348160
- <unknown>-12683 (-----) [004] .... 1920260.612601: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1216972 ofs=352256
- <unknown>-12683 (-----) [004] .... 1920260.612605: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1271924 ofs=356352
- <unknown>-12683 (-----) [004] .... 1920260.612605: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1369225 ofs=360448
- <unknown>-12683 (-----) [004] .... 1920260.612608: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1318474 ofs=364544
- <unknown>-12683 (-----) [004] .... 1920260.612609: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1227283 ofs=368640
- <unknown>-12683 (-----) [004] .... 1920260.612613: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1364376 ofs=372736
- <unknown>-12683 (-----) [004] .... 1920260.612613: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1073400 ofs=376832
+ <unknown>-12683 (-----) [006] .... 1920260.596191: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1222287 ofs=15859712
+ <unknown>-12683 (-----) [006] .... 1920260.596192: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1213146 ofs=15863808
+ <unknown>-12683 (-----) [006] .... 1920260.596192: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1310396 ofs=15867904
+ <unknown>-12683 (-----) [006] .... 1920260.596193: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1310177 ofs=15872000
+ <unknown>-12683 (-----) [006] .... 1920260.596194: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1187914 ofs=15876096
+ <unknown>-12683 (-----) [006] .... 1920260.596195: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1322409 ofs=15880192
+ <unknown>-12683 (-----) [006] .... 1920260.596195: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1282484 ofs=15884288
+ <unknown>-12683 (-----) [006] .... 1920260.596200: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1097245 ofs=15888384
+ <unknown>-12683 (-----) [006] .... 1920260.596200: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1416816 ofs=15892480
+ <unknown>-12683 (-----) [006] .... 1920260.596201: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1257125 ofs=15896576
+ <unknown>-12683 (-----) [006] .... 1920260.596201: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1403527 ofs=15900672
+ <unknown>-12683 (-----) [006] .... 1920260.596202: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1218006 ofs=15904768
+ <unknown>-12683 (-----) [006] .... 1920260.596202: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1153893 ofs=15908864
+ <unknown>-12683 (-----) [006] .... 1920260.596202: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1328023 ofs=15912960
+ <unknown>-12683 (-----) [006] .... 1920260.596203: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1465412 ofs=15917056
+ <unknown>-12683 (-----) [006] .... 1920260.596203: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1092448 ofs=15921152
+ <unknown>-12683 (-----) [006] .... 1920260.596204: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1239220 ofs=15925248
+ <unknown>-12683 (-----) [006] .... 1920260.596204: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1276491 ofs=15929344
+ <unknown>-12683 (-----) [006] .... 1920260.596205: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1262240 ofs=15933440
+ <unknown>-12683 (-----) [006] .... 1920260.596206: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1323793 ofs=15937536
+ <unknown>-12683 (-----) [006] .... 1920260.596206: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1074937 ofs=15941632
+ <unknown>-12683 (-----) [006] .... 1920260.596207: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1311157 ofs=15945728
+ <unknown>-12683 (-----) [006] .... 1920260.596207: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1308442 ofs=15949824
+ <unknown>-12683 (-----) [006] .... 1920260.596210: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1467709 ofs=15953920
+ <unknown>-12683 (-----) [006] .... 1920260.596211: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1394299 ofs=15958016
+ <unknown>-12683 (-----) [004] .... 1920260.612586: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1316156 ofs=344064
+ <unknown>-12683 (-----) [004] .... 1920260.612591: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1406323 ofs=348160
+ <unknown>-12683 (-----) [004] .... 1920260.612601: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1216972 ofs=352256
+ <unknown>-12683 (-----) [004] .... 1920260.612605: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1271924 ofs=356352
+ <unknown>-12683 (-----) [004] .... 1920260.612605: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1369225 ofs=360448
+ <unknown>-12683 (-----) [004] .... 1920260.612608: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1318474 ofs=364544
+ <unknown>-12683 (-----) [004] .... 1920260.612609: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1227283 ofs=368640
+ <unknown>-12683 (-----) [004] .... 1920260.612613: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1364376 ofs=372736
+ <unknown>-12683 (-----) [004] .... 1920260.612613: mm_filemap_add_to_page_cache: dev 0:64771 ino 180a page=0000000000000000 pfn=1073400 ofs=376832
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index 4f6524e0528b..c380d291d573 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -25,6 +25,7 @@ cc_defaults {
"slicer",
],
static_libs: [
+ "libcutils",
"libtinyxml2",
"liblog",
"libutils",
diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc
index 499c42e2888b..48b44d0fc99b 100644
--- a/startop/view_compiler/dex_builder.cc
+++ b/startop/view_compiler/dex_builder.cc
@@ -161,7 +161,7 @@ void WriteTestDexFile(const string& filename) {
MethodBuilder method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int(), string_type})};
- Value result = method.MakeRegister();
+ LiveRegister result = method.AllocRegister();
MethodDeclData string_length =
dex_file.GetOrDeclareMethod(string_type, "length", Prototype{TypeDescriptor::Int()});
@@ -314,7 +314,7 @@ ir::EncodedMethod* MethodBuilder::Encode() {
CHECK(decl_->prototype != nullptr);
size_t const num_args =
decl_->prototype->param_types != nullptr ? decl_->prototype->param_types->types.size() : 0;
- code->registers = num_registers_ + num_args + kMaxScratchRegisters;
+ code->registers = NumRegisters() + num_args + kMaxScratchRegisters;
code->ins_count = num_args;
EncodeInstructions();
code->instructions = slicer::ArrayView<const ::dex::u2>(buffer_.data(), buffer_.size());
@@ -327,7 +327,20 @@ ir::EncodedMethod* MethodBuilder::Encode() {
return method;
}
-Value MethodBuilder::MakeRegister() { return Value::Local(num_registers_++); }
+LiveRegister MethodBuilder::AllocRegister() {
+ // Find a free register
+ for (size_t i = 0; i < register_liveness_.size(); ++i) {
+ if (!register_liveness_[i]) {
+ register_liveness_[i] = true;
+ return LiveRegister{&register_liveness_, i};
+ }
+ }
+
+ // If we get here, all the registers are in use, so we have to allocate a new
+ // one.
+ register_liveness_.push_back(true);
+ return LiveRegister{&register_liveness_, register_liveness_.size() - 1};
+}
Value MethodBuilder::MakeLabel() {
labels_.push_back({});
@@ -600,7 +613,7 @@ size_t MethodBuilder::RegisterValue(const Value& value) const {
if (value.is_register()) {
return value.value();
} else if (value.is_parameter()) {
- return value.value() + num_registers_ + kMaxScratchRegisters;
+ return value.value() + NumRegisters() + kMaxScratchRegisters;
}
CHECK(false && "Must be either a parameter or a register");
return 0;
diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h
index 292d6599c115..3924e77fab59 100644
--- a/startop/view_compiler/dex_builder.h
+++ b/startop/view_compiler/dex_builder.h
@@ -140,6 +140,29 @@ class Value {
constexpr Value(size_t value, Kind kind) : value_{value}, kind_{kind} {}
};
+// Represents an allocated register returned by MethodBuilder::AllocRegister
+class LiveRegister {
+ friend class MethodBuilder;
+
+ public:
+ LiveRegister(LiveRegister&& other) : liveness_{other.liveness_}, index_{other.index_} {
+ other.index_ = {};
+ };
+ ~LiveRegister() {
+ if (index_.has_value()) {
+ (*liveness_)[*index_] = false;
+ }
+ };
+
+ operator const Value() const { return Value::Local(*index_); }
+
+ private:
+ LiveRegister(std::vector<bool>* liveness, size_t index) : liveness_{liveness}, index_{index} {}
+
+ std::vector<bool>* const liveness_;
+ std::optional<size_t> index_;
+};
+
// A virtual instruction. We convert these to real instructions in MethodBuilder::Encode.
// Virtual instructions are needed to keep track of information that is not known until all of the
// code is generated. This information includes things like how many local registers are created and
@@ -178,7 +201,8 @@ class Instruction {
}
// For most instructions, which take some number of arguments and have an optional return value.
template <typename... T>
- static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest, T... args) {
+ static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest,
+ const T&... args) {
return Instruction{opcode, /*index_argument=*/0, /*result_is_object=*/false, dest, args...};
}
@@ -199,14 +223,14 @@ class Instruction {
template <typename... T>
static inline Instruction InvokeVirtualObject(size_t index_argument,
std::optional<const Value> dest, Value this_arg,
- T... args) {
+ const T&... args) {
return Instruction{
Op::kInvokeVirtual, index_argument, /*result_is_object=*/true, dest, this_arg, args...};
}
// For direct calls (basically, constructors).
template <typename... T>
static inline Instruction InvokeDirect(size_t index_argument, std::optional<const Value> dest,
- Value this_arg, T... args) {
+ Value this_arg, const T&... args) {
return Instruction{
Op::kInvokeDirect, index_argument, /*result_is_object=*/false, dest, this_arg, args...};
}
@@ -234,7 +258,7 @@ class Instruction {
// For static calls.
template <typename... T>
static inline Instruction InvokeInterface(size_t index_argument, std::optional<const Value> dest,
- T... args) {
+ const T&... args) {
return Instruction{
Op::kInvokeInterface, index_argument, /*result_is_object=*/false, dest, args...};
}
@@ -277,7 +301,7 @@ class Instruction {
template <typename... T>
inline Instruction(Op opcode, size_t index_argument, bool result_is_object,
- std::optional<const Value> dest, T... args)
+ std::optional<const Value> dest, const T&... args)
: opcode_{opcode},
index_argument_{index_argument},
result_is_object_{result_is_object},
@@ -309,10 +333,8 @@ class MethodBuilder {
// Encode the method into DEX format.
ir::EncodedMethod* Encode();
- // Create a new register to be used to storing values. Note that these are not SSA registers, like
- // might be expected in similar code generators. This does no liveness tracking or anything, so
- // it's up to the caller to reuse registers as appropriate.
- Value MakeRegister();
+ // Create a new register to be used to storing values.
+ LiveRegister AllocRegister();
Value MakeLabel();
@@ -329,7 +351,7 @@ class MethodBuilder {
void BuildConst4(Value target, int value);
void BuildConstString(Value target, const std::string& value);
template <typename... T>
- void BuildNew(Value target, TypeDescriptor type, Prototype constructor, T... args);
+ void BuildNew(Value target, TypeDescriptor type, Prototype constructor, const T&... args);
// TODO: add builders for more instructions
@@ -427,7 +449,7 @@ class MethodBuilder {
static_assert(num_regs <= kMaxScratchRegisters);
std::array<Value, num_regs> regs;
for (size_t i = 0; i < num_regs; ++i) {
- regs[i] = std::move(Value::Local(num_registers_ + i));
+ regs[i] = std::move(Value::Local(NumRegisters() + i));
}
return regs;
}
@@ -457,8 +479,9 @@ class MethodBuilder {
// around to make legal DEX code.
static constexpr size_t kMaxScratchRegisters = 5;
- // How many registers we've allocated
- size_t num_registers_{0};
+ size_t NumRegisters() const {
+ return register_liveness_.size();
+ }
// Stores information needed to back-patch a label once it is bound. We need to know the start of
// the instruction that refers to the label, and the offset to where the actual label value should
@@ -478,6 +501,8 @@ class MethodBuilder {
// During encoding, keep track of the largest number of arguments needed, so we can use it for our
// outs count
size_t max_args_{0};
+
+ std::vector<bool> register_liveness_;
};
// A helper to build class definitions.
@@ -576,7 +601,8 @@ class DexBuilder {
};
template <typename... T>
-void MethodBuilder::BuildNew(Value target, TypeDescriptor type, Prototype constructor, T... args) {
+void MethodBuilder::BuildNew(Value target, TypeDescriptor type, Prototype constructor,
+ const T&... args) {
MethodDeclData constructor_data{dex_->GetOrDeclareMethod(type, "<init>", constructor)};
// allocate the object
ir::Type* type_def = dex_->GetOrAddType(type.descriptor());
diff --git a/startop/view_compiler/dex_layout_compiler.cc b/startop/view_compiler/dex_layout_compiler.cc
index 8febfb71ecd1..cb820f8f20fb 100644
--- a/startop/view_compiler/dex_layout_compiler.cc
+++ b/startop/view_compiler/dex_layout_compiler.cc
@@ -22,76 +22,94 @@
namespace startop {
using android::base::StringPrintf;
+using dex::Instruction;
+using dex::LiveRegister;
+using dex::Prototype;
+using dex::TypeDescriptor;
+using dex::Value;
+
+namespace {
+// TODO: these are a bunch of static initializers, which we should avoid. See if
+// we can make them constexpr.
+const TypeDescriptor kAttributeSet = TypeDescriptor::FromClassname("android.util.AttributeSet");
+const TypeDescriptor kContext = TypeDescriptor::FromClassname("android.content.Context");
+const TypeDescriptor kLayoutInflater = TypeDescriptor::FromClassname("android.view.LayoutInflater");
+const TypeDescriptor kResources = TypeDescriptor::FromClassname("android.content.res.Resources");
+const TypeDescriptor kString = TypeDescriptor::FromClassname("java.lang.String");
+const TypeDescriptor kView = TypeDescriptor::FromClassname("android.view.View");
+const TypeDescriptor kViewGroup = TypeDescriptor::FromClassname("android.view.ViewGroup");
+const TypeDescriptor kXmlResourceParser =
+ TypeDescriptor::FromClassname("android.content.res.XmlResourceParser");
+} // namespace
DexViewBuilder::DexViewBuilder(dex::MethodBuilder* method)
: method_{method},
- context_{dex::Value::Parameter(0)},
- resid_{dex::Value::Parameter(1)},
- inflater_{method->MakeRegister()},
- xml_{method->MakeRegister()},
- attrs_{method->MakeRegister()},
- classname_tmp_{method->MakeRegister()},
- xml_next_{method->dex_file()->GetOrDeclareMethod(
- dex::TypeDescriptor::FromClassname("android.content.res.XmlResourceParser"), "next",
- dex::Prototype{dex::TypeDescriptor::Int()})},
+ context_{Value::Parameter(0)},
+ resid_{Value::Parameter(1)},
+ inflater_{method->AllocRegister()},
+ xml_{method->AllocRegister()},
+ attrs_{method->AllocRegister()},
+ classname_tmp_{method->AllocRegister()},
+ xml_next_{method->dex_file()->GetOrDeclareMethod(kXmlResourceParser, "next",
+ Prototype{TypeDescriptor::Int()})},
try_create_view_{method->dex_file()->GetOrDeclareMethod(
- dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"), "tryCreateView",
- dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.View"),
- dex::TypeDescriptor::FromClassname("android.view.View"),
- dex::TypeDescriptor::FromClassname("java.lang.String"),
- dex::TypeDescriptor::FromClassname("android.content.Context"),
- dex::TypeDescriptor::FromClassname("android.util.AttributeSet")})},
+ kLayoutInflater, "tryCreateView",
+ Prototype{kView, kView, kString, kContext, kAttributeSet})},
generate_layout_params_{method->dex_file()->GetOrDeclareMethod(
- dex::TypeDescriptor::FromClassname("android.view.ViewGroup"), "generateLayoutParams",
- dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams"),
- dex::TypeDescriptor::FromClassname("android.util.AttributeSet")})},
+ kViewGroup, "generateLayoutParams",
+ Prototype{TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams"),
+ kAttributeSet})},
add_view_{method->dex_file()->GetOrDeclareMethod(
- dex::TypeDescriptor::FromClassname("android.view.ViewGroup"), "addView",
- dex::Prototype{
- dex::TypeDescriptor::Void(),
- dex::TypeDescriptor::FromClassname("android.view.View"),
- dex::TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams")})},
- // The register stack starts with one register, which will be null for the root view.
- register_stack_{{method->MakeRegister()}} {}
-
-void DexViewBuilder::Start() {
- dex::DexBuilder* const dex = method_->dex_file();
-
- // LayoutInflater inflater = LayoutInflater.from(context);
- auto layout_inflater_from = dex->GetOrDeclareMethod(
- dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"),
- "from",
- dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"),
- dex::TypeDescriptor::FromClassname("android.content.Context")});
- method_->AddInstruction(
- dex::Instruction::InvokeStaticObject(layout_inflater_from.id, /*dest=*/inflater_, context_));
+ kViewGroup, "addView",
+ Prototype{TypeDescriptor::Void(),
+ kView,
+ TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams")})} {}
+
+void DexViewBuilder::BuildGetLayoutInflater(Value dest) {
+ // dest = LayoutInflater.from(context);
+ auto layout_inflater_from = method_->dex_file()->GetOrDeclareMethod(
+ kLayoutInflater, "from", Prototype{kLayoutInflater, kContext});
+ method_->AddInstruction(Instruction::InvokeStaticObject(layout_inflater_from.id, dest, context_));
+}
- // Resources res = context.getResources();
- auto context_type = dex::TypeDescriptor::FromClassname("android.content.Context");
- auto resources_type = dex::TypeDescriptor::FromClassname("android.content.res.Resources");
+void DexViewBuilder::BuildGetResources(Value dest) {
+ // dest = context.getResources();
auto get_resources =
- dex->GetOrDeclareMethod(context_type, "getResources", dex::Prototype{resources_type});
- method_->AddInstruction(dex::Instruction::InvokeVirtualObject(get_resources.id, xml_, context_));
-
- // XmlResourceParser xml = res.getLayout(resid);
- auto xml_resource_parser_type =
- dex::TypeDescriptor::FromClassname("android.content.res.XmlResourceParser");
- auto get_layout =
- dex->GetOrDeclareMethod(resources_type,
- "getLayout",
- dex::Prototype{xml_resource_parser_type, dex::TypeDescriptor::Int()});
- method_->AddInstruction(dex::Instruction::InvokeVirtualObject(get_layout.id, xml_, xml_, resid_));
-
- // AttributeSet attrs = Xml.asAttributeSet(xml);
- auto as_attribute_set = dex->GetOrDeclareMethod(
- dex::TypeDescriptor::FromClassname("android.util.Xml"),
+ method_->dex_file()->GetOrDeclareMethod(kContext, "getResources", Prototype{kResources});
+ method_->AddInstruction(Instruction::InvokeVirtualObject(get_resources.id, dest, context_));
+}
+
+void DexViewBuilder::BuildGetLayoutResource(Value dest, Value resources, Value resid) {
+ // dest = resources.getLayout(resid);
+ auto get_layout = method_->dex_file()->GetOrDeclareMethod(
+ kResources, "getLayout", Prototype{kXmlResourceParser, TypeDescriptor::Int()});
+ method_->AddInstruction(Instruction::InvokeVirtualObject(get_layout.id, dest, resources, resid));
+}
+
+void DexViewBuilder::BuildLayoutResourceToAttributeSet(dex::Value dest,
+ dex::Value layout_resource) {
+ // dest = Xml.asAttributeSet(layout_resource);
+ auto as_attribute_set = method_->dex_file()->GetOrDeclareMethod(
+ TypeDescriptor::FromClassname("android.util.Xml"),
"asAttributeSet",
- dex::Prototype{dex::TypeDescriptor::FromClassname("android.util.AttributeSet"),
- dex::TypeDescriptor::FromClassname("org.xmlpull.v1.XmlPullParser")});
- method_->AddInstruction(dex::Instruction::InvokeStaticObject(as_attribute_set.id, attrs_, xml_));
+ Prototype{kAttributeSet, TypeDescriptor::FromClassname("org.xmlpull.v1.XmlPullParser")});
+ method_->AddInstruction(
+ Instruction::InvokeStaticObject(as_attribute_set.id, dest, layout_resource));
+}
- // xml.next(); // start document
- method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_));
+void DexViewBuilder::BuildXmlNext() {
+ // xml_.next();
+ method_->AddInstruction(Instruction::InvokeInterface(xml_next_.id, {}, xml_));
+}
+
+void DexViewBuilder::Start() {
+ BuildGetLayoutInflater(/*dest=*/inflater_);
+ BuildGetResources(/*dest=*/xml_);
+ BuildGetLayoutResource(/*dest=*/xml_, /*resources=*/xml_, resid_);
+ BuildLayoutResourceToAttributeSet(/*dest=*/attrs_, /*layout_resource=*/xml_);
+
+ // Advance past start document tag
+ BuildXmlNext();
}
void DexViewBuilder::Finish() {}
@@ -107,58 +125,57 @@ std::string ResolveName(const std::string& name) {
}
} // namespace
+void DexViewBuilder::BuildTryCreateView(Value dest, Value parent, Value classname) {
+ // dest = inflater_.tryCreateView(parent, classname, context_, attrs_);
+ method_->AddInstruction(Instruction::InvokeVirtualObject(
+ try_create_view_.id, dest, inflater_, parent, classname, context_, attrs_));
+}
+
void DexViewBuilder::StartView(const std::string& name, bool is_viewgroup) {
bool const is_root_view = view_stack_.empty();
- // xml.next(); // start tag
- method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_));
+ // Advance to start tag
+ BuildXmlNext();
- dex::Value view = AcquireRegister();
+ LiveRegister view = AcquireRegister();
// try to create the view using the factories
method_->BuildConstString(classname_tmp_,
name); // TODO: the need to fully qualify the classname
if (is_root_view) {
- dex::Value null = AcquireRegister();
+ LiveRegister null = AcquireRegister();
method_->BuildConst4(null, 0);
- method_->AddInstruction(dex::Instruction::InvokeVirtualObject(
- try_create_view_.id, view, inflater_, null, classname_tmp_, context_, attrs_));
- ReleaseRegister();
+ BuildTryCreateView(/*dest=*/view, /*parent=*/null, classname_tmp_);
} else {
- method_->AddInstruction(dex::Instruction::InvokeVirtualObject(
- try_create_view_.id, view, inflater_, GetCurrentView(), classname_tmp_, context_, attrs_));
+ BuildTryCreateView(/*dest=*/view, /*parent=*/GetCurrentView(), classname_tmp_);
}
auto label = method_->MakeLabel();
// branch if not null
method_->AddInstruction(
- dex::Instruction::OpWithArgs(dex::Instruction::Op::kBranchNEqz, /*dest=*/{}, view, label));
+ Instruction::OpWithArgs(Instruction::Op::kBranchNEqz, /*dest=*/{}, view, label));
// If null, create the class directly.
method_->BuildNew(view,
- dex::TypeDescriptor::FromClassname(ResolveName(name)),
- dex::Prototype{dex::TypeDescriptor::Void(),
- dex::TypeDescriptor::FromClassname("android.content.Context"),
- dex::TypeDescriptor::FromClassname("android.util.AttributeSet")},
+ TypeDescriptor::FromClassname(ResolveName(name)),
+ Prototype{TypeDescriptor::Void(), kContext, kAttributeSet},
context_,
attrs_);
- method_->AddInstruction(
- dex::Instruction::OpWithArgs(dex::Instruction::Op::kBindLabel, /*dest=*/{}, label));
+ method_->AddInstruction(Instruction::OpWithArgs(Instruction::Op::kBindLabel, /*dest=*/{}, label));
if (is_viewgroup) {
// Cast to a ViewGroup so we can add children later.
- const ir::Type* view_group_def = method_->dex_file()->GetOrAddType(
- dex::TypeDescriptor::FromClassname("android.view.ViewGroup").descriptor());
- method_->AddInstruction(dex::Instruction::Cast(view, dex::Value::Type(view_group_def->orig_index)));
+ const ir::Type* view_group_def = method_->dex_file()->GetOrAddType(kViewGroup.descriptor());
+ method_->AddInstruction(Instruction::Cast(view, Value::Type(view_group_def->orig_index)));
}
if (!is_root_view) {
// layout_params = parent.generateLayoutParams(attrs);
- dex::Value layout_params{AcquireRegister()};
- method_->AddInstruction(dex::Instruction::InvokeVirtualObject(
+ LiveRegister layout_params{AcquireRegister()};
+ method_->AddInstruction(Instruction::InvokeVirtualObject(
generate_layout_params_.id, layout_params, GetCurrentView(), attrs_));
- view_stack_.push_back({view, layout_params});
+ view_stack_.push_back({std::move(view), std::move(layout_params)});
} else {
- view_stack_.push_back({view, {}});
+ view_stack_.push_back({std::move(view), {}});
}
}
@@ -167,40 +184,24 @@ void DexViewBuilder::FinishView() {
method_->BuildReturn(GetCurrentView(), /*is_object=*/true);
} else {
// parent.add(view, layout_params)
- method_->AddInstruction(dex::Instruction::InvokeVirtual(
+ method_->AddInstruction(Instruction::InvokeVirtual(
add_view_.id, /*dest=*/{}, GetParentView(), GetCurrentView(), GetCurrentLayoutParams()));
// xml.next(); // end tag
- method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_));
+ method_->AddInstruction(Instruction::InvokeInterface(xml_next_.id, {}, xml_));
}
PopViewStack();
}
-dex::Value DexViewBuilder::AcquireRegister() {
- top_register_++;
- if (register_stack_.size() == top_register_) {
- register_stack_.push_back(method_->MakeRegister());
- }
- return register_stack_[top_register_];
-}
-
-void DexViewBuilder::ReleaseRegister() { top_register_--; }
+LiveRegister DexViewBuilder::AcquireRegister() { return method_->AllocRegister(); }
-dex::Value DexViewBuilder::GetCurrentView() const { return view_stack_.back().view; }
-dex::Value DexViewBuilder::GetCurrentLayoutParams() const {
+Value DexViewBuilder::GetCurrentView() const { return view_stack_.back().view; }
+Value DexViewBuilder::GetCurrentLayoutParams() const {
return view_stack_.back().layout_params.value();
}
-dex::Value DexViewBuilder::GetParentView() const {
- return view_stack_[view_stack_.size() - 2].view;
-}
+Value DexViewBuilder::GetParentView() const { return view_stack_[view_stack_.size() - 2].view; }
void DexViewBuilder::PopViewStack() {
- const auto& top = view_stack_.back();
- // release the layout params if we have them
- if (top.layout_params.has_value()) {
- ReleaseRegister();
- }
// Unconditionally release the view register.
- ReleaseRegister();
view_stack_.pop_back();
}
diff --git a/startop/view_compiler/dex_layout_compiler.h b/startop/view_compiler/dex_layout_compiler.h
index 170a1a610297..a34ed1f0168e 100644
--- a/startop/view_compiler/dex_layout_compiler.h
+++ b/startop/view_compiler/dex_layout_compiler.h
@@ -79,36 +79,41 @@ class DexViewBuilder {
private:
// Accessors for the stack of views that are under construction.
- dex::Value AcquireRegister();
- void ReleaseRegister();
+ dex::LiveRegister AcquireRegister();
dex::Value GetCurrentView() const;
dex::Value GetCurrentLayoutParams() const;
dex::Value GetParentView() const;
void PopViewStack();
+ // Methods to simplify building different code fragments.
+ void BuildGetLayoutInflater(dex::Value dest);
+ void BuildGetResources(dex::Value dest);
+ void BuildGetLayoutResource(dex::Value dest, dex::Value resources, dex::Value resid);
+ void BuildLayoutResourceToAttributeSet(dex::Value dest, dex::Value layout_resource);
+ void BuildXmlNext();
+ void BuildTryCreateView(dex::Value dest, dex::Value parent, dex::Value classname);
+
dex::MethodBuilder* method_;
- // Registers used for code generation
+ // Parameters to the generated method
dex::Value const context_;
dex::Value const resid_;
- const dex::Value inflater_;
- const dex::Value xml_;
- const dex::Value attrs_;
- const dex::Value classname_tmp_;
+
+ // Registers used for code generation
+ const dex::LiveRegister inflater_;
+ const dex::LiveRegister xml_;
+ const dex::LiveRegister attrs_;
+ const dex::LiveRegister classname_tmp_;
const dex::MethodDeclData xml_next_;
const dex::MethodDeclData try_create_view_;
const dex::MethodDeclData generate_layout_params_;
const dex::MethodDeclData add_view_;
- // used for keeping track of which registers are in use
- size_t top_register_{0};
- std::vector<dex::Value> register_stack_;
-
// Keep track of the views currently in progress.
struct ViewEntry {
- dex::Value view;
- std::optional<dex::Value> layout_params;
+ dex::LiveRegister view;
+ std::optional<dex::LiveRegister> layout_params;
};
std::vector<ViewEntry> view_stack_;
};
diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc
index 6dedf24e290d..5dda59e3473f 100644
--- a/startop/view_compiler/dex_testcase_generator.cc
+++ b/startop/view_compiler/dex_testcase_generator.cc
@@ -47,7 +47,7 @@ void GenerateSimpleTestCases(const string& outdir) {
// int return5() { return 5; }
auto return5{cbuilder.CreateMethod("return5", Prototype{TypeDescriptor::Int()})};
{
- Value r{return5.MakeRegister()};
+ LiveRegister r{return5.AllocRegister()};
return5.BuildConst4(r, 5);
return5.BuildReturn(r);
}
@@ -57,9 +57,9 @@ void GenerateSimpleTestCases(const string& outdir) {
auto integer_type{TypeDescriptor::FromClassname("java.lang.Integer")};
auto returnInteger5{cbuilder.CreateMethod("returnInteger5", Prototype{integer_type})};
[&](MethodBuilder& method) {
- Value five{method.MakeRegister()};
+ LiveRegister five{method.AllocRegister()};
method.BuildConst4(five, 5);
- Value object{method.MakeRegister()};
+ LiveRegister object{method.AllocRegister()};
method.BuildNew(
object, integer_type, Prototype{TypeDescriptor::Void(), TypeDescriptor::Int()}, five);
method.BuildReturn(object, /*is_object=*/true);
@@ -80,7 +80,7 @@ void GenerateSimpleTestCases(const string& outdir) {
auto returnStringLength{
cbuilder.CreateMethod("returnStringLength", Prototype{TypeDescriptor::Int(), string_type})};
{
- Value result = returnStringLength.MakeRegister();
+ LiveRegister result = returnStringLength.AllocRegister();
returnStringLength.AddInstruction(
Instruction::InvokeVirtual(string_length.id, result, Value::Parameter(0)));
returnStringLength.BuildReturn(result);
@@ -91,7 +91,7 @@ void GenerateSimpleTestCases(const string& outdir) {
MethodBuilder returnIfZero{cbuilder.CreateMethod(
"returnIfZero", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
{
- Value resultIfZero{returnIfZero.MakeRegister()};
+ LiveRegister resultIfZero{returnIfZero.AllocRegister()};
Value else_target{returnIfZero.MakeLabel()};
returnIfZero.AddInstruction(Instruction::OpWithArgs(
Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
@@ -112,7 +112,7 @@ void GenerateSimpleTestCases(const string& outdir) {
MethodBuilder returnIfNotZero{cbuilder.CreateMethod(
"returnIfNotZero", Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
{
- Value resultIfNotZero{returnIfNotZero.MakeRegister()};
+ LiveRegister resultIfNotZero{returnIfNotZero.AllocRegister()};
Value else_target{returnIfNotZero.MakeLabel()};
returnIfNotZero.AddInstruction(Instruction::OpWithArgs(
Instruction::Op::kBranchNEqz, /*dest=*/{}, Value::Parameter(0), else_target));
@@ -148,8 +148,8 @@ void GenerateSimpleTestCases(const string& outdir) {
MethodBuilder backwardsBranch{
cbuilder.CreateMethod("backwardsBranch", Prototype{TypeDescriptor::Int()})};
[](MethodBuilder& method) {
- Value zero = method.MakeRegister();
- Value result = method.MakeRegister();
+ LiveRegister zero = method.AllocRegister();
+ LiveRegister result = method.AllocRegister();
Value labelA = method.MakeLabel();
Value labelB = method.MakeLabel();
method.BuildConst4(zero, 0);
@@ -177,7 +177,7 @@ void GenerateSimpleTestCases(const string& outdir) {
// public static String returnNull() { return null; }
MethodBuilder returnNull{cbuilder.CreateMethod("returnNull", Prototype{string_type})};
[](MethodBuilder& method) {
- Value zero = method.MakeRegister();
+ LiveRegister zero = method.AllocRegister();
method.BuildConst4(zero, 0);
method.BuildReturn(zero, /*is_object=*/true);
}(returnNull);
@@ -188,7 +188,7 @@ void GenerateSimpleTestCases(const string& outdir) {
// public static String makeString() { return "Hello, World!"; }
MethodBuilder makeString{cbuilder.CreateMethod("makeString", Prototype{string_type})};
[](MethodBuilder& method) {
- Value string = method.MakeRegister();
+ LiveRegister string = method.AllocRegister();
method.BuildConstString(string, "Hello, World!");
method.BuildReturn(string, /*is_object=*/true);
}(makeString);
@@ -200,7 +200,7 @@ void GenerateSimpleTestCases(const string& outdir) {
MethodBuilder returnStringIfZeroAB{
cbuilder.CreateMethod("returnStringIfZeroAB", Prototype{string_type, TypeDescriptor::Int()})};
[&](MethodBuilder& method) {
- Value resultIfZero{method.MakeRegister()};
+ LiveRegister resultIfZero{method.AllocRegister()};
Value else_target{method.MakeLabel()};
method.AddInstruction(Instruction::OpWithArgs(
Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
@@ -220,7 +220,7 @@ void GenerateSimpleTestCases(const string& outdir) {
MethodBuilder returnStringIfZeroBA{
cbuilder.CreateMethod("returnStringIfZeroBA", Prototype{string_type, TypeDescriptor::Int()})};
[&](MethodBuilder& method) {
- Value resultIfZero{method.MakeRegister()};
+ LiveRegister resultIfZero{method.AllocRegister()};
Value else_target{method.MakeLabel()};
method.AddInstruction(Instruction::OpWithArgs(
Instruction::Op::kBranchEqz, /*dest=*/{}, Value::Parameter(0), else_target));
@@ -244,7 +244,7 @@ void GenerateSimpleTestCases(const string& outdir) {
cbuilder.CreateMethod("invokeStaticReturnObject",
Prototype{string_type, TypeDescriptor::Int(), TypeDescriptor::Int()})};
[&](MethodBuilder& method) {
- Value result{method.MakeRegister()};
+ LiveRegister result{method.AllocRegister()};
MethodDeclData to_string{dex_file.GetOrDeclareMethod(
TypeDescriptor::FromClassname("java.lang.Integer"),
"toString",
@@ -260,7 +260,7 @@ void GenerateSimpleTestCases(const string& outdir) {
MethodBuilder invokeVirtualReturnObject{cbuilder.CreateMethod(
"invokeVirtualReturnObject", Prototype{string_type, string_type, TypeDescriptor::Int()})};
[&](MethodBuilder& method) {
- Value result{method.MakeRegister()};
+ LiveRegister result{method.AllocRegister()};
MethodDeclData substring{dex_file.GetOrDeclareMethod(
string_type, "substring", Prototype{string_type, TypeDescriptor::Int()})};
method.AddInstruction(Instruction::InvokeVirtualObject(
@@ -291,7 +291,7 @@ void GenerateSimpleTestCases(const string& outdir) {
[&](MethodBuilder& method) {
const ir::FieldDecl* field =
dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int());
- Value result{method.MakeRegister()};
+ LiveRegister result{method.AllocRegister()};
method.AddInstruction(Instruction::GetStaticField(field->orig_index, result));
method.BuildReturn(result, /*is_object=*/false);
method.Encode();
@@ -304,7 +304,7 @@ void GenerateSimpleTestCases(const string& outdir) {
[&](MethodBuilder& method) {
const ir::FieldDecl* field =
dex_file.GetOrAddField(test_class, "staticInteger", TypeDescriptor::Int());
- Value number{method.MakeRegister()};
+ LiveRegister number{method.AllocRegister()};
method.BuildConst4(number, 7);
method.AddInstruction(Instruction::SetStaticField(field->orig_index, number));
method.BuildReturn();
@@ -318,7 +318,7 @@ void GenerateSimpleTestCases(const string& outdir) {
[&](MethodBuilder& method) {
const ir::FieldDecl* field =
dex_file.GetOrAddField(test_class, "instanceField", TypeDescriptor::Int());
- Value result{method.MakeRegister()};
+ LiveRegister result{method.AllocRegister()};
method.AddInstruction(Instruction::GetField(field->orig_index, result, Value::Parameter(0)));
method.BuildReturn(result, /*is_object=*/false);
method.Encode();
@@ -331,7 +331,7 @@ void GenerateSimpleTestCases(const string& outdir) {
[&](MethodBuilder& method) {
const ir::FieldDecl* field =
dex_file.GetOrAddField(test_class, "instanceField", TypeDescriptor::Int());
- Value number{method.MakeRegister()};
+ LiveRegister number{method.AllocRegister()};
method.BuildConst4(number, 7);
method.AddInstruction(Instruction::SetField(field->orig_index, Value::Parameter(0), number));
method.BuildReturn();
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 9a5ba8bd7c02..f1fe5b16b24f 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -19,6 +19,7 @@ package android.telecom;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.net.Uri;
import android.os.Build;
@@ -119,6 +120,20 @@ public final class Call {
public static final int STATE_PULLING_CALL = 11;
/**
+ * The state of a call that is active with the network, but the audio from the call is
+ * being intercepted by an app on the local device. Telecom does not hold audio focus in this
+ * state, and the call will be invisible to the user except for a persistent notification.
+ */
+ public static final int STATE_AUDIO_PROCESSING = 12;
+
+ /**
+ * The state of a call that is being presented to the user after being in
+ * {@link #STATE_AUDIO_PROCESSING}. The call is still active with the network in this case, and
+ * Telecom will hold audio focus and play a ringtone if appropriate.
+ */
+ public static final int STATE_SIMULATED_RINGING = 13;
+
+ /**
* The key to retrieve the optional {@code PhoneAccount}s Telecom can bundle with its Call
* extras. Used to pass the phone accounts to display on the front end to the user in order to
* select phone accounts to (for example) place a call.
@@ -732,6 +747,7 @@ public final class Call {
}
/** {@hide} */
+ @TestApi
public String getTelecomCallId() {
return mTelecomCallId;
}
@@ -1498,6 +1514,49 @@ public final class Call {
}
/**
+ * Instructs Telecom to put the call into the background audio processing state.
+ *
+ * This method can be called either when the call is in {@link #STATE_RINGING} or
+ * {@link #STATE_ACTIVE}. After Telecom acknowledges the request by setting the call's state to
+ * {@link #STATE_AUDIO_PROCESSING}, your app may setup the audio paths with the audio stack in
+ * order to capture and play audio on the call stream.
+ *
+ * This method can only be called by the default dialer app.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ //@RequiresPermission(android.Manifest.permission.BACKGROUND_CALL_AUDIO)
+ public void enterBackgroundAudioProcessing() {
+ if (mState != STATE_ACTIVE && mState != STATE_RINGING) {
+ throw new IllegalStateException("Call must be active or ringing");
+ }
+ mInCallAdapter.enterBackgroundAudioProcessing(mTelecomCallId);
+ }
+
+ /**
+ * Instructs Telecom to come out of the background audio processing state requested by
+ * {@link #enterBackgroundAudioProcessing()} or from the call screening service.
+ *
+ * This method can only be called when the call is in {@link #STATE_AUDIO_PROCESSING}.
+ *
+ * @param shouldRing If true, Telecom will put the call into the
+ * {@link #STATE_SIMULATED_RINGING} state and notify other apps that there is
+ * a ringing call. Otherwise, the call will go into {@link #STATE_ACTIVE}
+ * immediately.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ //@RequiresPermission(android.Manifest.permission.BACKGROUND_CALL_AUDIO)
+ public void exitBackgroundAudioProcessing(boolean shouldRing) {
+ if (mState != STATE_AUDIO_PROCESSING) {
+ throw new IllegalStateException("Call must in the audio processing state");
+ }
+ mInCallAdapter.exitBackgroundAudioProcessing(mTelecomCallId, shouldRing);
+ }
+
+ /**
* Instructs this {@code Call} to play a dual-tone multi-frequency signaling (DTMF) tone.
*
* Any other currently playing DTMF tone in the specified call is immediately stopped.
@@ -2009,6 +2068,10 @@ public final class Call {
return "DISCONNECTING";
case STATE_SELECT_PHONE_ACCOUNT:
return "SELECT_PHONE_ACCOUNT";
+ case STATE_SIMULATED_RINGING:
+ return "SIMULATED_RINGING";
+ case STATE_AUDIO_PROCESSING:
+ return "AUDIO_PROCESSING";
default:
Log.w(Call.class, "Unknown state %d", state);
return "UNKNOWN";
@@ -2098,6 +2161,9 @@ public final class Call {
}
int state = parcelableCall.getState();
+ if (mTargetSdkVersion < Phone.SDK_VERSION_R && state == Call.STATE_SIMULATED_RINGING) {
+ state = Call.STATE_RINGING;
+ }
boolean stateChanged = mState != state;
if (stateChanged) {
mState = state;
diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java
index e4f8d118e7df..ef1c790dcc83 100644
--- a/telecomm/java/android/telecom/CallScreeningService.java
+++ b/telecomm/java/android/telecom/CallScreeningService.java
@@ -18,6 +18,8 @@ package android.telecom;
import android.annotation.NonNull;
import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
@@ -136,23 +138,30 @@ public abstract class CallScreeningService extends Service {
private final boolean mShouldSilenceCall;
private final boolean mShouldSkipCallLog;
private final boolean mShouldSkipNotification;
+ private final boolean mShouldScreenCallFurther;
private CallResponse(
boolean shouldDisallowCall,
boolean shouldRejectCall,
boolean shouldSilenceCall,
boolean shouldSkipCallLog,
- boolean shouldSkipNotification) {
+ boolean shouldSkipNotification,
+ boolean shouldScreenCallFurther) {
if (!shouldDisallowCall
&& (shouldRejectCall || shouldSkipCallLog || shouldSkipNotification)) {
throw new IllegalStateException("Invalid response state for allowed call.");
}
+ if (shouldDisallowCall && shouldScreenCallFurther) {
+ throw new IllegalStateException("Invalid response state for allowed call.");
+ }
+
mShouldDisallowCall = shouldDisallowCall;
mShouldRejectCall = shouldRejectCall;
mShouldSkipCallLog = shouldSkipCallLog;
mShouldSkipNotification = shouldSkipNotification;
mShouldSilenceCall = shouldSilenceCall;
+ mShouldScreenCallFurther = shouldScreenCallFurther;
}
/*
@@ -191,12 +200,22 @@ public abstract class CallScreeningService extends Service {
return mShouldSkipNotification;
}
+ /**
+ * @return Whether we should enter the {@link Call#STATE_AUDIO_PROCESSING} state to allow
+ * for further screening of the call.
+ * @hide
+ */
+ public boolean getShouldScreenCallFurther() {
+ return mShouldScreenCallFurther;
+ }
+
public static class Builder {
private boolean mShouldDisallowCall;
private boolean mShouldRejectCall;
private boolean mShouldSilenceCall;
private boolean mShouldSkipCallLog;
private boolean mShouldSkipNotification;
+ private boolean mShouldScreenCallFurther;
/**
* Sets whether the incoming call should be blocked.
@@ -252,13 +271,32 @@ public abstract class CallScreeningService extends Service {
return this;
}
+ /**
+ * Sets whether to request background audio processing so that the in-call service can
+ * screen the call further. If set to {@code true}, {@link #setDisallowCall} should be
+ * called with {@code false}, and all other parameters in this builder will be ignored.
+ *
+ * This request will only be honored if the {@link CallScreeningService} shares the same
+ * uid as the default dialer app. Otherwise, the call will go through as usual.
+ *
+ * @param shouldScreenCallFurther Whether to request further call screening.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public Builder setShouldScreenCallFurther(boolean shouldScreenCallFurther) {
+ mShouldScreenCallFurther = shouldScreenCallFurther;
+ return this;
+ }
+
public CallResponse build() {
return new CallResponse(
mShouldDisallowCall,
mShouldRejectCall,
mShouldSilenceCall,
mShouldSkipCallLog,
- mShouldSkipNotification);
+ mShouldSkipNotification,
+ mShouldScreenCallFurther);
}
}
}
@@ -336,6 +374,8 @@ public abstract class CallScreeningService extends Service {
new ComponentName(getPackageName(), getClass().getName()));
} else if (response.getSilenceCall()) {
mCallScreeningAdapter.silenceCall(callDetails.getTelecomCallId());
+ } else if (response.getShouldScreenCallFurther()) {
+ mCallScreeningAdapter.screenCallFurther(callDetails.getTelecomCallId());
} else {
mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
}
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index a3159eda301b..3e353d68e3a8 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -24,6 +24,7 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
import android.telecom.Connection.VideoProvider;
+import android.telephony.Annotation.RilRadioTechnology;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.util.ArraySet;
@@ -695,7 +696,7 @@ public abstract class Conference extends Conferenceable {
*
* @hide
*/
- public final void setCallRadioTech(@ServiceState.RilRadioTechnology int vrat) {
+ public final void setCallRadioTech(@RilRadioTechnology int vrat) {
putExtra(TelecomManager.EXTRA_CALL_NETWORK_TYPE,
ServiceState.rilRadioTechnologyToNetworkType(vrat));
}
@@ -708,7 +709,7 @@ public abstract class Conference extends Conferenceable {
*
* @hide
*/
- public final @ServiceState.RilRadioTechnology int getCallRadioTech() {
+ public final @RilRadioTechnology int getCallRadioTech() {
int voiceNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
Bundle extras = getExtras();
if (extras != null) {
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index c00998811329..2ee023ed0d2c 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -34,6 +34,7 @@ import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.telephony.Annotation.RilRadioTechnology;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.util.ArraySet;
@@ -266,8 +267,13 @@ public abstract class Connection extends Conferenceable {
/**
* Speed up audio setup for MT call.
+ * <p>
+ * Used for IMS calls to indicate that mobile-terminated (incoming) call audio setup should take
+ * place as soon as the device answers the call, but prior to it being connected. This is an
+ * optimization some IMS stacks depend on to ensure prompt setup of call audio.
* @hide
*/
+ @SystemApi
public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00040000;
/**
@@ -304,6 +310,7 @@ public abstract class Connection extends Conferenceable {
* device.
* @hide
*/
+ @SystemApi
public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 0x00200000;
/**
@@ -359,28 +366,40 @@ public abstract class Connection extends Conferenceable {
/**
* Indicates that the current device callback number should be shown.
- *
+ * <p>
+ * Supports Telephony calls where CDMA emergency callback mode is active.
* @hide
*/
+ @SystemApi
public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 1<<0;
/**
* Whether the call is a generic conference, where we do not know the precise state of
* participants in the conference (eg. on CDMA).
- *
+ * <p>
+ * Supports legacy telephony CDMA calls.
* @hide
*/
+ @SystemApi
public static final int PROPERTY_GENERIC_CONFERENCE = 1<<1;
/**
* Connection is using high definition audio.
- * @hide
+ * <p>
+ * Indicates that the {@link Connection} is using a "high definition" audio codec. This usually
+ * implies something like AMR wideband, but the interpretation of when a call is considered high
+ * definition is left to the {@link ConnectionService} to decide.
+ * <p>
+ * Translates to {@link android.telecom.Call.Details#PROPERTY_HIGH_DEF_AUDIO}.
*/
public static final int PROPERTY_HIGH_DEF_AUDIO = 1<<2;
/**
* Connection is using WIFI.
- * @hide
+ * <p>
+ * Used to indicate that a call is taking place over WIFI versus a carrier network.
+ * <p>
+ * Translates to {@link android.telecom.Call.Details#PROPERTY_WIFI}.
*/
public static final int PROPERTY_WIFI = 1<<3;
@@ -407,8 +426,12 @@ public abstract class Connection extends Conferenceable {
/**
* Indicates that the connection represents a downgraded IMS conference.
+ * <p>
+ * This property is set when an IMS conference undergoes SRVCC and is re-added to Telecom as a
+ * new entity to indicate that the new connection was a conference.
* @hide
*/
+ @SystemApi
public static final int PROPERTY_IS_DOWNGRADED_CONFERENCE = 1<<6;
/**
@@ -434,7 +457,9 @@ public abstract class Connection extends Conferenceable {
/**
* Set by the framework to indicate that the network has identified a Connection as an emergency
* call.
- * @hide
+ * <p>
+ * This is used for incoming (mobile-terminated) calls to indicate the call is from emergency
+ * services.
*/
public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 1 << 10;
@@ -442,8 +467,11 @@ public abstract class Connection extends Conferenceable {
* Set by the framework to indicate that a Conference or Connection is hosted by a device other
* than the current one. Used in scenarios where the conference originator is the remote device
* and the current device is a participant of that conference.
+ * <p>
+ * This property is specific to IMS conference calls originating in Telephony.
* @hide
*/
+ @SystemApi
public static final int PROPERTY_REMOTELY_HOSTED = 1 << 11;
//**********************************************************************************************
@@ -496,8 +524,12 @@ public abstract class Connection extends Conferenceable {
/**
* Boolean connection extra key on a {@link Connection} which indicates that adding an
* additional call is disallowed.
+ * <p>
+ * Used for mobile-network calls to identify scenarios where carrier requirements preclude
+ * adding another call at the current time.
* @hide
*/
+ @SystemApi
public static final String EXTRA_DISABLE_ADD_CALL =
"android.telecom.extra.DISABLE_ADD_CALL";
@@ -521,6 +553,9 @@ public abstract class Connection extends Conferenceable {
* The TelephonyCS will ALSO try to add the existing connection to Telecom, except with the
* ID it originally referred to the connection as. Thus Telecom needs to know that the
* Connection with ID {@code ConnMan@1} is really the same as {@code TelephonyCS@1}.
+ * <p>
+ * This is an internal Telecom framework concept and is not exposed outside of the Telecom
+ * framework.
* @hide
*/
public static final String EXTRA_ORIGINAL_CONNECTION_ID =
@@ -538,7 +573,6 @@ public abstract class Connection extends Conferenceable {
* Connection event used to inform Telecom that it should play the on hold tone. This is used
* to play a tone when the peer puts the current call on hold. Sent to Telecom via
* {@link #sendConnectionEvent(String, Bundle)}.
- * @hide
*/
public static final String EVENT_ON_HOLD_TONE_START =
"android.telecom.event.ON_HOLD_TONE_START";
@@ -547,7 +581,6 @@ public abstract class Connection extends Conferenceable {
* Connection event used to inform Telecom that it should stop the on hold tone. This is used
* to stop a tone when the peer puts the current call on hold. Sent to Telecom via
* {@link #sendConnectionEvent(String, Bundle)}.
- * @hide
*/
public static final String EVENT_ON_HOLD_TONE_END =
"android.telecom.event.ON_HOLD_TONE_END";
@@ -578,10 +611,9 @@ public abstract class Connection extends Conferenceable {
/**
* Connection event used to inform Telecom when a hold operation on a call has failed.
- * Not intended for use by the UI at this time.
+ * <p>
* Sent via {@link #sendConnectionEvent(String, Bundle)}. The {@link Bundle} parameter is
* expected to be null when this connection event is used.
- * @hide
*/
public static final String EVENT_CALL_HOLD_FAILED = "android.telecom.event.CALL_HOLD_FAILED";
@@ -591,7 +623,6 @@ public abstract class Connection extends Conferenceable {
* <p>
* Sent via {@link #sendConnectionEvent(String, Bundle)}. The {@link Bundle} parameter is
* expected to be null when this connection event is used.
- * @hide
*/
public static final String EVENT_MERGE_START = "android.telecom.event.MERGE_START";
@@ -601,7 +632,6 @@ public abstract class Connection extends Conferenceable {
* <p>
* Sent via {@link #sendConnectionEvent(String, Bundle)}. The {@link Bundle} parameter is
* expected to be null when this connection event is used.
- * @hide
*/
public static final String EVENT_MERGE_COMPLETE = "android.telecom.event.MERGE_COMPLETE";
@@ -613,7 +643,6 @@ public abstract class Connection extends Conferenceable {
* call is being held locally on the device. When a capable {@link ConnectionService} receives
* signalling to indicate that the remote party has put the call on hold, it can send this
* connection event.
- * @hide
*/
public static final String EVENT_CALL_REMOTELY_HELD =
"android.telecom.event.CALL_REMOTELY_HELD";
@@ -626,7 +655,6 @@ public abstract class Connection extends Conferenceable {
* call is being held locally on the device. When a capable {@link ConnectionService} receives
* signalling to indicate that the remote party has taken the call off hold, it can send this
* connection event.
- * @hide
*/
public static final String EVENT_CALL_REMOTELY_UNHELD =
"android.telecom.event.CALL_REMOTELY_UNHELD";
@@ -669,49 +697,6 @@ public abstract class Connection extends Conferenceable {
private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
/**
- * Whether the given capabilities support the specified capability.
- *
- * @param capabilities A capability bit field.
- * @param capability The capability to check capabilities for.
- * @return Whether the specified capability is supported.
- * @hide
- */
- public static boolean can(int capabilities, int capability) {
- return (capabilities & capability) == capability;
- }
-
- /**
- * Whether the capabilities of this {@code Connection} supports the specified capability.
- *
- * @param capability The capability to check capabilities for.
- * @return Whether the specified capability is supported.
- * @hide
- */
- public boolean can(int capability) {
- return can(mConnectionCapabilities, capability);
- }
-
- /**
- * Removes the specified capability from the set of capabilities of this {@code Connection}.
- *
- * @param capability The capability to remove from the set.
- * @hide
- */
- public void removeCapability(int capability) {
- mConnectionCapabilities &= ~capability;
- }
-
- /**
- * Adds the specified capability to the set of capabilities of this {@code Connection}.
- *
- * @param capability The capability to add to the set.
- * @hide
- */
- public void addCapability(int capability) {
- mConnectionCapabilities |= capability;
- }
-
- /**
* Renders a set of capability bits ({@code CAPABILITY_*}) as a human readable string.
*
* @param capabilities A capability bit field.
@@ -740,70 +725,75 @@ public abstract class Connection extends Conferenceable {
builder.append("Capabilities:");
}
- if (can(capabilities, CAPABILITY_HOLD)) {
+ if ((capabilities & CAPABILITY_HOLD) == CAPABILITY_HOLD) {
builder.append(isLong ? " CAPABILITY_HOLD" : " hld");
}
- if (can(capabilities, CAPABILITY_SUPPORT_HOLD)) {
+ if ((capabilities & CAPABILITY_SUPPORT_HOLD) == CAPABILITY_SUPPORT_HOLD) {
builder.append(isLong ? " CAPABILITY_SUPPORT_HOLD" : " sup_hld");
}
- if (can(capabilities, CAPABILITY_MERGE_CONFERENCE)) {
+ if ((capabilities & CAPABILITY_MERGE_CONFERENCE) == CAPABILITY_MERGE_CONFERENCE) {
builder.append(isLong ? " CAPABILITY_MERGE_CONFERENCE" : " mrg_cnf");
}
- if (can(capabilities, CAPABILITY_SWAP_CONFERENCE)) {
+ if ((capabilities & CAPABILITY_SWAP_CONFERENCE) == CAPABILITY_SWAP_CONFERENCE) {
builder.append(isLong ? " CAPABILITY_SWAP_CONFERENCE" : " swp_cnf");
}
- if (can(capabilities, CAPABILITY_RESPOND_VIA_TEXT)) {
+ if ((capabilities & CAPABILITY_RESPOND_VIA_TEXT) == CAPABILITY_RESPOND_VIA_TEXT) {
builder.append(isLong ? " CAPABILITY_RESPOND_VIA_TEXT" : " txt");
}
- if (can(capabilities, CAPABILITY_MUTE)) {
+ if ((capabilities & CAPABILITY_MUTE) == CAPABILITY_MUTE) {
builder.append(isLong ? " CAPABILITY_MUTE" : " mut");
}
- if (can(capabilities, CAPABILITY_MANAGE_CONFERENCE)) {
+ if ((capabilities & CAPABILITY_MANAGE_CONFERENCE) == CAPABILITY_MANAGE_CONFERENCE) {
builder.append(isLong ? " CAPABILITY_MANAGE_CONFERENCE" : " mng_cnf");
}
- if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_RX)) {
+ if ((capabilities & CAPABILITY_SUPPORTS_VT_LOCAL_RX) == CAPABILITY_SUPPORTS_VT_LOCAL_RX) {
builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_LOCAL_RX" : " VTlrx");
}
- if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_TX)) {
+ if ((capabilities & CAPABILITY_SUPPORTS_VT_LOCAL_TX) == CAPABILITY_SUPPORTS_VT_LOCAL_TX) {
builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_LOCAL_TX" : " VTltx");
}
- if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)) {
+ if ((capabilities & CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)
+ == CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL) {
builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL" : " VTlbi");
}
- if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_RX)) {
+ if ((capabilities & CAPABILITY_SUPPORTS_VT_REMOTE_RX) == CAPABILITY_SUPPORTS_VT_REMOTE_RX) {
builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_REMOTE_RX" : " VTrrx");
}
- if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_TX)) {
+ if ((capabilities & CAPABILITY_SUPPORTS_VT_REMOTE_TX) == CAPABILITY_SUPPORTS_VT_REMOTE_TX) {
builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_REMOTE_TX" : " VTrtx");
}
- if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) {
+ if ((capabilities & CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)
+ == CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL) {
builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL" : " VTrbi");
}
- if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) {
+ if ((capabilities & CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)
+ == CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO) {
builder.append(isLong ? " CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO" : " !v2a");
}
- if (can(capabilities, CAPABILITY_SPEED_UP_MT_AUDIO)) {
+ if ((capabilities & CAPABILITY_SPEED_UP_MT_AUDIO) == CAPABILITY_SPEED_UP_MT_AUDIO) {
builder.append(isLong ? " CAPABILITY_SPEED_UP_MT_AUDIO" : " spd_aud");
}
- if (can(capabilities, CAPABILITY_CAN_UPGRADE_TO_VIDEO)) {
+ if ((capabilities & CAPABILITY_CAN_UPGRADE_TO_VIDEO) == CAPABILITY_CAN_UPGRADE_TO_VIDEO) {
builder.append(isLong ? " CAPABILITY_CAN_UPGRADE_TO_VIDEO" : " a2v");
}
- if (can(capabilities, CAPABILITY_CAN_PAUSE_VIDEO)) {
+ if ((capabilities & CAPABILITY_CAN_PAUSE_VIDEO) == CAPABILITY_CAN_PAUSE_VIDEO) {
builder.append(isLong ? " CAPABILITY_CAN_PAUSE_VIDEO" : " paus_VT");
}
- if (can(capabilities, CAPABILITY_CONFERENCE_HAS_NO_CHILDREN)) {
+ if ((capabilities & CAPABILITY_CONFERENCE_HAS_NO_CHILDREN)
+ == CAPABILITY_CONFERENCE_HAS_NO_CHILDREN) {
builder.append(isLong ? " CAPABILITY_SINGLE_PARTY_CONFERENCE" : " 1p_cnf");
}
- if (can(capabilities, CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)) {
+ if ((capabilities & CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)
+ == CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION) {
builder.append(isLong ? " CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION" : " rsp_by_con");
}
- if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
+ if ((capabilities & CAPABILITY_CAN_PULL_CALL) == CAPABILITY_CAN_PULL_CALL) {
builder.append(isLong ? " CAPABILITY_CAN_PULL_CALL" : " pull");
}
- if (can(capabilities, CAPABILITY_SUPPORT_DEFLECT)) {
+ if ((capabilities & CAPABILITY_SUPPORT_DEFLECT) == CAPABILITY_SUPPORT_DEFLECT) {
builder.append(isLong ? " CAPABILITY_SUPPORT_DEFLECT" : " sup_def");
}
- if (can(capabilities, CAPABILITY_SUPPORTS_RTT_REMOTE)) {
+ if ((capabilities & CAPABILITY_SUPPORTS_RTT_REMOTE) == CAPABILITY_SUPPORTS_RTT_REMOTE) {
builder.append(isLong ? " CAPABILITY_SUPPORTS_RTT_REMOTE" : " sup_rtt");
}
builder.append("]");
@@ -838,43 +828,44 @@ public abstract class Connection extends Conferenceable {
builder.append("Properties:");
}
- if (can(properties, PROPERTY_SELF_MANAGED)) {
+ if ((properties & PROPERTY_SELF_MANAGED) == PROPERTY_SELF_MANAGED) {
builder.append(isLong ? " PROPERTY_SELF_MANAGED" : " self_mng");
}
- if (can(properties, PROPERTY_EMERGENCY_CALLBACK_MODE)) {
+ if ((properties & PROPERTY_EMERGENCY_CALLBACK_MODE) == PROPERTY_EMERGENCY_CALLBACK_MODE) {
builder.append(isLong ? " PROPERTY_EMERGENCY_CALLBACK_MODE" : " ecbm");
}
- if (can(properties, PROPERTY_HIGH_DEF_AUDIO)) {
+ if ((properties & PROPERTY_HIGH_DEF_AUDIO) == PROPERTY_HIGH_DEF_AUDIO) {
builder.append(isLong ? " PROPERTY_HIGH_DEF_AUDIO" : " HD");
}
- if (can(properties, PROPERTY_WIFI)) {
+ if ((properties & PROPERTY_WIFI) == PROPERTY_WIFI) {
builder.append(isLong ? " PROPERTY_WIFI" : " wifi");
}
- if (can(properties, PROPERTY_GENERIC_CONFERENCE)) {
+ if ((properties & PROPERTY_GENERIC_CONFERENCE) == PROPERTY_GENERIC_CONFERENCE) {
builder.append(isLong ? " PROPERTY_GENERIC_CONFERENCE" : " gen_conf");
}
- if (can(properties, PROPERTY_IS_EXTERNAL_CALL)) {
+ if ((properties & PROPERTY_IS_EXTERNAL_CALL) == PROPERTY_IS_EXTERNAL_CALL) {
builder.append(isLong ? " PROPERTY_IS_EXTERNAL_CALL" : " xtrnl");
}
- if (can(properties, PROPERTY_HAS_CDMA_VOICE_PRIVACY)) {
+ if ((properties & PROPERTY_HAS_CDMA_VOICE_PRIVACY) == PROPERTY_HAS_CDMA_VOICE_PRIVACY) {
builder.append(isLong ? " PROPERTY_HAS_CDMA_VOICE_PRIVACY" : " priv");
}
- if (can(properties, PROPERTY_IS_RTT)) {
+ if ((properties & PROPERTY_IS_RTT) == PROPERTY_IS_RTT) {
builder.append(isLong ? " PROPERTY_IS_RTT" : " rtt");
}
- if (can(properties, PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL)) {
+ if ((properties & PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL)
+ == PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL) {
builder.append(isLong ? " PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL" : " ecall");
}
- if (can(properties, PROPERTY_REMOTELY_HOSTED)) {
+ if ((properties & PROPERTY_REMOTELY_HOSTED) == PROPERTY_REMOTELY_HOSTED) {
builder.append(isLong ? " PROPERTY_REMOTELY_HOSTED" : " remote_hst");
}
@@ -904,16 +895,10 @@ public abstract class Connection extends Conferenceable {
public void onConferenceablesChanged(
Connection c, List<Conferenceable> conferenceables) {}
public void onConferenceChanged(Connection c, Conference conference) {}
- /** @hide */
- public void onConferenceParticipantsChanged(Connection c,
- List<ConferenceParticipant> participants) {}
- public void onConferenceStarted() {}
public void onConferenceMergeFailed(Connection c) {}
public void onExtrasChanged(Connection c, Bundle extras) {}
public void onExtrasRemoved(Connection c, List<String> keys) {}
public void onConnectionEvent(Connection c, String event, Bundle extras) {}
- /** @hide */
- public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {}
public void onAudioRouteChanged(Connection c, int audioRoute, String bluetoothAddress) {}
public void onRttInitiationSuccess(Connection c) {}
public void onRttInitiationFailure(Connection c, int reason) {}
@@ -1830,11 +1815,15 @@ public abstract class Connection extends Conferenceable {
/**
* Returns the Telecom internal call ID associated with this connection. Should only be used
* for debugging and tracing purposes.
+ * <p>
+ * Note: Access to the Telecom internal call ID is used for logging purposes only; this API is
+ * provided to facilitate debugging of the Telephony stack only.
*
- * @return The Telecom call ID.
+ * @return The Telecom call ID, or {@code null} if it was not set.
* @hide
*/
- public final String getTelecomCallId() {
+ @SystemApi
+ public final @Nullable String getTelecomCallId() {
return mTelecomCallId;
}
@@ -1883,9 +1872,8 @@ public abstract class Connection extends Conferenceable {
* {@link VideoProfile#STATE_RX_ENABLED}.
*
* @return The video state of the connection.
- * @hide
*/
- public final int getVideoState() {
+ public final @VideoProfile.VideoState int getVideoState() {
return mVideoState;
}
@@ -1941,11 +1929,16 @@ public abstract class Connection extends Conferenceable {
* Retrieves the connection start time of the {@code Connnection}, if specified. A value of
* {@link Conference#CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the
* start time of the conference.
+ * <p>
+ * Note: This is an implementation detail specific to IMS conference calls over a mobile
+ * network.
*
- * @return The time at which the {@code Connnection} was connected.
+ * @return The time at which the {@code Connnection} was connected. Will be a value as retrieved
+ * from {@link System#currentTimeMillis()}.
*
* @hide
*/
+ @SystemApi
public final long getConnectTimeMillis() {
return mConnectTimeMillis;
}
@@ -1954,27 +1947,33 @@ public abstract class Connection extends Conferenceable {
* Retrieves the connection start time of the {@link Connection}, if specified. A value of
* {@link Conference#CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the
* start time of the conference.
- *
+ * <p>
* Based on the value of {@link SystemClock#elapsedRealtime()}, which ensures that wall-clock
* changes do not impact the call duration.
+ * <p>
+ * Used internally in Telephony when migrating conference participant data for IMS conferences.
*
* @return The time at which the {@link Connection} was connected.
*
* @hide
*/
+ @SystemApi
public final long getConnectElapsedTimeMillis() {
return mConnectElapsedTimeMillis;
}
/**
* Returns RIL voice radio technology used for current connection.
+ * <p>
+ * Used by the Telephony {@link ConnectionService}.
*
* @return the RIL voice radio technology used for current connection,
* see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}.
*
* @hide
*/
- public final @ServiceState.RilRadioTechnology int getCallRadioTech() {
+ @SystemApi
+ public final @RilRadioTechnology int getCallRadioTech() {
int voiceNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
Bundle extras = getExtras();
if (extras != null) {
@@ -2053,11 +2052,16 @@ public abstract class Connection extends Conferenceable {
/**
* Sets the telecom call ID associated with this Connection. The Telecom Call ID should be used
* ONLY for debugging purposes.
+ * <p>
+ * Note: Access to the Telecom internal call ID is used for logging purposes only; this API is
+ * provided to facilitate debugging of the Telephony stack only. Changing the ID via this
+ * method does NOT change any functionality in Telephony or Telecom and impacts only logging.
*
* @param callId The telecom call ID.
* @hide
*/
- public void setTelecomCallId(String callId) {
+ @SystemApi
+ public void setTelecomCallId(@NonNull String callId) {
mTelecomCallId = callId;
}
@@ -2394,12 +2398,15 @@ public abstract class Connection extends Conferenceable {
/**
* Sets the time at which a call became active on this Connection. This is set only
* when a conference call becomes active on this connection.
+ * <p>
+ * Used by telephony to maintain calls associated with an IMS Conference.
*
* @param connectTimeMillis The connection time, in milliseconds. Should be set using a value
* obtained from {@link System#currentTimeMillis()}.
*
* @hide
*/
+ @SystemApi
public final void setConnectTimeMillis(long connectTimeMillis) {
mConnectTimeMillis = connectTimeMillis;
}
@@ -2407,27 +2414,37 @@ public abstract class Connection extends Conferenceable {
/**
* Sets the time at which a call became active on this Connection. This is set only
* when a conference call becomes active on this connection.
- *
+ * <p>
+ * Used by telephony to maintain calls associated with an IMS Conference.
* @param connectElapsedTimeMillis The connection time, in milliseconds. Stored in the format
* {@link SystemClock#elapsedRealtime()}.
*
* @hide
*/
+ @SystemApi
public final void setConnectionStartElapsedRealTime(long connectElapsedTimeMillis) {
mConnectElapsedTimeMillis = connectElapsedTimeMillis;
}
/**
* Sets RIL voice radio technology used for current connection.
+ * <p>
+ * This property is set by the Telephony {@link ConnectionService}.
*
* @param vrat the RIL Voice Radio Technology used for current connection,
* see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}.
*
* @hide
*/
- public final void setCallRadioTech(@ServiceState.RilRadioTechnology int vrat) {
- putExtra(TelecomManager.EXTRA_CALL_NETWORK_TYPE,
+ @SystemApi
+ public final void setCallRadioTech(@RilRadioTechnology int vrat) {
+ Bundle extras = getExtras();
+ if (extras == null) {
+ extras = new Bundle();
+ }
+ extras.putInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE,
ServiceState.rilRadioTechnologyToNetworkType(vrat));
+ putExtras(extras);
// Propagates the call radio technology to its parent {@link android.telecom.Conference}
// This action only covers non-IMS CS conference calls.
// For IMS PS call conference call, it can be updated via its host connection
@@ -2495,9 +2512,12 @@ public abstract class Connection extends Conferenceable {
}
/**
+ * Resets the CDMA connection time.
+ * <p>
+ * This is an implementation detail specific to legacy CDMA calls on mobile networks.
* @hide
- * Resets the cdma connection time.
*/
+ @SystemApi
public final void resetConnectionTime() {
for (Listener l : mListeners) {
l.onConnectionTimeReset(this);
@@ -2537,13 +2557,6 @@ public abstract class Connection extends Conferenceable {
}
/**
- * @hide
- */
- public final ConnectionService getConnectionService() {
- return mConnectionService;
- }
-
- /**
* Sets the conference that this connection is a part of. This will fail if the connection is
* already part of a conference. {@link #resetConference} to un-set the conference first.
*
@@ -2653,45 +2666,6 @@ public abstract class Connection extends Conferenceable {
}
/**
- * Adds a boolean extra to this {@code Connection}.
- *
- * @param key The extra key.
- * @param value The value.
- * @hide
- */
- public final void putExtra(String key, boolean value) {
- Bundle newExtras = new Bundle();
- newExtras.putBoolean(key, value);
- putExtras(newExtras);
- }
-
- /**
- * Adds an integer extra to this {@code Connection}.
- *
- * @param key The extra key.
- * @param value The value.
- * @hide
- */
- public final void putExtra(String key, int value) {
- Bundle newExtras = new Bundle();
- newExtras.putInt(key, value);
- putExtras(newExtras);
- }
-
- /**
- * Adds a string extra to this {@code Connection}.
- *
- * @param key The extra key.
- * @param value The value.
- * @hide
- */
- public final void putExtra(String key, String value) {
- Bundle newExtras = new Bundle();
- newExtras.putString(key, value);
- putExtras(newExtras);
- }
-
- /**
* Removes extras from this {@code Connection}.
*
* @param keys The keys of the extras to remove.
@@ -3257,53 +3231,16 @@ public abstract class Connection extends Conferenceable {
}
/**
- * Notifies listeners that the merge request failed.
- *
- * @hide
+ * Called by a {@link ConnectionService} to notify Telecom that a {@link Conference#onMerge()}
+ * request failed.
*/
- protected final void notifyConferenceMergeFailed() {
+ public final void notifyConferenceMergeFailed() {
for (Listener l : mListeners) {
l.onConferenceMergeFailed(this);
}
}
/**
- * Notifies listeners of a change to conference participant(s).
- *
- * @param conferenceParticipants The participants.
- * @hide
- */
- protected final void updateConferenceParticipants(
- List<ConferenceParticipant> conferenceParticipants) {
- for (Listener l : mListeners) {
- l.onConferenceParticipantsChanged(this, conferenceParticipants);
- }
- }
-
- /**
- * Notifies listeners that a conference call has been started.
- * @hide
- */
- protected void notifyConferenceStarted() {
- for (Listener l : mListeners) {
- l.onConferenceStarted();
- }
- }
-
- /**
- * Notifies listeners when a change has occurred to the Connection which impacts its ability to
- * be a part of a conference call.
- * @param isConferenceSupported {@code true} if the connection supports being part of a
- * conference call, {@code false} otherwise.
- * @hide
- */
- protected void notifyConferenceSupportedChanged(boolean isConferenceSupported) {
- for (Listener l : mListeners) {
- l.onConferenceSupportedChanged(this, isConferenceSupported);
- }
- }
-
- /**
* Notifies listeners when phone account is changed. For example, when the PhoneAccount is
* changed due to an emergency call being redialed.
* @param pHandle The new PhoneAccountHandle for this connection.
@@ -3317,10 +3254,15 @@ public abstract class Connection extends Conferenceable {
/**
* Sets the {@link PhoneAccountHandle} associated with this connection.
+ * <p>
+ * Used by the Telephony {@link ConnectionService} to handle changes to the {@link PhoneAccount}
+ * which take place after call initiation (important for emergency calling scenarios).
*
+ * @param phoneAccountHandle the phone account handle to set.
* @hide
*/
- public void setPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) {
+ @SystemApi
+ public void setPhoneAccountHandle(@NonNull PhoneAccountHandle phoneAccountHandle) {
if (mPhoneAccountHandle != phoneAccountHandle) {
mPhoneAccountHandle = phoneAccountHandle;
notifyPhoneAccountChanged(phoneAccountHandle);
@@ -3329,10 +3271,16 @@ public abstract class Connection extends Conferenceable {
/**
* Returns the {@link PhoneAccountHandle} associated with this connection.
+ * <p>
+ * Used by the Telephony {@link ConnectionService} to handle changes to the {@link PhoneAccount}
+ * which take place after call initiation (important for emergency calling scenarios).
*
+ * @return the phone account handle specified via
+ * {@link #setPhoneAccountHandle(PhoneAccountHandle)}, or {@code null} if none was set.
* @hide
*/
- public PhoneAccountHandle getPhoneAccountHandle() {
+ @SystemApi
+ public @Nullable PhoneAccountHandle getPhoneAccountHandle() {
return mPhoneAccountHandle;
}
@@ -3389,9 +3337,14 @@ public abstract class Connection extends Conferenceable {
/**
* Sets the direction of this connection.
+ * <p>
+ * Used when calling {@link ConnectionService#addExistingConnection} to specify the existing
+ * call direction.
+ *
* @param callDirection The direction of this connection.
* @hide
*/
+ @SystemApi
public void setCallDirection(@Call.Details.CallDirection int callDirection) {
mCallDirection = callDirection;
}
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index 8678e33f68b6..261246818f1d 100644
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -16,8 +16,8 @@
package android.telecom;
-import android.net.Uri;
import android.bluetooth.BluetoothDevice;
+import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
@@ -149,6 +149,26 @@ public final class InCallAdapter {
}
/**
+ * @see Call#enterBackgroundAudioProcessing()
+ */
+ public void enterBackgroundAudioProcessing(String callId) {
+ try {
+ mAdapter.enterBackgroundAudioProcessing(callId);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * @see Call#exitBackgroundAudioProcessing(boolean)
+ */
+ public void exitBackgroundAudioProcessing(String callId, boolean shouldRing) {
+ try {
+ mAdapter.exitBackgroundAudioProcessing(callId, shouldRing);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Request audio routing to a specific bluetooth device. Calling this method may result in
* the device routing audio to a different bluetooth device than the one specified. A list of
* available devices can be obtained via {@link CallAudioState#getSupportedBluetoothDevices()}
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index 0cc052ef340d..2ecdb3035685 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -21,7 +21,6 @@ import android.annotation.UnsupportedAppUsage;
import android.bluetooth.BluetoothDevice;
import android.os.Build;
import android.os.Bundle;
-import android.os.RemoteException;
import android.util.ArrayMap;
import java.util.Collections;
@@ -111,6 +110,10 @@ public final class Phone {
public void onSilenceRinger(Phone phone) { }
}
+ // TODO: replace all usages of this with the actual R constant from Build.VERSION_CODES
+ /** @hide */
+ public static final int SDK_VERSION_R = 30;
+
// A Map allows us to track each Call by its Telecom-specified call ID
private final Map<String, Call> mCallByTelecomCallId = new ArrayMap<>();
@@ -143,6 +146,12 @@ public final class Phone {
}
final void internalAddCall(ParcelableCall parcelableCall) {
+ if (mTargetSdkVersion < SDK_VERSION_R
+ && parcelableCall.getState() == Call.STATE_AUDIO_PROCESSING) {
+ Log.i(this, "Skipping adding audio processing call for sdk compatibility");
+ return;
+ }
+
Call call = new Call(this, parcelableCall.getId(), mInCallAdapter,
parcelableCall.getState(), mCallingPackage, mTargetSdkVersion);
mCallByTelecomCallId.put(parcelableCall.getId(), call);
@@ -150,7 +159,7 @@ public final class Phone {
checkCallTree(parcelableCall);
call.internalUpdate(parcelableCall, mCallByTelecomCallId);
fireCallAdded(call);
- }
+ }
final void internalRemoveCall(Call call) {
mCallByTelecomCallId.remove(call.internalGetCallId());
@@ -164,12 +173,28 @@ public final class Phone {
}
final void internalUpdateCall(ParcelableCall parcelableCall) {
- Call call = mCallByTelecomCallId.get(parcelableCall.getId());
- if (call != null) {
- checkCallTree(parcelableCall);
- call.internalUpdate(parcelableCall, mCallByTelecomCallId);
- }
- }
+ if (mTargetSdkVersion < SDK_VERSION_R
+ && parcelableCall.getState() == Call.STATE_AUDIO_PROCESSING) {
+ Log.i(this, "removing audio processing call during update for sdk compatibility");
+ Call call = mCallByTelecomCallId.get(parcelableCall.getId());
+ if (call != null) {
+ internalRemoveCall(call);
+ }
+ return;
+ }
+
+ Call call = mCallByTelecomCallId.get(parcelableCall.getId());
+ if (call != null) {
+ checkCallTree(parcelableCall);
+ call.internalUpdate(parcelableCall, mCallByTelecomCallId);
+ } else {
+ // This call may have come out of audio processing. Try adding it if our target sdk
+ // version is low enough.
+ if (mTargetSdkVersion < SDK_VERSION_R) {
+ internalAddCall(parcelableCall);
+ }
+ }
+ }
final void internalSetPostDialWait(String telecomId, String remaining) {
Call call = mCallByTelecomCallId.get(telecomId);
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index ce45355e325c..dc535be7a3dd 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -16,6 +16,7 @@ package android.telecom;
import android.Manifest;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressAutoDoc;
@@ -33,6 +34,7 @@ import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.telephony.Annotation.CallState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -1430,7 +1432,7 @@ public class TelecomManager {
* @hide
*/
@SystemApi
- public @TelephonyManager.CallState int getCallState() {
+ public @CallState int getCallState() {
try {
if (isServiceConnected()) {
return getTelecomService().getCallState();
@@ -1930,6 +1932,29 @@ public class TelecomManager {
return result;
}
+
+ /**
+ * Creates the {@link Intent} which can be used with {@link Context#startActivity(Intent)} to
+ * launch the activity for emergency dialer.
+ *
+ * @param number Optional number to call in emergency dialer
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ public Intent createLaunchEmergencyDialerIntent(@Nullable String number) {
+ ITelecomService service = getTelecomService();
+ Intent result = null;
+ if (service != null) {
+ try {
+ result = service.createLaunchEmergencyDialerIntent(number);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error createLaunchEmergencyDialerIntent", e);
+ }
+ }
+ return result;
+ }
+
/**
* Determines whether Telecom would permit an incoming call to be added via the
* {@link #addNewIncomingCall(PhoneAccountHandle, Bundle)} API for the specified
@@ -2077,12 +2102,13 @@ public class TelecomManager {
/**
* Handles {@link Intent#ACTION_CALL} intents trampolined from UserCallActivity.
* @param intent The {@link Intent#ACTION_CALL} intent to handle.
+ * @param callingPackageProxy The original package that called this before it was trampolined.
* @hide
*/
- public void handleCallIntent(Intent intent) {
+ public void handleCallIntent(Intent intent, String callingPackageProxy) {
try {
if (isServiceConnected()) {
- getTelecomService().handleCallIntent(intent);
+ getTelecomService().handleCallIntent(intent, callingPackageProxy);
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException handleCallIntent: " + e);
diff --git a/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl b/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl
index 3ee3285793c4..83c8f62bb3db 100644
--- a/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl
@@ -30,6 +30,8 @@ oneway interface ICallScreeningAdapter {
void silenceCall(String callId);
+ void screenCallFurther(String callId);
+
void disallowCall(
String callId,
boolean shouldReject,
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
index 57df5c1e548e..60745e40aa77 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -44,6 +44,10 @@ oneway interface IInCallAdapter {
void setAudioRoute(int route, String bluetoothAddress);
+ void enterBackgroundAudioProcessing(String callId);
+
+ void exitBackgroundAudioProcessing(String callId, boolean shouldRing);
+
void playDtmfTone(String callId, char digit);
void stopDtmfTone(String callId);
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 4fcda4d00883..24119885a119 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -261,6 +261,11 @@ interface ITelecomService {
**/
Intent createManageBlockedNumbersIntent();
+ /**
+ * @see TelecomServiceImpl#createLaunchEmergencyDialerIntent
+ */
+ Intent createLaunchEmergencyDialerIntent(in String number);
+
/**
* @see TelecomServiceImpl#isIncomingCallPermitted
*/
@@ -282,6 +287,11 @@ interface ITelecomService {
void acceptHandover(in Uri srcAddr, int videoState, in PhoneAccountHandle destAcct);
/**
+ * @see TelecomServiceImpl#setTestEmergencyPhoneAccountPackageNameFilter
+ */
+ void setTestEmergencyPhoneAccountPackageNameFilter(String packageName);
+
+ /**
* @see TelecomServiceImpl#isInEmergencyCall
*/
boolean isInEmergencyCall();
@@ -289,7 +299,7 @@ interface ITelecomService {
/**
* @see TelecomServiceImpl#handleCallIntent
*/
- void handleCallIntent(in Intent intent);
+ void handleCallIntent(in Intent intent, in String callingPackageProxy);
void setTestDefaultCallRedirectionApp(String packageName);
@@ -302,6 +312,11 @@ interface ITelecomService {
void setTestAutoModeApp(String packageName);
/**
+ * @see TelecomServiceImpl#setSystemDialer
+ */
+ void setSystemDialer(in ComponentName testComponentName);
+
+ /**
* @see TelecomServiceImpl#setTestDefaultDialer
*/
void setTestDefaultDialer(in String packageName);
diff --git a/telephony/OWNERS b/telephony/OWNERS
index 2236cba498aa..2a6e8deeb13e 100644
--- a/telephony/OWNERS
+++ b/telephony/OWNERS
@@ -13,4 +13,5 @@ satk@google.com
shuoq@google.com
refuhoo@google.com
paulye@google.com
-nazaninb@google.com \ No newline at end of file
+nazaninb@google.com
+sarahchin@google.com \ No newline at end of file
diff --git a/telephony/common/com/android/internal/telephony/HbpcdLookup.java b/telephony/common/com/android/internal/telephony/HbpcdLookup.java
new file mode 100644
index 000000000000..d9a3e725b6fb
--- /dev/null
+++ b/telephony/common/com/android/internal/telephony/HbpcdLookup.java
@@ -0,0 +1,124 @@
+/*
+**
+** Copyright 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 com.android.internal.telephony;
+
+import android.net.Uri;
+import android.provider.BaseColumns;
+
+/**
+ * @hide
+ */
+public class HbpcdLookup {
+ public static final String AUTHORITY = "hbpcd_lookup";
+
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY);
+
+ public static final String PATH_MCC_IDD = "idd";
+ public static final String PATH_MCC_LOOKUP_TABLE = "lookup";
+ public static final String PATH_MCC_SID_CONFLICT = "conflict";
+ public static final String PATH_MCC_SID_RANGE = "range";
+ public static final String PATH_NANP_AREA_CODE = "nanp";
+ public static final String PATH_ARBITRARY_MCC_SID_MATCH = "arbitrary";
+ public static final String PATH_USERADD_COUNTRY = "useradd";
+
+ public static final String ID = "_id";
+ public static final int IDINDEX = 0;
+
+ /**
+ * @hide
+ */
+ public static class MccIdd implements BaseColumns {
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY + "/" + PATH_MCC_IDD);
+ public static final String DEFAULT_SORT_ORDER = "MCC ASC";
+
+ public static final String MCC = "MCC";
+ public static final String IDD = "IDD";
+
+ }
+
+ /**
+ * @hide
+ */
+ public static class MccLookup implements BaseColumns {
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY + "/" + PATH_MCC_LOOKUP_TABLE);
+ public static final String DEFAULT_SORT_ORDER = "MCC ASC";
+
+ public static final String MCC = "MCC";
+ public static final String COUNTRY_CODE = "Country_Code";
+ public static final String COUNTRY_NAME = "Country_Name";
+ public static final String NDD = "NDD";
+ public static final String NANPS = "NANPS";
+ public static final String GMT_OFFSET_LOW = "GMT_Offset_Low";
+ public static final String GMT_OFFSET_HIGH = "GMT_Offset_High";
+ public static final String GMT_DST_LOW = "GMT_DST_Low";
+ public static final String GMT_DST_HIGH = "GMT_DST_High";
+
+ }
+
+ /**
+ * @hide
+ */
+ public static class MccSidConflicts implements BaseColumns {
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY + "/" + PATH_MCC_SID_CONFLICT);
+ public static final String DEFAULT_SORT_ORDER = "MCC ASC";
+
+ public static final String MCC = "MCC";
+ public static final String SID_CONFLICT = "SID_Conflict";
+
+ }
+
+ /**
+ * @hide
+ */
+ public static class MccSidRange implements BaseColumns {
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY + "/" + PATH_MCC_SID_RANGE);
+ public static final String DEFAULT_SORT_ORDER = "MCC ASC";
+
+ public static final String MCC = "MCC";
+ public static final String RANGE_LOW = "SID_Range_Low";
+ public static final String RANGE_HIGH = "SID_Range_High";
+ }
+
+ /**
+ * @hide
+ */
+ public static class ArbitraryMccSidMatch implements BaseColumns {
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY + "/" + PATH_ARBITRARY_MCC_SID_MATCH);
+ public static final String DEFAULT_SORT_ORDER = "MCC ASC";
+
+ public static final String MCC = "MCC";
+ public static final String SID = "SID";
+
+ }
+
+ /**
+ * @hide
+ */
+ public static class NanpAreaCode implements BaseColumns {
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY + "/" + PATH_NANP_AREA_CODE);
+ public static final String DEFAULT_SORT_ORDER = "Area_Code ASC";
+
+ public static final String AREA_CODE = "Area_Code";
+ }
+}
diff --git a/telephony/common/com/android/internal/telephony/HbpcdUtils.java b/telephony/common/com/android/internal/telephony/HbpcdUtils.java
new file mode 100644
index 000000000000..2f3194214be6
--- /dev/null
+++ b/telephony/common/com/android/internal/telephony/HbpcdUtils.java
@@ -0,0 +1,163 @@
+/*
+ * 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 com.android.internal.telephony;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.telephony.Rlog;
+
+import com.android.internal.telephony.HbpcdLookup.ArbitraryMccSidMatch;
+import com.android.internal.telephony.HbpcdLookup.MccIdd;
+import com.android.internal.telephony.HbpcdLookup.MccLookup;
+import com.android.internal.telephony.HbpcdLookup.MccSidConflicts;
+import com.android.internal.telephony.HbpcdLookup.MccSidRange;
+
+public final class HbpcdUtils {
+ private static final String LOG_TAG = "HbpcdUtils";
+ private static final boolean DBG = false;
+ private ContentResolver resolver = null;
+
+ public HbpcdUtils(Context context) {
+ resolver = context.getContentResolver();
+ }
+
+ /**
+ * Resolves the unknown MCC with SID and Timezone information.
+ */
+ public int getMcc(int sid, int tz, int DSTflag, boolean isNitzTimeZone) {
+ int tmpMcc = 0;
+
+ // check if SID exists in arbitrary_mcc_sid_match table.
+ // these SIDs are assigned to more than 1 operators, but they are known to
+ // be used by a specific operator, other operators having the same SID are
+ // not using it currently, if that SID is in this table, we don't need to
+ // check other tables.
+ String projection2[] = {ArbitraryMccSidMatch.MCC};
+ Cursor c2 = resolver.query(ArbitraryMccSidMatch.CONTENT_URI, projection2,
+ ArbitraryMccSidMatch.SID + "=" + sid, null, null);
+
+ if (c2 != null) {
+ int c2Counter = c2.getCount();
+ if (DBG) {
+ Rlog.d(LOG_TAG, "Query unresolved arbitrary table, entries are " + c2Counter);
+ }
+ if (c2Counter == 1) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, "Query Unresolved arbitrary returned the cursor " + c2);
+ }
+ c2.moveToFirst();
+ tmpMcc = c2.getInt(0);
+ if (DBG) {
+ Rlog.d(LOG_TAG, "MCC found in arbitrary_mcc_sid_match: " + tmpMcc);
+ }
+ c2.close();
+ return tmpMcc;
+ }
+ c2.close();
+ }
+
+ // Then check if SID exists in mcc_sid_conflict table.
+ // and use the timezone in mcc_lookup table to check which MCC matches.
+ String projection3[] = {MccSidConflicts.MCC};
+ Cursor c3 = resolver.query(MccSidConflicts.CONTENT_URI, projection3,
+ MccSidConflicts.SID_CONFLICT + "=" + sid + " and (((" +
+ MccLookup.GMT_OFFSET_LOW + "<=" + tz + ") and (" + tz + "<=" +
+ MccLookup.GMT_OFFSET_HIGH + ") and (" + "0=" + DSTflag + ")) or ((" +
+ MccLookup.GMT_DST_LOW + "<=" + tz + ") and (" + tz + "<=" +
+ MccLookup.GMT_DST_HIGH + ") and (" + "1=" + DSTflag + ")))",
+ null, null);
+ if (c3 != null) {
+ int c3Counter = c3.getCount();
+ if (c3Counter > 0) {
+ if (c3Counter > 1) {
+ Rlog.w(LOG_TAG, "something wrong, get more results for 1 conflict SID: " + c3);
+ }
+ if (DBG) Rlog.d(LOG_TAG, "Query conflict sid returned the cursor " + c3);
+ c3.moveToFirst();
+ tmpMcc = c3.getInt(0);
+ if (DBG) {
+ Rlog.d(LOG_TAG, "MCC found in mcc_lookup_table. Return tmpMcc = " + tmpMcc);
+ }
+ if (!isNitzTimeZone) {
+ // time zone is not accurate, it may get wrong mcc, ignore it.
+ if (DBG) {
+ Rlog.d(LOG_TAG, "time zone is not accurate, mcc may be " + tmpMcc);
+ }
+ tmpMcc = 0;
+ }
+ c3.close();
+ return tmpMcc;
+ } else {
+ c3.close();
+ }
+ }
+
+ // if there is no conflict, then check if SID is in mcc_sid_range.
+ String projection5[] = {MccSidRange.MCC};
+ Cursor c5 = resolver.query(MccSidRange.CONTENT_URI, projection5,
+ MccSidRange.RANGE_LOW + "<=" + sid + " and " +
+ MccSidRange.RANGE_HIGH + ">=" + sid,
+ null, null);
+ if (c5 != null) {
+ if (c5.getCount() > 0) {
+ if (DBG) Rlog.d(LOG_TAG, "Query Range returned the cursor " + c5);
+ c5.moveToFirst();
+ tmpMcc = c5.getInt(0);
+ if (DBG) Rlog.d(LOG_TAG, "SID found in mcc_sid_range. Return tmpMcc = " + tmpMcc);
+ c5.close();
+ return tmpMcc;
+ }
+ c5.close();
+ }
+ if (DBG) Rlog.d(LOG_TAG, "SID NOT found in mcc_sid_range.");
+
+ if (DBG) Rlog.d(LOG_TAG, "Exit getMccByOtherFactors. Return tmpMcc = " + tmpMcc);
+ // If unknown MCC still could not be resolved,
+ return tmpMcc;
+ }
+
+ /**
+ * Gets country information with given MCC.
+ */
+ public String getIddByMcc(int mcc) {
+ if (DBG) Rlog.d(LOG_TAG, "Enter getHbpcdInfoByMCC.");
+ String idd = "";
+
+ Cursor c = null;
+
+ String projection[] = {MccIdd.IDD};
+ Cursor cur = resolver.query(MccIdd.CONTENT_URI, projection,
+ MccIdd.MCC + "=" + mcc, null, null);
+ if (cur != null) {
+ if (cur.getCount() > 0) {
+ if (DBG) Rlog.d(LOG_TAG, "Query Idd returned the cursor " + cur);
+ // TODO: for those country having more than 1 IDDs, need more information
+ // to decide which IDD would be used. currently just use the first 1.
+ cur.moveToFirst();
+ idd = cur.getString(0);
+ if (DBG) Rlog.d(LOG_TAG, "IDD = " + idd);
+
+ }
+ cur.close();
+ }
+ if (c != null) c.close();
+
+ if (DBG) Rlog.d(LOG_TAG, "Exit getHbpcdInfoByMCC.");
+ return idd;
+ }
+}
diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java
new file mode 100644
index 000000000000..f4eae8ef4b2a
--- /dev/null
+++ b/telephony/common/com/android/internal/telephony/SmsApplication.java
@@ -0,0 +1,1077 @@
+/*
+ * 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.internal.telephony;
+
+import android.Manifest.permission;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.app.role.RoleManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Binder;
+import android.os.Debug;
+import android.os.Process;
+import android.os.UserHandle;
+import android.provider.Telephony;
+import android.provider.Telephony.Sms.Intents;
+import android.telephony.Rlog;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Consumer;
+
+/**
+ * Class for managing the primary application that we will deliver SMS/MMS messages to
+ *
+ * {@hide}
+ */
+public final class SmsApplication {
+ static final String LOG_TAG = "SmsApplication";
+ private static final String PHONE_PACKAGE_NAME = "com.android.phone";
+ private static final String BLUETOOTH_PACKAGE_NAME = "com.android.bluetooth";
+ private static final String MMS_SERVICE_PACKAGE_NAME = "com.android.mms.service";
+ private static final String TELEPHONY_PROVIDER_PACKAGE_NAME = "com.android.providers.telephony";
+
+ private static final String SCHEME_SMS = "sms";
+ private static final String SCHEME_SMSTO = "smsto";
+ private static final String SCHEME_MMS = "mms";
+ private static final String SCHEME_MMSTO = "mmsto";
+ private static final boolean DEBUG = false;
+ private static final boolean DEBUG_MULTIUSER = false;
+
+ private static final int[] DEFAULT_APP_EXCLUSIVE_APPOPS = {
+ AppOpsManager.OP_READ_SMS,
+ AppOpsManager.OP_WRITE_SMS,
+ AppOpsManager.OP_RECEIVE_SMS,
+ AppOpsManager.OP_RECEIVE_WAP_PUSH,
+ AppOpsManager.OP_SEND_SMS,
+ AppOpsManager.OP_READ_CELL_BROADCASTS
+ };
+
+ private static SmsPackageMonitor sSmsPackageMonitor = null;
+
+ public static class SmsApplicationData {
+ /**
+ * Name of this SMS app for display.
+ */
+ @UnsupportedAppUsage
+ private String mApplicationName;
+
+ /**
+ * Package name for this SMS app.
+ */
+ public String mPackageName;
+
+ /**
+ * The class name of the SMS_DELIVER_ACTION receiver in this app.
+ */
+ private String mSmsReceiverClass;
+
+ /**
+ * The class name of the WAP_PUSH_DELIVER_ACTION receiver in this app.
+ */
+ private String mMmsReceiverClass;
+
+ /**
+ * The class name of the ACTION_RESPOND_VIA_MESSAGE intent in this app.
+ */
+ private String mRespondViaMessageClass;
+
+ /**
+ * The class name of the ACTION_SENDTO intent in this app.
+ */
+ private String mSendToClass;
+
+ /**
+ * The class name of the ACTION_DEFAULT_SMS_PACKAGE_CHANGED receiver in this app.
+ */
+ private String mSmsAppChangedReceiverClass;
+
+ /**
+ * The class name of the ACTION_EXTERNAL_PROVIDER_CHANGE receiver in this app.
+ */
+ private String mProviderChangedReceiverClass;
+
+ /**
+ * The class name of the SIM_FULL_ACTION receiver in this app.
+ */
+ private String mSimFullReceiverClass;
+
+ /**
+ * The user-id for this application
+ */
+ private int mUid;
+
+ /**
+ * Returns true if this SmsApplicationData is complete (all intents handled).
+ * @return
+ */
+ public boolean isComplete() {
+ return (mSmsReceiverClass != null && mMmsReceiverClass != null
+ && mRespondViaMessageClass != null && mSendToClass != null);
+ }
+
+ public SmsApplicationData(String packageName, int uid) {
+ mPackageName = packageName;
+ mUid = uid;
+ }
+
+ public String getApplicationName(Context context) {
+ if (mApplicationName == null) {
+ PackageManager pm = context.getPackageManager();
+ ApplicationInfo appInfo;
+ try {
+ appInfo = pm.getApplicationInfoAsUser(mPackageName, 0,
+ UserHandle.getUserHandleForUid(mUid));
+ } catch (NameNotFoundException e) {
+ return null;
+ }
+ if (appInfo != null) {
+ CharSequence label = pm.getApplicationLabel(appInfo);
+ mApplicationName = (label == null) ? null : label.toString();
+ }
+ }
+ return mApplicationName;
+ }
+
+ @Override
+ public String toString() {
+ return " mPackageName: " + mPackageName
+ + " mSmsReceiverClass: " + mSmsReceiverClass
+ + " mMmsReceiverClass: " + mMmsReceiverClass
+ + " mRespondViaMessageClass: " + mRespondViaMessageClass
+ + " mSendToClass: " + mSendToClass
+ + " mSmsAppChangedClass: " + mSmsAppChangedReceiverClass
+ + " mProviderChangedReceiverClass: " + mProviderChangedReceiverClass
+ + " mSimFullReceiverClass: " + mSimFullReceiverClass
+ + " mUid: " + mUid;
+ }
+ }
+
+ /**
+ * Returns the userId of the Context object, if called from a system app,
+ * otherwise it returns the caller's userId
+ * @param context The context object passed in by the caller.
+ * @return
+ */
+ private static int getIncomingUserId(Context context) {
+ int contextUserId = UserHandle.myUserId();
+ final int callingUid = Binder.getCallingUid();
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "getIncomingUserHandle caller=" + callingUid + ", myuid="
+ + android.os.Process.myUid() + "\n\t" + Debug.getCallers(4));
+ }
+ if (UserHandle.getAppId(callingUid)
+ < android.os.Process.FIRST_APPLICATION_UID) {
+ return contextUserId;
+ } else {
+ return UserHandle.getUserId(callingUid);
+ }
+ }
+
+ /**
+ * Returns the list of available SMS apps defined as apps that are registered for both the
+ * SMS_RECEIVED_ACTION (SMS) and WAP_PUSH_RECEIVED_ACTION (MMS) broadcasts (and their broadcast
+ * receivers are enabled)
+ *
+ * Requirements to be an SMS application:
+ * Implement SMS_DELIVER_ACTION broadcast receiver.
+ * Require BROADCAST_SMS permission.
+ *
+ * Implement WAP_PUSH_DELIVER_ACTION broadcast receiver.
+ * Require BROADCAST_WAP_PUSH permission.
+ *
+ * Implement RESPOND_VIA_MESSAGE intent.
+ * Support smsto Uri scheme.
+ * Require SEND_RESPOND_VIA_MESSAGE permission.
+ *
+ * Implement ACTION_SENDTO intent.
+ * Support smsto Uri scheme.
+ */
+ @UnsupportedAppUsage
+ public static Collection<SmsApplicationData> getApplicationCollection(Context context) {
+ return getApplicationCollectionAsUser(context, getIncomingUserId(context));
+ }
+
+ /**
+ * Same as {@link #getApplicationCollection} but it takes a target user ID.
+ */
+ public static Collection<SmsApplicationData> getApplicationCollectionAsUser(Context context,
+ int userId) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getApplicationCollectionInternal(context, userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private static Collection<SmsApplicationData> getApplicationCollectionInternal(
+ Context context, int userId) {
+ PackageManager packageManager = context.getPackageManager();
+
+ // Get the list of apps registered for SMS
+ Intent intent = new Intent(Intents.SMS_DELIVER_ACTION);
+ if (DEBUG) {
+ intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
+ }
+ List<ResolveInfo> smsReceivers = packageManager.queryBroadcastReceiversAsUser(intent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ userId);
+
+ HashMap<String, SmsApplicationData> receivers = new HashMap<String, SmsApplicationData>();
+
+ // Add one entry to the map for every sms receiver (ignoring duplicate sms receivers)
+ for (ResolveInfo resolveInfo : smsReceivers) {
+ final ActivityInfo activityInfo = resolveInfo.activityInfo;
+ if (activityInfo == null) {
+ continue;
+ }
+ if (!permission.BROADCAST_SMS.equals(activityInfo.permission)) {
+ continue;
+ }
+ final String packageName = activityInfo.packageName;
+ if (!receivers.containsKey(packageName)) {
+ final SmsApplicationData smsApplicationData = new SmsApplicationData(packageName,
+ activityInfo.applicationInfo.uid);
+ smsApplicationData.mSmsReceiverClass = activityInfo.name;
+ receivers.put(packageName, smsApplicationData);
+ }
+ }
+
+ // Update any existing entries with mms receiver class
+ intent = new Intent(Intents.WAP_PUSH_DELIVER_ACTION);
+ intent.setDataAndType(null, "application/vnd.wap.mms-message");
+ List<ResolveInfo> mmsReceivers = packageManager.queryBroadcastReceiversAsUser(intent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ userId);
+ for (ResolveInfo resolveInfo : mmsReceivers) {
+ final ActivityInfo activityInfo = resolveInfo.activityInfo;
+ if (activityInfo == null) {
+ continue;
+ }
+ if (!permission.BROADCAST_WAP_PUSH.equals(activityInfo.permission)) {
+ continue;
+ }
+ final String packageName = activityInfo.packageName;
+ final SmsApplicationData smsApplicationData = receivers.get(packageName);
+ if (smsApplicationData != null) {
+ smsApplicationData.mMmsReceiverClass = activityInfo.name;
+ }
+ }
+
+ // Update any existing entries with respond via message intent class.
+ intent = new Intent(TelephonyManager.ACTION_RESPOND_VIA_MESSAGE,
+ Uri.fromParts(SCHEME_SMSTO, "", null));
+ List<ResolveInfo> respondServices = packageManager.queryIntentServicesAsUser(intent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ UserHandle.of(userId));
+ for (ResolveInfo resolveInfo : respondServices) {
+ final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+ if (serviceInfo == null) {
+ continue;
+ }
+ if (!permission.SEND_RESPOND_VIA_MESSAGE.equals(serviceInfo.permission)) {
+ continue;
+ }
+ final String packageName = serviceInfo.packageName;
+ final SmsApplicationData smsApplicationData = receivers.get(packageName);
+ if (smsApplicationData != null) {
+ smsApplicationData.mRespondViaMessageClass = serviceInfo.name;
+ }
+ }
+
+ // Update any existing entries with supports send to.
+ intent = new Intent(Intent.ACTION_SENDTO,
+ Uri.fromParts(SCHEME_SMSTO, "", null));
+ List<ResolveInfo> sendToActivities = packageManager.queryIntentActivitiesAsUser(intent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ userId);
+ for (ResolveInfo resolveInfo : sendToActivities) {
+ final ActivityInfo activityInfo = resolveInfo.activityInfo;
+ if (activityInfo == null) {
+ continue;
+ }
+ final String packageName = activityInfo.packageName;
+ final SmsApplicationData smsApplicationData = receivers.get(packageName);
+ if (smsApplicationData != null) {
+ smsApplicationData.mSendToClass = activityInfo.name;
+ }
+ }
+
+ // Update any existing entries with the default sms changed handler.
+ intent = new Intent(Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED);
+ List<ResolveInfo> smsAppChangedReceivers =
+ packageManager.queryBroadcastReceiversAsUser(intent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "getApplicationCollectionInternal smsAppChangedActivities=" +
+ smsAppChangedReceivers);
+ }
+ for (ResolveInfo resolveInfo : smsAppChangedReceivers) {
+ final ActivityInfo activityInfo = resolveInfo.activityInfo;
+ if (activityInfo == null) {
+ continue;
+ }
+ final String packageName = activityInfo.packageName;
+ final SmsApplicationData smsApplicationData = receivers.get(packageName);
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "getApplicationCollectionInternal packageName=" +
+ packageName + " smsApplicationData: " + smsApplicationData +
+ " activityInfo.name: " + activityInfo.name);
+ }
+ if (smsApplicationData != null) {
+ smsApplicationData.mSmsAppChangedReceiverClass = activityInfo.name;
+ }
+ }
+
+ // Update any existing entries with the external provider changed handler.
+ intent = new Intent(Telephony.Sms.Intents.ACTION_EXTERNAL_PROVIDER_CHANGE);
+ List<ResolveInfo> providerChangedReceivers =
+ packageManager.queryBroadcastReceiversAsUser(intent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "getApplicationCollectionInternal providerChangedActivities=" +
+ providerChangedReceivers);
+ }
+ for (ResolveInfo resolveInfo : providerChangedReceivers) {
+ final ActivityInfo activityInfo = resolveInfo.activityInfo;
+ if (activityInfo == null) {
+ continue;
+ }
+ final String packageName = activityInfo.packageName;
+ final SmsApplicationData smsApplicationData = receivers.get(packageName);
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "getApplicationCollectionInternal packageName=" +
+ packageName + " smsApplicationData: " + smsApplicationData +
+ " activityInfo.name: " + activityInfo.name);
+ }
+ if (smsApplicationData != null) {
+ smsApplicationData.mProviderChangedReceiverClass = activityInfo.name;
+ }
+ }
+
+ // Update any existing entries with the sim full handler.
+ intent = new Intent(Intents.SIM_FULL_ACTION);
+ List<ResolveInfo> simFullReceivers =
+ packageManager.queryBroadcastReceiversAsUser(intent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "getApplicationCollectionInternal simFullReceivers="
+ + simFullReceivers);
+ }
+ for (ResolveInfo resolveInfo : simFullReceivers) {
+ final ActivityInfo activityInfo = resolveInfo.activityInfo;
+ if (activityInfo == null) {
+ continue;
+ }
+ final String packageName = activityInfo.packageName;
+ final SmsApplicationData smsApplicationData = receivers.get(packageName);
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "getApplicationCollectionInternal packageName="
+ + packageName + " smsApplicationData: " + smsApplicationData
+ + " activityInfo.name: " + activityInfo.name);
+ }
+ if (smsApplicationData != null) {
+ smsApplicationData.mSimFullReceiverClass = activityInfo.name;
+ }
+ }
+
+ // Remove any entries for which we did not find all required intents.
+ for (ResolveInfo resolveInfo : smsReceivers) {
+ final ActivityInfo activityInfo = resolveInfo.activityInfo;
+ if (activityInfo == null) {
+ continue;
+ }
+ final String packageName = activityInfo.packageName;
+ final SmsApplicationData smsApplicationData = receivers.get(packageName);
+ if (smsApplicationData != null) {
+ if (!smsApplicationData.isComplete()) {
+ receivers.remove(packageName);
+ }
+ }
+ }
+ return receivers.values();
+ }
+
+ /**
+ * Checks to see if we have a valid installed SMS application for the specified package name
+ * @return Data for the specified package name or null if there isn't one
+ */
+ public static SmsApplicationData getApplicationForPackage(
+ Collection<SmsApplicationData> applications, String packageName) {
+ if (packageName == null) {
+ return null;
+ }
+ // Is there an entry in the application list for the specified package?
+ for (SmsApplicationData application : applications) {
+ if (application.mPackageName.contentEquals(packageName)) {
+ return application;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the application we will use for delivering SMS/MMS messages.
+ *
+ * We return the preferred sms application with the following order of preference:
+ * (1) User selected SMS app (if selected, and if still valid)
+ * (2) Android Messaging (if installed)
+ * (3) The currently configured highest priority broadcast receiver
+ * (4) Null
+ */
+ private static SmsApplicationData getApplication(Context context, boolean updateIfNeeded,
+ int userId) {
+ TelephonyManager tm = (TelephonyManager)
+ context.getSystemService(Context.TELEPHONY_SERVICE);
+ RoleManager roleManager = (RoleManager) context.getSystemService(Context.ROLE_SERVICE);
+ // (b/134400042) RoleManager might be null in unit tests running older mockito versions
+ // that do not support mocking final classes.
+ if (!tm.isSmsCapable() && (roleManager == null || !roleManager.isRoleAvailable(
+ RoleManager.ROLE_SMS))) {
+ // No phone, no SMS
+ return null;
+ }
+
+ Collection<SmsApplicationData> applications = getApplicationCollectionInternal(context,
+ userId);
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "getApplication userId=" + userId);
+ }
+ // Determine which application receives the broadcast
+ String defaultApplication = getDefaultSmsPackage(context, userId);
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "getApplication defaultApp=" + defaultApplication);
+ }
+
+ SmsApplicationData applicationData = null;
+ if (defaultApplication != null) {
+ applicationData = getApplicationForPackage(applications, defaultApplication);
+ }
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "getApplication appData=" + applicationData);
+ }
+
+ // If we found a package, make sure AppOps permissions are set up correctly
+ if (applicationData != null) {
+ // We can only call checkOp if we are privileged (updateIfNeeded) or if the app we
+ // are checking is for our current uid. Doing this check from the unprivileged current
+ // SMS app allows us to tell the current SMS app that it is not in a good state and
+ // needs to ask to be the current SMS app again to work properly.
+ if (updateIfNeeded || applicationData.mUid == android.os.Process.myUid()) {
+ // Verify that the SMS app has permissions
+ boolean appOpsFixed =
+ tryFixExclusiveSmsAppops(context, applicationData, updateIfNeeded);
+ if (!appOpsFixed) {
+ // We can not return a package if permissions are not set up correctly
+ applicationData = null;
+ }
+ }
+
+ // We can only verify the phone and BT app's permissions from a privileged caller
+ if (applicationData != null && updateIfNeeded) {
+ // Ensure this component is still configured as the preferred activity. Usually the
+ // current SMS app will already be the preferred activity - but checking whether or
+ // not this is true is just as expensive as reconfiguring the preferred activity so
+ // we just reconfigure every time.
+ defaultSmsAppChanged(context);
+ }
+ }
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "getApplication returning appData=" + applicationData);
+ }
+ return applicationData;
+ }
+
+ private static String getDefaultSmsPackage(Context context, int userId) {
+ return context.getSystemService(RoleManager.class).getDefaultSmsPackage(userId);
+ }
+
+ /**
+ * Grants various permissions and appops on sms app change
+ */
+ private static void defaultSmsAppChanged(Context context) {
+ PackageManager packageManager = context.getPackageManager();
+ AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
+
+ // Assign permission to special system apps
+ assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps,
+ PHONE_PACKAGE_NAME);
+ assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps,
+ BLUETOOTH_PACKAGE_NAME);
+ assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps,
+ MMS_SERVICE_PACKAGE_NAME);
+ assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps,
+ TELEPHONY_PROVIDER_PACKAGE_NAME);
+
+ // Give AppOps permission to UID 1001 which contains multiple
+ // apps, all of them should be able to write to telephony provider.
+ // This is to allow the proxy package permission check in telephony provider
+ // to pass.
+ for (int appop : DEFAULT_APP_EXCLUSIVE_APPOPS) {
+ appOps.setUidMode(appop, Process.PHONE_UID, AppOpsManager.MODE_ALLOWED);
+ }
+ }
+
+ private static boolean tryFixExclusiveSmsAppops(Context context,
+ SmsApplicationData applicationData, boolean updateIfNeeded) {
+ AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
+ for (int appOp : DEFAULT_APP_EXCLUSIVE_APPOPS) {
+ int mode = appOps.checkOp(appOp, applicationData.mUid,
+ applicationData.mPackageName);
+ if (mode != AppOpsManager.MODE_ALLOWED) {
+ Rlog.e(LOG_TAG, applicationData.mPackageName + " lost "
+ + AppOpsManager.modeToName(appOp) + ": "
+ + (updateIfNeeded ? " (fixing)" : " (no permission to fix)"));
+ if (updateIfNeeded) {
+ appOps.setUidMode(appOp, applicationData.mUid, AppOpsManager.MODE_ALLOWED);
+ } else {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Sets the specified package as the default SMS/MMS application. The caller of this method
+ * needs to have permission to set AppOps and write to secure settings.
+ */
+ @UnsupportedAppUsage
+ public static void setDefaultApplication(String packageName, Context context) {
+ setDefaultApplicationAsUser(packageName, context, getIncomingUserId(context));
+ }
+
+ /**
+ * Same as {@link #setDefaultApplication} but takes a target user id.
+ */
+ public static void setDefaultApplicationAsUser(String packageName, Context context,
+ int userId) {
+ TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
+ RoleManager roleManager = (RoleManager) context.getSystemService(Context.ROLE_SERVICE);
+ // (b/134400042) RoleManager might be null in unit tests running older mockito versions
+ // that do not support mocking final classes.
+ if (!tm.isSmsCapable() && (roleManager == null || !roleManager.isRoleAvailable(
+ RoleManager.ROLE_SMS))) {
+ // No phone, no SMS
+ return;
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ setDefaultApplicationInternal(packageName, context, userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private static void setDefaultApplicationInternal(String packageName, Context context,
+ int userId) {
+ final UserHandle userHandle = UserHandle.of(userId);
+
+ // Get old package name
+ String oldPackageName = getDefaultSmsPackage(context, userId);
+
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "setDefaultApplicationInternal old=" + oldPackageName +
+ " new=" + packageName);
+ }
+
+ if (packageName != null && oldPackageName != null && packageName.equals(oldPackageName)) {
+ // No change
+ return;
+ }
+
+ // We only make the change if the new package is valid
+ PackageManager packageManager = context.getPackageManager();
+ Collection<SmsApplicationData> applications = getApplicationCollectionInternal(
+ context, userId);
+ SmsApplicationData oldAppData = oldPackageName != null ?
+ getApplicationForPackage(applications, oldPackageName) : null;
+ SmsApplicationData applicationData = getApplicationForPackage(applications, packageName);
+ if (applicationData != null) {
+ // Ignore relevant appops for the previously configured default SMS app.
+ AppOpsManager appOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
+ if (oldPackageName != null) {
+ try {
+ int uid = packageManager.getPackageInfoAsUser(
+ oldPackageName, 0, userId).applicationInfo.uid;
+ setExclusiveAppops(oldPackageName, appOps, uid, AppOpsManager.MODE_DEFAULT);
+ } catch (NameNotFoundException e) {
+ Rlog.w(LOG_TAG, "Old SMS package not found: " + oldPackageName);
+ }
+ }
+
+ // Update the setting.
+ CompletableFuture<Void> future = new CompletableFuture<>();
+ Consumer<Boolean> callback = successful -> {
+ if (successful) {
+ future.complete(null);
+ } else {
+ future.completeExceptionally(new RuntimeException());
+ }
+ };
+ context.getSystemService(RoleManager.class).addRoleHolderAsUser(
+ RoleManager.ROLE_SMS, applicationData.mPackageName, 0, UserHandle.of(userId),
+ AsyncTask.THREAD_POOL_EXECUTOR, callback);
+ try {
+ future.get(5, TimeUnit.SECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ Log.e(LOG_TAG, "Exception while adding sms role holder " + applicationData, e);
+ return;
+ }
+
+ defaultSmsAppChanged(context);
+ }
+ }
+
+ /**
+ * Sends broadcasts on sms app change:
+ * {@link Intent#ACTION_DEFAULT_SMS_PACKAGE_CHANGED}
+ * {@link Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL}
+ */
+ public static void broadcastSmsAppChange(Context context,
+ UserHandle userHandle, @Nullable String oldPackage, @Nullable String newPackage) {
+ Collection<SmsApplicationData> apps = getApplicationCollection(context);
+
+ broadcastSmsAppChange(context, userHandle,
+ getApplicationForPackage(apps, oldPackage),
+ getApplicationForPackage(apps, newPackage));
+ }
+
+ private static void broadcastSmsAppChange(Context context, UserHandle userHandle,
+ @Nullable SmsApplicationData oldAppData,
+ @Nullable SmsApplicationData applicationData) {
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "setDefaultApplicationInternal oldAppData=" + oldAppData);
+ }
+ if (oldAppData != null && oldAppData.mSmsAppChangedReceiverClass != null) {
+ // Notify the old sms app that it's no longer the default
+ final Intent oldAppIntent =
+ new Intent(Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED);
+ final ComponentName component = new ComponentName(oldAppData.mPackageName,
+ oldAppData.mSmsAppChangedReceiverClass);
+ oldAppIntent.setComponent(component);
+ oldAppIntent.putExtra(Intents.EXTRA_IS_DEFAULT_SMS_APP, false);
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "setDefaultApplicationInternal old=" + oldAppData.mPackageName);
+ }
+ context.sendBroadcastAsUser(oldAppIntent, userHandle);
+ }
+ // Notify the new sms app that it's now the default (if the new sms app has a receiver
+ // to handle the changed default sms intent).
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "setDefaultApplicationInternal new applicationData=" +
+ applicationData);
+ }
+ if (applicationData != null && applicationData.mSmsAppChangedReceiverClass != null) {
+ final Intent intent =
+ new Intent(Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED);
+ final ComponentName component = new ComponentName(applicationData.mPackageName,
+ applicationData.mSmsAppChangedReceiverClass);
+ intent.setComponent(component);
+ intent.putExtra(Intents.EXTRA_IS_DEFAULT_SMS_APP, true);
+ if (DEBUG_MULTIUSER) {
+ Log.i(LOG_TAG, "setDefaultApplicationInternal new=" + applicationData.mPackageName);
+ }
+ context.sendBroadcastAsUser(intent, userHandle);
+ }
+
+ // Send an implicit broadcast for the system server.
+ // (or anyone with MONITOR_DEFAULT_SMS_PACKAGE, really.)
+ final Intent intent =
+ new Intent(Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL);
+ context.sendBroadcastAsUser(intent, userHandle,
+ permission.MONITOR_DEFAULT_SMS_PACKAGE);
+
+ if (applicationData != null) {
+ MetricsLogger.action(context, MetricsEvent.ACTION_DEFAULT_SMS_APP_CHANGED,
+ applicationData.mPackageName);
+ }
+ }
+
+ /**
+ * Assign WRITE_SMS AppOps permission to some special system apps.
+ *
+ * @param context The context
+ * @param packageManager The package manager instance
+ * @param appOps The AppOps manager instance
+ * @param packageName The package name of the system app
+ */
+ private static void assignExclusiveSmsPermissionsToSystemApp(Context context,
+ PackageManager packageManager, AppOpsManager appOps, String packageName) {
+ // First check package signature matches the caller's package signature.
+ // Since this class is only used internally by the system, this check makes sure
+ // the package signature matches system signature.
+ final int result = packageManager.checkSignatures(context.getPackageName(), packageName);
+ if (result != PackageManager.SIGNATURE_MATCH) {
+ Rlog.e(LOG_TAG, packageName + " does not have system signature");
+ return;
+ }
+ try {
+ PackageInfo info = packageManager.getPackageInfo(packageName, 0);
+ int mode = appOps.checkOp(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
+ packageName);
+ if (mode != AppOpsManager.MODE_ALLOWED) {
+ Rlog.w(LOG_TAG, packageName + " does not have OP_WRITE_SMS: (fixing)");
+ setExclusiveAppops(packageName, appOps, info.applicationInfo.uid,
+ AppOpsManager.MODE_ALLOWED);
+ }
+ } catch (NameNotFoundException e) {
+ // No whitelisted system app on this device
+ Rlog.e(LOG_TAG, "Package not found: " + packageName);
+ }
+
+ }
+
+ private static void setExclusiveAppops(String pkg, AppOpsManager appOpsManager, int uid,
+ int mode) {
+ for (int appop : DEFAULT_APP_EXCLUSIVE_APPOPS) {
+ appOpsManager.setUidMode(appop, uid, mode);
+ }
+ }
+
+ /**
+ * Tracks package changes and ensures that the default SMS app is always configured to be the
+ * preferred activity for SENDTO sms/mms intents.
+ */
+ private static final class SmsPackageMonitor extends PackageMonitor {
+ final Context mContext;
+
+ public SmsPackageMonitor(Context context) {
+ super();
+ mContext = context;
+ }
+
+ @Override
+ public void onPackageDisappeared(String packageName, int reason) {
+ onPackageChanged();
+ }
+
+ @Override
+ public void onPackageAppeared(String packageName, int reason) {
+ onPackageChanged();
+ }
+
+ @Override
+ public void onPackageModified(String packageName) {
+ onPackageChanged();
+ }
+
+ private void onPackageChanged() {
+ PackageManager packageManager = mContext.getPackageManager();
+ Context userContext = mContext;
+ final int userId = getSendingUserId();
+ if (userId != UserHandle.USER_SYSTEM) {
+ try {
+ userContext = mContext.createPackageContextAsUser(mContext.getPackageName(), 0,
+ UserHandle.of(userId));
+ } catch (NameNotFoundException nnfe) {
+ if (DEBUG_MULTIUSER) {
+ Log.w(LOG_TAG, "Unable to create package context for user " + userId);
+ }
+ }
+ }
+ // Ensure this component is still configured as the preferred activity
+ ComponentName componentName = getDefaultSendToApplication(userContext, true);
+ if (componentName != null) {
+ configurePreferredActivity(packageManager, componentName, userId);
+ }
+ }
+ }
+
+ public static void initSmsPackageMonitor(Context context) {
+ sSmsPackageMonitor = new SmsPackageMonitor(context);
+ sSmsPackageMonitor.register(context, context.getMainLooper(), UserHandle.ALL, false);
+ }
+
+ @UnsupportedAppUsage
+ private static void configurePreferredActivity(PackageManager packageManager,
+ ComponentName componentName, int userId) {
+ // Add the four activity preferences we want to direct to this app.
+ replacePreferredActivity(packageManager, componentName, userId, SCHEME_SMS);
+ replacePreferredActivity(packageManager, componentName, userId, SCHEME_SMSTO);
+ replacePreferredActivity(packageManager, componentName, userId, SCHEME_MMS);
+ replacePreferredActivity(packageManager, componentName, userId, SCHEME_MMSTO);
+ }
+
+ /**
+ * Updates the ACTION_SENDTO activity to the specified component for the specified scheme.
+ */
+ private static void replacePreferredActivity(PackageManager packageManager,
+ ComponentName componentName, int userId, String scheme) {
+ // Build the set of existing activities that handle this scheme
+ Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts(scheme, "", null));
+ List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivitiesAsUser(
+ intent, PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_RESOLVED_FILTER,
+ userId);
+
+ // Build the set of ComponentNames for these activities
+ final int n = resolveInfoList.size();
+ ComponentName[] set = new ComponentName[n];
+ for (int i = 0; i < n; i++) {
+ ResolveInfo info = resolveInfoList.get(i);
+ set[i] = new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
+ }
+
+ // Update the preferred SENDTO activity for the specified scheme
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_SENDTO);
+ intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
+ intentFilter.addDataScheme(scheme);
+ packageManager.replacePreferredActivityAsUser(intentFilter,
+ IntentFilter.MATCH_CATEGORY_SCHEME + IntentFilter.MATCH_ADJUSTMENT_NORMAL,
+ set, componentName, userId);
+ }
+
+ /**
+ * Returns SmsApplicationData for this package if this package is capable of being set as the
+ * default SMS application.
+ */
+ @UnsupportedAppUsage
+ public static SmsApplicationData getSmsApplicationData(String packageName, Context context) {
+ Collection<SmsApplicationData> applications = getApplicationCollection(context);
+ return getApplicationForPackage(applications, packageName);
+ }
+
+ /**
+ * Gets the default SMS application
+ * @param context context from the calling app
+ * @param updateIfNeeded update the default app if there is no valid default app configured.
+ * @return component name of the app and class to deliver SMS messages to
+ */
+ @UnsupportedAppUsage
+ public static ComponentName getDefaultSmsApplication(Context context, boolean updateIfNeeded) {
+ return getDefaultSmsApplicationAsUser(context, updateIfNeeded, getIncomingUserId(context));
+ }
+
+ /**
+ * Gets the default SMS application on a given user
+ * @param context context from the calling app
+ * @param updateIfNeeded update the default app if there is no valid default app configured.
+ * @param userId target user ID.
+ * @return component name of the app and class to deliver SMS messages to
+ */
+ public static ComponentName getDefaultSmsApplicationAsUser(Context context,
+ boolean updateIfNeeded, int userId) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ ComponentName component = null;
+ SmsApplicationData smsApplicationData = getApplication(context, updateIfNeeded,
+ userId);
+ if (smsApplicationData != null) {
+ component = new ComponentName(smsApplicationData.mPackageName,
+ smsApplicationData.mSmsReceiverClass);
+ }
+ return component;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Gets the default MMS application
+ * @param context context from the calling app
+ * @param updateIfNeeded update the default app if there is no valid default app configured.
+ * @return component name of the app and class to deliver MMS messages to
+ */
+ @UnsupportedAppUsage
+ public static ComponentName getDefaultMmsApplication(Context context, boolean updateIfNeeded) {
+ int userId = getIncomingUserId(context);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ ComponentName component = null;
+ SmsApplicationData smsApplicationData = getApplication(context, updateIfNeeded,
+ userId);
+ if (smsApplicationData != null) {
+ component = new ComponentName(smsApplicationData.mPackageName,
+ smsApplicationData.mMmsReceiverClass);
+ }
+ return component;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Gets the default Respond Via Message application
+ * @param context context from the calling app
+ * @param updateIfNeeded update the default app if there is no valid default app configured.
+ * @return component name of the app and class to direct Respond Via Message intent to
+ */
+ @UnsupportedAppUsage
+ public static ComponentName getDefaultRespondViaMessageApplication(Context context,
+ boolean updateIfNeeded) {
+ int userId = getIncomingUserId(context);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ ComponentName component = null;
+ SmsApplicationData smsApplicationData = getApplication(context, updateIfNeeded,
+ userId);
+ if (smsApplicationData != null) {
+ component = new ComponentName(smsApplicationData.mPackageName,
+ smsApplicationData.mRespondViaMessageClass);
+ }
+ return component;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Gets the default Send To (smsto) application.
+ * <p>
+ * Caller must pass in the correct user context if calling from a singleton service.
+ * @param context context from the calling app
+ * @param updateIfNeeded update the default app if there is no valid default app configured.
+ * @return component name of the app and class to direct SEND_TO (smsto) intent to
+ */
+ public static ComponentName getDefaultSendToApplication(Context context,
+ boolean updateIfNeeded) {
+ int userId = getIncomingUserId(context);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ ComponentName component = null;
+ SmsApplicationData smsApplicationData = getApplication(context, updateIfNeeded,
+ userId);
+ if (smsApplicationData != null) {
+ component = new ComponentName(smsApplicationData.mPackageName,
+ smsApplicationData.mSendToClass);
+ }
+ return component;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Gets the default application that handles external changes to the SmsProvider and
+ * MmsProvider.
+ * @param context context from the calling app
+ * @param updateIfNeeded update the default app if there is no valid default app configured.
+ * @return component name of the app and class to deliver change intents to
+ */
+ public static ComponentName getDefaultExternalTelephonyProviderChangedApplication(
+ Context context, boolean updateIfNeeded) {
+ int userId = getIncomingUserId(context);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ ComponentName component = null;
+ SmsApplicationData smsApplicationData = getApplication(context, updateIfNeeded,
+ userId);
+ if (smsApplicationData != null
+ && smsApplicationData.mProviderChangedReceiverClass != null) {
+ component = new ComponentName(smsApplicationData.mPackageName,
+ smsApplicationData.mProviderChangedReceiverClass);
+ }
+ return component;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Gets the default application that handles sim full event.
+ * @param context context from the calling app
+ * @param updateIfNeeded update the default app if there is no valid default app configured.
+ * @return component name of the app and class to deliver change intents to
+ */
+ public static ComponentName getDefaultSimFullApplication(
+ Context context, boolean updateIfNeeded) {
+ int userId = getIncomingUserId(context);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ ComponentName component = null;
+ SmsApplicationData smsApplicationData = getApplication(context, updateIfNeeded,
+ userId);
+ if (smsApplicationData != null
+ && smsApplicationData.mSimFullReceiverClass != null) {
+ component = new ComponentName(smsApplicationData.mPackageName,
+ smsApplicationData.mSimFullReceiverClass);
+ }
+ return component;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Returns whether need to write the SMS message to SMS database for this package.
+ * <p>
+ * Caller must pass in the correct user context if calling from a singleton service.
+ */
+ @UnsupportedAppUsage
+ public static boolean shouldWriteMessageForPackage(String packageName, Context context) {
+ return !isDefaultSmsApplication(context, packageName);
+ }
+
+ /**
+ * Check if a package is default sms app (or equivalent, like bluetooth)
+ *
+ * @param context context from the calling app
+ * @param packageName the name of the package to be checked
+ * @return true if the package is default sms app or bluetooth
+ */
+ @UnsupportedAppUsage
+ public static boolean isDefaultSmsApplication(Context context, String packageName) {
+ if (packageName == null) {
+ return false;
+ }
+ final String defaultSmsPackage = getDefaultSmsApplicationPackageName(context);
+ if ((defaultSmsPackage != null && defaultSmsPackage.equals(packageName))
+ || BLUETOOTH_PACKAGE_NAME.equals(packageName)) {
+ return true;
+ }
+ return false;
+ }
+
+ private static String getDefaultSmsApplicationPackageName(Context context) {
+ final ComponentName component = getDefaultSmsApplication(context, false);
+ if (component != null) {
+ return component.getPackageName();
+ }
+ return null;
+ }
+}
diff --git a/telephony/common/com/android/internal/telephony/SmsNumberUtils.java b/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
new file mode 100644
index 000000000000..0d33af639113
--- /dev/null
+++ b/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
@@ -0,0 +1,627 @@
+/*
+ * 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 com.android.internal.telephony;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.os.Binder;
+import android.os.Build;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+
+import com.android.internal.telephony.HbpcdLookup.MccIdd;
+import com.android.internal.telephony.HbpcdLookup.MccLookup;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+
+/**
+ * This class implements handle the MO SMS target address before sending.
+ * This is special for VZW requirement. Follow the specifications of assisted dialing
+ * of MO SMS while traveling on VZW CDMA, international CDMA or GSM markets.
+ * {@hide}
+ */
+public class SmsNumberUtils {
+ private static final String TAG = "SmsNumberUtils";
+ private static final boolean DBG = Build.IS_DEBUGGABLE;
+
+ private static final String PLUS_SIGN = "+";
+
+ private static final int NANP_SHORT_LENGTH = 7;
+ private static final int NANP_MEDIUM_LENGTH = 10;
+ private static final int NANP_LONG_LENGTH = 11;
+
+ private static final int NANP_CC = 1;
+ private static final String NANP_NDD = "1";
+ private static final String NANP_IDD = "011";
+
+ private static final int MIN_COUNTRY_AREA_LOCAL_LENGTH = 10;
+
+ private static final int GSM_UMTS_NETWORK = 0;
+ private static final int CDMA_HOME_NETWORK = 1;
+ private static final int CDMA_ROAMING_NETWORK = 2;
+
+ private static final int NP_NONE = 0;
+ private static final int NP_NANP_BEGIN = 1;
+
+ /* <Phone Number>, <NXX>-<XXXX> N[2-9] */
+ private static final int NP_NANP_LOCAL = NP_NANP_BEGIN;
+
+ /* <Area_code>-<Phone Number>, <NXX>-<NXX>-<XXXX> N[2-9] */
+ private static final int NP_NANP_AREA_LOCAL = NP_NANP_BEGIN + 1;
+
+ /* <1>-<Area_code>-<Phone Number>, 1-<NXX>-<NXX>-<XXXX> N[2-9] */
+ private static final int NP_NANP_NDD_AREA_LOCAL = NP_NANP_BEGIN + 2;
+
+ /* <+><U.S.Country_code><Area_code><Phone Number>, +1-<NXX>-<NXX>-<XXXX> N[2-9] */
+ private static final int NP_NANP_NBPCD_CC_AREA_LOCAL = NP_NANP_BEGIN + 3;
+
+ /* <Local_IDD><Country_code><Area_code><Phone Number>, 001-1-<NXX>-<NXX>-<XXXX> N[2-9] */
+ private static final int NP_NANP_LOCALIDD_CC_AREA_LOCAL = NP_NANP_BEGIN + 4;
+
+ /* <+><Home_IDD><Country_code><Area_code><Phone Number>, +011-1-<NXX>-<NXX>-<XXXX> N[2-9] */
+ private static final int NP_NANP_NBPCD_HOMEIDD_CC_AREA_LOCAL = NP_NANP_BEGIN + 5;
+
+ private static final int NP_INTERNATIONAL_BEGIN = 100;
+ /* <+>-<Home_IDD>-<Country_code>-<Area_code>-<Phone Number>, +011-86-25-86281234 */
+ private static final int NP_NBPCD_HOMEIDD_CC_AREA_LOCAL = NP_INTERNATIONAL_BEGIN;
+
+ /* <Home_IDD>-<Country_code>-<Area_code>-<Phone Number>, 011-86-25-86281234 */
+ private static final int NP_HOMEIDD_CC_AREA_LOCAL = NP_INTERNATIONAL_BEGIN + 1;
+
+ /* <NBPCD>-<Country_code>-<Area_code>-<Phone Number>, +1-86-25-86281234 */
+ private static final int NP_NBPCD_CC_AREA_LOCAL = NP_INTERNATIONAL_BEGIN + 2;
+
+ /* <Local_IDD>-<Country_code>-<Area_code>-<Phone Number>, 00-86-25-86281234 */
+ private static final int NP_LOCALIDD_CC_AREA_LOCAL = NP_INTERNATIONAL_BEGIN + 3;
+
+ /* <Country_code>-<Area_code>-<Phone Number>, 86-25-86281234*/
+ private static final int NP_CC_AREA_LOCAL = NP_INTERNATIONAL_BEGIN + 4;
+
+ private static int[] ALL_COUNTRY_CODES = null;
+ private static int MAX_COUNTRY_CODES_LENGTH;
+ private static HashMap<String, ArrayList<String>> IDDS_MAPS =
+ new HashMap<String, ArrayList<String>>();
+
+ private static class NumberEntry {
+ public String number;
+ public String IDD;
+ public int countryCode;
+ public NumberEntry(String number) {
+ this.number = number;
+ }
+ }
+
+ /**
+ * Breaks the given number down and formats it according to the rules
+ * for different number plans and different network.
+ *
+ * @param number destination number which need to be format
+ * @param activeMcc current network's mcc
+ * @param networkType current network type
+ *
+ * @return the number after formatting.
+ */
+ private static String formatNumber(Context context, String number,
+ String activeMcc,
+ int networkType) {
+ if (number == null ) {
+ throw new IllegalArgumentException("number is null");
+ }
+
+ if (activeMcc == null || activeMcc.trim().length() == 0) {
+ throw new IllegalArgumentException("activeMcc is null or empty!");
+ }
+
+ String networkPortionNumber = PhoneNumberUtils.extractNetworkPortion(number);
+ if (networkPortionNumber == null || networkPortionNumber.length() == 0) {
+ throw new IllegalArgumentException("Number is invalid!");
+ }
+
+ NumberEntry numberEntry = new NumberEntry(networkPortionNumber);
+ ArrayList<String> allIDDs = getAllIDDs(context, activeMcc);
+
+ // First check whether the number is a NANP number.
+ int nanpState = checkNANP(numberEntry, allIDDs);
+ if (DBG) Rlog.d(TAG, "NANP type: " + getNumberPlanType(nanpState));
+
+ if ((nanpState == NP_NANP_LOCAL)
+ || (nanpState == NP_NANP_AREA_LOCAL)
+ || (nanpState == NP_NANP_NDD_AREA_LOCAL)) {
+ return networkPortionNumber;
+ } else if (nanpState == NP_NANP_NBPCD_CC_AREA_LOCAL) {
+ if (networkType == CDMA_HOME_NETWORK
+ || networkType == CDMA_ROAMING_NETWORK) {
+ // Remove "+"
+ return networkPortionNumber.substring(1);
+ } else {
+ return networkPortionNumber;
+ }
+ } else if (nanpState == NP_NANP_LOCALIDD_CC_AREA_LOCAL) {
+ if (networkType == CDMA_HOME_NETWORK) {
+ return networkPortionNumber;
+ } else if (networkType == GSM_UMTS_NETWORK) {
+ // Remove the local IDD and replace with "+"
+ int iddLength = numberEntry.IDD != null ? numberEntry.IDD.length() : 0;
+ return PLUS_SIGN + networkPortionNumber.substring(iddLength);
+ } else if (networkType == CDMA_ROAMING_NETWORK) {
+ // Remove the local IDD
+ int iddLength = numberEntry.IDD != null ? numberEntry.IDD.length() : 0;
+ return networkPortionNumber.substring(iddLength);
+ }
+ }
+
+ int internationalState = checkInternationalNumberPlan(context, numberEntry, allIDDs,
+ NANP_IDD);
+ if (DBG) Rlog.d(TAG, "International type: " + getNumberPlanType(internationalState));
+ String returnNumber = null;
+
+ switch (internationalState) {
+ case NP_NBPCD_HOMEIDD_CC_AREA_LOCAL:
+ if (networkType == GSM_UMTS_NETWORK) {
+ // Remove "+"
+ returnNumber = networkPortionNumber.substring(1);
+ }
+ break;
+
+ case NP_NBPCD_CC_AREA_LOCAL:
+ // Replace "+" with "011"
+ returnNumber = NANP_IDD + networkPortionNumber.substring(1);
+ break;
+
+ case NP_LOCALIDD_CC_AREA_LOCAL:
+ if (networkType == GSM_UMTS_NETWORK || networkType == CDMA_ROAMING_NETWORK) {
+ int iddLength = numberEntry.IDD != null ? numberEntry.IDD.length() : 0;
+ // Replace <Local IDD> to <Home IDD>("011")
+ returnNumber = NANP_IDD + networkPortionNumber.substring(iddLength);
+ }
+ break;
+
+ case NP_CC_AREA_LOCAL:
+ int countryCode = numberEntry.countryCode;
+
+ if (!inExceptionListForNpCcAreaLocal(numberEntry)
+ && networkPortionNumber.length() >= 11 && countryCode != NANP_CC) {
+ // Add "011"
+ returnNumber = NANP_IDD + networkPortionNumber;
+ }
+ break;
+
+ case NP_HOMEIDD_CC_AREA_LOCAL:
+ returnNumber = networkPortionNumber;
+ break;
+
+ default:
+ // Replace "+" with 011 in CDMA network if the number's country
+ // code is not in the HbpcdLookup database.
+ if (networkPortionNumber.startsWith(PLUS_SIGN)
+ && (networkType == CDMA_HOME_NETWORK || networkType == CDMA_ROAMING_NETWORK)) {
+ if (networkPortionNumber.startsWith(PLUS_SIGN + NANP_IDD)) {
+ // Only remove "+"
+ returnNumber = networkPortionNumber.substring(1);
+ } else {
+ // Replace "+" with "011"
+ returnNumber = NANP_IDD + networkPortionNumber.substring(1);
+ }
+ }
+ }
+
+ if (returnNumber == null) {
+ returnNumber = networkPortionNumber;
+ }
+ return returnNumber;
+ }
+
+ /**
+ * Query International direct dialing from HbpcdLookup.db
+ * for specified country code
+ *
+ * @param mcc current network's country code
+ *
+ * @return the IDD array list.
+ */
+ private static ArrayList<String> getAllIDDs(Context context, String mcc) {
+ ArrayList<String> allIDDs = IDDS_MAPS.get(mcc);
+ if (allIDDs != null) {
+ return allIDDs;
+ } else {
+ allIDDs = new ArrayList<String>();
+ }
+
+ String projection[] = {MccIdd.IDD, MccIdd.MCC};
+ String where = null;
+
+ // if mcc is null : return all rows
+ // if mcc is empty-string : return those rows whose mcc is emptry-string
+ String[] selectionArgs = null;
+ if (mcc != null) {
+ where = MccIdd.MCC + "=?";
+ selectionArgs = new String[] {mcc};
+ }
+
+ Cursor cursor = null;
+ try {
+ cursor = context.getContentResolver().query(MccIdd.CONTENT_URI, projection,
+ where, selectionArgs, null);
+ if (cursor.getCount() > 0) {
+ while (cursor.moveToNext()) {
+ String idd = cursor.getString(0);
+ if (!allIDDs.contains(idd)) {
+ allIDDs.add(idd);
+ }
+ }
+ }
+ } catch (SQLException e) {
+ Rlog.e(TAG, "Can't access HbpcdLookup database", e);
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ IDDS_MAPS.put(mcc, allIDDs);
+
+ if (DBG) Rlog.d(TAG, "MCC = " + mcc + ", all IDDs = " + allIDDs);
+ return allIDDs;
+ }
+
+
+ /**
+ * Verify if the the destination number is a NANP number
+ *
+ * @param numberEntry including number and IDD array
+ * @param allIDDs the IDD array list of the current network's country code
+ *
+ * @return the number plan type related NANP
+ */
+ private static int checkNANP(NumberEntry numberEntry, ArrayList<String> allIDDs) {
+ boolean isNANP = false;
+ String number = numberEntry.number;
+
+ if (number.length() == NANP_SHORT_LENGTH) {
+ // 7 digits - Seven digit phone numbers
+ char firstChar = number.charAt(0);
+ if (firstChar >= '2' && firstChar <= '9') {
+ isNANP = true;
+ for (int i=1; i< NANP_SHORT_LENGTH; i++ ) {
+ char c= number.charAt(i);
+ if (!PhoneNumberUtils.isISODigit(c)) {
+ isNANP = false;
+ break;
+ }
+ }
+ }
+ if (isNANP) {
+ return NP_NANP_LOCAL;
+ }
+ } else if (number.length() == NANP_MEDIUM_LENGTH) {
+ // 10 digits - Three digit area code followed by seven digit phone numbers/
+ if (isNANP(number)) {
+ return NP_NANP_AREA_LOCAL;
+ }
+ } else if (number.length() == NANP_LONG_LENGTH) {
+ // 11 digits - One digit U.S. NDD(National Direct Dial) prefix '1',
+ // followed by three digit area code and seven digit phone numbers
+ if (isNANP(number)) {
+ return NP_NANP_NDD_AREA_LOCAL;
+ }
+ } else if (number.startsWith(PLUS_SIGN)) {
+ number = number.substring(1);
+ if (number.length() == NANP_LONG_LENGTH) {
+ // '+' and 11 digits -'+', followed by NANP CC prefix '1' followed by
+ // three digit area code and seven digit phone numbers
+ if (isNANP(number)) {
+ return NP_NANP_NBPCD_CC_AREA_LOCAL;
+ }
+ } else if (number.startsWith(NANP_IDD) && number.length() == NANP_LONG_LENGTH + 3) {
+ // '+' and 14 digits -'+', followed by NANP IDD "011" followed by NANP CC
+ // prefix '1' followed by three digit area code and seven digit phone numbers
+ number = number.substring(3);
+ if (isNANP(number)) {
+ return NP_NANP_NBPCD_HOMEIDD_CC_AREA_LOCAL;
+ }
+ }
+ } else {
+ // Check whether it's NP_NANP_LOCALIDD_CC_AREA_LOCAL
+ for (String idd : allIDDs) {
+ if (number.startsWith(idd)) {
+ String number2 = number.substring(idd.length());
+ if(number2 !=null && number2.startsWith(String.valueOf(NANP_CC))){
+ if (isNANP(number2)) {
+ numberEntry.IDD = idd;
+ return NP_NANP_LOCALIDD_CC_AREA_LOCAL;
+ }
+ }
+ }
+ }
+ }
+
+ return NP_NONE;
+ }
+
+ private static boolean isNANP(String number) {
+ if (number.length() == NANP_MEDIUM_LENGTH
+ || (number.length() == NANP_LONG_LENGTH && number.startsWith(NANP_NDD))) {
+ if (number.length() == NANP_LONG_LENGTH) {
+ number = number.substring(1);
+ }
+ return (PhoneNumberUtils.isNanp(number));
+ }
+ return false;
+ }
+
+ /**
+ * Verify if the the destination number is an internal number
+ *
+ * @param numberEntry including number and IDD array
+ * @param allIDDs the IDD array list of the current network's country code
+ *
+ * @return the number plan type related international number
+ */
+ private static int checkInternationalNumberPlan(Context context, NumberEntry numberEntry,
+ ArrayList<String> allIDDs,String homeIDD) {
+ String number = numberEntry.number;
+ int countryCode = -1;
+
+ if (number.startsWith(PLUS_SIGN)) {
+ // +xxxxxxxxxx
+ String numberNoNBPCD = number.substring(1);
+ if (numberNoNBPCD.startsWith(homeIDD)) {
+ // +011xxxxxxxx
+ String numberCountryAreaLocal = numberNoNBPCD.substring(homeIDD.length());
+ if ((countryCode = getCountryCode(context, numberCountryAreaLocal)) > 0) {
+ numberEntry.countryCode = countryCode;
+ return NP_NBPCD_HOMEIDD_CC_AREA_LOCAL;
+ }
+ } else if ((countryCode = getCountryCode(context, numberNoNBPCD)) > 0) {
+ numberEntry.countryCode = countryCode;
+ return NP_NBPCD_CC_AREA_LOCAL;
+ }
+
+ } else if (number.startsWith(homeIDD)) {
+ // 011xxxxxxxxx
+ String numberCountryAreaLocal = number.substring(homeIDD.length());
+ if ((countryCode = getCountryCode(context, numberCountryAreaLocal)) > 0) {
+ numberEntry.countryCode = countryCode;
+ return NP_HOMEIDD_CC_AREA_LOCAL;
+ }
+ } else {
+ for (String exitCode : allIDDs) {
+ if (number.startsWith(exitCode)) {
+ String numberNoIDD = number.substring(exitCode.length());
+ if ((countryCode = getCountryCode(context, numberNoIDD)) > 0) {
+ numberEntry.countryCode = countryCode;
+ numberEntry.IDD = exitCode;
+ return NP_LOCALIDD_CC_AREA_LOCAL;
+ }
+ }
+ }
+
+ if (!number.startsWith("0") && (countryCode = getCountryCode(context, number)) > 0) {
+ numberEntry.countryCode = countryCode;
+ return NP_CC_AREA_LOCAL;
+ }
+ }
+ return NP_NONE;
+ }
+
+ /**
+ * Returns the country code from the given number.
+ */
+ private static int getCountryCode(Context context, String number) {
+ int countryCode = -1;
+ if (number.length() >= MIN_COUNTRY_AREA_LOCAL_LENGTH) {
+ // Check Country code
+ int[] allCCs = getAllCountryCodes(context);
+ if (allCCs == null) {
+ return countryCode;
+ }
+
+ int[] ccArray = new int[MAX_COUNTRY_CODES_LENGTH];
+ for (int i = 0; i < MAX_COUNTRY_CODES_LENGTH; i ++) {
+ ccArray[i] = Integer.parseInt(number.substring(0, i+1));
+ }
+
+ for (int i = 0; i < allCCs.length; i ++) {
+ int tempCC = allCCs[i];
+ for (int j = 0; j < MAX_COUNTRY_CODES_LENGTH; j ++) {
+ if (tempCC == ccArray[j]) {
+ if (DBG) Rlog.d(TAG, "Country code = " + tempCC);
+ return tempCC;
+ }
+ }
+ }
+ }
+
+ return countryCode;
+ }
+
+ /**
+ * Gets all country Codes information with given MCC.
+ */
+ private static int[] getAllCountryCodes(Context context) {
+ if (ALL_COUNTRY_CODES != null) {
+ return ALL_COUNTRY_CODES;
+ }
+
+ Cursor cursor = null;
+ try {
+ String projection[] = {MccLookup.COUNTRY_CODE};
+ cursor = context.getContentResolver().query(MccLookup.CONTENT_URI,
+ projection, null, null, null);
+
+ if (cursor.getCount() > 0) {
+ ALL_COUNTRY_CODES = new int[cursor.getCount()];
+ int i = 0;
+ while (cursor.moveToNext()) {
+ int countryCode = cursor.getInt(0);
+ ALL_COUNTRY_CODES[i++] = countryCode;
+ int length = String.valueOf(countryCode).trim().length();
+ if (length > MAX_COUNTRY_CODES_LENGTH) {
+ MAX_COUNTRY_CODES_LENGTH = length;
+ }
+ }
+ }
+ } catch (SQLException e) {
+ Rlog.e(TAG, "Can't access HbpcdLookup database", e);
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ return ALL_COUNTRY_CODES;
+ }
+
+ private static boolean inExceptionListForNpCcAreaLocal(NumberEntry numberEntry) {
+ int countryCode = numberEntry.countryCode;
+ boolean result = (numberEntry.number.length() == 12
+ && (countryCode == 7 || countryCode == 20
+ || countryCode == 65 || countryCode == 90));
+ return result;
+ }
+
+ private static String getNumberPlanType(int state) {
+ String numberPlanType = "Number Plan type (" + state + "): ";
+
+ if (state == NP_NANP_LOCAL) {
+ numberPlanType = "NP_NANP_LOCAL";
+ } else if (state == NP_NANP_AREA_LOCAL) {
+ numberPlanType = "NP_NANP_AREA_LOCAL";
+ } else if (state == NP_NANP_NDD_AREA_LOCAL) {
+ numberPlanType = "NP_NANP_NDD_AREA_LOCAL";
+ } else if (state == NP_NANP_NBPCD_CC_AREA_LOCAL) {
+ numberPlanType = "NP_NANP_NBPCD_CC_AREA_LOCAL";
+ } else if (state == NP_NANP_LOCALIDD_CC_AREA_LOCAL) {
+ numberPlanType = "NP_NANP_LOCALIDD_CC_AREA_LOCAL";
+ } else if (state == NP_NANP_NBPCD_HOMEIDD_CC_AREA_LOCAL) {
+ numberPlanType = "NP_NANP_NBPCD_HOMEIDD_CC_AREA_LOCAL";
+ } else if (state == NP_NBPCD_HOMEIDD_CC_AREA_LOCAL) {
+ numberPlanType = "NP_NBPCD_HOMEIDD_CC_AREA_LOCAL";
+ } else if (state == NP_HOMEIDD_CC_AREA_LOCAL) {
+ numberPlanType = "NP_HOMEIDD_CC_AREA_LOCAL";
+ } else if (state == NP_NBPCD_CC_AREA_LOCAL) {
+ numberPlanType = "NP_NBPCD_CC_AREA_LOCAL";
+ } else if (state == NP_LOCALIDD_CC_AREA_LOCAL) {
+ numberPlanType = "NP_LOCALIDD_CC_AREA_LOCAL";
+ } else if (state == NP_CC_AREA_LOCAL) {
+ numberPlanType = "NP_CC_AREA_LOCAL";
+ } else {
+ numberPlanType = "Unknown type";
+ }
+ return numberPlanType;
+ }
+
+ /**
+ * Filter the destination number if using VZW sim card.
+ */
+ public static String filterDestAddr(Context context, int subId, String destAddr) {
+ if (DBG) Rlog.d(TAG, "enter filterDestAddr. destAddr=\"" + Rlog.pii(TAG, destAddr) + "\"" );
+
+ if (destAddr == null || !PhoneNumberUtils.isGlobalPhoneNumber(destAddr)) {
+ Rlog.w(TAG, "destAddr" + Rlog.pii(TAG, destAddr) +
+ " is not a global phone number! Nothing changed.");
+ return destAddr;
+ }
+
+ final TelephonyManager telephonyManager = ((TelephonyManager) context
+ .getSystemService(Context.TELEPHONY_SERVICE)).createForSubscriptionId(subId);
+ final String networkOperator = telephonyManager.getNetworkOperator();
+ String result = null;
+
+ if (needToConvert(context, subId)) {
+ final int networkType = getNetworkType(telephonyManager);
+ if (networkType != -1 && !TextUtils.isEmpty(networkOperator)) {
+ String networkMcc = networkOperator.substring(0, 3);
+ if (networkMcc != null && networkMcc.trim().length() > 0) {
+ result = formatNumber(context, destAddr, networkMcc, networkType);
+ }
+ }
+ }
+
+ if (DBG) {
+ Rlog.d(TAG, "destAddr is " + ((result != null)?"formatted.":"not formatted."));
+ Rlog.d(TAG, "leave filterDestAddr, new destAddr=\"" + (result != null ? Rlog.pii(TAG,
+ result) : Rlog.pii(TAG, destAddr)) + "\"");
+ }
+ return result != null ? result : destAddr;
+ }
+
+ /**
+ * Returns the current network type
+ */
+ private static int getNetworkType(TelephonyManager telephonyManager) {
+ int networkType = -1;
+ int phoneType = telephonyManager.getPhoneType();
+
+ if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
+ networkType = GSM_UMTS_NETWORK;
+ } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
+ if (isInternationalRoaming(telephonyManager)) {
+ networkType = CDMA_ROAMING_NETWORK;
+ } else {
+ networkType = CDMA_HOME_NETWORK;
+ }
+ } else {
+ if (DBG) Rlog.w(TAG, "warning! unknown mPhoneType value=" + phoneType);
+ }
+
+ return networkType;
+ }
+
+ private static boolean isInternationalRoaming(TelephonyManager telephonyManager) {
+ String operatorIsoCountry = telephonyManager.getNetworkCountryIso();
+ String simIsoCountry = telephonyManager.getSimCountryIso();
+ boolean internationalRoaming = !TextUtils.isEmpty(operatorIsoCountry)
+ && !TextUtils.isEmpty(simIsoCountry)
+ && !simIsoCountry.equals(operatorIsoCountry);
+ if (internationalRoaming) {
+ if ("us".equals(simIsoCountry)) {
+ internationalRoaming = !"vi".equals(operatorIsoCountry);
+ } else if ("vi".equals(simIsoCountry)) {
+ internationalRoaming = !"us".equals(operatorIsoCountry);
+ }
+ }
+ return internationalRoaming;
+ }
+
+ private static boolean needToConvert(Context context, int subId) {
+ // Calling package may not have READ_PHONE_STATE which is required for getConfig().
+ // Clear the calling identity so that it is called as self.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ CarrierConfigManager configManager = (CarrierConfigManager)
+ context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configManager != null) {
+ PersistableBundle bundle = configManager.getConfigForSubId(subId);
+ if (bundle != null) {
+ return bundle.getBoolean(CarrierConfigManager
+ .KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ // by default this value is false
+ return false;
+ }
+}
diff --git a/telephony/java/com/google/android/mms/ContentType.java b/telephony/common/com/google/android/mms/ContentType.java
index 12e4b7e26e1e..12e4b7e26e1e 100644
--- a/telephony/java/com/google/android/mms/ContentType.java
+++ b/telephony/common/com/google/android/mms/ContentType.java
diff --git a/telephony/java/com/google/android/mms/InvalidHeaderValueException.java b/telephony/common/com/google/android/mms/InvalidHeaderValueException.java
index 2836c3075b3b..2836c3075b3b 100644
--- a/telephony/java/com/google/android/mms/InvalidHeaderValueException.java
+++ b/telephony/common/com/google/android/mms/InvalidHeaderValueException.java
diff --git a/telephony/java/com/google/android/mms/MmsException.java b/telephony/common/com/google/android/mms/MmsException.java
index 5be33ed1fac9..5be33ed1fac9 100644
--- a/telephony/java/com/google/android/mms/MmsException.java
+++ b/telephony/common/com/google/android/mms/MmsException.java
diff --git a/telephony/java/com/google/android/mms/package.html b/telephony/common/com/google/android/mms/package.html
index c9f96a66ab3b..c9f96a66ab3b 100755
--- a/telephony/java/com/google/android/mms/package.html
+++ b/telephony/common/com/google/android/mms/package.html
diff --git a/telephony/java/com/google/android/mms/pdu/AcknowledgeInd.java b/telephony/common/com/google/android/mms/pdu/AcknowledgeInd.java
index ae447d7a7417..ae447d7a7417 100644
--- a/telephony/java/com/google/android/mms/pdu/AcknowledgeInd.java
+++ b/telephony/common/com/google/android/mms/pdu/AcknowledgeInd.java
diff --git a/telephony/java/com/google/android/mms/pdu/Base64.java b/telephony/common/com/google/android/mms/pdu/Base64.java
index 483fa7f9842e..483fa7f9842e 100644
--- a/telephony/java/com/google/android/mms/pdu/Base64.java
+++ b/telephony/common/com/google/android/mms/pdu/Base64.java
diff --git a/telephony/java/com/google/android/mms/pdu/CharacterSets.java b/telephony/common/com/google/android/mms/pdu/CharacterSets.java
index 27da35e2d928..27da35e2d928 100644
--- a/telephony/java/com/google/android/mms/pdu/CharacterSets.java
+++ b/telephony/common/com/google/android/mms/pdu/CharacterSets.java
diff --git a/telephony/java/com/google/android/mms/pdu/DeliveryInd.java b/telephony/common/com/google/android/mms/pdu/DeliveryInd.java
index 7093ac63338c..7093ac63338c 100644
--- a/telephony/java/com/google/android/mms/pdu/DeliveryInd.java
+++ b/telephony/common/com/google/android/mms/pdu/DeliveryInd.java
diff --git a/telephony/java/com/google/android/mms/pdu/EncodedStringValue.java b/telephony/common/com/google/android/mms/pdu/EncodedStringValue.java
index 41662750842f..41662750842f 100644
--- a/telephony/java/com/google/android/mms/pdu/EncodedStringValue.java
+++ b/telephony/common/com/google/android/mms/pdu/EncodedStringValue.java
diff --git a/telephony/java/com/google/android/mms/pdu/GenericPdu.java b/telephony/common/com/google/android/mms/pdu/GenericPdu.java
index ebf16ac7e632..ebf16ac7e632 100644
--- a/telephony/java/com/google/android/mms/pdu/GenericPdu.java
+++ b/telephony/common/com/google/android/mms/pdu/GenericPdu.java
diff --git a/telephony/java/com/google/android/mms/pdu/MultimediaMessagePdu.java b/telephony/common/com/google/android/mms/pdu/MultimediaMessagePdu.java
index e108f7600baf..e108f7600baf 100644
--- a/telephony/java/com/google/android/mms/pdu/MultimediaMessagePdu.java
+++ b/telephony/common/com/google/android/mms/pdu/MultimediaMessagePdu.java
diff --git a/telephony/java/com/google/android/mms/pdu/NotificationInd.java b/telephony/common/com/google/android/mms/pdu/NotificationInd.java
index b561bd4ab3a7..b561bd4ab3a7 100644
--- a/telephony/java/com/google/android/mms/pdu/NotificationInd.java
+++ b/telephony/common/com/google/android/mms/pdu/NotificationInd.java
diff --git a/telephony/java/com/google/android/mms/pdu/NotifyRespInd.java b/telephony/common/com/google/android/mms/pdu/NotifyRespInd.java
index 3c70f86a0890..3c70f86a0890 100644
--- a/telephony/java/com/google/android/mms/pdu/NotifyRespInd.java
+++ b/telephony/common/com/google/android/mms/pdu/NotifyRespInd.java
diff --git a/telephony/java/com/google/android/mms/pdu/PduBody.java b/telephony/common/com/google/android/mms/pdu/PduBody.java
index 51914e4110b0..51914e4110b0 100644
--- a/telephony/java/com/google/android/mms/pdu/PduBody.java
+++ b/telephony/common/com/google/android/mms/pdu/PduBody.java
diff --git a/telephony/java/com/google/android/mms/pdu/PduComposer.java b/telephony/common/com/google/android/mms/pdu/PduComposer.java
index e24bf21a11b5..e24bf21a11b5 100644
--- a/telephony/java/com/google/android/mms/pdu/PduComposer.java
+++ b/telephony/common/com/google/android/mms/pdu/PduComposer.java
diff --git a/telephony/java/com/google/android/mms/pdu/PduContentTypes.java b/telephony/common/com/google/android/mms/pdu/PduContentTypes.java
index 8551b2f9b693..8551b2f9b693 100644
--- a/telephony/java/com/google/android/mms/pdu/PduContentTypes.java
+++ b/telephony/common/com/google/android/mms/pdu/PduContentTypes.java
diff --git a/telephony/java/com/google/android/mms/pdu/PduHeaders.java b/telephony/common/com/google/android/mms/pdu/PduHeaders.java
index b5244645fda1..b5244645fda1 100644
--- a/telephony/java/com/google/android/mms/pdu/PduHeaders.java
+++ b/telephony/common/com/google/android/mms/pdu/PduHeaders.java
diff --git a/telephony/java/com/google/android/mms/pdu/PduParser.java b/telephony/common/com/google/android/mms/pdu/PduParser.java
index f48399410723..f48399410723 100755
--- a/telephony/java/com/google/android/mms/pdu/PduParser.java
+++ b/telephony/common/com/google/android/mms/pdu/PduParser.java
diff --git a/telephony/java/com/google/android/mms/pdu/PduPart.java b/telephony/common/com/google/android/mms/pdu/PduPart.java
index 09b775118dc3..09b775118dc3 100644
--- a/telephony/java/com/google/android/mms/pdu/PduPart.java
+++ b/telephony/common/com/google/android/mms/pdu/PduPart.java
diff --git a/telephony/java/com/google/android/mms/pdu/PduPersister.java b/telephony/common/com/google/android/mms/pdu/PduPersister.java
index 93f30657bf1b..93f30657bf1b 100755
--- a/telephony/java/com/google/android/mms/pdu/PduPersister.java
+++ b/telephony/common/com/google/android/mms/pdu/PduPersister.java
diff --git a/telephony/java/com/google/android/mms/pdu/QuotedPrintable.java b/telephony/common/com/google/android/mms/pdu/QuotedPrintable.java
index 9d6535c72e90..9d6535c72e90 100644
--- a/telephony/java/com/google/android/mms/pdu/QuotedPrintable.java
+++ b/telephony/common/com/google/android/mms/pdu/QuotedPrintable.java
diff --git a/telephony/java/com/google/android/mms/pdu/ReadOrigInd.java b/telephony/common/com/google/android/mms/pdu/ReadOrigInd.java
index e38c62dde622..e38c62dde622 100644
--- a/telephony/java/com/google/android/mms/pdu/ReadOrigInd.java
+++ b/telephony/common/com/google/android/mms/pdu/ReadOrigInd.java
diff --git a/telephony/java/com/google/android/mms/pdu/ReadRecInd.java b/telephony/common/com/google/android/mms/pdu/ReadRecInd.java
index 9696bc259d00..9696bc259d00 100644
--- a/telephony/java/com/google/android/mms/pdu/ReadRecInd.java
+++ b/telephony/common/com/google/android/mms/pdu/ReadRecInd.java
diff --git a/telephony/java/com/google/android/mms/pdu/RetrieveConf.java b/telephony/common/com/google/android/mms/pdu/RetrieveConf.java
index 03755af4189c..03755af4189c 100644
--- a/telephony/java/com/google/android/mms/pdu/RetrieveConf.java
+++ b/telephony/common/com/google/android/mms/pdu/RetrieveConf.java
diff --git a/telephony/java/com/google/android/mms/pdu/SendConf.java b/telephony/common/com/google/android/mms/pdu/SendConf.java
index b85982791ada..b85982791ada 100644
--- a/telephony/java/com/google/android/mms/pdu/SendConf.java
+++ b/telephony/common/com/google/android/mms/pdu/SendConf.java
diff --git a/telephony/java/com/google/android/mms/pdu/SendReq.java b/telephony/common/com/google/android/mms/pdu/SendReq.java
index c1b7f934c0f7..c1b7f934c0f7 100644
--- a/telephony/java/com/google/android/mms/pdu/SendReq.java
+++ b/telephony/common/com/google/android/mms/pdu/SendReq.java
diff --git a/telephony/java/com/google/android/mms/pdu/package.html b/telephony/common/com/google/android/mms/pdu/package.html
index c9f96a66ab3b..c9f96a66ab3b 100755
--- a/telephony/java/com/google/android/mms/pdu/package.html
+++ b/telephony/common/com/google/android/mms/pdu/package.html
diff --git a/telephony/java/com/google/android/mms/util/AbstractCache.java b/telephony/common/com/google/android/mms/util/AbstractCache.java
index ab5d48a4ce3d..ab5d48a4ce3d 100644
--- a/telephony/java/com/google/android/mms/util/AbstractCache.java
+++ b/telephony/common/com/google/android/mms/util/AbstractCache.java
diff --git a/telephony/java/com/google/android/mms/util/DownloadDrmHelper.java b/telephony/common/com/google/android/mms/util/DownloadDrmHelper.java
index 118de465a518..118de465a518 100644
--- a/telephony/java/com/google/android/mms/util/DownloadDrmHelper.java
+++ b/telephony/common/com/google/android/mms/util/DownloadDrmHelper.java
diff --git a/telephony/java/com/google/android/mms/util/DrmConvertSession.java b/telephony/common/com/google/android/mms/util/DrmConvertSession.java
index 0e8ec91f4ef6..0e8ec91f4ef6 100644
--- a/telephony/java/com/google/android/mms/util/DrmConvertSession.java
+++ b/telephony/common/com/google/android/mms/util/DrmConvertSession.java
diff --git a/telephony/java/com/google/android/mms/util/PduCache.java b/telephony/common/com/google/android/mms/util/PduCache.java
index 94e38946f632..94e38946f632 100644
--- a/telephony/java/com/google/android/mms/util/PduCache.java
+++ b/telephony/common/com/google/android/mms/util/PduCache.java
diff --git a/telephony/java/com/google/android/mms/util/PduCacheEntry.java b/telephony/common/com/google/android/mms/util/PduCacheEntry.java
index 1ecd1bf93e7f..1ecd1bf93e7f 100644
--- a/telephony/java/com/google/android/mms/util/PduCacheEntry.java
+++ b/telephony/common/com/google/android/mms/util/PduCacheEntry.java
diff --git a/telephony/java/com/google/android/mms/util/SqliteWrapper.java b/telephony/common/com/google/android/mms/util/SqliteWrapper.java
index 2dd1dc11c2a9..2dd1dc11c2a9 100644
--- a/telephony/java/com/google/android/mms/util/SqliteWrapper.java
+++ b/telephony/common/com/google/android/mms/util/SqliteWrapper.java
diff --git a/telephony/java/com/google/android/mms/util/package.html b/telephony/common/com/google/android/mms/util/package.html
index c9f96a66ab3b..c9f96a66ab3b 100755
--- a/telephony/java/com/google/android/mms/util/package.html
+++ b/telephony/common/com/google/android/mms/util/package.html
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index a5af42abe405..24cecb415216 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -4013,13 +4013,21 @@ public final class Telephony {
public static final Uri CONTENT_URI = Uri.parse("content://cellbroadcasts");
/**
- * The id of the subscription which received this cell broadcast message.
+ * The subscription which received this cell broadcast message.
+ * @deprecated use {@link #SLOT_INDEX} instead.
* <P>Type: INTEGER</P>
* @hide
*/
public static final String SUB_ID = "sub_id";
/**
+ * The slot which received this cell broadcast message.
+ * <P>Type: INTEGER</P>
+ * @hide
+ */
+ public static final String SLOT_INDEX = "slot_index";
+
+ /**
* Message geographical scope. Valid values are:
* <ul>
* <li>{@link android.telephony.SmsCbMessage#GEOGRAPHICAL_SCOPE_CELL_WIDE}. meaning the
@@ -4253,7 +4261,7 @@ public final class Telephony {
public static final String MAXIMUM_WAIT_TIME = "maximum_wait_time";
/**
- * Query columns for instantiating {@link android.telephony.CellBroadcastMessage} objects.
+ * Query columns for instantiating com.android.cellbroadcastreceiver.CellBroadcastMessage.
* @hide
*/
@NonNull
@@ -4286,6 +4294,7 @@ public final class Telephony {
*/
public static final String[] QUERY_COLUMNS_FWK = {
_ID,
+ SLOT_INDEX,
GEOGRAPHICAL_SCOPE,
PLMN,
LAC,
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
new file mode 100644
index 000000000000..72f758eba39a
--- /dev/null
+++ b/telephony/java/android/telephony/Annotation.java
@@ -0,0 +1,512 @@
+package android.telephony;
+
+import android.annotation.IntDef;
+import android.telephony.data.ApnSetting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Telephony Annotations.
+ * Telephony sdk is a mainline module and others cannot reference hidden @IntDef. Moving some
+ * telephony annotations to a separate class to allow others statically link to it.
+ *
+ * @hide
+ */
+public class Annotation {
+ @IntDef(prefix = {"DATA_"}, value = {
+ TelephonyManager.DATA_ACTIVITY_NONE,
+ TelephonyManager.DATA_ACTIVITY_IN,
+ TelephonyManager.DATA_ACTIVITY_OUT,
+ TelephonyManager.DATA_ACTIVITY_INOUT,
+ TelephonyManager.DATA_ACTIVITY_DORMANT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DataActivityType {
+ }
+
+ @IntDef(prefix = {"DATA_"}, value = {
+ TelephonyManager.DATA_UNKNOWN,
+ TelephonyManager.DATA_DISCONNECTED,
+ TelephonyManager.DATA_CONNECTING,
+ TelephonyManager.DATA_CONNECTED,
+ TelephonyManager.DATA_SUSPENDED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DataState {
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"RADIO_POWER_"},
+ value = {
+ TelephonyManager.RADIO_POWER_OFF,
+ TelephonyManager.RADIO_POWER_ON,
+ TelephonyManager.RADIO_POWER_UNAVAILABLE,
+ })
+ public @interface RadioPowerState {
+ }
+
+ @IntDef({
+ TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN,
+ TelephonyManager.SIM_ACTIVATION_STATE_ACTIVATING,
+ TelephonyManager.SIM_ACTIVATION_STATE_ACTIVATED,
+ TelephonyManager.SIM_ACTIVATION_STATE_DEACTIVATED,
+ TelephonyManager.SIM_ACTIVATION_STATE_RESTRICTED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SimActivationState {
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"SRVCC_STATE_"},
+ value = {
+ TelephonyManager.SRVCC_STATE_HANDOVER_NONE,
+ TelephonyManager.SRVCC_STATE_HANDOVER_STARTED,
+ TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED,
+ TelephonyManager.SRVCC_STATE_HANDOVER_FAILED,
+ TelephonyManager.SRVCC_STATE_HANDOVER_CANCELED})
+ public @interface SrvccState {
+ }
+
+ @IntDef(prefix = {"CALL_STATE_"}, value = {
+ TelephonyManager.CALL_STATE_IDLE,
+ TelephonyManager.CALL_STATE_RINGING,
+ TelephonyManager.CALL_STATE_OFFHOOK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CallState {
+ }
+
+ @IntDef({
+ TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ TelephonyManager.NETWORK_TYPE_GPRS,
+ TelephonyManager.NETWORK_TYPE_EDGE,
+ TelephonyManager.NETWORK_TYPE_UMTS,
+ TelephonyManager.NETWORK_TYPE_CDMA,
+ TelephonyManager.NETWORK_TYPE_EVDO_0,
+ TelephonyManager.NETWORK_TYPE_EVDO_A,
+ TelephonyManager.NETWORK_TYPE_1xRTT,
+ TelephonyManager.NETWORK_TYPE_HSDPA,
+ TelephonyManager.NETWORK_TYPE_HSUPA,
+ TelephonyManager.NETWORK_TYPE_HSPA,
+ TelephonyManager.NETWORK_TYPE_IDEN,
+ TelephonyManager.NETWORK_TYPE_EVDO_B,
+ TelephonyManager.NETWORK_TYPE_LTE,
+ TelephonyManager.NETWORK_TYPE_EHRPD,
+ TelephonyManager.NETWORK_TYPE_HSPAP,
+ TelephonyManager.NETWORK_TYPE_GSM,
+ TelephonyManager.NETWORK_TYPE_TD_SCDMA,
+ TelephonyManager.NETWORK_TYPE_IWLAN,
+ TelephonyManager.NETWORK_TYPE_LTE_CA,
+ TelephonyManager.NETWORK_TYPE_NR,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NetworkType {
+ }
+
+ @IntDef(flag = true, prefix = {"TYPE_"}, value = {
+ ApnSetting.TYPE_DEFAULT,
+ ApnSetting.TYPE_MMS,
+ ApnSetting.TYPE_SUPL,
+ ApnSetting.TYPE_DUN,
+ ApnSetting.TYPE_HIPRI,
+ ApnSetting.TYPE_FOTA,
+ ApnSetting.TYPE_IMS,
+ ApnSetting.TYPE_CBS,
+ ApnSetting.TYPE_IA,
+ ApnSetting.TYPE_EMERGENCY,
+ ApnSetting.TYPE_MCX
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ApnType {
+ }
+
+ @IntDef(value = {
+ DataFailCause.NONE,
+ DataFailCause.OPERATOR_BARRED,
+ DataFailCause.NAS_SIGNALLING,
+ DataFailCause.LLC_SNDCP,
+ DataFailCause.INSUFFICIENT_RESOURCES,
+ DataFailCause.MISSING_UNKNOWN_APN,
+ DataFailCause.UNKNOWN_PDP_ADDRESS_TYPE,
+ DataFailCause.USER_AUTHENTICATION,
+ DataFailCause.ACTIVATION_REJECT_GGSN,
+ DataFailCause.ACTIVATION_REJECT_UNSPECIFIED,
+ DataFailCause.SERVICE_OPTION_NOT_SUPPORTED,
+ DataFailCause.SERVICE_OPTION_NOT_SUBSCRIBED,
+ DataFailCause.SERVICE_OPTION_OUT_OF_ORDER,
+ DataFailCause.NSAPI_IN_USE,
+ DataFailCause.REGULAR_DEACTIVATION,
+ DataFailCause.QOS_NOT_ACCEPTED,
+ DataFailCause.NETWORK_FAILURE,
+ DataFailCause.UMTS_REACTIVATION_REQ,
+ DataFailCause.FEATURE_NOT_SUPP,
+ DataFailCause.TFT_SEMANTIC_ERROR,
+ DataFailCause.TFT_SYTAX_ERROR,
+ DataFailCause.UNKNOWN_PDP_CONTEXT,
+ DataFailCause.FILTER_SEMANTIC_ERROR,
+ DataFailCause.FILTER_SYTAX_ERROR,
+ DataFailCause.PDP_WITHOUT_ACTIVE_TFT,
+ DataFailCause.ACTIVATION_REJECTED_BCM_VIOLATION,
+ DataFailCause.ONLY_IPV4_ALLOWED,
+ DataFailCause.ONLY_IPV6_ALLOWED,
+ DataFailCause.ONLY_SINGLE_BEARER_ALLOWED,
+ DataFailCause.ESM_INFO_NOT_RECEIVED,
+ DataFailCause.PDN_CONN_DOES_NOT_EXIST,
+ DataFailCause.MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED,
+ DataFailCause.COLLISION_WITH_NETWORK_INITIATED_REQUEST,
+ DataFailCause.ONLY_IPV4V6_ALLOWED,
+ DataFailCause.ONLY_NON_IP_ALLOWED,
+ DataFailCause.UNSUPPORTED_QCI_VALUE,
+ DataFailCause.BEARER_HANDLING_NOT_SUPPORTED,
+ DataFailCause.ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED,
+ DataFailCause.UNSUPPORTED_APN_IN_CURRENT_PLMN,
+ DataFailCause.INVALID_TRANSACTION_ID,
+ DataFailCause.MESSAGE_INCORRECT_SEMANTIC,
+ DataFailCause.INVALID_MANDATORY_INFO,
+ DataFailCause.MESSAGE_TYPE_UNSUPPORTED,
+ DataFailCause.MSG_TYPE_NONCOMPATIBLE_STATE,
+ DataFailCause.UNKNOWN_INFO_ELEMENT,
+ DataFailCause.CONDITIONAL_IE_ERROR,
+ DataFailCause.MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE,
+ DataFailCause.PROTOCOL_ERRORS,
+ DataFailCause.APN_TYPE_CONFLICT,
+ DataFailCause.INVALID_PCSCF_ADDR,
+ DataFailCause.INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN,
+ DataFailCause.EMM_ACCESS_BARRED,
+ DataFailCause.EMERGENCY_IFACE_ONLY,
+ DataFailCause.IFACE_MISMATCH,
+ DataFailCause.COMPANION_IFACE_IN_USE,
+ DataFailCause.IP_ADDRESS_MISMATCH,
+ DataFailCause.IFACE_AND_POL_FAMILY_MISMATCH,
+ DataFailCause.EMM_ACCESS_BARRED_INFINITE_RETRY,
+ DataFailCause.AUTH_FAILURE_ON_EMERGENCY_CALL,
+ DataFailCause.INVALID_DNS_ADDR,
+ DataFailCause.INVALID_PCSCF_OR_DNS_ADDRESS,
+ DataFailCause.CALL_PREEMPT_BY_EMERGENCY_APN,
+ DataFailCause.UE_INITIATED_DETACH_OR_DISCONNECT,
+ DataFailCause.MIP_FA_REASON_UNSPECIFIED,
+ DataFailCause.MIP_FA_ADMIN_PROHIBITED,
+ DataFailCause.MIP_FA_INSUFFICIENT_RESOURCES,
+ DataFailCause.MIP_FA_MOBILE_NODE_AUTHENTICATION_FAILURE,
+ DataFailCause.MIP_FA_HOME_AGENT_AUTHENTICATION_FAILURE,
+ DataFailCause.MIP_FA_REQUESTED_LIFETIME_TOO_LONG,
+ DataFailCause.MIP_FA_MALFORMED_REQUEST,
+ DataFailCause.MIP_FA_MALFORMED_REPLY,
+ DataFailCause.MIP_FA_ENCAPSULATION_UNAVAILABLE,
+ DataFailCause.MIP_FA_VJ_HEADER_COMPRESSION_UNAVAILABLE,
+ DataFailCause.MIP_FA_REVERSE_TUNNEL_UNAVAILABLE,
+ DataFailCause.MIP_FA_REVERSE_TUNNEL_IS_MANDATORY,
+ DataFailCause.MIP_FA_DELIVERY_STYLE_NOT_SUPPORTED,
+ DataFailCause.MIP_FA_MISSING_NAI,
+ DataFailCause.MIP_FA_MISSING_HOME_AGENT,
+ DataFailCause.MIP_FA_MISSING_HOME_ADDRESS,
+ DataFailCause.MIP_FA_UNKNOWN_CHALLENGE,
+ DataFailCause.MIP_FA_MISSING_CHALLENGE,
+ DataFailCause.MIP_FA_STALE_CHALLENGE,
+ DataFailCause.MIP_HA_REASON_UNSPECIFIED,
+ DataFailCause.MIP_HA_ADMIN_PROHIBITED,
+ DataFailCause.MIP_HA_INSUFFICIENT_RESOURCES,
+ DataFailCause.MIP_HA_MOBILE_NODE_AUTHENTICATION_FAILURE,
+ DataFailCause.MIP_HA_FOREIGN_AGENT_AUTHENTICATION_FAILURE,
+ DataFailCause.MIP_HA_REGISTRATION_ID_MISMATCH,
+ DataFailCause.MIP_HA_MALFORMED_REQUEST,
+ DataFailCause.MIP_HA_UNKNOWN_HOME_AGENT_ADDRESS,
+ DataFailCause.MIP_HA_REVERSE_TUNNEL_UNAVAILABLE,
+ DataFailCause.MIP_HA_REVERSE_TUNNEL_IS_MANDATORY,
+ DataFailCause.MIP_HA_ENCAPSULATION_UNAVAILABLE,
+ DataFailCause.CLOSE_IN_PROGRESS,
+ DataFailCause.NETWORK_INITIATED_TERMINATION,
+ DataFailCause.MODEM_APP_PREEMPTED,
+ DataFailCause.PDN_IPV4_CALL_DISALLOWED,
+ DataFailCause.PDN_IPV4_CALL_THROTTLED,
+ DataFailCause.PDN_IPV6_CALL_DISALLOWED,
+ DataFailCause.PDN_IPV6_CALL_THROTTLED,
+ DataFailCause.MODEM_RESTART,
+ DataFailCause.PDP_PPP_NOT_SUPPORTED,
+ DataFailCause.UNPREFERRED_RAT,
+ DataFailCause.PHYSICAL_LINK_CLOSE_IN_PROGRESS,
+ DataFailCause.APN_PENDING_HANDOVER,
+ DataFailCause.PROFILE_BEARER_INCOMPATIBLE,
+ DataFailCause.SIM_CARD_CHANGED,
+ DataFailCause.LOW_POWER_MODE_OR_POWERING_DOWN,
+ DataFailCause.APN_DISABLED,
+ DataFailCause.MAX_PPP_INACTIVITY_TIMER_EXPIRED,
+ DataFailCause.IPV6_ADDRESS_TRANSFER_FAILED,
+ DataFailCause.TRAT_SWAP_FAILED,
+ DataFailCause.EHRPD_TO_HRPD_FALLBACK,
+ DataFailCause.MIP_CONFIG_FAILURE,
+ DataFailCause.PDN_INACTIVITY_TIMER_EXPIRED,
+ DataFailCause.MAX_IPV4_CONNECTIONS,
+ DataFailCause.MAX_IPV6_CONNECTIONS,
+ DataFailCause.APN_MISMATCH,
+ DataFailCause.IP_VERSION_MISMATCH,
+ DataFailCause.DUN_CALL_DISALLOWED,
+ DataFailCause.INTERNAL_EPC_NONEPC_TRANSITION,
+ DataFailCause.INTERFACE_IN_USE,
+ DataFailCause.APN_DISALLOWED_ON_ROAMING,
+ DataFailCause.APN_PARAMETERS_CHANGED,
+ DataFailCause.NULL_APN_DISALLOWED,
+ DataFailCause.THERMAL_MITIGATION,
+ DataFailCause.DATA_SETTINGS_DISABLED,
+ DataFailCause.DATA_ROAMING_SETTINGS_DISABLED,
+ DataFailCause.DDS_SWITCHED,
+ DataFailCause.FORBIDDEN_APN_NAME,
+ DataFailCause.DDS_SWITCH_IN_PROGRESS,
+ DataFailCause.CALL_DISALLOWED_IN_ROAMING,
+ DataFailCause.NON_IP_NOT_SUPPORTED,
+ DataFailCause.PDN_NON_IP_CALL_THROTTLED,
+ DataFailCause.PDN_NON_IP_CALL_DISALLOWED,
+ DataFailCause.CDMA_LOCK,
+ DataFailCause.CDMA_INTERCEPT,
+ DataFailCause.CDMA_REORDER,
+ DataFailCause.CDMA_RELEASE_DUE_TO_SO_REJECTION,
+ DataFailCause.CDMA_INCOMING_CALL,
+ DataFailCause.CDMA_ALERT_STOP,
+ DataFailCause.CHANNEL_ACQUISITION_FAILURE,
+ DataFailCause.MAX_ACCESS_PROBE,
+ DataFailCause.CONCURRENT_SERVICE_NOT_SUPPORTED_BY_BASE_STATION,
+ DataFailCause.NO_RESPONSE_FROM_BASE_STATION,
+ DataFailCause.REJECTED_BY_BASE_STATION,
+ DataFailCause.CONCURRENT_SERVICES_INCOMPATIBLE,
+ DataFailCause.NO_CDMA_SERVICE,
+ DataFailCause.RUIM_NOT_PRESENT,
+ DataFailCause.CDMA_RETRY_ORDER,
+ DataFailCause.ACCESS_BLOCK,
+ DataFailCause.ACCESS_BLOCK_ALL,
+ DataFailCause.IS707B_MAX_ACCESS_PROBES,
+ DataFailCause.THERMAL_EMERGENCY,
+ DataFailCause.CONCURRENT_SERVICES_NOT_ALLOWED,
+ DataFailCause.INCOMING_CALL_REJECTED,
+ DataFailCause.NO_SERVICE_ON_GATEWAY,
+ DataFailCause.NO_GPRS_CONTEXT,
+ DataFailCause.ILLEGAL_MS,
+ DataFailCause.ILLEGAL_ME,
+ DataFailCause.GPRS_SERVICES_AND_NON_GPRS_SERVICES_NOT_ALLOWED,
+ DataFailCause.GPRS_SERVICES_NOT_ALLOWED,
+ DataFailCause.MS_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK,
+ DataFailCause.IMPLICITLY_DETACHED,
+ DataFailCause.PLMN_NOT_ALLOWED,
+ DataFailCause.LOCATION_AREA_NOT_ALLOWED,
+ DataFailCause.GPRS_SERVICES_NOT_ALLOWED_IN_THIS_PLMN,
+ DataFailCause.PDP_DUPLICATE,
+ DataFailCause.UE_RAT_CHANGE,
+ DataFailCause.CONGESTION,
+ DataFailCause.NO_PDP_CONTEXT_ACTIVATED,
+ DataFailCause.ACCESS_CLASS_DSAC_REJECTION,
+ DataFailCause.PDP_ACTIVATE_MAX_RETRY_FAILED,
+ DataFailCause.RADIO_ACCESS_BEARER_FAILURE,
+ DataFailCause.ESM_UNKNOWN_EPS_BEARER_CONTEXT,
+ DataFailCause.DRB_RELEASED_BY_RRC,
+ DataFailCause.CONNECTION_RELEASED,
+ DataFailCause.EMM_DETACHED,
+ DataFailCause.EMM_ATTACH_FAILED,
+ DataFailCause.EMM_ATTACH_STARTED,
+ DataFailCause.LTE_NAS_SERVICE_REQUEST_FAILED,
+ DataFailCause.DUPLICATE_BEARER_ID,
+ DataFailCause.ESM_COLLISION_SCENARIOS,
+ DataFailCause.ESM_BEARER_DEACTIVATED_TO_SYNC_WITH_NETWORK,
+ DataFailCause.ESM_NW_ACTIVATED_DED_BEARER_WITH_ID_OF_DEF_BEARER,
+ DataFailCause.ESM_BAD_OTA_MESSAGE,
+ DataFailCause.ESM_DOWNLOAD_SERVER_REJECTED_THE_CALL,
+ DataFailCause.ESM_CONTEXT_TRANSFERRED_DUE_TO_IRAT,
+ DataFailCause.DS_EXPLICIT_DEACTIVATION,
+ DataFailCause.ESM_LOCAL_CAUSE_NONE,
+ DataFailCause.LTE_THROTTLING_NOT_REQUIRED,
+ DataFailCause.ACCESS_CONTROL_LIST_CHECK_FAILURE,
+ DataFailCause.SERVICE_NOT_ALLOWED_ON_PLMN,
+ DataFailCause.EMM_T3417_EXPIRED,
+ DataFailCause.EMM_T3417_EXT_EXPIRED,
+ DataFailCause.RRC_UPLINK_DATA_TRANSMISSION_FAILURE,
+ DataFailCause.RRC_UPLINK_DELIVERY_FAILED_DUE_TO_HANDOVER,
+ DataFailCause.RRC_UPLINK_CONNECTION_RELEASE,
+ DataFailCause.RRC_UPLINK_RADIO_LINK_FAILURE,
+ DataFailCause.RRC_UPLINK_ERROR_REQUEST_FROM_NAS,
+ DataFailCause.RRC_CONNECTION_ACCESS_STRATUM_FAILURE,
+ DataFailCause.RRC_CONNECTION_ANOTHER_PROCEDURE_IN_PROGRESS,
+ DataFailCause.RRC_CONNECTION_ACCESS_BARRED,
+ DataFailCause.RRC_CONNECTION_CELL_RESELECTION,
+ DataFailCause.RRC_CONNECTION_CONFIG_FAILURE,
+ DataFailCause.RRC_CONNECTION_TIMER_EXPIRED,
+ DataFailCause.RRC_CONNECTION_LINK_FAILURE,
+ DataFailCause.RRC_CONNECTION_CELL_NOT_CAMPED,
+ DataFailCause.RRC_CONNECTION_SYSTEM_INTERVAL_FAILURE,
+ DataFailCause.RRC_CONNECTION_REJECT_BY_NETWORK,
+ DataFailCause.RRC_CONNECTION_NORMAL_RELEASE,
+ DataFailCause.RRC_CONNECTION_RADIO_LINK_FAILURE,
+ DataFailCause.RRC_CONNECTION_REESTABLISHMENT_FAILURE,
+ DataFailCause.RRC_CONNECTION_OUT_OF_SERVICE_DURING_CELL_REGISTER,
+ DataFailCause.RRC_CONNECTION_ABORT_REQUEST,
+ DataFailCause.RRC_CONNECTION_SYSTEM_INFORMATION_BLOCK_READ_ERROR,
+ DataFailCause.NETWORK_INITIATED_DETACH_WITH_AUTO_REATTACH,
+ DataFailCause.NETWORK_INITIATED_DETACH_NO_AUTO_REATTACH,
+ DataFailCause.ESM_PROCEDURE_TIME_OUT,
+ DataFailCause.INVALID_CONNECTION_ID,
+ DataFailCause.MAXIMIUM_NSAPIS_EXCEEDED,
+ DataFailCause.INVALID_PRIMARY_NSAPI,
+ DataFailCause.CANNOT_ENCODE_OTA_MESSAGE,
+ DataFailCause.RADIO_ACCESS_BEARER_SETUP_FAILURE,
+ DataFailCause.PDP_ESTABLISH_TIMEOUT_EXPIRED,
+ DataFailCause.PDP_MODIFY_TIMEOUT_EXPIRED,
+ DataFailCause.PDP_INACTIVE_TIMEOUT_EXPIRED,
+ DataFailCause.PDP_LOWERLAYER_ERROR,
+ DataFailCause.PDP_MODIFY_COLLISION,
+ DataFailCause.MAXINUM_SIZE_OF_L2_MESSAGE_EXCEEDED,
+ DataFailCause.NAS_REQUEST_REJECTED_BY_NETWORK,
+ DataFailCause.RRC_CONNECTION_INVALID_REQUEST,
+ DataFailCause.RRC_CONNECTION_TRACKING_AREA_ID_CHANGED,
+ DataFailCause.RRC_CONNECTION_RF_UNAVAILABLE,
+ DataFailCause.RRC_CONNECTION_ABORTED_DUE_TO_IRAT_CHANGE,
+ DataFailCause.RRC_CONNECTION_RELEASED_SECURITY_NOT_ACTIVE,
+ DataFailCause.RRC_CONNECTION_ABORTED_AFTER_HANDOVER,
+ DataFailCause.RRC_CONNECTION_ABORTED_AFTER_IRAT_CELL_CHANGE,
+ DataFailCause.RRC_CONNECTION_ABORTED_DURING_IRAT_CELL_CHANGE,
+ DataFailCause.IMSI_UNKNOWN_IN_HOME_SUBSCRIBER_SERVER,
+ DataFailCause.IMEI_NOT_ACCEPTED,
+ DataFailCause.EPS_SERVICES_AND_NON_EPS_SERVICES_NOT_ALLOWED,
+ DataFailCause.EPS_SERVICES_NOT_ALLOWED_IN_PLMN,
+ DataFailCause.MSC_TEMPORARILY_NOT_REACHABLE,
+ DataFailCause.CS_DOMAIN_NOT_AVAILABLE,
+ DataFailCause.ESM_FAILURE,
+ DataFailCause.MAC_FAILURE,
+ DataFailCause.SYNCHRONIZATION_FAILURE,
+ DataFailCause.UE_SECURITY_CAPABILITIES_MISMATCH,
+ DataFailCause.SECURITY_MODE_REJECTED,
+ DataFailCause.UNACCEPTABLE_NON_EPS_AUTHENTICATION,
+ DataFailCause.CS_FALLBACK_CALL_ESTABLISHMENT_NOT_ALLOWED,
+ DataFailCause.NO_EPS_BEARER_CONTEXT_ACTIVATED,
+ DataFailCause.INVALID_EMM_STATE,
+ DataFailCause.NAS_LAYER_FAILURE,
+ DataFailCause.MULTIPLE_PDP_CALL_NOT_ALLOWED,
+ DataFailCause.EMBMS_NOT_ENABLED,
+ DataFailCause.IRAT_HANDOVER_FAILED,
+ DataFailCause.EMBMS_REGULAR_DEACTIVATION,
+ DataFailCause.TEST_LOOPBACK_REGULAR_DEACTIVATION,
+ DataFailCause.LOWER_LAYER_REGISTRATION_FAILURE,
+ DataFailCause.DATA_PLAN_EXPIRED,
+ DataFailCause.UMTS_HANDOVER_TO_IWLAN,
+ DataFailCause.EVDO_CONNECTION_DENY_BY_GENERAL_OR_NETWORK_BUSY,
+ DataFailCause.EVDO_CONNECTION_DENY_BY_BILLING_OR_AUTHENTICATION_FAILURE,
+ DataFailCause.EVDO_HDR_CHANGED,
+ DataFailCause.EVDO_HDR_EXITED,
+ DataFailCause.EVDO_HDR_NO_SESSION,
+ DataFailCause.EVDO_USING_GPS_FIX_INSTEAD_OF_HDR_CALL,
+ DataFailCause.EVDO_HDR_CONNECTION_SETUP_TIMEOUT,
+ DataFailCause.FAILED_TO_ACQUIRE_COLOCATED_HDR,
+ DataFailCause.OTASP_COMMIT_IN_PROGRESS,
+ DataFailCause.NO_HYBRID_HDR_SERVICE,
+ DataFailCause.HDR_NO_LOCK_GRANTED,
+ DataFailCause.DBM_OR_SMS_IN_PROGRESS,
+ DataFailCause.HDR_FADE,
+ DataFailCause.HDR_ACCESS_FAILURE,
+ DataFailCause.UNSUPPORTED_1X_PREV,
+ DataFailCause.LOCAL_END,
+ DataFailCause.NO_SERVICE,
+ DataFailCause.FADE,
+ DataFailCause.NORMAL_RELEASE,
+ DataFailCause.ACCESS_ATTEMPT_ALREADY_IN_PROGRESS,
+ DataFailCause.REDIRECTION_OR_HANDOFF_IN_PROGRESS,
+ DataFailCause.EMERGENCY_MODE,
+ DataFailCause.PHONE_IN_USE,
+ DataFailCause.INVALID_MODE,
+ DataFailCause.INVALID_SIM_STATE,
+ DataFailCause.NO_COLLOCATED_HDR,
+ DataFailCause.UE_IS_ENTERING_POWERSAVE_MODE,
+ DataFailCause.DUAL_SWITCH,
+ DataFailCause.PPP_TIMEOUT,
+ DataFailCause.PPP_AUTH_FAILURE,
+ DataFailCause.PPP_OPTION_MISMATCH,
+ DataFailCause.PPP_PAP_FAILURE,
+ DataFailCause.PPP_CHAP_FAILURE,
+ DataFailCause.PPP_CLOSE_IN_PROGRESS,
+ DataFailCause.LIMITED_TO_IPV4,
+ DataFailCause.LIMITED_TO_IPV6,
+ DataFailCause.VSNCP_TIMEOUT,
+ DataFailCause.VSNCP_GEN_ERROR,
+ DataFailCause.VSNCP_APN_UNATHORIZED,
+ DataFailCause.VSNCP_PDN_LIMIT_EXCEEDED,
+ DataFailCause.VSNCP_NO_PDN_GATEWAY_ADDRESS,
+ DataFailCause.VSNCP_PDN_GATEWAY_UNREACHABLE,
+ DataFailCause.VSNCP_PDN_GATEWAY_REJECT,
+ DataFailCause.VSNCP_INSUFFICIENT_PARAMETERS,
+ DataFailCause.VSNCP_RESOURCE_UNAVAILABLE,
+ DataFailCause.VSNCP_ADMINISTRATIVELY_PROHIBITED,
+ DataFailCause.VSNCP_PDN_ID_IN_USE,
+ DataFailCause.VSNCP_SUBSCRIBER_LIMITATION,
+ DataFailCause.VSNCP_PDN_EXISTS_FOR_THIS_APN,
+ DataFailCause.VSNCP_RECONNECT_NOT_ALLOWED,
+ DataFailCause.IPV6_PREFIX_UNAVAILABLE,
+ DataFailCause.HANDOFF_PREFERENCE_CHANGED,
+ DataFailCause.OEM_DCFAILCAUSE_1,
+ DataFailCause.OEM_DCFAILCAUSE_2,
+ DataFailCause.OEM_DCFAILCAUSE_3,
+ DataFailCause.OEM_DCFAILCAUSE_4,
+ DataFailCause.OEM_DCFAILCAUSE_5,
+ DataFailCause.OEM_DCFAILCAUSE_6,
+ DataFailCause.OEM_DCFAILCAUSE_7,
+ DataFailCause.OEM_DCFAILCAUSE_8,
+ DataFailCause.OEM_DCFAILCAUSE_9,
+ DataFailCause.OEM_DCFAILCAUSE_10,
+ DataFailCause.OEM_DCFAILCAUSE_11,
+ DataFailCause.OEM_DCFAILCAUSE_12,
+ DataFailCause.OEM_DCFAILCAUSE_13,
+ DataFailCause.OEM_DCFAILCAUSE_14,
+ DataFailCause.OEM_DCFAILCAUSE_15,
+ DataFailCause.REGISTRATION_FAIL,
+ DataFailCause.GPRS_REGISTRATION_FAIL,
+ DataFailCause.SIGNAL_LOST,
+ DataFailCause.PREF_RADIO_TECH_CHANGED,
+ DataFailCause.RADIO_POWER_OFF,
+ DataFailCause.TETHERED_CALL_ACTIVE,
+ DataFailCause.ERROR_UNSPECIFIED,
+ DataFailCause.UNKNOWN,
+ DataFailCause.RADIO_NOT_AVAILABLE,
+ DataFailCause.UNACCEPTABLE_NETWORK_PARAMETER,
+ DataFailCause.CONNECTION_TO_DATACONNECTIONAC_BROKEN,
+ DataFailCause.LOST_CONNECTION,
+ DataFailCause.RESET_BY_FRAMEWORK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DataFailureCause {
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"PRECISE_CALL_STATE_"},
+ value = {
+ PreciseCallState.PRECISE_CALL_STATE_NOT_VALID,
+ PreciseCallState.PRECISE_CALL_STATE_IDLE,
+ PreciseCallState.PRECISE_CALL_STATE_ACTIVE,
+ PreciseCallState.PRECISE_CALL_STATE_HOLDING,
+ PreciseCallState.PRECISE_CALL_STATE_DIALING,
+ PreciseCallState.PRECISE_CALL_STATE_ALERTING,
+ PreciseCallState. PRECISE_CALL_STATE_INCOMING,
+ PreciseCallState.PRECISE_CALL_STATE_WAITING,
+ PreciseCallState.PRECISE_CALL_STATE_DISCONNECTED,
+ PreciseCallState.PRECISE_CALL_STATE_DISCONNECTING})
+ public @interface PreciseCallStates {}
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"RIL_RADIO_TECHNOLOGY_" }, value = {
+ ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN,
+ ServiceState.RIL_RADIO_TECHNOLOGY_GPRS,
+ ServiceState.RIL_RADIO_TECHNOLOGY_EDGE,
+ ServiceState.RIL_RADIO_TECHNOLOGY_UMTS,
+ ServiceState.RIL_RADIO_TECHNOLOGY_IS95A,
+ ServiceState.RIL_RADIO_TECHNOLOGY_IS95B,
+ ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT,
+ ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0,
+ ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A,
+ ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA,
+ ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA,
+ ServiceState.RIL_RADIO_TECHNOLOGY_HSPA,
+ ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B,
+ ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD,
+ ServiceState.RIL_RADIO_TECHNOLOGY_LTE,
+ ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP,
+ ServiceState.RIL_RADIO_TECHNOLOGY_GSM,
+ ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA,
+ ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN,
+ ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA,
+ ServiceState.RIL_RADIO_TECHNOLOGY_NR})
+ public @interface RilRadioTechnology {}
+}
diff --git a/telephony/java/android/telephony/CallAttributes.java b/telephony/java/android/telephony/CallAttributes.java
index 1c03d8089df7..cd830adf23b0 100644
--- a/telephony/java/android/telephony/CallAttributes.java
+++ b/telephony/java/android/telephony/CallAttributes.java
@@ -21,8 +21,8 @@ import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.TelephonyManager.NetworkType;
+import android.telephony.Annotation.NetworkType;
import java.util.Objects;
/**
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 3056f9b6dfa8..e2f60b857547 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -526,6 +526,15 @@ public class CarrierConfigManager {
"default_vm_number_roaming_string";
/**
+ * Where there is no preloaded voicemail number on a SIM card, specifies the carrier's default
+ * voicemail number while the device is both roaming and not registered for IMS.
+ * When empty string, no default voicemail number is specified for roaming network and
+ * unregistered state in IMS.
+ */
+ public static final String KEY_DEFAULT_VM_NUMBER_ROAMING_AND_IMS_UNREGISTERED_STRING =
+ "default_vm_number_roaming_and_ims_unregistered_string";
+
+ /**
* Flag that specifies to use the user's own phone number as the voicemail number when there is
* no pre-loaded voicemail number on the SIM card.
* <p>
@@ -1753,9 +1762,8 @@ public class CarrierConfigManager {
"allow_emergency_video_calls_bool";
/**
- * Flag indicating whether the carrier supports RCS presence indication for video calls. When
- * {@code true}, the carrier supports RCS presence indication for video calls. When presence
- * is supported, the device should use the
+ * Flag indicating whether the carrier supports RCS presence indication for
+ * User Capability Exchange (UCE). When presence is supported, the device should use the
* {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE} bit mask and set the
* {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE_VT_CAPABLE} bit to indicate
* whether each contact supports video calling. The UI is made aware that presence is enabled
@@ -1766,6 +1774,12 @@ public class CarrierConfigManager {
public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
/**
+ * Flag indicating whether the carrier supports RCS SIP OPTIONS indication for
+ * User Capability Exchange (UCE).
+ */
+ public static final String KEY_USE_RCS_SIP_OPTIONS_BOOL = "use_rcs_sip_options_bool";
+
+ /**
* The duration in seconds that platform call and message blocking is disabled after the user
* contacts emergency services. Platform considers values for below cases:
* 1) 0 <= VALUE <= 604800(one week): the value will be used as the duration directly.
@@ -2839,7 +2853,7 @@ public class CarrierConfigManager {
"ping_test_before_data_switch_bool";
/**
- * Controls time in milli seconds until DcTracker reevaluates 5G connection state.
+ * Controls time in milliseconds until DcTracker reevaluates 5G connection state.
* @hide
*/
public static final String KEY_5G_WATCHDOG_TIME_MS_LONG =
@@ -3221,6 +3235,13 @@ public class CarrierConfigManager {
public static final String KEY_CARRIER_CERTIFICATE_STRING_ARRAY =
"carrier_certificate_string_array";
+ /**
+ * DisconnectCause array to play busy tone. Value should be array of
+ * {@link android.telephony.DisconnectCause}.
+ */
+ public static final String KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY =
+ "disconnect_cause_play_busytone_int_array";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -3247,6 +3268,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL, true);
sDefaults.putString(KEY_DEFAULT_VM_NUMBER_STRING, "");
sDefaults.putString(KEY_DEFAULT_VM_NUMBER_ROAMING_STRING, "");
+ sDefaults.putString(KEY_DEFAULT_VM_NUMBER_ROAMING_AND_IMS_UNREGISTERED_STRING, "");
sDefaults.putBoolean(KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL, false);
sDefaults.putBoolean(KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS, true);
sDefaults.putBoolean(KEY_VILTE_DATA_IS_METERED_BOOL, true);
@@ -3464,6 +3486,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL, true);
sDefaults.putInt(KEY_EMERGENCY_SMS_MODE_TIMER_MS_INT, 0);
sDefaults.putBoolean(KEY_USE_RCS_PRESENCE_BOOL, false);
+ sDefaults.putBoolean(KEY_USE_RCS_SIP_OPTIONS_BOOL, false);
sDefaults.putBoolean(KEY_FORCE_IMEI_BOOL, false);
sDefaults.putInt(
KEY_CDMA_ROAMING_MODE_INT, TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT);
@@ -3653,6 +3676,8 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
sDefaults.putAll(Ims.getDefaults());
sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, null);
+ sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY,
+ new int[] {4 /* BUSY */});
}
/**
diff --git a/telephony/java/android/telephony/CellBroadcastService.java b/telephony/java/android/telephony/CellBroadcastService.java
new file mode 100644
index 000000000000..46eb9df8bdad
--- /dev/null
+++ b/telephony/java/android/telephony/CellBroadcastService.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.CallSuper;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.telephony.cdma.CdmaSmsCbProgramData;
+
+/**
+ * A service which exposes the cell broadcast handling module to the system.
+ * <p>
+ * To extend this class, you must declare the service in your manifest file to require the
+ * {@link android.Manifest.permission#BIND_CELL_BROADCAST_SERVICE} permission and include an intent
+ * filter with the {@link #CELL_BROADCAST_SERVICE_INTERFACE}.
+ * Implementations of this service should run in the phone process and with its UID.
+ * <p>
+ * For example:
+ * <pre>{@code
+ * <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ * android:sharedUserId="android.uid.phone">
+ * <service android:name=".MyCellBroadcastService"
+ * android:label="@string/service_name"
+ * android:process="com.android.phone"
+ * android:exported="true"
+ * android:permission="android.permission.BIND_CELL_BROADCAST_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.telephony.CellBroadcastService" />
+ * </intent-filter>
+ * </service>
+ * </manifest>
+ * }</pre>
+ * @hide
+ */
+@SystemApi
+public abstract class CellBroadcastService extends Service {
+
+ public static final String CELL_BROADCAST_SERVICE_INTERFACE =
+ "android.telephony.CellBroadcastService";
+
+ private final ICellBroadcastService.Stub mStubWrapper;
+
+ public CellBroadcastService() {
+ mStubWrapper = new ICellBroadcastServiceWrapper();
+ }
+
+ /**
+ * Handle a GSM cell broadcast SMS message forwarded from the system.
+ * @param slotIndex the index of the slot which received the message
+ * @param message the SMS PDU
+ */
+ public abstract void onGsmCellBroadcastSms(int slotIndex, byte[] message);
+
+ /**
+ * Handle a CDMA cell broadcast SMS message forwarded from the system.
+ * @param slotIndex the index of the slot which received the message
+ * @param bearerData the CDMA SMS bearer data
+ * @param serviceCategory the CDMA SCPT service category
+ */
+ public abstract void onCdmaCellBroadcastSms(int slotIndex, byte[] bearerData,
+ @CdmaSmsCbProgramData.Category int serviceCategory);
+
+ /**
+ * If overriding this method, call through to the super method for any unknown actions.
+ * {@inheritDoc}
+ */
+ @Override
+ @CallSuper
+ public IBinder onBind(Intent intent) {
+ return mStubWrapper;
+ }
+
+ /**
+ * A wrapper around ICellBroadcastService that forwards calls to implementations of
+ * {@link CellBroadcastService}.
+ * @hide
+ */
+ public class ICellBroadcastServiceWrapper extends ICellBroadcastService.Stub {
+ /**
+ * Handle a GSM cell broadcast SMS.
+ * @param slotIndex the index of the slot which received the broadcast
+ * @param message the SMS message PDU
+ */
+ @Override
+ public void handleGsmCellBroadcastSms(int slotIndex, byte[] message) {
+ CellBroadcastService.this.onGsmCellBroadcastSms(slotIndex, message);
+ }
+
+ /**
+ * Handle a CDMA cell broadcast SMS.
+ * @param slotIndex the index of the slot which received the broadcast
+ * @param bearerData the CDMA SMS bearer data
+ * @param serviceCategory the CDMA SCPT service category
+ */
+ @Override
+ public void handleCdmaCellBroadcastSms(int slotIndex, byte[] bearerData,
+ int serviceCategory) {
+ CellBroadcastService.this.onCdmaCellBroadcastSms(slotIndex, bearerData,
+ serviceCategory);
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index 432978d1c866..b7dab161c331 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -35,6 +35,15 @@ public abstract class CellIdentity implements Parcelable {
/** @hide */
public static final int INVALID_CHANNEL_NUMBER = -1;
+ /**
+ * parameters for validation
+ * @hide
+ */
+ public static final int MCC_LENGTH = 3;
+
+ private static final int MNC_MIN_LENGTH = 2;
+ private static final int MNC_MAX_LENGTH = 3;
+
// Log tag
/** @hide */
protected final String mTag;
@@ -207,6 +216,17 @@ public abstract class CellIdentity implements Parcelable {
dest.writeString(mAlphaShort);
}
+ /** Used by phone interface manager to verify if a given string is valid MccMnc
+ * @hide
+ */
+ public static boolean isValidPlmn(@NonNull String plmn) {
+ if (plmn.length() < MCC_LENGTH + MNC_MIN_LENGTH
+ || plmn.length() > MCC_LENGTH + MNC_MAX_LENGTH) {
+ return false;
+ }
+ return (isMcc(plmn.substring(0, MCC_LENGTH)) && isMnc(plmn.substring(MCC_LENGTH)));
+ }
+
/**
* Construct from Parcel
* @hide
@@ -267,10 +287,10 @@ public abstract class CellIdentity implements Parcelable {
/** @hide */
private static boolean isMcc(@NonNull String mcc) {
// ensure no out of bounds indexing
- if (mcc.length() != 3) return false;
+ if (mcc.length() != MCC_LENGTH) return false;
// Character.isDigit allows all unicode digits, not just [0-9]
- for (int i = 0; i < 3; i++) {
+ for (int i = 0; i < MCC_LENGTH; i++) {
if (mcc.charAt(i) < '0' || mcc.charAt(i) > '9') return false;
}
@@ -280,7 +300,7 @@ public abstract class CellIdentity implements Parcelable {
/** @hide */
private static boolean isMnc(@NonNull String mnc) {
// ensure no out of bounds indexing
- if (mnc.length() < 2 || mnc.length() > 3) return false;
+ if (mnc.length() < MNC_MIN_LENGTH || mnc.length() > MNC_MAX_LENGTH) return false;
// Character.isDigit allows all unicode digits, not just [0-9]
for (int i = 0; i < mnc.length(); i++) {
@@ -289,4 +309,5 @@ public abstract class CellIdentity implements Parcelable {
return true;
}
+
}
diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index 7edc91cd67e8..18687d400faf 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -189,11 +189,15 @@ public abstract class CellInfo implements Parcelable {
mTimeStamp = ts;
}
- /** @hide */
+ /**
+ * @return a {@link CellIdentity} instance.
+ */
@NonNull
public abstract CellIdentity getCellIdentity();
- /** @hide */
+ /**
+ * @return a {@link CellSignalStrength} instance.
+ */
@NonNull
public abstract CellSignalStrength getCellSignalStrength();
diff --git a/telephony/java/android/telephony/CellInfoNr.java b/telephony/java/android/telephony/CellInfoNr.java
index 9775abd5075c..cea83230391d 100644
--- a/telephony/java/android/telephony/CellInfoNr.java
+++ b/telephony/java/android/telephony/CellInfoNr.java
@@ -19,6 +19,8 @@ package android.telephony;
import android.annotation.NonNull;
import android.os.Parcel;
+import dalvik.annotation.codegen.CovariantReturnType;
+
import java.util.Objects;
/**
@@ -46,6 +48,7 @@ public final class CellInfoNr extends CellInfo {
/**
* @return a {@link CellIdentityNr} instance.
*/
+ @CovariantReturnType(returnType = CellIdentityNr.class, presentAfter = 29)
@Override
@NonNull
public CellIdentity getCellIdentity() {
@@ -55,6 +58,7 @@ public final class CellInfoNr extends CellInfo {
/**
* @return a {@link CellSignalStrengthNr} instance.
*/
+ @CovariantReturnType(returnType = CellSignalStrengthNr.class, presentAfter = 29)
@Override
@NonNull
public CellSignalStrength getCellSignalStrength() {
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index cdf01dab0059..4dc54f0fef58 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -245,9 +245,12 @@ public final class CellSignalStrengthWcdma extends CellSignalStrength implements
}
/**
- * Get the Ec/No as dB
+ * Get the Ec/No (Energy per chip over the noise spectral density) as dB.
*
- * @hide
+ * Reference: TS 25.133 Section 9.1.2.3
+ *
+ * @return the Ec/No of the measured cell in the range [-24, 1] or
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable
*/
public int getEcNo() {
return mEcNo;
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index ca264f738e1d..246bec7de59e 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -21,6 +21,7 @@ import android.annotation.SystemApi;
import android.content.Context;
import android.os.PersistableBundle;
+import android.telephony.Annotation.DataFailureCause;
import com.android.internal.util.ArrayUtils;
import java.lang.annotation.Retention;
@@ -968,355 +969,6 @@ public final class DataFailCause {
*/
public static final int HANDOVER_FAILED = 0x10006;
- /** @hide */
- @IntDef(value = {
- NONE,
- OPERATOR_BARRED,
- NAS_SIGNALLING,
- LLC_SNDCP,
- INSUFFICIENT_RESOURCES,
- MISSING_UNKNOWN_APN,
- UNKNOWN_PDP_ADDRESS_TYPE,
- USER_AUTHENTICATION,
- ACTIVATION_REJECT_GGSN,
- ACTIVATION_REJECT_UNSPECIFIED,
- SERVICE_OPTION_NOT_SUPPORTED,
- SERVICE_OPTION_NOT_SUBSCRIBED,
- SERVICE_OPTION_OUT_OF_ORDER,
- NSAPI_IN_USE,
- REGULAR_DEACTIVATION,
- QOS_NOT_ACCEPTED,
- NETWORK_FAILURE,
- UMTS_REACTIVATION_REQ,
- FEATURE_NOT_SUPP,
- TFT_SEMANTIC_ERROR,
- TFT_SYTAX_ERROR,
- UNKNOWN_PDP_CONTEXT,
- FILTER_SEMANTIC_ERROR,
- FILTER_SYTAX_ERROR,
- PDP_WITHOUT_ACTIVE_TFT,
- ACTIVATION_REJECTED_BCM_VIOLATION,
- ONLY_IPV4_ALLOWED,
- ONLY_IPV6_ALLOWED,
- ONLY_SINGLE_BEARER_ALLOWED,
- ESM_INFO_NOT_RECEIVED,
- PDN_CONN_DOES_NOT_EXIST,
- MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED,
- COLLISION_WITH_NETWORK_INITIATED_REQUEST,
- ONLY_IPV4V6_ALLOWED,
- ONLY_NON_IP_ALLOWED,
- UNSUPPORTED_QCI_VALUE,
- BEARER_HANDLING_NOT_SUPPORTED,
- ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED,
- UNSUPPORTED_APN_IN_CURRENT_PLMN,
- INVALID_TRANSACTION_ID,
- MESSAGE_INCORRECT_SEMANTIC,
- INVALID_MANDATORY_INFO,
- MESSAGE_TYPE_UNSUPPORTED,
- MSG_TYPE_NONCOMPATIBLE_STATE,
- UNKNOWN_INFO_ELEMENT,
- CONDITIONAL_IE_ERROR,
- MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE,
- PROTOCOL_ERRORS,
- APN_TYPE_CONFLICT,
- INVALID_PCSCF_ADDR,
- INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN,
- EMM_ACCESS_BARRED,
- EMERGENCY_IFACE_ONLY,
- IFACE_MISMATCH,
- COMPANION_IFACE_IN_USE,
- IP_ADDRESS_MISMATCH,
- IFACE_AND_POL_FAMILY_MISMATCH,
- EMM_ACCESS_BARRED_INFINITE_RETRY,
- AUTH_FAILURE_ON_EMERGENCY_CALL,
- INVALID_DNS_ADDR,
- INVALID_PCSCF_OR_DNS_ADDRESS,
- CALL_PREEMPT_BY_EMERGENCY_APN,
- UE_INITIATED_DETACH_OR_DISCONNECT,
- MIP_FA_REASON_UNSPECIFIED,
- MIP_FA_ADMIN_PROHIBITED,
- MIP_FA_INSUFFICIENT_RESOURCES,
- MIP_FA_MOBILE_NODE_AUTHENTICATION_FAILURE,
- MIP_FA_HOME_AGENT_AUTHENTICATION_FAILURE,
- MIP_FA_REQUESTED_LIFETIME_TOO_LONG,
- MIP_FA_MALFORMED_REQUEST,
- MIP_FA_MALFORMED_REPLY,
- MIP_FA_ENCAPSULATION_UNAVAILABLE,
- MIP_FA_VJ_HEADER_COMPRESSION_UNAVAILABLE,
- MIP_FA_REVERSE_TUNNEL_UNAVAILABLE,
- MIP_FA_REVERSE_TUNNEL_IS_MANDATORY,
- MIP_FA_DELIVERY_STYLE_NOT_SUPPORTED,
- MIP_FA_MISSING_NAI,
- MIP_FA_MISSING_HOME_AGENT,
- MIP_FA_MISSING_HOME_ADDRESS,
- MIP_FA_UNKNOWN_CHALLENGE,
- MIP_FA_MISSING_CHALLENGE,
- MIP_FA_STALE_CHALLENGE,
- MIP_HA_REASON_UNSPECIFIED,
- MIP_HA_ADMIN_PROHIBITED,
- MIP_HA_INSUFFICIENT_RESOURCES,
- MIP_HA_MOBILE_NODE_AUTHENTICATION_FAILURE,
- MIP_HA_FOREIGN_AGENT_AUTHENTICATION_FAILURE,
- MIP_HA_REGISTRATION_ID_MISMATCH,
- MIP_HA_MALFORMED_REQUEST,
- MIP_HA_UNKNOWN_HOME_AGENT_ADDRESS,
- MIP_HA_REVERSE_TUNNEL_UNAVAILABLE,
- MIP_HA_REVERSE_TUNNEL_IS_MANDATORY,
- MIP_HA_ENCAPSULATION_UNAVAILABLE,
- CLOSE_IN_PROGRESS,
- NETWORK_INITIATED_TERMINATION,
- MODEM_APP_PREEMPTED,
- PDN_IPV4_CALL_DISALLOWED,
- PDN_IPV4_CALL_THROTTLED,
- PDN_IPV6_CALL_DISALLOWED,
- PDN_IPV6_CALL_THROTTLED,
- MODEM_RESTART,
- PDP_PPP_NOT_SUPPORTED,
- UNPREFERRED_RAT,
- PHYSICAL_LINK_CLOSE_IN_PROGRESS,
- APN_PENDING_HANDOVER,
- PROFILE_BEARER_INCOMPATIBLE,
- SIM_CARD_CHANGED,
- LOW_POWER_MODE_OR_POWERING_DOWN,
- APN_DISABLED,
- MAX_PPP_INACTIVITY_TIMER_EXPIRED,
- IPV6_ADDRESS_TRANSFER_FAILED,
- TRAT_SWAP_FAILED,
- EHRPD_TO_HRPD_FALLBACK,
- MIP_CONFIG_FAILURE,
- PDN_INACTIVITY_TIMER_EXPIRED,
- MAX_IPV4_CONNECTIONS,
- MAX_IPV6_CONNECTIONS,
- APN_MISMATCH,
- IP_VERSION_MISMATCH,
- DUN_CALL_DISALLOWED,
- INTERNAL_EPC_NONEPC_TRANSITION,
- INTERFACE_IN_USE,
- APN_DISALLOWED_ON_ROAMING,
- APN_PARAMETERS_CHANGED,
- NULL_APN_DISALLOWED,
- THERMAL_MITIGATION,
- DATA_SETTINGS_DISABLED,
- DATA_ROAMING_SETTINGS_DISABLED,
- DDS_SWITCHED,
- FORBIDDEN_APN_NAME,
- DDS_SWITCH_IN_PROGRESS,
- CALL_DISALLOWED_IN_ROAMING,
- NON_IP_NOT_SUPPORTED,
- PDN_NON_IP_CALL_THROTTLED,
- PDN_NON_IP_CALL_DISALLOWED,
- CDMA_LOCK,
- CDMA_INTERCEPT,
- CDMA_REORDER,
- CDMA_RELEASE_DUE_TO_SO_REJECTION,
- CDMA_INCOMING_CALL,
- CDMA_ALERT_STOP,
- CHANNEL_ACQUISITION_FAILURE,
- MAX_ACCESS_PROBE,
- CONCURRENT_SERVICE_NOT_SUPPORTED_BY_BASE_STATION,
- NO_RESPONSE_FROM_BASE_STATION,
- REJECTED_BY_BASE_STATION,
- CONCURRENT_SERVICES_INCOMPATIBLE,
- NO_CDMA_SERVICE,
- RUIM_NOT_PRESENT,
- CDMA_RETRY_ORDER,
- ACCESS_BLOCK,
- ACCESS_BLOCK_ALL,
- IS707B_MAX_ACCESS_PROBES,
- THERMAL_EMERGENCY,
- CONCURRENT_SERVICES_NOT_ALLOWED,
- INCOMING_CALL_REJECTED,
- NO_SERVICE_ON_GATEWAY,
- NO_GPRS_CONTEXT,
- ILLEGAL_MS,
- ILLEGAL_ME,
- GPRS_SERVICES_AND_NON_GPRS_SERVICES_NOT_ALLOWED,
- GPRS_SERVICES_NOT_ALLOWED,
- MS_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK,
- IMPLICITLY_DETACHED,
- PLMN_NOT_ALLOWED,
- LOCATION_AREA_NOT_ALLOWED,
- GPRS_SERVICES_NOT_ALLOWED_IN_THIS_PLMN,
- PDP_DUPLICATE,
- UE_RAT_CHANGE,
- CONGESTION,
- NO_PDP_CONTEXT_ACTIVATED,
- ACCESS_CLASS_DSAC_REJECTION,
- PDP_ACTIVATE_MAX_RETRY_FAILED,
- RADIO_ACCESS_BEARER_FAILURE,
- ESM_UNKNOWN_EPS_BEARER_CONTEXT,
- DRB_RELEASED_BY_RRC,
- CONNECTION_RELEASED,
- EMM_DETACHED,
- EMM_ATTACH_FAILED,
- EMM_ATTACH_STARTED,
- LTE_NAS_SERVICE_REQUEST_FAILED,
- DUPLICATE_BEARER_ID,
- ESM_COLLISION_SCENARIOS,
- ESM_BEARER_DEACTIVATED_TO_SYNC_WITH_NETWORK,
- ESM_NW_ACTIVATED_DED_BEARER_WITH_ID_OF_DEF_BEARER,
- ESM_BAD_OTA_MESSAGE,
- ESM_DOWNLOAD_SERVER_REJECTED_THE_CALL,
- ESM_CONTEXT_TRANSFERRED_DUE_TO_IRAT,
- DS_EXPLICIT_DEACTIVATION,
- ESM_LOCAL_CAUSE_NONE,
- LTE_THROTTLING_NOT_REQUIRED,
- ACCESS_CONTROL_LIST_CHECK_FAILURE,
- SERVICE_NOT_ALLOWED_ON_PLMN,
- EMM_T3417_EXPIRED,
- EMM_T3417_EXT_EXPIRED,
- RRC_UPLINK_DATA_TRANSMISSION_FAILURE,
- RRC_UPLINK_DELIVERY_FAILED_DUE_TO_HANDOVER,
- RRC_UPLINK_CONNECTION_RELEASE,
- RRC_UPLINK_RADIO_LINK_FAILURE,
- RRC_UPLINK_ERROR_REQUEST_FROM_NAS,
- RRC_CONNECTION_ACCESS_STRATUM_FAILURE,
- RRC_CONNECTION_ANOTHER_PROCEDURE_IN_PROGRESS,
- RRC_CONNECTION_ACCESS_BARRED,
- RRC_CONNECTION_CELL_RESELECTION,
- RRC_CONNECTION_CONFIG_FAILURE,
- RRC_CONNECTION_TIMER_EXPIRED,
- RRC_CONNECTION_LINK_FAILURE,
- RRC_CONNECTION_CELL_NOT_CAMPED,
- RRC_CONNECTION_SYSTEM_INTERVAL_FAILURE,
- RRC_CONNECTION_REJECT_BY_NETWORK,
- RRC_CONNECTION_NORMAL_RELEASE,
- RRC_CONNECTION_RADIO_LINK_FAILURE,
- RRC_CONNECTION_REESTABLISHMENT_FAILURE,
- RRC_CONNECTION_OUT_OF_SERVICE_DURING_CELL_REGISTER,
- RRC_CONNECTION_ABORT_REQUEST,
- RRC_CONNECTION_SYSTEM_INFORMATION_BLOCK_READ_ERROR,
- NETWORK_INITIATED_DETACH_WITH_AUTO_REATTACH,
- NETWORK_INITIATED_DETACH_NO_AUTO_REATTACH,
- ESM_PROCEDURE_TIME_OUT,
- INVALID_CONNECTION_ID,
- MAXIMIUM_NSAPIS_EXCEEDED,
- INVALID_PRIMARY_NSAPI,
- CANNOT_ENCODE_OTA_MESSAGE,
- RADIO_ACCESS_BEARER_SETUP_FAILURE,
- PDP_ESTABLISH_TIMEOUT_EXPIRED,
- PDP_MODIFY_TIMEOUT_EXPIRED,
- PDP_INACTIVE_TIMEOUT_EXPIRED,
- PDP_LOWERLAYER_ERROR,
- PDP_MODIFY_COLLISION,
- MAXINUM_SIZE_OF_L2_MESSAGE_EXCEEDED,
- NAS_REQUEST_REJECTED_BY_NETWORK,
- RRC_CONNECTION_INVALID_REQUEST,
- RRC_CONNECTION_TRACKING_AREA_ID_CHANGED,
- RRC_CONNECTION_RF_UNAVAILABLE,
- RRC_CONNECTION_ABORTED_DUE_TO_IRAT_CHANGE,
- RRC_CONNECTION_RELEASED_SECURITY_NOT_ACTIVE,
- RRC_CONNECTION_ABORTED_AFTER_HANDOVER,
- RRC_CONNECTION_ABORTED_AFTER_IRAT_CELL_CHANGE,
- RRC_CONNECTION_ABORTED_DURING_IRAT_CELL_CHANGE,
- IMSI_UNKNOWN_IN_HOME_SUBSCRIBER_SERVER,
- IMEI_NOT_ACCEPTED,
- EPS_SERVICES_AND_NON_EPS_SERVICES_NOT_ALLOWED,
- EPS_SERVICES_NOT_ALLOWED_IN_PLMN,
- MSC_TEMPORARILY_NOT_REACHABLE,
- CS_DOMAIN_NOT_AVAILABLE,
- ESM_FAILURE,
- MAC_FAILURE,
- SYNCHRONIZATION_FAILURE,
- UE_SECURITY_CAPABILITIES_MISMATCH,
- SECURITY_MODE_REJECTED,
- UNACCEPTABLE_NON_EPS_AUTHENTICATION,
- CS_FALLBACK_CALL_ESTABLISHMENT_NOT_ALLOWED,
- NO_EPS_BEARER_CONTEXT_ACTIVATED,
- INVALID_EMM_STATE,
- NAS_LAYER_FAILURE,
- MULTIPLE_PDP_CALL_NOT_ALLOWED,
- EMBMS_NOT_ENABLED,
- IRAT_HANDOVER_FAILED,
- EMBMS_REGULAR_DEACTIVATION,
- TEST_LOOPBACK_REGULAR_DEACTIVATION,
- LOWER_LAYER_REGISTRATION_FAILURE,
- DATA_PLAN_EXPIRED,
- UMTS_HANDOVER_TO_IWLAN,
- EVDO_CONNECTION_DENY_BY_GENERAL_OR_NETWORK_BUSY,
- EVDO_CONNECTION_DENY_BY_BILLING_OR_AUTHENTICATION_FAILURE,
- EVDO_HDR_CHANGED,
- EVDO_HDR_EXITED,
- EVDO_HDR_NO_SESSION,
- EVDO_USING_GPS_FIX_INSTEAD_OF_HDR_CALL,
- EVDO_HDR_CONNECTION_SETUP_TIMEOUT,
- FAILED_TO_ACQUIRE_COLOCATED_HDR,
- OTASP_COMMIT_IN_PROGRESS,
- NO_HYBRID_HDR_SERVICE,
- HDR_NO_LOCK_GRANTED,
- DBM_OR_SMS_IN_PROGRESS,
- HDR_FADE,
- HDR_ACCESS_FAILURE,
- UNSUPPORTED_1X_PREV,
- LOCAL_END,
- NO_SERVICE,
- FADE,
- NORMAL_RELEASE,
- ACCESS_ATTEMPT_ALREADY_IN_PROGRESS,
- REDIRECTION_OR_HANDOFF_IN_PROGRESS,
- EMERGENCY_MODE,
- PHONE_IN_USE,
- INVALID_MODE,
- INVALID_SIM_STATE,
- NO_COLLOCATED_HDR,
- UE_IS_ENTERING_POWERSAVE_MODE,
- DUAL_SWITCH,
- PPP_TIMEOUT,
- PPP_AUTH_FAILURE,
- PPP_OPTION_MISMATCH,
- PPP_PAP_FAILURE,
- PPP_CHAP_FAILURE,
- PPP_CLOSE_IN_PROGRESS,
- LIMITED_TO_IPV4,
- LIMITED_TO_IPV6,
- VSNCP_TIMEOUT,
- VSNCP_GEN_ERROR,
- VSNCP_APN_UNATHORIZED,
- VSNCP_PDN_LIMIT_EXCEEDED,
- VSNCP_NO_PDN_GATEWAY_ADDRESS,
- VSNCP_PDN_GATEWAY_UNREACHABLE,
- VSNCP_PDN_GATEWAY_REJECT,
- VSNCP_INSUFFICIENT_PARAMETERS,
- VSNCP_RESOURCE_UNAVAILABLE,
- VSNCP_ADMINISTRATIVELY_PROHIBITED,
- VSNCP_PDN_ID_IN_USE,
- VSNCP_SUBSCRIBER_LIMITATION,
- VSNCP_PDN_EXISTS_FOR_THIS_APN,
- VSNCP_RECONNECT_NOT_ALLOWED,
- IPV6_PREFIX_UNAVAILABLE,
- HANDOFF_PREFERENCE_CHANGED,
- OEM_DCFAILCAUSE_1,
- OEM_DCFAILCAUSE_2,
- OEM_DCFAILCAUSE_3,
- OEM_DCFAILCAUSE_4,
- OEM_DCFAILCAUSE_5,
- OEM_DCFAILCAUSE_6,
- OEM_DCFAILCAUSE_7,
- OEM_DCFAILCAUSE_8,
- OEM_DCFAILCAUSE_9,
- OEM_DCFAILCAUSE_10,
- OEM_DCFAILCAUSE_11,
- OEM_DCFAILCAUSE_12,
- OEM_DCFAILCAUSE_13,
- OEM_DCFAILCAUSE_14,
- OEM_DCFAILCAUSE_15,
- REGISTRATION_FAIL,
- GPRS_REGISTRATION_FAIL,
- SIGNAL_LOST,
- PREF_RADIO_TECH_CHANGED,
- RADIO_POWER_OFF,
- TETHERED_CALL_ACTIVE,
- ERROR_UNSPECIFIED,
- UNKNOWN,
- RADIO_NOT_AVAILABLE,
- UNACCEPTABLE_NETWORK_PARAMETER,
- CONNECTION_TO_DATACONNECTIONAC_BROKEN,
- LOST_CONNECTION,
- RESET_BY_FRAMEWORK
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface FailCause{}
-
private static final Map<Integer, String> sFailCauseMap;
static {
sFailCauseMap = new HashMap<>();
@@ -1737,7 +1389,8 @@ public final class DataFailCause {
*
* @hide
*/
- public static boolean isRadioRestartFailure(@NonNull Context context, @FailCause int cause,
+ public static boolean isRadioRestartFailure(@NonNull Context context,
+ @DataFailureCause int cause,
int subId) {
CarrierConfigManager configManager = (CarrierConfigManager)
context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
@@ -1765,7 +1418,8 @@ public final class DataFailCause {
}
/** @hide */
- public static boolean isPermanentFailure(@NonNull Context context, @FailCause int failCause,
+ public static boolean isPermanentFailure(@NonNull Context context,
+ @DataFailureCause int failCause,
int subId) {
synchronized (sPermanentFailureCache) {
@@ -1825,7 +1479,7 @@ public final class DataFailCause {
}
/** @hide */
- public static boolean isEventLoggable(@FailCause int dataFailCause) {
+ public static boolean isEventLoggable(@DataFailureCause int dataFailCause) {
return (dataFailCause == OPERATOR_BARRED) || (dataFailCause == INSUFFICIENT_RESOURCES)
|| (dataFailCause == UNKNOWN_PDP_ADDRESS_TYPE)
|| (dataFailCause == USER_AUTHENTICATION)
@@ -1845,13 +1499,13 @@ public final class DataFailCause {
}
/** @hide */
- public static String toString(@FailCause int dataFailCause) {
+ public static String toString(@DataFailureCause int dataFailCause) {
int cause = getFailCause(dataFailCause);
return (cause == UNKNOWN) ? "UNKNOWN(" + dataFailCause + ")" : sFailCauseMap.get(cause);
}
/** @hide */
- public static int getFailCause(@FailCause int failCause) {
+ public static int getFailCause(@DataFailureCause int failCause) {
if (sFailCauseMap.containsKey(failCause)) {
return failCause;
} else {
diff --git a/telephony/java/android/telephony/ICellBroadcastService.aidl b/telephony/java/android/telephony/ICellBroadcastService.aidl
new file mode 100644
index 000000000000..bcd6cc546eed
--- /dev/null
+++ b/telephony/java/android/telephony/ICellBroadcastService.aidl
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+/**
+ * Service bound to by the system to allow custom handling of cell broadcast messages.
+ * <p>
+ * @see android.telephony.CellBroadcastService
+ * @hide
+ */
+interface ICellBroadcastService {
+
+ /** @see android.telephony.CellBroadcastService#onGsmCellBroadcastSms */
+ oneway void handleGsmCellBroadcastSms(int slotId, in byte[] message);
+
+ /** @see android.telephony.CellBroadcastService#onCdmaCellBroadcastSms */
+ oneway void handleCdmaCellBroadcastSms(int slotId, in byte[] bearerData, int serviceCategory);
+}
diff --git a/telephony/java/android/telephony/ICellInfoCallback.aidl b/telephony/java/android/telephony/ICellInfoCallback.aidl
index ee3c1b1be6d9..60732a3db59a 100644
--- a/telephony/java/android/telephony/ICellInfoCallback.aidl
+++ b/telephony/java/android/telephony/ICellInfoCallback.aidl
@@ -16,7 +16,6 @@
package android.telephony;
-import android.os.ParcelableException;
import android.telephony.CellInfo;
import java.util.List;
@@ -28,5 +27,5 @@ import java.util.List;
oneway interface ICellInfoCallback
{
void onCellInfo(in List<CellInfo> state);
- void onError(in int errorCode, in ParcelableException detail);
+ void onError(in int errorCode, in String exceptionName, in String message);
}
diff --git a/telephony/java/android/telephony/IFinancialSmsCallback.aidl b/telephony/java/android/telephony/IFinancialSmsCallback.aidl
deleted file mode 100644
index aa88615c15cf..000000000000
--- a/telephony/java/android/telephony/IFinancialSmsCallback.aidl
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
-** 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.
-*/
-package android.telephony;
-
-import android.app.PendingIntent;
-import android.database.CursorWindow;
-import android.net.Uri;
-import android.os.Bundle;
-import com.android.internal.telephony.SmsRawData;
-
-/** Interface for returning back the financial sms messages asynchrously.
- * @hide
- */
-interface IFinancialSmsCallback {
- /**
- * Return sms messages back to calling financial app.
- *
- * @param messages the sms messages returned for cinancial app.
- */
- oneway void onGetSmsMessagesForFinancialApp(in CursorWindow messages);
-}
diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
index da4da79a39df..45deea206cfc 100644
--- a/telephony/java/android/telephony/MbmsDownloadSession.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -243,6 +243,7 @@ public class MbmsDownloadSession implements AutoCloseable {
};
private AtomicReference<IMbmsDownloadService> mService = new AtomicReference<>(null);
+ private ServiceConnection mServiceConnection;
private final InternalDownloadSessionCallback mInternalCallback;
private final Map<DownloadStatusListener, InternalDownloadStatusListener>
mInternalDownloadStatusListeners = new HashMap<>();
@@ -318,56 +319,66 @@ public class MbmsDownloadSession implements AutoCloseable {
}
private int bindAndInitialize() {
- return MbmsUtils.startBinding(mContext, MBMS_DOWNLOAD_SERVICE_ACTION,
- new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- IMbmsDownloadService downloadService =
- IMbmsDownloadService.Stub.asInterface(service);
- int result;
- try {
- result = downloadService.initialize(mSubscriptionId, mInternalCallback);
- } catch (RemoteException e) {
- Log.e(LOG_TAG, "Service died before initialization");
- sIsInitialized.set(false);
- return;
- } catch (RuntimeException e) {
- Log.e(LOG_TAG, "Runtime exception during initialization");
- sendErrorToApp(
- MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
- e.toString());
- sIsInitialized.set(false);
- return;
- }
- if (result == MbmsErrors.UNKNOWN) {
- // Unbind and throw an obvious error
- close();
- throw new IllegalStateException("Middleware must not return an"
- + " unknown error code");
- }
- if (result != MbmsErrors.SUCCESS) {
- sendErrorToApp(result, "Error returned during initialization");
- sIsInitialized.set(false);
- return;
- }
- try {
- downloadService.asBinder().linkToDeath(mDeathRecipient, 0);
- } catch (RemoteException e) {
- sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST,
- "Middleware lost during initialization");
- sIsInitialized.set(false);
- return;
- }
- mService.set(downloadService);
- }
+ mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ IMbmsDownloadService downloadService =
+ IMbmsDownloadService.Stub.asInterface(service);
+ int result;
+ try {
+ result = downloadService.initialize(mSubscriptionId, mInternalCallback);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Service died before initialization");
+ sIsInitialized.set(false);
+ return;
+ } catch (RuntimeException e) {
+ Log.e(LOG_TAG, "Runtime exception during initialization");
+ sendErrorToApp(
+ MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+ e.toString());
+ sIsInitialized.set(false);
+ return;
+ }
+ if (result == MbmsErrors.UNKNOWN) {
+ // Unbind and throw an obvious error
+ close();
+ throw new IllegalStateException("Middleware must not return an"
+ + " unknown error code");
+ }
+ if (result != MbmsErrors.SUCCESS) {
+ sendErrorToApp(result, "Error returned during initialization");
+ sIsInitialized.set(false);
+ return;
+ }
+ try {
+ downloadService.asBinder().linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+ "Middleware lost during initialization");
+ sIsInitialized.set(false);
+ return;
+ }
+ mService.set(downloadService);
+ }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- Log.w(LOG_TAG, "bindAndInitialize: Remote service disconnected");
- sIsInitialized.set(false);
- mService.set(null);
- }
- });
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.w(LOG_TAG, "bindAndInitialize: Remote service disconnected");
+ sIsInitialized.set(false);
+ mService.set(null);
+ }
+
+ @Override
+ public void onNullBinding(ComponentName name) {
+ Log.w(LOG_TAG, "bindAndInitialize: Remote service returned null");
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+ "Middleware service binding returned null");
+ sIsInitialized.set(false);
+ mService.set(null);
+ mContext.unbindService(this);
+ }
+ };
+ return MbmsUtils.startBinding(mContext, MBMS_DOWNLOAD_SERVICE_ACTION, mServiceConnection);
}
/**
@@ -965,17 +976,19 @@ public class MbmsDownloadSession implements AutoCloseable {
public void close() {
try {
IMbmsDownloadService downloadService = mService.get();
- if (downloadService == null) {
+ if (downloadService == null || mServiceConnection == null) {
Log.i(LOG_TAG, "Service already dead");
return;
}
downloadService.dispose(mSubscriptionId);
+ mContext.unbindService(mServiceConnection);
} catch (RemoteException e) {
// Ignore
Log.i(LOG_TAG, "Remote exception while disposing of service");
} finally {
mService.set(null);
sIsInitialized.set(false);
+ mServiceConnection = null;
mInternalCallback.stop();
}
}
diff --git a/telephony/java/android/telephony/MbmsGroupCallSession.java b/telephony/java/android/telephony/MbmsGroupCallSession.java
index f1be31fa5477..d54071f28be9 100644
--- a/telephony/java/android/telephony/MbmsGroupCallSession.java
+++ b/telephony/java/android/telephony/MbmsGroupCallSession.java
@@ -80,6 +80,7 @@ public class MbmsGroupCallSession implements AutoCloseable {
};
private InternalGroupCallSessionCallback mInternalCallback;
+ private ServiceConnection mServiceConnection;
private Set<GroupCall> mKnownActiveGroupCalls = new ArraySet<>();
private final Context mContext;
@@ -163,7 +164,7 @@ public class MbmsGroupCallSession implements AutoCloseable {
public void close() {
try {
IMbmsGroupCallService groupCallService = mService.get();
- if (groupCallService == null) {
+ if (groupCallService == null || mServiceConnection == null) {
// Ignore and return, assume already disposed.
return;
}
@@ -172,11 +173,13 @@ public class MbmsGroupCallSession implements AutoCloseable {
s.getCallback().stop();
}
mKnownActiveGroupCalls.clear();
+ mContext.unbindService(mServiceConnection);
} catch (RemoteException e) {
// Ignore for now
} finally {
mService.set(null);
sIsInitialized.set(false);
+ mServiceConnection = null;
mInternalCallback.stop();
}
}
@@ -244,59 +247,69 @@ public class MbmsGroupCallSession implements AutoCloseable {
}
private int bindAndInitialize() {
- return MbmsUtils.startBinding(mContext, MBMS_GROUP_CALL_SERVICE_ACTION,
- new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- IMbmsGroupCallService groupCallService =
- IMbmsGroupCallService.Stub.asInterface(service);
- int result;
- try {
- result = groupCallService.initialize(mInternalCallback,
- mSubscriptionId);
- } catch (RemoteException e) {
- Log.e(LOG_TAG, "Service died before initialization");
- mInternalCallback.onError(
- MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
- e.toString());
- sIsInitialized.set(false);
- return;
- } catch (RuntimeException e) {
- Log.e(LOG_TAG, "Runtime exception during initialization");
- mInternalCallback.onError(
- MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
- e.toString());
- sIsInitialized.set(false);
- return;
- }
- if (result == MbmsErrors.UNKNOWN) {
- // Unbind and throw an obvious error
- close();
- throw new IllegalStateException("Middleware must not return"
- + " an unknown error code");
- }
- if (result != MbmsErrors.SUCCESS) {
- mInternalCallback.onError(result,
- "Error returned during initialization");
- sIsInitialized.set(false);
- return;
- }
- try {
- groupCallService.asBinder().linkToDeath(mDeathRecipient, 0);
- } catch (RemoteException e) {
- mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST,
- "Middleware lost during initialization");
- sIsInitialized.set(false);
- return;
- }
- mService.set(groupCallService);
- }
+ mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ IMbmsGroupCallService groupCallService =
+ IMbmsGroupCallService.Stub.asInterface(service);
+ int result;
+ try {
+ result = groupCallService.initialize(mInternalCallback,
+ mSubscriptionId);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Service died before initialization");
+ mInternalCallback.onError(
+ MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+ e.toString());
+ sIsInitialized.set(false);
+ return;
+ } catch (RuntimeException e) {
+ Log.e(LOG_TAG, "Runtime exception during initialization");
+ mInternalCallback.onError(
+ MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+ e.toString());
+ sIsInitialized.set(false);
+ return;
+ }
+ if (result == MbmsErrors.UNKNOWN) {
+ // Unbind and throw an obvious error
+ close();
+ throw new IllegalStateException("Middleware must not return"
+ + " an unknown error code");
+ }
+ if (result != MbmsErrors.SUCCESS) {
+ mInternalCallback.onError(result,
+ "Error returned during initialization");
+ sIsInitialized.set(false);
+ return;
+ }
+ try {
+ groupCallService.asBinder().linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+ "Middleware lost during initialization");
+ sIsInitialized.set(false);
+ return;
+ }
+ mService.set(groupCallService);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ sIsInitialized.set(false);
+ mService.set(null);
+ }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- sIsInitialized.set(false);
- mService.set(null);
- }
- });
+ @Override
+ public void onNullBinding(ComponentName name) {
+ Log.w(LOG_TAG, "bindAndInitialize: Remote service returned null");
+ mInternalCallback.onError(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+ "Middleware service binding returned null");
+ sIsInitialized.set(false);
+ mService.set(null);
+ mContext.unbindService(this);
+ }
+ };
+ return MbmsUtils.startBinding(mContext, MBMS_GROUP_CALL_SERVICE_ACTION, mServiceConnection);
}
}
diff --git a/telephony/java/android/telephony/MbmsStreamingSession.java b/telephony/java/android/telephony/MbmsStreamingSession.java
index cd465d22d331..3fbbc03f0c67 100644
--- a/telephony/java/android/telephony/MbmsStreamingSession.java
+++ b/telephony/java/android/telephony/MbmsStreamingSession.java
@@ -82,6 +82,7 @@ public class MbmsStreamingSession implements AutoCloseable {
};
private InternalStreamingSessionCallback mInternalCallback;
+ private ServiceConnection mServiceConnection;
private Set<StreamingService> mKnownActiveStreamingServices = new ArraySet<>();
private final Context mContext;
@@ -168,7 +169,7 @@ public class MbmsStreamingSession implements AutoCloseable {
public void close() {
try {
IMbmsStreamingService streamingService = mService.get();
- if (streamingService == null) {
+ if (streamingService == null || mServiceConnection == null) {
// Ignore and return, assume already disposed.
return;
}
@@ -177,11 +178,13 @@ public class MbmsStreamingSession implements AutoCloseable {
s.getCallback().stop();
}
mKnownActiveStreamingServices.clear();
+ mContext.unbindService(mServiceConnection);
} catch (RemoteException e) {
// Ignore for now
} finally {
mService.set(null);
sIsInitialized.set(false);
+ mServiceConnection = null;
mInternalCallback.stop();
}
}
@@ -286,59 +289,69 @@ public class MbmsStreamingSession implements AutoCloseable {
}
private int bindAndInitialize() {
- return MbmsUtils.startBinding(mContext, MBMS_STREAMING_SERVICE_ACTION,
- new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- IMbmsStreamingService streamingService =
- IMbmsStreamingService.Stub.asInterface(service);
- int result;
- try {
- result = streamingService.initialize(mInternalCallback,
- mSubscriptionId);
- } catch (RemoteException e) {
- Log.e(LOG_TAG, "Service died before initialization");
- sendErrorToApp(
- MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
- e.toString());
- sIsInitialized.set(false);
- return;
- } catch (RuntimeException e) {
- Log.e(LOG_TAG, "Runtime exception during initialization");
- sendErrorToApp(
- MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
- e.toString());
- sIsInitialized.set(false);
- return;
- }
- if (result == MbmsErrors.UNKNOWN) {
- // Unbind and throw an obvious error
- close();
- throw new IllegalStateException("Middleware must not return"
- + " an unknown error code");
- }
- if (result != MbmsErrors.SUCCESS) {
- sendErrorToApp(result, "Error returned during initialization");
- sIsInitialized.set(false);
- return;
- }
- try {
- streamingService.asBinder().linkToDeath(mDeathRecipient, 0);
- } catch (RemoteException e) {
- sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST,
- "Middleware lost during initialization");
- sIsInitialized.set(false);
- return;
- }
- mService.set(streamingService);
- }
+ mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ IMbmsStreamingService streamingService =
+ IMbmsStreamingService.Stub.asInterface(service);
+ int result;
+ try {
+ result = streamingService.initialize(mInternalCallback,
+ mSubscriptionId);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Service died before initialization");
+ sendErrorToApp(
+ MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+ e.toString());
+ sIsInitialized.set(false);
+ return;
+ } catch (RuntimeException e) {
+ Log.e(LOG_TAG, "Runtime exception during initialization");
+ sendErrorToApp(
+ MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+ e.toString());
+ sIsInitialized.set(false);
+ return;
+ }
+ if (result == MbmsErrors.UNKNOWN) {
+ // Unbind and throw an obvious error
+ close();
+ throw new IllegalStateException("Middleware must not return"
+ + " an unknown error code");
+ }
+ if (result != MbmsErrors.SUCCESS) {
+ sendErrorToApp(result, "Error returned during initialization");
+ sIsInitialized.set(false);
+ return;
+ }
+ try {
+ streamingService.asBinder().linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+ "Middleware lost during initialization");
+ sIsInitialized.set(false);
+ return;
+ }
+ mService.set(streamingService);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ sIsInitialized.set(false);
+ mService.set(null);
+ }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- sIsInitialized.set(false);
- mService.set(null);
- }
- });
+ @Override
+ public void onNullBinding(ComponentName name) {
+ Log.w(LOG_TAG, "bindAndInitialize: Remote service returned null");
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+ "Middleware service binding returned null");
+ sIsInitialized.set(false);
+ mService.set(null);
+ mContext.unbindService(this);
+ }
+ };
+ return MbmsUtils.startBinding(mContext, MBMS_STREAMING_SERVICE_ACTION, mServiceConnection);
}
private void sendErrorToApp(int errorCode, String message) {
diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java
index 43bc85c9f2bd..d105fe3ddc71 100644
--- a/telephony/java/android/telephony/ModemActivityInfo.java
+++ b/telephony/java/android/telephony/ModemActivityInfo.java
@@ -20,11 +20,10 @@ import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
-
import android.os.SystemClock;
import android.util.Range;
+
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
/**
@@ -64,17 +63,20 @@ public final class ModemActivityInfo implements Parcelable {
mTimestamp = timestamp;
mSleepTimeMs = sleepTimeMs;
mIdleTimeMs = idleTimeMs;
- if (txTimeMs != null) {
- populateTransmitPowerRange(txTimeMs);
- }
+ populateTransmitPowerRange(txTimeMs);
mRxTimeMs = rxTimeMs;
}
/** helper API to populate tx power range for each bucket **/
private void populateTransmitPowerRange(@NonNull int[] transmitPowerMs) {
- for (int i = 0; i < Math.min(transmitPowerMs.length, TX_POWER_LEVELS); i++) {
+ int i = 0;
+ for ( ; i < Math.min(transmitPowerMs.length, TX_POWER_LEVELS); i++) {
mTransmitPowerInfo.add(i, new TransmitPower(TX_POWER_RANGES[i], transmitPowerMs[i]));
}
+ // Make sure that mTransmitPowerInfo is fully initialized.
+ for ( ; i < TX_POWER_LEVELS; i++) {
+ mTransmitPowerInfo.add(i, new TransmitPower(TX_POWER_RANGES[i], 0));
+ }
}
@Override
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index a76b8da09064..56b723631115 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -24,7 +24,7 @@ import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.AccessNetworkConstants.TransportType;
-import android.telephony.TelephonyManager.NetworkType;
+import android.telephony.Annotation.NetworkType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -90,7 +90,7 @@ public final class NetworkRegistrationInfo implements Parcelable {
* Dual Connectivity(EN-DC).
* @hide
*/
- public static final int NR_STATE_NONE = -1;
+ public static final int NR_STATE_NONE = 0;
/**
* The device is camped on an LTE cell that supports E-UTRA-NR Dual Connectivity(EN-DC) but
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
deleted file mode 100644
index 1ffed2543ee4..000000000000
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ /dev/null
@@ -1,1268 +0,0 @@
-/*
- * 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.telephony;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerExecutor;
-import android.os.Looper;
-import android.telephony.emergency.EmergencyNumber;
-import android.telephony.ims.ImsReasonInfo;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.IPhoneStateListener;
-
-import dalvik.system.VMRuntime;
-
-import java.lang.ref.WeakReference;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Executor;
-
-/**
- * A listener class for monitoring changes in specific telephony states
- * on the device, including service state, signal strength, message
- * waiting indicator (voicemail), and others.
- * <p>
- * Override the methods for the state that you wish to receive updates for, and
- * pass your PhoneStateListener object, along with bitwise-or of the LISTEN_
- * flags to {@link TelephonyManager#listen TelephonyManager.listen()}. Methods are
- * called when the state changes, as well as once on initial registration.
- * <p>
- * Note that access to some telephony information is
- * permission-protected. Your application won't receive updates for protected
- * information unless it has the appropriate permissions declared in
- * its manifest file. Where permissions apply, they are noted in the
- * appropriate LISTEN_ flags.
- */
-public class PhoneStateListener {
- private static final String LOG_TAG = "PhoneStateListener";
- private static final boolean DBG = false; // STOPSHIP if true
-
- /**
- * Stop listening for updates.
- *
- * The PhoneStateListener is not tied to any subscription and unregistered for any update.
- */
- public static final int LISTEN_NONE = 0;
-
- /**
- * Listen for changes to the network service state (cellular).
- *
- * @see #onServiceStateChanged
- * @see ServiceState
- */
- public static final int LISTEN_SERVICE_STATE = 0x00000001;
-
- /**
- * Listen for changes to the network signal strength (cellular).
- * {@more}
- *
- * @see #onSignalStrengthChanged
- *
- * @deprecated by {@link #LISTEN_SIGNAL_STRENGTHS}
- */
- @Deprecated
- public static final int LISTEN_SIGNAL_STRENGTH = 0x00000002;
-
- /**
- * Listen for changes to the message-waiting indicator.
- * {@more}
- * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
- * READ_PHONE_STATE} or that the calling app has carrier privileges (see
- * {@link TelephonyManager#hasCarrierPrivileges}).
- * <p>
- * Example: The status bar uses this to determine when to display the
- * voicemail icon.
- *
- * @see #onMessageWaitingIndicatorChanged
- */
- public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 0x00000004;
-
- /**
- * Listen for changes to the call-forwarding indicator.
- * {@more}
- * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
- * READ_PHONE_STATE} or that the calling app has carrier privileges (see
- * {@link TelephonyManager#hasCarrierPrivileges}).
- *
- * @see #onCallForwardingIndicatorChanged
- */
- public static final int LISTEN_CALL_FORWARDING_INDICATOR = 0x00000008;
-
- /**
- * Listen for changes to the device's cell location. Note that
- * this will result in frequent callbacks to the listener.
- * {@more}
- * Requires Permission: {@link android.Manifest.permission#ACCESS_COARSE_LOCATION
- * ACCESS_COARSE_LOCATION}
- * <p>
- * If you need regular location updates but want more control over
- * the update interval or location precision, you can set up a listener
- * through the {@link android.location.LocationManager location manager}
- * instead.
- *
- * @see #onCellLocationChanged
- */
- public static final int LISTEN_CELL_LOCATION = 0x00000010;
-
- /**
- * Listen for changes to the device call state.
- * {@more}
- *
- * @see #onCallStateChanged
- */
- public static final int LISTEN_CALL_STATE = 0x00000020;
-
- /**
- * Listen for changes to the data connection state (cellular).
- *
- * @see #onDataConnectionStateChanged
- */
- public static final int LISTEN_DATA_CONNECTION_STATE = 0x00000040;
-
- /**
- * Listen for changes to the direction of data traffic on the data
- * connection (cellular).
- * {@more}
- * Example: The status bar uses this to display the appropriate
- * data-traffic icon.
- *
- * @see #onDataActivity
- */
- public static final int LISTEN_DATA_ACTIVITY = 0x00000080;
-
- /**
- * Listen for changes to the network signal strengths (cellular).
- * <p>
- * Example: The status bar uses this to control the signal-strength
- * icon.
- *
- * @see #onSignalStrengthsChanged
- */
- public static final int LISTEN_SIGNAL_STRENGTHS = 0x00000100;
-
- /**
- * Listen for changes to OTASP mode.
- *
- * @see #onOtaspChanged
- * @hide
- */
- public static final int LISTEN_OTASP_CHANGED = 0x00000200;
-
- /**
- * Listen for changes to observed cell info.
- *
- * @see #onCellInfoChanged
- */
- public static final int LISTEN_CELL_INFO = 0x00000400;
-
- /**
- * Listen for {@link PreciseCallState.State} of ringing, background and foreground calls.
- *
- * @hide
- */
- @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
- @SystemApi
- public static final int LISTEN_PRECISE_CALL_STATE = 0x00000800;
-
- /**
- * Listen for {@link PreciseDataConnectionState} on the data connection (cellular).
- *
- * @see #onPreciseDataConnectionStateChanged
- *
- * @hide
- */
- @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
- @SystemApi
- public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 0x00001000;
-
- /**
- * Listen for real time info for all data connections (cellular)).
- * {@more}
- * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
- * READ_PRECISE_PHONE_STATE}
- * @see #onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo)
- *
- * @deprecated Use {@link TelephonyManager#getModemActivityInfo()}
- * @hide
- */
- @Deprecated
- public static final int LISTEN_DATA_CONNECTION_REAL_TIME_INFO = 0x00002000;
-
- /**
- * Listen for changes to the SRVCC state of the active call.
- * @see #onServiceStateChanged(ServiceState)
- * @hide
- */
- @SystemApi
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public static final int LISTEN_SRVCC_STATE_CHANGED = 0x00004000;
-
- /**
- * Listen for OEM hook raw event
- *
- * @see #onOemHookRawEvent
- * @hide
- * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
- */
- @Deprecated
- public static final int LISTEN_OEM_HOOK_RAW_EVENT = 0x00008000;
-
- /**
- * Listen for carrier network changes indicated by a carrier app.
- *
- * @see #onCarrierNetworkRequest
- * @see TelephonyManager#notifyCarrierNetworkChange(boolean)
- * @hide
- */
- public static final int LISTEN_CARRIER_NETWORK_CHANGE = 0x00010000;
-
- /**
- * Listen for changes to the sim voice activation state
- * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
- * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
- * {@more}
- * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been
- * fully activated
- *
- * @see #onVoiceActivationStateChanged
- * @hide
- */
- @SystemApi
- public static final int LISTEN_VOICE_ACTIVATION_STATE = 0x00020000;
-
- /**
- * Listen for changes to the sim data activation state
- * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
- * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
- * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
- * {@more}
- * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been
- * fully activated
- *
- * @see #onDataActivationStateChanged
- * @hide
- */
- public static final int LISTEN_DATA_ACTIVATION_STATE = 0x00040000;
-
- /**
- * Listen for changes to the user mobile data state
- *
- * @see #onUserMobileDataStateChanged
- */
- public static final int LISTEN_USER_MOBILE_DATA_STATE = 0x00080000;
-
- /**
- * Listen for changes to the physical channel configuration.
- *
- * @see #onPhysicalChannelConfigurationChanged
- * @hide
- */
- public static final int LISTEN_PHYSICAL_CHANNEL_CONFIGURATION = 0x00100000;
-
- /**
- * Listen for changes to the phone capability.
- *
- * @see #onPhoneCapabilityChanged
- * @hide
- */
- public static final int LISTEN_PHONE_CAPABILITY_CHANGE = 0x00200000;
-
- /**
- * Listen for changes to active data subId. Active data subscription is
- * the current subscription used to setup Cellular Internet data. For example,
- * it could be the current active opportunistic subscription in use, or the
- * subscription user selected as default data subscription in DSDS mode.
- *
- * @see #onActiveDataSubscriptionIdChanged
- */
- public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 0x00400000;
-
- /**
- * Listen for changes to the radio power state.
- *
- * @see #onRadioPowerStateChanged
- * @hide
- */
- @SystemApi
- public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 0x00800000;
-
- /**
- * Listen for changes to emergency number list based on all active subscriptions.
- *
- * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
- * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
- *
- * @see #onEmergencyNumberListChanged
- */
- public static final int LISTEN_EMERGENCY_NUMBER_LIST = 0x01000000;
-
- /**
- * Listen for call disconnect causes which contains {@link DisconnectCause} and
- * {@link PreciseDisconnectCause}.
- *
- * @hide
- */
- @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
- @SystemApi
- public static final int LISTEN_CALL_DISCONNECT_CAUSES = 0x02000000;
-
- /**
- * Listen for changes to the call attributes of a currently active call.
- * {@more}
- * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
- * READ_PRECISE_PHONE_STATE}
- *
- * @see #onCallAttributesChanged
- * @hide
- */
- @SystemApi
- public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 0x04000000;
-
- /**
- * Listen for IMS call disconnect causes which contains
- * {@link android.telephony.ims.ImsReasonInfo}
- *
- * @see #onImsCallDisconnectCauseChanged(ImsReasonInfo)
- * @hide
- */
- @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
- @SystemApi
- public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 0x08000000;
-
- /**
- * Listen for the emergency number placed from an outgoing call.
- *
- * <p>Requires permission {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
- *
- * @see #onOutgoingEmergencyCall
- * @hide
- */
- @SystemApi
- @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
- public static final int LISTEN_OUTGOING_CALL_EMERGENCY_NUMBER = 0x10000000;
-
- /**
- * Listen for the emergency number placed from an outgoing SMS.
- *
- * <p>Requires permission {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
- *
- * @see #onOutgoingEmergencySms
- * @hide
- */
- @SystemApi
- @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
- public static final int LISTEN_OUTGOING_SMS_EMERGENCY_NUMBER = 0x20000000;
-
- /*
- * Subscription used to listen to the phone state changes
- * @hide
- */
- /** @hide */
- @UnsupportedAppUsage
- protected Integer mSubId;
-
- /**
- * @hide
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- @UnsupportedAppUsage
- public final IPhoneStateListener callback;
-
- /**
- * Create a PhoneStateListener for the Phone with the default subscription.
- * This class requires Looper.myLooper() not return null.
- */
- public PhoneStateListener() {
- this(null, Looper.myLooper());
- }
-
- /**
- * Create a PhoneStateListener for the Phone with the default subscription
- * using a particular non-null Looper.
- * @hide
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
- public PhoneStateListener(Looper looper) {
- this(null, looper);
- }
-
- /**
- * Create a PhoneStateListener for the Phone using the specified subscription.
- * This class requires Looper.myLooper() not return null. To supply your
- * own non-null Looper use PhoneStateListener(int subId, Looper looper) below.
- * @hide
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
- public PhoneStateListener(Integer subId) {
- this(subId, Looper.myLooper());
- if (subId != null && VMRuntime.getRuntime().getTargetSdkVersion()
- >= Build.VERSION_CODES.Q) {
- throw new IllegalArgumentException("PhoneStateListener with subId: "
- + subId + " is not supported, use default constructor");
- }
- }
- /**
- * Create a PhoneStateListener for the Phone using the specified subscription
- * and non-null Looper.
- * @hide
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
- public PhoneStateListener(Integer subId, Looper looper) {
- this(subId, new HandlerExecutor(new Handler(looper)));
- if (subId != null && VMRuntime.getRuntime().getTargetSdkVersion()
- >= Build.VERSION_CODES.Q) {
- throw new IllegalArgumentException("PhoneStateListener with subId: "
- + subId + " is not supported, use default constructor");
- }
- }
-
- /**
- * Create a PhoneStateListener for the Phone using the specified Executor
- *
- * <p>Create a PhoneStateListener with a specified Executor for handling necessary callbacks.
- * The Executor must not be null.
- *
- * @param executor a non-null Executor that will execute callbacks for the PhoneStateListener.
- */
- public PhoneStateListener(@NonNull Executor executor) {
- this(null, executor);
- }
-
- private PhoneStateListener(Integer subId, Executor e) {
- if (e == null) {
- throw new IllegalArgumentException("PhoneStateListener Executor must be non-null");
- }
- mSubId = subId;
- callback = new IPhoneStateListenerStub(this, e);
- }
-
- /**
- * Callback invoked when device service state changes on the registered subscription.
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @see ServiceState#STATE_EMERGENCY_ONLY
- * @see ServiceState#STATE_IN_SERVICE
- * @see ServiceState#STATE_OUT_OF_SERVICE
- * @see ServiceState#STATE_POWER_OFF
- */
- public void onServiceStateChanged(ServiceState serviceState) {
- // default implementation empty
- }
-
- /**
- * Callback invoked when network signal strength changes on the registered subscription.
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @see ServiceState#STATE_EMERGENCY_ONLY
- * @see ServiceState#STATE_IN_SERVICE
- * @see ServiceState#STATE_OUT_OF_SERVICE
- * @see ServiceState#STATE_POWER_OFF
- * @deprecated Use {@link #onSignalStrengthsChanged(SignalStrength)}
- */
- @Deprecated
- public void onSignalStrengthChanged(int asu) {
- // default implementation empty
- }
-
- /**
- * Callback invoked when the message-waiting indicator changes on the registered subscription.
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- */
- public void onMessageWaitingIndicatorChanged(boolean mwi) {
- // default implementation empty
- }
-
- /**
- * Callback invoked when the call-forwarding indicator changes on the registered subscription.
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- */
- public void onCallForwardingIndicatorChanged(boolean cfi) {
- // default implementation empty
- }
-
- /**
- * Callback invoked when device cell location changes on the registered subscription.
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- */
- public void onCellLocationChanged(CellLocation location) {
- // default implementation empty
- }
-
- /**
- * Callback invoked when device call state changes.
- * <p>
- * Reports the state of Telephony (mobile) calls on the device for the registered subscription.
- * <p>
- * Note: the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- * <p>
- * Note: The state returned here may differ from that returned by
- * {@link TelephonyManager#getCallState()}. Receivers of this callback should be aware that
- * calling {@link TelephonyManager#getCallState()} from within this callback may return a
- * different state than the callback reports.
- *
- * @param state call state
- * @param phoneNumber call phone number. If application does not have
- * {@link android.Manifest.permission#READ_CALL_LOG READ_CALL_LOG} permission or carrier
- * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), an empty string will be
- * passed as an argument.
- */
- public void onCallStateChanged(@TelephonyManager.CallState int state, String phoneNumber) {
- // default implementation empty
- }
-
- /**
- * Callback invoked when connection state changes on the registered subscription.
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @see TelephonyManager#DATA_DISCONNECTED
- * @see TelephonyManager#DATA_CONNECTING
- * @see TelephonyManager#DATA_CONNECTED
- * @see TelephonyManager#DATA_SUSPENDED
- */
- public void onDataConnectionStateChanged(int state) {
- // default implementation empty
- }
-
- /**
- * same as above, but with the network type. Both called.
- */
- public void onDataConnectionStateChanged(int state, int networkType) {
- }
-
- /**
- * Callback invoked when data activity state changes on the registered subscription.
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @see TelephonyManager#DATA_ACTIVITY_NONE
- * @see TelephonyManager#DATA_ACTIVITY_IN
- * @see TelephonyManager#DATA_ACTIVITY_OUT
- * @see TelephonyManager#DATA_ACTIVITY_INOUT
- * @see TelephonyManager#DATA_ACTIVITY_DORMANT
- */
- public void onDataActivity(int direction) {
- // default implementation empty
- }
-
- /**
- * Callback invoked when network signal strengths changes on the registered subscription.
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- */
- public void onSignalStrengthsChanged(SignalStrength signalStrength) {
- // default implementation empty
- }
-
-
- /**
- * The Over The Air Service Provisioning (OTASP) has changed on the registered subscription.
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * Requires the READ_PHONE_STATE permission.
- * @param otaspMode is integer <code>OTASP_UNKNOWN=1<code>
- * means the value is currently unknown and the system should wait until
- * <code>OTASP_NEEDED=2<code> or <code>OTASP_NOT_NEEDED=3<code> is received before
- * making the decision to perform OTASP or not.
- *
- * @hide
- */
- @UnsupportedAppUsage
- public void onOtaspChanged(int otaspMode) {
- // default implementation empty
- }
-
- /**
- * Callback invoked when a observed cell info has changed or new cells have been added
- * or removed on the registered subscription.
- * Note, the registration subId s from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @param cellInfo is the list of currently visible cells.
- */
- public void onCellInfoChanged(List<CellInfo> cellInfo) {
- }
-
- /**
- * Callback invoked when precise device call state changes on the registered subscription.
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- * @param callState {@link PreciseCallState}
- * @hide
- */
- @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
- @SystemApi
- public void onPreciseCallStateChanged(@NonNull PreciseCallState callState) {
- // default implementation empty
- }
-
- /**
- * Callback invoked when call disconnect cause changes on the registered subscription.
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @param disconnectCause {@link DisconnectCause}.
- * @param preciseDisconnectCause {@link PreciseDisconnectCause}.
- *
- * @hide
- */
- @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
- @SystemApi
- public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) {
- // default implementation empty
- }
-
- /**
- * Callback invoked when Ims call disconnect cause changes on the registered subscription.
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed.
- *
- * @hide
- */
- @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
- @SystemApi
- public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo) {
- // default implementation empty
- }
-
- /**
- * Callback invoked when data connection state changes with precise information
- * on the registered subscription.
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @param dataConnectionState {@link PreciseDataConnectionState}
- *
- * @hide
- */
- @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
- @SystemApi
- public void onPreciseDataConnectionStateChanged(
- @NonNull PreciseDataConnectionState dataConnectionState) {
- // default implementation empty
- }
-
- /**
- * Callback invoked when data connection real time info changes on the registered subscription.
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @hide
- */
- @UnsupportedAppUsage
- public void onDataConnectionRealTimeInfoChanged(
- DataConnectionRealTimeInfo dcRtInfo) {
- // default implementation empty
- }
-
- /**
- * Callback invoked when there has been a change in the Single Radio Voice Call Continuity
- * (SRVCC) state for the currently active call on the registered subscription.
- *
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @hide
- */
- @SystemApi
- public void onSrvccStateChanged(@TelephonyManager.SrvccState int srvccState) {
-
- }
-
- /**
- * Callback invoked when the SIM voice activation state has changed on the registered
- * subscription.
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @param state is the current SIM voice activation state
- * @hide
- */
- @SystemApi
- public void onVoiceActivationStateChanged(@TelephonyManager.SimActivationState int state) {
- }
-
- /**
- * Callback invoked when the SIM data activation state has changed on the registered
- * subscription.
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @param state is the current SIM data activation state
- * @hide
- */
- public void onDataActivationStateChanged(@TelephonyManager.SimActivationState int state) {
- }
-
- /**
- * Callback invoked when the user mobile data state has changed on the registered subscription.
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @param enabled indicates whether the current user mobile data state is enabled or disabled.
- */
- public void onUserMobileDataStateChanged(boolean enabled) {
- // default implementation empty
- }
-
- /**
- * Callback invoked when the current physical channel configuration has changed on the
- * registered subscription.
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @param configs List of the current {@link PhysicalChannelConfig}s
- * @hide
- */
- public void onPhysicalChannelConfigurationChanged(
- @NonNull List<PhysicalChannelConfig> configs) {
- // default implementation empty
- }
-
- /**
- * Callback invoked when the current emergency number list has changed on the registered
- * subscription.
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @param emergencyNumberList Map including the key as the active subscription ID
- * (Note: if there is no active subscription, the key is
- * {@link SubscriptionManager#getDefaultSubscriptionId})
- * and the value as the list of {@link EmergencyNumber};
- * null if this information is not available.
- * @hide
- */
- public void onEmergencyNumberListChanged(
- @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList) {
- // default implementation empty
- }
-
- /**
- * Callback invoked when an outgoing call is placed to an emergency number.
- *
- * @param placedEmergencyNumber the emergency number {@link EmergencyNumber} the call is placed
- * to.
- * @hide
- */
- public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber) {
- // default implementation empty
- }
-
- /**
- * Callback invoked when an outgoing SMS is placed to an emergency number.
- *
- * @param sentEmergencyNumber the emergency number {@link EmergencyNumber} the SMS is sent to.
- * @hide
- */
- public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber) {
- // default implementation empty
- }
-
- /**
- * Callback invoked when OEM hook raw event is received on the registered subscription.
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * Requires the READ_PRIVILEGED_PHONE_STATE permission.
- * @param rawData is the byte array of the OEM hook raw data.
- * @hide
- */
- @UnsupportedAppUsage
- public void onOemHookRawEvent(byte[] rawData) {
- // default implementation empty
- }
-
- /**
- * Callback invoked when phone capability changes.
- * Note, this callback triggers regardless of registered subscription.
- *
- * Requires the READ_PRIVILEGED_PHONE_STATE permission.
- * @param capability the new phone capability
- * @hide
- */
- public void onPhoneCapabilityChanged(PhoneCapability capability) {
- // default implementation empty
- }
-
- /**
- * Callback invoked when active data subId changes.
- * Note, this callback triggers regardless of registered subscription.
- *
- * Requires the READ_PHONE_STATE permission.
- * @param subId current subscription used to setup Cellular Internet data.
- * For example, it could be the current active opportunistic subscription in use,
- * or the subscription user selected as default data subscription in DSDS mode.
- */
- public void onActiveDataSubscriptionIdChanged(int subId) {
- // default implementation empty
- }
-
- /**
- * Callback invoked when the call attributes changes on the registered subscription.
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * Requires the READ_PRIVILEGED_PHONE_STATE permission.
- * @param callAttributes the call attributes
- * @hide
- */
- @SystemApi
- public void onCallAttributesChanged(@NonNull CallAttributes callAttributes) {
- // default implementation empty
- }
-
- /**
- * Callback invoked when modem radio power state changes on the registered subscription.
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * Requires
- * the READ_PRIVILEGED_PHONE_STATE permission.
- * @param state the modem radio power state
- * @hide
- */
- @SystemApi
- public void onRadioPowerStateChanged(@TelephonyManager.RadioPowerState int state) {
- // default implementation empty
- }
-
- /**
- * Callback invoked when telephony has received notice from a carrier
- * app that a network action that could result in connectivity loss
- * has been requested by an app using
- * {@link android.telephony.TelephonyManager#notifyCarrierNetworkChange(boolean)}
- *
- * Note, this callback is pinned to the registered subscription and will be invoked when
- * the notifying carrier app has carrier privilege rule on the registered
- * subscription. {@link android.telephony.TelephonyManager#hasCarrierPrivileges}
- *
- * @param active Whether the carrier network change is or shortly
- * will be active. This value is true to indicate
- * showing alternative UI and false to stop.
- *
- * @hide
- */
- public void onCarrierNetworkChange(boolean active) {
- // default implementation empty
- }
-
- /**
- * The callback methods need to be called on the handler thread where
- * this object was created. If the binder did that for us it'd be nice.
- *
- * Using a static class and weak reference here to avoid memory leak caused by the
- * IPhoneStateListener.Stub callback retaining references to the outside PhoneStateListeners:
- * even caller has been destroyed and "un-registered" the PhoneStateListener, it is still not
- * eligible for GC given the references coming from:
- * Native Stack --> PhoneStateListener --> Context (Activity).
- * memory of caller's context will be collected after GC from service side get triggered
- */
- private static class IPhoneStateListenerStub extends IPhoneStateListener.Stub {
- private WeakReference<PhoneStateListener> mPhoneStateListenerWeakRef;
- private Executor mExecutor;
-
- IPhoneStateListenerStub(PhoneStateListener phoneStateListener, Executor executor) {
- mPhoneStateListenerWeakRef = new WeakReference<PhoneStateListener>(phoneStateListener);
- mExecutor = executor;
- }
-
- public void onServiceStateChanged(ServiceState serviceState) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(() -> psl.onServiceStateChanged(serviceState)));
- }
-
- public void onSignalStrengthChanged(int asu) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(() -> psl.onSignalStrengthChanged(asu)));
- }
-
- public void onMessageWaitingIndicatorChanged(boolean mwi) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(() -> psl.onMessageWaitingIndicatorChanged(mwi)));
- }
-
- public void onCallForwardingIndicatorChanged(boolean cfi) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(() -> psl.onCallForwardingIndicatorChanged(cfi)));
- }
-
- public void onCellLocationChanged(Bundle bundle) {
- CellLocation location = CellLocation.newFromBundle(bundle);
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(() -> psl.onCellLocationChanged(location)));
- }
-
- public void onCallStateChanged(int state, String incomingNumber) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(() -> psl.onCallStateChanged(state, incomingNumber)));
- }
-
- public void onDataConnectionStateChanged(int state, int networkType) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(() -> mExecutor.execute(
- () -> {
- psl.onDataConnectionStateChanged(state, networkType);
- psl.onDataConnectionStateChanged(state);
- }));
- }
-
- public void onDataActivity(int direction) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(() -> psl.onDataActivity(direction)));
- }
-
- public void onSignalStrengthsChanged(SignalStrength signalStrength) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(() -> psl.onSignalStrengthsChanged(signalStrength)));
- }
-
- public void onOtaspChanged(int otaspMode) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(() -> psl.onOtaspChanged(otaspMode)));
- }
-
- public void onCellInfoChanged(List<CellInfo> cellInfo) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(() -> psl.onCellInfoChanged(cellInfo)));
- }
-
- public void onPreciseCallStateChanged(PreciseCallState callState) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(() -> psl.onPreciseCallStateChanged(callState)));
- }
-
- public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(() -> psl.onCallDisconnectCauseChanged(
- disconnectCause, preciseDisconnectCause)));
- }
-
- public void onPreciseDataConnectionStateChanged(
- PreciseDataConnectionState dataConnectionState) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(
- () -> psl.onPreciseDataConnectionStateChanged(dataConnectionState)));
- }
-
- public void onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo dcRtInfo) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(
- () -> psl.onDataConnectionRealTimeInfoChanged(dcRtInfo)));
- }
-
- public void onSrvccStateChanged(int state) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(() -> psl.onSrvccStateChanged(state)));
- }
-
- public void onVoiceActivationStateChanged(int activationState) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(
- () -> psl.onVoiceActivationStateChanged(activationState)));
- }
-
- public void onDataActivationStateChanged(int activationState) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(
- () -> psl.onDataActivationStateChanged(activationState)));
- }
-
- public void onUserMobileDataStateChanged(boolean enabled) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(
- () -> psl.onUserMobileDataStateChanged(enabled)));
- }
-
- public void onOemHookRawEvent(byte[] rawData) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(() -> psl.onOemHookRawEvent(rawData)));
- }
-
- public void onCarrierNetworkChange(boolean active) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(() -> psl.onCarrierNetworkChange(active)));
- }
-
- public void onPhysicalChannelConfigurationChanged(List<PhysicalChannelConfig> configs) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(
- () -> psl.onPhysicalChannelConfigurationChanged(configs)));
- }
-
- public void onEmergencyNumberListChanged(Map emergencyNumberList) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(
- () -> psl.onEmergencyNumberListChanged(emergencyNumberList)));
- }
-
- public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(
- () -> psl.onOutgoingEmergencyCall(placedEmergencyNumber)));
- }
-
- public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(
- () -> psl.onOutgoingEmergencySms(sentEmergencyNumber)));
- }
-
- public void onPhoneCapabilityChanged(PhoneCapability capability) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(() -> psl.onPhoneCapabilityChanged(capability)));
- }
-
- public void onRadioPowerStateChanged(@TelephonyManager.RadioPowerState int state) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(() -> psl.onRadioPowerStateChanged(state)));
- }
-
- public void onCallAttributesChanged(CallAttributes callAttributes) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(() -> psl.onCallAttributesChanged(callAttributes)));
- }
-
- public void onActiveDataSubIdChanged(int subId) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(() -> psl.onActiveDataSubscriptionIdChanged(subId)));
- }
-
- public void onImsCallDisconnectCauseChanged(ImsReasonInfo disconnectCause) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(
- () -> psl.onImsCallDisconnectCauseChanged(disconnectCause)));
-
- }
- }
-
-
- private void log(String s) {
- Rlog.d(LOG_TAG, s);
- }
-}
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index e1763ab44fba..4273f5a4a16e 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -19,8 +19,8 @@ package android.telephony;
import android.annotation.IntDef;
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.TelephonyManager.NetworkType;
+import android.telephony.Annotation.NetworkType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
diff --git a/telephony/java/android/telephony/PreciseCallState.java b/telephony/java/android/telephony/PreciseCallState.java
index 701a375a3039..9f75332c4a03 100644
--- a/telephony/java/android/telephony/PreciseCallState.java
+++ b/telephony/java/android/telephony/PreciseCallState.java
@@ -23,6 +23,7 @@ import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.Annotation.PreciseCallStates;
import android.telephony.DisconnectCause;
import android.telephony.PreciseDisconnectCause;
@@ -41,29 +42,13 @@ import java.util.Objects;
* <li>Precise background call state.
* </ul>
*
- * @see android.telephony.TelephonyManager.CallState which contains generic call states.
+ * @see android.telephony.Annotation.CallState which contains generic call states.
*
* @hide
*/
@SystemApi
public final class PreciseCallState implements Parcelable {
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"PRECISE_CALL_STATE_"},
- value = {
- PRECISE_CALL_STATE_NOT_VALID,
- PRECISE_CALL_STATE_IDLE,
- PRECISE_CALL_STATE_ACTIVE,
- PRECISE_CALL_STATE_HOLDING,
- PRECISE_CALL_STATE_DIALING,
- PRECISE_CALL_STATE_ALERTING,
- PRECISE_CALL_STATE_INCOMING,
- PRECISE_CALL_STATE_WAITING,
- PRECISE_CALL_STATE_DISCONNECTED,
- PRECISE_CALL_STATE_DISCONNECTING})
- public @interface State {}
-
/** Call state is not valid (Not received a call state). */
public static final int PRECISE_CALL_STATE_NOT_VALID = -1;
/** Call state: No activity. */
@@ -85,9 +70,9 @@ public final class PreciseCallState implements Parcelable {
/** Call state: Disconnecting. */
public static final int PRECISE_CALL_STATE_DISCONNECTING = 8;
- private @State int mRingingCallState = PRECISE_CALL_STATE_NOT_VALID;
- private @State int mForegroundCallState = PRECISE_CALL_STATE_NOT_VALID;
- private @State int mBackgroundCallState = PRECISE_CALL_STATE_NOT_VALID;
+ private @PreciseCallStates int mRingingCallState = PRECISE_CALL_STATE_NOT_VALID;
+ private @PreciseCallStates int mForegroundCallState = PRECISE_CALL_STATE_NOT_VALID;
+ private @PreciseCallStates int mBackgroundCallState = PRECISE_CALL_STATE_NOT_VALID;
private int mDisconnectCause = DisconnectCause.NOT_VALID;
private int mPreciseDisconnectCause = PreciseDisconnectCause.NOT_VALID;
@@ -97,8 +82,9 @@ public final class PreciseCallState implements Parcelable {
* @hide
*/
@UnsupportedAppUsage
- public PreciseCallState(@State int ringingCall, @State int foregroundCall,
- @State int backgroundCall, int disconnectCause,
+ public PreciseCallState(@PreciseCallStates int ringingCall,
+ @PreciseCallStates int foregroundCall,
+ @PreciseCallStates int backgroundCall, int disconnectCause,
int preciseDisconnectCause) {
mRingingCallState = ringingCall;
mForegroundCallState = foregroundCall;
@@ -131,21 +117,21 @@ public final class PreciseCallState implements Parcelable {
/**
* Returns the precise ringing call state.
*/
- public @State int getRingingCallState() {
+ public @PreciseCallStates int getRingingCallState() {
return mRingingCallState;
}
/**
* Returns the precise foreground call state.
*/
- public @State int getForegroundCallState() {
+ public @PreciseCallStates int getForegroundCallState() {
return mForegroundCallState;
}
/**
* Returns the precise background call state.
*/
- public @State int getBackgroundCallState() {
+ public @PreciseCallStates int getBackgroundCallState() {
return mBackgroundCallState;
}
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index 90d443a6d8ee..257d634f1577 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -23,6 +23,10 @@ import android.annotation.UnsupportedAppUsage;
import android.net.LinkProperties;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.Annotation.ApnType;
+import android.telephony.Annotation.DataFailureCause;
+import android.telephony.Annotation.DataState;
+import android.telephony.Annotation.NetworkType;
import android.telephony.data.ApnSetting;
import java.util.Objects;
@@ -47,10 +51,10 @@ import java.util.Objects;
@SystemApi
public final class PreciseDataConnectionState implements Parcelable {
- private @TelephonyManager.DataState int mState = TelephonyManager.DATA_UNKNOWN;
- private @TelephonyManager.NetworkType int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
- private @DataFailCause.FailCause int mFailCause = DataFailCause.NONE;
- private @ApnSetting.ApnType int mAPNTypes = ApnSetting.TYPE_NONE;
+ 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;
@@ -60,11 +64,11 @@ public final class PreciseDataConnectionState implements Parcelable {
* @hide
*/
@UnsupportedAppUsage
- public PreciseDataConnectionState(@TelephonyManager.DataState int state,
- @TelephonyManager.NetworkType int networkType,
- @ApnSetting.ApnType int apnTypes, String apn,
+ public PreciseDataConnectionState(@DataState int state,
+ @NetworkType int networkType,
+ @ApnType int apnTypes, String apn,
LinkProperties linkProperties,
- @DataFailCause.FailCause int failCause) {
+ @DataFailureCause int failCause) {
mState = state;
mNetworkType = networkType;
mAPNTypes = apnTypes;
@@ -99,7 +103,7 @@ public final class PreciseDataConnectionState implements Parcelable {
* Returns the state of data connection that supported the apn types returned by
* {@link #getDataConnectionApnTypeBitMask()}
*/
- public @TelephonyManager.DataState int getDataConnectionState() {
+ public @DataState int getDataConnectionState() {
return mState;
}
@@ -107,7 +111,7 @@ public final class PreciseDataConnectionState implements Parcelable {
* Returns the network type associated with this data connection.
* @hide
*/
- public @TelephonyManager.NetworkType int getDataConnectionNetworkType() {
+ public @NetworkType int getDataConnectionNetworkType() {
return mNetworkType;
}
@@ -115,7 +119,7 @@ public final class PreciseDataConnectionState implements Parcelable {
* Returns the data connection APN types supported by this connection and triggers
* {@link PreciseDataConnectionState} change.
*/
- public @ApnSetting.ApnType int getDataConnectionApnTypeBitMask() {
+ public @ApnType int getDataConnectionApnTypeBitMask() {
return mAPNTypes;
}
@@ -139,7 +143,7 @@ public final class PreciseDataConnectionState implements Parcelable {
/**
* Returns data connection fail cause, in case there was a failure.
*/
- public @DataFailCause.FailCause int getDataConnectionFailCause() {
+ public @Annotation.DataFailureCause int getDataConnectionFailCause() {
return mFailCause;
}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 811db06d5e37..91c917ceae45 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -29,6 +29,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.Annotation.RilRadioTechnology;
import android.telephony.NetworkRegistrationInfo.Domain;
import android.telephony.NetworkRegistrationInfo.NRState;
import android.text.TextUtils;
@@ -100,7 +102,7 @@ public class ServiceState implements Parcelable {
* Indicates frequency range is unknown.
* @hide
*/
- public static final int FREQUENCY_RANGE_UNKNOWN = -1;
+ public static final int FREQUENCY_RANGE_UNKNOWN = 0;
/**
* Indicates the frequency range is below 1GHz.
@@ -154,32 +156,6 @@ public class ServiceState implements Parcelable {
*/
public static final int DUPLEX_MODE_TDD = 2;
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "RIL_RADIO_TECHNOLOGY_" },
- value = {
- RIL_RADIO_TECHNOLOGY_UNKNOWN,
- RIL_RADIO_TECHNOLOGY_GPRS,
- RIL_RADIO_TECHNOLOGY_EDGE,
- RIL_RADIO_TECHNOLOGY_UMTS,
- RIL_RADIO_TECHNOLOGY_IS95A,
- RIL_RADIO_TECHNOLOGY_IS95B,
- RIL_RADIO_TECHNOLOGY_1xRTT,
- RIL_RADIO_TECHNOLOGY_EVDO_0,
- RIL_RADIO_TECHNOLOGY_EVDO_A,
- RIL_RADIO_TECHNOLOGY_HSDPA,
- RIL_RADIO_TECHNOLOGY_HSUPA,
- RIL_RADIO_TECHNOLOGY_HSPA,
- RIL_RADIO_TECHNOLOGY_EVDO_B,
- RIL_RADIO_TECHNOLOGY_EHRPD,
- RIL_RADIO_TECHNOLOGY_LTE,
- RIL_RADIO_TECHNOLOGY_HSPAP,
- RIL_RADIO_TECHNOLOGY_GSM,
- RIL_RADIO_TECHNOLOGY_TD_SCDMA,
- RIL_RADIO_TECHNOLOGY_IWLAN,
- RIL_RADIO_TECHNOLOGY_LTE_CA,
- RIL_RADIO_TECHNOLOGY_NR})
- public @interface RilRadioTechnology {}
/**
* Available radio technologies for GSM, UMTS and CDMA.
* Duplicates the constants from hardware/radio/include/ril.h
@@ -988,7 +964,7 @@ public class ServiceState implements Parcelable {
rtString = "LTE_CA";
break;
case RIL_RADIO_TECHNOLOGY_NR:
- rtString = "NR";
+ rtString = "LTE_NR";
break;
default:
rtString = "Unexpected";
@@ -1623,7 +1599,7 @@ public class ServiceState implements Parcelable {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
- public @TelephonyManager.NetworkType int getDataNetworkType() {
+ public @NetworkType int getDataNetworkType() {
final NetworkRegistrationInfo iwlanRegInfo = getNetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
final NetworkRegistrationInfo wwanRegInfo = getNetworkRegistrationInfo(
@@ -1650,7 +1626,7 @@ public class ServiceState implements Parcelable {
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
- public @TelephonyManager.NetworkType int getVoiceNetworkType() {
+ public @NetworkType int getVoiceNetworkType() {
final NetworkRegistrationInfo regState = getNetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
if (regState != null) {
diff --git a/telephony/java/android/telephony/SmsCbMessage.java b/telephony/java/android/telephony/SmsCbMessage.java
index c078764cfa24..dc991b9a3ea7 100644
--- a/telephony/java/android/telephony/SmsCbMessage.java
+++ b/telephony/java/android/telephony/SmsCbMessage.java
@@ -207,17 +207,19 @@ public final class SmsCbMessage implements Parcelable {
/** CMAS warning area coordinates. */
private final List<Geometry> mGeometries;
+ private int mSlotIndex = 0;
+
/**
* Create a new SmsCbMessage with the specified data.
*/
public SmsCbMessage(int messageFormat, int geographicalScope, int serialNumber,
@NonNull SmsCbLocation location, int serviceCategory, @Nullable String language,
@Nullable String body, int priority, @Nullable SmsCbEtwsInfo etwsWarningInfo,
- @Nullable SmsCbCmasInfo cmasWarningInfo) {
+ @Nullable SmsCbCmasInfo cmasWarningInfo, int slotIndex) {
this(messageFormat, geographicalScope, serialNumber, location, serviceCategory, language,
body, priority, etwsWarningInfo, cmasWarningInfo, 0 /* maximumWaitingTime */,
- null /* geometries */, System.currentTimeMillis());
+ null /* geometries */, System.currentTimeMillis(), slotIndex);
}
/**
@@ -227,7 +229,8 @@ public final class SmsCbMessage implements Parcelable {
public SmsCbMessage(int messageFormat, int geographicalScope, int serialNumber,
SmsCbLocation location, int serviceCategory, String language, String body,
int priority, SmsCbEtwsInfo etwsWarningInfo, SmsCbCmasInfo cmasWarningInfo,
- int maximumWaitTimeSec, List<Geometry> geometries, long receivedTimeMillis) {
+ int maximumWaitTimeSec, List<Geometry> geometries, long receivedTimeMillis,
+ int slotIndex) {
mMessageFormat = messageFormat;
mGeographicalScope = geographicalScope;
mSerialNumber = serialNumber;
@@ -241,6 +244,7 @@ public final class SmsCbMessage implements Parcelable {
mReceivedTimeMillis = receivedTimeMillis;
mGeometries = geometries;
mMaximumWaitTimeSec = maximumWaitTimeSec;
+ mSlotIndex = slotIndex;
}
/**
@@ -278,6 +282,7 @@ public final class SmsCbMessage implements Parcelable {
String geoStr = in.readString();
mGeometries = geoStr != null ? CbGeoUtils.parseGeometriesFromString(geoStr) : null;
mMaximumWaitTimeSec = in.readInt();
+ mSlotIndex = in.readInt();
}
/**
@@ -312,6 +317,7 @@ public final class SmsCbMessage implements Parcelable {
dest.writeString(
mGeometries != null ? CbGeoUtils.encodeGeometriesToString(mGeometries) : null);
dest.writeInt(mMaximumWaitTimeSec);
+ dest.writeInt(mSlotIndex);
}
@NonNull
@@ -423,6 +429,14 @@ public final class SmsCbMessage implements Parcelable {
}
/**
+ * Get the slotIndex associated with this message.
+ * @return the slotIndex associated with this message
+ */
+ public int getSlotIndex() {
+ return mSlotIndex;
+ }
+
+ /**
* Get the message format ({@link #MESSAGE_FORMAT_3GPP} or {@link #MESSAGE_FORMAT_3GPP2}).
* @return an integer representing 3GPP or 3GPP2 message format
*/
@@ -502,6 +516,7 @@ public final class SmsCbMessage implements Parcelable {
+ (mEtwsWarningInfo != null ? (", " + mEtwsWarningInfo.toString()) : "")
+ (mCmasWarningInfo != null ? (", " + mCmasWarningInfo.toString()) : "")
+ ", maximumWaitingTime = " + mMaximumWaitTimeSec
+ + ", slotIndex = " + mSlotIndex
+ ", geo=" + (mGeometries != null
? CbGeoUtils.encodeGeometriesToString(mGeometries) : "null")
+ '}';
@@ -522,6 +537,7 @@ public final class SmsCbMessage implements Parcelable {
@NonNull
public ContentValues getContentValues() {
ContentValues cv = new ContentValues(16);
+ cv.put(CellBroadcasts.SLOT_INDEX, mSlotIndex);
cv.put(CellBroadcasts.GEOGRAPHICAL_SCOPE, mGeographicalScope);
if (mLocation.getPlmn() != null) {
cv.put(CellBroadcasts.PLMN, mLocation.getPlmn());
@@ -563,6 +579,7 @@ public final class SmsCbMessage implements Parcelable {
}
cv.put(CellBroadcasts.MAXIMUM_WAIT_TIME, mMaximumWaitTimeSec);
+ cv.put(CellBroadcasts.SLOT_INDEX, mSlotIndex);
return cv;
}
@@ -584,6 +601,7 @@ public final class SmsCbMessage implements Parcelable {
String body = cursor.getString(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_BODY));
int format = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_FORMAT));
int priority = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_PRIORITY));
+ int slotIndex = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SLOT_INDEX));
String plmn;
int plmnColumn = cursor.getColumnIndex(CellBroadcasts.PLMN);
@@ -681,7 +699,7 @@ public final class SmsCbMessage implements Parcelable {
return new SmsCbMessage(format, geoScope, serialNum, location, category,
language, body, priority, etwsInfo, cmasInfo, maximumWaitTimeSec, geometries,
- receivedTimeMillis);
+ receivedTimeMillis, slotIndex);
}
/**
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 678d32ef47ce..ae6831b3fdba 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -27,14 +27,13 @@ import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
import android.app.PendingIntent;
-import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.CursorWindow;
import android.net.Uri;
-import android.os.BaseBundle;
import android.os.Binder;
+import android.os.BaseBundle;
import android.os.Build;
import android.os.Bundle;
import android.os.RemoteException;
@@ -45,7 +44,6 @@ import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.telephony.IIntegerConsumer;
-import com.android.internal.telephony.IMms;
import com.android.internal.telephony.ISms;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.SmsRawData;
@@ -358,6 +356,68 @@ public final class SmsManager {
true /* persistMessage*/, ActivityThread.currentPackageName());
}
+ /**
+ * Send a text based SMS with messaging options.
+ *
+ * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+ * manager on a multi-SIM device, this operation may fail sending the SMS message because no
+ * suitable default subscription could be found. In this case, if {@code sentIntent} is
+ * non-null, then the {@link PendingIntent} will be sent with an error code
+ * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
+ * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
+ * where this operation may fail.
+ * </p>
+ *
+ * @param destinationAddress the address to send the message to
+ * @param scAddress is the service center address or null to use
+ * the current default SMSC
+ * @param text the body of the message to send
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is successfully sent, or failed.
+ * The result code will be <code>Activity.RESULT_OK</code> for success,
+ * or one of these errors:<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.<br>
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ * @param priority Priority level of the message
+ * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
+ * ---------------------------------
+ * PRIORITY | Level of Priority
+ * ---------------------------------
+ * '00' | Normal
+ * '01' | Interactive
+ * '10' | Urgent
+ * '11' | Emergency
+ * ----------------------------------
+ * Any Other values included Negative considered as Invalid Priority Indicator of the message.
+ * @param expectMore is a boolean to indicate the sending messages through same link or not.
+ * @param validityPeriod Validity Period of the message in mins.
+ * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
+ * Validity Period(Minimum) -> 5 mins
+ * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
+ * Any Other values included Negative considered as Invalid Validity Period of the message.
+ *
+ * @throws IllegalArgumentException if destinationAddress or text are empty
+ * {@hide}
+ */
+ @UnsupportedAppUsage
+ public void sendTextMessage(
+ String destinationAddress, String scAddress, String text,
+ PendingIntent sentIntent, PendingIntent deliveryIntent,
+ int priority, boolean expectMore, int validityPeriod) {
+ sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
+ true /* persistMessage*/, priority, expectMore, validityPeriod);
+ }
+
private void sendTextMessageInternal(String destinationAddress, String scAddress,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
boolean persistMessage, String packageName) {
@@ -456,110 +516,6 @@ public final class SmsManager {
false /* persistMessage */, ActivityThread.currentPackageName());
}
- /**
- * A variant of {@link SmsManager#sendTextMessage} that allows self to be the caller. This is
- * for internal use only.
- *
- * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
- * applications or the Telephony framework and will never trigger an SMS disambiguation
- * dialog. If this method is called on a device that has multiple active subscriptions, this
- * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
- * default subscription is defined, the subscription ID associated with this message will be
- * INVALID, which will result in the SMS being sent on the subscription associated with logical
- * slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the
- * correct subscription.
- * </p>
- *
- * @param persistMessage whether to persist the sent message in the SMS app. the caller must be
- * the Phone process if set to false.
- *
- * @hide
- */
- public void sendTextMessageWithSelfPermissions(
- String destinationAddress, String scAddress, String text,
- PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessage) {
- android.util.SeempLog.record_str(75, destinationAddress);
- if (TextUtils.isEmpty(destinationAddress)) {
- throw new IllegalArgumentException("Invalid destinationAddress");
- }
-
- if (TextUtils.isEmpty(text)) {
- throw new IllegalArgumentException("Invalid message body");
- }
-
- try {
- ISms iSms = getISmsServiceOrThrow();
- iSms.sendTextForSubscriberWithSelfPermissions(getSubscriptionId(),
- ActivityThread.currentPackageName(),
- destinationAddress,
- scAddress, text, sentIntent, deliveryIntent, persistMessage);
- } catch (RemoteException ex) {
- notifySmsGenericError(sentIntent);
- }
- }
-
- /**
- * Send a text based SMS with messaging options.
- *
- * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
- * manager on a multi-SIM device, this operation may fail sending the SMS message because no
- * suitable default subscription could be found. In this case, if {@code sentIntent} is
- * non-null, then the {@link PendingIntent} will be sent with an error code
- * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
- * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
- * where this operation may fail.
- * </p>
- *
- * @param destinationAddress the address to send the message to
- * @param scAddress is the service center address or null to use
- * the current default SMSC
- * @param text the body of the message to send
- * @param sentIntent if not NULL this <code>PendingIntent</code> is
- * broadcast when the message is successfully sent, or failed.
- * The result code will be <code>Activity.RESULT_OK</code> for success,
- * or one of these errors:<br>
- * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
- * <code>RESULT_ERROR_RADIO_OFF</code><br>
- * <code>RESULT_ERROR_NULL_PDU</code><br>
- * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
- * the extra "errorCode" containing a radio technology specific value,
- * generally only useful for troubleshooting.<br>
- * The per-application based SMS control checks sentIntent. If sentIntent
- * is NULL the caller will be checked against all unknown applications,
- * which cause smaller number of SMS to be sent in checking period.
- * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
- * broadcast when the message is delivered to the recipient. The
- * raw pdu of the status report is in the extended data ("pdu").
- * @param priority Priority level of the message
- * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
- * ---------------------------------
- * PRIORITY | Level of Priority
- * ---------------------------------
- * '00' | Normal
- * '01' | Interactive
- * '10' | Urgent
- * '11' | Emergency
- * ----------------------------------
- * Any Other values included Negative considered as Invalid Priority Indicator of the message.
- * @param expectMore is a boolean to indicate the sending messages through same link or not.
- * @param validityPeriod Validity Period of the message in mins.
- * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
- * Validity Period(Minimum) -> 5 mins
- * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
- * Any Other values included Negative considered as Invalid Validity Period of the message.
- *
- * @throws IllegalArgumentException if destinationAddress or text are empty
- * {@hide}
- */
- @UnsupportedAppUsage
- public void sendTextMessage(
- String destinationAddress, String scAddress, String text,
- PendingIntent sentIntent, PendingIntent deliveryIntent,
- int priority, boolean expectMore, int validityPeriod) {
- sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
- true /* persistMessage*/, priority, expectMore, validityPeriod);
- }
-
private void sendTextMessageInternal(
String destinationAddress, String scAddress, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessage,
@@ -1080,37 +1036,6 @@ public final class SmsManager {
}
/**
- * Send a multi-part text based SMS without writing it into the SMS Provider.
- *
- * <p>Requires Permission:
- * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier
- * privileges.
- * </p>
- *
- * <p class="note"><strong>Note:</strong> This method is intended for internal use the Telephony
- * framework and will never trigger an SMS disambiguation dialog. If this method is called on a
- * device that has multiple active subscriptions, this {@link SmsManager} instance has been
- * created with {@link #getDefault()}, and no user-defined default subscription is defined, the
- * subscription ID associated with this message will be INVALID, which will result in the SMS
- * being sent on the subscription associated with logical slot 0. Use
- * {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the correct
- * subscription.
- * </p>
- *
- * @see #sendMultipartTextMessage(String, String, ArrayList, ArrayList,
- * ArrayList, int, boolean, int)
- * @hide
- **/
- public void sendMultipartTextMessageWithoutPersisting(
- String destinationAddress, String scAddress, List<String> parts,
- List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents,
- int priority, boolean expectMore, int validityPeriod) {
- sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents,
- deliveryIntents, false /* persistMessage*/, priority, expectMore,
- validityPeriod);
- }
-
- /**
* Send a data based SMS to a specific application port.
*
* <p class="note"><strong>Note:</strong> Using this method requires that your app has the
@@ -1183,46 +1108,6 @@ public final class SmsManager {
}
/**
- * A variant of {@link SmsManager#sendDataMessage} that allows self to be the caller. This is
- * for internal use only.
- *
- * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
- * applications or the Telephony framework and will never trigger an SMS disambiguation
- * dialog. If this method is called on a device that has multiple active subscriptions, this
- * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
- * default subscription is defined, the subscription ID associated with this message will be
- * INVALID, which will result in the SMS being sent on the subscription associated with logical
- * slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the
- * correct subscription.
- * </p>
- *
- * @hide
- */
- public void sendDataMessageWithSelfPermissions(
- String destinationAddress, String scAddress, short destinationPort,
- byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
- android.util.SeempLog.record_str(73, destinationAddress);
- if (TextUtils.isEmpty(destinationAddress)) {
- throw new IllegalArgumentException("Invalid destinationAddress");
- }
-
- if (data == null || data.length == 0) {
- throw new IllegalArgumentException("Invalid message data");
- }
-
- try {
- ISms iSms = getISmsServiceOrThrow();
- iSms.sendDataForSubscriberWithSelfPermissions(getSubscriptionId(),
- ActivityThread.currentPackageName(), destinationAddress, scAddress,
- destinationPort & 0xFFFF, data, sentIntent, deliveryIntent);
- } catch (RemoteException e) {
- Log.e(TAG, "sendDataMessageWithSelfPermissions: Couldn't send SMS - Exception: "
- + e.getMessage());
- notifySmsGenericError(sentIntent);
- }
- }
-
- /**
* Get the SmsManager associated with the default subscription id. The instance will always be
* associated with the default subscription id, even if the default subscription id changes.
*
@@ -1657,100 +1542,6 @@ public final class SmsManager {
/**
* Enable reception of cell broadcast (SMS-CB) messages with the given
- * message identifier and RAN type. The RAN type specify this message ID
- * belong to 3GPP (GSM) or 3GPP2(CDMA).Note that if two different clients
- * enable the same message identifier, they must both disable it for the device to stop
- * receiving those messages. All received messages will be broadcast in an
- * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
- * Note: This call is blocking, callers may want to avoid calling it from
- * the main thread of an application.
- *
- * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
- * applications or the Telephony framework and will never trigger an SMS disambiguation
- * dialog. If this method is called on a device that has multiple active subscriptions, this
- * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
- * default subscription is defined, the subscription ID associated with this message will be
- * INVALID, which will result in the operation being completed on the subscription associated
- * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
- * operation is performed on the correct subscription.
- * </p>
- *
- * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP)
- * or C.R1001-G (3GPP2)
- * @param ranType the message format as defined in {@link SmsCbMessage]
- * @return true if successful, false otherwise
- * @see #disableCellBroadcast(int, int)
- *
- * {@hide}
- */
- public boolean enableCellBroadcast(int messageIdentifier,
- @android.telephony.SmsCbMessage.MessageFormat int ranType) {
- boolean success = false;
-
- try {
- ISms iSms = getISmsService();
- if (iSms != null) {
- // If getSubscriptionId() returns INVALID or an inactive subscription, we will use
- // the default phone internally.
- success = iSms.enableCellBroadcastForSubscriber(getSubscriptionId(),
- messageIdentifier, ranType);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
-
- return success;
- }
-
- /**
- * Disable reception of cell broadcast (SMS-CB) messages with the given
- * message identifier and RAN type. The RAN type specify this message ID
- * belong to 3GPP (GSM) or 3GPP2(CDMA). Note that if two different clients
- * enable the same message identifier, they must both disable it for the
- * device to stop receiving those messages.
- * Note: This call is blocking, callers may want to avoid calling it from
- * the main thread of an application.
- *
- * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
- * applications or the Telephony framework and will never trigger an SMS disambiguation
- * dialog. If this method is called on a device that has multiple active subscriptions, this
- * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
- * default subscription is defined, the subscription ID associated with this message will be
- * INVALID, which will result in the operation being completed on the subscription associated
- * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
- * operation is performed on the correct subscription.
- * </p>
- *
- * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP)
- * or C.R1001-G (3GPP2)
- * @param ranType the message format as defined in {@link SmsCbMessage}
- * @return true if successful, false otherwise
- *
- * @see #enableCellBroadcast(int, int)
- *
- * {@hide}
- */
- public boolean disableCellBroadcast(int messageIdentifier,
- @android.telephony.SmsCbMessage.MessageFormat int ranType) {
- boolean success = false;
-
- try {
- ISms iSms = getISmsService();
- if (iSms != null) {
- // If getSubscriptionId() returns INVALID or an inactive subscription, we will use
- // the default phone internally.
- success = iSms.disableCellBroadcastForSubscriber(getSubscriptionId(),
- messageIdentifier, ranType);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
-
- return success;
- }
-
- /**
- * Enable reception of cell broadcast (SMS-CB) messages with the given
* message identifier range and RAN type. The RAN type specifies if this message ID
* belongs to 3GPP (GSM) or 3GPP2(CDMA). Note that if two different clients enable
* the same message identifier, they must both disable it for the device to stop
@@ -2047,6 +1838,36 @@ public final class SmsManager {
// SMS send failure result codes
+ /** @hide */
+ @IntDef(prefix = { "RESULT" }, value = {
+ RESULT_ERROR_NONE,
+ RESULT_ERROR_GENERIC_FAILURE,
+ RESULT_ERROR_RADIO_OFF,
+ RESULT_ERROR_NULL_PDU,
+ RESULT_ERROR_NO_SERVICE,
+ RESULT_ERROR_LIMIT_EXCEEDED,
+ RESULT_ERROR_FDN_CHECK_FAILURE,
+ RESULT_ERROR_SHORT_CODE_NOT_ALLOWED,
+ RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED,
+ RESULT_RADIO_NOT_AVAILABLE,
+ RESULT_NETWORK_REJECT,
+ RESULT_INVALID_ARGUMENTS,
+ RESULT_INVALID_STATE,
+ RESULT_NO_MEMORY,
+ RESULT_INVALID_SMS_FORMAT,
+ RESULT_SYSTEM_ERROR,
+ RESULT_MODEM_ERROR,
+ RESULT_NETWORK_ERROR,
+ RESULT_INVALID_SMSC_ADDRESS,
+ RESULT_OPERATION_NOT_ALLOWED,
+ RESULT_INTERNAL_ERROR,
+ RESULT_NO_RESOURCES,
+ RESULT_CANCELLED,
+ RESULT_REQUEST_NOT_SUPPORTED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Result {}
+
/**
* No error.
* @hide
@@ -2170,9 +1991,6 @@ public final class SmsManager {
@SystemApi
static public final int RESULT_REQUEST_NOT_SUPPORTED = 24;
-
- static private final String PHONE_PACKAGE_NAME = "com.android.phone";
-
/**
* Send an MMS message
*
@@ -2199,17 +2017,8 @@ public final class SmsManager {
if (contentUri == null) {
throw new IllegalArgumentException("Uri contentUri null");
}
- try {
- final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms == null) {
- return;
- }
-
- iMms.sendMessage(getSubscriptionId(), ActivityThread.currentPackageName(), contentUri,
+ MmsManager.getInstance().sendMultimediaMessage(getSubscriptionId(), contentUri,
locationUrl, configOverrides, sentIntent);
- } catch (RemoteException e) {
- // Ignore it
- }
}
/**
@@ -2242,16 +2051,8 @@ public final class SmsManager {
if (contentUri == null) {
throw new IllegalArgumentException("Uri contentUri null");
}
- try {
- final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms == null) {
- return;
- }
- iMms.downloadMessage(getSubscriptionId(), ActivityThread.currentPackageName(),
- locationUrl, contentUri, configOverrides, downloadedIntent);
- } catch (RemoteException e) {
- // Ignore it
- }
+ MmsManager.getInstance().downloadMultimediaMessage(getSubscriptionId(), locationUrl,
+ contentUri, configOverrides, downloadedIntent);
}
// MMS send/download failure result codes
@@ -2269,434 +2070,17 @@ public final class SmsManager {
/** Intent extra name for HTTP status code for MMS HTTP failure in integer type */
public static final String EXTRA_MMS_HTTP_STATUS = "android.telephony.extra.MMS_HTTP_STATUS";
- /**
- * Import a text message into system's SMS store
- *
- * Only default SMS apps can import SMS
- *
- * @param address the destination(source) address of the sent(received) message
- * @param type the type of the message
- * @param text the message text
- * @param timestampMillis the message timestamp in milliseconds
- * @param seen if the message is seen
- * @param read if the message is read
- * @return the message URI, null if failed
- * @hide
- */
- public Uri importTextMessage(String address, int type, String text, long timestampMillis,
- boolean seen, boolean read) {
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- return iMms.importTextMessage(ActivityThread.currentPackageName(),
- address, type, text, timestampMillis, seen, read);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- return null;
- }
-
/** Represents the received SMS message for importing {@hide} */
public static final int SMS_TYPE_INCOMING = 0;
/** Represents the sent SMS message for importing {@hide} */
public static final int SMS_TYPE_OUTGOING = 1;
- /**
- * Import a multimedia message into system's MMS store. Only the following PDU type is
- * supported: Retrieve.conf, Send.req, Notification.ind, Delivery.ind, Read-Orig.ind
- *
- * Only default SMS apps can import MMS
- *
- * @param contentUri the content uri from which to read the PDU of the message to import
- * @param messageId the optional message id. Use null if not specifying
- * @param timestampSecs the optional message timestamp. Use -1 if not specifying
- * @param seen if the message is seen
- * @param read if the message is read
- * @return the message URI, null if failed
- * @throws IllegalArgumentException if pdu is empty
- * {@hide}
- */
- public Uri importMultimediaMessage(Uri contentUri, String messageId, long timestampSecs,
- boolean seen, boolean read) {
- if (contentUri == null) {
- throw new IllegalArgumentException("Uri contentUri null");
- }
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- return iMms.importMultimediaMessage(ActivityThread.currentPackageName(),
- contentUri, messageId, timestampSecs, seen, read);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- return null;
- }
-
- /**
- * Delete a system stored SMS or MMS message
- *
- * Only default SMS apps can delete system stored SMS and MMS messages
- *
- * @param messageUri the URI of the stored message
- * @return true if deletion is successful, false otherwise
- * @throws IllegalArgumentException if messageUri is empty
- * {@hide}
- */
- public boolean deleteStoredMessage(Uri messageUri) {
- if (messageUri == null) {
- throw new IllegalArgumentException("Empty message URI");
- }
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- return iMms.deleteStoredMessage(ActivityThread.currentPackageName(), messageUri);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- return false;
- }
-
- /**
- * Delete a system stored SMS or MMS thread
- *
- * Only default SMS apps can delete system stored SMS and MMS conversations
- *
- * @param conversationId the ID of the message conversation
- * @return true if deletion is successful, false otherwise
- * {@hide}
- */
- public boolean deleteStoredConversation(long conversationId) {
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- return iMms.deleteStoredConversation(
- ActivityThread.currentPackageName(), conversationId);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- return false;
- }
-
- /**
- * Update the status properties of a system stored SMS or MMS message, e.g.
- * the read status of a message, etc.
- *
- * @param messageUri the URI of the stored message
- * @param statusValues a list of status properties in key-value pairs to update
- * @return true if update is successful, false otherwise
- * @throws IllegalArgumentException if messageUri is empty
- * {@hide}
- */
- public boolean updateStoredMessageStatus(Uri messageUri, ContentValues statusValues) {
- if (messageUri == null) {
- throw new IllegalArgumentException("Empty message URI");
- }
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- return iMms.updateStoredMessageStatus(ActivityThread.currentPackageName(),
- messageUri, statusValues);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- return false;
- }
-
/** Message status property: whether the message has been seen. 1 means seen, 0 not {@hide} */
public static final String MESSAGE_STATUS_SEEN = "seen";
/** Message status property: whether the message has been read. 1 means read, 0 not {@hide} */
public static final String MESSAGE_STATUS_READ = "read";
/**
- * Archive or unarchive a stored conversation
- *
- * @param conversationId the ID of the message conversation
- * @param archived true to archive the conversation, false to unarchive
- * @return true if update is successful, false otherwise
- * {@hide}
- */
- public boolean archiveStoredConversation(long conversationId, boolean archived) {
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- return iMms.archiveStoredConversation(ActivityThread.currentPackageName(),
- conversationId, archived);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- return false;
- }
-
- /**
- * Add a text message draft to system SMS store
- *
- * Only default SMS apps can add SMS draft
- *
- * @param address the destination address of message
- * @param text the body of the message to send
- * @return the URI of the stored draft message
- * {@hide}
- */
- public Uri addTextMessageDraft(String address, String text) {
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- return iMms.addTextMessageDraft(ActivityThread.currentPackageName(), address, text);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- return null;
- }
-
- /**
- * Add a multimedia message draft to system MMS store
- *
- * Only default SMS apps can add MMS draft
- *
- * @param contentUri the content uri from which to read the PDU data of the draft MMS
- * @return the URI of the stored draft message
- * @throws IllegalArgumentException if pdu is empty
- * {@hide}
- */
- public Uri addMultimediaMessageDraft(Uri contentUri) {
- if (contentUri == null) {
- throw new IllegalArgumentException("Uri contentUri null");
- }
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- return iMms.addMultimediaMessageDraft(ActivityThread.currentPackageName(),
- contentUri);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- return null;
- }
-
- /**
- * Send a system stored text message.
- *
- * You can only send a failed text message or a draft text message.
- *
- * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
- * manager on a multi-SIM device, this operation may fail sending the SMS message because no
- * suitable default subscription could be found. In this case, if {@code sentIntent} is
- * non-null, then the {@link PendingIntent} will be sent with an error code
- * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
- * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
- * where this operation may fail.
- * </p>
- *
- * @param messageUri the URI of the stored message
- * @param scAddress is the service center address or null to use the current default SMSC
- * @param sentIntent if not NULL this <code>PendingIntent</code> is
- * broadcast when the message is successfully sent, or failed.
- * The result code will be <code>Activity.RESULT_OK</code> for success,
- * or one of these errors:<br>
- * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
- * <code>RESULT_ERROR_RADIO_OFF</code><br>
- * <code>RESULT_ERROR_NULL_PDU</code><br>
- * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
- * the extra "errorCode" containing a radio technology specific value,
- * generally only useful for troubleshooting.<br>
- * The per-application based SMS control checks sentIntent. If sentIntent
- * is NULL the caller will be checked against all unknown applications,
- * which cause smaller number of SMS to be sent in checking period.
- * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
- * broadcast when the message is delivered to the recipient. The
- * raw pdu of the status report is in the extended data ("pdu").
- *
- * @throws IllegalArgumentException if messageUri is empty
- * {@hide}
- */
- public void sendStoredTextMessage(Uri messageUri, String scAddress, PendingIntent sentIntent,
- PendingIntent deliveryIntent) {
- if (messageUri == null) {
- throw new IllegalArgumentException("Empty message URI");
- }
- final Context context = ActivityThread.currentApplication().getApplicationContext();
- resolveSubscriptionForOperation(new SubscriptionResolverResult() {
- @Override
- public void onSuccess(int subId) {
- try {
- ISms iSms = getISmsServiceOrThrow();
- iSms.sendStoredText(subId, ActivityThread.currentPackageName(), messageUri,
- scAddress, sentIntent, deliveryIntent);
- } catch (RemoteException e) {
- Log.e(TAG, "sendStoredTextMessage: Couldn't send SMS - Exception: "
- + e.getMessage());
- notifySmsGenericError(sentIntent);
- }
- }
- @Override
- public void onFailure() {
- notifySmsErrorNoDefaultSet(context, sentIntent);
- }
- });
- }
-
- /**
- * Send a system stored multi-part text message.
- *
- * You can only send a failed text message or a draft text message.
- * The provided <code>PendingIntent</code> lists should match the part number of the
- * divided text of the stored message by using <code>divideMessage</code>
- *
- * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
- * manager on a multi-SIM device, this operation may fail sending the SMS message because no
- * suitable default subscription could be found. In this case, if {@code sentIntent} is
- * non-null, then the {@link PendingIntent} will be sent with an error code
- * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the
- * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions
- * where this operation may fail.
- * </p>
- *
- * @param messageUri the URI of the stored message
- * @param scAddress is the service center address or null to use
- * the current default SMSC
- * @param sentIntents if not null, an <code>ArrayList</code> of
- * <code>PendingIntent</code>s (one for each message part) that is
- * broadcast when the corresponding message part has been sent.
- * The result code will be <code>Activity.RESULT_OK</code> for success,
- * or one of these errors:<br>
- * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
- * <code>RESULT_ERROR_RADIO_OFF</code><br>
- * <code>RESULT_ERROR_NULL_PDU</code><br>
- * For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
- * the extra "errorCode" containing a radio technology specific value,
- * generally only useful for troubleshooting.<br>
- * The per-application based SMS control checks sentIntent. If sentIntent
- * is NULL the caller will be checked against all unknown applications,
- * which cause smaller number of SMS to be sent in checking period.
- * @param deliveryIntents if not null, an <code>ArrayList</code> of
- * <code>PendingIntent</code>s (one for each message part) that is
- * broadcast when the corresponding message part has been delivered
- * to the recipient. The raw pdu of the status report is in the
- * extended data ("pdu").
- *
- * @throws IllegalArgumentException if messageUri is empty
- * {@hide}
- */
- public void sendStoredMultipartTextMessage(Uri messageUri, String scAddress,
- ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
- if (messageUri == null) {
- throw new IllegalArgumentException("Empty message URI");
- }
- final Context context = ActivityThread.currentApplication().getApplicationContext();
- resolveSubscriptionForOperation(new SubscriptionResolverResult() {
- @Override
- public void onSuccess(int subId) {
- try {
- ISms iSms = getISmsServiceOrThrow();
- iSms.sendStoredMultipartText(subId, ActivityThread.currentPackageName(),
- messageUri, scAddress, sentIntents, deliveryIntents);
- } catch (RemoteException e) {
- Log.e(TAG, "sendStoredTextMessage: Couldn't send SMS - Exception: "
- + e.getMessage());
- notifySmsGenericError(sentIntents);
- }
- }
- @Override
- public void onFailure() {
- notifySmsErrorNoDefaultSet(context, sentIntents);
- }
- });
- }
-
- /**
- * Send a system stored MMS message
- *
- * This is used for sending a previously sent, but failed-to-send, message or
- * for sending a text message that has been stored as a draft.
- *
- * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
- * dialog. If this method is called on a device that has multiple active subscriptions, this
- * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
- * default subscription is defined, the subscription ID associated with this message will be
- * INVALID, which will result in the operation being completed on the subscription associated
- * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
- * operation is performed on the correct subscription.
- * </p>
- *
- * @param messageUri the URI of the stored message
- * @param configOverrides the carrier-specific messaging configuration values to override for
- * sending the message.
- * @param sentIntent if not NULL this <code>PendingIntent</code> is
- * broadcast when the message is successfully sent, or failed
- * @throws IllegalArgumentException if messageUri is empty
- * {@hide}
- */
- public void sendStoredMultimediaMessage(Uri messageUri, Bundle configOverrides,
- PendingIntent sentIntent) {
- if (messageUri == null) {
- throw new IllegalArgumentException("Empty message URI");
- }
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- iMms.sendStoredMessage(
- getSubscriptionId(), ActivityThread.currentPackageName(), messageUri,
- configOverrides, sentIntent);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- }
-
- /**
- * Turns on/off the flag to automatically write sent/received SMS/MMS messages into system
- *
- * When this flag is on, all SMS/MMS sent/received are stored by system automatically
- * When this flag is off, only SMS/MMS sent by non-default SMS apps are stored by system
- * automatically
- *
- * This flag can only be changed by default SMS apps
- *
- * @param enabled Whether to enable message auto persisting
- * {@hide}
- */
- public void setAutoPersisting(boolean enabled) {
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- iMms.setAutoPersisting(ActivityThread.currentPackageName(), enabled);
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- }
-
- /**
- * Get the value of the flag to automatically write sent/received SMS/MMS messages into system
- *
- * When this flag is on, all SMS/MMS sent/received are stored by system automatically
- * When this flag is off, only SMS/MMS sent by non-default SMS apps are stored by system
- * automatically
- *
- * @return the current value of the auto persist flag
- * {@hide}
- */
- public boolean getAutoPersisting() {
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- return iMms.getAutoPersisting();
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- return false;
- }
-
- /**
* Get carrier-dependent configuration values.
*
* <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
@@ -2712,15 +2096,7 @@ public final class SmsManager {
* @return bundle key/values pairs of configuration values
*/
public Bundle getCarrierConfigValues() {
- try {
- IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
- if (iMms != null) {
- return iMms.getCarrierConfigValues(getSubscriptionId());
- }
- } catch (RemoteException ex) {
- // ignore it
- }
- return null;
+ return MmsManager.getInstance().getCarrierConfigValues(getSubscriptionId());
}
/**
@@ -2759,7 +2135,9 @@ public final class SmsManager {
}
}
- /** callback for providing asynchronous sms messages for financial app. */
+ /**
+ * callback for providing asynchronous sms messages for financial app.
+ */
public abstract static class FinancialSmsCallback {
/**
* Callback to send sms messages back to financial app asynchronously.
@@ -2785,59 +2163,49 @@ public final class SmsManager {
* @param params the parameters to filter SMS messages returned.
* @param executor the executor on which callback will be invoked.
* @param callback a callback to receive CursorWindow with SMS messages.
+ *
*/
@RequiresPermission(android.Manifest.permission.SMS_FINANCIAL_TRANSACTIONS)
public void getSmsMessagesForFinancialApp(
Bundle params,
@NonNull @CallbackExecutor Executor executor,
@NonNull FinancialSmsCallback callback) {
- try {
- ISms iccSms = getISmsServiceOrThrow();
- iccSms.getSmsMessagesForFinancialApp(
- getSubscriptionId(), ActivityThread.currentPackageName(), params,
- new IFinancialSmsCallback.Stub() {
- public void onGetSmsMessagesForFinancialApp(CursorWindow msgs) {
- Binder.withCleanCallingIdentity(() -> executor.execute(
- () -> callback.onFinancialSmsMessages(msgs)));
- }});
- } catch (RemoteException ex) {
- ex.rethrowFromSystemServer();
- }
+ // This API is not functional and thus removed to avoid future confusion.
}
/**
- * @see #createAppSpecificSmsTokenWithPackageInfo().
+ * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
* The prefixes is a list of prefix {@code String} separated by this delimiter.
* @hide
*/
public static final String REGEX_PREFIX_DELIMITER = ",";
/**
- * @see #createAppSpecificSmsTokenWithPackageInfo().
+ * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
* The success status to be added into the intent to be sent to the calling package.
* @hide
*/
public static final int RESULT_STATUS_SUCCESS = 0;
/**
- * @see #createAppSpecificSmsTokenWithPackageInfo().
+ * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
* The timeout status to be added into the intent to be sent to the calling package.
* @hide
*/
public static final int RESULT_STATUS_TIMEOUT = 1;
/**
- * @see #createAppSpecificSmsTokenWithPackageInfo().
+ * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
* Intent extra key of the retrieved SMS message as a {@code String}.
* @hide
*/
public static final String EXTRA_SMS_MESSAGE = "android.telephony.extra.SMS_MESSAGE";
/**
- * @see #createAppSpecificSmsTokenWithPackageInfo().
+ * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
* Intent extra key of SMS retriever status, which indicates whether the request for the
* coming SMS message is SUCCESS or TIMEOUT
* @hide
*/
public static final String EXTRA_STATUS = "android.telephony.extra.STATUS";
/**
- * @see #createAppSpecificSmsTokenWithPackageInfo().
+ * @see #createAppSpecificSmsTokenWithPackageInfo(String, PendingIntent).
* [Optional] Intent extra key of the retrieved Sim card subscription Id if any. {@code int}
* @hide
*/
@@ -2887,74 +2255,6 @@ public final class SmsManager {
}
}
- /**
- * Filters a bundle to only contain MMS config variables.
- *
- * This is for use with bundles returned by {@link CarrierConfigManager} which contain MMS
- * config and unrelated config. It is assumed that all MMS_CONFIG_* keys are present in the
- * supplied bundle.
- *
- * @param config a Bundle that contains MMS config variables and possibly more.
- * @return a new Bundle that only contains the MMS_CONFIG_* keys defined above.
- * @hide
- */
- public static Bundle getMmsConfig(BaseBundle config) {
- Bundle filtered = new Bundle();
- filtered.putBoolean(MMS_CONFIG_APPEND_TRANSACTION_ID,
- config.getBoolean(MMS_CONFIG_APPEND_TRANSACTION_ID));
- filtered.putBoolean(MMS_CONFIG_MMS_ENABLED, config.getBoolean(MMS_CONFIG_MMS_ENABLED));
- filtered.putBoolean(MMS_CONFIG_GROUP_MMS_ENABLED,
- config.getBoolean(MMS_CONFIG_GROUP_MMS_ENABLED));
- filtered.putBoolean(MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED,
- config.getBoolean(MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED));
- filtered.putBoolean(MMS_CONFIG_ALIAS_ENABLED, config.getBoolean(MMS_CONFIG_ALIAS_ENABLED));
- filtered.putBoolean(MMS_CONFIG_ALLOW_ATTACH_AUDIO,
- config.getBoolean(MMS_CONFIG_ALLOW_ATTACH_AUDIO));
- filtered.putBoolean(MMS_CONFIG_MULTIPART_SMS_ENABLED,
- config.getBoolean(MMS_CONFIG_MULTIPART_SMS_ENABLED));
- filtered.putBoolean(MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED,
- config.getBoolean(MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED));
- filtered.putBoolean(MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION,
- config.getBoolean(MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION));
- filtered.putBoolean(MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES,
- config.getBoolean(MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES));
- filtered.putBoolean(MMS_CONFIG_MMS_READ_REPORT_ENABLED,
- config.getBoolean(MMS_CONFIG_MMS_READ_REPORT_ENABLED));
- filtered.putBoolean(MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED,
- config.getBoolean(MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED));
- filtered.putBoolean(MMS_CONFIG_CLOSE_CONNECTION,
- config.getBoolean(MMS_CONFIG_CLOSE_CONNECTION));
- filtered.putInt(MMS_CONFIG_MAX_MESSAGE_SIZE, config.getInt(MMS_CONFIG_MAX_MESSAGE_SIZE));
- filtered.putInt(MMS_CONFIG_MAX_IMAGE_WIDTH, config.getInt(MMS_CONFIG_MAX_IMAGE_WIDTH));
- filtered.putInt(MMS_CONFIG_MAX_IMAGE_HEIGHT, config.getInt(MMS_CONFIG_MAX_IMAGE_HEIGHT));
- filtered.putInt(MMS_CONFIG_RECIPIENT_LIMIT, config.getInt(MMS_CONFIG_RECIPIENT_LIMIT));
- filtered.putInt(MMS_CONFIG_ALIAS_MIN_CHARS, config.getInt(MMS_CONFIG_ALIAS_MIN_CHARS));
- filtered.putInt(MMS_CONFIG_ALIAS_MAX_CHARS, config.getInt(MMS_CONFIG_ALIAS_MAX_CHARS));
- filtered.putInt(MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD,
- config.getInt(MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD));
- filtered.putInt(MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD,
- config.getInt(MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD));
- filtered.putInt(MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE,
- config.getInt(MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE));
- filtered.putInt(MMS_CONFIG_SUBJECT_MAX_LENGTH,
- config.getInt(MMS_CONFIG_SUBJECT_MAX_LENGTH));
- filtered.putInt(MMS_CONFIG_HTTP_SOCKET_TIMEOUT,
- config.getInt(MMS_CONFIG_HTTP_SOCKET_TIMEOUT));
- filtered.putString(MMS_CONFIG_UA_PROF_TAG_NAME,
- config.getString(MMS_CONFIG_UA_PROF_TAG_NAME));
- filtered.putString(MMS_CONFIG_USER_AGENT, config.getString(MMS_CONFIG_USER_AGENT));
- filtered.putString(MMS_CONFIG_UA_PROF_URL, config.getString(MMS_CONFIG_UA_PROF_URL));
- filtered.putString(MMS_CONFIG_HTTP_PARAMS, config.getString(MMS_CONFIG_HTTP_PARAMS));
- filtered.putString(MMS_CONFIG_EMAIL_GATEWAY_NUMBER,
- config.getString(MMS_CONFIG_EMAIL_GATEWAY_NUMBER));
- filtered.putString(MMS_CONFIG_NAI_SUFFIX, config.getString(MMS_CONFIG_NAI_SUFFIX));
- filtered.putBoolean(MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS,
- config.getBoolean(MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS));
- filtered.putBoolean(MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER,
- config.getBoolean(MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER));
- return filtered;
- }
-
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"SMS_CATEGORY_"},
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 51de903ed37e..8425ec13b282 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -47,22 +47,20 @@ import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelUuid;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.telephony.TelephonyManager.NetworkType;
import android.telephony.euicc.EuiccManager;
import android.telephony.ims.ImsMmTelManager;
import android.util.DisplayMetrics;
import android.util.Log;
-import com.android.internal.telephony.IOnSubscriptionsChangedListener;
import com.android.internal.telephony.ISetOpportunisticDataCallback;
import com.android.internal.telephony.ISub;
-import com.android.internal.telephony.ITelephonyRegistry;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.Preconditions;
@@ -896,6 +894,11 @@ public class SubscriptionManager {
*/
public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
+ /**
+ * Integer extra to specify SIM slot index.
+ */
+ public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX";
+
private final Context mContext;
private volatile INetworkPolicyManager mNetworkPolicy;
@@ -919,20 +922,24 @@ public class SubscriptionManager {
OnSubscriptionsChangedListenerHandler(Looper looper) {
super(looper);
}
-
- @Override
- public void handleMessage(Message msg) {
- if (DBG) {
- log("handleMessage: invoke the overriden onSubscriptionsChanged()");
- }
- OnSubscriptionsChangedListener.this.onSubscriptionsChanged();
- }
}
- private final Handler mHandler;
+ /**
+ * Posted executor callback on the handler associated with a given looper.
+ * The looper can be the calling thread's looper or the looper passed from the
+ * constructor {@link #OnSubscriptionsChangedListener(Looper)}.
+ */
+ private final HandlerExecutor mExecutor;
+
+ /**
+ * @hide
+ */
+ public HandlerExecutor getHandlerExecutor() {
+ return mExecutor;
+ }
public OnSubscriptionsChangedListener() {
- mHandler = new OnSubscriptionsChangedListenerHandler();
+ mExecutor = new HandlerExecutor(new OnSubscriptionsChangedListenerHandler());
}
/**
@@ -941,7 +948,7 @@ public class SubscriptionManager {
* @hide
*/
public OnSubscriptionsChangedListener(Looper looper) {
- mHandler = new OnSubscriptionsChangedListenerHandler(looper);
+ mExecutor = new HandlerExecutor(new OnSubscriptionsChangedListenerHandler(looper));
}
/**
@@ -953,18 +960,6 @@ public class SubscriptionManager {
if (DBG) log("onSubscriptionsChanged: NOT OVERRIDDEN");
}
- /**
- * The callback methods need to be called on the handler thread where
- * this object was created. If the binder did that for us it'd be nice.
- */
- IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() {
- @Override
- public void onSubscriptionsChanged() {
- if (DBG) log("callback: received, sendEmptyMessage(0) to handler");
- mHandler.sendEmptyMessage(0);
- }
- };
-
private void log(String s) {
Rlog.d(LOG_TAG, s);
}
@@ -1006,21 +1001,19 @@ public class SubscriptionManager {
* onSubscriptionsChanged overridden.
*/
public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
+ if (listener == null) return;
String pkgName = mContext != null ? mContext.getOpPackageName() : "<unknown>";
if (DBG) {
logd("register OnSubscriptionsChangedListener pkgName=" + pkgName
+ " listener=" + listener);
}
- try {
- // We use the TelephonyRegistry as it runs in the system and thus is always
- // available. Where as SubscriptionController could crash and not be available
- ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
- "telephony.registry"));
- if (tr != null) {
- tr.addOnSubscriptionsChangedListener(pkgName, listener.callback);
- }
- } catch (RemoteException ex) {
- Log.e(LOG_TAG, "Remote exception ITelephonyRegistry " + ex);
+ // We use the TelephonyRegistry as it runs in the system and thus is always
+ // available. Where as SubscriptionController could crash and not be available
+ TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager)
+ mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
+ if (telephonyRegistryManager != null) {
+ telephonyRegistryManager.addOnSubscriptionsChangedListener(listener,
+ listener.mExecutor);
}
}
@@ -1032,21 +1025,18 @@ public class SubscriptionManager {
* @param listener that is to be unregistered.
*/
public void removeOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
+ if (listener == null) return;
String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
if (DBG) {
logd("unregister OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug
+ " listener=" + listener);
}
- try {
- // We use the TelephonyRegistry as it runs in the system and thus is always
- // available where as SubscriptionController could crash and not be available
- ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
- "telephony.registry"));
- if (tr != null) {
- tr.removeOnSubscriptionsChangedListener(pkgForDebug, listener.callback);
- }
- } catch (RemoteException ex) {
- Log.e(LOG_TAG, "Remote exception ITelephonyRegistry " + ex);
+ // We use the TelephonyRegistry as it runs in the system and thus is always
+ // available where as SubscriptionController could crash and not be available
+ TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager)
+ mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
+ if (telephonyRegistryManager != null) {
+ telephonyRegistryManager.removeOnSubscriptionsChangedListener(listener);
}
}
@@ -1065,7 +1055,6 @@ public class SubscriptionManager {
* for #onOpportunisticSubscriptionsChanged to be invoked.
*/
public static class OnOpportunisticSubscriptionsChangedListener {
- private Executor mExecutor;
/**
* Callback invoked when there is any change to any SubscriptionInfo. Typically
* this method would invoke {@link #getActiveSubscriptionInfoList}
@@ -1074,27 +1063,6 @@ public class SubscriptionManager {
if (DBG) log("onOpportunisticSubscriptionsChanged: NOT OVERRIDDEN");
}
- private void setExecutor(Executor executor) {
- mExecutor = executor;
- }
-
- /**
- * The callback methods need to be called on the handler thread where
- * this object was created. If the binder did that for us it'd be nice.
- */
- IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() {
- @Override
- public void onSubscriptionsChanged() {
- final long identity = Binder.clearCallingIdentity();
- try {
- if (DBG) log("onOpportunisticSubscriptionsChanged callback received.");
- mExecutor.execute(() -> onOpportunisticSubscriptionsChanged());
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- };
-
private void log(String s) {
Rlog.d(LOG_TAG, s);
}
@@ -1121,18 +1089,13 @@ public class SubscriptionManager {
+ " listener=" + listener);
}
- listener.setExecutor(executor);
-
- try {
- // We use the TelephonyRegistry as it runs in the system and thus is always
- // available. Where as SubscriptionController could crash and not be available
- ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
- "telephony.registry"));
- if (tr != null) {
- tr.addOnOpportunisticSubscriptionsChangedListener(pkgName, listener.callback);
- }
- } catch (RemoteException ex) {
- Log.e(LOG_TAG, "Remote exception ITelephonyRegistry " + ex);
+ // We use the TelephonyRegistry as it runs in the system and thus is always
+ // available where as SubscriptionController could crash and not be available
+ TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager)
+ mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
+ if (telephonyRegistryManager != null) {
+ telephonyRegistryManager.addOnOpportunisticSubscriptionsChangedListener(
+ listener, executor);
}
}
@@ -1152,16 +1115,10 @@ public class SubscriptionManager {
logd("unregister OnOpportunisticSubscriptionsChangedListener pkgForDebug="
+ pkgForDebug + " listener=" + listener);
}
- try {
- // We use the TelephonyRegistry as it runs in the system and thus is always
- // available where as SubscriptionController could crash and not be available
- ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
- "telephony.registry"));
- if (tr != null) {
- tr.removeOnSubscriptionsChangedListener(pkgForDebug, listener.callback);
- }
- } catch (RemoteException ex) {
- Log.e(LOG_TAG, "Remote exception ITelephonyRegistry " + ex);
+ TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager)
+ mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
+ if (telephonyRegistryManager != null) {
+ telephonyRegistryManager.removeOnOpportunisticSubscriptionsChangedListener(listener);
}
}
@@ -2096,13 +2053,13 @@ public class SubscriptionManager {
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static boolean isValidSlotIndex(int slotIndex) {
- return slotIndex >= 0 && slotIndex < TelephonyManager.getDefault().getSimCount();
+ return slotIndex >= 0 && slotIndex < TelephonyManager.getDefault().getSupportedModemCount();
}
/** @hide */
@UnsupportedAppUsage
public static boolean isValidPhoneId(int phoneId) {
- return phoneId >= 0 && phoneId < TelephonyManager.getDefault().getPhoneCount();
+ return phoneId >= 0 && phoneId < TelephonyManager.getDefault().getSupportedModemCount();
}
/** @hide */
@@ -2123,6 +2080,7 @@ public class SubscriptionManager {
if (VDBG) logd("putPhoneIdAndSubIdExtra: phoneId=" + phoneId + " subId=" + subId);
intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
intent.putExtra(EXTRA_SUBSCRIPTION_INDEX, subId);
+ intent.putExtra(EXTRA_SLOT_INDEX, phoneId);
intent.putExtra(PhoneConstants.PHONE_KEY, phoneId);
}
@@ -2414,8 +2372,12 @@ public class SubscriptionManager {
* @param plans the list of plans. The first plan is always the primary and
* most important plan. Any additional plans are secondary and
* may not be displayed or used by decision making logic.
+ * The list of all plans must meet the requirements defined in
+ * {@link SubscriptionPlan.Builder#setNetworkTypes(int[])}.
* @throws SecurityException if the caller doesn't meet the requirements
* outlined above.
+ * @throws IllegalArgumentException if plans don't meet the requirements
+ * mentioned above.
*/
public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) {
try {
@@ -2460,51 +2422,10 @@ public class SubscriptionManager {
*/
public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
@DurationMillisLong long timeoutMillis) {
- setSubscriptionOverrideUnmetered(subId, null, overrideUnmetered, timeoutMillis);
- }
-
- /**
- * Temporarily override the billing relationship between a carrier and
- * a specific subscriber to be considered unmetered for the given network
- * types. This will be reflected to apps via
- * {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED}.
- * This method is only accessible to the following narrow set of apps:
- * <ul>
- * <li>The carrier app for this subscriberId, as determined by
- * {@link TelephonyManager#hasCarrierPrivileges()}.
- * <li>The carrier app explicitly delegated access through
- * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
- * </ul>
- *
- * @param subId the subscriber this override applies to.
- * @param networkTypes all network types to set an override for. A null
- * network type means to apply the override to all network types.
- * Any unspecified network types will default to metered.
- * @param overrideUnmetered set if the billing relationship should be
- * considered unmetered.
- * @param timeoutMillis the timeout after which the requested override will
- * be automatically cleared, or {@code 0} to leave in the
- * requested state until explicitly cleared, or the next reboot,
- * whichever happens first.
- * @throws SecurityException if the caller doesn't meet the requirements
- * outlined above.
- * {@hide}
- */
- public void setSubscriptionOverrideUnmetered(int subId,
- @Nullable @NetworkType int[] networkTypes, boolean overrideUnmetered,
- @DurationMillisLong long timeoutMillis) {
try {
- long networkTypeMask = 0;
- if (networkTypes != null) {
- for (int networkType : networkTypes) {
- networkTypeMask |= TelephonyManager.getBitMaskForNetworkType(networkType);
- }
- } else {
- networkTypeMask = ~0;
- }
final int overrideValue = overrideUnmetered ? OVERRIDE_UNMETERED : 0;
getNetworkPolicy().setSubscriptionOverride(subId, OVERRIDE_UNMETERED, overrideValue,
- networkTypeMask, timeoutMillis, mContext.getOpPackageName());
+ timeoutMillis, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2536,52 +2457,10 @@ public class SubscriptionManager {
*/
public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
@DurationMillisLong long timeoutMillis) {
- setSubscriptionOverrideCongested(subId, null, overrideCongested, timeoutMillis);
- }
-
- /**
- * Temporarily override the billing relationship plan between a carrier and
- * a specific subscriber to be considered congested. This will cause the
- * device to delay certain network requests when possible, such as developer
- * jobs that are willing to run in a flexible time window.
- * <p>
- * This method is only accessible to the following narrow set of apps:
- * <ul>
- * <li>The carrier app for this subscriberId, as determined by
- * {@link TelephonyManager#hasCarrierPrivileges()}.
- * <li>The carrier app explicitly delegated access through
- * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
- * </ul>
- *
- * @param subId the subscriber this override applies to.
- * @param networkTypes all network types to set an override for. A null
- * network type means to apply the override to all network types.
- * Any unspecified network types will default to not congested.
- * @param overrideCongested set if the subscription should be considered
- * congested.
- * @param timeoutMillis the timeout after which the requested override will
- * be automatically cleared, or {@code 0} to leave in the
- * requested state until explicitly cleared, or the next reboot,
- * whichever happens first.
- * @throws SecurityException if the caller doesn't meet the requirements
- * outlined above.
- * @hide
- */
- public void setSubscriptionOverrideCongested(int subId,
- @Nullable @NetworkType int[] networkTypes, boolean overrideCongested,
- @DurationMillisLong long timeoutMillis) {
try {
- long networkTypeMask = 0;
- if (networkTypes != null) {
- for (int networkType : networkTypes) {
- networkTypeMask |= TelephonyManager.getBitMaskForNetworkType(networkType);
- }
- } else {
- networkTypeMask = ~0;
- }
final int overrideValue = overrideCongested ? OVERRIDE_CONGESTED : 0;
getNetworkPolicy().setSubscriptionOverride(subId, OVERRIDE_CONGESTED, overrideValue,
- networkTypeMask, timeoutMillis, mContext.getOpPackageName());
+ timeoutMillis, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/telephony/java/android/telephony/SubscriptionPlan.java b/telephony/java/android/telephony/SubscriptionPlan.java
index ec2050fb1a44..e24eb2696c6c 100644
--- a/telephony/java/android/telephony/SubscriptionPlan.java
+++ b/telephony/java/android/telephony/SubscriptionPlan.java
@@ -24,6 +24,7 @@ import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.Annotation.NetworkType;
import android.util.Range;
import android.util.RecurrenceRule;
@@ -33,6 +34,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.time.Period;
import java.time.ZonedDateTime;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.Objects;
@@ -80,6 +82,8 @@ public final class SubscriptionPlan implements Parcelable {
private int dataLimitBehavior = LIMIT_BEHAVIOR_UNKNOWN;
private long dataUsageBytes = BYTES_UNKNOWN;
private long dataUsageTime = TIME_UNKNOWN;
+ private @NetworkType int[] networkTypes;
+ private long networkTypesBitMask;
private SubscriptionPlan(RecurrenceRule cycleRule) {
this.cycleRule = Preconditions.checkNotNull(cycleRule);
@@ -93,6 +97,7 @@ public final class SubscriptionPlan implements Parcelable {
dataLimitBehavior = source.readInt();
dataUsageBytes = source.readLong();
dataUsageTime = source.readLong();
+ networkTypes = source.createIntArray();
}
@Override
@@ -109,6 +114,7 @@ public final class SubscriptionPlan implements Parcelable {
dest.writeInt(dataLimitBehavior);
dest.writeLong(dataUsageBytes);
dest.writeLong(dataUsageTime);
+ dest.writeIntArray(networkTypes);
}
@Override
@@ -121,13 +127,14 @@ public final class SubscriptionPlan implements Parcelable {
.append(" dataLimitBehavior=").append(dataLimitBehavior)
.append(" dataUsageBytes=").append(dataUsageBytes)
.append(" dataUsageTime=").append(dataUsageTime)
+ .append(" networkTypes=").append(Arrays.toString(networkTypes))
.append("}").toString();
}
@Override
public int hashCode() {
return Objects.hash(cycleRule, title, summary, dataLimitBytes, dataLimitBehavior,
- dataUsageBytes, dataUsageTime);
+ dataUsageBytes, dataUsageTime, Arrays.hashCode(networkTypes));
}
@Override
@@ -140,7 +147,8 @@ public final class SubscriptionPlan implements Parcelable {
&& dataLimitBytes == other.dataLimitBytes
&& dataLimitBehavior == other.dataLimitBehavior
&& dataUsageBytes == other.dataUsageBytes
- && dataUsageTime == other.dataUsageTime;
+ && dataUsageTime == other.dataUsageTime
+ && Arrays.equals(networkTypes, other.networkTypes);
}
return false;
}
@@ -204,6 +212,32 @@ public final class SubscriptionPlan implements Parcelable {
}
/**
+ * Return an array containing all {@link NetworkType}s this SubscriptionPlan applies to.
+ * A null array means this SubscriptionPlan applies to all network types.
+ */
+ public @Nullable @NetworkType int[] getNetworkTypes() {
+ return networkTypes;
+ }
+
+ /**
+ * Return the networkTypes array converted to a {@link TelephonyManager.NetworkTypeBitMask}
+ * @hide
+ */
+ public long getNetworkTypesBitMask() {
+ // calculate bitmask the first time and save for future calls
+ if (networkTypesBitMask == 0) {
+ if (networkTypes == null) {
+ networkTypesBitMask = ~0;
+ } else {
+ for (int networkType : networkTypes) {
+ networkTypesBitMask |= TelephonyManager.getBitMaskForNetworkType(networkType);
+ }
+ }
+ }
+ return networkTypesBitMask;
+ }
+
+ /**
* Return an iterator that will return all valid data usage cycles based on
* any recurrence rules. The iterator starts from the currently active cycle
* and walks backwards through time.
@@ -335,5 +369,24 @@ public final class SubscriptionPlan implements Parcelable {
plan.dataUsageTime = dataUsageTime;
return this;
}
+
+ /**
+ * Set the network types this SubscriptionPlan applies to.
+ * The developer must supply at least one plan that applies to all network types (default),
+ * and all additional plans may not include a particular network type more than once.
+ * Plan selection will prefer plans that have specific network types defined
+ * over plans that apply to all network types.
+ *
+ * @param networkTypes a set of all {@link NetworkType}s that apply to this plan.
+ * A null value or empty array means the plan applies to all network types.
+ */
+ public @NonNull Builder setNetworkTypes(@Nullable @NetworkType int[] networkTypes) {
+ if (networkTypes == null || networkTypes.length == 0) {
+ plan.networkTypes = null;
+ } else {
+ plan.networkTypes = networkTypes;
+ }
+ return this;
+ }
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a0f0552f7a84..7549adc616c8 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -63,8 +63,12 @@ import android.telecom.InCallService;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
+import android.telephony.Annotation.ApnType;
+import android.telephony.Annotation.CallState;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.Annotation.RadioPowerState;
+import android.telephony.Annotation.SimActivationState;
import android.telephony.VisualVoicemailService.VisualVoicemailTask;
-import android.telephony.data.ApnSetting;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
import android.telephony.ims.ImsMmTelManager;
@@ -254,17 +258,6 @@ public class TelephonyManager {
*/
public static final int UNINITIALIZED_CARD_ID = -2;
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"SRVCC_STATE_"},
- value = {
- SRVCC_STATE_HANDOVER_NONE,
- SRVCC_STATE_HANDOVER_STARTED,
- SRVCC_STATE_HANDOVER_COMPLETED,
- SRVCC_STATE_HANDOVER_FAILED,
- SRVCC_STATE_HANDOVER_CANCELED})
- public @interface SrvccState {}
-
private final Context mContext;
private final int mSubId;
@UnsupportedAppUsage
@@ -289,6 +282,21 @@ public class TelephonyManager {
};
/** @hide */
+ @IntDef(prefix = {"MODEM_COUNT_"},
+ value = {
+ MODEM_COUNT_NO_MODEM,
+ MODEM_COUNT_SINGLE_MODEM,
+ MODEM_COUNT_DUAL_MODEM,
+ MODEM_COUNT_TRI_MODEM
+ })
+ public @interface ModemCount {}
+
+ public static final int MODEM_COUNT_NO_MODEM = 0;
+ public static final int MODEM_COUNT_SINGLE_MODEM = 1;
+ public static final int MODEM_COUNT_DUAL_MODEM = 2;
+ public static final int MODEM_COUNT_TRI_MODEM = 3;
+
+ /** @hide */
@UnsupportedAppUsage
public TelephonyManager(Context context) {
this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
@@ -365,12 +373,26 @@ public class TelephonyManager {
/**
* Returns the number of phones available.
* Returns 0 if none of voice, sms, data is not supported
- * Returns 1 for Single standby mode (Single SIM functionality)
- * Returns 2 for Dual standby mode.(Dual SIM functionality)
- * Returns 3 for Tri standby mode.(Tri SIM functionality)
+ * Returns 1 for Single standby mode (Single SIM functionality).
+ * Returns 2 for Dual standby mode (Dual SIM functionality).
+ * Returns 3 for Tri standby mode (Tri SIM functionality).
+ * @deprecated Use {@link #getActiveModemCount} instead.
*/
+ @Deprecated
public int getPhoneCount() {
- int phoneCount = 1;
+ return getActiveModemCount();
+ }
+
+ /**
+ * Returns the number of logical modems currently configured to be activated.
+ *
+ * Returns 0 if none of voice, sms, data is not supported
+ * Returns 1 for Single standby mode (Single SIM functionality).
+ * Returns 2 for Dual standby mode (Dual SIM functionality).
+ * Returns 3 for Tri standby mode (Tri SIM functionality).
+ */
+ public @ModemCount int getActiveModemCount() {
+ int modemCount = 1;
switch (getMultiSimConfiguration()) {
case UNKNOWN:
ConnectivityManager cm = mContext == null ? null : (ConnectivityManager) mContext
@@ -378,33 +400,30 @@ public class TelephonyManager {
// check for voice and data support, 0 if not supported
if (!isVoiceCapable() && !isSmsCapable() && cm != null
&& !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)) {
- phoneCount = 0;
+ modemCount = MODEM_COUNT_NO_MODEM;
} else {
- phoneCount = 1;
+ modemCount = MODEM_COUNT_SINGLE_MODEM;
}
break;
case DSDS:
case DSDA:
- phoneCount = PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM;
+ modemCount = MODEM_COUNT_DUAL_MODEM;
break;
case TSTS:
- phoneCount = PhoneConstants.MAX_PHONE_COUNT_TRI_SIM;
+ modemCount = MODEM_COUNT_TRI_MODEM;
break;
}
- return phoneCount;
+ return modemCount;
}
/**
- *
- * Return how many phone / logical modem can be active simultaneously, in terms of device
+ * Return how many logical modem can be potentially active simultaneously, in terms of hardware
* capability.
- * For example, for a dual-SIM capable device, it always returns 2, even if only one logical
- * modem / SIM is active (aka in single SIM mode).
- *
- * TODO: b/139642279 publicize and rename.
- * @hide
+ * It might return different value from {@link #getActiveModemCount}. For example, for a
+ * dual-SIM capable device operating in single SIM mode (only one logical modem is turned on),
+ * {@link #getActiveModemCount} returns 1 while this API returns 2.
*/
- public static int getMaxPhoneCount() {
+ public @ModemCount int getSupportedModemCount() {
// TODO: b/139642279 when turning on this feature, remove dependency of
// PROPERTY_REBOOT_REQUIRED_ON_MODEM_CHANGE and always return result based on
// PROPERTY_MAX_ACTIVE_MODEMS.
@@ -413,9 +432,9 @@ public class TelephonyManager {
if (rebootRequired.equals("false")) {
// If no reboot is required, return max possible active modems.
return SystemProperties.getInt(
- TelephonyProperties.PROPERTY_MAX_ACTIVE_MODEMS, getDefault().getPhoneCount());
+ TelephonyProperties.PROPERTY_MAX_ACTIVE_MODEMS, getPhoneCount());
} else {
- return getDefault().getPhoneCount();
+ return getPhoneCount();
}
}
@@ -1177,6 +1196,35 @@ public class TelephonyManager {
"android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
/**
+ * Broadcast intent that indicates multi-SIM configuration is changed. For example, it changed
+ * from single SIM capable to dual-SIM capable (DSDS or DSDA) or triple-SIM mode.
+ *
+ * It doesn't indicate how many subscriptions are actually active, or which states SIMs are,
+ * or that all steps during multi-SIM change are done. To know those information you still need
+ * to listen to SIM_STATE changes or active subscription changes.
+ *
+ * See extra of {@link #EXTRA_NUM_OF_ACTIVE_SIM_SUPPORTED} for updated value.
+ */
+ public static final String ACTION_MULTI_SIM_CONFIG_CHANGED =
+ "android.telephony.action.MULTI_SIM_CONFIG_CHANGED";
+
+
+ /**
+ * The number of active SIM supported by current multi-SIM config. It's not related to how many
+ * SIM/subscriptions are currently active.
+ *
+ * For single SIM mode, it's 1.
+ * For DSDS or DSDA mode, it's 2.
+ * For triple-SIM mode, it's 3.
+ *
+ * Extra of {@link #ACTION_MULTI_SIM_CONFIG_CHANGED}.
+ *
+ * type: integer
+ */
+ public static final String EXTRA_NUM_OF_ACTIVE_SIM_SUPPORTED =
+ "android.telephony.extra.NUM_OF_ACTIVE_SIM_SUPPORTED";
+
+ /**
* @hide
*/
public static final String USSD_RESPONSE = "USSD_RESPONSE";
@@ -1571,12 +1619,11 @@ public class TelephonyManager {
* Returns the software version number for the device, for example,
* the IMEI/SV for GSM phones. Return null if the software version is
* not available.
- *
- * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
- * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ * <p>
+ * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}.
*/
- @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @Nullable
public String getDeviceSoftwareVersion() {
return getDeviceSoftwareVersion(getSlotIndex());
}
@@ -1585,12 +1632,16 @@ public class TelephonyManager {
* Returns the software version number for the device, for example,
* the IMEI/SV for GSM phones. Return null if the software version is
* not available.
+ * <p>
+ * Requires Permission: READ_PRIVILEGED_PHONE_STATE.
*
* @param slotIndex of which deviceID is returned
+ *
+ * @hide
*/
- /** {@hide} */
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- @UnsupportedAppUsage
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @Nullable
public String getDeviceSoftwareVersion(int slotIndex) {
ITelephony telephony = getITelephony();
if (telephony == null) return null;
@@ -1873,11 +1924,23 @@ public class TelephonyManager {
/**
* Returns the Network Access Identifier (NAI). Return null if NAI is not available.
*
- * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
- * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
+ * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+ * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+ * managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+ * access is deprecated and will be removed in a future release.
+ *
+ * <ul>
+ * <li>If the calling app's target SDK is API level 28 or lower and the app has the
+ * READ_PHONE_STATE permission then null is returned.</li>
+ * <li>If the calling app's target SDK is API level 28 or lower and the app does not have
+ * the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
+ * higher, then a SecurityException is thrown.</li>
+ * </ul>
*/
- @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public String getNai() {
return getNaiBySubscriberId(getSubId());
}
@@ -1885,6 +1948,21 @@ public class TelephonyManager {
/**
* Returns the NAI. Return null if NAI is not available.
*
+ * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
+ * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+ * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+ * managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+ * access is deprecated and will be removed in a future release.
+ *
+ * <ul>
+ * <li>If the calling app's target SDK is API level 28 or lower and the app has the
+ * READ_PHONE_STATE permission then null is returned.</li>
+ * <li>If the calling app's target SDK is API level 28 or lower and the app does not have
+ * the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
+ * higher, then a SecurityException is thrown.</li>
+ * </ul>
+ *
* @param slotIndex of which Nai is returned
*/
/** {@hide}*/
@@ -2452,41 +2530,45 @@ public class TelephonyManager {
* @return the lowercase 2 character ISO-3166 country code, or empty string if not available.
*/
public String getNetworkCountryIso() {
- return getNetworkCountryIsoForPhone(getPhoneId());
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) return "";
+ return telephony.getNetworkCountryIsoForPhone(getPhoneId(),
+ null /* no permission check */);
+ } catch (RemoteException ex) {
+ return "";
+ }
}
/**
- * Returns the ISO country code equivalent of the MCC (Mobile Country Code) of the current
+ * Returns the ISO-3166 country code equivalent of the MCC (Mobile Country Code) of the current
* registered operator or the cell nearby, if available.
* <p>
+ * The ISO-3166 country code is provided in lowercase 2 character format.
+ * <p>
+ * Note: In multi-sim, this returns a shared emergency network country iso from other
+ * subscription if the subscription used to create the TelephonyManager doesn't camp on
+ * a network due to some reason (e.g. pin/puk locked), or sim is absent in the corresponding
+ * slot.
* Note: Result may be unreliable on CDMA networks (use {@link #getPhoneType()} to determine
* if on a CDMA network).
- *
- * @param subId for which Network CountryIso is returned
- * @hide
- */
- @UnsupportedAppUsage
- public String getNetworkCountryIso(int subId) {
- return getNetworkCountryIsoForPhone(getPhoneId(subId));
- }
-
- /**
- * Returns the ISO country code equivalent of the current registered
- * operator's MCC (Mobile Country Code) of a subscription.
* <p>
- * Availability: Only when user is registered to a network. Result may be
- * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
- * on a CDMA network).
*
- * @param phoneId for which Network CountryIso is returned
+ * @param slotIndex the SIM slot index to get network country ISO.
+ *
+ * @return the lowercase 2 character ISO-3166 country code, or empty string if not available.
+ *
+ * {@hide}
*/
- /** {@hide} */
- @UnsupportedAppUsage
- public String getNetworkCountryIsoForPhone(int phoneId) {
+ @SystemApi
+ @TestApi
+ @NonNull
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public String getNetworkCountryIso(int slotIndex) {
try {
ITelephony telephony = getITelephony();
if (telephony == null) return "";
- return telephony.getNetworkCountryIsoForPhone(phoneId);
+ return telephony.getNetworkCountryIsoForPhone(slotIndex, getOpPackageName());
} catch (RemoteException ex) {
return "";
}
@@ -2545,33 +2627,6 @@ public class TelephonyManager {
/** Max network type number. Update as new types are added. Don't add negative types. {@hide} */
public static final int MAX_NETWORK_TYPE = NETWORK_TYPE_NR;
- /** @hide */
- @IntDef({
- NETWORK_TYPE_UNKNOWN,
- NETWORK_TYPE_GPRS,
- NETWORK_TYPE_EDGE,
- NETWORK_TYPE_UMTS,
- NETWORK_TYPE_CDMA,
- NETWORK_TYPE_EVDO_0,
- NETWORK_TYPE_EVDO_A,
- NETWORK_TYPE_1xRTT,
- NETWORK_TYPE_HSDPA,
- NETWORK_TYPE_HSUPA,
- NETWORK_TYPE_HSPA,
- NETWORK_TYPE_IDEN,
- NETWORK_TYPE_EVDO_B,
- NETWORK_TYPE_LTE,
- NETWORK_TYPE_EHRPD,
- NETWORK_TYPE_HSPAP,
- NETWORK_TYPE_GSM,
- NETWORK_TYPE_TD_SCDMA,
- NETWORK_TYPE_IWLAN,
- NETWORK_TYPE_LTE_CA,
- NETWORK_TYPE_NR,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface NetworkType{}
-
/**
* Return the current data network type.
*
@@ -2748,6 +2803,8 @@ public class TelephonyManager {
/** Class of broadly defined "4G" networks. {@hide} */
@UnsupportedAppUsage
public static final int NETWORK_CLASS_4_G = 3;
+ /** Class of broadly defined "5G" networks. {@hide} */
+ public static final int NETWORK_CLASS_5_G = 4;
/**
* Return general class of network type, such as "3G" or "4G". In cases
@@ -2780,6 +2837,8 @@ public class TelephonyManager {
case NETWORK_TYPE_IWLAN:
case NETWORK_TYPE_LTE_CA:
return NETWORK_CLASS_4_G;
+ case NETWORK_TYPE_NR:
+ return NETWORK_CLASS_5_G;
default:
return NETWORK_CLASS_UNKNOWN;
}
@@ -4568,6 +4627,17 @@ public class TelephonyManager {
}
/**
+ * Sim activation type: voice
+ * @hide
+ */
+ public static final int SIM_ACTIVATION_TYPE_VOICE = 0;
+ /**
+ * Sim activation type: data
+ * @hide
+ */
+ public static final int SIM_ACTIVATION_TYPE_DATA = 1;
+
+ /**
* Initial SIM activation state, unknown. Not set by any carrier apps.
* @hide
*/
@@ -4610,17 +4680,6 @@ public class TelephonyManager {
@SystemApi
public static final int SIM_ACTIVATION_STATE_RESTRICTED = 4;
- /** @hide */
- @IntDef({
- SIM_ACTIVATION_STATE_UNKNOWN,
- SIM_ACTIVATION_STATE_ACTIVATING,
- SIM_ACTIVATION_STATE_ACTIVATED,
- SIM_ACTIVATION_STATE_DEACTIVATED,
- SIM_ACTIVATION_STATE_RESTRICTED
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface SimActivationState{}
-
/**
* Sets the voice activation state
*
@@ -5005,15 +5064,6 @@ public class TelephonyManager {
*/
public static final int CALL_STATE_OFFHOOK = 2;
- /** @hide */
- @IntDef(prefix = { "CALL_STATE_" }, value = {
- CALL_STATE_IDLE,
- CALL_STATE_RINGING,
- CALL_STATE_OFFHOOK
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface CallState{}
-
/**
* Returns the state of all calls on the device.
* <p>
@@ -5529,18 +5579,20 @@ public class TelephonyManager {
telephony.requestCellInfoUpdate(
getSubId(),
new ICellInfoCallback.Stub() {
+ @Override
public void onCellInfo(List<CellInfo> cellInfo) {
Binder.withCleanCallingIdentity(() ->
executor.execute(() -> callback.onCellInfo(cellInfo)));
}
- public void onError(int errorCode, android.os.ParcelableException detail) {
+ @Override
+ public void onError(int errorCode, String exceptionName, String message) {
Binder.withCleanCallingIdentity(() ->
executor.execute(() -> callback.onError(
- errorCode, detail.getCause())));
+ errorCode,
+ createThrowableByClassName(exceptionName, message))));
}
}, getOpPackageName());
-
} catch (RemoteException ex) {
}
}
@@ -5569,21 +5621,36 @@ public class TelephonyManager {
telephony.requestCellInfoUpdateWithWorkSource(
getSubId(),
new ICellInfoCallback.Stub() {
+ @Override
public void onCellInfo(List<CellInfo> cellInfo) {
Binder.withCleanCallingIdentity(() ->
executor.execute(() -> callback.onCellInfo(cellInfo)));
}
- public void onError(int errorCode, android.os.ParcelableException detail) {
+ @Override
+ public void onError(int errorCode, String exceptionName, String message) {
Binder.withCleanCallingIdentity(() ->
executor.execute(() -> callback.onError(
- errorCode, detail.getCause())));
+ errorCode,
+ createThrowableByClassName(exceptionName, message))));
}
}, getOpPackageName(), workSource);
} catch (RemoteException ex) {
}
}
+ private static Throwable createThrowableByClassName(String className, String message) {
+ if (className == null) {
+ return null;
+ }
+ try {
+ Class<?> c = Class.forName(className);
+ return (Throwable) c.getConstructor(String.class).newInstance(message);
+ } catch (ReflectiveOperationException | ClassCastException e) {
+ }
+ return new RuntimeException(className + ": " + message);
+ }
+
/**
* Sets the minimum time in milli-seconds between {@link PhoneStateListener#onCellInfoChanged
* PhoneStateListener.onCellInfoChanged} will be invoked.
@@ -6836,6 +6903,40 @@ public class TelephonyManager {
}
/**
+ * Replace the contents of the forbidden PLMN SIM file with the provided values.
+ * Passing an empty list will clear the contents of the EFfplmn file.
+ * If the provided list is shorter than the size of EFfplmn, then the list will be padded
+ * up to the file size with 'FFFFFF'. (required by 3GPP TS 31.102 spec 4.2.16)
+ * If the list is longer than the size of EFfplmn, then the file will be written from the
+ * beginning of the list up to the file size.
+ *
+ * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ *
+ * @param fplmns a list of PLMNs to be forbidden.
+ *
+ * @return number of PLMNs that were successfully written to the SIM FPLMN list.
+ * This may be less than the number of PLMNs passed in where the SIM file does not have enough
+ * room for all of the values passed in. Return -1 in the event of an unexpected failure
+ */
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public int setForbiddenPlmns(@NonNull List<String> fplmns) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) return 0;
+ return telephony.setForbiddenPlmns(
+ getSubId(), APPTYPE_USIM, fplmns, getOpPackageName());
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "setForbiddenPlmns RemoteException: " + ex.getMessage());
+ } catch (NullPointerException ex) {
+ // This could happen before phone starts
+ Rlog.e(TAG, "setForbiddenPlmns NullPointerException: " + ex.getMessage());
+ }
+ return 0;
+ }
+
+ /**
* Get P-CSCF address from PCO after data connection is established or modified.
* @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN
* @return array of P-CSCF address
@@ -8309,15 +8410,6 @@ public class TelephonyManager {
return false;
}
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"RADIO_POWER_"},
- value = {RADIO_POWER_OFF,
- RADIO_POWER_ON,
- RADIO_POWER_UNAVAILABLE,
- })
- public @interface RadioPowerState {}
-
/**
* Radio explicitly powered off (e.g, airplane mode).
* @hide
@@ -10151,11 +10243,13 @@ public class TelephonyManager {
/**
* Action set from carrier signalling broadcast receivers to enable/disable radio
- * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
+ * Permissions {@link android.Manifest.permission.MODIFY_PHONE_STATE} is required.
* @param subId the subscription ID that this action applies to.
* @param enabled control enable or disable radio.
* @hide
*/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void carrierActionSetRadioEnabled(int subId, boolean enabled) {
try {
ITelephony service = getITelephony();
@@ -10170,11 +10264,13 @@ public class TelephonyManager {
/**
* Action set from carrier signalling broadcast receivers to start/stop reporting default
* network available events
- * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
+ * Permissions {@link android.Manifest.permission.MODIFY_PHONE_STATE} is required.
* @param subId the subscription ID that this action applies to.
* @param report control start/stop reporting network status.
* @hide
*/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void carrierActionReportDefaultNetworkStatus(int subId, boolean report) {
try {
ITelephony service = getITelephony();
@@ -10188,10 +10284,12 @@ public class TelephonyManager {
/**
* Action set from carrier signalling broadcast receivers to reset all carrier actions
- * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
+ * Permissions {@link android.Manifest.permission.MODIFY_PHONE_STATE} is required.
* @param subId the subscription ID that this action applies to.
* @hide
*/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void carrierActionResetAll(int subId) {
try {
ITelephony service = getITelephony();
@@ -11427,11 +11525,14 @@ public class TelephonyManager {
* 3) APN type is whitelisted. E.g. MMS is whitelisted if
* {@link SubscriptionManager#setAlwaysAllowMmsData} is turned on.
*
+ * @param apnType Value indicating the apn type. Apn types are defined in {@link ApnSetting}.
* @return whether data is enabled for a apn type.
*
* @hide
*/
- public boolean isDataEnabledForApn(@ApnSetting.ApnType int apnType) {
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isDataEnabledForApn(@ApnType int apnType) {
String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
try {
ITelephony service = getITelephony();
@@ -11452,7 +11553,7 @@ public class TelephonyManager {
*
* @hide
*/
- public boolean isApnMetered(@ApnSetting.ApnType int apnType) {
+ public boolean isApnMetered(@ApnType int apnType) {
try {
ITelephony service = getITelephony();
if (service != null) {
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index 28747dab38db..9ff851598648 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -104,7 +104,7 @@ public final class TelephonyScanManager {
private final Looper mLooper;
private final Messenger mMessenger;
- private SparseArray<NetworkScanInfo> mScanInfo = new SparseArray<NetworkScanInfo>();
+ private final SparseArray<NetworkScanInfo> mScanInfo = new SparseArray<NetworkScanInfo>();
public TelephonyScanManager() {
HandlerThread thread = new HandlerThread(TAG);
@@ -204,14 +204,16 @@ public final class TelephonyScanManager {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- int scanId = telephony.requestNetworkScan(
- subId, request, mMessenger, new Binder(), callingPackage);
- if (scanId == INVALID_SCAN_ID) {
- Rlog.e(TAG, "Failed to initiate network scan");
- return null;
+ synchronized (mScanInfo) {
+ int scanId = telephony.requestNetworkScan(
+ subId, request, mMessenger, new Binder(), callingPackage);
+ 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);
}
- saveScanInfo(scanId, request, executor, callback);
- return new NetworkScan(scanId, subId);
}
} catch (RemoteException ex) {
Rlog.e(TAG, "requestNetworkScan RemoteException", ex);
@@ -223,9 +225,7 @@ public final class TelephonyScanManager {
private void saveScanInfo(
int id, NetworkScanRequest request, Executor executor, NetworkScanCallback callback) {
- synchronized (mScanInfo) {
- mScanInfo.put(id, new NetworkScanInfo(request, executor, callback));
- }
+ mScanInfo.put(id, new NetworkScanInfo(request, executor, callback));
}
private ITelephony getITelephony() {
diff --git a/telephony/java/android/telephony/cdma/CdmaSmsCbProgramData.java b/telephony/java/android/telephony/cdma/CdmaSmsCbProgramData.java
index 5f2f75da5c41..02429b5c2a2c 100644
--- a/telephony/java/android/telephony/cdma/CdmaSmsCbProgramData.java
+++ b/telephony/java/android/telephony/cdma/CdmaSmsCbProgramData.java
@@ -16,12 +16,19 @@
package android.telephony.cdma;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
- * CDMA Service Category Program Data from SCPT teleservice SMS.
+ * CDMA Service Category Program Data from SCPT (Service Category Programming Teleservice) SMS,
+ * as defined in 3GPP2 C.S0015-B section 4.5.19.
+ * <p>
* The CellBroadcastReceiver app receives an Intent with action
* {@link android.provider.Telephony.Sms.Intents#SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION}
* containing an array of these objects to update its list of cell broadcast service categories
@@ -29,6 +36,7 @@ import android.os.Parcelable;
*
* {@hide}
*/
+@SystemApi
public final class CdmaSmsCbProgramData implements Parcelable {
/** Delete the specified service category from the list of enabled categories. */
@@ -40,40 +48,83 @@ public final class CdmaSmsCbProgramData implements Parcelable {
/** Clear all service categories from the list of enabled categories. */
public static final int OPERATION_CLEAR_CATEGORIES = 2;
- /** Alert option: no alert. */
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"OPERATION_"},
+ value = {
+ OPERATION_DELETE_CATEGORY,
+ OPERATION_ADD_CATEGORY,
+ OPERATION_CLEAR_CATEGORIES,
+ })
+ public @interface Operation {}
+
+ // CMAS alert service category assignments, see 3GPP2 C.R1001 table 9.3.3-1
+ /** Indicates a presidential-level alert */
+ public static final int CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT = 0x1000;
+
+ /** Indicates an extreme threat to life and property */
+ public static final int CATEGORY_CMAS_EXTREME_THREAT = 0x1001;
+
+ /** Indicates an severe threat to life and property */
+ public static final int CATEGORY_CMAS_SEVERE_THREAT = 0x1002;
+
+ /** Indicates an AMBER child abduction emergency */
+ public static final int CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY = 0x1003;
+
+ /** Indicates a CMAS test message */
+ public static final int CATEGORY_CMAS_TEST_MESSAGE = 0x1004;
+
+ /** The last reserved value of a CMAS service category according to 3GPP C.R1001 table
+ * 9.3.3-1. */
+ public static final int CATEGORY_CMAS_LAST_RESERVED_VALUE = 0x10ff;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"CATEGORY_"},
+ value = {
+ CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT,
+ CATEGORY_CMAS_EXTREME_THREAT,
+ CATEGORY_CMAS_SEVERE_THREAT,
+ CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY,
+ CATEGORY_CMAS_TEST_MESSAGE,
+ CATEGORY_CMAS_LAST_RESERVED_VALUE,
+ })
+ public @interface Category {}
+
+ /** Alert option: no alert. @hide */
public static final int ALERT_OPTION_NO_ALERT = 0;
- /** Alert option: default alert. */
+ /** Alert option: default alert. @hide */
public static final int ALERT_OPTION_DEFAULT_ALERT = 1;
- /** Alert option: vibrate alert once. */
+ /** Alert option: vibrate alert once. @hide */
public static final int ALERT_OPTION_VIBRATE_ONCE = 2;
- /** Alert option: vibrate alert - repeat. */
+ /** Alert option: vibrate alert - repeat. @hide */
public static final int ALERT_OPTION_VIBRATE_REPEAT = 3;
- /** Alert option: visual alert once. */
+ /** Alert option: visual alert once. @hide */
public static final int ALERT_OPTION_VISUAL_ONCE = 4;
- /** Alert option: visual alert - repeat. */
+ /** Alert option: visual alert - repeat. @hide */
public static final int ALERT_OPTION_VISUAL_REPEAT = 5;
- /** Alert option: low-priority alert once. */
+ /** Alert option: low-priority alert once. @hide */
public static final int ALERT_OPTION_LOW_PRIORITY_ONCE = 6;
- /** Alert option: low-priority alert - repeat. */
+ /** Alert option: low-priority alert - repeat. @hide */
public static final int ALERT_OPTION_LOW_PRIORITY_REPEAT = 7;
- /** Alert option: medium-priority alert once. */
+ /** Alert option: medium-priority alert once. @hide */
public static final int ALERT_OPTION_MED_PRIORITY_ONCE = 8;
- /** Alert option: medium-priority alert - repeat. */
+ /** Alert option: medium-priority alert - repeat. @hide */
public static final int ALERT_OPTION_MED_PRIORITY_REPEAT = 9;
- /** Alert option: high-priority alert once. */
+ /** Alert option: high-priority alert once. @hide */
public static final int ALERT_OPTION_HIGH_PRIORITY_ONCE = 10;
- /** Alert option: high-priority alert - repeat. */
+ /** Alert option: high-priority alert - repeat. @hide */
public static final int ALERT_OPTION_HIGH_PRIORITY_REPEAT = 11;
/** Service category operation (add/delete/clear). */
@@ -94,9 +145,12 @@ public final class CdmaSmsCbProgramData implements Parcelable {
/** Name of service category. */
private final String mCategoryName;
- /** Create a new CdmaSmsCbProgramData object with the specified values. */
- public CdmaSmsCbProgramData(int operation, int category, int language, int maxMessages,
- int alertOption, @NonNull String categoryName) {
+ /**
+ * Create a new CdmaSmsCbProgramData object with the specified values.
+ * @hide
+ */
+ public CdmaSmsCbProgramData(@Operation int operation, @Category int category, int language,
+ int maxMessages, int alertOption, @NonNull String categoryName) {
mOperation = operation;
mCategory = category;
mLanguage = language;
@@ -105,7 +159,10 @@ public final class CdmaSmsCbProgramData implements Parcelable {
mCategoryName = categoryName;
}
- /** Create a new CdmaSmsCbProgramData object from a Parcel. */
+ /**
+ * Create a new CdmaSmsCbProgramData object from a Parcel.
+ * @hide
+ */
CdmaSmsCbProgramData(Parcel in) {
mOperation = in.readInt();
mCategory = in.readInt();
@@ -133,23 +190,28 @@ public final class CdmaSmsCbProgramData implements Parcelable {
/**
* Returns the service category operation, e.g. {@link #OPERATION_ADD_CATEGORY}.
- * @return one of the {@code OPERATION_*} values
+ *
+ * @return the service category operation
*/
- public int getOperation() {
+ public @Operation int getOperation() {
return mOperation;
}
/**
- * Returns the CDMA service category to modify.
+ * Returns the CDMA service category to modify. See 3GPP2 C.S0015-B section 3.4.3.2 for more
+ * information on the service category. Currently only CMAS service categories 0x1000 through
+ * 0x10FF are supported.
+ *
* @return a 16-bit CDMA service category value
*/
- public int getCategory() {
+ public @Category int getCategory() {
return mCategory;
}
/**
* Returns the CDMA language code for this service category.
* @return one of the language values defined in BearerData.LANGUAGE_*
+ * @hide
*/
public int getLanguage() {
return mLanguage;
@@ -158,6 +220,7 @@ public final class CdmaSmsCbProgramData implements Parcelable {
/**
* Returns the maximum number of messages to store for this service category.
* @return the maximum number of messages to store for this service category
+ * @hide
*/
public int getMaxMessages() {
return mMaxMessages;
@@ -166,6 +229,7 @@ public final class CdmaSmsCbProgramData implements Parcelable {
/**
* Returns the service category alert option, e.g. {@link #ALERT_OPTION_DEFAULT_ALERT}.
* @return one of the {@code ALERT_OPTION_*} values
+ * @hide
*/
public int getAlertOption() {
return mAlertOption;
@@ -174,6 +238,7 @@ public final class CdmaSmsCbProgramData implements Parcelable {
/**
* Returns the service category name, in the language specified by {@link #getLanguage()}.
* @return an optional service category name
+ * @hide
*/
@NonNull
public String getCategoryName() {
@@ -196,7 +261,10 @@ public final class CdmaSmsCbProgramData implements Parcelable {
return 0;
}
- /** Creator for unparcelling objects. */
+ /**
+ * Creator for unparcelling objects.
+ */
+ @NonNull
public static final Parcelable.Creator<CdmaSmsCbProgramData>
CREATOR = new Parcelable.Creator<CdmaSmsCbProgramData>() {
@Override
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index a3c37db6f381..ae739334b9bf 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -26,6 +26,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.provider.Telephony;
import android.provider.Telephony.Carriers;
+import android.telephony.Annotation.ApnType;
+import android.telephony.Annotation.NetworkType;
import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
@@ -109,23 +111,6 @@ public class ApnSetting implements Parcelable {
/** APN type for MCX (Mission Critical Service) where X can be PTT/Video/Data */
public static final int TYPE_MCX = ApnTypes.MCX;
- /** @hide */
- @IntDef(flag = true, prefix = { "TYPE_" }, value = {
- TYPE_DEFAULT,
- TYPE_MMS,
- TYPE_SUPL,
- TYPE_DUN,
- TYPE_HIPRI,
- TYPE_FOTA,
- TYPE_IMS,
- TYPE_CBS,
- TYPE_IA,
- TYPE_EMERGENCY,
- TYPE_MCX
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ApnType {}
-
// Possible values for authentication types.
/** No authentication type. */
public static final int AUTH_TYPE_NONE = 0;
@@ -1430,7 +1415,7 @@ public class ApnSetting implements Parcelable {
*
* @hide
*/
- public boolean canSupportNetworkType(@TelephonyManager.NetworkType int networkType) {
+ public boolean canSupportNetworkType(@NetworkType int networkType) {
// Do a special checking for GSM. In reality, GSM is a voice only network type and can never
// be used for data. We allow it here because in some DSDS corner cases, on the non-DDS
// sub, modem reports data rat unknown. In that case if voice is GSM and this APN supports
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index 9170e88ce832..49625bbecf4f 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -24,8 +24,8 @@ import android.annotation.SystemApi;
import android.net.LinkAddress;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.Annotation.DataFailureCause;
import android.telephony.DataFailCause;
-import android.telephony.DataFailCause.FailCause;
import android.telephony.data.ApnSetting.ProtocolType;
import com.android.internal.annotations.VisibleForTesting;
@@ -67,7 +67,7 @@ public final class DataCallResponse implements Parcelable {
/** Indicates the data connection is active with physical link up. */
public static final int LINK_STATUS_ACTIVE = 2;
- private final @FailCause int mCause;
+ private final @DataFailureCause int mCause;
private final int mSuggestedRetryTime;
private final int mId;
private final @LinkStatus int mLinkStatus;
@@ -103,7 +103,7 @@ public final class DataCallResponse implements Parcelable {
*
* @removed Use the {@link Builder()} instead.
*/
- public DataCallResponse(@FailCause int cause, int suggestedRetryTime, int id,
+ public DataCallResponse(@DataFailureCause int cause, int suggestedRetryTime, int id,
@LinkStatus int linkStatus,
@ProtocolType int protocolType, @Nullable String interfaceName,
@Nullable List<LinkAddress> addresses,
@@ -150,7 +150,7 @@ public final class DataCallResponse implements Parcelable {
/**
* @return Data call fail cause. {@link DataFailCause#NONE} indicates no error.
*/
- @FailCause
+ @DataFailureCause
public int getCause() { return mCause; }
/**
@@ -314,7 +314,7 @@ public final class DataCallResponse implements Parcelable {
* </code></pre>
*/
public static final class Builder {
- private @FailCause int mCause;
+ private @DataFailureCause int mCause;
private int mSuggestedRetryTime;
@@ -348,7 +348,7 @@ public final class DataCallResponse implements Parcelable {
* @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error.
* @return The same instance of the builder.
*/
- public @NonNull Builder setCause(@FailCause int cause) {
+ public @NonNull Builder setCause(@DataFailureCause int cause) {
mCause = cause;
return this;
}
diff --git a/telephony/java/android/telephony/data/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java
index 0d79ec98fcbb..30c209b28a4d 100644
--- a/telephony/java/android/telephony/data/DataProfile.java
+++ b/telephony/java/android/telephony/data/DataProfile.java
@@ -25,8 +25,8 @@ import android.annotation.SystemApi;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.Annotation.ApnType;
import android.telephony.TelephonyManager.NetworkTypeBitMask;
-import android.telephony.data.ApnSetting.ApnType;
import android.telephony.data.ApnSetting.AuthType;
import android.text.TextUtils;
diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java
index 0e1751d50949..e793979a61c9 100644
--- a/telephony/java/android/telephony/data/QualifiedNetworksService.java
+++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java
@@ -27,8 +27,8 @@ import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.Annotation.ApnType;
import android.telephony.Rlog;
-import android.telephony.data.ApnSetting.ApnType;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
@@ -133,7 +133,7 @@ public abstract class QualifiedNetworksService extends Service {
* service.
*
* @param apnTypes APN types of the qualified networks. This must be a bitmask combination
- * of {@link ApnSetting.ApnType}.
+ * of {@link ApnType}.
* @param qualifiedNetworkTypes List of network types which are qualified for data
* connection setup for {@link @apnType} in the preferred order. Each element in the list
* is a {@link AccessNetworkType}. An empty list indicates no networks are qualified
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index 19d07242132b..16662652847d 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -18,6 +18,7 @@ package android.telephony.emergency;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.TestApi;
import android.hardware.radio.V1_4.EmergencyNumberSource;
import android.hardware.radio.V1_4.EmergencyServiceCategory;
import android.os.Parcel;
@@ -184,6 +185,7 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu
*
* @hide
*/
+ @TestApi
public static final int EMERGENCY_NUMBER_SOURCE_TEST = 1 << 5;
/** Bit-field which indicates the number is from the modem config. */
public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG =
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index cabd4dfd91f1..5a90cb13a548 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -32,6 +32,7 @@ import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.telephony.TelephonyManager;
+import android.telephony.euicc.EuiccCardManager.ResetOption;
import com.android.internal.telephony.euicc.IEuiccController;
@@ -821,17 +822,22 @@ public class EuiccManager {
}
/**
- * Erase all subscriptions and reset the eUICC.
+ * Erase all operational subscriptions and reset the eUICC.
*
* <p>Requires that the calling app has the
* {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
*
* @param callbackIntent a PendingIntent to launch when the operation completes.
+ *
+ * @deprecated From R, callers should specify a flag for specific set of subscriptions to erase
+ * and use @link{eraseSubscriptionsWithOptions} instead
+ *
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
- public void eraseSubscriptions(PendingIntent callbackIntent) {
+ @Deprecated
+ public void eraseSubscriptions(@NonNull PendingIntent callbackIntent) {
if (!isEnabled()) {
sendUnavailableError(callbackIntent);
return;
@@ -844,6 +850,32 @@ public class EuiccManager {
}
/**
+ * Erase all specific subscriptions and reset the eUICC.
+ *
+ * <p>Requires that the calling app has the
+ * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
+ *
+ * @param options flag indicating specific set of subscriptions to erase
+ * @param callbackIntent a PendingIntent to launch when the operation completes.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+ public void eraseSubscriptionsWithOptions(
+ @ResetOption int options, @NonNull PendingIntent callbackIntent) {
+ if (!isEnabled()) {
+ sendUnavailableError(callbackIntent);
+ return;
+ }
+ try {
+ getIEuiccController().eraseSubscriptionsWithOptions(mCardId, options, callbackIntent);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Ensure that subscriptions will be retained on the next factory reset.
*
* <p>By default, all subscriptions on the eUICC are erased the first time a device boots (ever
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index a1a7fcc5dd51..2fad8479178e 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -183,19 +183,17 @@ public class ImsMmTelManager {
/**
* Notifies the framework when the IMS Provider is registered to the IMS network.
*
- * @param imsTransportType the radio access technology. Valid values are defined in
- * {@link android.telephony.AccessNetworkConstants.TransportType}.
+ * @param imsTransportType the radio access technology.
*/
- public void onRegistered(int imsTransportType) {
+ public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType) {
}
/**
* Notifies the framework when the IMS Provider is trying to register the IMS network.
*
- * @param imsTransportType the radio access technology. Valid values are defined in
- * {@link android.telephony.AccessNetworkConstants.TransportType}.
+ * @param imsTransportType the radio access technology.
*/
- public void onRegistering(int imsTransportType) {
+ public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType) {
}
/**
@@ -207,15 +205,14 @@ public class ImsMmTelManager {
}
/**
- * A failure has occurred when trying to handover registration to another technology type,
- * defined in {@link android.telephony.AccessNetworkConstants.TransportType}
+ * A failure has occurred when trying to handover registration to another technology type.
*
- * @param imsTransportType The
- * {@link android.telephony.AccessNetworkConstants.TransportType}
- * transport type that has failed to handover registration to.
+ * @param imsTransportType The transport type that has failed to handover registration to.
* @param info A {@link ImsReasonInfo} that identifies the reason for failure.
*/
- public void onTechnologyChangeFailed(int imsTransportType, @Nullable ImsReasonInfo info) {
+ public void onTechnologyChangeFailed(
+ @AccessNetworkConstants.TransportType int imsTransportType,
+ @Nullable ImsReasonInfo info) {
}
/**
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index 1e0d9a786acc..10251d707c22 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -885,6 +885,12 @@ public final class ImsReasonInfo implements Parcelable {
*/
public static final int CODE_WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION = 1623;
+ /**
+ * The dialed RTT call should be retried without RTT
+ * @hide
+ */
+ public static final int CODE_RETRY_ON_IMS_WITHOUT_RTT = 3001;
+
/*
* OEM specific error codes. To be used by OEMs when they don't want to reveal error code which
* would be replaced by ERROR_UNSPECIFIED.
@@ -1065,6 +1071,7 @@ public final class ImsReasonInfo implements Parcelable {
CODE_REJECT_VT_AVPF_NOT_ALLOWED,
CODE_REJECT_ONGOING_ENCRYPTED_CALL,
CODE_REJECT_ONGOING_CS_CALL,
+ CODE_RETRY_ON_IMS_WITHOUT_RTT,
CODE_OEM_CAUSE_1,
CODE_OEM_CAUSE_2,
CODE_OEM_CAUSE_3,
diff --git a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
index 175769bd34e4..36ece958d501 100644
--- a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
@@ -17,6 +17,7 @@
package android.telephony.ims.stub;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.SystemApi;
import android.os.RemoteException;
import android.telephony.SmsManager;
@@ -148,14 +149,16 @@ public class ImsSmsImplBase {
*
* @param token unique token generated by the platform that should be used when triggering
* callbacks for this specific message.
- * @param messageRef the message reference.
- * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
- * {@link SmsMessage#FORMAT_3GPP2}.
+ * @param messageRef the message reference, which may be 1 byte if it is in
+ * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+ * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+ * @param format the format of the message.
* @param smsc the Short Message Service Center address.
* @param isRetry whether it is a retry of an already attempted message or not.
* @param pdu PDU representing the contents of the message.
*/
- public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry,
+ public void sendSms(int token, @IntRange(from = 0, to = 65535) int messageRef,
+ @SmsMessage.Format String format, String smsc, boolean isRetry,
byte[] pdu) {
// Base implementation returns error. Should be overridden.
try {
@@ -172,14 +175,13 @@ public class ImsSmsImplBase {
* provider.
*
* @param token token provided in {@link #onSmsReceived(int, String, byte[])}
- * @param messageRef the message reference
- * @param result result of delivering the message. Valid values are:
- * {@link #DELIVER_STATUS_OK},
- * {@link #DELIVER_STATUS_ERROR_GENERIC},
- * {@link #DELIVER_STATUS_ERROR_NO_MEMORY},
- * {@link #DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED}
+ * @param messageRef the message reference, which may be 1 byte if it is in
+ * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+ * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+ * @param result result of delivering the message.
*/
- public void acknowledgeSms(int token, int messageRef, @DeliverStatusResult int result) {
+ public void acknowledgeSms(int token, @IntRange(from = 0, to = 65535) int messageRef,
+ @DeliverStatusResult int result) {
Log.e(LOG_TAG, "acknowledgeSms() not implemented.");
}
@@ -191,12 +193,13 @@ public class ImsSmsImplBase {
*
* @param token token provided in {@link #onSmsStatusReportReceived(int, int, String, byte[])}
* or {@link #onSmsStatusReportReceived(int, String, byte[])}
- * @param messageRef the message reference
- * @param result result of delivering the message. Valid values are:
- * {@link #STATUS_REPORT_STATUS_OK},
- * {@link #STATUS_REPORT_STATUS_ERROR}
+ * @param messageRef the message reference, which may be 1 byte if it is in
+ * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+ * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+ * @param result result of delivering the message.
*/
- public void acknowledgeSmsReport(int token, int messageRef, @StatusReportResult int result) {
+ public void acknowledgeSmsReport(int token, @IntRange(from = 0, to = 65535) int messageRef,
+ @StatusReportResult int result) {
Log.e(LOG_TAG, "acknowledgeSmsReport() not implemented.");
}
@@ -210,12 +213,12 @@ public class ImsSmsImplBase {
* {@link #DELIVER_STATUS_ERROR_GENERIC} result code.
* @param token unique token generated by IMS providers that the platform will use to trigger
* callbacks for this message.
- * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
- * {@link SmsMessage#FORMAT_3GPP2}.
+ * @param format the format of the message.
* @param pdu PDU representing the contents of the message.
* @throws RuntimeException if called before {@link #onReady()} is triggered.
*/
- public final void onSmsReceived(int token, String format, byte[] pdu) throws RuntimeException {
+ public final void onSmsReceived(int token, @SmsMessage.Format String format, byte[] pdu)
+ throws RuntimeException {
synchronized (mLock) {
if (mListener == null) {
throw new RuntimeException("Feature not ready.");
@@ -241,13 +244,16 @@ public class ImsSmsImplBase {
* sent successfully.
*
* @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
- * @param messageRef the message reference. Should be between 0 and 255 per TS.123.040
+ * @param messageRef the message reference, which may be 1 byte if it is in
+ * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+ * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
*
* @throws RuntimeException if called before {@link #onReady()} is triggered or if the
* connection to the framework is not available. If this happens attempting to send the SMS
* should be aborted.
*/
- public final void onSendSmsResultSuccess(int token, int messageRef) throws RuntimeException {
+ public final void onSendSmsResultSuccess(int token,
+ @IntRange(from = 0, to = 65535) int messageRef) throws RuntimeException {
synchronized (mLock) {
if (mListener == null) {
throw new RuntimeException("Feature not ready.");
@@ -266,34 +272,11 @@ public class ImsSmsImplBase {
* to the platform.
*
* @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
- * @param messageRef the message reference. Should be between 0 and 255 per TS.123.040
+ * @param messageRef the message reference, which may be 1 byte if it is in
+ * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+ * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
* @param status result of sending the SMS.
- * @param reason reason in case status is failure. Valid values are:
- * {@link SmsManager#RESULT_ERROR_NONE},
- * {@link SmsManager#RESULT_ERROR_GENERIC_FAILURE},
- * {@link SmsManager#RESULT_ERROR_RADIO_OFF},
- * {@link SmsManager#RESULT_ERROR_NULL_PDU},
- * {@link SmsManager#RESULT_ERROR_NO_SERVICE},
- * {@link SmsManager#RESULT_ERROR_LIMIT_EXCEEDED},
- * {@link SmsManager#RESULT_ERROR_FDN_CHECK_FAILURE},
- * {@link SmsManager#RESULT_ERROR_SHORT_CODE_NOT_ALLOWED},
- * {@link SmsManager#RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED},
- * {@link SmsManager#RESULT_RADIO_NOT_AVAILABLE},
- * {@link SmsManager#RESULT_NETWORK_REJECT},
- * {@link SmsManager#RESULT_INVALID_ARGUMENTS},
- * {@link SmsManager#RESULT_INVALID_STATE},
- * {@link SmsManager#RESULT_NO_MEMORY},
- * {@link SmsManager#RESULT_INVALID_SMS_FORMAT},
- * {@link SmsManager#RESULT_SYSTEM_ERROR},
- * {@link SmsManager#RESULT_MODEM_ERROR},
- * {@link SmsManager#RESULT_NETWORK_ERROR},
- * {@link SmsManager#RESULT_ENCODING_ERROR},
- * {@link SmsManager#RESULT_INVALID_SMSC_ADDRESS},
- * {@link SmsManager#RESULT_OPERATION_NOT_ALLOWED},
- * {@link SmsManager#RESULT_INTERNAL_ERROR},
- * {@link SmsManager#RESULT_NO_RESOURCES},
- * {@link SmsManager#RESULT_CANCELLED},
- * {@link SmsManager#RESULT_REQUEST_NOT_SUPPORTED}
+ * @param reason reason in case status is failure.
*
* @throws RuntimeException if called before {@link #onReady()} is triggered or if the
* connection to the framework is not available. If this happens attempting to send the SMS
@@ -303,8 +286,8 @@ public class ImsSmsImplBase {
* send result.
*/
@Deprecated
- public final void onSendSmsResult(int token, int messageRef, @SendStatusResult int status,
- int reason) throws RuntimeException {
+ public final void onSendSmsResult(int token, @IntRange(from = 0, to = 65535) int messageRef,
+ @SendStatusResult int status, @SmsManager.Result int reason) throws RuntimeException {
synchronized (mLock) {
if (mListener == null) {
throw new RuntimeException("Feature not ready.");
@@ -324,34 +307,10 @@ public class ImsSmsImplBase {
* network.
*
* @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
- * @param messageRef the message reference. Should be between 0 and 255 per TS.123.040
+ * @param messageRef the message reference, which may be 1 byte if it is in
+ * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in
+ * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
* @param status result of sending the SMS.
- * @param reason Valid values are:
- * {@link SmsManager#RESULT_ERROR_NONE},
- * {@link SmsManager#RESULT_ERROR_GENERIC_FAILURE},
- * {@link SmsManager#RESULT_ERROR_RADIO_OFF},
- * {@link SmsManager#RESULT_ERROR_NULL_PDU},
- * {@link SmsManager#RESULT_ERROR_NO_SERVICE},
- * {@link SmsManager#RESULT_ERROR_LIMIT_EXCEEDED},
- * {@link SmsManager#RESULT_ERROR_FDN_CHECK_FAILURE},
- * {@link SmsManager#RESULT_ERROR_SHORT_CODE_NOT_ALLOWED},
- * {@link SmsManager#RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED},
- * {@link SmsManager#RESULT_RADIO_NOT_AVAILABLE},
- * {@link SmsManager#RESULT_NETWORK_REJECT},
- * {@link SmsManager#RESULT_INVALID_ARGUMENTS},
- * {@link SmsManager#RESULT_INVALID_STATE},
- * {@link SmsManager#RESULT_NO_MEMORY},
- * {@link SmsManager#RESULT_INVALID_SMS_FORMAT},
- * {@link SmsManager#RESULT_SYSTEM_ERROR},
- * {@link SmsManager#RESULT_MODEM_ERROR},
- * {@link SmsManager#RESULT_NETWORK_ERROR},
- * {@link SmsManager#RESULT_ENCODING_ERROR},
- * {@link SmsManager#RESULT_INVALID_SMSC_ADDRESS},
- * {@link SmsManager#RESULT_OPERATION_NOT_ALLOWED},
- * {@link SmsManager#RESULT_INTERNAL_ERROR},
- * {@link SmsManager#RESULT_NO_RESOURCES},
- * {@link SmsManager#RESULT_CANCELLED},
- * {@link SmsManager#RESULT_REQUEST_NOT_SUPPORTED}
* @param networkErrorCode the error code reported by the carrier network if sending this SMS
* has resulted in an error or {@link #RESULT_NO_NETWORK_ERROR} if no network error was
* generated. See 3GPP TS 24.011 Section 7.3.4 for valid error codes and more information.
@@ -360,9 +319,9 @@ public class ImsSmsImplBase {
* connection to the framework is not available. If this happens attempting to send the SMS
* should be aborted.
*/
- public final void onSendSmsResultError(int token, int messageRef, @SendStatusResult int status,
- int reason, int networkErrorCode)
- throws RuntimeException {
+ public final void onSendSmsResultError(int token,
+ @IntRange(from = 0, to = 65535) int messageRef, @SendStatusResult int status,
+ @SmsManager.Result int reason, int networkErrorCode) throws RuntimeException {
synchronized (mLock) {
if (mListener == null) {
throw new RuntimeException("Feature not ready.");
@@ -384,9 +343,10 @@ public class ImsSmsImplBase {
* the platform is not available, {@link #acknowledgeSmsReport(int, int, int)} will be called
* with the {@link #STATUS_REPORT_STATUS_ERROR} result code.
* @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
- * @param messageRef the message reference.
- * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
- * {@link SmsMessage#FORMAT_3GPP2}.
+ * @param messageRef the message reference, which may be 1 byte if it is in
+ * {@link SmsMessage#FORMAT_3GPP} format or 2 bytes if it is in
+ * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B).
+ * @param format the format of the message.
* @param pdu PDU representing the content of the status report.
* @throws RuntimeException if called before {@link #onReady()} is triggered
*
@@ -394,7 +354,8 @@ public class ImsSmsImplBase {
* message reference.
*/
@Deprecated
- public final void onSmsStatusReportReceived(int token, int messageRef, String format,
+ public final void onSmsStatusReportReceived(int token,
+ @IntRange(from = 0, to = 65535) int messageRef, @SmsMessage.Format String format,
byte[] pdu) throws RuntimeException {
synchronized (mLock) {
if (mListener == null) {
@@ -419,13 +380,12 @@ public class ImsSmsImplBase {
* with the {@link #STATUS_REPORT_STATUS_ERROR} result code.
* @param token unique token generated by IMS providers that the platform will use to trigger
* callbacks for this message.
- * @param format the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
- * {@link SmsMessage#FORMAT_3GPP2}.
+ * @param format the format of the message.
* @param pdu PDU representing the content of the status report.
* @throws RuntimeException if called before {@link #onReady()} is triggered
*/
- public final void onSmsStatusReportReceived(int token, String format, byte[] pdu)
- throws RuntimeException {
+ public final void onSmsStatusReportReceived(int token, @SmsMessage.Format String format,
+ byte[] pdu) throws RuntimeException {
synchronized (mLock) {
if (mListener == null) {
throw new RuntimeException("Feature not ready.");
@@ -450,13 +410,11 @@ public class ImsSmsImplBase {
}
/**
- * Returns the SMS format. Default is {@link SmsMessage#FORMAT_3GPP} unless overridden by IMS
- * Provider.
+ * Returns the SMS format that the ImsService expects.
*
- * @return the format of the message. Valid values are {@link SmsMessage#FORMAT_3GPP} and
- * {@link SmsMessage#FORMAT_3GPP2}.
+ * @return The expected format of the SMS messages.
*/
- public String getSmsFormat() {
+ public @SmsMessage.Format String getSmsFormat() {
return SmsMessage.FORMAT_3GPP;
}
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 668a6af08145..9e786ce32792 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -111,7 +111,7 @@ public class DctConstants {
public static final int EVENT_DATA_SERVICE_BINDING_CHANGED = BASE + 49;
public static final int EVENT_DEVICE_PROVISIONED_CHANGE = BASE + 50;
public static final int EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED = BASE + 51;
- public static final int EVENT_5G_NETWORK_CHANGED = BASE + 52;
+ public static final int EVENT_SERVICE_STATE_CHANGED = BASE + 52;
public static final int EVENT_5G_TIMER_HYSTERESIS = BASE + 53;
public static final int EVENT_5G_TIMER_WATCHDOG = BASE + 54;
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
deleted file mode 100644
index 90019eef62fd..000000000000
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ /dev/null
@@ -1,66 +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.internal.telephony;
-
-import android.os.Bundle;
-import android.telephony.CallAttributes;
-import android.telephony.CellInfo;
-import android.telephony.DataConnectionRealTimeInfo;
-import android.telephony.PhoneCapability;
-import android.telephony.PhysicalChannelConfig;
-import android.telephony.PreciseCallState;
-import android.telephony.PreciseDataConnectionState;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
-import android.telephony.emergency.EmergencyNumber;
-import android.telephony.ims.ImsReasonInfo;
-
-oneway interface IPhoneStateListener {
- void onServiceStateChanged(in ServiceState serviceState);
- void onSignalStrengthChanged(int asu);
- void onMessageWaitingIndicatorChanged(boolean mwi);
- void onCallForwardingIndicatorChanged(boolean cfi);
-
- // we use bundle here instead of CellLocation so it can get the right subclass
- void onCellLocationChanged(in Bundle location);
- void onCallStateChanged(int state, String incomingNumber);
- void onDataConnectionStateChanged(int state, int networkType);
- void onDataActivity(int direction);
- void onSignalStrengthsChanged(in SignalStrength signalStrength);
- void onPhysicalChannelConfigurationChanged(in List<PhysicalChannelConfig> configs);
- void onOtaspChanged(in int otaspMode);
- void onCellInfoChanged(in List<CellInfo> cellInfo);
- void onPreciseCallStateChanged(in PreciseCallState callState);
- void onPreciseDataConnectionStateChanged(in PreciseDataConnectionState dataConnectionState);
- void onDataConnectionRealTimeInfoChanged(in DataConnectionRealTimeInfo dcRtInfo);
- void onSrvccStateChanged(in int state);
- void onVoiceActivationStateChanged(int activationState);
- void onDataActivationStateChanged(int activationState);
- void onOemHookRawEvent(in byte[] rawData);
- void onCarrierNetworkChange(in boolean active);
- void onUserMobileDataStateChanged(in boolean enabled);
- void onPhoneCapabilityChanged(in PhoneCapability capability);
- void onActiveDataSubIdChanged(in int subId);
- void onRadioPowerStateChanged(in int state);
- void onCallAttributesChanged(in CallAttributes callAttributes);
- void onEmergencyNumberListChanged(in Map emergencyNumberList);
- void onOutgoingEmergencyCall(in EmergencyNumber placedEmergencyNumber);
- void onOutgoingEmergencySms(in EmergencyNumber sentEmergencyNumber);
- void onCallDisconnectCauseChanged(in int disconnectCause, in int preciseDisconnectCause);
- void onImsCallDisconnectCauseChanged(in ImsReasonInfo imsReasonInfo);
-}
-
diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl
index 21570a6ef708..6289c0eadc41 100644
--- a/telephony/java/com/android/internal/telephony/ISms.aidl
+++ b/telephony/java/com/android/internal/telephony/ISms.aidl
@@ -19,7 +19,6 @@ package com.android.internal.telephony;
import android.app.PendingIntent;
import android.net.Uri;
import android.os.Bundle;
-import android.telephony.IFinancialSmsCallback;
import com.android.internal.telephony.SmsRawData;
/**
@@ -570,17 +569,6 @@ interface ISms {
int subId, String callingPkg, String prefixes, in PendingIntent intent);
/**
- * Get sms inbox messages for the calling financial app.
- *
- * @param subId the SIM id.
- * @param callingPkg the package name of the calling app.
- * @param params parameters to filter the sms messages.
- * @param callback the callback interface to deliver the result.
- */
- void getSmsMessagesForFinancialApp(
- int subId, String callingPkg, in Bundle params, in IFinancialSmsCallback callback);
-
- /**
* Get the capacity count of sms on Icc card.
*
* @param subId for subId which getSmsCapacityOnIcc is queried.
diff --git a/telephony/java/com/android/internal/telephony/ISmsImplBase.java b/telephony/java/com/android/internal/telephony/ISmsImplBase.java
index 6a717d71572f..b7ec9fcef11a 100644
--- a/telephony/java/com/android/internal/telephony/ISmsImplBase.java
+++ b/telephony/java/com/android/internal/telephony/ISmsImplBase.java
@@ -19,7 +19,6 @@ package com.android.internal.telephony;
import android.app.PendingIntent;
import android.net.Uri;
import android.os.Bundle;
-import android.telephony.IFinancialSmsCallback;
import java.util.List;
@@ -198,12 +197,6 @@ public class ISmsImplBase extends ISms.Stub {
}
@Override
- public void getSmsMessagesForFinancialApp(
- int subId, String callingPkg, Bundle params, IFinancialSmsCallback callback) {
- throw new UnsupportedOperationException();
- }
-
- @Override
public int getSmsCapacityOnIccForSubscriber(int subId) {
throw new UnsupportedOperationException();
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 866e936a1211..f79a5c654a63 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -302,7 +302,7 @@ interface ITelephony {
* operator's MCC (Mobile Country Code).
* @see android.telephony.TelephonyManager#getNetworkCountryIso
*/
- String getNetworkCountryIsoForPhone(int phoneId);
+ String getNetworkCountryIsoForPhone(int phoneId, String callingPkg);
/**
* Returns the neighboring cell information of the device.
@@ -1612,6 +1612,18 @@ interface ITelephony {
String[] getForbiddenPlmns(int subId, int appType, String callingPackage);
/**
+ * Set the forbidden PLMN list from the givven app type (ex APPTYPE_USIM) on a particular
+ * subscription.
+ *
+ * @param subId subId the id of the subscription
+ * @param appType appType the uicc app type, must be USIM or SIM.
+ * @param fplmns plmns the Forbiden plmns list that needed to be written to the SIM.
+ * @param content callingPackage the op Package name.
+ * @return number of fplmns that is successfully written to the SIM
+ */
+ int setForbiddenPlmns(int subId, int appType, in List<String> fplmns, String callingPackage);
+
+ /**
* Check if phone is in emergency callback mode
* @return true if phone is in emergency callback mode
* @param subId the subscription ID that this action applies to.
@@ -1766,21 +1778,6 @@ interface ITelephony {
boolean isInEmergencySmsMode();
/**
- * Get a list of SMS apps on a user.
- */
- String[] getSmsApps(int userId);
-
- /**
- * Get the default SMS app on a given user.
- */
- String getDefaultSmsApp(int userId);
-
- /**
- * Set the default SMS app to a given package on a given user.
- */
- void setDefaultSmsApp(int userId, String packageName);
-
- /**
* Return the modem radio power state for slot index.
*
*/
@@ -2051,4 +2048,9 @@ interface ITelephony {
* data might be disabled on non-default data subscription but explicitly turned on by settings.
*/
boolean isDataAllowedInVoiceCall(int subId);
+
+ /**
+ * Command line command to enable or disable handling of CEP data for test purposes.
+ */
+ oneway void setCepEnabled(boolean isCepEnabled);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
deleted file mode 100644
index 98fee83d83a9..000000000000
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ /dev/null
@@ -1,99 +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.internal.telephony;
-
-import android.content.Intent;
-import android.net.LinkProperties;
-import android.net.NetworkCapabilities;
-import android.os.Bundle;
-import android.telephony.CallQuality;
-import android.telephony.CellInfo;
-import android.telephony.ims.ImsReasonInfo;
-import android.telephony.PhoneCapability;
-import android.telephony.PhysicalChannelConfig;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
-import android.telephony.emergency.EmergencyNumber;
-import com.android.internal.telephony.IPhoneStateListener;
-import com.android.internal.telephony.IOnSubscriptionsChangedListener;
-
-interface ITelephonyRegistry {
- void addOnSubscriptionsChangedListener(String pkg,
- IOnSubscriptionsChangedListener callback);
- void addOnOpportunisticSubscriptionsChangedListener(String pkg,
- IOnSubscriptionsChangedListener callback);
- void removeOnSubscriptionsChangedListener(String pkg,
- IOnSubscriptionsChangedListener callback);
- @UnsupportedAppUsage
- void listen(String pkg, IPhoneStateListener callback, int events, boolean notifyNow);
- void listenForSubscriber(in int subId, String pkg, IPhoneStateListener callback, int events,
- boolean notifyNow);
- @UnsupportedAppUsage
- void notifyCallState(int state, String incomingNumber);
- void notifyCallStateForPhoneId(in int phoneId, in int subId, int state, String incomingNumber);
- void notifyServiceStateForPhoneId(in int phoneId, in int subId, in ServiceState state);
- void notifySignalStrengthForPhoneId(in int phoneId, in int subId,
- in SignalStrength signalStrength);
- void notifyMessageWaitingChangedForPhoneId(in int phoneId, in int subId, in boolean mwi);
- @UnsupportedAppUsage(maxTargetSdk = 28)
- void notifyCallForwardingChanged(boolean cfi);
- void notifyCallForwardingChangedForSubscriber(in int subId, boolean cfi);
- @UnsupportedAppUsage(maxTargetSdk = 28)
- void notifyDataActivity(int state);
- void notifyDataActivityForSubscriber(in int subId, int state);
- void notifyDataConnection(int state, boolean isDataConnectivityPossible,
- String apn, String apnType, in LinkProperties linkProperties,
- in NetworkCapabilities networkCapabilities, int networkType, boolean roaming);
- void notifyDataConnectionForSubscriber(int phoneId, int subId, int state,
- boolean isDataConnectivityPossible,
- String apn, String apnType, in LinkProperties linkProperties,
- in NetworkCapabilities networkCapabilities, int networkType, boolean roaming);
- @UnsupportedAppUsage
- void notifyDataConnectionFailed(String apnType);
- void notifyDataConnectionFailedForSubscriber(int phoneId, int subId, String apnType);
- @UnsupportedAppUsage(maxTargetSdk = 28)
- void notifyCellLocation(in Bundle cellLocation);
- void notifyCellLocationForSubscriber(in int subId, in Bundle cellLocation);
- @UnsupportedAppUsage(maxTargetSdk = 28)
- void notifyOtaspChanged(in int subId, in int otaspMode);
- @UnsupportedAppUsage
- void notifyCellInfo(in List<CellInfo> cellInfo);
- void notifyPhysicalChannelConfigurationForSubscriber(in int phoneId, in int subId,
- in List<PhysicalChannelConfig> configs);
- void notifyPreciseCallState(int phoneId, int subId, int ringingCallState,
- int foregroundCallState, int backgroundCallState);
- void notifyDisconnectCause(int phoneId, int subId, int disconnectCause,
- int preciseDisconnectCause);
- void notifyPreciseDataConnectionFailed(int phoneId, int subId, String apnType, String apn,
- int failCause);
- void notifyCellInfoForSubscriber(in int subId, in List<CellInfo> cellInfo);
- void notifySrvccStateChanged(in int subId, in int lteState);
- void notifySimActivationStateChangedForPhoneId(in int phoneId, in int subId,
- int activationState, int activationType);
- void notifyOemHookRawEventForSubscriber(in int phoneId, in int subId, in byte[] rawData);
- void notifySubscriptionInfoChanged();
- void notifyOpportunisticSubscriptionInfoChanged();
- void notifyCarrierNetworkChange(in boolean active);
- void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state);
- void notifyPhoneCapabilityChanged(in PhoneCapability capability);
- void notifyActiveDataSubIdChanged(int activeDataSubId);
- void notifyRadioPowerStateChanged(in int phoneId, in int subId, in int state);
- void notifyEmergencyNumberList(in int phoneId, in int subId);
- void notifyCallQualityChanged(in CallQuality callQuality, int phoneId, int subId,
- int callNetworkType);
- void notifyImsDisconnectCause(int subId, in ImsReasonInfo imsReasonInfo);
-}
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index ee1a4766bf68..c19ae7b3916a 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -89,10 +89,6 @@ public class PhoneConstants {
@UnsupportedAppUsage
public static final int PRESENTATION_PAYPHONE = 4; // show pay phone info
- // Sim activation type
- public static final int SIM_ACTIVATION_TYPE_VOICE = 0;
- public static final int SIM_ACTIVATION_TYPE_DATA = 1;
-
public static final String PHONE_NAME_KEY = "phoneName";
public static final String DATA_NETWORK_TYPE_KEY = "networkType";
public static final String DATA_FAILURE_CAUSE_KEY = "failCause";
@@ -174,7 +170,7 @@ public class PhoneConstants {
public static final int RIL_CARD_MAX_APPS = 8;
- public static final int DEFAULT_CARD_INDEX = 0;
+ public static final int DEFAULT_SLOT_INDEX = 0;
public static final int MAX_PHONE_COUNT_SINGLE_SIM = 1;
diff --git a/telephony/java/com/android/internal/telephony/SmsApplication.java b/telephony/java/com/android/internal/telephony/SmsApplication.java
deleted file mode 100644
index f10398f09603..000000000000
--- a/telephony/java/com/android/internal/telephony/SmsApplication.java
+++ /dev/null
@@ -1,1081 +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.internal.telephony;
-
-import android.Manifest.permission;
-import android.annotation.Nullable;
-import android.app.AppOpsManager;
-import android.app.role.RoleManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Binder;
-import android.os.Debug;
-import android.os.Process;
-import android.os.UserHandle;
-import android.provider.Telephony;
-import android.provider.Telephony.Sms.Intents;
-import android.telephony.Rlog;
-import android.telephony.SmsManager;
-import android.telephony.TelephonyManager;
-import android.util.Log;
-
-import com.android.internal.content.PackageMonitor;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.function.Consumer;
-
-/**
- * Class for managing the primary application that we will deliver SMS/MMS messages to
- *
- * {@hide}
- */
-public final class SmsApplication {
- static final String LOG_TAG = "SmsApplication";
- private static final String PHONE_PACKAGE_NAME = "com.android.phone";
- private static final String BLUETOOTH_PACKAGE_NAME = "com.android.bluetooth";
- private static final String MMS_SERVICE_PACKAGE_NAME = "com.android.mms.service";
- private static final String TELEPHONY_PROVIDER_PACKAGE_NAME = "com.android.providers.telephony";
-
- private static final String SCHEME_SMS = "sms";
- private static final String SCHEME_SMSTO = "smsto";
- private static final String SCHEME_MMS = "mms";
- private static final String SCHEME_MMSTO = "mmsto";
- private static final boolean DEBUG = false;
- private static final boolean DEBUG_MULTIUSER = false;
-
- private static final int[] DEFAULT_APP_EXCLUSIVE_APPOPS = {
- AppOpsManager.OP_READ_SMS,
- AppOpsManager.OP_WRITE_SMS,
- AppOpsManager.OP_RECEIVE_SMS,
- AppOpsManager.OP_RECEIVE_WAP_PUSH,
- AppOpsManager.OP_SEND_SMS,
- AppOpsManager.OP_READ_CELL_BROADCASTS
- };
-
- private static SmsPackageMonitor sSmsPackageMonitor = null;
-
- public static class SmsApplicationData {
- /**
- * Name of this SMS app for display.
- */
- @UnsupportedAppUsage
- private String mApplicationName;
-
- /**
- * Package name for this SMS app.
- */
- public String mPackageName;
-
- /**
- * The class name of the SMS_DELIVER_ACTION receiver in this app.
- */
- private String mSmsReceiverClass;
-
- /**
- * The class name of the WAP_PUSH_DELIVER_ACTION receiver in this app.
- */
- private String mMmsReceiverClass;
-
- /**
- * The class name of the ACTION_RESPOND_VIA_MESSAGE intent in this app.
- */
- private String mRespondViaMessageClass;
-
- /**
- * The class name of the ACTION_SENDTO intent in this app.
- */
- private String mSendToClass;
-
- /**
- * The class name of the ACTION_DEFAULT_SMS_PACKAGE_CHANGED receiver in this app.
- */
- private String mSmsAppChangedReceiverClass;
-
- /**
- * The class name of the ACTION_EXTERNAL_PROVIDER_CHANGE receiver in this app.
- */
- private String mProviderChangedReceiverClass;
-
- /**
- * The class name of the SIM_FULL_ACTION receiver in this app.
- */
- private String mSimFullReceiverClass;
-
- /**
- * The user-id for this application
- */
- private int mUid;
-
- /**
- * Returns true if this SmsApplicationData is complete (all intents handled).
- * @return
- */
- public boolean isComplete() {
- return (mSmsReceiverClass != null && mMmsReceiverClass != null
- && mRespondViaMessageClass != null && mSendToClass != null);
- }
-
- public SmsApplicationData(String packageName, int uid) {
- mPackageName = packageName;
- mUid = uid;
- }
-
- public String getApplicationName(Context context) {
- if (mApplicationName == null) {
- PackageManager pm = context.getPackageManager();
- ApplicationInfo appInfo;
- try {
- appInfo = pm.getApplicationInfoAsUser(mPackageName, 0,
- UserHandle.getUserHandleForUid(mUid));
- } catch (NameNotFoundException e) {
- return null;
- }
- if (appInfo != null) {
- CharSequence label = pm.getApplicationLabel(appInfo);
- mApplicationName = (label == null) ? null : label.toString();
- }
- }
- return mApplicationName;
- }
-
- @Override
- public String toString() {
- return " mPackageName: " + mPackageName
- + " mSmsReceiverClass: " + mSmsReceiverClass
- + " mMmsReceiverClass: " + mMmsReceiverClass
- + " mRespondViaMessageClass: " + mRespondViaMessageClass
- + " mSendToClass: " + mSendToClass
- + " mSmsAppChangedClass: " + mSmsAppChangedReceiverClass
- + " mProviderChangedReceiverClass: " + mProviderChangedReceiverClass
- + " mSimFullReceiverClass: " + mSimFullReceiverClass
- + " mUid: " + mUid;
- }
- }
-
- /**
- * Returns the userId of the Context object, if called from a system app,
- * otherwise it returns the caller's userId
- * @param context The context object passed in by the caller.
- * @return
- */
- private static int getIncomingUserId(Context context) {
- int contextUserId = UserHandle.myUserId();
- final int callingUid = Binder.getCallingUid();
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "getIncomingUserHandle caller=" + callingUid + ", myuid="
- + android.os.Process.myUid() + "\n\t" + Debug.getCallers(4));
- }
- if (UserHandle.getAppId(callingUid)
- < android.os.Process.FIRST_APPLICATION_UID) {
- return contextUserId;
- } else {
- return UserHandle.getUserId(callingUid);
- }
- }
-
- /**
- * Returns the list of available SMS apps defined as apps that are registered for both the
- * SMS_RECEIVED_ACTION (SMS) and WAP_PUSH_RECEIVED_ACTION (MMS) broadcasts (and their broadcast
- * receivers are enabled)
- *
- * Requirements to be an SMS application:
- * Implement SMS_DELIVER_ACTION broadcast receiver.
- * Require BROADCAST_SMS permission.
- *
- * Implement WAP_PUSH_DELIVER_ACTION broadcast receiver.
- * Require BROADCAST_WAP_PUSH permission.
- *
- * Implement RESPOND_VIA_MESSAGE intent.
- * Support smsto Uri scheme.
- * Require SEND_RESPOND_VIA_MESSAGE permission.
- *
- * Implement ACTION_SENDTO intent.
- * Support smsto Uri scheme.
- */
- @UnsupportedAppUsage
- public static Collection<SmsApplicationData> getApplicationCollection(Context context) {
- return getApplicationCollectionAsUser(context, getIncomingUserId(context));
- }
-
- /**
- * Same as {@link #getApplicationCollection} but it takes a target user ID.
- */
- public static Collection<SmsApplicationData> getApplicationCollectionAsUser(Context context,
- int userId) {
- final long token = Binder.clearCallingIdentity();
- try {
- return getApplicationCollectionInternal(context, userId);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- private static Collection<SmsApplicationData> getApplicationCollectionInternal(
- Context context, int userId) {
- PackageManager packageManager = context.getPackageManager();
-
- // Get the list of apps registered for SMS
- Intent intent = new Intent(Intents.SMS_DELIVER_ACTION);
- if (DEBUG) {
- intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
- }
- List<ResolveInfo> smsReceivers = packageManager.queryBroadcastReceiversAsUser(intent,
- PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- userId);
-
- HashMap<String, SmsApplicationData> receivers = new HashMap<String, SmsApplicationData>();
-
- // Add one entry to the map for every sms receiver (ignoring duplicate sms receivers)
- for (ResolveInfo resolveInfo : smsReceivers) {
- final ActivityInfo activityInfo = resolveInfo.activityInfo;
- if (activityInfo == null) {
- continue;
- }
- if (!permission.BROADCAST_SMS.equals(activityInfo.permission)) {
- continue;
- }
- final String packageName = activityInfo.packageName;
- if (!receivers.containsKey(packageName)) {
- final SmsApplicationData smsApplicationData = new SmsApplicationData(packageName,
- activityInfo.applicationInfo.uid);
- smsApplicationData.mSmsReceiverClass = activityInfo.name;
- receivers.put(packageName, smsApplicationData);
- }
- }
-
- // Update any existing entries with mms receiver class
- intent = new Intent(Intents.WAP_PUSH_DELIVER_ACTION);
- intent.setDataAndType(null, "application/vnd.wap.mms-message");
- List<ResolveInfo> mmsReceivers = packageManager.queryBroadcastReceiversAsUser(intent,
- PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- userId);
- for (ResolveInfo resolveInfo : mmsReceivers) {
- final ActivityInfo activityInfo = resolveInfo.activityInfo;
- if (activityInfo == null) {
- continue;
- }
- if (!permission.BROADCAST_WAP_PUSH.equals(activityInfo.permission)) {
- continue;
- }
- final String packageName = activityInfo.packageName;
- final SmsApplicationData smsApplicationData = receivers.get(packageName);
- if (smsApplicationData != null) {
- smsApplicationData.mMmsReceiverClass = activityInfo.name;
- }
- }
-
- // Update any existing entries with respond via message intent class.
- intent = new Intent(TelephonyManager.ACTION_RESPOND_VIA_MESSAGE,
- Uri.fromParts(SCHEME_SMSTO, "", null));
- List<ResolveInfo> respondServices = packageManager.queryIntentServicesAsUser(intent,
- PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- UserHandle.of(userId));
- for (ResolveInfo resolveInfo : respondServices) {
- final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
- if (serviceInfo == null) {
- continue;
- }
- if (!permission.SEND_RESPOND_VIA_MESSAGE.equals(serviceInfo.permission)) {
- continue;
- }
- final String packageName = serviceInfo.packageName;
- final SmsApplicationData smsApplicationData = receivers.get(packageName);
- if (smsApplicationData != null) {
- smsApplicationData.mRespondViaMessageClass = serviceInfo.name;
- }
- }
-
- // Update any existing entries with supports send to.
- intent = new Intent(Intent.ACTION_SENDTO,
- Uri.fromParts(SCHEME_SMSTO, "", null));
- List<ResolveInfo> sendToActivities = packageManager.queryIntentActivitiesAsUser(intent,
- PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- userId);
- for (ResolveInfo resolveInfo : sendToActivities) {
- final ActivityInfo activityInfo = resolveInfo.activityInfo;
- if (activityInfo == null) {
- continue;
- }
- final String packageName = activityInfo.packageName;
- final SmsApplicationData smsApplicationData = receivers.get(packageName);
- if (smsApplicationData != null) {
- smsApplicationData.mSendToClass = activityInfo.name;
- }
- }
-
- // Update any existing entries with the default sms changed handler.
- intent = new Intent(Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED);
- List<ResolveInfo> smsAppChangedReceivers =
- packageManager.queryBroadcastReceiversAsUser(intent,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "getApplicationCollectionInternal smsAppChangedActivities=" +
- smsAppChangedReceivers);
- }
- for (ResolveInfo resolveInfo : smsAppChangedReceivers) {
- final ActivityInfo activityInfo = resolveInfo.activityInfo;
- if (activityInfo == null) {
- continue;
- }
- final String packageName = activityInfo.packageName;
- final SmsApplicationData smsApplicationData = receivers.get(packageName);
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "getApplicationCollectionInternal packageName=" +
- packageName + " smsApplicationData: " + smsApplicationData +
- " activityInfo.name: " + activityInfo.name);
- }
- if (smsApplicationData != null) {
- smsApplicationData.mSmsAppChangedReceiverClass = activityInfo.name;
- }
- }
-
- // Update any existing entries with the external provider changed handler.
- intent = new Intent(Telephony.Sms.Intents.ACTION_EXTERNAL_PROVIDER_CHANGE);
- List<ResolveInfo> providerChangedReceivers =
- packageManager.queryBroadcastReceiversAsUser(intent,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "getApplicationCollectionInternal providerChangedActivities=" +
- providerChangedReceivers);
- }
- for (ResolveInfo resolveInfo : providerChangedReceivers) {
- final ActivityInfo activityInfo = resolveInfo.activityInfo;
- if (activityInfo == null) {
- continue;
- }
- final String packageName = activityInfo.packageName;
- final SmsApplicationData smsApplicationData = receivers.get(packageName);
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "getApplicationCollectionInternal packageName=" +
- packageName + " smsApplicationData: " + smsApplicationData +
- " activityInfo.name: " + activityInfo.name);
- }
- if (smsApplicationData != null) {
- smsApplicationData.mProviderChangedReceiverClass = activityInfo.name;
- }
- }
-
- // Update any existing entries with the sim full handler.
- intent = new Intent(Intents.SIM_FULL_ACTION);
- List<ResolveInfo> simFullReceivers =
- packageManager.queryBroadcastReceiversAsUser(intent,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "getApplicationCollectionInternal simFullReceivers="
- + simFullReceivers);
- }
- for (ResolveInfo resolveInfo : simFullReceivers) {
- final ActivityInfo activityInfo = resolveInfo.activityInfo;
- if (activityInfo == null) {
- continue;
- }
- final String packageName = activityInfo.packageName;
- final SmsApplicationData smsApplicationData = receivers.get(packageName);
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "getApplicationCollectionInternal packageName="
- + packageName + " smsApplicationData: " + smsApplicationData
- + " activityInfo.name: " + activityInfo.name);
- }
- if (smsApplicationData != null) {
- smsApplicationData.mSimFullReceiverClass = activityInfo.name;
- }
- }
-
- // Remove any entries for which we did not find all required intents.
- for (ResolveInfo resolveInfo : smsReceivers) {
- final ActivityInfo activityInfo = resolveInfo.activityInfo;
- if (activityInfo == null) {
- continue;
- }
- final String packageName = activityInfo.packageName;
- final SmsApplicationData smsApplicationData = receivers.get(packageName);
- if (smsApplicationData != null) {
- if (!smsApplicationData.isComplete()) {
- receivers.remove(packageName);
- }
- }
- }
- return receivers.values();
- }
-
- /**
- * Checks to see if we have a valid installed SMS application for the specified package name
- * @return Data for the specified package name or null if there isn't one
- */
- public static SmsApplicationData getApplicationForPackage(
- Collection<SmsApplicationData> applications, String packageName) {
- if (packageName == null) {
- return null;
- }
- // Is there an entry in the application list for the specified package?
- for (SmsApplicationData application : applications) {
- if (application.mPackageName.contentEquals(packageName)) {
- return application;
- }
- }
- return null;
- }
-
- /**
- * Get the application we will use for delivering SMS/MMS messages.
- *
- * We return the preferred sms application with the following order of preference:
- * (1) User selected SMS app (if selected, and if still valid)
- * (2) Android Messaging (if installed)
- * (3) The currently configured highest priority broadcast receiver
- * (4) Null
- */
- private static SmsApplicationData getApplication(Context context, boolean updateIfNeeded,
- int userId) {
- TelephonyManager tm = (TelephonyManager)
- context.getSystemService(Context.TELEPHONY_SERVICE);
- RoleManager roleManager = (RoleManager) context.getSystemService(Context.ROLE_SERVICE);
- // (b/134400042) RoleManager might be null in unit tests running older mockito versions
- // that do not support mocking final classes.
- if (!tm.isSmsCapable() && (roleManager == null || !roleManager.isRoleAvailable(
- RoleManager.ROLE_SMS))) {
- // No phone, no SMS
- return null;
- }
-
- Collection<SmsApplicationData> applications = getApplicationCollectionInternal(context,
- userId);
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "getApplication userId=" + userId);
- }
- // Determine which application receives the broadcast
- String defaultApplication = getDefaultSmsPackage(context, userId);
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "getApplication defaultApp=" + defaultApplication);
- }
-
- SmsApplicationData applicationData = null;
- if (defaultApplication != null) {
- applicationData = getApplicationForPackage(applications, defaultApplication);
- }
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "getApplication appData=" + applicationData);
- }
-
- // If we found a package, make sure AppOps permissions are set up correctly
- if (applicationData != null) {
- // We can only call checkOp if we are privileged (updateIfNeeded) or if the app we
- // are checking is for our current uid. Doing this check from the unprivileged current
- // SMS app allows us to tell the current SMS app that it is not in a good state and
- // needs to ask to be the current SMS app again to work properly.
- if (updateIfNeeded || applicationData.mUid == android.os.Process.myUid()) {
- // Verify that the SMS app has permissions
- boolean appOpsFixed =
- tryFixExclusiveSmsAppops(context, applicationData, updateIfNeeded);
- if (!appOpsFixed) {
- // We can not return a package if permissions are not set up correctly
- applicationData = null;
- }
- }
-
- // We can only verify the phone and BT app's permissions from a privileged caller
- if (applicationData != null && updateIfNeeded) {
- // Ensure this component is still configured as the preferred activity. Usually the
- // current SMS app will already be the preferred activity - but checking whether or
- // not this is true is just as expensive as reconfiguring the preferred activity so
- // we just reconfigure every time.
- defaultSmsAppChanged(context);
- }
- }
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "getApplication returning appData=" + applicationData);
- }
- return applicationData;
- }
-
- private static String getDefaultSmsPackage(Context context, int userId) {
- return context.getSystemService(RoleManager.class).getDefaultSmsPackage(userId);
- }
-
- /**
- * Grants various permissions and appops on sms app change
- */
- private static void defaultSmsAppChanged(Context context) {
- PackageManager packageManager = context.getPackageManager();
- AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
-
- // Assign permission to special system apps
- assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps,
- PHONE_PACKAGE_NAME);
- assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps,
- BLUETOOTH_PACKAGE_NAME);
- assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps,
- MMS_SERVICE_PACKAGE_NAME);
- assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps,
- TELEPHONY_PROVIDER_PACKAGE_NAME);
-
- // Give AppOps permission to UID 1001 which contains multiple
- // apps, all of them should be able to write to telephony provider.
- // This is to allow the proxy package permission check in telephony provider
- // to pass.
- for (int appop : DEFAULT_APP_EXCLUSIVE_APPOPS) {
- appOps.setUidMode(appop, Process.PHONE_UID, AppOpsManager.MODE_ALLOWED);
- }
- }
-
- private static boolean tryFixExclusiveSmsAppops(Context context,
- SmsApplicationData applicationData, boolean updateIfNeeded) {
- AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
- for (int appOp : DEFAULT_APP_EXCLUSIVE_APPOPS) {
- int mode = appOps.checkOp(appOp, applicationData.mUid,
- applicationData.mPackageName);
- if (mode != AppOpsManager.MODE_ALLOWED) {
- Rlog.e(LOG_TAG, applicationData.mPackageName + " lost "
- + AppOpsManager.modeToName(appOp) + ": "
- + (updateIfNeeded ? " (fixing)" : " (no permission to fix)"));
- if (updateIfNeeded) {
- appOps.setUidMode(appOp, applicationData.mUid, AppOpsManager.MODE_ALLOWED);
- } else {
- return false;
- }
- }
- }
- return true;
- }
-
- /**
- * Sets the specified package as the default SMS/MMS application. The caller of this method
- * needs to have permission to set AppOps and write to secure settings.
- */
- @UnsupportedAppUsage
- public static void setDefaultApplication(String packageName, Context context) {
- setDefaultApplicationAsUser(packageName, context, getIncomingUserId(context));
- }
-
- /**
- * Same as {@link #setDefaultApplication} but takes a target user id.
- */
- public static void setDefaultApplicationAsUser(String packageName, Context context,
- int userId) {
- TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
- RoleManager roleManager = (RoleManager) context.getSystemService(Context.ROLE_SERVICE);
- // (b/134400042) RoleManager might be null in unit tests running older mockito versions
- // that do not support mocking final classes.
- if (!tm.isSmsCapable() && (roleManager == null || !roleManager.isRoleAvailable(
- RoleManager.ROLE_SMS))) {
- // No phone, no SMS
- return;
- }
-
- final long token = Binder.clearCallingIdentity();
- try {
- setDefaultApplicationInternal(packageName, context, userId);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- private static void setDefaultApplicationInternal(String packageName, Context context,
- int userId) {
- final UserHandle userHandle = UserHandle.of(userId);
-
- // Get old package name
- String oldPackageName = getDefaultSmsPackage(context, userId);
-
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "setDefaultApplicationInternal old=" + oldPackageName +
- " new=" + packageName);
- }
-
- if (packageName != null && oldPackageName != null && packageName.equals(oldPackageName)) {
- // No change
- return;
- }
-
- // We only make the change if the new package is valid
- PackageManager packageManager = context.getPackageManager();
- Collection<SmsApplicationData> applications = getApplicationCollectionInternal(
- context, userId);
- SmsApplicationData oldAppData = oldPackageName != null ?
- getApplicationForPackage(applications, oldPackageName) : null;
- SmsApplicationData applicationData = getApplicationForPackage(applications, packageName);
- if (applicationData != null) {
- // Ignore relevant appops for the previously configured default SMS app.
- AppOpsManager appOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
- if (oldPackageName != null) {
- try {
- int uid = packageManager.getPackageInfoAsUser(
- oldPackageName, 0, userId).applicationInfo.uid;
- setExclusiveAppops(oldPackageName, appOps, uid, AppOpsManager.MODE_DEFAULT);
- } catch (NameNotFoundException e) {
- Rlog.w(LOG_TAG, "Old SMS package not found: " + oldPackageName);
- }
- }
-
- // Update the setting.
- CompletableFuture<Void> future = new CompletableFuture<>();
- Consumer<Boolean> callback = successful -> {
- if (successful) {
- future.complete(null);
- } else {
- future.completeExceptionally(new RuntimeException());
- }
- };
- context.getSystemService(RoleManager.class).addRoleHolderAsUser(
- RoleManager.ROLE_SMS, applicationData.mPackageName, 0, UserHandle.of(userId),
- AsyncTask.THREAD_POOL_EXECUTOR, callback);
- try {
- future.get(5, TimeUnit.SECONDS);
- } catch (InterruptedException | ExecutionException | TimeoutException e) {
- Log.e(LOG_TAG, "Exception while adding sms role holder " + applicationData, e);
- return;
- }
-
- defaultSmsAppChanged(context);
- }
- }
-
- /**
- * Sends broadcasts on sms app change:
- * {@link Intent#ACTION_DEFAULT_SMS_PACKAGE_CHANGED}
- * {@link Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL}
- */
- public static void broadcastSmsAppChange(Context context,
- UserHandle userHandle, @Nullable String oldPackage, @Nullable String newPackage) {
- Collection<SmsApplicationData> apps = getApplicationCollection(context);
-
- broadcastSmsAppChange(context, userHandle,
- getApplicationForPackage(apps, oldPackage),
- getApplicationForPackage(apps, newPackage));
- }
-
- private static void broadcastSmsAppChange(Context context, UserHandle userHandle,
- @Nullable SmsApplicationData oldAppData,
- @Nullable SmsApplicationData applicationData) {
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "setDefaultApplicationInternal oldAppData=" + oldAppData);
- }
- if (oldAppData != null && oldAppData.mSmsAppChangedReceiverClass != null) {
- // Notify the old sms app that it's no longer the default
- final Intent oldAppIntent =
- new Intent(Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED);
- final ComponentName component = new ComponentName(oldAppData.mPackageName,
- oldAppData.mSmsAppChangedReceiverClass);
- oldAppIntent.setComponent(component);
- oldAppIntent.putExtra(Intents.EXTRA_IS_DEFAULT_SMS_APP, false);
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "setDefaultApplicationInternal old=" + oldAppData.mPackageName);
- }
- context.sendBroadcastAsUser(oldAppIntent, userHandle);
- }
- // Notify the new sms app that it's now the default (if the new sms app has a receiver
- // to handle the changed default sms intent).
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "setDefaultApplicationInternal new applicationData=" +
- applicationData);
- }
- if (applicationData != null && applicationData.mSmsAppChangedReceiverClass != null) {
- final Intent intent =
- new Intent(Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED);
- final ComponentName component = new ComponentName(applicationData.mPackageName,
- applicationData.mSmsAppChangedReceiverClass);
- intent.setComponent(component);
- intent.putExtra(Intents.EXTRA_IS_DEFAULT_SMS_APP, true);
- if (DEBUG_MULTIUSER) {
- Log.i(LOG_TAG, "setDefaultApplicationInternal new=" + applicationData.mPackageName);
- }
- context.sendBroadcastAsUser(intent, userHandle);
- }
-
- // Send an implicit broadcast for the system server.
- // (or anyone with MONITOR_DEFAULT_SMS_PACKAGE, really.)
- final Intent intent =
- new Intent(Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL);
- context.sendBroadcastAsUser(intent, userHandle,
- permission.MONITOR_DEFAULT_SMS_PACKAGE);
-
- if (applicationData != null) {
- MetricsLogger.action(context, MetricsEvent.ACTION_DEFAULT_SMS_APP_CHANGED,
- applicationData.mPackageName);
- }
- }
-
- /**
- * Assign WRITE_SMS AppOps permission to some special system apps.
- *
- * @param context The context
- * @param packageManager The package manager instance
- * @param appOps The AppOps manager instance
- * @param packageName The package name of the system app
- */
- private static void assignExclusiveSmsPermissionsToSystemApp(Context context,
- PackageManager packageManager, AppOpsManager appOps, String packageName) {
- // First check package signature matches the caller's package signature.
- // Since this class is only used internally by the system, this check makes sure
- // the package signature matches system signature.
- final int result = packageManager.checkSignatures(context.getPackageName(), packageName);
- if (result != PackageManager.SIGNATURE_MATCH) {
- Rlog.e(LOG_TAG, packageName + " does not have system signature");
- return;
- }
- try {
- PackageInfo info = packageManager.getPackageInfo(packageName, 0);
- int mode = appOps.checkOp(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
- packageName);
- if (mode != AppOpsManager.MODE_ALLOWED) {
- Rlog.w(LOG_TAG, packageName + " does not have OP_WRITE_SMS: (fixing)");
- setExclusiveAppops(packageName, appOps, info.applicationInfo.uid,
- AppOpsManager.MODE_ALLOWED);
- }
- } catch (NameNotFoundException e) {
- // No whitelisted system app on this device
- Rlog.e(LOG_TAG, "Package not found: " + packageName);
- }
-
- }
-
- private static void setExclusiveAppops(String pkg, AppOpsManager appOpsManager, int uid,
- int mode) {
- for (int appop : DEFAULT_APP_EXCLUSIVE_APPOPS) {
- appOpsManager.setUidMode(appop, uid, mode);
- }
- }
-
- /**
- * Tracks package changes and ensures that the default SMS app is always configured to be the
- * preferred activity for SENDTO sms/mms intents.
- */
- private static final class SmsPackageMonitor extends PackageMonitor {
- final Context mContext;
-
- public SmsPackageMonitor(Context context) {
- super();
- mContext = context;
- }
-
- @Override
- public void onPackageDisappeared(String packageName, int reason) {
- onPackageChanged();
- }
-
- @Override
- public void onPackageAppeared(String packageName, int reason) {
- onPackageChanged();
- }
-
- @Override
- public void onPackageModified(String packageName) {
- onPackageChanged();
- }
-
- private void onPackageChanged() {
- PackageManager packageManager = mContext.getPackageManager();
- Context userContext = mContext;
- final int userId = getSendingUserId();
- if (userId != UserHandle.USER_SYSTEM) {
- try {
- userContext = mContext.createPackageContextAsUser(mContext.getPackageName(), 0,
- UserHandle.of(userId));
- } catch (NameNotFoundException nnfe) {
- if (DEBUG_MULTIUSER) {
- Log.w(LOG_TAG, "Unable to create package context for user " + userId);
- }
- }
- }
- // Ensure this component is still configured as the preferred activity
- ComponentName componentName = getDefaultSendToApplication(userContext, true);
- if (componentName != null) {
- configurePreferredActivity(packageManager, componentName, userId);
- }
- }
- }
-
- public static void initSmsPackageMonitor(Context context) {
- sSmsPackageMonitor = new SmsPackageMonitor(context);
- sSmsPackageMonitor.register(context, context.getMainLooper(), UserHandle.ALL, false);
- }
-
- @UnsupportedAppUsage
- private static void configurePreferredActivity(PackageManager packageManager,
- ComponentName componentName, int userId) {
- // Add the four activity preferences we want to direct to this app.
- replacePreferredActivity(packageManager, componentName, userId, SCHEME_SMS);
- replacePreferredActivity(packageManager, componentName, userId, SCHEME_SMSTO);
- replacePreferredActivity(packageManager, componentName, userId, SCHEME_MMS);
- replacePreferredActivity(packageManager, componentName, userId, SCHEME_MMSTO);
- }
-
- /**
- * Updates the ACTION_SENDTO activity to the specified component for the specified scheme.
- */
- private static void replacePreferredActivity(PackageManager packageManager,
- ComponentName componentName, int userId, String scheme) {
- // Build the set of existing activities that handle this scheme
- Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts(scheme, "", null));
- List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivitiesAsUser(
- intent, PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_RESOLVED_FILTER,
- userId);
-
- // Build the set of ComponentNames for these activities
- final int n = resolveInfoList.size();
- ComponentName[] set = new ComponentName[n];
- for (int i = 0; i < n; i++) {
- ResolveInfo info = resolveInfoList.get(i);
- set[i] = new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
- }
-
- // Update the preferred SENDTO activity for the specified scheme
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_SENDTO);
- intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
- intentFilter.addDataScheme(scheme);
- packageManager.replacePreferredActivityAsUser(intentFilter,
- IntentFilter.MATCH_CATEGORY_SCHEME + IntentFilter.MATCH_ADJUSTMENT_NORMAL,
- set, componentName, userId);
- }
-
- /**
- * Returns SmsApplicationData for this package if this package is capable of being set as the
- * default SMS application.
- */
- @UnsupportedAppUsage
- public static SmsApplicationData getSmsApplicationData(String packageName, Context context) {
- Collection<SmsApplicationData> applications = getApplicationCollection(context);
- return getApplicationForPackage(applications, packageName);
- }
-
- /**
- * Gets the default SMS application
- * @param context context from the calling app
- * @param updateIfNeeded update the default app if there is no valid default app configured.
- * @return component name of the app and class to deliver SMS messages to
- */
- @UnsupportedAppUsage
- public static ComponentName getDefaultSmsApplication(Context context, boolean updateIfNeeded) {
- return getDefaultSmsApplicationAsUser(context, updateIfNeeded, getIncomingUserId(context));
- }
-
- /**
- * Gets the default SMS application on a given user
- * @param context context from the calling app
- * @param updateIfNeeded update the default app if there is no valid default app configured.
- * @param userId target user ID.
- * @return component name of the app and class to deliver SMS messages to
- */
- public static ComponentName getDefaultSmsApplicationAsUser(Context context,
- boolean updateIfNeeded, int userId) {
- final long token = Binder.clearCallingIdentity();
- try {
- ComponentName component = null;
- SmsApplicationData smsApplicationData = getApplication(context, updateIfNeeded,
- userId);
- if (smsApplicationData != null) {
- component = new ComponentName(smsApplicationData.mPackageName,
- smsApplicationData.mSmsReceiverClass);
- }
- return component;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Gets the default MMS application
- * @param context context from the calling app
- * @param updateIfNeeded update the default app if there is no valid default app configured.
- * @return component name of the app and class to deliver MMS messages to
- */
- @UnsupportedAppUsage
- public static ComponentName getDefaultMmsApplication(Context context, boolean updateIfNeeded) {
- int userId = getIncomingUserId(context);
- final long token = Binder.clearCallingIdentity();
- try {
- ComponentName component = null;
- SmsApplicationData smsApplicationData = getApplication(context, updateIfNeeded,
- userId);
- if (smsApplicationData != null) {
- component = new ComponentName(smsApplicationData.mPackageName,
- smsApplicationData.mMmsReceiverClass);
- }
- return component;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Gets the default Respond Via Message application
- * @param context context from the calling app
- * @param updateIfNeeded update the default app if there is no valid default app configured.
- * @return component name of the app and class to direct Respond Via Message intent to
- */
- @UnsupportedAppUsage
- public static ComponentName getDefaultRespondViaMessageApplication(Context context,
- boolean updateIfNeeded) {
- int userId = getIncomingUserId(context);
- final long token = Binder.clearCallingIdentity();
- try {
- ComponentName component = null;
- SmsApplicationData smsApplicationData = getApplication(context, updateIfNeeded,
- userId);
- if (smsApplicationData != null) {
- component = new ComponentName(smsApplicationData.mPackageName,
- smsApplicationData.mRespondViaMessageClass);
- }
- return component;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Gets the default Send To (smsto) application.
- * <p>
- * Caller must pass in the correct user context if calling from a singleton service.
- * @param context context from the calling app
- * @param updateIfNeeded update the default app if there is no valid default app configured.
- * @return component name of the app and class to direct SEND_TO (smsto) intent to
- */
- public static ComponentName getDefaultSendToApplication(Context context,
- boolean updateIfNeeded) {
- int userId = getIncomingUserId(context);
- final long token = Binder.clearCallingIdentity();
- try {
- ComponentName component = null;
- SmsApplicationData smsApplicationData = getApplication(context, updateIfNeeded,
- userId);
- if (smsApplicationData != null) {
- component = new ComponentName(smsApplicationData.mPackageName,
- smsApplicationData.mSendToClass);
- }
- return component;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Gets the default application that handles external changes to the SmsProvider and
- * MmsProvider.
- * @param context context from the calling app
- * @param updateIfNeeded update the default app if there is no valid default app configured.
- * @return component name of the app and class to deliver change intents to
- */
- public static ComponentName getDefaultExternalTelephonyProviderChangedApplication(
- Context context, boolean updateIfNeeded) {
- int userId = getIncomingUserId(context);
- final long token = Binder.clearCallingIdentity();
- try {
- ComponentName component = null;
- SmsApplicationData smsApplicationData = getApplication(context, updateIfNeeded,
- userId);
- if (smsApplicationData != null
- && smsApplicationData.mProviderChangedReceiverClass != null) {
- component = new ComponentName(smsApplicationData.mPackageName,
- smsApplicationData.mProviderChangedReceiverClass);
- }
- return component;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Gets the default application that handles sim full event.
- * @param context context from the calling app
- * @param updateIfNeeded update the default app if there is no valid default app configured.
- * @return component name of the app and class to deliver change intents to
- */
- public static ComponentName getDefaultSimFullApplication(
- Context context, boolean updateIfNeeded) {
- int userId = getIncomingUserId(context);
- final long token = Binder.clearCallingIdentity();
- try {
- ComponentName component = null;
- SmsApplicationData smsApplicationData = getApplication(context, updateIfNeeded,
- userId);
- if (smsApplicationData != null
- && smsApplicationData.mSimFullReceiverClass != null) {
- component = new ComponentName(smsApplicationData.mPackageName,
- smsApplicationData.mSimFullReceiverClass);
- }
- return component;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Returns whether need to write the SMS message to SMS database for this package.
- * <p>
- * Caller must pass in the correct user context if calling from a singleton service.
- */
- @UnsupportedAppUsage
- public static boolean shouldWriteMessageForPackage(String packageName, Context context) {
- if (SmsManager.getDefault().getAutoPersisting()) {
- return true;
- }
- return !isDefaultSmsApplication(context, packageName);
- }
-
- /**
- * Check if a package is default sms app (or equivalent, like bluetooth)
- *
- * @param context context from the calling app
- * @param packageName the name of the package to be checked
- * @return true if the package is default sms app or bluetooth
- */
- @UnsupportedAppUsage
- public static boolean isDefaultSmsApplication(Context context, String packageName) {
- if (packageName == null) {
- return false;
- }
- final String defaultSmsPackage = getDefaultSmsApplicationPackageName(context);
- if ((defaultSmsPackage != null && defaultSmsPackage.equals(packageName))
- || BLUETOOTH_PACKAGE_NAME.equals(packageName)) {
- return true;
- }
- return false;
- }
-
- private static String getDefaultSmsApplicationPackageName(Context context) {
- final ComponentName component = getDefaultSmsApplication(context, false);
- if (component != null) {
- return component.getPackageName();
- }
- return null;
- }
-}
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index d892e559c899..b357fa43008f 100755
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -878,8 +878,9 @@ public class SmsMessage extends SmsMessageBase {
* Parses a broadcast SMS, possibly containing a CMAS alert.
*
* @param plmn the PLMN for a broadcast SMS
+ * @param subId
*/
- public SmsCbMessage parseBroadcastSms(String plmn) {
+ public SmsCbMessage parseBroadcastSms(String plmn, int subId) {
BearerData bData = BearerData.decode(mEnvelope.bearerData, mEnvelope.serviceCategory);
if (bData == null) {
Rlog.w(LOG_TAG, "BearerData.decode() returned null");
@@ -895,7 +896,21 @@ public class SmsMessage extends SmsMessageBase {
return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP2,
SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE, bData.messageId, location,
mEnvelope.serviceCategory, bData.getLanguage(), bData.userData.payloadStr,
- bData.priority, null, bData.cmasWarningInfo);
+ bData.priority, null, bData.cmasWarningInfo, subId);
+ }
+
+ /**
+ * @return the bearer data byte array
+ */
+ public byte[] getEnvelopeBearerData() {
+ return mEnvelope.bearerData;
+ }
+
+ /**
+ * @return the 16-bit CDMA SCPT service category
+ */
+ public @CdmaSmsCbProgramData.Category int getEnvelopeServiceCategory() {
+ return mEnvelope.serviceCategory;
}
/**
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
index 98fa3af5c3c6..38154b8e6024 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
@@ -18,6 +18,7 @@ package com.android.internal.telephony.cdma.sms;
import android.annotation.UnsupportedAppUsage;
+import android.telephony.cdma.CdmaSmsCbProgramData;
public final class SmsEnvelope {
/**
@@ -56,12 +57,18 @@ public final class SmsEnvelope {
//...
// CMAS alert service category assignments, see 3GPP2 C.R1001 table 9.3.3-1
- public static final int SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT = 0x1000;
- public static final int SERVICE_CATEGORY_CMAS_EXTREME_THREAT = 0x1001;
- public static final int SERVICE_CATEGORY_CMAS_SEVERE_THREAT = 0x1002;
- public static final int SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY = 0x1003;
- public static final int SERVICE_CATEGORY_CMAS_TEST_MESSAGE = 0x1004;
- public static final int SERVICE_CATEGORY_CMAS_LAST_RESERVED_VALUE = 0x10ff;
+ public static final int SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT =
+ CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT; // = 4096
+ public static final int SERVICE_CATEGORY_CMAS_EXTREME_THREAT =
+ CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT; // = 4097
+ public static final int SERVICE_CATEGORY_CMAS_SEVERE_THREAT =
+ CdmaSmsCbProgramData.CATEGORY_CMAS_SEVERE_THREAT; // = 4098
+ public static final int SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY =
+ CdmaSmsCbProgramData.CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY; // = 4099
+ public static final int SERVICE_CATEGORY_CMAS_TEST_MESSAGE =
+ CdmaSmsCbProgramData.CATEGORY_CMAS_TEST_MESSAGE; // = 4100
+ public static final int SERVICE_CATEGORY_CMAS_LAST_RESERVED_VALUE =
+ CdmaSmsCbProgramData.CATEGORY_CMAS_LAST_RESERVED_VALUE; // = 4351
/**
* Provides the type of a SMS message like point to point, broadcast or acknowledge
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index 20169152539e..7422863d862c 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
@@ -44,5 +44,7 @@ interface IEuiccController {
oneway void updateSubscriptionNickname(int cardId, int subscriptionId, String nickname,
String callingPackage, in PendingIntent callbackIntent);
oneway void eraseSubscriptions(int cardId, in PendingIntent callbackIntent);
+ oneway void eraseSubscriptionsWithOptions(
+ int cardId, int options, in PendingIntent callbackIntent);
oneway void retainSubscriptionsForFactoryReset(int cardId, in PendingIntent callbackIntent);
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
index 6eea118787a7..c3d490a6d5cf 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
@@ -90,9 +90,10 @@ public class GsmSmsCbMessage {
* Create a new SmsCbMessage object from a header object plus one or more received PDUs.
*
* @param pdus PDU bytes
+ * @slotIndex slotIndex for which received sms cb message
*/
public static SmsCbMessage createSmsCbMessage(Context context, SmsCbHeader header,
- SmsCbLocation location, byte[][] pdus)
+ SmsCbLocation location, byte[][] pdus, int slotIndex)
throws IllegalArgumentException {
long receivedTimeMillis = System.currentTimeMillis();
if (header.isEtwsPrimaryNotification()) {
@@ -104,7 +105,7 @@ public class GsmSmsCbMessage {
header.getSerialNumber(), location, header.getServiceCategory(), null,
getEtwsPrimaryMessage(context, header.getEtwsInfo().getWarningType()),
SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, header.getEtwsInfo(),
- header.getCmasInfo(), 0, null /* geometries */, receivedTimeMillis);
+ header.getCmasInfo(), 0, null /* geometries */, receivedTimeMillis, slotIndex);
} else if (header.isUmtsFormat()) {
// UMTS format has only 1 PDU
byte[] pdu = pdus[0];
@@ -138,7 +139,7 @@ public class GsmSmsCbMessage {
header.getGeographicalScope(), header.getSerialNumber(), location,
header.getServiceCategory(), language, body, priority,
header.getEtwsInfo(), header.getCmasInfo(), maximumWaitingTimeSec, geometries,
- receivedTimeMillis);
+ receivedTimeMillis, slotIndex);
} else {
String language = null;
StringBuilder sb = new StringBuilder();
@@ -154,7 +155,7 @@ public class GsmSmsCbMessage {
header.getGeographicalScope(), header.getSerialNumber(), location,
header.getServiceCategory(), language, sb.toString(), priority,
header.getEtwsInfo(), header.getCmasInfo(), 0, null /* geometries */,
- receivedTimeMillis);
+ receivedTimeMillis, slotIndex);
}
}
@@ -461,7 +462,11 @@ public class GsmSmsCbMessage {
}
}
- static final class GeoFencingTriggerMessage {
+ /**
+ * Part of a GSM SMS cell broadcast message which may trigger geo-fencing logic.
+ * @hide
+ */
+ public static final class GeoFencingTriggerMessage {
/**
* Indicate the list of active alerts share their warning area coordinates which means the
* broadcast area is the union of the broadcast areas of the active alerts in this list.
@@ -476,6 +481,11 @@ public class GsmSmsCbMessage {
this.cbIdentifiers = cbIdentifiers;
}
+ /**
+ * Whether the trigger message indicates that the broadcast areas are shared between all
+ * active alerts.
+ * @return true if broadcast areas are to be shared
+ */
boolean shouldShareBroadcastArea() {
return type == TYPE_ACTIVE_ALERT_SHARE_WAC;
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
index 6bbff4b91ee7..cbe521182667 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
@@ -74,22 +74,22 @@ public class SmsCbHeader {
/**
* Length of SMS-CB header
*/
- static final int PDU_HEADER_LENGTH = 6;
+ public static final int PDU_HEADER_LENGTH = 6;
/**
* GSM pdu format, as defined in 3gpp TS 23.041, section 9.4.1
*/
- static final int FORMAT_GSM = 1;
+ public static final int FORMAT_GSM = 1;
/**
* UMTS pdu format, as defined in 3gpp TS 23.041, section 9.4.2
*/
- static final int FORMAT_UMTS = 2;
+ public static final int FORMAT_UMTS = 2;
/**
- * GSM pdu format, as defined in 3gpp TS 23.041, section 9.4.1.3
+ * ETWS pdu format, as defined in 3gpp TS 23.041, section 9.4.1.3
*/
- static final int FORMAT_ETWS_PRIMARY = 3;
+ public static final int FORMAT_ETWS_PRIMARY = 3;
/**
* Message type value as defined in 3gpp TS 25.324, section 11.1.
@@ -230,43 +230,43 @@ public class SmsCbHeader {
}
@UnsupportedAppUsage
- int getGeographicalScope() {
+ public int getGeographicalScope() {
return mGeographicalScope;
}
@UnsupportedAppUsage
- int getSerialNumber() {
+ public int getSerialNumber() {
return mSerialNumber;
}
@UnsupportedAppUsage
- int getServiceCategory() {
+ public int getServiceCategory() {
return mMessageIdentifier;
}
- int getDataCodingScheme() {
+ public int getDataCodingScheme() {
return mDataCodingScheme;
}
- DataCodingScheme getDataCodingSchemeStructedData() {
+ public DataCodingScheme getDataCodingSchemeStructedData() {
return mDataCodingSchemeStructedData;
}
@UnsupportedAppUsage
- int getPageIndex() {
+ public int getPageIndex() {
return mPageIndex;
}
@UnsupportedAppUsage
- int getNumberOfPages() {
+ public int getNumberOfPages() {
return mNrOfPages;
}
- SmsCbEtwsInfo getEtwsInfo() {
+ public SmsCbEtwsInfo getEtwsInfo() {
return mEtwsInfo;
}
- SmsCbCmasInfo getCmasInfo() {
+ public SmsCbCmasInfo getCmasInfo() {
return mCmasInfo;
}
@@ -274,7 +274,7 @@ public class SmsCbHeader {
* Return whether this broadcast is an emergency (PWS) message type.
* @return true if this message is emergency type; false otherwise
*/
- boolean isEmergencyMessage() {
+ public boolean isEmergencyMessage() {
return mMessageIdentifier >= SmsCbConstants.MESSAGE_ID_PWS_FIRST_IDENTIFIER
&& mMessageIdentifier <= SmsCbConstants.MESSAGE_ID_PWS_LAST_IDENTIFIER;
}
@@ -292,7 +292,7 @@ public class SmsCbHeader {
* Return whether this broadcast is an ETWS primary notification.
* @return true if this message is an ETWS primary notification; false otherwise
*/
- boolean isEtwsPrimaryNotification() {
+ public boolean isEtwsPrimaryNotification() {
return mFormat == FORMAT_ETWS_PRIMARY;
}
@@ -300,7 +300,7 @@ public class SmsCbHeader {
* Return whether this broadcast is in UMTS format.
* @return true if this message is in UMTS format; false otherwise
*/
- boolean isUmtsFormat() {
+ public boolean isUmtsFormat() {
return mFormat == FORMAT_UMTS;
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 28b331b17b91..da32c8c45a73 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -1496,7 +1496,7 @@ public class SmsMessage extends SmsMessageBase {
*
* @return true if this is a USIM data download message; false otherwise
*/
- public boolean isUsimDataDownload() {
+ boolean isUsimDataDownload() {
return messageClass == MessageClass.CLASS_2 &&
(mProtocolIdentifier == 0x7f || mProtocolIdentifier == 0x7c);
}
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index 749f0522846d..3dc2e90652a4 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -22,6 +22,7 @@ import android.graphics.Bitmap;
import android.graphics.Color;
import android.telephony.Rlog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.EncodeException;
import com.android.internal.telephony.GsmAlphabet;
@@ -29,6 +30,7 @@ import dalvik.annotation.compat.UnsupportedAppUsage;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
+import java.util.List;
/**
* Various methods, useful for dealing with SIM data.
@@ -36,6 +38,11 @@ import java.nio.charset.Charset;
public class IccUtils {
static final String LOG_TAG="IccUtils";
+ // 3GPP specification constants
+ // Spec reference TS 31.102 section 4.2.16
+ @VisibleForTesting
+ static final int FPLMN_BYTE_SIZE = 3;
+
// A table mapping from a number to a hex character for fast encoding hex strings.
private static final char[] HEX_CHARS = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
@@ -899,6 +906,30 @@ public class IccUtils {
return 0;
}
+ /**
+ * Encode the Fplmns into byte array to write to EF.
+ *
+ * @param fplmns Array of fplmns to be serialized.
+ * @param dataLength the size of the EF file.
+ * @return the encoded byte array in the correct format for FPLMN file.
+ */
+ public static byte[] encodeFplmns(List<String> fplmns, int dataLength) {
+ byte[] serializedFplmns = new byte[dataLength];
+ int offset = 0;
+ for (String fplmn : fplmns) {
+ if (offset >= dataLength) break;
+ stringToBcdPlmn(fplmn, serializedFplmns, offset);
+ offset += FPLMN_BYTE_SIZE;
+ }
+ //pads to the length of the EF file.
+ while (offset < dataLength) {
+ // required by 3GPP TS 31.102 spec 4.2.16
+ serializedFplmns[offset++] = (byte) 0xff;
+ }
+ return serializedFplmns;
+
+ }
+
static byte[]
stringToAdnStringField(String alphaTag) {
boolean isUcs2 = false;
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index adc9e2251c0b..81b1e49ffed1 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -19,10 +19,9 @@
java_sdk_library {
name: "android.test.mock",
- srcs: [
- "src/**/*.java",
- ":framework-all-sources",
- ],
+ srcs: ["src/**/*.java"],
+ api_srcs: [":framework-all-sources"],
+ libs: ["framework-all"],
api_packages: [
"android.test.mock",
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index a95b6f11e98a..5053ceedc703 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -758,6 +758,12 @@ public class MockContext extends Context {
/** {@hide} */
@Override
+ public Context createContextAsUser(UserHandle user, @CreatePackageOptions int flags) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@hide} */
+ @Override
public int getUserId() {
throw new UnsupportedOperationException();
}
diff --git a/tests/ApkVerityTest/Android.bp b/tests/ApkVerityTest/Android.bp
new file mode 100644
index 000000000000..adcbb4287dd0
--- /dev/null
+++ b/tests/ApkVerityTest/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_test_host {
+ name: "ApkVerityTests",
+ srcs: ["src/**/*.java"],
+ libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"],
+ test_suites: ["general-tests"],
+ target_required: [
+ "block_device_writer_module",
+ "ApkVerityTestApp",
+ "ApkVerityTestAppSplit",
+ ],
+ data: [
+ ":ApkVerityTestCertDer",
+ ":ApkVerityTestAppFsvSig",
+ ":ApkVerityTestAppDm",
+ ":ApkVerityTestAppDmFsvSig",
+ ":ApkVerityTestAppSplitFsvSig",
+ ":ApkVerityTestAppSplitDm",
+ ":ApkVerityTestAppSplitDmFsvSig",
+ ],
+}
diff --git a/tests/ApkVerityTest/AndroidTest.xml b/tests/ApkVerityTest/AndroidTest.xml
new file mode 100644
index 000000000000..73779cbd1a87
--- /dev/null
+++ b/tests/ApkVerityTest/AndroidTest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="APK fs-verity integration/regression test">
+ <option name="test-suite-tag" value="apct" />
+
+ <!-- This test requires root to write against block device. -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <!-- Disable package verifier prevents it holding the target APK's fd that prevents cache
+ eviction. -->
+ <option name="set-global-setting" key="package_verifier_enable" value="0" />
+ <option name="restore-settings" value="true" />
+
+ <!-- Skip in order to prevent reboot that confuses the test flow. -->
+ <option name="force-skip-system-props" value="true" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="block_device_writer->/data/local/tmp/block_device_writer" />
+ <option name="push" value="ApkVerityTestCert.der->/data/local/tmp/ApkVerityTestCert.der" />
+ </target_preparer>
+
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="ApkVerityTests.jar" />
+ </test>
+</configuration>
diff --git a/tests/ApkVerityTest/ApkVerityTestApp/Android.bp b/tests/ApkVerityTest/ApkVerityTestApp/Android.bp
new file mode 100644
index 000000000000..69632b215822
--- /dev/null
+++ b/tests/ApkVerityTest/ApkVerityTestApp/Android.bp
@@ -0,0 +1,29 @@
+// 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.
+
+android_test_helper_app {
+ name: "ApkVerityTestApp",
+ manifest: "AndroidManifest.xml",
+ srcs: ["src/**/*.java"],
+}
+
+android_test_helper_app {
+ name: "ApkVerityTestAppSplit",
+ manifest: "feature_split/AndroidManifest.xml",
+ srcs: ["src/**/*.java"],
+ aaptflags: [
+ "--custom-package com.android.apkverity.feature_x",
+ "--package-id 0x80",
+ ],
+}
diff --git a/tests/ApkVerityTest/ApkVerityTestApp/AndroidManifest.xml b/tests/ApkVerityTest/ApkVerityTestApp/AndroidManifest.xml
new file mode 100644
index 000000000000..0b3ff77c2cdf
--- /dev/null
+++ b/tests/ApkVerityTest/ApkVerityTestApp/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.apkverity">
+ <application>
+ <activity android:name=".DummyActivity"/>
+ </application>
+</manifest>
diff --git a/tests/ApkVerityTest/ApkVerityTestApp/feature_split/AndroidManifest.xml b/tests/ApkVerityTest/ApkVerityTestApp/feature_split/AndroidManifest.xml
new file mode 100644
index 000000000000..3f1a4f3a26a1
--- /dev/null
+++ b/tests/ApkVerityTest/ApkVerityTestApp/feature_split/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.apkverity"
+ android:isFeatureSplit="true"
+ split="feature_x">
+ <application>
+ <activity android:name=".feature_x.DummyActivity"/>
+ </application>
+</manifest>
diff --git a/tests/ApkVerityTest/ApkVerityTestApp/feature_split/src/com/android/apkverity/feature_x/DummyActivity.java b/tests/ApkVerityTest/ApkVerityTestApp/feature_split/src/com/android/apkverity/feature_x/DummyActivity.java
new file mode 100644
index 000000000000..0f694c293330
--- /dev/null
+++ b/tests/ApkVerityTest/ApkVerityTestApp/feature_split/src/com/android/apkverity/feature_x/DummyActivity.java
@@ -0,0 +1,22 @@
+/*
+ * 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.apkverity.feature_x;
+
+import android.app.Activity;
+
+/** Dummy class just to generate some dex */
+public class DummyActivity extends Activity {}
diff --git a/tests/ApkVerityTest/ApkVerityTestApp/src/com/android/apkverity/DummyActivity.java b/tests/ApkVerityTest/ApkVerityTestApp/src/com/android/apkverity/DummyActivity.java
new file mode 100644
index 000000000000..837c7be37504
--- /dev/null
+++ b/tests/ApkVerityTest/ApkVerityTestApp/src/com/android/apkverity/DummyActivity.java
@@ -0,0 +1,22 @@
+/*
+ * 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.apkverity;
+
+import android.app.Activity;
+
+/** Dummy class just to generate some dex */
+public class DummyActivity extends Activity {}
diff --git a/tests/ApkVerityTest/block_device_writer/Android.bp b/tests/ApkVerityTest/block_device_writer/Android.bp
new file mode 100644
index 000000000000..deed3a00d2fe
--- /dev/null
+++ b/tests/ApkVerityTest/block_device_writer/Android.bp
@@ -0,0 +1,30 @@
+// 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.
+
+// This is a cc_test just because it supports test_suites. This should be converted to something
+// like cc_binary_test_helper once supported.
+cc_test {
+ // Depending on how the test runs, the executable may be uploaded to different location.
+ // Before the bug in the file pusher is fixed, workaround by making the name unique.
+ // See b/124718249#comment12.
+ name: "block_device_writer_module",
+ stem: "block_device_writer",
+
+ srcs: ["block_device_writer.cpp"],
+ cflags: ["-Wall", "-Werror", "-Wextra", "-g"],
+ shared_libs: ["libbase", "libutils"],
+
+ test_suites: ["general-tests"],
+ gtest: false,
+}
diff --git a/tests/ApkVerityTest/block_device_writer/block_device_writer.cpp b/tests/ApkVerityTest/block_device_writer/block_device_writer.cpp
new file mode 100644
index 000000000000..02dfd732a716
--- /dev/null
+++ b/tests/ApkVerityTest/block_device_writer/block_device_writer.cpp
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <cassert>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <memory>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/fiemap.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/unique_fd.h>
+
+// This program modifies a file at given offset, but directly against the block
+// device, purposely to bypass the filesystem. Note that the change on block
+// device may not reflect the same way when read from filesystem, for example,
+// when the file is encrypted on disk.
+//
+// Only one byte is supported for now just so that we don't need to handle the
+// case when the range crosses different "extents".
+//
+// References:
+// https://www.kernel.org/doc/Documentation/filesystems/fiemap.txt
+// https://git.kernel.org/pub/scm/fs/xfs/xfsprogs-dev.git/tree/io/fiemap.c
+
+#ifndef F2FS_IOC_SET_PIN_FILE
+#ifndef F2FS_IOCTL_MAGIC
+#define F2FS_IOCTL_MAGIC 0xf5
+#endif
+#define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32)
+#define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32)
+#endif
+
+struct Args {
+ const char* block_device;
+ const char* file_name;
+ uint64_t byte_offset;
+ bool use_f2fs_pinning;
+};
+
+class ScopedF2fsFilePinning {
+ public:
+ explicit ScopedF2fsFilePinning(const char* file_path) {
+ fd_.reset(TEMP_FAILURE_RETRY(open(file_path, O_WRONLY | O_CLOEXEC, 0)));
+ if (fd_.get() == -1) {
+ perror("Failed to open");
+ return;
+ }
+ __u32 set = 1;
+ ioctl(fd_.get(), F2FS_IOC_SET_PIN_FILE, &set);
+ }
+
+ ~ScopedF2fsFilePinning() {
+ __u32 set = 0;
+ ioctl(fd_.get(), F2FS_IOC_SET_PIN_FILE, &set);
+ }
+
+ private:
+ android::base::unique_fd fd_;
+};
+
+ssize_t get_logical_block_size(const char* block_device) {
+ android::base::unique_fd fd(open(block_device, O_RDONLY));
+ if (fd.get() < 0) {
+ fprintf(stderr, "open %s failed\n", block_device);
+ return -1;
+ }
+
+ int size;
+ if (ioctl(fd, BLKSSZGET, &size) < 0) {
+ fprintf(stderr, "ioctl(BLKSSZGET) failed: %s\n", strerror(errno));
+ return -1;
+ }
+ return size;
+}
+
+int64_t get_physical_offset(const char* file_name, uint64_t byte_offset) {
+ android::base::unique_fd fd(open(file_name, O_RDONLY));
+ if (fd.get() < 0) {
+ fprintf(stderr, "open %s failed\n", file_name);
+ return -1;
+ }
+
+ const int map_size = sizeof(struct fiemap) + sizeof(struct fiemap_extent);
+ char fiemap_buffer[map_size] = {0};
+ struct fiemap* fiemap = reinterpret_cast<struct fiemap*>(&fiemap_buffer);
+
+ fiemap->fm_flags = FIEMAP_FLAG_SYNC;
+ fiemap->fm_start = byte_offset;
+ fiemap->fm_length = 1;
+ fiemap->fm_extent_count = 1;
+
+ int ret = ioctl(fd.get(), FS_IOC_FIEMAP, fiemap);
+ if (ret < 0) {
+ fprintf(stderr, "ioctl(FS_IOC_FIEMAP) failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if (fiemap->fm_mapped_extents != 1) {
+ fprintf(stderr, "fm_mapped_extents != 1 (is %d)\n",
+ fiemap->fm_mapped_extents);
+ return -1;
+ }
+
+ struct fiemap_extent* extent = &fiemap->fm_extents[0];
+ printf(
+ "logical offset: %llu, physical offset: %llu, length: %llu, "
+ "flags: %x\n",
+ extent->fe_logical, extent->fe_physical, extent->fe_length,
+ extent->fe_flags);
+ if (extent->fe_flags & (FIEMAP_EXTENT_UNKNOWN |
+ FIEMAP_EXTENT_UNWRITTEN)) {
+ fprintf(stderr, "Failed to locate physical offset safely\n");
+ return -1;
+ }
+
+ return extent->fe_physical + (byte_offset - extent->fe_logical);
+}
+
+int read_block_from_device(const char* device_path, uint64_t block_offset,
+ ssize_t block_size, char* block_buffer) {
+ assert(block_offset % block_size == 0);
+ android::base::unique_fd fd(open(device_path, O_RDONLY | O_DIRECT));
+ if (fd.get() < 0) {
+ fprintf(stderr, "open %s failed\n", device_path);
+ return -1;
+ }
+
+ ssize_t retval =
+ TEMP_FAILURE_RETRY(pread(fd, block_buffer, block_size, block_offset));
+ if (retval != block_size) {
+ fprintf(stderr, "read returns error or incomplete result (%zu): %s\n",
+ retval, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int write_block_to_device(const char* device_path, uint64_t block_offset,
+ ssize_t block_size, char* block_buffer) {
+ assert(block_offset % block_size == 0);
+ android::base::unique_fd fd(open(device_path, O_WRONLY | O_DIRECT));
+ if (fd.get() < 0) {
+ fprintf(stderr, "open %s failed\n", device_path);
+ return -1;
+ }
+
+ ssize_t retval = TEMP_FAILURE_RETRY(
+ pwrite(fd.get(), block_buffer, block_size, block_offset));
+ if (retval != block_size) {
+ fprintf(stderr, "write returns error or incomplete result (%zu): %s\n",
+ retval, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+std::unique_ptr<Args> parse_args(int argc, const char** argv) {
+ if (argc != 4 && argc != 5) {
+ fprintf(stderr,
+ "Usage: %s [--use-f2fs-pinning] block_dev filename byte_offset\n"
+ "\n"
+ "This program bypasses filesystem and damages the specified byte\n"
+ "at the physical position on <block_dev> corresponding to the\n"
+ "logical byte location in <filename>.\n",
+ argv[0]);
+ return nullptr;
+ }
+
+ auto args = std::make_unique<Args>();
+ const char** arg = &argv[1];
+ args->use_f2fs_pinning = strcmp(*arg, "--use-f2fs-pinning") == 0;
+ if (args->use_f2fs_pinning) {
+ ++arg;
+ }
+ args->block_device = *(arg++);
+ args->file_name = *(arg++);
+ args->byte_offset = strtoull(*arg, nullptr, 10);
+ if (args->byte_offset == ULLONG_MAX) {
+ perror("Invalid byte offset");
+ return nullptr;
+ }
+ return args;
+}
+
+int main(int argc, const char** argv) {
+ std::unique_ptr<Args> args = parse_args(argc, argv);
+ if (args == nullptr) {
+ return -1;
+ }
+
+ ssize_t block_size = get_logical_block_size(args->block_device);
+ if (block_size < 0) {
+ return -1;
+ }
+
+ std::unique_ptr<ScopedF2fsFilePinning> pinned_file;
+ if (args->use_f2fs_pinning) {
+ pinned_file = std::make_unique<ScopedF2fsFilePinning>(args->file_name);
+ }
+
+ int64_t physical_offset_signed = get_physical_offset(args->file_name, args->byte_offset);
+ if (physical_offset_signed < 0) {
+ return -1;
+ }
+
+ uint64_t physical_offset = static_cast<uint64_t>(physical_offset_signed);
+ uint64_t offset_within_block = physical_offset % block_size;
+ uint64_t physical_block_offset = physical_offset - offset_within_block;
+
+ // Direct I/O requires aligned buffer
+ std::unique_ptr<char> buf(static_cast<char*>(
+ aligned_alloc(block_size /* alignment */, block_size /* size */)));
+
+ if (read_block_from_device(args->block_device, physical_block_offset, block_size,
+ buf.get()) < 0) {
+ return -1;
+ }
+ char* p = buf.get() + offset_within_block;
+ printf("before: %hhx\n", *p);
+ *p ^= 0xff;
+ printf("after: %hhx\n", *p);
+ if (write_block_to_device(args->block_device, physical_block_offset, block_size,
+ buf.get()) < 0) {
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
new file mode 100644
index 000000000000..2445a6a52c08
--- /dev/null
+++ b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
@@ -0,0 +1,504 @@
+/*
+ * 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.apkverity;
+
+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.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.platform.test.annotations.RootPermissionTest;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+
+/**
+ * This test makes sure app installs with fs-verity signature, and on-access verification works.
+ *
+ * <p>When an app is installed, all or none of the files should have their corresponding .fsv_sig
+ * signature file. Otherwise, install will fail.
+ *
+ * <p>Once installed, file protected by fs-verity is verified by kernel every time a block is loaded
+ * from disk to memory. The file is immutable by design, enforced by filesystem.
+ *
+ * <p>In order to make sure a block of the file is readable only if the underlying block on disk
+ * stay intact, the test needs to bypass the filesystem and tampers with the corresponding physical
+ * address against the block device.
+ *
+ * <p>Requirements to run this test:
+ * <ul>
+ * <li>Device is rootable</li>
+ * <li>The filesystem supports fs-verity</li>
+ * <li>The feature flag is enabled</li>
+ * </ul>
+ */
+@RootPermissionTest
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class ApkVerityTest extends BaseHostJUnit4Test {
+ private static final String TARGET_PACKAGE = "com.android.apkverity";
+
+ private static final String BASE_APK = "ApkVerityTestApp.apk";
+ private static final String BASE_APK_DM = "ApkVerityTestApp.dm";
+ private static final String SPLIT_APK = "ApkVerityTestAppSplit.apk";
+ private static final String SPLIT_APK_DM = "ApkVerityTestAppSplit.dm";
+
+ private static final String INSTALLED_BASE_APK = "base.apk";
+ private static final String INSTALLED_BASE_DM = "base.dm";
+ private static final String INSTALLED_SPLIT_APK = "split_feature_x.apk";
+ private static final String INSTALLED_SPLIT_DM = "split_feature_x.dm";
+ private static final String INSTALLED_BASE_APK_FSV_SIG = "base.apk.fsv_sig";
+ private static final String INSTALLED_BASE_DM_FSV_SIG = "base.dm.fsv_sig";
+ private static final String INSTALLED_SPLIT_APK_FSV_SIG = "split_feature_x.apk.fsv_sig";
+ private static final String INSTALLED_SPLIT_DM_FSV_SIG = "split_feature_x.dm.fsv_sig";
+
+ private static final String DAMAGING_EXECUTABLE = "/data/local/tmp/block_device_writer";
+ private static final String CERT_PATH = "/data/local/tmp/ApkVerityTestCert.der";
+
+ private static final String APK_VERITY_STANDARD_MODE = "2";
+
+ /** Only 4K page is supported by fs-verity currently. */
+ private static final int FSVERITY_PAGE_SIZE = 4096;
+
+ private ITestDevice mDevice;
+ private String mKeyId;
+
+ @Before
+ public void setUp() throws DeviceNotAvailableException {
+ mDevice = getDevice();
+
+ String apkVerityMode = mDevice.getProperty("ro.apk_verity.mode");
+ assumeTrue(APK_VERITY_STANDARD_MODE.equals(apkVerityMode));
+
+ mKeyId = expectRemoteCommandToSucceed(
+ "mini-keyctl padd asymmetric fsv_test .fs-verity < " + CERT_PATH).trim();
+ if (!mKeyId.matches("^\\d+$")) {
+ String keyId = mKeyId;
+ mKeyId = null;
+ fail("Key ID is not decimal: " + keyId);
+ }
+
+ uninstallPackage(TARGET_PACKAGE);
+ }
+
+ @After
+ public void tearDown() throws DeviceNotAvailableException {
+ uninstallPackage(TARGET_PACKAGE);
+
+ if (mKeyId != null) {
+ expectRemoteCommandToSucceed("mini-keyctl unlink " + mKeyId + " .fs-verity");
+ }
+ }
+
+ @Test
+ public void testFsverityKernelSupports() throws DeviceNotAvailableException {
+ ITestDevice.MountPointInfo mountPoint = mDevice.getMountPointInfo("/data");
+ expectRemoteCommandToSucceed("test -f /sys/fs/" + mountPoint.type + "/features/verity");
+ }
+
+ @Test
+ public void testInstallBase() throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFileAndSignature(BASE_APK)
+ .run();
+ assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
+
+ verifyInstalledFiles(
+ INSTALLED_BASE_APK,
+ INSTALLED_BASE_APK_FSV_SIG);
+ verifyInstalledFilesHaveFsverity();
+ }
+
+ @Test
+ public void testInstallBaseWithWrongSignature()
+ throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFile(BASE_APK)
+ .addFile(SPLIT_APK_DM + ".fsv_sig",
+ BASE_APK + ".fsv_sig")
+ .runExpectingFailure();
+ }
+
+ @Test
+ public void testInstallBaseWithSplit()
+ throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFileAndSignature(BASE_APK)
+ .addFileAndSignature(SPLIT_APK)
+ .run();
+ assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
+
+ verifyInstalledFiles(
+ INSTALLED_BASE_APK,
+ INSTALLED_BASE_APK_FSV_SIG,
+ INSTALLED_SPLIT_APK,
+ INSTALLED_SPLIT_APK_FSV_SIG);
+ verifyInstalledFilesHaveFsverity();
+ }
+
+ @Test
+ public void testInstallBaseWithDm() throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFileAndSignature(BASE_APK)
+ .addFileAndSignature(BASE_APK_DM)
+ .run();
+ assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
+
+ verifyInstalledFiles(
+ INSTALLED_BASE_APK,
+ INSTALLED_BASE_APK_FSV_SIG,
+ INSTALLED_BASE_DM,
+ INSTALLED_BASE_DM_FSV_SIG);
+ verifyInstalledFilesHaveFsverity();
+ }
+
+ @Test
+ public void testInstallEverything() throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFileAndSignature(BASE_APK)
+ .addFileAndSignature(BASE_APK_DM)
+ .addFileAndSignature(SPLIT_APK)
+ .addFileAndSignature(SPLIT_APK_DM)
+ .run();
+ assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
+
+ verifyInstalledFiles(
+ INSTALLED_BASE_APK,
+ INSTALLED_BASE_APK_FSV_SIG,
+ INSTALLED_BASE_DM,
+ INSTALLED_BASE_DM_FSV_SIG,
+ INSTALLED_SPLIT_APK,
+ INSTALLED_SPLIT_APK_FSV_SIG,
+ INSTALLED_SPLIT_DM,
+ INSTALLED_SPLIT_DM_FSV_SIG);
+ verifyInstalledFilesHaveFsverity();
+ }
+
+ @Test
+ public void testInstallSplitOnly()
+ throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFileAndSignature(BASE_APK)
+ .run();
+ assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
+ verifyInstalledFiles(
+ INSTALLED_BASE_APK,
+ INSTALLED_BASE_APK_FSV_SIG);
+
+ new InstallMultiple()
+ .inheritFrom(TARGET_PACKAGE)
+ .addFileAndSignature(SPLIT_APK)
+ .run();
+
+ verifyInstalledFiles(
+ INSTALLED_BASE_APK,
+ INSTALLED_BASE_APK_FSV_SIG,
+ INSTALLED_SPLIT_APK,
+ INSTALLED_SPLIT_APK_FSV_SIG);
+ verifyInstalledFilesHaveFsverity();
+ }
+
+ @Test
+ public void testInstallSplitOnlyMissingSignature()
+ throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFileAndSignature(BASE_APK)
+ .run();
+ assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
+ verifyInstalledFiles(
+ INSTALLED_BASE_APK,
+ INSTALLED_BASE_APK_FSV_SIG);
+
+ new InstallMultiple()
+ .inheritFrom(TARGET_PACKAGE)
+ .addFile(SPLIT_APK)
+ .runExpectingFailure();
+ }
+
+ @Test
+ public void testInstallSplitOnlyWithoutBaseSignature()
+ throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFile(BASE_APK)
+ .run();
+ assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
+ verifyInstalledFiles(INSTALLED_BASE_APK);
+
+ new InstallMultiple()
+ .inheritFrom(TARGET_PACKAGE)
+ .addFileAndSignature(SPLIT_APK)
+ .run();
+ verifyInstalledFiles(
+ INSTALLED_BASE_APK,
+ INSTALLED_SPLIT_APK,
+ INSTALLED_SPLIT_APK_FSV_SIG);
+
+ }
+
+ @Test
+ public void testInstallOnlyBaseHasFsvSig()
+ throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFileAndSignature(BASE_APK)
+ .addFile(BASE_APK_DM)
+ .addFile(SPLIT_APK)
+ .addFile(SPLIT_APK_DM)
+ .runExpectingFailure();
+ }
+
+ @Test
+ public void testInstallOnlyDmHasFsvSig()
+ throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFile(BASE_APK)
+ .addFileAndSignature(BASE_APK_DM)
+ .addFile(SPLIT_APK)
+ .addFile(SPLIT_APK_DM)
+ .runExpectingFailure();
+ }
+
+ @Test
+ public void testInstallOnlySplitHasFsvSig()
+ throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFile(BASE_APK)
+ .addFile(BASE_APK_DM)
+ .addFileAndSignature(SPLIT_APK)
+ .addFile(SPLIT_APK_DM)
+ .runExpectingFailure();
+ }
+
+ @Test
+ public void testInstallBaseWithFsvSigThenSplitWithout()
+ throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFileAndSignature(BASE_APK)
+ .run();
+ assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
+ verifyInstalledFiles(
+ INSTALLED_BASE_APK,
+ INSTALLED_BASE_APK_FSV_SIG);
+
+ new InstallMultiple()
+ .addFile(SPLIT_APK)
+ .runExpectingFailure();
+ }
+
+ @Test
+ public void testInstallBaseWithoutFsvSigThenSplitWith()
+ throws DeviceNotAvailableException, FileNotFoundException {
+ new InstallMultiple()
+ .addFile(BASE_APK)
+ .run();
+ assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
+ verifyInstalledFiles(INSTALLED_BASE_APK);
+
+ new InstallMultiple()
+ .addFileAndSignature(SPLIT_APK)
+ .runExpectingFailure();
+ }
+
+ @Test
+ public void testFsverityFileIsImmutableAndReadable() throws DeviceNotAvailableException {
+ new InstallMultiple().addFileAndSignature(BASE_APK).run();
+ String apkPath = getApkPath(TARGET_PACKAGE);
+
+ assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
+ expectRemoteCommandToFail("echo -n '' >> " + apkPath);
+ expectRemoteCommandToSucceed("cat " + apkPath + " > /dev/null");
+ }
+
+ @Test
+ public void testFsverityFailToReadModifiedBlockAtFront() throws DeviceNotAvailableException {
+ new InstallMultiple().addFileAndSignature(BASE_APK).run();
+ String apkPath = getApkPath(TARGET_PACKAGE);
+
+ long apkSize = getFileSizeInBytes(apkPath);
+ long offsetFirstByte = 0;
+
+ // The first two pages should be both readable at first.
+ assertTrue(canReadByte(apkPath, offsetFirstByte));
+ if (apkSize > offsetFirstByte + FSVERITY_PAGE_SIZE) {
+ assertTrue(canReadByte(apkPath, offsetFirstByte + FSVERITY_PAGE_SIZE));
+ }
+
+ // Damage the file directly against the block device.
+ damageFileAgainstBlockDevice(apkPath, offsetFirstByte);
+
+ // Expect actual read from disk to fail but only at damaged page.
+ dropCaches();
+ assertFalse(canReadByte(apkPath, offsetFirstByte));
+ if (apkSize > offsetFirstByte + FSVERITY_PAGE_SIZE) {
+ long lastByteOfTheSamePage =
+ offsetFirstByte % FSVERITY_PAGE_SIZE + FSVERITY_PAGE_SIZE - 1;
+ assertFalse(canReadByte(apkPath, lastByteOfTheSamePage));
+ assertTrue(canReadByte(apkPath, lastByteOfTheSamePage + 1));
+ }
+ }
+
+ @Test
+ public void testFsverityFailToReadModifiedBlockAtBack() throws DeviceNotAvailableException {
+ new InstallMultiple().addFileAndSignature(BASE_APK).run();
+ String apkPath = getApkPath(TARGET_PACKAGE);
+
+ long apkSize = getFileSizeInBytes(apkPath);
+ long offsetOfLastByte = apkSize - 1;
+
+ // The first two pages should be both readable at first.
+ assertTrue(canReadByte(apkPath, offsetOfLastByte));
+ if (offsetOfLastByte - FSVERITY_PAGE_SIZE > 0) {
+ assertTrue(canReadByte(apkPath, offsetOfLastByte - FSVERITY_PAGE_SIZE));
+ }
+
+ // Damage the file directly against the block device.
+ damageFileAgainstBlockDevice(apkPath, offsetOfLastByte);
+
+ // Expect actual read from disk to fail but only at damaged page.
+ dropCaches();
+ assertFalse(canReadByte(apkPath, offsetOfLastByte));
+ if (offsetOfLastByte - FSVERITY_PAGE_SIZE > 0) {
+ long firstByteOfTheSamePage = offsetOfLastByte - offsetOfLastByte % FSVERITY_PAGE_SIZE;
+ assertFalse(canReadByte(apkPath, firstByteOfTheSamePage));
+ assertTrue(canReadByte(apkPath, firstByteOfTheSamePage - 1));
+ }
+ }
+
+ private void verifyInstalledFilesHaveFsverity() throws DeviceNotAvailableException {
+ // Verify that all files are protected by fs-verity
+ String apkPath = getApkPath(TARGET_PACKAGE);
+ String appDir = apkPath.substring(0, apkPath.lastIndexOf("/"));
+ long kTargetOffset = 0;
+ for (String basename : expectRemoteCommandToSucceed("ls " + appDir).split("\n")) {
+ if (basename.endsWith(".apk") || basename.endsWith(".dm")) {
+ String path = appDir + "/" + basename;
+ damageFileAgainstBlockDevice(path, kTargetOffset);
+
+ // Retry is sometimes needed to pass the test. Package manager may have FD leaks
+ // (see b/122744005 as example) that prevents the file in question to be evicted
+ // from filesystem cache. Forcing GC workarounds the problem.
+ int retry = 5;
+ for (; retry > 0; retry--) {
+ dropCaches();
+ if (!canReadByte(path, kTargetOffset)) {
+ break;
+ }
+ try {
+ Thread.sleep(1000);
+ String pid = expectRemoteCommandToSucceed("pidof system_server");
+ mDevice.executeShellV2Command("kill -10 " + pid); // force GC
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return;
+ }
+ }
+ assertTrue("Read from " + path + " should fail", retry > 0);
+ }
+ }
+ }
+
+ private void verifyInstalledFiles(String... filenames) throws DeviceNotAvailableException {
+ String apkPath = getApkPath(TARGET_PACKAGE);
+ String appDir = apkPath.substring(0, apkPath.lastIndexOf("/"));
+ HashSet<String> actualFiles = new HashSet<>(Arrays.asList(
+ expectRemoteCommandToSucceed("ls " + appDir).split("\n")));
+ assertTrue(actualFiles.remove("lib"));
+ assertTrue(actualFiles.remove("oat"));
+
+ HashSet<String> expectedFiles = new HashSet<>(Arrays.asList(filenames));
+ assertEquals(expectedFiles, actualFiles);
+ }
+
+ private void damageFileAgainstBlockDevice(String path, long offsetOfTargetingByte)
+ throws DeviceNotAvailableException {
+ assertTrue(path.startsWith("/data/"));
+ ITestDevice.MountPointInfo mountPoint = mDevice.getMountPointInfo("/data");
+ ArrayList<String> args = new ArrayList<>();
+ args.add(DAMAGING_EXECUTABLE);
+ if ("f2fs".equals(mountPoint.type)) {
+ args.add("--use-f2fs-pinning");
+ }
+ args.add(mountPoint.filesystem);
+ args.add(path);
+ args.add(Long.toString(offsetOfTargetingByte));
+ expectRemoteCommandToSucceed(String.join(" ", args));
+ }
+
+ private String getApkPath(String packageName) throws DeviceNotAvailableException {
+ String line = expectRemoteCommandToSucceed("pm path " + packageName + " | grep base.apk");
+ int index = line.trim().indexOf(":");
+ assertTrue(index >= 0);
+ return line.substring(index + 1);
+ }
+
+ private long getFileSizeInBytes(String packageName) throws DeviceNotAvailableException {
+ return Long.parseLong(expectRemoteCommandToSucceed("stat -c '%s' " + packageName).trim());
+ }
+
+ private void dropCaches() throws DeviceNotAvailableException {
+ expectRemoteCommandToSucceed("sync && echo 1 > /proc/sys/vm/drop_caches");
+ }
+
+ private boolean canReadByte(String filePath, long offset) throws DeviceNotAvailableException {
+ CommandResult result = mDevice.executeShellV2Command(
+ "dd if=" + filePath + " bs=1 count=1 skip=" + Long.toString(offset));
+ return result.getStatus() == CommandStatus.SUCCESS;
+ }
+
+ private String expectRemoteCommandToSucceed(String cmd) throws DeviceNotAvailableException {
+ CommandResult result = mDevice.executeShellV2Command(cmd);
+ assertEquals("`" + cmd + "` failed: " + result.getStderr(), CommandStatus.SUCCESS,
+ result.getStatus());
+ return result.getStdout();
+ }
+
+ private void expectRemoteCommandToFail(String cmd) throws DeviceNotAvailableException {
+ CommandResult result = mDevice.executeShellV2Command(cmd);
+ assertTrue("Unexpected success from `" + cmd + "`: " + result.getStderr(),
+ result.getStatus() != CommandStatus.SUCCESS);
+ }
+
+ private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
+ InstallMultiple() {
+ super(getDevice(), getBuild());
+ }
+
+ InstallMultiple addFileAndSignature(String filename) {
+ try {
+ addFile(filename);
+ addFile(filename + ".fsv_sig");
+ } catch (FileNotFoundException e) {
+ fail("Missing test file: " + e);
+ }
+ return this;
+ }
+ }
+}
diff --git a/tests/ApkVerityTest/src/com/android/apkverity/BaseInstallMultiple.java b/tests/ApkVerityTest/src/com/android/apkverity/BaseInstallMultiple.java
new file mode 100644
index 000000000000..02e73d157dde
--- /dev/null
+++ b/tests/ApkVerityTest/src/com/android/apkverity/BaseInstallMultiple.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.apkverity;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Base class for invoking the install-multiple command via ADB. Subclass this for less typing:
+ *
+ * <code> private class InstallMultiple extends BaseInstallMultiple&lt;InstallMultiple&gt; { public
+ * InstallMultiple() { super(getDevice(), null); } } </code>
+ */
+/*package*/ class BaseInstallMultiple<T extends BaseInstallMultiple<?>> {
+
+ private final ITestDevice mDevice;
+ private final IBuildInfo mBuild;
+
+ private final List<String> mArgs = new ArrayList<>();
+ private final Map<File, String> mFileToRemoteMap = new HashMap<>();
+
+ /*package*/ BaseInstallMultiple(ITestDevice device, IBuildInfo buildInfo) {
+ mDevice = device;
+ mBuild = buildInfo;
+ addArg("-g");
+ }
+
+ T addArg(String arg) {
+ mArgs.add(arg);
+ return (T) this;
+ }
+
+ T addFile(String filename) throws FileNotFoundException {
+ return addFile(filename, filename);
+ }
+
+ T addFile(String filename, String remoteName) throws FileNotFoundException {
+ CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild);
+ mFileToRemoteMap.put(buildHelper.getTestFile(filename), remoteName);
+ return (T) this;
+ }
+
+ T inheritFrom(String packageName) {
+ addArg("-r");
+ addArg("-p " + packageName);
+ return (T) this;
+ }
+
+ void run() throws DeviceNotAvailableException {
+ run(true);
+ }
+
+ void runExpectingFailure() throws DeviceNotAvailableException {
+ run(false);
+ }
+
+ private void run(boolean expectingSuccess) throws DeviceNotAvailableException {
+ final ITestDevice device = mDevice;
+
+ // Create an install session
+ final StringBuilder cmd = new StringBuilder();
+ cmd.append("pm install-create");
+ for (String arg : mArgs) {
+ cmd.append(' ').append(arg);
+ }
+
+ String result = device.executeShellCommand(cmd.toString());
+ TestCase.assertTrue(result, result.startsWith("Success"));
+
+ final int start = result.lastIndexOf("[");
+ final int end = result.lastIndexOf("]");
+ int sessionId = -1;
+ try {
+ if (start != -1 && end != -1 && start < end) {
+ sessionId = Integer.parseInt(result.substring(start + 1, end));
+ }
+ } catch (NumberFormatException e) {
+ throw new IllegalStateException("Failed to parse install session: " + result);
+ }
+ if (sessionId == -1) {
+ throw new IllegalStateException("Failed to create install session: " + result);
+ }
+
+ // Push our files into session. Ideally we'd use stdin streaming,
+ // but ddmlib doesn't support it yet.
+ for (final Map.Entry<File, String> entry : mFileToRemoteMap.entrySet()) {
+ final File file = entry.getKey();
+ final String remoteName = entry.getValue();
+ final String remotePath = "/data/local/tmp/" + file.getName();
+ if (!device.pushFile(file, remotePath)) {
+ throw new IllegalStateException("Failed to push " + file);
+ }
+
+ cmd.setLength(0);
+ cmd.append("pm install-write");
+ cmd.append(' ').append(sessionId);
+ cmd.append(' ').append(remoteName);
+ cmd.append(' ').append(remotePath);
+
+ result = device.executeShellCommand(cmd.toString());
+ TestCase.assertTrue(result, result.startsWith("Success"));
+ }
+
+ // Everything staged; let's pull trigger
+ cmd.setLength(0);
+ cmd.append("pm install-commit");
+ cmd.append(' ').append(sessionId);
+
+ result = device.executeShellCommand(cmd.toString());
+ if (expectingSuccess) {
+ TestCase.assertTrue(result, result.contains("Success"));
+ } else {
+ TestCase.assertFalse(result, result.contains("Success"));
+ }
+ }
+}
diff --git a/tests/ApkVerityTest/testdata/Android.bp b/tests/ApkVerityTest/testdata/Android.bp
new file mode 100644
index 000000000000..c10b0cef21d7
--- /dev/null
+++ b/tests/ApkVerityTest/testdata/Android.bp
@@ -0,0 +1,77 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+filegroup {
+ name: "ApkVerityTestKeyPem",
+ srcs: ["ApkVerityTestKey.pem"],
+}
+
+filegroup {
+ name: "ApkVerityTestCertPem",
+ srcs: ["ApkVerityTestCert.pem"],
+}
+
+filegroup {
+ name: "ApkVerityTestCertDer",
+ srcs: ["ApkVerityTestCert.der"],
+}
+
+filegroup {
+ name: "ApkVerityTestAppDm",
+ srcs: ["ApkVerityTestApp.dm"],
+}
+
+filegroup {
+ name: "ApkVerityTestAppSplitDm",
+ srcs: ["ApkVerityTestAppSplit.dm"],
+}
+
+genrule_defaults {
+ name: "apk_verity_sig_gen_default",
+ tools: ["fsverity"],
+ tool_files: [":ApkVerityTestKeyPem", ":ApkVerityTestCertPem"],
+ cmd: "$(location fsverity) sign $(in) $(out) " +
+ "--key=$(location :ApkVerityTestKeyPem) " +
+ "--cert=$(location :ApkVerityTestCertPem) " +
+ "> /dev/null",
+}
+
+genrule {
+ name: "ApkVerityTestAppFsvSig",
+ defaults: ["apk_verity_sig_gen_default"],
+ srcs: [":ApkVerityTestApp"],
+ out: ["ApkVerityTestApp.apk.fsv_sig"],
+}
+
+genrule {
+ name: "ApkVerityTestAppDmFsvSig",
+ defaults: ["apk_verity_sig_gen_default"],
+ srcs: [":ApkVerityTestAppDm"],
+ out: ["ApkVerityTestApp.dm.fsv_sig"],
+}
+
+genrule {
+ name: "ApkVerityTestAppSplitFsvSig",
+ defaults: ["apk_verity_sig_gen_default"],
+ srcs: [":ApkVerityTestAppSplit"],
+ out: ["ApkVerityTestAppSplit.apk.fsv_sig"],
+}
+
+genrule {
+ name: "ApkVerityTestAppSplitDmFsvSig",
+ defaults: ["apk_verity_sig_gen_default"],
+ srcs: [":ApkVerityTestAppSplitDm"],
+ out: ["ApkVerityTestAppSplit.dm.fsv_sig"],
+}
+
diff --git a/tests/ApkVerityTest/testdata/ApkVerityTestApp.dm b/tests/ApkVerityTest/testdata/ApkVerityTestApp.dm
new file mode 100644
index 000000000000..e53a86131366
--- /dev/null
+++ b/tests/ApkVerityTest/testdata/ApkVerityTestApp.dm
Binary files differ
diff --git a/tests/ApkVerityTest/testdata/ApkVerityTestAppSplit.dm b/tests/ApkVerityTest/testdata/ApkVerityTestAppSplit.dm
new file mode 100644
index 000000000000..75396f1ba730
--- /dev/null
+++ b/tests/ApkVerityTest/testdata/ApkVerityTestAppSplit.dm
Binary files differ
diff --git a/tests/ApkVerityTest/testdata/ApkVerityTestCert.der b/tests/ApkVerityTest/testdata/ApkVerityTestCert.der
new file mode 100644
index 000000000000..fe9029b53aa1
--- /dev/null
+++ b/tests/ApkVerityTest/testdata/ApkVerityTestCert.der
Binary files differ
diff --git a/tests/ApkVerityTest/testdata/ApkVerityTestCert.pem b/tests/ApkVerityTest/testdata/ApkVerityTestCert.pem
new file mode 100644
index 000000000000..6c0b7b1f635a
--- /dev/null
+++ b/tests/ApkVerityTest/testdata/ApkVerityTestCert.pem
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIFLjCCAxagAwIBAgIJAKZbtMlZZwtdMA0GCSqGSIb3DQEBCwUAMCwxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJDQTEQMA4GA1UECgwHQW5kcm9pZDAeFw0xODEyMTky
+MTA5MzVaFw0xOTAxMTgyMTA5MzVaMCwxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJD
+QTEQMA4GA1UECgwHQW5kcm9pZDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
+ggIBAKnrw4WiFgFBq6vXqcLc97iwvcYPZmeIjQqYRF+CHwXBXx8IyDlMfPrgyIYo
+ZLkosnUK/Exuypdu6UEtdqtYPknC6w9z4YkxqsKtyxyB1b13ptcTHh3bf2N8bqGr
+8gWWLxj0QjumCtFi7Z/TCwB5t3b3gtC+0jVfABSWrm5PNkgk7jIP+4KeYLDCDfiJ
+XH3uHu6OASiSHTOnrmLWSaSw0y6G4OFthHqQnMywasly0r6m+Mif+K0ZUV7hBRi/
+SfqcJ1HTCXTJMskEyV6Qx2sHF/VbK2gdUv56z6OVRNSs/FxPBiWVMuZZKh1FpBVI
+gbGxusf2Awwtc+Soxr4/P1YFcrwfA/ff9FK3Yg/Cd3ZMGbzUkbEMEkE5BW7Gbjmx
+wz3mYTiRfa2L/Bl4MiMqNi0tfORLkmg+V/EItzfhZ/HsXMOCBsnuj4KnFslmbamz
+t9opypj2JLGk+lXhZ5gFNFw8tYH1AnG1AIXe5u+6Fq2nQ1y/ncGUTR5Sw4de/Gee
+C0UgR+KiFEdKupMKbXgSKl+0QPz/i2eSpcDOKMwZ4WiNrkbccbCyr38so+j5DfWF
+IeZA9a/IlysA6G8yU2TfXBc65VCIEQRJOQdUOZFDO8OSoqGP+fbA6edpmovGw+TH
+sM/NkmpEXpQm7BVOI4oVjdf4pKPp0zaW2YcaA3xU2w6eF17pAgMBAAGjUzBRMB0G
+A1UdDgQWBBRGpHYy7yiLEYalGuF1va6zJKGD/zAfBgNVHSMEGDAWgBRGpHYy7yiL
+EYalGuF1va6zJKGD/zAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IC
+AQAao6ZBM122F0pYb2QLahIyyGEr3LfSdBGID4068pVik4ncIefFz36Xf9AFxRQd
+KHmwRYNPHiLRIEGdtqplC5pZDeHz41txIArNIZKzDWOYtdcFyCz8umuj912BmsoM
+YUQhT6F1sX53SWcKxEP/aJ2kltSlPFX99e3Vx9eRkceV1oe2NM6ZG8hnYCfCAMeJ
+jRTpbqCGaAsEHFtIx6wt3zEtUXIVg4aYFQs/qjTjeP8ByIj0b4lZrceEoTeRimuj
++4aAI+jBxLkwaN3hseQHzRNpgPehIVV/0RU92yzOD/WN4YwE6rwjKEI1lihHNBDa
++DwGtGbHmIUzjW1qArig+mzUIhfYIJAxrx20ynPz/Q+C7+iXhTDAYQlxTle0pX8m
+yM2DUdPo97eLOzQ4JDHxtcN3ntTEJKKvrmzKvWuxy/yoLwS7MtLH6RETTHabH3Qd
+CP83X7z8zTyxgPxHdfHo9sgR/4C9RHGJx4OpBTQaiqfjSpDqJSIQdbrHGOQDgYwL
+KQyiQuhukmNgRCB6dJoZJ/MyaNuMsXV9QobsDHW1oSuCvPAihVoWHJxt8m4Ma0jJ
+EIbEPT2Umw1F/P+CeXnVQwhPvzQKHCa+6cC/YdjTqIKLmQV8X3HUBUIMhP2JGDic
+MnUipTm/RwWZVOjCJaFqk5sVq3L0Lyd0XVUWSK1a4IcrsA==
+-----END CERTIFICATE-----
diff --git a/tests/ApkVerityTest/testdata/ApkVerityTestKey.pem b/tests/ApkVerityTest/testdata/ApkVerityTestKey.pem
new file mode 100644
index 000000000000..f0746c162421
--- /dev/null
+++ b/tests/ApkVerityTest/testdata/ApkVerityTestKey.pem
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQCp68OFohYBQaur
+16nC3Pe4sL3GD2ZniI0KmERfgh8FwV8fCMg5THz64MiGKGS5KLJ1CvxMbsqXbulB
+LXarWD5JwusPc+GJMarCrcscgdW9d6bXEx4d239jfG6hq/IFli8Y9EI7pgrRYu2f
+0wsAebd294LQvtI1XwAUlq5uTzZIJO4yD/uCnmCwwg34iVx97h7ujgEokh0zp65i
+1kmksNMuhuDhbYR6kJzMsGrJctK+pvjIn/itGVFe4QUYv0n6nCdR0wl0yTLJBMle
+kMdrBxf1WytoHVL+es+jlUTUrPxcTwYllTLmWSodRaQVSIGxsbrH9gMMLXPkqMa+
+Pz9WBXK8HwP33/RSt2IPwnd2TBm81JGxDBJBOQVuxm45scM95mE4kX2ti/wZeDIj
+KjYtLXzkS5JoPlfxCLc34Wfx7FzDggbJ7o+CpxbJZm2ps7faKcqY9iSxpPpV4WeY
+BTRcPLWB9QJxtQCF3ubvuhatp0Ncv53BlE0eUsOHXvxnngtFIEfiohRHSrqTCm14
+EipftED8/4tnkqXAzijMGeFoja5G3HGwsq9/LKPo+Q31hSHmQPWvyJcrAOhvMlNk
+31wXOuVQiBEESTkHVDmRQzvDkqKhj/n2wOnnaZqLxsPkx7DPzZJqRF6UJuwVTiOK
+FY3X+KSj6dM2ltmHGgN8VNsOnhde6QIDAQABAoICAGT21tWnisWyXKwd2BwWKgeO
+1SRDcEiihZO/CBlr+rzzum55TGdngHedauj0RW0Ttn3/SgysZCp415ZHylRjeZdg
+f0VOSLu5TEqi86X7q6IJ35O6I1IAY4AcpqvfvE3/f/qm4FgLADCMRL+LqeTdbdr9
+lLguOj9GNIkHQ5v96zYQ44vRnVNugetlUuHT1KZq/+wlaqDNuRZBU0gdJeL6wnDJ
+6gNojKg7F0A0ry8F0B1Cn16uVxebjJMAx4N93hpQALkI2XyQNGHnOzO6eROqQl0i
+j/csPW1CUfBUOHLaWpUKy483SOhAINsFz0pqK84G2gIItqTcuRksA/N1J1AYqqQO
++/8IK5Mb9j0RaYYrBG83luGCWYauAsWg2Yol6fUGju8IY/zavOaES42XogY588Ad
+JzW+njjxXcnoD/u5keWrGwbPdGfoaLLg4eMlRBT4yNicyT04knXjFG4QTfLY5lF/
+VKdvZk6RMoCLdAtgN6EKHtcwuoYR967otsbavshngZ9HE/ic5/TdNFCBjxs6q9bm
+docC4CLHU/feXvOCYSnIfUpDzEPV96Gbk6o0qeYn3RUSGzRpXQHxXXfszEESUWnd
+2rtfXxqA7C5n8CshBfKJND7/LKRGpBRaYWJtc4hFmo8prhXfOb40PEZNlx8mcsEz
+WYZpmvFQHU8+bZIm0a5RAoIBAQDaCAje9xLKN1CYzygA/U3x2CsGuWWyh9xM1oR5
+5t+nn0EeIMrzGuHrD4hdbZiTiJcO5dpSg/3dssc/QLJEdv+BoMEgSYTf3TX03dIb
+kSlj+ONobejO4nVoUP0axTvVe/nuMYvLguTM6OCFvgV752TFxVyVHl6RM+rQYGCl
+ajbBCsCRg4QgpZ/RHWf+3KMJunzwWBlsAXcjOudneYqEl713h/q1lc5cONIglQDU
+E+bc5q6q++c/H8dYaWq4QE4CQU8wsq77/bZk8z1jheOV0HkwaH5ShtKD7bk/4MA9
+jWQUDW6/LRXkNiPExsAZxnP3mxhtUToWq1nedF6kPmNBko+9AoIBAQDHgvAql6i7
+osTYUcY/GldPmnrvfqbKmD0nI8mnaJfN2vGwjB/ol3lm+pviKIQ4ER80xsdn4xK0
+2cC9OdkY2UX7zilKohxvKVsbVOYpUwfpoRQO1Euddb6iAMqgGDyDzRBDTzTx5pB5
+XL9B/XuJVCMkjsNdD9iEbjdY1Epv7kYf53zfvrXdqv24uSNAszPFBLLPHSC9yONE
+a/t3mHGZ2cjr52leGNGY7ib6GNGBUeA34SM9g97tU9pAgy712RfZhH6fA93CLk6T
+DKoch56YId71vZt2J0Lrk4TWnnpidSoRmzKfVIJwjCmgYbI+2eDp7h0Z0DnDbji6
+9BPt3RWsoZidAoIBAA2A7+O3U7+Ye3JraiPdjGVNKSUKeIT9KyTLKHtQVEvSbjsK
+dudlo9ZmKOD4d7mzfP+cNtBjgmanuvVs8V2SLTL/HNb+Fq+yyLO4xVmVvQWHFbaT
+EBc4KWNjmLl+u7z2J72b7feVzMvwJG/EHBzXcQNavOgzcFH38DQls/aqxGdiXhjl
+F1raRzKxao57ZdGlbjWIj1KEKLfS3yAmg/DAYSi1EE8MzzIhBsqjz+BStzq5Qtou
+Ld1X/4W3SbfNq8cx+lCe0H2k8hYAhq3STg0qU0cvQZuk5Abtw0p0hhOJ3UfsqQ5I
+IZH31HFMiftOskIEphenLzzWMgO4G2B6yLT3+dUCggEAOLF1i7Ti5sbfBtVd70qN
+6vnr2yhzPvi5z+h0ghTPpliD+3YmDxMUFXY7W63FvKTo6DdgLJ4zD58dDOhmT5BW
+ObKguyuLxu7Ki965NJ76jaIPMBOVlR4DWMe+zHV2pMFd0LKuSdsJzOLVGmxscV6u
+SdIjo8s/7InhQmW47UuZM7G1I2NvDJltVdOON/F0UZT/NqmBR0zRf/zrTVXNWjmv
+xZFRuMJ2tO1fuAvbZNMeUuKv/+f8LhZ424IrkwLoqw/iZ09S8b306AZeRJMpNvPR
+BqWlipKnioe15MLN5jKDDNO8M9hw5Ih/v6pjW0bQicj3DgHEmEs25bE8BIihgxe8
+ZQKCAQEAsWKsUv13OEbYYAoJgbzDesWF9NzamFB0NLyno9SChvFPH/d8RmAuti7Y
+BQUoBswLK24DF/TKf1YocsZq8tu+pnv0Nx1wtK4K+J3A1BYDm7ElpO3Km+HPUBtf
+C9KGT5hotlMQVTpYSDG/QeWbfl4UnNZcbg8pmv38NwV1eDoVDfaVrRYJzQn75+Tf
+s/WUq1x5PElR/4pNIU2i6pJGd6FimhRweJu/INR36spWmbMRNX8fyXx+9EBqMbVp
+vS2xGgxxQT6bAvBfRlpgi87T9v5Gqoy6/jM/wX9smH9PfUV1vK32n3Zrbd46gwZW
+p2aUlQOLXU9SjQTirZbdCZP0XHtFsg==
+-----END PRIVATE KEY-----
diff --git a/tests/ApkVerityTest/testdata/README.md b/tests/ApkVerityTest/testdata/README.md
new file mode 100644
index 000000000000..163cb183a5ad
--- /dev/null
+++ b/tests/ApkVerityTest/testdata/README.md
@@ -0,0 +1,13 @@
+This test only runs on rooted / debuggable device.
+
+The test tries to install subsets of base.{apk,dm}, split.{apk,dm} and their
+corresponding .fsv_sig files (generated by build rule). If installed, the
+tests also tries to tamper with the file at absolute disk offset to verify
+if fs-verity is effective.
+
+How to generate dex metadata (.dm)
+==================================
+
+ adb shell profman --generate-test-profile=/data/local/tmp/primary.prof
+ adb pull /data/local/tmp/primary.prof
+ zip foo.dm primary.prof
diff --git a/tests/BootImageProfileTest/AndroidTest.xml b/tests/BootImageProfileTest/AndroidTest.xml
index c13200778c4b..b4f2663585cc 100644
--- a/tests/BootImageProfileTest/AndroidTest.xml
+++ b/tests/BootImageProfileTest/AndroidTest.xml
@@ -22,8 +22,8 @@
<!-- we need this magic flag, otherwise it always reboots and breaks the selinux -->
<option name="force-skip-system-props" value="true" />
- <option name="run-command" value="setprop dalvik.vm.profilesystemserver true" />
- <option name="run-command" value="setprop dalvik.vm.profilebootclasspath true" />
+ <option name="run-command" value="device_config put runtime_native_boot profilesystemserver true" />
+ <option name="run-command" value="device_config put runtime_native_boot profilebootclasspath true" />
<!-- Profiling does not pick up the above changes we restart the shell -->
<option name="run-command" value="stop" />
diff --git a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
index 17986a3c9d61..ccdd452b3f1e 100644
--- a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
+++ b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
@@ -46,9 +46,11 @@ public class BootImageProfileTest implements IDeviceTest {
*/
@Test
public void testProperties() throws Exception {
- String res = mTestDevice.getProperty("dalvik.vm.profilebootclasspath");
+ String res = mTestDevice.getProperty(
+ "persist.device_config.runtime_native_boot.profilebootclasspath");
assertTrue("profile boot class path not enabled", res != null && res.equals("true"));
- res = mTestDevice.getProperty("dalvik.vm.profilesystemserver");
+ res = mTestDevice.getProperty(
+ "persist.device_config.runtime_native_boot.profilesystemserver");
assertTrue("profile system server not enabled", res != null && res.equals("true"));
}
@@ -66,10 +68,18 @@ public class BootImageProfileTest implements IDeviceTest {
String res;
res = mTestDevice.executeShellCommand("truncate -s 0 " + SYSTEM_SERVER_PROFILE).trim();
assertTrue(res, res.length() == 0);
- // Force save profiles in case the system just started.
+ // Wait up to 20 seconds for the profile to be saved.
+ for (int i = 0; i < 20; ++i) {
+ // Force save the profile since we truncated it.
+ forceSaveProfile("system_server");
+ String s = mTestDevice.executeShellCommand("wc -c <" + SYSTEM_SERVER_PROFILE).trim();
+ if (!"0".equals(s)) {
+ break;
+ }
+ Thread.sleep(1000);
+ }
+ // In case the profile is partially saved, wait an extra second.
Thread.sleep(1000);
- forceSaveProfile("system_server");
- Thread.sleep(2000);
// Validate that the profile is non empty.
res = mTestDevice.executeShellCommand("profman --dump-only --profile-file="
+ SYSTEM_SERVER_PROFILE);
@@ -84,5 +94,18 @@ public class BootImageProfileTest implements IDeviceTest {
}
assertTrue("Did not see framework.jar in " + res, sawFramework);
assertTrue("Did not see services.jar in " + res, sawServices);
+
+
+ // Test the profile contents contain common methods for core-oj that would normally be AOT
+ // compiled.
+ res = mTestDevice.executeShellCommand("profman --dump-classes-and-methods --profile-file="
+ + SYSTEM_SERVER_PROFILE + " --apk=/apex/com.android.art/javalib/core-oj.jar");
+ boolean sawObjectInit = false;
+ for (String line : res.split("\n")) {
+ if (line.contains("Ljava/lang/Object;-><init>()V")) {
+ sawObjectInit = true;
+ }
+ }
+ assertTrue("Did not see Object.<init> in " + res, sawObjectInit);
}
}
diff --git a/tests/Codegen/runTest.sh b/tests/Codegen/runTest.sh
index 614cbb7c9eb6..929f122e261e 100755
--- a/tests/Codegen/runTest.sh
+++ b/tests/Codegen/runTest.sh
@@ -13,11 +13,17 @@ else
header_and_eval m -j16 codegen_cli && \
header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java && \
header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java && \
- cd $ANDROID_BUILD_TOP &&
- header_and_eval mmma -j16 frameworks/base/tests/Codegen && \
- header_and_eval adb install -r -t $ANDROID_PRODUCT_OUT/testcases/CodegenTests/arm64/CodegenTests.apk && \
- # header_and_eval adb shell am set-debug-app -w com.android.codegentest && \
- header_and_eval adb shell am instrument -w -e package com.android.codegentest com.android.codegentest/androidx.test.runner.AndroidJUnitRunner
+ header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java && \
+ header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java && \
+ header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java && \
+ header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java && \
+ (
+ cd $ANDROID_BUILD_TOP &&
+ header_and_eval mmma -j16 frameworks/base/tests/Codegen && \
+ header_and_eval adb install -r -t "$(find $ANDROID_TARGET_OUT_TESTCASES -name 'CodegenTests.apk')" && \
+ # header_and_eval adb shell am set-debug-app -w com.android.codegentest && \
+ header_and_eval adb shell am instrument -w -e package com.android.codegentest com.android.codegentest/androidx.test.runner.AndroidJUnitRunner
+ )
exitCode=$?
diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.aidl b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.aidl
new file mode 100644
index 000000000000..ab62c83fc1b9
--- /dev/null
+++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.codegentest;
+
+parcelable HierrarchicalDataClassBase;
diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java
new file mode 100644
index 000000000000..325c1c09dd8c
--- /dev/null
+++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java
@@ -0,0 +1,108 @@
+/*
+ * 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.codegentest;
+
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * @see HierrarchicalDataClassChild
+ */
+@DataClass(
+ genConstructor = false,
+ genSetters = true)
+public class HierrarchicalDataClassBase implements Parcelable {
+
+ private int mBaseData;
+
+
+
+ // Code below generated by codegen v1.0.9.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ public int getBaseData() {
+ return mBaseData;
+ }
+
+ @DataClass.Generated.Member
+ public HierrarchicalDataClassBase setBaseData(int value) {
+ mBaseData = value;
+ return this;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@android.annotation.NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeInt(mBaseData);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ protected HierrarchicalDataClassBase(@android.annotation.NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int baseData = in.readInt();
+
+ this.mBaseData = baseData;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @android.annotation.NonNull Parcelable.Creator<HierrarchicalDataClassBase> CREATOR
+ = new Parcelable.Creator<HierrarchicalDataClassBase>() {
+ @Override
+ public HierrarchicalDataClassBase[] newArray(int size) {
+ return new HierrarchicalDataClassBase[size];
+ }
+
+ @Override
+ public HierrarchicalDataClassBase createFromParcel(@android.annotation.NonNull android.os.Parcel in) {
+ return new HierrarchicalDataClassBase(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1571258914826L,
+ codegenVersion = "1.0.9",
+ sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java",
+ inputSignatures = "private int mBaseData\nclass HierrarchicalDataClassBase extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genSetters=true)")
+ @Deprecated
+ private void __metadata() {}
+
+}
diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.aidl b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.aidl
new file mode 100644
index 000000000000..a0997222b0af
--- /dev/null
+++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.codegentest;
+
+parcelable HierrarchicalDataClassChild;
diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java
new file mode 100644
index 000000000000..6c92009f8533
--- /dev/null
+++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java
@@ -0,0 +1,130 @@
+/*
+ * 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.codegentest;
+
+import android.annotation.NonNull;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * An example of data classes that extend one another.
+ *
+ * Note that some features like constructor generation might not work well due to lack of
+ * information about the superclass when generating code for subclass.
+ *
+ * It is recommended to avoid inheritance in favor of composition for new data classes,
+ * particularly parcelable ones.
+ *
+ * However for legacy classes or where inheritance is desired for allocation efficiency,
+ * you can either use a technique from this example, opting for mutability/setters, or just write
+ * constructors by hand.
+ *
+ * @see HierrarchicalDataClassBase
+ */
+@DataClass(
+ genParcelable = true,
+ genConstructor = false,
+ genSetters = true)
+public class HierrarchicalDataClassChild extends HierrarchicalDataClassBase {
+
+ private @NonNull String mChildData;
+
+
+
+ // Code below generated by codegen v1.0.9.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ public @NonNull String getChildData() {
+ return mChildData;
+ }
+
+ @DataClass.Generated.Member
+ public HierrarchicalDataClassChild setChildData(@NonNull String value) {
+ mChildData = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mChildData);
+ return this;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ super.writeToParcel(dest, flags);
+
+ dest.writeString(mChildData);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ protected HierrarchicalDataClassChild(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ super(in);
+
+ String childData = in.readString();
+
+ this.mChildData = childData;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mChildData);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<HierrarchicalDataClassChild> CREATOR
+ = new Parcelable.Creator<HierrarchicalDataClassChild>() {
+ @Override
+ public HierrarchicalDataClassChild[] newArray(int size) {
+ return new HierrarchicalDataClassChild[size];
+ }
+
+ @Override
+ public HierrarchicalDataClassChild createFromParcel(@NonNull android.os.Parcel in) {
+ return new HierrarchicalDataClassChild(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1571258915848L,
+ codegenVersion = "1.0.9",
+ sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java",
+ inputSignatures = "private @android.annotation.NonNull java.lang.String mChildData\nclass HierrarchicalDataClassChild extends com.android.codegentest.HierrarchicalDataClassBase implements []\n@com.android.internal.util.DataClass(genParcelable=true, genConstructor=false, genSetters=true)")
+ @Deprecated
+ private void __metadata() {}
+
+}
diff --git a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java
new file mode 100644
index 000000000000..36def8a8dfb1
--- /dev/null
+++ b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java
@@ -0,0 +1,420 @@
+/*
+ * 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.codegentest;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import com.android.internal.util.AnnotationValidations;
+import com.android.internal.util.DataClass;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Additional test for various parcelling corner-cases.
+ */
+@DataClass(
+ genBuilder = true,
+ genAidl = false,
+ genToString = true)
+public class ParcelAllTheThingsDataClass implements Parcelable {
+
+ @NonNull String[] mStringArray = null;
+ @NonNull int[] mIntArray = null;
+ @NonNull List<String> mStringList = null;
+
+ @NonNull Map<String, SampleWithCustomBuilder> mMap = null;
+ @NonNull Map<String, String> mStringMap = null;
+
+ @NonNull SparseArray<SampleWithCustomBuilder> mSparseArray = null;
+ @NonNull SparseIntArray mSparseIntArray = null;
+
+ @SuppressWarnings({"WeakerAccess"})
+ @Nullable Boolean mNullableBoolean = null;
+
+
+
+ // Code below generated by codegen v1.0.9.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ /* package-private */ ParcelAllTheThingsDataClass(
+ @NonNull String[] stringArray,
+ @NonNull int[] intArray,
+ @NonNull List<String> stringList,
+ @NonNull Map<String,SampleWithCustomBuilder> map,
+ @NonNull Map<String,String> stringMap,
+ @NonNull SparseArray<SampleWithCustomBuilder> sparseArray,
+ @NonNull SparseIntArray sparseIntArray,
+ @SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean nullableBoolean) {
+ this.mStringArray = stringArray;
+ AnnotationValidations.validate(
+ NonNull.class, null, mStringArray);
+ this.mIntArray = intArray;
+ AnnotationValidations.validate(
+ NonNull.class, null, mIntArray);
+ this.mStringList = stringList;
+ AnnotationValidations.validate(
+ NonNull.class, null, mStringList);
+ this.mMap = map;
+ AnnotationValidations.validate(
+ NonNull.class, null, mMap);
+ this.mStringMap = stringMap;
+ AnnotationValidations.validate(
+ NonNull.class, null, mStringMap);
+ this.mSparseArray = sparseArray;
+ AnnotationValidations.validate(
+ NonNull.class, null, mSparseArray);
+ this.mSparseIntArray = sparseIntArray;
+ AnnotationValidations.validate(
+ NonNull.class, null, mSparseIntArray);
+ this.mNullableBoolean = nullableBoolean;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull String[] getStringArray() {
+ return mStringArray;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull int[] getIntArray() {
+ return mIntArray;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull List<String> getStringList() {
+ return mStringList;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Map<String,SampleWithCustomBuilder> getMap() {
+ return mMap;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Map<String,String> getStringMap() {
+ return mStringMap;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull SparseArray<SampleWithCustomBuilder> getSparseArray() {
+ return mSparseArray;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull SparseIntArray getSparseIntArray() {
+ return mSparseIntArray;
+ }
+
+ @DataClass.Generated.Member
+ public @SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean getNullableBoolean() {
+ return mNullableBoolean;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "ParcelAllTheThingsDataClass { " +
+ "stringArray = " + java.util.Arrays.toString(mStringArray) + ", " +
+ "intArray = " + java.util.Arrays.toString(mIntArray) + ", " +
+ "stringList = " + mStringList + ", " +
+ "map = " + mMap + ", " +
+ "stringMap = " + mStringMap + ", " +
+ "sparseArray = " + mSparseArray + ", " +
+ "sparseIntArray = " + mSparseIntArray + ", " +
+ "nullableBoolean = " + mNullableBoolean +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ int flg = 0;
+ if (mNullableBoolean != null) flg |= 0x80;
+ dest.writeInt(flg);
+ dest.writeStringArray(mStringArray);
+ dest.writeIntArray(mIntArray);
+ dest.writeStringList(mStringList);
+ dest.writeMap(mMap);
+ dest.writeMap(mStringMap);
+ dest.writeSparseArray(mSparseArray);
+ dest.writeSparseIntArray(mSparseIntArray);
+ if (mNullableBoolean != null) dest.writeBoolean(mNullableBoolean);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ protected ParcelAllTheThingsDataClass(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int flg = in.readInt();
+ String[] stringArray = in.createStringArray();
+ int[] intArray = in.createIntArray();
+ List<String> stringList = new java.util.ArrayList<>();
+ in.readStringList(stringList);
+ Map<String,SampleWithCustomBuilder> map = new java.util.LinkedHashMap<>();
+ in.readMap(map, SampleWithCustomBuilder.class.getClassLoader());
+ Map<String,String> stringMap = new java.util.LinkedHashMap<>();
+ in.readMap(stringMap, String.class.getClassLoader());
+ SparseArray<SampleWithCustomBuilder> sparseArray = (SparseArray) in.readSparseArray(SampleWithCustomBuilder.class.getClassLoader());
+ SparseIntArray sparseIntArray = (SparseIntArray) in.readSparseIntArray();
+ Boolean nullableBoolean = (flg & 0x80) == 0 ? null : (Boolean) in.readBoolean();
+
+ this.mStringArray = stringArray;
+ AnnotationValidations.validate(
+ NonNull.class, null, mStringArray);
+ this.mIntArray = intArray;
+ AnnotationValidations.validate(
+ NonNull.class, null, mIntArray);
+ this.mStringList = stringList;
+ AnnotationValidations.validate(
+ NonNull.class, null, mStringList);
+ this.mMap = map;
+ AnnotationValidations.validate(
+ NonNull.class, null, mMap);
+ this.mStringMap = stringMap;
+ AnnotationValidations.validate(
+ NonNull.class, null, mStringMap);
+ this.mSparseArray = sparseArray;
+ AnnotationValidations.validate(
+ NonNull.class, null, mSparseArray);
+ this.mSparseIntArray = sparseIntArray;
+ AnnotationValidations.validate(
+ NonNull.class, null, mSparseIntArray);
+ this.mNullableBoolean = nullableBoolean;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<ParcelAllTheThingsDataClass> CREATOR
+ = new Parcelable.Creator<ParcelAllTheThingsDataClass>() {
+ @Override
+ public ParcelAllTheThingsDataClass[] newArray(int size) {
+ return new ParcelAllTheThingsDataClass[size];
+ }
+
+ @Override
+ public ParcelAllTheThingsDataClass createFromParcel(@NonNull Parcel in) {
+ return new ParcelAllTheThingsDataClass(in);
+ }
+ };
+
+ /**
+ * A builder for {@link ParcelAllTheThingsDataClass}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static class Builder {
+
+ private @NonNull String[] mStringArray;
+ private @NonNull int[] mIntArray;
+ private @NonNull List<String> mStringList;
+ private @NonNull Map<String,SampleWithCustomBuilder> mMap;
+ private @NonNull Map<String,String> mStringMap;
+ private @NonNull SparseArray<SampleWithCustomBuilder> mSparseArray;
+ private @NonNull SparseIntArray mSparseIntArray;
+ private @SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean mNullableBoolean;
+
+ private long mBuilderFieldsSet = 0L;
+
+ public Builder() {
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Builder setStringArray(@NonNull String... value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mStringArray = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Builder setIntArray(@NonNull int... value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mIntArray = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Builder setStringList(@NonNull List<String> value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mStringList = value;
+ return this;
+ }
+
+ /** @see #setStringList */
+ @DataClass.Generated.Member
+ public @NonNull Builder addStringList(String value) {
+ // You can refine this method's name by providing item's singular name, e.g.:
+ // @DataClass.PluralOf("item")) mItems = ...
+
+ if (mStringList == null) setStringList(new java.util.ArrayList<>());
+ mStringList.add(value);
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Builder setMap(@NonNull Map<String,SampleWithCustomBuilder> value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8;
+ mMap = value;
+ return this;
+ }
+
+ /** @see #setMap */
+ @DataClass.Generated.Member
+ public @NonNull Builder addMap(String key, SampleWithCustomBuilder value) {
+ // You can refine this method's name by providing item's singular name, e.g.:
+ // @DataClass.PluralOf("item")) mItems = ...
+
+ if (mMap == null) setMap(new java.util.LinkedHashMap());
+ mMap.put(key, value);
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Builder setStringMap(@NonNull Map<String,String> value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x10;
+ mStringMap = value;
+ return this;
+ }
+
+ /** @see #setStringMap */
+ @DataClass.Generated.Member
+ public @NonNull Builder addStringMap(String key, String value) {
+ // You can refine this method's name by providing item's singular name, e.g.:
+ // @DataClass.PluralOf("item")) mItems = ...
+
+ if (mStringMap == null) setStringMap(new java.util.LinkedHashMap());
+ mStringMap.put(key, value);
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Builder setSparseArray(@NonNull SparseArray<SampleWithCustomBuilder> value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x20;
+ mSparseArray = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Builder setSparseIntArray(@NonNull SparseIntArray value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x40;
+ mSparseIntArray = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Builder setNullableBoolean(@SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x80;
+ mNullableBoolean = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public ParcelAllTheThingsDataClass build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x100; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x1) == 0) {
+ mStringArray = null;
+ }
+ if ((mBuilderFieldsSet & 0x2) == 0) {
+ mIntArray = null;
+ }
+ if ((mBuilderFieldsSet & 0x4) == 0) {
+ mStringList = null;
+ }
+ if ((mBuilderFieldsSet & 0x8) == 0) {
+ mMap = null;
+ }
+ if ((mBuilderFieldsSet & 0x10) == 0) {
+ mStringMap = null;
+ }
+ if ((mBuilderFieldsSet & 0x20) == 0) {
+ mSparseArray = null;
+ }
+ if ((mBuilderFieldsSet & 0x40) == 0) {
+ mSparseIntArray = null;
+ }
+ if ((mBuilderFieldsSet & 0x80) == 0) {
+ mNullableBoolean = null;
+ }
+ ParcelAllTheThingsDataClass o = new ParcelAllTheThingsDataClass(
+ mStringArray,
+ mIntArray,
+ mStringList,
+ mMap,
+ mStringMap,
+ mSparseArray,
+ mSparseIntArray,
+ mNullableBoolean);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x100) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1571258913802L,
+ codegenVersion = "1.0.9",
+ sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java",
+ inputSignatures = " @android.annotation.NonNull java.lang.String[] mStringArray\n @android.annotation.NonNull int[] mIntArray\n @android.annotation.NonNull java.util.List<java.lang.String> mStringList\n @android.annotation.NonNull java.util.Map<java.lang.String,com.android.codegentest.SampleWithCustomBuilder> mMap\n @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.String> mStringMap\n @android.annotation.NonNull android.util.SparseArray<com.android.codegentest.SampleWithCustomBuilder> mSparseArray\n @android.annotation.NonNull android.util.SparseIntArray mSparseIntArray\n @java.lang.SuppressWarnings({\"WeakerAccess\"}) @android.annotation.Nullable java.lang.Boolean mNullableBoolean\nclass ParcelAllTheThingsDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)")
+ @Deprecated
+ private void __metadata() {}
+
+}
diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
index f0c5baad222b..c444d61a0fba 100644
--- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
+++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
@@ -342,14 +342,18 @@ public final class SampleDataClass implements Parcelable {
- // Code below generated by codegen v1.0.1.
+ // Code below generated by codegen v1.0.9.
//
// DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
//
- // CHECKSTYLE:OFF Generated code
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
@IntDef(prefix = "STATE_", value = {
STATE_UNDEFINED,
@@ -1115,7 +1119,7 @@ public final class SampleDataClass implements Parcelable {
@Override
@DataClass.Generated.Member
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
// You can override field equality logic by defining either of the methods like:
// boolean fieldNameEquals(SampleDataClass other) { ... }
// boolean fieldNameEquals(FieldType otherValue) { ... }
@@ -1180,8 +1184,8 @@ public final class SampleDataClass implements Parcelable {
@DataClass.Generated.Member
void forEachField(
- DataClass.PerIntFieldAction<SampleDataClass> actionInt,
- DataClass.PerObjectFieldAction<SampleDataClass> actionObject) {
+ @NonNull DataClass.PerIntFieldAction<SampleDataClass> actionInt,
+ @NonNull DataClass.PerObjectFieldAction<SampleDataClass> actionObject) {
actionInt.acceptInt(this, "num", mNum);
actionInt.acceptInt(this, "num2", mNum2);
actionInt.acceptInt(this, "num4", mNum4);
@@ -1207,7 +1211,7 @@ public final class SampleDataClass implements Parcelable {
/** @deprecated May cause boxing allocations - use with caution! */
@Deprecated
@DataClass.Generated.Member
- void forEachField(DataClass.PerObjectFieldAction<SampleDataClass> action) {
+ void forEachField(@NonNull DataClass.PerObjectFieldAction<SampleDataClass> action) {
action.acceptObject(this, "num", mNum);
action.acceptObject(this, "num2", mNum2);
action.acceptObject(this, "num4", mNum4);
@@ -1254,7 +1258,7 @@ public final class SampleDataClass implements Parcelable {
@Override
@DataClass.Generated.Member
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
@@ -1290,6 +1294,123 @@ public final class SampleDataClass implements Parcelable {
@DataClass.Generated.Member
public int describeContents() { return 0; }
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ SampleDataClass(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ long flg = in.readLong();
+ int num = in.readInt();
+ int num2 = in.readInt();
+ int num4 = in.readInt();
+ String name = (flg & 0x8) == 0 ? null : in.readString();
+ String name2 = in.readString();
+ String name4 = in.readString();
+ AccessibilityNodeInfo otherParcelable = (flg & 0x40) == 0 ? null : (AccessibilityNodeInfo) in.readTypedObject(AccessibilityNodeInfo.CREATOR);
+ Date date = sParcellingForDate.unparcel(in);
+ Pattern pattern = sParcellingForPattern.unparcel(in);
+ List<LinkAddress> linkAddresses2 = new ArrayList<>();
+ in.readParcelableList(linkAddresses2, LinkAddress.class.getClassLoader());
+ ArrayList<LinkAddress> linkAddresses = new ArrayList<>();
+ in.readParcelableList(linkAddresses, LinkAddress.class.getClassLoader());
+ LinkAddress[] linkAddresses4 = (flg & 0x800) == 0 ? null : (LinkAddress[]) in.createTypedArray(LinkAddress.CREATOR);
+ String stateName = in.readString();
+ int flags = in.readInt();
+ int state = in.readInt();
+ CharSequence _charSeq = (CharSequence) in.readCharSequence();
+ LinkAddress[] linkAddresses5 = (flg & 0x10000) == 0 ? null : (LinkAddress[]) in.createTypedArray(LinkAddress.CREATOR);
+ int stringRes = in.readInt();
+ int dayOfWeek = in.readInt();
+ float[] coords = in.createFloatArray();
+
+ this.mNum = num;
+ this.mNum2 = num2;
+ this.mNum4 = num4;
+ this.mName = name;
+ this.mName2 = name2;
+ AnnotationValidations.validate(
+ NonNull.class, null, mName2);
+ this.mName4 = name4;
+ AnnotationValidations.validate(
+ NonNull.class, null, mName4);
+ this.mOtherParcelable = otherParcelable;
+ this.mDate = date;
+ AnnotationValidations.validate(
+ NonNull.class, null, mDate);
+ this.mPattern = pattern;
+ AnnotationValidations.validate(
+ NonNull.class, null, mPattern);
+ this.mLinkAddresses2 = linkAddresses2;
+ AnnotationValidations.validate(
+ NonNull.class, null, mLinkAddresses2);
+ this.mLinkAddresses = linkAddresses;
+ AnnotationValidations.validate(
+ NonNull.class, null, mLinkAddresses);
+ this.mLinkAddresses4 = linkAddresses4;
+ this.mStateName = stateName;
+
+ if (!(Objects.equals(mStateName, STATE_NAME_UNDEFINED))
+ && !(Objects.equals(mStateName, STATE_NAME_ON))
+ && !(Objects.equals(mStateName, STATE_NAME_OFF))) {
+ throw new java.lang.IllegalArgumentException(
+ "stateName was " + mStateName + " but must be one of: "
+ + "STATE_NAME_UNDEFINED(" + STATE_NAME_UNDEFINED + "), "
+ + "STATE_NAME_ON(" + STATE_NAME_ON + "), "
+ + "STATE_NAME_OFF(" + STATE_NAME_OFF + ")");
+ }
+
+ AnnotationValidations.validate(
+ NonNull.class, null, mStateName);
+ this.mFlags = flags;
+
+ Preconditions.checkFlagsArgument(
+ mFlags,
+ FLAG_MANUAL_REQUEST
+ | FLAG_COMPATIBILITY_MODE_REQUEST
+ | FLAG_AUGMENTED_REQUEST);
+ this.mState = state;
+
+ if (!(mState == STATE_UNDEFINED)
+ && !(mState == STATE_ON)
+ && !(mState == STATE_OFF)) {
+ throw new java.lang.IllegalArgumentException(
+ "state was " + mState + " but must be one of: "
+ + "STATE_UNDEFINED(" + STATE_UNDEFINED + "), "
+ + "STATE_ON(" + STATE_ON + "), "
+ + "STATE_OFF(" + STATE_OFF + ")");
+ }
+
+ this.charSeq = _charSeq;
+ AnnotationValidations.validate(
+ NonNull.class, null, charSeq);
+ this.mLinkAddresses5 = linkAddresses5;
+ this.mStringRes = stringRes;
+ AnnotationValidations.validate(
+ StringRes.class, null, mStringRes);
+ this.mDayOfWeek = dayOfWeek;
+ AnnotationValidations.validate(
+ android.annotation.IntRange.class, null, mDayOfWeek,
+ "from", 0,
+ "to", 6);
+ this.mCoords = coords;
+ AnnotationValidations.validate(
+ Size.class, null, mCoords.length,
+ "value", 2);
+ AnnotationValidations.validate(
+ NonNull.class, null, mCoords);
+ int coordsSize = mCoords.length;
+ for (int i = 0; i < coordsSize; i++) {
+ AnnotationValidations.validate(
+ FloatRange.class, null, mCoords[i],
+ "from", 0f);
+ }
+
+
+ onConstructed();
+ }
+
@DataClass.Generated.Member
public static final @NonNull Parcelable.Creator<SampleDataClass> CREATOR
= new Parcelable.Creator<SampleDataClass>() {
@@ -1299,55 +1420,8 @@ public final class SampleDataClass implements Parcelable {
}
@Override
- @SuppressWarnings({"unchecked", "RedundantCast"})
- public SampleDataClass createFromParcel(Parcel in) {
- // You can override field unparcelling by defining methods like:
- // static FieldType unparcelFieldName(Parcel in) { ... }
-
- long flg = in.readLong();
- int num = in.readInt();
- int num2 = in.readInt();
- int num4 = in.readInt();
- String name = (flg & 0x8) == 0 ? null : in.readString();
- String name2 = in.readString();
- String name4 = in.readString();
- AccessibilityNodeInfo otherParcelable = (flg & 0x40) == 0 ? null : (AccessibilityNodeInfo) in.readTypedObject(AccessibilityNodeInfo.CREATOR);
- Date date = sParcellingForDate.unparcel(in);
- Pattern pattern = sParcellingForPattern.unparcel(in);
- List<LinkAddress> linkAddresses2 = new ArrayList<>();
- in.readParcelableList(linkAddresses2, LinkAddress.class.getClassLoader());
- ArrayList<LinkAddress> linkAddresses = new ArrayList<>();
- in.readParcelableList(linkAddresses, LinkAddress.class.getClassLoader());
- LinkAddress[] linkAddresses4 = (flg & 0x800) == 0 ? null : (LinkAddress[]) in.createTypedArray(LinkAddress.CREATOR);
- String stateName = in.readString();
- int flags = in.readInt();
- int state = in.readInt();
- CharSequence _charSeq = (CharSequence) in.readCharSequence();
- LinkAddress[] linkAddresses5 = (flg & 0x10000) == 0 ? null : (LinkAddress[]) in.createTypedArray(LinkAddress.CREATOR);
- int stringRes = in.readInt();
- int dayOfWeek = in.readInt();
- float[] coords = in.createFloatArray();
- return new SampleDataClass(
- num,
- num2,
- num4,
- name,
- name2,
- name4,
- otherParcelable,
- date,
- pattern,
- linkAddresses2,
- linkAddresses,
- linkAddresses4,
- stateName,
- flags,
- state,
- _charSeq,
- linkAddresses5,
- stringRes,
- dayOfWeek,
- coords);
+ public SampleDataClass createFromParcel(@NonNull Parcel in) {
+ return new SampleDataClass(in);
}
};
@@ -1798,8 +1872,8 @@ public final class SampleDataClass implements Parcelable {
}
@DataClass.Generated(
- time = 1568235365376L,
- codegenVersion = "1.0.1",
+ time = 1571258911688L,
+ codegenVersion = "1.0.9",
sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java",
inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_UNDEFINED\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List<android.net.LinkAddress> mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList<android.net.LinkAddress> mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=6L) int mDayOfWeek\nprivate @android.annotation.Size(2L) @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate static java.lang.String defaultName4()\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)")
@Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java b/tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java
index 663620743af9..c7a773530963 100644
--- a/tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java
+++ b/tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java
@@ -25,9 +25,14 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
import android.net.LinkAddress;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
import androidx.test.runner.AndroidJUnit4;
@@ -36,6 +41,9 @@ import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -209,6 +217,32 @@ public class SampleDataClassTest {
newBuilder().setDayOfWeek(42).build();
}
+ @Test
+ public void testDataStructures_parcelCorrectly() {
+ SampleWithCustomBuilder otherParcelable = new SampleWithCustomBuilder.Builder().setDelay(3, SECONDS).build();
+
+ ParcelAllTheThingsDataClass instance = new ParcelAllTheThingsDataClass.Builder()
+ .setIntArray(40, 41)
+ .addMap("foo", otherParcelable)
+ .setSparseArray(new SparseArray<SampleWithCustomBuilder>() {{
+ put(45, otherParcelable);
+ }})
+ .setSparseIntArray(new SparseIntArray() {{
+ put(48, 49);
+ }})
+ .addStringMap("foo2", "fooValue")
+ .setStringArray("foo", "bar")
+ .addStringList("foo")
+ .build();
+
+ ParcelAllTheThingsDataClass unparceledInstance =
+ parcelAndUnparcel(instance, ParcelAllTheThingsDataClass.CREATOR);
+
+ // SparseArray and friends don't implement equals
+ // so just compare string representations instead
+ assertEquals(instance.toString(), unparceledInstance.toString());
+ }
+
private static <T extends Parcelable> T parcelAndUnparcel(
T original, Parcelable.Creator<T> creator) {
Parcel p = Parcel.obtain();
diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
index 86f37fe1057e..55feae7200ea 100644
--- a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
+++ b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
@@ -17,14 +17,16 @@
package com.android.codegentest;
import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.SystemClock;
import com.android.internal.util.DataClass;
import java.util.concurrent.TimeUnit;
-@DataClass(genBuilder = true)
-public class SampleWithCustomBuilder {
+@DataClass(genBuilder = true, genAidl = false, genToString = true)
+public class SampleWithCustomBuilder implements Parcelable {
long delayAmount = 0;
@NonNull
@@ -73,15 +75,28 @@ public class SampleWithCustomBuilder {
}
+ private static TimeUnit unparcelDelayUnit(Parcel p) {
+ return TimeUnit.values()[p.readInt()];
+ }
+
+ private void parcelDelayUnit(Parcel p, int flags) {
+ p.writeInt(delayUnit.ordinal());
+ }
+
+
- // Code below generated by codegen v1.0.1.
+ // Code below generated by codegen v1.0.9.
//
// DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
//
- // CHECKSTYLE:OFF Generated code
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
@DataClass.Generated.Member
/* package-private */ SampleWithCustomBuilder(
@@ -112,6 +127,68 @@ public class SampleWithCustomBuilder {
return creationTimestamp;
}
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "SampleWithCustomBuilder { " +
+ "delayAmount = " + delayAmount + ", " +
+ "delayUnit = " + delayUnit + ", " +
+ "creationTimestamp = " + creationTimestamp +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeLong(delayAmount);
+ parcelDelayUnit(dest, flags);
+ dest.writeLong(creationTimestamp);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ protected SampleWithCustomBuilder(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ long _delayAmount = in.readLong();
+ TimeUnit _delayUnit = unparcelDelayUnit(in);
+ long _creationTimestamp = in.readLong();
+
+ this.delayAmount = _delayAmount;
+ this.delayUnit = _delayUnit;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, delayUnit);
+ this.creationTimestamp = _creationTimestamp;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<SampleWithCustomBuilder> CREATOR
+ = new Parcelable.Creator<SampleWithCustomBuilder>() {
+ @Override
+ public SampleWithCustomBuilder[] newArray(int size) {
+ return new SampleWithCustomBuilder[size];
+ }
+
+ @Override
+ public SampleWithCustomBuilder createFromParcel(@NonNull Parcel in) {
+ return new SampleWithCustomBuilder(in);
+ }
+ };
+
/**
* A builder for {@link SampleWithCustomBuilder}
*/
@@ -176,10 +253,10 @@ public class SampleWithCustomBuilder {
}
@DataClass.Generated(
- time = 1568235366386L,
- codegenVersion = "1.0.1",
+ time = 1571258912752L,
+ codegenVersion = "1.0.9",
sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java",
- inputSignatures = " long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n long creationTimestamp\nclass SampleWithCustomBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true)\nabstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = " long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n long creationTimestamp\nprivate static java.util.concurrent.TimeUnit unparcelDelayUnit(android.os.Parcel)\nprivate void parcelDelayUnit(android.os.Parcel,int)\nclass SampleWithCustomBuilder extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)\nabstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java
new file mode 100644
index 000000000000..b967f19f9f7e
--- /dev/null
+++ b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.codegentest;
+
+import android.annotation.NonNull;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Test for some false positive pitfalls for
+ * {@link android.processor.staledataclass.StaleDataclassProcessor}
+ *
+ * Relies on the detector being run, failing the build should any of things here falsely
+ * register as stale.
+ */
+@DataClass(genConstructor = false, genBuilder = false)
+public class StaleDataclassDetectorFalsePositivesTest {
+
+ /** Interfaces should be ignored */
+ public interface SomeListener {
+ void onEvent();
+ }
+
+ /** Enums should be ignored */
+ private enum SomeEnum { ONE, TWO }
+
+ /** Annotations should be ignored */
+ public @interface SomeAnnotation {}
+
+ /* Static initializers should be ignored */
+ static {}
+
+ /* Initializers should be ignored */
+ {}
+
+ /** Unrelated methods should be noted, without triggering staleness false positives */
+ public @NonNull String someMethod(int param) { return null; }
+
+
+
+ // Code below generated by codegen v1.0.9.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated(
+ time = 1571258916868L,
+ codegenVersion = "1.0.9",
+ sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java",
+ inputSignatures = "public @android.annotation.NonNull java.lang.String someMethod(int)\nclass StaleDataclassDetectorFalsePositivesTest extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false)")
+ @Deprecated
+ private void __metadata() {}
+
+}
diff --git a/tests/FlickerTests/AndroidManifest.xml b/tests/FlickerTests/AndroidManifest.xml
index 5b1a36b84cc4..91fb7c12b392 100644
--- a/tests/FlickerTests/AndroidManifest.xml
+++ b/tests/FlickerTests/AndroidManifest.xml
@@ -21,8 +21,12 @@
<!-- 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" />
+ <!-- Write secure settings -->
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<!-- Capture screen contents -->
<uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+ <!-- Enable / Disable tracing !-->
+ <uses-permission android:name="android.permission.DUMP" />
<!-- Run layers trace -->
<uses-permission android:name="android.permission.HARDWARE_TEST"/>
<application>
@@ -33,4 +37,4 @@
android:targetPackage="com.android.server.wm.flicker"
android:label="WindowManager Flicker Tests">
</instrumentation>
-</manifest> \ No newline at end of file
+</manifest>
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
index e36f97656f2a..d433df56bc00 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -25,5 +25,6 @@
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="directory-keys" value="/sdcard/flicker" />
<option name="collect-on-run-ended-only" value="true" />
+ <option name="clean-up" value="false" />
</metrics_collector>
</configuration>
diff --git a/tests/FlickerTests/TEST_MAPPING b/tests/FlickerTests/TEST_MAPPING
index 55a61471dfb8..f34c43248e1a 100644
--- a/tests/FlickerTests/TEST_MAPPING
+++ b/tests/FlickerTests/TEST_MAPPING
@@ -1,7 +1,8 @@
{
"postsubmit": [
{
- "name": "FlickerTests"
+ "name": "FlickerTests",
+ "keywords": ["primary-device"]
}
]
} \ No newline at end of file
diff --git a/tests/FlickerTests/lib/Android.bp b/tests/FlickerTests/lib/Android.bp
deleted file mode 100644
index e0f0188ee618..000000000000
--- a/tests/FlickerTests/lib/Android.bp
+++ /dev/null
@@ -1,57 +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.
-//
-
-java_test {
- name: "flickerlib",
- platform_apis: true,
- srcs: ["src/**/*.java"],
- static_libs: [
- "androidx.test.janktesthelper",
- "cts-wm-util",
- "platformprotosnano",
- "layersprotosnano",
- "truth-prebuilt",
- "sysui-helper",
- "launcher-helper-lib",
- ],
-}
-
-java_library {
- name: "flickerlib_without_helpers",
- platform_apis: true,
- srcs: ["src/**/*.java"],
- exclude_srcs: ["src/**/helpers/*.java"],
- static_libs: [
- "cts-wm-util",
- "platformprotosnano",
- "layersprotosnano",
- "truth-prebuilt"
- ],
-}
-
-java_library {
- name: "flickerautomationhelperlib",
- sdk_version: "test_current",
- srcs: [
- "src/com/android/server/wm/flicker/helpers/AutomationUtils.java",
- "src/com/android/server/wm/flicker/WindowUtils.java",
- ],
- static_libs: [
- "sysui-helper",
- "launcher-helper-lib",
- "compatibility-device-util-axt",
- ],
-}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java
deleted file mode 100644
index 38255ee6fe8d..000000000000
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker;
-
-import java.util.concurrent.TimeUnit;
-import java.util.function.Function;
-
-/**
- * Collection of functional interfaces and classes representing assertions and their associated
- * results. Assertions are functions that are applied over a single trace entry and returns a
- * result which includes a detailed reason if the assertion fails.
- */
-public class Assertions {
- /**
- * Checks assertion on a single trace entry.
- *
- * @param <T> trace entry type to perform the assertion on.
- */
- @FunctionalInterface
- public interface TraceAssertion<T> extends Function<T, Result> {
- /**
- * Returns an assertion that represents the logical negation of this assertion.
- *
- * @return a assertion that represents the logical negation of this assertion
- */
- default TraceAssertion<T> negate() {
- return (T t) -> apply(t).negate();
- }
- }
-
- /**
- * Checks assertion on a single layers trace entry.
- */
- @FunctionalInterface
- public interface LayersTraceAssertion extends TraceAssertion<LayersTrace.Entry> {
-
- }
-
- /**
- * Utility class to store assertions with an identifier to help generate more useful debug
- * data when dealing with multiple assertions.
- */
- public static class NamedAssertion<T> {
- public final TraceAssertion<T> assertion;
- public final String name;
-
- public NamedAssertion(TraceAssertion<T> assertion, String name) {
- this.assertion = assertion;
- this.name = name;
- }
- }
-
- /**
- * Contains the result of an assertion including the reason for failed assertions.
- */
- public static class Result {
- public static final String NEGATION_PREFIX = "!";
- public final boolean success;
- public final long timestamp;
- public final String assertionName;
- public final String reason;
-
- public Result(boolean success, long timestamp, String assertionName, String reason) {
- this.success = success;
- this.timestamp = timestamp;
- this.assertionName = assertionName;
- this.reason = reason;
- }
-
- public Result(boolean success, String reason) {
- this.success = success;
- this.reason = reason;
- this.assertionName = "";
- this.timestamp = 0;
- }
-
- /**
- * Returns the negated {@code Result} and adds a negation prefix to the assertion name.
- */
- public Result negate() {
- String negatedAssertionName;
- if (this.assertionName.startsWith(NEGATION_PREFIX)) {
- negatedAssertionName = this.assertionName.substring(NEGATION_PREFIX.length() + 1);
- } else {
- negatedAssertionName = NEGATION_PREFIX + this.assertionName;
- }
- return new Result(!this.success, this.timestamp, negatedAssertionName, this.reason);
- }
-
- public boolean passed() {
- return this.success;
- }
-
- public boolean failed() {
- return !this.success;
- }
-
- @Override
- public String toString() {
- return "Timestamp: " + prettyTimestamp(timestamp)
- + "\nAssertion: " + assertionName
- + "\nReason: " + reason;
- }
-
- private String prettyTimestamp(long timestamp_ns) {
- StringBuilder prettyTimestamp = new StringBuilder();
- TimeUnit[] timeUnits = {TimeUnit.HOURS, TimeUnit.MINUTES, TimeUnit.SECONDS, TimeUnit
- .MILLISECONDS};
- String[] unitSuffixes = {"h", "m", "s", "ms"};
-
- for (int i = 0; i < timeUnits.length; i++) {
- long convertedTime = timeUnits[i].convert(timestamp_ns, TimeUnit.NANOSECONDS);
- timestamp_ns -= TimeUnit.NANOSECONDS.convert(convertedTime, timeUnits[i]);
- prettyTimestamp.append(convertedTime).append(unitSuffixes[i]);
- }
-
- return prettyTimestamp.toString();
- }
- }
-}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java
deleted file mode 100644
index 5c4df81299c1..000000000000
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java
+++ /dev/null
@@ -1,183 +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 com.android.server.wm.flicker.Assertions.NamedAssertion;
-import com.android.server.wm.flicker.Assertions.Result;
-import com.android.server.wm.flicker.Assertions.TraceAssertion;
-
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * Captures some of the common logic in {@link LayersTraceSubject} and {@link WmTraceSubject}
- * used to filter trace entries and combine multiple assertions.
- *
- * @param <T> trace entry type
- */
-public class AssertionsChecker<T extends ITraceEntry> {
- private boolean mFilterEntriesByRange = false;
- private long mFilterStartTime = 0;
- private long mFilterEndTime = 0;
- private AssertionOption mOption = AssertionOption.NONE;
- private List<NamedAssertion<T>> mAssertions = new LinkedList<>();
-
- public void add(Assertions.TraceAssertion<T> assertion, String name) {
- mAssertions.add(new NamedAssertion<>(assertion, name));
- }
-
- public void filterByRange(long startTime, long endTime) {
- mFilterEntriesByRange = true;
- mFilterStartTime = startTime;
- mFilterEndTime = endTime;
- }
-
- private void setOption(AssertionOption option) {
- if (mOption != AssertionOption.NONE && option != mOption) {
- throw new IllegalArgumentException("Cannot use " + mOption + " option with "
- + option + " option.");
- }
- mOption = option;
- }
-
- public void checkFirstEntry() {
- setOption(AssertionOption.CHECK_FIRST_ENTRY);
- }
-
- public void checkLastEntry() {
- setOption(AssertionOption.CHECK_LAST_ENTRY);
- }
-
- public void checkChangingAssertions() {
- setOption(AssertionOption.CHECK_CHANGING_ASSERTIONS);
- }
-
-
- /**
- * Filters trace entries then runs assertions returning a list of failures.
- *
- * @param entries list of entries to perform assertions on
- * @return list of failed assertion results
- */
- public List<Result> test(List<T> entries) {
- List<T> filteredEntries;
- List<Result> failures;
-
- if (mFilterEntriesByRange) {
- filteredEntries = entries.stream()
- .filter(e -> ((e.getTimestamp() >= mFilterStartTime)
- && (e.getTimestamp() <= mFilterEndTime)))
- .collect(Collectors.toList());
- } else {
- filteredEntries = entries;
- }
-
- switch (mOption) {
- case CHECK_CHANGING_ASSERTIONS:
- return assertChanges(filteredEntries);
- case CHECK_FIRST_ENTRY:
- return assertEntry(filteredEntries.get(0));
- case CHECK_LAST_ENTRY:
- return assertEntry(filteredEntries.get(filteredEntries.size() - 1));
- }
- return assertAll(filteredEntries);
- }
-
- /**
- * Steps through each trace entry checking if provided assertions are true in the order they
- * are added. Each assertion must be true for at least a single trace entry.
- *
- * This can be used to check for asserting a change in property over a trace. Such as visibility
- * for a window changes from true to false or top-most window changes from A to Bb and back to A
- * again.
- */
- private List<Result> assertChanges(List<T> entries) {
- List<Result> failures = new ArrayList<>();
- int entryIndex = 0;
- int assertionIndex = 0;
- int lastPassedAssertionIndex = -1;
-
- if (mAssertions.size() == 0) {
- return failures;
- }
-
- while (assertionIndex < mAssertions.size() && entryIndex < entries.size()) {
- TraceAssertion<T> currentAssertion = mAssertions.get(assertionIndex).assertion;
- Result result = currentAssertion.apply(entries.get(entryIndex));
- if (result.passed()) {
- lastPassedAssertionIndex = assertionIndex;
- entryIndex++;
- continue;
- }
-
- if (lastPassedAssertionIndex != assertionIndex) {
- failures.add(result);
- break;
- }
- assertionIndex++;
-
- if (assertionIndex == mAssertions.size()) {
- failures.add(result);
- break;
- }
- }
-
- if (failures.isEmpty()) {
- if (assertionIndex != mAssertions.size() - 1) {
- String reason = "\nAssertion " + mAssertions.get(assertionIndex).name
- + " never became false";
- reason += "\nPassed assertions: " + mAssertions.stream().limit(assertionIndex)
- .map(assertion -> assertion.name).collect(Collectors.joining(","));
- reason += "\nUntested assertions: " + mAssertions.stream().skip(assertionIndex + 1)
- .map(assertion -> assertion.name).collect(Collectors.joining(","));
-
- Result result = new Result(false /* success */, 0 /* timestamp */,
- "assertChanges", "Not all assertions passed." + reason);
- failures.add(result);
- }
- }
- return failures;
- }
-
- private List<Result> assertEntry(T entry) {
- List<Result> failures = new ArrayList<>();
- for (NamedAssertion<T> assertion : mAssertions) {
- Result result = assertion.assertion.apply(entry);
- if (result.failed()) {
- failures.add(result);
- }
- }
- return failures;
- }
-
- private List<Result> assertAll(List<T> entries) {
- return mAssertions.stream().flatMap(
- assertion -> entries.stream()
- .map(assertion.assertion)
- .filter(Result::failed))
- .collect(Collectors.toList());
- }
-
- private enum AssertionOption {
- NONE,
- CHECK_CHANGING_ASSERTIONS,
- CHECK_FIRST_ENTRY,
- CHECK_LAST_ENTRY,
- }
-}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java
deleted file mode 100644
index c47f7f42e54e..000000000000
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java
+++ /dev/null
@@ -1,27 +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;
-
-/**
- * Common interface for Layer and WindowManager trace entries.
- */
-public interface ITraceEntry {
- /**
- * @return timestamp of current entry
- */
- long getTimestamp();
-}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java
deleted file mode 100644
index 68986d48783a..000000000000
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java
+++ /dev/null
@@ -1,420 +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.graphics.Rect;
-import android.surfaceflinger.nano.Layers.LayerProto;
-import android.surfaceflinger.nano.Layers.RectProto;
-import android.surfaceflinger.nano.Layers.RegionProto;
-import android.surfaceflinger.nano.Layerstrace.LayersTraceFileProto;
-import android.surfaceflinger.nano.Layerstrace.LayersTraceProto;
-import android.util.SparseArray;
-
-import androidx.annotation.Nullable;
-
-import com.android.server.wm.flicker.Assertions.Result;
-
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
-/**
- * Contains a collection of parsed Layers trace entries and assertions to apply over
- * a single entry.
- *
- * Each entry is parsed into a list of {@link LayersTrace.Entry} objects.
- */
-public class LayersTrace {
- final private List<Entry> mEntries;
- @Nullable
- final private Path mSource;
-
- private LayersTrace(List<Entry> entries, Path source) {
- this.mEntries = entries;
- this.mSource = source;
- }
-
- /**
- * Parses {@code LayersTraceFileProto} from {@code data} and uses the proto to generates a list
- * of trace entries, storing the flattened layers into its hierarchical structure.
- *
- * @param data binary proto data
- * @param source Path to source of data for additional debug information
- */
- public static LayersTrace parseFrom(byte[] data, Path source) {
- List<Entry> entries = new ArrayList<>();
- LayersTraceFileProto fileProto;
- try {
- fileProto = LayersTraceFileProto.parseFrom(data);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- for (LayersTraceProto traceProto : fileProto.entry) {
- Entry entry = Entry.fromFlattenedLayers(traceProto.elapsedRealtimeNanos,
- traceProto.layers.layers);
- entries.add(entry);
- }
- return new LayersTrace(entries, source);
- }
-
- /**
- * Parses {@code LayersTraceFileProto} from {@code data} and uses the proto to generates a list
- * of trace entries, storing the flattened layers into its hierarchical structure.
- *
- * @param data binary proto data
- */
- public static LayersTrace parseFrom(byte[] data) {
- return parseFrom(data, null);
- }
-
- public List<Entry> getEntries() {
- return mEntries;
- }
-
- public Entry getEntry(long timestamp) {
- Optional<Entry> entry = mEntries.stream()
- .filter(e -> e.getTimestamp() == timestamp)
- .findFirst();
- if (!entry.isPresent()) {
- throw new RuntimeException("Entry does not exist for timestamp " + timestamp);
- }
- return entry.get();
- }
-
- public Optional<Path> getSource() {
- return Optional.ofNullable(mSource);
- }
-
- /**
- * Represents a single Layer trace entry.
- */
- public static class Entry implements ITraceEntry {
- private long mTimestamp;
- private List<Layer> mRootLayers; // hierarchical representation of layers
- private List<Layer> mFlattenedLayers = null;
-
- private Entry(long timestamp, List<Layer> rootLayers) {
- this.mTimestamp = timestamp;
- this.mRootLayers = rootLayers;
- }
-
- /**
- * Constructs the layer hierarchy from a flattened list of layers.
- */
- public static Entry fromFlattenedLayers(long timestamp, LayerProto[] protos) {
- SparseArray<Layer> layerMap = new SparseArray<>();
- ArrayList<Layer> orphans = new ArrayList<>();
- for (LayerProto proto : protos) {
- int id = proto.id;
- int parentId = proto.parent;
-
- Layer newLayer = layerMap.get(id);
- if (newLayer == null) {
- newLayer = new Layer(proto);
- layerMap.append(id, newLayer);
- } else if (newLayer.mProto != null) {
- throw new RuntimeException("Duplicate layer id found:" + id);
- } else {
- newLayer.mProto = proto;
- orphans.remove(newLayer);
- }
-
- // add parent placeholder
- if (layerMap.get(parentId) == null) {
- Layer orphanLayer = new Layer(null);
- layerMap.append(parentId, orphanLayer);
- orphans.add(orphanLayer);
- }
- layerMap.get(parentId).addChild(newLayer);
- newLayer.addParent(layerMap.get(parentId));
- }
-
- // Fail if we find orphan layers.
- orphans.remove(layerMap.get(-1));
- orphans.forEach(orphan -> {
- String childNodes = orphan.mChildren.stream().map(node ->
- Integer.toString(node.getId())).collect(Collectors.joining(", "));
- int orphanId = orphan.mChildren.get(0).mProto.parent;
- throw new RuntimeException(
- "Failed to parse layers trace. Found orphan layers with parent "
- + "layer id:" + orphanId + " : " + childNodes);
- });
-
- return new Entry(timestamp, layerMap.get(-1).mChildren);
- }
-
- /**
- * Extracts {@link Rect} from {@link RectProto}.
- */
- private static Rect extract(RectProto proto) {
- return new Rect(proto.left, proto.top, proto.right, proto.bottom);
- }
-
- /**
- * Extracts {@link Rect} from {@link RegionProto} by returning a rect that encompasses all
- * the rects making up the region.
- */
- private static Rect extract(RegionProto regionProto) {
- Rect region = new Rect();
- for (RectProto proto : regionProto.rect) {
- region.union(proto.left, proto.top, proto.right, proto.bottom);
- }
- return region;
- }
-
- /**
- * Checks if a region specified by {@code testRect} is covered by all visible layers.
- */
- public Result coversRegion(Rect testRect) {
- String assertionName = "coversRegion";
- Collection<Layer> layers = asFlattenedLayers();
-
- for (int x = testRect.left; x < testRect.right; x++) {
- for (int y = testRect.top; y < testRect.bottom; y++) {
- boolean emptyRegionFound = true;
- for (Layer layer : layers) {
- if (layer.isInvisible() || layer.isHiddenByParent()) {
- continue;
- }
- for (RectProto rectProto : layer.mProto.visibleRegion.rect) {
- Rect r = extract(rectProto);
- if (r.contains(x, y)) {
- y = r.bottom;
- emptyRegionFound = false;
- }
- }
- }
- if (emptyRegionFound) {
- String reason = "Region to test: " + testRect
- + "\nfirst empty point: " + x + ", " + y;
- reason += "\nvisible regions:";
- for (Layer layer : layers) {
- if (layer.isInvisible() || layer.isHiddenByParent()) {
- continue;
- }
- Rect r = extract(layer.mProto.visibleRegion);
- reason += "\n" + layer.mProto.name + r.toString();
- }
- return new Result(false /* success */, this.mTimestamp, assertionName,
- reason);
- }
- }
- }
- String info = "Region covered: " + testRect;
- return new Result(true /* success */, this.mTimestamp, assertionName, info);
- }
-
- /**
- * Checks if a layer with name {@code layerName} has a visible region
- * {@code expectedVisibleRegion}.
- */
- public Result hasVisibleRegion(String layerName, Rect expectedVisibleRegion) {
- String assertionName = "hasVisibleRegion";
- String reason = "Could not find " + layerName;
- for (Layer layer : asFlattenedLayers()) {
- if (layer.mProto.name.contains(layerName)) {
- if (layer.isHiddenByParent()) {
- reason = layer.getHiddenByParentReason();
- continue;
- }
- if (layer.isInvisible()) {
- reason = layer.getVisibilityReason();
- continue;
- }
- Rect visibleRegion = extract(layer.mProto.visibleRegion);
- if (visibleRegion.equals(expectedVisibleRegion)) {
- return new Result(true /* success */, this.mTimestamp, assertionName,
- layer.mProto.name + "has visible region " + expectedVisibleRegion);
- }
- reason = layer.mProto.name + " has visible region:" + visibleRegion + " "
- + "expected:" + expectedVisibleRegion;
- }
- }
- return new Result(false /* success */, this.mTimestamp, assertionName, reason);
- }
-
- /**
- * Checks if a layer with name {@code layerName} is visible.
- */
- public Result isVisible(String layerName) {
- String assertionName = "isVisible";
- String reason = "Could not find " + layerName;
- for (Layer layer : asFlattenedLayers()) {
- if (layer.mProto.name.contains(layerName)) {
- if (layer.isHiddenByParent()) {
- reason = layer.getHiddenByParentReason();
- continue;
- }
- if (layer.isInvisible()) {
- reason = layer.getVisibilityReason();
- continue;
- }
- return new Result(true /* success */, this.mTimestamp, assertionName,
- layer.mProto.name + " is visible");
- }
- }
- return new Result(false /* success */, this.mTimestamp, assertionName, reason);
- }
-
- @Override
- public long getTimestamp() {
- return mTimestamp;
- }
-
- public List<Layer> getRootLayers() {
- return mRootLayers;
- }
-
- /**
- * Returns all layers as a flattened list using a depth first traversal.
- */
- public List<Layer> asFlattenedLayers() {
- if (mFlattenedLayers == null) {
- mFlattenedLayers = new LinkedList<>();
- ArrayList<Layer> pendingLayers = new ArrayList<>(this.mRootLayers);
- while (!pendingLayers.isEmpty()) {
- Layer layer = pendingLayers.remove(0);
- mFlattenedLayers.add(layer);
- pendingLayers.addAll(0, layer.mChildren);
- }
- }
- return mFlattenedLayers;
- }
-
- public Rect getVisibleBounds(String layerName) {
- List<Layer> layers = asFlattenedLayers();
- for (Layer layer : layers) {
- if (layer.mProto.name.contains(layerName) && layer.isVisible()) {
- return extract(layer.mProto.visibleRegion);
- }
- }
- return new Rect(0, 0, 0, 0);
- }
- }
-
- /**
- * Represents a single layer with links to its parent and child layers.
- */
- public static class Layer {
- @Nullable
- public LayerProto mProto;
- public List<Layer> mChildren;
- @Nullable
- public Layer mParent = null;
-
- private Layer(LayerProto proto) {
- this.mProto = proto;
- this.mChildren = new ArrayList<>();
- }
-
- private void addChild(Layer childLayer) {
- this.mChildren.add(childLayer);
- }
-
- private void addParent(Layer parentLayer) {
- this.mParent = parentLayer;
- }
-
- public int getId() {
- return mProto.id;
- }
-
- public boolean isActiveBufferEmpty() {
- return this.mProto.activeBuffer == null || this.mProto.activeBuffer.height == 0
- || this.mProto.activeBuffer.width == 0;
- }
-
- public boolean isVisibleRegionEmpty() {
- if (this.mProto.visibleRegion == null) {
- return true;
- }
- Rect visibleRect = Entry.extract(this.mProto.visibleRegion);
- return visibleRect.height() == 0 || visibleRect.width() == 0;
- }
-
- public boolean isHidden() {
- return (this.mProto.flags & /* FLAG_HIDDEN */ 0x1) != 0x0;
- }
-
- public boolean isVisible() {
- return (!isActiveBufferEmpty() || isColorLayer())
- && !isHidden()
- && this.mProto.color != null
- && this.mProto.color.a > 0
- && !isVisibleRegionEmpty();
- }
-
- public boolean isColorLayer() {
- return this.mProto.type.equals("ColorLayer");
- }
-
- public boolean isRootLayer() {
- return mParent == null || mParent.mProto == null;
- }
-
- public boolean isInvisible() {
- return !isVisible();
- }
-
- public boolean isHiddenByParent() {
- return !isRootLayer() && (mParent.isHidden() || mParent.isHiddenByParent());
- }
-
- public String getHiddenByParentReason() {
- String reason = "Layer " + mProto.name;
- if (isHiddenByParent()) {
- reason += " is hidden by parent: " + mParent.mProto.name;
- } else {
- reason += " is not hidden by parent: " + mParent.mProto.name;
- }
- return reason;
- }
-
- public String getVisibilityReason() {
- String reason = "Layer " + mProto.name;
- if (isVisible()) {
- reason += " is visible:";
- } else {
- reason += " is invisible:";
- if (this.mProto.activeBuffer == null) {
- reason += " activeBuffer=null";
- } else if (this.mProto.activeBuffer.height == 0) {
- reason += " activeBuffer.height=0";
- } else if (this.mProto.activeBuffer.width == 0) {
- reason += " activeBuffer.width=0";
- }
- if (!isColorLayer()) {
- reason += " type != ColorLayer";
- }
- if (isHidden()) {
- reason += " flags=" + this.mProto.flags + " (FLAG_HIDDEN set)";
- }
- if (this.mProto.color == null || this.mProto.color.a == 0) {
- reason += " color.a=0";
- }
- if (isVisibleRegionEmpty()) {
- reason += " visible region is empty";
- }
- }
- return reason;
- }
- }
-} \ No newline at end of file
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java
deleted file mode 100644
index 4a5129ed2269..000000000000
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java
+++ /dev/null
@@ -1,140 +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.google.common.truth.Truth.assertAbout;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import android.graphics.Rect;
-
-import androidx.annotation.Nullable;
-
-import com.android.server.wm.flicker.Assertions.Result;
-import com.android.server.wm.flicker.LayersTrace.Entry;
-import com.android.server.wm.flicker.TransitionRunner.TransitionResult;
-
-import com.google.common.truth.FailureMetadata;
-import com.google.common.truth.Subject;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * Truth subject for {@link LayersTrace} objects.
- */
-public class LayersTraceSubject extends Subject<LayersTraceSubject, LayersTrace> {
- // Boiler-plate Subject.Factory for LayersTraceSubject
- private static final Subject.Factory<LayersTraceSubject, LayersTrace> FACTORY =
- new Subject.Factory<LayersTraceSubject, LayersTrace>() {
- @Override
- public LayersTraceSubject createSubject(
- FailureMetadata fm, @Nullable LayersTrace target) {
- return new LayersTraceSubject(fm, target);
- }
- };
-
- private AssertionsChecker<Entry> mChecker = new AssertionsChecker<>();
-
- private LayersTraceSubject(FailureMetadata fm, @Nullable LayersTrace subject) {
- super(fm, subject);
- }
-
- // User-defined entry point
- public static LayersTraceSubject assertThat(@Nullable LayersTrace entry) {
- return assertAbout(FACTORY).that(entry);
- }
-
- // User-defined entry point
- public static LayersTraceSubject assertThat(@Nullable TransitionResult result) {
- LayersTrace entries = LayersTrace.parseFrom(result.getLayersTrace(),
- result.getLayersTracePath());
- return assertWithMessage(result.toString()).about(FACTORY).that(entries);
- }
-
- // Static method for getting the subject factory (for use with assertAbout())
- public static Subject.Factory<LayersTraceSubject, LayersTrace> entries() {
- return FACTORY;
- }
-
- public void forAllEntries() {
- test();
- }
-
- public void forRange(long startTime, long endTime) {
- mChecker.filterByRange(startTime, endTime);
- test();
- }
-
- public LayersTraceSubject then() {
- mChecker.checkChangingAssertions();
- return this;
- }
-
- public void inTheBeginning() {
- if (getSubject().getEntries().isEmpty()) {
- fail("No entries found.");
- }
- mChecker.checkFirstEntry();
- test();
- }
-
- public void atTheEnd() {
- if (getSubject().getEntries().isEmpty()) {
- fail("No entries found.");
- }
- mChecker.checkLastEntry();
- test();
- }
-
- private void test() {
- List<Result> failures = mChecker.test(getSubject().getEntries());
- if (!failures.isEmpty()) {
- String failureLogs = failures.stream().map(Result::toString)
- .collect(Collectors.joining("\n"));
- String tracePath = "";
- if (getSubject().getSource().isPresent()) {
- tracePath = "\nLayers Trace can be found in: "
- + getSubject().getSource().get().toAbsolutePath() + "\n";
- }
- fail(tracePath + failureLogs);
- }
- }
-
- public LayersTraceSubject coversRegion(Rect rect) {
- mChecker.add(entry -> entry.coversRegion(rect),
- "coversRegion(" + rect + ")");
- return this;
- }
-
- public LayersTraceSubject hasVisibleRegion(String layerName, Rect size) {
- mChecker.add(entry -> entry.hasVisibleRegion(layerName, size),
- "hasVisibleRegion(" + layerName + size + ")");
- return this;
- }
-
- public LayersTraceSubject showsLayer(String layerName) {
- mChecker.add(entry -> entry.isVisible(layerName),
- "showsLayer(" + layerName + ")");
- return this;
- }
-
- public LayersTraceSubject hidesLayer(String layerName) {
- mChecker.add(entry -> entry.isVisible(layerName).negate(),
- "hidesLayer(" + layerName + ")");
- return this;
- }
-}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java
deleted file mode 100644
index 241a1c04bdb8..000000000000
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java
+++ /dev/null
@@ -1,433 +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.monitor.ITransitionMonitor.OUTPUT_DIR;
-
-import android.util.Log;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-import androidx.test.InstrumentationRegistry;
-
-import com.android.server.wm.flicker.monitor.ITransitionMonitor;
-import com.android.server.wm.flicker.monitor.LayersTraceMonitor;
-import com.android.server.wm.flicker.monitor.ScreenRecorder;
-import com.android.server.wm.flicker.monitor.WindowAnimationFrameStatsMonitor;
-import com.android.server.wm.flicker.monitor.WindowManagerTraceMonitor;
-
-import com.google.common.io.Files;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * Builds and runs UI transitions capturing test artifacts.
- *
- * User can compose a transition from simpler steps, specifying setup and teardown steps. During
- * a transition, Layers trace, WindowManager trace, screen recordings and window animation frame
- * stats can be captured.
- *
- * <pre>
- * Transition builder options:
- * {@link TransitionBuilder#run(Runnable)} run transition under test. Monitors will be started
- * before the transition and stopped after the transition is completed.
- * {@link TransitionBuilder#repeat(int)} repeat transitions under test multiple times recording
- * result for each run.
- * {@link TransitionBuilder#withTag(String)} specify a string identifier used to prefix logs and
- * artifacts generated.
- * {@link TransitionBuilder#runBeforeAll(Runnable)} run setup transitions once before all other
- * transition are run to set up an initial state on device.
- * {@link TransitionBuilder#runBefore(Runnable)} run setup transitions before each test transition
- * run.
- * {@link TransitionBuilder#runAfter(Runnable)} run teardown transitions after each test
- * transition.
- * {@link TransitionBuilder#runAfter(Runnable)} run teardown transitions once after all
- * other transition are run.
- * {@link TransitionBuilder#includeJankyRuns()} disables {@link WindowAnimationFrameStatsMonitor}
- * to monitor janky frames. If janky frames are detected, then the test run is skipped. This
- * monitor is enabled by default.
- * {@link TransitionBuilder#skipLayersTrace()} disables {@link LayersTraceMonitor} used to
- * capture Layers trace during a transition. This monitor is enabled by default.
- * {@link TransitionBuilder#skipWindowManagerTrace()} disables {@link WindowManagerTraceMonitor}
- * used to capture WindowManager trace during a transition. This monitor is enabled by
- * default.
- * {@link TransitionBuilder#recordAllRuns()} records the screen contents and saves it to a file.
- * All the runs including setup and teardown transitions are included in the recording. This
- * monitor is used for debugging purposes.
- * {@link TransitionBuilder#recordEachRun()} records the screen contents during test transitions
- * and saves it to a file for each run. This monitor is used for debugging purposes.
- *
- * Example transition to capture WindowManager and Layers trace when opening a test app:
- * {@code
- * TransitionRunner.newBuilder()
- * .withTag("OpenTestAppFast")
- * .runBeforeAll(UiAutomationLib::wakeUp)
- * .runBeforeAll(UiAutomationLib::UnlockDevice)
- * .runBeforeAll(UiAutomationLib::openTestApp)
- * .runBefore(UiAutomationLib::closeTestApp)
- * .run(UiAutomationLib::openTestApp)
- * .runAfterAll(UiAutomationLib::closeTestApp)
- * .repeat(5)
- * .build()
- * .run();
- * }
- * </pre>
- */
-public class TransitionRunner {
- private static final String TAG = "FLICKER";
- private final ScreenRecorder mScreenRecorder;
- private final WindowManagerTraceMonitor mWmTraceMonitor;
- private final LayersTraceMonitor mLayersTraceMonitor;
- private final WindowAnimationFrameStatsMonitor mFrameStatsMonitor;
-
- private final List<ITransitionMonitor> mAllRunsMonitors;
- private final List<ITransitionMonitor> mPerRunMonitors;
- private final List<Runnable> mBeforeAlls;
- private final List<Runnable> mBefores;
- private final List<Runnable> mTransitions;
- private final List<Runnable> mAfters;
- private final List<Runnable> mAfterAlls;
-
- private final int mIterations;
- private final String mTestTag;
-
- @Nullable
- private List<TransitionResult> mResults = null;
-
- private TransitionRunner(TransitionBuilder builder) {
- mScreenRecorder = builder.mScreenRecorder;
- mWmTraceMonitor = builder.mWmTraceMonitor;
- mLayersTraceMonitor = builder.mLayersTraceMonitor;
- mFrameStatsMonitor = builder.mFrameStatsMonitor;
-
- mAllRunsMonitors = builder.mAllRunsMonitors;
- mPerRunMonitors = builder.mPerRunMonitors;
- mBeforeAlls = builder.mBeforeAlls;
- mBefores = builder.mBefores;
- mTransitions = builder.mTransitions;
- mAfters = builder.mAfters;
- mAfterAlls = builder.mAfterAlls;
-
- mIterations = builder.mIterations;
- mTestTag = builder.mTestTag;
- }
-
- public static TransitionBuilder newBuilder() {
- return newBuilder(OUTPUT_DIR.toString());
- }
-
- public static TransitionBuilder newBuilder(String outputDir) {
- return new TransitionBuilder(outputDir);
- }
-
- /**
- * Runs the composed transition and calls monitors at the appropriate stages. If jank monitor
- * is enabled, transitions with jank are skipped.
- *
- * @return itself
- */
- public TransitionRunner run() {
- mResults = new ArrayList<>();
- mAllRunsMonitors.forEach(ITransitionMonitor::start);
- mBeforeAlls.forEach(Runnable::run);
- for (int iteration = 0; iteration < mIterations; iteration++) {
- mBefores.forEach(Runnable::run);
- mPerRunMonitors.forEach(ITransitionMonitor::start);
- mTransitions.forEach(Runnable::run);
- mPerRunMonitors.forEach(ITransitionMonitor::stop);
- mAfters.forEach(Runnable::run);
- if (runJankFree() && mFrameStatsMonitor.jankyFramesDetected()) {
- String msg = String.format("Skipping iteration %d/%d for test %s due to jank. %s",
- iteration, mIterations - 1, mTestTag, mFrameStatsMonitor.toString());
- Log.e(TAG, msg);
- continue;
- }
- mResults.add(saveResult(iteration));
- }
- mAfterAlls.forEach(Runnable::run);
- mAllRunsMonitors.forEach(monitor -> {
- monitor.stop();
- monitor.save(mTestTag);
- });
- return this;
- }
-
- /**
- * Returns a list of transition results.
- *
- * @return list of transition results.
- */
- public List<TransitionResult> getResults() {
- if (mResults == null) {
- throw new IllegalStateException("Results do not exist!");
- }
- return mResults;
- }
-
- /**
- * Deletes all transition results that are not marked for saving.
- *
- * @return list of transition results.
- */
- public void deleteResults() {
- if (mResults == null) {
- return;
- }
- mResults.stream()
- .filter(TransitionResult::canDelete)
- .forEach(TransitionResult::delete);
- mResults = null;
- }
-
- /**
- * Saves monitor results to file.
- *
- * @return object containing paths to test artifacts
- */
- private TransitionResult saveResult(int iteration) {
- Path windowTrace = null;
- Path layerTrace = null;
- Path screenCaptureVideo = null;
-
- if (mPerRunMonitors.contains(mWmTraceMonitor)) {
- windowTrace = mWmTraceMonitor.save(mTestTag, iteration);
- }
- if (mPerRunMonitors.contains(mLayersTraceMonitor)) {
- layerTrace = mLayersTraceMonitor.save(mTestTag, iteration);
- }
- if (mPerRunMonitors.contains(mScreenRecorder)) {
- screenCaptureVideo = mScreenRecorder.save(mTestTag, iteration);
- }
- return new TransitionResult(layerTrace, windowTrace, screenCaptureVideo);
- }
-
- private boolean runJankFree() {
- return mPerRunMonitors.contains(mFrameStatsMonitor);
- }
-
- public String getTestTag() {
- return mTestTag;
- }
-
- /**
- * Stores paths to all test artifacts.
- */
- @VisibleForTesting
- public static class TransitionResult {
- @Nullable
- public final Path layersTrace;
- @Nullable
- public final Path windowManagerTrace;
- @Nullable
- public final Path screenCaptureVideo;
- private boolean flaggedForSaving;
-
- public TransitionResult(@Nullable Path layersTrace, @Nullable Path windowManagerTrace,
- @Nullable Path screenCaptureVideo) {
- this.layersTrace = layersTrace;
- this.windowManagerTrace = windowManagerTrace;
- this.screenCaptureVideo = screenCaptureVideo;
- }
-
- public void flagForSaving() {
- flaggedForSaving = true;
- }
-
- public boolean canDelete() {
- return !flaggedForSaving;
- }
-
- public boolean layersTraceExists() {
- return layersTrace != null && layersTrace.toFile().exists();
- }
-
- public byte[] getLayersTrace() {
- try {
- return Files.toByteArray(this.layersTrace.toFile());
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- public Path getLayersTracePath() {
- return layersTrace;
- }
-
- public boolean windowManagerTraceExists() {
- return windowManagerTrace != null && windowManagerTrace.toFile().exists();
- }
-
- public byte[] getWindowManagerTrace() {
- try {
- return Files.toByteArray(this.windowManagerTrace.toFile());
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- public Path getWindowManagerTracePath() {
- return windowManagerTrace;
- }
-
- public boolean screenCaptureVideoExists() {
- return screenCaptureVideo != null && screenCaptureVideo.toFile().exists();
- }
-
- public Path screenCaptureVideoPath() {
- return screenCaptureVideo;
- }
-
- public void delete() {
- if (layersTraceExists()) layersTrace.toFile().delete();
- if (windowManagerTraceExists()) windowManagerTrace.toFile().delete();
- if (screenCaptureVideoExists()) screenCaptureVideo.toFile().delete();
- }
- }
-
- /**
- * Builds a {@link TransitionRunner} instance.
- */
- public static class TransitionBuilder {
- private ScreenRecorder mScreenRecorder;
- private WindowManagerTraceMonitor mWmTraceMonitor;
- private LayersTraceMonitor mLayersTraceMonitor;
- private WindowAnimationFrameStatsMonitor mFrameStatsMonitor;
-
- private List<ITransitionMonitor> mAllRunsMonitors = new LinkedList<>();
- private List<ITransitionMonitor> mPerRunMonitors = new LinkedList<>();
- private List<Runnable> mBeforeAlls = new LinkedList<>();
- private List<Runnable> mBefores = new LinkedList<>();
- private List<Runnable> mTransitions = new LinkedList<>();
- private List<Runnable> mAfters = new LinkedList<>();
- private List<Runnable> mAfterAlls = new LinkedList<>();
-
- private boolean mRunJankFree = true;
- private boolean mCaptureWindowManagerTrace = true;
- private boolean mCaptureLayersTrace = true;
- private boolean mRecordEachRun = false;
- private int mIterations = 1;
- private String mTestTag = "";
-
- private boolean mRecordAllRuns = false;
-
- public TransitionBuilder(String outputDir) {
- mScreenRecorder = new ScreenRecorder();
- mWmTraceMonitor = new WindowManagerTraceMonitor(outputDir);
- mLayersTraceMonitor = new LayersTraceMonitor(outputDir);
- mFrameStatsMonitor = new
- WindowAnimationFrameStatsMonitor(InstrumentationRegistry.getInstrumentation());
- }
-
- public TransitionRunner build() {
- if (mCaptureWindowManagerTrace) {
- mPerRunMonitors.add(mWmTraceMonitor);
- }
-
- if (mCaptureLayersTrace) {
- mPerRunMonitors.add(mLayersTraceMonitor);
- }
-
- if (mRunJankFree) {
- mPerRunMonitors.add(mFrameStatsMonitor);
- }
-
- if (mRecordAllRuns) {
- mAllRunsMonitors.add(mScreenRecorder);
- }
-
- if (mRecordEachRun) {
- mPerRunMonitors.add(mScreenRecorder);
- }
-
- return new TransitionRunner(this);
- }
-
- public TransitionBuilder runBeforeAll(Runnable runnable) {
- mBeforeAlls.add(runnable);
- return this;
- }
-
- public TransitionBuilder runBefore(Runnable runnable) {
- mBefores.add(runnable);
- return this;
- }
-
- public TransitionBuilder run(Runnable runnable) {
- mTransitions.add(runnable);
- return this;
- }
-
- public TransitionBuilder runAfter(Runnable runnable) {
- mAfters.add(runnable);
- return this;
- }
-
- public TransitionBuilder runAfterAll(Runnable runnable) {
- mAfterAlls.add(runnable);
- return this;
- }
-
- public TransitionBuilder repeat(int iterations) {
- mIterations = iterations;
- return this;
- }
-
- public TransitionBuilder skipWindowManagerTrace() {
- mCaptureWindowManagerTrace = false;
- return this;
- }
-
- public TransitionBuilder skipLayersTrace() {
- mCaptureLayersTrace = false;
- return this;
- }
-
- public TransitionBuilder includeJankyRuns() {
- mRunJankFree = false;
- return this;
- }
-
- public TransitionBuilder recordEachRun() {
- if (mRecordAllRuns) {
- throw new IllegalArgumentException("Invalid option with recordAllRuns");
- }
- mRecordEachRun = true;
- return this;
- }
-
- public TransitionBuilder recordAllRuns() {
- if (mRecordEachRun) {
- throw new IllegalArgumentException("Invalid option with recordEachRun");
- }
- mRecordAllRuns = true;
- return this;
- }
-
- public TransitionBuilder withTag(String testTag) {
- if (testTag.contains(" ")) {
- throw new IllegalArgumentException("The test tag can not contain spaces since it "
- + "is a part of the file name");
- }
- mTestTag = testTag;
- return this;
- }
- }
-}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java
deleted file mode 100644
index 412e72d82e55..000000000000
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java
+++ /dev/null
@@ -1,240 +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 androidx.annotation.Nullable;
-
-import com.android.server.wm.flicker.Assertions.Result;
-import com.android.server.wm.nano.AppWindowTokenProto;
-import com.android.server.wm.nano.StackProto;
-import com.android.server.wm.nano.TaskProto;
-import com.android.server.wm.nano.WindowManagerTraceFileProto;
-import com.android.server.wm.nano.WindowManagerTraceProto;
-import com.android.server.wm.nano.WindowStateProto;
-import com.android.server.wm.nano.WindowTokenProto;
-
-import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
-
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-
-/**
- * Contains a collection of parsed WindowManager trace entries and assertions to apply over
- * a single entry.
- *
- * Each entry is parsed into a list of {@link WindowManagerTrace.Entry} objects.
- */
-public class WindowManagerTrace {
- private static final int DEFAULT_DISPLAY = 0;
- private final List<Entry> mEntries;
- @Nullable
- final private Path mSource;
-
- private WindowManagerTrace(List<Entry> entries, Path source) {
- this.mEntries = entries;
- this.mSource = source;
- }
-
- /**
- * Parses {@code WindowManagerTraceFileProto} from {@code data} and uses the proto to
- * generates a list of trace entries.
- *
- * @param data binary proto data
- * @param source Path to source of data for additional debug information
- */
- public static WindowManagerTrace parseFrom(byte[] data, Path source) {
- List<Entry> entries = new ArrayList<>();
-
- WindowManagerTraceFileProto fileProto;
- try {
- fileProto = WindowManagerTraceFileProto.parseFrom(data);
- } catch (InvalidProtocolBufferNanoException e) {
- throw new RuntimeException(e);
- }
- for (WindowManagerTraceProto entryProto : fileProto.entry) {
- entries.add(new Entry(entryProto));
- }
- return new WindowManagerTrace(entries, source);
- }
-
- public static WindowManagerTrace parseFrom(byte[] data) {
- return parseFrom(data, null);
- }
-
- public List<Entry> getEntries() {
- return mEntries;
- }
-
- public Entry getEntry(long timestamp) {
- Optional<Entry> entry = mEntries.stream()
- .filter(e -> e.getTimestamp() == timestamp)
- .findFirst();
- if (!entry.isPresent()) {
- throw new RuntimeException("Entry does not exist for timestamp " + timestamp);
- }
- return entry.get();
- }
-
- public Optional<Path> getSource() {
- return Optional.ofNullable(mSource);
- }
-
- /**
- * Represents a single WindowManager trace entry.
- */
- public static class Entry implements ITraceEntry {
- private final WindowManagerTraceProto mProto;
-
- public Entry(WindowManagerTraceProto proto) {
- mProto = proto;
- }
-
- private static Result isWindowVisible(String windowTitle,
- WindowTokenProto[] windowTokenProtos) {
- boolean titleFound = false;
- for (WindowTokenProto windowToken : windowTokenProtos) {
- for (WindowStateProto windowState : windowToken.windows) {
- if (windowState.identifier.title.contains(windowTitle)) {
- titleFound = true;
- if (isVisible(windowState)) {
- return new Result(true /* success */,
- windowState.identifier.title + " is visible");
- }
- }
- }
- }
-
- String reason;
- if (!titleFound) {
- reason = windowTitle + " cannot be found";
- } else {
- reason = windowTitle + " is invisible";
- }
- return new Result(false /* success */, reason);
- }
-
- private static boolean isVisible(WindowStateProto windowState) {
- return windowState.windowContainer.visible;
- }
-
- @Override
- public long getTimestamp() {
- return mProto.elapsedRealtimeNanos;
- }
-
- /**
- * Returns window title of the top most visible app window.
- */
- private String getTopVisibleAppWindow() {
- StackProto[] stacks = mProto.windowManagerService.rootWindowContainer
- .displays[DEFAULT_DISPLAY].stacks;
- for (StackProto stack : stacks) {
- for (TaskProto task : stack.tasks) {
- for (AppWindowTokenProto token : task.appWindowTokens) {
- for (WindowStateProto windowState : token.windowToken.windows) {
- if (windowState.windowContainer.visible) {
- return task.appWindowTokens[0].name;
- }
- }
- }
- }
- }
-
- return "";
- }
-
- /**
- * Checks if aboveAppWindow with {@code windowTitle} is visible.
- */
- public Result isAboveAppWindowVisible(String windowTitle) {
- WindowTokenProto[] windowTokenProtos = mProto.windowManagerService
- .rootWindowContainer
- .displays[DEFAULT_DISPLAY].aboveAppWindows;
- Result result = isWindowVisible(windowTitle, windowTokenProtos);
- return new Result(result.success, getTimestamp(), "showsAboveAppWindow", result.reason);
- }
-
- /**
- * Checks if belowAppWindow with {@code windowTitle} is visible.
- */
- public Result isBelowAppWindowVisible(String windowTitle) {
- WindowTokenProto[] windowTokenProtos = mProto.windowManagerService
- .rootWindowContainer
- .displays[DEFAULT_DISPLAY].belowAppWindows;
- Result result = isWindowVisible(windowTitle, windowTokenProtos);
- return new Result(result.success, getTimestamp(), "isBelowAppWindowVisible",
- result.reason);
- }
-
- /**
- * Checks if imeWindow with {@code windowTitle} is visible.
- */
- public Result isImeWindowVisible(String windowTitle) {
- WindowTokenProto[] windowTokenProtos = mProto.windowManagerService
- .rootWindowContainer
- .displays[DEFAULT_DISPLAY].imeWindows;
- Result result = isWindowVisible(windowTitle, windowTokenProtos);
- return new Result(result.success, getTimestamp(), "isImeWindowVisible",
- result.reason);
- }
-
- /**
- * Checks if app window with {@code windowTitle} is on top.
- */
- public Result isVisibleAppWindowOnTop(String windowTitle) {
- String topAppWindow = getTopVisibleAppWindow();
- boolean success = topAppWindow.contains(windowTitle);
- String reason = "wanted=" + windowTitle + " found=" + topAppWindow;
- return new Result(success, getTimestamp(), "isAppWindowOnTop", reason);
- }
-
- /**
- * Checks if app window with {@code windowTitle} is visible.
- */
- public Result isAppWindowVisible(String windowTitle) {
- final String assertionName = "isAppWindowVisible";
- boolean titleFound = false;
- StackProto[] stacks = mProto.windowManagerService.rootWindowContainer
- .displays[DEFAULT_DISPLAY].stacks;
- for (StackProto stack : stacks) {
- for (TaskProto task : stack.tasks) {
- for (AppWindowTokenProto token : task.appWindowTokens) {
- if (token.name.contains(windowTitle)) {
- titleFound = true;
- for (WindowStateProto windowState : token.windowToken.windows) {
- if (windowState.windowContainer.visible) {
- return new Result(true /* success */, getTimestamp(),
- assertionName, "Window " + token.name +
- "is visible");
- }
- }
- }
- }
- }
- }
- String reason;
- if (!titleFound) {
- reason = "Window " + windowTitle + " cannot be found";
- } else {
- reason = "Window " + windowTitle + " is invisible";
- }
- return new Result(false /* success */, getTimestamp(), assertionName, reason);
- }
- }
-} \ No newline at end of file
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java
deleted file mode 100644
index 3d25fbed5135..000000000000
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java
+++ /dev/null
@@ -1,144 +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.content.Context;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.view.Surface;
-import android.view.WindowManager;
-
-import androidx.test.InstrumentationRegistry;
-
-/**
- * Helper functions to retrieve system window sizes and positions.
- */
-public class WindowUtils {
-
- public static Rect getDisplayBounds() {
- Point display = new Point();
- WindowManager wm =
- (WindowManager) InstrumentationRegistry.getContext().getSystemService(
- Context.WINDOW_SERVICE);
- wm.getDefaultDisplay().getRealSize(display);
- return new Rect(0, 0, display.x, display.y);
- }
-
- private static int getCurrentRotation() {
- WindowManager wm =
- (WindowManager) InstrumentationRegistry.getContext().getSystemService(
- Context.WINDOW_SERVICE);
- return wm.getDefaultDisplay().getRotation();
- }
-
- public static Rect getDisplayBounds(int requestedRotation) {
- Rect displayBounds = getDisplayBounds();
- int currentDisplayRotation = getCurrentRotation();
-
- boolean displayIsRotated = (currentDisplayRotation == Surface.ROTATION_90 ||
- currentDisplayRotation == Surface.ROTATION_270);
-
- boolean requestedDisplayIsRotated = requestedRotation == Surface.ROTATION_90 ||
- requestedRotation == Surface.ROTATION_270;
-
- // if the current orientation changes with the requested rotation,
- // flip height and width of display bounds.
- if (displayIsRotated != requestedDisplayIsRotated) {
- return new Rect(0, 0, displayBounds.height(), displayBounds.width());
- }
-
- return new Rect(0, 0, displayBounds.width(), displayBounds.height());
- }
-
-
- public static Rect getAppPosition(int requestedRotation) {
- Rect displayBounds = getDisplayBounds();
- int currentDisplayRotation = getCurrentRotation();
-
- boolean displayIsRotated = currentDisplayRotation == Surface.ROTATION_90 ||
- currentDisplayRotation == Surface.ROTATION_270;
-
- boolean requestedAppIsRotated = requestedRotation == Surface.ROTATION_90 ||
- requestedRotation == Surface.ROTATION_270;
-
- // display size will change if the display is reflected. Flip height and width of app if the
- // requested rotation is different from the current rotation.
- if (displayIsRotated != requestedAppIsRotated) {
- return new Rect(0, 0, displayBounds.height(), displayBounds.width());
- }
-
- return new Rect(0, 0, displayBounds.width(), displayBounds.height());
- }
-
- public static Rect getStatusBarPosition(int requestedRotation) {
- Resources resources = InstrumentationRegistry.getContext().getResources();
- String resourceName;
- Rect displayBounds = getDisplayBounds();
- int width;
- if (requestedRotation == Surface.ROTATION_0 || requestedRotation == Surface.ROTATION_180) {
- resourceName = "status_bar_height_portrait";
- width = Math.min(displayBounds.width(), displayBounds.height());
- } else {
- resourceName = "status_bar_height_landscape";
- width = Math.max(displayBounds.width(), displayBounds.height());
- }
-
- int resourceId = resources.getIdentifier(resourceName, "dimen", "android");
- int height = resources.getDimensionPixelSize(resourceId);
-
- return new Rect(0, 0, width, height);
- }
-
- public static Rect getNavigationBarPosition(int requestedRotation) {
- Resources resources = InstrumentationRegistry.getContext().getResources();
- Rect displayBounds = getDisplayBounds();
- int displayWidth = Math.min(displayBounds.width(), displayBounds.height());
- int displayHeight = Math.max(displayBounds.width(), displayBounds.height());
- int resourceId;
- if (requestedRotation == Surface.ROTATION_0 || requestedRotation == Surface.ROTATION_180) {
- resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
- int height = resources.getDimensionPixelSize(resourceId);
- return new Rect(0, displayHeight - height, displayWidth, displayHeight);
- } else {
- resourceId = resources.getIdentifier("navigation_bar_width", "dimen", "android");
- int width = resources.getDimensionPixelSize(resourceId);
- // swap display dimensions in landscape or seascape mode
- int temp = displayHeight;
- displayHeight = displayWidth;
- displayWidth = temp;
- if (requestedRotation == Surface.ROTATION_90) {
- return new Rect(0, 0, width, displayHeight);
- } else {
- return new Rect(displayWidth - width, 0, displayWidth, displayHeight);
- }
- }
- }
-
- public static int getNavigationBarHeight() {
- Resources resources = InstrumentationRegistry.getContext().getResources();
- int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
- return resources.getDimensionPixelSize(resourceId);
- }
-
- public static int getDockedStackDividerInset() {
- Resources resources = InstrumentationRegistry.getContext().getResources();
- int resourceId = resources.getIdentifier("docked_stack_divider_insets", "dimen",
- "android");
- return resources.getDimensionPixelSize(resourceId);
- }
-}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java
deleted file mode 100644
index 064cc2702f39..000000000000
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java
+++ /dev/null
@@ -1,192 +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.google.common.truth.Truth.assertAbout;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import androidx.annotation.Nullable;
-
-import com.android.server.wm.flicker.Assertions.Result;
-import com.android.server.wm.flicker.TransitionRunner.TransitionResult;
-
-import com.google.common.truth.FailureMetadata;
-import com.google.common.truth.Subject;
-
-import java.nio.file.Path;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
-/**
- * Truth subject for {@link WindowManagerTrace} objects.
- */
-public class WmTraceSubject extends Subject<WmTraceSubject, WindowManagerTrace> {
- // Boiler-plate Subject.Factory for WmTraceSubject
- private static final Subject.Factory<WmTraceSubject, WindowManagerTrace> FACTORY =
- new Subject.Factory<WmTraceSubject, WindowManagerTrace>() {
- @Override
- public WmTraceSubject createSubject(
- FailureMetadata fm, @Nullable WindowManagerTrace target) {
- return new WmTraceSubject(fm, target);
- }
- };
-
- private AssertionsChecker<WindowManagerTrace.Entry> mChecker = new AssertionsChecker<>();
-
- private WmTraceSubject(FailureMetadata fm, @Nullable WindowManagerTrace subject) {
- super(fm, subject);
- }
-
- // User-defined entry point
- public static WmTraceSubject assertThat(@Nullable WindowManagerTrace entry) {
- return assertAbout(FACTORY).that(entry);
- }
-
- // User-defined entry point
- public static WmTraceSubject assertThat(@Nullable TransitionResult result) {
- WindowManagerTrace entries = WindowManagerTrace.parseFrom(result.getWindowManagerTrace(),
- result.getWindowManagerTracePath());
- return assertWithMessage(result.toString()).about(FACTORY).that(entries);
- }
-
- // Static method for getting the subject factory (for use with assertAbout())
- public static Subject.Factory<WmTraceSubject, WindowManagerTrace> entries() {
- return FACTORY;
- }
-
- public void forAllEntries() {
- test();
- }
-
- public void forRange(long startTime, long endTime) {
- mChecker.filterByRange(startTime, endTime);
- test();
- }
-
- public WmTraceSubject then() {
- mChecker.checkChangingAssertions();
- return this;
- }
-
- public void inTheBeginning() {
- if (getSubject().getEntries().isEmpty()) {
- fail("No entries found.");
- }
- mChecker.checkFirstEntry();
- test();
- }
-
- public void atTheEnd() {
- if (getSubject().getEntries().isEmpty()) {
- fail("No entries found.");
- }
- mChecker.checkLastEntry();
- test();
- }
-
- private void test() {
- List<Result> failures = mChecker.test(getSubject().getEntries());
- if (!failures.isEmpty()) {
- Optional<Path> failureTracePath = getSubject().getSource();
- String failureLogs = failures.stream().map(Result::toString)
- .collect(Collectors.joining("\n"));
- String tracePath = "";
- if (failureTracePath.isPresent()) {
- tracePath = "\nWindowManager Trace can be found in: "
- + failureTracePath.get().toAbsolutePath() + "\n";
- }
- fail(tracePath + failureLogs);
- }
- }
-
- public WmTraceSubject showsAboveAppWindow(String partialWindowTitle) {
- mChecker.add(entry -> entry.isAboveAppWindowVisible(partialWindowTitle),
- "showsAboveAppWindow(" + partialWindowTitle + ")");
- return this;
- }
-
- public WmTraceSubject hidesAboveAppWindow(String partialWindowTitle) {
- mChecker.add(entry -> entry.isAboveAppWindowVisible(partialWindowTitle).negate(),
- "hidesAboveAppWindow" + "(" + partialWindowTitle + ")");
- return this;
- }
-
- public WmTraceSubject showsBelowAppWindow(String partialWindowTitle) {
- mChecker.add(entry -> entry.isBelowAppWindowVisible(partialWindowTitle),
- "showsBelowAppWindow(" + partialWindowTitle + ")");
- return this;
- }
-
- public WmTraceSubject hidesBelowAppWindow(String partialWindowTitle) {
- mChecker.add(entry -> entry.isBelowAppWindowVisible(partialWindowTitle).negate(),
- "hidesBelowAppWindow" + "(" + partialWindowTitle + ")");
- return this;
- }
-
- public WmTraceSubject showsImeWindow(String partialWindowTitle) {
- mChecker.add(entry -> entry.isImeWindowVisible(partialWindowTitle),
- "showsBelowAppWindow(" + partialWindowTitle + ")");
- return this;
- }
-
- public WmTraceSubject hidesImeWindow(String partialWindowTitle) {
- mChecker.add(entry -> entry.isImeWindowVisible(partialWindowTitle).negate(),
- "hidesImeWindow" + "(" + partialWindowTitle + ")");
- return this;
- }
-
- public WmTraceSubject showsAppWindowOnTop(String partialWindowTitle) {
- mChecker.add(
- entry -> {
- Result result = entry.isAppWindowVisible(partialWindowTitle);
- if (result.passed()) {
- result = entry.isVisibleAppWindowOnTop(partialWindowTitle);
- }
- return result;
- },
- "showsAppWindowOnTop(" + partialWindowTitle + ")"
- );
- return this;
- }
-
- public WmTraceSubject hidesAppWindowOnTop(String partialWindowTitle) {
- mChecker.add(
- entry -> {
- Result result = entry.isAppWindowVisible(partialWindowTitle).negate();
- if (result.failed()) {
- result = entry.isVisibleAppWindowOnTop(partialWindowTitle).negate();
- }
- return result;
- },
- "hidesAppWindowOnTop(" + partialWindowTitle + ")"
- );
- return this;
- }
-
- public WmTraceSubject showsAppWindow(String partialWindowTitle) {
- mChecker.add(entry -> entry.isAppWindowVisible(partialWindowTitle),
- "showsAppWindow(" + partialWindowTitle + ")");
- return this;
- }
-
- public WmTraceSubject hidesAppWindow(String partialWindowTitle) {
- mChecker.add(entry -> entry.isAppWindowVisible(partialWindowTitle).negate(),
- "hidesAppWindow(" + partialWindowTitle + ")");
- return this;
- }
-}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/helpers/AutomationUtils.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/helpers/AutomationUtils.java
deleted file mode 100644
index 6821ff02e371..000000000000
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/helpers/AutomationUtils.java
+++ /dev/null
@@ -1,263 +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.helpers;
-
-import static android.os.SystemClock.sleep;
-import static android.system.helpers.OverviewHelper.isRecentsInLauncher;
-import static android.view.Surface.ROTATION_0;
-
-import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.RemoteException;
-import android.support.test.launcherhelper.LauncherStrategyFactory;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.BySelector;
-import android.support.test.uiautomator.Configurator;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
-import android.util.Log;
-import android.util.Rational;
-import android.view.View;
-import android.view.ViewConfiguration;
-
-import androidx.test.InstrumentationRegistry;
-
-import com.android.server.wm.flicker.WindowUtils;
-
-/**
- * Collection of UI Automation helper functions.
- */
-public class AutomationUtils {
- private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
- private static final long FIND_TIMEOUT = 10000;
- private static final long LONG_PRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout() * 2L;
- private static final String TAG = "FLICKER";
-
- public static void wakeUpAndGoToHomeScreen() {
- UiDevice device = UiDevice.getInstance(InstrumentationRegistry
- .getInstrumentation());
- try {
- device.wakeUp();
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- device.pressHome();
- }
-
- /**
- * Sets {@link android.app.UiAutomation#waitForIdle(long, long)} global timeout to 0 causing
- * the {@link android.app.UiAutomation#waitForIdle(long, long)} function to timeout instantly.
- * This removes some delays when using the UIAutomator library required to create fast UI
- * transitions.
- */
- public static void setFastWait() {
- Configurator.getInstance().setWaitForIdleTimeout(0);
- }
-
- /**
- * Reverts {@link android.app.UiAutomation#waitForIdle(long, long)} to default behavior.
- */
- public static void setDefaultWait() {
- Configurator.getInstance().setWaitForIdleTimeout(10000);
- }
-
- public static boolean isQuickstepEnabled(UiDevice device) {
- return device.findObject(By.res(SYSTEMUI_PACKAGE, "recent_apps")) == null;
- }
-
- public static void openQuickstep(UiDevice device) {
- if (isQuickstepEnabled(device)) {
- int height = device.getDisplayHeight();
- UiObject2 navBar = device.findObject(By.res(SYSTEMUI_PACKAGE, "navigation_bar_frame"));
-
- Rect navBarVisibleBounds;
-
- // TODO(vishnun) investigate why this object cannot be found.
- if (navBar != null) {
- navBarVisibleBounds = navBar.getVisibleBounds();
- } else {
- Log.e(TAG, "Could not find nav bar, infer location");
- navBarVisibleBounds = WindowUtils.getNavigationBarPosition(ROTATION_0);
- }
-
- // Swipe from nav bar to 2/3rd down the screen.
- device.swipe(
- navBarVisibleBounds.centerX(), navBarVisibleBounds.centerY(),
- navBarVisibleBounds.centerX(), height * 2 / 3,
- (navBarVisibleBounds.centerY() - height * 2 / 3) / 100); // 100 px/step
- } else {
- try {
- device.pressRecentApps();
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
- BySelector RECENTS = By.res(SYSTEMUI_PACKAGE, "recents_view");
-
- // use a long timeout to wait until recents populated
- if (device.wait(
- Until.findObject(isRecentsInLauncher()
- ? getLauncherOverviewSelector(device) : RECENTS),
- 10000) == null) {
- fail("Recents didn't appear");
- }
- device.waitForIdle();
- }
-
- public static void clearRecents(UiDevice device) {
- if (isQuickstepEnabled(device)) {
- openQuickstep(device);
-
- for (int i = 0; i < 5; i++) {
- device.swipe(device.getDisplayWidth() / 2,
- device.getDisplayHeight() / 2, device.getDisplayWidth(),
- device.getDisplayHeight() / 2,
- 5);
-
- BySelector clearAllSelector = By.res("com.google.android.apps.nexuslauncher",
- "clear_all_button");
- UiObject2 clearAllButton = device.wait(Until.findObject(clearAllSelector), 100);
- if (clearAllButton != null) {
- clearAllButton.click();
- return;
- }
- }
- }
- }
-
- private static BySelector getLauncherOverviewSelector(UiDevice device) {
- return By.res(device.getLauncherPackageName(), "overview_panel");
- }
-
- private static void longPressRecents(UiDevice device) {
- BySelector recentsSelector = By.res(SYSTEMUI_PACKAGE, "recent_apps");
- UiObject2 recentsButton = device.wait(Until.findObject(recentsSelector), FIND_TIMEOUT);
- assertNotNull("Unable to find recents button", recentsButton);
- recentsButton.click(LONG_PRESS_TIMEOUT);
- }
-
- public static void launchSplitScreen(UiDevice device) {
- String mLauncherPackage = LauncherStrategyFactory.getInstance(device)
- .getLauncherStrategy().getSupportedLauncherPackage();
-
- if (isQuickstepEnabled(device)) {
- // Quickstep enabled
- openQuickstep(device);
-
- BySelector overviewIconSelector = By.res(mLauncherPackage, "icon")
- .clazz(View.class);
- UiObject2 overviewIcon = device.wait(Until.findObject(overviewIconSelector),
- FIND_TIMEOUT);
- assertNotNull("Unable to find app icon in Overview", overviewIcon);
- overviewIcon.click();
-
- BySelector splitscreenButtonSelector = By.text("Split screen");
- UiObject2 splitscreenButton = device.wait(Until.findObject(splitscreenButtonSelector),
- FIND_TIMEOUT);
- assertNotNull("Unable to find Split screen button in Overview", splitscreenButton);
- splitscreenButton.click();
- } else {
- // Classic long press recents
- longPressRecents(device);
- }
- // Wait for animation to complete.
- sleep(2000);
- }
-
- public static void exitSplitScreen(UiDevice device) {
- if (isQuickstepEnabled(device)) {
- // Quickstep enabled
- BySelector dividerSelector = By.res(SYSTEMUI_PACKAGE, "docked_divider_handle");
- UiObject2 divider = device.wait(Until.findObject(dividerSelector), FIND_TIMEOUT);
- assertNotNull("Unable to find Split screen divider", divider);
-
- // Drag the split screen divider to the top of the screen
- divider.drag(new Point(device.getDisplayWidth() / 2, 0), 400);
- } else {
- // Classic long press recents
- longPressRecents(device);
- }
- // Wait for animation to complete.
- sleep(2000);
- }
-
- public static void resizeSplitScreen(UiDevice device, Rational windowHeightRatio) {
- BySelector dividerSelector = By.res(SYSTEMUI_PACKAGE, "docked_divider_handle");
- UiObject2 divider = device.wait(Until.findObject(dividerSelector), FIND_TIMEOUT);
- assertNotNull("Unable to find Split screen divider", divider);
- int destHeight =
- (int) (WindowUtils.getDisplayBounds().height() * windowHeightRatio.floatValue());
- // Drag the split screen divider to so that the ratio of top window height and bottom
- // window height is windowHeightRatio
- device.drag(divider.getVisibleBounds().centerX(), divider.getVisibleBounds().centerY(),
- device.getDisplayWidth() / 2, destHeight, 10);
- //divider.drag(new Point(device.getDisplayWidth() / 2, destHeight), 400)
- divider = device.wait(Until.findObject(dividerSelector), FIND_TIMEOUT);
-
- // Wait for animation to complete.
- sleep(2000);
- }
-
- public static void closePipWindow(UiDevice device) {
- UiObject2 pipWindow = device.findObject(
- By.res(SYSTEMUI_PACKAGE, "background"));
- pipWindow.click();
- UiObject2 exitPipObject = device.findObject(
- By.res(SYSTEMUI_PACKAGE, "dismiss"));
- exitPipObject.click();
- // Wait for animation to complete.
- sleep(2000);
- }
-
- public static void expandPipWindow(UiDevice device) {
- UiObject2 pipWindow = device.findObject(
- By.res(SYSTEMUI_PACKAGE, "background"));
- pipWindow.click();
- pipWindow.click();
- }
-
- public static void stopPackage(Context context, String packageName) {
- runShellCommand("am force-stop " + packageName);
- int packageUid;
- try {
- packageUid = context.getPackageManager().getPackageUid(packageName, /* flags= */0);
- } catch (PackageManager.NameNotFoundException e) {
- return;
- }
- while (targetPackageIsRunning(packageUid)) {
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- //ignore
- }
- }
- }
-
- private static boolean targetPackageIsRunning(int uid) {
- final String result = runShellCommand(
- String.format("cmd activity get-uid-state %d", uid));
- return !result.contains("(NONEXISTENT)");
- }
-} \ No newline at end of file
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ITransitionMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ITransitionMonitor.java
deleted file mode 100644
index 67e0ecc1cde7..000000000000
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ITransitionMonitor.java
+++ /dev/null
@@ -1,63 +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.monitor;
-
-import android.os.Environment;
-
-import java.nio.file.Path;
-import java.nio.file.Paths;
-
-/**
- * Collects test artifacts during a UI transition.
- */
-public interface ITransitionMonitor {
- Path OUTPUT_DIR = Paths.get(Environment.getExternalStorageDirectory().toString(), "flicker");
-
- /**
- * Starts monitor.
- */
- void start();
-
- /**
- * Stops monitor.
- */
- void stop();
-
- /**
- * Saves any monitor artifacts to file adding {@code testTag} and {@code iteration}
- * to the file name.
- *
- * @param testTag suffix added to artifact name
- * @param iteration suffix added to artifact name
- *
- * @return Path to saved artifact
- */
- default Path save(String testTag, int iteration) {
- return save(testTag + "_" + iteration);
- }
-
- /**
- * Saves any monitor artifacts to file adding {@code testTag} to the file name.
- *
- * @param testTag suffix added to artifact name
- *
- * @return Path to saved artifact
- */
- default Path save(String testTag) {
- throw new UnsupportedOperationException("Save not implemented for this monitor");
- }
-}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java
deleted file mode 100644
index da75b3e86d6b..000000000000
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java
+++ /dev/null
@@ -1,64 +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.monitor;
-
-import android.os.RemoteException;
-import android.view.IWindowManager;
-import android.view.WindowManagerGlobal;
-
-/**
- * Captures Layers trace from SurfaceFlinger.
- */
-public class LayersTraceMonitor extends TraceMonitor {
- private IWindowManager mWm = WindowManagerGlobal.getWindowManagerService();
-
- public LayersTraceMonitor() {
- this(OUTPUT_DIR.toString());
- }
-
- public LayersTraceMonitor(String outputDir) {
- super(outputDir, "layers_trace.pb");
- }
-
- @Override
- public void start() {
- setEnabled(true);
- }
-
- @Override
- public void stop() {
- setEnabled(false);
- }
-
- @Override
- public boolean isEnabled() throws RemoteException {
- try {
- return mWm.isLayerTracing();
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- return false;
- }
-
- private void setEnabled(boolean isEnabled) {
- try {
- mWm.setLayerTracing(isEnabled);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
-}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java
deleted file mode 100644
index dce1c2739b15..000000000000
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java
+++ /dev/null
@@ -1,80 +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.monitor;
-
-import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
-
-import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
-
-import android.util.Log;
-
-import androidx.annotation.VisibleForTesting;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-
-/**
- * Captures screen contents and saves it as a mp4 video file.
- */
-public class ScreenRecorder implements ITransitionMonitor {
- @VisibleForTesting
- public static final Path DEFAULT_OUTPUT_PATH = OUTPUT_DIR.resolve("transition.mp4");
- private static final String TAG = "FLICKER";
- private Thread recorderThread;
-
- @VisibleForTesting
- public static Path getPath(String testTag) {
- return OUTPUT_DIR.resolve(testTag + ".mp4");
- }
-
- @Override
- public void start() {
- OUTPUT_DIR.toFile().mkdirs();
- String command = "screenrecord " + DEFAULT_OUTPUT_PATH;
- recorderThread = new Thread(() -> {
- try {
- Runtime.getRuntime().exec(command);
- } catch (IOException e) {
- Log.e(TAG, "Error executing " + command, e);
- }
- });
- recorderThread.start();
- }
-
- @Override
- public void stop() {
- runShellCommand("killall -s 2 screenrecord");
- try {
- recorderThread.join();
- } catch (InterruptedException e) {
- // ignore
- }
- }
-
- @Override
- public Path save(String testTag) {
- try {
- Path targetPath = Files.move(DEFAULT_OUTPUT_PATH, getPath(testTag),
- REPLACE_EXISTING);
- Log.i(TAG, "Video saved to " + targetPath.toString());
- return targetPath;
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java
deleted file mode 100644
index 1ba36bba92ef..000000000000
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java
+++ /dev/null
@@ -1,74 +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.monitor;
-
-import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
-
-import android.os.RemoteException;
-
-import androidx.annotation.VisibleForTesting;
-
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Locale;
-
-/**
- * Base class for monitors containing common logic to read the trace
- * as a byte array and save the trace to another location.
- */
-public abstract class TraceMonitor implements ITransitionMonitor {
- public static final String TAG = "FLICKER";
- private static final String TRACE_DIR = "/data/misc/wmtrace/";
-
- private Path mOutputDir;
- public String mTraceFileName;
-
- public abstract boolean isEnabled() throws RemoteException;
-
- public TraceMonitor(String outputDir, String traceFileName) {
- mOutputDir = Paths.get(outputDir);
- mTraceFileName = traceFileName;
- }
-
- /**
- * Saves trace file to the external storage directory suffixing the name with the testtag
- * and iteration.
- *
- * Moves the trace file from the default location via a shell command since the test app
- * does not have security privileges to access /data/misc/wmtrace.
- *
- * @param testTag suffix added to trace name used to identify trace
- *
- * @return Path to saved trace file
- */
- @Override
- public Path save(String testTag) {
- OUTPUT_DIR.toFile().mkdirs();
- Path traceFileCopy = getOutputTraceFilePath(testTag);
-
- // Read the input stream fully.
- String copyCommand = String.format(Locale.getDefault(), "mv %s%s %s", TRACE_DIR,
- mTraceFileName, traceFileCopy.toString());
- runShellCommand(copyCommand);
- return traceFileCopy;
- }
-
- @VisibleForTesting
- public Path getOutputTraceFilePath(String testTag) {
- return mOutputDir.resolve(mTraceFileName + "_" + testTag);
- }
-}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitor.java
deleted file mode 100644
index 3f86f0d001d7..000000000000
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitor.java
+++ /dev/null
@@ -1,100 +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.monitor;
-
-import static android.view.FrameStats.UNDEFINED_TIME_NANO;
-
-import android.app.Instrumentation;
-import android.util.Log;
-import android.view.FrameStats;
-
-/**
- * Monitors {@link android.view.WindowAnimationFrameStats} to detect janky frames.
- *
- * Adapted from {@link androidx.test.jank.internal.WindowAnimationFrameStatsMonitorImpl}
- * using the same threshold to determine jank.
- */
-public class WindowAnimationFrameStatsMonitor implements ITransitionMonitor {
-
- private static final String TAG = "FLICKER";
- // Maximum normalized error in frame duration before the frame is considered janky
- private static final double MAX_ERROR = 0.5f;
- // Maximum normalized frame duration before the frame is considered a pause
- private static final double PAUSE_THRESHOLD = 15.0f;
- private Instrumentation mInstrumentation;
- private FrameStats stats;
- private int numJankyFrames;
- private long mLongestFrameNano = 0L;
-
-
- /**
- * Constructs a WindowAnimationFrameStatsMonitor instance.
- */
- public WindowAnimationFrameStatsMonitor(Instrumentation instrumentation) {
- mInstrumentation = instrumentation;
- }
-
- private void analyze() {
- int frameCount = stats.getFrameCount();
- long refreshPeriodNano = stats.getRefreshPeriodNano();
-
- // Skip first frame
- for (int i = 2; i < frameCount; i++) {
- // Handle frames that have not been presented.
- if (stats.getFramePresentedTimeNano(i) == UNDEFINED_TIME_NANO) {
- // The animation must not have completed. Warn and break out of the loop.
- Log.w(TAG, "Skipping fenced frame.");
- break;
- }
- long frameDurationNano = stats.getFramePresentedTimeNano(i) -
- stats.getFramePresentedTimeNano(i - 1);
- double normalized = (double) frameDurationNano / refreshPeriodNano;
- if (normalized < PAUSE_THRESHOLD) {
- if (normalized > 1.0f + MAX_ERROR) {
- numJankyFrames++;
- }
- mLongestFrameNano = Math.max(mLongestFrameNano, frameDurationNano);
- }
- }
- }
-
- @Override
- public void start() {
- // Clear out any previous data
- numJankyFrames = 0;
- mLongestFrameNano = 0;
- mInstrumentation.getUiAutomation().clearWindowAnimationFrameStats();
- }
-
- @Override
- public void stop() {
- stats = mInstrumentation.getUiAutomation().getWindowAnimationFrameStats();
- analyze();
- }
-
- public boolean jankyFramesDetected() {
- return stats.getFrameCount() > 0 && numJankyFrames > 0;
- }
-
- @Override
- public String toString() {
- return stats.toString() +
- " RefreshPeriodNano:" + stats.getRefreshPeriodNano() +
- " NumJankyFrames:" + numJankyFrames +
- " LongestFrameNano:" + mLongestFrameNano;
- }
-} \ No newline at end of file
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java
deleted file mode 100644
index 11de4aa86343..000000000000
--- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java
+++ /dev/null
@@ -1,59 +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.monitor;
-
-import android.os.RemoteException;
-import android.view.IWindowManager;
-import android.view.WindowManagerGlobal;
-
-/**
- * Captures WindowManager trace from WindowManager.
- */
-public class WindowManagerTraceMonitor extends TraceMonitor {
- private IWindowManager mWm = WindowManagerGlobal.getWindowManagerService();
-
- public WindowManagerTraceMonitor() {
- this(OUTPUT_DIR.toString());
- }
-
- public WindowManagerTraceMonitor(String outputDir) {
- super(outputDir, "wm_trace.pb");
- }
-
- @Override
- public void start() {
- try {
- mWm.startWindowTrace();
- } catch (RemoteException e) {
- throw new RuntimeException("Could not start trace", e);
- }
- }
-
- @Override
- public void stop() {
- try {
- mWm.stopWindowTrace();
- } catch (RemoteException e) {
- throw new RuntimeException("Could not stop trace", e);
- }
- }
-
- @Override
- public boolean isEnabled() throws RemoteException{
- return mWm.isWindowTraceEnabled();
- }
-}
diff --git a/tests/FlickerTests/lib/test/Android.bp b/tests/FlickerTests/lib/test/Android.bp
deleted file mode 100644
index bfeb75b23469..000000000000
--- a/tests/FlickerTests/lib/test/Android.bp
+++ /dev/null
@@ -1,33 +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.
-//
-
-android_test {
- name: "FlickerLibTest",
- // sign this with platform cert, so this test is allowed to call private platform apis
- certificate: "platform",
- platform_apis: true,
- test_suites: ["tests"],
- srcs: ["src/**/*.java"],
- libs: ["android.test.runner"],
- static_libs: [
- "androidx.test.rules",
- "platform-test-annotations",
- "truth-prebuilt",
- "platformprotosnano",
- "layersprotosnano",
- "flickerlib",
- ],
-}
diff --git a/tests/FlickerTests/lib/test/AndroidManifest.xml b/tests/FlickerTests/lib/test/AndroidManifest.xml
deleted file mode 100644
index 6451a5710821..000000000000
--- a/tests/FlickerTests/lib/test/AndroidManifest.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright 2018 Google Inc. All Rights Reserved.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker">
-
- <uses-sdk android:minSdkVersion="27" android:targetSdkVersion="27"/>
- <!-- 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" />
- <!-- Capture screen contents -->
- <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
- <!-- Run layers trace -->
- <uses-permission android:name="android.permission.HARDWARE_TEST"/>
- <application android:label="FlickerLibTest">
- <uses-library android:name="android.test.runner"/>
- </application>
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker"
- android:label="WindowManager Flicker Lib Test">
- </instrumentation>
-
-</manifest> \ No newline at end of file
diff --git a/tests/FlickerTests/lib/test/AndroidTest.xml b/tests/FlickerTests/lib/test/AndroidTest.xml
deleted file mode 100644
index e4cc298a2aa8..000000000000
--- a/tests/FlickerTests/lib/test/AndroidTest.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright 2018 Google Inc. All Rights Reserved.
- -->
-<configuration description="Config for WindowManager Flicker Tests">
- <target_preparer class="com.google.android.tradefed.targetprep.GoogleDeviceSetup">
- <!-- keeps the screen on during tests -->
- <option name="screen-always-on" value="on" />
- <!-- prevents the phone from restarting -->
- <option name="force-skip-system-props" value="true" />
- </target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="cleanup-apks" value="true"/>
- <option name="test-file-name" value="FlickerLibTest.apk"/>
- </target_preparer>
- <test class="com.android.tradefed.testtype.AndroidJUnitTest">
- <option name="package" value="com.android.server.wm.flicker"/>
- <option name="hidden-api-checks" value="false" />
- </test>
-</configuration>
diff --git a/tests/FlickerTests/lib/test/assets/testdata/layers_trace_emptyregion.pb b/tests/FlickerTests/lib/test/assets/testdata/layers_trace_emptyregion.pb
deleted file mode 100644
index 98ee6f3ed269..000000000000
--- a/tests/FlickerTests/lib/test/assets/testdata/layers_trace_emptyregion.pb
+++ /dev/null
Binary files differ
diff --git a/tests/FlickerTests/lib/test/assets/testdata/layers_trace_invalid_layer_visibility.pb b/tests/FlickerTests/lib/test/assets/testdata/layers_trace_invalid_layer_visibility.pb
deleted file mode 100644
index 20572d79d826..000000000000
--- a/tests/FlickerTests/lib/test/assets/testdata/layers_trace_invalid_layer_visibility.pb
+++ /dev/null
Binary files differ
diff --git a/tests/FlickerTests/lib/test/assets/testdata/layers_trace_orphanlayers.pb b/tests/FlickerTests/lib/test/assets/testdata/layers_trace_orphanlayers.pb
deleted file mode 100644
index af4079707c69..000000000000
--- a/tests/FlickerTests/lib/test/assets/testdata/layers_trace_orphanlayers.pb
+++ /dev/null
Binary files differ
diff --git a/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome.pb b/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome.pb
deleted file mode 100644
index b3f31706f55c..000000000000
--- a/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome.pb
+++ /dev/null
Binary files differ
diff --git a/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome2.pb b/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome2.pb
deleted file mode 100644
index b3b73ce0518a..000000000000
--- a/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome2.pb
+++ /dev/null
Binary files differ
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsCheckerTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsCheckerTest.java
deleted file mode 100644
index 8e7fe1b4f942..000000000000
--- a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsCheckerTest.java
+++ /dev/null
@@ -1,181 +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.google.common.truth.Truth.assertThat;
-
-import com.android.server.wm.flicker.Assertions.Result;
-
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Contains {@link AssertionsChecker} tests.
- * To run this test: {@code atest FlickerLibTest:AssertionsCheckerTest}
- */
-public class AssertionsCheckerTest {
-
- /**
- * Returns a list of SimpleEntry objects with {@code data} and incremental timestamps starting
- * at 0.
- */
- private static List<SimpleEntry> getTestEntries(int... data) {
- List<SimpleEntry> entries = new ArrayList<>();
- for (int i = 0; i < data.length; i++) {
- entries.add(new SimpleEntry(i, data[i]));
- }
- return entries;
- }
-
- @Test
- public void canCheckAllEntries() {
- AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
- checker.add(SimpleEntry::isData42, "isData42");
-
- List<Result> failures = checker.test(getTestEntries(1, 1, 1, 1, 1));
-
- assertThat(failures).hasSize(5);
- }
-
- @Test
- public void canCheckFirstEntry() {
- AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
- checker.checkFirstEntry();
- checker.add(SimpleEntry::isData42, "isData42");
-
- List<Result> failures = checker.test(getTestEntries(1, 1, 1, 1, 1));
-
- assertThat(failures).hasSize(1);
- assertThat(failures.get(0).timestamp).isEqualTo(0);
- }
-
- @Test
- public void canCheckLastEntry() {
- AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
- checker.checkLastEntry();
- checker.add(SimpleEntry::isData42, "isData42");
-
- List<Result> failures = checker.test(getTestEntries(1, 1, 1, 1, 1));
-
- assertThat(failures).hasSize(1);
- assertThat(failures.get(0).timestamp).isEqualTo(4);
- }
-
- @Test
- public void canCheckRangeOfEntries() {
- AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
- checker.filterByRange(1, 2);
- checker.add(SimpleEntry::isData42, "isData42");
-
- List<Result> failures = checker.test(getTestEntries(1, 42, 42, 1, 1));
-
- assertThat(failures).hasSize(0);
- }
-
- @Test
- public void emptyRangePasses() {
- AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
- checker.filterByRange(9, 10);
- checker.add(SimpleEntry::isData42, "isData42");
-
- List<Result> failures = checker.test(getTestEntries(1, 1, 1, 1, 1));
-
- assertThat(failures).isEmpty();
- }
-
- @Test
- public void canCheckChangingAssertions() {
- AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
- checker.add(SimpleEntry::isData42, "isData42");
- checker.add(SimpleEntry::isData0, "isData0");
- checker.checkChangingAssertions();
-
- List<Result> failures = checker.test(getTestEntries(42, 0, 0, 0, 0));
-
- assertThat(failures).isEmpty();
- }
-
- @Test
- public void canCheckChangingAssertions_withNoAssertions() {
- AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
- checker.checkChangingAssertions();
-
- List<Result> failures = checker.test(getTestEntries(42, 0, 0, 0, 0));
-
- assertThat(failures).isEmpty();
- }
-
- @Test
- public void canCheckChangingAssertions_withSingleAssertion() {
- AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
- checker.add(SimpleEntry::isData42, "isData42");
- checker.checkChangingAssertions();
-
- List<Result> failures = checker.test(getTestEntries(42, 42, 42, 42, 42));
-
- assertThat(failures).isEmpty();
- }
-
- @Test
- public void canFailCheckChangingAssertions_ifStartingAssertionFails() {
- AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
- checker.add(SimpleEntry::isData42, "isData42");
- checker.add(SimpleEntry::isData0, "isData0");
- checker.checkChangingAssertions();
-
- List<Result> failures = checker.test(getTestEntries(0, 0, 0, 0, 0));
-
- assertThat(failures).hasSize(1);
- }
-
- @Test
- public void canFailCheckChangingAssertions_ifStartingAssertionAlwaysPasses() {
- AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
- checker.add(SimpleEntry::isData42, "isData42");
- checker.add(SimpleEntry::isData0, "isData0");
- checker.checkChangingAssertions();
-
- List<Result> failures = checker.test(getTestEntries(0, 0, 0, 0, 0));
-
- assertThat(failures).hasSize(1);
- }
-
- static class SimpleEntry implements ITraceEntry {
- long timestamp;
- int data;
-
- SimpleEntry(long timestamp, int data) {
- this.timestamp = timestamp;
- this.data = data;
- }
-
- @Override
- public long getTimestamp() {
- return timestamp;
- }
-
- Result isData42() {
- return new Result(this.data == 42, this.timestamp, "is42", "");
- }
-
- Result isData0() {
- return new Result(this.data == 0, this.timestamp, "is42", "");
- }
- }
-}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsTest.java
deleted file mode 100644
index 7fd178ca6e51..000000000000
--- a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsTest.java
+++ /dev/null
@@ -1,60 +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.google.common.truth.Truth.assertThat;
-
-import com.android.server.wm.flicker.Assertions.Result;
-
-import org.junit.Test;
-
-/**
- * Contains {@link Assertions} tests.
- * To run this test: {@code atest FlickerLibTest:AssertionsTest}
- */
-public class AssertionsTest {
- @Test
- public void traceEntryAssertionCanNegateResult() {
- Assertions.TraceAssertion<Integer> assertNumEquals42 =
- getIntegerTraceEntryAssertion();
-
- assertThat(assertNumEquals42.apply(1).success).isFalse();
- assertThat(assertNumEquals42.negate().apply(1).success).isTrue();
-
- assertThat(assertNumEquals42.apply(42).success).isTrue();
- assertThat(assertNumEquals42.negate().apply(42).success).isFalse();
- }
-
- @Test
- public void resultCanBeNegated() {
- String reason = "Everything is fine!";
- Result result = new Result(true, 0, "TestAssert", reason);
- Result negatedResult = result.negate();
- assertThat(negatedResult.success).isFalse();
- assertThat(negatedResult.reason).isEqualTo(reason);
- assertThat(negatedResult.assertionName).isEqualTo("!TestAssert");
- }
-
- private Assertions.TraceAssertion<Integer> getIntegerTraceEntryAssertion() {
- return (num) -> {
- if (num == 42) {
- return new Result(true, "Num equals 42");
- }
- return new Result(false, "Num doesn't equal 42, actual:" + num);
- };
- }
-} \ No newline at end of file
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceSubjectTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceSubjectTest.java
deleted file mode 100644
index d06c5d76552b..000000000000
--- a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceSubjectTest.java
+++ /dev/null
@@ -1,88 +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.LayersTraceSubject.assertThat;
-import static com.android.server.wm.flicker.TestFileUtils.readTestFile;
-
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import static org.junit.Assert.fail;
-
-import android.graphics.Rect;
-
-import org.junit.Test;
-
-import java.nio.file.Paths;
-
-/**
- * Contains {@link LayersTraceSubject} tests.
- * To run this test: {@code atest FlickerLibTest:LayersTraceSubjectTest}
- */
-public class LayersTraceSubjectTest {
- private static final Rect displayRect = new Rect(0, 0, 1440, 2880);
-
- private static LayersTrace readLayerTraceFromFile(String relativePath) {
- try {
- return LayersTrace.parseFrom(readTestFile(relativePath), Paths.get(relativePath));
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- @Test
- public void testCanDetectEmptyRegionFromLayerTrace() {
- LayersTrace layersTraceEntries = readLayerTraceFromFile("layers_trace_emptyregion.pb");
- try {
- assertThat(layersTraceEntries).coversRegion(displayRect).forAllEntries();
- fail("Assertion passed");
- } catch (AssertionError e) {
- assertWithMessage("Contains path to trace")
- .that(e.getMessage()).contains("layers_trace_emptyregion.pb");
- assertWithMessage("Contains timestamp")
- .that(e.getMessage()).contains("0h38m28s8ms");
- assertWithMessage("Contains assertion function")
- .that(e.getMessage()).contains("coversRegion");
- assertWithMessage("Contains debug info")
- .that(e.getMessage()).contains("Region to test: " + displayRect);
- assertWithMessage("Contains debug info")
- .that(e.getMessage()).contains("first empty point: 0, 99");
- }
- }
-
- @Test
- public void testCanDetectIncorrectVisibilityFromLayerTrace() {
- LayersTrace layersTraceEntries = readLayerTraceFromFile(
- "layers_trace_invalid_layer_visibility.pb");
- try {
- assertThat(layersTraceEntries).showsLayer("com.android.server.wm.flicker.testapp")
- .then().hidesLayer("com.android.server.wm.flicker.testapp").forAllEntries();
- fail("Assertion passed");
- } catch (AssertionError e) {
- assertWithMessage("Contains path to trace")
- .that(e.getMessage()).contains("layers_trace_invalid_layer_visibility.pb");
- assertWithMessage("Contains timestamp")
- .that(e.getMessage()).contains("70h13m14s303ms");
- assertWithMessage("Contains assertion function")
- .that(e.getMessage()).contains("!isVisible");
- assertWithMessage("Contains debug info")
- .that(e.getMessage()).contains(
- "com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp"
- + ".SimpleActivity#0 is visible");
- }
- }
-}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceTest.java
deleted file mode 100644
index 7d77126fd7d4..000000000000
--- a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceTest.java
+++ /dev/null
@@ -1,230 +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.TestFileUtils.readTestFile;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import static org.junit.Assert.fail;
-
-import android.content.Context;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.view.WindowManager;
-
-import androidx.test.InstrumentationRegistry;
-
-import org.junit.Test;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * Contains {@link LayersTrace} tests.
- * To run this test: {@code atest FlickerLibTest:LayersTraceTest}
- */
-public class LayersTraceTest {
- private static LayersTrace readLayerTraceFromFile(String relativePath) {
- try {
- return LayersTrace.parseFrom(readTestFile(relativePath));
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- private static Rect getDisplayBounds() {
- Point display = new Point();
- WindowManager wm =
- (WindowManager) InstrumentationRegistry.getContext().getSystemService(
- Context.WINDOW_SERVICE);
- wm.getDefaultDisplay().getRealSize(display);
- return new Rect(0, 0, display.x, display.y);
- }
-
- @Test
- public void canParseAllLayers() {
- LayersTrace trace = readLayerTraceFromFile(
- "layers_trace_emptyregion.pb");
- assertThat(trace.getEntries()).isNotEmpty();
- assertThat(trace.getEntries().get(0).getTimestamp()).isEqualTo(2307984557311L);
- assertThat(trace.getEntries().get(trace.getEntries().size() - 1).getTimestamp())
- .isEqualTo(2308521813510L);
- List<LayersTrace.Layer> flattenedLayers = trace.getEntries().get(0).asFlattenedLayers();
- String msg = "Layers:\n" + flattenedLayers.stream().map(layer -> layer.mProto.name)
- .collect(Collectors.joining("\n\t"));
- assertWithMessage(msg).that(flattenedLayers).hasSize(47);
- }
-
- @Test
- public void canParseVisibleLayers() {
- LayersTrace trace = readLayerTraceFromFile(
- "layers_trace_emptyregion.pb");
- assertThat(trace.getEntries()).isNotEmpty();
- assertThat(trace.getEntries().get(0).getTimestamp()).isEqualTo(2307984557311L);
- assertThat(trace.getEntries().get(trace.getEntries().size() - 1).getTimestamp())
- .isEqualTo(2308521813510L);
- List<LayersTrace.Layer> flattenedLayers = trace.getEntries().get(0).asFlattenedLayers();
- List<LayersTrace.Layer> visibleLayers = flattenedLayers.stream()
- .filter(layer -> layer.isVisible() && !layer.isHiddenByParent())
- .collect(Collectors.toList());
-
- String msg = "Visible Layers:\n" + visibleLayers.stream()
- .map(layer -> layer.mProto.name)
- .collect(Collectors.joining("\n\t"));
-
- assertWithMessage(msg).that(visibleLayers).hasSize(9);
- }
-
- @Test
- public void canParseLayerHierarchy() {
- LayersTrace trace = readLayerTraceFromFile(
- "layers_trace_emptyregion.pb");
- assertThat(trace.getEntries()).isNotEmpty();
- assertThat(trace.getEntries().get(0).getTimestamp()).isEqualTo(2307984557311L);
- assertThat(trace.getEntries().get(trace.getEntries().size() - 1).getTimestamp())
- .isEqualTo(2308521813510L);
- List<LayersTrace.Layer> layers = trace.getEntries().get(0).getRootLayers();
- assertThat(layers).hasSize(2);
- assertThat(layers.get(0).mChildren).hasSize(layers.get(0).mProto.children.length);
- assertThat(layers.get(1).mChildren).hasSize(layers.get(1).mProto.children.length);
- }
-
- // b/76099859
- @Test
- public void canDetectOrphanLayers() {
- try {
- readLayerTraceFromFile(
- "layers_trace_orphanlayers.pb");
- fail("Failed to detect orphaned layers.");
- } catch (RuntimeException exception) {
- assertThat(exception.getMessage()).contains(
- "Failed to parse layers trace. Found orphan layers "
- + "with parent layer id:1006 : 49");
- }
- }
-
- // b/75276931
- @Test
- public void canDetectUncoveredRegion() {
- LayersTrace trace = readLayerTraceFromFile(
- "layers_trace_emptyregion.pb");
- LayersTrace.Entry entry = trace.getEntry(2308008331271L);
-
- Assertions.Result result = entry.coversRegion(getDisplayBounds());
-
- assertThat(result.failed()).isTrue();
- assertThat(result.reason).contains("Region to test: Rect(0, 0 - 1440, 2880)");
- assertThat(result.reason).contains("first empty point: 0, 99");
- assertThat(result.reason).contains("visible regions:");
- assertWithMessage("Reason contains list of visible regions")
- .that(result.reason).contains("StatusBar#0Rect(0, 0 - 1440, 98");
- }
-
- // Visible region tests
- @Test
- public void canTestLayerVisibleRegion_layerDoesNotExist() {
- LayersTrace trace = readLayerTraceFromFile(
- "layers_trace_emptyregion.pb");
- LayersTrace.Entry entry = trace.getEntry(2308008331271L);
-
- final Rect expectedVisibleRegion = new Rect(0, 0, 1, 1);
- Assertions.Result result = entry.hasVisibleRegion("ImaginaryLayer",
- expectedVisibleRegion);
-
- assertThat(result.failed()).isTrue();
- assertThat(result.reason).contains("Could not find ImaginaryLayer");
- }
-
- @Test
- public void canTestLayerVisibleRegion_layerDoesNotHaveExpectedVisibleRegion() {
- LayersTrace trace = readLayerTraceFromFile(
- "layers_trace_emptyregion.pb");
- LayersTrace.Entry entry = trace.getEntry(2307993020072L);
-
- final Rect expectedVisibleRegion = new Rect(0, 0, 1, 1);
- Assertions.Result result = entry.hasVisibleRegion("NexusLauncherActivity#2",
- expectedVisibleRegion);
-
- assertThat(result.failed()).isTrue();
- assertThat(result.reason).contains(
- "Layer com.google.android.apps.nexuslauncher/com.google.android.apps"
- + ".nexuslauncher.NexusLauncherActivity#2 is invisible: activeBuffer=null"
- + " type != ColorLayer flags=1 (FLAG_HIDDEN set) visible region is empty");
- }
-
- @Test
- public void canTestLayerVisibleRegion_layerIsHiddenByParent() {
- LayersTrace trace = readLayerTraceFromFile(
- "layers_trace_emptyregion.pb");
- LayersTrace.Entry entry = trace.getEntry(2308455948035L);
-
- final Rect expectedVisibleRegion = new Rect(0, 0, 1, 1);
- Assertions.Result result = entry.hasVisibleRegion(
- "SurfaceView - com.android.chrome/com.google.android.apps.chrome.Main",
- expectedVisibleRegion);
-
- assertThat(result.failed()).isTrue();
- assertThat(result.reason).contains(
- "Layer SurfaceView - com.android.chrome/com.google.android.apps.chrome.Main#0 is "
- + "hidden by parent: com.android.chrome/com.google.android.apps.chrome"
- + ".Main#0");
- }
-
- @Test
- public void canTestLayerVisibleRegion_incorrectRegionSize() {
- LayersTrace trace = readLayerTraceFromFile(
- "layers_trace_emptyregion.pb");
- LayersTrace.Entry entry = trace.getEntry(2308008331271L);
-
- final Rect expectedVisibleRegion = new Rect(0, 0, 1440, 99);
- Assertions.Result result = entry.hasVisibleRegion(
- "StatusBar",
- expectedVisibleRegion);
-
- assertThat(result.failed()).isTrue();
- assertThat(result.reason).contains("StatusBar#0 has visible "
- + "region:Rect(0, 0 - 1440, 98) expected:Rect(0, 0 - 1440, 99)");
- }
-
- @Test
- public void canTestLayerVisibleRegion() {
- LayersTrace trace = readLayerTraceFromFile(
- "layers_trace_emptyregion.pb");
- LayersTrace.Entry entry = trace.getEntry(2308008331271L);
-
- final Rect expectedVisibleRegion = new Rect(0, 0, 1440, 98);
- Assertions.Result result = entry.hasVisibleRegion("StatusBar", expectedVisibleRegion);
-
- assertThat(result.passed()).isTrue();
- }
-
- @Test
- public void canTestLayerVisibleRegion_layerIsNotVisible() {
- LayersTrace trace = readLayerTraceFromFile(
- "layers_trace_invalid_layer_visibility.pb");
- LayersTrace.Entry entry = trace.getEntry(252794268378458L);
-
- Assertions.Result result = entry.isVisible("com.android.server.wm.flicker.testapp");
- assertThat(result.failed()).isTrue();
- assertThat(result.reason).contains(
- "Layer com.android.server.wm.flicker.testapp/com.android.server.wm.flicker"
- + ".testapp.SimpleActivity#0 is invisible: type != ColorLayer visible "
- + "region is empty");
- }
-}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TestFileUtils.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TestFileUtils.java
deleted file mode 100644
index c46175c1a977..000000000000
--- a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TestFileUtils.java
+++ /dev/null
@@ -1,36 +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.content.Context;
-
-import androidx.test.InstrumentationRegistry;
-
-import com.google.common.io.ByteStreams;
-
-import java.io.InputStream;
-
-/**
- * Helper functions for test file resources.
- */
-class TestFileUtils {
- static byte[] readTestFile(String relativePath) throws Exception {
- Context context = InstrumentationRegistry.getContext();
- InputStream in = context.getResources().getAssets().open("testdata/" + relativePath);
- return ByteStreams.toByteArray(in);
- }
-}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TransitionRunnerTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TransitionRunnerTest.java
deleted file mode 100644
index 9c5e2059a0e6..000000000000
--- a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TransitionRunnerTest.java
+++ /dev/null
@@ -1,258 +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.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-
-import android.os.Environment;
-
-import com.android.server.wm.flicker.TransitionRunner.TransitionBuilder;
-import com.android.server.wm.flicker.TransitionRunner.TransitionResult;
-import com.android.server.wm.flicker.monitor.LayersTraceMonitor;
-import com.android.server.wm.flicker.monitor.ScreenRecorder;
-import com.android.server.wm.flicker.monitor.WindowAnimationFrameStatsMonitor;
-import com.android.server.wm.flicker.monitor.WindowManagerTraceMonitor;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.InOrder;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.IOException;
-import java.nio.file.Paths;
-import java.util.List;
-
-/**
- * Contains {@link TransitionRunner} tests.
- * {@code atest FlickerLibTest:TransitionRunnerTest}
- */
-public class TransitionRunnerTest {
- @Mock
- private SimpleUiTransitions mTransitionsMock;
- @Mock
- private ScreenRecorder mScreenRecorderMock;
- @Mock
- private WindowManagerTraceMonitor mWindowManagerTraceMonitorMock;
- @Mock
- private LayersTraceMonitor mLayersTraceMonitorMock;
- @Mock
- private WindowAnimationFrameStatsMonitor mWindowAnimationFrameStatsMonitor;
- @InjectMocks
- private TransitionBuilder mTransitionBuilder;
-
- @Before
- public void init() {
- MockitoAnnotations.initMocks(this);
- }
-
- @Test
- public void transitionsRunInOrder() {
- TransitionRunner.newBuilder()
- .runBeforeAll(mTransitionsMock::turnOnDevice)
- .runBefore(mTransitionsMock::openApp)
- .run(mTransitionsMock::performMagic)
- .runAfter(mTransitionsMock::closeApp)
- .runAfterAll(mTransitionsMock::cleanUpTracks)
- .skipLayersTrace()
- .skipWindowManagerTrace()
- .build()
- .run();
-
- InOrder orderVerifier = inOrder(mTransitionsMock);
- orderVerifier.verify(mTransitionsMock).turnOnDevice();
- orderVerifier.verify(mTransitionsMock).openApp();
- orderVerifier.verify(mTransitionsMock).performMagic();
- orderVerifier.verify(mTransitionsMock).closeApp();
- orderVerifier.verify(mTransitionsMock).cleanUpTracks();
- }
-
- @Test
- public void canCombineTransitions() {
- TransitionRunner.newBuilder()
- .runBeforeAll(mTransitionsMock::turnOnDevice)
- .runBeforeAll(mTransitionsMock::turnOnDevice)
- .runBefore(mTransitionsMock::openApp)
- .runBefore(mTransitionsMock::openApp)
- .run(mTransitionsMock::performMagic)
- .run(mTransitionsMock::performMagic)
- .runAfter(mTransitionsMock::closeApp)
- .runAfter(mTransitionsMock::closeApp)
- .runAfterAll(mTransitionsMock::cleanUpTracks)
- .runAfterAll(mTransitionsMock::cleanUpTracks)
- .skipLayersTrace()
- .skipWindowManagerTrace()
- .build()
- .run();
-
- final int wantedNumberOfInvocations = 2;
- verify(mTransitionsMock, times(wantedNumberOfInvocations)).turnOnDevice();
- verify(mTransitionsMock, times(wantedNumberOfInvocations)).openApp();
- verify(mTransitionsMock, times(wantedNumberOfInvocations)).performMagic();
- verify(mTransitionsMock, times(wantedNumberOfInvocations)).closeApp();
- verify(mTransitionsMock, times(wantedNumberOfInvocations)).cleanUpTracks();
- }
-
- @Test
- public void emptyTransitionPasses() {
- List<TransitionResult> results = TransitionRunner.newBuilder()
- .skipLayersTrace()
- .skipWindowManagerTrace()
- .build()
- .run()
- .getResults();
- assertThat(results).hasSize(1);
- assertThat(results.get(0).layersTraceExists()).isFalse();
- assertThat(results.get(0).windowManagerTraceExists()).isFalse();
- assertThat(results.get(0).screenCaptureVideoExists()).isFalse();
- }
-
- @Test
- public void canRepeatTransitions() {
- final int wantedNumberOfInvocations = 10;
- TransitionRunner.newBuilder()
- .runBeforeAll(mTransitionsMock::turnOnDevice)
- .runBefore(mTransitionsMock::openApp)
- .run(mTransitionsMock::performMagic)
- .runAfter(mTransitionsMock::closeApp)
- .runAfterAll(mTransitionsMock::cleanUpTracks)
- .repeat(wantedNumberOfInvocations)
- .skipLayersTrace()
- .skipWindowManagerTrace()
- .build()
- .run();
- verify(mTransitionsMock).turnOnDevice();
- verify(mTransitionsMock, times(wantedNumberOfInvocations)).openApp();
- verify(mTransitionsMock, times(wantedNumberOfInvocations)).performMagic();
- verify(mTransitionsMock, times(wantedNumberOfInvocations)).closeApp();
- verify(mTransitionsMock).cleanUpTracks();
- }
-
- private void emptyTask() {
-
- }
-
- @Test
- public void canCaptureWindowManagerTrace() {
- mTransitionBuilder
- .run(this::emptyTask)
- .includeJankyRuns()
- .skipLayersTrace()
- .withTag("mCaptureWmTraceTransitionRunner")
- .build().run();
- InOrder orderVerifier = inOrder(mWindowManagerTraceMonitorMock);
- orderVerifier.verify(mWindowManagerTraceMonitorMock).start();
- orderVerifier.verify(mWindowManagerTraceMonitorMock).stop();
- orderVerifier.verify(mWindowManagerTraceMonitorMock)
- .save("mCaptureWmTraceTransitionRunner", 0);
- verifyNoMoreInteractions(mWindowManagerTraceMonitorMock);
- }
-
- @Test
- public void canCaptureLayersTrace() {
- mTransitionBuilder
- .run(this::emptyTask)
- .includeJankyRuns()
- .skipWindowManagerTrace()
- .withTag("mCaptureLayersTraceTransitionRunner")
- .build().run();
- InOrder orderVerifier = inOrder(mLayersTraceMonitorMock);
- orderVerifier.verify(mLayersTraceMonitorMock).start();
- orderVerifier.verify(mLayersTraceMonitorMock).stop();
- orderVerifier.verify(mLayersTraceMonitorMock)
- .save("mCaptureLayersTraceTransitionRunner", 0);
- verifyNoMoreInteractions(mLayersTraceMonitorMock);
- }
-
- @Test
- public void canRecordEachRun() throws IOException {
- mTransitionBuilder
- .run(this::emptyTask)
- .withTag("mRecordEachRun")
- .recordEachRun()
- .includeJankyRuns()
- .skipLayersTrace()
- .skipWindowManagerTrace()
- .repeat(2)
- .build().run();
- InOrder orderVerifier = inOrder(mScreenRecorderMock);
- orderVerifier.verify(mScreenRecorderMock).start();
- orderVerifier.verify(mScreenRecorderMock).stop();
- orderVerifier.verify(mScreenRecorderMock).save("mRecordEachRun", 0);
- orderVerifier.verify(mScreenRecorderMock).start();
- orderVerifier.verify(mScreenRecorderMock).stop();
- orderVerifier.verify(mScreenRecorderMock).save("mRecordEachRun", 1);
- verifyNoMoreInteractions(mScreenRecorderMock);
- }
-
- @Test
- public void canRecordAllRuns() throws IOException {
- doReturn(Paths.get(Environment.getExternalStorageDirectory().getAbsolutePath(),
- "mRecordAllRuns.mp4")).when(mScreenRecorderMock).save("mRecordAllRuns");
- mTransitionBuilder
- .run(this::emptyTask)
- .recordAllRuns()
- .includeJankyRuns()
- .skipLayersTrace()
- .skipWindowManagerTrace()
- .withTag("mRecordAllRuns")
- .repeat(2)
- .build().run();
- InOrder orderVerifier = inOrder(mScreenRecorderMock);
- orderVerifier.verify(mScreenRecorderMock).start();
- orderVerifier.verify(mScreenRecorderMock).stop();
- orderVerifier.verify(mScreenRecorderMock).save("mRecordAllRuns");
- verifyNoMoreInteractions(mScreenRecorderMock);
- }
-
- @Test
- public void canSkipJankyRuns() {
- doReturn(false).doReturn(true).doReturn(false)
- .when(mWindowAnimationFrameStatsMonitor).jankyFramesDetected();
- List<TransitionResult> results = mTransitionBuilder
- .run(this::emptyTask)
- .skipLayersTrace()
- .skipWindowManagerTrace()
- .repeat(3)
- .build().run().getResults();
- assertThat(results).hasSize(2);
- }
-
- public static class SimpleUiTransitions {
- public void turnOnDevice() {
- }
-
- public void openApp() {
- }
-
- public void performMagic() {
- }
-
- public void closeApp() {
- }
-
- public void cleanUpTracks() {
- }
- }
-}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WindowManagerTraceTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WindowManagerTraceTest.java
deleted file mode 100644
index 49278718932c..000000000000
--- a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WindowManagerTraceTest.java
+++ /dev/null
@@ -1,108 +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.TestFileUtils.readTestFile;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.android.server.wm.flicker.Assertions.Result;
-
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Contains {@link WindowManagerTrace} tests.
- * To run this test: {@code atest FlickerLibTest:WindowManagerTraceTest}
- */
-public class WindowManagerTraceTest {
- private WindowManagerTrace mTrace;
-
- private static WindowManagerTrace readWindowManagerTraceFromFile(String relativePath) {
- try {
- return WindowManagerTrace.parseFrom(readTestFile(relativePath));
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- @Before
- public void setup() {
- mTrace = readWindowManagerTraceFromFile("wm_trace_openchrome.pb");
- }
-
- @Test
- public void canParseAllEntries() {
- assertThat(mTrace.getEntries().get(0).getTimestamp()).isEqualTo(241777211939236L);
- assertThat(mTrace.getEntries().get(mTrace.getEntries().size() - 1).getTimestamp()).isEqualTo
- (241779809471942L);
- }
-
- @Test
- public void canDetectAboveAppWindowVisibility() {
- WindowManagerTrace.Entry entry = mTrace.getEntry(241777211939236L);
- Result result = entry.isAboveAppWindowVisible("NavigationBar");
- assertThat(result.passed()).isTrue();
- }
-
- @Test
- public void canDetectBelowAppWindowVisibility() {
- WindowManagerTrace.Entry entry = mTrace.getEntry(241777211939236L);
- Result result = entry.isBelowAppWindowVisible("wallpaper");
- assertThat(result.passed()).isTrue();
- }
-
- @Test
- public void canDetectAppWindowVisibility() {
- WindowManagerTrace.Entry entry = mTrace.getEntry(241777211939236L);
- Result result = entry.isAppWindowVisible("com.google.android.apps.nexuslauncher");
- assertThat(result.passed()).isTrue();
- }
-
- @Test
- public void canFailWithReasonForVisibilityChecks_windowNotFound() {
- WindowManagerTrace.Entry entry = mTrace.getEntry(241777211939236L);
- Result result = entry.isAboveAppWindowVisible("ImaginaryWindow");
- assertThat(result.failed()).isTrue();
- assertThat(result.reason).contains("ImaginaryWindow cannot be found");
- }
-
- @Test
- public void canFailWithReasonForVisibilityChecks_windowNotVisible() {
- WindowManagerTrace.Entry entry = mTrace.getEntry(241777211939236L);
- Result result = entry.isAboveAppWindowVisible("AssistPreviewPanel");
- assertThat(result.failed()).isTrue();
- assertThat(result.reason).contains("AssistPreviewPanel is invisible");
- }
-
- @Test
- public void canDetectAppZOrder() {
- WindowManagerTrace.Entry entry = mTrace.getEntry(241778130296410L);
- Result result = entry.isVisibleAppWindowOnTop("com.google.android.apps.chrome");
- assertThat(result.passed()).isTrue();
- }
-
- @Test
- public void canFailWithReasonForZOrderChecks_windowNotOnTop() {
- WindowManagerTrace.Entry entry = mTrace.getEntry(241778130296410L);
- Result result = entry.isVisibleAppWindowOnTop("com.google.android.apps.nexuslauncher");
- assertThat(result.failed()).isTrue();
- assertThat(result.reason).contains("wanted=com.google.android.apps.nexuslauncher");
- assertThat(result.reason).contains("found=com.android.chrome/"
- + "com.google.android.apps.chrome.Main");
- }
-}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WmTraceSubjectTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WmTraceSubjectTest.java
deleted file mode 100644
index d547a188a663..000000000000
--- a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WmTraceSubjectTest.java
+++ /dev/null
@@ -1,49 +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.TestFileUtils.readTestFile;
-import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
-
-import org.junit.Test;
-
-/**
- * Contains {@link WmTraceSubject} tests.
- * To run this test: {@code atest FlickerLibTest:WmTraceSubjectTest}
- */
-public class WmTraceSubjectTest {
- private static WindowManagerTrace readWmTraceFromFile(String relativePath) {
- try {
- return WindowManagerTrace.parseFrom(readTestFile(relativePath));
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- @Test
- public void testCanTransitionInAppWindow() {
- WindowManagerTrace trace = readWmTraceFromFile("wm_trace_openchrome2.pb");
-
- assertThat(trace).showsAppWindowOnTop("com.google.android.apps.nexuslauncher/"
- + ".NexusLauncherActivity").forRange(174684850717208L, 174685957511016L);
- assertThat(trace).showsAppWindowOnTop(
- "com.google.android.apps.nexuslauncher/.NexusLauncherActivity")
- .then()
- .showsAppWindowOnTop("com.android.chrome")
- .forAllEntries();
- }
-}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/LayersTraceMonitorTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/LayersTraceMonitorTest.java
deleted file mode 100644
index dbd6761a05b0..000000000000
--- a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/LayersTraceMonitorTest.java
+++ /dev/null
@@ -1,78 +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.monitor;
-
-import static android.surfaceflinger.nano.Layerstrace.LayersTraceFileProto.MAGIC_NUMBER_H;
-import static android.surfaceflinger.nano.Layerstrace.LayersTraceFileProto.MAGIC_NUMBER_L;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.surfaceflinger.nano.Layerstrace.LayersTraceFileProto;
-
-import com.google.common.io.Files;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.File;
-
-/**
- * Contains {@link LayersTraceMonitor} tests.
- * To run this test: {@code atest FlickerLibTest:LayersTraceMonitorTest}
- */
-public class LayersTraceMonitorTest {
- private LayersTraceMonitor mLayersTraceMonitor;
-
- @Before
- public void setup() {
- mLayersTraceMonitor = new LayersTraceMonitor();
- }
-
- @After
- public void teardown() {
- mLayersTraceMonitor.stop();
- mLayersTraceMonitor.getOutputTraceFilePath("captureLayersTrace").toFile().delete();
- }
-
- @Test
- public void canStartLayersTrace() throws Exception {
- mLayersTraceMonitor.start();
- assertThat(mLayersTraceMonitor.isEnabled()).isTrue();
- }
-
- @Test
- public void canStopLayersTrace() throws Exception {
- mLayersTraceMonitor.start();
- assertThat(mLayersTraceMonitor.isEnabled()).isTrue();
- mLayersTraceMonitor.stop();
- assertThat(mLayersTraceMonitor.isEnabled()).isFalse();
- }
-
- @Test
- public void captureLayersTrace() throws Exception {
- mLayersTraceMonitor.start();
- mLayersTraceMonitor.stop();
- File testFile = mLayersTraceMonitor.save("captureLayersTrace").toFile();
- assertThat(testFile.exists()).isTrue();
- byte[] trace = Files.toByteArray(testFile);
- assertThat(trace.length).isGreaterThan(0);
- LayersTraceFileProto mLayerTraceFileProto = LayersTraceFileProto.parseFrom(trace);
- assertThat(mLayerTraceFileProto.magicNumber).isEqualTo(
- (long) MAGIC_NUMBER_H << 32 | MAGIC_NUMBER_L);
- }
-}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java
deleted file mode 100644
index e73eecc348f0..000000000000
--- a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.monitor;
-
-import static android.os.SystemClock.sleep;
-
-import static com.android.server.wm.flicker.monitor.ScreenRecorder.DEFAULT_OUTPUT_PATH;
-import static com.android.server.wm.flicker.monitor.ScreenRecorder.getPath;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.File;
-import java.io.IOException;
-
-/**
- * Contains {@link ScreenRecorder} tests.
- * To run this test: {@code atest FlickerLibTest:ScreenRecorderTest}
- */
-public class ScreenRecorderTest {
- private static final String TEST_VIDEO_FILENAME = "test.mp4";
- private ScreenRecorder mScreenRecorder;
-
- @Before
- public void setup() {
- mScreenRecorder = new ScreenRecorder();
- }
-
- @After
- public void teardown() {
- DEFAULT_OUTPUT_PATH.toFile().delete();
- getPath(TEST_VIDEO_FILENAME).toFile().delete();
- }
-
- @Test
- public void videoIsRecorded() {
- mScreenRecorder.start();
- sleep(100);
- mScreenRecorder.stop();
- File file = DEFAULT_OUTPUT_PATH.toFile();
- assertThat(file.exists()).isTrue();
- }
-
- @Test
- public void videoCanBeSaved() {
- mScreenRecorder.start();
- sleep(100);
- mScreenRecorder.stop();
- mScreenRecorder.save(TEST_VIDEO_FILENAME);
- File file = getPath(TEST_VIDEO_FILENAME).toFile();
- assertThat(file.exists()).isTrue();
- }
-}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java
deleted file mode 100644
index f31238477e95..000000000000
--- a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java
+++ /dev/null
@@ -1,50 +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.monitor;
-
-import static com.android.server.wm.flicker.helpers.AutomationUtils.wakeUpAndGoToHomeScreen;
-
-import androidx.test.InstrumentationRegistry;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-
-/**
- * Contains {@link WindowAnimationFrameStatsMonitor} tests.
- * To run this test: {@code atest FlickerLibTest:WindowAnimationFrameStatsMonitorTest}
- */
-public class WindowAnimationFrameStatsMonitorTest {
- private WindowAnimationFrameStatsMonitor mWindowAnimationFrameStatsMonitor;
-
- @Before
- public void setup() {
- mWindowAnimationFrameStatsMonitor = new WindowAnimationFrameStatsMonitor(
- InstrumentationRegistry.getInstrumentation());
- wakeUpAndGoToHomeScreen();
- }
-
- // TODO(vishnun)
- @Ignore("Disabled until app-helper libraries are available.")
- @Test
- public void captureWindowAnimationFrameStats() throws Exception {
- mWindowAnimationFrameStatsMonitor.start();
- //AppHelperWrapper.getInstance().getHelper(CHROME).open();
- //AppHelperWrapper.getInstance().getHelper(CHROME).exit();
- mWindowAnimationFrameStatsMonitor.stop();
- }
-}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitorTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitorTest.java
deleted file mode 100644
index 56284d7d516a..000000000000
--- a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitorTest.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.monitor;
-
-import static com.android.server.wm.nano.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
-import static com.android.server.wm.nano.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.android.server.wm.nano.WindowManagerTraceFileProto;
-
-import com.google.common.io.Files;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.File;
-
-/**
- * Contains {@link WindowManagerTraceMonitor} tests.
- * To run this test: {@code atest FlickerLibTest:WindowManagerTraceMonitorTest}
- */
-public class WindowManagerTraceMonitorTest {
- private WindowManagerTraceMonitor mWindowManagerTraceMonitor;
-
- @Before
- public void setup() {
- mWindowManagerTraceMonitor = new WindowManagerTraceMonitor();
- }
-
- @After
- public void teardown() {
- mWindowManagerTraceMonitor.stop();
- mWindowManagerTraceMonitor.getOutputTraceFilePath("captureWindowTrace").toFile().delete();
- }
-
- @Test
- public void canStartWindowTrace() throws Exception {
- mWindowManagerTraceMonitor.start();
- assertThat(mWindowManagerTraceMonitor.isEnabled()).isTrue();
- }
-
- @Test
- public void canStopWindowTrace() throws Exception {
- mWindowManagerTraceMonitor.start();
- assertThat(mWindowManagerTraceMonitor.isEnabled()).isTrue();
- mWindowManagerTraceMonitor.stop();
- assertThat(mWindowManagerTraceMonitor.isEnabled()).isFalse();
- }
-
- @Test
- public void captureWindowTrace() throws Exception {
- mWindowManagerTraceMonitor.start();
- mWindowManagerTraceMonitor.stop();
- File testFile = mWindowManagerTraceMonitor.save("captureWindowTrace").toFile();
- assertThat(testFile.exists()).isTrue();
- byte[] trace = Files.toByteArray(testFile);
- assertThat(trace.length).isGreaterThan(0);
- WindowManagerTraceFileProto mWindowTraceFileProto = WindowManagerTraceFileProto.parseFrom(
- trace);
- assertThat(mWindowTraceFileProto.magicNumber).isEqualTo(
- (long) MAGIC_NUMBER_H << 32 | MAGIC_NUMBER_L);
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java
index b6860cbd8d96..aa591d919cbe 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java
@@ -29,11 +29,15 @@ 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.Before;
+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;
@@ -44,18 +48,19 @@ import java.util.Collection;
* Cycle through supported app rotations.
* To run this test: {@code atest FlickerTest:ChangeAppRotationTest}
*/
-@RunWith(Parameterized.class)
@LargeTest
+@RunWith(Parameterized.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ChangeAppRotationTest extends FlickerTestBase {
- private int beginRotation;
- private int endRotation;
+ private int mBeginRotation;
+ private int mEndRotation;
public ChangeAppRotationTest(String beginRotationName, String endRotationName,
int beginRotation, int endRotation) {
- this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+ this.mTestApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
"com.android.server.wm.flicker.testapp", "SimpleApp");
- this.beginRotation = beginRotation;
- this.endRotation = endRotation;
+ this.mBeginRotation = beginRotation;
+ this.mEndRotation = endRotation;
}
@Parameters(name = "{0}-{1}")
@@ -77,15 +82,19 @@ public class ChangeAppRotationTest extends FlickerTestBase {
@Before
public void runTransition() {
super.runTransition(
- changeAppRotation(testApp, uiDevice, beginRotation, endRotation).build());
+ changeAppRotation(mTestApp, mUiDevice, mBeginRotation, mEndRotation).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)
@@ -94,8 +103,8 @@ public class ChangeAppRotationTest extends FlickerTestBase {
@Test
public void checkPosition_navBarLayerRotatesAndScales() {
- Rect startingPos = getNavigationBarPosition(beginRotation);
- Rect endingPos = getNavigationBarPosition(endRotation);
+ Rect startingPos = getNavigationBarPosition(mBeginRotation);
+ Rect endingPos = getNavigationBarPosition(mEndRotation);
checkResults(result -> {
LayersTraceSubject.assertThat(result)
.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
@@ -108,22 +117,22 @@ public class ChangeAppRotationTest extends FlickerTestBase {
@Test
public void checkPosition_appLayerRotates() {
- Rect startingPos = getAppPosition(beginRotation);
- Rect endingPos = getAppPosition(endRotation);
+ Rect startingPos = getAppPosition(mBeginRotation);
+ Rect endingPos = getAppPosition(mEndRotation);
Log.e(TAG, "startingPos=" + startingPos + " endingPos=" + endingPos);
checkResults(result -> {
LayersTraceSubject.assertThat(result)
- .hasVisibleRegion(testApp.getPackage(), startingPos).inTheBeginning();
+ .hasVisibleRegion(mTestApp.getPackage(), startingPos).inTheBeginning();
LayersTraceSubject.assertThat(result)
- .hasVisibleRegion(testApp.getPackage(), endingPos).atTheEnd();
+ .hasVisibleRegion(mTestApp.getPackage(), endingPos).atTheEnd();
}
);
}
@Test
public void checkPosition_statusBarLayerScales() {
- Rect startingPos = getStatusBarPosition(beginRotation);
- Rect endingPos = getStatusBarPosition(endRotation);
+ Rect startingPos = getStatusBarPosition(mBeginRotation);
+ Rect endingPos = getStatusBarPosition(mEndRotation);
checkResults(result -> {
LayersTraceSubject.assertThat(result)
.hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, startingPos)
@@ -134,12 +143,16 @@ public class ChangeAppRotationTest extends FlickerTestBase {
);
}
+ @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)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToAppTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToAppTest.java
new file mode 100644
index 000000000000..022f798e82f5
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToAppTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.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.Before;
+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());
+ }
+
+ @Before
+ public void runTransition() {
+ run(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
new file mode 100644
index 000000000000..d6f994b5c0d5
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeAutoOpenWindowToHomeTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.Before;
+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());
+ }
+
+ @Before
+ public void runTransition() {
+ run(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();
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java
index 6590b86f1499..28da3af2b7c5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java
@@ -17,34 +17,39 @@
package com.android.server.wm.flicker;
import static com.android.server.wm.flicker.CommonTransitions.editTextLoseFocusToApp;
-import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
-
-import android.platform.helpers.IAppHelper;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.wm.flicker.helpers.ImeAppHelper;
import org.junit.Before;
+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: {@code atest FlickerTests:CloseImeWindowToAppTest}
*/
@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class CloseImeWindowToAppTest extends FlickerTestBase {
+@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);
- private static final String IME_WINDOW_TITLE = "InputMethod";
- private IAppHelper mImeTestApp = new StandardAppHelper(
- InstrumentationRegistry.getInstrumentation(),
- "com.android.server.wm.flicker.testapp", "ImeApp");
+ mTestApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
+ }
@Before
public void runTransition() {
- super.runTransition(editTextLoseFocusToApp(uiDevice)
+ run(editTextLoseFocusToApp((ImeAppHelper) mTestApp, mUiDevice, mBeginRotation)
.includeJankyRuns().build());
}
@@ -60,20 +65,14 @@ public class CloseImeWindowToAppTest extends FlickerTestBase {
@Test
public void checkVisibility_imeAppLayerIsAlwaysVisible() {
checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer(mImeTestApp.getPackage())
+ .showsLayer(mTestApp.getPackage())
.forAllEntries());
}
@Test
public void checkVisibility_imeAppWindowIsAlwaysVisible() {
checkResults(result -> WmTraceSubject.assertThat(result)
- .showsAppWindowOnTop(mImeTestApp.getPackage())
+ .showsAppWindowOnTop(mTestApp.getPackage())
.forAllEntries());
}
-
- @Test
- public void checkCoveredRegion_noUncoveredRegions() {
- checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
- getDisplayBounds()).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
index 4771b02000c0..fc6719e2f9d9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java
@@ -17,34 +17,39 @@
package com.android.server.wm.flicker;
import static com.android.server.wm.flicker.CommonTransitions.editTextLoseFocusToHome;
-import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
-
-import android.platform.helpers.IAppHelper;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.wm.flicker.helpers.ImeAppHelper;
import org.junit.Before;
+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: {@code atest FlickerTests:CloseImeWindowToHomeTest}
*/
@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class CloseImeWindowToHomeTest extends FlickerTestBase {
+@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);
- private static final String IME_WINDOW_TITLE = "InputMethod";
- private IAppHelper mImeTestApp = new StandardAppHelper(
- InstrumentationRegistry.getInstrumentation(),
- "com.android.server.wm.flicker.testapp", "ImeApp");
+ mTestApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
+ }
@Before
public void runTransition() {
- super.runTransition(editTextLoseFocusToHome(uiDevice)
+ run(editTextLoseFocusToHome((ImeAppHelper) mTestApp, mUiDevice, mBeginRotation)
.includeJankyRuns().build());
}
@@ -69,24 +74,18 @@ public class CloseImeWindowToHomeTest extends FlickerTestBase {
@Test
public void checkVisibility_imeAppLayerBecomesInvisible() {
checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer(mImeTestApp.getPackage())
+ .showsLayer(mTestApp.getPackage())
.then()
- .hidesLayer(mImeTestApp.getPackage())
+ .hidesLayer(mTestApp.getPackage())
.forAllEntries());
}
@Test
public void checkVisibility_imeAppWindowBecomesInvisible() {
checkResults(result -> WmTraceSubject.assertThat(result)
- .showsAppWindowOnTop(mImeTestApp.getPackage())
+ .showsAppWindowOnTop(mTestApp.getPackage())
.then()
- .hidesAppWindowOnTop(mImeTestApp.getPackage())
+ .hidesAppWindowOnTop(mTestApp.getPackage())
.forAllEntries());
}
-
- @Test
- public void checkCoveredRegion_noUncoveredRegions() {
- checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
- getDisplayBounds()).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
index 5cf2c1cd6827..fd31aa531107 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
@@ -37,10 +37,10 @@ import android.support.test.uiautomator.Until;
import android.util.Rational;
import android.view.Surface;
-import androidx.test.InstrumentationRegistry;
-
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.
@@ -67,32 +67,23 @@ class CommonTransitions {
device.setOrientationNatural();
}
// Wait for animation to complete
- sleep(3000);
+ sleep(1000);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
- private static void clickEditTextWidget(UiDevice device, IAppHelper testApp) {
- UiObject2 editText = device.findObject(By.res(testApp.getPackage(), "plain_text_input"));
- editText.click();
- sleep(500);
- }
-
- private static void clickEnterPipButton(UiDevice device, IAppHelper testApp) {
- UiObject2 enterPipButton = device.findObject(By.res(testApp.getPackage(), "enter_pip"));
- enterPipButton.click();
- sleep(500);
- }
-
static TransitionBuilder openAppWarm(IAppHelper testApp, UiDevice
- device) {
+ device, int beginRotation) {
return TransitionRunner.newBuilder()
- .withTag("OpenAppWarm_" + testApp.getLauncherName())
+ .withTag("OpenAppWarm_" + testApp.getLauncherName()
+ + rotationToString(beginRotation))
.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)
@@ -127,16 +118,19 @@ class CommonTransitions {
.repeat(ITERATIONS);
}
- static TransitionBuilder getOpenAppCold(IAppHelper testApp,
- UiDevice device) {
+ static TransitionBuilder openAppCold(IAppHelper testApp,
+ UiDevice device, int beginRotation) {
return TransitionRunner.newBuilder()
- .withTag("OpenAppCold_" + testApp.getLauncherName())
+ .withTag("OpenAppCold_" + testApp.getLauncherName()
+ + rotationToString(beginRotation))
.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);
}
@@ -201,28 +195,31 @@ class CommonTransitions {
.repeat(ITERATIONS);
}
- static TransitionBuilder editTextSetFocus(UiDevice device) {
- IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
- "com.android.server.wm.flicker.testapp", "ImeApp");
+ static TransitionBuilder editTextSetFocus(ImeAppHelper testApp, UiDevice device,
+ int beginRotation) {
return TransitionRunner.newBuilder()
- .withTag("editTextSetFocus_" + testApp.getLauncherName())
+ .withTag("editTextSetFocus_" + testApp.getLauncherName()
+ + rotationToString(beginRotation))
.runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
.runBefore(device::pressHome)
+ .runBefore(() -> setRotation(device, beginRotation))
.runBefore(testApp::open)
- .run(() -> clickEditTextWidget(device, testApp))
+ .run(() -> testApp.clickEditTextWidget(device))
.runAfterAll(testApp::exit)
.repeat(ITERATIONS);
}
- static TransitionBuilder resizeSplitScreen(IAppHelper testAppTop, IAppHelper testAppBottom,
- UiDevice device, Rational startRatio, Rational stopRatio) {
- String testTag = "resizeSplitScreen_" + testAppTop.getLauncherName() + "_" +
- testAppBottom.getLauncherName() + "_" +
- startRatio.toString().replace("/", ":") + "_to_" +
- stopRatio.toString().replace("/", ":");
+ static TransitionBuilder resizeSplitScreen(IAppHelper testAppTop, ImeAppHelper testAppBottom,
+ UiDevice device, int beginRotation, Rational startRatio, Rational stopRatio) {
+ String testTag = "resizeSplitScreen_" + testAppTop.getLauncherName() + "_"
+ + testAppBottom.getLauncherName() + "_"
+ + startRatio.toString().replace("/", ":") + "_to_"
+ + stopRatio.toString().replace("/", ":") + "_"
+ + rotationToString(beginRotation);
return TransitionRunner.newBuilder()
.withTag(testTag)
.runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+ .runBeforeAll(() -> setRotation(device, beginRotation))
.runBeforeAll(() -> clearRecents(device))
.runBefore(testAppBottom::open)
.runBefore(device::pressHome)
@@ -231,9 +228,10 @@ class CommonTransitions {
.runBefore(() -> launchSplitScreen(device))
.runBefore(() -> {
UiObject2 snapshot = device.findObject(
- By.res("com.google.android.apps.nexuslauncher", "snapshot"));
+ By.res(device.getLauncherPackageName(), "snapshot"));
snapshot.click();
})
+ .runBefore(() -> testAppBottom.clickEditTextWidget(device))
.runBefore(() -> AutomationUtils.resizeSplitScreen(device, startRatio))
.run(() -> AutomationUtils.resizeSplitScreen(device, stopRatio))
.runAfter(() -> exitSplitScreen(device))
@@ -243,77 +241,73 @@ class CommonTransitions {
.repeat(ITERATIONS);
}
- static TransitionBuilder editTextLoseFocusToHome(UiDevice device) {
- IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
- "com.android.server.wm.flicker.testapp", "ImeApp");
+ static TransitionBuilder editTextLoseFocusToHome(ImeAppHelper testApp, UiDevice device,
+ int beginRotation) {
return TransitionRunner.newBuilder()
- .withTag("editTextLoseFocusToHome_" + testApp.getLauncherName())
+ .withTag("editTextLoseFocusToHome_" + testApp.getLauncherName()
+ + rotationToString(beginRotation))
.runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
.runBefore(device::pressHome)
+ .runBefore(() -> setRotation(device, beginRotation))
.runBefore(testApp::open)
- .runBefore(() -> clickEditTextWidget(device, testApp))
+ .runBefore(() -> testApp.clickEditTextWidget(device))
.run(device::pressHome)
.run(device::waitForIdle)
.runAfterAll(testApp::exit)
.repeat(ITERATIONS);
}
- static TransitionBuilder editTextLoseFocusToApp(UiDevice device) {
- IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
- "com.android.server.wm.flicker.testapp", "ImeApp");
+ static TransitionBuilder editTextLoseFocusToApp(ImeAppHelper testApp, UiDevice device,
+ int beginRotation) {
return TransitionRunner.newBuilder()
- .withTag("editTextLoseFocusToApp_" + testApp.getLauncherName())
+ .withTag("editTextLoseFocusToApp_" + testApp.getLauncherName()
+ + rotationToString(beginRotation))
.runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
.runBefore(device::pressHome)
+ .runBefore(() -> setRotation(device, beginRotation))
.runBefore(testApp::open)
- .runBefore(() -> clickEditTextWidget(device, testApp))
+ .runBefore(() -> testApp.clickEditTextWidget(device))
.run(device::pressBack)
.run(device::waitForIdle)
.runAfterAll(testApp::exit)
.repeat(ITERATIONS);
}
- static TransitionBuilder enterPipMode(UiDevice device) {
- IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
- "com.android.server.wm.flicker.testapp", "PipApp");
+ static TransitionBuilder enterPipMode(PipAppHelper testApp, UiDevice device) {
return TransitionRunner.newBuilder()
.withTag("enterPipMode_" + testApp.getLauncherName())
.runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
.runBefore(device::pressHome)
.runBefore(testApp::open)
- .run(() -> clickEnterPipButton(device, testApp))
+ .run(() -> testApp.clickEnterPipButton(device))
.runAfter(() -> closePipWindow(device))
.runAfterAll(testApp::exit)
.repeat(ITERATIONS);
}
- static TransitionBuilder exitPipModeToHome(UiDevice device) {
- IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
- "com.android.server.wm.flicker.testapp", "PipApp");
+ static TransitionBuilder exitPipModeToHome(PipAppHelper testApp, UiDevice device) {
return TransitionRunner.newBuilder()
.withTag("exitPipModeToHome_" + testApp.getLauncherName())
.runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
.runBefore(device::pressHome)
.runBefore(testApp::open)
- .runBefore(() -> clickEnterPipButton(device, testApp))
+ .runBefore(() -> testApp.clickEnterPipButton(device))
.run(() -> closePipWindow(device))
.run(device::waitForIdle)
.runAfterAll(testApp::exit)
.repeat(ITERATIONS);
}
- static TransitionBuilder exitPipModeToApp(UiDevice device) {
- IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
- "com.android.server.wm.flicker.testapp", "PipApp");
+ static TransitionBuilder exitPipModeToApp(PipAppHelper testApp, UiDevice device) {
return TransitionRunner.newBuilder()
.withTag("exitPipModeToApp_" + testApp.getLauncherName())
.runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
.runBefore(device::pressHome)
.runBefore(testApp::open)
- .runBefore(() -> clickEnterPipButton(device, testApp))
+ .runBefore(() -> testApp.clickEnterPipButton(device))
.run(() -> expandPipWindow(device))
.run(device::waitForIdle)
.runAfterAll(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
index 61cca0d6b53f..8f0177c7afc5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java
@@ -22,40 +22,50 @@ import android.util.Rational;
import android.view.Surface;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
+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 FlickerTest:DebugTests#openAppCold
+ * atest FlickerTests:DebugTest#openAppCold
*/
@Test
public void openAppCold() {
- CommonTransitions.getOpenAppCold(testApp, uiDevice).recordAllRuns().build().run();
+ CommonTransitions.openAppCold(testApp, uiDevice, Surface.ROTATION_0)
+ .recordAllRuns().build().run();
}
/**
- * atest FlickerTest:DebugTests#openAppWarm
+ * atest FlickerTests:DebugTest#openAppWarm
*/
@Test
public void openAppWarm() {
- CommonTransitions.openAppWarm(testApp, uiDevice).recordAllRuns().build().run();
+ CommonTransitions.openAppWarm(testApp, uiDevice, Surface.ROTATION_0)
+ .recordAllRuns().build().run();
}
/**
- * atest FlickerTest:DebugTests#changeOrientationFromNaturalToLeft
+ * atest FlickerTests:DebugTest#changeOrientationFromNaturalToLeft
*/
@Test
public void changeOrientationFromNaturalToLeft() {
@@ -64,7 +74,7 @@ public class DebugTest {
}
/**
- * atest FlickerTest:DebugTests#closeAppWithBackKey
+ * atest FlickerTests:DebugTest#closeAppWithBackKey
*/
@Test
public void closeAppWithBackKey() {
@@ -72,7 +82,7 @@ public class DebugTest {
}
/**
- * atest FlickerTest:DebugTests#closeAppWithHomeKey
+ * atest FlickerTests:DebugTest#closeAppWithHomeKey
*/
@Test
public void closeAppWithHomeKey() {
@@ -80,7 +90,7 @@ public class DebugTest {
}
/**
- * atest FlickerTest:DebugTests#openAppToSplitScreen
+ * atest FlickerTests:DebugTest#openAppToSplitScreen
*/
@Test
public void openAppToSplitScreen() {
@@ -89,7 +99,7 @@ public class DebugTest {
}
/**
- * atest FlickerTest:DebugTests#splitScreenToLauncher
+ * atest FlickerTests:DebugTest#splitScreenToLauncher
*/
@Test
public void splitScreenToLauncher() {
@@ -99,70 +109,80 @@ public class DebugTest {
}
/**
- * atest FlickerTest:DebugTests#resizeSplitScreen
+ * atest FlickerTests:DebugTest#resizeSplitScreen
*/
@Test
public void resizeSplitScreen() {
- IAppHelper bottomApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
- "com.android.server.wm.flicker.testapp", "ImeApp");
- CommonTransitions.resizeSplitScreen(testApp, bottomApp, uiDevice, new Rational(1, 3),
- new Rational(2, 3)).includeJankyRuns().recordEachRun().build().run();
+ ImeAppHelper bottomApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
+ CommonTransitions.resizeSplitScreen(testApp, bottomApp, uiDevice, Surface.ROTATION_0,
+ new Rational(1, 3), new Rational(2, 3))
+ .includeJankyRuns().recordEachRun().build().run();
}
// IME tests
/**
- * atest FlickerTest:DebugTests#editTextSetFocus
+ * atest FlickerTests:DebugTest#editTextSetFocus
*/
@Test
public void editTextSetFocus() {
- CommonTransitions.editTextSetFocus(uiDevice).includeJankyRuns().recordEachRun()
+ ImeAppHelper testApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
+ CommonTransitions.editTextSetFocus(testApp, uiDevice, Surface.ROTATION_0)
+ .includeJankyRuns().recordEachRun()
.build().run();
}
/**
- * atest FlickerTest:DebugTests#editTextLoseFocusToHome
+ * atest FlickerTests:DebugTest#editTextLoseFocusToHome
*/
@Test
public void editTextLoseFocusToHome() {
- CommonTransitions.editTextLoseFocusToHome(uiDevice).includeJankyRuns().recordEachRun()
+ ImeAppHelper testApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
+ CommonTransitions.editTextLoseFocusToHome(testApp, uiDevice, Surface.ROTATION_0)
+ .includeJankyRuns().recordEachRun()
.build().run();
}
/**
- * atest FlickerTest:DebugTests#editTextLoseFocusToApp
+ * atest FlickerTests:DebugTest#editTextLoseFocusToApp
*/
@Test
public void editTextLoseFocusToApp() {
- CommonTransitions.editTextLoseFocusToHome(uiDevice).includeJankyRuns().recordEachRun()
+ ImeAppHelper testApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
+ CommonTransitions.editTextLoseFocusToHome(testApp, uiDevice, Surface.ROTATION_0)
+ .includeJankyRuns().recordEachRun()
.build().run();
}
// PIP tests
/**
- * atest FlickerTest:DebugTests#enterPipMode
+ * atest FlickerTests:DebugTest#enterPipMode
*/
@Test
public void enterPipMode() {
- CommonTransitions.enterPipMode(uiDevice).includeJankyRuns().recordEachRun().build().run();
+ PipAppHelper testApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation());
+ CommonTransitions.enterPipMode(testApp, uiDevice).includeJankyRuns().recordEachRun()
+ .build().run();
}
/**
- * atest FlickerTest:DebugTests#exitPipModeToHome
+ * atest FlickerTests:DebugTest#exitPipModeToHome
*/
@Test
public void exitPipModeToHome() {
- CommonTransitions.exitPipModeToHome(uiDevice).includeJankyRuns().recordEachRun()
+ PipAppHelper testApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation());
+ CommonTransitions.exitPipModeToHome(testApp, uiDevice).includeJankyRuns().recordEachRun()
.build().run();
}
/**
- * atest FlickerTest:DebugTests#exitPipModeToApp
+ * atest FlickerTests:DebugTest#exitPipModeToApp
*/
@Test
public void exitPipModeToApp() {
- CommonTransitions.exitPipModeToApp(uiDevice).includeJankyRuns().recordEachRun()
+ PipAppHelper testApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation());
+ CommonTransitions.exitPipModeToApp(testApp, uiDevice).includeJankyRuns().recordEachRun()
.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
index 8c9d6b4dc7a0..883d59ea8a92 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
@@ -16,20 +16,23 @@
package com.android.server.wm.flicker;
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
import static com.android.server.wm.flicker.helpers.AutomationUtils.setDefaultWait;
import static com.google.common.truth.Truth.assertWithMessage;
+import android.os.Bundle;
import android.platform.helpers.IAppHelper;
+import android.support.test.InstrumentationRegistry;
import android.support.test.uiautomator.UiDevice;
import android.util.Log;
-import androidx.test.InstrumentationRegistry;
-
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;
@@ -51,10 +54,16 @@ public class FlickerTestBase {
static final String DOCKED_STACK_DIVIDER = "DockedStackDivider";
private static HashMap<String, List<TransitionResult>> transitionResults =
new HashMap<>();
- IAppHelper testApp;
- UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- private List<TransitionResult> results;
- private TransitionResult lastResult = null;
+ IAppHelper mTestApp;
+ UiDevice mUiDevice;
+ private List<TransitionResult> mResults;
+ private TransitionResult mLastResult = null;
+
+ @Before
+ public void setUp() {
+ InstrumentationRegistry.registerInstance(getInstrumentation(), new Bundle());
+ mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ }
/**
* Teardown any system settings and clean up test artifacts from the file system.
@@ -89,16 +98,23 @@ public class FlickerTestBase {
/**
* Runs a transition, returns a cached result if the transition has run before.
*/
- void runTransition(TransitionRunner transition) {
+ void run(TransitionRunner transition) {
if (transitionResults.containsKey(transition.getTestTag())) {
- results = transitionResults.get(transition.getTestTag());
+ mResults = transitionResults.get(transition.getTestTag());
return;
}
- results = transition.run().getResults();
+ 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(results).isNotEmpty();
- transitionResults.put(transition.getTestTag(), results);
+ + "of Jank").that(mResults).isNotEmpty();
+ transitionResults.put(transition.getTestTag(), mResults);
+ }
+
+ /**
+ * Runs a transition, returns a cached result if the transition has run before.
+ */
+ void runTransition(TransitionRunner transition) {
+ run(transition);
}
/**
@@ -106,11 +122,11 @@ public class FlickerTestBase {
*/
void checkResults(Consumer<TransitionResult> assertion) {
- for (TransitionResult result : results) {
- lastResult = result;
+ for (TransitionResult result : mResults) {
+ mLastResult = result;
assertion.accept(result);
}
- lastResult = null;
+ mLastResult = null;
}
/**
@@ -119,8 +135,8 @@ public class FlickerTestBase {
*/
@After
public void markArtifactsForSaving() {
- if (lastResult != null) {
- lastResult.flagForSaving();
+ if (mLastResult != null) {
+ mLastResult.flagForSaving();
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.java b/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.java
new file mode 100644
index 000000000000..54941dc0f585
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.java
@@ -0,0 +1,80 @@
+/*
+ * 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/OpenAppColdTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java
index 7818c4e4ba50..efdfaee60e64 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java
@@ -16,91 +16,70 @@
package com.android.server.wm.flicker;
-import static com.android.server.wm.flicker.CommonTransitions.getOpenAppCold;
-import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+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 androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
+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(AndroidJUnit4.class)
-public class OpenAppColdTest extends FlickerTestBase {
+@RunWith(Parameterized.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class OpenAppColdTest extends NonRotationTestBase {
- public OpenAppColdTest() {
- this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+ public OpenAppColdTest(String beginRotationName, int beginRotation) {
+ super(beginRotationName, beginRotation);
+
+ this.mTestApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
"com.android.server.wm.flicker.testapp", "SimpleApp");
}
@Before
public void runTransition() {
- super.runTransition(getOpenAppCold(testApp, uiDevice).build());
- }
-
- @Test
- public void checkVisibility_navBarWindowIsAlwaysVisible() {
- checkResults(result -> assertThat(result)
- .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
- }
-
- @Test
- public void checkVisibility_statusBarWindowIsAlwaysVisible() {
- checkResults(result -> assertThat(result)
- .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+ run(openAppCold(mTestApp, mUiDevice, mBeginRotation)
+ .includeJankyRuns().build());
}
@Test
public void checkVisibility_wallpaperWindowBecomesInvisible() {
checkResults(result -> assertThat(result)
- .showsBelowAppWindow("wallpaper")
+ .showsBelowAppWindow("Wallpaper")
.then()
- .hidesBelowAppWindow("wallpaper")
+ .hidesBelowAppWindow("Wallpaper")
.forAllEntries());
}
+ @FlakyTest(bugId = 140855415)
+ @Ignore("Waiting bug feedback")
@Test
public void checkZOrder_appWindowReplacesLauncherAsTopWindow() {
checkResults(result -> assertThat(result)
.showsAppWindowOnTop(
- "com.google.android.apps.nexuslauncher/.NexusLauncherActivity")
+ "com.android.launcher3/.Launcher")
.then()
- .showsAppWindowOnTop(testApp.getPackage())
+ .showsAppWindowOnTop(mTestApp.getPackage())
.forAllEntries());
}
@Test
- public void checkCoveredRegion_noUncoveredRegions() {
- checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
- getDisplayBounds()).forAllEntries());
- }
-
- @Test
- public void checkVisibility_navBarLayerIsAlwaysVisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
- }
-
- @Test
- public void checkVisibility_statusBarLayerIsAlwaysVisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries());
- }
-
- @Test
public void checkVisibility_wallpaperLayerBecomesInvisible() {
checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer("wallpaper")
+ .showsLayer("Wallpaper")
.then()
- .hidesLayer("wallpaper")
+ .hidesLayer("Wallpaper")
.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
index 63018ec1d9e7..f8b7938901a8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
@@ -24,8 +24,10 @@ import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
+import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
/**
* Test open app to split screen.
@@ -33,16 +35,17 @@ import org.junit.runner.RunWith;
*/
@LargeTest
@RunWith(AndroidJUnit4.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class OpenAppToSplitScreenTest extends FlickerTestBase {
public OpenAppToSplitScreenTest() {
- this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+ this.mTestApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
"com.android.server.wm.flicker.testapp", "SimpleApp");
}
@Before
public void runTransition() {
- super.runTransition(appToSplitScreen(testApp, uiDevice).includeJankyRuns().build());
+ super.runTransition(appToSplitScreen(mTestApp, mUiDevice).includeJankyRuns().build());
}
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java
index 1aba93056c89..7ce6315f529a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java
@@ -17,90 +17,69 @@
package com.android.server.wm.flicker;
import static com.android.server.wm.flicker.CommonTransitions.openAppWarm;
-import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
+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(AndroidJUnit4.class)
-public class OpenAppWarmTest extends FlickerTestBase {
+@RunWith(Parameterized.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class OpenAppWarmTest extends NonRotationTestBase {
- public OpenAppWarmTest() {
- this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+ public OpenAppWarmTest(String beginRotationName, int beginRotation) {
+ super(beginRotationName, beginRotation);
+
+ this.mTestApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
"com.android.server.wm.flicker.testapp", "SimpleApp");
}
@Before
public void runTransition() {
- super.runTransition(openAppWarm(testApp, uiDevice).build());
- }
-
- @Test
- public void checkVisibility_navBarIsAlwaysVisible() {
- checkResults(result -> assertThat(result)
- .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
- }
-
- @Test
- public void checkVisibility_statusBarIsAlwaysVisible() {
- checkResults(result -> assertThat(result)
- .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+ super.runTransition(openAppWarm(mTestApp, mUiDevice, mBeginRotation)
+ .includeJankyRuns().build());
}
@Test
public void checkVisibility_wallpaperBecomesInvisible() {
checkResults(result -> assertThat(result)
- .showsBelowAppWindow("wallpaper")
+ .showsBelowAppWindow("Wallpaper")
.then()
- .hidesBelowAppWindow("wallpaper")
+ .hidesBelowAppWindow("Wallpaper")
.forAllEntries());
}
+ @FlakyTest(bugId = 140855415)
+ @Ignore("Waiting bug feedback")
@Test
public void checkZOrder_appWindowReplacesLauncherAsTopWindow() {
checkResults(result -> assertThat(result)
.showsAppWindowOnTop(
- "com.google.android.apps.nexuslauncher/.NexusLauncherActivity")
+ "com.android.launcher3/.Launcher")
.then()
- .showsAppWindowOnTop(testApp.getPackage())
+ .showsAppWindowOnTop(mTestApp.getPackage())
.forAllEntries());
}
@Test
- public void checkCoveredRegion_noUncoveredRegions() {
- checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
- getDisplayBounds()).forAllEntries());
- }
-
- @Test
- public void checkVisibility_navBarLayerIsAlwaysVisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
- }
-
- @Test
- public void checkVisibility_statusBarLayerIsAlwaysVisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries());
- }
-
- @Test
public void checkVisibility_wallpaperLayerBecomesInvisible() {
checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer("wallpaper")
+ .showsLayer("Wallpaper")
.then()
- .hidesLayer("wallpaper")
+ .hidesLayer("Wallpaper")
.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
index a81fa8e6d123..91d4a056d8fb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java
@@ -17,28 +17,39 @@
package com.android.server.wm.flicker;
import static com.android.server.wm.flicker.CommonTransitions.editTextSetFocus;
-import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.wm.flicker.helpers.ImeAppHelper;
import org.junit.Before;
+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(AndroidJUnit4.class)
-public class OpenImeWindowTest extends FlickerTestBase {
+@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());
+ }
+
@Before
public void runTransition() {
- super.runTransition(editTextSetFocus(uiDevice)
+ run(editTextSetFocus((ImeAppHelper) mTestApp, mUiDevice, mBeginRotation)
.includeJankyRuns().build());
}
@@ -59,10 +70,4 @@ public class OpenImeWindowTest extends FlickerTestBase {
.showsLayer(IME_WINDOW_TITLE)
.forAllEntries());
}
-
- @Test
- public void checkCoveredRegion_noUncoveredRegions() {
- checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
- getDisplayBounds()).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
index 50dba81e53b7..29b624005495 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java
@@ -24,64 +24,62 @@ import static com.android.server.wm.flicker.WindowUtils.getNavigationBarHeight;
import static com.google.common.truth.Truth.assertThat;
import android.graphics.Rect;
-import android.platform.helpers.IAppHelper;
import android.util.Rational;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.wm.flicker.helpers.ImeAppHelper;
import org.junit.Before;
+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 split screen resizing window transitions.
* To run this test: {@code atest FlickerTests:ResizeSplitScreenTest}
*/
@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class ResizeSplitScreenTest extends FlickerTestBase {
+@RunWith(Parameterized.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@FlakyTest(bugId = 140854698)
+@Ignore("Waiting bug feedback")
+public class ResizeSplitScreenTest extends NonRotationTestBase {
+
+ private static String sSimpleActivity = "SimpleActivity";
+ private static String sImeActivity = "ImeActivity";
- public ResizeSplitScreenTest() {
- this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+ public ResizeSplitScreenTest(String beginRotationName, int beginRotation) {
+ super(beginRotationName, beginRotation);
+
+ this.mTestApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
"com.android.server.wm.flicker.testapp", "SimpleApp");
}
@Before
public void runTransition() {
- IAppHelper bottomApp = new StandardAppHelper(InstrumentationRegistry
- .getInstrumentation(),
- "com.android.server.wm.flicker.testapp", "ImeApp");
- super.runTransition(resizeSplitScreen(testApp, bottomApp, uiDevice, new Rational(1, 3),
- new Rational(2, 3)).includeJankyRuns().build());
- }
-
- @Test
- public void checkVisibility_navBarLayerIsAlwaysVisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer(NAVIGATION_BAR_WINDOW_TITLE)
- .forAllEntries());
- }
-
- @Test
- public void checkVisibility_statusBarLayerIsAlwaysVisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer(STATUS_BAR_WINDOW_TITLE)
- .forAllEntries());
+ ImeAppHelper bottomApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
+ run(resizeSplitScreen(mTestApp, bottomApp, mUiDevice, mBeginRotation,
+ new Rational(1, 3), new Rational(2, 3))
+ .includeJankyRuns().build());
}
@Test
public void checkVisibility_topAppLayerIsAlwaysVisible() {
checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer("SimpleActivity")
+ .showsLayer(sSimpleActivity)
.forAllEntries());
}
@Test
public void checkVisibility_bottomAppLayerIsAlwaysVisible() {
checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer("ImeActivity")
+ .showsLayer(sImeActivity)
.forAllEntries());
}
@@ -142,11 +140,11 @@ public class ResizeSplitScreenTest extends FlickerTestBase {
displayBounds.bottom - getNavigationBarHeight());
LayersTraceSubject.assertThat(result)
- .hasVisibleRegion("SimpleActivity", startingTopAppBounds)
+ .hasVisibleRegion(sSimpleActivity, startingTopAppBounds)
.atTheEnd();
LayersTraceSubject.assertThat(result)
- .hasVisibleRegion("ImeActivity", startingBottomAppBounds)
+ .hasVisibleRegion(sImeActivity, startingBottomAppBounds)
.atTheEnd();
});
}
@@ -168,14 +166,14 @@ public class ResizeSplitScreenTest extends FlickerTestBase {
@Test
public void checkVisibility_topAppWindowIsAlwaysVisible() {
checkResults(result -> WmTraceSubject.assertThat(result)
- .showsAppWindow("SimpleActivity")
+ .showsAppWindow(sSimpleActivity)
.forAllEntries());
}
@Test
public void checkVisibility_bottomAppWindowIsAlwaysVisible() {
checkResults(result -> WmTraceSubject.assertThat(result)
- .showsAppWindow("ImeActivity")
+ .showsAppWindow(sImeActivity)
.forAllEntries());
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java
index 117ac5a8fadf..ae55a75d7e67 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java
@@ -33,8 +33,10 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import org.junit.Before;
+import org.junit.FixMethodOrder;
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;
@@ -47,6 +49,7 @@ import java.util.Collection;
*/
@LargeTest
@RunWith(Parameterized.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SeamlessAppRotationTest extends FlickerTestBase {
private int mBeginRotation;
private int mEndRotation;
@@ -105,7 +108,7 @@ public class SeamlessAppRotationTest extends FlickerTestBase {
super.runTransition(
changeAppRotation(mIntent, intentId, InstrumentationRegistry.getContext(),
- uiDevice, mBeginRotation, mEndRotation).repeat(5).build());
+ mUiDevice, mBeginRotation, mEndRotation).repeat(5).build());
}
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java
index 1d30df9750b2..85a14941a7fd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java
@@ -25,8 +25,11 @@ import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
+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.
@@ -34,16 +37,19 @@ import org.junit.runner.RunWith;
*/
@LargeTest
@RunWith(AndroidJUnit4.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@FlakyTest(bugId = 140856143)
+@Ignore("Waiting bug feedback")
public class SplitScreenToLauncherTest extends FlickerTestBase {
public SplitScreenToLauncherTest() {
- this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+ this.mTestApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
"com.android.server.wm.flicker.testapp", "SimpleApp");
}
@Before
public void runTransition() {
- super.runTransition(splitScreenToLauncher(testApp, uiDevice).includeJankyRuns().build());
+ super.runTransition(splitScreenToLauncher(mTestApp, mUiDevice).includeJankyRuns().build());
}
@Test
@@ -62,13 +68,12 @@ public class SplitScreenToLauncherTest extends FlickerTestBase {
.forAllEntries());
}
- @FlakyTest(bugId = 79686616)
@Test
public void checkVisibility_appLayerBecomesInVisible() {
checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer(testApp.getPackage())
+ .showsLayer(mTestApp.getPackage())
.then()
- .hidesLayer(testApp.getPackage())
+ .hidesLayer(mTestApp.getPackage())
.forAllEntries());
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/StandardAppHelper.java b/tests/FlickerTests/src/com/android/server/wm/flicker/StandardAppHelper.java
deleted file mode 100644
index 79a0220e0e87..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/StandardAppHelper.java
+++ /dev/null
@@ -1,59 +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.AbstractStandardAppHelper;
-
-/**
- * Class to take advantage of {@code IAppHelper} interface so the same test can be run against
- * first party and third party apps.
- */
-public class StandardAppHelper extends AbstractStandardAppHelper {
- private final String mPackageName;
- private final String mLauncherName;
-
- public StandardAppHelper(Instrumentation instr, String packageName, String launcherName) {
- super(instr);
- mPackageName = packageName;
- mLauncherName = launcherName;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String getPackage() {
- return mPackageName;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String getLauncherName() {
- return mLauncherName;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void dismissInitialDialogs() {
-
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.java b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.java
new file mode 100644
index 000000000000..42977f549162
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerAppHelper.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.helpers;
+
+import android.app.Instrumentation;
+
+import com.android.server.wm.flicker.StandardAppHelper;
+
+public abstract class FlickerAppHelper extends StandardAppHelper {
+
+ static int sFindTimeout = 10000;
+ static String sFlickerPackage = "com.android.server.wm.flicker.testapp";
+
+ public FlickerAppHelper(Instrumentation instr, String launcherName) {
+ super(instr, sFlickerPackage, launcherName);
+ }
+}
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.java
new file mode 100644
index 000000000000..56e1118590ea
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.helpers;
+
+import android.app.Instrumentation;
+import android.support.test.uiautomator.UiDevice;
+
+public class ImeAppAutoFocusHelper extends ImeAppHelper {
+
+ public ImeAppAutoFocusHelper(Instrumentation instr) {
+ super(instr, "ImeAppAutoFocus");
+ }
+
+ public void clickEditTextWidget(UiDevice device) {
+ // 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
new file mode 100644
index 000000000000..098fd6d4250b
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.java
@@ -0,0 +1,41 @@
+/*
+ * 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 android.os.SystemClock.sleep;
+
+import android.app.Instrumentation;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+
+public class ImeAppHelper extends FlickerAppHelper {
+
+ ImeAppHelper(Instrumentation instr, String launcherName) {
+ super(instr, launcherName);
+ }
+
+ public ImeAppHelper(Instrumentation instr) {
+ this(instr, "ImeApp");
+ }
+
+ public void clickEditTextWidget(UiDevice device) {
+ UiObject2 editText = device.findObject(By.res(getPackage(), "plain_text_input"));
+ editText.click();
+ sleep(500);
+ }
+}
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
new file mode 100644
index 000000000000..d00e11b2994d
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.java
@@ -0,0 +1,43 @@
+/*
+ * 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.getPipWindowSelector;
+
+import android.app.Instrumentation;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+
+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"));
+ enterPipButton.click();
+ UiObject2 pipWindow = device.wait(Until.findObject(getPipWindowSelector()), sFindTimeout);
+
+ if (pipWindow == null) {
+ throw new RuntimeException("Unable to find PIP window");
+ }
+ }
+
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index b694172d60ca..0fe968273567 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -38,6 +38,15 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+ <activity android:name=".ImeActivityAutoFocus"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivityAutoFocus"
+ android:windowSoftInputMode="stateVisible"
+ android:label="ImeAppAutoFocus">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
<activity android:name=".PipActivity"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
index d5eb02330441..4708cfd48381 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
@@ -18,6 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:focusableInTouchMode="true"
android:background="@android:color/holo_green_light">
<EditText android:id="@+id/plain_text_input"
android:layout_height="wrap_content"
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java
new file mode 100644
index 000000000000..05da717620aa
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java
@@ -0,0 +1,30 @@
+/*
+ * 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.testapp;
+
+import android.widget.EditText;
+
+public class ImeActivityAutoFocus extends ImeActivity {
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+
+ EditText editTextField = findViewById(R.id.plain_text_input);
+ editTextField.requestFocus();
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java
index 3a0c1c9382fe..5cf81cb90fbc 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java
@@ -17,7 +17,6 @@
package com.android.server.wm.flicker.testapp;
import static android.os.SystemClock.sleep;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
import static com.android.server.wm.flicker.testapp.ActivityOptions.EXTRA_STARVE_UI_THREAD;
@@ -39,8 +38,8 @@ public class SeamlessRotationActivity extends Activity {
super.onCreate(savedInstanceState);
enableSeamlessRotation();
setContentView(R.layout.activity_simple);
- boolean starveUiThread = getIntent().getExtras() != null &&
- getIntent().getExtras().getBoolean(EXTRA_STARVE_UI_THREAD);
+ boolean starveUiThread = getIntent().getExtras() != null
+ && getIntent().getExtras().getBoolean(EXTRA_STARVE_UI_THREAD);
if (starveUiThread) {
starveUiThread();
}
diff --git a/tests/MirrorSurfaceTest/Android.bp b/tests/MirrorSurfaceTest/Android.bp
new file mode 100644
index 000000000000..e359c64cc982
--- /dev/null
+++ b/tests/MirrorSurfaceTest/Android.bp
@@ -0,0 +1,6 @@
+android_test {
+ name: "MirrorSurfaceTest",
+ srcs: ["src/**/*.java"],
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/tests/MirrorSurfaceTest/AndroidManifest.xml b/tests/MirrorSurfaceTest/AndroidManifest.xml
new file mode 100644
index 000000000000..123cd0f26ff3
--- /dev/null
+++ b/tests/MirrorSurfaceTest/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.test.mirrorsurface">
+ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER"/>
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+
+ <application android:label="MirrorSurfaceTest">
+ <activity android:name=".MirrorSurfaceActivity"
+ android:label="Mirror Surface"
+ android:configChanges="orientation|screenSize">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/MirrorSurfaceTest/res/layout/activity_mirror_surface.xml b/tests/MirrorSurfaceTest/res/layout/activity_mirror_surface.xml
new file mode 100644
index 000000000000..73b509f743d1
--- /dev/null
+++ b/tests/MirrorSurfaceTest/res/layout/activity_mirror_surface.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="20dp"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/mirror_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="20dp"
+ android:text="Mirror"
+ android:textSize="20dp" />
+
+ <Button
+ android:id="@+id/remove_mirror_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="20dp"
+ android:text="Remove Mirror"
+ android:textSize="20dp" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="40dp"
+ android:layout_marginRight="40dp"
+ android:layout_marginTop="10dp"
+ android:orientation="horizontal">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="20dp"
+ android:text="SCALE: " />
+
+ <EditText
+ android:hint="0.5"
+ android:id="@+id/scale"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="numberDecimal" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="40dp"
+ android:layout_marginRight="40dp"
+ android:layout_marginTop="10dp"
+ android:orientation="horizontal">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="20dp"
+ android:text="DISPLAY FRAME: " />
+
+ <EditText
+ android:hint="0, 0, 20, 20"
+ android:id="@+id/displayFrame"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="numberDecimal|text"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="40dp"
+ android:layout_marginRight="40dp"
+ android:layout_marginTop="10dp"
+ android:orientation="horizontal">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="20dp"
+ android:text="SOURCE POSITION: " />
+
+ <TextView
+ android:id="@+id/sourcePosition"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="numberDecimal|text"/>
+ </LinearLayout>
+</LinearLayout>
diff --git a/tests/MirrorSurfaceTest/res/layout/move_view.xml b/tests/MirrorSurfaceTest/res/layout/move_view.xml
new file mode 100644
index 000000000000..57077006765e
--- /dev/null
+++ b/tests/MirrorSurfaceTest/res/layout/move_view.xml
@@ -0,0 +1,101 @@
+<?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.
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="20dp"
+ android:gravity="center">
+
+ <RelativeLayout
+ android:id="@+id/arrows"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <ImageButton
+ android:id="@+id/up_arrow"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toEndOf="@+id/right_arrow"
+ android:background="@android:color/holo_green_light"
+ android:padding="10dp"
+ android:src="@android:drawable/arrow_up_float" />
+
+ <ImageButton
+ android:id="@+id/down_arrow"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/up_arrow"
+ android:layout_marginTop="80dp"
+ android:layout_toEndOf="@+id/right_arrow"
+ android:background="@android:color/holo_green_light"
+ android:padding="10dp"
+ android:src="@android:drawable/arrow_down_float" />
+
+ <ImageButton
+ android:id="@+id/right_arrow"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/up_arrow"
+ android:layout_alignBottom="@+id/down_arrow"
+ android:layout_marginTop="55dp"
+ android:layout_marginEnd="15dp"
+ android:layout_marginBottom="55dp"
+ android:background="@android:color/holo_green_light"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:rotation="90"
+ android:src="@android:drawable/arrow_down_float" />
+
+ <ImageButton
+ android:id="@+id/left_arrow"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/up_arrow"
+ android:layout_alignBottom="@+id/down_arrow"
+ android:layout_marginStart="15dp"
+ android:layout_marginTop="55dp"
+ android:layout_marginBottom="55dp"
+ android:layout_toEndOf="@+id/down_arrow"
+ android:background="@android:color/holo_green_light"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:rotation="-90"
+ android:src="@android:drawable/arrow_down_float" />
+ </RelativeLayout>
+
+ <RelativeLayout
+
+ android:layout_gravity="center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <Button
+ android:id="@+id/zoom_in_button"
+ android:layout_width="40dp"
+ android:layout_marginBottom="-8dp"
+ android:layout_height="40dp"
+ android:text="+" />
+
+ <Button
+ android:layout_below="@+id/zoom_in_button"
+ android:id="@+id/zoom_out_button"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:text="-" />
+ </RelativeLayout>
+</FrameLayout> \ No newline at end of file
diff --git a/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java b/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java
new file mode 100644
index 000000000000..b863713df15b
--- /dev/null
+++ b/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java
@@ -0,0 +1,441 @@
+/*
+ * 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.google.android.test.mirrorsurface;
+
+
+import android.app.Activity;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.IWindowManager;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class MirrorSurfaceActivity extends Activity implements View.OnClickListener,
+ View.OnLongClickListener, View.OnTouchListener {
+ private static final int BORDER_SIZE = 10;
+ private static final int DEFAULT_SCALE = 2;
+ private static final int DEFAULT_BORDER_COLOR = Color.argb(255, 255, 153, 0);
+ private static final int MOVE_FRAME_AMOUNT = 20;
+
+ private IWindowManager mIWm;
+ private WindowManager mWm;
+
+ private SurfaceControl mSurfaceControl = new SurfaceControl();
+ private SurfaceControl mBorderSc;
+
+ private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+ private View mOverlayView;
+ private View mArrowOverlay;
+
+ private Rect mDisplayBounds = new Rect();
+
+ private EditText mScaleText;
+ private EditText mDisplayFrameText;
+ private TextView mSourcePositionText;
+
+ private Rect mTmpRect = new Rect();
+ private final Surface mTmpSurface = new Surface();
+
+ private boolean mHasMirror;
+
+ private Rect mCurrFrame = new Rect();
+ private float mCurrScale = DEFAULT_SCALE;
+
+ private final Handler mHandler = new Handler();
+
+ private MoveMirrorRunnable mMoveMirrorRunnable = new MoveMirrorRunnable();
+ private boolean mIsPressedDown = false;
+
+ private int mDisplayId;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_mirror_surface);
+ mWm = (WindowManager) getSystemService(WINDOW_SERVICE);
+ mIWm = WindowManagerGlobal.getWindowManagerService();
+
+ DisplayMetrics displayMetrics = new DisplayMetrics();
+ getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
+ mDisplayBounds.set(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels);
+
+ mScaleText = findViewById(R.id.scale);
+ mDisplayFrameText = findViewById(R.id.displayFrame);
+ mSourcePositionText = findViewById(R.id.sourcePosition);
+
+ mCurrFrame.set(0, 0, mDisplayBounds.width() / 2, mDisplayBounds.height() / 2);
+ mCurrScale = DEFAULT_SCALE;
+
+ mDisplayId = getWindowManager().getDefaultDisplay().getDisplayId();
+ updateEditTexts();
+
+ findViewById(R.id.mirror_button).setOnClickListener(view -> {
+ if (mArrowOverlay == null) {
+ createArrowOverlay();
+ }
+ createOrUpdateMirror();
+ });
+
+ findViewById(R.id.remove_mirror_button).setOnClickListener(v -> {
+ removeMirror();
+ removeArrowOverlay();
+ });
+
+ createMirrorOverlay();
+ }
+
+ private void updateEditTexts() {
+ mDisplayFrameText.setText(
+ String.format("%s, %s, %s, %s", mCurrFrame.left, mCurrFrame.top, mCurrFrame.right,
+ mCurrFrame.bottom));
+ mScaleText.setText(String.valueOf(mCurrScale));
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mOverlayView != null) {
+ removeMirror();
+ mWm.removeView(mOverlayView);
+ mOverlayView = null;
+ }
+ removeArrowOverlay();
+ }
+
+ private void createArrowOverlay() {
+ mArrowOverlay = getLayoutInflater().inflate(R.layout.move_view, null);
+ WindowManager.LayoutParams arrowParams = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
+ WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.RGBA_8888);
+ arrowParams.gravity = Gravity.RIGHT | Gravity.BOTTOM;
+ mWm.addView(mArrowOverlay, arrowParams);
+
+ View leftArrow = mArrowOverlay.findViewById(R.id.left_arrow);
+ View topArrow = mArrowOverlay.findViewById(R.id.up_arrow);
+ View rightArrow = mArrowOverlay.findViewById(R.id.right_arrow);
+ View bottomArrow = mArrowOverlay.findViewById(R.id.down_arrow);
+
+ leftArrow.setOnClickListener(this);
+ topArrow.setOnClickListener(this);
+ rightArrow.setOnClickListener(this);
+ bottomArrow.setOnClickListener(this);
+
+ leftArrow.setOnLongClickListener(this);
+ topArrow.setOnLongClickListener(this);
+ rightArrow.setOnLongClickListener(this);
+ bottomArrow.setOnLongClickListener(this);
+
+ leftArrow.setOnTouchListener(this);
+ topArrow.setOnTouchListener(this);
+ rightArrow.setOnTouchListener(this);
+ bottomArrow.setOnTouchListener(this);
+
+ mArrowOverlay.findViewById(R.id.zoom_in_button).setOnClickListener(v -> {
+ if (mCurrScale <= 1) {
+ mCurrScale *= 2;
+ } else {
+ mCurrScale += 0.5;
+ }
+
+ updateMirror(mCurrFrame, mCurrScale);
+ });
+ mArrowOverlay.findViewById(R.id.zoom_out_button).setOnClickListener(v -> {
+ if (mCurrScale <= 1) {
+ mCurrScale /= 2;
+ } else {
+ mCurrScale -= 0.5;
+ }
+
+ updateMirror(mCurrFrame, mCurrScale);
+ });
+ }
+
+ private void removeArrowOverlay() {
+ if (mArrowOverlay != null) {
+ mWm.removeView(mArrowOverlay);
+ mArrowOverlay = null;
+ }
+ }
+
+ private void createMirrorOverlay() {
+ mOverlayView = new LinearLayout(this);
+ WindowManager.LayoutParams params = new WindowManager.LayoutParams(mDisplayBounds.width(),
+ mDisplayBounds.height(),
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
+ WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.RGBA_8888);
+ params.gravity = Gravity.LEFT | Gravity.TOP;
+ params.setTitle("Mirror Overlay");
+ mOverlayView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+ | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
+
+ mWm.addView(mOverlayView, params);
+
+ }
+
+ private void removeMirror() {
+ if (mSurfaceControl.isValid()) {
+ mTransaction.remove(mSurfaceControl).apply();
+ }
+ mHasMirror = false;
+ }
+
+ private void createOrUpdateMirror() {
+ if (mHasMirror) {
+ updateMirror(getDisplayFrame(), getScale());
+ } else {
+ createMirror(getDisplayFrame(), getScale());
+ }
+
+ }
+
+ private Rect getDisplayFrame() {
+ mTmpRect.setEmpty();
+ String[] frameVals = mDisplayFrameText.getText().toString().split("\\s*,\\s*");
+ if (frameVals.length != 4) {
+ return mTmpRect;
+ }
+
+ try {
+ mTmpRect.set(Integer.parseInt(frameVals[0]), Integer.parseInt(frameVals[1]),
+ Integer.parseInt(frameVals[2]), Integer.parseInt(frameVals[3]));
+ } catch (Exception e) {
+ mTmpRect.setEmpty();
+ }
+
+ return mTmpRect;
+ }
+
+ private float getScale() {
+ try {
+ return Float.parseFloat(mScaleText.getText().toString());
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ private void createMirror(Rect displayFrame, float scale) {
+ boolean success = false;
+ try {
+ success = mIWm.mirrorDisplay(mDisplayId, mSurfaceControl);
+ } catch (RemoteException e) {
+ }
+
+ if (!success) {
+ return;
+ }
+
+ if (!mSurfaceControl.isValid()) {
+ return;
+ }
+
+ mHasMirror = true;
+
+ mBorderSc = new SurfaceControl.Builder()
+ .setName("Mirror Border")
+ .setBufferSize(1, 1)
+ .setFormat(PixelFormat.TRANSLUCENT)
+ .build();
+
+ updateMirror(displayFrame, scale);
+
+ mTransaction
+ .show(mSurfaceControl)
+ .reparent(mSurfaceControl, mOverlayView.getViewRootImpl().getSurfaceControl())
+ .setLayer(mBorderSc, 1)
+ .show(mBorderSc)
+ .reparent(mBorderSc, mSurfaceControl)
+ .apply();
+ }
+
+ private void updateMirror(Rect displayFrame, float scale) {
+ if (displayFrame.isEmpty()) {
+ Rect bounds = mDisplayBounds;
+ int defaultCropW = Math.round(bounds.width() / 2);
+ int defaultCropH = Math.round(bounds.height() / 2);
+ displayFrame.set(0, 0, defaultCropW, defaultCropH);
+ }
+
+ if (scale <= 0) {
+ scale = DEFAULT_SCALE;
+ }
+
+ mCurrFrame.set(displayFrame);
+ mCurrScale = scale;
+
+ int width = (int) Math.ceil(displayFrame.width() / scale);
+ int height = (int) Math.ceil(displayFrame.height() / scale);
+
+ Rect sourceBounds = getSourceBounds(displayFrame, scale);
+
+ mTransaction.setGeometry(mSurfaceControl, sourceBounds, displayFrame, Surface.ROTATION_0)
+ .setPosition(mBorderSc, sourceBounds.left, sourceBounds.top)
+ .setBufferSize(mBorderSc, width, height)
+ .apply();
+
+ drawBorder(mBorderSc, width, height, (int) Math.ceil(BORDER_SIZE / scale));
+
+ mSourcePositionText.setText(sourceBounds.left + ", " + sourceBounds.top);
+ mDisplayFrameText.setText(
+ String.format("%s, %s, %s, %s", mCurrFrame.left, mCurrFrame.top, mCurrFrame.right,
+ mCurrFrame.bottom));
+ mScaleText.setText(String.valueOf(mCurrScale));
+ }
+
+ private void drawBorder(SurfaceControl borderSc, int width, int height, int borderSize) {
+ mTmpSurface.copyFrom(borderSc);
+
+ Canvas c = null;
+ try {
+ c = mTmpSurface.lockCanvas(null);
+ } catch (IllegalArgumentException | Surface.OutOfResourcesException e) {
+ }
+ if (c == null) {
+ return;
+ }
+
+ // Top
+ c.save();
+ c.clipRect(new Rect(0, 0, width, borderSize));
+ c.drawColor(DEFAULT_BORDER_COLOR);
+ c.restore();
+ // Left
+ c.save();
+ c.clipRect(new Rect(0, 0, borderSize, height));
+ c.drawColor(DEFAULT_BORDER_COLOR);
+ c.restore();
+ // Right
+ c.save();
+ c.clipRect(new Rect(width - borderSize, 0, width, height));
+ c.drawColor(DEFAULT_BORDER_COLOR);
+ c.restore();
+ // Bottom
+ c.save();
+ c.clipRect(new Rect(0, height - borderSize, width, height));
+ c.drawColor(DEFAULT_BORDER_COLOR);
+ c.restore();
+
+ mTmpSurface.unlockCanvasAndPost(c);
+ }
+
+ @Override
+ public void onClick(View v) {
+ Point offset = findOffset(v);
+ moveMirrorForArrows(offset.x, offset.y);
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ mIsPressedDown = true;
+ Point point = findOffset(v);
+ mMoveMirrorRunnable.mXOffset = point.x;
+ mMoveMirrorRunnable.mYOffset = point.y;
+ mHandler.post(mMoveMirrorRunnable);
+ return false;
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mIsPressedDown = false;
+ break;
+ }
+ return false;
+ }
+
+ private Point findOffset(View v) {
+ Point offset = new Point(0, 0);
+
+ switch (v.getId()) {
+ case R.id.up_arrow:
+ offset.y = -MOVE_FRAME_AMOUNT;
+ break;
+ case R.id.down_arrow:
+ offset.y = MOVE_FRAME_AMOUNT;
+ break;
+ case R.id.right_arrow:
+ offset.x = -MOVE_FRAME_AMOUNT;
+ break;
+ case R.id.left_arrow:
+ offset.x = MOVE_FRAME_AMOUNT;
+ break;
+ }
+
+ return offset;
+ }
+
+ private void moveMirrorForArrows(int xOffset, int yOffset) {
+ mCurrFrame.offset(xOffset, yOffset);
+
+ updateMirror(mCurrFrame, mCurrScale);
+ }
+
+ /**
+ * Calculates the desired source bounds. This will be the area under from the center of the
+ * displayFrame, factoring in scale.
+ */
+ private Rect getSourceBounds(Rect displayFrame, float scale) {
+ int halfWidth = displayFrame.width() / 2;
+ int halfHeight = displayFrame.height() / 2;
+ int left = displayFrame.left + (halfWidth - (int) (halfWidth / scale));
+ int right = displayFrame.right - (halfWidth - (int) (halfWidth / scale));
+ int top = displayFrame.top + (halfHeight - (int) (halfHeight / scale));
+ int bottom = displayFrame.bottom - (halfHeight - (int) (halfHeight / scale));
+ return new Rect(left, top, right, bottom);
+ }
+
+ class MoveMirrorRunnable implements Runnable {
+ int mXOffset = 0;
+ int mYOffset = 0;
+
+ @Override
+ public void run() {
+ if (mIsPressedDown) {
+ moveMirrorForArrows(mXOffset, mYOffset);
+ mHandler.postDelayed(mMoveMirrorRunnable, 150);
+ }
+ }
+ }
+}
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 79f5095010e8..06b58fda5b7d 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -626,11 +626,40 @@ public class PackageWatchdogTest {
// Verify that health check is failed
assertThat(observer.mMitigatedPackages).containsExactly(APP_A);
- // Then clear failed packages and start observing a random package so requests are synced
- // and PackageWatchdog#onSupportedPackages is called and APP_A has a chance to fail again
- // this time due to package expiry.
+ // Clear failed packages and forward time to expire the observation duration
observer.mMitigatedPackages.clear();
- watchdog.startObservingHealth(observer, Arrays.asList(APP_B), LONG_DURATION);
+ moveTimeForwardAndDispatch(LONG_DURATION);
+
+ // Verify that health check failure is not notified again
+ assertThat(observer.mMitigatedPackages).isEmpty();
+ }
+
+ /**
+ * Tests failure when health check duration is different from package observation duration
+ * Failure is also notified only once.
+ */
+ @Test
+ public void testExplicitHealthCheckFailureAfterExpiry() {
+ TestController controller = new TestController();
+ PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */);
+ TestObserver observer = new TestObserver(OBSERVER_NAME_1,
+ PackageHealthObserverImpact.USER_IMPACT_MEDIUM);
+
+ // Start observing with explicit health checks for APP_A and
+ // package observation duration == SHORT_DURATION / 2
+ // health check duration == SHORT_DURATION (set by default in the TestController)
+ controller.setSupportedPackages(Arrays.asList(APP_A));
+ watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION / 2);
+
+ // Forward time to expire the observation duration
+ moveTimeForwardAndDispatch(SHORT_DURATION / 2);
+
+ // Verify that health check is failed
+ assertThat(observer.mMitigatedPackages).containsExactly(APP_A);
+
+ // Clear failed packages and forward time to expire the health check duration
+ observer.mMitigatedPackages.clear();
+ moveTimeForwardAndDispatch(SHORT_DURATION);
// Verify that health check failure is not notified again
assertThat(observer.mMitigatedPackages).isEmpty();
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index 231d045bd817..085c53cf45ea 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -19,7 +19,6 @@ android_test {
static_libs: ["androidx.test.rules", "cts-rollback-lib", "cts-install-lib"],
test_suites: ["general-tests"],
test_config: "RollbackTest.xml",
- // TODO: sdk_version: "test_current" when Intent#resolveSystemservice is TestApi
}
java_test_host {
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index ed8f272874e1..599ee56afdb3 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -33,6 +33,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
+import android.os.UserManager;
import android.provider.DeviceConfig;
import android.util.Log;
@@ -52,7 +53,9 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.Collections;
+import java.util.List;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
/**
* Test system Rollback APIs.
@@ -96,7 +99,10 @@ public class RollbackTest {
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.DELETE_PACKAGES,
Manifest.permission.TEST_MANAGE_ROLLBACKS,
- Manifest.permission.MANAGE_ROLLBACKS);
+ Manifest.permission.MANAGE_ROLLBACKS,
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.INTERACT_ACROSS_USERS);
// Register a broadcast receiver for notification when the
// rollback has been committed.
@@ -106,7 +112,6 @@ public class RollbackTest {
// Uninstall TestApp.A
Uninstall.packages(TestApp.A);
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
-
// TODO: There is currently a race condition between when the app is
// uninstalled and when rollback manager deletes the rollback. Fix it
// so that's not the case!
@@ -149,6 +154,12 @@ public class RollbackTest {
RollbackUtils.rollback(available.getRollbackId());
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ UserManager um = (UserManager) context.getSystemService(context.USER_SERVICE);
+ List<Integer> userIds = um.getUsers(true)
+ .stream().map(user -> user.id).collect(Collectors.toList());
+ assertThat(InstallUtils.isOnlyInstalledForUser(TestApp.A,
+ context.getUserId(), userIds)).isTrue();
+
// Verify we received a broadcast for the rollback.
// TODO: Race condition between the timeout and when the broadcast is
// received could lead to test flakiness.
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 9e6ac8ef679b..79b8b4623cd8 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -32,6 +32,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
+import android.provider.DeviceConfig;
import android.text.TextUtils;
import androidx.test.InstrumentationRegistry;
@@ -51,6 +52,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.concurrent.TimeUnit;
+
/**
* Tests for rollback of staged installs.
* <p>
@@ -64,6 +67,8 @@ public class StagedRollbackTest {
private static final String NETWORK_STACK_CONNECTOR_CLASS =
"android.net.INetworkStackConnector";
+ private static final String PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT =
+ "watchdog_trigger_failure_count";
private static final String MODULE_META_DATA_PACKAGE = getModuleMetadataPackageName();
@@ -76,7 +81,8 @@ public class StagedRollbackTest {
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.DELETE_PACKAGES,
Manifest.permission.TEST_MANAGE_ROLLBACKS,
- Manifest.permission.FORCE_STOP_PACKAGES);
+ Manifest.permission.FORCE_STOP_PACKAGES,
+ Manifest.permission.WRITE_DEVICE_CONFIG);
}
/**
@@ -92,7 +98,7 @@ public class StagedRollbackTest {
* Enable rollback phase.
*/
@Test
- public void testBadApkOnlyEnableRollback() throws Exception {
+ public void testBadApkOnly_Phase1() throws Exception {
Uninstall.packages(TestApp.A);
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
@@ -101,9 +107,6 @@ public class StagedRollbackTest {
InstallUtils.processUserData(TestApp.A);
Install.single(TestApp.ACrashing2).setEnableRollback().setStaged().commit();
-
- // At this point, the host test driver will reboot the device and run
- // testBadApkOnlyConfirmEnableRollback().
}
/**
@@ -111,7 +114,7 @@ public class StagedRollbackTest {
* Confirm that rollback was successfully enabled.
*/
@Test
- public void testBadApkOnlyConfirmEnableRollback() throws Exception {
+ public void testBadApkOnly_Phase2() throws Exception {
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
InstallUtils.processUserData(TestApp.A);
@@ -123,8 +126,12 @@ public class StagedRollbackTest {
Rollback.from(TestApp.A2).to(TestApp.A1));
assertThat(rollback.isStaged()).isTrue();
- // At this point, the host test driver will run
- // testBadApkOnlyTriggerRollback().
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
+ PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
+ Integer.toString(5), false);
+ RollbackUtils.sendCrashBroadcast(TestApp.A, 4);
+ // Sleep for a while to make sure we don't trigger rollback
+ Thread.sleep(TimeUnit.SECONDS.toMillis(30));
}
/**
@@ -133,15 +140,14 @@ public class StagedRollbackTest {
* rebooting the test out from under it.
*/
@Test
- public void testBadApkOnlyTriggerRollback() throws Exception {
- // Crash TestApp.A PackageWatchdog#TRIGGER_FAILURE_COUNT times to trigger rollback
- RollbackUtils.sendCrashBroadcast(TestApp.A, 5);
+ public void testBadApkOnly_Phase3() throws Exception {
+ // One more crash to trigger rollback
+ RollbackUtils.sendCrashBroadcast(TestApp.A, 1);
- // We expect the device to be rebooted automatically. Wait for that to
- // happen. At that point, the host test driver will wait for the
- // device to come back up and run testApkOnlyConfirmRollback().
+ // We expect the device to be rebooted automatically. Wait for that to happen.
Thread.sleep(30 * 1000);
+ // Raise an error anyway if reboot didn't happen.
fail("watchdog did not trigger reboot");
}
@@ -150,7 +156,7 @@ public class StagedRollbackTest {
* Confirm rollback phase.
*/
@Test
- public void testBadApkOnlyConfirmRollback() throws Exception {
+ public void testBadApkOnly_Phase4() throws Exception {
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
InstallUtils.processUserData(TestApp.A);
@@ -165,20 +171,11 @@ public class StagedRollbackTest {
assertThat(rollback.getCommittedSessionId()).isNotEqualTo(-1);
}
+ /**
+ * Stage install ModuleMetadata package to simulate a Mainline module update.
+ */
@Test
- public void resetNetworkStack() throws Exception {
- RollbackManager rm = RollbackUtils.getRollbackManager();
- String networkStack = getNetworkStackPackageName();
-
- rm.expireRollbackForPackage(networkStack);
- Uninstall.packages(networkStack);
-
- assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
- networkStack)).isNull();
- }
-
- @Test
- public void installModuleMetadataPackage() throws Exception {
+ public void testNativeWatchdogTriggersRollback_Phase1() throws Exception {
resetModuleMetadataPackage();
Context context = InstrumentationRegistry.getContext();
PackageInfo metadataPackageInfo = context.getPackageManager().getPackageInfo(
@@ -192,39 +189,57 @@ public class StagedRollbackTest {
+ metadataApkPath);
}
+ /**
+ * Verify the rollback is available.
+ */
@Test
- public void assertNetworkStackRollbackAvailable() throws Exception {
+ public void testNativeWatchdogTriggersRollback_Phase2() throws Exception {
RollbackManager rm = RollbackUtils.getRollbackManager();
assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
- getNetworkStackPackageName())).isNotNull();
+ MODULE_META_DATA_PACKAGE)).isNotNull();
}
+ /**
+ * Verify the rollback is committed after crashing.
+ */
@Test
- public void assertNetworkStackRollbackCommitted() throws Exception {
+ public void testNativeWatchdogTriggersRollback_Phase3() throws Exception {
RollbackManager rm = RollbackUtils.getRollbackManager();
assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
- getNetworkStackPackageName())).isNotNull();
+ MODULE_META_DATA_PACKAGE)).isNotNull();
}
@Test
- public void assertNoNetworkStackRollbackCommitted() throws Exception {
+ public void testNetworkFailedRollback_Phase1() throws Exception {
RollbackManager rm = RollbackUtils.getRollbackManager();
- assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
- getNetworkStackPackageName())).isNull();
+ String networkStack = getNetworkStackPackageName();
+
+ rm.expireRollbackForPackage(networkStack);
+ Uninstall.packages(networkStack);
+
+ assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
+ networkStack)).isNull();
}
@Test
- public void assertModuleMetadataRollbackAvailable() throws Exception {
+ public void testNetworkFailedRollback_Phase2() throws Exception {
RollbackManager rm = RollbackUtils.getRollbackManager();
assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
- MODULE_META_DATA_PACKAGE)).isNotNull();
+ getNetworkStackPackageName())).isNotNull();
}
@Test
- public void assertModuleMetadataRollbackCommitted() throws Exception {
+ public void testNetworkFailedRollback_Phase3() throws Exception {
RollbackManager rm = RollbackUtils.getRollbackManager();
assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
- MODULE_META_DATA_PACKAGE)).isNotNull();
+ getNetworkStackPackageName())).isNull();
+ }
+
+ @Test
+ public void testNetworkFailedRollback_Phase4() throws Exception {
+ RollbackManager rm = RollbackUtils.getRollbackManager();
+ assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
+ getNetworkStackPackageName())).isNotNull();
}
private String getNetworkStackPackageName() {
@@ -235,7 +250,7 @@ public class StagedRollbackTest {
}
@Test
- public void testPreviouslyAbandonedRollbacksEnableRollback() throws Exception {
+ public void testPreviouslyAbandonedRollbacks_Phase1() throws Exception {
Uninstall.packages(TestApp.A);
Install.single(TestApp.A1).commit();
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
@@ -253,7 +268,7 @@ public class StagedRollbackTest {
}
@Test
- public void testPreviouslyAbandonedRollbacksCommitRollback() throws Exception {
+ public void testPreviouslyAbandonedRollbacks_Phase2() throws Exception {
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
InstallUtils.processUserData(TestApp.A);
@@ -264,12 +279,38 @@ public class StagedRollbackTest {
}
@Test
- public void testPreviouslyAbandonedRollbacksCheckUserdataRollback() throws Exception {
+ public void testPreviouslyAbandonedRollbacks_Phase3() throws Exception {
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
InstallUtils.processUserData(TestApp.A);
Uninstall.packages(TestApp.A);
}
+ @Test
+ public void testNetworkPassedDoesNotRollback_Phase1() throws Exception {
+ RollbackManager rm = RollbackUtils.getRollbackManager();
+ String networkStack = getNetworkStackPackageName();
+
+ rm.expireRollbackForPackage(networkStack);
+ Uninstall.packages(networkStack);
+
+ assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
+ networkStack)).isNull();
+ }
+
+ @Test
+ public void testNetworkPassedDoesNotRollback_Phase2() throws Exception {
+ RollbackManager rm = RollbackUtils.getRollbackManager();
+ assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
+ getNetworkStackPackageName())).isNotNull();
+ }
+
+ @Test
+ public void testNetworkPassedDoesNotRollback_Phase3() throws Exception {
+ RollbackManager rm = RollbackUtils.getRollbackManager();
+ assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
+ getNetworkStackPackageName())).isNull();
+ }
+
@Nullable
private static String getModuleMetadataPackageName() {
String packageName = InstrumentationRegistry.getContext().getResources().getString(
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 bc98f06ebc56..f7fe6c728f6a 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
@@ -68,35 +68,34 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
*/
@Test
public void testBadApkOnly() throws Exception {
- runPhase("testBadApkOnlyEnableRollback");
+ runPhase("testBadApkOnly_Phase1");
getDevice().reboot();
- runPhase("testBadApkOnlyConfirmEnableRollback");
+ runPhase("testBadApkOnly_Phase2");
try {
// This is expected to fail due to the device being rebooted out
// from underneath the test. If this fails for reasons other than
// the device reboot, those failures should result in failure of
// the testApkOnlyConfirmRollback phase.
CLog.logAndDisplay(LogLevel.INFO, "testBadApkOnlyTriggerRollback is expected to fail");
- runPhase("testBadApkOnlyTriggerRollback");
+ runPhase("testBadApkOnly_Phase3");
} catch (AssertionError e) {
// AssertionError is expected.
}
getDevice().waitForDeviceAvailable();
- runPhase("testBadApkOnlyConfirmRollback");
+ runPhase("testBadApkOnly_Phase4");
}
@Test
public void testNativeWatchdogTriggersRollback() throws Exception {
//Stage install ModuleMetadata package - this simulates a Mainline module update
- runPhase("installModuleMetadataPackage");
+ runPhase("testNativeWatchdogTriggersRollback_Phase1");
// Reboot device to activate staged package
getDevice().reboot();
- getDevice().waitForDeviceAvailable();
- runPhase("assertModuleMetadataRollbackAvailable");
+ runPhase("testNativeWatchdogTriggersRollback_Phase2");
// crash system_server enough times to trigger a rollback
crashProcess("system_server", NATIVE_CRASHES_THRESHOLD);
@@ -113,7 +112,7 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
getDevice().waitForDeviceAvailable();
// verify rollback committed
- runPhase("assertModuleMetadataRollbackCommitted");
+ runPhase("testNativeWatchdogTriggersRollback_Phase3");
}
/**
@@ -122,7 +121,7 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
@Test
public void testNetworkFailedRollback() throws Exception {
// Remove available rollbacks and uninstall NetworkStack on /data/
- runPhase("resetNetworkStack");
+ runPhase("testNetworkFailedRollback_Phase1");
// Reduce health check deadline
getDevice().executeShellCommand("device_config put rollback "
+ "watchdog_request_timeout_millis 300000");
@@ -134,20 +133,19 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
Thread.sleep(5000);
// Reboot device to activate staged package
getDevice().reboot();
- getDevice().waitForDeviceAvailable();
// Verify rollback was enabled
- runPhase("assertNetworkStackRollbackAvailable");
+ runPhase("testNetworkFailedRollback_Phase2");
// Sleep for < health check deadline
Thread.sleep(5000);
// Verify rollback was not executed before health check deadline
- runPhase("assertNoNetworkStackRollbackCommitted");
+ runPhase("testNetworkFailedRollback_Phase3");
try {
// This is expected to fail due to the device being rebooted out
// from underneath the test. If this fails for reasons other than
// the device reboot, those failures should result in failure of
- // the assertNetworkStackExecutedRollback phase.
+ // the testNetworkFailedRollback_Phase4 phase.
CLog.logAndDisplay(LogLevel.INFO, "Sleep and expect to fail while sleeping");
// Sleep for > health check deadline
Thread.sleep(260000);
@@ -157,7 +155,7 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
getDevice().waitForDeviceAvailable();
// Verify rollback was executed after health check deadline
- runPhase("assertNetworkStackRollbackCommitted");
+ runPhase("testNetworkFailedRollback_Phase4");
}
/**
@@ -166,7 +164,7 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
@Test
public void testNetworkPassedDoesNotRollback() throws Exception {
// Remove available rollbacks and uninstall NetworkStack on /data/
- runPhase("resetNetworkStack");
+ runPhase("testNetworkPassedDoesNotRollback_Phase1");
// Reduce health check deadline, here unlike the network failed case, we use
// a longer deadline because joining a network can take a much longer time for
// reasons external to the device than 'not joining'
@@ -180,10 +178,9 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
Thread.sleep(5000);
// Reboot device to activate staged package
getDevice().reboot();
- getDevice().waitForDeviceAvailable();
// Verify rollback was enabled
- runPhase("assertNetworkStackRollbackAvailable");
+ runPhase("testNetworkPassedDoesNotRollback_Phase2");
// Connect to internet so network health check passes
getDevice().executeShellCommand("svc wifi enable");
@@ -196,7 +193,7 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
// Sleep for > health check deadline
Thread.sleep(310000);
// Verify rollback was not executed after health check deadline
- runPhase("assertNoNetworkStackRollbackCommitted");
+ runPhase("testNetworkPassedDoesNotRollback_Phase3");
}
/**
@@ -204,11 +201,11 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
*/
@Test
public void testPreviouslyAbandonedRollbacks() throws Exception {
- runPhase("testPreviouslyAbandonedRollbacksEnableRollback");
+ runPhase("testPreviouslyAbandonedRollbacks_Phase1");
getDevice().reboot();
- runPhase("testPreviouslyAbandonedRollbacksCommitRollback");
+ runPhase("testPreviouslyAbandonedRollbacks_Phase2");
getDevice().reboot();
- runPhase("testPreviouslyAbandonedRollbacksCheckUserdataRollback");
+ runPhase("testPreviouslyAbandonedRollbacks_Phase3");
}
private void crashProcess(String processName, int numberOfCrashes) throws Exception {
diff --git a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
index 7d9d0d52b002..7e8a13470c35 100644
--- a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
+++ b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
@@ -30,6 +30,7 @@ import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.usage.IntervalStats;
+import com.android.server.usage.PackagesTokenData;
import com.android.server.usage.UsageStatsDatabase;
import com.android.server.usage.UsageStatsDatabase.StatCombiner;
@@ -79,6 +80,7 @@ public class UsageStatsDatabasePerfTest {
sContext = InstrumentationRegistry.getTargetContext();
mTestDir = new File(sContext.getFilesDir(), "UsageStatsDatabasePerfTest");
sUsageStatsDatabase = new UsageStatsDatabase(mTestDir);
+ sUsageStatsDatabase.readMappingsLocked();
sUsageStatsDatabase.init(1);
}
@@ -140,6 +142,37 @@ public class UsageStatsDatabasePerfTest {
}
}
+ private void runObfuscateStatsTest(int packageCount, int eventsPerPackage) {
+ final ManualBenchmarkState benchmarkState = mPerfManualStatusReporter.getBenchmarkState();
+ IntervalStats intervalStats = new IntervalStats();
+ populateIntervalStats(intervalStats, packageCount, eventsPerPackage);
+ long elapsedTimeNs = 0;
+ while (benchmarkState.keepRunning(elapsedTimeNs)) {
+ final long startTime = SystemClock.elapsedRealtimeNanos();
+ PackagesTokenData packagesTokenData = new PackagesTokenData();
+ intervalStats.obfuscateData(packagesTokenData);
+ final long endTime = SystemClock.elapsedRealtimeNanos();
+ elapsedTimeNs = endTime - startTime;
+ clearUsageStatsFiles();
+ }
+ }
+
+ private void runDeobfuscateStatsTest(int packageCount, int eventsPerPackage) {
+ final ManualBenchmarkState benchmarkState = mPerfManualStatusReporter.getBenchmarkState();
+ IntervalStats intervalStats = new IntervalStats();
+ populateIntervalStats(intervalStats, packageCount, eventsPerPackage);
+ long elapsedTimeNs = 0;
+ while (benchmarkState.keepRunning(elapsedTimeNs)) {
+ PackagesTokenData packagesTokenData = new PackagesTokenData();
+ intervalStats.obfuscateData(packagesTokenData);
+ final long startTime = SystemClock.elapsedRealtimeNanos();
+ intervalStats.deobfuscateData(packagesTokenData);
+ final long endTime = SystemClock.elapsedRealtimeNanos();
+ elapsedTimeNs = endTime - startTime;
+ clearUsageStatsFiles();
+ }
+ }
+
@Test
public void testQueryUsageStats_FewPkgsLightUse() throws IOException {
runQueryUsageStatsTest(FEW_PKGS, LIGHT_USE);
@@ -151,6 +184,16 @@ public class UsageStatsDatabasePerfTest {
}
@Test
+ public void testObfuscateStats_FewPkgsLightUse() {
+ runObfuscateStatsTest(FEW_PKGS, LIGHT_USE);
+ }
+
+ @Test
+ public void testDeobfuscateStats_FewPkgsLightUse() {
+ runDeobfuscateStatsTest(FEW_PKGS, LIGHT_USE);
+ }
+
+ @Test
public void testQueryUsageStats_FewPkgsHeavyUse() throws IOException {
runQueryUsageStatsTest(FEW_PKGS, HEAVY_USE);
}
@@ -161,6 +204,16 @@ public class UsageStatsDatabasePerfTest {
}
@Test
+ public void testObfuscateStats_FewPkgsHeavyUse() {
+ runObfuscateStatsTest(FEW_PKGS, HEAVY_USE);
+ }
+
+ @Test
+ public void testDeobfuscateStats_FewPkgsHeavyUse() {
+ runDeobfuscateStatsTest(FEW_PKGS, HEAVY_USE);
+ }
+
+ @Test
public void testQueryUsageStats_ManyPkgsLightUse() throws IOException {
runQueryUsageStatsTest(MANY_PKGS, LIGHT_USE);
}
@@ -171,6 +224,16 @@ public class UsageStatsDatabasePerfTest {
}
@Test
+ public void testObfuscateStats_ManyPkgsLightUse() {
+ runObfuscateStatsTest(MANY_PKGS, LIGHT_USE);
+ }
+
+ @Test
+ public void testDeobfuscateStats_ManyPkgsLightUse() {
+ runDeobfuscateStatsTest(MANY_PKGS, LIGHT_USE);
+ }
+
+ @Test
public void testQueryUsageStats_ManyPkgsHeavyUse() throws IOException {
runQueryUsageStatsTest(MANY_PKGS, HEAVY_USE);
}
@@ -179,4 +242,14 @@ public class UsageStatsDatabasePerfTest {
public void testPutUsageStats_ManyPkgsHeavyUse() throws IOException {
runPutUsageStatsTest(MANY_PKGS, HEAVY_USE);
}
+
+ @Test
+ public void testObfuscateStats_ManyPkgsHeavyUse() {
+ runObfuscateStatsTest(MANY_PKGS, HEAVY_USE);
+ }
+
+ @Test
+ public void testDeobfuscateStats_ManyPkgsHeavyUse() {
+ runDeobfuscateStatsTest(MANY_PKGS, HEAVY_USE);
+ }
}
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index e91abb6c4a44..beb5e0db6422 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -5,7 +5,6 @@ java_defaults {
name: "FrameworksNetTests-jni-defaults",
jni_libs: [
"ld-android",
- "libartbase",
"libbacktrace",
"libbase",
"libbinder",
@@ -16,7 +15,6 @@ java_defaults {
"libcgrouprc",
"libcrypto",
"libcutils",
- "libdexfile",
"libdl_android",
"libhidl-gen-utils",
"libhidlbase",
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java
index b0464d9e656f..ae8285b8a908 100644
--- a/tests/net/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/common/java/android/net/LinkPropertiesTest.java
@@ -99,6 +99,7 @@ public class LinkPropertiesTest {
assertFalse(lp.isIpv4Provisioned());
assertFalse(lp.isIpv6Provisioned());
assertFalse(lp.isPrivateDnsActive());
+ assertFalse(lp.isWakeOnLanSupported());
}
private LinkProperties makeTestObject() {
@@ -120,6 +121,7 @@ public class LinkPropertiesTest {
lp.setMtu(MTU);
lp.setTcpBufferSizes(TCP_BUFFER_SIZES);
lp.setNat64Prefix(new IpPrefix("2001:db8:0:64::/96"));
+ lp.setWakeOnLanSupported(true);
return lp;
}
@@ -158,6 +160,9 @@ public class LinkPropertiesTest {
assertTrue(source.isIdenticalTcpBufferSizes(target));
assertTrue(target.isIdenticalTcpBufferSizes(source));
+ assertTrue(source.isIdenticalWakeOnLan(target));
+ assertTrue(target.isIdenticalWakeOnLan(source));
+
// Check result of equals().
assertTrue(source.equals(target));
assertTrue(target.equals(source));
@@ -1057,4 +1062,13 @@ public class LinkPropertiesTest {
lp.clear();
assertFalse(lp.isPrivateDnsActive());
}
+
+ @Test
+ public void testWakeOnLanSupported() {
+ final LinkProperties lp = makeTestObject();
+ assertTrue(lp.isWakeOnLanSupported());
+
+ lp.clear();
+ assertFalse(lp.isWakeOnLanSupported());
+ }
}
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index 2ca0d1a81e13..15691127cab7 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -271,7 +271,7 @@ public class NetworkCapabilitiesTest {
.addCapability(NET_CAPABILITY_NOT_METERED);
assertParcelingIsLossless(netCap);
netCap.setSSID(TEST_SSID);
- assertParcelSane(netCap, 11);
+ assertParcelSane(netCap, 12);
}
@Test
diff --git a/tests/net/java/android/net/ip/IpServerTest.java b/tests/net/java/android/net/ip/IpServerTest.java
index 05912e85426a..b6ccebb3dcd3 100644
--- a/tests/net/java/android/net/ip/IpServerTest.java
+++ b/tests/net/java/android/net/ip/IpServerTest.java
@@ -19,11 +19,13 @@ package android.net.ip;
import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
import static android.net.ConnectivityManager.TETHERING_USB;
import static android.net.ConnectivityManager.TETHERING_WIFI;
+import static android.net.ConnectivityManager.TETHERING_WIFI_P2P;
import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.net.ip.IpServer.STATE_AVAILABLE;
+import static android.net.ip.IpServer.STATE_LOCAL_ONLY;
import static android.net.ip.IpServer.STATE_TETHERED;
import static android.net.ip.IpServer.STATE_UNAVAILABLE;
import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
@@ -256,6 +258,23 @@ public class IpServerTest {
}
@Test
+ public void canBeTetheredAsWifiP2p() throws Exception {
+ initStateMachine(TETHERING_WIFI_P2P);
+
+ dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_LOCAL_ONLY);
+ InOrder inOrder = inOrder(mCallback, mNMService);
+ inOrder.verify(mNMService).getInterfaceConfig(IFACE_NAME);
+ inOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration);
+ inOrder.verify(mNMService).tetherInterface(IFACE_NAME);
+ inOrder.verify(mCallback).updateInterfaceState(
+ mIpServer, STATE_LOCAL_ONLY, TETHER_ERROR_NO_ERROR);
+ inOrder.verify(mCallback).updateLinkProperties(
+ eq(mIpServer), mLinkPropertiesCaptor.capture());
+ assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
+ verifyNoMoreInteractions(mNMService, mStatsService, mCallback);
+ }
+
+ @Test
public void handlesFirstUpstreamChange() throws Exception {
initTetheredStateMachine(TETHERING_BLUETOOTH, null);
@@ -419,6 +438,14 @@ public class IpServerTest {
}
@Test
+ public void startsDhcpServerOnWifiP2p() throws Exception {
+ initTetheredStateMachine(TETHERING_WIFI_P2P, UPSTREAM_IFACE);
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE);
+
+ assertDhcpStarted(new IpPrefix("192.168.49.0/24"));
+ }
+
+ @Test
public void doesNotStartDhcpServerIfDisabled() throws Exception {
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */);
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
diff --git a/tests/net/java/android/net/netlink/InetDiagSocketTest.java b/tests/net/java/android/net/netlink/InetDiagSocketTest.java
index 2adbb06babf1..46e27c1d3d3b 100644
--- a/tests/net/java/android/net/netlink/InetDiagSocketTest.java
+++ b/tests/net/java/android/net/netlink/InetDiagSocketTest.java
@@ -18,7 +18,6 @@ package android.net.netlink;
import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP;
import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
-import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.AF_INET6;
import static android.system.OsConstants.IPPROTO_TCP;
@@ -28,6 +27,7 @@ import static android.system.OsConstants.SOCK_STREAM;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -152,9 +152,13 @@ public class InetDiagSocketTest {
private void checkConnectionOwnerUid(int protocol, InetSocketAddress local,
InetSocketAddress remote, boolean expectSuccess) {
- final int expectedUid = expectSuccess ? Process.myUid() : INVALID_UID;
final int uid = mCm.getConnectionOwnerUid(protocol, local, remote);
- assertEquals(expectedUid, uid);
+
+ if (expectSuccess) {
+ assertEquals(Process.myUid(), uid);
+ } else {
+ assertNotEquals(Process.myUid(), uid);
+ }
}
private int findLikelyFreeUdpPort(UdpConnection conn) throws Exception {
@@ -165,11 +169,11 @@ public class InetDiagSocketTest {
return localPort;
}
+ /**
+ * Create a test connection for UDP and TCP sockets and verify that this
+ * {protocol, local, remote} socket result in receiving a valid UID.
+ */
public void checkGetConnectionOwnerUid(String to, String from) throws Exception {
- /**
- * For TCP connections, create a test connection and verify that this
- * {protocol, local, remote} socket result in receiving a valid UID.
- */
TcpConnection tcp = new TcpConnection(to, from);
checkConnectionOwnerUid(tcp.protocol, tcp.local, tcp.remote, true);
checkConnectionOwnerUid(IPPROTO_UDP, tcp.local, tcp.remote, false);
@@ -177,20 +181,14 @@ public class InetDiagSocketTest {
checkConnectionOwnerUid(tcp.protocol, tcp.local, new InetSocketAddress(0), false);
tcp.close();
- /**
- * For UDP connections, either a complete match {protocol, local, remote} or a
- * partial match {protocol, local} should return a valid UID.
- */
UdpConnection udp = new UdpConnection(to,from);
checkConnectionOwnerUid(udp.protocol, udp.local, udp.remote, true);
- checkConnectionOwnerUid(udp.protocol, udp.local, new InetSocketAddress(0), true);
checkConnectionOwnerUid(IPPROTO_TCP, udp.local, udp.remote, false);
checkConnectionOwnerUid(udp.protocol, new InetSocketAddress(findLikelyFreeUdpPort(udp)),
udp.remote, false);
udp.close();
}
- @Ignore
@Test
public void testGetConnectionOwnerUid() throws Exception {
checkGetConnectionOwnerUid("::", null);
@@ -203,6 +201,17 @@ public class InetDiagSocketTest {
checkGetConnectionOwnerUid("::1", "::1");
}
+ @Ignore("Times out on Marlin/Sailfish")
+ /* Verify fix for b/141603906 */
+ @Test
+ public void testB141603906() throws Exception {
+ final InetSocketAddress src = new InetSocketAddress(0);
+ final InetSocketAddress dst = new InetSocketAddress(0);
+ for (int i = 1; i <= 100000; i++) {
+ mCm.getConnectionOwnerUid(IPPROTO_TCP, src, dst);
+ }
+ }
+
// Hexadecimal representation of InetDiagReqV2 request.
private static final String INET_DIAG_REQ_V2_UDP_INET4_HEX =
// struct nlmsghdr
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index f2f258a737e3..61f37fd6c7e2 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -33,6 +33,7 @@ import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS;
+import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
@@ -207,7 +208,7 @@ import com.android.server.net.NetworkPinner;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.testutils.ExceptionUtils;
import com.android.testutils.HandlerUtilsKt;
-import com.android.testutils.RecorderCallback.CallbackRecord;
+import com.android.testutils.RecorderCallback.CallbackEntry;
import com.android.testutils.TestableNetworkCallback;
import org.junit.After;
@@ -258,13 +259,13 @@ public class ConnectivityServiceTest {
private static final String TAG = "ConnectivityServiceTest";
private static final int TIMEOUT_MS = 500;
- private static final int TEST_LINGER_DELAY_MS = 250;
+ private static final int TEST_LINGER_DELAY_MS = 300;
// Chosen to be less than the linger timeout. This ensures that we can distinguish between a
// LOST callback that arrives immediately and a LOST callback that arrives after the linger
// timeout. For this, our assertions should run fast enough to leave less than
// (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are
// supposedly fired, and the time we call expectCallback.
- private static final int TEST_CALLBACK_TIMEOUT_MS = 200;
+ private static final int TEST_CALLBACK_TIMEOUT_MS = 250;
// Chosen to be less than TEST_CALLBACK_TIMEOUT_MS. This ensures that requests have time to
// complete before callbacks are verified.
private static final int TEST_REQUEST_TIMEOUT_MS = 150;
@@ -274,6 +275,7 @@ public class ConnectivityServiceTest {
private static final String CLAT_PREFIX = "v4-";
private static final String MOBILE_IFNAME = "test_rmnet_data0";
private static final String WIFI_IFNAME = "test_wlan0";
+ private static final String WIFI_WOL_IFNAME = "test_wlan_wol";
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private MockContext mServiceContext;
@@ -343,6 +345,12 @@ public class ConnectivityServiceTest {
"mobile_mms,2,0,2,60000,true",
});
+ when(mResources.getStringArray(
+ com.android.internal.R.array.config_wakeonlan_supported_interfaces))
+ .thenReturn(new String[]{
+ WIFI_WOL_IFNAME,
+ });
+
mContentResolver = new MockContentResolver();
mContentResolver.addProvider(Settings.AUTHORITY, settingsProvider);
}
@@ -485,6 +493,8 @@ public class ConnectivityServiceTest {
private INetworkMonitor mNetworkMonitor;
private INetworkMonitorCallbacks mNmCallbacks;
private int mNmValidationResult = VALIDATION_RESULT_BASE;
+ private int mProbesCompleted;
+ private int mProbesSucceeded;
private String mNmValidationRedirectUrl = null;
private boolean mNmProvNotificationRequested = false;
@@ -504,6 +514,8 @@ public class ConnectivityServiceTest {
// Waits for the NetworkAgent to be registered, which includes the creation of the
// NetworkMonitor.
waitForIdle(TIMEOUT_MS);
+ HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+ HandlerUtilsKt.waitForIdle(ConnectivityThread.get(), TIMEOUT_MS);
}
@Override
@@ -550,6 +562,7 @@ public class ConnectivityServiceTest {
mNmProvNotificationRequested = false;
}
+ mNmCallbacks.notifyProbeStatusChanged(mProbesCompleted, mProbesSucceeded);
mNmCallbacks.notifyNetworkTested(
mNmValidationResult, mNmValidationRedirectUrl);
@@ -572,7 +585,7 @@ public class ConnectivityServiceTest {
* @param validated Indicate if network should pretend to be validated.
*/
public void connect(boolean validated) {
- connect(validated, true);
+ connect(validated, true, false /* isStrictMode */);
}
/**
@@ -580,13 +593,13 @@ public class ConnectivityServiceTest {
* @param validated Indicate if network should pretend to be validated.
* @param hasInternet Indicate if network should pretend to have NET_CAPABILITY_INTERNET.
*/
- public void connect(boolean validated, boolean hasInternet) {
+ public void connect(boolean validated, boolean hasInternet, boolean isStrictMode) {
assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_INTERNET));
ConnectivityManager.NetworkCallback callback = null;
final ConditionVariable validatedCv = new ConditionVariable();
if (validated) {
- setNetworkValid();
+ setNetworkValid(isStrictMode);
NetworkRequest request = new NetworkRequest.Builder()
.addTransportType(getNetworkCapabilities().getTransportTypes()[0])
.clearCapabilities()
@@ -611,15 +624,15 @@ public class ConnectivityServiceTest {
if (validated) {
// Wait for network to validate.
waitFor(validatedCv);
- setNetworkInvalid();
+ setNetworkInvalid(isStrictMode);
}
if (callback != null) mCm.unregisterNetworkCallback(callback);
}
- public void connectWithCaptivePortal(String redirectUrl) {
- setNetworkPortal(redirectUrl);
- connect(false);
+ public void connectWithCaptivePortal(String redirectUrl, boolean isStrictMode) {
+ setNetworkPortal(redirectUrl, isStrictMode);
+ connect(false, true /* hasInternet */, isStrictMode);
}
public void connectWithPartialConnectivity() {
@@ -627,34 +640,75 @@ public class ConnectivityServiceTest {
connect(false);
}
- public void connectWithPartialValidConnectivity() {
- setNetworkPartialValid();
- connect(false);
+ public void connectWithPartialValidConnectivity(boolean isStrictMode) {
+ setNetworkPartialValid(isStrictMode);
+ connect(false, true /* hasInternet */, isStrictMode);
}
- void setNetworkValid() {
+ void setNetworkValid(boolean isStrictMode) {
mNmValidationResult = VALIDATION_RESULT_VALID;
mNmValidationRedirectUrl = null;
+ int probesSucceeded = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTP;
+ if (isStrictMode) {
+ probesSucceeded |= NETWORK_VALIDATION_PROBE_PRIVDNS;
+ }
+ // The probesCompleted equals to probesSucceeded for the case of valid network, so put
+ // the same value into two different parameter of the method.
+ setProbesStatus(probesSucceeded, probesSucceeded);
}
- void setNetworkInvalid() {
+ void setNetworkInvalid(boolean isStrictMode) {
mNmValidationResult = VALIDATION_RESULT_INVALID;
mNmValidationRedirectUrl = null;
+ int probesCompleted = VALIDATION_RESULT_BASE;
+ int probesSucceeded = VALIDATION_RESULT_INVALID;
+ // If the isStrictMode is true, it means the network is invalid when NetworkMonitor
+ // tried to validate the private DNS but failed.
+ if (isStrictMode) {
+ probesCompleted &= ~NETWORK_VALIDATION_PROBE_HTTP;
+ probesSucceeded = probesCompleted;
+ probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS;
+ }
+ setProbesStatus(probesCompleted, probesSucceeded);
}
- void setNetworkPortal(String redirectUrl) {
- setNetworkInvalid();
+ void setNetworkPortal(String redirectUrl, boolean isStrictMode) {
+ setNetworkInvalid(isStrictMode);
mNmValidationRedirectUrl = redirectUrl;
+ // Suppose the portal is found when NetworkMonitor probes NETWORK_VALIDATION_PROBE_HTTP
+ // in the beginning, so the NETWORK_VALIDATION_PROBE_HTTPS hasn't probed yet.
+ int probesCompleted = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTPS;
+ int probesSucceeded = VALIDATION_RESULT_INVALID;
+ if (isStrictMode) {
+ probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS;
+ }
+ setProbesStatus(probesCompleted, probesSucceeded);
}
void setNetworkPartial() {
mNmValidationResult = VALIDATION_RESULT_PARTIAL;
mNmValidationRedirectUrl = null;
+ int probesCompleted = VALIDATION_RESULT_BASE;
+ int probesSucceeded = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTPS;
+ setProbesStatus(probesCompleted, probesSucceeded);
}
- void setNetworkPartialValid() {
- mNmValidationResult = VALIDATION_RESULT_PARTIAL | VALIDATION_RESULT_VALID;
- mNmValidationRedirectUrl = null;
+ void setNetworkPartialValid(boolean isStrictMode) {
+ setNetworkPartial();
+ mNmValidationResult |= VALIDATION_RESULT_VALID;
+ int probesCompleted = VALIDATION_RESULT_BASE;
+ int probesSucceeded = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTPS;
+ // Suppose the partial network cannot pass the private DNS validation as well, so only
+ // add NETWORK_VALIDATION_PROBE_DNS in probesCompleted but not probesSucceeded.
+ if (isStrictMode) {
+ probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS;
+ }
+ setProbesStatus(probesCompleted, probesSucceeded);
+ }
+
+ void setProbesStatus(int probesCompleted, int probesSucceeded) {
+ mProbesCompleted = probesCompleted;
+ mProbesSucceeded = probesSucceeded;
}
public String waitForRedirectUrl() {
@@ -1464,6 +1518,10 @@ public class ConnectivityServiceTest {
* received. assertNoCallback may be called at any time.
*/
private class TestNetworkCallback extends TestableNetworkCallback {
+ TestNetworkCallback() {
+ super(TEST_CALLBACK_TIMEOUT_MS);
+ }
+
@Override
public void assertNoCallback() {
// TODO: better support this use case in TestableNetworkCallback
@@ -1472,12 +1530,12 @@ public class ConnectivityServiceTest {
}
@Override
- public <T extends CallbackRecord> T expectCallback(final KClass<T> type, final HasNetwork n,
+ public <T extends CallbackEntry> T expectCallback(final KClass<T> type, final HasNetwork n,
final long timeoutMs) {
final T callback = super.expectCallback(type, n, timeoutMs);
- if (callback instanceof CallbackRecord.Losing) {
+ if (callback instanceof CallbackEntry.Losing) {
// TODO : move this to the specific test(s) needing this rather than here.
- final CallbackRecord.Losing losing = (CallbackRecord.Losing) callback;
+ final CallbackEntry.Losing losing = (CallbackEntry.Losing) callback;
final int maxMsToLive = losing.getMaxMsToLive();
String msg = String.format(
"Invalid linger time value %d, must be between %d and %d",
@@ -1538,16 +1596,16 @@ public class ConnectivityServiceTest {
cv = waitForConnectivityBroadcasts(2);
mWiFiNetworkAgent.disconnect();
- genericNetworkCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
- wifiNetworkCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
cellNetworkCallback.assertNoCallback();
waitFor(cv);
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
cv = waitForConnectivityBroadcasts(1);
mCellNetworkAgent.disconnect();
- genericNetworkCallback.expectCallback(CallbackRecord.LOST, mCellNetworkAgent);
- cellNetworkCallback.expectCallback(CallbackRecord.LOST, mCellNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
waitFor(cv);
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
@@ -1568,21 +1626,21 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- genericNetworkCallback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
genericNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
wifiNetworkCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
- cellNetworkCallback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
mWiFiNetworkAgent.disconnect();
- genericNetworkCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
- wifiNetworkCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
mCellNetworkAgent.disconnect();
- genericNetworkCallback.expectCallback(CallbackRecord.LOST, mCellNetworkAgent);
- cellNetworkCallback.expectCallback(CallbackRecord.LOST, mCellNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
}
@@ -1622,7 +1680,7 @@ public class ConnectivityServiceTest {
// We then get LOSING when wifi validates and cell is outscored.
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// TODO: Investigate sending validated before losing.
- callback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -1631,15 +1689,15 @@ public class ConnectivityServiceTest {
mEthernetNetworkAgent.connect(true);
callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
// TODO: Investigate sending validated before losing.
- callback.expectCallback(CallbackRecord.LOSING, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mWiFiNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
mEthernetNetworkAgent.disconnect();
- callback.expectCallback(CallbackRecord.LOST, mEthernetNetworkAgent);
- defaultCallback.expectCallback(CallbackRecord.LOST, mEthernetNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
@@ -1655,7 +1713,7 @@ public class ConnectivityServiceTest {
newNetwork = mWiFiNetworkAgent;
}
- callback.expectCallback(CallbackRecord.LOSING, oldNetwork);
+ callback.expectCallback(CallbackEntry.LOSING, oldNetwork);
// TODO: should we send an AVAILABLE callback to newNetwork, to indicate that it is no
// longer lingering?
defaultCallback.expectAvailableCallbacksValidated(newNetwork);
@@ -1669,7 +1727,7 @@ public class ConnectivityServiceTest {
// We expect a notification about the capabilities change, and nothing else.
defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED, mWiFiNetworkAgent);
defaultCallback.assertNoCallback();
- callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Wifi no longer satisfies our listen, which is for an unmetered network.
@@ -1678,11 +1736,11 @@ public class ConnectivityServiceTest {
// Disconnect our test networks.
mWiFiNetworkAgent.disconnect();
- defaultCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
mCellNetworkAgent.disconnect();
- defaultCallback.expectCallback(CallbackRecord.LOST, mCellNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
waitForIdle();
assertEquals(null, mCm.getActiveNetwork());
@@ -1713,8 +1771,8 @@ public class ConnectivityServiceTest {
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
- defaultCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
@@ -1725,15 +1783,15 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.adjustScore(50);
mWiFiNetworkAgent.connect(false); // Score: 70
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- callback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Tear down wifi.
mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
- defaultCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
@@ -1744,19 +1802,19 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.connect(true);
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// TODO: Investigate sending validated before losing.
- callback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
- defaultCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
mCellNetworkAgent.disconnect();
- callback.expectCallback(CallbackRecord.LOST, mCellNetworkAgent);
- defaultCallback.expectCallback(CallbackRecord.LOST, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
waitForIdle();
assertEquals(null, mCm.getActiveNetwork());
@@ -1771,7 +1829,7 @@ public class ConnectivityServiceTest {
defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
// TODO: Investigate sending validated before losing.
- callback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
@@ -1782,13 +1840,13 @@ public class ConnectivityServiceTest {
// TODO: should this cause an AVAILABLE callback, to indicate that the network is no longer
// lingering?
mCm.unregisterNetworkCallback(noopCallback);
- callback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
// Similar to the above: lingering can start even after the lingered request is removed.
// Disconnect wifi and switch to cell.
mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
- defaultCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
@@ -1807,12 +1865,12 @@ public class ConnectivityServiceTest {
callback.assertNoCallback();
// Now unregister cellRequest and expect cell to start lingering.
mCm.unregisterNetworkCallback(noopCallback);
- callback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
// Let linger run its course.
callback.assertNoCallback();
final int lingerTimeoutMs = mService.mLingerDelayMs + mService.mLingerDelayMs / 4;
- callback.expectCallback(CallbackRecord.LOST, mCellNetworkAgent, lingerTimeoutMs);
+ callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent, lingerTimeoutMs);
// Register a TRACK_DEFAULT request and check that it does not affect lingering.
TestNetworkCallback trackDefaultCallback = new TestNetworkCallback();
@@ -1821,20 +1879,20 @@ public class ConnectivityServiceTest {
mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
mEthernetNetworkAgent.connect(true);
callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
- callback.expectCallback(CallbackRecord.LOSING, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mWiFiNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
trackDefaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Let linger run its course.
- callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent, lingerTimeoutMs);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent, lingerTimeoutMs);
// Clean up.
mEthernetNetworkAgent.disconnect();
- callback.expectCallback(CallbackRecord.LOST, mEthernetNetworkAgent);
- defaultCallback.expectCallback(CallbackRecord.LOST, mEthernetNetworkAgent);
- trackDefaultCallback.expectCallback(CallbackRecord.LOST, mEthernetNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
+ trackDefaultCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
mCm.unregisterNetworkCallback(callback);
mCm.unregisterNetworkCallback(defaultCallback);
@@ -1864,7 +1922,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.connect(true);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- callback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
// File a request for cellular, then release it.
@@ -1873,7 +1931,7 @@ public class ConnectivityServiceTest {
NetworkCallback noopCallback = new NetworkCallback();
mCm.requestNetwork(cellRequest, noopCallback);
mCm.unregisterNetworkCallback(noopCallback);
- callback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
// Let linger run its course.
callback.assertNoCallback();
@@ -1917,12 +1975,12 @@ public class ConnectivityServiceTest {
// If the user chooses yes on the "No Internet access, stay connected?" dialog, we switch to
// wifi even though it's unvalidated.
mCm.setAcceptUnvalidated(mWiFiNetworkAgent.getNetwork(), true, false);
- callback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// Disconnect wifi, and then reconnect, again with explicitlySelected=true.
mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.explicitlySelected(true, false);
mWiFiNetworkAgent.connect(false);
@@ -1931,14 +1989,14 @@ public class ConnectivityServiceTest {
// If the user chooses no on the "No Internet access, stay connected?" dialog, we ask the
// network to disconnect.
mCm.setAcceptUnvalidated(mWiFiNetworkAgent.getNetwork(), false, false);
- callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
// Reconnect, again with explicitlySelected=true, but this time validate.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.explicitlySelected(true, false);
mWiFiNetworkAgent.connect(true);
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- callback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -1954,20 +2012,20 @@ public class ConnectivityServiceTest {
// (i.e., with explicitlySelected=true and acceptUnvalidated=true). Expect to switch to
// wifi immediately.
mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.explicitlySelected(true, true);
mWiFiNetworkAgent.connect(false);
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- callback.expectCallback(CallbackRecord.LOSING, mEthernetNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mEthernetNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mEthernetNetworkAgent.disconnect();
- callback.expectCallback(CallbackRecord.LOST, mEthernetNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent);
// Disconnect and reconnect with explicitlySelected=false and acceptUnvalidated=true.
// Check that the network is not scored specially and that the device prefers cell data.
mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.explicitlySelected(false, true);
mWiFiNetworkAgent.connect(false);
@@ -1978,8 +2036,8 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.disconnect();
mCellNetworkAgent.disconnect();
- callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
- callback.expectCallback(CallbackRecord.LOST, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
}
private int[] makeIntArray(final int size, final int value) {
@@ -2213,7 +2271,7 @@ public class ConnectivityServiceTest {
// With HTTPS probe disabled, NetworkMonitor should pass the network validation with http
// probe.
- mWiFiNetworkAgent.setNetworkPartialValid();
+ mWiFiNetworkAgent.setNetworkPartialValid(false /* isStrictMode */);
// If the user chooses yes to use this partial connectivity wifi, switch the default
// network to wifi and check if wifi becomes valid or not.
mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), true /* accept */,
@@ -2226,7 +2284,7 @@ public class ConnectivityServiceTest {
// Need a trigger point to let NetworkMonitor tell ConnectivityService that network is
// validated.
mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
- callback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
NetworkCapabilities nc = callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED,
mWiFiNetworkAgent);
assertTrue(nc.hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY));
@@ -2234,7 +2292,7 @@ public class ConnectivityServiceTest {
// Disconnect and reconnect wifi with partial connectivity again.
mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connectWithPartialConnectivity();
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
@@ -2246,7 +2304,7 @@ public class ConnectivityServiceTest {
// If the user chooses no, disconnect wifi immediately.
mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), false/* accept */,
false /* always */);
- callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
// If user accepted partial connectivity before, and device reconnects to that network
// again, but now the network has full connectivity. The network shouldn't contain
@@ -2262,14 +2320,14 @@ public class ConnectivityServiceTest {
// ConnectivityService#updateNetworkInfo().
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
- callback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
nc = callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
assertFalse(nc.hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY));
// Wifi should be the default network.
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
// The user accepted partial connectivity and selected "don't ask again". Now the user
// reconnects to the partial connectivity network. Switch to wifi as soon as partial
@@ -2283,17 +2341,17 @@ public class ConnectivityServiceTest {
// ConnectivityService#updateNetworkInfo().
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
- callback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent);
- mWiFiNetworkAgent.setNetworkValid();
+ mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */);
// Need a trigger point to let NetworkMonitor tell ConnectivityService that network is
// validated.
mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
// If the user accepted partial connectivity, and the device auto-reconnects to the partial
// connectivity network, it should contain both PARTIAL_CONNECTIVITY and VALIDATED.
@@ -2304,14 +2362,14 @@ public class ConnectivityServiceTest {
// NetworkMonitor will immediately (once the HTTPS probe fails...) report the network as
// valid, because ConnectivityService calls setAcceptPartialConnectivity before it calls
// notifyNetworkConnected.
- mWiFiNetworkAgent.connectWithPartialValidConnectivity();
+ mWiFiNetworkAgent.connectWithPartialValidConnectivity(false /* isStrictMode */);
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
- callback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(
NET_CAPABILITY_PARTIAL_CONNECTIVITY | NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
}
@Test
@@ -2330,7 +2388,7 @@ public class ConnectivityServiceTest {
// Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
String redirectUrl = "http://android.com/path";
- mWiFiNetworkAgent.connectWithCaptivePortal(redirectUrl);
+ mWiFiNetworkAgent.connectWithCaptivePortal(redirectUrl, false /* isStrictMode */);
captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), redirectUrl);
@@ -2348,12 +2406,12 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent);
// Report partial connectivity is accepted.
- mWiFiNetworkAgent.setNetworkPartialValid();
+ mWiFiNetworkAgent.setNetworkPartialValid(false /* isStrictMode */);
mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), true /* accept */,
false /* always */);
waitForIdle();
mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
- captivePortalCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
NetworkCapabilities nc =
validatedCallback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY,
@@ -2379,28 +2437,28 @@ public class ConnectivityServiceTest {
// Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
String firstRedirectUrl = "http://example.com/firstPath";
- mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
+ mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl, false /* isStrictMode */);
captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), firstRedirectUrl);
// Take down network.
// Expect onLost callback.
mWiFiNetworkAgent.disconnect();
- captivePortalCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
// Bring up a network with a captive portal.
// Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
String secondRedirectUrl = "http://example.com/secondPath";
- mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
+ mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl, false /* isStrictMode */);
captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), secondRedirectUrl);
// Make captive portal disappear then revalidate.
// Expect onLost callback because network no longer provides NET_CAPABILITY_CAPTIVE_PORTAL.
- mWiFiNetworkAgent.setNetworkValid();
+ mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */);
mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
- captivePortalCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
// Expect NET_CAPABILITY_VALIDATED onAvailable callback.
validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
@@ -2410,9 +2468,9 @@ public class ConnectivityServiceTest {
// Break network connectivity.
// Expect NET_CAPABILITY_VALIDATED onLost callback.
- mWiFiNetworkAgent.setNetworkInvalid();
+ mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */);
mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false);
- validatedCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ validatedCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
}
@Test
@@ -2441,10 +2499,10 @@ public class ConnectivityServiceTest {
mServiceContext.expectNoStartActivityIntent(fastTimeoutMs);
// Turn into a captive portal.
- mWiFiNetworkAgent.setNetworkPortal("http://example.com");
+ mWiFiNetworkAgent.setNetworkPortal("http://example.com", false /* isStrictMode */);
mCm.reportNetworkConnectivity(wifiNetwork, false);
captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- validatedCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ validatedCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
// Check that startCaptivePortalApp sends the expected command to NetworkMonitor.
mCm.startCaptivePortalApp(wifiNetwork);
@@ -2462,10 +2520,10 @@ public class ConnectivityServiceTest {
assertEquals(testValue, signInIntent.getStringExtra(testKey));
// Report that the captive portal is dismissed, and check that callbacks are fired
- mWiFiNetworkAgent.setNetworkValid();
+ mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */);
mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
- captivePortalCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
verify(mNotificationManager, times(1)).notifyAsUser(anyString(),
eq(NotificationType.LOGGED_IN.eventId), any(), eq(UserHandle.ALL));
@@ -2491,7 +2549,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
String firstRedirectUrl = "http://example.com/firstPath";
- mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
+ mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl, false /* isStrictMode */);
mWiFiNetworkAgent.expectDisconnected();
mWiFiNetworkAgent.expectPreventReconnectReceived();
@@ -2613,7 +2671,7 @@ public class ConnectivityServiceTest {
cFoo.assertNoCallback();
mWiFiNetworkAgent.setNetworkSpecifier(nsBar);
- cFoo.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ cFoo.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
cBar.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
for (TestNetworkCallback c: emptyCallbacks) {
c.expectCapabilitiesThat(mWiFiNetworkAgent,
@@ -2641,10 +2699,10 @@ public class ConnectivityServiceTest {
cBar.assertNoCallback();
mWiFiNetworkAgent.setNetworkSpecifier(null);
- cFoo.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
- cBar.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ cFoo.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ cBar.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
for (TestNetworkCallback c: emptyCallbacks) {
- c.expectCallback(CallbackRecord.NETWORK_CAPS_UPDATED, mWiFiNetworkAgent);
+ c.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mWiFiNetworkAgent);
}
assertNoCallbacks(cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo, cBar);
@@ -2787,7 +2845,7 @@ public class ConnectivityServiceTest {
// Bring down cell. Expect no default network callback, since it wasn't the default.
mCellNetworkAgent.disconnect();
- cellNetworkCallback.expectCallback(CallbackRecord.LOST, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
defaultNetworkCallback.assertNoCallback();
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
@@ -2802,11 +2860,11 @@ public class ConnectivityServiceTest {
// followed by AVAILABLE cell.
mWiFiNetworkAgent.disconnect();
cellNetworkCallback.assertNoCallback();
- defaultNetworkCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
defaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
mCellNetworkAgent.disconnect();
- cellNetworkCallback.expectCallback(CallbackRecord.LOST, mCellNetworkAgent);
- defaultNetworkCallback.expectCallback(CallbackRecord.LOST, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
waitForIdle();
assertEquals(null, mCm.getActiveNetwork());
@@ -2823,7 +2881,7 @@ public class ConnectivityServiceTest {
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
vpnNetworkAgent.disconnect();
- defaultNetworkCallback.expectCallback(CallbackRecord.LOST, vpnNetworkAgent);
+ defaultNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
waitForIdle();
assertEquals(null, mCm.getActiveNetwork());
}
@@ -2851,7 +2909,7 @@ public class ConnectivityServiceTest {
lp.setInterfaceName("foonet_data0");
mCellNetworkAgent.sendLinkProperties(lp);
// We should get onLinkPropertiesChanged().
- cellNetworkCallback.expectCallback(CallbackRecord.LINK_PROPERTIES_CHANGED,
+ cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
@@ -2859,7 +2917,7 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.suspend();
cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_SUSPENDED,
mCellNetworkAgent);
- cellNetworkCallback.expectCallback(CallbackRecord.SUSPENDED, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
// Register a garden variety default network request.
@@ -2874,7 +2932,7 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.resume();
cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_SUSPENDED,
mCellNetworkAgent);
- cellNetworkCallback.expectCallback(CallbackRecord.RESUMED, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.RESUMED, mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
dfltNetworkCallback = new TestNetworkCallback();
@@ -2937,10 +2995,10 @@ public class ConnectivityServiceTest {
// When wifi connects, cell lingers.
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- callback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
fgCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- fgCallback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
+ fgCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
fgCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
assertTrue(isForegroundNetwork(mCellNetworkAgent));
assertTrue(isForegroundNetwork(mWiFiNetworkAgent));
@@ -2948,7 +3006,7 @@ public class ConnectivityServiceTest {
// When lingering is complete, cell is still there but is now in the background.
waitForIdle();
int timeoutMs = TEST_LINGER_DELAY_MS + TEST_LINGER_DELAY_MS / 4;
- fgCallback.expectCallback(CallbackRecord.LOST, mCellNetworkAgent, timeoutMs);
+ fgCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent, timeoutMs);
// Expect a network capabilities update sans FOREGROUND.
callback.expectCapabilitiesWithout(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
assertFalse(isForegroundNetwork(mCellNetworkAgent));
@@ -2974,7 +3032,7 @@ public class ConnectivityServiceTest {
// Release the request. The network immediately goes into the background, since it was not
// lingering.
mCm.unregisterNetworkCallback(cellCallback);
- fgCallback.expectCallback(CallbackRecord.LOST, mCellNetworkAgent);
+ fgCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
// Expect a network capabilities update sans FOREGROUND.
callback.expectCapabilitiesWithout(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
assertFalse(isForegroundNetwork(mCellNetworkAgent));
@@ -2982,8 +3040,8 @@ public class ConnectivityServiceTest {
// Disconnect wifi and check that cell is foreground again.
mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
- fgCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ fgCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
fgCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertTrue(isForegroundNetwork(mCellNetworkAgent));
@@ -3120,7 +3178,7 @@ public class ConnectivityServiceTest {
testFactory.waitForNetworkRequests(1);
// ... and cell data to be torn down.
- cellNetworkCallback.expectCallback(CallbackRecord.LOST, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
assertLength(1, mCm.getAllNetworks());
testFactory.unregister();
@@ -3206,10 +3264,10 @@ public class ConnectivityServiceTest {
Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
// Fail validation on wifi.
- mWiFiNetworkAgent.setNetworkInvalid();
+ mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */);
mCm.reportNetworkConnectivity(wifiNetwork, false);
defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
- validatedWifiCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ validatedWifiCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
// Because avoid bad wifi is off, we don't switch to cellular.
defaultCallback.assertNoCallback();
@@ -3250,10 +3308,10 @@ public class ConnectivityServiceTest {
wifiNetwork = mWiFiNetworkAgent.getNetwork();
// Fail validation on wifi and expect the dialog to appear.
- mWiFiNetworkAgent.setNetworkInvalid();
+ mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */);
mCm.reportNetworkConnectivity(wifiNetwork, false);
defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
- validatedWifiCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ validatedWifiCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
// Simulate the user selecting "switch" and checking the don't ask again checkbox.
Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1);
@@ -3280,7 +3338,7 @@ public class ConnectivityServiceTest {
// If cell goes down, we switch to wifi.
mCellNetworkAgent.disconnect();
- defaultCallback.expectCallback(CallbackRecord.LOST, mCellNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
validatedWifiCallback.assertNoCallback();
@@ -3344,7 +3402,7 @@ public class ConnectivityServiceTest {
networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false,
TEST_CALLBACK_TIMEOUT_MS);
mWiFiNetworkAgent.disconnect();
- networkCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
// Validate that UNAVAILABLE is not called
networkCallback.assertNoCallback();
@@ -3364,7 +3422,7 @@ public class ConnectivityServiceTest {
mCm.requestNetwork(nr, networkCallback, timeoutMs);
// pass timeout and validate that UNAVAILABLE is called
- networkCallback.expectCallback(CallbackRecord.UNAVAILABLE, null);
+ networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, null);
// create a network satisfying request - validate that request not triggered
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
@@ -3455,7 +3513,7 @@ public class ConnectivityServiceTest {
// Simulate the factory releasing the request as unfulfillable and expect onUnavailable!
testFactory.triggerUnfulfillable(requests.get(newRequestId));
- networkCallback.expectCallback(CallbackRecord.UNAVAILABLE, null);
+ networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, null);
testFactory.waitForRequests();
// unregister network callback - a no-op (since already freed by the
@@ -4286,7 +4344,7 @@ public class ConnectivityServiceTest {
mCm.registerNetworkCallback(request, callback);
// Bring up wifi aware network.
- wifiAware.connect(false, false);
+ wifiAware.connect(false, false, false /* isStrictMode */);
callback.expectAvailableCallbacksUnvalidated(wifiAware);
assertNull(mCm.getActiveNetworkInfo());
@@ -4297,7 +4355,7 @@ public class ConnectivityServiceTest {
// Disconnect wifi aware network.
wifiAware.disconnect();
- callback.expectCallbackThat(TIMEOUT_MS, (info) -> info instanceof CallbackRecord.Lost);
+ callback.expectCallbackThat(TIMEOUT_MS, (info) -> info instanceof CallbackEntry.Lost);
mCm.unregisterNetworkCallback(callback);
verifyNoNetwork();
@@ -4315,16 +4373,16 @@ public class ConnectivityServiceTest {
assertFalse(mCm.isNetworkSupported(TYPE_NONE));
assertThrows(IllegalArgumentException.class,
- () -> { mCm.networkCapabilitiesForType(TYPE_NONE); });
+ () -> mCm.networkCapabilitiesForType(TYPE_NONE));
Class<UnsupportedOperationException> unsupported = UnsupportedOperationException.class;
- assertThrows(unsupported, () -> { mCm.startUsingNetworkFeature(TYPE_WIFI, ""); });
- assertThrows(unsupported, () -> { mCm.stopUsingNetworkFeature(TYPE_WIFI, ""); });
+ assertThrows(unsupported, () -> mCm.startUsingNetworkFeature(TYPE_WIFI, ""));
+ assertThrows(unsupported, () -> mCm.stopUsingNetworkFeature(TYPE_WIFI, ""));
// TODO: let test context have configuration application target sdk version
// and test that pre-M requesting for TYPE_NONE sends back APN_REQUEST_FAILED
- assertThrows(unsupported, () -> { mCm.startUsingNetworkFeature(TYPE_NONE, ""); });
- assertThrows(unsupported, () -> { mCm.stopUsingNetworkFeature(TYPE_NONE, ""); });
- assertThrows(unsupported, () -> { mCm.requestRouteToHostAddress(TYPE_NONE, null); });
+ assertThrows(unsupported, () -> mCm.startUsingNetworkFeature(TYPE_NONE, ""));
+ assertThrows(unsupported, () -> mCm.stopUsingNetworkFeature(TYPE_NONE, ""));
+ assertThrows(unsupported, () -> mCm.requestRouteToHostAddress(TYPE_NONE, null));
}
@Test
@@ -4346,12 +4404,12 @@ public class ConnectivityServiceTest {
// ConnectivityService.
TestNetworkAgentWrapper networkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp);
networkAgent.connect(true);
- networkCallback.expectCallback(CallbackRecord.AVAILABLE, networkAgent);
- networkCallback.expectCallback(CallbackRecord.NETWORK_CAPS_UPDATED, networkAgent);
- CallbackRecord.LinkPropertiesChanged cbi =
- networkCallback.expectCallback(CallbackRecord.LINK_PROPERTIES_CHANGED,
+ networkCallback.expectCallback(CallbackEntry.AVAILABLE, networkAgent);
+ networkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, networkAgent);
+ CallbackEntry.LinkPropertiesChanged cbi =
+ networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
networkAgent);
- networkCallback.expectCallback(CallbackRecord.BLOCKED_STATUS, networkAgent);
+ networkCallback.expectCallback(CallbackEntry.BLOCKED_STATUS, networkAgent);
networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, networkAgent);
networkCallback.assertNoCallback();
checkDirectlyConnectedRoutes(cbi.getLp(), Arrays.asList(myIpv4Address),
@@ -4366,7 +4424,7 @@ public class ConnectivityServiceTest {
newLp.addLinkAddress(myIpv6Address1);
newLp.addLinkAddress(myIpv6Address2);
networkAgent.sendLinkProperties(newLp);
- cbi = networkCallback.expectCallback(CallbackRecord.LINK_PROPERTIES_CHANGED, networkAgent);
+ cbi = networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, networkAgent);
networkCallback.assertNoCallback();
checkDirectlyConnectedRoutes(cbi.getLp(),
Arrays.asList(myIpv4Address, myIpv6Address1, myIpv6Address2),
@@ -4524,6 +4582,44 @@ public class ConnectivityServiceTest {
}
@Test
+ public void testPrivateDnsNotification() throws Exception {
+ NetworkRequest request = new NetworkRequest.Builder()
+ .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET)
+ .build();
+ TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(request, callback);
+ // Bring up wifi.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(false);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ // Private DNS resolution failed, checking if the notification will be shown or not.
+ mWiFiNetworkAgent.setNetworkInvalid(true /* isStrictMode */);
+ mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+ waitForIdle();
+ // If network validation failed, NetworkMonitor will re-evaluate the network.
+ // ConnectivityService should filter the redundant notification. This part is trying to
+ // simulate that situation and check if ConnectivityService could filter that case.
+ mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+ waitForIdle();
+ verify(mNotificationManager, timeout(TIMEOUT_MS).times(1)).notifyAsUser(anyString(),
+ eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), any(), eq(UserHandle.ALL));
+ // If private DNS resolution successful, the PRIVATE_DNS_BROKEN notification shouldn't be
+ // shown.
+ mWiFiNetworkAgent.setNetworkValid(true /* isStrictMode */);
+ mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+ waitForIdle();
+ verify(mNotificationManager, timeout(TIMEOUT_MS).times(1)).cancelAsUser(anyString(),
+ eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), eq(UserHandle.ALL));
+ // If private DNS resolution failed again, the PRIVATE_DNS_BROKEN notification should be
+ // shown again.
+ mWiFiNetworkAgent.setNetworkInvalid(true /* isStrictMode */);
+ mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+ waitForIdle();
+ verify(mNotificationManager, timeout(TIMEOUT_MS).times(2)).notifyAsUser(anyString(),
+ eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), any(), eq(UserHandle.ALL));
+ }
+
+ @Test
public void testPrivateDnsSettingsChange() throws Exception {
// Clear any interactions that occur as a result of CS starting up.
reset(mMockDnsResolver);
@@ -4572,12 +4668,12 @@ public class ConnectivityServiceTest {
assertTrue(ArrayUtils.containsAll(resolvrParams.tlsServers,
new String[] { "2001:db8::1", "192.0.2.1" }));
reset(mMockDnsResolver);
- cellNetworkCallback.expectCallback(CallbackRecord.AVAILABLE, mCellNetworkAgent);
- cellNetworkCallback.expectCallback(CallbackRecord.NETWORK_CAPS_UPDATED,
+ cellNetworkCallback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED,
mCellNetworkAgent);
- CallbackRecord.LinkPropertiesChanged cbi = cellNetworkCallback.expectCallback(
- CallbackRecord.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
- cellNetworkCallback.expectCallback(CallbackRecord.BLOCKED_STATUS, mCellNetworkAgent);
+ CallbackEntry.LinkPropertiesChanged cbi = cellNetworkCallback.expectCallback(
+ CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.BLOCKED_STATUS, mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
assertFalse(cbi.getLp().isPrivateDnsActive());
assertNull(cbi.getLp().getPrivateDnsServerName());
@@ -4608,7 +4704,7 @@ public class ConnectivityServiceTest {
setPrivateDnsSettings(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, "strict.example.com");
// Can't test dns configuration for strict mode without properly mocking
// out the DNS lookups, but can test that LinkProperties is updated.
- cbi = cellNetworkCallback.expectCallback(CallbackRecord.LINK_PROPERTIES_CHANGED,
+ cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
assertTrue(cbi.getLp().isPrivateDnsActive());
@@ -4631,12 +4727,12 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.sendLinkProperties(lp);
mCellNetworkAgent.connect(false);
waitForIdle();
- cellNetworkCallback.expectCallback(CallbackRecord.AVAILABLE, mCellNetworkAgent);
- cellNetworkCallback.expectCallback(CallbackRecord.NETWORK_CAPS_UPDATED,
+ cellNetworkCallback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED,
mCellNetworkAgent);
- CallbackRecord.LinkPropertiesChanged cbi = cellNetworkCallback.expectCallback(
- CallbackRecord.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
- cellNetworkCallback.expectCallback(CallbackRecord.BLOCKED_STATUS, mCellNetworkAgent);
+ CallbackEntry.LinkPropertiesChanged cbi = cellNetworkCallback.expectCallback(
+ CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackEntry.BLOCKED_STATUS, mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
assertFalse(cbi.getLp().isPrivateDnsActive());
assertNull(cbi.getLp().getPrivateDnsServerName());
@@ -4653,7 +4749,7 @@ public class ConnectivityServiceTest {
LinkProperties lp2 = new LinkProperties(lp);
lp2.addDnsServer(InetAddress.getByName("145.100.185.16"));
mCellNetworkAgent.sendLinkProperties(lp2);
- cbi = cellNetworkCallback.expectCallback(CallbackRecord.LINK_PROPERTIES_CHANGED,
+ cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
assertFalse(cbi.getLp().isPrivateDnsActive());
@@ -4677,7 +4773,7 @@ public class ConnectivityServiceTest {
// private dns fields should be sent.
mService.mNetdEventCallback.onPrivateDnsValidationEvent(
mCellNetworkAgent.getNetwork().netId, "145.100.185.16", "", true);
- cbi = cellNetworkCallback.expectCallback(CallbackRecord.LINK_PROPERTIES_CHANGED,
+ cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
assertTrue(cbi.getLp().isPrivateDnsActive());
@@ -4689,7 +4785,7 @@ public class ConnectivityServiceTest {
LinkProperties lp3 = new LinkProperties(lp2);
lp3.setMtu(1300);
mCellNetworkAgent.sendLinkProperties(lp3);
- cbi = cellNetworkCallback.expectCallback(CallbackRecord.LINK_PROPERTIES_CHANGED,
+ cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
assertTrue(cbi.getLp().isPrivateDnsActive());
@@ -4702,7 +4798,7 @@ public class ConnectivityServiceTest {
LinkProperties lp4 = new LinkProperties(lp3);
lp4.removeDnsServer(InetAddress.getByName("145.100.185.16"));
mCellNetworkAgent.sendLinkProperties(lp4);
- cbi = cellNetworkCallback.expectCallback(CallbackRecord.LINK_PROPERTIES_CHANGED,
+ cbi = cellNetworkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED,
mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
assertFalse(cbi.getLp().isPrivateDnsActive());
@@ -4780,7 +4876,7 @@ public class ConnectivityServiceTest {
// by NetworkMonitor
assertFalse(NetworkMonitorUtils.isValidationRequired(
vpnNetworkAgent.getNetworkCapabilities()));
- vpnNetworkAgent.setNetworkValid();
+ vpnNetworkAgent.setNetworkValid(false /* isStrictMode */);
vpnNetworkAgent.connect(false);
mMockVpn.connect();
@@ -4793,19 +4889,19 @@ public class ConnectivityServiceTest {
defaultCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- genericNetworkCallback.expectCallback(CallbackRecord.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
genericNotVpnNetworkCallback.assertNoCallback();
vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, nc -> null == nc.getUids());
- defaultCallback.expectCallback(CallbackRecord.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
ranges.clear();
vpnNetworkAgent.setUids(ranges);
- genericNetworkCallback.expectCallback(CallbackRecord.LOST, vpnNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
- vpnNetworkCallback.expectCallback(CallbackRecord.LOST, vpnNetworkAgent);
+ vpnNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
// TODO : The default network callback should actually get a LOST call here (also see the
// comment below for AVAILABLE). This is because ConnectivityService does not look at UID
@@ -4813,7 +4909,7 @@ public class ConnectivityServiceTest {
// can't currently update their UIDs without disconnecting, so this does not matter too
// much, but that is the reason the test here has to check for an update to the
// capabilities instead of the expected LOST then AVAILABLE.
- defaultCallback.expectCallback(CallbackRecord.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
ranges.add(new UidRange(uid, uid));
mMockVpn.setUids(ranges);
@@ -4825,23 +4921,23 @@ public class ConnectivityServiceTest {
vpnNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
// TODO : Here like above, AVAILABLE would be correct, but because this can't actually
// happen outside of the test, ConnectivityService does not rematch callbacks.
- defaultCallback.expectCallback(CallbackRecord.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
mWiFiNetworkAgent.disconnect();
- genericNetworkCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
- genericNotVpnNetworkCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
- wifiNetworkCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ genericNotVpnNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
vpnNetworkCallback.assertNoCallback();
defaultCallback.assertNoCallback();
vpnNetworkAgent.disconnect();
- genericNetworkCallback.expectCallback(CallbackRecord.LOST, vpnNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
- vpnNetworkCallback.expectCallback(CallbackRecord.LOST, vpnNetworkAgent);
- defaultCallback.expectCallback(CallbackRecord.LOST, vpnNetworkAgent);
+ vpnNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
assertEquals(null, mCm.getActiveNetwork());
mCm.unregisterNetworkCallback(genericNetworkCallback);
@@ -4869,7 +4965,8 @@ public class ConnectivityServiceTest {
ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */);
+ vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+ false /* isStrictMode */);
mMockVpn.connect();
defaultCallback.assertNoCallback();
@@ -4900,14 +4997,15 @@ public class ConnectivityServiceTest {
ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */);
+ vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */,
+ false /* isStrictMode */);
mMockVpn.connect();
defaultCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
vpnNetworkAgent.disconnect();
- defaultCallback.expectCallback(CallbackRecord.LOST, vpnNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
mCm.unregisterNetworkCallback(defaultCallback);
@@ -4932,13 +5030,14 @@ public class ConnectivityServiceTest {
ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */);
+ vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */,
+ false /* isStrictMode */);
mMockVpn.connect();
// Even though the VPN is unvalidated, it becomes the default network for our app.
callback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
// TODO: this looks like a spurious callback.
- callback.expectCallback(CallbackRecord.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+ callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
callback.assertNoCallback();
assertTrue(vpnNetworkAgent.getScore() > mEthernetNetworkAgent.getScore());
@@ -4955,7 +5054,7 @@ public class ConnectivityServiceTest {
vpnNetworkAgent.getNetworkCapabilities()));
// Pretend that the VPN network validates.
- vpnNetworkAgent.setNetworkValid();
+ vpnNetworkAgent.setNetworkValid(false /* isStrictMode */);
vpnNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
// Expect to see the validated capability, but no other changes, because the VPN is already
// the default network for the app.
@@ -4963,7 +5062,7 @@ public class ConnectivityServiceTest {
callback.assertNoCallback();
vpnNetworkAgent.disconnect();
- callback.expectCallback(CallbackRecord.LOST, vpnNetworkAgent);
+ callback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
callback.expectAvailableCallbacksValidated(mEthernetNetworkAgent);
}
@@ -4987,7 +5086,8 @@ public class ConnectivityServiceTest {
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.connect();
mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */);
+ vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+ false /* isStrictMode */);
vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
@@ -5086,7 +5186,8 @@ public class ConnectivityServiceTest {
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.connect();
mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */);
+ vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+ false /* isStrictMode */);
vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
@@ -5405,7 +5506,7 @@ public class ConnectivityServiceTest {
// Switch to METERED network. Restrict the use of the network.
mWiFiNetworkAgent.disconnect();
- defaultCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksValidatedAndBlocked(mCellNetworkAgent);
// Network becomes NOT_METERED.
@@ -5419,7 +5520,7 @@ public class ConnectivityServiceTest {
defaultCallback.assertNoCallback();
mCellNetworkAgent.disconnect();
- defaultCallback.expectCallback(CallbackRecord.LOST, mCellNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
defaultCallback.assertNoCallback();
mCm.unregisterNetworkCallback(defaultCallback);
@@ -5495,7 +5596,7 @@ public class ConnectivityServiceTest {
// the NAT64 prefix was removed because one was never discovered.
cellLp.addLinkAddress(myIpv4);
mCellNetworkAgent.sendLinkProperties(cellLp);
- networkCallback.expectCallback(CallbackRecord.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
verify(mMockDnsResolver, times(1)).stopPrefix64Discovery(cellNetId);
verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(any());
@@ -5508,7 +5609,7 @@ public class ConnectivityServiceTest {
cellLp.removeLinkAddress(myIpv4);
cellLp.removeRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
mCellNetworkAgent.sendLinkProperties(cellLp);
- networkCallback.expectCallback(CallbackRecord.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
// When NAT64 prefix discovery succeeds, LinkProperties are updated and clatd is started.
@@ -5517,14 +5618,14 @@ public class ConnectivityServiceTest {
mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */,
kNat64PrefixString, 96);
LinkProperties lpBeforeClat = networkCallback.expectCallback(
- CallbackRecord.LINK_PROPERTIES_CHANGED, mCellNetworkAgent).getLp();
+ CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent).getLp();
assertEquals(0, lpBeforeClat.getStackedLinks().size());
assertEquals(kNat64Prefix, lpBeforeClat.getNat64Prefix());
verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString());
// Clat iface comes up. Expect stacked link to be added.
clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true);
- networkCallback.expectCallback(CallbackRecord.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
List<LinkProperties> stackedLps = mCm.getLinkProperties(mCellNetworkAgent.getNetwork())
.getStackedLinks();
assertEquals(makeClatLinkProperties(myIpv4), stackedLps.get(0));
@@ -5532,7 +5633,7 @@ public class ConnectivityServiceTest {
// Change trivial linkproperties and see if stacked link is preserved.
cellLp.addDnsServer(InetAddress.getByName("8.8.8.8"));
mCellNetworkAgent.sendLinkProperties(cellLp);
- networkCallback.expectCallback(CallbackRecord.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
List<LinkProperties> stackedLpsAfterChange =
mCm.getLinkProperties(mCellNetworkAgent.getNetwork()).getStackedLinks();
@@ -5550,12 +5651,12 @@ public class ConnectivityServiceTest {
cellLp.addLinkAddress(myIpv4);
cellLp.addRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
mCellNetworkAgent.sendLinkProperties(cellLp);
- networkCallback.expectCallback(CallbackRecord.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
verify(mMockDnsResolver, times(1)).stopPrefix64Discovery(cellNetId);
// As soon as stop is called, the linkproperties lose the stacked interface.
- networkCallback.expectCallback(CallbackRecord.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
LinkProperties actualLpAfterIpv4 = mCm.getLinkProperties(mCellNetworkAgent.getNetwork());
LinkProperties expected = new LinkProperties(cellLp);
expected.setNat64Prefix(kNat64Prefix);
@@ -5582,11 +5683,11 @@ public class ConnectivityServiceTest {
cellLp.removeRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
cellLp.removeDnsServer(InetAddress.getByName("8.8.8.8"));
mCellNetworkAgent.sendLinkProperties(cellLp);
- networkCallback.expectCallback(CallbackRecord.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */,
kNat64PrefixString, 96);
- networkCallback.expectCallback(CallbackRecord.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString());
@@ -5606,7 +5707,7 @@ public class ConnectivityServiceTest {
// Clean up.
mCellNetworkAgent.disconnect();
- networkCallback.expectCallback(CallbackRecord.LOST, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
networkCallback.assertNoCallback();
mCm.unregisterNetworkCallback(networkCallback);
}
@@ -5638,7 +5739,7 @@ public class ConnectivityServiceTest {
reset(mNetworkManagementService);
mWiFiNetworkAgent.connect(true);
networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- networkCallback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
verify(mNetworkManagementService, times(1)).addIdleTimer(eq(WIFI_IFNAME), anyInt(),
eq(ConnectivityManager.TYPE_WIFI));
@@ -5647,7 +5748,7 @@ public class ConnectivityServiceTest {
// Disconnect wifi and switch back to cell
reset(mNetworkManagementService);
mWiFiNetworkAgent.disconnect();
- networkCallback.expectCallback(CallbackRecord.LOST, mWiFiNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
assertNoCallbacks(networkCallback);
verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME));
verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(),
@@ -5659,14 +5760,14 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.sendLinkProperties(wifiLp);
mWiFiNetworkAgent.connect(true);
networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- networkCallback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
// Disconnect cell
reset(mNetworkManagementService);
reset(mMockNetd);
mCellNetworkAgent.disconnect();
- networkCallback.expectCallback(CallbackRecord.LOST, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
// LOST callback is triggered earlier than removing idle timer. Broadcast should also be
// sent as network being switched. Ensure rule removal for cell will not be triggered
// unexpectedly before network being removed.
@@ -5696,7 +5797,6 @@ public class ConnectivityServiceTest {
}
@Test
- @FlakyTest(bugId = 140305678)
public void testTcpBufferReset() throws Exception {
final String testTcpBufferSizes = "1,2,3,4,5,6";
final NetworkRequest networkRequest = new NetworkRequest.Builder()
@@ -5717,12 +5817,12 @@ public class ConnectivityServiceTest {
LinkProperties lp = new LinkProperties();
lp.setTcpBufferSizes(testTcpBufferSizes);
mCellNetworkAgent.sendLinkProperties(lp);
- networkCallback.expectCallback(CallbackRecord.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
verifyTcpBufferSizeChange(testTcpBufferSizes);
// Clean up.
mCellNetworkAgent.disconnect();
- networkCallback.expectCallback(CallbackRecord.LOST, mCellNetworkAgent);
+ networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
networkCallback.assertNoCallback();
mCm.unregisterNetworkCallback(networkCallback);
}
@@ -5941,6 +6041,24 @@ public class ConnectivityServiceTest {
assertContainsExactly(uidCaptor.getValue(), APP2_UID);
}
+ @Test
+ public void testLinkPropertiesWithWakeOnLanForActiveNetwork() throws Exception {
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+
+ LinkProperties wifiLp = new LinkProperties();
+ wifiLp.setInterfaceName(WIFI_WOL_IFNAME);
+ wifiLp.setWakeOnLanSupported(false);
+
+ // Default network switch should update ifaces.
+ mWiFiNetworkAgent.connect(false);
+ mWiFiNetworkAgent.sendLinkProperties(wifiLp);
+ waitForIdle();
+
+ // ConnectivityService should have changed the WakeOnLanSupported to true
+ wifiLp.setWakeOnLanSupported(true);
+ assertEquals(wifiLp, mService.getActiveLinkProperties());
+ }
+
private TestNetworkAgentWrapper establishVpn(LinkProperties lp, int establishingUid,
Set<UidRange> vpnRange) throws Exception {
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index cd2bd26ef4bb..3fdba6eac55d 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -54,7 +54,6 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageList;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
@@ -70,6 +69,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.LocalServices;
+import com.android.server.pm.PackageList;
import org.junit.Before;
import org.junit.Test;
@@ -561,11 +561,17 @@ public class PermissionMonitorTest {
mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{SYSTEM_UID1});
}
- private PackageInfo addPackage(String packageName, int uid, String[] permissions)
+ private PackageInfo setPackagePermissions(String packageName, int uid, String[] permissions)
throws Exception {
PackageInfo packageInfo = packageInfoWithPermissions(permissions, PARTITION_SYSTEM);
when(mPackageManager.getPackageInfo(eq(packageName), anyInt())).thenReturn(packageInfo);
when(mPackageManager.getPackagesForUid(eq(uid))).thenReturn(new String[]{packageName});
+ return packageInfo;
+ }
+
+ private PackageInfo addPackage(String packageName, int uid, String[] permissions)
+ throws Exception {
+ PackageInfo packageInfo = setPackagePermissions(packageName, uid, permissions);
mObserver.onPackageAdded(packageName, uid);
return packageInfo;
}
@@ -616,14 +622,13 @@ public class PermissionMonitorTest {
}
@Test
- public void testPackageUpdate() throws Exception {
+ public void testPackageRemoveThenAdd() throws Exception {
final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS});
mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
| INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
- // Remove and install the same package to simulate the update action
when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{});
mObserver.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[]{MOCK_UID1});
@@ -633,6 +638,20 @@ public class PermissionMonitorTest {
}
@Test
+ public void testPackageUpdate() throws Exception {
+ final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
+
+ 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);
+ mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1});
+ }
+
+ @Test
public void testPackageUninstallWithMultiplePackages() throws Exception {
final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index c030c3e9ac86..5f62c08f55f3 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -25,8 +25,10 @@ import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER;
import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER;
import static android.net.ConnectivityManager.TETHERING_USB;
import static android.net.ConnectivityManager.TETHERING_WIFI;
+import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
@@ -90,6 +92,9 @@ import android.net.util.NetworkConstants;
import android.net.util.SharedLog;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
+import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.WifiP2pInfo;
+import android.net.wifi.p2p.WifiP2pManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.INetworkManagementService;
@@ -140,6 +145,7 @@ public class TetheringTest {
private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0";
private static final String TEST_USB_IFNAME = "test_rndis0";
private static final String TEST_WLAN_IFNAME = "test_wlan0";
+ private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0";
private static final int DHCPSERVER_START_TIMEOUT_MS = 1000;
@@ -216,9 +222,10 @@ public class TetheringTest {
assertTrue("Non-mocked interface " + ifName,
ifName.equals(TEST_USB_IFNAME)
|| ifName.equals(TEST_WLAN_IFNAME)
- || ifName.equals(TEST_MOBILE_IFNAME));
+ || ifName.equals(TEST_MOBILE_IFNAME)
+ || ifName.equals(TEST_P2P_IFNAME));
final String[] ifaces = new String[] {
- TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME };
+ TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME, TEST_P2P_IFNAME};
return new InterfaceParams(ifName, ArrayUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET,
MacAddress.ALL_ZEROS_ADDRESS);
}
@@ -361,6 +368,8 @@ public class TetheringTest {
.thenReturn(new String[] { "test_rndis\\d" });
when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs))
.thenReturn(new String[]{ "test_wlan\\d" });
+ when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_p2p_regexs))
+ .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" });
when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs))
.thenReturn(new String[0]);
when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
@@ -369,7 +378,7 @@ public class TetheringTest {
.thenReturn(false);
when(mNMService.listInterfaces())
.thenReturn(new String[] {
- TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME});
+ TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME});
when(mNMService.getInterfaceConfig(anyString()))
.thenReturn(new InterfaceConfiguration());
when(mRouterAdvertisementDaemon.start())
@@ -423,6 +432,31 @@ public class TetheringTest {
mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
+ private static final String[] P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST = {
+ android.Manifest.permission.ACCESS_FINE_LOCATION,
+ android.Manifest.permission.ACCESS_WIFI_STATE
+ };
+
+ private void sendWifiP2pConnectionChanged(
+ boolean isGroupFormed, boolean isGroupOwner, String ifname) {
+ WifiP2pInfo p2pInfo = new WifiP2pInfo();
+ p2pInfo.groupFormed = isGroupFormed;
+ p2pInfo.isGroupOwner = isGroupOwner;
+
+ NetworkInfo networkInfo = new NetworkInfo(TYPE_WIFI_P2P, 0, null, null);
+
+ WifiP2pGroup group = new WifiP2pGroup();
+ group.setIsGroupOwner(isGroupOwner);
+ group.setInterface(ifname);
+
+ final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
+ intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, p2pInfo);
+ intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, networkInfo);
+ intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, group);
+ mServiceContext.sendBroadcastAsUserMultiplePermissions(intent, UserHandle.ALL,
+ P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST);
+ }
+
private void sendUsbBroadcast(boolean connected, boolean configured, boolean rndisFunction) {
final Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
intent.putExtra(USB_CONNECTED, connected);
@@ -436,11 +470,11 @@ public class TetheringTest {
mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
- private void verifyInterfaceServingModeStarted() throws Exception {
- verify(mNMService, times(1)).getInterfaceConfig(TEST_WLAN_IFNAME);
+ private void verifyInterfaceServingModeStarted(String ifname) throws Exception {
+ verify(mNMService, times(1)).getInterfaceConfig(ifname);
verify(mNMService, times(1))
- .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
- verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME);
+ .setInterfaceConfig(eq(ifname), any(InterfaceConfiguration.class));
+ verify(mNMService, times(1)).tetherInterface(ifname);
}
private void verifyTetheringBroadcast(String ifname, String whichExtra) {
@@ -530,7 +564,7 @@ public class TetheringTest {
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_LOCAL_ONLY);
mLooper.dispatchAll();
- verifyInterfaceServingModeStarted();
+ verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
verify(mNMService, times(1)).setIpForwardingEnabled(true);
verify(mNMService, times(1)).startTethering(any(String[].class));
@@ -542,8 +576,9 @@ public class TetheringTest {
verifyNoMoreInteractions(mWifiManager);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks();
- // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
- assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls);
+ // This will be called twice, one is on entering IpServer.STATE_AVAILABLE,
+ // and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY.
+ assertEquals(2, mTetheringDependencies.isTetheringSupportedCalls);
// Emulate externally-visible WifiManager effects, when hotspot mode
// is being torn down.
@@ -552,9 +587,9 @@ public class TetheringTest {
mLooper.dispatchAll();
verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME);
- // TODO: Why is {g,s}etInterfaceConfig() called more than once?
- verify(mNMService, atLeastOnce()).getInterfaceConfig(TEST_WLAN_IFNAME);
- verify(mNMService, atLeastOnce())
+ // {g,s}etInterfaceConfig() called twice for enabling and disabling IPv4.
+ verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME);
+ verify(mNMService, times(2))
.setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
verify(mNMService, times(1)).stopTethering();
verify(mNMService, times(1)).setIpForwardingEnabled(false);
@@ -770,7 +805,7 @@ public class TetheringTest {
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
mLooper.dispatchAll();
- verifyInterfaceServingModeStarted();
+ verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
verify(mNMService, times(1)).setIpForwardingEnabled(true);
verify(mNMService, times(1)).startTethering(any(String[].class));
@@ -785,8 +820,9 @@ public class TetheringTest {
// In tethering mode, in the default configuration, an explicit request
// for a mobile network is also made.
verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest();
- // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
- assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls);
+ // This will be called twice, one is on entering IpServer.STATE_AVAILABLE,
+ // and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY.
+ assertEquals(2, mTetheringDependencies.isTetheringSupportedCalls);
/////
// We do not currently emulate any upstream being found.
@@ -809,7 +845,7 @@ public class TetheringTest {
mLooper.dispatchAll();
verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME);
- // TODO: Why is {g,s}etInterfaceConfig() called more than once?
+ // {g,s}etInterfaceConfig() called twice for enabling and disabling IPv4.
verify(mNMService, atLeastOnce()).getInterfaceConfig(TEST_WLAN_IFNAME);
verify(mNMService, atLeastOnce())
.setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
@@ -857,8 +893,9 @@ public class TetheringTest {
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
verify(mWifiManager).updateInterfaceIpState(
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
- // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
- assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls);
+ // There are 3 state change event:
+ // AVAILABLE -> STATE_TETHERED -> STATE_AVAILABLE.
+ assertEquals(3, mTetheringDependencies.isTetheringSupportedCalls);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
// This is called, but will throw.
verify(mNMService, times(1)).setIpForwardingEnabled(true);
@@ -1031,6 +1068,133 @@ public class TetheringTest {
assertEquals(fakeSubId, newConfig.subId);
}
+ private void workingWifiP2pGroupOwner(
+ boolean emulateInterfaceStatusChanged) throws Exception {
+ if (emulateInterfaceStatusChanged) {
+ mTethering.interfaceStatusChanged(TEST_P2P_IFNAME, true);
+ }
+ sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME);
+ mLooper.dispatchAll();
+
+ verifyInterfaceServingModeStarted(TEST_P2P_IFNAME);
+ verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_AVAILABLE_TETHER);
+ verify(mNMService, times(1)).setIpForwardingEnabled(true);
+ verify(mNMService, times(1)).startTethering(any(String[].class));
+ verifyNoMoreInteractions(mNMService);
+ verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
+ verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks();
+ // This will be called twice, one is on entering IpServer.STATE_AVAILABLE,
+ // and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY.
+ assertEquals(2, mTetheringDependencies.isTetheringSupportedCalls);
+
+ assertEquals(TETHER_ERROR_NO_ERROR, mTethering.getLastTetherError(TEST_P2P_IFNAME));
+
+ // Emulate externally-visible WifiP2pManager effects, when wifi p2p group
+ // is being removed.
+ sendWifiP2pConnectionChanged(false, true, TEST_P2P_IFNAME);
+ mTethering.interfaceRemoved(TEST_P2P_IFNAME);
+ mLooper.dispatchAll();
+
+ verify(mNMService, times(1)).untetherInterface(TEST_P2P_IFNAME);
+ // {g,s}etInterfaceConfig() called twice for enabling and disabling IPv4.
+ verify(mNMService, times(2)).getInterfaceConfig(TEST_P2P_IFNAME);
+ verify(mNMService, times(2))
+ .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class));
+ verify(mNMService, times(1)).stopTethering();
+ verify(mNMService, times(1)).setIpForwardingEnabled(false);
+ verify(mUpstreamNetworkMonitor, never()).getCurrentPreferredUpstream();
+ verify(mUpstreamNetworkMonitor, never()).selectPreferredUpstreamType(any());
+ verifyNoMoreInteractions(mNMService);
+ // Asking for the last error after the per-interface state machine
+ // has been reaped yields an unknown interface error.
+ assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME));
+ }
+
+ private void workingWifiP2pGroupClient(
+ boolean emulateInterfaceStatusChanged) throws Exception {
+ if (emulateInterfaceStatusChanged) {
+ mTethering.interfaceStatusChanged(TEST_P2P_IFNAME, true);
+ }
+ sendWifiP2pConnectionChanged(true, false, TEST_P2P_IFNAME);
+ mLooper.dispatchAll();
+
+ verify(mNMService, never()).getInterfaceConfig(TEST_P2P_IFNAME);
+ verify(mNMService, never())
+ .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class));
+ verify(mNMService, never()).tetherInterface(TEST_P2P_IFNAME);
+ verify(mNMService, never()).setIpForwardingEnabled(true);
+ verify(mNMService, never()).startTethering(any(String[].class));
+
+ // Emulate externally-visible WifiP2pManager effects, when wifi p2p group
+ // is being removed.
+ sendWifiP2pConnectionChanged(false, false, TEST_P2P_IFNAME);
+ mTethering.interfaceRemoved(TEST_P2P_IFNAME);
+ mLooper.dispatchAll();
+
+ verify(mNMService, never()).untetherInterface(TEST_P2P_IFNAME);
+ verify(mNMService, never()).getInterfaceConfig(TEST_P2P_IFNAME);
+ verify(mNMService, never())
+ .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class));
+ verify(mNMService, never()).stopTethering();
+ verify(mNMService, never()).setIpForwardingEnabled(false);
+ verifyNoMoreInteractions(mNMService);
+ // Asking for the last error after the per-interface state machine
+ // has been reaped yields an unknown interface error.
+ assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME));
+ }
+
+ @Test
+ public void workingWifiP2pGroupOwnerWithIfaceChanged() throws Exception {
+ workingWifiP2pGroupOwner(true);
+ }
+
+ @Test
+ public void workingWifiP2pGroupOwnerSansIfaceChanged() throws Exception {
+ workingWifiP2pGroupOwner(false);
+ }
+
+ private void workingWifiP2pGroupOwnerLegacyMode(
+ boolean emulateInterfaceStatusChanged) throws Exception {
+ // change to legacy mode and update tethering information by chaning SIM
+ when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_p2p_regexs))
+ .thenReturn(new String[]{});
+ final int fakeSubId = 1234;
+ mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId);
+
+ if (emulateInterfaceStatusChanged) {
+ mTethering.interfaceStatusChanged(TEST_P2P_IFNAME, true);
+ }
+ sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME);
+ mLooper.dispatchAll();
+
+ verify(mNMService, never()).getInterfaceConfig(TEST_P2P_IFNAME);
+ verify(mNMService, never())
+ .setInterfaceConfig(eq(TEST_P2P_IFNAME), any(InterfaceConfiguration.class));
+ verify(mNMService, never()).tetherInterface(TEST_P2P_IFNAME);
+ verify(mNMService, never()).setIpForwardingEnabled(true);
+ verify(mNMService, never()).startTethering(any(String[].class));
+ assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME));
+ }
+ @Test
+ public void workingWifiP2pGroupOwnerLegacyModeWithIfaceChanged() throws Exception {
+ workingWifiP2pGroupOwnerLegacyMode(true);
+ }
+
+ @Test
+ public void workingWifiP2pGroupOwnerLegacyModeSansIfaceChanged() throws Exception {
+ workingWifiP2pGroupOwnerLegacyMode(false);
+ }
+
+ @Test
+ public void workingWifiP2pGroupClientWithIfaceChanged() throws Exception {
+ workingWifiP2pGroupClient(true);
+ }
+
+ @Test
+ public void workingWifiP2pGroupClientSansIfaceChanged() throws Exception {
+ workingWifiP2pGroupClient(false);
+ }
+
// TODO: Test that a request for hotspot mode doesn't interfere with an
// already operating tethering mode interface.
}
diff --git a/tests/utils/testutils/java/android/os/test/TestLooper.java b/tests/utils/testutils/java/android/os/test/TestLooper.java
index a49eda3d86d0..01bd47b9c608 100644
--- a/tests/utils/testutils/java/android/os/test/TestLooper.java
+++ b/tests/utils/testutils/java/android/os/test/TestLooper.java
@@ -210,33 +210,36 @@ public class TestLooper {
/**
* Run method for the auto dispatch thread.
* The thread loops a maximum of MAX_LOOPS times with a 10ms sleep between loops.
- * The thread continues looping and attempting to dispatch all messages until at
- * least one message has been dispatched.
+ * The thread continues looping and attempting to dispatch all messages until
+ * {@link #stopAutoDispatch()} has been invoked.
*/
@Override
public void run() {
int dispatchCount = 0;
for (int i = 0; i < MAX_LOOPS; i++) {
try {
- dispatchCount = dispatchAll();
+ dispatchCount += dispatchAll();
} catch (RuntimeException e) {
mAutoDispatchException = e;
- }
- Log.d(TAG, "dispatched " + dispatchCount + " messages");
- if (dispatchCount > 0) {
return;
}
+ Log.d(TAG, "dispatched " + dispatchCount + " messages");
try {
Thread.sleep(LOOP_SLEEP_TIME_MS);
} catch (InterruptedException e) {
- mAutoDispatchException = new IllegalStateException(
- "stopAutoDispatch called before any messages were dispatched.");
+ if (dispatchCount == 0) {
+ Log.e(TAG, "stopAutoDispatch called before any messages were dispatched.");
+ mAutoDispatchException = new IllegalStateException(
+ "stopAutoDispatch called before any messages were dispatched.");
+ }
return;
}
}
- Log.e(TAG, "AutoDispatchThread did not dispatch any messages.");
- mAutoDispatchException = new IllegalStateException(
- "TestLooper did not dispatch any messages before exiting.");
+ if (dispatchCount == 0) {
+ Log.e(TAG, "AutoDispatchThread did not dispatch any messages.");
+ mAutoDispatchException = new IllegalStateException(
+ "TestLooper did not dispatch any messages before exiting.");
+ }
}
/**
@@ -287,4 +290,17 @@ public class TestLooper {
"stopAutoDispatch called without startAutoDispatch.");
}
}
+
+ /**
+ * If an AutoDispatchThread is currently running, stop and clean up.
+ * This method ignores exceptions raised for indicating that no messages were dispatched.
+ */
+ public void stopAutoDispatchAndIgnoreExceptions() {
+ try {
+ stopAutoDispatch();
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "stopAutoDispatch", e);
+ }
+
+ }
}
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index c9e3404e0f1a..957216e17925 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -41,6 +41,7 @@ public final class FrameworksTestsFilter extends SelectTest {
"android.view.DisplayCutoutTest",
"android.view.InsetsAnimationControlImplTest",
"android.view.InsetsControllerTest",
+ "android.view.InsetsFlagsTest",
"android.view.InsetsSourceTest",
"android.view.InsetsSourceConsumerTest",
"android.view.InsetsStateTest",
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 05375b0cb871..21386b88ce2c 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -787,7 +787,9 @@ int doDump(Bundle* bundle)
// The dynamicRefTable can be null if there are no resources for this asset cookie.
// This fine.
- const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
+ auto noop_destructor = [](const DynamicRefTable* /*ref_table */) { };
+ auto dynamicRefTable = std::shared_ptr<const DynamicRefTable>(
+ res.getDynamicRefTableForCookie(assetsCookie), noop_destructor);
Asset* asset = NULL;
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 7966ba27ebd8..8a43bb4ede35 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -169,17 +169,12 @@ int MainImpl(int argc, char** argv) {
aapt::text::Printer printer(&fout);
aapt::StdErrDiagnostics diagnostics;
- auto main_command = new aapt::MainCommand(&printer, &diagnostics);
+ aapt::MainCommand main_command(&printer, &diagnostics);
// Add the daemon subcommand here so it cannot be called while executing the daemon
- main_command->AddOptionalSubcommand(
+ main_command.AddOptionalSubcommand(
aapt::util::make_unique<aapt::DaemonCommand>(&fout, &diagnostics));
- return main_command->Execute(args, &std::cerr);
-}
-
-// TODO(b/141312058) stop leaks
-extern "C" const char *__asan_default_options() {
- return "detect_leaks=0";
+ return main_command.Execute(args, &std::cerr);
}
int main(int argc, char** argv) {
diff --git a/tools/aapt2/format/Container.cpp b/tools/aapt2/format/Container.cpp
index f1890488276c..9cef7b3d2ce3 100644
--- a/tools/aapt2/format/Container.cpp
+++ b/tools/aapt2/format/Container.cpp
@@ -30,6 +30,7 @@ namespace aapt {
constexpr const static uint32_t kContainerFormatMagic = 0x54504141u;
constexpr const static uint32_t kContainerFormatVersion = 1u;
+constexpr const static size_t kPaddingAlignment = 4u;
ContainerWriter::ContainerWriter(ZeroCopyOutputStream* out, size_t entry_count)
: out_(out), total_entry_count_(entry_count), current_entry_count_(0u) {
@@ -49,11 +50,17 @@ ContainerWriter::ContainerWriter(ZeroCopyOutputStream* out, size_t entry_count)
}
}
-inline static void WritePadding(int padding, CodedOutputStream* out) {
- if (padding < 4) {
- const uint32_t zero = 0u;
- out->WriteRaw(&zero, padding);
- }
+inline static size_t CalculatePaddingForAlignment(size_t size) {
+ size_t overage = size % kPaddingAlignment;
+ return overage == 0 ? 0 : kPaddingAlignment - overage;
+}
+
+inline static void WritePadding(size_t padding, CodedOutputStream* out) {
+ CHECK(padding < kPaddingAlignment);
+ const uint32_t zero = 0u;
+ static_assert(sizeof(zero) >= kPaddingAlignment, "Not enough source bytes for padding");
+
+ out->WriteRaw(&zero, padding);
}
bool ContainerWriter::AddResTableEntry(const pb::ResourceTable& table) {
@@ -70,7 +77,7 @@ bool ContainerWriter::AddResTableEntry(const pb::ResourceTable& table) {
// Write the aligned size.
const ::google::protobuf::uint64 size = table.ByteSize();
- const int padding = 4 - (size % 4);
+ const int padding = CalculatePaddingForAlignment(size);
coded_out.WriteLittleEndian64(size);
// Write the table.
@@ -103,9 +110,9 @@ bool ContainerWriter::AddResFileEntry(const pb::internal::CompiledFile& file,
// Write the aligned size.
const ::google::protobuf::uint32 header_size = file.ByteSize();
- const int header_padding = 4 - (header_size % 4);
+ const int header_padding = CalculatePaddingForAlignment(header_size);
const ::google::protobuf::uint64 data_size = in->TotalSize();
- const int data_padding = 4 - (data_size % 4);
+ const int data_padding = CalculatePaddingForAlignment(data_size);
coded_out.WriteLittleEndian64(kResFileEntryHeaderSize + header_size + header_padding + data_size +
data_padding);
diff --git a/tools/aapt2/format/binary/XmlFlattener_test.cpp b/tools/aapt2/format/binary/XmlFlattener_test.cpp
index 1dac493359e4..c24488b16153 100644
--- a/tools/aapt2/format/binary/XmlFlattener_test.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener_test.cpp
@@ -226,10 +226,10 @@ TEST_F(XmlFlattenerTest, FlattenNonStandardPackageId) {
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
// The tree needs a custom DynamicRefTable since it is not using a standard app ID (0x7f).
- android::DynamicRefTable dynamic_ref_table;
- dynamic_ref_table.addMapping(0x80, 0x80);
+ auto dynamic_ref_table = std::make_shared<android::DynamicRefTable>();
+ dynamic_ref_table->addMapping(0x80, 0x80);
- android::ResXMLTree tree(&dynamic_ref_table);
+ auto tree = android::ResXMLTree(std::move(dynamic_ref_table));
ASSERT_TRUE(Flatten(doc.get(), &tree));
while (tree.next() != android::ResXMLTree::START_TAG) {
diff --git a/tools/aapt2/formats.md b/tools/aapt2/formats.md
index bb31a005ef42..25a0e798dea2 100644
--- a/tools/aapt2/formats.md
+++ b/tools/aapt2/formats.md
@@ -23,7 +23,7 @@ boundary, so if a previous entry ends unaligned, padding must be inserted.
| Size (in bytes) | Field | Description |
|:----------------|:---------------|:----------------------------------------------------------------------------------------------------------|
| `4` | `entry_type` | The type of the entry. This can be one of two types: `RES_TABLE (0x00000000)` or `RES_FILE (0x00000001)`. |
-| `8` | `entry_length` | The length of the data that follows. |
+| `8` | `entry_length` | The length of the data that follows. Do not use if `entry_type` is `RES_FILE`; this value may be wrong. |
| `entry_length` | `data` | The payload. The contents of this varies based on the `entry_type`. |
If the `entry_type` is equal to `RES_TABLE (0x00000000)`, the `data` field contains a serialized
@@ -32,13 +32,14 @@ If the `entry_type` is equal to `RES_TABLE (0x00000000)`, the `data` field conta
If the `entry_type` is equal to `RES_FILE (0x00000001)`, the `data` field contains the following:
-| Size (in bytes) | Field | Description |
-|:----------------|:---------------|:----------------------------------------------------------------------------------------------------------|
-| `4` | `header_size` | The size of the `header` field. |
-| `8` | `data_size` | The size of the `data` field. |
-| `header_size` | `header` | The serialized Protobuf message [aapt.pb.internal.CompiledFile](ResourcesInternal.proto). |
-| `x` | `padding` | Up to 4 bytes of zeros, if padding is necessary to align the `data` field on a 32-bit boundary. |
-| `data_size` | `data` | The payload, which is determined by the `type` field in the `aapt.pb.internal.CompiledFile`. This can be a PNG file, binary XML, or [aapt.pb.XmlNode](Resources.proto). |
+| Size (in bytes) | Field | Description |
+|:----------------|:-----------------|:----------------------------------------------------------------------------------------------------------|
+| `4` | `header_size` | The size of the `header` field. |
+| `8` | `data_size` | The size of the `data` field. |
+| `header_size` | `header` | The serialized Protobuf message [aapt.pb.internal.CompiledFile](ResourcesInternal.proto). |
+| `x` | `header_padding` | Up to 3 bytes of zeros, if padding is necessary to align the `data` field on a 32-bit boundary. |
+| `data_size` | `data` | The payload, which is determined by the `type` field in the `aapt.pb.internal.CompiledFile`. This can be a PNG file, binary XML, or [aapt.pb.XmlNode](Resources.proto). |
+| `y` | `data_padding` | Up to 3 bytes of zeros, if `data_size` is not a multiple of 4. |
## AAPT2 Static Library Format (extension `.sapk`)
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 9e42c044e209..912c1ad377c1 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -1976,7 +1976,9 @@ def verify_nullability(clazz):
"""Catches missing nullability annotations"""
for f in clazz.fields:
- if f.value is not None and 'static' in f.split and 'final' in f.split:
+ if "enum_constant" in f.split:
+ continue # Enum constants are never null
+ if f.value is not None and 'final' in f.split:
continue # Nullability of constants can be inferred.
if f.typ not in PRIMITIVES and not has_nullability(f.annotations):
error(clazz, f, "M12", "Field must be marked either @NonNull or @Nullable")
@@ -1985,8 +1987,12 @@ def verify_nullability(clazz):
verify_nullability_args(clazz, c)
for m in clazz.methods:
- if m.name == "writeToParcel" or m.name == "onReceive":
- continue # Parcelable.writeToParcel() and BroadcastReceiver.onReceive() are not yet annotated
+ if m.name == "writeToParcel" or m.name == "onReceive" or m.name == "onBind":
+ continue # Parcelable.writeToParcel(), BroadcastReceiver.onReceive(), and Service.onBind() are not yet annotated
+
+ if (m.name == "equals" and m.args == ["java.lang.Object"] or
+ m.name == "toString" and m.args == []):
+ continue # Nullability of equals and toString is implicit.
if m.typ not in PRIMITIVES and not has_nullability(m.annotations):
error(clazz, m, "M12", "Return value must be marked either @NonNull or @Nullable")
diff --git a/tools/codegen/src/com/android/codegen/ClassInfo.kt b/tools/codegen/src/com/android/codegen/ClassInfo.kt
index 5061be2091e5..92da9dab863b 100644
--- a/tools/codegen/src/com/android/codegen/ClassInfo.kt
+++ b/tools/codegen/src/com/android/codegen/ClassInfo.kt
@@ -38,6 +38,11 @@ open class ClassInfo(val sourceLines: List<String>) {
val superInterfaces = (fileAst.types[0] as ClassOrInterfaceDeclaration)
.implementedTypes.map { it.asString() }
+ val superClass = run {
+ val superClasses = (fileAst.types[0] as ClassOrInterfaceDeclaration).extendedTypes
+ if (superClasses.isNonEmpty) superClasses[0] else null
+ }
+
val ClassName = classAst.nameAsString
private val genericArgsAst = classAst.typeParameters
val genericArgs = if (genericArgsAst.isEmpty()) "" else {
diff --git a/tools/codegen/src/com/android/codegen/ClassPrinter.kt b/tools/codegen/src/com/android/codegen/ClassPrinter.kt
index 1f0d4b8a7ec9..bd72d9e7ec21 100644
--- a/tools/codegen/src/com/android/codegen/ClassPrinter.kt
+++ b/tools/codegen/src/com/android/codegen/ClassPrinter.kt
@@ -1,6 +1,7 @@
package com.android.codegen
import com.github.javaparser.ast.Modifier
+import com.github.javaparser.ast.body.CallableDeclaration
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration
import com.github.javaparser.ast.body.TypeDeclaration
import com.github.javaparser.ast.expr.*
@@ -37,6 +38,7 @@ class ClassPrinter(
val GeneratedMember by lazy { classRef("com.android.internal.util.DataClass.Generated.Member") }
val Parcelling by lazy { classRef("com.android.internal.util.Parcelling") }
val Parcelable by lazy { classRef("android.os.Parcelable") }
+ val Parcel by lazy { classRef("android.os.Parcel") }
val UnsupportedAppUsage by lazy { classRef("android.annotation.UnsupportedAppUsage") }
init {
@@ -354,7 +356,9 @@ class ClassPrinter(
}
fun hasMethod(name: String, vararg argTypes: String): Boolean {
- return classAst.methods.any {
+ val members: List<CallableDeclaration<*>> =
+ if (name == ClassName) classAst.constructors else classAst.methods
+ return members.any {
it.name.asString() == name &&
it.parameters.map { it.type.asString() } == argTypes.toList()
}
@@ -365,6 +369,10 @@ class ClassPrinter(
.mapIndexed { i, node -> FieldInfo(index = i, fieldAst = node, classInfo = this) }
.filter { hasMethod("lazyInit${it.NameUpperCamel}") }
+ val extendsParcelableClass by lazy {
+ Parcelable !in superInterfaces && superClass != null
+ }
+
init {
val builderFactoryOverride = classAst.methods.find {
it.isStatic && it.nameAsString == "builder"
diff --git a/tools/codegen/src/com/android/codegen/FieldInfo.kt b/tools/codegen/src/com/android/codegen/FieldInfo.kt
index ba00264f5f5e..1a7fd6e241aa 100644
--- a/tools/codegen/src/com/android/codegen/FieldInfo.kt
+++ b/tools/codegen/src/com/android/codegen/FieldInfo.kt
@@ -191,7 +191,7 @@ data class FieldInfo(
* Parcel.write* and Parcel.read* method name wildcard values
*/
val ParcelMethodsSuffix = when {
- FieldClass in PRIMITIVE_TYPES - "char" - "boolean" +
+ FieldClass in PRIMITIVE_TYPES - "char" - "boolean" + BOXED_PRIMITIVE_TYPES +
listOf("String", "CharSequence", "Exception", "Size", "SizeF", "Bundle",
"FileDescriptor", "SparseBooleanArray", "SparseIntArray", "SparseArray") ->
FieldClass
diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt
index 914e475cfe41..431f378a8811 100644
--- a/tools/codegen/src/com/android/codegen/Generators.kt
+++ b/tools/codegen/src/com/android/codegen/Generators.kt
@@ -341,7 +341,7 @@ private fun ClassPrinter.generateBuilderSetters(visibility: String) {
}
}
- if (Type.contains("Map<")) {
+ if (FieldClass.endsWith("Map") && FieldInnerType != null) {
generateBuilderMethod(
name = adderName,
defVisibility = visibility,
@@ -417,11 +417,15 @@ fun ClassPrinter.generateParcelable() {
if (!isMethodGenerationSuppressed("writeToParcel", Parcel, "int")) {
+"@Override"
+GENERATED_MEMBER_HEADER
- "public void writeToParcel($Parcel dest, int flags)" {
+ "public void writeToParcel(@$NonNull $Parcel dest, int flags)" {
+"// You can override field parcelling by defining methods like:"
+"// void parcelFieldName(Parcel dest, int flags) { ... }"
+""
+ if (extendsParcelableClass) {
+ +"super.writeToParcel(dest, flags);\n"
+ }
+
if (booleanFields.isNotEmpty() || nullableFields.isNotEmpty()) {
+"$flagStorageType flg = 0;"
booleanFields.forEachApply {
@@ -463,6 +467,123 @@ fun ClassPrinter.generateParcelable() {
+""
}
+ if (!hasMethod(ClassName, Parcel)) {
+ val visibility = if (classAst.isFinal) "/* package-private */" else "protected"
+
+ +"/** @hide */"
+ +"@SuppressWarnings({\"unchecked\", \"RedundantCast\"})"
+ +GENERATED_MEMBER_HEADER
+ "$visibility $ClassName(@$NonNull $Parcel in) {" {
+ +"// You can override field unparcelling by defining methods like:"
+ +"// static FieldType unparcelFieldName(Parcel in) { ... }"
+ +""
+
+ if (extendsParcelableClass) {
+ +"super(in);\n"
+ }
+
+ if (booleanFields.isNotEmpty() || nullableFields.isNotEmpty()) {
+ +"$flagStorageType flg = in.read$FlagStorageType();"
+ }
+ booleanFields.forEachApply {
+ +"$Type $_name = (flg & $fieldBit) != 0;"
+ }
+ nonBooleanFields.forEachApply {
+
+ // Handle customized parceling
+ val customParcellingMethod = "unparcel$NameUpperCamel"
+ if (hasMethod(customParcellingMethod, Parcel)) {
+ +"$Type $_name = $customParcellingMethod(in);"
+ } else if (customParcellingClass != null) {
+ +"$Type $_name = $sParcelling.unparcel(in);"
+ } else if (hasAnnotation("@$DataClassEnum")) {
+ val ordinal = "${_name}Ordinal"
+ +"int $ordinal = in.readInt();"
+ +"$Type $_name = $ordinal < 0 ? null : $FieldClass.values()[$ordinal];"
+ } else {
+ val methodArgs = mutableListOf<String>()
+
+ // Create container if any
+ val containerInitExpr = when {
+ FieldClass.endsWith("Map") -> "new $LinkedHashMap<>()"
+ FieldClass == "List" || FieldClass == "ArrayList" ->
+ "new ${classRef("java.util.ArrayList")}<>()"
+ else -> ""
+ }
+ val passContainer = containerInitExpr.isNotEmpty()
+
+ // nullcheck +
+ // "FieldType fieldName = (FieldType)"
+ if (passContainer) {
+ methodArgs.add(_name)
+ !"$Type $_name = "
+ if (mayBeNull) {
+ +"null;"
+ !"if ((flg & $fieldBit) != 0) {"
+ pushIndent()
+ +""
+ !"$_name = "
+ }
+ +"$containerInitExpr;"
+ } else {
+ !"$Type $_name = "
+ if (mayBeNull) !"(flg & $fieldBit) == 0 ? null : "
+ if (ParcelMethodsSuffix == "StrongInterface") {
+ !"$FieldClass.Stub.asInterface("
+ } else if (Type !in PRIMITIVE_TYPES + "String" + "Bundle" &&
+ (!isArray || FieldInnerType !in PRIMITIVE_TYPES + "String") &&
+ ParcelMethodsSuffix != "Parcelable") {
+ !"($FieldClass) "
+ }
+ }
+
+ // Determine method args
+ when {
+ ParcelMethodsSuffix == "Parcelable" ->
+ methodArgs += "$FieldClass.class.getClassLoader()"
+ ParcelMethodsSuffix == "SparseArray" ->
+ methodArgs += "$FieldInnerClass.class.getClassLoader()"
+ ParcelMethodsSuffix == "TypedObject" ->
+ methodArgs += "$FieldClass.CREATOR"
+ ParcelMethodsSuffix == "TypedArray" ->
+ methodArgs += "$FieldInnerClass.CREATOR"
+ ParcelMethodsSuffix == "Map" ->
+ methodArgs += "${fieldTypeGenegicArgs[1].substringBefore("<")}.class.getClassLoader()"
+ ParcelMethodsSuffix.startsWith("Parcelable")
+ || (isList || isArray)
+ && FieldInnerType !in PRIMITIVE_TYPES + "String" ->
+ methodArgs += "$FieldInnerClass.class.getClassLoader()"
+ }
+
+ // ...in.readFieldType(args...);
+ when {
+ ParcelMethodsSuffix == "StrongInterface" -> !"in.readStrongBinder"
+ isArray -> !"in.create$ParcelMethodsSuffix"
+ else -> !"in.read$ParcelMethodsSuffix"
+ }
+ !"(${methodArgs.joinToString(", ")})"
+ if (ParcelMethodsSuffix == "StrongInterface") !")"
+ +";"
+
+ // Cleanup if passContainer
+ if (passContainer && mayBeNull) {
+ popIndent()
+ rmEmptyLine()
+ +"\n}"
+ }
+ }
+ }
+
+ +""
+ fields.forEachApply {
+ !"this."
+ generateSetFrom(_name)
+ }
+
+ generateOnConstructedCallback()
+ }
+ }
+
if (classAst.fields.none { it.variables[0].nameAsString == "CREATOR" }) {
val Creator = classRef("android.os.Parcelable.Creator")
@@ -477,104 +598,8 @@ fun ClassPrinter.generateParcelable() {
}
+"@Override"
- +"@SuppressWarnings({\"unchecked\", \"RedundantCast\"})"
- "public $ClassName createFromParcel($Parcel in)" {
- +"// You can override field unparcelling by defining methods like:"
- +"// static FieldType unparcelFieldName(Parcel in) { ... }"
- +""
- if (booleanFields.isNotEmpty() || nullableFields.isNotEmpty()) {
- +"$flagStorageType flg = in.read$FlagStorageType();"
- }
- booleanFields.forEachApply {
- +"$Type $_name = (flg & $fieldBit) != 0;"
- }
- nonBooleanFields.forEachApply {
-
- // Handle customized parceling
- val customParcellingMethod = "unparcel$NameUpperCamel"
- if (hasMethod(customParcellingMethod, Parcel)) {
- +"$Type $_name = $customParcellingMethod(in);"
- } else if (customParcellingClass != null) {
- +"$Type $_name = $sParcelling.unparcel(in);"
- } else if (hasAnnotation("@$DataClassEnum")) {
- val ordinal = "${_name}Ordinal"
- +"int $ordinal = in.readInt();"
- +"$Type $_name = $ordinal < 0 ? null : $FieldClass.values()[$ordinal];"
- } else {
- val methodArgs = mutableListOf<String>()
-
- // Create container if any
- val containerInitExpr = when {
- FieldClass.endsWith("Map") -> "new $LinkedHashMap<>()"
- FieldClass == "List" || FieldClass == "ArrayList" ->
- "new ${classRef("java.util.ArrayList")}<>()"
- else -> ""
- }
- val passContainer = containerInitExpr.isNotEmpty()
-
- // nullcheck +
- // "FieldType fieldName = (FieldType)"
- if (passContainer) {
- methodArgs.add(_name)
- !"$Type $_name = "
- if (mayBeNull) {
- +"null;"
- !"if ((flg & $fieldBit) != 0) {"
- pushIndent()
- +""
- !"$_name = "
- }
- +"$containerInitExpr;"
- } else {
- !"$Type $_name = "
- if (mayBeNull) !"(flg & $fieldBit) == 0 ? null : "
- if (ParcelMethodsSuffix == "StrongInterface") {
- !"$FieldClass.Stub.asInterface("
- } else if (Type !in PRIMITIVE_TYPES + "String" + "Bundle" &&
- (!isArray || FieldInnerType !in PRIMITIVE_TYPES + "String") &&
- ParcelMethodsSuffix != "Parcelable") {
- !"($Type) "
- }
- }
-
- // Determine method args
- when {
- ParcelMethodsSuffix == "Parcelable" ->
- methodArgs += "$FieldClass.class.getClassLoader()"
- ParcelMethodsSuffix == "TypedObject" ->
- methodArgs += "$FieldClass.CREATOR"
- ParcelMethodsSuffix == "TypedArray" ->
- methodArgs += "$FieldInnerClass.CREATOR"
- ParcelMethodsSuffix.startsWith("Parcelable")
- || FieldClass == "Map"
- || (isList || isArray)
- && FieldInnerType !in PRIMITIVE_TYPES + "String" ->
- methodArgs += "$FieldInnerClass.class.getClassLoader()"
- }
-
- // ...in.readFieldType(args...);
- when {
- ParcelMethodsSuffix == "StrongInterface" -> !"in.readStrongBinder"
- isArray -> !"in.create$ParcelMethodsSuffix"
- else -> !"in.read$ParcelMethodsSuffix"
- }
- !"(${methodArgs.joinToString(", ")})"
- if (ParcelMethodsSuffix == "StrongInterface") !")"
- +";"
-
- // Cleanup if passContainer
- if (passContainer && mayBeNull) {
- popIndent()
- rmEmptyLine()
- +"\n}"
- }
- }
- }
- "return new $ClassType(" {
- fields.forEachTrimmingTrailingComma {
- +"$_name,"
- }
- } + ";"
+ "public $ClassName createFromParcel(@$NonNull $Parcel in)" {
+ +"return new $ClassName(in);"
}
rmEmptyLine()
} + ";"
@@ -586,7 +611,7 @@ fun ClassPrinter.generateEqualsHashcode() {
if (!isMethodGenerationSuppressed("equals", "Object")) {
+"@Override"
+GENERATED_MEMBER_HEADER
- "public boolean equals(Object o)" {
+ "public boolean equals(@$Nullable Object o)" {
+"// You can override field equality logic by defining either of the methods like:"
+"// boolean fieldNameEquals($ClassName other) { ... }"
+"// boolean fieldNameEquals(FieldType otherValue) { ... }"
@@ -829,6 +854,7 @@ private fun ClassPrinter.generateFieldValidation(field: FieldInfo) = field.run {
it.nameAsString == intOrStringDef?.AnnotationName
|| it.nameAsString in knownNonValidationAnnotations
|| it in perElementValidations
+ || it.args.any { (_, value) -> value is ArrayInitializerExpr }
}.forEach { annotation ->
appendValidateCall(annotation,
valueToValidate = if (annotation.nameAsString == Size) sizeExpr else name)
@@ -849,14 +875,7 @@ fun ClassPrinter.appendValidateCall(annotation: AnnotationExpr, valueToValidate:
val validate = memberRef("com.android.internal.util.AnnotationValidations.validate")
"$validate(" {
!"${annotation.nameAsString}.class, null, $valueToValidate"
- val params = when (annotation) {
- is MarkerAnnotationExpr -> emptyMap()
- is SingleMemberAnnotationExpr -> mapOf("value" to annotation.memberValue)
- is NormalAnnotationExpr ->
- annotation.pairs.map { it.name.asString() to it.value }.toMap()
- else -> throw IllegalStateException()
- }
- params.forEach { name, value ->
+ annotation.args.forEach { name, value ->
!",\n\"$name\", $value"
}
}
@@ -885,7 +904,7 @@ fun ClassPrinter.generateForEachField() {
usedSpecializationsSet.toList().forEachLastAware { specType, isLast ->
val SpecType = specType.capitalize()
val ActionClass = classRef("com.android.internal.util.DataClass.Per${SpecType}FieldAction")
- +"$ActionClass<$ClassType> action$SpecType${if_(!isLast, ",")}"
+ +"@$NonNull $ActionClass<$ClassType> action$SpecType${if_(!isLast, ",")}"
}
}; " {" {
usedSpecializations.forEachIndexed { i, specType ->
@@ -900,7 +919,7 @@ fun ClassPrinter.generateForEachField() {
+"/** @deprecated May cause boxing allocations - use with caution! */"
+"@Deprecated"
+GENERATED_MEMBER_HEADER
- "void forEachField($PerObjectFieldAction<$ClassType> action)" {
+ "void forEachField(@$NonNull $PerObjectFieldAction<$ClassType> action)" {
fields.forEachApply {
+"action.acceptObject(this, \"$nameLowerCamel\", $name);"
}
diff --git a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
index 24cf4690dae1..d6953c00fc0b 100644
--- a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
+++ b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
@@ -87,6 +87,14 @@ private fun ClassPrinter.appendExpr(sb: StringBuilder, ex: Expression?) {
is IntegerLiteralExpr -> sb.append(ex.asInt()).append("L")
is LongLiteralExpr -> sb.append(ex.asLong()).append("L")
is DoubleLiteralExpr -> sb.append(ex.asDouble())
+ is ArrayInitializerExpr -> {
+ sb.append("{")
+ ex.values.forEachLastAware { arrayElem, isLast ->
+ appendExpr(sb, arrayElem)
+ if (!isLast) sb.append(", ")
+ }
+ sb.append("}")
+ }
else -> sb.append(ex)
}
}
diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt
index fa2b41adcacb..ce83d3dc8e51 100755
--- a/tools/codegen/src/com/android/codegen/Main.kt
+++ b/tools/codegen/src/com/android/codegen/Main.kt
@@ -9,6 +9,7 @@ const val GENERATED_WARNING_PREFIX = "Code below generated by $CODEGEN_NAME"
const val INDENT_SINGLE = " "
val PRIMITIVE_TYPES = listOf("byte", "short", "int", "long", "char", "float", "double", "boolean")
+val BOXED_PRIMITIVE_TYPES = PRIMITIVE_TYPES.map { it.capitalize() } - "Int" + "Integer" - "Char" + "Character"
val BUILTIN_SPECIAL_PARCELLINGS = listOf("Pattern")
@@ -95,7 +96,13 @@ In addition, for any field mMyField(or myField) of type FieldType you can define
you can use with final fields.
Version: $CODEGEN_VERSION
-Questions? Feedback? Contact: eugenesusla@
+
+Questions? Feedback?
+Contact: eugenesusla@
+Bug/feature request: http://go/codegen-bug
+
+Slides: http://go/android-codegen
+In-depth example: http://go/SampleDataClass
"""
fun main(args: Array<String>) {
@@ -132,11 +139,15 @@ fun main(args: Array<String>) {
// $GENERATED_WARNING_PREFIX v$CODEGEN_VERSION.
//
// DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
//
// To regenerate run:
// $ $cliExecutable ${cliArgs.dropLast(1).joinToString("") { "$it " }}$fileEscaped
//
- // CHECKSTYLE:OFF Generated code
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
"""
if (FeatureFlag.CONST_DEFS()) generateConstDefs()
@@ -146,8 +157,7 @@ fun main(args: Array<String>) {
generateConstructor("public")
} else if (FeatureFlag.BUILDER()
|| FeatureFlag.COPY_CONSTRUCTOR()
- || FeatureFlag.WITHERS()
- || FeatureFlag.PARCELABLE()) {
+ || FeatureFlag.WITHERS()) {
generateConstructor("/* package-private */")
}
if (FeatureFlag.COPY_CONSTRUCTOR()) generateCopyConstructor()
diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt
index b2cc81391510..3eb9e7bb68c6 100644
--- a/tools/codegen/src/com/android/codegen/SharedConstants.kt
+++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt
@@ -1,7 +1,7 @@
package com.android.codegen
const val CODEGEN_NAME = "codegen"
-const val CODEGEN_VERSION = "1.0.1"
+const val CODEGEN_VERSION = "1.0.9"
const val CANONICAL_BUILDER_CLASS = "Builder"
const val BASE_BUILDER_CLASS = "BaseBuilder"
diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt
index a1f068afa29a..e703397214eb 100644
--- a/tools/codegen/src/com/android/codegen/Utils.kt
+++ b/tools/codegen/src/com/android/codegen/Utils.kt
@@ -1,9 +1,6 @@
package com.android.codegen
-import com.github.javaparser.ast.Modifier
-import com.github.javaparser.ast.expr.AnnotationExpr
-import com.github.javaparser.ast.expr.Expression
-import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr
+import com.github.javaparser.ast.expr.*
import com.github.javaparser.ast.nodeTypes.NodeWithModifiers
import java.time.Instant
import java.time.ZoneId
@@ -88,3 +85,10 @@ fun abort(msg: String): Nothing {
}
fun bitAtExpr(bitIndex: Int) = "0x${java.lang.Long.toHexString(1L shl bitIndex)}"
+
+val AnnotationExpr.args: Map<String, Expression> get() = when (this) {
+ is MarkerAnnotationExpr -> emptyMap()
+ is SingleMemberAnnotationExpr -> mapOf("value" to memberValue)
+ is NormalAnnotationExpr -> pairs.map { it.name.asString() to it.value }.toMap()
+ else -> throw IllegalArgumentException("Unknown annotation expression: $this")
+}
diff --git a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt
index d00def625a39..7fe21c7aab3e 100644
--- a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt
+++ b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt
@@ -182,6 +182,11 @@ class StaleDataclassProcessor: AbstractProcessor() {
.filterNot {
it.kind == ElementKind.CLASS
|| it.kind == ElementKind.CONSTRUCTOR
+ || it.kind == ElementKind.INTERFACE
+ || it.kind == ElementKind.ENUM
+ || it.kind == ElementKind.ANNOTATION_TYPE
+ || it.kind == ElementKind.INSTANCE_INIT
+ || it.kind == ElementKind.STATIC_INIT
|| isGenerated(it)
}.map {
elemToString(it)
diff --git a/tools/protologtool/Android.bp b/tools/protologtool/Android.bp
index a86c226c2179..d1a86c245dec 100644
--- a/tools/protologtool/Android.bp
+++ b/tools/protologtool/Android.bp
@@ -1,27 +1,32 @@
-java_binary_host {
- name: "protologtool",
- manifest: "manifest.txt",
+java_library_host {
+ name: "protologtool-lib",
srcs: [
- "src/**/*.kt",
+ "src/com/android/protolog/tool/**/*.kt",
],
static_libs: [
+ "protolog-common",
"javaparser",
- "windowmanager-log-proto",
+ "protolog-proto",
"jsonlib",
],
}
+java_binary_host {
+ name: "protologtool",
+ manifest: "manifest.txt",
+ static_libs: [
+ "protologtool-lib",
+ ],
+}
+
java_test_host {
name: "protologtool-tests",
test_suites: ["general-tests"],
srcs: [
- "src/**/*.kt",
"tests/**/*.kt",
],
static_libs: [
- "javaparser",
- "windowmanager-log-proto",
- "jsonlib",
+ "protologtool-lib",
"junit",
"mockito",
],
diff --git a/tools/protologtool/README.md b/tools/protologtool/README.md
index 3439357af598..ba639570f88c 100644
--- a/tools/protologtool/README.md
+++ b/tools/protologtool/README.md
@@ -8,8 +8,13 @@ ProtoLogTool incorporates three different modes of operation:
### Code transformation
-Command: `process <protolog class path> <protolog implementation class path>
- <protolog groups class path> <config.jar> [<input.java>] <output.srcjar>`
+Command: `protologtool transform-protolog-calls
+ --protolog-class <protolog class name>
+ --protolog-impl-class <protolog implementation class name>
+ --loggroups-class <protolog groups class name>
+ --loggroups-jar <config jar path>
+ --output-srcjar <output.srcjar>
+ [<input.java>]`
In this mode ProtoLogTool transforms every ProtoLog logging call in form of:
```java
@@ -17,16 +22,20 @@ ProtoLog.x(ProtoLogGroup.GROUP_NAME, "Format string %d %s", value1, value2);
```
into:
```java
-if (GROUP_NAME.isLogToAny()) {
- ProtoLogImpl.x(ProtoLogGroup.GROUP_NAME, 123456, "Format string %d %s or null", value1, value2);
+if (ProtoLogImpl.isEnabled(GROUP_NAME)) {
+ int protoLogParam0 = value1;
+ String protoLogParam1 = String.valueOf(value2);
+ ProtoLogImpl.x(ProtoLogGroup.GROUP_NAME, 123456, 0b0100, "Format string %d %s or null", protoLogParam0, protoLogParam1);
}
```
where `ProtoLog`, `ProtoLogImpl` and `ProtoLogGroup` are the classes provided as arguments
(can be imported, static imported or full path, wildcard imports are not allowed) and, `x` is the
logging method. The transformation is done on the source level. A hash is generated from the format
- string and log level and inserted after the `ProtoLogGroup` argument. The format string is replaced
+ string, log level and log group name and inserted after the `ProtoLogGroup` argument. After the hash
+ we insert a bitmask specifying the types of logged parameters. The format string is replaced
by `null` if `ProtoLogGroup.GROUP_NAME.isLogToLogcat()` returns false. If `ProtoLogGroup.GROUP_NAME.isEnabled()`
- returns false the log statement is removed entirely from the resultant code.
+ returns false the log statement is removed entirely from the resultant code. The real generated code is inlined
+ and a number of new line characters is added as to preserve line numbering in file.
Input is provided as a list of java source file names. Transformed source is saved to a single
source jar file. The ProtoLogGroup class with all dependencies should be provided as a compiled
@@ -34,8 +43,12 @@ jar file (config.jar).
### Viewer config generation
-Command: `viewerconf <protolog class path> <protolog implementation class path
-<protolog groups class path> <config.jar> [<input.java>] <output.json>`
+Command: `generate-viewer-config
+ --protolog-class <protolog class name>
+ --loggroups-class <protolog groups class name>
+ --loggroups-jar <config jar path>
+ --viewer-conf <viewer.json>
+ [<input.java>]`
This command is similar in it's syntax to the previous one, only instead of creating a processed source jar
it writes a viewer configuration file with following schema:
@@ -46,8 +59,9 @@ it writes a viewer configuration file with following schema:
"123456": {
"message": "Format string %d %s",
"level": "ERROR",
- "group": "GROUP_NAME"
- },
+ "group": "GROUP_NAME",
+ "at": "com\/android\/server\/example\/Class.java"
+ }
},
"groups": {
"GROUP_NAME": {
@@ -60,13 +74,13 @@ it writes a viewer configuration file with following schema:
### Binary log viewing
-Command: `read <viewer.json> <wm_log.pb>`
+Command: `read-log --viewer-conf <viewer.json> <wm_log.pb>`
Reads the binary ProtoLog log file and outputs a human-readable LogCat-like text log.
## What is ProtoLog?
-ProtoLog is a logging system created for the WindowManager project. It allows both binary and text logging
+ProtoLog is a generic logging system created for the WindowManager project. It allows both binary and text logging
and is tunable in runtime. It consists of 3 different submodules:
* logging system built-in the Android app,
* log viewer for reading binary logs,
@@ -94,8 +108,7 @@ To add a new ProtoLogGroup simple create a new enum ProtoLogGroup member with de
To add a new logging statement just add a new call to ProtoLog.x where x is a log level.
-After doing any changes to logging groups or statements you should run `make update-protolog` to update
-viewer configuration saved in the code repository.
+After doing any changes to logging groups or statements you should build the project and follow instructions printed by the tool.
## How to change settings on device in runtime?
Use the `adb shell su root cmd window logging` command. To get help just type
diff --git a/tools/protologtool/manifest.txt b/tools/protologtool/manifest.txt
index f5e53c450f2a..cabebd51a2fa 100644
--- a/tools/protologtool/manifest.txt
+++ b/tools/protologtool/manifest.txt
@@ -1 +1 @@
-Main-class: com.android.protologtool.ProtoLogTool
+Main-class: com.android.protolog.tool.ProtoLogTool
diff --git a/tools/protologtool/src/com/android/protolog/tool/CodeUtils.kt b/tools/protologtool/src/com/android/protolog/tool/CodeUtils.kt
new file mode 100644
index 000000000000..a52c8042582b
--- /dev/null
+++ b/tools/protologtool/src/com/android/protolog/tool/CodeUtils.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.protolog.tool
+
+import com.github.javaparser.ast.CompilationUnit
+import com.github.javaparser.ast.ImportDeclaration
+import com.github.javaparser.ast.expr.BinaryExpr
+import com.github.javaparser.ast.expr.Expression
+import com.github.javaparser.ast.expr.StringLiteralExpr
+
+object CodeUtils {
+ /**
+ * Returns a stable hash of a string.
+ * We reimplement String::hashCode() for readability reasons.
+ */
+ fun hash(position: String, messageString: String, logLevel: LogLevel, logGroup: LogGroup): Int {
+ return (position + messageString + logLevel.name + logGroup.name)
+ .map { c -> c.toInt() }.reduce { h, c -> h * 31 + c }
+ }
+
+ fun checkWildcardStaticImported(code: CompilationUnit, className: String, fileName: String) {
+ code.findAll(ImportDeclaration::class.java)
+ .forEach { im ->
+ if (im.isStatic && im.isAsterisk && im.name.toString() == className) {
+ throw IllegalImportException("Wildcard static imports of $className " +
+ "methods are not supported.", ParsingContext(fileName, im))
+ }
+ }
+ }
+
+ fun isClassImportedOrSamePackage(code: CompilationUnit, className: String): Boolean {
+ val packageName = className.substringBeforeLast('.')
+ return code.packageDeclaration.isPresent &&
+ code.packageDeclaration.get().nameAsString == packageName ||
+ code.findAll(ImportDeclaration::class.java)
+ .any { im ->
+ !im.isStatic &&
+ ((!im.isAsterisk && im.name.toString() == className) ||
+ (im.isAsterisk && im.name.toString() == packageName))
+ }
+ }
+
+ fun staticallyImportedMethods(code: CompilationUnit, className: String): Set<String> {
+ return code.findAll(ImportDeclaration::class.java)
+ .filter { im ->
+ im.isStatic &&
+ im.name.toString().substringBeforeLast('.') == className
+ }
+ .map { im -> im.name.toString().substringAfterLast('.') }.toSet()
+ }
+
+ fun concatMultilineString(expr: Expression, context: ParsingContext): String {
+ return when (expr) {
+ is StringLiteralExpr -> expr.asString()
+ is BinaryExpr -> when {
+ expr.operator == BinaryExpr.Operator.PLUS ->
+ concatMultilineString(expr.left, context) +
+ concatMultilineString(expr.right, context)
+ else -> throw InvalidProtoLogCallException(
+ "expected a string literal " +
+ "or concatenation of string literals, got: $expr", context)
+ }
+ else -> throw InvalidProtoLogCallException("expected a string literal " +
+ "or concatenation of string literals, got: $expr", context)
+ }
+ }
+}
diff --git a/tools/protologtool/src/com/android/protolog/tool/CommandOptions.kt b/tools/protologtool/src/com/android/protolog/tool/CommandOptions.kt
new file mode 100644
index 000000000000..bfbbf7a32c22
--- /dev/null
+++ b/tools/protologtool/src/com/android/protolog/tool/CommandOptions.kt
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.protolog.tool
+
+import java.util.regex.Pattern
+
+class CommandOptions(args: Array<String>) {
+ companion object {
+ const val TRANSFORM_CALLS_CMD = "transform-protolog-calls"
+ const val GENERATE_CONFIG_CMD = "generate-viewer-config"
+ const val READ_LOG_CMD = "read-log"
+ private val commands = setOf(TRANSFORM_CALLS_CMD, GENERATE_CONFIG_CMD, READ_LOG_CMD)
+
+ private const val PROTOLOG_CLASS_PARAM = "--protolog-class"
+ private const val PROTOLOGIMPL_CLASS_PARAM = "--protolog-impl-class"
+ private const val PROTOLOGCACHE_CLASS_PARAM = "--protolog-cache-class"
+ private const val PROTOLOGGROUP_CLASS_PARAM = "--loggroups-class"
+ private const val PROTOLOGGROUP_JAR_PARAM = "--loggroups-jar"
+ private const val VIEWER_CONFIG_JSON_PARAM = "--viewer-conf"
+ private const val OUTPUT_SOURCE_JAR_PARAM = "--output-srcjar"
+ private val parameters = setOf(PROTOLOG_CLASS_PARAM, PROTOLOGIMPL_CLASS_PARAM,
+ PROTOLOGCACHE_CLASS_PARAM, PROTOLOGGROUP_CLASS_PARAM, PROTOLOGGROUP_JAR_PARAM,
+ VIEWER_CONFIG_JSON_PARAM, OUTPUT_SOURCE_JAR_PARAM)
+
+ val USAGE = """
+ Usage: ${Constants.NAME} <command> [<args>]
+ Available commands:
+
+ $TRANSFORM_CALLS_CMD $PROTOLOG_CLASS_PARAM <class name> $PROTOLOGIMPL_CLASS_PARAM
+ <class name> $PROTOLOGCACHE_CLASS_PARAM
+ <class name> $PROTOLOGGROUP_CLASS_PARAM <class name> $PROTOLOGGROUP_JAR_PARAM
+ <config.jar> $OUTPUT_SOURCE_JAR_PARAM <output.srcjar> [<input.java>]
+ - processes java files replacing stub calls with logging code.
+
+ $GENERATE_CONFIG_CMD $PROTOLOG_CLASS_PARAM <class name> $PROTOLOGGROUP_CLASS_PARAM
+ <class name> $PROTOLOGGROUP_JAR_PARAM <config.jar> $VIEWER_CONFIG_JSON_PARAM
+ <viewer.json> [<input.java>]
+ - creates viewer config file from given java files.
+
+ $READ_LOG_CMD $VIEWER_CONFIG_JSON_PARAM <viewer.json> <wm_log.pb>
+ - translates a binary log to a readable format.
+ """.trimIndent()
+
+ private fun validateClassName(name: String): String {
+ if (!Pattern.matches("^([a-z]+[A-Za-z0-9]*\\.)+([A-Za-z0-9$]+)$", name)) {
+ throw InvalidCommandException("Invalid class name $name")
+ }
+ return name
+ }
+
+ private fun getParam(paramName: String, params: Map<String, String>): String {
+ if (!params.containsKey(paramName)) {
+ throw InvalidCommandException("Param $paramName required")
+ }
+ return params.getValue(paramName)
+ }
+
+ private fun validateNotSpecified(paramName: String, params: Map<String, String>): String {
+ if (params.containsKey(paramName)) {
+ throw InvalidCommandException("Unsupported param $paramName")
+ }
+ return ""
+ }
+
+ private fun validateJarName(name: String): String {
+ if (!name.endsWith(".jar")) {
+ throw InvalidCommandException("Jar file required, got $name instead")
+ }
+ return name
+ }
+
+ private fun validateSrcJarName(name: String): String {
+ if (!name.endsWith(".srcjar")) {
+ throw InvalidCommandException("Source jar file required, got $name instead")
+ }
+ return name
+ }
+
+ private fun validateJSONName(name: String): String {
+ if (!name.endsWith(".json")) {
+ throw InvalidCommandException("Json file required, got $name instead")
+ }
+ return name
+ }
+
+ private fun validateJavaInputList(list: List<String>): List<String> {
+ if (list.isEmpty()) {
+ throw InvalidCommandException("No java source input files")
+ }
+ list.forEach { name ->
+ if (!name.endsWith(".java")) {
+ throw InvalidCommandException("Not a java source file $name")
+ }
+ }
+ return list
+ }
+
+ private fun validateLogInputList(list: List<String>): String {
+ if (list.isEmpty()) {
+ throw InvalidCommandException("No log input file")
+ }
+ if (list.size > 1) {
+ throw InvalidCommandException("Only one log input file allowed")
+ }
+ return list[0]
+ }
+ }
+
+ val protoLogClassNameArg: String
+ val protoLogGroupsClassNameArg: String
+ val protoLogImplClassNameArg: String
+ val protoLogCacheClassNameArg: String
+ val protoLogGroupsJarArg: String
+ val viewerConfigJsonArg: String
+ val outputSourceJarArg: String
+ val logProtofileArg: String
+ val javaSourceArgs: List<String>
+ val command: String
+
+ init {
+ if (args.isEmpty()) {
+ throw InvalidCommandException("No command specified.")
+ }
+ command = args[0]
+ if (command !in commands) {
+ throw InvalidCommandException("Unknown command.")
+ }
+
+ val params: MutableMap<String, String> = mutableMapOf()
+ val inputFiles: MutableList<String> = mutableListOf()
+
+ var idx = 1
+ while (idx < args.size) {
+ if (args[idx].startsWith("--")) {
+ if (idx + 1 >= args.size) {
+ throw InvalidCommandException("No value for ${args[idx]}")
+ }
+ if (args[idx] !in parameters) {
+ throw InvalidCommandException("Unknown parameter ${args[idx]}")
+ }
+ if (args[idx + 1].startsWith("--")) {
+ throw InvalidCommandException("No value for ${args[idx]}")
+ }
+ if (params.containsKey(args[idx])) {
+ throw InvalidCommandException("Duplicated parameter ${args[idx]}")
+ }
+ params[args[idx]] = args[idx + 1]
+ idx += 2
+ } else {
+ inputFiles.add(args[idx])
+ idx += 1
+ }
+ }
+
+ when (command) {
+ TRANSFORM_CALLS_CMD -> {
+ protoLogClassNameArg = validateClassName(getParam(PROTOLOG_CLASS_PARAM, params))
+ protoLogGroupsClassNameArg = validateClassName(getParam(PROTOLOGGROUP_CLASS_PARAM,
+ params))
+ protoLogImplClassNameArg = validateClassName(getParam(PROTOLOGIMPL_CLASS_PARAM,
+ params))
+ protoLogCacheClassNameArg = validateClassName(getParam(PROTOLOGCACHE_CLASS_PARAM,
+ params))
+ protoLogGroupsJarArg = validateJarName(getParam(PROTOLOGGROUP_JAR_PARAM, params))
+ viewerConfigJsonArg = validateNotSpecified(VIEWER_CONFIG_JSON_PARAM, params)
+ outputSourceJarArg = validateSrcJarName(getParam(OUTPUT_SOURCE_JAR_PARAM, params))
+ javaSourceArgs = validateJavaInputList(inputFiles)
+ logProtofileArg = ""
+ }
+ GENERATE_CONFIG_CMD -> {
+ protoLogClassNameArg = validateClassName(getParam(PROTOLOG_CLASS_PARAM, params))
+ protoLogGroupsClassNameArg = validateClassName(getParam(PROTOLOGGROUP_CLASS_PARAM,
+ params))
+ protoLogImplClassNameArg = validateNotSpecified(PROTOLOGIMPL_CLASS_PARAM, params)
+ protoLogCacheClassNameArg = validateNotSpecified(PROTOLOGCACHE_CLASS_PARAM, params)
+ protoLogGroupsJarArg = validateJarName(getParam(PROTOLOGGROUP_JAR_PARAM, params))
+ viewerConfigJsonArg = validateJSONName(getParam(VIEWER_CONFIG_JSON_PARAM, params))
+ outputSourceJarArg = validateNotSpecified(OUTPUT_SOURCE_JAR_PARAM, params)
+ javaSourceArgs = validateJavaInputList(inputFiles)
+ logProtofileArg = ""
+ }
+ READ_LOG_CMD -> {
+ protoLogClassNameArg = validateNotSpecified(PROTOLOG_CLASS_PARAM, params)
+ protoLogGroupsClassNameArg = validateNotSpecified(PROTOLOGGROUP_CLASS_PARAM, params)
+ protoLogImplClassNameArg = validateNotSpecified(PROTOLOGIMPL_CLASS_PARAM, params)
+ protoLogCacheClassNameArg = validateNotSpecified(PROTOLOGCACHE_CLASS_PARAM, params)
+ protoLogGroupsJarArg = validateNotSpecified(PROTOLOGGROUP_JAR_PARAM, params)
+ viewerConfigJsonArg = validateJSONName(getParam(VIEWER_CONFIG_JSON_PARAM, params))
+ outputSourceJarArg = validateNotSpecified(OUTPUT_SOURCE_JAR_PARAM, params)
+ javaSourceArgs = listOf()
+ logProtofileArg = validateLogInputList(inputFiles)
+ }
+ else -> {
+ throw InvalidCommandException("Unknown command.")
+ }
+ }
+ }
+}
diff --git a/tools/protologtool/src/com/android/protolog/tool/Constants.kt b/tools/protologtool/src/com/android/protolog/tool/Constants.kt
new file mode 100644
index 000000000000..aa3e00f2f4db
--- /dev/null
+++ b/tools/protologtool/src/com/android/protolog/tool/Constants.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.protolog.tool
+
+object Constants {
+ const val NAME = "protologtool"
+ const val VERSION = "1.0.0"
+ const val IS_ENABLED_METHOD = "isEnabled"
+ const val ENUM_VALUES_METHOD = "values"
+}
diff --git a/tools/protologtool/src/com/android/protolog/tool/LogGroup.kt b/tools/protologtool/src/com/android/protolog/tool/LogGroup.kt
new file mode 100644
index 000000000000..587f7b9db016
--- /dev/null
+++ b/tools/protologtool/src/com/android/protolog/tool/LogGroup.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.protolog.tool
+
+data class LogGroup(
+ val name: String,
+ val enabled: Boolean,
+ val textEnabled: Boolean,
+ val tag: String
+)
diff --git a/tools/protologtool/src/com/android/protolog/tool/LogLevel.kt b/tools/protologtool/src/com/android/protolog/tool/LogLevel.kt
new file mode 100644
index 000000000000..e88f0f8231bd
--- /dev/null
+++ b/tools/protologtool/src/com/android/protolog/tool/LogLevel.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.protolog.tool
+
+import com.github.javaparser.ast.Node
+
+enum class LogLevel {
+ DEBUG, VERBOSE, INFO, WARN, ERROR, WTF;
+
+ companion object {
+ fun getLevelForMethodName(name: String, node: Node, context: ParsingContext): LogLevel {
+ return when (name) {
+ "d" -> DEBUG
+ "v" -> VERBOSE
+ "i" -> INFO
+ "w" -> WARN
+ "e" -> ERROR
+ "wtf" -> WTF
+ else ->
+ throw InvalidProtoLogCallException("Unknown log level $name in $node", context)
+ }
+ }
+ }
+}
diff --git a/tools/protologtool/src/com/android/protolog/tool/LogParser.kt b/tools/protologtool/src/com/android/protolog/tool/LogParser.kt
new file mode 100644
index 000000000000..a59038fc99a0
--- /dev/null
+++ b/tools/protologtool/src/com/android/protolog/tool/LogParser.kt
@@ -0,0 +1,115 @@
+/*
+ * 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.protolog.tool
+
+import com.android.json.stream.JsonReader
+import com.android.server.protolog.common.InvalidFormatStringException
+import com.android.server.protolog.common.LogDataType
+import com.android.server.protolog.ProtoLogMessage
+import com.android.server.protolog.ProtoLogFileProto
+import java.io.BufferedReader
+import java.io.InputStream
+import java.io.InputStreamReader
+import java.io.PrintStream
+import java.lang.Exception
+import java.text.SimpleDateFormat
+import java.util.Date
+import java.util.Locale
+
+/**
+ * Implements a simple parser/viewer for binary ProtoLog logs.
+ * A binary log is translated into Android "LogCat"-like text log.
+ */
+class LogParser(private val configParser: ViewerConfigParser) {
+ companion object {
+ private val dateFormat = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
+ private val magicNumber =
+ ProtoLogFileProto.MagicNumber.MAGIC_NUMBER_H.number.toLong() shl 32 or
+ ProtoLogFileProto.MagicNumber.MAGIC_NUMBER_L.number.toLong()
+ }
+
+ private fun printTime(time: Long, offset: Long, ps: PrintStream) {
+ ps.print(dateFormat.format(Date(time / 1000000 + offset)) + " ")
+ }
+
+ private fun printFormatted(
+ protoLogMessage: ProtoLogMessage,
+ configEntry: ViewerConfigParser.ConfigEntry,
+ ps: PrintStream
+ ) {
+ val strParmIt = protoLogMessage.strParamsList.iterator()
+ val longParamsIt = protoLogMessage.sint64ParamsList.iterator()
+ val doubleParamsIt = protoLogMessage.doubleParamsList.iterator()
+ val boolParamsIt = protoLogMessage.booleanParamsList.iterator()
+ val args = mutableListOf<Any>()
+ val format = configEntry.messageString
+ val argTypes = LogDataType.parseFormatString(format)
+ try {
+ argTypes.forEach {
+ when (it) {
+ LogDataType.BOOLEAN -> args.add(boolParamsIt.next())
+ LogDataType.LONG -> args.add(longParamsIt.next())
+ LogDataType.DOUBLE -> args.add(doubleParamsIt.next())
+ LogDataType.STRING -> args.add(strParmIt.next())
+ null -> throw NullPointerException()
+ }
+ }
+ } catch (ex: NoSuchElementException) {
+ throw InvalidFormatStringException("Invalid format string in config", ex)
+ }
+ if (strParmIt.hasNext() || longParamsIt.hasNext() ||
+ doubleParamsIt.hasNext() || boolParamsIt.hasNext()) {
+ throw RuntimeException("Invalid format string in config - no enough matchers")
+ }
+ val formatted = format.format(*(args.toTypedArray()))
+ ps.print("${configEntry.level} ${configEntry.tag}: $formatted\n")
+ }
+
+ private fun printUnformatted(protoLogMessage: ProtoLogMessage, ps: PrintStream, tag: String) {
+ ps.println("$tag: ${protoLogMessage.messageHash} - ${protoLogMessage.strParamsList}" +
+ " ${protoLogMessage.sint64ParamsList} ${protoLogMessage.doubleParamsList}" +
+ " ${protoLogMessage.booleanParamsList}")
+ }
+
+ fun parse(protoLogInput: InputStream, jsonConfigInput: InputStream, ps: PrintStream) {
+ val jsonReader = JsonReader(BufferedReader(InputStreamReader(jsonConfigInput)))
+ val config = configParser.parseConfig(jsonReader)
+ val protoLog = ProtoLogFileProto.parseFrom(protoLogInput)
+
+ if (protoLog.magicNumber != magicNumber) {
+ throw InvalidInputException("ProtoLog file magic number is invalid.")
+ }
+ if (protoLog.version != Constants.VERSION) {
+ throw InvalidInputException("ProtoLog file version not supported by this tool," +
+ " log version ${protoLog.version}, viewer version ${Constants.VERSION}")
+ }
+
+ protoLog.logList.forEach { log ->
+ printTime(log.elapsedRealtimeNanos, protoLog.realTimeToElapsedTimeOffsetMillis, ps)
+ if (log.messageHash !in config) {
+ printUnformatted(log, ps, "UNKNOWN")
+ } else {
+ val conf = config.getValue(log.messageHash)
+ try {
+ printFormatted(log, conf, ps)
+ } catch (ex: Exception) {
+ printUnformatted(log, ps, "INVALID")
+ }
+ }
+ }
+ }
+}
diff --git a/tools/protologtool/src/com/android/protolog/tool/ParsingContext.kt b/tools/protologtool/src/com/android/protolog/tool/ParsingContext.kt
new file mode 100644
index 000000000000..c6aedfc3093e
--- /dev/null
+++ b/tools/protologtool/src/com/android/protolog/tool/ParsingContext.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.protolog.tool
+
+import com.github.javaparser.ast.Node
+
+data class ParsingContext(val filePath: String, val lineNumber: Int) {
+ constructor(filePath: String, node: Node)
+ : this(filePath, if (node.range.isPresent) node.range.get().begin.line else -1)
+
+ constructor() : this("", -1)
+}
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt
new file mode 100644
index 000000000000..2181cf680f6c
--- /dev/null
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.protolog.tool
+
+import com.github.javaparser.ast.CompilationUnit
+import com.github.javaparser.ast.expr.Expression
+import com.github.javaparser.ast.expr.FieldAccessExpr
+import com.github.javaparser.ast.expr.MethodCallExpr
+import com.github.javaparser.ast.expr.NameExpr
+
+/**
+ * Helper class for visiting all ProtoLog calls.
+ * For every valid call in the given {@code CompilationUnit} a {@code ProtoLogCallVisitor} callback
+ * is executed.
+ */
+open class ProtoLogCallProcessor(
+ private val protoLogClassName: String,
+ private val protoLogGroupClassName: String,
+ private val groupMap: Map<String, LogGroup>
+) {
+ private val protoLogSimpleClassName = protoLogClassName.substringAfterLast('.')
+ private val protoLogGroupSimpleClassName = protoLogGroupClassName.substringAfterLast('.')
+
+ private fun getLogGroupName(
+ expr: Expression,
+ isClassImported: Boolean,
+ staticImports: Set<String>,
+ fileName: String
+ ): String {
+ val context = ParsingContext(fileName, expr)
+ return when (expr) {
+ is NameExpr -> when {
+ expr.nameAsString in staticImports -> expr.nameAsString
+ else ->
+ throw InvalidProtoLogCallException("Unknown/not imported ProtoLogGroup: $expr",
+ context)
+ }
+ is FieldAccessExpr -> when {
+ expr.scope.toString() == protoLogGroupClassName
+ || isClassImported &&
+ expr.scope.toString() == protoLogGroupSimpleClassName -> expr.nameAsString
+ else ->
+ throw InvalidProtoLogCallException("Unknown/not imported ProtoLogGroup: $expr",
+ context)
+ }
+ else -> throw InvalidProtoLogCallException("Invalid group argument " +
+ "- must be ProtoLogGroup enum member reference: $expr", context)
+ }
+ }
+
+ private fun isProtoCall(
+ call: MethodCallExpr,
+ isLogClassImported: Boolean,
+ staticLogImports: Collection<String>
+ ): Boolean {
+ return call.scope.isPresent && call.scope.get().toString() == protoLogClassName ||
+ isLogClassImported && call.scope.isPresent &&
+ call.scope.get().toString() == protoLogSimpleClassName ||
+ !call.scope.isPresent && staticLogImports.contains(call.name.toString())
+ }
+
+ open fun process(code: CompilationUnit, callVisitor: ProtoLogCallVisitor?, fileName: String):
+ CompilationUnit {
+ CodeUtils.checkWildcardStaticImported(code, protoLogClassName, fileName)
+ CodeUtils.checkWildcardStaticImported(code, protoLogGroupClassName, fileName)
+
+ val isLogClassImported = CodeUtils.isClassImportedOrSamePackage(code, protoLogClassName)
+ val staticLogImports = CodeUtils.staticallyImportedMethods(code, protoLogClassName)
+ val isGroupClassImported = CodeUtils.isClassImportedOrSamePackage(code,
+ protoLogGroupClassName)
+ val staticGroupImports = CodeUtils.staticallyImportedMethods(code, protoLogGroupClassName)
+
+ code.findAll(MethodCallExpr::class.java)
+ .filter { call ->
+ isProtoCall(call, isLogClassImported, staticLogImports)
+ }.forEach { call ->
+ val context = ParsingContext(fileName, call)
+ if (call.arguments.size < 2) {
+ throw InvalidProtoLogCallException("Method signature does not match " +
+ "any ProtoLog method: $call", context)
+ }
+
+ val messageString = CodeUtils.concatMultilineString(call.getArgument(1),
+ context)
+ val groupNameArg = call.getArgument(0)
+ val groupName =
+ getLogGroupName(groupNameArg, isGroupClassImported,
+ staticGroupImports, fileName)
+ if (groupName !in groupMap) {
+ throw InvalidProtoLogCallException("Unknown group argument " +
+ "- not a ProtoLogGroup enum member: $call", context)
+ }
+
+ callVisitor?.processCall(call, messageString, LogLevel.getLevelForMethodName(
+ call.name.toString(), call, context), groupMap.getValue(groupName))
+ }
+ return code
+ }
+}
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt
new file mode 100644
index 000000000000..aa58b69d61cb
--- /dev/null
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.protolog.tool
+
+import com.github.javaparser.ast.expr.MethodCallExpr
+
+interface ProtoLogCallVisitor {
+ fun processCall(call: MethodCallExpr, messageString: String, level: LogLevel, group: LogGroup)
+}
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogGroupReader.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogGroupReader.kt
new file mode 100644
index 000000000000..75493b6427cb
--- /dev/null
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogGroupReader.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.protolog.tool
+
+import com.android.protolog.tool.Constants.ENUM_VALUES_METHOD
+import com.android.server.protolog.common.IProtoLogGroup
+import java.io.File
+import java.net.URLClassLoader
+
+class ProtoLogGroupReader {
+ private fun getClassloaderForJar(jarPath: String): ClassLoader {
+ val jarFile = File(jarPath)
+ val url = jarFile.toURI().toURL()
+ return URLClassLoader(arrayOf(url), ProtoLogGroupReader::class.java.classLoader)
+ }
+
+ private fun getEnumValues(clazz: Class<*>): List<IProtoLogGroup> {
+ val valuesMethod = clazz.getMethod(ENUM_VALUES_METHOD)
+ @Suppress("UNCHECKED_CAST")
+ return (valuesMethod.invoke(null) as Array<IProtoLogGroup>).toList()
+ }
+
+ fun loadFromJar(jarPath: String, className: String): Map<String, LogGroup> {
+ try {
+ val classLoader = getClassloaderForJar(jarPath)
+ val clazz = classLoader.loadClass(className)
+ val values = getEnumValues(clazz)
+ return values.map { group ->
+ group.name() to
+ LogGroup(group.name(), group.isEnabled, group.isLogToLogcat, group.tag)
+ }.toMap()
+ } catch (ex: ReflectiveOperationException) {
+ throw RuntimeException("Unable to load ProtoLogGroup enum class", ex)
+ }
+ }
+}
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
new file mode 100644
index 000000000000..629f720314b2
--- /dev/null
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.protolog.tool
+
+import com.android.protolog.tool.CommandOptions.Companion.USAGE
+import com.github.javaparser.ParseProblemException
+import com.github.javaparser.ParserConfiguration
+import com.github.javaparser.StaticJavaParser
+import com.github.javaparser.ast.CompilationUnit
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileOutputStream
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
+import java.util.jar.JarOutputStream
+import java.util.zip.ZipEntry
+import kotlin.system.exitProcess
+
+object ProtoLogTool {
+ private fun showHelpAndExit() {
+ println(USAGE)
+ exitProcess(-1)
+ }
+
+ private fun containsProtoLogText(source: String, protoLogClassName: String): Boolean {
+ val protoLogSimpleClassName = protoLogClassName.substringAfterLast('.')
+ return source.contains(protoLogSimpleClassName)
+ }
+
+ private fun processClasses(command: CommandOptions) {
+ val groups = ProtoLogGroupReader()
+ .loadFromJar(command.protoLogGroupsJarArg, command.protoLogGroupsClassNameArg)
+ val out = FileOutputStream(command.outputSourceJarArg)
+ val outJar = JarOutputStream(out)
+ val processor = ProtoLogCallProcessor(command.protoLogClassNameArg,
+ command.protoLogGroupsClassNameArg, groups)
+
+ val executor = newThreadPool()
+
+ command.javaSourceArgs.map { path ->
+ executor.submitCallable {
+ val transformer = SourceTransformer(command.protoLogImplClassNameArg,
+ command.protoLogCacheClassNameArg, processor)
+ val file = File(path)
+ val text = file.readText()
+ val outSrc = try {
+ val code = tryParse(text, path)
+ if (containsProtoLogText(text, command.protoLogClassNameArg)) {
+ transformer.processClass(text, path, code)
+ } else {
+ text
+ }
+ } catch (ex: ParsingException) {
+ // If we cannot parse this file, skip it (and log why). Compilation will fail
+ // in a subsequent build step.
+ println("\n${ex.message}\n")
+ text
+ }
+ path to outSrc
+ }
+ }.map { future ->
+ val (path, outSrc) = future.get()
+ outJar.putNextEntry(ZipEntry(path))
+ outJar.write(outSrc.toByteArray())
+ outJar.closeEntry()
+ }
+
+ executor.shutdown()
+
+ val cacheSplit = command.protoLogCacheClassNameArg.split(".")
+ val cacheName = cacheSplit.last()
+ val cachePackage = cacheSplit.dropLast(1).joinToString(".")
+ val cachePath = "gen/${cacheSplit.joinToString("/")}.java"
+
+ outJar.putNextEntry(ZipEntry(cachePath))
+ outJar.write(generateLogGroupCache(cachePackage, cacheName, groups,
+ command.protoLogImplClassNameArg, command.protoLogGroupsClassNameArg).toByteArray())
+
+ outJar.close()
+ out.close()
+ }
+
+ fun generateLogGroupCache(
+ cachePackage: String,
+ cacheName: String,
+ groups: Map<String, LogGroup>,
+ protoLogImplClassName: String,
+ protoLogGroupsClassName: String
+ ): String {
+ val fields = groups.values.map {
+ "public static boolean ${it.name}_enabled = false;"
+ }.joinToString("\n")
+
+ val updates = groups.values.map {
+ "${it.name}_enabled = " +
+ "$protoLogImplClassName.isEnabled($protoLogGroupsClassName.${it.name});"
+ }.joinToString("\n")
+
+ return """
+ package $cachePackage;
+
+ public class $cacheName {
+${fields.replaceIndent(" ")}
+
+ static {
+ $protoLogImplClassName.sCacheUpdater = $cacheName::update;
+ update();
+ }
+
+ static void update() {
+${updates.replaceIndent(" ")}
+ }
+ }
+ """.trimIndent()
+ }
+
+ private fun tryParse(code: String, fileName: String): CompilationUnit {
+ try {
+ return StaticJavaParser.parse(code)
+ } catch (ex: ParseProblemException) {
+ val problem = ex.problems.first()
+ throw ParsingException("Java parsing erro" +
+ "r: ${problem.verboseMessage}",
+ ParsingContext(fileName, problem.location.orElse(null)
+ ?.begin?.range?.orElse(null)?.begin?.line
+ ?: 0))
+ }
+ }
+
+ private fun viewerConf(command: CommandOptions) {
+ val groups = ProtoLogGroupReader()
+ .loadFromJar(command.protoLogGroupsJarArg, command.protoLogGroupsClassNameArg)
+ val processor = ProtoLogCallProcessor(command.protoLogClassNameArg,
+ command.protoLogGroupsClassNameArg, groups)
+ val builder = ViewerConfigBuilder(processor)
+
+ val executor = newThreadPool()
+
+ command.javaSourceArgs.map { path ->
+ executor.submitCallable {
+ val file = File(path)
+ val text = file.readText()
+ if (containsProtoLogText(text, command.protoLogClassNameArg)) {
+ try {
+ val code = tryParse(text, path)
+ val pack = if (code.packageDeclaration.isPresent) code.packageDeclaration
+ .get().nameAsString else ""
+ val newPath = pack.replace('.', '/') + '/' + file.name
+ builder.findLogCalls(code, newPath)
+ } catch (ex: ParsingException) {
+ // If we cannot parse this file, skip it (and log why). Compilation will fail
+ // in a subsequent build step.
+ println("\n${ex.message}\n")
+ null
+ }
+ } else {
+ null
+ }
+ }
+ }.forEach { future ->
+ builder.addLogCalls(future.get() ?: return@forEach)
+ }
+
+ executor.shutdown()
+
+ val out = FileOutputStream(command.viewerConfigJsonArg)
+ out.write(builder.build().toByteArray())
+ out.close()
+ }
+
+ private fun read(command: CommandOptions) {
+ LogParser(ViewerConfigParser())
+ .parse(FileInputStream(command.logProtofileArg),
+ FileInputStream(command.viewerConfigJsonArg), System.out)
+ }
+
+ @JvmStatic
+ fun main(args: Array<String>) {
+ StaticJavaParser.setConfiguration(ParserConfiguration().apply {
+ setLanguageLevel(ParserConfiguration.LanguageLevel.RAW)
+ setAttributeComments(false)
+ })
+
+ try {
+ val command = CommandOptions(args)
+ when (command.command) {
+ CommandOptions.TRANSFORM_CALLS_CMD -> processClasses(command)
+ CommandOptions.GENERATE_CONFIG_CMD -> viewerConf(command)
+ CommandOptions.READ_LOG_CMD -> read(command)
+ }
+ } catch (ex: InvalidCommandException) {
+ println("\n${ex.message}\n")
+ showHelpAndExit()
+ } catch (ex: CodeProcessingException) {
+ println("\n${ex.message}\n")
+ exitProcess(1)
+ }
+ }
+}
+
+private fun <T> ExecutorService.submitCallable(f: () -> T) = submit(f)
+
+private fun newThreadPool() = Executors.newFixedThreadPool(
+ Runtime.getRuntime().availableProcessors())
diff --git a/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
new file mode 100644
index 000000000000..0ad8091f97a2
--- /dev/null
+++ b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
@@ -0,0 +1,231 @@
+/*
+ * 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.protolog.tool
+
+import com.android.server.protolog.common.LogDataType
+import com.github.javaparser.StaticJavaParser
+import com.github.javaparser.ast.CompilationUnit
+import com.github.javaparser.ast.NodeList
+import com.github.javaparser.ast.body.VariableDeclarator
+import com.github.javaparser.ast.expr.BooleanLiteralExpr
+import com.github.javaparser.ast.expr.CastExpr
+import com.github.javaparser.ast.expr.Expression
+import com.github.javaparser.ast.expr.FieldAccessExpr
+import com.github.javaparser.ast.expr.IntegerLiteralExpr
+import com.github.javaparser.ast.expr.MethodCallExpr
+import com.github.javaparser.ast.expr.NameExpr
+import com.github.javaparser.ast.expr.NullLiteralExpr
+import com.github.javaparser.ast.expr.SimpleName
+import com.github.javaparser.ast.expr.TypeExpr
+import com.github.javaparser.ast.expr.VariableDeclarationExpr
+import com.github.javaparser.ast.stmt.BlockStmt
+import com.github.javaparser.ast.stmt.ExpressionStmt
+import com.github.javaparser.ast.stmt.IfStmt
+import com.github.javaparser.ast.type.ArrayType
+import com.github.javaparser.ast.type.ClassOrInterfaceType
+import com.github.javaparser.ast.type.PrimitiveType
+import com.github.javaparser.ast.type.Type
+import com.github.javaparser.printer.PrettyPrinter
+import com.github.javaparser.printer.PrettyPrinterConfiguration
+
+class SourceTransformer(
+ protoLogImplClassName: String,
+ protoLogCacheClassName: String,
+ private val protoLogCallProcessor: ProtoLogCallProcessor
+) : ProtoLogCallVisitor {
+ override fun processCall(
+ call: MethodCallExpr,
+ messageString: String,
+ level: LogLevel,
+ group: LogGroup
+ ) {
+ // Input format: ProtoLog.e(GROUP, "msg %d", arg)
+ if (!call.parentNode.isPresent) {
+ // Should never happen
+ throw RuntimeException("Unable to process log call $call " +
+ "- no parent node in AST")
+ }
+ if (call.parentNode.get() !is ExpressionStmt) {
+ // Should never happen
+ throw RuntimeException("Unable to process log call $call " +
+ "- parent node in AST is not an ExpressionStmt")
+ }
+ val parentStmt = call.parentNode.get() as ExpressionStmt
+ if (!parentStmt.parentNode.isPresent) {
+ // Should never happen
+ throw RuntimeException("Unable to process log call $call " +
+ "- no grandparent node in AST")
+ }
+ val ifStmt: IfStmt
+ if (group.enabled) {
+ val hash = CodeUtils.hash(fileName, messageString, level, group)
+ val newCall = call.clone()
+ if (!group.textEnabled) {
+ // Remove message string if text logging is not enabled by default.
+ // Out: ProtoLog.e(GROUP, null, arg)
+ newCall.arguments[1].replace(NameExpr("null"))
+ }
+ // Insert message string hash as a second argument.
+ // Out: ProtoLog.e(GROUP, 1234, null, arg)
+ newCall.arguments.add(1, IntegerLiteralExpr(hash))
+ val argTypes = LogDataType.parseFormatString(messageString)
+ val typeMask = LogDataType.logDataTypesToBitMask(argTypes)
+ // Insert bitmap representing which Number parameters are to be considered as
+ // floating point numbers.
+ // Out: ProtoLog.e(GROUP, 1234, 0, null, arg)
+ newCall.arguments.add(2, IntegerLiteralExpr(typeMask))
+ // Replace call to a stub method with an actual implementation.
+ // Out: com.android.server.protolog.ProtoLogImpl.e(GROUP, 1234, null, arg)
+ newCall.setScope(protoLogImplClassNode)
+ // Create a call to ProtoLog$Cache.GROUP_enabled
+ // Out: com.android.server.protolog.ProtoLog$Cache.GROUP_enabled
+ val isLogEnabled = FieldAccessExpr(protoLogCacheClassNode, "${group.name}_enabled")
+ if (argTypes.size != call.arguments.size - 2) {
+ throw InvalidProtoLogCallException(
+ "Number of arguments (${argTypes.size} does not mach format" +
+ " string in: $call", ParsingContext(fileName, call))
+ }
+ val blockStmt = BlockStmt()
+ if (argTypes.isNotEmpty()) {
+ // Assign every argument to a variable to check its type in compile time
+ // (this is assignment is optimized-out by dex tool, there is no runtime impact)/
+ // Out: long protoLogParam0 = arg
+ argTypes.forEachIndexed { idx, type ->
+ val varName = "protoLogParam$idx"
+ val declaration = VariableDeclarator(getASTTypeForDataType(type), varName,
+ getConversionForType(type)(newCall.arguments[idx + 4].clone()))
+ blockStmt.addStatement(ExpressionStmt(VariableDeclarationExpr(declaration)))
+ newCall.setArgument(idx + 4, NameExpr(SimpleName(varName)))
+ }
+ } else {
+ // Assign (Object[])null as the vararg parameter to prevent allocating an empty
+ // object array.
+ val nullArray = CastExpr(ArrayType(objectType), NullLiteralExpr())
+ newCall.addArgument(nullArray)
+ }
+ blockStmt.addStatement(ExpressionStmt(newCall))
+ // Create an IF-statement with the previously created condition.
+ // Out: if (com.android.server.protolog.ProtoLogImpl.isEnabled(GROUP)) {
+ // long protoLogParam0 = arg;
+ // com.android.server.protolog.ProtoLogImpl.e(GROUP, 1234, 0, null, protoLogParam0);
+ // }
+ ifStmt = IfStmt(isLogEnabled, blockStmt, null)
+ } else {
+ // Surround with if (false).
+ val newCall = parentStmt.clone()
+ ifStmt = IfStmt(BooleanLiteralExpr(false), BlockStmt(NodeList(newCall)), null)
+ newCall.setBlockComment(" ${group.name} is disabled ")
+ }
+ // Inline the new statement.
+ val printedIfStmt = inlinePrinter.print(ifStmt)
+ // Append blank lines to preserve line numbering in file (to allow debugging)
+ val parentRange = parentStmt.range.get()
+ val newLines = parentRange.end.line - parentRange.begin.line
+ val newStmt = printedIfStmt.substringBeforeLast('}') + ("\n".repeat(newLines)) + '}'
+ // pre-workaround code, see explanation below
+ /*
+ val inlinedIfStmt = StaticJavaParser.parseStatement(newStmt)
+ LexicalPreservingPrinter.setup(inlinedIfStmt)
+ // Replace the original call.
+ if (!parentStmt.replace(inlinedIfStmt)) {
+ // Should never happen
+ throw RuntimeException("Unable to process log call $call " +
+ "- unable to replace the call.")
+ }
+ */
+ /** Workaround for a bug in JavaParser (AST tree invalid after replacing a node when using
+ * LexicalPreservingPrinter (https://github.com/javaparser/javaparser/issues/2290).
+ * Replace the code below with the one commended-out above one the issue is resolved. */
+ if (!parentStmt.range.isPresent) {
+ // Should never happen
+ throw RuntimeException("Unable to process log call $call " +
+ "- unable to replace the call.")
+ }
+ val range = parentStmt.range.get()
+ val begin = range.begin.line - 1
+ val oldLines = processedCode.subList(begin, range.end.line)
+ val oldCode = oldLines.joinToString("\n")
+ val newCode = oldCode.replaceRange(
+ offsets[begin] + range.begin.column - 1,
+ oldCode.length - oldLines.lastOrNull()!!.length +
+ range.end.column + offsets[range.end.line - 1], newStmt)
+ newCode.split("\n").forEachIndexed { idx, line ->
+ offsets[begin + idx] += line.length - processedCode[begin + idx].length
+ processedCode[begin + idx] = line
+ }
+ }
+
+ private val inlinePrinter: PrettyPrinter
+ private val objectType = StaticJavaParser.parseClassOrInterfaceType("Object")
+
+ init {
+ val config = PrettyPrinterConfiguration()
+ config.endOfLineCharacter = " "
+ config.indentSize = 0
+ config.tabWidth = 1
+ inlinePrinter = PrettyPrinter(config)
+ }
+
+ companion object {
+ private val stringType: ClassOrInterfaceType =
+ StaticJavaParser.parseClassOrInterfaceType("String")
+
+ fun getASTTypeForDataType(type: Int): Type {
+ return when (type) {
+ LogDataType.STRING -> stringType.clone()
+ LogDataType.LONG -> PrimitiveType.longType()
+ LogDataType.DOUBLE -> PrimitiveType.doubleType()
+ LogDataType.BOOLEAN -> PrimitiveType.booleanType()
+ else -> {
+ // Should never happen.
+ throw RuntimeException("Invalid LogDataType")
+ }
+ }
+ }
+
+ fun getConversionForType(type: Int): (Expression) -> Expression {
+ return when (type) {
+ LogDataType.STRING -> { expr ->
+ MethodCallExpr(TypeExpr(StaticJavaParser.parseClassOrInterfaceType("String")),
+ SimpleName("valueOf"), NodeList(expr))
+ }
+ else -> { expr -> expr }
+ }
+ }
+ }
+
+ private val protoLogImplClassNode =
+ StaticJavaParser.parseExpression<FieldAccessExpr>(protoLogImplClassName)
+ private val protoLogCacheClassNode =
+ StaticJavaParser.parseExpression<FieldAccessExpr>(protoLogCacheClassName)
+ private var processedCode: MutableList<String> = mutableListOf()
+ private var offsets: IntArray = IntArray(0)
+ private var fileName: String = ""
+
+ fun processClass(
+ code: String,
+ path: String,
+ compilationUnit: CompilationUnit =
+ StaticJavaParser.parse(code)
+ ): String {
+ fileName = path
+ processedCode = code.split('\n').toMutableList()
+ offsets = IntArray(processedCode.size)
+ protoLogCallProcessor.process(compilationUnit, this, fileName)
+ return processedCode.joinToString("\n")
+ }
+}
diff --git a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigBuilder.kt b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigBuilder.kt
new file mode 100644
index 000000000000..c1008263c083
--- /dev/null
+++ b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigBuilder.kt
@@ -0,0 +1,120 @@
+/*
+ * 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.protolog.tool
+
+import com.android.json.stream.JsonWriter
+import com.github.javaparser.ast.CompilationUnit
+import com.android.protolog.tool.Constants.VERSION
+import com.github.javaparser.ast.expr.MethodCallExpr
+import java.io.StringWriter
+
+class ViewerConfigBuilder(
+ private val processor: ProtoLogCallProcessor
+) {
+ private fun addLogCall(logCall: LogCall, context: ParsingContext) {
+ val group = logCall.logGroup
+ val messageString = logCall.messageString
+ if (group.enabled) {
+ val key = logCall.key()
+ if (statements.containsKey(key)) {
+ if (statements[key] != logCall) {
+ throw HashCollisionException(
+ "Please modify the log message \"$messageString\" " +
+ "or \"${statements[key]}\" - their hashes are equal.", context)
+ }
+ } else {
+ groups.add(group)
+ statements[key] = logCall
+ }
+ }
+ }
+
+ private val statements: MutableMap<Int, LogCall> = mutableMapOf()
+ private val groups: MutableSet<LogGroup> = mutableSetOf()
+
+ fun findLogCalls(unit: CompilationUnit, fileName: String): List<Pair<LogCall, ParsingContext>> {
+ val calls = mutableListOf<Pair<LogCall, ParsingContext>>()
+ val visitor = object : ProtoLogCallVisitor {
+ override fun processCall(
+ call: MethodCallExpr,
+ messageString: String,
+ level: LogLevel,
+ group: LogGroup
+ ) {
+ val logCall = LogCall(messageString, level, group, fileName)
+ val context = ParsingContext(fileName, call)
+ calls.add(logCall to context)
+ }
+ }
+ processor.process(unit, visitor, fileName)
+
+ return calls
+ }
+
+ fun addLogCalls(calls: List<Pair<LogCall, ParsingContext>>) {
+ calls.forEach { (logCall, context) ->
+ addLogCall(logCall, context)
+ }
+ }
+
+ fun build(): String {
+ val stringWriter = StringWriter()
+ val writer = JsonWriter(stringWriter)
+ writer.setIndent(" ")
+ writer.beginObject()
+ writer.name("version")
+ writer.value(VERSION)
+ writer.name("messages")
+ writer.beginObject()
+ statements.toSortedMap().forEach { (key, value) ->
+ writer.name(key.toString())
+ writer.beginObject()
+ writer.name("message")
+ writer.value(value.messageString)
+ writer.name("level")
+ writer.value(value.logLevel.name)
+ writer.name("group")
+ writer.value(value.logGroup.name)
+ writer.name("at")
+ writer.value(value.position)
+ writer.endObject()
+ }
+ writer.endObject()
+ writer.name("groups")
+ writer.beginObject()
+ groups.toSortedSet(Comparator { o1, o2 -> o1.name.compareTo(o2.name) }).forEach { group ->
+ writer.name(group.name)
+ writer.beginObject()
+ writer.name("tag")
+ writer.value(group.tag)
+ writer.endObject()
+ }
+ writer.endObject()
+ writer.endObject()
+ stringWriter.buffer.append('\n')
+ return stringWriter.toString()
+ }
+
+ data class LogCall(
+ val messageString: String,
+ val logLevel: LogLevel,
+ val logGroup: LogGroup,
+ val position: String
+ ) {
+ fun key() = CodeUtils.hash(position, messageString, logLevel, logGroup)
+ }
+}
diff --git a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigParser.kt b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigParser.kt
new file mode 100644
index 000000000000..7278db0094e6
--- /dev/null
+++ b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigParser.kt
@@ -0,0 +1,122 @@
+/*
+ * 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.protolog.tool
+
+import com.android.json.stream.JsonReader
+
+open class ViewerConfigParser {
+ data class MessageEntry(
+ val messageString: String,
+ val level: String,
+ val groupName: String
+ )
+
+ fun parseMessage(jsonReader: JsonReader): MessageEntry {
+ jsonReader.beginObject()
+ var message: String? = null
+ var level: String? = null
+ var groupName: String? = null
+ while (jsonReader.hasNext()) {
+ when (jsonReader.nextName()) {
+ "message" -> message = jsonReader.nextString()
+ "level" -> level = jsonReader.nextString()
+ "group" -> groupName = jsonReader.nextString()
+ else -> jsonReader.skipValue()
+ }
+ }
+ jsonReader.endObject()
+ if (message.isNullOrBlank() || level.isNullOrBlank() || groupName.isNullOrBlank()) {
+ throw InvalidViewerConfigException("Invalid message entry in viewer config")
+ }
+ return MessageEntry(message, level, groupName)
+ }
+
+ data class GroupEntry(val tag: String)
+
+ fun parseGroup(jsonReader: JsonReader): GroupEntry {
+ jsonReader.beginObject()
+ var tag: String? = null
+ while (jsonReader.hasNext()) {
+ when (jsonReader.nextName()) {
+ "tag" -> tag = jsonReader.nextString()
+ else -> jsonReader.skipValue()
+ }
+ }
+ jsonReader.endObject()
+ if (tag.isNullOrBlank()) {
+ throw InvalidViewerConfigException("Invalid group entry in viewer config")
+ }
+ return GroupEntry(tag)
+ }
+
+ fun parseMessages(jsonReader: JsonReader): Map<Int, MessageEntry> {
+ val config: MutableMap<Int, MessageEntry> = mutableMapOf()
+ jsonReader.beginObject()
+ while (jsonReader.hasNext()) {
+ val key = jsonReader.nextName()
+ val hash = key.toIntOrNull()
+ ?: throw InvalidViewerConfigException("Invalid key in messages viewer config")
+ config[hash] = parseMessage(jsonReader)
+ }
+ jsonReader.endObject()
+ return config
+ }
+
+ fun parseGroups(jsonReader: JsonReader): Map<String, GroupEntry> {
+ val config: MutableMap<String, GroupEntry> = mutableMapOf()
+ jsonReader.beginObject()
+ while (jsonReader.hasNext()) {
+ val key = jsonReader.nextName()
+ config[key] = parseGroup(jsonReader)
+ }
+ jsonReader.endObject()
+ return config
+ }
+
+ data class ConfigEntry(val messageString: String, val level: String, val tag: String)
+
+ open fun parseConfig(jsonReader: JsonReader): Map<Int, ConfigEntry> {
+ var messages: Map<Int, MessageEntry>? = null
+ var groups: Map<String, GroupEntry>? = null
+ var version: String? = null
+
+ jsonReader.beginObject()
+ while (jsonReader.hasNext()) {
+ when (jsonReader.nextName()) {
+ "messages" -> messages = parseMessages(jsonReader)
+ "groups" -> groups = parseGroups(jsonReader)
+ "version" -> version = jsonReader.nextString()
+
+ else -> jsonReader.skipValue()
+ }
+ }
+ jsonReader.endObject()
+ if (messages == null || groups == null || version == null) {
+ throw InvalidViewerConfigException("Invalid config - definitions missing")
+ }
+ if (version != Constants.VERSION) {
+ throw InvalidViewerConfigException("Viewer config version not supported by this tool," +
+ " config version $version, viewer version ${Constants.VERSION}")
+ }
+ return messages.map { msg ->
+ msg.key to ConfigEntry(
+ msg.value.messageString, msg.value.level, groups[msg.value.groupName]?.tag
+ ?: throw InvalidViewerConfigException(
+ "Group definition missing for ${msg.value.groupName}"))
+ }.toMap()
+ }
+}
diff --git a/tools/protologtool/src/com/android/protolog/tool/exceptions.kt b/tools/protologtool/src/com/android/protolog/tool/exceptions.kt
new file mode 100644
index 000000000000..ae00df123353
--- /dev/null
+++ b/tools/protologtool/src/com/android/protolog/tool/exceptions.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.protolog.tool
+
+import java.lang.Exception
+
+open class CodeProcessingException(message: String, context: ParsingContext)
+ : Exception("Code processing error in ${context.filePath}:${context.lineNumber}:\n" +
+ " $message")
+
+class HashCollisionException(message: String, context: ParsingContext) :
+ CodeProcessingException(message, context)
+
+class IllegalImportException(message: String, context: ParsingContext) :
+ CodeProcessingException("Illegal import: $message", context)
+
+class InvalidProtoLogCallException(message: String, context: ParsingContext)
+ : CodeProcessingException("InvalidProtoLogCall: $message", context)
+
+class ParsingException(message: String, context: ParsingContext)
+ : CodeProcessingException(message, context)
+
+class InvalidViewerConfigException(message: String) : Exception(message)
+
+class InvalidInputException(message: String) : Exception(message)
+
+class InvalidCommandException(message: String) : Exception(message)
diff --git a/tools/protologtool/src/com/android/protologtool/CodeUtils.kt b/tools/protologtool/src/com/android/protologtool/CodeUtils.kt
deleted file mode 100644
index facca6290c91..000000000000
--- a/tools/protologtool/src/com/android/protologtool/CodeUtils.kt
+++ /dev/null
@@ -1,135 +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.protologtool
-
-import com.github.javaparser.StaticJavaParser
-import com.github.javaparser.ast.CompilationUnit
-import com.github.javaparser.ast.ImportDeclaration
-import com.github.javaparser.ast.NodeList
-import com.github.javaparser.ast.expr.BinaryExpr
-import com.github.javaparser.ast.expr.Expression
-import com.github.javaparser.ast.expr.MethodCallExpr
-import com.github.javaparser.ast.expr.SimpleName
-import com.github.javaparser.ast.expr.StringLiteralExpr
-import com.github.javaparser.ast.expr.TypeExpr
-import com.github.javaparser.ast.type.PrimitiveType
-import com.github.javaparser.ast.type.Type
-
-object CodeUtils {
- /**
- * Returns a stable hash of a string.
- * We reimplement String::hashCode() for readability reasons.
- */
- fun hash(str: String, level: LogLevel): Int {
- return (level.name + str).map { c -> c.toInt() }.reduce { h, c -> h * 31 + c }
- }
-
- fun isWildcardStaticImported(code: CompilationUnit, className: String): Boolean {
- return code.findAll(ImportDeclaration::class.java)
- .any { im -> im.isStatic && im.isAsterisk && im.name.toString() == className }
- }
-
- fun isClassImportedOrSamePackage(code: CompilationUnit, className: String): Boolean {
- val packageName = className.substringBeforeLast('.')
- return code.packageDeclaration.isPresent &&
- code.packageDeclaration.get().nameAsString == packageName ||
- code.findAll(ImportDeclaration::class.java)
- .any { im ->
- !im.isStatic &&
- ((!im.isAsterisk && im.name.toString() == className) ||
- (im.isAsterisk && im.name.toString() == packageName))
- }
- }
-
- fun staticallyImportedMethods(code: CompilationUnit, className: String): Set<String> {
- return code.findAll(ImportDeclaration::class.java)
- .filter { im ->
- im.isStatic &&
- im.name.toString().substringBeforeLast('.') == className
- }
- .map { im -> im.name.toString().substringAfterLast('.') }.toSet()
- }
-
- fun concatMultilineString(expr: Expression): String {
- return when (expr) {
- is StringLiteralExpr -> expr.asString()
- is BinaryExpr -> when {
- expr.operator == BinaryExpr.Operator.PLUS ->
- concatMultilineString(expr.left) + concatMultilineString(expr.right)
- else -> throw InvalidProtoLogCallException(
- "messageString must be a string literal " +
- "or concatenation of string literals.", expr)
- }
- else -> throw InvalidProtoLogCallException("messageString must be a string literal " +
- "or concatenation of string literals.", expr)
- }
- }
-
- enum class LogDataTypes(
- val type: Type,
- val toType: (Expression) -> Expression = { expr -> expr }
- ) {
- // When adding new LogDataType make sure to update {@code logDataTypesToBitMask} accordingly
- STRING(StaticJavaParser.parseClassOrInterfaceType("String"),
- { expr ->
- MethodCallExpr(TypeExpr(StaticJavaParser.parseClassOrInterfaceType("String")),
- SimpleName("valueOf"), NodeList(expr))
- }),
- LONG(PrimitiveType.longType()),
- DOUBLE(PrimitiveType.doubleType()),
- BOOLEAN(PrimitiveType.booleanType());
- }
-
- fun parseFormatString(messageString: String): List<LogDataTypes> {
- val types = mutableListOf<LogDataTypes>()
- var i = 0
- while (i < messageString.length) {
- if (messageString[i] == '%') {
- if (i + 1 >= messageString.length) {
- throw InvalidFormatStringException("Invalid format string in config")
- }
- when (messageString[i + 1]) {
- 'b' -> types.add(CodeUtils.LogDataTypes.BOOLEAN)
- 'd', 'o', 'x' -> types.add(CodeUtils.LogDataTypes.LONG)
- 'f', 'e', 'g' -> types.add(CodeUtils.LogDataTypes.DOUBLE)
- 's' -> types.add(CodeUtils.LogDataTypes.STRING)
- '%' -> {
- }
- else -> throw InvalidFormatStringException("Invalid format string field" +
- " %${messageString[i + 1]}")
- }
- i += 2
- } else {
- i += 1
- }
- }
- return types
- }
-
- fun logDataTypesToBitMask(types: List<LogDataTypes>): Int {
- if (types.size > 16) {
- throw InvalidFormatStringException("Too many log call parameters " +
- "- max 16 parameters supported")
- }
- var mask = 0
- types.forEachIndexed { idx, type ->
- val x = LogDataTypes.values().indexOf(type)
- mask = mask or (x shl (idx * 2))
- }
- return mask
- }
-}
diff --git a/tools/protologtool/src/com/android/protologtool/CommandOptions.kt b/tools/protologtool/src/com/android/protologtool/CommandOptions.kt
deleted file mode 100644
index df49e1566fbc..000000000000
--- a/tools/protologtool/src/com/android/protologtool/CommandOptions.kt
+++ /dev/null
@@ -1,205 +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.protologtool
-
-import java.util.regex.Pattern
-
-class CommandOptions(args: Array<String>) {
- companion object {
- const val TRANSFORM_CALLS_CMD = "transform-protolog-calls"
- const val GENERATE_CONFIG_CMD = "generate-viewer-config"
- const val READ_LOG_CMD = "read-log"
- private val commands = setOf(TRANSFORM_CALLS_CMD, GENERATE_CONFIG_CMD, READ_LOG_CMD)
-
- private const val PROTOLOG_CLASS_PARAM = "--protolog-class"
- private const val PROTOLOGIMPL_CLASS_PARAM = "--protolog-impl-class"
- private const val PROTOLOGGROUP_CLASS_PARAM = "--loggroups-class"
- private const val PROTOLOGGROUP_JAR_PARAM = "--loggroups-jar"
- private const val VIEWER_CONFIG_JSON_PARAM = "--viewer-conf"
- private const val OUTPUT_SOURCE_JAR_PARAM = "--output-srcjar"
- private val parameters = setOf(PROTOLOG_CLASS_PARAM, PROTOLOGIMPL_CLASS_PARAM,
- PROTOLOGGROUP_CLASS_PARAM, PROTOLOGGROUP_JAR_PARAM, VIEWER_CONFIG_JSON_PARAM,
- OUTPUT_SOURCE_JAR_PARAM)
-
- val USAGE = """
- Usage: ${Constants.NAME} <command> [<args>]
- Available commands:
-
- $TRANSFORM_CALLS_CMD $PROTOLOG_CLASS_PARAM <class name> $PROTOLOGIMPL_CLASS_PARAM
- <class name> $PROTOLOGGROUP_CLASS_PARAM <class name> $PROTOLOGGROUP_JAR_PARAM
- <config.jar> $OUTPUT_SOURCE_JAR_PARAM <output.srcjar> [<input.java>]
- - processes java files replacing stub calls with logging code.
-
- $GENERATE_CONFIG_CMD $PROTOLOG_CLASS_PARAM <class name> $PROTOLOGGROUP_CLASS_PARAM
- <class name> $PROTOLOGGROUP_JAR_PARAM <config.jar> $VIEWER_CONFIG_JSON_PARAM
- <viewer.json> [<input.java>]
- - creates viewer config file from given java files.
-
- $READ_LOG_CMD $VIEWER_CONFIG_JSON_PARAM <viewer.json> <wm_log.pb>
- - translates a binary log to a readable format.
- """.trimIndent()
-
- private fun validateClassName(name: String): String {
- if (!Pattern.matches("^([a-z]+[A-Za-z0-9]*\\.)+([A-Za-z0-9]+)$", name)) {
- throw InvalidCommandException("Invalid class name $name")
- }
- return name
- }
-
- private fun getParam(paramName: String, params: Map<String, String>): String {
- if (!params.containsKey(paramName)) {
- throw InvalidCommandException("Param $paramName required")
- }
- return params.getValue(paramName)
- }
-
- private fun validateNotSpecified(paramName: String, params: Map<String, String>): String {
- if (params.containsKey(paramName)) {
- throw InvalidCommandException("Unsupported param $paramName")
- }
- return ""
- }
-
- private fun validateJarName(name: String): String {
- if (!name.endsWith(".jar")) {
- throw InvalidCommandException("Jar file required, got $name instead")
- }
- return name
- }
-
- private fun validateSrcJarName(name: String): String {
- if (!name.endsWith(".srcjar")) {
- throw InvalidCommandException("Source jar file required, got $name instead")
- }
- return name
- }
-
- private fun validateJSONName(name: String): String {
- if (!name.endsWith(".json")) {
- throw InvalidCommandException("Json file required, got $name instead")
- }
- return name
- }
-
- private fun validateJavaInputList(list: List<String>): List<String> {
- if (list.isEmpty()) {
- throw InvalidCommandException("No java source input files")
- }
- list.forEach { name ->
- if (!name.endsWith(".java")) {
- throw InvalidCommandException("Not a java source file $name")
- }
- }
- return list
- }
-
- private fun validateLogInputList(list: List<String>): String {
- if (list.isEmpty()) {
- throw InvalidCommandException("No log input file")
- }
- if (list.size > 1) {
- throw InvalidCommandException("Only one log input file allowed")
- }
- return list[0]
- }
- }
-
- val protoLogClassNameArg: String
- val protoLogGroupsClassNameArg: String
- val protoLogImplClassNameArg: String
- val protoLogGroupsJarArg: String
- val viewerConfigJsonArg: String
- val outputSourceJarArg: String
- val logProtofileArg: String
- val javaSourceArgs: List<String>
- val command: String
-
- init {
- if (args.isEmpty()) {
- throw InvalidCommandException("No command specified.")
- }
- command = args[0]
- if (command !in commands) {
- throw InvalidCommandException("Unknown command.")
- }
-
- val params: MutableMap<String, String> = mutableMapOf()
- val inputFiles: MutableList<String> = mutableListOf()
-
- var idx = 1
- while (idx < args.size) {
- if (args[idx].startsWith("--")) {
- if (idx + 1 >= args.size) {
- throw InvalidCommandException("No value for ${args[idx]}")
- }
- if (args[idx] !in parameters) {
- throw InvalidCommandException("Unknown parameter ${args[idx]}")
- }
- if (args[idx + 1].startsWith("--")) {
- throw InvalidCommandException("No value for ${args[idx]}")
- }
- if (params.containsKey(args[idx])) {
- throw InvalidCommandException("Duplicated parameter ${args[idx]}")
- }
- params[args[idx]] = args[idx + 1]
- idx += 2
- } else {
- inputFiles.add(args[idx])
- idx += 1
- }
- }
-
- when (command) {
- TRANSFORM_CALLS_CMD -> {
- protoLogClassNameArg = validateClassName(getParam(PROTOLOG_CLASS_PARAM, params))
- protoLogGroupsClassNameArg = validateClassName(getParam(PROTOLOGGROUP_CLASS_PARAM,
- params))
- protoLogImplClassNameArg = validateClassName(getParam(PROTOLOGIMPL_CLASS_PARAM,
- params))
- protoLogGroupsJarArg = validateJarName(getParam(PROTOLOGGROUP_JAR_PARAM, params))
- viewerConfigJsonArg = validateNotSpecified(VIEWER_CONFIG_JSON_PARAM, params)
- outputSourceJarArg = validateSrcJarName(getParam(OUTPUT_SOURCE_JAR_PARAM, params))
- javaSourceArgs = validateJavaInputList(inputFiles)
- logProtofileArg = ""
- }
- GENERATE_CONFIG_CMD -> {
- protoLogClassNameArg = validateClassName(getParam(PROTOLOG_CLASS_PARAM, params))
- protoLogGroupsClassNameArg = validateClassName(getParam(PROTOLOGGROUP_CLASS_PARAM,
- params))
- protoLogImplClassNameArg = validateNotSpecified(PROTOLOGIMPL_CLASS_PARAM, params)
- protoLogGroupsJarArg = validateJarName(getParam(PROTOLOGGROUP_JAR_PARAM, params))
- viewerConfigJsonArg = validateJSONName(getParam(VIEWER_CONFIG_JSON_PARAM, params))
- outputSourceJarArg = validateNotSpecified(OUTPUT_SOURCE_JAR_PARAM, params)
- javaSourceArgs = validateJavaInputList(inputFiles)
- logProtofileArg = ""
- }
- READ_LOG_CMD -> {
- protoLogClassNameArg = validateNotSpecified(PROTOLOG_CLASS_PARAM, params)
- protoLogGroupsClassNameArg = validateNotSpecified(PROTOLOGGROUP_CLASS_PARAM, params)
- protoLogImplClassNameArg = validateNotSpecified(PROTOLOGIMPL_CLASS_PARAM, params)
- protoLogGroupsJarArg = validateNotSpecified(PROTOLOGGROUP_JAR_PARAM, params)
- viewerConfigJsonArg = validateJSONName(getParam(VIEWER_CONFIG_JSON_PARAM, params))
- outputSourceJarArg = validateNotSpecified(OUTPUT_SOURCE_JAR_PARAM, params)
- javaSourceArgs = listOf()
- logProtofileArg = validateLogInputList(inputFiles)
- }
- else -> {
- throw InvalidCommandException("Unknown command.")
- }
- }
- }
-}
diff --git a/tools/protologtool/src/com/android/protologtool/Constants.kt b/tools/protologtool/src/com/android/protologtool/Constants.kt
deleted file mode 100644
index 2ccfc4d20182..000000000000
--- a/tools/protologtool/src/com/android/protologtool/Constants.kt
+++ /dev/null
@@ -1,27 +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.protologtool
-
-object Constants {
- const val NAME = "protologtool"
- const val VERSION = "1.0.0"
- const val IS_ENABLED_METHOD = "isEnabled"
- const val IS_LOG_TO_LOGCAT_METHOD = "isLogToLogcat"
- const val IS_LOG_TO_ANY_METHOD = "isLogToAny"
- const val GET_TAG_METHOD = "getTag"
- const val ENUM_VALUES_METHOD = "values"
-}
diff --git a/tools/protologtool/src/com/android/protologtool/LogGroup.kt b/tools/protologtool/src/com/android/protologtool/LogGroup.kt
deleted file mode 100644
index 42a37a26e08a..000000000000
--- a/tools/protologtool/src/com/android/protologtool/LogGroup.kt
+++ /dev/null
@@ -1,24 +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.protologtool
-
-data class LogGroup(
- val name: String,
- val enabled: Boolean,
- val textEnabled: Boolean,
- val tag: String
-)
diff --git a/tools/protologtool/src/com/android/protologtool/LogLevel.kt b/tools/protologtool/src/com/android/protologtool/LogLevel.kt
deleted file mode 100644
index dc29557ef440..000000000000
--- a/tools/protologtool/src/com/android/protologtool/LogLevel.kt
+++ /dev/null
@@ -1,37 +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.protologtool
-
-import com.github.javaparser.ast.Node
-
-enum class LogLevel {
- DEBUG, VERBOSE, INFO, WARN, ERROR, WTF;
-
- companion object {
- fun getLevelForMethodName(name: String, node: Node): LogLevel {
- return when (name) {
- "d" -> DEBUG
- "v" -> VERBOSE
- "i" -> INFO
- "w" -> WARN
- "e" -> ERROR
- "wtf" -> WTF
- else -> throw InvalidProtoLogCallException("Unknown log level $name", node)
- }
- }
- }
-}
diff --git a/tools/protologtool/src/com/android/protologtool/LogParser.kt b/tools/protologtool/src/com/android/protologtool/LogParser.kt
deleted file mode 100644
index 4d0eb0e4a705..000000000000
--- a/tools/protologtool/src/com/android/protologtool/LogParser.kt
+++ /dev/null
@@ -1,112 +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.protologtool
-
-import com.android.json.stream.JsonReader
-import com.android.server.wm.ProtoLogMessage
-import com.android.server.wm.WindowManagerLogFileProto
-import java.io.BufferedReader
-import java.io.InputStream
-import java.io.InputStreamReader
-import java.io.PrintStream
-import java.lang.Exception
-import java.text.SimpleDateFormat
-import java.util.Date
-import java.util.Locale
-
-/**
- * Implements a simple parser/viewer for binary ProtoLog logs.
- * A binary log is translated into Android "LogCat"-like text log.
- */
-class LogParser(private val configParser: ViewerConfigParser) {
- companion object {
- private val dateFormat = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
- private val magicNumber =
- WindowManagerLogFileProto.MagicNumber.MAGIC_NUMBER_H.number.toLong() shl 32 or
- WindowManagerLogFileProto.MagicNumber.MAGIC_NUMBER_L.number.toLong()
- }
-
- private fun printTime(time: Long, offset: Long, ps: PrintStream) {
- ps.print(dateFormat.format(Date(time / 1000000 + offset)) + " ")
- }
-
- private fun printFormatted(
- protoLogMessage: ProtoLogMessage,
- configEntry: ViewerConfigParser.ConfigEntry,
- ps: PrintStream
- ) {
- val strParmIt = protoLogMessage.strParamsList.iterator()
- val longParamsIt = protoLogMessage.sint64ParamsList.iterator()
- val doubleParamsIt = protoLogMessage.doubleParamsList.iterator()
- val boolParamsIt = protoLogMessage.booleanParamsList.iterator()
- val args = mutableListOf<Any>()
- val format = configEntry.messageString
- val argTypes = CodeUtils.parseFormatString(format)
- try {
- argTypes.forEach {
- when (it) {
- CodeUtils.LogDataTypes.BOOLEAN -> args.add(boolParamsIt.next())
- CodeUtils.LogDataTypes.LONG -> args.add(longParamsIt.next())
- CodeUtils.LogDataTypes.DOUBLE -> args.add(doubleParamsIt.next())
- CodeUtils.LogDataTypes.STRING -> args.add(strParmIt.next())
- }
- }
- } catch (ex: NoSuchElementException) {
- throw InvalidFormatStringException("Invalid format string in config", ex)
- }
- if (strParmIt.hasNext() || longParamsIt.hasNext() ||
- doubleParamsIt.hasNext() || boolParamsIt.hasNext()) {
- throw RuntimeException("Invalid format string in config - no enough matchers")
- }
- val formatted = format.format(*(args.toTypedArray()))
- ps.print("${configEntry.level} ${configEntry.tag}: $formatted\n")
- }
-
- private fun printUnformatted(protoLogMessage: ProtoLogMessage, ps: PrintStream, tag: String) {
- ps.println("$tag: ${protoLogMessage.messageHash} - ${protoLogMessage.strParamsList}" +
- " ${protoLogMessage.sint64ParamsList} ${protoLogMessage.doubleParamsList}" +
- " ${protoLogMessage.booleanParamsList}")
- }
-
- fun parse(protoLogInput: InputStream, jsonConfigInput: InputStream, ps: PrintStream) {
- val jsonReader = JsonReader(BufferedReader(InputStreamReader(jsonConfigInput)))
- val config = configParser.parseConfig(jsonReader)
- val protoLog = WindowManagerLogFileProto.parseFrom(protoLogInput)
-
- if (protoLog.magicNumber != magicNumber) {
- throw InvalidInputException("ProtoLog file magic number is invalid.")
- }
- if (protoLog.version != Constants.VERSION) {
- throw InvalidInputException("ProtoLog file version not supported by this tool," +
- " log version ${protoLog.version}, viewer version ${Constants.VERSION}")
- }
-
- protoLog.logList.forEach { log ->
- printTime(log.elapsedRealtimeNanos, protoLog.realTimeToElapsedTimeOffsetMillis, ps)
- if (log.messageHash !in config) {
- printUnformatted(log, ps, "UNKNOWN")
- } else {
- val conf = config.getValue(log.messageHash)
- try {
- printFormatted(log, conf, ps)
- } catch (ex: Exception) {
- printUnformatted(log, ps, "INVALID")
- }
- }
- }
- }
-}
diff --git a/tools/protologtool/src/com/android/protologtool/ProtoLogCallProcessor.kt b/tools/protologtool/src/com/android/protologtool/ProtoLogCallProcessor.kt
deleted file mode 100644
index 29d8ae5c6694..000000000000
--- a/tools/protologtool/src/com/android/protologtool/ProtoLogCallProcessor.kt
+++ /dev/null
@@ -1,108 +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.protologtool
-
-import com.github.javaparser.ast.CompilationUnit
-import com.github.javaparser.ast.expr.Expression
-import com.github.javaparser.ast.expr.FieldAccessExpr
-import com.github.javaparser.ast.expr.MethodCallExpr
-import com.github.javaparser.ast.expr.NameExpr
-
-/**
- * Helper class for visiting all ProtoLog calls.
- * For every valid call in the given {@code CompilationUnit} a {@code ProtoLogCallVisitor} callback
- * is executed.
- */
-open class ProtoLogCallProcessor(
- private val protoLogClassName: String,
- private val protoLogGroupClassName: String,
- private val groupMap: Map<String, LogGroup>
-) {
- private val protoLogSimpleClassName = protoLogClassName.substringAfterLast('.')
- private val protoLogGroupSimpleClassName = protoLogGroupClassName.substringAfterLast('.')
-
- private fun getLogGroupName(
- expr: Expression,
- isClassImported: Boolean,
- staticImports: Set<String>
- ): String {
- return when (expr) {
- is NameExpr -> when {
- expr.nameAsString in staticImports -> expr.nameAsString
- else ->
- throw InvalidProtoLogCallException("Unknown/not imported ProtoLogGroup", expr)
- }
- is FieldAccessExpr -> when {
- expr.scope.toString() == protoLogGroupClassName
- || isClassImported &&
- expr.scope.toString() == protoLogGroupSimpleClassName -> expr.nameAsString
- else ->
- throw InvalidProtoLogCallException("Unknown/not imported ProtoLogGroup", expr)
- }
- else -> throw InvalidProtoLogCallException("Invalid group argument " +
- "- must be ProtoLogGroup enum member reference", expr)
- }
- }
-
- private fun isProtoCall(
- call: MethodCallExpr,
- isLogClassImported: Boolean,
- staticLogImports: Collection<String>
- ): Boolean {
- return call.scope.isPresent && call.scope.get().toString() == protoLogClassName ||
- isLogClassImported && call.scope.isPresent &&
- call.scope.get().toString() == protoLogSimpleClassName ||
- !call.scope.isPresent && staticLogImports.contains(call.name.toString())
- }
-
- open fun process(code: CompilationUnit, callVisitor: ProtoLogCallVisitor?): CompilationUnit {
- if (CodeUtils.isWildcardStaticImported(code, protoLogClassName) ||
- CodeUtils.isWildcardStaticImported(code, protoLogGroupClassName)) {
- throw IllegalImportException("Wildcard static imports of $protoLogClassName " +
- "and $protoLogGroupClassName methods are not supported.")
- }
-
- val isLogClassImported = CodeUtils.isClassImportedOrSamePackage(code, protoLogClassName)
- val staticLogImports = CodeUtils.staticallyImportedMethods(code, protoLogClassName)
- val isGroupClassImported = CodeUtils.isClassImportedOrSamePackage(code,
- protoLogGroupClassName)
- val staticGroupImports = CodeUtils.staticallyImportedMethods(code, protoLogGroupClassName)
-
- code.findAll(MethodCallExpr::class.java)
- .filter { call ->
- isProtoCall(call, isLogClassImported, staticLogImports)
- }.forEach { call ->
- if (call.arguments.size < 2) {
- throw InvalidProtoLogCallException("Method signature does not match " +
- "any ProtoLog method.", call)
- }
-
- val messageString = CodeUtils.concatMultilineString(call.getArgument(1))
- val groupNameArg = call.getArgument(0)
- val groupName =
- getLogGroupName(groupNameArg, isGroupClassImported, staticGroupImports)
- if (groupName !in groupMap) {
- throw InvalidProtoLogCallException("Unknown group argument " +
- "- not a ProtoLogGroup enum member", call)
- }
-
- callVisitor?.processCall(call, messageString, LogLevel.getLevelForMethodName(
- call.name.toString(), call), groupMap.getValue(groupName))
- }
- return code
- }
-}
diff --git a/tools/protologtool/src/com/android/protologtool/ProtoLogCallVisitor.kt b/tools/protologtool/src/com/android/protologtool/ProtoLogCallVisitor.kt
deleted file mode 100644
index 42a75f8cc22f..000000000000
--- a/tools/protologtool/src/com/android/protologtool/ProtoLogCallVisitor.kt
+++ /dev/null
@@ -1,23 +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.protologtool
-
-import com.github.javaparser.ast.expr.MethodCallExpr
-
-interface ProtoLogCallVisitor {
- fun processCall(call: MethodCallExpr, messageString: String, level: LogLevel, group: LogGroup)
-}
diff --git a/tools/protologtool/src/com/android/protologtool/ProtoLogGroupReader.kt b/tools/protologtool/src/com/android/protologtool/ProtoLogGroupReader.kt
deleted file mode 100644
index 664c8a6506b2..000000000000
--- a/tools/protologtool/src/com/android/protologtool/ProtoLogGroupReader.kt
+++ /dev/null
@@ -1,60 +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.protologtool
-
-import com.android.protologtool.Constants.ENUM_VALUES_METHOD
-import com.android.protologtool.Constants.GET_TAG_METHOD
-import com.android.protologtool.Constants.IS_ENABLED_METHOD
-import com.android.protologtool.Constants.IS_LOG_TO_LOGCAT_METHOD
-import java.io.File
-import java.lang.RuntimeException
-import java.net.URLClassLoader
-
-class ProtoLogGroupReader {
- private fun getClassloaderForJar(jarPath: String): ClassLoader {
- val jarFile = File(jarPath)
- val url = jarFile.toURI().toURL()
- return URLClassLoader(arrayOf(url), ProtoLogGroupReader::class.java.classLoader)
- }
-
- private fun getEnumValues(clazz: Class<*>): List<Enum<*>> {
- val valuesMethod = clazz.getMethod(ENUM_VALUES_METHOD)
- @Suppress("UNCHECKED_CAST")
- return (valuesMethod.invoke(null) as Array<Enum<*>>).toList()
- }
-
- private fun getLogGroupFromEnumValue(group: Any, clazz: Class<*>): LogGroup {
- val enabled = clazz.getMethod(IS_ENABLED_METHOD).invoke(group) as Boolean
- val textEnabled = clazz.getMethod(IS_LOG_TO_LOGCAT_METHOD).invoke(group) as Boolean
- val tag = clazz.getMethod(GET_TAG_METHOD).invoke(group) as String
- val name = (group as Enum<*>).name
- return LogGroup(name, enabled, textEnabled, tag)
- }
-
- fun loadFromJar(jarPath: String, className: String): Map<String, LogGroup> {
- try {
- val classLoader = getClassloaderForJar(jarPath)
- val clazz = classLoader.loadClass(className)
- val values = getEnumValues(clazz)
- return values.map { group ->
- group.name to getLogGroupFromEnumValue(group, clazz)
- }.toMap()
- } catch (ex: ReflectiveOperationException) {
- throw RuntimeException("Unable to load ProtoLogGroup enum class", ex)
- }
- }
-}
diff --git a/tools/protologtool/src/com/android/protologtool/ProtoLogTool.kt b/tools/protologtool/src/com/android/protologtool/ProtoLogTool.kt
deleted file mode 100644
index 618e4b14e4c5..000000000000
--- a/tools/protologtool/src/com/android/protologtool/ProtoLogTool.kt
+++ /dev/null
@@ -1,95 +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.protologtool
-
-import com.android.protologtool.CommandOptions.Companion.USAGE
-import com.github.javaparser.StaticJavaParser
-import java.io.File
-import java.io.FileInputStream
-import java.io.FileOutputStream
-import java.util.jar.JarOutputStream
-import java.util.zip.ZipEntry
-import kotlin.system.exitProcess
-
-object ProtoLogTool {
- private fun showHelpAndExit() {
- println(USAGE)
- exitProcess(-1)
- }
-
- private fun processClasses(command: CommandOptions) {
- val groups = ProtoLogGroupReader()
- .loadFromJar(command.protoLogGroupsJarArg, command.protoLogGroupsClassNameArg)
- val out = FileOutputStream(command.outputSourceJarArg)
- val outJar = JarOutputStream(out)
- val processor = ProtoLogCallProcessor(command.protoLogClassNameArg,
- command.protoLogGroupsClassNameArg, groups)
- val transformer = SourceTransformer(command.protoLogImplClassNameArg, processor)
-
- command.javaSourceArgs.forEach { path ->
- val file = File(path)
- val text = file.readText()
- val code = StaticJavaParser.parse(text)
- val outSrc = transformer.processClass(text, code)
- val pack = if (code.packageDeclaration.isPresent) code.packageDeclaration
- .get().nameAsString else ""
- val newPath = pack.replace('.', '/') + '/' + file.name
- outJar.putNextEntry(ZipEntry(newPath))
- outJar.write(outSrc.toByteArray())
- outJar.closeEntry()
- }
-
- outJar.close()
- out.close()
- }
-
- private fun viewerConf(command: CommandOptions) {
- val groups = ProtoLogGroupReader()
- .loadFromJar(command.protoLogGroupsJarArg, command.protoLogGroupsClassNameArg)
- val processor = ProtoLogCallProcessor(command.protoLogClassNameArg,
- command.protoLogGroupsClassNameArg, groups)
- val builder = ViewerConfigBuilder(processor)
- command.javaSourceArgs.forEach { path ->
- val file = File(path)
- builder.processClass(StaticJavaParser.parse(file))
- }
- val out = FileOutputStream(command.viewerConfigJsonArg)
- out.write(builder.build().toByteArray())
- out.close()
- }
-
- fun read(command: CommandOptions) {
- LogParser(ViewerConfigParser())
- .parse(FileInputStream(command.logProtofileArg),
- FileInputStream(command.viewerConfigJsonArg), System.out)
- }
-
- @JvmStatic
- fun main(args: Array<String>) {
- try {
- val command = CommandOptions(args)
- when (command.command) {
- CommandOptions.TRANSFORM_CALLS_CMD -> processClasses(command)
- CommandOptions.GENERATE_CONFIG_CMD -> viewerConf(command)
- CommandOptions.READ_LOG_CMD -> read(command)
- }
- } catch (ex: InvalidCommandException) {
- println(ex.message)
- showHelpAndExit()
- }
- }
-}
diff --git a/tools/protologtool/src/com/android/protologtool/SourceTransformer.kt b/tools/protologtool/src/com/android/protologtool/SourceTransformer.kt
deleted file mode 100644
index f915ea6eb186..000000000000
--- a/tools/protologtool/src/com/android/protologtool/SourceTransformer.kt
+++ /dev/null
@@ -1,194 +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.protologtool
-
-import com.android.protologtool.Constants.IS_LOG_TO_ANY_METHOD
-import com.github.javaparser.StaticJavaParser
-import com.github.javaparser.ast.CompilationUnit
-import com.github.javaparser.ast.NodeList
-import com.github.javaparser.ast.body.VariableDeclarator
-import com.github.javaparser.ast.expr.BooleanLiteralExpr
-import com.github.javaparser.ast.expr.CastExpr
-import com.github.javaparser.ast.expr.FieldAccessExpr
-import com.github.javaparser.ast.expr.IntegerLiteralExpr
-import com.github.javaparser.ast.expr.MethodCallExpr
-import com.github.javaparser.ast.expr.NameExpr
-import com.github.javaparser.ast.expr.NullLiteralExpr
-import com.github.javaparser.ast.expr.SimpleName
-import com.github.javaparser.ast.expr.VariableDeclarationExpr
-import com.github.javaparser.ast.stmt.BlockStmt
-import com.github.javaparser.ast.stmt.ExpressionStmt
-import com.github.javaparser.ast.stmt.IfStmt
-import com.github.javaparser.ast.type.ArrayType
-import com.github.javaparser.printer.PrettyPrinter
-import com.github.javaparser.printer.PrettyPrinterConfiguration
-import com.github.javaparser.printer.lexicalpreservation.LexicalPreservingPrinter
-
-class SourceTransformer(
- protoLogImplClassName: String,
- private val protoLogCallProcessor: ProtoLogCallProcessor
-) : ProtoLogCallVisitor {
- override fun processCall(
- call: MethodCallExpr,
- messageString: String,
- level: LogLevel,
- group: LogGroup
- ) {
- // Input format: ProtoLog.e(GROUP, "msg %d", arg)
- if (!call.parentNode.isPresent) {
- // Should never happen
- throw RuntimeException("Unable to process log call $call " +
- "- no parent node in AST")
- }
- if (call.parentNode.get() !is ExpressionStmt) {
- // Should never happen
- throw RuntimeException("Unable to process log call $call " +
- "- parent node in AST is not an ExpressionStmt")
- }
- val parentStmt = call.parentNode.get() as ExpressionStmt
- if (!parentStmt.parentNode.isPresent) {
- // Should never happen
- throw RuntimeException("Unable to process log call $call " +
- "- no grandparent node in AST")
- }
- val ifStmt: IfStmt
- if (group.enabled) {
- val hash = CodeUtils.hash(messageString, level)
- val newCall = call.clone()
- if (!group.textEnabled) {
- // Remove message string if text logging is not enabled by default.
- // Out: ProtoLog.e(GROUP, null, arg)
- newCall.arguments[1].replace(NameExpr("null"))
- }
- // Insert message string hash as a second argument.
- // Out: ProtoLog.e(GROUP, 1234, null, arg)
- newCall.arguments.add(1, IntegerLiteralExpr(hash))
- val argTypes = CodeUtils.parseFormatString(messageString)
- val typeMask = CodeUtils.logDataTypesToBitMask(argTypes)
- // Insert bitmap representing which Number parameters are to be considered as
- // floating point numbers.
- // Out: ProtoLog.e(GROUP, 1234, 0, null, arg)
- newCall.arguments.add(2, IntegerLiteralExpr(typeMask))
- // Replace call to a stub method with an actual implementation.
- // Out: com.android.server.wm.ProtoLogImpl.e(GROUP, 1234, null, arg)
- newCall.setScope(protoLogImplClassNode)
- // Create a call to GROUP.isLogAny()
- // Out: GROUP.isLogAny()
- val isLogAnyExpr = MethodCallExpr(newCall.arguments[0].clone(),
- SimpleName(IS_LOG_TO_ANY_METHOD))
- if (argTypes.size != call.arguments.size - 2) {
- throw InvalidProtoLogCallException(
- "Number of arguments does not mach format string", call)
- }
- val blockStmt = BlockStmt()
- if (argTypes.isNotEmpty()) {
- // Assign every argument to a variable to check its type in compile time
- // (this is assignment is optimized-out by dex tool, there is no runtime impact)/
- // Out: long protoLogParam0 = arg
- argTypes.forEachIndexed { idx, type ->
- val varName = "protoLogParam$idx"
- val declaration = VariableDeclarator(type.type, varName,
- type.toType(newCall.arguments[idx + 4].clone()))
- blockStmt.addStatement(ExpressionStmt(VariableDeclarationExpr(declaration)))
- newCall.setArgument(idx + 4, NameExpr(SimpleName(varName)))
- }
- } else {
- // Assign (Object[])null as the vararg parameter to prevent allocating an empty
- // object array.
- val nullArray = CastExpr(ArrayType(objectType), NullLiteralExpr())
- newCall.addArgument(nullArray)
- }
- blockStmt.addStatement(ExpressionStmt(newCall))
- // Create an IF-statement with the previously created condition.
- // Out: if (GROUP.isLogAny()) {
- // long protoLogParam0 = arg;
- // com.android.server.wm.ProtoLogImpl.e(GROUP, 1234, 0, null, protoLogParam0);
- // }
- ifStmt = IfStmt(isLogAnyExpr, blockStmt, null)
- } else {
- // Surround with if (false).
- val newCall = parentStmt.clone()
- ifStmt = IfStmt(BooleanLiteralExpr(false), BlockStmt(NodeList(newCall)), null)
- newCall.setBlockComment(" ${group.name} is disabled ")
- }
- // Inline the new statement.
- val printedIfStmt = inlinePrinter.print(ifStmt)
- // Append blank lines to preserve line numbering in file (to allow debugging)
- val newLines = LexicalPreservingPrinter.print(parentStmt).count { c -> c == '\n' }
- val newStmt = printedIfStmt.substringBeforeLast('}') + ("\n".repeat(newLines)) + '}'
- // pre-workaround code, see explanation below
- /*
- val inlinedIfStmt = StaticJavaParser.parseStatement(newStmt)
- LexicalPreservingPrinter.setup(inlinedIfStmt)
- // Replace the original call.
- if (!parentStmt.replace(inlinedIfStmt)) {
- // Should never happen
- throw RuntimeException("Unable to process log call $call " +
- "- unable to replace the call.")
- }
- */
- /** Workaround for a bug in JavaParser (AST tree invalid after replacing a node when using
- * LexicalPreservingPrinter (https://github.com/javaparser/javaparser/issues/2290).
- * Replace the code below with the one commended-out above one the issue is resolved. */
- if (!parentStmt.range.isPresent) {
- // Should never happen
- throw RuntimeException("Unable to process log call $call " +
- "- unable to replace the call.")
- }
- val range = parentStmt.range.get()
- val begin = range.begin.line - 1
- val oldLines = processedCode.subList(begin, range.end.line)
- val oldCode = oldLines.joinToString("\n")
- val newCode = oldCode.replaceRange(
- offsets[begin] + range.begin.column - 1,
- oldCode.length - oldLines.lastOrNull()!!.length +
- range.end.column + offsets[range.end.line - 1], newStmt)
- newCode.split("\n").forEachIndexed { idx, line ->
- offsets[begin + idx] += line.length - processedCode[begin + idx].length
- processedCode[begin + idx] = line
- }
- }
-
- private val inlinePrinter: PrettyPrinter
- private val objectType = StaticJavaParser.parseClassOrInterfaceType("Object")
-
- init {
- val config = PrettyPrinterConfiguration()
- config.endOfLineCharacter = " "
- config.indentSize = 0
- config.tabWidth = 1
- inlinePrinter = PrettyPrinter(config)
- }
-
- private val protoLogImplClassNode =
- StaticJavaParser.parseExpression<FieldAccessExpr>(protoLogImplClassName)
- private var processedCode: MutableList<String> = mutableListOf()
- private var offsets: IntArray = IntArray(0)
-
- fun processClass(
- code: String,
- compilationUnit: CompilationUnit =
- StaticJavaParser.parse(code)
- ): String {
- processedCode = code.split('\n').toMutableList()
- offsets = IntArray(processedCode.size)
- LexicalPreservingPrinter.setup(compilationUnit)
- protoLogCallProcessor.process(compilationUnit, this)
- // return LexicalPreservingPrinter.print(compilationUnit)
- return processedCode.joinToString("\n")
- }
-}
diff --git a/tools/protologtool/src/com/android/protologtool/ViewerConfigBuilder.kt b/tools/protologtool/src/com/android/protologtool/ViewerConfigBuilder.kt
deleted file mode 100644
index 8ce9a49c0302..000000000000
--- a/tools/protologtool/src/com/android/protologtool/ViewerConfigBuilder.kt
+++ /dev/null
@@ -1,91 +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.protologtool
-
-import com.android.json.stream.JsonWriter
-import com.github.javaparser.ast.CompilationUnit
-import com.android.protologtool.Constants.VERSION
-import com.github.javaparser.ast.expr.MethodCallExpr
-import java.io.StringWriter
-
-class ViewerConfigBuilder(
- private val protoLogCallVisitor: ProtoLogCallProcessor
-) : ProtoLogCallVisitor {
- override fun processCall(
- call: MethodCallExpr,
- messageString: String,
- level: LogLevel,
- group: LogGroup
- ) {
- if (group.enabled) {
- val key = CodeUtils.hash(messageString, level)
- if (statements.containsKey(key)) {
- if (statements[key] != Triple(messageString, level, group)) {
- throw HashCollisionException(
- "Please modify the log message \"$messageString\" " +
- "or \"${statements[key]}\" - their hashes are equal.")
- }
- } else {
- groups.add(group)
- statements[key] = Triple(messageString, level, group)
- }
- }
- }
-
- private val statements: MutableMap<Int, Triple<String, LogLevel, LogGroup>> = mutableMapOf()
- private val groups: MutableSet<LogGroup> = mutableSetOf()
-
- fun processClass(unit: CompilationUnit) {
- protoLogCallVisitor.process(unit, this)
- }
-
- fun build(): String {
- val stringWriter = StringWriter()
- val writer = JsonWriter(stringWriter)
- writer.setIndent(" ")
- writer.beginObject()
- writer.name("version")
- writer.value(VERSION)
- writer.name("messages")
- writer.beginObject()
- statements.toSortedMap().forEach { (key, value) ->
- writer.name(key.toString())
- writer.beginObject()
- writer.name("message")
- writer.value(value.first)
- writer.name("level")
- writer.value(value.second.name)
- writer.name("group")
- writer.value(value.third.name)
- writer.endObject()
- }
- writer.endObject()
- writer.name("groups")
- writer.beginObject()
- groups.toSortedSet(Comparator { o1, o2 -> o1.name.compareTo(o2.name) }).forEach { group ->
- writer.name(group.name)
- writer.beginObject()
- writer.name("tag")
- writer.value(group.tag)
- writer.endObject()
- }
- writer.endObject()
- writer.endObject()
- stringWriter.buffer.append('\n')
- return stringWriter.toString()
- }
-}
diff --git a/tools/protologtool/src/com/android/protologtool/ViewerConfigParser.kt b/tools/protologtool/src/com/android/protologtool/ViewerConfigParser.kt
deleted file mode 100644
index 69cf92d4d228..000000000000
--- a/tools/protologtool/src/com/android/protologtool/ViewerConfigParser.kt
+++ /dev/null
@@ -1,125 +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.protologtool
-
-import com.android.json.stream.JsonReader
-
-open class ViewerConfigParser {
- data class MessageEntry(
- val messageString: String,
- val level: String,
- val groupName: String
- )
-
- fun parseMessage(jsonReader: JsonReader): MessageEntry {
- jsonReader.beginObject()
- var message: String? = null
- var level: String? = null
- var groupName: String? = null
- while (jsonReader.hasNext()) {
- val key = jsonReader.nextName()
- when (key) {
- "message" -> message = jsonReader.nextString()
- "level" -> level = jsonReader.nextString()
- "group" -> groupName = jsonReader.nextString()
- else -> jsonReader.skipValue()
- }
- }
- jsonReader.endObject()
- if (message.isNullOrBlank() || level.isNullOrBlank() || groupName.isNullOrBlank()) {
- throw InvalidViewerConfigException("Invalid message entry in viewer config")
- }
- return MessageEntry(message, level, groupName)
- }
-
- data class GroupEntry(val tag: String)
-
- fun parseGroup(jsonReader: JsonReader): GroupEntry {
- jsonReader.beginObject()
- var tag: String? = null
- while (jsonReader.hasNext()) {
- val key = jsonReader.nextName()
- when (key) {
- "tag" -> tag = jsonReader.nextString()
- else -> jsonReader.skipValue()
- }
- }
- jsonReader.endObject()
- if (tag.isNullOrBlank()) {
- throw InvalidViewerConfigException("Invalid group entry in viewer config")
- }
- return GroupEntry(tag)
- }
-
- fun parseMessages(jsonReader: JsonReader): Map<Int, MessageEntry> {
- val config: MutableMap<Int, MessageEntry> = mutableMapOf()
- jsonReader.beginObject()
- while (jsonReader.hasNext()) {
- val key = jsonReader.nextName()
- val hash = key.toIntOrNull()
- ?: throw InvalidViewerConfigException("Invalid key in messages viewer config")
- config[hash] = parseMessage(jsonReader)
- }
- jsonReader.endObject()
- return config
- }
-
- fun parseGroups(jsonReader: JsonReader): Map<String, GroupEntry> {
- val config: MutableMap<String, GroupEntry> = mutableMapOf()
- jsonReader.beginObject()
- while (jsonReader.hasNext()) {
- val key = jsonReader.nextName()
- config[key] = parseGroup(jsonReader)
- }
- jsonReader.endObject()
- return config
- }
-
- data class ConfigEntry(val messageString: String, val level: String, val tag: String)
-
- open fun parseConfig(jsonReader: JsonReader): Map<Int, ConfigEntry> {
- var messages: Map<Int, MessageEntry>? = null
- var groups: Map<String, GroupEntry>? = null
- var version: String? = null
-
- jsonReader.beginObject()
- while (jsonReader.hasNext()) {
- val key = jsonReader.nextName()
- when (key) {
- "messages" -> messages = parseMessages(jsonReader)
- "groups" -> groups = parseGroups(jsonReader)
- "version" -> version = jsonReader.nextString()
-
- else -> jsonReader.skipValue()
- }
- }
- jsonReader.endObject()
- if (messages == null || groups == null || version == null) {
- throw InvalidViewerConfigException("Invalid config - definitions missing")
- }
- if (version != Constants.VERSION) {
- throw InvalidViewerConfigException("Viewer config version not supported by this tool," +
- " config version $version, viewer version ${Constants.VERSION}")
- }
- return messages.map { msg ->
- msg.key to ConfigEntry(
- msg.value.messageString, msg.value.level, groups[msg.value.groupName]?.tag
- ?: throw InvalidViewerConfigException(
- "Group definition missing for ${msg.value.groupName}"))
- }.toMap()
- }
-}
diff --git a/tools/protologtool/src/com/android/protologtool/exceptions.kt b/tools/protologtool/src/com/android/protologtool/exceptions.kt
deleted file mode 100644
index 2199785a335b..000000000000
--- a/tools/protologtool/src/com/android/protologtool/exceptions.kt
+++ /dev/null
@@ -1,44 +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.protologtool
-
-import com.github.javaparser.ast.Node
-import java.lang.Exception
-import java.lang.RuntimeException
-
-class HashCollisionException(message: String) : RuntimeException(message)
-
-class IllegalImportException(message: String) : Exception(message)
-
-class InvalidProtoLogCallException(message: String, node: Node)
- : RuntimeException("$message\nAt: $node")
-
-class InvalidViewerConfigException : Exception {
- constructor(message: String) : super(message)
-
- constructor(message: String, ex: Exception) : super(message, ex)
-}
-
-class InvalidFormatStringException : Exception {
- constructor(message: String) : super(message)
-
- constructor(message: String, ex: Exception) : super(message, ex)
-}
-
-class InvalidInputException(message: String) : Exception(message)
-
-class InvalidCommandException(message: String) : Exception(message)
diff --git a/tools/protologtool/tests/com/android/protolog/tool/CodeUtilsTest.kt b/tools/protologtool/tests/com/android/protolog/tool/CodeUtilsTest.kt
new file mode 100644
index 000000000000..b916f8f00a68
--- /dev/null
+++ b/tools/protologtool/tests/com/android/protolog/tool/CodeUtilsTest.kt
@@ -0,0 +1,182 @@
+/*
+ * 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.protolog.tool
+
+import com.github.javaparser.StaticJavaParser
+import com.github.javaparser.ast.expr.BinaryExpr
+import com.github.javaparser.ast.expr.StringLiteralExpr
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class CodeUtilsTest {
+ @Test
+ fun hash() {
+ assertEquals(-1259556708, CodeUtils.hash("Test.java:50", "test",
+ LogLevel.DEBUG, LogGroup("test", true, true, "TAG")))
+ }
+
+ @Test
+ fun hash_changeLocation() {
+ assertEquals(15793504, CodeUtils.hash("Test.java:10", "test2",
+ LogLevel.DEBUG, LogGroup("test", true, true, "TAG")))
+ }
+
+ @Test
+ fun hash_changeLevel() {
+ assertEquals(-731772463, CodeUtils.hash("Test.java:50", "test",
+ LogLevel.ERROR, LogGroup("test", true, true, "TAG")))
+ }
+
+ @Test
+ fun hash_changeMessage() {
+ assertEquals(-2026343204, CodeUtils.hash("Test.java:50", "test2",
+ LogLevel.DEBUG, LogGroup("test", true, true, "TAG")))
+ }
+
+ @Test
+ fun hash_changeGroup() {
+ assertEquals(1607870166, CodeUtils.hash("Test.java:50", "test2",
+ LogLevel.DEBUG, LogGroup("test2", true, true, "TAG")))
+ }
+
+ @Test(expected = IllegalImportException::class)
+ fun checkWildcardStaticImported_true() {
+ val code = """package org.example.test;
+ import static org.example.Test.*;
+ """
+ CodeUtils.checkWildcardStaticImported(
+ StaticJavaParser.parse(code), "org.example.Test", "")
+ }
+
+ @Test
+ fun checkWildcardStaticImported_notStatic() {
+ val code = """package org.example.test;
+ import org.example.Test.*;
+ """
+ CodeUtils.checkWildcardStaticImported(
+ StaticJavaParser.parse(code), "org.example.Test", "")
+ }
+
+ @Test
+ fun checkWildcardStaticImported_differentClass() {
+ val code = """package org.example.test;
+ import static org.example.Test2.*;
+ """
+ CodeUtils.checkWildcardStaticImported(
+ StaticJavaParser.parse(code), "org.example.Test", "")
+ }
+
+ @Test
+ fun checkWildcardStaticImported_notWildcard() {
+ val code = """package org.example.test;
+ import org.example.Test.test;
+ """
+ CodeUtils.checkWildcardStaticImported(
+ StaticJavaParser.parse(code), "org.example.Test", "")
+ }
+
+ @Test
+ fun isClassImportedOrSamePackage_imported() {
+ val code = """package org.example.test;
+ import org.example.Test;
+ """
+ assertTrue(CodeUtils.isClassImportedOrSamePackage(
+ StaticJavaParser.parse(code), "org.example.Test"))
+ }
+
+ @Test
+ fun isClassImportedOrSamePackage_samePackage() {
+ val code = """package org.example.test;
+ """
+ assertTrue(CodeUtils.isClassImportedOrSamePackage(
+ StaticJavaParser.parse(code), "org.example.test.Test"))
+ }
+
+ @Test
+ fun isClassImportedOrSamePackage_false() {
+ val code = """package org.example.test;
+ import org.example.Test;
+ """
+ assertFalse(CodeUtils.isClassImportedOrSamePackage(
+ StaticJavaParser.parse(code), "org.example.Test2"))
+ }
+
+ @Test
+ fun staticallyImportedMethods_ab() {
+ val code = """
+ import static org.example.Test.a;
+ import static org.example.Test.b;
+ """
+ val imported = CodeUtils.staticallyImportedMethods(StaticJavaParser.parse(code),
+ "org.example.Test")
+ assertTrue(imported.containsAll(listOf("a", "b")))
+ assertEquals(2, imported.size)
+ }
+
+ @Test
+ fun staticallyImportedMethods_differentClass() {
+ val code = """
+ import static org.example.Test.a;
+ import static org.example.Test2.b;
+ """
+ val imported = CodeUtils.staticallyImportedMethods(StaticJavaParser.parse(code),
+ "org.example.Test")
+ assertTrue(imported.containsAll(listOf("a")))
+ assertEquals(1, imported.size)
+ }
+
+ @Test
+ fun staticallyImportedMethods_notStatic() {
+ val code = """
+ import static org.example.Test.a;
+ import org.example.Test.b;
+ """
+ val imported = CodeUtils.staticallyImportedMethods(StaticJavaParser.parse(code),
+ "org.example.Test")
+ assertTrue(imported.containsAll(listOf("a")))
+ assertEquals(1, imported.size)
+ }
+
+ @Test
+ fun concatMultilineString_single() {
+ val str = StringLiteralExpr("test")
+ val out = CodeUtils.concatMultilineString(str, ParsingContext())
+ assertEquals("test", out)
+ }
+
+ @Test
+ fun concatMultilineString_double() {
+ val str = """
+ "test" + "abc"
+ """
+ val code = StaticJavaParser.parseExpression<BinaryExpr>(str)
+ val out = CodeUtils.concatMultilineString(code, ParsingContext())
+ assertEquals("testabc", out)
+ }
+
+ @Test
+ fun concatMultilineString_multiple() {
+ val str = """
+ "test" + "abc" + "1234" + "test"
+ """
+ val code = StaticJavaParser.parseExpression<BinaryExpr>(str)
+ val out = CodeUtils.concatMultilineString(code, ParsingContext())
+ assertEquals("testabc1234test", out)
+ }
+}
diff --git a/tools/protologtool/tests/com/android/protolog/tool/CommandOptionsTest.kt b/tools/protologtool/tests/com/android/protolog/tool/CommandOptionsTest.kt
new file mode 100644
index 000000000000..cf36651c3e39
--- /dev/null
+++ b/tools/protologtool/tests/com/android/protolog/tool/CommandOptionsTest.kt
@@ -0,0 +1,287 @@
+/*
+ * 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.protolog.tool
+
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class CommandOptionsTest {
+ companion object {
+ val TEST_JAVA_SRC = listOf(
+ "frameworks/base/services/core/java/com/android/server/wm/" +
+ "AccessibilityController.java",
+ "frameworks/base/services/core/java/com/android/server/wm/ActivityDisplay.java",
+ "frameworks/base/services/core/java/com/android/server/wm/" +
+ "ActivityMetricsLaunchObserver.java"
+ )
+ private const val TEST_PROTOLOG_CLASS = "com.android.server.wm.ProtoLog"
+ private const val TEST_PROTOLOGIMPL_CLASS = "com.android.server.wm.ProtoLogImpl"
+ private const val TEST_PROTOLOGCACHE_CLASS = "com.android.server.wm.ProtoLog\$Cache"
+ private const val TEST_PROTOLOGGROUP_CLASS = "com.android.server.wm.ProtoLogGroup"
+ private const val TEST_PROTOLOGGROUP_JAR = "out/soong/.intermediates/frameworks/base/" +
+ "services/core/services.core.wm.protologgroups/android_common/javac/" +
+ "services.core.wm.protologgroups.jar"
+ private const val TEST_SRC_JAR = "out/soong/.temp/sbox175955373/" +
+ "services.core.wm.protolog.srcjar"
+ private const val TEST_VIEWER_JSON = "out/soong/.temp/sbox175955373/" +
+ "services.core.wm.protolog.json"
+ private const val TEST_LOG = "./test_log.pb"
+ }
+
+ @Test(expected = InvalidCommandException::class)
+ fun noCommand() {
+ CommandOptions(arrayOf())
+ }
+
+ @Test(expected = InvalidCommandException::class)
+ fun invalidCommand() {
+ val testLine = "invalid"
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+
+ @Test
+ fun transformClasses() {
+ val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
+ "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
+ "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
+ "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
+ val cmd = CommandOptions(testLine.split(' ').toTypedArray())
+ assertEquals(CommandOptions.TRANSFORM_CALLS_CMD, cmd.command)
+ assertEquals(TEST_PROTOLOG_CLASS, cmd.protoLogClassNameArg)
+ assertEquals(TEST_PROTOLOGIMPL_CLASS, cmd.protoLogImplClassNameArg)
+ assertEquals(TEST_PROTOLOGGROUP_CLASS, cmd.protoLogGroupsClassNameArg)
+ assertEquals(TEST_PROTOLOGGROUP_JAR, cmd.protoLogGroupsJarArg)
+ assertEquals(TEST_SRC_JAR, cmd.outputSourceJarArg)
+ assertEquals(TEST_JAVA_SRC, cmd.javaSourceArgs)
+ }
+
+ @Test(expected = InvalidCommandException::class)
+ fun transformClasses_noProtoLogClass() {
+ val testLine = "transform-protolog-calls " +
+ "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
+ "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
+ "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+
+ @Test(expected = InvalidCommandException::class)
+ fun transformClasses_noProtoLogImplClass() {
+ val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
+ "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
+ "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+
+ @Test(expected = InvalidCommandException::class)
+ fun transformClasses_noProtoLogCacheClass() {
+ val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
+ "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
+ "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
+ "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+
+ @Test(expected = InvalidCommandException::class)
+ fun transformClasses_noProtoLogGroupClass() {
+ val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
+ "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
+ "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+
+ @Test(expected = InvalidCommandException::class)
+ fun transformClasses_noProtoLogGroupJar() {
+ val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
+ "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
+ "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
+ "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+
+ @Test(expected = InvalidCommandException::class)
+ fun transformClasses_noOutJar() {
+ val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
+ "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
+ "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
+ "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ TEST_JAVA_SRC.joinToString(" ")
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+
+ @Test(expected = InvalidCommandException::class)
+ fun transformClasses_noJavaInput() {
+ val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
+ "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
+ "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
+ "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--output-srcjar $TEST_SRC_JAR"
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+
+ @Test(expected = InvalidCommandException::class)
+ fun transformClasses_invalidProtoLogClass() {
+ val testLine = "transform-protolog-calls --protolog-class invalid " +
+ "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
+ "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
+ "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+
+ @Test(expected = InvalidCommandException::class)
+ fun transformClasses_invalidProtoLogImplClass() {
+ val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
+ "--protolog-impl-class invalid " +
+ "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
+ "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+
+ @Test(expected = InvalidCommandException::class)
+ fun transformClasses_invalidProtoLogCacheClass() {
+ val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
+ "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
+ "--protolog-cache-class invalid " +
+ "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
+ "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+
+ @Test(expected = InvalidCommandException::class)
+ fun transformClasses_invalidProtoLogGroupClass() {
+ val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
+ "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
+ "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ "--loggroups-class invalid " +
+ "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+
+ @Test(expected = InvalidCommandException::class)
+ fun transformClasses_invalidProtoLogGroupJar() {
+ val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
+ "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
+ "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
+ "--loggroups-jar invalid.txt " +
+ "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+
+ @Test(expected = InvalidCommandException::class)
+ fun transformClasses_invalidOutJar() {
+ val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
+ "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
+ "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
+ "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--output-srcjar invalid.db ${TEST_JAVA_SRC.joinToString(" ")}"
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+
+ @Test(expected = InvalidCommandException::class)
+ fun transformClasses_invalidJavaInput() {
+ val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
+ "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
+ "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
+ "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--output-srcjar $TEST_SRC_JAR invalid.py"
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+
+ @Test(expected = InvalidCommandException::class)
+ fun transformClasses_unknownParam() {
+ val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
+ "--unknown test --protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
+ "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
+ "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+
+ @Test(expected = InvalidCommandException::class)
+ fun transformClasses_noValue() {
+ val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
+ "--protolog-impl-class " +
+ "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
+ "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+
+ @Test
+ fun generateConfig() {
+ val testLine = "generate-viewer-config --protolog-class $TEST_PROTOLOG_CLASS " +
+ "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
+ "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--viewer-conf $TEST_VIEWER_JSON ${TEST_JAVA_SRC.joinToString(" ")}"
+ val cmd = CommandOptions(testLine.split(' ').toTypedArray())
+ assertEquals(CommandOptions.GENERATE_CONFIG_CMD, cmd.command)
+ assertEquals(TEST_PROTOLOG_CLASS, cmd.protoLogClassNameArg)
+ assertEquals(TEST_PROTOLOGGROUP_CLASS, cmd.protoLogGroupsClassNameArg)
+ assertEquals(TEST_PROTOLOGGROUP_JAR, cmd.protoLogGroupsJarArg)
+ assertEquals(TEST_VIEWER_JSON, cmd.viewerConfigJsonArg)
+ assertEquals(TEST_JAVA_SRC, cmd.javaSourceArgs)
+ }
+
+ @Test(expected = InvalidCommandException::class)
+ fun generateConfig_noViewerConfig() {
+ val testLine = "generate-viewer-config --protolog-class $TEST_PROTOLOG_CLASS " +
+ "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
+ "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ TEST_JAVA_SRC.joinToString(" ")
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+
+ @Test(expected = InvalidCommandException::class)
+ fun generateConfig_invalidViewerConfig() {
+ val testLine = "generate-viewer-config --protolog-class $TEST_PROTOLOG_CLASS " +
+ "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
+ "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--viewer-conf invalid.yaml ${TEST_JAVA_SRC.joinToString(" ")}"
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+
+ @Test
+ fun readLog() {
+ val testLine = "read-log --viewer-conf $TEST_VIEWER_JSON $TEST_LOG"
+ val cmd = CommandOptions(testLine.split(' ').toTypedArray())
+ assertEquals(CommandOptions.READ_LOG_CMD, cmd.command)
+ assertEquals(TEST_VIEWER_JSON, cmd.viewerConfigJsonArg)
+ assertEquals(TEST_LOG, cmd.logProtofileArg)
+ }
+}
diff --git a/tools/protologtool/tests/com/android/protolog/tool/LogParserTest.kt b/tools/protologtool/tests/com/android/protolog/tool/LogParserTest.kt
new file mode 100644
index 000000000000..04a3bfa499d8
--- /dev/null
+++ b/tools/protologtool/tests/com/android/protolog/tool/LogParserTest.kt
@@ -0,0 +1,187 @@
+/*
+ * 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.protolog.tool
+
+import com.android.json.stream.JsonReader
+import com.android.server.protolog.ProtoLogMessage
+import com.android.server.protolog.ProtoLogFileProto
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito
+import org.mockito.Mockito.mock
+import java.io.ByteArrayOutputStream
+import java.io.InputStream
+import java.io.OutputStream
+import java.io.PrintStream
+import java.text.SimpleDateFormat
+import java.util.Date
+import java.util.Locale
+
+class LogParserTest {
+ private val configParser: ViewerConfigParser = mock(ViewerConfigParser::class.java)
+ private val parser = LogParser(configParser)
+ private var config: MutableMap<Int, ViewerConfigParser.ConfigEntry> = mutableMapOf()
+ private var outStream: OutputStream = ByteArrayOutputStream()
+ private var printStream: PrintStream = PrintStream(outStream)
+ private val dateFormat = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
+
+ @Before
+ fun init() {
+ Mockito.`when`(configParser.parseConfig(any(JsonReader::class.java))).thenReturn(config)
+ }
+
+ private fun <T> any(type: Class<T>): T = Mockito.any<T>(type)
+
+ private fun getConfigDummyStream(): InputStream {
+ return "".byteInputStream()
+ }
+
+ private fun buildProtoInput(logBuilder: ProtoLogFileProto.Builder): InputStream {
+ logBuilder.setVersion(Constants.VERSION)
+ logBuilder.magicNumber =
+ ProtoLogFileProto.MagicNumber.MAGIC_NUMBER_H.number.toLong() shl 32 or
+ ProtoLogFileProto.MagicNumber.MAGIC_NUMBER_L.number.toLong()
+ return logBuilder.build().toByteArray().inputStream()
+ }
+
+ private fun testDate(timeMS: Long): String {
+ return dateFormat.format(Date(timeMS))
+ }
+
+ @Test
+ fun parse() {
+ config[70933285] = ViewerConfigParser.ConfigEntry("Test completed successfully: %b",
+ "ERROR", "WindowManager")
+
+ val logBuilder = ProtoLogFileProto.newBuilder()
+ val logMessageBuilder = ProtoLogMessage.newBuilder()
+ logMessageBuilder
+ .setMessageHash(70933285)
+ .setElapsedRealtimeNanos(0)
+ .addBooleanParams(true)
+ logBuilder.addLog(logMessageBuilder.build())
+
+ parser.parse(buildProtoInput(logBuilder), getConfigDummyStream(), printStream)
+
+ assertEquals("${testDate(0)} ERROR WindowManager: Test completed successfully: true\n",
+ outStream.toString())
+ }
+
+ @Test
+ fun parse_formatting() {
+ config[123] = ViewerConfigParser.ConfigEntry("Test completed successfully: %b %d %% %o" +
+ " %x %e %g %s %f", "ERROR", "WindowManager")
+
+ val logBuilder = ProtoLogFileProto.newBuilder()
+ val logMessageBuilder = ProtoLogMessage.newBuilder()
+ logMessageBuilder
+ .setMessageHash(123)
+ .setElapsedRealtimeNanos(0)
+ .addBooleanParams(true)
+ .addAllSint64Params(listOf(1000, 20000, 300000))
+ .addAllDoubleParams(listOf(0.1, 0.00001, 1000.1))
+ .addStrParams("test")
+ logBuilder.addLog(logMessageBuilder.build())
+
+ parser.parse(buildProtoInput(logBuilder), getConfigDummyStream(), printStream)
+
+ assertEquals("${testDate(0)} ERROR WindowManager: Test completed successfully: " +
+ "true 1000 % 47040 493e0 1.000000e-01 1.00000e-05 test 1000.100000\n",
+ outStream.toString())
+ }
+
+ @Test
+ fun parse_invalidParamsTooMany() {
+ config[123] = ViewerConfigParser.ConfigEntry("Test completed successfully: %b %d %% %o",
+ "ERROR", "WindowManager")
+
+ val logBuilder = ProtoLogFileProto.newBuilder()
+ val logMessageBuilder = ProtoLogMessage.newBuilder()
+ logMessageBuilder
+ .setMessageHash(123)
+ .setElapsedRealtimeNanos(0)
+ .addBooleanParams(true)
+ .addAllSint64Params(listOf(1000, 20000, 300000))
+ .addAllDoubleParams(listOf(0.1, 0.00001, 1000.1))
+ .addStrParams("test")
+ logBuilder.addLog(logMessageBuilder.build())
+
+ parser.parse(buildProtoInput(logBuilder), getConfigDummyStream(), printStream)
+
+ assertEquals("${testDate(0)} INVALID: 123 - [test] [1000, 20000, 300000] " +
+ "[0.1, 1.0E-5, 1000.1] [true]\n", outStream.toString())
+ }
+
+ @Test
+ fun parse_invalidParamsNotEnough() {
+ config[123] = ViewerConfigParser.ConfigEntry("Test completed successfully: %b %d %% %o" +
+ " %x %e %g %s %f", "ERROR", "WindowManager")
+
+ val logBuilder = ProtoLogFileProto.newBuilder()
+ val logMessageBuilder = ProtoLogMessage.newBuilder()
+ logMessageBuilder
+ .setMessageHash(123)
+ .setElapsedRealtimeNanos(0)
+ .addBooleanParams(true)
+ .addStrParams("test")
+ logBuilder.addLog(logMessageBuilder.build())
+
+ parser.parse(buildProtoInput(logBuilder), getConfigDummyStream(), printStream)
+
+ assertEquals("${testDate(0)} INVALID: 123 - [test] [] [] [true]\n",
+ outStream.toString())
+ }
+
+ @Test(expected = InvalidInputException::class)
+ fun parse_invalidMagicNumber() {
+ val logBuilder = ProtoLogFileProto.newBuilder()
+ logBuilder.setVersion(Constants.VERSION)
+ logBuilder.magicNumber = 0
+ val stream = logBuilder.build().toByteArray().inputStream()
+
+ parser.parse(stream, getConfigDummyStream(), printStream)
+ }
+
+ @Test(expected = InvalidInputException::class)
+ fun parse_invalidVersion() {
+ val logBuilder = ProtoLogFileProto.newBuilder()
+ logBuilder.setVersion("invalid")
+ logBuilder.magicNumber =
+ ProtoLogFileProto.MagicNumber.MAGIC_NUMBER_H.number.toLong() shl 32 or
+ ProtoLogFileProto.MagicNumber.MAGIC_NUMBER_L.number.toLong()
+ val stream = logBuilder.build().toByteArray().inputStream()
+
+ parser.parse(stream, getConfigDummyStream(), printStream)
+ }
+
+ @Test
+ fun parse_noConfig() {
+ val logBuilder = ProtoLogFileProto.newBuilder()
+ val logMessageBuilder = ProtoLogMessage.newBuilder()
+ logMessageBuilder
+ .setMessageHash(70933285)
+ .setElapsedRealtimeNanos(0)
+ .addBooleanParams(true)
+ logBuilder.addLog(logMessageBuilder.build())
+
+ parser.parse(buildProtoInput(logBuilder), getConfigDummyStream(), printStream)
+
+ assertEquals("${testDate(0)} UNKNOWN: 70933285 - [] [] [] [true]\n",
+ outStream.toString())
+ }
+}
diff --git a/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorTest.kt
new file mode 100644
index 000000000000..97f67a0a3fdb
--- /dev/null
+++ b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorTest.kt
@@ -0,0 +1,226 @@
+/*
+ * 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.protolog.tool
+
+import com.github.javaparser.StaticJavaParser
+import com.github.javaparser.ast.expr.MethodCallExpr
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class ProtoLogCallProcessorTest {
+ private data class LogCall(
+ val call: MethodCallExpr,
+ val messageString: String,
+ val level: LogLevel,
+ val group: LogGroup
+ )
+
+ private val groupMap: MutableMap<String, LogGroup> = mutableMapOf()
+ private val calls: MutableList<LogCall> = mutableListOf()
+ private val visitor = ProtoLogCallProcessor("org.example.ProtoLog", "org.example.ProtoLogGroup",
+ groupMap)
+ private val processor = object : ProtoLogCallVisitor {
+ override fun processCall(
+ call: MethodCallExpr,
+ messageString: String,
+ level: LogLevel,
+ group: LogGroup
+ ) {
+ calls.add(LogCall(call, messageString, level, group))
+ }
+ }
+
+ private fun checkCalls() {
+ assertEquals(1, calls.size)
+ val c = calls[0]
+ assertEquals("test %b", c.messageString)
+ assertEquals(groupMap["TEST"], c.group)
+ assertEquals(LogLevel.DEBUG, c.level)
+ }
+
+ @Test
+ fun process_samePackage() {
+ val code = """
+ package org.example;
+
+ class Test {
+ void test() {
+ ProtoLog.d(ProtoLogGroup.TEST, "test %b", true);
+ ProtoLog.e(ProtoLogGroup.ERROR, "error %d", 1);
+ }
+ }
+ """
+ groupMap["TEST"] = LogGroup("TEST", true, false, "WindowManager")
+ groupMap["ERROR"] = LogGroup("ERROR", true, true, "WindowManagerERROR")
+ visitor.process(StaticJavaParser.parse(code), processor, "")
+ assertEquals(2, calls.size)
+ var c = calls[0]
+ assertEquals("test %b", c.messageString)
+ assertEquals(groupMap["TEST"], c.group)
+ assertEquals(LogLevel.DEBUG, c.level)
+ c = calls[1]
+ assertEquals("error %d", c.messageString)
+ assertEquals(groupMap["ERROR"], c.group)
+ assertEquals(LogLevel.ERROR, c.level)
+ }
+
+ @Test
+ fun process_imported() {
+ val code = """
+ package org.example2;
+
+ import org.example.ProtoLog;
+ import org.example.ProtoLogGroup;
+
+ class Test {
+ void test() {
+ ProtoLog.d(ProtoLogGroup.TEST, "test %b", true);
+ }
+ }
+ """
+ groupMap["TEST"] = LogGroup("TEST", true, true, "WindowManager")
+ visitor.process(StaticJavaParser.parse(code), processor, "")
+ checkCalls()
+ }
+
+ @Test
+ fun process_importedStatic() {
+ val code = """
+ package org.example2;
+
+ import static org.example.ProtoLog.d;
+ import static org.example.ProtoLogGroup.TEST;
+
+ class Test {
+ void test() {
+ d(TEST, "test %b", true);
+ }
+ }
+ """
+ groupMap["TEST"] = LogGroup("TEST", true, true, "WindowManager")
+ visitor.process(StaticJavaParser.parse(code), processor, "")
+ checkCalls()
+ }
+
+ @Test(expected = InvalidProtoLogCallException::class)
+ fun process_groupNotImported() {
+ val code = """
+ package org.example2;
+
+ import org.example.ProtoLog;
+
+ class Test {
+ void test() {
+ ProtoLog.d(ProtoLogGroup.TEST, "test %b", true);
+ }
+ }
+ """
+ groupMap["TEST"] = LogGroup("TEST", true, true, "WindowManager")
+ visitor.process(StaticJavaParser.parse(code), processor, "")
+ }
+
+ @Test
+ fun process_protoLogNotImported() {
+ val code = """
+ package org.example2;
+
+ import org.example.ProtoLogGroup;
+
+ class Test {
+ void test() {
+ ProtoLog.d(ProtoLogGroup.TEST, "test %b", true);
+ }
+ }
+ """
+ groupMap["TEST"] = LogGroup("TEST", true, true, "WindowManager")
+ visitor.process(StaticJavaParser.parse(code), processor, "")
+ assertEquals(0, calls.size)
+ }
+
+ @Test(expected = InvalidProtoLogCallException::class)
+ fun process_unknownGroup() {
+ val code = """
+ package org.example;
+
+ class Test {
+ void test() {
+ ProtoLog.d(ProtoLogGroup.TEST, "test %b", true);
+ }
+ }
+ """
+ visitor.process(StaticJavaParser.parse(code), processor, "")
+ }
+
+ @Test(expected = InvalidProtoLogCallException::class)
+ fun process_staticGroup() {
+ val code = """
+ package org.example;
+
+ class Test {
+ void test() {
+ ProtoLog.d(TEST, "test %b", true);
+ }
+ }
+ """
+ visitor.process(StaticJavaParser.parse(code), processor, "")
+ }
+
+ @Test(expected = InvalidProtoLogCallException::class)
+ fun process_badGroup() {
+ val code = """
+ package org.example;
+
+ class Test {
+ void test() {
+ ProtoLog.d(0, "test %b", true);
+ }
+ }
+ """
+ visitor.process(StaticJavaParser.parse(code), processor, "")
+ }
+
+ @Test(expected = InvalidProtoLogCallException::class)
+ fun process_invalidSignature() {
+ val code = """
+ package org.example;
+
+ class Test {
+ void test() {
+ ProtoLog.d("test");
+ }
+ }
+ """
+ visitor.process(StaticJavaParser.parse(code), processor, "")
+ }
+
+ @Test
+ fun process_disabled() {
+ // Disabled groups are also processed.
+ val code = """
+ package org.example;
+
+ class Test {
+ void test() {
+ ProtoLog.d(ProtoLogGroup.TEST, "test %b", true);
+ }
+ }
+ """
+ groupMap["TEST"] = LogGroup("TEST", false, true, "WindowManager")
+ visitor.process(StaticJavaParser.parse(code), processor, "")
+ checkCalls()
+ }
+}
diff --git a/tools/protologtool/tests/com/android/protolog/tool/ProtoLogToolTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogToolTest.kt
new file mode 100644
index 000000000000..ea9a58d859af
--- /dev/null
+++ b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogToolTest.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.protolog.tool
+
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class ProtoLogToolTest {
+
+ @Test
+ fun generateLogGroupCache() {
+ val groups = mapOf(
+ "GROUP1" to LogGroup("GROUP1", true, true, "TAG1"),
+ "GROUP2" to LogGroup("GROUP2", true, true, "TAG2")
+ )
+ val code = ProtoLogTool.generateLogGroupCache("org.example", "ProtoLog\$Cache",
+ groups, "org.example.ProtoLogImpl", "org.example.ProtoLogGroups")
+
+ assertEquals("""
+ package org.example;
+
+ public class ProtoLog${'$'}Cache {
+ public static boolean GROUP1_enabled = false;
+ public static boolean GROUP2_enabled = false;
+
+ static {
+ org.example.ProtoLogImpl.sCacheUpdater = ProtoLog${'$'}Cache::update;
+ update();
+ }
+
+ static void update() {
+ GROUP1_enabled = org.example.ProtoLogImpl.isEnabled(org.example.ProtoLogGroups.GROUP1);
+ GROUP2_enabled = org.example.ProtoLogImpl.isEnabled(org.example.ProtoLogGroups.GROUP2);
+ }
+ }
+ """.trimIndent(), code)
+ }
+} \ No newline at end of file
diff --git a/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt b/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
new file mode 100644
index 000000000000..6f5955cd030b
--- /dev/null
+++ b/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
@@ -0,0 +1,451 @@
+/*
+ * 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.protolog.tool
+
+import com.github.javaparser.StaticJavaParser
+import com.github.javaparser.ast.CompilationUnit
+import com.github.javaparser.ast.expr.MethodCallExpr
+import com.github.javaparser.ast.stmt.IfStmt
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Test
+import org.mockito.Mockito
+
+class SourceTransformerTest {
+ companion object {
+ private const val PROTO_LOG_IMPL_PATH = "org.example.ProtoLogImpl"
+
+ /* ktlint-disable max-line-length */
+ private val TEST_CODE = """
+ package org.example;
+
+ class Test {
+ void test() {
+ ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1);
+ }
+ }
+ """.trimIndent()
+
+ private val TEST_CODE_MULTILINE = """
+ package org.example;
+
+ class Test {
+ void test() {
+ ProtoLog.w(TEST_GROUP, "test %d %f " +
+ "abc %s\n test", 100,
+ 0.1, "test");
+ }
+ }
+ """.trimIndent()
+
+ private val TEST_CODE_MULTICALLS = """
+ package org.example;
+
+ class Test {
+ void test() {
+ ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1); /* ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1); */ ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1);
+ ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1);
+ }
+ }
+ """.trimIndent()
+
+ private val TEST_CODE_NO_PARAMS = """
+ package org.example;
+
+ class Test {
+ void test() {
+ ProtoLog.w(TEST_GROUP, "test");
+ }
+ }
+ """.trimIndent()
+
+ private val TRANSFORMED_CODE_TEXT_ENABLED = """
+ package org.example;
+
+ class Test {
+ void test() {
+ if (org.example.ProtoLogCache.TEST_GROUP_enabled) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 1698911065, 9, "test %d %f", protoLogParam0, protoLogParam1); }
+ }
+ }
+ """.trimIndent()
+
+ private val TRANSFORMED_CODE_MULTILINE_TEXT_ENABLED = """
+ package org.example;
+
+ class Test {
+ void test() {
+ if (org.example.ProtoLogCache.TEST_GROUP_enabled) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; String protoLogParam2 = String.valueOf("test"); org.example.ProtoLogImpl.w(TEST_GROUP, 1780316587, 9, "test %d %f " + "abc %s\n test", protoLogParam0, protoLogParam1, protoLogParam2);
+
+ }
+ }
+ }
+ """.trimIndent()
+
+ private val TRANSFORMED_CODE_MULTICALL_TEXT_ENABLED = """
+ package org.example;
+
+ class Test {
+ void test() {
+ if (org.example.ProtoLogCache.TEST_GROUP_enabled) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 1698911065, 9, "test %d %f", protoLogParam0, protoLogParam1); } /* ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1); */ if (org.example.ProtoLogCache.TEST_GROUP_enabled) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 1698911065, 9, "test %d %f", protoLogParam0, protoLogParam1); }
+ if (org.example.ProtoLogCache.TEST_GROUP_enabled) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 1698911065, 9, "test %d %f", protoLogParam0, protoLogParam1); }
+ }
+ }
+ """.trimIndent()
+
+ private val TRANSFORMED_CODE_NO_PARAMS = """
+ package org.example;
+
+ class Test {
+ void test() {
+ if (org.example.ProtoLogCache.TEST_GROUP_enabled) { org.example.ProtoLogImpl.w(TEST_GROUP, -1741986185, 0, "test", (Object[]) null); }
+ }
+ }
+ """.trimIndent()
+
+ private val TRANSFORMED_CODE_TEXT_DISABLED = """
+ package org.example;
+
+ class Test {
+ void test() {
+ if (org.example.ProtoLogCache.TEST_GROUP_enabled) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 1698911065, 9, null, protoLogParam0, protoLogParam1); }
+ }
+ }
+ """.trimIndent()
+
+ private val TRANSFORMED_CODE_MULTILINE_TEXT_DISABLED = """
+ package org.example;
+
+ class Test {
+ void test() {
+ if (org.example.ProtoLogCache.TEST_GROUP_enabled) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; String protoLogParam2 = String.valueOf("test"); org.example.ProtoLogImpl.w(TEST_GROUP, 1780316587, 9, null, protoLogParam0, protoLogParam1, protoLogParam2);
+
+ }
+ }
+ }
+ """.trimIndent()
+
+ private val TRANSFORMED_CODE_DISABLED = """
+ package org.example;
+
+ class Test {
+ void test() {
+ if (false) { /* TEST_GROUP is disabled */ ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1); }
+ }
+ }
+ """.trimIndent()
+
+ private val TRANSFORMED_CODE_MULTILINE_DISABLED = """
+ package org.example;
+
+ class Test {
+ void test() {
+ if (false) { /* TEST_GROUP is disabled */ ProtoLog.w(TEST_GROUP, "test %d %f " + "abc %s\n test", 100, 0.1, "test");
+
+ }
+ }
+ }
+ """.trimIndent()
+ /* ktlint-enable max-line-length */
+
+ private const val PATH = "com.example.Test.java"
+ }
+
+ private val processor: ProtoLogCallProcessor = Mockito.mock(ProtoLogCallProcessor::class.java)
+ private val implName = "org.example.ProtoLogImpl"
+ private val cacheName = "org.example.ProtoLogCache"
+ private val sourceJarWriter = SourceTransformer(implName, cacheName, processor)
+
+ private fun <T> any(type: Class<T>): T = Mockito.any<T>(type)
+
+ @Test
+ fun processClass_textEnabled() {
+ var code = StaticJavaParser.parse(TEST_CODE)
+
+ Mockito.`when`(processor.process(any(CompilationUnit::class.java),
+ any(ProtoLogCallVisitor::class.java), any(String::class.java)))
+ .thenAnswer { invocation ->
+ val visitor = invocation.arguments[1] as ProtoLogCallVisitor
+
+ visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test %d %f",
+ LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"))
+
+ invocation.arguments[0] as CompilationUnit
+ }
+
+ val out = sourceJarWriter.processClass(TEST_CODE, PATH, code)
+ code = StaticJavaParser.parse(out)
+
+ val ifStmts = code.findAll(IfStmt::class.java)
+ assertEquals(1, ifStmts.size)
+ val ifStmt = ifStmts[0]
+ assertEquals("$cacheName.TEST_GROUP_enabled", ifStmt.condition.toString())
+ assertFalse(ifStmt.elseStmt.isPresent)
+ assertEquals(3, ifStmt.thenStmt.childNodes.size)
+ val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[0] as MethodCallExpr
+ assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString())
+ assertEquals("w", methodCall.name.asString())
+ assertEquals(6, methodCall.arguments.size)
+ assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
+ assertEquals("1698911065", methodCall.arguments[1].toString())
+ assertEquals(0b1001.toString(), methodCall.arguments[2].toString())
+ assertEquals("\"test %d %f\"", methodCall.arguments[3].toString())
+ assertEquals("protoLogParam0", methodCall.arguments[4].toString())
+ assertEquals("protoLogParam1", methodCall.arguments[5].toString())
+ assertEquals(TRANSFORMED_CODE_TEXT_ENABLED, out)
+ }
+
+ @Test
+ fun processClass_textEnabledMulticalls() {
+ var code = StaticJavaParser.parse(TEST_CODE_MULTICALLS)
+
+ Mockito.`when`(processor.process(any(CompilationUnit::class.java),
+ any(ProtoLogCallVisitor::class.java), any(String::class.java)))
+ .thenAnswer { invocation ->
+ val visitor = invocation.arguments[1] as ProtoLogCallVisitor
+
+ val calls = code.findAll(MethodCallExpr::class.java)
+ visitor.processCall(calls[0], "test %d %f",
+ LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"))
+ visitor.processCall(calls[1], "test %d %f",
+ LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"))
+ visitor.processCall(calls[2], "test %d %f",
+ LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"))
+
+ invocation.arguments[0] as CompilationUnit
+ }
+
+ val out = sourceJarWriter.processClass(TEST_CODE_MULTICALLS, PATH, code)
+ code = StaticJavaParser.parse(out)
+
+ val ifStmts = code.findAll(IfStmt::class.java)
+ assertEquals(3, ifStmts.size)
+ val ifStmt = ifStmts[1]
+ assertEquals("$cacheName.TEST_GROUP_enabled", ifStmt.condition.toString())
+ assertFalse(ifStmt.elseStmt.isPresent)
+ assertEquals(3, ifStmt.thenStmt.childNodes.size)
+ val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[0] as MethodCallExpr
+ assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString())
+ assertEquals("w", methodCall.name.asString())
+ assertEquals(6, methodCall.arguments.size)
+ assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
+ assertEquals("1698911065", methodCall.arguments[1].toString())
+ assertEquals(0b1001.toString(), methodCall.arguments[2].toString())
+ assertEquals("\"test %d %f\"", methodCall.arguments[3].toString())
+ assertEquals("protoLogParam0", methodCall.arguments[4].toString())
+ assertEquals("protoLogParam1", methodCall.arguments[5].toString())
+ assertEquals(TRANSFORMED_CODE_MULTICALL_TEXT_ENABLED, out)
+ }
+
+ @Test
+ fun processClass_textEnabledMultiline() {
+ var code = StaticJavaParser.parse(TEST_CODE_MULTILINE)
+
+ Mockito.`when`(processor.process(any(CompilationUnit::class.java),
+ any(ProtoLogCallVisitor::class.java), any(String::class.java)))
+ .thenAnswer { invocation ->
+ val visitor = invocation.arguments[1] as ProtoLogCallVisitor
+
+ visitor.processCall(code.findAll(MethodCallExpr::class.java)[0],
+ "test %d %f abc %s\n test", LogLevel.WARN, LogGroup("TEST_GROUP",
+ true, true, "WM_TEST"))
+
+ invocation.arguments[0] as CompilationUnit
+ }
+
+ val out = sourceJarWriter.processClass(TEST_CODE_MULTILINE, PATH, code)
+ code = StaticJavaParser.parse(out)
+
+ val ifStmts = code.findAll(IfStmt::class.java)
+ assertEquals(1, ifStmts.size)
+ val ifStmt = ifStmts[0]
+ assertEquals("$cacheName.TEST_GROUP_enabled", ifStmt.condition.toString())
+ assertFalse(ifStmt.elseStmt.isPresent)
+ assertEquals(4, ifStmt.thenStmt.childNodes.size)
+ val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[1] as MethodCallExpr
+ assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString())
+ assertEquals("w", methodCall.name.asString())
+ assertEquals(7, methodCall.arguments.size)
+ assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
+ assertEquals("1780316587", methodCall.arguments[1].toString())
+ assertEquals(0b001001.toString(), methodCall.arguments[2].toString())
+ assertEquals("protoLogParam0", methodCall.arguments[4].toString())
+ assertEquals("protoLogParam1", methodCall.arguments[5].toString())
+ assertEquals("protoLogParam2", methodCall.arguments[6].toString())
+ assertEquals(TRANSFORMED_CODE_MULTILINE_TEXT_ENABLED, out)
+ }
+
+ @Test
+ fun processClass_noParams() {
+ var code = StaticJavaParser.parse(TEST_CODE_NO_PARAMS)
+
+ Mockito.`when`(processor.process(any(CompilationUnit::class.java),
+ any(ProtoLogCallVisitor::class.java), any(String::class.java)))
+ .thenAnswer { invocation ->
+ val visitor = invocation.arguments[1] as ProtoLogCallVisitor
+
+ visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test",
+ LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"))
+
+ invocation.arguments[0] as CompilationUnit
+ }
+
+ val out = sourceJarWriter.processClass(TEST_CODE_NO_PARAMS, PATH, code)
+ code = StaticJavaParser.parse(out)
+
+ val ifStmts = code.findAll(IfStmt::class.java)
+ assertEquals(1, ifStmts.size)
+ val ifStmt = ifStmts[0]
+ assertEquals("$cacheName.TEST_GROUP_enabled", ifStmt.condition.toString())
+ assertFalse(ifStmt.elseStmt.isPresent)
+ assertEquals(1, ifStmt.thenStmt.childNodes.size)
+ val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[0] as MethodCallExpr
+ assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString())
+ assertEquals("w", methodCall.name.asString())
+ assertEquals(5, methodCall.arguments.size)
+ assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
+ assertEquals("-1741986185", methodCall.arguments[1].toString())
+ assertEquals(0.toString(), methodCall.arguments[2].toString())
+ assertEquals(TRANSFORMED_CODE_NO_PARAMS, out)
+ }
+
+ @Test
+ fun processClass_textDisabled() {
+ var code = StaticJavaParser.parse(TEST_CODE)
+
+ Mockito.`when`(processor.process(any(CompilationUnit::class.java),
+ any(ProtoLogCallVisitor::class.java), any(String::class.java)))
+ .thenAnswer { invocation ->
+ val visitor = invocation.arguments[1] as ProtoLogCallVisitor
+
+ visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test %d %f",
+ LogLevel.WARN, LogGroup("TEST_GROUP", true, false, "WM_TEST"))
+
+ invocation.arguments[0] as CompilationUnit
+ }
+
+ val out = sourceJarWriter.processClass(TEST_CODE, PATH, code)
+ code = StaticJavaParser.parse(out)
+
+ val ifStmts = code.findAll(IfStmt::class.java)
+ assertEquals(1, ifStmts.size)
+ val ifStmt = ifStmts[0]
+ assertEquals("$cacheName.TEST_GROUP_enabled", ifStmt.condition.toString())
+ assertFalse(ifStmt.elseStmt.isPresent)
+ assertEquals(3, ifStmt.thenStmt.childNodes.size)
+ val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[0] as MethodCallExpr
+ assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString())
+ assertEquals("w", methodCall.name.asString())
+ assertEquals(6, methodCall.arguments.size)
+ assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
+ assertEquals("1698911065", methodCall.arguments[1].toString())
+ assertEquals(0b1001.toString(), methodCall.arguments[2].toString())
+ assertEquals("null", methodCall.arguments[3].toString())
+ assertEquals("protoLogParam0", methodCall.arguments[4].toString())
+ assertEquals("protoLogParam1", methodCall.arguments[5].toString())
+ assertEquals(TRANSFORMED_CODE_TEXT_DISABLED, out)
+ }
+
+ @Test
+ fun processClass_textDisabledMultiline() {
+ var code = StaticJavaParser.parse(TEST_CODE_MULTILINE)
+
+ Mockito.`when`(processor.process(any(CompilationUnit::class.java),
+ any(ProtoLogCallVisitor::class.java), any(String::class.java)))
+ .thenAnswer { invocation ->
+ val visitor = invocation.arguments[1] as ProtoLogCallVisitor
+
+ visitor.processCall(code.findAll(MethodCallExpr::class.java)[0],
+ "test %d %f abc %s\n test", LogLevel.WARN, LogGroup("TEST_GROUP",
+ true, false, "WM_TEST"))
+
+ invocation.arguments[0] as CompilationUnit
+ }
+
+ val out = sourceJarWriter.processClass(TEST_CODE_MULTILINE, PATH, code)
+ code = StaticJavaParser.parse(out)
+
+ val ifStmts = code.findAll(IfStmt::class.java)
+ assertEquals(1, ifStmts.size)
+ val ifStmt = ifStmts[0]
+ assertEquals("$cacheName.TEST_GROUP_enabled", ifStmt.condition.toString())
+ assertFalse(ifStmt.elseStmt.isPresent)
+ assertEquals(4, ifStmt.thenStmt.childNodes.size)
+ val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[1] as MethodCallExpr
+ assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString())
+ assertEquals("w", methodCall.name.asString())
+ assertEquals(7, methodCall.arguments.size)
+ assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
+ assertEquals("1780316587", methodCall.arguments[1].toString())
+ assertEquals(0b001001.toString(), methodCall.arguments[2].toString())
+ assertEquals("null", methodCall.arguments[3].toString())
+ assertEquals("protoLogParam0", methodCall.arguments[4].toString())
+ assertEquals("protoLogParam1", methodCall.arguments[5].toString())
+ assertEquals("protoLogParam2", methodCall.arguments[6].toString())
+ assertEquals(TRANSFORMED_CODE_MULTILINE_TEXT_DISABLED, out)
+ }
+
+ @Test
+ fun processClass_disabled() {
+ var code = StaticJavaParser.parse(TEST_CODE)
+
+ Mockito.`when`(processor.process(any(CompilationUnit::class.java),
+ any(ProtoLogCallVisitor::class.java), any(String::class.java)))
+ .thenAnswer { invocation ->
+ val visitor = invocation.arguments[1] as ProtoLogCallVisitor
+
+ visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test %d %f",
+ LogLevel.WARN, LogGroup("TEST_GROUP", false, true, "WM_TEST"))
+
+ invocation.arguments[0] as CompilationUnit
+ }
+
+ val out = sourceJarWriter.processClass(TEST_CODE, PATH, code)
+ code = StaticJavaParser.parse(out)
+
+ val ifStmts = code.findAll(IfStmt::class.java)
+ assertEquals(1, ifStmts.size)
+ val ifStmt = ifStmts[0]
+ assertEquals("false", ifStmt.condition.toString())
+ assertEquals(TRANSFORMED_CODE_DISABLED, out)
+ }
+
+ @Test
+ fun processClass_disabledMultiline() {
+ var code = StaticJavaParser.parse(TEST_CODE_MULTILINE)
+
+ Mockito.`when`(processor.process(any(CompilationUnit::class.java),
+ any(ProtoLogCallVisitor::class.java), any(String::class.java)))
+ .thenAnswer { invocation ->
+ val visitor = invocation.arguments[1] as ProtoLogCallVisitor
+
+ visitor.processCall(code.findAll(MethodCallExpr::class.java)[0],
+ "test %d %f abc %s\n test", LogLevel.WARN, LogGroup("TEST_GROUP",
+ false, true, "WM_TEST"))
+
+ invocation.arguments[0] as CompilationUnit
+ }
+
+ val out = sourceJarWriter.processClass(TEST_CODE_MULTILINE, PATH, code)
+ code = StaticJavaParser.parse(out)
+
+ val ifStmts = code.findAll(IfStmt::class.java)
+ assertEquals(1, ifStmts.size)
+ val ifStmt = ifStmts[0]
+ assertEquals("false", ifStmt.condition.toString())
+ assertEquals(TRANSFORMED_CODE_MULTILINE_DISABLED, out)
+ }
+}
diff --git a/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigBuilderTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigBuilderTest.kt
new file mode 100644
index 000000000000..a24761aed9db
--- /dev/null
+++ b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigBuilderTest.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.protolog.tool
+
+import com.android.json.stream.JsonReader
+import com.android.protolog.tool.ViewerConfigBuilder.LogCall
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.mockito.Mockito
+import java.io.StringReader
+
+class ViewerConfigBuilderTest {
+ companion object {
+ private val TAG1 = "WM_TEST"
+ private val TAG2 = "WM_DEBUG"
+ private val TEST1 = ViewerConfigParser.ConfigEntry("test1", LogLevel.INFO.name, TAG1)
+ private val TEST2 = ViewerConfigParser.ConfigEntry("test2", LogLevel.DEBUG.name, TAG2)
+ private val TEST3 = ViewerConfigParser.ConfigEntry("test3", LogLevel.ERROR.name, TAG2)
+ private val GROUP1 = LogGroup("TEST_GROUP", true, true, TAG1)
+ private val GROUP2 = LogGroup("DEBUG_GROUP", true, true, TAG2)
+ private val GROUP3 = LogGroup("DEBUG_GROUP", true, true, TAG2)
+ private val GROUP_DISABLED = LogGroup("DEBUG_GROUP", false, true, TAG2)
+ private val GROUP_TEXT_DISABLED = LogGroup("DEBUG_GROUP", true, false, TAG2)
+ private const val PATH = "/tmp/test.java"
+ }
+
+ private val configBuilder = ViewerConfigBuilder(Mockito.mock(ProtoLogCallProcessor::class.java))
+
+ private fun parseConfig(json: String): Map<Int, ViewerConfigParser.ConfigEntry> {
+ return ViewerConfigParser().parseConfig(JsonReader(StringReader(json)))
+ }
+
+ @Test
+ fun processClass() {
+ configBuilder.addLogCalls(listOf(
+ LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH),
+ LogCall(TEST2.messageString, LogLevel.DEBUG, GROUP2, PATH),
+ LogCall(TEST3.messageString, LogLevel.ERROR, GROUP3, PATH)).withContext())
+
+ val parsedConfig = parseConfig(configBuilder.build())
+ assertEquals(3, parsedConfig.size)
+ assertEquals(TEST1, parsedConfig[CodeUtils.hash(PATH,
+ TEST1.messageString, LogLevel.INFO, GROUP1)])
+ assertEquals(TEST2, parsedConfig[CodeUtils.hash(PATH, TEST2.messageString,
+ LogLevel.DEBUG, GROUP2)])
+ assertEquals(TEST3, parsedConfig[CodeUtils.hash(PATH, TEST3.messageString,
+ LogLevel.ERROR, GROUP3)])
+ }
+
+ @Test
+ fun processClass_nonUnique() {
+ configBuilder.addLogCalls(listOf(
+ LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH),
+ LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH),
+ LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH)).withContext())
+
+ val parsedConfig = parseConfig(configBuilder.build())
+ assertEquals(1, parsedConfig.size)
+ assertEquals(TEST1, parsedConfig[CodeUtils.hash(PATH, TEST1.messageString,
+ LogLevel.INFO, GROUP1)])
+ }
+
+ @Test
+ fun processClass_disabled() {
+ configBuilder.addLogCalls(listOf(
+ LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH),
+ LogCall(TEST2.messageString, LogLevel.DEBUG, GROUP_DISABLED, PATH),
+ LogCall(TEST3.messageString, LogLevel.ERROR, GROUP_TEXT_DISABLED, PATH))
+ .withContext())
+
+ val parsedConfig = parseConfig(configBuilder.build())
+ assertEquals(2, parsedConfig.size)
+ assertEquals(TEST1, parsedConfig[CodeUtils.hash(
+ PATH, TEST1.messageString, LogLevel.INFO, GROUP1)])
+ assertEquals(TEST3, parsedConfig[CodeUtils.hash(
+ PATH, TEST3.messageString, LogLevel.ERROR, GROUP_TEXT_DISABLED)])
+ }
+
+ private fun List<LogCall>.withContext() = map { it to ParsingContext() }
+}
diff --git a/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigParserTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigParserTest.kt
new file mode 100644
index 000000000000..dc3ef7c57b35
--- /dev/null
+++ b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigParserTest.kt
@@ -0,0 +1,327 @@
+/*
+ * 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.protolog.tool
+
+import com.android.json.stream.JsonReader
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import java.io.StringReader
+
+class ViewerConfigParserTest {
+ private val parser = ViewerConfigParser()
+
+ private fun getJSONReader(str: String): JsonReader {
+ return JsonReader(StringReader(str))
+ }
+
+ @Test
+ fun parseMessage() {
+ val json = """
+ {
+ "message": "Test completed successfully: %b",
+ "level": "ERROR",
+ "group": "GENERIC_WM"
+ }
+ """
+ val msg = parser.parseMessage(getJSONReader(json))
+ assertEquals("Test completed successfully: %b", msg.messageString)
+ assertEquals("ERROR", msg.level)
+ assertEquals("GENERIC_WM", msg.groupName)
+ }
+
+ @Test
+ fun parseMessage_reorder() {
+ val json = """
+ {
+ "group": "GENERIC_WM",
+ "level": "ERROR",
+ "message": "Test completed successfully: %b"
+ }
+ """
+ val msg = parser.parseMessage(getJSONReader(json))
+ assertEquals("Test completed successfully: %b", msg.messageString)
+ assertEquals("ERROR", msg.level)
+ assertEquals("GENERIC_WM", msg.groupName)
+ }
+
+ @Test
+ fun parseMessage_unknownEntry() {
+ val json = """
+ {
+ "unknown": "unknown entries should not block parsing",
+ "message": "Test completed successfully: %b",
+ "level": "ERROR",
+ "group": "GENERIC_WM"
+ }
+ """
+ val msg = parser.parseMessage(getJSONReader(json))
+ assertEquals("Test completed successfully: %b", msg.messageString)
+ assertEquals("ERROR", msg.level)
+ assertEquals("GENERIC_WM", msg.groupName)
+ }
+
+ @Test(expected = InvalidViewerConfigException::class)
+ fun parseMessage_noMessage() {
+ val json = """
+ {
+ "level": "ERROR",
+ "group": "GENERIC_WM"
+ }
+ """
+ parser.parseMessage(getJSONReader(json))
+ }
+
+ @Test(expected = InvalidViewerConfigException::class)
+ fun parseMessage_noLevel() {
+ val json = """
+ {
+ "message": "Test completed successfully: %b",
+ "group": "GENERIC_WM"
+ }
+ """
+ parser.parseMessage(getJSONReader(json))
+ }
+
+ @Test(expected = InvalidViewerConfigException::class)
+ fun parseMessage_noGroup() {
+ val json = """
+ {
+ "message": "Test completed successfully: %b",
+ "level": "ERROR"
+ }
+ """
+ parser.parseMessage(getJSONReader(json))
+ }
+
+ @Test
+ fun parseGroup() {
+ val json = """
+ {
+ "tag": "WindowManager"
+ }
+ """
+ val group = parser.parseGroup(getJSONReader(json))
+ assertEquals("WindowManager", group.tag)
+ }
+
+ @Test
+ fun parseGroup_unknownEntry() {
+ val json = """
+ {
+ "unknown": "unknown entries should not block parsing",
+ "tag": "WindowManager"
+ }
+ """
+ val group = parser.parseGroup(getJSONReader(json))
+ assertEquals("WindowManager", group.tag)
+ }
+
+ @Test(expected = InvalidViewerConfigException::class)
+ fun parseGroup_noTag() {
+ val json = """
+ {
+ }
+ """
+ parser.parseGroup(getJSONReader(json))
+ }
+
+ @Test
+ fun parseMessages() {
+ val json = """
+ {
+ "70933285": {
+ "message": "Test completed successfully: %b",
+ "level": "ERROR",
+ "group": "GENERIC_WM"
+ },
+ "1792430067": {
+ "message": "Attempted to add window to a display that does not exist: %d. Aborting.",
+ "level": "WARN",
+ "group": "ERROR_WM"
+ }
+ }
+ """
+ val messages = parser.parseMessages(getJSONReader(json))
+ assertEquals(2, messages.size)
+ val msg1 =
+ ViewerConfigParser.MessageEntry("Test completed successfully: %b",
+ "ERROR", "GENERIC_WM")
+ val msg2 =
+ ViewerConfigParser.MessageEntry("Attempted to add window to a display that " +
+ "does not exist: %d. Aborting.", "WARN", "ERROR_WM")
+
+ assertEquals(msg1, messages[70933285])
+ assertEquals(msg2, messages[1792430067])
+ }
+
+ @Test(expected = InvalidViewerConfigException::class)
+ fun parseMessages_invalidHash() {
+ val json = """
+ {
+ "invalid": {
+ "message": "Test completed successfully: %b",
+ "level": "ERROR",
+ "group": "GENERIC_WM"
+ }
+ }
+ """
+ parser.parseMessages(getJSONReader(json))
+ }
+
+ @Test
+ fun parseGroups() {
+ val json = """
+ {
+ "GENERIC_WM": {
+ "tag": "WindowManager"
+ },
+ "ERROR_WM": {
+ "tag": "WindowManagerError"
+ }
+ }
+ """
+ val groups = parser.parseGroups(getJSONReader(json))
+ assertEquals(2, groups.size)
+ val grp1 = ViewerConfigParser.GroupEntry("WindowManager")
+ val grp2 = ViewerConfigParser.GroupEntry("WindowManagerError")
+ assertEquals(grp1, groups["GENERIC_WM"])
+ assertEquals(grp2, groups["ERROR_WM"])
+ }
+
+ @Test
+ fun parseConfig() {
+ val json = """
+ {
+ "version": "${Constants.VERSION}",
+ "messages": {
+ "70933285": {
+ "message": "Test completed successfully: %b",
+ "level": "ERROR",
+ "group": "GENERIC_WM"
+ }
+ },
+ "groups": {
+ "GENERIC_WM": {
+ "tag": "WindowManager"
+ }
+ }
+ }
+ """
+ val config = parser.parseConfig(getJSONReader(json))
+ assertEquals(1, config.size)
+ val cfg1 = ViewerConfigParser.ConfigEntry("Test completed successfully: %b",
+ "ERROR", "WindowManager")
+ assertEquals(cfg1, config[70933285])
+ }
+
+ @Test(expected = InvalidViewerConfigException::class)
+ fun parseConfig_invalidVersion() {
+ val json = """
+ {
+ "version": "invalid",
+ "messages": {
+ "70933285": {
+ "message": "Test completed successfully: %b",
+ "level": "ERROR",
+ "group": "GENERIC_WM"
+ }
+ },
+ "groups": {
+ "GENERIC_WM": {
+ "tag": "WindowManager"
+ }
+ }
+ }
+ """
+ parser.parseConfig(getJSONReader(json))
+ }
+
+ @Test(expected = InvalidViewerConfigException::class)
+ fun parseConfig_noVersion() {
+ val json = """
+ {
+ "messages": {
+ "70933285": {
+ "message": "Test completed successfully: %b",
+ "level": "ERROR",
+ "group": "GENERIC_WM"
+ }
+ },
+ "groups": {
+ "GENERIC_WM": {
+ "tag": "WindowManager"
+ }
+ }
+ }
+ """
+ parser.parseConfig(getJSONReader(json))
+ }
+
+ @Test(expected = InvalidViewerConfigException::class)
+ fun parseConfig_noMessages() {
+ val json = """
+ {
+ "version": "${Constants.VERSION}",
+ "groups": {
+ "GENERIC_WM": {
+ "tag": "WindowManager"
+ }
+ }
+ }
+ """
+ parser.parseConfig(getJSONReader(json))
+ }
+
+ @Test(expected = InvalidViewerConfigException::class)
+ fun parseConfig_noGroups() {
+ val json = """
+ {
+ "version": "${Constants.VERSION}",
+ "messages": {
+ "70933285": {
+ "message": "Test completed successfully: %b",
+ "level": "ERROR",
+ "group": "GENERIC_WM"
+ }
+ }
+ }
+ """
+ parser.parseConfig(getJSONReader(json))
+ }
+
+ @Test(expected = InvalidViewerConfigException::class)
+ fun parseConfig_missingGroup() {
+ val json = """
+ {
+ "version": "${Constants.VERSION}",
+ "messages": {
+ "70933285": {
+ "message": "Test completed successfully: %b",
+ "level": "ERROR",
+ "group": "GENERIC_WM"
+ }
+ },
+ "groups": {
+ "ERROR_WM": {
+ "tag": "WindowManager"
+ }
+ }
+ }
+ """
+ parser.parseConfig(getJSONReader(json))
+ }
+}
diff --git a/tools/protologtool/tests/com/android/protologtool/CodeUtilsTest.kt b/tools/protologtool/tests/com/android/protologtool/CodeUtilsTest.kt
deleted file mode 100644
index 82daa736e1bc..000000000000
--- a/tools/protologtool/tests/com/android/protologtool/CodeUtilsTest.kt
+++ /dev/null
@@ -1,206 +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.protologtool
-
-import com.github.javaparser.StaticJavaParser
-import com.github.javaparser.ast.expr.BinaryExpr
-import com.github.javaparser.ast.expr.StringLiteralExpr
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
-import org.junit.Test
-
-class CodeUtilsTest {
- @Test
- fun hash() {
- assertEquals(-1704685243, CodeUtils.hash("test", LogLevel.DEBUG))
- }
-
- @Test
- fun hash_changeLevel() {
- assertEquals(-1176900998, CodeUtils.hash("test", LogLevel.ERROR))
- }
-
- @Test
- fun hash_changeMessage() {
- assertEquals(-1305634931, CodeUtils.hash("test2", LogLevel.DEBUG))
- }
-
- @Test
- fun isWildcardStaticImported_true() {
- val code = """package org.example.test;
- import static org.example.Test.*;
- """
- assertTrue(CodeUtils.isWildcardStaticImported(
- StaticJavaParser.parse(code), "org.example.Test"))
- }
-
- @Test
- fun isWildcardStaticImported_notStatic() {
- val code = """package org.example.test;
- import org.example.Test.*;
- """
- assertFalse(CodeUtils.isWildcardStaticImported(
- StaticJavaParser.parse(code), "org.example.Test"))
- }
-
- @Test
- fun isWildcardStaticImported_differentClass() {
- val code = """package org.example.test;
- import static org.example.Test2.*;
- """
- assertFalse(CodeUtils.isWildcardStaticImported(
- StaticJavaParser.parse(code), "org.example.Test"))
- }
-
- @Test
- fun isWildcardStaticImported_notWildcard() {
- val code = """package org.example.test;
- import org.example.Test.test;
- """
- assertFalse(CodeUtils.isWildcardStaticImported(
- StaticJavaParser.parse(code), "org.example.Test"))
- }
-
- @Test
- fun isClassImportedOrSamePackage_imported() {
- val code = """package org.example.test;
- import org.example.Test;
- """
- assertTrue(CodeUtils.isClassImportedOrSamePackage(
- StaticJavaParser.parse(code), "org.example.Test"))
- }
-
- @Test
- fun isClassImportedOrSamePackage_samePackage() {
- val code = """package org.example.test;
- """
- assertTrue(CodeUtils.isClassImportedOrSamePackage(
- StaticJavaParser.parse(code), "org.example.test.Test"))
- }
-
- @Test
- fun isClassImportedOrSamePackage_false() {
- val code = """package org.example.test;
- import org.example.Test;
- """
- assertFalse(CodeUtils.isClassImportedOrSamePackage(
- StaticJavaParser.parse(code), "org.example.Test2"))
- }
-
- @Test
- fun staticallyImportedMethods_ab() {
- val code = """
- import static org.example.Test.a;
- import static org.example.Test.b;
- """
- val imported = CodeUtils.staticallyImportedMethods(StaticJavaParser.parse(code),
- "org.example.Test")
- assertTrue(imported.containsAll(listOf("a", "b")))
- assertEquals(2, imported.size)
- }
-
- @Test
- fun staticallyImportedMethods_differentClass() {
- val code = """
- import static org.example.Test.a;
- import static org.example.Test2.b;
- """
- val imported = CodeUtils.staticallyImportedMethods(StaticJavaParser.parse(code),
- "org.example.Test")
- assertTrue(imported.containsAll(listOf("a")))
- assertEquals(1, imported.size)
- }
-
- @Test
- fun staticallyImportedMethods_notStatic() {
- val code = """
- import static org.example.Test.a;
- import org.example.Test.b;
- """
- val imported = CodeUtils.staticallyImportedMethods(StaticJavaParser.parse(code),
- "org.example.Test")
- assertTrue(imported.containsAll(listOf("a")))
- assertEquals(1, imported.size)
- }
-
- @Test
- fun concatMultilineString_single() {
- val str = StringLiteralExpr("test")
- val out = CodeUtils.concatMultilineString(str)
- assertEquals("test", out)
- }
-
- @Test
- fun concatMultilineString_double() {
- val str = """
- "test" + "abc"
- """
- val code = StaticJavaParser.parseExpression<BinaryExpr>(str)
- val out = CodeUtils.concatMultilineString(code)
- assertEquals("testabc", out)
- }
-
- @Test
- fun concatMultilineString_multiple() {
- val str = """
- "test" + "abc" + "1234" + "test"
- """
- val code = StaticJavaParser.parseExpression<BinaryExpr>(str)
- val out = CodeUtils.concatMultilineString(code)
- assertEquals("testabc1234test", out)
- }
-
- @Test
- fun parseFormatString() {
- val str = "%b %d %o %x %f %e %g %s %%"
- val out = CodeUtils.parseFormatString(str)
- assertEquals(listOf(
- CodeUtils.LogDataTypes.BOOLEAN,
- CodeUtils.LogDataTypes.LONG,
- CodeUtils.LogDataTypes.LONG,
- CodeUtils.LogDataTypes.LONG,
- CodeUtils.LogDataTypes.DOUBLE,
- CodeUtils.LogDataTypes.DOUBLE,
- CodeUtils.LogDataTypes.DOUBLE,
- CodeUtils.LogDataTypes.STRING
- ), out)
- }
-
- @Test(expected = InvalidFormatStringException::class)
- fun parseFormatString_invalid() {
- val str = "%q"
- CodeUtils.parseFormatString(str)
- }
-
- @Test
- fun logDataTypesToBitMask() {
- val types = listOf(CodeUtils.LogDataTypes.STRING, CodeUtils.LogDataTypes.DOUBLE,
- CodeUtils.LogDataTypes.LONG, CodeUtils.LogDataTypes.BOOLEAN)
- val mask = CodeUtils.logDataTypesToBitMask(types)
- assertEquals(0b11011000, mask)
- }
-
- @Test(expected = InvalidFormatStringException::class)
- fun logDataTypesToBitMask_toManyParams() {
- val types = mutableListOf<CodeUtils.LogDataTypes>()
- for (i in 0..16) {
- types.add(CodeUtils.LogDataTypes.STRING)
- }
- CodeUtils.logDataTypesToBitMask(types)
- }
-}
diff --git a/tools/protologtool/tests/com/android/protologtool/CommandOptionsTest.kt b/tools/protologtool/tests/com/android/protologtool/CommandOptionsTest.kt
deleted file mode 100644
index c1cd473574c2..000000000000
--- a/tools/protologtool/tests/com/android/protologtool/CommandOptionsTest.kt
+++ /dev/null
@@ -1,250 +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.protologtool
-
-import org.junit.Assert.assertEquals
-import org.junit.Test
-
-class CommandOptionsTest {
- companion object {
- val TEST_JAVA_SRC = listOf(
- "frameworks/base/services/core/java/com/android/server/wm/" +
- "AccessibilityController.java",
- "frameworks/base/services/core/java/com/android/server/wm/ActivityDisplay.java",
- "frameworks/base/services/core/java/com/android/server/wm/" +
- "ActivityMetricsLaunchObserver.java"
- )
- private const val TEST_PROTOLOG_CLASS = "com.android.server.wm.ProtoLog"
- private const val TEST_PROTOLOGIMPL_CLASS = "com.android.server.wm.ProtoLogImpl"
- private const val TEST_PROTOLOGGROUP_CLASS = "com.android.server.wm.ProtoLogGroup"
- private const val TEST_PROTOLOGGROUP_JAR = "out/soong/.intermediates/frameworks/base/" +
- "services/core/services.core.wm.protologgroups/android_common/javac/" +
- "services.core.wm.protologgroups.jar"
- private const val TEST_SRC_JAR = "out/soong/.temp/sbox175955373/" +
- "services.core.wm.protolog.srcjar"
- private const val TEST_VIEWER_JSON = "out/soong/.temp/sbox175955373/" +
- "services.core.wm.protolog.json"
- private const val TEST_LOG = "./test_log.pb"
- }
-
- @Test(expected = InvalidCommandException::class)
- fun noCommand() {
- CommandOptions(arrayOf())
- }
-
- @Test(expected = InvalidCommandException::class)
- fun invalidCommand() {
- val testLine = "invalid"
- CommandOptions(testLine.split(' ').toTypedArray())
- }
-
- @Test
- fun transformClasses() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
- "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
- "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
- val cmd = CommandOptions(testLine.split(' ').toTypedArray())
- assertEquals(CommandOptions.TRANSFORM_CALLS_CMD, cmd.command)
- assertEquals(TEST_PROTOLOG_CLASS, cmd.protoLogClassNameArg)
- assertEquals(TEST_PROTOLOGIMPL_CLASS, cmd.protoLogImplClassNameArg)
- assertEquals(TEST_PROTOLOGGROUP_CLASS, cmd.protoLogGroupsClassNameArg)
- assertEquals(TEST_PROTOLOGGROUP_JAR, cmd.protoLogGroupsJarArg)
- assertEquals(TEST_SRC_JAR, cmd.outputSourceJarArg)
- assertEquals(TEST_JAVA_SRC, cmd.javaSourceArgs)
- }
-
- @Test(expected = InvalidCommandException::class)
- fun transformClasses_noProtoLogClass() {
- val testLine = "transform-protolog-calls " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
- "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
- "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
- }
-
- @Test(expected = InvalidCommandException::class)
- fun transformClasses_noProtoLogImplClass() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
- "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
- "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
- }
-
- @Test(expected = InvalidCommandException::class)
- fun transformClasses_noProtoLogGroupClass() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
- "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
- }
-
- @Test(expected = InvalidCommandException::class)
- fun transformClasses_noProtoLogGroupJar() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
- "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
- }
-
- @Test(expected = InvalidCommandException::class)
- fun transformClasses_noOutJar() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
- "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
- TEST_JAVA_SRC.joinToString(" ")
- CommandOptions(testLine.split(' ').toTypedArray())
- }
-
- @Test(expected = InvalidCommandException::class)
- fun transformClasses_noJavaInput() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
- "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
- "--output-srcjar $TEST_SRC_JAR"
- CommandOptions(testLine.split(' ').toTypedArray())
- }
-
- @Test(expected = InvalidCommandException::class)
- fun transformClasses_invalidProtoLogClass() {
- val testLine = "transform-protolog-calls --protolog-class invalid " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
- "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
- "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
- }
-
- @Test(expected = InvalidCommandException::class)
- fun transformClasses_invalidProtoLogImplClass() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class invalid " +
- "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
- "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
- "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
- }
-
- @Test(expected = InvalidCommandException::class)
- fun transformClasses_invalidProtoLogGroupClass() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--loggroups-class invalid " +
- "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
- "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
- }
-
- @Test(expected = InvalidCommandException::class)
- fun transformClasses_invalidProtoLogGroupJar() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
- "--loggroups-jar invalid.txt " +
- "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
- }
-
- @Test(expected = InvalidCommandException::class)
- fun transformClasses_invalidOutJar() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
- "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
- "--output-srcjar invalid.db ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
- }
-
- @Test(expected = InvalidCommandException::class)
- fun transformClasses_invalidJavaInput() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
- "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
- "--output-srcjar $TEST_SRC_JAR invalid.py"
- CommandOptions(testLine.split(' ').toTypedArray())
- }
-
- @Test(expected = InvalidCommandException::class)
- fun transformClasses_unknownParam() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--unknown test --protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
- "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
- "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
- }
-
- @Test(expected = InvalidCommandException::class)
- fun transformClasses_noValue() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class " +
- "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
- "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
- "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
- }
-
- @Test
- fun generateConfig() {
- val testLine = "generate-viewer-config --protolog-class $TEST_PROTOLOG_CLASS " +
- "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
- "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
- "--viewer-conf $TEST_VIEWER_JSON ${TEST_JAVA_SRC.joinToString(" ")}"
- val cmd = CommandOptions(testLine.split(' ').toTypedArray())
- assertEquals(CommandOptions.GENERATE_CONFIG_CMD, cmd.command)
- assertEquals(TEST_PROTOLOG_CLASS, cmd.protoLogClassNameArg)
- assertEquals(TEST_PROTOLOGGROUP_CLASS, cmd.protoLogGroupsClassNameArg)
- assertEquals(TEST_PROTOLOGGROUP_JAR, cmd.protoLogGroupsJarArg)
- assertEquals(TEST_VIEWER_JSON, cmd.viewerConfigJsonArg)
- assertEquals(TEST_JAVA_SRC, cmd.javaSourceArgs)
- }
-
- @Test(expected = InvalidCommandException::class)
- fun generateConfig_noViewerConfig() {
- val testLine = "generate-viewer-config --protolog-class $TEST_PROTOLOG_CLASS " +
- "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
- "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
- TEST_JAVA_SRC.joinToString(" ")
- CommandOptions(testLine.split(' ').toTypedArray())
- }
-
- @Test(expected = InvalidCommandException::class)
- fun generateConfig_invalidViewerConfig() {
- val testLine = "generate-viewer-config --protolog-class $TEST_PROTOLOG_CLASS " +
- "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
- "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
- "--viewer-conf invalid.yaml ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
- }
-
- @Test
- fun readLog() {
- val testLine = "read-log --viewer-conf $TEST_VIEWER_JSON $TEST_LOG"
- val cmd = CommandOptions(testLine.split(' ').toTypedArray())
- assertEquals(CommandOptions.READ_LOG_CMD, cmd.command)
- assertEquals(TEST_VIEWER_JSON, cmd.viewerConfigJsonArg)
- assertEquals(TEST_LOG, cmd.logProtofileArg)
- }
-}
diff --git a/tools/protologtool/tests/com/android/protologtool/LogParserTest.kt b/tools/protologtool/tests/com/android/protologtool/LogParserTest.kt
deleted file mode 100644
index 7106ea6fa168..000000000000
--- a/tools/protologtool/tests/com/android/protologtool/LogParserTest.kt
+++ /dev/null
@@ -1,187 +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.protologtool
-
-import com.android.json.stream.JsonReader
-import com.android.server.wm.ProtoLogMessage
-import com.android.server.wm.WindowManagerLogFileProto
-import org.junit.Assert.assertEquals
-import org.junit.Before
-import org.junit.Test
-import org.mockito.Mockito
-import org.mockito.Mockito.mock
-import java.io.ByteArrayOutputStream
-import java.io.InputStream
-import java.io.OutputStream
-import java.io.PrintStream
-import java.text.SimpleDateFormat
-import java.util.Date
-import java.util.Locale
-
-class LogParserTest {
- private val configParser: ViewerConfigParser = mock(ViewerConfigParser::class.java)
- private val parser = LogParser(configParser)
- private var config: MutableMap<Int, ViewerConfigParser.ConfigEntry> = mutableMapOf()
- private var outStream: OutputStream = ByteArrayOutputStream()
- private var printStream: PrintStream = PrintStream(outStream)
- private val dateFormat = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
-
- @Before
- fun init() {
- Mockito.`when`(configParser.parseConfig(any(JsonReader::class.java))).thenReturn(config)
- }
-
- private fun <T> any(type: Class<T>): T = Mockito.any<T>(type)
-
- private fun getConfigDummyStream(): InputStream {
- return "".byteInputStream()
- }
-
- private fun buildProtoInput(logBuilder: WindowManagerLogFileProto.Builder): InputStream {
- logBuilder.setVersion(Constants.VERSION)
- logBuilder.magicNumber =
- WindowManagerLogFileProto.MagicNumber.MAGIC_NUMBER_H.number.toLong() shl 32 or
- WindowManagerLogFileProto.MagicNumber.MAGIC_NUMBER_L.number.toLong()
- return logBuilder.build().toByteArray().inputStream()
- }
-
- private fun testDate(timeMS: Long): String {
- return dateFormat.format(Date(timeMS))
- }
-
- @Test
- fun parse() {
- config[70933285] = ViewerConfigParser.ConfigEntry("Test completed successfully: %b",
- "ERROR", "WindowManager")
-
- val logBuilder = WindowManagerLogFileProto.newBuilder()
- val logMessageBuilder = ProtoLogMessage.newBuilder()
- logMessageBuilder
- .setMessageHash(70933285)
- .setElapsedRealtimeNanos(0)
- .addBooleanParams(true)
- logBuilder.addLog(logMessageBuilder.build())
-
- parser.parse(buildProtoInput(logBuilder), getConfigDummyStream(), printStream)
-
- assertEquals("${testDate(0)} ERROR WindowManager: Test completed successfully: true\n",
- outStream.toString())
- }
-
- @Test
- fun parse_formatting() {
- config[123] = ViewerConfigParser.ConfigEntry("Test completed successfully: %b %d %% %o" +
- " %x %e %g %s %f", "ERROR", "WindowManager")
-
- val logBuilder = WindowManagerLogFileProto.newBuilder()
- val logMessageBuilder = ProtoLogMessage.newBuilder()
- logMessageBuilder
- .setMessageHash(123)
- .setElapsedRealtimeNanos(0)
- .addBooleanParams(true)
- .addAllSint64Params(listOf(1000, 20000, 300000))
- .addAllDoubleParams(listOf(0.1, 0.00001, 1000.1))
- .addStrParams("test")
- logBuilder.addLog(logMessageBuilder.build())
-
- parser.parse(buildProtoInput(logBuilder), getConfigDummyStream(), printStream)
-
- assertEquals("${testDate(0)} ERROR WindowManager: Test completed successfully: " +
- "true 1000 % 47040 493e0 1.000000e-01 1.00000e-05 test 1000.100000\n",
- outStream.toString())
- }
-
- @Test
- fun parse_invalidParamsTooMany() {
- config[123] = ViewerConfigParser.ConfigEntry("Test completed successfully: %b %d %% %o",
- "ERROR", "WindowManager")
-
- val logBuilder = WindowManagerLogFileProto.newBuilder()
- val logMessageBuilder = ProtoLogMessage.newBuilder()
- logMessageBuilder
- .setMessageHash(123)
- .setElapsedRealtimeNanos(0)
- .addBooleanParams(true)
- .addAllSint64Params(listOf(1000, 20000, 300000))
- .addAllDoubleParams(listOf(0.1, 0.00001, 1000.1))
- .addStrParams("test")
- logBuilder.addLog(logMessageBuilder.build())
-
- parser.parse(buildProtoInput(logBuilder), getConfigDummyStream(), printStream)
-
- assertEquals("${testDate(0)} INVALID: 123 - [test] [1000, 20000, 300000] " +
- "[0.1, 1.0E-5, 1000.1] [true]\n", outStream.toString())
- }
-
- @Test
- fun parse_invalidParamsNotEnough() {
- config[123] = ViewerConfigParser.ConfigEntry("Test completed successfully: %b %d %% %o" +
- " %x %e %g %s %f", "ERROR", "WindowManager")
-
- val logBuilder = WindowManagerLogFileProto.newBuilder()
- val logMessageBuilder = ProtoLogMessage.newBuilder()
- logMessageBuilder
- .setMessageHash(123)
- .setElapsedRealtimeNanos(0)
- .addBooleanParams(true)
- .addStrParams("test")
- logBuilder.addLog(logMessageBuilder.build())
-
- parser.parse(buildProtoInput(logBuilder), getConfigDummyStream(), printStream)
-
- assertEquals("${testDate(0)} INVALID: 123 - [test] [] [] [true]\n",
- outStream.toString())
- }
-
- @Test(expected = InvalidInputException::class)
- fun parse_invalidMagicNumber() {
- val logBuilder = WindowManagerLogFileProto.newBuilder()
- logBuilder.setVersion(Constants.VERSION)
- logBuilder.magicNumber = 0
- val stream = logBuilder.build().toByteArray().inputStream()
-
- parser.parse(stream, getConfigDummyStream(), printStream)
- }
-
- @Test(expected = InvalidInputException::class)
- fun parse_invalidVersion() {
- val logBuilder = WindowManagerLogFileProto.newBuilder()
- logBuilder.setVersion("invalid")
- logBuilder.magicNumber =
- WindowManagerLogFileProto.MagicNumber.MAGIC_NUMBER_H.number.toLong() shl 32 or
- WindowManagerLogFileProto.MagicNumber.MAGIC_NUMBER_L.number.toLong()
- val stream = logBuilder.build().toByteArray().inputStream()
-
- parser.parse(stream, getConfigDummyStream(), printStream)
- }
-
- @Test
- fun parse_noConfig() {
- val logBuilder = WindowManagerLogFileProto.newBuilder()
- val logMessageBuilder = ProtoLogMessage.newBuilder()
- logMessageBuilder
- .setMessageHash(70933285)
- .setElapsedRealtimeNanos(0)
- .addBooleanParams(true)
- logBuilder.addLog(logMessageBuilder.build())
-
- parser.parse(buildProtoInput(logBuilder), getConfigDummyStream(), printStream)
-
- assertEquals("${testDate(0)} UNKNOWN: 70933285 - [] [] [] [true]\n",
- outStream.toString())
- }
-}
diff --git a/tools/protologtool/tests/com/android/protologtool/ProtoLogCallProcessorTest.kt b/tools/protologtool/tests/com/android/protologtool/ProtoLogCallProcessorTest.kt
deleted file mode 100644
index dcb1f7fe3366..000000000000
--- a/tools/protologtool/tests/com/android/protologtool/ProtoLogCallProcessorTest.kt
+++ /dev/null
@@ -1,226 +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.protologtool
-
-import com.github.javaparser.StaticJavaParser
-import com.github.javaparser.ast.expr.MethodCallExpr
-import org.junit.Assert.assertEquals
-import org.junit.Test
-
-class ProtoLogCallProcessorTest {
- private data class LogCall(
- val call: MethodCallExpr,
- val messageString: String,
- val level: LogLevel,
- val group: LogGroup
- )
-
- private val groupMap: MutableMap<String, LogGroup> = mutableMapOf()
- private val calls: MutableList<LogCall> = mutableListOf()
- private val visitor = ProtoLogCallProcessor("org.example.ProtoLog", "org.example.ProtoLogGroup",
- groupMap)
- private val processor = object : ProtoLogCallVisitor {
- override fun processCall(
- call: MethodCallExpr,
- messageString: String,
- level: LogLevel,
- group: LogGroup
- ) {
- calls.add(LogCall(call, messageString, level, group))
- }
- }
-
- private fun checkCalls() {
- assertEquals(1, calls.size)
- val c = calls[0]
- assertEquals("test %b", c.messageString)
- assertEquals(groupMap["TEST"], c.group)
- assertEquals(LogLevel.DEBUG, c.level)
- }
-
- @Test
- fun process_samePackage() {
- val code = """
- package org.example;
-
- class Test {
- void test() {
- ProtoLog.d(ProtoLogGroup.TEST, "test %b", true);
- ProtoLog.e(ProtoLogGroup.ERROR, "error %d", 1);
- }
- }
- """
- groupMap["TEST"] = LogGroup("TEST", true, false, "WindowManager")
- groupMap["ERROR"] = LogGroup("ERROR", true, true, "WindowManagerERROR")
- visitor.process(StaticJavaParser.parse(code), processor)
- assertEquals(2, calls.size)
- var c = calls[0]
- assertEquals("test %b", c.messageString)
- assertEquals(groupMap["TEST"], c.group)
- assertEquals(LogLevel.DEBUG, c.level)
- c = calls[1]
- assertEquals("error %d", c.messageString)
- assertEquals(groupMap["ERROR"], c.group)
- assertEquals(LogLevel.ERROR, c.level)
- }
-
- @Test
- fun process_imported() {
- val code = """
- package org.example2;
-
- import org.example.ProtoLog;
- import org.example.ProtoLogGroup;
-
- class Test {
- void test() {
- ProtoLog.d(ProtoLogGroup.TEST, "test %b", true);
- }
- }
- """
- groupMap["TEST"] = LogGroup("TEST", true, true, "WindowManager")
- visitor.process(StaticJavaParser.parse(code), processor)
- checkCalls()
- }
-
- @Test
- fun process_importedStatic() {
- val code = """
- package org.example2;
-
- import static org.example.ProtoLog.d;
- import static org.example.ProtoLogGroup.TEST;
-
- class Test {
- void test() {
- d(TEST, "test %b", true);
- }
- }
- """
- groupMap["TEST"] = LogGroup("TEST", true, true, "WindowManager")
- visitor.process(StaticJavaParser.parse(code), processor)
- checkCalls()
- }
-
- @Test(expected = InvalidProtoLogCallException::class)
- fun process_groupNotImported() {
- val code = """
- package org.example2;
-
- import org.example.ProtoLog;
-
- class Test {
- void test() {
- ProtoLog.d(ProtoLogGroup.TEST, "test %b", true);
- }
- }
- """
- groupMap["TEST"] = LogGroup("TEST", true, true, "WindowManager")
- visitor.process(StaticJavaParser.parse(code), processor)
- }
-
- @Test
- fun process_protoLogNotImported() {
- val code = """
- package org.example2;
-
- import org.example.ProtoLogGroup;
-
- class Test {
- void test() {
- ProtoLog.d(ProtoLogGroup.TEST, "test %b", true);
- }
- }
- """
- groupMap["TEST"] = LogGroup("TEST", true, true, "WindowManager")
- visitor.process(StaticJavaParser.parse(code), processor)
- assertEquals(0, calls.size)
- }
-
- @Test(expected = InvalidProtoLogCallException::class)
- fun process_unknownGroup() {
- val code = """
- package org.example;
-
- class Test {
- void test() {
- ProtoLog.d(ProtoLogGroup.TEST, "test %b", true);
- }
- }
- """
- visitor.process(StaticJavaParser.parse(code), processor)
- }
-
- @Test(expected = InvalidProtoLogCallException::class)
- fun process_staticGroup() {
- val code = """
- package org.example;
-
- class Test {
- void test() {
- ProtoLog.d(TEST, "test %b", true);
- }
- }
- """
- visitor.process(StaticJavaParser.parse(code), processor)
- }
-
- @Test(expected = InvalidProtoLogCallException::class)
- fun process_badGroup() {
- val code = """
- package org.example;
-
- class Test {
- void test() {
- ProtoLog.d(0, "test %b", true);
- }
- }
- """
- visitor.process(StaticJavaParser.parse(code), processor)
- }
-
- @Test(expected = InvalidProtoLogCallException::class)
- fun process_invalidSignature() {
- val code = """
- package org.example;
-
- class Test {
- void test() {
- ProtoLog.d("test");
- }
- }
- """
- visitor.process(StaticJavaParser.parse(code), processor)
- }
-
- @Test
- fun process_disabled() {
- // Disabled groups are also processed.
- val code = """
- package org.example;
-
- class Test {
- void test() {
- ProtoLog.d(ProtoLogGroup.TEST, "test %b", true);
- }
- }
- """
- groupMap["TEST"] = LogGroup("TEST", false, true, "WindowManager")
- visitor.process(StaticJavaParser.parse(code), processor)
- checkCalls()
- }
-}
diff --git a/tools/protologtool/tests/com/android/protologtool/SourceTransformerTest.kt b/tools/protologtool/tests/com/android/protologtool/SourceTransformerTest.kt
deleted file mode 100644
index 2cd85627b94b..000000000000
--- a/tools/protologtool/tests/com/android/protologtool/SourceTransformerTest.kt
+++ /dev/null
@@ -1,445 +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.protologtool
-
-import com.github.javaparser.StaticJavaParser
-import com.github.javaparser.ast.CompilationUnit
-import com.github.javaparser.ast.expr.MethodCallExpr
-import com.github.javaparser.ast.stmt.IfStmt
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertFalse
-import org.junit.Test
-import org.mockito.Mockito
-
-class SourceTransformerTest {
- companion object {
- private const val PROTO_LOG_IMPL_PATH = "org.example.ProtoLogImpl"
-
- /* ktlint-disable max-line-length */
- private val TEST_CODE = """
- package org.example;
-
- class Test {
- void test() {
- ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1);
- }
- }
- """.trimIndent()
-
- private val TEST_CODE_MULTILINE = """
- package org.example;
-
- class Test {
- void test() {
- ProtoLog.w(TEST_GROUP, "test %d %f " +
- "abc %s\n test", 100,
- 0.1, "test");
- }
- }
- """.trimIndent()
-
- private val TEST_CODE_MULTICALLS = """
- package org.example;
-
- class Test {
- void test() {
- ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1); /* ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1); */ ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1);
- ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1);
- }
- }
- """.trimIndent()
-
- private val TEST_CODE_NO_PARAMS = """
- package org.example;
-
- class Test {
- void test() {
- ProtoLog.w(TEST_GROUP, "test");
- }
- }
- """.trimIndent()
-
- private val TRANSFORMED_CODE_TEXT_ENABLED = """
- package org.example;
-
- class Test {
- void test() {
- if (TEST_GROUP.isLogToAny()) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 835524026, 9, "test %d %f", protoLogParam0, protoLogParam1); }
- }
- }
- """.trimIndent()
-
- private val TRANSFORMED_CODE_MULTILINE_TEXT_ENABLED = """
- package org.example;
-
- class Test {
- void test() {
- if (TEST_GROUP.isLogToAny()) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; String protoLogParam2 = String.valueOf("test"); org.example.ProtoLogImpl.w(TEST_GROUP, -986393606, 9, "test %d %f " + "abc %s\n test", protoLogParam0, protoLogParam1, protoLogParam2);
-
- }
- }
- }
- """.trimIndent()
-
- private val TRANSFORMED_CODE_MULTICALL_TEXT_ENABLED = """
- package org.example;
-
- class Test {
- void test() {
- if (TEST_GROUP.isLogToAny()) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 835524026, 9, "test %d %f", protoLogParam0, protoLogParam1); } /* ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1); */ if (TEST_GROUP.isLogToAny()) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 835524026, 9, "test %d %f", protoLogParam0, protoLogParam1); }
- if (TEST_GROUP.isLogToAny()) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 835524026, 9, "test %d %f", protoLogParam0, protoLogParam1); }
- }
- }
- """.trimIndent()
-
- private val TRANSFORMED_CODE_NO_PARAMS = """
- package org.example;
-
- class Test {
- void test() {
- if (TEST_GROUP.isLogToAny()) { org.example.ProtoLogImpl.w(TEST_GROUP, 1282022424, 0, "test", (Object[]) null); }
- }
- }
- """.trimIndent()
-
- private val TRANSFORMED_CODE_TEXT_DISABLED = """
- package org.example;
-
- class Test {
- void test() {
- if (TEST_GROUP.isLogToAny()) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 835524026, 9, null, protoLogParam0, protoLogParam1); }
- }
- }
- """.trimIndent()
-
- private val TRANSFORMED_CODE_MULTILINE_TEXT_DISABLED = """
- package org.example;
-
- class Test {
- void test() {
- if (TEST_GROUP.isLogToAny()) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; String protoLogParam2 = String.valueOf("test"); org.example.ProtoLogImpl.w(TEST_GROUP, -986393606, 9, null, protoLogParam0, protoLogParam1, protoLogParam2);
-
- }
- }
- }
- """.trimIndent()
-
- private val TRANSFORMED_CODE_DISABLED = """
- package org.example;
-
- class Test {
- void test() {
- if (false) { /* TEST_GROUP is disabled */ ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1); }
- }
- }
- """.trimIndent()
-
- private val TRANSFORMED_CODE_MULTILINE_DISABLED = """
- package org.example;
-
- class Test {
- void test() {
- if (false) { /* TEST_GROUP is disabled */ ProtoLog.w(TEST_GROUP, "test %d %f " + "abc %s\n test", 100, 0.1, "test");
-
- }
- }
- }
- """.trimIndent()
- /* ktlint-enable max-line-length */
- }
-
- private val processor: ProtoLogCallProcessor = Mockito.mock(ProtoLogCallProcessor::class.java)
- private val sourceJarWriter = SourceTransformer("org.example.ProtoLogImpl", processor)
-
- private fun <T> any(type: Class<T>): T = Mockito.any<T>(type)
-
- @Test
- fun processClass_textEnabled() {
- var code = StaticJavaParser.parse(TEST_CODE)
-
- Mockito.`when`(processor.process(any(CompilationUnit::class.java),
- any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
- val visitor = invocation.arguments[1] as ProtoLogCallVisitor
-
- visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test %d %f",
- LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"))
-
- invocation.arguments[0] as CompilationUnit
- }
-
- val out = sourceJarWriter.processClass(TEST_CODE, code)
- code = StaticJavaParser.parse(out)
-
- val ifStmts = code.findAll(IfStmt::class.java)
- assertEquals(1, ifStmts.size)
- val ifStmt = ifStmts[0]
- assertEquals("TEST_GROUP.${Constants.IS_LOG_TO_ANY_METHOD}()",
- ifStmt.condition.toString())
- assertFalse(ifStmt.elseStmt.isPresent)
- assertEquals(3, ifStmt.thenStmt.childNodes.size)
- val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[0] as MethodCallExpr
- assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString())
- assertEquals("w", methodCall.name.asString())
- assertEquals(6, methodCall.arguments.size)
- assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
- assertEquals("835524026", methodCall.arguments[1].toString())
- assertEquals(0b1001.toString(), methodCall.arguments[2].toString())
- assertEquals("\"test %d %f\"", methodCall.arguments[3].toString())
- assertEquals("protoLogParam0", methodCall.arguments[4].toString())
- assertEquals("protoLogParam1", methodCall.arguments[5].toString())
- assertEquals(TRANSFORMED_CODE_TEXT_ENABLED, out)
- }
-
- @Test
- fun processClass_textEnabledMulticalls() {
- var code = StaticJavaParser.parse(TEST_CODE_MULTICALLS)
-
- Mockito.`when`(processor.process(any(CompilationUnit::class.java),
- any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
- val visitor = invocation.arguments[1] as ProtoLogCallVisitor
-
- val calls = code.findAll(MethodCallExpr::class.java)
- visitor.processCall(calls[0], "test %d %f",
- LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"))
- visitor.processCall(calls[1], "test %d %f",
- LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"))
- visitor.processCall(calls[2], "test %d %f",
- LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"))
-
- invocation.arguments[0] as CompilationUnit
- }
-
- val out = sourceJarWriter.processClass(TEST_CODE_MULTICALLS, code)
- code = StaticJavaParser.parse(out)
-
- val ifStmts = code.findAll(IfStmt::class.java)
- assertEquals(3, ifStmts.size)
- val ifStmt = ifStmts[1]
- assertEquals("TEST_GROUP.${Constants.IS_LOG_TO_ANY_METHOD}()",
- ifStmt.condition.toString())
- assertFalse(ifStmt.elseStmt.isPresent)
- assertEquals(3, ifStmt.thenStmt.childNodes.size)
- val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[0] as MethodCallExpr
- assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString())
- assertEquals("w", methodCall.name.asString())
- assertEquals(6, methodCall.arguments.size)
- assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
- assertEquals("835524026", methodCall.arguments[1].toString())
- assertEquals(0b1001.toString(), methodCall.arguments[2].toString())
- assertEquals("\"test %d %f\"", methodCall.arguments[3].toString())
- assertEquals("protoLogParam0", methodCall.arguments[4].toString())
- assertEquals("protoLogParam1", methodCall.arguments[5].toString())
- assertEquals(TRANSFORMED_CODE_MULTICALL_TEXT_ENABLED, out)
- }
-
- @Test
- fun processClass_textEnabledMultiline() {
- var code = StaticJavaParser.parse(TEST_CODE_MULTILINE)
-
- Mockito.`when`(processor.process(any(CompilationUnit::class.java),
- any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
- val visitor = invocation.arguments[1] as ProtoLogCallVisitor
-
- visitor.processCall(code.findAll(MethodCallExpr::class.java)[0],
- "test %d %f abc %s\n test", LogLevel.WARN, LogGroup("TEST_GROUP",
- true, true, "WM_TEST"))
-
- invocation.arguments[0] as CompilationUnit
- }
-
- val out = sourceJarWriter.processClass(TEST_CODE_MULTILINE, code)
- code = StaticJavaParser.parse(out)
-
- val ifStmts = code.findAll(IfStmt::class.java)
- assertEquals(1, ifStmts.size)
- val ifStmt = ifStmts[0]
- assertEquals("TEST_GROUP.${Constants.IS_LOG_TO_ANY_METHOD}()",
- ifStmt.condition.toString())
- assertFalse(ifStmt.elseStmt.isPresent)
- assertEquals(4, ifStmt.thenStmt.childNodes.size)
- val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[1] as MethodCallExpr
- assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString())
- assertEquals("w", methodCall.name.asString())
- assertEquals(7, methodCall.arguments.size)
- assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
- assertEquals("-986393606", methodCall.arguments[1].toString())
- assertEquals(0b001001.toString(), methodCall.arguments[2].toString())
- assertEquals("protoLogParam0", methodCall.arguments[4].toString())
- assertEquals("protoLogParam1", methodCall.arguments[5].toString())
- assertEquals("protoLogParam2", methodCall.arguments[6].toString())
- assertEquals(TRANSFORMED_CODE_MULTILINE_TEXT_ENABLED, out)
- }
-
- @Test
- fun processClass_noParams() {
- var code = StaticJavaParser.parse(TEST_CODE_NO_PARAMS)
-
- Mockito.`when`(processor.process(any(CompilationUnit::class.java),
- any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
- val visitor = invocation.arguments[1] as ProtoLogCallVisitor
-
- visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test",
- LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"))
-
- invocation.arguments[0] as CompilationUnit
- }
-
- val out = sourceJarWriter.processClass(TEST_CODE_NO_PARAMS, code)
- code = StaticJavaParser.parse(out)
-
- val ifStmts = code.findAll(IfStmt::class.java)
- assertEquals(1, ifStmts.size)
- val ifStmt = ifStmts[0]
- assertEquals("TEST_GROUP.${Constants.IS_LOG_TO_ANY_METHOD}()",
- ifStmt.condition.toString())
- assertFalse(ifStmt.elseStmt.isPresent)
- assertEquals(1, ifStmt.thenStmt.childNodes.size)
- val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[0] as MethodCallExpr
- assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString())
- assertEquals("w", methodCall.name.asString())
- assertEquals(5, methodCall.arguments.size)
- assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
- assertEquals("1282022424", methodCall.arguments[1].toString())
- assertEquals(0.toString(), methodCall.arguments[2].toString())
- assertEquals(TRANSFORMED_CODE_NO_PARAMS, out)
- }
-
- @Test
- fun processClass_textDisabled() {
- var code = StaticJavaParser.parse(TEST_CODE)
-
- Mockito.`when`(processor.process(any(CompilationUnit::class.java),
- any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
- val visitor = invocation.arguments[1] as ProtoLogCallVisitor
-
- visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test %d %f",
- LogLevel.WARN, LogGroup("TEST_GROUP", true, false, "WM_TEST"))
-
- invocation.arguments[0] as CompilationUnit
- }
-
- val out = sourceJarWriter.processClass(TEST_CODE, code)
- code = StaticJavaParser.parse(out)
-
- val ifStmts = code.findAll(IfStmt::class.java)
- assertEquals(1, ifStmts.size)
- val ifStmt = ifStmts[0]
- assertEquals("TEST_GROUP.${Constants.IS_LOG_TO_ANY_METHOD}()",
- ifStmt.condition.toString())
- assertFalse(ifStmt.elseStmt.isPresent)
- assertEquals(3, ifStmt.thenStmt.childNodes.size)
- val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[0] as MethodCallExpr
- assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString())
- assertEquals("w", methodCall.name.asString())
- assertEquals(6, methodCall.arguments.size)
- assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
- assertEquals("835524026", methodCall.arguments[1].toString())
- assertEquals(0b1001.toString(), methodCall.arguments[2].toString())
- assertEquals("null", methodCall.arguments[3].toString())
- assertEquals("protoLogParam0", methodCall.arguments[4].toString())
- assertEquals("protoLogParam1", methodCall.arguments[5].toString())
- assertEquals(TRANSFORMED_CODE_TEXT_DISABLED, out)
- }
-
- @Test
- fun processClass_textDisabledMultiline() {
- var code = StaticJavaParser.parse(TEST_CODE_MULTILINE)
-
- Mockito.`when`(processor.process(any(CompilationUnit::class.java),
- any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
- val visitor = invocation.arguments[1] as ProtoLogCallVisitor
-
- visitor.processCall(code.findAll(MethodCallExpr::class.java)[0],
- "test %d %f abc %s\n test", LogLevel.WARN, LogGroup("TEST_GROUP",
- true, false, "WM_TEST"))
-
- invocation.arguments[0] as CompilationUnit
- }
-
- val out = sourceJarWriter.processClass(TEST_CODE_MULTILINE, code)
- code = StaticJavaParser.parse(out)
-
- val ifStmts = code.findAll(IfStmt::class.java)
- assertEquals(1, ifStmts.size)
- val ifStmt = ifStmts[0]
- assertEquals("TEST_GROUP.${Constants.IS_LOG_TO_ANY_METHOD}()",
- ifStmt.condition.toString())
- assertFalse(ifStmt.elseStmt.isPresent)
- assertEquals(4, ifStmt.thenStmt.childNodes.size)
- val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[1] as MethodCallExpr
- assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString())
- assertEquals("w", methodCall.name.asString())
- assertEquals(7, methodCall.arguments.size)
- assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
- assertEquals("-986393606", methodCall.arguments[1].toString())
- assertEquals(0b001001.toString(), methodCall.arguments[2].toString())
- assertEquals("null", methodCall.arguments[3].toString())
- assertEquals("protoLogParam0", methodCall.arguments[4].toString())
- assertEquals("protoLogParam1", methodCall.arguments[5].toString())
- assertEquals("protoLogParam2", methodCall.arguments[6].toString())
- assertEquals(TRANSFORMED_CODE_MULTILINE_TEXT_DISABLED, out)
- }
-
- @Test
- fun processClass_disabled() {
- var code = StaticJavaParser.parse(TEST_CODE)
-
- Mockito.`when`(processor.process(any(CompilationUnit::class.java),
- any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
- val visitor = invocation.arguments[1] as ProtoLogCallVisitor
-
- visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test %d %f",
- LogLevel.WARN, LogGroup("TEST_GROUP", false, true, "WM_TEST"))
-
- invocation.arguments[0] as CompilationUnit
- }
-
- val out = sourceJarWriter.processClass(TEST_CODE, code)
- code = StaticJavaParser.parse(out)
-
- val ifStmts = code.findAll(IfStmt::class.java)
- assertEquals(1, ifStmts.size)
- val ifStmt = ifStmts[0]
- assertEquals("false", ifStmt.condition.toString())
- assertEquals(TRANSFORMED_CODE_DISABLED, out)
- }
-
- @Test
- fun processClass_disabledMultiline() {
- var code = StaticJavaParser.parse(TEST_CODE_MULTILINE)
-
- Mockito.`when`(processor.process(any(CompilationUnit::class.java),
- any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
- val visitor = invocation.arguments[1] as ProtoLogCallVisitor
-
- visitor.processCall(code.findAll(MethodCallExpr::class.java)[0],
- "test %d %f abc %s\n test", LogLevel.WARN, LogGroup("TEST_GROUP",
- false, true, "WM_TEST"))
-
- invocation.arguments[0] as CompilationUnit
- }
-
- val out = sourceJarWriter.processClass(TEST_CODE_MULTILINE, code)
- code = StaticJavaParser.parse(out)
-
- val ifStmts = code.findAll(IfStmt::class.java)
- assertEquals(1, ifStmts.size)
- val ifStmt = ifStmts[0]
- assertEquals("false", ifStmt.condition.toString())
- assertEquals(TRANSFORMED_CODE_MULTILINE_DISABLED, out)
- }
-}
diff --git a/tools/protologtool/tests/com/android/protologtool/ViewerConfigBuilderTest.kt b/tools/protologtool/tests/com/android/protologtool/ViewerConfigBuilderTest.kt
deleted file mode 100644
index 53d2e8b0f4fa..000000000000
--- a/tools/protologtool/tests/com/android/protologtool/ViewerConfigBuilderTest.kt
+++ /dev/null
@@ -1,120 +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.protologtool
-
-import com.android.json.stream.JsonReader
-import com.github.javaparser.ast.CompilationUnit
-import com.github.javaparser.ast.expr.MethodCallExpr
-import org.junit.Assert.assertEquals
-import org.junit.Test
-import org.mockito.Mockito
-import java.io.StringReader
-
-class ViewerConfigBuilderTest {
- companion object {
- private val TAG1 = "WM_TEST"
- private val TAG2 = "WM_DEBUG"
- private val TEST1 = ViewerConfigParser.ConfigEntry("test1", LogLevel.INFO.name, TAG1)
- private val TEST2 = ViewerConfigParser.ConfigEntry("test2", LogLevel.DEBUG.name, TAG2)
- private val TEST3 = ViewerConfigParser.ConfigEntry("test3", LogLevel.ERROR.name, TAG2)
- }
-
- private val processor: ProtoLogCallProcessor = Mockito.mock(ProtoLogCallProcessor::class.java)
- private val configBuilder = ViewerConfigBuilder(processor)
- private val dummyCompilationUnit = CompilationUnit()
-
- private fun <T> any(type: Class<T>): T = Mockito.any<T>(type)
-
- private fun parseConfig(json: String): Map<Int, ViewerConfigParser.ConfigEntry> {
- return ViewerConfigParser().parseConfig(JsonReader(StringReader(json)))
- }
-
- @Test
- fun processClass() {
- Mockito.`when`(processor.process(any(CompilationUnit::class.java),
- any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
- val visitor = invocation.arguments[1] as ProtoLogCallVisitor
-
- visitor.processCall(MethodCallExpr(), TEST1.messageString, LogLevel.INFO,
- LogGroup("TEST_GROUP", true, true, TAG1))
- visitor.processCall(MethodCallExpr(), TEST2.messageString, LogLevel.DEBUG,
- LogGroup("DEBUG_GROUP", true, true, TAG2))
- visitor.processCall(MethodCallExpr(), TEST3.messageString, LogLevel.ERROR,
- LogGroup("DEBUG_GROUP", true, true, TAG2))
-
- invocation.arguments[0] as CompilationUnit
- }
-
- configBuilder.processClass(dummyCompilationUnit)
-
- val parsedConfig = parseConfig(configBuilder.build())
- assertEquals(3, parsedConfig.size)
- assertEquals(TEST1, parsedConfig[CodeUtils.hash(TEST1.messageString,
- LogLevel.INFO)])
- assertEquals(TEST2, parsedConfig[CodeUtils.hash(TEST2.messageString,
- LogLevel.DEBUG)])
- assertEquals(TEST3, parsedConfig[CodeUtils.hash(TEST3.messageString,
- LogLevel.ERROR)])
- }
-
- @Test
- fun processClass_nonUnique() {
- Mockito.`when`(processor.process(any(CompilationUnit::class.java),
- any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
- val visitor = invocation.arguments[1] as ProtoLogCallVisitor
-
- visitor.processCall(MethodCallExpr(), TEST1.messageString, LogLevel.INFO,
- LogGroup("TEST_GROUP", true, true, TAG1))
- visitor.processCall(MethodCallExpr(), TEST1.messageString, LogLevel.INFO,
- LogGroup("TEST_GROUP", true, true, TAG1))
- visitor.processCall(MethodCallExpr(), TEST1.messageString, LogLevel.INFO,
- LogGroup("TEST_GROUP", true, true, TAG1))
-
- invocation.arguments[0] as CompilationUnit
- }
-
- configBuilder.processClass(dummyCompilationUnit)
-
- val parsedConfig = parseConfig(configBuilder.build())
- assertEquals(1, parsedConfig.size)
- assertEquals(TEST1, parsedConfig[CodeUtils.hash(TEST1.messageString, LogLevel.INFO)])
- }
-
- @Test
- fun processClass_disabled() {
- Mockito.`when`(processor.process(any(CompilationUnit::class.java),
- any(ProtoLogCallVisitor::class.java))).thenAnswer { invocation ->
- val visitor = invocation.arguments[1] as ProtoLogCallVisitor
-
- visitor.processCall(MethodCallExpr(), TEST1.messageString, LogLevel.INFO,
- LogGroup("TEST_GROUP", true, true, TAG1))
- visitor.processCall(MethodCallExpr(), TEST2.messageString, LogLevel.DEBUG,
- LogGroup("DEBUG_GROUP", false, true, TAG2))
- visitor.processCall(MethodCallExpr(), TEST3.messageString, LogLevel.ERROR,
- LogGroup("DEBUG_GROUP", true, false, TAG2))
-
- invocation.arguments[0] as CompilationUnit
- }
-
- configBuilder.processClass(dummyCompilationUnit)
-
- val parsedConfig = parseConfig(configBuilder.build())
- assertEquals(2, parsedConfig.size)
- assertEquals(TEST1, parsedConfig[CodeUtils.hash(TEST1.messageString, LogLevel.INFO)])
- assertEquals(TEST3, parsedConfig[CodeUtils.hash(TEST3.messageString, LogLevel.ERROR)])
- }
-}
diff --git a/tools/protologtool/tests/com/android/protologtool/ViewerConfigParserTest.kt b/tools/protologtool/tests/com/android/protologtool/ViewerConfigParserTest.kt
deleted file mode 100644
index c0cea733eadd..000000000000
--- a/tools/protologtool/tests/com/android/protologtool/ViewerConfigParserTest.kt
+++ /dev/null
@@ -1,327 +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.protologtool
-
-import com.android.json.stream.JsonReader
-import org.junit.Test
-import java.io.StringReader
-import org.junit.Assert.assertEquals
-
-class ViewerConfigParserTest {
- private val parser = ViewerConfigParser()
-
- private fun getJSONReader(str: String): JsonReader {
- return JsonReader(StringReader(str))
- }
-
- @Test
- fun parseMessage() {
- val json = """
- {
- "message": "Test completed successfully: %b",
- "level": "ERROR",
- "group": "GENERIC_WM"
- }
- """
- val msg = parser.parseMessage(getJSONReader(json))
- assertEquals("Test completed successfully: %b", msg.messageString)
- assertEquals("ERROR", msg.level)
- assertEquals("GENERIC_WM", msg.groupName)
- }
-
- @Test
- fun parseMessage_reorder() {
- val json = """
- {
- "group": "GENERIC_WM",
- "level": "ERROR",
- "message": "Test completed successfully: %b"
- }
- """
- val msg = parser.parseMessage(getJSONReader(json))
- assertEquals("Test completed successfully: %b", msg.messageString)
- assertEquals("ERROR", msg.level)
- assertEquals("GENERIC_WM", msg.groupName)
- }
-
- @Test
- fun parseMessage_unknownEntry() {
- val json = """
- {
- "unknown": "unknown entries should not block parsing",
- "message": "Test completed successfully: %b",
- "level": "ERROR",
- "group": "GENERIC_WM"
- }
- """
- val msg = parser.parseMessage(getJSONReader(json))
- assertEquals("Test completed successfully: %b", msg.messageString)
- assertEquals("ERROR", msg.level)
- assertEquals("GENERIC_WM", msg.groupName)
- }
-
- @Test(expected = InvalidViewerConfigException::class)
- fun parseMessage_noMessage() {
- val json = """
- {
- "level": "ERROR",
- "group": "GENERIC_WM"
- }
- """
- parser.parseMessage(getJSONReader(json))
- }
-
- @Test(expected = InvalidViewerConfigException::class)
- fun parseMessage_noLevel() {
- val json = """
- {
- "message": "Test completed successfully: %b",
- "group": "GENERIC_WM"
- }
- """
- parser.parseMessage(getJSONReader(json))
- }
-
- @Test(expected = InvalidViewerConfigException::class)
- fun parseMessage_noGroup() {
- val json = """
- {
- "message": "Test completed successfully: %b",
- "level": "ERROR"
- }
- """
- parser.parseMessage(getJSONReader(json))
- }
-
- @Test
- fun parseGroup() {
- val json = """
- {
- "tag": "WindowManager"
- }
- """
- val group = parser.parseGroup(getJSONReader(json))
- assertEquals("WindowManager", group.tag)
- }
-
- @Test
- fun parseGroup_unknownEntry() {
- val json = """
- {
- "unknown": "unknown entries should not block parsing",
- "tag": "WindowManager"
- }
- """
- val group = parser.parseGroup(getJSONReader(json))
- assertEquals("WindowManager", group.tag)
- }
-
- @Test(expected = InvalidViewerConfigException::class)
- fun parseGroup_noTag() {
- val json = """
- {
- }
- """
- parser.parseGroup(getJSONReader(json))
- }
-
- @Test
- fun parseMessages() {
- val json = """
- {
- "70933285": {
- "message": "Test completed successfully: %b",
- "level": "ERROR",
- "group": "GENERIC_WM"
- },
- "1792430067": {
- "message": "Attempted to add window to a display that does not exist: %d. Aborting.",
- "level": "WARN",
- "group": "ERROR_WM"
- }
- }
- """
- val messages = parser.parseMessages(getJSONReader(json))
- assertEquals(2, messages.size)
- val msg1 =
- ViewerConfigParser.MessageEntry("Test completed successfully: %b",
- "ERROR", "GENERIC_WM")
- val msg2 =
- ViewerConfigParser.MessageEntry("Attempted to add window to a display that " +
- "does not exist: %d. Aborting.", "WARN", "ERROR_WM")
-
- assertEquals(msg1, messages[70933285])
- assertEquals(msg2, messages[1792430067])
- }
-
- @Test(expected = InvalidViewerConfigException::class)
- fun parseMessages_invalidHash() {
- val json = """
- {
- "invalid": {
- "message": "Test completed successfully: %b",
- "level": "ERROR",
- "group": "GENERIC_WM"
- }
- }
- """
- parser.parseMessages(getJSONReader(json))
- }
-
- @Test
- fun parseGroups() {
- val json = """
- {
- "GENERIC_WM": {
- "tag": "WindowManager"
- },
- "ERROR_WM": {
- "tag": "WindowManagerError"
- }
- }
- """
- val groups = parser.parseGroups(getJSONReader(json))
- assertEquals(2, groups.size)
- val grp1 = ViewerConfigParser.GroupEntry("WindowManager")
- val grp2 = ViewerConfigParser.GroupEntry("WindowManagerError")
- assertEquals(grp1, groups["GENERIC_WM"])
- assertEquals(grp2, groups["ERROR_WM"])
- }
-
- @Test
- fun parseConfig() {
- val json = """
- {
- "version": "${Constants.VERSION}",
- "messages": {
- "70933285": {
- "message": "Test completed successfully: %b",
- "level": "ERROR",
- "group": "GENERIC_WM"
- }
- },
- "groups": {
- "GENERIC_WM": {
- "tag": "WindowManager"
- }
- }
- }
- """
- val config = parser.parseConfig(getJSONReader(json))
- assertEquals(1, config.size)
- val cfg1 = ViewerConfigParser.ConfigEntry("Test completed successfully: %b",
- "ERROR", "WindowManager")
- assertEquals(cfg1, config[70933285])
- }
-
- @Test(expected = InvalidViewerConfigException::class)
- fun parseConfig_invalidVersion() {
- val json = """
- {
- "version": "invalid",
- "messages": {
- "70933285": {
- "message": "Test completed successfully: %b",
- "level": "ERROR",
- "group": "GENERIC_WM"
- }
- },
- "groups": {
- "GENERIC_WM": {
- "tag": "WindowManager"
- }
- }
- }
- """
- parser.parseConfig(getJSONReader(json))
- }
-
- @Test(expected = InvalidViewerConfigException::class)
- fun parseConfig_noVersion() {
- val json = """
- {
- "messages": {
- "70933285": {
- "message": "Test completed successfully: %b",
- "level": "ERROR",
- "group": "GENERIC_WM"
- }
- },
- "groups": {
- "GENERIC_WM": {
- "tag": "WindowManager"
- }
- }
- }
- """
- parser.parseConfig(getJSONReader(json))
- }
-
- @Test(expected = InvalidViewerConfigException::class)
- fun parseConfig_noMessages() {
- val json = """
- {
- "version": "${Constants.VERSION}",
- "groups": {
- "GENERIC_WM": {
- "tag": "WindowManager"
- }
- }
- }
- """
- parser.parseConfig(getJSONReader(json))
- }
-
- @Test(expected = InvalidViewerConfigException::class)
- fun parseConfig_noGroups() {
- val json = """
- {
- "version": "${Constants.VERSION}",
- "messages": {
- "70933285": {
- "message": "Test completed successfully: %b",
- "level": "ERROR",
- "group": "GENERIC_WM"
- }
- }
- }
- """
- parser.parseConfig(getJSONReader(json))
- }
-
- @Test(expected = InvalidViewerConfigException::class)
- fun parseConfig_missingGroup() {
- val json = """
- {
- "version": "${Constants.VERSION}",
- "messages": {
- "70933285": {
- "message": "Test completed successfully: %b",
- "level": "ERROR",
- "group": "GENERIC_WM"
- }
- },
- "groups": {
- "ERROR_WM": {
- "tag": "WindowManager"
- }
- }
- }
- """
- val config = parser.parseConfig(getJSONReader(json))
- }
-}
diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp
index 14eead853f50..1390f63248f9 100644
--- a/tools/streaming_proto/Android.bp
+++ b/tools/streaming_proto/Android.bp
@@ -29,7 +29,7 @@ cc_defaults {
"-Werror",
],
- shared_libs: ["libprotoc"],
+ static_libs: ["libprotoc"],
}
cc_binary_host {
diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp
index 56a242f1daaf..5ac9dfd2a557 100644
--- a/tools/validatekeymaps/Main.cpp
+++ b/tools/validatekeymaps/Main.cpp
@@ -137,7 +137,6 @@ static bool validateFile(const char* filename) {
}
}
- log("No errors.\n\n");
return true;
}
diff --git a/wifi/java/android/net/wifi/IActionListener.aidl b/wifi/java/android/net/wifi/IActionListener.aidl
new file mode 100644
index 000000000000..faa0901cb087
--- /dev/null
+++ b/wifi/java/android/net/wifi/IActionListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+/**
+ * Interface for generic wifi callbacks.
+ * @hide
+ */
+oneway interface IActionListener
+{
+ void onSuccess();
+ void onFailure(int reason);
+}
diff --git a/wifi/java/android/net/wifi/ILocalOnlyHotspotCallback.aidl b/wifi/java/android/net/wifi/ILocalOnlyHotspotCallback.aidl
new file mode 100644
index 000000000000..b83b594c8cb9
--- /dev/null
+++ b/wifi/java/android/net/wifi/ILocalOnlyHotspotCallback.aidl
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.net.wifi.WifiConfiguration;
+
+/**
+ * Communicates LOHS status back to the application process.
+ *
+ * @hide
+ */
+oneway interface ILocalOnlyHotspotCallback {
+ void onHotspotStarted(in WifiConfiguration config);
+ void onHotspotStopped();
+ void onHotspotFailed(int reason);
+}
diff --git a/wifi/java/android/net/wifi/IScanResultsListener.aidl b/wifi/java/android/net/wifi/IScanResultsListener.aidl
new file mode 100644
index 000000000000..bec74a620380
--- /dev/null
+++ b/wifi/java/android/net/wifi/IScanResultsListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+/**
+ * Interface for Wi-Fi scan result available callback.
+ *
+ * @hide
+ */
+oneway interface IScanResultsListener
+{
+ void onScanResultsAvailable();
+}
diff --git a/wifi/java/android/net/wifi/ITxPacketCountListener.aidl b/wifi/java/android/net/wifi/ITxPacketCountListener.aidl
new file mode 100644
index 000000000000..8606ab5afa9c
--- /dev/null
+++ b/wifi/java/android/net/wifi/ITxPacketCountListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+/**
+ * Interface for tx packet counter callback.
+ * @hide
+ */
+oneway interface ITxPacketCountListener
+{
+ void onSuccess(int count);
+ void onFailure(int reason);
+}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index df9a1d55af20..5ab6678afe78 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -24,10 +24,14 @@ import android.net.wifi.hotspot2.IProvisioningCallback;
import android.net.DhcpInfo;
import android.net.Network;
+import android.net.wifi.IActionListener;
import android.net.wifi.IDppCallback;
+import android.net.wifi.ILocalOnlyHotspotCallback;
import android.net.wifi.INetworkRequestMatchCallback;
+import android.net.wifi.IScanResultsListener;
import android.net.wifi.ISoftApCallback;
import android.net.wifi.ITrafficStateCallback;
+import android.net.wifi.ITxPacketCountListener;
import android.net.wifi.IOnWifiUsabilityStatsListener;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiActivityEnergyInfo;
@@ -107,8 +111,6 @@ interface IWifiManager
int getWifiEnabledState();
- void setCountryCode(String country);
-
String getCountryCode();
boolean isDualBandSupported();
@@ -139,11 +141,11 @@ interface IWifiManager
boolean stopSoftAp();
- int startLocalOnlyHotspot(in Messenger messenger, in IBinder binder, String packageName);
+ int startLocalOnlyHotspot(in ILocalOnlyHotspotCallback callback, String packageName);
void stopLocalOnlyHotspot();
- void startWatchLocalOnlyHotspot(in Messenger messenger, in IBinder binder);
+ void startWatchLocalOnlyHotspot(in ILocalOnlyHotspotCallback callback);
void stopWatchLocalOnlyHotspot();
@@ -157,8 +159,6 @@ interface IWifiManager
void notifyUserOfApBandConversion(String packageName);
- Messenger getWifiServiceMessenger(String packageName);
-
void enableTdls(String remoteIPAddress, boolean enable);
void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable);
@@ -250,5 +250,17 @@ interface IWifiManager
void updateWifiUsabilityScore(int seqNum, int score, int predictionHorizonSec);
+ oneway void connect(in WifiConfiguration config, int netId, in IBinder binder, in IActionListener listener, int callbackIdentifier);
+
+ oneway void save(in WifiConfiguration config, in IBinder binder, in IActionListener listener, int callbackIdentifier);
+
+ oneway void forget(int netId, in IBinder binder, in IActionListener listener, int callbackIdentifier);
+
+ oneway void getTxPacketCount(String packageName, in IBinder binder, in ITxPacketCountListener listener, int callbackIdentifier);
+
+ void registerScanResultsListener(in IBinder binder, in IScanResultsListener Listener, int listenerIdentifier);
+
+ void unregisterScanResultsListener(int listenerIdentifier);
+
int getSoftApWifiGeneration();
}
diff --git a/wifi/java/android/net/wifi/RssiPacketCountInfo.java b/wifi/java/android/net/wifi/RssiPacketCountInfo.java
deleted file mode 100644
index 4301165308d1..000000000000
--- a/wifi/java/android/net/wifi/RssiPacketCountInfo.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Bundle of RSSI and packet count information, for WiFi watchdog
- *
- * @see WifiWatchdogStateMachine
- *
- * @hide
- */
-public class RssiPacketCountInfo implements Parcelable {
-
- public int rssi;
-
- public int txgood;
-
- public int txbad;
-
- public int rxgood;
-
- public RssiPacketCountInfo() {
- rssi = txgood = txbad = rxgood = 0;
- }
-
- private RssiPacketCountInfo(Parcel in) {
- rssi = in.readInt();
- txgood = in.readInt();
- txbad = in.readInt();
- rxgood = in.readInt();
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeInt(rssi);
- out.writeInt(txgood);
- out.writeInt(txbad);
- out.writeInt(rxgood);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- public static final @android.annotation.NonNull Parcelable.Creator<RssiPacketCountInfo> CREATOR =
- new Parcelable.Creator<RssiPacketCountInfo>() {
- @Override
- public RssiPacketCountInfo createFromParcel(Parcel in) {
- return new RssiPacketCountInfo(in);
- }
-
- @Override
- public RssiPacketCountInfo[] newArray(int size) {
- return new RssiPacketCountInfo[size];
- }
- };
-}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 258c5813f960..b51622c065fb 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -1113,26 +1113,6 @@ public class WifiConfiguration implements Parcelable {
}
/**
- * @hide
- * Returns Randomized MAC address to use with the network.
- * If it is not set/valid, creates a new randomized address.
- * If it can't generate a valid mac, returns the default MAC.
- */
- public @NonNull MacAddress getOrCreateRandomizedMacAddress() {
- int randomMacGenerationCount = 0;
- while (!isValidMacAddressForRandomization(mRandomizedMacAddress)
- && randomMacGenerationCount < MAXIMUM_RANDOM_MAC_GENERATION_RETRY) {
- mRandomizedMacAddress = MacAddress.createRandomUnicastAddress();
- randomMacGenerationCount++;
- }
-
- if (!isValidMacAddressForRandomization(mRandomizedMacAddress)) {
- mRandomizedMacAddress = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
- }
- return mRandomizedMacAddress;
- }
-
- /**
* Returns MAC address set to be the local randomized MAC address.
* Depending on user preference, the device may or may not use the returned MAC address for
* connections to this network.
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 0127c630baa9..7eadd3b3d9e9 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -16,6 +16,7 @@
package android.net.wifi;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -27,6 +28,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -34,7 +37,7 @@ import java.util.EnumMap;
import java.util.Locale;
/**
- * Describes the state of any Wifi connection that is active or
+ * Describes the state of any Wi-Fi connection that is active or
* is in the process of being set up.
*/
public class WifiInfo implements Parcelable {
@@ -96,6 +99,47 @@ public class WifiInfo implements Parcelable {
private int mRssi;
/**
+ * Wi-Fi unknown technology
+ */
+ public static final int WIFI_TECHNOLOGY_UNKNOWN = 0;
+
+ /**
+ * Wi-Fi 802.11a/b/g
+ */
+ public static final int WIFI_TECHNOLOGY_LEGACY = 1;
+
+ /**
+ * Wi-Fi 802.11n
+ */
+ public static final int WIFI_TECHNOLOGY_11N = 4;
+
+ /**
+ * Wi-Fi 802.11ac
+ */
+ public static final int WIFI_TECHNOLOGY_11AC = 5;
+
+ /**
+ * Wi-Fi 802.11ax
+ */
+ public static final int WIFI_TECHNOLOGY_11AX = 6;
+
+ /** @hide */
+ @IntDef(prefix = { "WIFI_TECHNOLOGY_" }, value = {
+ WIFI_TECHNOLOGY_UNKNOWN,
+ WIFI_TECHNOLOGY_LEGACY,
+ WIFI_TECHNOLOGY_11N,
+ WIFI_TECHNOLOGY_11AC,
+ WIFI_TECHNOLOGY_11AX
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface WifiTechnology{}
+
+ /**
+ * Wi-Fi technology for the connection
+ */
+ private @WifiTechnology int mWifiTechnology;
+
+ /**
* The unit in which links speeds are expressed.
*/
public static final String LINK_SPEED_UNITS = "Mbps";
@@ -301,6 +345,7 @@ public class WifiInfo implements Parcelable {
txSuccessRate = source.txSuccessRate;
rxSuccessRate = source.rxSuccessRate;
score = source.score;
+ mWifiTechnology = source.mWifiTechnology;
}
}
@@ -389,6 +434,22 @@ public class WifiInfo implements Parcelable {
}
/**
+ * Sets the Wi-Fi technology
+ * @hide
+ */
+ public void setWifiTechnology(@WifiTechnology int wifiTechnology) {
+ mWifiTechnology = wifiTechnology;
+ }
+
+ /**
+ * Get connection Wi-Fi technology
+ * @return the connection Wi-Fi technology
+ */
+ public @WifiTechnology int getWifiTechnology() {
+ return mWifiTechnology;
+ }
+
+ /**
* Returns the current link speed in {@link #LINK_SPEED_UNITS}.
* @return the link speed or {@link #LINK_SPEED_UNKNOWN} if link speed is unknown.
* @see #LINK_SPEED_UNITS
@@ -727,6 +788,7 @@ public class WifiInfo implements Parcelable {
.append(", Wifi Generation: ").append(mWifiGeneration)
.append(", TWT support: ").append(mTwtSupport)
.append(", Eight Max VHT Spatial streams support: ").append(mVhtMax8SpatialStreamsSupport)
+ .append(", Wi-Fi technology: ").append(mWifiTechnology)
.append(", RSSI: ").append(mRssi)
.append(", Link speed: ").append(mLinkSpeed).append(LINK_SPEED_UNITS)
.append(", Tx Link speed: ").append(mTxLinkSpeed).append(LINK_SPEED_UNITS)
@@ -785,6 +847,7 @@ public class WifiInfo implements Parcelable {
dest.writeInt(mWifiGeneration);
dest.writeInt(mVhtMax8SpatialStreamsSupport ? 1 : 0);
dest.writeInt(mTwtSupport ? 1 : 0);
+ dest.writeInt(mWifiTechnology);
}
/** Implement the Parcelable interface {@hide} */
@@ -829,6 +892,7 @@ public class WifiInfo implements Parcelable {
info.mWifiGeneration = in.readInt();
info.mVhtMax8SpatialStreamsSupport = in.readInt() != 0;
info.mTwtSupport = in.readInt() != 0;
+ info.mWifiTechnology = in.readInt();
return info;
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 026c6a8ec7d6..6d382eef6808 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -38,6 +38,7 @@ import android.net.DhcpInfo;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
+import android.net.NetworkStack;
import android.net.wifi.hotspot2.IProvisioningCallback;
import android.net.wifi.hotspot2.OsuProvider;
import android.net.wifi.hotspot2.PasspointConfiguration;
@@ -45,23 +46,19 @@ import android.net.wifi.hotspot2.ProvisioningCallback;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.WorkSource;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
-import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.AsyncChannel;
-import com.android.internal.util.Protocol;
import com.android.server.net.NetworkPinner;
import dalvik.system.CloseGuard;
@@ -75,7 +72,6 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
/**
@@ -566,7 +562,9 @@ public class WifiManager {
*
* @hide
*/
- public static final String EXTRA_WIFI_AP_INTERFACE_NAME = "wifi_ap_interface_name";
+ @SystemApi
+ public static final String EXTRA_WIFI_AP_INTERFACE_NAME =
+ "android.net.wifi.extra.WIFI_AP_INTERFACE_NAME";
/**
* The intended ip mode for this softap.
* @see #IFACE_IP_MODE_TETHERED
@@ -574,7 +572,8 @@ public class WifiManager {
*
* @hide
*/
- public static final String EXTRA_WIFI_AP_MODE = "wifi_ap_mode";
+ @SystemApi
+ public static final String EXTRA_WIFI_AP_MODE = "android.net.wifi.extra.WIFI_AP_MODE";
/** @hide */
@IntDef(flag = false, prefix = { "WIFI_AP_STATE_" }, value = {
@@ -680,6 +679,7 @@ public class WifiManager {
*
* @hide
*/
+ @SystemApi
public static final int IFACE_IP_MODE_UNSPECIFIED = -1;
/**
@@ -689,6 +689,7 @@ public class WifiManager {
*
* @hide
*/
+ @SystemApi
public static final int IFACE_IP_MODE_CONFIGURATION_ERROR = 0;
/**
@@ -698,6 +699,7 @@ public class WifiManager {
*
* @hide
*/
+ @SystemApi
public static final int IFACE_IP_MODE_TETHERED = 1;
/**
@@ -707,6 +709,7 @@ public class WifiManager {
*
* @hide
*/
+ @SystemApi
public static final int IFACE_IP_MODE_LOCAL_ONLY = 2;
/**
@@ -1204,26 +1207,9 @@ public class WifiManager {
IWifiManager mService;
private final int mTargetSdkVersion;
- private static final int INVALID_KEY = 0;
- private int mListenerKey = 1;
- private final SparseArray mListenerMap = new SparseArray();
- private final Object mListenerMapLock = new Object();
-
- private AsyncChannel mAsyncChannel;
- private CountDownLatch mConnected;
private Looper mLooper;
private boolean mVerboseLoggingEnabled = false;
- /* LocalOnlyHotspot callback message types */
- /** @hide */
- public static final int HOTSPOT_STARTED = 0;
- /** @hide */
- public static final int HOTSPOT_STOPPED = 1;
- /** @hide */
- public static final int HOTSPOT_FAILED = 2;
- /** @hide */
- public static final int HOTSPOT_OBSERVER_REGISTERED = 3;
-
private final Object mLock = new Object(); // lock guarding access to the following vars
@GuardedBy("mLock")
private LocalOnlyHotspotCallbackProxy mLOHSCallbackProxy;
@@ -1991,12 +1977,13 @@ public class WifiManager {
}
/**
- * Remove the Passpoint configuration identified by its FQDN (Fully Qualified Domain Name).
+ * Remove the Passpoint configuration identified by its FQDN (Fully Qualified Domain Name) added
+ * by the caller.
*
- * @param fqdn The FQDN of the Passpoint configuration to be removed
+ * @param fqdn The FQDN of the Passpoint configuration added by the caller to be removed
* @throws IllegalArgumentException if no configuration is associated with the given FQDN or
* Passpoint is not enabled on the device.
- * @deprecated This is no longer supported.
+ * @deprecated This will be non-functional in a future release.
*/
@Deprecated
@RequiresPermission(anyOf = {
@@ -2020,12 +2007,12 @@ public class WifiManager {
}
/**
- * Return the list of installed Passpoint configurations.
+ * Return the list of installed Passpoint configurations added by the caller.
*
* An empty list will be returned when no configurations are installed.
*
- * @return A list of {@link PasspointConfiguration}
- * @deprecated This is no longer supported.
+ * @return A list of {@link PasspointConfiguration} added by the caller
+ * @deprecated This will be non-functional in a future release.
*/
@Deprecated
@RequiresPermission(anyOf = {
@@ -2658,25 +2645,6 @@ public class WifiManager {
}
/**
- * Set the country code.
- * @param countryCode country code in ISO 3166 format.
- *
- * @hide
- */
- public void setCountryCode(@NonNull String country) {
- try {
- IWifiManager iWifiManager = getIWifiManager();
- if (iWifiManager == null) {
- if (TextUtils.isEmpty(country)) return;
- throw new RemoteException("Wifi service is not running");
- }
- iWifiManager.setCountryCode(country);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* get the country code.
* @return the country code in ISO 3166 format.
*
@@ -2801,8 +2769,23 @@ public class WifiManager {
*
* @hide for CTS test only
*/
- public void getTxPacketCount(TxPacketCountListener listener) {
- getChannel().sendMessage(RSSI_PKTCNT_FETCH, 0, putListener(listener));
+ public void getTxPacketCount(@NonNull TxPacketCountListener listener) {
+ if (listener == null) throw new IllegalArgumentException("listener cannot be null");
+ Binder binder = new Binder();
+ TxPacketCountListenerProxy listenerProxy =
+ new TxPacketCountListenerProxy(mLooper, listener);
+ try {
+ IWifiManager iWifiManager = getIWifiManager();
+ if (iWifiManager == null) {
+ throw new RemoteException("Wifi service is not running");
+ }
+ iWifiManager.getTxPacketCount(mContext.getOpPackageName(), binder, listenerProxy,
+ listener.hashCode());
+ } catch (RemoteException e) {
+ listenerProxy.onFailure(ERROR);
+ } catch (SecurityException e) {
+ listenerProxy.onFailure(NOT_AUTHORIZED);
+ }
}
/**
@@ -2843,16 +2826,21 @@ public class WifiManager {
/**
* Call allowing ConnectivityService to update WifiService with interface mode changes.
*
- * The possible modes include: {@link #IFACE_IP_MODE_TETHERED},
- * {@link #IFACE_IP_MODE_LOCAL_ONLY},
- * {@link #IFACE_IP_MODE_CONFIGURATION_ERROR}
- *
- * @param ifaceName String name of the updated interface
- * @param mode int representing the new mode
+ * @param ifaceName String name of the updated interface, or null to represent all interfaces
+ * @param mode int representing the new mode, one of:
+ * {@link #IFACE_IP_MODE_TETHERED},
+ * {@link #IFACE_IP_MODE_LOCAL_ONLY},
+ * {@link #IFACE_IP_MODE_CONFIGURATION_ERROR},
+ * {@link #IFACE_IP_MODE_UNSPECIFIED}
*
* @hide
*/
- public void updateInterfaceIpState(String ifaceName, int mode) {
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
+ })
+ public void updateInterfaceIpState(@Nullable String ifaceName, @IfaceIpMode int mode) {
try {
IWifiManager iWifiManager = getIWifiManager();
if (iWifiManager == null) {
@@ -2875,6 +2863,11 @@ public class WifiManager {
*
* @hide
*/
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
+ })
public boolean startSoftAp(@Nullable WifiConfiguration wifiConfig) {
try {
IWifiManager iWifiManager = getIWifiManager();
@@ -2892,6 +2885,11 @@ public class WifiManager {
*
* @hide
*/
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
+ })
public boolean stopSoftAp() {
try {
IWifiManager iWifiManager = getIWifiManager();
@@ -2902,6 +2900,13 @@ public class WifiManager {
}
}
+ private Executor executorForHandler(@Nullable Handler handler) {
+ if (handler == null) {
+ return mContext.getMainExecutor();
+ }
+ return new HandlerExecutor(handler);
+ }
+
/**
* Request a local only hotspot that an application can use to communicate between co-located
* devices connected to the created WiFi hotspot. The network created by this method will not
@@ -2959,21 +2964,20 @@ public class WifiManager {
*/
public void startLocalOnlyHotspot(LocalOnlyHotspotCallback callback,
@Nullable Handler handler) {
+ Executor executor = executorForHandler(handler);
synchronized (mLock) {
- Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
LocalOnlyHotspotCallbackProxy proxy =
- new LocalOnlyHotspotCallbackProxy(this, looper, callback);
+ new LocalOnlyHotspotCallbackProxy(this, executor, callback);
try {
IWifiManager iWifiManager = getIWifiManager();
if (iWifiManager == null) {
throw new RemoteException("Wifi service is not running");
}
String packageName = mContext.getOpPackageName();
- int returnCode = iWifiManager.startLocalOnlyHotspot(
- proxy.getMessenger(), new Binder(), packageName);
+ int returnCode = iWifiManager.startLocalOnlyHotspot(proxy, packageName);
if (returnCode != LocalOnlyHotspotCallback.REQUEST_REGISTERED) {
// Send message to the proxy to make sure we call back on the correct thread
- proxy.notifyFailed(returnCode);
+ proxy.onHotspotFailed(returnCode);
return;
}
mLOHSCallbackProxy = proxy;
@@ -3051,16 +3055,16 @@ public class WifiManager {
*/
public void watchLocalOnlyHotspot(LocalOnlyHotspotObserver observer,
@Nullable Handler handler) {
+ Executor executor = executorForHandler(handler);
synchronized (mLock) {
- Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
- mLOHSObserverProxy = new LocalOnlyHotspotObserverProxy(this, looper, observer);
+ mLOHSObserverProxy =
+ new LocalOnlyHotspotObserverProxy(this, executor, observer);
try {
IWifiManager iWifiManager = getIWifiManager();
if (iWifiManager == null) {
throw new RemoteException("Wifi service is not running");
}
- iWifiManager.startWatchLocalOnlyHotspot(
- mLOHSObserverProxy.getMessenger(), new Binder());
+ iWifiManager.startWatchLocalOnlyHotspot(mLOHSObserverProxy);
mLOHSObserverProxy.registered();
} catch (RemoteException e) {
mLOHSObserverProxy = null;
@@ -3237,76 +3241,6 @@ public class WifiManager {
}
}
- /* TODO: deprecate synchronous API and open up the following API */
-
- private static final int BASE = Protocol.BASE_WIFI_MANAGER;
-
- /* Commands to WifiService */
- /** @hide */
- public static final int CONNECT_NETWORK = BASE + 1;
- /** @hide */
- public static final int CONNECT_NETWORK_FAILED = BASE + 2;
- /** @hide */
- public static final int CONNECT_NETWORK_SUCCEEDED = BASE + 3;
-
- /** @hide */
- public static final int FORGET_NETWORK = BASE + 4;
- /** @hide */
- public static final int FORGET_NETWORK_FAILED = BASE + 5;
- /** @hide */
- public static final int FORGET_NETWORK_SUCCEEDED = BASE + 6;
-
- /** @hide */
- public static final int SAVE_NETWORK = BASE + 7;
- /** @hide */
- public static final int SAVE_NETWORK_FAILED = BASE + 8;
- /** @hide */
- public static final int SAVE_NETWORK_SUCCEEDED = BASE + 9;
-
- /** @hide
- * @deprecated This is deprecated
- */
- public static final int START_WPS = BASE + 10;
- /** @hide
- * @deprecated This is deprecated
- */
- public static final int START_WPS_SUCCEEDED = BASE + 11;
- /** @hide
- * @deprecated This is deprecated
- */
- public static final int WPS_FAILED = BASE + 12;
- /** @hide
- * @deprecated This is deprecated
- */
- public static final int WPS_COMPLETED = BASE + 13;
-
- /** @hide
- * @deprecated This is deprecated
- */
- public static final int CANCEL_WPS = BASE + 14;
- /** @hide
- * @deprecated This is deprecated
- */
- public static final int CANCEL_WPS_FAILED = BASE + 15;
- /** @hide
- * @deprecated This is deprecated
- */
- public static final int CANCEL_WPS_SUCCEDED = BASE + 16;
-
- /** @hide */
- public static final int DISABLE_NETWORK = BASE + 17;
- /** @hide */
- public static final int DISABLE_NETWORK_FAILED = BASE + 18;
- /** @hide */
- public static final int DISABLE_NETWORK_SUCCEEDED = BASE + 19;
-
- /** @hide */
- public static final int RSSI_PKTCNT_FETCH = BASE + 20;
- /** @hide */
- public static final int RSSI_PKTCNT_FETCH_SUCCEEDED = BASE + 21;
- /** @hide */
- public static final int RSSI_PKTCNT_FETCH_FAILED = BASE + 22;
-
/**
* Passed with {@link ActionListener#onFailure}.
* Indicates that the operation failed due to an internal error.
@@ -3329,6 +3263,11 @@ public class WifiManager {
*/
public static final int BUSY = 2;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({ERROR, IN_PROGRESS, BUSY})
+ public @interface ActionListenerFailureReason {}
+
/* WPS specific errors */
/** WPS overlap detected
* @deprecated This is deprecated
@@ -3373,20 +3312,13 @@ public class WifiManager {
public interface ActionListener {
/**
* The operation succeeded.
- * This is called when the scan request has been validated and ready
- * to sent to driver.
*/
- public void onSuccess();
+ void onSuccess();
/**
* The operation failed.
- * This is called when the scan request failed.
- * @param reason The reason for failure could be one of the following:
- * {@link #REASON_INVALID_REQUEST}} is specified when scan request parameters are invalid.
- * {@link #REASON_NOT_AUTHORIZED} is specified when requesting app doesn't have the required
- * permission to request a scan.
- * {@link #REASON_UNSPECIFIED} is specified when driver reports a scan failure.
+ * @param reason The reason for failure depends on the operation.
*/
- public void onFailure(int reason);
+ void onFailure(@ActionListenerFailureReason int reason);
}
/** Interface for callback invocation on a start WPS action
@@ -3431,6 +3363,41 @@ public class WifiManager {
}
/**
+ * Callback proxy for TxPacketCountListener objects.
+ *
+ * @hide
+ */
+ private class TxPacketCountListenerProxy extends ITxPacketCountListener.Stub {
+ private final Handler mHandler;
+ private final TxPacketCountListener mCallback;
+
+ TxPacketCountListenerProxy(Looper looper, TxPacketCountListener callback) {
+ mHandler = new Handler(looper);
+ mCallback = callback;
+ }
+
+ @Override
+ public void onSuccess(int count) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "TxPacketCounterProxy: onSuccess: count=" + count);
+ }
+ mHandler.post(() -> {
+ mCallback.onSuccess(count);
+ });
+ }
+
+ @Override
+ public void onFailure(int reason) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "TxPacketCounterProxy: onFailure: reason=" + reason);
+ }
+ mHandler.post(() -> {
+ mCallback.onFailure(reason);
+ });
+ }
+ }
+
+ /**
* Base class for soft AP callback. Should be extended by applications and set when calling
* {@link WifiManager#registerSoftApCallback(SoftApCallback, Handler)}.
*
@@ -3687,82 +3654,58 @@ public class WifiManager {
/**
* Callback proxy for LocalOnlyHotspotCallback objects.
*/
- private static class LocalOnlyHotspotCallbackProxy {
- private final Handler mHandler;
+ private static class LocalOnlyHotspotCallbackProxy extends ILocalOnlyHotspotCallback.Stub {
private final WeakReference<WifiManager> mWifiManager;
- private final Looper mLooper;
- private final Messenger mMessenger;
+ private final Executor mExecutor;
+ private final LocalOnlyHotspotCallback mCallback;
/**
- * Constructs a {@link LocalOnlyHotspotCallback} using the specified looper. All callbacks
- * will be delivered on the thread of the specified looper.
+ * Constructs a {@link LocalOnlyHotspotCallbackProxy} using the specified executor. All
+ * callbacks will run using the given executor.
*
* @param manager WifiManager
- * @param looper Looper for delivering callbacks
+ * @param executor Executor for delivering callbacks.
* @param callback LocalOnlyHotspotCallback to notify the calling application.
*/
- LocalOnlyHotspotCallbackProxy(WifiManager manager, Looper looper,
- final LocalOnlyHotspotCallback callback) {
+ LocalOnlyHotspotCallbackProxy(WifiManager manager, Executor executor,
+ LocalOnlyHotspotCallback callback) {
mWifiManager = new WeakReference<>(manager);
- mLooper = looper;
-
- mHandler = new Handler(looper) {
- @Override
- public void handleMessage(Message msg) {
- Log.d(TAG, "LocalOnlyHotspotCallbackProxy: handle message what: "
- + msg.what + " msg: " + msg);
-
- WifiManager manager = mWifiManager.get();
- if (manager == null) {
- Log.w(TAG, "LocalOnlyHotspotCallbackProxy: handle message post GC");
- return;
- }
+ mExecutor = executor;
+ mCallback = callback;
+ }
- switch (msg.what) {
- case HOTSPOT_STARTED:
- WifiConfiguration config = (WifiConfiguration) msg.obj;
- if (config == null) {
- Log.e(TAG, "LocalOnlyHotspotCallbackProxy: config cannot be null.");
- callback.onFailed(LocalOnlyHotspotCallback.ERROR_GENERIC);
- return;
- }
- callback.onStarted(manager.new LocalOnlyHotspotReservation(config));
- break;
- case HOTSPOT_STOPPED:
- Log.w(TAG, "LocalOnlyHotspotCallbackProxy: hotspot stopped");
- callback.onStopped();
- break;
- case HOTSPOT_FAILED:
- int reasonCode = msg.arg1;
- Log.w(TAG, "LocalOnlyHotspotCallbackProxy: failed to start. reason: "
- + reasonCode);
- callback.onFailed(reasonCode);
- Log.w(TAG, "done with the callback...");
- break;
- default:
- Log.e(TAG, "LocalOnlyHotspotCallbackProxy unhandled message. type: "
- + msg.what);
- }
- }
- };
- mMessenger = new Messenger(mHandler);
+ @Override
+ public void onHotspotStarted(WifiConfiguration config) {
+ WifiManager manager = mWifiManager.get();
+ if (manager == null) return;
+
+ if (config == null) {
+ Log.e(TAG, "LocalOnlyHotspotCallbackProxy: config cannot be null.");
+ onHotspotFailed(LocalOnlyHotspotCallback.ERROR_GENERIC);
+ return;
+ }
+ final LocalOnlyHotspotReservation reservation =
+ manager.new LocalOnlyHotspotReservation(config);
+ mExecutor.execute(() -> mCallback.onStarted(reservation));
}
- public Messenger getMessenger() {
- return mMessenger;
+ @Override
+ public void onHotspotStopped() {
+ WifiManager manager = mWifiManager.get();
+ if (manager == null) return;
+
+ Log.w(TAG, "LocalOnlyHotspotCallbackProxy: hotspot stopped");
+ mExecutor.execute(() -> mCallback.onStopped());
}
- /**
- * Helper method allowing the the incoming application call to move the onFailed callback
- * over to the desired callback thread.
- *
- * @param reason int representing the error type
- */
- public void notifyFailed(int reason) throws RemoteException {
- Message msg = Message.obtain();
- msg.what = HOTSPOT_FAILED;
- msg.arg1 = reason;
- mMessenger.send(msg);
+ @Override
+ public void onHotspotFailed(int reason) {
+ WifiManager manager = mWifiManager.get();
+ if (manager == null) return;
+
+ Log.w(TAG, "LocalOnlyHotspotCallbackProxy: failed to start. reason: "
+ + reason);
+ mExecutor.execute(() -> mCallback.onFailed(reason));
}
}
@@ -3830,191 +3773,115 @@ public class WifiManager {
/**
* Callback proxy for LocalOnlyHotspotObserver objects.
*/
- private static class LocalOnlyHotspotObserverProxy {
- private final Handler mHandler;
+ private static class LocalOnlyHotspotObserverProxy extends ILocalOnlyHotspotCallback.Stub {
private final WeakReference<WifiManager> mWifiManager;
- private final Looper mLooper;
- private final Messenger mMessenger;
+ private final Executor mExecutor;
+ private final LocalOnlyHotspotObserver mObserver;
/**
* Constructs a {@link LocalOnlyHotspotObserverProxy} using the specified looper.
* All callbacks will be delivered on the thread of the specified looper.
*
* @param manager WifiManager
- * @param looper Looper for delivering callbacks
+ * @param executor Executor for delivering callbacks
* @param observer LocalOnlyHotspotObserver to notify the calling application.
*/
- LocalOnlyHotspotObserverProxy(WifiManager manager, Looper looper,
+ LocalOnlyHotspotObserverProxy(WifiManager manager, Executor executor,
final LocalOnlyHotspotObserver observer) {
mWifiManager = new WeakReference<>(manager);
- mLooper = looper;
-
- mHandler = new Handler(looper) {
- @Override
- public void handleMessage(Message msg) {
- Log.d(TAG, "LocalOnlyHotspotObserverProxy: handle message what: "
- + msg.what + " msg: " + msg);
-
- WifiManager manager = mWifiManager.get();
- if (manager == null) {
- Log.w(TAG, "LocalOnlyHotspotObserverProxy: handle message post GC");
- return;
- }
-
- switch (msg.what) {
- case HOTSPOT_OBSERVER_REGISTERED:
- observer.onRegistered(manager.new LocalOnlyHotspotSubscription());
- break;
- case HOTSPOT_STARTED:
- WifiConfiguration config = (WifiConfiguration) msg.obj;
- if (config == null) {
- Log.e(TAG, "LocalOnlyHotspotObserverProxy: config cannot be null.");
- return;
- }
- observer.onStarted(config);
- break;
- case HOTSPOT_STOPPED:
- observer.onStopped();
- break;
- default:
- Log.e(TAG, "LocalOnlyHotspotObserverProxy unhandled message. type: "
- + msg.what);
- }
- }
- };
- mMessenger = new Messenger(mHandler);
- }
-
- public Messenger getMessenger() {
- return mMessenger;
+ mExecutor = executor;
+ mObserver = observer;
}
public void registered() throws RemoteException {
- Message msg = Message.obtain();
- msg.what = HOTSPOT_OBSERVER_REGISTERED;
- mMessenger.send(msg);
- }
- }
+ WifiManager manager = mWifiManager.get();
+ if (manager == null) return;
- // Ensure that multiple ServiceHandler threads do not interleave message dispatch.
- private static final Object sServiceHandlerDispatchLock = new Object();
-
- private class ServiceHandler extends Handler {
- ServiceHandler(Looper looper) {
- super(looper);
+ mExecutor.execute(() ->
+ mObserver.onRegistered(manager.new LocalOnlyHotspotSubscription()));
}
@Override
- public void handleMessage(Message message) {
- synchronized (sServiceHandlerDispatchLock) {
- dispatchMessageToListeners(message);
+ public void onHotspotStarted(WifiConfiguration config) {
+ WifiManager manager = mWifiManager.get();
+ if (manager == null) return;
+
+ if (config == null) {
+ Log.e(TAG, "LocalOnlyHotspotObserverProxy: config cannot be null.");
+ return;
}
+ mExecutor.execute(() -> mObserver.onStarted(config));
}
- private void dispatchMessageToListeners(Message message) {
- Object listener = removeListener(message.arg2);
- switch (message.what) {
- case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
- if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
- mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
- } else {
- Log.e(TAG, "Failed to set up channel connection");
- // This will cause all further async API calls on the WifiManager
- // to fail and throw an exception
- mAsyncChannel = null;
- }
- mConnected.countDown();
- break;
- case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
- // Ignore
- break;
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
- Log.e(TAG, "Channel connection lost");
- // This will cause all further async API calls on the WifiManager
- // to fail and throw an exception
- mAsyncChannel = null;
- getLooper().quit();
- break;
- /* ActionListeners grouped together */
- case WifiManager.CONNECT_NETWORK_FAILED:
- case WifiManager.FORGET_NETWORK_FAILED:
- case WifiManager.SAVE_NETWORK_FAILED:
- case WifiManager.DISABLE_NETWORK_FAILED:
- if (listener != null) {
- ((ActionListener) listener).onFailure(message.arg1);
- }
- break;
- /* ActionListeners grouped together */
- case WifiManager.CONNECT_NETWORK_SUCCEEDED:
- case WifiManager.FORGET_NETWORK_SUCCEEDED:
- case WifiManager.SAVE_NETWORK_SUCCEEDED:
- case WifiManager.DISABLE_NETWORK_SUCCEEDED:
- if (listener != null) {
- ((ActionListener) listener).onSuccess();
- }
- break;
- case WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED:
- if (listener != null) {
- RssiPacketCountInfo info = (RssiPacketCountInfo) message.obj;
- if (info != null)
- ((TxPacketCountListener) listener).onSuccess(info.txgood + info.txbad);
- else
- ((TxPacketCountListener) listener).onFailure(ERROR);
- }
- break;
- case WifiManager.RSSI_PKTCNT_FETCH_FAILED:
- if (listener != null) {
- ((TxPacketCountListener) listener).onFailure(message.arg1);
- }
- break;
- default:
- //ignore
- break;
- }
+ @Override
+ public void onHotspotStopped() {
+ WifiManager manager = mWifiManager.get();
+ if (manager == null) return;
+
+ mExecutor.execute(() -> mObserver.onStopped());
}
- }
- private int putListener(Object listener) {
- if (listener == null) return INVALID_KEY;
- int key;
- synchronized (mListenerMapLock) {
- do {
- key = mListenerKey++;
- } while (key == INVALID_KEY);
- mListenerMap.put(key, listener);
+ @Override
+ public void onHotspotFailed(int reason) {
+ // do nothing
}
- return key;
}
- private Object removeListener(int key) {
- if (key == INVALID_KEY) return null;
- synchronized (mListenerMapLock) {
- Object listener = mListenerMap.get(key);
- mListenerMap.remove(key);
- return listener;
+ /**
+ * Callback proxy for ActionListener objects.
+ */
+ private class ActionListenerProxy extends IActionListener.Stub {
+ private final String mActionTag;
+ private final Handler mHandler;
+ private final ActionListener mCallback;
+
+ ActionListenerProxy(String actionTag, Looper looper, ActionListener callback) {
+ mActionTag = actionTag;
+ mHandler = new Handler(looper);
+ mCallback = callback;
}
- }
- private synchronized AsyncChannel getChannel() {
- if (mAsyncChannel == null) {
- Messenger messenger = getWifiServiceMessenger();
- if (messenger == null) {
- throw new IllegalStateException(
- "getWifiServiceMessenger() returned null! This is invalid.");
+ @Override
+ public void onSuccess() {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "ActionListenerProxy:" + mActionTag + ": onSuccess");
}
+ mHandler.post(() -> {
+ mCallback.onSuccess();
+ });
+ }
- mAsyncChannel = new AsyncChannel();
- mConnected = new CountDownLatch(1);
+ @Override
+ public void onFailure(@ActionListenerFailureReason int reason) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "ActionListenerProxy:" + mActionTag + ": onFailure=" + reason);
+ }
+ mHandler.post(() -> {
+ mCallback.onFailure(reason);
+ });
+ }
+ }
- Handler handler = new ServiceHandler(mLooper);
- mAsyncChannel.connect(mContext, handler, messenger);
- try {
- mConnected.await();
- } catch (InterruptedException e) {
- Log.e(TAG, "interrupted wait at init");
+ private void connectInternal(@Nullable WifiConfiguration config, int networkId,
+ @Nullable ActionListener listener) {
+ ActionListenerProxy listenerProxy = null;
+ Binder binder = null;
+ if (listener != null) {
+ listenerProxy = new ActionListenerProxy("connect", mLooper, listener);
+ binder = new Binder();
+ }
+ try {
+ IWifiManager iWifiManager = getIWifiManager();
+ if (iWifiManager == null) {
+ throw new RemoteException("Wifi service is not running");
}
+ iWifiManager.connect(config, networkId, binder, listenerProxy,
+ listener == null ? 0 : listener.hashCode());
+ } catch (RemoteException e) {
+ if (listenerProxy != null) listenerProxy.onFailure(ERROR);
+ } catch (SecurityException e) {
+ if (listenerProxy != null) listenerProxy.onFailure(NOT_AUTHORIZED);
}
- return mAsyncChannel;
}
/**
@@ -4040,10 +3907,7 @@ public class WifiManager {
})
public void connect(@NonNull WifiConfiguration config, @Nullable ActionListener listener) {
if (config == null) throw new IllegalArgumentException("config cannot be null");
- // Use INVALID_NETWORK_ID for arg1 when passing a config object
- // arg1 is used to pass network id when the network already exists
- getChannel().sendMessage(CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,
- putListener(listener), config);
+ connectInternal(config, WifiConfiguration.INVALID_NETWORK_ID, listener);
}
/**
@@ -4066,7 +3930,7 @@ public class WifiManager {
})
public void connect(int networkId, @Nullable ActionListener listener) {
if (networkId < 0) throw new IllegalArgumentException("Network id cannot be negative");
- getChannel().sendMessage(CONNECT_NETWORK, networkId, putListener(listener));
+ connectInternal(null, networkId, listener);
}
/**
@@ -4097,7 +3961,24 @@ public class WifiManager {
})
public void save(@NonNull WifiConfiguration config, @Nullable ActionListener listener) {
if (config == null) throw new IllegalArgumentException("config cannot be null");
- getChannel().sendMessage(SAVE_NETWORK, 0, putListener(listener), config);
+ ActionListenerProxy listenerProxy = null;
+ Binder binder = null;
+ if (listener != null) {
+ listenerProxy = new ActionListenerProxy("save", mLooper, listener);
+ binder = new Binder();
+ }
+ try {
+ IWifiManager iWifiManager = getIWifiManager();
+ if (iWifiManager == null) {
+ throw new RemoteException("Wifi service is not running");
+ }
+ iWifiManager.save(config, binder, listenerProxy,
+ listener == null ? 0 : listener.hashCode());
+ } catch (RemoteException e) {
+ if (listenerProxy != null) listenerProxy.onFailure(ERROR);
+ } catch (SecurityException e) {
+ if (listenerProxy != null) listenerProxy.onFailure(NOT_AUTHORIZED);
+ }
}
/**
@@ -4121,7 +4002,24 @@ public class WifiManager {
})
public void forget(int netId, @Nullable ActionListener listener) {
if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative");
- getChannel().sendMessage(FORGET_NETWORK, netId, putListener(listener));
+ ActionListenerProxy listenerProxy = null;
+ Binder binder = null;
+ if (listener != null) {
+ listenerProxy = new ActionListenerProxy("forget", mLooper, listener);
+ binder = new Binder();
+ }
+ try {
+ IWifiManager iWifiManager = getIWifiManager();
+ if (iWifiManager == null) {
+ throw new RemoteException("Wifi service is not running");
+ }
+ iWifiManager.forget(netId, binder, listenerProxy,
+ listener == null ? 0 : listener.hashCode());
+ } catch (RemoteException e) {
+ if (listenerProxy != null) listenerProxy.onFailure(ERROR);
+ } catch (SecurityException e) {
+ if (listenerProxy != null) listenerProxy.onFailure(NOT_AUTHORIZED);
+ }
}
/**
@@ -4131,6 +4029,7 @@ public class WifiManager {
* @param listener for callbacks on success or failure. Can be null.
* @throws IllegalStateException if the WifiManager instance needs to be
* initialized again
+ * @deprecated This API is deprecated. Use {@link #disableNetwork(int)} instead.
* @hide
*/
@SystemApi
@@ -4139,9 +4038,19 @@ public class WifiManager {
android.Manifest.permission.NETWORK_SETUP_WIZARD,
android.Manifest.permission.NETWORK_STACK
})
+ @Deprecated
public void disable(int netId, @Nullable ActionListener listener) {
if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative");
- getChannel().sendMessage(DISABLE_NETWORK, netId, putListener(listener));
+ // Simple wrapper which forwards the call to disableNetwork. This is a temporary
+ // implementation until we can remove this API completely.
+ boolean status = disableNetwork(netId);
+ if (listener != null) {
+ if (status) {
+ listener.onSuccess();
+ } else {
+ listener.onFailure(ERROR);
+ }
+ }
}
/**
@@ -4197,24 +4106,6 @@ public class WifiManager {
}
/**
- * Get a reference to WifiService handler. This is used by a client to establish
- * an AsyncChannel communication with WifiService
- *
- * @return Messenger pointing to the WifiService handler
- */
- @UnsupportedAppUsage
- private Messenger getWifiServiceMessenger() {
- try {
- IWifiManager iWifiManager = getIWifiManager();
- if (iWifiManager == null) return null;
- return iWifiManager.getWifiServiceMessenger(mContext.getOpPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
-
- /**
* Allows an application to keep the Wi-Fi radio awake.
* Normally the Wi-Fi radio may turn off when the user has not used the device in a while.
* Acquiring a WifiLock will keep the radio on until the lock is released. Multiple
@@ -4674,16 +4565,6 @@ public class WifiManager {
}
}
- protected void finalize() throws Throwable {
- try {
- if (mAsyncChannel != null) {
- mAsyncChannel.disconnect();
- }
- } finally {
- super.finalize();
- }
- }
-
/**
* Set wifi verbose log. Called from developer settings.
* @hide
@@ -5643,4 +5524,90 @@ public class WifiManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Base class for scan results listener. Should be implemented by applications and set when
+ * calling {@link WifiManager#addScanResultsListener(Executor, ScanResultsListener)}.
+ */
+ public interface ScanResultsListener {
+
+ /**
+ * Called when new scan results available.
+ * Caller should use {@link WifiManager#getScanResults()} to get the scan results.
+ */
+ void onScanResultsAvailable();
+ }
+
+ private class ScanResultsListenerProxy extends IScanResultsListener.Stub {
+ private final Executor mExecutor;
+ private final ScanResultsListener mListener;
+
+ ScanResultsListenerProxy(Executor executor, ScanResultsListener listener) {
+ mExecutor = executor;
+ mListener = listener;
+ }
+
+ @Override
+ public void onScanResultsAvailable() {
+ mExecutor.execute(mListener::onScanResultsAvailable);
+ }
+ }
+
+ /**
+ * Add a listener for Scan Results. See {@link ScanResultsListener}.
+ * Caller will receive the event when scan results are available.
+ * Caller should use {@link WifiManager#getScanResults()} to get the scan results.
+ * Caller can remove a previously registered listener using
+ * {@link WifiManager#removeScanResultsListener(ScanResultsListener)}
+ * <p>
+ * Applications should have the
+ * {@link android.Manifest.permission#ACCESS_WIFI_STATE} permission. Callers
+ * without the permission will trigger a {@link java.lang.SecurityException}.
+ * <p>
+ *
+ * @param executor The executor to execute the listener of the {@code listener} object.
+ * @param listener listener for Scan Results events
+ */
+
+ @RequiresPermission(ACCESS_WIFI_STATE)
+ public void addScanResultsListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull ScanResultsListener listener) {
+ if (listener == null) throw new IllegalArgumentException("listener cannot be null");
+ if (executor == null) throw new IllegalArgumentException("executor cannot be null");
+ Log.v(TAG, "addScanResultsListener: listener=" + listener + ", executor=" + executor);
+ try {
+ IWifiManager iWifiManager = getIWifiManager();
+ if (iWifiManager == null) {
+ throw new RemoteException("Wifi service is not running");
+ }
+ iWifiManager.registerScanResultsListener(
+ new Binder(),
+ new ScanResultsListenerProxy(executor, listener),
+ mContext.getOpPackageName().hashCode());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Allow callers to remove a previously added listener. After calling this method,
+ * applications will no longer receive Scan Results events.
+ *
+ * @param listener listener to remove for Scan Results events
+ */
+ @RequiresPermission(ACCESS_WIFI_STATE)
+ public void removeScanResultsListener(@NonNull ScanResultsListener listener) {
+ if (listener == null) throw new IllegalArgumentException("listener cannot be null");
+ Log.v(TAG, "removeScanResultsListener: listener=" + listener);
+
+ try {
+ IWifiManager iWifiManager = getIWifiManager();
+ if (iWifiManager == null) {
+ throw new RemoteException("Wifi service is not running");
+ }
+ iWifiManager.unregisterScanResultsListener(mContext.getOpPackageName().hashCode());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
index 6c2d7ff882d3..ba9dd37398a1 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
@@ -157,7 +157,8 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc
*/
public @NonNull Builder setBssidPattern(
@NonNull MacAddress baseAddress, @NonNull MacAddress mask) {
- checkNotNull(baseAddress, mask);
+ checkNotNull(baseAddress);
+ checkNotNull(mask);
mBssidPatternMatcher = Pair.create(baseAddress, mask);
return this;
}
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index 426201732359..9b529cee58b3 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -23,6 +23,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
import android.net.MacAddress;
+import android.net.wifi.hotspot2.PasspointConfiguration;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
@@ -80,6 +81,10 @@ public final class WifiNetworkSuggestion implements Parcelable {
*/
private @Nullable WifiEnterpriseConfig mWpa3EnterpriseConfig;
/**
+ * The passpoint config for use with Hotspot 2.0 network
+ */
+ private @Nullable PasspointConfiguration mPasspointConfiguration;
+ /**
* This is a network that does not broadcast its SSID, so an
* SSID-specific probe request must be used for scans.
*/
@@ -110,6 +115,7 @@ public final class WifiNetworkSuggestion implements Parcelable {
mWpa3SaePassphrase = null;
mWpa2EnterpriseConfig = null;
mWpa3EnterpriseConfig = null;
+ mPasspointConfiguration = null;
mIsHiddenSSID = false;
mIsAppInteractionRequired = false;
mIsUserInteractionRequired = false;
@@ -234,6 +240,24 @@ public final class WifiNetworkSuggestion implements Parcelable {
}
/**
+ * Set the associated Passpoint configuration for this network. Needed for authenticating
+ * to Hotspot 2.0 networks. See {@link PasspointConfiguration} for description.
+ *
+ * @param passpointConfig Instance of {@link PasspointConfiguration}.
+ * @return Instance of {@link Builder} to enable chaining of the builder method.
+ * @throws IllegalArgumentException if passpoint configuration is invalid.
+ */
+ public @NonNull Builder setPasspointConfig(
+ @NonNull PasspointConfiguration passpointConfig) {
+ checkNotNull(passpointConfig);
+ if (!passpointConfig.validate()) {
+ throw new IllegalArgumentException("Passpoint configuration is invalid");
+ }
+ mPasspointConfiguration = passpointConfig;
+ return this;
+ }
+
+ /**
* Specifies whether this represents a hidden network.
* <p>
* <li>If not set, defaults to false (i.e not a hidden network).</li>
@@ -366,13 +390,24 @@ public final class WifiNetworkSuggestion implements Parcelable {
numSecurityTypes += !TextUtils.isEmpty(mWpa3SaePassphrase) ? 1 : 0;
numSecurityTypes += mWpa2EnterpriseConfig != null ? 1 : 0;
numSecurityTypes += mWpa3EnterpriseConfig != null ? 1 : 0;
+ numSecurityTypes += mPasspointConfiguration != null ? 1 : 0;
if (numSecurityTypes > 1) {
throw new IllegalStateException("only one of setIsEnhancedOpen, setWpa2Passphrase,"
- + "setWpa3Passphrase, setWpa2EnterpriseConfig or setWpa3EnterpriseConfig"
- + " can be invoked for network specifier");
+ + "setWpa3Passphrase, setWpa2EnterpriseConfig, setWpa3EnterpriseConfig"
+ + "or setPasspointConfig can be invoked for network suggestion");
}
}
+ private WifiConfiguration buildWifiConfigurationForPasspoint() {
+ WifiConfiguration wifiConfiguration = new WifiConfiguration();
+ wifiConfiguration.FQDN = mPasspointConfiguration.getHomeSp().getFqdn();
+ wifiConfiguration.priority = mPriority;
+ wifiConfiguration.meteredOverride =
+ mIsMetered ? WifiConfiguration.METERED_OVERRIDE_METERED
+ : WifiConfiguration.METERED_OVERRIDE_NONE;
+ return wifiConfiguration;
+ }
+
/**
* Create a network suggestion object for use in
* {@link WifiManager#addNetworkSuggestions(List)}.
@@ -384,29 +419,36 @@ public final class WifiNetworkSuggestion implements Parcelable {
* </p>
*
* For example:
- * To provide credentials for one open, one WPA2 and one WPA3 network with their
- * corresponding SSID's:
+ * To provide credentials for one open, one WPA2, one WPA3 network with their
+ * corresponding SSID's and one with Passpoint config:
*
* <pre>{@code
* final WifiNetworkSuggestion suggestion1 =
* new Builder()
* .setSsid("test111111")
- * .build()
+ * .build();
* final WifiNetworkSuggestion suggestion2 =
* new Builder()
* .setSsid("test222222")
* .setWpa2Passphrase("test123456")
- * .build()
+ * .build();
* final WifiNetworkSuggestion suggestion3 =
* new Builder()
* .setSsid("test333333")
* .setWpa3Passphrase("test6789")
- * .build()
+ * .build();
+ * final PasspointConfiguration passpointConfig= new PasspointConfiguration();
+ * // configure passpointConfig to include a valid Passpoint configuration
+ * final WifiNetworkSuggestion suggestion4 =
+ * new Builder()
+ * .setPasspointConfig(passpointConfig)
+ * .build();
* final List<WifiNetworkSuggestion> suggestionsList =
* new ArrayList<WifiNetworkSuggestion> { {
* add(suggestion1);
* add(suggestion2);
* add(suggestion3);
+ * add(suggestion4);
* } };
* final WifiManager wifiManager =
* context.getSystemService(Context.WIFI_SERVICE);
@@ -419,21 +461,37 @@ public final class WifiNetworkSuggestion implements Parcelable {
* @see WifiNetworkSuggestion
*/
public @NonNull WifiNetworkSuggestion build() {
- if (mSsid == null) {
- throw new IllegalStateException("setSsid should be invoked for suggestion");
- }
- if (TextUtils.isEmpty(mSsid)) {
- throw new IllegalStateException("invalid ssid for suggestion");
- }
- if (mBssid != null
- && (mBssid.equals(MacAddress.BROADCAST_ADDRESS)
- || mBssid.equals(MacAddress.ALL_ZEROS_ADDRESS))) {
- throw new IllegalStateException("invalid bssid for suggestion");
- }
validateSecurityParams();
+ WifiConfiguration wifiConfiguration;
+ if (mPasspointConfiguration != null) {
+ if (mSsid != null) {
+ throw new IllegalStateException("setSsid should not be invoked for suggestion "
+ + "with Passpoint configuration");
+ }
+ if (mIsHiddenSSID) {
+ throw new IllegalStateException("setIsHiddenSsid should not be invoked for "
+ + "suggestion with Passpoint configuration");
+ }
+ wifiConfiguration = buildWifiConfigurationForPasspoint();
+
+ } else {
+ if (mSsid == null) {
+ throw new IllegalStateException("setSsid should be invoked for suggestion");
+ }
+ if (TextUtils.isEmpty(mSsid)) {
+ throw new IllegalStateException("invalid ssid for suggestion");
+ }
+ if (mBssid != null
+ && (mBssid.equals(MacAddress.BROADCAST_ADDRESS)
+ || mBssid.equals(MacAddress.ALL_ZEROS_ADDRESS))) {
+ throw new IllegalStateException("invalid bssid for suggestion");
+ }
+ wifiConfiguration = buildWifiConfiguration();
+ }
return new WifiNetworkSuggestion(
- buildWifiConfiguration(),
+ wifiConfiguration,
+ mPasspointConfiguration,
mIsAppInteractionRequired,
mIsUserInteractionRequired,
Process.myUid(),
@@ -448,6 +506,12 @@ public final class WifiNetworkSuggestion implements Parcelable {
public final WifiConfiguration wifiConfiguration;
/**
+ * Passpoint configuration for the provided network.
+ * @hide
+ */
+ public final PasspointConfiguration passpointConfiguration;
+
+ /**
* Whether app needs to log in to captive portal to obtain Internet access.
* @hide
*/
@@ -474,6 +538,7 @@ public final class WifiNetworkSuggestion implements Parcelable {
/** @hide */
public WifiNetworkSuggestion() {
this.wifiConfiguration = null;
+ this.passpointConfiguration = null;
this.isAppInteractionRequired = false;
this.isUserInteractionRequired = false;
this.suggestorUid = -1;
@@ -481,14 +546,16 @@ public final class WifiNetworkSuggestion implements Parcelable {
}
/** @hide */
- public WifiNetworkSuggestion(@NonNull WifiConfiguration wifiConfiguration,
+ public WifiNetworkSuggestion(@NonNull WifiConfiguration networkConfiguration,
+ @Nullable PasspointConfiguration passpointConfiguration,
boolean isAppInteractionRequired,
boolean isUserInteractionRequired,
int suggestorUid, @NonNull String suggestorPackageName) {
- checkNotNull(wifiConfiguration);
+ checkNotNull(networkConfiguration);
checkNotNull(suggestorPackageName);
+ this.wifiConfiguration = networkConfiguration;
+ this.passpointConfiguration = passpointConfiguration;
- this.wifiConfiguration = wifiConfiguration;
this.isAppInteractionRequired = isAppInteractionRequired;
this.isUserInteractionRequired = isUserInteractionRequired;
this.suggestorUid = suggestorUid;
@@ -501,6 +568,7 @@ public final class WifiNetworkSuggestion implements Parcelable {
public WifiNetworkSuggestion createFromParcel(Parcel in) {
return new WifiNetworkSuggestion(
in.readParcelable(null), // wifiConfiguration
+ in.readParcelable(null), // PasspointConfiguration
in.readBoolean(), // isAppInteractionRequired
in.readBoolean(), // isUserInteractionRequired
in.readInt(), // suggestorUid
@@ -522,6 +590,7 @@ public final class WifiNetworkSuggestion implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(wifiConfiguration, flags);
+ dest.writeParcelable(passpointConfiguration, flags);
dest.writeBoolean(isAppInteractionRequired);
dest.writeBoolean(isUserInteractionRequired);
dest.writeInt(suggestorUid);
@@ -531,7 +600,8 @@ public final class WifiNetworkSuggestion implements Parcelable {
@Override
public int hashCode() {
return Objects.hash(wifiConfiguration.SSID, wifiConfiguration.BSSID,
- wifiConfiguration.allowedKeyManagement, suggestorUid, suggestorPackageName);
+ wifiConfiguration.allowedKeyManagement, wifiConfiguration.FQDN,
+ suggestorUid, suggestorPackageName);
}
/**
@@ -546,12 +616,17 @@ public final class WifiNetworkSuggestion implements Parcelable {
return false;
}
WifiNetworkSuggestion lhs = (WifiNetworkSuggestion) obj;
- return Objects.equals(this.wifiConfiguration.SSID, lhs.wifiConfiguration.SSID)
- && Objects.equals(this.wifiConfiguration.BSSID, lhs.wifiConfiguration.BSSID)
+ if (this.passpointConfiguration == null ^ lhs.passpointConfiguration == null) {
+ return false;
+ }
+
+ return TextUtils.equals(this.wifiConfiguration.SSID, lhs.wifiConfiguration.SSID)
+ && TextUtils.equals(this.wifiConfiguration.BSSID, lhs.wifiConfiguration.BSSID)
&& Objects.equals(this.wifiConfiguration.allowedKeyManagement,
- lhs.wifiConfiguration.allowedKeyManagement)
- && suggestorUid == lhs.suggestorUid
- && TextUtils.equals(suggestorPackageName, lhs.suggestorPackageName);
+ lhs.wifiConfiguration.allowedKeyManagement)
+ && TextUtils.equals(this.wifiConfiguration.FQDN, lhs.wifiConfiguration.FQDN)
+ && this.suggestorUid == lhs.suggestorUid
+ && TextUtils.equals(this.suggestorPackageName, lhs.suggestorPackageName);
}
@Override
@@ -559,6 +634,7 @@ public final class WifiNetworkSuggestion implements Parcelable {
StringBuilder sb = new StringBuilder("WifiNetworkSuggestion [")
.append(", SSID=").append(wifiConfiguration.SSID)
.append(", BSSID=").append(wifiConfiguration.BSSID)
+ .append(", FQDN=").append(wifiConfiguration.FQDN)
.append(", isAppInteractionRequired=").append(isAppInteractionRequired)
.append(", isUserInteractionRequired=").append(isUserInteractionRequired)
.append(", suggestorUid=").append(suggestorUid)
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 075531ce158e..68948cbbe7a9 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -17,6 +17,7 @@
package android.net.wifi;
import android.Manifest;
+import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
@@ -58,13 +59,23 @@ public class WifiScanner {
/** 5 GHz band excluding DFS channels */
public static final int WIFI_BAND_5_GHZ = 2; /* 5 GHz band without DFS channels */
/** DFS channels from 5 GHz band only */
- public static final int WIFI_BAND_5_GHZ_DFS_ONLY = 4; /* 5 GHz band with DFS channels */
+ public static final int WIFI_BAND_5_GHZ_DFS_ONLY = 4; /* 5 GHz band DFS channels */
+ /**
+ * 2.4Ghz band + DFS channels from 5 GHz band only
+ * @hide
+ */
+ public static final int WIFI_BAND_24_GHZ_WITH_5GHZ_DFS = 5;
/** 5 GHz band including DFS channels */
public static final int WIFI_BAND_5_GHZ_WITH_DFS = 6; /* 5 GHz band with DFS channels */
/** Both 2.4 GHz band and 5 GHz band; no DFS channels */
public static final int WIFI_BAND_BOTH = 3; /* both bands without DFS channels */
/** Both 2.4 GHz band and 5 GHz band; with DFS channels */
public static final int WIFI_BAND_BOTH_WITH_DFS = 7; /* both bands with DFS channels */
+ /**
+ * Max band value
+ * @hide
+ */
+ public static final int WIFI_BAND_MAX = 8;
/** Minimum supported scanning period */
public static final int MIN_SCAN_PERIOD_MS = 1000; /* minimum supported period */
@@ -375,19 +386,27 @@ public class WifiScanner {
*/
private int mBandScanned;
/** all scan results discovered in this scan, sorted by timestamp in ascending order */
- private ScanResult mResults[];
+ private final List<ScanResult> mResults;
- ScanData() {}
+ ScanData() {
+ mResults = new ArrayList<>();
+ }
public ScanData(int id, int flags, ScanResult[] results) {
mId = id;
mFlags = flags;
- mResults = results;
+ mResults = new ArrayList<>(Arrays.asList(results));
}
/** {@hide} */
public ScanData(int id, int flags, int bucketsScanned, int bandScanned,
ScanResult[] results) {
+ this(id, flags, bucketsScanned, bandScanned, new ArrayList<>(Arrays.asList(results)));
+ }
+
+ /** {@hide} */
+ public ScanData(int id, int flags, int bucketsScanned, int bandScanned,
+ List<ScanResult> results) {
mId = id;
mFlags = flags;
mBucketsScanned = bucketsScanned;
@@ -400,11 +419,9 @@ public class WifiScanner {
mFlags = s.mFlags;
mBucketsScanned = s.mBucketsScanned;
mBandScanned = s.mBandScanned;
- mResults = new ScanResult[s.mResults.length];
- for (int i = 0; i < s.mResults.length; i++) {
- ScanResult result = s.mResults[i];
- ScanResult newResult = new ScanResult(result);
- mResults[i] = newResult;
+ mResults = new ArrayList<>();
+ for (ScanResult scanResult : s.mResults) {
+ mResults.add(new ScanResult(scanResult));
}
}
@@ -427,7 +444,14 @@ public class WifiScanner {
}
public ScanResult[] getResults() {
- return mResults;
+ return mResults.toArray(new ScanResult[0]);
+ }
+
+ /** {@hide} */
+ public void addResults(@NonNull ScanResult[] newResults) {
+ for (ScanResult result : newResults) {
+ mResults.add(new ScanResult(result));
+ }
}
/** Implement the Parcelable interface {@hide} */
@@ -437,19 +461,11 @@ public class WifiScanner {
/** Implement the Parcelable interface {@hide} */
public void writeToParcel(Parcel dest, int flags) {
- if (mResults != null) {
- dest.writeInt(mId);
- dest.writeInt(mFlags);
- dest.writeInt(mBucketsScanned);
- dest.writeInt(mBandScanned);
- dest.writeInt(mResults.length);
- for (int i = 0; i < mResults.length; i++) {
- ScanResult result = mResults[i];
- result.writeToParcel(dest, flags);
- }
- } else {
- dest.writeInt(0);
- }
+ dest.writeInt(mId);
+ dest.writeInt(mFlags);
+ dest.writeInt(mBucketsScanned);
+ dest.writeInt(mBandScanned);
+ dest.writeParcelableList(mResults, 0);
}
/** Implement the Parcelable interface {@hide} */
@@ -460,11 +476,8 @@ public class WifiScanner {
int flags = in.readInt();
int bucketsScanned = in.readInt();
int bandScanned = in.readInt();
- int n = in.readInt();
- ScanResult results[] = new ScanResult[n];
- for (int i = 0; i < n; i++) {
- results[i] = ScanResult.CREATOR.createFromParcel(in);
- }
+ List<ScanResult> results = new ArrayList<>();
+ in.readParcelableList(results, ScanResult.class.getClassLoader());
return new ScanData(id, flags, bucketsScanned, bandScanned, results);
}
diff --git a/wifi/java/android/net/wifi/WifiUsabilityStatsEntry.java b/wifi/java/android/net/wifi/WifiUsabilityStatsEntry.java
index e59516485112..8f3635fd2f04 100644
--- a/wifi/java/android/net/wifi/WifiUsabilityStatsEntry.java
+++ b/wifi/java/android/net/wifi/WifiUsabilityStatsEntry.java
@@ -20,8 +20,8 @@ import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.TelephonyManager.NetworkType;
+import android.telephony.Annotation.NetworkType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/wifi/java/android/net/wifi/hotspot2/ConfigParser.java b/wifi/java/android/net/wifi/hotspot2/ConfigParser.java
index e8e873199e17..bb01365d6722 100644
--- a/wifi/java/android/net/wifi/hotspot2/ConfigParser.java
+++ b/wifi/java/android/net/wifi/hotspot2/ConfigParser.java
@@ -182,6 +182,12 @@ public final class ConfigParser {
throw new IOException("Passpoint profile missing credential");
}
+ // Don't allow the installer to make changes to the update identifier. This is an
+ // indicator of an R2 (or newer) network.
+ if (config.getUpdateIdentifier() != Integer.MIN_VALUE) {
+ config.setUpdateIdentifier(Integer.MIN_VALUE);
+ }
+
// Parse CA (Certificate Authority) certificate.
byte[] caCertData = mimeParts.get(TYPE_CA_CERT);
if (caCertData != null) {
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index 557b7c9b05e9..e9aa076798e1 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -65,6 +65,7 @@ public final class PasspointConfiguration implements Parcelable {
* Configurations under HomeSp subtree.
*/
private HomeSp mHomeSp = null;
+
/**
* Set the Home SP (Service Provider) information.
*
@@ -248,7 +249,10 @@ public final class PasspointConfiguration implements Parcelable {
mSubscriptionExpirationTimeInMillis = subscriptionExpirationTimeInMillis;
}
/**
- * @hide
+ * Utility method to get the time this subscription will expire. It is in the format of number
+ * of milliseconds since January 1, 1970, 00:00:00 GMT.
+ *
+ * @return The time this subscription will expire, or Long.MIN_VALUE to indicate unset value
*/
public long getSubscriptionExpirationTimeInMillis() {
return mSubscriptionExpirationTimeInMillis;
@@ -521,6 +525,8 @@ public final class PasspointConfiguration implements Parcelable {
.append("\n");
builder.append("UsageLimitDataLimit: ").append(mUsageLimitDataLimit).append("\n");
builder.append("UsageLimitTimeLimit: ").append(mUsageLimitTimeLimitInMinutes).append("\n");
+ builder.append("Provisioned by a subscription server: ")
+ .append(isOsuProvisioned() ? "Yes" : "No").append("\n");
if (mHomeSp != null) {
builder.append("HomeSP Begin ---\n");
builder.append(mHomeSp);
@@ -728,4 +734,14 @@ public final class PasspointConfiguration implements Parcelable {
}
return true;
}
+
+ /**
+ * Indicates if the Passpoint Configuration was provisioned by a subscription (OSU) server,
+ * which means that it's an R2 (or R3) profile.
+ *
+ * @return true if the Passpoint Configuration was provisioned by a subscription server.
+ */
+ public boolean isOsuProvisioned() {
+ return getUpdateIdentifier() != Integer.MIN_VALUE;
+ }
}
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index 03eb169f60b9..300f1a28416b 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -21,11 +21,15 @@ package com.android.server.wifi;
import android.content.pm.ParceledListSlice;
import android.net.DhcpInfo;
import android.net.Network;
+import android.net.wifi.IActionListener;
import android.net.wifi.IDppCallback;
+import android.net.wifi.ILocalOnlyHotspotCallback;
import android.net.wifi.INetworkRequestMatchCallback;
import android.net.wifi.IOnWifiUsabilityStatsListener;
+import android.net.wifi.IScanResultsListener;
import android.net.wifi.ISoftApCallback;
import android.net.wifi.ITrafficStateCallback;
+import android.net.wifi.ITxPacketCountListener;
import android.net.wifi.IWifiManager;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiActivityEnergyInfo;
@@ -205,11 +209,6 @@ public class BaseWifiService extends IWifiManager.Stub {
}
@Override
- public void setCountryCode(String country) {
- throw new UnsupportedOperationException();
- }
-
- @Override
public String getCountryCode() {
throw new UnsupportedOperationException();
}
@@ -284,22 +283,34 @@ public class BaseWifiService extends IWifiManager.Stub {
throw new UnsupportedOperationException();
}
- @Override
+ /** @deprecated replaced by {@link #startLocalOnlyHotspot(ILocalOnlyHotspotCallback, String)} */
+ @Deprecated
public int startLocalOnlyHotspot(Messenger messenger, IBinder binder, String packageName) {
throw new UnsupportedOperationException();
}
@Override
- public void stopLocalOnlyHotspot() {
+ public int startLocalOnlyHotspot(ILocalOnlyHotspotCallback callback, String packageName) {
throw new UnsupportedOperationException();
}
@Override
+ public void stopLocalOnlyHotspot() {
+ throw new UnsupportedOperationException();
+ }
+
+ /** @deprecated replaced by {@link #startWatchLocalOnlyHotspot(ILocalOnlyHotspotCallback)} */
+ @Deprecated
public void startWatchLocalOnlyHotspot(Messenger messenger, IBinder binder) {
throw new UnsupportedOperationException();
}
@Override
+ public void startWatchLocalOnlyHotspot(ILocalOnlyHotspotCallback callback) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void stopWatchLocalOnlyHotspot() {
throw new UnsupportedOperationException();
}
@@ -325,11 +336,6 @@ public class BaseWifiService extends IWifiManager.Stub {
}
@Override
- public Messenger getWifiServiceMessenger(String packageName) {
- throw new UnsupportedOperationException();
- }
-
- @Override
public void enableTdls(String remoteIPAddress, boolean enable) {
throw new UnsupportedOperationException();
}
@@ -494,6 +500,41 @@ public class BaseWifiService extends IWifiManager.Stub {
}
@Override
+ public void connect(WifiConfiguration config, int netId, IBinder binder,
+ IActionListener callback, int callbackIdentifier) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void save(WifiConfiguration config, IBinder binder, IActionListener callback,
+ int callbackIdentifier) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void forget(int netId, IBinder binder, IActionListener callback,
+ int callbackIdentifier) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void getTxPacketCount(String packageName, IBinder binder,
+ ITxPacketCountListener callback, int callbackIdentifier) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void registerScanResultsListener(
+ IBinder binder, IScanResultsListener listener, int listenerIdentifier) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void unregisterScanResultsListener(int listenerIdentifier) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public boolean isWifiCoverageExtendFeatureEnabled() {
throw new UnsupportedOperationException();
}
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithUpdateIdentifier.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithUpdateIdentifier.base64
new file mode 100644
index 000000000000..f4d2abb08e36
--- /dev/null
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithUpdateIdentifier.base64
@@ -0,0 +1,88 @@
+TUlNRS1WZXJzaW9uOiAxLjAKQ29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5
+PXtib3VuZGFyeX07IGNoYXJzZXQ9VVRGLTgKQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogYmFz
+ZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94LXBhc3Nwb2ludC1w
+cm9maWxlOyBjaGFyc2V0PVVURi04CkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IGJhc2U2NAoK
+UEUxbmJYUlVjbVZsSUhodGJHNXpQU0p6ZVc1amJXdzZaRzFrWkdZeExqSWlQZ29nSUR4V1pYSkVW
+RVErTVM0eVBDOVdaWEpFVkVRKwpDaUFnUEU1dlpHVStDaUFnSUNBOFRtOWtaVTVoYldVK1VHVnlV
+SEp2ZG1sa1pYSlRkV0p6WTNKcGNIUnBiMjQ4TDA1dlpHVk9ZVzFsClBnb2dJQ0FnUEZKVVVISnZj
+R1Z5ZEdsbGN6NEtJQ0FnSUNBZ1BGUjVjR1UrQ2lBZ0lDQWdJQ0FnUEVSRVJrNWhiV1UrZFhKdU9u
+ZG0KWVRwdGJ6cG9iM1J6Y0c5ME1tUnZkREF0Y0dWeWNISnZkbWxrWlhKemRXSnpZM0pwY0hScGIy
+NDZNUzR3UEM5RVJFWk9ZVzFsUGdvZwpJQ0FnSUNBOEwxUjVjR1UrQ2lBZ0lDQThMMUpVVUhKdmNH
+VnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVStWWEJrWVhSbFNX
+UmxiblJwWm1sbGNqd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lEeFdZV3gxWlQ0eE1qTTBQQzlXWVd4
+MVpUNEsKSUNBZ0lEd3ZUbTlrWlQ0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoYldV
+K2FUQXdNVHd2VG05a1pVNWhiV1UrQ2lBZwpJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJS
+bFRtRnRaVDVJYjIxbFUxQThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJRHhPCmIyUmxQZ29nSUNB
+Z0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdFpUd3ZUbTlrWlU1aGJXVStDaUFn
+SUNBZ0lDQWcKSUNBOFZtRnNkV1UrUTJWdWRIVnllU0JJYjNWelpUd3ZWbUZzZFdVK0NpQWdJQ0Fn
+SUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUR4TwpiMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZP
+WVcxbFBrWlJSRTQ4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsClBtMXBOaTVq
+Ynk1MWF6d3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxQ
+Z29nSUNBZ0lDQWcKSUNBZ1BFNXZaR1ZPWVcxbFBsSnZZVzFwYm1kRGIyNXpiM0owYVhWdFQwazhM
+MDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEZaaApiSFZsUGpFeE1qSXpNeXcwTkRVMU5qWThM
+MVpoYkhWbFBnb2dJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWdJQ0FnUEM5T2IyUmxQZ29nCklDQWdJ
+Q0E4VG05a1pUNEtJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStRM0psWkdWdWRHbGhiRHd2VG05a1pV
+NWhiV1UrQ2lBZ0lDQWcKSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1Vt
+VmhiRzA4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZwpQRlpoYkhWbFBuTm9ZV3RsYmk1emRH
+bHljbVZrTG1OdmJUd3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnCklE
+eE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsVnpaWEp1WVcxbFVHRnpjM2R2Y21R
+OEwwNXZaR1ZPWVcxbFBnb2cKSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4
+T2IyUmxUbUZ0WlQ1VmMyVnlibUZ0WlR3dlRtOWtaVTVoYldVKwpDaUFnSUNBZ0lDQWdJQ0FnSUR4
+V1lXeDFaVDVxWVcxbGN6d3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0Fn
+CklDQWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxCaGMzTjNiM0pr
+UEM5T2IyUmxUbUZ0WlQ0S0lDQWcKSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbGx0T1hWYVJFRjNUbmM5
+UFR3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNBOEwwNXZaR1UrQ2lBZwpJQ0FnSUNBZ0lDQThUbTlr
+WlQ0S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQa1ZCVUUxbGRHaHZaRHd2VG05a1pVNWhi
+V1UrCkNpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0FnSUR4T2IyUmxU
+bUZ0WlQ1RlFWQlVlWEJsUEM5T2IyUmwKVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBOFZtRnNk
+V1UrTWpFOEwxWmhiSFZsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThMMDV2WkdVKwpDaUFnSUNBZ0lDQWdJ
+Q0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE9iMlJsVG1GdFpUNUpibTVsY2sxbGRH
+aHZaRHd2ClRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQazFUTFVOSVFW
+QXRWakk4TDFaaGJIVmxQZ29nSUNBZ0lDQWcKSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lD
+QThMMDV2WkdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUR4TwpiMlJsUGdvZ0lD
+QWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrUnBaMmwwWVd4RFpYSjBhV1pwWTJGMFpUd3ZUbTlrWlU1
+aGJXVStDaUFnCklDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcx
+bFBrTmxjblJwWm1sallYUmxWSGx3WlR3dlRtOWsKWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4
+V1lXeDFaVDU0TlRBNWRqTThMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbApQZ29nSUNB
+Z0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmxUbUZ0WlQ1RFpYSjBVMGhC
+TWpVMlJtbHVaMlZ5CmNISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZz
+ZFdVK01XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXgKWmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4
+WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaand2Vm1Gc2RXVStDaUFnSUNBZwpJQ0Fn
+SUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFBnb2dJ
+Q0FnSUNBZ0lDQWdQRTV2ClpHVk9ZVzFsUGxOSlRUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJ
+Q0E4VG05a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk8KWVcxbFBrbE5VMGs4TDA1dlpHVk9Z
+VzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdVK2FXMXphVHd2Vm1Gc2RXVStDaUFnSUNBZwpJ
+Q0FnSUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BF
+NXZaR1ZPWVcxbFBrVkJVRlI1CmNHVThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBOFZt
+RnNkV1UrTWpROEwxWmhiSFZsUGdvZ0lDQWdJQ0FnSUNBZ1BDOU8KYjJSbFBnb2dJQ0FnSUNBZ0lE
+d3ZUbTlrWlQ0S0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ1BDOU9iMlJsUGdvZ0lEd3ZUbTlrWlQ0
+SwpQQzlOWjIxMFZISmxaVDRLCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlv
+bi94LXg1MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRM
+UzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlow
+bEtRVWxNYkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQw
+Sm5UbFlLUWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYy
+aGpUazFxV1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZr
+Rm5VVEJGZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtO
+blMwTkJVVVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIx
+V1ZYTldkVzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJO
+Q01TdHZSMWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpV
+ClJVRkRaV1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwy
+UzJoU1FqZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dy
+VUdKck1IVjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIz
+UWtzM2FFUTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNL
+MGxhCmFYQllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFV
+RzVXTTJreGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNX
+VVZHU1hkWU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJO
+MDFFYlVGR1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RW
+SkJkMFJuCldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldV
+UldVakJVUWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNX
+aDJZMDVCVVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQw
+aEZDamxIUlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5
+VVNYZEpWV00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5K
+T1EwOTBhWE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhs
+T2JXMVdUQW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhn
+MmVFMXVWR3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxX
+YUhwaFNFb3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5
+WnpGS1VDdHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQ
+TDNSa1RrNTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJK
+UmtsRFFWUkZMUzB0TFMwSwotLXtib3VuZGFyeX0tLQo= \ No newline at end of file
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index ba9fc786afe7..6d7e621a9bc2 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -19,7 +19,6 @@ package android.net.wifi;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import android.net.MacAddress;
@@ -62,7 +61,8 @@ public class WifiConfigurationTest {
config.updateIdentifier = "1234";
config.fromWifiNetworkSpecifier = true;
config.fromWifiNetworkSuggestion = true;
- MacAddress macBeforeParcel = config.getOrCreateRandomizedMacAddress();
+ config.setRandomizedMacAddress(MacAddress.createRandomUnicastAddress());
+ MacAddress macBeforeParcel = config.getRandomizedMacAddress();
Parcel parcelW = Parcel.obtain();
config.writeToParcel(parcelW, 0);
byte[] bytes = parcelW.marshall();
@@ -75,7 +75,7 @@ public class WifiConfigurationTest {
// lacking a useful config.equals, check two fields near the end.
assertEquals(cookie, reconfig.getMoTree());
- assertEquals(macBeforeParcel, reconfig.getOrCreateRandomizedMacAddress());
+ assertEquals(macBeforeParcel, reconfig.getRandomizedMacAddress());
assertEquals(config.updateIdentifier, reconfig.updateIdentifier);
assertFalse(reconfig.trusted);
assertTrue(config.fromWifiNetworkSpecifier);
@@ -193,19 +193,6 @@ public class WifiConfigurationTest {
}
@Test
- public void testGetOrCreateRandomizedMacAddress_SavesAndReturnsSameAddress() {
- WifiConfiguration config = new WifiConfiguration();
- MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
- assertEquals(defaultMac, config.getRandomizedMacAddress());
-
- MacAddress firstMacAddress = config.getOrCreateRandomizedMacAddress();
- MacAddress secondMacAddress = config.getOrCreateRandomizedMacAddress();
-
- assertNotEquals(defaultMac, firstMacAddress);
- assertEquals(firstMacAddress, secondMacAddress);
- }
-
- @Test
public void testSetRandomizedMacAddress_ChangesSavedAddress() {
WifiConfiguration config = new WifiConfiguration();
MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
@@ -219,36 +206,6 @@ public class WifiConfigurationTest {
}
@Test
- public void testGetOrCreateRandomizedMacAddress_ReRandomizesInvalidAddress() {
- WifiConfiguration config = new WifiConfiguration();
-
- MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
- MacAddress macAddressZeroes = MacAddress.ALL_ZEROS_ADDRESS;
- MacAddress macAddressMulticast = MacAddress.fromString("03:ff:ff:ff:ff:ff");
- MacAddress macAddressGlobal = MacAddress.fromString("fc:ff:ff:ff:ff:ff");
-
- config.setRandomizedMacAddress(null);
- MacAddress macAfterChange = config.getOrCreateRandomizedMacAddress();
- assertNotEquals(macAfterChange, null);
-
- config.setRandomizedMacAddress(defaultMac);
- macAfterChange = config.getOrCreateRandomizedMacAddress();
- assertNotEquals(macAfterChange, defaultMac);
-
- config.setRandomizedMacAddress(macAddressZeroes);
- macAfterChange = config.getOrCreateRandomizedMacAddress();
- assertNotEquals(macAfterChange, macAddressZeroes);
-
- config.setRandomizedMacAddress(macAddressMulticast);
- macAfterChange = config.getOrCreateRandomizedMacAddress();
- assertNotEquals(macAfterChange, macAddressMulticast);
-
- config.setRandomizedMacAddress(macAddressGlobal);
- macAfterChange = config.getOrCreateRandomizedMacAddress();
- assertNotEquals(macAfterChange, macAddressGlobal);
- }
-
- @Test
public void testSetRandomizedMacAddress_DoesNothingWhenNull() {
WifiConfiguration config = new WifiConfiguration();
MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
diff --git a/wifi/tests/src/android/net/wifi/WifiInfoTest.java b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
index 0ce5d66cf4f7..ea08ea8e8333 100644
--- a/wifi/tests/src/android/net/wifi/WifiInfoTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
@@ -38,6 +38,7 @@ public class WifiInfoTest {
private static final String TEST_PACKAGE_NAME = "com.test.example";
private static final String TEST_FQDN = "test.com";
private static final String TEST_PROVIDER_NAME = "test";
+ private static final int TEST_WIFI_TECHNOLOGY = WifiInfo.WIFI_TECHNOLOGY_11AC;
/**
* Verify parcel write/read with WifiInfo.
@@ -54,6 +55,7 @@ public class WifiInfoTest {
writeWifiInfo.setFQDN(TEST_FQDN);
writeWifiInfo.setProviderFriendlyName(TEST_PROVIDER_NAME);
writeWifiInfo.setNetworkSuggestionOrSpecifierPackageName(TEST_PACKAGE_NAME);
+ writeWifiInfo.setWifiTechnology(TEST_WIFI_TECHNOLOGY);
Parcel parcel = Parcel.obtain();
writeWifiInfo.writeToParcel(parcel, 0);
@@ -72,5 +74,6 @@ public class WifiInfoTest {
assertEquals(TEST_PACKAGE_NAME, readWifiInfo.getNetworkSuggestionOrSpecifierPackageName());
assertEquals(TEST_FQDN, readWifiInfo.getPasspointFqdn());
assertEquals(TEST_PROVIDER_NAME, readWifiInfo.getPasspointProviderFriendlyName());
+ assertEquals(TEST_WIFI_TECHNOLOGY, readWifiInfo.getWifiTechnology());
}
}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index e478f3830c0a..f8a0c8f5fab8 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -16,9 +16,6 @@
package android.net.wifi;
-import static android.net.wifi.WifiManager.HOTSPOT_FAILED;
-import static android.net.wifi.WifiManager.HOTSPOT_STARTED;
-import static android.net.wifi.WifiManager.HOTSPOT_STOPPED;
import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC;
import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE;
import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_NO_CHANNEL;
@@ -62,13 +59,14 @@ import android.net.wifi.WifiManager.LocalOnlyHotspotSubscription;
import android.net.wifi.WifiManager.NetworkRequestMatchCallback;
import android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback;
import android.net.wifi.WifiManager.OnWifiUsabilityStatsListener;
+import android.net.wifi.WifiManager.ScanResultsListener;
import android.net.wifi.WifiManager.SoftApCallback;
import android.net.wifi.WifiManager.TrafficStateCallback;
+import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
+import android.os.RemoteException;
import android.os.test.TestLooper;
import androidx.test.filters.SmallTest;
@@ -105,18 +103,17 @@ public class WifiManagerTest {
android.net.wifi.IWifiManager mWifiService;
@Mock ApplicationInfo mApplicationInfo;
@Mock WifiConfiguration mApConfig;
- @Mock IBinder mAppBinder;
@Mock SoftApCallback mSoftApCallback;
@Mock TrafficStateCallback mTrafficStateCallback;
@Mock NetworkRequestMatchCallback mNetworkRequestMatchCallback;
@Mock OnWifiUsabilityStatsListener mOnWifiUsabilityStatsListener;
+ @Mock ScanResultsListener mScanResultListener;
+ @Mock Executor mCallbackExecutor;
+ @Mock Executor mExecutor;
- private Executor mExecutor;
private Handler mHandler;
private TestLooper mLooper;
private WifiManager mWifiManager;
- private Messenger mWifiServiceMessenger;
- final ArgumentCaptor<Messenger> mMessengerCaptor = ArgumentCaptor.forClass(Messenger.class);
@Before public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -126,7 +123,6 @@ public class WifiManagerTest {
when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo);
when(mContext.getOpPackageName()).thenReturn(TEST_PACKAGE_NAME);
- mWifiServiceMessenger = new Messenger(mHandler);
mWifiManager = new WifiManager(mContext, mWifiService, mLooper.getLooper());
verify(mWifiService).getVerboseLoggingLevel();
}
@@ -176,7 +172,7 @@ public class WifiManagerTest {
@Test
public void testCreationAndCloseOfLocalOnlyHotspotReservation() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
- when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class),
+ when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
anyString())).thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
@@ -194,7 +190,7 @@ public class WifiManagerTest {
public void testLocalOnlyHotspotReservationCallsStopProperlyInTryWithResources()
throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
- when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class),
+ when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
anyString())).thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
@@ -355,7 +351,7 @@ public class WifiManagerTest {
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
verify(mWifiService)
- .startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class), anyString());
+ .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString());
}
/**
@@ -366,7 +362,7 @@ public class WifiManagerTest {
public void testStartLocalOnlyHotspotThrowsSecurityException() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
doThrow(new SecurityException()).when(mWifiService)
- .startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class), anyString());
+ .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString());
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
}
@@ -378,7 +374,7 @@ public class WifiManagerTest {
public void testStartLocalOnlyHotspotThrowsIllegalStateException() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
doThrow(new IllegalStateException()).when(mWifiService)
- .startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class), anyString());
+ .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString());
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
}
@@ -388,12 +384,13 @@ public class WifiManagerTest {
@Test
public void testCorrectLooperIsUsedForHandler() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
- when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class),
+ when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
anyString())).thenReturn(ERROR_INCOMPATIBLE_MODE);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mLooper.dispatchAll();
assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason);
verify(mContext, never()).getMainLooper();
+ verify(mContext, never()).getMainExecutor();
}
/**
@@ -404,15 +401,15 @@ public class WifiManagerTest {
public void testMainLooperIsUsedWhenHandlerNotProvided() throws Exception {
// record thread from looper.getThread and check ids.
TestLooper altLooper = new TestLooper();
- when(mContext.getMainLooper()).thenReturn(altLooper.getLooper());
+ when(mContext.getMainExecutor()).thenReturn(altLooper.getNewExecutor());
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
- when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class),
+ when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
anyString())).thenReturn(ERROR_INCOMPATIBLE_MODE);
mWifiManager.startLocalOnlyHotspot(callback, null);
altLooper.dispatchAll();
assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason);
assertEquals(altLooper.getLooper().getThread().getId(), callback.mCallingThreadId);
- verify(mContext).getMainLooper();
+ verify(mContext).getMainExecutor();
}
/**
@@ -424,18 +421,17 @@ public class WifiManagerTest {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
TestLooper callbackLooper = new TestLooper();
Handler callbackHandler = new Handler(callbackLooper.getLooper());
- when(mWifiService.startLocalOnlyHotspot(mMessengerCaptor.capture(),
- any(IBinder.class), anyString())).thenReturn(REQUEST_REGISTERED);
+ ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback =
+ ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class);
+ when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString()))
+ .thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, callbackHandler);
callbackLooper.dispatchAll();
mLooper.dispatchAll();
assertFalse(callback.mOnStartedCalled);
assertEquals(null, callback.mRes);
// now trigger the callback
- Message msg = new Message();
- msg.what = HOTSPOT_STARTED;
- msg.obj = mApConfig;
- mMessengerCaptor.getValue().send(msg);
+ internalCallback.getValue().onHotspotStarted(mApConfig);
mLooper.dispatchAll();
callbackLooper.dispatchAll();
assertTrue(callback.mOnStartedCalled);
@@ -451,17 +447,17 @@ public class WifiManagerTest {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
TestLooper callbackLooper = new TestLooper();
Handler callbackHandler = new Handler(callbackLooper.getLooper());
- when(mWifiService.startLocalOnlyHotspot(mMessengerCaptor.capture(),
- any(IBinder.class), anyString())).thenReturn(REQUEST_REGISTERED);
+ ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback =
+ ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class);
+ when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString()))
+ .thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, callbackHandler);
callbackLooper.dispatchAll();
mLooper.dispatchAll();
assertFalse(callback.mOnStartedCalled);
assertEquals(null, callback.mRes);
// now trigger the callback
- Message msg = new Message();
- msg.what = HOTSPOT_STARTED;
- mMessengerCaptor.getValue().send(msg);
+ internalCallback.getValue().onHotspotStarted(null);
mLooper.dispatchAll();
callbackLooper.dispatchAll();
assertFalse(callback.mOnStartedCalled);
@@ -476,16 +472,16 @@ public class WifiManagerTest {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
TestLooper callbackLooper = new TestLooper();
Handler callbackHandler = new Handler(callbackLooper.getLooper());
- when(mWifiService.startLocalOnlyHotspot(mMessengerCaptor.capture(),
- any(IBinder.class), anyString())).thenReturn(REQUEST_REGISTERED);
+ ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback =
+ ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class);
+ when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString()))
+ .thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, callbackHandler);
callbackLooper.dispatchAll();
mLooper.dispatchAll();
assertFalse(callback.mOnStoppedCalled);
// now trigger the callback
- Message msg = new Message();
- msg.what = HOTSPOT_STOPPED;
- mMessengerCaptor.getValue().send(msg);
+ internalCallback.getValue().onHotspotStopped();
mLooper.dispatchAll();
callbackLooper.dispatchAll();
assertTrue(callback.mOnStoppedCalled);
@@ -499,17 +495,16 @@ public class WifiManagerTest {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
TestLooper callbackLooper = new TestLooper();
Handler callbackHandler = new Handler(callbackLooper.getLooper());
- when(mWifiService.startLocalOnlyHotspot(mMessengerCaptor.capture(),
- any(IBinder.class), anyString())).thenReturn(REQUEST_REGISTERED);
+ ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback =
+ ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class);
+ when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString()))
+ .thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, callbackHandler);
callbackLooper.dispatchAll();
mLooper.dispatchAll();
assertEquals(ERROR_NOT_SET, callback.mFailureReason);
// now trigger the callback
- Message msg = new Message();
- msg.what = HOTSPOT_FAILED;
- msg.arg1 = ERROR_NO_CHANNEL;
- mMessengerCaptor.getValue().send(msg);
+ internalCallback.getValue().onHotspotFailed(ERROR_NO_CHANNEL);
mLooper.dispatchAll();
callbackLooper.dispatchAll();
assertEquals(ERROR_NO_CHANNEL, callback.mFailureReason);
@@ -521,7 +516,7 @@ public class WifiManagerTest {
@Test
public void testLocalOnlyHotspotCallbackFullOnIncompatibleMode() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
- when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class),
+ when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
anyString())).thenReturn(ERROR_INCOMPATIBLE_MODE);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mLooper.dispatchAll();
@@ -537,7 +532,7 @@ public class WifiManagerTest {
@Test
public void testLocalOnlyHotspotCallbackFullOnTetheringDisallowed() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
- when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class),
+ when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
anyString())).thenReturn(ERROR_TETHERING_DISALLOWED);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mLooper.dispatchAll();
@@ -555,7 +550,7 @@ public class WifiManagerTest {
public void testLocalOnlyHotspotCallbackFullOnSecurityException() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
doThrow(new SecurityException()).when(mWifiService)
- .startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class), anyString());
+ .startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString());
try {
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
} catch (SecurityException e) {
@@ -575,7 +570,7 @@ public class WifiManagerTest {
@Test
public void testLocalOnlyHotspotCallbackFullOnNoChannelError() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
- when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class),
+ when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
anyString())).thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mLooper.dispatchAll();
@@ -591,7 +586,7 @@ public class WifiManagerTest {
@Test
public void testCancelLocalOnlyHotspotRequestCallsStopOnWifiService() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
- when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class),
+ when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
anyString())).thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mWifiManager.cancelLocalOnlyHotspotRequest();
@@ -613,7 +608,7 @@ public class WifiManagerTest {
@Test
public void testCallbackAfterLocalOnlyHotspotWasCancelled() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
- when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class),
+ when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
anyString())).thenReturn(REQUEST_REGISTERED);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mWifiManager.cancelLocalOnlyHotspotRequest();
@@ -632,7 +627,7 @@ public class WifiManagerTest {
@Test
public void testCancelAfterLocalOnlyHotspotCallbackTriggered() throws Exception {
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
- when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class),
+ when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class),
anyString())).thenReturn(ERROR_INCOMPATIBLE_MODE);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mLooper.dispatchAll();
@@ -652,7 +647,7 @@ public class WifiManagerTest {
TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver();
mWifiManager.watchLocalOnlyHotspot(observer, mHandler);
- verify(mWifiService).startWatchLocalOnlyHotspot(any(Messenger.class), any(IBinder.class));
+ verify(mWifiService).startWatchLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class));
}
/**
@@ -663,7 +658,7 @@ public class WifiManagerTest {
public void testStartWatchLocalOnlyHotspotThrowsSecurityException() throws Exception {
TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver();
doThrow(new SecurityException()).when(mWifiService)
- .startWatchLocalOnlyHotspot(any(Messenger.class), any(IBinder.class));
+ .startWatchLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class));
mWifiManager.watchLocalOnlyHotspot(observer, mHandler);
}
@@ -675,7 +670,7 @@ public class WifiManagerTest {
public void testStartWatchLocalOnlyHotspotThrowsIllegalStateException() throws Exception {
TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver();
doThrow(new IllegalStateException()).when(mWifiService)
- .startWatchLocalOnlyHotspot(any(Messenger.class), any(IBinder.class));
+ .startWatchLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class));
mWifiManager.watchLocalOnlyHotspot(observer, mHandler);
}
@@ -820,6 +815,7 @@ public class WifiManagerTest {
verify(mWifiService).registerSoftApCallback(any(IBinder.class),
any(ISoftApCallback.Stub.class), anyInt());
verify(mContext, never()).getMainLooper();
+ verify(mContext, never()).getMainExecutor();
}
/**
@@ -832,6 +828,7 @@ public class WifiManagerTest {
mLooper.dispatchAll();
assertTrue(observer.mOnRegistered);
verify(mContext, never()).getMainLooper();
+ verify(mContext, never()).getMainExecutor();
}
/**
@@ -842,13 +839,13 @@ public class WifiManagerTest {
public void testMainLooperIsUsedWhenHandlerNotProvidedForObserver() throws Exception {
// record thread from looper.getThread and check ids.
TestLooper altLooper = new TestLooper();
- when(mContext.getMainLooper()).thenReturn(altLooper.getLooper());
+ when(mContext.getMainExecutor()).thenReturn(altLooper.getNewExecutor());
TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver();
mWifiManager.watchLocalOnlyHotspot(observer, null);
altLooper.dispatchAll();
assertTrue(observer.mOnRegistered);
assertEquals(altLooper.getLooper().getThread().getId(), observer.mCallingThreadId);
- verify(mContext).getMainLooper();
+ verify(mContext).getMainExecutor();
}
/**
@@ -863,8 +860,7 @@ public class WifiManagerTest {
assertFalse(observer.mOnRegistered);
assertEquals(null, observer.mSub);
mWifiManager.watchLocalOnlyHotspot(observer, observerHandler);
- verify(mWifiService).startWatchLocalOnlyHotspot(mMessengerCaptor.capture(),
- any(IBinder.class));
+ verify(mWifiService).startWatchLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class));
// now trigger the callback
observerLooper.dispatchAll();
mLooper.dispatchAll();
@@ -882,16 +878,14 @@ public class WifiManagerTest {
TestLooper observerLooper = new TestLooper();
Handler observerHandler = new Handler(observerLooper.getLooper());
mWifiManager.watchLocalOnlyHotspot(observer, observerHandler);
- verify(mWifiService).startWatchLocalOnlyHotspot(mMessengerCaptor.capture(),
- any(IBinder.class));
+ ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback =
+ ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class);
+ verify(mWifiService).startWatchLocalOnlyHotspot(internalCallback.capture());
observerLooper.dispatchAll();
mLooper.dispatchAll();
assertFalse(observer.mOnStartedCalled);
// now trigger the callback
- Message msg = new Message();
- msg.what = HOTSPOT_STARTED;
- msg.obj = mApConfig;
- mMessengerCaptor.getValue().send(msg);
+ internalCallback.getValue().onHotspotStarted(mApConfig);
mLooper.dispatchAll();
observerLooper.dispatchAll();
assertTrue(observer.mOnStartedCalled);
@@ -908,15 +902,14 @@ public class WifiManagerTest {
TestLooper observerLooper = new TestLooper();
Handler observerHandler = new Handler(observerLooper.getLooper());
mWifiManager.watchLocalOnlyHotspot(observer, observerHandler);
- verify(mWifiService).startWatchLocalOnlyHotspot(mMessengerCaptor.capture(),
- any(IBinder.class));
+ ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback =
+ ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class);
+ verify(mWifiService).startWatchLocalOnlyHotspot(internalCallback.capture());
observerLooper.dispatchAll();
mLooper.dispatchAll();
assertFalse(observer.mOnStartedCalled);
// now trigger the callback
- Message msg = new Message();
- msg.what = HOTSPOT_STARTED;
- mMessengerCaptor.getValue().send(msg);
+ internalCallback.getValue().onHotspotStarted(null);
mLooper.dispatchAll();
observerLooper.dispatchAll();
assertFalse(observer.mOnStartedCalled);
@@ -934,15 +927,14 @@ public class WifiManagerTest {
TestLooper observerLooper = new TestLooper();
Handler observerHandler = new Handler(observerLooper.getLooper());
mWifiManager.watchLocalOnlyHotspot(observer, observerHandler);
- verify(mWifiService).startWatchLocalOnlyHotspot(mMessengerCaptor.capture(),
- any(IBinder.class));
+ ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback =
+ ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class);
+ verify(mWifiService).startWatchLocalOnlyHotspot(internalCallback.capture());
observerLooper.dispatchAll();
mLooper.dispatchAll();
assertFalse(observer.mOnStoppedCalled);
// now trigger the callback
- Message msg = new Message();
- msg.what = HOTSPOT_STOPPED;
- mMessengerCaptor.getValue().send(msg);
+ internalCallback.getValue().onHotspotStopped();
mLooper.dispatchAll();
observerLooper.dispatchAll();
assertTrue(observer.mOnStoppedCalled);
@@ -971,25 +963,6 @@ public class WifiManagerTest {
}
/**
- * Verify that calls WifiServiceImpl to set country code when no exception happens.
- */
- @Test
- public void testSetWifiCountryCode() throws Exception {
- mWifiManager.setCountryCode(TEST_COUNTRY_CODE);
- verify(mWifiService).setCountryCode(TEST_COUNTRY_CODE);
- }
-
- /**
- * Verify that WifiManager.setCountryCode() rethrows exceptions if caller does not
- * have necessary permissions.
- */
- @Test(expected = SecurityException.class)
- public void testSetWifiCountryCodeFailedOnSecurityException() throws Exception {
- doThrow(new SecurityException()).when(mWifiService).setCountryCode(anyString());
- mWifiManager.setCountryCode(TEST_COUNTRY_CODE);
- }
-
- /**
* Test that calls to get the current WPS config token return null and do not have any
* interactions with WifiServiceImpl.
*/
@@ -1190,6 +1163,7 @@ i * Verify that a call to cancel WPS immediately returns a failure.
Handler altHandler = new Handler(altLooper.getLooper());
mWifiManager.registerTrafficStateCallback(mTrafficStateCallback, altHandler);
verify(mContext, never()).getMainLooper();
+ verify(mContext, never()).getMainExecutor();
verify(mWifiService).registerTrafficStateCallback(
any(IBinder.class), callbackCaptor.capture(), anyInt());
@@ -1646,4 +1620,159 @@ i * Verify that a call to cancel WPS immediately returns a failure.
assertTrue(mWifiManager.setWifiEnabled(false));
verify(mWifiService).setWifiEnabled(mContext.getOpPackageName(), false);
}
+
+ /**
+ * Test behavior of {@link WifiManager#connect(int, WifiManager.ActionListener)}
+ */
+ @Test
+ public void testConnectWithListener() throws Exception {
+ WifiManager.ActionListener externalListener = mock(WifiManager.ActionListener.class);
+ mWifiManager.connect(TEST_NETWORK_ID, externalListener);
+
+ ArgumentCaptor<IActionListener> binderListenerCaptor =
+ ArgumentCaptor.forClass(IActionListener.class);
+ verify(mWifiService).connect(eq(null), eq(TEST_NETWORK_ID), any(Binder.class),
+ binderListenerCaptor.capture(), anyInt());
+ assertNotNull(binderListenerCaptor.getValue());
+
+ // Trigger on success.
+ binderListenerCaptor.getValue().onSuccess();
+ mLooper.dispatchAll();
+ verify(externalListener).onSuccess();
+
+ // Trigger on failure.
+ binderListenerCaptor.getValue().onFailure(WifiManager.BUSY);
+ mLooper.dispatchAll();
+ verify(externalListener).onFailure(WifiManager.BUSY);
+ }
+
+ /**
+ * Test behavior of {@link WifiManager#connect(int, WifiManager.ActionListener)}
+ */
+ @Test
+ public void testConnectWithListenerHandleSecurityException() throws Exception {
+ doThrow(new SecurityException()).when(mWifiService)
+ .connect(eq(null), anyInt(), any(IBinder.class),
+ any(IActionListener.class), anyInt());
+ WifiManager.ActionListener externalListener = mock(WifiManager.ActionListener.class);
+ mWifiManager.connect(TEST_NETWORK_ID, externalListener);
+
+ mLooper.dispatchAll();
+ verify(externalListener).onFailure(WifiManager.NOT_AUTHORIZED);
+ }
+
+ /**
+ * Test behavior of {@link WifiManager#connect(int, WifiManager.ActionListener)}
+ */
+ @Test
+ public void testConnectWithListenerHandleRemoteException() throws Exception {
+ doThrow(new RemoteException()).when(mWifiService)
+ .connect(eq(null), anyInt(), any(IBinder.class),
+ any(IActionListener.class), anyInt());
+ WifiManager.ActionListener externalListener = mock(WifiManager.ActionListener.class);
+ mWifiManager.connect(TEST_NETWORK_ID, externalListener);
+
+ mLooper.dispatchAll();
+ verify(externalListener).onFailure(WifiManager.ERROR);
+ }
+
+ /**
+ * Test behavior of {@link WifiManager#connect(int, WifiManager.ActionListener)}
+ */
+ @Test
+ public void testConnectWithoutListener() throws Exception {
+ WifiConfiguration configuration = new WifiConfiguration();
+ mWifiManager.connect(configuration, null);
+
+ verify(mWifiService).connect(configuration, WifiConfiguration.INVALID_NETWORK_ID, null,
+ null, 0);
+ }
+
+ /**
+ * Test behavior of {@link WifiManager#getTxPacketCount(WifiManager.TxPacketCountListener)}
+ */
+ @Test
+ public void testGetTxPacketCount() throws Exception {
+ WifiManager.TxPacketCountListener externalListener =
+ mock(WifiManager.TxPacketCountListener.class);
+ mWifiManager.getTxPacketCount(externalListener);
+
+ ArgumentCaptor<ITxPacketCountListener> binderListenerCaptor =
+ ArgumentCaptor.forClass(ITxPacketCountListener.class);
+ verify(mWifiService).getTxPacketCount(anyString(), any(Binder.class),
+ binderListenerCaptor.capture(), anyInt());
+ assertNotNull(binderListenerCaptor.getValue());
+
+ // Trigger on success.
+ binderListenerCaptor.getValue().onSuccess(6);
+ mLooper.dispatchAll();
+ verify(externalListener).onSuccess(6);
+
+ // Trigger on failure.
+ binderListenerCaptor.getValue().onFailure(WifiManager.BUSY);
+ mLooper.dispatchAll();
+ verify(externalListener).onFailure(WifiManager.BUSY);
+ }
+
+ /**
+ * Verify an IllegalArgumentException is thrown if listener is not provided.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testAddScanResultsListenerWithNullListener() throws Exception {
+ mWifiManager.addScanResultsListener(mCallbackExecutor, null);
+ }
+
+ /**
+ * Verify an IllegalArgumentException is thrown if executor is not provided.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testAddScanResultsListenerWithNullExecutor() throws Exception {
+ mWifiManager.addScanResultsListener(null, mScanResultListener);
+ }
+
+ /**
+ * Verify client provided listener is being called to the right listener.
+ */
+ @Test
+ public void testAddScanResultsListenerAndReceiveEvent() throws Exception {
+ ArgumentCaptor<IScanResultsListener.Stub> callbackCaptor =
+ ArgumentCaptor.forClass(IScanResultsListener.Stub.class);
+ Executor executor = new SynchronousExecutor();
+ mWifiManager.addScanResultsListener(executor, mScanResultListener);
+ verify(mWifiService).registerScanResultsListener(any(IBinder.class),
+ callbackCaptor.capture(), anyInt());
+ callbackCaptor.getValue().onScanResultsAvailable();
+ verify(mScanResultListener).onScanResultsAvailable();
+ }
+
+ /**
+ * Verify client provided listener is being called on the right executor.
+ */
+ @Test
+ public void testAddScanResultsListenerWithTheTargetExecutor() throws Exception {
+ ArgumentCaptor<IScanResultsListener.Stub> callbackCaptor =
+ ArgumentCaptor.forClass(IScanResultsListener.Stub.class);
+ mWifiManager.addScanResultsListener(mExecutor, mScanResultListener);
+ verify(mWifiService).registerScanResultsListener(any(IBinder.class),
+ callbackCaptor.capture(), anyInt());
+ callbackCaptor.getValue().onScanResultsAvailable();
+ verify(mExecutor).execute(any(Runnable.class));
+ }
+
+ /**
+ * Verify client removeScanResultsListener.
+ */
+ @Test
+ public void testRemoveScanResultsListener() throws Exception {
+ mWifiManager.removeScanResultsListener(mScanResultListener);
+ verify(mWifiService).unregisterScanResultsListener(anyInt());
+ }
+
+ /**
+ * Verify client removeScanResultsListener with null listener will cause an exception.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testRemoveScanResultsListenerWithNullListener() throws Exception {
+ mWifiManager.removeScanResultsListener(null);
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index 4dfa96b8c606..699008905f0a 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -19,6 +19,8 @@ package android.net.wifi;
import static org.junit.Assert.*;
import android.net.MacAddress;
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.PasspointTestUtils;
import android.os.Parcel;
import android.os.Process;
@@ -39,6 +41,7 @@ public class WifiNetworkSuggestionTest {
private static final String TEST_BSSID = "12:12:12:12:12:12";
private static final String TEST_SSID_1 = "\"Test1234\"";
private static final String TEST_PRESHARED_KEY = "Test123";
+ private static final String TEST_FQDN = "fqdn";
/**
* Validate correctness of WifiNetworkSuggestion object created by
@@ -186,6 +189,25 @@ public class WifiNetworkSuggestionTest {
}
/**
+ * Validate correctness of WifiNetworkSuggestion object created by
+ * {@link WifiNetworkSuggestion.Builder#build()} for Passpoint network which requires
+ * app interaction and metered.
+ */
+ @Test
+ public void testWifiNetworkSuggestionBuilderForPasspointNetworkWithReqAppInteractionMetered() {
+ PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig();
+ WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
+ .setPasspointConfig(passpointConfiguration)
+ .setIsAppInteractionRequired(true)
+ .setIsMetered(true)
+ .build();
+ assertEquals(TEST_FQDN, suggestion.wifiConfiguration.FQDN);
+ assertTrue(suggestion.isAppInteractionRequired);
+ assertEquals(suggestion.wifiConfiguration.meteredOverride,
+ WifiConfiguration.METERED_OVERRIDE_METERED);
+ }
+
+ /**
* Ensure {@link WifiNetworkSuggestion.Builder#setSsid(String)} throws an exception
* when the string is not Unicode.
*/
@@ -209,6 +231,18 @@ public class WifiNetworkSuggestionTest {
}
/**
+ * Ensure {@link WifiNetworkSuggestion.Builder#setPasspointConfig(PasspointConfiguration)}}
+ * throws an exception when the PasspointConfiguration is not valid.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testWifiNetworkSuggestionBuilderSetPasspointConfigWithNonValid() {
+ PasspointConfiguration passpointConfiguration = new PasspointConfiguration();
+ new WifiNetworkSuggestion.Builder()
+ .setPasspointConfig(passpointConfiguration)
+ .build();
+ }
+
+ /**
* Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception
* when {@link WifiNetworkSuggestion.Builder#setSsid(String)} is not set.
*/
@@ -311,6 +345,91 @@ public class WifiNetworkSuggestionTest {
}
/**
+ * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception
+ * when both {@link WifiNetworkSuggestion.Builder#setSsid(String)} and
+ * {@link WifiNetworkSuggestion.Builder#setPasspointConfig(PasspointConfiguration)} are invoked.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSuggestionBuilderWithBothSsidAndPasspointConfig() {
+ PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig();
+ new WifiNetworkSuggestion.Builder()
+ .setSsid(TEST_SSID)
+ .setPasspointConfig(passpointConfiguration)
+ .build();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception
+ * when both {@link WifiNetworkSuggestion.Builder#setWpa2Passphrase(String)} and
+ * {@link WifiNetworkSuggestion.Builder#setPasspointConfig(PasspointConfiguration)} are invoked.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSuggestionBuilderWithBothWpa2PassphraseAndPasspointConfig() {
+ PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig();
+ new WifiNetworkSuggestion.Builder()
+ .setWpa2Passphrase(TEST_PRESHARED_KEY)
+ .setPasspointConfig(passpointConfiguration)
+ .build();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception
+ * when both {@link WifiNetworkSuggestion.Builder#setWpa3Passphrase(String)} and
+ * {@link WifiNetworkSuggestion.Builder#setPasspointConfig(PasspointConfiguration)} are invoked.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSuggestionBuilderWithBothWpa3PassphraseAndPasspointConfig() {
+ PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig();
+ new WifiNetworkSuggestion.Builder()
+ .setWpa3Passphrase(TEST_PRESHARED_KEY)
+ .setPasspointConfig(passpointConfiguration)
+ .build();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception
+ * when both {@link WifiNetworkSuggestion.Builder#setWpa3EnterpriseConfig(WifiEnterpriseConfig)}
+ * and {@link WifiNetworkSuggestion.Builder#setPasspointConfig(PasspointConfiguration)} are
+ * invoked.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSuggestionBuilderWithBothEnterpriseAndPasspointConfig() {
+ PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig();
+ new WifiNetworkSuggestion.Builder()
+ .setWpa3EnterpriseConfig(new WifiEnterpriseConfig())
+ .setPasspointConfig(passpointConfiguration)
+ .build();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception
+ * when both {@link WifiNetworkSuggestion.Builder#setIsEnhancedOpen(boolean)} and
+ * {@link WifiNetworkSuggestion.Builder#setPasspointConfig(PasspointConfiguration)} are invoked.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSuggestionBuilderWithBothEnhancedOpenAndPasspointConfig() {
+ PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig();
+ new WifiNetworkSuggestion.Builder()
+ .setIsEnhancedOpen(true)
+ .setPasspointConfig(passpointConfiguration)
+ .build();
+ }
+
+ /**
+ * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception
+ * when both {@link WifiNetworkSuggestion.Builder#setIsHiddenSsid(boolean)} and
+ * {@link WifiNetworkSuggestion.Builder#setPasspointConfig(PasspointConfiguration)} are invoked.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testWifiNetworkSuggestionBuilderWithBothHiddenSsidAndPasspointConfig() {
+ PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig();
+ new WifiNetworkSuggestion.Builder()
+ .setIsHiddenSsid(true)
+ .setPasspointConfig(passpointConfiguration)
+ .build();
+ }
+
+ /**
* Check that parcel marshalling/unmarshalling works
*/
@Test
@@ -319,8 +438,8 @@ public class WifiNetworkSuggestionTest {
configuration.SSID = TEST_SSID;
configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
- WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, false, true, TEST_UID, TEST_PACKAGE_NAME);
+ WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion(
+ configuration, null, false, true, TEST_UID, TEST_PACKAGE_NAME);
Parcel parcelW = Parcel.obtain();
suggestion.writeToParcel(parcelW, 0);
@@ -337,6 +456,39 @@ public class WifiNetworkSuggestionTest {
// SSID + keyMgmt + same UID). |isAppInteractionRequired| & |isUserInteractionRequired| are
// not considered for equality and hence needs to be checked for explicitly below.
assertEquals(suggestion, parcelSuggestion);
+ assertEquals(suggestion.hashCode(), parcelSuggestion.hashCode());
+ assertEquals(suggestion.isAppInteractionRequired,
+ parcelSuggestion.isAppInteractionRequired);
+ assertEquals(suggestion.isUserInteractionRequired,
+ parcelSuggestion.isUserInteractionRequired);
+ }
+
+ /**
+ * Check that parcel marshalling/unmarshalling works
+ */
+ @Test
+ public void testPasspointNetworkSuggestionParcel() {
+ PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig();
+ WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
+ .setPasspointConfig(passpointConfiguration)
+ .build();
+
+ Parcel parcelW = Parcel.obtain();
+ suggestion.writeToParcel(parcelW, 0);
+ byte[] bytes = parcelW.marshall();
+ parcelW.recycle();
+
+ Parcel parcelR = Parcel.obtain();
+ parcelR.unmarshall(bytes, 0, bytes.length);
+ parcelR.setDataPosition(0);
+ WifiNetworkSuggestion parcelSuggestion =
+ WifiNetworkSuggestion.CREATOR.createFromParcel(parcelR);
+
+ // Two suggestion objects are considered equal if they point to the same network (i.e same
+ // SSID + keyMgmt + same UID). |isAppInteractionRequired| & |isUserInteractionRequired| are
+ // not considered for equality and hence needs to be checked for explicitly below.
+ assertEquals(suggestion, parcelSuggestion);
+ assertEquals(suggestion.hashCode(), parcelSuggestion.hashCode());
assertEquals(suggestion.isAppInteractionRequired,
parcelSuggestion.isAppInteractionRequired);
assertEquals(suggestion.isUserInteractionRequired,
@@ -354,7 +506,7 @@ public class WifiNetworkSuggestionTest {
configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, true, false, TEST_UID,
+ new WifiNetworkSuggestion(configuration, null, true, false, TEST_UID,
TEST_PACKAGE_NAME);
WifiConfiguration configuration1 = new WifiConfiguration();
@@ -362,10 +514,11 @@ public class WifiNetworkSuggestionTest {
configuration1.BSSID = TEST_BSSID;
configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, false, true, TEST_UID,
+ new WifiNetworkSuggestion(configuration1, null, false, true, TEST_UID,
TEST_PACKAGE_NAME);
assertEquals(suggestion, suggestion1);
+ assertEquals(suggestion.hashCode(), suggestion1.hashCode());
}
/**
@@ -378,14 +531,14 @@ public class WifiNetworkSuggestionTest {
configuration.SSID = TEST_SSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, false, false, TEST_UID,
+ new WifiNetworkSuggestion(configuration, null, false, false, TEST_UID,
TEST_PACKAGE_NAME);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID_1;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, false, false, TEST_UID,
+ new WifiNetworkSuggestion(configuration1, null, false, false, TEST_UID,
TEST_PACKAGE_NAME);
assertNotEquals(suggestion, suggestion1);
@@ -402,14 +555,14 @@ public class WifiNetworkSuggestionTest {
configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, false, false, TEST_UID,
+ new WifiNetworkSuggestion(configuration, null, false, false, TEST_UID,
TEST_PACKAGE_NAME);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID;
configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, false, false, TEST_UID,
+ new WifiNetworkSuggestion(configuration1, null, false, false, TEST_UID,
TEST_PACKAGE_NAME);
assertNotEquals(suggestion, suggestion1);
@@ -425,14 +578,14 @@ public class WifiNetworkSuggestionTest {
configuration.SSID = TEST_SSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, false, false, TEST_UID,
+ new WifiNetworkSuggestion(configuration, null, false, false, TEST_UID,
TEST_PACKAGE_NAME);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID;
configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, false, false, TEST_UID,
+ new WifiNetworkSuggestion(configuration1, null, false, false, TEST_UID,
TEST_PACKAGE_NAME);
assertNotEquals(suggestion, suggestion1);
@@ -448,11 +601,11 @@ public class WifiNetworkSuggestionTest {
configuration.SSID = TEST_SSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, false, false, TEST_UID,
+ new WifiNetworkSuggestion(configuration, null, false, false, TEST_UID,
TEST_PACKAGE_NAME);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration, false, false, TEST_UID_OTHER,
+ new WifiNetworkSuggestion(configuration, null, false, false, TEST_UID_OTHER,
TEST_PACKAGE_NAME);
assertNotEquals(suggestion, suggestion1);
@@ -467,13 +620,48 @@ public class WifiNetworkSuggestionTest {
WifiConfiguration configuration = new WifiConfiguration();
configuration.SSID = TEST_SSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
- WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, false, false, TEST_UID, TEST_PACKAGE_NAME);
+ WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion(
+ configuration, null, false, false, TEST_UID, TEST_PACKAGE_NAME);
- WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration, false, false, TEST_UID,
- TEST_PACKAGE_NAME_OTHER);
+ WifiNetworkSuggestion suggestion1 = new WifiNetworkSuggestion(
+ configuration, null, false, false, TEST_UID, TEST_PACKAGE_NAME_OTHER);
+
+ assertNotEquals(suggestion, suggestion1);
+ }
+ /**
+ * Check NetworkSuggestion equals returns {@code true} for 2 Passpoint network suggestions with
+ * same FQDN.
+ */
+ @Test
+ public void testPasspointNetworkSuggestionEqualsSameWithSameFQDN() {
+ PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig();
+ PasspointConfiguration passpointConfiguration1 = PasspointTestUtils.createConfig();
+ WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
+ .setPasspointConfig(passpointConfiguration)
+ .build();
+ WifiNetworkSuggestion suggestion1 = new WifiNetworkSuggestion.Builder()
+ .setPasspointConfig(passpointConfiguration1)
+ .build();
+ assertEquals(suggestion, suggestion1);
+ assertEquals(suggestion.hashCode(), suggestion1.hashCode());
+ }
+ /**
+ * Check NetworkSuggestion equals returns {@code false} for 2 Passpoint network suggestions with
+ * different FQDN.
+ */
+ @Test
+ public void testPasspointNetworkSuggestionNotEqualsSameWithDifferentFQDN() {
+ PasspointConfiguration passpointConfiguration = PasspointTestUtils.createConfig();
+ PasspointConfiguration passpointConfiguration1 = PasspointTestUtils.createConfig();
+ passpointConfiguration1.getHomeSp().setFqdn(TEST_FQDN + 1);
+
+ WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
+ .setPasspointConfig(passpointConfiguration)
+ .build();
+ WifiNetworkSuggestion suggestion1 = new WifiNetworkSuggestion.Builder()
+ .setPasspointConfig(passpointConfiguration1)
+ .build();
assertNotEquals(suggestion, suggestion1);
}
}
diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
index dd05b47fbd4f..ea136d62b202 100644
--- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
@@ -22,7 +22,6 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -445,4 +444,37 @@ public class WifiScannerTest {
assertEquals(WifiScanner.CMD_STOP_PNO_SCAN, message.what);
}
+
+ @Test
+ public void testScanDataAddResults() throws Exception {
+ ScanResult scanResult1 = new ScanResult();
+ scanResult1.SSID = TEST_SSID_1;
+ ScanData scanData = new ScanData(0, 0, new ScanResult[]{scanResult1});
+
+ ScanResult scanResult2 = new ScanResult();
+ scanResult2.SSID = TEST_SSID_2;
+ scanData.addResults(new ScanResult[]{scanResult2});
+
+ ScanResult[] consolidatedScanResults = scanData.getResults();
+ assertEquals(2, consolidatedScanResults.length);
+ assertEquals(TEST_SSID_1, consolidatedScanResults[0].SSID);
+ assertEquals(TEST_SSID_2, consolidatedScanResults[1].SSID);
+ }
+
+ @Test
+ public void testScanDataParcel() throws Exception {
+ ScanResult scanResult1 = new ScanResult();
+ scanResult1.SSID = TEST_SSID_1;
+ ScanData scanData = new ScanData(5, 4, new ScanResult[]{scanResult1});
+
+ Parcel parcel = Parcel.obtain();
+ scanData.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0); // Rewind data position back to the beginning for read.
+ ScanData readScanData = ScanData.CREATOR.createFromParcel(parcel);
+
+ assertEquals(scanData.getId(), readScanData.getId());
+ assertEquals(scanData.getFlags(), readScanData.getFlags());
+ assertEquals(scanData.getResults().length, readScanData.getResults().length);
+ assertEquals(scanData.getResults()[0].SSID, readScanData.getResults()[0].SSID);
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java
index d9a1d9afff61..c815d7519c4c 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java
@@ -54,6 +54,8 @@ public class ConfigParserTest {
"assets/hsr1/HSR1ProfileWithInvalidContentType.base64";
private static final String PASSPOINT_INSTALLATION_FILE_WITHOUT_PROFILE =
"assets/hsr1/HSR1ProfileWithoutProfile.base64";
+ private static final String PASSPOINT_INSTALLATION_FILE_WITH_UPDATE_ID =
+ "assets/hsr1/HSR1ProfileWithUpdateIdentifier.base64";
/**
* Read the content of the given resource file into a String.
@@ -201,4 +203,22 @@ public class ConfigParserTest {
assertNull(ConfigParser.parsePasspointConfig(
"application/x-wifi-config", configStr.getBytes()));
}
+
+ /**
+ * Verify a valid installation file is parsed successfully with the matching contents, and that
+ * Update identifier is cleared.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseConfigFileWithUpdateIdentifier() throws Exception {
+ String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_UPDATE_ID);
+ PasspointConfiguration expectedConfig = generateConfigurationFromProfile();
+ PasspointConfiguration actualConfig =
+ ConfigParser.parsePasspointConfig(
+ "application/x-wifi-config", configStr.getBytes());
+ // Expected configuration does not contain an update identifier
+ assertTrue(actualConfig.equals(expectedConfig));
+ }
+
} \ No newline at end of file
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
index 88740d850643..f501b16d2c79 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -19,23 +19,15 @@ package android.net.wifi.hotspot2;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import android.net.wifi.EAPConstants;
-import android.net.wifi.hotspot2.pps.Credential;
-import android.net.wifi.hotspot2.pps.HomeSp;
-import android.net.wifi.hotspot2.pps.Policy;
-import android.net.wifi.hotspot2.pps.UpdateParameter;
import android.os.Parcel;
-import android.util.Base64;
import androidx.test.filters.SmallTest;
import org.junit.Test;
import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
/**
@@ -47,136 +39,6 @@ public class PasspointConfigurationTest {
private static final int CERTIFICATE_FINGERPRINT_BYTES = 32;
/**
- * Utility function for creating a {@link android.net.wifi.hotspot2.pps.HomeSP}.
- *
- * @return {@link android.net.wifi.hotspot2.pps.HomeSP}
- */
- private static HomeSp createHomeSp() {
- HomeSp homeSp = new HomeSp();
- homeSp.setFqdn("fqdn");
- homeSp.setFriendlyName("friendly name");
- homeSp.setRoamingConsortiumOis(new long[] {0x55, 0x66});
- return homeSp;
- }
-
- /**
- * Utility function for creating a {@link android.net.wifi.hotspot2.pps.Credential}.
- *
- * @return {@link android.net.wifi.hotspot2.pps.Credential}
- */
- private static Credential createCredential() {
- Credential cred = new Credential();
- cred.setRealm("realm");
- cred.setUserCredential(null);
- cred.setCertCredential(null);
- cred.setSimCredential(new Credential.SimCredential());
- cred.getSimCredential().setImsi("1234*");
- cred.getSimCredential().setEapType(EAPConstants.EAP_SIM);
- cred.setCaCertificate(null);
- cred.setClientCertificateChain(null);
- cred.setClientPrivateKey(null);
- return cred;
- }
-
- /**
- * Helper function for creating a {@link Policy} for testing.
- *
- * @return {@link Policy}
- */
- private static Policy createPolicy() {
- Policy policy = new Policy();
- policy.setMinHomeDownlinkBandwidth(123);
- policy.setMinHomeUplinkBandwidth(345);
- policy.setMinRoamingDownlinkBandwidth(567);
- policy.setMinRoamingUplinkBandwidth(789);
- policy.setMaximumBssLoadValue(12);
- policy.setExcludedSsidList(new String[] {"ssid1", "ssid2"});
- HashMap<Integer, String> requiredProtoPortMap = new HashMap<>();
- requiredProtoPortMap.put(12, "23,342,123");
- requiredProtoPortMap.put(23, "789,372,1235");
- policy.setRequiredProtoPortMap(requiredProtoPortMap);
-
- List<Policy.RoamingPartner> preferredRoamingPartnerList = new ArrayList<>();
- Policy.RoamingPartner partner1 = new Policy.RoamingPartner();
- partner1.setFqdn("partner1.com");
- partner1.setFqdnExactMatch(true);
- partner1.setPriority(12);
- partner1.setCountries("us,jp");
- Policy.RoamingPartner partner2 = new Policy.RoamingPartner();
- partner2.setFqdn("partner2.com");
- partner2.setFqdnExactMatch(false);
- partner2.setPriority(42);
- partner2.setCountries("ca,fr");
- preferredRoamingPartnerList.add(partner1);
- preferredRoamingPartnerList.add(partner2);
- policy.setPreferredRoamingPartnerList(preferredRoamingPartnerList);
-
- UpdateParameter policyUpdate = new UpdateParameter();
- policyUpdate.setUpdateIntervalInMinutes(1712);
- policyUpdate.setUpdateMethod(UpdateParameter.UPDATE_METHOD_OMADM);
- policyUpdate.setRestriction(UpdateParameter.UPDATE_RESTRICTION_HOMESP);
- policyUpdate.setServerUri("policy.update.com");
- policyUpdate.setUsername("username");
- policyUpdate.setBase64EncodedPassword(
- Base64.encodeToString("password".getBytes(), Base64.DEFAULT));
- policyUpdate.setTrustRootCertUrl("trust.cert.com");
- policyUpdate.setTrustRootCertSha256Fingerprint(
- new byte[CERTIFICATE_FINGERPRINT_BYTES]);
- policy.setPolicyUpdate(policyUpdate);
-
- return policy;
- }
-
- private static UpdateParameter createSubscriptionUpdate() {
- UpdateParameter subUpdate = new UpdateParameter();
- subUpdate.setUpdateIntervalInMinutes(9021);
- subUpdate.setUpdateMethod(UpdateParameter.UPDATE_METHOD_SSP);
- subUpdate.setRestriction(UpdateParameter.UPDATE_RESTRICTION_ROAMING_PARTNER);
- subUpdate.setServerUri("subscription.update.com");
- subUpdate.setUsername("subUsername");
- subUpdate.setBase64EncodedPassword(
- Base64.encodeToString("subPassword".getBytes(), Base64.DEFAULT));
- subUpdate.setTrustRootCertUrl("subscription.trust.cert.com");
- subUpdate.setTrustRootCertSha256Fingerprint(new byte[CERTIFICATE_FINGERPRINT_BYTES]);
- return subUpdate;
- }
- /**
- * Helper function for creating a {@link PasspointConfiguration} for testing.
- *
- * @return {@link PasspointConfiguration}
- */
- private static PasspointConfiguration createConfig() {
- PasspointConfiguration config = new PasspointConfiguration();
- config.setUpdateIdentifier(1234);
- config.setHomeSp(createHomeSp());
- config.setAaaServerTrustedNames(
- new String[] {"trusted.fqdn.com", "another-trusted.fqdn.com"});
- config.setCredential(createCredential());
- config.setPolicy(createPolicy());
- config.setSubscriptionUpdate(createSubscriptionUpdate());
- Map<String, byte[]> trustRootCertList = new HashMap<>();
- trustRootCertList.put("trustRoot.cert1.com",
- new byte[CERTIFICATE_FINGERPRINT_BYTES]);
- trustRootCertList.put("trustRoot.cert2.com",
- new byte[CERTIFICATE_FINGERPRINT_BYTES]);
- config.setTrustRootCertList(trustRootCertList);
- config.setUpdateIdentifier(1);
- config.setCredentialPriority(120);
- config.setSubscriptionCreationTimeInMillis(231200);
- config.setSubscriptionExpirationTimeInMillis(2134232);
- config.setSubscriptionType("Gold");
- config.setUsageLimitUsageTimePeriodInMinutes(3600);
- config.setUsageLimitStartTimeInMillis(124214213);
- config.setUsageLimitDataLimit(14121);
- config.setUsageLimitTimeLimitInMinutes(78912);
- Map<String, String> friendlyNames = new HashMap<>();
- friendlyNames.put("en", "ServiceName1");
- friendlyNames.put("kr", "ServiceName2");
- config.setServiceFriendlyNames(friendlyNames);
- return config;
- }
-
- /**
* Verify parcel write and read consistency for the given configuration.
*
* @param writeConfig The configuration to verify
@@ -209,7 +71,7 @@ public class PasspointConfigurationTest {
*/
@Test
public void verifyParcelWithFullConfiguration() throws Exception {
- verifyParcel(createConfig());
+ verifyParcel(PasspointTestUtils.createConfig());
}
/**
@@ -219,7 +81,7 @@ public class PasspointConfigurationTest {
*/
@Test
public void verifyParcelWithoutServiceNames() throws Exception {
- PasspointConfiguration config = createConfig();
+ PasspointConfiguration config = PasspointTestUtils.createConfig();
config.setServiceFriendlyNames(null);
verifyParcel(config);
}
@@ -231,7 +93,7 @@ public class PasspointConfigurationTest {
*/
@Test
public void verifyParcelWithoutHomeSP() throws Exception {
- PasspointConfiguration config = createConfig();
+ PasspointConfiguration config = PasspointTestUtils.createConfig();
config.setHomeSp(null);
verifyParcel(config);
}
@@ -243,7 +105,7 @@ public class PasspointConfigurationTest {
*/
@Test
public void verifyParcelWithoutCredential() throws Exception {
- PasspointConfiguration config = createConfig();
+ PasspointConfiguration config = PasspointTestUtils.createConfig();
config.setCredential(null);
verifyParcel(config);
}
@@ -255,7 +117,7 @@ public class PasspointConfigurationTest {
*/
@Test
public void verifyParcelWithoutPolicy() throws Exception {
- PasspointConfiguration config = createConfig();
+ PasspointConfiguration config = PasspointTestUtils.createConfig();
config.setPolicy(null);
verifyParcel(config);
}
@@ -267,7 +129,7 @@ public class PasspointConfigurationTest {
*/
@Test
public void verifyParcelWithoutSubscriptionUpdate() throws Exception {
- PasspointConfiguration config = createConfig();
+ PasspointConfiguration config = PasspointTestUtils.createConfig();
config.setSubscriptionUpdate(null);
verifyParcel(config);
}
@@ -280,7 +142,7 @@ public class PasspointConfigurationTest {
*/
@Test
public void verifyParcelWithoutTrustRootCertList() throws Exception {
- PasspointConfiguration config = createConfig();
+ PasspointConfiguration config = PasspointTestUtils.createConfig();
config.setTrustRootCertList(null);
verifyParcel(config);
}
@@ -293,7 +155,7 @@ public class PasspointConfigurationTest {
*/
@Test
public void verifyParcelWithoutAaaServerTrustedNames() throws Exception {
- PasspointConfiguration config = createConfig();
+ PasspointConfiguration config = PasspointTestUtils.createConfig();
config.setAaaServerTrustedNames(null);
verifyParcel(config);
}
@@ -318,10 +180,10 @@ public class PasspointConfigurationTest {
*/
@Test
public void validateFullConfig() throws Exception {
- PasspointConfiguration config = createConfig();
+ PasspointConfiguration config = PasspointTestUtils.createConfig();
assertTrue(config.validate());
- assertTrue(config.validateForR2());
+ assertFalse(config.isOsuProvisioned());
}
/**
@@ -332,7 +194,7 @@ public class PasspointConfigurationTest {
*/
@Test
public void validateFullConfigWithoutUpdateIdentifier() throws Exception {
- PasspointConfiguration config = createConfig();
+ PasspointConfiguration config = PasspointTestUtils.createConfig();
config.setUpdateIdentifier(Integer.MIN_VALUE);
assertTrue(config.validate());
@@ -346,7 +208,7 @@ public class PasspointConfigurationTest {
*/
@Test
public void validateConfigWithoutCredential() throws Exception {
- PasspointConfiguration config = createConfig();
+ PasspointConfiguration config = PasspointTestUtils.createConfig();
config.setCredential(null);
assertFalse(config.validate());
@@ -360,7 +222,7 @@ public class PasspointConfigurationTest {
*/
@Test
public void validateConfigWithoutHomeSp() throws Exception {
- PasspointConfiguration config = createConfig();
+ PasspointConfiguration config = PasspointTestUtils.createConfig();
config.setHomeSp(null);
assertFalse(config.validate());
@@ -375,11 +237,10 @@ public class PasspointConfigurationTest {
*/
@Test
public void validateConfigWithoutPolicy() throws Exception {
- PasspointConfiguration config = createConfig();
+ PasspointConfiguration config = PasspointTestUtils.createConfig();
config.setPolicy(null);
assertTrue(config.validate());
- assertTrue(config.validateForR2());
}
/**
@@ -390,7 +251,7 @@ public class PasspointConfigurationTest {
*/
@Test
public void validateConfigWithoutSubscriptionUpdate() throws Exception {
- PasspointConfiguration config = createConfig();
+ PasspointConfiguration config = PasspointTestUtils.createConfig();
config.setSubscriptionUpdate(null);
assertTrue(config.validate());
@@ -405,11 +266,10 @@ public class PasspointConfigurationTest {
*/
@Test
public void validateConfigWithoutAaaServerTrustedNames() throws Exception {
- PasspointConfiguration config = createConfig();
+ PasspointConfiguration config = PasspointTestUtils.createConfig();
config.setAaaServerTrustedNames(null);
assertTrue(config.validate());
- assertTrue(config.validateForR2());
}
/**
@@ -420,7 +280,7 @@ public class PasspointConfigurationTest {
*/
@Test
public void validateConfigWithInvalidTrustRootCertUrl() throws Exception {
- PasspointConfiguration config = createConfig();
+ PasspointConfiguration config = PasspointTestUtils.createConfig();
byte[] rawUrlBytes = new byte[MAX_URL_BYTES + 1];
Map<String, byte[]> trustRootCertList = new HashMap<>();
Arrays.fill(rawUrlBytes, (byte) 'a');
@@ -445,7 +305,7 @@ public class PasspointConfigurationTest {
*/
@Test
public void validateConfigWithInvalidTrustRootCertFingerprint() throws Exception {
- PasspointConfiguration config = createConfig();
+ PasspointConfiguration config = PasspointTestUtils.createConfig();
Map<String, byte[]> trustRootCertList = new HashMap<>();
trustRootCertList.put("test.cert.com", new byte[CERTIFICATE_FINGERPRINT_BYTES + 1]);
config.setTrustRootCertList(trustRootCertList);
@@ -482,8 +342,21 @@ public class PasspointConfigurationTest {
*/
@Test
public void validateCopyConstructorWithValidSource() throws Exception {
- PasspointConfiguration sourceConfig = createConfig();
+ PasspointConfiguration sourceConfig = PasspointTestUtils.createConfig();
PasspointConfiguration copyConfig = new PasspointConfiguration(sourceConfig);
assertTrue(copyConfig.equals(sourceConfig));
}
+
+ /**
+ * Verify that a configuration containing all fields is valid for R2.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateFullR2Config() throws Exception {
+ PasspointConfiguration config = PasspointTestUtils.createR2Config();
+ assertTrue(config.validate());
+ assertTrue(config.validateForR2());
+ assertTrue(config.isOsuProvisioned());
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointTestUtils.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointTestUtils.java
new file mode 100644
index 000000000000..8d55acb87f15
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointTestUtils.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.hotspot2;
+
+import android.net.wifi.EAPConstants;
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSp;
+import android.net.wifi.hotspot2.pps.Policy;
+import android.net.wifi.hotspot2.pps.UpdateParameter;
+import android.util.Base64;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class PasspointTestUtils {
+ private static final int CERTIFICATE_FINGERPRINT_BYTES = 32;
+
+ /**
+ * Utility function for creating a {@link android.net.wifi.hotspot2.pps.HomeSP}.
+ *
+ * @return {@link android.net.wifi.hotspot2.pps.HomeSP}
+ */
+ private static HomeSp createHomeSp() {
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn("fqdn");
+ homeSp.setFriendlyName("friendly name");
+ homeSp.setRoamingConsortiumOis(new long[] {0x55, 0x66});
+ return homeSp;
+ }
+
+ /**
+ * Utility function for creating a {@link android.net.wifi.hotspot2.pps.Credential}.
+ *
+ * @return {@link android.net.wifi.hotspot2.pps.Credential}
+ */
+ private static Credential createCredential() {
+ Credential cred = new Credential();
+ cred.setRealm("realm");
+ cred.setUserCredential(null);
+ cred.setCertCredential(null);
+ cred.setSimCredential(new Credential.SimCredential());
+ cred.getSimCredential().setImsi("1234*");
+ cred.getSimCredential().setEapType(EAPConstants.EAP_SIM);
+ cred.setCaCertificate(null);
+ cred.setClientCertificateChain(null);
+ cred.setClientPrivateKey(null);
+ return cred;
+ }
+
+ /**
+ * Helper function for creating a {@link Policy} for testing.
+ *
+ * @return {@link Policy}
+ */
+ private static Policy createPolicy() {
+ Policy policy = new Policy();
+ policy.setMinHomeDownlinkBandwidth(123);
+ policy.setMinHomeUplinkBandwidth(345);
+ policy.setMinRoamingDownlinkBandwidth(567);
+ policy.setMinRoamingUplinkBandwidth(789);
+ policy.setMaximumBssLoadValue(12);
+ policy.setExcludedSsidList(new String[] {"ssid1", "ssid2"});
+ HashMap<Integer, String> requiredProtoPortMap = new HashMap<>();
+ requiredProtoPortMap.put(12, "23,342,123");
+ requiredProtoPortMap.put(23, "789,372,1235");
+ policy.setRequiredProtoPortMap(requiredProtoPortMap);
+
+ List<Policy.RoamingPartner> preferredRoamingPartnerList = new ArrayList<>();
+ Policy.RoamingPartner partner1 = new Policy.RoamingPartner();
+ partner1.setFqdn("partner1.com");
+ partner1.setFqdnExactMatch(true);
+ partner1.setPriority(12);
+ partner1.setCountries("us,jp");
+ Policy.RoamingPartner partner2 = new Policy.RoamingPartner();
+ partner2.setFqdn("partner2.com");
+ partner2.setFqdnExactMatch(false);
+ partner2.setPriority(42);
+ partner2.setCountries("ca,fr");
+ preferredRoamingPartnerList.add(partner1);
+ preferredRoamingPartnerList.add(partner2);
+ policy.setPreferredRoamingPartnerList(preferredRoamingPartnerList);
+
+ UpdateParameter policyUpdate = new UpdateParameter();
+ policyUpdate.setUpdateIntervalInMinutes(1712);
+ policyUpdate.setUpdateMethod(UpdateParameter.UPDATE_METHOD_OMADM);
+ policyUpdate.setRestriction(UpdateParameter.UPDATE_RESTRICTION_HOMESP);
+ policyUpdate.setServerUri("policy.update.com");
+ policyUpdate.setUsername("username");
+ policyUpdate.setBase64EncodedPassword(
+ Base64.encodeToString("password".getBytes(), Base64.DEFAULT));
+ policyUpdate.setTrustRootCertUrl("trust.cert.com");
+ policyUpdate.setTrustRootCertSha256Fingerprint(
+ new byte[CERTIFICATE_FINGERPRINT_BYTES]);
+ policy.setPolicyUpdate(policyUpdate);
+
+ return policy;
+ }
+
+ private static UpdateParameter createSubscriptionUpdate() {
+ UpdateParameter subUpdate = new UpdateParameter();
+ subUpdate.setUpdateIntervalInMinutes(9021);
+ subUpdate.setUpdateMethod(UpdateParameter.UPDATE_METHOD_SSP);
+ subUpdate.setRestriction(UpdateParameter.UPDATE_RESTRICTION_ROAMING_PARTNER);
+ subUpdate.setServerUri("subscription.update.com");
+ subUpdate.setUsername("subUsername");
+ subUpdate.setBase64EncodedPassword(
+ Base64.encodeToString("subPassword".getBytes(), Base64.DEFAULT));
+ subUpdate.setTrustRootCertUrl("subscription.trust.cert.com");
+ subUpdate.setTrustRootCertSha256Fingerprint(new byte[CERTIFICATE_FINGERPRINT_BYTES]);
+ return subUpdate;
+ }
+ /**
+ * Helper function for creating a {@link PasspointConfiguration} for testing.
+ *
+ * @return {@link PasspointConfiguration}
+ */
+ public static PasspointConfiguration createConfig() {
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.setHomeSp(createHomeSp());
+ config.setAaaServerTrustedNames(
+ new String[] {"trusted.fqdn.com", "another-trusted.fqdn.com"});
+ config.setCredential(createCredential());
+ config.setPolicy(createPolicy());
+ config.setSubscriptionUpdate(createSubscriptionUpdate());
+ Map<String, byte[]> trustRootCertList = new HashMap<>();
+ trustRootCertList.put("trustRoot.cert1.com",
+ new byte[CERTIFICATE_FINGERPRINT_BYTES]);
+ trustRootCertList.put("trustRoot.cert2.com",
+ new byte[CERTIFICATE_FINGERPRINT_BYTES]);
+ config.setTrustRootCertList(trustRootCertList);
+ config.setCredentialPriority(120);
+ config.setSubscriptionCreationTimeInMillis(231200);
+ config.setSubscriptionExpirationTimeInMillis(2134232);
+ config.setSubscriptionType("Gold");
+ config.setUsageLimitUsageTimePeriodInMinutes(3600);
+ config.setUsageLimitStartTimeInMillis(124214213);
+ config.setUsageLimitDataLimit(14121);
+ config.setUsageLimitTimeLimitInMinutes(78912);
+ Map<String, String> friendlyNames = new HashMap<>();
+ friendlyNames.put("en", "ServiceName1");
+ friendlyNames.put("kr", "ServiceName2");
+ config.setServiceFriendlyNames(friendlyNames);
+ return config;
+ }
+
+ /**
+ * Helper function for creating an R2 {@link PasspointConfiguration} for testing.
+ *
+ * @return {@link PasspointConfiguration}
+ */
+ public static PasspointConfiguration createR2Config() {
+ PasspointConfiguration config = createConfig();
+ config.setUpdateIdentifier(1234);
+ return config;
+ }
+}